From 063f5bfd6dcc44437022504b9a6488fe077b1d71 Mon Sep 17 00:00:00 2001 From: Daniel Galvez Date: Tue, 10 May 2022 05:29:20 +0000 Subject: [PATCH 1/5] Delete lingvo. We're not using it. --- LICENSE | 201 - PUBLICATIONS.md | 1114 - codelabs/introduction.ipynb | 658 - docker/dev.dockerfile | 115 - docker/lib.dockerfile | 51 - docker/run_distributed.py | 223 - docs/apidoc/conf.py | 217 - docs/apidoc/index.rst | 17 - docs/generate_apidoc.sh | 45 - docs/single/build.sh | 35 - docs/single/conf.py | 52 - docs/single/index.rst | 5 - lingvo/.style.yapf | 3 - lingvo/BUILD | 311 - lingvo/__init__.py | 0 lingvo/base_runner.py | 424 - lingvo/base_trial.py | 126 - lingvo/compat.py | 411 - lingvo/compat_test.py | 53 - lingvo/core/BUILD | 2154 - lingvo/core/__init__.py | 0 lingvo/core/adagraft.py | 185 - lingvo/core/adagraft_test.py | 83 - lingvo/core/attention.py | 3277 -- lingvo/core/attention_test.py | 2377 -- lingvo/core/attention_util.py | 664 - lingvo/core/attention_util_test.py | 511 - lingvo/core/base_decoder.py | 394 - lingvo/core/base_input_generator.py | 1205 - lingvo/core/base_input_generator_test.py | 477 - lingvo/core/base_layer.py | 980 - lingvo/core/base_layer_test.py | 326 - lingvo/core/base_model.py | 1229 - lingvo/core/base_model_params.py | 109 - lingvo/core/base_model_params_test.py | 43 - lingvo/core/base_model_test.py | 528 - lingvo/core/batch_major_attention.py | 4216 -- lingvo/core/batch_major_attention_test.py | 2796 -- lingvo/core/batch_utils.py | 61 - lingvo/core/batch_utils_test.py | 101 - lingvo/core/beam_search_helper.py | 795 - lingvo/core/beam_search_helper_test.py | 308 - lingvo/core/bfloat16_variables.py | 93 - lingvo/core/bfloat16_variables_test.py | 57 - lingvo/core/bn_layers.py | 823 - lingvo/core/build_data.py | 24 - lingvo/core/builder.py | 314 - lingvo/core/builder_layers.py | 1259 - lingvo/core/builder_layers_test.py | 554 - lingvo/core/builder_test.py | 336 - lingvo/core/checkpointer.py | 264 - lingvo/core/checkpointer_test.py | 249 - lingvo/core/cluster.py | 549 - lingvo/core/cluster_factory.py | 129 - lingvo/core/cluster_test.py | 440 - lingvo/core/computation_cost.py | 73 - lingvo/core/conformer_layer.py | 357 - lingvo/core/conformer_layer_test.py | 75 - lingvo/core/constants.py | 19 - lingvo/core/conv_layers_builder.py | 530 - lingvo/core/conv_layers_builder_test.py | 616 - lingvo/core/conv_layers_with_time_padding.py | 800 - .../conv_layers_with_time_padding_test.py | 723 - lingvo/core/datasource.py | 409 - lingvo/core/datasource_test.py | 296 - lingvo/core/decoder_lib.py | 25 - lingvo/core/distillation_task.py | 170 - lingvo/core/distributed_shampoo.py | 668 - lingvo/core/distributed_shampoo_test.py | 414 - lingvo/core/early_stop.py | 206 - lingvo/core/early_stop_test.py | 211 - lingvo/core/egdd.py | 181 - lingvo/core/egdd_test.py | 114 - lingvo/core/ema_test.py | 142 - lingvo/core/generic_input.py | 170 - lingvo/core/generic_input_test.py | 300 - lingvo/core/gpipe.py | 607 - lingvo/core/gpipe_test.py | 241 - lingvo/core/gradient_combiner.py | 45 - lingvo/core/hyperparams.proto | 58 - lingvo/core/hyperparams.py | 891 - lingvo/core/hyperparams_test.py | 613 - lingvo/core/inference_graph.proto | 125 - lingvo/core/inference_graph_exporter.py | 535 - lingvo/core/inference_graph_exporter_test.py | 453 - lingvo/core/input_generator_helper.py | 122 - lingvo/core/input_generator_helper_test.py | 226 - lingvo/core/insertion.py | 328 - lingvo/core/insertion_test.py | 346 - lingvo/core/inspect_utils.py | 174 - lingvo/core/inspect_utils_test.py | 184 - lingvo/core/layers.py | 4978 --- lingvo/core/layers_test.py | 5338 --- lingvo/core/layers_with_attention.py | 2205 - lingvo/core/layers_with_attention_test.py | 2735 -- lingvo/core/layers_with_gpipe.py | 1432 - lingvo/core/layers_with_gpipe_test.py | 990 - lingvo/core/learner.py | 454 - lingvo/core/learner_test.py | 154 - lingvo/core/lstm_frnn_layer.py | 253 - lingvo/core/lstm_frnn_layer_test.py | 231 - lingvo/core/matrix_functions.py | 175 - lingvo/core/matrix_functions_test.py | 61 - lingvo/core/metrics.py | 443 - lingvo/core/metrics_test.py | 114 - lingvo/core/ml_perf_bleu_metric.py | 228 - lingvo/core/ml_perf_bleu_metric_test.py | 33 - lingvo/core/ml_perf_log.py | 80 - lingvo/core/ml_perf_log_test.py | 39 - lingvo/core/ml_perf_tokenizer.py | 34 - lingvo/core/model_helper.py | 79 - lingvo/core/moe_layers.py | 1373 - lingvo/core/multitask_model.py | 127 - lingvo/core/multitask_model_test.py | 138 - lingvo/core/ops/BUILD | 518 - lingvo/core/ops/__init__.py | 67 - lingvo/core/ops/ascii_tokenizer.cc | 180 - lingvo/core/ops/ascii_tokenizer.h | 49 - lingvo/core/ops/assert_kernels.cc | 113 - lingvo/core/ops/assert_ops_test.py | 66 - .../core/ops/beam_search_step_op_kernels.cc | 1071 - lingvo/core/ops/beam_search_step_op_kernels.h | 244 - lingvo/core/ops/beam_search_step_op_test.py | 655 - .../ops/beam_search_step_op_top_k_test.cc | 172 - lingvo/core/ops/best_step_op_kernels.cc | 169 - lingvo/core/ops/best_step_op_test.py | 85 - lingvo/core/ops/chain_record_yielder.cc | 77 - lingvo/core/ops/chain_record_yielder.h | 74 - lingvo/core/ops/chain_record_yielder_test.cc | 95 - lingvo/core/ops/functional_ops_kernels.cc | 127 - lingvo/core/ops/functional_ops_test.py | 55 - lingvo/core/ops/generic_input_op_kernels.cc | 317 - lingvo/core/ops/hyps.proto | 31 - lingvo/core/ops/input_common.cc | 95 - lingvo/core/ops/input_common.h | 116 - lingvo/core/ops/mass_op.cc | 256 - lingvo/core/ops/mass_op_test.py | 929 - lingvo/core/ops/ml_perf_subword_op.cc | 139 - lingvo/core/ops/ml_perf_subword_op.h | 46 - lingvo/core/ops/pack_ops.cc | 502 - lingvo/core/ops/pack_ops_test.py | 379 - lingvo/core/ops/preconditioner_captain.cc | 133 - lingvo/core/ops/preconditioner_captain.h | 81 - lingvo/core/ops/preconditioner_op_kernels.cc | 152 - .../ops/preconditioner_op_kernels_test.py | 88 - lingvo/core/ops/random_ops_kernels.cc | 96 - lingvo/core/ops/random_ops_test.py | 62 - lingvo/core/ops/record.proto | 10 - lingvo/core/ops/record_batcher.cc | 485 - lingvo/core/ops/record_batcher.h | 205 - lingvo/core/ops/record_batcher_test.cc | 316 - lingvo/core/ops/record_debug.cc | 76 - lingvo/core/ops/record_yielder.cc | 584 - lingvo/core/ops/record_yielder.h | 311 - lingvo/core/ops/record_yielder_test.cc | 538 - lingvo/core/ops/rope.h | 40 - lingvo/core/ops/sequential_record_yielder.cc | 89 - lingvo/core/ops/sequential_record_yielder.h | 55 - lingvo/core/ops/simple_vocab.cc | 258 - lingvo/core/ops/simple_vocab.h | 129 - lingvo/core/ops/simple_vocab_test.py | 163 - lingvo/core/ops/static_map_op.cc | 125 - lingvo/core/ops/static_map_op_test.py | 60 - lingvo/core/ops/testdata/BUILD | 44 - lingvo/core/ops/testdata/bpe_codes.vocab | 67 - lingvo/core/ops/testdata/bpe_words.vocab | 143 - .../ops/testdata/events.out.tfevents.test | Bin 80291 -> 0 bytes lingvo/core/ops/testdata/history.txt | 131 - lingvo/core/ops/testdata/history_bleu.txt | 97 - .../ops/testdata/mlperf.ende.subwords.vocab | 33708 ---------------- lingvo/core/ops/testdata/test_ngrams.txt | 255 - lingvo/core/ops/testdata/test_vocab.txt | 14 - lingvo/core/ops/testdata/wmt/sm18.txt | 3003 -- lingvo/core/ops/text_packing.cc | 103 - lingvo/core/ops/text_packing.h | 112 - lingvo/core/ops/text_packing_test.cc | 282 - lingvo/core/ops/tokenizer_op_headers.h | 151 - lingvo/core/ops/tokenizer_ops_kernels.cc | 374 - lingvo/core/ops/tokenizer_ops_test.py | 336 - lingvo/core/ops/versioned_file_set.proto | 24 - .../core/ops/weighted_mix_record_yielder.cc | 87 - lingvo/core/ops/weighted_mix_record_yielder.h | 88 - .../ops/weighted_mix_record_yielder_test.cc | 241 - lingvo/core/ops/x_ops.cc | 1027 - lingvo/core/ops/x_ops_helper.h | 75 - lingvo/core/ops/yielder_test_helper.cc | 85 - lingvo/core/ops/yielder_test_helper.h | 48 - lingvo/core/optimizer.py | 605 - lingvo/core/optimizer_test.py | 215 - lingvo/core/plot.py | 498 - lingvo/core/plot_test.py | 275 - lingvo/core/predictor.py | 307 - lingvo/core/predictor_runner_base.py | 290 - lingvo/core/predictor_test.py | 81 - lingvo/core/program.py | 1201 - lingvo/core/pruning_utils.py | 42 - lingvo/core/py_utils.py | 5428 --- lingvo/core/py_utils_test.py | 3340 -- lingvo/core/quant_utils.py | 1186 - lingvo/core/quant_utils_test.py | 444 - lingvo/core/recurrent.py | 1634 - lingvo/core/recurrent_gpu_test.py | 59 - lingvo/core/recurrent_test.py | 911 - lingvo/core/retry.py | 71 - lingvo/core/retry_test.py | 90 - lingvo/core/reversible_layers.py | 233 - lingvo/core/reversible_layers_test.py | 191 - lingvo/core/rnn_cell.py | 2586 -- lingvo/core/rnn_cell_test.py | 1447 - lingvo/core/rnn_layers.py | 1436 - lingvo/core/rnn_layers_test.py | 1560 - lingvo/core/saver.py | 333 - lingvo/core/saver_test.py | 123 - lingvo/core/schedule.py | 824 - lingvo/core/schedule_test.py | 572 - lingvo/core/scorers.py | 132 - lingvo/core/scorers_test.py | 98 - lingvo/core/self_attention_layer.py | 101 - lingvo/core/self_attention_layer_test.py | 167 - lingvo/core/sendrecv.py | 112 - lingvo/core/sendrecv_test.py | 105 - lingvo/core/spectrum_augmenter.py | 845 - lingvo/core/spectrum_augmenter_on_device.py | 57 - .../core/spectrum_augmenter_on_device_test.py | 492 - lingvo/core/spectrum_augmenter_test.py | 785 - lingvo/core/step.py | 693 - lingvo/core/step_test.py | 220 - lingvo/core/steps/BUILD | 85 - lingvo/core/steps/__init__.py | 0 lingvo/core/steps/attention_steps.py | 277 - lingvo/core/steps/attention_steps_test.py | 581 - lingvo/core/steps/embedding_steps.py | 65 - lingvo/core/steps/embedding_steps_test.py | 57 - lingvo/core/steps/rnn_steps.py | 260 - lingvo/core/steps/rnn_steps_test.py | 233 - lingvo/core/summary_utils.py | 381 - lingvo/core/summary_utils_test.py | 44 - lingvo/core/symbolic.py | 127 - lingvo/core/symbolic_test.py | 68 - lingvo/core/target_sequence_sampler.py | 168 - lingvo/core/target_sequence_sampler_test.py | 258 - lingvo/core/task_scheduler.py | 324 - lingvo/core/task_scheduler_test.py | 198 - lingvo/core/test_helper.py | 23 - lingvo/core/test_trainer_utils.py | 83 - lingvo/core/test_utils.py | 212 - lingvo/core/test_utils_test.py | 53 - lingvo/core/testdata/BUILD | 10 - .../lenet_test_model.data-00000-of-00001 | Bin 9165752 -> 0 bytes lingvo/core/testdata/lenet_test_model.index | Bin 629 -> 0 bytes lingvo/core/tokenizers.py | 339 - lingvo/core/tpu_embedding_layers.py | 547 - lingvo/core/tpu_summary.py | 227 - lingvo/core/tpu_summary_test.py | 224 - lingvo/core/trainer_test_utils.py | 137 - lingvo/core/tshape.py | 117 - lingvo/core/tshape_test.py | 49 - lingvo/core/wpm_encoder.py | 197 - lingvo/core/wpm_encoder_test.py | 104 - lingvo/core/wpm_tokenizer_test.py | 66 - lingvo/datasets.py | 28 - lingvo/executor.py | 347 - lingvo/ipython_kernel.py | 44 - lingvo/lingvo.bzl | 173 - lingvo/model_import_test.py | 23 - lingvo/model_imports.py | 104 - lingvo/model_registry.py | 272 - lingvo/model_registry_test.py | 149 - lingvo/models_test.py | 39 - lingvo/models_test_helper.py | 181 - lingvo/repo.bzl | 243 - lingvo/tasks/BUILD | 20 - lingvo/tasks/__init__.py | 0 lingvo/tasks/asr/BUILD | 286 - lingvo/tasks/asr/__init__.py | 0 lingvo/tasks/asr/align_input_generator.py | 147 - lingvo/tasks/asr/blocks.py | 315 - lingvo/tasks/asr/contextualizer_base.py | 126 - lingvo/tasks/asr/ctc_model.py | 333 - lingvo/tasks/asr/ctc_model_test.py | 75 - lingvo/tasks/asr/decoder.py | 1556 - lingvo/tasks/asr/decoder_test.py | 765 - lingvo/tasks/asr/decoder_utils.py | 168 - lingvo/tasks/asr/decoder_utils_test.py | 212 - lingvo/tasks/asr/encoder.py | 464 - lingvo/tasks/asr/encoder_test.py | 337 - lingvo/tasks/asr/encoder_v2.py | 127 - lingvo/tasks/asr/frontend.py | 480 - lingvo/tasks/asr/frontend_test.py | 480 - lingvo/tasks/asr/fusion.py | 177 - lingvo/tasks/asr/input_generator.py | 357 - lingvo/tasks/asr/input_generator_test.py | 176 - lingvo/tasks/asr/mlperf_compat_frontend.py | 148 - lingvo/tasks/asr/model.py | 496 - lingvo/tasks/asr/model_test.py | 430 - .../tasks/asr/model_test_input_generator.py | 268 - lingvo/tasks/asr/params/BUILD | 38 - lingvo/tasks/asr/params/README.md | 75 - lingvo/tasks/asr/params/inference_only.py | 94 - lingvo/tasks/asr/params/librispeech.py | 330 - lingvo/tasks/asr/params/librispeech_ctc.py | 462 - lingvo/tasks/asr/params/params.py | 22 - lingvo/tasks/asr/params/peoplesspeech_ctc.py | 192 - lingvo/tasks/asr/rnnt_model.py | 63 - lingvo/tasks/asr/rnnt_prediction.py | 155 - lingvo/tasks/asr/tools/BUILD | 43 - lingvo/tasks/asr/tools/__init__.py | 0 .../tools/librispeech.01.download_train.sh | 28 - .../tools/librispeech.02.download_devtest.sh | 28 - .../librispeech.03.parameterize_train.sh | 98 - .../librispeech.04.parameterize_devtest.sh | 32 - .../asr/tools/librispeech.05.train_ctc.sh | 17 - .../tasks/asr/tools/librispeech_get_data.sh | 22 - lingvo/tasks/asr/tools/librispeech_lib.sh | 55 - lingvo/tasks/asr/tools/qsub_script.sh | 13 - lingvo/tasks/asr/tools/run_model.sh | 14 - lingvo/tasks/asr/tools/simple_wer.py | 340 - lingvo/tasks/asr/tools/simple_wer_test.py | 34 - lingvo/tasks/asr/tools/simple_wer_v2.py | 437 - lingvo/tasks/asr/tools/simple_wer_v2_test.py | 90 - lingvo/tasks/asr/wpm_16k_librispeech.vocab | 16328 -------- lingvo/tasks/car/BUILD | 562 - lingvo/tasks/car/README.md | 158 - lingvo/tasks/car/__init__.py | 0 lingvo/tasks/car/ap_metric.py | 556 - lingvo/tasks/car/base_decoder.py | 89 - lingvo/tasks/car/base_extractor.py | 308 - lingvo/tasks/car/breakdown_metric.py | 778 - lingvo/tasks/car/breakdown_metric_test.py | 418 - lingvo/tasks/car/builder_lib.py | 1151 - lingvo/tasks/car/builder_lib_test.py | 466 - lingvo/tasks/car/calibration_processing.py | 228 - .../tasks/car/calibration_processing_test.py | 138 - lingvo/tasks/car/car_layers.py | 110 - lingvo/tasks/car/car_layers_test.py | 82 - lingvo/tasks/car/car_lib.py | 596 - lingvo/tasks/car/car_lib_test.py | 466 - lingvo/tasks/car/detection_3d_lib.py | 863 - lingvo/tasks/car/detection_3d_lib_test.py | 528 - lingvo/tasks/car/detection_3d_metrics.py | 546 - lingvo/tasks/car/detection_3d_metrics_test.py | 109 - lingvo/tasks/car/detection_decoder.py | 220 - lingvo/tasks/car/detection_decoder_test.py | 120 - lingvo/tasks/car/evaluation_metadata.py | 102 - lingvo/tasks/car/geometry.py | 645 - lingvo/tasks/car/geometry_test.py | 458 - lingvo/tasks/car/input_extractor.py | 188 - lingvo/tasks/car/input_extractor_test.py | 45 - lingvo/tasks/car/input_preprocessors.py | 3450 -- lingvo/tasks/car/kitti_ap_metric.py | 255 - lingvo/tasks/car/kitti_decoder.py | 389 - lingvo/tasks/car/kitti_input_generator.py | 649 - lingvo/tasks/car/kitti_metadata.py | 86 - lingvo/tasks/car/lr_util.py | 105 - lingvo/tasks/car/lr_util_test.py | 72 - lingvo/tasks/car/ops/BUILD | 181 - lingvo/tasks/car/ops/__init__.py | 38 - .../tasks/car/ops/average_precision_3d_op.cc | 219 - .../car/ops/average_precision_3d_op_test.py | 131 - lingvo/tasks/car/ops/box_util.cc | 327 - lingvo/tasks/car/ops/box_util.h | 143 - lingvo/tasks/car/ops/car_ops.cc | 258 - lingvo/tasks/car/ops/image_metrics.cc | 156 - lingvo/tasks/car/ops/image_metrics.h | 468 - lingvo/tasks/car/ops/nms_3d_op.cc | 196 - lingvo/tasks/car/ops/nms_3d_op_test.py | 204 - lingvo/tasks/car/ops/pairwise_iou_op.cc | 72 - lingvo/tasks/car/ops/point_grid_op.cc | 192 - lingvo/tasks/car/ops/point_grid_op_test.py | 133 - lingvo/tasks/car/ops/ps_utils.cc | 501 - lingvo/tasks/car/ops/ps_utils.h | 103 - lingvo/tasks/car/ops/ps_utils_test.cc | 371 - lingvo/tasks/car/ops/sampling_ops.cc | 83 - lingvo/tasks/car/ops/sampling_ops_test.py | 160 - lingvo/tasks/car/params/BUILD | 57 - lingvo/tasks/car/params/kitti.py | 628 - lingvo/tasks/car/params/params.py | 22 - lingvo/tasks/car/params/waymo.py | 473 - lingvo/tasks/car/pillars.py | 665 - lingvo/tasks/car/point_detector.py | 343 - lingvo/tasks/car/pointnet.py | 236 - lingvo/tasks/car/pointnet_test.py | 75 - lingvo/tasks/car/starnet.py | 909 - lingvo/tasks/car/summary.py | 594 - lingvo/tasks/car/summary_test.py | 112 - lingvo/tasks/car/testdata/BUILD | 24 - .../car/testdata/kitti_raw_calib_testdata.txt | 7 - .../car/testdata/kitti_raw_label_testdata.txt | 7 - lingvo/tasks/car/tools/BUILD | 109 - lingvo/tasks/car/tools/__init__.py | 0 lingvo/tasks/car/tools/compare_params_main.py | 47 - .../car/tools/create_kitti_crop_dataset.py | 272 - .../tasks/car/tools/export_kitti_detection.py | 232 - lingvo/tasks/car/tools/kitti_data.py | 364 - lingvo/tasks/car/tools/kitti_data_test.py | 139 - lingvo/tasks/car/tools/kitti_exporter.py | 265 - lingvo/tasks/car/transform_util.py | 188 - lingvo/tasks/car/transform_util_test.py | 135 - lingvo/tasks/car/waymo/BUILD | 121 - lingvo/tasks/car/waymo/__init__.py | 0 .../car/waymo/export_to_submission_format.py | 138 - lingvo/tasks/car/waymo/tools/BUILD | 40 - lingvo/tasks/car/waymo/tools/__init__.py | 0 .../car/waymo/tools/generate_waymo_tf.py | 77 - .../car/waymo/tools/waymo_proto_to_tfe.py | 592 - lingvo/tasks/car/waymo/waymo_ap_metric.py | 456 - .../tasks/car/waymo/waymo_ap_metric_test.py | 130 - lingvo/tasks/car/waymo/waymo_decoder.py | 294 - lingvo/tasks/car/waymo/waymo_metadata.py | 92 - .../car/waymo/waymo_open_input_generator.py | 1000 - .../waymo/waymo_open_input_generator_test.py | 53 - lingvo/tasks/image/BUILD | 64 - lingvo/tasks/image/__init__.py | 0 lingvo/tasks/image/classifier.py | 335 - lingvo/tasks/image/classifier_test.py | 126 - lingvo/tasks/image/input_generator.py | 87 - lingvo/tasks/image/input_generator_test.py | 99 - lingvo/tasks/image/params/BUILD | 28 - lingvo/tasks/image/params/mnist.py | 66 - lingvo/tasks/image/params/params.py | 21 - lingvo/tasks/lm/BUILD | 104 - lingvo/tasks/lm/__init__.py | 0 lingvo/tasks/lm/input_generator.py | 92 - lingvo/tasks/lm/input_generator_test.py | 58 - lingvo/tasks/lm/layers.py | 1322 - lingvo/tasks/lm/layers_test.py | 972 - lingvo/tasks/lm/model.py | 288 - lingvo/tasks/lm/model_test.py | 170 - lingvo/tasks/lm/params/BUILD | 33 - lingvo/tasks/lm/params/one_billion_wds.py | 259 - lingvo/tasks/lm/params/params.py | 21 - lingvo/tasks/lm/testdata/BUILD | 16 - lingvo/tasks/lm/testdata/lm1b_100.txt | 100 - lingvo/tasks/lm/testdata/small_word_vocab.txt | 64 - lingvo/tasks/lm/tools/BUILD | 15 - lingvo/tasks/lm/tools/__init__.py | 0 lingvo/tasks/lm/tools/download_lm1b.py | 60 - lingvo/tasks/mt/BUILD | 284 - lingvo/tasks/mt/README.md | 322 - lingvo/tasks/mt/__init__.py | 0 lingvo/tasks/mt/base_config.py | 509 - lingvo/tasks/mt/data_augmenter.py | 106 - lingvo/tasks/mt/data_augmenter_test.py | 65 - lingvo/tasks/mt/decoder.py | 2708 -- lingvo/tasks/mt/decoder_test.py | 1374 - lingvo/tasks/mt/encoder.py | 924 - lingvo/tasks/mt/encoder_test.py | 570 - lingvo/tasks/mt/input_generator.py | 1056 - lingvo/tasks/mt/input_generator_test.py | 452 - lingvo/tasks/mt/layers.py | 190 - lingvo/tasks/mt/layers_test.py | 363 - lingvo/tasks/mt/model.py | 386 - lingvo/tasks/mt/model_test.py | 704 - lingvo/tasks/mt/params/BUILD | 30 - lingvo/tasks/mt/params/params.py | 22 - lingvo/tasks/mt/params/wmt14_en_de.py | 202 - lingvo/tasks/mt/params/wmtm16_en_de.py | 114 - lingvo/tasks/mt/testdata/BUILD | 29 - lingvo/tasks/mt/testdata/en_de.text | 2 - lingvo/tasks/mt/testdata/en_fr.tfrecord | Bin 140 -> 0 bytes .../translate_ende_mlperf.packed.tfrecord | Bin 4618479 -> 0 bytes ...translate_ende_wmt32k-train-00511-of-00512 | Bin 1408590 -> 0 bytes .../testdata/wmt14_ende_wpm_32k_test.tfrecord | Bin 100951 -> 0 bytes .../mt/testdata/wmt14_ende_wpm_32k_test.vocab | 32000 --------------- lingvo/tasks/mt/text_input.proto | 33 - lingvo/tasks/mt/tools/__init__.py | 0 .../tools/wmt14.01.download_moses_scripts.sh | 22 - .../tasks/mt/tools/wmt14.02.download_train.sh | 29 - .../mt/tools/wmt14.03.download_devtest.sh | 25 - .../tasks/mt/tools/wmt14.04.unpack_train.sh | 28 - .../tasks/mt/tools/wmt14.05.unpack_devtest.sh | 24 - .../tasks/mt/tools/wmt14.06.tokenize_train.sh | 44 - .../mt/tools/wmt14.07.tokenize_devtest.sh | 42 - .../mt/tools/wmt14.08.wpm_encode_train.sh | 40 - .../mt/tools/wmt14.09.wpm_encode_devtest.sh | 33 - lingvo/tasks/mt/tools/wmt14_get_data.sh | 27 - lingvo/tasks/mt/tools/wmt14_lib.sh | 65 - .../tools/wmtm16.01.download_moses_scripts.sh | 22 - .../tasks/mt/tools/wmtm16.02.download_data.sh | 29 - .../tasks/mt/tools/wmtm16.03.unpack_data.sh | 28 - .../tasks/mt/tools/wmtm16.04.tokenize_data.sh | 43 - .../mt/tools/wmtm16.05.wpm_encode_data.sh | 33 - lingvo/tasks/mt/tools/wmtm16_get_data.sh | 23 - lingvo/tasks/mt/tools/wmtm16_lib.sh | 64 - lingvo/tasks/mt/wpm-ende-2k.voc | 2000 - lingvo/tasks/mt/wpm-ende.voc | 32000 --------------- lingvo/tasks/punctuator/BUILD | 70 - lingvo/tasks/punctuator/__init__.py | 0 lingvo/tasks/punctuator/input_generator.py | 130 - .../tasks/punctuator/input_generator_test.py | 85 - lingvo/tasks/punctuator/model.py | 63 - lingvo/tasks/punctuator/model_test.py | 166 - lingvo/tasks/punctuator/params/BUILD | 36 - .../params/brown_corpus_wpm.16000.vocab | 15942 -------- lingvo/tasks/punctuator/params/codelab.py | 125 - lingvo/tasks/punctuator/params/params.py | 21 - lingvo/tasks/punctuator/tools/BUILD | 15 - lingvo/tasks/punctuator/tools/__init__.py | 0 .../punctuator/tools/download_brown_corpus.py | 140 - lingvo/tools/BUILD | 209 - lingvo/tools/__init__.py | 0 lingvo/tools/audio_lib.py | 150 - lingvo/tools/audio_lib_test.py | 90 - lingvo/tools/beam_utils.py | 115 - lingvo/tools/beam_utils_test.py | 64 - lingvo/tools/bpe_word_tokenizer.py | 102 - lingvo/tools/compare_params.py | 115 - lingvo/tools/compare_params_test.py | 45 - lingvo/tools/compute_stats.py | 113 - lingvo/tools/count_records.py | 69 - lingvo/tools/create_asr_features.py | 218 - .../create_peoples_speech_asr_features.py | 189 - lingvo/tools/generate_proto_def.cc | 69 - lingvo/tools/generate_tf_dot_protos.sh | 37 - lingvo/tools/gke_launch.py | 498 - lingvo/tools/keras2ckpt.py | 63 - lingvo/tools/print_tf_records.py | 114 - lingvo/tools/testdata/BUILD | 12 - lingvo/tools/testdata/gan_or_vae.16k.wav | Bin 101244 -> 0 bytes lingvo/tools/testdata/gan_or_vae.flac | Bin 90062 -> 0 bytes lingvo/tools/testdata/gan_or_vae.wav | Bin 151844 -> 0 bytes lingvo/tools/wpm_encode_file.py | 139 - lingvo/trainer.py | 1882 - lingvo/trainer_test.py | 454 - missing_files.json | 1 - pip_package/MANIFEST.in | 1 - pip_package/README.md | 74 - pip_package/build.Dockerfile | 39 - pip_package/build.sh | 95 - pip_package/build_pip_pkg.sh | 70 - pip_package/setup.py | 102 - publications.bib | 706 - 532 files changed, 312011 deletions(-) delete mode 100644 LICENSE delete mode 100644 PUBLICATIONS.md delete mode 100644 codelabs/introduction.ipynb delete mode 100644 docker/dev.dockerfile delete mode 100644 docker/lib.dockerfile delete mode 100755 docker/run_distributed.py delete mode 100644 docs/apidoc/conf.py delete mode 100644 docs/apidoc/index.rst delete mode 100755 docs/generate_apidoc.sh delete mode 100755 docs/single/build.sh delete mode 100644 docs/single/conf.py delete mode 100644 docs/single/index.rst delete mode 100644 lingvo/.style.yapf delete mode 100644 lingvo/BUILD delete mode 100644 lingvo/__init__.py delete mode 100644 lingvo/base_runner.py delete mode 100644 lingvo/base_trial.py delete mode 100644 lingvo/compat.py delete mode 100644 lingvo/compat_test.py delete mode 100644 lingvo/core/BUILD delete mode 100644 lingvo/core/__init__.py delete mode 100644 lingvo/core/adagraft.py delete mode 100644 lingvo/core/adagraft_test.py delete mode 100644 lingvo/core/attention.py delete mode 100644 lingvo/core/attention_test.py delete mode 100644 lingvo/core/attention_util.py delete mode 100644 lingvo/core/attention_util_test.py delete mode 100644 lingvo/core/base_decoder.py delete mode 100644 lingvo/core/base_input_generator.py delete mode 100644 lingvo/core/base_input_generator_test.py delete mode 100644 lingvo/core/base_layer.py delete mode 100644 lingvo/core/base_layer_test.py delete mode 100644 lingvo/core/base_model.py delete mode 100644 lingvo/core/base_model_params.py delete mode 100644 lingvo/core/base_model_params_test.py delete mode 100644 lingvo/core/base_model_test.py delete mode 100644 lingvo/core/batch_major_attention.py delete mode 100644 lingvo/core/batch_major_attention_test.py delete mode 100644 lingvo/core/batch_utils.py delete mode 100644 lingvo/core/batch_utils_test.py delete mode 100644 lingvo/core/beam_search_helper.py delete mode 100644 lingvo/core/beam_search_helper_test.py delete mode 100644 lingvo/core/bfloat16_variables.py delete mode 100644 lingvo/core/bfloat16_variables_test.py delete mode 100644 lingvo/core/bn_layers.py delete mode 100644 lingvo/core/build_data.py delete mode 100644 lingvo/core/builder.py delete mode 100644 lingvo/core/builder_layers.py delete mode 100644 lingvo/core/builder_layers_test.py delete mode 100644 lingvo/core/builder_test.py delete mode 100644 lingvo/core/checkpointer.py delete mode 100644 lingvo/core/checkpointer_test.py delete mode 100644 lingvo/core/cluster.py delete mode 100644 lingvo/core/cluster_factory.py delete mode 100644 lingvo/core/cluster_test.py delete mode 100644 lingvo/core/computation_cost.py delete mode 100644 lingvo/core/conformer_layer.py delete mode 100644 lingvo/core/conformer_layer_test.py delete mode 100644 lingvo/core/constants.py delete mode 100644 lingvo/core/conv_layers_builder.py delete mode 100644 lingvo/core/conv_layers_builder_test.py delete mode 100644 lingvo/core/conv_layers_with_time_padding.py delete mode 100644 lingvo/core/conv_layers_with_time_padding_test.py delete mode 100644 lingvo/core/datasource.py delete mode 100644 lingvo/core/datasource_test.py delete mode 100644 lingvo/core/decoder_lib.py delete mode 100644 lingvo/core/distillation_task.py delete mode 100644 lingvo/core/distributed_shampoo.py delete mode 100644 lingvo/core/distributed_shampoo_test.py delete mode 100644 lingvo/core/early_stop.py delete mode 100644 lingvo/core/early_stop_test.py delete mode 100644 lingvo/core/egdd.py delete mode 100644 lingvo/core/egdd_test.py delete mode 100644 lingvo/core/ema_test.py delete mode 100644 lingvo/core/generic_input.py delete mode 100644 lingvo/core/generic_input_test.py delete mode 100644 lingvo/core/gpipe.py delete mode 100644 lingvo/core/gpipe_test.py delete mode 100644 lingvo/core/gradient_combiner.py delete mode 100644 lingvo/core/hyperparams.proto delete mode 100644 lingvo/core/hyperparams.py delete mode 100644 lingvo/core/hyperparams_test.py delete mode 100644 lingvo/core/inference_graph.proto delete mode 100644 lingvo/core/inference_graph_exporter.py delete mode 100644 lingvo/core/inference_graph_exporter_test.py delete mode 100644 lingvo/core/input_generator_helper.py delete mode 100644 lingvo/core/input_generator_helper_test.py delete mode 100644 lingvo/core/insertion.py delete mode 100644 lingvo/core/insertion_test.py delete mode 100644 lingvo/core/inspect_utils.py delete mode 100644 lingvo/core/inspect_utils_test.py delete mode 100644 lingvo/core/layers.py delete mode 100644 lingvo/core/layers_test.py delete mode 100644 lingvo/core/layers_with_attention.py delete mode 100644 lingvo/core/layers_with_attention_test.py delete mode 100644 lingvo/core/layers_with_gpipe.py delete mode 100644 lingvo/core/layers_with_gpipe_test.py delete mode 100644 lingvo/core/learner.py delete mode 100644 lingvo/core/learner_test.py delete mode 100644 lingvo/core/lstm_frnn_layer.py delete mode 100644 lingvo/core/lstm_frnn_layer_test.py delete mode 100644 lingvo/core/matrix_functions.py delete mode 100644 lingvo/core/matrix_functions_test.py delete mode 100644 lingvo/core/metrics.py delete mode 100644 lingvo/core/metrics_test.py delete mode 100644 lingvo/core/ml_perf_bleu_metric.py delete mode 100644 lingvo/core/ml_perf_bleu_metric_test.py delete mode 100644 lingvo/core/ml_perf_log.py delete mode 100644 lingvo/core/ml_perf_log_test.py delete mode 100644 lingvo/core/ml_perf_tokenizer.py delete mode 100644 lingvo/core/model_helper.py delete mode 100644 lingvo/core/moe_layers.py delete mode 100644 lingvo/core/multitask_model.py delete mode 100644 lingvo/core/multitask_model_test.py delete mode 100644 lingvo/core/ops/BUILD delete mode 100644 lingvo/core/ops/__init__.py delete mode 100644 lingvo/core/ops/ascii_tokenizer.cc delete mode 100644 lingvo/core/ops/ascii_tokenizer.h delete mode 100644 lingvo/core/ops/assert_kernels.cc delete mode 100644 lingvo/core/ops/assert_ops_test.py delete mode 100644 lingvo/core/ops/beam_search_step_op_kernels.cc delete mode 100644 lingvo/core/ops/beam_search_step_op_kernels.h delete mode 100644 lingvo/core/ops/beam_search_step_op_test.py delete mode 100644 lingvo/core/ops/beam_search_step_op_top_k_test.cc delete mode 100644 lingvo/core/ops/best_step_op_kernels.cc delete mode 100644 lingvo/core/ops/best_step_op_test.py delete mode 100644 lingvo/core/ops/chain_record_yielder.cc delete mode 100644 lingvo/core/ops/chain_record_yielder.h delete mode 100644 lingvo/core/ops/chain_record_yielder_test.cc delete mode 100644 lingvo/core/ops/functional_ops_kernels.cc delete mode 100644 lingvo/core/ops/functional_ops_test.py delete mode 100644 lingvo/core/ops/generic_input_op_kernels.cc delete mode 100644 lingvo/core/ops/hyps.proto delete mode 100644 lingvo/core/ops/input_common.cc delete mode 100644 lingvo/core/ops/input_common.h delete mode 100644 lingvo/core/ops/mass_op.cc delete mode 100644 lingvo/core/ops/mass_op_test.py delete mode 100644 lingvo/core/ops/ml_perf_subword_op.cc delete mode 100644 lingvo/core/ops/ml_perf_subword_op.h delete mode 100644 lingvo/core/ops/pack_ops.cc delete mode 100644 lingvo/core/ops/pack_ops_test.py delete mode 100644 lingvo/core/ops/preconditioner_captain.cc delete mode 100644 lingvo/core/ops/preconditioner_captain.h delete mode 100644 lingvo/core/ops/preconditioner_op_kernels.cc delete mode 100644 lingvo/core/ops/preconditioner_op_kernels_test.py delete mode 100644 lingvo/core/ops/random_ops_kernels.cc delete mode 100644 lingvo/core/ops/random_ops_test.py delete mode 100644 lingvo/core/ops/record.proto delete mode 100644 lingvo/core/ops/record_batcher.cc delete mode 100644 lingvo/core/ops/record_batcher.h delete mode 100644 lingvo/core/ops/record_batcher_test.cc delete mode 100644 lingvo/core/ops/record_debug.cc delete mode 100644 lingvo/core/ops/record_yielder.cc delete mode 100644 lingvo/core/ops/record_yielder.h delete mode 100644 lingvo/core/ops/record_yielder_test.cc delete mode 100644 lingvo/core/ops/rope.h delete mode 100644 lingvo/core/ops/sequential_record_yielder.cc delete mode 100644 lingvo/core/ops/sequential_record_yielder.h delete mode 100644 lingvo/core/ops/simple_vocab.cc delete mode 100644 lingvo/core/ops/simple_vocab.h delete mode 100644 lingvo/core/ops/simple_vocab_test.py delete mode 100644 lingvo/core/ops/static_map_op.cc delete mode 100644 lingvo/core/ops/static_map_op_test.py delete mode 100644 lingvo/core/ops/testdata/BUILD delete mode 100644 lingvo/core/ops/testdata/bpe_codes.vocab delete mode 100644 lingvo/core/ops/testdata/bpe_words.vocab delete mode 100644 lingvo/core/ops/testdata/events.out.tfevents.test delete mode 100644 lingvo/core/ops/testdata/history.txt delete mode 100644 lingvo/core/ops/testdata/history_bleu.txt delete mode 100644 lingvo/core/ops/testdata/mlperf.ende.subwords.vocab delete mode 100644 lingvo/core/ops/testdata/test_ngrams.txt delete mode 100644 lingvo/core/ops/testdata/test_vocab.txt delete mode 100644 lingvo/core/ops/testdata/wmt/sm18.txt delete mode 100644 lingvo/core/ops/text_packing.cc delete mode 100644 lingvo/core/ops/text_packing.h delete mode 100644 lingvo/core/ops/text_packing_test.cc delete mode 100644 lingvo/core/ops/tokenizer_op_headers.h delete mode 100644 lingvo/core/ops/tokenizer_ops_kernels.cc delete mode 100644 lingvo/core/ops/tokenizer_ops_test.py delete mode 100644 lingvo/core/ops/versioned_file_set.proto delete mode 100644 lingvo/core/ops/weighted_mix_record_yielder.cc delete mode 100644 lingvo/core/ops/weighted_mix_record_yielder.h delete mode 100644 lingvo/core/ops/weighted_mix_record_yielder_test.cc delete mode 100644 lingvo/core/ops/x_ops.cc delete mode 100644 lingvo/core/ops/x_ops_helper.h delete mode 100644 lingvo/core/ops/yielder_test_helper.cc delete mode 100644 lingvo/core/ops/yielder_test_helper.h delete mode 100644 lingvo/core/optimizer.py delete mode 100644 lingvo/core/optimizer_test.py delete mode 100644 lingvo/core/plot.py delete mode 100644 lingvo/core/plot_test.py delete mode 100644 lingvo/core/predictor.py delete mode 100644 lingvo/core/predictor_runner_base.py delete mode 100644 lingvo/core/predictor_test.py delete mode 100644 lingvo/core/program.py delete mode 100644 lingvo/core/pruning_utils.py delete mode 100644 lingvo/core/py_utils.py delete mode 100644 lingvo/core/py_utils_test.py delete mode 100644 lingvo/core/quant_utils.py delete mode 100644 lingvo/core/quant_utils_test.py delete mode 100644 lingvo/core/recurrent.py delete mode 100644 lingvo/core/recurrent_gpu_test.py delete mode 100644 lingvo/core/recurrent_test.py delete mode 100644 lingvo/core/retry.py delete mode 100644 lingvo/core/retry_test.py delete mode 100644 lingvo/core/reversible_layers.py delete mode 100644 lingvo/core/reversible_layers_test.py delete mode 100644 lingvo/core/rnn_cell.py delete mode 100644 lingvo/core/rnn_cell_test.py delete mode 100644 lingvo/core/rnn_layers.py delete mode 100644 lingvo/core/rnn_layers_test.py delete mode 100644 lingvo/core/saver.py delete mode 100644 lingvo/core/saver_test.py delete mode 100644 lingvo/core/schedule.py delete mode 100644 lingvo/core/schedule_test.py delete mode 100644 lingvo/core/scorers.py delete mode 100644 lingvo/core/scorers_test.py delete mode 100644 lingvo/core/self_attention_layer.py delete mode 100644 lingvo/core/self_attention_layer_test.py delete mode 100644 lingvo/core/sendrecv.py delete mode 100644 lingvo/core/sendrecv_test.py delete mode 100644 lingvo/core/spectrum_augmenter.py delete mode 100644 lingvo/core/spectrum_augmenter_on_device.py delete mode 100644 lingvo/core/spectrum_augmenter_on_device_test.py delete mode 100644 lingvo/core/spectrum_augmenter_test.py delete mode 100644 lingvo/core/step.py delete mode 100644 lingvo/core/step_test.py delete mode 100644 lingvo/core/steps/BUILD delete mode 100644 lingvo/core/steps/__init__.py delete mode 100644 lingvo/core/steps/attention_steps.py delete mode 100644 lingvo/core/steps/attention_steps_test.py delete mode 100644 lingvo/core/steps/embedding_steps.py delete mode 100644 lingvo/core/steps/embedding_steps_test.py delete mode 100644 lingvo/core/steps/rnn_steps.py delete mode 100644 lingvo/core/steps/rnn_steps_test.py delete mode 100644 lingvo/core/summary_utils.py delete mode 100644 lingvo/core/summary_utils_test.py delete mode 100644 lingvo/core/symbolic.py delete mode 100644 lingvo/core/symbolic_test.py delete mode 100644 lingvo/core/target_sequence_sampler.py delete mode 100644 lingvo/core/target_sequence_sampler_test.py delete mode 100644 lingvo/core/task_scheduler.py delete mode 100644 lingvo/core/task_scheduler_test.py delete mode 100644 lingvo/core/test_helper.py delete mode 100644 lingvo/core/test_trainer_utils.py delete mode 100644 lingvo/core/test_utils.py delete mode 100644 lingvo/core/test_utils_test.py delete mode 100644 lingvo/core/testdata/BUILD delete mode 100644 lingvo/core/testdata/lenet_test_model.data-00000-of-00001 delete mode 100644 lingvo/core/testdata/lenet_test_model.index delete mode 100644 lingvo/core/tokenizers.py delete mode 100644 lingvo/core/tpu_embedding_layers.py delete mode 100644 lingvo/core/tpu_summary.py delete mode 100644 lingvo/core/tpu_summary_test.py delete mode 100644 lingvo/core/trainer_test_utils.py delete mode 100644 lingvo/core/tshape.py delete mode 100644 lingvo/core/tshape_test.py delete mode 100644 lingvo/core/wpm_encoder.py delete mode 100644 lingvo/core/wpm_encoder_test.py delete mode 100644 lingvo/core/wpm_tokenizer_test.py delete mode 100644 lingvo/datasets.py delete mode 100644 lingvo/executor.py delete mode 100644 lingvo/ipython_kernel.py delete mode 100644 lingvo/lingvo.bzl delete mode 100644 lingvo/model_import_test.py delete mode 100644 lingvo/model_imports.py delete mode 100644 lingvo/model_registry.py delete mode 100644 lingvo/model_registry_test.py delete mode 100644 lingvo/models_test.py delete mode 100644 lingvo/models_test_helper.py delete mode 100644 lingvo/repo.bzl delete mode 100644 lingvo/tasks/BUILD delete mode 100644 lingvo/tasks/__init__.py delete mode 100644 lingvo/tasks/asr/BUILD delete mode 100644 lingvo/tasks/asr/__init__.py delete mode 100644 lingvo/tasks/asr/align_input_generator.py delete mode 100644 lingvo/tasks/asr/blocks.py delete mode 100644 lingvo/tasks/asr/contextualizer_base.py delete mode 100644 lingvo/tasks/asr/ctc_model.py delete mode 100644 lingvo/tasks/asr/ctc_model_test.py delete mode 100644 lingvo/tasks/asr/decoder.py delete mode 100644 lingvo/tasks/asr/decoder_test.py delete mode 100644 lingvo/tasks/asr/decoder_utils.py delete mode 100644 lingvo/tasks/asr/decoder_utils_test.py delete mode 100644 lingvo/tasks/asr/encoder.py delete mode 100644 lingvo/tasks/asr/encoder_test.py delete mode 100644 lingvo/tasks/asr/encoder_v2.py delete mode 100644 lingvo/tasks/asr/frontend.py delete mode 100644 lingvo/tasks/asr/frontend_test.py delete mode 100644 lingvo/tasks/asr/fusion.py delete mode 100644 lingvo/tasks/asr/input_generator.py delete mode 100644 lingvo/tasks/asr/input_generator_test.py delete mode 100644 lingvo/tasks/asr/mlperf_compat_frontend.py delete mode 100644 lingvo/tasks/asr/model.py delete mode 100644 lingvo/tasks/asr/model_test.py delete mode 100644 lingvo/tasks/asr/model_test_input_generator.py delete mode 100644 lingvo/tasks/asr/params/BUILD delete mode 100644 lingvo/tasks/asr/params/README.md delete mode 100644 lingvo/tasks/asr/params/inference_only.py delete mode 100644 lingvo/tasks/asr/params/librispeech.py delete mode 100644 lingvo/tasks/asr/params/librispeech_ctc.py delete mode 100644 lingvo/tasks/asr/params/params.py delete mode 100644 lingvo/tasks/asr/params/peoplesspeech_ctc.py delete mode 100644 lingvo/tasks/asr/rnnt_model.py delete mode 100644 lingvo/tasks/asr/rnnt_prediction.py delete mode 100644 lingvo/tasks/asr/tools/BUILD delete mode 100644 lingvo/tasks/asr/tools/__init__.py delete mode 100755 lingvo/tasks/asr/tools/librispeech.01.download_train.sh delete mode 100755 lingvo/tasks/asr/tools/librispeech.02.download_devtest.sh delete mode 100755 lingvo/tasks/asr/tools/librispeech.03.parameterize_train.sh delete mode 100755 lingvo/tasks/asr/tools/librispeech.04.parameterize_devtest.sh delete mode 100644 lingvo/tasks/asr/tools/librispeech.05.train_ctc.sh delete mode 100755 lingvo/tasks/asr/tools/librispeech_get_data.sh delete mode 100755 lingvo/tasks/asr/tools/librispeech_lib.sh delete mode 100644 lingvo/tasks/asr/tools/qsub_script.sh delete mode 100755 lingvo/tasks/asr/tools/run_model.sh delete mode 100644 lingvo/tasks/asr/tools/simple_wer.py delete mode 100644 lingvo/tasks/asr/tools/simple_wer_test.py delete mode 100644 lingvo/tasks/asr/tools/simple_wer_v2.py delete mode 100644 lingvo/tasks/asr/tools/simple_wer_v2_test.py delete mode 100644 lingvo/tasks/asr/wpm_16k_librispeech.vocab delete mode 100644 lingvo/tasks/car/BUILD delete mode 100644 lingvo/tasks/car/README.md delete mode 100644 lingvo/tasks/car/__init__.py delete mode 100644 lingvo/tasks/car/ap_metric.py delete mode 100644 lingvo/tasks/car/base_decoder.py delete mode 100644 lingvo/tasks/car/base_extractor.py delete mode 100644 lingvo/tasks/car/breakdown_metric.py delete mode 100644 lingvo/tasks/car/breakdown_metric_test.py delete mode 100644 lingvo/tasks/car/builder_lib.py delete mode 100644 lingvo/tasks/car/builder_lib_test.py delete mode 100644 lingvo/tasks/car/calibration_processing.py delete mode 100644 lingvo/tasks/car/calibration_processing_test.py delete mode 100644 lingvo/tasks/car/car_layers.py delete mode 100644 lingvo/tasks/car/car_layers_test.py delete mode 100644 lingvo/tasks/car/car_lib.py delete mode 100644 lingvo/tasks/car/car_lib_test.py delete mode 100644 lingvo/tasks/car/detection_3d_lib.py delete mode 100644 lingvo/tasks/car/detection_3d_lib_test.py delete mode 100644 lingvo/tasks/car/detection_3d_metrics.py delete mode 100644 lingvo/tasks/car/detection_3d_metrics_test.py delete mode 100644 lingvo/tasks/car/detection_decoder.py delete mode 100644 lingvo/tasks/car/detection_decoder_test.py delete mode 100644 lingvo/tasks/car/evaluation_metadata.py delete mode 100644 lingvo/tasks/car/geometry.py delete mode 100644 lingvo/tasks/car/geometry_test.py delete mode 100644 lingvo/tasks/car/input_extractor.py delete mode 100644 lingvo/tasks/car/input_extractor_test.py delete mode 100644 lingvo/tasks/car/input_preprocessors.py delete mode 100644 lingvo/tasks/car/kitti_ap_metric.py delete mode 100644 lingvo/tasks/car/kitti_decoder.py delete mode 100644 lingvo/tasks/car/kitti_input_generator.py delete mode 100644 lingvo/tasks/car/kitti_metadata.py delete mode 100644 lingvo/tasks/car/lr_util.py delete mode 100644 lingvo/tasks/car/lr_util_test.py delete mode 100644 lingvo/tasks/car/ops/BUILD delete mode 100644 lingvo/tasks/car/ops/__init__.py delete mode 100644 lingvo/tasks/car/ops/average_precision_3d_op.cc delete mode 100644 lingvo/tasks/car/ops/average_precision_3d_op_test.py delete mode 100644 lingvo/tasks/car/ops/box_util.cc delete mode 100644 lingvo/tasks/car/ops/box_util.h delete mode 100644 lingvo/tasks/car/ops/car_ops.cc delete mode 100644 lingvo/tasks/car/ops/image_metrics.cc delete mode 100644 lingvo/tasks/car/ops/image_metrics.h delete mode 100644 lingvo/tasks/car/ops/nms_3d_op.cc delete mode 100644 lingvo/tasks/car/ops/nms_3d_op_test.py delete mode 100644 lingvo/tasks/car/ops/pairwise_iou_op.cc delete mode 100644 lingvo/tasks/car/ops/point_grid_op.cc delete mode 100644 lingvo/tasks/car/ops/point_grid_op_test.py delete mode 100644 lingvo/tasks/car/ops/ps_utils.cc delete mode 100644 lingvo/tasks/car/ops/ps_utils.h delete mode 100644 lingvo/tasks/car/ops/ps_utils_test.cc delete mode 100644 lingvo/tasks/car/ops/sampling_ops.cc delete mode 100644 lingvo/tasks/car/ops/sampling_ops_test.py delete mode 100644 lingvo/tasks/car/params/BUILD delete mode 100644 lingvo/tasks/car/params/kitti.py delete mode 100644 lingvo/tasks/car/params/params.py delete mode 100644 lingvo/tasks/car/params/waymo.py delete mode 100644 lingvo/tasks/car/pillars.py delete mode 100644 lingvo/tasks/car/point_detector.py delete mode 100644 lingvo/tasks/car/pointnet.py delete mode 100644 lingvo/tasks/car/pointnet_test.py delete mode 100644 lingvo/tasks/car/starnet.py delete mode 100644 lingvo/tasks/car/summary.py delete mode 100644 lingvo/tasks/car/summary_test.py delete mode 100644 lingvo/tasks/car/testdata/BUILD delete mode 100644 lingvo/tasks/car/testdata/kitti_raw_calib_testdata.txt delete mode 100644 lingvo/tasks/car/testdata/kitti_raw_label_testdata.txt delete mode 100644 lingvo/tasks/car/tools/BUILD delete mode 100644 lingvo/tasks/car/tools/__init__.py delete mode 100644 lingvo/tasks/car/tools/compare_params_main.py delete mode 100644 lingvo/tasks/car/tools/create_kitti_crop_dataset.py delete mode 100644 lingvo/tasks/car/tools/export_kitti_detection.py delete mode 100644 lingvo/tasks/car/tools/kitti_data.py delete mode 100644 lingvo/tasks/car/tools/kitti_data_test.py delete mode 100644 lingvo/tasks/car/tools/kitti_exporter.py delete mode 100644 lingvo/tasks/car/transform_util.py delete mode 100644 lingvo/tasks/car/transform_util_test.py delete mode 100644 lingvo/tasks/car/waymo/BUILD delete mode 100644 lingvo/tasks/car/waymo/__init__.py delete mode 100644 lingvo/tasks/car/waymo/export_to_submission_format.py delete mode 100644 lingvo/tasks/car/waymo/tools/BUILD delete mode 100644 lingvo/tasks/car/waymo/tools/__init__.py delete mode 100644 lingvo/tasks/car/waymo/tools/generate_waymo_tf.py delete mode 100644 lingvo/tasks/car/waymo/tools/waymo_proto_to_tfe.py delete mode 100644 lingvo/tasks/car/waymo/waymo_ap_metric.py delete mode 100644 lingvo/tasks/car/waymo/waymo_ap_metric_test.py delete mode 100644 lingvo/tasks/car/waymo/waymo_decoder.py delete mode 100644 lingvo/tasks/car/waymo/waymo_metadata.py delete mode 100644 lingvo/tasks/car/waymo/waymo_open_input_generator.py delete mode 100644 lingvo/tasks/car/waymo/waymo_open_input_generator_test.py delete mode 100644 lingvo/tasks/image/BUILD delete mode 100644 lingvo/tasks/image/__init__.py delete mode 100644 lingvo/tasks/image/classifier.py delete mode 100644 lingvo/tasks/image/classifier_test.py delete mode 100644 lingvo/tasks/image/input_generator.py delete mode 100644 lingvo/tasks/image/input_generator_test.py delete mode 100644 lingvo/tasks/image/params/BUILD delete mode 100644 lingvo/tasks/image/params/mnist.py delete mode 100644 lingvo/tasks/image/params/params.py delete mode 100644 lingvo/tasks/lm/BUILD delete mode 100644 lingvo/tasks/lm/__init__.py delete mode 100644 lingvo/tasks/lm/input_generator.py delete mode 100644 lingvo/tasks/lm/input_generator_test.py delete mode 100644 lingvo/tasks/lm/layers.py delete mode 100644 lingvo/tasks/lm/layers_test.py delete mode 100644 lingvo/tasks/lm/model.py delete mode 100644 lingvo/tasks/lm/model_test.py delete mode 100644 lingvo/tasks/lm/params/BUILD delete mode 100644 lingvo/tasks/lm/params/one_billion_wds.py delete mode 100644 lingvo/tasks/lm/params/params.py delete mode 100644 lingvo/tasks/lm/testdata/BUILD delete mode 100644 lingvo/tasks/lm/testdata/lm1b_100.txt delete mode 100644 lingvo/tasks/lm/testdata/small_word_vocab.txt delete mode 100644 lingvo/tasks/lm/tools/BUILD delete mode 100644 lingvo/tasks/lm/tools/__init__.py delete mode 100644 lingvo/tasks/lm/tools/download_lm1b.py delete mode 100644 lingvo/tasks/mt/BUILD delete mode 100644 lingvo/tasks/mt/README.md delete mode 100644 lingvo/tasks/mt/__init__.py delete mode 100644 lingvo/tasks/mt/base_config.py delete mode 100644 lingvo/tasks/mt/data_augmenter.py delete mode 100644 lingvo/tasks/mt/data_augmenter_test.py delete mode 100644 lingvo/tasks/mt/decoder.py delete mode 100644 lingvo/tasks/mt/decoder_test.py delete mode 100644 lingvo/tasks/mt/encoder.py delete mode 100644 lingvo/tasks/mt/encoder_test.py delete mode 100644 lingvo/tasks/mt/input_generator.py delete mode 100644 lingvo/tasks/mt/input_generator_test.py delete mode 100644 lingvo/tasks/mt/layers.py delete mode 100644 lingvo/tasks/mt/layers_test.py delete mode 100644 lingvo/tasks/mt/model.py delete mode 100644 lingvo/tasks/mt/model_test.py delete mode 100644 lingvo/tasks/mt/params/BUILD delete mode 100644 lingvo/tasks/mt/params/params.py delete mode 100644 lingvo/tasks/mt/params/wmt14_en_de.py delete mode 100644 lingvo/tasks/mt/params/wmtm16_en_de.py delete mode 100644 lingvo/tasks/mt/testdata/BUILD delete mode 100644 lingvo/tasks/mt/testdata/en_de.text delete mode 100644 lingvo/tasks/mt/testdata/en_fr.tfrecord delete mode 100644 lingvo/tasks/mt/testdata/translate_ende_mlperf.packed.tfrecord delete mode 100644 lingvo/tasks/mt/testdata/translate_ende_wmt32k-train-00511-of-00512 delete mode 100644 lingvo/tasks/mt/testdata/wmt14_ende_wpm_32k_test.tfrecord delete mode 100644 lingvo/tasks/mt/testdata/wmt14_ende_wpm_32k_test.vocab delete mode 100644 lingvo/tasks/mt/text_input.proto delete mode 100644 lingvo/tasks/mt/tools/__init__.py delete mode 100755 lingvo/tasks/mt/tools/wmt14.01.download_moses_scripts.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14.02.download_train.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14.03.download_devtest.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14.04.unpack_train.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14.05.unpack_devtest.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14.06.tokenize_train.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14.07.tokenize_devtest.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14.08.wpm_encode_train.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14.09.wpm_encode_devtest.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14_get_data.sh delete mode 100755 lingvo/tasks/mt/tools/wmt14_lib.sh delete mode 100755 lingvo/tasks/mt/tools/wmtm16.01.download_moses_scripts.sh delete mode 100755 lingvo/tasks/mt/tools/wmtm16.02.download_data.sh delete mode 100755 lingvo/tasks/mt/tools/wmtm16.03.unpack_data.sh delete mode 100755 lingvo/tasks/mt/tools/wmtm16.04.tokenize_data.sh delete mode 100755 lingvo/tasks/mt/tools/wmtm16.05.wpm_encode_data.sh delete mode 100755 lingvo/tasks/mt/tools/wmtm16_get_data.sh delete mode 100755 lingvo/tasks/mt/tools/wmtm16_lib.sh delete mode 100644 lingvo/tasks/mt/wpm-ende-2k.voc delete mode 100644 lingvo/tasks/mt/wpm-ende.voc delete mode 100644 lingvo/tasks/punctuator/BUILD delete mode 100644 lingvo/tasks/punctuator/__init__.py delete mode 100644 lingvo/tasks/punctuator/input_generator.py delete mode 100644 lingvo/tasks/punctuator/input_generator_test.py delete mode 100644 lingvo/tasks/punctuator/model.py delete mode 100644 lingvo/tasks/punctuator/model_test.py delete mode 100644 lingvo/tasks/punctuator/params/BUILD delete mode 100644 lingvo/tasks/punctuator/params/brown_corpus_wpm.16000.vocab delete mode 100644 lingvo/tasks/punctuator/params/codelab.py delete mode 100644 lingvo/tasks/punctuator/params/params.py delete mode 100644 lingvo/tasks/punctuator/tools/BUILD delete mode 100644 lingvo/tasks/punctuator/tools/__init__.py delete mode 100644 lingvo/tasks/punctuator/tools/download_brown_corpus.py delete mode 100644 lingvo/tools/BUILD delete mode 100644 lingvo/tools/__init__.py delete mode 100644 lingvo/tools/audio_lib.py delete mode 100644 lingvo/tools/audio_lib_test.py delete mode 100644 lingvo/tools/beam_utils.py delete mode 100644 lingvo/tools/beam_utils_test.py delete mode 100644 lingvo/tools/bpe_word_tokenizer.py delete mode 100644 lingvo/tools/compare_params.py delete mode 100644 lingvo/tools/compare_params_test.py delete mode 100644 lingvo/tools/compute_stats.py delete mode 100644 lingvo/tools/count_records.py delete mode 100644 lingvo/tools/create_asr_features.py delete mode 100644 lingvo/tools/create_peoples_speech_asr_features.py delete mode 100644 lingvo/tools/generate_proto_def.cc delete mode 100755 lingvo/tools/generate_tf_dot_protos.sh delete mode 100644 lingvo/tools/gke_launch.py delete mode 100644 lingvo/tools/keras2ckpt.py delete mode 100644 lingvo/tools/print_tf_records.py delete mode 100644 lingvo/tools/testdata/BUILD delete mode 100644 lingvo/tools/testdata/gan_or_vae.16k.wav delete mode 100644 lingvo/tools/testdata/gan_or_vae.flac delete mode 100644 lingvo/tools/testdata/gan_or_vae.wav delete mode 100644 lingvo/tools/wpm_encode_file.py delete mode 100644 lingvo/trainer.py delete mode 100644 lingvo/trainer_test.py delete mode 100644 missing_files.json delete mode 100644 pip_package/MANIFEST.in delete mode 100644 pip_package/README.md delete mode 100644 pip_package/build.Dockerfile delete mode 100755 pip_package/build.sh delete mode 100755 pip_package/build_pip_pkg.sh delete mode 100644 pip_package/setup.py delete mode 100644 publications.bib diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 7bf9011a..00000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2017, The TensorFlow Authors. - - 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. diff --git a/PUBLICATIONS.md b/PUBLICATIONS.md deleted file mode 100644 index f8ac7eed..00000000 --- a/PUBLICATIONS.md +++ /dev/null @@ -1,1114 +0,0 @@ -# List of publications using Lingvo. - - - -## Translation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-[1] - -Y. Wu, M. Schuster, Z. Chen, Q. V. Le, M. Norouzi, W. Macherey, M. Krikun, - Y. Cao, Q. Gao, K. Macherey, J. Klingner, A. Shah, M. Johnson, X. Liu, - L. Kaiser, S. Gouws, Y. Kato, T. Kudo, H. Kazawa, K. Stevens, G. Kurian, - N. Patil, W. Wang, C. Young, J. Smith, J. Riesa, A. Rudnick, O. Vinyals, - G. Corrado, M. Hughes, and J. Dean, “Google's neural machine translation - system: Bridging the gap between human and machine translation,” tech. rep., - 2016. -[ pdf ] - -
-[2] - -M. Johnson, M. Schuster, Q. V. Le, M. Krikun, Y. Wu, Z. Chen, N. Thorat, - F. Viégas, M. Wattenberg, G. Corrado, M. Hughes, and J. Dean, - “Google's multilingual neural machine translation system: Enabling - zero-shot translation,” Transactions of the Association for - Computational Linguistics, vol. 5, pp. 339--351, 2017. -[ DOI | -pdf ] - -
-[3] - -A. Eriguchi, M. Johnson, O. Firat, H. Kazawa, and W. Macherey, “Zero-shot - cross-lingual classification using multilingual neural machine translation,” - arXiv preprint arXiv:1809.04686, 2018. -[ pdf ] - -
-[4] - -A. Bapna, M. X. Chen, O. Firat, Y. Cao, and Y. Wu, “Training deeper neural - machine translation models with transparent attention,” in Proc. - Conference on Empirical Methods in Natural Language Processing (EMNLP), - 2018. -[ pdf ] - -
-[5] - -C. Cherry, G. Foster, A. Bapna, O. Firat, and W. Macherey, “Revisiting - character-based neural machine translation with capacity and compression,” - in Proc. Conference on Empirical Methods in Natural Language Processing - (EMNLP), 2018. -[ pdf ] - -
-[6] - -M. X. Chen, O. Firat, A. Bapna, M. Johnson, W. Macherey, G. Foster, L. Jones, - M. Schuster, N. Shazeer, N. Parmar, A. Vaswani, J. Uszkoreit, L. Kaiser, - Z. Chen, Y. Wu, and M. Hughes, “The Best of Both Worlds: Combining Recent - Advances in Neural Machine Translation,” in Proc. Annual Meeting of - the Association for Computational Linguistics (ACL), 2018. -[ pdf ] - -
-[7] - -J. Kuczmarski and M. Johnson, “Gender-aware natural language translation,” - 2018. -[ pdf ] - -
-[8] - -R. Aharoni, M. Johnson, and O. Firat, “Massively multilingual neural machine - translation,” 2019. -[ pdf ] - -
-[9] - -J. Luo, Y. Cao, and R. Barzilay, “Neural decipherment via minimum-cost flow: - From ugaritic to linear b,” 2019. -[ http ] - -
-[10] - -N. Arivazhagan, C. Cherry, W. Macherey, C.-C. Chiu, S. Yavuz, R. Pang, W. Li, - and C. Raffel, “Monotonic infinite lookback attention for simultaneous - machine translation,” in Proc. Annual Meeting of the Association for - Computational Linguistics (ACL), 2019. -[ http ] - -
-[11] - -M. Freitag, I. Caswell, and S. Roy, “Ape at scale and its implications on mt - evaluation biases,” 2019. -[ pdf | -http ] - -
-[12] - -N. Arivazhagan, A. Bapna, O. Firat, D. Lepikhin, M. Johnson, M. Krikun, M. X. - Chen, Y. Cao, G. Foster, C. Cherry, W. Macherey, Z. Chen, and Y. Wu, - “Massively multilingual neural machine translation in the wild: Findings and - challenges,” 2019. -[ arXiv | -http ] - -
-[13] - -Y. Huang, Y. Cheng, A. Bapna, O. Firat, M. X. Chen, D. Chen, H. Lee, J. Ngiam, - Q. V. Le, Y. Wu, and Z. Chen, “Gpipe: Efficient training of giant neural - networks using pipeline parallelism,” in Advances in Neural Information - Processing Systems, 2019. -[ http ] - -
- -## Speech recognition - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-[1] - -C.-C.Chiu, T. N. Sainath, Y. Wu, R. Prabhavalkar, P. Nguyen, Z. Chen, - A. Kannan, R. J. Weiss, K. Rao, K. Gonina, N. Jaitly, B. Li, J. Chorowski, - and M. Bacchiani, “State-of-the-art speech recognition with - sequence-to-sequence models,” in Proc. IEEE International Conference - on Acoustics, Speech, and Signal Processing (ICASSP), 2018. -[ pdf ] - -
-[2] - -S. Toshniwal, T. N. Sainath, R. J. Weiss, B. Li, P. Moreno, E. Weinstein, and - K. Rao, “Multilingual speech recognition with a single end-to-end model,” - in Proc. IEEE International Conference on Acoustics, Speech, and - Signal Processing (ICASSP), 2018. -[ pdf ] - -
-[3] - -B. Li, T. N. Sainath, K. Sim, M. Bacchiani, E. Weinstein, P. Nguyen, Z. Chen, - Y. Wu, and K. Rao, “Multi-Dialect Speech Recognition With a Single - Sequence-to-Sequence Model,” in Proc. IEEE International Conference - on Acoustics, Speech, and Signal Processing (ICASSP), 2018. -[ pdf ] - -
-[4] - -T. N. Sainath, P. Prabhavalkar, S. Kumar, S. Lee, A. Kannan, D. Rybach, - V. Schogol, P. Nguyen, B. Li, Y. Wu, Z. Chen, and C. C. Chiu, “No Need for - a Lexicon? Evaluating the Value of the Pronunciation Lexica in End-to-End - Models,” in Proc. IEEE International Conference on Acoustics, - Speech, and Signal Processing (ICASSP), 2018. -[ pdf ] - -
-[5] - -D. Lawson, C. C. Chiu, G. Tucker, C. Raffel, K. Swersky, and N. Jaitly, - “Learning hard alignments with variational inference,” in Proc. - IEEE International Conference on Acoustics, Speech, and Signal Processing - (ICASSP), 2018. -[ pdf ] - -
-[6] - -A. Kannan, Y. Wu, P. Nguyen, T. N. Sainath, Z. Chen, and R. Prabhavalkar, “An - analysis of incorporating an external language model into a - sequence-to-sequence model,” in Proc. IEEE International Conference - on Acoustics, Speech, and Signal Processing (ICASSP), 2018. -[ pdf ] - -
-[7] - -R. Prabhavalkar, T. N. Sainath, Y. Wu, P. Nguyen, Z. Chen, C. C. Chiu, and - A. Kannan, “Minimum Word Error Rate Training for Attention-based - Sequence-to-sequence Models,” in Proc. IEEE International Conference - on Acoustics, Speech, and Signal Processing (ICASSP), 2018. -[ pdf ] - -
-[8] - -T. N. Sainath, C. C. Chiu, R. Prabhavalkar, A. Kannan, Y. Wu, P. Nguyen, and - Z. C. Z, “Improving the Performance of Online Neural Transducer Models,” - in Proc. IEEE International Conference on Acoustics, Speech, and - Signal Processing (ICASSP), 2018. -[ pdf ] - -
-[9] - -C. C. Chiu and C. Raffel, “Monotonic Chunkwise Attention,” in Proc. - International Conference on Learning Representations (ICLR), 2018. -[ pdf ] - -
-[10] - -I. Williams, A. Kannan, P. Aleksic, D. Rybach, and T. N. S. TN, “Contextual - Speech Recognition in End-to-End Neural Network Systems using Beam Search,” - in Proc. Interspeech, 2018. -[ pdf ] - -
-[11] - -C. C. Chiu, A. Tripathi, K. Chou, C. Co, N. Jaitly, D. Jaunzeikare, A. Kannan, - P. Nguyen, H. Sak, A. Sankar, J. Tansuwan, N. Wan, Y. Wu, and X. Zhang, - “Speech recognition for medical conversations,” in Proc. - Interspeech, 2018. -[ pdf ] - -
-[12] - -R. Pang, T. N. Sainath, R. Prabhavalkar, S. Gupta, Y. Wu, S. Zhang, and C. C. - Chiu, “Compression of End-to-End Models,” in Proc. Interspeech, - 2018. -[ pdf ] - -
-[13] - -S. Toshniwal, A. Kannan, C. C. Chiu, Y. Wu, T. N. Sainath, and K. Livescu, “A - comparison of techniques for language model integration in encoder-decoder - speech recognition,” in Proc. IEEE Spoken Language Technology - Workshop (SLT), 2018. -[ pdf ] - -
-[14] - -G. Pundak, T. N. Sainath, R. Prabhavalkar, A. Kannan, and D. Zhao, “Deep - context: End-to-end contextual speech recognition,” in Proc. IEEE - Spoken Language Technology Workshop (SLT), 2018. -[ pdf ] - -
-[15] - -B. Li, Y. Zhang, T. N. Sainath, Y. Wu, and W. Chan, “Bytes are all you need: - End-to-end multilingual speech recognition and synthesis with bytes,” in - Proc. IEEE International Conference on Acoustics, Speech, and Signal - Processing (ICASSP), 2019. -[ pdf ] - -
-[16] - -J. Guo, T. N. Sainath, and R. J. Weiss, “A spelling correction model for - end-to-end speech recognition,” in Proc. IEEE International - Conference on Acoustics, Speech, and Signal Processing (ICASSP), 2019. -[ pdf ] - -
-[17] - -U. Alon, G. Pundak, and T. N. Sainath, “Contextual speech recognition with - difficult negative training examples,” in Proc. IEEE International - Conference on Acoustics, Speech, and Signal Processing (ICASSP), 2019. -[ pdf ] - -
-[18] - -Y. Qin, N. Carlini, I. Goodfellow, G. Cottrell, and C. Raffel, “Imperceptible, - robust, and targeted adversarial examples for automatic speech recognition,” - in Proc. International Conference on Machine Learning (ICML), 2019. -[ pdf ] - -
-[19] - -D. S. Park, W. Chan, Y. Zhang, C. Chiu, B. Zoph, E. D. Cubuk, and Q. V. Le, - “SpecAugment: A Simple Data Augmentation Method for Automatic Speech - Recognition,” in arXiv, 2019. -[ pdf ] - -
-[20] - -B. Li, T. N. Sainath, R. Pang, and Z. Wu, “Semi-supervised training for - end-to-end models via weak distillation,” in Proc. IEEE International - Conference on Acoustics, Speech, and Signal Processing (ICASSP), 2019. -[ pdf ] - -
-[21] - -S.-Y. Chang, R. Prabhavalkar, Y. He, T. N. Sainath, and G. Simko, “Joint - endpointing and decoding with end-to-end models,” in Proc. IEEE - International Conference on Acoustics, Speech, and Signal Processing - (ICASSP), 2019. -[ pdf ] - -
-[22] - -J. Heymann, K. C. Sim, and B. Li, “Improving ctc using stimulated learning for - sequence modeling,” in Proc. IEEE International Conference on - Acoustics, Speech, and Signal Processing (ICASSP), 2019. -[ pdf ] - -
-[23] - -A. Bruguier, R. Prabhavalkar, G. Pundak, and T. N. Sainath, “Phoebe: - Pronunciation-aware contextualization for end-to-end speech recognition,” in - Proc. IEEE International Conference on Acoustics, Speech, and Signal - Processing (ICASSP), 2019. -[ pdf ] - -
-[24] - -Y. He, T. N. Sainath, R. Prabhavalkar, I. McGraw, R. Alvarez, D. Zhao, - D. Rybach, A. Kannan, Y. Wu, R. Pang, Q. Liang, D. Bhatia, Y. Shangguan, - B. Li, G. Pundak, K. C. Sim, T. Bagby, S.-Y. Chang, K. Rao, and - A. Gruenstein, “Streaming end-to-end speech recognition for mobile - devices,” in Proc. IEEE International Conference on Acoustics, - Speech, and Signal Processing (ICASSP), 2019. -[ pdf ] - -
-[25] - -K. Irie, R. Prabhavalkar, A. Kannan, A. Bruguier, D. Rybach, and P. Nguyen, - “On the choice of modeling unit for sequence-to-sequence speech - recognition,” in Proc. Interspeech, 2019. -[ pdf ] - -
-[26] - -C. Peyser, H. Zhang, T. N. Sainath, and Z. Wu, “Improving Performance of - End-to-End ASR on Numeric Sequences,” in Proc. Interspeech, 2019. -[ pdf ] - -
-[27] - -D. Zhao, T. N. Sainath, D. Rybach, D. Bhatia, B. Li, and R. Pang, - “Shallow-fusion end-to-end contextual biasing,” in Proc. Interspeech, - 2019. -[ pdf ] - -
-[28] - -T. N. Sainath, R. Pang, D. Rybach, Y. He, R. Prabhavalkar, W. Li, M. Visontai, - Q. Liang, T. Strohman, Y. Wu, I. McGraw, and C.-C. Chiu, “Two-pass - end-to-end speech recognition,” in Proc. Interspeech, 2019. -[ pdf ] - -
-[29] - -C.-C. Chiu, W. Han, Y. Zhang, R. Pang, S. Kishchenko, P. Nguyen, A. Narayanan, - H. Liao, S. Zhang, A. Kannan, R. Prabhavalkar, Z. Chen, T. Sainath, and - Y. Wu, “A comparison of end-to-end models for long-form speech - recognition,” 2019. -[ pdf ] - -
-[30] - -A. Narayanan, R. Prabhavalkar, C. Chiu, D. Rybach, T. Sainath, and T. Strohman, - “Recognizing long-form speech using streaming end-to-end models,” 2019. -[ pdf ] - -
-[31] - -T. N. Sainath, R. Pang, R. Weiss, Y. He, C.-C. Chiu, and T. Strohman, “An - attention-based joint acoustic and text on-device end-to-end model,” in - Proc. IEEE International Conference on Acoustics, Speech, and Signal - Processing (ICASSP), 2020. - -
-[32] - -Z. Lu, L. Cao, Y. Zhang, C.-C. Chiu, and J. Fan, “Speech sentiment analysis - via pre-trained features from end-to-end asr models,” in Proc. IEEE - International Conference on Acoustics, Speech, and Signal Processing - (ICASSP), 2020. - -
-[33] - -D. Park, Y. Zhang, C.-C. Chiu, Y. Chen, B. Li, W. Chan, Q. Le, and Y. Wu, - “Specaugment on large scale datasets,” in Proc. IEEE International - Conference on Acoustics, Speech, and Signal Processing (ICASSP), 2020. -[ pdf ] - -
-[34] - -T. Sainath, Y. He, B. Li, A. Narayanan, R. Pang, A. Bruguier, S. yiin Chang, - W. Li, R. Alvarez, Z. Chen, C. cheng Chiu, D. Garcia, A. Gruenstein, K. Hu, - M. Jin, A. Kannan, Q. Liang, I. McGraw, C. Peyser, R. Prabhavalkar, - G. Pundak, D. Rybach, Y. Shangguan, Y. Sheth, T. Strohman, M. Visontai, - Y. Wu, Y. Zhang, and D. Zhao, “A streaming on-device end-to-end model - surpassing server-side conventional model quality and latency,” in - Proc. IEEE International Conference on Acoustics, Speech, and Signal - Processing (ICASSP), 2020. - -
-[35] - -A. Gulati, J. Qin, C.-C. Chiu, N. Parmar, Y. Zhang, J. Yu, W. Han, S. Wang, - Z. Zhang, Y. Wu, and R. Pang, “Conformer: Convolution-augmented transformer - for speech recognition,” in Proc. Interspeech, 2020. -[ pdf ] - -
-[36] - -W. Han, Z. Zhang, Y. Zhang, J. Yu, C.-C. Chiu, J. Qin, A. Gulati, R. Pang, and - Y. Wu, “Contextnet: Improving convolutional neural networks for automatic - speech recognition with global context,” in Proc. Interspeech, 2020. -[ pdf ] - -
-[37] - -W. Li, J. Qin, C.-C. Chiu, R. Pang, and Y. He, “Parallel rescoring with - transformer for streaming on-device speech recognition,” in Proc. - Interspeech, 2020. - -
-[38] - -D. S. Park, Y. Zhang, Y. Jia, W. Han, C.-C. Chiu, B. Li, Y. Wu, and Q. V. Le, - “Improved noisy student training for automatic speech recognition,” in - Proc. Interspeech, 2020. -[ pdf ] - -
- -## Language understanding - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-[1] - -A. Kannan, K. Chen, D. Jaunzeikare, and A. Rajkomar, “Semi-Supervised - Learning for Information Extraction from Dialogue,” in Proc. - Interspeech, 2018. -[ pdf ] - -
-[2] - -S. Yavuz, C. C. Chiu, P. Nguyen, and Y. Wu, “CaLcs: Continuously - Approximating Longest Common Subsequence for Sequence Level Optimization,” - in Proc. Conference on Empirical Methods in Natural Language Processing - (EMNLP), 2018. -[ pdf ] - -
-[3] - -P. Haghani, A. Narayanan, M. Bacchiani, G. Chuang, N. Gaur, P. Moreno, - R. Prabhavalkar, Z. Qu, and A. Waters, “From Audio to Semantics: Approaches - to End-to-End Spoken Language Understanding,” in Proc. IEEE Spoken - Language Technology Workshop (SLT), 2018. -[ pdf ] - -
-[4] - -M. X. Chen, B. N. Lee, G. Bansal, Y. Cao, S. Zhang, J. Lu, J. Tsay, Y. Wang, - A. M. Dai, Z. Chen, T. Sohn, and Y. Wu, “Gmail smart compose: Real-time - assisted writing,” in Proceedings of the 25th ACM SIGKDD International - Conference on Knowledge Discovery & Data Mining, Association for Computing - Machinery, 2019. -[ pdf | -http ] - -
- -## Speech synthesis - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-[1] - -J. Shen, R. Pang, R. J. Weiss, M. Schuster, N. Jaitly, Z. Yang, Z. Chen, - Y. Zhang, Y. Wang, R. Skerry-Ryan, R. A. Saurous, Y. Agiomyrgiannakis, and - Y. Wu, “Natural TTS synthesis by conditioning WaveNet on mel spectrogram - predictions,” in Proc. IEEE International Conference on Acoustics, - Speech, and Signal Processing (ICASSP), 2018. -[ sound examples | -pdf ] - -
-[2] - -J. Chorowski, R. J. Weiss, R. A. Saurous, and S. Bengio, “On using - backpropagation for speech texture generation and voice conversion,” in - Proc. IEEE International Conference on Acoustics, Speech, and Signal - Processing (ICASSP), 2018. -[ sound examples | -pdf ] - -
-[3] - -Y. Jia, Y. Zhang, R. J. Weiss, Q. Wang, J. Shen, F. Ren, Z. Chen, P. Nguyen, - R. Pang, I. Lopez-Moreno, and Y. Wu, “Transfer learning from speaker - verification to multispeaker text-to-speech synthesis,” in Advances in - Neural Information Processing Systems, 2018. -[ sound examples | -pdf ] - -
-[4] - -W. N. Hsu, Y. Zhang, R. J. Weiss, H. Zen, Y. Wu, Y. Wang, Y. Cao, Y. Jia, - Z. Chen, J. Shen, P. Nguyen, and R. Pang, “Hierarchical generative modeling - for controllable speech synthesis,” in Proc. International Conference - on Learning Representations (ICLR), 2019. -[ sound examples | -pdf ] - -
-[5] - -W. N. Hsu, Y. Zhang, R. J. Weiss, Y. A. Chung, Y. Wang, Y. Wu, and J. Glass, - “Disentangling correlated speaker and noise for speech synthesis via data - augmentation and adversarial factorization,” in NeurIPS 2018 Workshop - on Interpretability and Robustness in Audio, Speech, and Language, 2018. -[ pdf ] - -
-[6] - -H. Zen, V. Dang, R. Clark, Y. Zhang, R. J. Weiss, Y. Jia, Z. Chen, and Y. Wu, - “LibriTTS: A corpus derived from LibriSpeech for text-to-speech,” in - Proc. Interspeech, 2019. -[ data | -pdf ] - -
-[7] - -F. Biadsy, R. J. Weiss, P. Moreno, D. Kanvesky, and Y. Jia, “Parrotron: An - end-to-end speech-to-speech conversion model and its applications to - hearing-impaired speech and speech separation,” in Proc. Interspeech, - 2019. -[ sound examples | -pdf ] - -
-[8] - -Y. Zhang, R. J. Weiss, H. Zen, Y. Wu, Z. Chen, R. J. Skerry-Ryan, Y. Jia, - A. Rosenberg, and B. Ramabhadran, “Learning to speak fluently in a foreign - language: Multilingual speech synthesis and cross-language voice cloning,” - in Proc. Interspeech, 2019. -[ sound examples | -pdf ] - -
-[9] - -G. Sun, Y. Zhang, R. J. Weiss, Y. Cao, H. Zen, and Y. Wu, “Fully-hierarchical - fine-grained prosody modeling for interpretable speech synthesis,” in - Proc. IEEE International Conference on Acoustics, Speech, and Signal - Processing (ICASSP), 2020. -[ sound examples | -pdf ] - -
-[10] - -G. Sun, Y. Zhang, R. J. Weiss, Y. Cao, H. Zen, A. Rosenberg, B. Ramabhadran, - and Y. Wu, “Generating diverse and natural text-to-speech samples using a - quantized fine-grained VAE and auto-regressive prosody prior,” in - Proc. IEEE International Conference on Acoustics, Speech, and Signal - Processing (ICASSP), 2020. -[ sound examples | -pdf ] - -
- -## Speech translation - - - - - - - - - - - - - - - - - - - - - - -
-[1] - -R. J. Weiss, J. Chorowski, N. Jaitly, Y. Wu, and Z. Chen, - “Sequence-to-sequence models can directly translate foreign speech,” in - Proc. Interspeech, 2017. -[ pdf ] - -
-[2] - -Y. Jia, M. Johnson, W. Macherey, R. J. Weiss, Y. Cao, C. C. Chiu, N. Ari, - S. Laurenzo, and Y. Wu, “Leveraging weakly supervised data to improve - end-to-end speech-to-text translation,” in Proc. IEEE International - Conference on Acoustics, Speech, and Signal Processing (ICASSP), 2019. -[ pdf ] - -
-[3] - -Y. Jia, R. J. Weiss, F. Biadsy, W. Macherey, M. Johnson, Z. Chen, and Y. Wu, - “Direct speech-to-speech translation with a sequence-to-sequence model,” in - Proc. Interspeech, 2019. -[ sound examples | -pdf ] - -
- -## Optimization - - - - - - - - - - - - - - - - - - - - - - -
-[1] - -R. Anil, V. Gupta, T. Koren, K. Regan, and Y. Singer, “Second order - optimization made practical,” arXiv preprint arXiv:2002.09018, 2020. -[ pdf ] - -
-[2] - -N. Agarwal, R. Anil, E. Hazan, T. Koren, and C. Zhang, “Disentangling adaptive - gradient methods from learning rates,” arXiv preprint - arXiv:2002.11803, 2020. -[ pdf ] - -
-[3] - -R. Anil, V. Gupta, T. Koren, and Y. Singer, “Memory efficient adaptive - optimization,” in Advances in Neural Information Processing Systems, - pp. 9749--9758, 2019. -[ pdf ] - -
\ No newline at end of file diff --git a/codelabs/introduction.ipynb b/codelabs/introduction.ipynb deleted file mode 100644 index 259c95a8..00000000 --- a/codelabs/introduction.ipynb +++ /dev/null @@ -1,658 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "view-in-github" - }, - "source": [ - "\u003ca href=\"https://colab.research.google.com/github/tensorflow/lingvo/blob/master/codelabs/introduction.ipynb\" target=\"_parent\"\u003e\u003cimg src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/\u003e\u003c/a\u003e" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "od97_nvR82Qo" - }, - "source": [ - "# Introduction to Lingvo\n", - "\n", - "This codelab will guide you through the implementation of a **sequence-to-sequence** model using [**Lingvo**](https://github.com/tensorflow/lingvo).\n", - "\n", - "**Sequence-to-sequence** models map input sequences of arbitrary length to\n", - "output sequences of arbitrary length. Example uses of sequence-to-sequence\n", - "models include machine translation, which maps a sequence of words from one\n", - "language into a sequence of words in another language with the same meaning;\n", - "speech recognition, which maps a sequence of acoustic features into a sequence\n", - "of words; and text summarization, which\n", - "maps a sequence of words into a shorter sequence which conveys the same meaning.\n", - "\n", - "In this codelab, you will create a model which restores punctuation and\n", - "capitalization to text which has been lowercased and stripped of punctuation.\n", - "For example, given the following text:\n", - "\n", - "\u003e she asked do you know the way to san jose\n", - "\n", - "The model will output the following properly-punctuated-and-capitalized text:\n", - "\n", - "\u003e She asked, \"Do you know the way to San Jose\"?\n", - "\n", - "We will train an RNMT+ model based off of [\"The Best of Both Worlds: Combining Recent Advances in Neural Machine Translation. (Chen et al., 2018)\"](https://arxiv.org/abs/1804.09849)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "4bPGJjZkLBdz" - }, - "source": [ - "## Table of Contents\n", - "\n", - "In Colab, click `[View]-\u003e[Table of contents]` on the menu bar." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "bnpyjXz1OFAu" - }, - "source": [ - "## Prerequisites\n", - "\n", - "The main goal of this codelab is to teach you how to define and train sequence-to-sequence models in Lingvo. We do not aim to teach either Python or Tensorflow, and no sophisticated Python or Tensorflow programming will be required. However, the following will be helpful in understanding this codelab:\n", - "\n", - "- Familiarity with high-level machine learning primitives, in particular,\n", - " recurrent neural networks, LSTMs, and attention.\n", - "- Comfort reading and writing simple Python code. In particular, you should\n", - " know how to define simple classes and how inheritance works.\n", - "- Basic knowledge of the Tensorflow training workflow. If you have trained\n", - " simple Tensorflow models before (e.g., via another codelab), you should know\n", - " enough for this codelab.\n", - "\n", - "### Resources\n", - "\n", - "- [Introduction to RNNs and LSTMs](https://colah.github.io/posts/2015-08-Understanding-LSTMs/)\n", - "- [Official Tensorflow Tutorials](https://www.tensorflow.org/tutorials/)\n", - "- For more advanced topics or to get a deeper understanding of Lingvo beyond this codelab, see the [paper](https://arxiv.org/abs/1902.08295)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "coa-U7N6Kunb" - }, - "source": [ - "## Learning Objectives\n", - "\n", - "This codelab will teach you the following:\n", - "\n", - "- How to generate input data for training a sequence-to-sequence model in Lingvo.\n", - "- How models are specified and configured in Lingvo, by adapting a pre-existing model architecture for machine translation.\n", - "- How to use the trained model for inference.\n", - "\n", - "This codelab does not:\n", - "\n", - "- Teach you how to design a model for solving specific tasks.\n", - "- Provide a state-of-the-art model for the punctuator task." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "_mtJSYor9d6d" - }, - "source": [ - "## Environment Setup\n", - "\n", - "To start with, we need to connect this Colab notebook with Lingvo.\n", - "\n", - "```shell\n", - "mkdir -p /tmp/lingvo_codelab \u0026\u0026 cd /tmp/lingvo_codelab\n", - "pip3 install lingvo\n", - "python3 -m lingvo.ipython_kernel\n", - "```\n", - "\n", - "Finally, on the top right hand side of this Colab notebook, open the dropdown beside \"CONNECT\" and select \"Connect to local runtime...\", enter `http://localhost:8888` and press CONNECT.\n", - "\n", - "You should now see the words \"CONNECTED\" and be able to execute the following cell." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "2JRfZTumHxpY" - }, - "outputs": [], - "source": [ - "import lingvo" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "lkN9qajvNtEs" - }, - "source": [ - "## Input Pipeline\n", - "\n", - "In order to train a sequence-to-sequence model, we need a set of pairs of source\n", - "and target sequences. For this codelab, our source sequences will be\n", - "text which has been lowercased and had its punctuation removed, and the target\n", - "sequences will be the original sentences, with their original casing and\n", - "punctuation.\n", - "\n", - "Since neural networks require numeric inputs, we will also need a tokenization scheme mapping the sequence of characters to a sequence of numbers. In this codelab, we will use a pre-trained word-piece model.\n", - "\n", - "### Download Raw Input\n", - "\n", - "We will use the [Brown Corpus](http://www.nltk.org/nltk_data) as the source of our training data. Run the following cell to download and preprocess the dataset. The script will generate `train.txt` and `test.txt` containing the training and test data at an 80:20 split with individual sentences on each line." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Asfie_a8YmN3" - }, - "outputs": [], - "source": [ - "!python3 -m lingvo.tasks.punctuator.tools.download_brown_corpus --outdir=/tmp/punctuator_data\n", - "!curl -O https://raw.githubusercontent.com/tensorflow/lingvo/master/lingvo/tasks/punctuator/params/brown_corpus_wpm.16000.vocab" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "BHq87NF-8lgD" - }, - "source": [ - "### Define an Input Generator\n", - "\n", - "In order to train a model, we need an input generator that provides structured mini-batches of source-target pairs. The input generator handles all the processing necessary to generate numeric data that can be fed to the model. This includes:\n", - "\n", - "- reading examples from the data source in random order, where the data source may be split across multiple files;\n", - "- processing the data -- for our task this involves generating a \"source\" sentence by converting all characters to lower-case and removing punctuation, and then tokenizing both the source and target sequences into integer tokens; and\n", - "- batching together examples by padding them to the same length. Multiple buckets of different lengths may be used to avoid inefficiency from padding a short input to a very long length.\n", - "\n", - "Fortunately, the majority of this is handled in the background by Lingvo. We only need to specify how the data should be processed.\n", - "\n", - "Input generators are subclasses of *BaseInputGenerator* found in [lingvo/core/base_input_generator.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/core/base_input_generator.py) and have the following structure:\n", - "\n", - "- a `Params` classmethod that returns a default Params object for configuring the input generator. Experiment configurations inside Lingvo are controlled using these Params objects.\n", - "- an `_InputBatch` method that returns a [`NestedMap`](https://github.com/tensorflow/lingvo/blob/3344e201719961183d88713784ccbae447f5c52a/lingvo/core/py_utils.py#L392) containing the input batch. `NestedMap` is an arbitrarily nested map structure used throughout Lingvo.\n", - "- an optional `_PreprocessInputBatch` method that preprocesses the batch returned by `_InputBatch`.\n", - "\n", - "Here is an example of the input generator for the Punctuator task, found in [lingvo/tasks/punctuator/input_generator.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/punctuator/input_generator.py).\n", - "\n", - "Run the cell below to write the file to disk." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "T2BzBCqE_yvt" - }, - "outputs": [], - "source": [ - "%%writefile input_generator.py\n", - "import string\n", - "import lingvo.compat as tf\n", - "from lingvo.core import base_input_generator\n", - "from lingvo.core import base_layer\n", - "from lingvo.core import generic_input\n", - "from lingvo.core import py_utils\n", - "from lingvo.core import tokenizers\n", - "\n", - "\n", - "class PunctuatorInput(base_input_generator.BaseSequenceInputGenerator):\n", - " \"\"\"Reads text line by line and processes them for the punctuator task.\"\"\"\n", - "\n", - " @classmethod\n", - " def Params(cls):\n", - " \"\"\"Defaults params for PunctuatorInput.\"\"\"\n", - " p = super(PunctuatorInput, cls).Params()\n", - " p.tokenizer = tokenizers.WpmTokenizer.Params()\n", - " return p\n", - "\n", - " def _ProcessLine(self, line):\n", - " \"\"\"A single-text-line processor.\n", - " Gets a string tensor representing a line of text that have been read from\n", - " the input file, and splits it to graphemes (characters).\n", - " We use original characters as the target labels, and the lowercased and\n", - " punctuation-removed characters as the source labels.\n", - " Args:\n", - " line: a 1D string tensor.\n", - " Returns:\n", - " A list of tensors, in the expected order by __init__.\n", - " \"\"\"\n", - " # Tokenize the input into integer ids.\n", - " # tgt_ids has the start-of-sentence token prepended, and tgt_labels has the\n", - " # end-of-sentence token appended.\n", - " tgt_ids, tgt_labels, tgt_paddings = self.StringsToIds(\n", - " tf.convert_to_tensor([line]))\n", - "\n", - " def Normalize(line):\n", - " # Lowercase and remove punctuation.\n", - " line = line.lower().translate(None, string.punctuation.encode('utf-8'))\n", - " # Convert multiple consecutive spaces to a single one.\n", - " line = b' '.join(line.split())\n", - " return line\n", - "\n", - " normalized_line = tf.py_func(Normalize, [line], tf.string, stateful=False)\n", - " _, src_labels, src_paddings = self.StringsToIds(\n", - " tf.convert_to_tensor([normalized_line]), is_source=True)\n", - " # The model expects the source without a start-of-sentence token.\n", - " src_ids = src_labels\n", - "\n", - " # Compute the length for bucketing.\n", - " bucket_key = tf.cast(\n", - " tf.round(\n", - " tf.maximum(\n", - " tf.reduce_sum(1.0 - src_paddings),\n", - " tf.reduce_sum(1.0 - tgt_paddings))), tf.int32)\n", - " tgt_weights = 1.0 - tgt_paddings\n", - "\n", - " # Return tensors in an order consistent with __init__.\n", - " out_tensors = [\n", - " src_ids, src_paddings, tgt_ids, tgt_paddings, tgt_labels, tgt_weights\n", - " ]\n", - " return [tf.squeeze(t, axis=0) for t in out_tensors], bucket_key\n", - "\n", - " def _DataSourceFromFilePattern(self, file_pattern):\n", - " \"\"\"Create the input processing op.\n", - " Args:\n", - " file_pattern: The file pattern to use as input.\n", - " Returns:\n", - " an operation that when executed, calls `_ProcessLine` on a line read\n", - " from `file_pattern`.\n", - " \"\"\"\n", - " return generic_input.GenericInput(\n", - " file_pattern=file_pattern,\n", - " processor=self._ProcessLine,\n", - " # Pad dimension 0 to the same length.\n", - " dynamic_padding_dimensions=[0] * 6,\n", - " # The constant values to use for padding each of the outputs.\n", - " dynamic_padding_constants=[0, 1, 0, 1, 0, 0],\n", - " **self.CommonInputOpArgs())\n", - "\n", - " def __init__(self, params):\n", - " super(PunctuatorInput, self).__init__(params)\n", - "\n", - " # Build the input processing graph.\n", - " (self._src_ids, self._src_paddings, self._tgt_ids, self._tgt_paddings,\n", - " self._tgt_labels,\n", - " self._tgt_weights), self._bucket_keys = self._BuildDataSource()\n", - "\n", - " def InfeedBatchSize(self):\n", - " return tf.shape(self._src_ids)[0]\n", - "\n", - " def _InputBatch(self):\n", - " \"\"\"Returns a single batch as a `.NestedMap` to be passed to the model.\"\"\"\n", - " ret = py_utils.NestedMap()\n", - "\n", - " ret.bucket_keys = self._bucket_keys\n", - "\n", - " ret.src = py_utils.NestedMap()\n", - " ret.src.ids = tf.cast(self._src_ids, dtype=tf.int32)\n", - " ret.src.paddings = self._src_paddings\n", - "\n", - " ret.tgt = py_utils.NestedMap()\n", - " ret.tgt.ids = self._tgt_ids\n", - " ret.tgt.labels = tf.cast(self._tgt_labels, dtype=tf.int32)\n", - " ret.tgt.weights = self._tgt_weights\n", - " ret.tgt.paddings = self._tgt_paddings\n", - "\n", - " return ret" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "lrwT1f0IfPca" - }, - "source": [ - "## Model Definition\n", - "\n", - "Next, we need to define the network structure for the task. The network is a nested structure of layers. Most classes in Lingvo are subclasses of *BaseLayer* found in [lingvo/core/base_layer.py](https://github.com/tensorflow/lingvo/blob/560f838bd576c7b911df379121eb58252b6ae326/lingvo/core/base_layer.py#L150) and inherit the following:\n", - "\n", - "- a Params classmethod that returns a default [Params](https://github.com/tensorflow/lingvo/blob/3344e201719961183d88713784ccbae447f5c52a/lingvo/core/hyperparams.py#L151) object for configuring the class. In addition to hyperparameters, the Params object can also contain Params objects for configuring child layers. Some of the properties present in all Params objects include:\n", - " - `cls`: the python class that the Params object is associated with. This can be used to construct an instance of the class;\n", - " - `name`: the name of this layer;\n", - " - `dtype`: the default dtype to use when creating variables.\n", - "- The `__init__` constructor. All child layers and variables should be created here.\n", - "- A `CreateVariable` method that is called to create variables.\n", - "- A `CreateChild` method that is called to create child layers.\n", - "- A `FProp` method that implements forward propagation through the layer.\n", - "\n", - "As a reference, many examples of layers can be found in [lingvo/core/layers.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/core/layers.py), [lingvo/core/attention.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/core/attention.py), and [lingvo/core/rnn_layers.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/core/rnn_layers.py).\n", - "\n", - "\u0026nbsp;\n", - "\n", - "The root layer for the network should be a subclass of `BaseTask` found in [lingvo/core/base_model.py](https://github.com/tensorflow/lingvo/blob/918c584f057481717eff6e1e29ae028aeab3d165/lingvo/core/base_model.py#L79), and implements the following:\n", - "\n", - "- A `ComputePredictions` method that takes the current variable values (`theta`) and `input_batch` and returns the network predictions.\n", - "- A `ComputeLoss` method that takes `theta`, `input_batch`, and the `predictions` returned from `ComputePredictions` and returns a dictionary of scalar metrics, one of which should be `loss`. These scalar metrics are exported to TensorBoard as summaries.\n", - "- An optional `Decode` method for creating a separate graph for decoding. For example, training and evaluation might use teacher forcing while decoding might not.\n", - "- An optional `Inference` method that returns a graph with feeds and fetches that can be used together with a saved checkpoint for inference. This differs from `Decode` in that it can be fed data directly instead of using data from the input generator.\n", - "\n", - "\u0026nbsp;\n", - "\n", - "This codelab uses the existing networks from [lingvo/tasks/punctuator/model.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/punctuator/model.py), which is derived from the networks in [lingvo/tasks/mt/model.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/mt/model.py) with an added `Inference` method for the punctuator task. The actual logic lies mostly in [lingvo/tasks/mt/encoder.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/mt/encoder.py) and [lingvo/tasks/mt/decoder.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/mt/decoder.py)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "9ItaOgXNpql0" - }, - "source": [ - "## Model Configuration\n", - "\n", - "After defining the input generator and the network, we need to create an model configuration with the specific hyperparameters to use for our model.\n", - "\n", - "Since there is only a single task, we create a subclass of `SingleTaskModelParams` found in [lingvo/core/base_model_params.py](https://github.com/tensorflow/lingvo/blob/4747cf80a7e6cf58211aa899bae854820a3b42f6/lingvo/core/base_model_params.py#L47). It has the following structure:\n", - "\n", - "- The `Train`/`Dev`/`Test` methods configure the input generator for the respective datasets.\n", - "- The `Task` method configures the network.\n", - "\n", - "The following cell contains the configuration that will be used in this codelab. It can also be found in [lingvo/tasks/punctuator/params/codelab.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/punctuator/params/codelab.py). The network configuration in the `Task` classmethod is delegated to [lingvo/tasks/mt/params/base_config.py](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/mt/params/base_config.py).\n", - "\n", - "Run the cell below to write the file to disk." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "3CjruUvXY5we" - }, - "outputs": [], - "source": [ - "%%writefile codelab.py\n", - "import input_generator\n", - "import os\n", - "from lingvo import model_registry\n", - "import lingvo.compat as tf\n", - "from lingvo.core import base_model_params\n", - "from lingvo.tasks.mt import base_config\n", - "from lingvo.tasks.punctuator import model\n", - "\n", - "\n", - "# This base class defines parameters for the input generator for a specific\n", - "# dataset. Specific network architectures will be implemented in subclasses.\n", - "class BrownCorpusWPM(base_model_params.SingleTaskModelParams):\n", - " \"\"\"Brown Corpus data with a Word-Piece Model tokenizer.\"\"\"\n", - "\n", - " # Generated using\n", - " # lingvo/tasks/punctuator/tools:download_brown_corpus.\n", - " _DATADIR = '/tmp/punctuator_data'\n", - " _VOCAB_FILE = 'brown_corpus_wpm.16000.vocab'\n", - " # _VOCAB_SIZE needs to be a multiple of 16 because we use a sharded softmax\n", - " # with 16 shards.\n", - " _VOCAB_SIZE = 16000\n", - "\n", - " def Train(self):\n", - " p = input_generator.PunctuatorInput.Params()\n", - " p.file_pattern = 'text:' + os.path.join(self._DATADIR, 'train.txt')\n", - " p.file_random_seed = 0 # Do not use a fixed seed.\n", - " p.file_parallelism = 1 # We only have a single input file.\n", - "\n", - " # The bucket upper bound specifies how to split the input into buckets. We\n", - " # train on sequences up to maximum bucket size and discard longer examples.\n", - " p.bucket_upper_bound = [10, 20, 30, 60, 120]\n", - "\n", - " # The bucket batch limit determines how many examples are there in each\n", - " # batch during training. We reduce the batch size for the buckets that\n", - " # have higher upper bound (batches that consist of longer sequences)\n", - " # in order to prevent out of memory issues.\n", - " # Note that this hyperparameter varies widely based on the model and\n", - " # language. Larger models may warrant smaller batches in order to fit in\n", - " # memory, for example; and ideographical languages like Chinese may benefit\n", - " # from more buckets.\n", - " p.bucket_batch_limit = [512, 256, 160, 80, 40]\n", - "\n", - " p.tokenizer.vocab_filepath = self._VOCAB_FILE\n", - " p.tokenizer.vocab_size = self._VOCAB_SIZE\n", - " p.tokenizer.pad_to_max_length = False\n", - "\n", - " # Set the tokenizer max length slightly longer than the largest bucket to\n", - " # discard examples that are longer than we allow.\n", - " p.source_max_length = p.bucket_upper_bound[-1] + 2\n", - " p.target_max_length = p.bucket_upper_bound[-1] + 2\n", - " return p\n", - "\n", - " # There is also a Dev method for dev set params, but we don't have a dev set.\n", - " def Test(self):\n", - " p = input_generator.PunctuatorInput.Params()\n", - " p.file_pattern = 'text:' + os.path.join(self._DATADIR, 'test.txt')\n", - " p.file_random_seed = 27182818 # Fix random seed for testing.\n", - " # The following two parameters are important if there's more than one input\n", - " # file. For this codelab it doesn't actually matter.\n", - " p.file_parallelism = 1 # Avoid randomness in testing.\n", - " # In order to make exactly one pass over the dev/test sets, we set buffer\n", - " # size to 1. Greater numbers may cause inaccurate dev/test scores.\n", - " p.file_buffer_size = 1\n", - "\n", - " p.bucket_upper_bound = [10, 20, 30, 60, 120, 200]\n", - " p.bucket_batch_limit = [16] * 4 + [4] * 2\n", - "\n", - " p.tokenizer.vocab_filepath = self._VOCAB_FILE\n", - " p.tokenizer.vocab_size = self._VOCAB_SIZE\n", - " p.tokenizer.pad_to_max_length = False\n", - "\n", - " p.source_max_length = p.bucket_upper_bound[-1] + 2\n", - " p.target_max_length = p.bucket_upper_bound[-1] + 2\n", - " return p\n", - "\n", - "\n", - "# This decorator registers the model in the Lingvo model registry.\n", - "# This file is lingvo/tasks/punctuator/params/codelab.py,\n", - "# so the model will be registered as punctuator.codelab.RNMTModel.\n", - "@model_registry.RegisterSingleTaskModel\n", - "class RNMTModel(BrownCorpusWPM):\n", - " \"\"\"RNMT+ Model.\"\"\"\n", - "\n", - " def Task(self):\n", - " p = base_config.SetupRNMTParams(\n", - " model.RNMTModel.Params(),\n", - " name='punctuator_rnmt',\n", - " vocab_size=self._VOCAB_SIZE,\n", - " embedding_dim=1024,\n", - " hidden_dim=1024,\n", - " num_heads=4,\n", - " num_encoder_layers=6,\n", - " num_decoder_layers=8,\n", - " learning_rate=1e-4,\n", - " l2_regularizer_weight=1e-5,\n", - " lr_warmup_steps=500,\n", - " lr_decay_start=400000,\n", - " lr_decay_end=1200000,\n", - " lr_min=0.5,\n", - " ls_uncertainty=0.1,\n", - " atten_dropout_prob=0.3,\n", - " residual_dropout_prob=0.3,\n", - " adam_beta2=0.98,\n", - " adam_epsilon=1e-6,\n", - " )\n", - " p.eval.samples_per_summary = 2466\n", - " return p" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "ooJYcOA4tcXW" - }, - "source": [ - "## Model Training\n", - "\n", - "The following cell trains the model. Note that this will require approximately 2.5GB of space in `logdir`." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "91eOF9Sqy1iG" - }, - "outputs": [], - "source": [ - "# Start tensorboard (access at http://localhost:6006)\n", - "import os\n", - "os.system('lsof -t -i:6006 || tensorboard --logdir=/tmp/punctuator \u0026')\n", - "\n", - "!python3 -m lingvo.trainer --model=codelab.RNMTModel --mode=sync --logdir=/tmp/punctuator --saver_max_to_keep=2 --noenable_asserts --run_locally=gpu" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "sOQMKZiuwNKr" - }, - "source": [ - "The following cell evaluates the model. In Lingvo, evaluation is meant to be run alongside training as a separate process that periodically looks for the latest checkpoint and evaluates it. There is only one process in Colab so running this cell will evaluate the current checkpoint then it will block indefinitely waiting for the next checkpoint." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "leWsSPQ6L__H" - }, - "outputs": [], - "source": [ - "!python3 -m lingvo.trainer --model=codelab.RNMTModel --job=evaler_test --logdir=/tmp/punctuator --run_locally=cpu" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "wwqXVIaG2hA_" - }, - "source": [ - "There is also a Decoder job that can be run the same way. The difference between the Evaler and Decoder varies by model." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "g1V7f-Oxw9vp" - }, - "source": [ - "## Model Inference\n", - "\n", - "After the model has been trained for around 10-20k steps (a few hours on GPU), its inference graph can be used to interact with the model using arbitrary inputs." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "cellView": "both", - "colab": {}, - "colab_type": "code", - "id": "7KU0zeUTKS9Z" - }, - "outputs": [], - "source": [ - "import string\n", - "\n", - "from lingvo import compat as tf\n", - "from lingvo import model_imports\n", - "from lingvo import model_registry\n", - "from lingvo.core import inference_graph_exporter\n", - "from lingvo.core import predictor\n", - "from lingvo.core.ops.hyps_pb2 import Hypothesis\n", - "\n", - "tf.flags.FLAGS.mark_as_parsed()\n", - "\n", - "\n", - "src = \"she asked do you know the way to san jose\" #@param {type:'string'}\n", - "src = src.lower().translate(str.maketrans('', '', string.punctuation))\n", - "print(src)\n", - "\n", - "checkpoint = tf.train.latest_checkpoint('/tmp/punctuator/train')\n", - "print('Using checkpoint %s' % checkpoint)\n", - "\n", - "# Run inference\n", - "params = model_registry.GetParams('codelab.RNMTModel', 'Test')\n", - "inference_graph = inference_graph_exporter.InferenceGraphExporter.Export(params)\n", - "pred = predictor.Predictor(inference_graph, \n", - " checkpoint=checkpoint, \n", - " device_type='cpu')\n", - "src_ids, decoded, scores, hyps = pred.Run(\n", - " ['src_ids', 'topk_decoded', 'topk_scores', 'topk_hyps'], src_strings=[src])\n", - "# print(src_ids[0])\n", - "for text, score in zip(decoded[0].tolist(), scores[0].tolist()):\n", - " print(\"%.5f: %s\" % (score, text))\n", - "# for i, hyp in enumerate(hyps[0]):\n", - "# print(\"=======hyp %d=======\" % i)\n", - "# print(Hypothesis().FromString(hyp))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "vBDiPV07mFmX" - }, - "source": [ - "Footnote: One might wonder why our result places the question mark outside the quotation. This happens because the Brown corpus follows a 1959 US Patent Office precedure of transliterating texts into punch cards, where the closing question mark is always punched before the punctuation, so that the punctuation mark occurs at the end of the sentence. See [this link](http://clu.uni.no/icame/manuals/BROWN/INDEX.HTM) for more details. Our result is just following this pattern in our training data." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "_hW7aTI8fM4O" - }, - "source": [ - "## Additional Resources\n", - "\n", - "For more advanced topics or to get a deeper understanding of Lingvo beyond this codelab, see the [paper](https://arxiv.org/abs/1902.08295)." - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "Introduction to Lingvo", - "provenance": [], - "toc_visible": true - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/docker/dev.dockerfile b/docker/dev.dockerfile deleted file mode 100644 index ac3eeea1..00000000 --- a/docker/dev.dockerfile +++ /dev/null @@ -1,115 +0,0 @@ -# Run the following commands in order: -# -# LINGVO_DIR="/tmp/lingvo" # (change to the cloned lingvo directory, e.g. "$HOME/lingvo") -# LINGVO_DEVICE="gpu" # (Leave empty to build and run CPU only docker) -# docker build --tag tensorflow:lingvo $(test "$LINGVO_DEVICE" = "gpu" && echo "--build-arg base_image=nvidia/cuda:10.0-cudnn7-runtime-ubuntu18.04") - < "$LINGVO_DIR/docker/dev.dockerfile" -# docker run --rm $(test "$LINGVO_DEVICE" = "gpu" && echo "--runtime=nvidia") -it -v ${LINGVO_DIR}:/tmp/lingvo -v ${HOME}/.gitconfig:/home/${USER}/.gitconfig:ro -p 6006:6006 -p 8888:8888 --name lingvo tensorflow:lingvo bash -# -# Test that everything worked: -# -# bazel test -c opt --test_output=streamed //lingvo:trainer_test //lingvo:models_test - -ARG cpu_base_image="ubuntu:18.04" -ARG base_image=$cpu_base_image -FROM $base_image - -LABEL maintainer="Lingvo team " - -# Re-declare args because the args declared before FROM can't be used in any -# instruction after a FROM. -ARG cpu_base_image="ubuntu:18.04" -ARG base_image=$cpu_base_image - -# Pick up some TF dependencies -RUN apt-get update && apt-get install -y --no-install-recommends software-properties-common -RUN apt-get update && apt-get install -y --no-install-recommends \ - aria2 \ - build-essential \ - curl \ - dirmngr \ - git \ - gpg-agent \ - less \ - libfreetype6-dev \ - libhdf5-serial-dev \ - libpng-dev \ - libzmq3-dev \ - lsof \ - pkg-config \ - rename \ - rsync \ - sox \ - unzip \ - vim \ - && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -# Install python 3.7 -RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA6932366A755776 -RUN echo "deb http://ppa.launchpad.net/deadsnakes/ppa/ubuntu bionic main" > /etc/apt/sources.list.d/deadsnakes-ppa-bionic.list -RUN apt-get update && apt-get install -y python3.7 -RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1000 -# bazel assumes the python executable is "python". -RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1000 - -RUN curl -O https://bootstrap.pypa.io/get-pip.py && python3 get-pip.py && rm get-pip.py - -ARG bazel_version=0.28.1 -# This is to install bazel, for development purposes. -ENV BAZEL_VERSION ${bazel_version} -RUN mkdir /bazel && \ - cd /bazel && \ - curl -H "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36" -fSsL -O https://github.com/bazelbuild/bazel/releases/download/$BAZEL_VERSION/bazel-$BAZEL_VERSION-installer-linux-x86_64.sh && \ - curl -H "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36" -fSsL -o /bazel/LICENSE.txt https://raw.githubusercontent.com/bazelbuild/bazel/master/LICENSE && \ - chmod +x bazel-*.sh && \ - ./bazel-$BAZEL_VERSION-installer-linux-x86_64.sh && \ - cd / && \ - rm -f /bazel/bazel-$BAZEL_VERSION-installer-linux-x86_64.sh - -ARG pip_dependencies=' \ - apache-beam[gcp]>=2.8 \ - contextlib2 \ - dataclasses \ - google-api-python-client \ - h5py \ - ipykernel \ - jupyter \ - jupyter_http_over_ws \ - matplotlib \ - model-pruning-google-research \ - numpy \ - oauth2client \ - pandas \ - Pillow \ - pyyaml \ - recommonmark \ - scikit-learn==0.20.3 \ - scipy \ - sklearn \ - sphinx \ - sphinx_rtd_theme \ - sympy \ - waymo-open-dataset-tf-2-2-0' - -RUN pip3 --no-cache-dir install $pip_dependencies -RUN python3 -m ipykernel.kernelspec - -# The latest tensorflow requires CUDA 10 compatible nvidia drivers (410.xx). -# If you are unable to update your drivers, an alternative is to compile -# tensorflow from source instead of installing from pip. -# Ensure we install the correct version by uninstalling first. -RUN pip3 uninstall -y tensorflow tensorflow-gpu tf-nightly tf-nightly-gpu -RUN pip3 --no-cache-dir install tensorflow-gpu==2.2.0 - -RUN jupyter serverextension enable --py jupyter_http_over_ws - -# TensorBoard -EXPOSE 6006 - -# Jupyter -EXPOSE 8888 - -WORKDIR "/tmp/lingvo" - -CMD ["/bin/bash"] diff --git a/docker/lib.dockerfile b/docker/lib.dockerfile deleted file mode 100644 index b28c9550..00000000 --- a/docker/lib.dockerfile +++ /dev/null @@ -1,51 +0,0 @@ -# CPU only: -# docker build --tag tensorflow:lingvo_lib - < docker/lib.dockerfile -# docker run --rm -it -p 6006:6006 -p 8888:8888 --name lingvo tensorflow:lingvo_lib bash -# -# With GPU support: -# docker build --tag tensorflow:lingvo_lib_gpu --build-arg base_image=nvidia/cuda:10.0-cudnn7-runtime-ubuntu18.04 - < docker/lib.dockerfile -# docker run --runtime=nvidia --rm -it -p 6006:6006 -p 8888:8888 --name lingvo tensorflow:lingvo_lib_gpu bash - -ARG cpu_base_image="ubuntu:18.04" -ARG base_image=$cpu_base_image -FROM $base_image - -LABEL maintainer="Lingvo Bot " - -# Re-declare args because the args declared before FROM can't be used in any -# instruction after a FROM. -ARG cpu_base_image="ubuntu:18.04" -ARG base_image=$cpu_base_image - -# Pick up some TF dependencies -RUN apt-get update && apt-get install -y --no-install-recommends software-properties-common -RUN apt-get update && apt-get install -y --no-install-recommends \ - build-essential \ - curl \ - git \ - less \ - lsof \ - pkg-config \ - python3-distutils \ - rsync \ - sox \ - unzip \ - vim \ - && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -RUN curl -O https://bootstrap.pypa.io/get-pip.py && python3 get-pip.py && rm get-pip.py - -RUN pip3 --no-cache-dir install lingvo - -RUN python3 -m ipykernel.kernelspec -RUN jupyter serverextension enable --py jupyter_http_over_ws - -# TensorBoard -EXPOSE 6006 - -# Jupyter -EXPOSE 8888 - -CMD ["/bin/bash"] diff --git a/docker/run_distributed.py b/docker/run_distributed.py deleted file mode 100755 index 181ea5eb..00000000 --- a/docker/run_distributed.py +++ /dev/null @@ -1,223 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Runs code on a fleet of machines. - -This runs the lingvo code on a fleet of docker for demonstration and testing -purposes. We assume the following: -* There is a running container -* There is a shared volume in /sharedfs. In reality, this would be something -like an NFS or HDFS mount. - -The script is run on the host and only requires python and the docker binary -to be installed. - -We run two "clusters": one for training, and one for decoding. The trainer -jobs (controller, trainer_client/worker or trainer/ps) are connected to -each other, whereas the decoder jobs are independent, only reading from -the shared filesystem. The trainer jobs are configured via a cluster spec -flag, whereas the decoder jobs are configured with individual flags. -""" - -import os -from pipes import quote as shell_quote -import shutil -import subprocess -import sys - -_SYNC_TRAIN_CLUSTER_SPEC = { - "worker": [ - "worker0:43222", - "worker1:43222", - "worker2:43222", - ], - "controller": ["controller:43214",], - "trainer_client": ["trainer_client:24601"], -} - -_ASYNC_TRAIN_CLUSTER_SPEC = { - "trainer": [ - "trainer0:43222", - "trainer1:43222", - "trainer2:43222", - ], - "ps": [ - "ps0:43221", - "ps1:43221", - ], - "controller": ["controller:43214",], -} - -DECODE_CLUSTER_SPEC = { - "evaler_test": ["evaler_test:23487"], - "decoder_test": ["decoder_test:24679"], -} - -MODEL = "image.mnist.LeNet5" -DATADIR = "/tmp/mnist" -TRAIN_MODE = "sync" - -TRAIN_CLUSTER_SPEC = ( - _SYNC_TRAIN_CLUSTER_SPEC - if TRAIN_MODE == "sync" else _ASYNC_TRAIN_CLUSTER_SPEC) - -DOCKER_BIN = "/usr/bin/docker" -# All that is required is that we have pip installed tensorflow. -DOCKER_IMAGE_NAME = "tensorflow:lingvo" -# This was created using -# bazel build -c opt //lingvo:trainer.par -# cp bazel-bin/lingvo/trainer.par . -# Since /tmp/lingvo is mounted, we can see it. -# TODO(drpng): hard-wiring below. -TRAINER_PACKAGE = "/tmp/lingvo/trainer.par" -DRY_RUN = False -NETWORK_NAME = "tf-net" - -SHARED_FS_MOUNTPOINT = "/tmp/sharedfs" - - -def _RunDocker(args): - print("Running: docker %s" % args) - if DRY_RUN: - return 0 - ret = subprocess.call([DOCKER_BIN] + args) - return ret - - -def _RunDockerOrDie(args): - ret = _RunDocker(args) - if ret != 0: - sys.stderr.write("Failed to run: %s\n" % ret) - sys.stderr.flush() - sys.exit(ret) - - -def _ExecInDocker(container_name, - cmd_array, - workdir=None, - logfile=None, - detach=False): - """Execute in docker container.""" - if not workdir: - workdir = "/tmp" - opts = ["-t", "-w", workdir] - if detach: - opts += ["-d"] - # TODO(drpng): avoid quoting hell. - base_cmd = ["exec"] + opts + [container_name] - if logfile: - # The logfile is in the container. - cmd = " ".join(shell_quote(x) for x in cmd_array) - cmd += " >& %s" % logfile - full_cmd = base_cmd + ["bash", "-c", cmd] - else: - full_cmd = base_cmd + cmd_array - ret = _RunDocker(full_cmd) - if ret != 0: - sys.stderr.write( - "Failed to exec within %s: %s" % (container_name, cmd_array)) - sys.exit(ret) - - -def _Machine(machine_port): - # From host:port to host. - return machine_port[:machine_port.index(":")] - - -def Cleanup(): - specs = list(TRAIN_CLUSTER_SPEC.values()) + list(DECODE_CLUSTER_SPEC.values()) - for job_machines in specs: - machines = [_Machine(x) for x in job_machines] - _RunDocker(["stop", "-t", "0"] + machines) - _RunDocker(["network", "rm", NETWORK_NAME]) - shutil.rmtree(SHARED_FS_MOUNTPOINT, ignore_errors=True) - - -def InitFiles(): - os.mkdir(SHARED_FS_MOUNTPOINT, 0o1777) - # Create these directories so that we own them, not root. - os.mkdir(SHARED_FS_MOUNTPOINT + "/log", 0o1777) - os.mkdir(SHARED_FS_MOUNTPOINT + "/log/train", 0o1777) - os.mkdir(SHARED_FS_MOUNTPOINT + "/log/decoder_test", 0o1777) - os.mkdir(SHARED_FS_MOUNTPOINT + "/log/eval_test", 0o1777) - - -def InitNetwork(): - _RunDockerOrDie(["network", "create", "--driver", "bridge", NETWORK_NAME]) - - -def StartFleet(): - specs = list(TRAIN_CLUSTER_SPEC.values()) + list(DECODE_CLUSTER_SPEC.values()) - for job_machines in specs: - for machine_port in job_machines: - machine_name = _Machine(machine_port) - _RunDockerOrDie([ - "run", "--rm", "--name", machine_name, "-dit", "--network", - NETWORK_NAME, "-v", ":".join([SHARED_FS_MOUNTPOINT] * 2), "-v", - ":".join([DATADIR] * 2 + ["ro"]), DOCKER_IMAGE_NAME, "bash" - ]) - - -def MakeFlagClusterSpec(cluster_spec): - job_specs = [] - for job_name in sorted(cluster_spec.keys()): - job_specs += [job_name + "=" + ",".join(cluster_spec[job_name])] - flag_spec = "@".join(job_specs) - return flag_spec - - -def CopyTrainerToSharedMount(): - shutil.copy(TRAINER_PACKAGE, SHARED_FS_MOUNTPOINT + "/trainer.par") - - -def InstallAndStartProcess(cluster_spec): - """Unpacks the trainer and kick off training.""" - cluster_spec_flag = MakeFlagClusterSpec(cluster_spec) - for job_name, machines in cluster_spec.items(): - task_idx = 0 - for machine_port in machines: - machine_name = _Machine(machine_port) - _ExecInDocker( - machine_name, [ - os.path.join(SHARED_FS_MOUNTPOINT, "trainer.par"), - "--cluster_spec=%s" % cluster_spec_flag, - "--job=%s" % job_name, - "--task=%d" % task_idx, - "--mode=%s" % TRAIN_MODE, - "--logtostderr", - "--model=%s" % MODEL, - "--logdir=%s/log" % SHARED_FS_MOUNTPOINT, - ], - workdir="/tmp", - logfile="%s/%s.%d.log" % (SHARED_FS_MOUNTPOINT, job_name, task_idx), - detach=True) - task_idx += 1 - - -def main(): - Cleanup() - InitFiles() - InitNetwork() - StartFleet() - CopyTrainerToSharedMount() - InstallAndStartProcess(TRAIN_CLUSTER_SPEC) - for role in sorted(DECODE_CLUSTER_SPEC.keys()): - # Each decode process is its own spec. - machine_spec = DECODE_CLUSTER_SPEC[role] - InstallAndStartProcess({role: machine_spec}) - - -if __name__ == "__main__": - main() diff --git a/docs/apidoc/conf.py b/docs/apidoc/conf.py deleted file mode 100644 index 9edba272..00000000 --- a/docs/apidoc/conf.py +++ /dev/null @@ -1,217 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Configuration file for the Sphinx documentation builder. - -This file does only contain a selection of the most common options. For a -full list see the documentation: -http://www.sphinx-doc.org/en/master/config -""" - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, u'/tmp/lingvo/lingvo') - -# -- Project information ----------------------------------------------------- - -project = u'Lingvo' -copyright = u'2018' -author = u'' - -# The short X.Y version -version = u'' -# The full version, including alpha/beta/rc tags -release = u'' - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.mathjax', - 'sphinx.ext.napoleon', 'sphinx.ext.todo', 'sphinx.ext.viewcode' -] - -autodoc_default_flags = [ - 'members', 'undoc-members', 'private-members', 'show-inheritance' -] -autodoc_member_order = 'bysource' -napoleon_google_docstring = True -default_role = 'py:obj' -intersphinx_mapping = { - 'python': ('https://docs.python.org/3.7', None), - 'numpy': ('http://docs.scipy.org/doc/numpy/', None), -} - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - - - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -from docutils.transforms import Transform -from recommonmark.parser import CommonMarkParser -source_parsers = { - '.md': CommonMarkParser, -} -source_suffix = ['.rst', '.md'] - -# The master toctree document. -master_doc = 'index' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = 'en' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path . -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = [] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'lingvodoc' - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'lingvo.tex', u'Lingvo Documentation', u'', 'manual'), -] - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [(master_doc, 'lingvo', u'Lingvo Documentation', [author], 1)] - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'lingvo', u'Lingvo Documentation', author, 'Lingvo', - 'One line description of project.', 'Miscellaneous'), -] - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project -epub_author = author -epub_publisher = author -epub_copyright = copyright - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - -# -- Extension configuration ------------------------------------------------- - -# -- Options for todo extension ---------------------------------------------- - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True - - -class ResetFlags(Transform): - - default_priority = 999 - - def apply(self): - from absl import flags # pylint: disable=g-import-not-at-top - for flag in list(flags.FLAGS): - if flag not in ('showprefixforinfo',): - delattr(flags.FLAGS, flag) - - -def setup(app): - app.add_transform(ResetFlags) diff --git a/docs/apidoc/index.rst b/docs/apidoc/index.rst deleted file mode 100644 index 385c88c6..00000000 --- a/docs/apidoc/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. lingvo documentation master file, created by - sphinx-quickstart on Thu Aug 30 16:01:44 2018. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to lingvo's documentation! -================================== - -.. include:: modules.rst - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/generate_apidoc.sh b/docs/generate_apidoc.sh deleted file mode 100755 index d59839fc..00000000 --- a/docs/generate_apidoc.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -e -OUTDIR=/tmp/lingvo_apidoc - -# Run this from inside docker. -bazel build -c opt \ - //lingvo/core/ops:x_ops.so \ - //lingvo/tasks/car/ops:car_ops.so \ - //lingvo/core:inference_graph_pb2.py \ - //lingvo/core:hyperparams_pb2.py \ - //lingvo/core/ops:hyps_pb2.py \ - //lingvo/core/ops:record_py_pb2 \ - //lingvo/core/ops:versioned_file_set_py_pb2 \ - //lingvo/tasks/mt:text_input_pb2.py \ - 2>&1 -cp -f bazel-bin/lingvo/core/ops/x_ops.so lingvo/core/ops -cp -f bazel-bin/lingvo/tasks/car/ops/car_ops.so lingvo/tasks/car/ops -cp -f bazel-genfiles/lingvo/core/inference_graph_pb2.py lingvo/core -cp -f bazel-genfiles/lingvo/core/hyperparams_pb2.py lingvo/core -cp -f bazel-genfiles/lingvo/core/ops/hyps_pb2.py lingvo/core/ops -cp -f bazel-genfiles/lingvo/core/ops/record_pb2.py lingvo/core/ops -cp -f bazel-genfiles/lingvo/core/ops/versioned_file_set_pb2.py lingvo/core/ops -cp -f bazel-genfiles/lingvo/tasks/mt/text_input_pb2.py lingvo/tasks/mt -sphinx-apidoc -o "$OUTDIR" -efPM --implicit-namespaces lingvo/ $(find . -name '*_test.py') -cp docs/apidoc/{conf.py,index.rst} "$OUTDIR" -(export PYTHONPATH="$(pwd)" && cd "$OUTDIR" && sphinx-build -b html -T -j auto . build) -rm -f lingvo/core/{inference_graph_pb2.py,hyperparams_pb2.py} -rm -f lingvo/core/ops/{x_ops.so,hyps_pb2.py,record_pb2.py,versioned_file_set_pb2.py} -rm -f lingvo/tasks/car/ops/car_ops.so -rm -f lingvo/tasks/mt/text_input_pb2.py diff --git a/docs/single/build.sh b/docs/single/build.sh deleted file mode 100755 index 2ccd2e21..00000000 --- a/docs/single/build.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# Copyright 2020 The TensorFlow 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. -# ============================================================================== - -cd "${0%/*}" # Change to script directory. - -if ! command -v sphinx-build &> /dev/null -then - echo "sphinx could not be found. Please install with 'pip3 install sphinx sphinx_rtd_theme recommonmark.'" - exit -fi - -cleanup() { - rm -rf single.py* _build __pycache__ - exit -} -trap cleanup ERR INT TERM - -echo "Paste the docstring to check (end with ctrl+D)." -cat > single.py - -sphinx-build -b singlehtml -q -T . _build -cleanup diff --git a/docs/single/conf.py b/docs/single/conf.py deleted file mode 100644 index cd99c06b..00000000 --- a/docs/single/conf.py +++ /dev/null @@ -1,52 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Configuration file for the Sphinx documentation builder.""" - -import os -import sys -from recommonmark.parser import CommonMarkParser - -# enable autodoc to load local modules -sys.path.insert(0, os.path.abspath('.')) - -project = 'Lingvo' -copyright = '2020' # pylint: disable=redefined-builtin -author = '' -extensions = [ - 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.mathjax', - 'sphinx.ext.napoleon', 'sphinx.ext.todo', 'sphinx.ext.viewcode' -] -autodoc_default_flags = [ - 'members', 'undoc-members', 'private-members', 'show-inheritance' -] -autodoc_member_order = 'bysource' -napoleon_google_docstring = True -default_role = 'py:obj' -intersphinx_mapping = { - 'python': ('https://docs.python.org/3.7', None), - 'numpy': ('http://docs.scipy.org/doc/numpy/', None), -} -templates_path = ['_templates'] -source_parsers = { - '.md': CommonMarkParser, -} -source_suffix = ['.rst', '.md'] -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] -pygments_style = 'sphinx' -html_theme = 'sphinx_rtd_theme' -html_static_path = [] -html_theme_options = {'nosidebar': True} -todo_include_todos = True diff --git a/docs/single/index.rst b/docs/single/index.rst deleted file mode 100644 index b8de17c4..00000000 --- a/docs/single/index.rst +++ /dev/null @@ -1,5 +0,0 @@ -single -========= - -.. automodule:: single - :members: diff --git a/lingvo/.style.yapf b/lingvo/.style.yapf deleted file mode 100644 index 7c93ab7b..00000000 --- a/lingvo/.style.yapf +++ /dev/null @@ -1,3 +0,0 @@ -[style] -based_on_style = google -indent_width = 2 \ No newline at end of file diff --git a/lingvo/BUILD b/lingvo/BUILD deleted file mode 100644 index bf8f795c..00000000 --- a/lingvo/BUILD +++ /dev/null @@ -1,311 +0,0 @@ -load( - "//lingvo:lingvo.bzl", - "lingvo_py_binary", -) - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) - -exports_files(["LICENSE"]) - -config_setting( - name = "cuda", - values = {"define": "using_cuda=true"}, -) - -py_library( - name = "base_runner", - srcs = ["base_runner.py"], - srcs_version = "PY3", - deps = [ - ":base_trial", - ":compat", - "//lingvo/core:cluster_factory", - "//lingvo/core:early_stop", - "//lingvo/core:py_utils", - # Implicit tensorflow py proto dependency. - ], -) - -py_library( - name = "base_trial", - srcs = ["base_trial.py"], - srcs_version = "PY3", - deps = [ - "//lingvo/core:hyperparams", - ], -) - -py_library( - name = "compat", - srcs = ["compat.py"], - srcs_version = "PY3", - deps = [ - # Implicit absl.flags dependency. - # Implicit absl.logging dependency. - # Implicit tensorflow dependency. - ], -) - -py_test( - name = "compat_test", - srcs = ["compat_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":compat", - ], -) - -py_library( - name = "model_imports_no_params", - srcs = ["model_imports.py"], - srcs_version = "PY3", -) - -# Depend on this for access to the model registry with params for all tasks as -# transitive deps. Only py_binary should depend on this target. -py_library( - name = "model_imports", - srcs_version = "PY3", - deps = [ - ":model_imports_no_params", - "//lingvo/tasks:all_params", - ], -) - -py_test( - name = "model_import_test", - srcs = ["model_import_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":compat", - ":model_imports_no_params", - ], -) - -py_library( - name = "models_test_helper", - testonly = 1, - srcs = ["models_test_helper.py"], - srcs_version = "PY3", - deps = [ - ":compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:base_model", - "//lingvo/core:bn_layers", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - ], -) - -py_test( - name = "models_test", - srcs = ["models_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":compat", - ":model_imports", - ":model_registry", - ":model_registry_test_lib", - ":models_test_helper", - "//lingvo/core:base_model", - ], -) - -py_library( - name = "model_registry", - srcs = ["model_registry.py"], - srcs_version = "PY3", - deps = [ - ":compat", - ":model_imports_no_params", - "//lingvo/core:base_model_params", - ], -) - -py_test( - name = "model_registry_test", - srcs = ["model_registry_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [":model_registry_test_lib"], -) - -py_library( - name = "model_registry_test_lib", - testonly = 1, - srcs = ["model_registry_test.py"], - srcs_version = "PY3", - deps = [ - ":compat", - ":model_registry", - "//lingvo/core:base_input_generator", - "//lingvo/core:base_model", - "//lingvo/core:base_model_params", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "datasets_lib", - srcs = ["datasets.py"], - srcs_version = "PY3", -) - -py_library( - name = "trainer_lib", - srcs = ["trainer.py"], - srcs_version = "PY3", - deps = [ - ":base_trial", - ":compat", - ":datasets_lib", - ":executor_lib", - ":model_imports_no_params", - ":model_registry", - # Implicit network file system dependency. - "//lingvo:base_runner", - # Implicit IPython dependency. - "//lingvo/core:base_layer", - "//lingvo/core:base_model", - "//lingvo/core:base_model_params", - "//lingvo/core:checkpointer_lib", - "//lingvo/core:cluster_factory", - "//lingvo/core:inference_graph_exporter", - "//lingvo/core:metrics", - "//lingvo/core:py_utils", - "//lingvo/core:summary_utils", - # Implicit numpy dependency. - # Implicit tensorflow grpc dependency. - ], -) - -lingvo_py_binary( - name = "trainer", - srcs = [":trainer_lib"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":model_imports", - ":trainer_lib", - ], -) - -py_test( - name = "trainer_test", - size = "large", - timeout = "long", - srcs = ["trainer_test.py"], - python_version = "PY3", - shard_count = 12, - srcs_version = "PY3", - tags = [ - "noasan", - "nomsan", - "notsan", - "optonly", - ], - deps = [ - ":base_trial", - ":compat", - ":model_registry", - ":trainer_lib", - # Implicit absl.testing.flagsaver dependency. - "//lingvo/core:base_input_generator", - "//lingvo/core:base_model", - "//lingvo/core:base_model_params", - "//lingvo/core:hyperparams", - "//lingvo/core:inference_graph_py_pb2", - "//lingvo/core:test_utils", - "//lingvo/core:trainer_test_utils", - "//lingvo/tasks/image:input_generator", - "//lingvo/tasks/image/params:mnist", # build_cleaner: keep - "//lingvo/tasks/punctuator/params:codelab", # build_cleaner: keep - # Implicit numpy dependency. - ], -) - -py_library( - name = "trainer_test_lib", - testonly = 1, - srcs = ["trainer_test.py"], - srcs_version = "PY3", - tags = [ - "noasan", - "nomsan", - "notsan", - "optonly", - ], - deps = [ - ":base_trial", - ":compat", - ":model_registry", - ":trainer_lib", - "//lingvo/core:base_input_generator", - "//lingvo/core:base_layer", - "//lingvo/core:base_model", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - "//lingvo/core:trainer_test_utils", - "//lingvo/tasks/image:input_generator", - "//lingvo/tasks/image/params:mnist", - "//lingvo/tasks/punctuator/params:codelab", - # Implicit numpy dependency. - ], -) - -lingvo_py_binary( - name = "ipython_kernel", - srcs = ["ipython_kernel.py"], - data = [ - "//lingvo/tasks/punctuator/tools:download_brown_corpus", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":model_imports", # build_cleaner: keep - ":trainer_lib", # build_cleaner: keep - # Implicit absl.app dependency. - "//lingvo/core:predictor_lib", # build_cleaner: keep - "//lingvo/core/ops:hyps_py_pb2", # build_cleaner: keep - ], -) - -genrule( - name = "tf_dot_protos", - srcs = [], - outs = ["tf_protos.tar"], - cmd = - "$(location //lingvo/tools:" + - "generate_tf_dot_protos) $(location " + - "//lingvo/tools:generate_proto_def) $(@D)", - tools = [ - "//lingvo/tools:generate_proto_def", - "//lingvo/tools:generate_tf_dot_protos", - ], -) - -py_library( - name = "executor_lib", - srcs = ["executor.py"], - srcs_version = "PY3", - deps = [ - ":compat", - # Implicit network file system dependency. - "//lingvo:base_runner", - "//lingvo/core:base_model", - "//lingvo/core:checkpointer_lib", - "//lingvo/core:cluster_factory", - "//lingvo/core:ml_perf_log", - "//lingvo/core:multitask_model", - "//lingvo/core:py_utils", - "//lingvo/core:task_scheduler", - # Implicit tensorflow grpc dependency. - ], -) diff --git a/lingvo/__init__.py b/lingvo/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/base_runner.py b/lingvo/base_runner.py deleted file mode 100644 index 71c24931..00000000 --- a/lingvo/base_runner.py +++ /dev/null @@ -1,424 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Base class for all jobs.""" - -import os -import time -import traceback - -from lingvo import base_trial -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import early_stop -from lingvo.core import py_utils - - -class BaseRunner: - """Base class for all jobs.""" - - def __init__(self, - params, - model_task_name, - logdir, - tf_master, - trial=base_trial.NoOpTrial()): - """Construct a new BaseRunner. - - Args: - params: Params object containing model configuration. - model_task_name: String name of the task this runner should execute for - multitask models only. See flag for details. - logdir: String path to the log directory to output to. - tf_master: String path to the master job, e.g. 'local'. - trial: An optional hyperparameter trial. Used by Vizier studies. - """ - p = params.Copy() - # Set in subclasses. - self._job_name = '' - - self._params = trial.OverrideModelParams(p) - tf.logging.info('=' * 60) - for line in self.params.ToText().split('\n'): - tf.logging.info('%s', line) - tf.logging.info('=' * 60) - tf.logging.info('FLAGS') - for attribute in dir(tf.flags.FLAGS): - tf.logging.info("%s=%s", attribute, getattr(tf.flags.FLAGS, attribute)) - tf.logging.info('=' * 60) - - self._logdir = logdir - self._tf_master = tf_master - self._model_task_name = model_task_name - self._trial = trial - # If the runner is conducting a Vizier trial, scope all the variables - # (e.g., global_step) by the trial id so that we do not share states across - # trials. - self._container_id = self._trial.Name() - self._should_report_metrics = False - - # To early terminate a runner, we set max_steps here and that will trigger - # appropriate ShouldStop behavior in the threads. This is used by Vizier - # to early stop a trial. - self._max_steps = None - - self.params.cluster.logdir = logdir - self._cluster = cluster_factory.Cluster(self.params.cluster) - self._train_dir = os.path.join(self._logdir, 'train') - tf.io.gfile.makedirs(self._train_dir) - self._graph = tf.Graph() - self._summary_writer = None - self._initialize_tables = None - self._dequeue_thread_complete = False - - early_stop.MetricHistory.SetLogdirInMetricHistories(p, logdir) - self._early_stop = None - if p.train.early_stop and p.train.early_stop.window: - self._early_stop = early_stop.EarlyStop(p.train.early_stop) - with self._graph.as_default(): - self._early_stop.FProp(None) - - self._SetStatusMessage('Starting ...') - - @property - def params(self): - return self._params - - def _InVizierStudy(self): - return not isinstance(self._trial, base_trial.NoOpTrial) - - def _FormatStatusMessage(self, message, retrying): - if self._trial.Name(): - message = 'Trial:{} {}'.format(self._trial.Name(), message) - if retrying: - message = f'Job {self._job_name}: Retrying as expected\n{message}' - return message - - def _SetStatusMessage(self, message, retrying=False): - """Update the status message for this task.""" - tf.logging.info(self._FormatStatusMessage(message, retrying)) - - def _ShouldStop(self, sess, step): - """Check if the runner should stop.""" - if step >= self.params.train.max_steps: - tf.logging.info('ShouldStop: step:%6d params.train.max_steps:%6d', - step, self.params.train.max_steps) - return True - - if self._max_steps and step >= self._max_steps: - tf.logging.info('ShouldStop: step:%6d _max_steps:%6d', step, - self._max_steps) - return True - - if self._early_stop and self._early_stop.Stop(sess): - tf.logging.info('ShouldStop: Early stopping.') - return True - - return False - - def _WriteToLog(self, text, logdir, filename): - """Logs `text` and saves it under `logdir/filename`.""" - with tf.io.gfile.GFile(os.path.join(logdir, filename), 'w') as f: - f.write(text) - - if self._summary_writer is not None: - # Force newlines to be rendered correctly by Markdown. - text = text.replace('\n', ' \n') - self._summary_writer.add_summary( - tf.Summary(value=[ - tf.Summary.Value( - tag=filename, tensor=tf.make_tensor_proto([text])) - ])) - - def _WaitUntilInit(self, sess, start_up_delay_steps=None): - """Wait until the model is ready.""" - # Wait a fix amount of time at start. - time.sleep(30) - - @py_utils.Retry(retry_value=(tf.errors.FailedPreconditionError,)) - def RetryLoop(): - try: - global_step = sess.run(py_utils.GetGlobalStep()) - except tf.errors.FailedPreconditionError as e: - tf.logging.info('%s: Probably the expected race on global_step: %s', - self._job_name, e) - raise - msg = 'step:%6d' % global_step - self._SetStatusMessage(msg) - if start_up_delay_steps: - if global_step < start_up_delay_steps: - msg = 'global step (%d) has not reached start up delay steps (%d)' % ( - global_step, self._start_up_delay_steps) - tf.logging.info('%s: %s', self._job_name, msg) - raise tf.errors.FailedPreconditionError( - node_def=None, op=None, message=msg) - return global_step - - return RetryLoop() - - @py_utils.Retry( - initial_delay_sec=1, delay_growth_factor=1.5, max_delay_sec=300) - def _FindNewCheckpoint(self, prev_path, sess): - """Returns the path to a new checkpoint, or raises RuntimeError.""" - if self._trial.ShouldStop() or self._ShouldStop(sess, 0): - return None - path = tf.train.latest_checkpoint(self._train_dir) - if not path: - msg = 'No check point is found in %s' % self._train_dir - tf.logging.info('%s: %s', self._job_name, msg) - raise RuntimeError(msg) - if path == prev_path: - msg = 'No new check point is found: %s' % path - tf.logging.info('%s: %s', self._job_name, msg) - raise RuntimeError(msg) - return path - - @py_utils.Retry(max_retries=0) - def _RunLoop(self, job_name, loop_func, loop_args=()): - """Runs `loop_func`, retrying on expected errors. - - Args: - job_name: string job name. - loop_func: callable to run and retry on expected errors. - loop_args: list or tuple of arguments to be passed to the loop_func. - """ - try: - tf.logging.info('%s started.', job_name) - loop_func(*loop_args) - tf.logging.info('%s done.', job_name) - return - except Exception as e: # pylint:disable=broad-except - fatal_error_msgs = [ - 'Compilation failure', - 'Run-time shape mismatch for TPUExecute argument' - ] - if any([x in str(e) for x in fatal_error_msgs]): - # Fatal error if failing to compile graph on TPU. - retry = False - elif isinstance(e, tf.errors.AbortedError): - # AbortedError: is thrown when processes restarts. - retry = True - if self._InVizierStudy(): - # With Vizier studies, we want to avoid retrying under some error - # conditions, these are captured here. - # Do not retry (via raise/retry) if AbortedError with RecvTensor - # message. This can happen if there are memory issues. - if ('The same RecvTensor (WorkerServiceImpl) request was received ' - 'twice' in str(e)): - retry = False - tf.logging.info('%s done (infeasible error).', job_name) - elif isinstance(e, tf.errors.OutOfRangeError): - # OutOfRangeError: Test/dev datasets are exhausted. - retry = self._cluster.do_eval - elif isinstance(e, tf.errors.InvalidArgumentError): - # InvalidArgumentError: variables were not initialized. Comes from - # ResourceVariableOp. - retry = True - # Do not retry within Vizier study when NaNs cause InvalidArgumentError. - if self._InVizierStudy(): - if 'Tensor had NaN values' in str(e): - retry = False - tf.logging.info('%s done (infeasible result due to NaN values).', - job_name) - elif isinstance( - e, py_utils.transient_tf_errors + - (tf.errors.DataLossError, tf.errors.CancelledError)): - # Retry on these errors. - # FailedPreconditionError: variables are not initialized. - # DataLossError: Race condition between evaler and trainer when saving - # or removing checkpoints. - # CancelledError: Node was closed (on TPU). - if isinstance(e, tf.errors.FailedPreconditionError): - # GALV: This error frequently was due to global_step not - # being initialized, which as far as I can tell is a hard - # error, so I disabling retrying in this case. - retry = False - else: - retry = True - else: - retry = False - - if retry: - # Retry indefinitely (error should be transient). - self._SetStatusMessage( - '%s exception: %s\n' % (job_name, e), retrying=True) - - for msg in traceback.format_exc().split('\n'): - tf.logging.vlog(1, msg) - - raise - else: - # Allow the job to complete on errors that are unlikely to be transient, - # e.g. caused by a mis-configured model. - if self._should_report_metrics: - self._trial.ReportDone( - infeasible=True, infeasible_reason='Fatal error encountered.') - tf.logging.error('%s done (fatal error): %s', job_name, type(e)) - - self._SetStatusMessage('%s exception: %s\n' % (job_name, e)) - - # Prints the error message line by line to avoid message cropping. - msgv = traceback.format_exc().split('\n') - for msg in msgv: - tf.logging.error(msg) - - # Check if we are potentially running within an experiment. If so, - # the worker should continue to the next trial instead of terminating - # the process. - if self._InVizierStudy(): - return - - # tf.logging.fatal prints out stack traces. Typically, that's not - # useful at all here. Here we want to exit the program - # definitively. Because LOG(QFATAL) is not easily available via - # python so far, we need a way to exit the program directly. - # Because sys.exit(1) must be called from the main thread, and does - # not cancel non-daemon threads anyway, we use os._exit instead. - # Because tf.logging.error() may return before the flush is complete, - # we need an extra sleep before exit. - time.sleep(15) - # GALV: print() statements will not be flushed by os._exit, so - # I flush sys.stdouthere! - import sys; sys.stdout.flush() - os._exit(1) # pylint: disable=protected-access - - def _DequeueThreadComplete(self): - self._dequeue_thread_complete = True - return - - def _LoopEnqueue(self, op, session_override=None): - """Runs the enqueue op in a loop.""" - p = self.params - sess = session_override or self._GetSession() - - with tf.container(self._container_id), sess: - if self._initialize_tables is not None: - sess.run(self._initialize_tables) - gsteps = py_utils.GetGlobalStep() - local_enqueue_steps = 0 - - # Global enqueue steps measures how many global steps have data enqueued - # for already. We use this to terminate; note that the enqueue op may - # hang in session.run if we do not terminate with this check. - global_enqueue_steps = None - - tf.logging.info('params.train.max_steps: %d, enqueue_max_steps: %d', - p.train.max_steps, p.train.enqueue_max_steps) - while True: - if self._dequeue_thread_complete: - tf.logging.info( - 'LoopEnqueue done since consuming thread is done.') - return - - global_step = sess.run(gsteps) - if global_enqueue_steps is None: - global_enqueue_steps = global_step - if local_enqueue_steps % 1000 == 0: - tf.logging.info( - 'Current global_enqueue_steps: %d, ' - 'local_enqueue_steps: %d, global_step: %d', global_enqueue_steps, - local_enqueue_steps, global_step) - - if py_utils.use_tpu(): - global_steps_with_available_data = int(global_enqueue_steps // - p.train.tpu_steps_per_loop * - p.train.tpu_steps_per_loop) - else: - global_steps_with_available_data = global_enqueue_steps - - if (self._ShouldStop(sess, global_steps_with_available_data) or - self._ShouldStop(sess, global_step)): - tf.logging.info('Done. ShouldStop is True.') - tf.logging.info('Enqueue loop sleeping') - time.sleep(15) - continue - if (p.train.enqueue_max_steps > 0 and - local_enqueue_steps >= p.train.enqueue_max_steps): - tf.logging.info('Done. train.enqueue_max_steps reached.') - tf.logging.info('Enqueue loop sleeping') - time.sleep(15) - continue - local_enqueue_steps += 1 - - # There are tpu_infeed_parallelism parallel threads enqueuing. - # We account for all of them when updating global_enqueue_steps. - global_enqueue_steps += p.input.tpu_infeed_parallelism - - sess.run([op]) - - def _GetSession(self, **kwargs): - graph = kwargs.pop('graph', self._graph) - return tf.Session( - self._tf_master, graph=graph, config=py_utils.SessionConfig(**kwargs)) - - def GetTrainDir(self): - return self._train_dir - - @classmethod - def _GetTtlDir(cls, path, duration): - """Returns a path to a time-limited directory under dir if required.""" - del duration - return path - - def _CreateSummaryWriter(self, logdir): - """Creates and returns a tf summary writer.""" - return tf.summary.FileWriter(logdir) - - def _WriteSummaries(self, - summary_writer, - job_name, - global_step, - summaries, - text_filename=None): - """Construct the summary and write them to the summary writer. - - Args: - summary_writer: The summary writer to use. - job_name: The name of the job that tries to write this summary. - global_step: The checkpoint used for eval is generated at this step. - summaries: a dict from keys to `tf.Summary` protobuf messages. - text_filename: If not None, writes the summary to the text file. - """ - status_metrics = [] - for name, summary in sorted(summaries.items()): - if not isinstance(summary, tf.summary.Summary): - tf.logging.warning( - 'Non tf.Summary args passed to _WriteSummaries, skipping: ' - 'job:%s name:%s @%s', job_name, name, global_step) - continue - summary_writer.add_summary(summary, global_step) - if summary.value: - for value in summary.value: - if value.HasField('simple_value'): - tf.logging.info('%s summary on checkpoint@%d %s = %.8g', - job_name, global_step, value.tag, - value.simple_value) - status_metrics.append('%s: %.8g' % (value.tag, value.simple_value)) - early_stop.MetricHistory.ConditionalAppend(job_name, value.tag, - global_step, - value.simple_value) - else: - tf.logging.info('%s summary on checkpoint@%d %s', job_name, - global_step, value.tag) - summary_writer.flush() - self._SetStatusMessage('%s: step:%6d, %s' % - (job_name, global_step, ', '.join(status_metrics))) - if text_filename is not None: - with tf.io.gfile.GFile(text_filename, 'w') as f: - f.write('\n'.join(status_metrics)) - - def _ExportMetrics(self, **kwargs): - """Exports metrics externally.""" - pass diff --git a/lingvo/base_trial.py b/lingvo/base_trial.py deleted file mode 100644 index 3664bbe2..00000000 --- a/lingvo/base_trial.py +++ /dev/null @@ -1,126 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Defines trials for parameter exploration.""" - -import time - -from lingvo.core import hyperparams - - -class Trial: - """Base class for a trial.""" - - @classmethod - def Params(cls): - """Default parameters for a trial.""" - p = hyperparams.Params() - p.Define( - 'report_interval_seconds', 600, - 'Interval between reporting trial results and checking for early ' - 'stopping.') - p.Define('vizier_objective_metric_key', 'loss', - 'Which eval metric to use as the "objective value" for tuning.') - p.Define( - 'report_during_training', False, - 'Whether to report objective metrics during the training process.') - return p - - def __init__(self, params): - self._params = params.Copy() - self._next_report_time = time.time() - - @property - def report_interval_seconds(self): - return self._params.report_interval_seconds - - @property - def objective_metric_key(self): - return self._params.vizier_objective_metric_key - - def Name(self): - raise NotImplementedError('Abstract method') - - def OverrideModelParams(self, model_params): - """Modifies `model_params` according to trial params. - - Through this method a `Trial` may tweak model hyperparams (e.g., learning - rate, shape, depth, or width of networks). - - Args: - model_params: the original model hyperparams. - - Returns: - The modified `model_params`. - """ - raise NotImplementedError('Abstract method') - - def ShouldStop(self): - """Returns whether the trial should stop.""" - raise NotImplementedError('Abstract method') - - def ReportDone(self, infeasible=False, infeasible_reason=''): - """Report that the trial is completed.""" - raise NotImplementedError('Abstract method') - - def ShouldStopAndMaybeReport(self, global_step, metrics_dict): - """Returns whether the trial should stop. - - Args: - global_step: The global step counter. - metrics_dict: If not None, contains the metric should be - reported. If None, do nothing but returns whether the - trial should stop. - """ - if not metrics_dict or not self._params.report_during_training: - return self.ShouldStop() - if time.time() < self._next_report_time: - return False - self._next_report_time = time.time() + self.report_interval_seconds - return self._DoReportTrainingProgress(global_step, metrics_dict) - - def _DoReportTrainingProgress(self, global_step, metrics_dict): - raise NotImplementedError('Abstract method') - - def ReportEvalMeasure(self, global_step, metrics_dict, checkpoint_path): - """Reports eval measurement and returns whether the trial should stop.""" - raise NotImplementedError('Abstract method') - - -class NoOpTrial(Trial): - """A Trial implementation that does nothing.""" - - def __init__(self): - super().__init__(Trial.Params()) - - def Name(self): - return '' - - def OverrideModelParams(self, model_params): - return model_params - - def ShouldStop(self): - return False - - def ReportDone(self, infeasible=False, infeasible_reason=''): - return False - - def ShouldStopAndMaybeReport(self, global_step, metrics_dict): - del global_step, metrics_dict # Unused - return False - - def ReportEvalMeasure(self, global_step, metrics_dict, checkpoint_path): - del global_step, metrics_dict, checkpoint_path # Unused - return False diff --git a/lingvo/compat.py b/lingvo/compat.py deleted file mode 100644 index 06901c6f..00000000 --- a/lingvo/compat.py +++ /dev/null @@ -1,411 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""The compatible tensorflow library.""" - -import tensorflow.compat.v1 as tf1 -from tensorflow.compat.v2 import * # pylint:disable=wildcard-import, g-bad-import-order - -# Import absl.flags and absl.logging to overwrite the Tensorflow ones. -# This is the intended behavior in TF 2.0. -# pylint:disable=g-bad-import-order, unused-import, g-import-not-at-top -from absl import flags -from absl import logging -# pylint: disable=g-direct-tensorflow-import - -from tensorflow.core.protobuf import config_pb2 -from tensorflow.python.data.ops import dataset_ops -from tensorflow.python.data.util import random_seed -from tensorflow.python.framework import function as _function_lib -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import check_ops -from tensorflow.python.ops import embedding_ops -from tensorflow.python.ops import functional_ops -from tensorflow.python.ops import gen_dataset_ops -from tensorflow.python.ops import gen_io_ops -from tensorflow.python.ops import inplace_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.platform import app -from tensorflow.python.util import module_wrapper as _module_wrapper -# pylint: enable=g-direct-tensorflow-import -# pylint: enable=unused-import, g-bad-import-order, g-import-not-at-top - -tf1.disable_eager_execution() - -if tf1.executing_eagerly(): - logging.warning("Lingvo does not support eager execution yet. Please disable " - "eager execution with tf.compat.v1.disable_eager_execution() " - "or proceed at your own risk.") - - -def _clone_module(m): - """Shallow clone of module `m`.""" - if isinstance(m, _module_wrapper.TFModuleWrapper): - # pylint: disable=protected-access - return _module_wrapper.TFModuleWrapper( - wrapped=_clone_module(m._tfmw_wrapped_module), - module_name=m._tfmw_module_name, - public_apis=m._tfmw_public_apis, - deprecation=m._tfmw_print_deprecation_warnings, - has_lite=m._tfmw_has_lite) - # pylint: enable=protected-access - out = type(m)(m.__name__, m.__doc__) - out.__dict__.update(m.__dict__) - return out - -# Aliases to a few routines lingvo libraries uses often. -Defun = _function_lib.Defun -While = functional_ops.While -If = functional_ops.If -InplaceUpdate = inplace_ops.alias_inplace_update -Empty = inplace_ops.empty -EmptyLike = inplace_ops.empty_like - -# pylint: disable=undefined-variable, used-before-assignment -# Move this V2 symbol here to avoid being overwritten by its following V1 -# version. -where_v2 = where -while_loop_v2 = while_loop - -# Import the local V2 module to maker sure the following V1 overwritting never -# applies to the global module and symbol. -data = _clone_module(data) -graph_util = _clone_module(graph_util) -image = _clone_module(image) -io = _clone_module(io) -losses = _clone_module(keras.losses) -metrics = _clone_module(keras.metrics) -nn = _clone_module(nn) -saved_model = _clone_module(saved_model) -strings = _clone_module(strings) -summary = _clone_module(summary) -test = _clone_module(test) -train = _clone_module(train) - -# By default, with TF2 enabled and (eager execution or tf.function), -# `tf.data` API will choose the stateful implementation for methods -# `tf.data.Dataset.shuffle()`, `tf.data.Dataset.cache()` and -# `tf.data.Dataset.list_files()`. which is not compatible with -# `tf.data.make_one_shot_iterator` in TF2 (see b/162270607). -# Here is a stateless implementation of `shuffle`, `cache` and -# `list_files` to resolve the TF2 imcompatibility issue. - -# Note that, these methods are meant for internal use only. Please don't use -# it unless you know exactly what you do. - - -class _CacheDataset(dataset_ops.UnaryUnchangedStructureDataset): - """A `Dataset` that caches elements of its input.""" - - def __init__(self, input_dataset, filename): - """Caches the elements in the dataset.""" - self._input_dataset = input_dataset - self._filename = ops.convert_to_tensor( - filename, dtype=string, name="filename") - variant_tensor = gen_dataset_ops.cache_dataset( - input_dataset._variant_tensor, # pylint: disable=protected-access - filename=self._filename, - **self._flat_structure) - super(_CacheDataset, self).__init__(input_dataset, variant_tensor) - - -class _ShuffleDataset(dataset_ops.UnaryUnchangedStructureDataset): - """A `Dataset` that randomly shuffles the elements of its input.""" - - def __init__(self, - input_dataset, - buffer_size, - seed=None, - reshuffle_each_iteration=None): - """Randomly shuffles the elements of this dataset.""" - self._input_dataset = input_dataset - self._buffer_size = ops.convert_to_tensor( - buffer_size, dtype=int64, name="buffer_size") - self._seed, self._seed2 = random_seed.get_seed(seed) - if reshuffle_each_iteration is None: - reshuffle_each_iteration = True - self._reshuffle_each_iteration = reshuffle_each_iteration - - variant_tensor = gen_dataset_ops.shuffle_dataset( - input_dataset._variant_tensor, # pylint: disable=protected-access - buffer_size=self._buffer_size, - seed=self._seed, - seed2=self._seed2, - reshuffle_each_iteration=self._reshuffle_each_iteration, - **self._flat_structure) - super(_ShuffleDataset, self).__init__(input_dataset, variant_tensor) - - -def stateless_shuffle_dataset(buffer_size, - seed=None, - reshuffle_each_iteration=None): - """Randomly shuffles the elements of the dataset based on a stateless shuffle implementation. - - This method returns a stateless ShuffleDataset unconditionally. It can be - used with `dataset.apply()` to obtain a stateless shuffled dataset, which - supports the TF1 compatibility API `tf.data.make_one_shot_iterator()` in TF2. - Example: - >>> dataset = tf.data.Dataset.range(3) - >>> dataset = dataset.apply( - ... stateless_shuffle_dataset((3, reshuffle_each_iteration=True)) - - Args: - buffer_size: A `tf.int64` scalar `tf.Tensor`, representing the number of - elements from this dataset from which the new dataset will sample. - seed: (Optional.) A `tf.int64` scalar `tf.Tensor`, representing the random - seed that will be used to create the distribution. See - `tf.random.set_seed` for behavior. - reshuffle_each_iteration: (Optional.) A boolean, which if true indicates - that the dataset should be pseudorandomly reshuffled each time it is - iterated over. (Defaults to `True`.) - - Returns: - Dataset: A `Dataset`. - """ - - def _apply_fn(dataset): - out_dataset = dataset_ops.DatasetV1Adapter( - _ShuffleDataset(dataset, buffer_size, seed, reshuffle_each_iteration)) - return out_dataset - - return _apply_fn - - -def stateless_cache_dataset(filename=""): - """Caches the elements in the dataset based on a stateless cache implementation. - - This method returns a stateless CacheDataset unconditionally. It can be - used with `dataset.apply()` to obtain a stateless cached dataset, which - supports the TF1 compatibility API `tf.data.make_one_shot_iterator()` in TF2. - - Example: - >>> dataset = tf.data.Dataset.range(3) - >>> dataset = dataset.apply(stateless_cache_dataset()) - - - Args: - filename: A `tf.string` scalar `tf.Tensor`, representing the name of a - directory on the filesystem to use for caching elements in this Dataset. - If a filename is not provided, the dataset will be cached in memory. - - Returns: - Dataset: A `Dataset`. - """ - - def _apply_fn(dataset): - out_dataset = dataset_ops.DatasetV1Adapter(_CacheDataset(dataset, filename)) - return out_dataset - - return _apply_fn - - -def stateless_list_files(file_pattern, shuffle=None, seed=None): - """A dataset of all files matching one or more glob patterns. - - Note that, if `shuffle` is not None, it will use a stateless shuffle - implementation. Then the returned dataset supports the TF1 compatibility API - `tf.data.make_one_shot_iterator()` in TF2. - - Example: - >>> dataset = tf.stateless_list_files("some_file_pattern") - - Args: - file_pattern: A string, a list of strings, or a `tf.Tensor` of string type - (scalar or vector), representing the filename glob (i.e. shell wildcard) - pattern(s) that will be matched. - shuffle: (Optional.) If `True`, the file names will be shuffled randomly - based on a stateless implementation. Defaults to `True`. - seed: (Optional.) A `tf.int64` scalar `tf.Tensor`, representing the random - seed that will be used to create the distribution. See - `tf.random.set_seed` for behavior. - - Returns: - Dataset: A `Dataset` of strings corresponding to file names. - """ - with ops.name_scope("list_files"): - if shuffle is None: - shuffle = True - file_pattern = ops.convert_to_tensor( - file_pattern, dtype=string, name="file_pattern") - matching_files = gen_io_ops.matching_files(file_pattern) - - # Raise an exception if `file_pattern` does not match any files. - condition = math_ops.greater( - array_ops.shape(matching_files)[0], 0, name="match_not_empty") - message = math_ops.add( - "No files matched pattern: ", - strings.reduce_join(file_pattern, separator=", "), - name="message") - - assert_not_empty = debugging.Assert( - condition, [message], summarize=1, name="assert_not_empty") - with control_dependencies([assert_not_empty]): - matching_files = identity(matching_files) - - dataset = data.Dataset.from_tensor_slices(matching_files) - if shuffle: - buffer_size = math_ops.maximum( - shape(matching_files, out_type=dtypes.int64)[0], 1) - # Use stateless shuffled dataset - dataset = dataset.apply(stateless_shuffle_dataset(buffer_size, seed=seed)) - return dataset -# pylint: enable=undefined-variable, used-before-assignment - -# TF 1.x symbols used in the codebase. -# To keep this list short, please use TF 2.x API whenever applicable. -# Only use TF 1.x API if it has no 2.x equivalent. -# pylint: disable=undefined-variable -add_to_collection = tf1.add_to_collection -all_variables = tf1.global_variables -# The following asserts can be directly replaced with TF2 `tf.debugging.*` -# after TF2/eager is enabled. -assert_integer = tf1.assert_integer -assert_positive = tf1.assert_positive -assert_type = tf1.assert_type -assert_scalar = tf1.assert_scalar -assign = tf1.assign -assign_add = tf1.assign_add -assign_sub = tf1.assign_sub -AUTO_REUSE = tf1.AUTO_REUSE -container = tf1.container -data.Dataset = tf1.data.Dataset -data.TFRecordDataset = tf1.data.TFRecordDataset -device = tf1.device -Dimension = tf1.Dimension -disable_eager_execution = tf1.disable_eager_execution -div = tf1.div -enable_eager_execution = tf1.enable_eager_execution -floor_div = tf1.floor_div -get_collection = tf1.get_collection -get_collection_ref = tf1.get_collection_ref -get_default_graph = tf1.get_default_graph -get_local_variable = tf1.get_local_variable -get_seed = tf1.get_seed -get_variable = tf1.get_variable -get_variable_scope = tf1.get_variable_scope -global_variables = tf1.global_variables -global_variables_initializer = tf1.global_variables_initializer -gradients = tf1.gradients -graph_util.convert_variables_to_constants = ( - tf1.graph_util.convert_variables_to_constants) -graph_util.extract_sub_graph = tf1.graph_util.extract_sub_graph -GraphDef = tf1.GraphDef -GraphKeys = tf1.GraphKeys -GraphOptions = tf1.GraphOptions -group = tf1.group -image.resize_bilinear = tf1.image.resize_bilinear -image.resize_images = tf1.image.resize_images -image.resize_nearest_neighbor = tf1.image.resize_nearest_neighbor -initialize_all_tables = tf1.initialize_all_tables -InteractiveSession = tf1.InteractiveSession -io.tf_record_iterator = tf1.io.tf_record_iterator -layers = tf1.layers -local_variables_initializer = tf1.local_variables_initializer -losses.absolute_difference = tf1.losses.absolute_difference -losses.add_loss = tf1.losses.add_loss -losses.compute_weighted_loss = tf1.losses.compute_weighted_loss -losses.get_regularization_loss = tf1.losses.get_regularization_loss -losses.huber_loss = tf1.losses.huber_loss -losses.mean_squared_error = tf1.losses.mean_squared_error -losses.Reduction.MEAN = tf1.losses.Reduction.MEAN -losses.Reduction.SUM = tf1.losses.Reduction.SUM -losses.sigmoid_cross_entropy = tf1.losses.sigmoid_cross_entropy -losses.softmax_cross_entropy = tf1.losses.softmax_cross_entropy -losses.sparse_softmax_cross_entropy = (tf1.losses.sparse_softmax_cross_entropy) -make_template = tf1.make_template -metrics.accuracy = tf1.metrics.accuracy -metrics.auc = tf1.metrics.auc -metrics.precision = tf1.metrics.precision -metrics.recall = tf1.metrics.recall -moving_average_variables = tf1.moving_average_variables -multinomial = tf1.multinomial -name_scope = tf1.name_scope -OptimizerOptions = tf1.OptimizerOptions -placeholder = tf1.placeholder -placeholder_with_default = tf1.placeholder_with_default -Print = tf1.Print -py_func = tf1.py_func -python_io = tf1.python_io -report_uninitialized_variables = tf1.report_uninitialized_variables -reset_default_graph = tf1.reset_default_graph -resource_loader = tf1.resource_loader -RunMetadata = tf1.RunMetadata -RunOptions = tf1.RunOptions -saved_model.build_signature_def = tf1.saved_model.build_signature_def -saved_model.Builder = tf1.saved_model.Builder -saved_model.load = tf1.saved_model.load -saved_model.loader = tf1.saved_model.loader -saved_model.signature_constants = tf1.saved_model.signature_constants -saved_model.simple_save = tf1.saved_model.simple_save -saved_model.tag_constants = tf1.saved_model.tag_constants -saved_model.utils = tf1.saved_model.utils -Session = tf1.Session -sparse_to_dense = tf1.sparse_to_dense -string_split = tf1.string_split -strings.reduce_join = tf1.reduce_join -strings.split = tf1.strings.split -Summary = tf1.Summary -if tf1.summary is not None: - # tf.summary are not supported on TPU so we sometimes set tf.summary to None - # to prohibit the direct use of it. - # It is safe to skip copying tf.summary members in such cases. - summary.audio = tf1.summary.audio - summary.FileWriter = tf1.summary.FileWriter - summary.histogram = tf1.summary.histogram - summary.image = tf1.summary.image - summary.merge = tf1.summary.merge - summary.merge_all = tf1.summary.merge_all - summary.scalar = tf1.summary.scalar - summary.Summary = tf1.summary.Summary - summary.Summary.FromString = tf1.summary.Summary.FromString -tables_initializer = tf1.tables_initializer -test.compute_gradient_error = tf1.test.compute_gradient_error -test.get_temp_dir = tf1.test.get_temp_dir -test.mock = tf1.test.mock -tpu = tf1.tpu -train.AdadeltaOptimizer = tf1.train.AdadeltaOptimizer -train.AdagradOptimizer = tf1.train.AdagradOptimizer -train.AdamOptimizer = tf1.train.AdamOptimizer -train.export_meta_graph = tf1.train.export_meta_graph -train.get_or_create_global_step = tf1.train.get_or_create_global_step -train.get_global_step = tf1.train.get_global_step -train.GradientDescentOptimizer = tf1.train.GradientDescentOptimizer -train.MomentumOptimizer = tf1.train.MomentumOptimizer -train.MonitoredTrainingSession = tf1.train.MonitoredTrainingSession -train.NewCheckpointReader = tf1.train.NewCheckpointReader -train.Optimizer = tf1.train.Optimizer -train.RMSPropOptimizer = tf1.train.RMSPropOptimizer -train.Saver = tf1.train.Saver -train.SaverDef = tf1.train.SaverDef -train.summary_iterator = tf1.train.summary_iterator -trainable_variables = tf1.trainable_variables -Variable = tf1.Variable -variables_initializer = tf1.variables_initializer -VariableScope = tf1.VariableScope -variance_scaling_initializer = tf1.variance_scaling_initializer -variable_scope = tf1.variable_scope -where = tf1.where -while_loop = tf1.while_loop -wrap_function = tf1.wrap_function - -# Explicit 1.x symbol import. -data.make_initializable_iterator = dataset_ops.make_initializable_iterator -data.make_one_shot_iterator = dataset_ops.make_one_shot_iterator -# For `nn.embedding_lookup`, v2 doesn't have the arg 'partition_strategy' in -# the API, and uses 'partition_strategy="div"' by default; -# while v1 uses 'partition_strategy="mod"' by default. Keep this for now. -nn.embedding_lookup = embedding_ops.embedding_lookup -# pylint: enable=undefined-variable diff --git a/lingvo/compat_test.py b/lingvo/compat_test.py deleted file mode 100644 index 09f1150d..00000000 --- a/lingvo/compat_test.py +++ /dev/null @@ -1,53 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for compat.py.""" - -import lingvo.compat as tf -import tensorflow.compat.v1 as tf1 -import tensorflow.compat.v2 as tf2 -from tensorflow.python.framework import function # pylint:disable=g-direct-tensorflow-import - - -class CompatTest(tf.test.TestCase): - - def testSomeTFSymbols(self): - self.assertIsNotNone(tf.logging) - self.assertIsNotNone(tf.flags) - self.assertIs(tf.Defun, function.Defun) - - def testDoesNotModifyTF2(self): - modules_no_overwritten = [ - (tf2.data, tf1.data), - (tf2.graph_util, tf1.graph_util), - (tf2.image, tf1.image), - (tf2.initializers, tf1.initializers), - (tf2.io, tf1.io), - (tf2.losses, tf1.losses), - (tf2.metrics, tf1.metrics), - (tf2.nn, tf1.nn), - (tf2.random, tf1.random), - (tf2.saved_model, tf1.saved_model), - (tf2.strings, tf1.strings), - (tf2.summary, tf1.summary), - (tf2.test, tf1.test), - (tf2.train, tf1.train), - ] - for modules in modules_no_overwritten: - self.assertIsNot(modules[0], modules[1]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/BUILD b/lingvo/core/BUILD deleted file mode 100644 index 425bb52b..00000000 --- a/lingvo/core/BUILD +++ /dev/null @@ -1,2154 +0,0 @@ -load( - "//lingvo:lingvo.bzl", - "lingvo_cuda_py_test", - "lingvo_proto_cc", - "lingvo_proto_py", - "lingvo_py_binary", -) - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "attention", - srcs = ["attention.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":layers", - ":py_utils", - ":quant_utils", - ":symbolic", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -lingvo_cuda_py_test( - name = "attention_test", - srcs = ["attention_test.py"], - python_version = "PY3", - shard_count = 5, - deps = [ - ":attention", - ":py_utils", - ":quant_utils", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "base_decoder", - srcs = ["base_decoder.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":beam_search_helper", - ":py_utils", - ":target_sequence_sampler", - "//lingvo:compat", - ], -) - -py_library( - name = "batch_major_attention", - srcs = ["batch_major_attention.py"], - srcs_version = "PY3", - deps = [ - ":attention_util", - ":base_layer", - ":builder", - ":conv_layers_builder", - ":gpipe", - ":hyperparams", - ":layers", - ":layers_with_attention", - ":moe_layers", - ":py_utils", - ":symbolic", - ":tshape", - "//lingvo:compat", - ], -) - -py_library( - name = "batch_utils", - srcs = ["batch_utils.py"], - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":py_utils", - ], -) - -py_test( - name = "batch_utils_test", - srcs = ["batch_utils_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":batch_utils", - ":cluster_factory", - # Implicit absl.testing.flagsaver dependency. - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - ], -) - -py_library( - name = "beam_search_helper", - srcs = [ - "beam_search_helper.py", - ], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - "//lingvo:compat", - "//lingvo/core/ops", - ], -) - -py_test( - name = "beam_search_helper_test", - srcs = ["beam_search_helper_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":beam_search_helper", - ":py_utils", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "bfloat16_variables", - srcs = ["bfloat16_variables.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - ], -) - -py_test( - name = "bfloat16_variables_test", - srcs = ["bfloat16_variables_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":bfloat16_variables", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "constants", - srcs = ["constants.py"], - srcs_version = "PY3", -) - -py_library( - name = "conformer_layer", - srcs = ["conformer_layer.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:batch_major_attention", - "//lingvo/core:bn_layers", - "//lingvo/core:conv_layers_with_time_padding", - "//lingvo/core:layers", - "//lingvo/core:layers_with_attention", - ], -) - -py_test( - name = "conformer_layer_test", - srcs = ["conformer_layer_test.py"], - python_version = "PY3", - shard_count = 2, - srcs_version = "PY3", - deps = [ - ":conformer_layer", - # Implicit absl.testing.flagsaver dependency. - # Implicit absl.testing.parameterized dependency. - "//lingvo/core:bn_layers", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "target_sequence_sampler", - srcs = [ - "target_sequence_sampler.py", - ], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - ":recurrent", - "//lingvo:compat", - ], -) - -py_test( - name = "target_sequence_sampler_test", - srcs = ["target_sequence_sampler_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":py_utils", - ":target_sequence_sampler", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "base_input_generator", - srcs = ["base_input_generator.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":batch_utils", - ":datasource", - ":hyperparams", - ":input_generator_helper", - ":inspect_utils", - ":py_utils", - ":tokenizers", - "//lingvo:compat", - "//lingvo/core/ops", - ], -) - -py_test( - name = "base_input_generator_test", - srcs = ["base_input_generator_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_input_generator", - ":cluster_factory", - ":hyperparams", - ":py_utils", - ":test_utils", - # Implicit absl.testing.flagsaver dependency. - "//lingvo:compat", - # Implicit mock dependency. - # Implicit numpy dependency. - ], -) - -py_test( - name = "batch_major_attention_test", - size = "large", - srcs = ["batch_major_attention_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":attention_util", - ":batch_major_attention", - ":py_utils", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "base_layer", - srcs = ["base_layer.py"], - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":hyperparams", - ":py_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "bn_layers", - srcs = ["bn_layers.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - ":summary_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "base_layer_test", - size = "small", - srcs = ["base_layer_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_layer", - ":hyperparams", - ":py_utils", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "reversible_layers", - srcs = ["reversible_layers.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "reversible_layers_test", - srcs = ["reversible_layers_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":layers", - ":py_utils", - ":reversible_layers", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "decoder_lib", - srcs = ["decoder_lib.py"], - srcs_version = "PY3", - deps = [ - ], -) - -py_library( - name = "base_model", - srcs = ["base_model.py"], - srcs_version = "PY3", - deps = [ - ":base_input_generator", - ":base_layer", - ":build_data", - ":cluster_factory", - ":early_stop", - ":hyperparams", - ":learner", - ":optimizer", - ":py_utils", - ":schedule", - ":summary_utils", - ":task_scheduler", - "//lingvo/core:decoder_lib", - # Implicit model_pruning.pruning dependency. - "//lingvo:compat", - ], -) - -py_test( - name = "base_model_test", - srcs = ["base_model_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_decoder", - ":base_input_generator", - ":base_layer", - ":base_model", - ":base_model_params", - ":distillation_task", - ":hyperparams", - ":layers", - ":learner", - ":py_utils", - ":task_scheduler", - ":test_utils", - # Implicit absl.testing.flagsaver dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "base_model_params", - srcs = ["base_model_params.py"], - srcs_version = "PY3", - deps = [ - ":base_input_generator", - ":base_model", - ":hyperparams", - "//lingvo:datasets_lib", - ], -) - -py_test( - name = "base_model_params_test", - srcs = ["base_model_params_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_model_params", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "build_data", - srcs = ["build_data.py"], - srcs_version = "PY3", - deps = [ - ":hyperparams", - # Implicit build_data dependency. - ], -) - -py_library( - name = "builder", - srcs = ["builder.py"], - srcs_version = "PY3", - deps = [ - ":builder_layers", - ":hyperparams", - ":layers", - ":py_utils", - ":tshape", - "//lingvo:compat", - ], -) - -py_library( - name = "builder_layers", - srcs = ["builder_layers.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":computation_cost", - ":py_utils", - ":recurrent", - ":summary_utils", - ":symbolic", - ":tshape", - "//lingvo:compat", - ], -) - -py_test( - name = "builder_layers_test", - srcs = ["builder_layers_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":builder_layers", - ":layers", - ":py_utils", - ":test_utils", - ":tshape", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_test( - name = "builder_test", - srcs = ["builder_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":builder", - ":cluster_factory", - ":layers", - ":py_utils", - ":test_utils", - ":tshape", - "//lingvo:compat", - # Implicit numpy dependency. - # Implicit sympy dependency. - ], -) - -py_library( - name = "cluster", - srcs = ["cluster.py"], - srcs_version = "PY3", - deps = [ - ":hyperparams", - ":py_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "cluster_factory", - srcs = ["cluster_factory.py"], - srcs_version = "PY3", - deps = [ - "//lingvo/core:cluster", - ], -) - -py_test( - name = "cluster_test", - size = "small", - srcs = ["cluster_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "datasource", - srcs = ["datasource.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "datasource_test", - size = "small", - srcs = ["datasource_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":datasource", - ":py_utils", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "matrix_functions", - srcs = ["matrix_functions.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core/ops", - # Implicit numpy dependency. - ], -) - -py_test( - name = "matrix_functions_test", - size = "small", - srcs = ["matrix_functions_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":distributed_shampoo", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "distributed_shampoo", - srcs = ["distributed_shampoo.py"], - srcs_version = "PY3", - deps = [ - ":matrix_functions", - "//lingvo:compat", - "//lingvo/core/ops", - # Implicit numpy dependency. - ], -) - -py_test( - name = "distributed_shampoo_test", - size = "small", - srcs = ["distributed_shampoo_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":distributed_shampoo", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "early_stop", - srcs = ["early_stop.py"], - srcs_version = "PY3", - deps = [ - ":hyperparams", - "//lingvo:compat", - "//lingvo/core/ops", - ], -) - -py_test( - name = "early_stop_test", - size = "small", - srcs = ["early_stop_test.py"], - data = [ - "//lingvo/core/ops/testdata:best_step_testdata", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":early_stop", - ":hyperparams", - ":test_helper", - ":test_utils", - "//lingvo:compat", - ], -) - -lingvo_proto_cc( - name = "inference_graph_proto", - src = "inference_graph.proto", -) - -lingvo_proto_py( - name = "inference_graph_py_pb2", - src = "inference_graph.proto", - deps = [":inference_graph_proto"], -) - -py_library( - name = "hyperparams", - srcs = ["hyperparams.py"], - srcs_version = "PY3", - deps = [ - ":hyperparams_py_pb2", - ":symbolic", - # Implicit python proto dependency. - # Implicit dataclasses dependency. - "//lingvo:compat", - ], -) - -lingvo_proto_cc( - name = "hyperparams_proto", - src = "hyperparams.proto", -) - -lingvo_proto_py( - name = "hyperparams_py_pb2", - src = "hyperparams.proto", - deps = [":hyperparams_proto"], -) - -py_test( - name = "hyperparams_test", - srcs = ["hyperparams_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":hyperparams", - ":hyperparams_py_pb2", - ":symbolic", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "inference_graph_exporter", - srcs = ["inference_graph_exporter.py"], - srcs_version = "PY3", - deps = [ - ":base_model", - ":bfloat16_variables", - ":inference_graph_py_pb2", - ":py_utils", - # Implicit python proto dependency. - "//lingvo:compat", - # Implicit six dependency. - ], -) - -py_test( - name = "inference_graph_exporter_test", - srcs = ["inference_graph_exporter_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_input_generator", - ":base_model", - ":base_model_params", - ":inference_graph_exporter", - ":inference_graph_py_pb2", - ":predictor_lib", - ":py_utils", - ":test_utils", - "//lingvo:compat", - "//lingvo:model_registry", - ], -) - -py_library( - name = "input_generator_helper", - srcs = ["input_generator_helper.py"], - srcs_version = "PY3", - deps = [ - ":py_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "input_generator_helper_test", - srcs = ["input_generator_helper_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":input_generator_helper", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "inspect_utils", - srcs = ["inspect_utils.py"], - srcs_version = "PY3", -) - -py_test( - name = "inspect_utils_test", - srcs = ["inspect_utils_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":hyperparams", - ":inspect_utils", - ":test_utils", - ], -) - -py_library( - name = "layers", - srcs = ["layers.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":bn_layers", - ":builder_layers", - ":computation_cost", - ":constants", - ":conv_layers_with_time_padding", - ":pruning_utils", - ":py_utils", - ":quant_utils", - ":recurrent", - ":schedule", - ":summary_utils", - ":symbolic", - ":tshape", - "//lingvo:compat", - # Implicit numpy dependency. - # Implicit sympy dependency. - ], -) - -py_library( - name = "learner", - srcs = ["learner.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":gradient_combiner", - ":optimizer", - ":py_utils", - ":schedule", - ":summary_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "learner_test", - srcs = ["learner_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_layer", - ":learner", - ":optimizer", - ":py_utils", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "conv_layers_with_time_padding", - srcs = ["conv_layers_with_time_padding.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":bn_layers", - ":py_utils", - ":symbolic", - ":tshape", - "//lingvo:compat", - ], -) - -py_library( - name = "conv_layers_builder", - srcs = ["conv_layers_builder.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":builder", - ":builder_layers", - ":conv_layers_with_time_padding", - ":py_utils", - # Implicit absl.flags dependency. - "//lingvo:compat", - ], -) - -lingvo_cuda_py_test( - name = "layers_test", - srcs = ["layers_test.py"], - python_version = "PY3", - shard_count = 20, - deps = [":layers_test_lib"], -) - -lingvo_cuda_py_test( - name = "conv_layers_builder_test", - srcs = ["conv_layers_builder_test.py"], - python_version = "PY3", - deps = [ - ":bn_layers", - ":conv_layers_builder", - ":conv_layers_with_time_padding", - ":layers", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -lingvo_cuda_py_test( - name = "conv_layers_with_time_padding_test", - srcs = ["conv_layers_with_time_padding_test.py"], - python_version = "PY3", - deps = [":layers_test_lib"], -) - -py_library( - name = "layers_test_lib", - testonly = 1, - srcs = ["layers_test.py"], - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":gpipe", - ":layers", - ":py_utils", - ":quant_utils", - ":symbolic", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "saver", - srcs = ["saver.py"], - srcs_version = "PY3", - deps = [ - ":py_utils", - # Implicit python proto dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "schedule", - srcs = ["schedule.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":early_stop", - ":py_utils", - "//lingvo:compat", - "//lingvo/core/ops", - ], -) - -py_test( - name = "saver_test", - srcs = ["saver_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":py_utils", - ":saver", - ":test_utils", - "//lingvo:compat", - "//lingvo/tasks/image/params:mnist", - # Implicit numpy dependency. - ], -) - -py_test( - name = "schedule_test", - size = "small", - timeout = "moderate", - srcs = ["schedule_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":early_stop", - ":schedule", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "metrics", - srcs = ["metrics.py"], - srcs_version = "PY3", - deps = [ - ":hyperparams", - ":plot", - ":py_utils", - ":scorers", - "//lingvo:compat", - # Implicit numpy dependency. - # Implicit sklearn dependency. # buildcleaner: keep - ], -) - -py_library( - name = "ml_perf_bleu_metric", - srcs = ["ml_perf_bleu_metric.py"], - srcs_version = "PY3", - deps = [ - ":metrics", - "//lingvo:compat", - # Implicit numpy dependency. - # Implicit six dependency. - ], -) - -py_test( - name = "ml_perf_bleu_metric_test", - srcs = ["ml_perf_bleu_metric_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ml_perf_bleu_metric", - ":test_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "metrics_test", - srcs = ["metrics_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":metrics", - ":py_utils", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "multitask_model", - srcs = ["multitask_model.py"], - srcs_version = "PY3", - deps = [ - ":base_model", - ":py_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "multitask_model_test", - srcs = ["multitask_model_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_input_generator", - ":base_layer", - ":base_model", - ":base_model_params", - ":hyperparams", - ":multitask_model", - ":py_utils", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "adagraft", - srcs = [ - "adagraft.py", - ], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core/ops", - # Implicit numpy dependency. - ], -) - -py_test( - name = "adagraft_test", - size = "small", - srcs = ["adagraft_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":adagraft", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "optimizer", - srcs = ["optimizer.py"], - srcs_version = "PY3", - deps = [ - ":adagraft", - ":base_layer", - ":distributed_shampoo", - ":py_utils", - ":summary_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "optimizer_test", - size = "small", - srcs = ["optimizer_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":layers", - ":optimizer", - ":py_utils", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "plot", - srcs = ["plot.py"], - srcs_version = "PY3", - deps = [ - ":py_utils", - # Implicit Tkinter dependency. # buildcleaner: keep - "//lingvo:compat", - # Implicit matplotlib dependency. - # Implicit numpy dependency. - # Implicit six dependency. - ], -) - -py_test( - name = "plot_test", - srcs = ["plot_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":plot", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "predictor_lib", - srcs = ["predictor.py"], - srcs_version = "PY3", - deps = [ - ":inference_graph_py_pb2", - ":py_utils", - # Implicit python proto dependency. - # Implicit IPython dependency. - "//lingvo:compat", - "//lingvo:model_imports_no_params", - ], -) - -lingvo_py_binary( - name = "predictor", - srcs = ["predictor.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":inference_graph_py_pb2", - ":py_utils", - # Implicit python proto dependency. - "//lingvo:compat", - "//lingvo:model_imports_no_params", - ], -) - -py_test( - name = "predictor_test", - size = "medium", - srcs = ["predictor_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_input_generator", - ":base_model", - ":inference_graph_exporter", - ":inference_graph_py_pb2", - ":predictor_lib", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "predictor_runner_base", - srcs = ["predictor_runner_base.py"], - srcs_version = "PY3", - deps = [ - ":predictor_lib", - ":py_utils", - # Implicit absl.flags dependency. - # Implicit concurrent.futures dependency. - "//lingvo:compat", - # Implicit six dependency. - ], -) - -py_library( - name = "pruning_utils", - srcs = ["pruning_utils.py"], - srcs_version = "PY3", - deps = [ - # Implicit model_pruning.pruning dependency. - "//lingvo:compat", - ], -) - -py_library( - name = "py_utils", - srcs = ["py_utils.py"], - srcs_version = "PY3", - deps = [ - ":hyperparams", - ":retry", - ":symbolic", - ":tshape", - "//lingvo:compat", - "//lingvo/core/ops", - # Implicit numpy dependency. - # Implicit six dependency. - # Implicit tensorflow py proto dependency. - ], -) - -py_test( - name = "py_utils_test", - size = "medium", - srcs = ["py_utils_test.py"], - data = [ - "//lingvo/core/testdata:lenet_test_model", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_layer", - ":builder_layers", - ":cluster_factory", - ":hyperparams", - ":py_utils", - ":recurrent", - ":symbolic", - ":test_helper", - ":test_utils", - # Implicit absl.testing.flagsaver dependency. - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - "//lingvo:model_registry", - "//lingvo/tasks/image/params:mnist", - # Implicit mock dependency. - # Implicit numpy dependency. - ], -) - -py_library( - name = "quant_utils", - srcs = ["quant_utils.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":hyperparams", - ":py_utils", - ":summary_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_test( - name = "quant_utils_test", - size = "small", - srcs = ["quant_utils_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":py_utils", - ":quant_utils", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "layers_with_gpipe", - srcs = ["layers_with_gpipe.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":batch_major_attention", - ":gpipe", - ":layers", - ":layers_with_attention", - ":py_utils", - ":tshape", - "//lingvo:compat", - ], -) - -py_test( - name = "layers_with_gpipe_test", - size = "large", - srcs = ["layers_with_gpipe_test.py"], - python_version = "PY3", - shard_count = 15, - srcs_version = "PY3", - deps = [ - ":layers_with_gpipe", - ":py_utils", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "spectrum_augmenter", - srcs = ["spectrum_augmenter.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "spectrum_augmenter_test", - srcs = ["spectrum_augmenter_test.py"], - python_version = "PY3", - shard_count = 1, - srcs_version = "PY3", - deps = [ - ":spectrum_augmenter", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "spectrum_augmenter_on_device", - srcs = ["spectrum_augmenter_on_device.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - ":spectrum_augmenter", - "//lingvo:compat", - ], -) - -py_test( - name = "spectrum_augmenter_on_device_test", - srcs = ["spectrum_augmenter_on_device_test.py"], - python_version = "PY3", - shard_count = 1, - srcs_version = "PY3", - deps = [ - ":spectrum_augmenter", - ":spectrum_augmenter_on_device", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "generic_input", - srcs = ["generic_input.py"], - srcs_version = "PY3", - deps = [ - ":py_utils", - "//lingvo:compat", - "//lingvo/core/ops", - # Implicit tensorflow python framework_for_generated_wrappers dependency. - ], -) - -py_test( - name = "generic_input_test", - srcs = ["generic_input_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":generic_input", - ":py_utils", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "gpipe", - srcs = ["gpipe.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":builder_layers", - ":py_utils", - ":recurrent", - ":tshape", - "//lingvo:compat", - ], -) - -py_test( - name = "gpipe_test", - srcs = ["gpipe_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_layer", - ":gpipe", - ":layers", - ":py_utils", - ":test_utils", - ":tshape", - "//lingvo:compat", - ], -) - -py_library( - name = "recurrent", - srcs = ["recurrent.py"], - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":constants", - ":py_utils", - ":sendrecv", - ":symbolic", - "//lingvo:compat", - ], -) - -py_test( - name = "recurrent_test", - srcs = ["recurrent_test.py"], - python_version = "PY3", - shard_count = 6, - srcs_version = "PY3", - deps = [":recurrent_test_lib"], -) - -py_library( - name = "recurrent_test_lib", - testonly = 1, - srcs = ["recurrent_test.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - ":recurrent", - ":symbolic", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -lingvo_cuda_py_test( - name = "recurrent_gpu_test", - srcs = ["recurrent_gpu_test.py"], - python_version = "PY3", - shard_count = 6, - deps = [ - ":py_utils", - ":recurrent", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "attention_util", - srcs = ["attention_util.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":layers", - ":py_utils", - ":summary_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "retry", - srcs = ["retry.py"], - srcs_version = "PY3", - deps = [ - # Implicit retry dependency. - ], -) - -py_test( - name = "attention_util_test", - srcs = ["attention_util_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":attention_util", - ":py_utils", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_test( - name = "retry_test", - srcs = ["retry_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":retry", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "self_attention_layer", - srcs = ["self_attention_layer.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":batch_major_attention", - ":py_utils", - ], -) - -py_library( - name = "sendrecv", - srcs = ["sendrecv.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - ], -) - -py_test( - name = "self_attention_layer_test", - timeout = "long", - srcs = ["self_attention_layer_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":batch_major_attention", - ":py_utils", - ":self_attention_layer", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_test( - name = "sendrecv_test", - srcs = ["sendrecv_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":sendrecv", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "summary_utils", - srcs = ["summary_utils.py"], - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":plot", - ":py_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "task_scheduler", - srcs = ["task_scheduler.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":early_stop", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_test( - name = "task_scheduler_test", - srcs = ["task_scheduler_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":early_stop", - ":task_scheduler", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "test_helper", - srcs = ["test_helper.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - ], -) - -py_library( - name = "test_utils", - srcs = ["test_utils.py"], - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":py_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_test( - name = "test_utils_test", - srcs = ["test_utils_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "trainer_test_utils", - srcs = ["trainer_test_utils.py"], - srcs_version = "PY3", - deps = [ - ":base_input_generator", - ":base_model", - ":py_utils", - ":summary_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "test_trainer_utils", - srcs = ["test_trainer_utils.py"], - srcs_version = "PY3", - deps = [ - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - "//lingvo:trainer_lib", - ], -) - -py_library( - name = "wpm_encoder", - srcs = ["wpm_encoder.py"], - srcs_version = "PY3", - deps = [ - ":py_utils", - "//lingvo:compat", - "//lingvo/core/ops", - # Implicit six dependency. - ], -) - -py_test( - name = "wpm_encoder_test", - srcs = ["wpm_encoder_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":test_utils", - ":wpm_encoder", - "//lingvo:compat", - ], -) - -py_library( - name = "tokenizers", - srcs = ["tokenizers.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - ":wpm_encoder", - "//lingvo:compat", - "//lingvo/core/ops", - # Implicit numpy dependency. - ], -) - -py_library( - name = "tpu_summary", - srcs = ["tpu_summary.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - ], -) - -py_test( - name = "wpm_tokenizer_test", - srcs = ["wpm_tokenizer_test.py"], - data = [ - "//lingvo/tasks/mt:wpm_ende", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":test_helper", - ":test_utils", - ":tokenizers", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "rnn_cell", - srcs = ["rnn_cell.py"], - srcs_version = "PY3", - deps = [ - ":hyperparams", - ":layers", - ":pruning_utils", - ":py_utils", - ":quant_utils", - ":summary_utils", - "//lingvo:compat", - ], -) - -lingvo_cuda_py_test( - name = "rnn_cell_test", - srcs = ["rnn_cell_test.py"], - python_version = "PY3", - deps = [ - ":py_utils", - ":quant_utils", - ":rnn_cell", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "layers_with_attention", - srcs = ["layers_with_attention.py"], - srcs_version = "PY3", - deps = [ - ":attention", - ":base_layer", - ":layers", - ":py_utils", - ":symbolic", - "//lingvo:compat", - ], -) - -py_test( - name = "layers_with_attention_test", - size = "large", - srcs = ["layers_with_attention_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":attention", - ":layers", - ":layers_with_attention", - ":py_utils", - ":test_utils", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "rnn_layers", - srcs = ["rnn_layers.py"], - srcs_version = "PY3", - deps = [ - ":attention", - ":base_layer", - ":layers", - ":py_utils", - ":quant_utils", - ":recurrent", - ":rnn_cell", - "//lingvo:compat", - ], -) - -lingvo_cuda_py_test( - name = "rnn_layers_test", - size = "large", - srcs = ["rnn_layers_test.py"], - python_version = "PY3", - shard_count = 15, - deps = [":rnn_layers_test_lib"], -) - -py_library( - name = "rnn_layers_test_lib", - testonly = 1, - srcs = ["rnn_layers_test.py"], - srcs_version = "PY3", - deps = [ - ":attention", - ":base_layer", - ":cluster_factory", - ":layers_with_attention", - ":py_utils", - ":rnn_cell", - ":rnn_layers", - ":test_utils", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "lstm_frnn_layer", - srcs = ["lstm_frnn_layer.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":recurrent", - ":rnn_cell", - ":rnn_layers", - # Implicit absl.flags dependency. - "//lingvo:compat", - ], -) - -py_test( - name = "lstm_frnn_layer_test", - srcs = ["lstm_frnn_layer_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":lstm_frnn_layer", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "scorers", - srcs = ["scorers.py"], - srcs_version = "PY3", - deps = [ - # Implicit six dependency. - ], -) - -py_test( - name = "scorers_test", - srcs = ["scorers_test.py"], - data = ["//lingvo/core/ops/testdata:wmt"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":scorers", - ":test_helper", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "model_helper", - srcs = ["model_helper.py"], - srcs_version = "PY3", - deps = [ - ":rnn_layers", - "//lingvo:compat", - ], -) - -py_library( - name = "moe_layers", - srcs = ["moe_layers.py"], - deps = [ - ":base_layer", - ":py_utils", - ":tpu_summary", - "//lingvo:compat", - ], -) - -py_library( - name = "tshape", - srcs = ["tshape.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - # Implicit sympy dependency. - ], -) - -py_test( - name = "tpu_summary_test", - srcs = ["tpu_summary_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":test_utils", - ":tpu_summary", - "//lingvo:compat", - ], -) - -py_test( - name = "tshape_test", - srcs = ["tshape_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":test_utils", - ":tshape", - "//lingvo:compat", - ], -) - -py_library( - name = "symbolic", - srcs = ["symbolic.py"], - srcs_version = "PY3", - deps = [ - # Implicit sympy dependency. - ], -) - -py_test( - name = "symbolic_test", - srcs = ["symbolic_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":symbolic", - ":test_utils", - "//lingvo:compat", - # Implicit sympy dependency. - ], -) - -py_library( - name = "insertion", - srcs = ["insertion.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - "//lingvo:compat", - ], -) - -lingvo_cuda_py_test( - name = "insertion_test", - srcs = ["insertion_test.py"], - python_version = "PY3", - deps = [ - ":insertion", - ":test_utils", - ], -) - -py_library( - name = "computation_cost", - srcs = ["computation_cost.py"], - srcs_version = "PY3", - deps = [ - ":bn_layers", - "//lingvo:compat", - ], -) - -py_library( - name = "checkpointer_lib", - srcs = ["checkpointer.py"], - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":py_utils", - "//lingvo:compat", - # Implicit six dependency. - ], -) - -py_library( - name = "program_lib", - srcs = ["program.py"], - srcs_version = "PY3", - deps = [ - ":base_model", - ":checkpointer_lib", - ":cluster_factory", - ":hyperparams", - ":metrics", - ":ml_perf_log", - ":py_utils", - ":summary_utils", - "//lingvo:compat", - # Implicit tensorflow grpc dependency. - ], -) - -py_library( - name = "step", - srcs = ["step.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":builder_layers", - ":py_utils", - ":recurrent", - "//lingvo:compat", - ], -) - -py_test( - name = "step_test", - size = "small", - srcs = ["step_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":builder_layers", - ":py_utils", - ":step", - ":test_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "summary_utils_test", - srcs = ["summary_utils_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":cluster_factory", - ":summary_utils", - ":test_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "checkpointer_test", - srcs = ["checkpointer_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_input_generator", - ":base_model", - ":checkpointer_lib", - ":py_utils", - ":test_utils", - "//lingvo:compat", - ], -) - -py_test( - name = "ema_test", - srcs = ["ema_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":base_decoder", - ":base_input_generator", - ":base_model", - ":checkpointer_lib", - ":layers", - ":py_utils", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "ml_perf_tokenizer", - srcs = ["ml_perf_tokenizer.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - ":tokenizers", - "//lingvo:compat", - "//lingvo/core/ops", - # Implicit numpy dependency. - ], -) - -py_library( - name = "ml_perf_log", - srcs = ["ml_perf_log.py"], - srcs_version = "PY3", -) - -py_test( - name = "ml_perf_log_test", - srcs = ["ml_perf_log_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ml_perf_log", - "//lingvo:compat", - ], -) - -py_library( - name = "egdd", - srcs = [ - "egdd.py", - ], - srcs_version = "PY3", - deps = [ - # Implicit numpy dependency. - # Implicit tensorflow dependency. - ], -) - -py_test( - name = "egdd_test", - size = "large", - srcs = ["egdd_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":egdd", - ":test_utils", - "//lingvo:compat", - ], -) - -py_library( - name = "distillation_task", - srcs = ["distillation_task.py"], - srcs_version = "PY3", - deps = [ - ":base_model", - ":py_utils", - ":schedule", - "//lingvo:compat", - ], -) - -py_library( - name = "gradient_combiner", - srcs = ["gradient_combiner.py"], - srcs_version = "PY3", - deps = [":base_layer"], -) - -py_library( - name = "tpu_embedding_layers", - srcs = ["tpu_embedding_layers.py"], - srcs_version = "PY3", - deps = [ - ":base_layer", - ":py_utils", - "//lingvo:compat", - ], -) diff --git a/lingvo/core/__init__.py b/lingvo/core/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/core/adagraft.py b/lingvo/core/adagraft.py deleted file mode 100644 index 0809b194..00000000 --- a/lingvo/core/adagraft.py +++ /dev/null @@ -1,185 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""AdaGraft optimizer https://arxiv.org/abs/2002.11803 .""" - -import lingvo.compat as tf - - -class AdaGraftOptimizer(tf.train.Optimizer): - """Optimizer which combines per-layer direction and magnitude from two optimizers. - - Disentangling Adaptive Gradient Methods from Learning Rates - Naman Agarwal, Rohan Anil, Elad Hazan, Tomer Koren, Cyril Zhang - https://arxiv.org/abs/2002.11803 - """ - - def __init__(self, - learning_rate, - magnitude_optimizer, - direction_optimizer, - diagnostic=False, - use_global_norm=False, - name="AdaGraft"): - """Construct a new AdaGraft optimizer. - - Args: - learning_rate: A `Tensor` or a floating point value. The learning rate. - magnitude_optimizer: Child Optimizer to inherit step sizes. - direction_optimizer: Child Optimizer to inherit step directions. - diagnostic: Whether to record per-tensor step norms. - use_global_norm: Graft global l2 norms rather than per-layer. - name: Optional name prefix for the operations created when applying - gradients. - """ - super().__init__(False, name) - self._learning_rate = learning_rate - self.magnitude_optimizer = magnitude_optimizer - self.direction_optimizer = direction_optimizer - self.diagnostic = diagnostic - self.use_global_norm = use_global_norm - - def _create_slots(self, var_list): - self.magnitude_optimizer._create_slots(var_list) # pylint: disable=protected-access - self.direction_optimizer._create_slots(var_list) # pylint: disable=protected-access - - for v in var_list: - with tf.ops.colocate_with(v): - self._zeros_slot(v, "scratch_copy", self._name) - if self.diagnostic or self.use_global_norm: - self._get_or_make_slot(v, tf.constant(0.0), "m_step_norm", self._name) - self._get_or_make_slot(v, tf.constant(0.0), "d_step_norm", self._name) - - def _prepare(self): - self.magnitude_optimizer._prepare() # pylint: disable=protected-access - self.direction_optimizer._prepare() # pylint: disable=protected-access - - learning_rate = self._call_if_callable(self._learning_rate) - self._learning_rate_tensor = tf.convert_to_tensor( - learning_rate, name="learning_rate") - - if self.use_global_norm: # create list of all vars for global _finish - self._variables = [] - - def _apply_dense(self, grad, var): # pylint: disable=g-doc-args - return self._internal_apply_dense( - grad, - var, - self.magnitude_optimizer._apply_dense, # pylint: disable=protected-access - self.direction_optimizer._apply_dense) # pylint: disable=protected-access - - def _resource_apply_dense(self, grad, var): - return self._internal_apply_dense( - grad, - var, - self.magnitude_optimizer._resource_apply_dense, # pylint: disable=protected-access - self.direction_optimizer._resource_apply_dense) # pylint: disable=protected-access - - def _internal_apply_dense(self, grad, var, magnitude_optimizer_apply_fn, - direction_optimizer_apply_fn): # pylint: disable=g-doc-args - """Main optimization logic of AdaGraft, which calls the child optimizers. - - Args: - grad: Tensor containing gradients. - var: Tensor containing parameter values. - magnitude_optimizer_apply_fn: Apply magnitude optimizer. - direction_optimizer_apply_fn: Apply direction optimizer. - - Returns: - The final update op, which increments var by the grafted step. - - Pseudocode: - - Copy weights into scratch space 'scratch_copy'. - - Run magnitude_optimizer in-place. - - Use scratch copy to figure out how far we moved ('magnitude_step'). - - Copy weights back. - - Run direction_optimizer in-place. - - Move weights along the line segment with scratch_copy. - """ - - if self.use_global_norm: - self._variables.append(var) - - # Slot with current parameter values - scratch_slot = self.get_slot(var, "scratch_copy") - old_var = tf.assign(scratch_slot, var) - - with tf.control_dependencies([old_var]): - m_updated_var = magnitude_optimizer_apply_fn(grad, var) # pylint: disable=protected-access - - # Run magnitude optimizer and compute the norm of the update. - with tf.control_dependencies([m_updated_var]): - m_step = var - old_var - m_step_norm = tf.norm(m_step) - if self.diagnostic or self.use_global_norm: - m_step_norm = tf.assign(self.get_slot(var, "m_step_norm"), m_step_norm) - - # Run direction optimizer and compute its norm, and the direction. - with tf.control_dependencies([m_step_norm]): - flushed_var = tf.assign(var, old_var) - with tf.control_dependencies([flushed_var]): - d_updated_var = direction_optimizer_apply_fn(grad, var) # pylint: disable=protected-access - - # Run an update of the direction optimizer with magnitude optimizer norm. - with tf.control_dependencies([d_updated_var]): - d_step = var - old_var - d_step_norm = tf.norm(d_step) - if self.diagnostic or self.use_global_norm: - d_step_norm = tf.assign(self.get_slot(var, "d_step_norm"), d_step_norm) - if self.use_global_norm: - flushed_var = tf.assign(var, old_var) - with tf.control_dependencies([d_step_norm, flushed_var]): - return tf.assign(scratch_slot, d_step) - step = tf.where( - tf.greater(d_step_norm, 0), - (m_step_norm / tf.maximum(d_step_norm, 1e-30)) * d_step, - tf.zeros_like(d_step)) - return tf.assign(var, old_var + self._learning_rate_tensor * step) - - def _finish(self, update_ops, name_scope): - with tf.control_dependencies(update_ops): - ops1 = self.magnitude_optimizer._finish([], name_scope + "_m") # pylint: disable=protected-access - ops2 = self.direction_optimizer._finish([], name_scope + "_d") # pylint: disable=protected-access - - if self.use_global_norm: # apply global grafting - with tf.control_dependencies([ops1, ops2]): - m_global_norm = tf.Variable(0.) - d_global_norm = tf.Variable(0.) - for var in self._variables: - m_step_norm = self.get_slot(var, "m_step_norm") - d_step_norm = self.get_slot(var, "d_step_norm") - tf.assign_add(m_global_norm, m_step_norm**2) - tf.assign_add(d_global_norm, d_step_norm**2) - - multiplier = tf.sqrt(m_global_norm / tf.maximum(d_global_norm, 1e-30)) - - step_ops = [] - for var in self._variables: - d_step = self.get_slot(var, "scratch_copy") - step = tf.where( - tf.greater(d_step_norm, 0), multiplier * d_step, - tf.zeros_like(d_step)) - step_op = tf.assign_add(var, self._learning_rate_tensor * step) - step_ops.append(step_op) - return tf.group(*step_ops, name=name_scope) - - return tf.group(*([ops1, ops2] + update_ops), name=name_scope) - - # Sparse gradients are not handled currently and is part of future work. - def _resource_apply_sparse(self, grad_values, var, grad_indices): - return tf.no_op() - - def _apply_sparse(self, grad, var): - return tf.no_op() diff --git a/lingvo/core/adagraft_test.py b/lingvo/core/adagraft_test.py deleted file mode 100644 index 5a468a9d..00000000 --- a/lingvo/core/adagraft_test.py +++ /dev/null @@ -1,83 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -# Lint as: python3 -"""Tests for AdaGraft.""" - -import lingvo.compat as tf -from lingvo.core import adagraft -from lingvo.core import test_utils - - -class AdagraftTest(test_utils.TestCase): - - def test_identity(self): - # AdaGraft(1, opt, opt) should do the same thing as opt. - opt1 = tf.train.AdamOptimizer(0.5, beta1=0.5, beta2=0.5) - opt2 = tf.train.AdamOptimizer(0.5, beta1=0.5, beta2=0.5) - opt3 = tf.train.AdamOptimizer(0.5, beta1=0.5, beta2=0.5) - opt = adagraft.AdaGraftOptimizer(1.0, opt1, opt2) - with self.cached_session(): - var0 = tf.Variable(2.0, name="var0") - var1 = tf.Variable(3.0, name="var1") - loss = (var0 - 1) * (var0 - 1) + (var1 - 1) * (var1 - 1) - o = opt.minimize(loss) - oo = opt3.minimize(loss) - self.evaluate(tf.global_variables_initializer()) - self.evaluate(o) - l1 = self.evaluate([loss, var0, var1]) - print(l1) - self.evaluate([tf.assign(var0, 2.0), tf.assign(var1, 3.0)]) - self.evaluate(oo) - l2 = self.evaluate([loss, var0, var1]) - print(l2) - self.assertAllClose(l1, l2) - - def test_step(self): - """Tests grafting of Adam and SGD steps. - - Derivation of one step of Adam and SGD: - Gradient value is [2,4]. - Adam Derivation: - Lr_1 = 0.5(1-0.6)^(0.5)/(1-0.5) = 0.63245553203 - Does not matter - m_1 = 0.5*G = [1,2] - v_1 = 0.4*G^2 = [1.6,6.4] - AdamStep = Lr_1*m_1/(sqrt{v_1}+eps) = [0.5, 0.5] - Normalized AdamStep = [1.0, 1.0] - SGDStep = [0.6, 1.2] Norm = [0.6, 1.2] - TotalStep = 0.9*[0.6, 1.2] - NewVar = [1.46, 1.92] - """ - opt1 = tf.train.GradientDescentOptimizer(0.3) - opt2 = tf.train.AdamOptimizer(0.5, beta1=0.5, beta2=0.6) - opt = adagraft.AdaGraftOptimizer(0.9, opt1, opt2) - with self.cached_session(): - var0 = tf.Variable(2.0, name="var0") - var1 = tf.Variable(3.0, name="var1") - loss = (var0 - 1) * (var0 - 1) + (var1 - 1) * (var1 - 1) - o = opt.minimize(loss) - self.evaluate(tf.global_variables_initializer()) - - correct_values = [[1.058, 1.46, 1.92], [0.22387284, 1.2116001, 1.4232]] - - for i in range(2): - self.evaluate(o) - step_values = self.evaluate([loss, var0, var1]) - print(step_values) - self.assertAllClose(correct_values[i], step_values) - - -if __name__ == "__main__": - tf.test.main() diff --git a/lingvo/core/attention.py b/lingvo/core/attention.py deleted file mode 100644 index a1a87893..00000000 --- a/lingvo/core/attention.py +++ /dev/null @@ -1,3277 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Attention models.""" - -import math -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import symbolic - -import numpy as np - -from tensorflow.python.ops import inplace_ops # pylint:disable=g-direct-tensorflow-import - - -# Currently, quantization statistics cannot be accumulated across arbitrary -# defuns, so we allow them to be disabled. A potentially more robust fix is -# to save and merge the attention state across the defun boundary as is -# done in recurrent.py. -def _ConditionalCallDefun(cond, f, inputs): - if not cond: - return f(inputs) - return py_utils.CallDefun(f, inputs) - - -def _ApplyAttentionDropout(params, x, global_step): - """Apply attention dropout according to the given parameters. - - If `params.atten_dropout_deterministic` is set to True, the dropout will be - fully deterministic. - - Args: - params: The parameters of attention layer. - x: A float Tensor on which to apply dropout. - global_step: Required for deterministic dropout. - - Returns: - A Tensor with the same shape as `x`. - """ - if params.atten_dropout_prob == 0: - return x - - if params.atten_dropout_deterministic: - seeds = py_utils.GenerateStepSeedPair(params, global_step) - return py_utils.DeterministicDropout(x, 1.0 - params.atten_dropout_prob, - seeds) - else: - return tf.nn.dropout( - x, rate=params.atten_dropout_prob, seed=params.random_seed) - - -def SafeCumprod(x, *args, **kwargs): - """Computes cumprod of x in logspace using cumsum to avoid underflow. - - The cumprod function and its gradient can result in numerical instabilities - when its argument has very small and/or zero values. As long as the argument - is all positive, we can instead compute the cumulative product as - exp(cumsum(log(x))). This function can be called identically to - tf.math.cumprod. - - Args: - x: Tensor to take the cumulative product of. - *args: Passed on to cumsum; these are identical to those in cumprod. - **kwargs: Passed on to cumsum; these are identical to those in cumprod. - - Returns: - Cumulative product of x. - """ - with tf.name_scope(None, 'SafeCumprod', [x]): - x = tf.convert_to_tensor(x, name='x') - tiny = np.finfo(x.dtype.as_numpy_dtype).tiny - return tf.exp( - py_utils.CumSum( - tf.math.log(tf.clip_by_value(x, tiny, 1)), *args, **kwargs)) - - -# pyformat: disable -def MonotonicAttentionProb(p_choose_i, previous_attention, mode): - """Compute monotonic attention distribution from choosing probabilities. - - Monotonic attention implies that the input sequence is processed in an - explicitly left-to-right manner when generating the output sequence. In - addition, once an input sequence element is attended to at a given output - timestep, elements occurring before it cannot be attended to at subsequent - output timesteps. This function generates attention distributions according - to these assumptions. For more information, see `Online and Linear-Time - Attention by Enforcing Monotonic Alignments`. - - Args: - p_choose_i: Probability of choosing input sequence/memory element i. Should - be of shape (batch_size, input_sequence_length), and should all be in the - range [0, 1]. - previous_attention: The attention distribution from the previous output - timestep. Should be of shape (batch_size, input_sequence_length). For - the first output timestep, preevious_attention[n] should be [1, 0, 0, ..., - 0] for all n in [0, ... batch_size - 1]. - mode: How to compute the attention distribution. Must be one of `recursive`, - `parallel`, or `hard`. - - * recursive: uses tf.scan to recursively compute the distribution. This is - slowest but is exact, general, and does not suffer from numerical - instabilities. - * parallel: uses parallelized cumulative-sum and cumulative-product - operations to compute a closed-form solution to the recurrence relation - defining the attention distribution. This makes it more efficient than - 'recursive', but it requires numerical checks which make the - distribution non-exact. This can be a problem in particular when - input_sequence_length is long and/or p_choose_i has entries very close - to 0 or 1. - * hard: requires that the probabilities in p_choose_i are all either 0 or - 1, and subsequently uses a more efficient and exact solution. - - Returns: - A tensor of shape (batch_size, input_sequence_length) representing the - attention distributions for each sequence in the batch. - - Raises: - ValueError: mode is not one of 'recursive', 'parallel', 'hard'. - """ - # pyformat: enable - # Force things to be tensors - p_choose_i = tf.convert_to_tensor(p_choose_i, name='p_choose_i') - previous_attention = tf.convert_to_tensor( - previous_attention, name='previous_attention') - if mode == 'recursive': - batch_size = py_utils.GetShape(p_choose_i)[0] - tf.logging.info(batch_size) - # Compute [1, 1 - p_choose_i[0], 1 - p_choose_i[1], ..., 1 - p_choose_i[-2]] - shifted_1mp_choose_i = tf.concat( - [tf.ones((batch_size, 1)), 1 - p_choose_i[:, :-1]], 1) - # Compute attention distribution recursively as - # q[i] = (1 - p_choose_i[i - 1])*q[i - 1] + previous_attention[i] - # attention[i] = p_choose_i[i]*q[i] - attention = p_choose_i * tf.transpose( - tf.scan( - # Need to use reshape to remind TF of the shape between loop - # iterations. - lambda x, yz: tf.reshape(yz[0] * x + yz[1], (batch_size,)), - # Loop variables yz[0] and yz[1] - [ - tf.transpose(shifted_1mp_choose_i), - tf.transpose(previous_attention) - ], - # Initial value of x is just zeros - tf.zeros((batch_size,)))) - elif mode == 'parallel': - # SafeCumprod computes cumprod in logspace with numeric checks - cumprod_1mp_choose_i = SafeCumprod(1 - p_choose_i, axis=1, exclusive=True) - # Compute recurrence relation solution - attention = p_choose_i * cumprod_1mp_choose_i * py_utils.CumSum( - previous_attention / - # Clip cumprod_1mp to avoid divide-by-zero - tf.clip_by_value(cumprod_1mp_choose_i, 1e-10, 1.), - axis=1) - elif mode == 'hard': - # Remove any probabilities before the index chosen last time step - p_choose_i *= tf.cumsum(previous_attention, axis=1) - # Now, use exclusive cumprod to remove probabilities after the first - # chosen index, like so: - # p_choose_i = [0, 0, 0, 1, 1, 0, 1, 1] - # cumprod(1 - p_choose_i, exclusive=True) = [1, 1, 1, 1, 0, 0, 0, 0] - # Product of above: [0, 0, 0, 1, 0, 0, 0, 0] - attention = p_choose_i * tf.math.cumprod( - 1 - p_choose_i, axis=1, exclusive=True) - else: - raise ValueError("mode must be 'recursive', 'parallel', or 'hard'.") - return attention - - -class BaseAttentionLayer(quant_utils.QuantizableLayer): - """A base class for all attention layers.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('atten_dropout_prob', 0.0, - 'Probability at which we apply dropout to the attention weights.') - p.Define( - 'atten_dropout_deterministic', False, - 'Whether to dropout in a fully deterministic way, which is more ' - 'suitable for TPU.') - p.Define('packed_input', False, - 'If True, each training example may pack multiple sequences.') - - p.qdomain.Define('softmax', None, 'QDomain for the internal softmax.') - p.qdomain.Define( - 'fullyconnected', None, 'Fully connected layers are fed ' - 'into activation functions which have known input ranges') - - return p - - def __init__(self, params): - """Constructs a BaseAttentionLayer object.""" - if not params.name: - raise ValueError('params.name is not set.') - super().__init__(params) - - self._source_init_done = False - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.TrackQTensor('logits', domain='fullyconnected') - - def InitForSourcePacked(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - """Initialize attention for the given source vectors. - - Must set `_source_init_done` to True in the function. - - Note: `source_segment_id`, if present, should always have the same shape as - `source_padding`. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: A single tensor of shape [time, batch_size, source_dim]. - source_contexts: A single tensor of shape [time, batch_size, some_dim]. - source_padding: A tensor of shape [time, batch_size]. - source_segment_id: A tensor of shape [time, batch_size]. source_segment_id - is not None for packed inputs where one training example may pack - multiple sequences. - - Returns: - A `.NestedMap` object to be passed to ComputeContextVectorWithSource. - The internal structure of the return value should be considered an - implementation detail of the attention mechanism and should not be - inspected or modified by its callers. - """ - self._source_init_done = True - self._packed_src = self.PackSource(theta, source_vecs, source_contexts, - source_padding, source_segment_id) - return self._packed_src - - def PackSource(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - """Packs source vectors. - - Does not change attention state. - - Note: `source_segment_id`, if present, should always have the same shape as - `source_padding`. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: A single tensor of shape [time, batch_size, source_dim]. - source_contexts: A single tensor of shape [time, batch_size, some_dim]. - source_padding: A tensor of shape [time, batch_size]. - source_segment_id: A tensor of shape [time, batch_size]. source_segment_id - is not None for packed inputs where one training example may pack - multiple sequences. - - Returns: - A `.NestedMap` object to be passed to ComputeContextVectorWithSource. - The internal structure of the return value should be considered an - implementation detail of the attention mechanism and should not be - inspected or modified by its callers. - """ - raise NotImplementedError('Abstract method.') - - def ComputeContextVectorWithSource(self, - theta, - packed_src, - query_vec, - attention_state=None, - per_step_source_padding=None, - query_segment_id=None): - """Computes the context vector given the current query output. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - packed_src: A `.NestedMap` object returned by PackSource or - InitForSourcePacked. - query_vec: a tensor of shape [batch_size, query_dim]. - attention_state: previous attention state. - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should have shape [target_batch_size, source_length]. - query_segment_id: a tensor of shape [batch_size]. - - Returns: - A tuple of 3 elements. - - - The attention context vector: [batch_size, context_dim] - - The attention probability vector: [batch_size, time] - - The new attention mechanism state: possibly nested tuple of tensors - with dimensions [target_batch, ...] - """ - raise NotImplementedError('Abstract method.') - - def ComputeContextVector(self, - theta, - query_vec, - attention_state=None, - per_step_source_padding=None, - query_segment_id=None): - """Computes the context vector given the current query output. - - Unlike `ComputeContextVectorWithSource` which explicitly asks for the packed - source tensors, `ComputeContextVector` uses the class' internal variables. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: a tensor of shape [batch_size, query_dim]. - attention_state: previous attention state. - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should be of shape [target_batch_size, source_length]. - query_segment_id: a tensor of shape [batch_size]. - - Returns: - A tuple of 3 elements. - - - The attention context vector. - - The attention probability vector. - - The new attention mechanism state: possibly nested tuple of tensors with - dimensions [target_batch, ...] - """ - assert self._source_init_done - return self.ComputeContextVectorWithSource(theta, self._packed_src, - query_vec, attention_state, - per_step_source_padding, - query_segment_id) - - def GetInitializationSourceState(self): - """Gets the attention initialization state. - - The base class only preserves the `concated_source_vecs`, - `concated_source_contexts` and `source_padding`. If subclasses use more - state than this and need to interact with inference code that must - fetch and reload state, this and `SetInitializationSourceState` must - be overridden. - - Returns: - A `.NestedMap` of Tensors that can be preserved and reset via - `SetInitializationSourceState()` at a later point. This allows, for - example, for attention computations to span session runs. - """ - assert self._source_init_done - return self._packed_src - - def SetInitializationSourceState(self, new_init_state): - """Sets the attention initialization state. - - Args: - new_init_state: A `.NestedMap` matching what was returned from - `GetInitializationSourceState`, which will return this layer to that - initialization state. - """ - self._source_init_done = True - self._packed_src = new_init_state.DeepCopy() - - def _PaddedSoftmax(self, logits, padding, narrow_to_asym_bit_depth=False): - """Performs a softmax as if padding were applied after exponentiation. - - The default implementation uses numerical techniques to approximate this - with a standard `tf.nn.softmax` (using large negative logits for padded - values). It defers to a `Defun` that may be replaced on low-range - implementations with a version that is numerically correct. - - Args: - logits: Logits. - padding: Padding (must be the same shape as logits). - narrow_to_asym_bit_depth: Narrows the bit depth, removing the upper limit - value. This is to accommodate certain interpreters that would cover a 0 - .... 2**bits - 1 range for quantization. - - Returns: - Result of the softmax. - """ - fns = self.fns - - if logits.dtype.is_complex: - logits = tf.abs(logits) - assert logits.dtype.is_floating - assert hasattr(logits.dtype, 'max') - very_negative_logits = ( - tf.ones_like(logits) * logits.dtype.max * - tf.constant(-0.7, dtype=logits.dtype)) - if self.do_eval: - very_negative_logits = self.QTensor('logits', very_negative_logits) - padded_logits = tf.where(padding > 0.0, very_negative_logits, logits) - # TFLite hardcodes the range of qsoftmax, setting explicitly to avoid - # incompatible concats. - return fns.qsoftmax( - padded_logits, - qdomain='softmax', - narrow_to_asym_bit_depth=narrow_to_asym_bit_depth) - - def _UpdatePaddingWithPackedInputMask(self, padding, source_segment_ids, - query_segment_ids): - """Creates an attention mask based on source and query segment ids. - - This creates a mask that removes invalid attention, where the query vector - might assign some weight to neighboring sequences in a packed input example. - Assumes `n = target_batch // source_batch`. - - Args: - padding: Padding for logits, a tensor of shape [time, n, source_batch]. - source_segment_ids: a tensor of shape [time, source_batch]. - query_segment_ids: a tensor of shape [target_batch]. - - Returns: - Logits with mask applied. - """ - # Generating packed input mask for attention padding. - source_segment_ids = tf.expand_dims(source_segment_ids, 1) - query_segment_ids = tf.reshape( - query_segment_ids, - [1, -1, py_utils.GetShape(source_segment_ids)[2]]) - padding = tf.where( - tf.equal(source_segment_ids, query_segment_ids), padding, - tf.ones_like(padding)) - return padding - - -class AdditiveAttention(BaseAttentionLayer): - """Implements additive attention (also known as "Bahdanau Attention"). - - Described in: - - Dzmitry Bahdanau, Kyunghyun Cho, Yoshua Bengio. - "Neural Machine Translation by Jointly Learning to Align and Translate." - ICLR 2015. - https://arxiv.org/abs/1409.0473 - """ - - @classmethod - def Params(cls): - """Params for this `AdditiveAttention` class.""" - p = super().Params() - p.Define('source_dim', 0, 'Number of source nodes.') - p.Define('query_dim', 0, 'Number of query nodes.') - p.Define('hidden_dim', 0, 'Number of hidden nodes.') - # Fill in reasonable default for params init - p.params_init = py_utils.WeightInit.GaussianSqrtDim() - p.Define( - 'same_batch_size', False, - 'True iff the source and target sequence has the same batch size.') - return p - - def __init__(self, params): - """Constructs an `AdditiveAttention` object.""" - super().__init__(params) - p = self.params - - def AttenProbs(inputs): - """Generates probs.""" - source_batch = py_utils.GetShape(inputs.source_padding)[1] - target_batch = py_utils.GetShape(inputs.per_step_source_padding)[0] - multiplier = target_batch // source_batch - - # Shape of summed is [sl, tb/sb, sb, hidden_dim]. - summed = tf.tanh(inputs.concated_source_vecs + inputs.query_vec_reshaped) - # logits is of shape [sl * tb/sb * sb, 1]. Computes dot product - # between v with every rows in 'summed'. Then we reshape the - # result to be of shape [sl, tb/sb, sb]. - # - # Another equivalent way is to do: - # logits = tf.reduce_sum(summed * - # tf.reshape(v, [1, 1, 1, hidden_dim]), 3) - logits = py_utils.Matmul( - tf.reshape(summed, [-1, p.hidden_dim]), - tf.reshape(inputs.v, [p.hidden_dim, 1])) - logits = tf.reshape(logits, tf.shape(summed)[:3]) - # Take out the padding states. - # _source_padding is of shape [source_length, source_batch]. - # reshaped to [source_length, 1, source_batch]. - # per_step_source_padding is reshaped to the same but with 'multiplier' - # for the second dim. - source_padding = tf.expand_dims(inputs.source_padding, 1) - per_step_source_padding = tf.reshape( - tf.transpose(inputs.per_step_source_padding), - [-1, multiplier, source_batch]) - source_padding += per_step_source_padding - - if p.packed_input: - source_padding = self._UpdatePaddingWithPackedInputMask( - source_padding, inputs.source_segment_id, inputs.query_segment_id) - # Reshape logits to a matrix of shape [target_batch, source_length] and - # takes the softmax to compute the probabilities. - logits = tf.transpose(tf.reshape(logits, [-1, target_batch])) - source_padding = tf.transpose( - tf.reshape(source_padding, [-1, target_batch])) - probs = self._PaddedSoftmax(logits, source_padding) - return probs - - # Adds the atten function into the graph's library. - def Atten(v, w, source_padding, source_segment_id, concated_source_vecs, - concated_source_contexts, query_vec, query_segment_id, - per_step_source_padding, global_step): - """Computes the attention context vector. - - Args: - v: hidden weight. [hidden_dim, 1]. - w: query weight. [query_dim, hidden_dim]. - source_padding: [source_length, source_batch]. - source_segment_id: [source_lentgh, source_batch] - concated_source_vecs: [source_length, source_batch, hidden_dim]. - concated_source_contexts: [source_batch, source_length, context_dim] - query_vec: [target_batch, query_dim] - query_segment_id: [target_batch] - per_step_source_padding: [target_batch, source_length] - global_step: Required for deterministic dropout. - Note: concated_source_vecs are the vectors that are used to compute the - attention score between the query_vec and each concated_source_vec. The - concated_source_contexts are the vectors that compose the result. The - attention context vector is computed as a weighted average of the - concated_source_contexts, using the scores that were computed using - concated_source_vecs. - - Returns: - attention context vectors and probabilities. - """ - source_batch = py_utils.GetShape(concated_source_vecs)[1] - target_batch = py_utils.GetShape(query_vec)[0] - multiplier = target_batch // source_batch - # concated_source_vecs is reshaped to - # [source_length, 1, source_batch, hidden_dims] - concated_source_vecs = tf.expand_dims(concated_source_vecs, 1) - query_vec_transformed = py_utils.Matmul(query_vec, w) - - # query_vec is reshaped to - # [1, target_batch/source_batch, source_batch, hidden_dims]. - query_vec_reshaped = tf.reshape( - query_vec_transformed, [1, multiplier, source_batch, p.hidden_dim]) - # probs is of shape [target_batch, source_length] - probs = py_utils.CallDefun( - AttenProbs, - py_utils.NestedMap( - concated_source_vecs=concated_source_vecs, - source_padding=source_padding, - query_vec_reshaped=query_vec_reshaped, - v=v, - per_step_source_padding=per_step_source_padding, - source_segment_id=source_segment_id, - query_segment_id=query_segment_id)) - probs.set_shape(per_step_source_padding.shape) - - # Apply dropout to weights if applicable. - if not self.do_eval: - probs = _ApplyAttentionDropout(p, probs, global_step) - - # Reshape probs to be of shape - # [target_batch/source_batch, source_batch, source_length] - probs_reshaped = tf.reshape(probs, [multiplier, source_batch, -1]) - # Transpose probs to be of shape - # [source_batch, target_batch/source_batch, source_length] - probs_reshaped = tf.transpose(probs_reshaped, [1, 0, 2]) - # Batched matmul - # [source_batch, target_batch/source_batch, source_length] * - # [source_batch, source_length, context_dim] = - # [source_batch, target_batch/source_batch, context_dim] - summed = tf.matmul(probs_reshaped, concated_source_contexts) - - # summed is of shape - # [target_batch/source_batch, source_batch, context_dim] - summed = tf.transpose(summed, [1, 0, 2]) - - return tf.reshape(summed, [target_batch, -1]), probs - - # The source batch size equals to the target batch size. - def AttenSameBatchSize(v, w, source_padding, source_segment_id, - concated_source_vecs, concated_source_contexts, - query_vec, query_segment_id, per_step_source_padding, - global_step): - """Computes the attention context vector. - - Args: - v: hidden weight. [hidden_dim]. - w: query weight. [query_dim, hidden_dim]. - source_padding: [sl, b] - source_segment_id: [sl, b] - concated_source_vecs: [sl, b, hidden_dim]. - concated_source_contexts: [b, sl, context_dim] - query_vec: [b, query_dim] - query_segment_id: [b] - per_step_source_padding: [b, sl] - global_step: Required for deterministic dropout. - - Returns: - attention context vectors and probabilities. - """ - # TODO(jiaye): support dropout - if p.atten_dropout_prob != 0: - raise NotImplementedError('dropout is not supported') - del global_step - - # [b, hidden_dim] - query_vec = py_utils.Matmul(query_vec, w) - # [sl, b] - def AttenProbs(inputs): - """Calculates atten probs with padding.""" - # tf.tanh(x+y) shape [sl, b, hidden_dim] - summed = tf.tanh(inputs.x + inputs.y) - # [-1, hidden_dim] * [hidden_dim, 1] = [-1, 1] - res = py_utils.Matmul( - tf.reshape(summed, [-1, p.hidden_dim]), tf.expand_dims(inputs.v, 1)) - # Reshape res to [sl, b] - logits = tf.reshape(res, tf.shape(summed)[:2]) - # Take out the padding states. _source_padding is of shape [sl, b]. - source_padding = inputs.source_padding + tf.transpose( - inputs.per_step_source_padding) - - if p.packed_input: - source_padding = self._UpdatePaddingWithPackedInputMask( - tf.expand_dims(source_padding, 1), inputs.source_segment_id, - inputs.query_segment_id) - source_padding = tf.squeeze(source_padding, 1) - # [b, sl] - source_padding = tf.transpose(source_padding) - logits = tf.transpose(logits) - # softmax to compute the probabilities. [b, sl] - probs = self._PaddedSoftmax(logits, source_padding) - return probs - - probs = py_utils.CallDefun( - AttenProbs, - py_utils.NestedMap( - x=concated_source_vecs, - source_padding=source_padding, - y=query_vec, - v=v, - per_step_source_padding=per_step_source_padding, - source_segment_id=source_segment_id, - query_segment_id=query_segment_id)) - probs.set_shape(per_step_source_padding.shape) - - # contexts[i, :] is a weighted (probs[i, :]) average of - # concated_source_vecs[i, :, :]. - # Reshaped probs is of shape [b, 1, sl] - reshaped_probs = tf.expand_dims(probs, 1) - # [b, 1, sl] * [b, sl, context_dim] = [b, 1, context_dim] - contexts = tf.matmul(reshaped_probs, concated_source_contexts) - # Reshaped context is of shape [b, context_dim] - contexts = tf.squeeze(contexts, axis=1) - return contexts, probs - - if p.same_batch_size: - self._ctx_vec = AttenSameBatchSize - else: - self._ctx_vec = Atten - - def EncodeSource(src_w, vecs, ctxs): - """Prepares source vec and ctx.""" - time, batch = py_utils.GetShape(vecs, 2) - ctxs = py_utils.HasShape(ctxs, [time, batch, -1]) - # source_dim can be a symbolic expression. - transformed_vecs = tf.reshape( - py_utils.Matmul( - tf.reshape(vecs, [-1, symbolic.ToStatic(p.source_dim)]), src_w), - [time, batch, -1]) - transformed_vecs = tf.identity( - transformed_vecs, name='source_vecs_projected') - transposed_ctxs = tf.transpose(ctxs, [1, 0, 2]) - transposed_ctxs = tf.identity(transposed_ctxs, name='source_ctx') - return transformed_vecs, transposed_ctxs - - self._encode_source = EncodeSource - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - pc = py_utils.WeightParams( - shape=[p.source_dim, p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['AdditiveAttention_vars']) - self.CreateVariable('source_var', pc, self.AddGlobalVN) - - pc = py_utils.WeightParams( - shape=[p.query_dim, p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['AdditiveAttention_vars']) - self.CreateVariable('query_var', pc, self.AddGlobalVN) - - pc = py_utils.WeightParams( - shape=[p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['AdditiveAttention_vars']) - self.CreateVariable('hidden_var', pc, self.AddGlobalVN) - - def PackSource(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - """Packs source vectors. - - Does not change attention state. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: A single tensor of shape [time, batch_size, source_dim]. - source_contexts: A single tensor of shape [time, batch_size, some_dim]. - source_padding: A tensor of shape [time, batch_size]. - source_segment_id: A tensor of shape [time, batch_size]. - - Returns: - A NestedMap containing the packed source. - """ - with tf.name_scope(self.params.name): - if source_segment_id is None: - source_segment_id = tf.zeros_like(source_padding) - - (concated_source_vecs, concated_source_contexts) = ( - self._encode_source(theta.source_var, source_vecs, source_contexts)) - return py_utils.NestedMap( - # [time, batch_size, hidden_dim]. - source_vecs=concated_source_vecs, - # [batch_size, time, context_dim]. - # Note the mismatch between `source_vecs` and `source_contexts`. In - # `source_vecs`, time is the first dim, while it is the second dim in - # `source_contexts`. - source_contexts=concated_source_contexts, - # [time, batch_size]. - source_padding=source_padding, - # [time, batch_size]. - source_segment_id=source_segment_id) - - def ZeroAttentionState(self, source_length, decoder_batch_size): - p = self.params - # This is just a dummy state. The first dimension of the state has to match - # decoder_batch_size. - zs = tf.zeros([decoder_batch_size, 1], dtype=py_utils.FPropDtype(p)) - return zs - - def ComputeContextVectorWithSource(self, - theta, - packed_src, - query_vec, - attention_state=None, - per_step_source_padding=None, - query_segment_id=None): - """Computes the context vector given the current query output. - - Note: `packed_src.source_vecs` are the vectors that are used to compute the - attention score between the `query_vec` and each `packed_src.source_vecs`. - The `packed_src.source_contexts` are the vectors that compose the result. - The attention context vector is computed as a weighted average of the - `packed_src.source_contexts`, using the scores that were computed using - `packed_src.source_vecs`. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - packed_src: A `.NestedMap` object returned by PackSource or - InitForSourcePacked. - query_vec: a tensor of shape [batch_size, query_dim]. - attention_state: previous attention state. It is not used in - `AdditiveAttention`, and is simply passed through. - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should be of shape [target_batch_size, source_length]. - query_segment_id: a tensor of shape [batch_size] - - Returns: - A tuple of 3 elements. - - - The attention context vector: [batch_size, context_dim] - - The attention probability vector: [batch_size, time] - - The new attention mechanism state: possibly nested tuple of tensors with - dimensions [target_batch, ...] - """ - p = self.params - concated_source_vecs = packed_src.source_vecs - concated_source_contexts = packed_src.source_contexts - source_padding = packed_src.source_padding - source_segment_id = packed_src.source_segment_id - query_batch_size = py_utils.GetShape(query_vec)[0] - source_length = py_utils.GetShape(source_padding)[0] - if per_step_source_padding is None: - zero = tf.constant(0.0, dtype=query_vec.dtype) - per_step_source_padding = tf.fill([query_batch_size, source_length], zero) - per_step_source_padding = py_utils.HasShape( - per_step_source_padding, [query_batch_size, source_length]) - hidden = py_utils.AddPerStepVN(p, theta.hidden_var) - query = py_utils.AddPerStepVN(p, theta.query_var) - - if source_segment_id is None: - source_segment_id = tf.zeros_like(source_padding) - if query_segment_id is None: - query_segment_id = tf.zeros( - tf.shape(query_vec)[0], dtype=source_padding.dtype) - - ctx_vec, prob = self._ctx_vec(hidden, query, source_padding, - source_segment_id, concated_source_vecs, - concated_source_contexts, query_vec, - query_segment_id, per_step_source_padding, - theta.global_step) - - return ctx_vec, prob, attention_state - - -class DotProductAttention(BaseAttentionLayer): - """Implements dot-product attention (also known as "Luong Attention"). - - Described in: - - Minh-Thang Luong, Hieu Pham, Christopher D. Manning. - "Effective Approaches to Attention-based Neural Machine Translation." - EMNLP 2015. - https://arxiv.org/abs/1508.04025 - """ - - @classmethod - def Params(cls): - """Params for `DotProductAttention`.""" - p = super().Params() - p.Define('source_dim', 0, 'Number of source nodes.') - p.Define('query_dim', 0, 'Number of query nodes.') - p.Define('hidden_dim', 0, 'Number of hidden nodes.') - p.Define( - 'use_dim_scale', True, 'Whether or not to use per_dim_scale to scale ' - 'the individual dims when calculating attention probabilities. It can ' - 'increase training stability when set to False.') - return p - - def __init__(self, params): - """Constructs a DotProductAttention object.""" - super().__init__(params) - p = self.params - # TODO(yonghui): relax these constraints. - assert p.source_dim == p.query_dim - assert p.source_dim == p.hidden_dim - - def AttenProbs(inputs): - """Main attention function. - - target_batch = source_batch * n where n is an integer >= 1. - In this case inputs.query_vec contains: - ------------------------- - | instance 1 | - | instance 2 | - 0 | ... | - | instance source_batch | - ------------------------- - | instance 1 | - | instance 2 | - 1 | ... | - | instance source_batch | - ------------------------- - ... - ------------------------- - | instance 1 | - | instance 2 | - n-1 | ... | - | instance source_batch | - ------------------------- - One use case is beam search where n = beam size. - - Args: - inputs: a NestedMap containing: - - per_dim_scale: [source_dim], a vec to scale individual dims. - - source_padding: [time, source_batch]. - - concated_source_vecs: [source_batch, time, source_dim]. - - query_vec: [target_batch, source_dim]. - - per_step_source_padding: [target_batch, source_length] - - source_segment_id: [time, source_batch]. - - query_segment_id: [target_batch]. - - Returns: - logits [target_batch, source_time]. - """ - source_padding = tf.transpose(inputs.source_padding) - concated_source_vecs = inputs.concated_source_vecs - - logit_scale = tf.stop_gradient( - tf.math.rsqrt( - tf.cast( - py_utils.GetShape(inputs.query_vec)[1], - dtype=py_utils.FPropDtype(p)))) - source_batch = py_utils.GetShape(concated_source_vecs)[0] - target_batch = py_utils.GetShape(inputs.query_vec)[0] - query_vec = inputs.query_vec * inputs.per_dim_scale - # The n here refers to the "n" described in the comment above. - n = target_batch // source_batch - query_vec = tf.reshape(query_vec, [n, source_batch, -1]) - # => [source_batch, source_dim, n] - query_vec = tf.transpose(query_vec, [1, 2, 0]) - # => [n, source_batch, source_sequence_len] - per_step_source_padding = tf.reshape(inputs.per_step_source_padding, - [n, source_batch, -1]) - # => [source_batch, source_sequence_len, n] - per_step_source_padding = tf.transpose(per_step_source_padding, [1, 2, 0]) - # Dot-product part. - # Calls batch_mat_mul since dim > 2 for per-instance matmul. - # [source_batch, time, source_dim] * [source_batch, source_dim, n] - # => [source_batch, time, n] - logits = tf.matmul(concated_source_vecs, query_vec) - logits *= logit_scale - # Exclude padding frames. - # [source_batch, time] => [source_batch, time, 1] - source_padding = tf.expand_dims(source_padding, 2) - source_padding += per_step_source_padding - if p.packed_input: - source_padding = tf.transpose(source_padding, [1, 2, 0]) - source_padding = self._UpdatePaddingWithPackedInputMask( - source_padding, inputs.source_segment_id, inputs.query_segment_id) - source_padding = tf.transpose(source_padding, [1, 2, 0]) - else: - source_padding = tf.transpose(source_padding, [2, 0, 1]) - - # => [n, source_batch, time] - logits = tf.transpose(logits, [2, 0, 1]) - - # => [n * source_batch, time]. - # This makes logits store content in the same order as query_vec. - logits = tf.reshape(logits, [target_batch, -1]) - source_padding = tf.reshape(source_padding, [target_batch, -1]) - probs = self._PaddedSoftmax(logits, source_padding) - return probs - - def Atten(per_dim_scale, source_padding, source_segment_id, - concated_source_vecs, concated_source_contexts, query_vec, - query_segment_id, per_step_source_padding, global_step): - """Main attention function. - - Args: - per_dim_scale: [source_dim], a vec to scale individual dims. - source_padding: [time, source_batch]. - source_segment_id: [time, source_batch]. - concated_source_vecs: [time, source_batch, source_dim]. - concated_source_contexts: [source_batch, time, context_dim]. - query_vec: [target_batch, source_dim]. - query_segment_id: [target_batch]. - per_step_source_padding: [target_batch, source_length] - global_step: Required for deterministic dropout. - Note: concated_source_vecs are the vectors that are used to compute the - attention score between the query_vec and each concated_source_vec. The - concated_source_contexts are the vectors that compose the result. The - attention context vector is computed as a weighted average of the - concated_source_contexts, using the scores that were computed using - concated_source_vecs. - - Returns: - Two tensors: - - - context_vector: [target_batch, context_dim]. - - probs: [target_batch, time]. - """ - py_utils.assert_shape_match([py_utils.GetShape(concated_source_vecs)[2]], - [py_utils.GetShape(query_vec)[1]]) - py_utils.assert_shape_match([py_utils.GetShape(concated_source_vecs)[2]], - [symbolic.ToStatic(p.source_dim)]) - source_batch = py_utils.GetShape(concated_source_vecs)[1] - target_batch = py_utils.GetShape(query_vec)[0] - n = target_batch // source_batch - concated_source_vecs = tf.transpose(concated_source_vecs, [1, 0, 2]) - concated_source_vecs = tf.identity( - concated_source_vecs, name='concated_source_vecs') - returned_probs = py_utils.CallDefun( - AttenProbs, - py_utils.NestedMap( - per_dim_scale=per_dim_scale, - source_padding=source_padding, - concated_source_vecs=concated_source_vecs, - query_vec=query_vec, - per_step_source_padding=per_step_source_padding, - source_segment_id=source_segment_id, - query_segment_id=query_segment_id)) - returned_probs.set_shape(per_step_source_padding.shape) - - # => [n, source_batch, time]. - probs = tf.reshape(returned_probs, [n, source_batch, -1]) - # => [source_batch, n, time]. - probs = tf.transpose(probs, [1, 0, 2]) - - # Apply dropout to weights if applicable. - if not self.do_eval: - probs = _ApplyAttentionDropout(p, probs, global_step) - - # Weight each frame with the probability and sum them. - # [source_batch, n, time] * [source_batch, time, context_dim] - # => [source_batch, n, context_dim]. - concated_source_contexts = tf.identity( - concated_source_contexts, name='concated_source_contexts') - context_vector = tf.matmul(probs, concated_source_contexts) - # => [n, source_batch, context_dim]. - context_vector = tf.transpose(context_vector, [1, 0, 2]) - # => [n * source_batch, context_dim]. - context_vector = tf.reshape(context_vector, [target_batch, -1]) - - return context_vector, returned_probs - - self._ctx_vec = Atten - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - if p.use_dim_scale: - pc = py_utils.WeightParams( - shape=[p.hidden_dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=['DotProductAttention_vars']) - - self.CreateVariable('per_dim_scale', pc) - - def PackSource(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - """Packs source vectors. - - Does not change attention state. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: A tensor of shape [time, source_batch, source_dim]. - source_contexts: A tensor of shape [time, source_batch, context_dim]. - source_padding: A tensor of shape [time, source_batch]. - source_segment_id: A tensor of shape [time, source_batch]. - - Returns: - A tuple (concated_source_vecs, concated_source_contexts, source_padding) - where `concated_source_vecs` is a tensor of shape [time, batch_size, - hidden_dim], `concated_source_contexts` is a tensor of shape - [batch_size, time, some_dim] and `source_padding` is a tensor of shape - [time, batch_size]. - """ - concated_source_vecs = tf.identity(source_vecs) - concated_source_contexts = tf.transpose(source_contexts, [1, 0, 2]) - if source_segment_id is None: - source_segment_id = tf.zeros_like(source_padding) - return py_utils.NestedMap( - # [time, batch_size, hidden_dim]. - source_vecs=concated_source_vecs, - # [batch_size, time, context_dim]. - # Note the mismatch between `source_vecs` and `source_contexts`. In - # `source_vecs`, time is the first dim, while it is the second dim in - # `source_contexts`. - source_contexts=concated_source_contexts, - # [time, batch_size]. - source_padding=source_padding, - # [time, batch_size]. - source_segment_id=source_segment_id) - - def ZeroAttentionState(self, source_length, decoder_batch_size): - p = self.params - # No states to keep track of currently. - return tf.zeros([decoder_batch_size, 1], dtype=p.dtype) - - def ComputeContextVectorWithSource(self, - theta, - packed_src, - query_vec, - attention_state=None, - per_step_source_padding=None, - query_segment_id=None): - """Computes the context vector given the current query output. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - packed_src: A `.NestedMap` object returned by PackSource or - InitForSourcePacked. - query_vec: a tensor of shape [target_batch, query_dim], where target_batch - = n * source_batch (e.g., n = num_hyps_per_beam in beamsearch). Along - the target_batch dimension, there are n groups of consecutive rows, each - group containing source_batch rows. - attention_state: previous attention state. It is not used in - AdditiveAttention, and is simply passed through. - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should be of shape [target_batch, source_length]. - query_segment_id: Query segment id with shape [target_batch]. - - Returns: - A tuple of 3 elements. - - - The attention context vector: [batch_size, context_dim] - - The attention probability vector: [batch_size, time] - - The new attention mechanism state: possibly nested tuple of tensors - with dimensions [target_batch, ...] - """ - concated_source_vecs = packed_src.source_vecs - concated_source_contexts = packed_src.source_contexts - - source_padding = packed_src.source_padding - source_segment_id = packed_src.source_segment_id - query_batch_size = py_utils.GetShape(query_vec)[0] - source_sequence_length = py_utils.GetShape(source_padding)[0] - if per_step_source_padding is None: - zero = tf.constant(0.0, dtype=query_vec.dtype) - per_step_source_padding = tf.fill( - [query_batch_size, source_sequence_length], zero) - per_step_source_padding = py_utils.HasShape( - per_step_source_padding, [query_batch_size, source_sequence_length]) - if source_segment_id is None: - source_segment_id = tf.zeros_like(source_padding) - if query_segment_id is None: - query_segment_id = tf.zeros( - py_utils.GetShape(query_vec)[0], dtype=source_padding.dtype) - - def ScaleFn(x): - return tf.nn.softplus(x) / tf.nn.softplus(tf.constant(0.0, dtype=x.dtype)) - - if self.params.use_dim_scale: - per_dim_scale_var = theta.per_dim_scale - else: - per_dim_scale_var = tf.constant(0.0, dtype=query_vec.dtype) - - ctx_vec, prob = self._ctx_vec( - ScaleFn(per_dim_scale_var), source_padding, source_segment_id, - concated_source_vecs, concated_source_contexts, query_vec, - query_segment_id, per_step_source_padding, theta.global_step) - return ctx_vec, prob, attention_state - - -def _RecursiveReshape(x, shape): - if x is None: - return None - elif isinstance(x, py_utils.NestedMap): - return x.Transform(lambda y: _RecursiveReshape(y, shape)) - else: - return tf.reshape(x, shape) if x.shape.ndims == 2 else x - - -class MultiHeadedAttention(BaseAttentionLayer, quant_utils.QuantizableLayer): - """Attention with multiple attention heads. - - Conceptually, the algorithm works as follows: - - 1. Source vectors (attention keys) are first projected to vectors of dim - p.hidden_dim. - 2. Query vectors are projected to vectors of dim p.hidden_dim as well. - 3. Context vectors (attention values) are not projected by default, unless - `enable_ctx_pre_proj` is True. - 4. Source vectors, query vectors and context vectors are all split into - p.num_attention_heads chunks. - 5. The inner atten mechanism is computed separately on each of the chunks. - 6. Attention contexts from each of the chunk are concatenated to form the - final context. - 7. Attention probs from each of the chunk are averaged to form the final - attention prob. - """ - - @classmethod - def Params(cls): - """Params for MultiHeadedAttention.""" - p = super().Params() - p.Define('source_dim', 0, 'Number of source nodes.') - p.Define('query_dim', 0, 'Number of query nodes.') - p.Define('context_dim', 0, 'Number of context nodes.') - p.Define('hidden_dim', 0, 'Number of hidden nodes.') - p.Define('num_attention_heads', 2, 'Num of attention heads.') - p.Define( - 'use_source_vec_as_attention_value', True, - 'Whether or not to use source_vec as the attention value as well.' - ' If True, we expect source_vec and source_contexts are the same.') - p.Define('enable_source_proj', True, - 'If False, source side linear projection is disabled.') - p.Define('enable_query_proj', True, - 'If False, query side linear projection is disabled.') - p.Define('inner_atten_params', DotProductAttention.Params(), - 'Params for underlying attention mechanism.') - p.Define( - 'enable_ctx_pre_proj', False, - 'If True, context is pre-projected before processing into' - ' hidden_dim.') - p.Define( - 'enable_ctx_post_proj', False, - 'If True, computed context is post projected into' - ' ctx_post_proj_dim.') - p.Define('ctx_post_proj_dim', 0, 'Number of post projection nodes.') - p.Define( - 'num_post_proj', 1, 'Number of post projections, usually the same as ' - 'number of tasks. Each task may choose to use one of the post ' - 'projection layers.') - p.Define( - 'proj_init', 'default', 'Initialization approach for projection ' - 'layers:' - 'uniform: Use uniform initialization. ' - 'default: Use the default Xavier initialization.') - p.Define( - 'attention_head_prob_index', -1, 'If > 0, instead of averaging ' - 'the probabilities of all attention heads when returning the ' - 'attention probability, instead return the selected index prob.') - - p.Define('use_bias', True, 'Whether to use bias for projection layer.') - p.Define('enable_per_dim_scale', True, - 'Whether to use per_dim_scale in inner_atten.') - - # Often the attention context output needs to be concated - # with tensors from another layer. This allows them to share - # quantization parameters. By convention, all attention layers - # need to include their context output vectors in this domain. - p.qdomain.Define('atten_context', None, - 'Quantization domain for attention context.') - - p.params_init = py_utils.WeightInit.Xavier(scale=1.0) - - return p - - def __init__(self, params): - """Constructs a MultiHeadedAttention object.""" - super().__init__(params) - p = self.params - assert symbolic.ToStatic(p.hidden_dim) % p.num_attention_heads == 0 - - if p.proj_init not in ('uniform', 'default'): - raise ValueError('Unknown proj_init: %s!' % p.proj_init) - - att_dim = p.hidden_dim // p.num_attention_heads - - att_p = p.inner_atten_params.Set( - source_dim=att_dim, - query_dim=att_dim, - hidden_dim=att_dim, - dtype=p.dtype, - atten_dropout_prob=p.atten_dropout_prob, - atten_dropout_deterministic=p.atten_dropout_deterministic, - packed_input=p.packed_input) - - if att_p.cls == DotProductAttention: - att_p.use_dim_scale = p.enable_per_dim_scale - - if not att_p.name: - att_p.name = 'inner_att' - self.CreateChild('atten', att_p) - if p.attention_head_prob_index >= 0: - assert p.attention_head_prob_index < p.num_attention_heads - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - def InitProj(layer_dim, bias=False): - if p.proj_init == 'uniform': - # Note we also initialize bias with uniform distribution here, following - # the default Pytorch implementation: - # https://pytorch.org/docs/stable/nn.html#linear - proj_init = py_utils.WeightInit.Uniform(scale=np.sqrt(1.0 / layer_dim)) - elif p.proj_init == 'default': - proj_init = py_utils.WeightInit.Constant(0.0) if bias else p.params_init - return proj_init - - if p.use_bias: - pc_bias = py_utils.WeightParams( - shape=[p.hidden_dim], - init=InitProj(p.hidden_dim, bias=True), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - if p.enable_source_proj: - pc = py_utils.WeightParams( - shape=[p.source_dim, p.hidden_dim], - init=InitProj(p.source_dim), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('source_proj', pc) - if p.use_bias: - self.CreateVariable('source_proj_b', pc_bias) - else: - assert p.source_dim == p.hidden_dim - - if p.enable_query_proj: - pc = py_utils.WeightParams( - shape=[p.query_dim, p.hidden_dim], - init=InitProj(p.query_dim), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('query_proj', pc) - if p.use_bias: - self.CreateVariable('query_proj_b', pc_bias) - else: - assert p.query_dim == p.hidden_dim - - if p.enable_ctx_pre_proj and not p.use_source_vec_as_attention_value: - assert p.context_dim - pc = py_utils.WeightParams( - shape=[p.context_dim, p.hidden_dim], - init=InitProj(p.context_dim), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('ctx_proj', pc) - if p.use_bias: - self.CreateVariable('ctx_proj_b', pc_bias) - - if p.enable_ctx_post_proj: - assert p.ctx_post_proj_dim - if p.num_post_proj == 1: - pc_shape = [p.hidden_dim, p.ctx_post_proj_dim] - pc_b_shape = [p.ctx_post_proj_dim] - elif p.num_post_proj > 1: - pc_shape = [p.hidden_dim, p.ctx_post_proj_dim, p.num_post_proj] - pc_b_shape = [p.ctx_post_proj_dim, p.num_post_proj] - else: - raise ValueError('num_post_proj must > 0!') - pc = py_utils.WeightParams( - shape=pc_shape, - init=InitProj(p.hidden_dim), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('ctx_post_proj', pc) - if p.use_bias: - pc_bias_post_proj = py_utils.WeightParams( - shape=pc_b_shape, - init=InitProj(p.ctx_post_proj_dim, bias=True), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('ctx_post_proj_b', pc_bias_post_proj) - - self.TrackQTensor('source_proj_matmul', 'source_proj_add', - 'query_proj_matmul', 'query_proj_add', - 'ctx_pre_proj_matmul', 'ctx_pre_proj_add') - # TODO(suderman): Remove the self.do_eval check below once brop quant within - # defun is fixed on the training side. This is less than ideal as-is because - # training will just trend to match downstream quant constraints vs force - # alignment. - self.TrackQTensor( - 'ctx_post_proj_matmul', 'ctx_post_proj_add', domain='atten_context') - - @classmethod - def SetOutputContextDim(cls, p, out_dim): - p.ctx_post_proj_dim = out_dim - - @py_utils.NameScopeDecorator('MultiHeadedAttention/PackSource') - def PackSource(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - """Packs source vectors. - - Does not change attention state. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: A tensor of shape [time, source_batch, source_dim]. - source_contexts: A tensor of shape [time, source_batch, context_dim]. - source_padding: A tensor of shape [time, source_batch]. - source_segment_id: A tensor of shape [time, source_batch]. - - Returns: - A NestedMap representing packed src. It will have the same structure - as the one returned by the inner atten, except that source_batch will be - source_batch * num_heads. - """ - - p = self.params - fns = self.fns - if not p.enable_source_proj: - assert p.source_dim == p.hidden_dim - if not p.enable_query_proj: - assert p.query_dim == p.hidden_dim - with tf.name_scope('init__0'): - if p.use_source_vec_as_attention_value: - source_vecs = py_utils.HasShape(source_vecs, - py_utils.GetShape(source_contexts)) - time_steps, batch_size = py_utils.GetShape(source_padding, 2) - # source_projected shape [time * source_batch, hidden] - with tf.name_scope('init__0a'): - source_vec_depth = py_utils.GetShape(source_vecs)[2] - with tf.name_scope('init__0b'): - if p.enable_source_proj: - source_projected = ( - fns.qbatchmatmul( - tf.reshape(source_vecs, [-1, source_vec_depth]), - fns.qweight(theta.source_proj), - qt='source_proj_matmul')) - if p.use_bias: - source_projected = fns.qadd( - source_projected, - fns.qweight(theta.source_proj_b), - qt='source_proj_add') - else: - source_projected = tf.reshape(source_vecs, [-1, source_vec_depth]) - with tf.name_scope('init__1'): - num_heads = p.num_attention_heads - # => [time, source_batch * num_heads, hidden / num_heads] - source_projected = tf.reshape(source_projected, [ - time_steps, batch_size * num_heads, - symbolic.ToStatic(p.hidden_dim // num_heads) - ]) - source_projected = self.ProcessProjectionVec(theta, source_projected, - 'source') - if p.use_source_vec_as_attention_value: - source_contexts_reshaped = source_projected - else: - if p.enable_ctx_pre_proj: - source_contexts_projected = fns.qbatchmatmul( - tf.reshape(source_contexts, - [-1, py_utils.GetShape(source_contexts)[2]]), - fns.qweight(theta.ctx_proj), - qt='ctx_pre_proj_matmul') - if p.use_bias: - source_contexts_projected = fns.qadd( - source_contexts_projected, - fns.qweight(theta.ctx_proj_b), - qt='ctx_pre_proj_add') - else: - source_contexts_projected = source_contexts - - source_context_depth = py_utils.GetShape(source_contexts_projected)[-1] - source_contexts_reshaped = tf.reshape(source_contexts_projected, [ - time_steps, batch_size * num_heads, - source_context_depth // num_heads - ]) - source_contexts_projected = self.ProcessProjectionVec( - theta, source_contexts_projected, 'ctx') - - with tf.name_scope('init__2'): - source_padding_replicated = tf.reshape( - tf.tile( - tf.reshape(source_padding, [time_steps, batch_size, 1]), - [1, 1, num_heads]), [time_steps, batch_size * num_heads]) - if source_segment_id is None: - source_segment_id_repl = tf.zeros_like(source_padding_replicated) - else: - source_segment_id_repl = tf.reshape( - tf.tile( - tf.reshape(source_segment_id, [time_steps, batch_size, 1]), - [1, 1, num_heads]), [time_steps, batch_size * num_heads]) - - return self.atten.PackSource(theta.atten, source_projected, - source_contexts_reshaped, - source_padding_replicated, - source_segment_id_repl) - - @py_utils.NameScopeDecorator('MultiHeadedAttention/ExtendSourcePacked') - def ExtendSourcePacked(self, - theta, - new_source_vecs, - new_source_contexts, - new_source_paddings, - new_source_segment_ids, - cached_packed_src, - t=None): - """Extend cached source_vecs and source_contexts by one more timestep. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - new_source_vecs: A tensor of shape [source_batch, source_dim]. - new_source_contexts: A tensor of shape [source_batch, context_dim]. - new_source_vecs and new_source_contexts are source_vecs and - source_contexts for the new timestep to be extended. - new_source_paddings: If not None, a tensor of shape [source_batch]. - source_padding for the new timestep. - new_source_segment_ids: If not None, a tensor of shape [source_batch]. - source_segment_id for the new timestep. - cached_packed_src: a `.NestedMap` object, containing already preprocessed - source_vecs and source_contexts for the previous t-1 steps. To support - tf.while_loop on TPU (satisfying static shape requirement), instead of - using tf.concat to update the cached vectors, the time dimension of each - cached vector is fixed as the max_sequence_length and inplace - update op is used to update the information for each time step: - * source_vecs: A tensor of shape [max_sequence_length, source_batch, - hidden_dim]. [:t, :, :] contains valid preprocessed source_vecs in the - previous t - 1 timesteps, the rests are invalid data. - * source_contexts: A tensor of shape [max_sequence_length, source_batch, - hidden_dim]. [:t, :, :] contains valid preprocessed source_contexts in - the previous t - 1 timesteps, the rests are invalid data. - * source_padding: If not None, a tensor of shape [max_sequence_length, - source_batch, num_heads]. [:t, :, :] contains cached source padding - for the previous t - 1 timesteps, the rests are invalid data. - * source_segment_id: If not None, a tensor of shape - [max_sequence_length, source_batch, num_heads]. [:t, :, :] contains - cached source segment id for the previous t - 1 timesteps, the rests - are invalid data. - When t is None (not running on TPU or the while loop is unrolled): - * source_vecs: A tensor of shape [t - 1, source_batch, hidden_dim]. - * source_contexts: A tensor of shape [t - 1, source_batch, hidden_dim]. - * source_padding: If not None, a tensor of shape [t - 1, source_batch, - num_heads], cached source padding for the previous t - 1 timesteps. - * source_segment_id: If not None, a tensor of shape [t - 1, - source_batch, num_heads], cached source segment id for the previous t - - 1 timesteps. - t: a scalar, the current time step, 0-based. - - Returns: - Extended cached source_vecs, source_contexts, source_paddings, and - source_segment_ids. The time dimension of each cached state is fixed: - 'extended_source_vec' is of shape [max_sequence_length, batch_size, - num_heads * dim]; - 'extended_source_context' is of shape [max_sequence_length, batch_size, - num_heads * dim]; - 'source_padding' is of shape [max_sequence_length, batch_size, num_heads]; - 'source_segment_id' is of shape [max_sequence_length, batch_size, - num_heads]. - But only [:(t + 1), :, :] contains valid data. - If t is not given, - 'extended_source_vec' is of shape [t, batch_size, num_heads * dim]; - 'extended_source_context' is of shape [t, batch_size, num_heads * dim]; - 'source_padding' is of shape [t, batch_size, num_heads]; - 'source_segment_id' is of shape [t, batch_size, num_heads]. - """ - batch_size = py_utils.GetShape(new_source_vecs)[0] - if new_source_paddings is None: - new_source_paddings = tf.zeros([batch_size], dtype=new_source_vecs.dtype) - if new_source_segment_ids is None: - new_source_segment_ids = tf.zeros([batch_size], - dtype=new_source_vecs.dtype) - processed_packed_src = self.InitForSourcePacked( - theta, tf.expand_dims(new_source_vecs, 0), - tf.expand_dims(new_source_contexts, 0), - tf.expand_dims(new_source_paddings, 0), - tf.expand_dims(new_source_segment_ids, 0)) - extended_packed_src = py_utils.NestedMap() - for key in ('source_vecs', 'source_contexts', 'source_padding', - 'source_segment_id'): - if cached_packed_src.get(key, None) is None: - extended_packed_src[key] = None - else: - if t is not None: - processed = tf.reshape(processed_packed_src[key], [batch_size, -1]) - # Make sure t is a scaler instead of tensors having shape like [1,]. - # This could happen in cases where function is called by recurrent.py - # (for example target_sequence_sampler.) - t = tf.reshape(t, []) - extended_packed_src[key] = inplace_ops.alias_inplace_update( - cached_packed_src[key], t, processed) - else: - processed = tf.reshape(processed_packed_src[key], [1, batch_size, -1]) - extended_packed_src[key] = tf.concat( - [cached_packed_src[key], processed], axis=0) - return extended_packed_src - - @py_utils.NameScopeDecorator('MultiHeadedAttention/ZeroAttentionState') - def ZeroAttentionState(self, source_length, decoder_batch_size): - zero_att_state = self.atten.ZeroAttentionState( - source_length, decoder_batch_size * self.params.num_attention_heads) - # [batch * num_heads, length] => [batch, num_heads * length]. - zero_att_state = _RecursiveReshape(zero_att_state, [decoder_batch_size, -1]) - nested_map_zero_att_state = py_utils.NestedMap(inner=zero_att_state) - if self.params.attention_head_prob_index >= 0: - selected_prob_head = tf.zeros([decoder_batch_size, source_length]) - nested_map_zero_att_state[ - 'selected_attention_head_probs'] = selected_prob_head - return nested_map_zero_att_state - - def ProcessProjectionVec(self, theta, projection_vec, projection_type): - # no-op for this class but allows subclasses to override to process - # projected vectors. - return projection_vec - - @py_utils.NameScopeDecorator( - 'MultiHeadedAttention/ComputeContextVectorWithSource') - def ComputeContextVectorWithSource(self, - theta, - packed_src, - query_vec, - attention_state=None, - per_step_source_padding=None, - query_segment_id=None, - atten_idx=None): - """Computes the context vector given the current query output. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - packed_src: A `.NestedMap` object returned by PackSource or - InitForSourcePacked. - query_vec: a tensor of shape [target_batch, query_dim]. - attention_state: A NestedMap. 'inner' contains the inner attention - state. It is not used in AdditiveAttention, and is simply passed - through. Optionally, if attention_head_prob_index >= 0, then - 'selected_attention_head_probs' contains the selected attention - probability head. - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should be of shape [target_batch_size, source_length]. - query_segment_id: a tensor of shape [target_batch]. - atten_idx: If not None, then apply a different attention projection for - different samples in a batch, each of which may come from different - tasks. This is usually used in multi-task setting. A tensor of shape - [target_batch]. - Note: concated_source_vecs are the vectors that are used to compute the - attention score between the query_vec and each concated_source_vec. The - concated_source_contexts are the vectors that compose the result. The - attention context vector is computed as a weighted average of the - concated_source_contexts, using the scores that were computed using - concated_source_vecs. - - Returns: - A tuple of 3 elements. - - - The attention context vector: [batch_size, context_dim] - - The attention probability vector: [batch_size, time] - - The new attention mechanism state: A nested tuple of tensors with - dimensions [target_batch, ...]. See input 'attention_state' for - description of items in the nested tuple. - """ - p = self.params - fns = self.fns - source_padding = packed_src.source_padding - source_seq_len = py_utils.GetShape(source_padding)[0] - num_heads = p.num_attention_heads - batch_size = py_utils.GetShape(query_vec)[0] - static_inner_atten_dim = symbolic.ToStatic(p.hidden_dim // num_heads) - query_vec_projected_shape = [batch_size * num_heads, static_inner_atten_dim] - - if p.enable_query_proj: - query_vec_projected = fns.qbatchmatmul( - query_vec, fns.qweight(theta.query_proj), qt='query_proj_matmul') - if p.use_bias: - query_vec_projected = fns.qadd( - query_vec_projected, - fns.qweight(theta.query_proj_b), - qt='query_proj_add') - query_vec_projected = tf.reshape(query_vec_projected, - query_vec_projected_shape) - query_vec_projected = self.ProcessProjectionVec(theta, - query_vec_projected, - 'query') - else: - query_vec_projected = tf.reshape(query_vec, query_vec_projected_shape) - - query_batch_size = py_utils.GetShape(query_vec)[0] - if query_segment_id is None: - query_segment_id = tf.zeros( - query_batch_size * num_heads, dtype=source_padding.dtype) - else: - query_segment_id_repl = tf.tile( - tf.expand_dims(query_segment_id, 1), [1, num_heads]) - query_segment_id = tf.reshape(query_segment_id_repl, [-1]) - - if per_step_source_padding is None: - zero = tf.constant(0.0, dtype=query_vec.dtype) - per_step_source_padding = tf.fill([query_batch_size, source_seq_len], - zero) - per_step_source_padding = py_utils.HasShape( - per_step_source_padding, [query_batch_size, source_seq_len]) - per_step_source_padding = tf.reshape( - tf.tile(per_step_source_padding, [1, num_heads]), [-1, source_seq_len]) - attention_state = _RecursiveReshape(attention_state, - [batch_size * num_heads, -1]) - if isinstance(attention_state, py_utils.NestedMap): - if 'emit_probs' in attention_state: - inner_state = attention_state - elif 'inner' in attention_state: - inner_state = attention_state.inner - else: - inner_state = attention_state - ctx_vec, prob, new_inner_state = self.atten.ComputeContextVectorWithSource( - theta.atten, packed_src, query_vec_projected, inner_state, - per_step_source_padding, query_segment_id) - ctx_vec = tf.reshape(ctx_vec, [batch_size, -1]) - if p.enable_ctx_post_proj: - if atten_idx is None: - assert p.num_post_proj == 1, ( - 'atten_idx is None, this means there is no need to select ' - 'different post projections, and p.num_post_proj is supposed to be ' - '1. However you set p.num_post_proj=%s .' % p.num_post_proj) - ctx_vec = fns.qbatchmatmul( - ctx_vec, - fns.qweight(theta.ctx_post_proj), - qt='ctx_post_proj_matmul') - if p.use_bias: - ctx_vec = fns.qadd( - ctx_vec, - fns.qweight(theta.ctx_post_proj_b), - qt='ctx_post_proj_add') - else: - assert p.num_post_proj > 1, ( - 'atten_idx is not None, this means there are multiple post ' - 'projections, and p.num_post_proj is supposed to be > 1. However ' - 'you set p.num_post_proj=%s .' % p.num_post_proj) - bs_range = [tf.range(batch_size)] - select = tf.transpose(tf.concat([bs_range, [atten_idx]], axis=0)) - # => [batch, dim, num_langs] - ctx_vec = tf.einsum('ab,bcd->acd', ctx_vec, theta.ctx_post_proj) - if p.use_bias: - ctx_vec += tf.expand_dims(theta.ctx_post_proj_b, 0) - # => [batch, num_langs, dim] - ctx_vec = tf.transpose(ctx_vec, [0, 2, 1]) - # => [batch, dim] - ctx_vec = tf.gather_nd(ctx_vec, select) - ctx_vec = self.ProcessProjectionVec(theta, ctx_vec, 'ctx_post') - - # explicitly name this tensor for potential future reference - multi_headed_atten_prob = tf.reshape( - prob, [batch_size, num_heads, -1], name='multi_headed_atten_prob') - # TODO(laurenzo): Use a better named range function (we want to represent - # 0..1 probs). - prob = self.QRSoftmax(tf.reduce_mean(multi_headed_atten_prob, 1)) - if isinstance(attention_state, py_utils.NestedMap): - att_state = attention_state - if 'emit_probs' in attention_state: - att_state = new_inner_state - elif 'inner' in attention_state: - att_state.inner = new_inner_state - else: - att_state = new_inner_state - if p.attention_head_prob_index >= 0: - selected_prob_head = multi_headed_atten_prob[:, p. - attention_head_prob_index, :] - att_state.selected_attention_head_probs = selected_prob_head - att_state = _RecursiveReshape(att_state, [batch_size, -1]) - return ctx_vec, prob, att_state - - @py_utils.NameScopeDecorator( - 'MultiHeadedAttention/ComputeContextVectorWithAttenProbs') - def ComputeContextVectorWithAttenProbs(self, theta, packed_context, - atten_probs): - """Computes the context vector given the attention probailities. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - packed_context: Concated source contexts with shape [ batch_size * - num_heads, time, context_dim // num_heads]. - atten_probs: The attention probability vector: [batch_size * num_heads, - time]. - - Returns: - The attention context vector shaped [target_batch, source_dim]. - If p.enable_ctx_post_proj is false, source_dim = context_dim, - otherwise, source_dim = p.ctx_post_proj_dim. - """ - p = self.params - num_heads = p.num_attention_heads - # packed_context: [batch_size * num_head, num_style, - # hidden_dim / num_head] - # inp: [batch_size * num_head, num_style] - packed_context = py_utils.with_dependencies([ - py_utils.assert_shape_match([py_utils.GetShape(packed_context)[0]], - [py_utils.GetShape(atten_probs)[0]]) - ], packed_context) - b_size = py_utils.GetShape(packed_context)[0] // num_heads - ctx_vec = tf.reshape( - tf.matmul(tf.expand_dims(atten_probs, 1), packed_context), [b_size, -1]) - if p.enable_ctx_post_proj: - ctx_vec_proj = tf.matmul(ctx_vec, theta.ctx_post_proj) - ctx_vec_proj += theta.ctx_post_proj_b - ctx_vec_proj = self.ProcessProjectionVec(theta, ctx_vec_proj, 'ctx_post') - else: - ctx_vec_proj = ctx_vec - return ctx_vec_proj, ctx_vec - - def PackCachedSource(self, cached_src): - p = self.params - concated_source_vecs = cached_src.source_vecs - concated_source_contexts = cached_src.source_contexts - source_padding = cached_src.source_padding - source_segment_id = cached_src.source_segment_id - batch_size = py_utils.GetShape(concated_source_vecs)[1] - src_seq_len = py_utils.GetShape(concated_source_vecs)[0] - num_heads = p.num_attention_heads - packed_src = py_utils.NestedMap() - packed_src.source_vecs = tf.reshape( - concated_source_vecs, [src_seq_len, batch_size * num_heads, -1]) - # TODO(yonghui): Rewrite the following with just one transpose. - packed_src.source_contexts = tf.transpose( - tf.reshape(concated_source_contexts, - [src_seq_len, batch_size * num_heads, -1]), [1, 0, 2]) - if source_padding is not None: - packed_src.source_padding = tf.reshape( - source_padding, [src_seq_len, batch_size * num_heads]) - else: - packed_src.source_padding = tf.zeros( - [src_seq_len, batch_size * num_heads], dtype=py_utils.FPropDtype(p)) - if source_segment_id is None: - packed_src.source_segment_id = tf.zeros( - [src_seq_len, batch_size * num_heads], - dtype=packed_src.source_padding.dtype) - else: - packed_src.source_segment_id = tf.reshape( - source_segment_id, [src_seq_len, batch_size * num_heads]) - return packed_src - - @py_utils.NameScopeDecorator( - 'MultiHeadedAttention/ComputeContextVectorWithCachedSource') - def ComputeContextVectorWithCachedSource(self, - theta, - cached_src, - query_vec, - attention_state=None, - per_step_source_padding=None, - query_segment_id=None): - """Same as the ComputeContextVectorWithSource api above, except values ... - - in source_vecs, source_contexts and source_padding are ordered differently. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - cached_src: A `.NestedMap` object returned by ExtendSourcePacked. - query_vec: a tensor of shape [target_batch, query_dim]. - attention_state: previous attention state. It is not used in - AdditiveAttention, and is simply passed through. - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should be of shape [target_batch_size, source_length]. - query_segment_id: a tensor of shape [target_batch]. - - Returns: - A tuple of 3 tensors: - - - The attention context vector: [target_batch, source_dim] - - The attention probability vector: [target_batch, time] - - The new attention mechanism state: possibly nested tuple of tensors with - dimensions [target_batch....] - """ - return self.ComputeContextVectorWithSource( - theta, self.PackCachedSource(cached_src), query_vec, attention_state, - per_step_source_padding, query_segment_id) - - -class LocationSensitiveAttention(BaseAttentionLayer): - """An attention that also takes into account previously attended locations. - - See section 2.2 of this paper for a description of this technique: - http://papers.nips.cc/paper/5847-attention-based-models-for-speech-recognition.pdf - """ - - @classmethod - def Params(cls): - """Params for this LocationSensitiveAttention class.""" - p = super().Params() - p.Define('source_dim', 0, 'Number of source nodes.') - p.Define('location_filter_size', 0, - 'Location filter size, should be an odd number e.g. 31.') - p.Define('location_num_filters', 0, 'Number of location filters, e.g. 32.') - p.Define('query_dim', 0, 'Number of query nodes.') - p.Define('hidden_dim', 0, 'Number of hidden nodes.') - p.Define( - 'same_batch_size', False, - 'True iff the source and target sequence has the same batch size.') - p.Define( - 'location_features', ['PREV_PROBS'], - 'List signals to run the convolutions on. Possible options are: ' - 'PREV_PROBS, CUMULATIVE_PROBS.') - - # Often the attention context output needs to be concated - # with tensors from another layer. This allows them to share - # quantization parameters. By convention, all attention layers - # need to include their context output vectors in this domain. - p.qdomain.Define('atten_context', None, - 'Quantization domain for attention context.') - - # Fill in reasonable default for params init - p.params_init = py_utils.WeightInit.GaussianSqrtDim() - return p - - def __init__(self, params): - """Constructs an LocationSensitiveAttention object.""" - super().__init__(params) - p = self.params - self._is_quantized = p.qdomain.default is not None - assert not p.packed_input, ('Packed input is not supported yet for ' - 'LocationSensitiveAttention.') - - if p.atten_dropout_prob != 0: - raise NotImplementedError('dropout is not supported') - - def AttenLogits(inputs): - """Generates logits.""" - fns = self.fns - - def CollapseOutDim(x): - return tf.reshape(x, [-1, tf.shape(x)[-1]]) - - # => [sl, sb, hd] - location_feats = tf.transpose(inputs.location_feats, [2, 0, 1]) - location_hidden = fns.qmatmul( - CollapseOutDim(location_feats), inputs.location_var, qt='logits_mul') - - sl = py_utils.GetShape(location_feats)[0] - tb = py_utils.GetShape(location_feats)[1] - hd = py_utils.GetShape(inputs.location_var)[1] - location_hidden = tf.reshape(location_hidden, [sl, tb, hd]) - sb = py_utils.GetShape(inputs.query_vec_reshaped)[2] - bs_mult = py_utils.GetShape(inputs.query_vec_reshaped)[1] - location_hidden = tf.reshape(location_hidden, [sl, bs_mult, sb, hd]) - - # Shape of summed is [sl, tb/sb, sb, hidden_dim]. - summed = fns.qadd( - inputs.concated_source_vecs, - inputs.query_vec_reshaped, - qt='logits_add') - summed = fns.qadd(summed, location_hidden, qt='logits_bias') - summed = fns.qtanh(summed) - # logits is of shape [sl * tb/sb * sb, 1]. Computes dot product - # between v with every rows in 'summed'. Then we reshape the - # result to be of shape [sl, tb/sb, sb]. - logits = fns.qmatmul( - tf.reshape(summed, [-1, p.hidden_dim]), - tf.reshape(inputs.hidden_v, [p.hidden_dim, 1]), - qt='logits') - logits = tf.reshape(logits, py_utils.GetShape(summed)[:3]) - return logits - - def AttenLogitsSameBatchSize(inputs): - """Generates logits. - - Optimized code path for when the target and the source have the same batch - size. - - Args: - inputs: a NestedMap containing: - - concated_source_vecs: Tensor of shape [sl, batch, dim] - - query_vec_transformed: Tensor of shape [batch, dim] - - hidden_v: Tensor of shape [dim] - - location_feats: Tensor of shape [batch, location_feature_dim, sl] - - location_var: Tensor of shape [location_feature_dim, dim] - - Returns: - logits in the shape [sl, batch_size]. - """ - - def CollapseOutDim(x): - return tf.reshape(x, [-1, tf.shape(x)[-1]]) - - fns = self.fns - # => [sl, sb, hd] - location_feats = tf.transpose(inputs.location_feats, [2, 0, 1]) - location_hidden = fns.qmatmul( - CollapseOutDim(location_feats), inputs.location_var, qt='logits_mul') - sl = tf.shape(location_feats)[0] - tb = tf.shape(location_feats)[1] - hd = tf.shape(inputs.location_var)[1] - location_hidden = tf.reshape(location_hidden, [sl, tb, hd]) - - # Shape of summed is [sl, sb, hidden_dim]. - summed = fns.qadd( - inputs.concated_source_vecs, - tf.expand_dims(inputs.query_vec_transformed, 0), - qt='logits_add') - - summed = fns.qadd(summed, location_hidden, qt='logits_bias') - summed = fns.qtanh(summed) - - # logits is of shape [sl * sb, 1]. Computes dot product - # between v with every rows in 'summed'. Then we reshape the - # result to be of shape [sl, tb]. - logits = fns.qmatmul( - tf.reshape(summed, [-1, p.hidden_dim]), - tf.reshape(inputs.hidden_v, [p.hidden_dim, 1]), - qt='logits') - logits = tf.reshape(logits, py_utils.GetShape(summed)[:2]) - return logits - - def Atten(hidden_var, query_var, source_padding, concated_source_vecs, - concated_source_contexts, query_vec, attention_state, - location_filter_var, location_var, per_step_source_padding): - """Computes the attention context vector.""" - p = self.params - # attention_state shape [batch, len(p.location_features), slen] - # it contains previous and accumulated attention probabilites. - attention_state = py_utils.HasShape(attention_state, - [-1, len(p.location_features), -1]) - - fns = self.fns - location_feats = self._ApplyConv(attention_state, location_filter_var) - - # concated_source_vecs is of shape [sl, sb, dims] - # concated_source_contexts is of shape [sb, sl, context_dim] - # query_vec is of shape [tb, dims] - sb = py_utils.GetShape(concated_source_vecs)[1] - tb = py_utils.GetShape(query_vec)[0] - multiplier = tb // sb - # concated_source_vecs is reshaped to [sl, 1, sb, hidden_dims] - concated_source_vecs = tf.expand_dims(concated_source_vecs, 1) - query_vec_transformed = fns.qmatmul( - query_vec, query_var, qt='atten_matmul') - # query_vec is reshaped to [1, tb/sb, sb, hidden_dims]. - query_vec_reshaped = tf.reshape(query_vec_transformed, - [1, multiplier, sb, p.hidden_dim]) - # logits is of shape [sl, tb/sb, sb] - logits = _ConditionalCallDefun( - self._is_quantized, AttenLogits, - py_utils.NestedMap( - concated_source_vecs=concated_source_vecs, - query_vec_reshaped=query_vec_reshaped, - hidden_v=hidden_var, - location_feats=location_feats, - location_var=location_var)) - # Take out the padding states. - # _source_padding is of shape [sl, sb]. - # reshaped to [sl, 1, sb]. - source_padding = tf.expand_dims(source_padding, 1) - per_step_source_padding = tf.reshape( - tf.transpose(per_step_source_padding), [-1, multiplier, sb]) - - source_padding = self.QRPadding( - tf.add(source_padding, per_step_source_padding)) - - # Reshape logits to a matrix of shape [tb, sl] and takes the - # softmax to compute the probabilities. - logits = tf.transpose(tf.reshape(logits, [-1, tb])) - source_padding = tf.transpose(tf.reshape(source_padding, [-1, tb])) - probs = self._PaddedSoftmax( - logits, source_padding, narrow_to_asym_bit_depth=True) - # Reshape probs to be of shape [tb/sb, sb, sl]. - probs_reshaped = tf.reshape(probs, [multiplier, sb, -1]) - # Transpose probs to be of shape [sb, tb/sb, sl] - probs_reshaped = tf.transpose(probs_reshaped, [1, 0, 2]) - # [sb, tb/sb, sl] * [sb, sl, context_dim] = [sb, tb/sb, context_dim] - summed = fns.qbatchmatmul( - tf.cast(probs_reshaped, concated_source_contexts.dtype), - concated_source_contexts, - qt='atten_context') - # summed is of shape [tb/sb, sb, context_dim] - summed = tf.transpose(summed, [1, 0, 2]) - return tf.reshape(summed, [tb, -1]), probs - - def AttenSameBatchSize(hidden_var, query_var, source_padding, - concated_source_vecs, concated_source_contexts, - query_vec, attention_state, location_filter_var, - location_var, per_step_source_padding): - """Computes the attention context vector. - - Optimized code path for when source and target have the same batch size. - """ - del per_step_source_padding - p = self.params - # attention_state shape [batch, len(p.location_features), slen] - # it contains previous and accumulated attention probabilites. - attention_state = py_utils.HasShape(attention_state, - [-1, len(p.location_features), -1]) - - fns = self.fns - location_feats = self._ApplyConv(attention_state, location_filter_var) - query_vec_transformed = fns.qmatmul( - query_vec, query_var, qt='atten_matmul') - # logits is of shape [sl, sb] - logits = _ConditionalCallDefun( - not self._is_quantized, AttenLogitsSameBatchSize, - py_utils.NestedMap( - concated_source_vecs=concated_source_vecs, - query_vec_transformed=query_vec_transformed, - hidden_v=hidden_var, - location_feats=location_feats, - location_var=location_var)) - # => [sl, tb] - logits.set_shape(source_padding.shape) - # Reshape logits to a matrix of shape [tb, sl] and takes the - # softmax to compute the probabilities. - logits = tf.transpose(logits) - source_padding = tf.transpose(source_padding) - probs = self._PaddedSoftmax( - logits, source_padding, narrow_to_asym_bit_depth=True) - summed = fns.qbatchmatmul( - tf.cast(tf.expand_dims(probs, 1), concated_source_contexts.dtype), - concated_source_contexts, - qt='atten_context') - return tf.squeeze(summed, 1), probs - - if p.same_batch_size: - self._ctx_vec = AttenSameBatchSize - else: - self._ctx_vec = Atten - - def EncodeSource(src_w, vecs, ctxs): - fns = self.fns - time, batch = py_utils.GetShape(vecs, 2) - ctxs = py_utils.HasShape(ctxs, [time, batch, -1]) - transformed_vecs = tf.reshape( - fns.qmatmul( - tf.reshape(vecs, [-1, p.source_dim]), src_w, qt='encode_matmul'), - [time, batch, -1]) - transposed_ctxs = tf.transpose(ctxs, [1, 0, 2]) - return transformed_vecs, transposed_ctxs - - self._encode_source = EncodeSource - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - pc = py_utils.WeightParams( - shape=[p.source_dim, p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['LocationSensitiveAttention_vars']) - self.CreateVariable('source_var', pc, self.AddGlobalVN) - - pc = py_utils.WeightParams( - shape=[p.query_dim, p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['LocationSensitiveAttention_vars']) - self.CreateVariable('query_var', pc, self.AddGlobalVN) - - pc = py_utils.WeightParams( - shape=[p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['LocationSensitiveAttention_vars']) - self.CreateVariable('hidden_var', pc, self.AddGlobalVN) - - assert p.location_filter_size % 2 == 1 - assert p.location_num_filters > 0 - - location_filter_shape = [ - p.location_filter_size, - len(p.location_features), p.location_num_filters - ] - # TODO(yonghui): Don't hard code how params are initialized. - location_filter_pc = py_utils.WeightParams( - shape=location_filter_shape, - init=py_utils.WeightInit.Uniform(0.05), - dtype=p.dtype, - collections=['LocationSensitiveAttention_vars']) - self.CreateVariable('location_filter_var', location_filter_pc, - self.AddGlobalVN) - location_var_shape = [p.location_num_filters, p.hidden_dim] - location_pc = py_utils.WeightParams( - shape=location_var_shape, - init=py_utils.WeightInit.Uniform(0.05), - dtype=p.dtype, - collections=['LocationSensitiveAttention_vars']) - self.CreateVariable('location_var', location_pc, self.AddGlobalVN) - - self.TrackQTensor('atten_conv') - self.TrackQTensor('atten_context', domain='atten_context') - self.TrackQTensor( - 'atten_matmul', - 'logits_add', - 'encode_matmul', - 'logits_mul', - 'logits_bias', - domain='fullyconnected') - - def _ApplyConv(self, attention_state, location_filter_var): - """Applies the convolution on attention state.""" - p = self.params - fns = self.fns - attention_state_f32 = attention_state - location_filter_var_f32 = location_filter_var - if p.dtype != tf.float32: - attention_state_f32 = tf.cast(attention_state, tf.float32) - location_filter_var_f32 = tf.cast(location_filter_var, tf.float32) - data_format = 'NCW' - if not py_utils.use_xla(): - # NCW format is not supported on CPU. - attention_state_f32 = tf.transpose(attention_state_f32, [0, 2, 1]) - data_format = 'NWC' - location_feats = fns.qconv1d( - attention_state_f32, - location_filter_var_f32, - 1, - 'SAME', - data_format=data_format, - qt='atten_conv') - if not py_utils.use_xla(): - location_feats = tf.transpose(location_feats, [0, 2, 1]) - if p.dtype != tf.float32: - location_feats = tf.cast(location_feats, p.dtype) - # [sb, hd, sl] - return location_feats - - def PackSource(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - with tf.name_scope(self.params.name): - if source_segment_id is None: - source_segment_id = tf.zeros_like(source_padding) - (concated_source_vecs, concated_source_contexts) = ( - self._encode_source( - self.QWeight(theta.source_var), source_vecs, source_contexts)) - return py_utils.NestedMap( - # [time, batch_size, hidden_dim]. - source_vecs=concated_source_vecs, - # [batch_size, time, context_dim]. - # Note the mismatch between `source_vecs` and `source_contexts`. In - # `source_vecs`, time is the first dim, while it is the second dim in - # `source_contexts`. - source_contexts=concated_source_contexts, - # [time, batch_size]. - source_padding=source_padding, - # [time, batch_size]. - source_segment_id=source_segment_id) - - def ZeroAttentionState(self, source_length, decoder_batch_size): - p = self.params - dtype = p.dtype.real_dtype - num_features = len(p.location_features) - with tf.name_scope(p.name): - state = tf.concat([ - tf.ones([decoder_batch_size, num_features, 1], dtype=dtype), - tf.zeros([decoder_batch_size, num_features, source_length - 1], - dtype=dtype) - ], 2) - - state = self.QRSoftmax(state) - return state - - def ComputeContextVectorWithSource(self, - theta, - packed_src, - query_vec, - attention_state=None, - per_step_source_padding=None, - query_segment_id=None): - """Computes the context vector given the current query output. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - packed_src: A `.NestedMap` object returned by PackSource or - InitForSourcePacked. - query_vec: a tensor of shape [batch_size, query_dim]. - attention_state: If `params().location_features == ['PREV_PROBS', - 'CUMULATIVE_PROBS']`, then `attention_state` is a tensor of shape - [batch_size, 2, src_len]. - - - attention_state[:, 0, :] contains previous attention probabilities. - - attention_state[:, 1, :] contains a sum over previous timesteps of - attention probabilities. - - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should be of shape [target_batch_size, source_length]. - query_segment_id: Query segment id with shape [batch_size]. - - Note: concated_source_vecs are the vectors that are used to compute the - attention score between the query_vec and each concated_source_vec. The - concated_source_contexts are the vectors that compose the result. The - attention context vector is computed as a weighted average of the - concated_source_contexts, using the scores that were computed using - concated_source_vecs. - - Returns: - A tuple of 3 elements. - - - The attention context vector: [batch_size, context_dim] - - The attention probability vector: [batch_size, time] - - The new attention mechanism state: possibly nested tuple of tensors with - dimensions [target_batch, ...] - """ - del query_segment_id - p = self.params - concated_source_vecs = packed_src.source_vecs - concated_source_contexts = packed_src.source_contexts - source_padding = packed_src.source_padding - if p.same_batch_size: - assert per_step_source_padding is None - query_batch_size = py_utils.GetShape(query_vec)[0] - source_length = py_utils.GetShape(source_padding)[0] - if per_step_source_padding is None: - zero = tf.constant(0.0, dtype=query_vec.dtype) - per_step_source_padding = tf.fill([query_batch_size, source_length], zero) - per_step_source_padding = py_utils.HasShape( - per_step_source_padding, [query_batch_size, source_length]) - - hidden = py_utils.AddPerStepVN(p, theta.hidden_var) - query = py_utils.AddPerStepVN(p, theta.query_var) - location_filter = py_utils.AddPerStepVN(p, theta.location_filter_var) - location = py_utils.AddPerStepVN(p, theta.location_var) - - ctx_vec, prob = self._ctx_vec(hidden, query, source_padding, - concated_source_vecs, - concated_source_contexts, query_vec, - attention_state, location_filter, location, - per_step_source_padding) - - new_feats = {'PREV_PROBS': prob} - if 'CUMULATIVE_PROBS' in p.location_features: - # Quantization must match the _PaddedSoftmax method. - cum_prob_index = p.location_features.index('CUMULATIVE_PROBS') - new_feats['CUMULATIVE_PROBS'] = self.QRSoftmax( - tf.add(prob, attention_state[:, cum_prob_index, :]), - narrow_to_asym_bit_depth=True) - new_attention_state = tf.stack([new_feats[f] for f in p.location_features], - axis=1) - return ctx_vec, prob, new_attention_state - - -def MergeSourcePaddingWithPerStepSourcePadding(source_padding, - per_step_source_padding, tb): - """Merges source padding with per-step source padding. - - Args: - source_padding: [sl, sb]. - per_step_source_padding: [tb, sl]. - tb: target batch size. - - Returns: - A tensor of shape [tb, sl]. - """ - # source_padding is of shape [sl, sb]. - sl = py_utils.GetShape(source_padding)[0] - sb = py_utils.GetShape(source_padding)[1] - - if per_step_source_padding is None: - zero = tf.constant(0.0, dtype=source_padding.dtype) - per_step_source_padding = tf.fill([tb, sl], zero) - per_step_source_padding = py_utils.HasShape(per_step_source_padding, [tb, sl]) - - # Transpose and reshape source_padding to [1, sb, sl]. - source_padding = tf.expand_dims(tf.transpose(source_padding), 0) - # Merge source_padding and per_step_source_padding. - source_padding = tf.maximum(source_padding, - tf.reshape(per_step_source_padding, [-1, sb, sl])) - return tf.reshape(source_padding, [tb, -1]) - - -class MonotonicAttention(BaseAttentionLayer): - """An attention mechanism which enforces monotonic alignments. - - This layer implements the monotonic attention mechanism described in - Online and Linear-Time Attention by Enforcing Mononotonic Alignments - (https://arxiv.org/abs/1704.00784). It is used in exactly the same way as - AdditiveAttention, but both the attention distribution and the energy function - are different. - - Rather than using a softmax, this mechanism feeds the attention energy into a - (hard or soft) sigmoid and treats the output as Bernoulli probabilities - representing the probability of attending to a given entry in the input - sequence, processed from left-to-right. Based on this interpretation, the - resulting distribution over input sequence entries is computed with a dynamic - program. The intended use is to train with soft sigmoids according to the - expected output (setting param hard_sigmoid=False), then use hard sigmoids at - test time to allow for online and linear-time decoding. To encourge the train - and test-time behavior to be similar, noise can optionally be added to the - sigmoid activations during training (param pre_sigmoid_noise). For the energy - function, rather than computing:: - - E = dot(v, tanh(dot(W, query) + dot(W, encoder_states))) - - it computes:: - - E = dot(g*v/||v||, tanh(dot(W, query) + dot(W, encoder_states) + b)) + r - - where g and r are scalars and b is a vector, and ||v|| is the L2 norm of v. - instead. These modifications address the fact that the sigmoids in the - monotonic attention mechanism are sensitive to offset and a bit harder to - train compared to the softmax function. It can be helpful to initialize the - energy bias scalar r to a negative value (param hidden_bias_init). - """ - - @classmethod - def Params(cls): - """Params for this MonotonicAttention class.""" - p = super().Params() - p.Define('source_dim', 0, 'Number of source nodes.') - p.Define('query_dim', 0, 'Number of query nodes.') - p.Define('hidden_dim', 0, 'Number of hidden nodes.') - p.Define('pre_sigmoid_noise', 0, 'Standard deviation of pre-sigmoid noise.') - p.Define('hidden_bias_init', -1, 'Initial value of hidden bias.') - p.Define('hard_sigmoid', False, 'Whether to use a hard sigmoid.') - # Fill in reasonable default for params init - p.params_init = py_utils.WeightInit.GaussianSqrtDim() - return p - - def __init__(self, params): - """Constructs an MonotonicAttention object.""" - super().__init__(params) - p = self.params - assert not p.packed_input, ('Packed input not supported for Monotonic ' - 'Attention.') - if p.atten_dropout_prob != 0: - raise NotImplementedError('dropout is not supported') - - # When running eval, don't add pre-sigmoid noise, and use a hard sigmoid to - # match behavior of online decoding. - if self.do_eval: - p.pre_sigmoid_noise = 0. - p.hard_sigmoid = True - - def EncodeSource(src_w, vecs, ctxs): - time, batch = py_utils.GetShape(vecs, 2) - ctxs = py_utils.HasShape(ctxs, [time, batch, -1]) - transformed_vecs = tf.reshape( - py_utils.Matmul(tf.reshape(vecs, [-1, p.source_dim]), src_w), - [time, batch, -1]) - transposed_ctxs = tf.transpose(ctxs, [1, 0, 2]) - return transformed_vecs, transposed_ctxs - - self._encode_source = EncodeSource - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - # source is the weight matrix for the memory/encoder states - pc = py_utils.WeightParams( - shape=[p.source_dim, p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['MonotonicAttention_vars']) - self.CreateVariable('source_var', pc, self.AddGlobalVN) - - # query is the weight matrix for the query/decoder RNN state - pc = py_utils.WeightParams( - shape=[p.query_dim, p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['MonotonicAttention_vars']) - self.CreateVariable('query_var', pc, self.AddGlobalVN) - - # hidden is the pre-softmax vector which converts from tanh to scalar - pc = py_utils.WeightParams( - shape=[p.hidden_dim], - init=p.params_init, - dtype=p.dtype, - collections=['MonotonicAttention_vars']) - self.CreateVariable('hidden_var', pc, self.AddGlobalVN) - - # energy_bias is the bias vector which appears inside of tanh - # Initialize the bias vector to all zeros - pc = py_utils.WeightParams( - shape=[p.hidden_dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=['MonotonicAttention_vars']) - self.CreateVariable('energy_bias_var', pc) - - # hidden_scale is the weight normalization scale for hidden - # Initialize so that the initial scale is 1/sqrt(hidden_dim) - pc = py_utils.WeightParams( - shape=[], - init=py_utils.WeightInit.Constant(1 / np.sqrt(p.hidden_dim)), - dtype=p.dtype, - collections=['MonotonicAttention_vars']) - self.CreateVariable('hidden_scale_var', pc) - - # hidden_bias is the bias scalar applied before the sigmoid - # Use the hidden_bias_init hyperparam to set the initial value - pc = py_utils.WeightParams( - shape=[], - init=py_utils.WeightInit.Constant(p.hidden_bias_init), - dtype=p.dtype, - collections=['MonotonicAttention_vars']) - self.CreateVariable('hidden_bias_var', pc) - - def PackSource(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - with tf.name_scope(self.params.name): - if source_segment_id is None: - source_segment_id = tf.zeros_like(source_padding) - (concated_source_vecs, concated_source_contexts) = ( - self._encode_source(theta.source_var, source_vecs, source_contexts)) - return py_utils.NestedMap( - # [time, batch_size, hidden_dim]. - source_vecs=concated_source_vecs, - # [batch_size, time, context_dim]. - # Note the mismatch between `source_vecs` and `source_contexts`. In - # `source_vecs`, time is the first dim, while it is the second dim in - # `source_contexts`. - source_contexts=concated_source_contexts, - # [time, batch_size]. - source_padding=source_padding, - # [time, batch_size]. - source_segment_id=source_segment_id) - - def ZeroAttentionState(self, source_length, decoder_batch_size): - p = self.params - dtype = p.dtype - with tf.name_scope(p.name): - # Set initial previous attention to [1, 0, ... 0] to avoid special-casing - emit_probs = tf.one_hot( - tf.zeros((decoder_batch_size,), dtype=tf.int32), - source_length, - dtype=dtype) - return py_utils.NestedMap(emit_probs=emit_probs) - - def ComputeProbabilities(self, theta, concated_source_vecs, - merged_source_padding, query_vec, attention_state): - """Computes probabilities of emissions.""" - - # concated_source_contexts is of shape [sb, sl, context_dim] - # query_vec is of shape [tb, dims] - sb = tf.shape(concated_source_vecs)[1] - tb = tf.shape(query_vec)[0] - multiplier = tb // sb - - p = self.params - - def AttenLogits(inputs): - """Computes logits from source, query, and variables. - - Args: - inputs: a NestedMap containing: - - concated_source_vecs: [sl, sb, hidden_dims]. - - query_vec: [tb, query_dim]. - - query_v: [query_dim, hidden_dim] - - energy_b: [hidden_dim]. - - hidden_v: [hidden_dim]. - - hidden_g: []. - - hidden_b: []. - - Returns: - logits shaped [tb, sl]. - """ - # Apply query matrix to query. Becomes [tb, hidden_dim]. - query_vec_transformed = py_utils.Matmul( - inputs.query_vec, inputs.query_v, name='query_transformation') - # query_vec is reshaped to [1, tb/sb, sb, hidden_dim]. - query_vec_reshaped = tf.reshape(query_vec_transformed, - [1, multiplier, sb, p.hidden_dim]) - - # [sl, 1, sb, hidden_dim]. - concated_source_vecs = tf.expand_dims(inputs.concated_source_vecs, 1) - energy_b = tf.reshape(inputs.energy_b, [1, 1, 1, -1]) - # Shape of summed is [sl, tb/sb, sb, hidden_dim]. - summed = tf.tanh(concated_source_vecs + query_vec_reshaped + energy_b) - hidden_v = inputs.hidden_g * tf.nn.l2_normalize(inputs.hidden_v, axis=0) - # logits is of shape [sl * tb/sb * sb, 1]. Computes dot product - # between v with every rows in 'summed'. Then we reshape the - # result to be of shape [sl, tb/sb, sb]. - # - # Another equivalent way is to do: - # logits = tf.reduce_sum(summed * - # tf.reshape(v, [1, 1, 1, hidden_dim]), 3) - logits = py_utils.Matmul( - tf.reshape(summed, [-1, p.hidden_dim]), - tf.reshape(hidden_v, [p.hidden_dim, 1])) - logits += inputs.hidden_b - # [tb, sl]. - logits = tf.transpose(tf.reshape(logits, [-1, tb]), [1, 0]) - return logits - - with tf.name_scope('logits'): - logits = py_utils.CallDefun( - AttenLogits, - py_utils.NestedMap( - concated_source_vecs=concated_source_vecs, - query_vec=query_vec, - query_v=theta.query_var, - energy_b=theta.energy_bias_var, - hidden_v=theta.hidden_var, - hidden_g=theta.hidden_scale_var, - hidden_b=theta.hidden_bias_var)) - - previous_attention = attention_state.emit_probs - with tf.name_scope('prob'): - if self.params.hard_sigmoid: - # If using a hard sigmoid, just compare against 0 - p_choose_i = tf.cast(tf.greater(logits, 0), logits.dtype) - # Never choose padded values. - p_choose_i = tf.where(merged_source_padding > 0.0, - tf.zeros_like(p_choose_i), p_choose_i) - # Compute probability distribution assuming hard probabilities - probs = MonotonicAttentionProb(p_choose_i, previous_attention, 'hard') - else: - # Compute pre-sigmoid noise. - activation_noise = tf.random.stateless_normal( - py_utils.GetShape(logits), - py_utils.GenerateStepSeedPair(p, theta.global_step), - dtype=logits.dtype) - # Compute sigmoid probabilities. - p_choose_i = tf.nn.sigmoid(logits + self.params.pre_sigmoid_noise * - activation_noise) - # Never choose padded values. - p_choose_i = tf.where(merged_source_padding > 0, - tf.zeros_like(p_choose_i), p_choose_i) - # Compute attention distribution - probs = MonotonicAttentionProb(p_choose_i, previous_attention, - 'parallel') - - # [tb, sl]. - return probs, py_utils.NestedMap(emit_probs=probs) - - def ComputeContextVectorWithSource(self, - theta, - packed_src, - query_vec, - attention_state, - per_step_source_padding=None, - query_segment_id=None): - """Computes the context vector given the current query output. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - packed_src: A `.NestedMap` object returned by PackSource or - InitForSourcePacked. - query_vec: a tensor of shape [batch_size, query_dim]. - attention_state: The attention probs computed at the previous timestep. - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should be of shape [target_batch_size, source_length]. - query_segment_id: a tensor of shape [batch_size]. - Note: concated_source_vecs are the vectors that are used to compute the - attention score between the query_vec and each concated_source_vec. The - concated_source_contexts are the vectors that compose the result. The - attention context vector is computed as a weighted average of the - concated_source_contexts, using the scores that were computed using - concated_source_vecs. - - Returns: - A tuple of 3 elements. - - - The attention context vector: [batch_size, context_dim] - - The attention probability vector: [batch_size, time] - - The attention probability vector: (again, to be interpreted as state). - """ - del query_segment_id - concated_source_vecs = packed_src.source_vecs - concated_source_contexts = packed_src.source_contexts - source_padding = packed_src.source_padding - sb = tf.shape(concated_source_vecs)[1] - tb = tf.shape(query_vec)[0] - multiplier = tb // sb - merged_source_padding = MergeSourcePaddingWithPerStepSourcePadding( - source_padding, per_step_source_padding, tb) - - probs, new_state = self.ComputeProbabilities(theta, concated_source_vecs, - merged_source_padding, - query_vec, attention_state) - - with tf.name_scope('sum'): - # Reshape probs to be of shape [tb/sb, sb, sl] - probs_reshaped = tf.reshape(probs, [multiplier, sb, -1]) - # Transpose probs to be of shape [sb, tb/sb, sl] - probs_reshaped = tf.transpose(probs_reshaped, [1, 0, 2]) - # Batched matmul - # [sb, tb/sb, sl] * [sb, sl, context_dim] = [sb, tb/sb, context_dim] - summed = tf.matmul(probs_reshaped, concated_source_contexts) - # summed is of shape [tb/sb, sb, context_dim] - summed = tf.transpose(summed, [1, 0, 2]) - ctx_vec = tf.reshape(summed, [tb, -1]) - - return ctx_vec, probs, new_state - - -class GmmMonotonicAttention(BaseAttentionLayer): - """A GMM-based monotonic attention module. - - Based on "Generating Sequences With Recurrent Neural Networks" by Alex Graves. - Eq [46-51] in https://arxiv.org/abs/1308.0850. - """ - - @classmethod - def Params(cls): - """Params for this MonotonicAttention class.""" - p = super().Params() - p.Define('source_dim', 0, 'Number of source nodes.') - p.Define('query_dim', 0, 'Number of query nodes.') - p.Define('hidden_dim', 128, - 'Number of hidden units for the MLP that predicts GMM params.') - p.Define('max_offset', -1, - 'Max offset to move attention pointer, Enabled only when > 0.') - p.Define('num_mixtures', 5, 'Number of location GMM components.') - p.Define( - 'normalize_probs', False, - 'Whether to normalize probabilities computed by GMM. Otherwise, ' - 'the attention weights (i.e. probabilities) may not add up to ' - '1.0.') - - # TODO(ngyuzh): find a good initialize for both TTS and ASR. Consider split - # the layer if it's very sensitive to the initialization - p.params_init = py_utils.WeightInit.Xavier(0.1) - return p - - def __init__(self, params): - """Constructs a GMM-based monotonic attention module.""" - super().__init__(params) - p = self.params - if p.atten_dropout_prob != 0: - raise NotImplementedError('dropout is not supported.') - - # TODO(ngyuzh): Compare Sigmoid and other activation functions. - ff_params = layers.FeedForwardNet.Params().Set( - name=p.name, - input_dim=p.query_dim, - hidden_layer_dims=[p.hidden_dim, p.num_mixtures * 3], - activation=['SIGMOID', 'NONE'], - params_init=p.params_init.Copy()) - self.CreateChild('GMM', ff_params) - - def ComputeProbs(encoder_positions, priors, means, variances): - """Computes the location GMM probabilities at all encoder positions. - - This function assumes that the first 2 dimensions of `priors`, `means`, - `variances`, and the return value: - `multiplier (target_batch / source_batch)` and `source_batch` are - transposed, and `encoder_positions` has only non-one dimensions. - - Args: - encoder_positions: [source_batch, source_length] - priors: [multiplier, source_batch, num_mixtures] - means: [multiplier, source_batch, num_mixtures] - variances: [multiplier, source_batch, num_mixtures] - - Returns: - Probabilities shaped [multiplier, source_batch, source_length]. - """ - # [multiplier, source_batch, 1, num_mixtures] - priors = tf.expand_dims(priors, 2) - means = tf.expand_dims(means, 2) - variances = tf.expand_dims(variances, 2) - epsilon = 1e-8 - - # [source_batch, source_length, 1] - encoder_positions = tf.expand_dims(encoder_positions, 2) - - # [multiplier, source_batch, source_length, num_mixtures] - probs = ((priors * tf.math.rsqrt(2 * np.pi * variances + epsilon)) * - tf.exp(-(encoder_positions - means)**2 / - (2 * variances + epsilon))) - - # [multiplier, source_batch, source_length] - return tf.reduce_sum(probs, axis=3) - - def Atten(source_padding, concated_source_vecs, concated_source_contexts, - query_vec, priors, means, variances, encoder_positions, - per_step_source_padding): - """Computes the attention context vector. - - Args: - source_padding: [source_length, source_batch] - concated_source_vecs: [source_length, source_batch, hidden_dim] - concated_source_contexts: [source_batch, source_length, context_dim] - query_vec: [target_batch, query_dim] - priors: [target_batch, num_mixtures] - means: [target_batch, num_mixtures] - variances: [target_batch, num_mixtures] - encoder_positions: [source_batch, source_length] - per_step_source_padding: [target_batch, source_length] - - Returns: - Tuple(context vector, atten probs): - - - context vector: [target_batch, context_dim] - - attention probabilities: [target_batch, source_length] - """ - # Note: shape [target_batch] can be converted to - # [multiplier, source_batch], not [source_batch, multiplier]. - p = self.params - source_batch = tf.shape(concated_source_vecs)[1] - target_batch = tf.shape(query_vec)[0] - multiplier = target_batch // source_batch - - # [multiplier, source_batch, num_mixtures] - priors = tf.reshape(priors, [multiplier, source_batch, p.num_mixtures]) - means = tf.reshape(means, [multiplier, source_batch, p.num_mixtures]) - variances = tf.reshape(variances, - [multiplier, source_batch, p.num_mixtures]) - - # [multiplier, source_batch, source_length] - probs = ComputeProbs(encoder_positions, priors, means, variances) - - # [source_batch, source_length] - source_padding = tf.transpose(source_padding) - - # [multiplier, source_batch, source_length] - per_step_source_padding = tf.reshape(per_step_source_padding, - [multiplier, source_batch, -1]) - source_padding += per_step_source_padding - source_padding = tf.minimum(source_padding, 1.0) - - # [multiplier, source_batch, source_length] - probs *= (1.0 - source_padding) - if p.normalize_probs: - probs /= tf.maximum(tf.reduce_sum(probs, axis=2, keepdims=True), 1e-12) - - # [source_batch, multiplier, source_length] - probs_transposed = tf.transpose(probs, [1, 0, 2]) - - # Matmul: - # [source_batch, multiplier, source_length] - # @ [source_batch, source_length, context_dim] - # -> [source_batch, multiplier, context_dim] - context_vector_transposed = tf.matmul(probs_transposed, - concated_source_contexts) - - # [multiplier, source_batch, context_dim] - context_vector = tf.transpose(context_vector_transposed, [1, 0, 2]) - - # [target_batch, context_dim], [target_batch, source_length] - return (tf.reshape(context_vector, [target_batch, -1]), - tf.reshape(probs, [target_batch, -1])) - - self._ctx_vec = Atten - - def EncodeSource(vecs, ctxs): - # TODO(ngyuzh): combine with content-base attention. - time, batch = py_utils.GetShape(vecs, 2) - ctxs = py_utils.HasShape(ctxs, [time, batch, -1]) - transposed_ctxs = tf.transpose(ctxs, [1, 0, 2]) - return vecs, transposed_ctxs - - self._encode_source = EncodeSource - - def PackSource(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - with tf.name_scope(self.params.name): - if source_segment_id is None: - source_segment_id = tf.zeros_like(source_padding) - (concated_source_vecs, concated_source_contexts) = ( - self._encode_source(source_vecs, source_contexts)) - return py_utils.NestedMap( - # [source_length, source_batch, hidden_dim]. - source_vecs=concated_source_vecs, - # [source_batch, source_length, context_dim]. - # Note the mismatch between `source_vecs` and `source_contexts`. In - # `source_vecs`, `source_length` is the first dim, while it is the - # second dim in `source_contexts`. - source_contexts=concated_source_contexts, - # [source_length, source_batch]. - source_padding=source_padding, - # [source_length, source_batch]. - source_segment_id=source_segment_id) - - def ZeroAttentionState(self, source_length, decoder_batch_size): - p = self.params - - # [target_batch, num_mixtures] - position = tf.zeros([decoder_batch_size, p.num_mixtures], dtype=p.dtype) - position_offsets = tf.zeros([decoder_batch_size, p.num_mixtures], - dtype=p.dtype) - variances = tf.ones([decoder_batch_size, p.num_mixtures], dtype=p.dtype) - priors = tf.zeros([decoder_batch_size, p.num_mixtures], dtype=p.dtype) - - # [target_batch, num_mixtures, 4] - return tf.stack([position, position_offsets, variances, priors], axis=2) - - def ComputeContextVectorWithSource(self, - theta, - packed_src, - query_vec, - attention_state, - per_step_source_padding=None, - query_segment_id=None): - """Computes the context vector given the current query output. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - packed_src: A `.NestedMap` object returned by PackSource or - InitForSourcePacked. - query_vec: a tensor of shape [target_batch, query_dim]. - attention_state: previous attention state, a tensor of shape - [target_batch, num_mixtures, 4]. - - attention_state[:, :, 0] contains previous location - - attention_state[:, :, 1] contains previous offset. - - attention_state[:, :, 2] contains previous variance. - - attention_state[:, :, 3] contains previous prior. - per_step_source_padding: Source sequence padding to apply at this step. If - not None, it should be of shape [target_batch, source_length]. - query_segment_id: a tensor of shape [target_batch]. - Note: concated_source_vecs are the vectors that are used to compute the - attention score between the query_vec and each concated_source_vec. The - concated_source_contexts are the vectors that compose the result. The - attention context vector is computed as a weighted average of the - concated_source_contexts, using the scores that were computed using - concated_source_vecs. - - Returns: - A tuple of 3 elements. - - - The attention context vector: [target_batch, context_dim] - - The attention probability vector: [target_batch, source_length] - - The new attention state vector: [target_batch, num_mixtures, 4] - """ - del query_segment_id - p = self.params - concated_source_vecs = packed_src.source_vecs - concated_source_contexts = packed_src.source_contexts - source_padding = packed_src.source_padding - - target_batch = tf.shape(query_vec)[0] - source_length = tf.shape(source_padding)[0] - source_batch = tf.shape(source_padding)[1] - - # [target_batch, source_length] - if per_step_source_padding is None: - per_step_source_padding = tf.zeros([target_batch, source_length], - dtype=query_vec.dtype) - per_step_source_padding = py_utils.HasShape(per_step_source_padding, - [target_batch, source_length]) - - # [target_batch, num_mixtures * 3] - out = self.GMM.FProp(theta.GMM, query_vec) - - # [target_batch, num_mixtures] - priors_logits, position_offset_logits, log_variances = tf.split( - out, 3, axis=1, name='GMM') - - log_variances = tf.minimum(log_variances, layers.LOG_SCALE_CLAMP_BOUND) - variances = tf.exp(log_variances) - - priors = tf.nn.softmax(priors_logits) - - if p.max_offset > 0: - position_offset = tf.nn.sigmoid(position_offset_logits) - position_offset *= p.max_offset - else: - position_offset = tf.exp(position_offset_logits) - - new_position = attention_state[:, :, 0] + position_offset - - # Tile and reshape encoder_positions to [source_batch, source_length] - # so that it can be evaluated by locations GMMs in a vectorized way. - encoder_positions = tf.expand_dims( - tf.cast(tf.range(source_length), tf.float32), 0) - encoder_positions = tf.tile(encoder_positions, [source_batch, 1]) - - # [target_batch, context_dim], [target_batch, source_length] - ctx_vec, prob = self._ctx_vec(source_padding, concated_source_vecs, - concated_source_contexts, query_vec, priors, - new_position, variances, encoder_positions, - per_step_source_padding) - - # [target_batch, num_mixtures, 4] - new_atten_states = tf.stack( - [new_position, position_offset, variances, priors], axis=2) - - return ctx_vec, prob, new_atten_states - - -class MergerLayer(base_layer.BaseLayer): - """Merges a list of input tensors with various options into a single tensor. - - Implements a merger/combiner operator given a list of tensors. The merger - operator outputs a single tensor with the following options (merger_op): - - - atten: Applies attention over the set of input tensors given query vector. - - mean: Takes the mean of input tensors. - - concat: Concatenates the input tensors over the last dimension. - - sum: Sum up all the input tensors. - - weighted_sum: Use learnt weights to combine input tensors. - - gated_avg: Learnt input dependent gates are used to average tensors. - - This class is expected to be called by multi-source/multi-column models. - """ - - @classmethod - def Params(cls): - """Params for this MergerLayer class.""" - p = super().Params() - p.Define('merger_op', None, 'How to merge input tensors.') - p.Define('source_dim', 0, 'Number of source nodes.') - p.Define('query_dim', 0, 'Number of query nodes.') - p.Define('hidden_dim', 0, 'Number of hidden nodes.') - p.Define('attention_tpl', AdditiveAttention.Params(), - 'Attention used by the merger layer when merger_op is atten.') - p.Define( - 'pre_proj_input_dims', None, - 'If set, should be a list of depths for the tensors to be merged.' - ' Setting this will result in a pre-projection to source_dim' - ' before the merger.') - p.Define( - 'pre_proj_output_dims', None, - 'Should be a list of depths which the input tensors specified in ' - 'pre_proj_input_dims need to be projected to. Should match the length ' - 'of pre_proj_input_dims.') - p.Define( - 'proj_tpl', - layers.ProjectionLayer.Params().Set( - batch_norm=False, weight_norm=True, has_bias=True), - 'Configs template for the projection layer.') - p.Define('gated_avg_tpl', layers.GatedAverageLayer.Params(), - 'Configs template for the gated average layer.') - p.Define('num_sources', 0, 'If merger_op=weighted_sum, then must specify ' - 'num of sources.') - p.Define('post_proj', None, - 'Post projection for the merged context vector.') - return p - - # Merging operation keys supported by this layer. - MERGER_OPS = ['mean', 'atten', 'concat', 'sum', 'weighted_sum', 'gated_avg'] - - def __init__(self, params): - super().__init__(params) - p = self.params - if not p.name: - raise ValueError('Layer must have a specified name!') - if p.merger_op not in set(self.MERGER_OPS): - raise ValueError('Merger op must be one of: ', self.MERGER_OPS) - - if p.merger_op == 'atten': - atten_params = p.attention_tpl.Copy() - atten_params.source_dim = p.source_dim - atten_params.query_dim = p.query_dim - atten_params.hidden_dim = p.hidden_dim - atten_params.dtype = p.dtype - if atten_params.params_init is None: - atten_params.params_init = py_utils.WeightInit.Gaussian( - 1. / math.sqrt(atten_params.source_dim + atten_params.query_dim), - seed=p.random_seed) - self.CreateChild('atten', atten_params) - - if p.pre_proj_input_dims: - if not p.pre_proj_output_dims: - raise ValueError('Output dims should be specified for projection.') - if len(p.pre_proj_input_dims) != len(p.pre_proj_output_dims): - raise ValueError( - 'Output dims should be the same length as input dims. ' - 'Expected: %s obtained: %s' % - (len(p.pre_proj_input_dims), len(p.pre_proj_output_dims))) - pre_proj_params = [] - for i, (pre_proj_input_dim, pre_proj_output_dim) in enumerate( - zip(p.pre_proj_input_dims, p.pre_proj_output_dims)): - proj_p = p.proj_tpl.Copy() - proj_p.name = 'merger_pre_proj_%d' % i - proj_p.input_dim = pre_proj_input_dim - proj_p.output_dim = pre_proj_output_dim - pre_proj_params.append(proj_p) - self.CreateChildren('pre_proj', pre_proj_params) - - if p.merger_op == 'gated_avg': - assert p.num_sources > 0, ('For merger_op=gated_avg, must specify ' - 'num_sources > 0.') - params = p.gated_avg_tpl.Copy() - params.name = 'g_avg_merger' - params.num_nodes = p.source_dim - params.num_inputs = p.num_sources - self.CreateChild('gated_average', params) - - if p.post_proj: - self.CreateChild('post_proj', p.post_proj) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - if p.merger_op == 'weighted_sum': - assert p.num_sources > 0, ('For merger_op=weighted_sum, must specify ' - 'num_sources > 0.') - params_init = py_utils.WeightInit.Constant(1.0 / p.num_sources) - # Weights to be learned. - pw = py_utils.WeightParams( - shape=[p.num_sources], - init=params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('sum_weight', pw) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - if 'atten' in self.children: - self.atten.InstantiateVariables() - if 'gated_average' in self.children: - self.gated_average.InstantiateVariables() - if 'pre_proj' in self.children: - for proj in self.pre_proj: - proj.InstantiateVariables() - super()._CreateChildrenVariables() - - def FProp(self, theta, inputs, query_vec=None): - """Combines the list of input tensors into a single tensor. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: A list of tensors of shape [..., hidden_dim] or [..., - [pre_proj_input_dims[i]]] if pre_proj_input_dims is specified. - query_vec: A tensor of shape [..., hidden_dim]. - - Returns: - A tensor of the same shape with input tensors. - - Raises: - ValueError: p.merger_op is not defined. - """ - p = self.params - n_sources = len(inputs) - - if p.pre_proj_input_dims and len(p.pre_proj_input_dims) != n_sources: - raise ValueError('pre_proj_input_dims must be specified for each input.') - - if n_sources == 1: - return inputs[0] - - # Pre-projection operation. - if p.pre_proj_input_dims: - for i in range(n_sources): - inputs[i] = self.pre_proj[i].FProp(theta.pre_proj[i], inputs[i]) - - tensor_pairs = list(zip(inputs[:-1], inputs[1:])) - if p.merger_op == 'mean': - # Simply take the mean, all dims must match. - with tf.control_dependencies([ - py_utils.assert_shape_match(tf.shape(t1), tf.shape(t2)) - for t1, t2 in tensor_pairs - ]): - output = tf.add_n(inputs) / n_sources - - elif p.merger_op == 'sum': - # Sum up all sources, all dims must match. - with tf.control_dependencies([ - py_utils.assert_shape_match(tf.shape(t1), tf.shape(t2)) - for t1, t2 in tensor_pairs - ]): - output = tf.add_n(inputs) - - elif p.merger_op == 'weighted_sum': - # Weighted sum of all sources, all dims must match. - # For weighted_sum, assume input is a list of rank 3 tensors - inputs = tf.stack(inputs) - inputs = py_utils.HasRank(inputs, 4) - - with tf.control_dependencies([ - py_utils.assert_shape_match(tf.shape(t1), tf.shape(t2)) - for t1, t2 in tensor_pairs - ]): - w = tf.expand_dims( - tf.expand_dims(tf.expand_dims(theta.sum_weight, 1), 1), 1) - w = tf.tile( - w, - [1, - tf.shape(inputs)[1], - tf.shape(inputs)[2], - tf.shape(inputs)[3]]) - output = tf.reduce_sum(inputs * w, axis=0) - - elif p.merger_op == 'atten': - # Apply attention over the concatenated tensor, all dims must match. - with tf.control_dependencies([ - py_utils.assert_shape_match(tf.shape(t1), tf.shape(t2)) - for t1, t2 in tensor_pairs - ]): - inputs = tf.stack(inputs, axis=0) - batch_size = tf.shape(inputs)[1] - paddings = tf.zeros([n_sources, batch_size], dtype=inputs.dtype) - self.atten.InitForSourcePacked(theta.atten, inputs, inputs, paddings) - output, _, _ = self.atten.ComputeContextVector( - theta.atten, tf.reshape(query_vec, [-1, p.query_dim])) - - elif p.merger_op == 'concat': - # Concatenate over the last dim, all dims but last must match. - with tf.control_dependencies([ - py_utils.assert_equal(tf.shape(t1)[:-1], - tf.shape(t2)[:-1]) for t1, t2 in tensor_pairs - ]): - output = tf.concat(inputs, axis=-1) - - elif p.merger_op == 'gated_avg': - output = self.gated_average.FProp(theta.gated_average, inputs) - - else: - raise ValueError('Unrecognized merge op!') - - if p.post_proj: - output = self.post_proj.FProp(theta.post_proj, output) - - return output - - -class MultiSourceAttention(BaseAttentionLayer): - """Attention with multiple source sub-attentions. - - It attends to multiple sources and uses one query as input to generates a - combined attention context. The dimension of the combined context vector is a - sum of all source context vectors. Each source attention has its separate - params and is associated with a source key. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('source_atten_tpls', None, - 'A list of (source_key, attention_param) ' - 'pairs.') - p.Define('source_dim', 0, 'Default source dimension.') - p.Define( - 'query_dim', 0, 'Number of query nodes. Child attention params ' - 'must have query_dim less or euqal than 0 or equal to this value.') - p.Define( - 'primary_source_key', 'source_0', 'Key for the primary source ' - 'whose attention probabilities will be used as an output.') - p.Define( - 'atten_merger_tpl', - MergerLayer.Params().Set( - params_init=py_utils.WeightInit.Uniform(0.04), merger_op='sum'), - 'Params to specify how to merge source attention vectors.') - return p - - def __init__(self, params): - """Constructs an MultiSourceAttention object.""" - super().__init__(params) - p = self.params - for source_key, atten_p in p.source_atten_tpls: - child_p = atten_p.Copy() - if child_p.query_dim <= 0: - child_p.query_dim = p.query_dim - else: - assert child_p.query_dim == p.query_dim - if child_p.source_dim <= 0: - child_p.source_dim = p.source_dim - self.CreateChild('atten_%s' % source_key, child_p) - - # Initialize source context vector merging layer. - merger_p = p.atten_merger_tpl.Copy() - merger_p.name = 'atten_merger' - merger_p.source_dim = p.source_dim - merger_p.query_dim = p.query_dim - self.CreateChild('atten_merger', merger_p) - - def PackSource(self, - theta, - source_vecs, - source_contexts, - source_padding, - source_segment_id=None): - p = self.params - with tf.name_scope(self.params.name): - packed_src = py_utils.NestedMap() - for source_key, _ in p.source_atten_tpls: - packed_src[source_key] = ( - self.children['atten_%s' % source_key].InitForSourcePacked( - theta['atten_%s' % source_key], source_vecs[source_key], - source_contexts[source_key], source_padding[source_key], - source_segment_id[source_key] if source_segment_id else None)) - return packed_src - - def ZeroAttentionState(self, source_seq_length, decoder_batch_size): - p = self.params - with tf.name_scope(self.params.name): - return py_utils.NestedMap({ - source_key: getattr(self, 'atten_%s' % source_key).ZeroAttentionState( - source_seq_length[source_key], decoder_batch_size) - for source_key, _ in p.source_atten_tpls - }) - - def ComputeContextVectorWithSource(self, - theta, - packed_src, - query_vec, - attention_state=None, - per_step_source_padding=None, - query_segment_id=None): - p = self.params - assert per_step_source_padding is None - with tf.name_scope(self.params.name): - result_map = py_utils.NestedMap() - for source_key, _ in p.source_atten_tpls: - result_map[source_key] = ( - self.children['atten_%s' % - source_key].ComputeContextVectorWithSource( - theta.get('atten_%s' % source_key), - packed_src[source_key], query_vec, - attention_state[source_key] - if attention_state else None, - per_step_source_padding, query_segment_id)) - return self._CombineContext(theta, result_map, query_vec) - - def _CombineContext(self, theta, context_map, query_vec): - ctxs = context_map.Flatten() - combined_context = ( - self.atten_merger.FProp(theta.atten_merger, [ctx for ctx, _, _ in ctxs], - query_vec)) - return ( - combined_context, - # Return atten_probs of the primary source. - # TODO(huk): Maybe return a NestedMap. - context_map[self.params.primary_source_key][1], - py_utils.NestedMap({ - src_key: context_map[src_key][2] - for src_key, _ in self.params.source_atten_tpls - })) diff --git a/lingvo/core/attention_test.py b/lingvo/core/attention_test.py deleted file mode 100644 index 40961894..00000000 --- a/lingvo/core/attention_test.py +++ /dev/null @@ -1,2377 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for attention.""" - -import math -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import attention -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import test_utils -import numpy as np - - -class AttentionTest(test_utils.TestCase, parameterized.TestCase): - """Test attention models.""" - - def _CheckStaticShapes(self, atten_vec, atten_prob, target_batch_size, - source_length, context_dim): - """Static shape must be set correctly for RNN beam search compatibility.""" - self.assertIsNotNone(atten_prob.shape.ndims) - self.assertEqual((target_batch_size, source_length), atten_prob.shape) - self.assertIsNotNone(atten_vec.shape.ndims) - self.assertEqual((target_batch_size, context_dim), atten_vec.shape) - - def _AdditiveAttentionInputs(self, packed_inputs=False, tgt_bs=6): - np.random.seed(12345) - source_vecs = tf.constant(np.random.rand(6, 3, 4), dtype=tf.float32) - source_contexts = tf.constant(np.random.rand(6, 3, 5), dtype=tf.float32) - source_padding = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]], - dtype=tf.float32)) - source_segment_id = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 1, 1], [0, 0, 1, 1, 2, 2], [0, 1, 1, 1, 1, 2]], - dtype=tf.float32)) - query_vec = tf.constant(np.random.rand(tgt_bs, 7), dtype=tf.float32) - qsi = [0, 1, 1, 1, 2, 2] - query_segment_id = tf.constant(qsi[:tgt_bs], dtype=tf.float32) - - params = attention.AdditiveAttention.Params() - params.name = 'atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = 4 - params.query_dim = 7 - params.hidden_dim = 7 - params.vn.global_vn = False - params.vn.per_step_vn = False - params.packed_input = packed_inputs - tensors = (source_vecs, source_contexts, source_padding, source_segment_id, - query_vec, query_segment_id) - return params, tensors - - def testAdditiveAttention(self): - with self.session(use_gpu=True): - params, tensors = self._AdditiveAttentionInputs() - source_vecs, source_contexts, source_padding, _, query_vec, _ = tensors - atten = attention.AdditiveAttention(params) - self.assertLen(atten.vars.Flatten(), 3) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - # TODO(yonghui): add atten.vars for the variables attention model - # declares. - atten_vars = tf.get_collection('AdditiveAttention_vars') - self.assertLen(atten_vars, 3) - - self.evaluate(tf.global_variables_initializer()) - - all_vars = tf.trainable_variables() - for v in all_vars: - print(v.eval()) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - - print(['additive attention prob_out', np.array_repr(prob_out)]) - print(['additive attention atten_vec_out', np.array_repr(atten_vec_out)]) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_prob_out = [ - [0.2555742 , 0.24073002, 0. , 0. , 0.25412574, - 0.24957004], - [0. , 0.25394136, 0.24764746, 0.25480017, 0. , - 0.24361098], - [0.25094295, 0.2499937 , 0. , 0.24308342, 0. , - 0.25597993], - [0.25559244, 0.24070661, 0. , 0. , 0.25412717, - 0.24957375], - [0. , 0.25393167, 0.24765188, 0.25481117, 0. , - 0.24360526], - [0.25113183, 0.24990553, 0. , 0.24246082, 0. , - 0.25650182]] - - expected_atten_vec_out = [ - [0.49745506, 0.63471669, 0.49220526, 0.5683012 , 0.42753702], - [0.51502365, 0.56183743, 0.37644109, 0.87425125, 0.46182787], - [0.57862502, 0.44246522, 0.36931852, 0.41002905, 0.14327194], - [0.49745634, 0.63471717, 0.49220967, 0.56829125, 0.4275257 ], - [0.51501834, 0.56183696, 0.37644821, 0.87425053, 0.46182543], - [0.57893348, 0.44248882, 0.36938411, 0.41006744, 0.14328158]] - # pylint: enable=bad-whitespace - # pyformat: enable - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def testAdditiveAttentionWithPackedInputs(self): - with self.session(use_gpu=True): - params, tensors = self._AdditiveAttentionInputs(packed_inputs=True) - (source_vecs, source_contexts, source_padding, source_segment_id, - query_vec, query_segment_id) = tensors - atten = attention.AdditiveAttention(params) - self.assertLen(atten.vars.Flatten(), 3) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding, source_segment_id) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec, query_segment_id=query_segment_id) - - # TODO(yonghui): add atten.vars for the variables attention model - # declares. - atten_vars = tf.get_collection('AdditiveAttention_vars') - self.assertLen(atten_vars, 3) - - self.evaluate(tf.global_variables_initializer()) - - all_vars = tf.trainable_variables() - for v in all_vars: - print(v.eval()) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - print(['packed additive attention prob_out', np.array_repr(prob_out)]) - print([ - 'packed additive attention atten_vec_out', - np.array_repr(atten_vec_out) - ]) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_prob_out = [ - [0.51495469, 0.48504525, 0 , 0 , 0 , - 0 ], - [0 , 0 , 0.49288213, 0.50711787, 0 , - 0 ], - [0. , 0.5070073 , 0. , 0.4929927 , 0. , - 0 ], - [0. , 0 , 0. , 0. , 0.50451994, - 0.49548006], - [0. , 0. , 0. , 0. , 0. , - 1 ], - [0. , 0. , 0. , 0. , 0. , - 1 ]] - - expected_atten_vec_out = [ - [0.35256192, 0.68348885, 0.41128731, 0.48906463, 0.50537711], - [0.45880911, 0.6068666 , 0.59867024, 0.82797134, 0.33504993], - [0.54934788, 0.50335771, 0.26117462, 0.32834488, 0.16398546], - [0.64022166, 0.58665955, 0.571935 , 0.64637613, 0.35084069], - [0.27927336, 0.06444023, 0.19862361, 0.93168277, 0.85441357], - [0.95473474, 0.05225335, 0.57947171, 0.48049626, 0.02170898]] - # pylint: enable=bad-whitespace - # pyformat: enable - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def testAdditiveAttentionDeterministicDropout(self): - with self.session(use_gpu=True): - params, tensors = self._AdditiveAttentionInputs() - source_vecs, source_contexts, source_padding, _, query_vec, _ = tensors - params.atten_dropout_prob = 0.5 - params.atten_dropout_deterministic = True - params.random_seed = 78924 - - atten = attention.AdditiveAttention(params) - self.assertLen(atten.vars.Flatten(), 3) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - atten_vars = tf.get_collection('AdditiveAttention_vars') - self.assertLen(atten_vars, 3) - - self.evaluate(tf.global_variables_initializer()) - - all_vars = tf.trainable_variables() - for v in all_vars: - print(v.eval()) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - - print('additive attention prob_out %r' % prob_out) - print('additive attention atten_vec_out %r' % atten_vec_out) - - expected_prob_out = [ - [0.51114839, 0.48146003, 0., 0., 0., 0.], - [0., 0.50788271, 0., 0.50960034, 0., 0.48722193], - [0., 0.49998739, 0., 0., 0., 0.51195991], - [0., 0.48141322, 0., 0., 0.50825435, 0.4991475], - [0., 0.50786334, 0.49530372, 0., 0., 0.48721054], - [0., 0.49981108, 0., 0., 0., 0.51300365], - ] - - expected_atten_vec_out = [ - [0.34995595, 0.67843682, 0.40824726, 0.4854497, 0.50164163], - [0.60576487, 0.80303985, 0.46480939, 1.3962903, 0.79863495], - [0.90196574, 0.47579059, 0.31802341, 0.34388986, 0.15836108], - [0.81517166, 0.90433061, 0.72681838, 1.02123988, 0.72982419], - [0.99326241, 0.83445895, 0.43935478, 1.26866817, 0.71197236], - [0.90281653, 0.47568679, 0.31862068, 0.34435683, 0.15833181], - ] - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def _testSameBatchSize(self, same_batch_size, packed_inputs=False): - with self.session(use_gpu=True, graph=tf.Graph()): - tf.random.set_seed(398847392) - params, tensors = self._AdditiveAttentionInputs(packed_inputs, tgt_bs=3) - source_vecs, source_contexts, source_padding, _, query_vec, _ = tensors - params.same_batch_size = same_batch_size - - atten = attention.AdditiveAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - self.assertLen(atten.vars.Flatten(), 3) - - self.evaluate(tf.global_variables_initializer()) - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - return atten_vec_out, prob_out - - def testAdditiveAttentionSameBatchSize(self): - vec0, prob0 = self._testSameBatchSize(False) - vec1, prob1 = self._testSameBatchSize(True) - self.assertAllClose(vec0, vec1) - self.assertAllClose(prob0, prob1) - - def testAdditiveAttentionSameBatchSizePackedInputs(self): - vec0, prob0 = self._testSameBatchSize(False, True) - vec1, prob1 = self._testSameBatchSize(True, True) - self.assertAllClose(vec0, vec1) - self.assertAllClose(prob0, prob1) - - def testAdditiveAttentionSmallerHiddenLayer(self): - with self.session(use_gpu=True): - np.random.seed(12345) - source_vecs = tf.constant(np.random.rand(6, 3, 4), dtype=tf.float32) - source_contexts = tf.constant(np.random.rand(6, 3, 5), dtype=tf.float32) - source_padding = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]], - dtype=tf.float32)) - query_vec = tf.constant(np.random.rand(6, 7), dtype=tf.float32) - - params = attention.AdditiveAttention.Params() - params.name = 'atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = 4 - params.query_dim = 7 - params.hidden_dim = 5 - params.vn.global_vn = False - params.vn.per_step_vn = False - - atten = attention.AdditiveAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - atten_vars = tf.get_collection('AdditiveAttention_vars') - self.assertLen(atten_vars, 3) - - self.evaluate(tf.global_variables_initializer()) - - all_vars = tf.trainable_variables() - for v in all_vars: - print(v.eval()) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - - print(['prob_out smaller hidden layer', np.array_repr(prob_out)]) - print( - ['atten_vec_out smaller hidden layer', - np.array_repr(atten_vec_out)]) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_prob_out = [ - [0.25242305, 0.24356601, 0. , 0. , 0.25346902, - 0.25054196], - [0. , 0.25230604, 0.24693871, 0.25406054, 0. , - 0.24669473], - [0.2501823 , 0.24922216, 0. , 0.24693316, 0. , - 0.25366238], - [0.25267059, 0.24300526, 0. , 0. , 0.25369659, - 0.25062758], - [0. , 0.25272119, 0.24642748, 0.25435579, 0. , - 0.24649554], - [0.25044653, 0.24924593, 0. , 0.24560687, 0. , - 0.25470066]] - - expected_atten_vec_out = [ - [0.49746257, 0.63428223, 0.4914251 , 0.57035601, 0.42964566], - [0.51383036, 0.55960417, 0.37601081, 0.87443453, 0.46342701], - [0.57660079, 0.44147781, 0.36953348, 0.41017395, 0.14293665], - [0.49755943, 0.63429612, 0.49157569, 0.57015073, 0.42933062], - [0.51371205, 0.55982226, 0.37590009, 0.87454152, 0.4633899 ], - [0.57732767, 0.44161472, 0.36958888, 0.41019297, 0.14298658]] - # pylint: enable=bad-whitespace - # pyformat: enable - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def testAdditiveAttentionFp16NoNaN(self): - with self.session(use_gpu=True): - np.random.seed(12345) - source_vecs = tf.constant(np.random.rand(6, 3, 4), dtype=tf.float16) - source_contexts = tf.constant(np.random.rand(6, 3, 5), dtype=tf.float16) - source_padding = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]], - dtype=tf.float16)) - query_vec = tf.constant(np.random.rand(6, 7), dtype=tf.float16) - - params = attention.AdditiveAttention.Params() - params.dtype = tf.float16 - params.name = 'atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = 4 - params.query_dim = 7 - params.hidden_dim = 7 - params.vn.global_vn = False - params.vn.per_step_vn = False - - atten = attention.AdditiveAttention(params) - self.assertLen(atten.vars.Flatten(), 3) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - # TODO(yonghui): add atten.vars for the variables attention model - # declares. - atten_vars = tf.get_collection('AdditiveAttention_vars') - self.assertLen(atten_vars, 3) - - self.evaluate(tf.global_variables_initializer()) - - all_vars = tf.trainable_variables() - for v in all_vars: - print(v.eval()) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - print(atten_vec_out.dtype) - print(prob_out.dtype) - self.assertTrue(np.all(np.isfinite(atten_vec_out))) - self.assertTrue(np.all(np.isfinite(prob_out))) - - def testAdditiveAttentionVN64bits(self): - with self.session(use_gpu=True): - np.random.seed(12345) - source_vecs = tf.constant(np.random.rand(5, 3, 4), dtype=tf.float64) - source_contexts = tf.constant(np.random.rand(5, 3, 5), dtype=tf.float64) - source_padding = tf.transpose( - tf.constant([[0, 0, 1, 1, 0], [1, 0, 0, 0, 1], [0, 0, 1, 0, 1]], - dtype=tf.float64)) - query_vec = tf.constant(np.random.rand(6, 7), dtype=tf.float64) - - params = attention.AdditiveAttention.Params() - params.name = 'atten' - params.dtype = tf.float64 - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = 4 - params.query_dim = 7 - params.hidden_dim = 5 - params.vn.global_vn = True - params.vn.per_step_vn = True - params.vn.scale = 1.0 - params.vn.seed = 54321 - - atten = attention.AdditiveAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - atten_vars = tf.get_collection('AdditiveAttention_vars') - self.assertLen(atten_vars, 3) - - self.evaluate(tf.global_variables_initializer()) - - all_vars = tf.trainable_variables() - for v in all_vars: - print(v.eval()) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - - print(['prob_out with vn:', np.array_repr(prob_out)]) - print(['atten_vec_out with vn:', np.array_repr(atten_vec_out)]) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_prob_out =[ - [ 0.43249266, 0.18638571, 0. , 0. , 0.38112162], - [ 0. , 0.32589137, 0.3505654 , 0.32354323, 0. ], - [ 0.26777833, 0.43991441, 0. , 0.29230726, 0. ], - [ 0.34583678, 0.32633085, 0. , 0. , 0.32783237], - [ 0. , 0.32734872, 0.34749836, 0.32515292, 0. ], - [ 0.33614176, 0.33607175, 0. , 0.32778649, 0. ] - ] - expected_atten_vec_out = [ - [ 0.56117282, 0.37872234, 0.42109472, 0.38981267, 0.45946841], - [ 0.85743407, 0.37325286, 0.66322611, 0.69286686, 0.141359 ], - [ 0.7377786 , 0.42298519, 0.39970782, 0.67703222, 0.4157012 ], - [ 0.51011499, 0.35817489, 0.47894328, 0.41259201, 0.54384056], - [ 0.85716326, 0.37340558, 0.66250852, 0.69187486, 0.14179651], - [ 0.78078121, 0.45646575, 0.4052385 , 0.68248276, 0.43502425] - ] - # pylint: enable=bad-whitespace - # pyformat: enable - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def _DotProductAttention(self, packed_inputs): - with self.session(use_gpu=True): - np.random.seed(12345) - # source_vecs_p, source_contexts_p, source_padding_p, query_vec_p are used - # for both TensorFlow and numpy computation. - source_vecs_p = [np.random.rand(3, 4) for _ in range(6)] - source_vecs = tf.stack( - [tf.constant(x, dtype=tf.float32) for x in source_vecs_p]) - source_contexts_p = [np.random.rand(3, 5) for _ in range(6)] - source_contexts = tf.stack( - [tf.constant(x, dtype=tf.float32) for x in source_contexts_p]) - source_padding_p = [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], - [0, 0, 1, 0, 1, 0]] - source_padding = tf.transpose( - tf.constant(source_padding_p, dtype=tf.float32)) - query_vec_p = np.random.rand(6, 4) - query_vec = tf.constant(query_vec_p, dtype=tf.float32) - query_segment_id_p = [0, 1, 1, 1, 2, 2] - source_segment_id_p = [[0, 0, 1, 1, 1, 1], [0, 0, 1, 1, 2, 2], - [0, 1, 1, 1, 1, 2]] - source_segment_id = None - query_segment_id = None - if packed_inputs: - source_segment_id = tf.transpose( - tf.constant(source_segment_id_p, dtype=tf.float32)) - query_segment_id = tf.constant(query_segment_id_p, dtype=tf.float32) - params = attention.DotProductAttention.Params() - params.name = 'dotproduct_atten' - params.source_dim = 4 - params.query_dim = 4 - params.hidden_dim = 4 - params.packed_input = packed_inputs - atten = attention.DotProductAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding, source_segment_id) - self.evaluate(tf.global_variables_initializer()) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec, query_segment_id=query_segment_id) - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - # Use numpy to perform the same computation to generate expected results. - source_vecs_p = np.array(source_vecs_p) - # Dot-product part. - expected_logit = np.array([ - np.dot(source_vecs_p[:, i % 3, :], query_vec_p[i, :]) - for i in range(6) - ]) / math.sqrt(4) - elexp = np.exp(expected_logit) - source_padding_p = np.array(source_padding_p) - elexp *= (1 - np.tile(source_padding_p, (2, 1))) - if packed_inputs: - # Manually constructed packed input mask. - mask = np.asarray([[1, 1, 0, 0, 0, 0], [0, 0, 1, 1, 0, 0], - [0, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 1], - [0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 1]]) - elexp *= mask - expected_prob_out = elexp / np.expand_dims(np.sum(elexp, axis=1), axis=1) - expanded_epo = np.expand_dims(expected_prob_out, axis=2) - source_contexts_p = np.array(source_contexts_p) - expected_atten_vec_out = np.array([ - np.sum( - source_contexts_p[:, i % 3, :] * expanded_epo[i, :, :], axis=0) - for i in range(6) - ]) - - print(['additive attention prob_out', np.array_repr(prob_out)]) - print(['additive attention atten_vec_out', np.array_repr(atten_vec_out)]) - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def testDotProductAttention(self): - self._DotProductAttention(packed_inputs=False) - - def testDotProductAttentionPackedInput(self): - self._DotProductAttention(packed_inputs=True) - - def _MultiHeadedAttentionInputs(self, source_dim=4, dtype=tf.float32): - np.random.seed(6348575) - # source_vecs_p, source_contexts_p, source_padding_p, query_vec_p are used - # for both TensorFlow and numpy computation. - source_vecs_p = [np.random.rand(3, source_dim) for _ in range(6)] - source_vecs = tf.stack([tf.constant(x, dtype=dtype) for x in source_vecs_p]) - source_contexts_p = [np.random.rand(3, 6) for _ in range(6)] - source_contexts = tf.stack( - [tf.constant(x, dtype=dtype) for x in source_contexts_p]) - source_padding_p = [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], - [0, 0, 1, 0, 1, 0]] - source_padding = tf.transpose(tf.constant(source_padding_p, dtype=dtype)) - query_vec_p = np.random.rand(6, 4) - query_vec = tf.constant(query_vec_p, dtype=dtype) - query_segment_id_p = [0, 1, 1, 1, 2, 2] - source_segment_id_p = [[0, 0, 1, 1, 1, 1], [0, 0, 1, 1, 2, 2], - [0, 1, 1, 1, 1, 2]] - source_segment_id = tf.transpose( - tf.constant(source_segment_id_p, dtype=dtype)) - query_segment_id = tf.constant(query_segment_id_p, dtype=dtype) - return (source_vecs, source_contexts, source_padding, source_padding_p, - query_vec, source_segment_id, query_segment_id) - - def testMultiHeadedAttentionDotProductWithFeedinProbs(self): - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, _, _, _, - _) = self._MultiHeadedAttentionInputs() - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False) - atten = params.Instantiate() - packed_src = atten.InitForSourcePacked(atten.theta, source_vecs, - source_contexts, source_padding) - self.evaluate(tf.global_variables_initializer()) - atten_probs = tf.constant([[1.0] + [0.0] * 5] * 3 * 2, dtype=tf.float32) - atten_vec_proj, atten_vec = atten.ComputeContextVectorWithAttenProbs( - atten.theta, packed_src.source_contexts, atten_probs) - atten_vec_proj, atten_vec, packed_context = self.evaluate( - [atten_vec_proj, atten_vec, packed_src.source_contexts]) - self.assertAllClose( - atten_vec, - np.reshape(np.transpose(packed_context, (0, 2, 1)), [3, 6, 6])[:, :, - 0]) - self.assertAllClose([2.5694468, 4.36386967, 3.24537992], - np.sum(atten_vec_proj, axis=1)) - - def _testMultiHeadedAttentionExtendCachedSourceVecsHelper( - self, additive_atten, dtype, fprop_dtype): - # source_batch:3, target_batch:6. Test n = 2 case. - use_gpu = (dtype == tf.float32 and fprop_dtype == tf.float32) - with self.session(use_gpu=use_gpu): - (source_vecs, source_contexts, source_padding, _, query_vec, - source_seg_id, - query_seg_id) = self._MultiHeadedAttentionInputs(dtype=fprop_dtype) - if additive_atten: - iap = attention.AdditiveAttention.Params() - iap.name = 'add_atten' - else: - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - dtype=dtype, - fprop_dtype=fprop_dtype, - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False, - packed_input=True) - atten = params.Instantiate() - theta = atten.theta - packed_src1 = atten.InitForSourcePacked(theta, source_vecs, - source_contexts, source_padding, - source_seg_id) - cached_src = py_utils.NestedMap( - source_vecs=tf.zeros([0, 3, 4], dtype=packed_src1.source_vecs.dtype), - source_contexts=tf.zeros([0, 3, 6], - dtype=packed_src1.source_contexts.dtype), - source_padding=tf.zeros([0, 3, 2], - dtype=packed_src1.source_padding.dtype), - source_segment_id=tf.zeros([0, 3, 2], - dtype=packed_src1.source_segment_id.dtype)) - for i in range(6): - cached_src = atten.ExtendSourcePacked(theta, source_vecs[i, :, :], - source_contexts[i, :, :], - source_padding[i, :], - source_seg_id[i, :], cached_src) - packed_src2 = atten.PackCachedSource(cached_src) - self.evaluate(tf.global_variables_initializer()) - - atten_vec_1, prob_1, _ = atten.ComputeContextVectorWithSource( - theta, packed_src1, query_vec, query_segment_id=query_seg_id) - atten_vec_2, prob_2, _ = atten.ComputeContextVectorWithCachedSource( - theta, cached_src, query_vec, query_segment_id=query_seg_id) - - packed_src1_v, packed_src2_v, cached_src_v = self.evaluate( - [packed_src1, packed_src2, cached_src]) - tf.logging.info('packed_src1=%s', packed_src1_v) - tf.logging.info('packed_src2=%s', packed_src2_v) - tf.logging.info('cached_src=%s', cached_src_v) - self.assertAllClose(packed_src1_v.source_vecs, packed_src2_v.source_vecs) - self.assertAllClose(packed_src1_v.source_contexts, - packed_src2_v.source_contexts) - self.assertAllClose(packed_src1_v.source_padding, - packed_src2_v.source_padding) - self.assertAllClose(packed_src1_v.source_segment_id, - packed_src2_v.source_segment_id) - atten_vec1_v, prob1_v, atten_vec2_v, prob2_v = self.evaluate( - [atten_vec_1, prob_1, atten_vec_2, prob_2]) - self.assertAllClose(prob1_v, prob2_v) - self.assertAllClose(atten_vec1_v, atten_vec2_v) - - def testMultiHeadedAttentionExtendCachedSourceVecsAdditiveFloat32(self): - self._testMultiHeadedAttentionExtendCachedSourceVecsHelper( - additive_atten=True, dtype=tf.float32, fprop_dtype=tf.float32) - - def testMultiHeadedAttentionExtendCachedSourceVecsAdditiveFloat32Float16( - self): - self._testMultiHeadedAttentionExtendCachedSourceVecsHelper( - additive_atten=True, dtype=tf.float32, fprop_dtype=tf.float16) - - def testMultiHeadedAttentionExtendCachedSourceVecsDotFloat32(self): - self._testMultiHeadedAttentionExtendCachedSourceVecsHelper( - additive_atten=False, dtype=tf.float32, fprop_dtype=tf.float32) - - def testMultiHeadedAttentionExtendCachedSourceVecsDotFloat32Float16(self): - self._testMultiHeadedAttentionExtendCachedSourceVecsHelper( - additive_atten=False, dtype=tf.float32, fprop_dtype=tf.float16) - - def _testMultiHeadedAttentionExtendCachedSourceVecsNoPaddingsHelper( - self, additive_attention=False): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, _, _, query_vec, _, - _) = self._MultiHeadedAttentionInputs() - source_padding = tf.zeros([6, 3]) - if additive_attention: - iap = attention.AdditiveAttention.Params() - iap.name = 'add_atten' - else: - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False) - atten = params.Instantiate() - packed_src1 = atten.InitForSourcePacked(atten.theta, source_vecs, - source_contexts, source_padding) - cached_src = py_utils.NestedMap( - source_vecs=tf.zeros([0, 3, 4], dtype=packed_src1.source_vecs.dtype), - source_contexts=tf.zeros([0, 3, 6], - dtype=packed_src1.source_contexts.dtype), - source_padding=None, - source_seg_id=None) - for i in range(6): - cached_src = atten.ExtendSourcePacked(atten.theta, source_vecs[i, :, :], - source_contexts[i, :, :], None, - None, cached_src) - packed_src2 = atten.PackCachedSource(cached_src) - self.evaluate(tf.global_variables_initializer()) - - atten_vec_1, prob_1, _ = atten.ComputeContextVectorWithSource( - atten.theta, packed_src1, query_vec) - atten_vec_2, prob_2, _ = atten.ComputeContextVectorWithCachedSource( - atten.theta, cached_src, query_vec) - - (source_vec1_v, source_context1_v, source_vec2_v, source_context2_v, - atten_vec1_v, prob1_v, atten_vec2_v, prob2_v) = self.evaluate([ - packed_src1.source_vecs, packed_src1.source_contexts, - packed_src2.source_vecs, packed_src2.source_contexts, atten_vec_1, - prob_1, atten_vec_2, prob_2 - ]) - self.assertAllClose(source_vec1_v, source_vec2_v) - self.assertAllClose(source_context1_v, source_context2_v) - self.assertAllClose(atten_vec1_v, atten_vec2_v) - self.assertAllClose(prob1_v, prob2_v) - - def testMultiHeadedDotAttentionExtendCachedSourceVecsNoPaddings(self): - self._testMultiHeadedAttentionExtendCachedSourceVecsNoPaddingsHelper(False) - - def testMultiHeadedAddAttentionExtendCachedSourceVecsNoPaddings(self): - self._testMultiHeadedAttentionExtendCachedSourceVecsNoPaddingsHelper(True) - - def testMultiHeadedAttentionDotProduct(self): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, source_padding_p, - query_vec, _, _) = self._MultiHeadedAttentionInputs() - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False) - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - self.evaluate(tf.global_variables_initializer()) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - self.assertAllClose([ - 2.84679317, 2.36924601, 3.54831171, 2.86487937, 2.3537426, 3.54308939 - ], np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - t_batch_size = 6 - s_batch_size = 3 - for i in range(t_batch_size): - # Test to make sure we didn't mess up indexing. - s_index = i % s_batch_size - atten.InitForSourcePacked(atten.theta, - source_vecs[:, s_index:s_index + 1, :], - source_contexts[:, s_index:s_index + 1, :], - source_padding[:, s_index:s_index + 1]) - atten_vec_i, prob_i, _ = atten.ComputeContextVector( - atten.theta, query_vec[i:i + 1]) - atten_vec_i_out, prob_i_out = self.evaluate([atten_vec_i, prob_i]) - self.assertAllClose(prob_i_out, prob_out[i:i + 1]) - self.assertAllClose(atten_vec_i_out, atten_vec_out[i:i + 1]) - padding_i = source_padding_p[s_index] - # Check to make sure prob exists only on valid timesteps. - self.assertEqual(0.0, np.sum(padding_i * prob_i_out)) - - def testMultiHeadedAttentionDotProductNoPerDimScaleNoBias(self): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, source_padding_p, - query_vec, _, _) = self._MultiHeadedAttentionInputs() - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False, - enable_per_dim_scale=False, - use_bias=False) - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - self.evaluate(tf.global_variables_initializer()) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - self.assertAllClose([ - 2.84679317, 2.36924601, 3.54831171, 2.86487937, 2.3537426, 3.54308939 - ], np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - t_batch_size = 6 - s_batch_size = 3 - for i in range(t_batch_size): - # Test to make sure we didn't mess up indexing. - s_index = i % s_batch_size - atten.InitForSourcePacked(atten.theta, - source_vecs[:, s_index:s_index + 1, :], - source_contexts[:, s_index:s_index + 1, :], - source_padding[:, s_index:s_index + 1]) - atten_vec_i, prob_i, _ = atten.ComputeContextVector( - atten.theta, query_vec[i:i + 1]) - atten_vec_i_out, prob_i_out = self.evaluate([atten_vec_i, prob_i]) - self.assertAllClose(prob_i_out, prob_out[i:i + 1]) - self.assertAllClose(atten_vec_i_out, atten_vec_out[i:i + 1]) - padding_i = source_padding_p[s_index] - # Check to make sure prob exists only on valid timesteps. - self.assertEqual(0.0, np.sum(padding_i * prob_i_out)) - - def testMultiHeadedAttentionDotProductPackedInput(self): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, source_padding_p, - query_vec, source_seg_id, - query_seg_id) = self._MultiHeadedAttentionInputs() - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False, - packed_input=True) - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding, source_seg_id) - self.evaluate(tf.global_variables_initializer()) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec, query_segment_id=query_seg_id) - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - self.assertAllClose( - [2.565648, 2.268182, 3.739031, 3.093884, 2.770367, 3.580353], - np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - t_batch_size = 6 - s_batch_size = 3 - for i in range(t_batch_size): - # Test to make sure we didn't mess up indexing. - s_index = i % s_batch_size - src_seg_id = source_seg_id[:, s_index:s_index + 1] - atten.InitForSourcePacked(atten.theta, - source_vecs[:, s_index:s_index + 1, :], - source_contexts[:, s_index:s_index + 1, :], - source_padding[:, s_index:s_index + 1], - src_seg_id) - qry_seg_id = query_seg_id[i:i + 1] - atten_vec_i, prob_i, _ = atten.ComputeContextVector( - atten.theta, query_vec[i:i + 1], query_segment_id=qry_seg_id) - atten_vec_i_out, prob_i_out = self.evaluate([atten_vec_i, prob_i]) - self.assertAllClose(prob_i_out, prob_out[i:i + 1]) - self.assertAllClose(atten_vec_i_out, atten_vec_out[i:i + 1]) - padding_i = source_padding_p[s_index] - # Check to make sure prob exists only on valid timesteps. - self.assertEqual(0.0, np.sum(padding_i * prob_i_out)) - - def testMultiHeadedAttentionDotProductDeterministicDropout(self): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - source_vecs, source_contexts, source_padding, _, query_vec, _, _ = ( - self._MultiHeadedAttentionInputs()) - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - atten_dropout_prob=0.5, - atten_dropout_deterministic=True, - random_seed=7249528, - use_source_vec_as_attention_value=False) - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - atten_state = atten.ZeroAttentionState(2, 6) - print('atten_state:', atten_state) - - atten_vec, atten_prob, atten_state = atten.ComputeContextVector( - atten.theta, query_vec, atten_state) - - self.evaluate(tf.global_variables_initializer()) - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - self.assertAllClose( - [4.152538, 3.423376, 4.782746, 4.238819, 1.785596, 3.239613], - np.sum(atten_vec_out, axis=1)) - - print('atten_vec_out %r' % atten_vec_out) - print('prob_out %r' % prob_out) - - expected_prob_out = [ - [0.235899, 0.26425028, 0., 0., 0.2534047, 0.24644604], - [0., 0.28664297, 0.2365877, 0.24659133, 0., 0.23017803], - [0.2340432, 0.26618454, 0., 0.257336, 0., 0.24243627], - [0.25304407, 0.25804248, 0., 0., 0.24191463, 0.24699883], - [0., 0.24431466, 0.25021935, 0.24959373, 0., 0.2558723], - [0.2792741, 0.21746796, 0., 0.25468093, 0., 0.24857706] - ] - expected_atten_vec_out = [ - [ - 0.87741864, 0.73626477, 0.90619636, 0.64786565, 0.6392238, - 0.3455683 - ], - [0.39735186, 0.6100546, 1.1262057, 0.43140602, 0.29049844, 0.5678591], - [ - 0.90514874, 1.1781378, 0.82465374, 0.64402366, 0.8315829, - 0.39919922 - ], - [ - 0.7801036, 0.92708683, 0.8839339, 0.70208144, 0.56675506, - 0.37885764 - ], - [0.1725478, 0.02231066, 0.06681296, 0.5807549, 0.25445306, 0.6887169], - [0.46632785, 0.9006454, 0.8054941, 0.4738411, 0.30304378, 0.2902605] - ] - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def testMultiHeadedAttentionMonotonic(self): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, source_padding_p, - query_vec, _, _) = self._MultiHeadedAttentionInputs() - iap = attention.MonotonicAttention.Params() - iap.name = 'mono_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False) - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - # [batch * 2 heads, time] - atten_init_state = self._attentionStateWithRandomEmitProbabilities( - atten, 12, 6) - print('atten_init_state', atten_init_state) - self.evaluate(tf.global_variables_initializer()) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec, atten_init_state) - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - self.assertAllClose( - [1.494033, 1.120422, 1.699309, 1.508609, 1.1329, 1.670303], - np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - t_batch_size = 6 - s_batch_size = 3 - for i in range(t_batch_size): - # Test to make sure we didn't mess up indexing. - s_index = i % s_batch_size - atten.InitForSourcePacked(atten.theta, - source_vecs[:, s_index:s_index + 1, :], - source_contexts[:, s_index:s_index + 1, :], - source_padding[:, s_index:s_index + 1]) - j = i * 2 - sliced_atten_state = py_utils.NestedMap( - emit_probs=atten_init_state.emit_probs[j:j + 2]) - atten_vec_i, prob_i, _ = atten.ComputeContextVector( - atten.theta, query_vec[i:i + 1], sliced_atten_state) - atten_vec_i_out, prob_i_out = self.evaluate([atten_vec_i, prob_i]) - self.assertAllClose(prob_i_out, prob_out[i:i + 1]) - self.assertAllClose(atten_vec_i_out, atten_vec_out[i:i + 1]) - padding_i = source_padding_p[s_index] - # Check to make sure prob exists only on valid timesteps. - self.assertEqual(0.0, np.sum(padding_i * prob_i_out)) - - def testMultiHeadedAttentionDotProductWithAllProj(self): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, source_padding_p, - query_vec, _, _) = self._MultiHeadedAttentionInputs() - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False, - enable_ctx_pre_proj=True, - enable_ctx_post_proj=True, - ctx_post_proj_dim=5, - context_dim=6) - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - self.evaluate(tf.global_variables_initializer()) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=params.ctx_post_proj_dim) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - self.assertAllClose([ - 1.356745, 0.65274805, 1.39460433, 1.34961343, 0.63025361, 1.41543126 - ], np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - t_batch_size = 6 - s_batch_size = 3 - for i in range(t_batch_size): - # Test to make sure we didn't mess up indexing. - s_index = i % s_batch_size - atten.InitForSourcePacked(atten.theta, - source_vecs[:, s_index:s_index + 1, :], - source_contexts[:, s_index:s_index + 1, :], - source_padding[:, s_index:s_index + 1]) - atten_vec_i, prob_i, _ = atten.ComputeContextVector( - atten.theta, query_vec[i:i + 1]) - atten_vec_i_out, prob_i_out = self.evaluate([atten_vec_i, prob_i]) - self.assertAllClose(prob_i_out, prob_out[i:i + 1]) - self.assertAllClose(atten_vec_i_out, atten_vec_out[i:i + 1]) - padding_i = source_padding_p[s_index] - # Check to make sure prob exists only on valid timesteps. - self.assertEqual(0.0, np.sum(padding_i * prob_i_out)) - - def testMultiHeadedAttentionDotProductMultiPostProj(self): - # Test for multiple attention post-projection. - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, _, query_vec, _, - _) = self._MultiHeadedAttentionInputs() - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False, - enable_ctx_pre_proj=True, - enable_ctx_post_proj=True, - ctx_post_proj_dim=6, - context_dim=6, - num_post_proj=2) - atten = params.Instantiate() - packed_src = atten.InitForSourcePacked(atten.theta, source_vecs, - source_contexts, source_padding) - # Duplicate atten_idx n=2 times. - atten_idx = tf.constant([0, 1, 1] * 2, dtype=tf.int32) - self.evaluate(tf.global_variables_initializer()) - atten_vec, atten_prob, _ = atten.ComputeContextVectorWithSource( - atten.theta, packed_src, query_vec, atten_idx=atten_idx) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - self.assertAllClose([ - 0.66697717, 0.52266854, 0.7827165, 0.65693897, 0.51808167, 0.82977116 - ], np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - - def _testMultiHeadedAttentionAdditiveHelper(self, - source_dim, - expected_vec, - packed_input=False): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, source_padding_p, - query_vec, source_seg_id, - query_seg_id) = self._MultiHeadedAttentionInputs(source_dim) - if not packed_input: - source_seg_id = None - query_seg_id = None - iap = attention.AdditiveAttention.Params() - iap.name = 'add_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=source_dim, - query_dim=4, - hidden_dim=4, - num_attention_heads=2, - inner_atten_params=iap, - use_source_vec_as_attention_value=False, - vn=py_utils.VariationalNoiseParams(0.0, False, False), - packed_input=packed_input) - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding, source_seg_id) - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec, query_segment_id=query_seg_id) - - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - self.evaluate(tf.global_variables_initializer()) - atten_vec_out, prob_out = self.evaluate([atten_vec, atten_prob]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - self.assertAllClose(expected_vec, np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - t_batch_size = 6 - s_batch_size = 3 - for i in range(t_batch_size): - # Test to make sure we didn't mess up indexing. - s_index = i % s_batch_size - src_seg_id = None - if packed_input: - src_seg_id = source_seg_id[:, s_index:s_index + 1] - atten.InitForSourcePacked(atten.theta, - source_vecs[:, s_index:s_index + 1, :], - source_contexts[:, s_index:s_index + 1, :], - source_padding[:, s_index:s_index + 1], - src_seg_id) - qry_seg_id = None - if packed_input: - qry_seg_id = query_seg_id[i:i + 1] - atten_vec_i, prob_i, _ = atten.ComputeContextVector( - atten.theta, query_vec[i:i + 1], query_segment_id=qry_seg_id) - atten_vec_i_out, prob_i_out = self.evaluate([atten_vec_i, prob_i]) - self.assertAllClose(prob_i_out, prob_out[i:i + 1]) - self.assertAllClose(atten_vec_i_out, atten_vec_out[i:i + 1]) - padding_i = source_padding_p[s_index] - # Check to make sure prob exists only on valid timesteps. - self.assertEqual(0.0, np.sum(padding_i * prob_i_out)) - - def testMultiHeadedAttentionAttStateSingleProb(self): - # source_batch:3, target_batch:6. Test n = 2 case. - with self.session(use_gpu=True): - (source_vecs, source_contexts, source_padding, _, query_vec, _, - _) = self._MultiHeadedAttentionInputs() - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False, - attention_head_prob_index=1) - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - atten_state = atten.ZeroAttentionState(2, 6) - print('atten_state:', atten_state) - - atten_vec, atten_prob, atten_state = atten.ComputeContextVector( - atten.theta, query_vec, atten_state) - self.evaluate(tf.global_variables_initializer()) - atten_vec_out, _, atten_state = self.evaluate( - [atten_vec, atten_prob, atten_state]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - print('atten_state', atten_state) - self.assertAllClose([ - 2.84679317, 2.36924601, 3.54831171, 2.86487937, 2.3537426, 3.54308939 - ], np.sum(atten_vec_out, axis=1)) - expected_prob_atten_state = ([ - [0.24530524, 0.24182455, 0, 0, 0.2497975, 0.2630728], - [0, 0.24692935, 0.23176268, 0.26929834, 0, 0.2520097], - [0.28280658, 0.23664463, 0, 0.18057014, 0, 0.29997864], - [0.21294391, 0.2421909, 0, 0, 0.2702513, 0.27461392], - [0, 0.25139052, 0.24466391, 0.25138932, 0, 0.25255626], - [0.25900277, 0.2514635, 0, 0.23059677, 0, 0.25893703] - ]) - self.assertAllClose(expected_prob_atten_state, - atten_state.selected_attention_head_probs) - - def testMultiHeadedAttentionAdditive(self): - self._testMultiHeadedAttentionAdditiveHelper( - 4, [2.858081, 2.33295, 3.529434, 2.856466, 2.342262, 3.526487]) - - def testMultiHeadedAttentionAdditivePackedInput(self): - self._testMultiHeadedAttentionAdditiveHelper( - 4, [2.585192, 2.267683, 3.708972, 3.107646, 2.770367, 3.580353], - packed_input=True) - - def testMultiHeadedAttentionAdditiveUnequalDim(self): - self._testMultiHeadedAttentionAdditiveHelper( - 14, [3.189594, 2.462574, 2.912001, 3.19924, 2.462459, 2.909231]) - - def testLocationSensitiveAttention1(self): - with self.session(use_gpu=True): - np.random.seed(12345) - source_vecs = tf.stack([ - tf.constant(np.random.rand(3, 4), dtype=tf.float32) for _ in range(6) - ]) - source_contexts = tf.stack([ - tf.constant(np.random.rand(3, 5), dtype=tf.float32) for _ in range(6) - ]) - source_padding = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]], - dtype=tf.float32)) - query_vec = tf.constant(np.random.rand(6, 7), dtype=tf.float32) - - params = attention.LocationSensitiveAttention.Params() - params.name = 'loc_atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = 4 - params.query_dim = 7 - params.hidden_dim = 7 - params.vn.global_vn = False - params.vn.per_step_vn = False - params.location_filter_size = 3 - params.location_num_filters = 4 - - atten = attention.LocationSensitiveAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - - atten_init_state = tf.nn.softmax( - tf.constant( - np.random.rand(6, len(params.location_features), 6), - dtype=tf.float32)) - - atten_vec, atten_prob, atten_state = atten.ComputeContextVector( - atten.theta, query_vec, atten_init_state) - - atten_vars = tf.get_collection('LocationSensitiveAttention_vars') - self.assertLen(atten_vars, 5) - - self.evaluate(tf.global_variables_initializer()) - - atten_vec_out, prob_out, atten_init_state_out, atten_state_out = self.evaluate( - [atten_vec, atten_prob, atten_init_state, atten_state]) - - self.assertEqual(atten_init_state_out.shape, atten_state_out.shape) - - print(['additive attention prob_out', np.array_repr(prob_out)]) - print(['additive attention atten_vec_out', np.array_repr(atten_vec_out)]) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_prob_out = [ - [ 0.25557119, 0.2407331 , 0. , 0. , 0.25413439, - 0.24956135], - [ 0. , 0.2539435 , 0.24765202, 0.25480285, 0. , - 0.24360162], - [ 0.25094694, 0.25000173, 0. , 0.24308425, 0. , - 0.25596702], - [ 0.25559491, 0.24071115, 0. , 0. , 0.2541317 , - 0.24956223], - [ 0. , 0.25393987, 0.24765508, 0.25481141, 0. , - 0.24359357], - [ 0.25112614, 0.24990462, 0. , 0.24246819, 0. , - 0.25650105]] - expected_atten_vec_out = [ - [ 0.49745601, 0.63471878, 0.49220741, 0.56829882, 0.42753279], - [ 0.51502693, 0.56184328, 0.37644374, 0.87425017, 0.46182287], - [ 0.57862061, 0.44247472, 0.36931327, 0.41002682, 0.14327496], - [ 0.49745524, 0.63471991, 0.49221092, 0.56828701, 0.427522 ], - [ 0.51502484, 0.5618462 , 0.37644884, 0.87424958, 0.46181911], - [ 0.57893252, 0.44248456, 0.36938512, 0.4100675 , 0.14328022]] - # pyformat: enable - # pylint: enable=bad-whitespace - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def testLocationSensitiveAttention2(self): - with self.session(use_gpu=True): - np.random.seed(12345) - source_vecs = tf.stack([ - tf.constant(np.random.rand(3, 4), dtype=tf.float32) for _ in range(6) - ]) - source_contexts = tf.stack([ - tf.constant(np.random.rand(3, 5), dtype=tf.float32) for _ in range(6) - ]) - source_padding = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]], - dtype=tf.float32)) - query_vec = tf.constant(np.random.rand(6, 7), dtype=tf.float32) - - params = attention.LocationSensitiveAttention.Params() - params.name = 'loc_atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = 4 - params.query_dim = 7 - params.hidden_dim = 7 - params.vn.global_vn = False - params.vn.per_step_vn = False - params.location_filter_size = 3 - params.location_num_filters = 4 - params.location_features = ['PREV_PROBS', 'CUMULATIVE_PROBS'] - - atten = attention.LocationSensitiveAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - - atten_init_state = atten.ZeroAttentionState(tf.shape(source_vecs)[0], 6) - - (unused_atten_vec, unused_atten_prob, - atten_state) = atten.ComputeContextVector(atten.theta, query_vec, - atten_init_state) - - atten_vars = tf.get_collection('LocationSensitiveAttention_vars') - self.assertLen(atten_vars, 5) - - self.evaluate(tf.global_variables_initializer()) - - atten_init_state_out, atten_state_out = self.evaluate( - [atten_init_state, atten_state]) - - self.assertEqual(atten_init_state_out.shape, atten_state_out.shape) - - def _testLocationSensitiveAttentionSameBatchSizeHelper( - self, same_batch_size, quantized=False): - with self.session(tf.Graph(), use_gpu=True): - np.random.seed(12345) - dtype = tf.float32 if quantized else tf.float64 - source_vecs = tf.constant(np.random.rand(6, 3, 4), dtype=dtype) - source_contexts = tf.constant(np.random.rand(6, 3, 5), dtype=dtype) - source_padding = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]], - dtype=dtype)) - - query_vec = tf.constant(np.random.rand(3, 7), dtype=dtype) - - params = attention.LocationSensitiveAttention.Params() - params.dtype = dtype - params.name = 'loc_atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = 4 - params.query_dim = 7 - params.hidden_dim = 7 - params.vn.global_vn = False - params.vn.per_step_vn = False - params.location_filter_size = 3 - params.location_num_filters = 4 - params.same_batch_size = same_batch_size - - if quantized: - cc_schedule = quant_utils.FakeQuantizationSchedule.Params().Set( - clip_start_step=0, - clip_end_step=13000, - quant_start_step=14000, - start_cap=8.0, - end_cap=1.0) - qdomain = quant_utils.SymmetricScheduledClipQDomain.Params().Set( - cc_schedule=cc_schedule.Copy()) - params.qdomain.default = qdomain.Copy() - params.qdomain.atten_context = qdomain.Copy() - - atten = attention.LocationSensitiveAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - - atten_init_state = tf.nn.softmax( - tf.constant( - np.random.rand(3, len(params.location_features), 6), dtype=dtype)) - - atten_vec, atten_prob, atten_state = atten.ComputeContextVector( - atten.theta, query_vec, atten_init_state) - - atten_vars = tf.get_collection('LocationSensitiveAttention_vars') - self.assertLen(atten_vars, 5) - - self.evaluate(tf.global_variables_initializer()) - - atten_vec_out, prob_out, atten_init_state_out, atten_state_out = self.evaluate( - [atten_vec, atten_prob, atten_init_state, atten_state]) - - self.assertEqual(atten_init_state_out.shape, atten_state_out.shape) - return atten_vec_out, prob_out, atten_init_state_out, atten_state_out - - def testLocationSensitiveAttentionSameBatchSize(self): - (atten_vec_out1, prob_out1, atten_init_state_out1, atten_state_out1) = ( - self._testLocationSensitiveAttentionSameBatchSizeHelper(True)) - (atten_vec_out2, prob_out2, atten_init_state_out2, atten_state_out2) = ( - self._testLocationSensitiveAttentionSameBatchSizeHelper(False)) - self.assertAllClose(atten_vec_out1, atten_vec_out2, rtol=1e-04, atol=1e-04) - self.assertAllClose(prob_out1, prob_out2, rtol=1e-04, atol=1e-04) - self.assertAllClose( - atten_init_state_out1, atten_init_state_out2, rtol=1e-04, atol=1e-04) - self.assertAllClose( - atten_state_out1, atten_state_out2, rtol=1e-04, atol=1e-04) - - def testLocationSensitiveAttentionQuantized(self): - (atten_vec_out1, prob_out1, atten_init_state_out1, atten_state_out1) = ( - self._testLocationSensitiveAttentionSameBatchSizeHelper(False, False)) - (atten_vec_out2, prob_out2, atten_init_state_out2, atten_state_out2) = ( - self._testLocationSensitiveAttentionSameBatchSizeHelper(False, True)) - self.assertAllClose(atten_vec_out1, atten_vec_out2, rtol=1e-02, atol=1e-02) - self.assertAllClose(prob_out1, prob_out2, rtol=1e-02, atol=1e-02) - self.assertAllClose( - atten_init_state_out1, atten_init_state_out2, rtol=1e-02, atol=1e-02) - self.assertAllClose( - atten_state_out1, atten_state_out2, rtol=1e-02, atol=1e-02) - - def testLocationSensitiveAttentionQuantizedSameBatch(self): - (atten_vec_out1, prob_out1, atten_init_state_out1, atten_state_out1) = ( - self._testLocationSensitiveAttentionSameBatchSizeHelper(True, False)) - (atten_vec_out2, prob_out2, atten_init_state_out2, atten_state_out2) = ( - self._testLocationSensitiveAttentionSameBatchSizeHelper(True, True)) - self.assertAllClose(atten_vec_out1, atten_vec_out2, rtol=1e-02, atol=1e-02) - self.assertAllClose(prob_out1, prob_out2, rtol=1e-02, atol=1e-02) - self.assertAllClose( - atten_init_state_out1, atten_init_state_out2, rtol=1e-02, atol=1e-02) - self.assertAllClose( - atten_state_out1, atten_state_out2, rtol=1e-02, atol=1e-02) - - def _attentionStateWithRandomEmitProbabilities(self, - atten, - batch_size, - time, - dtype=tf.float32): - atten_state = atten.ZeroAttentionState(time, batch_size) - atten_state.emit_probs = tf.nn.softmax( - tf.constant(np.random.rand(batch_size, time), dtype=dtype)) - return atten_state - - def testMonotonicAttention(self): - with self.session(use_gpu=True): - np.random.seed(12345) - batch_size = 3 - source_dim = 4 - context_dim = 5 - time = 6 - query_dim = 7 - source_vecs = tf.constant( - np.random.rand(time, batch_size, source_dim), dtype=tf.float32) - source_contexts = tf.constant( - np.random.rand(time, batch_size, context_dim), dtype=tf.float32) - source_padding = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]], - dtype=tf.float32)) - query_vec = tf.constant( - np.random.rand(batch_size, query_dim), dtype=tf.float32) - - params = attention.MonotonicAttention.Params() - params.name = 'monotonic_attention' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = source_dim - params.query_dim = query_dim - params.hidden_dim = query_dim - params.vn.global_vn = False - params.vn.per_step_vn = False - - atten = attention.MonotonicAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - - atten_init_state = self._attentionStateWithRandomEmitProbabilities( - atten, batch_size, time) - atten_vec, atten_prob, atten_state = atten.ComputeContextVector( - atten.theta, query_vec, atten_init_state) - - atten_vars = tf.get_collection('MonotonicAttention_vars') - self.assertLen(atten_vars, 6) - - self.evaluate(tf.global_variables_initializer()) - - atten_vec_out, prob_out, atten_init_state_out, atten_state_out = self.evaluate( - [atten_vec, atten_prob, atten_init_state, atten_state]) - - self.assertEqual(atten_init_state_out.emit_probs.shape, - atten_state_out.emit_probs.shape) - - print(['monotonic attention prob_out', np.array_repr(prob_out)]) - print(['monotonic attention atten_vec_out', np.array_repr(atten_vec_out)]) - - expected_prob_out = [[ - 0.03654566, 0.05925026, 0., 0., 0.20958641, 0.19560105 - ], [0., 0.09670404, 0.13182665, 0.13221622, 0., - 0.18074416], [0.04112773, 0.07072841, 0., 0.13837409, 0., 0.23935230]] - - expected_atten_vec_out = [ - [0.2937718, 0.30372939, 0.27034321, 0.31328040, 0.19393572], - [0.2553753, 0.26388022, 0.20429659, 0.47469878, 0.27512118], - [0.33394262, 0.1191523, 0.22405925, 0.21366173, 0.03946214] - ] - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def testMonotonicAttentionHard(self): - with self.session(use_gpu=True): - batch_size = 3 - source_dim = 4 - context_dim = 5 - time = 6 - query_dim = 10 - source_vecs = tf.constant( - np.random.randn(time, batch_size, source_dim), dtype=tf.float32) - source_contexts = tf.constant( - np.random.randn(time, batch_size, context_dim), dtype=tf.float32) - source_padding = tf.zeros((time, batch_size), dtype=tf.float32) - query_vec = tf.constant( - np.random.randn(batch_size, query_dim), dtype=tf.float32) - - params = attention.MonotonicAttention.Params() - params.name = 'monotonic_attention' - params.params_init = py_utils.WeightInit.Gaussian(0.1) - params.source_dim = source_dim - params.query_dim = query_dim - params.hidden_dim = query_dim - params.vn.global_vn = False - params.vn.per_step_vn = False - params.hard_sigmoid = True - # To encourage some probabilities to be > 0 - params.hidden_bias_init = 0. - - atten = attention.MonotonicAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - - atten_init_state = atten.ZeroAttentionState(time, batch_size) - - _, atten_prob, atten_state = atten.ComputeContextVector( - atten.theta, query_vec, atten_init_state) - - atten_vars = tf.get_collection('MonotonicAttention_vars') - self.assertLen(atten_vars, 6) - - self.evaluate(tf.global_variables_initializer()) - - prob_out, atten_state_out = self.evaluate([atten_prob, atten_state]) - print(['hard monotonic prob', np.array_repr(prob_out)]) - # Make sure all probabilities are binary - self.assertTrue(np.all(np.logical_or(prob_out == 0, prob_out == 1))) - # Make sure either one index was attended or none were - prob_sum = np.sum(prob_out, 1) - self.assertTrue(np.all(np.logical_or(prob_sum == 1, prob_sum == 0))) - - query_vec = tf.constant( - np.random.randn(batch_size, query_dim), dtype=tf.float32) - # Feed state back in - _, atten_prob, atten_state = atten.ComputeContextVector( - atten.theta, query_vec, atten_state_out) - prob_out2 = self.evaluate(atten_prob) - print(['hard monotonic prob2', np.array_repr(prob_out2)]) - # Get indices of where attention was assigned at each output timestep - idx1 = np.argmax(prob_out, 1) - idx2 = np.argmax(prob_out2, 1) - # Either the index must have increased, or all probs were 0 - self.assertTrue( - np.all(np.logical_or(idx1 <= idx2, - np.sum(prob_out2, 1) == 0))) - - def testMonotonicAttentionBackProp(self): - with self.session(use_gpu=True) as sess: - # Use float64 dtype for numeric checks - dtype = tf.float64 - tf.random.set_seed(398847392) - np.random.seed(12345) - batch_size = 3 - source_dim = 4 - context_dim = 5 - time = 6 - query_dim = 7 - source_vecs = tf.constant( - np.random.rand(time, batch_size, source_dim), dtype=tf.float64) - source_contexts = tf.constant( - np.random.rand(time, batch_size, context_dim), dtype=tf.float64) - source_padding = tf.zeros((time, batch_size), dtype=tf.float64) - query_vec = tf.constant( - np.random.rand(batch_size, query_dim), dtype=tf.float64) - - params = attention.MonotonicAttention.Params() - params.name = 'monotonic_attention' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - params.source_dim = source_dim - params.query_dim = query_dim - params.hidden_dim = query_dim - params.dtype = dtype - params.vn.global_vn = False - params.vn.per_step_vn = False - - atten = attention.MonotonicAttention(params) - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - - atten_init_state = self._attentionStateWithRandomEmitProbabilities( - atten, batch_size, time, dtype=dtype) - - atten_vec, _, _ = atten.ComputeContextVector(atten.theta, query_vec, - atten_init_state) - - loss = tf.reduce_sum(atten_vec) - - all_vars = tf.trainable_variables() - self.assertLen(all_vars, 6) - - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v) for v in all_vars - ] - - print(sym_grads) - print(num_grads) - - for sg, ng in zip(sym_grads, num_grads): - self.assertAllClose(sg, ng, rtol=1e-06, atol=1e-06) - - def _testPerStepSourcePaddingHelper(self, atten, depth=6, atten_state=None): - with self.session(use_gpu=True): - np.random.seed(505837249) - source_vecs = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=tf.float32) - for _ in range(6) - ]) - source_contexts = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=tf.float32) - for _ in range(6) - ]) - source_padding = tf.transpose( - tf.constant([[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0]], - dtype=tf.float32)) - query_vec = tf.constant(np.random.rand(2, depth), dtype=tf.float32) - query_vec = tf.concat([query_vec, query_vec], 0) - - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - # No per_step_padding. - atten_vec1, atten_prob1, _ = atten.ComputeContextVector( - atten.theta, - query_vec, - attention_state=atten_state, - per_step_source_padding=None) - per_step_padding = tf.constant( - [[0.0, 1.0, 1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 1.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], - dtype=tf.float32) - atten_vec2, atten_prob2, _ = atten.ComputeContextVector( - atten.theta, - query_vec, - attention_state=atten_state, - per_step_source_padding=per_step_padding) - - self.evaluate(tf.global_variables_initializer()) - atten_vec1_out, atten_prob1_out = self.evaluate([atten_vec1, atten_prob1]) - atten_vec2_out, atten_prob2_out = self.evaluate([atten_vec2, atten_prob2]) - print('atten_prob1_out', atten_prob1_out) - print('atten_prob2_out', atten_prob2_out) - print('atten_vec1_out', atten_vec1_out) - print('atten_vec2_out', atten_vec2_out) - self.assertAllClose(atten_prob1_out[:2], atten_prob1_out[2:]) - self.assertAllClose(atten_vec1_out[:2], atten_vec1_out[2:]) - self.assertAllClose(atten_prob1_out[1], atten_prob2_out[1]) - self.assertAllClose(atten_vec1_out[1], atten_vec2_out[1]) - self.assertAllClose(atten_prob1_out[3], atten_prob2_out[3]) - self.assertAllClose(atten_vec1_out[3], atten_vec2_out[3]) - self.assertAllClose(atten_prob2_out[1], atten_prob2_out[3]) - self.assertAllClose(atten_vec2_out[1], atten_vec2_out[3]) - self.assertGreater( - np.max(np.abs(atten_prob1_out[0] - atten_prob2_out[0])), 0.1) - self.assertGreater( - np.max(np.abs(atten_prob1_out[2] - atten_prob2_out[2])), 0.1) - self.assertGreater( - np.max(np.abs(atten_prob2_out[0] - atten_prob2_out[2])), 0.1) - return atten_prob2_out, atten_vec2_out - - def testPerStepSourcePaddingAdditiveAttention(self): - params = attention.AdditiveAttention.Params() - params.name = 'atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 877374) - depth = 6 - params.source_dim = depth - params.query_dim = depth - params.hidden_dim = depth - params.vn.global_vn = False - params.vn.per_step_vn = False - atten = params.Instantiate() - prob_out, vec_out = self._testPerStepSourcePaddingHelper(atten, depth) - print('vec_out', np.array_repr(np.sum(vec_out, 1))) - self.assertAllClose([2.00084352, 3.2933836, 2.30622029, 3.2933836], - np.sum(vec_out, 1)) - self.assertAllClose([1.0, 1.0, 1.0, 1.0], np.sum(prob_out, 1)) - - def testPerStepSourcePaddingDotProductAttention(self): - params = attention.DotProductAttention.Params() - params.name = 'atten' - depth = 6 - params.source_dim = depth - params.query_dim = depth - params.hidden_dim = depth - params.vn.global_vn = False - params.vn.per_step_vn = False - atten = params.Instantiate() - prob_out, vec_out = self._testPerStepSourcePaddingHelper(atten, depth) - print('vec_out', np.array_repr(np.sum(vec_out, 1))) - self.assertAllClose([2.02671742, 3.38590097, 2.34964013, 3.38590097], - np.sum(vec_out, 1)) - self.assertAllClose([1.0, 1.0, 1.0, 1.0], np.sum(prob_out, 1)) - - def testPerStepSourcePaddingMultiHeadedAttention(self): - params = attention.MultiHeadedAttention.Params() - params.name = 'atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 877374) - depth = 6 - params.source_dim = depth - params.query_dim = depth - params.hidden_dim = depth - params.vn.global_vn = False - params.vn.per_step_vn = False - atten = params.Instantiate() - prob_out, vec_out = self._testPerStepSourcePaddingHelper(atten, depth) - print('vec_out', np.array_repr(np.sum(vec_out, 1))) - self.assertAllClose([-0.006338, -0.025153, 0.041647, -0.025153], - np.sum(vec_out, 1)) - self.assertAllClose([1.0, 1.0, 1.0, 1.0], np.sum(prob_out, 1)) - - def testPerStepSourcePaddingLocationSensitiveAttention(self): - params = attention.LocationSensitiveAttention.Params() - params.name = 'atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 877374) - depth = 6 - params.source_dim = depth - params.query_dim = depth - params.hidden_dim = depth - params.location_filter_size = 3 - params.location_num_filters = 4 - params.vn.global_vn = False - params.vn.per_step_vn = False - atten_state = tf.concat( - [tf.ones([4, 1], tf.float32), - tf.zeros([4, 5], tf.float32)], 1) - atten_state = tf.expand_dims(atten_state, 1) - atten = params.Instantiate() - prob_out, vec_out = self._testPerStepSourcePaddingHelper( - atten, depth, atten_state=atten_state) - print('vec_out', np.array_repr(np.sum(vec_out, 1))) - self.assertAllClose([2.001103, 3.293414, 2.306448, 3.293414], - np.sum(vec_out, 1)) - self.assertAllClose([1.0, 1.0, 1.0, 1.0], np.sum(prob_out, 1)) - - def testPerStepSourcePaddingMonotonicAttention(self): - params = attention.MonotonicAttention.Params() - params.name = 'atten' - params.params_init = py_utils.WeightInit.Gaussian(0.1, 877374) - depth = 6 - params.source_dim = depth - params.query_dim = depth - params.hidden_dim = depth - params.vn.global_vn = False - params.vn.per_step_vn = False - atten = params.Instantiate() - atten_state = atten.ZeroAttentionState(6, 4) - atten_state.emit_probs = tf.concat( - [tf.ones([4, 1], tf.float32), - tf.zeros([4, 5], tf.float32)], 1) - prob_out, vec_out = self._testPerStepSourcePaddingHelper( - atten, depth, atten_state=atten_state) - print('prob_out', np.array_repr(np.sum(prob_out, 1))) - print('vec_out', np.array_repr(np.sum(vec_out, 1))) - - def testGmmMonotonicAttentionDropout(self): - p = attention.GmmMonotonicAttention.Params().Set( - name='gmm_monotonic_attention', atten_dropout_prob=0.5) - with self.assertRaises(NotImplementedError): - p.Instantiate() - - def testGmmMonotonicAttention(self): - with self.session(use_gpu=True): - np.random.seed(12345) - source_vecs = tf.stack([ - tf.constant(np.random.rand(3, 4), dtype=tf.float32) for _ in range(6) - ]) - source_contexts = tf.stack([ - tf.constant(np.random.rand(3, 5), dtype=tf.float32) for _ in range(6) - ]) - source_padding = tf.transpose( - tf.constant( - [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]], - dtype=tf.float32)) - query_vec = tf.constant(np.random.rand(6, 7), dtype=tf.float32) - - params = attention.GmmMonotonicAttention.Params() - params.name = 'gmm_atten' - params.params_init = py_utils.WeightInit.Xavier(0.1) - params.source_dim = 4 - params.query_dim = 7 - params.hidden_dim = 7 - params.num_mixtures = 2 - params.vn.global_vn = False - params.vn.per_step_vn = False - - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, source_vecs, source_contexts, - source_padding) - # target_batch=6 - atten_init_state = atten.ZeroAttentionState(tf.shape(source_vecs)[0], 6) - - atten_vec, atten_prob, atten_state = atten.ComputeContextVector( - atten.theta, query_vec, atten_init_state) - - self.evaluate(tf.global_variables_initializer()) - - atten_vec_out, prob_out, atten_init_state_out, atten_state_out = self.evaluate( - [atten_vec, atten_prob, atten_init_state, atten_state]) - - self.assertEqual(atten_init_state_out.shape, atten_state_out.shape) - self.assertEqual(atten_init_state_out.shape, (6, 2, 4)) - - print(['gmm attention prob_out', np.array_repr(prob_out)]) - print(['gmm attention atten_vec_out', np.array_repr(atten_vec_out)]) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_prob_out = [ - [ 2.45764434e-01, 3.97835493e-01, 0., 0., 4.25808690e-03, - 1.29864624e-04], - [ 0., 3.98021877e-01, 2.37964690e-01, 5.23146540e-02, 0., - 1.29256863e-04], - [ 2.46294901e-01, 3.97767872e-01, 0., 5.21243662e-02, 0., - 1.29372784e-04], - [ 2.45875627e-01, 3.97635251e-01, 0., 0., 4.27022483e-03, - 1.30706903e-04], - [ 0., 3.97709191e-01, 2.37897262e-01, 5.24106659e-02, 0., - 1.30714150e-04], - [ 2.46048093e-01, 3.97871077e-01, 0., 5.21884784e-02, 0., - 1.29211781e-04]] - expected_atten_vec_out = [ - [ 0.23010808, 0.43757612, 0.25150469, 0.3631629 , 0.37140277], - [ 0.54693544, 0.56182981, 0.21333349, 0.58108622, 0.21566363], - [ 0.4048025 , 0.53986353, 0.13288836, 0.22497796, 0.17450145], - [ 0.23008531, 0.4375343 , 0.25150725, 0.36303982, 0.37127423], - [ 0.54661846, 0.5615437 , 0.21332006, 0.58084518, 0.21558265], - [ 0.40484226, 0.53978455, 0.13283314, 0.22490481, 0.17447782]] - # pyformat: enable - # pylint: enable=bad-whitespace - - self.assertAllClose(expected_prob_out, prob_out) - self.assertAllClose(expected_atten_vec_out, atten_vec_out) - - def testMergerLayerMean(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 5 - n_sources = 3 - p_ctxs = [ - np.random.rand(batch, depth).astype('float32') - for _ in range(n_sources) - ] - ctxs = [tf.constant(ctx, dtype=tf.float32) for ctx in p_ctxs] - - p = attention.MergerLayer.Params() - p.name = 'merger_layer' - p.merger_op = 'mean' - p.source_dim = depth - merger = p.Instantiate() - - ctx = merger.FProp(merger.theta, ctxs) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate([ctx])[0] - - expected_ctx = np.mean(p_ctxs, axis=0) - self.assertEqual(actual_ctx.shape, (batch, depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testMergerLayerAdditiveAttention(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 5 - query_dim = 7 - n_sources = 3 - ctxs = [ - tf.constant(np.random.rand(batch, depth), dtype=tf.float32) - for _ in range(n_sources) - ] - query_vec = tf.constant( - np.random.rand(batch * 2, query_dim), dtype=tf.float32) - p = attention.MergerLayer.Params() - p.name = 'merger_layer' - p.merger_op = 'atten' - p.source_dim = depth - p.query_dim = query_dim - p.hidden_dim = depth - merger = p.Instantiate() - - ctx = merger.FProp(merger.theta, ctxs, query_vec) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate(ctx) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [ 0.40796196, 0.50855637, 0.92564321, 0.72608167], - [ 0.34300309, 0.17305931, 0.64801621, 0.4161588 ], - [ 0.40570667, 0.28166312, 0.07109687, 0.07077176], - [ 0.44923055, 0.56033343, 0.70899796, 0.73256713], - [ 0.56362778, 0.42331296, 0.47032064, 0.76701462], - [ 0.40873578, 0.50516003, 0.92537481, 0.72435796], - [ 0.33702248, 0.17404726, 0.65101075, 0.41883218], - [ 0.40316698, 0.28128177, 0.0709244 , 0.07073996], - [ 0.44036126, 0.53640223, 0.68623006, 0.75264776], - [ 0.54324883, 0.42487082, 0.4616943 , 0.77234119]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_ctx.shape, (batch * 2, depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testMergerLayerDotProductAttention(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 5 - n_sources = 3 - ctxs = [ - tf.constant(np.random.rand(batch, depth), dtype=tf.float32) - for _ in range(n_sources) - ] - query_vec = tf.constant( - np.random.rand(batch * 2, depth), dtype=tf.float32) - p = attention.MergerLayer.Params() - p.name = 'merger_layer' - p.merger_op = 'atten' - p.source_dim = depth - p.query_dim = depth - p.hidden_dim = depth - p.attention_tpl = attention.DotProductAttention.Params() - merger = p.Instantiate() - - ctx = merger.FProp(merger.theta, ctxs, query_vec) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate(ctx) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [ 0.40122974, 0.53032947, 0.92722446, 0.73408204], - [ 0.37834394, 0.16492322, 0.6284582 , 0.40583336], - [ 0.43172807, 0.28519249, 0.07334236, 0.07126588], - [ 0.48187545, 0.56433642, 0.7028234 , 0.77750808], - [ 0.59640014, 0.46689704, 0.47688526, 0.74523771], - [ 0.41653261, 0.50926942, 0.92638767, 0.74147904], - [ 0.34954029, 0.16965927, 0.64286244, 0.41876066], - [ 0.44629157, 0.28723121, 0.07451884, 0.07151417], - [ 0.509902 , 0.62019253, 0.75361776, 0.74199384], - [ 0.56122077, 0.42407531, 0.46921006, 0.76747787]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_ctx.shape, (batch * 2, depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testMergerLayerConcat(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 5 - n_sources = 3 - ctxs = [ - tf.constant(np.random.rand(batch, depth), dtype=tf.float32) - for _ in range(n_sources) - ] - p = attention.MergerLayer.Params() - p.name = 'merger_layer' - p.merger_op = 'concat' - p.source_dim = depth - merger = p.Instantiate() - - ctx = merger.FProp(merger.theta, ctxs) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate([ctx])[0] - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [ 0.1177848 , 0.94777811, 0.94537693, 0.6216979 , 0.51051533, - 0.5474115 , 0.93749231, 0.93760508, 0.5904724 , 0.05267439, - 0.89581013, 0.63010913], - [ 0.25139269, 0.13851869, 0.65362513, 0.57537138, 0.05093541, - 0.28593501, 0.84663856, 0.39284077, 0.79584485, 0.07670615, - 0.40381077, 0.26504567], - [ 0.1108813 , 0.23381528, 0.05560364, 0.06867393, 0.77289224, - 0.32918185, 0.10567363, 0.07876136, 0.35448784, 0.28477612, - 0.05394353, 0.06531866], - [ 0.82317245, 0.78475511, 0.82936037, 0.99494314, 0.07920805, - 0.02165302, 0.25108394, 0.92048419, 0.44413447, 0.81940264, - 0.98786688, 0.35846332], - [ 0.86243463, 0.75607926, 0.54042 , 0.58698255, 0.13624814, - 0.47994047, 0.28561282, 0.87185597, 0.66811442, 0.07942203, - 0.56781054, 0.83598584]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_ctx.shape, (batch, n_sources * depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testMergerLayerConcatPostProj(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 5 - n_sources = 3 - ctxs = [ - tf.constant(np.random.rand(batch, depth), dtype=tf.float32) - for _ in range(n_sources) - ] - p = attention.MergerLayer.Params() - p.name = 'merger_layer' - p.merger_op = 'concat' - p.source_dim = depth - # Post projection to a dimensionality of 4. - p.post_proj = layers.ProjectionLayer.Params().Set( - name='post_proj', - batch_norm=False, - weight_norm=False, - has_bias=True, - input_dim=12, - output_dim=4) - merger = p.Instantiate() - ctx = merger.FProp(merger.theta, ctxs) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate([ctx])[0] - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [0.05845007, 0.14603308, 2.099096 , 0.03618803], - [0.50603 , 0.1128372 , 1.0714196 , 0.3054366 ], - [0. , 0.17477296, 0. , 0. ], - [0.34721488, 0. , 0.9593564 , 0.6714128 ], - [0.012324 , 0. , 1.3537602 , 0.16794051]] - - # pyformat: enable - # pylint: enable=bad-whitespace - tf.logging.info(np.array_repr(actual_ctx)) - self.assertEqual(actual_ctx.shape, (batch, depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testMergerLayerConcatPreProjections(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 5 - n_sources = 3 - ctxs = [ - tf.constant(np.random.rand(batch, depth), dtype=tf.float32) - for _ in range(n_sources) - ] - p = attention.MergerLayer.Params() - # We down project all of the sources to dimensionality 1. - p.pre_proj_input_dims = [4, 4, 4] - p.pre_proj_output_dims = [1, 1, 1] - p.name = 'merger_layer' - p.merger_op = 'concat' - p.source_dim = depth - merger = p.Instantiate() - - ctx = merger.FProp(merger.theta, ctxs) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate([ctx])[0] - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [ 0., 0.72890908, 0. ], - [ 0.4647972, 0.28266785, 0. ], - [ 0., 0.74580085, 0.09588336], - [ 0.46080768, 0., 0.66402191], - [ 0.19947493, 0.38837075, 0. ], - ] - # pyformat: enable - # pylint: enable=bad-whitespace - tf.logging.info(np.array_repr(actual_ctx)) - # The final context vector will have shape (5, 3) since each source - # has dimensionality 1 after the down projection above. - self.assertEqual(actual_ctx.shape, (batch, n_sources)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testInvalidPreProjections(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - p = attention.MergerLayer.Params() - # We intentionally set output_dims to be of a different - # length. This should cause a ValueError to be raised - # during init. - p.pre_proj_input_dims = [4, 4, 4] - p.pre_proj_output_dims = [1, 1] - p.name = 'merger_layer' - p.merger_op = 'concat' - p.source_dim = depth - with self.assertRaisesRegex( - ValueError, 'Output dims should be the same length as input dims.*'): - _ = p.Instantiate() - - def testMergerLayerWeightedSum(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 2 - n_sources = 3 - ctxs = [[[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 4.0, 5.0]], - [[3.0, 4.0, 5.0, 6.0], [6.0, 7.0, 8.0, 9.0]], - [[4.0, 5.0, 6.0, 7.0], [7.0, 8.0, 1.0, 2.0]]] - p = attention.MergerLayer.Params() - p.name = 'merger_layer' - p.merger_op = 'weighted_sum' - p.source_dim = depth - p.num_sources = n_sources - merger = p.Instantiate() - - ctxs = [tf.expand_dims(i, 2) for i in ctxs] - ctx = tf.squeeze(merger.FProp(merger.theta, ctxs), 2) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate(ctx) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [[ 2.66666675, 3.66666675, 4.66666698, 5.66666698], - [ 5.0, 6.0, 4.33333349, 5.33333349]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_ctx.shape, (batch, depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testMergerLayerGatedAvg(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 2 - n_sources = 3 - - inp_1 = np.asarray([[0.0, 0.0, 0.0, 0.0], [-1.0, -1.0, 1.0, 1.0]], - dtype=np.float32) - inp_2 = np.asarray([[1.0, 1.0, 1.0, 1.0], [-1.0, -1.0, 1.0, 1.0]], - dtype=np.float32) - inp_3 = np.asarray([[-1.0, -1.0, -1.0, -1.0], [-1.0, -1.0, 1.0, 1.0]], - dtype=np.float32) - p = attention.MergerLayer.Params() - p.name = 'merger_layer' - p.merger_op = 'gated_avg' - p.source_dim = depth - p.num_sources = n_sources - merger = p.Instantiate() - - ctx = merger.FProp(merger.theta, [inp_1, inp_2, inp_3]) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate(ctx) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [ 0.365041, 0.365041, 0.365041, 0.365041], - [ -1.0, -1.0, 1.0 , 1.0]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_ctx.shape, (batch, depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testMultiSourceMultiHeadedAttention(self): - with self.session(use_gpu=True) as sess: - (source_vecs, source_contexts, source_padding, source_padding_p, - query_vec, _, _) = self._MultiHeadedAttentionInputs() - iap = attention.DotProductAttention.Params() - iap.name = 'dot_atten' - mha_params = attention.MultiHeadedAttention.Params().Set( - name='multihead_atten', - source_dim=4, - query_dim=4, - hidden_dim=4, - inner_atten_params=iap, - num_attention_heads=2, - use_source_vec_as_attention_value=False) - - # Single-source attention. - params = attention.MultiSourceAttention.Params().Set( - name='one_source_atten', - source_dim=4, - query_dim=4, - source_atten_tpls=[('src_1', mha_params)], - primary_source_key='src_1') - atten = params.Instantiate() - atten.InitForSourcePacked(atten.theta, - py_utils.NestedMap(src_1=source_vecs), - py_utils.NestedMap(src_1=source_contexts), - py_utils.NestedMap(src_1=source_padding)) - tf.global_variables_initializer().run() - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=source_contexts.shape[2]) - - atten_vec_out, prob_out = sess.run([atten_vec, atten_prob]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - - self.assertAllClose( - [2.8940253, 2.2901258, 3.5679011, 2.894734, 2.2989905, 3.5306041], - np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - t_batch_size = 6 - s_batch_size = 3 - for i in range(t_batch_size): - # Test to make sure we didn't mess up indexing. - s_i = i % s_batch_size - atten.InitForSourcePacked( - atten.theta, py_utils.NestedMap(src_1=source_vecs[:, - s_i:s_i + 1, :]), - py_utils.NestedMap(src_1=source_contexts[:, s_i:s_i + 1, :]), - py_utils.NestedMap(src_1=source_padding[:, s_i:s_i + 1])) - atten_vec_i, prob_i, _ = atten.ComputeContextVector( - atten.theta, query_vec[i:i + 1]) - atten_vec_i_out, prob_i_out = sess.run([atten_vec_i, prob_i]) - self.assertAllClose(prob_i_out, prob_out[i:i + 1]) - self.assertAllClose(atten_vec_i_out, atten_vec_out[i:i + 1]) - padding_i = source_padding_p[s_i] - # Check to make sure prob exists only on valid timesteps. - self.assertEqual(0.0, np.sum(padding_i * prob_i_out)) - - # Two-source attention. - atten_merger_p = attention.MergerLayer.Params().Set( - params_init=py_utils.WeightInit.Uniform(0.04), - merger_op='concat', # concatenate attention - pre_proj_input_dims=[6, 6], - pre_proj_output_dims=[6, 6]) - params = attention.MultiSourceAttention.Params().Set( - name='two_source_atten', - source_dim=4, - query_dim=4, - source_atten_tpls=[('src_1', mha_params), - ('src_2', - mha_params.Copy().Set(name='multihead_atten2'))], - primary_source_key='src_1', - atten_merger_tpl=atten_merger_p) - atten = params.Instantiate() - - (source_vecs2, source_contexts2, source_padding2, source_padding_p, - query_vec, _, _) = self._MultiHeadedAttentionInputs() - atten.InitForSourcePacked( - atten.theta, - py_utils.NestedMap(src_1=source_vecs, src_2=source_vecs2), - py_utils.NestedMap(src_1=source_contexts, src_2=source_contexts2), - py_utils.NestedMap(src_1=source_padding, src_2=source_padding2)) - tf.global_variables_initializer().run() - atten_vec, atten_prob, _ = atten.ComputeContextVector( - atten.theta, query_vec) - self._CheckStaticShapes( - atten_vec, - atten_prob, - target_batch_size=query_vec.shape[0], - source_length=source_contexts.shape[0], - context_dim=2 * source_contexts.shape[2]) - - atten_vec_out, prob_out = sess.run([atten_vec, atten_prob]) - print('atten_vec_out', np.sum(atten_vec_out, axis=1)) - - self.assertAllClose( - [2.860059, 2.022061, 3.128138, 2.8762774, 2.103229, 3.1187325], - np.sum(atten_vec_out, axis=1)) - print('atten_vec_out', atten_vec_out) - print('prob_out', prob_out) - t_batch_size = 6 - s_batch_size = 3 - for i in range(t_batch_size): - # Test to make sure we didn't mess up indexing. - s_i = i % s_batch_size - atten.InitForSourcePacked( - atten.theta, - py_utils.NestedMap( - src_1=source_vecs[:, s_i:s_i + 1, :], - src_2=source_vecs2[:, s_i:s_i + 1, :]), - py_utils.NestedMap( - src_1=source_contexts[:, s_i:s_i + 1, :], - src_2=source_contexts2[:, s_i:s_i + 1, :]), - py_utils.NestedMap( - src_1=source_padding[:, s_i:s_i + 1], - src_2=source_padding2[:, s_i:s_i + 1])) - atten_vec_i, prob_i, _ = atten.ComputeContextVector( - atten.theta, query_vec[i:i + 1]) - atten_vec_i_out, prob_i_out = sess.run([atten_vec_i, prob_i]) - self.assertAllClose(prob_i_out, prob_out[i:i + 1]) - self.assertAllClose(atten_vec_i_out, atten_vec_out[i:i + 1]) - padding_i = source_padding_p[s_i] - # Check to make sure prob exists only on valid timesteps. - self.assertEqual(0.0, np.sum(padding_i * prob_i_out)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/attention_util.py b/lingvo/core/attention_util.py deleted file mode 100644 index 3fbc9e9c..00000000 --- a/lingvo/core/attention_util.py +++ /dev/null @@ -1,664 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Attention related utils, e.g. relative positional embeddings.""" - -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core import summary_utils - - -def ConvertToBlocks(x, block_size, padding_val=0.0): - """Turns a sequence to non overlapping blocks. - - Args: - x: a tensor of [batch, time, ...]. - block_size: int. Number of time frames in a block. - padding_val: float. value on the padded frames. - - Returns: - A tensor of [batch, num_blocks, block_size, ...], with necessary paddings, - where output[:, i, ...] are x[:, i*block_size:(i+1)*block_size, ...]. - """ - shape = py_utils.GetShape(x) - b, t = shape[:2] - if block_size < 1: - raise ValueError('block_size must be at least 1, got {}'.format(block_size)) - w = block_size - # Pad t to be a multiply of w. - num_blocks = (t + w - 1) // w - pad_to_length = num_blocks * w - padded = py_utils.PadSequenceDimension(x, pad_to_length, padding_val) - reshaped = tf.reshape(padded, [b, num_blocks, w] + shape[2:]) - return reshaped - - -def ExtractBlockContext(x, - block_size, - left_context, - right_context, - padding_val=0.0): - """Extracts temporal context for every block. - - Args: - x: a tensor of [batch, time, ...]. - block_size: int. Number of time frames in a block. - left_context: int. Left context size. - right_context: int. Right context size. - padding_val: float. value on the padded frames. - - Returns: - A tensor of [batch, num_blocks, context_size, ...], with necessary paddings, - where context_size = block_size + (left_context - 1) + right_context, - and output[:, i, ...] are x[:, start-left_context+1:end+right_context, ...], - start = i * block_size, end = (i + 1) * block_size. - """ - if block_size < 1: - raise ValueError('block_size must be at least 1, got {}'.format(block_size)) - if left_context < 1 or left_context > block_size + 1: - raise ValueError( - 'left_context must be at least 1 and at most block_size + 1 = {}, ' - 'got {}'.format(block_size + 1, left_context)) - if right_context < 0 or right_context > block_size: - raise ValueError( - 'right_context must be at least 0 and at most block_size = {}, ' - 'got {}'.format(block_size, right_context)) - - block = ConvertToBlocks(x, block_size, padding_val) - concat_list = [block] - - if left_context > 1: - if block_size == left_context - 1: - left_block = tf.roll(block, shift=1, axis=1) - else: - x_shift = tf.roll(x, shift=left_context - 1, axis=1) - x_shift_block = ConvertToBlocks(x_shift, block_size, padding_val) - left_block = x_shift_block[:, :, :left_context - 1:, ...] - concat_list = [left_block] + concat_list - - if right_context > 0: - if block_size == right_context: - right_block = tf.roll(block, shift=-1, axis=1) - else: - x_shift = tf.roll(x, shift=-right_context, axis=1) - x_shift_block = ConvertToBlocks(x_shift, block_size, padding_val) - right_block = x_shift_block[:, :, -right_context:, ...] - concat_list += [right_block] - - return tf.concat(concat_list, axis=2) - - -def MakeCausalPadding(seq_len, - block_size, - left_context, - right_context, - dtype=tf.float32): - """Makes the causal padding tensor for a full sequence. - - Args: - seq_len: int or scalar int tensor. Sequence length. - block_size: int. Number of time frames in a block. - left_context: int. Left context size. - right_context: int. Right context size. - dtype: tf.dtype, default is tf.float32. - - Returns: - A tensor of [num_blocks, block_size, context_size] taking values in {0, 1}, - where context_size = block_size + (left_context - 1) + right_context. - Element b, i, j is zero if in the b-th block, the i-th frame can access - the j-th frame in the context. - """ - seq_len = py_utils.with_dependencies([ - py_utils.assert_greater_equal( - seq_len, 1, message='seq_len must be at least 1') - ], seq_len) - - num_blocks = (seq_len + block_size - 1) // block_size - context_size = block_size + (left_context - 1) + right_context - - # [num_blocks, block_size]: source positions in the original sequence. - src_positions = tf.reshape( - tf.range(num_blocks * block_size), [num_blocks, block_size]) - # [num_blocks,]: source positions at the start of each block. - block_start_positions = tf.range(0, num_blocks * block_size, block_size) - # [context_size]: positions relative to the block start. - relative_context_positions = tf.range(context_size) - (left_context - 1) - - # [num_blocks, context_size]: target positions in the original sequence. - tgt_positions = ( - block_start_positions[:, tf.newaxis] + - relative_context_positions[tf.newaxis, :]) - # [num_blocks, block_size, context_size]: position differences between source- - # target pairs. - position_diff = src_positions[:, :, tf.newaxis] - tgt_positions[:, - tf.newaxis, :] - # [num_blocks, block_size, context_size]: if attention is allowed between - # source-target pairs. - valid_atten = tf.math.logical_and(-right_context <= position_diff, - position_diff < left_context) - - # [num_blocks, block_size]: if the source position is valid, not padded. - valid_src = src_positions < seq_len - # [num_blocks, context_size]: if the target position is valid, not padded. - valid_tgt = tf.math.logical_and(0 <= tgt_positions, tgt_positions < seq_len) - - valid_atten &= tf.math.logical_and(valid_src[:, :, tf.newaxis], - valid_tgt[:, tf.newaxis, :]) - - padding = 1.0 - tf.cast(valid_atten, dtype=dtype) - - return padding - - -def RelShift(x): - """Performs relative shift on 4D tensor (first 2 axis are batching dims). - - Given input of shape [?, ?, W, W], this does "relative shifting" for the - last two dims, s.t. output[b, n, i, j] = 0 if i > j else input[b, n, i, j-i] - - Args: - x: A Tensor of shape [?, ?, W, W] - - Returns: - A Tensor of the same shape as input with its content shifted (as described - above). - """ - b, n, w, _ = py_utils.GetShape(x) - x = py_utils.HasShape(x, [-1, -1, w, w]) - x = tf.pad(x, ((0, 0), (0, 0), (0, 0), (0, 1))) - x = tf.reshape(x, [b, n, w + 1, w]) - x = x[:, :, :w, :] - return x - - -def RelPositionBias(content, abs_pos_emb, skip_term_b=False): - """Compute relative position bias. - - This is a subroutine used by variants of self-attentions with relative - positional embedding. - - B: batch size - T: sequence length - N: num of attention heads. - H: per-head attention dimension. - - output[b][n][i][j] = content[b][i][n] x abs_pos_emb[i-j+T-1][n] - - Notice padding is supposed to be masked by the caller of this function. - - Args: - tensors of the following shapes: - content: [N, H] if skip_term_b else [B, T, N, H] - abs_pos_emb: [2T - 1, N, H], the absolute positional embedding. - abs_pos_emb[i] is the emb of relative distance i - (T-1). - skip_term_b: If to skip term_b in section 3.3 equation. - - Returns: - The attention logits tensor. [N, T, T] if skip_term_b else [B, N, T, T]. - """ - - if not skip_term_b: - b, t, n, h = py_utils.GetShape(content) - l = 2 * t - 1 - abs_pos_emb = py_utils.HasShape(abs_pos_emb, [l, n, h]) - else: - n, h = py_utils.GetShape(content) - l = py_utils.GetShape(abs_pos_emb)[0] - t = (l + 1) // 2 - - if not skip_term_b: - # [B, N, T, L=2T-1] - term_bd = tf.einsum('BTNH,LNH->BNTL', content, abs_pos_emb) - term_bd = tf.reshape(term_bd, [b, n, t * l], name='flatten') - # [B, N, T * (L + 1)]. - term_bd = tf.pad(term_bd, ((0, 0), (0, 0), (0, t))) - # [B, N, T, L + 1]. - term_bd = tf.reshape(term_bd, [b, n, t, l + 1], name='restore') - return term_bd[:, :, :, t - 1::-1] - else: - # [N, L=2T-1] - term_d = tf.einsum('NH,LNH->NL', content, abs_pos_emb) - # [N, T, L] - term_d = tf.tile(tf.expand_dims(term_d, axis=1), [1, t, 1], name='tile') - term_d = tf.reshape(term_d, [n, t * l]) - # [N, T * (L + 1)]. - term_d = tf.pad(term_d, ((0, 0), (0, t))) - # [N, T, L + 1]. - term_d = tf.reshape(term_d, [n, t, l + 1], name='restore') - return term_d[:, :, t - 1::-1] - - -def _AttenLogits(query, - key, - abs_pos_emb, - content_bias=None, - positional_bias=None, - skip_term_b=False): - """Attention logits from ... - - Transformer-XL(https://arxiv.org/pdf/1901.02860.pdf, section 3.3) version of - self attention with relative position embedding. - - Notice padding is supposed to be masked by the caller of this function. - - B: batch size - T: sequence length - N: num of attention heads. - H: per-head attention dimension. - - Args: - tensors of the following shapes: - query: [B, T, N, H] - key: [B, T, N, H] - abs_pos_emb: [2T - 1, N, H]. The sinusoid positional embedding from - https://arxiv.org/abs/1706.03762. abs_pos_emb[i] is the emb of relative - distance i - (T-1). - content_bias: [N, H] or None - positional_bias: [N, H] or None - skip_term_b: If to skip term_b in section 3.3 equation. - - Returns: - The attention logits tensor. [B, N, T, T] - """ - b, t, n, h = py_utils.GetShape(query) - - key = py_utils.HasShape(key, [b, t, n, h]) - if content_bias is not None: - content_bias = py_utils.HasShape(content_bias, [n, h]) - else: - content_bias = 0 - if positional_bias is not None: - positional_bias = py_utils.HasShape(positional_bias, [n, h]) - else: - positional_bias = 0 - - # [B, N, T, S=T] - with tf.name_scope('term_ac'): - term_ac = tf.einsum('BTNH,BSNH->BNTS', query + content_bias, key) - with tf.name_scope('term_bd'): - if skip_term_b: - content = positional_bias - else: - content = query + positional_bias - term_bd = RelPositionBias(content, abs_pos_emb, skip_term_b) - return term_ac + term_bd - - -def AttenLogitsTransformerXL(query, - key, - abs_pos_emb, - content_bias, - positional_bias, - skip_term_b=False): - """Attention logits from ... - - Transformer-XL(https://arxiv.org/pdf/1901.02860.pdf, section 3.3) version of - self attention with relative position embedding. - - Notice padding is supposed to be masked by the caller of this function. - - B: batch size - T: sequence length - N: num of attention heads. - H: per-head attention dimension. - - Args: - tensors of the following shapes: - query: [B, T, N, H] - key: [B, T, N, H] - abs_pos_emb: [2T - 1, N, H]. The sinusoid positional embedding from - https://arxiv.org/abs/1706.03762. abs_pos_emb[i] is the emb of relative - distance i - (T-1). - content_bias: [N, H] - positional_bias: [N, H] - skip_term_b: If to skip term_b in section 3.3 equation. - - Returns: - The attention logits tensor. [B, N, T, T] - """ - return _AttenLogits(query, key, abs_pos_emb, content_bias, positional_bias, - skip_term_b) - - -def AttenLogitsRPE(query, key, abs_pos_emb): - """Attention logits from ... - - https://arxiv.org/pdf/1803.02155.pdf with trainable rel position emb. - - Notice padding is supposed to be masked by the caller of this function. - - B: batch size - T: sequence length - N: num of attention heads. - H: per-head attention dimension. - - Args: - tensors of the following shapes: - query: [B, T, N, H] - key: [B, T, N, H] - abs_pos_emb: [2T - 1, N, H]. The trainable embdding. abs_pos_emb[i] is - the emb of relative distance i - (T-1). - - Returns: - The attention logits tensor. [B, N, T, T] - """ - return _AttenLogits(query, key, abs_pos_emb) - - -class KMeansClusteringForAtten(base_layer.BaseLayer): - """Implements k-means clustering with mini-batch updates. - - This is used in the implementation of https://arxiv.org/pdf/2003.05997. - - We use the following capital letters to denote shape parameters: - B = batch size - L = length of the input sequence (referred to as S or T elsewhere) - N = number of attention heads - H = dimensions of each attention head - K = number of clusters - """ - - @classmethod - def Params(cls): - """Params.""" - p = super().Params() - p.Define( - 'num_clusters', 0, 'Number of clusters, typically around the square' - ' root of the sequence length.') - p.Define('num_heads', 1, 'Num of attention heads.') - p.Define('dim_per_head', 0, 'Dimensions of each attention head.') - p.Define('decay', 0.999, 'The decay with which to update centroids.') - p.Define('epsilon', 1e-6, 'Tiny value to guard against divide by 0.') - p.Define( - 'apply_layer_norm', True, 'Whether to apply LayerNorm() on the ' - 'inputs first. If unset, caller must normalize first.') - return p - - def __init__(self, params): - """Constructs an instance which tracks its own set of centroids.""" - super().__init__(params) - p = self.params - assert p.num_clusters - assert p.dim_per_head - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - # The per-head centroids. Shape [N, K, H]. - means = py_utils.WeightParams( - shape=[p.num_heads, p.num_clusters, p.dim_per_head], - init=py_utils.WeightInit.Gaussian(), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('means', means) - - @classmethod - def LayerNorm(cls, x, epsilon=1e-6): - """Performs layer normalization on the last dimension of 'x'. - - This differs from layers.LayerNorm in that it fixes both scale and bias at - 0. - - Args: - x: An input tensor to be normalized. - epsilon: Tiny value used to guard against rsqrt of 0. - - Returns: - 'x' with its last dimension normalized. - """ - counts, means_ss, variance_ss, _, = tf.nn.sufficient_statistics( - x, axes=[-1], keepdims=True) - mean, variance = tf.nn.normalize_moments(counts, means_ss, variance_ss, - None) - return (x - mean) * tf.math.rsqrt(variance + epsilon) - - def FProp(self, theta, x, paddings=None, update=False): - """Computes distances of the given input 'x' to all centroids. - - This implementation applies layer normalization on 'x' internally first, - and the returned 'dists' is computed using the normalized 'x'. - - Args: - theta: A `.NestedMap` of weights' values of this layer. - x: A tensor of shape [B, L, N, H]. - paddings: If not None, a tensor of shape [B, L]. - update: bool, whether to update centroids using x. - - Returns: - dists: "distances" of the given input 'x' to all centroids. - Shape [B, L, N, K]. - k_means_loss: the average squared Euclidean distances to the closest - centroid, a scalar. - """ - p = self.params - if paddings is None: - paddings = tf.zeros_like(x[:, :, 0, 0]) - # Shape [B, L, 1, 1] - paddings_4d = paddings[:, :, None, None] - - if p.apply_layer_norm: - x = KMeansClusteringForAtten.LayerNorm(x, p.epsilon) - - # 'x' is normalized (but theta.means is not), we use negative dot product to - # approximate the Euclidean distance here. - dists = -tf.einsum('BLNH, NKH -> BLNK', x, theta.means) - - # For padded positions we update the distances to very large numbers. - very_large_dists = tf.ones_like(dists) * tf.constant( - 0.1, dtype=dists.dtype) * dists.dtype.max - paddings_tiled = tf.tile(paddings_4d, [1, 1, p.num_heads, p.num_clusters]) - dists = tf.where(paddings_tiled > 0.0, very_large_dists, dists) - - # Shape [B, L, N, K], the same as 'dists' above. - nearest_one_hot = tf.one_hot(tf.math.argmin(dists, axis=-1), p.num_clusters) - # Same shape as the input 'x'. - nearest_centroid = tf.einsum('BLNK, NKH -> BLNH', nearest_one_hot, - theta.means) - diff = tf.math.squared_difference(x, tf.stop_gradient(nearest_centroid)) - diff = py_utils.ApplyPadding(paddings_4d, diff) - diff = tf.math.reduce_mean(diff, axis=2) - - # The commitment loss which when back proped against encourages the 'x' - # values to commit to their chosen centroids. - k_means_loss = tf.math.reduce_sum(diff) / tf.math.reduce_sum(1.0 - paddings) - summary_utils.scalar('k_means/squared_distance_loss', k_means_loss) - - # TODO(zhouwk): investigate normalizing theta.means after each update. - means_norm = tf.norm(theta.means) - summary_utils.scalar('k_means/centroid_l2_norm/min', - tf.math.reduce_min(means_norm)) - summary_utils.scalar('k_means/centroid_l2_norm/mean', - tf.math.reduce_mean(means_norm)) - - if not update: - return dists, k_means_loss - - # To update the centroids (self.vars.means), we apply gradient descent on - # the mini-batch of input 'x', which yields the following: - # new_centroid = centroid + (1 - decay) * (x_mean - centroid) - # where x_mean is the average over all the input vectors closest to this - # centroid. - # - # Note that this approach is equivalent with backprop via - # loss = tf.math.reduce_mean( - # tf.math.squared_difference(tf.stop_gradient(x), nearest_centroid))) - # , except that here the learning rate is independently set via 'decay'. - - # Ensure that the padded positions are not used to update the centroids. - nearest_one_hot = py_utils.ApplyPadding(paddings_4d, nearest_one_hot) - - # Sum away batch and sequence length dimensions to get per cluster count. - # Shape: [N, K] - per_cluster_count = tf.reduce_sum(nearest_one_hot, axis=[0, 1]) - summary_utils.histogram('k_means/per_cluster_vec_count', per_cluster_count) - - # Sum of the input 'x' per each closest centroid. - sum_x = tf.einsum('BLNK, BLNH -> NKH', nearest_one_hot, x) - - if py_utils.use_tpu(): - per_cluster_count = tf.tpu.cross_replica_sum(per_cluster_count) - sum_x = tf.tpu.cross_replica_sum(sum_x) - - # If per_cluster_count for a cluster is 0, then 'nearest_one_hot' in that - # cluster's position will always be 0, hence 'sum_x' in that dimension will - # be 0. - new_means = sum_x / tf.maximum( - tf.constant(1.0, dtype=per_cluster_count.dtype), - tf.expand_dims(per_cluster_count, axis=-1)) - - # We use exponential moving average. TODO(zhouwk): investigate smooth this - # over an exponentially moving averaged per cluster count. - # - # Note that we intentionally do not normalize the means after this update - # as empirically this works better. - update_means_diff = (1.0 - p.decay) * (new_means - theta.means) - return py_utils.with_dependencies( - [tf.assign_add(self.vars.means, update_means_diff)], - dists), k_means_loss - - -def ComputeSparseAttention(q, k, v, sparsity_indices, paddings=None): - """Computes attention according to a sparsity pattern. - - We use the following capital letters to denote shape parameters: - B = batch size - S = length of the source sequence - T = length of the target sequence - N = number of attention heads - H = dimensions of each attention head - K = number of clusters - W = attention window (K <= S) - - The 'sparsity_indices' is a tensor of integral type where the last dimension - contains W indices (W is the attention window) for each corresponding position - along S in 'k' that the query is allowed to attend to. - - For example, if sparsity_indices[batch_idx, target time step, head_idx] = - [1, 7, 8], it means that token in the query attends to values with indices - 1, 7, and 8, and the attention window here is 3. - - The valid values in 'sparsity_indices' are [-1, S-1]. Note that the value -1 - is reserved to mean paddings, distinct from the value (S-1). - - For example, if W=S and 'sparsity_indices' contains range(S) on the last - dimension, this degenerates to the original full attention. - - We require that 'sparsity_indices' does not contain duplicates (except for -1 - to indicate paddings), but we do not require 'sparsity_indices' to be sorted. - - Note that this implementation is flexible and geneic but is not optimized for - time or space complexity. Please consider grouping queries that attend to the - same subset of values first for efficiency. - - Args: - q: (projected) queries, [B, T, N, H]; - k: (projected) keys, [B, S, N, H]; - v: (projected) values, [B, S, N, H]; - sparsity_indices: [B, T, N, W], where W is the attention window; - paddings: paddings for keys, [B, S] if not None. - - Returns: - output: the encoded output, [B, T, N, H]. - atten_probs: the attention weights, [B, T, N, S]. - """ - q = tf.convert_to_tensor(q) - k = tf.convert_to_tensor(k) - v = tf.convert_to_tensor(v) - sparsity_indices = tf.convert_to_tensor(sparsity_indices) - - k = py_utils.HasRank(k, 4) - _, source_length, _, dim_per_head = py_utils.GetShape(k, 4) - sparsity_indices = py_utils.HasRank(sparsity_indices, 4) - batch_size, target_length, num_heads, attention_window = py_utils.GetShape( - sparsity_indices, 4) - py_utils.assert_less_equal( - attention_window, source_length, - 'The provided sparsity_indices has attention window ' - ' > source length. This is likely an error.') - - # To prepare for gathering the relevant vectors from 'k', we prepare - # gather_idx of shape [B, T, N, W, 3] where the last dimension corresponds to - # slices in 'k' indexed by (batch index, source time step, head index), - # where the source length index comes from the original W dimension in - # 'sparsity_indices'. - seq_idx = tf.expand_dims(sparsity_indices, axis=-1) - # Overwrite the paddings -1 with valid gather indices (zeros). We will - # fix the logits with -inf in these positions later. - seq_idx = tf.where(seq_idx < 0, tf.zeros_like(seq_idx), seq_idx) - batch_idx = tf.reshape( - tf.range(0, batch_size, dtype=sparsity_indices.dtype), - [batch_size, 1, 1, 1, 1]) - batch_idx = tf.tile(batch_idx, - [1, target_length, num_heads, attention_window, 1]) - head_idx = tf.reshape( - tf.range(0, num_heads, dtype=sparsity_indices.dtype), - [1, 1, num_heads, 1, 1]) - head_idx = tf.tile(head_idx, - [batch_size, target_length, 1, attention_window, 1]) - # [B, T, N, W, 3], where last dimension is (batch index, source length index, - # head index). - gather_idx = tf.concat([batch_idx, seq_idx, head_idx], axis=-1) - - # Both the gathered k and v have shape [B, T, N, W, H] - k = tf.gather_nd(k, gather_idx) - v = tf.gather_nd(v, gather_idx) - - if paddings is None: - paddings = tf.zeros([batch_size, source_length]) - paddings = tf.convert_to_tensor(paddings) - paddings = tf.expand_dims(paddings, axis=-1) - # [B, S, N] - paddings = tf.tile(paddings, [1, 1, num_heads]) - # [B, T, N, W] - paddings = tf.gather_nd(paddings, gather_idx) - - logits = tf.einsum('BTNH, BTNWH -> BTNW', q, k) - logits *= tf.math.rsqrt(tf.cast(dim_per_head, q.dtype)) - - very_negative_logits = ( - tf.ones_like(logits) * logits.dtype.max * - tf.constant(-0.7, dtype=logits.dtype)) - padded_logits = tf.where( - tf.math.logical_or(sparsity_indices < 0, paddings > 0.0), - very_negative_logits, logits) - - # [B, T, N, W] - atten_probs = tf.nn.softmax(padded_logits, name='attention_weights') - atten_probs = tf.where(sparsity_indices < 0, tf.zeros_like(logits), - atten_probs) - output = tf.einsum('BTNW, BTNWH -> BTNH', atten_probs, v) - - # Scatter 'atten_probs' back into the original source length. - # [B, T, N, W, 1] - batch_idx = tf.tile( - tf.range(batch_size)[:, None, None, None, None], - [1, target_length, num_heads, attention_window, 1]) - # [B, T, N, W, 1] - target_seq_idx = tf.tile( - tf.range(target_length)[None, :, None, None, None], - [batch_size, 1, num_heads, attention_window, 1]) - # [B, T, N, W, 1] - head_idx = tf.tile( - tf.range(num_heads)[None, None, :, None, None], - [batch_size, target_length, 1, attention_window, 1]) - # seq_idx: [B, T, N, W, 1] - # [B, T, N, W, 4] - scatter_idx = tf.concat([batch_idx, target_seq_idx, head_idx, seq_idx], -1) - # [B, T, N, S] - scattered_probs = tf.scatter_nd( - scatter_idx, atten_probs, - [batch_size, target_length, num_heads, source_length]) - return output, scattered_probs diff --git a/lingvo/core/attention_util_test.py b/lingvo/core/attention_util_test.py deleted file mode 100644 index b1507574..00000000 --- a/lingvo/core/attention_util_test.py +++ /dev/null @@ -1,511 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for attention_util.""" - -from absl.testing import parameterized - -from lingvo import compat as tf -from lingvo.core import attention_util -from lingvo.core import test_utils - -import numpy as np - -FLAGS = tf.flags.FLAGS - - -class RelPositionBiasTest(test_utils.TestCase, parameterized.TestCase): - - def testBasic(self): - with self.session(): - t = 3 - # [BTNH]. - content = tf.linalg.diag(tf.ones([t]))[None, :, None, :] - # [LNH]. - abs_pos_emb = tf.reshape( - tf.range(t * (2 * t - 1), dtype=tf.float32), [2 * t - 1, 1, t]) - tf.logging.info('content=%s abs_pos_emb=%s', content.eval(), - abs_pos_emb.eval()) - self.assertAllClose([[[[6., 3., 0.], [10., 7., 4.], [14., 11., 8.]]]], - attention_util.RelPositionBias(content, - abs_pos_emb).eval()) - - -def OracleAttentionLogits(query, - key, - abs_pos_emb, - content_bias, - positional_bias, - skip_term_b=False): - """Computes expected attention logits using non-vectorized approach.""" - batch, seqlen, num_heads, _ = query.shape - tgtlen, srclen = seqlen, seqlen - - logits = np.zeros((batch, num_heads, tgtlen, srclen)) - - for b in range(batch): - for n in range(num_heads): - for i in range(tgtlen): - for j in range(srclen): - offset = seqlen - 1 - pos_emb = abs_pos_emb[i - j + offset] - logits[b][n][i][j] = np.dot(query[b][i][n], key[b][j][n]) - if not skip_term_b: - logits[b][n][i][j] += np.dot(query[b][i][n], pos_emb[n]) - if content_bias is not None: - logits[b][n][i][j] += np.dot(content_bias[n], key[b][j][n]) - if positional_bias is not None: - logits[b][n][i][j] += np.dot(positional_bias[n], pos_emb[n]) - return logits - - -class TransformerXLRelativeAttentionTest(test_utils.TestCase, - parameterized.TestCase): - - def setUp(self): - super().setUp() - self.input_dim = 32 - self.num_heads = 4 - self.batch = 4 - self.seqlen = 16 - - def _GetTestInputs(self): - np.random.seed(FLAGS.test_random_seed) - query = 3 * np.random.rand(self.batch, self.seqlen, self.num_heads, - self.input_dim).astype(np.float32) - key = 5 * np.random.rand(self.batch, self.seqlen, self.num_heads, - self.input_dim).astype(np.float32) - abs_pos_emb = 7 * np.random.rand(2 * self.seqlen - 1, self.num_heads, - self.input_dim).astype(np.float32) - content_bias = 11 * np.random.rand(self.num_heads, self.input_dim).astype( - np.float32) - positional_bias = 13 * np.random.rand(self.num_heads, - self.input_dim).astype(np.float32) - return query, key, abs_pos_emb, content_bias, positional_bias - - @parameterized.named_parameters( - ('Base', False), - ('Lite', True), - ) - def testTransformerXL(self, skip_term_b): - (query, key, abs_pos_emb, content_bias, - positional_bias) = self._GetTestInputs() - expected = OracleAttentionLogits(query, key, abs_pos_emb, content_bias, - positional_bias, skip_term_b) - actual_t = attention_util.AttenLogitsTransformerXL(query, key, abs_pos_emb, - content_bias, - positional_bias, - skip_term_b) - with self.session() as sess: - actual = sess.run(actual_t) - self.assertAllClose(expected, actual) - - def testRPE(self): - (query, key, abs_pos_emb, _, _) = self._GetTestInputs() - expected = OracleAttentionLogits(query, key, abs_pos_emb, None, None) - actual_t = attention_util.AttenLogitsRPE(query, key, abs_pos_emb) - with self.session() as sess: - actual = sess.run(actual_t) - self.assertAllClose(expected, actual) - - -class BlockUtilsTest(test_utils.TestCase, parameterized.TestCase): - - @parameterized.named_parameters( - ('single_block', 7), - ('one_frame_block', 1), - ('two_frame_blocks', 2), - ) - def testConvertToBlocks(self, block_size): - x_val = np.random.random([2, 6, 2, 3, 4]) - with self.session() as sess: - x = tf.convert_to_tensor(x_val, tf.float32) - x_blocks = attention_util.ConvertToBlocks(x, block_size) - x_blocks_val = sess.run(x_blocks) - # Check shape. - batch_size = x_val.shape[0] - other_dims = x_val.shape[2:] - num_blocks = int(np.ceil(x_val.shape[1] / float(block_size))) - expected_shape = (batch_size, num_blocks, block_size) + other_dims - self.assertAllEqual(expected_shape, x_blocks_val.shape) - - # Check values. - x_recover = x_blocks_val.reshape((x_blocks_val.shape[0], -1) + - x_blocks_val.shape[3:]) - x_recover = x_recover[:, :x_val.shape[1], ...] - self.assertAllClose(x_val, x_recover) - - @parameterized.named_parameters( - ('single_block', 7, 2, 1), - ('single_frame_context', 1, 1, 0), - ('other_case_1', 3, 4, 1), - ('other_case_2', 4, 2, 4), - ) - def testExtractBlockContext(self, block_size, left_context, right_context): - x_val = np.random.random([2, 6, 2, 3, 4]) - with self.session() as sess: - x = tf.convert_to_tensor(x_val, tf.float32) - x_context = attention_util.ExtractBlockContext(x, block_size, - left_context, - right_context) - x_context_val = sess.run(x_context) - # Check shape. - batch_size = x_val.shape[0] - other_dims = x_val.shape[2:] - num_blocks = int(np.ceil(x_val.shape[1] / float(block_size))) - context_size = block_size + left_context - 1 + right_context - expected_shape = (batch_size, num_blocks, context_size) + other_dims - self.assertAllEqual(expected_shape, x_context_val.shape) - - # Check values block by block. - for block_idx in range(num_blocks): - context_start = block_idx * block_size - left_context + 1 - context_end = (block_idx + 1) * block_size + right_context - slice_start = max(0, context_start) - slice_end = min(x_val.shape[1], context_end) - expected_val = x_val[:, slice_start:slice_end, ...] - actual_val = x_context_val[:, block_idx, ...] - # remove paddings - front_padding = slice_start - context_start - back_padding = context_end - slice_end - actual_val = actual_val[:, front_padding:context_size - back_padding, ...] - self.assertAllClose(expected_val, actual_val) - - def _getReferenceCausalPadding(self, seq_len, block_size, left_context, - right_context): - num_blocks = int(np.ceil(seq_len / float(block_size))) - context_size = block_size + left_context - 1 + right_context - padding = np.ones((num_blocks, block_size, context_size)) - - for i in range(num_blocks): - for j in range(block_size): - actual_src_pos = j + i * block_size - if actual_src_pos < seq_len: - for k in range(context_size): - actual_tgt_pos = k + i * block_size - (left_context - 1) - if 0 <= actual_tgt_pos and actual_tgt_pos < seq_len: - diff = actual_src_pos - actual_tgt_pos - if -right_context <= diff and diff < left_context: - padding[i, j, k] = 0 - - return padding - - @parameterized.named_parameters( - ('single_block', 6, 9, 2, 1), - ('single_frame_block', 6, 1, 2, 1), - ('single_frame_context', 6, 1, 1, 0), - ('other_case_1', 6, 3, 4, 1), - ('other_case_2', 6, 4, 2, 4), - ) - def testMakeCausalPadding(self, seq_len, block_size, left_context, - right_context): - with self.session() as sess: - seq_len_t = tf.convert_to_tensor(seq_len) - padding = attention_util.MakeCausalPadding(seq_len_t, block_size, - left_context, right_context) - padding_val = sess.run(padding) - - ref_padding = self._getReferenceCausalPadding(seq_len, block_size, - left_context, right_context) - self.assertAllEqual(ref_padding, padding_val) - - -class KMeansClusteringForAttenTest(test_utils.TestCase): - - def testFProp(self): - p = attention_util.KMeansClusteringForAtten.Params() - p.name = 'k_means' - p.num_clusters = 2 - p.dim_per_head = 4 - p.num_heads = 3 - batch_size = 5 - seq_length = 6 - x = np.random.rand(batch_size, seq_length, p.num_heads, - p.dim_per_head).astype(np.float32) - k_means = p.Instantiate() - - with self.session(): - dists, loss = k_means.FProp(k_means.theta, x, update=True) - self.evaluate(tf.global_variables_initializer()) - dists, loss = self.evaluate([dists, loss]) - self.assertEqual(dists.shape, - (batch_size, seq_length, p.num_heads, p.num_clusters)) - self.assertEqual(loss.shape, ()) - - def testFPropFixedInput(self): - p = attention_util.KMeansClusteringForAtten.Params() - p.name = 'k_means' - p.num_clusters = 3 - p.dim_per_head = 6 - p.num_heads = 4 - p.decay = 0.5 - k_means = p.Instantiate() - batch_size = 2 - seq_length = 5 - - with self.session(): - x = np.random.rand(batch_size, seq_length, p.num_heads, - p.dim_per_head).astype(np.float32) - self.evaluate(tf.global_variables_initializer()) - fixed_loss = None - for _ in range(10): - dists, loss = k_means.FProp(k_means.theta, x, update=False) - dists, loss = self.evaluate([dists, loss]) - if not fixed_loss: - fixed_loss = loss - else: - # If we do not update, the loss remain fixed. - self.assertEqual(loss, fixed_loss) - prev_loss = fixed_loss - self.evaluate(k_means.FProp(k_means.theta, x, update=True)) - for _ in range(5): - dists, loss = k_means.FProp(k_means.theta, x, update=True) - _, loss = self.evaluate([dists, loss]) - # If we update the centroids, the loss should strictly decrease. - self.assertGreater(prev_loss - loss, 1e-5) - prev_loss = loss - - def testFPropClustering(self): - p = attention_util.KMeansClusteringForAtten.Params() - p.name = 'k_means' - p.num_clusters = 2 - p.dim_per_head = 3 - p.num_heads = 2 - p.decay = 0.8 - k_means = p.Instantiate() - batch_size = 3 - seq_length = 3 - - with self.session() as sess: - self.evaluate(tf.global_variables_initializer()) - - def _GenInput(): - # We randomly generate inputs such that head 0 is clustered - # around (±1/√2, ±1/√2, ∓√2), while head 1 is clustered around - # (∓-√2, ±1/√2, ±1/√2). - noise = 0.05 * np.random.rand(batch_size, seq_length, p.num_heads, - p.dim_per_head).astype(np.float32) - x1 = np.random.binomial(1, 0.5, [batch_size, seq_length, 1, 1]) * 2 - 1 - x1 = np.tile( - np.array([1., 1., -1.], dtype=np.float32), - [batch_size, seq_length, 1, 1]) * x1 - x2 = np.random.binomial(1, 0.5, [batch_size, seq_length, 1, 1]) * 2 - 1 - x2 = np.tile( - np.array([-1., 1., 1.], dtype=np.float32), - [batch_size, seq_length, 1, 1]) * x2 - x = np.concatenate([x1, x2], axis=2) + noise - return x.astype(np.float32) - - for _ in range(25): - _, loss = sess.run( - k_means.FProp(k_means.theta, _GenInput(), update=True)) - final_means = k_means.theta.means.eval() - # We assert that the centroids are close to the true centers. - self.assertAllClose( - np.abs(final_means), [[[0.71, 0.71, 1.41], [0.71, 0.71, 1.41]], - [[1.41, 0.71, 0.71], [1.41, 0.71, 0.71]]], - rtol=0.03, - atol=0.03) - self.assertLess(loss, 0.005) - - def testFPropPadding(self): - p = attention_util.KMeansClusteringForAtten.Params() - p.name = 'k_means' - p.num_clusters = 2 - p.dim_per_head = 3 - p.num_heads = 1 - p.decay = 0.7 - k_means = p.Instantiate() - batch_size = 3 - seq_length = 5 - - with self.session() as sess: - self.evaluate(tf.global_variables_initializer()) - - def _GenInput(): - # We randomly generate inputs such that inputs are clustered - # around (±1/√2, ±1/√2, ∓√2) or (∓-√2, ±1/√2, ±1/√2) with one of them - # hidden by padding. - paddings = np.random.binomial(1, 0.5, [batch_size, seq_length]).astype( - np.float32) - x = np.expand_dims(np.expand_dims(paddings, axis=-1), axis=-1) - # When padding is 0, we generate (∓1, 1, ±1); when padding is 1, we - # generate (±1, 1, ∓1). - x = np.concatenate([2 * x - 1, np.ones_like(x), 1 - 2 * x], axis=-1) - x *= np.random.binomial(1, 0.5, [batch_size, seq_length, 1, 1]) * 2 - 1 - return x, paddings - - for _ in range(30): - x, paddings = _GenInput() - self.assertEqual(x.shape, - (batch_size, seq_length, p.num_heads, p.dim_per_head)) - self.assertEqual(paddings.shape, (batch_size, seq_length)) - _, loss1 = sess.run( - k_means.FProp(k_means.theta, x, paddings, update=True)) - means1 = k_means.theta.means.eval() - - # We reverse the padding to hide the other half. - for _ in range(40): - x, paddings = _GenInput() - _, loss2 = sess.run( - k_means.FProp(k_means.theta, x, 1.0 - paddings, update=True)) - means2 = k_means.theta.means.eval() - - # We compute the loss using the previous input centering on - # different centroids. The squared distance should be 3. - _, loss3 = sess.run( - k_means.FProp(k_means.theta, x, paddings, update=False)) - - self.assertAllClose( - np.abs(means1), [[[1.41, 0.71, 0.71], [1.41, 0.71, 0.71]]], - rtol=0.03, - atol=0.03) - self.assertLess(loss1, 1e-5) - self.assertAllClose( - np.abs(means2), [[[0.71, 0.71, 1.41], [0.71, 0.71, 1.41]]], - rtol=0.03, - atol=0.03) - self.assertLess(loss2, 1e-5) - - self.assertAllClose(loss3, 3.0, 1e-4, 1e-4) - - def testFPropClusteringEmptyCluster(self): - p = attention_util.KMeansClusteringForAtten.Params() - p.name = 'k_means' - p.num_clusters = 10 - p.dim_per_head = 3 - p.num_heads = 1 - p.decay = 0.8 - k_means = p.Instantiate() - batch_size = 3 - seq_length = 4 - - # All of our inputs are (1, 1, -1) - x = np.ones([batch_size, seq_length, 1, 1], dtype=np.float32) - x = np.concatenate([x, x, -x], axis=-1) - with self.session() as sess: - self.evaluate(tf.global_variables_initializer()) - for _ in range(30): - dists, loss = sess.run(k_means.FProp(k_means.theta, x, update=True)) - means = k_means.theta.means.eval() - idx = np.argmin(dists, axis=-1) - idx_1 = idx[0, 0, 0] - # We assert that 'dists' achieves minimum all at the same cluster. - self.assertAllEqual(idx, - np.array(idx_1 * np.ones([batch_size, seq_length, 1]))) - # We assert that at this index the centroid is close to (1/√2, 1/√2, -√2). - means = np.squeeze(means[:, idx_1, :]) - self.assertAllClose(means, [0.71, 0.71, -1.41], rtol=0.03, atol=0.03) - self.assertLess(loss, 1e-4) - - -class ComputeSparseAttention(test_utils.TestCase): - - def testBasics(self): - batch_size = 3 - source_length = 5 - target_length = 4 - num_heads = 2 - dim_per_head = 3 - q = np.random.rand(batch_size, target_length, num_heads, - dim_per_head).astype(np.float32) - k = np.random.rand(batch_size, source_length, num_heads, - dim_per_head).astype(np.float32) - v = np.random.rand(batch_size, source_length, num_heads, - dim_per_head).astype(np.float32) - # attention window = 2 - sparsity_indices = np.concatenate([ - np.zeros([batch_size, target_length, num_heads, 1], dtype=np.int32), - np.ones([batch_size, target_length, num_heads, 1], dtype=np.int32), - ], - axis=-1) - - with self.session() as sess: - out, probs = sess.run( - attention_util.ComputeSparseAttention(q, k, v, sparsity_indices)) - self.assertEqual(out.shape, - (batch_size, target_length, num_heads, dim_per_head)) - self.assertEqual(probs.shape, - (batch_size, target_length, num_heads, source_length)) - # attention weights sum to 1. - self.assertAllClose( - np.sum(probs, axis=-1), - np.ones([batch_size, target_length, num_heads])) - - # attention window = 4, but last two are always paddings. - sparsity_indices = np.concatenate([ - sparsity_indices, - -np.ones([batch_size, target_length, num_heads, 2], dtype=np.int32), - ], - axis=-1) - with self.session() as sess: - out2, probs2 = sess.run( - attention_util.ComputeSparseAttention(q, k, v, sparsity_indices)) - # We assert that the encoded outputs are the same as before, - # and the attention weights are 0 on the padded positions. - self.assertAllClose(out, out2) - self.assertAllClose(probs, probs2) - - # attention window = 4. - sparsity_indices = np.tile( - np.arange(4, dtype=np.int32), [batch_size, target_length, num_heads, 1]) - # but position 2 and 3 are padded. - paddings = np.tile([0., 0., 1., 1., 0.], [batch_size, 1]) - with self.session() as sess: - out3, probs3 = sess.run( - attention_util.ComputeSparseAttention(q, k, v, sparsity_indices, - paddings)) - # We assert that the encoded outputs and attention weights are the same - # as before. - self.assertAllClose(out2, out3) - self.assertAllClose(probs2, probs3) - - def testFullAttention(self): - batch_size = 4 - source_length = 7 - target_length = 6 - num_heads = 3 - dim_per_head = 5 - q = np.random.rand(batch_size, target_length, num_heads, - dim_per_head).astype(np.float32) - k = np.random.rand(batch_size, source_length, num_heads, - dim_per_head).astype(np.float32) - v = np.random.rand(batch_size, source_length, num_heads, - dim_per_head).astype(np.float32) - # attention window = source length, randomly permutated - # np.arange(source_length) - sparsity_indices = np.tile( - np.random.permutation(source_length).astype(np.int32), - [batch_size, target_length, num_heads, 1]) - - with self.session() as sess: - out, probs = sess.run( - attention_util.ComputeSparseAttention(q, k, v, sparsity_indices)) - - # compute full attention in numpy - expected_logit = np.einsum('BTNH, BSNH -> BTNS', q, k) - expected_logit /= np.sqrt(dim_per_head) - elexp = np.exp(expected_logit) - expected_probs = elexp / np.expand_dims(np.sum(elexp, axis=-1), axis=-1) - expected_output = np.einsum('BTNS, BSNH -> BTNH', expected_probs, v) - - # We assert that the output is close to the full attention, - # since our sparsity_indices is range(source_length) - self.assertAllClose(probs, expected_probs) - self.assertAllClose(out, expected_output) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/base_decoder.py b/lingvo/core/base_decoder.py deleted file mode 100644 index eb434285..00000000 --- a/lingvo/core/base_decoder.py +++ /dev/null @@ -1,394 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Common decoder interface.""" - -import collections - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import beam_search_helper -from lingvo.core import py_utils -from lingvo.core import target_sequence_sampler - -# metrics: Dict[Text, Tuple[float, float]] A dict of named metrics, which must -# include 'loss'. The value of the dict is (metric_val, count), where -# metric_val is the sum of the metric over all examples, and count is the -# number of examples seen. The mean value of the metric is metric_val/count. -# This is the first output of ComputeLoss. -# predictions: Union[Tensor, Dict[Text, Tensor], NestedMap] This is the output -# of ComputePredictions. -# per_sequence: Dict[Text, Tensor] This is the second output of ComputeLoss. -DecoderOutput = collections.namedtuple( - 'DecoderOutput', - ['metrics', 'predictions', 'per_sequence'], -) - - -class BaseDecoder(base_layer.BaseLayer): - """Base class for all decoders.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'packed_input', False, 'If True, decoder and all layers support ' - 'multiple examples in a single sequence.') - return p - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Sets the vocab size and wpm model in the params. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model target vocabulary params updated with the vocab size and wpm model. - """ - raise NotImplementedError('Abstract method') - - def FProp(self, theta, encoder_outputs, targets): - """Decodes `targets` given encoded source. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: a NestedMap computed by encoder. - targets: A NestedMap containing additional inputs to the decoder, - such as the targets being predicted. - - Returns: - A DecoderOutput namedtuple. - """ - predictions = self.ComputePredictions(theta, encoder_outputs, targets) - metrics, per_sequence = self.ComputeLoss(theta, predictions, targets) - return DecoderOutput( - metrics=metrics, predictions=predictions, per_sequence=per_sequence) - - def ComputePredictions(self, theta, encoder_outputs, targets): - raise NotImplementedError('Abstract method: %s' % type(self)) - - def ComputeLoss(self, theta, predictions, targets): - raise NotImplementedError('Abstract method: %s' % type(self)) - - -class BaseBeamSearchDecoder(BaseDecoder): - """Decoder that does beam search.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('target_sos_id', 1, 'Id of the target sequence sos symbol.') - p.Define('target_eos_id', 2, 'Id of the target sequence eos symbol.') - # TODO(rpang): remove target_seq_len and use beam_search.target_seq_len - # instead. - p.Define('target_seq_len', 0, 'Target seq length.') - p.Define('beam_search', beam_search_helper.BeamSearchHelper.Params(), - 'BeamSearchHelper params.') - p.Define('greedy_search', beam_search_helper.GreedySearchHelper.Params(), - 'GreedySearchHelper params.') - p.Define('target_sequence_sampler', - target_sequence_sampler.TargetSequenceSampler.Params(), - 'TargetSequenceSampler params.') - p.Define( - 'bias_only_if_consistent', True, 'BeamSearchBiased bias is only' - 'applied if the hypothesis has been consistent with targets so far.') - return p - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Sets the vocab size and wpm model in the params. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model target vocabulary params updated with the vocab size and wpm model. - """ - raise NotImplementedError('Abstract method') - - def __init__(self, params): - super().__init__(params) - p = self.params - p.beam_search.target_seq_len = p.target_seq_len - p.beam_search.target_sos_id = p.target_sos_id - p.beam_search.target_eos_id = p.target_eos_id - self.CreateChild('beam_search', p.beam_search) - p.greedy_search.target_seq_len = p.target_seq_len - p.greedy_search.target_sos_id = p.target_sos_id - p.greedy_search.target_eos_id = p.target_eos_id - self.CreateChild('greedy_search', p.greedy_search) - p.target_sequence_sampler.target_seq_len = p.target_seq_len - p.target_sequence_sampler.target_sos_id = p.target_sos_id - p.target_sequence_sampler.target_eos_id = p.target_eos_id - self.CreateChild('target_sequence_sampler', p.target_sequence_sampler) - - def AddExtraDecodingInfo(self, encoder_outputs, targets): - """Adds extra decoding information to encoded_outputs. - - Args: - encoder_outputs: a NestedMap computed by encoder. - targets: a NestedMap containing target input fields. - - Returns: - encoder_ouputs with extra information used for decoding. - """ - return encoder_outputs - - def BeamSearchDecode(self, encoder_outputs, num_hyps_per_beam_override=0): - """Performs beam search based decoding. - - Args: - encoder_outputs: the outputs of the encoder. - num_hyps_per_beam_override: If set to a value <= 0, this parameter is - ignored. If set to a value > 0, then this value will be used to override - p.num_hyps_per_beam. - - Returns: - `.BeamSearchDecodeOutput`, A namedtuple whose elements are tensors. - """ - return self.BeamSearchDecodeWithTheta(self.theta, encoder_outputs, - num_hyps_per_beam_override) - - def BeamSearchDecodeWithTheta(self, - theta, - encoder_outputs, - num_hyps_per_beam_override=0): - return self.beam_search.BeamSearchDecode(theta, encoder_outputs, - num_hyps_per_beam_override, - self._InitBeamSearchStateCallback, - self._PreBeamSearchStepCallback, - self._PostBeamSearchStepCallback) - - def GreedySearchDecode(self, encoder_outputs): - """Performs beam search based decoding. - - Args: - encoder_outputs: the outputs of the encoder. - - Returns: - greedy search decode output. - """ - return self.GreedySearchDecodeWithTheta(self.theta, encoder_outputs) - - def GreedySearchDecodeWithTheta(self, theta, encoder_outputs): - return self.greedy_search.GreedySearchDecode( - theta, encoder_outputs, - self._InitBeamSearchStateCallback, - self._PreBeamSearchStepCallback, - self._PostBeamSearchStepCallback) - - def SampleTargetSequences(self, theta, encoder_outputs, random_seed): - """Performs target sequence sampling. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - encoder_outputs: a NestedMap computed by encoder. - random_seed: a scalar int32 tensor representing the random seed. - - Returns: - A NestedMap containing the following tensors - - - 'ids': [batch, max_target_length] of int32, representing the target - sequence ids, not including target_sos_id, but maybe ending with - target_eos_id if target_eos_id is sampled. - - 'paddings': [batch, max_target_length] of 0/1, where 1 represents - a padded timestep. - """ - return self.target_sequence_sampler.Sample( - theta, encoder_outputs, random_seed, self._InitBeamSearchStateCallback, - self._PreBeamSearchStepCallback, self._PostBeamSearchStepCallback) - - def BeamSearchDecodeBiased(self, - encoder_outputs, - num_hyps_per_beam_override=0): - """Performs beam-search decoding while biasing towards provided targets. - - Args: - encoder_outputs: a NestedMap computed by encoder. Must include `targets`, - which is used to bias beam search. - num_hyps_per_beam_override: If set to a value <= 0, this parameter is - ignored. If set to a value > 0, then this value will be used to override - `p.num_hyps_per_beam`. - - Returns: - BeamSearchDecodeOutput, a namedtuple containing the decode results. - """ - p = self.params - - targets = encoder_outputs.targets - targets.weights *= (1.0 - targets.paddings) - - def PadToTargetSeqLen(tensor, constant): - length = tf.shape(tensor)[1] - pad = tf.maximum(0, p.beam_search.target_seq_len - length) - return tf.pad(tensor, [[0, 0], [0, pad]], constant_values=constant) - - targets.labels = PadToTargetSeqLen(targets.labels, 0) - targets.weights = PadToTargetSeqLen(targets.weights, 0) - - def InitBeamSearchStateCallback(theta, encoder_outputs, num_hyps_per_beam): - """Wrapper for adding bias to _InitBeamSearchStateCallback. - - Exapnds state to track consistency of hypothesis with provided target. - - Args: - theta: A NestedMap object containing weights' values of this layer and - its children layers. - encoder_outputs: A NestedMap computed by encoder. - num_hyps_per_beam: An int, number hyps to keep for source sentence. - - Returns: - initial_results: a `.NestedMap` of initial results. - states: a `.NestedMap` of initial model states that the client - would like to keep track of for each hyp. The states relevant here - are: - time_step: A scalar indicating current step (=0 for initial state) of - decoder. Must be provided and maintained by super. - consistent: A boolean tensor of shape [tgt_batch, 1] which tracks - whether each hypothesis has exactly matched - encoder_outputs.targets - so far. - """ - initial_results, states = self._InitBeamSearchStateCallback( - theta, encoder_outputs, num_hyps_per_beam) - assert hasattr(states, 'time_step') - if tf.is_tensor(encoder_outputs.padding): - batch_size = tf.shape(encoder_outputs.padding)[1] - else: # Required for multisource models. - batch_size = tf.shape(list(encoder_outputs.padding.values())[0])[1] - num_hyps = batch_size * num_hyps_per_beam - # states.consistent is initially all True - states.consistent = tf.ones([ - num_hyps, - ], dtype=tf.bool) - return initial_results, states - - def PreBeamSearchStepCallback(theta, encoder_outputs, step_ids, states, - num_hyps_per_beam, *args, **kwargs): - """Wrapper for adding bias to _PreBeamSearchStateCallback. - - Biases results.log_probs towards provided encoder_outputs.targets. - - Args: - theta: a NestedMap of parameters. - encoder_outputs: a NestedMap computed by encoder. - step_ids: A tensor of shape [tgt_batch, 1]. - states: A `.NestedMap` of tensors representing states that the clients - would like to keep track of for each of the active hyps. - num_hyps_per_beam: Beam size. - *args: additional arguments to _PreBeamSearchStepCallback. - **kwargs: additional arguments to _PreBeamSearchStepCallback. - - Returns: - A tuple (results, out_states). - results: A `.NestedMap` of beam search results. - atten_probs: - The updated attention probs, of shape [tgt_batch, src_len]. - log_probs: - Log prob for each of the tokens in the target vocab. This is of - shape - [tgt_batch, vocab_size]. - out_states: a `.NestedMap` The updated states. The states relevant here - are: - time_step: A scalar indicating current step of decoder. Must be - provided and maintained by subclass. - consistent: A boolean vector of shape [tgt_batch, ] which tracks - whether each hypothesis has exactly matched - encoder_outputs.targets - so far. - """ - p = self.params - time_step = states.time_step - bs_results, out_states = self._PreBeamSearchStepCallback( - theta, encoder_outputs, step_ids, states, num_hyps_per_beam, *args, - **kwargs) - labels = encoder_outputs.targets.labels - weights = encoder_outputs.targets.weights - - def ApplyBias(): - """Bias and update log_probs and consistent.""" - - def TileForBeamAndFlatten(tensor): - tensor = tf.reshape(tensor, [1, -1]) # [1, src_batch] - tensor = tf.tile( - tensor, [num_hyps_per_beam, 1]) # [num_hyps_per_beam, src_batch] - tgt_batch = tf.shape(step_ids)[0] # num_hyps_per_beam*src_batch - return tf.reshape(tensor, [tgt_batch]) - - # Consistent if step_ids == labels from previous step - # TODO(navari): Consider updating consistent only if weights > 0. Then - # re-evaluate the need for bias_only_if_consistent=True. - # Note that prev_label is incorrrect for step 0 but is overridden later - prev_label = TileForBeamAndFlatten( - tf.gather(labels, tf.maximum(time_step - 1, 0), axis=1)) - is_step0 = tf.equal(time_step, 0) - local_consistence = tf.math.logical_or( - is_step0, tf.equal(prev_label, tf.squeeze(step_ids, 1))) - consistent = tf.math.logical_and(states.consistent, local_consistence) - - # get label, weight slices corresponding to current time_step - label = TileForBeamAndFlatten(tf.gather(labels, time_step, axis=1)) - weight = TileForBeamAndFlatten(tf.gather(weights, time_step, axis=1)) - if p.bias_only_if_consistent: - weight = weight * tf.cast(consistent, py_utils.FPropDtype(p)) - - # convert from dense label to sparse label probs - vocab_size = tf.shape(bs_results.log_probs)[1] - uncertainty = tf.constant(1e-10, py_utils.FPropDtype( - p)) # avoid 0 probs which may cause issues with log - label_probs = tf.one_hot( - label, - vocab_size, - on_value=1 - uncertainty, - off_value=uncertainty / - tf.cast(vocab_size - 1, py_utils.FPropDtype(p)), - dtype=py_utils.FPropDtype(p)) # [tgt_batch, vocab_size] - pred_probs = tf.exp(bs_results.log_probs) - - # interpolate predicted probs and label probs - weight = tf.expand_dims(weight, 1) - probs = py_utils.with_dependencies([ - py_utils.assert_less_equal(weight, 1.), - py_utils.assert_greater_equal(weight, 0.) - ], (1.0 - weight) * pred_probs + weight * label_probs) - return tf.math.log(probs), consistent - - def NoApplyBias(): - """No-op. Return original log_probs and consistent.""" - return bs_results.log_probs, states.consistent - - log_probs, consistent = tf.cond( - tf.reduce_all(tf.equal(weights, 0.0)), NoApplyBias, ApplyBias) - bs_results.log_probs = log_probs - out_states.consistent = consistent - - return bs_results, out_states - - return self.beam_search.BeamSearchDecode(self.theta, encoder_outputs, - num_hyps_per_beam_override, - InitBeamSearchStateCallback, - PreBeamSearchStepCallback, - self._PostBeamSearchStepCallback) - - def InferenceAdditionalEncoder(self, feeds): - """Generate an inference graph for the additional encoder.""" - return py_utils.NestedMap(), py_utils.NestedMap() diff --git a/lingvo/core/base_input_generator.py b/lingvo/core/base_input_generator.py deleted file mode 100644 index e527b144..00000000 --- a/lingvo/core/base_input_generator.py +++ /dev/null @@ -1,1205 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Input generators. - -There are three types of batch sizes: - -* Device split batch size: Defined by Params() and is the batch size - on each device/TPU core. BaseInputGenerator.params.batch_size and - BaseSequenceInputGenerator.params.bucket_batch_limit specify per-split batch - size. - -* GlobalBatchSize: number of examples in a global batch. - -* InfeedBatchSize: global_batch_size // num_infeed_hosts, where - num_infeed_hosts is cluster.num_tpu_hosts if using per-host infeed with TPU, - otherwise num_infeed_hosts is 1. - -TODO(rpang): Deal with on packed_inputs. -""" - -import inspect - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import batch_utils -from lingvo.core import datasource -from lingvo.core import hyperparams -from lingvo.core import input_generator_helper as ig_helper -from lingvo.core import inspect_utils -from lingvo.core import ops -from lingvo.core import py_utils -from lingvo.core import tokenizers -import tensorflow.compat.v1 as tf1 -import tensorflow.compat.v2 as tf2 - -# pylint: disable=g-direct-tensorflow-import -from tensorflow.python.ops import io_ops -from tensorflow.python.tpu import tpu_embedding as tpu_embedding_lib -from tensorflow.python.tpu import tpu_feed -# pylint: enable=g-direct-tensorflow-import - -DEFAULT_TOKENIZER_KEY = 'default' - - -class BaseInputGenerator(base_layer.BaseLayer): - """The abstract base input generator.""" - - @classmethod - def DefineInfeedParams(cls, p): - # TPU related infeed tuning. - # Supported use cases: - # - # Data parallelism (num_partitions=None) - # - single host (use_per_host_infeed=False, tpu_infeed_parallelism=1)) - # - multi host (use_per_host_infeed=False, tpu_infeed_parallelism>1) - # - per host (use_per_host_infeed=True) - # Model parallelism (num_partitions>1 where) - # - non-partitioned infeed (use_partitioned_infeed_queue=False): - # - Only first partition gets infeed (e.g. manual partition) - # - single host (use_per_host_infeed=False) - # - per host (use_per_host_infeed=True) - # - All partitions gets data parallel infeed (e.g. MoE) - # - single host not supported - # - per host (use_per_host_infeed=True, use_per_core_infeed=True) - # num_partitions should be set to number of partitions per replica - # - partitioned infeed (use_partitioned_infeed_queue=True) - # - single host (use_per_host_infeed=False) - # - per host (use_per_host_infeed=True) - # num_partitions should be set to number of partitions per replica - # and all partitions should exist on a single host - p.Define('use_per_host_infeed', False, - 'Whether run infeed op on each host.') - p.Define('use_per_core_infeed', False, - 'Whether to shard the infeed per TPU core instead of per replica') - p.Define('tpu_infeed_parallelism', 1, - 'Uses these many python threads to drive infeed concurrently.') - p.Define('use_partitioned_infeed_queue', False, 'Use partitioned infeed') - p.Define('num_partitions', None, 'Num partitions') - - @classmethod - def Params(cls): - """Defaults params for input generators.""" - p = super().Params() - p.name = 'input' - p.Define( - 'batch_size', 0, 'Batch size for a device split. This will be ' - 'scaled to match the accelarator hardware topology.') - p.Define( - 'num_samples', 0, - 'If non-zero, the dataset contains these many samples. ' - 'For test/eval dataset, if we want the test/evel job evaluate ' - 'the whole dataset, this param must be set precisely. Otherwise, ' - 'this param is optional.') - p.Define('resettable', False, - 'If True, the input generator must implement Reset().') - cls.DefineInfeedParams(p) - - p.Define('remote', hyperparams.Params(), - 'Params to configure remote input policy.') - pp = p.remote - pp.Define( - 'shardable_batch', True, - 'True if and only if this input generates simple batches whose 1st ' - 'dimension of every tensor in a batch is the batch dimension, and ' - 'other dimensions are always the same.') - pp.Define( - 'max_inflights_per_target', 32, 'The maximum number of ' - 'concurrent inflight remote input fetches per remote target.') - - return p - - def __init__(self, params): - super().__init__(params) - # parameter to tell the bprop one hot for all the files. - # TODO(ankurbpn): Initialize when using sources from mixed record yielders. - self._bprop_onehot = tf.constant([1], dtype=tf.float32) - # Each entry is a regular expression specifying the set of variables - # to bprop per data source. - self._bprop_variable_filters = [''] - # For TPU enqueue ops, we do not use graph collections, instead, we rely - # on this member variable. This is especially useful for - # executor-driven multiple programs, as we need more fine-grained - # access to drive the infeed for a specific program, rather than - # a single global collection across the graph. - self._tpu_infeed_op = None - # A list of InfeedQueues. - self._tpu_queues = [] - - # Set to true in GetProcessedInputBatch() (and thus _InputBatch()) - self._in_get_processed_input_batch = False - - def CommonInputOpArgs(self): - """Common input params.""" - return {} - - def GetBpropVariableFilters(self): - return self._bprop_variable_filters - - def GetInputSourceOneHot(self): - """Get the current bprop type of the input generator batch.""" - return self._bprop_onehot - - def GlobalBatchSize(self): - """Returns the total batch size (for stats), int or dynamic int tensor.""" - # Uses `InfeedBatchSize()` instead of calculating it from `p.batch_size` - # because the behavior would be overridden by subclasses. - global_batch_size = batch_utils.scale_infeed_to_global( - self.InfeedBatchSize(), self.params.use_per_host_infeed) - tf.logging.info('GlobalBatchSize {}'.format(global_batch_size)) - return global_batch_size - - def InfeedBatchSize(self): - """Returns the batch size of the input batch: int or dynamic int tensor.""" - batch_per_input = batch_utils.scale_split_to_infeed( - self.params.batch_size, self.params.use_per_host_infeed) - tf.logging.info('batch_per_input: %d', batch_per_input) - return batch_per_input - - def _InputBatch(self): - """The current input batch, not preprocessed. - - This is meant to be overridden by subclasses, but not called directly. - Callers should use `GetPreprocessedInputBatch()`. - - Returns: - A `.NestedMap` of input tensors. Each tensor's dim-0 must be the same - and denotes the batch dimension. - """ - raise NotImplementedError('Abstract method') - - def _PreprocessInputBatch(self, batch): - """Preprocesses input batch from _InputBatch. - - Args: - batch: A NestedMap containing input tensors in the format returned by - _InputBatch. - - Returns: - A NestedMap containing preprocessed inputs to feed to the model. - """ - return batch - - def GetPreprocessedInputBatch(self): - """Returns a NestedMap containing a preprocessed batch of inputs. - - These are the actual inputs fed to the model. - - Subclasses generally should not override this function directly. Instead, - override _InputBatch and maybe _PreprocessInputBatch. - """ - self._in_get_processed_input_batch = True - res = self._PreprocessInputBatch(self._InputBatch()) - self._in_get_processed_input_batch = False - return res - - @property - def tpu_number_of_shards(self): - p = self.params - cluster = self.cluster - num_tpu_hosts = cluster.num_tpu_hosts - num_infeed_hosts = num_tpu_hosts if p.use_per_host_infeed else 1 - shards = (cluster.total_worker_devices // num_infeed_hosts) - if p.use_partitioned_infeed_queue or not p.use_per_core_infeed: - shards = shards // cluster.num_devices_per_split - return shards - - def CreateTpuEnqueueOps(self): - """Create the host-side enqueue ops. - - This should be called in an outer non-TPU context. - """ - assert not self._tpu_queues, ('CreateTpuEnqueueOps should only be called ' - 'once.') - self._tpu_queues = [] - p = self.params - cluster = self.cluster - num_tpu_hosts = cluster.num_tpu_hosts - num_cores_per_host = cluster.total_worker_devices // num_tpu_hosts - tf.logging.info( - 'CreateTpuEnqueueOps num_splits_per_client={} ' - 'num_devices_per_split={} num_tpu_hosts={} use_per_host_infeed={}' - .format(cluster.num_splits_per_client, cluster.num_devices_per_split, - num_tpu_hosts, p.use_per_host_infeed)) - - assert num_tpu_hosts > 0, ('num_tpu_hosts: %d' % num_tpu_hosts) - if p.use_per_core_infeed: - if (not p.use_per_host_infeed) or p.use_partitioned_infeed_queue: - raise ValueError('use_per_core_infeed need to have use_per_host_infeed ' - 'but not use_partitioned_infeed_queue.') - if (cluster.num_devices_per_split > num_cores_per_host and - p.use_per_host_infeed): - tf.logging.fatal('Doesn\'t support per host infeed mode when ' - 'num_devices_per_split({}) > num_cores_per_host({}).' - 'Each host must be able to accommodate >= 1 split when ' - 'using per_host_infeed.'.format( - cluster.num_devices_per_split, num_cores_per_host)) - - shards = self.tpu_number_of_shards - tf.logging.info('shards {}'.format(shards)) - - input_ops_list = [] - tpu_embedding_collection = tf.get_collection(py_utils.TPU_EMBEDDING) - tpu_embedding = ( - tpu_embedding_collection[0] if tpu_embedding_collection else None) - - if num_tpu_hosts > 1 and tpu_embedding is not None: - if not p.use_per_host_infeed: - tf.logging.fatal( - 'TPU Embedding must be used with per_host_infeed with multiple ' - 'TPU host topologies.') - - tpu_emb_input_keys = ( - list(tpu_embedding.feature_to_config_dict.keys()) - if tpu_embedding is not None else []) - tf.logging.info('tpu_emb_input_keys: %r', tpu_emb_input_keys) - - num_infeed_hosts = num_tpu_hosts if p.use_per_host_infeed else 1 - tf.logging.info('num_infeed_hosts: %d', num_infeed_hosts) - for task_id in range(num_infeed_hosts): - host_device = '/task:{}/device:CPU:0'.format(task_id) - with tf.device(host_device): - self._batch = self.GetPreprocessedInputBatch() - if isinstance(self._batch, py_utils.NestedMap): - # Hack: bucket_keys and xxx.bucket_keys are not needed on TPU. - # Note that when MultiTaskData is used, bucket_keys will be at the - # second level of the dictionary. - self._batch = self._batch.FilterKeyVal( - lambda k, _: not k.endswith('bucket_keys')) - tf.logging.info('host_device: %s, batch: %r', host_device, self._batch) - - for k, x in self._batch.FlattenItems(): - assert x.shape.is_fully_defined(), ( - 'Shape must be fully defined: %s: %s' % (k, x)) - # TODO(cwhipkey): if it's a string (or other type not supported on - # TPU), drop it from feeding and on the other end add in an op that - # fails if used. - shapes = self._batch.Transform(lambda x: x.shape).Flatten() - dtypes = self._batch.Transform(lambda x: x.dtype).Flatten() - - tf.logging.info('host_device: %s infeed shapes: %r', host_device, - shapes) - tf.logging.info('host_device: %s infeed dtypes: %r', host_device, - dtypes) - - if p.use_partitioned_infeed_queue: - device_assignment = py_utils.GetTpuDeviceAssignment() - - host_device = device_assignment.host_device( - replica=0, job=tf.flags.FLAGS.tf_master) - host_id = int(host_device.split('/task:')[1].split('/device:')[0]) - tf.logging.info('host_id: {} host_device: {}'.format( - host_id, host_device)) - q = tpu_feed._PartitionedInfeedQueue( # pylint: disable=protected-access - number_of_tuple_elements=len(dtypes), - device_assignment=device_assignment, - host_id=host_id, - input_partition_dims=[ - [p.num_partitions] + [1] * (len(s) - 1) for s in shapes - ], - tuple_types=dtypes, - tuple_shapes=shapes) - else: - if p.use_per_core_infeed: - q = tpu_feed.InfeedQueue( - tuple_types=dtypes, - tuple_shapes=shapes, - number_of_partitions=p.num_partitions) - else: - q = tpu_feed.InfeedQueue(tuple_types=dtypes, tuple_shapes=shapes) - assert shards is not None - q.set_number_of_shards(shards) - - self._tpu_queues.append(q) - - if p.use_partitioned_infeed_queue: - input_ops = q.generate_enqueue_ops([self._batch.Flatten()]) - elif p.use_per_host_infeed: - # TODO(ylc/zhifengc): Add this to a policy module and test it. - def TPUOrdinalFunction(shard_index_in_host): - if p.use_per_core_infeed: - return shard_index_in_host - device_assignment = py_utils.GetTpuDeviceAssignment() - if device_assignment: - # We put both enqueue/dequeue ops at core 0 in each replica. - replica = device_assignment.lookup_replicas( - task_id, 0)[shard_index_in_host] # pylint: disable=cell-var-from-loop - return device_assignment.tpu_ordinal(replica=replica) - else: - return shard_index_in_host - - input_ops = q.split_inputs_and_generate_enqueue_ops( - self._batch.Flatten(), - placement_function=lambda x: host_device, # pylint: disable=cell-var-from-loop - tpu_ordinal_function=TPUOrdinalFunction) - else: - input_ops = q.split_inputs_and_generate_enqueue_ops( - self._batch.Flatten(), - device_assignment=py_utils.GetTpuDeviceAssignment()) - input_ops_list += input_ops - - tf.logging.info('input_ops_list %s', input_ops_list) - grouped_infeed_op = tf.group(*input_ops_list) - self._tpu_infeed_op = [] - for _ in range(p.tpu_infeed_parallelism): - self._tpu_infeed_op.append(grouped_infeed_op) - - def TpuDequeueBatch(self): - """Create TPU dequeue ops. - - This should only be called within a TPU context. - - Returns: - - A NestedMap of the input batch. - """ - assert self._tpu_queues, 'CreateTpuEnqueueOps must be called first.' - with tf.device(tf.tpu.core(0)): - # Note that the dequeue_tuple op on the TPU core - # only cares about the shape/types being dequeued - # which is why this is hard-coded to the first Queue. - tensors = self._tpu_queues[0].generate_dequeue_op() - return self._batch.Pack(tensors) - - def CreateTpuEmbeddingEnqueueOps(self): - """Creates the TpuEmbedding enqueue ops on the host. - - Note that this must be called after the instantiation of the - monolithic TPUEmbeddingLayer. - """ - p = self.params - cluster = self.cluster - num_tpu_hosts = cluster.num_tpu_hosts - num_infeed_hosts = num_tpu_hosts if p.use_per_host_infeed else 1 - - tpu_embedding_collection = tf.get_collection(py_utils.TPU_EMBEDDING) - tpu_embedding = ( - tpu_embedding_collection[0] if tpu_embedding_collection else None) - - enqueue_ops = [] - - if num_tpu_hosts > 1 and tpu_embedding is not None: - if not p.use_per_host_infeed: - tf.logging.fatal( - 'TPU Embedding must be used with per_host_infeed with multiple ' - 'TPU host topologies.') - tpu_emb_input_keys = ( - list(tpu_embedding.feature_to_config_dict.keys()) - if tpu_embedding is not None else []) - tf.logging.info('tpu_emb_input_keys: %r', tpu_emb_input_keys) - if not tpu_embedding: - return - - for task_id in range(num_infeed_hosts): - host_device = '/task:{}/device:CPU:0'.format(task_id) - with tf.device(host_device): - if isinstance(self._batch, py_utils.NestedMap): - # Hack: bucket_keys and xxx.bucket_keys are not needed on TPU. - # Note that when MultiTaskData is used, bucket_keys will be at the - # second level of the dictionary. - self._batch = self._batch.FilterKeyVal( - lambda k, _: not k.endswith('bucket_keys')) - tf.logging.info('host_device: %s, batch: %r', host_device, self._batch) - - enqueue_dict_per_core = [ - {} for _ in range(tpu_embedding.num_cores_per_host) - ] - num_cores_per_host = tpu_embedding.num_cores_per_host - for key in tpu_emb_input_keys: - feat = self._batch[key] - tpu_emb_feat_splitted = tf.split(feat, num_cores_per_host) - for core, split in enumerate(tpu_emb_feat_splitted): - # Dense to sparse. Note the assumption of a padding id. - sample_indices = tf.where(tf.not_equal(split, -1)) - embedding_indices = tf.gather_nd(split, sample_indices) - enqueue_data = tpu_embedding_lib.EnqueueData( - embedding_indices, sample_indices) - enqueue_dict_per_core[core][key] = enqueue_data - enqueue_ops += tpu_embedding.generate_enqueue_ops(enqueue_dict_per_core) - self._tpu_infeed_op.append(tf.group(*enqueue_ops)) - - @property - def tpu_infeed_op(self): - if self._tpu_infeed_op is not None: - return self._tpu_infeed_op - else: - raise ValueError('TPU infeed op not set. Call CreateTpuEnqueueOps first.') - - def SplitInputBatch(self, num_splits): - """Splits the current InputBatch into num_splits ways. - - Args: - num_splits: The number of splits. - - Returns: - A list of `.NestedMap`. Each `.NestedMap` represents the input - tensors in one split. - """ - assert num_splits >= 1 - - batch = self.GetPreprocessedInputBatch() - if num_splits == 1: - # Special case. No split is needed. - return [batch] - - assert not py_utils.use_tpu() - field_split = ig_helper.SplitTensors(batch.Flatten(), num_splits) - num_fields = len(field_split) - ret = [] - for j in range(num_splits): - split_flatten = [field_split[i][j] for i in range(num_fields)] - split = batch.Pack(split_flatten) - ret += [split] - return ret - - def Reset(self, tf_session): - """Reset the input-generator. - - Override so that the input_generator reproduces examples as if from a fresh - instantiation. - - Args: - tf_session: A tensorflow session. - """ - raise NotImplementedError() - - -class BaseInputGeneratorFromFiles(BaseInputGenerator): - """Base class for input generators that reads from files.""" - - @classmethod - def Params(cls): - """Defaults params for input generators.""" - p = super().Params() - p.Define( - # NOTE: file_pattern is deprecated. New params should use - # file_datasource instead. - # TODO(b/139345706) remove file_pattern parameter - 'file_pattern', - '', - 'A single file pattern string, a list of file pattern strings or a list' - ' of pairs or a list of tuples. Some of the cases may not be supported ' - 'Depending on the value of use_within_batch_mixing and use_chaining.' - 'In the later 2 cases, probablistic samples are from the inputs ' - 'proportional to their weights. Typically, values are binary ' - 'protocol buffers containing train/eval samples. Keys are not used.') - p.Define( - 'file_datasource', None, 'A DataSource describing the file sources ' - 'including any weights and bprop_variable_filters required.') - p.Define('file_random_seed', 301, - 'Random seed for shuffling the input data.') - p.Define( - 'file_buffer_size', 10000, - 'How many records are buffered for random shuffling. This param ' - 'affects how much RAM a train/test job needs. E.g., if an average ' - 'record is about 500KB, the buffer needs 5GB ram.') - p.Define( - 'file_buffer_size_in_seconds', 0, - 'If non-zero, keep enough records in the buffer to handle N seconds ' - 'worth of demand. E.g., if the training job is reading 1000 records ' - 'per second and this parameter is set to 10, the buffer is resized ' - 'to contain 10000 records. This parameter is useful when reading from ' - 'many data sources at different speeds, as it automatically tunes the ' - 'size of buffers to fit demand. The file_buffer_size parameter is an ' - 'upper bound to the buffer size.') - p.Define('file_parallelism', 16, 'How many files to read concurrently.') - p.Define( - 'bucket_adjust_every_n', 0, 'If non-zero, optimize the values of ' - 'bucket_upper_bound except the last one after every N records ' - 'based on the current input length distribution.') - p.Define( - 'flush_every_n', 0, 'If non-zero, flushes all batches buffered ' - 'so far every these many records are yielded.') - p.Define('num_batcher_threads', 1, 'Number of threads to use for input ' - 'record batcher.') - p.Define( - 'require_sequential_order', False, - 'If true, the input op is required to process the file glob as ' - 'well as the contents of each file in a deterministic sequential order.' - ' This is intended for unit tests. Setting this automatically disables ' - 'file_random_seed, file_buffer_size, file_parallelism, ' - 'num_batcher_threads, and requires a single file_pattern.') - p.Define( - 'repeat_count', -1, - 'Number of repetitions of a dataset before throwing OutOfRange error ' - 'when using require_sequential_order. Must only be set if ' - 'require_sequential_order is True.') - # TODO(b/139345706) when file_pattern is deleted use_within_batch_mixing - # will be specified by passing a WithinBatchMixingDataSource to - # p.file_datasource and this param should be deleted as well. - p.Define( - 'use_within_batch_mixing', False, 'Whether to mix records from ' - 'different input sources within batch or across batches (the ' - 'default option). This option only takes effect when file_pattern' - ' is a list of file patterns with weights.') - # TODO(b/139345706) when file_pattern is deleted use_chaining - # will be specified by passing a ChainingDataSource to - # p.file_datasource and this param should be deleted as well. - p.Define( - 'use_chaining', False, 'Whether to output records from ' - 'different input sources one after another, i.e., first all records ' - 'from a first input source, then all records from a second one, etc. ' - 'use_chaining does not guarantee that records from subsequent input ' - 'sources are placed in separate input batches. ' - 'This option only takes effect when file_pattern is a list of file ' - 'patterns.') - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.use_per_host_infeed and p.file_random_seed != 0: - raise ValueError('file_random_seed needs to be 0 when ' - 'use_per_host_infeed == True.') - - assert not (p.file_pattern and p.file_datasource - ), 'Only one of file_pattern and data_source can be specified' - - # TODO(b/139345706) remove support for file_pattern - if not p.file_datasource: - if isinstance(p.file_pattern, str): - p.file_datasource = datasource.SimpleDataSource.Params().Set( - file_pattern=p.file_pattern) - elif isinstance(p.file_pattern, (list, tuple)): - if all([isinstance(x, str) for x in p.file_pattern]): - # While this violates the documentation and intended use, there are - # subclasses that have used a tuple of strings, rather than a list of - # string, weight tuples. Rather than treating lists and tuples - # differently, support both here until p.file_pattern is removed. - p.file_datasource = datasource.SimpleDataSource.Params().Set( - file_pattern=','.join(p.file_pattern)) - elif p.use_within_batch_mixing: - assert not p.use_chaining, "Can't both use chaining and mixing" - - if max(list(map(len, p.file_pattern))) >= 3: - # Within batch mixing doesn't work with backprop filters, i.e. when - # file_pattern param contains a list of - # tuples. - raise ValueError('Expected a list of pairs, got %s' % - p.file_pattern) - - file_patterns, weights = (list(x) for x in zip(*p.file_pattern)) - - p.file_datasource = datasource.WithinBatchMixingDataSource.Params( - ).Set( - file_patterns=file_patterns, weights=weights) - elif p.use_chaining: - p.file_datasource = datasource.ChainingDataSource.Params().Set( - file_patterns=p.file_pattern) - else: - # Otherwise fall back to MixByWeight-based approach. - file_patterns = [] - weights = [] - bprop_variable_filters = [] - for input_entry in p.file_pattern: - if isinstance(input_entry, str): - raise ValueError( - 'Should explicitly specify weights, got string: %s' % - input_entry) - file_pattern, weight = input_entry[:2] - file_patterns.append(file_pattern) - weights.append(weight) - bprop_variable_filter = input_entry[2] if len( - input_entry) > 2 else '' - bprop_variable_filters.append(bprop_variable_filter) - p.file_datasource = datasource.CrossBatchMixingDataSource.Params( - ).Set( - file_patterns=file_patterns, - weights=weights, - bprop_variable_filters=bprop_variable_filters) - else: - raise ValueError('Cannot parse p.file_pattern into a datasource.') - - self.CreateChild('datasource', p.file_datasource) - - def CommonInputOpArgs(self): - """Common input params.""" - p = self.params - args = super().CommonInputOpArgs() - if p.file_datasource and issubclass(p.file_datasource.cls, - datasource.ChainingDataSource): - # If a user provides a ChainingDataSource make sure that the - # param is set correctly when passed to the InputOp - p.use_chaining = True - args.update({ - 'file_random_seed': p.file_random_seed, - 'file_buffer_size': p.file_buffer_size, - 'file_parallelism': p.file_parallelism, - 'file_buffer_size_in_seconds': p.file_buffer_size_in_seconds, - 'bucket_adjust_every_n': p.bucket_adjust_every_n, - 'flush_every_n': p.flush_every_n, - 'num_threads': p.num_batcher_threads, - 'require_sequential_order': p.require_sequential_order, - 'repeat_count': p.repeat_count, - 'use_chaining': p.use_chaining, - }) - args.update(self._InputOpBucketingArgs()) - return args - - def _InputOpBucketingArgs(self): - return { - 'bucket_upper_bound': [1000000000], - 'bucket_batch_limit': [self.InfeedBatchSize()], - } - - # TODO(b/139345706): After p.file_pattern is deleted, the following functions - # _DataSourceFromFilePattern, _BuildDataSourceWithMetadata, _BuildDataSource - # can be deleted and functionality moved to using the DataSource directly. - def _DataSourceFromFilePattern(self, file_pattern, input_source_weights=None): - """Read and return input batch from a string file_pattern. - - Args: - file_pattern: A string file pattern. - input_source_weights: A list of float input source weights to control - input example mix in the batch. The records will be sampled from inputs - proportionally to these weights. Defaults to None which should be - treated as an empty list. - - Returns: - A tuple of tf.Tensors where the tensors which contain the input data and - have major dimension same as size of input batch. - """ - raise NotImplementedError() - - def _BuildDataSourceWithMetadata(self): - """Read and return input batch from `p.file_pattern`. - - `p.file_pattern` may be a string file_pattern or a - list of (file_pattern, weight, [bprop_variable_filter]) tuples. - bprop_variable_filter is optional. When bprop_variable_filter is used, - batches will always contain the examples from the same source. Otherwise, - examples from different sources may be mixed together. - - Returns: - A `.NestedMap` containing - - - data: a tuple of tf.Tensor or `.NestedMap` of - tf.Tensor same as `self._DataSourceFromFilePattern()` - - source_selected: a tensor of size [batch_size, number of data sources] - or None. - - selected_bprop: a tensor of size [number of data sources] or None. - - bprop_variable_filters: a list of bprop_variable filters for each source - or None. - - Raises: - ValueError: If file_datasource is not set - """ - p = self.params - if p.use_per_host_infeed and not self._in_get_processed_input_batch: - raise ValueError( - 'This input generator does not support p.use_per_host_infeed. ' - 'Please set it to False, or move the call to self._BuildDataSource() ' - 'from self.__init__() to self._InputBatch() for batches to be ' - 'correctly replicated per host.') - if not p.file_datasource and p.file_pattern: - # This is a workaround for subclasses which have defined - # their own data source-like functionality. - tf.logging.info( - 'Creating data source-like output from class %s using ' - 'file_pattern %s', self, p.file_pattern) - ret = py_utils.NestedMap() - ret.data = self._DataSourceFromFilePattern(p.file_pattern) - else: - tf.logging.info( - 'Building data source %s with params %s and ' - 'file_pattern %s', self.datasource, self.datasource.params, - p.file_pattern) - ret = self.datasource.BuildDataSource(self._DataSourceFromFilePattern) - if 'selected_bprop' in ret: - self._bprop_onehot = ret.selected_bprop - if 'bprop_variable_filters' in ret: - self._bprop_variable_filters = ret.bprop_variable_filters - if 'source_selected' not in ret: - ret.source_selected = None - return ret - - def _BuildDataSource(self): - """Read and return input batch from `p.file_pattern`. - - Same as _BuildDataSourceWithMetadata but does not return any metadata. - - Returns: - A tuple of tf.Tensor or `.NestedMap` of tf.Tensor same as - `self._DataSourceFromFilePattern()`. - - Raises: - ValueError: If unknown token type. - """ - return self._BuildDataSourceWithMetadata()['data'] - - -class BaseSequenceInputGenerator(BaseInputGeneratorFromFiles): - """The basic sequence input generator.""" - - @classmethod - def Params(cls): - """Defaults params for sequence input generators.""" - p = super().Params() - p.Delete('batch_size') - p.remote.shardable_batch = False - - # How input should be bucketized. - p.Define( - 'bucket_upper_bound', [2560], 'Bucketing scheme. Required to be' - 'a sorted list of integers. Examples that are longer than all bucket' - 'upper bounds are skipped.') - p.Define( - 'bucket_batch_limit', [8], - 'Desired per-split batch size per bucket. Scaled in ' - 'infeed_bucket_batch_size to the infeed size.' - 'Must be the same length as bucket_upper_bound.') - p.Define('source_max_length', None, - 'The maximum length of the source sequence.') - p.Define('target_max_length', 300, - 'The maximum length of the target sequence.') - p.Define('pad_to_max_seq_length', False, - 'If True, input tensors will be padded to max_length.') - p.Define('tokenizer', tokenizers.AsciiTokenizer.Params(), - 'Tokenizer params.') - p.Define( - 'tokenizer_dict', {}, - 'If multiple tokenizers are required, they can be accessed through ' - 'this dict via a key.') - return p - - def __init__(self, params): - super().__init__(params) - - p = self.params - - if p.tokenizer: - assert DEFAULT_TOKENIZER_KEY not in p.tokenizer_dict - p.tokenizer_dict[DEFAULT_TOKENIZER_KEY] = p.tokenizer - - self.tokenizer_dict = {} - for k, p in p.tokenizer_dict.items(): - if p: - name = '_tokenizer_' + k - self.CreateChild(name, p) - self.tokenizer_dict[k] = self.children[name] - else: - self.tokenizer_dict[k] = None - - if DEFAULT_TOKENIZER_KEY in self.tokenizer_dict: - self.tokenizer = self.tokenizer_dict[DEFAULT_TOKENIZER_KEY] - - @property # Adjust batch size according to the cluster spec. - def infeed_bucket_batch_limit(self): - """Returns the bucket batch limit for one infeed host.""" - p = self.params - cluster = self.cluster - infeed_bucket_batch_limit = [ - batch_utils.scale_split_to_infeed(b, p.use_per_host_infeed) - for b in p.bucket_batch_limit - ] - tf.logging.info( - 'infeed_bucket_batch_limit={} num_splits_per_client={} bucket_batch_limit={}' - .format(infeed_bucket_batch_limit, cluster.num_splits_per_client, - p.bucket_batch_limit)) - return infeed_bucket_batch_limit - - def InfeedBatchSize(self): - """Returns the batch size of one infeed pipeline. - - Override in subclass to provide dynamically shaped infeed batch size. - - If use_per_host_infeed is False then there is only one infeed pipeline and - then the GlobalBatchSize() and the InfeedBatchSize() is the same. - """ - buckets = self.infeed_bucket_batch_limit - if any(x != buckets[0] for x in buckets): - tf.logging.warning('Using max bucket batch limit but not all limits are ' - 'the same {}'.format(buckets)) - infeed_size = max(buckets) - tf.logging.info('InfeedBatchSize: %d', infeed_size) - return infeed_size - - def _InputOpBucketingArgs(self): - p = self.params - bucket_batch_limit = self.infeed_bucket_batch_limit - tf.logging.info('infeed_bucket_batch_limit %r', bucket_batch_limit) - return { - 'bucket_upper_bound': p.bucket_upper_bound, - 'bucket_batch_limit': bucket_batch_limit, - } - - def StringsToIds(self, - strs, - is_source=False, - external_max_length=None, - external_append_eos=None, - key=None, - languages=None): - """Tokenize strs into vocab ids. - - Args: - strs: A vector of strings. - is_source: A bool to indicate whether to use `source_max_length` to pad - 'strs'. - external_max_length: An int providing the max_length for strs. - external_append_eos: Bool or None. If None, will be ignored and - `params.append_eos` will be used. If bool, will determine if an eos - symbol will be added to tokens. - key: A string key in case the model has multiple tokenizers. - languages: A vector of str with the same length as `strs`. - - Returns: - A tuple (ids, labels, paddings) with the same shape [batch, maxlen]. - - - ids[i, j] is the input token id of i-th sample for j-th step. - - labels[i, j] is the target token id of i-th sample for j-th step. - - paddings[i, j] is 1 iff i-th sample's j-th step is padded. - - Raises: - ValueError: If unknown token type. - """ - p = self.params - - if external_max_length is not None: - maxlen = external_max_length - elif is_source: - maxlen = p.source_max_length - else: - maxlen = p.target_max_length - - key = key or DEFAULT_TOKENIZER_KEY - return self.tokenizer_dict[key].StringsToIds( - strs, maxlen, external_append_eos, languages=languages) - - def IdsToStrings(self, ids, lens, key=None): - """Converts ids back to strings. - - Args: - ids: A matrix of shape [batch, seqlen]. ids[i, :] is the i-th sample's - ids. - lens: A vector of shape [batch]. lens[i] is the sequence length of the - i-th sample. Only the first lens[i] tokens in ids[i, :] are valid tokens - for the i-th sequence. - key: A string key in case the model has multiple tokenizers. - - Returns: - sequences - A vector of shape [batch]. The converted string sequence. - - Raises: - ValueError: If unknown token type. - """ - key = key or DEFAULT_TOKENIZER_KEY - return self.tokenizer_dict[key].IdsToStrings(ids, lens) - - def Cast(self, v): - """Cast tensor dtype to fprop_dtype.""" - if not v.dtype.is_floating: - return v - return tf.cast(v, py_utils.FPropDtype(self.params)) - - -class BaseTinyDatasetInput(BaseInputGenerator): - """Input generator for tiny dataset which are stored in tf checkpoint. - - | Input batch (b: batch size, h: height, w: width, d: depth): - | raw: Samples. [b, h, w, d]. - | data: Preprocessed samples. [b, h, w, d]. - | label: Labels. [b]. - | weight: [b]. weight[i] is 1.0 if i-th sample is considered to - | be a real example. Otherwise, weight[i] is 0.0. - """ - - @classmethod - def Params(cls): - """Defaults params.""" - p = super().Params() - p.Define('ckpt', None, 'A TensorFlow checkpoint.') - p.Define('data', 'x_train', 'The tensor name in the ckpt.') - p.Define('data_dtype', tf.uint8, 'The tensor dtype in the ckpt.') - p.Define( - 'data_shape', (0, 0, 0), 'A tuple of ints. E.g., a tiny image ' - 'has the shape (height, weight, depth).') - p.Define('label', 'y_train', 'The tensor name in the ckpt.') - p.Define('label_dtype', tf.uint8, 'The tensor dtype in the ckpt.') - p.Define('repeat', True, 'If true, goes through the dataset repeatedly.') - p.use_per_host_infeed = True - return p - - def _InputBatch(self): - p = self.params - - @tf.function - def ReadData(): - x, y = io_ops.restore_v2(p.ckpt, [p.data, p.label], [''] * 2, - [p.data_dtype, p.label_dtype]) - # Always convert to float32. - return tf.cast(x, tf.float32), tf.cast(y, tf.float32) - - # Loads data and label into memory and keep it around. - data, label = ops.cached_call( - f=ReadData.get_concrete_function(), T=[tf.float32, tf.float32]) - b, shape = self.InfeedBatchSize(), list(p.data_shape) - data = tf.reshape(data, [-1] + shape) - label = tf.reshape(label, [-1]) - label = py_utils.HasShape(label, [tf.shape(data)[0]]) - sample_ids = ops.random_permutation_sequence( - num=p.num_samples, - batch=b, - repeat=p.repeat, - seed=p.random_seed if p.random_seed else 0) - n = tf.shape(sample_ids)[0] - raw = py_utils.PadOrTrimTo(tf.gather(data, sample_ids), [b] + shape) - ret = py_utils.NestedMap( - raw=raw, - data=self._Preprocess(raw), - label=py_utils.PadOrTrimTo(tf.gather(label, sample_ids), [b]), - weight=py_utils.PadOrTrimTo(tf.ones([n], dtype=tf.float32), [b])) - # Okay, so here is my problem. - if not py_utils.use_tpu(): - ret['sample_ids'] = sample_ids - return ret - - def _Preprocess(self, raw): - return raw - - -class BaseDataExampleInputGenerator(BaseInputGenerator): - """Base class for input generators that read Feature protos via tf.data.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_files', None, 'Delimited glob of input files.') - p.Define( - 'dataset_type', None, - 'A dataset class constructor such as tf.data.TFRecordDataset. ' - 'The class constructor must take a list of filenames and produce an ' - 'object that extends tf.data.Dataset.') - p.Define('randomize_order', True, 'Whether to randomize the order.') - p.Define('parallel_readers', 1, 'Number of parallel reader threads.') - p.Define('num_examples', -1, 'Number of examples (-1 for unlimited).') - p.Define( - 'num_epochs', -1, - 'Number of passes through the data to make (-1 for unlimited).' - '`tf.errors.OutOfRangeError` is thrown after the limit is reached.') - p.Define('randomize_shuffle_size', 500, - 'Size of the random shuffle buffer.') - p.remote.shardable_batch = False - return p - - def __init__(self, params): - super().__init__(params) - p = params - assert p.input_files, ( - 'input_files is required for a tf.data example input generator') - assert p.dataset_type, ( - 'dataset_type is required for a tf.data example input generator') - - def GetFeatureSpec(self): - """Subclasses must implement and return a feature spec. - - Returns: - NestedMap of features compatible with tf.io.parse_example. Default - implementation returns an empty dict. - """ - return {} - - def GetPreprocessedInputBatch(self): - p = self.params - - def ParseAndProcess(*cols): - """Parses a Tensorflow example into features.""" - # Assume either one or two column input. If one, then the record is - # assumed to be that column. If 2, then it is assumed to be a KV store - # and the record is the second column. - assert len(cols) in [ - 1, 2 - ], ('BaseExampleInputGenerator supports one or two column input') - record = cols[-1] - feature_spec = self.GetFeatureSpec() - features = py_utils.NestedMap(tf.io.parse_example(record, feature_spec)) - return self._PreprocessInputBatch(features) - - dataset_factory = p.dataset_type - dataset = ( - tf.data.Dataset.list_files( - p.input_files, shuffle=bool(p.randomize_order)).apply( - tf.data.experimental.parallel_interleave( - dataset_factory, - cycle_length=p.parallel_readers, - sloppy=p.randomize_order))) - - if p.randomize_order: - dataset = dataset.shuffle(p.randomize_shuffle_size) - dataset = dataset.take(p.num_examples) - dataset = dataset.repeat(p.num_epochs) - dataset = dataset.batch(self.InfeedBatchSize(), drop_remainder=True) - dataset = dataset.map( - ParseAndProcess, num_parallel_calls=p.parallel_readers) - dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE) - iterator = dataset.make_one_shot_iterator() - input_batch = iterator.get_next() - return input_batch - - -def DefineTFDataInput(name, func, ignore_args=None, map_args=None): - """Defines a new InputGenerator class from given tf.data pipeline. - - This function allows users to utilize existing tf.data pipelines which are - defined externally, without making binding boilerplates. - The generated InputGenerator behaves like a one-shot iterator of the given - pipeline. If the iterator is designed to be repeated, the returned - InputGenerator will work similarly. - This function generates `Params` automatically by analysing the given - pipeline's signature so that the behavior of the pipeline can be saved into - `Params`. - This function defines the InputGenerator class on the caller's module. To - avoid any confusion, the returned class have to be stored in the module-level - symbol with the same identifier with given `name`. - - Example: - >>> # A tf.data pipeline which returns a dict of Tensors. - >>> def my_dataset(begin=0, end=10): - ... ds = tf.data.Dataset.from_tensor_slices(tf.range(begin, end)) - ... return ds.map(lambda x: {'value': x}) - - >>> # Defines the InputGenerator class for my_dataset. - >>> MyInput = DefineTFDataInput('MyInput', my_dataset) - - >>> # Obtains Params of MyInput. - >>> p = MyInput.Params() - >>> assert p.args.begin == 0 - >>> assert p.args.end == 10 - - >>> # Instantiates the InputGenerator from Params. - >>> ig = p.Instantiate() - >>> assert isinstance(ig, MyInput) - - >>> # Obtains the data tensors. - - >>> # In TFv1: - >>> data = ig.GetPreprocessedInputBatch() - >>> with tf.Session() as sess: - ... values = sess.run(data) # {'value': 0} - ... values = sess.run(data) # {'value': 1} - ... values = sess.run(data) # {'value': 2} - - >>> # In TFv2: - >>> values = ig.GetPreprocessedInputBatch() # {'value': 0} - >>> values = ig.GetPreprocessedInputBatch() # {'value': 1} - >>> values = ig.GetPreprocessedInputBatch() # {'value': 2} - - Args: - name: A string, representing the name of the new InputGenerator class. - func: A callable to be analysed to generate the new InputGenerator. The - return value of `func` must be a single `tf.data.Dataset` which yields a - dict or its subclasses. The signature (parameter list) of `func` must have - all explicit parameters needed to configure the pipeline. `*args` and - `**kwargs` parameters would be ignored from defining `Params`. - ignore_args: A collection of strings, representing the set of parameter - names to be ignored from defining `Params`. - map_args: A {str: str} dict, representing mappings from existing fields in - `Params()` to `func`'s parameter. These mappings can be used to propagate - some particular Lingvo-specific options defined by others (typically by - super classes: `BaseInputGenerator` or `BaseLayer`) to the given function. - Each entry in the dict represents a `{func_param: layer_param}` pair such - that the `Params().layer_param` field will be mapped to the parameter - `func_param` of `func`. `func_param` won't be added into `Params().args` - to avoid duplicated definitions about the same parameters. - - Returns: - A new InputGenerator class that invokes `func` internally. The `Params()` - method of the returned class makes a new Params containing the `args` field - representing the parameters of `func`. The `GetPreprocessedInputBatch()` - method returns a `py_utils.NestedMap` representing the same dict of the - obtained data from the dataset. - """ - ignore_args = set(ignore_args if ignore_args is not None else ()) - map_args = dict(map_args if map_args is not None else {}) - - # Defines the class first as it will be required to call `super()`. - generated_cls = type(name, (BaseInputGenerator,), {}) - - @classmethod - def _Params(cls): - """Generates Params to configure the InputGenerator. - - This function analyses the signature of the given callable `func` and - defines corresponding fields into `Params` to the obtained function - parameters. - - Returns: - An `InstantiableParams` object representing the InputGenerator. It has the - `args` field which contains the set of parameters of `func`. - """ - # Keys in `map_args` will also be ignored. - actual_ignore_args = ignore_args | set(map_args.keys()) - - p = super(generated_cls, cls).Params() - - # Introduces a new group `args` to avoid confusion between `func`'s - # parameters and existing params defined by super classes. - # TODO(oday): For better UX, consider removing this nested field and add - # `func`s parameters to `p` directly. We need to make sure that there are no - # side effects by integrating `func`'s parameters and follows: - # - BaseInputGenerator.Params() - # - BaseLayer.Params() - # - InstantiableParams.cls - p.Define('args', hyperparams.Params(), 'Parameter list of the pipeline.') - inspect_utils.DefineParams(func, p.args, actual_ignore_args) - return p - - def _Init(self, params): - """Initializes the InputGenerator.""" - super(generated_cls, self).__init__(params) - p = self.params - - # We have to make the one-shot iterator only once as _InputBatch will be - # called repeatedly in TFv2. - overrides = {k: p.Get(v) for k, v in map_args.items()} - dataset = inspect_utils.CallWithParams(func, p.args, **overrides) - assert isinstance(dataset, (tf1.data.Dataset, tf2.data.Dataset)), ( - 'DefineTFDataInput must take a callable which returns a ' - '`tf.data.Dataset`. The given callable `%s` returned `%s`' % - (func, dataset)) - self.iterator = tf1.data.make_one_shot_iterator(dataset) - - def _InputBatch(self): - """Generates data tensors by invoking the pipeline.""" - - # TFv1: Returns Tensors which will be determined by Session.run(). - # TFv2: Returns Tensors with actual values. - data = self.iterator.get_next() - - # Converts dict to NestedMap to maintain consistency with existing - # functionalities in base_input_generator. - # TODO(oday): Consider mitigating this restriction. - assert isinstance(data, dict), ( - 'DefineTFDataInput accepts only datasets that returns a dict or its ' - 'subclasses.') - if not isinstance(data, py_utils.NestedMap): - data = py_utils.NestedMap.FromNestedDict(data) - - return data - - # Overrides member methods. - generated_cls.Params = _Params - generated_cls.__init__ = _Init - generated_cls._InputBatch = _InputBatch # pylint: disable=protected-access - - # Sets __module__ to the caller's module name for pickling and restoring from - # Params to work. - # See also the namedtuple's implementation for details. - module = inspect.stack()[1].frame.f_globals.get('__name__', '__main__') - generated_cls.__module__ = module - - return generated_cls diff --git a/lingvo/core/base_input_generator_test.py b/lingvo/core/base_input_generator_test.py deleted file mode 100644 index 6fd469fe..00000000 --- a/lingvo/core/base_input_generator_test.py +++ /dev/null @@ -1,477 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for base_input_generator.""" - -import os -import shutil -import tempfile -from absl.testing import flagsaver -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import cluster_factory -from lingvo.core import hyperparams -from lingvo.core import py_utils -from lingvo.core import test_utils -import mock -import numpy as np - - -import tensorflow.compat.v1 as tf1 -import tensorflow.compat.v2 as tf2 - - -def _CreateFakeTFRecordFiles(record_count=10): - tmpdir = tempfile.mkdtemp() - data_path = os.path.join(tmpdir, 'fake.tfrecord') - with tf.io.TFRecordWriter(data_path) as w: - for _ in range(record_count): - feature = { - 'audio': - tf.train.Feature( - float_list=tf.train.FloatList( - value=np.random.uniform(-1.0, 1.0, 48000))), - } - example = tf.train.Example(features=tf.train.Features(feature=feature)) - w.write(example.SerializeToString()) - return tmpdir, data_path - - -class BaseInputGeneratorBatchSizeTest(test_utils.TestCase): - - @flagsaver.flagsaver(xla_device='tpu', enable_asserts=False) - def testSingleHostInfeed(self): - with cluster_factory.ForTestingWorker(tpus=128): - p = base_input_generator.BaseInputGenerator.Params() - p.batch_size = 16 - p.use_per_host_infeed = False - input_generator = p.Instantiate() - - self.assertEqual(2048, input_generator.InfeedBatchSize()) - self.assertEqual(2048, input_generator.GlobalBatchSize()) - - @flagsaver.flagsaver(xla_device='tpu', enable_asserts=False) - def testPerHostInfeed(self): - with cluster_factory.ForTestingWorker(tpus=128, num_tpu_hosts=8): - p = base_input_generator.BaseInputGenerator.Params() - p.batch_size = 16 - p.use_per_host_infeed = True - input_generator = p.Instantiate() - - self.assertEqual(256, input_generator.InfeedBatchSize()) - self.assertEqual(2048, input_generator.GlobalBatchSize()) - - -class ToyInputGenerator(base_input_generator.BaseDataExampleInputGenerator): - - def GetFeatureSpec(self): - return {'audio': tf.io.FixedLenFeature([48000], tf.float32)} - - -class BaseExampleInputGeneratorTest(test_utils.TestCase): - - def setUp(self): - super().setUp() - tf.reset_default_graph() - - def tearDown(self): - super().tearDown() - if hasattr(self, '_tmpdir'): - shutil.rmtree(self._tmpdir) - - def testTfRecordFile(self): - p = ToyInputGenerator.Params() - p.batch_size = 2 - self._tmpdir, p.input_files = _CreateFakeTFRecordFiles() - p.dataset_type = tf.data.TFRecordDataset - p.randomize_order = False - p.parallel_readers = 1 - ig = p.Instantiate() - with self.session(graph=tf.get_default_graph()): - inputs = ig.GetPreprocessedInputBatch() - eval_inputs = self.evaluate(inputs) - input_shapes = eval_inputs.Transform(lambda t: t.shape) - self.assertEqual(input_shapes.audio, (2, 48000)) - - def testTfRecordFileLargeBatch(self): - p = ToyInputGenerator.Params() - p.batch_size = 200 - self._tmpdir, p.input_files = _CreateFakeTFRecordFiles() - p.dataset_type = tf.data.TFRecordDataset - p.randomize_order = False - p.parallel_readers = 1 - ig = p.Instantiate() - with self.session(graph=tf.get_default_graph()): - inputs = ig.GetPreprocessedInputBatch() - eval_inputs = self.evaluate(inputs) - input_shapes = eval_inputs.Transform(lambda t: t.shape) - self.assertEqual(input_shapes.audio, (200, 48000)) - - def testNumEpochs(self): - p = ToyInputGenerator.Params() - p.batch_size = 3 - p.num_epochs = 7 - self._tmpdir, p.input_files = _CreateFakeTFRecordFiles( - record_count=p.batch_size) - p.dataset_type = tf.data.TFRecordDataset - p.randomize_order = False - p.parallel_readers = 1 - ig = p.Instantiate() - with self.session(graph=tf.get_default_graph()): - inputs = ig.GetPreprocessedInputBatch() - for _ in range(p.num_epochs): - eval_inputs = self.evaluate(inputs) - self.assertEqual(eval_inputs.audio.shape, (p.batch_size, 48000)) - with self.assertRaisesRegex(tf.errors.OutOfRangeError, 'End of sequence'): - self.evaluate(inputs) - - def testRespectsInfeedBatchSize(self): - p = ToyInputGenerator.Params() - p.batch_size = 3 - self._tmpdir, p.input_files = _CreateFakeTFRecordFiles() - p.dataset_type = tf.data.TFRecordDataset - - ig = p.Instantiate() - batch = ig.GetPreprocessedInputBatch() - self.assertEqual(batch.audio.shape[0], p.batch_size) - self.assertEqual(p.batch_size, ig.InfeedBatchSize()) - - tf.reset_default_graph() - ig = p.Instantiate() - with mock.patch.object( - ig, 'InfeedBatchSize', return_value=42) as mock_method: - batch = ig.GetPreprocessedInputBatch() - self.assertEqual(batch.audio.shape[0], 42) - mock_method.assert_called() - - -# Dataset pipelines for TFDataInputTest. -def _TestDatasetFn(begin=0, end=10): - """Test tf.data pipeline.""" - ds = tf.data.Dataset.from_tensor_slices(tf.range(begin, end)) - return ds.map(lambda x: {'value': x}) - - -def _TestDatasetFnWithoutDefault(begin, end=10): - """Test tf.data pipeline with non-defaulted parameters.""" - ds = tf.data.Dataset.from_tensor_slices(tf.range(begin, end)) - return ds.map(lambda x: {'value': x}) - - -def _TestDatasetFnWithRepeat(begin=0, end=10): - """Test tf.data pipeline with repeat.""" - ds = tf.data.Dataset.from_tensor_slices(tf.range(begin, end)).repeat() - return ds.map(lambda x: {'value': x}) - - -def _TestDatasetFnV1(begin=0, end=10): - """Similar to _TestDatasetFn but returns TFv1's dataset explicitly.""" - ds = tf1.data.Dataset.from_tensor_slices(tf.range(begin, end)) - return ds.map(lambda x: {'value': x}) - - -def _TestDatasetFnV2(begin=0, end=10): - """Similar to _TestDatasetFn but returns TFv2's dataset explicitly.""" - ds = tf2.data.Dataset.from_tensor_slices(tf.range(begin, end)) - return ds.map(lambda x: {'value': x}) - - -class _TestDatasetClass: - """A class that generates tf.data by its member function.""" - - def __init__(self, begin): - self._begin = begin - - def DatasetFn(self, end=10): - ds = tf.data.Dataset.from_tensor_slices(tf.range(self._begin, end)) - return ds.map(lambda x: {'value': x}) - - -# A class object which will be instantiated at importing the module. -# It can be used in DefineTFDataInput(). -_TestDatasetObject = _TestDatasetClass(begin=0) - -# InputGenerators for TFDataInputTest. -_TestTFDataInput = base_input_generator.DefineTFDataInput( - '_TestTFDataInput', _TestDatasetFn) -_TestTFDataInputWithIgnoreArgs = base_input_generator.DefineTFDataInput( - '_TestTFDataInputWithIgnoreArgs', _TestDatasetFn, ignore_args=('begin',)) -_TestTFDataInputWithMapArgs = base_input_generator.DefineTFDataInput( - '_TestTFDataInputWithMapArgs', - _TestDatasetFn, - map_args={'end': 'num_samples'}) -_TestTFDataInputWithoutDefault = base_input_generator.DefineTFDataInput( - '_TestTFDataInputWithoutDefault', _TestDatasetFnWithoutDefault) -_TestTFDataInputWithRepeat = base_input_generator.DefineTFDataInput( - '_TestTFDataInputWithRepeat', _TestDatasetFnWithRepeat) -_TestTFDataInputWithBoundMethod = base_input_generator.DefineTFDataInput( - '_TestTFDataInputWithBoundMethod', _TestDatasetObject.DatasetFn) -_TestTFDataInputV1 = base_input_generator.DefineTFDataInput( - '_TestTFDataInputV1', _TestDatasetFnV1) -_TestTFDataInputV2 = base_input_generator.DefineTFDataInput( - '_TestTFDataInputV2', _TestDatasetFnV2) - - -class TFDataInputTest(test_utils.TestCase): - - def testModule(self): - self.assertEqual(_TestTFDataInput.__module__, '__main__') - self.assertEqual(_TestTFDataInputWithIgnoreArgs.__module__, '__main__') - self.assertEqual(_TestTFDataInputWithMapArgs.__module__, '__main__') - self.assertEqual(_TestTFDataInputWithoutDefault.__module__, '__main__') - self.assertEqual(_TestTFDataInputWithRepeat.__module__, '__main__') - self.assertEqual(_TestTFDataInputWithBoundMethod.__module__, '__main__') - self.assertEqual(_TestTFDataInputV1.__module__, '__main__') - self.assertEqual(_TestTFDataInputV2.__module__, '__main__') - - def testExample(self): - """Tests the example code in the function docstring.""" - p = _TestTFDataInput.Params() - self.assertIn('args', p) - self.assertIn('begin', p.args) - self.assertIn('end', p.args) - self.assertEqual(p.args.begin, 0) - self.assertEqual(p.args.end, 10) - - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInput) - - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Consumes all data. - for i in range(p.args.begin, p.args.end): - self.assertEqual(sess.run(data).value, i) - - with self.assertRaises(tf.errors.OutOfRangeError): - sess.run(data) - - def testToFromProto(self): - """Similar to `testExample` but params will be restored from a proto.""" - serialized_proto = _TestTFDataInput.Params().ToProto() - p = hyperparams.Params.FromProto(serialized_proto) - self.assertIn('args', p) - self.assertIn('begin', p.args) - self.assertIn('end', p.args) - self.assertEqual(p.args.begin, 0) - self.assertEqual(p.args.end, 10) - - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInput) - - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Consumes all data. - for i in range(p.args.begin, p.args.end): - self.assertEqual(sess.run(data).value, i) - - with self.assertRaises(tf.errors.OutOfRangeError): - sess.run(data) - - def testWithIgnoreArgs(self): - """Tests the `ignore_args` parameter.""" - p = _TestTFDataInputWithIgnoreArgs.Params() - self.assertIn('args', p) - self.assertNotIn('begin', p.args) - self.assertIn('end', p.args) - self.assertEqual(p.args.end, 10) - - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInputWithIgnoreArgs) - - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Consumes all data. - for i in range(p.args.end): - self.assertEqual(sess.run(data).value, i) - - with self.assertRaises(tf.errors.OutOfRangeError): - sess.run(data) - - def testWithMapArgs(self): - """Tests the `map_args` parameter.""" - p = _TestTFDataInputWithMapArgs.Params() - self.assertIn('args', p) - self.assertIn('num_samples', p) # Defined by BaseInputGenerator. - self.assertIn('begin', p.args) - self.assertNotIn('end', p.args) - self.assertEqual(p.num_samples, 0) - self.assertEqual(p.args.begin, 0) - - p.num_samples = 20 - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInputWithMapArgs) - - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Consumes all data. - for i in range(p.args.begin, p.num_samples): - self.assertEqual(sess.run(data).value, i) - - with self.assertRaises(tf.errors.OutOfRangeError): - sess.run(data) - - def testWithoutDefault(self): - """Tests parameters without defaults.""" - p = _TestTFDataInputWithoutDefault.Params() - self.assertIn('args', p) - self.assertIn('begin', p.args) - self.assertIn('end', p.args) - self.assertIsNone(p.args.begin) - self.assertEqual(p.args.end, 10) - - p.args.begin = 0 - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInputWithoutDefault) - - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Consumes all data. - for i in range(p.args.begin, p.args.end): - self.assertEqual(sess.run(data).value, i) - - with self.assertRaises(tf.errors.OutOfRangeError): - sess.run(data) - - def testWithRepeat(self): - """Tests if the repeated dataset runs forever.""" - p = _TestTFDataInputWithRepeat.Params() - self.assertIn('args', p) - self.assertIn('begin', p.args) - self.assertIn('end', p.args) - self.assertEqual(p.args.begin, 0) - self.assertEqual(p.args.end, 10) - - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInputWithRepeat) - - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Runs the dataset several times: it should not raise OutOfRangeError. - for _ in range(3): - for i in range(p.args.begin, p.args.end): - self.assertEqual(sess.run(data).value, i) - - def testWithBoundMethod(self): - """Tests pipeline defined by a bound method: member function with self.""" - p = _TestTFDataInputWithBoundMethod.Params() - self.assertIn('args', p) - self.assertNotIn('begin', p.args) - self.assertIn('end', p.args) - self.assertEqual(p.args.end, 10) - - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInputWithBoundMethod) - - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Consumes all data. - for i in range(p.args.end): - self.assertEqual(sess.run(data).value, i) - - with self.assertRaises(tf.errors.OutOfRangeError): - sess.run(data) - - def testDatasetV1(self): - """Tests the TFv1 Dataset.""" - p = _TestTFDataInputV1.Params() - self.assertIn('args', p) - self.assertIn('begin', p.args) - self.assertIn('end', p.args) - self.assertEqual(p.args.begin, 0) - self.assertEqual(p.args.end, 10) - - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInputV1) - - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Consumes all data. - for i in range(p.args.begin, p.args.end): - self.assertEqual(sess.run(data).value, i) - - with self.assertRaises(tf.errors.OutOfRangeError): - sess.run(data) - - def testDatasetV2(self): - """Tests the TFv2 Dataset.""" - p = _TestTFDataInputV2.Params() - self.assertIn('args', p) - self.assertIn('begin', p.args) - self.assertIn('end', p.args) - self.assertEqual(p.args.begin, 0) - self.assertEqual(p.args.end, 10) - - ig = p.Instantiate() - self.assertIsInstance(ig, _TestTFDataInputV2) - - # We keep the TFv1's Session here since v1/v2 behaviors would not coexist. - # TODO(oday): write TFv2-specific tests. - with self.session(graph=tf.get_default_graph()) as sess: - data = ig.GetPreprocessedInputBatch() - self.assertIsInstance(data, py_utils.NestedMap) - self.assertIsInstance(data.value, tf.Tensor) - self.assertAllEqual(data.value.shape, ()) - self.assertEqual(data.value.dtype, tf.int32) - - # Consumes all data. - for i in range(p.args.begin, p.args.end): - self.assertEqual(sess.run(data).value, i) - - with self.assertRaises(tf.errors.OutOfRangeError): - sess.run(data) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/base_layer.py b/lingvo/core/base_layer.py deleted file mode 100644 index 77501beb..00000000 --- a/lingvo/core/base_layer.py +++ /dev/null @@ -1,980 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Base class for all layers.""" - -import abc -import collections -import enum -import itertools -import re -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import hyperparams -from lingvo.core import py_utils - -FLAGS = tf.flags.FLAGS - -_LAYER_STACK = py_utils.ThreadLocalStack() -_CREATE_VARIABLES_STACK = py_utils.ThreadLocalStack() - - -class Accumulator: - """Layers can register accumulators to persist step-level state. - - Accumulators must be represented by a Tensor of a fixed shape. The default - value must be supplied by overriding DefaultValue(). It is important that - the default tensor value is created on each call in order to avoid - accumulators leaking to different graphs. - - Accumulators can be enabled (default) or disabled by pairing - Disable()/Enable() calls. When disabled, the accumulator will only return - the default value and will silently drop calls to SetValue(). When computing - gradients that may touch accumulators, calls should be bracketed with - Disable()/Enable(). - - Care must be taken when manipulating accumulators across Defun boundaries. - Typically, values for all accumulators in a layer must be explicitly - retrieved and passed in to the Defun scope by calling - layer.GetAccumulatorValues(), marshalling into the Defun and setting them - via layer.SetAccumulatorValues(). The reverse must be done on return. - """ - - def __init__(self): - # None for initial value or the current Tensor value. - self._value = None - self._disable_count = 0 - - @property - def is_disabled(self): - """Whether the accumulator is disabled.""" - return self._disable_count > 0 - - def Disable(self): - """Disables the accumulator (must be balanced with Enable).""" - self._disable_count += 1 - - def Enable(self): - """Enables the accumulator (must balance a Disable).""" - assert self._disable_count > 0, 'Unbalanced Accumulator Enable/Disable' - self._disable_count -= 1 - - def GetValue(self): - """Gets the current value of the accumulator Tensor.""" - if self.is_disabled or self._value is None: - return self.DefaultValue() - else: - return self._value - - def SetValue(self, value): - """Sets the current value of the accumulator Tensor.""" - if not self.is_disabled: - self._value = value - - def Reset(self): - """Resets the accumulator to its default value.""" - if not self.is_disabled: - self._value = None - - def DefaultValue(self): - raise NotImplementedError('DefaultValue must be implemented') - - -def _BaseLayerInitWrapper(func): # pylint: disable=invalid-name - """A decorator for layer's __init__. - - Args: - func: The __init__ method of `BaseLayer`'s subclasses. - - Returns: - A decorator wrapper for layer's initializer. Note that this wrapper can - be called multiple times for the same layer instance, once for each - __init__() for classes on the class hierarchy. - """ - - def Wrapper(self, *args, **kwargs): - """Decorator wrapper fn.""" - stack = _LAYER_STACK.stack - if stack and stack[-1] is self: - # Short circuit if called multiple times (eg. super() chain). - func(self, *args, **kwargs) - return - - # Push back self (the current layer) to the stack. - stack_size = len(stack) - stack.append(self) - try: - # Calls the layer's real __init__ method. - func(self, *args, **kwargs) - if len(stack) > 1: - # Records the fact stack[-2] just created a sub-layer self. - stack[-2]._AutoAddChild(self) # pylint: disable=protected-access - finally: - # Pop out self (the current layer). - assert stack[-1] is self - stack.pop() - assert len(stack) == stack_size - - if not stack: - # Outermost layer just finished __init__. - if self.cluster.immediately_create_variables: - self.InstantiateVariables() - - return Wrapper - - -def RecursiveFindLayerParams(params): - """Returns all params that define a layer.""" - if not isinstance(params, hyperparams.Params): - return [] - layer_params = [] - if hasattr(params, 'cls') and issubclass(params.cls, BaseLayer): - layer_params.append(params) - for _, p in params.IterParams(): - if isinstance(p, (list, tuple)): - for item in p: - layer_params.extend(RecursiveFindLayerParams(item)) - elif isinstance(p, dict): - for item in p.items(): - layer_params.extend(RecursiveFindLayerParams(item)) - else: - layer_params.extend(RecursiveFindLayerParams(p)) - return layer_params - - -class BaseLayerMeta(type): - """Metaclass tracking child layers and variable initialization.""" - - # pylint: disable=bad-mcs-classmethod-argument - def __new__(mcs, name, bases, dct): - cls = super(BaseLayerMeta, mcs).__new__(mcs, name, bases, dct) - if '__init__' not in dct: - - def TrivialInit(self, params): - super(cls, self).__init__(params) # pylint: disable=bad-super-call - - cls.__init__ = TrivialInit - - cls.__init__ = _BaseLayerInitWrapper(cls.__init__) - return cls - # pylint: enable=bad-mcs-classmethod-argument - - def __call__(cls, *args, **kwargs): - self = super().__call__(*args, **kwargs) - # This happens after self.__init__() - # pylint: disable=protected-access - self._disable_create_child = True - self._VerifyChildren() - # pylint: enable=protected-access - return self - - -class ABCLayerMeta(BaseLayerMeta, abc.ABCMeta): - pass - - -# NamedTuple that records the metadata for creating a variable. -# For internal use only. Subclasses of BaseLayer should use -# self.CreateVariable() to create variables. -CreateVariableMeta = collections.namedtuple( - 'CreateVariableMeta', ['var_params', 'theta_fn', 'kwargs']) - - -class _CreateLayerVariablesStatus(enum.Enum): - NOT_CALLED = 1 - IN_PROGRESS = 2 - COMPLETED = 3 - - -LAYER_WT = 'layer_weight_variable' - - -class BaseLayer(tf.Module, metaclass=BaseLayerMeta): - r"""Base class for all the layer object. - - As this BaseLayer is a proper sub-class of tf.Module, it supports proper - tracking and reflection of key constituents such as variables and submodules. - - self.submodules returns a list of submodules that are reachable through - recursive member access from self. - - self.variables returns a list of Variables that are reachable through - recursive member access from self. - - self(\*args, \*\*kwargs) carries out computation on the input args and kwargs. - """ - - # Set to an inference driver name if this is an inference specialization - # class. - _INFERENCE_DRIVER_NAME = None - - @classmethod - def Params(cls): - """Returns the layer params.""" - p = hyperparams.InstantiableParams(cls) - p.Define('inference_driver_name', cls._INFERENCE_DRIVER_NAME, - 'Name of the inference driver used to construct this layer.') - p.Define('name', '', 'Name of this layer object.') - p.Define('dtype', tf.float32, 'Datatype to use.') - # None value will make FProp use dtype instead of fprop_dtype. - # TODO(lepikhin): all @tf.Defun should use p.fprop_dtype if it is set. - p.Define('fprop_dtype', None, 'Activations datatype to use.') - p.Define( - 'random_seed', None, 'Random seed for deterministic unittests. This ' - 'is inherited by child layers if they do not set a random_seed.') - p.Define('vn', py_utils.DefaultVN(), - 'How variational noise should be applied.') - p.Define( - 'params_init', py_utils.DefaultParamInit(), - 'How model weights should be initialized. Not to be confused with ' - 'hyperparams.') - # Makes additional alterations for graphs being used for inference. - p.Define('is_inference', None, 'True if in inference mode.') - # In addition to is_inference, indicate that the inference graph is - # for a single step. - p.Define( - 'allow_implicit_capture', None, - 'When using Defuns, code often asserts that the Defun does not ' - 'capture undeclared inputs. This eliminates a source of bugs ' - 'at the expense of making some kinds of models or utilities ' - 'hard/impossible to use. Setting this to True/False (versus None) ' - 'causes the setting to apply to this layer and its children.') - p.Define( - 'skip_lp_regularization', None, - 'If True, all variables in this layer will skip Lp regularization. ' - 'If None/False, only variables explicitly in the ' - 'SKIP_LP_REGULARIZATION collection will skip Lp regularization. ' - 'Also propagated to child layers with default settings (None).') - return p - - @staticmethod - def CopyBaseParams(from_params, to_params): - """Copies BaseLayer params from `from_params` to `to_params`.""" - assert issubclass(from_params.cls, BaseLayer) - assert issubclass(to_params.cls, BaseLayer) - # Copy-over the BaseLayer params. - if to_params.dtype == tf.float32: - to_params.dtype = from_params.dtype - if from_params.fprop_dtype is not None: - to_params.fprop_dtype = from_params.fprop_dtype - if to_params.random_seed is None: - to_params.random_seed = from_params.random_seed - if to_params.is_inference is None: - to_params.is_inference = from_params.is_inference - if to_params.allow_implicit_capture is None: - to_params.allow_implicit_capture = from_params.allow_implicit_capture - if to_params.skip_lp_regularization is None: - to_params.skip_lp_regularization = from_params.skip_lp_regularization - - # Only copy from base when vn config is using the default setting. - if to_params.vn == py_utils.DefaultVN(): - to_params.vn = from_params.vn.Copy() - - # TODO(rpang): derive to_params.params_init.seed from - # from_params.params_init.seed if it is specified in 'from_params' and not - # in 'to_params'. - if py_utils.IsDefaultParamInit(to_params.params_init): - # Copy over params_init as well. - to_params.params_init = from_params.params_init.Copy() - return to_params - - def __init__(self, params): - """Layer constructor. - - Args: - params: A params used to construct this layer. - """ - assert params.name, ( - 'Layer params for %s must have a "name"' % self.__class__.__name__) - - tf_module_name = params.name - tf_module_name = re.sub('[^a-zA-Z0-9_]+', '_', tf_module_name) - tf_module_name = 'bbf_' + self.__class__.__name__ + '_' + tf_module_name - py_utils.NestedMap.CheckKey(tf_module_name) - - # initialize the base class. - super().__init__(tf_module_name) - - # Note AutoTracking doesn't work properly due to its inability to walk - # through py_utils.NestedMap data structures which are used widely - # throughout the Lingvo codebase. Also there seems to be some performance - # hit in turning on auto-tracking in constructing graphs. For now, we - # disable auto-tracking. - # TODO(lingvo): Re-enable auto-tracking when fuller support is - # added for key data structures used in Lingvo, and performance issue is - # debugged more and understood better. - self._setattr_tracking = False - - self._parent = None - for parent in reversed(_LAYER_STACK.stack): - if parent is not self: - self._parent = parent - break - self._params = params.Copy() - tf.logging.debug('Creating layer %s with params: \n %s \n', - self.__class__.__name__, str(params)) - # Vars created by this layer. - self._private_vars = py_utils.NestedMap() - # Theta derived from this layer's vars. - self._private_theta = py_utils.NestedMap() - # Child layers created by this layer through CreateChild/CreateChildren. - self._private_children = py_utils.NestedMap() - # Child layers created by this layer. A well-formed layer should - # have self._private_children equals to self._children_list. I.e., - # all child layers are created using CreateChild/CreateChildren. - self._children_list = [] - # Extra theta's not directly correspond to any underlying vars. For example, - # the concatenated sharded variables. - self._extra_theta = py_utils.NestedMap() - # All registered accumulators. - self._private_accumulators = py_utils.NestedMap() - # Layer-private functions. Add with AddFunction. - self._private_fns = dict() - # Mapping from variable names to its symbolic shape. - # self._var_symbolic_shape_map['var_name'] will be a tuple of integers or - # symbolic expressions, one for each dimension of the variable. - self._var_symbolic_shape_map = {} - - self._is_variable_free = False - self._variables_to_create = {} - self._create_variables_status = _CreateLayerVariablesStatus.NOT_CALLED - - def SetVariableFree(self, value=True): - """Marks this layer as having no variables. - - Note that this status affects sublayers and child layers too. - - Args: - value: True to set layer as variable free. - """ - if self._create_variables_status != _CreateLayerVariablesStatus.NOT_CALLED: - raise ValueError( - 'Variable free status for %s must be set before InstantiateVariables().' - % self.params.cls) - if self._variables_to_create: - raise ValueError('Cannot set layer %s with variables as variable free.' % - self.params.cls) - self._is_variable_free = value - - def FPropDefaultTheta(self, *args, **kwargs): - """Calls `FProp`.""" - return self.FProp(self.theta, *args, **kwargs) - - def __call__(self, *args, **kwargs): - """Forwards call to FPropDefaultTheta.""" - return self.FPropDefaultTheta(*args, **kwargs) - - def FProp(self, theta, *args, **kwargs): - """Forward propagation. - - The central interface that subclasses should implement. The caller - calls `FProp` with a `theta` dictionary. E.g.:: - - foo = InstanceOfASubClassOfFoo(params) - y = foo.FProp(foo.theta, x) - - The implementation of `FProp()` computes a function given - the theta and the inputs. E.g.:: - - subs = self.children - inputs = args[0] - a0 = subs.linear.FProp(theta.linear, inputs) - a1 = subs.softmax.FProp(theta.softmax, a0) - # The same layer applied twice. - a2 = subs.linear.FProp(theta.linear, a1) - return a2 - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - *args: List args. - **kwargs: Keyward args. - """ - del theta - del args - del kwargs - raise NotImplementedError('Abstract method of %s' % self) - - @classmethod - def FPropMeta(cls, params, *args, **kwargs): - """Returns metadata about the `FProp` computation for this layer. - - **Experimental feature.** - Don't use or depend on it without consulting Lingvo authors. - - E.g.:: - - p = SomeComplexLayer.Params() - meta = p.cls.FPropMeta(p, tshape.Shape([128, 20, 50, 'channels'])) - - `meta.flops` gives an estimate count of floating point operations done by - one `FProp` given an input tensor of shape [128, 20, 50, channels]. - `meta.out_shapes` is a tuple of TShape, which tells you what shape - of tensors this layer will return. - - Args: - params: The param of a layer of this layer type. - *args: Corresponds to FProp with Tensors replaced by `TensorShape`. - **kwargs: Corresponds to FProp with Tensors replaced by `TensorShape`. - - Returns: - A `.NestedMap` with - - - flops - The estimated number of floating point operations incurred by - this fprop. - - out_shapes - A tuple of `TShape`. I.e., `out_shapes[i]` - represents the shape of the `i`-th returned tensor of the fprop. - """ - raise NotImplementedError('FPropMeta of %s' % cls) - - @property - def params(self): - """Returns the params upon which this layer is built.""" - return self._params - - @property - def cluster(self): - """Returns the current cluster configuration.""" - return cluster_factory.Current() - - @property - def do_eval(self): - return self.cluster.do_eval - - @property - def parent(self): - """None if self is the root layer, otherwise the parent layer of self.""" - return self._parent - - @property - def path(self): - """Returns a '.'-separated string with all layer names from the root.""" - if self.parent: - return self.parent.path + '.' + self.params.name - else: - return self.params.name - - @property - def layer_type(self): - """Returns layer type prefixed with 'lingvo.'.""" - return 'lingvo.' + self.__class__.__name__ - - @property - def children(self): - """Returns children layers of this layer in a `.NestedMap`.""" - return self._private_children - - @property - def global_step(self): - if self._create_variables_status == _CreateLayerVariablesStatus.NOT_CALLED: - raise ValueError( - 'Cannot access global_step before InstantiateVariables().') - return self._global_step - - def __getattr__(self, name): - """Returns the child layer of the given name.""" - if name == '_private_children': - # Raising AttributeError without custom message triggers normal python - # handling of __getattr__ AttributeError. - raise AttributeError() - if name in self._private_children: - return self._private_children[name] - elif (hasattr(type(self), name) and - isinstance(getattr(type(self), name), property)): - # There was an AttributeError raised by a property getter. - # Call property getter again directly to raise the same error. - return getattr(type(self), name).fget(self) - else: - raise AttributeError('%s is not a sub-layer of %s.' % (name, self)) - - def GetDescendant(self, path): - """Returns a descendant layer given the path. - - NOTE(yonghui): This GetDescendant is not complete. It is not able to descent - into list/tuple substructures. - - Args: - path: a comma separated string denoting a descendant of this layer. - - Returns: - The descendant layer. - - Raises: - KeyError: if the descendant is not found. - """ - sub = self - if path: - for k in path.split('.'): - if k not in sub.children: - raise KeyError('%s not found in %s' % (k, list(sub.children.keys()))) - sub = sub.children[k] - return sub - - @property - def vars(self): - """Returns variables of this layer and its children in a `.NestedMap`.""" - if self._is_variable_free: - return self._private_children.Transform(lambda _: py_utils.NestedMap()) - if self._create_variables_status == _CreateLayerVariablesStatus.NOT_CALLED: - raise ValueError( - 'Cannot access vars for layer %s before they have been created.' % - self.params.cls) - ret = self._private_children.Transform(lambda x: x.vars) - for k in self._private_vars.keys(): - ret[k] = self._private_vars[k] - return ret - - @property - def theta(self): - """Returns theta of this layer and its children in a `.NestedMap`.""" - if self._is_variable_free: - return self._private_children.Transform(lambda _: py_utils.NestedMap()) - if self._create_variables_status == _CreateLayerVariablesStatus.NOT_CALLED: - raise ValueError( - 'Cannot access theta for layer %s before they have been created.' % - self.params.cls) - ret = self._private_children.Transform(lambda x: x.theta) - - private_theta = self._private_theta - - if (self._params.fprop_dtype is not None and - self._params.fprop_dtype != self._params.dtype): - - def MaybeCastToFPropDtype(x): - if x is not None and x.dtype == self._params.dtype: - return tf.cast(x, self._params.fprop_dtype) - else: - return x - - private_theta = private_theta.Transform(MaybeCastToFPropDtype) - - ret.update(private_theta) - return ret - - @property - def accumulators(self): - """Returns `.NestedMap` of `Accumulator` instances for this and children.""" - ret = self._private_children.Transform(lambda x: x.accumulators) - for k, acc in self._private_accumulators.items(): - ret[k] = acc - return ret - - @property - def fns(self): - """Returns a read-only view of layer local functions. - - Functions can be accessed by index (['name']) or attribute notation - (`fns.foo`). - - Returns: - Read-only attribute accessible dict view of the layer's function library. - """ - return py_utils.ReadOnlyAttrDictView(self._private_fns) - - def AddFunction(self, name, f, replace=False): - """Adds a function to the layer's `fns` collection. - - This should be used to add op-like functions specific to the operation - of the layer and its children. Such functions should be added in `__init__` - and may either be raw python functions or TensorFlow Defuns. This - facility is just a mechanism for organizing them and having basic checks - on name collisions. - - Args: - name: The function name. It will be accessible as `self.fns.{name}`. - f: The function body. - replace: Whether to replace an existing function (default False). - - Raises: - AttributeError: If the function already exists and replace == False. - """ - py_utils.NestedMap.CheckKey(name) - if not replace: - if name in self._private_fns: - raise AttributeError( - 'Function "%s" is already defined on layer "%r"' % (name, self)) - self._private_fns[name] = f - - def _CheckName(self, name): - """Asserts name's validity.""" - py_utils.NestedMap.CheckKey(name) - assert name not in self._private_vars, ( - '%s exists in vars, %s' % (name, list(self._private_vars.keys()))) - assert name not in self._private_theta, ( - '%s exists in theta, %s' % (name, list(self._private_theta.keys()))) - assert name not in self._private_children, ('%s exists in children, %s' % ( - name, list(self._private_children.keys()))) - assert name not in self._private_accumulators, ( - '%s exists in global_accumulator: %s' % - (name, list(self._private_accumulators.keys()))) - - def _VariableCollections(self): - return [LAYER_WT, '%s_vars' % self.__class__.__name__] - - def RegisterAccumulator(self, name, acc): - """Registers an accumulator for this layer. - - An accumulator is used to propagate some state to a future point, - where it is acted on (typically as part of `PostTrainingStepUpdate`). This - mechanism allows for arbitrarily nested parts of a model to export state - back to the global scope. Accumulators must be specially handled - when crossing into `Defun` or recurrent scopes. By abstracting the - mechanism, it allows all such state to be handled uniformly and generically. - - Example (typically from `__init__`):: - - class MyAccumulator(base_layer.Accumulator): - def DefaultValue(self): - # [count, min, max] - return tf.convert_to_tensor([0.0, 0.0, 0.0]) - def Update(self, state1): - state0 = self.GetValue() - self.SetValue(tf.stack([ - state0[0] + state1[0], - tf.minimum(state0[1], state1[1]), - tf.maximum(state0[2], state1[2])])) - - self.RegisterAccumulator('mytracker', acc) - - Later, access the current value and update it:: - - acc = self.accumulators.mytracker - acc.Update(tf.convert_to_tensor([1.0, batch_min, batch_max])) - - Then, typically in `PostTrainingStepUpdate`:: - - acc = self.accumulator.mytracker - acc_value = acc.GetValue() - # Do something with the value. - acc.Reset() - - Args: - name: The accumulator name. Shares a namespace with children, vars and - extra theta. - acc: An `Accumulator` instance. - """ - self._CheckName(name) - self._private_accumulators[name] = acc - - def GetAccumulatorValues(self): - """Recursively gets values of all accumulators. - - Returns: - `.NestedMap` of Tensors for each registered accumulator. - """ - return self.accumulators.Transform(lambda acc: acc.GetValue()) - - def SetAccumulatorValues(self, new_values_nmap): - """Recursively sets the values of all accumulators from a map. - - Args: - new_values_nmap: `.NestedMap` of accumulator name:Tensor. - """ - accumulator_list = self.accumulators.Flatten() - value_list = new_values_nmap.Flatten() - for acc, value in zip(accumulator_list, value_list): - acc.SetValue(value) - - def GetVariableSymbolicShape(self, var_name): - """Returns the variable's symbolic shape.""" - return self._var_symbolic_shape_map.get(var_name, None) - - def CreateVariable(self, name, var_params, theta_fn=None, **kwargs): - """Create a variable of this layer according to the parameter `var_params`. - - E.g.:: - - def __init__(self, ...): # A layer's constructor - self.CreateVariable( - 'weight', py_utils.WeightParams(shape=[100, 100])) - - `theta_fn` is used to apply a simple transformation on the created - variable's value before used by the forward computation. E.g., to - add the global variational noise according to this layer's - parameter, one can do:: - - def __init__(self, ...): # A layer's constructor - self.CreateVariable( - name='weight', - var_params=py_utils.WeightParams(shape=[100, 100]), - theta_fn=self.AddGlobalVN) - - In some contexts, eg. TPU training, variables may not be created immediately - but rather the creation request will be cached and created later via a call - to layer.InstantiateVariables(). - - Args: - name: Variable name which is used as the key into vars/theta. - var_params: `Params` used to create the variable. - theta_fn: A python function that takes a variable's value and returns a - new value to be used later for computation. Its signature must be - (tf.Tensor) -> (tf.Tensor). - **kwargs: Keyword args passed to `.py_utils.CreateVariable`. - """ - if self._is_variable_free: - raise ValueError('Cannot create variable in variable free layer.') - if self._create_variables_status == _CreateLayerVariablesStatus.COMPLETED: - raise ValueError( - 'CreateVariable call after variable creation has completed! ' - 'CreateVariable should be called in __init__ or _CreateLayerVariables.' - ) - self._CheckName(name) - if (self.params.skip_lp_regularization and - py_utils.SKIP_LP_REGULARIZATION not in var_params.collections): - var_params = py_utils.WeightParams( - shape=var_params.shape, - dtype=var_params.dtype, - init=var_params.init, - collections=(var_params.collections + - [py_utils.SKIP_LP_REGULARIZATION])) - self._var_symbolic_shape_map[name] = var_params.shape - meta = CreateVariableMeta( - var_params=var_params.Copy(), - theta_fn=theta_fn, - kwargs=kwargs) - if self._create_variables_status == _CreateLayerVariablesStatus.IN_PROGRESS: - # If InstantiateVariables has been called, create variable immediately. - self._CreateVariableInternal(name, meta) - else: - # Otherwise cache the variable to be created. - self._variables_to_create[name] = meta - - def _CreateVariableInternal(self, name, meta): - """Immediately creates the variable described by `meta`. - - DO NOT OVERRIDE. For internal use only. Subclasses of BaseLayer should use - self.CreateVariable() to create variables. - - Args: - name: The variable name. - meta: A CreateVariableMeta describing the variable to be created. - """ - meta.kwargs.setdefault('default_seed', self.params.random_seed) - var = py_utils.CreateVariable(name, meta.var_params, **meta.kwargs) - self._private_vars[name] = var - if FLAGS.no_identity_on_vars: - value = var - else: - with tf.device(var.device): - value = tf.identity(var) - if meta.theta_fn is not None: - value = meta.theta_fn(value) - self._private_theta[name] = value - - def InstantiateVariables(self): - """Create variables for this layer and child layers. - - DO NOT OVERRIDE. Override self._CreateLayerVariables instead. - """ - if self._create_variables_status != _CreateLayerVariablesStatus.NOT_CALLED: - return - self._create_variables_status = _CreateLayerVariablesStatus.IN_PROGRESS - - stack_size = len(_CREATE_VARIABLES_STACK.stack) - _CREATE_VARIABLES_STACK.stack.append(self) - try: - self._global_step = py_utils.GetGlobalStep() - self._CreateChildrenVariables() - - if not self._is_variable_free: - self.AddExtraTheta('global_step', self._global_step) - with tf.variable_scope( - py_utils.SanitizeScopeKey(self.params.name), - auxiliary_name_scope=False): - for name, meta in list(self._variables_to_create.items()): - self._CreateVariableInternal(name, meta) - self._CreateLayerVariables() - finally: - assert _CREATE_VARIABLES_STACK.stack[-1] is self - _CREATE_VARIABLES_STACK.stack.pop() - assert len(_CREATE_VARIABLES_STACK.stack) == stack_size - - self._create_variables_status = _CreateLayerVariablesStatus.COMPLETED - - if not _CREATE_VARIABLES_STACK.stack: - # Outermost layer just finished InstantiateVariables. - self._VerifyVarsAndTheta() - - def _CreateChildrenVariables(self): - """Create variables for child layers. - - Should be rarely overridden, only in cases when control over the context of - children InstantiateVariables calls are needed. eg, if children variables - need to be created inside of a specific context manager. - - There are a few cases of this in the codebase marked as for backwards - compability. This is only to ensure that variable scopes remain compatible - through the code migration. New layers should not copy that pattern, and - instead follow the standard pattern of self.CreateChild() in __init__() and - self.CreateVariable() in _CreateLayerVariables(). If you are okay with - breaking old checkpoints, you can go ahead and delete those functions. - """ - with tf.variable_scope( - py_utils.SanitizeScopeKey(self.params.name), - auxiliary_name_scope=False): - for child in self._children_list: - if self._is_variable_free and not child._is_variable_free: # pylint: disable=protected-access - raise ValueError( - 'Variable free layer %s(%s) child %s(%s) has variables.' % - (self.params.name, self.params.cls, child.params.name, - child.params.cls)) - child.InstantiateVariables() - - def _CreateLayerVariables(self): - """Actually create variables for this layer. - - Subclasses should override this function. - - Variables are created inside of tf.variable_scope(self.params.name). - """ - pass - - def AddExtraTheta(self, theta_name, theta_value): - """Add extra `theta` that doesn't directly correspond to `vars`.""" - self._CheckName(theta_name) - self._private_theta[theta_name] = theta_value - self._extra_theta[theta_name] = theta_value - - def AddGlobalVN(self, value): - return py_utils.AddGlobalVN(self.params, value) - - def CreateChild(self, name, params): - """Create a sub layer. - - The created sub layer can be accessed by `name`. E.g.:: - - self.CreateChild('foo', ...) - self.foo.FProp... - - or:: - - self.children['foo'].Fprop... - self.children.foo.Fprop... - - Args: - name: Sub layer name which is used as the key into vars/theta. - params: `Hyperparams` object to instantiate a layer. - """ - if hasattr(self, '_disable_create_child') and self._disable_create_child: - raise ValueError('Attempting to call CreateChild outside of __init__.') - self._CheckName(name) - if not params.name: - params.name = name - p = self.CopyBaseParams(self.params, params.Copy()) - child = p.Instantiate() - self._private_children[name] = child - - def CreateChildren(self, name, params): - """Create a list or dict of sub layers. - - The created sub layer list can be accessed by `name`. E.g.:: - - self.CreateChildren('foo', ...) - self.foo[10].FProp... - - or:: - - self.children['foo'][10].Fprop... - self.children.foo[10].Fprop... - - Args: - name: The name for the sub layers, which is used as the key into - vars/theta. - params: a list or dict of `Hyperparams` objects to create. - """ - if hasattr(self, '_disable_create_child') and self._disable_create_child: - raise ValueError('Attempting to call CreateChildren outside of __init__.') - self._CheckName(name) - - uid = itertools.count() - - def Instantiate(p): - p = self.CopyBaseParams(self.params, p.Copy()) - if not p.name: - p.name = '%s_%d' % (name, next(uid)) - return p.Instantiate() - - self._private_children[name] = py_utils.NestedMap( - sub=params).Transform(Instantiate).sub - - def AddChild(self, name, children): - """Add existing layer or layers as sublayer.""" - for child in py_utils.Flatten(children): - assert isinstance(child, BaseLayer) - self._CheckName(name) - self._private_children[name] = children - - def _AutoAddChild(self, child): - """Record that a layer `child` is instantiated by this layer. - - This method should only be called internally by BaseLayerMeta. - - Args: - child: A sub-layer of this layer. - """ - self._children_list.append(child) - - def _VerifyChildren(self): - """Verify all children created by this layer are via `CreateChild(ren)`.""" - created_children = self._private_children.Flatten() - for v in self._children_list: - if v not in created_children: - tf.logging.info([ - (child.params.name, type(child)) for child in created_children - ]) - raise ValueError( - '%s is not created by BaseLayer.CreateChild(ren) in %r.' % - (v.params.name, self)) - - def _VerifyVarsAndTheta(self): - """Verify that vars and theta have the same nested structure.""" - for child in self._children_list: - child._VerifyVarsAndTheta() # pylint: disable=protected-access - - def MatchKeys(x, y): - assert len(x) <= len(y) - for k in x.keys(): - assert k in y, '%s not in %s.' % (k, y) - if isinstance(x[k], py_utils.NestedMap): - assert isinstance(y[k], py_utils.NestedMap), '%s is not a map' % y[k] - MatchKeys(x[k], y[k]) - - # NOTE: this check can be quadratically expensive. Maybe only - # enable this in unittests. - MatchKeys(self.vars, self.theta) - - # Make sure whatever not in self.vars are in self._extra_theta - for k in self.theta.keys(): - assert k in self.vars or k in self._extra_theta - - def PostTrainingStepUpdate(self, global_step): - """Returns a TF op which will be invoked at each training step. - - Subclasses of `BaseLayer` can implement this method. The method should - return a TF op to be invoked during training after gradients are applied. - - Args: - global_step: the global step. - """ - update_ops = [ - child.PostTrainingStepUpdate(global_step) - for child in self._private_children.Flatten() - ] - return tf.group(*update_ops) - - -def IsLayerParams(x): - return (isinstance(x, hyperparams.InstantiableParams) and - issubclass(x.cls, BaseLayer)) diff --git a/lingvo/core/base_layer_test.py b/lingvo/core/base_layer_test.py deleted file mode 100644 index 78ca2543..00000000 --- a/lingvo/core/base_layer_test.py +++ /dev/null @@ -1,326 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for base_layer.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import hyperparams -from lingvo.core import py_utils -from lingvo.core import test_utils - - -class AddingAccumulator(base_layer.Accumulator): - - def DefaultValue(self): - return tf.convert_to_tensor(0.0) - - def Update(self, new_value): - self.SetValue(self.GetValue() + new_value) - - -def EvalAndFlatten(nmap): - return nmap.Transform(lambda x: x.eval()).FlattenItems() - - -class TestLayer(base_layer.BaseLayer): - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - self.CreateVariable( - 'w', - py_utils.WeightParams( - shape=[4, 4], - dtype=p.dtype, - init=p.params_init, - collections=[self.__class__.__name__ + '_vars'])) - self.CreateVariable( - 'b', - py_utils.WeightParams( - shape=[4], - dtype=p.dtype, - init=py_utils.WeightInit.Constant(), - collections=[ - self.__class__.__name__ + '_vars', - py_utils.SKIP_LP_REGULARIZATION - ])) - - -class BaseLayerTest(test_utils.TestCase): - - def testCopyBaseParams(self): - # CopyBaseParams should only overwrite vn setting when target use - # default vn config. - layer_base_p = base_layer.BaseLayer.Params() - from_param = layer_base_p.Copy() - to_param = layer_base_p.Copy() - from_param.vn.global_vn = True - from_param.random_seed = 1234 - from_param.skip_lp_regularization = True - # Target use default, overwrite. - base_layer.BaseLayer.CopyBaseParams(from_param, to_param) - self.assertTrue(to_param.vn.global_vn) - self.assertEqual(1234, to_param.random_seed) - self.assertTrue(to_param.skip_lp_regularization) - to_param = layer_base_p.Copy() - to_param.vn.per_step_vn = True - to_param.random_seed = 4321 - to_param.skip_lp_regularization = False - # Target does not use default, should not overwrite. - base_layer.BaseLayer.CopyBaseParams(from_param, to_param) - self.assertTrue(to_param.vn.per_step_vn) - self.assertFalse(to_param.vn.global_vn) - self.assertEqual(4321, to_param.random_seed) - self.assertFalse(to_param.skip_lp_regularization) - - def testCreateChildren(self): - layer_p = base_layer.BaseLayer.Params() - layer_p.name = 'test' - layer = layer_p.Instantiate() - layer._disable_create_child = False # pylint: disable=protected-access - layer.CreateChildren('a', [ - layer_p, - [layer_p, layer_p], - { - 'b': layer_p, - 'c': { - 'd': [layer_p, layer_p] - } - }, - ]) - self.assertEqual(len(layer.a), 3) - self.assertEqual(len(layer.a[1]), 2) - self.assertEqual(len(layer.a[2]), 2) - self.assertEqual(len(layer.a[2]['c']['d']), 2) - self.assertEqual(len(layer.vars.a), 3) - self.assertEqual(len(layer.vars.a[1]), 2) - self.assertEqual(len(layer.vars.a[2]), 2) - self.assertEqual(len(layer.vars.a[2]['c']['d']), 2) - self.assertEqual(len(layer.theta.a), 3) - self.assertEqual(len(layer.theta.a[1]), 2) - self.assertEqual(len(layer.theta.a[2]), 2) - self.assertEqual(len(layer.theta.a[2]['c']['d']), 2) - - def testCreateVariable(self): - layer_p = TestLayer.Params().Set(name='test') - layer = layer_p.Instantiate() - self.assertEqual('test/w/var:0', layer.vars.w.name) - self.assertEqual('test/b/var:0', layer.vars.b.name) - self.assertNotIn(layer.vars.w, - tf.get_collection(py_utils.SKIP_LP_REGULARIZATION)) - # 'b' always skips Lp regularization. - self.assertIn(layer.vars.b, - tf.get_collection(py_utils.SKIP_LP_REGULARIZATION)) - - def testCreateVariableSkipLpRegularization(self): - layer_p = TestLayer.Params().Set(name='test', skip_lp_regularization=True) - layer = layer_p.Instantiate() - self.assertIn(layer.vars.w, - tf.get_collection(py_utils.SKIP_LP_REGULARIZATION)) - self.assertIn(layer.vars.b, - tf.get_collection(py_utils.SKIP_LP_REGULARIZATION)) - - def testGetDescendant(self): - q = base_layer.BaseLayer.Params() - q.name = 'test' - # pylint: disable=protected-access - l = q.Instantiate() - p = base_layer.BaseLayer.Params() - l._disable_create_child = False - l.CreateChild('a', p) - l.CreateChild('b', p) - l.a._disable_create_child = False - l.a.CreateChild('c', p) - l.a.c._disable_create_child = False - l.a.c.CreateChild('d', p) - l.b._disable_create_child = False - l.b.CreateChild('e', p) - # pylint: enable=protected-access - self.assertEqual(l, l.GetDescendant('')) - self.assertEqual(l.a, l.GetDescendant('a')) - self.assertEqual(l.b, l.GetDescendant('b')) - self.assertEqual(l.a.c, l.GetDescendant('a.c')) - self.assertEqual(l.a.c.d, l.GetDescendant('a.c.d')) - self.assertEqual(l.b.e, l.GetDescendant('b.e')) - - def testCreateAccumulator(self): - layer_p = base_layer.BaseLayer.Params() - layer_p.name = 'test' - layer = layer_p.Instantiate() - layer._disable_create_child = False # pylint: disable=protected-access - layer.CreateChild('child', layer_p) - - # First accumulator should succeed. - acc1 = AddingAccumulator() - layer.RegisterAccumulator('acc1', acc1) - - # Name of existing child should fail. - with self.assertRaises(AssertionError): - layer.RegisterAccumulator('child', AddingAccumulator()) - - # Duplicate should fail. - with self.assertRaises(AssertionError): - layer.RegisterAccumulator('acc1', AddingAccumulator()) - - # Child with the same name should fail. - with self.assertRaises(AssertionError): - layer.CreateChild('acc1', layer_p) - - self.assertEqual(acc1, layer.accumulators.acc1) - - # Get of not created accumulator should fail. - with self.assertRaises(AttributeError): - layer.accumulators.notexist - - def testGetUpdateAccumulator(self): - with self.session(): - layer_p = base_layer.BaseLayer.Params() - layer_p.name = 'test' - layer = layer_p.Instantiate() - - layer.RegisterAccumulator('acc1', AddingAccumulator()) - - # Initial value. - self.assertEqual(0.0, layer.accumulators.acc1.GetValue().eval()) - - # Update/merge. - layer.accumulators.acc1.Update(1.0) - layer.accumulators.acc1.Update(1.0) - self.assertEqual(2.0, layer.accumulators.acc1.GetValue().eval()) - - # Reset. - layer.accumulators.Transform(lambda acc: acc.Reset()) - self.assertEqual(0.0, layer.accumulators.acc1.GetValue().eval()) - - def testAccumulatorDisableEnable(self): - with self.session(): - layer_p = base_layer.BaseLayer.Params() - layer_p.name = 'test' - layer = layer_p.Instantiate() - - layer.RegisterAccumulator('acc1', AddingAccumulator()) - layer.accumulators.acc1.Update(1.0) - - # Disable should force value to 0 and reject updates. - layer.accumulators.Transform(lambda acc: acc.Disable()) - self.assertEqual(0.0, layer.accumulators.acc1.GetValue().eval()) - layer.accumulators.acc1.Update(3.0) - self.assertEqual(0.0, layer.accumulators.acc1.GetValue().eval()) - layer.accumulators.Transform(lambda acc: acc.Enable()) - - # Should restore. - self.assertEqual(1.0, layer.accumulators.acc1.GetValue().eval()) - - def testGetSetAccumulatorValues(self): - with self.session(): - layer_p = base_layer.BaseLayer.Params() - layer_p.name = 'test' - layer1 = layer_p.Instantiate() - layer1._disable_create_child = False # pylint: disable=protected-access - layer1.CreateChild('layer1a', layer_p) - layer1.CreateChild('layer1b', layer_p) - layer1.layer1b._disable_create_child = False # pylint: disable=protected-access - layer1.layer1b.CreateChild('layer1b1', layer_p) - - # Create nested accumulators: - # acc1: layer1 - # acc2: layer1.layer1a - # acc3: layer1.layer1b.layer1b1 - layer1.RegisterAccumulator('acc1', AddingAccumulator()) - layer1.layer1a.RegisterAccumulator('acc2', AddingAccumulator()) - layer1.layer1b.layer1b1.RegisterAccumulator('acc3', AddingAccumulator()) - - # Pack with initial values. - initial_pack = layer1.GetAccumulatorValues() - initial_pack_eval = EvalAndFlatten(initial_pack) - print('Initial values pack =', initial_pack_eval) - self.assertEqual(initial_pack_eval, [('acc1', 0.0), ('layer1a.acc2', 0.0), - ('layer1b.layer1b1.acc3', 0.0)]) - - # Update to a new known state. - layer1.accumulators.acc1.Update(1.0) - layer1.layer1a.accumulators.acc2.Update(2.0) - layer1.layer1b.layer1b1.accumulators.acc3.Update(3.0) - updated_pack = layer1.GetAccumulatorValues() - updated_pack_eval = EvalAndFlatten(updated_pack) - print('Updated values pack =', updated_pack_eval) - self.assertEqual(updated_pack_eval, [('acc1', 1.0), ('layer1a.acc2', 2.0), - ('layer1b.layer1b1.acc3', 3.0)]) - - # Save and reset. - saved_pack = layer1.GetAccumulatorValues() - layer1.accumulators.Transform(lambda acc: acc.Reset()) - self.assertEqual(0.0, layer1.accumulators.acc1.GetValue().eval()) - self.assertEqual(0.0, layer1.layer1a.accumulators.acc2.GetValue().eval()) - self.assertEqual( - 0.0, - layer1.layer1b.layer1b1.accumulators.acc3.GetValue().eval()) - - # Set and check. - layer1.SetAccumulatorValues(saved_pack) - self.assertEqual(1.0, layer1.accumulators.acc1.GetValue().eval()) - self.assertEqual(2.0, layer1.layer1a.accumulators.acc2.GetValue().eval()) - self.assertEqual( - 3.0, - layer1.layer1b.layer1b1.accumulators.acc3.GetValue().eval()) - - def testAddFunction(self): - layer_p = base_layer.BaseLayer.Params() - layer_p.name = 'test' - layer = layer_p.Instantiate() - - layer.AddFunction('test1', lambda: 1) - with self.assertRaises(AttributeError): - layer.AddFunction('test1', lambda: 2) - self.assertEqual(1, layer.fns.test1()) - - def testAttributeErrorInPropertyGetter(self): - - class BadLayer(base_layer.BaseLayer): - - @classmethod - def Params(cls): - return super().Params() - - def __init__(self, params): - super().__init__(params) - - @property - def bad_property(self): - raise AttributeError('INTERNAL') - - layer_p = BadLayer.Params() - layer_p.name = 'test' - layer = layer_p.Instantiate() - - with self.assertRaisesRegex(AttributeError, 'bad_sub_layer'): - _ = layer.bad_sub_layer - - with self.assertRaisesRegex(AttributeError, 'INTERNAL'): - _ = layer.bad_property - - def testIsLayerParams(self): - self.assertTrue(base_layer.IsLayerParams(base_layer.BaseLayer.Params())) - self.assertTrue(base_layer.IsLayerParams(TestLayer.Params())) - self.assertFalse(base_layer.IsLayerParams(None)) - self.assertFalse(base_layer.IsLayerParams(hyperparams.Params())) - self.assertFalse( - base_layer.IsLayerParams( - hyperparams.InstantiableParams(base_layer.Accumulator))) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/base_model.py b/lingvo/core/base_model.py deleted file mode 100644 index 6ceecea7..00000000 --- a/lingvo/core/base_model.py +++ /dev/null @@ -1,1229 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Base model.""" - -import collections -import re - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_layer -from lingvo.core import build_data -from lingvo.core import cluster_factory -from lingvo.core import early_stop -from lingvo.core import hyperparams -from lingvo.core import learner -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import summary_utils -from lingvo.core import task_scheduler -from lingvo.core import decoder_lib -from model_pruning.python import pruning - - -class DecodeFinalizeArgs( - collections.namedtuple('DecodeFinalizeArgs', - ['decode_out_path', 'decode_out'])): - """Arguments to BaseTask.DecodeFinalize(). - - Attributes: - decode_out_path: Path to where decoder outputs can be written. - decode_out: A list of key value pairs aggregated from return values of. - PostProcessDecodeOut(). - """ - - -class BaseTask(base_layer.BaseLayer): - """A single encoder/decoder task. - - One task usually consists of one InputGenerator, one train_op, - a list of eval_metrics, etc. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input', None, 'Input generator Params.') - p.Define('encoder', None, 'Encoder Params.') - p.Define('online_encoder', None, 'Online Encoder Params.') - p.Define('decoder', None, 'Decoder Params.') - p.Define( - 'task_global_step', False, - 'Whether or not to use task-specific global steps, which causes each ' - 'task to use its own global_step instead of the true global_step. ' - 'NOTE: this may be severely broken. Verify carefully!') - p.Define( - 'defer_global_step_update', False, - 'Whether or not to defer the global step update. This is used when ' - 'doing gradient accumulation, which update the global step only when ' - 'weights are updated. Currently this supports only true global step.') - p.Define('train', hyperparams.Params(), - 'Params to control how this task should be trained.') - - p.Define('ml_perf', hyperparams.Params(), 'MlPerf configuration.') - - tp = p.train - tp.Define( - 'start_up_delay_steps', 200, 'i-th replica starts training after ' - 'i*(i+1)/2*start_up_delay_steps steps') - tp.Define('max_steps', 4 * 10**6, 'Maximum number of training steps.') - tp.Define('tpu_steps_per_loop', 1000, 'The number of training steps per ' - 'training loop for TPUs.') - tp.Define( - 'vn_start_step', 200000000, - 'Step starting from which variational noise is added to ' - 'params values during training.') - tp.Define('vn_std', 0.0, 'Std of the variational noise.') - tp.Define('early_stop', early_stop.EarlyStop.Params(), - 'Early stopping based on dev-set performance.') - tp.Define( - 'ema_decay', 0.0, - 'If > 0, enable ExponentialMovingAverage during training ' - 'with the give decay. ' - 'Must be < 1. Disabled if <= 0.') - tp.Define( - 'ema_decay_moving_vars', None, - 'If True, include variables from collection "moving_vars" in ema.') - tp.Define( - 'init_from_checkpoint_rules', {}, - 'If not None, a dictionary with keys corresponding to a checkpoint ' - 'path and values corresponding to variable loading rules is expected. ' - 'Each key is expected to be a path to a checkpoint from which to ' - 'initialize part of the model. Variables are only loaded from this ' - 'path during initialization and will override values provided by ' - 'initialization.' - 'The corresponding values (loading_rules) are expected to be a tuple ' - 'consisting of two list: loading rules, and ignore rules, respectively.' - 'The first list (loading rules) contains the list of variables ' - 'which should be initialized from the checkpoint: each element in the ' - 'list is a pair of strings. The first element is a regex and the ' - 'second is a python format string. If a variable in the model matches ' - 'a regex, we rename using the format string to determine the ' - 'corresponding var in the checkpoint. Note that, it is an error if a ' - 'model variable matches multiple loading rules, for the same ' - 'checkpoint or across checkpoints.' - 'The second list (ignore rules) is a list of regexes which specify ' - 'variables in the model which should not be initialized using the ' - 'loading rules. Thus, if a variable in the model to be trained matches ' - 'one of the rules in the loading rules, as well as one of the regular ' - 'expressions in the ignore rules, the variable will not be initialized ' - 'from the checkpoint, but will instead be initialized from the ' - 'variable initalizer defined in the graph.' - 'Examples:' - '{"checkpoint_path": ([("(.*)", "%s")], [])} will initialize all the ' - 'model parameters from the checkpoint_path.') - tp.Define( - 'pruning_hparams_dict', None, 'Pruning related hyperparameters. A dict ' - 'with hyperparameter: value pairs. See google-research.model_pruning.') - tp.Define( - 'enqueue_max_steps', -1, 'Max enqueue steps. -1 meaning no limit.' - ' This flag should be set for unit-test only.') - tp.Define('save_interval_seconds', 60 * 10, - 'Generates a checkpoint roughly once every this many seconds.') - tp.Define('save_max_to_keep', 100, - 'Maximum number of recent checkpoints to keep.') - tp.Define('save_keep_checkpoint_every_n_hours', 0.5, - 'How often to keep a checkpoint.') - - tp.Define('summary_interval_steps', 100, - 'Generates a summary roughly once every this many steps.') - # The following params must mirror those in Learner.Params(). - # TODO(rpang): migrate existing params to use learner and - # delete legacy params. - # LINT.IfChange - tp.Define( - 'learner', None, 'One or a list of optimization programs. ' - 'If None, uses a Learner created from the legacy params ' - 'defined below: learning_rate, lr_schedule, optimizer, etc.') - tp.Define( - 'l2_regularizer_weight', None, - 'If not None, L2 regularization to apply to the weights. ' - 'Otherwise, disable L2 regularization.') - tp.Define( - 'l1_regularizer_weight', None, - 'If not None, L1 regularization to apply to the weights. ' - 'Otherwise, disable L1 regularization.') - tp.Define('learning_rate', 0.0, 'learning rate to use.') - tp.Define( - 'clip_gradient_norm_to_value', 0.0, - 'Clip gradient by global norm to this value. This is similar to ' - 'the bahaviour of tf.clip_by_global_norm, if you are looking for ' - 'tf.clip_by_norm refer to clip_gradient_single_norm_to_value. Note ' - 'these are mutually exclusive.') - tp.Define( - 'clip_gradient_single_norm_to_value', 0.0, - 'Clip gradient by single tensor norm to this value. This is ' - 'similar to the bahaviour of tf.clip_by_norm. Note this is mutually ' - 'exlusive to using clip_gradient_norm_to_value.') - tp.Define('grad_norm_to_clip_to_zero', 0.0, - 'Clip gradient to 0 if its norm exceeds this value.') - tp.Define('grad_norm_tracker', None, 'Params for GradNormTracker.') - tp.Define('optimizer', optimizer.Adam.Params(), 'Params for the optimizer.') - tp.Define('lr_schedule', schedule.ContinuousSchedule.Params(), - 'Learning rate decay schedule.') - tp.Define( - 'bprop_variable_filter', None, - 'If set, only backprop variables whose names partially match ' - 'this regexp (re.search).') - tp.Define( - 'bprop_variable_exclusion', None, - 'If set, do not backprop variables whose names partially match ' - 'this regexp (re.search).') - tp.Define( - 'grad_aggregation_method', tf.AggregationMethod.EXPERIMENTAL_TREE, - 'Specifies the method used to combine gradient terms. Accepted ' - 'values are constants defined in the class AggregationMethod.') - tp.Define( - 'gate_gradients', False, - 'If True, add a tuple around the gradients returned for an ' - 'operations. This avoids some race conditions.') - tp.Define('colocate_gradients_with_ops', True, - 'If True, try colocating gradients with the corresponding op.') - tp.Define('scale_gradients', True, - 'Whether to apply gradients adjustment and scaling.') - # LINT.ThenChange(learner.py) - p.Define('eval', hyperparams.Params(), - 'Params to control how this task should be evaled.') - ep = p.eval - ep.Define( - 'samples_per_summary', 1000, - 'If > 0, generates one summary after this many samples, at most. ' - 'If == 0 or the dataset has fewer examples, evaluate the whole set.') - ep.Define( - 'decoder_samples_per_summary', 0, - 'If > 0, each decoder summary will contain at most this many samples. ' - 'If == 0, defaults to `samples_per_summary` for ' - 'backwards compatibility.') - ep.Define( - 'load_checkpoint_from', None, - 'If not None, specifies a location for the checkpoint that ' - 'should be used for eval. One example format is a ' - 'checkpoint directory of a training run.') - ep.Define('start_eval_after', 0, - 'Start evaluation after specified number of steps.') - ep.Define('start_decoder_after', 0, - 'Only decode checkpoints after this step.') - return p - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Updates params with the vocab size and wpm model. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model params updated with the vocab size and wpm model. - """ - dp = p.decoder - p.decoder = dp.cls.UpdateTargetVocabSize(dp, vocab_size, wpm_model) - return p - - def __init__(self, params): - assert issubclass(params.cls, BaseTask) - # Ensure global_step exists before calling super. - py_utils.GetOrCreateGlobalStepVar() - super().__init__(params) - - p = self.params - - self._encoder = None - self._online_encoder = None - self._decoder = None - - self._loss = None - self._num_predictions = None - self._train_op = None - self._post_train_ops = [] - self._eval_metrics = {} - self._per_example = {} - - # Create the gradient mask, - self._per_input_gradient_mask = None - - if p.task_global_step: - with tf.name_scope(None), tf.variable_scope( - py_utils.GetGlobalVariableScope()): - var_name = p.name + '_global_step' - # Create the variable immediately. - self._CreateVariableInternal( - var_name, - base_layer.CreateVariableMeta( - var_params=py_utils.WeightParams( - [], py_utils.WeightInit.Constant(0), tf.int64), - theta_fn=None, - kwargs=dict( - trainable=False, - collections=[tf.GraphKeys.GLOBAL_VARIABLES]))) - summary_utils.scalar(var_name, self._private_vars[var_name]) - self._global_step_var = self._private_vars[var_name] - else: - self._global_step_var = py_utils.GetOrCreateGlobalStepVar() - - if p.input: - # TODO(zhifengc): Consider a simpler way to ensure the input - # generator stops after one epoch. - if self.do_eval and p.eval: - seq_inp = issubclass(p.input.cls, - base_input_generator.BaseInputGeneratorFromFiles) - if p.input.num_samples == 0: - # Dataset size is unknown. Computes eval summary based on num_samples. - assert p.eval.samples_per_summary > 0 - elif (p.eval.samples_per_summary == 0) or (p.input.num_samples < - p.eval.samples_per_summary): - # If we know the dataset size and we want to evaluate the full - # set, we need to coordinate the input generator to flush out - # all samples so the evaler and decoder compute metrics on the - # whole set for each summary step. - if seq_inp: - p.input.flush_every_n = p.input.num_samples - p.eval.samples_per_summary = p.input.num_samples - if seq_inp and p.input.num_batcher_threads > 1: - tf.logging.warning( - 'input.num_batcher_threads > 1 inside eval mode. ' - 'The input generator may not iterate over exactly ' - 'one epoch per run') - tf.logging.info('input_params: %s', p.input) - input_params = self.cluster.PlaceInput(p.input) - - # For TPU training, we create the input generator in a - # different scope and AddChild it in later. - if 'skip_create_child' not in p.input: - self.CreateChild('input', input_params) - - tp = p.train - - # p.train can be None if this task is the teacher/student task in a - # DistillationTask. - if tp: - self._SetLearnerFromLegacyParams(tp) - if tp.learner is not None: - if isinstance(tp.learner, (list, tuple)): - self.CreateChildren('learners', tp.learner) - else: - self.CreateChildren('learners', [tp.learner]) - self._UpdateVnConfig() - - def InstantiateVariables(self): - with py_utils.GlobalStepContext( - tf.identity(self._global_step_var, name='global_step_tensor')): - super().InstantiateVariables() - - def _SetLearnerFromLegacyParams(self, tp): - """Sets tp.learner based on legacy params.""" - if tp.learner is not None: - return - tp.learner = learner.ExtractLearnerFromLegacyParams(tp) - - def ComputePredictions(self, theta, input_batch): - """Computes predictions for `input_batch`. - - The output can be in the form of probablistic distributions, e.g., softmax - logits for discrete outputs, mixture of logistics for continuous values, or - regression values. - - For training/evaluation, the output will be used for computing loss and - gradient updates, including comparing predicted distributions between - teacher and student for distillation. During inference the output can be - used to compute final outputs, perhaps with sampling. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - input_batch: A `.NestedMap` object containing input tensors to this tower. - - Returns: - Predictions, either a single Tensor, a `.NestedMap`, or a namedtuple. - """ - raise NotImplementedError('Abstract method') - - def ComputeLoss(self, theta, predictions, input_batch): - """Computes loss and other metrics for the given predictions. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - predictions: The output of `ComputePredictions`. - input_batch: A `.NestedMap` object containing input tensors to this tower. - - Returns: - (dict, dict): - - - A dict containing str keys and (metric, weight) pairs as values, where - one of the keys is expected to be 'loss'. - - A dict containing arbitrary tensors describing something about each - training example, where the first dimension of each tensor is the batch - index. - """ - raise NotImplementedError('Abstract method') - - def FilterPerExampleTensors(self, per_example): - """Return the per-example tensors ProcessFPropResults needs. - - By default we don't send any per-example tensors to ProcessFPropResults - because some may be expensive to compute. Implement this method to let - some of them pass through. - - Args: - per_example: A dict of tensors returned as per-example tensors from FProp. - - Returns: - A dict containing a subset of the key/value pairs in per_example. - """ - return {} - - def ProcessFPropResults(self, sess, global_step, metrics, per_example): - """Called once for each train loop. - - BaseModel.ProcessFPropResults is also called on each loop, so you - can put your implementation wherever it is most convenient for you. - - Be sure to implement BaseTask.FilterPerExampleTensors if you plan to use any - per-example tensors in this method. - - Args: - sess: a session. - global_step: task global step. Since ProcessFPropResults is called after - sess.run(train_op), this value will be 1 higher than the value in FProp. - metrics: the metrics dict returned by FPropTower. - per_example: the per_example dict returned by FPropTower. - """ - pass - - def FPropTower(self, theta, input_batch): - """Forward propagation through one tower of the model. - - Args: - theta: A `.NestedMap` object containing variable values of this task - copied to this tower's devices. - input_batch: A `.NestedMap` object containing input tensors to this tower. - - Returns: - (dict, dict): - - - A dict containing str keys and (metric, weight) pairs as values, where - one of the keys is expected to be 'loss'. - - A dict containing arbitrary tensors describing something about each - training example, where the first dimension of each tensor is the batch - index. - """ - predictions = self.ComputePredictions(theta, input_batch) - return self.ComputeLoss(theta, predictions, input_batch) - - def FProp(self, theta, input_batch): - """Forward propagation. - - This default `FProp` implementation here supports batch splitting in - synchronous and asynchronous training when sub-classes implement - `FPropTower`. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - input_batch: The input batch. A `NestedMap` of tensors. Or, if input batch - spiltting is used, a list of `NestedMap`, one for each split. - - Returns: - (dict, dict): - - - A dict containing str keys and (metric, weight) pairs as values, where - one of the keys is expected to be 'loss'. - - A dict containing arbitrary tensors describing something about each - training example, where the first dimension of each tensor is the batch - index. - """ - p = self.params - with tf.name_scope('fprop'), tf.name_scope(p.name): - # Always reset step seed at the start of a new global_step. - py_utils.ResetStepSeed() - metrics, per_example = self._FPropSplitInputBatch(theta, input_batch) - self._FPropResult(metrics, per_example) - return metrics, per_example - - def _FPropTpu(self, theta, input_batch): - with tf.name_scope('tower_0_0'): - metrics, per_example = self.FPropTower(theta, input_batch) - metrics = py_utils.WeightedAvgOfMetrics([metrics]) - return metrics, per_example - - def _FPropSplitInputBatch(self, theta, input_batch): - """Splits the input batch on the input device.""" - if py_utils.use_tpu(): - return self._FPropTpu(theta, input_batch) - - cluster = self.cluster - num_splits = cluster.num_splits_per_client - - if not isinstance(input_batch, list): - input_batch = [input_batch] - - assert len(input_batch) == num_splits, (len(input_batch), num_splits) - - # dev_list_per_replica[i][j] is the i-th worker's j-th device. - dev_list_per_replica = cluster.available_devices.tolist() - - # Asserts invariant of the total number of splits w.r.t., - # splits per worker. - splits_per_replica = cluster.num_splits_per_replica - assert num_splits == splits_per_replica * len(dev_list_per_replica), ( - num_splits, splits_per_replica, len(dev_list_per_replica)) - - all_metrics = [] - all_per_example_tensors = [] - with cluster: - for w_id, w_devs in enumerate(dev_list_per_replica): - # Make local copy of the vars, shard on devices for this worker. - theta_local = py_utils.CreateLocalTheta( - theta, w_devs, label='worker %d' % w_id) - - for s_id in range(splits_per_replica): - # s_id-th split for the w_id-th worker. - split_id = splits_per_replica * w_id + s_id - with cluster_factory.SetModelSplit(split_id) as c: - with tf.device(c.WorkerDeviceInModelSplit(0)): - with tf.name_scope('tower_%d_%d' % (w_id, s_id)): - batch = input_batch[split_id] - metrics, per_example = self.FPropTower(theta_local, batch) - all_metrics.append(metrics) - all_per_example_tensors.append(per_example) - return py_utils.WeightedAvgOfMetrics( - all_metrics), py_utils.ConcatPerExampleTensors(all_per_example_tensors) - - def _FPropResult(self, metrics, per_example): - # Adds stats about the input batch. - p = self._params - if p.input is not None: - metrics['num_samples_in_batch'] = (tf.convert_to_tensor( - self.input_generator.GlobalBatchSize()), tf.constant(1.0)) - # Generates summaries. - for name, (value, weight) in metrics.items(): - self.AddEvalMetric(name, value, weight) - per_example = self.FilterPerExampleTensors(per_example) - for name, value in per_example.items(): - self.AddPerExampleTensor(name, value) - # Loss. - self._loss, self._num_predictions = metrics['loss'] - self._loss = py_utils.CheckNumerics(self._loss) - self._metrics = metrics - summary_utils.scalar('num_predictions', self._num_predictions) - - def GetInputBatch(self): - """Gets an input batch.""" - if py_utils.use_tpu(): - return self.input_generator.TpuDequeueBatch() - else: - return self.input_generator.SplitInputBatch( - self.cluster.num_splits_per_client) - - def FPropDefaultTheta(self, input_batch=None): - """Calls `FProp` with this layer's parameters.""" - if input_batch is None: - input_batch = self.GetInputBatch() - return self.FProp(self.theta, input_batch) - - def AdjustGradients(self, vars_gradients): - """Allow for custom gradient manipulation prior to clipping.""" - tf.logging.info('BaseTask.AdjustGradients') - return vars_gradients - - def PostTrainingLoop(self): - self._post_training_loop_op = tf.group(*[ - opt.ApplyPostTrainingLoop(self._global_step_var) - for opt in self.learners - ]) - - def BProp(self): - self._BPropForVariables(self.vars) - - def _BPropGenTrainOps(self, vmap, metrics=None, add_summary=True): - """Populates the train_ops dictionary in a backwards pass.""" - metrics = metrics or self._metrics - - bprop_variable_filters = self.input_generator.GetBpropVariableFilters() - # Only compute the mask if the variable filters are not empty. - if bprop_variable_filters != [''] * len(bprop_variable_filters): - self._ComputeGradientMask(bprop_variable_filters) - train_ops = {} # mapping from op name to op. - gradient_mask = None - if self._per_input_gradient_mask: - # TODO(neerajgaur): Change this to use source_selected from input_batch. - onehot = self.input_generator.GetInputSourceOneHot() - gradient_mask = { - k: tf.tensordot(v, onehot, 1) - for k, v in self._per_input_gradient_mask.items() - } - all_losses = [] - for optimization in self.learners: - learner_name = optimization.params.name - (losses, train_ops['train/%s' % learner_name], - eval_metrics) = optimization.Apply( - metrics, - vmap, - gradient_mask=gradient_mask, - gradient_adjuster=self.AdjustGradients) - all_losses.extend(losses) - if add_summary: - for key, (value, weight) in eval_metrics.items(): - self.AddEvalMetric(key + '/' + learner_name, value, weight) - - relevant_bn_updates, _ = py_utils.FindRelevantBatchNormUpdates( - all_losses, tf.get_collection(py_utils.BATCH_NORM_UPDATES)) - train_ops['bn_updates'] = relevant_bn_updates - - var_update_ops = [ - tf.group(*tf.nest.flatten(train_ops), name='var_update_ops') - ] - # Post training step update. - with tf.control_dependencies(var_update_ops): - post_step_op = self.PostTrainingStepUpdate(self.global_step) - - train_ops = {} - with tf.control_dependencies([post_step_op]): - # Get the op to update the weight masks and thresholds - mask_update_op = self._GetMaskUpdateOp() - train_ops['mask_updates'] = mask_update_op - with tf.control_dependencies([mask_update_op]): - true_global_step = py_utils.GetOrCreateGlobalStepVar() - with tf.ops.colocate_with(true_global_step): - if self.params.defer_global_step_update: - increment_global_steps = true_global_step - else: - increment_global_steps = tf.assign_add(true_global_step, 1) - if self._global_step_var != true_global_step: - with tf.ops.colocate_with(self._global_step_var): - increment_global_steps = tf.group( - increment_global_steps, tf.assign_add(self._global_step_var, 1)) - train_ops['global_step'] = increment_global_steps - - # If we are using Tpu Embeddings, generate the monolithic send - # gradient op. - tpu_embedding_activations = tf.get_collection( - py_utils.TPU_EMBEDDING_ACTIVATIONS) - if tpu_embedding_activations: - tpu_embedding_activations_dict = tpu_embedding_activations[0] - tpu_embedding = tf.get_collection(py_utils.TPU_EMBEDDING)[0] - tpu_embedding_send_gradient_op = py_utils.ComputeTpuEmbeddingGradients( - self.loss, tpu_embedding_activations_dict, tpu_embedding) - train_ops['tpu_embedding'] = tpu_embedding_send_gradient_op - - for op_name, op in train_ops.items(): - assert op is not None, op_name - return train_ops - - def _BPropForVariables(self, vmap): - """Constructs the backward graph.""" - train_ops = self._BPropGenTrainOps(vmap) - - # TODO(rpang): try to structure _train_op as: - # tf.cond(skip_step, , ) - # so that we skip all other updates when a step is skipped. - with tf.control_dependencies( - [tf.group(*tf.nest.flatten(train_ops), name='train_ops')]): - self._train_op = tf.group(self._post_train_ops, name='bprop') - - def _ComputeGradientMask(self, bprop_variable_filters): - """Compute gradient mask for each variable and bprop_variable_filters. - - Note that per_input_gradient_mask[var][i] will be 1 if var matches - bprop_variable_filter[i], 0 otherwise. - - Args: - bprop_variable_filters: A list of regex bprop_variable_filters for each - file pattern. - """ - self._per_input_gradient_mask = py_utils.NestedMap() - all_vars = set(self.vars.Flatten()) - for var in all_vars: - self._per_input_gradient_mask[var.name] = ( - tf.zeros(len(bprop_variable_filters), dtype=tf.float32)) - for i in range(len(bprop_variable_filters)): - if re.search(bprop_variable_filters[i], var.name): - tf.logging.info( - 'Keep gradient after filtering, regex: %s var: %s' % - (bprop_variable_filters[i], var.name)) - self._per_input_gradient_mask[var.name] += ( - tf.one_hot(i, len(bprop_variable_filters), dtype=tf.float32)) - - def ApplyExponentialMovingAverage(self, ema): - """Wraps `self.train_op` with an op updating exponential moving average.""" - if (self._create_variables_status != - base_layer._CreateLayerVariablesStatus.COMPLETED): # pylint: disable=protected-access - raise ValueError( - 'ApplyExponentialMovingAverage called before InstantiateVariables!') - # TODO(rpang): raise an exception if this is called in the eval mode. - p = self.params - # We need to apply EMA to trainable and moving average variable of this - # Task, not just bprop vars, so that we create a shadow - # '/ExponentialMovingAverage' variable for every trainable and moving - # average variable. - all_vars = set(tf.trainable_variables()) | set( - tf.moving_average_variables()) - if p.train.ema_decay_moving_vars: - all_vars |= set(tf.get_collection('moving_vars')) - all_vars &= set(self.vars.Flatten()) - for var in all_vars: - tf.logging.debug('ApplyExponentialMovingAverage: %s', var.name) - with tf.name_scope('moving_average'): - self._post_train_ops.append(ema.apply(all_vars)) - - # TODO(blee): Rename Decode->DecodeWithDefaultTheta, DecodeWithTheta->Decode. - def Decode(self, input_batch): - """Constructs the inference graph for eval decoding. - - Args: - input_batch: The input batch. A `NestedMap` of tensors. Or, if input batch - spiltting is used, a list of `NestedMap`, one for each split. - - Returns: - a dict of Tensors as decoder output. - """ - return self.DecodeWithTheta(self.theta, input_batch) - - def DecodeWithTheta(self, theta, input_batch): - """Constructs the inference graph for eval decoding with theta. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - input_batch: The input batch. A `NestedMap` of tensors. Or, if input batch - spiltting is used, a list of `NestedMap`, one for each split. - - Returns: - a dict of Tensors as decoder output. - """ - return {} - - def Inference(self): - """Constructs the inference graph. - - Each subgraph represents a public API for a part of the graph which can - be operated independently. By convention, the subgraph named 'default' - should perform end to end inference via the input generator. - - Note that having distinct subgraphs (e.g. 'encoder', 'decoder') is - not just a space optimization: when driving the graph externally in an - online fashion, evaluation often needs to be broken into pieces. In this - case, the graph will be constructed with only those pieces. - - Returns: - An `inference_graph_pb2.InferenceGraph` message. - """ - raise NotImplementedError('Abstract method') - - def CreateDecoderMetrics(self): - """Creates a dict of decoder metrics for `PostProcessDecodeOut` to update. - - Returns a dict mapping from string keys to `.BaseMetric` objects. - """ - pass - - def PostProcessDecodeOut(self, decode_out_dict, decode_metrics_dict): - """Post-processes decoder out and updates contents of `decode_metrics_dict`. - - Args: - decode_out_dict: A dictionary of Tensors fetched. - decode_metrics_dict: A dict mapping from string key to `.BaseMetric` - object as created by `CreateDecoderMetrics`. - - Returns: - output_key_value_pairs - a list of (key, value) pairs that can be saved - (i.e. of type str, bytes, or unicode). - """ - pass - - def DecodeFinalize(self, decode_finalize_args): - """Finalize any work for decoding. - - Args: - decode_finalize_args: A DecodeFinalizeArgs namedtuple. - """ - decode_out_path = decode_finalize_args.decode_out_path - decode_out = decode_finalize_args.decode_out - if decode_out: - decoder_lib.WriteKeyValuePairs(decode_out_path, decode_out) - - @property - def loss(self): - assert self._loss is not None, ('No loss is defined. Call FProp first.') - return self._loss - - @property - def train_op(self): - assert self._train_op is not None, ( - 'No train op is defined. Call BProp first.') - return self._train_op - - @property - def post_training_loop_op(self): - assert self._post_training_loop_op is not None, ( - 'No post_training_loop_op op is defined. Call PostTrainingLoop first.') - return self._post_training_loop_op - - @property - def global_step(self): - return self._global_step_var - - @property - def input_generator(self): - return self.input - - @property - def eval_metrics(self): - """Returns the evaluation metrics. - - Returns: - A map from metric name (a python string) to a tuple (value, weight). - Both value and weight are scalar Tensors. - """ - return self._eval_metrics - - @property - def per_example_tensors(self): - """Returns per-example outputs. - - Returns: - A map from tensor name (a python string) to a tensor, where the - first dimension is the batch index of the training example corresponding - to this output. - """ - return self._per_example - - def AddEvalMetric(self, name, value, weight): - """Adds a metric to the eval metrics. - - Args: - name: A python string. The name of the metric. - value: A scalar Tensor. - weight: A scalar Tensor. - - Raises: - ValueError: if `name` is already defined. - - """ - if name in self._eval_metrics: - raise ValueError('Metric %s has already been defined.' % name) - self._eval_metrics[name] = (value, weight) - - def AddPerExampleTensor(self, name, value): - if name in self._per_example: - raise ValueError('Metric %s has already been defined.' % name) - self._per_example[name] = value - - def _UpdateVnConfig(self): - """Update vn config from the various vn flags.""" - p = self.params - tp = p.train - if tp: - vn_enabled = ((tp.vn_std > 0) and p.vn and - (p.vn.global_vn or p.vn.per_step_vn)) - if self.do_eval or (not vn_enabled): - p.vn = py_utils.VariationalNoiseParams(None, False, False) - else: - # vn.scale is dependent on global_step. - p.vn.scale = tf.cast(self.global_step > tp.vn_start_step, - py_utils.FPropDtype(p)) * tp.vn_std - - def _GetMaskUpdateOp(self): - """Returns op to update masks and threshold variables for model pruning.""" - p = self.params - tp = p.train - mask_update_op = tf.no_op() - if tp.pruning_hparams_dict: - assert isinstance(tp.pruning_hparams_dict, dict) - pruning_hparams = pruning.get_pruning_hparams().override_from_dict( - tp.pruning_hparams_dict) - pruning_obj = pruning.Pruning( - pruning_hparams, global_step=self.global_step) - if self.cluster.add_summary: - pruning_obj.add_pruning_summaries() - mask_update_op = pruning_obj.conditional_mask_update_op() - return mask_update_op - - -class BaseModel(base_layer.BaseLayer): - """The abstract model class. All models are sub-class of this class.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'model', None, 'Which python function generates the param. It includes ' - 'the file name and lineno where the function is defined.') - p.Define( - 'cluster', cluster_factory.Cluster.Params(), - 'The training cluster. Individual layer may config differently' - ' based on training cluster it is running under.') - p.Define('input', None, 'Input generator Params.') - p.Define('build_data', build_data.BuildData(), 'Build data of this binary.') - p.Define('train', hyperparams.Params(), - 'Params to control how this model should be trained.') - tp = p.train - tp.Define( - 'start_up_delay_steps', 200, 'i-th replica starts training after ' - 'i*(i+1)/2*start_up_delay_steps steps') - tp.Define('max_steps', 4 * 10**6, 'Training max of 4M steps.') - tp.Define('tpu_steps_per_loop', 1000, 'The number of training steps per ' - 'training loop for TPUs.') - tp.Define( - 'ema_decay', 0.0, - 'If > 0, enable ExponentialMovingAverage during training ' - 'with the give decay. ' - 'Must be < 1. Disabled if <= 0. ' - 'Must be set consistent across all tasks.') - tp.Define( - 'ema_decay_moving_vars', None, - 'If True, include variables from collection "moving_vars" in ema. ' - 'Must be set consistent across all tasks.') - tp.Define('init_from_checkpoint_rules', {}, - 'See BaseTask documentation for details.') - tp.Define('early_stop', None, - 'Early stopping based on dev-set performance.') - tp.Define( - 'enqueue_max_steps', -1, 'Max enqueue steps. -1 meaning no limit.' - ' This flag should be set for unit-test only.') - tp.Define('save_interval_seconds', 60 * 10, - 'Generates a checkpoint roughly once every this many seconds.') - tp.Define('save_max_to_keep', 100, - 'Maximum number of recent checkpoints to keep.') - tp.Define('save_keep_checkpoint_every_n_hours', 0.5, - 'How often to keep a checkpoint.') - tp.Define('summary_interval_steps', 100, - 'Generates a checkpoint roughly once every this many steps.') - - return p - - def __init__(self, params): - """Initializes this Model.""" - assert issubclass(params.cls, BaseModel) - super().__init__(params) - tf.logging.info('Training parameters for %s: %s', params.cls, - self.params.train) - self._global_step_var = py_utils.GetOrCreateGlobalStepVar() - - tp = self.params.train - if tp.ema_decay > 0: - assert tp.ema_decay < 1.0 - self._ema = tf.train.ExponentialMovingAverage( - decay=tp.ema_decay, num_updates=self.global_step) - else: - self._ema = None - - @property - def global_step(self): - return self._global_step_var - - @property - def ema(self): - return self._ema - - @property - def variables_for_ema(self): - p = self.params - all_vars = set(tf.trainable_variables()) | set( - tf.moving_average_variables()) - if p.train.ema_decay_moving_vars: - all_vars |= set(tf.get_collection('moving_vars')) - all_vars &= set(self.vars.Flatten()) - for var in all_vars: - tf.logging.debug('variables_for_ema: %s', var.name) - return all_vars - - def ConstructFPropBPropGraph(self): - raise NotImplementedError('Abstract method') - - def ConstructFPropGraph(self): - raise NotImplementedError('Abstract method') - - def ConstructPostTrainingLoop(self): - raise NotImplementedError('Abstract method') - - @property - def tasks(self): - """Returns a list of all tasks.""" - raise NotImplementedError('Abstract method') - - def GetTask(self, task_name): - """Return the task associated with 'task_name'. - - Args: - task_name: string, the name of the model task to be returned. - - Returns: - An instance of `BaseTask`. - """ - raise NotImplementedError('Abstract method') - - def ProcessFPropResults(self, sess, global_step, metrics, per_example): - """Called once for each train loop. - - BaseTask.ProcessFPropResults is also called on each loop, so you - can put your implementation wherever it is most convenient for you. - - Be sure to implement BaseTask.FilterPerExampleTensors if you plan to use any - per-example tensors in this method. - - Args: - sess: a session. - global_step: model global step. Since ProcessFPropResults is called after - sess.run(train_op), this value will be 1 higher than the value in FProp. - metrics: the metrics dict returned by FPropTower. - per_example: the per_example dict returned by FPropTower. - """ - pass - - -class SingleTaskBase(BaseModel): - """Represents a single task from a model. - - Subclasses must create a Task in self._task by the end of __init__. - """ - - @property - def tasks(self): - return [self._task] - - def GetTask(self, task_name=None): - assert not task_name, 'Must not specify >task_name< for single-task model.' - return self._task - - def SampleTask(self, global_step): - return self._task - - def ConstructFPropBPropGraph(self): - if self.ema: - tf.logging.info('ApplyExponentialMovingAverage on %s', self._task) - self._task.ApplyExponentialMovingAverage(self.ema) - self._task.FPropDefaultTheta() - self._task.BProp() - - def ConstructFPropGraph(self): - self._task.FPropDefaultTheta() - - def ConstructPostTrainingLoop(self): - self._task.PostTrainingLoop() - - -class SingleTaskModel(SingleTaskBase): - """Model that consists of a single task.""" - - @classmethod - def Params(cls, task_params=None): - p = super().Params() - p.Define( - 'task', None, - '`InstantiableParams` object for a `BaseTask` or its derivatives.') - - if task_params is not None: - # Copy over model parameters from the task parameters. - p.task = task_params - base_layer.BaseLayer.CopyBaseParams(p.task, p) - tp = p.train - tp.start_up_delay_steps = p.task.train.start_up_delay_steps - tp.max_steps = p.task.train.max_steps - tp.tpu_steps_per_loop = p.task.train.tpu_steps_per_loop - # init_from_checkpoint_rules does not need to be copied. - tp.early_stop = p.task.train.early_stop - tp.enqueue_max_steps = p.task.train.enqueue_max_steps - tp.save_interval_seconds = p.task.train.save_interval_seconds - tp.save_max_to_keep = p.task.train.save_max_to_keep - tp.save_keep_checkpoint_every_n_hours = ( - p.task.train.save_keep_checkpoint_every_n_hours) - tp.summary_interval_steps = p.task.train.summary_interval_steps - - return p - - def __init__(self, params): - assert issubclass(params.cls, SingleTaskModel) - assert params.task - p = params.Copy() # Make a copy to avoid modifying the input. - p.name = p.name or p.task.name - p.task.name = p.task.name or p.name - if p.input: - assert not p.task.input - p.task.input = p.input - else: - assert p.task.input - p.input = p.task.input - p.train.ema_decay = p.task.train.ema_decay - p.train.ema_decay_moving_vars = p.task.train.ema_decay_moving_vars - - super().__init__(p) - - self.CreateChild('_task', self.params.task) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self._task.InstantiateVariables() - super()._CreateChildrenVariables() - - -class MultiTaskSubModel(SingleTaskBase): - """'Model' consisting of a task from a multi-task model. - - The entire multi-task model is constructed, but otherwise this model - appears to be a SingleTaskModel consisting of just one of the multi-task - model's tasks. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'multi_task_sub_model' - p.Define('task_name', '', 'The name of the task to execute from the ' - 'enclosing model.') - return p - - def __init__(self, params, shared_model=None): - super().__init__(params) - p = self.params - self._model = shared_model - self._task = self._model.children.Get(p.task_name) - - -class MultiTaskModel(BaseModel): - """Model that consists of multiple tasks.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'task_params', hyperparams.Params(), - 'Params object mapping task name to `BaskTask`(or derivatives) ' - 'Params.') - p.Define( - 'task_probs', hyperparams.Params(), - 'Params object mapping task name to the relative likelihood the ' - 'task will be sampled during training.') - p.Define('task_schedule', None, 'Task schedule.') - p.Define( - 'task_global_step', False, - 'Whether or not to use task-specific global steps, which causes each ' - 'task to use its own global_step instead of the true global_step. ' - 'NOTE: this may be severely broken. Verify carefully!') - p.Define( - 'task_name_var_scope', True, - 'Whether or not to use the task name as a variable scope. Note that ' - 'this has been the default behavior for some time, but seems to be ' - 'redundant since the individual tasks scope by their `name`.') - return p - - @staticmethod - def TaskNames(params): - return sorted(task_name for task_name, _ in params.task_params.IterParams()) - - def __init__(self, params): - assert issubclass(params.cls, MultiTaskModel) - super().__init__(params) - p = self.params - assert len(p.task_params) > 1 - - sorted_task_params = sorted( - (task_name, task_params) - for task_name, task_params in p.task_params.IterParams()) - - # Pass input params to tasks. - assert isinstance(p.input, hyperparams.Params) - - for task_name, task_params in sorted_task_params: - assert isinstance(task_params, hyperparams.Params) - assert not task_params.input - try: - task_params.input = p.input.Get(task_name) - except AttributeError as e: - tf.logging.error( - 'Missing input params for task %s !' - 'Check that you have the correct datasets ' - 'passed to DefineMultitaskDatasets.', task_name) - raise e - - if p.task_global_step: - assert task_name == task_params.name, (task_name, task_params.name) - task_params.task_global_step = True - - assert set(dir(p.input)) == set(dir(p.task_params)) - - # For compatibility with older API (with p.task_probs) - if p.task_schedule is None: - p.task_schedule = task_scheduler.ConstantScheduler.Params() - p.task_schedule.task_probs = sorted(list(p.task_probs.IterParams())) - - if p.train.ema_decay > 0: - for task_name, task_params in sorted_task_params: - for field in ['ema_decay', 'ema_decay_moving_vars']: - if task_params.train.Get(field) != p.train.Get(field): - raise ValueError('Params did not match for field %s in task %s' % - (field, task_name)) - - # CreateChild copies over global configs in p to individual task params, - # which then gets propagated down to all sub-layers during - # BaseTask._PropagateDownGlobalConfigs(), or through sub-sequent CreateChild - # or CreateChildren calls. - with tf.name_scope(p.name): - for task_name, task_params in sorted_task_params: - self.CreateChild(task_name, task_params) - - self.CreateChild('task_schedule', p.task_schedule) - - def _CreateChildrenVariables(self): - with tf.name_scope(self.params.name): - for task_name, task in zip(self.task_names, self.tasks): - if self.params.task_name_var_scope: - with tf.variable_scope(task_name): - task.InstantiateVariables() - else: - task.InstantiateVariables() - self.task_schedule.InstantiateVariables() - super()._CreateChildrenVariables() - - @property - def task_names(self): - return MultiTaskModel.TaskNames(self.params) - - @property - def tasks(self): - return [self.children[name] for name in self.task_names] - - def GetTask(self, task_name): - assert task_name, 'Must specify >task_name< for multi-task model.' - return self.children[task_name] - - def SampleTask(self, global_step): - """Sample a task according self.task_schedule. - - `self.task_schedule.cur_probs` will also be updated. - - Args: - global_step: int. Current time step. - """ - sampled_task = self.task_schedule.Sample(global_step) - tf.logging.info('Sampled task: %s', sampled_task) - return self.children[sampled_task] - - def ConstructFPropBPropGraph(self): - for task_name in self.task_names: - with tf.name_scope(task_name): - task = self.GetTask(task_name) - if self.ema: - task.ApplyExponentialMovingAverage(self.ema) - task.FPropDefaultTheta() - task.BProp() - - def ConstructFPropGraph(self): - for task_name in self.task_names: - with tf.name_scope(task_name): - task = self.GetTask(task_name) - task.FPropDefaultTheta() diff --git a/lingvo/core/base_model_params.py b/lingvo/core/base_model_params.py deleted file mode 100644 index 23a81a59..00000000 --- a/lingvo/core/base_model_params.py +++ /dev/null @@ -1,109 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""BaseModelParams class definition.""" - -from lingvo import datasets -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import hyperparams - - -class DatasetError(Exception): - """Dataset error exception class.""" - pass - - -class _BaseModelParams: - """Base class for storing model Params for a single experiment.""" - - def GetDatasetParams(self, dataset): - """Convenience function that returns the param for the given dataset name. - - Args: - dataset: A python string. Typically, 'Dev', 'Test', etc. - - Returns: - If there is a `cls.${dataset}` method defined, call that method to - generate a hyperparam for the input data. - - Raises: - DatasetError: if there is not a `${dataset}` method defined under `cls`. - """ - try: - f = getattr(self, dataset) - except AttributeError as e: - raise DatasetError( - str(e) + - '; available datasets are: %s' % datasets.GetDatasets(type(self))) - return f() - - -class SingleTaskModelParams(_BaseModelParams): - """Model Params for a `.SingleTaskModel`.""" - - def Train(self): - """Returns Params for the training dataset.""" - return base_input_generator.BaseSequenceInputGenerator.Params().Set( - name='Train') - - def Dev(self): - """Returns Params for the development dataset.""" - return base_input_generator.BaseSequenceInputGenerator.Params().Set( - name='Dev') - - def Test(self): - """Returns Params for the testing dataset.""" - return base_input_generator.BaseSequenceInputGenerator.Params().Set( - name='Test') - - def Task(self): - """Returns task params.""" - raise NotImplementedError('Abstract method') - - def Model(self): - """Returns model params. - - Emulates structure of `MultiTaskModelParams`. - """ - return base_model.SingleTaskModel.Params(self.Task()) - - def ProgramSchedule(self): - """Returns a schedule for the Executor.""" - raise NotImplementedError('Abstract method') - - -class MultiTaskModelParams(_BaseModelParams): - """Model Params for a `.MultiTaskModel`.""" - - def Train(self): - """Returns Params for the training dataset.""" - return hyperparams.Params() - - def Dev(self): - """Returns Params for the development dataset.""" - return hyperparams.Params() - - def Test(self): - """Returns Params for the testing dataset.""" - return hyperparams.Params() - - def Model(self): - """Returns model params.""" - raise NotImplementedError('Abstract method') - - def ProgramSchedule(self): - """Returns a schedule for the Executor.""" - raise NotImplementedError('Abstract method') diff --git a/lingvo/core/base_model_params_test.py b/lingvo/core/base_model_params_test.py deleted file mode 100644 index 1c00958f..00000000 --- a/lingvo/core/base_model_params_test.py +++ /dev/null @@ -1,43 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for base_model_params.""" - -import lingvo.compat as tf -from lingvo.core import base_model_params -from lingvo.core import test_utils - - -class BaseModelParamsTest(test_utils.TestCase): - - def testGetDatasetParams_SingleTaskModelParams(self): - dummy_model = base_model_params.SingleTaskModelParams() - self.assertEqual(dummy_model.Train(), dummy_model.GetDatasetParams('Train')) - self.assertEqual(dummy_model.Dev(), dummy_model.GetDatasetParams('Dev')) - self.assertEqual(dummy_model.Test(), dummy_model.GetDatasetParams('Test')) - with self.assertRaises(base_model_params.DatasetError): - dummy_model.GetDatasetParams('Invalid') - - def testGetDatasetParams_MultiTaskModelParams(self): - dummy_model = base_model_params.MultiTaskModelParams() - self.assertEqual(dummy_model.Train(), dummy_model.GetDatasetParams('Train')) - self.assertEqual(dummy_model.Dev(), dummy_model.GetDatasetParams('Dev')) - self.assertEqual(dummy_model.Test(), dummy_model.GetDatasetParams('Test')) - with self.assertRaises(base_model_params.DatasetError): - dummy_model.GetDatasetParams('Invalid') - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/base_model_test.py b/lingvo/core/base_model_test.py deleted file mode 100644 index 3f31ca16..00000000 --- a/lingvo/core/base_model_test.py +++ /dev/null @@ -1,528 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for base_model.""" - -from absl.testing import flagsaver -import lingvo.compat as tf -from lingvo.core import base_decoder -from lingvo.core import base_input_generator -from lingvo.core import base_layer -from lingvo.core import base_model -from lingvo.core import base_model_params -from lingvo.core import distillation_task -from lingvo.core import hyperparams -from lingvo.core import layers -from lingvo.core import learner -from lingvo.core import py_utils -from lingvo.core import task_scheduler -from lingvo.core import test_utils -import numpy as np - - -FLAGS = tf.flags.FLAGS - -_NUMPY_RANDOM_SEED = 9885784 - - -class TestTask(base_model.BaseTask): - - def __init__(self, params): - super().__init__(params) - self.CreateChild('x', layers.BatchNormLayer.Params().Set(name='x', dim=1)) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.CreateVariable( - 'a', - py_utils.WeightParams(shape=[], init=py_utils.WeightInit.Constant(0))) - self.CreateVariable( - 'b', - py_utils.WeightParams(shape=[], init=py_utils.WeightInit.Constant(0))) - - -class BaseTaskTest(test_utils.TestCase): - - @classmethod - def TestParams(cls): - p = TestTask.Params() - p.name = 'base_mdl' - p.encoder = base_layer.BaseLayer.Params() - p.encoder.name = 'encoder' - p.decoder = base_decoder.BaseDecoder.Params() - p.decoder.name = 'decoder' - return p - - def testInit(self): - _ = self.TestParams().Instantiate() - - @flagsaver.flagsaver - def testScaleGradients(self): - p = self.TestParams() - task = p.Instantiate() - var_a = task.theta.a - var_grads = py_utils.NestedMap( - a=py_utils.VarGrad(var_a, tf.ones_like(var_a))) - scaled_grads_map = task.learners[0].ScaleGradients(var_grads) - - FLAGS.enable_check_numerics = False - with self.session(): - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(1.0, scaled_grads_map.grad_scale.eval()) - # The final gradient must be finite. - self.assertFalse( - tf.math.is_nan(scaled_grads_map.final_var_grads.a[1]).eval()) - self.assertTrue( - tf.math.is_finite(scaled_grads_map.final_var_grads.a[1]).eval()) - - @flagsaver.flagsaver - def testScaleGradientsInf(self): - FLAGS.enable_check_numerics = False - p = self.TestParams() - task = p.Instantiate() - var_a = task.theta.a - # Infinite gradient. - var_grads = py_utils.NestedMap(a=py_utils.VarGrad(var_a, tf.math.log(0.))) - scaled_grads_map = task.learners[0].ScaleGradients(var_grads) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(0., scaled_grads_map.grad_scale.eval()) - # The final gradient must be finite. - self.assertFalse( - tf.math.is_nan(scaled_grads_map.final_var_grads.a[1]).eval()) - self.assertTrue( - tf.math.is_finite(scaled_grads_map.final_var_grads.a[1]).eval()) - - @flagsaver.flagsaver - def testScaleGradientsNaN(self): - FLAGS.enable_check_numerics = False - p = self.TestParams() - task = p.Instantiate() - var_a = task.theta.a - # Make a NaN gradient. - var_grads = py_utils.NestedMap( - a=py_utils.VarGrad(var_a, 0. * tf.math.log(0.))) - scaled_grads_map = task.learners[0].ScaleGradients(var_grads) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(0., scaled_grads_map.grad_scale.eval()) - # The final gradient must be finite. - self.assertFalse( - tf.math.is_nan(scaled_grads_map.final_var_grads.a[1]).eval()) - self.assertTrue( - tf.math.is_finite(scaled_grads_map.final_var_grads.a[1]).eval()) - - @flagsaver.flagsaver - def testScaleGradientsCheckNumerics(self): - """ScaleGradients when enable_check_numerics=True.""" - FLAGS.enable_check_numerics = True - p = self.TestParams() - task = p.Instantiate() - var_a = task.theta.a - # Make a NaN gradient. - var_grads = py_utils.NestedMap( - a=py_utils.VarGrad(var_a, 0. * tf.math.log(0.))) - scaled_grads_map = task.learners[0].ScaleGradients(var_grads) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(0., scaled_grads_map.grad_scale.eval()) - # Fetching the gradient raises an exception with enable_check_numerics. - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, - 'is not finite'): - _ = scaled_grads_map.final_var_grads.a[1].eval() - - def testScaleGradientsError(self): - p = self.TestParams() - p.train.clip_gradient_single_norm_to_value = 1.0 - p.train.clip_gradient_norm_to_value = 1.0 - task = p.Instantiate() - var_a = task.theta.a - var_grads = py_utils.NestedMap( - a=py_utils.VarGrad(var_a, tf.ones_like(var_a))) - self.assertRaises(ValueError, task.learners[0].ScaleGradients, var_grads) - - @flagsaver.flagsaver - def testScaleGradientsSingleTensorNorm(self): - p = self.TestParams() - p.train.clip_gradient_single_norm_to_value = 1.0 - p.train.clip_gradient_norm_to_value = None - task = p.Instantiate() - - var_a = task.theta.a - var_b = task.theta.b - var_grads = py_utils.NestedMap( - a=py_utils.VarGrad(var_a, - tf.ones_like(var_a) * 10.0), - b=py_utils.VarGrad(var_b, - tf.ones_like(var_b) * 0.5)) - scaled_grads_map = task.learners[0].ScaleGradients(var_grads) - - FLAGS.enable_check_numerics = False - with self.session(): - self.evaluate(tf.global_variables_initializer()) - - # Each variable is clipped indipendently to grad scale of 1. - self.assertAllClose(scaled_grads_map.final_var_grads.a[1].eval(), 1.0) - self.assertAllClose(scaled_grads_map.final_var_grads.b[1].eval(), 0.5) - - -class TeacherTask(base_model.BaseTask): - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.CreateVariable( - 'x', - py_utils.WeightParams(shape=[], init=py_utils.WeightInit.Constant(0))) - - def ComputePredictions(self, theta, input_batch): - return theta.x - - -class StudentTask(base_model.BaseTask): - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.CreateVariable( - 'x', - py_utils.WeightParams(shape=[], init=py_utils.WeightInit.Uniform())) - - def ComputePredictions(self, theta, input_batch): - return theta.x - - -class TestInputGenerator(base_input_generator.BaseSequenceInputGenerator): - - def InfeedBatchSize(self): - """Override BaseSequenceInputGenerator.""" - return 1 - - def _InputBatch(self): - return 0 - - -class DistillationTestTask(distillation_task.DistillationTask): - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'distillation_test' - p.teacher = TeacherTask.Params() - p.student = StudentTask.Params() - p.input = TestInputGenerator.Params() - p.train.learning_rate = 1e3 - p.teacher.train = None - p.teacher.eval = None - p.student.train = None - p.student.eval = None - return p - - def __init__(self, params): - super().__init__(params) - - def ComputeLoss(self, theta, predictions, input_batch): - return {'loss': (predictions.teacher - predictions.student, 1)}, {} - - -class DistillationTaskTest(test_utils.TestCase): - - def _GetVarValuesBeforeAndAfter(self, params, steps=10): - task = params.Instantiate() - self.assertIsNotNone(task.teacher.params.input) - self.assertIsNotNone(task.student.params.input) - metrics = task.FPropDefaultTheta()[0] - self.assertCountEqual(['loss', 'num_samples_in_batch'], - list(metrics.keys())) - task.BProp() - # Expected side effects of BProp(). - self.assertIsNotNone(task.train_op) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - - variables = {} - values_before_training = {} - values_after_training = {} - for child in ('teacher', 'student'): - variables[child] = { - k: v - for k, v in getattr(task, child).vars.FlattenItems() - } - values_before_training[child] = self.evaluate(variables[child]) - - # Train for a few steps. - for _ in range(10): - self.evaluate(task.train_op) - for child in ('teacher', 'student'): - values_after_training[child] = self.evaluate(variables[child]) - return values_before_training, values_after_training - - def testFProp(self): - values_before_training, values_after_training = ( - self._GetVarValuesBeforeAndAfter(DistillationTestTask.Params())) - for child in ('teacher', 'student'): - for k, v in values_after_training[child].items(): - print('Comparing variable %s' % k) - if child == 'teacher': - # Teacher vars should not change after training. - self.assertAllEqual(values_before_training[child][k], v) - else: - # Student vars should change after training. - self.assertNotAlmostEqual(values_before_training[child][k], v) - - def testFPropTeacherEnabled(self): - params = DistillationTestTask.Params() - params.train_teacher = True - params.distillation_loss_weight.value = 0.5 - values_before_training, values_after_training = ( - self._GetVarValuesBeforeAndAfter(params)) - for child in ('teacher', 'student'): - for k, v in values_after_training[child].items(): - print('Comparing variable %s' % k) - if child == 'teacher': - # Teacher vars should change after training. - self.assertNotAlmostEqual(values_before_training[child][k], v) - else: - # Student vars should change after training. - self.assertNotAlmostEqual(values_before_training[child][k], v) - - -class SingleTaskModelTest(test_utils.TestCase): - - def testInit(self): - p = base_model.SingleTaskModel.Params() - p.task = BaseTaskTest.TestParams() - p.task.input = base_input_generator.BaseSequenceInputGenerator.Params() - p.task.train.learner = (learner.Learner.Params().Set(name='loss')) - model = p.Instantiate() - self.assertEqual(model.params.name, model.GetTask().params.name) - self.assertEqual(model.params.task, model.GetTask().params) - self.assertEqual(len(model.tasks), 1) - self.assertEqual(model.tasks[0], model.GetTask()) - self.assertEqual(model.tasks[0], model.SampleTask(None)) - - def testExponentialMovingAverage(self): - p = base_model.SingleTaskModel.Params() - p.task = BaseTaskTest.TestParams() - p.task.input = base_input_generator.BaseSequenceInputGenerator.Params() - p.task.train.ema_decay = 0.9 - p.task.train.ema_decay_moving_vars = False - model = p.Instantiate() - task = model._task - task._train_op = tf.no_op() - task.ApplyExponentialMovingAverage(model.ema) - with tf.variable_scope('base_mdl', reuse=True): - beta = tf.get_variable('x/beta/var') - mean = tf.get_variable('x/moving_mean/var') - self.assertIsNotNone(model.ema.average(beta)) - self.assertIsNone(model.ema.average(mean)) - - def testExponentialMovingAverageIncludingMovingVars(self): - p = base_model.SingleTaskModel.Params() - p.task = BaseTaskTest.TestParams() - p.task.input = base_input_generator.BaseSequenceInputGenerator.Params() - p.task.train.ema_decay = 0.9 - p.task.train.ema_decay_moving_vars = True - p.task.input = base_input_generator.BaseSequenceInputGenerator.Params() - model = p.Instantiate() - task = model._task - task._train_op = tf.no_op() - task.ApplyExponentialMovingAverage(model.ema) - with tf.variable_scope('base_mdl', reuse=True): - beta = tf.get_variable('x/beta/var') - mean = tf.get_variable('x/moving_mean/var') - self.assertIsNotNone(model.ema.average(beta)) - self.assertIsNotNone(model.ema.average(mean)) - - -class MultiTaskModelTest(test_utils.TestCase): - - def testInitMissingInputParams(self): - p = base_model.MultiTaskModel.Params() - p.name = 'MultiTaskModel' - p0 = BaseTaskTest.TestParams() - p0.train.learner = (learner.Learner.Params().Set(name='loss')) - p1 = BaseTaskTest.TestParams() - p1.train.learner = (learner.Learner.Params().Set(name='loss')) - - p.input = base_model_params.MultiTaskModelParams().Train() - p.input.Define('a', - base_input_generator.BaseSequenceInputGenerator.Params(), '') - - p.task_params = hyperparams.Params() - p.task_params.Define('a', p0, '') - p.task_params.Define('b', p1, '') - - p.task_probs = hyperparams.Params() - p.task_probs.Define('a', 0.5, '') - p.task_probs.Define('b', 0.5, '') - self.assertRaises(AttributeError, p.Instantiate) - - def testInit(self): - p = base_model.MultiTaskModel.Params() - p.name = 'MultiTaskModel' - p0 = BaseTaskTest.TestParams() - p0.train.learner = (learner.Learner.Params().Set(name='loss')) - p1 = BaseTaskTest.TestParams() - p1.train.learner = (learner.Learner.Params().Set(name='loss')) - - p.input = base_model_params.MultiTaskModelParams().Train() - p.input.Define('a', - base_input_generator.BaseSequenceInputGenerator.Params(), '') - p.input.Define('b', - base_input_generator.BaseSequenceInputGenerator.Params(), '') - - p.task_params = hyperparams.Params() - p.task_params.Define('a', p0, '') - p.task_params.Define('b', p1, '') - - p.task_probs = hyperparams.Params() - p.task_probs.Define('a', 0.5, '') - p.task_probs.Define('b', 0.5, '') - - model = p.Instantiate() - self.assertEqual(len(model.tasks), 2) - self.assertEqual(set(model.task_names), {'a', 'b'}) - self.assertEqual(set(model.tasks), {model.GetTask('a'), model.GetTask('b')}) - self.assertEqual(model.params.task_params.a, model.GetTask('a').params) - self.assertEqual(model.params.task_params.b, model.GetTask('b').params) - - def _setUpTestSampleTask(self): - np.random.seed(_NUMPY_RANDOM_SEED) - - # define and initialize tasks, model and params - p = base_model.MultiTaskModel.Params() - p.name = 'MultiTaskModel' - p0 = BaseTaskTest.TestParams() - p1 = BaseTaskTest.TestParams() - - p.input = base_model_params.MultiTaskModelParams().Train() - p.input.Define('a', - base_input_generator.BaseSequenceInputGenerator.Params(), '') - p.input.Define('b', - base_input_generator.BaseSequenceInputGenerator.Params(), '') - - p.task_params = hyperparams.Params() - p.task_params.Define('a', p0, '') - p.task_params.Define('b', p1, '') - - return p - - def _testSampleTaskHelper(self, p): - model = p.Instantiate() - - task_to_id = {model.children['a']: 'a', model.children['b']: 'b'} - task_counts = {'a': 0, 'b': 0} - - # initialize tensorflow graph and global step - with self.session(): - self.evaluate(tf.global_variables_initializer()) - global_step = self.evaluate(model.global_step) - for _ in range(100): - task = model.SampleTask(global_step) - task_counts[task_to_id[task]] += 1 - - self.assertEqual(task_counts['a'], 83) - self.assertEqual(task_counts['b'], 17) - - def testSampleTaskSpecifiedWithoutScheduler(self): - """Expected distribution: 'a': 0.8 , 'b': 0.2.""" - p = self._setUpTestSampleTask() - - p.task_probs = hyperparams.Params() - p.task_probs.Define('a', 0.8, '') - p.task_probs.Define('b', 0.2, '') - - self._testSampleTaskHelper(p) - - def testSampleTask(self): - """Expected distribution: 'a': 0.8 , 'b': 0.2.""" - p = self._setUpTestSampleTask() - - p.task_schedule = task_scheduler.ConstantScheduler.Params() - p.task_schedule.task_probs = [('a', 0.8), ('b', 0.2)] - - self._testSampleTaskHelper(p) - - -class PostTrainingTask(base_model.BaseTask): - - def __init__(self, params): - super().__init__(params) - p = layers.FeedForwardNet.Params().Set( - name='ffn', - input_dim=10, - hidden_layer_dims=[20, 30], - batch_norm=True, - activation='TANH', - params_init=py_utils.WeightInit.Uniform(1.0)) - self.CreateChild('ffn', p) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.CreateVariable( - 'counter1', - py_utils.WeightParams(shape=[], init=py_utils.WeightInit.Constant(0))) - self.CreateVariable( - 'counter2', - py_utils.WeightParams(shape=[], init=py_utils.WeightInit.Constant(0))) - - def PostTrainingStepUpdate(self, global_step): - # We expect the training step to be done, so capture - # the value of counter1 into counter2. - return tf.assign(self.vars.counter2, self.vars.counter1) - - def ComputePredictions(self, theta, input_batch): - input_data = tf.random.normal([1, 10], dtype=tf.float32) + tf.cast( - input_batch, tf.float32) - add = tf.assign_add(self.vars.counter1, 1.) - input_data += add - result = self.ffn.FProp(theta.ffn, input_data) - return {'result': result} - - def ComputeLoss(self, theta, predictions, input_batch): - loss = tf.reduce_sum(predictions['result']) - return {'loss': (loss, 1)}, {} - - -class PostTrainingTest(test_utils.TestCase): - - @classmethod - def TestParams(cls): - p = PostTrainingTask.Params() - p.name = 'base_mdl' - p.input = TestInputGenerator.Params() - return p - - def testPost(self): - p = self.TestParams() - task = p.Instantiate() - task.FPropDefaultTheta() - task.BProp() - train_op = task.train_op - with self.session(): - self.evaluate(tf.global_variables_initializer()) - for _ in range(20): - self.evaluate(train_op) - c1, c2 = self.evaluate([task.vars.counter1, task.vars.counter2]) - # Both vars should have the same value if the PostTrainingStep - # happens after the training step. - self.assertEqual(c1, c2) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/batch_major_attention.py b/lingvo/core/batch_major_attention.py deleted file mode 100644 index 7c1dc4dd..00000000 --- a/lingvo/core/batch_major_attention.py +++ /dev/null @@ -1,4216 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Multi-headed attention layers for Transformer machine translation. - -[1] Attention is all you need. - https://arxiv.org/pdf/1706.03762.pdf Section 3. -""" - -import bisect -from lingvo import compat as tf -from lingvo.core import attention_util -from lingvo.core import base_layer -from lingvo.core import builder -from lingvo.core import conv_layers_builder as conv_layers -from lingvo.core import gpipe -from lingvo.core import hyperparams -from lingvo.core import layers -from lingvo.core import layers_with_attention -from lingvo.core import moe_layers -from lingvo.core import py_utils -from lingvo.core import symbolic -from lingvo.core import tshape -# pylint: disable=g-direct-tensorflow-import -from tensorflow.python.ops import inplace_ops - -# pylint: enable=g-direct-tensorflow-import - - -def CausalPadding(slen, dtype=tf.float32): - return 1 - tf.linalg.band_part(tf.ones([slen, slen], dtype=dtype), -1, 0) - - -def GetDtypeMin(dtype=tf.float32): - return tf.constant(-0.7, dtype=dtype) * dtype.max - - -def SegmentMask(segment_id, - source_segment_id, - dtype=tf.float32, - apply_dtype_min=True): - """Calculates a segment mask for attention. - - Args: - segment_id: [B, T] - source_segment_id: [B, S] - dtype: data type of generated mask. - apply_dtype_min: Outputs a 0/1 padding mask if set to False. This is needed - for GPipe layers to avoid nan issues. - - Returns: - segment_mask: [B, 1, T, S]: A mask that is ready to - be added to [B, N, T, S] attention logits. if apply_dtype_min is False, - outputs a 0/1 padding mask instead. - """ - if segment_id is None or source_segment_id is None: - return None - # Compute [B, T, S] = [B, T, 1] != [B, 1, S] - ret = tf.cast( - tf.not_equal( - tf.expand_dims(segment_id, 2), tf.expand_dims(source_segment_id, 1)), - dtype=dtype) - if apply_dtype_min: - ret *= GetDtypeMin(ret.dtype) - # [B, T, S] -> [B, 1, T, S] - return tf.expand_dims(ret, axis=1) - - -class PerDimScaleLayer(base_layer.BaseLayer): - """A layer to scale individual dims of the input.""" - - @classmethod - def Params(cls): - """Params for `PerDimScaleLayer`.""" - p = super().Params() - p.Define('dim', 0, 'Number of individual dims .') - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - pc = py_utils.WeightParams( - shape=[p.dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('per_dim_scale', pc) - - def FProp(self, theta, inputs): - """Return theta.scale * inputs. - - Args: - theta: weights defined in this layer. - inputs: 4D tensor with shape [..., p.dim] - - Returns: - outpus: 4D tensor with shape [..., p.dim] - """ - p = self.params - dim = symbolic.ToStatic(p.dim) - inputs = py_utils.HasShape(inputs, [-1, -1, -1, dim]) - scale = tf.math.rsqrt(tf.cast(dim, inputs.dtype)) - scale *= tf.nn.softplus(theta.per_dim_scale) / tf.nn.softplus( - tf.constant(0.0, dtype=inputs.dtype)) - return inputs * scale - - @classmethod - def FPropMeta(cls, p, inputs): - py_utils.CheckShapes((inputs,)) - return py_utils.NestedMap( - flops=inputs.num_elements() * 5, out_shapes=(inputs,)) - - -class MultiHeadedProjectionLayer(base_layer.BaseLayer): - """Layer that computes multi heads projection. - - This layer is expected to be used within MultiHeadedAttention below. - """ - - @classmethod - def Params(cls): - """Params for MultiHeadedProjectionLayer.""" - p = super().Params() - p.Define('input_dim', 0, 'Input dimension.') - p.Define('num_heads', 0, 'Number of heads.') - p.Define('dim_per_head', 0, 'Size of each head.') - p.Define( - 'is_output_projection', False, - 'Whether it is out projection or not. If False, we use ' - '"BTD,DNH->BTNH" for query,key,value projection. Otherwise we use ' - '"BTNH,DNH->BTD" for output projection.') - p.Define('use_bias', True, 'If to add bias in projection.') - p.Define('xla_num_partitions', None, 'Number of SPMD partitions.') - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - pc = py_utils.WeightParams( - shape=[p.input_dim, p.num_heads, p.dim_per_head], - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w', pc) - if p.use_bias: - if p.is_output_projection: - pc_bias = py_utils.WeightParams( - shape=[p.input_dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - else: - pc_bias = py_utils.WeightParams( - shape=[p.num_heads, p.dim_per_head], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('b', pc_bias) - - def FProp(self, theta, inputs): - """Computes the multi headed projection for inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: A tensor of shape [batch_size, time_steps, num_heads, - dim_per_head] or [batch_size, time_steps, hidden_size]. - - Returns: - The projected tensor with shape [[batch_size, time_steps, hidden_size] or - [batch_size, time_steps, num_heads, dim_per_head]. - """ - p = self.params - if p.xla_num_partitions: - theta.w = moe_layers.Split( - theta.w, 1, p.xla_num_partitions, use_sharding_op=True) - if p.is_output_projection: - inputs = py_utils.HasShape( - inputs, [-1, -1, p.num_heads, - symbolic.ToStatic(p.dim_per_head)]) - ret = tf.einsum('BTNH,DNH->BTD', inputs, theta.w) - else: - inputs = py_utils.HasShape( - inputs, [-1, -1, symbolic.ToStatic(p.input_dim)]) - ret = tf.einsum('BTD,DNH->BTNH', inputs, theta.w) - if p.use_bias: - if p.xla_num_partitions and not p.is_output_projection: - theta.b = moe_layers.Split( - theta.b, 0, p.xla_num_partitions, use_sharding_op=True) - ret += theta.b - return ret - - -class MultiHeadedAttention(base_layer.BaseLayer): - """Dot-product attention with multiple attention heads. - - This implementation heavily uses einsum (wrapped in py_utils.Einsum) to be - efficient on TPUs. We use the following capital letters to denote certain - tensor parameters. - - B = batch size - S = length of the key/value (source) - T = length of the query (target) - D = model dimension - N = number of attention heads - H = dimensions of each attention head. - - The algorithm is sketched as follows. Each intermediate tensor or weight - tensor is annotated with its shape. E.g., Wq, the weight tensor for query's - projection, its shape is [D, N, H]. - - Trainable weights: - Wq, Wk, Wv: [D, N, H] - Wout: [D, N, H] - - Input q:[B, T, D]; k:[B, S, D]; v:[B, S, D] - q_proj:[B, T, N, H] = einsum('BTD,DNH->BTNH', x, Wq) - k_proj:[B, S, N, H] = einsum('BSD,DNH->BSNH', x, Wk) - v_proj:[B, S, N, H] = einsum('BSD,DNH->BSNH', x, Wv) - logits:[B, N, T, S] = einsum('BTNH,BSNH->BNTS', q_proj, k_proj) / sqrt(H) - probs:[B, N, T, S] = softmax(logits) - context:[B, T, N, H] = einsum('BNTS,BSNH->BTNH', probs, v_proj) - Output y:[B, T, D] = einsum('BTNH,DNH>BTD', context, Wout) - """ - - @classmethod - def Params(cls): - """Params for _MultiHeadedAttention.""" - p = super().Params() - p.Define('input_dim', 0, 'Number of key nodes.') - p.Define('hidden_dim', 0, 'Number of hidden nodes.') - p.Define('num_heads', 1, 'Num of attention heads.') - p.Define('dropout_tpl', layers.DropoutLayer.Params(), - 'Params for dropout layer.') - p.Define( - 'enable_value_proj', True, 'Whether value v is pre-projected ' - ' before self attention or not.') - p.Define('enable_per_dim_scale', True, - 'Whether using per_dim_scale or scaling by a constant factor.') - p.Define('atten_dropout_prob', 0.0, - 'Probability at which we apply dropout to the attention weights.') - p.Define('proj_tpl', MultiHeadedProjectionLayer.Params(), 'Params for ' - 'projection layer.') - p.Define('packed_input', False, 'Whether there is packed input.') - p.Define('use_bias', True, 'Whether to use bias for projection layers.') - p.Define('xla_num_partitions', None, 'Number of SPMD partitions.') - p.Define( - 'enable_scaling_code_motion', False, 'Move scalings from the side ' - 'of T^2 to the side of T for better performance. This may result ' - 'in model quality drops when using bf16 for some models due to ' - 'different XLA fusion decisions.') - return p - - def __init__(self, params): - """Constructs a _MultiHeadedAttention object.""" - super().__init__(params) - p = self.params - assert p.input_dim, 'input_dim is {}'.format(p.input_dim) - assert p.hidden_dim, 'hidden_dim is {}'.format(p.hidden_dim) - assert (symbolic.IsExpr(p.hidden_dim) or p.hidden_dim % p.num_heads == 0), ( - f'hidden_dim: {p.hidden_dim} is not a multiple of num_heads: ' - f'{p.num_heads}.') - dim_per_head = p.hidden_dim // p.num_heads - - def ProjectInput(): - return p.proj_tpl.Copy().Set( - input_dim=p.input_dim, - num_heads=p.num_heads, - dim_per_head=dim_per_head, - use_bias=p.use_bias, - xla_num_partitions=p.xla_num_partitions) - - self.CreateChild('key', ProjectInput()) - self.CreateChild('query', ProjectInput()) - if p.enable_value_proj: - self.CreateChild('value', ProjectInput()) - if p.enable_per_dim_scale: - self.CreateChild('per_dim_scale', - PerDimScaleLayer.Params().Set(dim=dim_per_head)) - self.CreateChild('atten_dropout', - p.dropout_tpl.Set(keep_prob=1.0 - p.atten_dropout_prob)) - # Setting is_output_projection=True to set the projection direction - # from hidden dim to input dim. - self.CreateChild( - 'post', - p.proj_tpl.Copy().Set( - input_dim=p.input_dim, - num_heads=p.num_heads, - dim_per_head=dim_per_head, - is_output_projection=True, - use_bias=p.use_bias, - xla_num_partitions=p.xla_num_partitions)) - - def _AttenLogits(self, theta, query, key): - """Computes attention logits. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: A Tensor of shape [B, T, N, H] - key: A Tensor of shape [B, T, N, H] - - Returns: - A Tensor of shape [B, N, T, S] - """ - return tf.einsum('BTNH,BSNH->BNTS', query, key) - - def _AttenLogitsOneStep(self, theta, query, key, time_step): - """Attention logits for one single target (query) step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, N, H]. - key: [S, B, N, H] or [S, B, N*H/128, 128]. - time_step: Current time step. - - Returns: - A Tensor of shape [S, B, N] - """ - p = self.params - s, b, _, _ = py_utils.GetShape(key, 4) - n = p.num_heads - h = p.hidden_dim // n - - # [s, b, n] - return tf.einsum('BNH,SBNH->SBN', query, tf.reshape(key, [s, b, n, h])) - - def AttenProbs(self, - theta, - query, - key, - paddings, - segment_mask, - per_step_padding=None): - """Compute attention probability. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, T, N, H]. - key: [B, S, N, H]. - paddings: [B, S]. - segment_mask: [B, 1, T, S]: A mask that is applied to prevent attention - between different segments. This is already been converted into large - negative logits. Only applied if packed_input = True. - per_step_padding: A mask used by decoder self-attention to prevent - information flow from future (causal padding). It has shape [B, T, S] if - not None. - - Returns: - probs: [B, N, T, S]. - probs_sum: [B, N, T, 1]. - """ - key = py_utils.HasRank(key, 4) - b, s, n, h = py_utils.GetShape(key, 4) - query = py_utils.HasShape(query, [b, -1, n, h]) - t = py_utils.GetShape(query)[1] - if segment_mask is not None and self.params.packed_input: - segment_mask = py_utils.HasShape(segment_mask, [b, 1, t, s]) - - with tf.name_scope('logits'): - # Keep softmax computation in float32 otherwise the low precision can - # can lead to worse quality. - logits = tf.cast(self._AttenLogits(theta, query, key), tf.float32) - - # Apply segment mask. - if self.params.packed_input and segment_mask is not None: - # Paddings have been included in segment_mask. - padded_logits = logits + tf.cast(segment_mask, tf.float32) - else: - # Exclude padding frames. - paddings = py_utils.HasShape(paddings, [b, s]) - paddings = tf.tile(tf.reshape(paddings, [b, 1, 1, s]), [1, n, t, 1]) - if per_step_padding is not None: - per_step_padding = tf.tile( - tf.expand_dims(per_step_padding, 1), [1, n, 1, 1]) - paddings += per_step_padding - - very_negative_logits = ( - tf.ones_like(logits) * logits.dtype.max * - tf.constant(-0.7, dtype=logits.dtype)) - padded_logits = tf.where(paddings > 0.0, very_negative_logits, logits) - - if self.params.enable_scaling_code_motion: - # Split the softmax into two parts. Do the 1st part here; the 2nd part - # (scaling) is moved after _AttenContext for better performance. - probs = padded_logits - tf.stop_gradient( - tf.reduce_max(padded_logits, -1, True)) - probs = tf.cast(tf.exp(probs), key.dtype) - probs_sum = tf.reduce_sum(probs, -1, True) - else: - probs = tf.cast(tf.nn.softmax(padded_logits), key.dtype) - probs_sum = None - - probs = py_utils.HasShape(probs, [b, n, t, s]) - return probs, probs_sum - - def _AttenContext(self, theta, probs, value): - return tf.einsum('BNTS,BSNH->BTNH', probs, value) - - def _AttenContextOneStep(self, theta, probs, value, time_step): - s, b, _, _ = py_utils.GetShape(value, 4) - n = self.params.num_heads - h = self.params.hidden_dim // n - - return tf.einsum('SBN,SBNH->BNH', probs, tf.reshape(value, [s, b, n, h])) - - def _DotAtten(self, - theta, - query, - key, - value, - paddings, - segment_mask, - per_step_padding=None): - """Main attention function. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, T, N, H]. - key: [B, S, N, H]. - value: [B, S, N, H]. - paddings: [B, S]. - segment_mask: [B, 1, T, S]: A mask that is applied to prevent attention - between different segments. This is already been converted into large - negative logits. Only applied if packed_input = True. - per_step_padding: A mask used by decoder self-attention to prevent - information flow from future (causal padding). It has shape [B, T, S] if - not None. - - Returns: - encoded: [B, T, N, H]. - atten_probs: [B, N, T, S]. - """ - p = self.params - # Scale the query projection. - if p.enable_per_dim_scale: - query = self.per_dim_scale.FProp(theta.per_dim_scale, query) - else: - query *= (p.hidden_dim // p.num_heads)**-0.5 - - # Compute prob with shape [batch, heads, target_time, source_time]. - with tf.name_scope('probs'): - probs, probs_sum = self.AttenProbs(theta, query, key, paddings, - segment_mask, per_step_padding) - # Apply dropout to probs. - probs = self.atten_dropout.FProp(theta.atten_dropout, probs) - - # Compute the attention context vector. - with tf.name_scope('ctx'): - encoded = self._AttenContext(theta, probs, value) - if p.enable_scaling_code_motion: - # The 2nd part of the softamx --- scaling. - encoded = encoded / tf.transpose(probs_sum, [0, 2, 1, 3]) - - return encoded, probs - - def _DotAttenOneStep(self, - theta, - query, - key, - value, - paddings, - segment_mask, - per_step_padding=None, - time_step=None, - use_short_seq_opt=False): - """Dot attention function for queries with 1 time step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, 1, N, H]. - key: [S, B, N, H] or [S, B, N*H/128, 128]. - value: [S, B, N, H] or [S, B, N*H/128, 128]. - paddings: [B, S]. - segment_mask: [B, 1, T, S]: A mask that is applied to prevent attention - between different segments. This is already been converted into large - negative logits. Only applied if packed_input = True. - per_step_padding: A mask used by decoder self-attention to prevent - information flow from future (causal padding). It has shape [B, 1, S] if - not None. - time_step: Current time step. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - encoded: [B, 1, N, H]. - """ - p = self.params - # Scale the query projection. - if p.enable_per_dim_scale: - query = self.per_dim_scale.FProp(theta.per_dim_scale, query) - else: - query *= (p.hidden_dim // p.num_heads)**-0.5 - - key = py_utils.HasRank(key, 4) - - b, t, n, h = py_utils.GetShape(query, 4) - s, b, _, _ = py_utils.GetShape(key, 4) - paddings = py_utils.HasShape(paddings, [b, s]) - assert t == 1 - - if per_step_padding is not None: - paddings += tf.squeeze(per_step_padding, 1) - - query = tf.reshape(query, [b, n, h]) - pad = tf.reshape( - tf.tile(tf.expand_dims(tf.transpose(paddings), 2), [1, 1, n]), [s, -1]) - very_negative_logits = ( - tf.ones_like(pad) * query.dtype.max * - tf.constant(-0.7, dtype=query.dtype)) - - def _LongSeq(): - """For long sequence, directly apply to the entire tensor with padding.""" - logits = self._AttenLogitsOneStep(theta, query, key, time_step) - - logits = tf.reshape(logits, [s, -1]) - padded_logits = tf.where(pad > 0.0, very_negative_logits, logits) - probs = tf.nn.softmax(padded_logits, axis=0) - probs = tf.reshape(probs, [s, b, n]) - - encoded = self._AttenContextOneStep(theta, probs, value, time_step) - return tf.expand_dims(encoded, 1) - - def _ShortSeq(): - """For short sequence, using while loop for early exit.""" - - def _AttenStep(o, k, q, ts): - """Computes logits for attention prob for one step. - - Args: - o: the output logits of shape [S, B*N] - k: cached key of shape [S, B, N*H/128, 8] - q: query of shape [B, N, H] - ts: a scala tensor to represent time_step - - Returns: - Updated logits and time steps. - """ - ot = tf.reshape( - tf.reduce_sum(tf.reshape(tf.gather(k, ts), [-1, n, h]) * q, -1), - [-1]) - return inplace_ops.alias_inplace_update(o, ts, ot), k, q, ts + 1 - - logits, _, _, _ = tf.while_loop( - lambda _o, _k, _q, ts: ts <= time_step, - _AttenStep, - loop_vars=(inplace_ops.empty([s, b * n], query.dtype, - init=True), key, query, - tf.zeros([], tf.int32))) - - padded_logits = tf.where(pad > 0.0, very_negative_logits, logits) - probs = tf.nn.softmax(padded_logits, axis=0) - - def _DotStep(o, p, v, ts): - """Computes encoded activation. - - Args: - o: the output activation of shape [B, N, H] - p: probabiliy of shape [S, B*N] - v: cached value of shape [S, B, N*H/128, 8] - ts: a scala tensor to represent time_step - - Returns: - Updated output and time steps. - """ - return o + tf.reshape(tf.gather(p, ts), [-1, n, 1]) * tf.reshape( - tf.gather(v, ts), [-1, n, h]), p, v, ts + 1 - - encoded, _, _, _ = tf.while_loop( - lambda o, p, v, ts: ts <= time_step, - _DotStep, - loop_vars=(tf.zeros([b, n, h], - probs.dtype), probs, value, tf.zeros([], - tf.int32))) - return tf.expand_dims(encoded, 1) - - return _ShortSeq() if use_short_seq_opt else _LongSeq() - - def FProp(self, - theta, - query_vec, - key_vec, - value_vec, - paddings, - segment_mask=None, - per_step_padding=None): - """Computes the value vector given the current query output. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [B, T, D]. - key_vec: [B, S, D]. - value_vec: [B, S, D]. - paddings: [B, S]. - segment_mask: [B, 1, T, S]. A mask only applied if packed_input=True. - per_step_padding: A mask used by decoder self-attention to prevent - information flow from future (causal padding). It has shape [B, T, T] if - not None. - - Returns: - encoded: [B, T, D]. - atten_probs: [B, N, T, S]. - - Raises: - ValueError: If value projection is disabled. - """ - p = self.params - - # Project inputs to key, value and query, respectively has shape - # [B, S, N, H], [B, S, N, H], and [B, T, N, H]. - query_proj = self.query.FProp(theta.query, query_vec) - key_proj = self.key.FProp(theta.key, key_vec) - if p.enable_value_proj: - value_proj = self.value.FProp(theta.value, value_vec) - else: - h = p.num_heads - _, _, d = py_utils.GetShape(value_vec, 3) - dh = d // h - # TODO(b/119531146): Reshape is inefficient here. Use one-hot matmul - # avoids the data formatting. Change this back to reshape once XLA - # has optimized reshape performance. - rhs = tf.reshape( - tf.one_hot(tf.range(d) // dh, h, dtype=value_vec.dtype), - [d, h, 1]) * tf.reshape( - tf.one_hot(tf.range(d) % dh, dh, dtype=value_vec.dtype), - [d, 1, dh]) - value_proj = tf.einsum('BTD,DNH->BTNH', value_vec, rhs) - - if p.packed_input and not self.do_eval: - assert segment_mask is not None - encoded, atten_probs = self._DotAtten(theta, query_proj, key_proj, - value_proj, paddings, segment_mask, - per_step_padding) - # Post projection - encoded = self.post.FProp(theta.post, encoded) - return encoded, atten_probs - - def InitStates(self, theta, target_batch_size, target_max_length): - p = self.params - num_heads = p.num_heads - atten_dim = p.hidden_dim - if not atten_dim: # Check for Pathways as atten_tpl.hidden_dim is not set. - atten_dim = p.input_dim - dim_per_head = atten_dim // num_heads - # TODO(shafey): Determine if we want to make the cached shape 128 to - # avoid padding and more efficient interpolation in beamsearch. - return py_utils.NestedMap( - key=inplace_ops.empty( - shape=(target_max_length, target_batch_size, num_heads, - dim_per_head), - dtype=py_utils.FPropDtype(p), - init=True), - value=inplace_ops.empty( - shape=(target_max_length, target_batch_size, num_heads, - dim_per_head), - dtype=py_utils.FPropDtype(p), - init=True)) - - def ExtendStep(self, - theta, - query_vec, - cached_states, - paddings, - segment_mask, - per_step_padding, - time_step, - use_short_seq_opt=False): - """Computes the value vector given the query of the current step. - - This function is used by autoregressive decoding. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [B, 1, D]. - cached_states: A `.NestedMap` object containing tensors which are the - results of previous attentions, used for fast decoding. key - [T, B, - N, H]. value - [T, B, N, H]. - paddings: [B, T], or None if there is no padding. - segment_mask: [B, 1, T, S] or None. - per_step_padding: A mask used by decoder self-attention to prevent - information flow from future (causal padding). It has shape [B, 1, T] if - not None. - time_step: A scalar or tensor with [B], current decode step, 0-based. - if it's a scalar, all the time step are the same decode step. - if it's a tensor, it represents current decode step for each sample. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - encoded: [B, 1, D]. - updated_key_vec: [T, B, N, H]. - updated_value_vec: [T, B, N, H]. - - Raises: - ValueError: If value projection is disabled. - """ - p = self.params - if not p.enable_value_proj: - raise ValueError('Value projection must be enabled for Transformer ' - 'machine translation.') - - time_step = tf.convert_to_tensor(time_step) - synced_time_step = (time_step.shape.ndims == 0) - t, b, n, h = py_utils.GetShape(cached_states.key, 4) - - # Project inputs to key, value and query. Each has shape [B, 1, N, H]. - new_key_proj = self.key.FProp(theta.key, query_vec) - new_value_proj = self.value.FProp(theta.value, query_vec) - query_proj = self.query.FProp(theta.query, query_vec) - - # Using a if condtion, in case it's more efficient to update the same index. - if synced_time_step: - # The extended_key and extended_value have shape [T, B, N, H]. - extended_key = inplace_ops.alias_inplace_update( - cached_states.key, time_step, tf.reshape(new_key_proj, [b, n, h])) - extended_value = inplace_ops.alias_inplace_update( - cached_states.value, time_step, tf.reshape(new_value_proj, [b, n, h])) - else: - # The extended_key and extended_value have shape [T, B, N, H]. - selected_indices = tf.range(b) + time_step * b - extended_key = inplace_ops.alias_inplace_update( - tf.reshape(cached_states.key, [-1, n, h]), selected_indices, - tf.reshape(new_key_proj, [b, n, h])) - extended_value = inplace_ops.alias_inplace_update( - tf.reshape(cached_states.value, [-1, n, h]), selected_indices, - tf.reshape(new_value_proj, [b, n, h])) - extended_key = tf.reshape(extended_key, [t, b, n, h]) - extended_value = tf.reshape(extended_value, [t, b, n, h]) - updated_state = py_utils.NestedMap(key=extended_key, value=extended_value) - - if paddings is None: - paddings = tf.zeros([b, t], dtype=query_vec.dtype) - - encoded = self._DotAttenOneStep( - theta, - query_proj, - extended_key, - extended_value, - paddings, - segment_mask, - per_step_padding, - time_step=time_step, - use_short_seq_opt=use_short_seq_opt) - - # Post projection. - encoded = self.post.FProp(theta.post, encoded) - return encoded, updated_state - - @classmethod - def FPropMeta(cls, p, *args): - # args[0]: [b, t, d], args[1]: [b, s, d], args[2]: [b, s, d], - # args[3]: [b, s], args[4]: [b, t, s] if not None - args = tuple(py_utils.Flatten(args)) - py_utils.CheckShapes(args) - b, t, d = args[0] - s = args[3][1] - n = p.num_heads - # O(b * t * s * d) computation for self-attention and there are four - # projection layers, two of which has O(b * t * d^2), the other two has - # O(b * s * d^2). Each multiple-sum took 2 flops. Approximately - # self_attention took 15 flops per element since softmax is expensive. - flops = 15 * b * t * s * d + 2 * 2 * (b * t * d * d + b * s * d * d) - return py_utils.NestedMap(flops=flops, out_shapes=(args[0], (b, n, t, s))) - - -class MultiHeadedAttentionXL(MultiHeadedAttention): - """Transformer-XL multiheaded attention with relative positional embedding. - - https://arxiv.org/pdf/1901.02860.pdf section 3.3. - - Notice this is only intended for self attention. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('rel_pos_emb_dim', None, - 'Dimension of relative positional embedding.') - p.Define('skip_term_b', False, - 'If True, skip term_b in the paper section 3.3.') - return p - - def __init__(self, params): - """Constructs a MultiHeadedAttentionXL object.""" - super().__init__(params) - params = self.params - - assert not params.packed_input, 'Packed input not implemented yet.' - - if params.rel_pos_emb_dim is None or params.rel_pos_emb_dim <= 0: - raise ValueError('Invalide rel_pos_emb_dim: %s' % params.rel_pos_emb_dim) - - emb_params = layers.PositionalEmbeddingLayer.Params().Set( - embedding_dim=params.rel_pos_emb_dim) - self.CreateChild('pos_emb', emb_params) - - # Projection layer for relative position encoding - dim_per_head = params.hidden_dim // params.num_heads - pos_proj_tpl = params.proj_tpl.Copy().Set( - input_dim=params.rel_pos_emb_dim, - num_heads=params.num_heads, - dim_per_head=dim_per_head, - use_bias=False) - self.CreateChild('pos_proj', pos_proj_tpl) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - params = self.params - - dim_per_head = params.hidden_dim // params.num_heads - u_pc = py_utils.WeightParams( - shape=[params.num_heads, dim_per_head], - init=py_utils.WeightInit.Constant(0.0), - dtype=params.dtype, - collections=[self.__class__.__name__ + '_vars']) - v_pc = py_utils.WeightParams( - shape=[params.num_heads, dim_per_head], - init=py_utils.WeightInit.Constant(0.0), - dtype=params.dtype, - collections=[self.__class__.__name__ + '_vars']) - - self.CreateVariable('u', u_pc) - self.CreateVariable('v', v_pc) - - def _AttenLogits(self, theta, query, key): - b, _, n, h = py_utils.GetShape(key, 4) - t = py_utils.GetShape(query)[1] - - # This layer only supports self attention. - key = py_utils.HasShape(key, [b, t, n, h]) - - # [1, 2T - 1] - pos = tf.expand_dims(tf.range(-(t - 1), t, name='relative_pos'), 0) - sin_emb = self.pos_emb.FPropWithPosition(theta.pos_emb, pos) - # [1, 2T - 1, N, H] - sin_emb = self.pos_proj.FProp(theta.pos_proj, sin_emb) - # [2T - 1, N, H] - sin_emb = tf.squeeze(sin_emb, 0) - - logits = attention_util.AttenLogitsTransformerXL(query, key, sin_emb, - theta.u, theta.v, - self.params.skip_term_b) - return logits - - def _AttenLogitsOneStep(self, theta, query, key, time_step): - """Attention logits for one single target (query) step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, N, H]. - key: [S, B, N, H] or [S, B, N*H/128, 128]. - time_step: Current time step. - if it's a scalar, all the time step are the same decode step. - if it's a tensor, it represents current decode step for each sample. - - Returns: - A Tensor of shape [S, B, N] - """ - p = self.params - synced_time_step = (time_step.shape.ndims == 0) - s, b, _, _ = py_utils.GetShape(key, 4) - n = p.num_heads - h = p.hidden_dim // n - - # Transformer_XL relative attention. - if time_step is None: - raise ValueError('`time_step` can not be None when using relative ' - 'position encoding in attention.') - # term a and c. - logits = tf.einsum('BNH,SBNH->SBN', query + theta.u, - tf.reshape(key, [s, b, n, h])) - if synced_time_step: - position = tf.expand_dims(time_step - tf.range(s), 0) - else: - # [b, s] - position = ( - tf.expand_dims(time_step, -1) - - tf.tile(tf.expand_dims(tf.range(s), 0), [b, 1])) - # [b, s, emb_dim] - sin_emb = self.pos_emb.FPropWithPosition(theta.pos_emb, position) - # [b, s, n, h] - sin_emb = self.pos_proj.FProp(theta.pos_proj, sin_emb) - if synced_time_step: - # [s, n, h] - sin_emb = tf.squeeze(sin_emb, 0) - # term b an d. - if not p.skip_term_b: - logits += tf.einsum('BNH,SNH->SBN', query + theta.v, sin_emb) - else: - logits += tf.expand_dims(tf.einsum('NH,SNH->SN', theta.v, sin_emb), 1) - else: - # term b an d. - if not p.skip_term_b: - logits += tf.einsum('BNH,BSNH->SBN', query + theta.v, sin_emb) - else: - logits += tf.einsum('NH,BSNH->BSN', theta.v, sin_emb) - return logits - - def ExtendStep(self, - theta, - query_vec, - cached_states, - paddings, - segment_mask, - per_step_padding, - time_step, - use_short_seq_opt=False): - # TODO(jamesqin): support use_short_seq_opt for TransofrmerXL attention. - assert not use_short_seq_opt - return super().ExtendStep(theta, query_vec, cached_states, paddings, - segment_mask, per_step_padding, time_step, - use_short_seq_opt) - - -class MultiHeadedAttentionRPE(MultiHeadedAttention): - """Multiheaded attention with relative positional embedding ... - - See https://arxiv.org/pdf/1803.02155.pdf. - - Notice this is only intended for self attention. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('rel_pos_emb_dim', None, - 'Dimension of relative positional embedding.') - p.Define('rel_pos_radius', None, - 'Relative distance is clipped to [-radius, radius].') - p.Define('skip_value_emb', False, 'If skipping value positional embedding.') - p.Define( - 'use_global_emb', True, - 'If using global relative positional embedding. Only effective if ' - '`rel_pos_emb_tpl` is not None.') - return p - - def __init__(self, params): - """Constructs a MultiHeadedAttentionRPE object.""" - super().__init__(params) - params = self.params - - assert not params.packed_input, 'Packed input not implemented yet.' - - if not params.rel_pos_radius: - raise ValueError('Invalid rel_pos_radius: %s' % params.rel_pos_radius) - - if params.rel_pos_emb_dim is None: - rel_pos_emb_dim = params.hidden_dim - else: - rel_pos_emb_dim = params.rel_pos_emb_dim - - rel_pos_emb_tpl = layers.RelativePositionalEmbeddingLayer.Params().Set( - radius=params.rel_pos_radius, dim=rel_pos_emb_dim) - if rel_pos_emb_dim != params.hidden_dim: - # Projection layer for relative position encoding - dim_per_head = params.hidden_dim // params.num_heads - pos_proj_tpl = params.proj_tpl.Copy().Set( - input_dim=rel_pos_emb_dim, - num_heads=params.num_heads, - dim_per_head=dim_per_head, - use_bias=False) - else: - pos_proj_tpl = None - - self.CreateChild('key_emb', rel_pos_emb_tpl) - # Add projection layer if rel_pos_emb_dim is different from hidden_dim. - if pos_proj_tpl is not None: - self.CreateChild('key_pos_proj', pos_proj_tpl) - if not params.skip_value_emb: - self.CreateChild('value_emb', rel_pos_emb_tpl) - if pos_proj_tpl is not None: - self.CreateChild('value_pos_proj', pos_proj_tpl) - - def _CreateChildrenVariables(self): - with tf.variable_scope( - self.params.name, - reuse=tf.AUTO_REUSE if self.params.use_global_emb else False): - for child in ['key_emb', 'key_pos_proj', 'value_emb', 'value_pos_proj']: - if child in self.children: - self.children[child].InstantiateVariables() - super()._CreateChildrenVariables() - - def _RelativePositionValueEmb(self, theta, key): - """Gets relative positional value embedding. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - key: The attention key, a tensor of shape [batch, seqlen, dim] - - Returns: - Relative positional embedding, a Tensor of shape - [tgt_time=seqlen, src_time=seqlen, num_heads, attenion_dim] - """ - emb_layer = self.value_emb - emb_theta = theta.value_emb - - seqlen = py_utils.GetShape(key)[1] - src_time_indices = tf.tile(tf.expand_dims(tf.range(seqlen), 0), [seqlen, 1]) - tgt_time_indices = tf.tile( - tf.expand_dims(tf.range(seqlen), -1), [1, seqlen]) - - # [tgt_time=T, src_time=T, num_heads x hidden_dim] - pos_emb = emb_layer.FProp(emb_theta, src_time_indices - tgt_time_indices) - - params = self.params - num_heads = self.params.num_heads - tgt_time, src_time, _ = py_utils.GetShape(pos_emb) - - pos_proj_layer = 'value_pos_proj' - if hasattr(self, pos_proj_layer): - return getattr(self, pos_proj_layer).FProp( - getattr(theta, pos_proj_layer), pos_emb) - else: - return tf.reshape( - pos_emb, - [tgt_time, src_time, num_heads, params.hidden_dim // num_heads]) - - def _AttenLogits(self, theta, query, key): - # TODO(jamesqin): optimize it. - b, _, n, h = py_utils.GetShape(key, 4) - t = py_utils.GetShape(query)[1] - - # This layer only supports self attention. - key = py_utils.HasShape(key, [b, t, n, h]) - - # [1, 2T - 1] - pos = tf.expand_dims(tf.range(-(t - 1), t), 0) - # [1, 2T - 1, rel_pos_emb_dim] - abs_emb = self.key_emb.FProp(theta.key_emb, pos) - if hasattr(self, 'key_pos_proj'): - # [1, 2T - 1, N, H] - abs_emb = self.key_pos_proj.FProp(theta.key_pos_proj, abs_emb) - # [2T - 1, N, H] - abs_emb = tf.squeeze(abs_emb, 0) - else: - abs_emb = tf.reshape(abs_emb, [2 * t - 1, n, h]) - - return attention_util.AttenLogitsRPE(query, key, abs_emb) - - def _AttenLogitsOneStep(self, theta, query, key, time_step): - """Attention logits for one single target (query) step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, N, H]. - key: [S, B, N, H] or [S, B, N*H/128, 128]. - time_step: Current time step. - - Returns: - A Tensor of shape [S, B, N] - """ - p = self.params - s, b, _, _ = py_utils.GetShape(key, 4) - n = p.num_heads - h = p.hidden_dim // n - - # Transformer_XL relative attention. - if time_step is None: - raise ValueError('`time_step` can not be None when using relative ' - 'position encoding in attention.') - # Gets positional embedding. - # [1, S] - rel_dists = tf.expand_dims(time_step - tf.range(s), 0) - # [1, S, rel_pos_emb_dim] - pos_emb = self.key_emb.FPropDefaultTheta(rel_dists) - if hasattr(self, 'key_pos_proj'): - # [1, S, N, H] - pos_emb = self.key_pos_proj.FProp(theta.key_pos_proj, pos_emb) - # [S, 1, N, H] - pos_emb = tf.transpose(pos_emb, [1, 0, 2, 3]) - else: - pos_emb = tf.reshape(pos_emb, [s, 1, n, h]) - return tf.einsum('BNH,SBNH->SBN', query, - tf.reshape(key, [s, b, n, h]) + pos_emb) - - def _AttenContext(self, theta, probs, value): - # TODO(jamesqin): optimize it. - encoded = tf.einsum('BNij,BjNH->BiNH', probs, value) - - if not self.params.skip_value_emb: - encoded += tf.einsum('BNij,ijNH->BiNH', probs, - self._RelativePositionValueEmb(theta, value)) - return encoded - - def _AttenContextOneStep(self, theta, probs, value, time_step): - s, b, _, _ = py_utils.GetShape(value, 4) - n = self.params.num_heads - h = self.params.hidden_dim // n - - logits = tf.einsum('SBN,SBNH->BNH', probs, tf.reshape(value, [s, b, n, h])) - - if not self.params.skip_value_emb: - # [1, S] - rel_dists = tf.expand_dims(time_step - tf.range(s), 0) - # [1, S, rel_pos_emb_dim] - pos_emb = self.value_emb.FProp(theta.value_emb, rel_dists) - if hasattr(self, 'value_pos_proj'): - # [1, S, N, H] - pos_emb = self.value_pos_proj.FProp(theta.value_pos_proj, pos_emb) - pos_emb = tf.squeeze(pos_emb, 0) - else: - pos_emb = tf.reshape(pos_emb, [s, n, h]) - logits += tf.einsum('SBN,SNH->BNH', probs, pos_emb) - return logits - - def ExtendStep(self, - theta, - query_vec, - cached_states, - paddings, - segment_mask, - per_step_padding, - time_step, - use_short_seq_opt=False): - # TODO(jamesqin): support use_short_seq_opt. - assert not use_short_seq_opt - return super().ExtendStep(theta, query_vec, cached_states, paddings, - segment_mask, per_step_padding, time_step, - use_short_seq_opt) - - @classmethod - def FPropMeta(cls, p, *args): - return NotImplementedError() - - -class LocalSelfAttention(MultiHeadedAttention): - """Dot-product causal self attention using a sliding window. - - We use the following capital letters to denote certain - tensor parameters. - - B = batch size - S=T= length of the key/value (source) and query (target) - D = model dimension - N = number of attention heads - H = dimensions of each attention head - W = block size - L = left context size, including left L-1 positions and self - R = right context size - F = L + R = context size of one position. - C = L + R + W - 1 = context size of a block of W positions. - U = ceiling(T/W). - - The key difference to base class is on calculating logits: - Base class: - 1) Compute the full S x T attention. - 2) Apply a S x T mask to enforce local attention window. - This implementation: - 1) Compute a W x C attention for each of the U blocks. Where the i-th - block has query[W*i:W*(i+1)] and key[W*(i-1)-L-1:W*(i+1)+R]. - 2) Apply a W x C mask for each block. - - Effectively, we reduce both time and space complexities for computing the - sliding window attention from O(S * T) to O(S * C). In practice we observe - reduced HBM usage on TPU but no speed gains. - - Note: Cross attention is not supported. As a result in speech models this - class can only be used for encoder. - - TODO(weihan): add masking based local attention to the base class. - - """ - - @classmethod - def Params(cls): - """Params for LocalSelfAttention.""" - p = super().Params() - p.Define( - 'block_size', None, 'Size of a processing block, if unset, default to ' - 'max(1, left_context-1).') - p.Define( - 'left_context', None, 'Number of left positions to attend ' - '(including current position).') - p.Define('right_context', 0, 'Number of right positions to attend.') - return p - - def __init__(self, params): - """Constructs a LocalSelfAttention object.""" - super().__init__(params) - - p = self.params - assert p.left_context >= 1, 'Left context should be at least one.' - assert not p.packed_input, 'Packed input not implemented yet.' - if p.block_size is None: - p.block_size = max(1, p.left_context - 1) - tf.logging.warning('block_size not set, use default value {}'.format( - p.block_size)) - - assert not p.packed_input, 'Packed input not implemented yet.' - - def _AttenLogits(self, theta, query, key): - return tf.einsum('BUTNH,BUSNH->BNUTS', query, key) - - def AttenProbs(self, - theta, - query, - key, - paddings, - segment_mask, - unused_per_step_padding=None): - """Compute attention probability. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, T, N, H]. - key: [B, S=T, N, H]. - paddings: [B, T]. - segment_mask: [B, 1, T, S] not used right now. - unused_per_step_padding: Not used. - - Returns: - logits: [B, U, N, W, 2 * W] - """ - p = self.params - key = py_utils.HasRank(key, 4) - b, t, n, h = py_utils.GetShape(key, 4) - paddings = py_utils.HasShape(paddings, [b, t]) - query = py_utils.HasShape(query, [b, t, n, h]) - - # -> [B, U, C, N, H] - key_block_context = attention_util.ExtractBlockContext( - key, - block_size=p.block_size, - left_context=p.left_context, - right_context=p.right_context) - _, u, c, _, _ = py_utils.GetShape(key_block_context) - - # -> [B, U, W, N, H] - query_blocks = attention_util.ConvertToBlocks( - query, block_size=p.block_size) - _, _, w, _, _ = py_utils.GetShape(query_blocks) - - # -> [B, U, C] - paddings_block_context = attention_util.ExtractBlockContext( - paddings, - block_size=p.block_size, - left_context=p.left_context, - right_context=p.right_context, - padding_val=1) - - # -> [B, N, U, W, C] - paddings = tf.tile( - tf.reshape(paddings_block_context, [b, 1, u, 1, c]), [1, n, 1, w, 1]) - - # Make local causal paddings. - # -> [U, W, C] - local_causal_padding = attention_util.MakeCausalPadding( - seq_len=t, - block_size=p.block_size, - left_context=p.left_context, - right_context=p.right_context, - dtype=paddings.dtype) - paddings += local_causal_padding - - # -> [B, N, U, W, C] - logits = self._AttenLogits(theta, query_blocks, key_block_context) - - very_negative_logits = ( - tf.ones_like(logits) * logits.dtype.max * - tf.constant(-0.7, dtype=logits.dtype)) - padded_logits = tf.where(paddings > 0.0, very_negative_logits, logits) - - probs = tf.nn.softmax(padded_logits) - return probs - - def _DotAtten(self, - theta, - query, - key, - value, - paddings, - segment_mask, - per_step_padding=None): - """Main attention function. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, T, N, H]. - key: [B, S=T, N, H]. - value: [B, S=T, N, H]. - paddings: [B, S=T]. - segment_mask: [B, 1, S=T, S=T]. - per_step_padding: A mask of shape [B, T, S=T] if not None. - - Returns: - encoded: [B, T, N, H]. - atten_probs: [B, N, T, S]. - """ - p = self.params - # Scale the query projection. - if p.enable_per_dim_scale: - query = self.per_dim_scale.FProp(theta.per_dim_scale, query) - else: - query *= (p.hidden_dim // p.num_heads)**-0.5 - t0 = py_utils.GetShape(query)[1] - - # -> [B, N, U, W, C] - probs = self.AttenProbs(theta, query, key, paddings, segment_mask, - per_step_padding) - - # Apply dropout to probs. - probs = self.atten_dropout.FProp(theta.atten_dropout, probs) - - # -> [B, U, C, N, H] - value_block_context = attention_util.ExtractBlockContext( - value, - block_size=p.block_size, - left_context=p.left_context, - right_context=p.right_context) - - # Compute the attention context vector. - # -> [B, U, W, N, H] - encoded = tf.einsum('BNUWC,BUCNH->BUWNH', probs, value_block_context) - b, u, w, n, h = py_utils.GetShape(encoded) - encoded = tf.reshape(encoded, [b, u * w, n, h]) - # Remove the extra time padding introduced by converting to blocks. - encoded = encoded[:, :t0, ...] - return encoded, probs - - def ExtendStep(self, - theta, - query_vec, - cached_states, - paddings, - segment_mask=None, - per_step_padding=None, - time_step=None, - use_short_seq_opt=False): - """Computes the value vector given the query of the current step. - - This function is used by autoregressive decoding. This function knows the - length of full sequence, thus it is different from StreamingExtendStep. - - Note: When the context window size is much smaller than target sequence - length, to make it run more efficent, T below can be just the window size. - Then, time_step should be the relative decode step and not bigger than T. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [B, 1, D]. - cached_states: A `.NestedMap` object containing tensors which are the - results of previous attentions, used for fast decoding. key - [T, B, - N, H]. value - [T, B, N, H]. - paddings: [B, T], or None if there is no padding. - segment_mask: [B, 1, T, S] or None. Not used right now. - per_step_padding: A mask used by decoder self-attention to prevent - information flow from future (causal padding). It has shape [B, 1, T] if - not None. Not used right now. - time_step: A scalar, the current decode step, 0-based. - use_short_seq_opt: A bool, whether using short sequence optimization. Not - supported right now. - - Returns: - encoded: [B, 1, D]. - updated_key_vec: [T, B, N, H]. - updated_value_vec: [T, B, N, H]. - - Raises: - ValueError: If right_context is non-zero. - NotImplementedError: If use_short_seq_opt is true. - """ - p = self.params - if p.right_context != 0: - raise ValueError( - 'Right context must be zero for autoregressive decoding.') - if use_short_seq_opt: - raise NotImplementedError('use_short_seq_opt is not supported yet.') - - # Make local causal paddings, which have shape [B, T]. - t, b, _, _ = py_utils.GetShape(cached_states.key, 4) - if paddings is None: - paddings = tf.zeros([b, t], dtype=query_vec.dtype) - position_diff = tf.tile(tf.range(t)[tf.newaxis, :], [b, 1]) - time_step - valid_atten = tf.math.logical_and(position_diff > -p.left_context, - position_diff <= 0) - local_causal_padding = 1.0 - tf.cast(valid_atten, dtype=query_vec.dtype) - paddings += local_causal_padding - - return super().ExtendStep(theta, query_vec, cached_states, paddings, - segment_mask, per_step_padding, time_step, - use_short_seq_opt) - - def zero_state(self, batch_size=1): - """Returns the initial state given the batch size. - - Args: - batch_size: the batch size. - - Returns: - state: The initial state for streaming inference. - """ - p = self.params - assert p.enable_value_proj, 'Value projection must be enabled.' - assert p.right_context == 0, ('StreamingExtendStep does not support look ' - 'ahead') - key_state = tf.zeros( - shape=[ - p.left_context, batch_size, p.num_heads, p.hidden_dim // p.num_heads - ], - dtype=tf.float32) - value_state = tf.zeros( - shape=[ - p.left_context, batch_size, p.num_heads, p.hidden_dim // p.num_heads - ], - dtype=tf.float32) - state = py_utils.NestedMap(key=key_state, value=value_state) - return state - - def StreamingExtendStep(self, query_vec, state, time_step): - """Computes the value vector given the query of the current step. - - This function doesn't know the length of full sequence, thus it is - different from ExtendStep. - - Args: - query_vec: A query vector of shape [B, 1, D]. - state: A `.NestedMap` object containing tensors {key, value} which are - results of previous attentions. key, value are of shape [T, B, N, H] - where T is the state size of this layer. - time_step: A tensor of shape [1] and type tf.int32. Note, we can not use - scalar tensor here because TfLiteConverter doesn't have good support of - it (b/138865275). - - Returns: - output: Output of the given query vector with shape [B, 1, D]. - state: updated state. - """ - p = self.params - assert p.enable_value_proj, 'Value projection must be enabled.' - assert p.right_context == 0, ('StreamingExtendStep does not support look ' - 'ahead') - query_vec = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(query_vec), [-1, 1, p.input_dim]) - ], query_vec) - state.key = py_utils.with_dependencies([ - py_utils.assert_shape_match( - tf.shape(state.key), - [p.left_context, -1, p.num_heads, p.hidden_dim // p.num_heads]) - ], state.key) - state.value = py_utils.with_dependencies([ - py_utils.assert_shape_match( - tf.shape(state.value), - [p.left_context, -1, p.num_heads, p.hidden_dim // p.num_heads]) - ], state.value) - - t, b, n, h = py_utils.GetShape(state.key, 4) # t: context window size - - # Computes key, value projection and updates state. - new_key_proj = self.key.FProp(self.theta.key, query_vec) # [B, 1, N, H] - new_key_proj = tf.reshape(new_key_proj, [1, b, n, h]) - new_value_proj = self.key.FProp(self.theta.value, query_vec) # [B, 1, N, H] - new_value_proj = tf.reshape(new_value_proj, [1, b, n, h]) - state.key = tf.concat([state.key[1:, :, :, :], new_key_proj], axis=0) - state.value = tf.concat([state.value[1:, :, :, :], new_value_proj], axis=0) - - # For a time step less than the context window size, the time dimension of - # input of logits computation is equal to the time step (not a full context - # window). - t = tf.math.minimum(time_step[0] + 1, t) - key_input = state.key[-t:, :, :, :] - value_input = state.value[-t:, :, :, :] - - # Computes query projection. - query_proj = self.query.FProp(self.theta.query, query_vec) # [B, 1, N, H] - - # Scales the query projection. - if p.enable_per_dim_scale: - query_proj = self.per_dim_scale.FProp(self.theta.per_dim_scale, - query_proj) - else: - query_proj *= h**-0.5 - query_proj = tf.reshape(query_proj, [b, n, h]) - - # Computes attention outputs. - # TODO(wildstone): Replaces the einsum ops used below with mat mul to get - # rid of TfLite Flex ops. - logits = self._AttenLogitsOneStep(self.theta, query_proj, key_input, - t - 1) # [T, B, N] - logits = tf.reshape(logits, [t, -1]) - posteriors = tf.nn.softmax(logits, axis=0) - posteriors = tf.reshape(posteriors, [t, b, n]) - output = tf.einsum('TBN, TBNH->BNH', posteriors, value_input) - - # Post projection. - output = tf.expand_dims(output, 1) - output = self.post.FProp(self.theta.post, output) - return output, state - - @classmethod - def FPropMeta(cls, p, *args): - raise NotImplementedError() - - -class LocalSelfAttentionXL(LocalSelfAttention): - """Local causal version of transformer-xl self attention.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('rel_pos_emb_dim', None, - 'Dimension of relative positional embedding.') - p.Define('skip_term_b', False, - 'If True, skip term_b in the paper section 3.3.') - return p - - def __init__(self, params): - """Constructs a LocalSelfAttentionXL object.""" - super().__init__(params) - params = self.params - if params.rel_pos_emb_dim is None or params.rel_pos_emb_dim <= 0: - raise ValueError('Invalid rel_pos_emb_dim: %s' % params.rel_pos_emb_dim) - - emb_params = layers.PositionalEmbeddingLayer.Params().Set( - embedding_dim=params.rel_pos_emb_dim) - self.CreateChild('pos_emb', emb_params) - - # Projection layer for relative position encoding - dim_per_head = params.hidden_dim // params.num_heads - pos_proj_tpl = params.proj_tpl.Copy().Set( - input_dim=params.rel_pos_emb_dim, - num_heads=params.num_heads, - dim_per_head=dim_per_head, - use_bias=False) - self.CreateChild('pos_proj', pos_proj_tpl) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - params = self.params - - dim_per_head = params.hidden_dim // params.num_heads - u_pc = py_utils.WeightParams( - shape=[params.num_heads, dim_per_head], - init=py_utils.WeightInit.Constant(0.0), - dtype=params.dtype, - collections=[self.__class__.__name__ + '_vars']) - v_pc = py_utils.WeightParams( - shape=[params.num_heads, dim_per_head], - init=py_utils.WeightInit.Constant(0.0), - dtype=params.dtype, - collections=[self.__class__.__name__ + '_vars']) - - self.CreateVariable('u', u_pc) - self.CreateVariable('v', v_pc) - - def _AttenLogits(self, theta, query, key): - b, u, w, _, _ = py_utils.GetShape(query) - _, _, c, _, _ = py_utils.GetShape(key) - n = self.params.num_heads - l = self.params.left_context - r = self.params.right_context - f = l + r - # term a and c - term_ac = tf.einsum('BUTNH,BUSNH->BNUTS', query + theta.u, key) - - # term b and d - # [1, F] - pos = tf.expand_dims(tf.range(l - 1, -r - 1, -1), 0) - sin_emb = self.pos_emb.FPropWithPosition(theta.pos_emb, pos) - # [1, F, N, H] - sin_emb = self.pos_proj.FProp(theta.pos_proj, sin_emb) - # [F, N, H] - sin_emb = tf.squeeze(sin_emb, 0) - - p = self.params - if not p.skip_term_b: - # [B, N, U, W, F] - term_bd = tf.einsum('BUWNH,FNH->BNUWF', query + theta.v, sin_emb) - - # Perform relative shift in order to get [B, N, U, W, C] - # Pads the input to [B, N, U, C, C+1] - term_bd = tf.pad(term_bd, - ((0, 0), (0, 0), (0, 0), (0, c - w), (0, c + 1 - f))) - - # Reshapes to [B, N, U, C+1, C]. Note the output last dim is 1-smaller - # than the input, which "pushses" one element off to the next row for each - # row. The accumulated effect is row_i is right-shifted i steps (i>=0). - term_bd = tf.reshape(term_bd, [b, n, u, c + 1, c]) - - # Keeps useful slices. [B, N, U, W, C] - term_bd = tf.slice(term_bd, [0, 0, 0, 0, 0], [-1, -1, -1, w, -1]) - else: - # [N, F] - term_d = tf.einsum('NH,FNH->NF', theta.v, sin_emb) - # [N, W, F] - term_d = tf.tile(tf.expand_dims(term_d, 1), [1, w, 1]) - # [N, C, C+1] - term_d = tf.pad(term_d, ((0, 0), (0, c - w), (0, c + 1 - f))) - # [N, C+1, C] - term_d = tf.reshape(term_d, [n, c + 1, c]) - # Keeps useful slices. [N, W, C] - term_d = tf.slice(term_d, [0, 0, 0], [-1, w, -1]) - term_bd = tf.reshape(term_d, [1, n, 1, w, c]) - return term_ac + term_bd - - def _AttenLogitsOneStep(self, theta, query, key, time_step): - """Attention logits for one single target (query) step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, N, H]. - key: [S, B, N, H] or [S, B, N*H/128, 128]. - time_step: Current time step. - - Returns: - A Tensor of shape [S, B, N] - """ - p = self.params - s, b, _, _ = py_utils.GetShape(key, 4) - n = p.num_heads - h = p.hidden_dim // n - - # Transformer_XL relative attention. - if time_step is None: - raise ValueError('`time_step` can not be None when using relative ' - 'position encoding in attention.') - # term a and c. - logits = tf.einsum('BNH,SBNH->SBN', query + theta.u, - tf.reshape(key, [s, b, n, h])) - position = tf.expand_dims(time_step - tf.range(s), 0) - # [1, s, emb_dim] - sin_emb = self.pos_emb.FPropWithPosition(theta.pos_emb, position) - sin_emb = self.pos_proj.FProp(theta.pos_proj, sin_emb) - # [s, n, h] - sin_emb = tf.squeeze(sin_emb, 0) - - # term b an d. - if not p.skip_term_b: - logits += tf.einsum('BNH,SNH->SBN', query + theta.v, sin_emb) - else: - logits += tf.expand_dims(tf.einsum('NH,SNH->SN', theta.v, sin_emb), 1) - return logits - - -class RoutingAttention(MultiHeadedAttention): - """"Implements a sparse attention based on k-means clustering. - - This is used in the routing transformer https://arxiv.org/pdf/2003.05997. - - This verison of multi-headed attention differs from the full attention - in that it uses k-means clusterting to cluster the queries and keys first, - and each query only attend to a subset of keys that are close to the centroid - closest to that query. As Euclidean distance is used to determine closeness, - we layer normalize queries and keys first so that closeness lead to a larger - dot product. - - TODO(zhouwk) This class is missing the following features: - * propagate clustering loss; - * supporting packed inputs; - * support attention dropout; - * support relative position encoding; - * support using local attention on some heads. - - We use the following capital letters to denote shape parameters: - B = batch size - S = length of the source sequence - T = length of the target sequence - N = number of attention heads - H = dimensions of each attention head - D = model dimension - - K = number of clusters - W = attention window - """ - - @classmethod - def Params(cls): - """Params.""" - p = super().Params() - p.Define( - 'num_clusters', 0, 'Number of clusters, typically around the square' - ' root of the sequence length.') - p.Define('attention_window', 0, 'The number of keys each query attends to.') - p.Define('clustering', attention_util.KMeansClusteringForAtten.Params(), - 'The params for a clustering layer.') - p.Define( - 'causal_masking', False, - 'Whether causal masking is enabled. When set, a query at position idx ' - 'is only allowed to attend to keys/values at positions <= idx.') - p.Define( - 'fast_path', True, - 'Whether to use a more efficient implementation. The fast path is ' - 'signanificantly faster by grouping queries when determining which ' - 'values to attend to (which might leave out some queries or duplicate ' - 'others); fast_path=False computes this per each query.') - p.Define( - 'query_group_size_factor', 1.2, - 'Only used when p.fast_path=True. When grouping queries, we make the ' - 'group size larger by this multiplier to not leave out any queries due ' - 'to potential cluster imbalance.') - return p - - def __init__(self, params): - """Constructs an instance of RoutingAttention.""" - super().__init__(params) - p = self.params - assert p.num_clusters - assert p.attention_window - assert not p.packed_input - - clustering_p = p.clustering - clustering_p.num_clusters = p.num_clusters - clustering_p.num_heads = p.num_heads - clustering_p.dim_per_head = p.hidden_dim // p.num_heads - # We normalize manually prior so that we can reuse the same normalized - # query/key to compute attention probs later. - clustering_p.apply_layer_norm = False - self.CreateChild('clustering', clustering_p) - - def _DotAtten(self, - theta, - query, - key, - value, - paddings, - segment_mask=None, - per_step_padding=None, - query_paddings=None): - """Computes the attention. - - Each query selects 'p.attention_window' number of keys to attend to. First - we find the closest centroid to that query, and we only allow that query to - attend to the 'p.attention_window' closest keys to that centroid. - - In order to use K-means, this implementation applies layer normalization - to both the queries and the keys, and uses the normalized results to compute - attention weights. - - When 'p.attention_window' is the source length, this should evalue to the - full attention (using layer normalized queries and keys). - - The caller should pass in the paddings for both 'key' and 'query' because - during training, when we update the clustering we need to know the paddings - for both. (For the inference path only 'key_paddings' is useful.) - - Args: - theta: A `.NestedMap` of the values of this layer's weights. - query: [B, T, N, H]. - key: [B, S, N, H]. - value: [B, S, N, H]. - paddings: [B, S], paddings for key. - segment_mask: must be None. - per_step_padding: must be None. Please use p.causal_masking. - query_paddings: [B, T], or None. - - Returns: - encoded: [B, T, N, H]. - atten_probs: [B, T, N, S]. - """ - p = self.params - if segment_mask is not None or per_step_padding is not None: - raise ValueError('Requires segment_mask=None and per_step_padding=None.') - key_paddings = paddings - b, t = py_utils.GetShape(query, 2) - if query_paddings is None: - query_paddings = tf.zeros([b, t], dtype=key_paddings.dtype) - - is_self_attention = (query is key) - # Whether to update the centroids. Only do this during training. - update = not self.do_eval - - query = attention_util.KMeansClusteringForAtten.LayerNorm(query) - # [B, T, N, K] - q_dists, _ = self.clustering.FProp( - theta.clustering, query, query_paddings, update=update) - - if is_self_attention: - key = query - k_dists = q_dists - else: - key = attention_util.KMeansClusteringForAtten.LayerNorm(key) - # [B, S, N, K] - k_dists, _ = self.clustering.FProp( - theta.clustering, key, key_paddings, update=update) - if p.fast_path: - return self._DotAttenFastPath(theta, query, key, value, q_dists, k_dists, - query_paddings, key_paddings) - else: - return self._DotAttenSlowPath(theta, query, key, value, q_dists, k_dists, - query_paddings, key_paddings) - - def InitStates(self, theta, target_batch_size, target_max_length): - """Initialize 'states' with .key, .value, and .key_dists.""" - p = self.params - states = super().InitStates(theta, target_batch_size, target_max_length) - states.key_dists = inplace_ops.empty( - shape=(target_max_length, target_batch_size, p.num_heads, - p.num_clusters), - dtype=py_utils.FPropDtype(p), - init=True) - return states - - def ExtendStep(self, - theta, - query_vec, - cached_states, - paddings, - time_step, - segment_mask=None, - per_step_padding=None, - use_short_seq_opt=False): - """Computes the value vector given the query of the current step. - - This function is used by autoregressive decoding. Used for self-attention - (hence S=T) with p.causal_masking is True. - - We compute the key/value/key_dists at `time_step` and cache the updated - full length results in `cache_states` to reduce duplicate computation. - - p.fast_path is ignored (as if p.fast_path=False) as at each step we only - compute for query of length 1. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [B, 1, D]. - cached_states: A `.NestedMap` object containing tensors which are the - results of previous attentions, used for fast decoding. It contains .key - and .value with shape [T, B, N, H], and .key_dists with shape [T, B, N, - K]. Note that they are all time-major. - paddings: [B, T], or None if there is no padding. - time_step: Scalar, the current decode step, 0-based. - segment_mask: must be None. - per_step_padding: must be None. We obey causal masking. - use_short_seq_opt: must be False. - - Returns: - encoded: [B, 1, D]. - updated_states: `.NestedMap` with .key, .value, .key_dists. - - Raises: - ValueError: If value projection is disabled. - """ - p = self.params - if not p.enable_value_proj: - raise ValueError('Value projection must be enabled: ' - 'set p.enable_value_proj = True.') - if not p.causal_masking: - raise ValueError('p.causal_masking must be true.') - if segment_mask is not None or per_step_padding is not None: - raise ValueError('Requires segment_mask=None and per_step_padding=None.') - if use_short_seq_opt: - raise ValueError('Requires use_short_seq_opt=False.') - if time_step is None: - raise ValueError('Requires valid time_step, not None.') - - t, b, n, h = py_utils.GetShape(cached_states.key, 4) - - # Project inputs to key, value and query. Each has shape [B, 1, N, H]. - key_proj = self.key.FProp(theta.key, query_vec) - value_proj = self.value.FProp(theta.value, query_vec) - query_proj = self.query.FProp(theta.query, query_vec) - - query_proj = attention_util.KMeansClusteringForAtten.LayerNorm(query_proj) - key_proj = attention_util.KMeansClusteringForAtten.LayerNorm(key_proj) - # [B, 1, N, K] - k_dists, _ = self.clustering.FProp(theta.clustering, key_proj) - - # The updated_key and extended_value have shape [T, B, N, H]. - updated_key = inplace_ops.alias_inplace_update( - cached_states.key, time_step, tf.reshape(key_proj, [b, n, h])) - updated_value = inplace_ops.alias_inplace_update( - cached_states.value, time_step, tf.reshape(value_proj, [b, n, h])) - # Shape [T, B, N, K] - updated_key_dists = inplace_ops.alias_inplace_update( - cached_states.key_dists, time_step, - tf.reshape(k_dists, [b, n, p.num_clusters])) - updated_states = py_utils.NestedMap( - key=updated_key, value=updated_value, key_dists=updated_key_dists) - - if paddings is None: - paddings = tf.zeros([b, t], dtype=query_vec.dtype) - # Apply causal padding. Shape [B, T] - paddings = tf.where( - tf.greater( - tf.tile(tf.range(t)[None, :], [b, 1]), tf.fill([b, t], time_step)), - tf.ones_like(paddings), paddings) - query_paddings = tf.zeros([b, 1], dtype=paddings.dtype) - - encoded = self._DotAttenOneStep( - theta, - query_proj, - updated_states, - query_paddings=query_paddings, - key_paddings=paddings, - time_step=time_step) - # Post projection. - encoded = self.post.FProp(theta.post, encoded) - return encoded, updated_states - - def _DotAttenOneStep(self, theta, query, states, query_paddings, key_paddings, - time_step): - """Dot attention function for queries with 1 time step. - - Called from ExtendStep(). Used for self-attention with p.causal_masking - is True. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query: [B, 1, N, H], already normalized. - states: .key and .value with shape [T, B, N, H], .key_dists with shape - [T, B, N, K]. .key is normalized. - query_paddings: [B, 1]. - key_paddings: [B, T]. - time_step: Scalar, the current decode step, 0-based. - - Returns: - encoded: [B, 1, N, H]. - """ - p = self.params - # [B, 1, N, K] - q_dists, _ = self.clustering.FProp(theta.clustering, query) - # [B, T, N, K] - k_dists = tf.transpose(states.key_dists, [1, 0, 2, 3]) - - very_large_dists = tf.ones_like(k_dists) * tf.constant( - 0.1, dtype=k_dists.dtype) * k_dists.dtype.max - paddings_tiled = tf.tile(key_paddings[:, :, None, None], - [1, 1, p.num_heads, p.num_clusters]) - k_dists = tf.where(paddings_tiled > 0.0, very_large_dists, k_dists) - - key = tf.transpose(states.key, [1, 0, 2, 3]) - value = tf.transpose(states.value, [1, 0, 2, 3]) - encoded, _ = self._DotAttenSlowPath( - theta, - query, - key, - value, - q_dists, - k_dists, - query_paddings, - key_paddings, - query_relative_position_shift=time_step) - return encoded - - def _DotAttenSlowPath(self, - theta, - query, - key, - value, - q_dists, - k_dists, - query_paddings, - key_paddings, - query_relative_position_shift=0): - """Computes the attention via the slow path. - - This implementation selects, on a per query basis, p.attention_window - number of keys/values to attend to. - - Args: - theta: A `.NestedMap` of the values of this layer's weights. - query: [B, T, N, H], already normalized. - key: [B, S, N, H], already normalized. - value: [B, S, N, H]. - q_dists: [B, T, N, K]. - k_dists: [B, S, N, K]. - query_paddings: [B, T]. - key_paddings: [B, S]. - query_relative_position_shift: scalar. The position (relative to key[0]) - of query[0]. This impacts relative position encoding (not yet - implemented) and causal masking. - - Returns: - encoded: [B, T, N, H]. - atten_probs: [B, T, N, S]. - """ - p = self.params - - # [B, N, K, S] - # If key is padded in a position, 'k_dists' is inf which ensures - # that we consider all non-padded keys even if some padded keys - # might appear closer. - k_dists = tf.transpose(k_dists, [0, 2, 3, 1]) - - # [B, N, K, W], for each centroid, the indices of closest key vecs. - # It's okay if W is so larger such that a padded index is included, - # because below in attention_util.ComputeSparseAttention() correctly - # handles 'paddings'. - _, closest_indices = tf.math.top_k(-k_dists, p.attention_window) - # [B, T, N, K], one hot encoded closest centroid for each query vec. - nearest_one_hot = tf.one_hot( - tf.math.argmin(q_dists, axis=-1), - p.num_clusters, - dtype=closest_indices.dtype) - - # For each query vec, we allow it to attend to those keys that are the - # W closest to its centroid, where W is the attention window. - sparsity_indices = tf.einsum('BTNK, BNKW -> BTNW', nearest_one_hot, - closest_indices) - if p.causal_masking: - batch_size, q_length, num_heads = py_utils.GetShape(query, 3) - query_positions = tf.range(q_length) + query_relative_position_shift - # [B, T, N, W] where the T dimension is range(T) - query_positions = tf.tile(query_positions[None, :, None, None], - [batch_size, 1, num_heads, p.attention_window]) - masked_indices = -tf.ones_like(sparsity_indices) - # Replace key positions in the future with -1 to indicate masking. - # - # Note that this is done after selecting top_k from 'k_dists', so for - # example if all the closest keys are in the future, we waste - # p.attention_window on padded keys when in theory we could have attended - # to further away keys that are not in the future (in order to achieve - # that we need to pick top_k from 'k_dists' differently for each query). - sparsity_indices = tf.where( - tf.math.greater(sparsity_indices, query_positions), masked_indices, - sparsity_indices) - - return attention_util.ComputeSparseAttention(query, key, value, - sparsity_indices, key_paddings) - - def _DotAttenFastPath(self, theta, query, key, value, q_dists, k_dists, - query_paddings, key_paddings): - """Computes the attention via the fast path. - - This implementation compute groups of queries, and for each group, - selects a set of p.attention_window number of keys/values that each - query in that group all attend to. - - There is no guarantee a query uniquely belong to a single group, although - via clustering this should likely be the case. When a query belong to - multiple groups, the attention is averaged post softmax; when a query - does not belong to any group, the attention result is zero. - - Args: - theta: A `.NestedMap` of the values of this layer's weights. - query: [B, T, N, H], already normalized. - key: [B, S, N, H], already normalized. - value: [B, S, N, H]. - q_dists: [B, T, N, K]. - k_dists: [B, S, N, K]. - query_paddings: [B, T]. - key_paddings: [B, S]. - - Returns: - encoded: [B, T, N, H]. - atten_probs: [B, T, N, S]. Note, N * S * T space complexity here. - """ - p = self.params - # [B, N, K, S] - # If key is padded in a position, 'k_dists' is inf which ensures - # that we consider all non-padded keys even if some padded keys - # might appear closer. - k_dists = tf.transpose(k_dists, [0, 2, 3, 1]) - - # [B, N, K, W], for each centroid, the indices of closest key vecs. - # closest_k may include padded positions. - _, closest_k = tf.math.top_k(-k_dists, p.attention_window) - - q_length = py_utils.GetShape(query, 2)[1] - k_length = py_utils.GetShape(key, 2)[1] - assert isinstance(q_length, int) - assert isinstance(k_length, int) - q_cluster_size = int(p.query_group_size_factor * q_length / p.num_clusters) - # Of shape [B, N, K, T] - q_dists = tf.transpose(q_dists, [0, 2, 3, 1]) - # closest_q of shape [B, N, K, V], where V = q_cluster_size - # closest_q may include padded positions. - _, closest_q = tf.math.top_k(-q_dists, q_cluster_size) - - def gather(v, indx): - """Gathers values from v. - - Args: - v: A tensor of shape [B, T, N, D] - indx: A tensor of shape [B, N, K, W] - - Returns: - A value of shape [B, N, K, W, D] - """ - # pylint: disable=invalid-name - B, _, N, _ = py_utils.GetShape(v, 4) - _, _, K, W = py_utils.GetShape(indx, 4) - # pylint: enable=invalid-name - batch_idx = tf.range(B)[:, None, None, None, None] - batch_idx = tf.tile(batch_idx, [1, N, K, W, 1]) - seq_idx = indx[:, :, :, :, None] - head_idx = tf.range(N)[None, :, None, None, None] - head_idx = tf.tile(head_idx, [B, 1, K, W, 1]) - gather_idx = tf.concat([batch_idx, seq_idx, head_idx], 4) - return tf.gather_nd(v, gather_idx) - - # c_ shorts for clustered. - _, _, num_heads, dim_per_head = py_utils.GetShape(query, 4) - # of shape [B, N, K, V, D] - c_query = gather(query, closest_q) - # of shape [B, N, K, W, D] - c_key, c_value = tf.split( - gather(tf.concat([key, value], -1), closest_k), 2, -1) - # of shape [B, N, K, W, 1] - c_key_paddings = gather( - # [B, T, N, 1] - tf.tile(key_paddings[:, :, None, None], [1, 1, num_heads, 1]), - closest_k) - # of shape [B, N, K, V, W] - is_key_padded = tf.tile( - tf.transpose(c_key_paddings, [0, 1, 2, 4, 3]), - [1, 1, 1, q_cluster_size, 1]) > 0.5 - if p.causal_masking: - # both position matrices of shape [B, N, K, V, W] - c_query_positions = tf.tile(closest_q[:, :, :, :, None], - [1, 1, 1, 1, p.attention_window]) - c_key_positions = tf.tile(closest_k[:, :, :, None, :], - [1, 1, 1, q_cluster_size, 1]) - # We pad the logit for future key positions relative to each query - is_key_padded = tf.math.logical_or( - is_key_padded, tf.math.greater(c_key_positions, c_query_positions)) - - logits = tf.einsum('BNKVD,BNKWD->BNKVW', c_query, c_key) - logits *= tf.math.rsqrt(tf.cast(dim_per_head, p.dtype)) - - very_negative_logits = ( - tf.ones_like(logits) * logits.dtype.max * - tf.constant(-0.7, dtype=logits.dtype)) - padded_logits = tf.where(is_key_padded, very_negative_logits, logits) - - c_atten_probs = tf.nn.softmax(padded_logits) - c_outputs = tf.einsum('BNKWD,BNKVW->BNKVD', c_value, c_atten_probs) - - def scatter(v, indx, seq_len): - """Scatters v according to indx. - - Args: - v: A tensor of shape [B, N, K, V, D]. - indx: A tensor of shape [B, N, K, V]. - seq_len: sequence length of the output. - - Returns: - output: A tensor of shape [B, T, N, D], where T = seq_len. - """ - # Need to scatter outputs back to the original shape. - # pylint: disable=invalid-name - B, N, K, V, D = py_utils.GetShape(v, 5) - # pylint: enable=invalid-name - # [B, N, K, V, 1] - batch_idx = tf.tile( - tf.range(B)[:, None, None, None, None], [1, N, K, V, 1]) - # [B, N, K, V, 1] - seq_idx = indx[:, :, :, :, None] - # [B, N, K, V, 1] - head_idx = tf.tile( - tf.range(N)[None, :, None, None, None], [B, 1, K, V, 1]) - scatter_idx = tf.concat([batch_idx, seq_idx, head_idx], 4) - scattered = tf.scatter_nd( - scatter_idx, tf.concat([v, tf.ones_like(v[:, :, :, :, :1])], -1), - [B, seq_len, N, D + 1]) - # We need to normaliz as one query vector may appear in multiple clusters. - scattered, den = tf.split(scattered, [v.shape.as_list()[-1], 1], -1) - # den = tf.squeeze(den, -1) - out = scattered / tf.maximum(tf.constant(1.0, dtype=den.dtype), - den) # [:, :, :, None]) - return out - - def scatter_atten_prob(c_atten_probs, closest_k, closest_q, k_length, - q_length): - """Scatters c_atten_probs. - - Args: - c_atten_probs: A tensor of shape [B, N, K, V, W]. - closest_k: A tensor of shape [B, N, K, W]. - closest_q: A tensor of shape [B, N, K, V]. - k_length: Length of the key vectors. - q_length: Length of the query vectors. - - Returns: - output: A tensor of shape [B, q_length, N, k_length]. - """ - # Need to scatter outputs back to the original shape. - # pylint: disable=invalid-name - B, N, K, V, W = py_utils.GetShape(c_atten_probs, 5) - # pylint: enable=invalid-name - # [B, N, K, V, W, 1] - batch_idx = tf.tile( - tf.range(B)[:, None, None, None, None, None], [1, N, K, V, W, 1]) - # [B, N, K, V, W, 1] - k_idx = tf.tile(closest_k[:, :, :, None, :, None], [1, 1, 1, V, 1, 1]) - q_idx = tf.tile(closest_q[:, :, :, :, None, None], [1, 1, 1, 1, W, 1]) - head_idx = tf.tile( - tf.range(N)[None, :, None, None, None, None], [B, 1, K, V, W, 1]) - scatter_idx = tf.concat([batch_idx, q_idx, head_idx, k_idx], 5) - scattered_prob = tf.scatter_nd(scatter_idx, c_atten_probs, - [B, q_length, N, k_length]) - - # We need to normalize the attention prob as one query vector may appear - # in multiple clusters. - # [B, N, K, V, 3] - times_idx = tf.concat([batch_idx, q_idx, head_idx], 5)[:, :, :, :, 0, :] - # [B, q_length, N] - times = tf.scatter_nd( - times_idx, tf.cast(tf.ones_like(closest_q), scattered_prob.dtype), - [B, q_length, N]) - times = tf.maximum(1.0, times[:, :, :, None]) - out = scattered_prob / times - return out - - out = scatter(c_outputs, closest_q, q_length) - out_prob = scatter_atten_prob(c_atten_probs, closest_k, closest_q, k_length, - q_length) - return out, out_prob - - -class MultiSourceAttention(base_layer.BaseLayer): - """Batch major attention with multiple source sub-attentions. - - It attends to multiple sources and uses one query as input to generates a - combined attention context. The dimension of the combined context vector is a - sum of all source context vectors. Each source attention has its separate - params and is associated with a source key. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('source_atten_tpls', None, - 'A list of (source_key, attention_param) pairs.') - p.Define('input_dim', 0, 'Default key dimension.') - p.Define('hidden_dim', 0, 'Default hidden dimension.') - p.Define( - 'primary_source_key', 'source_0', 'Key for the primary source ' - 'whose attention probabilities will be used as an output.') - p.Define('atten_merger_tpl', None, - 'Params to specify how to merge source attention vectors.') - return p - - def __init__(self, params): - """Constructs an MultiSourceAttention object.""" - super().__init__(params) - p = self.params - assert p.primary_source_key in [ - x for x, _ in p.source_atten_tpls - ], ('Source attention must have the primary source key.') - for source_key, atten_p in p.source_atten_tpls: - child_p = atten_p.Copy() - if child_p.hidden_dim <= 0: - child_p.hidden_dim = p.hidden_dim - if child_p.input_dim <= 0: - child_p.input_dim = p.input_dim - self.CreateChild('atten_%s' % source_key, child_p) - - # Initialize source context vector merging layer. - merger_p = p.atten_merger_tpl.Copy() - merger_p.name = 'atten_merger' - merger_p.source_dim = p.input_dim - merger_p.query_dim = p.input_dim - self.CreateChild('atten_merger', merger_p) - - def FProp(self, - theta, - query_vec, - key_vec, - value_vec, - paddings, - segment_mask=None, - per_step_padding=None): - p = self.params - with tf.name_scope(self.params.name): - result_map = py_utils.NestedMap() - for source_key, _ in p.source_atten_tpls: - result_map[source_key] = ( - self.children['atten_%s' % source_key].FProp( - theta.get('atten_%s' % source_key), query_vec, - key_vec[source_key], value_vec[source_key], - paddings[source_key], - segment_mask[source_key] if segment_mask else None, - per_step_padding)) - return self._CombineContext(theta, result_map, query_vec) - - def _CombineContext(self, theta, enc_map, query_vec): - encs = enc_map.Flatten() - combined_enc = ( - self.atten_merger.FProp(theta.atten_merger, [enc for enc, _ in encs], - query_vec)) - # Return atten_probs of the primary source. - return combined_enc, enc_map[self.params.primary_source_key][1] - - def AttenProbs(self, - theta, - query, - key, - paddings, - segment_mask, - per_step_padding=None): - primary_source_key = self.params.primary_source_key - child_name = 'atten_%s' % primary_source_key - return self.children[child_name].AttenProbs( - theta.get(child_name), query, key[primary_source_key], - paddings[primary_source_key], - segment_mask[primary_source_key] if segment_mask else None, - per_step_padding) - - -class TransformerAttentionLayer(base_layer.BaseLayer): - """Multiheaded attention sub-layer in Transformer layer. - - Input is first normalized using Layer Normalization. Output of layer - normalization is processed using multi-headed attention. And finally, the - output of the attention layer is combined with the residual connection. - - This layer will be used in the following two scenarios: - - 1. Multi-Headed Self-Attention, where attention keys, values (source_vecs) and - queries come from the same previous layer output. - 2. Masked Multi-Headed Self-Attention, where attention keys, values and - queries all come from the same previous layer output, but rightward - activations are masked to prevent information flow from future. This is the - use case for Transformer decoder self-attention layers. Can be activated by - setting is_masked flag of this layer. - 3. Multi-Headed Cross-Attention, where attention keys and values - (source_vecs) are coming from a different source (output of the encoder), - and queries coming from the previous layer outputs (decoder). - - We use the same capital letters to denote certain tensor parameters as - MultiHeadedAttention class. - - B = batch size - S = length of the key/value (source) - T = length of the query (target) - D = model dimension - N = number of attention heads - H = dimensions of each attention head. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the transformer block input.') - p.Define('hidden_dim', 0, 'Dimension of the attention hidden dim.') - p.Define('num_heads', 8, 'Number of attention heads.') - p.Define( - 'is_masked', False, - 'If set, uses causal non local multiheaded attention.' - 'This option is not valid when atten_tpl is LocalSelfAttention ' - 'or its subclass(es).') - p.Define( - 'atten_dropout_prob', 0.0, - 'Probability at which we apply dropout to the attention probs. ' - 'This practically drops memory values at random positions.') - p.Define( - 'residual_dropout_prob', 0.0, - 'Probability at which we apply dropout to the residual layers, ' - 'such that, residual(x, y) = (x + dropout(y)).') - p.Define('add_unnormalized_input', True, - 'If set, uses unnormalized input in the residual add.') - p.Define('add_skip_connection', True, - 'If True, add input (or normalized input) to the output.') - p.Define('ln_tpl', layers.LayerNorm.Params(), - 'Layer norm default params. No layernorm if set to None.') - p.Define('atten_tpl', - MultiHeadedAttention.Params().Set(), - 'Multi-Headed Dot-Product Attention default params') - p.Define( - 'dropout_tpl', layers.DropoutLayer.Params(), - 'Residual dropout params template. keep_prop will be reset to ' - '(1.0 - residual_dropout_prob).') - return p - - @classmethod - def CommonParams(cls, - input_dim, - num_heads, - is_masked=False, - use_relative_atten=False, - relative_pos_emb_dim=None, - local_context=None, - left_context=None, - right_context=None, - dropout_prob=0.): - # pylint: disable=g-doc-args - """Returns a hyperparam for the most representative cases. - - CommonParams is not expected to be extended to an omnipotent/generic builder - method. Specific use cases should take the return value of it and apply - further customization. It should be kept lean and only extended cautiously - for very common cases. - """ - # pylint: enable=g-doc-args - if not use_relative_atten: - assert not relative_pos_emb_dim - else: - relative_pos_emb_dim = relative_pos_emb_dim or input_dim - - if local_context: - assert not left_context and not right_context, ( - 'local_context and (left_context, right_context) can not be set ' - 'at the same time.') - left_context = local_context + 1 # include 'self' position. - right_context = local_context - - p = cls.Params().Set( - input_dim=input_dim, - num_heads=num_heads, - is_masked=is_masked, - atten_dropout_prob=dropout_prob, - residual_dropout_prob=dropout_prob) - - is_local = left_context or right_context - if is_local: - atten_cls = ( - LocalSelfAttentionXL if use_relative_atten else LocalSelfAttention) - else: - atten_cls = ( - MultiHeadedAttentionXL - if use_relative_atten else MultiHeadedAttention) - p.atten_tpl = atten_cls.Params() - if use_relative_atten: - p.atten_tpl.rel_pos_emb_dim = relative_pos_emb_dim - if is_local: - p.atten_tpl.Set(left_context=left_context, right_context=right_context) - return p - - def _InitAttentionParams(self, atten_tpl): - """Returns an initialized transformer attention parameters.""" - p = self.params - params = atten_tpl.Copy() - params.name = 'multihead_atten' - params.input_dim = p.input_dim - params.hidden_dim = p.hidden_dim - params.num_heads = p.num_heads - params.atten_dropout_prob = p.atten_dropout_prob - return params - - def __init__(self, params): - super().__init__(params) - p = self.params - - if not p.hidden_dim: - p.hidden_dim = p.input_dim - - # Initialize attention. - params = self._InitAttentionParams(p.atten_tpl) - if p.is_masked and issubclass(params.cls, LocalSelfAttention): - tf.logging.warn('\'is_masked\' is not effective when used with ' - 'LocalSelfAttention and its subclass(es).') - self.CreateChild('atten', params) - - # Initialize attention layer normalization. - if p.ln_tpl: - params = p.ln_tpl.Copy() - params.name = 'atten_ln' - params.input_dim = p.input_dim - self.CreateChild('layer_norm', params) - - # Initialize residual dropout. - dropout_tpl = p.dropout_tpl.Copy() - dropout_tpl.keep_prob = (1.0 - p.residual_dropout_prob) - self.CreateChild('residual_dropout', dropout_tpl) - - def FProp(self, - theta, - query_vec, - source_vecs, - paddings, - per_step_padding_override=None, - segment_mask=None): - """Compute the result of Transformer attention layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [B, T, D]. - source_vecs: [B, S, D] (cross_attention) or None (self-attention). - paddings: [B, S]. - per_step_padding_override: [B, T, T] for self attention or [B, T, S] for - cross attention. - segment_mask: [B, 1, T, S]. - - Returns: - output: [B, T, D]. - atten_probs: [B, N, T, S]. - """ - p = self.params - b, t, _ = py_utils.GetShape(query_vec, 3) - unnormalized_query_vec = query_vec - - # Layer normalization. - if p.ln_tpl: - query_vec = self.layer_norm.FProp(theta.layer_norm, query_vec) - - # For self-attention: keys = queries. - if source_vecs is None: - source_vecs = query_vec - - # Generates mask, with shape [b, t, t]. - if per_step_padding_override is None: - if p.is_masked and segment_mask is None: - # causal padding. - per_step_padding = tf.tile( - tf.expand_dims(CausalPadding(t, dtype=query_vec.dtype), 0), - [b, 1, 1]) - else: - per_step_padding = None - else: - per_step_padding = per_step_padding_override - - # Multiheaded attention. - with tf.name_scope('atten'): - ctx_vec, atten_probs = self.atten.FProp( - theta.atten, - query_vec, # query - source_vecs, # key - source_vecs, # value - paddings, - segment_mask=segment_mask, - per_step_padding=per_step_padding) - - # Residual connection. - ctx_vec = self.residual_dropout.FProp(theta.residual_dropout, ctx_vec) - input_to_add = ( - unnormalized_query_vec if p.add_unnormalized_input else query_vec) - if p.add_skip_connection: - ctx_vec += input_to_add - return ctx_vec, atten_probs - - def InitStates(self, theta, target_batch_size, target_max_length): - return self.atten.InitStates(theta.atten, target_batch_size, - target_max_length) - - def ExtendStep(self, - theta, - query_vec, - cached_states, - time_step, - use_short_seq_opt=False): - """Compute the result and update cached states for the current step. - - This function is used by autoregressive decoding. This function knows the - length of full sequence, thus it is different from StreamingExtendStep. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [B, 1, D] - cached_states: A `.NestedMap` object containing tensors which are the - results of previous attentions, used for fast decoding. key - [T, B, - N, H]. value - [T, B, N, H]. - time_step: A scalar or tensor with [B], current decode step, 0-based. - if it's a scalar, all the time step are the same decode step. - if it's a tensor, it represents current decode step for each sample. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - cur_output: [B, 1, D] - updated_states: A `.NestedMap` object containing the updated states. - key - [T, B, N, H]. - value - [T, B, N, H]. - - Raises: - ValueError: If not used as masked/causal self-attention. - """ - p = self.params - if not p.is_masked: - raise ValueError( - 'ExtendStep should be used only by masked/causal self-attention.') - - t, b, _, _ = py_utils.GetShape(cached_states.key, 4) - unnormalized_query_vec = query_vec - time_step = tf.convert_to_tensor(time_step) - - if time_step.shape.ndims == 0: - batch_time_step = tf.tile(tf.reshape(time_step, [-1]), [b]) - else: - batch_time_step = time_step - - # Generates mask, with shape [b, 1, t]. - zero_padding = tf.zeros([b, t], dtype=query_vec.dtype) - # [b, t] - per_step_padding = tf.where( - tf.less( - tf.tile(tf.expand_dims(tf.range(t), 0), [b, 1]), - tf.expand_dims(batch_time_step + 1, -1)), zero_padding, - tf.ones_like(zero_padding, dtype=query_vec.dtype)) - # [b, 1, t] - per_step_padding = tf.expand_dims(per_step_padding, 1) - - # Layer normalization. - if p.ln_tpl: - query_vec = self.layer_norm.FProp(theta.layer_norm, query_vec) - - # Multiheaded masked/causal self-attention. - ctx_vec, updated_states = self.atten.ExtendStep(theta.atten, query_vec, - cached_states, None, None, - per_step_padding, time_step, - use_short_seq_opt) - - # Residual connection. - ctx_vec = self.residual_dropout.FProp(theta.residual_dropout, ctx_vec) - input_to_add = ( - unnormalized_query_vec if p.add_unnormalized_input else query_vec) - if p.add_skip_connection: - ctx_vec += input_to_add - return ctx_vec, updated_states - - def zero_state(self, batch_size=1): - """Returns the initial state given the batch size. - - Args: - batch_size: the batch size. - - Returns: - state: The initial state for streaming inference. - """ - return self.atten.zero_state(batch_size) - - def StreamingExtendStep(self, query_vec, state, time_step): - """Computes the value vector given the query of the current step. - - Args: - query_vec: A query vector of shape [B, 1, D]. - state: A `.NestedMap` object containing tensors {key, value} which are - results of previous attentions. key, value are of shape [T, B, N, H] - where T is the context size of this layer. - time_step: A tensor of shape [1] and type tf.int32. Note, we can not use - scalar tensor here because TfLiteConverter doesn't have good support of - it (b/138865275). - - Returns: - output: Output of the given query vector with shape [B, 1, D]. - state: updated state. - """ - assert isinstance(self.atten, LocalSelfAttention) or isinstance( - self.atten, LocalSelfAttentionXL) - - p = self.params - unnormalized_query_vec = query_vec - if p.ln_tpl: - query_vec = self.layer_norm.FProp(self.theta.layer_norm, query_vec) - output, state = self.atten.StreamingExtendStep(query_vec, state, time_step) - - # Residual connection. - input_to_add = ( - unnormalized_query_vec if p.add_unnormalized_input else query_vec) - if p.add_skip_connection: - output += input_to_add - return output, state - - -class TransformerMultiSourceAttentionLayer(TransformerAttentionLayer): - """Batch major multi-source multi-headed attention. - - Only supports scenarios 3 described by comments on TransformerAttentionLayer: - - 3. Multi-source multi-headed cross-attention, where attention keys and values - (source_vecs) are coming from different sources (one of them is usually - the outputs of the encoder), and queries coming from the previous layer - outputs (decoder). Specifically, attention keys and values are NestedMaps - containing encodings of different sources. This corresponds to a - multi-source decoder-to-encoder attention mechanism, i.e., decoder attends - to encoder outputs and other sources. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_source', 0, 'Number of sources to attend to.') - p.Define( - 'primary_source_index', 0, 'Index of the primary source whose ' - 'attention probs will be returned.') - p.Define('multi_source_atten', MultiSourceAttention.Params(), - 'Multi-source attention params.') - # Only used for case 3. - p.is_masked = False - return p - - def _InitAttentionParams(self, atten_tpl): - """Returns an initialized multi-source transformer attention parameters.""" - p = self.params - source_atten_tpls = [] - # Set up each source attention. - for i in range(p.num_source): - src_key = 'source_%d' % i - src_atten = atten_tpl.Copy() - src_atten = super()._InitAttentionParams(src_atten) - src_atten.name = 'multihead_atten_%s' % src_key - source_atten_tpls.append((src_key, src_atten)) - - # Initialize multi-source attention. - msa = p.multi_source_atten.Copy() - msa.name = 'multi_source_atten' - msa.input_dim = p.input_dim - msa.hidden_dim = p.hidden_dim - msa.source_atten_tpls = source_atten_tpls - msa.primary_source_key = 'source_%d' % p.primary_source_index - return msa - - -class TransformerLayer(base_layer.BaseLayer): - """Transformer layer with multiheaded attention. - - Applies self-attention followed by a cross-attention and feed forward layer. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the transformer block input.') - p.Define('output_dim', 0, 'Dimension of the transformer block output.') - p.Define('num_heads', None, 'Num of heads in self attention.') - p.Define('has_aux_atten', False, - 'If set, introduces a second attention layer') - p.Define('mask_self_atten', False, 'If True, use masked self-attention.') - p.Define('tr_atten_tpl', - TransformerAttentionLayer.Params().Set(), - 'Transformer Attention Layer params.') - p.Define( - 'tr_self_atten_tpl', None, - 'Attention template for self attention. If unset, use tr_atten_tpl.') - p.Define( - 'tr_fflayer_tpl', - layers_with_attention.TransformerFeedForwardLayer.Params().Set( - hidden_dim=2048), 'Transformer Feed-Forward Layer params.') - p.Define('packed_input', False, - 'If True, each training example may pack multiple sequences.') - return p - - @classmethod - def CommonParams(cls, - input_dim, - atten_num_heads, - atten_is_relative=False, - atten_local_context=None, - atten_left_context=None, - atten_right_context=None, - has_aux_atten=False, - mask_self_atten=False, - fflayer_hidden_dim=None, - fflayer_output_dim=None, - dropout_prob=0.): - # pylint: disable=g-doc-args - """Returns a hyperparam for the most representative cases. - - CommonParams is not expected to be extended to an omnipotent/generic builder - method. Specific use cases should take the return value of it and apply - further customization. It should be kept lean and only extended cautiously - for very common cases. - """ - # pylint: enable=g-doc-args - output_dim = fflayer_output_dim or input_dim - fflayer_hidden_dim = fflayer_hidden_dim or 4 * input_dim - # TODO(jamesqin): check how mask_self_atten work with local atten. - p = cls.Params().Set( - name='transformer_layer', - input_dim=input_dim, - output_dim=output_dim, - num_heads=atten_num_heads, - has_aux_atten=has_aux_atten, - mask_self_atten=mask_self_atten) - p.tr_self_atten_tpl = TransformerAttentionLayer.CommonParams( - input_dim, - atten_num_heads, - is_masked=mask_self_atten, - local_context=atten_local_context, - left_context=atten_left_context, - right_context=atten_right_context, - dropout_prob=dropout_prob) - p.tr_fflayer_tpl.Set( - hidden_dim=fflayer_hidden_dim, - residual_dropout_prob=dropout_prob, - relu_dropout_prob=dropout_prob) - return p - - @classmethod - def SetNumInputNodes(cls, p, num_input_nodes): - p.input_dim = num_input_nodes - - @classmethod - def NumOutputNodes(cls, p): - return p.output_dim - - def __init__(self, params): - super().__init__(params) - p = self.params - - # Initialize masked multi-headed self-attention - if p.tr_self_atten_tpl is not None: - self_atten_tpl = p.tr_self_atten_tpl - else: - self_atten_tpl = p.tr_atten_tpl - params = self_atten_tpl.Copy() - params.name = 'multihead_self_atten' - params.input_dim = p.input_dim - params.is_masked = p.mask_self_atten - if p.num_heads: - params.num_heads = p.num_heads - params.atten_tpl.packed_input = p.packed_input - self.CreateChild('self_atten', params) - - if p.has_aux_atten: - # Initialize multi-headed cross-attention - params = p.tr_atten_tpl.Copy() - params.name = 'multihead_cross_atten' - params.input_dim = p.input_dim - if p.num_heads: - params.num_heads = p.num_heads - params.atten_tpl.packed_input = p.packed_input - self.CreateChild('cross_atten', params) - - # Initialize feed-forward layer - params = p.tr_fflayer_tpl.Copy() - params.name = 'tr_fflayer' - params.input_dim = p.input_dim - params.output_dim = p.output_dim - self.CreateChild('fflayer', params) - - def _GetSourceBatchSize(self, aux_vec): - return py_utils.GetShape(aux_vec, 2)[0] - - def _GetSourceLength(self, aux_vec): - return py_utils.GetShape(aux_vec, 2)[1] - - def FProp(self, - theta, - query_vec, - paddings, - aux_vec=None, - aux_paddings=None, - per_step_padding_override=None, - segment_mask=None, - aux_segment_mask=None): - """Transformer decoder layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, target_time, dim]. - paddings: [target_batch, target_time]. - aux_vec: [source_batch, source_time, dim]. - aux_paddings: [source_batch, source_time]. - per_step_padding_override: [target_batch, target_time, target_time]. - segment_mask: [target_batch, 1, target_time, target_time] - aux_segment_mask: [source_batch, 1, target_time, source_time] - - target_batch can be a multiple of source_batch, where samples in - target_batch are arranged in the order of [m, source_batch] where m = - target_batch / source_batch. - - Returns: - The fflayer output with shape [target_batch, target_time, dim]. - atten_probs: [B, N, T, S]. - """ - p = self.params - # First the self-attention layer. - if p.packed_input: - assert aux_segment_mask is not None, ('Need to specify aux_segment_mask ' - 'for packed input.') - with tf.name_scope('self_atten'): - atten_vec, atten_probs = self.self_atten.FProp( - theta.self_atten, - query_vec, - None, - paddings, - segment_mask=segment_mask, - per_step_padding_override=per_step_padding_override) - - if p.has_aux_atten: - with tf.name_scope('aux_atten'): - # Next the cross-attention layer. - target_batch, target_time, dim = py_utils.GetShape(query_vec, 3) - - source_batch = self._GetSourceBatchSize(aux_vec) - source_time = self._GetSourceLength(aux_vec) - - atten_vec = tf.reshape(atten_vec, [-1, source_batch, target_time, dim]) - atten_vec = tf.reshape( - tf.transpose(atten_vec, [1, 0, 2, 3]), [source_batch, -1, dim]) - atten_vec, atten_probs = self.cross_atten.FProp( - theta.cross_atten, - atten_vec, - aux_vec, - aux_paddings, - segment_mask=aux_segment_mask) - num_heads = py_utils.GetShape(atten_probs)[1] - atten_probs = tf.reshape( - atten_probs, - [source_batch, -1, num_heads, target_time, source_time]) - atten_probs = tf.transpose(atten_probs, [1, 0, 2, 3, 4]) - atten_probs = tf.reshape( - atten_probs, [target_batch, num_heads, target_time, source_time]) - atten_vec = tf.reshape(atten_vec, [source_batch, -1, target_time, dim]) - atten_vec = tf.transpose(atten_vec, [1, 0, 2, 3]) - atten_vec = tf.reshape(atten_vec, [target_batch, target_time, dim]) - - # Finally the feed-forward layer. - with tf.name_scope('fflayer'): - return self.fflayer.FProp(theta.fflayer, atten_vec, paddings), atten_probs - - def InitStates(self, theta, target_batch_size, target_max_length): - return self.self_atten.InitStates(theta.self_atten, target_batch_size, - target_max_length) - - def ExtendStep(self, - theta, - query_vec, - aux_vec, - aux_paddings, - cached_states, - time_step, - use_short_seq_opt=False): - """Transformer decoder layer, extend one step in autoregressive decoding. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, 1, dim]. - aux_vec: [source_batch, source_time, dim] - aux_paddings: [source_batch, source_time] - cached_states: A `.NestedMap` object containing tensors which are the - results of previous attentions, used for fast decoding. key - - [target_time, target_batch, num_heads, dim_per_head]. value - - [target_time, target_batch, num_heads, dim_per_head]. - time_step: A scalar, the current decode step, 0-based. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - cur_output: [target_batch, 1, dim] - updated_states: A `.NestedMap` object containing the updated states. - key - [target_time, target_batch, num_heads, dim_per_head]. - value - [target_time, target_batch, num_heads, dim_per_head]. - """ - target_batch, _, dim = py_utils.GetShape(query_vec, 3) - - # First the self-attention layer. - atten_vec, updated_states = self.self_atten.ExtendStep( - theta.self_atten, query_vec, cached_states, time_step, - use_short_seq_opt) - if self.params.has_aux_atten: - source_batch = self._GetSourceBatchSize(aux_vec) - # Next the cross-attention layer. - atten_vec = tf.reshape(atten_vec, [source_batch, -1, dim]) - atten_vec, _ = self.cross_atten.FProp(theta.cross_atten, atten_vec, - aux_vec, aux_paddings) - atten_vec = tf.reshape(atten_vec, [target_batch, 1, -1]) - - # Finally the feed-forward layer. - cur_output = self.fflayer.FProp( - theta.fflayer, atten_vec, - tf.zeros([target_batch, 1], dtype=atten_vec.dtype)) - return cur_output, updated_states - - -class MultiSourceTransformerLayer(TransformerLayer): - """Multi-source transformer layer with multiheaded attention. - - Multi-source attention is used for cross attention. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_source', 0, 'Number of sources to attend to.') - p.Define( - 'primary_source_index', 0, 'Index for the primary source ' - 'whose attention probabilities will be used as an output.') - return p - - def __init__(self, params): - assert issubclass(params.tr_atten_tpl.cls, - TransformerMultiSourceAttentionLayer) - # Set up multi-source attention layer - cross_atten_p = params.tr_atten_tpl - cross_atten_p.num_source = params.num_source - cross_atten_p.primary_source_index = params.primary_source_index - assert params.tr_self_atten_tpl - super().__init__(params) - - @property - def primary_source_key(self): - return 'source_%d' % self.params.primary_source_index - - def _GetSourceBatchSize(self, aux_vec): - return py_utils.GetShape(aux_vec[self.primary_source_key], 2)[0] - - def _GetSourceLength(self, aux_vec): - return py_utils.GetShape(aux_vec[self.primary_source_key], 2)[1] - - -# mt_attention_layer.MultiHeadedAttentionXL -ATTEN_TRANSFORMER_XL = 'transformer_xl' -# mt_attention_layer.MultiHeadedAttentionRPE -ATTEN_RPE = 'rpe' - - -def UseRelativeAttentionInTransformerLayer(transformer_params, - rel_pos_emb_dim, - atten_type=ATTEN_TRANSFORMER_XL): - """Uses transformer-xl attention for self attention of a transformer layer. - - Args: - transformer_params: A mt_attention_layer.TransformerLayer.Params() object. - rel_pos_emb_dim: (int) Relative positional embedding dim to be set. - atten_type: (string) Attention type. Supported: - - 'transformer_xl': mt_attention_layer.MultiHeadedAttentionXL - - 'rpe': mt_attention_layer.MultiHeadedAttentionRPE - - Returns: - A mt_attention_layer.TransformerLayer.Params() object with relative pos emb. - """ - if not issubclass(transformer_params.cls, TransformerLayer): - raise ValueError('Unsupported input transformer layer: %s' % - transformer_params.cls) - - if atten_type not in (ATTEN_TRANSFORMER_XL, ATTEN_RPE): - raise ValueError('Relative attention type: %s unsupported' % atten_type) - - # Gets multiheaded attention tpl from self attention config in transformer. - trans_params_copy = transformer_params.Copy() - if trans_params_copy.tr_self_atten_tpl is None: - trans_params_copy.tr_self_atten_tpl = trans_params_copy.tr_atten_tpl.Copy() - atten_tpl = trans_params_copy.tr_self_atten_tpl.atten_tpl - - # If already using relative attention class. - if atten_tpl.cls in (MultiHeadedAttentionRPE, MultiHeadedAttentionXL, - LocalSelfAttentionXL): - atten_tpl.rel_pos_emb_dim = rel_pos_emb_dim - return trans_params_copy - - if atten_type == ATTEN_TRANSFORMER_XL: - if atten_tpl.cls == MultiHeadedAttention: - rel_atten_tpl = MultiHeadedAttentionXL.Params() - elif atten_tpl.cls == LocalSelfAttention: - rel_atten_tpl = (LocalSelfAttentionXL.Params()) - else: - raise ValueError('Unsupported attention: %s' % atten_tpl.cls) - elif atten_type == ATTEN_RPE: - rel_atten_tpl = MultiHeadedAttentionRPE.Params() - - rel_atten_tpl = hyperparams.CopyFieldsTo(atten_tpl, rel_atten_tpl) - rel_atten_tpl.rel_pos_emb_dim = rel_pos_emb_dim - - trans_params_copy.tr_self_atten_tpl.atten_tpl = rel_atten_tpl - return trans_params_copy - - -def ClearRelativeAttentionInTransformerLayer(transformer_params): - """Removes relative position attention in the transformer layer. - - Args: - transformer_params: A mt_attention_layer.TransformerLayer param. - - Returns: - A mt_attention_layer.TransformerLayer param without relative attention. - """ - if not issubclass(transformer_params.cls, TransformerLayer): - raise ValueError('Unsupported input transformer layer: %s' % - transformer_params.cls) - trans_params_copy = transformer_params.Copy() - if trans_params_copy.tr_self_atten_tpl is None: - trans_params_copy.tr_self_atten_tpl = trans_params_copy.tr_atten_tpl.Copy() - attention_tpl = trans_params_copy.tr_self_atten_tpl.atten_tpl - if attention_tpl.cls == MultiHeadedAttentionXL: - new_attention_tpl = MultiHeadedAttention.Params() - elif attention_tpl.cls == (LocalSelfAttentionXL): - new_attention_tpl = LocalSelfAttention.Params() - else: - raise ValueError('Unsupported attention params: %s' % attention_tpl.cls) - - new_attention_tpl = hyperparams.CopyFieldsTo( - attention_tpl, new_attention_tpl, skip=['rel_pos_emb_dim', 'skip_term_b']) - trans_params_copy.tr_self_atten_tpl.atten_tpl = new_attention_tpl - return trans_params_copy - - -class TransformerDecoderLayer(TransformerLayer): - """Transformer decoder layer with multiheaded attention.""" - - @classmethod - def Params(cls): - p = super().Params() - p.has_aux_atten = True - p.mask_self_atten = True - return p - - -class MultiSourceTransformerDecoderLayer(MultiSourceTransformerLayer): - """Multi-source transformer decoder layer with multiheaded attention.""" - - @classmethod - def Params(cls): - p = super().Params() - p.has_aux_atten = True - p.mask_self_atten = True - return p - - -class StackedTransformerLayers(base_layer.BaseLayer): - """A stack of Batch-Major Transformer layers.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('has_aux_atten', False, - 'If set, introduces a second attention layer') - p.Define('mask_self_atten', False, 'If True, use masked self-attention.') - p.Define('num_layers', 0, 'Num of layers in this stack.') - p.Define('mdl_dim', 0, 'Model dimension in Transformer layers.') - p.Define('hidden_dim', 0, - 'The hidden layer dimension in Transformer layers.') - p.Define('num_atten_heads', 0, 'Num of attention heads.') - p.Define('dropout_prob', 0.0, - 'Apply dropout at this prob at various places.') - p.Define('add_unnormalized_input', True, - 'If set, uses unnormalized input in the residual add.') - p.Define('transformer_layer_params_tpl', TransformerLayer.Params(), - 'A template of TransformerLayer.params.') - p.Define('final_layer_norm', False, - 'If true, apply layer normalization to the final output.') - p.Define('packed_input', False, - 'If True, each training example may pack multiple sequences.') - p.Define('use_fused_layernorm', False, 'Whether to use fused layernorm.') - p.Define( - 'splits', None, 'None or a list of layer indices. If None, all layers ' - 'are placed on the same and only one partition. Else, len(splits) is ' - 'the number of partitions the stack is sliced into. layer_i is placed ' - 'on the kth partition (0-based) where split[k] < i <= split[k+1].') - return p - - def __init__(self, params): - if not params.splits: - params.splits = [params.num_layers - 1] - else: - assert all(x <= params.num_layers - 1 for x in params.splits) - # Assert p.splits is strictly monotonically increasing. - assert sorted(list(set(params.splits))) == params.splits - super().__init__(params) - p = self.params - - assert p.num_layers > 0 - assert p.mdl_dim > 0 - assert p.hidden_dim > 0 - assert p.num_atten_heads > 0 - assert 0.0 <= p.dropout_prob < 1.0 - - def _LayerParams(ii): - """Construct ii-th layer params.""" - p_ii = p.transformer_layer_params_tpl.Copy() - p_ii.name = 'layer_%d' % ii - p_ii.has_aux_atten = p.has_aux_atten - p_ii.mask_self_atten = p.mask_self_atten - p_ii.input_dim = p.mdl_dim - p_ii.output_dim = p.mdl_dim - p_ii.packed_input = p.packed_input - p_ii.tr_atten_tpl.num_heads = p.num_atten_heads - p_ii.tr_atten_tpl.atten_dropout_prob = p.dropout_prob - p_ii.tr_atten_tpl.residual_dropout_prob = p.dropout_prob - p_ii.tr_atten_tpl.add_unnormalized_input = p.add_unnormalized_input - p_ii.tr_fflayer_tpl.hidden_dim = p.hidden_dim - p_ii.tr_fflayer_tpl.residual_dropout_prob = p.dropout_prob - p_ii.tr_fflayer_tpl.relu_dropout_prob = p.dropout_prob - return p_ii - - layer_params = [_LayerParams(ii) for ii in range(p.num_layers)] - - self.CreateChildren('x_layers', layer_params) - - if p.final_layer_norm: - final_ln_p = layers.LayerNorm.Params().Set( - input_dim=p.mdl_dim, use_fused_layernorm=p.use_fused_layernorm) - self.CreateChild('final_ln', final_ln_p) - - @classmethod - def GetSplitForLayer(cls, buckets, layer_index): - assert layer_index <= buckets[-1], ( - f'layer_index:{layer_index} > buckets[-1]:{buckets[-1]}') - # Return index of the smallest element greater than or equal to layer_index - return bisect.bisect_left(buckets, layer_index) - - def FProp(self, - theta, - query_vec, - paddings, - aux_vec=None, - aux_paddings=None, - segment_mask=None, - aux_segment_mask=None): - """Stacked Transformer layer. - - Args: - theta: A `NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [batch, target_time, dim]. - paddings: [batch, target_time]. - aux_vec: [batch, source_time, dim]. - aux_paddings: [batch, source_time]. - segment_mask: [batch, 1, target_time, target_time] - aux_segment_mask: [batch, 1, target_time, source_time] - - Returns: - (context, paddings), where the context vector has shape [batch, - target_time, dim]. - """ - p = self.params - x_out = query_vec - cluster = self.cluster - - with tf.name_scope(p.name): - for i in range(p.num_layers): - x_in = x_out - with tf.device( - cluster.WorkerDeviceInModelSplit( - self.GetSplitForLayer(self.params.splits, i))): - x_out, _ = self.x_layers[i].FProp(theta.x_layers[i], x_in, paddings, - aux_vec, aux_paddings, segment_mask, - aux_segment_mask) - if p.final_layer_norm: - # Place on the last device. - with tf.device( - cluster.WorkerDeviceInModelSplit( - self.GetSplitForLayer(self.params.splits, p.num_layers - 1))): - x_out = self.final_ln.FProp(theta.final_ln, x_out) - return x_out, paddings - - def InitStates(self, theta, *args, **kwargs): - return py_utils.NestedMap(x_layers=[ - layer.InitStates(layer_theta, *args, **kwargs) - for layer, layer_theta in zip(self.x_layers, theta.x_layers) - ]) - - def ExtendStep(self, - theta, - query_vec, - aux_vec, - aux_paddings, - cached_states, - time_step, - use_short_seq_opt=False): - """Transformer decoder layer, extend one step in autoregressive decoding. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, 1, dim]. - aux_vec: [source_batch, source_time, dim] - aux_paddings: [source_batch, source_time] - cached_states: A `.NestedMap` object containing tensors which are the - results of previous attentions, used for fast decoding. - cached_states.x_layers is a list corresponding to self.x_layers, where - each element is a NestedMap with attention keys and values: - "key" - [target_time, target_batch, num_heads, dim_per_head]. - "value" - [target_time, target_batch, num_heads, dim_per_head]. - time_step: A scalar, the current decode step, 0-based. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - cur_output: The last decoder layer output of shape [target_batch, 1, dim]. - updated_states: A `.NestedMap` object containing the updated states. - updated_states.x_layers is a list corresponding to self.x_layers, where - each element is a NestedMap with attention keys and values: - "key" - [target_time, target_batch, num_heads, dim_per_head]. - "value" - [target_time, target_batch, num_heads, dim_per_head]. - """ - p = self.params - with tf.name_scope(p.name): - updated_states = py_utils.NestedMap(x_layers=[]) - decoder_input = query_vec - for layer, layer_theta, layer_states in zip(self.x_layers, theta.x_layers, - cached_states.x_layers): - decoder_output, updated_layer_states = layer.ExtendStep( - layer_theta, decoder_input, aux_vec, aux_paddings, layer_states, - time_step, use_short_seq_opt) - updated_states.x_layers.append(updated_layer_states) - decoder_input = decoder_output - return decoder_output, updated_states - - -class TransformerFeedForwardLayerWithTaskId( - layers_with_attention.TransformerFeedForwardLayer): - """TransformerFeedForwardLayer with optional task_id input args.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('use_task_ids', False, - 'If set, introduces a second attention layer') - return p - - def FProp(self, theta, inputs, paddings, task_id=None): - """Feed-forward, residual and layer-norm. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: [batch, time, dim]. - paddings: [batch, time] - task_id: optional task_id with shape [batch] - - Returns: - tensor of the same shape with inputs - """ - p = self.params - if p.use_task_ids: - if task_id is None: - raise ValueError('Must pass task_id if use_task_ids.') - inputs_normalized = self.layer_norm.FProp(theta.layer_norm, inputs) - if hasattr(self, 'res_proj_layer'): - inputs = self.res_proj_layer.FProp(theta.res_proj_layer, inputs) - expanded_paddings = tf.expand_dims(paddings, -1) - fflayer_args = [inputs_normalized, expanded_paddings] - fflayer_args += [task_id] if p.use_task_ids else [] - h = inputs + self.residual_dropout.FProp( - theta.residual_dropout, self.fflayer.FProp(theta.fflayer, - *fflayer_args)) - return h - - -# TODO(ankurbpn,huangyp): Remove this layer. -class GPipeTransformerLayer(TransformerLayer): - """GPipe compatible transformer layer. - - DEPRECATED: This layer and its use in GPipeTransformerStack is - deprecated. Consider using the new GPipeBatchMajorTransformerStack instead. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.tr_fflayer_tpl = TransformerFeedForwardLayerWithTaskId.Params() - return p - - def FProp(self, - theta, - source_vecs, - source_paddings, - target_vecs, - target_paddings, - source_segment_id, - target_segment_id, - transparent_acc, - transparent_acc_helper, - source_task_id=None, - target_task_id=None): - p = self.params - with tf.name_scope(p.name): - if p.has_aux_atten: # Decoder FProp - seg_mask = SegmentMask(target_segment_id, target_segment_id) - aux_seg_mask = SegmentMask(target_segment_id, source_segment_id) - atten_vec, _ = self.self_atten.FProp( - theta.self_atten, - target_vecs, - None, - target_paddings, - segment_mask=seg_mask) - atten_vec, _ = self.cross_atten.FProp( - theta.cross_atten, - atten_vec, - source_vecs, - source_paddings, - segment_mask=aux_seg_mask) - atten_vec = self.fflayer.FProp(theta.fflayer, atten_vec, - target_paddings, target_task_id) - atten_vec.set_shape(target_vecs.shape) - return (source_vecs, source_paddings, atten_vec, target_paddings, - source_segment_id, target_segment_id, transparent_acc, - transparent_acc_helper, source_task_id, target_task_id) - # Encoder FProp - seg_mask = SegmentMask(source_segment_id, source_segment_id) - atten_vec, _ = self.self_atten.FProp( - theta.self_atten, - source_vecs, - None, - source_paddings, - segment_mask=seg_mask) - atten_vec = self.fflayer.FProp(theta.fflayer, atten_vec, source_paddings, - source_task_id) - atten_vec.set_shape(source_vecs.shape) - - return (atten_vec, source_paddings, target_vecs, target_paddings, - source_segment_id, target_segment_id, transparent_acc, - transparent_acc_helper, source_task_id, target_task_id) - - @classmethod - def FPropMeta(cls, p, inputs, *args): - py_utils.CheckShapes((inputs,)) - flops_per_element = 5 - source_batch, src_time, dim = inputs - flops = flops_per_element * src_time * src_time * source_batch * dim - args = args if isinstance(args, tuple) else (args,) - return py_utils.NestedMap(flops=flops, out_shapes=(inputs,) + args) - - @classmethod - def SetupDeterministicDropout(cls, params): - """Replaced dropout layers in transformer with deterministic ones.""" - params.tr_atten_tpl.dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - params.tr_atten_tpl.atten_tpl.dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - params.tr_fflayer_tpl.residual_dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - params.tr_fflayer_tpl.fflayer_tpl.dropout = ( - layers.DeterministicDropoutLayer.Params()) - return params - - def ExtendStep(self, - theta, - query_vec, - aux_vec, - aux_paddings, - cached_states, - time_step, - task_id=None, - use_short_seq_opt=False): - """Transformer decoder layer, extend one step in autoregressive decoding. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, 1, dim]. - aux_vec: [source_batch, source_time, dim] - aux_paddings: [source_batch, source_time] - cached_states: A `.NestedMap` object containing tensors which are the - results of previous attentions, used for fast decoding. key - - [target_time, target_batch, num_heads, dim_per_head]. value - - [target_time, target_batch, num_heads, dim_per_head]. - time_step: A scalar, the current decode step, 0-based. - task_id: [batch_size]: the input task_id meta information. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - cur_output: [target_batch, 1, dim] - updated_states: A `.NestedMap` object containing the updated states. - key - [target_time, target_batch, num_heads, dim_per_head]. - value - [target_time, target_batch, num_heads, dim_per_head]. - """ - target_batch, _, dim = py_utils.GetShape(query_vec, 3) - source_batch = py_utils.GetShape(aux_vec)[0] - - # First the self-attention layer. - atten_vec, updated_states = self.self_atten.ExtendStep( - theta.self_atten, query_vec, cached_states, time_step, - use_short_seq_opt) - - # Next the cross-attention layer. - atten_vec = tf.reshape(atten_vec, [source_batch, -1, dim]) - atten_vec, _ = self.cross_atten.FProp(theta.cross_atten, atten_vec, aux_vec, - aux_paddings) - atten_vec = tf.reshape(atten_vec, [target_batch, 1, -1]) - - # Finally the feed-forward layer. - cur_output = self.fflayer.FProp( - theta.fflayer, atten_vec, - tf.zeros([target_batch, 1], dtype=atten_vec.dtype), task_id) - return cur_output, updated_states - - -class GPipeBatchMajorTransformerLayer(TransformerLayer): - """GPipe compatible batch majortransformer layer. - - To be used with the new GPipeBatchMajorStack. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('ln_tpl', layers.LayerNorm.Params(), - 'Layer norm default params. No layernorm if set to None.') - p.Define('output_layer_norm', False, - 'Whether to layer normalize the output of the layer.') - return p - - def __init__(self, params): - # Initialize output layer norm. - super().__init__(params) - p = self.params - if p.output_layer_norm: - params = p.ln_tpl.Copy() - params.name = 'output_ln' - params.input_dim = p.input_dim - self.CreateChild('layer_norm', params) - - def FProp(self, theta, source_vecs, source_paddings, target_vecs, - target_paddings, encoder_self_atten_segment_mask, - decoder_self_atten_segment_mask, decoder_cross_atten_segment_mask): - p = self.params - with tf.name_scope(p.name): - if p.has_aux_atten: # Decoder FProp - sa_mask, ca_mask = None, None - if p.packed_input: - # This computation doesn't behave nicely when outside - # recurrent.Recurrent resulting in nans for splits > 1 - min_val = GetDtypeMin(decoder_self_atten_segment_mask.dtype) - # Operator overloading with * produces type-errors when running on - # borg with splits > 1. - sa_mask = tf.math.multiply(min_val, decoder_self_atten_segment_mask) - ca_mask = tf.math.multiply(min_val, decoder_cross_atten_segment_mask) - atten_vec, _ = self.self_atten.FProp( - theta.self_atten, - target_vecs, - None, - target_paddings, - segment_mask=sa_mask) - atten_vec, _ = self.cross_atten.FProp( - theta.cross_atten, - atten_vec, - source_vecs, - source_paddings, - segment_mask=ca_mask) - atten_vec = self.fflayer.FProp(theta.fflayer, atten_vec, - target_paddings) - atten_vec.set_shape(target_vecs.shape) - if p.output_layer_norm: - atten_vec = self.layer_norm.FProp(theta.layer_norm, atten_vec) - return (source_vecs, source_paddings, atten_vec, target_paddings, - encoder_self_atten_segment_mask, - decoder_self_atten_segment_mask, - decoder_cross_atten_segment_mask) - - # Encoder FProp - sa_mask = None - if p.packed_input: - min_val = GetDtypeMin(encoder_self_atten_segment_mask.dtype) - sa_mask = tf.math.multiply(min_val, encoder_self_atten_segment_mask) - atten_vec, _ = self.self_atten.FProp( - theta.self_atten, - source_vecs, - None, - source_paddings, - segment_mask=sa_mask) - atten_vec = self.fflayer.FProp(theta.fflayer, atten_vec, source_paddings) - atten_vec.set_shape(source_vecs.shape) - if p.output_layer_norm: - atten_vec = self.layer_norm.FProp(theta.layer_norm, atten_vec) - - return (atten_vec, source_paddings, target_vecs, target_paddings, - encoder_self_atten_segment_mask, decoder_self_atten_segment_mask, - decoder_cross_atten_segment_mask) - - @classmethod - def FPropMeta(cls, p, inputs, *args): - py_utils.CheckShapes((inputs,)) - flops_per_element = 5 - source_batch, src_time, dim = inputs - flops = flops_per_element * src_time * src_time * source_batch * dim - args = args if isinstance(args, tuple) else (args,) - return py_utils.NestedMap(flops=flops, out_shapes=(inputs,) + args) - - @classmethod - def SetupDeterministicDropout(cls, params): - """Replaced dropout layers in transformer with deterministic ones.""" - params.tr_atten_tpl.dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - params.tr_atten_tpl.atten_tpl.dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - params.tr_fflayer_tpl.residual_dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - params.tr_fflayer_tpl.fflayer_tpl.dropout = ( - layers.DeterministicDropoutLayer.Params()) - return params - - def ExtendStep(self, - theta, - query_vec, - aux_vec, - aux_paddings, - cached_states, - time_step, - use_short_seq_opt=False): - """Transformer decoder layer, extend one step in autoregressive decoding. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, 1, dim]. - aux_vec: [source_batch, source_time, dim] - aux_paddings: [source_batch, source_time] - cached_states: A `.NestedMap` object containing tensors which are the - results of previous attentions, used for fast decoding. key - - [target_time, target_batch, num_heads, dim_per_head]. value - - [target_time, target_batch, num_heads, dim_per_head]. - time_step: A scalar, the current decode step, 0-based. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - cur_output: [target_batch, 1, dim] - updated_states: A `.NestedMap` object containing the updated states. - key - [target_time, target_batch, num_heads, dim_per_head]. - value - [target_time, target_batch, num_heads, dim_per_head]. - """ - target_batch, _, dim = py_utils.GetShape(query_vec, 3) - source_batch = py_utils.GetShape(aux_vec)[0] - - # First the self-attention layer. - atten_vec, updated_states = self.self_atten.ExtendStep( - theta.self_atten, query_vec, cached_states, time_step, - use_short_seq_opt) - - # Next the cross-attention layer. - atten_vec = tf.reshape(atten_vec, [source_batch, -1, dim]) - atten_vec, _ = self.cross_atten.FProp(theta.cross_atten, atten_vec, aux_vec, - aux_paddings) - atten_vec = tf.reshape(atten_vec, [target_batch, 1, -1]) - - # Finally the feed-forward layer. - cur_output = self.fflayer.FProp( - theta.fflayer, atten_vec, - tf.zeros([target_batch, 1], dtype=atten_vec.dtype)) - - if self.params.output_layer_norm: - cur_output = self.layer_norm.FProp(theta.layer_norm, cur_output) - return cur_output, updated_states - - -# pyformat: disable -class Builder(builder.Base): - """Builder for self-attention layers.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('model_dim', 4, 'Model dim of this layer.') - p.Define('num_heads', 1, 'Number of heads in the atten layer.') - p.Define('ff_hidden_dim', 4, 'Hidden dim of the feedforward layer') - p.Define('attention_hidden_dim', None, - 'Hidden dim of the attention layer.') - p.Define('residual_dropout_prob', 0, - 'Dropout prob to the output of each sub-layer before it is added ' - 'to the sub-layer input.') - p.Define('ff_activation_fn', tf.nn.relu, - 'Activation function in Feedforward layer.') - p.Define('ff_residual_weight', 1.0, 'Weight given to F(x) in the residual ' - 'connection: y = x + ff_residual_weight * F(x), in Feedforward ' - 'layer.') - p.Define('relu_dropout_prob', 0, - 'Probability at which we apply dropout to the hidden layer of ' - 'feed-forward network.') - p.Define('atten_dropout_prob', 0, - 'Probability at which we apply dropout to the attention layer') - p.Define('selfatten_add_unnormalized_input', True, - 'Whether to use unnormalized input in the residual add.') - p.Define('selfatten_enable_value_proj', True, - 'Whether value v is pre-projected before self attention or not.') - p.Define('conv_activation', 'RELU', - 'Activation function for convolution layer in Builder.') - p.Define('num_splits', 1, - 'Number of model parallelism splits.') - p.Define('num_micro_batches', 1, - 'Number of spatial partition along the batch dimension. ' - 'When num_micro_batches > 1, the effective batch size of the ' - 'intermediate activation is batch_size // num_micro_batches.' - 'This allows models to try larger batch size which might improve ' - 'model quality') - p.Define('glu_with_tanh', False, - 'If the Gated Linear Unit should apply tanh on the activation ' - 'input.') - p.Define('packed_input', False, - 'Whether to support packed input') - p.Define('enable_per_dim_scale', True, - 'Whether using per_dim_scale or scaling by a constant factor.') - p.Define('use_fused_layernorm', False, 'Whether to use fused layernorm.') - p.Define('use_bias', True, 'Whether to use bias for projection layer.') - p.Define('norm_layer_tpl', None, - 'If specified, the normalization layer template.') - p.Define( - 'enable_scaling_code_motion', False, 'Move scalings from the side ' - 'of T^2 to the side of T for better performance. This may result ' - 'in model quality drops when using bf16 for some models due to ' - 'different XLA fusion decisions.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.num_splits > 1 or p.num_micro_batches > 1: - assert p.deterministic_dropout - - def _Dropout(self, name, drop_prob): - """Returns a DropoutLayer Params.""" - return super()._Dropout(name, keep_prob=1.0 - drop_prob) - - def _Add(self, name, residual_weight=1.0): - return self._Fn(name, fn=lambda x, y: x + residual_weight * y, - fn_out=lambda x, y: x) - - def _ExpandDims(self, name): - return self._Fn(name, - fn=lambda x: tf.expand_dims(x, 2), - fn_out=lambda x: tshape.Shape(x[0:2] + [1] + x[2:]), - fn_flops=lambda x: 1) - - def _Squeeze(self, name): - return self._Fn(name, - fn=lambda x: tf.squeeze(x, 2), - fn_out=lambda x: tshape.Shape(x[0:2] + x[3:]), - fn_flops=lambda x: 1) - - def _Glu(self, name): - def _GLUFn(inputs): - gated_inputs, act_inputs = tf.split(inputs, 2, axis=-1) - return act_inputs * tf.sigmoid(gated_inputs) - - def _GatedTanhFn(inputs): - gated_inputs, act_inputs = tf.split(inputs, 2, axis=-1) - return tf.tanh(act_inputs) * tf.sigmoid(gated_inputs) - - fn = _GatedTanhFn if self.params.glu_with_tanh else _GLUFn - - return self._Fn(name, - fn=fn, - fn_out=lambda x: tshape.Shape(x[:-1] + [x[-1] / 2]), - fn_flops=lambda x: 15 * x.size) - - def _Pad(self, name): - return self._Fn( - name, - fn=lambda x, p: py_utils.ApplyPadding(tf.expand_dims(p, -1), x), - fn_out=lambda x, p: x, - fn_flops=lambda x, p: 2 * max(x.size, p.size)) - - def _MultiHeadedAtten(self, name, num_heads=None): - """Returns a MultiHeadedAttention params.""" - p = self.params - if num_heads is None: - num_heads = p.num_heads - atten_p = MultiHeadedAttention.Params().Set( - name=name, - input_dim=p.model_dim, - hidden_dim=p.attention_hidden_dim or p.model_dim, - num_heads=num_heads, - atten_dropout_prob=p.atten_dropout_prob, - enable_value_proj=p.selfatten_enable_value_proj, - enable_per_dim_scale=p.enable_per_dim_scale, - packed_input=p.packed_input, - fprop_dtype=p.fprop_dtype, - use_bias=p.use_bias, - enable_scaling_code_motion=p.enable_scaling_code_motion, - ) - if p.deterministic_dropout: - atten_p.dropout_tpl = layers.DeterministicDropoutLayer.Params() - return atten_p - - def Feedforward(self, name, is_causal=False, ff_hidden_dim=None): - del is_causal - p = self.params - if ff_hidden_dim is None: - ff_hidden_dim = p.ff_hidden_dim - sub_list = [ - ('i.vec->after_feedforward', - self._Seq( - 'feedforward', - self._LN('ln', p.model_dim, - use_fused_layernorm=p.use_fused_layernorm), - self._Linear('linear01', p.model_dim, ff_hidden_dim), - self._Bias('bias01', ff_hidden_dim), - self._Activation('act', p.ff_activation_fn), - self._Dropout('relu_dropout', p.relu_dropout_prob), - self._Linear('linear02', ff_hidden_dim, p.model_dim), - self._Bias('bias02', p.model_dim), - self._Dropout('dropout', p.residual_dropout_prob))), - ('i.vec,after_feedforward->added', self._Add('add', p.ff_residual_weight)), - ('added,i.paddings->o.vec', self._Pad('pad')), - ('i.paddings->o.paddings', self._Id('id')), - ] - - if p.packed_input: - sub_list.append(('i.segment_mask->o.segment_mask', - self._Id('segment_mask'))) - - return self._Graph( - name, - ['i'], # input NestedMap with {vec, paddings, segment_mask} - ['o'], # output NestedMap with {vec, paddings, segment_mask} - *sub_list) - - def _MaybeSplit(self, name, blocks): - p = self.params - if p.num_splits == 1 and p.num_micro_batches == 1: - return None - - num_layers = len(blocks) - assert num_layers >= p.num_splits - layers_per_split = (num_layers - 1) // p.num_splits + 1 - cells = [] - while blocks: - head, blocks = blocks[:layers_per_split], blocks[layers_per_split:] - cells.append(self._Seq('cell_{}'.format(len(cells)), *head)) - assert len(cells) == p.num_splits - - return gpipe.PipeliningLayer.Params().Set( - name=name, - cell_tpl=cells, - nested_map_fprop=True, - num_micro_batches=p.num_micro_batches) - - def _DepthwiseConv2D(self, name, filter_size, is_causal=False): - """A depthwise convolution block for lightweight conv.""" - p = self.params - conv_builder_params = conv_layers.Builder.Params() - if p.norm_layer_tpl: - conv_builder_params.norm_layer_tpl = p.norm_layer_tpl - conv_builder = conv_builder_params.Instantiate() - return conv_builder.DepthwiseConv2D( - name=name, - in_dim=p.model_dim, - depth_multiplier=1, - filter_shape=[filter_size, 1], - stride=(1, 1), - dilation=(1, 1), - activation=p.conv_activation, - is_causal=is_causal) - - def _NormalizedDepthwiseConv2D(self, name, kernel_size, is_causal=False): - """A depthwise convolution block for lightweight conv.""" - p = self.params - conv_builder_params = conv_layers.Builder.Params() - conv_builder = conv_builder_params.Instantiate() - return conv_builder.NormalizedDepthwiseConv2D( - name=name, - kernel_size=kernel_size, - num_heads=p.num_heads, - in_dim=p.model_dim, - dropconnect_prob=p.atten_dropout_prob, - deterministic_dropout=p.deterministic_dropout, - is_causal=is_causal) - - def LConv(self, - name, - kernel_size, - is_causal=False, - convolution_fn=None): - """[DEPRECATED] A lightweight convolution block as described in. - - Use conv_layer_builder.LConv() instead. - - https://arxiv.org/abs/1901.10430 - Corresponding PyTorch Implementation (L587): - https://github.com/pytorch/fairseq/blob/v0.6.2/fairseq/models/lightconv.py - - - This block can be used as an alternative to self-attention block. - - Args: - name: name of the params - kernel_size: kernel size used in the conv layer. - is_causal: is causal padding or not. - convolution_fn: Convolution to apply, default _NormalizedDepthwiseConv2D. - - Returns: - A LightWeightConvLayerBlock layer params. - """ - p = self.params - if convolution_fn is None: - convolution_fn = getattr(self, '_NormalizedDepthwiseConv2D') - - sub_list = [ - ('i.vec->pre_conv', - self._Seq( - 'pre_conv', - self._LN('ln', p.model_dim, - use_fused_layernorm=p.use_fused_layernorm), - self._Linear('linear', p.model_dim, p.model_dim * 2), - self._Bias('bias', p.model_dim * 2), - self._Glu('glu'), - self._ExpandDims('expand'))), - ('pre_conv,i.paddings->post_conv,o.paddings', - convolution_fn('conv', kernel_size, is_causal)), - ('post_conv->after_dropout', - self._Seq( - 'post_conv', - self._Squeeze('squeeze'), - self._Linear('linear', p.model_dim, p.model_dim), - self._Bias('bias', p.model_dim), - self._Dropout('dropout', p.residual_dropout_prob))), - ('i.vec,after_dropout->o.vec', self._Add('add')), - ] - if p.packed_input: - sub_list.append(('i.segment_mask->o.segment_mask', self._Id('segment_mask'))) - - return self._Graph( - name, - ['i'], # input NestedMap with {vec, paddings, segment_mask} - ['o'], # output NestedMap with {vec, paddings, segment_mask} - *sub_list - ) - - def LconvBlock(self, name, kernel_size, is_causal, - convolution_fn): - """A lightweight conv block followed by a feedforward one.""" - return self._Seq( - name, - self.LConv( - name='lconv', - kernel_size=kernel_size, - is_causal=is_causal, - convolution_fn=convolution_fn), - self.Feedforward('ff', is_causal)) - - def Seq(self, name, *subs): - """Returns a stack of sequential layers.""" - return self._Seq(name, *subs) - - def LConvStack(self, name, kernel_sizes, is_causal=False): - """Returns a stack of LConv layers with kernel size in kernel_sizes.""" - blocks = [] - for i, kernel_size in enumerate(kernel_sizes): - blocks.append( - self.LconvBlock( - name='block_{}'.format(i), - kernel_size=kernel_size, - is_causal=is_causal, - convolution_fn=None)) - return self._MaybeSplit(name, blocks) or self._Seq(name, *blocks) - - def _Stride(self, name, stride, first_n=None): - """Strides the input sequence. - - Args: - name: name of this layer. - stride: To use every k-th token, set the stride to k. When stride == 0, - only returns the first token of the input. When stride == 1, returns - every token in the input. - first_n: only considers the first N tokens for the output. We use - [:first_n:stride] to select the output tokens. If first_n is None, this - flag is a no-op. If stride is positive, the output sequence length is - "(first_n-1) // stride + 1". If stride is 0, first_n has to be None or - 1. first_n can't be 0. If first_n <= stride, only the first token is - used. - - Returns: - A layer params that does stride. - """ - assert first_n is None or first_n > 0 - if stride == 0: - assert first_n is None or first_n == 1 - return self._Fn( - name=name, - fn=lambda x: tf.expand_dims(x[:, 0], 1), - fn_out=lambda x: tshape.Shape(x[0:1] + [1] + x[2:]), - fn_flops=lambda x: 1) - - if first_n: - # out_seq_len is 1 if first_n is 1 ~ stride and is 2 if it's stride+1 ~ - # 2*stride... - out_seq_len = (first_n - 1) // stride + 1 - return self._Fn( - name=name, - fn=lambda x: x[:, :first_n:stride], - fn_out=lambda x: tshape.Shape(x[0:1] + [out_seq_len] + x[2:]), - fn_flops=lambda x: 1) - - if stride == 1: - return self._Id(name) - - return self._Fn( - name=name, - fn=lambda x: x[:, ::stride], - fn_out=lambda x: tshape.Shape(x[0:1] + x[1] // stride + x[2:]), - fn_flops=lambda x: 1) - - def _StridedAttention(self, name, stride=1, first_n=None, num_heads=None): - """Computes self attention with optional stride. - - Args: - name: name of this layer. - stride: If omitted, the default is 1: use every token in the query. To use - every k-th token, set the stride to k. When set to 0, only use the first - token of the query. - first_n: only considers the first N tokens for the output. We use - [:first_n:stride] to select the output tokens. If first_n is None, this - flag is a no-op. If stride is positive, the output sequence length is - "(first_n-1) // stride + 1". If stride is 0, first_n has to be None or - 1. first_n can't be 0. If first_n <= stride, only the first token is - used. - num_heads: the number of heads. - - Returns: - A self attention layer params. - """ - p = self.params - input_to_add = ('i.vec' - if p.selfatten_add_unnormalized_input else 'after_ln') - - attention_inputs = 'strided_query,after_ln,after_ln,i.paddings' - if p.packed_input: - attention_inputs += ',i.segment_mask' - - if num_heads is None: - num_heads = p.num_heads - - sub_list = [ - ('i.vec->after_ln', - self._LN('LN', p.model_dim, - use_fused_layernorm=p.use_fused_layernorm)), - ('after_ln->strided_query', - self._Stride('query_after_stride', stride, first_n)), - ('{}->after_att,prob'.format(attention_inputs), - self._MultiHeadedAtten('atten', num_heads)), - ('after_att->after_dropout', - self._Dropout('dropout', p.residual_dropout_prob)), - ('{}->strided_input'.format(input_to_add), - self._Stride('before_add', stride, first_n)), - ('strided_input,after_dropout->o.vec', - self._Add('add')), - ('i.paddings->o.paddings', - self._Stride('padding_after_Stride', stride, first_n)), - ] - if p.packed_input: - sub_list.append(('i.segment_mask->o.segment_mask', self._Id('segment_mask'))) - - return self._Graph( - name, - ['i'], # input NestedMap with {vec, paddings, segment_mask} - ['o'], # output NestedMap with {vec, paddings, segment_mask} - *sub_list) - - def TransformerEncoderLayer(self, name, stride=1, first_n=None, - ff_hidden_dim=None, num_heads=None): - """(inputs, paddings) -> (encoded, paddings). - - Args: - name: the string name of the encoder layer params. - stride: To use every k-th token, set the stride to k. When stride == 0, - only returns the first token of the input. When stride == 1, returns - every token in the input. - first_n: only considers the first N tokens for the output. We use - [:first_n:stride] to select the output tokens. If first_n is None, this - flag is a no-op. If stride is positive, the output sequence length is - "(first_n-1) // stride + 1". If stride is 0, first_n has to be None or - 1. first_n can't be 0. If first_n <= stride, only the first token is - used. - ff_hidden_dim: The feed forward layer's hidden dimension. If specified, - this will override p.ff_hidden_dim. - num_heads: The number of heads for the multi-head attention module. If - specified, this will override p.num_heads. - - Returns: - A transformer encoder layer params that supports optional stride. - """ - p = self.params - if ff_hidden_dim is None: - ff_hidden_dim = p.ff_hidden_dim - if num_heads is None: - num_heads = p.num_heads - return self._Seq(name, self._Seq( - 'block', - self._StridedAttention('self_atten', stride=stride, - first_n=first_n, num_heads=num_heads), - self.Feedforward('ff', ff_hidden_dim=ff_hidden_dim))) - - def Stack(self, name, blocks): - """Returns a stack of sequential layers.""" - return self._MaybeSplit(name, blocks) or self._Seq(name, *blocks) - - def TransformerEncoderStack(self, name, num_layers=1): - """Returns a stack of num_layers self-attention layers.""" - blocks = [ - self.TransformerEncoderLayer(name='iter_{:0>3d}'.format(d)) - for d in range(num_layers) - ] - return self.Stack(name, blocks) -# pyformat: enable - - -class LmBuilder(Builder): - """Langange model builder with causal padding.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('xla_num_partitions', None, 'Number of SPMD partitions.') - p.Define('dtype', tf.float32, 'Datatype to use.') - return p - - def _ShardedVar(self, name, weights, split_dim): - return moe_layers.ShardedVarLayer.Params().Set( - name=name, - weights=weights, - split_dimension=split_dim, - fprop_dtype=self.params.fprop_dtype, - num_devices=self.params.xla_num_partitions) - - def _LinearWeight(self, name, input_dim, output_dim, split_dim): - return self._ShardedVar( - name=name, - weights=[('w', - py_utils.WeightParams( - shape=[input_dim, output_dim], - init=py_utils.WeightInit.Uniform((3. / input_dim)**0.5), - dtype=self.params.dtype))], - split_dim=split_dim) - - def _Linear(self, name, input_dim, output_dim, split_dim=0): - return self._Graph( - name, - ['inputs'], - ['outputs'], - ('->w', self._LinearWeight('w', input_dim, output_dim, split_dim)), - ('inputs,w->outputs', - self._Fn( - 'linear', - fn=lambda inputs, w: tf.einsum('BLI,IO->BLO', inputs, w))), - ) - - def _BiasWeight(self, name, dim): - return self._ShardedVar( - name=name, - weights=[('b', - py_utils.WeightParams( - shape=[dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=self.params.dtype))], - split_dim=0) - - def _Bias(self, name, dim): - return self._Graph( - name, - ['inputs'], - ['outputs'], - ('->b', self._BiasWeight('b', dim)), - ('inputs,b->outputs', self._Fn('bias', - fn=lambda inputs, b: inputs + b)), - ) - - def Feedforward(self, name): - p = self.params - - ff_list = [ - self._LN('ln', p.model_dim, use_fused_layernorm=p.use_fused_layernorm), - self._Linear('linear01', p.model_dim, p.ff_hidden_dim, split_dim=1) - ] - if p.use_bias: - ff_list.append(self._Bias('bias01', p.ff_hidden_dim)) - ff_list += [ - self._Activation('act', p.ff_activation_fn), - self._Dropout('relu_dropout', p.relu_dropout_prob), - self._Linear('linear02', p.ff_hidden_dim, p.model_dim, split_dim=0) - ] - if p.use_bias: - ff_list.append(self._Bias('bias02', p.model_dim)) - ff_list.append(self._Dropout('dropout', p.residual_dropout_prob)) - - sub_list = [ - ('i.vec->after_feedforward', self._Seq('feedforward', *ff_list)), - ('i.vec,after_feedforward->added', self._Add('add', - p.ff_residual_weight)), - ('added,i.paddings->o.vec', self._Pad('pad')), - ('i.paddings->o.paddings', self._Id('id')), - ] - - return self._Graph( - name, - ['i'], # input NestedMap with {vec, paddings} - ['o'], # output NestedMap with {vec, paddings} - *sub_list) - - def _Attention(self, name, is_causal=True): - """Computes self attention with optional stride. - - Args: - name: name of this layer. - is_causal: If true, add cause per_step padding to the attention layer. - - Returns: - A self attention layer params. - """ - p = self.params - tr_atten_p = TransformerAttentionLayer.Params().Set( - name='transformer_atten', - input_dim=p.model_dim, - hidden_dim=p.attention_hidden_dim or p.model_dim, - is_masked=is_causal, - num_heads=p.num_heads, - residual_dropout_prob=p.residual_dropout_prob, - atten_dropout_prob=p.atten_dropout_prob, - fprop_dtype=p.fprop_dtype, - add_unnormalized_input=p.selfatten_add_unnormalized_input, - ) - tr_atten_p.atten_tpl.use_bias = p.use_bias - tr_atten_p.atten_tpl.enable_value_proj = p.selfatten_enable_value_proj - tr_atten_p.atten_tpl.enable_per_dim_scale = p.enable_per_dim_scale - tr_atten_p.atten_tpl.xla_num_partitions = p.xla_num_partitions - if p.deterministic_dropout: - tr_atten_p.dropout_tpl = layers.DeterministicDropoutLayer.Params() - tr_atten_p.atten_p.dropout_tpl = layers.DeterministicDropoutLayer.Params() - - return self._Graph( - name, - ['i'], # input NestedMap with {vec, paddings} - ['o'], # output NestedMap with {vec, paddings} - ('i.vec,i.vec,i.paddings->o.vec,unused_prob', tr_atten_p), - ('i.paddings->o.paddings', self._Id('id'))) - - def TransformerEncoderLayer(self, name, is_causal=True): - """(inputs, paddings) -> (encoded, paddings). - - Args: - name: the string name of the encoder layer params. - is_causal: If true, add cause per_step padding to the attention layer. - - Returns: - A transformer encoder layer params that supports optional stride. - """ - # Hack to be compatible with ckpt generated by self._rep - return self._Seq( - name, - self._Seq('block', self._Attention('self_atten', is_causal=is_causal), - self.Feedforward('ff'))) diff --git a/lingvo/core/batch_major_attention_test.py b/lingvo/core/batch_major_attention_test.py deleted file mode 100644 index 91a96df7..00000000 --- a/lingvo/core/batch_major_attention_test.py +++ /dev/null @@ -1,2796 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for batch_major_attention.""" - -from absl.testing import parameterized -from lingvo import compat as tf -from lingvo.core import attention as tm_attention -from lingvo.core import attention_util -from lingvo.core import base_layer -from lingvo.core import batch_major_attention as attention -from lingvo.core import hyperparams -from lingvo.core import py_utils -from lingvo.core import test_utils -import numpy as np - - -class MultiHeadSelfAttentionTest(test_utils.TestCase, parameterized.TestCase): - """Test attention models.""" - - def _AttentionInputs(self, input_dim=4, dtype=tf.float32): - np.random.seed(6348575) - batch_size = 6 - seq_len = 6 - input_vecs_p = [ - np.random.rand(seq_len, input_dim) for _ in range(batch_size) - ] - input_vecs = tf.stack([tf.constant(x, dtype=dtype) for x in input_vecs_p]) - # pyformat: disable - input_padding_p = [[0, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0], - [0, 0, 1, 0, 1, 0], [0, 0, 1, 1, 0, 0], - [1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0]] - # pyformat: enable - input_padding = tf.constant(input_padding_p, dtype=dtype) - - return input_vecs, input_padding, input_vecs_p, input_padding_p - - def testDotProductAttention(self): - (input_vecs, input_padding, input_vecs_p, - input_padding_p) = self._AttentionInputs() - p = attention.MultiHeadedAttention.Params().Set( - name='self_atten', - input_dim=4, - hidden_dim=4, - enable_scaling_code_motion=True) - l = p.Instantiate() - - probs, probs_sum = l.AttenProbs( - l.theta, - tf.expand_dims(input_vecs, 2), - tf.expand_dims(input_vecs, 2), - input_padding, - segment_mask=None) - - with self.session(use_gpu=False) as sess: - tf.global_variables_initializer().run() - prob_out = sess.run(tf.squeeze(probs / probs_sum)) - - # Use numpy to perform the same computation to generate expected results. - input_vecs_p = np.array(input_vecs_p) - target_vecs_p = np.transpose(input_vecs_p, (0, 2, 1)) - expected_logit = np.matmul(input_vecs_p, target_vecs_p) - expected_logit = np.transpose(expected_logit, (0, 2, 1)) - elexp = np.exp(expected_logit) - input_padding_p = np.array(input_padding_p) - input_padding_p = np.expand_dims(input_padding_p, axis=1) - input_padding_p = np.tile(input_padding_p, (1, 6, 1)) - elexp *= (1 - input_padding_p) - expected_prob_out = elexp / np.expand_dims(np.sum(elexp, axis=-1), axis=-1) - expected_prob_out = np.reshape(expected_prob_out, (6, 6, 6)) - self.assertAllClose(expected_prob_out, prob_out) - - def testMultiHeadedAttentionDotProduct(self): - # input_batch:6, seq_len:6. Test n = 2 case. - with self.session(use_gpu=True) as sess: - input_vecs, input_padding, _, _ = self._AttentionInputs() - p = attention.MultiHeadedAttention.Params().Set( - name='self_atten', num_heads=2, input_dim=4, hidden_dim=4) - - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - - l = p.Instantiate() - tf.global_variables_initializer().run() - ctx_vec, _ = l.FProp( - l.theta, - input_vecs, - input_vecs, - input_vecs, - input_padding, - segment_mask=None) - context_vec_out = sess.run(ctx_vec) - context_vec_out = np.reshape(context_vec_out, (6, 24)) - self.assertAllClose( - [27.417763, 31.783672, 19.99568, 23.907103, 21.078259, 28.429199], - np.sum(context_vec_out, axis=1)) - - def testMultiHeadedAttentionDotProductSegmentMask(self): - # input_batch:6, seq_len:6. Test n = 2 case. - with self.session(use_gpu=True) as sess: - input_vecs, input_padding, _, _ = self._AttentionInputs() - p = attention.MultiHeadedAttention.Params().Set( - name='self_atten', - num_heads=2, - input_dim=4, - hidden_dim=4, - packed_input=True) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - - segment_id = tf.zeros([6, 6]) - segment_mask = attention.SegmentMask(segment_id, segment_id) - padding = tf.tile(tf.reshape(input_padding, [6, 1, 1, 6]), [1, 1, 6, 1]) - padding_mask = padding * segment_mask.dtype.max * tf.constant( - -0.7, dtype=segment_mask.dtype) - segment_mask += padding_mask - - l = p.Instantiate() - tf.global_variables_initializer().run() - ctx_vec, _ = l.FProp( - l.theta, - input_vecs, - input_vecs, - input_vecs, - input_padding, - segment_mask=segment_mask) - context_vec_out = sess.run(ctx_vec) - context_vec_out = np.reshape(context_vec_out, (6, 24)) - self.assertAllClose( - [27.417763, 31.783672, 19.99568, 23.907103, 21.078259, 28.429199], - np.sum(context_vec_out, axis=1)) - - -class MultiHeadedAttentionXLOracle: - """Oracle layer used for computing ground truths for MultiHeadedAttention. - - Written in a non-vectorized way. - """ - - def __init__(self, u, v, pos_proj, sinusoid_emb): - """Constructor. - - Args: - u: A numpy ndarray of shape [N, H] - v: A numpy ndarray of shape [N, H] - pos_proj: A numpy ndarray of shape [embed_dim, N, H] - sinusoid_emb: A numpy ndarray of shape [seqlen, emb_dim]. - """ - assert u.shape == v.shape - assert u.shape == pos_proj.shape[1:] - assert sinusoid_emb.shape[-1] == pos_proj.shape[0] - # [N, H] - self._u = u - # [N, H] - self._v = v - # [?, N, H] - self._pos_proj = pos_proj - - self._num_heads = u.shape[0] - self._atten_dim = u.shape[-1] - self._hidden_dim = u.shape[0] * u.shape[-1] - self._sinusoid_emb = sinusoid_emb - - def _GetPositionEnc(self, tgt_t, src_t, head, seqlen): - """Gets positional encoding. - - Args: - tgt_t: A Python int, time step of target seq. - src_t: A Python int, time step of source seq. - head: A Python int, num of heads of the attention. - seqlen: A Python int, sequence length of target/source seq. - - Returns: - A numpy array of shape [head, emb_dim // head]. - """ - # [emb_dim] - sinusoid_enc = self._sinusoid_emb[tgt_t - src_t + seqlen - 1] - return np.einsum('DNH,D->NH', self._pos_proj, sinusoid_enc)[head] - - def AttenProbs(self, key, query, paddings, per_step_padding): - """Computes attention probs in a non vectorized way. - - Args: - key: A numpy ndarray of shape [batch, seqlen, heads, dim]. - query: A numpy ndarray of the same shape as `key`. - paddings: A numpy ndarray of shape [batch, seqlen]. - per_step_padding: A numpy ndarray of shape [batch, seqlen, seqlen]. - - Returns: - A numpy ndarray of shape [batch, query_seqlen, key_seqlen] - """ - - assert query.ndim == 4 - assert paddings.ndim == 2 - assert key.shape == query.shape - - batch, seqlen = query.shape[:2] - tgtlen, srclen = seqlen, seqlen - assert query.shape[2] == self._num_heads - assert query.shape[3] == self._atten_dim - assert paddings.shape == query.shape[:2] - - logits = np.zeros((batch, self._num_heads, tgtlen, srclen)) - probs = np.zeros((batch, self._num_heads, tgtlen, srclen)) - - def Normalize(vec): - expx = np.exp(vec) - expxsum = np.sum(expx, axis=-1) - return expx / expxsum - - # [b, tgtlen, srclen] - paddings = np.broadcast_to( - np.reshape(paddings, (batch, 1, seqlen)), (batch, seqlen, seqlen)) - for b in range(batch): - for h in range(self._num_heads): - for i in range(tgtlen): - for j in range(srclen): - pos_enc = self._GetPositionEnc(i, j, h, seqlen) - logits[b][h][i][j] = ( - np.dot(query[b][i][h], key[b][j][h]) + - np.dot(query[b][i][h], pos_enc) + - np.dot(self._u[h], key[b][j][h]) + np.dot(self._v[h], pos_enc)) - - total_padding = paddings[b][i] + per_step_padding[b][i] - logits[b][h][i] = np.where(total_padding > 0, - np.finfo(np.float32).max * (-0.7), - logits[b][h][i]) - probs[b][h][i] = Normalize(logits[b][h][i]) - return probs - - -def _AttentionInputs(input_dim=4, dtype=tf.float32, is_causal=True): - np.random.seed(6348575) - batch_size = 6 - seq_len = 6 - query_vec_p = [np.random.rand(seq_len, input_dim) for _ in range(batch_size)] - query_vec_p = np.array(query_vec_p).astype(dtype.as_numpy_dtype) - query_vec = tf.convert_to_tensor(query_vec_p) - - memory_vec_p = [np.random.rand(seq_len, input_dim) for _ in range(batch_size)] - memory_vec_p = np.array(memory_vec_p).astype(dtype.as_numpy_dtype) - memory_vec = tf.convert_to_tensor(memory_vec_p) - # pyformat: disable - paddings_p = np.array( - [[0, 0, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 1, 1], [0, 0, 1, 1, 1, 1], - [0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 1]]).astype(dtype.as_numpy_dtype) - paddings = tf.convert_to_tensor(paddings_p) - # causal padding. - if is_causal: - per_step_padding_p = [ - [0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1], - [0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0]] - else: - per_step_padding_p = np.zeros((seq_len, seq_len)) - per_step_padding_p = [per_step_padding_p for _ in range(batch_size)] - per_step_padding_p = np.array(per_step_padding_p).astype(dtype.as_numpy_dtype) - per_step_padding = tf.convert_to_tensor(per_step_padding_p) - - # pyformat: enable - return (query_vec, memory_vec, paddings, per_step_padding, query_vec_p, - memory_vec_p, paddings_p, per_step_padding_p) - - -class MultiHeadedAttentionTest(test_utils.TestCase, parameterized.TestCase): - """Test dot-product multiheaded attention.""" - - def _AttentionExtendStepInputs(self, - input_dim=4, - num_heads=2, - dtype=tf.float32): - np.random.seed(6348575) - batch_size = 6 - seq_len = 6 - query_vec_p = [np.random.rand(1, input_dim) for _ in range(batch_size)] - query_vec = tf.stack([tf.constant(x, dtype=dtype) for x in query_vec_p]) - # pyformat: disable - per_step_padding_p = [[0, 1, 1, 1, 1, 1]] - per_step_padding_p = [per_step_padding_p for _ in range(batch_size)] - # pyformat: enable - per_step_padding = tf.stack( - [tf.constant(x, dtype=dtype) for x in per_step_padding_p]) - source_vecs = tf.constant( - np.random.normal( - 0.1, 0.5, [seq_len, batch_size, num_heads, input_dim // num_heads]), - dtype=dtype) - source_ctxs = tf.constant( - np.random.normal( - 0.1, 0.5, [seq_len, batch_size, num_heads, input_dim // num_heads]), - dtype=dtype) - cached_states = py_utils.NestedMap(key=source_vecs, value=source_ctxs) - return query_vec, cached_states, per_step_padding - - def testAttenProbs(self): - (query_vec, key_vec, paddings, per_step_padding, query_vec_p, key_vec_p, - paddings_p, per_step_padding_p) = _AttentionInputs() - p = attention.MultiHeadedAttention.Params().Set( - name='atten', - input_dim=4, - hidden_dim=4, - enable_scaling_code_motion=True) - l = p.Instantiate() - probs, probs_sum = l.AttenProbs( - l.theta, - tf.expand_dims(query_vec, 2), - tf.expand_dims(key_vec, 2), - paddings, - segment_mask=None, - per_step_padding=per_step_padding) - - with self.session(use_gpu=False) as sess: - tf.global_variables_initializer().run() - prob_out = sess.run(tf.squeeze(probs / probs_sum)) - - # Use numpy to perform the same computation to generate expected results. - query_vec_p = np.array(query_vec_p) - key_vec_p = np.array(key_vec_p) - key_vec_p = np.transpose(key_vec_p, (0, 2, 1)) - expected_logit = np.matmul(query_vec_p, key_vec_p) - paddings_p = np.array(paddings_p) - paddings_p = np.expand_dims(paddings_p, axis=1) - paddings_p = np.tile(paddings_p, (1, 6, 1)) - per_step_padding_p = np.array(per_step_padding_p) - paddings_p = 1.0 * np.logical_or(paddings_p, per_step_padding_p) - elexp = np.exp(expected_logit) - elexp *= (1.0 - paddings_p) - elexp += 1e-9 - expected_prob_out = elexp / np.expand_dims(np.sum(elexp, axis=-1), axis=-1) - expected_prob_out = np.reshape(expected_prob_out, (6, 6, 6)) - self.assertAllClose(expected_prob_out, prob_out) - - def testFPropCrossAttention(self): - # input_batch:6, seq_len:6. Test n = 2 case. - with self.session(use_gpu=True) as sess: - query_vec, memory_vec, paddings, per_step_padding, _, _, _, _ = ( - _AttentionInputs()) - p = attention.MultiHeadedAttention.Params().Set( - name='cross_atten', num_heads=2, input_dim=4, hidden_dim=4) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - tf.global_variables_initializer().run() - ctx_vec, _ = l.FProp( - l.theta, - query_vec, - memory_vec, - memory_vec, - paddings, - segment_mask=None, - per_step_padding=per_step_padding) - context_vec_out = sess.run(ctx_vec) - context_vec_out = np.reshape(context_vec_out, (6, 24)) - self.assertAllClose( - [24.624561, 27.805634, 23.358835, 11.085404, 27.165989, 23.750813], - np.sum(context_vec_out, axis=1)) - - @parameterized.named_parameters( - { - 'testcase_name': '_short_seq', - 'use_short_seq_opt': True, - }, { - 'testcase_name': '_long_seq', - 'use_short_seq_opt': False, - }) - def testExtendStepSelfAttention(self, use_short_seq_opt): - # input_batch:6, seq_len:6, query_len: 1. Test n = 2 case. - with self.session(use_gpu=True) as sess: - query_vec, cached_states, per_step_padding = self._AttentionExtendStepInputs( - ) - p = attention.MultiHeadedAttention.Params().Set( - name='atten', num_heads=2, input_dim=4, hidden_dim=4) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - tf.global_variables_initializer().run() - ctx_vec, updated_states = l.ExtendStep(l.theta, query_vec, cached_states, - None, None, per_step_padding, 0, - use_short_seq_opt) - context_vec_out = sess.run(ctx_vec) - new_source_vecs = sess.run(updated_states.key) - context_vec_out = np.reshape(context_vec_out, (6, 4)) - self.assertAllClose( - [5.381485, 5.384035, 4.493689, 3.544395, 3.424472, 3.311054], - np.sum(context_vec_out, axis=1)) - new_source_vecs = np.reshape(new_source_vecs, (6, 24)) - self.assertAllClose( - [4.116683, 1.340482, 1.065773, 1.035415, 4.928454, 3.161165], - np.sum(new_source_vecs, axis=1)) - - @parameterized.named_parameters({ - 'testcase_name': '_long_seq', - 'use_short_seq_opt': False, - }) - def testExtendStepAsyncTimeStepSelfAttention(self, use_short_seq_opt): - # input_batch:6, seq_len:6, query_len: 1. Test n = 2 case. - with self.session(use_gpu=True) as sess: - query_vec, cached_states, per_step_padding = self._AttentionExtendStepInputs( - ) - p = attention.MultiHeadedAttention.Params().Set( - name='atten', num_heads=2, input_dim=4, hidden_dim=4) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - - allzero_time_step = tf.constant([0] * 6) - time_step = tf.constant([0, 1, 2, 3, 4, 5]) - l = p.Instantiate() - tf.global_variables_initializer().run() - ctx_vec, updated_states = l.ExtendStep(l.theta, query_vec, cached_states, - None, None, per_step_padding, 0, - use_short_seq_opt) - ctx_vec_async, updated_states_async = l.ExtendStep( - l.theta, query_vec, cached_states, None, None, per_step_padding, - allzero_time_step, use_short_seq_opt) - - context_vec_out = sess.run(ctx_vec) - new_source_vecs = sess.run(updated_states.key) - context_vec_out_async = sess.run(ctx_vec_async) - new_source_vecs_async = sess.run(updated_states_async.key) - - self.assertAllClose( - np.sum(context_vec_out, axis=1), - np.sum(context_vec_out_async, axis=1)) - self.assertAllClose( - np.sum(new_source_vecs, axis=1), - np.sum(new_source_vecs_async, axis=1)) - - ctx_vec_async, updated_states_async = l.ExtendStep( - l.theta, query_vec, cached_states, None, None, per_step_padding, - time_step, use_short_seq_opt) - _, updated_states_step1 = l.ExtendStep(l.theta, query_vec, cached_states, - None, None, per_step_padding, 1, - use_short_seq_opt) - - context_vec_out_async = sess.run(ctx_vec_async) - new_source_vecs_async = sess.run(updated_states_async.key) - - new_source_vecs_async_step1 = sess.run(updated_states_step1.key) - - context_vec_out_async = np.reshape(context_vec_out_async, (6, 4)) - self.assertAllClose( - [5.381485, -1.943824, 2.214111, 0.840045, -0.939259, 0.752783], - np.sum(context_vec_out_async, axis=1)) - # Updated status are the same at step 0. - self.assertAllClose(new_source_vecs_async[0][0], new_source_vecs[0][0]) - self.assertAllClose(new_source_vecs_async[1][1], - new_source_vecs_async_step1[1][1]) - - @parameterized.named_parameters({ - 'testcase_name': '_long_seq', - 'use_short_seq_opt': False, - }) - def testMultipleExtendStepAsyncTimeStepSelfAttention(self, use_short_seq_opt): - # input_batch:6, seq_len:6, query_len: 1. Test n = 2 case. - num_heads, input_dim, hidden_dim, batch, seqlen = 2, 4, 4, 6, 6 - with self.session(use_gpu=True): - tf.random.set_seed(12345) - (query_vec, _, paddings, _, _, _, _, _) = _AttentionInputs() - p = attention.MultiHeadedAttention.Params().Set( - name='atten', - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=hidden_dim) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - - tf.global_variables_initializer().run() - - # Verify ExtendStep() via compare N ExtendStep() with one FProp() call on - # a seq with length N. - per_step_padding = 1 - tf.linalg.band_part( - tf.ones((seqlen, seqlen)), -1, 0) - per_step_padding = tf.stack([per_step_padding] * batch) - dims_per_head = hidden_dim // num_heads - - def _ResetCachedStates(): - cached_source_vecs = tf.constant( - np.random.normal(0.1, 0.5, - [seqlen, batch, num_heads, dims_per_head]), - dtype=tf.float32) - cached_source_ctxs = tf.constant( - np.random.normal(0.1, 0.5, - [seqlen, batch, num_heads, dims_per_head]), - dtype=tf.float32) - cached_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_ctxs) - return cached_states - - encoded_all = [] - cached_states = _ResetCachedStates() - for i in range(seqlen): - per_step_paddings = 1. - tf.cast( - tf.sequence_mask([i + 1] * batch, seqlen), tf.float32) - per_step_paddings = tf.expand_dims(per_step_paddings, 1) - encoded, cached_states = l.ExtendStep(l.theta, query_vec[:, i:i + 1, :], - cached_states, paddings, None, - per_step_paddings, i) - # [batch, 1, dims_per_head] - encoded_all.append(encoded) - - encoded_all_async = [] - cached_states = _ResetCachedStates() - for i in range(seqlen): - # Sample 1 to batch -1 time step are synchoronized: 1 -> Seqlen - # Sample batch, the time step are [0, 0, 0, 1, .., Seqlen-2] - index = i - 3 if i > 2 else 0 - new_query_vec = tf.concat([ - query_vec[:(batch - 1), i:i + 1, :], query_vec[(batch - 1):, - index:index + 1, :] - ], - axis=0) - time_step = tf.constant([i] * (batch - 1) + [index], dtype=tf.int32) - per_step_paddings = 1. - tf.cast( - tf.sequence_mask([i + 1] * - (batch - 1) + [index + 1], seqlen), tf.float32) - per_step_paddings = tf.expand_dims(per_step_paddings, 1) - encoded, cached_states = l.ExtendStep(l.theta, new_query_vec, - cached_states, paddings, None, - per_step_paddings, time_step) - # [batch, 1, dims_per_head] - encoded_all_async.append(encoded) - # [batch, T, dims_per_head] - actual_ctx_vec = tf.concat(encoded_all, axis=1) - actual_ctx_vec_async = tf.concat(encoded_all_async, axis=1) - - self.assertAllClose(actual_ctx_vec_async.eval()[:-1], - actual_ctx_vec.eval()[:-1]) - # Sample batch move 3 step slower than the synchronized version. - self.assertAllClose(actual_ctx_vec_async.eval()[-1][3:], - actual_ctx_vec.eval()[-1][:3]) - - -class MultiSourceMultiHeadedAttentionTest(MultiHeadedAttentionTest): - - def testAttenProbs(self): - (query_vec, key_vec, paddings, per_step_padding, query_vec_p, key_vec_p, - paddings_p, per_step_padding_p) = _AttentionInputs() - - # Two-source attention. - mha_params = attention.MultiHeadedAttention.Params().Set( - name='atten', - input_dim=4, - hidden_dim=4, - enable_scaling_code_motion=True) - atten_merger_p = tm_attention.MergerLayer.Params().Set( - params_init=py_utils.WeightInit.Uniform(0.04), - merger_op='concat', # concatenate attention - pre_proj_input_dims=[4, 4], - pre_proj_output_dims=[4, 4]) - params = attention.MultiSourceAttention.Params().Set( - name='two_source_atten', - input_dim=4, - hidden_dim=4, - source_atten_tpls=[('src_1', mha_params), - ('src_2', mha_params.Copy().Set(name='atten2'))], - primary_source_key='src_1', - atten_merger_tpl=atten_merger_p) - l = params.Instantiate() - - probs, probs_sum = l.AttenProbs( - l.theta, - tf.expand_dims(query_vec, 2), - py_utils.NestedMap({ - 'src_1': tf.expand_dims(key_vec, 2), - 'src_2': tf.expand_dims(key_vec, 2) - }), - py_utils.NestedMap({ - 'src_1': paddings, - 'src_2': paddings - }), - segment_mask=None, - per_step_padding=per_step_padding) - - with self.session(use_gpu=False) as sess: - tf.global_variables_initializer().run() - prob_out = sess.run(tf.squeeze(probs / probs_sum)) - - # Use numpy to perform the same computation to generate expected results. - query_vec_p = np.array(query_vec_p) - key_vec_p = np.array(key_vec_p) - key_vec_p = np.transpose(key_vec_p, (0, 2, 1)) - expected_logit = np.matmul(query_vec_p, key_vec_p) - paddings_p = np.array(paddings_p) - paddings_p = np.expand_dims(paddings_p, axis=1) - paddings_p = np.tile(paddings_p, (1, 6, 1)) - per_step_padding_p = np.array(per_step_padding_p) - paddings_p = 1.0 * np.logical_or(paddings_p, per_step_padding_p) - elexp = np.exp(expected_logit) - elexp *= (1.0 - paddings_p) - elexp += 1e-9 - expected_prob_out = elexp / np.expand_dims(np.sum(elexp, axis=-1), axis=-1) - expected_prob_out = np.reshape(expected_prob_out, (6, 6, 6)) - self.assertAllClose(expected_prob_out, prob_out) - - def testFPropCrossAttention(self): - # input_batch:6, seq_len:6. Test n = 2 case. - with self.session(use_gpu=True) as sess: - query_vec, memory_vec, paddings, per_step_padding, _, _, _, _ = ( - _AttentionInputs()) - mha_params = attention.MultiHeadedAttention.Params().Set( - name='cross_atten', num_heads=2, input_dim=4, hidden_dim=4) - mha_params.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - atten_merger_p = tm_attention.MergerLayer.Params().Set( - params_init=py_utils.WeightInit.Uniform(0.04), - merger_op='concat', # concatenate attention - pre_proj_input_dims=[4, 4], - pre_proj_output_dims=[4, 4]) - # Two-source attention. - p = attention.MultiSourceAttention.Params().Set( - name='two_source_atten', - input_dim=4, - hidden_dim=4, - source_atten_tpls=[('src_1', mha_params), - ('src_2', mha_params.Copy().Set(name='atten2'))], - primary_source_key='src_1', - atten_merger_tpl=atten_merger_p) - l = p.Instantiate() - - tf.global_variables_initializer().run() - ctx_vec, _ = l.FProp( - l.theta, - query_vec, - py_utils.NestedMap({ - 'src_1': memory_vec, - 'src_2': memory_vec - }), - py_utils.NestedMap({ - 'src_1': memory_vec, - 'src_2': memory_vec - }), - py_utils.NestedMap({ - 'src_1': paddings, - 'src_2': paddings - }), - segment_mask=None, - per_step_padding=per_step_padding) - context_vec_out = sess.run(ctx_vec) - context_vec_out = np.reshape(context_vec_out, (12, 24)) - self.assertAllClose([ - 5.6162043, 5.0109887, 6.0565553, 6.0565553, 4.5718207, 5.253615, - 2.0541124, 2.490314, 6.049119, 5.5567484, 4.409875, 5.8939424 - ], np.sum(context_vec_out, axis=1)) - - -class MultiHeadedAttentionXLTest(test_utils.TestCase, parameterized.TestCase): - """Test dot-product multiheaded attention.""" - - def _AttentionExtendStepInputs(self, - input_dim, - batch_size, - seq_len, - dtype=tf.float32): - np.random.seed(6348575) - query_vec_p = [ - np.random.rand(seq_len, input_dim) for _ in range(batch_size) - ] - query_vec = tf.stack([tf.constant(x, dtype=dtype) for x in query_vec_p]) - paddings_p = [[0] * seq_len] * batch_size - paddings = tf.constant(paddings_p, dtype=dtype) - return query_vec, paddings - - @parameterized.named_parameters(('OneHead', 1), ('OneHeadCausal', 1, True), - ('MultiHead', 2), - ('MultiHeadCausal', 2, True)) - def testAttenProbs(self, num_heads, is_causal=False): - batch, slen = 6, 6 - atten_dim = 4 - input_dim = num_heads * atten_dim - (input_vecs, _, input_padding, per_step_padding, input_vecs_p, _, - input_padding_p, per_step_padding_p) = _AttentionInputs( - input_dim=input_dim, is_causal=is_causal) - p = attention.MultiHeadedAttentionXL.Params().Set( - name='self_atten', - input_dim=input_dim, - num_heads=num_heads, - hidden_dim=input_dim, - rel_pos_emb_dim=input_dim, - enable_scaling_code_motion=True) - - l = p.Instantiate() - query = tf.reshape(input_vecs, (batch, slen, num_heads, atten_dim)) - probs, probs_sum = l.AttenProbs( - l.theta, - query, - query, - input_padding, - segment_mask=None, - per_step_padding=per_step_padding) - - # [1, 2 * slen - 1] - positions = np.expand_dims(np.arange(-(slen - 1), slen), 0) - sinusoid_emb = l.pos_emb.FPropWithPosition(l.theta.pos_emb, - tf.convert_to_tensor(positions)) - # [ 2 * slen - 1, emb_dim=input_dim] - sinusoid_emb = tf.squeeze(sinusoid_emb, 0) - - with self.session(use_gpu=False) as sess: - tf.global_variables_initializer().run() - u, v, pos_proj = sess.run([l.vars.u, l.vars.v, l.pos_proj.vars.w]) - actual_probs = sess.run(probs / probs_sum) - sinusoid_emb_p = sess.run(sinusoid_emb) - - # Compute ground truth with oracle class. - - # Use numpy to perform the same computation to generate expected results. - # [B, tgt_t, H] - input_vecs_p = np.array(input_vecs_p) - # [B, tgt_t, N, H] - input_vecs_p = np.reshape(input_vecs_p, (batch, slen, num_heads, atten_dim)) - input_padding_p = np.array(input_padding_p) - oracle = MultiHeadedAttentionXLOracle(u, v, pos_proj, sinusoid_emb_p) - expected_probs = oracle.AttenProbs(input_vecs_p, input_vecs_p, - input_padding_p, per_step_padding_p) - self.assertAllClose(expected_probs, actual_probs) - - def testFPropSelfAttention(self): - # input_batch:6, seq_len:6. Test n = 2 case. - with self.session(use_gpu=True) as sess: - query_vec, _, paddings, _, _, _, _, _ = _AttentionInputs() - num_heads, input_dim, hidden_dim = 2, 4, 4 - p = attention.MultiHeadedAttentionXL.Params().Set( - name='self_atten', - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=hidden_dim, - rel_pos_emb_dim=num_heads * hidden_dim) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - - l = p.Instantiate() - ctx_vec, _ = l.FPropDefaultTheta( - query_vec, query_vec, query_vec, paddings, segment_mask=None) - - tf.global_variables_initializer().run() - context_vec_out = sess.run(ctx_vec) - context_vec_out = np.reshape(context_vec_out, (6, 24)) - self.assertAllClose( - [32.33513, 28.584404, 20.54517, 23.407812, 18.616188, 24.212755], - np.sum(context_vec_out, axis=1)) - - def testExtendStepAsyncTimeStepSelfAttention(self): - num_heads, input_dim, hidden_dim, batch, seqlen = 2, 4, 4, 6, 6 - emb_dim = 4 - with self.session(use_gpu=True): - tf.random.set_seed(12345) - query_vec, paddings = self._AttentionExtendStepInputs( - input_dim, batch, seqlen) - p = attention.MultiHeadedAttentionXL.Params().Set( - name='atten', - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=hidden_dim, - rel_pos_emb_dim=emb_dim, - random_seed=0) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - - tf.global_variables_initializer().run() - - # Verify ExtendStep() via compare N ExtendStep() with one FProp() call on - # a seq with length N. - per_step_padding = 1 - tf.linalg.band_part( - tf.ones((seqlen, seqlen)), -1, 0) - per_step_padding = tf.stack([per_step_padding] * batch) - dims_per_head = hidden_dim // num_heads - - def _ResetCachedStates(): - cached_source_vecs = tf.constant( - np.random.normal(0.1, 0.5, - [seqlen, batch, num_heads, dims_per_head]), - dtype=tf.float32) - cached_source_ctxs = tf.constant( - np.random.normal(0.1, 0.5, - [seqlen, batch, num_heads, dims_per_head]), - dtype=tf.float32) - cached_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_ctxs) - return cached_states - - encoded_all = [] - cached_states = _ResetCachedStates() - for i in range(seqlen): - per_step_paddings = 1. - tf.cast( - tf.sequence_mask([i + 1] * batch, seqlen), tf.float32) - per_step_paddings = tf.expand_dims(per_step_paddings, 1) - encoded, cached_states = l.ExtendStep(l.theta, query_vec[:, i:i + 1, :], - cached_states, paddings, None, - per_step_paddings, i) - # [batch, 1, dims_per_head] - encoded_all.append(encoded) - - encoded_all_async = [] - cached_states = _ResetCachedStates() - for i in range(seqlen): - # Sample 1 to batch -1 time step are synchoronized: 1 -> Seqlen - # Sample batch, the time step are [0, 0, 0, 1, .., Seqlen-2] - index = i - 3 if i > 2 else 0 - new_query_vec = tf.concat([ - query_vec[:(batch - 1), i:i + 1, :], query_vec[(batch - 1):, - index:index + 1, :] - ], - axis=0) - time_step = tf.constant([i] * (batch - 1) + [index], dtype=tf.int32) - per_step_paddings = 1. - tf.cast( - tf.sequence_mask([i + 1] * - (batch - 1) + [index + 1], seqlen), tf.float32) - per_step_paddings = tf.expand_dims(per_step_paddings, 1) - encoded, cached_states = l.ExtendStep(l.theta, new_query_vec, - cached_states, paddings, None, - per_step_paddings, time_step) - # [batch, 1, dims_per_head] - encoded_all_async.append(encoded) - # [batch, T, dims_per_head] - actual_ctx_vec = tf.concat(encoded_all, axis=1) - actual_ctx_vec_async = tf.concat(encoded_all_async, axis=1) - - self.assertAllClose(actual_ctx_vec_async.eval()[:-1], - actual_ctx_vec.eval()[:-1]) - # Sample batch move 3 step slower than the synchronized version. - self.assertAllClose(actual_ctx_vec_async.eval()[-1][3:], - actual_ctx_vec.eval()[-1][:3]) - - def testExtendStepSelfAttention(self): - num_heads, input_dim, hidden_dim, batch, seqlen = 2, 4, 4, 6, 6 - emb_dim = 4 - with self.session(use_gpu=True): - tf.random.set_seed(12345) - query_vec, paddings = self._AttentionExtendStepInputs( - input_dim, batch, seqlen) - p = attention.MultiHeadedAttentionXL.Params().Set( - name='atten', - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=hidden_dim, - rel_pos_emb_dim=emb_dim, - random_seed=0) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - tf.global_variables_initializer().run() - - # Verify ExtendStep() via compare N ExtendStep() with one FProp() call on - # a seq with length N. - per_step_padding = 1 - tf.linalg.band_part( - tf.ones((seqlen, seqlen)), -1, 0) - per_step_padding = tf.stack([per_step_padding] * batch) - expected_ctx_vec, _ = l.FPropDefaultTheta( - query_vec, - query_vec, - query_vec, - paddings, - segment_mask=None, - per_step_padding=per_step_padding) - dims_per_head = hidden_dim // num_heads - cached_source_vecs = tf.constant( - np.random.normal(0.1, 0.5, [seqlen, batch, num_heads, dims_per_head]), - dtype=tf.float32) - cached_source_ctxs = tf.constant( - np.random.normal(0.1, 0.5, [seqlen, batch, num_heads, dims_per_head]), - dtype=tf.float32) - cached_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_ctxs) - - encoded_all = [] - for i in range(seqlen): - per_step_paddings = 1. - tf.cast( - tf.sequence_mask([i + 1] * batch, seqlen), tf.float32) - per_step_paddings = tf.expand_dims(per_step_paddings, 1) - encoded, cached_states = l.ExtendStep(l.theta, query_vec[:, i:i + 1, :], - cached_states, paddings, None, - per_step_paddings, i) - # [batch, 1, dims_per_head] - encoded_all.append(encoded) - # [batch, T, dims_per_head] - actual_ctx_vec = tf.concat(encoded_all, axis=1) - self.assertAllClose(expected_ctx_vec.eval(), actual_ctx_vec.eval()) - - -class MultiHeadedAttentionRPEOracle: - """Computes ground truths for MultiHeadedfAttentionRPE. - - Written in a non-vectorized way. - """ - - def __init__(self, num_heads, key_embs, value_embs): - """Constructor. - - Args: - num_heads: A Python int. - key_embs: A numpy array of shape [2 * radius + 1, hidden_dim] - value_embs: A numpy array of shape [2 * radius + 1, hidden_dim] - """ - assert key_embs.shape == value_embs.shape - self._num_heads = num_heads - self._hidden_dim = key_embs.shape[-1] - self._atten_dim = self._hidden_dim // self._num_heads - assert self._atten_dim * self._num_heads == self._hidden_dim - - self._key_embs = np.reshape( - key_embs, [key_embs.shape[0], self._num_heads, self._atten_dim]) - self._value_embs = np.reshape( - value_embs, [value_embs.shape[0], self._num_heads, self._atten_dim]) - self._radius = key_embs.shape[0] // 2 - - def _GetEmb(self, tgt_t, src_t, head, emb_wt): - radius = self._radius - distance = np.clip(src_t - tgt_t, -radius, radius) - return emb_wt[distance][head] - - def GetKeyEmb(self, tgt_t, src_t, head): - return self._GetEmb(tgt_t, src_t, head, self._key_embs) - - def GetValueEmb(self, tgt_t, src_t, head): - return self._GetEmb(tgt_t, src_t, head, self._value_embs) - - def AttenProbs(self, key, query, paddings): - assert query.ndim == 4 - assert paddings.ndim == 2 - assert key.shape == query.shape - - batch, seqlen = query.shape[:2] - tgtlen, srclen = seqlen, seqlen - assert query.shape[2] == self._num_heads - assert query.shape[3] == self._atten_dim - assert paddings.shape == query.shape[:2] - - # [B, N, T, T] - logits = np.zeros((batch, self._num_heads, tgtlen, srclen)) - # [B, N, T, T] - probs = np.zeros((batch, self._num_heads, tgtlen, srclen)) - - paddings = np.broadcast_to( - np.reshape(paddings, (batch, 1, 1, seqlen)), - (batch, self._num_heads, seqlen, seqlen)) - - def Normalize(vec): - expx = np.exp(vec) - expxsum = np.sum(expx, axis=-1) - return expx / expxsum - - for b in range(batch): - for h in range(self._num_heads): - for i in range(tgtlen): - for j in range(srclen): - logits[b][h][i][j] = np.dot(query[b][i][h], - key[b][j][h] + self.GetKeyEmb(i, j, h)) - logits[b][h][i] = np.where(paddings[b][h][i] > 0, - np.finfo(np.float32).max * (-0.7), - logits[b][h][i]) - probs[b][h][i] = Normalize(logits[b][h][i]) - return probs - - def AttenContext(self, probs, values): - assert probs.ndim == 4 - assert values.ndim == 4 - - assert probs.shape[0] == values.shape[0] # batch - assert probs.shape[1] == values.shape[2] # head - assert probs.shape[2] == values.shape[1] # tgtlen - assert probs.shape[3] == probs.shape[2] # slen - assert values.shape[-1] == self._atten_dim - - batch, _, tgtlen, srclen = probs.shape - # [B, N, T, H] - ctx = np.zeros((batch, self._num_heads, tgtlen, self._atten_dim)) - for b in range(batch): - for h in range(self._num_heads): - for i in range(tgtlen): - for j in range(srclen): - ctx[b][h][i] += probs[b][h][i][j] * ( - values[b][j][h] + self.GetValueEmb(i, j, h)) - # [B, T, N, H] - return np.transpose(ctx, (0, 2, 1, 3)) - - -class MultiHeadedAttentionRPETest(test_utils.TestCase, parameterized.TestCase): - - @parameterized.named_parameters(('OneHead', 1), ('MultiHead', 2)) - def testAttenProbs(self, num_heads): - batch, slen = 6, 6 - atten_dim = 4 - radius = 3 - input_dim = num_heads * atten_dim - (input_vecs, _, input_padding, _, input_vecs_p, _, input_padding_p, - _) = _AttentionInputs(input_dim=input_dim) - p = attention.MultiHeadedAttentionRPE.Params().Set( - name='self_atten', - input_dim=input_dim, - num_heads=num_heads, - hidden_dim=input_dim, - rel_pos_radius=radius, - enable_scaling_code_motion=True) - - l = p.Instantiate() - query = tf.reshape(input_vecs, (batch, slen, num_heads, atten_dim)) - probs, probs_sum = l.AttenProbs( - l.theta, query, query, input_padding, segment_mask=None) - - with self.session(use_gpu=False) as sess: - tf.global_variables_initializer().run() - # [radius * 2 + 1, hidden_dim], [B, tgt_t, src_t] - key_emb, value_emb, actual_probs = sess.run( - [l.key_emb.vars.w, l.value_emb.vars.w, probs / probs_sum]) - - oracle = MultiHeadedAttentionRPEOracle(num_heads, key_emb, value_emb) - - # Use numpy to perform the same computation to generate expected results. - # [B, tgt_t, N, H] - input_vecs_p = np.reshape(input_vecs_p, (batch, slen, num_heads, atten_dim)) - expected_probs = oracle.AttenProbs(input_vecs_p, input_vecs_p, - input_padding_p) - self.assertAllClose(expected_probs, actual_probs) - - @parameterized.named_parameters(('OneHead', 1), ('MultiHead', 2)) - def testAttenContext(self, num_heads): - batch, slen = 6, 6 - atten_dim = 4 - radius = 3 - input_dim = num_heads * atten_dim - (input_vecs, _, _, _, input_vecs_p, _, _, - _) = _AttentionInputs(input_dim=input_dim) - p = attention.MultiHeadedAttentionRPE.Params().Set( - name='self_atten', - input_dim=input_dim, - num_heads=num_heads, - hidden_dim=input_dim, - rel_pos_radius=radius) - - l = p.Instantiate() - probs = np.random.rand(batch, num_heads, slen, slen).astype(np.float32) - probs = np.exp(probs) / np.sum(np.exp(probs), axis=-1, keepdims=True) - ctx = l._AttenContext( - l.theta, tf.convert_to_tensor(probs), - tf.reshape(input_vecs, (batch, slen, num_heads, atten_dim))) - - with self.session(use_gpu=False) as sess: - tf.global_variables_initializer().run() - key_emb, value_emb, actual_ctx = sess.run( - [l.key_emb.vars.w, l.value_emb.vars.w, ctx]) - - oracle = MultiHeadedAttentionRPEOracle(num_heads, key_emb, value_emb) - - # [B, tgt_t, N, H] - input_vecs_p = np.reshape(input_vecs_p, (batch, slen, num_heads, atten_dim)) - expected_ctx = oracle.AttenContext(probs, input_vecs_p) - self.assertAllClose(expected_ctx, actual_ctx) - - @parameterized.named_parameters(('OneHead', 1), ('MultiHead', 2)) - def testAttenLogitsOneStep(self, num_heads): - batch, slen = 6, 6 - atten_dim = 4 - radius = 3 - input_dim = num_heads * atten_dim - (input_vecs, _, _, _, _, _, _, _) = _AttentionInputs( - input_dim=input_dim, is_causal=True) - p = attention.MultiHeadedAttentionRPE.Params().Set( - name='self_atten', - input_dim=input_dim, - num_heads=num_heads, - hidden_dim=input_dim, - rel_pos_radius=radius) - - l = p.Instantiate() - # [B, T, N, H] - query = tf.reshape(input_vecs, (batch, slen, num_heads, atten_dim)) - - # Causal self attention. - # [B, N, T, S] - logits = l._AttenLogits( - l.theta, - query, - query, - ) - - one_step_logits = [] - # [S=T, B, N, H] - key = tf.transpose(query, [1, 0, 2, 3]) - for i in range(slen): - local_logits = l._AttenLogitsOneStep(l.theta, query[:, i, :, :], key, i) - one_step_logits.append(local_logits) - # [T, S, B, N] - stacked_logits = tf.stack(one_step_logits) - stacked_logits = tf.transpose(stacked_logits, [2, 3, 0, 1]) - - with self.session(use_gpu=False) as sess: - tf.global_variables_initializer().run() - expected_logits, actual_logits = sess.run([logits, stacked_logits]) - self.assertAllClose(expected_logits, actual_logits) - - @parameterized.named_parameters(('OneHead', 1), ('MultiHead', 2)) - def testAttenContextsOneStep(self, num_heads): - batch, slen = 6, 6 - atten_dim = 4 - radius = 3 - input_dim = num_heads * atten_dim - (input_vecs, _, _, per_step_padding, _, _, _, _) = _AttentionInputs( - input_dim=input_dim, is_causal=True) - p = attention.MultiHeadedAttentionRPE.Params().Set( - name='self_atten', - input_dim=input_dim, - num_heads=num_heads, - hidden_dim=input_dim, - rel_pos_radius=radius) - - l = p.Instantiate() - # [B, N, T, S=T] - # Make causal attention probs. - probs = np.random.rand(batch, num_heads, slen, slen).astype(np.float32) - per_step_padding = 1 - np.tril(np.ones((slen, slen))).astype(np.float32) - probs *= per_step_padding - # Normalize - probs = np.exp(probs) / np.sum(np.exp(probs), axis=-1, keepdims=True) - - # Causal self attention. - # [B, N, T, S] - ctx = l._AttenContext( - l.theta, tf.convert_to_tensor(probs), - tf.reshape(input_vecs, (batch, slen, num_heads, atten_dim))) - - one_step_ctx = [] - # [B, T, N, H] -> [S=T, B, N, H] - value = tf.reshape(input_vecs, (batch, slen, num_heads, atten_dim)) - value = tf.transpose(value, [1, 0, 2, 3]) - for i in range(slen): - # [B, N, S] - local_prob = probs[:, :, i, :] - # [S, B, N] - local_prob = tf.transpose(local_prob, [2, 0, 1]) - # [B, N, H] - local_ctx = l._AttenContextOneStep(l.theta, local_prob, value, i) - one_step_ctx.append(local_ctx) - # [T, B, N, H] - stacked_ctx = tf.stack(one_step_ctx) - stacked_ctx = tf.transpose(stacked_ctx, [1, 0, 2, 3]) - - with self.session(use_gpu=False) as sess: - tf.global_variables_initializer().run() - expected_ctx, actual_ctx = sess.run([ctx, stacked_ctx]) - self.assertAllClose(expected_ctx, actual_ctx) - - -class LocalSelfAttentionTest(test_utils.TestCase, parameterized.TestCase): - """Test local causual self attention.""" - - def _LocalCasualPadding(self, b, t, l, r): - padding = np.ones((b, t, t)) - for i in range(t): - padding[:, i, max(0, i - l + 1):i + r + 1] = 0 - return tf.constant(padding, dtype=tf.float32) - - @parameterized.named_parameters( - { - 'testcase_name': 'block_size_unspecified', - 'block_size': None, - 'left_context': 4, - 'right_context': 1 - }, { - 'testcase_name': 'left_context_only', - 'block_size': 3, - 'left_context': 4, - 'right_context': 0, - }, { - 'testcase_name': 'block_longer_than_sequence', - 'block_size': 10, - 'left_context': 7, - 'right_context': 0, - }, { - 'testcase_name': 'pos_emb_left_context_only', - 'block_size': 3, - 'left_context': 4, - 'right_context': 0, - 'pos_emb_dim': 8, - }, { - 'testcase_name': 'pos_emb_left_and_right_context', - 'block_size': 3, - 'left_context': 4, - 'right_context': 2, - 'pos_emb_dim': 8, - }, { - 'testcase_name': 'lite_pos_emb_left_and_right_context', - 'block_size': 3, - 'left_context': 4, - 'right_context': 2, - 'pos_emb_dim': 8, - 'skip_term_b': True, - }) - def testFPropAgainstReference(self, - block_size, - left_context, - right_context, - pos_emb_dim=0, - num_heads=2, - input_dim=4, - hidden_dim=4, - skip_term_b=False, - use_additional_per_step_padding=False): - tf.reset_default_graph() - with self.session(use_gpu=True) as sess: - query_vec, _, paddings, _, _, _, _, _ = _AttentionInputs(input_dim) - if use_additional_per_step_padding: - # Generate a random binary mask of shape [N, T, S]. - additional_per_step_padding_val = np.random.random_integers( - low=0, high=1, size=(6, 6, 6)) - additional_per_step_padding = tf.constant( - additional_per_step_padding_val, tf.float32) - else: - additional_per_step_padding = None - - # Use the reference implementation + local casual padding to verify - # correctness. - if pos_emb_dim == 0: - p_cls = attention.LocalSelfAttention - expected_p_cls = attention.MultiHeadedAttention - else: - p_cls = attention.LocalSelfAttentionXL - expected_p_cls = attention.MultiHeadedAttentionXL - p = p_cls.Params().Set( - name='self_atten', - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=hidden_dim, - block_size=block_size, - left_context=left_context, - right_context=right_context) - expected_p = expected_p_cls.Params().Set( - name='expected_self_atten', - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=hidden_dim) - if pos_emb_dim != 0: - p.rel_pos_emb_dim = pos_emb_dim - expected_p.rel_pos_emb_dim = pos_emb_dim - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - expected_p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - - l = p.Instantiate() - expected_l = expected_p.Instantiate() - - tf.global_variables_initializer().run() - ctx_vec, _ = l.FProp( - l.theta, - query_vec, - query_vec, - query_vec, - paddings, - segment_mask=None, - per_step_padding=additional_per_step_padding) - context_vec_out = sess.run(ctx_vec) - per_step_padding = self._LocalCasualPadding(6, 6, left_context, - right_context) - if additional_per_step_padding is not None: - per_step_padding += additional_per_step_padding - expected_ctx_vec, _ = expected_l.FProp(expected_l.theta, query_vec, - query_vec, query_vec, paddings, - None, per_step_padding) - expected_context_vec_out = sess.run(expected_ctx_vec) - - # Don't compare if the query position is padded, or if all key positions - # are padded. - paddings_val = sess.run(paddings) - per_step_padding_val = sess.run(per_step_padding) - per_step_padding_val += paddings_val[:, :, np.newaxis] - per_step_padding_val += paddings_val[:, np.newaxis, :] - - dont_compare = np.sum( - per_step_padding_val > 0, axis=-1) == per_step_padding_val.shape[-1] - expected_context_vec_out *= (1 - dont_compare)[..., np.newaxis] - context_vec_out *= (1 - dont_compare)[..., np.newaxis] - self.assertAllClose(context_vec_out, expected_context_vec_out) - - def testFPropWithDropout(self): - with self.session(use_gpu=True) as sess: - query_vec, _, paddings, _, _, _, _, _ = _AttentionInputs(input_dim=4) - p = attention.LocalSelfAttention.Params().Set( - name='self_atten', - num_heads=2, - input_dim=4, - hidden_dim=4, - block_size=2, - left_context=2, - right_context=0, - atten_dropout_prob=0.3, - ) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - tf.global_variables_initializer().run() - ctx_vec, _ = l.FProp( - l.theta, query_vec, query_vec, query_vec, paddings, segment_mask=None) - ctx_vec_val = sess.run(ctx_vec) - print(ctx_vec_val) - - def _AttentionExtendStepInputs(self, - batch_size=6, - input_dim=4, - num_heads=2, - dtype=tf.float32): - np.random.seed(6348575) - seq_len = 6 - query_vec_p = [np.random.rand(1, input_dim) for _ in range(batch_size)] - query_vec = tf.stack([tf.constant(x, dtype=dtype) for x in query_vec_p]) - source_vecs = tf.constant( - np.random.normal( - 0.1, 0.5, [seq_len, batch_size, num_heads, input_dim // num_heads]), - dtype=dtype) - source_ctxs = tf.constant( - np.random.normal( - 0.1, 0.5, [seq_len, batch_size, num_heads, input_dim // num_heads]), - dtype=dtype) - cached_states = py_utils.NestedMap(key=source_vecs, value=source_ctxs) - return query_vec, cached_states - - def testExtendStepSelfAttention(self): - # input_batch:6, seq_len:6, query_len: 1. Test n = 2 case. - batch_size = 6 - input_dim = 4 - num_heads = 2 - with self.session(use_gpu=True) as sess: - query_vec, cached_states = ( - self._AttentionExtendStepInputs( - batch_size=batch_size, input_dim=input_dim, num_heads=num_heads)) - p = attention.LocalSelfAttention.Params().Set( - name='self_atten', - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=4, - block_size=2, - left_context=2, - right_context=0, - atten_dropout_prob=0.3, - ) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - tf.global_variables_initializer().run() - ctx_vec, updated_states = l.ExtendStep( - l.theta, - query_vec, - cached_states, - paddings=None, - segment_mask=None, - per_step_padding=None, - time_step=3, - use_short_seq_opt=False) - context_vec_out = sess.run(ctx_vec) - new_source_vecs = sess.run(updated_states.key) - context_vec_out = np.reshape(context_vec_out, (6, 4)) - - tf.logging.info(np.array_repr(np.sum(context_vec_out, axis=1))) - self.assertAllClose( - [3.303124, 3.90266, 2.971359, 2.486641, 3.109267, 1.54773], - np.sum(context_vec_out, axis=1)) - new_source_vecs = np.reshape(new_source_vecs, (6, 24)) - tf.logging.info(np.array_repr(np.sum(new_source_vecs, axis=1))) - self.assertAllClose( - [5.135725, 1.340482, 1.065773, 4.116683, 4.928454, 3.161165], - np.sum(new_source_vecs, axis=1)) - - def testStreamingExtendStep(self): - batch_size = 1 - input_dim = 4 - hidden_dim = 4 - num_heads = 2 - seq_length = 5 - left_context = 2 - with self.session(use_gpu=True): - # Prepares inputs. - tf.random.set_seed(12345) - np.random.seed(123456789) - inputs = tf.constant( - np.random.normal(0.1, 0.5, [batch_size, seq_length, input_dim]), - dtype=tf.float32) - paddings = tf.zeros(shape=[batch_size, seq_length], dtype=tf.float32) - - # Prepares the model. - p = attention.LocalSelfAttention.Params().Set( - name='local_self_atten', - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=hidden_dim, - left_context=left_context, - right_context=0, - ) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - model = p.Instantiate() - self.evaluate(tf.global_variables_initializer()) - - outputs, _ = model.FProp(model.theta, inputs, inputs, inputs, paddings) - outputs_shape = py_utils.GetShape(outputs) - self.assertListEqual([batch_size, seq_length, input_dim], outputs_shape) - - # Streaming inference - state = model.zero_state(batch_size) - for i in range(0, seq_length): - time_step = tf.constant([i], dtype=tf.int32) - i_input = inputs[:, i:(i + 1), :] # [B, 1, D] - i_output, state = model.StreamingExtendStep(i_input, state, time_step) - i_output_shape = py_utils.GetShape(i_output) - self.assertListEqual([batch_size, 1, input_dim], i_output_shape) - - i_expected_output = outputs[:, i:(i + 1), :] - v, expected_v = self.evaluate([i_output, i_expected_output]) - self.assertAllClose(v, expected_v) - - -class LocalSelfAttentionXLTest(test_utils.TestCase, parameterized.TestCase): - """Test local causual self attention with relative pos embedding.""" - - def testStreamingExtendStep(self): - batch_size = 1 - input_dim = 4 - hidden_dim = 4 - num_heads = 2 - seq_length = 5 - left_context = 2 - rel_pos_emb_dim = 2 - with self.session(use_gpu=True): - # Prepares inputs. - tf.random.set_seed(12345) - np.random.seed(123456789) - inputs = tf.constant( - np.random.normal(0.1, 0.5, [batch_size, seq_length, input_dim]), - dtype=tf.float32) - paddings = tf.zeros(shape=[batch_size, seq_length], dtype=tf.float32) - - # Prepares the model. - p = attention.LocalSelfAttentionXL.Params().Set( - name='local_self_atten_xl', - rel_pos_emb_dim=rel_pos_emb_dim, - num_heads=num_heads, - input_dim=input_dim, - hidden_dim=hidden_dim, - left_context=left_context, - right_context=0, - ) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - model = p.Instantiate() - self.evaluate(tf.global_variables_initializer()) - - outputs, _ = model.FProp(model.theta, inputs, inputs, inputs, paddings) - outputs_shape = py_utils.GetShape(outputs) - self.assertListEqual([batch_size, seq_length, input_dim], outputs_shape) - - # Streaming inference - state = model.zero_state(batch_size) - for i in range(0, seq_length): - time_step = tf.constant([i], dtype=tf.int32) - i_input = inputs[:, i:(i + 1), :] # [B, 1, D] - i_output, state = model.StreamingExtendStep(i_input, state, time_step) - i_output_shape = py_utils.GetShape(i_output) - self.assertListEqual([batch_size, 1, input_dim], i_output_shape) - - i_expected_output = outputs[:, i:(i + 1), :] - v, expected_v = self.evaluate([i_output, i_expected_output]) - self.assertAllClose(v, expected_v) - - -class RoutingAttentionTest(test_utils.TestCase, parameterized.TestCase): - """Tests for RoutingAttention.""" - - def testDotAttenSlow(self): - batch_size = 7 - source_length = 6 - target_length = 4 - num_heads = 2 - dim_per_head = 5 - num_clusters = 3 - attention_window = 4 - q = np.random.rand(batch_size, target_length, num_heads, - dim_per_head).astype(np.float32) - k = np.random.rand(batch_size, source_length, num_heads, - dim_per_head).astype(np.float32) - v = np.random.rand(batch_size, source_length, num_heads, - dim_per_head).astype(np.float32) - query_paddings = np.zeros([batch_size, target_length], dtype=np.float32) - key_paddings = np.zeros([batch_size, source_length], dtype=np.float32) - p = attention.RoutingAttention.Params().Set( - name='routing_atten', - input_dim=1, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads, - num_clusters=num_clusters, - attention_window=attention_window, - fast_path=False) - atten = p.Instantiate() - with self.session() as sess: - tf.global_variables_initializer().run() - encoded, probs = sess.run( - atten._DotAtten( - atten.theta, q, k, v, key_paddings, - query_paddings=query_paddings)) - self.assertEqual(encoded.shape, - (batch_size, target_length, num_heads, dim_per_head)) - self.assertEqual(probs.shape, - (batch_size, target_length, num_heads, source_length)) - # attention weights sum to 1. - self.assertAllClose( - np.sum(probs, axis=-1), - np.ones([batch_size, target_length, num_heads])) - - def testDotAttenFast(self): - batch_size = 6 - source_length = 8 - target_length = 7 - num_heads = 3 - dim_per_head = 5 - num_clusters = 2 - attention_window = source_length - q = np.random.rand(batch_size, target_length, num_heads, - dim_per_head).astype(np.float32) - k = np.random.rand(batch_size, source_length, num_heads, - dim_per_head).astype(np.float32) - v = np.random.rand(batch_size, source_length, num_heads, - dim_per_head).astype(np.float32) - - q_paddings = np.zeros([batch_size, target_length], dtype=np.float32) - k_paddings = np.zeros([batch_size, source_length], dtype=np.float32) - p = attention.RoutingAttention.Params().Set( - name='routing_atten', - input_dim=1, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads, - num_clusters=num_clusters, - attention_window=attention_window, - query_group_size_factor=1.5, # each group has 6 queries: 8 / 2 * 1.5. - fast_path=True) - atten = p.Instantiate() - # increase group size to 7. - atten2 = p.Copy().Set( - name='increase_group_size_routing_atten', - query_group_size_factor=1.75).Instantiate() - p = attention.MultiHeadedAttention.Params().Set( - name='full_atten', - input_dim=1, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads) - full_atten = p.Instantiate() - with self.session() as sess: - tf.global_variables_initializer().run() - encoded, probs = sess.run( - atten._DotAtten( - atten.theta, q, k, v, k_paddings, query_paddings=q_paddings)) - self.assertEqual(encoded.shape, - (batch_size, target_length, num_heads, dim_per_head)) - self.assertEqual(probs.shape, - (batch_size, target_length, num_heads, source_length)) - _, probs2 = sess.run( - atten2._DotAtten( - atten2.theta, q, k, v, k_paddings, query_paddings=q_paddings)) - # In order to match the full attention, we apply layer norm first. - q_ln = attention_util.KMeansClusteringForAtten.LayerNorm(q) - k_ln = attention_util.KMeansClusteringForAtten.LayerNorm(k) - full_encoded_t, full_probs_t = full_atten._DotAtten( - full_atten.theta, q_ln, k_ln, v, k_paddings, None) - full_probs_t = tf.transpose(full_probs_t, [0, 2, 1, 3]) - full_probs, full_encoded = sess.run([full_probs_t, full_encoded_t]) - - # When we increase p.query_group_size_factor, the number of left out queries - # decreases. - self.assertLess(np.sum(probs), np.sum(probs2)) - for batch_idx in range(batch_size): - for time_idx in range(target_length): - for head_idx in range(num_heads): - sub_probs = probs[batch_idx, time_idx, head_idx, :] - sub_encoded = encoded[batch_idx, time_idx, head_idx, :] - # encoded output is either 0 or matching full attention output - # for each query position. - if np.allclose(sub_probs, np.zeros_like(sub_probs)): - self.assertAllClose(sub_encoded, np.zeros_like(sub_encoded)) - continue - self.assertAllClose(sub_probs, full_probs[batch_idx, time_idx, - head_idx, :]) - self.assertAllClose(sub_encoded, full_encoded[batch_idx, time_idx, - head_idx, :]) - - @parameterized.parameters((False, 0), (False, 1), (False, 2), (True, 0), - (True, 1), (True, 2)) - def testDotAttenFull(self, fast_path, num_padded): - batch_size = 2 - source_length = 5 - target_length = 6 - num_heads = 2 - dim_per_head = 5 - # fast_path=True with multiple clusters might leave out some queries. - # For the purpose of this test we only use a single cluster. - num_clusters = 1 if fast_path else 3 - attention_window = source_length - q = tf.random.normal( - shape=[batch_size, target_length, num_heads, dim_per_head]) - k = tf.random.normal( - shape=[batch_size, source_length, num_heads, dim_per_head]) - v = tf.random.normal( - shape=[batch_size, source_length, num_heads, dim_per_head]) - - q_paddings = np.zeros([batch_size, target_length], dtype=np.float32) - k_paddings = np.zeros([batch_size, source_length], dtype=np.float32) - if num_padded: - # randomly pad elements. - for i in range(batch_size): - zero_index = np.random.choice(source_length, num_padded, False) - for j in zero_index: - k_paddings[i, j] = 1. - p = attention.RoutingAttention.Params().Set( - name='routing_atten', - input_dim=1, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads, - num_clusters=num_clusters, - attention_window=attention_window, - query_group_size_factor=1.0, - fast_path=fast_path) - atten = p.Instantiate() - p = attention.MultiHeadedAttention.Params().Set( - name='full_atten', - input_dim=1, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads) - full_atten = p.Instantiate() - with self.session() as sess: - tf.global_variables_initializer().run() - encoded_t, probs_t = atten._DotAtten( - atten.theta, q, k, v, k_paddings, query_paddings=q_paddings) - gradients_t = tf.gradients(encoded_t, [q, k, v]) - # In order to match the full attention, we apply layer norm first. - q_ln = attention_util.KMeansClusteringForAtten.LayerNorm(q) - k_ln = attention_util.KMeansClusteringForAtten.LayerNorm(k) - full_encoded_t, full_probs_t = full_atten._DotAtten( - full_atten.theta, q_ln, k_ln, v, k_paddings, None) - full_probs_t = tf.transpose(full_probs_t, [0, 2, 1, 3]) - full_gradients_t = tf.gradients(full_encoded_t, [q, k, v]) - (encoded, probs, full_encoded, full_probs, gradients, - full_gradients) = sess.run([ - encoded_t, probs_t, full_encoded_t, full_probs_t, gradients_t, - full_gradients_t - ]) - self.assertAllClose(probs, full_probs) - self.assertAllClose(encoded, full_encoded) - # The 3 gradients (dq, dk, dv) should also match - self.assertAllClose(gradients, full_gradients) - - @parameterized.parameters(False, True) - def testDotAttenCausalMasking(self, fast_path): - batch_size = 3 - seq_length = 12 - num_heads = 2 - dim_per_head = 4 - num_clusters = 1 if fast_path else 3 - attention_window = seq_length - q = np.random.rand(batch_size, seq_length, num_heads, - dim_per_head).astype(np.float32) - k = np.random.rand(batch_size, seq_length, num_heads, - dim_per_head).astype(np.float32) - v = np.random.rand(batch_size, seq_length, num_heads, - dim_per_head).astype(np.float32) - - q_paddings = np.zeros([batch_size, seq_length], dtype=np.float32) - k_paddings = np.zeros([batch_size, seq_length], dtype=np.float32) - p = attention.RoutingAttention.Params().Set( - name='routing_atten', - input_dim=1, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads, - num_clusters=num_clusters, - attention_window=attention_window, - causal_masking=True, - query_group_size_factor=1.0, - fast_path=fast_path) - atten = p.Instantiate() - p = attention.MultiHeadedAttention.Params().Set( - name='full_atten', - input_dim=1, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads) - full_atten = p.Instantiate() - with self.session() as sess: - tf.global_variables_initializer().run() - encoded, probs = sess.run( - atten._DotAtten( - atten.theta, q, k, v, k_paddings, query_paddings=q_paddings)) - # In order to match the full attention, we apply layer norm first. - q_ln = attention_util.KMeansClusteringForAtten.LayerNorm(q) - k_ln = attention_util.KMeansClusteringForAtten.LayerNorm(k) - # Manually apply causal padding to full attention. - per_step_padding = tf.tile( - tf.expand_dims( - attention.CausalPadding(seq_length, dtype=q_ln.dtype), 0), - [batch_size, 1, 1]) - full_encoded, full_probs = full_atten._DotAtten( - full_atten.theta, - q_ln, - k_ln, - v, - k_paddings, - segment_mask=None, - per_step_padding=per_step_padding) - full_probs = tf.transpose(full_probs, [0, 2, 1, 3]) - self.assertAllClose(probs, full_probs.eval()) - self.assertAllClose(encoded, full_encoded.eval()) - - # Verify that the first token only attends to position 0. - first_token_probs = probs[:, 0, :, :] - expected = np.zeros_like(first_token_probs) - expected[:, :, 0] = 1. - self.assertAllClose(first_token_probs, expected) - - @parameterized.parameters(False, True) - def testSelfAtten(self, fast_path): - batch_size = 4 - target_length = 8 - num_heads = 4 - dim_per_head = 5 - num_clusters = 3 - attention_window = 6 - q = tf.random.normal( - shape=[batch_size, target_length, num_heads, dim_per_head]) - v = tf.random.normal( - shape=[batch_size, target_length, num_heads, dim_per_head]) - q_copy = tf.identity(q) - - paddings = np.zeros([batch_size, target_length], dtype=np.float32) - p = attention.RoutingAttention.Params().Set( - name='routing_atten', - input_dim=1, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads, - num_clusters=num_clusters, - attention_window=attention_window, - query_group_size_factor=1.0, - fast_path=fast_path) - atten = p.Instantiate() - with self.session() as sess: - tf.global_variables_initializer().run() - # self attention path - encoded_self_t, probs_self_t = atten._DotAtten( - atten.theta, q, q, v, paddings, query_paddings=paddings) - # computed as cross attention - encoded_t, probs_t = atten._DotAtten( - atten.theta, q, q_copy, v, paddings, query_paddings=paddings) - encoded, probs, encoded_self, probs_self = sess.run( - [encoded_t, probs_t, encoded_self_t, probs_self_t]) - self.assertAllClose(probs, probs_self) - self.assertAllClose(encoded, encoded_self) - - def testExtendStep(self): - batch_size = 8 - target_length = 10 - num_heads = 4 - dim_per_head = 5 - num_clusters = 6 - attention_window = target_length - input_dim = 7 - q = np.random.rand(batch_size, target_length, input_dim).astype(np.float32) - paddings = np.zeros([batch_size, target_length], dtype=np.float32) - p = attention.RoutingAttention.Params().Set( - name='routing_atten', - input_dim=input_dim, - hidden_dim=num_heads * dim_per_head, - num_heads=num_heads, - num_clusters=num_clusters, - attention_window=attention_window, - causal_masking=True, - fast_path=False) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - atten = p.Instantiate() - # We ensure that the encoded attention result is the same between FProp() - # and sequential calls to ExtendStep(). - with self.session() as sess: - # self attention path via ExtendStep - encoded_all = [] - states = atten.InitStates(atten.theta, batch_size, target_length) - self.assertEqual(states.key.shape, - (target_length, batch_size, num_heads, dim_per_head)) - self.assertEqual(states.value.shape, - (target_length, batch_size, num_heads, dim_per_head)) - self.assertEqual(states.key_dists.shape, - (target_length, batch_size, num_heads, num_clusters)) - for i in range(target_length): - encoded, states = atten.ExtendStep(atten.theta, q[:, i:i + 1, :], - states, paddings, i) - self.assertEqual(encoded.shape, (batch_size, 1, input_dim)) - encoded_all.append(encoded) - encoded_extend_t = tf.concat(encoded_all, axis=1) - - # self attention path via FProp - encoded_fprop_t, _ = atten.FProp(atten.theta, q, q, q, paddings) - self.assertEqual(encoded_fprop_t.shape, - (batch_size, target_length, input_dim)) - - tf.global_variables_initializer().run() - encoded_extend, encoded_fprop = sess.run( - [encoded_extend_t, encoded_fprop_t]) - self.assertAllClose(encoded_extend, encoded_fprop) - - -class TransformerLayerTest(test_utils.TestCase, parameterized.TestCase): - """Test Transformer decoder layers.""" - - def _TransformerAttentionLayerInputs(self, input_dim=4, dtype=tf.float32): - np.random.seed(6348575) - query_vec = tf.transpose( - tf.stack([ - tf.constant(np.random.rand(2, input_dim), dtype=dtype) - for _ in range(5) - ]), [1, 0, 2]) - paddings = tf.constant([[0, 0, 1, 1, 0], [1, 0, 0, 0, 1]], dtype=dtype) - aux_vec = tf.transpose( - tf.stack([ - tf.constant(np.random.rand(2, input_dim), dtype=dtype) - for _ in range(7) - ]), [1, 0, 2]) - aux_paddings = tf.constant([[0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]], - dtype=dtype) - return query_vec, paddings, aux_vec, aux_paddings - - def testTransformerAttentionLayerFPropMaskedSelfAttention(self): - with self.session(use_gpu=True) as sess: - query_vec, paddings, _, _ = self._TransformerAttentionLayerInputs() - - p = attention.TransformerAttentionLayer.Params().Set( - name='transformer_masked_self_atten', - input_dim=4, - is_masked=True, - num_heads=2) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - ctx_vec, _ = l.FProp(l.theta, query_vec, None, paddings) - - tf.global_variables_initializer().run() - actual_ctx = sess.run(ctx_vec) - actual_ctx = np.reshape(actual_ctx, (10, 4)) - tf.logging.info(np.array_repr(actual_ctx)) - expected_ctx = [7.777687, 5.219166, 6.305151, 4.817311] - self.assertAllClose(expected_ctx, np.sum(actual_ctx, axis=0)) - - def testAttentionLayerFPropMaskedSelfAttentionPaddingOverride(self): - with self.session(use_gpu=True) as sess: - query_vec, paddings, _, _ = self._TransformerAttentionLayerInputs() - - p = attention.TransformerAttentionLayer.Params().Set( - name='transformer_masked_self_atten', - input_dim=4, - is_masked=True, - num_heads=2) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - triangle_padding = 1.0 - tf.linalg.band_part( - tf.ones([5, 5], dtype=query_vec.dtype), -1, 0) - per_step_padding_override = tf.tile( - tf.expand_dims(triangle_padding, 0), [2, 1, 1]) - - ctx_vec1, _ = l.FProp(l.theta, query_vec, None, paddings, - per_step_padding_override) - expected_ctx1, _ = l.FProp(l.theta, query_vec, None, paddings) - per_step_padding_override = tf.zeros([2, 5, 5]) - ctx_vec2, _ = l.FProp(l.theta, query_vec, None, paddings, - per_step_padding_override) - - tf.global_variables_initializer().run() - actual_ctx1, actual_ctx2, actual_expected_ctx1 = sess.run( - [ctx_vec1, ctx_vec2, expected_ctx1]) - tf.logging.info(np.array_repr(actual_ctx1)) - tf.logging.info(np.array_repr(actual_ctx2)) - expected_ctx2 = [7.9491496, 5.2976646, 6.5383415, 5.0169916] - self.assertAllClose(actual_expected_ctx1, ctx_vec1) - self.assertAllClose(expected_ctx2, - np.sum(np.reshape(actual_ctx2, (10, 4)), axis=0)) - - def testTransformerAttentionLayerFPropCrossAttention(self): - with self.session(use_gpu=True) as sess: - (query_vec, _, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs() - p = attention.TransformerAttentionLayer.Params().Set( - name='transformer_cross_atten', - input_dim=4, - is_masked=False, - num_heads=2) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - ctx_vec, _ = l.FProp(l.theta, query_vec, aux_vec, aux_paddings) - - tf.global_variables_initializer().run() - actual_ctx = sess.run(ctx_vec) - actual_ctx = np.reshape(actual_ctx, (10, 4)) - tf.logging.info(np.array_repr(actual_ctx)) - expected_ctx = [19.345360, 15.057412, 13.744134, 13.387347] - self.assertAllClose(expected_ctx, np.sum(actual_ctx, axis=0)) - - def testMultiSourceTransformerAttentionLayerFPropCrossAttention(self): - with self.session(use_gpu=True) as sess: - (query_vec, _, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs() - p = attention.TransformerMultiSourceAttentionLayer.Params().Set( - name='transformer_multi_source_cross_atten', - input_dim=4, - is_masked=False, - num_heads=2, - num_source=2) - p.multi_source_atten.atten_merger_tpl = ( - tm_attention.MergerLayer.Params().Set(merger_op='sum')) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - ctx_vec, _ = l.FProp( - l.theta, query_vec, - py_utils.NestedMap({ - 'source_0': aux_vec, - 'source_1': aux_vec - }), - py_utils.NestedMap({ - 'source_0': aux_paddings, - 'source_1': aux_paddings - })) - - tf.global_variables_initializer().run() - actual_ctx = sess.run(ctx_vec) - actual_ctx = np.reshape(actual_ctx, (10, 4)) - tf.logging.info(np.array_repr(actual_ctx)) - expected_ctx = [32.4878, 25.145725, 21.534966, 22.007454] - self.assertAllClose(expected_ctx, np.sum(actual_ctx, axis=0)) - - @parameterized.named_parameters( - { - 'testcase_name': '_short_seq', - 'use_short_seq_opt': True, - }, { - 'testcase_name': '_long_seq', - 'use_short_seq_opt': False, - }) - def testTransformerAttentionLayerExtendStep(self, use_short_seq_opt): - with self.session(use_gpu=True) as sess: - query_vec, _, _, _ = self._TransformerAttentionLayerInputs() - paddings = tf.zeros([2, 5]) - cached_key = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - cached_value = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - prefix_states = py_utils.NestedMap(key=cached_key, value=cached_value) - - p = attention.TransformerAttentionLayer.Params().Set( - name='transformer_atten', input_dim=4, is_masked=True, num_heads=2) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - - ctx_vec1, _ = l.FProp(l.theta, query_vec, None, paddings) - - ctx_vec2 = [] - for i in range(5): - ctx_vec, prefix_states = l.ExtendStep( - l.theta, tf.expand_dims(query_vec[:, i, :], 1), prefix_states, i, - use_short_seq_opt) - ctx_vec2.append(tf.squeeze(ctx_vec, 1)) - ctx_vec2 = tf.transpose(tf.stack(ctx_vec2), [1, 0, 2]) - - tf.global_variables_initializer().run() - ctx1, ctx2 = sess.run([ctx_vec1, ctx_vec2]) - self.assertAllClose(ctx1, ctx2) - - def testTransformerAttentionLayerNoLayernorm(self): - """Verify if Transformer attention allows no layernorm in FProp and Extend.""" - with self.session(use_gpu=True) as sess: - query_vec, _, _, _ = self._TransformerAttentionLayerInputs() - paddings = tf.zeros([2, 5]) - cached_key = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - cached_value = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - prefix_states = py_utils.NestedMap(key=cached_key, value=cached_value) - - p = attention.TransformerAttentionLayer.Params().Set( - name='transformer_atten', - input_dim=4, - is_masked=True, - num_heads=2, - ln_tpl=None) # Set ln_tpl to None. - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - - ctx_vec1, _ = l.FProp(l.theta, query_vec, None, paddings) - - ctx_vec2 = [] - for i in range(5): - ctx_vec, prefix_states = l.ExtendStep( - l.theta, tf.expand_dims(query_vec[:, i, :], 1), prefix_states, i, - False) - ctx_vec2.append(tf.squeeze(ctx_vec, 1)) - ctx_vec2 = tf.transpose(tf.stack(ctx_vec2), [1, 0, 2]) - - tf.global_variables_initializer().run() - ctx1, ctx2 = sess.run([ctx_vec1, ctx_vec2]) - self.assertAllClose(ctx1, ctx2) - - def _ConstructTransformerDecoderLayer(self, use_relative_atten=False): - p = attention.TransformerDecoderLayer.Params() - p.name = 'transformer_decoder_layer' - p.input_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_heads = 2 - if use_relative_atten: - p = attention.UseRelativeAttentionInTransformerLayer(p, 4) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - return attention.TransformerDecoderLayer(p) - - def testTransformerLayerCommonParams(self): - with self.session(use_gpu=True) as sess: - input_dim, fflayer_hidden_dim, num_heads = 4, 7, 2 - (query_vec, _, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs( - input_dim=input_dim) - query_vec = tf.tile(query_vec, [1, 1, 1]) - paddings = tf.zeros([2, 5]) - p = attention.TransformerLayer.CommonParams( - input_dim=input_dim, - atten_num_heads=num_heads, - fflayer_hidden_dim=fflayer_hidden_dim) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - ctx_vec, _ = l.FProp(l.theta, query_vec, paddings, aux_vec, aux_paddings) - - tf.global_variables_initializer().run() - actual_ctx = sess.run(ctx_vec) - actual_ctx = np.reshape(actual_ctx, (10, 4)) - tf.logging.info(np.array_repr(actual_ctx)) - expected_ctx = [ - 4.7839108, 4.5303655, 5.5551023, 5.0657663, 5.0493064, 3.2142467, - 2.820018, 5.659971, 4.3814187, 2.60475 - ] - self.assertAllClose(expected_ctx, np.sum(actual_ctx, axis=1)) - - @parameterized.named_parameters(('SingleBatch', 1), ('DoubleBatch', 2)) - def testTransformerLayerFPropWithCrossAttention(self, multiplier): - with self.session(use_gpu=True) as sess: - (query_vec, _, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs() - query_vec = tf.tile(query_vec, [multiplier, 1, 1]) - paddings = tf.zeros([2 * multiplier, 5]) - p = attention.TransformerLayer.Params() - p.name = 'transformer_layer' - p.input_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_heads = 2 - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - ctx_vec, _ = l.FProp(l.theta, query_vec, paddings, aux_vec, aux_paddings) - - tf.global_variables_initializer().run() - actual_ctx = sess.run(ctx_vec) - actual_ctx = np.reshape(actual_ctx, (10 * multiplier, 4)) - tf.logging.info(np.array_repr(actual_ctx)) - expected_ctx = [ - 4.7839108, 4.5303655, 5.5551023, 5.065767, 5.0493064, 3.2142467, - 2.8200178, 5.659971, 4.3814187, 2.60475 - ] * multiplier - self.assertAllClose(expected_ctx, np.sum(actual_ctx, axis=1)) - - @parameterized.named_parameters(('SingleBatch', 1), ('DoubleBatch', 2)) - def testMultiSourceTransformerLayerFPropWithCrossAttention(self, multiplier): - with self.session(use_gpu=True) as sess: - (query_vec, _, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs() - query_vec = tf.tile(query_vec, [multiplier, 1, 1]) - paddings = tf.zeros([2 * multiplier, 5]) - p = attention.TransformerLayer.Params() - p.name = 'transformer_layer' - p.input_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - # multi-source cross attention - p.tr_atten_tpl = ( - attention.TransformerMultiSourceAttentionLayer.Params().Set( - num_source=2, primary_source_index=0, num_heads=2)) - p.tr_self_atten_tpl = attention.TransformerAttentionLayer.Params().Set( - input_dim=4, num_heads=2) - l = p.Instantiate() - ctx_vec, _ = l.FProp( - l.theta, query_vec, paddings, - py_utils.NestedMap({ - 'source_0': aux_vec, - 'source_1': aux_vec - }), - py_utils.NestedMap({ - 'source_0': aux_paddings, - 'source_1': aux_paddings - })) - - tf.global_variables_initializer().run() - actual_ctx = sess.run(ctx_vec) - actual_ctx = np.reshape(actual_ctx, (10 * multiplier, 4)) - tf.logging.info(np.array_repr(actual_ctx)) - expected_ctx = [ - 4.7839108, 4.5303655, 5.5551023, 5.0657663, 5.0493064, 3.2142467, - 2.820018, 5.659971, 4.3814187, 2.60475 - ] * multiplier - self.assertAllClose(expected_ctx, np.sum(actual_ctx, axis=1)) - - @parameterized.named_parameters(('Base', False), ('RelativeAtten', True)) - def testTransformerDecoderLayerConstruction(self, use_relative_atten): - _ = self._ConstructTransformerDecoderLayer( - use_relative_atten=use_relative_atten) - - def testTransformerDecoderLayerFProp(self): - with self.session(use_gpu=True) as sess: - (query_vec, paddings, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs() - l = self._ConstructTransformerDecoderLayer() - - layer_output, _ = l.FProp(l.theta, query_vec, paddings, aux_vec, - aux_paddings) - - tf.global_variables_initializer().run() - actual_layer_output = sess.run(layer_output) - actual_layer_output = np.reshape(actual_layer_output, (10, 4)) - tf.logging.info(np.array_repr(actual_layer_output)) - expected_layer_output = [16.939590, 24.121685, 19.975197, 15.924350] - self.assertAllClose(expected_layer_output, - np.sum(actual_layer_output, axis=0)) - - def _ConstructTransformerEncoderLayerStack(self): - p = attention.StackedTransformerLayers.Params() - p.name = 'encoder_layers' - p.has_aux_atten = False - p.mask_self_atten = False - p.num_layers = 2 - p.mdl_dim = 4 - p.hidden_dim = 8 - p.num_atten_heads = 2 - p.dropout_prob = 0.2 - p.params_init = py_utils.WeightInit.Xavier() - p.random_seed = 12345 - return p.Instantiate() - - def _ConstructTransformerDecoderLayerStack(self, dropout_prob=0.2): - p = attention.StackedTransformerLayers.Params() - p.name = 'decoder_layers' - p.has_aux_atten = True - p.mask_self_atten = True - p.num_layers = 2 - p.mdl_dim = 4 - p.hidden_dim = 8 - p.num_atten_heads = 2 - p.dropout_prob = dropout_prob - p.params_init = py_utils.WeightInit.Xavier() - p.random_seed = 12345 - return p.Instantiate() - - def testStackedTransformerGetSplitForLayer(self): - cls = attention.StackedTransformerLayers - - buckets = [2, 4, 5, 6, 9, 11, 15] - ys = [cls.GetSplitForLayer(buckets, i) for i in range(16)] - self.assertEqual(0, ys[0]) - self.assertEqual(0, ys[1]) - self.assertEqual(0, ys[2]) - self.assertEqual(1, ys[3]) - - self.assertEqual(1, ys[4]) - self.assertEqual(2, ys[5]) - self.assertEqual(3, ys[6]) - self.assertEqual(4, ys[7]) - - self.assertEqual(4, ys[8]) - self.assertEqual(4, ys[9]) - self.assertEqual(5, ys[10]) - self.assertEqual(5, ys[11]) - - self.assertEqual(6, ys[12]) - self.assertEqual(6, ys[13]) - self.assertEqual(6, ys[14]) - self.assertEqual(6, ys[15]) - - def testTransformerEncoderLayerStackFProp(self): - with self.session(use_gpu=True) as sess: - (query_vec, paddings, _, _) = self._TransformerAttentionLayerInputs() - l = self._ConstructTransformerEncoderLayerStack() - layer_output, _ = l.FProp(l.theta, query_vec=query_vec, paddings=paddings) - tf.global_variables_initializer().run() - actual_layer_output = sess.run(layer_output) - actual_layer_output = np.reshape(actual_layer_output, (10, 4)) - tf.logging.info(np.array_repr(actual_layer_output)) - expected_layer_output = [6.178955, -11.376661, 7.032681, -1.532627] - self.assertAllClose(expected_layer_output, - np.sum(actual_layer_output, axis=0)) - - def testTransformerDecoderLayerStackFProp(self): - with self.session(use_gpu=True) as sess: - (query_vec, paddings, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs() - l = self._ConstructTransformerDecoderLayerStack() - layer_output, _ = l.FProp( - l.theta, - query_vec=query_vec, - paddings=paddings, - aux_vec=aux_vec, - aux_paddings=aux_paddings) - tf.global_variables_initializer().run() - actual_layer_output = sess.run(layer_output) - actual_layer_output = np.reshape(actual_layer_output, (10, 4)) - tf.logging.info(np.array_repr(actual_layer_output)) - expected_layer_output = [9.926413, -4.491376, 27.051598, 2.112684] - self.assertAllClose(expected_layer_output, - np.sum(actual_layer_output, axis=0)) - - @parameterized.named_parameters( - { - 'testcase_name': '_short_seq', - 'use_short_seq_opt': True, - }, { - 'testcase_name': '_long_seq', - 'use_short_seq_opt': False, - }) - def testTransformerDecoderLayerStackExtendStep(self, use_short_seq_opt): - - def _Rnd(seed): - return tf.random.normal([5, 2, 2, 2], seed=seed) - - graph = tf.Graph() - with graph.as_default(): - tf.random.set_seed(123456) - query_vec, _, aux_vec, aux_paddings = ( - self._TransformerAttentionLayerInputs()) - paddings = tf.zeros([2, 5]) - layer_prefix_states_1 = py_utils.NestedMap(key=_Rnd(1), value=_Rnd(2)) - layer_prefix_states_2 = py_utils.NestedMap(key=_Rnd(3), value=_Rnd(4)) - prefix_states = py_utils.NestedMap( - x_layers=[layer_prefix_states_1, layer_prefix_states_2]) - - l = self._ConstructTransformerDecoderLayerStack(dropout_prob=0.) - - layer_output1, _ = l.FProp(l.theta, query_vec, paddings, aux_vec, - aux_paddings) - - layer_output2 = [] - for i in range(5): - layer_output, prefix_states = l.ExtendStep( - l.theta, tf.expand_dims(query_vec[:, i, :], 1), aux_vec, - aux_paddings, prefix_states, i, use_short_seq_opt) - layer_output2.append(tf.squeeze(layer_output, 1)) - layer_output2 = tf.transpose(tf.stack(layer_output2), [1, 0, 2]) - - with self.session(graph=graph, use_gpu=True) as sess: - tf.global_variables_initializer().run() - actual_layer_output1, actual_layer_output2 = sess.run( - [layer_output1, layer_output2]) - - self.assertAllClose(actual_layer_output1, actual_layer_output2) - - @parameterized.named_parameters( - { - 'testcase_name': '_short_seq', - 'use_short_seq_opt': True, - }, { - 'testcase_name': '_long_seq', - 'use_short_seq_opt': False, - }) - def testTransformerDecoderLayerExtendStep(self, use_short_seq_opt): - with self.session(use_gpu=True) as sess: - (query_vec, _, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs() - paddings = tf.zeros([2, 5]) - cached_key = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - cached_value = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - prefix_states = py_utils.NestedMap(key=cached_key, value=cached_value) - - l = self._ConstructTransformerDecoderLayer() - - layer_output1, _ = l.FProp(l.theta, query_vec, paddings, aux_vec, - aux_paddings) - - layer_output2 = [] - for i in range(5): - layer_output, prefix_states = l.ExtendStep( - l.theta, tf.expand_dims(query_vec[:, i, :], 1), aux_vec, - aux_paddings, prefix_states, i, use_short_seq_opt) - layer_output2.append(tf.squeeze(layer_output, 1)) - layer_output2 = tf.transpose(tf.stack(layer_output2), [1, 0, 2]) - - tf.global_variables_initializer().run() - actual_layer_output1, actual_layer_output2 = sess.run( - [layer_output1, layer_output2]) - self.assertAllClose(actual_layer_output1, actual_layer_output2) - - def _ConstructMultiSourceTransformerDecoderLayer(self, - use_relative_atten=False): - p = attention.MultiSourceTransformerDecoderLayer.Params().Set(num_source=2) - p.name = 'multi_source_transformer_decoder_layer' - p.input_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - # multi-source cross attention - p.tr_atten_tpl = ( - attention.TransformerMultiSourceAttentionLayer.Params().Set( - num_source=2, primary_source_index=0, num_heads=2)) - p.tr_self_atten_tpl = attention.TransformerAttentionLayer.Params().Set( - input_dim=4, num_heads=2) - p.tr_atten_tpl.multi_source_atten.atten_merger_tpl = ( - tm_attention.MergerLayer.Params().Set(merger_op='sum')) - if use_relative_atten: - p = attention.UseRelativeAttentionInTransformerLayer(p, 4) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - return attention.MultiSourceTransformerDecoderLayer(p) - - @parameterized.named_parameters( - { - 'testcase_name': '_short_seq', - 'use_short_seq_opt': True, - }, { - 'testcase_name': '_long_seq', - 'use_short_seq_opt': False, - }) - def testMultiSourceTransformerDecoderLayerExtendStep(self, use_short_seq_opt): - with self.session(use_gpu=True) as sess: - (query_vec, _, aux_vec, - aux_paddings) = self._TransformerAttentionLayerInputs() - paddings = tf.zeros([2, 5]) - cached_key = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - cached_value = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - prefix_states = py_utils.NestedMap(key=cached_key, value=cached_value) - - l = self._ConstructMultiSourceTransformerDecoderLayer() - - ms_aux_vec = py_utils.NestedMap({ - 'source_0': aux_vec, - 'source_1': aux_vec - }) - ms_aux_paddings = py_utils.NestedMap({ - 'source_0': aux_paddings, - 'source_1': aux_paddings - }) - layer_output1, _ = l.FProp(l.theta, query_vec, paddings, ms_aux_vec, - ms_aux_paddings) - - layer_output2 = [] - for i in range(5): - layer_output, prefix_states = l.ExtendStep( - l.theta, tf.expand_dims(query_vec[:, i, :], 1), ms_aux_vec, - ms_aux_paddings, prefix_states, i, use_short_seq_opt) - layer_output2.append(tf.squeeze(layer_output, 1)) - layer_output2 = tf.transpose(tf.stack(layer_output2), [1, 0, 2]) - - tf.global_variables_initializer().run() - actual_layer_output1, actual_layer_output2 = sess.run( - [layer_output1, layer_output2]) - self.assertAllClose(actual_layer_output1, actual_layer_output2) - - def testGPipeTransformerLayerConstruction(self): - p = attention.GPipeTransformerLayer.Params() - p.name = 'gpipe_transformer_layer' - p.input_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_heads = 2 - p.tr_atten_tpl.residual_dropout_prob = 0.1 - p.cls.SetupDeterministicDropout(p) - layer = p.Instantiate() - self.assertEqual(0.1, layer.params.tr_atten_tpl.residual_dropout_prob) - - -class GPipeBatchMajorTransformerLayerTest(test_utils.TestCase, - parameterized.TestCase): - """Test GPipeBatchMajorTransformer layers.""" - - def _ConstructGPipeBatchMajorTransformerLayer(self, - decoder=False, - packed=True, - dropout=0.1): - p = attention.GPipeBatchMajorTransformerLayer.Params() - p.name = 'gpipe_transformer_layer' - p.input_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_heads = 2 - p.tr_atten_tpl.residual_dropout_prob = dropout - p.packed_input = packed - if decoder: - p.has_aux_atten = True - p.mask_self_atten = True - p.cls.SetupDeterministicDropout(p) - layer = p.Instantiate() - return p, layer - - def _GPipeBatchMajorTransformerLayerInputs(self, - input_dim=4, - dtype=tf.float32): - np.random.seed(6348575) - target_vec = tf.transpose( - tf.stack([ - tf.constant(np.random.rand(2, input_dim), dtype=dtype) - for _ in range(5) - ]), [1, 0, 2]) - target_paddings = tf.constant([[0, 0, 0, 0, 1], [0, 0, 0, 0, 0]], - dtype=dtype) - aux_vec = tf.transpose( - tf.stack([ - tf.constant(np.random.rand(2, input_dim), dtype=dtype) - for _ in range(7) - ]), [1, 0, 2]) - aux_paddings = tf.constant([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1]], - dtype=dtype) - aux_segment_ids = tf.constant( - [[0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1]], dtype=dtype) - target_segment_ids = tf.constant([[0, 0, 0, 1, 1], [0, 0, 1, 1, 1]], - dtype=dtype) - target_sa_mask = attention.SegmentMask( - target_segment_ids, target_segment_ids, apply_dtype_min=False) - aux_sa_mask = attention.SegmentMask( - aux_segment_ids, aux_segment_ids, apply_dtype_min=False) - ca_mask = attention.SegmentMask( - target_segment_ids, aux_segment_ids, apply_dtype_min=False) - causal_padding = tf.expand_dims( - tf.tile( - tf.expand_dims(attention.CausalPadding(5, dtype=dtype), 0), - [2, 1, 1]), 1) - target_sa_mask = tf.math.maximum(causal_padding, target_sa_mask) - return (target_vec, target_paddings, target_sa_mask, aux_vec, aux_paddings, - aux_sa_mask, ca_mask) - - def testGPipeBatchMajorTransformerEncoderLayerConstruction(self): - _, layer = self._ConstructGPipeBatchMajorTransformerLayer() - self.assertEqual(0.1, layer.params.tr_atten_tpl.residual_dropout_prob) - - def testGPipeBatchMajorTransformerDecoderLayerConstruction(self): - _, layer = self._ConstructGPipeBatchMajorTransformerLayer(decoder=True) - self.assertEqual(0.1, layer.params.tr_atten_tpl.residual_dropout_prob) - - def testGPipeBatchMajorTransformerEncoderLayerFProp(self): - with self.session(use_gpu=True) as sess: - (_, _, _, aux_vec, aux_paddings, aux_sa_mask, - _) = self._GPipeBatchMajorTransformerLayerInputs() - _, l = self._ConstructGPipeBatchMajorTransformerLayer() - - layer_output = l.FProp(l.theta, aux_vec, aux_paddings, None, None, - aux_sa_mask, None, None)[0] - - tf.global_variables_initializer().run() - actual_layer_output = sess.run(layer_output) - actual_layer_output = np.reshape(actual_layer_output, (14, 4)) - tf.logging.info(np.array_repr(actual_layer_output)) - expected_layer_output = [7.616176, 8.611565, -0.932456, -4.5797] - self.assertAllClose(expected_layer_output, - np.sum(actual_layer_output, axis=0)) - - def testGPipeBatchMajorTransformerDecoderLayerFProp(self): - with self.session(use_gpu=True) as sess: - (target_vec, target_paddings, target_sa_mask, aux_vec, aux_paddings, - aux_sa_mask, ca_mask) = self._GPipeBatchMajorTransformerLayerInputs() - _, l = self._ConstructGPipeBatchMajorTransformerLayer(decoder=True) - - layer_output = l.FProp(l.theta, aux_vec, aux_paddings, target_vec, - target_paddings, aux_sa_mask, target_sa_mask, - ca_mask)[2] - - tf.global_variables_initializer().run() - actual_layer_output = sess.run(layer_output) - actual_layer_output = np.reshape(actual_layer_output, (10, 4)) - tf.logging.info(np.array_repr(actual_layer_output)) - expected_layer_output = [2.721037, 5.228053, 2.27512, 6.92945] - self.assertAllClose(expected_layer_output, - np.sum(actual_layer_output, axis=0)) - - def testGPipeBatchMajorTransformerDecoderLayerExtendStep(self): - with self.session(use_gpu=True) as sess: - (target_vec, _, _, aux_vec, aux_paddings, _, - _) = self._GPipeBatchMajorTransformerLayerInputs() - target_paddings = tf.zeros([2, 5]) - cached_key = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - cached_value = tf.constant( - np.random.normal(0.1, 0.5, [5, 2, 2, 2]), dtype=tf.float32) - prefix_states = py_utils.NestedMap(key=cached_key, value=cached_value) - _, l = self._ConstructGPipeBatchMajorTransformerLayer( - decoder=True, packed=False, dropout=0.0) - - layer_output1 = l.FProp(l.theta, aux_vec, aux_paddings, target_vec, - target_paddings, None, None, None)[2] - - layer_output2 = [] - for i in range(5): - layer_output, prefix_states = l.ExtendStep( - l.theta, tf.expand_dims(target_vec[:, i, :], 1), aux_vec, - aux_paddings, prefix_states, i) - layer_output2.append(tf.squeeze(layer_output, 1)) - layer_output2 = tf.transpose(tf.stack(layer_output2), [1, 0, 2]) - - tf.global_variables_initializer().run() - actual_layer_output1, actual_layer_output2 = sess.run( - [layer_output1, layer_output2]) - self.assertAllClose(actual_layer_output1, actual_layer_output2) - - -class BuilderTest(test_utils.TestCase, parameterized.TestCase): - - def _testGraph(self, glu_with_tanh=False, dtype=tf.float32): - tf.random.set_seed(398847392) - np.random.seed(12345) - atten_builder = attention.Builder.Params().Set( - model_dim=4, num_heads=2, ff_hidden_dim=16, glu_with_tanh=glu_with_tanh) - params = atten_builder.Instantiate().LConvStack( - name='lightconv', kernel_sizes=[3, 3]) - params.dtype = dtype - params.random_seed = 0 - params.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = params.Instantiate() - l_in = tf.constant(np.random.rand(2, 3, 4), dtype=dtype) - l_padding = tf.zeros([2, 3], dtype=dtype) - l_out = l.FPropDefaultTheta( - py_utils.NestedMap(vec=l_in, paddings=l_padding)) - return l_out.vec - - @parameterized.parameters((False, 38.163662), (True, 35.88797)) - def testFprop(self, glu_with_tanh, expected_result): - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - l_out = self._testGraph(glu_with_tanh) - l_out = tf.reduce_sum(l_out) - tf.global_variables_initializer().run() - l_out_eval = sess.run(l_out) - self.assertAllClose(expected_result, l_out_eval) - - def testBProp(self): - with self.session(use_gpu=True) as sess: - output = self._testGraph(dtype=tf.float64) - loss = tf.reduce_sum(output) - all_vars = tf.trainable_variables() - grads = tf.gradients(loss, all_vars) - tf.global_variables_initializer().run() - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v) for v in all_vars - ] - for ng, sg in zip(num_grads, sym_grads): - self.assertAllClose(ng, sg, rtol=5e-02, atol=5e-02) - - @parameterized.named_parameters( - { - 'testcase_name': '_baseline', - 'strides': [1, 1], - }, { - 'testcase_name': '_stride_2', - 'strides': [2, 1], - }, { - 'testcase_name': '_first_token', - 'strides': [2, 0], - }) - def testTransformerStackWithStride(self, strides): - with self.session(use_gpu=False) as sess: - bs = 2 - sl = 10 - d = 16 - tf.random.set_seed(12345) - atten_builder = attention.Builder.Params().Set( - model_dim=d, num_heads=2, ff_hidden_dim=5).Instantiate() - layers = [] - accumulate_stride = 1 - for layer_i, stride in enumerate(strides): - accumulate_stride *= stride - layers.append( - atten_builder.TransformerEncoderLayer( - name='atten_{}'.format(layer_i), stride=stride)) - p = atten_builder.Seq('model', *layers) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - input_embs = tf.constant( - np.random.random(size=[bs, sl, d]), dtype=np.float) - paddings = tf.zeros([bs, sl]) - l_out = l.FPropDefaultTheta( - py_utils.NestedMap(vec=input_embs, paddings=paddings)) - enc_out = l_out.vec - tf.global_variables_initializer().run() - actual_enc_out = sess.run(enc_out) - seq_len = sl // accumulate_stride if accumulate_stride != 0 else 1 - self.assertAllEqual([bs, seq_len, d], actual_enc_out.shape) - - @parameterized.named_parameters( - { - 'testcase_name': '_baseline', - 'strides': [(1, 6), (1, 3), 3], - }, { - 'testcase_name': '_stride_2', - 'strides': [(2, 4), (1, None), 2], - }, { - 'testcase_name': '_first_token', - 'strides': [(2, 5), (0, None), 1], - }) - def testTransformerStackWithStrideAndOutLength(self, strides): - with self.session(use_gpu=False) as sess: - bs = 2 - sl = 10 - d = 16 - tf.random.set_seed(12345) - atten_builder = attention.Builder.Params().Set( - model_dim=d, num_heads=2, ff_hidden_dim=5).Instantiate() - layers = [] - out_seq_len = strides.pop() - for layer_i, (stride, first_n) in enumerate(strides): - layers.append( - atten_builder.TransformerEncoderLayer( - name='atten_{}'.format(layer_i), stride=stride, - first_n=first_n)) - p = atten_builder.Seq('model', *layers) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - input_embs = tf.constant( - np.random.random(size=[bs, sl, d]), dtype=np.float) - paddings = tf.zeros([bs, sl]) - l_out = l.FPropDefaultTheta( - py_utils.NestedMap(vec=input_embs, paddings=paddings)) - enc_out = l_out.vec - tf.global_variables_initializer().run() - actual_enc_out = sess.run(enc_out) - self.assertAllEqual([bs, out_seq_len, d], actual_enc_out.shape) - - def testEncoderLayerWithPerLayerParam(self): - with self.session(use_gpu=False) as sess: - bs = 2 - sl = 10 - d = 16 - tf.random.set_seed(398847392) - np.random.seed(12345) - heads = [1, 2, 4] - ff_dims = [16, 32, 16] - atten_builder = attention.Builder.Params().Set( - model_dim=16, num_heads=heads, ff_hidden_dim=ff_dims).Instantiate() - layers = [] - for layer_i, (head, ff_dim) in enumerate(zip(heads, ff_dims)): - layers.append( - atten_builder.TransformerEncoderLayer( - name='atten_{}'.format(layer_i), - ff_hidden_dim=ff_dim, - num_heads=head, - stride=1 if layer_i < 2 else 0)) - p = atten_builder.Seq('model', *layers) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - input_embs = tf.constant( - np.random.random(size=[bs, sl, d]), dtype=np.float) - paddings = tf.zeros([bs, sl]) - l_out = l.FPropDefaultTheta( - py_utils.NestedMap(vec=input_embs, paddings=paddings)) - out = tf.reduce_sum(l_out.vec) - tf.global_variables_initializer().run() - actual_out = sess.run(out) - self.assertAllClose(actual_out, 17.40516) - - -class LmBuilderTest(test_utils.TestCase): - - def _testGraph(self, dtype=tf.float32): - tf.random.set_seed(398847392) - np.random.seed(12345) - atten_builder = attention.LmBuilder.Params().Set( - model_dim=4, num_heads=2, ff_hidden_dim=16, dtype=dtype) - params = atten_builder.Instantiate().TransformerEncoderStack( - name='xformer', num_layers=2) - params.dtype = dtype - params.random_seed = 0 - params.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = params.Instantiate() - l_in = tf.constant(np.random.rand(2, 3, 4), dtype=dtype) - l_padding = tf.zeros([2, 3], dtype=dtype) - l_out = l.FPropDefaultTheta( - py_utils.NestedMap(vec=l_in, paddings=l_padding)) - return l_out.vec - - def testFprop(self): - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - l_out = self._testGraph() - l_out = tf.reduce_sum(l_out) - tf.global_variables_initializer().run() - l_out_eval = sess.run(l_out) - self.assertAllClose(36.04808, l_out_eval) - - def testBProp(self): - with self.session(use_gpu=True) as sess: - output = self._testGraph(dtype=tf.float64) - loss = tf.reduce_sum(output) - all_vars = tf.trainable_variables() - grads = tf.gradients(loss, all_vars) - tf.global_variables_initializer().run() - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v) for v in all_vars - ] - for ng, sg in zip(num_grads, sym_grads): - self.assertAllClose(ng, sg, rtol=5e-02, atol=5e-02) - - -def _CreateDummyParams(field_names): - p = hyperparams.Params() - for name in field_names: - p.Define(name, None, 'Dummy') - return p - - -class DummyDecoderRNNT(base_layer.BaseLayer): - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'dummy_decoder_rnnt' - p.Define('emb', _CreateDummyParams(['vocab_size']), 'Dummy emb.') - p.Define('target_seq_len', 20, 'Dummy target seq len.') - p.Define('num_classes', None, 'Dummy num classes.') - return p - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - p.emb.vocab_size = vocab_size - p.num_classes = vocab_size - return p - - -class RelativeAttentionHelperTest(test_utils.TestCase, parameterized.TestCase): - - @parameterized.named_parameters( - ('MultiHeadedAttentionXL', attention.MultiHeadedAttentionXL, - attention.MultiHeadedAttention), - ('LocalSelfAttentionXL', attention.LocalSelfAttentionXL, - attention.LocalSelfAttention)) - def testClearRelativeAttentionInTransformerLayer(self, atten_cls, - expected_atten_cls): - """Tests scenarios in clear relative attention in transformer layer.""" - trans_p = attention.TransformerLayer.Params() - # set attention params in transformer layer. - input_dim = 4 - rel_pos_emb_dim = 4 - # Set rel_pos_emb_dim in attention params. - trans_p.tr_atten_tpl.atten_tpl = ( - atten_cls.Params().Set( - input_dim=input_dim, rel_pos_emb_dim=rel_pos_emb_dim)) - new_trans_p = attention.ClearRelativeAttentionInTransformerLayer(trans_p) - tr_atten_tpl = new_trans_p.tr_self_atten_tpl.atten_tpl - self.assertEqual(tr_atten_tpl.cls, expected_atten_cls) - self.assertEqual(tr_atten_tpl.input_dim, input_dim) - - def testClearRelativeAttentionTransformerLayerNotSupportedError(self): - transformer_params = DummyDecoderRNNT.Params() - with self.assertRaises(ValueError): - _ = attention.ClearRelativeAttentionInTransformerLayer(transformer_params) - - def testClearRelativeAttentionAttentionParamsNotSupportedError(self): - trans_p = attention.TransformerLayer.Params() - # MultiHeadedAttention is not supported in ClearRelativeAttention. - attention_params = attention.MultiHeadedAttention.Params() - trans_p.tr_atten_tpl.atten_tpl = attention_params - with self.assertRaises(ValueError): - _ = attention.ClearRelativeAttentionInTransformerLayer(trans_p) - - @parameterized.named_parameters( - ('AttentionParamsNotSupported', _CreateDummyParams( - ['name', 'cls']), attention.ATTEN_TRANSFORMER_XL), - ('AttentionTypeNotSupported', attention.MultiHeadedAttention.Params(), - 'unsupported_atten_type')) - def testUseRelativeAttentionInTransformerLayerValueError( - self, attention_params, attention_type): - """Tests unsupported Use Relative Attention cases.""" - transformer_param = attention.TransformerLayer.Params() - transformer_param.tr_atten_tpl.atten_tpl = attention_params - rel_pos_emb_dim = 4 - with self.assertRaises(ValueError): - _ = attention.UseRelativeAttentionInTransformerLayer( - transformer_param, rel_pos_emb_dim, atten_type=attention_type) - - def testUseRelativeAttentionInTransformerLayerNotSupportedError(self): - """Tests unsupported input transformer params in Use Relative Attention.""" - transformer_params = DummyDecoderRNNT.Params() - with self.assertRaises(ValueError): - _ = attention.UseRelativeAttentionInTransformerLayer( - transformer_params, 4, atten_type=attention.ATTEN_TRANSFORMER_XL) - - @parameterized.named_parameters( - ('MultiHeadedAttention', attention.MultiHeadedAttention, - attention.MultiHeadedAttentionXL, attention.ATTEN_TRANSFORMER_XL), - ('LocalSelfAttention', attention.LocalSelfAttention, - attention.LocalSelfAttentionXL, attention.ATTEN_TRANSFORMER_XL), - ('MultiHeadedAttentionRPE', attention.MultiHeadedAttention, - attention.MultiHeadedAttentionRPE, attention.ATTEN_RPE)) - def testUseRelativeAttentionInTransformerLayer(self, atten_cls, - expected_atten_cls, - atten_type): - """Tests different scenarios in Use Relative Attention.""" - trans_p = attention.TransformerLayer.Params() - # set attenion params in transformer layer. - input_dim = 4 - trans_p.tr_atten_tpl.atten_tpl = atten_cls.Params().Set(input_dim=input_dim) - rel_pos_emb_dim = 4 - new_trans_p = attention.UseRelativeAttentionInTransformerLayer( - trans_p, rel_pos_emb_dim, atten_type=atten_type) - tr_atten_tpl = new_trans_p.tr_self_atten_tpl.atten_tpl - self.assertEqual(tr_atten_tpl.cls, expected_atten_cls) - self.assertEqual(tr_atten_tpl.rel_pos_emb_dim, rel_pos_emb_dim) - self.assertEqual(tr_atten_tpl.input_dim, input_dim) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/batch_utils.py b/lingvo/core/batch_utils.py deleted file mode 100644 index cd01bcd9..00000000 --- a/lingvo/core/batch_utils.py +++ /dev/null @@ -1,61 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Utilities to calculate batch sizes.""" - -from lingvo.core import cluster_factory -from lingvo.core import py_utils - - -def scale_infeed_to_global(infeed_batch_size, use_per_host_infeed): - """Obtains a global batch size from an infeed batch size and cluster configs. - - Args: - infeed_batch_size: int: Per-infeed batch size. - use_per_host_infeed: bool: Whether to use an individual infeed for each - host. - - Returns: - int: Global batch size. - """ - cluster = cluster_factory.Current() - if use_per_host_infeed and cluster.num_tpu_hosts > 0: - if not py_utils.use_tpu(): - raise ValueError('Scaling to TPU hosts without TPUs. {}'.format( - cluster.num_tpu_hosts)) - return infeed_batch_size * cluster.num_tpu_hosts - else: - return infeed_batch_size - - -def scale_split_to_infeed(split_batch_size, use_per_host_infeed): - """Obtains an infeed batch size from a split batch size and cluster configs. - - Args: - split_batch_size: int: Per-split batch size. - use_per_host_infeed: bool: Whether to use an individual infeed for each - host. - - Returns: - int: Per-infeed batch size. - """ - cluster = cluster_factory.Current() - global_batch_size = split_batch_size * cluster.num_splits_per_client - # If use_per_host_infeed, each input op is only responsible - # for generating a subset of the whole batch. - if use_per_host_infeed and cluster.num_tpu_hosts > 0: - return global_batch_size // cluster.num_tpu_hosts - else: - return global_batch_size diff --git a/lingvo/core/batch_utils_test.py b/lingvo/core/batch_utils_test.py deleted file mode 100644 index b39e0587..00000000 --- a/lingvo/core/batch_utils_test.py +++ /dev/null @@ -1,101 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.core.batch_utils.""" - -import itertools - -from absl.testing import flagsaver -from absl.testing import parameterized -from lingvo import compat as tf -from lingvo.core import batch_utils -from lingvo.core import cluster_factory - - -class BatchUtilsTest(tf.test.TestCase, parameterized.TestCase): - - @parameterized.parameters({'use_per_host_infeed': False}, - {'use_per_host_infeed': True}) - def testScaleInfeedToGlobalCPU(self, use_per_host_infeed): - with cluster_factory.ForTestingWorker(cpus=128): - self.assertEqual( - batch_utils.scale_infeed_to_global(1024, use_per_host_infeed), 1024) - - @parameterized.parameters({'use_per_host_infeed': False}, - {'use_per_host_infeed': True}) - def testScaleInfeedToGlobalGPU(self, use_per_host_infeed): - with cluster_factory.ForTestingWorker(gpus=128): - self.assertEqual( - batch_utils.scale_infeed_to_global(1024, use_per_host_infeed), 1024) - - @parameterized.parameters( - itertools.product( - (False, True), # use_per_host_infeed - (1, 4))) # num_tpu_hosts - def testScaleInfeedToGlobalTPU(self, use_per_host_infeed, num_tpu_hosts): - with flagsaver.flagsaver(xla_device='tpu', enable_asserts=False): - with cluster_factory.ForTestingWorker( - tpus=128, num_tpu_hosts=num_tpu_hosts): - num_infeeds = num_tpu_hosts if use_per_host_infeed else 1 - self.assertEqual( - batch_utils.scale_infeed_to_global(1024, use_per_host_infeed), - 1024 * num_infeeds) - - @parameterized.parameters( - itertools.product( - (False, True), # use_per_host_infeed - (1, 8))) # split_size - def testScaleSplitToInfeedCPU(self, use_per_host_infeed, split_size): - with cluster_factory.ForTestingWorker( - cpus=128, split_size=split_size) as cluster: - num_splits = 128 // split_size - self.assertEqual(cluster.num_splits_per_client, num_splits) - self.assertEqual( - batch_utils.scale_split_to_infeed(1024, use_per_host_infeed), - 1024 * num_splits) - - @parameterized.parameters( - itertools.product( - (False, True), # use_per_host_infeed - (1, 8))) # split_size - def testScaleSplitToInfeedGPU(self, use_per_host_infeed, split_size): - with cluster_factory.ForTestingWorker( - gpus=128, split_size=split_size) as cluster: - num_splits = 128 // split_size - self.assertEqual(cluster.num_splits_per_client, num_splits) - self.assertEqual( - batch_utils.scale_split_to_infeed(1024, use_per_host_infeed), - 1024 * num_splits) - - @parameterized.parameters( - itertools.product( - (False, True), # use_per_host_infeed - (1, 8), # split_size - (1, 4))) # num_tpu_hosts - def testScaleSplitToInfeedTPU(self, use_per_host_infeed, split_size, - num_tpu_hosts): - with cluster_factory.ForTestingWorker( - tpus=128, split_size=split_size, - num_tpu_hosts=num_tpu_hosts) as cluster: - num_splits = 128 // split_size - num_infeeds = num_tpu_hosts if use_per_host_infeed else 1 - self.assertEqual(cluster.num_splits_per_client, num_splits) - self.assertEqual( - batch_utils.scale_split_to_infeed(1024, use_per_host_infeed), - 1024 * num_splits // num_infeeds) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/beam_search_helper.py b/lingvo/core/beam_search_helper.py deleted file mode 100644 index 0f1d6cde..00000000 --- a/lingvo/core/beam_search_helper.py +++ /dev/null @@ -1,795 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Helper class for implementing a beam search decoder. - -Individual models just need to provide a few callback functions. -""" - -import collections -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import ops -from lingvo.core import py_utils - -from tensorflow.python.ops import inplace_ops - -# TODO(yonghui): -# 1) Change the tensor shape [max_decoder_time_steps, batch_size * -# num_hyps_per_beam] to [max_decoder_time_steps, num_hyps_per_beam, -# batch_size] to avoid confusing and mis-interpretation of the results. - -# Defines a namedtuple to store the results of BeamSearchDecode. It contains -# the following entries: -# done_hyps: A string Tensor of shape -# [max_decoder_time_steps, batch_size * num_hyps_per_beam] which can be -# either an empty string, or a serialized Hypothesis proto. The non-empty -# hyps in done_hyps are terminated hypotheses. The 'h'-th hyp for sample -# 'b' at time step 't' can be found at done_hyps[t, batch_size * h + b]. -# topk_hyps: A string Tensor of shape [batch_size, num_hyps_per_beam]. -# topk_hyps[b, h] is the h-th hypothesis for the sample 'b' in the -# batch, which can either be an empty string or a serialized Hypothesis -# proto. -# topk_ids: Int32 Tensor of shape [batch_size * num_hyps_per_beam, -# target_seq_len] which contains the IDs of the targets in each of the -# hypotheses in the beam for the samples in the batch. For sample -# 'b' in the batch, the h-th hypothesis for this sample can be found at -# position [b * num_hyps_per_beam + h, :]. -# topk_lens: Int32 Tensor of shape [batch_size * num_hyps_per_beam] which -# indicates the length (>=0) of each of the hypotheses. -# topk_scores: Float32 Tensor of shape [batch_size * num_hyps_per_beam] -# containing the scores (negative log probabilities) of each of the -# hypotheses in the beam. -# topk_decoded: A string Tensor of shape [batch_size * num_hyps_per_beam] which -# contains the decoded target strings in each of the hypotheses in the -# beam for the samples in the batch. The 'h'-th hyp for sample 'b' can -# be found at topk_decoded[b * num_hyps_per_beam + h] -BeamSearchDecodeOutput = collections.namedtuple( - 'BeamSearchDecodeOutput', - [ - 'done_hyps', 'topk_hyps', 'topk_ids', 'topk_lens', 'topk_scores', - 'topk_decoded', 'other_states' - ], -) -# Make the last attribute default to None. -BeamSearchDecodeOutput.__new__.__defaults__ = (None,) - - -# Keys in fusion state that can be two dimensional, with the batch element in -# the second dimension, requiring special treatment in hypothesis reordering. -POSSIBLY_TIME_MAJOR_STATE_KEYS = [ - 'misc_states.fusion_states.lm_states.prev_ids', - 'misc_states.fusion_states.lm_states.prev_paddings', - 'fusion_states.lm_states.prev_ids', - 'fusion_states.lm_states.prev_paddings', -] - - -class BeamSearchHelper(base_layer.BaseLayer): - """Helper class for performing beam search. - - The user of this helper class needs to implement three callbacks. - - This callback is called once only at the beginning of beam search: - - .. code-block:: none - - def InitBeamSearchState(theta, encoder_outputs, num_hyps_per_beam): - Args: - theta: A NestedMap object containing weights' values of this layer and - its children layers. - encoder_outputs: A NestedMap computed by encoder. - num_hyps_per_beam: An int, number hyps to keep for source sentence. - - Returns: - A tuple (initial_results, states): - - - initial_results: a `.NestedMap` of initial results. It must contain - the 'atten_probs' and 'log_probs' tensors. Optionally it may - contain 'step_ids'. - - - log_probs: The initial log probs for each of the tokens in the - target vocab of shape [num_hyps_per_beam * src_batch, vocab_size]. - src_batch "b" and hyp_per_beam "h" is represented at index - ``(h * src_batch + b)``. - - atten_probs: The initial attention probs, of shape - [num_hyps_per_beam * src_batch, src_len]. src_batch "b" and - hyp_per_beam "h" is represented at index ``(h * src_batch + b)``. - - step_ids: Optional. The initial ids of shape [num_hyps_per_beam * - src_batch, 1] for which to start the beam search. src_batch "b" - hyp_per_beam "h" is represented at index ``(h * src_batch + b)``. - If not specified, defaults to a tensor filled with target_sos_id. - - - states: a `.NestedMap` of tensors representing states that the - client would like to keep track of for each hyp. - - This callback is called once every decoding time step before beam_search_step - is called: - - .. code-block:: none - - def PreBeamSearchStepCallback(theta, - encoder_outputs, - step_ids, - in_states, - num_hyps_per_beam): - Args: - theta: A NestedMap object containing weights' values of this layer and - its children layers. - encoder_outputs: A NestedMap computed by encoder. - step_ids: A tensor of shape [num_hyps_per_beam * src_batch, 1]. - in_states: A `.NestedMap` of tensors representing states that the - clients would like to keep track of for each of the active hyps. - - Returns: - A tuple (results, out_states): - - - results: A `.NestedMap` of beam search results. It should contain - the 'atten_probs' and 'log_probs' tensors at the minimal. - Optionally it may contain 'is_last_chunk' if it is decoding a - neural transducer model. - - - atten_probs: The updated attention probs, of shape - [num_hyps_per_beam * src_batch, src_len]. src_batch "b" and - hyp_per_beam "h" is represented at index ``(h * src_batch + b)``. - - log_probs: Log prob for each of the tokens in the target vocab. - This is of shape [num_hyps_per_beam * src_batch, vocab_size]. - src_batch "b" and hyp_per_beam "h" is represented at index - ``(h * src_batch + b)``. - - is_last_chunk: Whether each of the hyp is at the end of a chunk. - If non-empty, it has shape [num_hyps_per_beam * src_batch, 1]. - - - out_states: A `.NestedMap`. The updated states. This 'out_states' - should be of the exact same structure as 'in_states' - - This callback is called once every decoding time step after beam_search_step - is called: - - .. code-block:: none - - def PostBeamSearchStepCallback(theta, - encoder_outputs, - new_step_ids, - other_states): - Args: - theta: A NestedMap object containing weights' values of this layer and - its children layers. - encoder_outputs: A NestedMap computed by encoder. - new_step_ids: Token ids for the next beam search step. - other_states: A `.NestedMap`. - - Returns: - final_states, A `.NestedMap`. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_hyps_per_beam', 8, - 'Num of hyps to keep per beam during decoding.') - p.Define( - 'target_seq_length_ratio', 1.0, - 'Ratio of the average target sequence length over the average ' - 'source sequence length.') - p.Define('length_normalization', 0.0, - 'Beam search length normalization ratio.') - p.Define('coverage_penalty', 0.0, 'Beam search coverage penalty.') - p.Define( - 'valid_eos_max_logit_delta', 5.0, - 'During beam search, allow to terminate a hyp only if its ' - 'logit is no more than than this value away from the logit of the ' - 'best candidate.') - p.Define( - 'beam_size', 3.0, - 'The maximum difference between best hyp and the worst in a beam.' - ' This allows to prune our search when none of the active hyp is' - ' close enough to the current best.') - p.Define('target_sos_id', 1, 'Id of the start of sentence token.') - p.Define('target_eos_id', 2, 'Id of the end of sentence token.') - p.Define( - 'target_eoc_id', -1, - 'Id of the end of chunk token. Used by neural transducer only.' - ' Set this id to a non-negative value only for NT.') - p.Define( - 'target_seq_len', 0, 'Maximum allowed target seq length. Note ' - 'that decoding terminates if an end of sentence token ' - 'is not emitted after target_seq_len decode steps.') - p.Define( - 'merge_paths', False, 'If true, hyps which are identical when ' - 'epsilons are removed will be combined into a single hyp. The ' - 'probability for that combined hyp will be the sum of the ' - 'probabilities of the component hyps. This can only be applied ' - 'for epsilon-emitting models (RNN-T and NT).') - p.Define( - 'allow_empty_terminated_hyp', True, 'Whether it is okay to consider a ' - 'hyp that consists only of epsilons as terminated. By default this ' - 'is true, as an utterance may consist of silence. It should be set ' - 'to false when EMBR training epsilon-emitting models (e.g., RNN-T), ' - 'which are prone to emit all-epsilon hyps even in the presence of ' - 'speech. Note that a hyp that terminates in EOS is not considered ' - 'empty, so this flag has no effect for non-epsilon-emitting models.') - p.Define( - 'ensure_full_beam', False, 'If True, we will not terminate the search ' - 'until both of these conditions are satisfied: we have found ' - 'num_hyps_per_beam terminated hyps AND no active hyps have a score ' - 'within beam_size of the best terminated hyp. If False, only the ' - 'second condition must be satisfied. Note that in either case, we can ' - 'also terminate if we have run for target_seq_len steps. Generally ' - 'this should be False unless beam search is being run as part of ' - 'minimum word error rate training.') - p.Define( - 'force_eos_in_last_step', False, - 'For all active hyps that are still on the beam after target_seq_len ' - 'steps, return partial hyps with EOS set as the last token.') - p.Define( - 'batch_major_state', True, 'If True, we use batch as the major ' - 'dimension of the hyp states. Otherwise, timing becomes the major ' - 'dimension, and the gathers are performed along the second-to-major ' - 'dimension.') - p.Define( - 'batch_major_compute', False, 'If True, the target batch dimension ' - 'is organized as num_beams by num_hyps_per_beam during the ' - 'ExtendStep computation and the cache is stored following this order. ' - 'So the topk indices into the cache for ReOrderHyps needs to be ' - 'reordered before usage. Otherwise, the indices will be directly used ' - 'without extra transformation.') - p.Define( - 'short_seq_limit', 0, - 'An integer, the sequence length limit for using early stop ' - 'method in attention layer (batch-major implementation). The sequence ' - 'is always treated as the default long sequence for decoding when the ' - 'limit is set to 0. For typical mt transformer config ' - '(batch 16, sequence length 150), the break even point is around 40 ' - 'on TPU V3, and 50 on TPU V2. This may slightly change for ' - 'different batch size and sequence length, which requires more ' - 'experiments to set the value.') - p.Define( - 'local_eos_threshold', -100.0, - 'During beam search, allow to terminate a hyp if the local score ' - 'for is greater than local_eos_threshold.') - p.name = 'beam_search' - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._model_uses_eoc_id = p.target_eoc_id >= 0 - - def _BeamSearchStep(self, theta, encoder_outputs, cur_step, step_ids, - core_bs_states, other_states, num_hyps_per_beam, - pre_beam_search_step_callback, - post_beam_search_step_callback): - """Extend beam search hyps for one step. - - | num_beams = Number of source sequences to be decoded. - | num_hyps_per_beam = Number of hyps to keep per source sequence. - | num_hyps = num_beams * num_hyps_per_beam - | src_seq_len = Number of time steps in the source sequence. - | src_batch = Number of examples in the source sequence. - | tgt_seq_len = Maximum allowed time steps in the target sequence. - | tgt_batch = num_hyps_per_beam * src_batch - - Args: - theta: A `.NestedMap` object containing weights' values of the decoder - layer and its children layers. - encoder_outputs: A `.NestedMap` containing encoder outputs to be passed to - the callbacks. - cur_step: A scalar int tensor, the current time step, 0-based. - step_ids: An int tensor of shape [num_hyps, 1]. The input ids to the - current search step. - core_bs_states: A tuple of core beam search states. This list is - maintained by this helper class. - other_states: A `.NestedMap` of other beam search states. This - `.NestedMap` is managed and updated by the client. It is expected that - each of its member tensors are of rank >= 1. t[i, ...] is the state of - the i-th hyp at the beginning of this search step. - num_hyps_per_beam: Num of hyps to keep per beam. - pre_beam_search_step_callback: The `PreBeamSearchStepCallback` callback. - See class header comments for more details. - post_beam_search_step_callback: The `PostBeamSearchStepCallback` callback. - See class header comments for more details. - - Returns: - A tuple of following elements for the next beam search step, - (next step, all_done, step_ids, core_bs_states, other_states) - """ - p = self.params - - bs_results, other_states = pre_beam_search_step_callback( - theta, encoder_outputs, step_ids, other_states, num_hyps_per_beam) - - (best_scores, cumulative_scores, in_scores, in_hyps, in_prev_hyps, - in_done_hyps, in_atten_probs) = core_bs_states - - (out_best_scores, out_cumulative_scores, out_scores, out_hyps, - out_prev_hyps, out_done_hyps, out_atten_probs, - all_done) = ops.beam_search_step( - tf.cast(bs_results.log_probs, dtype=p.dtype), - tf.cast(bs_results.atten_probs, dtype=p.dtype), - best_scores, - cumulative_scores, - in_scores, - in_hyps, - in_prev_hyps, - in_done_hyps, - in_atten_probs, - bs_results.is_last_chunk if self._model_uses_eoc_id else [], - cur_step, - eoc_id=p.target_eoc_id, - eos_id=p.target_eos_id, - beam_size=p.beam_size, - num_hyps_per_beam=num_hyps_per_beam, - valid_eos_max_logit_delta=p.valid_eos_max_logit_delta, - merge_paths=p.merge_paths, - allow_empty_terminated_hyp=p.allow_empty_terminated_hyp, - ensure_full_beam=p.ensure_full_beam, - force_eos_in_last_step=p.force_eos_in_last_step, - local_eos_threshold=p.local_eos_threshold) - - new_step_ids = tf.reshape(out_hyps[cur_step, :], tf.shape(step_ids)) - new_step_ids.set_shape(step_ids.get_shape()) - - old_hyp_ids = tf.reshape( - tf.slice(out_prev_hyps, begin=[cur_step, 0], size=[1, -1]), [-1]) - - if p.batch_major_compute: - # Transformed the indices into the key/value cache for fast decoding - # (prefix_states in other_states) due to the num_hyps dimension of - # cache is computed as num_beams by num_hyps_per_beam, which is different - # from the old_hyp_ids assumption (num_hyps_per_beam by num_beams). - # Both transpose and recomputation are required to correct the indices. - num_beams = tf.shape(best_scores)[0] - old_hyp_ids_in_cache_order = tf.reshape( - tf.transpose(tf.reshape(old_hyp_ids, [num_hyps_per_beam, -1])), [-1]) - old_hyp_ids_in_cache_order = ( - (old_hyp_ids_in_cache_order % num_beams) * num_hyps_per_beam + - old_hyp_ids_in_cache_order // num_beams) - - new_bs_states = (out_best_scores, out_cumulative_scores, out_scores, - out_hyps, out_prev_hyps, out_done_hyps, out_atten_probs) - - def ReOrderHyps(key, x_in): - """Reorders x_in based on prev hyp ids.""" - if (isinstance(x_in, tf.Tensor) and x_in.shape.ndims and - x_in.shape.ndims > 0): - if x_in.shape.ndims > 2 and not p.batch_major_state: - # Use corrected indices only here for batch major compute as key/value - # caches are the states being affected. - correct_old_hyp_ids = ( - old_hyp_ids_in_cache_order - if p.batch_major_compute else old_hyp_ids) - x_out = tf.gather(x_in, correct_old_hyp_ids, axis=1) - elif key in POSSIBLY_TIME_MAJOR_STATE_KEYS: - x_out = tf.gather(x_in, old_hyp_ids, axis=-1) - else: - x_out = tf.gather(x_in, old_hyp_ids) - x_out.set_shape(x_in.get_shape()) - return x_out - else: - return x_in - - new_other_states = other_states.TransformWithKey(ReOrderHyps) - - final_other_states = post_beam_search_step_callback(theta, encoder_outputs, - new_step_ids, - new_other_states) - - return (cur_step + 1, all_done, new_step_ids, new_bs_states, - final_other_states) - - def BeamSearchDecode(self, - theta, - encoder_outputs, - num_hyps_per_beam_override=0, - init_beam_search_state=None, - pre_beam_search_step_callback=None, - post_beam_search_step_callback=None, - max_steps=None): - """Performs beam-search based decoding. - - Args: - theta: A NestedMap object containing weights' values of the decoder layer - and its children layers. - encoder_outputs: A NestedMap containing encoder outputs to be passed to - the callbacks. Mostly opaque to BeamSearchHelper, except that it should - contain either a 'seq_lengths' field of shape [source_batch_size] or - a 'paddings' field of shape [source_max_lengths, source_batch_size]. - num_hyps_per_beam_override: If set to a value <= 0, this parameter is - ignored. If set to a value > 0, then this value will be used to override - `p.num_hyps_per_beam`. - init_beam_search_state: The `InitBeamSearchState` callback. Please refer - to the class header comments for more details. - pre_beam_search_step_callback: The `PreBeamSearchStepCallback` callback. - Please refer to the class header comments for more details. - post_beam_search_step_callback: The `PostBeamSearchStepCallback` callback. - Please refer to the class header comments for more details. - max_steps: maximum beam search steps. If None, use - self.params.target_seq_len. - - Returns: - A `BeamSearchDecodeOutput`. - """ - p = self.params - num_hyps_per_beam = p.num_hyps_per_beam - if num_hyps_per_beam_override > 0: - num_hyps_per_beam = num_hyps_per_beam_override - if max_steps is None: - max_steps = p.target_seq_len - - initial_results, other_states = init_beam_search_state( - theta, encoder_outputs, num_hyps_per_beam) - - num_hyps = tf.shape(initial_results.log_probs)[0] - num_beams = num_hyps // num_hyps_per_beam - - if 'step_ids' in initial_results: - # [num_hyps, 1] - step_ids = tf.ensure_shape(initial_results.step_ids, [None, 1]) - else: - step_ids = tf.fill([num_hyps, 1], - tf.constant(p.target_sos_id, dtype=tf.int32)) - - min_score = -1e36 - best_scores = (tf.zeros(shape=[num_beams], dtype=p.dtype) + min_score) - cumulative_scores = tf.zeros(shape=[num_hyps], dtype=p.dtype) - in_scores = tf.zeros([max_steps, num_hyps], dtype=p.dtype) - in_hyps = tf.zeros([max_steps, num_hyps], dtype=tf.int32) - in_prev_hyps = tf.zeros([max_steps, num_hyps], dtype=tf.int32) - in_done_hyps = tf.zeros([max_steps, num_hyps], dtype=tf.string) - bs_atten_probs = tf.zeros( - [max_steps, num_hyps, - tf.shape(initial_results.atten_probs)[1]], - dtype=p.dtype) - cur_step = tf.constant(0, dtype=tf.int32) - all_done = tf.constant(False, dtype=tf.bool) - core_bs_states = (best_scores, cumulative_scores, in_scores, in_hyps, - in_prev_hyps, in_done_hyps, bs_atten_probs) - - def LoopContinue(cur_step, all_done, unused_step_ids, unused_core_bs_states, - unused_other_states_list): - return tf.math.logical_and(cur_step < max_steps, - tf.math.logical_not(all_done)) - - def LoopBody(cur_step, unused_all_done, step_ids, core_bs_states, - other_states_list): - (cur_step, all_done, new_step_ids, new_bs_states, - new_other_states) = self._BeamSearchStep( - theta, encoder_outputs, cur_step, step_ids, core_bs_states, - other_states.Pack(other_states_list), num_hyps_per_beam, - pre_beam_search_step_callback, post_beam_search_step_callback) - return (cur_step, all_done, new_step_ids, new_bs_states, - new_other_states.Flatten()) - - flat_other_states = other_states.Flatten() - _, _, _, final_bs_states, flat_final_other_states = tf.while_loop( - LoopContinue, - LoopBody, - loop_vars=(cur_step, all_done, step_ids, core_bs_states, - flat_other_states), - parallel_iterations=10, - back_prop=False, - swap_memory=False, - shape_invariants=(tf.TensorShape(cur_step.get_shape()), - tf.TensorShape(all_done.get_shape()), - tf.TensorShape(step_ids.get_shape()), - _GetShapes(core_bs_states), - _GetShapes(flat_other_states, none_shapes=True))) - # [target_seq_len, num_beams * num_hyps_per_beam]. - final_done_hyps = final_bs_states[5] - final_other_states = other_states.Pack(flat_final_other_states) - - # Assume that `paddings` has shape [source_max_lengths, source_batch_size] - # by default, and compute `encoded_seq_lengths` accordingly. This can be - # overridden by directly passing `seq_lengths` in the `encoder_outputs` - # NestedMap. - encoded_seq_lengths = getattr(encoder_outputs, 'seq_lengths', None) - if encoded_seq_lengths is None: - source_paddings = encoder_outputs.padding - if isinstance(source_paddings, py_utils.NestedMap): - encoded_seq_lengths = tf.cast( - tf.round( - tf.reduce_sum(1.0 - tf.transpose(source_paddings.Flatten()[0]), - 1)), tf.int32) - else: - encoded_seq_lengths = tf.cast( - tf.round(tf.reduce_sum(1.0 - tf.transpose(source_paddings), 1)), - tf.int32) - - # [num_beams, num_hyps_per_beam]. - topk_hyps = ops.top_k_terminated_hyps( - final_done_hyps, - encoded_seq_lengths, - k=num_hyps_per_beam, - num_hyps_per_beam=num_hyps_per_beam, - length_normalization=p.length_normalization, - coverage_penalty=p.coverage_penalty, - target_seq_length_ratio=p.target_seq_length_ratio, - eoc_id=p.target_eoc_id, - merge_paths=p.merge_paths) - # [num_beams * num_hyps_per_beam, ...]. - max_seq_length = 0 if isinstance(max_steps, tf.Tensor) else max_steps - topk_ids, topk_lens, topk_scores = ops.unpack_hyp( - tf.reshape(topk_hyps, [-1]), max_seq_length=max_seq_length) - # [num_beams, num_hyps_per_beam]. - topk_scores = tf.reshape(topk_scores, tf.shape(topk_hyps)) - - return BeamSearchDecodeOutput(final_done_hyps, topk_hyps, topk_ids, - topk_lens, topk_scores, None, - final_other_states) - - -def _GetShapes(tensors, none_shapes=False): - """Util for getting nested structure of shapes from structure of tensors. - - Args: - tensors: Structure of Tensors to get shapes for. - none_shapes: Returns None shapes if true. - - Returns: - The same structure as tensors but of corresponding `TensorShape` objects. - """ - shapes = [] - for t in tf.nest.flatten(tensors): - shape = t.get_shape() if isinstance(t, tf.Tensor) else None - if none_shapes: - if shape: - shapes.append(tf.TensorShape([None] * len(shape))) - else: - shapes.append(tf.TensorShape(None)) - else: - shapes.append(tf.TensorShape(shape)) - - return type(tensors)(tf.nest.pack_sequence_as(tensors, shapes)) - - -def MergeBeamSearchOutputs(max_hyps_per_beam, beam_search_outputs): - """Merges beam search hyps from multiple decoders. - - Args: - max_hyps_per_beam: the number of top hyps in the merged results. Must be - less than or equal to total number of input hyps. - beam_search_outputs: a list of BeamSearchDecodeOutput objects. Must share - the same source_batch and max sequence length. - - Returns: - A BeamSearchDecodeOutput object containing max_hyps_per_beam hypotheses per - beam. - """ - source_batch = tf.shape(beam_search_outputs[0].topk_hyps)[0] - value_dict = {} - for output in beam_search_outputs: - hyps_per_beam = py_utils.with_dependencies([ - py_utils.assert_equal(source_batch, - tf.shape(output.topk_hyps)[0]), - ], - tf.shape(output.topk_hyps)[1]) - for k, v in output._asdict().items(): - if v is None: - continue - if k == 'done_hyps': - v = tf.transpose(v) - if k not in value_dict: - value_dict[k] = [] - value_dict[k].append(tf.reshape(v, [source_batch, hyps_per_beam, -1])) - - # Concatenate the tensors along the 'num_hyps_per_beam' dimension. - concatenated = {} - for k, values in value_dict.items(): - if len(values) != len(beam_search_outputs): - raise ValueError('Incomplete values for %s: %s' % - (k, beam_search_outputs)) - concatenated[k] = tf.concat(values, axis=1) - - scores = concatenated['topk_scores'] - scores = tf.where( - tf.equal(concatenated['topk_lens'], 0), tf.fill(tf.shape(scores), -1e6), - scores) - scores = tf.squeeze(scores, -1) - - # Select top max_hyps_per_beam indices per beam. - _, top_indices = tf.nn.top_k(scores, max_hyps_per_beam) - batch_ids = tf.tile( - tf.expand_dims(tf.range(source_batch), -1), [1, max_hyps_per_beam]) - # [source_batch, max_hyps_per_beam, 2] - gather_indices = tf.stack([batch_ids, top_indices], axis=-1) - - # Gather the merged top hyps according to 'gather_indices'. - top = beam_search_outputs[0]._asdict() - total_hyps = source_batch * max_hyps_per_beam - for k, v in concatenated.items(): - v = tf.gather_nd(v, gather_indices) - if k == 'done_hyps': - v = tf.transpose(tf.reshape(v, [total_hyps, -1])) - elif k == 'topk_hyps': - v = tf.reshape(v, [source_batch, max_hyps_per_beam]) - elif k == 'topk_ids': - v = tf.reshape(v, [total_hyps, -1]) - elif k in ('topk_lens', 'topk_scores', 'topk_decoded'): - v = tf.reshape(v, [total_hyps]) - else: - raise ValueError('Unexpected field: %s' % k) - top[k] = v - return BeamSearchDecodeOutput(**top) - - -class GreedySearchHelper(base_layer.BaseLayer): - """Helper class for performing greedy decoding. - - The user of this helper class needs to implement three callbacks just as in a - beam search decoder. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('target_sos_id', 1, 'Id of the start of sentence token.') - p.Define('target_eos_id', 2, 'Id of the end of sentence token.') - p.Define( - 'target_seq_len', 0, 'Maximum allowed target seq length. Note ' - 'that decoding terminates if an end of sentence token ' - 'is not emitted after target_seq_len decode steps.') - p.name = 'greedy_search' - return p - - def _GreedySearchStep(self, theta, encoder_outputs, cur_step, step_ids, - hyp_ids, hyp_lens, done_hyps, other_states, - pre_beam_search_step_callback, - post_beam_search_step_callback): - """Extend greedy search hyps for one step. - - Args: - theta: A `.NestedMap` object containing weights' values of the decoder - layer and its children layers. - encoder_outputs: A `.NestedMap` containing encoder outputs to be passed to - the callbacks. - cur_step: A scalar int tensor, the current time step, 0-based. - step_ids: An int tensor of shape [num_hyps, 1]. The input ids to the - current search step. - hyp_ids: An int tensor of shape [num_hyps, tgt_seq_len]. - hyp_lens: Valid length of all the hyps. Tokens after eos ids are not - counted. - done_hyps: Whether or not a hyp has finished. - other_states: A `.NestedMap` of other beam search states. This - `.NestedMap` is managed and updated by the client. It is expected that - each of its member tensors are of rank >= 1. t[i, ...] is the state of - the i-th hyp at the beginning of this search step. - pre_beam_search_step_callback: The `PreBeamSearchStepCallback` callback. - See class header comments for more details. - post_beam_search_step_callback: The `PostBeamSearchStepCallback` callback. - See class header comments for more details. - - Returns: - A tuple of following elements for the next greedy search step, - (next step, new_step_ids, hyp_ids, hyp_lens, done_hyps, other_states) - """ - p = self.params - # Increment hyp_lens by 1 if the hyp is not finished yet. - hyp_lens = hyp_lens + (1 - tf.cast(done_hyps, tf.int32)) - - bs_results, new_other_states = pre_beam_search_step_callback( - theta, encoder_outputs, step_ids, other_states, 1) # num_hyps_per_beam - new_step_ids = tf.math.argmax(bs_results.log_probs, 1) - new_step_ids = tf.cast(new_step_ids, tf.int32) - new_step_ids = tf.reshape(new_step_ids, tf.shape(step_ids)) - final_other_states = post_beam_search_step_callback(theta, encoder_outputs, - new_step_ids, - new_other_states) - - # Stash new_step_ids into the right slot. - new_step_ids_1d = tf.reshape(new_step_ids, [-1]) - hyp_ids = inplace_ops.alias_inplace_update(hyp_ids, cur_step, - new_step_ids_1d) - # Update done_hyps if the current step_ids is the end of sequence token. - done_hyps = tf.math.logical_or(done_hyps, - tf.equal(new_step_ids_1d, p.target_eos_id)) - - return (cur_step + 1, new_step_ids, hyp_ids, hyp_lens, done_hyps, - final_other_states) - - def GreedySearchDecode(self, - theta, - encoder_outputs, - init_beam_search_state=None, - pre_beam_search_step_callback=None, - post_beam_search_step_callback=None, - max_steps=None): - """Performs greedy-search based decoding. - - Args: - theta: A NestedMap object containing weights' values of the decoder layer - and its children layers. - encoder_outputs: A NestedMap containing encoder outputs to be passed to - the callbacks. - init_beam_search_state: The `InitBeamSearchState` callback. Please refer - to the class header comments for more details. - pre_beam_search_step_callback: The `PreBeamSearchStepCallback` callback. - Please refer to the class header comments for more details. - post_beam_search_step_callback: The `PostBeamSearchStepCallback` callback. - Please refer to the class header comments for more details. - max_steps: maximum beam search steps. If None, use - self.params.target_seq_len. - - Returns: - A tuple (hyp_ids, hyp_lens, done_hyps). Note that num_hyps is same as - src_batch_size. - - - hyp_ids: [num_hyps, max_step]. Hyps end with token if the - token is encountered during search. - - hyp_lens: [num_hyps]. - - done_hyps: [num_hyps], whether or not an eos is encountered. - """ - p = self.params - if max_steps is None: - max_steps = p.target_seq_len - - initial_results, other_states = init_beam_search_state( - theta, - encoder_outputs, - 1 # num_hyps_per_beam - ) - - num_hyps = tf.shape(initial_results.log_probs)[0] - - if 'step_ids' in initial_results: - # [num_hyps, 1] - step_ids = tf.ensure_shape(initial_results.step_ids, [None, 1]) - else: - step_ids = tf.fill([num_hyps, 1], - tf.constant(p.target_sos_id, dtype=tf.int32)) - - cur_step = tf.constant(0, dtype=tf.int32) - done_hyps = inplace_ops.empty(shape=[num_hyps], dtype=tf.bool, init=True, - name='done_hyps') - hyp_lens = inplace_ops.empty(shape=[num_hyps], dtype=tf.int32, init=True, - name='hyp_lens') - hyp_ids = inplace_ops.empty( - shape=[max_steps, num_hyps], dtype=tf.int32, init=True, - name='hyp_ids') - - def LoopContinue(cur_step, unused_step_ids, unused_hyp_ids, unused_hyp_lens, - done_hyps, unused_other_states_list): - return tf.math.logical_and(cur_step < max_steps, - tf.math.logical_not(tf.reduce_all(done_hyps))) - - def LoopBody(cur_step, step_ids, hyp_ids, hyp_lens, done_hyps, - other_states_list): - (cur_step, new_step_ids, hyp_ids, hyp_lens, done_hyps, - new_other_states) = self._GreedySearchStep( - theta, encoder_outputs, cur_step, - step_ids, hyp_ids, hyp_lens, done_hyps, - other_states.Pack(other_states_list), pre_beam_search_step_callback, - post_beam_search_step_callback) - return (cur_step, new_step_ids, hyp_ids, hyp_lens, done_hyps, - new_other_states.Flatten()) - - flat_other_states = other_states.Flatten() - _, _, final_hyp_ids, final_hyp_lens, final_done_hyps, _ = tf.while_loop( - LoopContinue, - LoopBody, - loop_vars=(cur_step, step_ids, hyp_ids, hyp_lens, done_hyps, - flat_other_states), - parallel_iterations=10, - back_prop=False, - swap_memory=False, - shape_invariants=(tf.TensorShape(cur_step.get_shape()), - tf.TensorShape(step_ids.get_shape()), - tf.TensorShape(hyp_ids.get_shape()), - tf.TensorShape(hyp_lens.get_shape()), - tf.TensorShape(done_hyps.get_shape()), - _GetShapes(flat_other_states, none_shapes=True))) - - # transpose hyp_ids so it matches BeamSearchDecode's output - final_hyp_ids = tf.transpose(final_hyp_ids) - return final_hyp_ids, final_hyp_lens, final_done_hyps diff --git a/lingvo/core/beam_search_helper_test.py b/lingvo/core/beam_search_helper_test.py deleted file mode 100644 index bd39eada..00000000 --- a/lingvo/core/beam_search_helper_test.py +++ /dev/null @@ -1,308 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for beam_search_helper.""" - -import lingvo.compat as tf -from lingvo.core import beam_search_helper -from lingvo.core import py_utils -from lingvo.core import test_utils -import numpy as np - - -def GetBeamSearchHelperResults(sess, num_hyps_per_beam, pass_seq_lengths=False): - np.random.seed(9384758) - tf.random.set_seed(8274758) - vocab_size = 12 - src_len = 5 - tgt_len = 7 - src_batch_size = 2 - tgt_batch_size = src_batch_size * num_hyps_per_beam - p = beam_search_helper.BeamSearchHelper.Params().Set( - name='bsh', target_seq_len=tgt_len) - bs_helper = p.Instantiate() - - def InitBeamSearchState(unused_theta, unused_encoder_outputs, - unused_num_hyps_per_beam): - atten_probs = tf.constant( - np.random.normal(size=(tgt_batch_size, src_len)), dtype=tf.float32) - return (py_utils.NestedMap({ - 'log_probs': tf.zeros([tgt_batch_size, vocab_size]), - 'atten_probs': atten_probs, - }), py_utils.NestedMap({'atten_probs': atten_probs})) - - def PreBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_step_ids, states, - unused_num_hyps_per_beam): - atten_probs = tf.identity(states.atten_probs) - logits = tf.random.normal([tgt_batch_size, vocab_size], seed=8273747) - return (py_utils.NestedMap({ - 'atten_probs': atten_probs, - 'log_probs': logits - }), states) - - def PostBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_new_step_ids, states): - return states - - src_enc = tf.random.normal([src_len, src_batch_size, 8], seed=982774838) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float32) - encoder_outputs = py_utils.NestedMap(encoded=src_enc, padding=src_enc_padding) - if pass_seq_lengths: - encoder_outputs['seq_lengths'] = tf.constant([4, 3], dtype=tf.int32) - - theta = py_utils.NestedMap() - decoder_output = bs_helper.BeamSearchDecode(theta, encoder_outputs, - num_hyps_per_beam, - InitBeamSearchState, - PreBeamSearchStepCallback, - PostBeamSearchStepCallback) - - topk_ids, topk_lens, topk_scores = sess.run([ - decoder_output.topk_ids, decoder_output.topk_lens, - decoder_output.topk_scores - ]) - return topk_ids, topk_lens, topk_scores - - -class BeamSearchHelperTest(test_utils.TestCase): - - # TODO(yonghui): Add more thorough tests. - def testBeamSearchHelper(self): - with self.session(use_gpu=False) as sess: - topk_ids, topk_lens, topk_scores = GetBeamSearchHelperResults( - sess, num_hyps_per_beam=3) - print(np.array_repr(topk_ids)) - print(np.array_repr(topk_lens)) - print(np.array_repr(topk_scores)) - expected_topk_ids = [[4, 3, 4, 3, 2, 0, 0], [4, 3, 11, 2, 0, 0, 0], - [4, 3, 6, 2, 0, 0, 0], [6, 0, 4, 6, 6, 11, 2], - [6, 0, 4, 6, 1, 2, 0], [6, 0, 4, 6, 6, 2, 0]] - expected_topk_lens = [5, 4, 4, 7, 6, 6] - expected_topk_scores = [[8.27340603, 6.26949024, 5.59490776], - [9.74691486, 8.46679497, 7.14809656]] - self.assertEqual(expected_topk_ids, topk_ids.tolist()) - self.assertEqual(expected_topk_lens, topk_lens.tolist()) - self.assertAllClose(expected_topk_scores, topk_scores) - - def testBeamSearchHelperHypsOne(self): - with self.session(use_gpu=False) as sess: - topk_ids, topk_lens, topk_scores = GetBeamSearchHelperResults( - sess, num_hyps_per_beam=1) - print(np.array_repr(topk_ids)) - print(np.array_repr(topk_lens)) - print(np.array_repr(topk_scores)) - expected_topk_ids = [[9, 2, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]] - expected_topk_lens = [2, 0] - expected_topk_scores = [[3.778749], [0.0]] - self.assertEqual(expected_topk_ids, topk_ids.tolist()) - self.assertEqual(expected_topk_lens, topk_lens.tolist()) - self.assertAllClose(expected_topk_scores, topk_scores) - - def testBeamSearchHelperWithSeqLengths(self): - with self.session(use_gpu=False) as sess: - topk_ids, topk_lens, topk_scores = GetBeamSearchHelperResults( - sess, num_hyps_per_beam=3, pass_seq_lengths=True) - print(np.array_repr(topk_ids)) - print(np.array_repr(topk_lens)) - print(np.array_repr(topk_scores)) - expected_topk_ids = [[4, 3, 4, 3, 2, 0, 0], [4, 3, 11, 2, 0, 0, 0], - [4, 3, 6, 2, 0, 0, 0], [6, 0, 4, 6, 6, 11, 2], - [6, 0, 4, 6, 1, 2, 0], [6, 0, 4, 6, 6, 2, 0]] - expected_topk_lens = [5, 4, 4, 7, 6, 6] - expected_topk_scores = [[8.27340603, 6.26949024, 5.59490776], - [9.74691486, 8.46679497, 7.14809656]] - self.assertEqual(expected_topk_ids, topk_ids.tolist()) - self.assertEqual(expected_topk_lens, topk_lens.tolist()) - self.assertAllClose(expected_topk_scores, topk_scores) - - def testCustomStepIds(self): - with self.session(use_gpu=False): - np.random.seed(9384758) - tf.random.set_seed(8274758) - vocab_size = 12 - src_len = 5 - tgt_len = 7 - num_hyps_per_beam = 3 - src_batch_size = 2 - tgt_batch_size = src_batch_size * num_hyps_per_beam - p = beam_search_helper.BeamSearchHelper.Params().Set( - name='bsh', target_seq_len=tgt_len) - bs_helper = p.Instantiate() - - def InitBeamSearchState(unused_theta, unused_encoder_outputs, - unused_num_hyps_per_beam): - atten_probs = tf.constant( - np.random.normal(size=(tgt_batch_size, src_len)), dtype=tf.float32) - return (py_utils.NestedMap({ - 'log_probs': tf.zeros([tgt_batch_size, vocab_size]), - 'atten_probs': atten_probs, - 'step_ids': tf.zeros([tgt_batch_size, 1], dtype=tf.int32) - }), py_utils.NestedMap({'atten_probs': atten_probs})) - - def PreBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_step_ids, states, - unused_num_hyps_per_beam): - atten_probs = tf.identity(states.atten_probs) - logits = tf.random.normal([tgt_batch_size, vocab_size], seed=8273747) - return (py_utils.NestedMap({ - 'atten_probs': atten_probs, - 'log_probs': logits - }), states) - - def PostBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_new_step_ids, states): - return states - - src_enc = tf.random.normal([src_len, src_batch_size, 8], seed=982774838) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float32) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - - theta = py_utils.NestedMap() - decoder_output = bs_helper.BeamSearchDecode(theta, encoder_outputs, - num_hyps_per_beam, - InitBeamSearchState, - PreBeamSearchStepCallback, - PostBeamSearchStepCallback) - - topk_ids, topk_lens, topk_scores = self.evaluate([ - decoder_output.topk_ids, decoder_output.topk_lens, - decoder_output.topk_scores - ]) - print(np.array_repr(topk_ids)) - print(np.array_repr(topk_lens)) - print(np.array_repr(topk_scores)) - expected_topk_ids = [[4, 3, 4, 3, 2, 0, 0], [4, 3, 11, 2, 0, 0, 0], - [4, 3, 6, 2, 0, 0, 0], [6, 0, 4, 6, 6, 11, 2], - [6, 0, 4, 6, 1, 2, 0], [6, 0, 4, 6, 6, 2, 0]] - expected_topk_lens = [5, 4, 4, 7, 6, 6] - expected_topk_scores = [[8.27340603, 6.26949024, 5.59490776], - [9.74691486, 8.46679497, 7.14809656]] - self.assertEqual(expected_topk_ids, topk_ids.tolist()) - self.assertEqual(expected_topk_lens, topk_lens.tolist()) - self.assertAllClose(expected_topk_scores, topk_scores) - - -class MergeBeamSearchOutputsTest(test_utils.TestCase): - - def testMergeBeamSearchOutputs(self): - with self.session(): - topk_scores_1 = [[1., 3., 5.], [-2., -1., 0.]] - topk_ids_1 = [[[10, 11, 12], [30, 31, 32], [50, 51, 52]], - [[20, 21, 22], [10, 11, 12], [0, 0, 0]]] - topk_lens_1 = [[3, 3, 2], [3, 3, 0]] - topk_hyps_1 = [['one', 'three', 'five'], ['minus two', 'minus one', '']] - topk_1 = beam_search_helper.BeamSearchDecodeOutput( - None, tf.constant(topk_hyps_1), - tf.reshape(tf.constant(topk_ids_1), [6, -1]), - tf.reshape(tf.constant(topk_lens_1), [-1]), - tf.reshape(tf.constant(topk_scores_1), [-1]), None, None) - - topk_scores_2 = [[2., 4.], [-3., 0.]] - topk_ids_2 = [[[20, 21, 22], [40, 41, 42]], [[30, 31, 33], [0, 0, 0]]] - topk_lens_2 = [[3, 2], [3, 0]] - topk_hyps_2 = [['two', 'four'], ['minus three', '']] - topk_2 = beam_search_helper.BeamSearchDecodeOutput( - None, tf.constant(topk_hyps_2), - tf.reshape(tf.constant(topk_ids_2), [4, -1]), - tf.reshape(tf.constant(topk_lens_2), [-1]), - tf.reshape(tf.constant(topk_scores_2), [-1]), None, None) - - topk = beam_search_helper.MergeBeamSearchOutputs(3, [topk_1, topk_2]) - self.assertIsNone(topk.done_hyps) - self.assertIsNone(topk.topk_decoded) - self.assertAllEqual([5., 4., 3., -1., -2., -3.], topk.topk_scores.eval()) - self.assertAllEqual([2, 2, 3, 3, 3, 3], topk.topk_lens.eval()) - self.assertAllEqual([[50, 51, 52], [40, 41, 42], [30, 31, 32], - [10, 11, 12], [20, 21, 22], [30, 31, 33]], - topk.topk_ids.eval()) - self.assertAllEqual([[b'five', b'four', b'three'], - [b'minus one', b'minus two', b'minus three']], - topk.topk_hyps.eval()) - - -class GreedySearchHelperTest(test_utils.TestCase): - - def testGreedySearchHelper(self): - with self.session(use_gpu=False): - np.random.seed(9384758) - tf.random.set_seed(8274758) - vocab_size = 12 - src_len = 5 - tgt_len = 7 - src_batch_size = 2 - tgt_batch_size = src_batch_size - p = beam_search_helper.GreedySearchHelper.Params().Set( - name='gsh', target_seq_len=tgt_len) - gs_helper = p.Instantiate() - - def InitGreedySearchState(unused_theta, unused_encoder_outputs, - unused_num_hyps_per_beam): - atten_probs = tf.constant( - np.random.normal(size=(tgt_batch_size, src_len)), dtype=tf.float32) - return (py_utils.NestedMap({ - 'log_probs': tf.zeros([tgt_batch_size, vocab_size]), - 'atten_probs': atten_probs, - }), py_utils.NestedMap({'atten_probs': atten_probs})) - - def PreGreedySearchStepCallback(unused_theta, unused_encoder_outputs, - unused_step_ids, states, - unused_num_hyps_per_beam): - atten_probs = tf.identity(states.atten_probs) - logits = tf.random.normal([tgt_batch_size, vocab_size], seed=8273747) - return (py_utils.NestedMap({ - 'atten_probs': atten_probs, - 'log_probs': logits - }), states) - - def PostGreedySearchStepCallback(unused_theta, unused_encoder_outputs, - unused_new_step_ids, states): - return states - - src_enc = tf.random.normal([src_len, src_batch_size, 8], seed=982774838) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float32) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - - theta = py_utils.NestedMap() - (final_hyp_ids, final_hyp_lens, - final_done_hyps) = gs_helper.GreedySearchDecode( - theta, encoder_outputs, InitGreedySearchState, - PreGreedySearchStepCallback, PostGreedySearchStepCallback) - - (final_hyp_ids, final_hyp_lens, final_done_hyps) = self.evaluate( - [final_hyp_ids, final_hyp_lens, final_done_hyps]) - - print(np.array_repr(final_hyp_ids)) - print(np.array_repr(final_hyp_lens)) - print(np.array_repr(final_done_hyps)) - - expected_hyp_ids = [[2, 2, 6, 7, 1, 9, 4], [3, 9, 3, 9, 6, 5, 10]] - expected_hyp_lens = [1, 7] - expected_done_hyps = [True, False] - self.assertEqual(expected_hyp_ids, final_hyp_ids.tolist()) - self.assertEqual(expected_hyp_lens, final_hyp_lens.tolist()) - self.assertEqual(expected_done_hyps, final_done_hyps.tolist()) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/bfloat16_variables.py b/lingvo/core/bfloat16_variables.py deleted file mode 100644 index 7c916345..00000000 --- a/lingvo/core/bfloat16_variables.py +++ /dev/null @@ -1,93 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Various methods for bfloat16 training & inference. - -Bfloat16VariableSaveable: Saveable that restores variable into bfloat16 type. - -Usage: - - Given a checkpoint_path with a variable of type tf.float32, this particular - saveable allows restore them as tf.bfloat16. This is specifically useful for - inference. - - Say: checkpoint_path contains a variable "var" with dtype tf.float32:: - - variable_name = "var" - original_dtype = tf.float32 - - bfloat16_var = tf.Variable( - 0.0, name=variable_name, dtype=tf.bfloat16, use_resource=True) - - saveable = bfloat16_variables.Bfloat16VariableSaveable( - bfloat16_var, original_dtype, slice_spec, variable_name) - saver = tf.train.Saver( - {variable_name: saveable}, restore_sequentially=True) - saver.restore(sess, checkpoint_path) - - # bfloat16_var is now loaded from the checkpoint. -""" -import lingvo.compat as tf -from tensorflow.python.training import saver - - -class Bfloat16VariableSaveable(saver.BaseSaverBuilder.SaveableObject): - """Saveable that loads Variables as bfloat16.""" - - def __init__(self, var, orig_dtype, slice_spec, name): - # TODO(rohananil): Investigate if we can avoid using a callable, instead - # change the saveable api to make use of dtype passed in. - def _make_callable_var(): - return var - - spec = saver.BaseSaverBuilder.SaveSpec( - _make_callable_var, - slice_spec, - name, - dtype=orig_dtype, - device=var.device) - super().__init__(var, [spec], name) - - def restore(self, restored_tensors, restored_shapes): - restored_tensor = restored_tensors[0] - if restored_shapes is not None: - restored_tensor = tf.reshape(restored_tensor, restored_shapes[0]) - return tf.assign( - self.op, - tf.cast(restored_tensor, tf.bfloat16), - validate_shape=restored_shapes is None and - self.op.get_shape().is_fully_defined()) - - -def get_saver_spec_for_variables_with_bf16_overrides(variables_to_restore): - """Returns a dictionary containing overrides to load variables as bf16. - - Args: - variables_to_restore: A mapping from variable to name (on checkpoint) to the - Variable object. - - Returns: - A saver dictionary which can be used to load from checkpoints. - """ - saver_dict = {} - for var_name, v in variables_to_restore.items(): - if v.dtype == tf.bfloat16: - # TODO(rohananil): Add support for PartitionedVariables if there is - # demand. - savable = Bfloat16VariableSaveable(v, tf.float32, '', var_name) - saver_dict[var_name] = savable - else: - saver_dict[var_name] = v - return saver_dict diff --git a/lingvo/core/bfloat16_variables_test.py b/lingvo/core/bfloat16_variables_test.py deleted file mode 100644 index adfeac41..00000000 --- a/lingvo/core/bfloat16_variables_test.py +++ /dev/null @@ -1,57 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for bfloat16_variables.""" - -import os -import lingvo.compat as tf -from lingvo.core import bfloat16_variables -from lingvo.core import test_utils - - -class Bfloat16VariablesTest(test_utils.TestCase): - - def testBfloat16Reload(self): - checkpoint_path = os.path.join(self.get_temp_dir(), "bfloat16_restore") - - # Create a resource variable of type tf.float32 and save them to disk. - g_for_save_graph = tf.Graph() - fl = 0.99 - with self.session(graph=g_for_save_graph) as sess: - v0 = tf.Variable(fl, name="v0", dtype=tf.float32, use_resource=True) - self.evaluate(tf.global_variables_initializer()) - self.assertAlmostEqual(fl, v0.eval()) - - saver = tf.train.Saver({ - "v0": v0, - }, restore_sequentially=True) - val = saver.save(sess, checkpoint_path) - self.assertEqual(checkpoint_path, val) - - # Restore the variable as bfloat16. - g_for_restore_graph = tf.Graph() - with self.session(graph=g_for_restore_graph) as sess: - v0 = tf.Variable(0.0, name="v0", dtype=tf.bfloat16, use_resource=True) - self.evaluate(tf.global_variables_initializer()) - self.assertAlmostEqual(0.0, v0.eval()) - saveable = bfloat16_variables.Bfloat16VariableSaveable( - v0, tf.float32, "", "v0") - saver = tf.train.Saver({"v0": saveable}, restore_sequentially=True) - saver.restore(sess, checkpoint_path) - self.assertAlmostEqual(fl, v0.eval()) - - -if __name__ == "__main__": - tf.test.main() diff --git a/lingvo/core/bn_layers.py b/lingvo/core/bn_layers.py deleted file mode 100644 index ebea9e62..00000000 --- a/lingvo/core/bn_layers.py +++ /dev/null @@ -1,823 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================= -"""Batch normalization layers.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core import summary_utils - -from tensorflow.python.ops import nn # pylint:disable=g-direct-tensorflow-import -from tensorflow.python.tpu import tpu_function # pylint:disable=g-direct-tensorflow-import - -_BN_FLOPS_PER_ELEMENT = 10 - - -# TODO(rpang): move AddingAccumulator to a separate library. -class AddingAccumulator(base_layer.Accumulator): - """Accumulator for the sufficient statistics.""" - - def __init__(self, shape, dtype): - super().__init__() - self.dtype = dtype - self.shape = shape - - def DefaultValue(self): - """Returns the default value of the accumulator.""" - return tf.zeros(self.shape, dtype=self.dtype) - - def Update(self, value): - """Adds value to the accumulator.""" - self.SetValue(self.GetValue() + tf.cast(value, self.dtype)) - - -def ComputeMomentsWithPadding(inputs, - padding, - reduce_over_dims, - cumulative_axis=None, - enable_cross_replica_sum_on_tpu=False, - keepdims=False): - """Computes mean and variance over the valid data points in inputs.""" - mask = 1.0 - padding - inputs = py_utils.with_dependencies([ - py_utils.assert_equal(tf.rank(inputs), tf.rank(mask)), - py_utils.assert_greater_equal(mask, tf.zeros_like(mask)), - ], inputs) - sum_v = tf.reduce_sum( - inputs * tf.cast(mask, inputs.dtype), reduce_over_dims, keepdims=keepdims) - count_v = tf.reduce_sum(mask, reduce_over_dims, keepdims=keepdims) - - if cumulative_axis is not None: - sum_v = tf.math.cumsum(sum_v, axis=cumulative_axis) - count_v = tf.math.cumsum(count_v, axis=cumulative_axis) - # Input shape is guaranteed to be a multiple of mask shape because the - # inputs * mask op above was successfully broadcasted. - input_size_on_reduced_dims = tf.reduce_prod( - tf.gather(tf.shape(inputs), reduce_over_dims)) - mask_size_on_reduced_dims = tf.reduce_prod( - tf.gather(tf.shape(mask), reduce_over_dims)) - mask_multiplier = tf.math.truediv(input_size_on_reduced_dims, - mask_size_on_reduced_dims) - count_v *= tf.cast(mask_multiplier, count_v.dtype) - if py_utils.use_tpu() and enable_cross_replica_sum_on_tpu: - sum_v = tf.tpu.cross_replica_sum(sum_v) - count_v = tf.tpu.cross_replica_sum(count_v) - - count_v = tf.maximum(count_v, 1.0) - mean = sum_v / count_v - sum_vv = tf.reduce_sum( - (inputs - mean) * (inputs - mean) * mask, - reduce_over_dims, - keepdims=keepdims) - if cumulative_axis is not None: - sum_vv = tf.math.cumsum(sum_vv, axis=cumulative_axis) - - if py_utils.use_tpu() and enable_cross_replica_sum_on_tpu: - sum_vv = tf.tpu.cross_replica_sum(sum_vv) - - variance = py_utils.with_dependencies([ - py_utils.assert_greater_equal(sum_vv, tf.zeros_like(sum_vv)), - ], sum_vv / count_v) - return mean, variance - - -class BatchNormLayer(base_layer.BaseLayer): - """Batch normalization layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('dim', 0, 'Depth of the input/output.') - p.Define( - 'decay', 0.999, - 'Decay in updating the mean and variance moving average used in' - ' batch normalization.') - p.Define( - 'enable_cross_replica_sum_on_tpu', True, - 'If true, calls cross_replica_sum to the aggregate moving averages' - ' across all replicas.') - p.Define( - 'use_moving_avg_in_training', False, - 'If True, use global moving avg (mean, variance) during training' - ' to avoid mismatch between train and eval, which then' - ' essentially acts as an adaptive normalization step.') - p.Define( - 'freeze_bn_stats', False, - 'If True, uses moving avg (mean, variance) during both training and ' - 'inference. It behaves like force_eval but the gamma/beta are still ' - 'trained when do_eval is False. The moving mean/var can be set by ' - 'loading pretrained checkpoints. A use case is training detectors ' - 'based on an pretrained checkpoint while BN stats are frozen.') - p.Define( - 'gamma_zero_init', False, - 'If True, initialize gamma to zeros according to the technique ' - 'introduced in the tech report: https://arxiv.org/abs/1706.02677') - p.Define( - 'gamma_one_init', False, - 'If True, explicitly initialize gamma to one without invoking ' - 'theta_fn.') - # TODO(rpang): remove this hparam, as it is replaced - # by p.train.ema_decay_moving_vars. - p.Define( - 'add_stats_to_moving_average_variables', None, - 'If True, adds (mean, variance) to the MOVING_AVERAGE_VARIABLES ' - 'collection to be compatible with ema_decay. ' - 'Recommendation: set to True for new models, and to False to maintain ' - 'checkpoint compatibility.') - p.Define('set_padded_output_to_zero', True, - 'If True, sets the padded outputs to zero.') - p.Define( - 'use_fused_batch_norm_for_eval', False, - 'If True, uses tf.compat.v1.nn.fused_batch_norm instead of ' - 'tf.nn.batch_normalization during eval. The fused version may be more ' - 'efficient but it has more restrictions on the expected input shapes.' - 'The input tensor has to be rank 4, where the first dimension ' - 'corresponds to the batch, and the last dimension corresponds to the ' - 'features to normalize over. This usually corresponds to NHWC with ' - 'image inputs. Note that fused_batch_norm wants to track its own ' - 'mean and variance during training, so we are unable to use it ' - 'for training since we want to have a custom mean and variance to ' - 'support padding.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._epsilon = 0.001 - self._decay = p.decay - - def _GetWeightShape(self): - return [self.params.dim] - - def _CreateLayerVariables(self): - p = self.params - - beta_pc = py_utils.WeightParams( - shape=self._GetWeightShape(), - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - gamma_pc = py_utils.WeightParams( - shape=self._GetWeightShape(), - init=py_utils.WeightInit.Constant(1.0) - if p.gamma_one_init else py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - if not p.use_moving_avg_in_training: - self.CreateVariable('beta', beta_pc) - if p.gamma_zero_init or p.gamma_one_init: - # initialization to BN gamma - self.CreateVariable('gamma', gamma_pc) - else: - # Note, The real gamma to use is 1 + gamma. - self.CreateVariable('gamma', gamma_pc, lambda x: 1.0 + x) - - # Two statistics. - moving_collections = ['moving_vars', self.__class__.__name__ + '_vars'] - if p.add_stats_to_moving_average_variables: - moving_collections += [tf.GraphKeys.MOVING_AVERAGE_VARIABLES] - elif p.add_stats_to_moving_average_variables is None: - # TODO(rpang): force all models to set this param explicitly. - tf.logging.warning( - 'BatchNormLayer.add_stats_to_moving_average_variables should be ' - 'set to True for new models, and to False explicitly for ' - 'checkpoint compatibility.') - # Add to the MOVING_AVERAGE_VARIABLES collection so that they are returned - # by tf.moving_average_variables() and included in EMA variables if - # ema_decay is enabled. - mva = py_utils.WeightParams( - shape=[p.dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=moving_collections) - self.CreateVariable( - 'moving_mean', - mva, - trainable=False, - aggregation=tf.VariableAggregation.MEAN) - - mvv = py_utils.WeightParams( - shape=[p.dim], - init=py_utils.WeightInit.Constant(1.0), - dtype=p.dtype, - collections=moving_collections) - self.CreateVariable( - 'moving_variance', - mvv, - trainable=False, - aggregation=tf.VariableAggregation.MEAN) - - @property - def epsilon(self): - return self._epsilon - - def _GetDefaultPaddings(self, inputs): - """Gets the default paddings for an input.""" - return tf.zeros( - tf.concat([tf.shape(inputs)[:-1], [1]], 0), dtype=inputs.dtype) - - def _GetBetaGamma(self, theta, inputs, **kwargs): - del inputs - del kwargs - p = self.params - if p.use_moving_avg_in_training: - beta = 0.0 - gamma = 1.0 - else: - beta = theta.beta - gamma = theta.gamma - return beta, gamma - - def GetCurrentMoments(self, theta): - """Gets the current computed moments, which should be applied at eval. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - Returns: - Tuple of (mean, variance, beta, gamma). - """ - p = self.params - if p.use_moving_avg_in_training: - return self.vars.moving_mean, self.vars.moving_variance, 0.0, 1.0 - else: - return (self.vars.moving_mean, self.vars.moving_variance, theta.beta, - theta.gamma) - - def ComputeAndUpdateMoments(self, theta, inputs, paddings=None, **kwargs): - """Computes moments and updates state. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. Shaped [..., dim]. - paddings: The paddings tensor. Shaped [..., 1], with the same rank as the - input tensor. - **kwargs: Additional inputs. - - Returns: - Tuple of (mean, variance, beta, gamma). - """ - p = self.params - if paddings is None: - paddings = self._GetDefaultPaddings(inputs) - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match([tf.shape(paddings)[-1]], [1]), - ], inputs) - with tf.name_scope(p.name): - if self.do_eval or p.freeze_bn_stats: - # The mean and variance used for normalization. - norm_mean, norm_variance = (self.vars.moving_mean, - self.vars.moving_variance) - else: - rank = tf.rank(paddings) - reduce_over_dims = tf.range(0, rank - 1) - mean, variance = ComputeMomentsWithPadding( - inputs, paddings, reduce_over_dims, None, - p.enable_cross_replica_sum_on_tpu) - - py_utils.UpdateBatchNormVars(self.vars.moving_mean, mean, self._decay) - py_utils.UpdateBatchNormVars(self.vars.moving_variance, variance, - self._decay) - # Add some summaries for visualization. - summary_utils.histogram('%s_mean' % p.name, tf.cast(mean, tf.float32)) - summary_utils.histogram('%s_variance' % p.name, - tf.cast(variance, tf.float32)) - summary_utils.histogram('%s_moving_mean' % p.name, - tf.cast(self.vars.moving_mean, tf.float32)) - summary_utils.histogram('%s_moving_variance' % p.name, - tf.cast(self.vars.moving_variance, tf.float32)) - summary_utils.histogram( - '%s_mean_diff' % p.name, - tf.cast( - tf.cast(mean, self.vars.moving_mean.dtype.base_dtype) - - self.vars.moving_mean, tf.float32)) - summary_utils.histogram( - '%s_variance_diff' % p.name, - tf.cast( - tf.cast(variance, self.vars.moving_variance.dtype.base_dtype) - - self.vars.moving_variance, tf.float32)) - if p.use_moving_avg_in_training: - # Use the global statistics for normalization. - # Control dependencies on mean and variance make sure - # moving_mean and variance will be updated for every training step. - norm_mean = py_utils.with_dependencies([mean], self.vars.moving_mean) - norm_variance = py_utils.with_dependencies([variance], - self.vars.moving_variance) - else: - # Use the batch statistics for normalization. - norm_mean = mean - norm_variance = variance - - norm_mean = py_utils.CheckNumerics( - norm_mean, 'mean of %s failed numeric check' % p.name) - norm_variance = py_utils.CheckNumerics( - norm_variance, 'variance of %s failed numeric check' % p.name) - - beta, gamma = self._GetBetaGamma(theta, inputs, **kwargs) - return norm_mean, norm_variance, beta, gamma - - def _ComputeBN(self, inputs, paddings, gamma, beta, norm_mean, norm_variance): - p = self.params - with tf.control_dependencies([ - py_utils.assert_greater_equal(norm_variance, - tf.zeros_like(norm_variance)), - py_utils.assert_shape_match([tf.shape(inputs)[-1]], - tf.shape(norm_mean)), - py_utils.assert_shape_match([tf.shape(inputs)[-1]], - tf.shape(norm_variance)), - ]): - if p.use_fused_batch_norm_for_eval and (self.do_eval or - p.freeze_bn_stats): - bn_output, _, _ = nn.fused_batch_norm( - inputs, - gamma, - beta, - norm_mean, - norm_variance, - self._epsilon, - is_training=False) - else: - bn_output = tf.nn.batch_normalization(inputs, norm_mean, norm_variance, - beta, gamma, self._epsilon) - if p.set_padded_output_to_zero: - bn_output *= 1.0 - paddings - return bn_output - - def _MaybeExpandPaddings(self, inputs, paddings): - # rank difference is at most one. - rank_diff = tf.rank(inputs) - tf.rank(paddings) - paddings = py_utils.with_dependencies([ - py_utils.assert_less_equal(rank_diff, 1), - py_utils.assert_greater_equal(rank_diff, 0) - ], paddings) - - # Pads [1] to the end of paddings. - paddings = tf.reshape( - paddings, - tf.concat( - [tf.shape(paddings), tf.tile([1], [rank_diff])], axis=0)) - return paddings - - def FProp(self, theta, inputs, paddings=None): - """Apply batch normalization. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. Shaped [..., dim]. - paddings: The paddings tensor. Shaped [..., 1] or [...], the rank is - either the same as inputs or tf.rank(inputs) - 1. - - Returns: - Output after applying batch normalization, with the same shape as - 'inputs'. - """ - p = self.params - if paddings is None: - paddings = self._GetDefaultPaddings(inputs) - - # shape [..., 1] - paddings = self._MaybeExpandPaddings(inputs, paddings) - - with tf.name_scope(p.name): - norm_mean, norm_variance, beta, gamma = self.ComputeAndUpdateMoments( - theta, inputs, paddings) - - return self._ComputeBN(inputs, paddings, gamma, beta, norm_mean, - norm_variance) - - @classmethod - def FPropMeta(cls, p, inputs, padding=None): - py_utils.CheckShapes((inputs,)) - return py_utils.NestedMap( - flops=inputs.num_elements() * _BN_FLOPS_PER_ELEMENT, - out_shapes=(inputs,)) - - -class CategoricalBN(BatchNormLayer): - """Implements a categorical BN which is akin to ... - - https://arxiv.org/pdf/1809.11096.pdf - - Specifically, the moving stats are category-agnostic, while {beta, gamma} are - category-aware. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('class_emb_dim', None, 'Dim of input class embedding.') - - p.use_moving_avg_in_training = False - p.use_fused_batch_norm_for_eval = False - p.add_stats_to_moving_average_variables = True - return p - - def __init__(self, params): - assert params.name - assert not params.use_moving_avg_in_training - assert not params.use_fused_batch_norm_for_eval - assert params.add_stats_to_moving_average_variables - super().__init__(params) - - def _GetWeightShape(self): - return [self.params.class_emb_dim, self.params.dim] - - def _GetBetaGamma(self, theta, inputs, **kwargs): - assert 'class_emb' in kwargs - class_emb = kwargs['class_emb'] - - # class_emb is a one-hot vector of shape [batch, class_emb_dim=num_classes]. - class_ids = tf.math.argmax(class_emb, axis=-1, output_type=tf.int32) - # [batch, dim] - # Not using matmul/einsum to avoid potential precision problem on TPU with - # sparse inputs. - beta = tf.gather(theta.beta, class_ids) - gamma = tf.gather(theta.gamma, class_ids) - - # Extend to [batch, 1, ... 1, dim] - batch = py_utils.GetShape(inputs)[0] - to_shape = tf.concat( - [[batch], - tf.ones([py_utils.GetRank(inputs) - 2], tf.int32), [self.params.dim]], - axis=0) - beta = tf.reshape(beta, to_shape) - gamma = tf.reshape(gamma, to_shape) - return beta, gamma - - def FProp(self, theta, inputs, paddings, class_emb): - """Apply batch normalization. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. Shaped [batch, ..., dim]. - paddings: The paddings tensor. Shaped [batch, ..., 1], with the same rank - as the input tensor. - class_emb: The conditioning inputs, Shaped [batch, emb_dim]. - - Returns: - Output after applying batch normalization, with the same shape as - 'inputs'. - """ - p = self.params - batch = py_utils.GetShape(inputs)[0] - class_emb = py_utils.HasShape(class_emb, [batch, p.class_emb_dim]) - if not py_utils.use_tpu(): - class_emb = py_utils.with_dependencies([ - py_utils.assert_less_equal( - tf.cast(class_emb, tf.int32), 1, name='one_hot_assert1'), - py_utils.assert_greater_equal( - tf.cast(class_emb, tf.int32), 0, name='one_hot_assert2'), - py_utils.assert_equal( - tf.ones([batch], tf.int32), - tf.cast(tf.reduce_sum(class_emb, -1), tf.int32), - name='one_hot_assert3'), - ], class_emb) - - with tf.name_scope(p.name): - norm_mean, norm_variance, beta, gamma = self.ComputeAndUpdateMoments( - theta, inputs, paddings=paddings, class_emb=class_emb) - return self._ComputeBN(inputs, paddings, gamma, beta, norm_mean, - norm_variance) - - -class BatchNormLayerNoPadding(base_layer.BaseLayer): - """Batchnorm layer without padding.""" - - @classmethod - def Params(cls): - """Parameters for BatchNormLayerNoPadding.""" - p = super().Params() - p.Define('dim', 0, 'Depth of the input/output.') - p.Define( - 'decay', 0.997, - 'Decay in updating the mean and variance moving average used in' - ' batch normalization.') - p.Define('epsilon', 0.001, - 'Small float added to variance to avoid dividing by zero.') - p.Define( - 'bn_group_size', 1, - 'The number of shards participating in normalization when distributed' - ' batchnorm is used. Only used for TPU.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name, 'Name of BatchNormLayerNoPadding is not set.' - p.fprop_dtype = None - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - # Skip L-P regularization for these variables. - collections = [ - self.__class__.__name__ + '_vars', py_utils.SKIP_LP_REGULARIZATION - ] - pc = py_utils.WeightParams( - shape=[p.dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=collections) - - self.CreateVariable('beta', pc) - # Note, The real gamma to use is 1 + gamma. - self.CreateVariable('gamma', pc, lambda x: 1.0 + x) - - moving_collections = [ - 'moving_vars', tf.GraphKeys.MOVING_AVERAGE_VARIABLES, - self.__class__.__name__ + '_vars' - ] - mva = py_utils.WeightParams( - shape=[p.dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=moving_collections) - # Two statistics computed from sufficient stats. - self.CreateVariable('moving_mean', mva, trainable=False) - mvv = py_utils.WeightParams( - shape=[p.dim], - init=py_utils.WeightInit.Constant(1.0), - dtype=p.dtype, - collections=moving_collections) - self.CreateVariable('moving_variance', mvv, trainable=False) - - # Accumulate bn sufficient stats over micro-batches. - dim = self.vars.beta.shape[0] - self.RegisterAccumulator('counts', AddingAccumulator([], p.dtype)) - self.RegisterAccumulator('mean_ss', AddingAccumulator([dim], p.dtype)) - self.RegisterAccumulator('variance_ss', AddingAccumulator([dim], p.dtype)) - - def PostTrainingStepUpdate(self, global_step): - """Updates moving_mean, moving_variance after each training step.""" - p = self.params - # Get sufficient stats that accumulates over microbatches. - counts = self.accumulators.counts.GetValue() - mean_ss = self.accumulators.mean_ss.GetValue() - variance_ss = self.accumulators.variance_ss.GetValue() - # Compute batch mean and batch variance from sufficient stats - mean, variance = tf.nn.normalize_moments(counts, mean_ss, variance_ss, None) - decay = tf.convert_to_tensor(1.0 - p.decay, p.dtype) - # Update moving_mean, moving_variance from batch mean and batch variance. - with tf.name_scope(p.name) as scope: - with tf.ops.colocate_with(self.vars.moving_mean): - mean_update = tf.assign_sub( - self.vars.moving_mean, - tf.where( - tf.greater(counts, 0.5), - (self.vars.moving_mean - tf.cast(mean, p.dtype)) * decay, - tf.zeros_like(self.vars.moving_mean)), - name='moving_mean_update') - with tf.ops.colocate_with(self.vars.moving_variance): - var_update = tf.assign_sub( - self.vars.moving_variance, - tf.where( - tf.greater(counts, 0.5), - (self.vars.moving_variance - tf.cast(variance, p.dtype)) * - decay, tf.zeros_like(self.vars.moving_variance)), - name='moving_variance_update') - py_utils.CheckNumerics( - self.vars.moving_mean, - 'moving mean of {} failed numeric check'.format(scope)) - py_utils.CheckNumerics( - self.vars.moving_variance, - 'moving variance of {} failed numeric check'.format(scope)) - self.accumulators.counts.Reset() - self.accumulators.mean_ss.Reset() - self.accumulators.variance_ss.Reset() - return tf.group(mean_update, var_update) - - def _Moments(self, inputs, group_size): - """Computes mean and variance over N,H,W dimensions in inputs.""" - counts, mean_ss, variance_ss, _, = tf.nn.sufficient_statistics( - inputs, axes=[0, 1, 2], keepdims=False) - self.accumulators.counts.Update(counts) - self.accumulators.mean_ss.Update(mean_ss) - self.accumulators.variance_ss.Update(variance_ss) - # Distributed batch norm that computes sufficient statistics from group_size - # replicas. This is useful when batch_size_per_replica is too small to - # compute reliable sufficient statistics. - if py_utils.use_tpu() and group_size > 1: - group_assignment = None - num_shards = tpu_function.get_tpu_context().number_of_shards - if num_shards is not None: - if num_shards < group_size: - raise ValueError('TPU shards={} less than bn_gropu_size={}.'.format( - num_shards, group_size)) - if num_shards % group_size: - raise ValueError( - 'TPU shards={} not divisible by bn_group_size={}.'.format( - num_shards, group_size)) - num_groups = num_shards // group_size - group_assignment = [] - for g in range(num_groups): - replica_ids = [g * group_size + i for i in range(group_size)] - group_assignment.append(replica_ids) - counts *= group_size - mean_ss = tf.tpu.cross_replica_sum(mean_ss, group_assignment) - variance_ss = tf.tpu.cross_replica_sum(variance_ss, group_assignment) - # At each micro-step, batch_mean and batch_variance are computed - # to normalize inputs. But they are not used to update moving_mean and - # moving_variance variables until the last micro batch. - mean, variance = tf.nn.normalize_moments(counts, mean_ss, variance_ss, None) - return mean, variance - - def FProp(self, theta, inputs): - """Applies batch normalization. - - Using the implementation in github.com/ - tensorflow/tpu/blob/master/models/official/amoeba_net/network_utils.py#L550 - - Args: - theta: A nested map object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. Shaped [..., dim]. - - Returns: - Output after applying batch normalization, with the same shape as - 'inputs'. - """ - p = self.params - inputs_dtype = inputs.dtype - inputs = tf.cast(inputs, p.dtype) - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match([tf.shape(inputs)[-1]], tf.shape( - theta.beta)) - ], inputs) - with tf.name_scope(p.name) as scope: - if self.do_eval: - outputs = tf.nn.batch_normalization(inputs, theta.moving_mean, - theta.moving_variance, - theta.beta, theta.gamma, p.epsilon) - else: - mean, variance = self._Moments(inputs, p.bn_group_size) - mean = py_utils.CheckNumerics( - mean, 'mean of {} failed numeric check'.format(scope)) - variance = py_utils.CheckNumerics( - variance, 'variance of {} failed numeric check'.format(scope)) - outputs = tf.nn.batch_normalization(inputs, mean, variance, theta.beta, - theta.gamma, p.epsilon) - outputs.set_shape(inputs.get_shape()) - return tf.cast(outputs, inputs_dtype) - - @classmethod - def FPropMeta(cls, p, inputs): - """Returns metadata about the `FProp` computation for this layer.""" - py_utils.CheckShapes((inputs,)) - return py_utils.NestedMap( - flops=inputs.num_elements() * _BN_FLOPS_PER_ELEMENT, - out_shapes=(inputs,)) - - -class GroupNormLayer(base_layer.BaseLayer): - """Group normalization layer(https://arxiv.org/abs/1803.08494).""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('dim', 0, 'Depth of the input/output.') - p.Define('num_groups', 32, 'Number of groups for GroupNorm.') - p.Define('min_group_size', 1, 'Minimum group size for GroupNorm') - p.Define('cumulative', False, 'If true, only normalize by current and ' - 'previous time steps.') - p.Define('input_rank', 4, 'Rank of input. Only 3(BTD) and 4(NHWC) are ' - 'supported.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.num_groups > 0 - assert p.min_group_size > 0 - if p.dim >= p.num_groups: - assert p.dim % p.num_groups == 0, ('p.dim({0}) is not dividable by ' - 'p.num_groups({1})').format( - p.dim, p.num_groups) - self._epsilon = 0.001 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - assert p.input_rank == 3 or p.input_rank == 4 - - collections = [ - self.__class__.__name__ + '_vars', py_utils.SKIP_LP_REGULARIZATION - ] - - shape = [1, 1, 1, p.dim] if p.input_rank == 4 else [1, 1, p.dim] - pc = py_utils.WeightParams( - shape=shape, - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=collections) - - self.CreateVariable('beta', pc) - # Note, The real gamma to use is 1 + gamma. - self.CreateVariable('gamma', pc, lambda x: 1.0 + x) - - def FProp(self, theta, inputs, paddings=None): - """Apply group normalization. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - inputs: The inputs tensor with shape [batch_size, height, width, channel]. - paddings: The paddings tensor with shape [batch_size, height]. Intended to - be used for sequence processing where `height` is `time`. - - Returns: - A single tensor as the output after applying group normalization, with - the same shape as 'inputs'. Or a output, output_paddings pair if input - paddings is not None. - """ - p = self.params - inputs = py_utils.with_dependencies( - [py_utils.assert_greater_equal(py_utils.GetRank(inputs), p.input_rank)], - inputs) - - min_group_size = min(p.min_group_size, p.dim) - group_size = max(p.dim // p.num_groups, min_group_size) - num_groups = p.dim // group_size - - input_shape = py_utils.GetShape(inputs) - with tf.name_scope(p.name): - x = tf.reshape(inputs, input_shape[:-1] + [num_groups, group_size]) - expanded_rank = p.input_rank + 1 - all_dims = list(range(expanded_rank)) - if paddings is None: - # Skip d0, d[-2] - axes = all_dims[1:-2] + all_dims[-1:] - counts, means_ss, variance_ss, _, = tf.nn.sufficient_statistics( - x, axes=axes, keepdims=True) - norm_mean, norm_variance = tf.nn.normalize_moments( - counts, means_ss, variance_ss, None) - else: - expanded_paddings = tf.reshape( - paddings, input_shape[:2] + [1] * (expanded_rank - 2)) - # skip the batching and group dim - if p.cumulative: - # Skip d0, d1 and d[-2] - reduce_over_dims = all_dims[2:-2] + all_dims[-1:] - norm_mean, norm_variance = ComputeMomentsWithPadding( - x, - expanded_paddings, - reduce_over_dims=reduce_over_dims, - cumulative_axis=1, - keepdims=True) - else: - # Skip d0, d[-2] - reduce_over_dims = all_dims[1:-2] + all_dims[-1:] - norm_mean, norm_variance = ComputeMomentsWithPadding( - x, expanded_paddings, reduce_over_dims, keepdims=True) - - norm_mean = py_utils.CheckNumerics( - norm_mean, 'mean of %s failed numeric check' % p.name) - norm_variance = py_utils.CheckNumerics( - norm_variance, 'variance of %s failed numeric check' % p.name) - - beta = theta.beta - gamma = theta.gamma - n = input_shape[0] - t = input_shape[1] if p.cumulative else 1 - norm_shape = [n, t, 1, num_groups, 1 - ] if p.input_rank == 4 else [n, t, num_groups, 1] - with tf.control_dependencies([ - py_utils.assert_greater_equal(norm_variance, - tf.cast(0., norm_variance.dtype)), - py_utils.assert_shape_match(norm_shape, tf.shape(norm_mean)), - py_utils.assert_shape_match(norm_shape, tf.shape(norm_variance)), - ]): - x = (x - norm_mean) / tf.sqrt(norm_variance + self._epsilon) - x = tf.reshape(x, input_shape) - gn_output = x * gamma + beta - gn_output = tf.reshape(gn_output, input_shape) - if paddings is None: - return gn_output - else: - return gn_output, paddings - - @classmethod - def FPropMeta(cls, p, inputs): - py_utils.CheckShapes((inputs,)) - flops_per_element = 10 # Approximately 10 flops per element. - return py_utils.NestedMap( - flops=inputs.num_elements() * flops_per_element, out_shapes=(inputs,)) diff --git a/lingvo/core/build_data.py b/lingvo/core/build_data.py deleted file mode 100644 index 138ae4c1..00000000 --- a/lingvo/core/build_data.py +++ /dev/null @@ -1,24 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Build data.""" - -from lingvo.core import hyperparams - - -def BuildData(): - """Returns a hyperparam recording build information of this py binary.""" - p = hyperparams.Params() - return p diff --git a/lingvo/core/builder.py b/lingvo/core/builder.py deleted file mode 100644 index 605bc9d6..00000000 --- a/lingvo/core/builder.py +++ /dev/null @@ -1,314 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""A library to build composite layers. - -WARNING: - The builder pattern is still experimental and we need to gain experience - on when to use and when not to use. - Please discuss w/ teammates before using it to build complicated - layers. -""" - -import functools -from lingvo import compat as tf -from lingvo.core import builder_layers -from lingvo.core import hyperparams -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import tshape - - -class Base: - """Model builder with commonly used layers. - - A method in a builder class constructs a layer param. FProp of a layer - constructed by a builder takes a tuple of tf.Tensor (one or more) and returns - a tuple of tf.Tensor (one or more). Even though certain layers support FProp - argument being None (e.g., Conv2DLayer), builder should not depend on such a - support. - - The constructed layer is often a composition of multiple sub-layers connected - in certain patterns. We expect to have a few methods to facilitate building - these patterns. For example, _Seq() helps to build a sequential layer that - calls its sub-layer one after another. - - TODO(zhifengc): Adds a more concrete example. - """ - - @classmethod - def Params(cls): - """The params of this layer.""" - p = hyperparams.InstantiableParams(cls) - p.Define('deterministic_dropout', False, - 'Used deterministic dropout or not.') - p.Define( - 'fprop_dtype', None, - 'Activations datatype to use. To enable bfloat16 activations for ' - 'layers built using model builder, set fprop_dtype to ' - 'tf.bfloat16, which will be propagated to layers that support ' - 'bfloat16 activations. Default is None, which will use float32 ' - 'activations.') - return p - - @property - def params(self): - """Returns the params upon which this layer is built.""" - return self._params - - def __init__(self, params): - # Sub-classes should put some options common to many layers in __init__. - self._params = params.Copy() - - ###################################################################### - # Layers to compose multiple layers. - # - # Sub-classes are discouraged to override these composition method. - ###################################################################### - def _Rep(self, name, repeat, *subs): - r"""Connects sub-layers sequentially and repeat multiple times. - - E.g., _Rep('foo', 2, sa, sb, sc) constructs a layer with 6 layers - sequentially connected: [sa1, sb1, sc1, sa2, sb2, sc2]. sa1 and sa2 have - the same structure as the given sa, but sa1 and sa2 do not share the same - weight. - - Args: - name: The layer name. - repeat: Repeat \*subs this many times in the compose layer. - *subs: A list of sub-layers. - - Returns: - The param for the composed layer. - """ - iterations = [] - for i in range(repeat): - iterations.append(self._Seq('iter_%03d' % i, *[p.Copy() for p in subs])) - return self._Seq(name, *iterations) - - def _Seq(self, name, *subs): - """Connects sub-layers sequentially.""" - return builder_layers.SequentialLayer.Params().Set( - name=name, sub=list(subs)) - - def _Graph(self, name, input_endpoints, output_endpoints, - *signature_sub_param_list): - """Connects sub-layers into a data flow graph.""" - return builder_layers.GraphLayer.Params().Set( - name=name, - input_endpoints=input_endpoints, - output_endpoints=output_endpoints, - sub=list(signature_sub_param_list)) - - def _Id(self, name): - """Identity. (t_1, ..., t_n) -> (t1, ..., t_n).""" - return self._Seq(name) - - def _Arg(self, name, index): - """Picks index-th element. (t_1, ..., t_n) -> (t_{index},).""" - return builder_layers.ArgIndexLayer.Params().Set(name=name, idx=[index]) - - def _Par(self, name, *subs): - """y = (f1, f2, ..., fn)(x). - - We feed the input tuple to all sub-layers and concatenates their output - tuples into one tuple. - - Args: - name: The layer name. - *subs: A list of sub-layers. - - Returns: - The param for the composed layer. - """ - - def ConcatTuples(tuples): - # tuples is a list of tuples. - return tuple(functools.reduce(lambda x, y: x + list(y), tuples, [])) - - def ConcatMeta(tuples): - return py_utils.NestedMap( - flops=0, - out_shapes=tuple( - functools.reduce(lambda x, y: x + list(y), tuples, []))) - - return builder_layers.ParallelLayer.Params().Set( - name=name, sub=list(subs), merge=ConcatTuples, merge_meta=ConcatMeta) - - def _Fn(self, name, fn, fn_out=None, fn_flops=None): - """y = fn(x). - - Applies a fn: tuple(Tensor) -> a single Tensor or tuple(Tensor) to the input - tuple. Typically, fn is a very simple python function. If fn is rather - complicated, we advice to implement the logic as a sub-class of BaseLayer. - - Args: - name: The layer name. - fn: A lambda tuple(Tensor) -> tuple(Tensor). - fn_out: A lambda tuple(tshape.Shape) -> output tuple(tshape.Shape) - fn_flops: A lambda tuple(tshape.Shape) -> estimated flops of fn. - If None, we assume flops == sum of elements in the inputs. - - Returns: - The param for the composed layer. - """ - - def FnMeta(*shapes): - """A lambda tuple(tshape.Shape) -> NestedMap{flops, out_shapes}.""" - if fn_out: - out_shapes = fn_out(*shapes) - if isinstance(out_shapes, tshape.Shape): - out_shapes = (out_shapes,) - else: - out_shapes = shapes - if fn_flops: - flops = fn_flops(*shapes) - else: - flops = sum([s.size for s in shapes]) - return py_utils.NestedMap(flops=flops, out_shapes=out_shapes) - - return builder_layers.FnLayer.Params().Set(name=name, fn=fn, fn_meta=FnMeta) - - def _Save(self, name): - """Returns a layer from which the activation and gradient can be accessed.""" - return layers.FetchLayer.Params().Set(name=name) - - def _AddFetches(self, name, body, fetches): - """Fetches saved activations in the body sub-layer. - - E.g.: - _AddFetches('foo', _Seq( 'stack', _Layer('layer1', ...), - _Save('layer1_out', ...), _Layer('layer2', ...), _Save('layer2_out', ...), - _Output('output', ...)), ['layer1_out', 'layer2_out']) - - The layer returns the stack's final output together with intermediate - activations from layer1_out and layer2_out. - - Args: - name: This layer's name. - body: The sub-layer. - fetches: A list of fetch names inside the sub-layer body. - - Returns: - A layer whose outputs correspond to the activations of fetch points - in the sub-layer body. [input1, input2, ..., inputN, fetch1, ..., fetchM]. - """ - return builder_layers.BranchLayer.Params().Set( - name=name, body=body, fetches=fetches) - - def _Rematerialize(self, name, body): - """Forces rematerialization on FProp of the body layer.""" - return builder_layers.RematerializationLayer.Params().Set( - name=name, body=body) - - def _BatchParallel(self, name, sub): - """Splits the batch and compute the forward pass on multiple devices. - - Args: - name: This layer's name. - sub: The sub-layer. - - Returns: - A BatchParallel layer which splits the batch and computes the forward pass - on multiple devices. - """ - return builder_layers.BatchParallelLayer.Params().Set(name=name, sub=sub) - - def _PrintShape(self, name): - """Print FProp input shape information.""" - return builder_layers.PrintShapeLayer.Params().Set(name=name) - - ########################################################################### - # Basic nn layers. - # - # The following method returns a layer param, whose FProp takes a single - # Tensor and returns a single Tensor. - # - # These methods are designed to have minimal knobs. Sub-classes which needs to - # be flexible can override these methods with different options. E.g., a - # sub-class builder can override _BN() to tune the decay option. - ########################################################################### - def _BN(self, name, dims): - """Batch norm.""" - return layers.BatchNormLayer.Params().Set(name=name, dim=dims, decay=0.99) - - def _LN(self, name, dims, use_fused_layernorm=False): - """Layer norm.""" - return layers.LayerNorm.Params().Set( - name=name, - input_dim=dims, - use_fused_layernorm=use_fused_layernorm, - fprop_dtype=self.params.fprop_dtype) - - def _Dropout(self, name, keep_prob, noise_shape_broadcast_dims=None): - """Returns a DropoutLayer Params.""" - if self.params.deterministic_dropout: - return layers.DeterministicDropoutLayer.Params().Set( - name=name, - keep_prob=keep_prob, - noise_shape_broadcast_dims=noise_shape_broadcast_dims) - return layers.DropoutLayer.Params().Set( - name=name, - keep_prob=keep_prob, - noise_shape_broadcast_dims=noise_shape_broadcast_dims, - fprop_dtype=self.params.fprop_dtype) - - def _Linear(self, name, idims, odims): - """Linear layer. y = matmul([..., idims], [idims, odims]).""" - return builder_layers.LinearLayer.Params().Set( - name=name, - input_dims=idims, - output_dims=odims, - fprop_dtype=self.params.fprop_dtype) - - def _Bias(self, name, dims): - """Bias layer. The bias is added to the last dimension of the input.""" - return builder_layers.BiasLayer.Params().Set( - name=name, dims=dims, fprop_dtype=self.params.fprop_dtype) - - def _Activation(self, name, fn=tf.nn.relu): - """Activation layer.""" - return self._Fn(name, fn) - - def _FC(self, name, idims, odims): - """Feed-forward fully connected. y = relu(matmul(x, w) + b).""" - # pyformat: disable - return self._Seq( - name, - self._Linear('linear', idims, odims), - self._Bias('bias', odims), - self._Activation('act')) - - def _MLP(self, name, dims): - """Multiple layers of feed-forward fully connected. - - Args: - name: The layer name. - dims: A list of int. i-th layer has dims[i] as its input dimension, and - dims[i+1] as its output dimensions. - - Returns: - The param for the composed layer. - """ - l = [] - for n, (i, o) in enumerate(zip(dims[:-1], dims[1:])): - l += [self._FC('l%03d' % n, i, o)] - return self._Seq(name, *l) - - def _Conv2D(self, name, filter_shape, filter_stride): - """Conv2D layer.""" - return layers.Conv2DLayerNoPadding.Params().Set( - name=name, filter_shape=filter_shape, filter_stride=filter_stride, - fprop_dtype=self.params.fprop_dtype) diff --git a/lingvo/core/builder_layers.py b/lingvo/core/builder_layers.py deleted file mode 100644 index 7dbbb123..00000000 --- a/lingvo/core/builder_layers.py +++ /dev/null @@ -1,1259 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Abstractions for composing layers.""" - -import re -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import computation_cost -from lingvo.core import py_utils -from lingvo.core import recurrent -from lingvo.core import summary_utils -from lingvo.core import symbolic -from lingvo.core import tshape - - -class FirstNLayer(base_layer.BaseLayer): - """Returns the first n args.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('n', 0, 'The number of args to return.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.n > 0 - - def FProp(self, theta, *args): - """Return the first n args.""" - p = self.params - assert len(args) >= p.n - return tuple(args[:p.n]) if p.n > 1 else args[0] - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - if p.n > 1: - out_shapes = args[:p.n] - else: - out_shapes = (args[0],) - return py_utils.NestedMap(flops=0, out_shapes=out_shapes) - - -class ArgIndexLayer(base_layer.BaseLayer): - """Select args with a list of indices.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('idx', [], 'The indices of args to return.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.idx - - def FProp(self, theta, *args): - """Return the indexed args.""" - p = self.params - assert p.idx - for i in p.idx: - assert 0 <= i <= len(args) - r = [args[i] for i in p.idx] - return tuple(r) if len(r) > 1 else r[0] - - -def _ToTuple(x): - return x if isinstance(x, tuple) else (x,) - - -def _MaybeStackExtraTheta(theta, all_vars, repeat): - var_set = set([key for key, _ in all_vars.FlattenItems()]) - values = [] - for key, value in theta.FlattenItems(): - if key not in var_set and value is not None: - # Replicate non-variable theta by p.repeat times. - value = tf.stack([value] * repeat) - values.append(value) - return theta.Pack(values) - - -class RepeatLayer(base_layer.BaseLayer): - """A layer which repeats itself sequentially using lingvo Recurrent.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('body', None, 'The param for the main network layer.') - p.Define('repeat', 1, - 'Repeat layers specified in \'body\' this many times.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.repeat > 0 - self.CreateChild('body', p.body) - - def _CreateChildrenVariables(self): - with tf.variable_scope(self.params.name): - with py_utils.VariableShapePrefixContext(self.params.repeat): - self.body.InstantiateVariables() - super()._CreateChildrenVariables() - - def FProp(self, theta, *args): - p = self.params - # Collects all variable key and values into sets. - theta_stack = _MaybeStackExtraTheta(theta.body, self.body.vars, p.repeat) - - def _ArgsToState(arg_list): - """Returns a NestedMap from a list of FProp args.""" - state = py_utils.NestedMap() - # Maintains a mapping from arg_idx to tensor. states cannot contains - # None tensors. - for idx in range(len(args)): - if arg_list[idx] is not None: - state['_s{}'.format(idx)] = arg_list[idx] - return state - - def _StateToArgs(state): - """Returns a list of FProp args from a NestedMap.""" - arg_list = [] - for idx in range(len(args)): - attr = '_s{}'.format(idx) - arg_list.append(state[attr] if attr in state else None) - if arg_list[-1] is not None: - arg_list[-1].set_shape(args[idx].shape) - return arg_list - - def _CellFn(unused_theta, state0, theta_i): - """Recurrent cell function wrapper of body.FProp.""" - # Retrieves fprop arguments from state and sets shapes. - frop_inputs = _StateToArgs(state0) - - # Sets shapes for theta_i as well. - for dst, src in zip(theta_i.Flatten(), theta_stack.Flatten()): - if src is not None: - dst.set_shape(tf.TensorShape(src.shape.as_list()[1:])) - - # Runs the actual body.FProp - frop_outputs = self.body.FProp(theta_i, *frop_inputs) - frop_outputs = _ToTuple(frop_outputs) - assert len(frop_outputs) == len(frop_inputs) - - # Passes fprop outputs to the next layer through state. - state1 = _ArgsToState(frop_outputs) - return state1, py_utils.NestedMap() - - with tf.name_scope(p.name): - # Add FProp arg list to state0. - state0 = _ArgsToState(args) - # Runs body.FProp k times using Recurrent where k = dim 0 of var_nmap. - _, state1 = recurrent.Recurrent( - theta=py_utils.NestedMap(), - state0=state0, - inputs=theta_stack, # Pass cell_fn theta through inputs. - cell_fn=_CellFn) - - # Retrieves fprop outputs from state1 and sets shapes. - output_tensors = _StateToArgs(state1) - return output_tensors[0] if len(args) == 1 else tuple(output_tensors) - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - meta = p.body.cls.FPropMeta(p.body, *args) - py_utils.CheckShapes(meta.out_shapes) - total = meta.flops * p.repeat - return py_utils.NestedMap(flops=total, out_shapes=args) - - -class SoftCondLayer(base_layer.BaseLayer): - r"""A wrapper layer implements soft conditional computation. - - This layer computes - - output = p.body.FProp( \sum_i w_i theta_i, \*inputs) - - where the theta passed to p.body is the weighted average over p.num_experts - copies of theta.body, and w_i is the example-dependent coefficient for the - i-th expert (theta_i). - - Reference: - Soft Conditional Computation, B. Yang, G. Bender, Q.V. Le, J. Ngiam - https://arxiv.org/abs/1904.04971 - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_tasks', None, 'The Params for the main network layer.') - p.Define('body', None, 'The Params for the main network layer.') - p.Define('num_experts', None, 'Number of experts.') - p.Define( - 'cond_dim', None, - 'This layer maintains a weight matrix of shape [cond_dim, num_experts] ' - 'to map from inputs to the expert dimension.') - p.Define('nonzeros_mean', False, 'Whether to only use nonzero values for ' - 'calculating the mean.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.num_experts - assert p.cond_dim - - self.CreateChild('body', p.body) - - def _CreateChildrenVariables(self): - with tf.variable_scope(self.params.name): - # Prepends p.num_experts to the tensor shape of every variable created - # by p.body. - with py_utils.VariableShapePrefixContext(self.params.num_experts): - self.body.InstantiateVariables() - super()._CreateChildrenVariables() - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - # Create Variables for task weight mapping. - collections = [ - self.__class__.__name__ + '_vars', - ] - w_p = py_utils.WeightParams( - shape=[p.cond_dim, p.num_experts], - init=p.params_init, # TODO(huangyp): try zero init instead. - dtype=p.dtype, - collections=collections) - self.CreateVariable('w', w_p) - - def _GetExpertDist(self, theta, inputs, *args): - """Get the task id from inputs tensors.""" - # TODO(huangyp): support the more general case when batch size is not 1. - # Input shape can be either [batch, length, dim] or [length, batch, dim] - reshaped_inputs = tf.reshape(inputs, [-1, self.params.cond_dim]) - if self.params.nonzeros_mean: - per_example_emb = tf.reduce_sum(reshaped_inputs, 0) - nonzeros = tf.cast( - tf.math.count_nonzero(reshaped_inputs, 0), dtype=tf.float32) - per_example_emb /= (nonzeros + 1e-10) - else: - per_example_emb = tf.reduce_mean(reshaped_inputs, 0) - expert_dist = tf.nn.sigmoid(tf.einsum('i,ij->j', per_example_emb, theta.w)) - return expert_dist - - def FProp(self, theta, inputs, *args): - p = self.params - with tf.name_scope(p.name) as scope: - expert_dist = self._GetExpertDist(theta, inputs, *args) - if not self.do_eval: - summary_utils.histogram('soft_cond_{}'.format(scope), expert_dist) - - # Excludes non-variable extra_theta like global_step. - var_set = set([key for key, _ in self.body.vars.FlattenItems()]) - values = [] - for key, value in theta.body.FlattenItems(): - if key in var_set and value is not None: - # Weighted average for all variables created in the body layer. - value = tf.einsum('i,i...->...', expert_dist, value) - values.append(value) - weighted_theta = theta.body.Pack(values) - return self.body.FProp(weighted_theta, inputs, *args) - - -class ParallelRepeatLayer(RepeatLayer): - """A layer that connects identical sublayers in parallel. - - This layer consists of p.repeat copies of sub-layers, each of which - shares the same structure but with different weights. Conceptually it - computes: - - output= [f(theta[i], input[i]) for i in range(p.repeat)] - - where f is the fprop function of the sublayer, theta[i] and input[i] are - the weights and inputs of the i-th sublayer. - """ - - def _InferOutShapes(self, args): - input_shapes = [ - None if arg is None else tshape.Shape(arg.get_shape().as_list()[1:]) - for arg in args - ] - out_shapes = self.body.FPropMeta(self.body.params, *input_shapes).out_shapes - return [None if s is None else s.ToTensorShape() for s in out_shapes] - - def FProp(self, theta, *args): - """Runs p.repeat copies of self.body.FProp independently. - - Args: - theta: Layer model parameters. The shape of each variable in theta is - always [p.repeat, ...]. And the i-th slice theta[i] becomes theta of the - i-th copy of self.body. - *args: Input arguments. The shape of each tensor in args is always - [p.repeat, ....]. And the list [arg[i] for arg in args] becomes inputs - to the i-th copy of self.body.FProp. - - Returns: - The accumulated output_tensors. Each tensor t in the return has the shape - [p.repeat, ....] and the tuple (t[i] for i in output_tensors) is the - return tuple of the i-th self.body.FProp. - """ - p = self.params - for arg in args: - if arg is not None: - arg = py_utils.HasShape(arg, [p.repeat], ndims=1) - - theta_stack = _MaybeStackExtraTheta(theta.body, self.body.vars, p.repeat) - inputs = py_utils.NestedMap(theta=theta_stack, args=list(args)) - # Infer out_shapes from FPropMeta. - out_shapes = self._InferOutShapes(args) - - def _CellFn(unused_theta, unused_state0, inputs): - """Recurrent cell function wrapper of body.FProp.""" - # Sets shapes for both theta and inputs to self.body.FProp. - for dst, src in zip(inputs.args + inputs.theta.Flatten(), - list(args) + theta_stack.Flatten()): - if src is not None: - dst.set_shape(tf.TensorShape(src.shape.as_list()[1:])) - - # Runs the actual body.FProp - fprop_outputs = self.body.FProp(inputs.theta, *inputs.args) - fprop_outputs = _ToTuple(fprop_outputs) - assert len(fprop_outputs) == len(out_shapes) - # Passes fprop outputs to the next layer through state. - state1 = py_utils.NestedMap(outputs=list(fprop_outputs)) - return state1, py_utils.NestedMap() - - with tf.name_scope(p.name): - # Initiate state0 with inferred output shapes. - state0 = py_utils.NestedMap( - outputs=[tf.zeros(shape, args[0].dtype) for shape in out_shapes]) - # Runs body.FProp p.repeat times using Recurrent. - acc_states, _ = recurrent.Recurrent( - theta=py_utils.NestedMap(), - state0=state0, - inputs=inputs, - cell_fn=_CellFn) - - # Retrieves fprop outputs from state1 and sets shapes. - output_tensors = tuple(acc_states.outputs) - for out_idx in range(len(output_tensors)): - output_tensors[out_idx].set_shape( - tf.TensorShape([p.repeat] + out_shapes[out_idx].as_list())) - - return output_tensors[0] if len(args) == 1 else tuple(output_tensors) - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - input_shapes = [ - None if arg is None else tshape.Shape(arg.get_shape().as_list()[1:]) - for arg in args - ] - meta = p.body.cls.FPropMeta(p.body, *input_shapes) - py_utils.CheckShapes(meta.out_shapes) - total = meta.flops * p.repeat - out_shapes = [ - None if s is None else tshape.Shape([p.repeat] + s[:]) - for s in meta.out_shapes - ] - return py_utils.NestedMap(flops=total, out_shapes=tuple(out_shapes)) - - -class SequentialLayer(base_layer.BaseLayer): - """A layer which connects a few layers in a sequence.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('sub', [], 'A list of layers\' params.') - p.Define('repeat', 1, 'Repeat layers specified in \'sub\' ' - 'this many times.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - if p.repeat <= 1: - self._seq = [] - for sub in p.sub: - self.CreateChild(sub.name, sub) - self._seq.append((sub.name, self.children[sub.name])) - else: - # We create 'repeat' number of sub layers. Each sub layer is a - # sequential layer specified by 'sub'. This allows us to name each - # repetition with a unique name. - children = [] - for i in range(p.repeat): - children.append(p.Copy().Set(name='%03d' % i, repeat=1)) - self.CreateChildren('rep', children) - - def FProp(self, theta, *args): - p = self.params - with tf.name_scope(p.name): - tf.logging.vlog(1, 'layer %s', self.params.name) - if p.repeat <= 1: - for (name, ch) in self._seq: - th = theta[name] - args = _ToTuple(args) - tf.logging.vlog(1, 'SequentialLayer: call %s %s %d %s', - ch.params.name, ch, len(args), str(args)) - args = ch.FProp(th, *args) - else: - for (ch, th) in zip(self.rep, theta.rep): - args = _ToTuple(args) - tf.logging.vlog(1, ' call %s %s %d %s', ch.params.name, ch, - len(args), str(args)) - args = ch.FProp(th, *args) - args = _ToTuple(args) - return args[0] if len(args) == 1 else args - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - total = 0 - for _ in range(p.repeat): - for sub in p.sub: - tf.logging.vlog(1, ' seq abs fprop %s %s %d %s', sub.name, sub.cls, - len(args), str(args)) - meta = sub.cls.FPropMeta(sub, *args) - py_utils.CheckShapes(meta.out_shapes) - total += meta.flops - args = meta.out_shapes - return py_utils.NestedMap(flops=total, out_shapes=args) - - -class UnarySequentialLayer(base_layer.BaseLayer): - """A layer which connects a few layers in a sequence. - - Each layer FProp must take a single input arg (besides theta) and its return - value will be used as the input for the next layer or as the final output - if it's the last layer. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('sub', [], 'A list of layers\' params.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - self._seq = [] - for sub in p.sub: - self.CreateChild(sub.name, sub) - self._seq.append((sub.name, self.children[sub.name])) - - def FProp(self, theta, x): - tf.logging.vlog(1, 'layer %s', self.params.name) - with tf.name_scope(self.params.name): - for (name, ch) in self._seq: - th = theta[name] - tf.logging.vlog(1, ' call %s %s %s', ch.params.name, ch, x) - x = ch.FProp(th, x) - return x - - @classmethod - def FPropMeta(cls, p, x): - total = 0 - for sub in p.sub: - tf.logging.vlog(1, ' seq abs fprop %s %s %s', sub.name, sub.cls, x) - meta = sub.cls.FPropMeta(sub, x) - total += meta.flops - x = meta.out_shapes - return py_utils.NestedMap(flops=total, out_shapes=x) - - -class GraphTensors: - """A collection of named tensors (or NestedMaps of tensors).""" - - def __init__(self): - self._named_tensors = py_utils.NestedMap() - - def StoreTensor(self, path, tensor): - """Add tensor 't' to 'named_tensors' at 'path'. - - A path may be a name or a path into a NestedMap. For instance, - StoreTensor('a.b.c', [1]), is equivalent to this: - {'a', {'b': {'c': [1]}}. - - NestedMaps will be created if they do not already exist, or modified if they - do exist. However, tensors cannot be overwritten. - - Args: - path: A path input a NestedMap. - tensor: The item to store (may be a NestedMap or a tensor). - """ - names = path.strip().split('.') - named_tensors = self._named_tensors - while len(names) > 1: - n = names.pop(0) - assert isinstance(named_tensors, py_utils.NestedMap), named_tensors - if n not in named_tensors: - named_tensors[n] = py_utils.NestedMap() - named_tensors = named_tensors[n] - n = names.pop(0) - if n in named_tensors: - raise ValueError('A tensor named "%s" (%s) already exists.' % (n, path)) - named_tensors[n] = tensor - - def GetTensor(self, path): - """Returns the tensor at 'path' in 'named_tensors'. - - Path may be a NestedMap key or a path through a series of NestedMaps. - For instance, a path of 'a.b.c' could be used to retrieve [1] from - this structure: - {'a': {'b': {'c': [1]}}} - - Args: - path: A path through a series of NestedMaps. - """ - names = path.strip().split('.') - named_tensors = self._named_tensors - while names: - assert isinstance(named_tensors, py_utils.NestedMap), named_tensors - n = names.pop(0) - assert n in named_tensors, '%s not found in %s' % (n, named_tensors) - named_tensors = named_tensors[n] - return named_tensors - - -class GraphSignature: - """Represents the input/output signature of a GraphLayer. - - A signature is of the form: - input->output - or, optionally just an input: - input - - The output part may be: - out1 (a tensor name) - out1.a.b (a path into a NestedMap) - out1,myvar,out2.x (or a sequence of these things) - - The input part may be: - in1 (a tensor name) - in1.b (a path into a NestedMap) - [in1,myvar.c] (a list) - (a=in1,b=in2) (a NestedMap, using the syntax of the NestedMap constructor) - in1,[a,b] (or a sequence of these things) - - In BNF:: - - key := [A-Za-z_][A-Za-z0-9_]* - path := key | path '.' key - path_seq := | path | path_seq ',' path - item := path | list | map - list := '[' item_seq ']' - item_seq := | item | item_seq ',' item - map := '(' map_pair_seq ')' - map_pair := key '=' item - map_pair_seq := | map_pair | map_pair_seq ',' map_pair - input := item_seq - output := path_seq - """ - - def __init__(self, signature): - self.signature = signature - self.symbols = set(['[', ']', '=', '(', ')', ',']) - parts = self.signature.split('->') - self._input_signature = parts[0] - self._output_signature = None - if len(parts) > 1: - self._output_signature = parts[1] - - self._tokens = self._TokenizeInputs(self._input_signature) - self._outputs = self._ParseOutputs(self._output_signature) - self._inputs = self._ParseInputs() - - def __str__(self): - return '(%s->%s)' % (self._inputs, self.outputs) - - @property - def inputs(self): - return self._inputs - - @property - def outputs(self): - return self._outputs - - def _ParseOutputs(self, outputs): - """Splits the output spec into comma-delimited parts. - - Args: - outputs: A string containing the output specification, like, 'a,b'. - - Returns: - The parsed representation, e.g. ['a', 'b'] - """ - if outputs is None: - return [] - o_tensors = [x.strip() for x in outputs.split(',')] - for x in o_tensors: - assert x - return o_tensors - - def _TokenizeInputs(self, inputs): - """Splits the input signature into tokens (not data structures). - - Args: - inputs: A string containing the input speficiation, like "[a,b],c" - - Returns: - The tokenized representation, like ['[', 'a', ',', 'b', ']', ',', 'c'] - """ - start = -1 - tokens = [] - for j in range(len(inputs)): - # Each letter can either be a symbol or part of a variable/path. - if inputs[j] in self.symbols: - if start >= 0: - # Output the last variable/path. - tokens.append(inputs[start:j].strip()) - start = -1 - tokens.append(inputs[j]) - elif start < 0: - start = j - if start >= 0: - tokens.append(inputs[start:].strip()) - id_regex = re.compile(r'[_a-zA-Z][_a-zA-Z0-9]*(\.[_a-zA-Z][_a-zA-Z0-9]*)*$') - for token in tokens: - if not (token in self.symbols or id_regex.match(token)): - raise ValueError( - 'token(%s) is not a symbol(%r) or matched by id_regex.' % - (token, self.symbols)) - # Wrapping the tokens in list brackets allows us to parse this using - # _ConsumeList. - return ['['] + tokens + [']'] - - def _ConsumePath(self): - """Return the path found at the current token position and increment. - - Returns: - The path at the current token position. - """ - if self._i >= len(self._tokens): - raise ValueError('Ran out of tokens while looking for a path/key') - if self._tokens[self._i] in self.symbols: - raise ValueError('Found a symbol %s while looking for a path/key' % - (self._tokens[self._i])) - self._i += 1 - return self._tokens[self._i - 1] - - def _ConsumeKey(self): - """Return the key found at the current token position and increment. - - Returns: - The key at the current token position. - """ - token = self._ConsumePath() - assert '.' not in token, token - return token - - def _ConsumeSymbol(self, symbol): - """Verify that the current token is this symbol, and increment. - - Args: - symbol: The symbol that we expect at the current token position. - """ - assert symbol in self.symbols - if self._i >= len(self._tokens): - raise ValueError('Ran out of tokens while looking for a %s' % symbol) - if not self._MaybeConsumeSymbol(symbol): - raise ValueError('Found a symbol %s while looking for %s' % - (self._tokens[self._i], symbol)) - - def _MaybeConsumeSymbol(self, symbol): - """Attempt to consume the given symbol. - - The symbol must be a member of the symbol set: []()=, - - Args: - symbol: The symbol that we expect at the current token position. - - Returns: - True if the symbol was consumed, False otherwise. - """ - assert symbol in self.symbols - if self._i < len(self._tokens) and self._tokens[self._i] == symbol: - self._i += 1 - return True - return False - - def _ConsumeItem(self): - """Return whatever is at the current token position and increment. - - Returns: - A list, NestedMap, or path/key. - """ - if self._i >= len(self._tokens): - raise ValueError( - 'Ran out of tokens while looking for a variable, list or NestedMap.') - if self._tokens[self._i] == '(': - return self._ConsumeMap() - if self._tokens[self._i] == '[': - return self._ConsumeList() - return self._ConsumePath() - - def _ConsumeMap(self): - """Return the NestedMap that starts at the current position, and increment. - - Returns: - The NestedMap that starts at the current token position. - """ - if self._i >= len(self._tokens): - raise ValueError('Ran out of tokens while looking for a NestedMap.') - if self._tokens[self._i] != '(': - raise ValueError('Expected ( at token position %d' % self._i) - self._i += 1 - if self._MaybeConsumeSymbol(')'): - # Empty NestedMaps are allowed. - return py_utils.NestedMap() - result = py_utils.NestedMap() - while self._i < len(self._tokens): - name = self._ConsumeKey() - self._ConsumeSymbol('=') - result[name] = self._ConsumeItem() - if self._MaybeConsumeSymbol(')'): - return result - self._ConsumeSymbol(',') - raise ValueError('Ran out of tokens while looking for end of NestedMap.') - - def _ConsumeList(self): - """Return the list that starts at the current position, and increment. - - Returns: - The list that starts at the current token position. - """ - if self._i >= len(self._tokens): - raise ValueError('Ran out of tokens while looking for a list.') - if self._tokens[self._i] != '[': - raise ValueError('Expected [ at token position %d' % self._i) - self._i += 1 - if self._MaybeConsumeSymbol(']'): - # Empty lists are allowed. - return [] - result = [] - while self._i < len(self._tokens): - result.append(self._ConsumeItem()) - if self._MaybeConsumeSymbol(']'): - return result - self._ConsumeSymbol(',') - raise ValueError('Ran out of tokens while looking for end of list.') - - def _ParseInputs(self): - """Parse the inputs signature string. - - Returns: - dict: The parsed inputs structure, like ``{'a': ['b', 'c']}``. - """ - self._i = 0 - # The tokenization process adds fake '[' and ']' tokens so we can - # parse them as a list. - return self._ConsumeList() - - -class GraphLayer(base_layer.BaseLayer): - r"""A layer that connects a few layers in a simple data flow graph. - - Params.sub specifies a list of (signature, layer param) pairs for all the - sub-layers. 'Signature' of a layer specifies the signature of the - corresponding layer's FProp function. It also names its input and output - tensors. A 'signature' is of the form 'x,b->c', where tensors on the left - side of '->' specifies the input to the layer and tensors to the right - specifies the output from the layer. Note, the input to a layer has to be - produced before, either as input to the GraphLayer or as produced by some - previous layer. The output of a layer must be uniquely named, i.e. they can't - reuse names assigned previous layer output or the input to this GraphLayer. - - The full grammar of the signature is described in the GraphSignature class - definition above. - - Example - input: ['a', 'b'] - 'a->c', Fn(lambda x : tf.nn.relu(x)) - 'c,b->d', Fn(lambda x, y : x + y) - - The above example computes relu(a) + b - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_endpoints', [], 'Names of the input tensors.') - p.Define('output_endpoints', [], 'Names of the output tensors.') - # TODO(yonghui): Define a NamedTuple for this pair. - p.Define('sub', [], 'A list of (signature, layer params) pairs.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.input_endpoints - self._seq = [] - for i, (signature, sub) in enumerate(p.sub): - assert signature - sig = GraphSignature(signature) - assert sig.outputs, '{}'.format(signature) - name = sub.name - if not name: - name = '%s_%02d' % (sig.outputs[0], i) - sub.name = name - self.CreateChild(name, sub) - self._seq.append((name, sig, self.children[name])) - - def FProp(self, theta, *args): - p = self.params - - graph_tensors = self._fprop = GraphTensors() - with tf.name_scope(p.name): - if len(p.input_endpoints) != len(args): - raise ValueError( - 'Wrong number of inputs for {}: required={}, provided={}'.format( - p.name, len(p.input_endpoints), len(args))) - for n, t in zip(p.input_endpoints, args): - if isinstance(t, py_utils.NestedMap): - assert all(isinstance(x, tf.Tensor) for x in t.Flatten()), t - else: - assert isinstance(t, tf.Tensor) - graph_tensors.StoreTensor(n, t) - - ch_out = None - for i, (name, sig, ch) in enumerate(self._seq): - th = theta[name] - template = py_utils.NestedMap(inputs=sig.inputs) - packed = template.Transform(graph_tensors.GetTensor) - input_args = packed.inputs - tf.logging.vlog(1, 'signature: %s', p.sub[i][0]) - tf.logging.vlog(1, 'GraphLayer: call %s %s %d %s', ch.params.name, ch, - len(input_args), str(input_args)) - ch_out = ch.FProp(th, *input_args) - if len(sig.outputs) == 1: - ch_out = (ch_out,) - assert len(sig.outputs) == len(ch_out) - for n, t in zip(sig.outputs, ch_out): - graph_tensors.StoreTensor(n, t) - - layer_out = tuple(graph_tensors.GetTensor(x) for x in p.output_endpoints) - if len(layer_out) == 1: - layer_out = layer_out[0] - - return layer_out - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - total = 0 - - graph_tensors = GraphTensors() - assert len(p.input_endpoints) == len(args) - for n, t in zip(p.input_endpoints, args): - graph_tensors.StoreTensor(n, t) - - ch_out = None - for signature, sub in p.sub: - sig = GraphSignature(signature) - template = py_utils.NestedMap(inputs=sig.inputs) - packed = template.Transform(graph_tensors.GetTensor) - input_args = packed.inputs - - meta = sub.cls.FPropMeta(sub, *input_args) - total += meta.flops - ch_out = meta.out_shapes - assert len(ch_out) == len(sig.outputs) - for n, t in zip(sig.outputs, ch_out): - graph_tensors.StoreTensor(n, t) - - layer_out = tuple(graph_tensors.GetTensor(x) for x in p.output_endpoints) - return py_utils.NestedMap(flops=total, out_shapes=layer_out) - - -class ParallelLayer(base_layer.BaseLayer): - """A layer which connects a few layers in a parallel.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'sub', [], 'A list of layers\' params. Each layer\'s ' - 'FProp must return one Tensor or a tuple of Tensors. ' - 'Their return values then can be merged according to the ' - 'merge method. ') - p.Define( - 'merge', None, 'Method to combine sub-layers\' outputs.' - 'It must be a callable list(tuple(tf.Tensor)) -> tuple(tf.Tensor).') - p.Define( - 'merge_meta', None, 'Callable to compute the meta of merge(). It ' - 'takes a list of tuples of TensorShape, and returns a NestedMap with ' - 'flops and out_shapes, etc.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - self._seq = [] - for sub in p.sub: - self.CreateChild(sub.name, sub) - self._seq.append((sub.name, self.children[sub.name])) - - def FProp(self, theta, *args): - p = self.params - - with tf.name_scope(p.name): - # Computes sub layers in parallel. - outputs = [] - for (name, ch) in self._seq: - th = theta[name] - out = ch.FProp(th, *args) - if isinstance(out, (list, tuple)): - outputs.append(tuple(out)) - else: - outputs.append((out,)) - rets = p.merge(outputs) - return rets if len(rets) > 1 else rets[0] - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - total = 0 - outputs = [] - for sub in p.sub: - tf.logging.vlog(1, ' par abs fprop %s %s %d %s', sub.name, sub.cls, - len(args), str(args)) - meta = sub.cls.FPropMeta(sub, *args) - py_utils.CheckShapes(meta.out_shapes) - meta.VLog( - 1, ' par abs fprop {} {} {} {}'.format(sub.name, sub.cls, len(args), - str(args))) - total += meta.flops - outputs.append(meta.out_shapes) - - meta = p.merge_meta(outputs) - py_utils.CheckShapes(meta.out_shapes) - meta.flops += total - return meta - - -class MapLayer(base_layer.BaseLayer): - """A layer applies a lambda on every argument.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('fn', None, 'A callable tensor->tensor.') - p.Define('fn_meta', None, 'A callable shape->(flops, shape).') - p.Define('kwargs', {}, 'Keyword arguments to fn.') - return p - - def FProp(self, theta, *args): - r"""Applies lambda(x, \*kwargs) for every non-None arg.""" - del theta - p = self.params - with tf.name_scope(p.name): - ret = [None if x is None else p.fn(x, **p.kwargs) for x in args] - return tuple(ret) if len(ret) > 1 else ret[0] - - @classmethod - def FPropMeta(cls, p, *args): - flops, rets = 0, [] - for x in args: - if x is None: - rets.append(None) - else: - cost, shape = p.fn_meta(x) - py_utils.CheckShapes((shape,)) - flops += cost - rets.append(shape) - return py_utils.NestedMap(flops=flops, out_shapes=tuple(rets)) - - -class LinearLayer(base_layer.BaseLayer): - """Linear layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dims', 0, 'Depth of the input.') - p.Define('output_dims', 0, 'Depth of the output.') - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - self.CreateVariable( - 'w', - py_utils.WeightParams( - shape=[p.input_dims, p.output_dims], - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - - def FProp(self, theta, inputs): - """Apply projection to inputs. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - inputs: The inputs tensor. Shaped [..., input_dims]. - - Returns: - Projected inputs. - """ - p = self.params - with tf.name_scope(p.name): - computation_cost.Add( - self, 'flops', - tf.reduce_prod(tf.cast(tf.shape(inputs)[:-1], tf.int64)) * - tf.cast(symbolic.ToTensor(p.input_dims * p.output_dims), tf.int64) * - 2) - return py_utils.ProjectLastDim(inputs, theta.w, p.input_dims, - p.output_dims) - - @classmethod - def FPropMeta(cls, p, inputs): - py_utils.CheckShapes((inputs,)) - assert p.input_dims == inputs[-1] - # c_{ij} += x_{ik} * y_{kj} are considered 2 flops. - return py_utils.NestedMap( - flops=inputs.size * p.output_dims * 2, - out_shapes=(inputs[:-1] + [p.output_dims],)) - - -class BiasLayer(base_layer.BaseLayer): - """Bias layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('dims', 0, 'Depth of the input.') - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - self.CreateVariable( - 'b', - py_utils.WeightParams( - shape=[p.dims], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - - def FProp(self, theta, inputs): - """Adds bias to inputs. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - inputs: The inputs tensor. Shaped [..., dims]. - - Returns: - Inputs plus bias. - """ - with tf.name_scope(self.params.name): - return inputs + theta.b - - @classmethod - def FPropMeta(cls, p, inputs): - py_utils.CheckShapes((inputs,)) - assert inputs[-1] == p.dims - return py_utils.NestedMap(flops=inputs.size, out_shapes=(inputs,)) - - -class BranchLayer(base_layer.BaseLayer): - """A layer to help constructing a network structure with multiple outputs.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('body', None, 'The param for the main network layer.') - p.Define( - 'fetches', [], 'Fetch points within the body layer. Each fetch ' - 'layers\' activation is appended to the output of body.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - self.CreateChild('body', p.body) - - def FProp(self, theta, *args): - p = self.params - with tf.name_scope(p.name): - args = _ToTuple(self.body.FProp(theta.body, *args)) - for fetch in p.fetches: - args += (self.body.GetDescendant(fetch).activation,) - return args - - -class BatchParallelLayer(base_layer.BaseLayer): - """A layer splits the batch and compute the FProp on multiple devices.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('sub', None, 'A layer param.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - self.CreateChild('sub', p.sub) - - def FProp(self, theta, *args): - """FProp through multiple devices in the split. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - *args: A tuple of Tensors (one or more). Every tensor's first dimension is - the same (the batch dimension). - - Returns: - The sub layer's output. - """ - p = self.params - with tf.name_scope(p.name): - assert all(isinstance(x, tf.Tensor) for x in args) - cluster = self.cluster - num = cluster.num_devices_per_split - if num == 1: - return self.sub.FProp(theta.sub, *args) - inps = py_utils.SplitRecursively(list(args), num, axis=0) - outs = [] - for i, xs in enumerate(inps): - device = cluster.WorkerDeviceInModelSplit(i) - tf.logging.info('%d on device %s', i, device) - with tf.device(device): - ys = self.sub.FProp(theta.sub, *xs) - if isinstance(ys, tuple): - outs += [list(ys)] - else: - outs += [ys] # ys is a single tensor - ret = py_utils.ConcatRecursively(outs, axis=0) - if isinstance(ret, list): - return tuple(ret) - else: - return ret # ys is a single tensor - - -class FnLayer(base_layer.BaseLayer): - """A layer applies a function on a tuple of tensors.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('fn', None, 'A lambda tuple(Tensor) -> tuple(Tensor) ' - 'or a single Tensor.') - p.Define( - 'fn_meta', None, 'Callable to compute the meta of fn(). It ' - 'takes a tuple of TensorShape, and returns a NestedMap with ' - 'flops and out_shapes, etc.') - return p - - def FProp(self, theta, *args): - r"""Applies a function (p.fn) on args. - - Args: - theta: Unused. - *args: A tuple of Tensors (one or more). - - Returns: - fn(\*args). - """ - with tf.name_scope(self.params.name): - return self.params.fn(*args) - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - meta = p.fn_meta(*args) - py_utils.CheckShapes(meta.out_shapes) - return meta - - -class RematerializationLayer(base_layer.BaseLayer): - """A wrapper layer with rematerialization.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('body', None, - 'The main layer whose FProp will be wrapped by RematerializeFn.') - return p - - def __init__(self, params): - super().__init__(params) - self.CreateChild('body', self.params.body) - - def FProp(self, theta, *xs): - input_list = theta.body.Flatten() - theta_len = len(input_list) - input_list += list(xs) - input_len = len(input_list) - - def Fn(*args): - body_theta = theta.body.Pack(args[:theta_len]) - return self.body.FProp(body_theta, *args[theta_len:input_len]) - - return py_utils.RematerializeFn(Fn, *input_list) - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - return p.body.cls.FPropMeta(p.body, *args) - - -class PrintShapeLayer(base_layer.BaseLayer): - """A layer prints debug information.""" - - def FProp(self, theta, *args): - p = self.params - with tf.name_scope(p.name) as name_scope: - for i, arg in enumerate(args): - if not isinstance(arg, tf.Tensor): - tf.logging.info( - 'FProp non-Tensor input in {}: arg_{} arg = {}'.format( - name_scope, i, arg)) - else: - tf.logging.info( - 'FProp inputs in {}: arg_{} shape = {} dtype = {}'.format( - name_scope, i, arg.shape, arg.dtype.name)) - if len(args) == 1: - return args[0] - else: - return args - - @classmethod - def FPropMeta(cls, p, *args): - py_utils.CheckShapes(args) - return py_utils.NestedMap(flops=0, out_shapes=args) diff --git a/lingvo/core/builder_layers_test.py b/lingvo/core/builder_layers_test.py deleted file mode 100644 index 8697c55a..00000000 --- a/lingvo/core/builder_layers_test.py +++ /dev/null @@ -1,554 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for builder_layers.""" - -from lingvo import compat as tf -from lingvo.core import builder_layers as layers -from lingvo.core import layers as lingvo_layers -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core import tshape -import numpy as np - - -class BuilderLayerTest(test_utils.TestCase): - - def testFirstNLayerFProp(self): - with self.session(): - params = layers.FirstNLayer.Params() - params.name = 'fn' - params.n = 2 - - fn_layer = layers.FirstNLayer(params) - a = tf.constant(1) - b = tf.constant(2) - c = tf.constant(3) - - fn_out = self.evaluate(fn_layer.FPropDefaultTheta(a, b, c)) - - self.assertEqual((1, 2), fn_out) - - def testArgIndexLayerFProp(self): - with self.session(): - params = layers.ArgIndexLayer.Params().Set(name='argidx', idx=[1, 3]) - argidx_layer = layers.ArgIndexLayer(params) - args = [tf.constant(i) for i in range(5)] - - argidx_out = self.evaluate(argidx_layer.FPropDefaultTheta(*args)) - self.assertEqual((1, 3), argidx_out) - - def testSequentialLayer(self): - g = tf.Graph() - with g.as_default(), self.SetEval(True): - tf.random.set_seed(24332) - p = layers.SequentialLayer.Params().Set( - name='seq', - repeat=2, - sub=[ - lingvo_layers.FCLayer.Params().Set( - name='foo', input_dim=32, output_dim=8), - lingvo_layers.FCLayer.Params().Set( - name='bar', input_dim=8, output_dim=8), - lingvo_layers.FCLayer.Params().Set( - name='baz', input_dim=8, output_dim=32), - lingvo_layers.DropoutLayer.Params().Set( - name='dropout', keep_prob=0.5) - ]) - l = p.Instantiate() - x = tf.random.normal(shape=[2, 32]) - y = l.FPropDefaultTheta(x) - l.vars.Transform(lambda x: x.shape).VLog(0, 'vars: ') - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - x_val, y_val, w = self.evaluate([x, y, l.vars]) - - act = x_val - # relu(act \dot w + b) - for i in range(2): - act = np.maximum(0, np.dot(act, w.rep[i].foo.w) + w.rep[i].foo.b) - act = np.maximum(0, np.dot(act, w.rep[i].bar.w) + w.rep[i].bar.b) - act = np.maximum(0, np.dot(act, w.rep[i].baz.w) + w.rep[i].baz.b) - self.assertAllClose(act, y_val) - - def testEmptySequentialLayer(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(24332) - p = layers.SequentialLayer.Params().Set(name='seq') - l = p.Instantiate() - x = tf.random.normal(shape=[2, 32]) - y = l.FPropDefaultTheta(x) - self.assertIsInstance(y, tf.Tensor) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - x_val, y_val = self.evaluate([x, y]) - self.assertAllEqual(x_val, y_val) - - def testEmptySequentialLayerFPropMeta(self): - g = tf.Graph() - with g.as_default(): - p = layers.SequentialLayer.Params().Set(name='seq') - l = p.Instantiate() - x = py_utils.NestedMap(val=tf.random.normal(shape=[2, 32])) - y = l.FPropDefaultTheta(x) - self.assertIsInstance(y.val, tf.Tensor) - y_shape = l.FPropMeta( - p, py_utils.Transform(lambda t: tshape.Shape(t.shape), - x)).out_shapes[0] - self.assertEqual(y.val.shape.as_list(), - y_shape.val.ToTensorShape().as_list()) - - def testUnarySequentialLayer(self): - g = tf.Graph() - with g.as_default(), self.SetEval(True): - tf.random.set_seed(24332) - p = layers.UnarySequentialLayer.Params().Set( - name='seq', - sub=[ - lingvo_layers.FCLayer.Params().Set( - name='foo', input_dim=32, output_dim=8), - lingvo_layers.FCLayer.Params().Set( - name='bar', input_dim=8, output_dim=8), - lingvo_layers.FCLayer.Params().Set( - name='baz', input_dim=8, output_dim=32), - lingvo_layers.DropoutLayer.Params().Set( - name='dropout', keep_prob=0.5) - ]) - l = p.Instantiate() - x = tf.random.normal(shape=[2, 32]) - y = l.FPropDefaultTheta(x) - l.vars.Transform(lambda x: x.shape).VLog(0, 'vars: ') - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - x_val, y_val, w = self.evaluate([x, y, l.vars]) - - act = x_val - # relu(act \dot w + b) - act = np.maximum(0, np.dot(act, w.foo.w) + w.foo.b) - act = np.maximum(0, np.dot(act, w.bar.w) + w.bar.b) - act = np.maximum(0, np.dot(act, w.baz.w) + w.baz.b) - self.assertAllClose(act, y_val) - - def testParallelLayer(self): - g = tf.Graph() - with g.as_default(), self.SetEval(True): - tf.random.set_seed(24332) - p = layers.ParallelLayer.Params().Set( - name='test', - merge=lambda xs: tuple([tf.add_n(x) for x in zip(*xs)]), - sub=[ - lingvo_layers.FCLayer.Params().Set( - name='foo', input_dim=32, output_dim=4), - lingvo_layers.FCLayer.Params().Set( - name='bar', input_dim=32, output_dim=4), - layers.SequentialLayer.Params().Set( - name='seq', - sub=[ - lingvo_layers.FCLayer.Params().Set( - name='baz', input_dim=32, output_dim=4), - lingvo_layers.DropoutLayer.Params().Set( - name='dropout', keep_prob=0.5) - ]) - ]) - l = p.Instantiate() - x = tf.random.normal(shape=[2, 32]) - y = l.FPropDefaultTheta(x) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - x_val, y_val, w = self.evaluate([x, y, l.vars]) - - out = [] - act = x_val - # relu(act \dot w + b) - out += [np.maximum(0, np.matmul(act, w.foo.w) + w.foo.b)] - self.assertEqual(out[-1].shape, (2, 4)) - out += [np.maximum(0, np.matmul(act, w.bar.w) + w.bar.b)] - self.assertEqual(out[-1].shape, (2, 4)) - out += [np.maximum(0, np.matmul(act, w.seq.baz.w) + w.seq.baz.b)] - self.assertEqual(out[-1].shape, (2, 4)) - - np_result = out[0] - for v in out[1:]: - np_result = np.add(np_result, v) - self.assertAllClose(np_result, y_val) - - def testParallelMatmulLayer(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(24332) - - def MergeFn(xs): - result = [] - for x in zip(*xs): - val = x[0] - for v in x[1:]: - val = tf.matmul(val, v) - result.append(val) - return tuple(result) - - p = layers.ParallelLayer.Params().Set( - name='parallel', - merge=MergeFn, - sub=[ - lingvo_layers.FCLayer.Params().Set( - name='foo', input_dim=32, output_dim=4), - lingvo_layers.FCLayer.Params().Set( - name='bar', input_dim=32, output_dim=4), - lingvo_layers.FCLayer.Params().Set( - name='baz', input_dim=32, output_dim=4) - ]) - l = p.Instantiate() - x = tf.random.normal(shape=[2, 4, 32]) - y = l.FPropDefaultTheta(x) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - x_val, y_val, w = self.evaluate([x, y, l.vars]) - - out = [] - act = x_val - # relu(act \dot w + b) - out += [np.maximum(0, np.matmul(act, w.foo.w) + w.foo.b)] - self.assertEqual(out[-1].shape, (2, 4, 4)) - out += [np.maximum(0, np.matmul(act, w.bar.w) + w.bar.b)] - self.assertEqual(out[-1].shape, (2, 4, 4)) - out += [np.maximum(0, np.matmul(act, w.baz.w) + w.baz.b)] - self.assertEqual(out[-1].shape, (2, 4, 4)) - - np_result = out[0] - for v in out[1:]: - np_result = np.matmul(np_result, v) - self.assertAllClose(np_result, y_val, atol=1e-5, rtol=1e-5) - - def testParalellMultiOutputsLayer(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(24332) - - def Merge(xs): - rets = [] - for x in zip(*xs): - if x[0] is None: - rets.append(None) - else: - rets.append(tf.add_n(list(x))) - return tuple(rets) - - p = layers.ParallelLayer.Params().Set( - name='parallel', - merge=Merge, - sub=[ - lingvo_layers.ConvLayer.Params().Set( - name='p%d' % i, - filter_shape=(3, 3, 3, 5), - filter_stride=(1, 1), - batch_norm=False) for i in range(3) - ]) - l = p.Instantiate() - x = tf.zeros(shape=[2, 32, 32, 3]) - y0, y1 = l.FPropDefaultTheta(x) - y_sum = tf.reduce_sum(y0) - # Ensures the 2nd return value (None) are handled properly. - self.assertEqual(None, y1) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - y_sum_val = self.evaluate(y_sum) - - self.assertEqual(y_sum_val, 0.) - - def testMapLayer(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(24332) - p = layers.MapLayer.Params().Set( - name='map', fn=tf.reduce_max, kwargs={'axis': 1}) - l = p.Instantiate() - x0, x1 = [tf.random.normal(shape=[2, 3, 5])] * 2 - y0, y1 = l.FPropDefaultTheta(x0, x1) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - vx0, vx1, vy0, vy1 = self.evaluate([x0, x1, y0, y1]) - - self.assertAllClose(np.max(vx0, 1), vy0) - self.assertAllClose(np.max(vx1, 1), vy1) - - def testLinearLayer(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(24332) - p = layers.LinearLayer.Params().Set( - name='test', input_dims=10, output_dims=5) - l = p.Instantiate() - xs = [] - ys = [] - for shape in ([2, 10], [2, 3, 10], [2, 3, 5, 10], [2, 3, 5, 7, 10]): - x = tf.random.normal(shape=shape) - y = l.FPropDefaultTheta(x) - xs += [x] - ys += [y] - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - xs_val, ys_val, w_val = self.evaluate([xs, ys, l.vars]) - - self.assertEqual(w_val.w.shape, (10, 5)) - for (xv, yv) in zip(xs_val, ys_val): - self.assertAllClose(np.matmul(xv, w_val.w), yv) - - def testBiasLayer(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(24332) - p = layers.BiasLayer.Params().Set(name='test', dims=10) - l = p.Instantiate() - x = tf.random.normal(shape=[2, 10]) - y = l.FPropDefaultTheta(x) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - x_val, y_val, w_val = self.evaluate([x, y, l.vars]) - - self.assertEqual(w_val.b.shape, (10,)) - self.assertAllClose(x_val + w_val.b, y_val) - - def testGraphTensors(self): - graph_tensors = layers.GraphTensors() - graph_tensors.StoreTensor('t', - py_utils.NestedMap(a=py_utils.NestedMap(b='c'))) - self.assertEqual('c', graph_tensors.GetTensor('t.a.b')) - - def testSignatureParsing(self): - sig = layers.GraphSignature('a,b->c') - self.assertEqual(['a', 'b'], sig.inputs) - self.assertEqual(['c'], sig.outputs) - - sig = layers.GraphSignature('[a,b],d->c') - self.assertEqual([['a', 'b'], 'd'], sig.inputs) - self.assertEqual(['c'], sig.outputs) - - # also test nested structures, like nested lists and dicts. - sig = layers.GraphSignature('(x=a,y=b)->c') - self.assertEqual([{'x': 'a', 'y': 'b'}], sig.inputs) - self.assertEqual(['c'], sig.outputs) - - # Make sure that empty lists and dicts work. - sig = layers.GraphSignature('(x=[]),()->d') - self.assertEqual([{'x': []}, {}], sig.inputs) - - sig = layers.GraphSignature('(x=a,y=[f,(z=g.h)]),[d,e]->j') - self.assertEqual([{ - 'x': 'a', - 'y': ['f', { - 'z': 'g.h' - }] - }, ['d', 'e']], sig.inputs) - self.assertEqual(['j'], sig.outputs) - - def testGraphLayer(self): - g = tf.Graph() - with g.as_default(), self.SetEval(True): - tf.random.set_seed(24332) - - def _FnMeta(*shapes): - return py_utils.NestedMap(flops=1, out_shapes=shapes) - - p = layers.GraphLayer.Params().Set( - name='graph', - input_endpoints=['x'], - output_endpoints=['y'], - sub=[ - ('x.a->y.c', - layers.FnLayer.Params().Set(fn=lambda x: 2 * x, - fn_meta=_FnMeta)), - ('x.b->y.d', layers.FnLayer.Params().Set( - name='bar', fn=lambda x: x + 2, fn_meta=_FnMeta)), - ('y.c,y.d->y.e, y.f', layers.FnLayer.Params().Set( - name='baz', fn=lambda x, y: (x + y, x - y), fn_meta=_FnMeta)), - ]) - l = p.Instantiate() - x = py_utils.NestedMap(a=tf.constant(1.0), b=tf.constant(2.0)) - y = l.FProp(l.theta, x) - y_shape = l.FPropMeta( - p, py_utils.Transform(lambda t: tshape.Shape(t.shape), - x)).out_shapes[0] - self.assertDictEqual( - py_utils.Transform(lambda t: t.shape.as_list(), y), - py_utils.Transform(lambda t: t.ToTensorShape().as_list(), y_shape)) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - y_val = self.evaluate(y) - print(y_val) - self.assertEqual(py_utils.NestedMap(c=2.0, d=4.0, e=6.0, f=-2.0), y_val) - - def testSoftCondLayer(self): - num_experts = 100 - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(24332) - p = layers.SoftCondLayer.Params().Set( - name='soft_cond', - cond_dim=2, - num_experts=num_experts, - body=lingvo_layers.FCLayer.Params().Set(input_dim=2, output_dim=2)) - l = p.Instantiate() - x = tf.random.normal(shape=[1, 2, 2]) - y = l.FPropDefaultTheta(x) - self.evaluate(tf.global_variables_initializer()) - x_val, y_val, vars_val = self.evaluate([x, y, l.vars]) - - p_nz = layers.SoftCondLayer.Params().Set( - name='soft_cond_nonzeros', - cond_dim=2, - num_experts=num_experts, - nonzeros_mean=True, - body=lingvo_layers.FCLayer.Params().Set(input_dim=2, output_dim=2)) - l_nz = p_nz.Instantiate() - x_nz = tf.random.normal(shape=[1, 2, 2]) - y_nz = l_nz.FPropDefaultTheta(x_nz) - self.evaluate(tf.global_variables_initializer()) - x_nz_val, y_nz_val, vars_nz_val = self.evaluate([x_nz, y_nz, l_nz.vars]) - - np_val = x_val[0] - np_nz_val = x_nz_val[0] - taks_weight = np.exp(-1.0 * np.dot(np.mean(np_val, 0), vars_val.w)) - taks_weight = 1.0 / (1.0 + taks_weight) - nzs = np.count_nonzero(np_nz_val, 0).astype('float32') + 1e-10 - taks_weight_nz = np.exp(-1.0 * - np.dot(np.sum(np_nz_val, 0) / nzs, vars_nz_val.w)) - taks_weight_nz = 1.0 / (1.0 + taks_weight_nz) - weighted_weight = np.einsum('i,ijk->jk', taks_weight, vars_val.body.w) - weighted_weight_nz = np.einsum('i,ijk->jk', taks_weight_nz, - vars_nz_val.body.w) - weighted_bias = np.einsum('i,ij->j', taks_weight, vars_val.body.b) - weighted_bias_nz = np.einsum('i,ij->j', taks_weight_nz, vars_nz_val.body.b) - np_val_out = np.maximum(0, np.dot(np_val, weighted_weight) + weighted_bias) - np_val_out_nz = np.maximum( - 0, - np.dot(np_nz_val, weighted_weight_nz) + weighted_bias_nz) - self.assertAllClose(np_val_out, y_val[0]) - self.assertAllClose(np_val_out_nz, y_nz_val[0]) - - def testRepeatLayer(self): - repeat = 100 - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(24332) - p = layers.RepeatLayer.Params().Set( - name='recurrent', - repeat=repeat, - body=lingvo_layers.FCLayer.Params().Set(input_dim=2, output_dim=2)) - l = p.Instantiate() - x = tf.random.normal(shape=[2, 2]) - y = l.FPropDefaultTheta(x) - self.evaluate(tf.global_variables_initializer()) - x_val, y_val, w = self.evaluate([x, y, l.vars]) - - np_val = x_val - - # relu(act \dot w + b) - for i in range(repeat): - np_val = np.maximum(0, np.dot(np_val, w.body.w[i]) + w.body.b[i]) - self.assertAllClose(np_val, y_val) - - def testParallelRepeatLayerLayer(self): - repeat = 100 - body_p = layers.SequentialLayer.Params().Set( - name='body', - sub=[ - layers.LinearLayer.Params().Set( - name='ln1', input_dims=2, output_dims=4), - layers.FnLayer.Params().Set( - name='relu', - fn=tf.nn.relu, - fn_meta=lambda x: py_utils.NestedMap(flops=1, out_shapes=(x,))), - layers.LinearLayer.Params().Set( - name='ln2', input_dims=4, output_dims=2) - ]) - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(24332) - p = layers.ParallelRepeatLayer.Params().Set( - name='moe', repeat=repeat, body=body_p) - l = p.Instantiate() - x = tf.random.normal(shape=[repeat, 2, 2]) - y = l.FPropDefaultTheta(x) - self.evaluate(tf.global_variables_initializer()) - x_val, y_val, w = self.evaluate([x, y, l.vars]) - - np_val = [] - - for i in range(repeat): - # relu(act \dot w_1) \dot w_2 - np_val.append( - np.dot( - np.maximum(0, np.dot(x_val[i], w.body.ln1.w[i])), - w.body.ln2.w[i])) - np_val = np.stack(np_val) - self.assertAllClose(np_val, y_val) - - def testRematerializationLayer(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(24332) - - def MulSumFnMeta(x): - return py_utils.NestedMap(flops=2, out_shapes=(x,)) - - def AddFnMeta(x, y): - del y - return py_utils.NestedMap(flops=2, out_shapes=(x,)) - - p = layers.GraphLayer.Params().Set( - name='graph', - input_endpoints=['a', 'b'], - output_endpoints=['e'], - sub=[ - ('a->c', layers.FnLayer.Params().Set( - fn=lambda x: 2 * x, fn_meta=MulSumFnMeta)), - ('b->d', layers.FnLayer.Params().Set( - name='bar', fn=lambda x: x + 2, fn_meta=MulSumFnMeta)), - ('c,d->e', layers.FnLayer.Params().Set( - name='baz', fn=lambda x, y: x + y, fn_meta=AddFnMeta)), - ]) - p = layers.RematerializationLayer.Params().Set(name='remat', body=p) - l = p.Instantiate() - x = tf.constant(1.0) - y = tf.constant(2.0) - z = l.FProp(l.theta, x, y) - self.evaluate(tf.global_variables_initializer()) - z_val = self.evaluate(z) - print(z_val) - self.assertAllClose(6.0, z_val) - - def testPrintShapeLayer(self): - g = tf.Graph() - with g.as_default(): - p = layers.PrintShapeLayer.Params().Set(name='test') - l = p.Instantiate() - x = tf.constant(1.0) - y = l.FPropDefaultTheta(x) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - x_val, y_val = self.evaluate([x, y]) - self.assertEqual(x_val, y_val) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/builder_test.py b/lingvo/core/builder_test.py deleted file mode 100644 index 9dbeec32..00000000 --- a/lingvo/core/builder_test.py +++ /dev/null @@ -1,336 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for the Builder pattern.""" - -import inspect -from lingvo import compat as tf -from lingvo.core import builder -from lingvo.core import cluster_factory -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core import tshape -import numpy as np -import sympy - - -class BuilderTest(test_utils.TestCase): - - def _Expect(self, expected_cost, p, *inputs): - meta = p.cls.FPropMeta(p, *(tshape.Shape(s) for s in inputs)) - self.assertEqual(meta.flops, expected_cost) - - g = tf.Graph() - with g.as_default(): - l = p.Instantiate() - xs = [tf.random.normal(shape=s) for s in inputs] - ys = l.FPropDefaultTheta(*xs) - - with self.session(graph=g) as sess: - sess.run(tf.global_variables_initializer()) - _ = sess.run(ys) - - def testCostEstimate(self): - b = builder.Base.Params() - b = b.Instantiate() - # pyformat: disable - self._Expect(2400, b._Linear('l', 10, 20), [2, 3, 10]) - self._Expect(60, b._Bias('b', 10), [2, 3, 10]) - self._Expect(150, b._Activation('a'), [3, 5, 10]) - self._Expect(600, b._BN('b', 10), [2, 3, 10]) - self._Expect( - (2**21) * 9 * 2, - b._Conv2D('c', (3, 3, 8, 16), (1, 1)), - [16, 32, 32, 8]) - self._Expect( - (2**19) * 9 * 2, - b._Conv2D('c', (3, 3, 8, 16), (2, 2)), - [16, 32, 32, 8]) - self._Expect( - (2**20) * 9 * 2, - b._Conv2D('c', (3, 3, 8, 16), (1, 2)), - [16, 32, 32, 8]) - self._Expect( - 24 * 10 * 10 * 2 * 7, - b._Rep( - 'rep', - 7, - b._Linear('l', 10, 10)), - [8, 3, 10]) - self._Expect( - (2**21) * 9 * 2 + (2**14) * 16 * 32 * 2 + (2**14) * 32, - b._Seq( - 'seq', - b._Conv2D('c', (3, 3, 8, 16), (1, 1)), - b._Linear('l', 16, 32), - b._Activation('a')), - [16, 32, 32, 8]) - self._Expect( - (((2**19) * 9 * 2 + (2**12) * 16 * 32 * 2 + (2**12) * 32) * 2), - b._Par( - 'p', - b._Seq( - 'b0', - b._Conv2D('c', (3, 3, 8, 16), (2, 2)), - b._Linear('l', 16, 32), - b._Activation('a')), - b._Seq( - 'b1', - b._Conv2D('c', (3, 3, 8, 16), (2, 2)), - b._Linear('l', 16, 32), - b._Activation('a'))), - [16, 32, 32, 8]) - # pyformat: enable - - def testFetch(self): - # Construct a layer w/ two parallel branches. We want to demonstrate that - # we can fetch intermediate values from two branches independently. - # pyformat: disable - b = builder.Base.Params() - b = b.Instantiate() - p = b._Par( - 'p', - b._Seq( - 'b0', - b._Conv2D('c', (3, 3, 8, 16), (2, 2)), - b._Linear('l', 16, 32), - b._Save('fetch'), - b._Activation('a')), - b._Seq( - 'b1', - b._Conv2D('c', (3, 3, 8, 16), (2, 2)), - b._Linear('l', 16, 32), - b._Activation('a'), - b._Save('fetch'))) - p = b._AddFetches('mh', p, ['b0.fetch', 'b1.fetch']) - # pyformat: enable - g = tf.Graph() - with g.as_default(): - l = p.Instantiate() - x = tf.random.normal(shape=[17, 64, 64, 8]) - outs = l.FPropDefaultTheta(x) - x, y, b0, b1 = outs - - with self.session(graph=g) as sess: - sess.run(tf.global_variables_initializer()) - v0, v1, u, v = sess.run([b0, b1, x, y]) - - self.assertAllClose(np.maximum(0, v0), u) - self.assertAllClose(v1, v) - - def testFetchGrad(self): - # Tests we can fetch backprop gradients. - # pyformat: disable - b = builder.Base.Params() - b = b.Instantiate() - p = b._Seq( - 'seq', - b._Linear('l', 16, 32), - b._Bias('b', 32), - b._Save('fetch'), - b._Activation('a')) - # pyformat: enable - - g = tf.Graph() - with g.as_default(): - l = p.Instantiate() - x = tf.random.normal(shape=[4, 16]) - y = l.FPropDefaultTheta(x) - loss = tf.reduce_sum(tf.square(y)) - _ = tf.gradients(ys=loss, xs=x) - - act, dact = l.fetch.activation, l.fetch.gradient - - with self.session(graph=g) as sess: - sess.run(tf.global_variables_initializer()) - act_v, dact_v = sess.run([act, dact]) - - # The last layer two layers is sum(square(relu(act))). - # So the dact is simply 2*relu(act). - self.assertAllClose(2 * np.maximum(0, act_v), dact_v) - - def testBatchParallel(self): - # pyformat: disable - b = builder.Base.Params() - b = b.Instantiate() - p = b._BatchParallel( - 'bp', - b._Seq( - 'main', - b._Linear('l', 8, 4), - b._PrintShape('debug'), - b._Bias('b', 4))) - # pyformat: enable - g = tf.Graph() - with g.as_default(): - l = p.Instantiate() - x = tf.random.normal(shape=[4, 8]) - with cluster_factory.ForTestingWorker(cpus=4, split_size=1): - y1 = l.FPropDefaultTheta(x) - with cluster_factory.ForTestingWorker(cpus=4, split_size=2): - y2 = l.FPropDefaultTheta(x) - with cluster_factory.ForTestingWorker(cpus=4, split_size=4): - y4 = l.FPropDefaultTheta(x) - - cfg = tf.config_pb2.ConfigProto() - cfg.device_count['CPU'] = 4 - with self.session(config=cfg, graph=g) as sess: - sess.run(tf.global_variables_initializer()) - v1, v2, v4 = sess.run([y1, y2, y4]) - - self.assertAllClose(v1, v2) - self.assertAllClose(v1, v4) - - def testFn(self): - b = builder.Base.Params() - b = b.Instantiate() - - p = b._Fn('fn', lambda x, y: x + y, fn_out=lambda x, y: x) - - meta = p.cls.FPropMeta(p, tshape.Shape([4, 6]), tshape.Shape([4, 6])) - self.assertEqual(meta.flops, 48) - self.assertEqual(meta.out_shapes[0].ToTensorShape().as_list(), [4, 6]) - - g = tf.Graph() - with g.as_default(): - l = p.Instantiate() - x = tf.random.normal(shape=[4, 8]) - y = tf.random.normal(shape=[4, 1]) - z = l.FPropDefaultTheta(x, y) - - with self.session(graph=g) as sess: - sess.run(tf.global_variables_initializer()) - v = sess.run([x, y, z]) - - self.assertAllClose(v[0] + v[1], v[2]) - - def testRematerialize(self): - # Test the dropout consistency between fprop and bprop. - b = builder.Base.Params() - b = b.Instantiate() - start_block = layers.DeterministicDropoutLayer.Params().Set( - name='start_dropout', keep_prob=0.7) - # Build 4 dropout layers, each wrapped by RematerializeFn. - num_blocks = 4 - blocks = [] - blocks_per_cell = 2 - for i in range(num_blocks): - blocks.append(layers.DeterministicDropoutLayer.Params().Set( - name='dropout_{}'.format(i), keep_prob=0.7)) - cells = [] - while blocks: - heads, blocks = blocks[:blocks_per_cell], blocks[blocks_per_cell:] - cell_name = 'cell_{}'.format(len(cells)) - cells.append( - b._Rematerialize(name=cell_name, body=b._Seq(cell_name, *heads))) - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - tf.random.set_seed(12345) - p = b._Seq('test', start_block, *cells) - mdl = p.Instantiate() - # y = mdl.Frop(x * w) - # Fake input - x = tf.ones([4, 5]) - # Construct weights. - w = tf.get_variable( - 'w', shape=[4, 5], initializer=tf.constant_initializer([[1] * 5] * 4)) - y = mdl.FPropDefaultTheta(x * w) - # Construct loss function such that gradients = final activation. - # dy/dw = y = mdl.Frop(x * w) when w is 1. - loss = tf.reduce_sum(y) - grads = py_utils.ComputeGradients(loss, py_utils.NestedMap(w=w)) - tf.global_variables_initializer().run() - y_val, grads_val = sess.run([y, grads.Transform(tuple)]) - grads_val = grads_val['w'][1] - self.assertAllClose(y_val, grads_val) - self.assertEqual(py_utils.GetStepSeed().eval(), 1553244033) - - def testFnDefaultMeta(self): - b = builder.Base.Params() - b = b.Instantiate() - - def Foo(x, y): - return x * x, y * 2 - - p = b._Fn('fn', Foo) - - meta = p.cls.FPropMeta(p, tshape.Shape([4, 6]), tshape.Shape([3, 3])) - self.assertEqual(meta.flops, 33) - self.assertEqual(meta.out_shapes[0].ToTensorShape().as_list(), [4, 6]) - self.assertEqual(meta.out_shapes[1].ToTensorShape().as_list(), [3, 3]) - - g = tf.Graph() - with g.as_default(): - l = p.Instantiate() - x = tf.random.normal(shape=[4, 8]) - y = tf.random.normal(shape=[3, 3]) - z0, z1 = l.FPropDefaultTheta(x, y) - - with self.session(graph=g) as sess: - sess.run(tf.global_variables_initializer()) - vx, vy, vz0, vz1 = sess.run([x, y, z0, z1]) - - self.assertAllClose(vx * vx, vz0) - self.assertAllClose(vy * 2, vz1) - - def testSymbolicDims(self): - p = builder.Base.Params() - b = p.Instantiate() - - f1 = tshape.Shape(['kh', 'kw', 'idims', 'odims']) - kh, kw, idims, odims = f1 - f2 = tshape.Shape([kh, kw, odims, odims]) - p = b._Seq('test', b._Conv2D('conv', f1, (2, 2)), - b._Conv2D('conv', f2, (2, 2)), b._Bias('bias', odims)) - - inp = tshape.Shape(['b', 'h', 'w', idims]) - b, h, w, _ = inp - meta = p.cls.FPropMeta(p, inp) - print('flops = ', meta.flops) - out = meta.out_shapes[0] - print('outputs = ', out) - - # sympy.lambdify can help us to do faster numerical evaluation. - # Might be useful to build a "cost" model given a builder layer. - f = sympy.lambdify([b, h, w, kh, kw, idims, odims], meta.flops, 'numpy') - print('f.source = ', inspect.getsource(f)) - self.assertEqual(f(8, 224, 224, 3, 3, 8, 32), 925646848) - self.assertEqual(f(8, 224, 224, 5, 5, 8, 32), 2569814016) - - def testDoEval(self): - p = builder.Base.Params().Instantiate()._Dropout('dropout', 0.5) - - g = tf.Graph() - with g.as_default(): - l = p.Instantiate() - x = tf.random.normal(shape=[16, 16]) - # FProp three times each with different do_eval mode. - with cluster_factory.SetEval(mode=None): - a = l.FPropDefaultTheta(x) - with cluster_factory.SetEval(mode=False): - b = l.FPropDefaultTheta(x) - with cluster_factory.SetEval(mode=True): - c = l.FPropDefaultTheta(x) - - with self.session(graph=g) as sess: - x, a, b, c = sess.run([x, a, b, c]) - - self.assertGreater(np.linalg.norm(x - a), 0) - self.assertGreater(np.linalg.norm(x - b), 0) - self.assertAllEqual(x, c) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/checkpointer.py b/lingvo/core/checkpointer.py deleted file mode 100644 index 6d904ea5..00000000 --- a/lingvo/core/checkpointer.py +++ /dev/null @@ -1,264 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Checkpointing utilities for save/restore.""" - -import os -import time - -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import py_utils -import six - - -class Checkpointer: - """Checkpointing utility class. - - Needs to be created within a graph context. - """ - - def __init__(self, - train_dir, - model, - init_op=None, - train_params=None, - save_only=False): - """Initialize Checkpointer. - - Args: - train_dir: Training directory for saving checkpoints. - model: A BaseModel instance or None. - init_op: The initialize variables op. If unset, it will call - tf.global_variables_initializer(). - train_params: If specified, use these training params instead of those in - the `model`. - save_only: This checkpointer is only intended for saving checkpoints. - """ - self._train_dir = train_dir - self._save_only = save_only - if init_op: - self._init_op = init_op - else: - self._init_op = tf.global_variables_initializer() - - self._save_path = os.path.join(self._train_dir, 'ckpt') - - if train_params: - self._train_params = train_params - self._model = None - else: - assert model - self._train_params = model.params.train - self._model = model - - if not self._save_only: - self._params = model.params - self._model_tasks = model.tasks - self._model = model - - self._next_checkpoint_seconds = 0 - self._save_interval_seconds = self._train_params.save_interval_seconds - self._saver = self._GetSaver() - - self._uninitialized_vars = tf.report_uninitialized_variables( - tf.global_variables()) - - def _GetSaver(self): - """Returns a saver.""" - do_eval = cluster_factory.Current().do_eval - if not self._save_only and self._model.ema and do_eval: - tf.logging.info('Using EMA for evaluation.') - return tf.train.Saver( - self._model.ema.variables_to_restore(self._model.variables_for_ema)) - return tf.train.Saver( - sharded=True, - max_to_keep=self._train_params.save_max_to_keep, - keep_checkpoint_every_n_hours=( - self._train_params.save_keep_checkpoint_every_n_hours), - pad_step_number=True, # %08d - write_version=tf.train.SaverDef.V2) - - def RestoreFromPath(self, sess, checkpoint_path): - """Load the checkpoint from specified path.""" - assert not self._save_only - tf.logging.info('Load from checkpoint %s.', checkpoint_path) - self._saver.restore(sess, checkpoint_path) - tf.logging.info('Load checkpoint done.') - # Successfully restored from checkpoint. - uninitialized_var_names = self._GetUninitializedVarNames(sess) - assert not uninitialized_var_names, uninitialized_var_names - - def MaybeSave(self, sess, gsteps): - """If it's time to save, save the checkpoint. - - Args: - sess: tf.Session. - gsteps: Current global step. - Returns: - Whether a checkpoint was saved. - """ - now = time.time() - if now >= self._next_checkpoint_seconds: - self.Save(sess, gsteps) - self._next_checkpoint_seconds = now + self._save_interval_seconds - return True - return False - - def Save(self, sess, gsteps): - """Save the checkpoint. - - Args: - sess: tf.Session. - gsteps: Current global step. - """ - tf.logging.info('Save checkpoint') - path = self._saver.save(sess, self._save_path, gsteps) - tf.logging.info('Save checkpoint done: %s', path) - - def _RestoreFromLatestCheckpoint(self, sess): - assert not self._save_only - path = tf.train.latest_checkpoint(self._train_dir) - if path: - self.RestoreFromPath(sess, path) - return True - return False - - def _GetUninitializedVarNames(self, sess): - uninitialized_var_names = sorted(list(sess.run(self._uninitialized_vars))) - # uninitialized_var_names is a list of strings without ":0" suffix. - # tf.report_uninitialized_variables returns binary strings. - assert all(isinstance(s, bytes) for s in uninitialized_var_names) - return uninitialized_var_names - - def Restore(self, sess, force_reinitialize=False): - """Restore from latest checkpoint if available, or initialize.""" - # Try and restore from the latest checkpoint. - if self._RestoreFromLatestCheckpoint(sess): - # Successfully restored from checkpoint. - uninitialized_var_names = self._GetUninitializedVarNames(sess) - assert not uninitialized_var_names, uninitialized_var_names - return - - # Otherwise we need to initialize. - uninitialized_var_names = self._GetUninitializedVarNames(sess) - tf.logging.info('Uninitialized var list: %s', uninitialized_var_names) - if not force_reinitialize: - # There should only be uninitialized variables if all variables are - # uninitialized - with the exception of global_step due to - # RestoreGlobalStepIfNeeded in the _LoopEnqueue of TrainerTpu. - all_var_names = [ - six.ensure_binary(v.name[:-2]) for v in tf.global_variables() - ] - already_initialized_vars = ( - set(all_var_names) - set(uninitialized_var_names)) - already_initialized_vars.discard(b'global_step') - assert not already_initialized_vars, ('Already initialized vars: %s' % - sorted(already_initialized_vars)) - - # At this point all variables are uninitialized, so it is safe to run a - # global initializer. - sess.run(self._init_op) - tf.logging.info('Initialized all vars.') - - # TODO(b/160786085): Move this logic into Overriding vars logic itself, - # which requires refactoring things out of py_utils to avoid circular deps. - def _ResolveCkptPath(ckpt_rules): - return {GetSpecificCheckpoint(k): v for k, v in ckpt_rules.items()} - - # Restore specific variables based on init_from_checkpoint_rules. - for task in self._model.tasks: - tp = task.params.train - if tp.init_from_checkpoint_rules: - rules = _ResolveCkptPath(tp.init_from_checkpoint_rules) - tf.logging.info('OverrideVarsFromCheckpoints %s', rules) - py_utils.OverrideVarsFromCheckpoints(sess, tf.global_variables(), rules) - - if self._params.train.init_from_checkpoint_rules: - tp = self._params.train - rules = _ResolveCkptPath(tp.init_from_checkpoint_rules) - tf.logging.info('OverrideVarsFromCheckpoints %s', rules) - py_utils.OverrideVarsFromCheckpoints(sess, tf.global_variables(), rules) - - def RestoreIfNeeded(self, sess): - """If vars are not initialized, restore from checkpoint.""" - assert not self._save_only - uninitialized_var_names = self._GetUninitializedVarNames(sess) - if not uninitialized_var_names: - # All variables are already initialized. - return - - self.Restore(sess) - - def RestoreGlobalStepIfNeeded(self, sess): - """If global step is not initialized, load it from the checkpoint. - - Args: - sess: tf.Session. - """ - assert not self._save_only - uninitialized_vars = self._GetUninitializedVarNames(sess) - if six.ensure_binary('global_step') not in uninitialized_vars: - return - - with sess.graph.as_default(): - gstep = py_utils.GetGlobalStep() - - path = tf.train.latest_checkpoint(self._train_dir) - if path: - reader = tf.train.NewCheckpointReader(path) - value = reader.get_tensor('global_step') - tf.logging.info('Restoring global step: %s', value) - sess.run(gstep.assign(value)) - else: - tf.logging.info('Initializing global step') - sess.run(gstep.initializer) - - -def GetSpecificCheckpoint(load_checkpoint_from): - """Returns a specific checkpoint given `load_checkpoint_from`. - - When load_checkpoint_from is a directory, we find the latest - checkpoint in the directory and use that as the checkpoint - to evaluate. - - When load_checkpoint_from is a specific checkpoint, we - validate the path and return it. - - Args: - load_checkpoint_from: If not None, specifies the directory or specific - checkpoint to load. If a directory, the latest checkpoint in the - directory will be used. - """ - if not load_checkpoint_from: - return None - - # If load_checkpoint_from is a directory, return the latest - # checkpoint in the directory. - if tf.io.gfile.isdir(load_checkpoint_from): - return tf.train.latest_checkpoint(load_checkpoint_from) - - # We assume that load_checkpoint_from is a specific checkpoint to - # evaluate since it is not a directory. - # - # Check validity of eval path by looking for the index file. - if tf.io.gfile.exists(load_checkpoint_from + '.index'): - return load_checkpoint_from - - # Fail if we see an unexpected load_checkpoint_from. - # - # This might happen if load_checkpoint_from refers to a checkpoint - # but the index file cannot be found. - raise ValueError('Invalid load_checkpoint_from: %s' % load_checkpoint_from) diff --git a/lingvo/core/checkpointer_test.py b/lingvo/core/checkpointer_test.py deleted file mode 100644 index 11d65f5a..00000000 --- a/lingvo/core/checkpointer_test.py +++ /dev/null @@ -1,249 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for checkpointer.""" - -import os -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import checkpointer -from lingvo.core import py_utils -from lingvo.core import test_utils - - -class LinearModel(base_model.BaseTask): - """A basic linear model.""" - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'linear_model' - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w = py_utils.WeightParams( - shape=[3], - init=py_utils.WeightInit.Gaussian(scale=1.0, seed=123456), - dtype=p.dtype) - b = py_utils.WeightParams( - shape=[], - init=py_utils.WeightInit.Gaussian(scale=1.0, seed=234567), - dtype=p.dtype) - self.CreateVariable('w', w) - self.CreateVariable('b', b) - - -class CheckpointerTest(test_utils.TestCase): - - def testSaveRestore(self): - train_dir = os.path.join(self.get_temp_dir(), 'testSaveRestore') - os.mkdir(train_dir) - p = base_model.SingleTaskModel.Params(LinearModel.Params()) - p.input = base_input_generator.BaseInputGenerator.Params() - - final_global_step = 10 - expected_w = [0.38615, 2.975221, -0.852826] - initial_b = 1.418741 - final_b = 1234 - - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - self.evaluate(tf.global_variables_initializer()) - w, b = self.evaluate([model.GetTask().vars.w, model.GetTask().vars.b]) - self.assertAllClose(expected_w, w) - self.assertAlmostEqual(initial_b, b, places=5) - - saver = checkpointer.Checkpointer(train_dir, model) - self.evaluate( - tf.assign(py_utils.GetOrCreateGlobalStepVar(), final_global_step)) - self.evaluate(tf.assign(model.GetTask().vars.b, final_b)) - saver.Save(sess, model.global_step) - - w, b = self.evaluate([model.GetTask().vars.w, model.GetTask().vars.b]) - self.assertAllClose(expected_w, w) - self.assertEqual(final_b, b) - - self.assertTrue( - os.path.isfile( - os.path.join(train_dir, 'ckpt-%08d.index' % final_global_step))) - - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - saver = checkpointer.Checkpointer(train_dir, model) - saver.RestoreIfNeeded(sess) - - w, b, global_step = self.evaluate( - [model.GetTask().vars.w, - model.GetTask().vars.b, model.global_step]) - self.assertAllClose(expected_w, w) - self.assertEqual(final_b, b) - self.assertEqual(final_global_step, global_step) - - # Restore from checkpoint will always work, even though vars are already - # initialized. - saver.Restore(sess) - - def testRestoreWithGlobalStepAlreadyInitialized(self): - train_dir = os.path.join(self.get_temp_dir(), - 'testRestoreWithGlobalStepAlreadyInitialized') - os.mkdir(train_dir) - p = base_model.SingleTaskModel.Params(LinearModel.Params()) - p.input = base_input_generator.BaseInputGenerator.Params() - - with self.session(graph=tf.Graph()) as sess: - global_step = tf.compat.v1.train.get_or_create_global_step() - self.evaluate(tf.global_variables_initializer()) - - model = p.Instantiate() - saver = checkpointer.Checkpointer(train_dir, model) - - with self.assertRaises(tf.errors.FailedPreconditionError): - self.evaluate([model.GetTask().vars.w, model.GetTask().vars.b]) - - saver.RestoreIfNeeded(sess) - w, b, global_step = self.evaluate( - [model.GetTask().vars.w, - model.GetTask().vars.b, model.global_step]) - self.assertAllClose([0.38615, 2.975221, -0.852826], w) - self.assertAlmostEqual(1.418741, b, places=5) - self.assertEqual(0, global_step) - - self.assertFalse( - os.path.isfile(os.path.join(train_dir, 'ckpt-00000000.index'))) - - def testRestore(self): - train_dir = os.path.join(self.get_temp_dir(), 'testRestore') - os.mkdir(train_dir) - p = base_model.SingleTaskModel.Params(LinearModel.Params()) - p.input = base_input_generator.BaseInputGenerator.Params() - - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - saver = checkpointer.Checkpointer(train_dir, model) - - with self.assertRaises(tf.errors.FailedPreconditionError): - self.evaluate([model.GetTask().vars.w, model.GetTask().vars.b]) - - saver.RestoreIfNeeded(sess) - w, b, global_step = self.evaluate( - [model.GetTask().vars.w, - model.GetTask().vars.b, model.global_step]) - self.assertAllClose([0.38615, 2.975221, -0.852826], w) - self.assertAlmostEqual(1.418741, b, places=5) - self.assertEqual(0, global_step) - - with self.assertRaises(AssertionError): - # When initializing from scratch, variables are expected to not already - # be initialized. - saver.Restore(sess) - - # Unless force_reinitialize is used. - saver.Restore(sess, force_reinitialize=True) - - def testRestoreWithoutCheckpointInitializesVars(self): - train_dir = os.path.join(self.get_temp_dir(), - 'testRestoreWithoutCheckpointInitializesVars') - os.mkdir(train_dir) - p = base_model.SingleTaskModel.Params(LinearModel.Params()) - p.input = base_input_generator.BaseInputGenerator.Params() - - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - saver = checkpointer.Checkpointer(train_dir, model) - - with self.assertRaises(tf.errors.FailedPreconditionError): - self.evaluate([model.GetTask().vars.w, model.GetTask().vars.b]) - - saver.RestoreIfNeeded(sess) - w, b, global_step = self.evaluate( - [model.GetTask().vars.w, - model.GetTask().vars.b, model.global_step]) - self.assertAllClose([0.38615, 2.975221, -0.852826], w) - self.assertAlmostEqual(1.418741, b, places=5) - self.assertEqual(0, global_step) - - self.assertFalse( - os.path.isfile(os.path.join(train_dir, 'ckpt-00000000.index'))) - - def testInitRulesDirectory(self): - train_dir = os.path.join(self.get_temp_dir(), 'testInitRulesDirectory') - os.mkdir(train_dir) - p = base_model.SingleTaskModel.Params(LinearModel.Params()) - p.input = base_input_generator.BaseInputGenerator.Params() - b1 = 1234 - g1 = 10 - b2 = 12345 - g2 = 100 - - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - self.evaluate(tf.global_variables_initializer()) - saver = checkpointer.Checkpointer(train_dir, model) - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), g1)) - self.evaluate(tf.assign(model.GetTask().vars.b, b1)) - saver.Save(sess, model.global_step) - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), g2)) - self.evaluate(tf.assign(model.GetTask().vars.b, b2)) - saver.Save(sess, model.global_step) - - train_dir_2 = os.path.join(self.get_temp_dir(), 'testInitRulesDirectory_2') - - # Set init_checkpoint_rules to only restore b from a specific ckpt - # the first one, not the latest one. - rules = [('(.*)', '%s')] - spec_dir = os.path.join(train_dir, 'ckpt-00000010') - p.train.init_from_checkpoint_rules = {spec_dir: (rules, [])} - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - saver = checkpointer.Checkpointer(train_dir_2, model) - saver.RestoreIfNeeded(sess) - new_b = self.evaluate(model.GetTask().vars.b) - self.assertEqual(b1, new_b) - - # Set init_checkpoint_rules to restore all from the latest checkpoint - # by specifying just the original train directory, not a specific - # checkpoint. - rules = [('(.*)', '%s')] - p.train.init_from_checkpoint_rules = {train_dir: (rules, [])} - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - saver = checkpointer.Checkpointer(train_dir_2, model) - saver.RestoreIfNeeded(sess) - new_b = self.evaluate(model.GetTask().vars.b) - self.assertEqual(b2, new_b) - - def testSaveOnly(self): - train_dir = os.path.join(self.get_temp_dir(), 'testSaveOnly') - os.mkdir(train_dir) - p = base_model.SingleTaskModel.Params(LinearModel.Params()) - p.input = base_input_generator.BaseInputGenerator.Params() - - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - self.evaluate(tf.global_variables_initializer()) - saver = checkpointer.Checkpointer(train_dir, model, save_only=True) - saver.Save(sess, model.global_step) - with self.assertRaises(AssertionError): - saver.RestoreIfNeeded(sess) - - self.assertTrue( - os.path.isfile(os.path.join(train_dir, 'ckpt-00000000.index'))) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/cluster.py b/lingvo/core/cluster.py deleted file mode 100644 index 4718a2ac..00000000 --- a/lingvo/core/cluster.py +++ /dev/null @@ -1,549 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Specification of a training cluster.""" - -import heapq -import lingvo.compat as tf -from lingvo.core import hyperparams -from lingvo.core import py_utils -import numpy as np - -FLAGS = tf.flags.FLAGS - -_CLUSTER_STACK = py_utils.ThreadLocalStack() - - -class _Cluster: - """The whole training cluster from a single task's point of view.""" - - @classmethod - def _JobSpec(cls, replicas): - """Construct a job spec param with the given number of replicas.""" - p = hyperparams.Params() - # By default, we use /job:localhost so that most of tests can just - # work out of the box. trainer.py will then set job names accordingly. - # Is this being redefined? - p.Define('name', '/job:localhost', - 'TensorFlow job spec, e.g., /job:trainer, /job:ps') - p.Define('replicas', replicas, 'The number of tasks of a job.') - p.Define( - 'targets', '', 'The target network address(es) to which we can ' - 'create tf sessions. E.g., a single ip:port, or a list of ' - 'comma-separated grpc://ip:port, etc.') - p.Define('cpus_per_replica', 1, 'The number of CPU devices to use per ' - 'replica.') - p.Define('gpus_per_replica', 0, 'The number of GPU devices to use per ' - 'replica.') - p.Define( - 'devices_per_split', 1, 'Devices of a replica are grouped into ' - 'splits. Each split contains these many devices. One split is a ' - 'group of devices on which the computation nodes of a graph is ' - 'placed upon.E.g., one can place the forward lstm on device 0 of ' - 'a split and place the backward lstm on device 1. etc.') - p.Define('tpus_per_replica', 0, - 'The number of tpu cores to use per replica.') - p.Define('num_tpu_hosts', 0, 'The number of tpu hosts.') - return p - - @classmethod - def Params(cls): - """Defaults parameters for a cluster.""" - p = hyperparams.InstantiableParams(cls) - p.Define( - 'mode', 'async', 'A string noting the overall training method. ' - 'Valid values: sync, async.') - p.Define( - 'job', 'trainer', 'The role of this job in the training cluster. ' - 'E.g., trainer_client, trainer, controller, etc.') - p.Define('task', 0, 'This process is the task-th task in the job.') - p.Define('logdir', '', 'The log directory.') - - # Helpful - # How the cluster is composed. - # - # A typical training cluster has a few jobs (controller, worker, ps, etc). - # One can potentially place computation on any device of these jobs. - # Here, we specify how each job is configured. E.g., number of GPUs each - # task is equipped with, the number of replicas, etc. - # - # Note that trainer client may dispatch operations on just a - # smaller subset of jobs. For example, the controller only places - # computations onto the controller and ps devices; while evaler - # only places computations on the evaler devices. - # - # cluster.job refers to the role of a client process performs. It - # can be 'controller', 'trainer', 'trainer_client', 'evaler' and - # 'decoder', etc. Often, a client can be the same process as one - # of the compute devices (e.g., controller). Sometimes, they can - # be a separate processes. E.g., trainer_client is a separate - # standalone process. It places computations on the worker and - # ps devices, while itself does not host any. - p.Define('controller', cls._JobSpec(1), 'The controller job.') - p.Define('worker', cls._JobSpec(1), 'The worker job.') - p.Define('ps', cls._JobSpec(1), 'The ps job.') - p.Define('input', cls._JobSpec(0), 'The input job.') - p.Define('evaler', cls._JobSpec(0), 'The evaler job.') - p.Define('decoder', cls._JobSpec(0), 'The decoder job.') - - # A few 'global' knobs. - p.Define( - 'add_summary', None, 'Whether to add summaries. If None, ' - 'decides based on the job type.') - p.Define('do_eval', None, 'Whether to do eval.') - p.Define('split_id', 0, 'Split id for the model.') - p.Define('immediately_create_variables', True, - 'Whether to create variables immediately.') - return p - - @classmethod - def _MakeDeviceString(cls, job_name, task_id, device_name, device_id): - del cls - return '%s/replica:0/task:%d/device:%s:%d' % (job_name, task_id, - device_name, device_id) - - @classmethod - def ListDevices(cls, job_spec): - """Lists devices in the job. - - Args: - job_spec: A param object specifying a job in a training cluster. - - Returns: - Returns a 2D np string array. ret[i, j] is the i-th replica's j-th - devices. - """ - if job_spec.tpus_per_replica: - tpus = job_spec.tpus_per_replica - # Unfortunately, decoder replicas is 0. I need to fix this. - ret = np.empty((job_spec.replicas, tpus), np.object) - for i in range(job_spec.replicas): - for j in range(tpus): - ret[i, j] = cls._MakeDeviceString(job_spec.name, i, 'TPU', j) - elif not job_spec.gpus_per_replica: - cpus = job_spec.cpus_per_replica - ret = np.empty((job_spec.replicas, cpus), np.object) - for i in range(job_spec.replicas): - for j in range(cpus): - ret[i, j] = cls._MakeDeviceString(job_spec.name, i, 'CPU', j) - else: - ret = np.empty((job_spec.replicas, job_spec.gpus_per_replica), np.object) - for i in range(job_spec.replicas): - for j in range(job_spec.gpus_per_replica): - ret[i, j] = cls._MakeDeviceString(job_spec.name, i, 'GPU', j) - return ret - - def __enter__(self): - _CLUSTER_STACK.stack.append(self) - return self - - def __exit__(self, type_arg, value_arg, traceback_arg): - assert _CLUSTER_STACK.stack - assert _CLUSTER_STACK.stack[-1] is self - _CLUSTER_STACK.stack.pop() - - @staticmethod - def Top(): - return _CLUSTER_STACK.stack[-1] if _CLUSTER_STACK.stack else None - - def __init__(self, params): - self._params = params.Copy() - p = self.params - - print("GALVEZ:params=", str(params)) - print("GALVEZ:task_id=", params.task) - import sys; sys.stdout.flush() - - # A set of invariants about the setup of the cluster. - # - # NOTE. Two job specs can be identical. E.g., if p.worker.name is - # the same as p.ps.name, that means ps is colocated with worker. - assert p.ps.replicas >= 0 - assert p.ps.gpus_per_replica >= 0 - if p.mode == 'async' and p.job == 'controller': - # There is only 1 controller. - assert p.controller.replicas == 1 - assert p.task == 0 - elif p.mode == 'async' and p.job == 'trainer': - assert p.worker.replicas >= 1 - assert p.worker.gpus_per_replica >= 0 - assert p.worker.devices_per_split >= 1 - # In async mode, trainers colocate with workers. - assert 0 <= p.task and p.task < p.worker.replicas - if p.ps.replicas == 0: - # There is no ps. We are doing single-replica training. - assert p.worker.replicas == 1 - elif p.mode == 'async' and p.job == 'evaler': - assert 0 <= p.task and p.task < p.evaler.replicas - elif p.mode == 'async' and p.job == 'decoder': - assert 0 <= p.task and p.task < p.decoder.replicas - elif p.mode == 'sync' and p.job == 'controller': - # There is only 1 controller. - assert p.controller.replicas == 1 - assert p.task == 0 - elif p.mode == 'sync' and p.job == 'trainer_client': - assert p.worker.replicas >= 1 - assert p.worker.gpus_per_replica >= 0 - assert p.worker.devices_per_split >= 1 - elif p.mode == 'sync' and p.job == 'evaler': - assert 0 <= p.task and p.task < p.evaler.replicas - elif p.mode == 'sync' and p.job == 'decoder': - assert 0 <= p.task and p.task < p.decoder.replicas - elif p.mode == 'sync' and p.job == 'executor_tpu': - assert p.worker.replicas >= 1 - elif p.mode == 'sync' and p.job == 'tpu_decoder': - # from IPython import embed; embed() - # assert p.decoder.replicas == 1 - pass - else: - assert False, (p.mode, p.job) - - if p.job == 'controller': - self._job_spec = p.controller - elif p.job in ('trainer', 'worker', 'trainer_client'): - self._job_spec = p.worker - elif p.job == 'evaler': - self._job_spec = p.evaler - elif p.job == 'decoder': - self._job_spec = p.decoder - elif p.job == 'executor_tpu': - self._job_spec = p.worker - elif p.job == 'tpu_decoder': - # Not quite sure of this is right - self._job_spec = p.decoder - else: - assert False, (p.mode, p.job) - - @property - def params(self): - return self._params - - @property - def mode(self): - return self.params.mode - - @property - def job(self): - return self.params.job - - @property - def logdir(self): - return self.params.logdir - - @property - def task(self): - return self.params.task - - @property - def job_spec(self): - return self._job_spec - - @property - def asynchronous(self): - """Returns True if configured for asynchronous training.""" - return self.params.mode == 'async' - - @property - def synchronous(self): - """Returns True if configured for synchronous training.""" - return self.params.mode == 'sync' - - @property - def num_replicas(self): - return self._job_spec.replicas - - @property - def tpus_per_replica(self): - return self._job_spec.tpus_per_replica - - @property - def num_tpu_hosts(self): - return self._job_spec.num_tpu_hosts - - @property - def num_devices_per_replica(self): - return (self._job_spec.gpus_per_replica or - self._job_spec.tpus_per_replica or self._job_spec.cpus_per_replica) - - @property - def total_worker_devices(self): - """Return the total number of discrete worker devices in the cluster.""" - worker_spec = self.params.worker - devices_per_replica = ( - worker_spec.gpus_per_replica or worker_spec.tpus_per_replica or - self._job_spec.cpus_per_replica) - num_replicas = worker_spec.replicas - return devices_per_replica * num_replicas - - @property - def num_devices_per_split(self): - """Return number of accelerators to use per split.""" - return self._job_spec.devices_per_split - - @property - def num_splits_per_replica(self): - # Note that a split must be within a replica. - assert self.num_devices_per_replica % self.num_devices_per_split == 0 - return int(self.num_devices_per_replica / self.num_devices_per_split) - - @property - def num_splits_per_client(self): - """The number of splits visible by one trainer client.""" - if self.synchronous and self.job == 'trainer_client': - # One client drives all the workers. - return self.num_splits_per_replica * self.num_replicas - elif self.synchronous and self.job == 'executor_tpu': - # One client drives all the workers. - return self.num_splits_per_replica * self.num_replicas - else: - # One client colocates with one worker and drives the worker only. - return self.num_splits_per_replica - - @property - def available_devices(self): - """Returns all compute devices available in a 2D array. - - Returns: - A 2D array (python list of python lists) of strings. ret[i, j] - is the j-th visible device on i-th visible replica. - """ - if self._job_spec.tpus_per_replica: - ret = np.empty((1, self.num_devices_per_split), np.object) - for i in range(self.num_devices_per_split): - ret[0, i] = tf.tpu.core(i) - return ret - - if self.job == 'trainer' and self.asynchronous: - # In async mode, each trainer task can only use its own devices. - return self.ListDevices(self._job_spec)[self.task:(self.task + 1), :] - - if self.job == 'trainer_client' and self.synchronous: - # In sync mode, trainer_client can use every device. - return self.ListDevices(self._job_spec) - - if self.job == 'executor_tpu' and self.synchronous: - # executor_tpu can use every device. - return self.ListDevices(self._job_spec) - - if self.job in ('controller', 'evaler', 'decoder', 'tpu_decoder'): - # Our current policy is that each controller/evaler/decoder task - # only uses 1 replica. - - # Okay, so what is replica? Am I stuck using just one TPU for - # decoding then? No, I don't think so. - return self.ListDevices(self._job_spec)[self.task:(self.task + 1), :] - - assert False, (self.job, self.mode) - - @property - def input_device(self): - """Returns the tensorflow device name to place input op on.""" - p = self.params - if self.synchronous and p.input.replicas > 0: - # Uses a separate job for input processing. - assert p.input.replicas == 1 - return self.ListDevices(p.input)[0, 0] - else: - return '' - - def PlaceInput(self, input_params): - """Applies a placement policy on the given input generator params. - - By default, the policy is to place the input generator onto the input - device. Subclass can override PlaceInput method to implement more advanced - placement policy. - - Args: - input_params: An input generator params. - - Returns: - An input params which places the input generator on the input device. - """ - - class _UseInputDevice(input_params.cls): - """Places the input generator on the input device.""" - - def __init__(self, params): - with tf.device(self.cluster.input_device): - super().__init__(params) - - def SplitInputBatch(self, num_splits): - with tf.device(self.cluster.input_device): - return super().SplitInputBatch(num_splits) - - return input_params.Copy().Set(cls=_UseInputDevice) - - @property - def input_targets(self): - """Returns a list of network addresses of the input job.""" - p = self.params.input - if not p.targets: - return [] - targets = p.targets.split(',') - assert p.replicas == len(targets), '{} vs. {}'.format(p.replicas, targets) - return targets - - def WorkerDeviceInModelSplit(self, device_index): - """Returns the device to use for 'device_index' for the current model split. - - Args: - device_index: An int, the device index within 'model_split'. - - Returns: - A string. The device to place ops onto. - - Raises: - ValueError: if split_id of cluster is incorrectly set. - """ - devices = self.available_devices.reshape([-1]).tolist() - if not devices: - return '' - else: - split_id = self.params.split_id - if split_id < 0 or split_id >= self.num_splits_per_client: - raise ValueError('split_id (%d) not in [0, %d)' % - (split_id, self.num_splits_per_client)) - devices_per_split = self.num_devices_per_split - return devices[devices_per_split * split_id + - device_index % devices_per_split] - - def GetPlacer(self, strategy=None): - """Returns a device function for placing ops within the cluster. - - Args: - strategy: A string. Identifier for a placement strategy. By default, we - use a least loaded policy to place variables. - - Returns: - Returns a device function can be used in tf.device(). - - Raises: - ValueError: when strategy is not supported. - """ - if self.job == 'evaler': # or self.job == 'decoder': - # Currently, we only support evaler/decoder uses 1 accelerator. - return self.ListDevices(self.job_spec)[self.task, 0] - elif strategy is None: - return _LeastLoadedPlacer(self).DeviceFunction - raise ValueError('Unsupported placement policy: ', strategy) - - @property - def add_summary(self): - p = self.params - if p.add_summary is None: - return self.job in ['controller', 'decoder'] - else: - return p.add_summary - - @property - def do_eval(self): - return self.params.do_eval - - @property - def immediately_create_variables(self): - return self.params.immediately_create_variables - - @property - def worker_cluster_def(self): - """Returns a tf.train.ClusterDef representing the worker cluster.""" - p = self.params.worker - - if not p.targets: - return None - - job = p.name.replace('/job:', '', 1) - workers = [addr.replace('grpc://', '', 1) for addr in p.targets.split(',')] - - return tf.train.ClusterSpec({job: workers}).as_cluster_def() - - -# Ops that must be placed on the 'ps' devices. -_VAR_OPS = ['Variable', 'VariableV2', 'AutoReloadVariable', 'VarHandleOp'] - - -class VarPlacer: - """Placer which places variables across a set of devices. - - VarPlacer places non-variable ops on the worker device. - """ - - def __init__(self, cluster): - self._cluster = cluster - self._devices = cluster.ListDevices(cluster.job_spec) - - def _AssignVar(self, _): - raise ValueError('Unimplemented') - - def DeviceFunction(self, op): - """Choose a device for 'op'. - - Args: - op: an Operation. - - Returns: - The device to use for the Operation. - """ - # Op has already assigned to a device explicitly. Don't change it. - if op.device: - return op.device - - # Place vars according our policy. - if op.type in _VAR_OPS: - return self._AssignVar(op) - - # The default policy is to place the op on the 1st device visible - # to this task. - assert self._devices is not None, ('Unexpected job: %s' % self._cluster.job) - # You know, I wonder if this is exactly the cuase of the problem Anjali and I encountered. This DeivceFunction may have misplaced a the AsciiToId operation on TPU... - # print("GALVEZ:", self._devices) - # print("GALVEZ:", len(self._devices)) - # import sys; sys.stdout.flush() - task = self._cluster.params.task - assert 0 <= task and task < len(self._devices) - return self._devices[task, 0] - - -class _LeastLoadedPlacer(VarPlacer): - """Placer which places a variable on the least loaded var device. - - We use total byte sizes of variables placed on a device to indicate - the device's load. - - """ - - def __init__(self, cluster): - super().__init__(cluster) - # A min heap of (size, device) - var_devices = cluster.ListDevices(cluster.params.ps).flatten().tolist() - tf.logging.info('_LeastLoadedPlacer : %s', var_devices) - self._var_space_pq = [(0, d) for d in var_devices] - - def _AssignVar(self, var_op): - size = var_op.get_attr('dtype').size - shape = tf.TensorShape(var_op.get_attr('shape')) - assert self._var_space_pq, ('No ps devices to use.') - allocated, device = heapq.heappop(self._var_space_pq) - if shape.num_elements() is None: - assert var_op.name.endswith( - 'wb/var'), 'Unexpected name pattern: %s' % var_op.name - # CuDNN RNN vars shape aren't known statically, decide to make a constant - # estimate to avoid introducing more complexities. - allocated += 10 * 1024**2 * size - else: - allocated += shape.num_elements() * size - heapq.heappush(self._var_space_pq, (allocated, device)) - tf.logging.info('Place variable %s on %s %d', var_op.name, device, - allocated) - return device diff --git a/lingvo/core/cluster_factory.py b/lingvo/core/cluster_factory.py deleted file mode 100644 index 97be958b..00000000 --- a/lingvo/core/cluster_factory.py +++ /dev/null @@ -1,129 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Cluster factory.""" - -from lingvo.core import cluster - -Cluster = cluster._Cluster # pylint: disable=protected-access - - -def SetCluster(cls): - """Sets Cluster implementation.""" - global Cluster # pylint: disable=invalid-name - Cluster = cls - - -def Current(): - """Returns the current cluster specification. - - E.g.:: - - with Cluster(cluster_params) as foo: - model = p.Instantiate() - model.FProp() # FProp can access foo through cluster_factory.Current(). - """ - current = Cluster.Top() - if not current: - current = Cluster(Cluster.Params()) - return current - - -def ForTestingWorker(mode=None, - job=None, - gpus=None, - split_size=None, - tpus=None, - add_summary=None, - cpus=None, - do_eval=None, - num_tpu_hosts=None): - """Returns a Cluster for unittesting with a worker.""" - p = Current().params.Copy() - if mode is not None: - p.mode = mode - if job is not None: - p.job = job - if gpus is not None: - p.worker.gpus_per_replica = gpus - if tpus is not None: - p.worker.tpus_per_replica = tpus - p.worker.num_tpu_hosts = 1 - if cpus is not None: - p.worker.cpus_per_replica = cpus - if split_size is not None: - p.worker.devices_per_split = split_size - if add_summary is not None: - p.add_summary = add_summary - if do_eval is not None: - p.do_eval = do_eval - if num_tpu_hosts is not None: - p.worker.num_tpu_hosts = num_tpu_hosts - return p.Instantiate() - - -def SetEval(mode): - """Returns a cluster with do_eval option turned on/off. - - E.g.:: - - def FProp(...): - with SetEval(mode=True): - # Turns off dropout, noise, etc. - y = self.foo.FProp(..., x) - z = self.bar.FProp(..., y) - # Returns to previous state (e.g., training). - y = self.foo.FProp(..., x) - z = self.foo.FProp(..., y) - - Args: - mode: True, False or None. - - Returns: - A new Cluster instance. - """ - return Current().params.Copy().Set(do_eval=mode).Instantiate() - - -def SetImmediatelyInstantiateVariables(mode): - """Returns a cluster with immediately_create_variables option turned on/off. - - Args: - mode: True, False or None. - - Returns: - A new Cluster instance. - """ - return Current().params.Copy().Set( - immediately_create_variables=mode).Instantiate() - - -def SetModelSplit(split_id): - """Returns the current cluster with the model split id set. - - E.g.:: - - def FProp(...): - with cluster_factory.SetModelSplit(1) as c: - with tf.device(c.WorkerDeviceInModelSplit(0)): - ... - - Args: - split_id: Integer split id for the model. - - Returns: - A new Cluster instance. - """ - return Current().params.Copy().Set(split_id=split_id).Instantiate() diff --git a/lingvo/core/cluster_test.py b/lingvo/core/cluster_test.py deleted file mode 100644 index 7530d1a7..00000000 --- a/lingvo/core/cluster_test.py +++ /dev/null @@ -1,440 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for cluster.""" - -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import test_utils -import numpy as np - - -class ClusterTest(test_utils.TestCase): - - def testDefaultParams(self): - p = cluster_factory.Cluster.Params() - c = cluster_factory.Cluster(p) - self.assertFalse(c.add_summary) - g = tf.Graph() - vs = [] - with g.as_default(): - with tf.device(c.GetPlacer()): - for i in range(10): - vs.append(tf.get_variable('x%d' % i, (10, 10, 10))) - sum_all = tf.add_n(vs) - for v in vs: - self.assertEqual( - v.device, - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='CPU', - device_id=0)) - self.assertEqual( - sum_all.device, - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='CPU', - device_id=0)) - - def testDefaultParamsWithDynamicShape(self): - p = cluster_factory.Cluster.Params() - c = cluster_factory.Cluster(p) - g = tf.Graph() - vs = [] - with g.as_default(): - with tf.device(c.GetPlacer()): - for i in range(10): - dyn_shape = tf.constant([2], dtype=tf.int32) - dyn_shape = tf.placeholder_with_default(dyn_shape, shape=[None]) - v = tf.get_variable( - 'x%d_wb/var' % i, - initializer=tf.random.uniform(dyn_shape, dtype=tf.float64), - validate_shape=False) - vs.append(v) - sum_all = tf.add_n(vs) - for v in vs: - self.assertEqual( - v.device, - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='CPU', - device_id=0)) - self.assertEqual( - sum_all.device, - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='CPU', - device_id=0)) - - def testNoPS(self): - p = cluster_factory.Cluster.Params() - p.worker.name = '/job:trainer' - p.worker.replicas = 1 - p.ps.name = '/job:trainer' - p.ps.replicas = 1 - c = cluster_factory.Cluster(p) - g = tf.Graph() - vs = [] - with g.as_default(): - with tf.device(c.GetPlacer()): - for i in range(10): - vs.append(tf.get_variable('x%d' % i, (10, 10, 10))) - sum_all = tf.add_n(vs) - for v in vs: - self.assertEqual( - v.device, - c._MakeDeviceString( - job_name='/job:trainer', - task_id=0, - device_name='CPU', - device_id=0)) - self.assertEqual( - sum_all.device, - c._MakeDeviceString( - job_name='/job:trainer', task_id=0, device_name='CPU', device_id=0)) - - def testNoPSWithGPUs(self): - p = cluster_factory.Cluster.Params() - p.worker.name = '/job:trainer' - p.worker.replicas = 1 - p.worker.gpus_per_replica = 4 - p.ps.name = '/job:trainer' - p.ps.replicas = 1 - p.ps.gpus_per_replica = 4 - - c = cluster_factory.Cluster(p) - g = tf.Graph() - vs = [] - with g.as_default(): - with tf.device(c.GetPlacer()): - for i in range(10): - vs.append(tf.get_variable('x%d' % i, (10, 10, 10))) - sum_all = tf.add_n(vs) - for i, v in enumerate(vs): - self.assertEqual( - v.device, - c._MakeDeviceString( - job_name='/job:trainer', - task_id=0, - device_name='GPU', - device_id=i % 4)) - self.assertEqual( - sum_all.device, - c._MakeDeviceString( - job_name='/job:trainer', - task_id=0, - device_name='GPU', - device_id=0)) - - def testPS(self): - p = cluster_factory.Cluster.Params() - p.worker.name = '/job:trainer' - p.worker.replicas = 1 - p.ps.name = '/job:ps' - p.ps.replicas = 4 - c = cluster_factory.Cluster(p) - g = tf.Graph() - vs = [] - with g.as_default(): - with tf.device(c.GetPlacer()): - for i in range(10): - vs.append(tf.get_variable('x%d' % i, (10, 10, 10))) - sum_all = tf.add_n(vs) - for i, v in enumerate(vs): - self.assertEqual( - v.device, - c._MakeDeviceString( - job_name='/job:ps', task_id=i % 4, device_name='CPU', - device_id=0)) - self.assertEqual( - sum_all.device, - c._MakeDeviceString( - job_name='/job:trainer', task_id=0, device_name='CPU', device_id=0)) - - def testPSWithGPUs(self): - p = cluster_factory.Cluster.Params() - p.worker.name = '/job:trainer' - p.worker.replicas = 1 - p.ps.name = '/job:ps' - p.ps.replicas = 4 - p.ps.gpus_per_replica = 2 - c = cluster_factory.Cluster(p) - g = tf.Graph() - vs = [] - with g.as_default(): - with tf.device(c.GetPlacer()): - for i in range(10): - vs.append(tf.get_variable('x%d' % i, (10, 10, 10))) - sum_all = tf.add_n(vs) - for i, v in enumerate(vs): - self.assertEqual( - v.device, - c._MakeDeviceString( - job_name='/job:ps', - task_id=(i / 2) % 4, - device_name='GPU', - device_id=i % 2)) - self.assertEqual( - sum_all.device, - c._MakeDeviceString( - job_name='/job:trainer', task_id=0, device_name='CPU', device_id=0)) - - def testPSRandomSize(self): - p = cluster_factory.Cluster.Params() - p.worker.name = '/job:trainer' - p.ps.name = '/job:ps' - p.ps.replicas = 10 - c = cluster_factory.Cluster(p) - g = tf.Graph() - vs = [] - np.random.seed(301) - with g.as_default(): - with tf.device(c.GetPlacer()): - # Creates 200 variables with different sizes. - for i in range(200): - if i % 13: - size = np.random.randint(10000) - elif i % 7: - size = np.random.randint(100) - else: - size = np.random.randint(10) - vs.append(tf.get_variable('x%d' % i, shape=(size))) - sum_all = tf.add_n([tf.reduce_sum(x) for x in vs]) - # Computes the total size of variables placed on each device. - total_size = {} # device name -> size - for v in vs: - size = tf.TensorShape(v.op.get_attr('shape')).num_elements() - if v.device in total_size: - total_size[v.device] += size - else: - total_size[v.device] = size - for (device, allocated) in zip( - sorted(total_size), - [91701, 91361, 90346, 88738, 87240, 89265, 91944, 92472, 88051, 95053]): - self.assertEqual(total_size[device], allocated) - self.assertEqual( - sum_all.device, - c._MakeDeviceString( - job_name='/job:trainer', task_id=0, device_name='CPU', device_id=0)) - - def testDeviceListOneRepliaCpu(self): - p = cluster_factory.Cluster.Params() - p.mode = 'async' - p.job = 'trainer' - p.worker.cpus_per_replica = 2 - c = cluster_factory.Cluster(p) - cpu_devices = c.available_devices - expected_cpu_devices = [[ - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='CPU', - device_id=0), - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='CPU', - device_id=1), - ]] - print(expected_cpu_devices) - self.assertAllEqual(cpu_devices, expected_cpu_devices) - - def testDeviceListOneReplicaGpu(self): - p = cluster_factory.Cluster.Params() - p.mode = 'async' - p.job = 'trainer' - p.worker.gpus_per_replica = 2 - c = cluster_factory.Cluster(p) - gpu_devices = c.available_devices - expected_gpu_devices = [[ - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='GPU', - device_id=0), - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='GPU', - device_id=1), - ]] - self.assertAllEqual(gpu_devices, expected_gpu_devices) - - def testDeviceListMultiReplicaNoSyncSgd(self): - p = cluster_factory.Cluster.Params() - p.mode = 'async' - p.job = 'trainer' - p.task = 1 - p.worker.replicas = 2 - p.worker.gpus_per_replica = 2 - c = cluster_factory.Cluster(p) - gpu_devices = c.available_devices - expected_gpu_devices = [[ - c._MakeDeviceString( - job_name='/job:localhost', - task_id=1, - device_name='GPU', - device_id=0), - c._MakeDeviceString( - job_name='/job:localhost', - task_id=1, - device_name='GPU', - device_id=1), - ]] - self.assertAllEqual(gpu_devices, expected_gpu_devices) - - # Compute the total number of worker devices for a multi - # replica setup. - self.assertEqual(4, c.total_worker_devices) - - # Even when the job is different, we still look at the worker - # information. - p.job = 'controller' - p.task = 0 - c = cluster_factory.Cluster(p) - self.assertEqual(4, c.total_worker_devices) - - def testDeviceListMultiReplicaSyncSgd(self): - p = cluster_factory.Cluster.Params() - p.mode = 'sync' - p.job = 'trainer_client' - p.worker.name = '/job:localhost' - p.worker.replicas = 2 - p.worker.gpus_per_replica = 2 - c = cluster_factory.Cluster(p) - gpu_devices = c.available_devices - expected_gpu_devices = [[ - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='GPU', - device_id=0), - c._MakeDeviceString( - job_name='/job:localhost', - task_id=0, - device_name='GPU', - device_id=1), - ], [ - c._MakeDeviceString( - job_name='/job:localhost', - task_id=1, - device_name='GPU', - device_id=0), - c._MakeDeviceString( - job_name='/job:localhost', - task_id=1, - device_name='GPU', - device_id=1), - ]] - self.assertAllEqual(gpu_devices, expected_gpu_devices) - - def testInputDevice(self): - p = cluster_factory.Cluster.Params() - p.mode = 'sync' - p.job = 'decoder' - p.decoder.replicas = 1 - p.task = 0 - p.input.name = '/job:input' - p.input.replicas = 1 - c = cluster_factory.Cluster(p) - input_device = c.input_device - expected_device = c._MakeDeviceString( - job_name='/job:input', task_id=0, device_name='CPU', device_id=0) - self.assertEqual(input_device, expected_device) - - def testModelSplit(self): - p = cluster_factory.Cluster.Params() - with p.Instantiate() as c: - with cluster_factory.SetModelSplit(2) as c1: - self.assertEqual(c1.params.split_id, 2) - with cluster_factory.SetModelSplit(3) as c2: - self.assertEqual(c2.params.split_id, 3) - self.assertEqual(c.params.split_id, 0) - - def testInputTargets(self): - p = cluster_factory.Cluster.Params() - p.input.name = '/job:input' - p.input.replicas = 2 - p.input.targets = '10.100.1.1:10001,10.100.1.2:10002' - c = cluster_factory.Cluster(p) - self.assertEqual(c.input_targets, ['10.100.1.1:10001', '10.100.1.2:10002']) - - def testWorkerDeviceInModelSplitSync(self): - p = cluster_factory.Cluster.Params() - p.mode = 'sync' - p.job = 'trainer_client' - p.worker.name = '/job:trainer' - p.worker.replicas = 4 - p.worker.gpus_per_replica = 4 - p.worker.devices_per_split = 2 - with cluster_factory.Cluster(p): - with cluster_factory.SetModelSplit(1) as c: - d = c.WorkerDeviceInModelSplit(1) - expected_device = c._MakeDeviceString( - job_name='/job:trainer', task_id=0, device_name='GPU', device_id=3) - self.assertEqual(expected_device, d) - - def testWorkerDeviceInModelSplit(self): - p = cluster_factory.Cluster.Params() - p.mode = 'async' - p.job = 'trainer' - p.task = 3 - p.worker.name = '/job:trainer' - p.worker.replicas = 4 - p.worker.gpus_per_replica = 4 - p.worker.devices_per_split = 2 - with cluster_factory.Cluster(p): - with cluster_factory.SetModelSplit(1) as c: - d = c.WorkerDeviceInModelSplit(1) - expected_device = c._MakeDeviceString( - job_name='/job:trainer', task_id=3, device_name='GPU', device_id=3) - self.assertEqual(expected_device, d) - - def testWorkerClusterDef(self): - p = cluster_factory.Cluster.Params() - - p.worker.name = '/job:trainer_client' - p.worker.targets = ','.join([ - 'grpc://localhost:8470', - 'grpc://localhost:8471', - 'grpc://localhost:8472', - 'grpc://localhost:8473', - ]) - - cluster_def = tf.train.ClusterSpec({ - 'trainer_client': [ - 'localhost:8470', 'localhost:8471', 'localhost:8472', - 'localhost:8473' - ] - }).as_cluster_def() - - cluster = p.Instantiate() - self.assertEqual(cluster.worker_cluster_def, cluster_def) - - def testWorkerClusterDefWithoutTargets(self): - p = cluster_factory.Cluster.Params() - - p.worker.name = '/job:trainer_client' - - cluster = p.Instantiate() - self.assertIsNone(cluster.worker_cluster_def) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/computation_cost.py b/lingvo/core/computation_cost.py deleted file mode 100644 index d1b54482..00000000 --- a/lingvo/core/computation_cost.py +++ /dev/null @@ -1,73 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Utilities to estimate computation costs of layers.""" - -from lingvo import compat as tf -from lingvo.core import bn_layers # for AddingAccumulator - -COST_METRICS = { - 'flops': tf.int64, -} - - -def Prepare(layer): - """Registers cost accumulators for layer and each of its child layers. - - This function should be called before FProp(). - - Args: - layer: the root layer. - """ - - def _Traverse(layer): - """Adds accumulators to layer and its descendant layers.""" - if isinstance(layer, (list, tuple)): - for layer_i in layer: - _Traverse(layer_i) - return - with tf.name_scope(layer.params.name): - for cost_metric_name in COST_METRICS: - dtype = COST_METRICS[cost_metric_name] - layer.RegisterAccumulator( - cost_metric_name, - bn_layers.AddingAccumulator(shape=[], dtype=dtype)) - for _, child in sorted(layer.children.items()): - _Traverse(child) - - _Traverse(layer) - - -def _HasAccumulator(layer, cost_metric_name): - assert cost_metric_name in COST_METRICS - return cost_metric_name in layer.accumulators - - -def _GetAccumulator(layer, cost_metric_name): - assert cost_metric_name in COST_METRICS - if cost_metric_name not in layer.accumulators: - raise ValueError('Prepare was not called for %s' % layer.path) - return layer.accumulators[cost_metric_name] - - -def Add(layer, cost_metric_name, cost): - if not _HasAccumulator(layer, cost_metric_name): - return - _GetAccumulator(layer, cost_metric_name).Update(cost) - - -def Get(layer, cost_metric_name): - """Returns the aggregated cost metric.""" - return _GetAccumulator(layer, cost_metric_name).GetValue() diff --git a/lingvo/core/conformer_layer.py b/lingvo/core/conformer_layer.py deleted file mode 100644 index 5de9b17c..00000000 --- a/lingvo/core/conformer_layer.py +++ /dev/null @@ -1,357 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Conformer layers as in https://arxiv.org/abs/2005.08100.""" - -from lingvo import compat as tf - -from lingvo.core import base_layer -from lingvo.core import batch_major_attention as attention_lib -from lingvo.core import bn_layers -from lingvo.core import conv_layers_with_time_padding -from lingvo.core import layers -from lingvo.core import layers_with_attention - - -class LConvLayer(base_layer.BaseLayer): - r"""Lightweight conv layer. - - architecture:: - - input - / \ - | ln(.) # input_dim - | fflayer(.) # 2 * input_dim - | | - | glu(.) # input_dim - | depthwise_conv_1d(.) - | norm(.) - | act(.) - | | - | fflayer(.) - | dropout(.) - \ / - + - | - output - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', None, 'Input and (in fact,) output dimension.') - p.Define('kernel_size', None, 'Kernel size of 1d deptwise conv.') - p.Define('conv_activation', 'SWISH', 'Activation after normalization.') - p.Define( - 'glu_activation', 'NONE', - 'Activation in GLU. Check lingvo.core.layers._ACTIVATIONS for ' - 'other options.') - p.Define('dropout_prob', 0., 'Dropout probability.') - - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Input layer norm template.') - p.Define('linear_start_tpl', layers.FCLayer.Params(), 'Linear start layer.') - p.Define('depthwise_conv_tpl', - conv_layers_with_time_padding.DepthwiseConv2DLayer.Params(), - 'Depthwise conv template.') - p.Define('conv_norm_layer_tpl', bn_layers.BatchNormLayer.Params(), - 'Normalization layer after conv.') - p.Define('linear_end_tpl', layers.FCLayer.Params(), 'Linear end layer.') - p.Define('dropout_tpl', layers.DropoutLayer.Params(), - 'Residual dropout layer.') - - p.linear_start_tpl.Set(activation='NONE', has_bias=True) - p.linear_end_tpl.Set(activation='NONE', has_bias=True) - return p - - @classmethod - def CommonParams(cls, - input_dim=None, - kernel_size=None, - conv_activation='SWISH', - dropout_prob=0.): - p = cls.Params().Set( - input_dim=input_dim, - kernel_size=kernel_size, - conv_activation=conv_activation, - dropout_prob=dropout_prob) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - ln_p = p.ln_tpl.Copy().Set(name='ln', input_dim=p.input_dim) - self.CreateChild('ln', ln_p) - - linear_start_p = p.linear_start_tpl.Copy().Set( - name='linear_start', input_dim=p.input_dim, output_dim=2 * p.input_dim) - linear_end_p = p.linear_end_tpl.Copy().Set( - name='linear_end', input_dim=p.input_dim, output_dim=p.input_dim) - self.CreateChild('linear_start', linear_start_p) - self.CreateChild('linear_end', linear_end_p) - - norm_p = p.conv_norm_layer_tpl.Copy().Set( - name='norm_layer', dim=p.input_dim) - self.CreateChild('norm', norm_p) - - # 1d depthwise conv with channel_mulitplier = 1 - depthwise_conv_p = p.depthwise_conv_tpl.Copy().Set( - name='depthwise_conv', - filter_shape=(p.kernel_size, 1, p.input_dim, 1), - filter_stride=(1, 1)) - self.CreateChild('depthwise_conv1d', depthwise_conv_p) - - dropout_p = p.dropout_tpl.Copy().Set( - name='dropout', keep_prob=1. - p.dropout_prob) - self.CreateChild('dropout', dropout_p) - - def _GLU(self, inputs): - p = self.params - gated_inputs, act_inputs = tf.split(inputs, 2, axis=-1) - return self._ApplyActivation(act_inputs, - p.glu_activation) * tf.sigmoid(gated_inputs) - - def _ApplyActivation(self, inputs, act_name): - if act_name == 'NONE': - return inputs - return layers._ACTIVATIONS[act_name](inputs) # pylint:disable=protected-access - - def FProp(self, theta, inputs, paddings): - """Builds FProp graph. - - Args: - theta: A NestedMap of Tensors, see base class. - inputs: A Tensor of shape [batch, seqlen, dim0]. - paddings: A Tensor of shape [batch, seqlen]. - - Returns: - output: A Tensor of shape [batch, seqlen, dim0]. - out_paddings: A Tensor of shape [batch, seqlen]. - """ - - p = self.params - with tf.name_scope(p.name): - unnormalized_inputs = inputs - - inputs = self.ln.FProp(theta.ln, inputs) - inputs = self.linear_start.FProp(theta.linear_start, inputs) - - inputs = self._GLU(inputs) - - # [b, t, d] --> [b, t, 1, d] - inputs = tf.expand_dims(inputs, 2) - inputs, paddings = self.depthwise_conv1d.FProp(theta.depthwise_conv1d, - inputs, paddings) - inputs = tf.squeeze(inputs, 2) - - inputs = self.norm.FProp(theta.norm, inputs) - inputs = self._ApplyActivation(inputs, p.conv_activation) - - inputs = self.linear_end.FProp(theta.linear_end, inputs) - inputs = self.dropout.FProp(theta.dropout, inputs) - - output = inputs + unnormalized_inputs - return output, paddings - - -class ConformerLayer(base_layer.BaseLayer): - """Conformer layer as in https://arxiv.org/abs/2005.08100. - - Canonical version (with default params.) - x = x + 1/2 * FFN(x) - x = x + MHSA(x) - x = x + Lconv(x) - x = x + 1/2 * FFN(x) - y = ln(x) - - Optionally one can change the order of MHSA and conv. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', None, 'Input dimension.') - # atten layer - p.Define('atten_num_heads', None, - 'Num of heads in multi-head self-attention.') - p.Define( - 'atten_left_context', None, 'Local self attention left context.' - 'If None, infinite left context.') - p.Define( - 'atten_right_context', None, 'Local self attention right context.' - 'If None, infinite right context.') - p.Define('use_relative_atten', True, 'If using relative attention.') - p.Define( - 'relative_pos_emb_dim', None, - 'If use_relative_atten, sets the relative pos embedding dim.' - 'Default is the same as input_dim.') - p.Define('layer_order', 'mhsa_before_conv', - 'Only mhsa_before_conv or conv_before_mhsa are supported.') - - # lconv layer - p.Define('kernel_size', None, 'Kernel size of 1d lightweight conv.') - - # fflayer - p.Define('fflayer_hidden_dim', None, - 'Hidden dim of the fflayers (start and end).') - p.Define('fflayer_activation', 'SWISH', 'fflayer activation.') - p.Define('fflayer_residual_weight', 0.5, 'fflayer residual weight.') - p.Define('dropout_prob', None, 'Signature dropout prob of inner componets.') - - # tpl - p.Define('fflayer_start_tpl', - layers_with_attention.TransformerFeedForwardLayer.Params(), - 'Layer params for Feed forward layer at the beginning.') - p.Define('trans_atten_tpl', - attention_lib.TransformerAttentionLayer.Params(), - 'Self attention layer params.') - p.Define('lconv_tpl', LConvLayer.Params(), 'Convolution module params.') - p.Define('fflayer_end_tpl', - layers_with_attention.TransformerFeedForwardLayer.Params(), - 'Layer params for Feed forward layer at the end.') - p.Define('final_ln_tpl', layers.LayerNorm.Params(), 'Final layer norm.') - return p - - @classmethod - def CommonParams(cls, - input_dim=None, - atten_num_heads=None, - atten_local_context=None, - atten_left_context=None, - atten_right_context=None, - use_relative_atten=True, - kernel_size=None, - fflayer_hidden_dim=None, - fflayer_activation='SWISH', - layer_order='mhsa_before_conv', - dropout_prob=0.): - assert all([input_dim, atten_num_heads, kernel_size, fflayer_hidden_dim]) - - if atten_local_context: - assert not any([atten_left_context, atten_right_context]), ( - 'atten_local_context and atten_{left,right}_context can not be set' - 'at the same time.') - atten_left_context = atten_local_context + 1 # including self position. - atten_right_context = atten_local_context - - p = cls.Params().Set( - input_dim=input_dim, - atten_num_heads=atten_num_heads, - atten_left_context=atten_left_context, - atten_right_context=atten_right_context, - use_relative_atten=use_relative_atten, - fflayer_hidden_dim=fflayer_hidden_dim, - fflayer_activation=fflayer_activation, - kernel_size=kernel_size, - layer_order=layer_order, - dropout_prob=0.) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.layer_order in ['mhsa_before_conv', 'conv_before_mhsa'] - - fflayer_start_p = p.fflayer_start_tpl.Copy().Set( - input_dim=p.input_dim, - hidden_dim=p.fflayer_hidden_dim, - activation='SWISH', - residual_weight=p.fflayer_residual_weight, - residual_dropout_prob=p.dropout_prob, - relu_dropout_prob=p.dropout_prob) - self.CreateChild('fflayer_start', fflayer_start_p) - - fflayer_end_p = p.fflayer_end_tpl.Copy().Set( - input_dim=p.input_dim, - hidden_dim=p.fflayer_hidden_dim, - activation='SWISH', - residual_weight=p.fflayer_residual_weight, - residual_dropout_prob=p.dropout_prob, - relu_dropout_prob=p.dropout_prob) - self.CreateChild('fflayer_end', fflayer_end_p) - - trans_atten_p = p.trans_atten_tpl.Copy().Set( - input_dim=p.input_dim, - num_heads=p.atten_num_heads, - atten_dropout_prob=p.dropout_prob, - residual_dropout_prob=p.dropout_prob) - self._ConfigSelfAttenParams(trans_atten_p) - self.CreateChild('trans_atten', trans_atten_p) - - lconv_p = p.lconv_tpl.cls.CommonParams( - input_dim=p.input_dim, kernel_size=p.kernel_size) - self.CreateChild('lconv', lconv_p) - - ln_p = p.final_ln_tpl.Copy().Set(name='final_ln', input_dim=p.input_dim) - self.CreateChild('final_ln', ln_p) - - def _ConfigSelfAttenParams(self, trans_atten_p): - p = self.params - if not p.relative_pos_emb_dim: - p.relative_pos_emb_dim = p.input_dim - if not any([p.atten_left_context, p.atten_right_context]): - atten_type = 'global' if not p.use_relative_atten else 'global_relative' - elif p.atten_left_context is None and p.atten_right_context == 0: - assert not p.use_relative_atten, ( - 'Relative attention isn\'t supported for causal attention.') - atten_type = 'causal' - else: - atten_type = 'local_relative' if p.use_relative_atten else 'local' - - if atten_type == 'causal': - trans_atten_p.is_masked = True - elif atten_type == 'global_relative': - trans_atten_p.atten_tpl = ( - attention_lib.MultiHeadedAttentionXL.Params().Set( - rel_pos_emb_dim=p.relative_pos_emb_dim)) - elif atten_type == 'local_relative': - trans_atten_p.atten_tpl = attention_lib.LocalSelfAttentionXL.Params().Set( - left_context=p.atten_left_context, - right_context=p.atten_right_context, - rel_pos_emb_dim=p.relative_pos_emb_dim) - elif atten_type == 'local': - trans_atten_p.atten_tpl = attention_lib.LocalSelfAttention.Params().Set( - left_context=p.atten_left_context, - right_context=p.atten_right_context) - # No op for 'global' atten - - def _SelfAtten(self, theta, inputs, paddings): - inputs, _ = self.trans_atten.FProp( - theta.trans_atten, - query_vec=inputs, - source_vecs=None, - paddings=paddings) - return inputs, paddings - - def _LConv(self, theta, inputs, paddings): - inputs, paddings = self.lconv.FProp(theta.lconv, inputs, paddings) - return inputs, paddings - - def FProp(self, theta, inputs, paddings): - p = self.params - - with tf.name_scope(p.name): - inputs = self.fflayer_start.FProp(theta.fflayer_start, inputs, paddings) - if p.layer_order == 'mhsa_before_conv': - inputs, paddings = self._SelfAtten(theta, inputs, paddings) - inputs, paddings = self._LConv(theta, inputs, paddings) - else: - assert p.layer_order == 'conv_before_mhsa' - inputs, paddings = self._LConv(theta, inputs, paddings) - inputs, paddings = self._SelfAtten(theta, inputs, paddings) - inputs = self.fflayer_end.FProp(theta.fflayer_end, inputs, paddings) - - inputs = self.final_ln.FProp(theta.final_ln, inputs) - return inputs, paddings diff --git a/lingvo/core/conformer_layer_test.py b/lingvo/core/conformer_layer_test.py deleted file mode 100644 index 64287c16..00000000 --- a/lingvo/core/conformer_layer_test.py +++ /dev/null @@ -1,75 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for conformer layers as in https://arxiv.org/abs/2005.08100.""" -# Lint as: PY3 -from absl.testing import parameterized - -from lingvo import compat as tf -from lingvo.core import conformer_layer -from lingvo.core import test_utils - - -class LConvLayerTest(test_utils.TestCase, parameterized.TestCase): - - def testBasic(self): - batch, seqlen, dim = 2, 16, 4 - inputs = tf.zeros([batch, seqlen, dim]) - paddings = tf.zeros([batch, seqlen]) - - p = conformer_layer.LConvLayer.CommonParams(input_dim=dim, kernel_size=3) - p.name = 'lconv_layer' - l = p.Instantiate() - outputs = l.FPropDefaultTheta(inputs, paddings) - - with self.session() as sess: - tf.global_variables_initializer().run() - out_vals = sess.run(outputs) - print([x.shape for x in out_vals]) - - -class ConformerLayerTest(test_utils.TestCase, parameterized.TestCase): - - @parameterized.named_parameters( - ('Base',), - ('Reordered', 'conv_before_mhsa'), - ) - def testBasic(self, layer_order='mhsa_before_conv'): - batch, seqlen, dim, heads = 2, 32, 4, 2 - context = 2 - - inputs = tf.zeros([batch, seqlen, dim]) - paddings = tf.zeros([batch, seqlen]) - - p = conformer_layer.ConformerLayer.CommonParams( - input_dim=dim, - atten_num_heads=heads, - atten_left_context=context + 1, - atten_right_context=context, - kernel_size=3, - fflayer_hidden_dim=4 * dim) - p.name = 'conformer_layer' - p.layer_order = layer_order - l = p.Instantiate() - outputs = l.FPropDefaultTheta(inputs, paddings) - - with self.session() as sess: - tf.global_variables_initializer().run() - out_vals = sess.run(outputs) - print([x.shape for x in out_vals]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/constants.py b/lingvo/core/constants.py deleted file mode 100644 index c5d6ee77..00000000 --- a/lingvo/core/constants.py +++ /dev/null @@ -1,19 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Constants used by lingvo.""" - - -REFERENCE_ANNOTATION = 'arxiv.org/abs/1902.08295' diff --git a/lingvo/core/conv_layers_builder.py b/lingvo/core/conv_layers_builder.py deleted file mode 100644 index c7a6d3e5..00000000 --- a/lingvo/core/conv_layers_builder.py +++ /dev/null @@ -1,530 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""2D conv layers that are expected to be used with sequence inputs.""" -import math -from absl import flags - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import builder -from lingvo.core import builder_layers -from lingvo.core import conv_layers_with_time_padding -from lingvo.core import py_utils -from lingvo.core import tshape - -FLAGS = flags.FLAGS - -Conv2DLayerWithPadding = conv_layers_with_time_padding.Conv2DLayerWithPadding -CausalConv2DLayerWithPadding = conv_layers_with_time_padding.CausalConv2DLayerWithPadding -DepthwiseConv2DLayer = conv_layers_with_time_padding.DepthwiseConv2DLayer -CausalDepthwiseConv2DLayer = conv_layers_with_time_padding.CausalDepthwiseConv2DLayer -ConvBatchNormLayer = conv_layers_with_time_padding.ConvBatchNormLayer -ConvCategoricalBN = conv_layers_with_time_padding.ConvCategoricalBN -ActivationLayer = conv_layers_with_time_padding.ActivationLayer -PaddingLayer = conv_layers_with_time_padding.PaddingLayer -NormalizedDepthwiseConv2DLayer = conv_layers_with_time_padding.NormalizedDepthwiseConv2DLayer -CausalNormalizedDepthwiseConv2DLayer = conv_layers_with_time_padding.CausalNormalizedDepthwiseConv2DLayer -GlobalPoolingLayer = conv_layers_with_time_padding.GlobalPoolingLayer - - -class BiasLayer(builder_layers.BiasLayer): - - def FProp(self, theta, inputs, paddings): - bias_added = super().FProp(theta, inputs) - return bias_added, paddings - - -class CausalPoolingLayer(base_layer.BaseLayer): - """Pooling layer with causal dependency on the time axis.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('pooling_type', 'AVG', 'Pooling type: MAX|AVG') - p.Define( - 'left_context', None, 'Number of frames to the left in the pooling' - 'window (including the current frame). A special value "-1" means ' - 'using all left frames') - return p - - def FProp(self, theta, inputs, paddings): - """Applies causal pooling to inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. It is expected to be of shape [batch, time, - frequency, channel]. The time dimension corresponds to the height - dimension as in images and the frequency dimension corresponds to the - width dimension as in images. - paddings: The paddings tensor. It is expected to be of shape [batch, - time]. - - Returns: - outputs, out_paddings pair. - - outputs: has the same shape as inputs. - - out_paddings: has the same tshape as paddings. - """ - - p = self.params - if p.left_context == -1: - if p.pooling_type == 'AVG': - cumulative_sum = tf.math.cumsum(inputs, axis=1) - cumulative_count = 1.0 + tf.range( - py_utils.GetShape(inputs)[1], dtype=p.dtype) - cumulative_mean = cumulative_sum / cumulative_count[tf.newaxis, :, - tf.newaxis, - tf.newaxis] - cumulative_mean *= 1.0 - paddings[..., tf.newaxis, tf.newaxis] - return cumulative_mean, paddings - else: - raise NotImplementedError('Cumulative max pooling not implemented.') - - window_size = p.left_context - left_pad_size = window_size - 1 - large_negative = p.dtype.max * tf.constant(-0.7, dtype=p.dtype) - # For max pooling, use a large negative padding value such that the max - # element is almost always from a non-padding position. - pad_value = 0 if p.pooling_type == 'AVG' else large_negative - inputs = tf.pad( - inputs, [[0, 0], [left_pad_size, 0], [0, 0], [0, 0]], - constant_values=pad_value) - - out_feature = tf.nn.pool( - inputs, - window_shape=(window_size, 1), - pooling_type=p.pooling_type, - padding='VALID') - - if p.pooling_type == 'AVG': - # Count the fraction of non-padding elements inside each pooling window. - max_seq_len = py_utils.GetShape(paddings)[1] - num_non_padded_elements = tf.range(1, 1 + max_seq_len, dtype=p.dtype) - num_non_padded_elements = tf.minimum(num_non_padded_elements, - tf.cast(window_size, p.dtype)) - non_padded_ratio = num_non_padded_elements / tf.cast(window_size, p.dtype) - # Divide by non-padding ratios to eliminate the effect of padded zeros. - out_feature *= tf.math.reciprocal_no_nan(non_padded_ratio[tf.newaxis, :, - tf.newaxis, - tf.newaxis]) - out_feature *= 1.0 - paddings[..., tf.newaxis, tf.newaxis] - return out_feature, paddings - - -class Builder(builder.Base): - """Builder patterns for commonly used conv layers.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('norm_layer_tpl', - ConvBatchNormLayer.Params().Set(decay=0.999), - 'If specified, the normalization layer template.') - p.Define('weight_norm', False, 'Add weight norm for kernel weights or not.') - p.Define( - 'v2_padding', False, 'Prefer setting to True. The default ' - 'implementation is incorrect for strided convolutions.') - - return p - - def _BiasNoPadding(self, name, dims): - return super()._Bias(name, dims) - - def _Bias(self, name, dims): - """Bias layer. The bias is added to the last dimension of the input.""" - return BiasLayer.Params().Set(name=name, dims=dims) - - def _Norm(self, name, dims): - return self.params.norm_layer_tpl.Copy().Set(name=name, dim=dims) - - def _NormOrBias(self, name, dims): - if self.params.norm_layer_tpl: - return self._Norm(name, dims) - else: - return self._Bias(name, dims) - - def _MaybeNorm(self, name, dims): - if self.params.norm_layer_tpl: - return self._Norm(name, dims) - else: - return self._Id(name) - - def _Activation(self, name, activation): - return ActivationLayer.Params().Set(name=name, activation=activation) - - def _Padding(self, name): - return PaddingLayer.Params().Set(name=name) - - def _RawConv2D(self, - name, - in_dim, - out_dim, - filter_shape, - stride, - dilation, - is_causal): - if is_causal: - conv_cls = CausalConv2DLayerWithPadding - else: - conv_cls = Conv2DLayerWithPadding - return conv_cls.Params().Set( - name=name, - filter_shape=filter_shape + [in_dim, out_dim], - filter_stride=stride, - dilation_rate=dilation, - weight_norm=self.params.weight_norm, - v2_padding=self.params.v2_padding) - - def _RawDepthwiseConv2D(self, - name, - in_dim, - depth_multiplier, - filter_shape, - stride, - dilation, - is_causal): - if is_causal: - conv_cls = CausalDepthwiseConv2DLayer - else: - conv_cls = DepthwiseConv2DLayer - return conv_cls.Params().Set( - name=name, - filter_shape=filter_shape + [in_dim, depth_multiplier], - filter_stride=stride, - dilation_rate=dilation, - weight_norm=self.params.weight_norm, - v2_padding=self.params.v2_padding) - - def _GlobalPooling(self, name, pooling_type): - return GlobalPoolingLayer.Params().Set(name=name, pooling_type=pooling_type) - - def Conv2D(self, - name, - in_dim, - out_dim, - filter_shape, - stride=None, - dilation=None, - activation='RELU', - conv_last=False, - is_causal=False): - if stride is None: - stride = [1, 1] - if dilation is None: - dilation = [1, 1] - if conv_last: - layers_in_sequence = [ - self._MaybeNorm('bn', in_dim), - self._Activation('act', activation), - self._RawConv2D('conv_2d', in_dim, out_dim, filter_shape, stride, - dilation, is_causal), - self._Bias('bias', out_dim), - self._Padding('pad') - ] - else: - layers_in_sequence = [ - self._RawConv2D('conv_2d', in_dim, out_dim, filter_shape, stride, - dilation, is_causal), - self._NormOrBias('bn_or_bias', out_dim), - self._Activation('act', activation), - self._Padding('pad') - ] - return self._Seq(name, *layers_in_sequence) - - def DepthwiseConv2D(self, - name, - in_dim, - depth_multiplier, - filter_shape, - stride=None, - dilation=None, - activation='RELU', - conv_last=False, - is_causal=False): - if stride is None: - stride = [1, 1] - if dilation is None: - dilation = [1, 1] - if conv_last: - layers_in_sequence = [ - self._MaybeNorm('bn', in_dim), - self._Activation('act', activation), - self._RawDepthwiseConv2D('conv_2d', in_dim, depth_multiplier, - filter_shape, stride, dilation, is_causal), - self._Bias('bias', in_dim * depth_multiplier), - self._Padding('pad') - ] - else: - layers_in_sequence = [ - self._RawDepthwiseConv2D('conv_2d', in_dim, depth_multiplier, - filter_shape, stride, dilation, is_causal), - self._NormOrBias('bn_or_bias', in_dim * depth_multiplier), - self._Activation('act', activation), - self._Padding('pad') - ] - return self._Seq(name, *layers_in_sequence) - - def SeparableConv2D(self, - name, - in_dim, - out_dim, - depth_multiplier, - filter_shape, - stride=None, - dilation=None, - activation='RELU', - conv_last=False, - is_causal=False): - if stride is None: - stride = [1, 1] - if dilation is None: - dilation = [1, 1] - if conv_last: - layers_in_sequence = [ - self._MaybeNorm('bn', in_dim), - self._Activation('act', activation), - self._RawDepthwiseConv2D('conv_2d', in_dim, depth_multiplier, - filter_shape, stride, dilation, is_causal), - # No need to add a padding layer here as subsequent conv layer always - # properly zeros out padded nodes. - self._RawConv2D( - 'conv_1x1', - in_dim * depth_multiplier, - out_dim, - filter_shape=[1, 1], - stride=[1, 1], - dilation=[1, 1], - is_causal=False), - self._Bias('bias', out_dim), - self._Padding('pad') - ] - else: - layers_in_sequence = [ - self._RawDepthwiseConv2D('conv_2d', in_dim, depth_multiplier, - filter_shape, stride, dilation, is_causal), - # No need to add a padding layer here as subsequent conv layer always - # properly zeros out padded nodes. - self._RawConv2D( - 'conv_1x1', - in_dim * depth_multiplier, - out_dim, - filter_shape=[1, 1], - stride=[1, 1], - dilation=[1, 1], - is_causal=False), - self._NormOrBias('bn_or_bias', out_dim), - self._Activation('act', activation), - self._Padding('pad') - ] - return self._Seq(name, *layers_in_sequence) - - def NormalizedDepthwiseConv2D(self, - name, - kernel_size, - num_heads, - in_dim, - dropconnect_prob=0, - deterministic_dropout=False, - is_causal=False): - if is_causal: - conv_cls = CausalNormalizedDepthwiseConv2DLayer - else: - conv_cls = NormalizedDepthwiseConv2DLayer - return conv_cls.Params().Set( - name=name, - filter_shape=[kernel_size, 1, num_heads, 1], - weight_tiling_factor=in_dim // num_heads, - deterministic_dropout=deterministic_dropout, - params_init=py_utils.WeightInit.TruncatedGaussian( - scale=math.sqrt(2.6 / kernel_size)), # Fan-out initialization. - dropconnect_prob=dropconnect_prob, - v2_padding=self.params.v2_padding) - - def _Add(self, name, residual_weight=1.0): - return self._Fn( - name, fn=lambda x, y: x + residual_weight * y, fn_out=lambda x, y: x) - - def _ExpandDims(self, name): - return self._Fn( - name, - fn=lambda x: tf.expand_dims(x, 2), - fn_out=lambda x: tshape.Shape(x[0:2] + [1] + x[2:]), - fn_flops=lambda x: 1) - - def _Squeeze(self, name): - return self._Fn( - name, - fn=lambda x: tf.squeeze(x, 2), - fn_out=lambda x: tshape.Shape(x[0:2] + x[3:]), - fn_flops=lambda x: 1) - - def _Glu(self, name, glu_with_tanh): - - def _GLUFn(inputs): - gated_inputs, act_inputs = tf.split(inputs, 2, axis=-1) - return act_inputs * tf.sigmoid(gated_inputs) - - def _GatedTanhFn(inputs): - gated_inputs, act_inputs = tf.split(inputs, 2, axis=-1) - return tf.tanh(act_inputs) * tf.sigmoid(gated_inputs) - - fn = _GatedTanhFn if glu_with_tanh else _GLUFn - - return self._Fn( - name, - fn=fn, - fn_out=lambda x: tshape.Shape(x[:-1] + [x[-1] / 2]), - fn_flops=lambda x: 15 * x.size) - - def _LConvCommon(self, - name, - input_dim, - kernel_size, - activation='RELU', - is_causal=False, - glu_with_tanh=False, - residual_dropout_prob=0): - # pyformat: disable - return py_utils.NestedMap( - pre_conv=('i.vec->pre_conv', - self._Seq( - 'pre_conv', - self._LN('ln', input_dim), - self._Linear('linear', input_dim, input_dim * 2), - self._BiasNoPadding('bias', input_dim * 2), - self._Glu('glu', glu_with_tanh), - self._ExpandDims('expand'))), - post_conv=('post_conv->after_dropout', - self._Seq( - 'post_conv', - self._Squeeze('squeeze'), - self._Linear('linear', input_dim, input_dim), - self._BiasNoPadding('bias', input_dim), - self._Dropout( - 'dropout', keep_prob=1 - residual_dropout_prob))), - residual_add=('i.vec,after_dropout->o.vec', self._Add('add')) - ) - # pyformat: enable - - def LConv(self, - name, - input_dim, - kernel_size, - activation='RELU', - is_causal=False, - glu_with_tanh=False, - residual_dropout_prob=0): - """A lightweight convolution block as described in ... - - https://arxiv.org/abs/1901.10430 - - Reference PyTorch Implementation (L587): - https://github.com/pytorch/fairseq/blob/v0.6.2/fairseq/models/lightconv.py - - Args: - name: name of the params - input_dim: Input dimension. - kernel_size: kernel size used in the conv layer. - activation: A string, activation function used by the inner conv block. - is_causal: is causal padding or not. - glu_with_tanh: if the Gated Linear Unit should apply tanh on the - activation input. - residual_dropout_prob: Residual dropout prob. - - Returns: - A GraphLayer params with a FProp() function of signature - f(inputs, paddings) -> outputs, out_paddings - """ - sub_nmap = self._LConvCommon( - name, - input_dim, - kernel_size, - activation=activation, - is_causal=is_causal, - residual_dropout_prob=residual_dropout_prob) - conv_graph = ('pre_conv,i.paddings->post_conv,o.paddings', - self.DepthwiseConv2D( - name, - in_dim=input_dim, - depth_multiplier=1, - filter_shape=[kernel_size, 1], - activation=activation, - is_causal=is_causal)) - sub_list = [ - sub_nmap.pre_conv, conv_graph, sub_nmap.post_conv, sub_nmap.residual_add - ] - - return self._Graph( - name, - ['i'], # input NestedMap with {vec, paddings} - ['o'], # output NestedMap with {vec, paddings} - *sub_list) - - def NormalizedLConv(self, - name, - input_dim, - kernel_size, - num_heads, - activation='RELU', - is_causal=False, - glu_with_tanh=False, - residual_dropout_prob=0, - dropconnect_prob=0): - """A lightweight convolution block as described in ... - - https://arxiv.org/abs/2004.11886 - - Args: - name: name of the params - input_dim: Input dimension. - kernel_size: kernel size used in the conv layer. - num_heads: Num of heads. - activation: A string, activation function used by the inner conv block. - is_causal: is causal padding or not. - glu_with_tanh: if the Gated Linear Unit should apply tanh on the - activation input. - residual_dropout_prob: Residual dropout prob. - dropconnect_prob: attention dropout prob. - - Returns: - A GraphLayer params with a FProp() function of signature - f(inputs, paddings) -> outputs, out_paddings - """ - sub_nmap = self._LConvCommon( - name, - input_dim, - kernel_size, - activation=activation, - is_causal=is_causal, - residual_dropout_prob=residual_dropout_prob) - conv_graph = ('pre_conv,i.paddings->post_conv,o.paddings', - self.NormalizedDepthwiseConv2D( - name, - kernel_size=kernel_size, - num_heads=num_heads, - in_dim=input_dim, - dropconnect_prob=dropconnect_prob, - deterministic_dropout=self.params.deterministic_dropout, - is_causal=is_causal)) - sub_list = [ - sub_nmap.pre_conv, conv_graph, sub_nmap.post_conv, sub_nmap.residual_add - ] - - return self._Graph( - name, - ['i'], # input NestedMap with {vec, paddings} - ['o'], # output NestedMap with {vec, paddings} - *sub_list) diff --git a/lingvo/core/conv_layers_builder_test.py b/lingvo/core/conv_layers_builder_test.py deleted file mode 100644 index f71a1a2a..00000000 --- a/lingvo/core/conv_layers_builder_test.py +++ /dev/null @@ -1,616 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for conv layers builder.""" - -from absl.testing import parameterized -from lingvo import compat as tf -from lingvo.core import bn_layers -from lingvo.core import conv_layers_builder -from lingvo.core import conv_layers_with_time_padding -from lingvo.core import layers -from lingvo.core import test_utils -import numpy as np - - -class ConvPaddedLayersTest(test_utils.TestCase): - - def _ConvTestHelper(self, dilation, stride, activation, batch_norm, - weight_norm, in_dim, out_dim, filter_shape, conv_last, - causal_conv): - with self.session(use_gpu=True) as sess: - p1 = layers.Conv2DLayer.Params().Set( - name='conv_2d01', - filter_shape=filter_shape + [in_dim, out_dim], - filter_stride=stride, - dilation_rate=dilation, - activation=activation, - batch_norm=batch_norm, - weight_norm=weight_norm, - bias=not batch_norm, - conv_last=conv_last, - causal_convolution=causal_conv) - builder_params = conv_layers_builder.Builder.Params().Set( - weight_norm=weight_norm) - if batch_norm: - norm_p = conv_layers_with_time_padding.ConvBatchNormLayer.Params().Set( - decay=0.999) - builder_params.norm_layer_tpl = norm_p - else: - builder_params.norm_layer_tpl = None - p2 = builder_params.Instantiate().Conv2D( - 'conv_2d02', - in_dim, - out_dim, - filter_shape, - stride=stride, - dilation=dilation, - activation=activation, - conv_last=conv_last, - is_causal=causal_conv) - - l1 = p1.Instantiate() - l2 = p2.Instantiate() - - conv_in = tf.constant(np.random.normal(size=[4, 5, 6, 3]), tf.float32) - conv_pad = np.full([4, 5], 0.0) - conv_pad[2, 3] = 1.0 - conv_pad[2, 4] = 1.0 - conv_pad = tf.constant(conv_pad, tf.float32) - l1_theta = l1.theta.Transform(tf.identity) - l2_theta = l2.theta.Transform(tf.identity) - conv_out1, out1_padding = l1.FProp(l1_theta, conv_in, conv_pad) - conv_out2, out2_padding = l2.FProp(l2_theta, conv_in, conv_pad) - - tf.logging.info(l1_theta) - tf.logging.info(l2_theta) - l1_num_vars = l1_theta.Flatten() - l2_num_var2 = l2_theta.Flatten() - if len(l1_num_vars) != len(l2_num_var2): - tf.logging.info( - 'Mismatched number of vars: l1: %d vars, l2: %d vars', - len(l1_num_vars), len(l2_num_var2)) - - w1 = l1_theta.w - w2 = l2_theta.conv_2d.w - # b1 = l1_theta.b - # b2 = l2_theta.bn_or_bias.b - - tf.global_variables_initializer().run() - v1, p1 = sess.run([conv_out1, out1_padding]) - w1_v = sess.run(w1) - v2, p2 = sess.run([conv_out2, out2_padding], feed_dict={w2: w1_v}) - - self.assertAllClose(v1, v2) - self.assertAllClose(p1, p2) - - def testConvBasic(self): - dilation = [1, 1] - stride = [2, 3] - activation = 'NONE' - batch_norm = False - weight_norm = False - in_dim = 3 - out_dim = 3 - filter_shape = [2, 2] - conv_last = False - causal_conv = False - self._ConvTestHelper(dilation, stride, activation, batch_norm, weight_norm, - in_dim, out_dim, filter_shape, conv_last, causal_conv) - - def testConvBnWnTanh(self): - dilation = [1, 1] - stride = [2, 3] - activation = 'TANH' - batch_norm = True - weight_norm = True - in_dim = 3 - out_dim = 3 - filter_shape = [2, 2] - conv_last = False - causal_conv = False - self._ConvTestHelper(dilation, stride, activation, batch_norm, weight_norm, - in_dim, out_dim, filter_shape, conv_last, causal_conv) - - def testConvGn(self): - dilation = [1, 1] - stride = [2, 3] - activation = 'TANH' - in_dim = 3 - out_dim = 4 - filter_shape = [2, 2] - conv_last = False - causal_conv = False - - with self.session(use_gpu=True) as sess: - builder_params = conv_layers_builder.Builder.Params().Set( - weight_norm=True) - builder_params.norm_layer_tpl = bn_layers.GroupNormLayer.Params().Set( - num_groups=2) - p = builder_params.Instantiate().Conv2D( - 'conv_2d02', - in_dim, - out_dim, - filter_shape, - stride=stride, - dilation=dilation, - activation=activation, - conv_last=conv_last, - is_causal=causal_conv) - - l = p.Instantiate() - - conv_in = tf.constant(np.random.normal(size=[4, 5, 6, 3]), tf.float32) - conv_pad = np.full([4, 5], 0.0) - conv_pad[2, 3] = 1.0 - conv_pad[2, 4] = 1.0 - conv_pad = tf.constant(conv_pad, tf.float32) - conv_out, _ = l.FProp(l.theta, conv_in, conv_pad) - tf.global_variables_initializer().run() - v = sess.run(tf.reduce_sum(conv_out, 0)) - - expected_out = [[[-0.35070014, -1.7821487, 0.8349923, 1.1709788], - [-0.18872532, 0.9702145, 0.5534694, -1.1386856]], - [[0.34970748, -0.5403709, -0.9809327, -2.0930214], - [0.54232424, 1.1565661, 1.0349312, 1.3458138]], - [[0, 0, 0, 0], [0, 0, 0, 0]]] - - self.assertAllClose(v, expected_out) - - def testConvLastWnTanh(self): - dilation = [1, 1] - stride = [2, 3] - activation = 'TANH' - batch_norm = False - weight_norm = True - in_dim = 3 - out_dim = 3 - filter_shape = [2, 2] - conv_last = True - causal_conv = False - self._ConvTestHelper(dilation, stride, activation, batch_norm, weight_norm, - in_dim, out_dim, filter_shape, conv_last, causal_conv) - - def testConvLastCausal(self): - dilation = [1, 1] - stride = [2, 3] - activation = 'TANH' - batch_norm = True - weight_norm = True - in_dim = 3 - out_dim = 3 - filter_shape = [2, 1] - conv_last = True - causal_conv = True - self._ConvTestHelper(dilation, stride, activation, batch_norm, weight_norm, - in_dim, out_dim, filter_shape, conv_last, causal_conv) - - def _DepthwiseConvTestHelper(self, dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, - filter_shape, conv_last, causal_conv): - with self.session(use_gpu=True) as sess: - p1 = layers.DepthwiseConv2DLayer.Params().Set( - name='conv_2d01', - filter_shape=filter_shape + [in_dim, depth_multiplier], - filter_stride=stride, - dilation_rate=dilation, - activation=activation, - batch_norm=batch_norm, - weight_norm=weight_norm, - bias=not batch_norm, - conv_last=conv_last, - causal_convolution=causal_conv) - builder_params = conv_layers_builder.Builder.Params().Set( - weight_norm=weight_norm) - if batch_norm: - norm_p = conv_layers_with_time_padding.ConvBatchNormLayer.Params().Set( - decay=0.999) - builder_params.norm_layer_tpl = norm_p - else: - builder_params.norm_layer_tpl = None - - p2 = builder_params.Instantiate().DepthwiseConv2D( - 'conv_2d02', - in_dim, - depth_multiplier, - filter_shape, - stride=stride, - activation=activation, - dilation=dilation, - conv_last=conv_last, - is_causal=causal_conv) - - l1 = p1.Instantiate() - l2 = p2.Instantiate() - - conv_in = tf.constant(np.random.normal(size=[4, 5, 6, 3]), tf.float32) - conv_pad = np.full([4, 5], 0.0) - conv_pad[2, 3] = 1.0 - conv_pad[2, 4] = 1.0 - conv_pad = tf.constant(conv_pad, tf.float32) - l1_theta = l1.theta.Transform(tf.identity) - l2_theta = l2.theta.Transform(tf.identity) - conv_out1, out1_padding = l1.FProp(l1_theta, conv_in, conv_pad) - conv_out2, out2_padding = l2.FProp(l2_theta, conv_in, conv_pad) - - tf.logging.info(l1_theta) - tf.logging.info(l2_theta) - l1_num_vars = l1_theta.Flatten() - l2_num_var2 = l2_theta.Flatten() - if len(l1_num_vars) != len(l2_num_var2): - tf.logging.info( - 'Mismatched number of vars: l1: %d vars, l2: %d vars', - len(l1_num_vars), len(l2_num_var2)) - - w1 = l1_theta.w - w2 = l2_theta.conv_2d.w - # b1 = l1_theta.b - # b2 = l2_theta.bn_or_bias.b - - tf.global_variables_initializer().run() - v1, p1 = sess.run([conv_out1, out1_padding]) - w1_v = sess.run([w1])[0] - v2, p2 = sess.run([conv_out2, out2_padding], feed_dict={w2: w1_v}) - - self.assertAllClose(v1, v2) - self.assertAllClose(p1, p2) - - def testDepthConvBasic(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'NONE' - batch_norm = False - weight_norm = False - in_dim = 3 - depth_multiplier = 2 - filter_shape = [2, 2] - conv_last = False - causal_conv = False - self._DepthwiseConvTestHelper(dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, - filter_shape, conv_last, causal_conv) - - def testDepthConvBnWnTanh(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'TANH' - batch_norm = True - weight_norm = True - in_dim = 3 - depth_multiplier = 3 - filter_shape = [2, 2] - conv_last = False - causal_conv = False - self._DepthwiseConvTestHelper(dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, - filter_shape, conv_last, causal_conv) - - def testDepthConvGn(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'TANH' - in_dim = 4 - depth_multiplier = 1 - filter_shape = [2, 2] - conv_last = False - causal_conv = False - - with self.session(use_gpu=True) as sess: - builder_params = conv_layers_builder.Builder.Params().Set( - weight_norm=True) - builder_params.norm_layer_tpl = bn_layers.GroupNormLayer.Params().Set( - num_groups=2) - p = builder_params.Instantiate().DepthwiseConv2D( - 'conv_2d02', - in_dim, - depth_multiplier, - filter_shape, - stride=stride, - activation=activation, - dilation=dilation, - conv_last=conv_last, - is_causal=causal_conv) - l = p.Instantiate() - - conv_in = tf.constant(np.random.normal(size=[4, 5, 6, 4]), tf.float32) - conv_pad = np.full([4, 5], 0.0) - conv_pad[2, 3] = 1.0 - conv_pad[2, 4] = 1.0 - conv_pad = tf.constant(conv_pad, tf.float32) - conv_out, _ = l.FProp(l.theta, conv_in, conv_pad) - tf.global_variables_initializer().run() - v = sess.run(tf.reduce_sum(conv_out, 0)) - - expected_out = [[[-0.77095497, 0.30285388, -0.05714864, 1.0386012], - [0.74034333, 0.04982221, -0.41769135, -2.9531932], - [-0.2647084, -0.1936804, 0.6598473, 0.42537105]], - [[1.3095646, -0.85996866, 2.2734299, -1.8457952], - [-0.9542263, -0.14199251, 0.51472515, 0.91931283], - [0.47267163, 1.4824618, 0.4548889, 0.93488806]], - [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]] - - self.assertAllClose(expected_out, v) - - def testDepthConvLastWnTanh(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'TANH' - batch_norm = False - weight_norm = True - in_dim = 3 - depth_multiplier = 3 - filter_shape = [2, 2] - conv_last = True - causal_conv = False - self._DepthwiseConvTestHelper(dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, - filter_shape, conv_last, causal_conv) - - def testDepthConvLastCausal(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'TANH' - batch_norm = True - weight_norm = True - in_dim = 3 - depth_multiplier = 3 - filter_shape = [2, 1] - conv_last = True - causal_conv = True - self._DepthwiseConvTestHelper(dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, - filter_shape, conv_last, causal_conv) - - def _SeparableConvTestHelper(self, dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, out_dim, - filter_shape, conv_last, causal_conv, - assert_equality=True): - with self.session(use_gpu=True) as sess: - p1 = layers.SeparableConv2DLayer.Params().Set( - name='conv_2d01', - filter_shape=filter_shape + [in_dim, out_dim], - depth_multiplier=depth_multiplier, - filter_stride=stride, - dilation_rate=dilation, - activation=activation, - batch_norm=batch_norm, - weight_norm=weight_norm, - bias=not batch_norm, - conv_last=conv_last, - causal_convolution=causal_conv) - builder_params = conv_layers_builder.Builder.Params().Set( - weight_norm=weight_norm) - if batch_norm: - norm_p = conv_layers_with_time_padding.ConvBatchNormLayer.Params().Set( - decay=0.999) - builder_params.norm_layer_tpl = norm_p - else: - builder_params.norm_layer_tpl = None - p2 = builder_params.Instantiate().SeparableConv2D( - 'conv_2d02', - in_dim, - out_dim, - depth_multiplier, - filter_shape, - stride=stride, - activation=activation, - dilation=dilation, - conv_last=conv_last, - is_causal=causal_conv) - - l1 = p1.Instantiate() - l2 = p2.Instantiate() - - conv_in = tf.constant(np.random.normal(size=[4, 5, 6, 3]), tf.float32) - conv_pad = np.full([4, 5], 0.0) - conv_pad[2, 3] = 1.0 - conv_pad[2, 4] = 1.0 - conv_pad = tf.constant(conv_pad, tf.float32) - l1_theta = l1.theta.Transform(tf.identity) - l2_theta = l2.theta.Transform(tf.identity) - conv_out1, out1_padding = l1.FProp(l1_theta, conv_in, conv_pad) - conv_out2, out2_padding = l2.FProp(l2_theta, conv_in, conv_pad) - - tf.logging.info(l1_theta) - tf.logging.info(l2_theta) - l1_num_vars = l1_theta.Flatten() - l2_num_var2 = l2_theta.Flatten() - if len(l1_num_vars) != len(l2_num_var2): - tf.logging.info( - 'Mismatched number of vars: l1: %d vars, l2: %d vars', - len(l1_num_vars), len(l2_num_var2)) - - pointwise_conv_w1 = l1_theta.w - depth_conv_w1 = l1_theta.depthwise_conv.w - pointwise_conv_w2 = l2_theta.conv_1x1.w - depth_conv_w2 = l2_theta.conv_2d.w - # b1 = l1_theta.b - # b2 = l2_theta.bn_or_bias.b - tf.global_variables_initializer().run() - v1, p1 = sess.run([conv_out1, out1_padding]) - p_w1_v, d_w1_v = sess.run([pointwise_conv_w1, depth_conv_w1]) - v2, p2 = sess.run([conv_out2, out2_padding], - feed_dict={ - pointwise_conv_w2: p_w1_v, - depth_conv_w2: d_w1_v - }) - - if assert_equality: - self.assertAllClose(v1, v2) - self.assertAllClose(p1, p2) - - def testSeparableConv2DLayerBasic(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'NONE' - batch_norm = False - weight_norm = False - in_dim = 3 - depth_multiplier = 3 - out_dim = 2 - filter_shape = [2, 2] - conv_last = False - causal_conv = False - self._SeparableConvTestHelper(dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, - out_dim, filter_shape, conv_last, causal_conv) - - def testSeparableConvWnWnTanh(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'TANH' - batch_norm = False - weight_norm = True - in_dim = 3 - depth_multiplier = 3 - out_dim = 2 - filter_shape = [2, 1] - conv_last = False - causal_conv = True - self._SeparableConvTestHelper(dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, - out_dim, filter_shape, conv_last, causal_conv) - - def testSeparableConvLastBnWnTanh(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'TANH' - batch_norm = True - weight_norm = True - in_dim = 3 - depth_multiplier = 3 - out_dim = 2 - filter_shape = [2, 1] - conv_last = True - causal_conv = True - # New implementation is not equivallent to the old. - self._SeparableConvTestHelper(dilation, stride, activation, batch_norm, - weight_norm, in_dim, depth_multiplier, - out_dim, filter_shape, conv_last, causal_conv, - assert_equality=False) - - def testSeparableConvGn(self): - dilation = [1, 1] - stride = [2, 2] - activation = 'TANH' - in_dim = 4 - depth_multiplier = 1 - out_dim = 2 - filter_shape = [2, 1] - conv_last = True - causal_conv = True - - with self.session(use_gpu=True) as sess: - builder_params = conv_layers_builder.Builder.Params().Set( - weight_norm=True) - builder_params.norm_layer_tpl = bn_layers.GroupNormLayer.Params().Set( - num_groups=2) - p = builder_params.Instantiate().SeparableConv2D( - 'conv_2d02', - in_dim, - out_dim, - depth_multiplier, - filter_shape, - stride=stride, - activation=activation, - dilation=dilation, - conv_last=conv_last, - is_causal=causal_conv) - - l = p.Instantiate() - - conv_in = tf.constant(np.random.normal(size=[4, 5, 6, 4]), tf.float32) - conv_pad = np.full([4, 5], 0.0) - conv_pad[2, 3] = 1.0 - conv_pad[2, 4] = 1.0 - conv_pad = tf.constant(conv_pad, tf.float32) - conv_out, _ = l.FProp(l.theta, conv_in, conv_pad) - tf.global_variables_initializer().run() - v = sess.run(tf.reduce_sum(conv_out, 0)) - - expected_out = [[[0.00963847, -0.04019006], [0.36265337, -0.06592329], - [0.65582913, -0.1533944]], - [[0.7512939, -0.7282307], [0.96100605, -1.9509676], - [0.4639647, 0.2485837]], [[0., 0.], [0., 0.], [0., 0.]]] - - self.assertAllClose(expected_out, v) - - -class CausalPoolingLayerTest(test_utils.TestCase, parameterized.TestCase): - """Tests for CausalPoolingLayer.""" - - @parameterized.named_parameters( - { - 'testcase_name': 'max_pooling', - 'pooling_type': 'MAX', - 'left_context': 2, - 'inputs': np.array([-2, 0, 2, 4, 0, 0]), - 'input_paddings': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output': np.array([-2, 0, 2, 4, 0, 0]), - 'expected_output_padding': np.array([0, 0, 0, 0, 1, 1]), - }, { - 'testcase_name': 'avg_pooling', - 'pooling_type': 'AVG', - 'left_context': 2, - 'inputs': np.array([-2, 0, 2, 4, 0, 0]), - 'input_paddings': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output': np.array([-2, -1, 1, 3, 0, 0]), - 'expected_output_padding': np.array([0, 0, 0, 0, 1, 1]), - }, { - 'testcase_name': 'max_pooling_large_window', - 'pooling_type': 'MAX', - 'left_context': 10, - 'inputs': np.array([-2, 0, 2, 4, 0, 0]), - 'input_paddings': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output': np.array([-2, 0, 2, 4, 0, 0]), - 'expected_output_padding': np.array([0, 0, 0, 0, 1, 1]), - }, { - 'testcase_name': 'avg_pooling_large_window', - 'pooling_type': 'AVG', - 'left_context': 10, - 'inputs': np.array([-2, 0, 2, 4, 0, 0]), - 'input_paddings': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output': np.array([-2, -1, 0, 1, 0, 0]), - 'expected_output_padding': np.array([0, 0, 0, 0, 1, 1]), - }, { - 'testcase_name': 'avg_pooling_infinte_window', - 'pooling_type': 'AVG', - 'left_context': -1, - 'inputs': np.array([-2, 0, 2, 4, 0, 0]), - 'input_paddings': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output': np.array([-2, -1, 0, 1, 0, 0]), - 'expected_output_padding': np.array([0, 0, 0, 0, 1, 1]), - }) - def testSimpleCase(self, pooling_type, left_context, inputs, input_paddings, - expected_output, expected_output_padding): - inputs = inputs[np.newaxis, :, np.newaxis, np.newaxis] - input_paddings = input_paddings[np.newaxis, :] - param = conv_layers_builder.CausalPoolingLayer.Params().Set( - name='test_layer', pooling_type=pooling_type, left_context=left_context) - pooling_layer = param.Instantiate() - with self.session(use_gpu=True) as sess: - inputs = tf.convert_to_tensor(inputs, dtype=tf.float32) - input_paddings = tf.convert_to_tensor(input_paddings, dtype=tf.float32) - output, output_paddings = pooling_layer.FPropDefaultTheta( - inputs, input_paddings) - tf.global_variables_initializer().run() - output_val, output_paddings_val = sess.run([output, output_paddings]) - - self.assertAllClose(expected_output, output_val.flatten()) - self.assertAllEqual(expected_output_padding, output_paddings_val.flatten()) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/conv_layers_with_time_padding.py b/lingvo/core/conv_layers_with_time_padding.py deleted file mode 100644 index 0992e9df..00000000 --- a/lingvo/core/conv_layers_with_time_padding.py +++ /dev/null @@ -1,800 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Common conv layers. - -WARNING: Strided convolutions are buggy. Consider using v2_padding=True. -""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import bn_layers -from lingvo.core import py_utils -from lingvo.core import symbolic -from lingvo.core import tshape - - -# Yummy! -def ComputeConvOutputShape(in_shape, - t_stride, - f_stride, - outc=None, - padding='SAME'): - """Computes output shape for convolution and pooling layers. - - If `in_shape` is a dynamic shape, the output will be Tensors, while if - `in_shape` is a list of ints then the output will also be a list of ints. - - Args: - in_shape: A length 4 Tensor or list representing the input shape. - t_stride: The stride along the time dimension. - f_stride: The stride along the frequency dimension. - outc: The expected output channel. If None, will use the input channel. - padding: 'SAME' or 'VALID'. - - Returns: - The expected output shape. - """ - # In the order of batch, time, frequency, channel - n = in_shape[0] - t = in_shape[1] - f = in_shape[2] - c = in_shape[3] - # Last two dimensions has to be specified. - assert f is not None and c is not None - if padding == 'VALID': - if t: - t -= t_stride - 1 - f -= f_stride - 1 - ot = t - if ot is not None: - ot = (ot + t_stride - 1) // t_stride - of = (f + f_stride - 1) // f_stride - if outc is None: - outc = c - return [n, ot, of, outc] - - -def ComputeConvOutputPadding(paddings, - window, - stride, - padding_algorithm='SAME', - v2_padding=False): - """Computes paddings for convolution and pooling output. - - WARNING: This implementation is buggy prefer using ComputeConvOutputPaddingV2. - - out_padding[i] == 1 iff any in_padding corresponding to that output is 1. - - Args: - paddings: The paddings tensor. It is expected to be of shape [batch, time]. - window: The size of the windows. - stride: The time-stride between adjacent windows. - padding_algorithm: 'SAME' or 'VALID'. - v2_padding: Prefer setting to True. The default implementation is buggy for - strided convolutions. - - Returns: - out_padding, The new padding tensor of size [batch, ceil(time / stride)]. - """ - if v2_padding: - return _ComputeConvOutputPaddingV2(paddings, window, stride, - padding_algorithm) - - if stride == 1: - return paddings - - # Pad so input_length divides stride. - input_length = py_utils.GetShape(paddings)[1] - pad_len = (input_length + stride - 1) // stride * stride - input_length - paddings = tf.pad(paddings, [[0, 0], [0, pad_len]], constant_values=1.0) - out_padding = tf.nn.pool( - tf.expand_dims(paddings, -1), - [window], - 'MAX', - padding=padding_algorithm, - strides=[stride], - ) - return tf.squeeze(out_padding, -1) - - -def _ComputeConvOutputPaddingV2(paddings, - window, - stride, - padding_algorithm='SAME'): - """Computes paddings for convolution and pooling output. - - - If padding_algorithm='SAME': out_padding[i] == 0 if the in_padding - corresponding to that output is 0. This prevents the output from shrinking - unnecessarily when striding. - - If padding algorithm='VALID': out_padding[i] == 1 iff any in_padding - corresponding to that output is 1. - - Args: - paddings: The paddings tensor. It is expected to be of shape [batch, time]. - window: The size of the windows. - stride: The time-stride between adjacent windows. - padding_algorithm: 'SAME' or 'VALID'. - - Returns: - out_padding, The new padding tensor of size [batch, ceil(time / stride)]. - """ - if stride == 1 and padding_algorithm == 'SAME': - return paddings - - paddings, slice_len = _PadForLengthCompatibleStridesV2( - paddings, stride, padding_algorithm, 1.0) - - expanded_paddings = tf.expand_dims(paddings, -1) - - if padding_algorithm == 'SAME': - # Using a strided conv1d of size 1x1 we find all non-padded positions for - # the specified stride. - out_paddings = tf.nn.conv1d( - expanded_paddings, - filters=tf.ones([1, 1, 1], paddings.dtype), - stride=stride, - padding='SAME', - name='padding_conv') - elif padding_algorithm == 'VALID': - out_paddings = tf.nn.pool( - expanded_paddings, [window], - 'MAX', - padding=padding_algorithm, - strides=[stride]) - out_paddings = tf.squeeze(out_paddings, -1) - if stride > 1: - slice_end = py_utils.GetShape(out_paddings)[1] - slice_len - out_paddings = out_paddings[:, :slice_end] - return out_paddings - - -def _PadForLengthCompatibleStridesV2(tensor, stride, padding_algorithm, - constant_values): - """Pads tensor to make strided convolutions start in the first position. - - Tensorflow strided convolutions and Lingvo paddings are incompatible. - Strided convolutions always end at the last index of the length dimension. - Therefore, the output of a Lingvo padded tensor depends on the length - dimension. Here we remove this dependency by pre-padding the tensor so that - the first convolution starts in the first position. - - Args: - tensor: The tensor to prepare for convolution. [batch, time, ...]. - stride: The stride in the length dimension. - padding_algorithm: 'SAME' or 'VALID'. - constant_values: Value to pad 0. for data tensor and 1.0 for padding tensor. - - Returns: - A tuple (tensor, padded_length) where tensor is the potentionally padded - tensor and padded_length is the number paddings. - """ - if padding_algorithm == 'VALID': - return tensor, 0 - - input_length = py_utils.GetShape(tensor)[1] - pad_len = ((input_length // stride) + 1) * stride - 1 - input_length - if pad_len == 0: - return tensor, 0 - tensor = py_utils.PadSequenceDimension(tensor, input_length + pad_len, - constant_values) - return tensor, pad_len - - -class BaseConv2DLayerWithPadding(base_layer.BaseLayer): - """Abstract base class for 2D convolution layers. - - WARNING: Strided convolutions are buggy. Prefer using v2_padding=True. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'filter_shape', (0, 0, 0, 0), - 'Filter shape. Must be a sequence of length 4. Elements are in' - ' the order of height (time), width (frequency), in_channel,' - ' out_channel. For causal convolution, filter_shape[0]' - ' is the actual number of trained weights in the time dimension' - ' of the kernel.') - p.Define( - 'filter_stride', (1, 1), - 'Filter stride to use. Must be a pair of ints. The first int' - ' specifies the stride on the time dimension. The second int' - ' specifies the stride on the frequency dimension.') - p.Define( - 'dilation_rate', (1, 1), - 'If > 1, dilation rate for atrous convolution. ' - 'Must be a pair of ints. ' - 'The first int specifies the dilation rate on the time dimension. ' - 'The second int specifies the dilation rate on the frequency ' - 'dimension. ' - 'If any value of dilation_rate is > 1, then all values of strides ' - 'must be 1.') - p.Define( - 'weight_norm', False, - 'If true, apply weight normalization to weights as proposed by' - ' Salimans and Kingma, 2016: https://arxiv.org/abs/1602.07868') - p.Define( - 'partial_conv', False, 'If true, rescale positions near sequence' - 'boundaries as proposed in https://arxiv.org/abs/1811.11718') - p.Define( - 'v2_padding', False, 'Prefer setting to True. The default ' - 'implementation is incorrect for strided convolutions.') - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert len(p.filter_shape) == 4 - assert len(p.filter_stride) == 2 - assert all(x > 0 for x in p.filter_stride) - assert len(p.dilation_rate) == 2 - assert all(x > 0 for x in p.dilation_rate) - # Dilation and stride can't be combined. - if any(x > 1 for x in p.dilation_rate): - assert all(x == 1 for x in p.filter_stride) - - @classmethod - def OutputChannels(cls, p): - """The number of output channels for this conv layer.""" - raise NotImplementedError() - - @property - def output_channels(self): - return self.OutputChannels(self.params) - - @property - def input_channels(self): - """The number of input channels for this conv layer.""" - return self.params.filter_shape[2] - - @property - def filter_stride(self): - return self.params.filter_stride - - def OutShape(self, in_shape): - """Compute the output shape given the input shape.""" - p = self.params - return ComputeConvOutputShape(in_shape, p.filter_stride[0], - p.filter_stride[1], self.output_channels) - - def FProp(self, theta, inputs, paddings): - """Apply convolution to inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. It is expected to be of shape [batch, time, - frequency, channel]. The time dimension corresponds to the height - dimension as in images and the frequency dimension corresponds to the - width dimension as in images. - paddings: The paddings tensor, expected to be of shape [batch, time]. - - Returns: - outputs, out_paddings pair. - """ - p = self.params - with tf.name_scope(p.name): - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(paddings), [-1, -1]), - py_utils.assert_shape_match( - tf.shape(inputs), - tf.concat([ - tf.shape(paddings), - [-1, symbolic.ToStatic(self.input_channels)] - ], 0)) - ], inputs) - - def _ApplyPadding(tensor_in, padding_in): - padding_expanded = tf.expand_dims(tf.expand_dims(padding_in, -1), -1) - return tensor_in * (1.0 - padding_expanded) - - # Zeroing out padded inputs. - inputs = _ApplyPadding(inputs, paddings) - - # Apply conv on 'inputs'. - if p.v2_padding: - padded_inputs, slice_len = _PadForLengthCompatibleStridesV2( - inputs, p.filter_stride[0], 'SAME', 0.) - out = self._ApplyConv(theta, padded_inputs) - if p.filter_stride[0] > 1: - slice_end = py_utils.GetShape(out)[1] - slice_len - out = out[:, :slice_end, :, :] - else: - out = self._ApplyConv(theta, inputs) - - if p.partial_conv: - out = self._RescaleBoundary(out, paddings) - # NOTE: this may be slightly inaccurate when p.dilation_rate[0] > 1. - # But there's likely no real problems. Trying to set it gives an error: - # pooling with SAME padding is not implemented for dilation_rate > 1. - # implementation. Consider updating it to be the actual shape. - if p.v2_padding: - conv_padding = _ComputeConvOutputPaddingV2( - paddings, window=p.filter_shape[0], stride=p.filter_stride[0]) - else: - conv_padding = ComputeConvOutputPadding( - paddings, window=p.filter_stride[0], stride=p.filter_stride[0]) - - # Assuming padded nodes will be properly zero-ed out if necessary by - # sub-sequent layers. - # out = _ApplyPadding(out, conv_padding) - out = py_utils.HasShape( - out, symbolic.ToStatic(self.OutShape(tf.shape(inputs)))) - return out, conv_padding - - def _RescaleBoundary(self, out, in_paddings): - # Rescale every output position by: - # (# input positions) / (# non-padding input positions) - # where (# input posisions) = filter_size. - p = self.params - in_mask = 1.0 - in_paddings - - # Compute the left and right implicity padding size used in 'SAME' mode. - filter_t = p.filter_shape[0] - effective_filter_size = (filter_t - 1) * p.dilation_rate[0] + 1 - left_pad_size = (effective_filter_size - 1) // 2 - right_pad_size = effective_filter_size // 2 - - # Compute the rescaling factor. - # This expanded tensor has 1 on all valid positions, 0 on all padded ones, - # which include both explicit padding provided by 'in_padding', and implicit - # padding on boundaries. - in_mask_padded = tf.pad(in_mask, [[0, 0], [left_pad_size, right_pad_size]]) - # (# non-padding input positions) / (# input positions) - factor_inverse = tf.nn.pool( - in_mask_padded[:, :, tf.newaxis], - window_shape=(filter_t,), - pooling_type='AVG', - strides=(p.filter_stride[0],), - padding='VALID', - dilations=(p.dilation_rate[0],)) - - factor = tf.math.reciprocal_no_nan(factor_inverse) - return out * factor[..., tf.newaxis] - - def _ApplyConv(self, theta, conv_input): - return self._EvaluateConvKernel(theta, conv_input) - - def _EvaluateConvKernel(self, theta, conv_input): - """Evaluate the convolution kernel on input 'conv_input'.""" - raise NotImplementedError - - -class Conv2DLayerWithPadding(BaseConv2DLayerWithPadding): - """Conv2D layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('bias', False, 'Whether or not to apply a bias before activation.') - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w_pc = py_utils.WeightParams( - shape=p.filter_shape, - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w', w_pc) - if p.weight_norm: - self.CreateVariable( - 'g', - py_utils.WeightParams( - shape=[p.filter_shape[-1]], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - if p.bias: - # NOTE(jiahuiyu): bias is subject to LP regularization in this version. - self.CreateVariable( - 'b', - py_utils.WeightParams( - shape=[self.output_channels], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - - @classmethod - def OutputChannels(cls, p): - """The number of output channels for this conv layer.""" - return p.filter_shape[-1] - - def _GetWeight(self, theta): - p = self.params - if p.weight_norm: - # Normalize along the last dim (standard conv). - filter_w = tf.nn.l2_normalize(theta.w, [0, 1, 2]) * tf.reshape( - (theta.g + 1.0), [1, 1, 1, p.filter_shape[-1]]) - else: - filter_w = theta.w - return filter_w - - def _ApplyConv(self, theta, conv_input): - out = self._EvaluateConvKernel(theta, conv_input) - p = self.params - if p.bias: - out = tf.nn.bias_add(out, theta.b) - return out - - def _EvaluateConvKernel(self, theta, inputs): - """Apply convolution to inputs.""" - p = self.params - filter_w = self._GetWeight(theta) - return tf.nn.convolution( - inputs, - filter_w, - strides=p.filter_stride, - dilations=p.dilation_rate, - data_format='NHWC', - padding='SAME') - - -class CausalConv2DLayerWithPadding(Conv2DLayerWithPadding): - """2D conv layer with causal dependency on the time axis.""" - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.filter_shape[1] == 1, 'Only 1d causal convolution is supported.' - - def _EvaluateConvKernel(self, theta, inputs): - """Apply convolution to inputs.""" - p = self.params - assert p.filter_shape[1] == 1, 'Only 1D causal convolutions supported.' - # Use VALID padding and shift the inputs to the right to ensure that the - # first output only depends on the first input and so on. The output is - # the same size as the input, as if the convolution used SAME padding. - padding_algorithm = 'VALID' - # The effective spatial filter width for dilated convolutions is - # (kernel_width - 1) * dilation_rate + 1 as according to - # https://www.tensorflow.org/api_docs/python/tf/nn/convolution. - causal_pad_size = (p.filter_shape[0] - 1) * p.dilation_rate[0] - inputs = tf.pad(inputs, [[0, 0], [causal_pad_size, 0], [0, 0], [0, 0]]) - - filter_w = self._GetWeight(theta) - return tf.nn.convolution( - inputs, - filter_w, - strides=p.filter_stride, - dilations=p.dilation_rate, - data_format='NHWC', - padding=padding_algorithm) - - -class DepthwiseConv2DLayer(BaseConv2DLayerWithPadding): - """Depthwise conv 2D layer. - - paper: https://arxiv.org/abs/1610.02357 - """ - - @classmethod - def Params(cls): - p = super().Params() - # Redefine 'filter_shape' since the semantic of shape elements is different - # from regular Conv2D. - p.Delete('filter_shape') - p.Define( - 'filter_shape', (0, 0, 0, 0), - 'Filter shape. Must be a sequence of length 4. Elements are in' - ' the order of height (time), width (frequency), in_channel,' - ' channel_multipliers. ') - p.Define('bias', False, 'Whether or not to apply a bias before activation.') - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w_pc = py_utils.WeightParams( - shape=p.filter_shape, - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - self.CreateVariable('w', w_pc) - if p.weight_norm: - self.CreateVariable( - 'g', - py_utils.WeightParams( - shape=[p.filter_shape[2], p.filter_shape[3]], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - if p.bias: - # NOTE(jiahuiyu): bias is subject to LP regularization in this version. - self.CreateVariable( - 'b', - py_utils.WeightParams( - shape=[self.output_channels], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - - @classmethod - def OutputChannels(cls, p): - """The number of output channels for this conv layer.""" - # Depthwise convolution filter shape is: - # [..., in_channels, channel_multiplier]. - return p.filter_shape[2] * p.filter_shape[3] - - def _GetWeight(self, theta): - p = self.params - if p.weight_norm: - # Normalize along the last two dims. - filter_w = tf.nn.l2_normalize(theta.w, [0, 1]) * tf.reshape( - (theta.g + 1.0), [1, 1, p.filter_shape[2], p.filter_shape[3]]) - else: - filter_w = theta.w - return filter_w - - def _ApplyConv(self, theta, conv_input): - out = self._EvaluateConvKernel(theta, conv_input) - p = self.params - if p.bias: - out = tf.nn.bias_add(out, theta.b) - return out - - def _EvaluateConvKernel(self, theta, inputs): - """Apply convolution to inputs.""" - p = self.params - filter_w = self._GetWeight(theta) - return tf.nn.depthwise_conv2d( - inputs, - filter_w, - strides=[1, p.filter_stride[0], p.filter_stride[1], 1], - dilations=p.dilation_rate, - data_format='NHWC', - padding='SAME') - - -class CausalDepthwiseConv2DLayer(DepthwiseConv2DLayer): - """Depthwise conv layer with causal dependency on the time axis.""" - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.filter_shape[1] == 1, 'Only 1d causal convolution is supported.' - - def _EvaluateConvKernel(self, theta, inputs): - """Apply convolution to inputs.""" - p = self.params - assert p.filter_shape[1] == 1, 'Only 1D causal convolutions supported.' - # Use VALID padding and shift the inputs to the right to ensure that the - # first output only depends on the first input and so on. The output is - # the same size as the input, as if the convolution used SAME padding. - padding_algorithm = 'VALID' - # The effective spatial filter width for dilated convolutions is - # (kernel_width - 1) * dilation_rate + 1 as according to - # https://www.tensorflow.org/api_docs/python/tf/nn/convolution. - causal_pad_size = (p.filter_shape[0] - 1) * p.dilation_rate[0] - inputs = tf.pad(inputs, [[0, 0], [causal_pad_size, 0], [0, 0], [0, 0]]) - filter_w = self._GetWeight(theta) - return tf.nn.depthwise_conv2d( - inputs, - filter_w, - strides=[1, p.filter_stride[0], p.filter_stride[1], 1], - dilations=p.dilation_rate, - data_format='NHWC', - padding=padding_algorithm) - - -class NormalizedDepthwiseConv2DLayer(DepthwiseConv2DLayer): - """DepthwiseConv2DLayer where weights are normalized over the time dim. - - https://arxiv.org/abs/1901.10430 - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('dropconnect_prob', 0.0, - 'Prob at which DropConnect regularization is performed.') - p.Define('deterministic_dropout', False, 'Use determnisitc dropout or not.') - p.Define('temperature', 1.0, - 'Temperature for the softmax normalization of the weights.') - p.Define('weight_tiling_factor', 1, - 'Number of times weights are tiled over the input channels.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.filter_shape[1] == 1, 'Only 1d convolution is supported.' - assert p.temperature > 0.0, 'Absolute zero temperature is not possible.' - - @classmethod - def OutputChannels(cls, p): - """The number of output channels for this conv layer.""" - # Depthwise convolution filter shape is: - # [kernel_size, 1, in_channels, channel_multiplier]. - return p.filter_shape[2] * p.filter_shape[3] * p.weight_tiling_factor - - @property - def input_channels(self): - """The number of output channels for this conv layer.""" - p = self.params - return p.filter_shape[2] * p.weight_tiling_factor - - def _GetWeight(self, theta): - p = self.params - filter_w = theta.w - - # First normalize filter_w over the temporal dimension here. - filter_w = tf.nn.softmax(filter_w / p.temperature, axis=0) - - # Add dropconnect on the weights for regularization. - if p.dropconnect_prob > 0.0 and not self.do_eval: - if p.deterministic_dropout: - filter_w = py_utils.DeterministicDropout( - filter_w, 1.0 - p.dropconnect_prob, - py_utils.GenerateStepSeedPair(p, theta.global_step)) - else: - filter_w = tf.nn.dropout( - filter_w, rate=p.dropconnect_prob, seed=p.random_seed) - - # Tie the parameters of every subsequent number of weight_tiling_factor - # channels. - filter_w = tf.tile(filter_w, [1, 1, p.weight_tiling_factor, 1]) - return filter_w - - @classmethod - def FPropMeta(cls, p, inputs, paddings): - py_utils.CheckShapes((inputs, paddings)) - b, t, f, _ = inputs - assert f == 1 - oc = p.filter_shape[2] * p.filter_shape[3] * p.weight_tiling_factor - outputs = tshape.Shape([b, t, f, oc]) - flops = b * t * f * p.filter_shape[0] * oc * 5 - return py_utils.NestedMap(flops=flops, out_shapes=(outputs, paddings)) - - -class CausalNormalizedDepthwiseConv2DLayer(NormalizedDepthwiseConv2DLayer): - """Depthwise conv layer with causal dependency on the time axis.""" - - def _EvaluateConvKernel(self, theta, inputs): - """Apply convolution to inputs.""" - # Same as CausalDepthwiseConv2DLayer. - p = self.params - assert p.filter_shape[1] == 1, 'Only 1D causal convolutions supported.' - padding_algorithm = 'VALID' - causal_pad_size = (p.filter_shape[0] - 1) * p.dilation_rate[0] - inputs = tf.pad(inputs, [[0, 0], [causal_pad_size, 0], [0, 0], [0, 0]]) - filter_w = self._GetWeight(theta) - return tf.nn.depthwise_conv2d( - inputs, - filter_w, - strides=[1, p.filter_stride[0], p.filter_stride[1], 1], - dilations=p.dilation_rate, - data_format='NHWC', - padding=padding_algorithm) - - -class ConvBatchNormLayer(bn_layers.BatchNormLayer): - """A wrapper around regular BatchNormLayer that pass around the ... - - paddings layers. - """ - - def FProp(self, theta, inputs, paddings): - paddings_expanded = tf.expand_dims(tf.expand_dims(paddings, -1), -1) - bned = super().FProp(theta, inputs, paddings_expanded) - return bned, paddings - - -class ConvCategoricalBN(bn_layers.CategoricalBN): - """A wrapper around regular CategoricalBN that pass around the ... - - paddings layers. - """ - - def FProp(self, theta, inputs, paddings, class_emb): - paddings_expanded = tf.expand_dims(tf.expand_dims(paddings, -1), -1) - bned = super().FProp(theta, inputs, paddings_expanded, class_emb) - return bned, paddings - - -# Supported activation functions. -_ACTIVATIONS = { - 'RELU': tf.nn.relu, - 'RELU6': tf.nn.relu6, - 'SIGMOID': tf.sigmoid, - 'TANH': tf.tanh, - 'SWISH': tf.nn.swish, - 'NONE': tf.identity, -} - - -class ActivationLayer(base_layer.BaseLayer): - """Applies activation function to the inputs.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('activation', 'RELU', 'The activation function to apply') - return p - - def FProp(self, theta, inputs, paddings): - p = self.params - out = _ACTIVATIONS[p.activation](inputs) - return out, paddings - - -class PaddingLayer(base_layer.BaseLayer): - """Zeros out padded positions.""" - - def FProp(self, theta, inputs, paddings): - paddings_expanded = tf.expand_dims(tf.expand_dims(paddings, -1), -1) - return inputs * (1.0 - paddings_expanded), paddings - - -class GlobalPoolingLayer(base_layer.BaseLayer): - """Padding aware global pooling.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('pooling_type', 'MAX', 'Pooling type: MAX|AVG') - return p - - def FProp(self, theta, inputs, paddings): - """Apply global spatial pooling to inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. It is expected to be of shape [batch, time, - frequency, channel]. The time dimension corresponds to the height - dimension as in images and the frequency dimension corresponds to the - width dimension as in images. - paddings: The paddings tensor. It is expected to be of shape [batch, - time]. Defaults to None, which means there no paddings. - - Returns: - outputs, out_paddings pair. - - outputs: has shape [batch, 1, 1, channel]. - - out_paddings: None or has shape [batch, 1]. - """ - p = self.params - assert p.pooling_type in ['MAX', 'AVG'], p.pooling_type - b, t, f = py_utils.GetShape(inputs, ndims=3) - - if paddings is not None: - paddings = py_utils.HasShape(paddings, [b, t]) - - if paddings is not None: - mask = 1.0 - paddings[..., tf.newaxis, tf.newaxis] - else: - mask = tf.ones([b, t, 1, 1], p.dtype) - if p.pooling_type == 'AVG': - global_sum = tf.reduce_sum(inputs * mask, axis=[1, 2], keepdims=True) - f = tf.cast(tf.convert_to_tensor(f), p.dtype) - count = f * tf.reduce_sum(mask, axis=[1, 2], keepdims=True) - out_feature = global_sum / tf.maximum(1.0, count) - elif p.pooling_type == 'MAX': - large_negative = ( - tf.ones_like(inputs) * p.dtype.max * tf.constant(-0.7, dtype=p.dtype)) - padded_inputs = tf.where_v2(mask > 0.0, inputs, large_negative) - out_feature = tf.reduce_max(padded_inputs, axis=[1, 2], keepdims=True) - if paddings is None: - out_paddings = None - else: - out_paddings = tf.reduce_min(paddings, axis=1, keepdims=True) - out_feature *= 1.0 - out_paddings[..., tf.newaxis, tf.newaxis] - return out_feature, out_paddings diff --git a/lingvo/core/conv_layers_with_time_padding_test.py b/lingvo/core/conv_layers_with_time_padding_test.py deleted file mode 100644 index 70d384b4..00000000 --- a/lingvo/core/conv_layers_with_time_padding_test.py +++ /dev/null @@ -1,723 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.core.conv_layers.""" - -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import conv_layers_with_time_padding as conv_layers -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core import tshape -import numpy as np - - -class ConvLayerTest(parameterized.TestCase, test_utils.TestCase): - """Tests conv layers. - - Note that there are multiple subclasses of BaseConv2DLayer and most cases - are tested via the concrete Conv2DLayer. Other tests are done against - other subclasses to cover key differences. - """ - - def testConv2DLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = conv_layers.Conv2DLayerWithPadding.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - _ = params.Instantiate() - conv_vars = tf.get_collection('Conv2DLayerWithPadding_vars') - conv_var_names = [x.name for x in conv_vars] - expected_var_names = ['conv/w/var:0'] - self.assertEqual(expected_var_names, conv_var_names) - - def testConv2DLayerWithPaddingOutputChannels(self): - with self.session(): - params = conv_layers.Conv2DLayerWithPadding.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - actual_output_channels = params.cls.OutputChannels(params) - self.assertEqual(32, actual_output_channels) - - @parameterized.parameters(False, True) - def testConv2DLayerOutShape(self, v2_padding): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = conv_layers.Conv2DLayerWithPadding.Params() - params.v2_padding = v2_padding - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - conv_layer = params.Instantiate() - in_shape = [None, None, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, None, 5, 32]) - in_shape = [None, 20, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, 10, 5, 32]) - - # ComputeConvOutputPadding is broken for strided convolutions. Below we mark - # cases that are in correct with "Bug". testComputeConvOutputPaddingV2 below - # has the same test cases but without the bugs. - @parameterized.parameters( - ([0, 0, 0, 1], [0, 0, 0, 1], 1, 'SAME'), - ([0, 0, 0, 0], [0, 0], 2, 'SAME'), - ([0, 0, 0, 1], [0, 1], 2, 'SAME'), - ([0, 0, 1, 1], [1, 1], 2, 'SAME'), # Bug - ([0, 0, 0, 0, 0], [0, 0, 1], 2, 'SAME'), # Bug - ([0, 0, 0, 0, 1], [0, 1, 1], 2, 'SAME'), # Bug - ([0, 0, 0, 1, 1], [0, 1, 1], 2, 'SAME'), # Bug - ([0, 0, 1, 1, 1], [1, 1, 1], 2, 'SAME'), # Bug - ([0, 0, 0, 0, 0, 0], [0, 0, 0], 2, 'SAME'), - ([0, 0, 0, 0, 0, 1], [0, 0, 1], 2, 'SAME'), - ([0, 0, 0, 0, 1, 1], [0, 1, 1], 2, 'SAME'), # Bug - ([0, 0, 0, 1, 1, 1], [0, 1, 1], 2, 'SAME'), - ([0, 0, 1, 1, 1, 1], [1, 1, 1], 2, 'SAME'), - ([0, 0, 0, 0], [0, 0, 0, 0], 1, 'VALID'), # Bug - ([0, 0, 0, 1], [0, 0, 0, 1], 1, 'VALID'), # Bug - ([0, 0, 0, 0], [0], 2, 'VALID'), - ([0, 0, 0, 1], [0], 2, 'VALID'), - ([0, 0, 1, 1], [1], 2, 'VALID'), - ([0, 0, 0, 0, 0], [0, 0], 2, 'VALID'), - ([0, 0, 0, 0, 1], [0, 1], 2, 'VALID'), - ([0, 0, 0, 1, 1], [0, 1], 2, 'VALID'), - ([0, 0, 1, 1, 1], [1, 1], 2, 'VALID'), - ([0, 0, 0, 0, 0, 0], [0, 0], 2, 'VALID'), - ([0, 0, 0, 0, 0, 1], [0, 0], 2, 'VALID'), - ([0, 0, 0, 0, 1, 1], [0, 1], 2, 'VALID'), - ([0, 0, 0, 1, 1, 1], [0, 1], 2, 'VALID'), - ([0, 0, 1, 1, 1, 1], [1, 1], 2, 'VALID'), - ) - def testComputeConvOutputPadding(self, padding, expected_padding, stride, - padding_algorithm): - """Tests padding behavior. There are multiple bugs in the implementation.""" - padding = tf.constant([padding], tf.float32) - expected_padding = tf.constant([expected_padding], tf.float32) - with self.session(use_gpu=True): - conv_padding = conv_layers.ComputeConvOutputPadding( - padding, window=3, stride=stride, padding_algorithm=padding_algorithm) - self.evaluate(tf.global_variables_initializer()) - conv_padding = py_utils.Debug(conv_padding) - conv_padding = self.evaluate(conv_padding) - tf.logging.info('expected_padding {expected_padding}') - self.assertAllClose(expected_padding, conv_padding) - - @parameterized.parameters( - ([0, 0, 0, 1], [0, 0, 0, 1], 1, 'SAME'), - ([0, 0, 0, 0], [0, 0], 2, 'SAME'), - ([0, 0, 0, 1], [0, 0], 2, 'SAME'), - ([0, 0, 1, 1], [0, 1], 2, 'SAME'), - ([0, 0, 0, 0, 0], [0, 0, 0], 2, 'SAME'), - ([0, 0, 0, 0, 1], [0, 0, 1], 2, 'SAME'), - ([0, 0, 0, 1, 1], [0, 0, 1], 2, 'SAME'), - ([0, 0, 1, 1, 1], [0, 1, 1], 2, 'SAME'), - ([0, 0, 0, 0, 0, 0], [0, 0, 0], 2, 'SAME'), - ([0, 0, 0, 0, 0, 1], [0, 0, 0], 2, 'SAME'), - ([0, 0, 0, 0, 1, 1], [0, 0, 1], 2, 'SAME'), - ([0, 0, 0, 1, 1, 1], [0, 0, 1], 2, 'SAME'), - ([0, 0, 1, 1, 1, 1], [0, 1, 1], 2, 'SAME'), - ([0, 0, 0, 0], [0, 0], 1, 'VALID'), - ([0, 0, 0, 1], [0, 1], 1, 'VALID'), - ([0, 0, 0, 0], [0], 2, 'VALID'), - ([0, 0, 0, 1], [0], 2, 'VALID'), - ([0, 0, 1, 1], [1], 2, 'VALID'), - ([0, 0, 0, 0, 0], [0, 0], 2, 'VALID'), - ([0, 0, 0, 0, 1], [0, 1], 2, 'VALID'), - ([0, 0, 0, 1, 1], [0, 1], 2, 'VALID'), - ([0, 0, 1, 1, 1], [1, 1], 2, 'VALID'), - ([0, 0, 0, 0, 0, 0], [0, 0], 2, 'VALID'), - ([0, 0, 0, 0, 0, 1], [0, 0], 2, 'VALID'), - ([0, 0, 0, 0, 1, 1], [0, 1], 2, 'VALID'), - ([0, 0, 0, 1, 1, 1], [0, 1], 2, 'VALID'), - ([0, 0, 1, 1, 1, 1], [1, 1], 2, 'VALID'), - ) - def testComputeConvOutputPaddingV2(self, padding, expected_padding, stride, - padding_algorithm): - """Test Convolution padding computation.""" - padding = tf.constant([padding], tf.float32) - expected_padding = tf.constant([expected_padding], tf.float32) - with self.session(use_gpu=True): - conv_padding = conv_layers._ComputeConvOutputPaddingV2( - padding, window=3, stride=stride, padding_algorithm=padding_algorithm) - self.evaluate(tf.global_variables_initializer()) - conv_padding = py_utils.Debug(conv_padding) - conv_padding = self.evaluate(conv_padding) - tf.logging.info('expected_padding {expected_padding}') - self.assertAllClose(expected_padding, conv_padding) - - @parameterized.parameters(5, 6) - def testConv2DLayerStridedWithPaddingFProp(self, seq_len): - """Check strided convs get the same values for different length dim.""" - # TODO(isaace): THIS TEST SHOWS THAT THERE IS A BUG IN THE CODE. - with self.session(use_gpu=True): - batch_size = 3 - expected_seq_len = 3 - - params = conv_layers.Conv2DLayerWithPadding.Params() - params.weight_norm = False - params.filter_stride = [2, 2] - params.name = 'conv' - params.filter_shape = [3, 3, 1, 1] - params.params_init = py_utils.WeightInit.Constant(1.0) - conv_layer = params.Instantiate() - - # Set up the padding for the sequence length. (starting at 5). - in_padding = tf.constant([ - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 1], - [0, 0, 0, 1, 1], - ], tf.float32) - in_padding = tf.pad( - in_padding, [[0, 0], [0, seq_len - 5]], constant_values=1.0) - - inputs = 1.0 + tf.tile( - tf.reshape(tf.range(seq_len, dtype=tf.float32), [1, seq_len, 1, 1]), - [batch_size, 1, 3, 1]) - inputs = py_utils.ApplyPadding( - tf.reshape(in_padding, [batch_size, seq_len, 1, 1]), inputs) - - inputs = py_utils.Debug(inputs) - - output, out_padding = conv_layer.FPropDefaultTheta(inputs, in_padding) - - output = py_utils.Debug(output) - out_padding = py_utils.Debug(out_padding) - - self.evaluate(tf.global_variables_initializer()) - output, out_padding = self.evaluate([output, out_padding]) - - self.assertEqual((batch_size, expected_seq_len, 2, 1), output.shape) - self.assertAllClose([ - [0, 0, 1], - [0, 0, 1], - [0, 1, 1], - ], out_padding) - - # This here shows a bug in the implementation; the output should be the - # same. Also there are bugs with the output not having the correct - # padding. - if seq_len == 5: - self.assertAllClose([ - [[[6], [6]], [[18], [18]], [[18], [18]]], - [[[6], [6]], [[18], [18]], [[8], [8]]], - [[[6], [6]], [[10], [10]], [[0], [0]]], - ], output) - elif seq_len == 6: - self.assertAllClose([ - [[[12], [12]], [[24], [24]], [[10], [10]]], - [[[12], [12]], [[14], [14]], [[0], [0]]], - [[[12], [12]], [[6], [6]], [[0], [0]]], - ], output) - else: - raise ValueError('Test does not handle length {seq_len}') - - @parameterized.parameters(5, 6) - def testConv2DLayerStridedWithPaddingFPropV2(self, seq_len): - """Check strided convs get the same values for different seq_len.""" - with self.session(use_gpu=True): - batch_size = 3 - expected_seq_len = 3 - - params = conv_layers.Conv2DLayerWithPadding.Params() - params.v2_padding = True - params.weight_norm = False - params.filter_stride = [2, 2] - params.name = 'conv' - params.filter_shape = [3, 3, 1, 1] - params.params_init = py_utils.WeightInit.Constant(1.0) - conv_layer = params.Instantiate() - - # Set up the padding for the sequence length. (starting at 5). - in_padding = tf.constant([ - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 1], - [0, 0, 0, 1, 1], - ], tf.float32) - in_padding = tf.pad( - in_padding, [[0, 0], [0, seq_len - 5]], constant_values=1.0) - - inputs = 1.0 + tf.tile( - tf.reshape(tf.range(seq_len, dtype=tf.float32), [1, seq_len, 1, 1]), - [batch_size, 1, 3, 1]) - inputs = py_utils.ApplyPadding( - tf.reshape(in_padding, [batch_size, seq_len, 1, 1]), inputs) - - inputs = py_utils.Debug(inputs) - - output, out_padding = conv_layer.FPropDefaultTheta(inputs, in_padding) - - output = py_utils.Debug(output) - out_padding = py_utils.Debug(out_padding) - - self.evaluate(tf.global_variables_initializer()) - output, out_padding = self.evaluate([output, out_padding]) - - self.assertEqual((batch_size, expected_seq_len, 2, 1), output.shape) - self.assertAllClose([ - [0, 0, 0], - [0, 0, 1], - [0, 0, 1], - ], out_padding) - - # Explanation of some computations (0s are padded) - # 6 = (1*0 + 1*0 + 1*0) + (1*0 + 1*1 + 1*1) + (1*0 + 1*2 + 1*2) - # 18 = (1*1 + 1*1 + 1*0) + (1*3 + 1*3 + 1*0) + (1*5 + 1*5 + 1*0) - self.assertAllClose( - [ - [[[6], [6]], [[18], [18]], [[18], [18]]], - [[[6], [6]], [[18], [18]], [[8], [8]]], # NOTE: Not padded. - [[[6], [6]], [[10], [10]], [[0], [0]]], - ], - output) - - def testConv2DLayerWithPaddingFPropRandom(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - - params = conv_layers.Conv2DLayerWithPadding.Params() - params.weight_norm = True - params.filter_stride = [2, 2] - params.name = 'conv' - params.filter_shape = [3, 3, 3, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - conv_layer = params.Instantiate() - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 3]), dtype=tf.float32) - output, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - out_sum = tf.reduce_sum(output) - out_sum_squared = tf.reduce_sum(output * output) - self.evaluate(tf.global_variables_initializer()) - v1, v2 = self.evaluate([out_sum, out_sum_squared]) - tf.logging.info('actual = %f, %f', v1, v2) - self.assertAllClose([-0.293671, 4.198602], [v1, v2]) - - @parameterized.parameters(5, 6) - def testCausalConv2DLayerStridedWithPaddingFProp(self, seq_len): - """Check strided convs get the same values for different length dim.""" - # TODO(isaace): THIS TEST SHOWS THAT THERE IS A BUG WITH PADDING - with self.session(use_gpu=True): - batch_size = 5 - expected_seq_len = 3 - - params = conv_layers.CausalConv2DLayerWithPadding.Params() - params.weight_norm = False - params.filter_stride = [2, 2] - params.name = 'conv' - params.filter_shape = [3, 1, 1, 1] - params.params_init = py_utils.WeightInit.Constant(1.0) - conv_layer = params.Instantiate() - - # Set up the padding for the sequence length. (starting at 5). - in_padding = tf.constant([ - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 1], - [0, 0, 0, 1, 1], - [0, 0, 1, 1, 1], - [0, 1, 1, 1, 1], - ], tf.float32) - in_padding = tf.pad( - in_padding, [[0, 0], [0, seq_len - 5]], constant_values=1.0) - - inputs = 1.0 + tf.tile( - tf.reshape(tf.range(seq_len, dtype=tf.float32), [1, seq_len, 1, 1]), - [batch_size, 1, 3, 1]) - inputs = py_utils.ApplyPadding( - tf.reshape(in_padding, [batch_size, seq_len, 1, 1]), inputs) - - inputs = py_utils.Debug(inputs) - - output, out_padding = conv_layer.FPropDefaultTheta(inputs, in_padding) - - output = py_utils.Debug(output) - out_padding = py_utils.Debug(out_padding) - - self.evaluate(tf.global_variables_initializer()) - output, out_padding = self.evaluate([output, out_padding]) - - self.assertEqual((batch_size, expected_seq_len, 2, 1), output.shape) - self.assertAllClose([ - [0, 0, 1], - [0, 0, 1], - [0, 1, 1], - [0, 1, 1], - [1, 1, 1], - ], out_padding) - - # NOTE: There is a bug in the output not being padded correctly. - self.assertAllClose([ - [[[1], [1]], [[6], [6]], [[12], [12]]], - [[[1], [1]], [[6], [6]], [[7], [7]]], - [[[1], [1]], [[6], [6]], [[3], [3]]], - [[[1], [1]], [[3], [3]], [[0], [0]]], - [[[1], [1]], [[1], [1]], [[0], [0]]], - ], output) - - @parameterized.parameters(5, 6) - def testCausalConv2DLayerStridedWithPaddingFPropV2(self, seq_len): - """Check strided convs get the same values for different length dim.""" - with self.session(use_gpu=True): - batch_size = 5 - expected_seq_len = 3 - - params = conv_layers.CausalConv2DLayerWithPadding.Params() - params.v2_padding = True - params.weight_norm = False - params.filter_stride = [2, 2] - params.name = 'conv' - params.filter_shape = [3, 1, 1, 1] - params.params_init = py_utils.WeightInit.Constant(1.0) - conv_layer = params.Instantiate() - - # Set up the padding for the sequence length. (starting at 5). - in_padding = tf.constant([ - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 1], - [0, 0, 0, 1, 1], - [0, 0, 1, 1, 1], - [0, 1, 1, 1, 1], - ], tf.float32) - in_padding = tf.pad( - in_padding, [[0, 0], [0, seq_len - 5]], constant_values=1.0) - - inputs = 1.0 + tf.tile( - tf.reshape(tf.range(seq_len, dtype=tf.float32), [1, seq_len, 1, 1]), - [batch_size, 1, 3, 1]) - inputs = py_utils.ApplyPadding( - tf.reshape(in_padding, [batch_size, seq_len, 1, 1]), inputs) - - inputs = py_utils.Debug(inputs) - - output, out_padding = conv_layer.FPropDefaultTheta(inputs, in_padding) - - output = py_utils.Debug(output) - out_padding = py_utils.Debug(out_padding) - - self.evaluate(tf.global_variables_initializer()) - output, out_padding = self.evaluate([output, out_padding]) - - self.assertEqual((batch_size, expected_seq_len, 2, 1), output.shape) - self.assertAllClose([ - [0, 0, 0], - [0, 0, 1], - [0, 0, 1], - [0, 1, 1], - [0, 1, 1], - ], out_padding) - - self.assertAllClose( - [ - [[[1], [1]], [[6], [6]], [[12], [12]]], - [[[1], [1]], [[6], [6]], [[7], [7]]], - [[[1], [1]], [[6], [6]], [[3], [3]]], # NOTE: not padded. - [[[1], [1]], [[3], [3]], [[0], [0]]], - [[[1], [1]], [[1], [1]], [[0], [0]]], - ], - output) - - def testCausalConv2DLayerWithPaddingFPropRandom(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - - params = (conv_layers.CausalConv2DLayerWithPadding.Params()) - params.weight_norm = True - params.filter_stride = [2, 2] - params.name = 'conv' - params.filter_shape = [2, 1, 3, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - conv_layer = params.Instantiate() - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 3]), dtype=tf.float32) - output, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - out_sum = tf.reduce_sum(output) - out_sum_squared = tf.reduce_sum(output * output) - self.evaluate(tf.global_variables_initializer()) - v1, v2 = self.evaluate([out_sum, out_sum_squared]) - tf.logging.info('actual = %f, %f', v1, v2) - self.assertAllClose([-3.584711, 3.324082], [v1, v2]) - - def testDepthwiseConv2DLayerOutputChannels(self): - with self.session(): - params = conv_layers.DepthwiseConv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 2] - params.bias = True - actual_output_channels = params.cls.OutputChannels(params) - self.assertEqual(6, actual_output_channels) - - def testDepthwiseConv2DLayerFProp(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - - params = conv_layers.DepthwiseConv2DLayer.Params() - params.weight_norm = True - params.filter_stride = [2, 2] - params.name = 'conv' - params.filter_shape = [3, 3, 3, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - conv_layer = params.Instantiate() - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 3]), dtype=tf.float32) - output, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - out_sum = tf.reduce_sum(output) - out_sum_squared = tf.reduce_sum(output * output) - self.evaluate(tf.global_variables_initializer()) - v1, v2 = self.evaluate([out_sum, out_sum_squared]) - tf.logging.info('actual = %f, %f', v1, v2) - self.assertAllClose([-1.455162, 6.813269], [v1, v2]) - - def testCausalDepthwiseConv2DLayer(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - - params = conv_layers.CausalDepthwiseConv2DLayer.Params() - params.weight_norm = True - params.filter_stride = [2, 2] - params.name = 'conv' - params.filter_shape = [2, 1, 3, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = params.Instantiate() - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 3]), dtype=tf.float32) - output, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - self.evaluate(tf.global_variables_initializer()) - out_sum = tf.reduce_sum(output) - out_sum_squared = tf.reduce_sum(output * output) - self.evaluate(tf.global_variables_initializer()) - v1, v2 = self.evaluate([out_sum, out_sum_squared]) - tf.logging.info('actual = %f, %f', v1, v2) - self.assertAllClose([-2.031689, 7.911201], [v1, v2]) - - def testActivationLayer(self): - with self.session(use_gpu=True): - p = conv_layers.ActivationLayer.Params() - p.name = 'act' - l = p.Instantiate() - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 3]), dtype=tf.float32) - in_padding = tf.zeros([2, 4], dtype=tf.float32) - out, out_padding = l.FProp(l.theta, inputs, in_padding) - self.evaluate(tf.global_variables_initializer()) - v1, v2 = self.evaluate([out, out_padding]) - print(v1, v2) - - def _testNormalizedDepthwiseConv2DHelper(self, - is_causal=False, - dropconnect_prob=0): - if is_causal: - conv_cls = (conv_layers.CausalNormalizedDepthwiseConv2DLayer) - else: - conv_cls = conv_layers.NormalizedDepthwiseConv2DLayer - tf.random.set_seed(398847392) - np.random.seed(12345) - params = conv_cls.Params().Set( - name='conv', - weight_tiling_factor=2, - filter_shape=[3, 1, 2, 1], - dropconnect_prob=dropconnect_prob, - deterministic_dropout=True) - conv_layer = params.Instantiate() - in_padding = tf.zeros([2, 4], dtype=tf.float32) - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 1, 4]), dtype=tf.float32) - output, _ = conv_layer.FPropDefaultTheta(inputs, in_padding) - return output - - def testNormalizedDepthwiseConv2DLayerOutputChannels(self): - with self.session(): - params = (conv_layers.NormalizedDepthwiseConv2DLayer.Params()) - params.name = 'conv' - params.filter_shape = [3, 1, 2, 1] - params.weight_tiling_factor = 2 - actual_output_channels = params.cls.OutputChannels(params) - self.assertEqual(4, actual_output_channels) - - def testNormalizedDepthwiseConv2DLayerFPropMeta(self): - params = (conv_layers.NormalizedDepthwiseConv2DLayer.Params()) - params.name = 'conv' - params.filter_shape = [3, 1, 2, 1] - params.weight_tiling_factor = 2 - batch, time, frequency, in_channel = 2, 4, 1, 4 - output_channels = 4 - inputs_shape = tshape.Shape([batch, time, frequency, in_channel]) - paddings_shape = tshape.Shape([batch, time]) - with self.session(): - out = params.cls.FPropMeta(params, inputs_shape, paddings_shape) - expected_flops = batch * time * frequency * params.filter_shape[ - 0] * output_channels * 5 - self.assertEqual(expected_flops, out.flops) - out_shapes = out.out_shapes - self.assertEqual(out_shapes[0].ToTensorShape().as_list(), - [batch, time, frequency, output_channels]) - self.assertEqual(out_shapes[1].ToTensorShape().as_list(), [batch, time]) - - def testNormalizedDepthwiseConv2DLayerFProp(self): - expected_output = [[0.91136134, 1.25781929, 1.76708317, 0.9021343], - [0.52296412, 0.7703352, 0.65711987, 0.23177178]] - with self.session(use_gpu=True): - output = self._testNormalizedDepthwiseConv2DHelper() - output_sum = tf.squeeze(tf.reduce_sum(output, -1)) - self.evaluate(tf.global_variables_initializer()) - output_sum_val = self.evaluate(output_sum) - self.assertAllClose(expected_output, output_sum_val) - - def testCausalNormalizedDepthwiseConv2DLayerFProp(self): - expected_output = [[0.00819603, 0.91136134, 1.25781929, 1.76708317], - [-0.07673456, 0.52296412, 0.7703352, 0.65711987]] - with self.session(use_gpu=True): - output = self._testNormalizedDepthwiseConv2DHelper(is_causal=True) - output_sum = tf.squeeze(tf.reduce_sum(output, -1)) - self.evaluate(tf.global_variables_initializer()) - output_sum_val = self.evaluate(output_sum) - self.assertAllClose(expected_output, output_sum_val) - - def testNormalizedDepthwiseConv2DLayerBackProp(self): - with self.session(use_gpu=True) as sess: - output = self._testNormalizedDepthwiseConv2DHelper(dropconnect_prob=0.1) - loss = tf.reduce_sum(output) - all_vars = tf.trainable_variables() - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v) for v in all_vars - ] - for sg, ng in zip(sym_grads, num_grads): - self.assertAllClose(sg, ng, rtol=1e-02, atol=1e-02) - - def testCausualNormalizedDepthwiseConv2DLayerBackProp(self): - with self.session(use_gpu=True) as sess: - output = self._testNormalizedDepthwiseConv2DHelper( - is_causal=True, dropconnect_prob=0.1) - loss = tf.reduce_sum(output) - all_vars = tf.trainable_variables() - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v) for v in all_vars - ] - for sg, ng in zip(sym_grads, num_grads): - self.assertAllClose(sg, ng, rtol=1e-02, atol=1e-02) - - -class GlobalPoolingLayerTest(test_utils.TestCase): - """Tests for GlobalPoolingLayer.""" - - def _testHelper(self, - pooling_type, - inputs, - input_paddings, - expected_output, - expected_output_padding, - feed_dict=None): - param = conv_layers.GlobalPoolingLayer.Params().Set( - name='test_layer', pooling_type=pooling_type) - pooling_layer = param.Instantiate() - with self.session(use_gpu=True) as sess: - inputs = tf.convert_to_tensor(inputs, dtype=tf.float32) - input_paddings = None if input_paddings is None else tf.convert_to_tensor( - input_paddings, dtype=tf.float32) - output, output_paddings = pooling_layer.FPropDefaultTheta( - inputs, input_paddings) - self.evaluate(tf.global_variables_initializer()) - if input_paddings is None: - self.assertIsNone(output_paddings) - output_val = sess.run(output, feed_dict=feed_dict) - else: - output_val, output_paddings_val = sess.run([output, output_paddings], - feed_dict=feed_dict) - - self.assertAllClose(expected_output, output_val) - if input_paddings is not None: - self.assertAllEqual(expected_output_padding, output_paddings_val) - - def testPooling(self): - inputs = np.random.random([3, 5, 2, 4]) - 0.5 - expected_avg_output = np.mean(inputs, axis=(1, 2), keepdims=True) - expected_max_output = np.amax(inputs, axis=(1, 2), keepdims=True) - self._testHelper('AVG', inputs, None, expected_avg_output, None) - self._testHelper('MAX', inputs, None, expected_max_output, None) - - def testPoolingWithPadding(self): - inputs = np.random.random([4, 3, 2, 4]) - 0.5 - paddings = np.array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 1, 1]]) - expected_paddings = np.array([[0], [0], [0], [1]]) - expected_avg_output = np.array([ - np.mean(inputs[0][:3], axis=(0, 1), keepdims=True), - np.mean(inputs[1][:2], axis=(0, 1), keepdims=True), - np.mean(inputs[2][:1], axis=(0, 1), keepdims=True), - np.zeros((1, 1, 4)) - ]) - expected_max_output = np.array([ - np.amax(inputs[0][:3], axis=(0, 1), keepdims=True), - np.amax(inputs[1][:2], axis=(0, 1), keepdims=True), - np.amax(inputs[2][:1], axis=(0, 1), keepdims=True), - np.zeros((1, 1, 4)) - ]) - - self._testHelper('AVG', inputs, paddings, expected_avg_output, - expected_paddings) - self._testHelper('MAX', inputs, paddings, expected_max_output, - expected_paddings) - - def testPoolingWithUnknowShapeInput(self): - """Tests GlobalPooling layer with unknown shape tensor.""" - - def remove_shape(tensor): - shape = tf.placeholder(tf.int32, name='removed_shape') - return tf.reshape(tensor, shape) - - g = tf.Graph() - with g.as_default(), tf.Session(graph=g) as _: - tf.random.set_seed(24332) - input_shape = [3, 5, 2, 4] - inputs = np.random.random(input_shape) - 0.5 - expected_avg_output = np.mean(inputs, axis=(1, 2), keepdims=True) - input_tensor = tf.convert_to_tensor(inputs, dtype=tf.float32) - # initial shape is [3, 5, 2, 4] - self.assertEqual(py_utils.GetShape(input_tensor), input_shape) - # remove shape using a tf Defun and verify dynamic tensor shape. - input_tensor = remove_shape(input_tensor) - self.assertIsInstance(py_utils.GetShape(input_tensor), tf.Tensor) - self.assertIsNone(input_tensor.shape.rank) - self._testHelper( - 'AVG', - input_tensor, - None, - expected_avg_output, - None, - feed_dict={'removed_shape:0': input_shape}) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/datasource.py b/lingvo/core/datasource.py deleted file mode 100644 index 49710e97..00000000 --- a/lingvo/core/datasource.py +++ /dev/null @@ -1,409 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""DataSources describe how files should be used to provide data.""" - -import os - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils - - -class DataSource(base_layer.BaseLayer): - """A base class for file based Data Sources.""" - - @classmethod - def Params(cls): - return super().Params().Set(name='datasource') - - def __init__(self, params): - super().__init__(params) - self.SetVariableFree() - - def BuildDataSource(self, data_source_from_file_pattern_fn): - """Builds a data source. - - Subclasses implement this. - - Args: - data_source_from_file_pattern_fn: a function that takes file_pattern and - input_source_weights as arguments and returns an input batch from a - string file_pattern. - - Returns: - A NestedMap containing - - - data: (Required) a tuple of tf.Tensor or `.NestedMap` of tf.Tensor same - as ``BaseInputGeneratorFromFiles._DataSourceFromFilePattern( - file_pattern, input_source_weights=None)`` - - source_selected: (Optional) a tensor of size - [batch_size, number of datasources] - - selected_bprop: (Optional) a tensor of size [number of data sources] - - bprop_variable_filters: (Optional) containing a list of bprop_variable - filters for each source. - """ - raise NotImplementedError() - - -class SimpleDataSource(DataSource): - """A simple file based data source.""" - - @classmethod - def Params(cls): - p = super().Params() - # TODO(b/139345706): move filetype prefix (eg tfrecord:) into its own param - # and clean up existing usages. - p.Define( - 'file_pattern', '', 'A single file pattern string which can ' - 'contain a single file pattern, or a comma separated list of patterns.' - 'Samples from each file with unspecified likelihood, though in practice' - ' this will be roughly equal per file. To explicitly ' - 'describe the mixture weights of different file patterns use ' - 'WithinBatchMixingDataSource or CrossBatchMixingDataSource') - p.Define('file_type', '', 'A file type, such as `tfrecord`.') - - return p - - def BuildDataSource(self, data_source_from_file_pattern_fn): - """Builds a simple, unweighted Data Source. - - Args: - data_source_from_file_pattern_fn: a function that takes file_pattern as an - argument and returns an input batch. - - Returns: - A NestedMap containing `data`, which is a tuple of tf.Tensor or - `.NestedMap` of tf.Tensor. - """ - p = self.params - if not isinstance(p.file_pattern, str): - raise ValueError('SimpleDataSource expects p.file_pattern to be a string.' - ' To use multiple files use a comma separated string, ' - 'e.g. \', \'.join(list_of_file_patterns)') - - if p.file_type: - file_pattern = '{}:{}'.format(p.file_type, p.file_pattern) - else: - file_pattern = p.file_pattern - - ret = py_utils.NestedMap() - ret.data = data_source_from_file_pattern_fn(file_pattern) - ret.bprop_variable_filters = [''] - return ret - - -class ChainingDataSource(DataSource): - """A data source that reads each file_pattern in sequence.""" - - @classmethod - def Params(cls): - p = super().Params() - # TODO(b/139345706): This can probably be a list of DataSource params - # instead of a list of file_patterns to be more generic. - p.Define( - 'file_patterns', [], 'A list of file pattern strings which are read ' - 'from in sequence. Commas cannot be used in individual file_patterns.') - return p - - def BuildDataSource(self, data_source_from_file_pattern_fn): - """Builds a Chaining Data Source. - - Args: - data_source_from_file_pattern_fn: a function that takes file_pattern as an - argument and returns an input batch. - - Returns: - A NestedMap containing `data`, which is a tuple of tf.Tensor or - `.NestedMap` of tf.Tensor. - - Raises: - ValueError: If unknown token type. - """ - p = self.params - if not isinstance(p.file_patterns, list): - raise ValueError('Expected a list, got %s' % p.file_patterns) - if not all(isinstance(x, str) for x in p.file_patterns): - # Chaining doesn't work with weights or backprop filters, i.e. when - # file_pattern param contains a list of - # tuples. - raise ValueError('Expected a list of strings, got %s' % p.file_patterns) - - for file_pattern in p.file_patterns: - if ',' in file_pattern: - raise ValueError(('Can not use commas in file_pattern when chaining ' - 'is used. file_pattern: %s') % file_pattern) - ret = py_utils.NestedMap() - ret.data = data_source_from_file_pattern_fn(','.join(p.file_patterns)) - ret.bprop_variable_filters = [''] * len(p.file_patterns) - return ret - - -class WithinBatchMixingDataSource(DataSource): - """Mixes records from different sources into the same batch.""" - - @classmethod - def Params(cls): - p = super().Params() - # TODO(b/139345706): This can probably be a list of DataSource params - # instead of a list of file_patterns to be more generic. - p.Define( - 'file_patterns', [], 'A list of file pattern strings which are read ' - 'from in sequence. Commas cannot be used in individual file_patterns. ') - p.Define('weights', [], 'A list of weights for each file pattern') - return p - - def BuildDataSource(self, data_source_from_file_pattern_fn): - """Read and return input batch from p.file_patterns list weighted by p.weights. - - Examples in the batch will be mixed together from different file_pattern - source proportionally to the weights. - - Args: - data_source_from_file_pattern_fn: a function that takes file_pattern and - input_source_weights as arguments and returns an input batch from a - string file_pattern. - - Returns: - A NestedMap containing: data: a tuple of tf.Tensor or `.NestedMap` of - tf.Tensor - - Raises: - ValueError: If unknown token type. - """ - p = self.params - if not isinstance(p.file_patterns, list): - raise ValueError('Expected a list, got %s' % p.file_patterns) - if not isinstance(p.weights, list): - raise ValueError('Expected a list, got %s' % p.weights) - if len(p.file_patterns) != len(p.weights): - raise ValueError( - 'Expected p.file_patterns and p.weights to be the same length. ' - 'Found %d file_patterns, and %d weights' % - (len(p.file_patterns), len(p.weights))) - # TODO(rosenberg) confirm that weights are numeric - if not all(isinstance(x, str) for x in p.file_patterns): - raise ValueError('Expected all elements of p.file_patterns to be strings') - - file_patterns = p.file_patterns - weights = p.weights - for file_pattern in file_patterns: - if ',' in file_pattern: - raise ValueError( - ('Can not use commas in file_pattern when within-batch ' - 'mixing is used. file_pattern: %s') % file_pattern) - ret = py_utils.NestedMap() - ret.data = data_source_from_file_pattern_fn( - ','.join(file_patterns), input_source_weights=weights) - ret.bprop_variable_filters = [''] * len(file_patterns) - return ret - - -class CrossBatchMixingDataSource(DataSource): - """Mixes batches from different sources, each batch from only one source.""" - - @classmethod - def Params(cls): - p = super().Params() - # TODO(b/139345706): This can probably be a list of DataSource params - # instead of a list of file_patterns to be more generic. - p.Define( - 'file_patterns', [], 'A list of file pattern strings which are read ' - 'from in sequence. Commas cannot be used in individual file_patterns. ') - p.Define('weights', [], 'A list of weights for each file pattern') - p.Define( - 'bprop_variable_filters', [], 'An optional list of ' - 'bprop_variariable_filters for each file_pattern. If not empty, ' - 'expected to have the same length as file_pattern and weights') - return p - - def BuildDataSource(self, data_source_from_file_pattern_fn): - """Read and return input batch from a p.file_pattern list. - - `p.file_patterns` is a list of file patterns, `p.weights` contains - weights for each file pattern. If provided `p.bprop_variable_filters` - includes a bprop_variable_filter for each file pattern. - - Args: - data_source_from_file_pattern_fn: a function that takes file_pattern as an - argument and returns an input batch. - - Returns: - A NestedMap containing: - data: a tuple of tf.Tensor or `.NestedMap` of tf.Tensor - source_selected: a tensor of size [batch_size, number of data sources] - selected_bprop: a tensor of size [number of data sources] - bprop_variable_filters: containing a list of bprop_variable filters for - each source - - Raises: - ValueError: If unknown token type. - """ - p = self.params - - def _MakeDataSourceFromFilePatternFunc(data_source_from_file_pattern_fn, - file_pattern): - # It's important to invoke self._DataSourceFromFilePattern() inside the - # lambda to make sure that the record is drawn from data source - # only if it will be used. Weights are handled by MixByWeight, not the - # data_source_from_file_pattern_fn. - return lambda: data_source_from_file_pattern_fn(file_pattern) - - if len(p.weights) != len(p.file_patterns): - raise ValueError( - 'Expected p.file_patterns and p.weights to be the same length. ' - 'Found %d file_patterns, and %d weights' % - (len(p.file_patterns), len(p.weights))) - if not all(isinstance(x, str) for x in p.file_patterns): - raise ValueError('Expected all elements of p.file_patterns to be strings') - - # TODO(rosenberg) replace this with functools.partial - inputs = [ - _MakeDataSourceFromFilePatternFunc(data_source_from_file_pattern_fn, - file_pattern) - for file_pattern in p.file_patterns - ] - weights = p.weights - if not p.bprop_variable_filters: - bprop_variable_filters = [''] * len(inputs) - else: - bprop_variable_filters = p.bprop_variable_filters - - data_source, selected_bprop = py_utils.MixByWeight( - inputs, weights, seed=p.random_seed) - # TODO(neerajgaur): Remove _bprop_onehot and change code that uses it to - # use source_selected from input_batch. - batch_size = py_utils.GetShape(tf.nest.flatten(data_source)[0])[0] - ret = py_utils.NestedMap() - ret.data = data_source - ret.bprop_variable_filters = bprop_variable_filters - ret.selected_bprop = selected_bprop - ret.source_selected = tf.tile( - tf.expand_dims(selected_bprop, 0), [batch_size, 1]) - return ret - - -class CurriculumDataSource(DataSource): - """A data source that reads different DataSources in stages. - - Supports multiple stages of training, where each stage yields batches for - based on global step boundaries. The contents per stage are defined by nested - DataSources. - - Boundaries are defined by the training global step. - - Currently bprop_variable_filters are not_supported. - # TODO(rosenberg) support bprop_variable_filter within CurriculumDataSource - The issue here is that by conditioning on tf.Variable global_step, the - bprop_variable_filter from the selected DataSource is itself a tf.Variable. - Other bprop_variable_filters are python strings. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'datasource_params', [], 'A list of DataSource Params which define ' - 'the DataSource curriculum.') - p.Define( - 'boundaries', [], 'A list of global step thresholds determining when ' - 'to move from one training stage to another.') - p.Define( - 'bprop_variable_filters', [''], 'A list of bprop_variable_filters to ' - 'apply during training. NOTE: these are constant across all stages.' - 'Changing variable filters per stage is not supported.') - return p - - def BuildDataSource(self, data_source_from_file_pattern_fn): - """Read and return input batch. - - Args: - data_source_from_file_pattern_fn: a function to read and return input - batch from a string file_pattern - - Returns: - A NestedMap containing: - data: a tuple of tf.Tensor or `.NestedMap` of tf.Tensor - - Raises: - ValueError: inconsistent sizes between boundaries and datasource_params, - specification of unsupported datasources, or out of order boundaries. - """ - p = self.params - - if len(p.datasource_params) != len(p.boundaries) + 1: - raise ValueError( - 'Expected p.datasource_params to have one more entry than ' - 'p.boundaries. Found %d datasource_params, and %d boundaries' % - (len(p.datasource_params), len(p.boundaries))) - - for ds_p in p.datasource_params: - if 'bprop_variable_filters' in ds_p: - if any(filter for filter in ds_p.bprop_variable_filters): - raise ValueError('CurriculumDataSource does not support distinct ' - 'bprop_variable_filters per stage.') - - for idx in range(len(p.boundaries) - 1): - if p.boundaries[idx] > p.boundaries[idx + 1]: - raise ValueError('Expected p.boundaries to monotonically increase, but ' - 'found %d > %d at position %d' % - (p.boundaries[idx], p.boundaries[idx + 1], idx)) - - global_step = py_utils.GetGlobalStep() - datasources = [ds_p.Instantiate() for ds_p in p.datasource_params] - - def GetDatasourceFn(idx): - - def DatasourceFn(): - datasource = datasources[idx].BuildDataSource( - data_source_from_file_pattern_fn) - datasource.pop('bprop_variable_filters', None) - return datasource - - return DatasourceFn - - cases = [] - for idx in range(len(p.boundaries)): - cases.append( - (tf.less(global_step, - tf.constant(p.boundaries[idx], - dtype=global_step.dtype)), GetDatasourceFn(idx))) - - ret = tf.case(cases, default=GetDatasourceFn(-1)) - ret.bprop_variable_filters = p.bprop_variable_filters - return ret - - -class PrefixedDataSource(SimpleDataSource): - """Prepends path prefix to file patterns.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'file_pattern_prefix', '', - 'Prefix to add to file_pattern, eg. a base directory that contains ' - 'dataset files.') - - return p - - def __init__(self, params): - super().__init__(params) - - p = self.params - - patterns = p.file_pattern.split(',') - p.file_pattern = ','.join( - os.path.join(p.file_pattern_prefix, pattern) for pattern in patterns) diff --git a/lingvo/core/datasource_test.py b/lingvo/core/datasource_test.py deleted file mode 100644 index 1aa2337f..00000000 --- a/lingvo/core/datasource_test.py +++ /dev/null @@ -1,296 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.core.datasource.""" - -import lingvo.compat as tf - -from lingvo.core import datasource -from lingvo.core import py_utils -from lingvo.core import test_utils - - -def _MockDataSourceFromFilePattern(file_pattern, input_source_weights=None): - """Read and return input batch from a string file_pattern. - - Args: - file_pattern: A string file pattern. - input_source_weights: A list of float input source weights to control input - example mix in the batch. The records will be sampled from inputs - proportionally to these weights. Defaults to None which should be treated - as an empty list. - - Returns: - file_pattern. This is the file_pattern that will be sent to InputOp along - with input_source_weights and other parameters to generate a batch. - """ - del input_source_weights # Unused. - return tf.constant([file_pattern]) - - -class DatasourceTest(test_utils.TestCase): - - def testSimpleDataSourceSucceedsWithStringInput(self): - ds_params = datasource.SimpleDataSource.Params().Set( - file_pattern='path_to_file') - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertCountEqual( - sorted(ret.keys()), ['bprop_variable_filters', 'data']) - self.assertAllEqual(ret.data, [[b'path_to_file']]) - - def testSimpleDataSourceSucceedsWithFileType(self): - ds_params = datasource.SimpleDataSource.Params().Set( - file_pattern='pattern1,pattern2', file_type='tfrecord') - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertAllEqual(ret.data, [[b'tfrecord:pattern1,pattern2']]) - - def testSimpleDataSourceFailsWithListInput(self): - files = ['file1', 'file2'] - ds_params = datasource.SimpleDataSource.Params().Set(file_pattern=files) - - ds = ds_params.Instantiate() - with self.assertRaises(ValueError): - ds.BuildDataSource(_MockDataSourceFromFilePattern) - - def testChainingDataSourceSucceedsWithListInput(self): - files = ['path_to_file1', 'path_to_file2'] - ds_params = datasource.ChainingDataSource.Params().Set(file_patterns=files) - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertCountEqual( - sorted(ret.keys()), ['bprop_variable_filters', 'data']) - self.assertAllEqual(ret.data, [[b'path_to_file1,path_to_file2']]) - self.assertCountEqual(ret.bprop_variable_filters, [''] * len(files)) - - def testChainingDataSourceFailsWithWeightedTupleListInput(self): - files = [('file1', 1.0), ('file2', 2.0)] - ds_params = datasource.ChainingDataSource.Params().Set(file_patterns=files) - ds = ds_params.Instantiate() - - with self.assertRaises(ValueError): - ds.BuildDataSource(_MockDataSourceFromFilePattern) - - def testWithinBatchMixingDataSourceSucceedsWithListFilesAndWeights(self): - files = ['path_to_file1', 'path_to_file2'] - weights = [1, 4] - ds_params = datasource.WithinBatchMixingDataSource.Params().Set( - file_patterns=files, weights=weights) - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertCountEqual( - sorted(ret.keys()), ['bprop_variable_filters', 'data']) - self.assertAllEqual(ret.data, [[b'path_to_file1,path_to_file2']]) - self.assertCountEqual(ret.bprop_variable_filters, [''] * len(files)) - - # TODO(b/139345706) should fail when the p.file_pattern behavior is deprecated - def testWithinBatchMixingDataSourceFailsWithListTuplesFiles(self): - # This legacy p.file_pattern behavior is only supported through - # base_input_generator. - files = [('file1', 1.0), ('file2', 2.0)] - ds_params = datasource.WithinBatchMixingDataSource.Params().Set( - file_patterns=files) - ds = ds_params.Instantiate() - - with self.assertRaises(ValueError): - ds.BuildDataSource(_MockDataSourceFromFilePattern) - - def testCrossBatchMixingDataSourceSucceedsWithListFilesAndWeights(self): - files = ['path_to_file', 'path_to_file'] - weights = [1, 4] - ds_params = datasource.CrossBatchMixingDataSource.Params().Set( - file_patterns=files, weights=weights) - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertCountEqual( - sorted(ret.keys()), - ['bprop_variable_filters', 'data', 'selected_bprop', 'source_selected']) - # CrossBatchMixing operates on the python side of the tf op so a single - # element will be returned by _MockDataSourceFromFilePattern - self.assertAllEqual(ret.data, [[b'path_to_file']]) - self.assertCountEqual(ret.bprop_variable_filters, [''] * len(files)) - self.assertAllEqual(ret.selected_bprop.shape, [2]) - self.assertAllEqual(ret.source_selected.shape, [1, 2]) - - def testCrossBatchMixingDataSourceFailsWithListTuplesFiles(self): - # This legacy p.file_pattern behavior is only supported through - # base_input_generator. - files = [('file1', 1.0), ('file2', 2.0)] - ds_params = datasource.CrossBatchMixingDataSource.Params().Set( - file_patterns=files) - ds = ds_params.Instantiate() - - with self.assertRaises(ValueError): - ds.BuildDataSource(_MockDataSourceFromFilePattern) - - def testCurriculumDataSourceSucceedsWithSimpleDataSource(self): - sources = [ - datasource.SimpleDataSource.Params().Set(file_pattern='file1'), - datasource.SimpleDataSource.Params().Set(file_pattern='file2'), - ] - ds_params = datasource.CurriculumDataSource.Params().Set( - datasource_params=sources, boundaries=[5]) - ds = ds_params.Instantiate() - - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - with tf.Session(): - self.evaluate(tf.global_variables_initializer()) - ret.data = self.evaluate([ret.data]) - - self.assertCountEqual( - sorted(ret.keys()), ['bprop_variable_filters', 'data']) - self.assertAllEqual(ret.data, [[b'file1']]) - self.assertCountEqual(ret.bprop_variable_filters, ['']) - - def testCurriculumDataSourceTransitionsCorrectlyWithSimpleDataSource(self): - sources = [ - datasource.SimpleDataSource.Params().Set(file_pattern='file1'), - datasource.SimpleDataSource.Params().Set(file_pattern='file2'), - ] - boundary = 5 - ds_params = datasource.CurriculumDataSource.Params().Set( - datasource_params=sources, boundaries=[boundary]) - ds = ds_params.Instantiate() - - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - with tf.Session(): - # Advance the global step to the next curriculum stage - global_step = py_utils.GetOrCreateGlobalStepVar() - self.evaluate(tf.global_variables_initializer()) - set_global_step = tf.assign(global_step, boundary, name='advance_step') - self.evaluate(set_global_step) - - ret.data = self.evaluate([ret.data]) - - self.assertCountEqual( - sorted(ret.keys()), ['bprop_variable_filters', 'data']) - self.assertAllEqual(ret.data, [[b'file2']]) - self.assertCountEqual(ret.bprop_variable_filters, ['']) - - def testCurriculumDataSourceTransitionsCorrectlyWithMixingDataSource(self): - sources = [ - datasource.WithinBatchMixingDataSource.Params().Set( - file_patterns=['file1', 'file2'], weights=[1, 5]), - datasource.WithinBatchMixingDataSource.Params().Set( - file_patterns=['file3', 'file4'], weights=[2, 3]) - ] - boundary = 5 - ds_params = datasource.CurriculumDataSource.Params().Set( - datasource_params=sources, boundaries=[boundary]) - ds = ds_params.Instantiate() - - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - with tf.Session(): - # Advance the global step to the next curriculum stage - global_step = py_utils.GetOrCreateGlobalStepVar() - self.evaluate(tf.global_variables_initializer()) - set_global_step = tf.assign(global_step, boundary, name='advance_step') - self.evaluate(set_global_step) - - ret.data = self.evaluate([ret.data]) - - self.assertCountEqual( - sorted(ret.keys()), ['bprop_variable_filters', 'data']) - self.assertAllEqual(ret.data, [[b'file3,file4']]) - self.assertCountEqual(ret.bprop_variable_filters, ['']) - - def testCurriculumDataSourceFailsWithBadBoundaries(self): - sources = [ - datasource.WithinBatchMixingDataSource.Params().Set( - file_patterns=['file1', 'file2'], weights=[1, 5]), - datasource.WithinBatchMixingDataSource.Params().Set( - file_patterns=['file3', 'file4'], weights=[2, 3]) - ] - ds_params = datasource.CurriculumDataSource.Params().Set( - datasource_params=sources, boundaries=[10, 5]) - ds = ds_params.Instantiate() - with self.assertRaises(ValueError): - ds.BuildDataSource(_MockDataSourceFromFilePattern) - - def testPrefixDataSourceSucceedsWithDirectory(self): - ds_params = datasource.PrefixedDataSource.Params().Set( - file_pattern='filename-*.tfrecord', - file_type=None, - file_pattern_prefix='/dir/') - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertAllEqual(ret.data, [[b'/dir/filename-*.tfrecord']]) - - def testPrefixDataSourceSucceedsWithMultiplePatterns(self): - ds_params = datasource.PrefixedDataSource.Params().Set( - file_pattern='filename-*.tfrecord,other/file/pattern/*', - file_type=None, - file_pattern_prefix='/dir/') - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertAllEqual( - ret.data, [[b'/dir/filename-*.tfrecord,/dir/other/file/pattern/*']]) - - def testPrefixDataSourceSucceedsWithGcsBucket(self): - ds_params = datasource.PrefixedDataSource.Params().Set( - file_pattern='filename-*.tfrecord', - file_type=None, - file_pattern_prefix='gs://bucket/dir') - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertAllEqual(ret.data, [[b'gs://bucket/dir/filename-*.tfrecord']]) - - def testPrefixDataSourceSucceedsWithFileType(self): - ds_params = datasource.PrefixedDataSource.Params().Set( - file_pattern='filename-*.tfrecord', - file_type='tfrecord', - file_pattern_prefix='dir') - ds = ds_params.Instantiate() - ret = ds.BuildDataSource(_MockDataSourceFromFilePattern) - - with tf.Session(): - ret.data = self.evaluate([ret.data]) - - self.assertAllEqual(ret.data, [[b'tfrecord:dir/filename-*.tfrecord']]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/decoder_lib.py b/lingvo/core/decoder_lib.py deleted file mode 100644 index 9b813b05..00000000 --- a/lingvo/core/decoder_lib.py +++ /dev/null @@ -1,25 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Helpers for the decoding phase of jobs.""" - -import pickle -import lingvo.compat as tf - - -def WriteKeyValuePairs(filename, key_value_pairs): - """Writes `key_value_pairs` to `filename`.""" - with tf.io.gfile.GFile(filename, mode='wb') as f: - pickle.dump(key_value_pairs, f, protocol=pickle.HIGHEST_PROTOCOL) diff --git a/lingvo/core/distillation_task.py b/lingvo/core/distillation_task.py deleted file mode 100644 index 8f0fb759..00000000 --- a/lingvo/core/distillation_task.py +++ /dev/null @@ -1,170 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Base class for tasks that implement knowledge distillation.""" -from lingvo import compat as tf -from lingvo.core import base_model -from lingvo.core import py_utils -from lingvo.core import schedule - - -class DistillationTask(base_model.BaseTask): - """A task to distill knowledge from a teacher task to a student task. - - The training parameters (e.g., learning rate) are determined only by - `DistillationTask.params.train`. Teacher and student task's training and eval - parameters must be set to None. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('teacher', None, 'The teacher task params.') - p.Define('student', None, 'The student task params.') - p.Define( - 'distillation_loss_weight', - # Only uses distillation loss by default. - schedule.ConstantOne.Params(), - 'A schedule of distillation loss weight. ' - 'The weight determines the fraction of total loss contributed by ' - 'distillation loss, while the rest loss will be computed against ' - 'the ground truth. ' - 'A weight of 0 means to only use ground-truth and ignore teacher ' - 'predictions, while a weight 1 means to only use teacher ' - 'predictions and ignore ground truth. ' - 'The weight is specified as a schedule to allow it to change ' - 'during training.') - p.Define( - 'teacher_target_type', 'truth', 'The target type for the teacher. ' - 'Choices are: ' - ' "truth": using the ground-truth target labels ' - ' "beam": using the 1-best hypothesis from the beam search.') - p.Define( - 'beam_search_temperature', 1.0, 'The temperature to scale the' - 'log-prob of each beam search hypothesis. This is used in ' - 'training only') - p.Define( - 'train_teacher', False, 'Adds the teacher\'s loss (w.r.t the ground ' - 'truth labels) to the overall ground truth loss. This can be used for ' - 'instance when the teacher is trained in parallel to the student.') - return p - - def __init__(self, params): - assert issubclass(params.cls, DistillationTask) - super().__init__(params) - - p = self.params - # While student does not need its own input generator for training, it - # needs an input generator for inference graphs. - p.student.input = p.input - # Teacher also might need an input generator, eg. for waveform_processor. - p.teacher.input = p.input - for child in ('teacher', 'student'): - child_p = getattr(p, child) - assert issubclass(child_p.cls, base_model.BaseTask) - assert child_p.train is None - assert child_p.eval is None - # In theory it's ok for teacher to be a DistillationTask. In practice - # it probably won't happen. - assert not issubclass(child_p.cls, DistillationTask) - child_p.name = child - self.CreateChild(child, child_p) - self.CreateChild('distillation_loss_weight', p.distillation_loss_weight) - - def ComputePredictions(self, theta, input_batch): - p = self.params - with tf.name_scope(p.name): - if p.teacher_target_type == 'truth': - teacher_predictions = self.teacher.ComputePredictions( - theta.teacher, input_batch) - student_predictions = self.student.ComputePredictions( - theta.student, input_batch) - return py_utils.NestedMap( - teacher=teacher_predictions, student=student_predictions) - elif p.teacher_target_type == 'beam': - (teacher_predictions, teacher_input_batch, - teacher_beam_prob) = self.teacher.ComputeBeamPredictions( - theta.teacher, input_batch, p.beam_search_temperature) - # We use 'teacher_input_batch' instead of 'input_batch' for 'student' - # because the training of student network uses target transcripts for - # the "teacher forcing" mode and here the target transcripts should come - # from the teacher's beam search. - student_predictions = self.student.ComputePredictions( - theta.student, teacher_input_batch) - return py_utils.NestedMap( - teacher=teacher_predictions, - student=student_predictions, - teacher_beam_prob=teacher_beam_prob) - else: - raise ValueError('teacher target type not defined properly: %s' % - self.p.teacher_target_type) - - def ComputeLoss(self, theta, predictions, input_batch): - p = self.params - per_example = {} - with tf.name_scope('groundtruth_loss'): - student_groundtruth_loss, student_groundtruth_per_example = ( - self.student.ComputeLoss(theta.student, predictions.student, - input_batch)) - groundtruth_loss = student_groundtruth_loss - groundtruth_loss['student_groundtruth_loss'] = ( - student_groundtruth_loss['loss']) - per_example.update(student_groundtruth_per_example) - - if p.train_teacher: - teacher_groundtruth_loss, _ = self.teacher.ComputeLoss( - theta.teacher, predictions.teacher, input_batch) - groundtruth_loss['teacher_groundtruth_loss'] = ( - teacher_groundtruth_loss['loss']) - # The new loss is the wighted sum of the teacher and student losses. - groundtruth_loss['loss'] = py_utils.WeightedAvg(*zip( - teacher_groundtruth_loss['loss'], student_groundtruth_loss['loss'])) - - with tf.name_scope('distillation_loss'): - distillation_loss, distill_per_example = self.ComputeDistillationLoss( - theta, predictions, input_batch) - distillation_loss['distillation_loss'] = distillation_loss['loss'] - per_example.update(distill_per_example) - - distillation_loss_weight = self.distillation_loss_weight.FProp( - theta.distillation_loss_weight, self.global_step) - metrics = py_utils.CombineMetrics([ - (groundtruth_loss, 1 - distillation_loss_weight), - (distillation_loss, distillation_loss_weight), - ]) - return metrics, per_example - - def ComputeDistillationLoss(self, theta, predictions, input_batch): - raise NotImplementedError('Abstract method') - - def BProp(self): - p = self.params - if p.train_teacher: - return super().BProp() - else: - # Only bprop on student variables. - self._BPropForVariables(self.student.vars) - - def Decode(self, input_batch): - return self.student.Decode(input_batch) - - def Inference(self): - return self.student.Inference() - - def CreateDecoderMetrics(self): - return self.student.CreateDecoderMetrics() - - def PostProcessDecodeOut(self, dec_out_dict, dec_metrics_dict): - return self.student.PostProcessDecodeOut(dec_out_dict, dec_metrics_dict) diff --git a/lingvo/core/distributed_shampoo.py b/lingvo/core/distributed_shampoo.py deleted file mode 100644 index e9a6da02..00000000 --- a/lingvo/core/distributed_shampoo.py +++ /dev/null @@ -1,668 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Implementation for distributed Shampoo optimizer.""" - -import functools -import lingvo.compat as tf -from lingvo.core import matrix_functions -from lingvo.core import ops as x_ops -import numpy as np - -# pylint: disable=g-direct-tensorflow-import -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import state_ops -from tensorflow.python.training import optimizer -# pylint: enable=g-direct-tensorflow-import - - -class PartitionConfig: - """Config for tensor partitioning.""" - - def __init__(self, max_dim_size, partition_size): - """Initialize the `PartitionConfig`. - - Args: - max_dim_size: Partitions dimensions with size greater than this value. - partition_size: Size of each partition - """ - self.max_dim_size = max_dim_size - self.partition_size = partition_size - - -class PartitionMetadata: - """Metadata for partitioning.""" - - def __init__(self, split_sizes_per_dim, num_splits_per_dim): - """Initialize the `PartitionMetadata`. - - Args: - split_sizes_per_dim: Split sizes per dimemsion. - num_splits_per_dim: Number of splits per dimension ( inferred from - split_sizes_per_dim). - """ - self.split_sizes_per_dim = split_sizes_per_dim - self.num_splits_per_dim = num_splits_per_dim - - -class TensorPartitioner: - """Shards Tensor's across its axis. - - In cases of TPUs, these partitions are zero cost, and does not involve data - movement. - """ - - @classmethod - def partition_metadata(cls, tensor, partition_info): - """Returns metadata required for partitioning and reforming tensors. - - Args: - tensor: Tensor to partition. - partition_info: Partitioning info. - - Returns: - split_sizes_per_dim and num_splits_per_dim. - """ - shape = tensor.get_shape() - # Split if dim is greater than max_dim. - axis_to_shard = [s > partition_info.max_dim_size for s in shape] - split_sizes_per_dim = [] - # Compute the number of splits, and the sizes of the splits for - # each dimension - for sharded, dim in zip(axis_to_shard, shape): - dim = int(dim) - split_sizes_per_dim.append([dim]) - if sharded: - split_sizes = [] - num_shards = dim // partition_info.partition_size - if num_shards > 0: - split_sizes = [partition_info.partition_size] * num_shards - last_shard_size = dim % partition_info.partition_size - if last_shard_size > 0: - split_sizes.append(last_shard_size) - else: - split_sizes.append(dim) - split_sizes_per_dim[-1] = split_sizes - num_splits_per_dim = [len(v) for v in split_sizes_per_dim] - return PartitionMetadata(split_sizes_per_dim, num_splits_per_dim) - - @classmethod - def partition_tensor(cls, tensor, partition_info): - """Returns partitioned tensors.""" - metadata = (TensorPartitioner.partition_metadata(tensor, partition_info)) - # Split from last to first axis. - partitioned_tensors = [tensor] - rank = len(metadata.num_splits_per_dim) - for raxis, (num_splits, sizes) in enumerate( - zip( - reversed(metadata.num_splits_per_dim), - reversed(metadata.split_sizes_per_dim))): - if num_splits > 1: - tmp_partitioned_tensors = [] - for item in partitioned_tensors: - tmp_partitioned_tensors += tf.split( - item, sizes, axis=rank - raxis - 1) - partitioned_tensors = tmp_partitioned_tensors - return partitioned_tensors - - @classmethod - def reform_tensor(cls, partitioned_tensors, num_splits_per_dim): - """Returns a tensor concatenated from the given partitions.""" - # Concatenates tensors across all dimension. Assumes the `partitions` tensor - # was created by partition_tensor. - for axis, num_splits in enumerate(num_splits_per_dim): - if num_splits > 1: - tmp_partitioned_tensors = [] - num_concat = len(partitioned_tensors) // num_splits - for i in range(num_concat): - tensors_to_concat = ( - partitioned_tensors[i * num_splits:(i + 1) * num_splits]) - tmp_partitioned_tensors.append( - tf.concat(tensors_to_concat, axis=axis)) - partitioned_tensors = tmp_partitioned_tensors - return partitioned_tensors[0] - - -class DistributedShampoo(optimizer.Optimizer): - """Approximates full-matrix AdaGrad per layer. - - Approximates full-matrix AdaGrad with kronecker-products of two statistics - matrices based on only the first-order gradients of the layer. - - "Second-order optimization made practical.", 2019 - Rohan Anil, Vineet Gupta, Tomer Koren, Kevin Regan, Yoram Singer. - """ - - def __init__(self, - learning_rate, - momentum=0.0, - initial_accumulator_value=0.0, - start_preconditioning_steps=1000, - statistics_computation_frequency=1, - matrix_epsilon=1e-6, - synchronous_preconditioning=False, - second_moment_averaging=1.0, - fallback_to_diagonal_dim=4096, - max_any_dim=6656, - block_size=4096, - block_partition_threshold_size=1000000, - global_step=None, - exponent_multiplier=1.0, - name="DistributedShampoo"): - """Construct a DistributedShampoo optimizer. - - Args: - learning_rate: A `Tensor` or a floating point value. The learning rate. - momentum: A `Tensor` or a floating point value. Momentum is not applied to - sparse updates. - initial_accumulator_value: A floating point value. - start_preconditioning_steps: A int32 value which indicates when to start - preconditioning. - statistics_computation_frequency: A int32 step value which indicates how - often to compute statistics for preconditioning. - matrix_epsilon: An epsilon regularizer to make the matrices positive - definite. - synchronous_preconditioning: Whether to run preconditioning synchronously. - second_moment_averaging: 1.0 means sum of gradients squares, while less - than 1.0 switches to RMSProp style exponential moving averages of the - second moments. - fallback_to_diagonal_dim: Fallback to diagonal version of AFMA if the any - of the dimension is larger than fallback_to_diagonal_dim. - max_any_dim: If maximum value for any dimension is greater than this value - we skip preconditioning and fall back to the diagonal. - block_size: Dimension of the partitioned tensors. - block_partition_threshold_size: Partitions diemnsions beyond this size. - global_step: Global step for training. - exponent_multiplier: A multiplier 'e` for the exponent for the inverse - calculation. e * -1/(2*rank). Only applies when calculating inverses - through svd. - name: Optional name prefix for the operations created when applying - gradients. - """ - super().__init__(False, name) - self._learning_rate = learning_rate - self._momentum = momentum - self._initial_accumulator_value = initial_accumulator_value - self._start_preconditioning_steps = start_preconditioning_steps - self._matrix_epsilon = matrix_epsilon - self._synchronous_preconditioning = synchronous_preconditioning - self._second_moment_averaging = second_moment_averaging - self._fallback_to_diagonal_dim = fallback_to_diagonal_dim - self._max_any_dim = max_any_dim - self._block_size = block_size - # NOTE: On XLA - int64 is not handled properly. - if global_step is not None: - self._global_step = tf.cast(tf.identity(global_step), tf.int32) - else: - self._global_step = tf.cast( - tf.identity(tf.train.get_or_create_global_step()), tf.int32) - self._run_nondiagonal_update = tf.greater_equal( - self._global_step, self._start_preconditioning_steps) - start_steps_f = tf.cast(self._start_preconditioning_steps, tf.float32) - global_step_f = tf.cast(self._global_step, tf.float32) - self._run_nondiagonal_update_warmup = tf.minimum( - 1.0, tf.maximum((global_step_f - start_steps_f) / start_steps_f, 0.0)) - # Computes statistics every K steps. - self._statistics_computation_frequency = statistics_computation_frequency - self._run_statistics_computation = tf.equal( - tf.math.floormod(self._global_step, - self._statistics_computation_frequency), 0) - # All vars that are preconditioned. - self._all_vars_for_preconditioning = [] - self._exponent_multiplier = exponent_multiplier - self._partition_info = PartitionConfig(block_partition_threshold_size, - block_size) - self._partitioner_metadata = {} - - def _fallback_to_diagonal_for_shape(self, shape): - """Returns whether we should fallback to the diagonal update given shape.""" - # We fallback to diagonal for the following usecases: - # - # (a) Rank <= 1 tensors - # (b) if any dim of Tensor is > max_any_dim. - # (c) if all dims are 1 or are greater than fallback_to_diagonal_dim - # - if len(shape) <= 1: - return True - if any([d > self._max_any_dim for d in shape]): - return True - if all([d == 1 for d in shape]): - return True - return False - - def _preconditioner_available_for_dims(self, shape): - """Returns indicator vector if preconditioner exists for each axis.""" - # If any of the dims < fallback_to_diagonal_dim and not 1, we run a - # a preconditioner for that particular dimension. - return [d <= self._fallback_to_diagonal_dim and d != 1 for d in shape] - - def _preconditioner_indices(self, shape): - """Returns indices of the available preconditioner.""" - preconditioners_available_for_dims = ( - self._preconditioner_available_for_dims(shape)) - indices = [] - index = 0 - for is_avail_for_dim_i in preconditioners_available_for_dims: - indices.append(index) - if is_avail_for_dim_i: - index += 1 - return indices - - def _make_named_slot(self, var, val, slot_name): - _ = self._get_or_make_slot(var, val, slot_name, - self._name + "_" + slot_name) - - def make_named_zeros_slot(self, var, slot_name): - self._zeros_slot(var, slot_name, self._name + "_" + slot_name) - - def _generalized_inverse_pth_root(self, input_t, exponent, epsilon=1e-12): - input_t_f64 = tf.cast(input_t, tf.float64) - s, u, v = tf.linalg.svd( - input_t_f64 + - tf.eye(tf.shape(input_t_f64)[0], dtype=tf.float64) * epsilon, - full_matrices=True) - inv_s = tf.reshape( - tf.pow(tf.maximum(s, epsilon), tf.cast(exponent, tf.float64)), [1, -1]) - val = tf.matmul(u * inv_s, v, adjoint_b=True) - return tf.cast(val, tf.float32), tf.reduce_max(tf.abs(u - v)) - - def _specialized_inverse_pth_root(self, input_t, exponent, epsilon=1e-12): - input_t_f64 = tf.cast(input_t, tf.float64) - val, error = matrix_functions.inlined_matrix_inverse_pth_root( - input_t_f64, - tf.shape(input_t_f64)[0], - exponent, - iter_count=40, - ridge_epsilon=epsilon) - return tf.cast(val, tf.float32), error - - def _inverse_pth_root_graph(self, epsilon): - graph = tf.Graph() - with graph.as_default(): - exponent_t = tf.reshape( - tf.placeholder(dtype=tf.float32, name="exponent", shape=None), []) - # Apply exponent multiplier. - exponent_t = exponent_t * self._exponent_multiplier - input_t = tf.placeholder(dtype=tf.float32, name="input", shape=None) - # For p = 2, 4 or 8, we use the iterative Newton-Schur method for - # computing the inverse-pth root. - either_p_2_4_8 = tf.math.logical_or( - tf.math.logical_or( - tf.equal(-1.0 / exponent_t, 2), tf.equal(-1.0 / exponent_t, 4)), - tf.equal(-1.0 / exponent_t, 8)) - # 4096 is the larger dimension SVD is tractable for. - greater_than_4096 = tf.greater(tf.shape(input_t)[0], 4096) - run_specialized_iterative_method = tf.math.logical_and( - greater_than_4096, either_p_2_4_8) - specialized_fn = functools.partial(self._specialized_inverse_pth_root, - input_t, exponent_t, epsilon) - generalized_fn = functools.partial(self._generalized_inverse_pth_root, - input_t, exponent_t, epsilon) - output, diff = tf.cond(run_specialized_iterative_method, specialized_fn, - generalized_fn) - - tf.identity(output, "output") - tf.identity(tf.cast(diff, tf.float32), "diff") - return graph.as_graph_def().SerializeToString() - - def _create_slots(self, var_list): - self._preconditioner_compute_graphdef = self._inverse_pth_root_graph( - epsilon=self._matrix_epsilon) - for v in var_list: - self._make_named_slot(v, - tf.ones_like(v) * self._initial_accumulator_value, - "accumulator") - - if self._momentum > 0.0: - self.make_named_zeros_slot(v, "momentum") - shape = np.array(v.get_shape()) - self._partitioner_metadata[v] = TensorPartitioner.partition_metadata( - v, self._partition_info) - partitioned_v = TensorPartitioner.partition_tensor( - v, self._partition_info) - if not self._fallback_to_diagonal_for_shape(shape): - self._all_vars_for_preconditioning.append(v) - if self._momentum > 0.0: - self.make_named_zeros_slot(v, "precond_grad_momentum") - num_partitions = len(partitioned_v) - for pt_idx, pt_v in enumerate(partitioned_v): - pt_v_shape = pt_v.get_shape() - preconditioner_exists_for_dim = ( - self._preconditioner_available_for_dims(pt_v_shape)) - for i, d in enumerate(pt_v_shape): - if preconditioner_exists_for_dim[i]: - mat_stat_init = array_ops.zeros([d, d], dtype=pt_v.dtype) - self._make_named_slot( - v, mat_stat_init, - self._statistics_key_for_partition_and_dim( - i, pt_idx, num_partitions)) - self._make_named_slot( - v, mat_stat_init, - self._preconditioner_key_for_partition_and_dim( - i, pt_idx, num_partitions)) - - def _prepare(self): - learning_rate = self._call_if_callable(self._learning_rate) - self._learning_rate_tensor = ops.convert_to_tensor( - learning_rate, name="learning_rate") - momentum = self._call_if_callable(self._momentum) - self._momentum_tensor = ops.convert_to_tensor(momentum, name="momentum") - - def invoke_async_preconditioner_computation(self, global_step_int32): - """Invokes SVD preconditioner and graph runs on the CPU.""" - keys_stats_and_rank = [] - for var in self._all_vars_for_preconditioning: - shape = var.get_shape() - if not self._fallback_to_diagonal_for_shape(shape): - partitioned_v = TensorPartitioner.partition_tensor( - var, self._partition_info) - num_partitions = len(partitioned_v) - for pt_idx, pt_v in enumerate(partitioned_v): - pt_v_shape = pt_v.get_shape() - preconditioner_exists_for_dim = ( - self._preconditioner_available_for_dims(pt_v_shape)) - for i in range(len(pt_v_shape)): - if preconditioner_exists_for_dim[i]: - rank = sum(preconditioner_exists_for_dim) - key = self._key_for_var(var, i, pt_idx) - stat = self.get_slot( - var, - self._statistics_key_for_partition_and_dim( - i, pt_idx, num_partitions)) - keys_stats_and_rank.append((key, stat, rank)) - - if not keys_stats_and_rank: - return tf.no_op() - keys, stats, ranks = zip(*keys_stats_and_rank) - - return x_ops.compute_preconditioners( - stats, [-1.0 / (2.0 * r) for r in ranks], - global_step_int32, - keys=keys, - sync=self._synchronous_preconditioning, - preconditioner_compute_graphdef=self._preconditioner_compute_graphdef) - - def assign_preconditioner_to_host_vars(self): - """Assign/Grab latest copy of preconditioners.""" - keys_shapes_and_preconditioner_vars = [] - assign_ops = [] - for var in self._all_vars_for_preconditioning: - shape = var.get_shape() - if not self._fallback_to_diagonal_for_shape(shape): - partitioned_v = TensorPartitioner.partition_tensor( - var, self._partition_info) - num_partitions = len(partitioned_v) - for pt_idx, pt in enumerate(partitioned_v): - pt_shape = pt.get_shape() - preconditioner_exists_for_dim = ( - self._preconditioner_available_for_dims(pt_shape)) - var_rank = len(pt_shape) - for i in range(var_rank): - if preconditioner_exists_for_dim[i]: - key = self._key_for_var(var, i, pt_idx) - preconditioner = self.get_slot( - var, - self._preconditioner_key_for_partition_and_dim( - i, pt_idx, num_partitions)) - keys_shapes_and_preconditioner_vars.append( - (key, tf.shape(preconditioner), preconditioner)) - - if not keys_shapes_and_preconditioner_vars: - return tf.no_op() - - keys, shapes, preconditioner_vars = zip( - *keys_shapes_and_preconditioner_vars) - - preconditioner_vals, successes = x_ops.get_preconditioners( - shapes, - keys=keys, - preconditioner_compute_graphdef=( - self._preconditioner_compute_graphdef)) - - for preconditioner_var, preconditioner_val, success in zip( - preconditioner_vars, preconditioner_vals, successes): - success_mult = tf.cast(success, preconditioner.dtype) - assign_ops.append( - state_ops.assign(preconditioner_var, - (1.0 - success_mult) * preconditioner_var + - success_mult * preconditioner_val)) - return tf.group(*assign_ops) - - def _statistics_key_for_partition_and_dim(self, dim_index, partition_index, - num_partitions): - if num_partitions == 1: - return "mat_statistics_" + str(dim_index) - else: - return str(partition_index) + "_mat_statistics_" + str(dim_index) - - def _preconditioner_key_for_partition_and_dim(self, dim_index, - partition_index, - num_partitions): - if num_partitions == 1: - return "mat_preconditioner_" + str(dim_index) - else: - return str(partition_index) + "_mat_preconditioner_" + str(dim_index) - - def _key_for_var(self, var, dim_index, partition_index): - return "P_" + str(partition_index) + "_D_" + str(dim_index) + "_" + var.name - - def _updated_statistics(self, var, partitioned_grads): - """Returns updated Shampoo statistics L_t, R_t, etc. - - Args: - var: tf.Variable associated with the gradient. - partitioned_grads: Partitioned gradient tensor. - - Returns: - A list of updated statistics matrices. - """ - precond_statistics_update = [] - num_partitions = len(partitioned_grads) - mat_stats = [] - mat_grads = [] - mat_dims = [] - for pt_idx, pt_grad in enumerate(partitioned_grads): - pt_shape = pt_grad.get_shape() - preconditioner_exists_for_dim = ( - self._preconditioner_available_for_dims(pt_shape)) - rank = len(pt_shape) - # Calculates the preconditioner statistics for each tensor. - for i in range(rank): - if preconditioner_exists_for_dim[i]: - mat_stats.append( - self.get_slot( - var, - self._statistics_key_for_partition_and_dim( - i, pt_idx, num_partitions))) - mat_grads.append(pt_grad) - mat_dims.append(i) - - # axes is the list of indices to reduce - everything but - # the current i. - def _update_statistics(dim, stat_var, grad): - """Update preconditioner statistics.""" - with tf.name_scope("GradientStatistics"): - var_rank = len(grad.get_shape()) - axes = list(range(dim)) + list(range(dim + 1, var_rank)) - new_stat = math_ops.tensordot(grad, grad, axes=(axes, axes)) - if self._second_moment_averaging == 1.0: - updated_stat = state_ops.assign_add(stat_var, new_stat) - else: - updated_stat = state_ops.assign_add( - stat_var, (self._second_moment_averaging - 1.0) * stat_var + - (1.0 - self._second_moment_averaging) * new_stat) - return updated_stat - - if self._statistics_computation_frequency <= 1: - for mat_stat, mat_grad, dim in zip(mat_stats, mat_grads, mat_dims): - precond_statistics_update.append( - _update_statistics(dim, mat_stat, mat_grad)) - else: - - # NOTE: We rewrite tf.cond() as a while loop to avoid certain overheads - # in XLA from buffer allocation. - def _loop_body(mat_stats, mat_grads, mat_dims, unused_perform_step): - precond_statistics_update_ops = [] - for mat_stat, mat_grad, dim in zip(mat_stats, mat_grads, mat_dims): - precond_statistics_update_ops.append( - _update_statistics(dim, mat_stat, mat_grad)) - with tf.control_dependencies(precond_statistics_update_ops): - return tf.constant(False) - - loop_body_fn = functools.partial(_loop_body, mat_stats, mat_grads, - mat_dims) - precond_statistics_update.append( - tf.while_loop(lambda perform_step: perform_step, loop_body_fn, - [self._run_statistics_computation])) - - return precond_statistics_update - - def _compute_preconditioned_raw_grad(self, var, partitioned_grads): - """Returns preconditioned gradient. - - Args: - var: tf.Variable associated with the gradient. - partitioned_grads: Partitioned gradient tensor. - - Returns: - A preconditioned gradient tensor. - """ - - partitioned_preconditioned_grads = [] - num_partitions = len(partitioned_grads) - for pt_idx, pt_grad in enumerate(partitioned_grads): - pt_shape = pt_grad.get_shape() - rank = len(pt_shape) - preconditioner_exists_for_dim = ( - self._preconditioner_available_for_dims(pt_shape)) - preconditioner_indices = self._preconditioner_indices(pt_shape) - mat_preconditioner_list = [] - for i in range(rank): - if preconditioner_exists_for_dim[i]: - mat_preconditioner_list.append( - self.get_slot( - var, - self._preconditioner_key_for_partition_and_dim( - i, pt_idx, num_partitions))) - precond_grad = pt_grad - if rank == 2 and all(preconditioner_exists_for_dim): - # Fast path for speedup. - precond_grad = tf.matmul( - tf.matmul(mat_preconditioner_list[0], precond_grad), - mat_preconditioner_list[1]) - else: - for i in range(rank): - if preconditioner_exists_for_dim[i]: - precond_grad = tf.tensordot( - precond_grad, - mat_preconditioner_list[preconditioner_indices[i]], - axes=([0], [0])) - else: - # if preconditioner is not available we transpose it to - # permute the axis for the next preconditioner. - precond_grad = tf.transpose( - precond_grad, perm=list(range(1, rank)) + [0]) - partitioned_preconditioned_grads.append(precond_grad) - return TensorPartitioner.reform_tensor( - partitioned_preconditioned_grads, - self._partitioner_metadata[var].num_splits_per_dim) - - def _preconditioned_update(self, var, partitioned_grads, - diagonal_grad_update): - """Computes the matrix preconditioned update. - - Args: - var: Variable for which we are computing the preconditioned gradient. - partitioned_grads: Partitioned gradients. - diagonal_grad_update: Update as given by diagonal adagrad. - - Returns: - scaled preconditioned gradient. - """ - - def _l2_norm(v): - return tf.sqrt(tf.reduce_sum(tf.square(v))) - - precond_grad = self._compute_preconditioned_raw_grad(var, partitioned_grads) - if self._momentum > 0.0: - gbar = self.get_slot(var, "precond_grad_momentum") - matrix_preconditioned_grad = state_ops.assign( - gbar, gbar * self._momentum_tensor + precond_grad * - (1.0 - self._momentum_tensor)) - else: - matrix_preconditioned_grad = precond_grad - - # We use the direction from Shampoo while using the step size scale from - # diagonal AdaGrad. - precond_l2_norm = _l2_norm(matrix_preconditioned_grad) - diagonal_l2_norm = _l2_norm(diagonal_grad_update) - multiplier = tf.where( - tf.greater(precond_l2_norm, 0.0), - tf.maximum(diagonal_l2_norm, 1e-30) / - (tf.maximum(precond_l2_norm, 1e-30)), 1.0) - return matrix_preconditioned_grad * multiplier - - def _apply_dense(self, grad, var): - # Calculates the preconditioner statistics for each tensor. - partitioned_grads = TensorPartitioner.partition_tensor( - grad, self._partition_info) - shape = var.get_shape() - fallback_to_diagonal = self._fallback_to_diagonal_for_shape(shape) - - precond_statistics_update = [] - if not fallback_to_diagonal: - precond_statistics_update = self._updated_statistics( - var, partitioned_grads) - - accumulator = self.get_slot(var, "accumulator") - accumulator_updated = state_ops.assign_add(accumulator, grad * grad) - accumulator_inv_sqrt = math_ops.rsqrt(accumulator_updated + 1e-30) - if self._momentum > 0.0: - scaled_g = (1.0 - self._momentum_tensor) * (grad * accumulator_inv_sqrt) - gbar = self.get_slot(var, "momentum") - gbar_updated = state_ops.assign_add( - gbar, - gbar * (self._momentum_tensor - 1.0) + scaled_g) - else: - gbar_updated = (grad * accumulator_inv_sqrt) - - if not fallback_to_diagonal: - # Update the preconditioner statistics followed by computing the - # preconditioned gradient. - with ops.control_dependencies(precond_statistics_update): - s = tf.cast(self._run_nondiagonal_update, tf.float32) - preconditioned_grad = self._preconditioned_update( - var, partitioned_grads, gbar_updated) - # slowly adapt from diagonal to preconditioned gradient. - w = self._run_nondiagonal_update_warmup - warmup_update = s * self._learning_rate_tensor * ( - w * preconditioned_grad + (1.0 - w) * gbar_updated) - fallback_update = (1 - s) * (self._learning_rate_tensor * gbar_updated) - return state_ops.assign_sub(var, warmup_update + fallback_update) - else: - return state_ops.assign_sub(var, - self._learning_rate_tensor * gbar_updated) - - def _resource_apply_dense(self, grad, var): - return self._apply_dense(grad, var) - - # Sparse gradients are not handled currently and is part of future work. - def _resource_apply_sparse(self, grad_values, var, grad_indices): - return tf.no_op() - - def _apply_sparse(self, grad, var): - return tf.no_op() diff --git a/lingvo/core/distributed_shampoo_test.py b/lingvo/core/distributed_shampoo_test.py deleted file mode 100644 index 56fc2c90..00000000 --- a/lingvo/core/distributed_shampoo_test.py +++ /dev/null @@ -1,414 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.distributed_shampoo.""" - -from lingvo.core import distributed_shampoo -from lingvo.core import test_utils - -import numpy as np -import tensorflow.compat.v1 as tf - - -class DistributedShampooTest(test_utils.TestCase): - """A test that demonstrates the use of distributed matrix preconditioner.""" - - def testShampooWithMatrixShapedTensors(self): - # Parameter matrix of size [4,2] would result in L_{t}, and R_{t} of - # sizes [4, 4] and [2, 2] - size = [4, 2] - init_var_np = np.zeros(size) - # Initialize gradient as random tensor. - grad_np = np.random.rand(size[0], size[1]) - - with tf.Session(): - global_step = tf.Variable(0, dtype=tf.int64) - var = tf.Variable(init_var_np, dtype=tf.float32) - grad = tf.constant(grad_np, dtype=tf.float32) - - opt = distributed_shampoo.DistributedShampoo( - learning_rate=1.0, - momentum=0.0, - start_preconditioning_steps=0, - synchronous_preconditioning=True, - global_step=global_step) - - # Run a single step of gradient update. - update = opt.apply_gradients(zip([grad], [var]), global_step=global_step) - - # Preconditioner computation and assignments to variables. - compute_preconditioner_op = opt.invoke_async_preconditioner_computation( - tf.cast(global_step, tf.int32)) - assign_preconditioners_to_vars_op = ( - opt.assign_preconditioner_to_host_vars()) - - self.evaluate(tf.global_variables_initializer()) - tf.tables_initializer().run() - - init_val = self.evaluate(var) - self.assertAllCloseAccordingToType(init_var_np, init_val) - - def np_power(mat_g, alpha, matrix_epsilon=1e-6): - """Computes mat_g^alpha for a square symmetric matrix mat_g.""" - mat_for_svd = mat_g + np.eye(mat_g.shape[0]) * matrix_epsilon - mat_u, diag_d, mat_v = np.linalg.svd(mat_for_svd, full_matrices=True) - diag_d = np.power(np.maximum(diag_d, matrix_epsilon), alpha) - return np.dot(mat_u, np.dot(np.diag(diag_d), mat_v)) - - def norm(val): - return np.sqrt(np.sum(np.square(val))) - - # Run a step of preconditioner update. - update.run() - - mat_g1 = np.dot(grad_np, grad_np.transpose()) - expected_mat_g1 = self.evaluate(opt.get_slot(var, 'mat_statistics_0')) - self.assertAllCloseAccordingToType(mat_g1, expected_mat_g1, atol=1e-1) - - mat_g2 = np.dot(grad_np.transpose(), grad_np) - expected_mat_g2 = self.evaluate(opt.get_slot(var, 'mat_statistics_1')) - self.assertAllCloseAccordingToType(mat_g2, expected_mat_g2, atol=1e-1) - - compute_preconditioner_op.run() - assign_preconditioners_to_vars_op.run() - - mat_left = np_power(mat_g1, -0.25) - expected_mat_left = self.evaluate( - opt.get_slot(var, 'mat_preconditioner_0')) - self.assertAllCloseAccordingToType(mat_left, expected_mat_left, atol=1e-1) - - mat_right = np_power(mat_g2, -0.25) - expected_mat_right = self.evaluate( - opt.get_slot(var, 'mat_preconditioner_1')) - self.assertAllCloseAccordingToType( - mat_right, expected_mat_right, atol=1e-1) - - # As the preconditioners are initialized to all zero. We don't make - # any update. - var_step_0_val = self.evaluate(var) - self.assertAllCloseAccordingToType(init_var_np, var_step_0_val, atol=1e-1) - - # Run another step of training. - update.run() - var_step_1_val = self.evaluate(var) - - # New update has the scale of the second diagonal adagrad update. - adagrad_update = grad_np / np.sqrt(2 * np.square(grad_np)) - preconditioned_grad_update = np.dot(np.dot(mat_left, grad_np), mat_right) - - # With normalization by diagonal enabled. - var_step_1_np = init_var_np - preconditioned_grad_update * norm( - adagrad_update) / norm(preconditioned_grad_update) - self.assertAllCloseAccordingToType( - var_step_1_np, var_step_1_val, atol=1e-1) - - # Compute new preconditioners. - compute_preconditioner_op.run() - assign_preconditioners_to_vars_op.run() - - # Gradients are summed over time. - mat_g1 += np.dot(grad_np, grad_np.transpose()) - mat_left = np_power(mat_g1, -0.25) - expected_mat_left = self.evaluate( - opt.get_slot(var, 'mat_preconditioner_0')) - self.assertAllCloseAccordingToType(mat_left, expected_mat_left, atol=1e-1) - - mat_g2 += np.dot(grad_np.transpose(), grad_np) - mat_right = np_power(mat_g2, -0.25) - expected_mat_right = self.evaluate( - opt.get_slot(var, 'mat_preconditioner_1')) - self.assertAllCloseAccordingToType( - mat_right, expected_mat_right, atol=1e-1) - - def testShampooWithMatrixShapedTensorsRightOnlyPreconditioner(self): - # Parameter matrix of size [4,2] would result in L_{t}, and R_{t} of - # sizes [4, 4] and [2, 2]. Since max_any_dim is set to 3, it would skip - # L_{t} and only use R_{t}. The exponent in the inverse used to compute - # the preconditioner becomes -1/2. - size = [4, 2] - init_var_np = np.zeros(size) - # Initialize gradient as random tensor. - grad_np = np.random.rand(size[0], size[1]) - - with tf.Session(): - global_step = tf.Variable(0, dtype=tf.int64) - var = tf.Variable(init_var_np, dtype=tf.float32) - grad = tf.constant(grad_np, dtype=tf.float32) - - opt = distributed_shampoo.DistributedShampoo( - learning_rate=1.0, - momentum=0.0, - fallback_to_diagonal_dim=3, - start_preconditioning_steps=0, - synchronous_preconditioning=True, - global_step=global_step) - - # Run a single step of gradient update. - update = opt.apply_gradients(zip([grad], [var]), global_step=global_step) - - # Preconditioner computation and assignments to variables. - compute_preconditioner_op = opt.invoke_async_preconditioner_computation( - tf.cast(global_step, tf.int32)) - assign_preconditioners_to_vars_op = ( - opt.assign_preconditioner_to_host_vars()) - - self.evaluate(tf.global_variables_initializer()) - tf.tables_initializer().run() - - init_val = self.evaluate(var) - self.assertAllCloseAccordingToType(init_var_np, init_val) - - def np_power(mat_g, alpha, matrix_epsilon=1e-6): - """Computes mat_g^alpha for a square symmetric matrix mat_g.""" - mat_for_svd = mat_g + np.eye(mat_g.shape[0]) * matrix_epsilon - mat_u, diag_d, mat_v = np.linalg.svd(mat_for_svd, full_matrices=True) - diag_d = np.power(np.maximum(diag_d, matrix_epsilon), alpha) - return np.dot(mat_u, np.dot(np.diag(diag_d), mat_v)) - - def norm(val): - return np.sqrt(np.sum(np.square(val))) - - # Run a step of preconditioner update. - update.run() - - mat_g2 = np.dot(grad_np.transpose(), grad_np) - expected_mat_g2 = self.evaluate(opt.get_slot(var, 'mat_statistics_1')) - self.assertAllCloseAccordingToType(mat_g2, expected_mat_g2, atol=1e-1) - - compute_preconditioner_op.run() - assign_preconditioners_to_vars_op.run() - - mat_right = np_power(mat_g2, -0.5) - expected_mat_right = self.evaluate( - opt.get_slot(var, 'mat_preconditioner_1')) - self.assertAllCloseAccordingToType( - mat_right, expected_mat_right, atol=1e-1) - - # As the preconditioners are initialized to all zero. We don't make - # any update. - var_step_0_val = self.evaluate(var) - self.assertAllCloseAccordingToType(init_var_np, var_step_0_val, atol=1e-1) - - # Run another step of training. - update.run() - var_step_1_val = self.evaluate(var) - - # New update has the scale of the second diagonal adagrad update. - adagrad_update = grad_np / np.sqrt(2 * np.square(grad_np)) - preconditioned_grad_update = np.matmul(grad_np, mat_right) - - # With normalization by diagonal enabled. - var_step_1_np = init_var_np - preconditioned_grad_update * norm( - adagrad_update) / norm(preconditioned_grad_update) - - self.assertAllCloseAccordingToType( - var_step_1_np, var_step_1_val, atol=1e-1) - - # Compute new preconditioners. - compute_preconditioner_op.run() - assign_preconditioners_to_vars_op.run() - - # Gradients are summed over time. - - mat_g2 += np.dot(grad_np.transpose(), grad_np) - mat_right = np_power(mat_g2, -0.5) - expected_mat_right = self.evaluate( - opt.get_slot(var, 'mat_preconditioner_1')) - self.assertAllCloseAccordingToType( - mat_right, expected_mat_right, atol=1e-1) - - def testShampooWithMatrixShapedTensorsWithBlocks(self): - # Parameter matrix of size [4,2] would result in 4 L_{t}, and R_{t} of - # sizes [2, 2] and [2, 2]. - size = [4, 2] - init_var_np = np.zeros(size) - # Initialize gradient as random tensor. - grad_np = np.random.rand(size[0], size[1]) - - with tf.Session(): - global_step = tf.Variable(0, dtype=tf.int64) - var = tf.Variable(init_var_np, dtype=tf.float32) - grad = tf.constant(grad_np, dtype=tf.float32) - - opt = distributed_shampoo.DistributedShampoo( - learning_rate=1.0, - momentum=0.0, - block_partition_threshold_size=3, - block_size=2, - start_preconditioning_steps=0, - synchronous_preconditioning=True, - global_step=global_step) - - # Run a single step of gradient update. - update = opt.apply_gradients(zip([grad], [var]), global_step=global_step) - - # Preconditioner computation and assignments to variables. - compute_preconditioner_op = opt.invoke_async_preconditioner_computation( - tf.cast(global_step, tf.int32)) - assign_preconditioners_to_vars_op = ( - opt.assign_preconditioner_to_host_vars()) - - self.evaluate(tf.global_variables_initializer()) - tf.tables_initializer().run() - - init_val = self.evaluate(var) - self.assertAllCloseAccordingToType(init_var_np, init_val) - - def np_power(mat_g, alpha, matrix_epsilon=1e-6): - """Computes mat_g^alpha for a square symmetric matrix mat_g.""" - mat_for_svd = mat_g + np.eye(mat_g.shape[0]) * matrix_epsilon - mat_u, diag_d, mat_v = np.linalg.svd(mat_for_svd, full_matrices=True) - diag_d = np.power(np.maximum(diag_d, matrix_epsilon), alpha) - return np.dot(mat_u, np.dot(np.diag(diag_d), mat_v)) - - def norm(val): - return np.sqrt(np.sum(np.square(val))) - - # Run a step of preconditioner update. - update.run() - - block_0_grad_np = grad_np[:2, :2] - block_1_grad_np = grad_np[2:4, :2] - - block_0_mat_g1 = np.dot(block_0_grad_np, block_0_grad_np.transpose()) - expected_block_0_mat_g1 = self.evaluate( - opt.get_slot(var, '0_mat_statistics_0')) - - self.assertAllCloseAccordingToType( - block_0_mat_g1, expected_block_0_mat_g1, atol=1e-1) - - block_0_mat_g2 = np.dot(block_0_grad_np.transpose(), block_0_grad_np) - expected_block_0_mat_g2 = self.evaluate( - opt.get_slot(var, '0_mat_statistics_1')) - self.assertAllCloseAccordingToType( - block_0_mat_g2, expected_block_0_mat_g2, atol=1e-1) - - block_1_mat_g1 = np.dot(block_1_grad_np, block_1_grad_np.transpose()) - expected_block_1_mat_g1 = self.evaluate( - opt.get_slot(var, '1_mat_statistics_0')) - self.assertAllCloseAccordingToType( - block_1_mat_g1, expected_block_1_mat_g1, atol=1e-1) - - block_1_mat_g2 = np.dot(block_1_grad_np.transpose(), block_1_grad_np) - expected_block_1_mat_g2 = self.evaluate( - opt.get_slot(var, '1_mat_statistics_1')) - self.assertAllCloseAccordingToType( - block_1_mat_g2, expected_block_1_mat_g2, atol=1e-1) - - compute_preconditioner_op.run() - assign_preconditioners_to_vars_op.run() - - block_0_mat_left = np_power(block_0_mat_g1, -0.25) - expected_block_0_mat_left = self.evaluate( - opt.get_slot(var, '0_mat_preconditioner_0')) - self.assertAllCloseAccordingToType( - block_0_mat_left, expected_block_0_mat_left, atol=1e-1) - - block_0_mat_right = np_power(block_0_mat_g2, -0.25) - expected_block_0_mat_right = self.evaluate( - opt.get_slot(var, '0_mat_preconditioner_1')) - self.assertAllCloseAccordingToType( - block_0_mat_right, expected_block_0_mat_right, atol=1e-1) - - block_1_mat_left = np_power(block_1_mat_g1, -0.25) - expected_block_1_mat_left = self.evaluate( - opt.get_slot(var, '1_mat_preconditioner_0')) - self.assertAllCloseAccordingToType( - block_1_mat_left, expected_block_1_mat_left, atol=1e-1) - - block_1_mat_right = np_power(block_1_mat_g2, -0.25) - expected_block_1_mat_right = self.evaluate( - opt.get_slot(var, '1_mat_preconditioner_1')) - self.assertAllCloseAccordingToType( - block_1_mat_right, expected_block_1_mat_right, atol=1e-1) - - # As the preconditioners are initialized to all zero. We don't make - # any update. - var_step_0_val = self.evaluate(var) - self.assertAllCloseAccordingToType(init_var_np, var_step_0_val, atol=1e-1) - - # Run another step of training. - update.run() - var_step_1_val = self.evaluate(var) - - # New update has the scale of the second diagonal adagrad update. - adagrad_update = grad_np / np.sqrt(2 * np.square(grad_np)) - - block_0_update = np.dot( - np.dot(block_0_mat_left, block_0_grad_np), block_0_mat_right) - block_1_update = np.dot( - np.dot(block_1_mat_left, block_1_grad_np), block_1_mat_right) - preconditioned_grad_update = np.concatenate( - (block_0_update, block_1_update), axis=0) - # With normalization by diagonal enabled. - var_step_1_np = init_var_np - preconditioned_grad_update * norm( - adagrad_update) / norm(preconditioned_grad_update) - self.assertAllCloseAccordingToType( - var_step_1_np, var_step_1_val, atol=1e-1) - - # Compute new preconditioners. - compute_preconditioner_op.run() - assign_preconditioners_to_vars_op.run() - - # Gradients are summed over time. - block_0_mat_g1 += np.dot(block_0_grad_np, block_0_grad_np.transpose()) - block_0_mat_left = np_power(block_0_mat_g1, -0.25) - expected_block_0_mat_left = self.evaluate( - opt.get_slot(var, '0_mat_preconditioner_0')) - self.assertAllCloseAccordingToType( - block_0_mat_left, expected_block_0_mat_left, atol=1e-1) - - block_0_mat_g2 += np.dot(block_0_grad_np.transpose(), block_0_grad_np) - block_0_mat_right = np_power(block_0_mat_g2, -0.25) - expected_block_0_mat_right = self.evaluate( - opt.get_slot(var, '0_mat_preconditioner_1')) - self.assertAllCloseAccordingToType( - block_0_mat_right, expected_block_0_mat_right, atol=1e-1) - - block_1_mat_g1 += np.dot(block_1_grad_np, block_1_grad_np.transpose()) - block_1_mat_left = np_power(block_1_mat_g1, -0.25) - expected_block_1_mat_left = self.evaluate( - opt.get_slot(var, '1_mat_preconditioner_0')) - self.assertAllCloseAccordingToType( - block_1_mat_left, expected_block_1_mat_left, atol=1e-1) - - block_1_mat_g2 += np.dot(block_1_grad_np.transpose(), block_1_grad_np) - block_1_mat_right = np_power(block_1_mat_g2, -0.25) - expected_block_1_mat_right = self.evaluate( - opt.get_slot(var, '1_mat_preconditioner_1')) - self.assertAllCloseAccordingToType( - block_1_mat_right, expected_block_1_mat_right, atol=1e-1) - - -class TensorPartitionerTest(test_utils.TestCase): - """Tensor partitioner tests.""" - - def testTensorPartitioner(self): - with tf.Session(): - w1 = tf.get_variable('w1', [255, 255], tf.float32) - self.evaluate(tf.global_variables_initializer()) - partition_info = distributed_shampoo.PartitionConfig(200, 128) - grad = tf.constant(w1.eval()) - metadata = distributed_shampoo.TensorPartitioner.partition_metadata( - w1, partition_info) - partitioned_grad = distributed_shampoo.TensorPartitioner.partition_tensor( - w1, partition_info) - reformed_grad = distributed_shampoo.TensorPartitioner.reform_tensor( - partitioned_grad, metadata.num_splits_per_dim) - self.assertAllCloseAccordingToType(reformed_grad, grad) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/early_stop.py b/lingvo/core/early_stop.py deleted file mode 100644 index a7baa517..00000000 --- a/lingvo/core/early_stop.py +++ /dev/null @@ -1,206 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Early stopping based on dev-set performance.""" - -import os -import lingvo.compat as tf -from lingvo.core import hyperparams -from lingvo.core import ops - - -class MetricHistory: - """Record given metric versus global_step history to a file.""" - - @staticmethod - def SetLogdirInMetricHistories(params, logdir): - """Set the logdir member in all MetricHistory.params objects in params. - - Args: - params: global model params. - logdir: root dir for current run. - - Needs to be called by trainer once the logdir is known, but before - MetricHistory objects are constructed. - """ - for _, p in params.IterParams(): - if isinstance(p, hyperparams.Params): - try: - p.Get('name') - except AttributeError: - pass - else: - if p.name == 'MetricHistory': - p.logdir = logdir - MetricHistory.SetLogdirInMetricHistories(p, logdir) - - # Global map from jobname + metric key to class. - _metric_histories_map = {} - - @staticmethod - def _Key(jobname, metric): - """Generate a key for _metric_histories_map.""" - return jobname + '.' + metric - - @classmethod - def Params(cls): - p = hyperparams.Params() - p.Define('name', 'MetricHistory', 'Used by SetLogdirInMetricHistories.') - p.Define('jobname', 'eval_dev', 'Job and dataset to which metric applies.') - p.Define('metric', 'log_pplx', 'Metric to record.') - p.Define( - 'minimize', True, - 'If True, training minimizes the metric. If False, training ' - 'maximizes the metric.') - p.Define('logdir', '', 'Root dir for BF logs.') - p.Define( - 'tfevent_file', False, 'If True, read the metric from ' - 'events.out.tfevents.* files in the job dir instead of ' - 'maintaining a history file.') - p.Define('local_filesystem', False, - 'Logdir is on local filesystem (needed for unit test).') - return p - - def __init__(self, params): - self.params = params.Copy() - if params.tfevent_file: - self._hist_file = os.path.join(params.logdir, params.jobname, - 'events.out.tfevents*') - else: - fname = params.metric + '.history.txt' - self._hist_file = os.path.join(params.logdir, params.jobname, fname) - self._metric_histories_map[self._Key(params.jobname, params.metric)] = self - self._minimize = params.minimize - self._metric = params.metric - self._tfevent_file = params.tfevent_file - - @property - def hist_file(self): - return self._hist_file - - @property - def minimize(self): - return self._minimize - - @property - def metric(self): - return self._metric - - @property - def tfevent_file(self): - return self._tfevent_file - - @classmethod - def ConditionalAppend(cls, jobname, metric, global_step, value): - """Updates history file iff we are recording given metric and jobname.""" - key = cls._Key(jobname, metric) - if key in cls._metric_histories_map: - if cls._metric_histories_map[key].tfevent_file: - return False - cls._metric_histories_map[key].Append(global_step, value) - return True - else: - return False - - def Append(self, global_step, value): - """Updates history file with given record.""" - fname = self._hist_file - if not self.params.local_filesystem: - fname += '%r=3.2:sl=8M' - with tf.io.gfile.GFile(fname, 'a') as f: - f.write('%d %f\n' % (global_step, value)) - - -class EarlyStop: - """Early stopping based on dev-set performance. - - Factors out the steps needed to perform early stopping in the trainer when a - selected metric hasn't improved for a given number of steps. If the window - param is 0 this is guaranteed to be a no-op. - """ - - @classmethod - def Params(cls): - p = hyperparams.Params() - p.Define('name', 'EarlyStop', '') - p.Define('metric_history', MetricHistory.Params(), 'Metric history params.') - p.Define( - 'tolerance', 0.0, 'Minimum significant difference in metric; ' - 'useful if progress is asymptotic.') - p.Define('window', 0, 'Maximum number of steps between best and current.') - p.Define('verbose', True, 'Log early-stop checks.') - p.Define('min_steps', 0, 'Minimum number of steps before stopping.') - return p - - def __init__(self, params): - self.params = params.Copy() - if self.params.window: - self._metric_history = MetricHistory(self.params.metric_history) - else: - self._metric_history = None - self._node = None - self._best_step = 0 - self._last_step = 0 - - @property - def metric_history(self): - return self._metric_history - - @property - def best_step(self): - return self._best_step - - @property - def last_step(self): - return self._last_step - - def FProp(self, theta): - """Creates an op to determine the best step from the metric history file. - - Args: - theta: Not currently used. - Returns: - The created op. - - This uses BestStepOp rather than reading the file directly from python in - order to ensure compatibility with DevBasedSchedule for learning-rate decay. - It is natural to use dev-based decay and early stopping together, for - example decaying when dev-set perplexity hasn't improved for n steps, and - stopping when it hasn't improved for 3n steps. - """ - del theta # not used - if self.params.window: - self._node = ops.best_step(self.metric_history.hist_file, - self.params.tolerance, - self.metric_history.minimize, - self.metric_history.metric) - else: - self._node = None - return self._node - - def Stop(self, session): - """Returns true if stop criterion is met.""" - if self.params.window and self._node is not None: - self._best_step, self._last_step = session.run(self._node) - s = ( - self._last_step - self._best_step > self.params.window and - self._last_step >= self.params.min_steps) - if self.params.verbose: - tf.logging.info( - 'early stop check: best_step=%d, last_step=%d, stop=%d', - self._best_step, self._last_step, s) - return s - else: - return False diff --git a/lingvo/core/early_stop_test.py b/lingvo/core/early_stop_test.py deleted file mode 100644 index f6c800e9..00000000 --- a/lingvo/core/early_stop_test.py +++ /dev/null @@ -1,211 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for early_stop.""" - -import os -import lingvo.compat as tf -from lingvo.core import early_stop -from lingvo.core import hyperparams -from lingvo.core import test_helper -from lingvo.core import test_utils - - -class MetricHistoryTest(test_utils.TestCase): - - def setUp(self): - super().setUp() - early_stop.MetricHistory._metric_histories_map = {} - - def testSetLogdirInMetricHistories(self): - p = hyperparams.Params() - p.Define('name', 'testparams', 'test params') - p.Define('logdir', None, 'dummy logdir') - p.Define('mh1', early_stop.MetricHistory.Params(), 'history1') - p2 = hyperparams.Params() - p2.Define('mh2', early_stop.MetricHistory.Params(), 'history2') - p.Define('subparams', p2, 'subparams') - - early_stop.MetricHistory.SetLogdirInMetricHistories(p, 'dir') - - self.assertEqual(p.mh1.logdir, 'dir') - self.assertEqual(p.subparams.mh2.logdir, 'dir') - self.assertNotEqual(p.logdir, 'dir') - - def testMetricHistoriesMapUniqueness(self): - # pylint: disable=unused-variable - p = early_stop.MetricHistory.Params() - mh1 = early_stop.MetricHistory(p.Set(jobname='job1', metric='m1')) - mh2 = early_stop.MetricHistory(p.Set(jobname='job2', metric='m2')) - mh3 = early_stop.MetricHistory(p.Set(jobname='job1', metric='m1')) - - m = early_stop.MetricHistory._metric_histories_map - self.assertEqual(len(m), 2) - self.assertEqual(m[early_stop.MetricHistory._Key('job1', 'm1')], mh3) - self.assertEqual(m[early_stop.MetricHistory._Key('job2', 'm2')], mh2) - - def testMetricHistoriesFiles(self): - logdir = tf.test.get_temp_dir() - tf.io.gfile.mkdir(os.path.join(logdir, 'job1')) - tf.io.gfile.mkdir(os.path.join(logdir, 'job2')) - - p = early_stop.MetricHistory.Params().Set(logdir=logdir) - mh1 = early_stop.MetricHistory( - p.Set(jobname='job1', metric='m1', local_filesystem=True)) - mh2 = early_stop.MetricHistory( - p.Set(jobname='job2', metric='m2', local_filesystem=True)) - - early_stop.MetricHistory.ConditionalAppend('job1', 'm1', 1, 10.0) - early_stop.MetricHistory.ConditionalAppend('job1', 'm2', 1, 10.0) - early_stop.MetricHistory.ConditionalAppend('job2', 'm2', 1, 10.0) - early_stop.MetricHistory.ConditionalAppend('job1', 'm1', 2, 5.0) - - self.assertTrue(tf.io.gfile.exists(mh1.hist_file)) - self.assertTrue(tf.io.gfile.exists(mh2.hist_file)) - with tf.io.gfile.GFile(mh1.hist_file) as f: - lines = f.readlines() - self.assertEqual(len(lines), 2) - self.assertEqual(lines[0].rstrip(), '1 10.000000') - self.assertEqual(lines[1].rstrip(), '2 5.000000') - with tf.io.gfile.GFile(mh2.hist_file) as f: - lines = f.readlines() - self.assertEqual(len(lines), 1) - self.assertEqual(lines[0].rstrip(), '1 10.000000') - - -class EarlyStopTest(test_utils.TestCase): - - def setUp(self): - super().setUp() - early_stop.MetricHistory._metric_histories_map = {} - - def testEarlyStopDefaultIsNoOp(self): - p = early_stop.EarlyStop.Params() - es = early_stop.EarlyStop(p) - es.FProp(None) - mh = early_stop.MetricHistory - a = mh.ConditionalAppend(es.params.metric_history.jobname, - es.params.metric_history.metric, 1, 10.0) - s = es.Stop(None) - - self.assertFalse(a) - self.assertFalse(s) - self.assertIsNone(es._node) - self.assertEqual(len(early_stop.MetricHistory._metric_histories_map), 0) - - def testEarlyStopping(self): - logdir = tf.test.get_temp_dir() - tf.io.gfile.mkdir(os.path.join(logdir, 'eval_dev')) - - p = early_stop.EarlyStop.Params() - p.window = 2 - p.tolerance = 1.0 - p.metric_history.local_filesystem = True - early_stop.MetricHistory.SetLogdirInMetricHistories(p, logdir) - - es = early_stop.EarlyStop(p) - es.FProp(None) - with self.session() as sess: - jobname = es.metric_history.params.jobname - metric = es.metric_history.params.metric - mh = early_stop.MetricHistory - - mh.ConditionalAppend(jobname, metric, 1, 10.0) - self.assertFalse(es.Stop(sess)) - self.assertEqual(es.best_step, 1) - self.assertEqual(es.last_step, 1) - - mh.ConditionalAppend(jobname, metric, 2, 5.0) - self.assertFalse(es.Stop(sess)) - self.assertEqual(es.best_step, 2) - self.assertEqual(es.last_step, 2) - - mh.ConditionalAppend(jobname, metric, 3, 4.0) - self.assertFalse(es.Stop(sess)) - self.assertEqual(es.best_step, 2) - self.assertEqual(es.last_step, 3) - - mh.ConditionalAppend(jobname, metric, 5, 4.0) - self.assertTrue(es.Stop(sess)) - self.assertEqual(es.best_step, 2) - self.assertEqual(es.last_step, 5) - - def testEarlyStoppingAscendingMetric(self): - logdir = tf.test.get_temp_dir() - tf.io.gfile.mkdir(os.path.join(logdir, 'decoder_dev')) - - p = early_stop.EarlyStop.Params() - p.window = 2 - p.tolerance = 1.0 - p.metric_history.local_filesystem = True - p.metric_history.minimize = False - p.metric_history.jobname = 'decoder_dev' - p.metric_history.metric = 'canonical_bleu' - early_stop.MetricHistory.SetLogdirInMetricHistories(p, logdir) - - es = early_stop.EarlyStop(p) - es.FProp(None) - with self.session() as sess: - jobname = es.metric_history.params.jobname - metric = es.metric_history.params.metric - mh = early_stop.MetricHistory - - mh.ConditionalAppend(jobname, metric, 1, 0.0) - self.assertFalse(es.Stop(sess)) - self.assertEqual(es.best_step, 1) - self.assertEqual(es.last_step, 1) - - mh.ConditionalAppend(jobname, metric, 2, 1.0) - self.assertFalse(es.Stop(sess)) - self.assertEqual(es.best_step, 1) - self.assertEqual(es.last_step, 2) - - mh.ConditionalAppend(jobname, metric, 3, 2.5) - self.assertFalse(es.Stop(sess)) - self.assertEqual(es.best_step, 3) - self.assertEqual(es.last_step, 3) - - mh.ConditionalAppend(jobname, metric, 5, 2.0) - self.assertFalse(es.Stop(sess)) - self.assertEqual(es.best_step, 3) - self.assertEqual(es.last_step, 5) - - mh.ConditionalAppend(jobname, metric, 6, 1.0) - self.assertTrue(es.Stop(sess)) - self.assertEqual(es.best_step, 3) - self.assertEqual(es.last_step, 6) - - def testEarlyStoppingAscendingTfEvents(self): - logdir = test_helper.test_src_dir_path('core/ops') - p = early_stop.EarlyStop.Params() - p.window = 1000 - p.tolerance = 0.0 - p.metric_history.local_filesystem = True - p.metric_history.minimize = False - p.metric_history.jobname = 'testdata' - p.metric_history.metric = 'bleu/dev' - p.metric_history.tfevent_file = True - early_stop.MetricHistory.SetLogdirInMetricHistories(p, logdir) - - es = early_stop.EarlyStop(p) - es.FProp(None) - with self.session() as sess: - self.assertTrue(es.Stop(sess)) - self.assertEqual(es.best_step, 102600) - self.assertEqual(es.last_step, 185200) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/egdd.py b/lingvo/core/egdd.py deleted file mode 100644 index c8f0ef04..00000000 --- a/lingvo/core/egdd.py +++ /dev/null @@ -1,181 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Exponentiated Gradient Delta-Delta optimizer.""" - -# pylint: disable=g-direct-tensorflow-import -from tensorflow.python.framework import ops -from tensorflow.python.ops import array_ops -from tensorflow.python.ops import clip_ops -from tensorflow.python.ops import control_flow_ops -from tensorflow.python.ops import linalg_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.ops import state_ops -from tensorflow.python.training import optimizer -# pylint: enable=g-direct-tensorflow-import - - -class EGDD(optimizer.Optimizer): - """A version of GD Momentum with adaptive gain and learning rate. - - Exponentiated Gradient Delta-delta optimizer starts with a local gain of 1.0 - for every weight and a lr_scale of 1.0 for all weights. The EGDD update rule - applies: - - momentum <- mu * momentum + learning_rate * gain * grad - var <- var - lr_scale * momentum - - The gain as well as the lr_scale are updated using the unnormalized - exponentiated gradient algorithm [KW97]. - - Reference: TBA - - [KW97] Kivinen, J., & Warmuth, M. K. Exponentiated gradient versus gradient - descent for linear predictors. Information and Computation, 1997. - """ - - def __init__(self, - learning_rate, - momentum, - beta=0.9, - gain_learning_rate=0.01, - scale_learning_rate=0.001, - initial_gain=1.0, - min_gain=1e-2, - max_gain=1e2, - initial_scale=1.0, - min_scale=1e-1, - max_scale=1e1, - use_directions=True, - use_signs=True, - name="EGDD"): - """Construct a new EG-DD optimizer. - - Args: - learning_rate: A `Tensor` or a floating point value. The learning rate. - momentum: A `Tensor` or a floating point value. - beta: `float` decay rate of the gradient EMA. - gain_learning_rate: `float` gain learning rate. - scale_learning_rate: `float` scale learning rate. - initial_gain: `float` initial gain. - min_gain: `float` minimum gain. - max_gain: `float` maximum gain, - initial_scale: `float` initial scale. - min_scale: `float` minimum learning rate scale. - max_scale: `float` maximum learning rate scale. - use_directions: `bool` whether to use directions only for scale updates. - use_signs: `bool` whether to use the signs for updating gains. - name: Optional name prefix for the operations created when applying - gradients. - - Raises: - ValueError: If the `initial_accumulator_value` is invalid. - - """ - super().__init__(False, name) - self._learning_rate = learning_rate - self._momentum = momentum - self._beta = beta - self._gain_learning_rate = gain_learning_rate - self._scale_learning_rate = scale_learning_rate - self._initial_gain = initial_gain - self._min_gain = min_gain - self._max_gain = max_gain - self._initial_scale = initial_scale - self._min_scale = min_scale - self._max_scale = max_scale - self._use_directions = use_directions - self._use_signs = use_signs - - def _create_slots(self, var_list): - for v in var_list: - self._zeros_slot(v, "momentum", self._name) - self._zeros_slot(v, "gbar", self._name) - g_tensor = ops.convert_to_tensor(v) - gain_init = self._initial_gain * array_ops.ones_like(g_tensor) - _ = self._get_or_make_slot(v, self._initial_scale * array_ops.ones((1)), - "lr_scale", self._name) - _ = self._get_or_make_slot(v, gain_init, "gain", self._name) - _ = self._get_or_make_slot(v, array_ops.zeros((1)), "counter", self._name) - - def _prepare(self): - learning_rate = self._call_if_callable(self._learning_rate) - self._learning_rate_tensor = ops.convert_to_tensor( - learning_rate, name="learning_rate") - momentum = self._call_if_callable(self._momentum) - self._momentum_tensor = ops.convert_to_tensor(momentum, name="momentum") - - def _apply_dense(self, grad, var): - lr_scale = self.get_slot(var, "lr_scale") - momentum = self.get_slot(var, "momentum") - gbar = self.get_slot(var, "gbar") - gain = self.get_slot(var, "gain") - counter = self.get_slot(var, "counter") - counter_updated = state_ops.assign(counter, counter + 1) - - # lr_scale update uses normalized grad and momentum to be independent of dim - normalized_grad = grad / (linalg_ops.norm(grad) + 1e-10) - normalized_momentum = momentum / (linalg_ops.norm(momentum) + 1e-10) - # Apply EG updates on lr_scale: - # grad_lr_scale = -inner_product(current_grad, old_momentum) - # lr_scale <- lr_scale * exp(-scale_learning_rate * grad_lr_scale) - lr_scale_unnormalized_updated = clip_ops.clip_by_value( - lr_scale * math_ops.exp( - self._scale_learning_rate * math_ops.reduce_sum(grad * momentum)), - self._min_scale, self._max_scale) - lr_scale_normalized_updated = clip_ops.clip_by_value( - lr_scale * math_ops.exp(self._scale_learning_rate * math_ops.reduce_sum( - normalized_grad * normalized_momentum)), self._min_scale, - self._max_scale) - lr_scale_updated = state_ops.assign( - lr_scale, - array_ops.where(self._use_directions, lr_scale_normalized_updated, - lr_scale_unnormalized_updated)) - # remove the bias of zero initialization in gbar - corrected_gbar = gbar / ( - 1.0 - self._beta**math_ops.maximum(counter_updated - 1, 1)) - # Apply EG updates on gain: - # grad_gain = - current_grad * old_gbar - # gain <- gain * exp(-gain_learning_rate * grad_gain) - gain_unnormalized_updated = clip_ops.clip_by_value( - gain * math_ops.exp(self._gain_learning_rate * grad * corrected_gbar), - self._min_gain, self._max_gain) - # Normalized update uses sign(grad) * sign(gbar) as a proxy for grad_gain. - gain_normalized_updated = clip_ops.clip_by_value( - gain * math_ops.exp(self._gain_learning_rate * math_ops.sign(grad) * - math_ops.sign(gbar)), self._min_gain, - self._max_gain) - gain_updated = state_ops.assign( - gain, - array_ops.where(self._use_signs, gain_normalized_updated, - gain_unnormalized_updated)) - scaled_g = self._learning_rate_tensor * gain_updated * grad - with ops.control_dependencies([lr_scale_updated, scaled_g]): - momentum_updated = state_ops.assign( - momentum, self._momentum_tensor * momentum + scaled_g) - gbar_updated = state_ops.assign( - gbar, self._beta * gbar + (1.0 - self._beta) * grad) - with ops.control_dependencies([gbar_updated]): - return state_ops.assign_sub(var, lr_scale_updated * momentum_updated) - - def _resource_apply_dense(self, grad, var): - return self._apply_dense(grad, var) - - # Sparse gradients are not handled currently and is part of future work. - def _resource_apply_sparse(self, grad_values, var, grad_indices): - return control_flow_ops.no_op() - - def _apply_sparse(self, grad, var): - return control_flow_ops.no_op() diff --git a/lingvo/core/egdd_test.py b/lingvo/core/egdd_test.py deleted file mode 100644 index e537fdd3..00000000 --- a/lingvo/core/egdd_test.py +++ /dev/null @@ -1,114 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for EG-DD optimizer.""" -from lingvo import compat as tf -from lingvo.core import egdd -from lingvo.core import test_utils - - -class EGDD(test_utils.TestCase): - - def testDenseLayer(self): - """EG-DD update.""" - - with self.cached_session() as sess: - var = tf.Variable([0.5, 1.0]) - grad = tf.placeholder(tf.float32, shape=[2]) - opt = egdd.EGDD( - learning_rate=0.1, - momentum=0.9, - beta=0.1, - gain_learning_rate=1e-2, - scale_learning_rate=1e-3, - use_signs=False) - - step = opt.apply_gradients([(grad, var)]) - tf.global_variables_initializer().run() - - pre_var = sess.run(var) - pre_momentum = sess.run(opt.get_slot(var, 'momentum')) - pre_gain = sess.run(opt.get_slot(var, 'gain')) - pre_lr_scale = sess.run(opt.get_slot(var, 'lr_scale')) - self.assertAllClose([0.5, 1.0], pre_var) - self.assertAllClose([0.0, 0.0], pre_momentum) - self.assertAllClose([1.0, 1.0], pre_gain) - self.assertAllClose([1.0], pre_lr_scale) - sess.run(step, feed_dict={grad: [0.1, -0.5]}) - pre_var = sess.run(var) - pre_momentum = sess.run(opt.get_slot(var, 'momentum')) - pre_gain = sess.run(opt.get_slot(var, 'gain')) - pre_lr_scale = sess.run(opt.get_slot(var, 'lr_scale')) - self.assertAllClose([0.49, 1.05], pre_var) - self.assertAllClose([0.01, -0.05], pre_momentum) - self.assertAllClose([1, 1], pre_gain) - self.assertAllClose([1.0], pre_lr_scale) - sess.run(step, feed_dict={grad: [-1.0, -1.5]}) - pre_var = sess.run(var) - pre_momentum = sess.run(opt.get_slot(var, 'momentum')) - pre_gain = sess.run(opt.get_slot(var, 'gain')) - pre_lr_scale = sess.run(opt.get_slot(var, 'lr_scale')) - self.assertAllClose([0.5810, 1.2463], pre_var, atol=1e-4) - self.assertAllClose([-0.0909, -0.1961], pre_momentum, atol=1e-4) - self.assertAllClose([0.9990, 1.0075], pre_gain, atol=1e-4) - self.assertAllClose([1.0007], pre_lr_scale, atol=1e-4) - - def testDenseLayerSigns(self): - """EG-DD update.""" - - with self.cached_session() as sess: - var = tf.Variable([0.5, 1.0]) - grad = tf.placeholder(tf.float32, shape=[2]) - opt = egdd.EGDD( - learning_rate=0.1, - momentum=0.9, - beta=0.1, - gain_learning_rate=1e-2, - scale_learning_rate=1e-3, - use_signs=True) - - step = opt.apply_gradients([(grad, var)]) - tf.global_variables_initializer().run() - - pre_var = sess.run(var) - pre_momentum = sess.run(opt.get_slot(var, 'momentum')) - pre_gain = sess.run(opt.get_slot(var, 'gain')) - pre_lr_scale = sess.run(opt.get_slot(var, 'lr_scale')) - self.assertAllClose([0.5, 1.0], pre_var) - self.assertAllClose([0.0, 0.0], pre_momentum) - self.assertAllClose([1.0, 1.0], pre_gain) - self.assertAllClose([1.0], pre_lr_scale) - sess.run(step, feed_dict={grad: [0.1, -0.5]}) - pre_var = sess.run(var) - pre_momentum = sess.run(opt.get_slot(var, 'momentum')) - pre_gain = sess.run(opt.get_slot(var, 'gain')) - pre_lr_scale = sess.run(opt.get_slot(var, 'lr_scale')) - self.assertAllClose([0.49, 1.05], pre_var) - self.assertAllClose([0.01, -0.05], pre_momentum) - self.assertAllClose([1, 1], pre_gain) - self.assertAllClose([1.0], pre_lr_scale) - sess.run(step, feed_dict={grad: [-1.0, -1.5]}) - pre_var = sess.run(var) - pre_momentum = sess.run(opt.get_slot(var, 'momentum')) - pre_gain = sess.run(opt.get_slot(var, 'gain')) - pre_lr_scale = sess.run(opt.get_slot(var, 'lr_scale')) - self.assertAllClose([0.5801, 1.2466], pre_var, atol=1e-4) - self.assertAllClose([-0.0900, -0.1965], pre_momentum, atol=1e-4) - self.assertAllClose([0.9900, 1.0101], pre_gain, atol=1e-4) - self.assertAllClose([1.0007], pre_lr_scale, atol=1e-4) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ema_test.py b/lingvo/core/ema_test.py deleted file mode 100644 index 61161657..00000000 --- a/lingvo/core/ema_test.py +++ /dev/null @@ -1,142 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for ExponentialMovingAverage support in Lingvo.""" - -import os -import lingvo.compat as tf -from lingvo.core import base_decoder -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import checkpointer -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import test_utils -import numpy as np - - -class TestTask(base_model.BaseTask): - """A task with a single 'encoder' child layer.""" - - def __init__(self, params): - super().__init__(params) - p = self.params - self.CreateChild('encoder', p.encoder) - - -class EmaTest(test_utils.TestCase): - - @classmethod - def TestParams(cls, encoder_params): - p = TestTask.Params() - p.name = 'base_mdl' - p.input = base_input_generator.BaseSequenceInputGenerator.Params() - p.encoder = encoder_params - p.decoder = base_decoder.BaseDecoder.Params() - return p - - def testBatchNormLayer(self): - p = base_model.SingleTaskModel.Params() - p.task = self.TestParams(layers.BatchNormLayer.Params().Set(dim=1)) - p.task.train.ema_decay = 0.9 - p.task.train.ema_decay_moving_vars = True - model = p.Instantiate() - self.assertIsNotNone(model.ema) - task = model._task - task._train_op = tf.no_op() - task.ApplyExponentialMovingAverage(model.ema) - - layer = task.encoder - self.assertLen(layer.vars, 4) - for var in layer.vars.Flatten(): - self.assertIsNotNone(model.ema.average(var), msg=var.name) - beta = layer.vars.beta - mean = layer.vars.moving_mean - - global_step = 100 - beta_1 = np.asarray([.2]) - mean_1 = np.asarray([.03]) - beta_1_ema = beta_1 * .1 - mean_1_ema = mean_1 * .1 - with self.session() as sess: - # Test EMA values. - self.evaluate(tf.global_variables_initializer()) - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), global_step)) - self.evaluate(tf.assign(beta, beta_1)) - self.evaluate(tf.assign(mean, mean_1)) - self.evaluate(task._post_train_ops) - - self.assertAllClose([beta_1, beta_1_ema, mean_1, mean_1_ema], - self.evaluate([ - beta, - model.ema.average(beta), mean, - model.ema.average(mean) - ])) - - # Test checkpointer. - train_dir = os.path.join(self.get_temp_dir(), 'testSaveRestore') - os.mkdir(train_dir) - saver = checkpointer.Checkpointer(train_dir, model) - saver.Save(sess, model.global_step) - - self.assertTrue( - os.path.isfile( - os.path.join(train_dir, 'ckpt-%08d.index' % global_step))) - - # Restore from ckpt in training mode. - with self.session(graph=tf.Graph()) as sess: - model = p.Instantiate() - self.assertIsNotNone(model.ema) - task = model._task - task._train_op = tf.no_op() - task.ApplyExponentialMovingAverage(model.ema) - layer = task.encoder - for var in layer.vars.Flatten(): - self.assertIsNotNone(model.ema.average(var), msg=var.name) - beta = layer.vars.beta - mean = layer.vars.moving_mean - - saver = checkpointer.Checkpointer(train_dir, model) - saver.RestoreIfNeeded(sess) - - self.assertAllClose([beta_1, beta_1_ema, mean_1, mean_1_ema], - self.evaluate([ - beta, - model.ema.average(beta), mean, - model.ema.average(mean) - ])) - - # Restore from ckpt in eval mode. - with self.session(graph=tf.Graph()) as sess, self.SetEval(True): - model = p.Instantiate() - self.assertIsNotNone(model.ema) - task = model._task - # task._train_op = tf.no_op() - # task.ApplyExponentialMovingAverage(model.ema) - layer = task.encoder - # for var in layer.vars.Flatten(): - # self.assertIsNotNone(model.ema.average(var), msg=var.name) - beta = layer.vars.beta - mean = layer.vars.moving_mean - - saver = checkpointer.Checkpointer(train_dir, model) - saver.RestoreIfNeeded(sess) - - # Both beta and mean should use the EMA value. - self.assertAllClose([beta_1_ema, mean_1_ema], self.evaluate([beta, mean])) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/generic_input.py b/lingvo/core/generic_input.py deleted file mode 100644 index be6693cc..00000000 --- a/lingvo/core/generic_input.py +++ /dev/null @@ -1,170 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Generic input.""" - -import lingvo.compat as tf -from lingvo.core import ops -from lingvo.core import py_utils -from tensorflow.python.util import tf_inspect # pylint: disable=g-direct-tensorflow-import - - -def GenericInput(processor, **kwargs): - """Builds a generic input pipeline. - - Example usage:: - - def ParseRecord(record): - # Given a tf.string record, return a (NestedMap, bucketing key) pair. - feature_map = ... - features = tf.io.parse_single_example(record, feature_map) - # Each example is represented by a NestedMap of tensors (without a - # batch dimension). - example = py_utils.NestedMap(field1=..., field2=...) - # bucketing_key is a scalar convertible to tf.int32. - # Use 1 if all examples are of the same size. - bucketing_key = 1 - return example, bucketing_key - - input_batch, bucket_keys = GenericInput(ParseRecord, file_pattern=..., ...) - # input_batch is a NestedMap of tensors, where dim 0 of each tensor - # represents the batch dimension. - input_batch.field1 = ... - - ParseRecord can also take both 'source_id' and 'record' as inputs (the arg - names must be exactly 'source_id' and 'record'): - - def ParseRecord(source_id, record): - # Given a tf.int32 source_id and a tf.string record, return a (NestedMap, - # bucketing key) pair. - example = py_utils.NestedMap(source_id=source_id, ...) - ... - return example, bucketing_key - - input_batch, bucket_keys = GenericInput(ParseRecord, file_pattern=..., ...) - - Args: - processor: a function that takes either a tf.string record or a - (source_id: tf.int32, record: tf.string) pair as input and returns a tuple - (output, bucketing_key). `output` must be a NestedMap or a list of tensors - representing an example. `bucketing_key` must be a scalar convertible to - a tf.int32 tensor that represents the bucketing key (e.g., sequence - length for sequence inputs). If `bucketing_key` is a negative number, - the record is dropped. - **kwargs: additional keyword args for x_ops.generic_input. - - Returns: - A tuple of (outputs, bucket_keys): - - - outputs: a NestedMap or a list of tensors, similar to `processor`'s - return, except every tensor will have an additional dimension 0 that - represents the batch dimension. - - bucket_keys: a tf.int32 vector. - """ - output_tmpl = py_utils.NestedMap() - - @tf.function(autograph=False) - def _FlatOutputProcessor(source_id, record): - """Returns a flattened list of 'processor(inputs)'.""" - processor_spec = tf_inspect.getargspec(processor) - tf.logging.debug('GenericInput.processor.argspec=%s', processor_spec) - processor_args = set(processor_spec.args) - set(['self']) - if len(processor_args) == 1: - output, bucketing_key = processor(record) - elif processor_args == set(['source_id', 'record']): - output, bucketing_key = processor(source_id=source_id, record=record) - else: - raise ValueError( - 'GenericInput: processor should take either a single arg ' - 'or two args named as "source_id" and "record". ' - 'Actual: %s' % processor_args) - if isinstance(output, list): - assert output - assert all(isinstance(x, tf.Tensor) for x in output), '{}'.format(output) - else: - assert isinstance(output, py_utils.NestedMap), '{}'.format(output) - assert output - assert all( - isinstance(x, tf.Tensor) for x in output.Flatten()), '{}'.format( - output.DebugString()) - bucketing_key = tf.cast(bucketing_key, tf.int32) - tf.logging.debug('Processor outputs=%s bucketing_key=%s', output, - bucketing_key) - output_tmpl.out_values = output - flat_output_tmpl = output_tmpl.Flatten() - tf.logging.debug('Processor flat outputs=%s', flat_output_tmpl) - tf.logging.debug('extra_inputs=%s extra_args=%s extra_vars=%s', - py_utils.GetExtraInputs(), py_utils.GetExtraArgs(), - py_utils.GetExtraVars()) - assert not py_utils.GetExtraArgs(), ( - 'fns {} is not pure: extra_args={}'.format(processor, - py_utils.GetExtraArgs())) - return flat_output_tmpl + [bucketing_key] - - proc_fn = _FlatOutputProcessor.get_concrete_function( - tf.TensorSpec([], tf.int32), tf.TensorSpec([], tf.string)) - - out_types = [ - tf.DType(a.type) for a in proc_fn.function_def.signature.output_arg - ] - assert out_types[-1] == tf.int32, ('%s is not expected.' % out_types[-1]) - flat_outputs, bucket_keys = ops.gen_x_ops.generic_input( - processor=proc_fn, out_types=out_types[:-1], **kwargs) - tf.logging.debug('x_ops.generic_input flat_outputs=%s', flat_outputs) - # Pack flat_outputs to outputs. - outputs = output_tmpl.Pack(flat_outputs).out_values - tf.logging.debug('x_ops.generic_input outputs=%s', outputs) - return outputs, bucket_keys - - -def ReplicatedGenericInput(processor, num_replicas, replica_device_fn, - **kwargs): - """Builds a replicated input pipeline. - - This is similar to GenericInput, except that the input processing can be - distributed across devices and then concatenated at the current device. - - Args: - processor: see comments for GenericInput. - num_replicas: the number of input processing replicas. Usually set to number - of infeed hosts. - replica_device_fn: a int -> string function that takes the replica index in - range [0, num_replicas) and returns a TF device string, e.g., - lambda i: '/task:{}/device:CPU:0'.format(i) - **kwargs: additional keyword args for x_ops.generic_input. - - Returns: - A tuple of (outputs, bucket_keys): - - - outputs: a NestedMap or a list of tensors, similar to `processor`'s - return, except every tensor will have an additional dimension 0 that - represents the batch dimension. The batch size will be - (num_replicas * bucket_batch_limit[...]), i.e., - kwargs['bucket_batch_limit'] specifies the per-replica batch size. - - bucket_keys: a tf.int32 vector. - """ - if num_replicas > 1 and 'bucket_batch_limit' in kwargs: - assert all(b == max(kwargs['bucket_batch_limit']) - for b in kwargs['bucket_batch_limit']) - replica_outputs = [] - for replica_i in range(num_replicas): - replica_device = replica_device_fn(replica_i) - with tf.device(replica_device): - replica_outputs.append(GenericInput(processor, **kwargs)) - output_nmaps, output_bucket_keys = zip(*replica_outputs) - concat_nmap = tf.nest.map_structure(lambda *t: tf.concat(t, axis=0), - *output_nmaps) - concat_bucket_keys = tf.concat(output_bucket_keys, axis=0) - return concat_nmap, concat_bucket_keys diff --git a/lingvo/core/generic_input_test.py b/lingvo/core/generic_input_test.py deleted file mode 100644 index 5c8535c3..00000000 --- a/lingvo/core/generic_input_test.py +++ /dev/null @@ -1,300 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for generic_input_op.""" - -import collections -import os -import pickle -from absl.testing import parameterized -from lingvo import compat as tf -from lingvo.core import generic_input -from lingvo.core import py_utils -from lingvo.core import test_utils -import numpy as np - - -def get_test_input(path, bucket_batch_limit=8, **kwargs): - return generic_input.GenericInput( - file_pattern='tfrecord:' + path, - file_random_seed=0, - file_buffer_size=32, - file_parallelism=4, - bucket_batch_limit=[bucket_batch_limit], - **kwargs) - - -def run_basic_graph(use_nested_map, - bucket_fn=lambda x: 1, - bucket_batch_limit=8): - # Generate a test file w/ 100 records. - tmp = os.path.join(tf.test.get_temp_dir(), 'basic') - with tf.python_io.TFRecordWriter(tmp) as w: - for i in range(100): - w.write(('%08d' % i).encode('utf-8')) - - # A simple string parsing routine. Just convert a string to a - # number. - def str_to_num(s): - return np.array(float(s), dtype=np.float32) - - # A record processor written in TF graph. - def _process(source_id, record): - num, = tf.py_func(str_to_num, [record], [tf.float32]) - num = tf.stack([num, tf.square(num)]) - if use_nested_map: - return py_utils.NestedMap( - source_id=source_id, record=record, num=num), bucket_fn(num) - else: - return [source_id, record, num], bucket_fn(num) - - # Samples random records from the data files and processes them - # to generate batches. - inputs, _ = get_test_input( - tmp, - bucket_batch_limit=bucket_batch_limit, - bucket_upper_bound=[1], - processor=_process) - if use_nested_map: - return inputs - else: - src_ids, strs, vals = inputs - return py_utils.NestedMap(source_id=src_ids, record=strs, num=vals) - - -class GenericInputOpTest(test_utils.TestCase, parameterized.TestCase): - - @parameterized.named_parameters(('OutputList', False, 8), - ('OutputNestedMap', True, 8), - ('OutputNestedMap_Batch1', True, 1)) - def testBasic(self, use_nested_map, bucket_batch_limit): - input_batch = run_basic_graph( - use_nested_map=use_nested_map, bucket_batch_limit=bucket_batch_limit) - with self.session(): - record_seen = set() - for i in range(100): - ans_input_batch = self.evaluate(input_batch) - for s in ans_input_batch.record: - record_seen.add(s) - self.assertEqual(ans_input_batch.source_id.shape, (bucket_batch_limit,)) - self.assertEqual(ans_input_batch.record.shape, (bucket_batch_limit,)) - self.assertEqual(ans_input_batch.num.shape, (bucket_batch_limit, 2)) - ans_vals = ans_input_batch.num - self.assertAllEqual(np.square(ans_vals[:, 0]), ans_vals[:, 1]) - for i in range(100): - self.assertIn(('%08d' % i).encode('utf-8'), record_seen) - - def testPadding(self): - # Generate a test file w/ 50 records of different lengths. - tmp = os.path.join(tf.test.get_temp_dir(), 'basic') - with tf.python_io.TFRecordWriter(tmp) as w: - for n in range(1, 50): - w.write(pickle.dumps(np.full([n, 3, 3], n, np.int32))) - - g = tf.Graph() - with g.as_default(): - # A record processor written in TF graph. - def _process(record): - num = tf.py_func(pickle.loads, [record], tf.int32) - bucket_key = tf.shape(num)[0] - return [num, tf.transpose(num, [1, 0, 2])], bucket_key - - # Samples random records from the data files and processes them - # to generate batches. - (vals_t, transposed_vals_t), _ = get_test_input( - tmp, - bucket_upper_bound=[10], - processor=_process, - dynamic_padding_dimensions=[0, 1], - dynamic_padding_constants=[0] * 2) - - with self.session(graph=g): - for _ in range(10): - vals, transposed_vals = self.evaluate([vals_t, transposed_vals_t]) - print(vals, np.transpose(transposed_vals, [0, 2, 1, 3])) - self.assertEqual(vals.shape[0], 8) - self.assertEqual(vals.shape[2], 3) - self.assertEqual(vals.shape[3], 3) - largest = np.amax(vals) - self.assertLessEqual(largest, 10) - self.assertEqual(vals.shape[1], largest) - for j in range(8): - n = vals[j, 0, 0, 0] - self.assertTrue(np.all(vals[j, :n] == n)) - self.assertTrue(np.all(vals[j, n:] == 0)) - self.assertAllEqual(vals, np.transpose(transposed_vals, [0, 2, 1, 3])) - - def testDropRecordIfNegativeBucketKey(self): - - def bucket_fn(num): - # Drops record if num[0] is odd. - return tf.cond( - tf.equal(tf.math.floormod(num[0], 2), 0), lambda: 1, - lambda: -tf.cast(num[0], tf.int32)) - - input_batch = run_basic_graph(use_nested_map=False, bucket_fn=bucket_fn) - - with self.session(): - record_seen = set() - for i in range(100): - ans_input_batch = self.evaluate(input_batch) - for s in ans_input_batch.record: - record_seen.add(s) - for i in range(100): - if i % 2 == 0: - self.assertIn(('%08d' % i).encode('utf-8'), record_seen) - else: - self.assertNotIn(('%08d' % i).encode('utf-8'), record_seen) - - def testWithinBatchMixing(self): - # Generate couple files. - def generate_test_data(tag, cnt): - tmp = os.path.join(tf.test.get_temp_dir(), tag) - with tf.python_io.TFRecordWriter(tmp) as w: - for i in range(cnt): - w.write(('%s:%08d' % (tag, i)).encode('utf-8')) - return tmp - - path1 = generate_test_data('input1', 100) - path2 = generate_test_data('input2', 200) - path3 = generate_test_data('input3', 10) - - g = tf.Graph() - with g.as_default(): - # A record processor written in TF graph. - def _process(source_id, record): - return py_utils.NestedMap(source_id=source_id, record=record), 1 - - # Samples random records from the data files and processes them - # to generate batches. - input_batch, buckets = generic_input.GenericInput( - file_pattern=','.join( - ['tfrecord:' + path1, 'tfrecord:' + path2, 'tfrecord:' + path3]), - input_source_weights=[0.2, 0.3, 0.5], - file_random_seed=0, - file_buffer_size=32, - file_parallelism=4, - bucket_batch_limit=[8], - bucket_upper_bound=[1], - processor=_process) - - with self.session(graph=g): - source_id_count = collections.defaultdict(int) - tags_count = collections.defaultdict(int) - total_count = 10000 - for _ in range(total_count): - ans_input_batch, ans_buckets = self.evaluate([input_batch, buckets]) - for s in ans_input_batch.source_id: - source_id_count[s] += 1 - for s in ans_input_batch.record: - tags_count[s.split(b':')[0]] += 1 - self.assertEqual(ans_input_batch.source_id.shape, (8,)) - self.assertEqual(ans_input_batch.record.shape, (8,)) - self.assertAllEqual(ans_buckets, [1] * 8) - self.assertEqual(sum(source_id_count.values()), total_count * 8) - self.assertEqual(sum(tags_count.values()), total_count * 8) - num_records = 8. * total_count - self.assertAlmostEqual( - tags_count[b'input1'] / num_records, 0.2, delta=0.01) - self.assertAlmostEqual( - tags_count[b'input2'] / num_records, 0.3, delta=0.01) - self.assertAlmostEqual( - tags_count[b'input3'] / num_records, 0.5, delta=0.01) - self.assertAlmostEqual(source_id_count[0] / num_records, 0.2, delta=0.01) - self.assertAlmostEqual(source_id_count[1] / num_records, 0.3, delta=0.01) - self.assertAlmostEqual(source_id_count[2] / num_records, 0.5, delta=0.01) - - def testBoolDType(self): - tmp = os.path.join(tf.test.get_temp_dir(), 'bool') - with tf.python_io.TFRecordWriter(tmp) as w: - for i in range(50): - w.write(pickle.dumps(True if i % 2 == 0 else False)) - - g = tf.Graph() - with g.as_default(): - # A record processor written in TF graph. - def _process(record): - bucket_key = 1 - num, = tf.py_func(pickle.loads, [record], [tf.bool]) - return [num], bucket_key - - # Samples random records from the data files and processes them - # to generate batches. - inputs, _ = get_test_input( - tmp, bucket_upper_bound=[1], processor=_process) - - with self.session(graph=g): - for _ in range(10): - inputs_vals = self.evaluate(inputs)[0] - self.assertEqual(inputs_vals.dtype, bool) - - def testExtraArgs(self): - - def _parse_record(record): - del record - example = py_utils.NestedMap(t=tf.convert_to_tensor(0)) - bucketing_key = 1 - return example, bucketing_key - - def _parse_record_stateful(record): - del record - extra = tf.Variable(0) - example = py_utils.NestedMap(t=extra.value()) - bucketing_key = 1 - return example, bucketing_key - - generic_input.GenericInput( - _parse_record, - file_pattern='', - bucket_upper_bound=[1], - bucket_batch_limit=[1]) - - with self.assertRaisesRegex(AssertionError, 'is not pure: extra_args='): - generic_input.GenericInput( - _parse_record_stateful, - file_pattern='', - bucket_upper_bound=[1], - bucket_batch_limit=[1]) - - def testTfData(self): - """Checks that GenericInput can be invoked from a tf.data.Dataset. - - TODO(b/162015923): Fix generic_input op to work with tf.data. - """ - - def _input_batch(): - return run_basic_graph(use_nested_map=True) - - # Trick to create dataset from tensor coming from custom op. - dummy_dataset = tf.data.Dataset.from_tensors(0).repeat() - dataset = dummy_dataset.map(lambda _: _input_batch()) - - with self.session(use_gpu=False) as sess: - it = tf.compat.v1.data.make_initializable_iterator(dataset) - with self.assertRaises(tf.errors.NotFoundError): - # Gives an error that the user-provided function is undefined. - sess.run(it.initializer) - - -class GenericInputOpBenchmark(tf.test.Benchmark): - - def benchmark_basic(self): - input_batch = run_basic_graph(use_nested_map=True) - with tf.Session() as sess: - print(self.run_op_benchmark(sess, input_batch, min_iters=10)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/gpipe.py b/lingvo/core/gpipe.py deleted file mode 100644 index 57e54996..00000000 --- a/lingvo/core/gpipe.py +++ /dev/null @@ -1,607 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""A recurrent model which enables pipelining model parallelism. - -Reference: -'GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism' -https://arxiv.org/abs/1811.06965 - -Example implementation of Transformer Language model: -tasks/lm/layers.GPipeTransformerLm - -Sample params for the one billion words task: -tasks/lm/params/one_billion_wds.OneBWdsGPipeTransformer. - -More examples in machine translation, image classifications and others -will be included. -""" - -import contextlib -import copy - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import builder_layers -from lingvo.core import py_utils -from lingvo.core import recurrent -from lingvo.core import tshape - - -_MICRO_BATCH_STATE_NAME = 'micro_batch_state' -_OVERWRITE_GLOBAL_STEP_COLLECTION = 'lingvo__OVERWRITE_GLOBAL_STEP_COLLECTION' - - -def GetOverWriteGlobalStep(graph=None): - graph = graph or tf.get_default_graph() - mb_tensors = graph.get_collection_ref(_OVERWRITE_GLOBAL_STEP_COLLECTION) - if len(mb_tensors) == 1: - mb_tensor = mb_tensors[0] - else: - mb_tensor = py_utils.GetGlobalStep() - return mb_tensor - - -def SetOverWriteGlobalStep(tensor, graph=None): - graph = graph or tf.get_default_graph() - mb_tensors = graph.get_collection_ref(_OVERWRITE_GLOBAL_STEP_COLLECTION) - if len(mb_tensors) == 1: - mb_tensors[0] = tensor - else: - graph.add_to_collection(_OVERWRITE_GLOBAL_STEP_COLLECTION, tensor) - - -def GenerateStepSeedPair(p, unused_global_step=None, op_seed=None): - """Override py_utils.GenerateStepSeedPair to use GetOverWriteGlobalStep.""" - seed_dtype = tf.int32 if py_utils.use_tpu() else tf.int64 - if p.is_inference and p.random_seed is None: - # Unlike tf.random*, stateless random ops are completely determined by the - # passed-in seeds. This means at inference time the same inputs will produce - # the same outputs, even if the model is supposed to have randomness such as - # dropout during inference. We inject additional randomness only during - # inference if the graph is exported with random_seed=None as a workaround. - return tf.random.uniform([2], maxval=seed_dtype.max, dtype=seed_dtype) - - with tf.name_scope('op_seed') as scope: - global_step = tf.cast(GetOverWriteGlobalStep(), seed_dtype) - step_seed = tf.cast(py_utils.GenerateSeedFromName(scope), seed_dtype) - seeds = tf.stack([global_step, step_seed]) - - if p.random_seed is not None: - seeds += p.random_seed - if op_seed is not None: - seeds += op_seed - return seeds - - -@contextlib.contextmanager -def CellFnFPropOpReplacementWrapper(): - """Hacks to replace certain unwanted tensorflow ops.""" - # Hack to replace GenerateStepSeedPair since global_step is not available - # in temp graph created by optional.while. - saved_get_op_seed = py_utils.GenerateStepSeedPair - py_utils.GenerateStepSeedPair = GenerateStepSeedPair - - yield - - py_utils.GenerateStepSeedPair = saved_get_op_seed - - -def _ToTuple(x): - if isinstance(x, list): - return tuple(x) - return x if isinstance(x, tuple) else (x,) - - -class FeatureExtractionLayer(base_layer.BaseLayer): - """A layer that extrac features from a sequence of layers. - - FeatureExtractionLayer is a layer which connects a few layers in a sequence. - It is also capable of fetching and forwarding activation endpoints. - # TODO(huangyp): Make it a sublayer of builder_layers.SequentialLayer - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('variable_name_prefix', '', - 'Prefix for variable names in sub layers') - p.Define('sub', [], 'A list of layers\' params.') - p.Define('num_act_inputs', 0, 'Number of activation inputs.') - p.Define('num_act_outputs', 0, 'Number of activation outputs.') - p.Define('act_fetch_layers', [], - 'Names of fetch layers that cached extra activations') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.num_act_inputs >= 0 - assert p.num_act_outputs >= 0 - p.act_fetch_layers = p.act_fetch_layers or [] - assert p.num_act_outputs == p.num_act_inputs + len(p.act_fetch_layers) - self._seq = [] - for sub in p.sub: - assert sub.name - sub.name = p.variable_name_prefix + sub.name - self.CreateChild(sub.name, sub) - self._seq.append((sub.name, self.children[sub.name])) - - def FProp(self, theta, *args): - p = self.params - assert len(args) > p.num_act_inputs - out_args = args[:-p.num_act_inputs] if p.num_act_inputs > 0 else args - extra_args = args[-p.num_act_inputs:] if p.num_act_inputs > 0 else () - for (name, ch) in self._seq: - th = theta[name] - out_args = _ToTuple(out_args) - out_args = ch.FProp(th, *out_args) - # Append fetched activations to fprop outputs. - for fetch_layer in p.act_fetch_layers: - assert fetch_layer in self.children - activation = self.children[fetch_layer].activation - if isinstance(activation, (tuple, list)): - activation = activation[0] - extra_args += (activation,) - if extra_args: - out_args = _ToTuple(out_args) + extra_args - return out_args - - @classmethod - def FPropMeta(cls, p, *args): - assert len(args) > p.num_act_inputs - seq_args = args[:-p.num_act_inputs] if p.num_act_inputs > 0 else args - extra_args = args[-p.num_act_inputs:] if p.num_act_inputs > 0 else () - total = 0 - act_fetch_metas = {} - for sub in p.sub: - meta = sub.cls.FPropMeta(sub, *seq_args) - if sub.name in p.act_fetch_layers: - act_fetch_metas[sub.name] = meta.out_shapes[0] - total += meta.flops - seq_args = meta.out_shapes - for fetch_layer in p.act_fetch_layers: - extra_args += (act_fetch_metas[fetch_layer],) - return py_utils.NestedMap(flops=total, out_shapes=seq_args + extra_args) - - -def PartitionSequentialLayers(params, num_partitions, *shapes): - r"""Partition a layer composed of sequential layers. - - This routine strives to partition layers so that each partition costs roughly - the same flops given the input shapes. - - Args: - params: A layer param or a list of layer param. - num_partitions: The desired number of partitions. - *shapes: A tuple of tshape.Shape representing input tensors to the first - layer. - - Returns: - A list of FeatureExtractionLayer params. - """ - - # Recursively concatenate SequentialLayer into a list. - def FlattenSeq(p): - if isinstance(p, list): - return p - if p.cls not in [builder_layers.SequentialLayer, FeatureExtractionLayer]: - return [p.Copy()] - subs = [] - for _ in range(p.repeat): - for s in p.sub: - subs += FlattenSeq(s) - return subs - - subs = FlattenSeq(params) - - assert len(shapes) == 1 - tf.logging.info('num_partitions: {} input_shape: {}'.format( - num_partitions, shapes[0])) - - # Computes the estimate cost for each sub layer. - total, histo, output_shapes = 0, [], [] - for i, s in enumerate(subs): - s.name = 'cell_%03d' % i - meta = s.cls.FPropMeta(s, *shapes) - total += meta.flops - histo.append(total) - output_shapes.append(meta.out_shapes) - shapes = meta.out_shapes - tf.logging.vlog(1, 'len %d histogram = %s', len(subs), histo) - - # Computes the normalized cumulative histogram of the layer's cost. - histo_pct = [float(x / total) for x in histo] - tf.logging.vlog(1, 'cost pct = %s', histo_pct) - - # i-th sub layer is put into partition j, where j is roughly i-th cumulative - # histogram times num_partitions. - parts = [[] for _ in range(num_partitions)] - parts_cost = [0] * num_partitions - pre_hist_cost = 0 - for i, s in enumerate(subs): - j = min(int(histo_pct[i] * num_partitions), num_partitions - 1) - # The boundary at parts[j] where j > 0 - if j > 0 and not parts[j]: - parts_cost[j - 1] = histo_pct[i - 1] - pre_hist_cost - pre_hist_cost = histo_pct[i - 1] - parts[j].append(s) - - parts_cost[num_partitions - 1] = 1.0 - pre_hist_cost - seqs = [] - for i, pa in enumerate(parts): - tf.logging.info('Partition %d #subs %d #cost %.3f', i, len(pa), - parts_cost[i]) - - seqs.append(FeatureExtractionLayer.Params().Set(name='d%d' % i, sub=pa)) - return seqs - - -class SeqLayer(base_layer.BaseLayer): - """Round-robin every children cells in cell_tpl among worker devices.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('before_tpl', [], - 'Config for the CNN layers that runs before pipelining.') - p.Define('cell_tpl', [], 'A list of FeatureExtractionLayer layers.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._before_layers = [] - self._cells = [] - for l in p.before_tpl: - self.CreateChild(l.name, l) - self._before_layers.append((l.name, self.children[l.name])) - for l in p.cell_tpl: - self.CreateChild(l.name, l) - self._cells.append((l.name, self.children[l.name])) - - def _CreateChildrenVariables(self): - p = self.params - - num_cells = len(p.cell_tpl) - before_tpl_device = '' - cell_devices = [''] * num_cells - if py_utils.use_tpu(): - cluster = self.cluster - before_tpl_device = cluster.WorkerDeviceInModelSplit(0) - cell_devices = [ - cluster.WorkerDeviceInModelSplit(i) for i in range(num_cells) - ] - - for unused_name, l in self._before_layers: - with tf.device(before_tpl_device): - l.InstantiateVariables() - - for i, (unused_name, l) in enumerate(self._cells): - with tf.device(cell_devices[i]): - l.InstantiateVariables() - - super()._CreateChildrenVariables() - - def FProp(self, theta, *args): - """Round-robin every children cells in cell_tpl among worker devices. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - *args: Input args - - Returns: - A list contains one tensor of [batch_size, feature_height, feature_width, - channel]. - """ - num_layers = len(self.params.cell_tpl) - cluster = self.cluster - - for (name, l) in self._before_layers: - l_theta = theta[name] - args = _ToTuple(args) - args = l.FProp(l_theta, *args) - for i in range(num_layers): - with tf.device(cluster.WorkerDeviceInModelSplit(i)): - cell_name, cell = self._cells[i] - args = _ToTuple(args) - args = cell.FProp(theta[cell_name], *args) - - return args - - -class PipeliningLayer(SeqLayer): - """Pipelining a sequence of layers on multiple devices.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_micro_batches', 1, 'Number of micro batches.') - p.Define('micro_batch_size', None, 'Size of a micro batch.') - p.Define('batch_dim', 0, 'The batch dimension.') - p.Define('state_dtype', None, 'Externally specify dtype for states.') - p.Define( - 'nested_map_fprop', False, 'Whether arguments and returns of ' - 'cell fprop functions are nested maps') - return p - - def _CalculateOutputShapes(self, input_shapes): - """Calcuate the output shape of intermediate layers. - - Given the FPropMeta function in each FeatureExtractionLayer, calcuates - the shapes of outputs of that layer. This is used to recover the shape - information in StackedRecurrent. - - Args: - input_shapes: NestedMap or tuple of input TensorShapes. - - Returns: - Return a list of K + 1 NestedMaps or lists of tShape where K is - the number of partitions. - """ - p = self.params - shapes = [] - - # Converts TensorShape to tshape.Shape. - def _ToTShape(x): - if x is None: - return None - return tshape.Shape(x.as_list()) - - shapes = py_utils.Transform(_ToTShape, input_shapes) - shapes = _ToTuple(shapes) - - state_shapes = [] - for (_, cell) in self._before_layers: - shapes = cell.FPropMeta(cell.params, *shapes).out_shapes - - state_shapes.append(shapes[0] if p.nested_map_fprop else shapes) - - for (_, cell) in self._cells: - shapes = cell.FPropMeta(cell.params, *shapes).out_shapes - state_shapes.append(shapes[0] if p.nested_map_fprop else shapes) - - return state_shapes - - def _get_state_dtype(self, *args): - if self.params.state_dtype: - return self.params.state_dtype - if self.params.nested_map_fprop: - inputs = args[0].Filter(lambda x: x is not None) - return py_utils.Flatten(inputs)[0].dtype - return args[0].dtype - - def _get_input_shapes(self, *args): - p = self.params - if p.nested_map_fprop: - assert len(args) == 1 - assert isinstance(args[0], py_utils.NestedMap) - input_tensors = py_utils.Flatten(args[0]) - else: - input_tensors = _ToTuple(args) - # Get batch size from the first tensor which is not None. - mini_batch_size = None - for input_tensor in input_tensors: - if input_tensor is not None: - mini_batch_size = input_tensor.get_shape().as_list()[p.batch_dim] - assert mini_batch_size is not None - micro_batch_size = p.micro_batch_size - if not micro_batch_size: - if p.num_micro_batches > mini_batch_size: - p.num_micro_batches = mini_batch_size - micro_batch_size = mini_batch_size // p.num_micro_batches - if mini_batch_size is not None: - if micro_batch_size * p.num_micro_batches != mini_batch_size: - raise ValueError('micro_batch_size * num_micro_batches != batch_size.') - - input_shapes = () - for input_tensor in input_tensors: - if input_tensor is not None: - input_shape = input_tensor.get_shape().as_list() - input_shape[p.batch_dim] = micro_batch_size - input_shapes += (tf.TensorShape(input_shape),) - else: - input_shapes += (None,) - - if p.nested_map_fprop: - input_shapes = py_utils.Pack(args[0], input_shapes) - return input_shapes - - def FProp(self, theta, *args): - """Run multiple cells in different devices in a pipelining manner. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - *args: Non-keyworded variable length argument list of input tensors. - - Returns: - A list of output tensors - """ - # TODO(huangyp): handle optional None inputs. - p = self.params - if self.do_eval and self.cluster.num_devices_per_split == 1: - outputs = copy.copy(args) - for (name, l) in self._before_layers + self._cells: - outputs = _ToTuple(outputs) - outputs = l.FProp(theta[name], *outputs) - return outputs - - num_cells = len(p.cell_tpl) - cluster = self.cluster - - # Compute shapes of input and output tensors. - input_shapes = self._get_input_shapes(*args) - state_dtype = self._get_state_dtype(*args) - state_shapes = self._CalculateOutputShapes(input_shapes) - tf.logging.info('state_shapes={}'.format(state_shapes)) - - def GetCellFn(i): - """Get the ith feature extraction layer.""" - - def CellFn(theta, state0, inputs): - """A cell fn is exectued inside of StackedRecurrent.""" - del state0 - - def _FPropInputSetShape(name, t_shape): - if t_shape is None: - return None - inputs[name].set_shape(t_shape.ToTensorShape().as_list()) - return inputs[name] - - if p.nested_map_fprop: - # pylint: disable=protected-access - fprop_inputs = state_shapes[i]._RecursiveMap(_FPropInputSetShape) - # pylint: enable=protected-access - else: - fprop_inputs = [] - for input_idx, input_shape in enumerate(state_shapes[i]): - name = 's{}'.format(input_idx) - fprop_inputs.append(_FPropInputSetShape(name, input_shape)) - - with py_utils.RemoveAssertContext(remove=True): - with CellFnFPropOpReplacementWrapper(): - tf.logging.info('cell {} input {}'.format(i, fprop_inputs)) - mb_tensor = inputs[_MICRO_BATCH_STATE_NAME] - SetOverWriteGlobalStep(mb_tensor) - _, cell = self._cells[i] - fprop_inputs = _ToTuple(fprop_inputs) - outputs = cell.FProp(theta, *fprop_inputs) - - if p.nested_map_fprop: - assert py_utils.IsCompatible(outputs, state_shapes[i + 1]) - state1 = outputs.Filter(lambda x: x is not None) - else: - state1 = py_utils.NestedMap() - outputs = _ToTuple(outputs) - assert len(outputs) == len(state_shapes[i + 1]) - for output_idx in range(len(outputs)): - if outputs[output_idx] is not None: - name = 's{}'.format(output_idx) - state1[name] = outputs[output_idx] - state1[_MICRO_BATCH_STATE_NAME] = mb_tensor - return state1, py_utils.NestedMap() - - return CellFn - - cell_fns = [] - accumulator_layers = [] - thetas = [] - init_states = [] - devices = [] - for cell_idx in range(num_cells): - cell_name, cell = self._cells[cell_idx] - accumulator_layers.append(cell) - cell_fns.append(GetCellFn(cell_idx)) - thetas.append(theta[cell_name]) - - def _TfZeros(t_shape): - if t_shape is None: - return None - return tf.zeros(t_shape.ToTensorShape().as_list(), dtype=state_dtype) - - if p.nested_map_fprop: - init_state = py_utils.Transform(_TfZeros, state_shapes[cell_idx + 1]) - init_state = init_state.Filter(lambda x: x is not None) - else: - init_state = py_utils.NestedMap() - for output_idx, state in enumerate(state_shapes[cell_idx + 1]): - state = _TfZeros(state) - if state is not None: - name = 's{}'.format(output_idx) - init_state[name] = state - init_state[_MICRO_BATCH_STATE_NAME] = tf.cast(0, dtype=state_dtype) - init_states.append(init_state) - - devices.append(cluster.WorkerDeviceInModelSplit(cell_idx)) - - cell_grads = [None] * num_cells - cell_outs = [lambda x: x] * num_cells - cell_out_grads = [lambda x: x] * num_cells - - with tf.device(devices[0]): - previous = _ToTuple(args) - for (name, l) in self._before_layers: - previous = l.FProp(theta[name], *previous) - previous = _ToTuple(previous) - - def _StackAndSplit(x): - # Split tensors into microbatches. - if x is None: - return None - return tf.stack(tf.split(x, p.num_micro_batches, axis=p.batch_dim)) - - if p.nested_map_fprop: - inputs = py_utils.Transform(_StackAndSplit, previous[0]) - inputs = inputs.Filter(lambda x: x is not None) - else: - inputs = py_utils.NestedMap() - for output_idx, output_tensor in enumerate(previous): - output_tensor = _StackAndSplit(output_tensor) - if output_tensor is not None: - name = 's{}'.format(output_idx) - inputs[name] = output_tensor - gs_tensor = py_utils.GetGlobalStep() - inputs[_MICRO_BATCH_STATE_NAME] = tf.stack([ - tf.cast(gs_tensor * p.num_micro_batches + t, dtype=state_dtype) - for t in range(p.num_micro_batches) - ]) - tf.logging.info('pipeline input = {}'.format(inputs)) - output_state, _ = recurrent.StackedRecurrent( - devices=devices, - cell_fns=cell_fns, - cell_grads=cell_grads, - cell_outs=cell_outs, - cell_out_grads=cell_out_grads, - thetas=thetas, - init_states=init_states, - inputs=inputs, - accumulator_layers=accumulator_layers, - unused_acc_state=True) - - with tf.device(devices[-1]): - - def _ReshapeRetVal(name, t_shape): - """Restore shape for tensors in microbatches.""" - if t_shape is None: - return None - output_tensor = output_state[name] - if p.batch_dim != 0: - perm = list(range(1, p.batch_dim + 1)) + [0] - perm += list(range(p.batch_dim + 1, t_shape.rank + 1)) - output_tensor = tf.transpose(output_tensor, perm=perm) - output_shape = t_shape.ToTensorShape().as_list() - output_shape[p.batch_dim] *= p.num_micro_batches - output_tensor = tf.reshape(output_tensor, output_shape) - return output_tensor - - # Construct the final return values from output_state. - if p.nested_map_fprop: - # pylint: disable=protected-access - output_tensors = state_shapes[-1]._RecursiveMap(_ReshapeRetVal) - # pylint: enable=protected-access - else: - output_tensors = [] - for output_idx, state_shape in enumerate(state_shapes[-1]): - output_name = 's{}'.format(output_idx) - output_tensor = _ReshapeRetVal(output_name, state_shape) - output_tensors.append(output_tensor) - if len(output_tensors) == 1: - output_tensors = output_tensors[0] - else: - output_tensors = tuple(output_tensors) - tf.logging.info('pipeline output = {}'.format(output_tensors)) - return output_tensors diff --git a/lingvo/core/gpipe_test.py b/lingvo/core/gpipe_test.py deleted file mode 100644 index 4fd82416..00000000 --- a/lingvo/core/gpipe_test.py +++ /dev/null @@ -1,241 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo gpipe.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core import tshape -from lingvo.core.gpipe import FeatureExtractionLayer -from lingvo.core.gpipe import PartitionSequentialLayers -from lingvo.core.gpipe import PipeliningLayer -from lingvo.core.layers import Conv2DLayerNoPadding -from lingvo.core.layers import FetchLayer - - -class _TimestepAccumulator(base_layer.Accumulator): - """Simple accumulator for counting timesteps in the pipeline.""" - - def DefaultValue(self): - return tf.convert_to_tensor(0.0) - - def Increment(self): - self.SetValue(self.GetValue() + 1.0) - - -class _SimpyLayer(base_layer.BaseLayer): - """Simpy Layer with accumulator that counts time step.""" - - def __init__(self, params): - super().__init__(params) - conv = Conv2DLayerNoPadding.Params().Set( - name='conv', - filter_shape=(3, 3, 1, 1), - filter_stride=(1, 1), - params_init=py_utils.WeightInit.Constant(0.1)) - self.CreateChild('conv', conv) - self.RegisterAccumulator('ts_count', _TimestepAccumulator()) - - def FProp(self, theta, inputs): - p = self.params - with tf.name_scope(p.name): - inputs = self.conv.FProp(theta.conv, inputs) - self.accumulators.ts_count.Increment() - return tf.nn.relu(inputs) - - @classmethod - def FPropMeta(cls, p, inputs): - py_utils.CheckShapes((inputs,)) - return py_utils.NestedMap(flops=1, out_shapes=(inputs,)) - - -class _SimpyLayerWithNestedMapInput(_SimpyLayer): - - def FProp(self, theta, inputs): - p = self.params - with tf.name_scope(p.name): - vec = self.conv.FProp(theta.conv, inputs.vec) - return py_utils.NestedMap(vec=vec, paddings=inputs.paddings) - - @classmethod - def FPropMeta(cls, p, inputs): - py_utils.CheckShapes( - tuple(inputs.Filter(lambda x: x is not None).Flatten())) - return py_utils.NestedMap(flops=1, out_shapes=(inputs,)) - - -def _Partition(params, num_splits, *shapes): - seqs = PartitionSequentialLayers(params, num_splits, *shapes) - return [ - FeatureExtractionLayer.Params().Set(name='d%d' % i, sub=seqs[i].sub) - for i in range(len(seqs)) - ] - - -def _BuildDummyPipelineCnn(num_splits=4, - num_micro_batches=8, - micro_batch_size=None): - """Construct a dummy layer that consist of 16 3x3 conv layers. - - In addition, each conv layer increments a count every time step. - - Args: - num_splits: number of cells for pipeline cnn - num_micro_batches: number of time steps. - micro_batch_size: Size of a micro batch. - - Returns: - A PipeliningLayer layer. - """ - assert num_splits in [1, 2, 4, 8, 16] - num_layers = 16 - layers = [] - for i in range(num_layers): - layers.append(_SimpyLayer.Params().Set(name='layer_{}'.format(i))) - - if num_splits == 1: - p = FeatureExtractionLayer.Params().Set(name='seq', sub=layers) - else: - cell_tpl = [] - layers_per_split = num_layers // num_splits - num_act_outputs = 0 - num_act_inputs = 0 - act_fetch_layers = None - for split in range(num_splits): - sub = layers[split * layers_per_split:(split + 1) * layers_per_split] - if split == 0: - sub.append(FetchLayer.Params().Set(name='fetch')) - num_act_outputs = 1 - act_fetch_layers = ['fetch'] - else: - num_act_inputs = 1 - act_fetch_layers = [] - split_layer = FeatureExtractionLayer.Params().Set( - name='split_{}'.format(split), - sub=sub, - act_fetch_layers=act_fetch_layers, - num_act_inputs=num_act_inputs, - num_act_outputs=num_act_outputs) - cell_tpl.append(split_layer) - p = PipeliningLayer.Params().Set( - name='pipeline', - num_micro_batches=num_micro_batches, - micro_batch_size=micro_batch_size, - cell_tpl=cell_tpl, - before_tpl=[]) - layer = p.Instantiate() - return layer - - -class DummyPipelineCnnTest(test_utils.TestCase): - - def _verify_timestep_counts(self, - num_splits, - auto_partition=False, - micro_batch_size=None): - num_micro_batches = 8 - batch_size = 16 - with self.session(graph=tf.Graph()): - tf.random.set_seed(1245) - inputs = tf.random.uniform([batch_size, 8, 8, 1], seed=12345) - if auto_partition: - layers = [ - _SimpyLayer.Params().Set(name='layer_{}'.format(i)) - for i in range(16) - ] - net = PipeliningLayer.Params().Set( - name='pipeline', - num_micro_batches=num_micro_batches, - cell_tpl=_Partition(layers, num_splits, - tshape.Shape([batch_size, 8, 8, - 1]))).Instantiate() - else: - net = _BuildDummyPipelineCnn( - num_splits=num_splits, - micro_batch_size=micro_batch_size, - num_micro_batches=num_micro_batches) - endpoints = net.FPropDefaultTheta(inputs) - if isinstance(endpoints, (list, tuple)): - logits, aux_logits = endpoints - else: - logits = endpoints - aux_logits = None - loss = tf.reduce_mean(logits) - grads = tf.gradients(loss, tf.trainable_variables()) - grad_norm = tf.sqrt(py_utils.SumSquared(grads)) - ts = net.GetAccumulatorValues().Flatten() - - self.evaluate(tf.global_variables_initializer()) - grad_norm_val, ts_vals = self.evaluate([grad_norm, ts]) - test_utils.CompareToGoldenSingleFloat(self, 0.268087, grad_norm_val) - # Accumulator values should be equal to number of time steps in pipeline. - for ts_val in list(ts_vals): - expected_ts = num_micro_batches if num_splits > 1 else 1 - self.assertEqual(ts_val, expected_ts) - if aux_logits is not None: - aux_logit_tensor = self.evaluate(aux_logits) - self.assertEqual(aux_logit_tensor.shape, (batch_size, 8, 8, 1)) - - def testDummyPipelineCnnOneSplit(self): - self._verify_timestep_counts(num_splits=1) - - def testDummyPipelineCnnTwoSplits(self): - self._verify_timestep_counts(num_splits=2) - - def testDummyPipelineCnnTwoSplitsMicroBatchSize1(self): - with self.assertRaises(ValueError): - self._verify_timestep_counts(num_splits=2, micro_batch_size=1) - - def testDummyPipelineCnnTwoSplitsMicroBatchSize2(self): - self._verify_timestep_counts(num_splits=2, micro_batch_size=2) - - def testDummyPipelineCnnFourSplits(self): - self._verify_timestep_counts(num_splits=4) - - def testDummyPipelineCnnAutoPartitionTwoSplits(self): - self._verify_timestep_counts(num_splits=2, auto_partition=True) - - def testDummyPipelineCnnAutoPartitionFourSplits(self): - self._verify_timestep_counts(num_splits=4, auto_partition=True) - - def testDummyPipelineCnnNestedMapInput(self): - batch_size = 16 - num_layers = 4 - cells = [] - with self.session(graph=tf.Graph()): - for i in range(num_layers): - cells.append(_SimpyLayerWithNestedMapInput.Params().Set( - name='layer_{}'.format(i))) - p = PipeliningLayer.Params().Set( - name='pipeline', - num_micro_batches=8, - micro_batch_size=2, - nested_map_fprop=True, - cell_tpl=cells, - before_tpl=[]) - layer = p.Instantiate() - tf.random.set_seed(1245) - inputs = tf.random.uniform([batch_size, 8, 8, 1], seed=12345) - outputs = layer.FPropDefaultTheta( - py_utils.NestedMap(vec=inputs, paddings=None)) - self.evaluate(tf.global_variables_initializer()) - self.evaluate(outputs.vec) - self.assertEqual(outputs.vec.shape, (batch_size, 8, 8, 1)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/gradient_combiner.py b/lingvo/core/gradient_combiner.py deleted file mode 100644 index ff01d2cf..00000000 --- a/lingvo/core/gradient_combiner.py +++ /dev/null @@ -1,45 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Layers to combine gradients computed from multiple losses. - -Multi-task learning can sometimes benefit from more sophisticated gradient -combination algorithms than simple linear aggregation, for example - -* Gradient surgery: https://arxiv.org/abs/2001.06782 -* GradNorm: https://arxiv.org/abs/1711.02257 -""" - -from lingvo.core import base_layer - - -class GradientCombiner(base_layer.BaseLayer): - """An abstract class to combine gradients (from multiple loss functions).""" - - def Combine(self, vmap, losses_and_gradients): - """Combines gradients on the variables. - - Args: - vmap: a NestedMap containing the variables. - losses_and_gradients: a Dict[str, loss_and_grads], where each key - represents the loss name used to compute the gradients and each value is - a NestedMap with the following entries, 'loss_metric', a (loss, weight) - pair representing the loss; 'grads', a NestedMap containing the gradient - tensors for variables, with the identical structure as 'vmap'. - - Returns: - A NestedMap of combined gradients, with an identical structure as 'vmap'. - """ - raise NotImplementedError(type(self)) diff --git a/lingvo/core/hyperparams.proto b/lingvo/core/hyperparams.proto deleted file mode 100644 index ca86019d..00000000 --- a/lingvo/core/hyperparams.proto +++ /dev/null @@ -1,58 +0,0 @@ -syntax = "proto2"; - -package tensorflow.lingvo; - -// This is a top-level entry, typically each entry corresponds to a p.Define() -// statement in the Python code. The key is the parameter name. It is also used -// for when the parameter itself is a dictionary. -message Hyperparam { - map items = 1; -} - -// This is to serialize a list or tuple of parameters. For example, some -// parameters are a list of integers. The distinction between list and tuple is -// by the field name in the HyperparamValue below. -message HyperparamRepeated { - repeated HyperparamValue items = 1; -} - -// Once the repetition is handled (with HyperparamList et al. above), the -// parameter can either be a recursively set to another set of params via -// p.Define, or a raw type (such as a string, float, ...). -message HyperparamValue { - oneof kind { - // The NoneType is represented by not setting any of the values below. - Hyperparam param_val = 1; - HyperparamRepeated list_val = 2; - HyperparamRepeated tuple_val = 3; - Hyperparam dict_val = 4; - string type_val = 5; - string dtype_val = 6; - string string_val = 7; - bool bool_val = 8; - int64 int_val = 9; - float float_val = 10; - ProtoVal proto_val = 11; - EnumVal enum_val = 12; - NamedTupleVal named_tuple_val = 13; - } -} - -// A serialized proto. The message includes both the proto message name and the -// proto serialized value. -message ProtoVal { - optional string type = 1; - optional bytes val = 2; -} - -// An enum. -message EnumVal { - optional string type = 1; - optional string name = 2; -} - -// A namedtuple. -message NamedTupleVal { - optional string type = 1; - repeated HyperparamValue items = 2; -} diff --git a/lingvo/core/hyperparams.py b/lingvo/core/hyperparams.py deleted file mode 100644 index fc41b43d..00000000 --- a/lingvo/core/hyperparams.py +++ /dev/null @@ -1,891 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Defines Params base class, used for defining class/function parameters.""" - -import ast -import copy -import enum -import importlib -import inspect -import re -import sys - -import dataclasses -import lingvo.compat as tf -from lingvo.core import hyperparams_pb2 -from lingvo.core import symbolic - -from google.protobuf import message -from google.protobuf import text_format - - -def _QuoteString(s): - """Quotes a string with appropriate quotes and escaping. - - This performs lite escaping by choosing enclosing quotation marks that would - escape the least (either single or double quotes) and escaping those quotes - and the backslash. Note that this does not escape newlines. If the string - contains embedded newlines, they will be output verbatim. - - Args: - s: String to quote. - Returns: - Quotes string (possibly multiline). - """ - single_quote_count = s.count('\'') - double_quote_count = s.count('"') - quote_delim = '\'' if single_quote_count <= double_quote_count else '"' - # Apply escaping to the chosen quote character and the backslash. - encoded = re.sub(r'([%s\\])' % quote_delim, r'\\\1', s) - return quote_delim + encoded + quote_delim - - -def _UnquoteString(quoted): - if quoted and quoted[0] in ['"', '\'']: - # Note that only the limited set of escaping produced by _QuoteString is - # supported. - contents = quoted.strip(quoted[0]) - return re.sub(r"""\\([\\'"])""", r'\1', contents) - else: - # Just return literal text. - return quoted - - -def _EndsWithTerminalQuote(s, quote_char): - """Returns whether a string ends with a valid terminal quote.""" - endm = re.search(r'(\\*)%s$' % quote_char, s) - if not endm: - return False - backslashes = endm.group(1) - if len(backslashes) % 2 == 0: - # Even number of backslashes preceding the quote means the quote is - # not escaped. - return True - else: - # Terminal quote is escaped. - return False - - -def _IsNamedTuple(x): - """Returns whether an object is an instance of a collections.namedtuple. - - Examples:: - - _IsNamedTuple((42, 'hi')) ==> False - Foo = collections.namedtuple('Foo', ['a', 'b']) - _IsNamedTuple(Foo(a=42, b='hi')) ==> True - - Args: - x: The object to check. - """ - return isinstance(x, tuple) and hasattr(x, '_fields') - - -class _SortedDict(dict): - """A dict with a __repr__ that is always sorted by key.""" - - def __repr__(self): - return '{' + ', '.join( - '%r: %r' % item for item in sorted(self.items())) + '}' - - -class _Param: - """Stores data for a single parameter.""" - - def __init__(self, name, default_value, description): - self._name = name - self._value = default_value - self._description = description - - def __eq__(self, other): - # pylint: disable=protected-access - return self._name == other._name and self._value == other._value - - # Deep copy the value only if it is supported. - def __deepcopy__(self, memo): - if isinstance(self._value, (tf.Tensor, symbolic.Symbol)): - # In case self._value is a tensor/symbol, let's just make a reference. - value = self._value - else: - value = copy.deepcopy(self._value, memo) - p = _Param(self._name, value, self._description) - # Q(yonghui): Is this the right use of memo. - memo[id(self)] = p - return p - - def ToString(self, nested_depth): - """Prints the parameter as a string.""" - - def GetRepr(val): - """Get the representation of `val`.""" - if isinstance(val, Params): - return _SortedDict({k: GetRepr(v) for k, v in val.IterParams()}) - if isinstance(val, dict): - return _SortedDict({k: GetRepr(v) for k, v in val.items()}) - if isinstance(val, (list, tuple)) and not _IsNamedTuple(val): - # NB: this constructor signature works for tuples, but not namedtuples. - return type(val)([GetRepr(v) for v in val]) - # NOTE(markmurphy): I introduced Repr() because it's impossible (afaik) to - # overwrite the __str__ or __repr__ method of a types.FunctionType object. - if hasattr(val, 'Repr'): - return val.Repr() - return val - - nested_indent = ' ' * nested_depth - if isinstance(self._value, Params): - # pylint: disable=protected-access - value_str = self._value._ToString(nested_depth) - elif isinstance(self._value, str): - return '%s%s: "%s"' % (nested_indent, self._name, self._value) - else: - value_str = str(GetRepr(self._value)) - return '%s%s: %s' % (nested_indent, self._name, value_str) - - def Set(self, value): - # Note that we don't make a copy of Params objects. - # TODO(sadovsky): Maybe add safeguard to ensure that Params object is not - # owned by other Params objects. - self._value = value - - def Get(self): - return self._value - - -def CopyFieldsTo(from_p, to_p, skip=None): - """Copy fields from one Params to another, with optional skipped params. - - Preserves `type(to_p.Instantiate())`. Use `from_p.Copy()` instead if requiring - a deep copy of `from_p`, without updating `to_p`. - - Args: - from_p: Source params to copy from. - to_p: Destination params to copy to. - skip: If not None, a list of strings of param names to skip. Automatically - skips InstantiableParams' 'cls' parameter. - - Returns: - The updated to_p. - """ - skip = skip or [] - skip.append('cls') - for n, p in from_p.IterParams(): - if n in skip: - continue - if isinstance(p, Params): - to_p.Set(**{n: p.Copy()}) - else: - to_p.Set(**{n: p}) - return to_p - - -class Params: - """Stores data for a set of parameters. - - Provides attribute-based API, e.g. "params.foo = 5". - Uses internal {'name': _Param} dict for storing parameter data. - """ - - def __init__(self): - self.__dict__['_immutable'] = False - self._params = {} # name => _Param - - def __setattr__(self, name, value): - if self._immutable: - raise TypeError('This Params instance is immutable.') - if name == '_params' or name == '_immutable': - self.__dict__[name] = value - else: - try: - self._params[name].Set(value) - except KeyError: - raise AttributeError(self._KeyErrorString(name)) - - def __getattr__(self, name): - if name == '_params' or name == '_immutable': - return self.__dict__[name] - try: - return self._params[name].Get() - except KeyError: - # cPickle expects __getattr__ to raise AttributeError, not KeyError. - raise AttributeError(self._KeyErrorString(name)) - - def __dir__(self): - return sorted(self._params.keys()) - - def __contains__(self, name): - return name in self._params - - def __len__(self): - return len(self._params) - - # Note: This gets called by _Param.__eq__() on nested Params objects. - def __eq__(self, other): - return isinstance(other, Params) and self._params == other._params # pylint: disable=protected-access - - def __ne__(self, other): - return not self == other - - def __str__(self): - return self._ToString(0) - - def _ToString(self, nested_depth): - # Note: We use iteritems() below so as to sort by name. - sorted_param_strs = [ - v.ToString(nested_depth + 1) for (_, v) in sorted(self._params.items()) - ] - nested_indent = ' ' * nested_depth - return '{\n%s\n%s}' % ('\n'.join(sorted_param_strs), nested_indent) - - # Override __deepcopy__ so that copy.deepcopy(self._params) properly - # deep-copies nested Params objects. - # TODO(sadovsky): Is it okay not to touch memo? - def __deepcopy__(self, unused_memo): - return self.Copy() - - def _SimilarKeys(self, name): - """Return a list of params keys that are similar to name.""" - - def _Overlaps(name, key): - """The fraction of 3-char substrings in that appear in key.""" - matches = 0 - trials = 0 - for i in range(len(name) - 3): - trials += 1 - if name[i:i + 3] in key: - matches += 1 - if trials: - return float(matches) / trials - return 0 - - if '_params' in self.__dict__: - return [key for key in self._params if _Overlaps(name, key) > 0.5] - return [] - - def _KeyErrorString(self, name): - similar = self._SimilarKeys(name) - if similar: - return name + ' (did you mean: [%s])' % (','.join(sorted(similar))) - return name - - def Copy(self): - """Creates a deep copy of self.""" - return self._CopyTo(type(self)()) - - def _CopyTo(self, res): - # pylint: disable=protected-access - res._params = copy.deepcopy(self._params) - res._immutable = self._immutable - # pylint: enable=protected-access - return res - - # TODO(sadovsky): - # - Maybe let users specify whether this parameter is allowed to have - # value=None, and if not, assert on Get(), like required proto field. - # - Maybe enforce that value is one of - # {number, string, bool, list, dict, Params}. - def Define(self, name, default_value, description): - """Defines a parameter. - - Args: - name: The parameter name. Must only contain lowercase letters, numbers, - and underscores. Must start with lowercase letter. - default_value: Default value for this parameter. May be None. - description: String description of this parameter. - - Raises: - AttributeError: If parameter 'name' is already defined. - """ - if self._immutable: - raise TypeError('This Params instance is immutable.') - assert name is not None and isinstance( - name, str) and (re.match('^[a-z][a-z0-9_]*$', name) is not None) - if name in self._params: - raise AttributeError('Parameter %s is already defined' % name) - self._params[name] = _Param(name, default_value, description) - - def Freeze(self): - """Marks this Params as immutable.""" - self._immutable = True - - def IsImmutable(self): - """Return whether this Params is immutable.""" - return self._immutable - - def _GetNested(self, name): - """Returns nested param by its name.""" - parts = name.split('.') - curr = self - for i, part in enumerate(parts[:-1]): - # Get the value (nested Params object) associated with name 'part'. - try: - is_list = re.match(r'^(.+)\[(.+)\]$', part) - if is_list: - part = is_list.group(1) - list_index = int(is_list.group(2)) - # pylint: disable=protected-access - curr = curr._params[part].Get() - if is_list: - curr = curr[list_index] - except KeyError: - raise AttributeError('.'.join(parts[:i + 1])) - assert isinstance(curr, Params), ( - 'Cannot introspect %s for %s' % (type(curr), '.'.join(parts[:i + 1]))) - return curr, parts[-1] - - def Set(self, **kwargs): - """Sets multiple parameters. - - Dots in names indicate navigation into nested Params objects. We do not - allow navigation into lists or dicts, and may ban these types altogether in - favor of string representations. - - Args: - **kwargs: Name-value pairs to set. - - Returns: - self - """ - if self._immutable: - raise TypeError('This Params instance is immutable: %s' % self) - for name, value in kwargs.items(): - # Get nested param. - param, key = self._GetNested(name) - # Update the value associated with key. - try: - # pylint: disable=protected-access - param._params[key].Set(value) - except KeyError: - raise AttributeError(self._KeyErrorString(name)) - return self - - def Get(self, name): - """Get parameter. - - Dots in names indicate navigation into nested Params objects. We do not - allow navigation into lists or dicts, and may ban these types altogether in - favor of string representations. - - Args: - name: (str) Name. - - Returns: - value. - - Raises: - AttributeError: if parameter is not found - """ - param, key = self._GetNested(name) - # Get the value associated with key. - try: - # pylint: disable=protected-access - return param._params[key].Get() - except KeyError: - raise AttributeError(self._KeyErrorString(name)) - - def Delete(self, *args): - """Deletes multiple parameters. - - Dots in names indicate navigation into nested Params objects. We do not - allow navigation into lists or dicts, and may ban these types altogether in - favor of string representations. - - Args: - *args: List of names. - - Returns: - self - """ - if self._immutable: - raise TypeError('This Params instance is immutable.') - for name in args: - # Get nested param. - param, key = self._GetNested(name) - # Delete the key. - try: - # pylint: disable=protected-access - del param._params[key] - except KeyError: - raise AttributeError(self._KeyErrorString(name)) - return self - - def IterParams(self): - """Pythonic dict-like iteration.""" - for name, param in self._params.items(): - yield (name, param.Get()) - - def ToProto(self): - """Writes to a Hyperparams proto. - - Serializes the Hyperparams into a proto that can be then written to disk or - sent over the network. Note that serialization is not guaranteed to be - unique or stable (this is a feature of protos themselves, not this code), so - using it for fingerprinting for example may not be appropriate. Refer to the - ToText() method for a serialization approach that Lingvo controls. - - Returns: - The serialized params as a Hyperparams proto. - """ - - def _ToParamValue(val): - """Serializes to HyperparamValue proto.""" - param_pb = hyperparams_pb2.HyperparamValue() - if isinstance(val, Params): - param_pb.param_val.CopyFrom(_ToParam(val)) - elif isinstance(val, list) or isinstance(val, range): - # The range function is serialized by explicitely calling it. - param_pb.list_val.items.extend([_ToParamValue(v) for v in val]) - elif dataclasses.is_dataclass(val) or _IsNamedTuple(val): - val_cls = type(val) - vals = val.__dict__.values() if dataclasses.is_dataclass( - val) else val._asdict().values() - param_pb.named_tuple_val.type = inspect.getmodule( - val_cls).__name__ + '/' + val_cls.__name__ - param_pb.named_tuple_val.items.extend([_ToParamValue(v) for v in vals]) - elif isinstance(val, tuple): - param_pb.tuple_val.items.extend([_ToParamValue(v) for v in val]) - elif isinstance(val, dict): - param_pb.dict_val.SetInParent() - for k, v in val.items(): - param_pb.dict_val.items[k].CopyFrom(_ToParamValue(v)) - elif isinstance(val, type): - param_pb.type_val = inspect.getmodule(val).__name__ + '/' + val.__name__ - elif isinstance(val, tf.DType): - param_pb.dtype_val = val.name - elif isinstance(val, str): - param_pb.string_val = val - elif isinstance(val, bool): - param_pb.bool_val = val - elif isinstance(val, int): - param_pb.int_val = val - elif isinstance(val, float): - param_pb.float_val = val - elif isinstance(val, enum.Enum): - enum_cls = type(val) - param_pb.enum_val.type = inspect.getmodule( - enum_cls).__name__ + '/' + enum_cls.__name__ - param_pb.enum_val.name = val.name - elif isinstance(val, message.Message): - proto_cls = type(val) - param_pb.proto_val.type = inspect.getmodule( - proto_cls).__name__ + '/' + proto_cls.__name__ - param_pb.proto_val.val = val.SerializeToString() - elif val is None: - # We represent a NoneType by the absence of any of the oneof. - pass - else: - raise AttributeError('Unsupported type: %s' % type(val)) - return param_pb - - def _ToParam(val): - """Serializes to Hyperparam proto.""" - - param_pb = hyperparams_pb2.Hyperparam() - for k, v in val.IterParams(): - param_pb.items[k].CopyFrom(_ToParamValue(v)) - return param_pb - - return _ToParam(self) - - # TODO(tonybruguier): Move to module-level function (cls is never used). - @classmethod - def FromProto(cls, param_pb): - """Reads from a Hyperparams proto.""" - - def _LoadClass(module_and_class_name): - tokens = module_and_class_name.split('/') - assert len(tokens) == 2, module_and_class_name - return getattr(importlib.import_module(tokens[0]), tokens[1]) - - def _FromParamValue(param_pb): - """Deserializes HyperparamValue proto.""" - - which_oneof = param_pb.WhichOneof('kind') - if which_oneof == 'param_val': - return _FromParam(param_pb.param_val) - elif which_oneof == 'list_val': - return [_FromParamValue(val) for val in param_pb.list_val.items] - elif which_oneof == 'named_tuple_val': - named_tuple_cls = _LoadClass(param_pb.named_tuple_val.type) - if not dataclasses.is_dataclass(named_tuple_cls) and not issubclass( - named_tuple_cls, tuple): - return None - return named_tuple_cls( - *[_FromParamValue(val) for val in param_pb.named_tuple_val.items]) - elif which_oneof == 'tuple_val': - return tuple([_FromParamValue(val) for val in param_pb.tuple_val.items]) - elif which_oneof == 'dict_val': - dict_val = dict() - for k in param_pb.dict_val.items: - dict_val[k] = _FromParamValue(param_pb.dict_val.items[k]) - return dict_val - elif which_oneof == 'type_val': - tokens = param_pb.type_val.split('/') - assert len(tokens) == 2 - return getattr(importlib.import_module(tokens[0]), tokens[1]) - elif which_oneof == 'dtype_val': - return tf.as_dtype(param_pb.dtype_val) - elif which_oneof == 'string_val': - return param_pb.string_val - elif which_oneof == 'int_val': - return param_pb.int_val - elif which_oneof == 'float_val': - return param_pb.float_val - elif which_oneof == 'bool_val': - return param_pb.bool_val - elif which_oneof == 'enum_val': - enum_cls = _LoadClass(param_pb.enum_val.type) - if not issubclass(enum_cls, enum.Enum): - return None - return enum_cls[param_pb.enum_val.name] - elif which_oneof == 'proto_val': - proto_cls = _LoadClass(param_pb.proto_val.type) - if not issubclass(proto_cls, message.Message): - return None - proto_msg = proto_cls() - proto_msg.ParseFromString(param_pb.proto_val.val) - return proto_msg - else: - # If nothing is set, it's the None type. - return None - - def _FromParam(param_pb): - """Deserializes Hyperparam proto.""" - - params = InstantiableParams() if 'cls' in param_pb.items else Params() - for k in param_pb.items: - val = _FromParamValue(param_pb.items[k]) - if k == 'cls': - params.Set(**{k: val}) - else: - params.Define(k, val, '') - return params - - return _FromParam(param_pb) - - def ToText(self, include_types=False): - """Encodes params into a simple text format. - - Each param is represented as a single line in the output. The param - name and value is separated by a ":". The nest param name is - separated by ".". For values of non-trivial types (types other than - int, float, bool, str, and a few, etc.), we just print out the name - of its type. - - Note that strings are enclosed in appropriate single or double quotes - (whichever would involve the least escaping) and will have some characters - backslash escaped. String properties can span multiple lines. - - Args: - include_types: Should we return types of the values. If True, the types - dict will be returned as a second val in a return tuple - - Returns: - The encoded text or (encoded text, types dict) if include_types is True. - """ - kv = {} - types = {} - - def GetRepr(val): - """Get the representation of `val`.""" - if isinstance(val, Params): - return _SortedDict({k: GetRepr(v) for k, v in val.IterParams()}) - if isinstance(val, dict): - return _SortedDict({k: GetRepr(v) for k, v in val.items()}) - if dataclasses.is_dataclass(val): - return _SortedDict({k: GetRepr(v) for k, v in val.__dict__.items()}) - if _IsNamedTuple(val): - return _SortedDict({k: GetRepr(v) for k, v in val._asdict().items()}) - if isinstance(val, (list, tuple)): - return type(val)([GetRepr(v) for v in val]) - if isinstance(val, (int, float, bool, str, enum.Enum)): - return val - if isinstance(val, tf.DType): - return val.name - if isinstance(val, message.Message): - proto_str = text_format.MessageToString(val, as_one_line=True) - return 'proto/%s/%s/%s' % (inspect.getmodule(val).__name__, - type(val).__name__, proto_str) - if isinstance(val, type): - return 'type/' + inspect.getmodule(val).__name__ + '/' + val.__name__ - return type(val).__name__ - - def Traverse(p, prefix, kv): - """Traverses 'p' and inserts key-value pairs to 'kv'.""" - if isinstance(p, Params): - for key, val in p.IterParams(): - Traverse(val, prefix + '.' + key, kv) - elif (isinstance(p, (list, tuple)) and - all(isinstance(x, Params) for x in p)): - for i, val in enumerate(p): - Traverse(val, '%s[%d]' % (prefix, i), kv) - elif isinstance(p, str): - kv[prefix] = _QuoteString(p) - types[prefix[1:]] = 'str' - else: - kv[prefix] = str(GetRepr(p)) - types[prefix[1:]] = type(p).__name__ - - Traverse(self, '', kv) - ret = '' - for (k, v) in sorted(kv.items()): - ret += k[1:] + ' : ' + v + '\n' - - return (ret, types) if include_types else ret - - def FromText(self, text, type_overrides=None): - """Merges params specified in 'text' into 'params'. - - 'text' follows the simple text format as produced by ToText. - For a param specified in both 'params' and 'text', overwrites the value in - 'params' according to 'text'. Params specified in 'text' but not in 'params' - are ignored. - - Args: - text: A text representation of params. - type_overrides: Overrides for the types of the params. - Raises: - AttributeError: text contains invalid parameter key - ValueError: text contains invalid parameter value, or the format is - wrong. - """ - if self._immutable: - raise TypeError('This Params instance is immutable.') - kv = {} - type_overrides = type_overrides or {} - string_continue = None # None or (key, quote, value) - for line in text.split('\n'): - # Continuing a multi-line string. - if string_continue: - value_stripped = line.rstrip() - if not _EndsWithTerminalQuote(value_stripped, string_continue[1]): - # String continues - string_continue = (string_continue[0], string_continue[1], - string_continue[2] + '\n' + line) - continue - # String terminates. - kv[string_continue[0]] = string_continue[2] + '\n' + value_stripped - string_continue = None - continue - - # Regular line. - line = line.strip() - if not line or line[0] == '#': - # empty line or comment - continue - pair = line.split(':', 1) - if len(pair) == 2: - key = pair[0].strip() - value = pair[1].lstrip() - value_stripped = value.rstrip() - # Detect single vs multi-line string start. - if value and value[0] in ['"', '\'']: - quote_char = value[0] - if not _EndsWithTerminalQuote(value[1:], quote_char): - # Multi-line string. - string_continue = (key, quote_char, value) - continue - kv[key] = value_stripped - else: - raise ValueError('Line {} is not in : format'.format(line)) - - def _ValueFromText(key, old_val, val): - """Returns the new param value from its text representation.""" - val_type = type(old_val).__name__ - if isinstance(old_val, str): - val_type = 'str' - if key in type_overrides: - val_type = type_overrides[key] - # Converts val (a string) to a best-guessed typed value. - if val_type == 'bool': - return val and (val != 'False') and (val != 'false') - elif val_type == 'int': - return int(val) - elif val_type == 'float': - return float(val) - elif val_type == 'DType': - return tf.as_dtype(val) - elif dataclasses.is_dataclass(old_val) or _IsNamedTuple(old_val): - # Maps field name to new value (or its string repr, if non-POD). - name_to_new_value = ast.literal_eval(val) - contents = {} - items = old_val.__dict__.items() if dataclasses.is_dataclass( - old_val) else old_val._asdict().items() - for k, old_field_value in items: - new_field_value = name_to_new_value[k] - # Recurse to parse any non-POD contents not converted by - # literal_eval(). - if isinstance(new_field_value, str): - contents[k] = _ValueFromText(k, old_field_value, new_field_value) - else: - contents[k] = new_field_value - return type(old_val)(**contents) - elif val_type in ['list', 'tuple']: - return ast.literal_eval(val) - elif val_type == 'dict': - return ast.literal_eval(val) if val != 'dict' else {} - elif val_type == 'str': - val = _UnquoteString(val) - if val.startswith('[') and val.endswith(']'): - # We may have stored a list as a string, try converting to a list. - # In case of ValueError - use the string as is. - try: - return ast.literal_eval(val) - except ValueError: - pass - return val - elif isinstance(old_val, enum.Enum): - cls, _, name = val.rpartition('.') - if val_type != cls: - raise ValueError('Expected enum of class %s but got %s' % - (val_type, cls)) - return type(old_val)[name] - elif (isinstance(old_val, type) or isinstance(old_val, message.Message) or - old_val is None): - if val == 'NoneType': - return None - elif old_val is None and val in ('False', 'false'): - return False - elif old_val is None and val in ('True', 'true'): - return True - else: - try: - val_type, pkg, cls = val.split('/', 2) - if val_type == 'type': - return getattr(sys.modules[pkg], cls) - elif val_type == 'proto': - cls, proto_str = cls.split('/', 1) - proto_cls = getattr(sys.modules[pkg], cls) - if not issubclass(proto_cls, message.Message): - raise ValueError('%s is not a proto class.' % proto_cls) - return text_format.Parse(proto_str, proto_cls()) - except ValueError as e: - raise ValueError('Error processing %r : %r with %r' % (key, val, e)) - else: - raise ValueError('Failed to read a parameter: %r : %r' % (key, val)) - - for key, val in kv.items(): - old_val = self.Get(key) - new_val = _ValueFromText(key, old_val, val) - self.Set(**{key: new_val}) - - def ToTextWithTypes(self): - """Same as ToText but encodes both params and their types.""" - text, types = self.ToText(include_types=True) - text += '\n\n' - for (k, v) in sorted(types.items()): - text += k + ' : ' + v + '\n' - return text - - def FromTextWithTypes(self, text): - """Same as FromText but expects to have types encoded in the text.""" - text, types_str = text.split('\n\n') - types = {} - for row in types_str.split('\n'): - if not row: - continue - k, v = row.split(':') - types[k.strip()] = v.strip() - self.FromText(text, type_overrides=types) - - def TextDiff(self, other): - """Return the differences between this object and another as a string. - - Args: - other: The other Params object. - - Returns: - A string of differences. - """ - - def TextDiffHelper(a, b, spaces): - """Return the differences between a and b as a string.""" - a_keys = set([key for key, _ in a.IterParams()]) - b_keys = set([key for key, _ in b.IterParams()]) - all_keys = a_keys.union(b_keys) - diff = '' - for key in sorted(all_keys): - if key in a_keys and key not in b_keys: - diff += '>' + spaces + key + ': ' + str(a.Get(key)) + '\n' - elif key in b_keys and key not in a_keys: - diff += '<' + spaces + key + ': ' + str(b.Get(key)) + '\n' - elif a.Get(key) != b.Get(key): - if isinstance(a.Get(key), Params): - diff += '?' + spaces + key + ':\n' - diff += TextDiffHelper(a.Get(key), b.Get(key), spaces + ' ') - else: - diff += '>' + spaces + key + ': ' + str(a.Get(key)) + '\n' - diff += '<' + spaces + key + ': ' + str(b.Get(key)) + '\n' - return diff - - return TextDiffHelper(self, other, spaces=' ') - - -class InstantiableParams(Params): - """Params which can be instantiated. - - When using InstantiableParams, callers must provide a class which supports - initialization using a Params instance. - - This covers a common use case of Params to hold a configuration for a given - class. - """ - - def __init__(self, cls=None): - super().__init__() - self.Define('cls', cls, 'Cls that this param object is associated with.') - - def Instantiate(self, **args): - """Instantiate an instance that this Params is configured for. - - Example: - params = InstantiableParams(cls=MyObject) - params.Define('weight', 0.2, 'Training weight.') - params.weight = 0.9 - obj = params.Instantiate() - - It's common for classes to have a classmethod called Params that returns - a pre-made InstantiableParams, like this: - - params = MyObject.Params() - params.weight = 0.9 - obj = params.Instantiate() - - By convention, anything that parameterizes the behavior of your class - should be stored in this Params object. However, your class may also use - shared state objects which aren't really parameters, like a shared lock. - These can be passed as extra arguments to Instantiate. - - Example: - lock = threading.Lock() - params = MyObject.Params() - obj_a = params.Instantiate(lock=lock) - obj_b = params.Instantiate(lock=lock) - - Args: - **args: Additional keyword arguments to pass to the constructor in - addition to this Params object. - - Returns: - A constructed object where type(object) == cls. - """ - assert self.cls is not None - - # The class initializer is expected to support initialization using Params. - return self.cls(self, **args) - - def Copy(self): - """See base class.""" - return self._CopyTo(type(self)(self.cls)) diff --git a/lingvo/core/hyperparams_test.py b/lingvo/core/hyperparams_test.py deleted file mode 100644 index 6eae84fa..00000000 --- a/lingvo/core/hyperparams_test.py +++ /dev/null @@ -1,613 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for hyperparams.""" - -import collections -import enum - -import dataclasses -import lingvo.compat as tf -from lingvo.core import hyperparams as _params -from lingvo.core import hyperparams_pb2 -from lingvo.core import symbolic -from lingvo.core import test_utils - -FLAGS = tf.flags.FLAGS - - -class TestClass1: - """This class is used in ParamsTest.testToText as a value of a variable.""" - pass - - -class TestClass2: - """This class is used in ParamsTest.testToText as a value of a variable.""" - pass - - -class TestEnum(enum.Enum): - """Test enum class.""" - A = 1 - B = 2 - - -@dataclasses.dataclass -class TestDataClass: - """Test dataclasses.dataclass.""" - a: str - b: tf.DType - - -class TestNamedTuple(collections.namedtuple('TestNamedTuple', ['a', 'b'])): - """Test namedtuple class.""" - pass - - -class InstantiableClass: - """Used for testing InstantiableParams.""" - - def __init__(self, params, other=None): - self.params = params - self.other = other - - -class ParamsTest(test_utils.TestCase): - - def testEquals(self): - # pylint: disable=g-generic-assert - params1 = _params.Params() - params2 = _params.Params() - self.assertTrue(params1 == params2) - params1.Define('first', 'firstvalue', '') - self.assertFalse(params1 == params2) - params2.Define('first', 'firstvalue', '') - self.assertTrue(params1 == params2) - some_object = object() - other_object = object() - params1.Define('second', some_object, '') - params2.Define('second', other_object, '') - self.assertFalse(params1 == params2) - params2.second = some_object - self.assertTrue(params1 == params2) - params1.Define('third', _params.Params(), '') - params2.Define('third', _params.Params(), '') - self.assertTrue(params1 == params2) - params1.third.Define('fourth', 'x', '') - params2.third.Define('fourth', 'y', '') - self.assertFalse(params1 == params2) - params2.third.fourth = 'x' - self.assertTrue(params1 == params2) - # Comparing params to non-param instances. - self.assertFalse(params1 == 3) - self.assertFalse(3 == params1) - # pylint: enable=g-generic-assert - - def testDeepCopy(self): - inner = _params.Params() - inner.Define('alpha', 2, '') - inner.Define('tensor', tf.constant(0), '') - inner.Define('symbol', symbolic.Symbol('symbol'), '') - outer = _params.Params() - outer.Define('beta', 1, '') - outer.Define('inner', inner, '') - outer_copy = outer.Copy() - self.assertIsNot(outer, outer_copy) - self.assertEqual(outer, outer_copy) - self.assertIsNot(outer.inner, outer_copy.inner) - self.assertEqual(outer.inner, outer_copy.inner) - self.assertEqual(outer.inner.alpha, outer_copy.inner.alpha) - self.assertIs(outer.inner.tensor, outer_copy.inner.tensor) - self.assertIs(outer.inner.symbol, outer_copy.inner.symbol) - - def testCopyFieldsTo(self): - source = _params.Params() - dest = _params.Params() - source.Define('a', 'a', '') - source.Define('b', 'b', '') - source.Define('c', 'c', '') - dest.Define('a', '', '') - _params.CopyFieldsTo(source, dest, skip=['b', 'c']) - self.assertEqual(source.a, dest.a) - self.assertNotIn('b', dest) - self.assertNotIn('c', dest) - - def testCopyFieldsToDoesNotCopyClass(self): - source = _params.InstantiableParams(cls=_params.Params) - dest = _params.InstantiableParams(cls=_params.InstantiableParams) - _params.CopyFieldsTo(source, dest) - self.assertEqual(dest.cls, _params.InstantiableParams) - - def testDefineExisting(self): - p = _params.Params() - p.Define('foo', 1, '') - self.assertRaisesRegex(AttributeError, 'already defined', - lambda: p.Define('foo', 1, '')) - - def testLegalParamNames(self): - p = _params.Params() - self.assertRaises(AssertionError, lambda: p.Define(None, 1, '')) - self.assertRaises(AssertionError, lambda: p.Define('', 1, '')) - self.assertRaises(AssertionError, lambda: p.Define('_foo', 1, '')) - self.assertRaises(AssertionError, lambda: p.Define('Foo', 1, '')) - self.assertRaises(AssertionError, lambda: p.Define('1foo', 1, '')) - self.assertRaises(AssertionError, lambda: p.Define('foo$', 1, '')) - p.Define('foo_bar', 1, '') - p.Define('foo9', 1, '') - - def testSetAndGet(self): - p = _params.Params() - self.assertRaisesRegex(AttributeError, 'foo', lambda: p.Set(foo=4)) - # We use setattr() because lambda cannot contain explicit assignment. - self.assertRaisesRegex(AttributeError, 'foo', lambda: setattr(p, 'foo', 4)) - p.Define('foo', 1, '') - self.assertEqual(p.foo, 1) - self.assertEqual(p.Get('foo'), 1) - self.assertIn('foo', p) - self.assertNotIn('bar', p) - p.Set(foo=2) - self.assertEqual(p.foo, 2) - self.assertEqual(p.Get('foo'), 2) - p.foo = 3 - self.assertEqual(p.foo, 3) - self.assertEqual(p.Get('foo'), 3) - p.Delete('foo') - self.assertNotIn('foo', p) - self.assertNotIn('bar', p) - self.assertRaisesRegex(AttributeError, 'foo', lambda: p.foo) - self.assertRaisesRegex(AttributeError, 'foo', p.Get, 'foo') - - def testSetAndGetNestedParam(self): - innermost = _params.Params() - innermost.Define('delta', 22, '') - innermost.Define('zeta', 5, '') - - inner = _params.Params() - inner.Define('alpha', 2, '') - inner.Define('innermost', innermost, '') - - outer = _params.Params() - outer.Define('beta', 1, '') - outer.Define('inner', inner, '') - outer.Define('d', dict(foo='bar'), '') - - self.assertEqual(inner.alpha, 2) - self.assertEqual(outer.beta, 1) - self.assertEqual(outer.d['foo'], 'bar') - self.assertEqual(outer.inner.alpha, 2) - self.assertEqual(outer.inner.innermost.delta, 22) - self.assertEqual(outer.inner.innermost.zeta, 5) - - self.assertEqual(inner.Get('alpha'), 2) - self.assertEqual(outer.Get('beta'), 1) - self.assertEqual(outer.Get('d')['foo'], 'bar') - self.assertEqual(outer.Get('inner.alpha'), 2) - self.assertEqual(outer.Get('inner.innermost.delta'), 22) - self.assertEqual(outer.Get('inner.innermost.zeta'), 5) - - outer.Set(**{'inner.alpha': 3}) - outer.Set(d=dict(foo='baq')) - outer.Delete('beta') - outer.Delete('inner.innermost.zeta') - - self.assertEqual(inner.alpha, 3) - self.assertRaisesRegex(AttributeError, 'beta', lambda: outer.beta) - self.assertEqual(outer.d['foo'], 'baq') - self.assertEqual(outer.inner.alpha, 3) - self.assertEqual(outer.inner.innermost.delta, 22) - self.assertRaisesRegex(AttributeError, 'zeta', - lambda: outer.inner.innermost.zeta) - - self.assertEqual(inner.Get('alpha'), 3) - self.assertRaisesRegex(AttributeError, 'beta', outer.Get, 'beta') - self.assertEqual(outer.Get('d')['foo'], 'baq') - self.assertEqual(outer.Get('inner.alpha'), 3) - self.assertEqual(outer.Get('inner.innermost.delta'), 22) - self.assertRaisesRegex(AttributeError, 'inner.innermost.zeta', outer.Get, - 'inner.innermost.zeta') - - # NOTE(igushev): Finding nested Param object is shared between Get, Set and - # Delete, so we test only Set. - self.assertRaisesRegex(AttributeError, r'inner\.gamma', - lambda: outer.Set(**{'inner.gamma': 5})) - self.assertRaisesRegex(AttributeError, r'inner\.innermost\.bad', - lambda: outer.Set(**{'inner.innermost.bad': 5})) - self.assertRaisesRegex(AssertionError, '^Cannot introspect', - lambda: outer.Set(**{'d.foo': 'baz'})) - - def testFreeze(self): - p = _params.Params() - self.assertRaises(AssertionError, lambda: p.Define('_immutable', 1, '')) - self.assertRaisesRegex(AttributeError, 'foo', lambda: p.Set(foo=4)) - # We use setattr() because lambda cannot contain explicit assignment. - self.assertRaisesRegex(AttributeError, 'foo', lambda: setattr(p, 'foo', 4)) - p.Define('foo', 1, '') - p.Define('nested', p.Copy(), '') - self.assertEqual(p.foo, 1) - self.assertEqual(p.Get('foo'), 1) - self.assertEqual(p.nested.foo, 1) - p.Freeze() - - self.assertRaises(TypeError, lambda: p.Set(foo=2)) - self.assertEqual(p.Get('foo'), 1) - self.assertRaises(TypeError, lambda: setattr(p, 'foo', 3)) - self.assertEqual(p.foo, 1) - self.assertRaises(TypeError, lambda: p.Delete('foo')) - self.assertEqual(p.foo, 1) - self.assertRaises(TypeError, lambda: p.Define('bar', 1, '')) - self.assertRaisesRegex(AttributeError, 'bar', p.Get, 'bar') - - p.nested.foo = 2 - self.assertEqual(p.foo, 1) - self.assertEqual(p.nested.foo, 2) - - self.assertRaises(TypeError, lambda: setattr(p, '_immutable', False)) - - # Copies are still immutable. - q = p.Copy() - self.assertRaises(TypeError, lambda: q.Set(foo=2)) - - def testToString(self): - outer = _params.Params() - outer.Define('foo', 1, '') - inner = _params.Params() - inner.Define('bar', 2, '') - outer.Define('inner', inner, '') - outer.Define('list', [1, inner, 2], '') - outer.Define('dict', {'a': 1, 'b': inner}, '') - outer.Define('enum', TestEnum.B, '') - self.assertEqual( - '\n' + str(outer), """ -{ - dict: {'a': 1, 'b': {'bar': 2}} - enum: TestEnum.B - foo: 1 - inner: { - bar: 2 - } - list: [1, {'bar': 2}, 2] -}""") - - def testIterParams(self): - keys, values = ['a', 'b', 'c', 'd', 'e'], [True, None, 'zippidy', 78.5, 5] - p = _params.Params() - for k, v in zip(keys, values): - p.Define(k, v, 'description of %s' % k) - - k_set, v_set = set(keys), set(values) - number_of_params = 0 - for k, v in p.IterParams(): - self.assertIn(k, k_set) - self.assertIn(v, v_set) - number_of_params += 1 - self.assertEqual(number_of_params, len(keys)) - - def testToText(self): - outer = _params.Params() - outer.Define('foo', 1, '') - inner = _params.Params() - inner.Define('bar', 2.71, '') - inner.Define('baz', 'hello', '') - outer.Define('inner', inner, '') - outer.Define('tau', False, '') - outer.Define('dtype', tf.float32, '') - outer.Define('dtype2', tf.int32, '') - outer.Define('seqlen', [10, inner, 30], '') - outer.Define('tuple', (1, None), '') - outer.Define('list_of_params', [inner.Copy()], '') - outer.Define('class', TestClass1, '') - outer.Define('plain_dict', {'a': 10}, '') - outer.Define('complex_dict', {'a': 10, 'b': inner}, '') - outer.Define('complex_dict_escape', {'a': 'abc"\'\ndef'}, '') - outer.Define('some_class', complex(0, 1), '') - outer.Define('optional_bool', None, '') - outer.Define('enum', TestEnum.B, '') - outer.Define('dataclass', TestDataClass(a=[42], b=tf.float32), '') - outer.Define('namedtuple', TestNamedTuple([42], tf.float32), '') - outer.Define('namedtuple2', tf.io.FixedLenSequenceFeature([42], tf.float32), - '') - # Arbitrarily use HyperparameterValue as some example proto. - outer.Define('proto', hyperparams_pb2.HyperparamValue(int_val=42), '') - - self.assertEqual( - '\n' + outer.ToText(), r""" -class : type/__main__/TestClass1 -complex_dict : {'a': 10, 'b': {'bar': 2.71, 'baz': 'hello'}} -complex_dict_escape : {'a': 'abc"\'\ndef'} -dataclass : {'a': [42], 'b': 'float32'} -dtype : float32 -dtype2 : int32 -enum : TestEnum.B -foo : 1 -inner.bar : 2.71 -inner.baz : 'hello' -list_of_params[0].bar : 2.71 -list_of_params[0].baz : 'hello' -namedtuple : {'a': [42], 'b': 'float32'} -namedtuple2 : {'allow_missing': False, 'default_value': 'NoneType', 'dtype': 'float32', 'shape': [42]} -optional_bool : NoneType -plain_dict : {'a': 10} -proto : proto/lingvo.core.hyperparams_pb2/HyperparamValue/int_val: 42 -seqlen : [10, {'bar': 2.71, 'baz': 'hello'}, 30] -some_class : complex -tau : False -tuple : (1, 'NoneType') -""") - - outer.FromText(""" - dataclass : {'a': 27, 'b': 'int32'} - dtype2 : float32 - inner.baz : 'world' - # foo : 123 - optional_bool : true - list_of_params[0].bar : 2.72 - seqlen : [1, 2.0, '3', [4]] - plain_dict : {'x': 0.3} - class : type/__main__/TestClass2 - tau : true - tuple : (2, 3) - enum : TestEnum.A - # Note dtypes and other non-POD are represented as strings. - namedtuple : {'a': 27, 'b': 'int32'} - namedtuple2 : {'allow_missing': True, 'default_value': 'NoneType', 'dtype': 'int32', 'shape': [43]} - proto : proto/lingvo.core.hyperparams_pb2/HyperparamValue/string_val: "a/b" - """) - - # Note that the 'hello' has turned into 'world'! - self.assertEqual( - '\n' + outer.ToText(), r""" -class : type/__main__/TestClass2 -complex_dict : {'a': 10, 'b': {'bar': 2.71, 'baz': 'world'}} -complex_dict_escape : {'a': 'abc"\'\ndef'} -dataclass : {'a': 27, 'b': 'int32'} -dtype : float32 -dtype2 : float32 -enum : TestEnum.A -foo : 1 -inner.bar : 2.71 -inner.baz : 'world' -list_of_params[0].bar : 2.72 -list_of_params[0].baz : 'hello' -namedtuple : {'a': 27, 'b': 'int32'} -namedtuple2 : {'allow_missing': True, 'default_value': 'NoneType', 'dtype': 'int32', 'shape': [43]} -optional_bool : True -plain_dict : {'x': 0.3} -proto : proto/lingvo.core.hyperparams_pb2/HyperparamValue/string_val: "a/b" -seqlen : [1, 2.0, '3', [4]] -some_class : complex -tau : True -tuple : (2, 3) -""") - self.assertEqual(outer.dataclass.b, tf.int32) - self.assertEqual(outer.namedtuple.b, tf.int32) - self.assertEqual(outer.namedtuple2.dtype, tf.int32) - self.assertIsNone(outer.namedtuple2.default_value, tf.int32) - - def testToFromProto(self): - outer = _params.Params() - outer.Define('integer_val', 1, '') - outer.Define('cls_type', type(int), '') - inner = _params.Params() - inner.Define('float_val', 2.71, '') - inner.Define('string_val', 'rosalie et adrien', '') - inner.Define('bool_val', True, '') - inner.Define('list_of_tuples_of_dicts', [({'string_key': 1729})], '') - inner.Define('range', range(1, 3), '') - outer.Define('inner', inner, '') - outer.Define('empty_list', [], '') - outer.Define('empty_tuple', (), '') - outer.Define('empty_dict', {}, '') - outer.Define('enum', TestEnum.B, '') - outer.Define('proto', hyperparams_pb2.HyperparamValue(int_val=42), '') - outer.Define('dataclass', TestDataClass(a=[42], b=tf.float32), '') - outer.Define('namedtuple', tf.io.FixedLenSequenceFeature([42], tf.float32), - '') - - rebuilt_outer = _params.InstantiableParams.FromProto(outer.ToProto()) - - self.assertNotIn('cls', rebuilt_outer) - self.assertEqual(outer.integer_val, rebuilt_outer.integer_val) - self.assertEqual(outer.cls_type, rebuilt_outer.cls_type) - self.assertNear(outer.inner.float_val, rebuilt_outer.inner.float_val, 1e-6) - self.assertEqual(outer.inner.string_val, rebuilt_outer.inner.string_val) - self.assertEqual(outer.inner.bool_val, rebuilt_outer.inner.bool_val) - self.assertEqual(outer.inner.list_of_tuples_of_dicts, - rebuilt_outer.inner.list_of_tuples_of_dicts) - self.assertEqual([1, 2], rebuilt_outer.inner.range) # Rebuilt as list. - self.assertEqual(outer.empty_list, rebuilt_outer.empty_list) - self.assertEqual(outer.empty_tuple, rebuilt_outer.empty_tuple) - self.assertEqual(outer.empty_dict, rebuilt_outer.empty_dict) - self.assertEqual(outer.enum, rebuilt_outer.enum) - self.assertEqual(outer.proto, rebuilt_outer.proto) - self.assertEqual(outer.dataclass, rebuilt_outer.dataclass) - self.assertEqual(outer.namedtuple, rebuilt_outer.namedtuple) - - def testStringEscaping(self): - p = _params.Params() - p.Define('bs_end_quote', 'Single\\', '') - p.Define('embedded_newlines', 'Split\nAcross\nLines', '') - p.Define('empty', '', '') - p.Define('empty_first_line', '\nNext', '') - p.Define('end_escape_quote', '""Split\'\nLine', '') - p.Define('escaping_single', 'In "quotes"', '') - p.Define('escaping_double', 'In \\\'quotes\'', '') - - # Make sure it escapes properly. - text_value = p.ToText() - self.assertEqual( - '\n' + text_value, r""" -bs_end_quote : 'Single\\' -embedded_newlines : 'Split -Across -Lines' -empty : '' -empty_first_line : ' -Next' -end_escape_quote : '""Split\' -Line' -escaping_double : "In \\'quotes'" -escaping_single : 'In "quotes"' -""") - - # Reset the values and make sure that reading back in parses. - p.bs_end_quote = '' - p.embedded_newlines = '' - p.empty_first_line = '' - p.end_escape_quote = '' - p.escaping_double = '' - p.escaping_single = '' - - p.FromText(text_value) - self.assertEqual(p.bs_end_quote, 'Single\\') - self.assertEqual(p.embedded_newlines, 'Split\nAcross\nLines') - self.assertEqual(p.empty_first_line, '\nNext') - self.assertEqual(p.end_escape_quote, '""Split\'\nLine') - self.assertEqual(p.escaping_single, 'In "quotes"') - self.assertEqual(p.escaping_double, 'In \\\'quotes\'') - - def testFromToText(self): - p = _params.Params() - p.Define('activation', 'RELU', 'Can be a string or a list of strings.') - np = p.Copy() - p.Set(activation=['RELU', 'NONE']) - np.FromText(p.ToText()) - self.assertEqual(np.activation, ['RELU', 'NONE']) - - def testSimilarKeys(self): - p = _params.Params() - p.Define('activation', 'RELU', 'Can be a string or a list of strings.') - p.Define('activations', 'RELU', 'Many activations.') - p.Define('cheesecake', None, 'dessert') - p.Define('tofu', None, 'not dessert') - - def set_param(): - p.actuvation = 1 - - self.assertRaisesWithLiteralMatch( - AttributeError, 'actuvation (did you mean: [activation,activations])', - set_param) - - def testFromToTextTypes(self): - p = _params.Params() - p.Define('scale', None, 'A float scale but default is None.') - np1 = p.Copy() - np2 = p.Copy() - p.Set(scale=1.0) - self.assertRaises(ValueError, lambda: np1.FromText(p.ToText())) - text, types = p.ToText(include_types=True) - self.assertEqual(types['scale'], 'float') - np2.FromText(text, type_overrides=types) - self.assertEqual(np2.scale, 1.0) - - def testFromTextBadFormat(self): - p = _params.Params() - p.Define('scale', 1.0, 'A float parameter.') - np1 = p.Copy() - self.assertRaises(ValueError, lambda: np1.FromText('scale=2.0')) - np2 = p.Copy() - np2.FromText('scale:2.0') - self.assertEqual(np2.scale, 2.0) - - def testTypeOverride(self): - p = _params.Params() - p.Define('scale', '1', 'A str that will be overriden by float.') - np1 = p.Copy() - np2 = p.Copy() - p.Set(scale=2.1) - - # Check that type is erased if types are not used (old behavior). - no_types_text = p.ToText() - np1.FromText(no_types_text) - self.assertEqual(np1.scale, '2.1') - - # Check that type is not erased if we use type_overrides. - types_text, types = p.ToText(include_types=True) - self.assertEqual(types['scale'], 'float') - np2.FromText(types_text, type_overrides=types) - self.assertEqual(np2.scale, 2.1) - - def testDeterministicSerialize(self): - p = _params.Params() - p.Define('a', 42, '') - p.Define('b', None, '') - p.Define('c', 'C', '') - p.Define('d', None, '') - pnest = _params.Params() - pnest.Define('x', 'X', '') - p.Define('e', pnest, '') - p.Define('f', [pnest.Copy().Set(x=2)], '') - pclean = p.Copy() - - p.a = 43 - p.d = [1, 2, 3] - p.e.x = 7 - base_serialized = p.ToTextWithTypes() - for _ in range(10): - serialized = p.ToTextWithTypes() - serialized_copy = p.Copy().ToTextWithTypes() - self.assertEqual(serialized, base_serialized) - self.assertEqual(serialized_copy, base_serialized) - for x in [serialized, serialized_copy]: - deserialized = pclean.Copy() - deserialized.FromTextWithTypes(x) - self.assertEqual(p, deserialized) - - def testDiff(self): - a = _params.Params() - d_inner = _params.Params() - d_inner.Define('hey', 'hi', '') - a.Define('a', 42, '') - a.Define('c', 'C', '') - a.Define('d', d_inner, '') - b = a.Copy() - - # Everything is the same so we don't expect diffs. - self.assertEqual(a.TextDiff(b), '') - - a.a = 43 - self.assertEqual(a.TextDiff(b), '> a: 43\n' '< a: 42\n') - - b.d.hey = 'hello' - self.assertEqual( - a.TextDiff(b), '> a: 43\n' - '< a: 42\n' - '? d:\n' - '> hey: hi\n' - '< hey: hello\n') - - def testInstantiate(self): - a = _params.InstantiableParams(InstantiableClass) - a.Define('new_param', None, 'A meaningless param.') - a.new_param = 'hi' - - obj = a.Instantiate() - self.assertIsInstance(obj, InstantiableClass) - self.assertEqual(obj.params.new_param, 'hi') - - def testInstantiateWithParams(self): - a = _params.InstantiableParams(InstantiableClass) - a.Define('new_param', None, 'A meaningless param.') - a.new_param = 'hi' - - # Same as the previous test, but InstantiableClass should also get - # other=15 passed as a keyword argument to the constructor. - obj = a.Instantiate(other=15) - self.assertIsInstance(obj, InstantiableClass) - self.assertEqual(obj.params.new_param, 'hi') - self.assertEqual(obj.other, 15) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/inference_graph.proto b/lingvo/core/inference_graph.proto deleted file mode 100644 index 0cb45881..00000000 --- a/lingvo/core/inference_graph.proto +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -syntax = "proto2"; - -package tensorflow.lingvo; - -import "google/protobuf/any.proto"; -import "tensorflow/core/framework/graph.proto"; -import "tensorflow/core/framework/types.proto"; -import "tensorflow/core/protobuf/meta_graph.proto"; -import "tensorflow/core/protobuf/saver.proto"; - -// Represents a frozen graph constructed at training time which can be used with -// the Predictor class in predictor.py to perform inference. -message InferenceGraph { - // The graph definition, which can be imported using tf.import_graph_def. - // Note that in some situations, the actual graph def is saved separately - // and may be absent here. - optional tensorflow.GraphDef graph_def = 1; - - // The saver definition, which is used to load checkpoints correctly. Can be - // imported using tf.Saver(saver_def=_). - // Note that if the graph is frozen (all vars converted to constants) and - // can no longer import a checkpoint, this may be absent. - optional tensorflow.SaverDef saver_def = 2; - - // Defines discrete subgraphs of the overall inference graph. - // This is populated if the overall inference graph is generated for piecewise - // evaluation within a larger pipeline. - message Subgraph { - // Subgraph level meta data. - optional SubgraphMeta meta = 1; - - // Maps logical feed/fetch name to tensor name with the GraphDef. - // - // RNN states should be named the same in both the feed and fetch lists. - // Optionally if multiple related RNN states are present they can be of the - // form some_state_name:0, some_state_name:1, ... to indicate to tooling and - // readers that the states are grouped. - map feeds = 2; - map fetches = 3; - - // Separate feed/fetch metadata. - map feeds_meta = 4; - map fetches_meta = 5; - } - map subgraphs = 5; - - // Serialized dump of the model hyperparameters used to generate this - // instance. - optional string hyperparameters = 7; - - // Optional metadata about a subgraph. - message SubgraphMeta { - // Private extension data between producer and driver code. - repeated google.protobuf.Any extensions = 1; - } - - // Optional metadata about a feed or fetch. - // Some of this is redundant with data that can be derived from the GraphDef - // but is useful to have explicitly. - // - // NEXT TAG: 14 - message FeedFetchMeta { - // Private extension data between producer and driver code. - repeated google.protobuf.Any extensions = 1; - - // Datatype of the feed/fetch. - optional tensorflow.DataType data_type = 2; - - // Shape of the feed/fetch. If a dimension is not fixed, it is -1. - // Omitted/empty if shape not specified. - repeated int32 shape = 3 [packed = true]; - - // Char-per-dimension layout of a feed/fetch. - // This is by convention between the producer and driver or just for - // documentation. It is not intended for driving behavior but can be used - // for checks. - // In general, the following is recommended: - // t=time, b=batch, d=depth, f=feature, c=channel, 1=single - // Examples: - // 'tbd': time-batch-depth - // 'btd': batch-time-depth - // 'btfc': batch-time-feature-channel - optional string layout = 4; - - // Axis along which the feed/fetch can be coalesced at runtime. - // For example, a feed having the shape [time, batch, depth] will have a - // dispatch_stride_axis of 0. - optional int32 dispatch_stride_axis = 13; - - // If the feed/fetch is quantized, this is the number of bits in the - // quantized representation. If the ranges are non-const, then the - // *_tensor variants will be populated with the tensor name (which - // will be guaranteed to be a reference within the overall subgraph). - optional int32 quantized_num_bits = 8; - oneof quantized_min_oneof { - double quantized_min_const = 9; - string quantized_min_tensor = 10; - } - oneof quantized_max_oneof { - double quantized_max_const = 11; - string quantized_max_tensor = 12; - } - - reserved 5, 6, 7; // deprecated fields. - } - - reserved 3, 4, 6; // deprecated fields. - - repeated tensorflow.AssetFileDef asset_file_def = 10; -} diff --git a/lingvo/core/inference_graph_exporter.py b/lingvo/core/inference_graph_exporter.py deleted file mode 100644 index 3294a614..00000000 --- a/lingvo/core/inference_graph_exporter.py +++ /dev/null @@ -1,535 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Utility for exporting an InferenceGraph proto from model params.""" - -import collections -import contextlib -import re -import lingvo.compat as tf -from lingvo.core import base_model -from lingvo.core import bfloat16_variables -from lingvo.core import inference_graph_pb2 -from lingvo.core import py_utils -import six - -from google.protobuf import text_format - -FLAGS = tf.flags.FLAGS - -# InferenceDeviceOptions contains options to configure inference on the device. -# device: Device to infer on. -# retain_device_placement: If true, the specified device in the generated -# inference graph nodes will be retained. Otherwise, the specified device -# will be cleared, so that the runtime can choose automatically. -# var_options: Options on handling variables. For TPUs, variables can be -# either placed on device through 'ON_DEVICE' option, or treated as -# constants with AS_CONSTANTS. -# gen_init_op: Whether to serialize initialization ops for the device. For TPUs, -# servers can be initialized globally once, in which case this should be -# turned off to avoid tripping initialization checks. -# dtype_override: Whether to override the dtype to use for activations and -# weights in the model. Options supported are None or tf.bfloat16. -InferenceDeviceOptions = collections.namedtuple('InferenceDeviceOptions', [ - 'device', 'retain_device_placement', 'var_options', 'gen_init_op', - 'dtype_override' -]) - -_CONST_GUARANTEE = None - - -@contextlib.contextmanager -def NoConstGuaranteeScope(): - """Disallow const gauranteeing variable with-in scope.""" - global _CONST_GUARANTEE - var_scope = tf.get_variable_scope() - old_caching_device = var_scope.caching_device - old_val = _CONST_GUARANTEE - var_scope.set_caching_device(None) - _CONST_GUARANTEE = False - yield - _CONST_GUARANTEE = old_val - var_scope.set_caching_device(old_caching_device) - - -# Marks variable as constants for compilation -def MaybeGuaranteeConstGetter(getter, name, *args, **kwargs): - global _CONST_GUARANTEE - if _CONST_GUARANTEE: - with tf.control_dependencies(None): - return tf.guarantee_const( - getter(name, *args, **kwargs), name=name + '/GuaranteeConst') - else: - return getter(name, *args, **kwargs) - - -@contextlib.contextmanager -def ConstGuaranteeScope(): - """Treats all variables under this scope as constants.""" - global _CONST_GUARANTEE - var_scope = tf.get_variable_scope() - old_custom_getter = var_scope.custom_getter - old_caching_device = var_scope.caching_device - old_val = _CONST_GUARANTEE - var_scope.set_custom_getter(MaybeGuaranteeConstGetter) - var_scope.set_caching_device(lambda op: op.device) - _CONST_GUARANTEE = True - yield - _CONST_GUARANTEE = old_val - var_scope.set_custom_getter(old_custom_getter) - var_scope.set_caching_device(old_caching_device) - - -@contextlib.contextmanager -def _DummyScope(): - yield None - - -def _GetVarName(v): - return v.name[:-len(':0')] - - -def _MakeVariableDictionary(variables): - """Returns a dictionary with name -> tf.Variable() mapping.""" - vars_dict = {} - for v in variables: - vars_dict[_GetVarName(v)] = v - return vars_dict - - -def IsTpu(device_options): - return device_options.device == 'tpu' - - -def ShouldForceBfloat16ForWeightsAndActivations(device_options): - return device_options.dtype_override == tf.bfloat16 - - -def ConvertSubgraphDictToProto(subgraphs_dict): - """Converts dict of subgraphs/feeds/fetches to InferenceGraph. - - Args: - subgraphs_dict: Dict of (fetches, feeds) where each fetches/feeds is a - NestedMap. - - Returns: - Equivalent InferenceGraph. - """ - # Build the output inference graph. - inference_graph_proto = inference_graph_pb2.InferenceGraph() - for subgraph_name, tensors in subgraphs_dict.items(): - fetches = tensors[0] - feeds = tensors[1] - - # Rewrite fetches and feeds to map to their tensor name instead of - # Tensor instance. - named_fetches = {k: v.name for k, v in fetches.items() if v is not None} - named_feeds = {k: v.name for k, v in feeds.items() if v is not None} - - # Export as subgraph. - inference_graph_proto.subgraphs[subgraph_name].fetches.update(named_fetches) - inference_graph_proto.subgraphs[subgraph_name].feeds.update(named_feeds) - return inference_graph_proto - - -def GetOutputOpNames(graph, - inference_graph_proto, - subgraphs=None, - preserve_colocation_nodes=True, - preserve_saver_restore_nodes=False, - preserve_extra_ops=None): - """Gets output op names from an inference graph. - - Args: - graph: The tf graph. - inference_graph_proto: an InferenceGraph proto. - subgraphs: an optional list of subgraph names. If provided, only output ops - from these subgraphs are preserved. Otherwise, all subgraphs are included. - preserve_colocation_nodes: a Python bool, default to True. Preserves nodes - colocating with the closure of output ops in the returned array. - preserve_saver_restore_nodes: a Python bool, default to True. Preserves - nodes for restoring according to inference_graph_proto.saver_def. - preserve_extra_ops: an optional list of extra op names to preserve as long - as they present in the graph. - - Returns: - Array of tf op names that should be preserved in the graph. - """ - output_op_names = set() - - def _GetOpName(tensor_or_op_name): - """Returns the op name of the given node name.""" - # Tensor names have format :. Some inference - # graphs put tensors and others put ops in the feeds/fetches (depends - # on how it is used). We differentiate here. We still do the lookup in - # the graph to sanity check (versus relying on the text manipulation). - # If this logic ever breaks, TensorFlow will raise a ValueError with - # a description of the syntax of each. - if re.search(r':[0-9]+$', tensor_or_op_name): - # Tensor-name. - t = graph.get_tensor_by_name(tensor_or_op_name) - return t.op.name - else: - op = graph.get_operation_by_name(tensor_or_op_name) - return op.name - - for subgraph_name, subgraph in inference_graph_proto.subgraphs.items(): - if subgraphs and subgraph_name not in subgraphs: - tf.logging.info('Skip subgraph %s.', subgraph_name) - continue - # Sometimes feeds aren't connected to any outputs but keep them in the graph - # anyways to avoid errors. - for tensor_or_op_name in ( - list(subgraph.feeds.values()) + list(subgraph.fetches.values())): - output_op_names.add(_GetOpName(tensor_or_op_name)) - - if preserve_saver_restore_nodes: - # Only nodes for restoring is preserved. saver_def.save_tensor_name is - # skipped because it's only used for saving. - saver_def = inference_graph_proto.saver_def - output_op_names.add(_GetOpName(saver_def.filename_tensor_name)) - output_op_names.add(_GetOpName(saver_def.restore_op_name)) - - if not preserve_colocation_nodes and not preserve_extra_ops: - return sorted(list(output_op_names)) - - # We also need to preserve any nodes that are used for colocation. - # E.g., a node may have this attr: - # attr { - # key: "_class" - # value { - # list { - # s: "loc:@inference/embedding_lookup/Read/ReadVariableOp" - # } - # } - # } - # - # In this case, we need to make sure the node - # inference/embedding_lookup/Read/ReadVariableOp is not pruned. - # - # TODO(zhifengc): It's possible that it's better to fix in - # tf.graph_util.extract_sub_graph. - graph_def = tf.graph_util.extract_sub_graph(graph.as_graph_def(), - list(output_op_names)) - reachable_vars = [node.name for node in graph_def.node] - - for node in graph.get_operations(): - if preserve_extra_ops and node.name in preserve_extra_ops: - output_op_names.add(node.name) - elif preserve_colocation_nodes and '_class' in node.node_def.attr: - for loc in node.node_def.attr['_class'].list.s: - loc = six.ensure_text(loc, 'utf-8') - if loc.startswith('loc:@'): - loc_name = loc[5:] - if loc_name not in reachable_vars: - # Skip nodes that cannot be reached from the pruned graph. - continue - output_op_names.add(node.name) - - return sorted(list(output_op_names)) - - -def _ParamExists(param_obj, param_name): - """Tests whether param_name is contained in param_obj.""" - if not param_obj: - return - for k, _ in param_obj.IterParams(): - if k == param_name: - return True - return False - - -def _FreezeGraphFromCheckpoint(graph, saver, checkpoint, output_op_names): - """Freezes a graph from a checkpoint. - - Args: - graph: tf.Graph. - saver: The tf.Saver to use for restoration. - checkpoint: The checkpoint to restore. - output_op_names: Names of output ops. - - Returns: - Resulting tf.GraphDef. - """ - sess = tf.Session(graph=graph, config=py_utils.SessionConfig()) - saver.restore(sess, checkpoint) - return tf.graph_util.convert_variables_to_constants( - sess, graph.as_graph_def(), output_op_names) - - -def _FreezeDefaults(graph, output_op_names): - """Default initializes a graph and freezes it. - - Args: - graph: tf.Graph. - output_op_names: Names of output ops. - - Returns: - Resulting tf.GraphDef. - """ - with tf.Session(graph=graph, config=py_utils.SessionConfig()) as sess: - sess.run(graph.get_operation_by_name('init_all_variables')) - return tf.graph_util.convert_variables_to_constants(sess, - graph.as_graph_def(), - output_op_names) - - -class InferenceGraphExporter: - """Class for exporting inference graphs.""" - - @classmethod - def Export(cls, - model_cfg, - model_task_name=None, - device_options=InferenceDeviceOptions( - device='', - retain_device_placement=False, - var_options=None, - gen_init_op=True, - dtype_override=None), - freeze_checkpoint=None, - freeze_defaults=False, - export_path=None, - subgraph_filter=None, - random_seed=None, - disable_packed_input=True): - """Exports a InferenceGraph proto with piecewise subgraphs. - - Sets FLAGS.enable_asserts to False unless user explicitly sets it to True. - - Args: - model_cfg: a Params instance as returned by - model_registry.GetParams(modelname, 'Test') or model_params.Model(). - model_task_name: The task to generate an inference graph for. Should be - None for single-task models. - device_options: Device options for the accelerator used for serving. - freeze_checkpoint: The checkpoint to load. Loads and freezes the model if - given. - freeze_defaults: Default initializes the graph and freeze. Useful for - early testing of downstream tools without having a checkpoint. - export_path: If not None, write the inference graph in ASCII to this path. - subgraph_filter: A list of subgraph names. If not None or empty, export - only this list of inference subgraphs. - random_seed: Fixes the random seed in the exported inference graph. - disable_packed_input: Disable packed input for inference writing purposes. - - Returns: - InferenceGraph proto. - - Raises: - ValueError: if the model does not support the listed subgraphs. - """ - assert issubclass(model_cfg.cls, base_model.BaseModel) - - # Disable assertions unless user explicitly enables it. - if FLAGS['enable_asserts'].using_default_value: - FLAGS.enable_asserts = False - - # TODO(laurenzo): Work out how much we need to specify here in terms of - # cluster configuration. - cls._SetClusterParams(model_cfg.cluster, device_options) - - # Configure the model. - model_cfg.random_seed = random_seed - model_cfg.is_inference = True - - if disable_packed_input: - - def _DisablePackedInput(task): - if (_ParamExists(task, 'encoder') and - _ParamExists(task.encoder, 'packed_input')): - task.encoder.packed_input = False - if (_ParamExists(task, 'decoder') and - _ParamExists(task.decoder, 'packed_input')): - task.decoder.packed_input = False - - if issubclass(model_cfg.cls, base_model.MultiTaskModel): - for _, task_param in model_cfg.task_params.IterParams(): - _DisablePackedInput(task_param) - else: - _DisablePackedInput(model_cfg.task) - - tf.logging.info('Model %s params:', model_cfg.name) - for line in model_cfg.ToText().split('\n'): - tf.logging.info('%s', line) - - # Instantiate the graph. - graph = tf.Graph() - with graph.as_default(): - tf.random.set_seed(random_seed) - cluster = model_cfg.cluster.Instantiate() - device = cluster.GetPlacer() - tpu_const_scope = _DummyScope() - if (IsTpu(device_options) and - device_options.var_options == 'AS_CONSTANTS'): - # Do not specify devices for variables if we are marking them as - # constants. - device = '' - tpu_const_scope = ConstGuaranteeScope() - - with cluster, tf.device(device), tpu_const_scope: - - bfloat16_override = ShouldForceBfloat16ForWeightsAndActivations( - device_options) - - if bfloat16_override: - py_utils.UpdateDtype(model_cfg, tf.bfloat16) - py_utils.UpdateFpropDtype(model_cfg, tf.bfloat16) - - # Hard-code TPU-related flags prior to instantiating model. - old_enable_asserts = FLAGS.enable_asserts - old_xla_device = FLAGS.xla_device - if IsTpu(device_options): - FLAGS.enable_asserts = False - FLAGS.xla_device = 'tpu' - - try: - mdl = model_cfg.Instantiate() - task = mdl.GetTask(model_task_name) - - variables_to_restore = ( - _MakeVariableDictionary(tf.global_variables()) if not mdl.ema else - mdl.ema.variables_to_restore(mdl.variables_for_ema)) - - if bfloat16_override: - saver_var_spec = ( - bfloat16_variables - .get_saver_spec_for_variables_with_bf16_overrides( - variables_to_restore)) - else: - saver_var_spec = variables_to_restore - - saver = tf.train.Saver(saver_var_spec) - tf.variables_initializer( - tf.global_variables(), name='init_all_variables') - if IsTpu(device_options) and device_options.gen_init_op: - tf.group(tf.tpu.initialize_system(), name='tpu_init_op') - - inference_graph_proto = inference_graph_pb2.InferenceGraph() - subgraphs_proto = task.Inference() - if isinstance(subgraphs_proto, dict): - subgraphs_proto = ConvertSubgraphDictToProto(subgraphs_proto) - for name, subgraph in subgraphs_proto.subgraphs.items(): - if not subgraph_filter or name in subgraph_filter: - inference_graph_proto.subgraphs[name].CopyFrom(subgraph) - - # Yes, graph collections are bad, however this seems to be the - # easiest way to get this assets registered from - # TextFileInitializer. - assets_collection = tf.compat.v1.get_collection( - tf.compat.v1.GraphKeys.ASSET_FILEPATHS) - for asset in assets_collection: - if asset.op.type == 'Const' and asset.op.get_attr( - 'dtype') == tf.dtypes.string: - constant_value = asset.op.get_attr('value') - if constant_value.string_val: - tf.logging.info('Found asset file_path: %s', - constant_value.string_val[0]) - asset_file_def = inference_graph_proto.asset_file_def.add() - asset_file_def.tensor_info.name = asset.name - asset_file_def.filename = constant_value.string_val[0] - - # Add a table init op and global variable init op to the graph. - # Tables can be declared anywhere in the graph, so this op has to be - # added last. - tf.tables_initializer(name='init_all_tables') - finally: - # Reset TPU-related flags after model instantiation. - FLAGS.enable_asserts = old_enable_asserts - FLAGS.xla_device = old_xla_device - - tf.logging.info('Graph contains ops: %r', - [op.name for op in graph.get_operations()]) - - inference_graph_proto.saver_def.CopyFrom(saver.as_saver_def()) - - # Freezing. - if freeze_defaults or freeze_checkpoint: - output_op_names = GetOutputOpNames( - graph, inference_graph_proto, preserve_colocation_nodes=False) - if cls._DeviceSupportsFreezing(device_options): - raise ValueError('freeze_checkpoint cannot be used with device ' + - device_options.device) - if freeze_checkpoint: - tf.logging.info('Freezing graph from checkpoint: %s', - freeze_checkpoint) - graph_def = _FreezeGraphFromCheckpoint(graph, saver, freeze_checkpoint, - output_op_names) - elif freeze_defaults: - tf.logging.info('Default initializing graph and freezing.') - graph_def = _FreezeDefaults(graph, output_op_names) - else: - output_op_names = GetOutputOpNames(graph, inference_graph_proto) - - # Prune the graph to just the parts we need. - # To support restoring, we have to not prune out the restore node. - output_op_names.append('init_all_tables') - output_op_names.append('init_all_variables') - output_op_names.append('save/control_dependency') - output_op_names.append('save/restore_all') - if IsTpu(device_options) and device_options.gen_init_op: - output_op_names.append('tpu_init_op') - graph_def = graph.as_graph_def() - tf.logging.info('Pruning graph to output ops: %r', output_op_names) - graph_def = tf.graph_util.extract_sub_graph(graph_def, output_op_names) - - if not device_options.retain_device_placement: - # Clear the device so that the runtime can choose. - tf.logging.info('Clearing device placement for: %s', - device_options.device) - for node in graph_def.node: - node.ClearField('device') - for function in graph_def.library.function: - for node_def in function.node_def: - node_def.ClearField('device') - - inference_graph_proto.graph_def.CopyFrom(graph_def) - - if export_path: - with tf.io.gfile.GFile(export_path, 'w') as f: - f.write(text_format.MessageToString(inference_graph_proto)) - return inference_graph_proto - - @classmethod - def _SetClusterParams(cls, cluster_params, device_options): - """Sets cluster params. - - Args: - cluster_params: Model().cluster config. - device_options: InferenceDeviceOptions. - """ - - def Update(p): - """Update cluster params `p`.""" - p.name = '/job:localhost' - p.replicas = 1 - p.tpus_per_replica = 1 if IsTpu(device_options) else 0 - p.gpus_per_replica = 0 - p.devices_per_split = 1 - - cluster_params.mode = 'sync' - cluster_params.job = 'decoder' - cluster_params.add_summary = False - cluster_params.do_eval = True - Update(cluster_params.controller) - Update(cluster_params.worker) - Update(cluster_params.ps) - Update(cluster_params.evaler) - Update(cluster_params.decoder) - Update(cluster_params.input) - - @classmethod - def _DeviceSupportsFreezing(cls, device_options): - return IsTpu(device_options) diff --git a/lingvo/core/inference_graph_exporter_test.py b/lingvo/core/inference_graph_exporter_test.py deleted file mode 100644 index 35dc2a81..00000000 --- a/lingvo/core/inference_graph_exporter_test.py +++ /dev/null @@ -1,453 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for inference_graph_exporter.""" - -from lingvo import model_registry -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import base_model_params -from lingvo.core import inference_graph_exporter -from lingvo.core import inference_graph_pb2 -from lingvo.core import predictor -from lingvo.core import py_utils -from lingvo.core import test_utils - - -class DummyLegacyModel(base_model.BaseTask): - - def Inference(self): - if py_utils.use_tpu(): - raise NotImplementedError('TPU is not supported.') - with tf.name_scope('inference'): - feed1 = tf.placeholder(name='feed1_node', dtype=tf.float32, shape=[1]) - fetch1 = tf.identity(feed1, name='fetch1_node') - return { - 'default': ( - py_utils.NestedMap({ - 'fetch1': fetch1, - 'fetch_op': fetch1.op, # Tests that ops are supported. - }), - py_utils.NestedMap({ - 'feed1': feed1, - })), - 'unused': (py_utils.NestedMap({}), py_utils.NestedMap({})), - } - - -@model_registry.RegisterSingleTaskModel -class DummyLegacyModelParams(base_model_params.SingleTaskModelParams): - - @classmethod - def Test(cls): - p = base_input_generator.BaseSequenceInputGenerator.Params() - p.name = 'input' - return p - - @classmethod - def Task(cls): - p = DummyLegacyModel.Params() - p.name = 'testing' - return p - - -class DummyModel(base_model.BaseTask): - - def Inference(self): - with tf.name_scope('inference'): - feed1 = tf.placeholder(name='feed1_node', dtype=tf.float32, shape=[1]) - fetch1 = tf.identity(feed1, name='fetch1_node') - inference_graph = inference_graph_pb2.InferenceGraph() - subgraph = inference_graph.subgraphs['default'] - subgraph.feeds['feed1'] = feed1.name - subgraph.fetches['fetch1'] = fetch1.name - # Tests that ops are supported. - subgraph.fetches['fetch_op'] = fetch1.op.name - return inference_graph - - -@model_registry.RegisterSingleTaskModel -class DummyModelParams(base_model_params.SingleTaskModelParams): - - @classmethod - def Test(cls): - p = base_input_generator.BaseSequenceInputGenerator.Params() - p.name = 'input' - return p - - @classmethod - def Task(cls): - p = DummyModel.Params() - p.name = 'testing' - return p - - -class InferenceGraphExporterTest(test_utils.TestCase): - - def testExportModelParamsWithSubgraphDict(self): - params = model_registry.GetParams('test.DummyLegacyModelParams', 'Test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params, subgraph_filter=['default']) - - # Should populate subgraphs. - self.assertIn('default', inference_graph.subgraphs) - self.assertNotIn('unused', inference_graph.subgraphs) - subgraph = inference_graph.subgraphs['default'] - self.assertIn('feed1', subgraph.feeds) - self.assertIn('fetch1', subgraph.fetches) - - self.assertEqual(subgraph.feeds['feed1'], 'inference/feed1_node:0') - self.assertEqual(subgraph.fetches['fetch1'], 'inference/fetch1_node:0') - self.assertEqual(subgraph.fetches['fetch_op'], 'inference/fetch1_node') - - def testExportModelParamsWithInferenceGraph(self): - params = model_registry.GetParams('test.DummyModelParams', 'Test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params) - - # Should populate subgraphs. - self.assertIn('default', inference_graph.subgraphs) - subgraph = inference_graph.subgraphs['default'] - self.assertIn('feed1', subgraph.feeds) - self.assertIn('fetch1', subgraph.fetches) - - self.assertEqual(subgraph.feeds['feed1'], 'inference/feed1_node:0') - self.assertEqual(subgraph.fetches['fetch1'], 'inference/fetch1_node:0') - self.assertEqual(subgraph.fetches['fetch_op'], 'inference/fetch1_node') - - def testExportModelDoesNotAffectFlagsOnException(self): - initial_flags = {k: tf.flags.FLAGS[k].value for k in tf.flags.FLAGS} - params = model_registry.GetParams('test.DummyLegacyModelParams', 'Test') - with self.assertRaises(NotImplementedError): - inference_graph_exporter.InferenceGraphExporter.Export( - params, - device_options=inference_graph_exporter.InferenceDeviceOptions( - device='tpu', - retain_device_placement=False, - var_options=None, - gen_init_op=True, - dtype_override=None)) - self.assertDictEqual(initial_flags, - {k: tf.flags.FLAGS[k].value for k in tf.flags.FLAGS}) - - -class NoConstGuaranteeScopeTest(test_utils.TestCase): - - def testNoConsting(self): - with inference_graph_exporter.ConstGuaranteeScope(): - wp = py_utils.WeightParams( - shape=[1], - init=py_utils.WeightInit.Constant(0.0), - dtype=tf.float32, - collections=['v']) - v = py_utils.CreateVariable('v', wp) - self.assertEqual(tf.Tensor, type(v)) - with inference_graph_exporter.NoConstGuaranteeScope(): - v = py_utils.CreateVariable('v', wp, reuse=True) - self.assertIsInstance(v, tf.Variable) - - -class LinearModel(base_model.BaseTask): - """A basic linear model.""" - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'linear_model' - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w = py_utils.WeightParams( - shape=[3], - init=py_utils.WeightInit.Gaussian(scale=1.0, seed=123456), - dtype=p.dtype) - b = py_utils.WeightParams( - shape=[], - init=py_utils.WeightInit.Gaussian(scale=1.0, seed=234567), - dtype=p.dtype) - self.CreateVariable('w', w) - self.CreateVariable('b', b) - - def Inference(self): - """Computes y = w^T x + b. Returns y and x, as outputs and inputs.""" - # Add a dummy file def to the collection - filename = tf.convert_to_tensor( - 'dummy.txt', tf.dtypes.string, name='asset_filepath') - tf.compat.v1.add_to_collection(tf.compat.v1.GraphKeys.ASSET_FILEPATHS, - filename) - - with tf.name_scope('inference'): - x = tf.placeholder(dtype=tf.float32, name='input') - r = tf.random.stateless_uniform([3], - seed=py_utils.GenerateStepSeedPair( - self.params, self.theta.global_step)) - y = tf.reduce_sum((self.vars.w + r) * x) + self.vars.b - return {'default': ({'output': y}, {'input': x})} - - -class LinearModelTpu(LinearModel): - """A basic linear model that runs inference on the TPU.""" - - def Inference(self): - """Computes y = w^T x + b. Returns y and x, as outputs and inputs.""" - with tf.name_scope('inference'): - x = tf.placeholder(dtype=tf.bfloat16, name='input') - - def InferenceFn(x): - return tf.reduce_sum(self.vars.w * x) + self.vars.b - - y = tf.tpu.rewrite(InferenceFn, [x]) - return {'tpu': ({'output': y[0]}, {'input': x})} - - -@model_registry.RegisterSingleTaskModel -class LinearModelParams(base_model_params.SingleTaskModelParams): - - @classmethod - def Test(cls): - p = base_input_generator.BaseSequenceInputGenerator.Params() - p.name = 'input' - return p - - @classmethod - def Task(cls): - p = LinearModel.Params() - p.name = 'testing' - return p - - -@model_registry.RegisterSingleTaskModel -class LinearModelTpuParams(base_model_params.SingleTaskModelParams): - - @classmethod - def Test(cls): - p = base_input_generator.BaseSequenceInputGenerator.Params() - p.name = 'input' - return p - - @classmethod - def Task(cls): - p = LinearModelTpu.Params() - p.name = 'testing' - return p - - -@model_registry.RegisterSingleTaskModel -class LinearModelTpuParamsWithEma(base_model_params.SingleTaskModelParams): - - @classmethod - def Test(cls): - p = base_input_generator.BaseSequenceInputGenerator.Params() - p.name = 'input' - return p - - @classmethod - def Task(cls): - p = LinearModelTpu.Params() - p.name = 'testing' - p.train.ema_decay = 0.99 - return p - - -class InferenceGraphExporterLinearModelTest(test_utils.TestCase): - - def testExport(self): - """Test basic export.""" - params = model_registry.GetParams('test.LinearModelParams', 'Test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params, subgraph_filter=['default']) - self.assertIn('default', inference_graph.subgraphs) - self.assertEqual(1, len(inference_graph.asset_file_def)) - - def testExportFreezeDefault(self): - """Test exporting frozen graph.""" - params = model_registry.GetParams('test.LinearModelParams', 'Test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params, freeze_defaults=True, subgraph_filter=['default']) - self.assertIn('default', inference_graph.subgraphs) - # Test graphs are well-formed and importable. - with tf.Graph().as_default(): - tf.import_graph_def(inference_graph.graph_def) - - def testTpuBfloat16OverrideExport(self): - """Test that we can export with tf.bfloat16 dtype.""" - params = model_registry.GetParams('test.LinearModelTpuParams', 'Test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params, - subgraph_filter=['tpu'], - device_options=inference_graph_exporter.InferenceDeviceOptions( - device='tpu', - retain_device_placement=True, - var_options='ON_DEVICE', - gen_init_op=True, - dtype_override=tf.bfloat16)) - self.assertIn('tpu', inference_graph.subgraphs) - - def testTpuBfloat16OverrideExportWithEma(self): - """Test that we can export with tf.bfloat16 dtype.""" - params = model_registry.GetParams('test.LinearModelTpuParamsWithEma', - 'Test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params, - subgraph_filter=['tpu'], - device_options=inference_graph_exporter.InferenceDeviceOptions( - device='tpu', - retain_device_placement=True, - var_options='ON_DEVICE', - gen_init_op=True, - dtype_override=tf.bfloat16)) - self.assertIn('tpu', inference_graph.subgraphs) - - def testExportWithRandomSeeds(self): - """Test the effect of setting random seeds on export.""" - params = model_registry.GetParams('test.LinearModelParams', 'Test') - # Default -- use random_seed = None. - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params, subgraph_filter=['default']) - pred = predictor.Predictor(inference_graph) - [no_op_seed_1] = pred.Run(['output'], input=3) - [no_op_seed_2] = pred.Run(['output'], input=3) - self.assertNotEqual(no_op_seed_1, no_op_seed_2) - pred = predictor.Predictor(inference_graph) - [no_op_seed_3] = pred.Run(['output'], input=3) - self.assertNotEqual(no_op_seed_1, no_op_seed_3) - - # Use a fixed random_seed. - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params, subgraph_filter=['default'], random_seed=1234) - pred = predictor.Predictor(inference_graph) - [fixed_op_seed_1] = pred.Run(['output'], input=3) - [fixed_op_seed_2] = pred.Run(['output'], input=3) - self.assertEqual(fixed_op_seed_1, fixed_op_seed_2) - pred = predictor.Predictor(inference_graph) - [fixed_op_seed_3] = pred.Run(['output'], input=3) - self.assertEqual(fixed_op_seed_1, fixed_op_seed_3) - - # A different seed gives different results. - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params, subgraph_filter=['default'], random_seed=1235) - pred = predictor.Predictor(inference_graph) - [fixed_op_seed_4] = pred.Run(['output'], input=3) - self.assertNotEqual(fixed_op_seed_1, fixed_op_seed_4) - - -class GetOutputNamesTest(test_utils.TestCase): - - def _TestGraph(self): - params = model_registry.GetParams('test.LinearModelParams', 'Test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params) - graph = tf.Graph() - with graph.as_default(): - tf.import_graph_def(inference_graph.graph_def, name='') - return graph, inference_graph - - def testDefault(self): - graph, inference_graph = self._TestGraph() - output_op_names = inference_graph_exporter.GetOutputOpNames( - graph, inference_graph) - self.assertEqual(output_op_names, [ - # pyformat: disable - 'inference/add_2', - 'inference/input', - 'testing/b/var', - 'testing/b/var/Initializer/random_normal', - 'testing/b/var/Initializer/random_normal/RandomStandardNormal', - 'testing/b/var/Initializer/random_normal/mean', - 'testing/b/var/Initializer/random_normal/mul', - 'testing/b/var/Initializer/random_normal/shape', - 'testing/b/var/Initializer/random_normal/stddev', - 'testing/w/var', - 'testing/w/var/Initializer/random_normal', - 'testing/w/var/Initializer/random_normal/RandomStandardNormal', - 'testing/w/var/Initializer/random_normal/mean', - 'testing/w/var/Initializer/random_normal/mul', - 'testing/w/var/Initializer/random_normal/shape', - 'testing/w/var/Initializer/random_normal/stddev', - # pyformat: enable - ]) - - def testNoPreserveColocationNodes(self): - graph, inference_graph = self._TestGraph() - output_op_names = inference_graph_exporter.GetOutputOpNames( - graph, inference_graph, preserve_colocation_nodes=False) - self.assertEqual(output_op_names, [ - # pyformat: disable - 'inference/add_2', - 'inference/input', - # pyformat: enable - ]) - - def testPreserveSaverRestoreNodes(self): - graph, inference_graph = self._TestGraph() - output_op_names = inference_graph_exporter.GetOutputOpNames( - graph, - inference_graph, - preserve_colocation_nodes=False, - preserve_saver_restore_nodes=True) - self.assertEqual(output_op_names, [ - # pyformat: disable - 'inference/add_2', - 'inference/input', - 'save/Const', - 'save/restore_all', - # pyformat: enable - ]) - - def testPreserveExtraOps(self): - graph, inference_graph = self._TestGraph() - output_op_names = inference_graph_exporter.GetOutputOpNames( - graph, - inference_graph, - preserve_colocation_nodes=False, - preserve_extra_ops=[ - 'init_all_tables', 'init_all_variables', 'tpu_init_op' - ]) - self.assertEqual(output_op_names, [ - # pyformat: disable - 'inference/add_2', - 'inference/input', - 'init_all_tables', - 'init_all_variables', - # pyformat: enable - ]) - - def testPreserveSaverNodesAndExtraOps(self): - graph, inference_graph = self._TestGraph() - output_op_names = inference_graph_exporter.GetOutputOpNames( - graph, - inference_graph, - preserve_colocation_nodes=False, - preserve_saver_restore_nodes=True, - preserve_extra_ops=[ - 'init_all_tables', 'init_all_variables', 'tpu_init_op' - ]) - self.assertEqual(output_op_names, [ - # pyformat: disable - 'inference/add_2', - 'inference/input', - 'init_all_tables', - 'init_all_variables', - 'save/Const', - 'save/restore_all', - # pyformat: enable - ]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/input_generator_helper.py b/lingvo/core/input_generator_helper.py deleted file mode 100644 index 253232fa..00000000 --- a/lingvo/core/input_generator_helper.py +++ /dev/null @@ -1,122 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Helper functions for model.""" - -import lingvo.compat as tf -from lingvo.core import py_utils - - -def ComputeSplits(batch_size, num_splits): - """Creates a tensor of size num_splits of number of values per split. - - Assigns each split floor(batch_size/num_splits) and round-robins - the remainder (if any) to each split. - - Example:: - - batch_size: [5] - num_splits: 3 - returns: [2, 2, 1] - - Args: - batch_size: tensor of rank 0, size of tensor to be split - num_splits: number of splits to split tensor into - Returns: - tensor of length num_splits containing sizes of each split - """ - values = tf.tile( - tf.div([batch_size], num_splits), - tf.constant( - [num_splits], dtype=tf.int32)) - mods = tf.tile(tf.constant([1]), tf.math.floormod([batch_size], num_splits)) - zeros = tf.tile(tf.constant([0]), - tf.subtract(tf.shape(values), tf.shape(mods))) - mods = tf.concat([mods, zeros], 0) - ret = tf.add(values, mods) - # for some reason TF erases shape information if num_splits is 1 - if num_splits == 1: - ret.set_shape([1]) - return ret - - -def SplitTensors(xs, num_splits): - """Splits tensors in `xs` evenly into num_splits along the 1st dimenion. - - Args: - xs: A tuple of tensors. Each tensor's 1st dimension is the same size. - num_splits: A python integer. - - Returns: - A tuple of lists of tensors, num elements in the tuple = len(xs). - - i-th element in each list corresponds to i-th split of each tensor in xs - along the first dimension of each tensor. - """ - # assert first dim of all tensors in xs is equal - batch_dims = [tf.shape(x)[0] for x in xs] - all_batch_dims = tf.stack(batch_dims) - - all_batch_dims = py_utils.with_dependencies([ - py_utils.assert_equal( - all_batch_dims, - tf.shape(xs[0])[0], - message='first dim of tensors in xs must match'), - py_utils.assert_greater_equal( - tf.shape(xs[0])[0], - num_splits, - message='first dim of tensors in xs must be greater than num_splits') - ], all_batch_dims) - - splits = ComputeSplits(tf.shape(xs[0])[0], num_splits) - # add the above assertion into the compute graph - splits = py_utils.with_dependencies([all_batch_dims], splits) - split_xs = [tf.split(axis=0, num_or_size_splits=splits, value=x) for x in xs] - - return split_xs - - -def SplitDictOfTensors(t_dict, num_splits): - """Splits tensors in `t_dict` evenly into `num_splits` along the 1st dimenion. - - Args: - t_dict: A dictionary of tensors. Each tensor's 1st dimension is the same - size. - num_splits: A python integer. - - Returns: - A list of dictionaries of tensors, num elements in the list = num_splits - - i-th dictionary in the list corresponds to i-th split of each tensor - along the first dimension of each tensor for each key in the original dict. - """ - keys = [] - values = [] - for k, v in sorted(t_dict.items()): - keys.append(k) - values.append(v) - - splits = SplitTensors(tuple(values), num_splits) - - assert all(len(lst) == len(splits[0]) for lst in splits) - - ret_list = [] - for s in range(num_splits): - d = {} - for k in range(len(splits)): - d[keys[k]] = splits[k][s] - ret_list.append(d) - - return ret_list diff --git a/lingvo/core/input_generator_helper_test.py b/lingvo/core/input_generator_helper_test.py deleted file mode 100644 index 2c4d3412..00000000 --- a/lingvo/core/input_generator_helper_test.py +++ /dev/null @@ -1,226 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for input_generator_helper.""" - -import lingvo.compat as tf -from lingvo.core import input_generator_helper -from lingvo.core import test_utils -import numpy as np - - -class InputGeneratorHelperTest(test_utils.TestCase): - - def testComputeSplitsLessThanNumSplits(self): - with self.session(use_gpu=False): - batch_size = tf.constant(2, dtype=tf.int32) - num_splits = 4 - splits = input_generator_helper.ComputeSplits(batch_size, num_splits) - expected = [1, 1, 0, 0] - - actual = self.evaluate(splits) - self.assertAllEqual(actual, expected) - - def testComputeSplitsEven(self): - with self.session(use_gpu=False): - batch_size = tf.constant(32, dtype=tf.int32) - num_splits = 4 - splits = input_generator_helper.ComputeSplits(batch_size, num_splits) - expected = [8, 8, 8, 8] - - actual = self.evaluate(splits) - self.assertAllEqual(actual, expected) - - def testComputeSplitsUnevenOne(self): - with self.session(use_gpu=False): - batch_size = tf.constant(31, dtype=tf.int32) - num_splits = 4 - splits = input_generator_helper.ComputeSplits(batch_size, num_splits) - expected = [8, 8, 8, 7] - - actual = self.evaluate(splits) - self.assertAllEqual(actual, expected) - - def testComputeSplitsUnevenTwo(self): - with self.session(use_gpu=False): - batch_size = tf.constant(30, dtype=tf.int32) - num_splits = 4 - splits = input_generator_helper.ComputeSplits(batch_size, num_splits) - expected = [8, 8, 7, 7] - - actual = self.evaluate(splits) - self.assertAllEqual(actual, expected) - - def testComputeSplitsUnevenThree(self): - with self.session(use_gpu=False): - batch_size = tf.constant(29, dtype=tf.int32) - num_splits = 4 - splits = input_generator_helper.ComputeSplits(batch_size, num_splits) - expected = [8, 7, 7, 7] - - actual = self.evaluate(splits) - self.assertAllEqual(actual, expected) - - def _assertTupleOfListsEqual(self, actual, expected): - self.assertEqual(len(actual), len(expected)) - for i in range(len(actual)): - self.assertEqual(len(actual[i]), len(expected[i])) - for j in range(len(actual[i])): - self.assertAllEqual(actual[i][j], expected[i][j]) - - def _assertListOfDictsEqual(self, actual, expected): - self.assertEqual(len(actual), len(expected)) - for i in range(len(actual)): - self.assertSetEqual(set(actual[i].keys()), set(expected[i].keys())) - for k in actual[i].keys(): - self.assertAllEqual(actual[i][k], expected[i][k]) - - def testSplitTensorsLessThanNumSplits(self): - t1 = tf.constant([[1, 2, 3, 4]]) - t2 = tf.constant([[5, 6, 7, 8]]) - t3 = tf.constant([[13, 14, 15, 16]]) - - tensor_tuple = (t1, t2, t3) - num_splits = 2 - splits = input_generator_helper.SplitTensors(tensor_tuple, num_splits) - - with self.session(use_gpu=False): - with self.assertRaisesRegex( - tf.errors.InvalidArgumentError, - 'first dim of tensors in xs must be greater ' - 'than num_splits'): - self.evaluate(splits) - - def testSplitTensorsOne(self): - with self.session(use_gpu=False): - t1 = tf.constant([[1, 2, 3, 4], [4, 5, 6, 7]]) - t2 = tf.constant([[5, 6, 7, 8], [9, 10, 11, 12]]) - t3 = tf.constant([[13, 14, 15, 16], [14, 15, 16, 17]]) - - tensor_tuple = (t1, t2, t3) - num_splits = 1 - splits = input_generator_helper.SplitTensors(tensor_tuple, num_splits) - expected = ([np.array([[1, 2, 3, 4], [4, 5, 6, 7]])], - [np.array([[5, 6, 7, 8], [9, 10, 11, 12]])], - [np.array([[13, 14, 15, 16], [14, 15, 16, 17]])]) - - actual = self.evaluate(splits) - self._assertTupleOfListsEqual(actual, expected) - - def testSplitTensorsEven(self): - with self.session(use_gpu=False): - t1 = tf.constant([[1, 2, 3, 4], [4, 5, 6, 7]]) - t2 = tf.constant([[5, 6, 7, 8], [9, 10, 11, 12]]) - t3 = tf.constant([[13, 14, 15, 16], [14, 15, 16, 17]]) - - tensor_tuple = (t1, t2, t3) - num_splits = 2 - splits = input_generator_helper.SplitTensors(tensor_tuple, num_splits) - expected = ([np.array([[1, 2, 3, 4]]), np.array([[4, 5, 6, 7]])], - [np.array([[5, 6, 7, 8]]), np.array([[9, 10, 11, 12]])], - [np.array([[13, 14, 15, 16]]), np.array([[14, 15, 16, 17]])]) - - actual = self.evaluate(splits) - self._assertTupleOfListsEqual(actual, expected) - - def testSplitTensorsUneven(self): - with self.session(use_gpu=False): - t1 = tf.constant([[1], [4], [8]]) - t2 = tf.constant([[5], [9], [10]]) - t3 = tf.constant([[13], [14], [11]]) - - tensor_tuple = (t1, t2, t3) - num_splits = 2 - splits = input_generator_helper.SplitTensors(tensor_tuple, num_splits) - expected = ([np.array([[1], [4]]), np.array([[8]])], - [np.array([[5], [9]]), np.array([[10]])], - [np.array([[13], [14]]), np.array([[11]])]) - - actual = self.evaluate(splits) - self._assertTupleOfListsEqual(actual, expected) - - def testSplitTensorsAssert(self): - t1 = tf.constant([[1], [7], [8]]) - t2 = tf.constant([[5], [9], [10]]) - t3 = tf.constant([[13], [14]]) - - tensor_tuple = (t1, t2, t3) - num_splits = 2 - - with self.assertRaisesRegex( - ValueError, r'can\'t split axis of size 2 into pieces of size \[2,1\]'): - _ = input_generator_helper.SplitTensors(tensor_tuple, num_splits) - - def testSplitDictOfTensorsEven(self): - with self.session(use_gpu=False): - t1 = tf.constant([[1], [4], [8], [9]]) - t2 = tf.constant([[5], [9], [10], [12]]) - t3 = tf.constant([[13], [14], [11], [15]]) - - tensor_dict = {'a': t1, 'b': t2, 'c': t3} - num_splits = 2 - splits = input_generator_helper.SplitDictOfTensors(tensor_dict, - num_splits) - expected = [{ - 'a': np.array([[1], [4]]), - 'b': np.array([[5], [9]]), - 'c': np.array([[13], [14]]) - }, { - 'a': np.array([[8], [9]]), - 'b': np.array([[10], [12]]), - 'c': np.array([[11], [15]]) - }] - - actual = self.evaluate(splits) - self._assertListOfDictsEqual(actual, expected) - - def testSplitDictOfTensorsUneven(self): - with self.session(use_gpu=False): - t1 = tf.constant([[1], [4], [8]]) - t2 = tf.constant([[5], [9], [10]]) - t3 = tf.constant([[13], [14], [11]]) - - tensor_dict = {'a': t1, 'b': t2, 'c': t3} - num_splits = 2 - splits = input_generator_helper.SplitDictOfTensors(tensor_dict, - num_splits) - expected = [{ - 'a': np.array([[1], [4]]), - 'b': np.array([[5], [9]]), - 'c': np.array([[13], [14]]) - }, { - 'a': np.array([[8]]), - 'b': np.array([[10]]), - 'c': np.array([[11]]) - }] - - actual = self.evaluate(splits) - self._assertListOfDictsEqual(actual, expected) - - def testSplitDictOfTensorsAssert(self): - t1 = tf.constant([[1], [7], [8]]) - t2 = tf.constant([[5], [9], [10]]) - t3 = tf.constant([[13], [14]]) - - tensor_dict = {'a': t1, 'b': t2, 'c': t3} - num_splits = 2 - - with self.assertRaisesRegex( - ValueError, 'can\'t split axis of size 2 into pieces of size \[2,1\]'): - splits = input_generator_helper.SplitDictOfTensors(tensor_dict, - num_splits) - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/insertion.py b/lingvo/core/insertion.py deleted file mode 100644 index e029ec34..00000000 --- a/lingvo/core/insertion.py +++ /dev/null @@ -1,328 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Insertion-based Framework. - -References: - KERMIT: https://arxiv.org/pdf/1906.01604.pdf - Insertion Transformer: https://arxiv.org/pdf/1902.03249.pdf -""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils - - -def SequenceTrimLastToken(x, x_paddings): - """Trims the last token off of sequence `x`, and set trimmed elements to 0. - - Args: - x: A sequence of tokens of shape [batch_size, x_len_max]. - x_paddings: The paddings of `x`. - - Returns: - A tuple. - - The new sequence, Tensor of shape [batch_size, x_len_max]. - - The new paddings, Tensor of shape [batch_size, x_len_max]. - """ - x_len = tf.reduce_sum(1 - x_paddings, 1) - x_len_max = py_utils.GetShape(x)[1] - x_trimmed_len = tf.maximum(x_len - 1, 0) - x_trimmed_paddings = tf.sequence_mask(x_trimmed_len, x_len_max, - x_paddings.dtype) - x_trimmed = x * tf.cast(x_trimmed_paddings, x.dtype) - return x_trimmed, 1 - x_trimmed_paddings - - -def SequenceAppendToken(x, x_paddings, token, extend=False): - """Appends to sequence `x`. - - Args: - x: A sequence of tokens of shape [batch_size, x_len_max]. - x_paddings: The paddings of `x`. - token: The token to append (of type integer). - extend: Whether to extend `x` along the length dimension, this must be true - for any sequence length in `x` that is `x_len_max` or else an invalid - sequence will be emitted. - - Returns: - A tuple. - - The new sequence, Tensor of shape [batch_size, x_len_max]. - - The new paddings, Tensor of shape [batch_size, x_len_max]. - """ - batch_size = py_utils.GetShape(x)[0] - x_len = tf.cast(tf.round(tf.reduce_sum(1 - x_paddings, 1)), tf.int32) - if extend: - x = tf.pad(x, [[0, 0], [0, 1]]) - # Mask all invalid entries of `x` to 0. - x *= tf.sequence_mask(x_len, py_utils.GetShape(x)[1], x.dtype) - # Append the based on `x_len`. - x += tf.scatter_nd( - tf.stack([tf.range(batch_size), x_len], axis=1), - tf.cast(tf.fill([batch_size], token), x.dtype), py_utils.GetShape(x)) - x_paddings = 1 - tf.sequence_mask(x_len + 1, - py_utils.GetShape(x)[1], x_paddings.dtype) - return x, x_paddings - - -def SequenceConcat(x, x_paddings, y, y_paddings, pad=0): - """Concats sequence `x` with sequence `y`. - - This function is length aware (based off the paddings). - - Args: - x: A sequence of tokens of shape [batch_size, x_len_max]. - x_paddings: The paddings of `x`. - y: A sequence of tokens of shape [batch_size, y_len_max]. - y_paddings: The paddings of `y`. - pad: The token to fill the concatenated sequence (of type integer). - - Returns: - A tuple. - - Concatenation of `x` and `y` of shape - [batch_size, x_len_max + y_len_max]. - - Paddings of the concatenation of shape - [batch_size, x_len_max + y_len_max]. - """ - # Get the length (w/ eos). - x_len = tf.cast(tf.round(tf.reduce_sum(1 - x_paddings, 1)), tf.int32) - y_len = tf.cast(tf.round(tf.reduce_sum(1 - y_paddings, 1)), tf.int32) - - batch_size = py_utils.GetShape(x)[0] - y_len_max = py_utils.GetShape(y)[1] - - # Pad `x` with necessary . - x = tf.concat([x, tf.fill(py_utils.GetShape(y), pad)], 1) - # Replace all with 0. - x = tf.where(tf.not_equal(x, pad), x, tf.fill(py_utils.GetShape(x), 0)) - - # Compute the write indices of `y` in `xy`. - indices = tf.stack([ - tf.tile(tf.expand_dims(tf.range(batch_size), 1), [1, y_len_max]), - (tf.tile(tf.expand_dims(tf.range(y_len_max), 0), [batch_size, 1]) + - tf.expand_dims(x_len, 1)), - ], 2) - - xy = x + tf.scatter_nd(indices, y, py_utils.GetShape(x)) - - # We need to remap all to `pad`. - xy = tf.where( - tf.less( - tf.expand_dims(tf.range(py_utils.GetShape(xy)[1]), 0), - tf.expand_dims(x_len + y_len, 1)), xy, - tf.fill(py_utils.GetShape(xy), pad)) - xy_paddings = 1 - tf.sequence_mask(x_len + y_len, - py_utils.GetShape(xy)[1], x_paddings.dtype) - return xy, xy_paddings - - -class SymbolInsertionLayer(base_layer.BaseLayer): - """Insertion-based framework for symbols. - - This constructs the sampled rollin (observed) canvas, as well as the targets - for an insertion-based model. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'rollin_policy', 'oracle', - 'Rollin policy, should be {oracle, uniform}. Rollin policy is the ' - 'sampling policy from which we draw the canvas. ' - '`oracle` means same as `oracle_policy`.') - p.Define( - 'oracle_policy', 'uniform', - 'Oracle policy, should be one of {uniform}. Oracle policy is the ' - 'target policy from which we select our targets and train our ' - 'models.') - return p - - def __init__(self, params): - super().__init__(params) - - def FProp(self, - theta, - x, - x_paddings=None, - eos_id=1, - force_sample_last_token=True): - """Applies SymbolInsertionLayer. - - We take in a `x`, which represents the groundtruth sequence (i.e., English - sequence). We return a sampled rollin (observed) canvas (i.e., random subset - of the English sequence), as well as the target (indices) for an - insertion-based model (i.e., the targets given the random observed subset). - - Args: - theta: Ignored, this can be None. - x: The symbol ids of shape `[batch_size, time_dim]`. - x_paddings: The paddings (1 or 0) of shape `[batch_size, time_dim]` where - 0 is valid and 1 is invalid. - eos_id: The token id to represent end-of-slot. - force_sample_last_token: Set True to force sample the last token of `x`. - - Returns: - A `NestedMap`. - - canvas: The canvas (based off of the `rollin_policy`) of shape - [batch_size, c_dim]. Note that, `c_dim` <= `time_dim` but need not be - equal. - - canvas_indices: The canvas indices (into `x`). - - canvas_paddings: The paddings of `canvas_indices`. - - target_indices: The target indices of shape [num_targets, 3]. - `num_targets` is the number of total targets in the entire batch. - [:, 0] captures the batch, [:, 1] captures the slot, and [:, 2] - captures the token. Each row [batch, slot, vocab] represents the - indices of the target -- i.e., the batch, slot and vocab combination - of the target. Typical usage of these indices is to tf.gather_nd - the log-probs (from the softmax layer). - - target_weights: The target weights. - - Raises: - ValueError: If invalid params. - """ - p = self.params - - batch_size = py_utils.GetShape(x)[0] - time_dim = py_utils.GetShape(x)[1] - - if x_paddings is None: - x_paddings = tf.zeros([batch_size, time_dim], tf.float32) - - oracle_policy = p.oracle_policy - rollin_policy = ( - oracle_policy if p.rollin_policy == 'oracle' else p.rollin_policy) - - if rollin_policy != 'uniform': - raise ValueError('Unknown or unsupported rollin policy: %s' % - rollin_policy) - if oracle_policy != 'uniform': - raise ValueError('Unknown or unsupported oracle policy: %s' % - oracle_policy) - - x_len = tf.cast(tf.round(tf.reduce_sum(1 - x_paddings, 1)), tf.int32) - - # Compute the desired length per example in the batch. - ratio = tf.random.uniform([batch_size], 0.0, 1.0, seed=p.random_seed) - if force_sample_last_token: - c_len = tf.minimum( - tf.cast(ratio * tf.cast(x_len, tf.float32), tf.int32), x_len - 1) + 1 - else: - c_len = tf.minimum( - tf.cast(ratio * tf.cast(x_len + 1, tf.float32), tf.int32), x_len) - # Compute the maximum length across the batch. - c_len_max = tf.reduce_max(c_len) - - # Grab subset of random valid indices per example. - z_logits = tf.cast( - tf.expand_dims(tf.range(time_dim), 0) >= tf.expand_dims(x_len, 1), - tf.float32) * -1e9 - if force_sample_last_token: - # Force sample the last token -- i.e., as indexed by `x_len - 1`. We can - # accomplish this by add +LARGE_NUMBER to the logits. - z_logits += tf.cast( - tf.equal( - tf.expand_dims(tf.range(time_dim), 0), tf.expand_dims( - x_len - 1, 1)), tf.float32) * 1e9 - # Gumbel-max trick to sample (we only sample valid positions per sample in - # the batch). - z = -tf.math.log(-tf.math.log( - tf.random.uniform([batch_size, time_dim], seed=p.random_seed))) - unused_c_values, c_indices = tf.nn.top_k(z_logits + z, time_dim) - - # Trim everything > c_len_max. - c_indices = c_indices[:, :c_len_max] - - # Invalidate any indices >= c_len, we use the last index as the default - # invalid index. - c_indices = tf.where( - tf.expand_dims(tf.range(c_len_max), 0) < tf.expand_dims(c_len, 1), - c_indices, tf.fill(py_utils.GetShape(c_indices), time_dim - 1)) - - # Materialize the canvas. - c_indices = tf.sort(c_indices) - c = tf.gather_nd( - x, - tf.stack([ - tf.reshape( - tf.tile( - tf.expand_dims(tf.range(batch_size), 1), [1, c_len_max]), - [-1]), - tf.reshape(c_indices, [-1]) - ], 1)) - c = tf.reshape(c, [batch_size, c_len_max]) - - # Compute the paddings. - c_paddings = 1 - tf.sequence_mask(c_len, c_len_max, dtype=x_paddings.dtype) - c *= tf.cast(1 - c_paddings, tf.int32) - - indices = tf.concat([ - tf.reshape( - tf.tile(tf.expand_dims(tf.range(batch_size), 1), [1, c_len_max]), - [batch_size * c_len_max, 1]), - tf.reshape(c_indices, [batch_size * c_len_max, 1]) - ], 1) - x_token_is_observed = tf.scatter_nd( - indices, tf.ones([batch_size * c_len_max], tf.int32), - py_utils.GetShape(x)) - # `x_segments` captures which slot each `x` belongs to (both observed and - # tokens that need to be observed). - x_segments = tf.cumsum(x_token_is_observed, 1, exclusive=True) - - x_token_is_observed = tf.cast(x_token_is_observed, tf.bool) - prev_x_token_is_observed = tf.pad( - x_token_is_observed[:, :-1], [[0, 0], [1, 0]], constant_values=True) - x_token_is_observed = tf.reshape(x_token_is_observed, [-1]) - prev_x_token_is_observed = tf.reshape(prev_x_token_is_observed, [-1]) - x_is_valid = tf.cast(1 - x_paddings, tf.bool) - x_is_valid = tf.reshape(x_is_valid, [-1]) - - # Remap all the observed to , note some of these need a zero weight - # (or else there would be and valid token in the same slot). - target_indices = tf.cast(tf.reshape(x, [-1, 1]), tf.int32) - target_indices = tf.where( - x_token_is_observed, tf.fill(py_utils.GetShape(target_indices), eos_id), - target_indices) - - # TODO(williamchan): We give uniform 1.0 weight, however, math suggests - # we may want to weigh this term by the original sequence length. - target_weights = tf.ones_like(target_indices, tf.float32) - - # We need to set all the weights for which actually have valid tokens - # in the slot to zero. - target_weights = tf.where(x_token_is_observed & ~prev_x_token_is_observed, - tf.zeros_like(target_weights), target_weights) - - # TODO(williamchan): Consider dropping the entries w/ weight zero. - - # Add the batch and slot indices. - target_indices = tf.concat([ - tf.reshape( - tf.tile(tf.expand_dims(tf.range(batch_size), 1), [1, time_dim]), - [batch_size * time_dim, 1]), - tf.reshape(x_segments, [-1, 1]), target_indices - ], 1) - - # Select only the valid indices. The selected valid ones include slots w/ - # . - target_indices = target_indices[x_is_valid] - target_weights = target_weights[x_is_valid] - - return py_utils.NestedMap( - canvas=c, - canvas_indices=c_indices, - canvas_paddings=c_paddings, - target_indices=target_indices, - target_weights=target_weights) diff --git a/lingvo/core/insertion_test.py b/lingvo/core/insertion_test.py deleted file mode 100644 index a2caedc8..00000000 --- a/lingvo/core/insertion_test.py +++ /dev/null @@ -1,346 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Insertion Framework tests.""" - -import lingvo.compat as tf -from lingvo.core import insertion -from lingvo.core import test_utils -import numpy as np - - -class SequenceTest(test_utils.TestCase): - - def testSequenceTrimLastToken(self): - x = np.asarray([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]], - np.int32) - x_paddings = np.asarray( - [[0, 0, 0, 0], [0, 0, 0, 1], [1, 1, 1, 1], [0, 1, 1, 1]], np.float32) - - with self.session(): - x_trimmed, x_trimmed_paddings = insertion.SequenceTrimLastToken( - tf.convert_to_tensor(x), tf.convert_to_tensor(x_paddings)) - - x_trimmed, x_trimmed_paddings = self.evaluate( - [x_trimmed, x_trimmed_paddings]) - - # `x_trimmed_gold` is the same as `x` w/ last token removed. - # `x_trimmed_paddings_gold` is the corresponding paddings. - x_trimmed_gold = np.asarray( - [[1, 2, 3, 0], [1, 2, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], np.int32) - x_trimmed_paddings_gold = np.asarray( - [[0, 0, 0, 1], [0, 0, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], np.float32) - - self.assertAllEqual(x_trimmed, x_trimmed_gold) - self.assertAllEqual(x_trimmed_paddings, x_trimmed_paddings_gold) - - def testSequenceAppendToken(self): - x = np.asarray([[1, 2, 3, 0], [1, 2, 3, 4], [0, 0, 0, 0], [1, 0, 0, 0]], - np.int32) - x_paddings = np.asarray( - [[0, 0, 0, 1], [0, 0, 0, 1], [1, 1, 1, 1], [0, 1, 1, 1]], np.float32) - - with self.session(): - x_appended, x_appended_paddings = insertion.SequenceAppendToken( - tf.convert_to_tensor(x), tf.convert_to_tensor(x_paddings), 10) - - x_appended, x_appended_paddings = self.evaluate([ - tf.convert_to_tensor(x_appended), - tf.convert_to_tensor(x_appended_paddings) - ]) - - # `x_appended_gold` is the same as `x` w/ token `10` appended. - # `x_appended_paddings_gold` is the corresponding paddings. - x_appended_gold = np.asarray( - [[1, 2, 3, 10], [1, 2, 3, 10], [10, 0, 0, 0], [1, 10, 0, 0]], - np.int32) - x_appended_paddings_gold = np.asarray( - [[0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 1, 1], [0, 0, 1, 1]], np.float32) - - self.assertAllEqual(x_appended, x_appended_gold) - self.assertAllEqual(x_appended_paddings, x_appended_paddings_gold) - - def testSequenceAppendTokenExtend(self): - x = np.asarray([[1, 2, 3, 0], [1, 2, 3, 4], [0, 0, 0, 0], [1, 0, 0, 0]], - np.int32) - x_paddings = np.asarray( - [[0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 1, 1], [0, 1, 1, 1]], np.int32) - - with self.session(): - x_appended, x_appended_paddings = insertion.SequenceAppendToken( - tf.convert_to_tensor(x), tf.convert_to_tensor(x_paddings), 10, True) - - x_appended, x_appended_paddings = self.evaluate( - [x_appended, x_appended_paddings]) - - # `x_appended_gold` is the same as `x` w/ token `10` appended, we also - # test for the condition of extend=True which requires +1 dim in the - # time dimension. - # `x_appended_paddings_gold` is the corresponding paddings. - x_appended_gold = np.asarray([[1, 2, 3, 10, 0], [1, 2, 3, 4, 10], - [10, 0, 0, 0, 0], [1, 10, 0, 0, 0]], - np.int32) - x_appended_paddings_gold = np.asarray( - [[0, 0, 0, 0, 1], [0, 0, 0, 0, 0], [0, 1, 1, 1, 1], [0, 0, 1, 1, 1]], - np.int32) - - self.assertAllEqual(x_appended, x_appended_gold) - self.assertAllEqual(x_appended_paddings, x_appended_paddings_gold) - - def testSequenceConcat(self): - x = np.asarray([[1, 2, 3, 0], [1, 2, 3, 4], [0, 0, 0, 0], [1, 0, 0, 0]], - np.int32) - x_paddings = np.asarray( - [[0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 1, 1], [0, 1, 1, 1]], np.float32) - - y = np.asarray( - [[10, 20, 30, 0], [10, 20, 30, 40], [0, 0, 0, 0], [10, 0, 0, 0]], - np.int32) - y_paddings = np.asarray( - [[0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 1, 1], [0, 1, 1, 1]], np.float32) - - with self.session(): - xy, xy_paddings = insertion.SequenceConcat( - tf.convert_to_tensor(x), tf.convert_to_tensor(x_paddings), - tf.convert_to_tensor(y), tf.convert_to_tensor(y_paddings), 999) - - xy, xy_paddings = self.evaluate( - [tf.convert_to_tensor(xy), - tf.convert_to_tensor(xy_paddings)]) - - # `xy_gold` is `x` and `y` concatenated. - # `xy_paddings_gold` is the corresponding paddings. - xy_gold = np.asarray( - [[1, 2, 3, 10, 20, 30, 999, 999], [1, 2, 3, 4, 10, 20, 30, 40], - [999, 999, 999, 999, 999, 999, 999, 999], - [1, 10, 999, 999, 999, 999, 999, 999]], np.int32) - xy_paddings_gold = np.asarray( - [[0, 0, 0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 1]], np.float32) - - self.assertAllEqual(xy, xy_gold) - self.assertAllEqual(xy_paddings, xy_paddings_gold) - - -class SymbolInsertionLayerTest(test_utils.TestCase): - - def testGetValidCanvasUnderUniformRollinPolicy(self): - with self.session(use_gpu=True): - params = insertion.SymbolInsertionLayer.Params() - params.name = 'insertion' - params.rollin_policy = 'oracle' - params.oracle_policy = 'uniform' - - insertion_layer = insertion.SymbolInsertionLayer(params) - - batch_size = 4 - time_dim = 10 - - inputs = tf.tile(tf.expand_dims(tf.range(time_dim), 0), [batch_size, 1]) - spec = insertion_layer.FProp(None, inputs, force_sample_last_token=False) - - canvas, canvas_indices, canvas_paddings = self.evaluate( - [spec.canvas, spec.canvas_indices, spec.canvas_paddings]) - - for b in range(batch_size): - length = np.sum(1 - canvas_paddings[b, :]).astype(np.int32) - self.assertAllEqual(canvas[b, :length], canvas_indices[b, :length]) - # Test the invalid slots. - self.assertAllEqual(canvas[b, length:], - [0] * (canvas.shape[1] - length)) - self.assertAllEqual(canvas_indices[b, length:], - [time_dim - 1] * (canvas.shape[1] - length)) - - def testMaxCanvasSizeUnderUniformRollinPolicy(self): - """Tests for valid canvas size.""" - with self.session(use_gpu=True): - params = insertion.SymbolInsertionLayer.Params() - params.name = 'insertion' - params.rollin_policy = 'oracle' - params.oracle_policy = 'uniform' - - insertion_layer = insertion.SymbolInsertionLayer(params) - - batch_size = 4 - time_dim = 10 - - inputs = tf.tile(tf.expand_dims(tf.range(time_dim), 0), [batch_size, 1]) - inputs_len = tf.random.uniform([batch_size], 0, time_dim, tf.int32) - paddings = 1 - tf.sequence_mask(inputs_len, time_dim, tf.int32) - spec = insertion_layer.FProp( - None, inputs, paddings, force_sample_last_token=False) - - canvas_with_max_length = False - for _ in range(1000): - canvas_max_len, canvas, canvas_paddings = self.evaluate( - [inputs_len, spec.canvas, spec.canvas_paddings]) - - for b in range(batch_size): - max_len = canvas_max_len[b] - length = np.sum(1 - canvas_paddings[b, :]).astype(np.int32) - canvas_with_max_length |= length == max_len - self.assertLessEqual(length, max_len) - # Invalid entries of canvas should be 0. - self.assertAllEqual(canvas[b, length:], - [0] * (canvas.shape[1] - length)) - - # With high probability, there should be at least one canvas that is - # of the same size as the maximum canvas size. - self.assertEqual(canvas_with_max_length, True) - - def testContiguousCanvasUnderUniformRollinPolicy(self): - """Tests for valid canvas size.""" - with self.session(use_gpu=True): - params = insertion.SymbolInsertionLayer.Params() - params.name = 'insertion' - params.rollin_policy = 'oracle' - params.oracle_policy = 'uniform' - - insertion_layer = insertion.SymbolInsertionLayer(params) - - batch_size = 4 - time_dim = 10 - - inputs = tf.tile( - tf.expand_dims(tf.range(time_dim), 0) + 100, [batch_size, 1]) - inputs_len = tf.random.uniform([batch_size], 0, time_dim, tf.int32) - paddings = 1 - tf.sequence_mask(inputs_len, time_dim, tf.int32) - spec = insertion_layer.FProp( - None, inputs, paddings, force_sample_last_token=False) - - for _ in range(1000): - canvas, canvas_paddings = self.evaluate( - [spec.canvas, spec.canvas_paddings]) - - for b in range(batch_size): - length = np.sum(1 - canvas_paddings[b, :]).astype(np.int32) - # Check for valid part of the canvas and padding. - for l in range(length): - self.assertEqual(canvas_paddings[b, l], 0) - self.assertNotEqual(canvas[b, l], 0) - # Check for invalid part of the canvas and padding. - for l in range(length, canvas.shape[1]): - self.assertEqual(canvas_paddings[b, l], 1) - self.assertEqual(canvas[b, l], 0) - - def testGetValidCanvasAndTargetsUnderUniformOraclePolicyWithoutForcedSample( - self): - """Tests for canvas+targets under uniform (rollin+oracle) policy.""" - with self.session(use_gpu=True): - params = insertion.SymbolInsertionLayer.Params() - params.name = 'insertion' - params.rollin_policy = 'oracle' - params.oracle_policy = 'uniform' - params.random_seed = 12345 - - insertion_layer = params.Instantiate() - - x = np.asarray( - [[10, 11, 12, 13, 14, 15, 16], [10, 11, 12, 13, 14, 15, 16], - [10, 0, 0, 0, 0, 0, 0], [10, 11, 12, 13, 14, 15, 0]], np.int32) - x_paddings = np.asarray([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 1]], - np.float32) - - spec = insertion_layer.FProp( - None, - tf.convert_to_tensor(x), - tf.convert_to_tensor(x_paddings), - force_sample_last_token=False) - - (canvas, canvas_paddings, target_indices, - target_weights) = self.evaluate([ - spec.canvas, spec.canvas_paddings, spec.target_indices, - spec.target_weights - ]) - - canvas_gold = np.asarray([[10, 12, 13, 15, 16], [14, 0, 0, 0, 0], - [10, 0, 0, 0, 0], [11, 12, 13, 0, 0]], np.int32) - canvas_paddings_gold = np.asarray( - [[0., 0., 0., 0., 0.], [0., 1., 1., 1., 1.], [0., 1., 1., 1., 1.], - [0., 0., 0., 1., 1.]], np.float32) - target_indices_gold = np.asarray( - [[0, 0, 1], [0, 1, 11], [0, 1, 1], [0, 2, 1], [0, 3, 14], [0, 3, 1], - [0, 4, 1], [1, 0, 10], [1, 0, 11], [1, 0, 12], [1, 0, 13], [1, 0, 1], - [1, 1, 15], [1, 1, 1], [2, 0, 1], [3, 0, 10], [3, 0, 1], [3, 1, 1], - [3, 2, 1], [3, 3, 14], [3, 3, 15]], np.int32) - target_weights_gold = np.asarray([1, 1, 0, 1, 1, 0, 1] + - [1, 1, 1, 1, 0, 1, 0] + [1] + - [1, 0, 1, 1, 1, 1], np.float32) - target_weights_gold = np.reshape(target_weights_gold, - [target_weights_gold.shape[0], 1]) - - self.assertAllEqual(canvas, canvas_gold) - self.assertAllEqual(canvas_paddings, canvas_paddings_gold) - self.assertAllEqual(target_indices, target_indices_gold) - self.assertAllEqual(target_weights, target_weights_gold) - - def testGetValidCanvasAndTargetsUnderUniformOraclePolicyForcedSample(self): - """Tests for canvas+targets under uniform (rollin+oracle) policy.""" - with self.session(use_gpu=True): - params = insertion.SymbolInsertionLayer.Params() - params.name = 'insertion' - params.rollin_policy = 'oracle' - params.oracle_policy = 'uniform' - params.random_seed = 12345 - - insertion_layer = insertion.SymbolInsertionLayer(params) - - x = np.asarray( - [[10, 11, 12, 13, 14, 15, 16, 1], [10, 11, 12, 13, 14, 15, 16, 1], - [10, 1, 0, 0, 0, 0, 0, 0], [10, 11, 12, 13, 14, 15, 1, 0]], np.int32) - x_paddings = np.asarray( - [[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1]], np.float32) - - spec = insertion_layer.FProp(None, tf.convert_to_tensor(x), - tf.convert_to_tensor(x_paddings)) - - (canvas, canvas_indices, canvas_paddings, target_indices, - target_weights) = self.evaluate([ - spec.canvas, spec.canvas_indices, spec.canvas_paddings, - spec.target_indices, spec.target_weights - ]) - - canvas_gold = np.asarray([[10, 12, 13, 15, 16, 1], [13, 1, 0, 0, 0, 0], - [10, 1, 0, 0, 0, 0], [10, 12, 14, 1, 0, 0]], - np.int32) - canvas_indices_gold = np.asarray([[0, 2, 3, 5, 6, 7], [3, 7, 7, 7, 7, 7], - [0, 1, 7, 7, 7, 7], [0, 2, 4, 6, 7, 7]], - np.int32) - canvas_paddings_gold = np.asarray( - [[0., 0., 0., 0., 0., 0.], [0., 0., 1., 1., 1., 1.], - [0., 0., 1., 1., 1., 1.], [0., 0., 0., 0., 1., 1.]], np.float32) - target_indices_gold = np.asarray( - [[0, 0, 1], [0, 1, 11], [0, 1, 1], [0, 2, 1], [0, 3, 14], [0, 3, 1], - [0, 4, 1], [0, 5, 1], [1, 0, 10], [1, 0, 11], [1, 0, 12], [1, 0, 1], - [1, 1, 14], [1, 1, 15], [1, 1, 16], [1, 1, 1], [2, 0, 1], [2, 1, 1], - [3, 0, 1], [3, 1, 11], [3, 1, 1], [3, 2, 13], [3, 2, 1], [3, 3, 15], - [3, 3, 1]], np.int32) - target_weights_gold = np.asarray([1, 1, 0, 1, 1, 0, 1, 1] + - [1, 1, 1, 0, 1, 1, 1, 0] + [1, 1] + - [1, 1, 0, 1, 0, 1, 0], np.float32) - target_weights_gold = np.reshape(target_weights_gold, - [target_weights_gold.shape[0], 1]) - - self.assertAllEqual(canvas, canvas_gold) - self.assertAllEqual(canvas_indices, canvas_indices_gold) - self.assertAllEqual(canvas_paddings, canvas_paddings_gold) - self.assertAllEqual(target_indices, target_indices_gold) - self.assertAllEqual(target_weights, target_weights_gold) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/inspect_utils.py b/lingvo/core/inspect_utils.py deleted file mode 100644 index 11d1da32..00000000 --- a/lingvo/core/inspect_utils.py +++ /dev/null @@ -1,174 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Utilities to bind function signatures to params.""" - -import inspect - -# Groups of parameter kinds. -DEFINABLE_PARAMETER_KINDS = (inspect.Parameter.POSITIONAL_OR_KEYWORD, - inspect.Parameter.KEYWORD_ONLY) -IGNORABLE_PARAMETER_KINDS = (inspect.Parameter.VAR_POSITIONAL, - inspect.Parameter.VAR_KEYWORD) - - -def _IsDefinableParameter(parameter): - """Checks if the parameter can be defined in `Params`. - - Args: - parameter: inspect.Parameter to be checked. - - Returns: - True if the `parameter`'s kind is either `POSITIONAL_OR_KEYWORD` or - `KEYWORD_ONLY` which are definable in `Params`, False if it is either - `VAR_POSITIONAL` or `VAR_KEYWORD` which are ignorable. - - Raises: - ValueError: The `parameter` has another kind which are possibly not - supported, e.g., `POSITIONAL_ONLY` parameters. - """ - if parameter.kind in DEFINABLE_PARAMETER_KINDS: - return True - elif parameter.kind in IGNORABLE_PARAMETER_KINDS: - return False - else: - raise ValueError('Unsupported parameter signature `%s` with kind `%s`.' % - (parameter.name, parameter.kind)) - - -def _ExtractParameters(func, ignore, bound): - """Extracts parameters of func which can be defined in Params. - - Args: - func: A callable to be analysed. - ignore: A collection of parameter names in `func` to be ignored. - bound: Whether the `func` is used as a bound function (an object method or a - class method) or not. If True, the first parameter of the `func` will be - ignored. - - Returns: - A generator of `inspect.Parameter` representing definable parameters. - """ - ignore = set(ignore if ignore is not None else ()) - - # Obtains parameter signatures. - parameters = tuple(inspect.signature(func).parameters.values()) - # Ignores the bound parameter: typically `self` or `cls`. - parameters = parameters[(1 if bound else 0):] - # Filters unnecessary parameters. - parameters = filter(_IsDefinableParameter, parameters) - parameters = (p for p in parameters if p.name not in ignore) - - return parameters - - -def DefineParams(func, params, ignore=None, bound=False): - """Defines params for each parameter of given callable. - - This allows you to define the parameters necessary to call a callable without - having to type the Define statements yourself. - Default values for the function parameters will be copied into the params - object as well. - - To use this function for analysing a class instantiation, users usually can - pass the class type as the `func`. If it does not work correctly, pass the - `__init__` method of the class with `bound=True` instead. - - Args: - func: A callable to be analysed. Parameters of this function will be defined - in `params`. This function expects that `func` maintains the explicit - signature of its parameters. Implicit parameters that are stored in - `*args` or `**kwargs` could not be analysed correctly. - params: A `Params` object to be updated. New parameters will be defined - here. - ignore: A collection of parameter names in `func` to be ignored from - defining corresponding entries in `params`. - bound: Whether `func` will be used as a bound function (an object method or - a class method) or not. If True, the first parameter of `func` (typically - `self` or `cls`) will be ignored. - """ - for p in _ExtractParameters(func, ignore, bound): - default = p.default - if default is inspect.Parameter.empty: - # TODO(oday): If Params supported required fields, change this behavior to - # set the "required" flag. - default = None - - params.Define(p.name, default, 'Function parameter.') - - -def _MakeArgs(func, params, **kwargs): - """Generates an argument list to call func. - - Args: - func: A callable to be called. - params: A Params object containing arguments for `func`. - **kwargs: Argument/value pairs that should override params. - - Returns: - A dict containing function parameters to be used as `**kwargs` of `func`. - """ - out_kwargs = {} - - # Here we set bound=False so the `func` is expected to be already a bound - # function. - for p in _ExtractParameters(func, ignore=None, bound=False): - key = p.name - - # We will collect only args defined in at least either `kwargs` or `params`. - # Args in `func`'s signature but in neither both will be skipped. - - if key in kwargs: - # Anything in kwargs overrides parameters. - out_kwargs[key] = kwargs[key] - elif key in params: - value = params.Get(key) - # If the value in params is the same as the function default, we do not - # set the arg so that we will let the function signature fill in this - # parameter by itself. - if value != p.default: - out_kwargs[key] = value - - return out_kwargs - - -def CallWithParams(func, params, **kwargs): - """Call a function or method with a Params object. - - Args: - func: A callable to be called. - params: A Params object containing parameters of `func`. - **kwargs: Argument/value pairs that should override `params`. - - Returns: - The return values from func. - """ - return func(**_MakeArgs(func, params, **kwargs)) - - -# TODO(oday): Remove this function and replace it with CallWithParams when the -# bug on the initializer of Keras layers has been resolved. -def ConstructWithParams(cls, params, **kwargs): - """Constructs a class object with a Params object. - - Args: - cls: A class type to be constructed. - params: A Params object containing parameters of `cls.__init__`. - **kwargs: Argument/value pairs that should override `params`. - - Returns: - The constructed object. - """ - return cls(**_MakeArgs(cls.__init__, params, **kwargs)) diff --git a/lingvo/core/inspect_utils_test.py b/lingvo/core/inspect_utils_test.py deleted file mode 100644 index 77a70541..00000000 --- a/lingvo/core/inspect_utils_test.py +++ /dev/null @@ -1,184 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for inspect_utils.""" - -import lingvo.compat as tf -from lingvo.core import hyperparams -from lingvo.core import inspect_utils -from lingvo.core import test_utils - - -class InspectUtilsTest(test_utils.TestCase): - - def testBareFunction(self): - - def my_function(a, b): - return a + 1, b + 2 - - params = hyperparams.Params() - inspect_utils.DefineParams(my_function, params) - self.assertIn('a', params) - self.assertIn('b', params) - self.assertIsNone(params.a) - self.assertIsNone(params.b) - - params.a = 5 - params.b = 6 - a1, b1 = inspect_utils.CallWithParams(my_function, params) - self.assertEqual(a1, 5 + 1) - self.assertEqual(b1, 6 + 2) - - def testFunctionWithDefaults(self): - - def my_function(a, b=3): - return a + 1, b + 2 - - params = hyperparams.Params() - inspect_utils.DefineParams(my_function, params) - self.assertIn('a', params) - self.assertIn('b', params) - self.assertIsNone(params.a) - self.assertEqual(params.b, 3) - - params.a = 6 - a1, b1 = inspect_utils.CallWithParams(my_function, params) - self.assertEqual(a1, 6 + 1) - self.assertEqual(b1, 3 + 2) - - def testFunctionWithIgnore(self): - - def my_function(a, b=3, c=4): - return a + 1, b + 2, c + 3 - - params = hyperparams.Params() - inspect_utils.DefineParams(my_function, params, ignore=['c']) - self.assertIn('a', params) - self.assertIn('b', params) - self.assertNotIn('c', params) - self.assertIsNone(params.a) - self.assertEqual(params.b, 3) - - params.a = 6 - a1, b1, c1 = inspect_utils.CallWithParams(my_function, params, c=9) - self.assertEqual(a1, 6 + 1) - self.assertEqual(b1, 3 + 2) - self.assertEqual(c1, 9 + 3) - - def testFunctionWithOverrides(self): - - def my_function(a, b=3): - return a + 1, b + 2 - - params = hyperparams.Params() - inspect_utils.DefineParams(my_function, params) - self.assertIn('a', params) - self.assertIn('b', params) - self.assertIsNone(params.a) - self.assertEqual(params.b, 3) - - params.a = 6 - a1, b1 = inspect_utils.CallWithParams(my_function, params, a=7) - self.assertEqual(a1, 7 + 1) - self.assertEqual(b1, 3 + 2) - - def testFunctionWithVarArgs(self): - - def my_function(a, *args, b=3, **kwargs): - del args - del kwargs - return a + 1, b + 2 - - params = hyperparams.Params() - inspect_utils.DefineParams(my_function, params) - self.assertIn('a', params) - self.assertNotIn('args', params) - self.assertIn('b', params) - self.assertNotIn('kwargs', params) - self.assertIsNone(params.a) - self.assertEqual(params.b, 3) - - params.a = 6 - a1, b1 = inspect_utils.CallWithParams(my_function, params) - self.assertEqual(a1, 6 + 1) - self.assertEqual(b1, 3 + 2) - - def testClassInit(self): - - class MyClass: - - def __init__(self, a, b=3): - self.a = a - self.b = b - - params = hyperparams.Params() - inspect_utils.DefineParams(MyClass, params) - self.assertIn('a', params) - self.assertIn('b', params) - self.assertIsNone(params.a) - self.assertEqual(params.b, 3) - - params.a = 9 - params.b = 5 - obj = inspect_utils.CallWithParams(MyClass, params) - self.assertEqual(obj.a, 9) - self.assertEqual(obj.b, 5) - - # TODO(oday): Remove this test when the bug on Keras has been resolved. - def testClassInit2(self): - - class MyClass: - - def __init__(self, a, b=3): - self.a = a - self.b = b - - params = hyperparams.Params() - inspect_utils.DefineParams(MyClass.__init__, params, bound=True) - self.assertNotIn('self', params) - self.assertIn('a', params) - self.assertIn('b', params) - self.assertIsNone(params.a) - self.assertEqual(params.b, 3) - - params.a = 9 - params.b = 5 - obj = inspect_utils.ConstructWithParams(MyClass, params) - self.assertEqual(obj.a, 9) - self.assertEqual(obj.b, 5) - - def testMethod(self): - - class MyClass: - - def __init__(self): - self._s = 'a/b' - - def split(self, sep): - return self._s.split(sep) - - params = hyperparams.Params() - inspect_utils.DefineParams(MyClass.split, params, bound=True) - self.assertNotIn('self', params) - self.assertIn('sep', params) - self.assertIsNone(params.sep) - - params.sep = '/' - parts = inspect_utils.CallWithParams(MyClass().split, params) - self.assertEqual(['a', 'b'], parts) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/layers.py b/lingvo/core/layers.py deleted file mode 100644 index 78fbbe41..00000000 --- a/lingvo/core/layers.py +++ /dev/null @@ -1,4978 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Common layers.""" - -import copy -import math -import numbers -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import bn_layers -from lingvo.core import builder_layers -from lingvo.core import computation_cost -from lingvo.core import conv_layers_with_time_padding -from lingvo.core import pruning_utils -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import recurrent -from lingvo.core import schedule -from lingvo.core import summary_utils -from lingvo.core import symbolic -from lingvo.core import tshape -import numpy as np -import sympy - -# pylint:disable=g-direct-tensorflow-import -from tensorflow.python.ops import inplace_ops -# pylint:enable=g-direct-tensorflow-import - - -class DeconvLayer(base_layer.BaseLayer): - """Deconv (transposed conv2d) layer. - - DeconvLayer is different from ConvTransposeLayer in that - DeconvLayer does not support padding and biasing. Hence, - it's simpler and more basic than ConvTransposeLayer. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'filter_shape', (0, 0, 0, 0), - 'Filter shape. Must be a sequence of length 4. Elements are in' - ' the order of height, width, out_channel, in_channel.') - p.Define( - 'filter_stride', (0, 0), - 'Filter stride to use. Must be a pair of ints. The first int' - ' specifies the stride on the height dimension. The second int' - ' specifies the stride on the width dimension.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert len(p.filter_shape) == 4 - assert len(p.filter_stride) == 2 - assert all(x > 0 for x in p.filter_shape) - assert all(x > 0 for x in p.filter_stride) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w_pc = py_utils.WeightParams( - shape=p.filter_shape, - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w', w_pc) - - def OutShape(self, in_shape): - """Compute the output shape given the input shape.""" - p = self.params - t_stride = p.filter_stride[0] - f_stride = p.filter_stride[1] - return tf.stack([ - in_shape[0], in_shape[1] * t_stride, in_shape[2] * f_stride, - p.filter_shape[2] - ]) - - def _ApplyConv(self, theta, inputs): - p = self.params - w = theta.w - strides = [1, p.filter_stride[0], p.filter_stride[1], 1] - # TODO(miachen): remove casting once tf.nn.conv2d supports tf.float64. - assert inputs.dtype == w.dtype - dtype = inputs.dtype - if dtype != tf.float32: - inputs = tf.cast(inputs, tf.float32) - w = tf.cast(w, tf.float32) - # TODO(zhifengc): Try some better way to do Deconv. Search for - # "resize-convolution". - out = tf.nn.conv2d_transpose( - inputs, - w, - output_shape=self.OutShape(tf.shape(inputs)), - strides=strides, - padding='SAME') - if dtype != tf.float32: - out = tf.cast(out, dtype) - return py_utils.HasShape(out, [-1, -1, -1, p.filter_shape[2]]) - - def FProp(self, theta, inputs): - """Apply deconvolution to inputs. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - inputs: The inputs tensor. It is expected to be of shape [batch, height, - width, channel]. - - Returns: - outputs. outputs is expected to have shape [batch, height * height_stride, - width * width_stride, out_channel]. - """ - p = self.params - inputs = py_utils.HasShape(inputs, [-1, -1, -1, p.filter_shape[3]]) - return self._ApplyConv(theta, inputs) - - -def Gelu(input_tensor): - """Gaussian Error Linear Unit. - - This is a smoother version of the RELU. - Original paper: https://arxiv.org/abs/1606.08415 - - Args: - input_tensor: float Tensor to perform activation. - - Returns: - `input_tensor` with the GELU activation applied. - """ - cdf = 0.5 * (1.0 + tf.math.erf( - input_tensor / tf.cast(tf.sqrt(2.0), input_tensor.dtype))) - return input_tensor * cdf - - -# Supported activation functions. -_ACTIVATIONS = { - 'RELU': tf.nn.relu, - 'RELU6': tf.nn.relu6, - 'SIGMOID': tf.sigmoid, - 'TANH': tf.tanh, - 'GELU': Gelu, - 'SWISH': tf.nn.swish, - 'SOFTPLUS': tf.nn.softplus, -} - -_ACTIVATIONS_FLOPS = { - 'NONE': 0, - 'RELU': 1, - 'RELU6': 1, - # 1 / (1 + exp(-x)) - 'SIGMOID': 4, # neg, exp, add, div - # (exp(2*x) - 1) / (exp(2*x) - 1) - 'TANH': 7, # mul, exp, sub, mul, exp, add, div - # Gelu is tough, let's assume it is approximated as x * sigmoid(1.702 * x). - 'GELU': 6, # mul, sigmoid, mul - # x * sigmoid(x) - 'SWISH': 5, # sigmoid, mul - # ln(1+exp(x)) - 'SOFTPLUS': 3, # exp, add, ln -} - -# A subset of activation functions are supported by TFLite as fused activation -# functions with a preceding matmul or conv. If this is the case, then they -# require special treatment for quantization. -_TFLITE_FUSED_ACTIVATION_NAMES = ( - 'RELU', - 'RELU6', -) - -LOG_SCALE_CLAMP_BOUND = 20.0 - - -class IdentityLayer(base_layer.BaseLayer): - """Identity layer, adds name and propagates its input.""" - - def FProp(self, theta, inputs, *args): - """Identity mapping. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The input tensor or the input NestedMap. - *args: Arguments to be ignored. - - Returns: - Tensor with the same shape and type of inputs. - """ - p = self.params - with tf.name_scope(p.name): - return tf.nest.map_structure(tf.identity, inputs) - - @classmethod - def FPropMeta(cls, p, inputs, *args): - py_utils.CheckShapes((inputs,)) - return py_utils.NestedMap(flops=0, out_shapes=(inputs,)) - - -# TODO(yonghui/jonathanasdf): Remove the forwarded links. -_ComputeConvOutputShape = conv_layers_with_time_padding.ComputeConvOutputShape -_ComputeConvOutputPadding = ( - conv_layers_with_time_padding.ComputeConvOutputPadding) -BatchNormLayer = bn_layers.BatchNormLayer -BatchNormLayerNoPadding = bn_layers.BatchNormLayerNoPadding -AddingAccumulator = bn_layers.AddingAccumulator - - -class BaseConv2DLayer(quant_utils.QuantizableLayer): - """Base class for 2D convolution layers. - - Has support for optional batch-normalization, activation and sequence - padding. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'filter_shape', (0, 0, 0, 0), - 'Filter shape. Must be a sequence of length 4. Elements are in' - ' the order of height (time), width (frequency), in_channel,' - ' out_channel. When causal_convolution is True, filter_shape[1]' - ' is the actual number of trained weights in the time dimension' - ' of the kernel.') - p.Define( - 'filter_stride', (0, 0), - 'Filter stride to use. Must be a pair of ints. The first int' - ' specifies the stride on the time dimension. The second int' - ' specifies the stride on the frequency dimension.') - p.Define( - 'dilation_rate', (1, 1), - 'If > 1, dilation rate for atrous convolution. ' - 'Must be a pair of ints. ' - 'The first int specifies the dilation rate on the time dimension. ' - 'The second int specifies the dilation rate on the frequency ' - 'dimension. ' - 'If any value of dilation_rate is > 1, then all values of strides ' - 'must be 1.') - p.Define( - 'activation', 'RELU', - 'Activation function to use. Options are RELU, RELU6, SIGMOID, ' - 'TANH, NONE.') - p.Define('bias', False, 'Whether or not to apply a bias before activation.') - p.Define('batch_norm', True, 'Whether or not to apply batch norm.') - p.Define( - 'bn_decay', 0.999, - 'Decay in updating the mean and variance moving average used in' - ' batch normalization.') - p.Define( - 'bn_fold_weights', None, - 'Fold the batch norm parameters into the convolution weights at ' - 'eval/inference time as per https://arxiv.org/pdf/1712.05877.pdf. ' - 'Requires that batch_norm be True and is incompatible with some other ' - 'parameters (conv_last=True).') - p.Define( - 'causal_convolution', False, - 'If true, conv layer output only depends on time steps in' - ' the past.') - p.Define( - 'conv_last', False, - 'If true, apply the convolution transformation as the last step, ' - 'i.e., first apply batch normalization on the input, followed ' - 'by activation, and finally the convolution. ' - 'Otherwise, apply convolution first, followed by batch ' - 'normalization and activation. Not compatible with bn_fold_weights ' - 'or quantization.') - p.Define( - 'weight_norm', False, - 'If true, apply weight normalization to weights as proposed by' - ' Salimans and Kingma, 2016: https://arxiv.org/abs/1602.07868') - p.Define( - 'disable_activation_quantization', False, - 'Disables the quantization tracking/clamping for the output ' - 'activation. This is most often used in conjunction with a concat ' - 'layer which needs to have a merged set of statistics.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert len(p.filter_shape) == 4 - assert len(p.filter_stride) == 2 - assert len(p.dilation_rate) == 2 - assert all(x > 0 for x in p.filter_stride) - assert all(x > 0 for x in p.dilation_rate) - if any(x > 1 for x in p.dilation_rate): - assert all(x == 1 for x in p.filter_stride) - # Bias is not needed with batch_norm=True. - if p.batch_norm: - assert not p.bias - assert (p.activation == 'NONE' or p.activation in _ACTIVATIONS) - - if p.batch_norm: - # batch normalization dimension is number of input channels - # (filter_shape[2]) if we apply batch_norm on input and convolution - # in the end, number of output channels otherwise. - bn_dim = p.filter_shape[2] if p.conv_last else self.output_channels - bn_params = BatchNormLayer.Params().Set( - dim=bn_dim, decay=p.bn_decay, name=p.name, params_init=p.params_init) - self.CreateChild('bn', bn_params) - - if self._is_bn_folded: - assert p.batch_norm, 'bn_fold_weights requires batch_norm = True' - assert not p.conv_last, 'bn_fold_weights requires conv_last = False' - - # TODO(yonghui): implement the variational noise logic. - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w_pc = py_utils.WeightParams( - shape=p.filter_shape, - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w', w_pc) - if p.bias: - self.CreateVariable( - 'b', - py_utils.WeightParams( - shape=[self.output_channels], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - if p.weight_norm: - self.CreateVariable( - 'g', - py_utils.WeightParams( - shape=self.filter_output_shape, - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - - if not p.disable_activation_quantization: - self.TrackQTensor('activation') - if (p.activation not in _TFLITE_FUSED_ACTIVATION_NAMES and - p.activation != 'NONE'): - self.TrackQTensor('pre_activation') - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - if self.params.batch_norm: - self.bn.InstantiateVariables() - super()._CreateChildrenVariables() - - @property - def output_channels(self): - """The number of output channels for this conv layer.""" - # Normal convolution filter shape is [..., out_channels]. - p = self.params - return p.filter_shape[-1] - - @property - def filter_output_shape(self): - """Final dims of the filter corresponding to the output channels. - - Returns: - A one (standard conv) or two (depthwise conv) element shape representing - the final dimensions of the filter weights that are output channel - specific for this layer. This shape is needed for any arithmetic that - needs to convert between a linear list of filter weights and the - arrangement in the actual filter. - """ - # Standard convolution has all output channels in the last dim. - p = self.params - return [p.filter_shape[-1]] - - @property - def _is_bn_folded(self): - """Whether batchnorm folded weights are effectively enabled.""" - p = self.params - if not p.batch_norm: - return False - return (p.bn_fold_weights or - (p.bn_fold_weights is None and p.qdomain.default is not None)) - - def _EvaluateConvKernel(self, inputs, filter_w, strides, dilation_rate, - padding_algorithm, data_format): - """Evaluates the lower level convolution kernel. - - Args: - inputs: As to tf.nn.convolution. - filter_w: As to tf.nn.depthwise_conv2d. - strides: As to tf.nn.convolution. - dilation_rate: As to tf.nn.convolution. - padding_algorithm: As to tf.nn.convolution (padding argument). - data_format: As to tf.nn.convolution. - - Returns: - Convolution kernel output. - """ - raise NotImplementedError() - - @classmethod - def OutputShape(cls, params, in_shape): - return _ComputeConvOutputShape(in_shape, params.filter_stride[0], - params.filter_stride[1], - params.filter_shape[-1]) - - def OutShape(self, in_shape): - """Compute the output shape given the input shape.""" - p = self.params - return _ComputeConvOutputShape(in_shape, p.filter_stride[0], - p.filter_stride[1], self.output_channels) - - def _GetWeights(self, - theta, - convolution_lambda, - folded_bn_padding, - cast_dtype=None): - """Gets a dictionary of weights and biases for the convolution. - - This is necessary for some operating modes where the weights are fused - with batch normalization differently for training vs eval. - - Args: - theta: A `.NestedMap` object containing underlying weights values of this - layer and its children layers. - convolution_lambda: Lambda which takes the convolution weights and runs - the convolution. - folded_bn_padding: Padding to apply to folded batch normalization moment - computation (or None for no padding). - cast_dtype: If not None, cast weights to the given dtype. - - Returns: - Tuple of (filter, biases). - """ - p = self.params - - # Original weights. - filter_w = theta.w - filter_output_shape = self.filter_output_shape - # TODO(miachen): remove casting once tf.nn.conv2d supports tf.float64. - if cast_dtype: - filter_w = tf.cast(filter_w, tf.float32) - if p.weight_norm: - if len(filter_output_shape) == 1: - # Normalize along the last dim (standard conv). - filter_w = tf.nn.l2_normalize(filter_w, [0, 1, 2]) * tf.reshape( - (theta.g + 1.0), [1, 1, 1, p.filter_shape[-1]]) - elif len(filter_output_shape) == 2: - # Normalize along the last two dimensions (depthwise conv). - filter_w = tf.nn.l2_normalize(filter_w, [0, 1]) * tf.reshape( - (theta.g + 1.0), [1, 1] + filter_output_shape) - else: - assert False, 'Unsupported weight norm filter shape' - - # Original bias. - if p.bias: - b = theta.b - else: - b = tf.zeros([symbolic.ToStatic(self.output_channels)], - dtype=filter_w.dtype) - - # Pass-through if weights are not folded with batch normalization. - if not self._is_bn_folded: - return filter_w, b - - # If batch norm is fused with weights, then compute the weights as from - # figure C.8 of https://arxiv.org/pdf/1712.05877.pdf for training and - # figure C.6 for eval. - if self.do_eval: - # Gets current moments without updating. - mean, variance, beta, gamma = self.bn.GetCurrentMoments(theta.bn) - else: - # Updates moments based on a trial run of the convolution. - raw_conv_output = convolution_lambda(filter_w) - mean, variance, beta, gamma = self.bn.ComputeAndUpdateMoments( - theta.bn, raw_conv_output, folded_bn_padding) - - # Fold weights and bias. Note that this layer's bias is not used (not - # applicable for batch norm case). - sigma_recip = tf.math.rsqrt(variance + self.bn.epsilon) - scale_correction = gamma * sigma_recip - # Normal conv will have all weights in the last dim - # ([_, _, _, output_channels]), which matches the 1D layout from - # batch norm. Depthwise uses the last two dims so reshape - # ([_, _, in_c, c_multiplier]). - scale_correction = tf.reshape(scale_correction, filter_output_shape) - filter_w = filter_w * scale_correction - b = (beta - (gamma * mean * sigma_recip)) - return filter_w, b - - def _ApplyConv(self, theta, inputs, folded_bn_padding=None): - p = self.params - strides = [p.filter_stride[0], p.filter_stride[1]] - dtype = inputs.dtype - cast_dtype = None - if dtype != tf.float32: - cast_dtype = tf.float32 - inputs = tf.cast(inputs, cast_dtype) - - padding_algorithm = 'SAME' - if p.causal_convolution: - # Causal convolution is only applied in time (height) dimension. - # Use VALID padding and shift the inputs to the right to ensure that the - # first output only depends on the first input and so on. The output is - # the same size as the input, as if the convolution used SAME padding. - padding_algorithm = 'VALID' - # The effective spatial filter size for dilated convolutions is - # (kernel - 1) * dilation_rate + 1 as according to - # https://www.tensorflow.org/api_docs/python/tf/nn/convolution. - causal_pad_size = (p.filter_shape[0] - 1) * p.dilation_rate[0] - - # Apply padding in width dimension to mimic SAME padding. - # Using the similar logic as above to produce the same number of output - # as if SAME padding is used. - width_pad_size = (p.filter_shape[1] - 1) * p.dilation_rate[1] - - # The amount of padding on the left is tricky. If stride > 1, total - # padding required for SAME padding would be: - # pad = ceil(input_size / stride - 1) * stride + eff_kernel - input_size - # where eff_kernel = (kernel - 1) * dilation_rate + 1 - # TensorFlow also pads more on the right / bottom side if total padding - # required is an odd number, so pad_left = pad // 2 - # Therefore pad_left could depend on input size, which might be dynamic. - # Here we only handle two special cases where 1) stride = 1, then - # pad_left = (eff_kernel - 1) // 2 - # and 2) kernel = 1, then - # pad_left = 0 - if p.filter_stride[1] > 1 and p.filter_shape[1] > 1: - raise ValueError('Causal convolution only supports width stride = 1 ' - 'or filter width = 1.') - width_pad_left = max(0, width_pad_size - 1) // 2 - width_pad_right = width_pad_size - width_pad_left - inputs = tf.pad(inputs, [[0, 0], [causal_pad_size, 0], - [width_pad_left, width_pad_right], [0, 0]]) - - # Lambda for computing the actual convolution. - def ComputeRawConvolution(filter_w): - return self._EvaluateConvKernel( - inputs, - filter_w=filter_w, - strides=strides, - dilation_rate=p.dilation_rate, - data_format='NHWC', - padding_algorithm=padding_algorithm) - - filter_w, b = self._GetWeights( - theta, ComputeRawConvolution, folded_bn_padding, cast_dtype=cast_dtype) - - # TODO(miachen): remove casting once tf.nn.conv2d supports tf.float64. - assert inputs.dtype == filter_w.dtype - - filter_w = self.QWeight(filter_w) - out = ComputeRawConvolution(filter_w) - - # Note that we always apply the bias (which may be zero) because some - # normalization mechanisms do implicitly produce a bias. - b = tf.cast(b, tf.float32) - out = tf.nn.bias_add(out, b) - - if dtype != tf.float32: - out = tf.cast(out, dtype) - return out - - def FProp(self, theta, inputs, paddings=None): - """Apply convolution to inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. It is expected to be of shape [batch, time, - frequency, channel]. The time dimension corresponds to the height - dimension as in images and the frequency dimension corresponds to the - width dimension as in images. - paddings: The paddings tensor. If None, the inputs have no paddings in the - sense of sequence training (e.g., in CNN models). Otherwise, it is - expected to be of shape [batch, time]. - - Returns: - outputs, out_paddings pair. - """ - p = self.params - if paddings is None: - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match( - tf.shape(inputs), [-1, -1, -1, p.filter_shape[2]]) - ], inputs) - else: - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(paddings), [-1, -1]), - py_utils.assert_shape_match( - tf.shape(inputs), - tf.concat([tf.shape(paddings), [-1, p.filter_shape[2]]], 0)) - ], inputs) - # Zeroing out padded inputs. - qpadding = self.QRPadding( - tf.expand_dims(tf.expand_dims(paddings, -1), -1)) - # Select based padding is required for quantized inference but is - # causing regressions on other platforms. TODO: Remove use_select - # attribute when root-caused/resolved. - inputs = py_utils.ApplyPadding( - qpadding, - inputs, - use_select=p.is_inference and p.qdomain.default is not None) - - with tf.name_scope(p.name): - input_shape = tf.shape(inputs) - - if paddings is None: - conv_padding = None - else: - # NOTE: this may be slightly inaccurate when p.dilation_rate[0] > 1. - # But there's likely no real problems. Trying to set it gives an error: - # pooling with SAME padding is not implemented for dilation_rate > 1. - # NOTE: window=p.filter_stride[0] means output i will be padded if any - # input in the stride between the two conv centers are padded. - conv_padding = _ComputeConvOutputPadding( - paddings, window=p.filter_stride[0], stride=p.filter_stride[0]) - - if p.conv_last: - out = self._ComputeConvLast(theta, inputs, paddings, conv_padding) - else: - out = self._Compute(theta, inputs, paddings, conv_padding) - - # Lastly zeroing out padded states. - if conv_padding is not None: - qpadding = self.QRPadding( - tf.expand_dims(tf.expand_dims(conv_padding, -1), -1)) - # Select based padding is required for quantized inference but is - # causing regressions on other platforms. TODO: Remove use_select - # attribute when root-caused/resolved. - out = py_utils.ApplyPadding( - qpadding, - out, - use_select=p.is_inference and p.qdomain.default is not None) - - out = py_utils.HasShape( - out, symbolic.ToStatic(BaseConv2DLayer.OutShape(self, input_shape))) - return out, conv_padding - - def _Compute(self, theta, inputs, paddings, conv_padding): - """Computes the forward prop (conv, bn, act).""" - p = self.params - - bn_padding = conv_padding - if bn_padding is None: - bn_padding_expanded = None - else: - batch_time = tf.shape(bn_padding) - batch_time_any_any = tf.concat([batch_time, [-1, -1]], 0) - bn_padding_expanded = tf.reshape(bn_padding, - tf.concat([batch_time, [1, 1]], 0)) - - out = self._ApplyConv(theta, inputs, bn_padding_expanded) - if bn_padding is not None: - out = py_utils.with_dependencies([ - py_utils.assert_shape_match(batch_time, [-1, -1]), - py_utils.assert_shape_match(tf.shape(out), batch_time_any_any) - ], out) - - # Only apply batch norm if it was not folded into the weights. - if p.batch_norm and not p.bn_fold_weights: - out = self.bn.FProp(theta.bn, out, bn_padding_expanded) - - # Apply activation. - if p.activation != 'NONE': - if p.activation not in _TFLITE_FUSED_ACTIVATION_NAMES: - out = self.QTensor('pre_activation', out) - out = _ACTIVATIONS[p.activation](out) - if not p.disable_activation_quantization: - out = self.QTensor('activation', out) - - return out - - def _ComputeConvLast(self, theta, inputs, paddings, conv_padding): - """Computes the forward prop in conv_last mode (bn, act, conv).""" - p = self.params - out = inputs - out_padding = paddings - - if p.batch_norm: - if out_padding is None: - out_padding_expanded = None - else: - batch_time = tf.shape(out_padding) - batch_time_any_any = tf.concat([batch_time, [-1, -1]], 0) - out = py_utils.with_dependencies([ - py_utils.assert_shape_match(batch_time, [-1, -1]), - py_utils.assert_shape_match(tf.shape(out), batch_time_any_any) - ], out) - out_padding_expanded = tf.reshape(out_padding, - tf.concat([batch_time, [1, 1]], 0)) - out = self.bn.FProp(theta.bn, out, out_padding_expanded) - - if p.activation != 'NONE': - out = _ACTIVATIONS[p.activation](out) - - out = self._ApplyConv(theta, out) - - return out - - -class Conv2DLayer(BaseConv2DLayer): - """Convolution layer, with optional batch-normalization and activation.""" - - def _EvaluateConvKernel(self, inputs, filter_w, strides, dilation_rate, - padding_algorithm, data_format): - p = self.params - return tf.nn.convolution( - inputs, - filter_w, - strides=strides, - dilations=p.dilation_rate, - data_format='NHWC', - padding=padding_algorithm) - - -class ConvNN2DLayer(BaseConv2DLayer): - """Convolution layer, based on tf.nn.conv2d instead of tf.nn.convolution. - - tf.nn.convolution is using a different implementation on atrous convolutions, - by wrapping the actual convolution with space_to_batch and batch_to_space. - This implementation is not supported in tflite conversion, hence we need - a different layer for using atrous convolutions. - """ - - def _EvaluateConvKernel(self, inputs, filter_w, strides, dilation_rate, - padding_algorithm, data_format): - p = self.params - return tf.nn.conv2d( - inputs, - filter_w, - strides=strides, - dilations=p.dilation_rate, - data_format='NHWC', - padding='SAME') - - -# Alias of Conv2DLayer (for compatibility with historical uses). -ConvLayer = Conv2DLayer - - -class DepthwiseConv2DLayer(BaseConv2DLayer): - """Depthwise conv 2D layer. - - paper: https://arxiv.org/abs/1610.02357 - """ - - @classmethod - def Params(cls): - p = super().Params() - # Redefine 'filter_shape' since the semantic of shape elements is different - # from regular Conv2D. - p.Delete('filter_shape') - p.Define( - 'filter_shape', (0, 0, 0, 0), - 'Filter shape. Must be a sequence of length 4. Elements are in' - ' the order of height (time), width (frequency), in_channel,' - ' channel_multipliers. ') - return p - - @property - def output_channels(self): - """The number of output channels for this conv layer.""" - p = self.params - # Depthwise convolution filter shape is: - # [..., in_channels, channel_multiplier]. - return p.filter_shape[-2] * p.filter_shape[-1] - - @property - def filter_output_shape(self): - """Final dims of the filter corresponding to the output channels.""" - # Depthwise convolution uses the final two dims for output channels. - p = self.params - _, _, in_c, c_mul = p.filter_shape - return [in_c, c_mul] - - def _EvaluateConvKernel(self, inputs, filter_w, strides, dilation_rate, - padding_algorithm, data_format): - p = self.params - return tf.nn.depthwise_conv2d( - inputs, - filter=filter_w, - strides=[1, strides[0], strides[1], 1], - dilations=p.dilation_rate, - data_format='NHWC', - padding=padding_algorithm) - - -class SeparableConv2DLayer(Conv2DLayer): - """Separable 2D convolution. - - This class aggregates a DepthwiseConv2DLayer that feeds in to the point - wise convolution defined by this layer. Since the point wise convolution - controls the output, this class is defined in terms of that and delegates - to a depthwise sub-layer. - - The `filter_shape` parameter is rewritten on initialization from the form: - (h, w, cin, cout) - To: - Depthwise filter: (h, w, cin, p.depth_multiplier) - Pointwise filter (on this instance): (1, 1, cin * p.depth_multiplier, cout) - - This way, the layer is configured as if it were a normal 2D convolution - but is internally reconfigured to be separable. - - paper: https://arxiv.org/abs/1610.02357 - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'depth_multiplier', 1, - 'Number of depthwise convolution output channels per input channel. ' - 'The total number of depthwise convolution output channels will be.' - 'equal to in_channel * depth_multiplier.') - p.Define('depthwise_tpl', - DepthwiseConv2DLayer.Params().Set(activation='NONE'), - 'Template for the depthwise conv sub-layer.') - return p - - def __init__(self, params): - # Rewrite the filter. - params = params.Copy() - h, w, cin, cout = params.filter_shape - params.filter_shape = (1, 1, cin * params.depth_multiplier, cout) - depthwise_filter_shape = (h, w, cin, params.depth_multiplier) - - # Dilation rate and stride go to the depthwise layer and reset ours. - depthwise_filter_stride = params.filter_stride - depthwise_dilation_rate = params.dilation_rate - params.filter_stride = (1, 1) - params.dilation_rate = (1, 1) - - super().__init__(params) - p = self.params - del params - - # Create the depthwise sub-layer. - depthwise_params = p.depthwise_tpl.Copy().Set( - filter_shape=depthwise_filter_shape, - filter_stride=depthwise_filter_stride, - dilation_rate=depthwise_dilation_rate, - causal_convolution=p.causal_convolution, - weight_norm=p.weight_norm, - batch_norm=p.batch_norm, - bn_decay=p.bn_decay, - bn_fold_weights=p.bn_fold_weights) - depthwise_params.qdomain.default = p.qdomain.default - self.CreateChild('depthwise_conv', depthwise_params) - - def FProp(self, theta, inputs, paddings=None): - inputs, paddings = self.depthwise_conv.FProp(theta.depthwise_conv, inputs, - paddings) - return super().FProp(theta, inputs, paddings) - - def OutShape(self, in_shape): - """Compute the output shape given the input shape.""" - in_shape = self.depthwise_conv.OutShape(in_shape) - return super().OutShape(in_shape) - - -class ProjectionLayer(quant_utils.QuantizableLayer): - """Projection layer, with batch normalization and relu activation.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Depth of the input.') - p.Define('output_dim', 0, 'Depth of the output.') - p.Define( - 'activation', 'RELU', - 'Activation function to use. Options are RELU, RELU6, SIGMOID, ' - 'TANH, NONE.') - p.Define('batch_norm', None, 'Whether or not to apply batch norm.') - p.Define('has_bias', False, - 'Whether or not to introduce the bias params to the layer.') - p.Define('bias_init', 0.0, 'Initial value for the bias') - p.Define( - 'affine_last', False, - 'If true, apply the affine transformation as the last step, i.e., ' - 'first apply batch normalization on the input, followed ' - 'by activation, and finally the affine transformation. ' - 'Otherwise, apply affine transformation first, followed by batch ' - 'normalization and activation.') - p.Define( - 'weight_norm', False, - 'If true, apply weight normalization to weights as proposed by' - ' Salimans and Kingma, 2016: https://arxiv.org/abs/1602.07868') - p.Define( - 'bn_fold_weights', None, - 'Fold the batch norm parameters into the convolution weights at ' - 'eval/inference time as per https://arxiv.org/pdf/1712.05877.pdf. ' - 'Defaults to None which means that it will be disabled by default ' - 'and enabled when quantized training is enabled. Not compatible with ' - 'affine_last=True') - p.Define('bn_params', - BatchNormLayer.Params().Set(decay=0.999), - 'Default params for batch norm layer.') - p.Define('apply_pruning', False, - 'Whether to prune the weights while training') - p.Define( - 'use_einsum', True, 'Whether to use tf.einsum for optimizing ' - 'computations. When this is set to False, this causes an increase in ' - 'TPU memory usage (b/158336491). When this is set to True, it might ' - ' cause problems with model quantization for on device inference ' - '(b/146421936)') - p.Define( - 'use_blocked_matmul', False, 'Whether to use blocked matrix ' - 'multiplications. This allows for weight updates to be paralellized' - ' across the cores for Shampoo optimizer.') - p.Define('block_dim', 1024, 'Dimension of the block') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert symbolic.EvalExpr(symbolic.STATIC_VALUES, p.input_dim) > 0 - assert symbolic.EvalExpr(symbolic.STATIC_VALUES, p.output_dim) > 0 - assert p.activation == 'NONE' or p.activation in _ACTIVATIONS - if p.batch_norm is None: - raise RuntimeError( - 'ProjectionLayer.batch_norm not set explicitly for %s' % self.path) - if p.batch_norm and p.has_bias: - tf.logging.warning( - 'Projection layer enables both batch_norm and has_bias. ' - 'This is generally redundant/wasteful and may introduce ' - 'accuracy problems in some inference scenarios.') - if self._is_bn_folded: - assert not p.use_blocked_matmul, ( - 'bn_fold_weights requires use_blocked_matmul = False') - assert not p.affine_last, ( - 'Folded batchnorm is not compatible with affine_last') - - if p.use_einsum: - assert not p.use_blocked_matmul, ( - 'use_einsum requires use_blocked_matmul = False') - - if p.batch_norm: - bn_params = p.bn_params.Copy() - bn_params.name = p.name - bn_params.dim = p.input_dim if p.affine_last else p.output_dim - - self.CreateChild('bn', bn_params) - # TODO(yonghui): implement the variational noise logic. - - def _GetBlockedMatMulInputOutputMultipliers(self): - """Get number of input and output blocks.""" - p = self.params - # Number of input and output blocks. - w_im = p.input_dim // p.block_dim - w_om = p.output_dim // p.block_dim - # Add padding if input_dim / output_dim is not divisible by block_dim. - if p.input_dim % p.block_dim != 0: - w_im += 1 - if p.output_dim % p.block_dim != 0: - w_om += 1 - return w_im, w_om - - def _GetBlockedWeightMatrix(self, w): - """Returns a 3D weight matrix for blocked matmul.""" - p = self.params - # w is 3D Tensor of shape [i * o, block_dim, block_dim] such that - # i * block_dim = num_inputs (modulo padding). - # j * block_dim = num_outputs - # - # To efficiently apply forward prop, we transpose and reshape w into - # shape [i * block_dim, o, block_dim] - w_im, w_om = self._GetBlockedMatMulInputOutputMultipliers() - block_dim = p.block_dim - w_4d = tf.reshape(w, [w_im, w_om, block_dim, block_dim]) - # Transpose to [i, block_dim, o, block_dim]. - w_4d_t = tf.transpose(w_4d, [0, 2, 1, 3]) - w = tf.reshape(w_4d_t, [w_im * block_dim, w_om, block_dim]) - # Slice out padding from the weight matrix. - if p.input_dim % p.block_dim != 0: - w = tf.slice(w, [0, 0, 0], [p.input_dim, w_om, block_dim]) - return w - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - if p.use_blocked_matmul: - w_im, w_om = self._GetBlockedMatMulInputOutputMultipliers() - w_pc = py_utils.WeightParams( - shape=[w_im * w_om, p.block_dim, p.block_dim], - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - else: - w_pc = py_utils.WeightParams( - shape=[p.input_dim, p.output_dim], - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - if p.apply_pruning: - mask_w_pc = py_utils.WeightParams(w_pc.shape, - py_utils.WeightInit.Constant(1.0), - p.dtype) - threshold_w_pc = py_utils.WeightParams([], - py_utils.WeightInit.Constant(0.0), - tf.float32) - if p.has_bias: - b_pc = py_utils.WeightParams( - shape=[p.output_dim], - init=py_utils.WeightInit.Constant(scale=p.bias_init), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - if p.weight_norm: - g_pc = py_utils.WeightParams( - shape=[p.output_dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - weights_var_name = 'w' - if p.apply_pruning: - mask_var_name = 'mask' - threshold_var_name = 'threshold' - self.CreateVariable( - mask_var_name, mask_w_pc, theta_fn=None, trainable=False) - self.CreateVariable( - threshold_var_name, threshold_w_pc, theta_fn=None, trainable=False) - - def MaskWeightFn(weight): - return tf.multiply( - self.AddGlobalVN(weight), getattr(self.vars, mask_var_name), - 'masked_w') - - self.CreateVariable(weights_var_name, w_pc, theta_fn=MaskWeightFn) - pruning_utils.AddToPruningCollections( - getattr(self.vars, weights_var_name), getattr(self.vars, - mask_var_name), - getattr(self.vars, threshold_var_name)) - else: - self.CreateVariable(weights_var_name, w_pc) - - if p.has_bias: - self.CreateVariable('b', b_pc) - if p.weight_norm: - self.CreateVariable('g', g_pc) - - # Determine quantization needs based on whether fusing activation - # or not. - self._pre_activation_qt_name = None - self._output_qt_name = ('activation' - if p.activation != 'NONE' else 'affine_matmul') - if (p.activation != 'NONE' and - p.activation not in _TFLITE_FUSED_ACTIVATION_NAMES): - # Not a fused activation function. - # Need a qtensor to track the pre-activation tensor. The name is - # compatible with older checkpoints. - self._pre_activation_qt_name = 'affine_matmul' - self.TrackQTensor(self._output_qt_name) - if self._pre_activation_qt_name: - self.TrackQTensor(self._pre_activation_qt_name) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - if self.params.batch_norm: - self.bn.InstantiateVariables() - super()._CreateChildrenVariables() - - @classmethod - def NumOutputNodes(cls, p): - return p.output_dim - - @property - def output_qt_name(self): - """Name of QTensor used for the output value. - - Useful for grabbing the quantization of the output. - - Returns: - String name of output qtensor. - """ - return self._output_qt_name - - def FProp(self, theta, inputs, paddings=None): - """Apply projection to inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. Shaped [..., input_dim]. - paddings: The paddings tensor. Shaped [..., 1], where all but the last - dimension match. - - Returns: - Output after applying projection, and optionally batch normalization and - relu non-linearity. - """ - p = self.params - with tf.name_scope(p.name): - if paddings is None: - paddings = tf.zeros( - tf.concat([py_utils.GetShape(inputs)[:-1], [1]], axis=0), - dtype=inputs.dtype) - w, b = self._GetWeights(theta, inputs, paddings) - w = self.QWeight(w) - - if p.affine_last: - # Reversed computation. Does not handle folding. - out = inputs - if p.batch_norm: - out = self.bn.FProp(theta.bn, out, paddings) - if p.activation != 'NONE': - if not p.is_inference: - out = py_utils.CheckNumerics(out) - out = _ACTIVATIONS[p.activation](out) - out = self._ApplyProjectionKernel(w, b, out, with_activation=False) - else: - # Normal ordered projection. - if self._is_bn_folded or not p.batch_norm: - # Everything folded together. This is the only variant that supports - # quantization. - out = self._ApplyProjectionKernel(w, b, inputs, quant=True) - else: - # Projection kernel(no activation fn) -> BN -> Activation fn. - out = self._ApplyProjectionKernel(w, b, inputs, with_activation=False) - if p.batch_norm: - out = self.bn.FProp(theta.bn, out, paddings) - if p.activation != 'NONE': - if not p.is_inference: - out = py_utils.CheckNumerics(out) - out = _ACTIVATIONS[p.activation](out) - return py_utils.ApplyPadding(self.QRPadding(paddings), out) - - @property - def _is_bn_folded(self): - """Whether batchnorm folded weights are effectively enabled.""" - p = self.params - if not p.batch_norm: - return False - return (p.bn_fold_weights or - (p.bn_fold_weights is None and p.qdomain.default is not None)) - - def _GetWeights(self, theta, inputs, paddings): - """Gets the weights for the computation. - - Weights will always have weight_norm applied and may have batch_norm - folded if enabled. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: Inputs (needed for batchnorm folding). - paddings: Paddings (needed for batchnorm folding). - - Returns: - Tuple of (w, b) to use for the forward pass. b may be None if bias is - disabled. - """ - p = self.params - w = theta.w - b = theta.b if p.has_bias else None - if p.use_blocked_matmul: - w = self._GetBlockedWeightMatrix(w) - if p.weight_norm: - w = tf.nn.l2_normalize(w, 0) - else: - if p.weight_norm: - w = tf.reshape((theta.g + 1.0) * tf.nn.l2_normalize(w, [0]), - py_utils.ToStaticShape([p.input_dim, p.output_dim])) - - if not self._is_bn_folded: - return w, b - - # If batch norm is fused with weights, then compute the weights as from - # figure C.8 of https://arxiv.org/pdf/1712.05877.pdf for training and - # figure C.6 for eval. - if self.do_eval: - # Gets current moments without updating. - mean, variance, beta, gamma = self.bn.GetCurrentMoments(theta.bn) - else: - # Updates moments based on a trial run of the kernel (without activation - # function). - raw_output = self._ApplyProjectionKernel( - w, b, inputs, with_activation=False) - mean, variance, beta, gamma = self.bn.ComputeAndUpdateMoments( - theta.bn, raw_output, paddings) - - # Fold weights and bias. - sigma_recip = tf.math.rsqrt(variance + self.bn.epsilon) - scale_correction = gamma * sigma_recip - w = w * scale_correction - b = beta - (gamma * mean * sigma_recip) - return w, b - - def _ApplyProjectionKernel(self, - w, - b, - inputs, - with_activation=True, - quant=False, - bn=False): - """Applies matmul/bias/activation in one step. - - Note that it is important that these three ops be computed in this way as - downstream inference engines (esp. for quantized inference) can recognize - and fuse them. For floating point, this is an optimization, but for - quantization, it is required. - - Args: - w: Weight matrix. - b: Bias vector (or None). - inputs: FProp inputs. - with_activation: Whether to also compute the activation function. - quant: Whether to apply quantization. - bn: Apply batchnorm. - - Returns: - Output tensor reshaped. - """ - p = self.params - - if not p.use_blocked_matmul: - if p.use_einsum: - out = py_utils.ProjectLastDim(inputs, w, p.input_dim, p.output_dim) - else: - out = py_utils.Matmul( - tf.reshape(inputs, py_utils.ToStaticShape([-1, p.input_dim])), w) - else: - x = tf.reshape(inputs, py_utils.ToStaticShape([-1, p.input_dim])) - out = tf.einsum('bn,nmk->bmk', x, w) - # Create an output layer [b, num_outputs]. - bsz = py_utils.GetShape(out)[0] - out = tf.reshape(out, [bsz, -1]) - if p.output_dim % p.block_dim != 0: - out_shape = [bsz, p.output_dim] - out = tf.slice(out, [0, 0], out_shape) - - if b is not None: - out += b # NOTE: Bias on matmul is never quantized. - return self._ApplyActivationFunction(out, inputs, with_activation, quant) - - def _ApplyActivationFunction(self, - out, - inputs, - with_activation=True, - quant=False): - """Applies the activation function in one step. - - Args: - out: The result of applying the weight matrix (and bias) to the inputs. - inputs: FProp inputs. - with_activation: Whether to also compute the activation function. - quant: Whether to apply quantization. - - Returns: - Output tensor reshaped. - """ - p = self.params - if with_activation and p.activation != 'NONE': - if self._pre_activation_qt_name: - # Track quantization for unfused activation function. - out = self.QTensor(self._pre_activation_qt_name, out) - if not p.is_inference: - out = py_utils.CheckNumerics(out) - out = _ACTIVATIONS[p.activation](out) - if quant: - out = self.QTensor(self._output_qt_name, out) - if not p.use_einsum: - out = tf.reshape( - out, - tf.concat([ - py_utils.GetShape(inputs)[:-1], - py_utils.ToStaticShape([p.output_dim]) - ], - axis=0)) - return out - - @classmethod - def FPropMeta(cls, p, inputs, paddings=None): - py_utils.CheckShapes((inputs,)) - assert inputs[-1] == p.input_dim - flops = 0 - in_dim = inputs[-1] - other_dims = inputs.num_elements() / in_dim - # matmuls. - flops += other_dims * p.input_dim * p.output_dim * 2 - # activations. - flops += other_dims * p.output_dim * _ACTIVATIONS_FLOPS[p.activation] - if p.has_bias: - flops += p.output_dim - out_shape = tshape.Shape(inputs[:-1] + [p.output_dim]) - if p.batch_norm: - bn_meta = p.bn_params.cls.FPropMeta( - p.bn_params.Copy().Set(dim=p.output_dim), out_shape) - flops += bn_meta.flops - if p.weight_norm: - # l2 normalize + element-wise multiply. - flops += 2 * p.input_dim + 2 * p.input_dim * p.output_dim + 2 - - return py_utils.NestedMap(flops=flops, out_shapes=(out_shape,)) - - -class FCLayer(ProjectionLayer): - """Fully-connected layer (matmul + bias + optional activation).""" - - @classmethod - def Params(cls): - p = super().Params() - p.batch_norm = False - p.has_bias = True - return p - - -class FeedForwardNet(quant_utils.QuantizableLayer): - """A simple multiple layer feedforward network. - - This class represents a stack of fully connected feedforward network. Each - layer in the network can be configured for whether or not to have batch-norm - applied to its output, its activation function, whether or not to apply - dropout to post-activation output. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Depth of the input to the network.') - p.Define('hidden_layer_dims', [], 'Depth of the hidden layer outputs.') - p.Define( - 'projection', ProjectionLayer.Params(), - 'Projection layer params. A single parameter that will be shared by' - 'all layers.') - p.Define( - 'dropout', DropoutLayer.Params(), - 'Dropout layer params. Can be a single params or a tuple/list of params' - ' having the same length as the number of layers.') - p.Define( - 'batch_norm', False, - 'Whether or not to apply BN to hidden layer output. ' - 'This can be a single bool or a tuple/list of bools having the' - ' same length as the number of layers.') - p.Define( - 'activation', 'RELU', - 'The activation function to use. Can be a single string, or a' - ' tuple/list of strings having the same length as the number' - ' of layers.') - p.Define( - 'has_bias', None, 'Whether or not to use bias for projection layers.' - 'This can be a None, single bool or a tuple/list of bools having the ' - 'same length as the number of layers. If None, the has_bias is set to ' - 'True whenever batch_norm is False for each projection layer.') - p.Define( - 'weight_norm', False, - 'Whether or not to apply weight normalization to weights. This can be ' - 'a single bool or a tuple/list of bools having the same length as the ' - 'number of layers.') - p.Define('skip_connections', None, 'Must be None.') - p.Define( - 'bn_fold_weights', None, 'Force folding the batch normalization ' - 'weights in the projection layer.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert symbolic.ToStatic(p.input_dim) > 0 - assert all(symbolic.ToStatic(x) > 0 for x in p.hidden_layer_dims) - - assert p.skip_connections is None - batch_norm = p.batch_norm - num_layers = len(p.hidden_layer_dims) - if isinstance(batch_norm, (list, tuple)): - assert len(batch_norm) == num_layers - else: - batch_norm = [batch_norm] * num_layers - weight_norm = p.weight_norm - if isinstance(weight_norm, (list, tuple)): - assert len(weight_norm) == num_layers - else: - weight_norm = [weight_norm] * num_layers - - activation = p.activation - if isinstance(activation, str): - activation = [activation] * num_layers - else: - assert len(activation) == num_layers - has_bias = p.has_bias - if isinstance(has_bias, (list, tuple)): - assert len(has_bias) == num_layers - else: - has_bias = [has_bias] * num_layers - # Set has_bias to (not batch_norm) if None. - for i in range(num_layers): - if has_bias[i] is None: - has_bias[i] = (not batch_norm[i]) - params_dropout_layers = p.dropout - if isinstance(params_dropout_layers, (list, tuple)): - assert len(params_dropout_layers) == num_layers - else: - params_dropout_layers = [params_dropout_layers] * num_layers - - # Residual connections work better in the form of: - # y = x + Affine(Activation(BatchNorm(x))) - params_fc_layers = [] - in_dim = p.input_dim - for i in range(num_layers): - out_dim = p.hidden_layer_dims[i] - proj_out_dim = out_dim - name = '%s_%d' % (p.name, i) - params_i = p.projection.Copy().Set( - batch_norm=batch_norm[i], - weight_norm=weight_norm[i], - has_bias=has_bias[i], - activation=activation[i], - input_dim=in_dim, - output_dim=proj_out_dim, - bn_fold_weights=p.bn_fold_weights, - name=name) - params_fc_layers.append(params_i) - in_dim = out_dim - - if p.qdomain.default is not None: - params_i.qdomain.default = p.qdomain.default.Copy() - - self.CreateChildren('fc', params_fc_layers) - self.CreateChildren('dropout', params_dropout_layers) - - @property - def output_dim(self): - """Returns output dimension of the FeedForwardNet.""" - return self.params.hidden_layer_dims[-1] - - def FProp(self, theta, inputs, paddings=None): - p = self.params - num_layers = len(self.fc) - - in_dim, layer_in = p.input_dim, inputs - for i in range(num_layers): - layer_in = py_utils.with_dependencies([ - py_utils.assert_shape_match([tf.shape(layer_in)[-1]], - [symbolic.ToStatic(in_dim)]) - ], layer_in) - out_dim = p.hidden_layer_dims[i] - layer_out = self.fc[i].FProp(theta.fc[i], layer_in, paddings) - layer_out = self.dropout[i].FProp(theta.dropout[i], layer_out) - layer_in = layer_out - in_dim = out_dim - return layer_in - - @classmethod - def FPropMeta(cls, p, inputs, paddings=None): - py_utils.CheckShapes((inputs,)) - assert inputs[-1] == p.input_dim - flops = 0 - with tf.Graph().as_default(): # throw-away graph. - instance = p.Instantiate() - for fc in instance.fc: - proj_params = fc.params - proj_shape = tshape.Shape(inputs[:-1] + [proj_params.input_dim]) - proj_meta = proj_params.cls.FPropMeta(proj_params, proj_shape) - flops += proj_meta.flops - out_shape = tshape.Shape(inputs[:-1] + [p.hidden_layer_dims[-1]]) - return py_utils.NestedMap(flops=flops, out_shapes=(out_shape,)) - - -class StackingOverTime(base_layer.BaseLayer): - """Stacking applied along the time axis. - - At each time step of an input sequence, elements are stacked over the - window of ('left_context' + 1 + 'right_context') steps around the current - time step. Zeros will be padded to the left or right of the sequence for - elements around the boundaries. Finally the stacked outputs are emitted - once every 'stride' steps. - - E.g. if an input sequence is: [4], [1], [9], [3], [5], [2], [8] - left_context = 1, right_context = 1, stride = 3, - then the output sequence would be: [0, 4, 1], [9, 3, 5], [2, 8, 0] - - Note that this layer only performs tensor transformation, so there are no - learnable parameters. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('left_context', 0, - 'Number of time steps to stack on the left to the central step.') - p.Define('right_context', 0, - 'Number of time steps to stack on the right to the central step.') - p.Define('stride', 1, 'The stride for emitting the stacked output.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.left_context >= 0 - assert p.right_context >= 0 - assert p.stride >= 1 - - @property - def window_size(self): - """Returns the stacking window size. - - The output dimension will be window_size * the input dimension. - - Returns: - Window size. - """ - p = self.params - return p.left_context + p.right_context + 1 - - def _ApplyStack(self, inputs, pad_value=0.0): - """The core function to apply the stacking to inputs. - - Args: - inputs: [batch, time, depth]. - pad_value: the padding value for left/right context. - - Returns: - [batch, ceil(time / stride), depth * stacking_window_length] tensor. - """ - p = self.params - if p.left_context == 0 and p.right_context == 0: - out = inputs - else: - inputs_max_len = py_utils.GetShape(inputs, 3)[1] - # Add zero paddings to the left and right of the input sequence. - inputs = tf.pad( - inputs, [[0, 0], [p.left_context, p.right_context], [0, 0]], - constant_values=pad_value) - - # Make window_size() copies of the padded sequence with the original - # sequence length, where each copy is offset by 1 time step. - pieces = [] - for i in range(self.window_size): - pieces.append(inputs[:, i:i + inputs_max_len]) - # Apply stacking. - out = tf.concat(pieces, 2) - - # Apply striding. - out = out[:, ::p.stride] - return out - - def FProp(self, inputs, paddings=None): - """Apply the stacking to inputs along the time axis. - - Args: - inputs: The inputs tensor. It is expected to be of shape [batch, time, - feature]. - paddings: The paddings tensor. It is expected to be of shape [batch, time, - 1], where all but the last dimension match inputs. Each value is 0 or 1 - indicating whether a time step of a sequence is padded in the inputs to - reach the max length in the batch. - - Returns: - (outputs, out_paddings) pair. - outputs is of shape [batch, ceil(time / stride), feature * stacking]. - out_paddings is of shape [batch, ceil(time / stride), 1]. out_paddings - will be 0 if any of the corresponding input padding is 0. - """ - if paddings is None: - paddings = tf.zeros( - tf.concat([py_utils.GetShape(inputs)[:-1], [1]], 0), - dtype=inputs.dtype) - inputs = py_utils.with_dependencies( - [ - # Checks the inputs shape has 3 dimensions. - py_utils.assert_shape_match(tf.shape(inputs), [-1, -1, -1]), - # Checks the paddings shape has 3 dimensions, and the last one is 1. - py_utils.assert_shape_match(tf.shape(paddings), [-1, -1, 1]), - # Checks the first two dimensions of inputs and paddings match. - py_utils.assert_shape_match( - tf.shape(inputs)[:-1], - tf.shape(paddings)[:-1]) - ], - inputs) - p = self.params - with tf.name_scope(p.name): - outputs = self._ApplyStack(inputs) - - # Stack the padding values with the same context and stride parameters. - # Then take the minimum padding values within each stacking window, since - # an output time step becomes a padded one only if all of the underlying - # stacked steps are padded ones. - out_paddings = self._ApplyStack(paddings, pad_value=1) - out_paddings = tf.reduce_min(out_paddings, axis=2, keepdims=True) - - return outputs, out_paddings - - def Unstack(self, stacked): - """Inverts stacking over time. - - Given 'stacked' outputs from this StackingOverTime layer, - - stacked, _ = this_layer.FProp(inputs), - - this method attempts to reconstruct the original 'inputs'. - - If stride > window_size, the original input cannot be recovered, and a - ValueError is raised. - - Otherwise, if right_context + 1 >= stride, this method returns a Tensor that - is identical to 'inputs' but potentially longer due to paddings. - - If right_context + 1 < stride, this method returns a Tensor that may be up - to ```stride - right_context - 1``` frames shorter than the original input, - but identical in the frames that are returned. e.g.:: - - left_context = 2, right_context = 1, stride = 4 - input sequence: 1 2 3 4 5 6 7 8 - after padding: 0 0 1 2 3 4 5 6 7 8 0 - windows: - [0 0 (1) 2] 3 4 5 6 7 8 0 - 0 0 1 2 [3 4 (5) 6] 7 8 0 - stacked: - [[0 0 1 2], [3 4 5 6]] - unstacked: - [1 2 3 4 5 6], which is 4 - 1 - 1 = 2 (stride - right_context - 1) - frames shorter than the original input. - - `Unstack()` can be used to project the outputs of downstream layers back to - the shape of the original unstacked inputs. For example:: - - inputs = ... # [batch, length, input_dim] - # [batch, ceil(length / stride), rnn_dim] - rnn_out = rnn.FProp(stacking.FProp(inputs)[0]) - # [batch, length, rnn_dim] - back_projected_rnn_out = py_utils.PadOrTrimTo( - stacking.Unstack(tf.tile(rnn_out, [1, 1, stacking.window_size])), - py_utils.GetShape(inputs)) - - Note this method does not take or return a separate padding tensor. The - caller is responsible for knowing which of outputs are padding (e.g. based - on the padding of the original FProp inputs). - - Args: - stacked: Tensor of shape [batch, time, window_size * feature_dim], assumed - to be the output of `FProp`. - - Returns: - The reconstructed input Tensor, with shape - [batch, (frames - 1) * stride + right_context + 1, feature_dim]. - - Raises: - ValueError: if stride > window_size. - """ - p = self.params - if p.stride > self.window_size: - raise ValueError( - "Can't invert StackingOverTime with stride (%d) > window_size (%d)" % - (p.stride, self.window_size)) - - # Reshape to allow indexing individual frames within each stacked window. - batch_size, stacked_length, _ = py_utils.GetShape(stacked, 3) - stacked = tf.reshape(stacked, - [batch_size, stacked_length, self.window_size, -1]) - - # Compute the index of the window and frame in 'stacked' where each frame of - # the original input is located, and extract them with tf.gather_nd. - # First compute for all except the last window, since these elements have - # the potential of being looked up from the next window. - input_indices = tf.range(0, (stacked_length - 1) * p.stride) - mod = input_indices % p.stride - in_next_window = tf.cast(tf.greater(mod, p.right_context), tf.int32) - window_index = input_indices // p.stride + in_next_window - frame_index = p.left_context + mod - p.stride * in_next_window - # Now handle the last window explicitly and concatenate onto the existing - # window_index/frame_index tensors. - last_window_length = p.right_context + 1 - window_index = tf.concat( - [window_index, - tf.fill([last_window_length], stacked_length - 1)], - axis=0) - frame_index = tf.concat( - [frame_index, p.left_context + tf.range(last_window_length)], axis=0) - # Stack the indices for tf.gather_nd. - window_and_frame_indices = tf.stack([window_index, frame_index], axis=1) - window_and_frame_indices = tf.tile( - tf.expand_dims(window_and_frame_indices, 0), [batch_size, 1, 1]) - return tf.gather_nd(stacked, window_and_frame_indices, batch_dims=1) - - -class PoolingLayer(quant_utils.QuantizableLayer): - """Pooling layer, by default performs max-pooling. - - Quantization notes: Unlike the common pattern, the pooling layer inputs - and output must be quantized to the same range, so it tracks both (vs - just the output). The preceding layer must have its output quantization - disabled. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'window_shape', (0, 0), - 'Window shape. Must be a pair of ints. Elements are in' - ' the order of height (time), width (frequency).') - p.Define( - 'window_stride', (0, 0), - 'Window stride to use. Must be a pair of ints. The first int' - ' specifies the stride on the time dimension. The second int' - ' specifies the stride on the frequency dimension.') - p.Define('pooling_type', 'MAX', 'Pooling type: MAX|AVG') - p.Define( - 'padding_algorithm', 'SAME', - 'Padding algorithm. See the "returns" section of ' - '`tf.nn.convolution` for details. ' - 'Roughly, VALID = NO_PADDING and SAME (default) = PAD INPUT') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert len(p.window_shape) == 2 - assert len(p.window_stride) == 2 - assert all([x > 0 for x in p.window_shape]) - assert all([x > 0 for x in p.window_stride]) - assert p.pooling_type in ['MAX', 'AVG'] - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.TrackQTensor('output') - - def OutShape(self, in_shape): - """Compute the output shape given the input shape.""" - p = self.params - return _ComputeConvOutputShape( - in_shape, - p.window_stride[0], - p.window_stride[1], - padding=p.padding_algorithm) - - def FProp(self, theta, inputs, paddings=None): - """Apply pooling to inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. It is expected to be of shape [batch, time, - frequency, channel]. The time dimension corresponds to the height - dimension as in images and the frequency dimension corresponds to the - width dimension as in images. - paddings: The paddings tensor. It is expected to be of shape [batch, - time]. Defaults to None, which means there no paddings. - - Returns: - outputs, out_paddings pair. - """ - p = self.params - stride = p.window_stride - window = p.window_shape - if paddings is not None: - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(paddings), [-1, -1]), - py_utils.assert_shape_match(tf.shape(inputs)[:2], tf.shape(paddings)) - ], inputs) - with tf.name_scope(p.name): - if paddings is not None: - out_padding = _ComputeConvOutputPadding(paddings, window[0], stride[0], - p.padding_algorithm) - if p.pooling_type == 'MAX': - # Fill dtype.min in padded positions. - min_value = tf.ones_like(inputs) * p.dtype.min - inputs = py_utils.ApplyPadding(paddings[..., tf.newaxis, tf.newaxis], - inputs, min_value) - else: - out_padding = None - inputs = self.QTensor('output', inputs) - - out = tf.nn.pool( - inputs, - window, - p.pooling_type, - strides=stride, - padding=p.padding_algorithm, - data_format='NHWC', - ) - if paddings is not None and p.pooling_type == 'AVG': - # Count the fraction of non-padding elements inside each pooling window. - in_mask = 1.0 - paddings - non_padding_ratio = tf.nn.pool( - in_mask[:, :, tf.newaxis], - window_shape=(p.window_shape[0],), - pooling_type='AVG', - strides=(p.window_stride[0],), - padding=p.padding_algorithm) - # Divide by non-padding ratios to eliminate the effect of padded values. - out *= tf.math.reciprocal_no_nan(non_padding_ratio)[..., tf.newaxis] - - out = self.QTensor('output', out) - if out_padding is not None: - out *= tf.expand_dims(tf.expand_dims(1.0 - out_padding, -1), -1) - return out, out_padding - - -class BlurPoolLayer(base_layer.BaseLayer): - """BlurPool from https://arxiv.org/pdf/1904.11486.pdf. - - This layer blurs the input with a fixed filter and performs subsampling - afterwards. Only supports 2x1 or 2x2 spatial reduction. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('blur_filter', 'B5', 'One of [R2, T3, B5]; the fixed blur filter.') - p.Define('subsample_type', '1D', 'Choose between [1D, 2D] subsampling.') - p.Define('input_channels', None, 'Number of input channels.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.blur_filter in ['R2', 'T3', 'B5'] - assert p.subsample_type in ['1D', '2D'] - assert p.input_channels - - filter_dict = { - 'B5': np.array([1, 4, 6, 4, 1], dtype=np.float32), - 'T3': np.array([1, 2, 1], dtype=np.float32), - 'R2': np.array([1, 1], dtype=np.float32) - } - base_filter = filter_dict[p.blur_filter] - - if p.subsample_type == '2D': - base_filter = base_filter[:, np.newaxis] * base_filter[np.newaxis, :] - else: - base_filter = base_filter[:, np.newaxis] - base_filter /= base_filter.sum() - - self._blur_filter = np.tile(base_filter[..., np.newaxis, np.newaxis], - (1, 1, p.input_channels, 1)) - conv_params = DepthwiseConv2DLayer.Params().Set( - activation='NONE', - batch_norm=False, - filter_stride=(1, 1), - filter_shape=self._blur_filter.shape) - - self.CreateChild('blur_conv', conv_params) - - def FProp(self, theta, inputs, paddings=None): - """Apply blur pooling. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. It is expected to be of shape [batch, time, - frequency, channel]. The time dimension corresponds to the height - dimension as in images and the frequency dimension corresponds to the - width dimension as in images. - paddings: The paddings tensor. It is expected to be of shape [batch, - time]. Defaults to None, which means there no paddings. - - Returns: - outputs, out_paddings pair. - """ - p = self.params - if paddings is not None: - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(paddings), [-1, -1]), - py_utils.assert_shape_match(tf.shape(inputs)[:2], tf.shape(paddings)) - ], inputs) - # blur - theta_cp = copy.copy(theta.blur_conv) - theta_cp.w = tf.convert_to_tensor(self._blur_filter, dtype=p.dtype) - out, out_padding = self.blur_conv.FProp(theta_cp, inputs, paddings) - - # b/142399320 - # Use stride in blur conv for subsampling once non-square stride gets - # supported. - if p.subsample_type == '2D': - out = out[:, ::2, ::2, :] - else: - out = out[:, ::2, :, :] - - if out_padding is not None: - out_padding = _ComputeConvOutputPadding( - out_padding, window=2, stride=2, padding_algorithm='SAME') - out *= (1.0 - out_padding)[..., tf.newaxis, tf.newaxis] - else: - out_padding = None - - return out, out_padding - - -class SingleShardEmbeddingLayer(base_layer.BaseLayer): - """Embedding layer that is not sharded. - - This embedding layer is expected to be replicated over all compute devices - (e.g. tpu cores). It is intended to support small to medium embedding tables - (< 50k) only. - - This is intended to be a unification of EmbeddingLayer and - SimpleEmbeddingLayer (and cleanup of both). It is targeting the most common - use-case we have in speech/nmt/tts/deeprank. Currently we often first - configure a model using EmbeddingLayer, and then call ChangeToSimpleEmbedding - to switch to SimpleEmbedding where we lose some configuration (e.g. - scale_by_sqrt_dim). - - TODO(lingvo): Implement the matmul option which should be more efficient for - small vocabs (e.g. < 1k vocab). - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('vocab_size', 0, 'Num tokens in vocab.') - p.Define('embedding_dim', 0, 'Depth of the output.') - p.Define( - 'scale_sqrt_depth', False, 'If set True, activations are scaled' - ' with sqrt(embedding_dim) in EmbLookup.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.vocab_size > 0 - assert p.embedding_dim > 0 - assert p.name - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w_pc = py_utils.WeightParams( - shape=[p.vocab_size, p.embedding_dim], - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('emb_var', w_pc) - - def EmbLookupDefaultTheta(self, ids): - return self.EmbLookup(self.theta, ids) - - def EmbLookup(self, theta, ids): - """Looks up embedding vectors for ids. - - Args: - theta: Named tuple with the weight matrix for the embedding. - ids: A rank-N int32 tensor. - - Returns: - A rank-(N+1) params.dtype tensor. - embs[indices, :] is the embedding vector for ids[indices]. - """ - p = self.params - ids = tf.convert_to_tensor(ids) - ids = py_utils.with_dependencies([ - py_utils.assert_between( - ids, 0, p.vocab_size, name='vocab_id_validation') - ], ids) - embs = tf.nn.embedding_lookup(theta.emb_var, tf.reshape(ids, [-1])) - if p.scale_sqrt_depth: - embs *= p.embedding_dim**0.5 - if p.vn.global_vn or p.vn.per_step_vn: - embs = py_utils.AddGlobalVN(p, embs) - out_shape = tf.concat([tf.shape(ids), [p.embedding_dim]], 0) - return tf.reshape(embs, out_shape) - - def FProp(self, theta, ids): - return self.EmbLookup(theta, ids) - - -class EmbeddingLayer(base_layer.BaseLayer): - """Embedding layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('vocab_size', 0, 'Depth of the input.') - p.Define('embedding_dim', 0, 'Depth of the output.') - p.Define('max_num_shards', 0, 'Num param shards.') - p.Define('on_ps', True, 'True if to perform the embedding lookup on ps.') - p.Define( - 'scale_sqrt_depth', False, 'If set True, activations are scaled' - ' with sqrt(embedding_dim) in EmbLookup.') - return p - - # Min number of params per shard. - MIN_PARAMS_PER_SHARD = 1024 * 256 - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.vocab_size > 0 - assert p.embedding_dim > 0 - assert p.max_num_shards > 0 - assert p.name - - total_size = p.vocab_size * p.embedding_dim - self._actual_shards = min( - p.max_num_shards, - int(math.ceil(float(total_size) / self.MIN_PARAMS_PER_SHARD))) - self._ids_per_shard = int( - math.ceil(float(p.vocab_size) / self._actual_shards)) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w_pc = py_utils.WeightParams( - shape=[self._ids_per_shard, p.embedding_dim], - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - # EmbeddingLayer handles vars/theta differently from other layers - # because when embedding shards are placed on ps, it's more - # efficiently to do embedding lookups on ps and sends the result - # back to the worker. - emb_vars = [] - emb_shards = [] - for i in range(self._actual_shards): - var_name = 'var_%d' % i - self.CreateVariable(var_name, w_pc) - emb_vars.append(self.vars[var_name]) - # NOTE: self.theta[var_name] has transformations such as variational noise - # applied via theta_fn in self.CreateVariable. For embedding layer we - # apply variational noise explicitly in EmbLookup, so we do not use - # self.theta[var_name] here. - v = self.vars[var_name] - if not p.on_ps: - v = tf.identity(v) - if p.fprop_dtype is not None and p.fprop_dtype != p.dtype: - v = tf.cast(v, p.fprop_dtype) - emb_shards.append(v) - # Remove from _private_vars / _private_thetas to be added later as wm. - del self._private_vars[var_name] - del self._private_theta[var_name] - self._private_vars['wm'] = emb_vars - self._private_theta['wm'] = emb_shards - - def EmbLookupDefaultTheta(self, ids): - return self.EmbLookup(self.theta, ids) - - def EmbLookup(self, theta, ids): - """Looks up embedding vectors for ids. - - Args: - theta: Named tuple with the weight matrix for the embedding. - ids: A rank-N int32 tensor. - - Returns: - A rank-(N+1) params.dtype tensor. - embs[indices, :] is the embedding vector for ids[indices]. - """ - p = self.params - ids = tf.convert_to_tensor(ids) - ids = py_utils.with_dependencies([ - py_utils.assert_between( - ids, 0, p.vocab_size, name='vocab_id_validation') - ], ids) - embs = tf.nn.embedding_lookup(theta.wm, tf.reshape(ids, [-1])) - if p.scale_sqrt_depth: - embs *= p.embedding_dim**0.5 - if p.vn.global_vn or p.vn.per_step_vn: - embs = py_utils.AddGlobalVN(p, embs) - out_shape = tf.concat([tf.shape(ids), [p.embedding_dim]], 0) - return tf.reshape(embs, out_shape) - - -class SimpleEmbeddingLayer(quant_utils.QuantizableLayer): - """An embedding layer that is simple to compile (by XLA and Toco). - - The params use_matmul and use_gather control how the lookup is performed. - If neither is True, then a loop is used to compute the embedding. - - This layer is "simple" in comparison to 'EmbeddingLayer' in that it does - not shard the embeddings. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('vocab_size', 0, - 'Depth of the input. I.e., the number of classes.') - p.Define('embedding_dim', 0, 'Depth of the output.') - p.Define( - 'use_matmul', False, 'If True, use a matmul to implement ' - 'the embedding lookup. Depending on vocab_size and #ids, ' - 'e.g., when vocab_size is small, use_matmul can be more ' - 'efficient. On the other hand, use_matmul creates a 0/1 ' - 'sparse matrix and hence may use more memory than the ' - 'final output.') - p.Define( - 'fprop_mode', None, 'Sets the mode used for computing the fprop ' - '(different inference engines have different capabilities and this ' - 'accomodates them). Can be "loop", "matmul" or "gather". If None, ' - 'defaults to "matmul" if use_matmul or "loop" if false.') - p.Define( - 'use_3d_weight_tensor', False, 'If True, and use_matmul is False,' - 'in TPU compatibility mode, we reshape the normal 2D weight' - 'tensor to [num_rows, embed_dim] to be ' - '[num_rows, embed_dim // 128, 128].') - p.Define('apply_pruning', False, - 'Whether to prune the weights while training') - p.Define( - 'scale_sqrt_depth', False, 'If set True, activations are scaled' - ' with sqrt(embedding_dim) in EmbLookup.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.vocab_size > 0 - assert symbolic.ToStatic(p.embedding_dim) > 0 - - valid_fprop_modes = ['loop', 'matmul', 'gather'] - self._fprop_mode = p.fprop_mode - if not self._fprop_mode: - self._fprop_mode = 'matmul' if p.use_matmul else 'gather' - assert self._fprop_mode in valid_fprop_modes, ( - 'fprop_mode must be one of %r' % valid_fprop_modes) - - def _FpropImpl(self, embs, ids_vec): - """The embedding lookup implementation.""" - p = self.params - emb_shape_suf, weight_shape = self._GetWeightShape() - - def EmbBprop(xs, ys, dys): - """Embedding backprop. - - Effectively, it computes: - num = size of xs.ids_vec - dembs = zeros_like(xs.embs) - for i in range(num): - dembs[xs.ids_vec[i], :] += dys[i, :] - return dembs, zeros_like(xs.ids_vec) - - Args: - xs: A NestedMap containing: - - embs: The embedding matrix. Unused in the backprop. - - ids_vec: A vector of int32 embedding ids. - ys: Required by py_utils._DefineDefun, not used here. - dys: A matrix of size (size of xs.ids_vec, embedding dims). - - Returns: - A NestedMap containing: - - - embs: A matrix of the same shape of xs.embs. Gradients for xs.embs. - - ids_vec: Zeros. Same shape as xs.ids_vec. - """ - del ys - num = tf.shape(xs.ids_vec)[0] - dembs = inplace_ops.empty(weight_shape, py_utils.FPropDtype(p), init=True) - if len(weight_shape) != 2: - dys_shape = tf.shape(dys) - dys = tf.reshape(dys, [dys_shape[0]] + emb_shape_suf) - - def EmbBpropLoop(i, state): - # row_id = state.ids_vec[i] - row_id = tf.gather(state.ids_vec, i) - # row = state.drets[i] - row = tf.reshape(tf.gather(state.drets, i), [1] + emb_shape_suf) - # state.dembs[row_id] = row - state.dembs = inplace_ops.alias_inplace_add(state.dembs, [row_id], row) - return state - - dembs = py_utils.ForLoop( - body=EmbBpropLoop, - start=0, - limit=num, - delta=1, - loop_state=py_utils.NestedMap( - ids_vec=xs.ids_vec, drets=dys, dembs=dembs)).dembs - - if p.scale_sqrt_depth: - dembs *= p.embedding_dim**0.5 - - return py_utils.NestedMap(embs=dembs, ids_vec=tf.zeros_like(ids_vec)) - - def EmbFprop(xs): - """Embedding forward prop. - - Effectively, it computes: - num = size of xs.ids_vec - rets = zeros([num, embedding dim]) - for i in range(num): - rets[i, :] = xs.embs[xs.ids_vec[i], :] - return rets - - Args: - xs: A NestedMap containing: - - embs: The embedding matrix. - - ids_vec: A vector of int32 embedding ids. - - Returns: - The result of embedding lookups. A matrix of shape - [num ids in xs.ids_vec, embedding dims]. - """ - num = tf.shape(xs.ids_vec)[0] - rets = inplace_ops.empty([num] + emb_shape_suf, py_utils.FPropDtype(p)) - - def EmbFpropLoop(i, state): - # row_id = state.ids_vec[i] - row_id = tf.gather(state.ids_vec, i) - # row = state.embs[row_id] - row = tf.reshape(tf.gather(state.embs, row_id), [1] + emb_shape_suf) - # state.rets[i] = row - state.rets = inplace_ops.alias_inplace_update(state.rets, [i], row) - return state - - rets = py_utils.ForLoop( - body=EmbFpropLoop, - start=0, - limit=num, - delta=1, - loop_state=py_utils.NestedMap( - embs=xs.embs, ids_vec=xs.ids_vec, rets=rets)).rets - if len(weight_shape) > 2: - rets = tf.reshape(rets, [num, symbolic.ToStatic(p.embedding_dim)]) - return rets - - def EmbMatmul(xs): - """Lookups embedding vectors by doing Matmul with one-hot vector.""" - # lhs[i, j] is True iff xs.ids_vec[i] == j. - lhs = tf.equal( - tf.expand_dims(xs.ids_vec, 1), - tf.range(p.vocab_size, dtype=xs.ids_vec.dtype)) - return tf.matmul(tf.cast(lhs, xs.embs.dtype), xs.embs) - - def EmbGather(xs): - """Lookups embedding vectors.""" - return tf.nn.embedding_lookup(xs.embs, xs.ids_vec) - - xs = py_utils.NestedMap(embs=embs, ids_vec=ids_vec) - if self._fprop_mode == 'matmul': - return py_utils.CallDefun(EmbMatmul, xs) - elif self._fprop_mode == 'loop': - return py_utils.CallDefun( - EmbFprop, xs, bak=EmbBprop, bak_as_function=True) - elif self._fprop_mode == 'gather': - return EmbGather(xs) - - def _GetWeightShape(self): - p = self.params - if py_utils.tpu_compat() and self._fprop_mode != 'matmul': - if p.use_3d_weight_tensor: - assert symbolic.ToStatic(p.embedding_dim) % 128 == 0 - emb_shape_suf = [symbolic.ToStatic(p.embedding_dim) // 128, 128] - else: - emb_shape_suf = [symbolic.ToStatic(p.embedding_dim)] - else: - emb_shape_suf = [symbolic.ToStatic(p.embedding_dim)] - weight_shape = [p.vocab_size] + emb_shape_suf - return emb_shape_suf, weight_shape - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - _, weight_shape = self._GetWeightShape() - - # Define weights - pc = py_utils.WeightParams( - shape=weight_shape, - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - if p.apply_pruning: - mask_pc = py_utils.WeightParams(pc.shape, - py_utils.WeightInit.Constant(1.0), - p.dtype) - threshold_pc = py_utils.WeightParams([], - py_utils.WeightInit.Constant(0.0), - tf.float32) - self.CreateVariable('mask', mask_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'threshold', threshold_pc, theta_fn=None, trainable=False) - - def MaskWeightFn(weight): - return tf.multiply( - self.AddGlobalVN(weight), self.vars.mask, 'masked_weights') - - self.CreateVariable('wm', pc, theta_fn=MaskWeightFn) - pruning_utils.AddToPruningCollections(self.vars.wm, self.vars.mask, - self.vars.threshold) - else: - self.CreateVariable('wm', pc) - - def EmbLookupDefaultTheta(self, ids): - """Lookups embedding vectors for ids.""" - return self.FProp(self.theta, ids) - - def EmbLookup(self, theta, ids): - return self.FProp(theta, ids) - - def EmbLookupDefaultThetaOnCpu(self, ids): - """A faster path for CPU inference than the default gather.""" - p = self.params - embs = tf.nn.embedding_lookup(self.theta.wm, tf.reshape(ids, [-1])) - out_shape = tf.concat([tf.shape(ids), [symbolic.ToStatic(p.embedding_dim)]], - 0) - if p.scale_sqrt_depth: - embs *= p.embedding_dim**0.5 - return tf.reshape(embs, out_shape) - - def _FlatFProp(self, theta, ids): - """Lookups embedding vectors for ids. - - Args: - theta: Named tuple collection of weights for the layer. - ids: A rank-N int32 tensor. - - Returns: - A tuple of the flattened inputs to the embedding lookup, and a tensor that - is ready to be reshaped into the final shape in FProp. - """ - if not isinstance(ids, tf.Tensor): - tf.logging.warning('ids should be a tf.Tensor!') - ids = tf.convert_to_tensor(ids, tf.int32) - elif ids.dtype != tf.int32: - tf.logging.warning('ids should be tf.int32, but is %s!', ids.dtype) - ids = tf.cast(ids, tf.int32) - p = self.params - ids = py_utils.with_dependencies([ - py_utils.assert_between( - ids, 0, p.vocab_size, name='vocab_id_validation') - ], ids) - flat_ids = tf.reshape(ids, [-1]) - embs_result = self._FpropImpl(self.QWeight(theta.wm), flat_ids) - if p.vn.global_vn or p.vn.per_step_vn: - emb_noise = p.vn.scale * tf.random.normal( - tf.shape(embs_result), - stddev=1.0, - dtype=embs_result.dtype, - seed=p.random_seed) - embs_result += emb_noise - - if p.scale_sqrt_depth: - embs_result *= p.embedding_dim**0.5 - return flat_ids, embs_result - - def FProp(self, theta, ids): - """Lookups embedding vectors for ids. - - Args: - theta: Named tuple collection of weights for the layer. - ids: A rank-N int32 tensor. - - Returns: - A rank-(N+1) params.dtype tensor. - embs[indices, :] is the embedding vector for ids[indices]. - """ - _, embs_result = self._FlatFProp(theta, ids) - out_shape = tf.concat( - [tf.shape(ids), [symbolic.ToStatic(self.params.embedding_dim)]], 0) - return tf.reshape(embs_result, out_shape) - - -class OneHotEmbeddingLayer(base_layer.BaseLayer): - """Generates one-hot embeddings with uncertainties.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('vocab_size', 0, - 'Depth of the input. I.e., the number of classes.') - p.Define('embedding_dim', 0, 'Depth of the output.') - p.Define('uncertainty', 0.0, 'Uncertainty of the correct ID.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.vocab_size > 1 - assert p.embedding_dim == p.vocab_size - - def EmbLookupDefaultTheta(self, ids): - """Lookups embedding vectors for ids.""" - return self.FProp(self.theta, ids) - - def EmbLookup(self, theta, ids): - return self.FProp(theta, ids) - - def FProp(self, theta, ids): - """Lookups embedding vectors for ids. - - Args: - theta: Named tuple collection of weights for the layer. - ids: A rank-N int32 tensor. - - Returns: - A rank-(N+1) params.dtype tensor. - embs[indices, :] is the embedding vector for ids[indices]. - """ - del theta - p = self.params - ids = py_utils.with_dependencies([ - py_utils.assert_between( - ids, 0, p.vocab_size, name='vocab_id_validation') - ], ids) - low_confidence = p.uncertainty / tf.cast(p.vocab_size - 1, tf.float32) - high_confidence = 1.0 - p.uncertainty - embs_result = tf.one_hot( - ids, - depth=p.vocab_size, - on_value=high_confidence, - off_value=low_confidence) - if p.fprop_dtype is not None: - embs_result = tf.cast(embs_result, p.fprop_dtype) - return embs_result - - -class PositionalEmbeddingLayer(base_layer.BaseLayer): - """Generates sinusoidals with respect to the position in time and dimension. - - Implements the positional embedding layer from 'Attention is All You Need', - the Transformer Network. - - Code and comments are adapted from tensor2tensor/layers/common_attention.py - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'min_timescale', 1, 'Start of the geometric index.' - 'Determines the periodicity of the added signal.') - p.Define( - 'max_timescale', 10000, 'End of the geometric index. ' - 'Determines the frequency of the added signal.') - p.Define('embedding_dim', 0, 'Dimension of the embedding to be generated.') - p.Define( - 'trainable_scaling', False, - 'Introduces a trainable scaling parameter (a scalar) that' - ' multiplies the positional embedding in FProp.') - p.Define('trainable_scaling_init', 1.0, - 'Initial value of the scaling parameter.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.min_timescale - assert p.max_timescale - assert p.embedding_dim % 2 == 0 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - if p.trainable_scaling: - pc = py_utils.WeightParams( - shape=[1], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('scale', pc) - - def _PosEmbeddingsFromPositions(self, theta, position): - """Generates the positional embeddings given the position tensor. - - Factors out the common code from FProp and FPropWithPosition. Returns - positional embeddings corresponding to the input position tensor. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - position: Position tensor of dtype float and shape [bs, seq_length] to - generate positional embeddings. - - Returns: - a Tensor of shape [bs, seq_length, embedding_dim]. - """ - p = self.params - seq_length = tf.shape(position)[1] - num_timescales = p.embedding_dim // 2 - log_timescale_increment = ( - math.log(float(p.max_timescale) / float(p.min_timescale)) / tf.maximum( - tf.cast(1.0, py_utils.FPropDtype(p)), - tf.cast(num_timescales, py_utils.FPropDtype(p)) - 1)) - - inv_timescales = p.min_timescale * tf.exp( - tf.cast(tf.range(num_timescales), py_utils.FPropDtype(p)) * - -log_timescale_increment) - - scaled_time = tf.expand_dims(position, 2) * tf.reshape( - inv_timescales, [1, 1, -1]) - - signal = tf.concat([tf.sin(scaled_time), tf.cos(scaled_time)], axis=2) - signal = tf.pad( - signal, [[0, 0], [0, 0], [0, tf.math.floormod(p.embedding_dim, -1)]]) - signal = tf.reshape(signal, [-1, seq_length, p.embedding_dim]) - if p.trainable_scaling: - signal *= (p.trainable_scaling_init + theta.scale) - return signal - - def FProp(self, theta, seq_length): - """Generates a Tensor of sinusoids with different frequencies. - - Each channel (dimension) of the generated positionanl embedding Tensor - corresponds to a sinusoid of different frequency and phase. - - This allows attention to learn to use absolute and relative positions. - Timing signals should be added to some precursors of both the query and the - memory inputs to attention. - - The use of relative position is possible because sin(x+y) and cos(x+y) can - be experessed in terms of y, sin(x) and cos(x). - - In particular, we use a geometric sequence of timescales starting with - min_timescale and ending with max_timescale. The number of different - timescales is equal to channels (dimension) / 2. For each timescale, we - generate the two sinusoidal signals sin(timestep/timescale) and - cos(timestep/timescale). All of these sinusoids are concatenated in - the channels dimension. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - seq_length: Sequence length of the embeddings to be generated - - Returns: - a Tensor of shape [seq_length, embedding_dim]. - """ - p = self.params - position = tf.reshape( - tf.cast(tf.range(seq_length), py_utils.FPropDtype(p)), [1, seq_length]) - pos_emb = self._PosEmbeddingsFromPositions(theta, position) - return tf.reshape(pos_emb, [seq_length, -1]) - - def FPropWithPosition(self, theta, position_tensor): - """Generates a Tensor of sinusoids with different frequencies. - - Uses the provided position tensor to generate positional embeddings. Refer - to FProp description for details of sinusoidal positional embeddings. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - position_tensor: Position tensor of shape [bs, seq_length] to generate - positional embeddings. - - Returns: - a Tensor of shape [bs, seq_length, embedding_dim]. - """ - position = tf.cast(position_tensor, py_utils.FPropDtype(self.params)) - return self._PosEmbeddingsFromPositions(theta, position) - - -class RelativePositionalEmbeddingLayer(base_layer.BaseLayer): - """Relative positional embedding. - - Section 3.2 of https://arxiv.org/pdf/1803.02155.pdf - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'radius', None, - 'Radius of the relative window size. Distance are clipped to ' - '[-radius, radius].') - p.Define('dim', None, 'Dimension of embedding.') - return p - - def __init__(self, params): - super().__init__(params) - params = self.params - if not isinstance(params.radius, numbers.Integral) or params.radius <= 0: - raise ValueError('params.radius must be a positive int, but is %s' % - params.radius) - if not isinstance(params.dim, numbers.Integral) or params.dim <= 0: - raise ValueError('params.dim must be a positive int, but is %s' % - params.radius) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - pc = py_utils.WeightParams( - shape=[2 * self.params.radius + 1, self.params.dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=self.params.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w', pc) - - def FProp(self, theta, relative_distance): - """Computes relative positional embedding. - - Args: - theta: A NestedMap of Tensors of layer weights. - relative_distance: A Tensor. - - Returns: - A Tensor of shape relative_distance.shape + [params.dim] - """ - params = self.params - clipped_indices = tf.clip_by_value(relative_distance, -params.radius, - params.radius) - # Right-shift indices to make them all non-negative. - calibrated_indices = clipped_indices + params.radius - return tf.gather_nd(theta.w, tf.expand_dims(calibrated_indices, -1)) - - -class SinusoidalPositionalEmbeddingLayer(base_layer.BaseLayer): - """Generates sinusoidals with respect to the position in time and dimension. - - Implements the a variant of the positional embedding layer from 'Attention is - All You Need', the Transformer Network that doesn't require tuning of the - max_timescale/min_timescale. See this blog post and Ron's colab. - https://kazemnejad.com/blog/transformer_architecture_positional_encoding - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('embedding_dim', 0, 'Dimension of the embedding to be generated.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.embedding_dim % 2 != 0: - raise ValueError('embedding_dim needs to be even.') - - def FProp(self, theta, seq_length): - """Generates a Tensor of sinusoids with different frequencies. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - seq_length: Sequence length of the embeddings to be generated - - Returns: - a Tensor of shape [seq_length, embedding_dim]. - """ - p = self.params - positions = tf.cast(tf.range(seq_length), py_utils.FPropDtype(p)) - num_timescales = p.embedding_dim // 2 - freq = tf.range( - 1, num_timescales + 1, - dtype=py_utils.FPropDtype(p)) * (2 * math.pi / seq_length) - scaled_pos = tf.matmul(positions[:, tf.newaxis], freq[tf.newaxis, :]) - sincos = tf.concat([tf.sin(scaled_pos), tf.cos(scaled_pos)], axis=-1) - return tf.reshape(sincos, [seq_length, -1]) - - -class SoftmaxLayer(quant_utils.QuantizableLayer): - """Base class for softmax layers.""" - - @classmethod - def Params(cls): - """Params for SoftmaxLayer.""" - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the input.') - p.Define('num_classes', 0, 'Total number of target classes.') - p.Define( - 'logits_abs_max', None, 'If not None, logits are clipped to be within' - ' [-logits_abs_max, logits_abs_max]. This can be a scalar' - ' or a scalar tensor. Applies back pressure at training time; ignored' - ' for inference.') - p.Define( - 'chunk_size', 0, 'If non-zero, computes the per example ' - 'xent by small chunks along the batch dimension.') - return p - - def Logits(self, **unused): - """Returns the logits computed before the softmax.""" - raise NotImplementedError('GetLogits is not implemented.') - - def XentLossFromLogits(self, **unused): - """Returns the Xent loss from pre-computed logits.""" - raise NotImplementedError('XentLossFromLogits is not implemented.') - - def XentLoss(self, *args, **kwargs): - """Computes cross entropy.""" - return self.FProp(self.theta, *args, **kwargs) - - def _FProp2D(self, - theta, - inputs, - class_weights, - class_ids=None, - class_probabilities=None): - """Specialized FProp for matrix inputs.""" - raise NotImplementedError( - 'Subclasses of SoftmaxLayer must implement _FProp2D') - - def FProp(self, - theta, - inputs, - class_weights, - class_ids=None, - class_probabilities=None): - """Computes logit, cross entropy etc. - - This function can both work with class_ids, or probability distributions - over classes. Exactly one of class_ids or class_probabilities must be - provided. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: a list of a single tensor, or a single tensor with the shape [..., - input_dim]. - class_weights: a tensor with shape [...] containing the weights for each - target word. - class_ids: a tensor with shape [..., 1] of int32 dtype containing the - target class labels. - class_probabilities: a tensor with shape [..., num_classes] of float - values indicating class-membership probabilities. - - Returns: - A `.NestedMap` containing the following fields - - - logits: with shape [..., num_classes]. Unnormalized softmax's logits. - - per_example_argmax: with shape [...]. argmax of i-th example. - - per_example_xent: with shape [...]. Cross entropy between i-th example's - prediction and its label. - - per_example_weight: with shape [...]. class_weights casted to - this layer's dtype. - - total_xent: A scalar. The sum of per_example_weight * per_example_xent. - - total_weight: A scalar. The sum of per_example_weight. - - avg_xent: A scalar. total_loss / total_weight. - """ - p = self.params - - # Consolidate list/single value into a list. - if not isinstance(inputs, list): - inputs = [inputs] - - # If inputs are matrices already, delegate to _FProp2D. - if inputs[0].shape.ndims == 2: - return self._FProp2D(theta, inputs, class_weights, class_ids, - class_probabilities) - - # Remembers the original shape[1:-1]. - shape_mid = tf.shape(inputs[0])[1:-1] - - # Reshape inputs to matrices, labels to vectors, etc. - inputs = [ - tf.reshape(x, py_utils.ToStaticShape([-1, p.input_dim])) for x in inputs - ] - class_weights = tf.reshape(class_weights, [-1]) - if class_ids is not None: - class_ids = tf.reshape(class_ids, [-1, 1]) - if class_probabilities is not None: - class_probabilities = tf.reshape(class_probabilities, [-1, p.num_classes]) - - # Delegates to _FProp2D. - xent_loss = self._FProp2D(theta, inputs, class_weights, class_ids, - class_probabilities) - - # Reshapes xent_loss fields according to the inputs' shape. - xent_loss.logits = tf.reshape( - xent_loss.logits, tf.concat([[-1], shape_mid, [p.num_classes]], axis=0)) - per_example_shape = tf.concat([[-1], shape_mid], axis=0) - xent_loss.per_example_argmax = tf.reshape(xent_loss.per_example_argmax, - per_example_shape) - xent_loss.per_example_xent = tf.reshape(xent_loss.per_example_xent, - per_example_shape) - xent_loss.per_example_weight = tf.reshape(xent_loss.per_example_weight, - per_example_shape) - return xent_loss - - -class SimpleFullSoftmax(SoftmaxLayer): - """A somewhat simple softmax layer.""" - - @classmethod - def Params(cls): - """Params for SimpleFullSoftmax.""" - p = super().Params() - p.Define( - 'num_sampled', 0, 'Number of samples to use for the sampled soft-max. ' - 'Default value of 0 means no sampling is done; if set to > 0 then ' - 'training will use sampled soft-max when both chunk_size == 0 and ' - 'FProp is called with class_probabilities=None.') - p.Define( - 'num_shards', 1, - 'Number of shards to split params into. num_shards should' - ' divide num_classes.') - p.Define('apply_pruning', False, - 'Whether to prune the weights while training') - p.Define( - 'use_num_classes_major_weight', False, - 'Whether to use num_classes as major dimension for weight params. ' - 'This shows performance benefit especially when sharing embedding ' - 'and softmax. By removing the transpose before gather, it allows ' - 'better XLA fusions and optimizations.') - - p.Define( - 'use_bias', True, 'Whether or not to use a bias variable.' - 'Not using bias is not compatible with sampled softmax ' - '(num_sampled > 0).') - return p - - def __init__(self, params): - """Constructs a SimpleFullSoftmax layer.""" - super().__init__(params) - p = self.params - assert p.name - # We shard params across the class dimension. - assert p.num_classes % p.num_shards == 0 - if not p.use_bias: - assert p.num_sampled == 0, 'Sampled softmax requires bias.' - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - num_classes_per_shard = p.num_classes // p.num_shards - # When using sampled soft-max we'd rather work with weights of - # shape=[num_classes_per_shard, p.input_dim] to avoid an expensive transpose - # op before computing the sampled_softmax_loss. - self._transpose_weight_params = False - weights_shard_shape = [p.input_dim, num_classes_per_shard] - if p.num_sampled or p.use_num_classes_major_weight: - self._transpose_weight_params = True - weights_shard_shape = [num_classes_per_shard, p.input_dim] - - pc = py_utils.WeightParams( - shape=weights_shard_shape, - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - if p.apply_pruning: - mask_pc = py_utils.WeightParams(pc.shape, - py_utils.WeightInit.Constant(1.0), - p.dtype) - threshold_pc = py_utils.WeightParams([], - py_utils.WeightInit.Constant(0.0), - tf.float32) - - for i in range(p.num_shards): - weights_var_name = 'weight_%d' % i - if p.apply_pruning: - mask_var_name = 'mask_%d' % i - threshold_var_name = 'threshold_%d' % i - self.CreateVariable( - mask_var_name, mask_pc, theta_fn=None, trainable=False) - self.CreateVariable( - threshold_var_name, threshold_pc, theta_fn=None, trainable=False) - - def MaskWeightFn(weight): - return tf.multiply( - self.AddGlobalVN(weight), getattr(self.vars, mask_var_name), - 'masked_weights') - - self.CreateVariable(weights_var_name, pc, theta_fn=MaskWeightFn) - pruning_utils.AddToPruningCollections( - getattr(self.vars, weights_var_name), - getattr(self.vars, mask_var_name), - getattr(self.vars, threshold_var_name)) - - else: - self.CreateVariable(weights_var_name, pc, self.AddGlobalVN) - - pc = py_utils.WeightParams( - shape=[num_classes_per_shard], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - if p.use_bias: - for i in range(p.num_shards): - self.CreateVariable('bias_%d' % i, pc, self.AddGlobalVN) - - self.TrackQTensor('inputs', 'logits') - - def _GetInputs(self, inputs): - if isinstance(inputs, list): - assert len(inputs) == 1 - return inputs[0] - return inputs - - def _ConcatWeights(self, theta): - p = self.params - # Add per-step noise if configured so. - concat_axis = 1 - if self._transpose_weight_params: - concat_axis = 0 - weights = [ - self.QWeight(theta['weight_%d' % i]) for i in range(p.num_shards) - ] - new_theta = theta.copy() - if p.use_bias: - biases = [self.QWeight(theta['bias_%d' % i]) for i in range(p.num_shards)] - new_theta.bias = py_utils.AddPerStepVN(p, tf.concat(biases, axis=0)) - new_theta.wm = py_utils.AddPerStepVN(p, - tf.concat(weights, axis=concat_axis)) - return new_theta - - def _LogitsUsingConcatenatedWeightsHelper(self, theta, inputs): - p = self.params - inputs = self.QTensor('inputs', inputs) - wm = self.QWeight(theta.wm) - - if p.use_bias: - bias = self.QWeight(theta.bias) - - # x * w + b - # Note that theta.wm and theta.bias are transformed to concated/clipped - # by caller. - logits = tf.nn.bias_add( - py_utils.Matmul( - inputs, wm, transpose_b=self._transpose_weight_params), bias) - else: - logits = py_utils.Matmul( - inputs, wm, transpose_b=self._transpose_weight_params) - - # Clip logits by range. - # Note that this is generally not used in conjunction with quantization and - # shouldn't be needed at inference time as the quantized matmul above will - # take care of clipping naturally based on the data type and qparams. - abs_max = p.logits_abs_max - if abs_max is not None and not p.is_inference: - abs_min = -abs_max # pylint: disable=invalid-unary-operand-type - logits = py_utils.clip_by_value(logits, abs_min, abs_max) - return logits - - def _LogitsUsingConcatenatedWeights(self, theta, inputs): - logits = self._LogitsUsingConcatenatedWeightsHelper(theta, inputs) - return self.QTensor('logits', logits) - - def SimpleLogits(self, theta, inputs): - """Returns the simple logits computed before the softmax. - - Compared to the Logits function, this one has only weights, no bias for the - linear projection. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: A tensor with the shape [N, input_dim]. - - Returns: - logits: [N, num_classes] - """ - inputs = self.QTensor('inputs', inputs) - theta = self._ConcatWeights(theta) - wm = self.QWeight(theta.wm) - logits = py_utils.Matmul( - inputs, wm, transpose_b=self._transpose_weight_params) - - return self.QTensor('logits', logits) - - def Logits(self, theta, inputs): - """Returns the logits computed before the softmax. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: a list of a single tensor, or a single tensor with the shape [N, - input_dim]. - - Returns: - logits [batch, num_classes] - """ - return self._LogitsUsingConcatenatedWeights( - self._ConcatWeights(theta), self._GetInputs(inputs)) - - def _XentLossByChunk(self, theta, activation, class_ids): - """Computes per-example xent loss between activation and class_ids.""" - p = self.params - - # We reshape activation from a matrix to a 3-D tensor (a sequence - # of matrices), where the 2nd dimenion is p.chunk_size. Because - # the batch dimenion may not be multiple of p.chunk_size, we pad - # zeros. - activation = py_utils.HasRank(activation, 2) - batch, input_dim = tf.unstack(tf.shape(activation)) - dim0, dim1 = (batch + p.chunk_size - 1) // p.chunk_size, p.chunk_size - pad = dim0 * dim1 - batch - padded_activation = tf.concat( - [activation, - tf.zeros([pad, input_dim], dtype=activation.dtype)], - axis=0) - class_ids = py_utils.HasShape(class_ids, [batch, 1]) - padded_class_ids = tf.concat( - [class_ids, tf.zeros([pad, 1], dtype=class_ids.dtype)], axis=0) - - if py_utils.use_tpu(): - id_dtype = tf.int32 - else: - id_dtype = tf.int64 - padded_class_ids = tf.cast(padded_class_ids, id_dtype) - - # For each chunk, we compute logits of padded_activation[i, :, :], - # and its xent loss with padded_class_ids[i, :]. - def ChunkFn(theta, state0, inputs): - del state0 - activation, class_ids = inputs.activation, inputs.class_ids - logits = self._LogitsUsingConcatenatedWeights(theta, activation) - xent = tf.nn.sparse_softmax_cross_entropy_with_logits( - logits=logits, labels=class_ids) - amax = tf.stop_gradient(py_utils.ArgMax(logits)) - return py_utils.NestedMap(xent=xent, amax=amax), py_utils.NestedMap() - - acc, _ = recurrent.Recurrent( - theta=self._ConcatWeights(theta), - state0=py_utils.NestedMap( - xent=tf.zeros([p.chunk_size], dtype=p.dtype), - amax=tf.zeros([p.chunk_size], dtype=id_dtype)), - inputs=py_utils.NestedMap( - activation=tf.reshape(padded_activation, [dim0, dim1, input_dim]), - class_ids=tf.reshape(padded_class_ids, [dim0, dim1])), - cell_fn=ChunkFn) - - # acc.xent has the shape [dim0, dim1]. acc.xent[i, :] are - # per-example xent loss for examples in the i-th chunk. We - # reshape acc.xent to a vector and slice the first 'batch' values. - def GetBatch(x): - return tf.reshape(x, [-1])[:batch] - - return GetBatch(acc.xent), GetBatch(acc.amax) - - def _FProp2D(self, - theta, - inputs, - class_weights, - class_ids=None, - class_probabilities=None): - """Computes xent loss and log-prob logit.""" - p = self.params - inputs = self._GetInputs(inputs) - logits = self.Logits(theta, inputs) - if class_probabilities is not None: - per_example_xent, per_example_argmax = self.XentLossFromLogits( - theta, logits, class_weights, class_ids, class_probabilities) - elif p.chunk_size: - class_ids = py_utils.HasShape(class_ids, [-1, 1]) - per_example_xent, per_example_argmax = self._XentLossByChunk( - theta, inputs, class_ids) - elif p.num_sampled == 0 or self.do_eval: - per_example_xent, per_example_argmax = self.XentLossFromLogits( - theta, logits, class_weights, class_ids, class_probabilities) - else: # Use sampled soft-max in training mode with p.num_sampled set. - assert p.num_sampled > 0 - assert p.use_bias - tf.logging.vlog( - 0, 'Using sampled_softmax_loss(..., num_sampled=%d, ' - 'num_classes=%d) in SimpleFullSoftmax::_FProp2D', p.num_sampled, - p.num_classes) - per_example_xent = tf.nn.sampled_softmax_loss( - weights=[theta['weight_%d' % i] for i in range(p.num_shards)], - biases=tf.concat([theta['bias_%d' % i] for i in range(p.num_shards)], - axis=0), - labels=tf.reshape(class_ids, [-1, 1]), - inputs=self._GetInputs(inputs), - num_sampled=p.num_sampled, - num_classes=p.num_classes, - seed=p.random_seed) - # Avoid computing logits; per_example_argmax is going to be always right. - per_example_argmax = tf.identity(class_ids) - - label_weights = tf.reshape( - tf.cast(class_weights, py_utils.FPropDtype(p)), [-1]) - total_xent = tf.reduce_sum(per_example_xent * label_weights) - total_weights = tf.reduce_sum(label_weights) - return py_utils.NestedMap( - logits=logits, - log_probs=tf.nn.log_softmax(logits), - per_example_argmax=per_example_argmax, - per_example_xent=per_example_xent, - per_example_weight=label_weights, - total_xent=total_xent, - total_weight=total_weights, - avg_xent=total_xent / total_weights) - - def XentLossFromLogits(self, - theta, - logits, - class_weights, - class_ids=None, - class_probabilities=None): - """Computes cross-entropy, argmax etc. from logits.""" - p = self.params - assert logits is not None - if class_probabilities is not None: - per_example_xent = tf.nn.softmax_cross_entropy_with_logits( - labels=class_probabilities, logits=logits) - per_example_argmax = py_utils.ArgMax(logits) - elif p.num_sampled == 0 or self.do_eval: - assert class_ids is not None - tf.logging.vlog( - 0, 'Using sparse_softmax_cross_entropy_with_logits() in ' - 'SimpleFullSoftmax::_FProp2D logits_shape=%r', - py_utils.GetShape(logits)) - per_example_xent = tf.nn.sparse_softmax_cross_entropy_with_logits( - labels=tf.reshape(class_ids, [-1]), logits=logits) - per_example_argmax = py_utils.ArgMax(logits) - else: - raise ValueError( - 'This set of arguments is not supported for XentLossFromLogits.') - return per_example_xent, per_example_argmax - - -class SharedSoftmaxLayer(SimpleFullSoftmax): - """Shared softmax layer for decoder embedding/softmax matrix.""" - - @classmethod - def Params(cls): - """Params for SharedSoftmaxLayer.""" - p = super().Params() - p.Define( - 'scale_sqrt_depth', False, 'If set True, activations are scaled' - ' with sqrt(input_dim) in EmbLookup.') - p.Define( - 'embedding_dim', 0, 'Set to be compatible with embedding layer, ' - ' and it is equivalent to input_dim') - p.Define( - 'vocab_size', 0, 'Set to be compatible with embedding layer, and ' - 'it is equivalent to num_classes') - return p - - def EmbLookup(self, theta, ids): - p = self.params - ids = py_utils.with_dependencies([ - py_utils.assert_between( - ids, - 0, - p.num_classes, - summarize=100000, - message='{}:class_id_validation'.format(p.cls)) - ], ids) - - wm = self._ConcatWeights(theta).wm - if not self._transpose_weight_params: - wm = tf.transpose(wm) - embs_result = tf.gather(wm, ids) - - if p.scale_sqrt_depth: - embs_result *= p.input_dim**0.5 - - return embs_result - - -class SingleShardFullSoftmax(SoftmaxLayer): - """Full softmax layer.""" - - def __init__(self, params): - """Constructs a SingleShardFullSoftmax layer.""" - super().__init__(params) - p = self.params - assert p.name - linear_p = builder_layers.LinearLayer.Params().Set( - name='linear', input_dims=p.input_dim, output_dims=p.num_classes) - self.CreateChild('linear', linear_p) - bias_p = builder_layers.BiasLayer.Params().Set( - name='bias', dims=p.num_classes) - self.CreateChild('bias', bias_p) - - def Logits(self, theta, inputs): - """Returns the logits computed before the softmax. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: A single tensor with shape [..., input_dim]. - - Returns: - logits [..., num_classes] - """ - p = self.params - if isinstance(inputs, (list, tuple)): - assert len(inputs) == 1 - inputs = inputs[0] - after_proj = self.linear.FProp(theta.linear, inputs) - logits = self.bias.FProp(theta.bias, after_proj) - # Clip logits by range. - # Note that this is generally not used in conjunction with quantization and - # shouldn't be needed at inference time as the quantized matmul above will - # take care of clipping naturally based on the data type and qparams. - abs_max = p.logits_abs_max - if abs_max is not None and not p.is_inference: - abs_min = -abs_max # pylint: disable=invalid-unary-operand-type - logits = py_utils.clip_by_value(logits, abs_min, abs_max) - return logits - - def XentLossFromLogits(self, - theta, - logits, - class_ids=None, - class_probabilities=None): - """Computes cross-entropy, argmax etc. from logits.""" - assert logits is not None - if class_probabilities is not None: - per_example_xent = tf.nn.softmax_cross_entropy_with_logits( - labels=class_probabilities, logits=logits) - per_example_argmax = tf.stop_gradient(py_utils.ArgMax(logits)) - else: - assert class_ids is not None - per_example_xent = tf.nn.sparse_softmax_cross_entropy_with_logits( - labels=class_ids, logits=logits) - per_example_argmax = tf.stop_gradient(py_utils.ArgMax(logits)) - return per_example_xent, per_example_argmax - - def XentLossByChunk(self, theta, activation, class_ids, class_probabilities): - """Computes per-example xent loss.""" - p = self.params - - act_orig_shape = tf.shape(activation) - batch_size = act_orig_shape[0] - chunk_size = p.chunk_size - num_chunks = batch_size // chunk_size - - num_chunks = py_utils.with_dependencies([ - py_utils.assert_equal( - 0, - tf.math.floormod(batch_size, chunk_size), - summarize=2, - message='assert_equal') - ], num_chunks) - - def ReshapeX(x): - if x is None: - return None - x_shape = tf.shape(x) - new_shape = tf.concat([[num_chunks, chunk_size], x_shape[1:]], 0) - return tf.reshape(x, new_shape) - - activation = ReshapeX(activation) - class_ids = ReshapeX(class_ids) - class_probabilities = ReshapeX(class_probabilities) - - # For each chunk, we compute logits of activation[i, :, :], - # and its xent loss with class_ids[i, :]. - def ChunkFn(theta, state0, inputs): - del state0 - activation = inputs.activation - class_ids = inputs.get('class_ids', None) - class_probabilities = inputs.get('class_probabilities', None) - logits = self.Logits(theta, activation) - per_example_xent, per_example_argmax = self.XentLossFromLogits( - theta, logits, class_ids, class_probabilities) - return py_utils.NestedMap( - xent=per_example_xent, amax=per_example_argmax), py_utils.NestedMap() - - inputs_nmap = py_utils.NestedMap(activation=activation) - if class_ids is not None: - inputs_nmap.class_ids = class_ids - if class_probabilities is not None: - inputs_nmap.class_probabilities = class_probabilities - - xent_state0 = tf.zeros(tf.shape(activation)[1:-1], dtype=p.dtype) - argmax_out_dtype = tf.int32 if py_utils.use_tpu() else tf.int64 - amax_state0 = tf.zeros(tf.shape(activation)[1:-1], dtype=argmax_out_dtype) - - acc, _ = recurrent.Recurrent( - theta=theta, - state0=py_utils.NestedMap(xent=xent_state0, amax=amax_state0), - inputs=inputs_nmap, - cell_fn=ChunkFn) - - # acc.xent has the shape [dim0, dim1]. acc.xent[i, :] are - # per-example xent loss for examples in the i-th chunk. We - # reshape acc.xent to a vector and slice the first 'batch' values. - def GetBatch(x): - return tf.reshape(x, act_orig_shape[:-1]) - - return GetBatch(acc.xent), GetBatch(acc.amax) - - def FProp(self, - theta, - inputs, - class_weights, - class_ids=None, - class_probabilities=None): - """Computes logits, cross entropy etc. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: a single tensor with shape [..., input_dim]. - class_weights: a tensor with shape [..., 1] containing the weights for - each target word. - class_ids: a tensor with shape [..., 1] of int32 dtype containing the - target class labels. - class_probabilities: a tensor with shape [..., num_classes] of float - values indicating class-membership probabilities. - - Returns: - A `.NestedMap` containing the following fields - - - logits: with shape [..., num_classes]. Unnormalized softmax's logits. - - per_example_argmax: with shape [...]. argmax of i-th example. - - per_example_xent: with shape [...]. Cross entropy between i-th example's - prediction and its label. - - per_example_weight: with shape [...]. class_weights casted to - this layer's dtype. - - total_xent: A scalar. The sum of per_example_weight * per_example_xent. - - total_weight: A scalar. The sum of per_example_weight. - - avg_xent: A scalar. total_loss / total_weight. - """ - p = self.params - if isinstance(inputs, (list, tuple)): - assert len(inputs) == 1 - inputs = inputs[0] - - inputs_shape = tf.shape(inputs) - ids_shape = tf.concat([inputs_shape[:-1], [1]], 0) - probs_shape = tf.concat([inputs_shape[:-1], [p.num_classes]], 0) - - class_weights = py_utils.HasShape(class_weights, ids_shape) - class_weights = tf.squeeze(class_weights, -1) - if class_ids is not None: - class_ids = py_utils.HasShape(class_ids, ids_shape) - class_ids = tf.squeeze(class_ids, -1) - if class_probabilities is not None: - class_probabilities = py_utils.HasShape(class_probabilities, probs_shape) - - if (not self.do_eval) and (p.chunk_size > 0): - # Chunking. - logits = None - log_probs = None - per_example_xent, per_example_argmax = self.XentLossByChunk( - theta, inputs, class_ids, class_probabilities) - else: - logits = self.Logits(theta, inputs) - log_probs = tf.nn.log_softmax(logits) - per_example_xent, per_example_argmax = self.XentLossFromLogits( - theta, logits, class_ids, class_probabilities) - - label_weights = tf.cast(class_weights, py_utils.FPropDtype(p)) - total_xent = tf.reduce_sum(per_example_xent * label_weights) - total_weights = tf.reduce_sum(label_weights) - output_nmap = py_utils.NestedMap( - per_example_argmax=per_example_argmax, - per_example_xent=per_example_xent, - per_example_weight=label_weights, - total_xent=total_xent, - total_weight=total_weights, - avg_xent=total_xent / (total_weights + 1e-6)) - if logits is not None: - output_nmap.logits = logits - output_nmap.log_probs = log_probs - return output_nmap - - -class SingleShardSharedEmbeddingSoftmax(SingleShardFullSoftmax): - """A shared softmax/embedding layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('vocab_size', 0, 'Num tokens in vocab.') - p.Define('embedding_dim', 0, 'Depth of the output.') - p.Define( - 'scale_sqrt_depth', False, 'If set True, activations are scaled' - ' with sqrt(embedding_dim) in EmbLookup.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.vocab_size == p.num_classes - assert p.embedding_dim == p.input_dim - - def EmbLookupDefaultTheta(self, ids): - return self.EmbLookup(self.theta, ids) - - def EmbLookup(self, theta, ids): - """Looks up embedding vectors for ids. - - Args: - theta: Named tuple with the weight matrix for the embedding. - ids: A rank-N int32 tensor. - - Returns: - A rank-(N+1) params.dtype tensor. - embs[indices, :] is the embedding vector for ids[indices]. - """ - p = self.params - ids = tf.convert_to_tensor(ids) - ids = py_utils.with_dependencies([ - py_utils.assert_between( - ids, 0, p.vocab_size, name='vocab_id_validation') - ], ids) - # TODO(yonghui): Get rid of this extra copy (tf.transpose). - emb_vars = tf.transpose(theta.linear.w) - embs = tf.nn.embedding_lookup(emb_vars, tf.reshape(ids, [-1])) - if p.scale_sqrt_depth: - embs *= p.embedding_dim**0.5 - if p.vn.global_vn or p.vn.per_step_vn: - embs = py_utils.AddGlobalVN(p, embs) - out_shape = tf.concat([tf.shape(ids), [p.embedding_dim]], 0) - return tf.reshape(embs, out_shape) - - -class ConvSoftmax(quant_utils.QuantizableLayer): - """A softmax implementation based on 1x1 convolution. - - On TPU this is much more memory efficient than MatMul after reshaping logits - to a matrix. - """ - - @classmethod - def Params(cls): - """Params for SoftmaxLayer.""" - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the input.') - p.Define('hidden_dim', 0, 'Dimension of the hidden layer.') - p.Define('num_classes', 0, 'Total number of target classes.') - return p - - def _CreateLayerVariables(self): - """Constructs a SimpleFullSoftmax layer.""" - super()._CreateLayerVariables() - p = self.params - if p.hidden_dim: - w_proj_pc = py_utils.WeightParams( - shape=(1, p.input_dim, p.hidden_dim), - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w_proj', w_proj_pc) - w_pc = py_utils.WeightParams( - shape=(1, p.hidden_dim or p.input_dim, p.num_classes), - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w', w_pc) - self.CreateVariable( - 'b', - py_utils.WeightParams( - shape=[p.num_classes], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'])) - - def Logits(self, theta, inputs): - p = self.params - with tf.name_scope(p.name): - if inputs.shape.ndims == 2: - # [batch, time, depth] - x = inputs[:, tf.newaxis, :] - else: - x = py_utils.HasShape(inputs, [-1, -1, -1]) - if p.hidden_dim: - x = tf.nn.conv1d(x, theta.w_proj, 1, 'VALID') - logits = tf.nn.bias_add(tf.nn.conv1d(x, theta.w, 1, 'VALID'), theta.b) - if inputs.shape.ndims == 2: - return logits[:, 0, :] - else: - return logits - - -class DropoutLayer(base_layer.BaseLayer): - """Apply dropout during trainig.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('keep_prob', 1.0, 'Keep probability.') - # noise_shape is unknown when building layer params. - p.Define( - 'noise_shape', None, 'A 1-D `Tensor` of type `int32`, representing' - ' the shape for randomly generated keep/drop flags.') - p.Define( - 'noise_shape_broadcast_dims', None, - 'A list of dimension where the noise shape is broadcasted. For ' - 'example, noise_shape = [n, h, w, 1] when ' - 'noise_shape_broadcast_dims=[-1] ') - # We typically want to replace dropout by expectation during eval. - # However, in certain cases E(f(x)) != f(E(x)), and replacing dropout by its - # expectation during eval leads to worse quality. - p.Define('dropout_at_eval', False, - 'Whether or not to also perform dropout at eval time.') - return p - - def _Dropout(self, theta, inputs, noise_shape): - return tf.nn.dropout( - inputs, - rate=1 - self.params.keep_prob, - noise_shape=noise_shape, - seed=self.params.random_seed) - - @classmethod - def NumOutputNodes(cls, p): - # The layer does element-wise processing thus is input-shape agnostic. - return - - def FProp(self, theta, inputs): - """Apply dropout to inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. - - Returns: - inputs with dropout applied at training time. - """ - p = self.params - if not self.do_eval or p.dropout_at_eval: - if isinstance(p.keep_prob, numbers.Real) and p.keep_prob == 1.0: - return inputs - if p.noise_shape_broadcast_dims: - noise_shape = p.noise_shape or py_utils.GetShape(inputs) - for dim in p.noise_shape_broadcast_dims: - if dim >= len(noise_shape): - raise ValueError('Invalid broadcasted dim {}'.format(dim)) - noise_shape[dim] = 1 - else: - noise_shape = p.noise_shape - ret = self._Dropout(theta, inputs, noise_shape) - ret.set_shape(inputs.get_shape()) - return ret - else: - return inputs - - @classmethod - def FPropMeta(cls, p, inputs, *args): - py_utils.CheckShapes((inputs,)) - flops_per_element = 10 # Approximately 10 flops per element. - return py_utils.NestedMap( - flops=inputs.num_elements() * flops_per_element, out_shapes=(inputs,)) - - -class DeterministicDropoutLayer(DropoutLayer): - """Apply dropout during trainig.""" - - def _Dropout(self, theta, inputs, noise_shape): - return py_utils.DeterministicDropout( - inputs, - keep_prob=self.params.keep_prob, - seeds=py_utils.GenerateStepSeedPair(self.params, theta.global_step), - noise_shape=noise_shape) - - -class LayerNorm(base_layer.BaseLayer): - """Layer normalization. - - Implements layer normalization: - https://arxiv.org/abs/1607.06450 - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Depth of the input to the network.') - p.Define('epsilon', 1e-6, 'Tiny value to guard rsqrt.') - p.Define('use_fused_layernorm', False, 'Whether to use fused layernorm.') - p.Define( - 'direct_scale', False, 'Whether to apply scale directly ' - 'without a +1.0. Var is initialized to 1.0 instead. This makes ' - 'the layer weight-compatible with the implementation in ' - 'contrib.layers.') - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.input_dim > 0 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - pc = py_utils.WeightParams( - shape=[p.input_dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'] + - [py_utils.SKIP_LP_REGULARIZATION]) - self.CreateVariable('bias', pc) - - if p.direct_scale: - scale_pc = py_utils.WeightParams( - shape=[p.input_dim], - init=py_utils.WeightInit.Constant(1.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'] + - [py_utils.SKIP_LP_REGULARIZATION]) - else: - scale_pc = pc - self.CreateVariable('scale', scale_pc) - - def _GetScaleAndBias(self, theta): - return theta.scale, theta.bias - - def FProp(self, theta, inputs): - """Applies normalization over the last dimension (layer). - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: A tensor of shape [..., hidden_dim]. - - Returns: - tensor of the same shape with inputs - """ - p = self.params - inputs = py_utils.with_dependencies( - [py_utils.assert_equal(tf.shape(inputs)[-1], p.input_dim)], inputs) - - cur_scale, cur_bias = self._GetScaleAndBias(theta) - - if p.direct_scale: - scale = cur_scale - else: - scale = 1.0 + cur_scale - - if p.use_fused_layernorm: - counts, means_ss, variance_ss, _, = tf.nn.sufficient_statistics( - inputs, axes=[-1], keepdims=True) - mean, variance = tf.nn.normalize_moments(counts, means_ss, variance_ss, - None) - inputs_norm = (inputs - mean) * tf.math.rsqrt(variance + p.epsilon) - return inputs_norm * scale + cur_bias - - def Normalize(xs): - """Normalize `xs.x` w/ `xs.scale` and `xs.bias` gain/shift.""" - x_shape = py_utils.GetShape(xs.x) - inner_dim = x_shape[-1] - x_reshaped = tf.reshape(xs.x, [-1, inner_dim]) - mean = tf.reduce_mean(x_reshaped, axis=[1], keepdims=True) - variance = tf.reduce_mean( - tf.square(x_reshaped - mean), axis=[1], keepdims=True) - x_norm = (x_reshaped - mean) * tf.math.rsqrt(variance + p.epsilon) - x_norm = tf.reshape(x_norm, x_shape) - return x_norm * xs.scale + xs.bias - - return py_utils.CallDefun( - Normalize, py_utils.NestedMap(x=inputs, scale=scale, bias=cur_bias)) - - @classmethod - def NumOutputNodes(cls, p): - return p.input_dim - - @classmethod - def FPropMeta(cls, p, inputs): - py_utils.CheckShapes((inputs,)) - return py_utils.NestedMap( - flops=inputs.num_elements() * 10, out_shapes=(inputs,)) - - -class CategoricalLayerNorm(LayerNorm): - """Categorical layer normalization. - - Allow dynamic switch of normalization params based on given class_index. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_classes', 1, - 'Number of privatized copies of layer norm params.') - return p - - def _BiasVarName(self, i): - return 'bias_' + str(i) - - def _ScaleVarName(self, i): - return 'scale_' + str(i) - - def _CreateLayerVariables(self): - # Skip LayerNorm's _CreateLayerVariables() as bias and scale variables will - # be created in this function. - super(LayerNorm, self)._CreateLayerVariables() # pylint: disable=bad-super-call - p = self.params - pc = py_utils.WeightParams( - shape=[self.params.input_dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars'] + - [py_utils.SKIP_LP_REGULARIZATION]) - for i in range(p.num_classes): - self.CreateVariable(self._BiasVarName(i), pc) - self.CreateVariable(self._ScaleVarName(i), pc) - - def __init__(self, params): - super().__init__(params) - p = self.params - assert isinstance(p.num_classes, int) - assert p.num_classes > 0 - self.AddExtraTheta('class_index', tf.constant(0, dtype=tf.int32)) - - def _GetScaleAndBias(self, theta): - p = self.params - with tf.control_dependencies( - [py_utils.assert_between(theta.class_index, 0, p.num_classes)]): - biases = [theta[self._BiasVarName(i)] for i in range(p.num_classes)] - cur_bias = tf.gather(biases, theta.class_index) - scales = [theta[self._ScaleVarName(i)] for i in range(p.num_classes)] - cur_scale = tf.gather(scales, theta.class_index) - return cur_scale, cur_bias - - -class ConvSetLayer(quant_utils.QuantizableLayer): - """Set of Convolutions with different filter sizes in a single layer. - - Applies a set of convolutions with different filter shapes to the inputs and - returns the concatenated outputs. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('cnn_tpl', - ConvLayer.Params().Set(filter_stride=(1, 1)), - 'Conv layer template for the set of conv layers.') - p.Define( - 'filter_shapes', [(0, 0, 0, 0)], - 'Must be a list of sequences of 4. Elements are in order of height' - ' (time), width (frequency), in_channel, out_channel') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - - filter_set = set() - input_shape = None - # Asserting kernel sizes are different and input sizes are the same. - for filter_shape in p.filter_shapes: - key = '%d_%d' % (filter_shape[0], filter_shape[1]) - assert key not in filter_set - filter_set.add(key) - if input_shape is None: - input_shape = filter_shape[2] - assert input_shape == filter_shape[2] - - params_conv_set = [] - for filter_shape in p.filter_shapes: - conv_p = p.cnn_tpl.Copy() - conv_p.name = '%d_%d' % (filter_shape[0], filter_shape[1]) - # Important: combined quantization will be done pre-concat versus - # by each layer on its output. Otherwise, inherit quantization params - # from this layer. - if p.qdomain.default is not None: - conv_p.qdomain.default = p.qdomain.default.Copy() - conv_p.disable_activation_quantization = True - conv_p.filter_shape = filter_shape - params_conv_set.append(conv_p) - self.CreateChildren('conv_set', params_conv_set) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - # The same QTensor is used for all inputs to the concat. - self.TrackQTensor('activation') - - def FProp(self, theta, inputs, paddings): - """Apply all convolution sets to inputs and concatenate outputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: The inputs tensor. It is expected to be of shape [batch, time, - frequency, channel]. The time dimension corresponds to the height - dimension as in images and the frequency dimension corresponds to the - width dimension as in images. - paddings: The paddings tensor. It is expected to be of shape [batch, - time]. - - Returns: - A tuple (out, output_paddings). - - - out: output tensor. Expected to be of shape [batch, time_mod, - frequency_mod, out_channel_1 + out_channel_2 ...] where time_mod and - frequency_mod depend on the conv layer strides and out_channel_i is - the output channel size of the i-th conv layer in the set. - - output_paddings: Modified paddings generated within `ConvLayer.FProp`. - Expected to be of the shape [batch, time_mod]. - """ - p = self.params - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(paddings), [-1, -1]), - py_utils.assert_shape_match( - tf.shape(inputs), - tf.concat([tf.shape(paddings), [-1, p.filter_shapes[0][2]]], 0)) - ], inputs) - - conv_outputs = [] - output_paddings = None - # output_padding should be same for all filters for the same stride. - for i, conv_i in enumerate(self.conv_set): - conv_i_output, conv_i_padding = conv_i.FProp(theta.conv_set[i], inputs, - paddings) - if output_paddings is None: - output_paddings = conv_i_padding - conv_outputs.append(conv_i_output) - - # Track for quantization. - conv_outputs = [self.QTensor('activation', t) for t in conv_outputs] - - out = tf.concat(conv_outputs, -1) - return out, output_paddings - - -class LocalizedLabelSmoother(base_layer.BaseLayer): - """Smooths labels given as class ids. - - Implements the smoothing from https://arxiv.org/abs/1612.02695. Instead of - 1-hot class ids the model is trained to predict a distribution over classes - that includes the correct class label and with a small probability the labels - of tokens that appear nearby in time in the ground truth. This typically acts - as a strong regularizer. - - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_classes', 0, 'Number of classes') - p.Define( - 'offsets', [], 'Offset (over time) for smoothing. At time T the ' - 'smoothed target is class[T] + sum_i weights[i]*class[T+offset[i]]') - p.Define('weights', [], 'Weight of the smoothing at corresponding offset') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.num_classes > 0 - assert len(p.offsets) == len(p.weights) - assert p.name - - def FProp(self, theta, target_paddings, target_labels, target_ids): - """Convert class_ids to 1hot and smooth by neighborhood. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - target_paddings: float32 matrix [bs, seq_len] - target_labels: int32 matrix [bs, seq_len]. This stores the target label - output at each decoder step as generated by the speech input generator - input_batch.tgt.labels - target_ids: int32 matrix [bs, seq_len]. This stores the target_id that is - fed to the decoder, as generated by the speech input generator - input_batch.tgt.ids - - Returns: - A tensor [bs, seq_len, num_classes] denoting a smoothed distribution over - num_classes. - """ - del target_ids # Unused. - p = self.params - class_probabilities = tf.one_hot( - target_labels, p.num_classes, dtype=py_utils.FPropDtype(p)) - - # Start list keeping the scaled class-probabilities at different offsets. - output_distributions = [class_probabilities] - seq_len = tf.shape(class_probabilities)[1] - # If offsets < 0 we force a future output_act to be like a past token. - # If offsets > 0 we force a past output_act to be like a future token. - min_offset = np.min(p.offsets + [0]) - max_offset = np.max(p.offsets + [0]) - class_probabilities = tf.pad(class_probabilities, - [[0, 0], [-min_offset, max_offset], [0, 0]]) - # Shift the weights to the left by one location - we don't make the - # EOS more probable. - class_weights = tf.pad(1.0 - target_paddings[:, 1:], - [[0, 0], [-min_offset, max_offset + 1]]) - class_weights = tf.expand_dims(class_weights, 2) - - for offset, weight in zip(p.offsets, p.weights): - offset_in_padded = offset - min_offset - output_distributions.append( - class_probabilities[:, offset_in_padded:offset_in_padded + seq_len, :] - * class_weights[:, offset_in_padded:offset_in_padded + seq_len, :] * - weight) - output_distributions = tf.add_n(output_distributions) - output_distributions /= tf.reduce_sum( - output_distributions, axis=-1, keepdims=True) - return output_distributions - - -class UniformLabelSmoother(base_layer.BaseLayer): - """Smooths labels given as class ids and confidence. - - Implements the smoothing from https://arxiv.org/abs/1512.00567. Correct class - label confidence is dropped by eps and all the other classes are increased - by eps/num_classes. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_classes', 0, 'Number of classes') - p.Define('uncertainty', 0.1, 'Uncertainty of correct label, eps.') - p.Define( - 'uncertainty_larger', 0.1, - 'Apply a larger uncertainty to specific tokens, as specified ' - 'by token_from_target_ids.') - p.Define('token_id_uncertainty_larger', None, 'Id of token from target_ids ' - 'to apply uncertainty_larger to.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.num_classes > 0 - assert 0.0 <= p.uncertainty < 1.0 - assert p.token_id_uncertainty_larger is None or ( - p.token_id_uncertainty_larger >= 0) - assert p.name - - def FProp(self, theta, target_paddings, target_labels, target_ids): - """Convert target_labels to 1hot and smooth uniformly. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - target_paddings: float32 matrix [bs, seq_len] - target_labels: int32 matrix [bs, seq_len]. This stores the target label - output at each decoder step as generated by the speech input generator - input_batch.tgt.labels - target_ids: int32 matrix [bs, seq_len]. This stores the target_id that is - fed to the decoder, as generated by the speech input generator - input_batch.tgt.ids - - Returns: - A tensor of float32 [bs, seq_len, num_classes] denoting a smoothed - distribution over num_classes. - """ - del target_paddings # Unused by FProp. - p = self.params - - low_confidence = p.uncertainty / tf.cast(p.num_classes - 1, tf.float32) - high_confidence = (1.0 - p.uncertainty) - - smooth_targets = tf.one_hot( - tf.cast(target_labels, tf.int32), - depth=p.num_classes, - on_value=high_confidence, - off_value=low_confidence) - if p.token_id_uncertainty_larger is not None: - assert target_ids is not None - low_confidence_larger = p.uncertainty_larger / tf.cast( - p.num_classes - 1, tf.float32) - high_confidence_larger = (1.0 - p.uncertainty_larger) - smooth_targets_larger = tf.one_hot( - tf.cast(target_labels, tf.int32), - depth=p.num_classes, - on_value=high_confidence_larger, - off_value=low_confidence_larger) - should_smooth_larger = tf.tile( - tf.expand_dims( - tf.equal(target_ids, p.token_id_uncertainty_larger), -1), - multiples=[1, 1, p.num_classes]) - smooth_targets = tf.where(should_smooth_larger, smooth_targets_larger, - smooth_targets) - return smooth_targets - - -class HighwaySkipLayer(base_layer.BaseLayer): - """A highway skip layer. - - This class represents a highway skip layer, which takes multiple - inputs (from different layers of the network) and gates them. - This returns C(x)x + T(x)h, initially biasing C to be open. - For some discussion about initialization please see: - Section 2.2 in [Srivastava, 2015]: https://arxiv.org/pdf/1505.00387v2.pdf - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the input to the network.') - p.Define( - 'batch_norm', False, - 'Whether or not to apply BN to the highway skip layer output. ' - 'Note this is only a single bool.') - p.Define('carry_bias_init', 1.0, 'carry gates bias initialization') - p.Define('couple_carry_transform_gates', False, - 'Boolean on whether to couple the transform and carry gates.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - carry_gate_params = ProjectionLayer.Params().Set( - batch_norm=p.batch_norm, - has_bias=True, - activation='SIGMOID', - input_dim=p.input_dim, - output_dim=p.input_dim, - bias_init=p.carry_bias_init, - name='%s_carry_gate' % p.name) - self.CreateChild('carry_gate', carry_gate_params) - - if not p.couple_carry_transform_gates: - transform_gate_params = ProjectionLayer.Params().Set( - batch_norm=p.batch_norm, - has_bias=True, - activation='SIGMOID', - input_dim=p.input_dim, - output_dim=p.input_dim, - bias_init=-p.carry_bias_init, - name='%s_transform_gate' % p.name) - self.CreateChild('transform_gate', transform_gate_params) - - def FProp(self, theta, x, transformed_x, paddings=None): - """Fprop for Highway Skip layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - x: feature at the lower layer. - transformed_x: transformation of x at a higher layer. - paddings: padding applied to the features. - - Returns: - layer_out - activations after forward propagation. - """ - p = self.params - assert self.carry_gate is not None - carry = self.carry_gate.FProp(theta.carry_gate, x, paddings) - if p.couple_carry_transform_gates: - transform = 1 - carry - else: - assert self.transform_gate is not None - transform = self.transform_gate.FProp(theta.transform_gate, x, paddings) - layer_out = x * carry + transformed_x * transform - return layer_out - - -class GatingLayer(base_layer.BaseLayer): - """A gating layer. - - This class represents a gating layer, which takes 2 inputs of the same shape - and gates them. - - The output is: carry * x + (1 - carry) * y where, carry is given by - sigmoid(x @ w_1 + y @ w_2 + bias). - - This is different from the HighwaySkipLayer above in that carry is also a - function of y (named transformed_x in HighwaySkipLayer). - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the input to the network.') - p.Define('has_bias', False, 'Whether carry has a bias term.') - p.Define('carry_bias_init', 0.0, 'carry gates bias initialization') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - carry_gate_params = ProjectionLayer.Params().Set( - batch_norm=False, - has_bias=p.has_bias, - activation='SIGMOID', - input_dim=p.input_dim * 2, - output_dim=p.input_dim, - bias_init=p.carry_bias_init, - name='carry') - self.CreateChild('carry_gate', carry_gate_params) - - def FProp(self, theta, x, y, paddings=None): - """Fprop for the gating layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - x: An input feature, the last dimension must match p.input_dim. - y: Another input feature. Must have the same shape as 'x'. - paddings: padding applied to the features. When x and y have shape [..., - input_dim], 'paddings', when specified, must have shaped [..., 1], where - all but the last dimension match. - - Returns: - layer_out - activations after forward propagation. Same shape as x and y. - """ - y = py_utils.with_dependencies( - [py_utils.assert_shape_match(tf.shape(x), tf.shape(y))], y) - carry = self.carry_gate.FProp(theta.carry_gate, tf.concat([x, y], axis=-1), - paddings) - layer_out = x * carry + y * (1 - carry) - return layer_out - - -class GradNormTracker(base_layer.BaseLayer): - """A helper class to keep track of gradient norm stats.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('decay', 0.995, - 'Decay in updating the moving avgs in grad norm stats') - p.Define('grad_norm_lower_cap', 1e-2, 'The minimal gradient norm value.') - p.Define( - 'clip_threshold', 4.0, - 'Distance threshold at which gradients are clipped to 0.0.' - ' Distance is measured in the number of standard deviations a' - ' given gradient norm is from the mean gradient norm. The' - ' default value of 4.0 means we are throwing away roughly' - ' 0.15% of steps.') - p.Define( - 'grad_norm_clip_cap_min', 0.0, - 'We stop clipping if grad norm is already smaller than this' - ' value.') - return p - - def __init__(self, params): - super().__init__(params) - self._decay = params.decay - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - - pc = py_utils.WeightParams( - shape=[], - init=py_utils.WeightInit.Constant(0.0), - dtype=tf.float32, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('log_mean', pc, trainable=False) - self.CreateVariable('log_mean_squared', pc, trainable=False) - self.CreateVariable('total_weight', pc, trainable=False) - self.CreateVariable('total_rejections', pc, trainable=False) - - def FProp(self, theta, grad_norm, has_nan=None): - """Update gradient norm moving avgs, and returns whether or not ... - - to clip gradients to 0.0. If the current batch has NaN grads, does not - update the moving avgs and forces to clip the gradients to 0.0. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - grad_norm: A float scalar tensor. - has_nan: A boolean scalar tensor to indicate if the current batch has nan. - - Returns: - A scalar float tensor with value of either 1.0 or 0.0. The value of 0.0 - means the gradient norm is excessively large or contains NaN, and the step - should be aborted completely. - """ - p = self.params - with tf.name_scope(p.name): - grad_norm = tf.maximum(grad_norm, p.grad_norm_lower_cap) - - # Exponentially decayed moving avg of log(grad_norm) mean. - mean = theta.log_mean / tf.maximum(theta.total_weight, 1e-6) - # Exponentially decayed moving avg of log(grad_norm) variance. - var = ((theta.log_mean_squared / tf.maximum(theta.total_weight, 1e-6)) - - mean * mean) - std = tf.sqrt(tf.maximum(var, 1e-6)) - - summary_utils.scalar('log_grad_norm_mean', mean) - summary_utils.scalar('log_grad_norm_std', std) - summary_utils.scalar('clip_ratio_threshold', - tf.exp(std * p.clip_threshold)) - summary_utils.scalar('clip_threshold', - tf.exp(mean + std * p.clip_threshold) - 1.0) - summary_utils.scalar('total_rejections', theta.total_rejections) - - log_grad_norm = tf.math.log(grad_norm + 1.0) - log_grad_norm_cap = tf.cast(mean + std * p.clip_threshold, tf.float32) - log_grad_norm_cap_min = tf.math.log(p.grad_norm_clip_cap_min + 1.0) - log_grad_norm_cap = tf.maximum(log_grad_norm_cap, log_grad_norm_cap_min) - - def UpdateExpMovingAvg(ref_var, val, ignore): - if ignore is not None: - delta = tf.where(ignore, tf.zeros([]), - (1.0 - p.decay) * (val - ref_var)) - else: - delta = (1.0 - p.decay) * (val - ref_var) - return tf.assign_add(ref_var, delta) - - # We trigger when total_weight is at least half of max weight or the - # current batch contains NaNs. - trigger = tf.math.logical_and(log_grad_norm > log_grad_norm_cap, - theta.total_weight > 0.75) - if has_nan is not None: - trigger = tf.math.logical_or(trigger, has_nan) - - log_grad_norm_capped = tf.minimum(log_grad_norm, log_grad_norm_cap) - - update_moving_avg = tf.group( - UpdateExpMovingAvg(self.vars.log_mean, log_grad_norm_capped, has_nan), - UpdateExpMovingAvg(self.vars.log_mean_squared, - log_grad_norm_capped * log_grad_norm_capped, - has_nan), - UpdateExpMovingAvg(self.vars.total_weight, tf.constant(1.0), has_nan), - tf.assign_add(self.vars.total_rejections, - tf.cast(trigger, tf.float32))) - - return py_utils.with_dependencies([update_moving_avg], - 1.0 - tf.cast(trigger, tf.float32)) - - -class WeightedSumLayer(base_layer.BaseLayer): - """Returns the weighted sum of a list of input tensors.""" - - @classmethod - def Params(cls): - """Params for this MergerLayer class.""" - p = super().Params() - p.Define('num_sources', 0, 'Number of input sources to combine.') - p.Define('weighted_merger_dropout_prob', 0.1, - 'Applies dropout to the weights.') - p.Define( - 'weighted_merger_softmax', True, 'If set, applies a softmax ' - 'layer on top of the weights for normalization.') - p.Define('global_weight_scale', 1.0, 'A global scale put on weights.') - p.Define('minimal_prob', 0.0, 'The minimal weight for each component.') - p.Define('add_weight_summaries', False, 'If set, creates summaries for the ' - 'sum weights.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if not p.name: - raise ValueError('Layer must have a specified name!') - - assert p.num_sources > 0, ('Must specify num_sources > 0.') - - if p.weighted_merger_dropout_prob > 0.0: - dropout_tpl = DropoutLayer.Params() - dropout_tpl.keep_prob = (1.0 - p.weighted_merger_dropout_prob) - self.CreateChild('weighted_merger_dropout', dropout_tpl) - else: - self.CreateChild('weighted_merger_dropout', IdentityLayer.Params()) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - params_init = py_utils.WeightInit.Constant(0.0) - # Weights to be learned. - pw = py_utils.WeightParams( - shape=[p.num_sources], - init=params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('sum_weight', pw) - - def FProp(self, theta, inputs): - """Combines the list of input tensors into a single tensor. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: A list of tensors of shape [time, batch, hidden_dim] - - Returns: - A tensor of the same shape with input tensors. - """ - p = self.params - n_sources = len(inputs) - - if n_sources == 1: - return inputs[0] - - # Weighted sum of all sources, all dims must match. - # For weighted_sum, assume input is a list of rank 3 tensors - inputs = tf.stack(inputs) - inputs = py_utils.HasRank(inputs, 4) - - # The constant factor is just meant to support the non-normalized scenario. - # If softmax is applied, this factor will cancel out. - w = theta.sum_weight * p.global_weight_scale + (1 / p.num_sources) - w = self.weighted_merger_dropout.FProp(theta.weighted_merger_dropout, w) - - if p.weighted_merger_softmax: - residual_weights = p.minimal_prob * p.num_sources - assert residual_weights >= 0.0 - assert residual_weights < 1.0 - w = tf.nn.softmax(w, axis=0) * (1.0 - residual_weights) + p.minimal_prob - - if p.add_weight_summaries: - for i in range(p.num_sources): - summary_utils.scalar(p.name + 'weight_%d' % i, w[i]) - w = tf.reshape(w, [p.num_sources, 1, 1, 1]) - output = tf.reduce_sum(inputs * w, axis=0) - - return output - - -class GatedAverageLayer(base_layer.BaseLayer): - """Gated combination of n input vectors. - - Given n inputs, x_1 ... x_n. First learns a gate g in a single layer. - Returns g_1 * x_1 + ... g_n * x_n. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_nodes', 0, 'Number of nodes in each input vector.') - p.Define('num_inputs', 0, 'Number of input vectors to combine.') - return p - - def __init__(self, params): - """Initializes GatedAverageLayer.""" - super().__init__(params) - p = self.params - - assert p.num_nodes > 0, 'Number of dimensions should be greater than 0.' - assert p.num_inputs > 0, 'Number of inputs should be greater than 0.' - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - in_size = p.num_inputs * p.num_nodes - - # Weight matrix for scalar gates - gm_pc = py_utils.WeightParams( - shape=[in_size, p.num_inputs], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('gm', gm_pc) - - def FProp(self, theta, inputs): - """Gates, then merges a list of n input vectors. - - Args: - theta: gm (gate matrix) - inputs: List of inputs, each of shape [..., num_nodes] - - Returns: - a gated output vector [..., num_nodes] - """ - p = self.params - assert len(inputs) == p.num_inputs, 'Number of inputs should match params.' - - for i, inp in enumerate(inputs): - inputs[i] = py_utils.with_dependencies([ - py_utils.assert_shape_match([tf.shape(inp)[-1]], [p.num_nodes]), - py_utils.assert_shape_match(tf.shape(inp), tf.shape(inputs[0])), - ], inp) - - input_shape = tf.shape(inputs[0]) - - reshaped_inputs = [tf.reshape(inp, [-1, p.num_nodes]) for inp in inputs] - concat_inputs = tf.concat(reshaped_inputs, axis=1) - - xmg = tf.nn.softmax(py_utils.Matmul(concat_inputs, theta.gm)) - xmg = tf.expand_dims(xmg, 2) - inputs = tf.reshape(concat_inputs, [-1, p.num_inputs, p.num_nodes]) - gated_sum = tf.reduce_sum(xmg * inputs, axis=1) - - return tf.reshape(gated_sum, input_shape) - - -class LHUCLayer(base_layer.BaseLayer): - """`Learning Hidden Unit Contribution (LHUC)` layer. - - This paper proposes to use LHUC layer for NMT adaptation: - http://aclweb.org/anthology/N18-2080 - - During base model training, LHUC layer is fixed to 1.0 (no-op in - multiplication). During adaptation, only LHUC layer is trained, and all other - parameters in the model are frozen. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the input and output.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.input_dim > 0 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - pc = py_utils.WeightParams( - shape=[p.input_dim], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('w', pc) - - def FProp(self, theta, inp): - """Add learnt gate for adaptation.""" - out = 2.0 * tf.sigmoid(theta.w) * inp - return out - - -class ResidualAdapterLayer(base_layer.BaseLayer): - """Residual Adapter layer for NLP tasks. - - This paper proposes using residual adapters for fine-tuning new tasks on BERT. - https://arxiv.org/pdf/1902.00751.pdf - - During adaptation, residual adapter layers can be added to a pre-trained - model and trained, while all other parameters are frozen. - In terms of operations, the layer is identical to a vanilla Transformer - feedforward layer. Separate implementation is meant to distinguish function. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the input to the adapter.') - p.Define('bottleneck_dim', 0, 'Dimension of the feedforward inner layer.') - p.Define('ln_tpl', LayerNorm.Params(), 'Layer norm default params.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - - bottleneck_params = FeedForwardNet.Params().Set( - name='bottleneck', - activation=['RELU', 'NONE'], - input_dim=p.input_dim, - hidden_layer_dims=[p.bottleneck_dim, p.input_dim]) - self.CreateChild('bottleneck', bottleneck_params) - - params = p.ln_tpl.Copy() - params.name = 'adapter_ln' - params.input_dim = p.input_dim - self.CreateChild('layer_norm', params) - - def FProp(self, theta, x, paddings=None): - """Fprop for Residual Adapter. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - x: [..., input_dim]. - paddings: padding applied to the features. - - Returns: - layer_out - [..., input_dim]. - """ - normalized_x = self.layer_norm.FProp(theta.layer_norm, x) - bottleneck_x = self.bottleneck.FProp(theta.bottleneck, normalized_x, - paddings) - return x + bottleneck_x - - -def Conv2DFlops(inputs, filter_shape, stride, padding): - """Returns number of float operations (mult/adds) for a Conv2D op. - - Args: - inputs: the input shape. Must have four elements. - filter_shape: the convolution filter shape. Must have four elements. - stride: the strides along height and width, respectively. - padding: 'SAME' or 'VALID'. - - Returns: - Number of multiplications and additions. - """ - b, h, w = inputs[0], inputs[1], inputs[2] - fh, fw, ic, oc = filter_shape - sh, sw = stride - - def _CeilDiv(x, y): - return tf.math.floordiv(x + y - 1, y) - - if padding == 'SAME': - oh = _CeilDiv(h, sh) - ow = _CeilDiv(w, sw) - else: - assert padding == 'VALID' - oh = _CeilDiv(h - fh + 1, sh) - ow = _CeilDiv(w - fw + 1, sw) - # Mul/add counts as 2 flops. - return (tf.cast(b * oh * ow, tf.int64) * - tf.cast(fh * fw * ic * oc, tf.int64) * 2) - - -class Conv2DLayerNoPadding(base_layer.BaseLayer): - """2-D Convolution layer w/o padding. - - TODO(laurenzo): Dedup in favor of SeparableConv2DLayer where possible. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'filter_shape', (0, 0, 0, 0), - 'Filter shape. Must be a sequence of length 4. Elements are in' - ' the order of height (time), width (frequency), in_channel,' - ' out_channel. ') - p.Define( - 'filter_stride', (0, 0), - 'Filter stride to use. Must be a pair of ints. The first int' - ' specifies the stride on the height dimension. The second int' - ' specifies the stride on the width dimension.') - p.Define( - 'dilations', (1, 1), ' An optional list of ints. Defaults to [1, 1]. ' - '1-D tensor of length 2. The dilation factor for each dimension ' - 'of input. If set to k > 1, there will be k-1 skipped cells ' - 'between each filter element on that dimension.') - p.Define('padding', 'SAME', 'SAME|VALID') - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.padding in ['SAME', 'VALID'] - assert len(p.filter_shape) == 4 - assert len(p.filter_stride) == 2 - assert len(p.dilations) == 2 - assert all(x > 0 for x in p.filter_stride) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - w_pc = py_utils.WeightParams( - shape=p.filter_shape, - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w', w_pc) - - def FProp(self, theta, x): - """Apply convolution to inputs. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - x: The inputs tensor. It is expected to be of shape [batch, height, width, - channel]. - - Returns: - Convolution output. - """ - p = self.params - with tf.name_scope(p.name): - computation_cost.Add( - self, 'flops', - Conv2DFlops( - tf.shape(x), - filter_shape=symbolic.EvalExpr(symbolic.TENSOR_VALUES, - p.filter_shape), - stride=p.filter_stride, - padding=p.padding)) - return tf.nn.conv2d( - input=x, - filters=theta.w, - strides=[1, p.filter_stride[0], p.filter_stride[1], 1], - padding=p.padding, - dilations=[1, p.dilations[0], p.dilations[1], 1], - data_format='NHWC') - - @classmethod - def FPropMeta(cls, p, inputs): - py_utils.CheckShapes((inputs,)) - b, h, w, c = inputs - fh, fw, ic, oc = p.filter_shape - assert ic == c - sh, sw = p.filter_stride - if p.padding == 'SAME': - oh = sympy.ceiling(h / sh) - ow = sympy.ceiling(w / sw) - else: - oh = sympy.ceiling((h - fh + 1) / sh) - ow = sympy.ceiling((w - fw + 1) / sw) - flops = b * oh * ow * fh * fw * ic * oc * 2 # mul/add counts as 2 flop. - outputs = tshape.Shape([b, oh, ow, oc]) - return py_utils.NestedMap(flops=flops, out_shapes=(outputs,)) - - -class FetchLayer(base_layer.BaseLayer): - """A layer facilitating fetching activations and their gradients.""" - - def __init__(self, params): - super().__init__(params) - assert self.params.name - self._activations = None - self._gradients = None - - @classmethod - def FPropMeta(cls, params, *args): - return py_utils.NestedMap(flops=0, out_shapes=args) - - def _ReturnSingleValueOrList(self, lst): - assert lst is not None - assert isinstance(lst, list) - return lst if len(lst) > 1 else lst[0] - - @property - def activation(self): - return self._ReturnSingleValueOrList(self._activations) - - @property - def gradient(self): - return self._ReturnSingleValueOrList(self._gradients) - - def FProp(self, theta, *args): - del theta - num = len(args) - self._activations = [None] * num - self._gradients = [None] * num - - for i, v in enumerate(args): - - def FetchBak(xs, ys, dys, index=i): - del xs, ys - self._gradients[index] = dys - return dys - - def FetchFwd(x): - return x - - self._activations[i] = py_utils.CallDefun(FetchFwd, v, bak=FetchBak) - - return tuple(self._activations) if num > 1 else self._activations[0] - - -class GluLayer(base_layer.BaseLayer): - """Gated Linear Unit. - - See https://arxiv.org/abs/1612.08083 for more details. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the layer input.') - p.Define('output_dim', 0, 'Dimension of the layer output.') - p.Define('ln_tpl', LayerNorm.Params(), 'Layer norm default params.') - p.Define('dense_tpl', FCLayer.Params().Set(), 'Fully connected layer.') - p.Define( - 'activation', 'RELU', - 'Non-linearity applied after the dense layer in the value branch.') - p.Define('dropout_tpl', DropoutLayer.Params(), 'Dropout applied to output.') - p.Define('apply_residual', True, 'Whether or not to add inputs to outputs.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.input_dim - - if p.output_dim: - output_dim = p.output_dim - else: - output_dim = p.input_dim - - if p.apply_residual: - assert output_dim == p.input_dim - - # Initialize value feed-forward layer. - params = p.dense_tpl.Copy() - params.name = 'value_layer' - params.input_dim = p.input_dim - params.activation = p.activation - params.output_dim = output_dim - self.CreateChild('value_layer', params) - - # Initialize gate feed-forward layer. - params = p.dense_tpl.Copy() - params.name = 'gate_layer' - params.input_dim = p.input_dim - params.activation = 'SIGMOID' - params.output_dim = output_dim - self.CreateChild('gate_layer', params) - - # Initialize layer norm. - params = p.ln_tpl.Copy() - params.name = 'layer_norm' - params.input_dim = p.input_dim - self.CreateChild('layer_norm', params) - - # Initialize dropout. - dropout_tpl = p.dropout_tpl.Copy() - self.CreateChild('dropout', dropout_tpl) - - def FProp(self, theta, inputs, paddings): - inputs_normalized = self.layer_norm.FProp(theta.layer_norm, inputs) - values = self.value_layer.FProp(theta.value_layer, inputs_normalized, - tf.expand_dims(paddings, -1)) - gates = self.gate_layer.FProp(theta.gate_layer, inputs_normalized, - tf.expand_dims(paddings, -1)) - glu_output = values * gates - glu_output = self.dropout.FProp(theta.dropout, glu_output) - if self.params.apply_residual: - return inputs + glu_output - return glu_output - - -class MultitaskAdapterLayer(base_layer.BaseLayer): - """Residual adapter layer for multilingual models. - - Residual adapters can be used to fine-tune a single model to multiple - domains, tasks, or languages: https://arxiv.org/pdf/1902.00751.pdf - - Each adapter consists of a "down" projection to a smaller dimension followed - by an "up" projection, the result of which is added back to the input - activation. The projection weights and biases are task-specific. - - Whereas ResidualAdapterLayer learns and applies the parameters for a single - task, this layer learns and applies the parameters for multiple tasks so that - we have a single model serving the different tasks. The parameters can be - trained for all tasks at the same time, or in one-off per-task training jobs. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_tasks', 0, 'Number of tasks.') - p.Define('input_dim', 0, 'Dimension of the input to the adapter.') - p.Define('bottleneck_dim', 0, 'Dimension of the bottleneck.') - p.Define('layer_norm_tpl', LayerNorm.Params(), 'Layer norm default params.') - p.Define( - 'projection_params_init', None, - 'Weight initialization for up and down projections. Only used for ' - 'weights, not biases. If None, uses default weight init, which is ' - 'typically Xavier with scale of 1.0.') - p.Define( - 'data_format', 'TBC', 'String(enum) specifying the input and output ' - 'data format for this layer. Supported formats: ' - '"TBC": [time, batch, input_dim] and "BTC": [batch, time, input_dim].') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - # Data format is either 'TBC' (time-major) or 'BTC' (batch-major). - assert p.data_format in ('TBC', 'BTC') - base_emb_params = EmbeddingLayer.Params().Set( - vocab_size=p.num_tasks, max_num_shards=1) - down_proj_w_params = base_emb_params.Copy() - down_proj_w_params.Set( - embedding_dim=p.input_dim * p.bottleneck_dim, name='down_proj_w') - if p.projection_params_init: - down_proj_w_params.params_init = p.projection_params_init - down_proj_b_params = base_emb_params.Copy() - down_proj_b_params.Set(embedding_dim=p.bottleneck_dim, name='down_proj_b') - up_proj_w_params = base_emb_params.Copy() - up_proj_w_params.Set( - embedding_dim=p.bottleneck_dim * p.input_dim, name='up_proj_w') - if p.projection_params_init: - up_proj_w_params.params_init = p.projection_params_init - up_proj_b_params = base_emb_params.Copy() - up_proj_b_params.Set(embedding_dim=p.input_dim, name='up_proj_b') - - self.CreateChild('down_proj_w', down_proj_w_params) - self.CreateChild('down_proj_b', down_proj_b_params) - self.CreateChild('up_proj_w', up_proj_w_params) - self.CreateChild('up_proj_b', up_proj_b_params) - params = p.layer_norm_tpl.Copy() - params.name = 'adapter_ln' - params.input_dim = p.input_dim - self.CreateChild('layer_norm', params) - - def FProp(self, theta, inputs, tasks): - """Fprop for multitask adapter. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - inputs: A tensor containing the activations from the previous layer. For - 'TBC', the shape is [time, batch, input_dim] and for 'BTC', it's [batch, - time, input_dim]. - tasks: An int32 tensor containing the task ID for each input. If 'tasks' - is of rank 2, we assume it to be of shape [time, batch] if 'BTC' and - [batch, time] if 'TBC', indicating a different task for each timestep. - In this case we look up adapter params for each timestep. If 'tasks' is - of rank 1, we assume it to be of shape [batch], indicating a single task - for all timesteps of a sequence. This latter setup uses substantially - less memory and is generally preferred. - - Returns: - A tensor containing the adapted activations with shape - [time, batch, input_dim] for 'TBC' and [batch, time, input_dim] for 'BTC'. - """ - p = self.params - inputs_shape = tf.shape(inputs) - per_timestep_task = (tasks.shape.ndims == 2) - batch_index = 1 if p.data_format == 'TBC' else 0 - time_index = 1 - batch_index - inputs = py_utils.with_dependencies( - [ - # Checks that inputs has 3 dimensions, last is hidden dim. - py_utils.assert_shape_match(inputs_shape, [-1, -1, p.input_dim]), - # Checks that inputs and tasks have same batch dimension. - py_utils.assert_shape_match([inputs_shape[batch_index]], [ - tf.shape(tasks)[batch_index] - if per_timestep_task else tf.shape(tasks)[0] - ]) - ], - inputs) - - # To support different task for each timetstep, flatten inputs and - # tasks. Below, 'batch' now refers to flattened batch size, time * batch. - if per_timestep_task: - tasks = py_utils.with_dependencies( - [ - # Checks that inputs and tasks have same time dimension. - py_utils.assert_shape_match(inputs_shape[:1], - tf.shape(tasks)[:1]) - ], - tasks) - tasks = tf.reshape(tasks, [-1]) - if p.data_format == 'TBC': - inputs = tf.reshape(inputs, [1, -1, p.input_dim]) - else: - inputs = tf.reshape(inputs, [-1, 1, p.input_dim]) - - # Lookup all weights and biases - # [batch] -> [batch, hidden * k] -> [batch, hidden, k] - down_weights = tf.reshape( - self.down_proj_w.EmbLookup(theta.down_proj_w, tasks), - [-1, p.input_dim, p.bottleneck_dim]) - # [batch] -> [batch, k] -> [1, batch, k] if 'TBC' else [batch, 1, k] - down_biases = tf.expand_dims( - self.down_proj_b.EmbLookup(theta.down_proj_b, tasks), time_index) - # [batch] -> [batch, k * hidden] -> [batch, k, hidden] - up_weights = tf.reshape( - self.up_proj_w.EmbLookup(theta.up_proj_w, tasks), - [-1, p.bottleneck_dim, p.input_dim]) - # [batch] -> [batch, h] -> [1, batch, h] if 'TBC' else [batch, 1, h] - up_biases = tf.expand_dims( - self.up_proj_b.EmbLookup(theta.up_proj_b, tasks), time_index) - - # Layer norm -> down-projection -> non-linearity -> up-projection - norm_inputs = self.layer_norm.FProp(theta.layer_norm, inputs) - # If per_timestep_task, t = 1, b = time * batch. - # Otherwise, t = time, b = batch. - if p.data_format == 'TBC': - down_projected = tf.einsum('tbh,bhk->tbk', norm_inputs, down_weights) - else: - down_projected = tf.einsum('bth,bhk->btk', norm_inputs, down_weights) - down_projected += down_biases - down_projected = tf.nn.relu(down_projected) - if p.data_format == 'TBC': - up_projected = tf.einsum('tbk,bkh->tbh', down_projected, up_weights) - else: - up_projected = tf.einsum('btk,bkh->bth', down_projected, up_weights) - up_projected += up_biases - output = inputs + up_projected - - # Unflatten output: - # for 'TBC': [1, time * batch, hidden] -> [time, batch, hidden] - # for 'BTC': [1, batch * time, hidden] -> [batch, time, hidden] - if per_timestep_task: - output = tf.reshape(output, inputs_shape) - return output - - -class CCTGatingNetwork(quant_utils.QuantizableLayer): - """A gating network that is continous for training and discrete for eval. - - Based on the gating network from https://arxiv.org/abs/2002.07106. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Depth of the input to the network.') - p.Define('hidden_layer_dim', 0, 'Depth of the hidden layer outputs.') - p.Define('num_outputs', 0, 'Number of scalar gate outputs.') - p.Define('noise_std', 1.0, 'Standard deviation for gating noise.') - p.Define('noise_warmup_steps', 1.0, 'Steps to full noise.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - params = schedule.PolynomialSchedule.Params() - params.start = (0, 0.0) - params.limit = (p.noise_warmup_steps, p.noise_std) - self.CreateChild('noise_std', params) - - params = FeedForwardNet.Params() - params.name = 'gating_layer' - params.input_dim = p.input_dim - params.activation = ['RELU', 'NONE'] - params.hidden_layer_dims = [p.hidden_layer_dim, p.num_outputs] - self.CreateChild('gatingfflayer', params) - - def FProp(self, theta, inputs, paddings=None): - p = self.params - p_c = self.gatingfflayer.FProp(theta.gatingfflayer, inputs, paddings) - if self.do_eval: - ones = tf.ones(tf.shape(p_c), py_utils.FPropDtype(p)) - zeros = tf.zeros(tf.shape(p_c), py_utils.FPropDtype(p)) - p_c = tf.where( - tf.greater_equal(p_c, tf.constant(0.0, dtype=py_utils.FPropDtype(p))), - ones, zeros) - else: - noise_std = self.noise_std.FProp(theta.noise_std, theta.global_step) - noise = py_utils.DeterministicVN( - p, - py_utils.GenerateStepSeedPair(p, theta.global_step), - tf.shape(p_c), - std=noise_std) - p_c = tf.nn.sigmoid(p_c + noise) - return p_c - - @classmethod - def FPropMeta(cls, p, inputs, paddings=None): - py_utils.CheckShapes((inputs,)) - assert inputs[-1] == p.input_dim - flops = 0 - in_dim = inputs[-1] - other_dims = inputs.num_elements() / in_dim - flops = 5 * other_dims * in_dim * p.hidden_layer_dim - flops = 5 * other_dims * p.num_outputs * p.hidden_layer_dim - out_shape = tshape.Shape(inputs[:-1] + [symbolic.ToStatic(p.num_outputs)]) - return py_utils.NestedMap(flops=flops, out_shapes=(out_shape,)) - - -class CondScaleShiftFFNLayer(base_layer.BaseLayer): - """Feature Modulation layer. - - https://distill.pub/2018/feature-wise-transformations/ - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Depth of the input.') - p.Define('output_dim', 0, 'Depth of the output.') - p.Define('ffn', FeedForwardNet.Params(), 'Projection layer params') - p.Define('scale_fn', 'NONE', - 'The activation function to use for scale output') - p.Define('shift_fn', 'NONE', - 'The activation function to use for shift output') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - - output_dim = p.output_dim * 2 # 1st split for shift, 2nd split for scale - params_ffn = p.ffn.Copy().Set( - input_dim=p.input_dim, name='{}_ffn'.format(p.name)) - params_fcout = FCLayer.Params().Copy().Set( - input_dim=params_ffn.hidden_layer_dims[-1], - output_dim=output_dim, - activation='NONE', - name='{}_fcout'.format(p.name)) - self.CreateChild('ffn', params_ffn) - self.CreateChild('fcout', params_fcout) - - def FProp(self, theta, inputs, paddings=None): - """Calculate scale shift and modify input. - - Args: - theta: params. - inputs: The input tensor. Shaped [..., input_dim]. - paddings: The input padding tensors. - - Returns: - Output after calculating shift and scale (2 tensors). - Shaped [..., output_dim]. - """ - p = self.params - - ffn_output = self.ffn.FProp(theta.ffn, inputs, paddings) - fcout_output = self.fcout.FProp(theta.fcout, ffn_output, paddings) - scale_output, shift_output = tf.split( - fcout_output, num_or_size_splits=2, axis=-1) - - def OpWrapper(name, tensor): - """Wrapper for retrieve tf operations.""" - if name in _ACTIVATIONS: - op = _ACTIVATIONS[name] - else: - if name == 'EXP': - op = tf.exp - elif name == 'NONE': - op = tf.identity - else: - raise ValueError() - return op(tensor) - - scale_output = OpWrapper(p.scale_fn, scale_output) - shift_output = OpWrapper(p.shift_fn, shift_output) - return scale_output, shift_output diff --git a/lingvo/core/layers_test.py b/lingvo/core/layers_test.py deleted file mode 100644 index be2b33d5..00000000 --- a/lingvo/core/layers_test.py +++ /dev/null @@ -1,5338 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for layers.""" - -import math - -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import bn_layers -from lingvo.core import cluster_factory -from lingvo.core import gpipe -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import symbolic -from lingvo.core import test_utils -from lingvo.core import tshape -import numpy as np - - -class ActivationsTest(test_utils.TestCase): - - def testGeluActivation(self): - with self.session(use_gpu=True): - inputs = tf.constant( - np.linspace(-10.0, 10.0, num=21, dtype='float32'), dtype=tf.float32) - grads_gelu = tf.gradients(layers.Gelu(inputs), inputs) - grads_relu = tf.gradients(tf.nn.relu(inputs), inputs) - - self.assertEqual(0.0, - layers.Gelu(tf.constant(-10.0, dtype='float32')).eval()) - self.assertEqual(0.0, - layers.Gelu(tf.constant(0.0, dtype='float32')).eval()) - self.assertEqual(10.0, - layers.Gelu(tf.constant(10.0, dtype='float32')).eval()) - actual_grads_gelu = grads_gelu[0].eval() - actual_grads_relu = grads_relu[0].eval() - - self.assertAllClose(actual_grads_gelu[-5:], actual_grads_relu[-5:]) - self.assertAllClose(actual_grads_gelu[:5], actual_grads_relu[:5]) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_grads_gelu = [ - -7.69459925e-22, -9.25176121e-18, -4.04182472e-14, -6.39430453e-11, - -3.64552299e-08, -7.13557529e-06, -5.03641320e-04, -1.19456425e-02, - -8.52318183e-02, -8.33154917e-02, 5.00000000e-01, 1.08331549e+00, - 1.08523178e+00, 1.01194561e+00, 1.00050366e+00, 1.00000715e+00, - 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, - 1.00000000e+00] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_grads_gelu, actual_grads_gelu) - - -class BatchNormLayerTest(test_utils.TestCase, parameterized.TestCase): - - def testBatchNormLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayer.Params() - params.name = 'bn' - params.dim = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - params.add_stats_to_moving_average_variables = True - layers.BatchNormLayer(params) - bn_vars = tf.get_collection('BatchNormLayer_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [ - 'bn/beta/var:0', 'bn/gamma/var:0', 'bn/moving_mean/var:0', - 'bn/moving_variance/var:0' - ] - self.assertEqual(expected_var_names, bn_var_names) - self.assertEqual(['bn/moving_mean/var:0', 'bn/moving_variance/var:0'], - [x.name for x in tf.moving_average_variables()]) - - def testBatchNormLayerMoments(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - - in_padding1 = tf.zeros([2, 2, 8, 1], dtype=tf.float32) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 2, 8, 2]), dtype=tf.float32) - mean1, var1 = bn_layers.ComputeMomentsWithPadding( - bn_in1, in_padding1, reduce_over_dims=[0, 1, 2]) - mean2, var2 = tf.nn.moments(bn_in1, [0, 1, 2]) - - in_padding2 = tf.ones([2, 2, 8, 1], dtype=tf.float32) - bn_in2 = tf.constant( - np.random.normal(-0.3, 1.0, [2, 2, 8, 2]), dtype=tf.float32) - in_padding3 = tf.concat([in_padding1, in_padding2], 1) - bn_in3 = tf.concat([bn_in1, bn_in2], 1) - mean3, var3 = bn_layers.ComputeMomentsWithPadding( - bn_in3, in_padding3, reduce_over_dims=[0, 1, 2]) - mean4, var4 = tf.nn.moments(bn_in3, [0, 1, 2]) - - mean_diff = tf.reduce_sum(tf.square(mean3 - mean4)) - var_diff = tf.reduce_sum(tf.square(var3 - var4)) - - self.evaluate(tf.global_variables_initializer()) - - self.assertAllClose(mean2.eval(), mean1.eval()) - self.assertAllClose(var2.eval(), var1.eval()) - self.assertAllClose(mean3.eval(), mean1.eval()) - self.assertAllClose(var3.eval(), var1.eval()) - # Since tf.nn.moments() doesn't support padding, it is expected to produce - # different results than our own implementation (of moments). - self.assertAllClose(0.095987, mean_diff.eval()) - self.assertAllClose(0.364456, var_diff.eval()) - - def testBatchNormLayerFProp(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayer.Params() - params.name = 'bn' - params.dim = 3 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - bn_layer = layers.BatchNormLayer(params) - in_padding1 = tf.zeros([2, 8, 1], dtype=tf.float32) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 3]), dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1, in_padding1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(0.0, sig1.eval(), atol=1e-5) - self.assertAllClose(47.8371887, sig2.eval()) - - def testBatchNormLayerFPropUseGlobalStatsForTraining(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayer.Params() - params.name = 'bn' - params.dim = 3 - params.use_moving_avg_in_training = True - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - bn_layer = layers.BatchNormLayer(params) - in_padding1 = tf.zeros([2, 8, 1], dtype=tf.float32) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 3]), dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1, in_padding1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(2.6593573, sig1.eval(), atol=1e-5) - self.assertAllClose(15.464208, sig2.eval()) - - def testBatchNormLayerFPropWithUpdateUseGlobalStatsForTraining(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayer.Params() - params.name = 'bn' - params.dim = 3 - params.use_moving_avg_in_training = True - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - bn_layer = layers.BatchNormLayer(params) - in_padding1 = tf.zeros([2, 8, 1], dtype=tf.float32) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 3]), dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1, in_padding1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - - # get updates which should be invoked during training step - # but we call them here, so that UpdateBatchNormVars is tested too - bn_update_dict = py_utils._get_batch_norm_updates_dict() - bn_update_list = list(bn_update_dict.keys()) - - self.evaluate(tf.global_variables_initializer()) - self.evaluate(bn_update_list) - self.assertAllClose(2.6575434, sig1.eval(), atol=1e-5) - self.assertAllClose(15.473802, sig2.eval()) - - def testBatchNormLayerMomentsForConv(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - - in_padding1 = tf.zeros([2, 8, 1, 1], dtype=tf.float32) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 4, 3]), dtype=tf.float32) - mean1, var1 = bn_layers.ComputeMomentsWithPadding( - bn_in1, in_padding1, reduce_over_dims=[0, 1, 2]) - mean2, var2 = tf.nn.moments(bn_in1, [0, 1, 2]) - - in_padding2 = tf.ones([2, 8, 1, 1], dtype=tf.float32) - bn_in2 = tf.constant( - np.random.normal(-0.3, 1.0, [2, 8, 4, 3]), dtype=tf.float32) - in_padding3 = tf.concat([in_padding1, in_padding2], 1) - bn_in3 = tf.concat([bn_in1, bn_in2], 1) - mean3, var3 = bn_layers.ComputeMomentsWithPadding( - bn_in3, in_padding3, reduce_over_dims=[0, 1, 2]) - mean4, var4 = tf.nn.moments(bn_in3, [0, 1, 2]) - - mean_diff = tf.reduce_sum(tf.square(mean3 - mean4)) - var_diff = tf.reduce_sum(tf.square(var3 - var4)) - - self.evaluate(tf.global_variables_initializer()) - - self.assertAllClose(mean2.eval(), mean1.eval()) - self.assertAllClose(var2.eval(), var1.eval()) - self.assertAllClose(mean3.eval(), mean1.eval()) - self.assertAllClose(var3.eval(), var1.eval()) - self.assertAllClose(0.1726295, mean_diff.eval()) - self.assertAllClose(0.5592572093009949, var_diff.eval()) - - def testBatchNormLayerFPropForConv(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayer.Params() - params.name = 'bn_conv' - params.dim = 32 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - bn_layer = layers.BatchNormLayer(params) - in_padding1 = tf.zeros([2, 8, 1, 1], dtype=tf.float32) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 4, 32]), dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1, in_padding1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(0.0, sig1.eval(), atol=1e-4) - self.assertAllClose(2039.398681, sig2.eval()) - - @parameterized.named_parameters( - ('FuseEvalNoFreeze', True, True, False), - ('NoFuseEvalNoFreeze', False, True, False), - ('FuseTrainingFreeze', True, False, True), - ('NoFuseTrainingFreeze', False, False, True), - ) - def testBatchNormLayerFPropForConvWithFusedEvalWithFreezeBNStats( - self, use_fused_batch_norm_for_eval, do_eval, freeze_bn_stats): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayer.Params() - params.name = 'bn_conv' - params.dim = 32 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - params.use_fused_batch_norm_for_eval = use_fused_batch_norm_for_eval - params.freeze_bn_stats = freeze_bn_stats - with cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', do_eval=True): - bn_layer = params.Instantiate() - bn_layer._epsilon = 0.0 # Enables a lower tolerance in the test check. - in_padding1 = tf.zeros([2, 4, 1, 1], dtype=tf.float32) - np_in1 = np.random.normal(0.1, 0.5, [2, 4, 1, 32]) - bn_in1 = tf.constant(np_in1, dtype=tf.float32) - bn_out = bn_layer.FPropDefaultTheta(bn_in1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - # Moving mean and variance are set to defaults, we set gamma and beta - # through assignment such that the outputs are inputs * 2 + 1. - moving_mean_init = np.zeros(bn_layer.vars.moving_mean.shape.as_list()) - moving_variance_init = np.ones( - bn_layer.vars.moving_variance.shape.as_list()) - self.evaluate([ - tf.assign(bn_layer.vars.gamma, - np.ones(bn_layer.vars.gamma.shape.as_list())), - tf.assign(bn_layer.vars.beta, - np.ones(bn_layer.vars.beta.shape.as_list())), - tf.assign(bn_layer.vars.moving_mean, moving_mean_init), - tf.assign(bn_layer.vars.moving_variance, moving_variance_init), - ]) - self.assertAllClose( - np_in1 * 2. + 1., bn_out.eval(), atol=1e-5, rtol=1e-5) - # check moving stats modified or not - moving_mean = self.evaluate(bn_layer.vars.moving_mean) - moving_variance = self.evaluate(bn_layer.vars.moving_variance) - if do_eval or freeze_bn_stats: - self.assertAllClose(moving_mean, moving_mean_init) - self.assertAllClose(moving_variance, moving_variance_init) - - -class CategoricalBNTest(test_utils.TestCase, parameterized.TestCase): - - def testConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = bn_layers.CategoricalBN.Params() - params.name = 'bn' - params.dim = 2 - params.class_emb_dim = 4 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - bn_layers.CategoricalBN(params) - bn_vars = tf.get_collection('CategoricalBN_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [ - 'bn/beta/var:0', 'bn/gamma/var:0', 'bn/moving_mean/var:0', - 'bn/moving_variance/var:0' - ] - self.assertEqual(expected_var_names, bn_var_names) - self.assertEqual(['bn/moving_mean/var:0', 'bn/moving_variance/var:0'], - [x.name for x in tf.moving_average_variables()]) - - def testFPropSameClass(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = bn_layers.CategoricalBN.Params() - params.name = 'bn' - params.dim = 3 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - params.class_emb_dim = 4 - - bn_layer = bn_layers.CategoricalBN(params) - in_padding1 = tf.zeros([2, 8, 1], dtype=tf.float32) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 3]), dtype=tf.float32) - domain_in1 = tf.one_hot([0, 0], - depth=params.class_emb_dim, - dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1, in_padding1, domain_in1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(0.0, sig1.eval(), atol=1e-5) - self.assertAllClose(47.8371887, sig2.eval()) - - def testFPropDifferenClasses(self): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - params = bn_layers.CategoricalBN.Params() - params.name = 'bn' - params.dim = 3 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - params.class_emb_dim = 4 - - bn_layer = bn_layers.CategoricalBN(params) - in_padding1 = tf.zeros([4, 8, 1], dtype=tf.float32) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [4, 8, 3]), dtype=tf.float32) - domain_in1 = tf.one_hot([0, 1, 1, 2], - depth=params.class_emb_dim, - dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1, in_padding1, domain_in1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - self.evaluate(tf.global_variables_initializer()) - - sig1_v, sig2_v = sess.run([sig1, sig2]) - self.assertAllClose(0.0, sig1_v, atol=1e-5) - self.assertAllClose(95.6266, sig2_v) - - -class GroupNormLayerTest(test_utils.TestCase, parameterized.TestCase): - - def testGroupNormLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = bn_layers.GroupNormLayer.Params() - params.name = 'gn' - params.dim = 2 - params.num_groups = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - bn_layers.GroupNormLayer(params) - gn_vars = tf.get_collection('GroupNormLayer_vars') - gn_var_names = [x.name for x in gn_vars] - expected_var_names = ['gn/beta/var:0', 'gn/gamma/var:0'] - self.assertEqual(expected_var_names, gn_var_names) - - def testGroupNormLayerFProp(self): - with self.session(use_gpu=True): - params = bn_layers.GroupNormLayer.Params() - params.name = 'gn' - params.dim = 4 - params.num_groups = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - gn_in = tf.reshape(np.arange(32, dtype=np.float32), [2, 2, 2, 4]) - - gn_layer = bn_layers.GroupNormLayer(params) - gn_out = gn_layer.FPropDefaultTheta(gn_in) - - tf.global_variables_initializer().run() - base_block = np.array([[[-1.44440889, -1.22219217], - [-0.55554187, -0.33332515]], - [[0.33332515, 0.55554187], - [1.22219217, 1.44440889]]]) - expected_out = np.array([ - np.concatenate((base_block, base_block), -1), - np.concatenate((base_block, base_block), -1) - ]) - self.assertAllClose(expected_out, gn_out.eval(), atol=1e-5) - - def testGroupNormLayerFPropWithPaddings(self): - with self.session(use_gpu=True): - params = bn_layers.GroupNormLayer.Params() - params.name = 'gn' - params.dim = 4 - params.num_groups = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - gn_in = tf.reshape(np.arange(32, dtype=np.float32), [2, 2, 2, 4]) - paddings = tf.convert_to_tensor([[0, 0], [0, 1]], dtype=tf.float32) - - gn_layer = bn_layers.GroupNormLayer(params) - gn_out, paddings_out = gn_layer.FPropDefaultTheta(gn_in, paddings) - - tf.global_variables_initializer().run() - base_block1 = np.array([[[-1.44440889, -1.22219217], - [-0.55554187, -0.33332515]], - [[0.33332515, 0.55554187], - [1.22219217, 1.44440889]]]) - - base_block2 = np.array([[[-1.2125355, -0.7275213], [0.7275213, - 1.2125355]], - [[2.6675782, 3.1525922], [4.607635, 5.092649]]]) - - expected_out = np.array([ - np.concatenate((base_block1, base_block1), -1), - np.concatenate((base_block2, base_block2), -1) - ]) - print(gn_out.eval()) - self.assertAllClose(expected_out, gn_out.eval(), atol=1e-5) - self.assertAllEqual(paddings.eval(), paddings_out.eval()) - - def testGroupNormLayerFPropWithPaddings3DInput(self): - with self.session(use_gpu=True): - params = bn_layers.GroupNormLayer.Params() - params.name = 'gn' - params.dim = 4 - params.num_groups = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - gn_in = np.reshape(np.arange(16, dtype=np.float32), [2, 2, 1, 4]) - paddings = np.array([[0, 0], [0, 1]], dtype=np.float32) - - gn_layer = bn_layers.GroupNormLayer(params) - gn_out, paddings_out = gn_layer.FPropDefaultTheta( - tf.convert_to_tensor(gn_in), tf.convert_to_tensor(paddings)) - - params_3d = params.Copy().Set(input_rank=3, name='gn_3d') - gn_layer_3d = bn_layers.GroupNormLayer(params_3d) - gn_out_3d, paddings_out_3d = gn_layer_3d.FPropDefaultTheta( - tf.convert_to_tensor(gn_in.reshape([2, 2, 4])), - tf.convert_to_tensor(paddings)) - - tf.global_variables_initializer().run() - print(gn_out.eval()) - # Tests against reference. - self.assertAllClose( - gn_out_3d.eval(), gn_out.eval().reshape([2, 2, 4]), atol=1e-5) - self.assertAllEqual(paddings_out_3d.eval(), paddings_out.eval()) - - @parameterized.named_parameters(('4D',), ('3D', 3)) - def testGroupNormLayerFPropCumulativeMode(self, input_rank=4): - with self.session(use_gpu=True): - params = bn_layers.GroupNormLayer.Params() - params.name = 'gn' - params.dim = 2 - params.num_groups = 2 - params.cumulative = True - params.input_rank = input_rank - # gn_in[0]: [[0, 1], [2, 3], [4, 5], [6, 7]] - # gn_in[1]: [[8, 9], [10, 11], [12, 13], [14, 15]] - input_shape = [2, 4, 1, 2] if input_rank == 4 else [2, 4, 2] - gn_in = tf.reshape(np.arange(16, dtype=np.float32), input_shape) - paddings = tf.zeros([2, 4], tf.float32) - gn_layer = bn_layers.GroupNormLayer(params) - gn_out, _ = gn_layer.FPropDefaultTheta(gn_in, paddings) - - tf.global_variables_initializer().run() - base_block = np.array([[0., 0.], [1.4128014, 1.4128014], - [1.5487288, 1.5487288], [1.6033384, 1.6033384]]) - - expected_out = np.stack([base_block, base_block], - axis=0).reshape(input_shape) - self.assertAllClose(expected_out, gn_out.eval(), atol=1e-5) - - -class ConvLayerTest(test_utils.TestCase): - """Tests conv layers. - - Note that there are multiple subclasses of BaseConv2DLayer and most cases - are tested via the concrete Conv2DLayer. Other tests are done against - other subclasses to cover key differences. - """ - - def testConv2DLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.Conv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - layers.Conv2DLayer(params) - conv_vars = tf.get_collection('Conv2DLayer_vars') - conv_var_names = [x.name for x in conv_vars] - expected_var_names = ['conv/w/var:0'] - self.assertEqual(expected_var_names, conv_var_names) - bn_vars = tf.get_collection('BatchNormLayer_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [ - 'conv/beta/var:0', 'conv/gamma/var:0', 'conv/moving_mean/var:0', - 'conv/moving_variance/var:0' - ] - self.assertEqual(expected_var_names, bn_var_names) - - def testDepthwiseConv2DLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.DepthwiseConv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - layers.DepthwiseConv2DLayer(params) - conv_vars = tf.get_collection('DepthwiseConv2DLayer_vars') - conv_var_names = [x.name for x in conv_vars] - expected_var_names = ['conv/w/var:0'] - self.assertEqual(expected_var_names, conv_var_names) - bn_vars = tf.get_collection('BatchNormLayer_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [ - 'conv/beta/var:0', 'conv/gamma/var:0', 'conv/moving_mean/var:0', - 'conv/moving_variance/var:0' - ] - self.assertEqual(expected_var_names, bn_var_names) - - def testDepthwiseConv2DLayerModuleInterface(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.DepthwiseConv2DLayer.Params() - params.name = 'conv1' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv1 = layers.DepthwiseConv2DLayer(params) - params.name = 'conv2' - conv2 = layers.DepthwiseConv2DLayer(params) - - def ModuleName(m): - return m.name - - conv1_variables = [v.name for v in conv1.variables] - conv1_submodules = [ModuleName(v) for v in conv1.submodules] - conv2_variables = [v.name for v in conv2.variables] - conv2_submodules = [ModuleName(v) for v in conv2.submodules] - expected_conv1_vars = [ - 'global_step:0', 'conv1/w/var:0', 'conv1/moving_mean/var:0', - 'conv1/moving_variance/var:0', 'conv1/beta/var:0', 'conv1/gamma/var:0' - ] - expected_conv2_vars = [ - 'global_step:0', 'conv2/w/var:0', 'conv2/moving_mean/var:0', - 'conv2/moving_variance/var:0', 'conv2/beta/var:0', 'conv2/gamma/var:0' - ] - expected_conv1_modules = ['bbf_BatchNormLayer_conv1'] - expected_conv2_modules = ['bbf_BatchNormLayer_conv2'] - self.assertCountEqual(expected_conv1_vars, conv1_variables) - self.assertCountEqual(expected_conv2_vars, conv2_variables) - self.assertCountEqual(expected_conv1_modules, conv1_submodules) - self.assertCountEqual(expected_conv2_modules, conv2_submodules) - - def testSeparableConv2DLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.SeparableConv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - params.Instantiate() - # Vars for the outer conv layer. - conv_vars = tf.get_collection('SeparableConv2DLayer_vars') - conv_var_names = [x.name for x in conv_vars] - expected_var_names = ['conv/w/var:0'] - self.assertSetEqual(set(expected_var_names), set(conv_var_names)) - # Vars for the inner depthwise layer. - conv_vars = tf.get_collection('DepthwiseConv2DLayer_vars') - conv_var_names = [x.name for x in conv_vars] - expected_var_names = ['conv/depthwise_conv/w/var:0'] - self.assertSetEqual(set(expected_var_names), set(conv_var_names)) - bn_vars = tf.get_collection('BatchNormLayer_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [ - # Outer conv batchnorm. - 'conv/beta/var:0', - 'conv/gamma/var:0', - 'conv/moving_mean/var:0', - 'conv/moving_variance/var:0', - # Inner depthwise batchnorm. - 'conv/depthwise_conv/beta/var:0', - 'conv/depthwise_conv/gamma/var:0', - 'conv/depthwise_conv/moving_mean/var:0', - 'conv/depthwise_conv/moving_variance/var:0', - ] - self.assertSetEqual(set(expected_var_names), set(bn_var_names)) - - def testConv2DLayerWithBiasConstruction(self): - """Tests Conv2DLayer with only bias and without batch normalization.""" - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.Conv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - params.bias = True - params.batch_norm = False - layers.Conv2DLayer(params) - conv_vars = tf.get_collection('Conv2DLayer_vars') - conv_var_names = [x.name for x in conv_vars] - # Has both 'w' and 'b'. - expected_var_names = ['conv/w/var:0', 'conv/b/var:0'] - self.assertEqual(expected_var_names, conv_var_names) - # No BatchNorm variables. - bn_vars = tf.get_collection('BatchNormLayer_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [] - self.assertEqual(expected_var_names, bn_var_names) - - def testDepthwiseConv2DLayerWithBiasConstruction(self): - """Tests DepthwiseConv2D with only bias and without batch normalization.""" - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.DepthwiseConv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - params.bias = True - params.batch_norm = False - layers.DepthwiseConv2DLayer(params) - conv_vars = tf.get_collection('DepthwiseConv2DLayer_vars') - conv_var_names = [x.name for x in conv_vars] - # Has both 'w' and 'b'. - expected_var_names = ['conv/w/var:0', 'conv/b/var:0'] - self.assertEqual(expected_var_names, conv_var_names) - # No BatchNorm variables. - bn_vars = tf.get_collection('BatchNormLayer_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [] - self.assertEqual(expected_var_names, bn_var_names) - - def testConv2DLayerOutShape(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.Conv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = layers.Conv2DLayer(params) - in_shape = [None, None, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, None, 5, 32]) - in_shape = [None, 20, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, 10, 5, 32]) - - def testDepthwiseConv2DLayerOutShape(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.DepthwiseConv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = layers.DepthwiseConv2DLayer(params) - in_shape = [None, None, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, None, 5, 96]) - in_shape = [None, 20, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, 10, 5, 96]) - - def testSeparableConv2DLayerOutShape(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.SeparableConv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = params.Instantiate() - in_shape = [None, None, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, None, 5, 32]) - in_shape = [None, 20, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, 10, 5, 32]) - - def testConv2DLayerWithDilationOutShape(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.Conv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [1, 1] - params.dilation_rate = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = layers.Conv2DLayer(params) - # dilation_rate does not change output shape. - in_shape = [None, None, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, None, 10, 32]) - in_shape = [None, 20, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, 20, 10, 32]) - - def testDepthwiseConv2DLayerWithDilationOutShape(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.DepthwiseConv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [1, 1] - params.dilation_rate = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = layers.DepthwiseConv2DLayer(params) - # dilation_rate does not change output shape. - in_shape = [None, None, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, None, 10, 96]) - in_shape = [None, 20, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, 20, 10, 96]) - - def testSeparableConv2DLayerWithDilationOutShape(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.SeparableConv2DLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 32] - params.filter_stride = [1, 1] - params.dilation_rate = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = params.Instantiate() - # dilation_rate does not change output shape. - in_shape = [None, None, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, None, 10, 32]) - in_shape = [None, 20, 10, 3] - out_shape = conv_layer.OutShape(in_shape) - self.assertEqual(out_shape, [None, 20, 10, 32]) - - def testConvPoolComputeOutPadding(self): - with self.session(use_gpu=True): - in_padding = tf.constant( - [[0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0], - [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0]], - dtype=tf.float32) - out_padding = layers._ComputeConvOutputPadding(in_padding, 2, 2) - expected_out_padding = [[1, 1, 0, 0, 0, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1, 0]] - - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(expected_out_padding, out_padding.eval().tolist()) - - def testConvPoolComputeOutPaddingUnevenStride(self): - with self.session(use_gpu=True): - in_padding = tf.constant([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]], - dtype=tf.float32) - out_padding = layers._ComputeConvOutputPadding(in_padding, 3, 3) - expected_out_padding = [[0, 0, 0, 0, 1], [0, 0, 0, 1, 1], [0, 0, 1, 1, 1]] - - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(expected_out_padding, out_padding.eval().tolist()) - - def _checkConvLayerShapes(self, - input_shape, - filter_shape, - filter_stride, - dilation_rate=None, - depth_multiplier=None, - params_builder=layers.Conv2DLayer.Params): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = params_builder() - params.name = 'conv' - params.filter_shape = filter_shape - params.filter_stride = filter_stride - if dilation_rate: - params.dilation_rate = dilation_rate - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - if depth_multiplier is not None: - params.depth_multiplier = depth_multiplier - conv_layer = params.Instantiate() - - inp = tf.random.uniform(input_shape) - inp_pad = tf.floor(0.5 + tf.random.uniform(input_shape[:2])) - out, out_pad = conv_layer.FPropDefaultTheta(inp, inp_pad) - - with self.session(use_gpu=True, graph=g): - self.evaluate(tf.global_variables_initializer()) - out, out_pad = self.evaluate([out, out_pad]) - print(out.shape, out_pad.shape) - # We expect conv_layer.OutShape can compute the actual output shape. - self.assertAllEqual(out.shape, conv_layer.OutShape(inp.shape.as_list())) - # We expect out_pad.shape matches the 1st 2 dimensions of out. - self.assertAllEqual(out.shape[:2], out_pad.shape) - - def testConv2DLayerOutputShapes(self): - self._checkConvLayerShapes([2, 4, 4, 3], [3, 3, 3, 32], [1, 1]) - self._checkConvLayerShapes([2, 4, 4, 3], [3, 3, 3, 32], [2, 2]) - self._checkConvLayerShapes([2, 10, 4, 3], [3, 3, 3, 32], [3, 3]) - - self._checkConvLayerShapes([2, 10, 4, 3], [3, 3, 3, 32], [1, 1], - dilation_rate=[2, 2]) - self._checkConvLayerShapes([2, 10, 4, 3], [3, 3, 3, 32], [1, 1], - dilation_rate=[3, 3]) - - def testDepthwiseConv2DLayerOutputShapes(self): - self._checkConvLayerShapes( - [2, 4, 4, 3], [3, 3, 3, 32], [1, 1], - params_builder=layers.DepthwiseConv2DLayer.Params) - self._checkConvLayerShapes( - [2, 4, 4, 3], [3, 3, 3, 32], [2, 2], - params_builder=layers.DepthwiseConv2DLayer.Params) - self._checkConvLayerShapes( - [2, 10, 4, 3], [3, 3, 3, 32], [3, 3], - params_builder=layers.DepthwiseConv2DLayer.Params) - - self._checkConvLayerShapes( - [2, 10, 4, 3], [3, 3, 3, 32], [1, 1], - dilation_rate=[2, 2], - params_builder=layers.DepthwiseConv2DLayer.Params) - self._checkConvLayerShapes( - [2, 10, 4, 3], [3, 3, 3, 32], [1, 1], - dilation_rate=[3, 3], - params_builder=layers.DepthwiseConv2DLayer.Params) - - def testSeparableConv2DLayerOutputShapes(self): - self._checkConvLayerShapes( - [2, 4, 4, 3], [3, 3, 3, 32], [1, 1], - params_builder=layers.SeparableConv2DLayer.Params) - self._checkConvLayerShapes( - [2, 4, 4, 3], [3, 3, 3, 32], [2, 2], - params_builder=layers.SeparableConv2DLayer.Params) - self._checkConvLayerShapes( - [2, 10, 4, 3], [3, 3, 3, 32], [3, 3], - params_builder=layers.SeparableConv2DLayer.Params) - # Dilations. - self._checkConvLayerShapes( - [2, 10, 4, 3], [3, 3, 3, 32], [1, 1], - dilation_rate=[2, 2], - params_builder=layers.SeparableConv2DLayer.Params) - self._checkConvLayerShapes( - [2, 10, 4, 3], [3, 3, 3, 32], [1, 1], - dilation_rate=[3, 3], - params_builder=layers.SeparableConv2DLayer.Params) - # Depth multiplier. - self._checkConvLayerShapes( - [2, 4, 4, 3], [3, 3, 3, 32], [1, 1], - params_builder=layers.SeparableConv2DLayer.Params, - depth_multiplier=2) - self._checkConvLayerShapes( - [2, 4, 4, 3], [3, 3, 3, 32], [2, 2], - params_builder=layers.SeparableConv2DLayer.Params, - depth_multiplier=6) - self._checkConvLayerShapes( - [2, 10, 4, 3], [3, 3, 3, 32], [3, 3], - params_builder=layers.SeparableConv2DLayer.Params, - depth_multiplier=12) - - def _evalConvLayerFProp(self, - params_builder=layers.Conv2DLayer.Params, - batch_norm=True, - weight_norm=False, - bias=False, - activation='RELU', - conv_last=False, - strides=(2, 2), - dilation_rate=(1, 1), - bn_fold_weights=False, - is_eval=False, - quantized=False): - self._ClearCachedSession() - tf.reset_default_graph() - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = params_builder() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 2] - params.filter_stride = strides - params.dilation_rate = dilation_rate - params.params_init = py_utils.WeightInit.Gaussian(0.1) - params.conv_last = conv_last - params.batch_norm = batch_norm - params.bn_fold_weights = bn_fold_weights - params.weight_norm = weight_norm - params.bias = bias - params.activation = activation - - if quantized: - params.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - - conv_layer = params.Instantiate() - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 3]), dtype=tf.float32) - - output1, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - output2, _ = conv_layer.FPropDefaultTheta(inputs1) - self.evaluate(tf.global_variables_initializer()) - v1, v2 = self.evaluate([output1, output2]) - self.assertAllClose(v1, v2) - return v1 - - def testConv2DLayerFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output1 = [ - [[[ 0.36669245, 0.91488785], - [ 0.07532132, 0. ]], - [[ 0.34952009, 0. ], - [ 1.91783941, 0. ]]], - [[[ 0.28304493, 0. ], - [ 0. , 0. ]], - [[ 0. , 0.86575812], - [ 0. , 1.60203481]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp() - print('actual = ', np.array_repr(actual)) - self.assertAllClose(expected_output1, actual) - - def testDepthwiseConv2DLayerFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output1 = [ - [[[ 0.93514717, 0.35602099, 0. , 0.51261222, 0. , - 1.4310323 ], - [ 0. , 0. , 0.49176404, 0. , 1.01494753, - 0.51337928]], - [[ 0.62087697, 0.34572476, 0. , 0.19352221, 0.47142431, - 0. ], - [ 0.81119895, 1.00890303, 0.90471351, 0. , 1.22736526, - 0. ]]], - [[[ 0. , 0. , 0.48927376, 0. , 0.74019426, - 0. ], - [ 0. , 0. , 1.49952257, 0. , 0. , - 0. ]], - [[ 0.29156703, 0. , 0. , 1.14509106, 0. , - 0.74238932], - [ 0.91312039, 1.39783907, 0. , 1.47650909, 0. , - 0.37969294]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp( - params_builder=layers.DepthwiseConv2DLayer.Params) - print('actual = ', np.array_repr(actual)) - self.assertAllClose(expected_output1, actual) - - def testSeparableConv2DLayerFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output1 =[ - [[[ 0.39866772, 0. ], - [ 1.36471784, 0. ]], - [[ 0. , 0. ], - [ 0. , 0. ]]], - [[[ 1.15356529, 0.1036691 ], - [ 0.12865055, 0.61244327]], - [[ 0.03609803, 1.81620765], - [ 0. , 0.23052886]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp( - params_builder=layers.SeparableConv2DLayer.Params) - print('actual = ', np.array_repr(actual)) - self.assertAllClose(expected_output1, actual) - - def testConv2DLayerWithDilationFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output1 = [ - [[[ 0. , 0.48857123], - [ 1.07320869, 0. ], - [ 0. , 0.1550007 ], - [ 0. , 1.59097648]], - [[ 0. , 0. ], - [ 0.20024362, 0. ], - [ 0. , 0.64265913], - [ 1.52903616, 0. ]], - [[ 0.099805 , 0. ], - [ 0. , 0.61720949], - [ 1.31608474, 0. ], - [ 0. , 0. ]], - [[ 0.0175612 , 0. ], - [ 0. , 0.17234094], - [ 0.21719536, 0. ], - [ 1.68514931, 0. ]]], - [[[ 1.45240796, 0. ], - [ 0. , 0. ], - [ 0.72675145, 1.971596 ], - [ 0. , 0.01062769]], - [[ 0. , 1.70299017], - [ 1.36936104, 1.29897082], - [ 1.40132439, 1.74345171], - [ 0.02585058, 0.29061913]], - [[ 0. , 0. ], - [ 0.32962656, 0.05025356], - [ 0. , 0. ], - [ 0. , 0. ]], - [[ 0.97244394, 0. ], - [ 0.23401484, 0.5722279 ], - [ 0. , 0.40940297], - [ 0. , 0.52711827]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp(strides=[1, 1], dilation_rate=[2, 2]) - print('testConvLayerWithDilationFProp actual = ', np.array_repr(actual)) - self.assertAllClose(expected_output1, actual, atol=1e-5) - - def testSeparableConv2DLayerWithDilationFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output1 = [ - [[[ 0.21535617, 0.86965537], - [ 2.11499524, 1.2463783 ], - [ 0. , 0.39275286], - [ 0. , 0. ]], - [[ 1.12706482, 1.37450278], - [ 0. , 0. ], - [ 0. , 0. ], - [ 1.2390101 , 0.22932449]], - [[ 0. , 0. ], - [ 0.15051894, 1.32616639], - [ 0. , 0. ], - [ 0.72912866, 0.47753802]], - [[ 0.91655868, 0. ], - [ 0.88526261, 0.26690534], - [ 0. , 0.26084688], - [ 0.42923039, 0. ]]], - [[[ 0.82440329, 0. ], - [ 0.49015623, 0.52662987], - [ 0. , 0. ], - [ 0.35344127, 0. ]], - [[ 0. , 0. ], - [ 0. , 0. ], - [ 0.43848675, 0. ], - [ 0. , 1.21124518]], - [[ 1.1026746 , 1.39578998], - [ 0. , 0. ], - [ 0.34652925, 0. ], - [ 0. , 1.26868236]], - [[ 0.91519427, 0.09030763], - [ 0. , 0.59271163], - [ 0. , 0.54207176], - [ 0. , 0. ]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp( - strides=[1, 1], - dilation_rate=[2, 2], - params_builder=layers.SeparableConv2DLayer.Params) - print('testConvLayerWithDilationFProp actual = ', np.array_repr(actual)) - self.assertAllClose(expected_output1, actual) - - def testConv2DLayerConvFirstVsLastFProp(self): - """Compare results of conv first vs. last.""" - # ... with batch_norm and activation disabled. - self.assertAllClose( - self._evalConvLayerFProp( - batch_norm=False, activation='NONE', conv_last=False), - self._evalConvLayerFProp( - batch_norm=False, activation='NONE', conv_last=True)) - - def testConv2DLayerFPropConvLast(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output1 = [ - [[[ 0.22165056, 0.20731729], - [ 0.09577402, -0.15359652]], - [[ 0.07151584, 0.03027298], - [ 0.05370769, 0.0143405 ]]], - [[[-0.08854639, 0.06143938], - [-0.37708873, 0.00889082]], - [[-0.58154356, 0.30798748], - [-0.37575331, 0.54729235]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp(conv_last=True) - print(['ConvLast actual = ', np.array_repr(actual)]) - self.assertAllClose(expected_output1, actual) - - def testConv2DLayerConvWithBias(self): - """Compare results with bias vs. with neither batch_norm nor bias.""" - # Results should match since bias is initialized to be 0. - self.assertAllClose( - self._evalConvLayerFProp(batch_norm=False, bias=False), - self._evalConvLayerFProp(batch_norm=False, bias=True)) - - def testConv2DLayerWeightNormFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output = [ - [[[ 0.37172362, 0.92405349], - [ 0.07635488, 0.]], - [[ 0.35431579, 0.], - [ 1.94415355, 0.]]], - [[[ 0.28692839, 0.], - [ 0. , 0.]], - [[ 0. , 0.87443149], - [ 0. , 1.61808443]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp(weight_norm=True) - print('actual1 = ', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testDepthwiseConv2DLayerWeightNormFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output = [ - [[[ 0.97023201, 0.37429881, 0. , 0.53157473, 0. , - 1.60764372], - [ 0. , 0. , 0.50401598, 0. , 1.07683432, - 0.57673818]], - [[ 0.644171 , 0.36347377, 0. , 0.20068097, 0.50016963, - 0. ], - [ 0.8416335 , 1.06069875, 0.92725372, 0. , 1.30220449, - 0. ]]], - [[[ 0. , 0. , 0.50146359, 0. , 0.78532791, - 0. ], - [ 0. , 0. , 1.53688192, 0. , 0. , - 0. ]], - [[ 0.302506 , 0. , 0. , 1.18745029, 0. , - 0.83401161], - [ 0.94737887, 1.46960247, 0. , 1.53112805, 0. , - 0.42655289]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp( - weight_norm=True, params_builder=layers.DepthwiseConv2DLayer.Params) - print('actual1 = ', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testSeparableConv2DLayerWeightNormFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output = [ - [[[ 0.41837293, 0. ], - [ 1.39592457, 0. ]], - [[ 0. , 0. ], - [ 0. , 0. ]]], - [[[ 1.20513153, 0.11938372], - [ 0.1284119 , 0.6927582 ]], - [[ 0.0227453 , 2.05591369], - [ 0. , 0.26530063]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalConvLayerFProp( - weight_norm=True, params_builder=layers.SeparableConv2DLayer.Params) - print('actual1 = ', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testConv2DLayerFoldedBatchNormFProp(self): - actual_unfolded = self._evalConvLayerFProp( - batch_norm=True, bn_fold_weights=False) - actual_folded = self._evalConvLayerFProp( - batch_norm=True, bn_fold_weights=True) - print('testConvLayerFoldedBatchNormFProp folded = ', - np.array_repr(actual_folded)) - print('testConvLayerFoldedBatchNormFProp unfolded = ', - np.array_repr(actual_unfolded)) - self.assertAllClose(actual_folded, actual_unfolded) - - def testDepthwiseConv2DLayerFoldedBatchNormFProp(self): - actual_unfolded = self._evalConvLayerFProp( - batch_norm=True, - bn_fold_weights=False, - params_builder=layers.DepthwiseConv2DLayer.Params) - actual_folded = self._evalConvLayerFProp( - batch_norm=True, - bn_fold_weights=True, - params_builder=layers.DepthwiseConv2DLayer.Params) - print('testDepthwiseConvLayerFoldedBatchNormFProp folded = ', - np.array_repr(actual_folded)) - print('testDepthwiseConvLayerFoldedBatchNormFProp unfolded = ', - np.array_repr(actual_unfolded)) - self.assertAllClose(actual_folded, actual_unfolded) - - def testSeparableConv2DLayerFoldedBatchNormFProp(self): - actual_unfolded = self._evalConvLayerFProp( - batch_norm=True, - bn_fold_weights=False, - params_builder=layers.SeparableConv2DLayer.Params) - actual_folded = self._evalConvLayerFProp( - batch_norm=True, - bn_fold_weights=True, - params_builder=layers.SeparableConv2DLayer.Params) - print('testSeparableConvLayerFoldedBatchNormFProp folded = ', - np.array_repr(actual_folded)) - print('testSeparableConvLayerFoldedBatchNormFProp unfolded = ', - np.array_repr(actual_unfolded)) - self.assertAllClose(actual_folded, actual_unfolded) - - def testConvLayerFoldedBatchNormFPropEval(self): - actual_unfolded = self._evalConvLayerFProp( - batch_norm=True, bn_fold_weights=False, is_eval=True) - actual_folded = self._evalConvLayerFProp( - batch_norm=True, bn_fold_weights=True, is_eval=True) - print('testConvLayerFoldedBatchNormFPropEval folded = ', - np.array_repr(actual_folded)) - print('testConvLayerFoldedBatchNormFPropEval unfolded = ', - np.array_repr(actual_unfolded)) - self.assertAllClose(actual_folded, actual_unfolded) - - def testConv2DLayerNoPadding(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(24332) - p = layers.Conv2DLayerNoPadding.Params().Set( - name='test', filter_shape=(3, 3, 3, 5), filter_stride=(2, 2)) - l = p.Instantiate() - x = tf.random.normal(shape=[17, 64, 64, 3]) - y = l.FPropDefaultTheta(x) - - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - y_val = self.evaluate(y) - - self.assertEqual(y_val.shape, (17, 32, 32, 5)) - - def testConvLayerFoldedBatchNormFPropQuantized(self): - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output = [ - [[[ 0.36997819, 0.91361964], - [ 0.07550576, 0. ]], - - [[ 0.35487702, 0. ], - [ 1.92539668, 0. ]]], - [[[ 0.27937129, 0. ], - [ 0. , 0. ]], - - [[ 0. , 0.86831617], - [ 0. , 1.59317136]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - - actual_folded = self._evalConvLayerFProp( - batch_norm=True, bn_fold_weights=True, quantized=True) - print('testConvLayerFoldedBatchNormFPropQuantized folded = ', - np.array_repr(actual_folded)) - self.assertAllClose(actual_folded, expected_output) - - def testCausalConvLayerFProp(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ConvLayer.Params() - params.name = 'conv' - params.filter_shape = [2, 1, 3, 2] - params.filter_stride = [1, 1] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - params.causal_convolution = True - params.activation = 'NONE' - params.batch_norm = False - - conv_layer = layers.ConvLayer(params) - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 3, 3]), dtype=tf.float32) - # Change the input for the last two steps. - inputs2 = tf.concat([inputs1[:, :2, :, :], inputs1[:, 2:, :, :] + 0.5], 1) - - output1, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - output2, _ = conv_layer.FPropDefaultTheta(inputs2, in_padding1) - self.evaluate(tf.global_variables_initializer()) - v1, v2 = self.evaluate([output1, output2]) - tf.logging.info('CausalConv output: %s', np.array_repr(v1)) - # pylint: disable=bad-whitespace,bad-continuation,line-too-long - self.assertAllClose(v1, [ - [[[-0.01093466, 0.00369835], - [ 0.03474921, 0.01418608], - [ 0.01887876, -0.00763734]], - [[-0.06922598, -0.04526342], - [-0.02428233, 0.02042499], - [-0.04504267, -0.01260209]], - [[-0.14253227, -0.11353028], - [-0.09067881, 0.03742362], - [ 0.01281691, 0.00644186]], - [[-0.06524619, -0.0555004 ], - [-0.18850081, -0.05325979], - [ 0.04960757, 0.05512709]]], - [[[-0.01077277, 0.03013588], - [ 0.00325067, -0.0223705 ], - [-0.00895232, 0.03310337]], - [[ 0.03113075, -0.02388876], - [ 0.03238059, 0.00590346], - [ 0.12839797, -0.02194144]], - [[-0.09115655, -0.06798521], - [-0.09801255, -0.01440183], - [-0.04321899, 0.00340509]], - [[-0.089603 , -0.07257183], - [-0.04469771, -0.0389927 ], - [-0.01747611, 0.00903451]]] - ]) # pyformat: disable - # pylint: enable=bad-whitespace,bad-continuation,line-too-long - self.assertAllClose(v1[:, :2, :, :], v2[:, :2, :, :]) - with self.assertRaises(AssertionError): - self.assertAllClose(v1[:, 2:, :, :], v2[:, 2:, :, :]) - - def testCausalConv2DLayerFProp(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ConvLayer.Params() - params.name = 'causal_conv' - params.filter_shape = [2, 2, 3, 2] - params.filter_stride = [1, 1] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - params.causal_convolution = True - params.activation = 'NONE' - params.batch_norm = False - - conv_layer = layers.ConvLayer(params) - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 3, 3]), dtype=tf.float32) - - output1, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - v1 = self.evaluate(output1) - tf.logging.info('CausalConv output: %s', np.array_repr(v1)) - # pylint: disable=bad-whitespace,bad-continuation,line-too-long - self.assertAllClose(v1, [ - [[[-0.065196 , -0.0597635 ], - [ 0.02871699, -0.02915794], - [-0.00529849, -0.02677475]], - [[-0.0227601 , 0.06118587], - [ 0.25884673, -0.13917476], - [ 0.03899311, -0.06894699]], - [[-0.28780231, -0.12121122], - [ 0.2447218 , 0.09553684], - [-0.07054863, 0.12110104]], - [[ 0.17036264, -0.00258163], - [ 0.28644818, -0.02746056], - [ 0.06173857, -0.11599959]]], - [[[ 0.1468567 , 0.12725323], - [-0.00131077, -0.03644447], - [ 0.0266833 , 0.01140832]], - [[-0.23816 , -0.07873908], - [-0.07348203, 0.25653225], - [-0.21931274, -0.0569509 ]], - [[-0.06972647, -0.03123237], - [ 0.07432974, -0.03340006], - [ 0.10474236, 0.00807726]], - [[ 0.07581483, 0.25381109], - [ 0.07091375, -0.14229891], - [ 0.05247882, -0.08783717]]] - ]) # pyformat: disable - # pylint: enable=bad-whitespace,bad-continuation,line-too-long - - def testCausalConv2DEqualsConv2DWithPadding(self): - # Causal conv is equivalent to regular conv with zero pre-padding. - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ConvLayer.Params() - params.name = 'causal_conv' - params.filter_shape = [2, 2, 3, 2] - params.filter_stride = [1, 1] - params.params_init = py_utils.WeightInit.Gaussian(0.1, seed=12345) - - params.causal_convolution = True - params.activation = 'NONE' - params.batch_norm = False - causal_conv_layer = layers.ConvLayer(params) - - normal_conv_params = params.Copy() - normal_conv_params.name = 'conv' - normal_conv_params.causal_convolution = False - normal_conv_layer = layers.ConvLayer(normal_conv_params) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 3, 3]), dtype=tf.float32) - # Causal conv with kernel height (time) = 2 requires prepadding size 1. - inputs1_pad = tf.concat([tf.zeros([2, 1, 3, 3]), inputs1], axis=1) - - output_causal, _ = causal_conv_layer.FPropDefaultTheta(inputs1) - output_normal, _ = normal_conv_layer.FPropDefaultTheta(inputs1_pad) - self.evaluate(tf.global_variables_initializer()) - v_causal, v_normal = self.evaluate([output_causal, output_normal]) - # Normal conv would produce an extra timestep due to SAME padding at the - # end. - self.assertAllClose(v_causal, v_normal[:, :-1]) - - def testCausalConv2DEqualsConv2DWithKernelHeightOne(self): - # When kernel height (time) = 1, causal convolution regresses to normal - # convolution with SAME padding. - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ConvLayer.Params() - params.name = 'causal_conv' - params.filter_shape = [1, 2, 3, 2] - params.filter_stride = [1, 1] - params.params_init = py_utils.WeightInit.Gaussian(0.1, seed=12345) - - params.causal_convolution = True - params.activation = 'NONE' - params.batch_norm = False - causal_conv_layer = layers.ConvLayer(params) - - normal_conv_params = params.Copy() - normal_conv_params.name = 'conv' - normal_conv_params.causal_convolution = False - normal_conv_layer = layers.ConvLayer(normal_conv_params) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 3, 3]), dtype=tf.float32) - - output_causal, _ = causal_conv_layer.FPropDefaultTheta(inputs1) - output_normal, _ = normal_conv_layer.FPropDefaultTheta(inputs1) - self.evaluate(tf.global_variables_initializer()) - v_causal, v_normal = self.evaluate([output_causal, output_normal]) - - self.assertAllClose(v_causal, v_normal) - - def testConvLayerBackProp(self): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ConvLayer.Params() - params.name = 'conv' - params.filter_shape = [3, 3, 3, 2] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = layers.ConvLayer(params) - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 3]), dtype=tf.float32) - output1, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - loss = tf.reduce_sum(output1) - - all_vars = tf.trainable_variables() - self.assertEqual(3, len(all_vars)) - - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v) for v in all_vars - ] - - for sg, ng in zip(sym_grads, num_grads): - self.assertAllClose(sg, ng, rtol=1e-02, atol=1e-02) - - def testConvLayerFPropTanh(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ConvLayer.Params() - params.activation = 'TANH' - params.name = 'conv' - params.filter_shape = [3, 3, 3, 2] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = layers.ConvLayer(params) - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 3]), dtype=tf.float32) - - output1, _ = conv_layer.FPropDefaultTheta(inputs1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_output1 = [ - [[[ 0.35109526, 0.72346997], - [ 0.0751792 , -0.84315312]], - [[ 0.33594984, -0.18976833], - [ 0.95773894, -0.28015777]]], - [[[ 0.27572086, -0.26577294], - [-0.38503852, -0.88501388]], - [[-0.92332661, 0.69921255], - [-0.75103623, 0.9219743 ]]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = output1.eval() - print(['actual = ', actual]) - self.assertAllClose(expected_output1, actual) - - def testConvSetLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ConvSetLayer.Params() - params.name = 'conv_set' - params.filter_shapes = [[3, 3, 3, 32], [8, 5, 3, 64]] - params.cnn_tpl.filter_stride = [2, 2] - params.cnn_tpl.params_init = py_utils.WeightInit.Gaussian(0.1) - - layers.ConvSetLayer(params) - - def _evalConvSetLayerFProp(self, - batch_norm=True, - bn_fold_weights=False, - weight_norm=False, - bias=False, - activation='RELU', - conv_last=False, - strides=(2, 2), - dilation_rate=(1, 1), - quantized=False, - dump_graphdef=False): - self._ClearCachedSession() - tf.reset_default_graph() - with self.session(use_gpu=True) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ConvSetLayer.Params() - params.name = 'conv_set' - params.filter_shapes = [[2, 2, 6, 1], [3, 5, 6, 3]] - params.cnn_tpl.filter_stride = strides - params.cnn_tpl.dilation_rate = dilation_rate - params.cnn_tpl.params_init = py_utils.WeightInit.Gaussian(0.1) - params.cnn_tpl.conv_last = conv_last - params.cnn_tpl.batch_norm = batch_norm - params.cnn_tpl.bn_fold_weights = bn_fold_weights - params.cnn_tpl.weight_norm = weight_norm - params.cnn_tpl.bias = bias - params.cnn_tpl.activation = activation - - if quantized: - params.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - - conv_set_layer = layers.ConvSetLayer(params) - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 6]), dtype=tf.float32) - - output1, _ = conv_set_layer.FPropDefaultTheta(inputs1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - - if dump_graphdef: - print('ConvSet GraphDef:', sess.graph.as_graph_def()) - assert False, 'Disable "dump_graphdef" before submit' - - return output1.eval() - - def testConvSetLayerFProp(self): - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - expected_output1 = [ - [[[ 1.04307961, 0. , 1.27613628, 0. ], - [ 0. , 0. , 0. , 1.21081829 ]], - [[ 0. , 0.18475296, 0. , 0. ], - [ 1.34087086 , 2.2726357 , 0. , 0. ]]], - [[[ 0. , 0.25231963, 0. , 0. ], - [ 1.13677704 , 0. , 0.996117 , 1.836285 ]], - [[ 0. , 0. , 1.04101253, 0. ], - [ 0.12628449 , 0.37599814, 0.3134549 , 0.51208746 ]]] - ] - # pyformat: enable - # pylint: enable=bad-whitespace,bad-continuation - actual = self._evalConvSetLayerFProp() - print(['actual = ', np.array_repr(actual)]) - self.assertAllClose(expected_output1, actual) - - def testConvSetLayerFPropQuantized(self): - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - expected_output1 = [ - [[[ 1.04016984, 0. , 1.28103447, 0. ], - [ 0. , 0. , 0. , 1.20986581]], - [[ 0. , 0.18681753, 0. , 0. ], - [ 1.35328221, 2.26849842, 0. , 0. ]]], - [[[ 0. , 0.24909003, 0. , 0. ], - [ 1.14100266, 0. , 0.98746401, 1.83259094]], - [[ 0. , 0. , 1.04084051, 0. ], - [ 0.12736773, 0.38253111, 0.32025862, 0.5159722 ]]]] - # pyformat: enable - # pylint: enable=bad-whitespace,bad-continuation - actual = self._evalConvSetLayerFProp(bn_fold_weights=True, quantized=True) - # Note that we don't have many ways to verify in a unit test that the - # quant nodes were added properly; however, if their placement changes, - # it will very likely perturb the golden values above. If digging deeper, - # add 'dump_graphdef=True' to the above call and inspect the graphdef: - # There should be one layer of fake_quant* nodes before the ConcatV2. - print('actual = ', np.array_repr(actual)) - self.assertAllClose(expected_output1, actual) - - # TODO(yonghui): more test for convolution layer - - -class PoolingLayerTest(test_utils.TestCase, parameterized.TestCase): - - # TODO(lingvo): fix 'VALID' padding in pooling. - @parameterized.named_parameters( - { - 'testcase_name': 'max_pooling_same_padding', - 'pooling_type': 'MAX', - 'padding_algorithm': 'SAME', - 'window_shape': (3, 1), - 'window_stride': (1, 1), - 'inputs': np.array([-2, -10, -3, -4, 0, 0]), - 'input_paddings': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output': np.array([-2, -2, -3, -3, 0, 0]), - 'expected_output_padding': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output_without_padding': np.array([-2, -2, -3, 0, 0, 0]), - }, { - 'testcase_name': 'avg_pooling_same_padding', - 'pooling_type': 'AVG', - 'padding_algorithm': 'SAME', - 'window_shape': (3, 1), - 'window_stride': (1, 1), - 'inputs': np.array([-2, 0, 2, 4, 0, 0]), - 'input_paddings': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output': np.array([-1, 0, 2, 3, 0, 0]), - 'expected_output_padding': np.array([0, 0, 0, 0, 1, 1]), - 'expected_output_without_padding': np.array([-1, 0, 2, 2, 4 / 3, 0]), - }) - def testSimpleCases(self, inputs, input_paddings, pooling_type, window_shape, - window_stride, padding_algorithm, expected_output, - expected_output_padding, expected_output_without_padding): - inputs = inputs[np.newaxis, :, np.newaxis, np.newaxis] - input_paddings = input_paddings[np.newaxis, :] - param = layers.PoolingLayer.Params().Set( - name='test_layer', - pooling_type=pooling_type, - window_shape=window_shape, - window_stride=window_stride, - padding_algorithm=padding_algorithm) - pooling_layer = param.Instantiate() - with self.session(use_gpu=True) as sess: - inputs = tf.convert_to_tensor(inputs, dtype=tf.float32) - input_paddings = tf.convert_to_tensor(input_paddings, dtype=tf.float32) - output, output_paddings = pooling_layer.FPropDefaultTheta( - inputs, input_paddings) - output_without_padding, _ = pooling_layer.FPropDefaultTheta(inputs) - tf.global_variables_initializer().run() - output_val, output_paddings_val, output_without_padding_val = sess.run( - [output, output_paddings, output_without_padding]) - - self.assertAllClose(expected_output, output_val.flatten()) - self.assertAllEqual(expected_output_padding, output_paddings_val.flatten()) - self.assertAllClose(expected_output_without_padding, - output_without_padding_val.flatten()) - - def testPoolLayerFProp(self): - with self.session(use_gpu=True): - params = layers.PoolingLayer.Params() - params.name = 'pool' - params.window_shape = [3, 3] - params.window_stride = [1, 2] - - pool_layer = layers.PoolingLayer(params) - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.arange(96, dtype='float32').reshape([2, 4, 4, 3]), - dtype=tf.float32) - - output1, _ = pool_layer.FPropDefaultTheta(inputs1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - print([np.array_repr(output1.eval())]) - # pyformat: disable - expected_output1 = [ - [[[18., 19., 20.], - [21., 22., 23.]], - [[30., 31., 32.], - [33., 34., 35.]], - [[42., 43., 44.], - [45., 46., 47.]], - [[42., 43., 44.], - [45., 46., 47.]]], - [[[66., 67., 68.], - [69., 70., 71.]], - [[78., 79., 80.], - [81., 82., 83.]], - [[90., 91., 92.], - [93., 94., 95.]], - [[90., 91., 92.], - [93., 94., 95.]]]] - # pyformat: enable - self.assertAllClose(expected_output1, output1.eval()) - - def testPoolLayerMoreShapes(self): - with self.session(use_gpu=True): - for window_shape, window_stride in [ - [[3, 3], [1, 2]], - [[2, 2], [1, 2]], - [[3, 4], [1, 3]], - ]: - params = layers.PoolingLayer.Params() - params.name = 'pool' - params.window_shape = window_shape - params.window_stride = window_stride - - pool_layer = layers.PoolingLayer(params) - in_padding1 = tf.zeros([2, 4], dtype=tf.float32) - inputs1 = tf.constant( - np.arange(96, dtype='float32').reshape([2, 4, 4, 3]), - dtype=tf.float32) - - output1, _ = pool_layer.FPropDefaultTheta(inputs1, in_padding1) - - output2 = tf.nn.max_pool(inputs1, [1] + params.window_shape + [1], - [1] + params.window_stride + [1], 'SAME') - - predicted_out_shape = pool_layer.OutShape(inputs1.shape.as_list()) - - self.evaluate(tf.global_variables_initializer()) - output1_v = output1.eval() - self.assertAllClose(output2.eval(), output1_v) - self.assertAllClose(predicted_out_shape, output1_v.shape) - - -class BlurPoolLayerTest(test_utils.TestCase): - - def _testBlurPool(self, subsample_type, blur_filter, expected_output): - with self.session(use_gpu=True): - p = layers.BlurPoolLayer.Params().Set( - name='blur_pool', - input_channels=3, - subsample_type=subsample_type, - blur_filter=blur_filter) - - layer = p.Instantiate() - in_padding1 = tf.convert_to_tensor([[0, 0, 0, 1], [0, 0, 1, 1]], - dtype=tf.float32) - expected_out_padding = [[0, 1], [0, 1]] - inputs1 = tf.constant( - np.arange(24, dtype='float32').reshape([2, 4, 1, 3]), - dtype=tf.float32) - - output1, out_padding1 = layer.FPropDefaultTheta(inputs1, in_padding1) - self.evaluate(tf.global_variables_initializer()) - print([np.array_repr(output1.eval())]) - self.assertAllClose(expected_output, output1.eval()) - self.assertAllClose(expected_out_padding, out_padding1.eval()) - - def testBlurPool1D(self): - expected_output = np.array([[[[1.125, 1.8125, 2.5]], [[0, 0, 0]]], - [[[8.25, 8.875, 9.5]], [[0, 0, 0]]]], - dtype=np.float32) - self._testBlurPool('1D', 'B5', expected_output) - - def testBlurPool2D(self): - expected_output = np.array([[[[0.421875, 0.6796875, 0.9375]], [[0, 0, 0]]], - [[[3.09375, 3.328125, 3.5625]], [[0, 0, 0]]]], - dtype=np.float32) - self._testBlurPool('2D', 'B5', expected_output) - - -class ProjectionLayerTest(test_utils.TestCase, parameterized.TestCase): - - def testProjectionLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ProjectionLayer.Params() - params.name = 'proj' - params.input_dim = 2 - params.output_dim = 3 - params.batch_norm = True - params.params_init = py_utils.WeightInit.Gaussian(0.1) - layers.ProjectionLayer(params) - proj_vars = tf.get_collection('ProjectionLayer_vars') - proj_var_names = [x.name for x in proj_vars] - self.assertEqual(['proj/w/var:0'], proj_var_names) - bn_vars = tf.get_collection('BatchNormLayer_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [ - 'proj/beta/var:0', 'proj/gamma/var:0', 'proj/moving_mean/var:0', - 'proj/moving_variance/var:0' - ] - self.assertEqual(expected_var_names, bn_var_names) - - def _evalProjectionLayer(self, - reshape_to_2d=False, - batch_norm=True, - weight_norm=False, - activation='RELU', - affine_last=False, - input_dim=3, - output_dim=2, - quantized=False, - has_bias=False, - bn_fold_weights=None, - expect_bn_fold_weights=None, - is_eval=False, - layer_callback=None, - bn_decay=0.999, - bn_use_moving_avg_in_training=False, - use_einsum=True, - block_dim=0): - self._ClearCachedSession() - tf.reset_default_graph() - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ProjectionLayer.Params() - params.name = 'proj' - params.input_dim = input_dim - params.output_dim = output_dim - params.has_bias = has_bias - if has_bias: - params.bias_init = 5.0 - params.activation = activation - params.batch_norm = batch_norm - params.weight_norm = weight_norm - params.affine_last = affine_last - params.params_init = py_utils.WeightInit.Gaussian(0.1) - params.bn_fold_weights = bn_fold_weights - params.bn_params.decay = bn_decay - params.bn_params.use_moving_avg_in_training = bn_use_moving_avg_in_training - params.use_einsum = use_einsum - params.block_dim = block_dim - params.use_blocked_matmul = True if block_dim > 0 else False - - if quantized: - cc_schedule = quant_utils.FakeQuantizationSchedule.Params().Set( - clip_end_step=1, quant_start_step=1) - qdomain_default = quant_utils.SymmetricScheduledClipQDomain.Params( - ).Set(cc_schedule=cc_schedule.Copy()) - params.qdomain.default = qdomain_default.Copy() - - in_padding = tf.zeros([2, 4, 1], dtype=tf.float32) - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, input_dim]), dtype=tf.float32) - if reshape_to_2d: - in_padding = tf.reshape(in_padding, [-1, 1]) - inputs = tf.reshape(inputs, [-1, input_dim]) - - proj_layer = layers.ProjectionLayer(params) - if layer_callback: - layer_callback(proj_layer) - if expect_bn_fold_weights is not None: - self.assertEqual(expect_bn_fold_weights, proj_layer._is_bn_folded) - - output = proj_layer.FPropDefaultTheta(inputs, in_padding) - self.evaluate(tf.global_variables_initializer()) - if quantized: - # Put it in the fully quantized range. - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 5)) - return output.eval() - - def testProjectionLayerFProp(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 0. , 0.33779466], - [ 0.4527415 , 0.99911398], - [ 0.44320837, 0. ], - [ 0. , 0.04557215]], - [[ 0.69273949, 0. ], - [ 0.30908319, 0. ], - [ 0. , 0. ], - [ 0. , 1.54578114]]] - # pyformat: enable - # pylint: enable=bad-whitespace - for reshape_to_2d in (False, True): - actual = self._evalProjectionLayer( - reshape_to_2d=reshape_to_2d, expect_bn_fold_weights=False) - if reshape_to_2d: - expected_output = np.reshape(np.array(expected_output), (-1, 2)) - tf.logging.info('expected = %s', expected_output) - tf.logging.info('actual = %s', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testProjectionLayerFPropWithBias(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 4.98987579, 5.03493643], - [ 5.01192808, 5.0917592 ], - [ 5.01156807, 4.99741936], - [ 4.96849394, 5.00982761]], - [[ 5.02098131, 4.98014927], - [ 5.00650883, 4.87676954], - [ 4.98995209, 4.91770315], - [ 4.95948696, 5.138731 ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - # Tested without batch_norm because batch_norm will mostly cancel out the - # affect of bias. - actual = self._evalProjectionLayer( - has_bias=True, - batch_norm=False, - expect_bn_fold_weights=False, - activation='RELU6') - tf.logging.info('expected = %s', expected_output) - tf.logging.info('actual = %s', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testProjectionLayerExplicitFolding(self): - unfolded = self._evalProjectionLayer( - bn_fold_weights=False, expect_bn_fold_weights=False) - folded = self._evalProjectionLayer( - bn_fold_weights=True, expect_bn_fold_weights=True) - tf.logging.info('unfolded = %s', np.array_repr(unfolded)) - tf.logging.info('folded = %s', np.array_repr(folded)) - self.assertAllClose(folded, unfolded) - - def testProjectionLayerExplicitFoldingEval(self): - unfolded = self._evalProjectionLayer( - bn_fold_weights=False, expect_bn_fold_weights=False, is_eval=True) - folded = self._evalProjectionLayer( - bn_fold_weights=True, expect_bn_fold_weights=True, is_eval=True) - tf.logging.info('unfolded = %s', np.array_repr(unfolded)) - tf.logging.info('folded = %s', np.array_repr(folded)) - self.assertAllClose(folded, unfolded) - - def testProjectionLayerExplicitFoldingNoBatchNorm(self): - unfolded = self._evalProjectionLayer( - batch_norm=False, bn_fold_weights=False, expect_bn_fold_weights=False) - # Note that weight folding will report as disabled because batch norm is - # disabled. - folded = self._evalProjectionLayer( - batch_norm=False, bn_fold_weights=True, expect_bn_fold_weights=False) - tf.logging.info('unfolded = %s', np.array_repr(unfolded)) - tf.logging.info('folded = %s', np.array_repr(folded)) - self.assertAllClose(folded, unfolded) - - def testProjectionLayerExplicitFoldingWithWeightNorm(self): - unfolded = self._evalProjectionLayer( - weight_norm=True, bn_fold_weights=False, expect_bn_fold_weights=False) - folded = self._evalProjectionLayer( - weight_norm=True, bn_fold_weights=True, expect_bn_fold_weights=True) - tf.logging.info('unfolded = %s', np.array_repr(unfolded)) - tf.logging.info('folded = %s', np.array_repr(folded)) - self.assertAllClose(folded, unfolded) - - def testProjectionLayerWeightNorm(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 0. , 0.36285588], - [ 0.82909501, 1.07323885], - [ 0.81163716, 0. ], - [ 0. , 0.04895319]], - [[ 1.26859784, 0. ], - [ 0.56601691, 0. ], - [ 0. , 0. ], - [ 0. , 1.66046333]]] - # pyformat: enable - # pylint: enable=bad-whitespace - for reshape_to_2d in (False, True): - actual = self._evalProjectionLayer( - reshape_to_2d=reshape_to_2d, weight_norm=True) - if reshape_to_2d: - expected_output = np.reshape(np.array(expected_output), (-1, 2)) - tf.logging.info('expected = %s', expected_output) - tf.logging.info('actual = %s', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testProjectionLayerAffineFirstVsLastFProp(self): - """Compare results of affine first vs. last.""" - # ... with batch_norm and activation disabled. - self.assertAllClose( - self._evalProjectionLayer( - batch_norm=False, activation='NONE', affine_last=False), - self._evalProjectionLayer( - batch_norm=False, activation='NONE', affine_last=True)) - - def testProjectionLayerAffineLastFProp(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output1 = [ - [[ 0. , 0. ], - [ 0.03410175, 0.04741348], - [ 0.02665393, -0.02072855], - [-0.01116518, -0.06280501]], - [[ 0.04615254, -0.03589247], - [-0.00376316, -0.0464084 ], - [-0.01111402, -0.13706152], - [-0.02596203, 0.16340451]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = self._evalProjectionLayer(affine_last=True) - print(['actual = ', np.array_repr(actual)]) - self.assertAllClose(expected_output1, actual) - - def testProjectionLayerBackProp(self): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.ProjectionLayer.Params() - params.name = 'proj' - params.dtype = tf.float64 - params.input_dim = 3 - params.output_dim = 2 - params.batch_norm = True - params.params_init = py_utils.WeightInit.Gaussian(0.01) - - proj_layer = layers.ProjectionLayer(params) - in_padding1 = tf.zeros([2, 4, 1], dtype=tf.float64) - inputs1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 3]), dtype=tf.float64) - output1 = proj_layer.FPropDefaultTheta(inputs1, in_padding1) - loss = tf.reduce_sum(output1) - - all_vars = tf.trainable_variables() - self.assertLen(all_vars, 3) - - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v, 1e-6) - for v in all_vars - ] - - for sg, ng in zip(sym_grads, num_grads): - self.assertAllClose(sg, ng, rtol=1e-06, atol=1e-06) - - def testProjectionLayerFPropQuantizedWithUnfusedActivation(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[-0.1328125, 0.3125 ], - [ 0.421875 , 0.734375 ], - [ 0.421875 , -0.109375 ], - [-0.6015625, 0.0078125]], - [[ 0.6015625, -0.3046875], - [ 0.3046875, -0.7578125], - [-0.125 , -0.7578125], - [-0.734375 , 0.7578125]]] - # pyformat: enable - # pylint: enable=bad-whitespace - def CheckLayer(proj_layer): - # Should not error because this qtensor is defined. - proj_layer.QTensor('activation', tf.convert_to_tensor(0.)) - # The intermediate tensor should be defined. - proj_layer.QTensor('affine_matmul', tf.convert_to_tensor(0.)) - - # When quantization enabled, batchnorm folding should auto enable. - # TANH is unfused. - actual = self._evalProjectionLayer( - activation='TANH', - quantized=True, - expect_bn_fold_weights=True, - layer_callback=CheckLayer) - tf.logging.info('expected = %s', expected_output) - tf.logging.info('actual = %s', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testProjectionLayerFPropQuantizedWithFusedActivation(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 0. , 0.3203125], - [ 0.453125 , 0.9375 ], - [ 0.4453125, 0. ], - [ 0. , 0.0078125]], - [[ 0.6953125, 0. ], - [ 0.3125 , 0. ], - [ 0. , 0. ], - [ 0. , 0.9921875]]] - # pyformat: enable - # pylint: enable=bad-whitespace - def CheckLayer(proj_layer): - # Should not error because this qtensor is defined. - proj_layer.QTensor('activation', tf.convert_to_tensor(0.)) - with self.assertRaises(AssertionError): - # The intermediate tensor should *not* be quantized. - proj_layer.QTensor('affine_matmul', tf.convert_to_tensor(0.)) - - # When quantization enabled, batchnorm folding should auto enable. - # RELU6 is fused. - actual = self._evalProjectionLayer( - activation='RELU6', - quantized=True, - expect_bn_fold_weights=True, - layer_callback=CheckLayer) - tf.logging.info('expected = %s', expected_output) - tf.logging.info('actual = %s', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testProjectionLayerFPropQuantizedOnlyMatmul(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[-0.0078125, 0.0390625], - [ 0.0078125, 0.09375 ], - [ 0.0078125, 0. ], - [-0.03125 , 0.015625 ]], - [[ 0.015625 , -0.015625 ], - [ 0.0078125, -0.125 ], - [-0.0078125, -0.078125 ], - [-0.0390625, 0.1484375]]] - # pyformat: enable - # pylint: enable=bad-whitespace - def CheckLayer(proj_layer): - # Should not error because this qtensor is defined. - proj_layer.QTensor('affine_matmul', tf.convert_to_tensor(0.)) - - actual = self._evalProjectionLayer( - activation='NONE', - quantized=True, - batch_norm=False, - expect_bn_fold_weights=False, - layer_callback=CheckLayer) - tf.logging.info('expected = %s', expected_output) - tf.logging.info('actual = %s', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testProjectionLayerFPropQuantizedOnlyMatmulBias(self): - # pylint: disable=bad-whitespace - # pyformat: disable - # Saturated because of the out of range bias. - expected_output = [[[0.9921875, 0.9921875], [0.9921875, 0.9921875], - [0.9921875, 0.9921875], [0.9921875, 0.9921875]], - [[0.9921875, 0.9921875], [0.9921875, 0.9921875], - [0.9921875, 0.9921875], [0.9921875, 0.9921875]]] - - # pyformat: enable - # pylint: enable=bad-whitespace - def CheckLayer(proj_layer): - # Should not error because this qtensor is defined. - proj_layer.QTensor('affine_matmul', tf.convert_to_tensor(0.)) - - actual = self._evalProjectionLayer( - activation='NONE', - quantized=True, - has_bias=True, - batch_norm=False, - expect_bn_fold_weights=False, - layer_callback=CheckLayer) - tf.logging.info('expected = %s', expected_output) - tf.logging.info('actual = %s', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testFCLayerConstruction(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.FCLayer.Params() - params.name = 'fc' - params.input_dim = 2 - params.output_dim = 3 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - layers.FCLayer(params) - proj_vars = tf.get_collection('FCLayer_vars') - proj_var_names = [x.name for x in proj_vars] - expected_var_names = ['fc/w/var:0', 'fc/b/var:0'] - self.assertEqual(expected_var_names, proj_var_names) - - def testFCLayerFProp(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.FCLayer.Params() - params.name = 'fc' - params.input_dim = 3 - params.output_dim = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - proj_layer = layers.FCLayer(params) - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 3]), dtype=tf.float32) - - output = proj_layer.FPropDefaultTheta(inputs) - self.evaluate(tf.global_variables_initializer()) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 0. , 0.04883499], - [ 0.17094055, 0. ], - [ 0.09287541, 0. ], - [ 0. , 0.19471419]], - [[ 0.15290432, 0. ], - [ 0. , 0. ], - [ 0. , 0.10548697], - [ 0. , 0.22610095]]] - # pyformat: enable - # pylint: enable=bad-whitespace - actual = output.eval() - print(['actual = ', np.array_repr(actual)]) - self.assertAllClose(expected_output, actual) - - def testFCLayerBackProp(self): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.FCLayer.Params() - params.name = 'fc' - params.dtype = tf.float64 - params.input_dim = 3 - params.output_dim = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.01) - - proj_layer = layers.FCLayer(params) - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 3]), dtype=tf.float64) - output = proj_layer.FPropDefaultTheta(inputs) - loss = tf.reduce_sum(output) - - all_vars = tf.trainable_variables() - self.assertLen(all_vars, 2) - - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v, 1e-6) - for v in all_vars - ] - - for sg, ng in zip(sym_grads, num_grads): - self.assertAllClose(sg, ng, rtol=1e-06, atol=1e-06) - - def testProjectionLayerFPropUsingMovingAvgInTraining(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [[[0. , 0.03491905], - [0.01192194, 0.09171353], - [0.01156251, 0. ], - [0. , 0.00982281]], - [[0.02097072, 0. ], - [0.00650552, 0. ], - [0. , 0. ], - [0. , 0.13866161]]] - # pyformat: enable - # pylint: enable=bad-whitespace - for reshape_to_2d in (False, True): - actual = self._evalProjectionLayer( - reshape_to_2d=reshape_to_2d, - expect_bn_fold_weights=False, - bn_use_moving_avg_in_training=True) - if reshape_to_2d: - expected_output = np.reshape(np.array(expected_output), (-1, 2)) - tf.logging.info('expected = %s', expected_output) - tf.logging.info('actual = %s', np.array_repr(actual)) - self.assertAllClose(expected_output, actual) - - def testProjectionLayerFPropEinsum(self): - output_with_einsum = self._evalProjectionLayer(use_einsum=True) - output_without_einsum = self._evalProjectionLayer(use_einsum=False) - self.assertAllClose(output_with_einsum, output_without_einsum) - - def testProjectionLayerFPropBlockMatmul(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [[[0., 0., 0., 0.], - [0., 0.03153732, 0.01891183, 0.02154643], - [0.01373154, 0.09479743, 0., 0.03639744], - [0.05870617, 0.19162716, 0.05567084, 0.15362406]], - - [[0.06591958, 0.2321615, 0., 0.14735883], - [0.1003774, 0.33637568, 0., 0.22276597], - [0.02362791, 0.10728893, 0.01922233, 0.07299631], - [0., 0., 0., 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - output_with_block_matmul = self._evalProjectionLayer( - input_dim=4, - output_dim=4, - batch_norm=False, - use_einsum=False, - block_dim=2) - tf.logging.info(output_with_block_matmul) - self.assertAllClose(output_with_block_matmul, expected_output) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [[[0., 0., 0., 0., 0.], - [0., 0., 0.06118044, 0., 0.02968791], - [0., 0., 0.09687695, 0., 0.], - [0.10228965, 0., 0.01826946, 0.0219113, 0.16076824]], - - [[0.03506518, 0.27432495, 0.25932777, 0., 0.], - [0., 0.00882578, 0.06655132, 0., 0.], - [0., 0., 0., 0.10196716, 0.], - [0., 0., 0.08253407, 0., 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - # Test case for odd input and output dimensions. - output_with_block_matmul = self._evalProjectionLayer( - input_dim=5, - output_dim=5, - batch_norm=False, - use_einsum=False, - block_dim=2) - tf.logging.info(output_with_block_matmul) - self.assertAllClose(output_with_block_matmul, expected_output) - - @parameterized.named_parameters( - { - 'testcase_name': 'RELU', - 'activation': 'RELU', - 'input_dims': [2, 4, 3], - 'expected_extra_flops': 0, - 'expected_per_fn_flops': 1 - }, - { - 'testcase_name': 'SIGMOID', - 'activation': 'SIGMOID', - 'input_dims': [2048, 10], - 'expected_extra_flops': 0, - 'expected_per_fn_flops': 4 - }, - { - 'testcase_name': 'BatchNorm', - 'activation': 'RELU', - 'input_dims': [2048, 10], - 'output_dim': 8, - 'expected_extra_flops': 2048 * 8 * 10, # 10 flops per element. - 'expected_per_fn_flops': 1, - 'batch_norm': True - }, - { - 'testcase_name': 'WeightNorm', - 'activation': 'RELU', - 'input_dims': [2048, 10], - 'output_dim': 100, - 'expected_extra_flops': 2 * 10 + 2 * 10 * 100 + 2, - 'expected_per_fn_flops': 1, - 'weight_norm': True - }, - { - 'testcase_name': 'Bias', - 'activation': 'RELU', - 'input_dims': [2048, 10], - 'output_dim': 100, - 'expected_extra_flops': 100, - 'expected_per_fn_flops': 1, - 'has_bias': True - }) - # Extra flops are for bias, batch norm, weight norm, etc. - def testProjectionLayerMeta(self, - input_dims, - expected_per_fn_flops, - expected_extra_flops, - activation='RELU', - batch_norm=False, - weight_norm=False, - has_bias=False, - output_dim=2): - with self.session(use_gpu=True): - params = layers.ProjectionLayer.Params() - params.name = 'fc' - params.input_dim = input_dims[-1] - params.output_dim = output_dim - params.params_init = py_utils.WeightInit.Gaussian(0.1) - params.activation = activation - params.batch_norm = batch_norm - params.weight_norm = weight_norm - params.has_bias = has_bias - - meta = params.cls.FPropMeta(params, tshape.Shape(input_dims)) - self.assertEqual( - meta.flops, - expected_extra_flops + (np.prod(input_dims[:-1]) * - (2 * params.input_dim * params.output_dim + - params.output_dim * expected_per_fn_flops))) - self.assertEqual(meta.out_shapes[0].ToTensorShape().as_list(), - input_dims[:-1] + [params.output_dim]) - - -class StackingOverTimeLayerTest(test_utils.TestCase): - - def testStackingOverTimeFProp(self): - with self.session(use_gpu=True): - params = layers.StackingOverTime.Params() - params.name = 'stackingOverTime' - params.left_context = 2 - params.right_context = 0 - params.stride = 2 - - stacker = layers.StackingOverTime(params) - self.assertEqual(stacker.window_size, 3) - - inputs = tf.constant([[[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6]], - [[7, 7], [8, 8], [0, 0], [0, 0], [0, 0], [0, 0]]], - dtype=tf.float32) - paddings = tf.constant( - [[[0], [0], [0], [0], [0], [0]], [[0], [0], [1], [1], [1], [1]]], - dtype=tf.float32) - - outputs, output_paddings = stacker.FProp(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - print([np.array_repr(outputs.eval())]) - - expected_outputs = [ - [[0, 0, 0, 0, 1, 1], [1, 1, 2, 2, 3, 3], [3, 3, 4, 4, 5, 5]], - [[0, 0, 0, 0, 7, 7], [7, 7, 8, 8, 0, 0], [0, 0, 0, 0, 0, 0]], - ] - self.assertAllClose(expected_outputs, outputs.eval()) - - expected_output_paddings = [[[0], [0], [0]], [[0], [0], [1]]] - self.assertAllClose(expected_output_paddings, output_paddings.eval()) - - def testStackingOverTimeFProp2(self): - with self.session(use_gpu=True): - params = layers.StackingOverTime.Params() - params.name = 'stackingOverTime' - params.left_context = 0 - params.right_context = 1 - params.stride = 2 - - stacker = layers.StackingOverTime(params) - self.assertEqual(stacker.window_size, 2) - - inputs = tf.random.normal([2, 21, 16], seed=78123) - paddings = 1.0 - tf.sequence_mask([9, 14], 21, tf.float32) - paddings = tf.expand_dims(paddings, -1) - - outputs, output_paddings = stacker.FProp(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - - inputs_v, outputs_v, paddings_v = self.evaluate( - [inputs, outputs, output_paddings]) - - # length - self.assertAllEqual([5, 7], np.sum(1.0 - paddings_v, (1, 2))) - # input and output sums are equal - self.assertAllClose(np.sum(inputs_v, (1, 2)), np.sum(outputs_v, (1, 2))) - - def testStackingOverTimeIdentityFProp(self): - with self.session(use_gpu=True): - params = layers.StackingOverTime.Params() - params.name = 'stackingOverTime' - params.left_context = 0 - params.right_context = 0 - params.stride = 1 - - stacker = layers.StackingOverTime(params) - self.assertEqual(stacker.window_size, 1) - inputs = tf.constant([[[1], [2], [3], [4], [5]]], dtype=tf.float32) - paddings = tf.zeros([1, 5, 1], dtype=tf.float32) - - outputs, output_paddings = stacker.FProp(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - print([np.array_repr(outputs.eval())]) - expected_outputs = [[[1], [2], [3], [4], [5]]] - self.assertAllClose(expected_outputs, outputs.eval()) - expected_output_paddings = [[[0], [0], [0], [0], [0]]] - self.assertAllClose(expected_output_paddings, output_paddings.eval()) - - def _testUnstack(self, inputs, **kwargs): - params = layers.StackingOverTime.Params().Set( - name='stackingOverTime', **kwargs) - with self.session(use_gpu=True): - stacker = params.Instantiate() - stacked, _ = stacker.FProp(inputs) - unstacked = stacker.Unstack(stacked) - inputs, stacked, unstacked = self.evaluate([inputs, stacked, unstacked]) - - batch, input_length, depth = inputs.shape - stacked_length = stacked.shape[1] - stride = stacker.params.stride - right_context = stacker.params.right_context - - self.assertAllEqual( - unstacked.shape, - [batch, (stacked_length - 1) * stride + right_context + 1, depth]) - if right_context + 1 >= stride: - self.assertGreaterEqual(unstacked.shape[1], input_length) - self.assertAllClose(inputs, unstacked[:, :input_length]) - else: - self.assertLessEqual(unstacked.shape[1], input_length) - # The final up to stride - right_context - 1 values are missing. - self.assertLessEqual(input_length - unstacked.shape[1], - stride - right_context - 1) - self.assertAllClose(inputs[:, :unstacked.shape[1]], unstacked) - - def testStackingOverTimeUnstack(self): - batch_size = 2 - length = 7 - depth = 3 - inputs = tf.reshape( - tf.cast(tf.range(batch_size * length * depth), tf.float32), - [batch_size, length, depth]) - self._testUnstack(inputs, left_context=2, stride=1) - with self.assertRaises(ValueError): - self._testUnstack(inputs, stride=2) - self._testUnstack(inputs, stride=2, right_context=3) - self._testUnstack(inputs, left_context=2, stride=3) - self._testUnstack(inputs, stride=4, right_context=3) - self._testUnstack(inputs, stride=4, left_context=1, right_context=2) - - -class SingleShardEmbeddingLayerTest(test_utils.TestCase): - - def testSingleShardEmbeddingLayer(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.SingleShardEmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 80000 - params.embedding_dim = 128 - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - emb_layer = params.Instantiate() - ids = tf.constant([[89], [100]]) - embs = emb_layer.EmbLookupDefaultTheta(ids) - embs_sum = tf.reduce_sum(embs) - self.evaluate(tf.global_variables_initializer()) - test_utils.CompareToGoldenSingleFloat(self, 0.126485, embs_sum.eval()) - - def testCheckedIds(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.SingleShardEmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 16 - params.embedding_dim = 128 - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - emb_layer = params.Instantiate() - - neg_ids = tf.constant([[-1]]) - oov_ids = tf.constant([[params.vocab_size]]) - self.evaluate(tf.global_variables_initializer()) - - with self.assertRaises(tf.errors.InvalidArgumentError): - neg_embs = emb_layer.EmbLookupDefaultTheta(neg_ids) - neg_embs.eval() - with self.assertRaises(tf.errors.InvalidArgumentError): - oov_embs = emb_layer.EmbLookupDefaultTheta(oov_ids) - oov_embs.eval() - - def testEmbeddingLayerScaling(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.SingleShardEmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 80000 - params.embedding_dim = 128 - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - params.scale_sqrt_depth = True - emb_layer = params.Instantiate() - ids = tf.constant([[89], [100]]) - embs = emb_layer.EmbLookupDefaultTheta(ids) - embs_sum = tf.reduce_sum(embs) - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(0.126485 * params.embedding_dim**0.5, - self.evaluate(embs_sum)) - - def testEmbeddingLayerWithVN(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.SimpleEmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 80000 - params.embedding_dim = 128 - params.params_init = py_utils.WeightInit.Gaussian(0.01, seed=398847392) - params.vn.global_vn = True - params.vn.per_step_vn = False - params.vn.scale = 0.5 - params.vn.seed = 398847392 - params.random_seed = 12345 - emb_layer = params.Instantiate() - self.assertEqual(len(emb_layer.vars.Flatten()), 1) - ids = tf.constant([[89], [100]]) - embs = emb_layer.EmbLookupDefaultTheta(ids) - embs_sum = tf.reduce_sum(embs) - self.evaluate(tf.global_variables_initializer()) - test_utils.CompareToGoldenSingleFloat(self, 1.56561, embs_sum.eval()) - - -class EmbeddingLayerTest(test_utils.TestCase): - - def testEmbeddingLayer(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.EmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 80000 - params.embedding_dim = 128 - params.max_num_shards = 4 - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - emb_layer = layers.EmbeddingLayer(params) - ids = tf.constant([[89], [100]]) - embs = emb_layer.EmbLookupDefaultTheta(ids) - embs_sum = tf.reduce_sum(embs) - self.evaluate(tf.global_variables_initializer()) - test_utils.CompareToGoldenSingleFloat(self, 0.234941, embs_sum.eval()) - - def testCheckedIds(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.EmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 16 - params.embedding_dim = 128 - params.max_num_shards = 4 - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - emb_layer = layers.EmbeddingLayer(params) - - neg_ids = tf.constant([[-1]]) - oov_ids = tf.constant([[params.vocab_size]]) - self.evaluate(tf.global_variables_initializer()) - - with self.assertRaises(tf.errors.InvalidArgumentError): - neg_embs = emb_layer.EmbLookupDefaultTheta(neg_ids) - neg_embs.eval() - with self.assertRaises(tf.errors.InvalidArgumentError): - oov_embs = emb_layer.EmbLookupDefaultTheta(oov_ids) - oov_embs.eval() - - def testEmbeddingLayerScaling(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.EmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 80000 - params.embedding_dim = 128 - params.max_num_shards = 4 - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - params.scale_sqrt_depth = True - emb_layer = layers.EmbeddingLayer(params) - ids = tf.constant([[89], [100]]) - embs = emb_layer.EmbLookupDefaultTheta(ids) - embs_sum = tf.reduce_sum(embs) - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(0.23494134843349457 * params.embedding_dim**0.5, - self.evaluate(embs_sum)) - - def testEmbeddingLayerWithVN(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.EmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 80000 - params.embedding_dim = 128 - params.max_num_shards = 4 - params.params_init = py_utils.WeightInit.Gaussian(0.01, seed=398847392) - params.vn.global_vn = True - params.vn.per_step_vn = False - params.vn.scale = 0.5 - params.vn.seed = 398847392 - emb_layer = layers.EmbeddingLayer(params) - self.assertEqual(len(emb_layer.vars.Flatten()), 4) - ids = tf.constant([[89], [100]]) - embs = emb_layer.EmbLookupDefaultTheta(ids) - embs_sum = tf.reduce_sum(embs) - self.evaluate(tf.global_variables_initializer()) - test_utils.CompareToGoldenSingleFloat(self, -6.807296, embs_sum.eval()) - - def _testSimpleEmbeddingLayer(self, - use_matmul, - use_3d_weight_tensor, - fprop_mode, - scale_sqrt_depth=False): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(398847392) - params = layers.SimpleEmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 8000 - params.embedding_dim = 128 - params.use_matmul = use_matmul - params.fprop_mode = fprop_mode - params.use_3d_weight_tensor = use_3d_weight_tensor - params.scale_sqrt_depth = scale_sqrt_depth - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - - emb_layer = layers.SimpleEmbeddingLayer(params) - expected_fprop_mode = fprop_mode - if expected_fprop_mode is None: - expected_fprop_mode = 'matmul' if use_matmul else 'gather' - self.assertEqual(emb_layer._fprop_mode, expected_fprop_mode) - - emb_matrix = emb_layer.vars.wm - ids = tf.constant([[89], [100]]) - outputs = emb_layer.EmbLookupDefaultTheta(ids) - fast_outputs = emb_layer.EmbLookupDefaultThetaOnCpu(ids) - - with self.session(use_gpu=True, graph=g): - self.evaluate(tf.global_variables_initializer()) - emb_matrix_val, ids_val, outputs_val, fast_outputs_val = self.evaluate( - [emb_matrix, ids, outputs, fast_outputs]) - if scale_sqrt_depth: - emb_matrix_val *= params.embedding_dim**0.5 - - self.assertEqual(emb_matrix_val.shape, (8000, 128)) - self.assertEqual(ids_val.shape, (2, 1)) - - self.assertEqual(outputs_val.shape, (2, 1, 128)) - self.assertAllClose(emb_matrix_val[89, :], outputs_val[0, 0, :]) - self.assertAllClose(emb_matrix_val[100, :], outputs_val[1, 0, :]) - - self.assertEqual(fast_outputs_val.shape, (2, 1, 128)) - self.assertAllClose(emb_matrix_val[89, :], fast_outputs_val[0, 0, :]) - self.assertAllClose(emb_matrix_val[100, :], fast_outputs_val[1, 0, :]) - - def testSimpleEmbeddingLayerForLoop(self): - self._testSimpleEmbeddingLayer(False, True, None) - - def testSimpleEmbeddingLayerForLoop2D(self): - self._testSimpleEmbeddingLayer(False, False, None) - - def testSimpleEmbeddingLayerMatmul(self): - self._testSimpleEmbeddingLayer(True, False, None) - - def testSimpleEmbeddingLayerGather(self): - self._testSimpleEmbeddingLayer(False, False, 'gather') - - def testSimpleEmbeddingLayerScaling(self): - self._testSimpleEmbeddingLayer(True, False, None, True) - - def testSimpleEmbeddingLayerMasked(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(398847392) - params = layers.SimpleEmbeddingLayer.Params() - params.name = 'emd' - params.dtype = tf.float32 - params.vocab_size = 10 - params.embedding_dim = 5 - params.fprop_mode = 'gather' - params.use_3d_weight_tensor = False - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - params.apply_pruning = True - - emb_layer = layers.SimpleEmbeddingLayer(params) - emb_matrix = emb_layer.vars.wm - ids = tf.constant([[1], [2]]) - outputs = emb_layer.EmbLookupDefaultTheta(ids) - - self.assertIn('wm', emb_layer.vars.wm.name) - self.assertIn('mask', emb_layer.vars.mask.name) - self.assertIn('threshold', emb_layer.vars.threshold.name) - - self.assertEqual(emb_layer.theta.wm.get_shape(), tf.TensorShape([10, 5])) - self.assertEqual(emb_layer.theta.mask.get_shape(), tf.TensorShape([10, - 5])) - self.assertEqual(emb_layer.theta.threshold.get_shape(), - tf.TensorShape([])) - - embedding_var_count = 1 - wts = tf.get_collection('SimpleEmbeddingLayer_vars') - self.assertEqual(embedding_var_count, len(wts)) - - embedding_mask_count = 1 - masks = tf.get_collection('masks') - self.assertEqual(embedding_mask_count, len(masks)) - - emebdding_threshold_count = 1 - threshold = tf.get_collection('thresholds') - self.assertEqual(emebdding_threshold_count, len(threshold)) - - with self.session(use_gpu=False, graph=g): - self.evaluate(tf.global_variables_initializer()) - emb_matrix_val, _, outputs_val = self.evaluate([emb_matrix, ids, outputs]) - - self.assertAllClose(emb_matrix_val[1:3], outputs_val[:, 0, :]) - - def _testSimpleEmbeddingLayerGrad(self, - use_matmul, - use_3d_weight_tensor, - scale_sqrt_depth=False): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - params = layers.SimpleEmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 8000 - params.embedding_dim = 128 - params.use_matmul = use_matmul - params.use_3d_weight_tensor = use_3d_weight_tensor - params.scale_sqrt_depth = scale_sqrt_depth - params.params_init = py_utils.WeightInit.Gaussian(0.01) - params.vn.global_vn = False - params.vn.per_step_vn = False - emb_layer = layers.SimpleEmbeddingLayer(params) - ids = tf.constant([89, 100, 89, 89]) - embs = emb_layer.EmbLookupDefaultTheta(ids) * tf.constant([[0.1], [0.2], - [0.3], [0.4]]) - embs_sum = tf.reduce_sum(embs) - emb_weight = emb_layer.vars.wm - emb_grad, = tf.gradients(ys=[embs_sum], xs=[emb_weight]) - self.evaluate(tf.global_variables_initializer()) - emb_grad_val = self.evaluate(emb_grad) - - if not use_matmul: - # tf.embedding_lookup's gradient is a sparse representation. - # For testing, we convert it to a dense representation. - o_grad_matrix = np.zeros((8000, 128)) - for i in range(emb_grad_val.indices.shape[0]): - o_grad_matrix[emb_grad_val.indices[i], :] += emb_grad_val.values[i, :] - emb_grad_val = o_grad_matrix - - expected_emb_grad = np.zeros(shape=(8000, 128)) - expected_emb_grad[89, :] = 0.8 - expected_emb_grad[100, :] = 0.2 - if scale_sqrt_depth: - expected_emb_grad *= params.embedding_dim**0.5 - self.assertAllClose(expected_emb_grad, emb_grad_val) - - def testSimpleEmbeddingLayerGradForLoop(self): - self._testSimpleEmbeddingLayerGrad(False, True) - - def testSimpleEmbeddingLayerGradForLoop2D(self): - self._testSimpleEmbeddingLayerGrad(False, False) - - def testSimpleEmbeddingLayerGradMatmul(self): - self._testSimpleEmbeddingLayerGrad(True, False) - - def testSimpleEmbeddingLayerGradScaling(self): - self._testSimpleEmbeddingLayerGrad(True, False, True) - - def testCompareEmbeddingLayers(self): - classes = 8000 - dims = 128 - g = tf.Graph() - with g.as_default(): - ids = tf.placeholder(tf.int32) - - def CreateSimple(): - tf.random.set_seed(398847392) - p = layers.SimpleEmbeddingLayer.Params() - p.name = 'emb' - p.dtype = tf.float32 - p.vocab_size = classes - p.embedding_dim = dims - p.params_init = py_utils.WeightInit.Gaussian(0.01) - p.vn.global_vn = False - p.vn.per_step_vn = False - return layers.SimpleEmbeddingLayer(p) - - simple = CreateSimple() - simple_outs = simple.EmbLookupDefaultTheta(ids) - simple_grad = tf.gradients(simple_outs, simple.vars.wm)[0] - - def CreateOriginal(): - tf.random.set_seed(398847392) - p = layers.EmbeddingLayer.Params() - p.name = 'emb' - p.dtype = tf.float32 - p.vocab_size = classes - p.embedding_dim = dims - p.max_num_shards = 1 - p.params_init = py_utils.WeightInit.Gaussian(0.01) - p.vn.global_vn = False - p.vn.per_step_vn = False - return layers.EmbeddingLayer(p) - - original = CreateOriginal() - weight = tf.identity(simple.vars.wm) - theta = py_utils.NestedMap() - theta.wm = [weight] - original_outs = original.EmbLookup(theta, ids) - original_grad = tf.gradients(original_outs, weight)[0] - - ids_val = np.random.randint(0, high=classes, size=(4000,)) - with self.session(graph=g) as sess: - self.evaluate(tf.global_variables_initializer()) - s_outs, s_grad, o_outs, o_grad = sess.run( - [simple_outs, simple_grad, original_outs, original_grad], - feed_dict={ids: ids_val}) - self.assertAllClose(s_outs, o_outs) - self.assertAllClose(s_grad, o_grad) - - def testPositionalEmbeddingLayer(self): - with self.session(use_gpu=False): - p = layers.PositionalEmbeddingLayer.Params() - p.name = 'position_emb' - p.min_timescale = 1 - p.max_timescale = 7 - p.embedding_dim = 4 - seq_length = 11 - - pos_emb_layer = layers.PositionalEmbeddingLayer(p) - position_embs = pos_emb_layer.FPropDefaultTheta(seq_length) - actual_position_embs, = self.evaluate([position_embs]) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [ 0. , 0. , 1. , 1. ], - [ 0.84147096, 0.14237173, 0.54030228, 0.98981327], - [ 0.90929741, 0.28184283, -0.41614676, 0.95946062], - [ 0.14112 , 0.4155719 , -0.9899925 , 0.90956032], - [-0.7568025 , 0.54083425, -0.65364361, 0.84112918], - [-0.95892417, 0.65507787, 0.28366217, 0.75556135], - [-0.27941549, 0.75597537, 0.96017027, 0.65460002], - [ 0.65698659, 0.84147096, 0.7539022 , 0.54030228], - [ 0.98935831, 0.90982294, -0.14550003, 0.41499668], - [ 0.41211855, 0.9596386 , -0.91113025, 0.28123617], - [-0.54402113, 0.98990309, -0.83907151, 0.14174587]] - # pyformat: enable - # pylint: enable=bad-whitespace - print('expected_position_embs:', expected_output) - print('actual_position_embs:', actual_position_embs) - self.assertAllClose(actual_position_embs, expected_output) - - def testPositionalEmbeddingLayerWithPosition(self): - with self.session(use_gpu=False): - p = layers.PositionalEmbeddingLayer.Params() - p.name = 'position_emb' - p.min_timescale = 1 - p.max_timescale = 7 - p.embedding_dim = 4 - pos_tensor = tf.constant( - np.asarray([[0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3], - [0, 1, 2, 0, 1, 2, 3, 4, 0, 1, 0]]), - dtype=tf.int32) - - pos_emb_layer = layers.PositionalEmbeddingLayer(p) - position_embs = pos_emb_layer.FPropWithPosition(pos_emb_layer.theta, - pos_tensor) - actual_position_embs, = self.evaluate([position_embs]) - - # pylint: disable=bad-whitespace,bad-continuation - # pyformat: disable - expected_output = [ - [[ 0. , 0. , 1. , 1. ], - [ 0.84147096, 0.14237173, 0.54030228, 0.98981327], - [ 0.90929741, 0.28184283, -0.41614676, 0.95946062], - [ 0.14112 , 0.4155719 , -0.9899925 , 0.90956032], - [-0.7568025 , 0.54083425, -0.65364361, 0.84112918], - [-0.95892417, 0.65507787, 0.28366217, 0.75556135], - [-0.27941549, 0.75597537, 0.96017027, 0.65460002], - [ 0. , 0. , 1. , 1. ], - [ 0.84147096, 0.14237173, 0.54030228, 0.98981327], - [ 0.90929741, 0.28184283, -0.41614676, 0.95946062], - [ 0.14112 , 0.4155719 , -0.9899925 , 0.90956032]], - [[ 0. , 0. , 1. , 1. ], - [ 0.84147096, 0.14237173, 0.54030228, 0.98981327], - [ 0.90929741, 0.28184283, -0.41614676, 0.95946062], - [ 0. , 0. , 1. , 1. ], - [ 0.84147096, 0.14237173, 0.54030228, 0.98981327], - [ 0.90929741, 0.28184283, -0.41614676, 0.95946062], - [ 0.14112 , 0.4155719 , -0.9899925 , 0.90956032], - [-0.7568025 , 0.54083425, -0.65364361, 0.84112918], - [ 0. , 0. , 1. , 1. ], - [ 0.84147096, 0.14237173, 0.54030228, 0.98981327], - [ 0. , 0. , 1. , 1. ]] - ] - # pyformat: enable - # pylint: enable=bad-whitespace,bad-continuation - print('expected_position_embs:', expected_output) - print('actual_position_embs:', actual_position_embs) - self.assertAllClose(actual_position_embs, expected_output) - - def testPositionalEmbeddingLayerWithScaling(self): - with self.session(use_gpu=False): - p = layers.PositionalEmbeddingLayer.Params() - p.name = 'position_emb' - p.min_timescale = 1 - p.max_timescale = 7 - p.embedding_dim = 4 - p.trainable_scaling = True - p.trainable_scaling_init = 1.0 / np.sqrt(p.embedding_dim) - seq_length = 11 - - pos_emb_layer = layers.PositionalEmbeddingLayer(p) - position_embs = pos_emb_layer.FPropDefaultTheta(seq_length) - self.evaluate(tf.global_variables_initializer()) - actual_position_embs, = self.evaluate([position_embs]) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [ 0. , 0. , 1. , 1. ], - [ 0.84147096, 0.14237173, 0.54030228, 0.98981327], - [ 0.90929741, 0.28184283, -0.41614676, 0.95946062], - [ 0.14112 , 0.4155719 , -0.9899925 , 0.90956032], - [-0.7568025 , 0.54083425, -0.65364361, 0.84112918], - [-0.95892417, 0.65507787, 0.28366217, 0.75556135], - [-0.27941549, 0.75597537, 0.96017027, 0.65460002], - [ 0.65698659, 0.84147096, 0.7539022 , 0.54030228], - [ 0.98935831, 0.90982294, -0.14550003, 0.41499668], - [ 0.41211855, 0.9596386 , -0.91113025, 0.28123617], - [-0.54402113, 0.98990309, -0.83907151, 0.14174587]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_output / np.sqrt(p.embedding_dim), - actual_position_embs) - - def testRelativePositionalEmbeddingLayer(self): - with self.session(use_gpu=False): - radius = 3 - p = layers.RelativePositionalEmbeddingLayer.Params().Set( - name='rel_position_emb', radius=radius, dim=4) - layer = p.Instantiate() - indices = np.array([-5, -2, 0, 1, 4], dtype=np.int32) - pos_emb = layer.FPropDefaultTheta(tf.convert_to_tensor(indices)) - - self.evaluate(tf.global_variables_initializer()) - actual_pos_emb, full_emb = self.evaluate([pos_emb, layer.vars.w]) - - clipped_indices = np.vectorize(lambda x: max(-radius, min(radius, x)))( - indices) + radius - expected_output = np.take_along_axis(full_emb, - np.expand_dims(clipped_indices, -1), - 0) - print('expected_position_embs:', expected_output) - print('actual_position_embs:', actual_pos_emb) - self.assertAllClose(actual_pos_emb, expected_output) - - def testSinusoidalPositionalEmbeddingLayer(self): - with self.session(use_gpu=False): - p = layers.SinusoidalPositionalEmbeddingLayer.Params() - p.name = 'position_emb' - p.embedding_dim = 2 - seq_length = 4 - - pos_emb_layer = layers.SinusoidalPositionalEmbeddingLayer(p) - position_embs = pos_emb_layer.FPropDefaultTheta(seq_length) - actual_position_embs, = self.evaluate([position_embs]) - expected_output = [[math.sin(p / 2 * math.pi), - math.cos(p / 2 * math.pi)] for p in range(4)] - self.assertAllClose(actual_position_embs, expected_output) - - def testOneHotEmbeddingLayer(self): - with self.session(use_gpu=True): - params = layers.OneHotEmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 4 - params.embedding_dim = 4 - emb_layer = layers.OneHotEmbeddingLayer(params) - ids = tf.constant([[0], [2]]) - embs = emb_layer.EmbLookupDefaultTheta(ids) - self.evaluate(tf.global_variables_initializer()) - expected_output = [[[1., 0., 0., 0.]], [[0., 0., 1., 0.]]] - self.assertAllClose(expected_output, embs.eval()) - - def testOneHotEmbeddingLayerWithUncertainty(self): - with self.session(use_gpu=True): - params = layers.OneHotEmbeddingLayer.Params() - params.name = 'emb' - params.dtype = tf.float32 - params.vocab_size = 4 - params.embedding_dim = 4 - params.uncertainty = 0.3 - emb_layer = layers.OneHotEmbeddingLayer(params) - ids = tf.constant([[0], [2]]) - embs = emb_layer.EmbLookupDefaultTheta(ids) - self.evaluate(tf.global_variables_initializer()) - expected_output = [[[0.7, 0.1, 0.1, 0.1]], [[0.1, 0.1, 0.7, 0.1]]] - self.assertAllClose(expected_output, embs.eval()) - - -class SoftmaxLayerTest(test_utils.TestCase): - - def _RunSimpleFullSoftmax(self, - num_shards=1, - chunk_size=0, - inputs=None, - class_ids=None, - class_weights=None, - class_probabilities=None, - num_samples=0, - default_qdomain=None, - training_step=-1, - seed=None, - dtype=tf.float32, - fprop_dtype=None, - apply_pruning=False, - use_bias=True): - if fprop_dtype is None: - fprop_dtype = dtype - with self.session(use_gpu=True, graph=tf.Graph()): - if seed is not None: - tf.random.set_seed(seed) - if class_ids is None: - class_ids = tf.constant([[1], [5], [10]], dtype=tf.int32) - else: - class_ids = tf.constant(class_ids) - if class_weights is None: - class_weights = tf.constant([1.0, 0.4, 0.8], dtype=fprop_dtype) - else: - class_weights = tf.constant(class_weights) - np.random.seed(12345) - if inputs is None: - inputs = [tf.constant(np.random.rand(3, 10), dtype=fprop_dtype)] - else: - inputs = [tf.constant(inputs, dtype=fprop_dtype)] - - params = layers.SimpleFullSoftmax.Params() - params.dtype = dtype - params.fprop_dtype = fprop_dtype - params.name = 'softmax' - params.input_dim = 10 - params.num_classes = 32 - params.num_shards = num_shards - params.chunk_size = chunk_size - params.apply_pruning = apply_pruning - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - params.random_seed = 12345678 - params.use_bias = use_bias - - if default_qdomain is not None: - params.qdomain.default = default_qdomain - - if num_samples > 0: - # Turn on sampled soft-max; the asserts need to hold for it to be used. - params.num_sampled = num_samples - assert class_probabilities is None - assert chunk_size == 0 - - params.vn.global_vn = False - softmax = layers.SimpleFullSoftmax(params) - xent_loss = softmax.FProp( - softmax.theta, - inputs, - class_weights=class_weights, - class_ids=class_ids, - class_probabilities=class_probabilities) - - all_vars = tf.get_collection('SimpleFullSoftmax_vars') - expected_var_names = [] - for i in range(num_shards): - expected_var_names.append(u'softmax/weight_%d/var:0' % i) - if use_bias: - expected_var_names.append(u'softmax/bias_%d/var:0' % i) - - all_var_names = [v.name for v in all_vars] - self.assertCountEqual(expected_var_names, all_var_names) - - self.evaluate(tf.global_variables_initializer()) - if training_step >= 0: - self.evaluate( - tf.assign(py_utils.GetOrCreateGlobalStepVar(), training_step)) - return self.evaluate(xent_loss) - - def testSimpleFullSoftmaxMasked(self): - num_shards = 2 - apply_pruning = True - params = layers.SimpleFullSoftmax.Params() - params.name = 'softmax' - params.dtype = tf.float32 - params.input_dim = 10 - params.num_classes = 32 - params.fprop_dtype = tf.float32 - params.num_shards = num_shards - params.apply_pruning = apply_pruning - params.random_seed = 12345678 - softmax_layer = layers.SimpleFullSoftmax(params) - - self.assertIn('weight_0', softmax_layer.vars.weight_0.name) - self.assertIn('weight_1', softmax_layer.vars.weight_1.name) - self.assertIn('mask_0', softmax_layer.vars.mask_0.name) - self.assertIn('mask_1', softmax_layer.vars.mask_1.name) - self.assertIn('threshold_0', softmax_layer.vars.threshold_0.name) - self.assertIn('threshold_1', softmax_layer.vars.threshold_1.name) - - self.assertEqual(softmax_layer.theta.weight_0.get_shape(), - tf.TensorShape([10, 16])) - self.assertEqual(softmax_layer.theta.weight_1.get_shape(), - tf.TensorShape([10, 16])) - self.assertEqual(softmax_layer.theta.mask_0.get_shape(), - tf.TensorShape([10, 16])) - self.assertEqual(softmax_layer.theta.mask_1.get_shape(), - tf.TensorShape([10, 16])) - self.assertEqual(softmax_layer.theta.threshold_0.get_shape(), - tf.TensorShape([])) - self.assertEqual(softmax_layer.theta.threshold_0.get_shape(), - tf.TensorShape([])) - - softmax_var_count = 4 # 2 each for weights and biases (we have 2 shards) - wts = tf.get_collection('SimpleFullSoftmax_vars') - self.assertEqual(softmax_var_count, len(wts)) - - softmax_mask_count = 2 - masks = tf.get_collection('masks') - self.assertEqual(softmax_mask_count, len(masks)) - - softmax_threshold_count = 2 - threshold = tf.get_collection('thresholds') - self.assertEqual(softmax_threshold_count, len(threshold)) - - # Sampled and Masked - xent_loss = self._RunSimpleFullSoftmax( - num_samples=32, seed=12345, apply_pruning=True) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - self.assertNear(loss, 8.681571, 1e-5) - self.assertNear(log_perplexity, 3.946169, 1e-5) - - # Sharded and Masked - xent_loss = self._RunSimpleFullSoftmax(num_shards=2, apply_pruning=True) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - self.assertNear(loss, 6.14888, 1e-5) - self.assertNear(log_perplexity, 2.79495, 1e-5) - - # Non_2D and Masked - xent_loss = self._RunSimpleFullSoftmax( - inputs=np.random.rand(4, 3, 10), - class_weights=np.ones((4, 3)), - class_ids=np.random.randint(32, size=(4, 3)), - apply_pruning=True) - self.assertEqual(xent_loss.logits.shape, (4, 3, 32)) - self.assertEqual(xent_loss.per_example_xent.shape, (4, 3)) - self.assertEqual(xent_loss.per_example_weight.shape, (4, 3)) - - xent_loss = self._RunSimpleFullSoftmax( - inputs=np.random.rand(4, 3, 10), - class_weights=np.ones((4, 3)), - class_probabilities=np.random.uniform(size=(4, 3, 32)), - apply_pruning=True) - self.assertEqual(xent_loss.logits.shape, (4, 3, 32)) - self.assertEqual(xent_loss.per_example_xent.shape, (4, 3)) - self.assertEqual(xent_loss.per_example_weight.shape, (4, 3)) - - # Chunked and Masked - for chunk_size in (0, 1, 2, 3, 4, 5): - print('chunk_size = ', chunk_size) - xent_output = self._RunSimpleFullSoftmax( - chunk_size=chunk_size, apply_pruning=True) - loss = xent_output.total_xent - log_perplexity = xent_output.avg_xent - print('xent_output ', xent_output) - print('xent_output.per_example_argmax.dtype ', - xent_output.per_example_argmax.dtype) - self.assertAllClose(loss, 6.22425) - self.assertAllClose(log_perplexity, 2.82920) - self.assertAllEqual(xent_output.per_example_argmax, - np.argmax(xent_output.logits, axis=1)) - - def testSimpleFullSoftmax_Sampled(self): - xent_loss = self._RunSimpleFullSoftmax(num_samples=32, seed=12345) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - self.assertNear(loss, 8.681571, 1e-5) - self.assertNear(log_perplexity, 3.946169, 1e-5) - - def testSimpleFullSoftmax_NoBias(self): - xent_loss = self._RunSimpleFullSoftmax(seed=12345, use_bias=False) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - err = 1e-5 - self.assertNear(loss, 12.476410, err=err) - self.assertNear(log_perplexity, 5.671095, err=err) - - def testSimpleFullSoftmax_SampledAndSharded(self): - xent_loss = self._RunSimpleFullSoftmax( - num_shards=4, num_samples=32, seed=12345) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - self.assertNear(loss, 8.510439, 1e-5) - self.assertNear(log_perplexity, 3.868381, 1e-5) - - def testSimpleFullSoftmax_Non2D(self): - xent_loss = self._RunSimpleFullSoftmax( - inputs=np.random.rand(4, 3, 10), - class_weights=np.ones((4, 3)), - class_ids=np.random.randint(32, size=(4, 3))) - self.assertEqual(xent_loss.logits.shape, (4, 3, 32)) - self.assertEqual(xent_loss.per_example_xent.shape, (4, 3)) - self.assertEqual(xent_loss.per_example_weight.shape, (4, 3)) - - xent_loss = self._RunSimpleFullSoftmax( - inputs=np.random.rand(4, 3, 10), - class_weights=np.ones((4, 3)), - class_probabilities=np.random.uniform(size=(4, 3, 32))) - self.assertEqual(xent_loss.logits.shape, (4, 3, 32)) - self.assertEqual(xent_loss.per_example_xent.shape, (4, 3)) - self.assertEqual(xent_loss.per_example_weight.shape, (4, 3)) - - def _testSimpleFullSoftmax_Basic_Helper(self, dtype, fprop_dtype): - xent_loss = self._RunSimpleFullSoftmax(dtype=dtype, fprop_dtype=fprop_dtype) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - print(['loss', loss]) - print(['log_perplexity', log_perplexity]) - err = 1e-5 - if fprop_dtype == tf.float16 or fprop_dtype == tf.bfloat16: - err = 1e-2 - self.assertNear(loss, 6.22425, err=err) - self.assertNear(log_perplexity, 2.8292, err=err) - self.assertAllEqual(xent_loss.per_example_argmax, - np.argmax(xent_loss.logits, axis=1)) - - def testSimpleFullSoftmax_Basic_Float32(self): - self._testSimpleFullSoftmax_Basic_Helper( - dtype=tf.float32, fprop_dtype=tf.float32) - - def testSimpleFullSoftmax_Basic_Float32Float16(self): - self._testSimpleFullSoftmax_Basic_Helper( - dtype=tf.float32, fprop_dtype=tf.float16) - - def testSimpleFullSoftmax_Sharded(self): - xent_loss = self._RunSimpleFullSoftmax(2) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - print(['loss', loss]) - print(['log_perplexity', log_perplexity]) - self.assertNear(loss, 6.14888, 1e-5) - self.assertNear(log_perplexity, 2.79495, 1e-5) - - def testSimpleFullSoftmax_Chunked(self): - for chunk_size in (0, 1, 2, 3, 4, 5): - print('chunk_size = ', chunk_size) - xent_output = self._RunSimpleFullSoftmax(chunk_size=chunk_size) - loss = xent_output.total_xent - log_perplexity = xent_output.avg_xent - print('xent_output ', xent_output) - print('xent_output.per_example_argmax.dtype ', - xent_output.per_example_argmax.dtype) - self.assertAllClose(loss, 6.22425) - self.assertAllClose(log_perplexity, 2.82920) - self.assertAllEqual(xent_output.per_example_argmax, - np.argmax(xent_output.logits, axis=1)) - - def testSimpleFullSoftmax_Basic_Distributions(self): - with self.session(use_gpu=False): - class_ids = tf.constant([1, 5, 10], dtype=tf.int32) - class_weights = tf.constant([1.0, 0.4, 0.8], dtype=tf.float32) - np.random.seed(12345) - inputs = [tf.constant(np.random.rand(3, 10), dtype=tf.float32)] - - params = layers.SimpleFullSoftmax.Params() - params.name = 'softmax' - params.input_dim = 10 - params.num_classes = 32 - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - params.vn.global_vn = False - softmax = layers.SimpleFullSoftmax(params) - xent_loss = softmax.XentLoss( - inputs, - class_weights=class_weights, - class_probabilities=tf.one_hot(class_ids, params.num_classes)) - self.evaluate(tf.global_variables_initializer()) - loss = self.evaluate(xent_loss.total_xent) - log_perplexity = self.evaluate(xent_loss.avg_xent) - print(['loss', loss]) - print(['log_perplexity', log_perplexity]) - self.assertNear(loss, 6.22425, 1e-5) - self.assertNear(log_perplexity, 2.8292, 1e-5) - - def testSimpleFullSoftmax_GlobalVN(self): - with self.session(use_gpu=False): - class_ids = tf.constant([1, 5, 10], dtype=tf.int32) - class_weights = tf.constant([1.0, 0.4, 0.8], dtype=tf.float32) - np.random.seed(12345) - inputs = [tf.constant(np.random.rand(3, 10), dtype=tf.float32)] - - params = layers.SimpleFullSoftmax.Params() - params.name = 'softmax' - params.input_dim = 10 - params.num_classes = 32 - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - params.vn.global_vn = True - params.vn.seed = 23456 - params.vn.scale = 1.0 - softmax = layers.SimpleFullSoftmax(params) - xent_loss = softmax.XentLoss( - inputs, class_weights=class_weights, class_ids=class_ids) - self.evaluate(tf.global_variables_initializer()) - loss = self.evaluate(xent_loss.total_xent) - log_perplexity = self.evaluate(xent_loss.avg_xent) - print(['testSimpleFullSoftmax_GlobalVN loss', loss]) - print(['testSimpleFullSoftmax_GlobalVN log_perplexity', log_perplexity]) - self.assertNear(loss, 19.9612, 1e-4) - self.assertNear(log_perplexity, 3.46426, 1e-4) - - def testSimpleFullSoftmax_PerStepVN(self): - with self.session(use_gpu=False): - class_ids = tf.constant([1, 5, 10], dtype=tf.int32) - class_weights = tf.constant([1.0, 0.4, 0.8], dtype=tf.float32) - np.random.seed(12345) - inputs = [tf.constant(np.random.rand(3, 10), dtype=tf.float32)] - - params = layers.SimpleFullSoftmax.Params() - params.name = 'softmax' - params.input_dim = 10 - params.num_classes = 32 - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - params.vn.global_vn = False - params.vn.per_step_vn = True - params.vn.seed = 23456 - params.vn.scale = 1.0 - softmax = layers.SimpleFullSoftmax(params) - xent_loss = softmax.XentLoss( - inputs, class_weights=class_weights, class_ids=class_ids) - self.evaluate(tf.global_variables_initializer()) - loss = self.evaluate(xent_loss.total_xent) - log_perplexity = self.evaluate(xent_loss.avg_xent) - print(['testShardedFullSoftmax_PerStepVN loss', loss]) - print(['testShardedFullSoftmax_PerStepVN log_perplexity', log_perplexity]) - self.assertNear(loss, 19.9612, 1e-4) - self.assertNear(log_perplexity, 3.46426, 1e-4) - - def testSimpleFullSoftmax_FakeQuantized(self): - default_qdomain = quant_utils.SymmetricScheduledClipQDomain.Params() - default_qdomain.cc_schedule = quant_utils.FakeQuantizationSchedule.Params( - ).Set( - clip_start_step=0, clip_end_step=2, quant_start_step=2) - xent_loss = self._RunSimpleFullSoftmax( - default_qdomain=default_qdomain, training_step=5) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - print(['loss', loss]) - print(['log_perplexity', log_perplexity]) - self.assertNear(loss, 6.285590, 1e-5) - self.assertNear(log_perplexity, 2.857086, 1e-5) - - def _RunSimpleFullSoftmaxGradientChecker(self, batch_size, num_classes, - chunk_size, num_shards): - for (dtype, use_gpu, tolerance) in [(tf.float32, True, 1e-2), - (tf.float64, False, 1e-6)]: - tf.logging.info('dtype %s tolerance %g', dtype, tolerance) - with self.session(use_gpu=use_gpu, graph=tf.Graph()) as sess: - input_dim = 10 - np.random.seed(12345) - class_ids = tf.constant( - np.random.randint(num_classes, size=(batch_size, 1)), - dtype=tf.int32) - class_weights = tf.constant(np.random.rand(batch_size), dtype=dtype) - inputs = [ - tf.constant(np.random.rand(batch_size, input_dim), dtype=dtype) - ] - - params = layers.SimpleFullSoftmax.Params() - params.name = 'softmax' - params.dtype = dtype - params.input_dim = input_dim - params.num_classes = num_classes - params.num_shards = num_shards - params.chunk_size = chunk_size - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - params.vn.global_vn = False - softmax = layers.SimpleFullSoftmax(params) - xent_loss = softmax.XentLoss( - inputs, class_weights=class_weights, class_ids=class_ids) - softmax_vars = softmax.vars.Flatten() - # Now add the backward graph. - grads = tf.gradients(xent_loss.total_xent, softmax_vars) - - self.evaluate(tf.global_variables_initializer()) - assert len(softmax_vars) == len(grads) - for x, grad_x in zip(softmax_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_loss.total_xent, x) - self.assertAllClose( - grad_symbolic, grad_numeric, atol=tolerance, rtol=tolerance) - - def testSimpleFullSoftmaxGradientChecker(self): - self._RunSimpleFullSoftmaxGradientChecker(3, 4, 0, 1) - self._RunSimpleFullSoftmaxGradientChecker(3, 4, 0, 2) - self._RunSimpleFullSoftmaxGradientChecker(3, 4, 2, 2) - self._RunSimpleFullSoftmaxGradientChecker(3, 4, 5, 2) - - def testSimpleFullSoftmax_SymbolicShape(self): - with self.session(use_gpu=False): - class_ids = tf.constant([1, 5, 10], dtype=tf.int32) - class_weights = tf.constant([1.0, 0.4, 0.8], dtype=tf.float32) - np.random.seed(12345) - inputs = [tf.constant(np.random.rand(3, 10), dtype=tf.float32)] - - # Use a symbol to represent the input dim. - input_dim = symbolic.Symbol('input_dim') - params = layers.SimpleFullSoftmax.Params() - params.name = 'softmax' - params.input_dim = input_dim - params.num_classes = 32 - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, {input_dim: 10}): - softmax = layers.SimpleFullSoftmax(params) - xent_loss = softmax.XentLoss( - inputs, class_weights=class_weights, class_ids=class_ids) - self.evaluate(tf.global_variables_initializer()) - self.evaluate(xent_loss.total_xent) - - -class SingleShardSoftmaxLayerTest(test_utils.TestCase): - - def _RunSimpleFullSoftmax(self, - inputs=None, - class_ids=None, - class_weights=None, - class_probabilities=None, - chunk_size=0, - dtype=tf.float32, - fprop_dtype=None): - if fprop_dtype is None: - fprop_dtype = dtype - with self.session(use_gpu=True, graph=tf.Graph()): - inputs = tf.constant(inputs, dtype=fprop_dtype) - if class_ids is not None: - class_ids = tf.constant(class_ids, dtype=tf.int32) - if class_weights is not None: - class_weights = tf.constant(class_weights, dtype=dtype) - if class_probabilities is not None: - class_probabilities = tf.constant(class_probabilities, dtype=dtype) - - params = layers.SingleShardFullSoftmax.Params() - params.dtype = dtype - params.fprop_dtype = fprop_dtype - params.name = 'softmax' - params.input_dim = 10 - params.num_classes = 32 - params.chunk_size = chunk_size - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - params.random_seed = 12345678 - - params.vn.global_vn = False - softmax = params.Instantiate() - xent_loss = softmax.FProp( - softmax.theta, - inputs, - class_weights=class_weights, - class_ids=class_ids, - class_probabilities=class_probabilities) - - self.evaluate(tf.global_variables_initializer()) - return self.evaluate(xent_loss) - - def testSimpleFullSoftmax_Non2D_ClassId(self): - np.random.seed(1234578) - xent_loss = self._RunSimpleFullSoftmax( - inputs=np.random.rand(4, 3, 10), - class_weights=np.ones((4, 3, 1)), - class_ids=np.random.randint(32, size=(4, 3, 1)), - chunk_size=2) - self.assertEqual(xent_loss.per_example_xent.shape, (4, 3)) - self.assertEqual(xent_loss.per_example_weight.shape, (4, 3)) - - def testSimpleFullSoftmax_Non2D_ClassProb(self): - np.random.seed(12345) - xent_loss = self._RunSimpleFullSoftmax( - inputs=np.random.rand(4, 3, 10), - class_weights=np.ones((4, 3, 1)), - class_probabilities=np.random.randint(32, size=(4, 3, 32)), - chunk_size=1) - self.assertEqual(xent_loss.per_example_xent.shape, (4, 3)) - self.assertEqual(xent_loss.per_example_weight.shape, (4, 3)) - - def _testSimpleFullSoftmax_Basic_Helper(self, dtype, fprop_dtype): - np.random.seed(12345) - class_ids = [[1], [5], [10]] - class_weights = [[1.0], [0.4], [0.8]] - inputs = np.random.rand(3, 10) - xent_loss = self._RunSimpleFullSoftmax( - inputs=inputs, - class_weights=class_weights, - class_ids=class_ids, - dtype=dtype, - fprop_dtype=fprop_dtype) - loss = xent_loss.total_xent - log_perplexity = xent_loss.avg_xent - print(['loss', loss]) - print(['log_perplexity', log_perplexity]) - err = 1e-5 - if fprop_dtype == tf.float16 or fprop_dtype == tf.bfloat16: - err = 1e-2 - self.assertNear(loss, 6.22425, err=err) - self.assertNear(log_perplexity, 2.8292, err=err) - self.assertAllEqual(xent_loss.per_example_argmax, - np.argmax(xent_loss.logits, axis=1)) - - def testSimpleFullSoftmax_Basic_Float32(self): - self._testSimpleFullSoftmax_Basic_Helper( - dtype=tf.float32, fprop_dtype=tf.float32) - - def testSimpleFullSoftmax_Basic_Float32Float16(self): - self._testSimpleFullSoftmax_Basic_Helper( - dtype=tf.float32, fprop_dtype=tf.float16) - - def testSimpleFullSoftmax_Chunked(self): - np.random.seed(12345) - class_ids = [[1], [5], [10]] - class_weights = [[1.0], [0.4], [0.8]] - inputs = np.random.rand(3, 10) - per_example_xent = None - per_example_argmax = None - for chunk_size in (0, 1, 3): - xent_output = self._RunSimpleFullSoftmax( - inputs=inputs, - class_weights=class_weights, - class_ids=class_ids, - chunk_size=chunk_size) - loss = xent_output.total_xent - log_perplexity = xent_output.avg_xent - print('xent_output ', xent_output) - print('xent_output.per_example_argmax.dtype ', - xent_output.per_example_argmax.dtype) - self.assertAllClose(loss, 6.22425) - self.assertAllClose(log_perplexity, 2.82920) - if per_example_xent is None: - per_example_xent = xent_output.per_example_xent - per_example_argmax = xent_output.per_example_argmax - else: - self.assertAllClose(per_example_xent, xent_output.per_example_xent) - self.assertAllClose(per_example_argmax, xent_output.per_example_argmax) - - def _RunSimpleFullSoftmaxGradientChecker(self, batch_size, num_classes, - chunk_size): - for (dtype, use_gpu, tolerance) in [(tf.float32, True, 1e-2), - (tf.float64, False, 1e-6)]: - tf.logging.info('dtype %s tolerance %g', dtype, tolerance) - with self.session(use_gpu=use_gpu, graph=tf.Graph()) as sess: - input_dim = 10 - np.random.seed(12345) - class_ids = tf.constant( - np.random.randint(num_classes, size=(batch_size, 1)), - dtype=tf.int32) - class_weights = tf.constant(np.random.rand(batch_size, 1), dtype=dtype) - inputs = tf.constant(np.random.rand(batch_size, input_dim), dtype=dtype) - - params = layers.SingleShardFullSoftmax.Params() - params.name = 'softmax' - params.dtype = dtype - params.input_dim = input_dim - params.num_classes = num_classes - params.chunk_size = chunk_size - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - params.vn.global_vn = False - softmax = params.Instantiate() - xent_loss = softmax.FProp( - softmax.theta, - inputs, - class_weights=class_weights, - class_ids=class_ids) - softmax_vars = softmax.vars.Flatten() - # Now add the backward graph. - grads = tf.gradients(xent_loss.total_xent, softmax_vars) - - self.evaluate(tf.global_variables_initializer()) - assert len(softmax_vars) == len(grads) - for x, grad_x in zip(softmax_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_loss.total_xent, x) - self.assertAllClose( - grad_symbolic, grad_numeric, atol=tolerance, rtol=tolerance) - - def testSimpleFullSoftmaxGradientChecker(self): - self._RunSimpleFullSoftmaxGradientChecker(3, 4, 0) - self._RunSimpleFullSoftmaxGradientChecker(3, 4, 1) - self._RunSimpleFullSoftmaxGradientChecker(3, 4, 3) - - -class SoftmaxLayerLogitsTest(test_utils.TestCase): - """Testing SoftmaxLayer.Logits().""" - - def _Logits(self, params, batch_size=2, seq_length=None): - with self.session(use_gpu=True, graph=tf.Graph()): - np.random.seed(12345) - tf.random.set_seed(1234) - - params.name = 'softmax' - if not params.input_dim: - params.input_dim = 3 - if not params.num_classes: - params.num_classes = 4 - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - softmax = params.Instantiate() - - input_dim = params.input_dim - if seq_length: - inputs = np.random.rand(batch_size, seq_length, input_dim) - else: - inputs = np.random.rand(batch_size, input_dim) - inputs = tf.constant(inputs, dtype=py_utils.FPropDtype(params)) - logits = softmax.Logits(softmax.theta, inputs) - - if seq_length: - logits = py_utils.HasShape(logits, - [batch_size, seq_length, params.num_classes]) - else: - logits = py_utils.HasShape(logits, [batch_size, params.num_classes]) - self.evaluate(tf.global_variables_initializer()) - return logits.eval() - - def testConvSoftmaxLogits(self): - params = layers.ConvSoftmax.Params() - self.assertAllClose([[0.52536774, -0.17598523, 0.38314393, -0.36068222], - [0.75792629, -0.18001975, 0.42298675, -0.35423514]], - self._Logits(params)) - - def testSimpleFullSoftmax(self): - params = layers.SimpleFullSoftmax.Params() - self.assertAllClose([[0.52536774, -0.17598523, 0.38314393, -0.36068222], - [0.75792629, -0.18001975, 0.42298675, -0.35423514]], - self._Logits(params)) - - def testConvSoftmaxLogitsWith3DInputs(self): - params = layers.ConvSoftmax.Params() - logits = self._Logits(params, seq_length=5) - self.assertAllClose(6.9934864, np.sum(logits)) - - -class SharedSoftmaxLayerTest(SoftmaxLayerTest): - - def _testSharedSoftmaxLayerEmbLookup(self, scale_sqrt_depth=False): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(398847392) - params = layers.SharedSoftmaxLayer.Params() - params.dtype = tf.float32 - params.fprop_dtype = None - params.name = 'shared_layer' - params.input_dim = 128 - params.num_classes = 8000 - params.num_shards = 1 - params.chunk_size = 0 - params.apply_pruning = False - params.params_init = py_utils.WeightInit.Gaussian(0.5, 123456) - params.scale_sqrt_depth = scale_sqrt_depth - params.random_seed = 12345678 - - emb_layer = layers.SharedSoftmaxLayer(params) - - emb_matrix = tf.einsum('ji', emb_layer._ConcatWeights(emb_layer.theta).wm) - ids = tf.constant([[89], [100]]) - outputs = emb_layer.EmbLookup(emb_layer.theta, ids) - - with self.session(use_gpu=True, graph=g): - self.evaluate(tf.global_variables_initializer()) - emb_matrix_val, ids_val, outputs_val = self.evaluate( - [emb_matrix, ids, outputs]) - self.assertEqual(emb_matrix_val.shape, (8000, 128)) - self.assertEqual(ids_val.shape, (2, 1)) - self.assertEqual(outputs_val.shape, (2, 1, 128)) - if scale_sqrt_depth: - emb_matrix_val *= params.input_dim**0.5 - self.assertAllClose(emb_matrix_val[89, :], outputs_val[0, 0, :]) - self.assertAllClose(emb_matrix_val[100, :], outputs_val[1, 0, :]) - - def testSharedSoftmaxLayerEmbLookup(self): - self._testSharedSoftmaxLayerEmbLookup() - - def testSharedSoftmaxLayerEmbLookupScaling(self): - self._testSharedSoftmaxLayerEmbLookup(True) - - -class FeedForwardNetTest(test_utils.TestCase): - - def testFeedForwardNetConstruction(self): - with self.session(use_gpu=False): - p = layers.FeedForwardNet.Params().Set( - name='ffn', - input_dim=10, - hidden_layer_dims=[20, 30], - batch_norm=True, - activation='TANH', - params_init=py_utils.WeightInit.Uniform(1.0)) - p.dropout.keep_prob = 0.5 - proj_l = p.Instantiate() - a = tf.constant(1.0, shape=[20, 10]) - proj_l.FPropDefaultTheta(a) - # check output_dim equals last hidden layer dim. - self.assertEqual(p.hidden_layer_dims[-1], proj_l.output_dim) - - p = layers.FeedForwardNet.Params().Set( - name='ffn2', - input_dim=10, - hidden_layer_dims=[20, 30], - batch_norm=True, - activation='TANH', - params_init=py_utils.WeightInit.Uniform(1.0)) - p.dropout = [ - layers.DropoutLayer.Params().Set(keep_prob=0.5), - layers.DropoutLayer.Params().Set(keep_prob=0.9) - ] - proj_l = p.Instantiate() - a = tf.constant(1.0, shape=[20, 10]) - proj_l.FPropDefaultTheta(a) - - p = layers.FeedForwardNet.Params().Set( - name='ffn3', - input_dim=10, - hidden_layer_dims=[20, 30], - batch_norm=[True, False], - activation=['TANH', 'RELU'], - params_init=py_utils.WeightInit.Uniform(1.0)) - p.dropout = [ - layers.DropoutLayer.Params().Set(keep_prob=0.5), - layers.DropoutLayer.Params().Set(keep_prob=0.9) - ] - proj_l = p.Instantiate() - a = tf.constant(1.0, shape=[20, 10]) - proj_l.FPropDefaultTheta(a) - - def testFeedForwardNet(self): - with self.session(use_gpu=False): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.FeedForwardNet.Params().Set( - name='ffn', - input_dim=10, - hidden_layer_dims=[20, 30], - batch_norm=False, - activation=['RELU', 'NONE']) - params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=837465638) - p.params_init = params_init - feedforward_net = p.Instantiate() - - p1 = layers.ProjectionLayer.Params().Set( - name='p1', - input_dim=10, - output_dim=20, - activation='RELU', - batch_norm=False) - p1.params_init = params_init - p1_l = p1.Instantiate() - - p2 = layers.ProjectionLayer.Params().Set( - name='p2', - input_dim=20, - output_dim=30, - activation='NONE', - batch_norm=False) - p2.params_init = params_init - p2_l = p2.Instantiate() - - a = tf.constant(np.random.rand(5, 10), dtype=tf.float32) - out1 = feedforward_net.FPropDefaultTheta(a) - - out2 = p2_l.FPropDefaultTheta(p1_l.FPropDefaultTheta(a)) - - self.evaluate(tf.global_variables_initializer()) - out1_v, out2_v = self.evaluate([out1, out2]) - self.assertAllClose(out1_v, out2_v) - - def testFeedForwardNetQuantized(self): - with self.session(use_gpu=False): - tf.random.set_seed(398847392) - np.random.seed(12345) - - cc_schedule = quant_utils.FakeQuantizationSchedule.Params().Set( - clip_start_step=1, - clip_end_step=2, - quant_start_step=2, - start_cap=8.0, - end_cap=2.0) - proj_qdomain = quant_utils.SymmetricScheduledClipQDomain.Params().Set( - cc_schedule=cc_schedule) - - p = layers.FeedForwardNet.Params().Set( - name='ffn', - input_dim=10, - hidden_layer_dims=[20, 30], - batch_norm=False, - activation=['RELU', 'NONE']) - p.qdomain.default = proj_qdomain.Copy() - params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=837465638) - p.params_init = params_init - feedforward_net = p.Instantiate() - - p1 = layers.ProjectionLayer.Params().Set( - name='p1', - input_dim=10, - output_dim=20, - activation='RELU', - batch_norm=False) - p1.qdomain.default = proj_qdomain.Copy() - p1.params_init = params_init - p1_l = p1.Instantiate() - - p2 = layers.ProjectionLayer.Params().Set( - name='p2', - input_dim=20, - output_dim=30, - activation='NONE', - batch_norm=False) - p2.params_init = params_init - p2.qdomain.default = proj_qdomain.Copy() - p2_l = p2.Instantiate() - - a = tf.constant(np.random.rand(5, 10), dtype=tf.float32) - out1 = feedforward_net.FPropDefaultTheta(a) - out2 = p2_l.FPropDefaultTheta(p1_l.FPropDefaultTheta(a)) - - self.evaluate(tf.global_variables_initializer()) - - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 5)) - out1_v, out2_v = self.evaluate([out1, out2]) - self.assertAllClose(out1_v, out2_v) - - def testFeedForwardNetBnFolded(self): - with self.session(use_gpu=False): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.FeedForwardNet.Params().Set( - name='ffn', - input_dim=10, - hidden_layer_dims=[20, 30], - batch_norm=True, - bn_fold_weights=True, - activation=['RELU', 'NONE']) - params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=837465638) - p.params_init = params_init - feedforward_net = p.Instantiate() - - p1 = layers.ProjectionLayer.Params().Set( - name='p1', - input_dim=10, - output_dim=20, - activation='RELU', - batch_norm=True, - bn_fold_weights=True) - p1.params_init = params_init - p1_l = p1.Instantiate() - - p2 = layers.ProjectionLayer.Params().Set( - name='p2', - input_dim=20, - output_dim=30, - activation='NONE', - batch_norm=True, - bn_fold_weights=True) - p2.params_init = params_init - p2_l = p2.Instantiate() - - a = tf.constant(np.random.rand(5, 10), dtype=tf.float32) - out1 = feedforward_net.FPropDefaultTheta(a) - - out2 = p2_l.FPropDefaultTheta(p1_l.FPropDefaultTheta(a)) - - self.evaluate(tf.global_variables_initializer()) - out1_v, out2_v = self.evaluate([out1, out2]) - self.assertAllClose(out1_v, out2_v) - - def testFeedForwardNetSmokeTest(self): - with self.session(use_gpu=False): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.FeedForwardNet.Params().Set( - name='ffn', - input_dim=10, - hidden_layer_dims=[20, 30], - activation=['RELU', 'NONE']) - params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=837465638) - p.params_init = params_init - feedforward_net = p.Instantiate() - a = tf.constant(np.random.rand(5, 10), dtype=tf.float32) - out = tf.reduce_sum(feedforward_net.FPropDefaultTheta(a)) - out_abs = tf.reduce_sum(tf.abs(feedforward_net.FPropDefaultTheta(a))) - - self.evaluate(tf.global_variables_initializer()) - # pyformat: disable - test_utils.CompareToGoldenSingleFloat(self, 8.190775, out.eval(), atol=1e-5) # pylint: disable=line-too-long - # pyformat: enable - test_utils.CompareToGoldenSingleFloat(self, 36.773586, out_abs.eval()) - - def testDropoutLayerTrain(self): - with self.session(use_gpu=True): - tf.random.set_seed(3980847392) - p = layers.DropoutLayer.Params() - p.keep_prob = 0.5 - p.random_seed = 1234 - p.name = 'dropout' - - dl = p.Instantiate() - - x = tf.random.normal([10, 10, 10, 3]) - xd = dl.FPropDefaultTheta(x) - x, xd = self.evaluate([x, xd]) - self.assertGreater((xd == 0).mean(), 0.3) - self.assertLess((xd == 0).mean(), 0.7) - self.assertAllClose(xd[xd != 0], x[xd != 0] / p.keep_prob) - - def testDropoutLayerEval(self): - with self.session(use_gpu=True), self.SetEval(True): - tf.random.set_seed(3980847392) - p = layers.DropoutLayer.Params() - p.keep_prob = 0.5 - p.random_seed = 1234 - p.name = 'dropout' - dl = p.Instantiate() - - x = tf.random.normal([10, 10, 10, 3]) - xd = dl.FPropDefaultTheta(x) - - x, xd = self.evaluate([x, xd]) - - self.assertAllEqual(xd, x) - - def testDeterministicSerialize(self): - p = layers.FeedForwardNet.Params().Set( - input_dim=4, - projection=layers.ProjectionLayer.Params().Set( - has_bias=True, - params_init=py_utils.WeightInit.KaimingUniformFanInRelu()), - activation='TANH', - hidden_layer_dims=[5, 5, 1], - batch_norm=True, - weight_norm=False) - base_serialized = p.ToTextWithTypes() - for _ in range(10): - serialized = p.ToTextWithTypes() - serialized_copy = p.Copy().ToTextWithTypes() - self.assertEqual(serialized, base_serialized) - self.assertEqual(serialized_copy, base_serialized) - for x in [serialized, serialized_copy]: - deserialized = layers.FeedForwardNet.Params() - deserialized.FromTextWithTypes(x) - self.assertEqual(p, deserialized) - - def testFeedForwardNetMeta(self): - p = layers.FeedForwardNet.Params().Set( - name='ffn', - input_dim=10, - hidden_layer_dims=[20, 30], - activation=['RELU', 'NONE']) - meta = p.cls.FPropMeta(p, tshape.Shape([5, 10])) - self.assertEqual( - meta.flops, - # Last layer has no activation fns but need to add bias. - 5 * 2 * (10 * 20 + 20 * 30) + 5 * 20 + (20 + 30)) - self.assertEqual(meta.out_shapes[0].ToTensorShape().as_list(), - [5, p.hidden_layer_dims[-1]]) - - -class AddingAccumulatorTest(test_utils.TestCase): - """Test for AddingAccumulator.""" - - def testAddingAccumulator(self): - with self.session(): - layer_p = layers.IdentityLayer.Params() - layer_p.name = 'test' - layer = layer_p.Instantiate() - - layer.RegisterAccumulator('acc1', layers.AddingAccumulator([], - tf.float32)) - - # Initial value. - self.assertEqual(0.0, layer.accumulators.acc1.GetValue().eval()) - - # Update/merge. - layer.accumulators.acc1.Update(1.0) - layer.accumulators.acc1.Update(1.0) - self.assertEqual(2.0, layer.accumulators.acc1.GetValue().eval()) - - # Reset. - layer.accumulators.Transform(lambda acc: acc.Reset()) - self.assertEqual(0.0, layer.accumulators.acc1.GetValue().eval()) - - -class BatchNormLayerNoPaddingTest(test_utils.TestCase, parameterized.TestCase): - - def testBatchNormLayerNoPaddingConstruction(self): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayerNoPadding.Params() - params.name = 'bn' - params.dim = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - layers.BatchNormLayerNoPadding(params) - bn_vars = tf.get_collection('BatchNormLayerNoPadding_vars') - bn_var_names = [x.name for x in bn_vars] - expected_var_names = [ - 'bn/beta/var:0', 'bn/gamma/var:0', 'bn/moving_mean/var:0', - 'bn/moving_variance/var:0' - ] - self.assertEqual(expected_var_names, bn_var_names) - - @parameterized.named_parameters({ - 'testcase_name': '_eval', - 'is_eval': True, - }, { - 'testcase_name': '_train', - 'is_eval': False, - }) - def testBatchNormLayerNoPaddingFProp(self, is_eval): - with self.session(use_gpu=True), self.SetEval(is_eval): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayerNoPadding.Params() - params.name = 'bn' - params.dim = 3 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - bn_layer = layers.BatchNormLayerNoPadding(params) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 3]), dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - expected_sig1 = 2.6593573 if is_eval else 0 - expected_sig2 = 15.4642076 if is_eval else 47.850193 - - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(expected_sig1, sig1.eval(), atol=1e-5) - self.assertAllClose(expected_sig2, sig2.eval(), atol=1e-5) - - def testBatchNormLayerNoPaddingFPropUseGlobalStatsForTraining(self): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayerNoPadding.Params() - params.name = 'bn' - params.dim = 3 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - bn_layer = layers.BatchNormLayerNoPadding(params) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 3]), dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - with self.session(use_gpu=True): - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(1.19209289551e-06, sig1.eval(), atol=1e-5) - self.assertAllClose(47.8501930237, sig2.eval(), atol=1e-5) - - def testBatchNormLayerNoPaddingPostTrainingStepUpdate(self): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayerNoPadding.Params() - params.name = 'bn' - params.dim = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - bn_layer = layers.BatchNormLayerNoPadding(params) - bn_layer.accumulators.counts.Update(0.0) - bn_layer.accumulators.mean_ss.Update([1.0, 1.0]) - bn_layer.accumulators.variance_ss.Update([5.0, 5.0]) - bn_updates = bn_layer.PostTrainingStepUpdate(tf.constant(100)) - - with self.session(use_gpu=True): - self.evaluate(tf.global_variables_initializer()) - self.evaluate(bn_updates) - moving_mean = self.evaluate(bn_layer.vars.moving_mean) - moving_std = self.evaluate(bn_layer.vars.moving_variance) - self.assertAllClose([0.0, 0.0], moving_mean) - self.assertAllClose([1.0, 1.0], moving_std) - - def testBatchNormLayerNoPaddingFPropForConv(self): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.BatchNormLayerNoPadding.Params() - params.name = 'bn_conv' - params.dim = 32 - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - bn_layer = layers.BatchNormLayerNoPadding(params) - bn_in1 = tf.constant( - np.random.normal(0.1, 0.5, [2, 8, 4, 32]), dtype=tf.float32) - - bn_out = bn_layer.FPropDefaultTheta(bn_in1) - sig1 = tf.reduce_sum(bn_out) - sig2 = tf.reduce_sum(bn_out * bn_out) - with self.session(use_gpu=True): - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose(0.0, sig1.eval(), atol=1e-4) - self.assertAllClose(2039.398681, sig2.eval()) - - def _BuildDummyStackedBNLayer(self, splits): - num_micro_batches = 8 - if splits == 0: - endpoint = layers.BatchNormLayerNoPadding.Params().Set( - decay=0.997, name='bn', dim=1) - else: - cell_tpl = [] - for split in range(splits): - nets_to_split = [ - layers.BatchNormLayerNoPadding.Params().Set( - decay=0.997, name='bn_{}'.format(split), dim=1), - ] - split_layer = gpipe.FeatureExtractionLayer.Params().Set( - name='split_{}'.format(split), sub=nets_to_split) - cell_tpl.append(split_layer) - endpoint = gpipe.PipeliningLayer.Params().Set( - name='pipeline', - num_micro_batches=num_micro_batches, - cell_tpl=cell_tpl, - before_tpl=[]) - layer = endpoint.Instantiate() - return layer - - @parameterized.named_parameters({ - 'testcase_name': '_baseline', - 'splits': 0, - }, { - 'testcase_name': '_two_splits', - 'splits': 2, - }, { - 'testcase_name': '_four_splits', - 'splits': 4, - }) - def testBatchNormLayerNoPaddingAccumulators(self, splits): - batch_size = 1024 - with self.session(graph=tf.Graph()): - # Construct a network where loss = w * x + b - inputs = tf.concat([ - tf.ones([batch_size // 2, 1, 1, 1]), - tf.zeros([batch_size // 2, 1, 1, 1]) - ], - axis=0) - net = self._BuildDummyStackedBNLayer(splits) - logits = net.FPropDefaultTheta(inputs) - loss = tf.reduce_mean(logits) - grads = tf.gradients(loss, tf.trainable_variables()) - # Check the accumulator values - counts = [] - means = [] - variances = [] - for i in range(splits): - l = net.children['split_{}'.format(i)].children['bn_{}'.format(i)] - counts.append(l.accumulators.counts.GetValue()) - means.append(l.accumulators.mean_ss.GetValue()) - variances.append(l.accumulators.variance_ss.GetValue()) - if splits == 0: - counts.append(net.accumulators.counts.GetValue()) - means.append(net.accumulators.mean_ss.GetValue()) - variances.append(net.accumulators.variance_ss.GetValue()) - post_training_step_updates = net.PostTrainingStepUpdate( - net.theta.global_step) - - self.evaluate(tf.global_variables_initializer()) - _, count_vals, mean_vals, var_vals = self.evaluate( - [grads, counts, means, variances]) - - self.assertSameElements(count_vals, {batch_size}) - - self.assertEqual(batch_size // 2, mean_vals[0]) - if len(mean_vals) > 1: - self.assertSameElements(mean_vals[1:], {0}) - - self.assertEqual(batch_size // 2, var_vals[0]) - if len(var_vals) > 1: - self.assertSameElements(var_vals[1:], {0}) - self.evaluate(post_training_step_updates) - moving_vars = self.evaluate(tf.get_collection('moving_vars')) - self.assertEqual(0.0015, moving_vars[0]) - self.assertNear(0.997750, moving_vars[1], err=1.0e-6) - - -class LayerNormTest(test_utils.TestCase): - - def testLayerNormFProp(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.LayerNorm.Params() - p.name = 'ln' - p.input_dim = 3 - layer_norm = layers.LayerNorm(p) - npy_input = np.random.normal(1.0, 0.5, - [2, 4, 4, p.input_dim]).astype('float32') - inputs = tf.constant(npy_input, dtype=tf.float32) - output = layer_norm.FPropDefaultTheta(inputs) - - self.evaluate(tf.global_variables_initializer()) - sym_output = self.evaluate(output) - - # Mean should be zero and variance should be close to one. - self.assertNear(0.0, sym_output.sum(), 1e-5) - self.assertNear(1.0, np.var(sym_output), 1e-4) - - # Compare with numpy. - mean = npy_input.mean(-1, keepdims=True) - variance = np.mean(np.square(npy_input - mean), -1, keepdims=True) - npy_output = (npy_input - mean) / np.sqrt(variance + p.epsilon) - self.assertAllClose(sym_output, npy_output) - - def testLayerNormFPropDirectScale(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.LayerNorm.Params() - p.name = 'ln' - p.input_dim = 3 - p.direct_scale = True - layer_norm = layers.LayerNorm(p) - npy_input = np.random.normal(1.0, 0.5, - [2, 4, 4, p.input_dim]).astype('float32') - inputs = tf.constant(npy_input, dtype=tf.float32) - output = layer_norm.FPropDefaultTheta(inputs) - - self.evaluate(tf.global_variables_initializer()) - sym_output = self.evaluate(output) - - # Mean should be zero and variance should be close to one. - self.assertNear(0.0, sym_output.sum(), 1e-5) - self.assertNear(1.0, np.var(sym_output), 1e-4) - - # Compare with numpy. - mean = npy_input.mean(-1, keepdims=True) - variance = np.mean(np.square(npy_input - mean), -1, keepdims=True) - npy_output = (npy_input - mean) / np.sqrt(variance + p.epsilon) - self.assertAllClose(sym_output, npy_output) - - def testLayerNormBProp(self): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.LayerNorm.Params() - p.name = 'ln' - p.input_dim = 3 - layer_norm = layers.LayerNorm(p) - - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, p.input_dim]), dtype=tf.float32) - output = layer_norm.FPropDefaultTheta(inputs) - loss = tf.reduce_sum(output) - - all_vars = tf.trainable_variables() - self.assertEqual(2, len(all_vars)) - - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - sym_grads = [sg.eval() for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v) for v in all_vars - ] - - for sg, ng in zip(sym_grads, num_grads): - self.assertAllClose(sg, ng, rtol=1e-02, atol=1e-02) - - -class CategoricalLayerNormTest(test_utils.TestCase): - - def testCategoricalLayerNormFProp(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.CategoricalLayerNorm.Params() - p.name = 'cat_ln' - p.input_dim = 3 - p.num_classes = 2 - layer_norm = layers.CategoricalLayerNorm(p) - npy_input = np.random.normal(1.0, 0.5, - [2, 4, 4, p.input_dim]).astype('float32') - - inputs = tf.constant(npy_input, dtype=tf.float32) - output = layer_norm.FPropDefaultTheta(inputs) - - self.evaluate(tf.global_variables_initializer()) - # Set different bias and scale for different copy of ln params - self.evaluate(tf.assign(layer_norm.vars.scale_0, [0.0] * 3)) - self.evaluate(tf.assign(layer_norm.vars.scale_1, [1.0] * 3)) - self.evaluate(tf.assign(layer_norm.vars.bias_0, [0.0] * 3)) - self.evaluate(tf.assign(layer_norm.vars.bias_1, [1.0] * 3)) - - output = layer_norm.FPropDefaultTheta(inputs) - sym_output_c1 = self.evaluate(output) - - # Redefine output to use the value of new theta - theta = layer_norm.theta - theta.class_index = tf.constant(1, dtype=tf.int32) - output = layer_norm.FProp(theta, inputs) - sym_output_c2 = self.evaluate(output) - - # Mean should be zero and variance should be close to one. - self.assertNotAllClose(sym_output_c1, sym_output_c2) - self.assertNear(0.0, sym_output_c1.mean(), 1e-5) - self.assertNear(1.0, np.var(sym_output_c1), 1e-4) - # Mean should be 1 and variance should be close to 2^2 - self.assertNear(1.0, sym_output_c2.mean(), 1e-5) - self.assertNear(4.0, np.var(sym_output_c2), 1e-4) - - # Compare with numpy. - mean = npy_input.mean(-1, keepdims=True) - variance = np.mean(np.square(npy_input - mean), -1, keepdims=True) - npy_output = (npy_input - mean) / np.sqrt(variance + p.epsilon) - self.assertAllClose(sym_output_c1, npy_output) - - variance = np.mean(np.square(npy_input - mean), -1, keepdims=True) - npy_output = 2 * (npy_input - mean) / np.sqrt(variance + p.epsilon) + 1 - self.assertAllClose(sym_output_c2, npy_output) - - def testCategoricalLayerNormBProp(self): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.CategoricalLayerNorm.Params() - p.name = 'cat_ln' - p.input_dim = 3 - p.num_classes = 2 - layer_norm = layers.CategoricalLayerNorm(p) - - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, p.input_dim]), dtype=tf.float32) - output = layer_norm.FPropDefaultTheta(inputs) - loss = tf.reduce_sum(output) - - all_vars = tf.trainable_variables() - self.assertEqual(4, len(all_vars)) - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - print('grads = {}'.format(grads)) - sym_grads = [self.evaluate(sg) for sg in grads] - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v) for v in all_vars - ] - - for sg, ng in zip(sym_grads, num_grads): - self.assertAllClose(sg, ng, rtol=1e-02, atol=1e-02) - - -class DeterministicDropoutTest(test_utils.TestCase, parameterized.TestCase): - - def testDeterministicDropoutLayer(self): - params = layers.DeterministicDropoutLayer.Params().Set(keep_prob=0.7) - params.name = 'drop' - dropout = layers.DeterministicDropoutLayer(params) - - x = tf.ones([4, 6], dtype=tf.float32) - x_expected = np.array([ - [1, 0, 0, 0, 1, 1], - [1, 1, 1, 1, 1, 1], - [1, 0, 0, 1, 1, 0], - [1, 0, 0, 1, 1, 1], - ]) / 0.7 - - with self.session(): - tf.assign(py_utils.GetOrCreateGlobalStepVar(), 1234).eval() - py_utils.ResetStepSeed(seed=5678) - x_val = dropout.FPropDefaultTheta(x).eval() - self.assertAllClose(x_expected, x_val) - self.assertEqual(5679, py_utils.GetStepSeed().eval()) - - # Different step seed gives different result. - x_val = dropout.FPropDefaultTheta(x).eval() - self.assertNotAllClose(x_expected, x_val) - - # Different global step gives different result - tf.assign(py_utils.GetOrCreateGlobalStepVar(), 1235).eval() - py_utils.ResetStepSeed(seed=5678) - x_val = dropout.FPropDefaultTheta(x).eval() - self.assertNotAllClose(x_expected, x_val) - - # The same seeds in the same session is consistent. - tf.assign(py_utils.GetOrCreateGlobalStepVar(), 1234).eval() - py_utils.ResetStepSeed(seed=5678) - x_val = dropout.FPropDefaultTheta(x).eval() - self.assertAllClose(x_expected, x_val) - - # The same seeds in a different session is consistent. - with self.session(): - tf.assign(py_utils.GetOrCreateGlobalStepVar(), 1234).eval() - py_utils.ResetStepSeed(seed=5678) - x_val = dropout.FPropDefaultTheta(x).eval() - self.assertAllClose(x_expected, x_val) - - def testNoiseShapeBroadcastDims(self): - params = layers.DeterministicDropoutLayer.Params().Set( - keep_prob=0.7, noise_shape_broadcast_dims=[-1]) - params.name = 'drop' - dropout = layers.DeterministicDropoutLayer(params) - - x = tf.ones([4, 6]) - x_expected = np.array([ - [1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - ]) / 0.7 - - with self.session(): - tf.assign(py_utils.GetOrCreateGlobalStepVar(), 1234).eval() - self.assertEqual(1234, dropout.theta.global_step.eval()) - py_utils.ResetStepSeed(seed=5678) - x_val = dropout.FPropDefaultTheta(x).eval() - self.assertEqual(5679, py_utils.GetStepSeed().eval()) - self.assertAllClose(x_expected, x_val) - - @parameterized.named_parameters( - { - 'testcase_name': 'baseline', - 'splits': 1, - 'num_micro_batches': 1 - }, - { - 'testcase_name': 'OneSplitTwoMicroBatches', - 'splits': 1, - 'num_micro_batches': 2 - }, - { - 'testcase_name': 'TwoSplitsOneMicroBatch', - 'splits': 2, - 'num_micro_batches': 1 - }, - { - 'testcase_name': 'TwoSplitsTwoMicroBatches', - 'splits': 2, - 'num_micro_batches': 2 - }, - ) - def testDropoutInRecurrent(self, splits=1, num_micro_batches=1): - """Test to verify the drop mask used in fprop and bprop is identical.""" - assert splits in [1, 2, 4] - with self.session(): - tf.random.set_seed(12345) - num_layers = 4 - # Build a model with 4 dropout layers. - blocks = [] - for l in range(num_layers): - blocks.append(layers.DeterministicDropoutLayer.Params().Set( - name='dropout_{}'.format(l), keep_prob=0.7)) - # Divide the model into splits partitions. - cell_tpl = [] - blocks_per_split = num_layers // splits - for i in range(splits): - sub = blocks[i * blocks_per_split:(i + 1) * blocks_per_split] - cell_tpl.append(gpipe.FeatureExtractionLayer.Params().Set( - name='cell_{}'.format(i), sub=sub)) - # Parallelize partitions using pipeline. - p = gpipe.PipeliningLayer.Params().Set( - name='pipeline', - num_micro_batches=num_micro_batches, - cell_tpl=cell_tpl) - # Fake input - x = tf.ones([2, 3]) - # Construct weights. - w = tf.get_variable( - 'w', shape=[2, 3], initializer=tf.constant_initializer([[1] * 3] * 2)) - mdl = p.Instantiate() - y = mdl.FPropDefaultTheta(x * w) - # Construct loss function such that gradients = final activation. - loss = tf.reduce_sum(y) - grads = py_utils.ComputeGradients(loss, py_utils.NestedMap(w=w)) - self.evaluate(tf.global_variables_initializer()) - y_val = self.evaluate(y) - grads_val = self.evaluate(grads.w.grad) - self.assertAllClose(y_val, grads_val) - - -class GradNormTrackerTest(test_utils.TestCase): - - def testGradNormTracker(self): - with self.session(use_gpu=False) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.GradNormTracker.Params().Set( - name='grad_norm_tracker', clip_threshold=3.0) - grad_norm_tracker = p.Instantiate() - grad_norm = tf.placeholder(tf.float32) - grad_norm_clip = grad_norm_tracker.FPropDefaultTheta(grad_norm) - - self.evaluate(tf.global_variables_initializer()) - - random_normal = np.exp(np.random.normal(5.0, 1.0, size=10000)) - # We are expected to reject 16% of the outliers. - outliers = np.exp(np.random.normal(7.0, 1.0, size=100)) - total_rejections = 0 - for i in range(100): - for j in range(100): - sess.run([grad_norm_clip], {grad_norm: random_normal[i * 100 + j]}) - clip = sess.run([grad_norm_clip], {grad_norm: outliers[i]})[0] - if clip == 0.0: - total_rejections += 1 - # Q(yonghui): Why is total_rejections not deterministic? - print('total_rejections', total_rejections) - self.assertGreater(total_rejections, 5) - - def testGradNormTrackerClipCapMin(self): - with self.session(use_gpu=False) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.GradNormTracker.Params().Set( - name='grad_norm_tracker', - clip_threshold=3.0, - grad_norm_clip_cap_min=math.exp(10.0)) - grad_norm_tracker = p.Instantiate() - grad_norm = tf.placeholder(tf.float32) - grad_norm_clip = grad_norm_tracker.FPropDefaultTheta(grad_norm) - - self.evaluate(tf.global_variables_initializer()) - - random_normal = np.exp(np.random.normal(5.0, 1.0, size=10000)) - # We expect no outliers being rejected due to the grad_norm_clip_cap_min. - outliers = np.exp(np.random.normal(7.0, 1.0, size=100)) - total_rejections = 0 - for i in range(100): - for j in range(100): - sess.run([grad_norm_clip], {grad_norm: random_normal[i * 100 + j]}) - clip = sess.run([grad_norm_clip], {grad_norm: outliers[i]})[0] - if clip == 0.0: - total_rejections += 1 - print('total_rejections', total_rejections) - self.assertEqual(total_rejections, 0) - - def testGradNormTrackerHasNan(self): - with self.session(use_gpu=False) as sess: - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.GradNormTracker.Params().Set( - name='grad_norm_tracker', clip_threshold=3.0) - grad_norm_tracker = p.Instantiate() - grad_norm = tf.placeholder(tf.float32) - has_nan = tf.cast(tf.ones([]), dtype=tf.bool) - grad_norm_clip = grad_norm_tracker.FPropDefaultTheta(grad_norm, has_nan) - - self.evaluate(tf.global_variables_initializer()) - - random_normal = np.exp(np.random.normal(5.0, 1.0, size=10000)) - outliers = np.exp(np.random.normal(7.0, 1.0, size=100)) - total_rejections = 0 - for i in range(100): - for j in range(100): - sess.run([grad_norm_clip], {grad_norm: random_normal[i * 100 + j]}) - clip = sess.run([grad_norm_clip], {grad_norm: outliers[i]})[0] - if clip == 0.0: - total_rejections += 1 - self.assertEqual(total_rejections, 100) - - -class HighwaySkipLayerTest(test_utils.TestCase): - - def testHighwaySkipLayerConstruction(self): - with self.session(use_gpu=False): - p = layers.HighwaySkipLayer.Params().Set( - name='gffn', - input_dim=10, - carry_bias_init=1.0, - couple_carry_transform_gates=True, - batch_norm=False, - params_init=py_utils.WeightInit.Uniform(1.0)) - proj_l = p.Instantiate() - a = tf.constant(1.0, shape=[20, 10]) - b = tf.constant(-2.0, shape=[20, 10]) - proj_l.FPropDefaultTheta(a, b) - - def testHighwaySkipLayerCarryGate(self): - with self.session(use_gpu=False): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.HighwaySkipLayer.Params().Set( - name='gffn', - input_dim=10, - carry_bias_init=1000.0, - couple_carry_transform_gates=True, - batch_norm=False, - params_init=py_utils.WeightInit.Uniform(1.0)) - proj_l = p.Instantiate() - a = tf.constant(1.0, shape=[20, 10]) - b = tf.constant(-2.0, shape=[20, 10]) - out = proj_l.FPropDefaultTheta(a, b) - self.evaluate(tf.global_variables_initializer()) - a, out = self.evaluate([a, out]) - self.assertAllClose(a, out) - - -class GatingLayerTest(test_utils.TestCase): - - def testGatingLayerConstruction(self): - with self.session(use_gpu=False): - p = layers.GatingLayer.Params().Set( - name='gating', - input_dim=10, - carry_bias_init=1.0, - params_init=py_utils.WeightInit.Uniform(1.0)) - gate = p.Instantiate() - a = tf.constant(1.0, shape=[20, 10]) - b = tf.constant(-2.0, shape=[20, 10]) - gate.FPropDefaultTheta(a, b) - - def testGatingLayerFProp(self): - with self.session(use_gpu=True): - p = layers.GatingLayer.Params().Set( - name='gate', input_dim=6, has_bias=False) - gate = p.Instantiate() - a = tf.constant(np.random.uniform(size=[10, 6]), dtype=tf.float32) - b = tf.constant(np.random.uniform(size=[10, 6]), dtype=tf.float32) - out = gate.FPropDefaultTheta(a, b) - self.evaluate(tf.global_variables_initializer()) - actual_out, w = self.evaluate([out, gate.theta.carry_gate.w]) - self.assertAllEqual(np.shape(w), [12, 6]) - w = np.matmul(a.eval(), w[:6, :]) + np.matmul(b.eval(), w[6:, :]) - sigmoid_w = 1 / (1 + np.exp(-w)) - expected_out = a * sigmoid_w + b * (1 - sigmoid_w) - self.assertAllClose(actual_out, expected_out) - - def testGatingLayerFPropSaturated(self): - with self.session(use_gpu=True): - p = layers.GatingLayer.Params().Set( - name='gate', input_dim=6, has_bias=True, carry_bias_init=100) - gate = p.Instantiate() - a = tf.constant(np.random.uniform(size=[10, 6]), dtype=tf.float32) - b = tf.constant(np.random.uniform(size=[10, 6]), dtype=tf.float32) - out = gate.FPropDefaultTheta(a, b) - self.evaluate(tf.global_variables_initializer()) - # High initial bias, causing the carry gate to saturate and the - # output will be very close to a. - self.assertAllClose(out.eval(), a.eval()) - - -class UniformLabelSmootherTest(test_utils.TestCase): - - def testUniformLabelSmoother(self): - with self.session(use_gpu=False): - params = layers.UniformLabelSmoother.Params() - params.name = 'uls' - params.num_classes = 5 - params.uncertainty = 0.1 - - smooth_layer = layers.UniformLabelSmoother(params) - target_labels = tf.constant([[0, 1, 2, 3, 3, 3, 4]], dtype=tf.int32) - target_ids = tf.constant([[0, 0, 1, 2, 3, 3, 3]], dtype=tf.int32) - target_paddings = tf.zeros(tf.shape(target_ids)) - output = smooth_layer.FPropDefaultTheta(target_paddings, target_labels, - target_ids) - self.evaluate(tf.global_variables_initializer()) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [[ - [0.89999998, 0.025 , 0.025 , 0.025 , 0.025 ], - [0.025 , 0.89999998, 0.025 , 0.025 , 0.025 ], - [0.025 , 0.025 , 0.89999998, 0.025 , 0.025 ], - [0.025 , 0.025 , 0.025 , 0.89999998, 0.025 ], - [0.025 , 0.025 , 0.025 , 0.89999998, 0.025 ], - [0.025 , 0.025 , 0.025 , 0.89999998, 0.025 ], - [0.025 , 0.025 , 0.025 , 0.025 , 0.89999998] - ]] - # pyformat: enable - # pylint: enable=bad-whitespace - output_v = output.eval() - self.assertAllClose(expected_output, output_v, atol=1e-2, rtol=1e-2) - self.assertAllClose(np.ones(output_v.shape[:-1]), output_v.sum(-1)) - - def testUniformLabelSmootherLargerToken(self): - with self.session(use_gpu=False): - params = layers.UniformLabelSmoother.Params() - params.name = 'uls' - params.num_classes = 5 - params.uncertainty = 0.1 - params.uncertainty_larger = 0.2 - params.token_id_uncertainty_larger = 4 - - smooth_layer = layers.UniformLabelSmoother(params) - target_labels = tf.constant([[0, 1, 2, 3, 3, 3, 3]], dtype=tf.int32) - target_ids = tf.constant([[0, 0, 1, 2, 4, 4, 4]], dtype=tf.int32) - target_paddings = tf.zeros(tf.shape(target_ids)) - output = smooth_layer.FPropDefaultTheta(target_paddings, target_labels, - target_ids) - self.evaluate(tf.global_variables_initializer()) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [[ - [0.89999998, 0.025 , 0.025 , 0.025 , 0.025 ], - [0.025 , 0.89999998, 0.025 , 0.025 , 0.025 ], - [0.025 , 0.025 , 0.89999998, 0.025 , 0.025 ], - [0.025 , 0.025 , 0.025 , 0.89999998, 0.025 ], - [0.05 , 0.05 , 0.05 , 0.80000001, 0.05 ], - [0.05 , 0.05 , 0.05 , 0.80000001, 0.05 ], - [0.05 , 0.05 , 0.05 , 0.80000001, 0.05 ] - ]] - # pyformat: enable - # pylint: enable=bad-whitespace - output_v = output.eval() - self.assertAllClose(expected_output, output_v, atol=1e-2, rtol=1e-2) - self.assertAllClose(np.ones(output_v.shape[:-1]), output_v.sum(-1)) - - -class WeightedSumLayerTest(test_utils.TestCase): - - def testWeightedSumLayer(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 2 - n_sources = 3 - ctxs = [[[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 4.0, 5.0]], - [[3.0, 4.0, 5.0, 6.0], [6.0, 7.0, 8.0, 9.0]], - [[4.0, 5.0, 6.0, 7.0], [7.0, 8.0, 1.0, 2.0]]] - p = layers.WeightedSumLayer.Params() - p.name = 'transparent_layer' - p.num_sources = n_sources - p.random_seed = 505837249 - merger = p.Instantiate() - - ctxs = [tf.expand_dims(i, 2) for i in ctxs] - ctx = tf.squeeze(merger.FProp(merger.theta, ctxs), 2) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate(ctx) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [[ 2.66666675, 3.66666675, 4.66666698, 5.66666698], - [ 5.0, 6.0, 4.33333349, 5.33333349]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_ctx.shape, (batch, depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - def testWeightedSumLayerGlobalWeightAndMinimalProb(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 2 - n_sources = 3 - ctxs = [[[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 4.0, 5.0]], - [[3.0, 4.0, 5.0, 6.0], [6.0, 7.0, 8.0, 9.0]], - [[4.0, 5.0, 6.0, 7.0], [7.0, 8.0, 1.0, 2.0]]] - p = layers.WeightedSumLayer.Params() - p.name = 'transparent_layer' - p.num_sources = n_sources - p.random_seed = 505837249 - p.minimal_prob = 0.01 - p.global_weight_scale = 10.0 - merger = p.Instantiate() - - ctxs = [tf.expand_dims(i, 2) for i in ctxs] - ctx = tf.squeeze(merger.FProp(merger.theta, ctxs), 2) - self.evaluate(tf.global_variables_initializer()) - actual_ctx = self.evaluate(ctx) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [[ 2.66666675, 3.66666675, 4.66666698, 5.66666698], - [ 5.0, 6.0, 4.33333349, 5.33333349]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_ctx.shape, (batch, depth)) - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - - -class DeconvLayerTest(test_utils.TestCase): - - def testDeconvLayerFProp(self): - with self.session(use_gpu=True): - tf.random.set_seed(398847392) - np.random.seed(12345) - params = layers.DeconvLayer.Params() - params.name = 'deconv' - params.filter_shape = [3, 3, 2, 8] - params.filter_stride = [2, 2] - params.params_init = py_utils.WeightInit.Gaussian(0.1) - - conv_layer = params.Instantiate() - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 4, 8]), dtype=tf.float32) - - out = conv_layer.FPropDefaultTheta(inputs) - out_shape = conv_layer.OutShape(tf.shape(inputs)) - - self.evaluate(tf.global_variables_initializer()) - out_v, shape_v = self.evaluate([out, out_shape]) - self.assertAllEqual(shape_v, [2, 8, 8, 2]) - self.assertAllEqual(out_v.shape, shape_v) - - summary = np.sum(np.square(out_v), axis=(1, 2, 3)) - tf.logging.info('testDeconvLaye rFProp actual = %s', - np.array_repr(summary)) - self.assertAllClose([4.77159977, 5.47860432], summary) - - -class GatedAverageLayerTest(test_utils.TestCase): - - def testGatedAverageLayer(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 2 - num_inputs = 3 - - inp_1 = np.asarray([[0.0, 0.0, 0.0, 0.0], [-1.0, -1.0, 1.0, 1.0]], - dtype=np.float32) - inp_2 = np.asarray([[1.0, 1.0, 1.0, 1.0], [-1.0, -1.0, 1.0, 1.0]], - dtype=np.float32) - inp_3 = np.asarray([[-1.0, -1.0, -1.0, -1.0], [-1.0, -1.0, 1.0, 1.0]], - dtype=np.float32) - p = layers.GatedAverageLayer.Params() - p.name = 'gated_avg_layer' - p.num_inputs = num_inputs - p.num_nodes = depth - p.random_seed = 505837249 - g_avg = p.Instantiate() - - avg = g_avg.FProp(g_avg.theta, [inp_1, inp_2, inp_3]) - self.evaluate(tf.global_variables_initializer()) - actual_avg = self.evaluate(avg) - - expected_avg = [[0.092766, 0.092766, 0.092766, 0.092766], - [-1., -1., 1., 1.]] - self.assertEqual(actual_avg.shape, (batch, depth)) - self.assertAllClose(expected_avg, actual_avg, rtol=1e-05, atol=1e-05) - - -class LHUCLayerTest(test_utils.TestCase): - - def testLHUCLayer(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 2 - - inp = np.asarray([[1.0, 1.0, 1.0, 1.0], [-1.0, -1.0, -1.0, -1.0]], - dtype=np.float32) - p = layers.LHUCLayer.Params() - p.name = 'lhuc_layer' - p.input_dim = depth - p.random_seed = 505837249 - lhuc = p.Instantiate() - - lhuc = lhuc.FProp(lhuc.theta, inp) - self.evaluate(tf.global_variables_initializer()) - actual_avg = self.evaluate(lhuc) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_avg = [[1.0, 1.0, 1.0, 1.0], [-1.0, -1.0, -1.0, -1.0]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_avg.shape, (batch, depth)) - self.assertAllClose(expected_avg, actual_avg, rtol=1e-05, atol=1e-05) - - -class ResidualAdapterLayerTest(test_utils.TestCase): - - def testResidualAdapterLayer(self): - with self.session(use_gpu=True): - np.random.seed(505837249) - depth = 4 - batch = 2 - - inp = np.asarray([[1.0, 1.0, 1.0, 1.0], [-1.0, -1.0, -1.0, -1.0]], - dtype=np.float32) - p = layers.ResidualAdapterLayer.Params() - p.name = 'resadap_layer' - p.input_dim = depth - p.bottleneck_dim = 2 - p.random_seed = 505837249 - resadap = p.Instantiate() - - resadap = resadap.FProp(resadap.theta, inp) - self.evaluate(tf.global_variables_initializer()) - actual_avg = self.evaluate(resadap) - - # pylint: disable=bad-whitespace - # pyformat: disable - expected_avg = [[1.0, 1.0, 1.0, 1.0], [-1.0, -1.0, -1.0, -1.0]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertEqual(actual_avg.shape, (batch, depth)) - self.assertAllClose(expected_avg, actual_avg, rtol=1e-05, atol=1e-05) - - -class GluLayerTest(test_utils.TestCase): - - def testGlu(self): - with self.session(use_gpu=True): - tf.random.set_seed(3980847392) - inputs = tf.random.normal([5, 2, 3], seed=948387483) - paddings = tf.zeros([5, 2]) - p = layers.GluLayer.Params() - p.name = 'glu_layers' - p.input_dim = 3 - glu_layer = layers.GluLayer(p) - - h = glu_layer.FPropDefaultTheta(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate(h) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ -1.84272185e-01, -3.82728219e-01, 8.69752645e-01], - [ 4.42533880e-01, 1.51665461e+00, 3.26201534e+00]], - [[ -7.06624031e-01, -6.52632236e-01, 1.22156203e+00], - [ 1.66484845e+00, 5.98078966e-01, 1.14039946e+00]], - [[ 3.26439053e-01, 2.47359693e-01, -1.14889514e+00], - [ 7.71084905e-01, 1.07083774e+00, 1.74589559e-01]], - [[ 5.70576251e-01, 7.95466423e-01, -4.07778949e-01], - [ -8.71581078e-01, -5.38501918e-01, -2.50373930e-01]], - [[ -3.88817638e-01, 5.84501982e-01, -6.60797715e-01], - [ -1.34579837e+00, -2.18637614e-03, 1.55258143e+00]]] - # pyformat: enable - # pylint: enable=bad-whitespace - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testGluWithoutResidual(self): - with self.session(use_gpu=True): - tf.random.set_seed(3980847392) - inputs = tf.random.normal([5, 2, 3], seed=948387483) - paddings = tf.zeros([5, 2]) - p = layers.GluLayer.Params() - p.name = 'glu_layers' - p.input_dim = 3 - p.output_dim = 4 - p.apply_residual = False - glu_layer = layers.GluLayer(p) - - h = glu_layer.FPropDefaultTheta(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate(h) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 0.2498899 , 0. , 0.62683833, 0. ], - [ 0.34115699, 0. , 0.38020864, 0. ]], - [[ 0.3014423 , 0. , 0.59274423, 0. ], - [ 0. , 0.35897657, 0.2908403 , 0.03678071]], - [[ 0. , 0.78786391, 0. , 0.38839644], - [ 0. , 0.44012907, 0. , 0.41553062]], - [[ 0. , 0.61838603, 0. , 0.41521466], - [ 0.34117079, 0. , 0.0372162 , 0. ]], - [[ 0. , 0. , 0. , 0.28136203], - [ 0.34413674, 0. , 0.30943182, 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - -class MultitaskAdapterLayerTest(test_utils.TestCase, parameterized.TestCase): - - def _MultitaskAdapterParams(self, data_format='TBC'): - return layers.MultitaskAdapterLayer.Params().Set( - name='multi_adapter', - input_dim=4, - bottleneck_dim=2, - num_tasks=3, - data_format=data_format, - random_seed=505837249) - - @parameterized.parameters('TBC', 'BTC') - def testSingleStepFProp(self, data_format): - with self.session(use_gpu=True): - np.random.seed(1234567) - # Inputs are of shape [1, batch, input_dim] (single time step) - # Batch elements 0, 2, and 3 are identical, but 0 and 2 have the same - # task ID where as 3 has a different task ID. - inputs = tf.constant([[[0.5, 0.3, -0.2, 0.0], [0.0, 0.7, -1.0, 2.0], - [0.5, 0.3, -0.2, 0.0], [0.5, 0.3, -0.2, 0.0]]], - dtype=tf.float32) - tasks = tf.constant([1, 0, 1, 0], dtype=tf.int32) - if data_format == 'BTC': - inputs = tf.transpose(inputs, [1, 0, 2]) - p = self._MultitaskAdapterParams(data_format) - adapter = p.Instantiate() - output = adapter.FProp(adapter.theta, inputs, tasks) - self.evaluate(tf.global_variables_initializer()) - actual = self.evaluate(output) - if data_format == 'BTC': - actual = tf.transpose(actual, [1, 0, 2]) - tf.logging.info('testSingleStepFProp actual=%r' % actual) - expected = [[[1.1579462, 1.2241995, -0.6177901, 0.23089096], - [0.05022227, 0.9056754, -0.3771479, 2.1245508], - [1.1579462, 1.2241995, -0.6177901, 0.23089096], - [0.55389655, 0.5207228, 0.46842042, 0.13366304]]] - self.assertEqual(actual.shape, (1, 4, 4)) - # Batch elements 0 and 2 are equal because they had the same input - # and the same task ID. - self.assertAllClose(actual[0][0], actual[0][2], rtol=1e-05, atol=1e-05) - self.assertAllClose(expected, actual, rtol=1e-05, atol=1e-05) - - @parameterized.parameters('TBC', 'BTC') - def testMultiStepFProp(self, data_format): - with self.session(use_gpu=True): - np.random.seed(1234567) - # Inputs are same as above but of shape [time, batch, input_dim] - inputs = tf.constant([[[0.5, 0.3, -0.2, 0.0], [0.0, 0.7, -1.0, 2.0]], - [[0.5, 0.3, -0.2, 0.0], [0.5, 0.3, -0.2, 0.0]]], - dtype=tf.float32) - # tasks is of shape [batch] indicating one task for each sequence. - tasks = tf.constant([1, 0], dtype=tf.int32) - if data_format == 'BTC': - inputs = tf.transpose(inputs, [1, 0, 2]) - p = self._MultitaskAdapterParams(data_format) - adapter = p.Instantiate() - output = adapter.FProp(adapter.theta, inputs, tasks) - self.evaluate(tf.global_variables_initializer()) - actual = self.evaluate(output) - if data_format == 'BTC': - actual = tf.transpose(actual, [1, 0, 2]) - tf.logging.info('testMultiStepFProp actual=%r' % actual) - # Output is same as above but with shape same as input. - expected = [[[1.1579462, 1.2241995, -0.6177901, 0.23089096], - [0.05022227, 0.9056754, -0.3771479, 2.1245508]], - [[1.1579462, 1.2241995, -0.6177901, 0.23089096], - [0.55389655, 0.5207228, 0.46842045, 0.13366304]]] - self.assertEqual(actual.shape, (2, 2, 4)) - self.assertAllClose(expected, actual, rtol=1e-05, atol=1e-05) - - @parameterized.parameters('TBC', 'BTC') - def testSpecifyTaskPerTimestepFProp(self, data_format): - with self.session(use_gpu=True): - np.random.seed(1234567) - inputs = tf.constant([[[0.5, 0.3, -0.2, 0.0], [0.0, 0.7, -1.0, 2.0]], - [[0.5, 0.3, -0.2, 0.0], [0.5, 0.3, -0.2, 0.0]]], - dtype=tf.float32) - # tasks are same as above but of shape [time, batch] indicating that - # we should look up adapter params per timestep. In this example we - # still have the task ID consistent across timesteps in order to - # replicate the previous test's output. - tasks = tf.constant([[1, 0], [1, 0]], dtype=tf.int32) - if data_format == 'BTC': - inputs = tf.transpose(inputs, [1, 0, 2]) - tasks = tf.transpose(tasks, [1, 0]) - p = self._MultitaskAdapterParams(data_format) - adapter = p.Instantiate() - output = adapter.FProp(adapter.theta, inputs, tasks) - self.evaluate(tf.global_variables_initializer()) - actual = self.evaluate(output) - if data_format == 'BTC': - actual = tf.transpose(actual, [1, 0, 2]) - tf.logging.info('testSpecifyTaskPerTimestepFProp actual=%r' % actual) - # Output is same as above. - expected = [[[1.1579462, 1.2241995, -0.6177901, 0.23089096], - [0.05022227, 0.9056754, -0.3771479, 2.1245508]], - [[1.1579462, 1.2241995, -0.6177901, 0.23089096], - [0.55389655, 0.5207228, 0.46842042, 0.13366304]]] - self.assertEqual(actual.shape, (2, 2, 4)) - self.assertAllClose(expected, actual, rtol=1e-05, atol=1e-05) - - @parameterized.parameters('TBC', 'BTC') - def testDifferentTaskPerTimestepFProp(self, data_format): - with self.session(use_gpu=True): - np.random.seed(1234567) - inputs = tf.constant([[[0.5, 0.3, -0.2, 0.0], [0.0, 0.7, -1.0, 2.0]], - [[0.5, 0.3, -0.2, 0.0], [0.5, 0.3, -0.2, 0.0]]], - dtype=tf.float32) - # tasks are again of shape [time, batch] but with different tasks - # for each timestep. - tasks = tf.constant([[1, 0], [2, 1]], dtype=tf.int32) - if data_format == 'BTC': - inputs = tf.transpose(inputs, [1, 0, 2]) - tasks = tf.transpose(tasks, [1, 0]) - p = self._MultitaskAdapterParams(data_format) - adapter = p.Instantiate() - output = adapter.FProp(adapter.theta, inputs, tasks) - self.evaluate(tf.global_variables_initializer()) - actual = self.evaluate(output) - if data_format == 'BTC': - actual = tf.transpose(actual, [1, 0, 2]) - tf.logging.info('testDifferentTaskPerTimestepFProp actual=%r' % actual) - expected = [[[1.1579462, 1.2241995, -0.6177901, 0.23089096], - [0.05022227, 0.9056754, -0.3771479, 2.1245508]], - [[0.6961179, 0.06690431, -0.08757646, 0.40129724], - [1.1579462, 1.2241995, -0.6177901, 0.23089096]]] - self.assertEqual(actual.shape, (2, 2, 4)) - self.assertAllClose(expected, actual, rtol=1e-05, atol=1e-05) - - def testGradientChecker(self): - with self.session(use_gpu=True): - np.random.seed(1234567) - inputs = tf.constant([[[0.5, 0.3, -0.2, 0.0], [0.0, 0.7, -1.0, 2.0]], - [[0.5, 0.3, -0.2, 0.0], [0.5, 0.3, -0.2, 0.0]]], - dtype=tf.float32) - tasks = tf.constant([1, 0], dtype=tf.int32) - p = self._MultitaskAdapterParams() - adapter = p.Instantiate() - output = adapter.FProp(adapter.theta, inputs, tasks) - loss = tf.reduce_sum(output) - all_vars = tf.trainable_variables() - grads = tf.gradients(loss, all_vars) - self.evaluate(tf.global_variables_initializer()) - - def DenseGrad(var, grad): - if isinstance(grad, tf.Tensor): - return grad - elif isinstance(grad, tf.IndexedSlices): - return tf.math.unsorted_segment_sum(grad.values, grad.indices, - tf.shape(var)[0]) - - dense_grads = [DenseGrad(x, y) for (x, y) in zip(all_vars, grads)] - dense_grad_sums = [tf.reduce_sum(g) for g in dense_grads] - grad_vs = self.evaluate(dense_grad_sums) - self.assertAllClose([ - -5.364418e-07, 3.405262e+00, 1.252710e+01, 1.600000e+01, 1.335246e+00, - 2.513876e-01 - ], - grad_vs, - rtol=1e-05, - atol=1e-05) - - -class CCTGatingNetworkTest(test_utils.TestCase): - - def testCCTGatingNetworkConstruction(self): - with self.session(use_gpu=False): - p = layers.CCTGatingNetwork.Params().Set( - name='cct_gating', - input_dim=10, - hidden_layer_dim=20, - num_outputs=3, - noise_std=5.0, - noise_warmup_steps=300) - cct_l = p.Instantiate() - a = tf.constant(1.0, shape=[20, 10]) - cct_l.FPropDefaultTheta(a) - - def testCCTGatingNetworkTraining(self): - with self.session(use_gpu=False): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.CCTGatingNetwork.Params().Set( - name='cct_gating', - input_dim=10, - hidden_layer_dim=20, - num_outputs=3, - noise_std=5.0, - noise_warmup_steps=300) - params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=837465638) - p.params_init = params_init - p.is_inference = False - cct_net = p.Instantiate() - - a = tf.constant(np.random.rand(3, 10), dtype=tf.float32) - out = cct_net.FPropDefaultTheta(a) - - self.evaluate(tf.global_variables_initializer()) - out_v = self.evaluate([out]) - self.assertAllClose( - out_v, - [[[0.412904, 0.520129, 0.694699], [0.395485, 0.47316, 0.632451], - [0.404144, 0.502593, 0.644338]]]) - - def testCCTGatingNetworkInference(self): - with self.session(use_gpu=False), self.SetEval(True): - tf.random.set_seed(398847392) - np.random.seed(12345) - p = layers.CCTGatingNetwork.Params().Set( - name='cct_gating', - input_dim=10, - hidden_layer_dim=20, - num_outputs=3, - noise_std=5.0, - noise_warmup_steps=300) - params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=837465638) - p.params_init = params_init - cct_net = p.Instantiate() - - a = tf.constant(np.random.rand(3, 10), dtype=tf.float32) - out = cct_net.FPropDefaultTheta(a) - - self.evaluate(tf.global_variables_initializer()) - out_v = self.evaluate([out]) - self.assertAllClose(out_v, [[[0., 1., 1.], [0., 0., 1.], [0., 1., 1.]]]) - - -class CondScaleShiftFFNLayerTest(test_utils.TestCase): - - def testCondScaleShiftFFNLayerConstruction(self): - with self.session(use_gpu=False): - params = layers.CondScaleShiftFFNLayer.Params().Set( - name='ss_ffn', - input_dim=10, - output_dim=7, - scale_fn='NONE', - shift_fn='NONE') - params.ffn.hidden_layer_dims = [5, 5] - layer_ss = params.Instantiate() - time_c, batch_c, in_dim_c = 15, 2, 10 - a = tf.constant(1.0, shape=[time_c, batch_c, in_dim_c]) - layer_ss.FPropDefaultTheta(a) - - def testCondScaleShiftFFNLayerFprop(self): - with self.session(use_gpu=False): - time_c, batch_c, in_dim_c, out_dim_c = 15, 2, 10, 7 - params = layers.CondScaleShiftFFNLayer.Params().Set( - name='ss_ffn', - input_dim=in_dim_c, - output_dim=out_dim_c, - scale_fn='NONE', - shift_fn='NONE') - params.ffn.hidden_layer_dims = [5, 5] - layer_ss = params.Instantiate() - - a = tf.constant(1.0, shape=[time_c, batch_c, in_dim_c]) - scale_out, shift_out = layer_ss.FPropDefaultTheta(a) - self.evaluate(tf.global_variables_initializer()) - scale_out, shift_out = self.evaluate([scale_out, shift_out]) - self.assertEqual(scale_out.shape, (time_c, batch_c, out_dim_c)) - self.assertEqual(shift_out.shape, (time_c, batch_c, out_dim_c)) - - -class IdentityLayerTest(test_utils.TestCase): - - def testIdentityLayerNestedMap(self): - with self.session(use_gpu=False): - p = layers.IdentityLayer.Params().Set(name='Nested') - layer = p.Instantiate() - a = tf.constant(1.0, shape=[20, 10]) - b = tf.constant(-2.0, shape=[20, 10]) - inputs = py_utils.NestedMap(a=a, b=b) - outputs = layer.FPropDefaultTheta(inputs) - self.assertAllEqual(inputs.a.eval(), outputs.a.eval()) - self.assertAllEqual(inputs.b.eval(), outputs.b.eval()) - a_copy = layer.FPropDefaultTheta(a) - self.assertAllEqual(a.eval(), a_copy.eval()) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/layers_with_attention.py b/lingvo/core/layers_with_attention.py deleted file mode 100644 index c6c54ea3..00000000 --- a/lingvo/core/layers_with_attention.py +++ /dev/null @@ -1,2205 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Lingvo layers that depend on attention layers but are not recurrent.""" - -import lingvo.compat as tf -from lingvo.core import attention -from lingvo.core import base_layer -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import symbolic - - -class TransformerAttentionLayer(base_layer.BaseLayer): - """Multi-headed attention, add and norm used by 'Attention Is All You Need'. - - This class implements the first sub-layer of Transformer Layer. Input is - first processed using a multi-headed (self) attention. Output of the - attention layer is combined with the residual connection. And the finally, - output is normalized using Layer Normalization. - - Layer can be used in five scenarios: - - 1. Multi-Headed Self-Attention, where attention keys (source vectors), - attention values (context vectors) and queries come from the same previous - layer output, `query_vec`. This is the general use case for encoder - Transformer Layers. - 2. Masked Multi-Headed Self-Attention, where attention keys, attention values - and queries all come from the same previous layer output, but rightward - activations are masked to prevent information flow from future. This is the - use case for decoder self-attention Transformer Layers. Can be activated by - setting `is_masked` flag of this layer. - 3. Multi-Headed Attention, where attention keys and attention values - `source_vecs`, are coming from a different source (output of the encoder) - and queries `query_vec`, coming from the previous layer outputs (decoder). - This corresponds to the standard attention mechanism, decoder attending the - encoder outputs. - 4. Multi-Headed Attention, where attention values `context_vecs` are coming - from a different source than queries and keys, e.g. for positional - attention, where keys and queries are positional encodings and values are - decoder states. - 5. Masked Multi-Headed Self-Attention, where attention keys, attention values - and queries all come from the same previous layer output, but the - activations for the current position are masked to reduce the impact of - high self-similarity. This is the use case for non-autoregressive decoder - self-attention Transformer Layers. Can be activated by setting `is_masked` - flag of this layer and setting `mask_type="eye"`. - 6. Masked Multi-Headed Self-Attention, where attention keys, attention values - and queries all come from the same previous layer output, but: - . rightward activations are masked to prevent information flow from future. - . leftward activations are also masked to prevent information flow from - past tokens that are beyond the N-gram context [K-N+1, K-1] when predicting - the target token in position K. This is the use case for decoder - self-attention Transformer Layers in N-gram mode. Can be activated by - setting `is_masked` flag of this layer, and setting both - `mask_type="ngram"` and `mask_ngram_order=N-1` to use as context only the - previous N-1 tokens (as expected for an N-gram model); for details and - experimental results see https://arxiv.org/abs/2001.04589. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('source_dim', 0, 'Dimension of the transformer block input.') - p.Define('context_dim', 0, 'Dimension of the attention contexts.') - p.Define('atten_hidden_dim', 0, 'Dimension of the attention hidden dim.') - p.Define('num_attention_heads', 8, 'Number of attention heads.') - p.Define('is_masked', False, 'If set, uses masked MultiHeadedAttention.') - p.Define( - 'mask_ngram_order', 0, 'N-gram order, relevant only when' - '`mask_type` is set to "ngram".') - p.Define( - 'mask_type', 'future', 'Type of attention mask if `is_masked` is' - 'set. Either "future" for masking out attention to future' - 'positions or "eye" for masking out the token itself, or "ngram" for' - 'bounding the left context to the previous N-1 tokens, where N is set' - 'by `mask_ngram_order`.') - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params.') - p.Define( - 'atten_tpl', - attention.MultiHeadedAttention.Params().Set( - use_source_vec_as_attention_value=False, enable_ctx_post_proj=True), - 'Multi-Headed Dot-Attention default params.') - p.Define( - 'atten_dropout_prob', 0.0, - 'Probability at which we apply dropout to the attention probs. ' - 'This practically drops memory values at random positions.') - p.Define( - 'residual_dropout_prob', 0.0, - 'Probability at which we apply dropout to the residual layers, ' - 'such that, residual(x, f(x)) = (x + dropout(f(x))).') - p.Define( - 'residual_dropout_tpl', layers.DropoutLayer.Params(), - 'Residual dropout params template. keep_prop will be reset to ' - '(1.0 - residual_dropout_prob).') - p.Define('packed_input', False, - 'If True, each training example may pack multiple sequences.') - p.Define('add_unnormalized_input', False, 'If set, uses unnormalized input ' - 'in the residual add.') - p.Define( - 'residual_function', None, 'When None (the default), use simple ' - 'sum for the residual connection (output = x + f(x)). For example, can ' - 'use layers.HighwaySkipLayer.Params() or layers.GatingLayer.Params() ' - 'for gated residual add, where output is instead ' - 'residual_function.FProp(x, f(x)).') - p.Define( - 'pre_layer_norm', True, 'When True, layer norm is used before attention' - 'module, otherwise used after attention module which is consistent with' - 'Vaswani et al\'s paper') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.source_dim - - if not p.atten_hidden_dim: - p.atten_hidden_dim = p.source_dim - - if not p.context_dim: - p.context_dim = p.source_dim - - if p.is_masked: - assert p.mask_type in ['future', 'eye', 'ngram'] - - params = self._InitAttention(p.atten_tpl) - self.CreateChild('atten', params) - - # Initialize attention layer norm - params = p.ln_tpl.Copy() - params.name = 'atten_ln' - params.input_dim = p.source_dim - self.CreateChild('layer_norm', params) - - dropout_tpl = p.residual_dropout_tpl.Copy() - dropout_tpl.keep_prob = (1.0 - p.residual_dropout_prob) - self.CreateChild('residual_dropout', dropout_tpl) - - if p.residual_function is not None: - params = p.residual_function.Copy() - params.input_dim = p.atten_hidden_dim - self.CreateChild('residual_function', params) - - def _InitAttention(self, atten_tpl): - p = self.params - # Initialize multi-headed attention - params = atten_tpl.Copy() - params.name = 'multihead_atten' - params.source_dim = p.source_dim - params.query_dim = p.source_dim - params.hidden_dim = p.atten_hidden_dim - params.context_dim = p.context_dim - params.ctx_post_proj_dim = p.source_dim - params.num_attention_heads = p.num_attention_heads - params.atten_dropout_prob = p.atten_dropout_prob - params.packed_input = p.packed_input - return params - - def _GetSourceLength(self, source_paddings): - return py_utils.GetShape(source_paddings)[0] - - def FProp(self, - theta, - query_vec, - source_paddings, - source_vecs=None, - query_segment_id=None, - source_segment_id=None, - context_vecs=None, - **kwargs): - """Transformer attention, residual and normalization layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_time, target_batch, dim] - source_paddings: [source_time, source_batch] - source_vecs: [source_time, source_batch, dim]. - query_segment_id: [target_time, target_batch] - source_segment_id: [source_time, source_batch] - context_vecs: [source_time, target_batch, dim] - **kwargs: Can be optional params for the attention layer, eg. attention - projection index tensor. - - Returns: - (output, atten_probs). output is of shape [target_time, target_batch, - context_dim], atten_probs is of shape [target_time, target_batch, - source_time]. - """ - p = self.params - unnormalized_query_vec = query_vec - if p.pre_layer_norm: - query_vec = self.layer_norm.FProp(theta.layer_norm, query_vec) - - if source_vecs is None: # For self-attention: keys = queries. - source_vecs = query_vec - source_segment_id = query_segment_id - - if context_vecs is None: # Inter/self-attention: keys = values/contexts. - context_vecs = source_vecs - - target_time, target_bs, query_dim = py_utils.GetShape(query_vec, 3) - if p.is_masked: - assert source_vecs is not None - query_vec = py_utils.with_dependencies([ - py_utils.assert_shape_match( - tf.shape(source_vecs), tf.shape(query_vec)) - ], query_vec) - # Prepares mask for self-attention - # Padding is complemented, so time indexes that we want to mask out - # receive padding weight 1.0. - if p.mask_type == 'future': - padding = py_utils.CausalSelfAttenPadding( - target_time, dtype=py_utils.FPropDtype(p)) - elif p.mask_type == 'eye': - padding = tf.eye(target_time, target_time, dtype=py_utils.FPropDtype(p)) - elif p.mask_type == 'ngram': # Maybe apply N-gram mask. - assert p.mask_ngram_order - padding = 1.0 - tf.linalg.band_part( - tf.ones([target_time, target_time], dtype=py_utils.FPropDtype(p)), - tf.minimum(p.mask_ngram_order - 1, target_time - 1), 0) - - # [time, batch, time] - causal_padding = tf.tile(tf.expand_dims(padding, 1), [1, target_bs, 1]) - - causal_padding = tf.reshape(causal_padding, [-1, target_time]) - else: - causal_padding = None - - # Projects keys and values. - packed_src = self.atten.PackSource( - theta=theta.atten, - source_vecs=source_vecs, # keys - source_contexts=context_vecs, # values - source_padding=source_paddings, - source_segment_id=source_segment_id) - - if query_segment_id is not None: - query_segment_id = tf.reshape(query_segment_id, [-1]) - - ctx_vec, atten_prob, _ = self.atten.ComputeContextVectorWithSource( - theta=theta.atten, - packed_src=packed_src, - query_vec=tf.reshape(query_vec, [-1, query_dim]), - per_step_source_padding=causal_padding, - query_segment_id=query_segment_id, - **kwargs) - - ctx_vec = self.residual_dropout.FProp(theta.residual_dropout, ctx_vec) - input_to_add = ( - unnormalized_query_vec if p.add_unnormalized_input else query_vec) - input_after_sublayer = tf.reshape( - ctx_vec, - [ - target_time, - target_bs, - -1 # Either projected or not. - ]) - if p.residual_function is None: - h = input_to_add + input_after_sublayer - else: - h = self.residual_function.FProp(theta.residual_function, input_to_add, - input_after_sublayer) - if not p.pre_layer_norm: - h = self.layer_norm.FProp(theta.layer_norm, h) - atten_prob = tf.reshape( - atten_prob, - [target_time, target_bs, - self._GetSourceLength(source_paddings)]) - return h, atten_prob - - def _FinishExtendStep(self, - theta, - query_vec, - unnormalized_query_vec, - extended_packed_src, - t=None): - """Finish extending prefix by one more time step. - - Isolating this function from ExtendStep allows generalizing self-attention - to causal attention on other inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, dim] - unnormalized_query_vec: [target_batch, dim] - extended_packed_src: A `.NestedMap` object containing source_vecs, - source_contexts, source_paddings, and source_segment_ids - t: a scalar, the current time step, 0-based. - - Returns: - A triplet (cur_output, atten_prob, new_state) where cur_output is a tensor - representing the output from the current state, and new_state is the new - state `.NestedMap`. - """ - p = self.params - - # Compute per_step_source_padding. Padding is complemented, so time indexes - # that we want to mask out receive padding weight 1.0. - query_batch_size = py_utils.GetShape(query_vec)[0] - source_seq_len = py_utils.GetShape(extended_packed_src.source_vecs)[0] - zero_padding = tf.fill([source_seq_len], - tf.constant(0.0, dtype=query_vec.dtype)) - ones_padding = tf.ones_like(zero_padding, dtype=query_vec.dtype) - if t is not None: - per_step_source_padding = tf.where( - tf.less(tf.range(source_seq_len), tf.fill([source_seq_len], t + 1)), - zero_padding, ones_padding) - per_step_source_padding = tf.tile( - tf.expand_dims(per_step_source_padding, axis=0), - [query_batch_size, 1]) - # Maybe apply N-gram masking. - # TODO(ciprianchelba): As pointed out by miachen, to get the expected - # speed-up we should go with per_step_source_padding=None here, and - # everytime we update the prefix_states, we not only extend one step, but - # also only keep the prefix_states for the most recent N steps instead of - # the prefix states all the way from step 0. - elif p.is_masked and p.mask_type == 'ngram': - assert p.mask_ngram_order - idx = tf.maximum(0, source_seq_len - p.mask_ngram_order) - per_step_source_padding = tf.where( - tf.less(tf.range(source_seq_len), tf.fill([source_seq_len], idx)), - ones_padding, zero_padding) - per_step_source_padding = tf.tile( - tf.expand_dims(per_step_source_padding, axis=0), - [query_batch_size, 1]) - else: - per_step_source_padding = None - - ctx_vec, atten_prob, _ = self.atten.ComputeContextVectorWithCachedSource( - theta.atten, - extended_packed_src, - query_vec, - per_step_source_padding=per_step_source_padding) - - ctx_vec = self.residual_dropout.FProp(theta.residual_dropout, ctx_vec) - input_to_add = ( - unnormalized_query_vec if p.add_unnormalized_input else query_vec) - input_after_sublayer = tf.reshape(ctx_vec, py_utils.GetShape(query_vec)) - if p.residual_function is None: - h = input_to_add + input_after_sublayer - else: - h = self.residual_function.FProp(theta.residual_function, input_to_add, - input_after_sublayer) - - if not p.pre_layer_norm: - h = self.layer_norm.FProp(theta.layer_norm, h) - - new_states = py_utils.NestedMap( - key=extended_packed_src.source_vecs, - value=extended_packed_src.source_contexts) - return h, atten_prob, new_states - - def ExtendStep(self, theta, query_vec, prefix_state, t=None): - """Extend prefix by one more time step. - - This function is expected to be called during fast decoding of the - Transformer model. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, dim] - prefix_state: dict, containing tensors which are the results of previous - attentions, used for fast decoding. - t: a scalar, the current time step, 0-based. - - Returns: - A triplet (cur_output, atten_prob, new_state) where cur_output is a tensor - representing the output from the current state, and new_state is the new - state `.NestedMap`. - """ - p = self.params - assert p.is_masked # Must be causal attention. - unnormalized_query_vec = query_vec - if p.pre_layer_norm: - query_vec = self.layer_norm.FProp(theta.layer_norm, query_vec) - - cached_packed_src = py_utils.NestedMap( - source_vecs=prefix_state.key, - source_contexts=prefix_state.value, - source_padding=None, - source_segment_id=None) - extended_packed_src = self.atten.ExtendSourcePacked(theta.atten, query_vec, - query_vec, None, None, - cached_packed_src, t) - return self._FinishExtendStep(theta, query_vec, unnormalized_query_vec, - extended_packed_src, t) - - -class TransformerMultiSourceAttentionLayer(TransformerAttentionLayer): - """Multi-source multi-headed attention. - - Only supports scenarios 3 and 4 in the base class. Now the two scenarios are: - - 3. Multi-source multi-Headed Attention, where attention keys and attention - values `source_vecs`, are different encodings and queries `query_vec`, - coming from the previous layer outputs (decoder). In addition, - attention keys and values are NestedMaps containing encodings of different - sources. This corresponds to a multi-source decoder-to-encoder attention - mechanism, i.e., decoder attends to encoder outputs and other sources. - 4. Similar to 3 but attention values `context_vecs` are coming from a - different source than queries and keys. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_source', 0, 'Number of sources to attend to.') - p.Define( - 'primary_source_index', 0, 'Index of the primary source whose ' - 'attention probs will be returned.') - p.Define('multi_source_atten', attention.MultiSourceAttention.Params(), - 'Multi-source attention params.') - # Only used for case 3 and 4. - p.is_masked = False - return p - - def _InitAttention(self, atten_tpl): - p = self.params - source_atten_tpls = [] - # Set up each source attention. - for i in range(p.num_source): - src_key = 'source_%d' % i - src_atten = atten_tpl.Copy() - src_atten = super()._InitAttention(src_atten) - src_atten.name = 'multihead_atten_%s' % src_key - source_atten_tpls.append((src_key, src_atten)) - - # Initialize multi-source attention. - msa = p.multi_source_atten.Copy() - msa.name = 'multi_source_atten' - msa.source_dim = p.source_dim - msa.query_dim = p.source_dim - msa.source_atten_tpls = source_atten_tpls - msa.primary_source_key = 'source_%d' % p.primary_source_index - return msa - - def _GetSourceLength(self, source_paddings): - return py_utils.GetShape( - source_paddings['source_%d' % self.params.primary_source_index])[0] - - -class TransformerFeedForwardLayer(base_layer.BaseLayer): - """Feed-forward, add and norm layer used by 'Attention Is All You Need'. - - This class implements the second sub-layer of Transformer Layer. First, - input passes through a feed-forward neural network with one hidden layer and - then projected back to the original input dimension to apply residual. Output - of the layer, is then normalized using Layer Normalization. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the layer input.') - p.Define('output_dim', 0, 'Dimension of the layer output.') - p.Define('hidden_dim', 0, 'Dimension of the hidden layer.') - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params') - p.Define('activation', 'RELU', 'Non-linearity.') - p.Define( - 'residual_weight', 1., 'Weight applied on residual connection.' - 'Final output is residual_weight * residual_fn(x) + x.' - 'Only effective when add_skip_connection is True.') - p.Define('fflayer_tpl', - layers.FeedForwardNet.Params().Set(activation=['RELU', 'NONE']), - 'Feed forward layer default params') - p.Define( - 'res_proj_tpl', - layers.ProjectionLayer.Params().Set(batch_norm=True), - 'Residual projection default params, used when input_dim != ' - 'output_dim.') - p.Define( - 'residual_dropout_prob', 0.0, - 'Probability at which we apply dropout to the residual layers, ' - 'such that, residual(x, y) = (x + dropout(y)).') - p.Define( - 'residual_dropout_tpl', layers.DropoutLayer.Params(), - 'Residual dropout params template. keep_prop will be reset to ' - '(1.0 - residual_dropout_prob).') - p.Define( - 'relu_dropout_prob', 0.0, - 'Probability at which we apply dropout to the hidden layer ' - 'of feed-forward network.') - p.Define('add_skip_connection', True, - 'If True, add skip_connection from input to output.') - p.Define('pre_layer_norm', True, 'Pre or post layer norm') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.input_dim - assert symbolic.ToStatic(p.hidden_dim) > 0 - - # Initialize feed-forward layer - params = p.fflayer_tpl.Copy() - params.name = 'fflayer' - params.input_dim = p.input_dim - params.activation = [p.activation, 'NONE'] - if p.output_dim == 0: - params.hidden_layer_dims = [p.hidden_dim, p.input_dim] - else: - params.hidden_layer_dims = [p.hidden_dim, p.output_dim] - - if p.output_dim != p.input_dim: - pj = p.res_proj_tpl.Copy() - pj.name = 'res_proj' - pj.input_dim = p.input_dim - pj.output_dim = p.output_dim - pj.activation = 'NONE' - self.CreateChild('res_proj_layer', pj) - - params.dropout = [ - params.dropout.cls.Params().Set(keep_prob=1.0 - p.relu_dropout_prob), - params.dropout.cls.Params().Set(keep_prob=1.0) - ] - self.CreateChild('fflayer', params) - - # Initialize feed-forward layer norm - params = p.ln_tpl.Copy() - params.name = 'fflayer_ln' - params.input_dim = p.input_dim - self.CreateChild('layer_norm', params) - - dropout_tpl = p.residual_dropout_tpl.Copy() - dropout_tpl.keep_prob = (1.0 - p.residual_dropout_prob) - self.CreateChild('residual_dropout', dropout_tpl) - - @property - def output_dim(self): - """Returns output dimension of the transformer layer.""" - return self.fflayer.output_dim - - @classmethod - def NumOutputNodes(cls, p): - return p.output_dim if p.output_dim else p.input_dim - - def FProp(self, theta, inputs, paddings): - """Feed-forward, residual and layer-norm. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: [time, batch, dim]. - paddings: [time, batch] - - Returns: - tensor of the same shape with inputs - """ - if self.params.pre_layer_norm: - inputs_normalized = self.layer_norm.FProp(theta.layer_norm, inputs) - else: - inputs_normalized = inputs - if hasattr(self, 'res_proj_layer'): - inputs = self.res_proj_layer.FProp(theta.res_proj_layer, inputs) - h = self.residual_dropout.FProp( - theta.residual_dropout, - self.fflayer.FProp(theta.fflayer, inputs_normalized, - tf.expand_dims(paddings, -1))) - if self.params.add_skip_connection: - h = inputs + h * self.params.residual_weight - if not self.params.pre_layer_norm: - h = self.layer_norm.FProp(theta.layer_norm, h) - return h - - -class TransformerLayer(base_layer.BaseLayer): - """Transformer Layer proposed by 'Attention Is All You Need'. - - Applies self-attention followed by a feed forward network and - layer normalization. Uses residual connections between each consecutive - layer. In particular, adds residuals from layer input and attention output - and from attention output (feed-forward input) to feed-forward output. - - Implements the transformer block in 'Attention is All You Need': - https://arxiv.org/abs/1706.03762. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('source_dim', 0, 'Dimension of the transformer block input.') - p.Define('output_dim', 0, 'Dimension of the transformer block output.') - p.Define('tr_atten_tpl', - TransformerAttentionLayer.Params().Set(num_attention_heads=8), - 'Transformer Attention Layer params.') - p.Define('tr_post_ln_tpl', None, - '(Optional) Layer norm at end of transformer layer.') - p.Define('tr_fflayer_tpl', - TransformerFeedForwardLayer.Params().Set(hidden_dim=2048), - 'Transformer Feed-Forward Layer params.') - p.Define( - 'has_aux_atten', False, - 'If set, introduces a second attention layer, which attends to' - ' the auxiliary source contexts.') - p.Define('tr_aux_atten_tpl', None, 'Transformer Attention Layer params.') - p.Define('mask_self_atten', False, 'If True, use masked self-attention.') - p.Define('packed_input', False, - 'If True, each training example may pack multiple sequences.') - p.Define( - 'is_decoder', False, '(Deprecated) ' - 'If true, forces both has_aux_atten and mask_self_atten to true.') - p.Define( - 'num_aux_atten_post_proj', 1, 'Number of post projections for aux ' - 'attention. This is usually used in multi-task setting, in which ' - 'each task uses one dedicated projection layer.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.source_dim - - if p.is_decoder: - tf.logging.warning('TransformerLayer.is_decoder is deprecated.') - p.has_aux_atten = True - p.mask_self_atten = True - - # Initialize multi-headed self-attention - params = p.tr_atten_tpl.Copy() - params.name = 'multihead_self_atten' - params.source_dim = p.source_dim - params.packed_input = p.packed_input - params.is_masked = p.mask_self_atten - self.CreateChild('self_atten', params) - - if p.has_aux_atten: - # Initialize masked-multi-headed attention - params = ( - p.tr_atten_tpl.Copy() - if p.tr_aux_atten_tpl is None else p.tr_aux_atten_tpl.Copy()) - params.name = 'multihead_atten' - params.source_dim = p.source_dim - params.packed_input = p.packed_input - if hasattr(params.atten_tpl, 'num_post_proj'): - params.atten_tpl.num_post_proj = p.num_aux_atten_post_proj - self.CreateChild('atten', params) - - # Initialize feed-forward layer - params = p.tr_fflayer_tpl.Copy() - params.name = 'tr_fflayer' - params.input_dim = p.source_dim - params.output_dim = p.output_dim - self.CreateChild('fflayer', params) - - # Initialize output layer norm - if p.tr_post_ln_tpl: - params = p.tr_post_ln_tpl.Copy() - params.name = 'tr_post_layer_norm' - params.input_dim = p.source_dim - self.CreateChild('layer_norm', params) - - @property - def output_dim(self): - """Returns output dimension of the transformer layer.""" - # output_dim is equal to p.source_dim when p.output_dim is zero. - return self.fflayer.output_dim - - @classmethod - def NumOutputNodes(cls, p): - return p.output_dim if p.output_dim else p.source_dim - - def FProp(self, - theta, - source_vecs, - source_paddings, - aux_vecs=None, - aux_paddings=None, - source_segment_id=None, - aux_segment_id=None, - **kwargs): - """Transformer Layer. - - Transformer layer has the naming scheme as follows: `source_vecs` and - `source_paddings` are all assumed to be coming from the activations of the - layer below. When `TransformerLayer` is used in the Encoder (default - behavior of this layer) `source_*` tensors correspond to the outputs of - previous encoder layer. Further, keys, values and queries are all - forked from `source_vecs`. When TransformerLayer is used in the Decoder - (has_aux_atten=True), `source_*` tensors correspond to the outputs of - previous decoder layer and used as the queries. - - For the cases when `TransformerLayer` is used in the decoder - (has_aux_atten=True) `aux_*` tensors have to be provided. Auxiliary inputs, - `aux_*` tensors, are then correspond to the top-most layer encoder outputs - and used by the second `TransformerAttentionLayer` as keys and values. - - Regardless of the encoder or decoder, queries are always assumed to be - coming from the activations of layer below, in particular `source_vecs`. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: [source_time, source_batch, dim]. - source_paddings: [source_time, source_batch] - aux_vecs: [aux_time, aux_batch, dim] - aux_paddings: [aux_time, aux_batch] - source_segment_id: [source_time, source_batch] - aux_segment_id: [aux_time, aux_batch] - **kwargs: Can be optional params for the attention layer, eg. attention - projection index tensor. - - Returns: - The attention context vector, [source_time, source_batch, dim]. - - The attention probability vector, [source_time, source_batch, source_time] - if has_aux_atten is False, otherwise [source_time, source_batch, - aux_time]. - """ - p = self.params - if p.packed_input: - assert source_segment_id is not None, ('Need to specify segment id for ' - 'packed input.') - - with tf.name_scope('self_atten'): - atten_vec, atten_prob = self.self_atten.FProp( - theta.self_atten, - source_vecs, - source_paddings, - query_segment_id=source_segment_id) - - if p.has_aux_atten: - assert aux_vecs is not None - assert aux_paddings is not None - with tf.name_scope('aux_atten'): - atten_vec, atten_prob = self.atten.FProp(theta.atten, atten_vec, - aux_paddings, aux_vecs, - source_segment_id, - aux_segment_id, **kwargs) - - with tf.name_scope('fflayer'): - h = self.fflayer.FProp(theta.fflayer, atten_vec, source_paddings) - if p.tr_post_ln_tpl: - with tf.name_scope('layer_norm'): - h = self.layer_norm.FProp(theta.layer_norm, h) - return h, atten_prob - - def ExtendStep(self, - theta, - source_vecs, - prefix_states, - aux_vecs=None, - aux_paddings=None, - t=None, - **kwargs): - """Transformer Layer, extend one step in decoding. - - This function is expected to be called during fast decoding of Transformer - models. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: [source_batch, dim]. - prefix_states: dict, containing tensors which are the results of previous - attentions, used for fast decoding. - aux_vecs: [aux_time, aux_batch, dim] - aux_paddings: [aux_time, aux_batch] - t: a scalar, the current time step, 0-based. - **kwargs: Can be optional params for the attention layer, eg. attention - projection index tensor. - - Returns: - The attention context vector, [target_batch, source_dim] - - The attention probability vector, [source_time, target_batch] - - Updated prefix states - """ - p = self.params - - if p.has_aux_atten: - assert aux_vecs is not None - assert aux_paddings is not None - - batch_size = py_utils.GetShape(source_vecs)[0] - - # First the self-attention layer. - atten_vec, atten_prob, new_states = self.self_atten.ExtendStep( - theta.self_atten, source_vecs, prefix_states, t) - - atten_vec = tf.expand_dims(atten_vec, axis=0) - # Next the source attention layer. - if p.has_aux_atten: - atten_vec, atten_prob = self.atten.FProp(theta.atten, atten_vec, - aux_paddings, aux_vecs, **kwargs) - - # Finally, the feedforward layer. - h = self.fflayer.FProp( - theta.fflayer, atten_vec, - tf.zeros([1, batch_size], dtype=py_utils.FPropDtype(p))) - if p.tr_post_ln_tpl: - h = self.layer_norm.FProp(theta.layer_norm, h) - h = tf.squeeze(h, 0) - return h, atten_prob, new_states - - -class EvolvedTransformerEncoderBranchedConvsLayer(base_layer.BaseLayer): - """Evolved Transformer encoder branched convolutions layer. - - This constructs the branched convolution portion of the Evolved Transformer - encoder described in https://arxiv.org/abs/1901.11117 . - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params') - p.Define('input_dim', 0, 'Dimension of the layer input.') - p.Define('activation', 'RELU', - 'Activation applied after the left and right branches.') - p.Define('dropout_tpl', layers.DropoutLayer.Params(), - 'Dropout applied to each layer output.') - p.Define('dense_tpl', layers.FCLayer.Params(), - 'Fully connected "dense" layer.') - p.Define('conv_tpl', layers.Conv2DLayer.Params(), - 'Standard convolution layer.') - p.Define('separable_conv_tpl', layers.SeparableConv2DLayer.Params(), - 'Separable convolution layer.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.input_dim - - # Initialize first layer norm. - params = p.ln_tpl.Copy() - params.name = 'first_layer_norm' - params.input_dim = p.input_dim - self.CreateChild('first_layer_norm', params) - - # Initialize second layer norm. - params = p.ln_tpl.Copy() - params.name = 'second_layer_norm' - params.input_dim = p.input_dim * 4 - self.CreateChild('second_layer_norm', params) - - # Initialize dense layer. - params = p.dense_tpl.Copy() - params.name = 'dense_layer' - params.input_dim = p.input_dim - params.activation = p.activation - params.output_dim = p.input_dim * 4 - self.CreateChild('dense_layer', params) - - # Initialize standard conv. - params = p.conv_tpl.Copy() - params.name = 'conv_layer' - params.bias = True - params.batch_norm = False - params.activation = p.activation - params.filter_stride = (1, 1) - params.filter_shape = (3, 1, p.input_dim, int(p.input_dim / 2)) - self.CreateChild('conv_layer', params) - - # Initialize separable conv. - params = p.separable_conv_tpl.Copy() - params.name = 'separable_conv_layer' - params.bias = True - params.batch_norm = False - params.activation = 'NONE' - params.filter_stride = (1, 1) - params.filter_shape = (9, 1, int(p.input_dim * 4), p.input_dim) - self.CreateChild('separable_conv_layer', params) - - # Initialize dropout. - dropout_tpl = p.dropout_tpl.Copy() - self.CreateChild('dropout', dropout_tpl) - - def FProp(self, theta, inputs, paddings): - inputs_normalized = self.first_layer_norm.FProp(theta.first_layer_norm, - inputs) - - left_branch = self.dense_layer.FProp(theta.dense_layer, inputs_normalized, - tf.expand_dims(paddings, -1)) - left_branch = self.dropout.FProp(theta.dropout, left_branch) - # Newly computed padding is discarded. - right_branch = self.conv_layer.FProp( - theta.conv_layer, tf.expand_dims(inputs_normalized, axis=2), - paddings)[0] - right_branch = tf.squeeze(right_branch, axis=2) - right_branch = self.dropout.FProp(theta.dropout, right_branch) - right_branch = tf.pad( - right_branch, - [[0, 0], [0, 0], - [0, tf.shape(left_branch)[-1] - tf.shape(right_branch)[-1]]], - constant_values=0) - - hidden_state = left_branch + right_branch - - hidden_state = self.second_layer_norm.FProp(theta.second_layer_norm, - hidden_state) - # Newly computed padding is discarded. - hidden_state = self.separable_conv_layer.FProp( - theta.separable_conv_layer, tf.expand_dims(hidden_state, axis=2), - paddings)[0] - hidden_state = tf.squeeze(hidden_state, axis=2) - hidden_state = tf.pad( - hidden_state, [[0, 0], [0, 0], - [0, tf.shape(inputs)[-1] - tf.shape(hidden_state)[-1]]], - constant_values=0) - hidden_state = self.dropout.FProp(theta.dropout, hidden_state) - hidden_state += inputs - - return hidden_state - - -class EvolvedTransformerDecoderBranchedConvsLayer(base_layer.BaseLayer): - """Evolved Transformer decoder branched convolutions layer. - - This constructs the branched convolution portion of the Evolved Transformer - decoder described in https://arxiv.org/abs/1901.11117 . - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params') - p.Define('input_dim', 0, 'Dimension of the layer input.') - p.Define('activation', 'RELU', - 'Activation applied to the left convolution branch output.') - p.Define('dropout_tpl', layers.DropoutLayer.Params(), - 'Dropout applied to each layer output.') - p.Define('separable_conv_tpl', - layers.SeparableConv2DLayer.Params().Set(causal_convolution=True), - 'Separable convolution layer.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.input_dim - - # Initialize first layer norm. - params = p.ln_tpl.Copy() - params.name = 'first_layer_norm' - params.input_dim = p.input_dim - self.CreateChild('first_layer_norm', params) - - # Initialize second layer norm. - params = p.ln_tpl.Copy() - params.name = 'second_layer_norm' - params.input_dim = p.input_dim * 2 - self.CreateChild('second_layer_norm', params) - - # Initialize separable conv. - params = p.separable_conv_tpl.Copy() - params.name = 'separable_conv_11x1_layer' - params.bias = True - params.batch_norm = False - params.activation = p.activation - params.filter_stride = (1, 1) - params.filter_shape = (11, 1, p.input_dim, int(p.input_dim * 2)) - self.CreateChild('separable_conv_11x1_layer', params) - - # Initialize first separable conv. - params = p.separable_conv_tpl.Copy() - params.name = 'separable_conv_7x1_layer' - params.bias = True - params.batch_norm = False - params.activation = 'NONE' - params.filter_stride = (1, 1) - params.filter_shape = (7, 1, p.input_dim, int(p.input_dim / 2)) - self.CreateChild('separable_conv_7x1_layer', params) - - # Initialize second separable conv. - params = p.separable_conv_tpl.Copy() - params.name = 'separable_conv_7x1_layer_2' - params.bias = True - params.batch_norm = False - params.activation = 'NONE' - params.filter_stride = (1, 1) - params.filter_shape = (7, 1, int(p.input_dim * 2), p.input_dim) - self.CreateChild('separable_conv_7x1_layer_2', params) - - # Initialize dropout. - dropout_tpl = p.dropout_tpl.Copy() - self.CreateChild('dropout', dropout_tpl) - - def FProp(self, theta, inputs, paddings): - inputs_normalized = self.first_layer_norm.FProp(theta.first_layer_norm, - inputs) - - left_branch = self.separable_conv_11x1_layer.FProp( - theta.separable_conv_11x1_layer, - tf.expand_dims(inputs_normalized, axis=2), paddings)[0] - left_branch = self.dropout.FProp(theta.dropout, left_branch) - - right_branch = self.separable_conv_7x1_layer.FProp( - theta.separable_conv_7x1_layer, tf.expand_dims( - inputs_normalized, axis=2), paddings)[0] - right_branch = self.dropout.FProp(theta.dropout, right_branch) - right_branch = tf.pad( - right_branch, - [[0, 0], [0, 0], [0, 0], - [0, tf.shape(left_branch)[-1] - tf.shape(right_branch)[-1]]], - constant_values=0) - - hidden_state = left_branch + right_branch - hidden_state = self.second_layer_norm.FProp(theta.second_layer_norm, - hidden_state) - - hidden_state = self.separable_conv_7x1_layer_2.FProp( - theta.separable_conv_7x1_layer_2, hidden_state, paddings)[0] - hidden_state = self.dropout.FProp(theta.dropout, hidden_state) - - hidden_state = tf.squeeze(hidden_state, axis=2) - return hidden_state + inputs - - -class EvolvedTransformerBaseLayer(base_layer.BaseLayer): - """Base layer for the Evolved Transformer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('source_dim', 0, 'Dimension of the transformer block input.') - p.Define( - 'has_aux_atten', False, - 'If set, introduces a second attention layer, which attends to' - ' the auxiliary source contexts.') - p.Define('packed_input', False, - 'If True, each training example may pack multiple sequences.') - return p - - -class EvolvedTransformerEncoderLayer(EvolvedTransformerBaseLayer): - """Evolved Transformer encoder layer. - - An Evolved Transformer encoder layer as described in - https://arxiv.org/abs/1901.11117 . - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('glu_tpl', layers.GluLayer.Params(), 'Glu layer.') - p.Define('branched_convs_tpl', - EvolvedTransformerEncoderBranchedConvsLayer.Params(), - 'Evolved Transformer branched convolutional layers.') - p.Define('transformer_tpl', TransformerLayer.Params(), 'Transformer layer.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.source_dim - # Auxiliary attention not supported. - if p.has_aux_atten: - raise ValueError('Auxiliary attention not supported.') - - # Initialize Glu layer. - params = p.glu_tpl.Copy() - params.name = 'glu_layer' - params.input_dim = p.source_dim - self.CreateChild('glu_layer', params) - - # Initialize branched convolutions layer. - params = p.branched_convs_tpl.Copy() - params.name = 'branched_convs_layer' - params.input_dim = p.source_dim - self.CreateChild('branched_convs_layer', params) - - # Initialize branched convolutional layers. - params = p.transformer_tpl.Copy() - params.name = 'transformer_layer' - params.source_dim = p.source_dim - params.output_dim = p.source_dim - params.tr_fflayer_tpl.hidden_dim = 4 * p.source_dim - # Decoder functionality is not supported so disable auxiliary attention. - params.has_aux_atten = False - params.tr_aux_atten_tpl = None - params.mask_self_atten = False - params.is_decoder = False - params.packed_input = p.packed_input - self.CreateChild('transformer_layer', params) - - def FProp(self, - theta, - source_vecs, - source_paddings, - aux_vecs=None, - aux_paddings=None, - source_segment_id=None, - aux_segment_id=None): - hidden_state = self.glu_layer.FProp(theta.glu_layer, source_vecs, - source_paddings) - - hidden_state = tf.transpose(hidden_state, [1, 0, 2]) - source_paddings = tf.transpose(source_paddings, [1, 0]) - hidden_state = self.branched_convs_layer.FProp( - theta.branched_convs_layer, hidden_state, source_paddings) - hidden_state = tf.transpose(hidden_state, [1, 0, 2]) - source_paddings = tf.transpose(source_paddings, [1, 0]) - - hidden_state, atten_prob = self.transformer_layer.FProp( - theta.transformer_layer, hidden_state, source_paddings, aux_vecs, - aux_paddings, source_segment_id, aux_segment_id) - - return hidden_state, atten_prob - - -class EvolvedTransformerDecoderLayer(EvolvedTransformerBaseLayer): - """Evolved Transformer decoder layer. - - An Evolved Transformer decoder layer as described in - https://arxiv.org/abs/1901.11117 . - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('tr_atten_tpl', - TransformerAttentionLayer.Params().Set(num_attention_heads=8), - 'Transformer attention layer params.') - p.Define('tr_double_heads_atten_tpl', - TransformerAttentionLayer.Params().Set(num_attention_heads=16), - 'Transformer double heads attention layer params.') - p.Define('branched_convs_tpl', - EvolvedTransformerDecoderBranchedConvsLayer.Params(), - 'Evolved Transformer branched convolutional layers.') - p.Define('transformer_tpl', TransformerLayer.Params(), 'Transformer layer.') - p.Define('tr_aux_atten_tpl', None, 'Transformer Attention Layer params.') - p.Define('mask_self_atten', False, 'If True, use masked self-attention.') - p.has_aux_atten = True - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.source_dim - - # Initialize multi-headed self-attention. - params = p.tr_double_heads_atten_tpl.Copy() - params.name = 'self_atten_double_heads' - params.source_dim = p.source_dim - params.is_masked = p.mask_self_atten - # Packed input is not supported. - params.packed_input = p.packed_input - self.CreateChild('self_atten_double_heads', params) - - if p.has_aux_atten: - # Initialize masked-multi-headed encoder attention. - params = ( - p.tr_aux_atten_tpl.Copy() - if p.tr_aux_atten_tpl is not None else p.tr_atten_tpl.Copy()) - params.name = 'attend_to_encoder' - params.source_dim = p.source_dim - # Packed input is not supported. - params.packed_input = p.packed_input - self.CreateChild('attend_to_encoder', params) - - # Initialize branched convolutional layers. - params = p.branched_convs_tpl.Copy() - params.name = 'branched_convs' - params.input_dim = p.source_dim - self.CreateChild('branched_convs', params) - - # Initialize transformer layer. - params = p.transformer_tpl.Copy() - params.name = 'transformer_layer' - params.source_dim = p.source_dim - params.output_dim = p.source_dim - params.tr_fflayer_tpl.hidden_dim = 4 * p.source_dim - params.tr_aux_atten_tpl = p.tr_aux_atten_tpl - params.has_aux_atten = p.has_aux_atten - params.mask_self_atten = p.mask_self_atten - params.tr_fflayer_tpl.activation = 'SWISH' - # Packed input is not supported. - params.packed_input = p.packed_input - self.CreateChild('transformer_layer', params) - - def FProp(self, - theta, - source_vecs, - source_paddings, - aux_vecs=None, - aux_paddings=None, - source_segment_id=None, - aux_segment_id=None): - p = self.params - - if p.has_aux_atten: - assert aux_vecs is not None - assert aux_paddings is not None - - with tf.name_scope('self_atten_double_heads'): - left_branch, _ = self.self_atten_double_heads.FProp( - theta.self_atten_double_heads, - source_vecs, - source_paddings, - query_segment_id=source_segment_id) - - if p.has_aux_atten: - with tf.name_scope('attend_to_encoder'): - right_branch, _ = self.attend_to_encoder.FProp( - theta.attend_to_encoder, source_vecs, aux_paddings, aux_vecs, - source_segment_id, aux_segment_id) - - hidden_state = left_branch + right_branch + source_vecs - else: - hidden_state = left_branch + source_vecs - - hidden_state = tf.transpose(hidden_state, [1, 0, 2]) - source_paddings = tf.transpose(source_paddings, [1, 0]) - hidden_state = self.branched_convs.FProp(theta.branched_convs, hidden_state, - source_paddings) - hidden_state = tf.transpose(hidden_state, [1, 0, 2]) - source_paddings = tf.transpose(source_paddings, [1, 0]) - - hidden_state, atten_prob = self.transformer_layer.FProp( - theta.transformer_layer, hidden_state, source_paddings, aux_vecs, - aux_paddings, source_segment_id, aux_segment_id) - - return hidden_state, atten_prob - - def ExtendStep(self, - theta, - source_vecs, - prefix_states, - aux_vecs=None, - aux_paddings=None, - t=None): - """Evolved Transformer decoder layer, extended one step in decoding. - - This function is expected to be called during fast decoding of Evolved - Transformer models. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: [source_batch, dim]. - prefix_states: dict, containing tensors which are the results of previous - attentions, used for fast decoding. - aux_vecs: [aux_time, aux_batch, dim] - aux_paddings: [aux_time, aux_batch] - t: a scalar, the current time step, 0-based. - - Returns: - The attention context vector, [target_batch, source_dim]. - - The attention probability vector, [source_time, target_batch]. - - Updated prefix states. - """ - p = self.params - - if p.has_aux_atten: - assert aux_vecs is not None - assert aux_paddings is not None - - inputs = tf.expand_dims(source_vecs, axis=0) - new_states = prefix_states - - double_head_attention_states = prefix_states.double_head_attention_states - # First the self-attention layer. - (left_branch, _, - double_head_attention_states) = self.self_atten_double_heads.ExtendStep( - theta.self_atten_double_heads, source_vecs, - double_head_attention_states, t) - new_states.double_head_attention_states = double_head_attention_states - left_branch = tf.expand_dims(left_branch, axis=0) - - hidden_state = left_branch + inputs - - # Next the source attention layer. - if p.has_aux_atten: - hidden_state += self.attend_to_encoder.FProp( - theta.attend_to_encoder, inputs, aux_paddings, aux_vecs)[0] - - branched_convs_input = prefix_states.branched_convs_input - branched_convs_input = tf.concat([branched_convs_input, hidden_state], - axis=0) - new_states.branched_convs_input = branched_convs_input - # The receptive field of the branched convs is 17 and so we do not need - # to consider inputs that come before that to compute the final position. - # TODO(davidso): Create an ExtendStep method for branched_convs to make this - # more efficient. - inputs_length = tf.minimum(tf.shape(branched_convs_input)[0], 17) - branched_convs_input = branched_convs_input[-inputs_length:, :, :] - branched_convs_input = tf.transpose(branched_convs_input, [1, 0, 2]) - hidden_state = self.branched_convs.FProp(theta.branched_convs, - branched_convs_input, None) - hidden_state = tf.transpose(hidden_state, [1, 0, 2]) - - transformer_layer_input = tf.squeeze(hidden_state[-1, :, :]) - transformer_layer_states = prefix_states.transformer_layer_states - (hidden_state, atten_prob, - transformer_layer_states) = self.transformer_layer.ExtendStep( - theta.transformer_layer, - transformer_layer_input, - transformer_layer_states, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - t=t) - - new_states.transformer_layer_states = transformer_layer_states - - return hidden_state, atten_prob, new_states - - -class StyleLayer(base_layer.BaseLayer): - """A layer that performs weighted style emb lookup.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 0, 'Dimension of the input.') - p.Define('output_dim', 0, 'Dimension of the output.') - p.Define('num_styles', 0, 'Num of styles.') - p.Define('num_heads', 4, 'Number of attention heads.') - p.Define( - 'enable_ctx_post_proj', True, - 'If True, computed context is post projected into' - ' ctx_post_proj_dim.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.num_styles > 0 - assert p.input_dim > 0 - assert p.output_dim > 0 - - atten_p = attention.MultiHeadedAttention.Params().Set( - source_dim=p.output_dim, - context_dim=p.output_dim, - hidden_dim=p.output_dim, - query_dim=p.input_dim, - ctx_post_proj_dim=p.output_dim, - num_attention_heads=p.num_heads, - use_source_vec_as_attention_value=False, - enable_ctx_post_proj=p.enable_ctx_post_proj) - self.CreateChild('atten', atten_p) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - # The styles table. - w_shape = [p.num_styles, 1, p.output_dim] - w_init = py_utils.WeightInit.Gaussian(scale=1.0, seed=p.random_seed) - w_pc = py_utils.WeightParams( - shape=w_shape, - init=w_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('styles_w', w_pc) - - def EmbLookup(self, theta, ids): - """Looks up style embedding vectors for ids only for test purpose. - - Args: - theta: Named tuple with the weight matrix for the embedding. - ids: A rank-N int32 tensor. - - Returns: - embs, A rank-(N+1) params.dtype tensor. - embs[indices, :] is the embedding vector for ids[indices]. - """ - p = self.params - # TODO(ngyuzh): call this function for virsualize big discrete table, - # e.g. num_styles > 2^10. - embs = tf.nn.embedding_lookup(theta.styles_w, tf.reshape(ids, [-1])) - out_shape = tf.concat([tf.shape(ids), [p.output_dim]], 0) - return tf.reshape(tf.nn.tanh(embs), out_shape) - - def StyleEmbFromProbs(self, theta, inp): - """Look up style embedding based on feedin probabilities. - - Args: - theta: params for this layer and its sub-layers. - inp: attention probabilities of shape [batch_size, num_styles]. - - Returns: - style_emb - weighted combined style embedding based on inp. - """ - p = self.params - b_size = tf.shape(inp)[0] - styles_w = tf.tile(tf.nn.tanh(theta.styles_w), [1, b_size, 1]) - styles_paddings = tf.zeros([p.num_styles, b_size], - dtype=py_utils.FPropDtype(p)) - atten_probs = tf.tile(tf.expand_dims(inp, 1), [1, p.num_heads, 1]) - atten_probs = tf.reshape(atten_probs, [-1, p.num_styles]) - packed_src = self.atten.InitForSourcePacked(theta.atten, styles_w, styles_w, - styles_paddings) - style_emb, _ = self.atten.ComputeContextVectorWithAttenProbs( - theta.atten, packed_src.source_contexts, atten_probs) - return style_emb - - def FProp(self, theta, inp): - """Look up style embedding.""" - - p = self.params - b_size = tf.shape(inp)[0] - styles_w = tf.tile(tf.nn.tanh(theta.styles_w), [1, b_size, 1]) - styles_paddings = tf.zeros([p.num_styles, b_size], - dtype=py_utils.FPropDtype(p)) - packed_src = self.atten.InitForSourcePacked(theta.atten, styles_w, styles_w, - styles_paddings) - style_emb, probs, _ = self.atten.ComputeContextVectorWithSource( - theta.atten, packed_src, inp) - # TODO(yonghui): Extract and return the attention probabilities. - return style_emb, probs - - -class TransformerLayerWithMultitaskAdapters(TransformerLayer): - """Transformer Layer with multitask residual adapters. - - Applies transformer layer, followed by multitask adapters. Requires an - additional input specifying the task_id for each input. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('adapter_tpl', layers.MultitaskAdapterLayer.Params(), - 'Template to use for multitask adapters.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - params = p.adapter_tpl.Copy() - params.name = 'adapters' - self.CreateChild('adapters', params) - - def FProp(self, - theta, - source_vecs, - source_paddings, - aux_vecs=None, - aux_paddings=None, - source_segment_id=None, - aux_segment_id=None, - source_task_id=None): - """Transformer Layer with multitask adapters. - - First applies the standard transformer layer. Then applies adapter layers. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: [source_time, source_batch, dim]. - source_paddings: [source_time, source_batch] - aux_vecs: [aux_time, aux_batch, dim] - aux_paddings: [aux_time, aux_batch] - source_segment_id: [source_time, source_batch] - aux_segment_id: [aux_time, aux_batch] - source_task_id: [source_time, source_batch] - - Returns: - The attention context vector, [source_time, source_batch, dim]. - - The attention probability vector, [source_time, source_batch, source_time] - if has_aux_atten is False, otherwise [source_time, source_batch, - aux_time]. - """ - p = self.params - hidden, atten_prob = super().FProp(theta, source_vecs, source_paddings, - aux_vecs, aux_paddings, - source_segment_id, aux_segment_id) - # Assumes the same task_id for the entire sequence during eval or when - # not using packed_input. - if not p.packed_input and not self.do_eval: - source_task_id = source_task_id[0, :] - hidden = self.adapters.FProp(theta.adapters, hidden, source_task_id) - return hidden, atten_prob - - def ExtendStep(self, - theta, - source_vecs, - prefix_states, - aux_vecs=None, - aux_paddings=None, - timestep=None, - source_task_id=None): - """Transformer Layer with adapters, extend one step in decoding. - - Applies TransformerLayer.ExtendStep, then applies adapters. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: [source_batch, dim]. - prefix_states: dict, containing tensors which are the results of previous - attentions, used for fast decoding. - aux_vecs: [aux_time, aux_batch, dim] - aux_paddings: [aux_time, aux_batch] - timestep: a scalar, the current time step, 0-based. - source_task_id: [source_batch] - - Returns: - The attention context vector, [target_batch, source_dim] - - The attention probability vector, [source_time, target_batch] - - Updated prefix states - """ - p = self.params - - if p.has_aux_atten: - assert aux_vecs is not None - assert aux_paddings is not None - - batch_size = tf.shape(source_vecs)[0] - - # First the self-attention layer. - atten_vec, atten_prob, new_states = self.self_atten.ExtendStep( - theta.self_atten, source_vecs, prefix_states, timestep) - - atten_vec = tf.expand_dims(atten_vec, axis=0) - # Next the source attention layer. - if p.has_aux_atten: - atten_vec, atten_prob = self.atten.FProp(theta.atten, atten_vec, - aux_paddings, aux_vecs) - - # Finally, the feedforward layer. - hidden = self.fflayer.FProp( - theta.fflayer, atten_vec, - tf.zeros([1, batch_size], dtype=py_utils.FPropDtype(p))) - - # Now adapter layers. - hidden = self.adapters.FProp(theta.adapters, hidden, source_task_id) - - hidden = tf.squeeze(hidden, 0) - return hidden, atten_prob, new_states - - -# TODO(ankurbpn): Implementation is slightly different from the original. -# In the original implementation the KV projection outputs were explicitly -# zeroed out by the gating networks. Here we control the inputs instead. -# Verify if this still works as well as the original implementation. -class CCTAttentionLayer(base_layer.BaseLayer): - """Multi-headed attention, add and norm used by 'Attention Is All You Need'. - - Supports CCT attention gating as in the paper here: - https://arxiv.org/abs/2002.07106 - """ - - @classmethod - def Params(cls): - p = super().Params() - - # Transformer Attention params. - p.Define('source_dim', 0, 'Dimension of the transformer block input.') - p.Define('context_dim', 0, 'Dimension of the attention contexts.') - p.Define('atten_hidden_dim', 0, 'Dimension of the attention hidden dim.') - p.Define('num_attention_heads', 8, 'Number of attention heads.') - p.Define('is_masked', False, 'If set, uses masked MultiHeadedAttention.') - p.Define( - 'mask_type', 'future', 'Type of attention mask if `is_masked` is' - 'set. Either "future" for masking out attention to future' - 'positions or "eye" for masking out the token itself.') - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params') - p.Define( - 'atten_tpl', - attention.MultiHeadedAttention.Params().Set( - use_source_vec_as_attention_value=False, enable_ctx_post_proj=True), - 'Multi-Headed Dot-Attention default params') - p.Define( - 'atten_dropout_prob', 0.0, - 'Probability at which we apply dropout to the attention probs. ' - 'This practically drops memory values at random positions.') - p.Define( - 'residual_dropout_prob', 0.0, - 'Probability at which we apply dropout to the residual layers, ' - 'such that, residual(x, y) = (x + dropout(y)).') - p.Define( - 'residual_dropout_tpl', layers.DropoutLayer.Params(), - 'Residual dropout params template. keep_prop will be reset to ' - '(1.0 - residual_dropout_prob).') - p.Define('packed_input', False, - 'If True, each training example may pack multiple sequences.') - p.Define('add_unnormalized_input', False, 'If set, uses unnormalized input ' - 'in the residual add.') - - # CCT params. - p.Define('gating_tpl', layers.CCTGatingNetwork.Params(), '') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.source_dim - - if not p.atten_hidden_dim: - p.atten_hidden_dim = p.source_dim - - if not p.context_dim: - p.context_dim = p.source_dim - - if p.is_masked: - assert p.mask_type in ['future', 'eye'] - - # Initialize multi-headed attention - params = p.atten_tpl.Copy() - params.name = 'multihead_atten' - params.source_dim = p.source_dim - params.query_dim = p.source_dim - params.hidden_dim = p.atten_hidden_dim - params.context_dim = p.context_dim - params.ctx_post_proj_dim = p.source_dim - params.num_attention_heads = p.num_attention_heads - params.atten_dropout_prob = p.atten_dropout_prob - params.packed_input = p.packed_input - self.CreateChild('atten', params) - - dropout_tpl = p.residual_dropout_tpl.Copy() - dropout_tpl.keep_prob = (1.0 - p.residual_dropout_prob) - self.CreateChild('residual_dropout', dropout_tpl) - - # Initialize attention layer norm - params = p.ln_tpl.Copy() - params.name = 'atten_ln' - params.input_dim = p.source_dim - self.CreateChild('layer_norm', params) - - # CCT specific operations. - ff_gating = p.gating_tpl.Copy() - ff_gating.input_dim = p.source_dim - ff_gating.num_outputs = 1 - ff_gating.name = 'query_gating_net' - self.CreateChild('query_gating', ff_gating) - - ff_gating = p.gating_tpl.Copy() - ff_gating.input_dim = p.source_dim - ff_gating.num_outputs = 1 - ff_gating.name = 'kv_gating_net' - self.CreateChild('kv_gating', ff_gating) - - # Initialize source_vec layer norm - params = p.ln_tpl.Copy() - params.name = 'source_ln' - params.input_dim = p.source_dim - self.CreateChild('source_layer_norm', params) - - # Initialize ctx_vec layer norm - params = p.ln_tpl.Copy() - params.name = 'ctx_ln' - params.input_dim = p.source_dim - self.CreateChild('ctx_layer_norm', params) - - def FProp(self, - theta, - query_vec, - source_paddings, - source_vecs=None, - query_segment_id=None, - source_segment_id=None, - **kwargs): - """CCT attention, residual and normalization layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_time, target_batch, dim] - source_paddings: [source_time, source_batch] - source_vecs: [source_time, source_batch, dim]. - query_segment_id: [target_time, target_batch] - source_segment_id: [source_time, source_batch] - **kwargs: Can be optional params for the attention layer, eg. attention - projection index tensor. - - Returns: - (output, atten_probs). output is of shape [target_time, target_batch, - context_dim], atten_probs is of shape [target_time, target_batch, - source_time]. - """ - p = self.params - unnormalized_query_vec = query_vec - query_vec = self.layer_norm.FProp(theta.layer_norm, query_vec) - - if source_vecs is None: # For self-attention: keys = queries. - source_vecs = query_vec - source_segment_id = query_segment_id - else: - source_vecs = self.source_layer_norm.FProp(theta.source_layer_norm, - source_vecs) - - # Gating the query computation. - query_p_c = self.query_gating.FProp(theta.query_gating, query_vec) - source_p_c = self.kv_gating.FProp(theta.kv_gating, source_vecs) - source_vecs *= source_p_c # Gate the source vectors. - - if p.is_masked: - assert source_vecs is not None - query_vec = py_utils.with_dependencies([ - py_utils.assert_shape_match( - tf.shape(source_vecs), tf.shape(query_vec)) - ], query_vec) - # Prepares mask for self-attention - # [time, time] - target_time = tf.shape(query_vec)[0] - target_bs = tf.shape(query_vec)[1] - - if p.mask_type == 'future': - padding = 1.0 - tf.linalg.band_part( - tf.ones([target_time, target_time], dtype=py_utils.FPropDtype(p)), - -1, 0) - elif p.mask_type == 'eye': - padding = tf.eye(target_time, target_time, dtype=py_utils.FPropDtype(p)) - - # [time, batch, time] - causal_padding = tf.tile(tf.expand_dims(padding, 1), [1, target_bs, 1]) - - causal_padding = tf.reshape(causal_padding, [-1, target_time]) - else: - causal_padding = None - - query_dim = tf.shape(query_vec)[-1] - - # Projects keys and values. - packed_src = self.atten.PackSource( - theta=theta.atten, - source_vecs=source_vecs, # keys - source_contexts=source_vecs, # values - source_padding=source_paddings, - source_segment_id=source_segment_id) - - if query_segment_id is not None: - query_segment_id = tf.reshape(query_segment_id, [-1]) - - ctx_vec, atten_prob, _ = self.atten.ComputeContextVectorWithSource( - theta=theta.atten, - packed_src=packed_src, - query_vec=tf.reshape(query_vec, [-1, query_dim]), - per_step_source_padding=causal_padding, - query_segment_id=query_segment_id, - **kwargs) - - # Gating operations - ctx_vec = query_p_c * tf.reshape( - self.ctx_layer_norm.FProp(theta.ctx_layer_norm, ctx_vec), - tf.shape(query_vec)) - - ctx_vec = self.residual_dropout.FProp(theta.residual_dropout, ctx_vec) - input_to_add = ( - unnormalized_query_vec if p.add_unnormalized_input else query_vec) - h = input_to_add + ctx_vec - atten_prob = tf.reshape(atten_prob, [ - tf.shape(query_vec)[0], - tf.shape(query_vec)[1], - tf.shape(source_vecs)[0] - ]) - return h, atten_prob, query_p_c, source_p_c - - def _FinishExtendStep(self, - theta, - query_vec, - unnormalized_query_vec, - extended_packed_src, - t=None): - """Finish extending prefix by one more time step. - - Isolating this function from ExtendStep allows generalizing self-attention - to causal attention on other inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, dim] - unnormalized_query_vec: [target_batch, dim] - extended_packed_src: A `.NestedMap` object containing source_vecs, - source_contexts, source_paddings, and source_segment_ids - t: a scalar, the current time step, 0-based. - - Returns: - A triplet (cur_output, atten_prob, new_state) where cur_output is a tensor - representing the output from the current state, and new_state is the new - state `.NestedMap`. - """ - p = self.params - # Gating operations - query_p_c = self.query_gating.FProp(theta.query_gating, query_vec) - - if t is not None: - source_seq_len = tf.shape(extended_packed_src.source_vecs)[0] - zero_padding = tf.fill([source_seq_len], - tf.constant(0.0, dtype=query_vec.dtype)) - per_step_source_padding = tf.where( - tf.less(tf.range(source_seq_len), tf.fill([source_seq_len], t + 1)), - zero_padding, tf.ones_like(zero_padding, dtype=query_vec.dtype)) - query_batch_size = tf.shape(query_vec)[0] - per_step_source_padding = tf.tile( - tf.expand_dims(per_step_source_padding, axis=0), - [query_batch_size, 1]) - else: - per_step_source_padding = None - ctx_vec, atten_prob, _ = self.atten.ComputeContextVectorWithCachedSource( - theta.atten, - extended_packed_src, - query_vec, - per_step_source_padding=per_step_source_padding) - - # Gating operations - ctx_vec = self.ctx_layer_norm.FProp(theta.ctx_layer_norm, ctx_vec) - ctx_vec = query_p_c * tf.reshape(ctx_vec, tf.shape(query_vec)) - ctx_vec = self.residual_dropout.FProp(theta.residual_dropout, ctx_vec) - input_to_add = ( - unnormalized_query_vec if p.add_unnormalized_input else query_vec) - h = input_to_add + ctx_vec - - new_states = py_utils.NestedMap( - key=extended_packed_src.source_vecs, - value=extended_packed_src.source_contexts) - return h, atten_prob, new_states - - def ExtendStep(self, theta, query_vec, prefix_state, t=None): - """Extend prefix by one more time step. - - This function is expected to be called during fast decoding of the - Transformer model. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - query_vec: [target_batch, dim] - prefix_state: dict, containing tensors which are the results of previous - attentions, used for fast decoding. - t: a scalar, the current time step, 0-based. - - Returns: - A triplet (cur_output, atten_prob, new_state) where cur_output is a tensor - representing the output from the current state, and new_state is the new - state `.NestedMap`. - """ - p = self.params - assert p.is_masked # Must be causal attention. - - # Gating operations - unnormalized_query_vec = query_vec - query_vec = self.layer_norm.FProp(theta.layer_norm, query_vec) - source_p_c = self.kv_gating.FProp(theta.kv_gating, query_vec) - source_vec = source_p_c * query_vec - - cached_packed_src = py_utils.NestedMap( - source_vecs=prefix_state.key, - source_contexts=prefix_state.value, - source_padding=None, - source_segment_id=None) - extended_packed_src = self.atten.ExtendSourcePacked(theta.atten, source_vec, - source_vec, None, None, - cached_packed_src, t) - return self._FinishExtendStep(theta, query_vec, unnormalized_query_vec, - extended_packed_src, t) - - -class CCTFeedForwardLayer(base_layer.BaseLayer): - """Transformer FF layer with CCT gating. - - https://arxiv.org/abs/2002.07106 - - Differences from standard Transformer FF layer: - 1. Each feedforward layer is divided into num_blocks smaller layers (divided - along the hidden dimension). - 2. Each block has its separate input layer norm. - 3. Each block has its separate output layer norm. - 4. Outputs from each block are gated with CCTGatingNetwork output - which is - between 0 and 1 for training and either 0 or 1 during inference. - """ - - @classmethod - def Params(cls): - p = super().Params() - # Transformer Feedforward params. - p.Define('input_dim', 0, 'Dimension of the layer input.') - p.Define('output_dim', 0, 'Dimension of the layer output.') # Deprecated. - p.Define('hidden_dim', 0, 'Dimension of the hidden layer.') - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params') - p.Define('activation', 'RELU', 'Non-linearity.') - p.Define('fflayer_tpl', - layers.FeedForwardNet.Params().Set(activation=['RELU', 'NONE']), - 'Feed forward layer default params') - p.Define( - 'res_proj_tpl', layers.ProjectionLayer.Params(), - 'Residual projection default params, used when input_dim != ' - 'output_dim.') - p.Define( - 'residual_dropout_prob', 0.0, - 'Probability at which we apply dropout to the residual layers, ' - 'such that, residual(x, y) = (x + dropout(y)).') - p.Define( - 'residual_dropout_tpl', layers.DropoutLayer.Params(), - 'Residual dropout params template. keep_prop will be reset to ' - '(1.0 - residual_dropout_prob).') - p.Define( - 'relu_dropout_prob', 0.0, - 'Probability at which we apply dropout to the hidden layer ' - 'of feed-forward network.') - - # Expert params. - p.Define('num_blocks', 1, 'Number of separately gated ff blocks.') - p.Define('gating_tpl', layers.CCTGatingNetwork.Params(), 'gating template.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - assert p.input_dim - assert p.hidden_dim - assert not p.output_dim, 'output_dim should not be set.' - - # Initialize feed-forward layer - params = p.fflayer_tpl.Copy() - params.name = 'fflayer' - params.input_dim = p.input_dim - params.activation = [p.activation, 'NONE'] - if p.output_dim == 0: - params.hidden_layer_dims = [p.hidden_dim, p.input_dim] - else: - params.hidden_layer_dims = [p.hidden_dim, p.output_dim] - - params.dropout = [ - params.dropout.cls.Params().Set(keep_prob=1.0 - p.relu_dropout_prob), - params.dropout.cls.Params().Set(keep_prob=1.0) - ] - - ffs = [] - ln_params = [] - out_layer_norm = [] # Required for stabilizing CCT. - for i in range(p.num_blocks): - ff_p = params.Copy() - ff_p.name += '_%d' % i - ffs.append(ff_p) - - ln_p = p.ln_tpl.Copy() - ln_p.name = 'fflayer_ln_%d' % i - ln_p.input_dim = p.input_dim - ln_params.append(ln_p) - - ln_p = p.ln_tpl.Copy() - ln_p.name = 'fflayer_ln_out_%d' % i - ln_p.input_dim = p.input_dim - out_layer_norm.append(ln_p) - self.CreateChildren('fflayers', ffs) - self.CreateChildren('layer_norm', ln_params) - self.CreateChildren('out_layer_norm', out_layer_norm) - - # Note: Set gating noise and warmup in parent layer. - ff_gating = p.gating_tpl.Copy() - ff_gating.input_dim = p.input_dim - ff_gating.num_outputs = p.num_blocks - ff_gating.name = 'gating_net' - self.CreateChild('ff_gating', ff_gating) - - dropout_tpl = p.residual_dropout_tpl.Copy() - dropout_tpl.keep_prob = (1.0 - p.residual_dropout_prob) - self.CreateChild('residual_dropout', dropout_tpl) - - def FProp(self, theta, inputs, paddings): - """Feed-forward, layer-norm, residual, gating and layer-norm. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: [time, batch, dim]. - paddings: [time, batch] - - Returns: - tensor of the same shape with inputs - """ - p = self.params - ff_outputs = [] - for i in range(p.num_blocks): - inputs_normalized = self.layer_norm[i].FProp(theta.layer_norm[i], inputs) - ff_output = self.fflayers[i].FProp( - theta.fflayers[i], - inputs_normalized, - paddings=tf.expand_dims(paddings, -1)) - ff_output = self.out_layer_norm[i].FProp(theta.out_layer_norm[i], - ff_output) - ff_outputs.append(ff_output) - p_c = self.ff_gating.FProp(theta.ff_gating, inputs_normalized) - out = inputs + self.residual_dropout.FProp( - theta.residual_dropout, - tf.reduce_sum( - tf.expand_dims(p_c, -1) * tf.stack(ff_outputs, -2), axis=-2)) - return out, p_c - - -class TransformerWithContextLayer(base_layer.BaseLayer): - """A transformer layer with 3 attention layers. - - The same as layers_with_attention.TransformerLayer, but with an - additional attention layer to attend to a third transformer stack - representing context. - - self-attention => context attention (newly added as tertiary_atten) => - encoder attention (named aux_atten in TransformerLayer). - - The weights are *not* shared between these three attention layers. - - See https://arxiv.org/pdf/1810.03581.pdf - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('source_dim', 0, 'Dimension of the transformer block input.') - p.Define('output_dim', 0, 'Dimension of the transformer block output.') - p.Define( - 'tr_atten_tpl', - TransformerAttentionLayer.Params().Set(num_attention_heads=8), - 'Transformer Attention Layer params. The same template is applied ' - 'to all three attention layers.') - p.Define( - 'tr_tertiary_atten_tpl', None, - 'Transformer Attention Layer params for the tertiary attention. ' - 'When None, copies tr_atten_tpl above.') - p.Define('tr_fflayer_tpl', - TransformerFeedForwardLayer.Params().Set(hidden_dim=2048), - 'Transformer Feed-Forward Layer params.') - p.Define('packed_input', False, - 'If True, each training example may pack multiple sequences.') - # Required params used by the decoder. - p.Define('has_aux_atten', True, 'Must be True.') - p.Define('mask_self_atten', True, 'Must be True.') - # removed: p.num_aux_atten_post_proj, p.tr_post_ln_tpl - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.has_aux_atten - assert p.mask_self_atten - if not p.source_dim: - raise ValueError('p.source_dim not set') - - # Initialize multi-headed self-attention - params = p.tr_atten_tpl.Copy() - params.name = 'multihead_self_atten' - params.source_dim = p.source_dim - params.packed_input = p.packed_input - params.is_masked = True - self.CreateChild('self_atten', params) - - # Initialize tertiary attention. - # If p.tr_tertiary_atten_tpl is None, we fall back to using - # p.tr_tertiary_atten_tpl. - params = p.tr_tertiary_atten_tpl or p.tr_atten_tpl.Copy() - params.name = 'tertiary_multihead_atten' - params.source_dim = p.source_dim - params.packed_input = p.packed_input - self.CreateChild('tertiary_atten', params) - - # Initialize multi-headed encoder attention - params = p.tr_atten_tpl.Copy() - params.name = 'multihead_atten' - params.source_dim = p.source_dim - params.packed_input = p.packed_input - self.CreateChild('atten', params) - - # Initialize feed-forward layer - params = p.tr_fflayer_tpl.Copy() - params.name = 'tr_fflayer' - params.input_dim = p.source_dim - params.output_dim = p.output_dim - self.CreateChild('fflayer', params) - - def FProp(self, - theta, - source_vecs, - source_paddings, - aux_vecs, - aux_paddings, - tertiary_vecs, - tertiary_paddings, - source_segment_id=None, - aux_segment_id=None, - tertiary_segment_id=None, - **kwargs): - """Transformer Layer. - - Please see docstring of TransformerAttentionLayer.FProp. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: [source_time, source_batch, dim]. - source_paddings: [source_time, source_batch] - aux_vecs: [aux_time, aux_batch, dim] - aux_paddings: [aux_time, aux_batch] - tertiary_vecs: [tertiary_time, tertiary_batch, dim] - tertiary_paddings: [tertiary_time, tertiary_batch] - source_segment_id: [source_time, source_batch] - aux_segment_id: [aux_time, aux_batch] - tertiary_segment_id: [tertiary_time, tertiary_batch] - **kwargs: Can be optional params for the attention layer, eg. attention - projection index tensor. - - Returns: - The attention context vector, [source_time, source_batch, dim]. - - The attention probability vector, [source_time, source_batch, aux_time]. - """ - p = self.params - if p.packed_input: - assert source_segment_id is not None, ('Need to specify segment id for ' - 'packed input.') - assert aux_segment_id is not None, ('Need to specify segment id for ' - 'packed input.') - assert tertiary_segment_id is not None, ('Need to specify segment id for ' - 'packed input.') - - atten_vec, atten_prob = self.self_atten.FProp( - theta.self_atten, - source_vecs, - source_paddings, - query_segment_id=source_segment_id) - atten_vec, atten_prob = self.tertiary_atten.FProp( - theta.tertiary_atten, atten_vec, tertiary_paddings, tertiary_vecs, - source_segment_id, tertiary_segment_id) - atten_vec, atten_prob = self.atten.FProp(theta.atten, atten_vec, - aux_paddings, aux_vecs, - source_segment_id, aux_segment_id, - **kwargs) - - h = self.fflayer.FProp(theta.fflayer, atten_vec, source_paddings) - return h, atten_prob - - def ExtendStep(self, - theta, - source_vecs, - prefix_states, - aux_vecs, - aux_paddings, - tertiary_vecs, - tertiary_paddings, - t=None, - **kwargs): - """Transformer Layer, extend one step in decoding. - - Please see docstring of TransformerAttentionLayer.ExtendStep. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_vecs: [source_batch, dim]. - prefix_states: dict, containing tensors which are the results of previous - attentions, used for fast decoding. - aux_vecs: [aux_time, aux_batch, dim] - aux_paddings: [aux_time, aux_batch] tertiary_vecs=None, - tertiary_paddings=None, - tertiary_vecs: [tertiary_time, tertiary_batch, dim] - tertiary_paddings: [tertiary_time, tertiary_batch] - t: a scalar, the current time step, 0-based. - **kwargs: Can be optional params for the attention layer, eg. attention - projection index tensor. - - Returns: - The attention context vector, [target_batch, source_dim] - - The attention probability vector from the encoder attention layer (the - last attention layer) only, [source_time, target_batch]. - TODO(zhouwk): Return also the attention prob from the tertiary attention. - - Updated prefix states - """ - p = self.params - - batch_size = py_utils.GetShape(source_vecs)[0] - - # First the self-attention layer. - atten_vec, _, new_states = self.self_atten.ExtendStep( - theta.self_atten, source_vecs, prefix_states, t) - - # Next the context attention (tertiary_atten) layer. - atten_vec = tf.expand_dims(atten_vec, axis=0) - atten_vec, _ = self.tertiary_atten.FProp(theta.tertiary_atten, atten_vec, - tertiary_paddings, tertiary_vecs) - - # Next the source attention (aux_atten) layer. - atten_vec, atten_prob = self.atten.FProp(theta.atten, atten_vec, - aux_paddings, aux_vecs, **kwargs) - - # Finally, the feedforward layer. - h = self.fflayer.FProp( - theta.fflayer, atten_vec, - tf.zeros([1, batch_size], dtype=py_utils.FPropDtype(p))) - h = tf.squeeze(h, 0) - return h, atten_prob, new_states diff --git a/lingvo/core/layers_with_attention_test.py b/lingvo/core/layers_with_attention_test.py deleted file mode 100644 index db408070..00000000 --- a/lingvo/core/layers_with_attention_test.py +++ /dev/null @@ -1,2735 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for layers_with_attention.""" - -import lingvo.compat as tf -from lingvo.core import layers -from lingvo.core import layers_with_attention -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core.test_utils import CompareToGoldenSingleFloat -import numpy as np - - -class LayersWithAttentionTest(test_utils.TestCase): - - def testTransformerFeedForwardLayerConstruction(self): - p = layers_with_attention.TransformerFeedForwardLayer.Params() - p.name = 'transformer_fflayer_1' - p.input_dim = 3 - p.hidden_dim = 7 - transformer_fflayer = layers_with_attention.TransformerFeedForwardLayer(p) - self.assertEqual(0, p.output_dim) - # output_dim = p.input_dim when p.output_dim is zero. - self.assertEqual(p.input_dim, transformer_fflayer.output_dim) - - # output_dim equals p.output_dim when p.output_dim is non zero. - p.output_dim = 10 - p.name = 'transformer_fflayer_2' - transformer_fflayer = p.Instantiate() - self.assertEqual(p.output_dim, transformer_fflayer.output_dim) - - def testTransformerFeedForwardLayer(self): - with self.session(use_gpu=True): - tf.random.set_seed(3980847392) - inputs = tf.random.normal([5, 2, 3], seed=948387483) - paddings = tf.zeros([5, 2]) - p = layers_with_attention.TransformerFeedForwardLayer.Params() - p.name = 'transformer_fflayer' - p.input_dim = 3 - p.hidden_dim = 7 - transformer_fflayer = layers_with_attention.TransformerFeedForwardLayer(p) - - h = transformer_fflayer.FPropDefaultTheta(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate(h) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[-0.88366592, -0.05049637, 0.01003706], - [-0.10550675, 1.68050027, 2.29110384]], - [[-1.30083609, -0.40521634, 0.1911681 ], - [ 1.2597878 , 1.45850968, 1.58734488]], - [[ 0.10373873, -0.2716777 , 0.2314173 ], - [ 0.46293864, -0.06359965, 1.20189023]], - [[ 0.3673597 , -0.1691664 , 0.78656065], - [-1.51081395, -0.70281881, -0.9093715 ]], - [[-1.04800868, -0.70610946, -0.35321558], - [-1.92480004, 0.08361804, 0.62713993]]] - # pyformat: enable - # pylint: enable=bad-whitespace - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testTransformerFeedForwardLayerSpecOutDim(self): - with self.session(use_gpu=True): - tf.random.set_seed(3980847392) - inputs = tf.random.normal([5, 2, 3], seed=948387483) - paddings = tf.zeros([5, 2]) - p = layers_with_attention.TransformerFeedForwardLayer.Params() - p.name = 'transformer_fflayer' - p.input_dim = 3 - p.output_dim = 5 - p.hidden_dim = 7 - transformer_fflayer = layers_with_attention.TransformerFeedForwardLayer(p) - - h = transformer_fflayer.FPropDefaultTheta(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate(h) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 1.42697251, 0.79269135, -0.85500956, -0.8122285 , -1.56555367], - [-1.7876718 , 0.26025945, -3.18244219, 1.34756351, 0.25739765]], - [[ 1.27962363, 0.88677615, -1.23556185, -1.06855559, -1.27293301], - [ 0.89336467, 2.46229172, 0.11302143, 1.19385004, -2.37805009]], - [[ 2.80146003, -0.66912627, 1.50160134, -2.30645609, -1.18872762], - [ 1.61967182, -0.51639485, 0.24441491, -1.0871532 , -0.95539457]], - [[ 2.03333473, -0.78205228, 0.71245927, -1.63276744, -0.91654319], - [ 1.54542768, -0.30343491, 0.10666496, -1.67965126, -0.15671858]], - [[ 1.60873222, -1.88402128, 0.79040933, -1.97199082, 0.4778356 ], - [-0.13516766, -0.42583361, -1.86275542, -1.09650302, 0.83263111]]] - # pyformat: enable - # pylint: enable=bad-whitespace - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def _testTransformerAttentionLayerInputs(self, - depth=3, - context_depth=3, - dtype=tf.float32): - np.random.seed(505837249) - source_vecs = tf.stack( - [tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(5)]) - source_padding = tf.transpose( - tf.constant([[0, 0, 1, 1, 0], [1, 0, 0, 0, 1]], dtype=dtype)) - aux_source_vecs = tf.stack( - [tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7)]) - aux_source_paddings = tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]], - dtype=dtype)) - context_vecs = tf.stack([ - tf.constant(np.random.rand(2, context_depth), dtype=dtype) - for _ in range(7) - ]) - return (source_vecs, source_padding, aux_source_vecs, aux_source_paddings, - context_vecs) - - def testTransformerAttentionLayerCase1(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (source_vecs, source_padding, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - ctx, probs = transformer_atten.FPropDefaultTheta(source_vecs, - source_padding) - self.evaluate(tf.global_variables_initializer()) - actual_ctx, actual_probs = self.evaluate([ctx, probs]) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.47126436, 1.46579707, 0.39105844, -0.88563323], - [-1.29514003, -1.08241224, 1.49894714, 2.5935874 ]], - [[-0.00313053, 1.17399275, -1.28071034, -1.6311729 ], - [-0.77028418, -0.18855178, -0.75814998, 2.19872856]], - [[ 1.72851753, -0.40323859, -1.19053328, -1.39761829], - [-1.72141743, -0.78715289, 1.28404212, 2.78338313]], - [[-0.8881942 , 0.33776048, 1.28791749, -0.45082122], - [ 1.4362365 , 0.46009994, -1.45436597, -1.90602148]], - [[-0.51681399, -0.70075679, -0.48352116, 1.93754733], - [-1.44486678, 0.81801879, -1.03079689, 1.86697066]]] - expected_probs = [ - [[ 0.21387868, 0.22080734, 0. , 0. , 0.56531399], - [ 0. , 0.30584112, 0.24723588, 0.44692296, 0. ]], - [[ 0.25358215, 0.50932312, 0. , 0. , 0.23709476], - [ 0. , 0.56834149, 0.2632803 , 0.16837817, 0. ]], - [[ 0.38519409, 0.55454361, 0. , 0. , 0.06026226], - [ 0. , 0.33708778, 0.21976741, 0.4431448 , 0. ]], - [[ 0.27139962, 0.12790371, 0. , 0. , 0.60069668], - [ 0. , 0.31849149, 0.28174096, 0.39976761, 0. ]], - [[ 0.16272782, 0.15781289, 0. , 0. , 0.67945927], - [ 0. , 0.55003977, 0.26049581, 0.18946445, 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - - def testTransformerAttentionLayerCase1GatedResidualConnection(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - p.add_unnormalized_input = True - p.residual_function = layers.HighwaySkipLayer.Params().Set( - carry_bias_init=100, couple_carry_transform_gates=True) - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (source_vecs, source_padding, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - ctx, probs = transformer_atten.FPropDefaultTheta(source_vecs, - source_padding) - self.evaluate(tf.global_variables_initializer()) - actual_ctx, _, actual_source_vecs = self.evaluate( - [ctx, probs, source_vecs]) - # Due to the high bias, the gated residual connection is saturated and - # returns the original (unnormalized) input. - self.assertAllClose(actual_source_vecs, actual_ctx, rtol=1e-4, atol=1e-4) - - def testTransformerAttentionLayerCase2(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = True - p.num_attention_heads = 2 - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (source_vecs, source_padding, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - ctx, probs = transformer_atten.FPropDefaultTheta(source_vecs, - source_padding) - self.evaluate(tf.global_variables_initializer()) - actual_ctx, actual_probs = self.evaluate([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-0.14429152, 1.15510106, 1.11930299, -1.19245839], - [-0.69580591, -0.47006619, 0.82592297, 0.69593251]], - [[ 0.24164687, 0.53328454, -1.02119482, -1.49412084], - [-0.82601064, 0.024203 , -1.11880171, 1.80784416]], - [[ 1.7644347 , -0.53346401, -1.1461122 , -1.42797422], - [-0.95326459, 0.39580142, 0.39262164, 0.67513674]], - [[-0.28252155, -0.95237327, 2.08757687, -0.21231559], - [ 1.4362365 , 0.46009994, -1.45436597, -1.90602148]], - [[-0.51681399, -0.70075679, -0.48352116, 1.93754733], - [-1.44486678, 0.81801879, -1.03079689, 1.86697066]]] - expected_probs = [ - [[ 1. , 0. , 0. , 0. , 0. ], - [ 0.2 , 0.2 , 0.2 , 0.2 , 0.2 ]], - [[ 0.3966811 , 0.60331887, 0. , 0. , 0. ], - [ 0. , 1. , 0. , 0. , 0. ]], - [[ 0.41050252, 0.58949745, 0. , 0. , 0. ], - [ 0. , 0.5245893 , 0.4754107 , 0. , 0. ]], - [[ 0.58882225, 0.41117775, 0. , 0. , 0. ], - [ 0. , 0.31849149, 0.28174096, 0.39976761, 0. ]], - [[ 0.16272782, 0.15781289, 0. , 0. , 0.67945927], - [ 0. , 0.55003977, 0.26049581, 0.18946445, 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx) - self.assertAllClose(expected_probs, actual_probs) - - def testTransformerAttentionLayerDeterministicDropout(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - - p.residual_dropout_tpl = layers.DeterministicDropoutLayer.Params() - p.residual_dropout_prob = 0.1 - - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (source_vecs, source_padding, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - ctx, probs = transformer_atten.FProp(transformer_atten.theta, source_vecs, - source_padding) - - self.evaluate(tf.global_variables_initializer()) - actual_ctx, actual_probs = self.evaluate([ctx, probs]) - - # pylint: disable=bad-whitespace - # pyformat: disable - print(np.array_repr(actual_ctx)) - expected_ctx = np.array([ - [[-1.45762944, 1.5337404 , 0.34037334, -0.97208667], - [-1.35992002, -1.06530988, 1.53705895, 2.79370689]], - [[ 0.00657134, 1.12030125, -1.32564592, -1.73569465], - [-0.80793667, -0.10877949, -0.80295694, 2.25494242]], - [[ 1.76956046, -0.50777751, -1.19745886, -1.46751583], - [-1.79178905, -0.77374339, 1.31586027, 2.98173356]], - [[-0.85498607, -0.37413225, 1.25707364, -0.50043333], - [ 1.62276983, 0.50820369, -1.52967572, -2.02076197]], - [[-0.66754031, -0.68657839, -0.51643699, 1.96581018], - [-1.4816376 , 0.89419198, -0.57226259, 1.90177512]] - ], dtype=np.float32) - - print(np.array_repr(actual_probs)) - expected_probs = np.array([ - [[ 0.21387868, 0.22080734, 0. , 0. , 0.56531399], - [ 0. , 0.30584112, 0.24723588, 0.44692296, 0. ]], - [[ 0.25358215, 0.50932312, 0. , 0. , 0.23709476], - [ 0. , 0.56834149, 0.2632803 , 0.16837817, 0. ]], - [[ 0.38519409, 0.55454361, 0. , 0. , 0.06026226], - [ 0. , 0.33708778, 0.21976741, 0.4431448 , 0. ]], - [[ 0.27139962, 0.12790371, 0. , 0. , 0.60069668], - [ 0. , 0.31849149, 0.28174096, 0.39976761, 0. ]], - [[ 0.16272782, 0.15781289, 0. , 0. , 0.67945927], - [ 0. , 0.55003977, 0.26049581, 0.18946445, 0. ]] - ], dtype=np.float32) - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - - def testTransformerAttentionLayerStepByStep(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = True - p.num_attention_heads = 2 - x_atten = layers_with_attention.TransformerAttentionLayer(p) - - (source_vecs, _, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - ctx1, probs1 = x_atten.FPropDefaultTheta(source_vecs, source_padding) - ctx2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - ctx, probs, prefix_states = x_atten.ExtendStep(x_atten.theta, - source_vecs[i, :, :], - prefix_states) - probs_pad = tf.zeros([2, 5 - i - 1]) - padded_probs = tf.concat([probs, probs_pad], 1) - ctx2.append(ctx) - probs2.append(padded_probs) - - ctx2 = tf.stack(ctx2) - probs2 = tf.stack(probs2) - - self.evaluate(tf.global_variables_initializer()) - ctx1_v, probs1_v, ctx2_v, probs2_v = self.evaluate( - [ctx1, probs1, ctx2, probs2]) - tf.logging.info(np.array_repr(ctx1_v)) - tf.logging.info(np.array_repr(probs1_v)) - tf.logging.info(np.array_repr(ctx2_v)) - tf.logging.info(np.array_repr(probs2_v)) - self.assertAllClose(ctx1_v, ctx2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testTransformerAttentionLayerGatedResidualConnectionStepByStep(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = True - p.num_attention_heads = 2 - p.residual_function = layers.HighwaySkipLayer.Params().Set( - couple_carry_transform_gates=True) - x_atten = layers_with_attention.TransformerAttentionLayer(p) - - (source_vecs, _, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - ctx1, probs1 = x_atten.FPropDefaultTheta(source_vecs, source_padding) - ctx2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - ctx, probs, prefix_states = x_atten.ExtendStep(x_atten.theta, - source_vecs[i, :, :], - prefix_states) - probs_pad = tf.zeros([2, 5 - i - 1]) - padded_probs = tf.concat([probs, probs_pad], 1) - ctx2.append(ctx) - probs2.append(padded_probs) - - ctx2 = tf.stack(ctx2) - probs2 = tf.stack(probs2) - - self.evaluate(tf.global_variables_initializer()) - ctx1_v, probs1_v, ctx2_v, probs2_v = self.evaluate( - [ctx1, probs1, ctx2, probs2]) - self.assertAllClose(ctx1_v, ctx2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testTransformerAttentionLayerCase3(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (query_vec, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - ctx, probs = transformer_atten.FPropDefaultTheta(query_vec, aux_paddings, - aux_vecs) - self.evaluate(tf.global_variables_initializer()) - actual_ctx, actual_probs = self.evaluate([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.42420077, 1.19024372, 1.35146523, 0.85896158], - [-0.44974625, -1.00108492, 1.63387251, 1.678146 ]], - [[ 0.1134335 , 1.97617495, -0.35918081, 0.26396495], - [-0.19688171, -0.71197301, 0.0659425 , 2.5417304 ]], - [[ 1.58169425, 0.81259179, -0.58948535, 0.20254248], - [-0.84438968, -0.65845209, 1.45584249, 1.87587976]], - [[-1.01532316, -0.05166581, 2.07901478, 0.97540361], - [ 2.08563352, 0.34328598, -0.23240227, -0.19035631]], - [[-0.53881919, -0.60117185, 0.29170275, 2.6474514 ], - [-0.88318163, 0.37149727, -0.16098523, 2.3810885 ]]] - expected_probs = [ - [[ 0.32392544, 0., 0.27218491, 0., 0.19574419, 0., 0.20814547], - [ 0., 0.273045 , 0., 0.43572819, 0., 0.2912268 , 0.]], - [[ 0.24094662, 0., 0.23919827, 0., 0.26563686, 0., 0.25421822], - [ 0., 0.21680018, 0., 0.33962148, 0.,0.44357836 , 0.]], - [[ 0.20083594, 0., 0.20683075, 0., 0.28931937, 0., 0.30301392], - [ 0., 0.24710922, 0., 0.453915 , 0.,0.29897571 , 0.]], - [[ 0.32845193, 0., 0.26491433, 0., 0.18304622, 0., 0.22358747], - [ 0., 0.39426237, 0., 0.19774443, 0.,0.4079932 , 0.]], - [[ 0.23542665, 0., 0.27910906, 0., 0.30036426, 0., 0.18510005], - [ 0., 0.20147586, 0., 0.37759233, 0., 0.42093182, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - - def _testTransformerAttentionLayerInputsMultiAuxSource( - self, aux_source_list, depth=3, context_depth=3, dtype=tf.float32): - (source_vecs, source_padding, _, _, _) = ( - self._testTransformerAttentionLayerInputs(depth, context_depth, dtype)) - np.random.seed(505837249) - aux_source_vecs = py_utils.NestedMap() - for aux_src_key in aux_source_list: - aux_source_vecs[aux_src_key] = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7) - ]) - aux_source_paddings = py_utils.NestedMap({ - aux_src_key: tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]], - dtype=dtype)) for aux_src_key in aux_source_list - }) - context_vecs = py_utils.NestedMap() - for aux_src_key in aux_source_list: - context_vecs[aux_src_key] = tf.stack([ - tf.constant(np.random.rand(2, context_depth), dtype=dtype) - for _ in range(7) - ]) - return (source_vecs, source_padding, aux_source_vecs, aux_source_paddings, - context_vecs) - - def testTransformerAttentionLayerCase3MultiSource(self): - with self.session(use_gpu=True) as sess: - depth = 4 - p = layers_with_attention.TransformerMultiSourceAttentionLayer.Params() - p.name = 'transformer_atten_multisource' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - p.num_source = 2 - transformer_atten = ( - layers_with_attention.TransformerMultiSourceAttentionLayer(p)) - - (query_vec, _, aux_vecs, aux_paddings, _) = ( - self._testTransformerAttentionLayerInputsMultiAuxSource( - ['source_0', 'source_1'], depth=depth)) - - ctx, probs = transformer_atten.FPropDefaultTheta(query_vec, aux_paddings, - aux_vecs) - tf.global_variables_initializer().run() - actual_ctx, actual_probs = sess.run([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.9893163 , 0.8076348 , -0.33805895, -0.20369706], - [-1.4164762 , -1.0597495 , -0.3834126 , 0.3456189 ]], - [[-0.32503036, 1.4952568 , -1.9324137 , -0.77024114], - [-0.9230547 , -0.89096445, -1.7928462 , 1.0901089 ]], - [[ 1.2240632 , 0.26689315, -2.0940783 , -0.9101793 ], - [-1.805772 , -0.74725944, -0.5485071 , 0.5403221 ]], - [[-1.5880606 , -0.43595213, 0.3818947 , -0.15712431], - [ 0.968494 , 0.19423638, -2.308594 , -1.4253062 ]], - [[-0.8178122 , -1.1570994 , -1.1993079 , 1.4127911 ], - [-1.7231476 , 0.17116357, -2.0703826 , 0.96320933]]] - expected_probs = [ - [[0.16679956, 0., 0.2122806 , 0., 0.23512313, 0., 0.38579667], - [0., 0.28562695, 0., 0.3442661 , 0., 0.370107 , 0.]], - [[0.28629708, 0., 0.18837643, 0., 0.2644571 , 0., 0.26086944], - [0., 0.5590873 , 0., 0.22519027, 0., 0.21572247, 0.]], - [[0.3374045 , 0., 0.21468817, 0., 0.25822428, 0., 0.18968314], - [0., 0.2896077 , 0., 0.34381902, 0., 0.36657327, 0.]], - [[0.14310986, 0., 0.2507791 , 0., 0.22308563, 0., 0.3830254 ], - [0., 0.43070328, 0., 0.2930708 , 0., 0.27622598, 0.]], - [[0.30523974, 0., 0.30610216, 0., 0.2248916 , 0., 0.1637665 ], - [0., 0.49082592, 0., 0.26013914, 0., 0.24903494, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - - def testTransformerAttentionLayerCase3MultiSourceMatchSingle(self): - with self.session(use_gpu=True) as sess: - # Prepare inputs. - depth = 4 - (query_vec, _, aux_vecs, aux_paddings, _) = ( - self._testTransformerAttentionLayerInputsMultiAuxSource( - ['source_0', 'source_1'], depth=depth)) - - # Create two source inputs but use single-source attention - p = layers_with_attention.TransformerMultiSourceAttentionLayer.Params() - p.random_seed = 123 - p.name = 'transformer_atten_multisource_single' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - p.num_source = 1 - msa = layers_with_attention.TransformerMultiSourceAttentionLayer(p) - msa_ctx, msa_probs = ( - msa.FPropDefaultTheta(query_vec, aux_paddings, aux_vecs)) - - # Original single source attention layer. - p = layers_with_attention.TransformerAttentionLayer.Params() - p.random_seed = 123 - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - ssa = layers_with_attention.TransformerAttentionLayer(p) - ssa_ctx, ssa_probs = ssa.FPropDefaultTheta(query_vec, - aux_paddings['source_0'], - aux_vecs['source_0']) - - # Compare two context vectors and probabilities. - tf.global_variables_initializer().run() - actual_msa_ctx, actual_msa_probs, actual_ssa_ctx, actual_ssa_probs = ( - sess.run([msa_ctx, msa_probs, ssa_ctx, ssa_probs])) - - # pylint: disable=bad-whitespace - # pyformat: disable - self.assertAllClose(actual_msa_ctx, actual_ssa_ctx, - rtol=1e-05, atol=1e-05) - self.assertAllClose(actual_msa_probs, actual_ssa_probs, - rtol=1e-05, atol=1e-05) - - def testTransformerAttentionLayerSourceContext(self): - # Equivalent: Passing no context vecs and source vecs as context vecs. - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (query_vec, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs( - depth=depth, context_depth=depth) - - ctx1, probs1 = transformer_atten.FPropDefaultTheta( - query_vec=query_vec, - source_paddings=aux_paddings, - source_vecs=aux_vecs, - context_vecs=aux_vecs) - - ctx2, probs2 = transformer_atten.FPropDefaultTheta( - query_vec=query_vec, - source_paddings=aux_paddings, - source_vecs=aux_vecs) - - self.evaluate(tf.global_variables_initializer()) - actual_ctx1, actual_probs1, actual_ctx2, actual_probs2 = self.evaluate( - [ctx1, probs1, ctx2, probs2]) - self.assertAllEqual(actual_ctx1, actual_ctx2) - self.assertAllEqual(actual_probs1, actual_probs2) - - def testTransformerAttentionLayerCase4a(self): - # Distinct key and value vectors of the same size. - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (query_vec, _, aux_vecs, aux_paddings, - context_vecs) = self._testTransformerAttentionLayerInputs( - depth=depth, context_depth=depth) - - ctx, probs = transformer_atten.FPropDefaultTheta( - query_vec=query_vec, - source_paddings=aux_paddings, - source_vecs=aux_vecs, - context_vecs=context_vecs) - - self.evaluate(tf.global_variables_initializer()) - actual_ctx, actual_probs = self.evaluate([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.20854747, 1.25685954, 1.39818001, 0.558267 ], - [-0.39904317, -0.85738903, 1.45404375, 1.16389585]], - [[ 0.27544549, 1.93070388, -0.24477535, 0.12131107], - [-0.07007086, -0.53334039, -0.01144788, 2.03883505]], - [[ 1.72718525, 0.73558617, -0.45405889, 0.1063388 ], - [-0.76255953, -0.52610761, 1.30195093, 1.3571732 ]], - [[-0.79346895, 0.03049853, 2.11432981, 0.64747918], - [ 1.86823332, 0.3250314 , -0.50979781, -0.40038702]], - [[-0.30053592, -0.53348505, 0.41098642, 2.43903708], - [-0.75298154, 0.50427407, -0.23542863, 1.89634883]]] - expected_probs = [ - [[ 0.32392544, 0., 0.27218491, 0., 0.19574417, 0., 0.20814548], - [ 0., 0.273045 , 0., 0.43572825, 0., 0.2912268 , 0.]], - [[ 0.24094665, 0., 0.23919825, 0., 0.26563686, 0., 0.25421822], - [ 0., 0.21680018, 0., 0.33962148, 0., 0.44357836, 0.]], - [[ 0.20083596, 0., 0.20683077, 0., 0.28931937, 0., 0.30301392], - [ 0., 0.24710923, 0., 0.45391506, 0., 0.29897574, 0.]], - [[ 0.32845187, 0., 0.26491439, 0., 0.18304622, 0., 0.22358751], - [ 0., 0.39426237, 0., 0.1977444 , 0., 0.4079932 , 0.]], - [[ 0.23542665, 0., 0.27910906, 0., 0.30036426, 0., 0.18510005], - [ 0., 0.20147583, 0., 0.37759233, 0., 0.42093182, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - - def testTransformerAttentionLayerCase4aMultiSource(self): - # Distinct key and value vectors of the same size. - with self.session(use_gpu=True) as sess: - depth = 4 - p = layers_with_attention.TransformerMultiSourceAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - p.num_source = 2 - transformer_atten = ( - layers_with_attention.TransformerMultiSourceAttentionLayer(p)) - - (query_vec, _, aux_vecs, aux_paddings, - context_vecs) = self._testTransformerAttentionLayerInputsMultiAuxSource( - ['source_0', 'source_1'], depth=depth, context_depth=depth) - - ctx, probs = transformer_atten.FPropDefaultTheta( - query_vec=query_vec, - source_paddings=aux_paddings, - source_vecs=aux_vecs, - context_vecs=context_vecs) - - tf.global_variables_initializer().run() - actual_ctx, actual_probs = sess.run([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-2.263544 , -0.6288333 , 0.56436384, 0.01389617], - [-1.2714428 , -2.6551175 , 1.2088637 , 0.48963785]], - [[-0.7530552 , 0.2863059 , -1.0583341 , -0.62887365], - [-0.96861804, -2.3108015 , -0.32213187, 1.4070555 ]], - [[ 0.6888912 , -0.83782226, -1.3349627 , -0.69250315], - [-1.646423 , -2.3046758 , 1.0617565 , 0.6768545 ]], - [[-1.8710074 , -1.9080507 , 1.2318314 , 0.14334393], - [ 0.92007947, -1.775676 , -1.1390316 , -0.9541185 ]], - [[-1.375605 , -2.3637016 , -0.5955716 , 1.8448071 ], - [-1.6682272 , -1.2519215 , -0.5330956 , 1.2296966 ]]] - expected_probs = [ - [[0.22346233, 0., 0.27624047, 0., 0.18855348, 0., 0.31174374], - [0., 0.17387941, 0., 0.4642802 , 0., 0.36184043, 0.]], - [[0.23724607, 0., 0.24033949, 0., 0.3725937 , 0., 0.14982074], - [0., 0.15892553, 0., 0.4639521 , 0., 0.37712237, 0.]], - [[0.25570837, 0., 0.21216837, 0., 0.40378904, 0., 0.12833425], - [0., 0.16656096, 0., 0.47455215, 0., 0.3588869 , 0.]], - [[0.22077632, 0., 0.27379048, 0., 0.14691363, 0., 0.35851952], - [0., 0.5620029 , 0., 0.21104112, 0., 0.22695602, 0.]], - [[0.20673111, 0., 0.22832122, 0., 0.12665181, 0., 0.43829578], - [0., 0.17881572, 0., 0.45228398, 0., 0.36890027, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - - def testTransformerAttentionLayerCase4b(self): - # Distinct key and value vectors of different sizes. - with self.session(use_gpu=True): - depth = 4 - context_depth = 3 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - print(p) - p.num_attention_heads = 2 - p.atten_tpl.enable_ctx_pre_proj = True # Project values first. - p.context_dim = context_depth - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (query_vec, _, aux_vecs, aux_paddings, - context_vecs) = self._testTransformerAttentionLayerInputs( - depth=depth, context_depth=context_depth) - - ctx, probs = transformer_atten.FPropDefaultTheta( - query_vec=query_vec, - source_paddings=aux_paddings, - source_vecs=aux_vecs, - context_vecs=context_vecs) - - self.evaluate(tf.global_variables_initializer()) - actual_ctx, actual_probs = self.evaluate([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.78694427, 0.47923172, 0.89032698, 0.05556235], - [-0.91133636, -2.05677342, 1.30821121, 1.17388368]], - [[-0.24106422, 1.27436733, -0.84274787, -0.58437365], - [-0.58214164, -1.7144506 , -0.21780583, 2.03152227]], - [[ 1.22925639, 0.15926462, -1.10279834, -0.69442266], - [-1.2955091 , -1.72805309, 1.15411568, 1.39945638]], - [[-1.38178754, -0.7436831 , 1.60785818, 0.16023314], - [ 1.5662415 , -0.77094424, -0.63392496, -0.6477108 ]], - [[-0.83664525, -1.20021605, -0.15795891, 1.81301379], - [-1.27991939, -0.67706013, -0.42443359, 1.92405224]]] - # Probabilities are unaffected by change of value vectors. - expected_probs = [ - [[ 0.32392544, 0., 0.27218491, 0., 0.19574417, 0., 0.20814548], - [ 0., 0.273045 , 0., 0.43572825, 0., 0.2912268 , 0.]], - [[ 0.24094665, 0., 0.23919825, 0., 0.26563686, 0., 0.25421822], - [ 0., 0.21680018, 0., 0.33962148, 0., 0.44357836, 0.]], - [[ 0.20083596, 0., 0.20683077, 0., 0.28931937, 0., 0.30301392], - [ 0., 0.24710923, 0., 0.45391506, 0., 0.29897574, 0.]], - [[ 0.32845187, 0., 0.26491439, 0., 0.18304622, 0., 0.22358751], - [ 0., 0.39426237, 0., 0.1977444 , 0., 0.4079932 , 0.]], - [[ 0.23542665, 0., 0.27910906, 0., 0.30036426, 0., 0.18510005], - [ 0., 0.20147583, 0., 0.37759233, 0., 0.42093182, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - - def testTransformerAttentionLayerCase4bMultiSource(self): - # Distinct key and value vectors of different sizes. - with self.session(use_gpu=True) as sess: - depth = 4 - context_depth = 3 - p = layers_with_attention.TransformerMultiSourceAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - print(p) - p.num_attention_heads = 2 - p.atten_tpl.enable_ctx_pre_proj = True # Project values first. - p.context_dim = context_depth - p.num_source = 2 - transformer_atten = ( - layers_with_attention.TransformerMultiSourceAttentionLayer(p)) - - (query_vec, _, aux_vecs, aux_paddings, - context_vecs) = self._testTransformerAttentionLayerInputsMultiAuxSource( - ['source_0', 'source_1'], depth=depth, context_depth=context_depth) - - ctx, probs = transformer_atten.FPropDefaultTheta( - query_vec=query_vec, - source_paddings=aux_paddings, - source_vecs=aux_vecs, - context_vecs=context_vecs) - - tf.global_variables_initializer().run() - actual_ctx, actual_probs = sess.run([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-0.52144265, 1.7370229 , 0.09479183, 1.3142197 ], - [ 0.48182625, -0.41524518, 0.2950616 , 2.3245158 ]], - [[ 1.0139368 , 2.6589985 , -1.528513 , 0.6880791 ], - [ 0.7810391 , -0.05419022, -1.227257 , 3.2472034 ]], - [[ 2.4781933 , 1.5413835 , -1.7759092 , 0.6057711 ], - [ 0.11952043, -0.07813096, 0.12346762, 2.5386043 ]], - [[-0.12219751, 0.46310303, 0.7768879 , 1.4295386 ], - [ 2.8404353 , 0.901297 , -1.5073049 , 0.60736287]], - [[ 0.37801886, -0.05114734, -1.003877 , 3.0894797 ], - [ 0.10942292, 0.975695 , -1.4856565 , 3.1215234 ]]] - # Probabilities are unaffected by change of value vectors. - expected_probs = [ - [[0.22346234, 0., 0.27624047, 0., 0.18855348, 0., 0.31174374], - [0., 0.17387941, 0., 0.4642802 , 0., 0.36184043, 0.]], - [[0.23724607, 0., 0.24033949, 0., 0.3725937 , 0., 0.14982076], - [0., 0.15892553, 0., 0.4639521 , 0., 0.3771224 , 0.]], - [[0.2557084 , 0., 0.21216837, 0., 0.403789 , 0., 0.12833424], - [0., 0.16656098, 0., 0.47455215, 0., 0.3588869 , 0.]], - [[0.22077632, 0., 0.27379048, 0., 0.14691365, 0., 0.35851952], - [0., 0.5620028 , 0., 0.21104114, 0., 0.22695604, 0.]], - [[0.20673111, 0., 0.22832122, 0., 0.12665181, 0., 0.43829578], - [0., 0.17881574, 0., 0.45228398, 0., 0.36890027, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - - def testTransformerAttentionLayerCase5(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = True - p.mask_type = 'eye' - p.num_attention_heads = 2 - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (source_vecs, source_padding, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - ctx, probs = transformer_atten.FPropDefaultTheta(source_vecs, - source_padding) - self.evaluate(tf.global_variables_initializer()) - actual_ctx, actual_probs = self.evaluate([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.89149332, 1.18417633, 0.09695292, -0.83397102], - [-1.29514003, -1.08241224, 1.49894726, 2.59358764]], - [[ 0.79232693, 2.47633171, -0.90657401, -1.5221628 ], - [-0.14457735, 0.09040731, -0.12422991, 2.13300467]], - [[ 1.72851753, -0.40323859, -1.19053328, -1.39761829], - [-2.15129089, -1.16594994, 1.1004864 , 3.07194686]], - [[-0.88819426, 0.3377606 , 1.28791749, -0.45082125], - [1.97874951, 1.50414598, -1.15547466, -1.18697572]], - [[ 0.10235745, -1.51675844, 0.13308235, 1.26194644], - [-1.44486666, 0.81801897, -1.03079677, 1.86697078]]] - expected_probs = [ - [[ 0. , 0.33807203, 0. , 0. , 0.661928 ], - [ 0. , 0.30584112, 0.24723586, 0.44692296, 0. ]], - [[ 0.63300228, 0. , 0. , 0. , 0.36699772], - [ 0. , 0. , 0.70683479, 0.29316518, 0. ]], - [[ 0.38519406, 0.55454367, 0. , 0. , 0.06026225], - [ 0. , 0.51602799, 0. , 0.48397198, 0. ]], - [[ 0.27139962, 0.12790368, 0. , 0. , 0.60069668], - [ 0. , 0.46712866, 0.53287131, 0. , 0. ]], - [[ 0.55518425, 0.4448157 , 0. , 0. , 0. ], - [ 0. , 0.55003977, 0.26049584, 0.18946445, 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx) - self.assertAllClose(expected_probs, actual_probs) - - def testTransformerAttentionLayerCase6(self): - with self.session(use_gpu=True): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = True - p.mask_type = 'ngram' - p.mask_ngram_order = 3 - p.num_attention_heads = 2 - transformer_atten = layers_with_attention.TransformerAttentionLayer(p) - - (source_vecs, source_padding, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - ctx, probs = transformer_atten.FPropDefaultTheta(source_vecs, - source_padding) - self.evaluate(tf.global_variables_initializer()) - actual_ctx, actual_probs = self.evaluate([ctx, probs]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info('actual_probs=%r', np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-0.14429152, 1.155101, 1.119303, -1.1924583], - [-0.6958059, -0.47006613, 0.8259231, 0.6959326]], - [[0.24164662, 0.5332843, -1.0211949, -1.4941208], - [-0.8260106, 0.024203, -1.1188016, 1.807844]], - [[1.7644346, -0.533464, -1.1461123, -1.4279743], - [-0.95326424, 0.39580172, 0.39262217, 0.6751373]], - [[-1.3441969, -2.3305228, 1.7523124, 0.15416345], - [1.4362367, 0.46009994, -1.4543657, -1.9060212]], - [[-0.8291472, 0.21259767, -0.9077787, 1.6243731], - [-1.0709695, 0.74920934, -0.5950014, 1.5919089]]] - expected_probs = [ - [[1. , 0. , 0. , 0. , 0. ], - [0.2 , 0.2 , 0.2 , 0.2 , 0.2 ]], - [[0.3966811 , 0.6033189 , 0. , 0. , 0. ], - [0. , 1. , 0. , 0. , 0. ]], - [[0.41050246, 0.5894975 , 0. , 0. , 0. ], - [0. , 0.5245893 , 0.4754107 , 0. , 0. ]], - [[0. , 1. , 0. , 0. , 0. ], - [0. , 0.31849146, 0.28174093, 0.39976764, 0. ]], - [[0. , 0. , 0. , 0. , 1. ], - [0. , 0. , 0.5881755 , 0.41182452, 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx) - self.assertAllClose(expected_probs, actual_probs) - - def testTransformerLayerConstruction(self): - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer_1' - p.source_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - p.has_aux_atten = True - p.mask_self_atten = True - layer = layers_with_attention.TransformerLayer(p) - # output_dim is equal to source_dim when p.output_dim == 0 - self.assertEqual(0, p.output_dim) - self.assertEqual(p.source_dim, layer.output_dim) - # output_dim corresponds to p.output_dim when it is non-zero. - p.output_dim = 6 - p.name = 'transformer_2' - layer = p.Instantiate() - self.assertEqual(p.output_dim, layer.output_dim) - - def testTransformerLayerFProp(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - transformer = layers_with_attention.TransformerLayer(p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - h, probs = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, actual_prob_output = self.evaluate([h, probs]) - tf.logging.info(np.array_repr(actual_layer_output)) - tf.logging.info(np.array_repr(actual_prob_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[ 0.68134278, 0.74287307, 0.04602078, 1.99463582], - [ 0.20382279, -1.50973201, 1.33421206, 0.53317755]], - [[ 2.46715426, 2.84406185, -0.60359633, 0.51742059], - [ 1.06444919, -1.45264888, -0.06196141, 0.35242724]], - [[ 2.3442452 , -0.56243378, -1.1149826 , 0.50276589], - [ 1.04868603, -1.68515253, 0.3093726 , -0.19512933]], - [[-0.11517292, -1.21290886, 1.31996512, 1.14821553], - [ 3.14395714, -1.07060659, 0.27842081, -1.81273639]], - [[ 1.39219522, -0.81882864, -0.32732445, 1.36851478], - [-0.79119539, -0.28148842, 0.29963702, 1.37034667]]] - expected_prob_output = [ - [[ 0.21795762, 0., 0.26612395, 0., 0.31251648, 0., 0.20340192], - [ 0., 0.2677784 , 0., 0.32895881, 0., 0.40326279, 0.]], - [[ 0.25721505, 0., 0.24116731, 0., 0.25138181, 0., 0.2502358 ], - [ 0., 0.25691482, 0., 0.31076014, 0., 0.43232504, 0.]], - [[ 0.24550268, 0., 0.25128055, 0., 0.25109866, 0., 0.25211811], - [ 0., 0.26769161, 0., 0.32481128, 0., 0.40749705, 0.]], - [[ 0.22675318, 0., 0.26633731, 0., 0.28919035, 0., 0.21771915], - [ 0., 0.35955882, 0., 0.36869824, 0., 0.271743 , 0.]], - [[ 0.21504655, 0., 0.26958644, 0., 0.30847484, 0., 0.20689213], - [ 0., 0.29516917, 0., 0.29359812, 0., 0.41123265, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - self.assertAllClose(expected_prob_output, actual_prob_output) - - def testMultiAuxSourceTransformerLayerFProp(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.tr_aux_atten_tpl = ( - layers_with_attention.TransformerMultiSourceAttentionLayer.Params() - .Set( - source_dim=p.source_dim, - num_source=2, - primary_source_index=0, - num_attention_heads=4)) - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - transformer = layers_with_attention.TransformerLayer(p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputsMultiAuxSource( - ['source_0', 'source_1'], depth=depth) - - h, probs = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, actual_prob_output = self.evaluate([h, probs]) - tf.logging.info(np.array_repr(actual_layer_output)) - tf.logging.info(np.array_repr(actual_prob_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[-0.06297368, 0.75025094, -0.18167767, 2.27935 ], - [-0.22771487, -1.9459789 , 0.758848 , 1.2273839 ]], - [[ 1.6866916 , 2.9894042 , -1.2287276 , 0.8018402 ], - [ 0.656631 , -1.2074132 , -0.41612232, 1.4099871 ]], - [[ 1.6463919 , -0.493517 , -1.3494966 , 0.6977608 ], - [ 0.49527422, -1.5192728 , -0.1677584 , 0.781141 ]], - [[-0.86701846, -1.2044021 , 1.0710557 , 1.4103888 ], - [ 3.0039275 , -0.98788637, -0.48796502, -0.90612394]], - [[ 0.6298464 , -0.33676302, -0.22484902, 1.8341833 ], - [-1.2259507 , -0.716857 , -0.1336647 , 1.9020087 ]]] - expected_prob_output = [ - [[0.23055646, 0., 0.270754 , 0., 0.20824522, 0., 0.2904443 ], - [0., 0.34072176, 0., 0.34083408, 0., 0.31844413, 0.]], - [[0.25588194, 0., 0.21465777, 0., 0.26527345, 0., 0.26418683], - [0., 0.31694067, 0., 0.35715103, 0., 0.32590824, 0.]], - [[0.24147315, 0., 0.22742277, 0., 0.2734162 , 0., 0.25768787], - [0., 0.33686832, 0., 0.34380934, 0., 0.31932235, 0.]], - [[0.22445586, 0., 0.29794338, 0., 0.20764738, 0., 0.26995337], - [0., 0.3731808 , 0., 0.29736063, 0., 0.32945853, 0.]], - [[0.2221506 , 0., 0.2830769 , 0., 0.21007922, 0., 0.2846933 ], - [0., 0.3024338 , 0., 0.36399618, 0., 0.33357003, 0.]]] - # # pyformat: enable - # # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - self.assertAllClose(expected_prob_output, actual_prob_output) - - def testMultiAuxSourceTransformerLayerFPropMatchSingle(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - # Multi-source transformer layer - p = layers_with_attention.TransformerLayer.Params().Set( - name='multi_source_trans', random_seed=123) - p.tr_atten_tpl.num_attention_heads = 4 - p.source_dim = depth - p.has_aux_atten = True - p.tr_aux_atten_tpl = ( - layers_with_attention.TransformerMultiSourceAttentionLayer.Params() - .Set( - source_dim=p.source_dim, - num_source=1, - primary_source_index=0, - num_attention_heads=4)) - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - msa_trans = layers_with_attention.TransformerLayer(p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputsMultiAuxSource( - ['source_0', 'source_1'], depth=depth) - - msa_h, msa_probs = msa_trans.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - # Original single-source transformer decoder. - p = layers_with_attention.TransformerLayer.Params().Set( - name='single_source_trans', random_seed=123) - p.tr_atten_tpl.num_attention_heads = 4 - p.tr_atten_tpl.random_seed = 123 - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - ssa_trans = layers_with_attention.TransformerLayer(p) - ssa_h, ssa_probs = ssa_trans.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs['source_0'], - aux_paddings=aux_paddings['source_0']) - - self.evaluate(tf.global_variables_initializer()) - msa_layer_output, msa_prob_output, ssa_layer_output, ssa_prob_output = ( - self.evaluate([msa_h, msa_probs, ssa_h, ssa_probs])) - - self.assertAllClose( - msa_layer_output, ssa_layer_output, rtol=1e-05, atol=1e-05) - self.assertAllClose( - msa_prob_output, ssa_prob_output, rtol=1e-05, atol=1e-05) - - def testTransformerLayerOutputLayerNormFProp(self): - """Test post-layernorm Fprop.""" - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.tr_post_ln_tpl = layers.LayerNorm.Params() - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - transformer = layers_with_attention.TransformerLayer(p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - h, probs = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, actual_prob_output = self.evaluate([h, probs]) - tf.logging.info(np.array_repr(actual_layer_output)) - tf.logging.info(np.array_repr(actual_prob_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[-0.2617511, -0.17463534, -1.1612566, 1.5976431], - [ 0.06115358, -1.5903126, 1.1505843, 0.37857458]], - [[ 0.821784, 1.0885929, -1.351966, -0.5584109], - [ 1.1864979, -1.5562507, -0.04089222, 0.41064504]], - [[ 1.5548539, -0.6477773, -1.0664893, 0.15941268], - [ 1.1784918, -1.5536082, 0.43964866, -0.06453241]], - [[-0.38961875, -1.4583365, 1.0075824, 0.84037286], - [ 1.5903242, -0.6370207, 0.07592358, -1.0292271]], - [[ 0.99643826, -1.232215, -0.73679215, 0.972569], - [-1.1702524, -0.5360445, 0.18702725, 1.5192697]]] - expected_prob_output = [ - [[ 0.21795762, 0., 0.26612395, 0., 0.31251648, 0., 0.20340192], - [ 0., 0.2677784 , 0., 0.32895881, 0., 0.40326279, 0.]], - [[ 0.25721505, 0., 0.24116731, 0., 0.25138181, 0., 0.2502358 ], - [ 0., 0.25691482, 0., 0.31076014, 0., 0.43232504, 0.]], - [[ 0.24550268, 0., 0.25128055, 0., 0.25109866, 0., 0.25211811], - [ 0., 0.26769161, 0., 0.32481128, 0., 0.40749705, 0.]], - [[ 0.22675318, 0., 0.26633731, 0., 0.28919035, 0., 0.21771915], - [ 0., 0.35955882, 0., 0.36869824, 0., 0.271743 , 0.]], - [[ 0.21504655, 0., 0.26958644, 0., 0.30847484, 0., 0.20689213], - [ 0., 0.29516917, 0., 0.29359812, 0., 0.41123265, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - self.assertAllClose(expected_prob_output, actual_prob_output) - - def testTransformerLayerFPropMultiPostProj(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - p.num_aux_atten_post_proj = 2 - transformer = layers_with_attention.TransformerLayer(p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - # Duplicate atten_idx n=2 times. - atten_idx = tf.constant([0, 1, 1, 0, 1] * 2, dtype=tf.int32) - h, probs = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - atten_idx=atten_idx) - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, actual_prob_output = self.evaluate([h, probs]) - tf.logging.info(np.array_repr(actual_layer_output)) - tf.logging.info(np.array_repr(actual_prob_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[-0.77411413, 0.86493313, 0.08914688, 1.4910977 ], - [-1.0093606 , -1.7337079 , 1.2784883 , 0.49974248]], - [[ 1.0396315 , 2.902943 , -1.1812847 , 0.19860795], - [-0.37676954, -0.79837584, 0.6419263 , 0.45496815]], - [[ 1.0858665 , -0.6838142 , -1.2464247 , 0.14764154], - [-0.45331526, -1.0229169 , 1.0660815 , -0.06151289]], - [[-1.3433903 , -1.3154784 , 1.1818855 , 0.790216 ], - [ 1.8400799 , -1.5192697 , 0.05896807, -1.94113 ]], - [[-0.11429042, -0.24730963, 0.06099784, 1.0156208 ], - [-1.9910344 , -0.5176018 , 0.2490384 , 1.3254449 ]]] - expected_prob_output = [ - [[ 0.21795762, 0., 0.26612395, 0., 0.31251648, 0., 0.20340192], - [ 0., 0.2677784 , 0., 0.32895881, 0., 0.40326279, 0.]], - [[ 0.25721505, 0., 0.24116731, 0., 0.25138181, 0., 0.2502358 ], - [ 0., 0.25691482, 0., 0.31076014, 0., 0.43232504, 0.]], - [[ 0.24550268, 0., 0.25128055, 0., 0.25109866, 0., 0.25211811], - [ 0., 0.26769161, 0., 0.32481128, 0., 0.40749705, 0.]], - [[ 0.22675318, 0., 0.26633731, 0., 0.28919035, 0., 0.21771915], - [ 0., 0.35955882, 0., 0.36869824, 0., 0.271743 , 0.]], - [[ 0.21504655, 0., 0.26958644, 0., 0.30847484, 0., 0.20689213], - [ 0., 0.29516917, 0., 0.29359812, 0., 0.41123265, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - self.assertAllClose(expected_prob_output, actual_prob_output) - - def testTransformerLayerWithInputPackingFProp(self): - with self.session(use_gpu=True): - with tf.variable_scope('transformer_packed_test', reuse=tf.AUTO_REUSE): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - packed_params = p.Copy() - transformer = layers_with_attention.TransformerLayer(p) - packed_params.packed_input = True - transformer_packed = layers_with_attention.TransformerLayer( - packed_params) - - dtype = tf.float32 - source_vecs = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(5) - ]) - source_padding = tf.transpose( - tf.constant([[0, 0, 0, 0, 1], [0, 0, 0, 0, 0]], dtype=dtype)) - aux_vecs = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7) - ]) - aux_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1]], - dtype=dtype)) - - source_vecs_packed = tf.reshape(source_vecs, [-1, 1, depth]) - aux_vecs_packed = tf.reshape(aux_vecs, [-1, 1, depth]) - source_padding_packed = tf.reshape(source_padding, [-1, 1]) - aux_padding_packed = tf.reshape(aux_paddings, [-1, 1]) - source_segment_id = tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]], dtype=tf.float32)) - aux_segment_id = tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]], - dtype=tf.float32)) - - h, _ = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - source_segment_id=None, - aux_segment_id=None) - - h_packed, _ = transformer_packed.FPropDefaultTheta( - source_vecs_packed, - source_padding_packed, - aux_vecs=aux_vecs_packed, - aux_paddings=aux_padding_packed, - source_segment_id=source_segment_id, - aux_segment_id=aux_segment_id) - h_packed = tf.reshape(h_packed, tf.shape(h)) - - self.evaluate(tf.global_variables_initializer()) - actual_layer, p_layer = self.evaluate([h, h_packed]) - self.assertAllClose(actual_layer, p_layer) - - def testTransformerLayerExtendStep(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_atten_tpl.num_attention_heads = 2 - transformer = layers_with_attention.TransformerLayer(p) - - (source_vecs, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - h1, probs1 = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - h2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, probs, prefix_states = transformer.ExtendStep( - transformer.theta, source_vecs[i, :, :], prefix_states, aux_vecs, - aux_paddings) - h2.append(h) - probs2.append(probs) - - h2 = tf.stack(h2) - probs2 = tf.concat(probs2, 0) - - self.evaluate(tf.global_variables_initializer()) - h1_v, probs1_v, h2_v, probs2_v = self.evaluate([h1, probs1, h2, probs2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testMultiAuxSourceTransformerLayerExtendStep(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.tr_aux_atten_tpl = ( - layers_with_attention.TransformerMultiSourceAttentionLayer.Params() - .Set( - source_dim=p.source_dim, - num_source=2, - primary_source_index=0, - num_attention_heads=4)) - p.mask_self_atten = True - p.tr_atten_tpl.num_attention_heads = 2 - transformer = layers_with_attention.TransformerLayer(p) - - (source_vecs, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputsMultiAuxSource( - ['source_0', 'source_1'], depth=depth) - source_padding = tf.zeros([5, 2]) - - h1, probs1 = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - h2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, probs, prefix_states = transformer.ExtendStep( - transformer.theta, source_vecs[i, :, :], prefix_states, aux_vecs, - aux_paddings) - h2.append(h) - probs2.append(probs) - - h2 = tf.stack(h2) - probs2 = tf.concat(probs2, 0) - - self.evaluate(tf.global_variables_initializer()) - h1_v, probs1_v, h2_v, probs2_v = self.evaluate([h1, probs1, h2, probs2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testMultiAuxSourceTransformerLayerExtendStepMatchSingle(self): - with self.session(use_gpu=True): - # Prepare inputs - np.random.seed(6348575) - depth = 4 - (source_vecs, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputsMultiAuxSource( - ['source_0', 'source_1'], depth=depth) - - # Multi-source transformer layer - p = layers_with_attention.TransformerLayer.Params().Set( - name='multi_source_trans', random_seed=123) - p.tr_atten_tpl.num_attention_heads = 4 - p.source_dim = depth - p.has_aux_atten = True - p.tr_aux_atten_tpl = ( - layers_with_attention.TransformerMultiSourceAttentionLayer.Params() - .Set( - source_dim=p.source_dim, - num_source=1, - primary_source_index=0, - num_attention_heads=4)) - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - msa_trans = layers_with_attention.TransformerLayer(p) - - h_msa = [] - probs_msa = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, probs, prefix_states = msa_trans.ExtendStep(msa_trans.theta, - source_vecs[i, :, :], - prefix_states, aux_vecs, - aux_paddings) - h_msa.append(h) - probs_msa.append(probs) - h_msa = tf.stack(h_msa) - probs_msa = tf.concat(probs_msa, 0) - - # Original single-source transformer decoder. - p = layers_with_attention.TransformerLayer.Params().Set( - name='single_source_trans', random_seed=123) - p.tr_atten_tpl.num_attention_heads = 4 - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - ssa_trans = layers_with_attention.TransformerLayer(p) - - h_ssa = [] - probs_ssa = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, probs, prefix_states = ssa_trans.ExtendStep(ssa_trans.theta, - source_vecs[i, :, :], - prefix_states, - aux_vecs['source_0'], - aux_paddings['source_0']) - h_ssa.append(h) - probs_ssa.append(probs) - h_ssa = tf.stack(h_ssa) - probs_ssa = tf.concat(probs_ssa, 0) - - self.evaluate(tf.global_variables_initializer()) - h_msa_v, h_ssa_v, probs_msa_v, probs_ssa_v = self.evaluate( - [h_msa, h_ssa, probs_msa, probs_ssa]) - tf.logging.info(np.array_repr(h_msa_v)) - tf.logging.info(np.array_repr(h_ssa_v)) - self.assertAllClose(h_msa_v, h_ssa_v) - self.assertAllClose(probs_msa_v, probs_ssa_v) - - def testTransformerLayerWithNgramMaskExtendStep(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_atten_tpl.num_attention_heads = 2 - # Turn on N-gram masking in the TransformerLayer. - # Before doing so though copy the self-attention params to avoid - # the auxilliary attention being masked as well. - p.tr_aux_atten_tpl = p.tr_atten_tpl.Copy() - p.tr_atten_tpl.is_masked = True - p.tr_atten_tpl.mask_ngram_order = 3 - p.tr_atten_tpl.mask_type = 'ngram' - transformer = layers_with_attention.TransformerLayer(p) - - (source_vecs, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - h1, probs1 = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - h2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, probs, prefix_states = transformer.ExtendStep( - transformer.theta, source_vecs[i, :, :], prefix_states, aux_vecs, - aux_paddings) - h2.append(h) - probs2.append(probs) - - h2 = tf.stack(h2) - probs2 = tf.concat(probs2, 0) - - self.evaluate(tf.global_variables_initializer()) - h1_v, probs1_v, h2_v, probs2_v = self.evaluate([h1, probs1, h2, probs2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testTransformerLayerWithPostLayernormExtendStep(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_atten_tpl.num_attention_heads = 2 - p.tr_post_ln_tpl = layers.LayerNorm.Params() - transformer = layers_with_attention.TransformerLayer(p) - - (source_vecs, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - h1, probs1 = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - h2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, probs, prefix_states = transformer.ExtendStep( - transformer.theta, source_vecs[i, :, :], prefix_states, aux_vecs, - aux_paddings) - h2.append(h) - probs2.append(probs) - - h2 = tf.stack(h2) - probs2 = tf.concat(probs2, 0) - - self.evaluate(tf.global_variables_initializer()) - h1_v, probs1_v, h2_v, probs2_v = self.evaluate([h1, probs1, h2, probs2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testEvolvedTransformerEncoderBranchedConvsLayer(self): - layer = layers_with_attention.EvolvedTransformerEncoderBranchedConvsLayer - with self.session(use_gpu=True): - tf.random.set_seed(3980847392) - inputs = tf.random.normal([5, 2, 3], seed=948387483) - paddings = tf.zeros([5, 2]) - p = layer.Params() - p.name = 'et_encoder_branched_convs' - p.input_dim = 3 - et_branched_convs = layer(p) - - h = et_branched_convs.FPropDefaultTheta(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate(h) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[-0.13232423, -0.46060669, 0.72598207], - [ 0.6725747 , 1.58664441, 2.64087844]], - [[-0.21702465, -0.68267912, 1.20886588], - [ 1.69793618, 0.53306532, 1.02958691]], - [[-0.46037287, -0.42950529, -1.68443251], - [ 0.21459752, 0.42246291, -0.01271994]], - [[-0.23293658, 0.15300342, -0.83518255], - [-0.48914853, -0.44239512, -0.2328119 ]], - [[-0.57934833, 0.24165238, -1.05392623], - [-0.8292231 , 0.06175411, 1.28672981]]] - # pyformat: enable - # pylint: enable=bad-whitespace - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testEvolvedTransformerDecoderBranchedConvsLayer(self): - layer = layers_with_attention.EvolvedTransformerDecoderBranchedConvsLayer - with self.session(use_gpu=True): - tf.random.set_seed(3980847392) - inputs = tf.random.normal([5, 2, 3], seed=948387483) - paddings = tf.zeros([5, 2]) - p = layer.Params() - p.name = 'et_decoder_branched_convs' - p.input_dim = 3 - et_branched_convs = layer(p) - - h = et_branched_convs.FPropDefaultTheta(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate(h) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[-0.31987068, -0.65715098, 0.90350437], - [ 0.00773269, 1.07779562, 4.11094666]], - [[-0.84862059, -0.93186408, 1.16371167], - [ 1.31467259, 0.03560367, 2.36822462]], - [[ 0.02183507, -0.0799394 , -1.68870354], - [ 0.77921551, 1.30145741, -0.86353606]], - [[ 0.31672907, 0.50000876, -0.93973017], - [-0.54707348, 0.19211179, -1.45307386]], - [[-0.46405494, 0.65833056, -1.09345317], - [-1.17221224, -0.08027397, 0.84021652]]] - # pyformat: enable - # pylint: enable=bad-whitespace - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testEvolvedTransformerEncoderLayerConstruction(self): - p = layers_with_attention.EvolvedTransformerEncoderLayer.Params() - p.name = 'evolved_transformer_encoder' - p.source_dim = 4 - p.transformer_tpl.tr_fflayer_tpl.hidden_dim = 7 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - _ = layers_with_attention.EvolvedTransformerEncoderLayer(p) - - def testEvolvedTransformerEncoderLayerFProp(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.EvolvedTransformerEncoderLayer.Params() - p.name = 'evolved_transformer_encoder' - p.source_dim = depth - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - transformer = layers_with_attention.EvolvedTransformerEncoderLayer(p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - h, probs = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, actual_prob_output = self.evaluate([h, probs]) - tf.logging.info(np.array_repr(actual_layer_output)) - tf.logging.info(np.array_repr(actual_prob_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[-1.69196141e+00, -5.03859818e-01, 2.42652583e+00, -1.47606134e+00], - [-1.27175665e+00, -1.81504273e+00, 7.16031432e-01, 1.40093648e+00]], - [[-1.83027089e-02, 8.73535872e-04, 2.04444170e+00, -2.74493122e+00], - [-6.90074801e-01, -2.06015229e-01, -1.21154499e+00, 1.17104244e+00]], - [[1.84662449e+00, 6.33037746e-01, -2.02951849e-01, -1.70747042e+00], - [-9.46833491e-01, -3.77074480e-01, -8.77807617e-01, 1.56822240e+00]], - [[-7.63426960e-01, -3.30413729e-01, 2.38541245e+00, -8.12596083e-01], - [1.91653550e+00, -1.47251439e+00, -2.19180465e+00, 5.32990336e-01]], - [[-1.46879780e+00, 5.38376629e-01, 1.50257730e+00, -8.52106392e-01], - [-1.55632758e+00, -3.48120153e-01, -9.21136498e-01, 2.02043033e+00]]] - expected_prob_output = [ - [[ 0.27934468, 0.28112975, 0., 0., 0.43952557], - [ 0., 0.24881637, 0.25068569, 0.50049794, 0. ]], - [[ 0.32433772, 0.33424711, 0., 0., 0.34141517], - [ 0., 0.33490175, 0.29024804, 0.37485027, 0. ]], - [[ 0.38673952, 0.33638299, 0., 0., 0.27687752], - [ 0., 0.30134204, 0.25906932, 0.43958867, 0. ]], - [[ 0.30161232, 0.33053303, 0., 0., 0.36785468], - [ 0., 0.36827549, 0.39080781, 0.24091662, 0. ]], - [[ 0.21496946, 0.22309673, 0., 0., 0.56193388], - [ 0., 0.27062884, 0.23510428, 0.4942669, 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - self.assertAllClose(expected_prob_output, actual_prob_output) - - def testEvolvedTransformerDecoderLayerConstruction(self): - p = layers_with_attention.EvolvedTransformerDecoderLayer.Params() - p.name = 'evolved_transformer_decoder' - p.source_dim = 16 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - p.has_aux_atten = True - p.mask_self_atten = True - _ = layers_with_attention.EvolvedTransformerDecoderLayer(p) - - def testEvolvedTransformerDecoderLayerFProp(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.EvolvedTransformerDecoderLayer.Params() - p.name = 'evolved_transformer_decoder' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_double_heads_atten_tpl.num_attention_heads = 2 - p.tr_atten_tpl.num_attention_heads = 2 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - transformer = layers_with_attention.EvolvedTransformerDecoderLayer(p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - h, probs = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, actual_prob_output = self.evaluate([h, probs]) - tf.logging.info(np.array_repr(actual_layer_output)) - tf.logging.info(np.array_repr(actual_prob_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output =[ - [[-2.15844011, 0.54941475, 1.01636434, 0.13751738], - [-1.31499887, -0.9501676, 0.874282, 0.58270419]], - [[-0.49268177, 2.71167898, -0.78087997, 0.43936318], - [-1.11428595, -1.38933206, 0.34404463, 0.43363893]], - [[ 0.57303172, 0.42080224, -0.50416583, -1.36097562], - [-1.26460135, -1.21081781, 0.9377467, 0.03642488]], - [[-1.52767372, -0.93615997, 1.33185053, 0.24640131], - [ 0.16062447, 2.39912128, 0.1896024, -0.70986807]], - [[-1.27725732, -1.51283062, 0.26704332, 0.65503371], - [-1.64287043, -0.30310085, -0.36987182, 1.57325172]]] - expected_prob_output = [ - [[0.28604817, 0., 0.24327257, 0., 0.26117378, 0., 0.20950545], - [0., 0.26639479, 0., 0.38120365, 0., 0.35240155, 0.]], - [[0.24309734, 0., 0.24040565, 0., 0.22922358, 0., 0.2872735], - [0., 0.27082229, 0., 0.36431897, 0., 0.36485875, 0.]], - [[0.25640261, 0., 0.25117433, 0., 0.25067171, 0., 0.24175137], - [0., 0.27037328, 0., 0.38163245, 0., 0.34799421, 0.]], - [[0.27474535, 0., 0.25523224, 0., 0.27800021, 0., 0.19202216], - [0., 0.34553668, 0., 0.35240823, 0., 0.30205506, 0.]], - [[0.24020916, 0., 0.25431803, 0., 0.26219654, 0., 0.24327625], - [0., 0.30723149, 0., 0.32563132, 0., 0.36713719, 0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - self.assertAllClose(expected_prob_output, actual_prob_output) - - def testEvolvedTransformerDecoderLayerExtendStep(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.EvolvedTransformerDecoderLayer.Params() - p.name = 'evolved_transformer_decoder' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_double_heads_atten_tpl.num_attention_heads = 2 - p.tr_atten_tpl.num_attention_heads = 2 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - et_decoder = layers_with_attention.EvolvedTransformerDecoderLayer(p) - - (source_vecs, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - h1, probs1 = et_decoder.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings) - - h2 = [] - probs2 = [] - - double_head_attention_states = py_utils.NestedMap( - key=tf.zeros([0, 2, 4]), value=tf.zeros([0, 2, 4])) - transformer_layer_states = py_utils.NestedMap( - key=tf.zeros([0, 2, 4]), value=tf.zeros([0, 2, 4])) - branched_convs_input = tf.zeros([0, 2, 4]) - - prefix_states = py_utils.NestedMap( - double_head_attention_states=double_head_attention_states, - transformer_layer_states=transformer_layer_states, - branched_convs_input=branched_convs_input) - - for i in range(5): - h, probs, prefix_states = et_decoder.ExtendStep(et_decoder.theta, - source_vecs[i, :, :], - prefix_states, aux_vecs, - aux_paddings) - h2.append(h) - probs2.append(probs) - - h2 = tf.stack(h2) - probs2 = tf.concat(probs2, 0) - - self.evaluate(tf.global_variables_initializer()) - h1_v, probs1_v, h2_v, probs2_v = self.evaluate([h1, probs1, h2, probs2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testStyleLayer(self): - with self.session(use_gpu=False): - p = layers_with_attention.StyleLayer.Params().Set( - name='style_layer', - input_dim=10, - output_dim=8, - num_styles=16, - random_seed=28384) - - tf.random.set_seed(8372749040) - np.random.seed(12345) - sl = p.Instantiate() - features = tf.random.normal([2, 10], seed=28384) - latent, atten_probs = sl.FPropDefaultTheta(features) - self.evaluate(tf.global_variables_initializer()) - latent_v, atten_probs_v = self.evaluate([latent, atten_probs]) - CompareToGoldenSingleFloat(self, -1.208686, np.sum(latent_v)) - CompareToGoldenSingleFloat(self, 2.0, np.sum(atten_probs_v)) - - def testStyleLayerWithFeedinAttenProbs(self): - with self.session(use_gpu=False): - p = layers_with_attention.StyleLayer.Params().Set( - name='style_layer', - input_dim=10, - output_dim=8, - num_styles=16, - num_heads=4, - enable_ctx_post_proj=False, - random_seed=28384) - - tf.random.set_seed(8372749040) - np.random.seed(12345) - sl = p.Instantiate() - atten_probs = tf.constant([[1.0] + [0.0] * 15] * 2, dtype=tf.float32) - ids = tf.constant([0, 0], dtype=tf.int32) - latent_from_probs = sl.StyleEmbFromProbs(sl.theta, atten_probs) - latent_from_lookup = sl.EmbLookup(sl.theta, ids) - self.evaluate(tf.global_variables_initializer()) - latent_p, latent_l = self.evaluate( - [latent_from_probs, latent_from_lookup]) - self.assertAllClose(latent_p, latent_l) - - def testStyleLayer02(self): - with self.session(use_gpu=False): - p = layers_with_attention.StyleLayer.Params().Set( - name='style_layer', - input_dim=10, - output_dim=8, - num_styles=16, - random_seed=72738) - tf.random.set_seed(8372749040) - np.random.seed(12345) - sl = p.Instantiate() - features = tf.random.normal([2, 10]) - features = tf.concat([features, features], 0) - latent, _ = sl.FPropDefaultTheta(features) - self.evaluate(tf.global_variables_initializer()) - latent_v = self.evaluate(latent) - # Makes sure identical input results in identical style output. - self.assertAllClose(latent_v[:2], latent_v[2:]) - - def _testTransformerMultitaskLayerInputs(self, depth=3, dtype=tf.float32): - np.random.seed(505837249) - source_vecs = tf.stack( - [tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(5)]) - source_padding = tf.transpose( - tf.constant([[0, 0, 1, 1, 0], [1, 0, 0, 0, 1]], dtype=dtype)) - aux_source_vecs = tf.stack( - [tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7)]) - aux_source_paddings = tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]], - dtype=dtype)) - source_task_id = tf.constant([[2, 3]], dtype=tf.int32) - return (source_vecs, source_padding, aux_source_vecs, aux_source_paddings, - source_task_id) - - def testTransformerLayerWithMultitaskAdaptersConstruction(self): - p = layers_with_attention.TransformerLayerWithMultitaskAdapters.Params() - p.name = 'transformer_with_adapters' - p.source_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - p.has_aux_atten = True - p.mask_self_atten = True - p.adapter_tpl.input_dim = 4 - p.adapter_tpl.num_tasks = 4 - p.adapter_tpl.bottleneck_dim = 2 - _ = layers_with_attention.TransformerLayerWithMultitaskAdapters(p) - - def testTransformerLayerWithMultitaskAdaptersFProp(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayerWithMultitaskAdapters.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - p.adapter_tpl.input_dim = 4 - p.adapter_tpl.num_tasks = 4 - p.adapter_tpl.bottleneck_dim = 2 - transformer = layers_with_attention.TransformerLayerWithMultitaskAdapters( - p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, - source_task_id) = self._testTransformerMultitaskLayerInputs(depth=depth) - - h, probs = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - source_task_id=source_task_id) - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, actual_prob_output = self.evaluate([h, probs]) - tf.logging.info(np.array_repr(actual_layer_output)) - tf.logging.info(np.array_repr(actual_prob_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[ 0.02441728, 0.26923186, 0.68582684, 1.1531992 ], - [ 0.69027936, -1.94770098, 2.00558615, 0.17057157]], - [[ 1.81022859, 2.37042093, 0.03620988, -0.32401592], - [ 1.66707945, -1.95131969, 0.64937419, 0.05853128]], - [[ 1.53475547, -0.60239077, -0.05797344, -0.48760295], - [ 1.53514266, -2.1231215 , 0.98074663, -0.5577352 ]], - [[-1.32504404, -1.28702664, 2.597996 , 0.24809647], - [ 3.7842629 , -1.46549737, 0.91363102, -2.37071466]], - [[ 0.52196532, -0.73371518, 0.86030912, 0.33838278], - [ 0.01923725, -0.8887378 , 1.08245265, 1.19935369]] - ] - expected_prob_output = [ - [[ 0.21795765, 0, 0.26612395, 0, 0.31251645, 0, 0.20340192], - [ 0, 0.2677784 , 0, 0.32895881, 0, 0.40326279, 0]], - [[ 0.25721508, 0, 0.24116732, 0, 0.25138181, 0, 0.2502358 ], - [ 0, 0.25691482, 0, 0.31076014, 0, 0.43232504, 0]], - [[ 0.24550268, 0, 0.25128055, 0, 0.25109866, 0, 0.25211811], - [ 0, 0.26769164, 0, 0.32481131, 0, 0.40749705, 0]], - [[ 0.22675318, 0, 0.26633731, 0, 0.28919035, 0, 0.21771917], - [ 0, 0.35955882, 0, 0.36869821, 0, 0.271743 , 0]], - [[ 0.21504655, 0, 0.26958644, 0, 0.30847484, 0, 0.20689213], - [ 0, 0.29516917, 0, 0.29359812, 0, 0.41123268, 0]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - self.assertAllClose(expected_prob_output, actual_prob_output) - - def testTransformerLayerWithMultitaskAdaptersWithInputPackingFProp(self): - with self.session(use_gpu=True): - with tf.variable_scope('transformer_packed_test', reuse=tf.AUTO_REUSE): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayerWithMultitaskAdapters.Params() - p.name = 'transformer_with_adapters' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - p.adapter_tpl.input_dim = 4 - p.adapter_tpl.num_tasks = 4 - p.adapter_tpl.bottleneck_dim = 2 - packed_params = p.Copy() - transformer = layers_with_attention.TransformerLayerWithMultitaskAdapters( - p) - packed_params.packed_input = True - transformer_packed = layers_with_attention.TransformerLayerWithMultitaskAdapters( - packed_params) - - dtype = tf.float32 - source_vecs = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(5) - ]) - source_padding = tf.transpose( - tf.constant([[0, 0, 0, 0, 1], [0, 0, 0, 0, 0]], dtype=dtype)) - aux_vecs = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7) - ]) - aux_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1]], - dtype=dtype)) - source_task_id = tf.constant([[2, 3]], dtype=tf.int32) - - source_vecs_packed = tf.reshape(source_vecs, [-1, 1, depth]) - aux_vecs_packed = tf.reshape(aux_vecs, [-1, 1, depth]) - source_padding_packed = tf.reshape(source_padding, [-1, 1]) - aux_padding_packed = tf.reshape(aux_paddings, [-1, 1]) - source_task_id_packed = tf.transpose( - tf.constant([[2, 3, 2, 3, 2, 3, 2, 3, 2, 3]], dtype=tf.int32)) - source_segment_id = tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]], dtype=tf.float32)) - aux_segment_id = tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]], - dtype=tf.float32)) - - h, _ = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - source_segment_id=None, - aux_segment_id=None, - source_task_id=source_task_id) - - h_packed, _ = transformer_packed.FPropDefaultTheta( - source_vecs_packed, - source_padding_packed, - aux_vecs=aux_vecs_packed, - aux_paddings=aux_padding_packed, - source_segment_id=source_segment_id, - aux_segment_id=aux_segment_id, - source_task_id=source_task_id_packed) - h_packed = tf.reshape(h_packed, tf.shape(h)) - - self.evaluate(tf.global_variables_initializer()) - actual_layer, p_layer = self.evaluate([h, h_packed]) - self.assertAllClose(actual_layer, p_layer) - - def testTransformerLayerWithMultitaskAdaptersExtendStep(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayerWithMultitaskAdapters.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_atten_tpl.num_attention_heads = 2 - p.adapter_tpl.input_dim = 4 - p.adapter_tpl.num_tasks = 4 - p.adapter_tpl.bottleneck_dim = 2 - transformer = layers_with_attention.TransformerLayerWithMultitaskAdapters( - p) - - (source_vecs, _, aux_vecs, aux_paddings, - source_task_id) = self._testTransformerMultitaskLayerInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - h1, probs1 = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - source_task_id=source_task_id) - - h2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, probs, prefix_states = transformer.ExtendStep( - transformer.theta, - source_vecs[i, :, :], - prefix_states, - aux_vecs, - aux_paddings, - source_task_id=source_task_id[0, :]) - h2.append(h) - probs2.append(probs) - - h2 = tf.stack(h2) - probs2 = tf.concat(probs2, 0) - - self.evaluate(tf.global_variables_initializer()) - h1_v, probs1_v, h2_v, probs2_v = self.evaluate([h1, probs1, h2, probs2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testCCTFeedForwardLayerConstruction(self): - p = layers_with_attention.CCTFeedForwardLayer.Params() - p.name = 'cct_fflayer_1' - p.input_dim = 3 - p.hidden_dim = 7 - p.num_blocks = 2 - p.gating_tpl.hidden_layer_dim = 2 - p.gating_tpl.noise_std = 5.0 - p.gating_tpl.noise_warmup_steps = 100 - _ = layers_with_attention.CCTFeedForwardLayer(p) - - def testCCTFeedForwardLayerTraining(self): - with self.session(use_gpu=True): - tf.random.set_seed(3980847392) - inputs = tf.random.normal([5, 2, 3], seed=948387483) - paddings = tf.zeros([5, 2]) - p = layers_with_attention.CCTFeedForwardLayer.Params() - p.name = 'transformer_fflayer' - p.input_dim = 3 - p.hidden_dim = 7 - p.num_blocks = 2 - p.gating_tpl.hidden_layer_dim = 2 - p.gating_tpl.noise_std = 5.0 - p.gating_tpl.noise_warmup_steps = 100 - cct_fflayer = layers_with_attention.CCTFeedForwardLayer(p) - - h, p_c = cct_fflayer.FPropDefaultTheta(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, p_c_val = self.evaluate([h, p_c]) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 0.49714983, -1.1684668 , 0.4889576 ], - [ 1.7869478 , 1.4456576 , 1.4123362 ]], - [[ 0.10564739, -1.5359519 , 0.67742175], - [ 1.6211604 , 0.583192 , 1.056936 ]], - [[-0.01121134, -0.78554434, -0.84111285], - [ 0.45078042, 0.63005054, 0.08024757]], - [[ 0.162924 , 0.14500974, -0.32797086], - [ 0.41885388, -0.5852693 , -1.7245001 ]], - [[-0.6601118 , 0.30835745, -0.48543385], - [-0.04813027, -0.04633661, -0.21723843]]] - expected_p_c = [ - [[0.5607947 , 0.49624035], - [0.72082597, 0.50216115]], - [[0.6352798 , 0.49843985], - [0.5 , 0.5 ]], - [[0.5 , 0.5 ], - [0.5 , 0.5 ]], - [[0.5 , 0.5 ], - [0.7562946 , 0.50510687]], - [[0.62267053, 0.50738835], - [0.73273706, 0.5029184 ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(actual_layer_output, expected_output) - self.assertAllClose(p_c_val, expected_p_c) - - def testCCTFeedForwardLayerInference(self): - with self.session(use_gpu=True), self.SetEval(True): - tf.random.set_seed(3980847392) - inputs = tf.random.normal([5, 2, 3], seed=948387483) - paddings = tf.zeros([5, 2]) - p = layers_with_attention.CCTFeedForwardLayer.Params() - p.name = 'transformer_fflayer' - p.input_dim = 3 - p.hidden_dim = 7 - p.num_blocks = 2 - p.gating_tpl.hidden_layer_dim = 2 - p.gating_tpl.noise_std = 5.0 - p.gating_tpl.noise_warmup_steps = 100 - cct_fflayer = layers_with_attention.CCTFeedForwardLayer(p) - - h, p_c = cct_fflayer.FPropDefaultTheta(inputs, paddings) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, p_c_val = self.evaluate([h, p_c]) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_output = [ - [[ 1.1921753 , -0.78980637, -0.58472836], - [ 2.5051842 , 1.6491661 , 0.49059153]], - [[ 0.6877271 , -1.1452659 , -0.29534382], - [ 1.5774723 , 0.6462606 , 1.0375552 ]], - [[ 0.12175584, -1.2262938 , -0.5333306 ], - [ 0.4632102 , 0.7119628 , -0.01409443]], - [[ 0.16090955, 0.06721614, -0.24816278], - [ 0.9799552 , -0.2861529 , -2.5847178 ]], - [[-0.48719 , 0.18763718, -0.53763545], - [ 0.5886377 , 0.21293162, -1.1132748 ]] - ] - expected_p_c = [ - [[1., 0.], - [1., 1.]], - [[1., 0.], - [1., 1.]], - [[1., 1.], - [1., 1.]], - [[1., 1.], - [1., 1.]], - [[1., 1.], - [1., 1.]] - ] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(actual_layer_output, expected_output) - self.assertAllClose(p_c_val, expected_p_c) - - def testTransformerWithContextLayerConstruction(self): - p = layers_with_attention.TransformerWithContextLayer.Params() - p.name = 'transformer_1' - p.source_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - layer = p.Instantiate() - # output_dim is equal to source_dim when p.output_dim == 0 - self.assertEqual(0, p.output_dim) - self.assertEqual(p.source_dim, layer.fflayer.output_dim) - - def testTransformerWithContextLayerFProp(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerWithContextLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - transformer = p.Instantiate() - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth) - - h, probs = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - tertiary_vecs=aux_vecs, - tertiary_paddings=aux_paddings) - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output, actual_prob_output = self.evaluate([h, probs]) - tf.logging.info(np.array_repr(actual_layer_output)) - tf.logging.info(np.array_repr(actual_prob_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[ 0.55129296, -0.7571765 , 0.281192 , 0.8710322 ], - [ 0.5072957 , -1.3714458 , 1.5689826 , -0.0971924 ]], - [[ 2.2560897 , 2.7890472 , 0.016873 , -0.5172725 ], - [ 1.4128124 , -2.0595124 , 0.37241971, -0.6075135 ]], - [[ 2.57011 , -0.8678784 , -0.33203793, -0.18508816], - [ 1.3549538 , -2.0990794 , 0.62103236, -0.9975941 ]], - [[ 0.15144205, -1.1681134 , 1.7113727 , 0.4682465 ], - [ 2.9454587 , -1.4413761 , 0.5215157 , -2.1541023 ]], - [[ 1.5092299 , -1.7608491 , 0.21144068, 0.22785848], - [-0.766488 , -0.487573 , 1.0574573 , 0.81118184]]] - expected_prob_output = [ - [[0.223735 , 0. , 0.26685917, 0. , 0.2968173 , - 0. , 0.2125885 ], - [0. , 0.28585374, 0. , 0.35088098, 0. , - 0.36326528, 0. ]], - [[0.2703818 , 0. , 0.23092957, 0. , 0.2249705 , - 0. , 0.27371815], - [0. , 0.26997963, 0. , 0.33745134, 0. , - 0.39256904, 0. ]], - [[0.25208434, 0. , 0.24830116, 0. , 0.23168065, - 0. , 0.26793382], - [0. , 0.2847324 , 0. , 0.3477454 , 0. , - 0.36752218, 0. ]], - [[0.23778549, 0. , 0.26169604, 0. , 0.26542395, - 0. , 0.23509452], - [0. , 0.3603859 , 0. , 0.37519425, 0. , - 0.26441985, 0. ]], - [[0.22522289, 0. , 0.26782405, 0. , 0.28599125, - 0. , 0.22096181], - [0. , 0.29979968, 0. , 0.31155068, 0. , - 0.38864967, 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - self.assertAllClose(expected_prob_output, actual_prob_output) - - def testTransformerWithContextLayerPackedInputFProp(self): - with self.session(use_gpu=True): - with tf.variable_scope('transformer_packed_test', reuse=tf.AUTO_REUSE): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - transformer = p.Instantiate() - packed_params = p.Copy() - packed_params.packed_input = True - transformer_packed = packed_params.Instantiate() - - dtype = tf.float32 - source_vecs = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(5) - ]) - source_padding = tf.transpose( - tf.constant([[0, 0, 0, 0, 1], [0, 0, 0, 0, 0]], dtype=dtype)) - aux_vecs = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7) - ]) - tertiary_vecs = tf.stack([ - tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7) - ]) - aux_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1]], - dtype=dtype)) - - source_vecs_packed = tf.reshape(source_vecs, [-1, 1, depth]) - aux_vecs_packed = tf.reshape(aux_vecs, [-1, 1, depth]) - tertiary_vecs_packed = tf.reshape(tertiary_vecs, [-1, 1, depth]) - source_padding_packed = tf.reshape(source_padding, [-1, 1]) - aux_padding_packed = tf.reshape(aux_paddings, [-1, 1]) - source_segment_id = tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]], dtype=tf.float32)) - aux_segment_id = tf.transpose( - tf.constant([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]], - dtype=tf.float32)) - - h, _ = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - tertiary_vecs=tertiary_vecs, - tertiary_paddings=aux_paddings) - - h_packed, _ = transformer_packed.FPropDefaultTheta( - source_vecs_packed, - source_padding_packed, - aux_vecs=aux_vecs_packed, - aux_paddings=aux_padding_packed, - source_segment_id=source_segment_id, - aux_segment_id=aux_segment_id, - tertiary_vecs=tertiary_vecs_packed, - tertiary_paddings=aux_padding_packed, - tertiary_segment_id=aux_segment_id) - h_packed = tf.reshape(h_packed, tf.shape(h)) - - self.evaluate(tf.global_variables_initializer()) - actual_layer, p_layer = self.evaluate([h, h_packed]) - self.assertAllClose(actual_layer, p_layer) - - def testTransformerWithContextLayerExtendStep(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = layers_with_attention.TransformerWithContextLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.tr_atten_tpl.num_attention_heads = 2 - transformer = p.Instantiate() - - (source_vecs, source_padding, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth) - source_padding = tf.zeros([5, 2]) - - h1, probs1 = transformer.FPropDefaultTheta( - source_vecs, - source_padding, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - tertiary_vecs=aux_vecs, - tertiary_paddings=aux_paddings) - - h2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, probs, prefix_states = transformer.ExtendStep( - transformer.theta, - source_vecs[i, :, :], - prefix_states, - aux_vecs, - aux_paddings, - tertiary_vecs=aux_vecs, - tertiary_paddings=aux_paddings) - h2.append(h) - probs2.append(probs) - - h2 = tf.stack(h2) - probs2 = tf.concat(probs2, 0) - - self.evaluate(tf.global_variables_initializer()) - h1_v, probs1_v, h2_v, probs2_v = self.evaluate([h1, probs1, h2, probs2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testCCTAttentionLayerSelfAttentionTraining(self): - with self.session(use_gpu=True) as sess: - depth = 4 - p = layers_with_attention.CCTAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = True - p.num_attention_heads = 2 - p.gating_tpl.hidden_layer_dim = 2 - p.gating_tpl.noise_std = 5.0 - p.gating_tpl.noise_warmup_steps = 100 - transformer_atten = layers_with_attention.CCTAttentionLayer(p) - - (source_vecs, source_padding, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - ctx, probs, qpc, spc = transformer_atten.FPropDefaultTheta( - source_vecs, source_padding) - tf.global_variables_initializer().run() - actual_ctx, actual_probs, actual_qpc, actual_spc = sess.run( - [ctx, probs, qpc, spc]) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-0.9170906 , 0.89127994, 0.8682031 , -0.8423924 ], - [-1.2874005 , -0.76474655, 0.5771928 , 1.4749541 ]], - [[ 0.34465155, 0.74996084, -0.48622286, -0.6083897 ], - [-0.7486481 , -0.07628638, -0.99187833, 1.8168143 ]], - [[ 1.6986014 , -0.44173932, -0.7130059 , -0.5438557 ], - [-1.3927674 , -0.09861529, 0.3361559 , 1.1552272 ]], - [[-0.5439662 , -1.0707575 , 1.8813989 , -0.26667514], - [ 1.1484473 , 0.9964316 , -1.2344118 , -0.91046673]], - [[-0.06898946, -1.5815425 , -0.45298773, 2.1035194 ], - [-1.7475295 , 0.27231437, -0.8034381 , 2.2786536 ]]] - expected_probs = [ - [[1. , 0. , 0. , 0. , 0. ], - [0.2 , 0.2 , 0.2 , 0.2 , 0.2 ]], - [[0.4238176 , 0.57618237, 0. , 0. , 0. ], - [0. , 1. , 0. , 0. , 0. ]], - [[0.34105754, 0.65894246, 0. , 0. , 0. ], - [0. , 0.55719167, 0.44280833, 0. , 0. ]], - [[0.6528083 , 0.34719166, 0. , 0. , 0. ], - [0. , 0.32477915, 0.36445653, 0.31076428, 0. ]], - [[0.28325003, 0.21873125, 0. , 0. , 0.49801874], - [0. , 0.43867606, 0.2793855 , 0.28193837, 0. ]]] - expected_qpc = [ - [[0.5 ], - [0.5818492 ]], - [[0.5411409 ], - [0.55023897]], - [[0.56948507], - [0.5499979 ]], - [[0.5166038 ], - [0.58645904]], - [[0.54155153], - [0.5 ]]] - expected_spc = [ - [[0.21472901], - [0.06997871]], - [[0.53207266], - [0.39812705]], - [[0.5217048 ], - [0.07829338]], - [[0.06743541], - [0.5 ]], - [[0.32987863], - [0.5442441 ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_qpc, actual_qpc, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_spc, actual_spc, rtol=1e-05, atol=1e-05) - - def testCCTAttentionLayerSelfAttentionEval(self): - with self.session(use_gpu=True) as sess, self.SetEval(True): - depth = 4 - p = layers_with_attention.CCTAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = True - p.num_attention_heads = 2 - p.gating_tpl.hidden_layer_dim = 2 - p.gating_tpl.noise_std = 5.0 - p.gating_tpl.noise_warmup_steps = 100 - transformer_atten = layers_with_attention.CCTAttentionLayer(p) - - (source_vecs, source_padding, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - ctx, probs, qpc, spc = transformer_atten.FPropDefaultTheta( - source_vecs, source_padding) - tf.global_variables_initializer().run() - actual_ctx, actual_probs, actual_qpc, actual_spc = sess.run( - [ctx, probs, qpc, spc]) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.5939784e+00, 8.5430717e-01, 8.4722424e-01, -1.0755297e-01], - [-1.6199683e+00, -1.9144357e+00, 1.0950426e+00, 2.4393613e+00]], - [[ 2.0492536e-01, -5.0217152e-02, -1.5521961e-01, 5.1122904e-04], - [-4.3141130e-01, -9.0650195e-01, -3.5488802e-01, 1.6928028e+00]], - [[ 1.7034934e+00, -1.1774492e+00, -4.2603785e-01, -1.0000569e-01], - [-1.0880733e+00, -9.0783793e-01, 9.9768031e-01, 9.9823117e-01]], - [[-1.1584746e+00, -2.0163212e+00, 2.3776212e+00, 7.9717481e-01], - [ 1.3303024e+00, -1.4763023e+00, 2.6441175e-01, -1.1841190e-01]], - [[-3.0323851e-01, -2.5461116e+00, 5.0698155e-01, 2.3423686e+00], - [-2.0771229e+00, -8.0027932e-01, -7.4258000e-02, 2.9516606e+00]]] - expected_probs = [ - [[1. , 0. , 0. , 0. , 0. ], - [0.2 , 0.2 , 0.2 , 0.2 , 0.2 ]], - [[0.35538384, 0.6446162 , 0. , 0. , 0. ], - [0. , 1. , 0. , 0. , 0. ]], - [[0.18125553, 0.8187444 , 0. , 0. , 0. ], - [0. , 0.5 , 0.5 , 0. , 0. ]], - [[0.7752405 , 0.22475953, 0. , 0. , 0. ], - [0. , 0.36166608, 0.36166608, 0.27666792, 0. ]], - [[0.40603536, 0.18792923, 0. , 0. , 0.40603536], - [0. , 0.32476988, 0.32476988, 0.35046023, 0. ]]] - expected_qpc = [ - [[1.], - [1.]], - [[1.], - [1.]], - [[1.], - [1.]], - [[1.], - [1.]], - [[1.], - [1.]]] - expected_spc = [ - [[0.], - [0.]], - [[1.], - [0.]], - [[1.], - [0.]], - [[0.], - [1.]], - [[0.], - [1.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_qpc, actual_qpc, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_spc, actual_spc, rtol=1e-05, atol=1e-05) - - def testCCTAttentionLayerStepByStep(self): - with self.session(use_gpu=True) as sess, self.SetEval(True): - depth = 4 - p = layers_with_attention.CCTAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = True - p.num_attention_heads = 2 - p.gating_tpl.hidden_layer_dim = 2 - p.gating_tpl.noise_std = 5.0 - p.gating_tpl.noise_warmup_steps = 100 - x_atten = layers_with_attention.CCTAttentionLayer(p) - - (source_vecs, _, _, _, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - ctx1, probs1, _, _ = x_atten.FPropDefaultTheta(source_vecs, - source_padding) - ctx2 = [] - probs2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - ctx, probs, prefix_states = x_atten.ExtendStep(x_atten.theta, - source_vecs[i, :, :], - prefix_states) - probs_pad = tf.zeros([2, 5 - i - 1]) - padded_probs = tf.concat([probs, probs_pad], 1) - ctx2.append(ctx) - probs2.append(padded_probs) - - ctx2 = tf.stack(ctx2) - probs2 = tf.stack(probs2) - - tf.global_variables_initializer().run() - ctx1_v, probs1_v, ctx2_v, probs2_v = sess.run( - [ctx1, probs1, ctx2, probs2]) - self.assertAllClose(ctx1_v, ctx2_v) - self.assertAllClose(probs1_v, probs2_v) - - def testCCTAttentionLayerCrossAttenTraining(self): - with self.session(use_gpu=True) as sess: - depth = 4 - p = layers_with_attention.CCTAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - p.gating_tpl.hidden_layer_dim = 2 - p.gating_tpl.noise_std = 5.0 - p.gating_tpl.noise_warmup_steps = 100 - transformer_atten = layers_with_attention.CCTAttentionLayer(p) - - (query_vec, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - ctx, probs, qpc, spc = transformer_atten.FPropDefaultTheta( - query_vec, aux_paddings, aux_vecs) - tf.global_variables_initializer().run() - actual_ctx, actual_probs, actual_qpc, actual_spc = sess.run( - [ctx, probs, qpc, spc]) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.9043474 , 1.6999874 , 0.4292767 , -0.22491673], - [-0.84242177, -0.50577486, 0.29762083, 1.0505756 ]], - [[-0.33607534, 2.5800223 , -1.3375163 , -0.90643084], - [-0.4973639 , -0.17019022, -1.1589761 , 1.8265318 ]], - [[ 1.1859869 , 1.5021455 , -1.6327672 , -1.0553647 ], - [-1.2359238 , -0.22244841, 0.19330817, 1.2650642 ]], - [[-1.5131142 , 0.49699292, 1.129034 , -0.11291274], - [ 2.1162672 , 0.6308829 , -1.0373113 , -1.7098385 ]], - [[-0.9935959 , 0.07386243, -0.6836246 , 1.6033579 ], - [-1.0807116 , 0.85268646, -1.2622242 , 1.4902495 ]]] - expected_probs = [ - [[0.24303743, 0. , 0.30685946, 0. , 0.25564623, - 0. , 0.1944569 ], - [0. , 0.28801104, 0. , 0.34431183, 0. , - 0.36767715, 0. ]], - [[0.2644446 , 0. , 0.23458862, 0. , 0.23393473, - 0. , 0.26703206], - [0. , 0.22837642, 0. , 0.2820819 , 0. , - 0.4895417 , 0. ]], - [[0.2599384 , 0. , 0.19412258, 0. , 0.21307275, - 0. , 0.33286628], - [0. , 0.27514488, 0. , 0.35259444, 0. , - 0.3722607 , 0. ]], - [[0.24153353, 0. , 0.3045342 , 0. , 0.2569951 , - 0. , 0.19693717], - [0. , 0.36325702, 0. , 0.26765382, 0. , - 0.36908916, 0. ]], - [[0.21663833, 0. , 0.28198314, 0. , 0.29308724, - 0. , 0.20829134], - [0. , 0.2337277 , 0. , 0.319759 , 0. , - 0.44651327, 0. ]]] - expected_qpc = [ - [[0.5 ], - [0.5818492 ]], - [[0.541141 ], - [0.55023897]], - [[0.56948507], - [0.5499979 ]], - [[0.5166038 ], - [0.58645904]], - [[0.54155153], - [0.5 ]]] - expected_spc = [ - [[0.09838167], - [0.5 ]], - [[0.51203823], - [0.22011107]], - [[0.27349436], - [0.5230051 ]], - [[0.5 ], - [0.0911701 ]], - [[0.2730832 ], - [0.5 ]], - [[0.54982626], - [0.44889307]], - [[0.10193098], - [0.11123485]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_qpc, actual_qpc, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_spc, actual_spc, rtol=1e-05, atol=1e-05) - - def testCCTAttentionLayerCrossAttenEval(self): - with self.session(use_gpu=True) as sess, self.SetEval(True): - depth = 4 - p = layers_with_attention.CCTAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - p.gating_tpl.hidden_layer_dim = 2 - p.gating_tpl.noise_std = 5.0 - p.gating_tpl.noise_warmup_steps = 100 - transformer_atten = layers_with_attention.CCTAttentionLayer(p) - - (query_vec, _, aux_vecs, aux_paddings, - _) = self._testTransformerAttentionLayerInputs(depth=depth) - - ctx, probs, qpc, spc = transformer_atten.FPropDefaultTheta( - query_vec, aux_paddings, aux_vecs) - tf.global_variables_initializer().run() - actual_ctx, actual_probs, actual_qpc, actual_spc = sess.run( - [ctx, probs, qpc, spc]) - tf.logging.info(np.array_repr(actual_ctx)) - tf.logging.info(np.array_repr(actual_probs)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.5939784 , 0.8543072 , 0.84722424, -0.10755297], - [-0.7121205 , -1.2363338 , 1.1559415 , 0.7925127 ]], - [[-0.09044743, 1.6572162 , -0.87628996, -0.69047904], - [-0.4314113 , -0.90650195, -0.35488802, 1.6928028 ]], - [[ 1.3591317 , 0.5376119 , -1.1282029 , -0.7685402 ], - [-1.0880733 , -0.9078379 , 0.9976803 , 0.9982312 ]], - [[-1.1870676 , -0.37413225, 1.5655125 , -0.00431258], - [ 1.62277 , 0.02716666, -0.7765793 , -0.87335706]], - [[-0.6675403 , -0.8283625 , -0.18727894, 1.6831816 ], - [-1.113929 , 0.13246097, -0.57226247, 1.5537308 ]]] - expected_probs = [ - [[0.25 , 0. , 0.25 , 0. , 0.25 , - 0. , 0.25 ], - [0. , 0.33333334, 0. , 0.33333334, 0. , - 0.33333334, 0. ]], - [[0.25 , 0. , 0.25 , 0. , 0.25 , - 0. , 0.25 ], - [0. , 0.33333334, 0. , 0.33333334, 0. , - 0.33333334, 0. ]], - [[0.25 , 0. , 0.25 , 0. , 0.25 , - 0. , 0.25 ], - [0. , 0.33333334, 0. , 0.33333334, 0. , - 0.33333334, 0. ]], - [[0.25 , 0. , 0.25 , 0. , 0.25 , - 0. , 0.25 ], - [0. , 0.33333334, 0. , 0.33333334, 0. , - 0.33333334, 0. ]], - [[0.25 , 0. , 0.25 , 0. , 0.25 , - 0. , 0.25 ], - [0. , 0.33333334, 0. , 0.33333334, 0. , - 0.33333334, 0. ]]] - expected_qpc = [ - [[1.], - [1.]], - [[1.], - [1.]], - [[1.], - [1.]], - [[1.], - [1.]], - [[1.], - [1.]]] - expected_spc = [ - [[0.], - [1.]], - [[1.], - [0.]], - [[0.], - [1.]], - [[1.], - [0.]], - [[0.], - [1.]], - [[1.], - [0.]], - [[0.], - [0.]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_ctx, actual_ctx, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_probs, actual_probs, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_qpc, actual_qpc, rtol=1e-05, atol=1e-05) - self.assertAllClose(expected_spc, actual_spc, rtol=1e-05, atol=1e-05) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/layers_with_gpipe.py b/lingvo/core/layers_with_gpipe.py deleted file mode 100644 index 66bcb8a1..00000000 --- a/lingvo/core/layers_with_gpipe.py +++ /dev/null @@ -1,1432 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Lingvo layers that depend on layers and gpipe.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import batch_major_attention -from lingvo.core import layers -from lingvo.core import layers_with_attention -from lingvo.core import py_utils -from lingvo.core import tshape -from lingvo.core.gpipe import FeatureExtractionLayer -from lingvo.core.gpipe import PipeliningLayer - - -def _common_gpipe_transformer_params(p): - """Add GPipe params to layer.""" - p.Define( - 'is_transparent', False, - 'If set, encoder outputs a list of layer outputs while decoder ' - 'expects a list of source input vectors.') - p.Define('transparent_merger_tpl', None, - 'Creates weights for transparent combination.') - p.Define( - 'final_enc_layer', False, - 'True for final encoder layer. To be used for final transparent merger.') - p.Define( - 'normalize_output', False, - 'If set, encoder outputs a list of layer outputs while decoder ' - 'expects a list of source input vectors.') - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params') - return p - - -def _common_gpipe_transformer_init(layer): - """Initialize a GPipe layer.""" - p = layer.params - - if p.normalize_output: - params = p.ln_tpl.Copy() - params.name = 'encoder_ln' - params.input_dim = p.source_dim - layer.CreateChild('layer_norm', params) - - if p.is_transparent and p.transparent_merger_tpl is not None: - transparent_param = p.transparent_merger_tpl.Copy() - transparent_param.name = 'transparent_0' - layer.CreateChild('transparent_merger', transparent_param) - assert p.name - - -def _common_gpipe_transformer_encoder_fprop( - layer, layer_class, theta, source_vecs, source_paddings, target_vecs, - target_paddings, source_segment_id, target_segment_id, transparent_acc, - transparent_acc_helper, source_task_id, target_task_id): - """GPipe encoder FProp.""" - p = layer.params - if source_task_id is not None or target_task_id is not None: - h, _ = super(layer_class, layer).FProp( - theta, - source_vecs, - source_paddings, - source_segment_id=source_segment_id, - source_task_id=source_task_id, - target_task_id=target_task_id) - else: - h, _ = super(layer_class, layer).FProp( - theta, - source_vecs, - source_paddings, - source_segment_id=source_segment_id) - h.set_shape(source_vecs.shape) - if p.is_transparent: - if p.transparent_merger_tpl is not None: - transparent_acc_helper = layer.transparent_merger.FProp( - theta.transparent_merger) - transparent_acc = tf.zeros_like(source_vecs) - transparent_acc = transparent_acc + transparent_acc_helper[0] * source_vecs - if p.final_enc_layer: - h = transparent_acc + h * transparent_acc_helper[-1] - transparent_acc = None - transparent_acc_helper = None - else: - transparent_acc_helper = transparent_acc_helper[1:] - if p.normalize_output: - h = layer.layer_norm.FProp(theta.layer_norm, h) - - if source_task_id is not None or target_task_id is not None: - return (h, source_paddings, target_vecs, target_paddings, source_segment_id, - target_segment_id, transparent_acc, transparent_acc_helper, - source_task_id, target_task_id) - else: - return (h, source_paddings, target_vecs, target_paddings, source_segment_id, - target_segment_id, transparent_acc, transparent_acc_helper) - - -def _common_gpipe_transformer_decoder_fprop( - layer, layer_class, theta, source_vecs, source_paddings, target_vecs, - target_paddings, source_segment_id, target_segment_id, transparent_acc, - transparent_acc_helper, source_task_id, target_task_id): - """GPipe decoder FProp.""" - assert target_vecs is not None - assert target_paddings is not None - if source_task_id is not None or target_task_id is not None: - h, _ = super(layer_class, layer).FProp( - theta, - target_vecs, - target_paddings, - aux_vecs=source_vecs, - aux_paddings=source_paddings, - source_segment_id=target_segment_id, - aux_segment_id=source_segment_id, - source_task_id=source_task_id, - target_task_id=target_task_id) - else: - h, _ = super(layer_class, layer).FProp( - theta, - target_vecs, - target_paddings, - aux_vecs=source_vecs, - aux_paddings=source_paddings, - source_segment_id=target_segment_id, - aux_segment_id=source_segment_id) - h.set_shape(target_vecs.shape) - - if source_task_id is not None or target_task_id is not None: - return (source_vecs, source_paddings, h, target_paddings, source_segment_id, - target_segment_id, transparent_acc, transparent_acc_helper, - source_task_id, target_task_id) - else: - return (source_vecs, source_paddings, h, target_paddings, source_segment_id, - target_segment_id, transparent_acc, transparent_acc_helper) - - -def _common_gpipe_transformer_fprop_meta(p, inputs, *args): - """GPipe FPropMeta function.""" - # TODO(huangyp): return accurate estimate of flops. - py_utils.CheckShapes((inputs,)) - flops_per_element = 5 - src_time, source_batch, dim = inputs - flops = flops_per_element * src_time * src_time * source_batch * dim - args = args if isinstance(args, tuple) else (args,) - if not p.has_aux_atten and p.is_transparent: # Transparent Encoder FPropMeta - if p.transparent_merger_tpl is not None: - args = args[:5] + (inputs, - tshape.Shape([p.transparent_merger_tpl.num_sources])) - args = args[:6] + (tshape.Shape([args[6][0] - 1]),) - if p.final_enc_layer: - args = args[:5] + (None, None) - return py_utils.NestedMap(flops=flops, out_shapes=(inputs,) + args) - - -class GPipeTransformerLayer(layers_with_attention.TransformerLayer): - """GPipe compatible transformer layer.""" - - @classmethod - def Params(cls): - """Configs for TransformerStack.""" - p = super().Params() - return _common_gpipe_transformer_params(p) - - def __init__(self, params): - super().__init__(params) - _common_gpipe_transformer_init(self) - - @classmethod - def SetupDeterministicDropout(cls, params): - """Replaced dropout layers in transformer with deterministic ones.""" - params.tr_atten_tpl.residual_dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - params.tr_atten_tpl.atten_tpl.atten_dropout_deterministic = True - params.tr_atten_tpl.atten_tpl.inner_atten_params \ - .atten_dropout_deterministic = True - params.tr_fflayer_tpl.residual_dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - params.tr_fflayer_tpl.fflayer_tpl.dropout = ( - layers.DeterministicDropoutLayer.Params()) - return params - - def FProp(self, - theta, - source_vecs, - source_paddings, - target_vecs, - target_paddings, - source_segment_id, - target_segment_id, - transparent_acc, - transparent_acc_helper, - source_task_id=None, - target_task_id=None): - p = self.params - with tf.name_scope(p.name): - if p.has_aux_atten: # Decoder FProp - return _common_gpipe_transformer_decoder_fprop( - self, GPipeTransformerLayer, theta, source_vecs, source_paddings, - target_vecs, target_paddings, source_segment_id, target_segment_id, - transparent_acc, transparent_acc_helper, source_task_id, - target_task_id) - else: # Encoder FProp - return _common_gpipe_transformer_encoder_fprop( - self, GPipeTransformerLayer, theta, source_vecs, source_paddings, - target_vecs, target_paddings, source_segment_id, target_segment_id, - transparent_acc, transparent_acc_helper, source_task_id, - target_task_id) - - @classmethod - def FPropMeta(cls, p, inputs, *args): - return _common_gpipe_transformer_fprop_meta(p, inputs, *args) - - -class GPipeEvolvedTransformerEncoderLayer( - layers_with_attention.EvolvedTransformerEncoderLayer): - """GPipe-compatible Evolved Transformer encoder layer.""" - - @classmethod - def Params(cls): - p = super().Params() - return _common_gpipe_transformer_params(p) - - def __init__(self, params): - super().__init__(params) - _common_gpipe_transformer_init(self) - - def FProp(self, - theta, - source_vecs, - source_paddings, - target_vecs, - target_paddings, - source_segment_id, - target_segment_id, - transparent_acc, - transparent_acc_helper, - source_task_id=None, - target_task_id=None): - with tf.name_scope(self.params.name): - return _common_gpipe_transformer_encoder_fprop( - self, GPipeEvolvedTransformerEncoderLayer, theta, source_vecs, - source_paddings, target_vecs, target_paddings, source_segment_id, - target_segment_id, None, None, source_task_id, target_task_id) - - @classmethod - def FPropMeta(cls, p, inputs, *args): - return _common_gpipe_transformer_fprop_meta(p, inputs, *args) - - @classmethod - def _AttentionSetupDeterministicDropout(cls, tr_atten_tpl): - tr_atten_tpl.residual_dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - tr_atten_tpl.atten_tpl.atten_dropout_deterministic = True - tr_atten_tpl.atten_tpl.inner_atten_params.atten_dropout_deterministic = True - - @classmethod - def _TransformerSetupDeterministicDropout(cls, transformer_tpl): - cls._AttentionSetupDeterministicDropout(transformer_tpl.tr_atten_tpl) - transformer_tpl.tr_fflayer_tpl.residual_dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - transformer_tpl.tr_fflayer_tpl.fflayer_tpl.dropout = ( - layers.DeterministicDropoutLayer.Params()) - - @classmethod - def SetupDeterministicDropout(cls, params): - """Replaces dropout layers in ET with deterministic ones.""" - cls._TransformerSetupDeterministicDropout(params.transformer_tpl) - params.branched_convs_tpl.dropout_tpl = \ - layers.DeterministicDropoutLayer.Params() - if hasattr(params, 'glu_tpl'): - params.glu_tpl.dropout_tpl = layers.DeterministicDropoutLayer.Params() - if hasattr(params, 'tr_atten_tpl'): - cls._AttentionSetupDeterministicDropout(params.tr_atten_tpl) - if hasattr(params, 'tr_double_heads_atten_tpl'): - cls._AttentionSetupDeterministicDropout(params.tr_double_heads_atten_tpl) - return params - - -class GPipeEvolvedTransformerDecoderLayer( - layers_with_attention.EvolvedTransformerDecoderLayer): - """GPipe-compatible Evolved Transformer decoder layer.""" - - @classmethod - def Params(cls): - p = super().Params() - return _common_gpipe_transformer_params(p) - - def __init__(self, params): - super().__init__(params) - _common_gpipe_transformer_init(self) - - def FProp(self, - theta, - source_vecs, - source_paddings, - target_vecs, - target_paddings, - source_segment_id, - target_segment_id, - transparent_acc, - transparent_acc_helper, - source_task_id=None, - target_task_id=None): - with tf.name_scope(self.params.name): - return _common_gpipe_transformer_decoder_fprop( - self, GPipeEvolvedTransformerDecoderLayer, theta, source_vecs, - source_paddings, target_vecs, target_paddings, source_segment_id, - target_segment_id, transparent_acc, transparent_acc_helper, - source_task_id, target_task_id) - - @classmethod - def FPropMeta(cls, p, inputs, *args): - return _common_gpipe_transformer_fprop_meta(p, inputs, *args) - - @classmethod - def _AttentionSetupDeterministicDropout(cls, tr_atten_tpl): - tr_atten_tpl.residual_dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - tr_atten_tpl.atten_tpl.atten_dropout_deterministic = True - tr_atten_tpl.atten_tpl.inner_atten_params.atten_dropout_deterministic = True - - @classmethod - def _TransformerSetupDeterministicDropout(cls, transformer_tpl): - cls._AttentionSetupDeterministicDropout(transformer_tpl.tr_atten_tpl) - transformer_tpl.tr_fflayer_tpl.residual_dropout_tpl = ( - layers.DeterministicDropoutLayer.Params()) - transformer_tpl.tr_fflayer_tpl.fflayer_tpl.dropout = ( - layers.DeterministicDropoutLayer.Params()) - - @classmethod - def SetupDeterministicDropout(cls, params): - """Replaces dropout layers in ET with deterministic ones.""" - cls._TransformerSetupDeterministicDropout(params.transformer_tpl) - params.branched_convs_tpl.dropout_tpl = \ - layers.DeterministicDropoutLayer.Params() - if hasattr(params, 'glu_tpl'): - params.glu_tpl.dropout_tpl = layers.DeterministicDropoutLayer.Params() - if hasattr(params, 'tr_atten_tpl'): - cls._AttentionSetupDeterministicDropout(params.tr_atten_tpl) - if hasattr(params, 'tr_double_heads_atten_tpl'): - cls._AttentionSetupDeterministicDropout(params.tr_double_heads_atten_tpl) - return params - - -class GPipeTransformerSoftmaxLayer(layers.SimpleFullSoftmax): - """GPipe compatible softmax layer for transformers for computing logits.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('inputs_from_decoder', False, - 'Bool, whether inputs to this layer come from decoder or not.') - return p - - def FProp(self, - theta, - source_vecs, - source_paddings, - target_vecs, - target_paddings, - source_segment_id, - target_segment_id, - transparent_acc, - transparent_acc_helper, - source_task_id=None, - target_task_id=None): - del source_task_id - del target_task_id - p = self.params - if p.inputs_from_decoder: - transformer_output = target_vecs - else: - transformer_output = source_vecs - dim1, dim2 = tf.shape(transformer_output)[0], tf.shape( - transformer_output)[1] - softmax_input = tf.reshape(transformer_output, [-1, p.input_dim]) - output_shape = [dim1, dim2, p.num_classes] - return tf.reshape(super().Logits(theta, [softmax_input]), output_shape) - - @classmethod - def FPropMeta(cls, p, inputs, *args): - dim1, dim2 = args[1][:2] if p.inputs_from_decoder else inputs[:2] - logits = tshape.Shape([dim1, dim2, p.num_classes]) - return py_utils.NestedMap(flops=100, out_shapes=(logits,)) - - -class GPipeTransformerEmbeddingLayer(base_layer.BaseLayer): - """GPipe compatible embeddings for transformers.""" - - @classmethod - def Params(cls): - """Configs of Embedding layers for TransformerStack.""" - p = super().Params() - # Note: we use the same configs for src and tgt embeddings right now. - p.Define('token_emb', layers.SimpleEmbeddingLayer.Params(), - 'The embedding layer params.') - p.Define('position_emb', layers.PositionalEmbeddingLayer.Params(), - 'Position embedding layer params.') - p.Define('input_dropout_prob', 0.0, 'Prob at which we do input dropout.') - p.Define( - 'dropout_tpl', layers.DropoutLayer.Params(), - 'Replace with deterministic dropout for splits > 1 ' - 'or microbatches > 1.') - p.Define('add_tgt_embedding_layer', False, - 'Set True if layer embeds tgt instead of src.') - p.Define('packed_input', False, 'Set True to support packed inputs.') - p.Define( - 'is_transparent', False, - 'If set, encoder outputs a list of layer outputs while decoder ' - 'expects a list of source input vectors.') - p.Define('max_seq_len', 300, 'Max. seq len for decoding.') - p.Define('target_vocab_size', 0, 'Target vocab size, if different.') - - # Supporting task embeddings as additional input. - p.Define('dec_task_emb', None, - 'Adds task embeddings to every decoder timestep.') - p.Define('enc_task_emb', None, - 'Adds task embeddings to every encoder timestep.') - p.Define('batch_dim', 1, 'The batch dimension.') - p.Define('ret_task_ids', False, - 'Includes src_task_id and tgt_id in the fprop returns') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - p.token_emb.name = 'src_token_emb' - p.position_emb.name = 'src_position_emb' - self.CreateChild('src_token_emb', p.token_emb) - self.CreateChild('src_pos_emb', p.position_emb) - if p.enc_task_emb: - self.CreateChild('src_task_emb', p.enc_task_emb) - - p.dropout_tpl.keep_prob = (1.0 - p.input_dropout_prob) - p.dropout_tpl.name = 'src_dropout' - self.CreateChild('src_dropout', p.dropout_tpl) - - if p.add_tgt_embedding_layer: - params = p.token_emb.Copy() - if p.target_vocab_size: - params.vocab_size = p.target_vocab_size - params.name = 'tgt_token_emb' - self.CreateChild('tgt_token_emb', params) - params = p.position_emb.Copy() - params.name = 'tgt_position_emb' - self.CreateChild('tgt_pos_emb', params) - if p.dec_task_emb: - self.CreateChild('tgt_task_emb', p.dec_task_emb) - - params = p.dropout_tpl.Copy() - params.keep_prob = (1.0 - p.input_dropout_prob) - params.name = 'tgt_dropout' - self.CreateChild('tgt_dropout', params) - - def GetEmbeddings(self, emb_theta, emb, pos_emb_theta, pos_emb, dropout_theta, - dropout, input_ids, input_segment_pos, task_emb_theta, - task_emb, task_ids): - p = self.params - time_dim = 0 if p.batch_dim else 1 - seq_len = tf.shape(input_ids)[time_dim] - input_embs = emb.EmbLookup(emb_theta, input_ids) - if p.packed_input: # Packed inputs. - pos_embs = pos_emb.FPropWithPosition(pos_emb_theta, input_segment_pos) - else: - pos_embs = tf.expand_dims( - pos_emb.FProp(pos_emb_theta, seq_len), p.batch_dim) - - input_embs += pos_embs - if task_emb: - input_embs += task_emb.EmbLookup(task_emb_theta, task_ids) - input_embs = dropout.FProp(dropout_theta, input_embs) - return input_embs - - # To be used for decoding. - def GetEncoderEmbeddingsDefaultTheta(self, input_ids, task_ids=None): - p = self.params - time_dim = 0 if p.batch_dim else 1 - seq_len = tf.shape(input_ids)[time_dim] - input_embs = self.src_token_emb.EmbLookup(self.theta.src_token_emb, - input_ids) - pos_embs = tf.expand_dims( - self.src_pos_emb.FProp(self.theta.src_pos_emb, seq_len), p.batch_dim) - input_embs += pos_embs - if task_ids is not None and p.enc_task_emb: - input_embs += self.src_task_emb.EmbLookup(self.theta.src_task_emb, - task_ids) - input_embs = self.src_dropout.FProp(self.theta.src_dropout, input_embs) - return input_embs - - # To be used for decoding. - def GetDecoderEmbeddingsDefaultTheta(self, input_ids, task_ids=None, t=None): - p = self.params - input_embs = self.tgt_token_emb.EmbLookup(self.theta.tgt_token_emb, - input_ids) - if t is None: - time_dim = 0 if p.batch_dim else 1 - seq_len = tf.shape(input_ids)[time_dim] - pos_embs = tf.expand_dims( - self.tgt_pos_emb.FProp(self.theta.tgt_pos_emb, seq_len), p.batch_dim) - else: # Support decoding. - pos_embs = tf.slice( - self.tgt_pos_emb.FProp(self.theta.tgt_pos_emb, p.max_seq_len), [t, 0], - [1, p.token_emb.embedding_dim]) - input_embs += pos_embs - if task_ids is not None and p.dec_task_emb: - input_embs += self.tgt_task_emb.EmbLookup(self.theta.tgt_task_emb, - task_ids) - input_embs = self.tgt_dropout.FProp(self.theta.tgt_dropout, input_embs) - return input_embs - - def FProp(self, theta, source_id, source_paddings, target_id, target_paddings, - source_segment_id, target_segment_id, source_segment_pos, - target_segment_pos, source_task_id, target_task_id): - p = self.params - with tf.name_scope(p.name): - src_task_emb, src_task_emb_theta = None, None - if p.enc_task_emb: - src_task_emb, src_task_emb_theta = self.src_task_emb, theta.src_task_emb - source_vecs = self.GetEmbeddings(theta.src_token_emb, self.src_token_emb, - theta.src_pos_emb, self.src_pos_emb, - theta.src_dropout, self.src_dropout, - source_id, source_segment_pos, - src_task_emb_theta, src_task_emb, - source_task_id) - target_vecs = None - if p.add_tgt_embedding_layer: - tgt_task_emb, tgt_task_emb_theta = None, None - if p.enc_task_emb: - tgt_task_emb, tgt_task_emb_theta = (self.tgt_task_emb, - theta.tgt_task_emb) - target_vecs = self.GetEmbeddings(theta.tgt_token_emb, - self.tgt_token_emb, theta.tgt_pos_emb, - self.tgt_pos_emb, theta.tgt_dropout, - self.tgt_dropout, target_id, - target_segment_pos, tgt_task_emb_theta, - tgt_task_emb, target_task_id) - rets = (source_vecs, source_paddings, target_vecs, target_paddings, - source_segment_id, target_segment_id, None, None) - rets += (source_task_id, target_task_id) if p.ret_task_ids else () - return rets - - @classmethod - def FPropMeta(cls, p, inputs, *args): - # TODO(ankurbpn): return accurate estimate of flops. - py_utils.CheckShapes((inputs,)) - flops_per_element = 2 # Is this correct? - vocab = p.token_emb.vocab_size - dim = p.token_emb.embedding_dim - src_dim_0, src_dim_1 = inputs - flops = flops_per_element * src_dim_0 * src_dim_1 * dim * vocab - args = args if isinstance(args, tuple) else (args,) - new_inputs = tshape.Shape([src_dim_0, src_dim_1, dim]) - new_args = list(args) - if p.add_tgt_embedding_layer: - tgt_dim_0, tgt_dim_1 = args[1] - new_args[1] = tshape.Shape([tgt_dim_0, tgt_dim_1, dim]) - if p.ret_task_ids: - new_args = new_args[:5] + [None, None] + new_args[7:] - else: - new_args = new_args[:5] + [None, None] - new_args = tuple(new_args) - return py_utils.NestedMap(flops=flops, out_shapes=(new_inputs,) + new_args) - - -# TODO(ankurbpn,huangyp): Deprecate support for batch major layers here. -class GPipeTransformerStack(PipeliningLayer): - """Stacked self- multi-head attention and fully connected layers. - - With optional layer normalization applied to the final output. - - See 'Attention Is All You Need' https://arxiv.org/abs/1706.03762 - for details. - - The use of this stack for batch major transformer is deprecated. - Use GPipeBatchMajorTransformerStack instead. - """ - - @classmethod - def Params(cls): - """Configs for TransformerStack.""" - p = super().Params() - - # GPipe Related - p.Define( - 'splits', 1, - 'Number of splits, or list of integers specifying the ending index for ' - 'each split in ascending order. Last index should be num_layers.') - - # Transformer related - p.Define('model_dim', 1024, 'Characteristic depth (dimension).') - p.Define('num_encoder_layers', 0, 'Number of transformer encoder layers.') - p.Define('num_decoder_layers', 0, 'Number of transformer encoder layers.') - p.Define('use_pipelined_embeddings', True, 'Deprecated.') - p.Define('emb_tpl', GPipeTransformerEmbeddingLayer.Params(), - 'Prepare embeddings for Transformer input.') - p.Define('softmax_tpl', GPipeTransformerSoftmaxLayer.Params(), - 'Optional softmax layer to compute the logits.') - p.Define('label_smoothing', None, 'Label smoothing Params.') - p.Define('encoder_tpl', GPipeTransformerLayer.Params(), - 'TransformerLayer Encoder params tpl.') - p.Define('decoder_tpl', GPipeTransformerLayer.Params(), - 'TransformerLayer Decoder params tpl.') - p.Define('transparent_merger_dropout_prob', 0.1, - 'Dropout probability in WeightedSumLayer') - p.Define( - 'is_transparent', False, - 'If set, encoder outputs a merger of embeddings and ' - 'layer outputs.') - p.Define('transparent_merger_tpl', DeterministicWeightsLayer.Params(), - 'Creates weights for transparent combination.') - p.Define('packed_input', False, - 'If True, assumes multiple training samples per input.') - p.Define('normalize_encoder', False, - 'If True, layer-normalizes final encoder layer output.') - p.encoder_tpl.has_aux_atten = False - p.decoder_tpl.has_aux_atten = True - p.decoder_tpl.mask_self_atten = True - p.batch_dim = 1 - return p - - def __init__(self, params): - p = params.Copy() - num_layers = p.num_encoder_layers + p.num_decoder_layers - - if isinstance(p.splits, (list, tuple)): - assert p.splits[-1] == num_layers - for i, j in zip(p.splits[:-1], p.splits[1:]): - assert i <= j, 'Splits must be in increasing order.' - else: - num_splits = p.splits - layers_per_split = (num_layers - 1) // num_splits + 1 - p.splits = [] - for i in range(num_splits): - p.splits.append((i + 1) * layers_per_split) - p.splits[-1] = num_layers - - transformers = [] - - if p.is_transparent: - p.transparent_merger_tpl.num_sources = p.num_encoder_layers + 1 - p.transparent_merger_tpl.dropout_tpl.keep_prob = ( - 1 - p.transparent_merger_dropout_prob) - - # Encoder Embedding layer. - if len(p.splits) > 1 or p.num_micro_batches > 1: - p.emb_tpl.dropout_tpl = layers.DeterministicDropoutLayer.Params() - p.emb_tpl.packed_input = p.packed_input - p.emb_tpl.is_transparent = p.is_transparent - p.emb_tpl.add_tgt_embedding_layer = (p.num_decoder_layers > 0) - p.emb_tpl.name = 'emb' - p.emb_tpl.batch_dim = p.batch_dim - transformers.append(p.emb_tpl) - if p.softmax_tpl: - p.softmax_tpl.name = 'softmax' - p.softmax_tpl.inputs_from_decoder = p.num_decoder_layers > 0 - # Encoder layers. - for i in range(p.num_encoder_layers): - params = p.encoder_tpl.Copy() - params.name = 'encoder_%d' % i - if p.is_transparent: - params.is_transparent = p.is_transparent - params.final_enc_layer = (i == (p.num_encoder_layers - 1)) - if p.normalize_encoder and (i == (p.num_encoder_layers - 1)): - params.normalize_output = p.normalize_encoder - params.final_enc_layer = (i == (p.num_encoder_layers - 1)) - if p.packed_input: - params.packed_input = p.packed_input - # Use DeterministicDropoutLayer when used in temp graphs. - if len(p.splits) > 1 or p.num_micro_batches > 1: - params = params.cls.SetupDeterministicDropout(params) - assert not params.has_aux_atten - if p.is_transparent and i == 0: - params.transparent_merger_tpl = p.transparent_merger_tpl.Copy() - transformers.append(params) - - # Decoder layers. - for i in range(p.num_decoder_layers): - params = p.decoder_tpl.Copy() - params.name = 'decoder_%d' % i - params.mask_self_atten = True - if p.packed_input: - params.packed_input = p.packed_input - if len(p.splits) > 1 or p.num_micro_batches > 1: - params = params.cls.SetupDeterministicDropout(params) - assert params.has_aux_atten - transformers.append(params) - cells = [] - cell_start = 0 - # To account for embedding layers in the pipeline. - offset = 1 - for split, cell_end in enumerate(p.splits): - # Layer 0 (embeddings) is always in split 0. - sub = transformers[cell_start:(cell_end + offset)] - if split == len(p.splits) - 1 and p.softmax_tpl: - sub.append(p.softmax_tpl) - cell = FeatureExtractionLayer.Params().Set( - name='cell_{}'.format(split), sub=sub) - cells.append(cell) - cell_start = cell_end + offset - p.cell_tpl = cells - super().__init__(p) - - if p.label_smoothing: - self.CreateChild('smoother', p.label_smoothing) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - if self.params.label_smoothing: - self.smoother.InstantiateVariables() - super()._CreateChildrenVariables() - - def Logits(self, theta, inputs): - num_splits = len(self.params.splits) - softmax = self.children['cell_{}'.format(num_splits - 1)].softmax - softmax_theta = theta['cell_{}'.format(num_splits - 1)].softmax - return softmax.Logits(softmax_theta, inputs) - - def GetEncoders(self): - encoders = [] - p = self.params - cell_start = 0 - for split, cell_end in enumerate(p.splits): - for encoder_id in range(cell_start, cell_end): - if encoder_id >= p.num_encoder_layers: - break - encoder_l = self.children['cell_{}'.format(split)].children[ - 'encoder_{}'.format(encoder_id)] - encoders.append(encoder_l) - cell_start = cell_end - return encoders - - def GetDecoders(self): - decoders = [] - p = self.params - cell_start = 0 - for split, cell_end in enumerate(p.splits): - for layer_id in range(cell_start, cell_end): - decoder_id = layer_id - p.num_encoder_layers - if decoder_id < 0: - continue - decoder_l = self.children['cell_{}'.format(split)].children[ - 'decoder_{}'.format(decoder_id)] - decoders.append(decoder_l) - cell_start = cell_end - assert len(decoders) == p.num_decoder_layers - return decoders - - def EncoderEmbedFPropDefaultTheta(self, source_id, source_task_id=None): - emb = self.children['cell_0'].children['emb'] - return emb.GetEncoderEmbeddingsDefaultTheta(source_id, source_task_id) - - def DecoderEmbedFPropDefaultTheta(self, tgt_id, tgt_task_id=None, t=None): - emb = self.children['cell_0'].children['emb'] - return emb.GetDecoderEmbeddingsDefaultTheta(tgt_id, tgt_task_id, t) - - def EncoderFPropDefaultTheta(self, - source_vecs, - source_paddings, - source_segment_id=None, - source_task_id=None, - target_task_id=None): - p = self.params - transparent_acc = None - transparent_weights = None - for encoder_l in self.GetEncoders(): - if source_task_id is not None or target_task_id is not None: - encoder_outs = encoder_l.FProp( - encoder_l.theta, - source_vecs, - source_paddings, - None, - None, - source_segment_id, - None, - transparent_acc, - transparent_weights, - source_task_id=source_task_id, - target_task_id=target_task_id) - else: - encoder_outs = encoder_l.FProp(encoder_l.theta, source_vecs, - source_paddings, None, None, - source_segment_id, None, transparent_acc, - transparent_weights) - source_vecs = encoder_outs[0] - if p.is_transparent and len(encoder_outs) == 8: - transparent_acc = encoder_outs[6] - transparent_weights = encoder_outs[7] - return source_vecs - - def FProp(self, - theta, - source_input, - source_paddings, - target_input=None, - target_paddings=None, - source_segment_id=None, - target_segment_id=None, - labels=None, - label_weights=None, - source_segment_pos=None, - target_segment_pos=None, - source_task_id=None, - target_task_id=None): - """Transforms source sequence of Tensors with Transformers layers. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_input: A sequence of ints indicating source input ids of [time, - batch] shape or [batch, time] if batch_dim is 0. - source_paddings: A sequence of 0s and 1s indicating input paddings of - [time, batch] shape or [batch, time] if batch_dim is 0. - target_input: A sequence of ints indicating target input ids of [time, - batch] shape or [batch, time] if batch_dim is 0. - target_paddings: [target_time, target_batch] or [target_batch, - target_time] if batch_dim is 0. - source_segment_id: A sequence of ints indicating source segment ids of - [time, batch] shape or [batch, time] if batch_dim is 0. - target_segment_id: A sequence of ints indicating target segment ids of - [time, batch] shape or [batch, time] if batch_dim is 0. - labels: A sequence of ints indicating label ids of [time, batch] shape, or - [batch, time] if batch_dim is 0. - label_weights: A sequence of floats indicates label weights of [time, - batch] shape, or [batch, time] if batch_dim is 0. - source_segment_pos: A sequence of ints indicating source position ids of - [time, batch] shape, or [batch, time] if batch_dim is 0. - target_segment_pos: A sequence of ints indicating target position ids of - [time, batch] shape, or [batch, time] if batch_dim is 0. - source_task_id: A sequence of ints indicating source task ids of [time, - batch] shape, or [batch, time] if batch_dim is 0. - target_task_id: A sequence of ints indicating target task ids of [time, - batch] shape, or [batch, time] if batch_dim is 0. - - Returns: - transformer_output with shape [time, batch, dim] or [batch, time, dim] - if batch_dim is 0. - """ - p = self.params - if p.num_decoder_layers > 0: - assert target_input is not None - assert target_paddings is not None - if p.packed_input: - assert source_segment_id is not None, ( - 'Need to specify src_segment_id if packed input is supported.') - assert source_segment_pos is not None, ( - 'Need to specify src_segment_pos for packed input and embeddings.') - - logits = super().FProp(theta, source_input, source_paddings, target_input, - target_paddings, source_segment_id, - target_segment_id, source_segment_pos, - target_segment_pos, source_task_id, target_task_id) - if not p.softmax_tpl: - return logits - label_weights = tf.reshape(label_weights, [-1]) - target_probs = None - if p.label_smoothing: - if p.batch_dim: # Time-major - target_probs = tf.transpose( - self.smoother.FProp( - theta.smoother, - tf.transpose(target_paddings), - tf.transpose(labels), - target_ids=None), [1, 0, 2]) - else: - target_probs = self.smoother.FProp( - theta.smoother, target_paddings, labels, target_ids=None) - target_probs = tf.reshape(target_probs, [-1, p.softmax_tpl.num_classes]) - reshaped_logits = tf.reshape(logits, [-1, p.softmax_tpl.num_classes]) - tgt_labels = tf.reshape(labels, [-1]) - num_splits = len(p.splits) - softmax = self.children['cell_{}'.format(num_splits - 1)].softmax - softmax_theta = theta['cell_{}'.format(num_splits - 1)].softmax - per_example_xent, _ = softmax.XentLossFromLogits( - softmax_theta, - reshaped_logits, - class_weights=tf.reshape(label_weights, [-1]), - class_ids=tgt_labels, - class_probabilities=target_probs) - xent_shape = tf.shape(logits)[:2] - per_example_xent = tf.reshape(per_example_xent, xent_shape) - return per_example_xent, logits - - -class GPipeEvolvedTransformerStack(GPipeTransformerStack): - """Evolved Transformer stack for GPipe. - - With optional layer normalization applied to the final output. - - See 'Evolved Transformer' for more details: - https://arxiv.org/abs/1901.11117 . - """ - - @classmethod - def Params(cls): - """Configs for EvolvedTransformerStack.""" - p = super().Params() - p.encoder_tpl = GPipeEvolvedTransformerEncoderLayer.Params() - p.decoder_tpl = GPipeEvolvedTransformerDecoderLayer.Params() - return p - - -class DeterministicWeightsLayer(base_layer.BaseLayer): - """WeightedSumLayer with deterministic dropout.""" - - @classmethod - def Params(cls): - """Params for this MergerLayer class.""" - p = super().Params() - p.Define('num_sources', 0, 'Number of input sources to combine.') - p.Define('weighted_merger_dropout_prob', 0.0, - 'Applies dropout to the weights.') - p.Define( - 'weighted_merger_softmax', True, 'If set, applies a softmax ' - 'layer on top of the weights for normalization.') - p.Define('global_weight_scale', 1.0, 'A global scale put on weights.') - p.Define('minimal_prob', 0.0, 'The minimal weight for each component.') - p.Define('dropout_tpl', layers.DeterministicDropoutLayer.Params(), - 'Dropout layer') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if not p.name: - raise ValueError('Layer must have a specified name!') - - assert p.num_sources > 0, ('Must specify num_sources > 0.') - - p.dropout_tpl.name = 'dropout' - self.CreateChild('weighted_merger_dropout', p.dropout_tpl) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - params_init = py_utils.WeightInit.Constant(0.0) - # Weights to be learned. - pw = py_utils.WeightParams( - shape=[p.num_sources], - init=params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('sum_weight', pw) - - def FProp(self, theta): - """Combines the list of input tensors into a single tensor. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - - Returns: - A tensor of weights with dropout applied with shape [num_sources]. - """ - p = self.params - - # The constant factor is just meant to support the non-normalized scenario. - # If softmax is applied, this factor will cancel out. - w = theta.sum_weight * p.global_weight_scale + (1 / p.num_sources) - w = tf.reshape(w, [p.num_sources]) - w = self.weighted_merger_dropout.FProp(theta.weighted_merger_dropout, w) - if p.weighted_merger_softmax: - residual_weights = p.minimal_prob * p.num_sources - assert residual_weights >= 0.0 - assert residual_weights < 1.0 - w = tf.nn.softmax(w, axis=0) * (1.0 - residual_weights) + p.minimal_prob - return w - - -class GPipeBatchMajorTransformerSoftmaxLayer(layers.SimpleFullSoftmax): - """GPipe compatible softmax layer for transformers for computing logits. - - FProp interface is different for batch major stack, using segment_masks - instead of segment_ids. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('inputs_from_decoder', False, - 'Bool, whether inputs to this layer come from decoder or not.') - return p - - def FProp(self, theta, source_vecs, source_paddings, target_vecs, - target_paddings, encoder_self_atten_segment_mask, - decoder_self_atten_segment_mask, decoder_cross_atten_segment_mask): - p = self.params - if p.inputs_from_decoder: - transformer_output = target_vecs - else: - transformer_output = source_vecs - dim1, dim2 = tf.shape(transformer_output)[0], tf.shape( - transformer_output)[1] - softmax_input = tf.reshape(transformer_output, [-1, p.input_dim]) - output_shape = [dim1, dim2, p.num_classes] - return tf.reshape(super().Logits(theta, [softmax_input]), output_shape) - - @classmethod - def FPropMeta(cls, p, inputs, *args): - dim1, dim2 = args[1][:2] if p.inputs_from_decoder else inputs[:2] - logits = tshape.Shape([dim1, dim2, p.num_classes]) - return py_utils.NestedMap(flops=100, out_shapes=(logits,)) - - -class GPipeBatchMajorTransformerEmbeddingLayer(base_layer.BaseLayer): - """GPipe compatible embeddings for transformers.""" - - @classmethod - def Params(cls): - """Configs of Embedding layers for TransformerStack.""" - p = super().Params() - # Note: we use the same configs for src and tgt embeddings right now. - p.Define('token_emb', - layers.SimpleEmbeddingLayer.Params().Set(scale_sqrt_depth=True), - 'The embedding layer params.') - p.Define('position_emb', layers.PositionalEmbeddingLayer.Params(), - 'Position embedding layer params.') - p.Define('input_dropout_prob', 0.0, 'Prob at which we do input dropout.') - p.Define( - 'dropout_tpl', layers.DropoutLayer.Params(), - 'Replace with deterministic dropout for splits > 1 ' - 'or microbatches > 1.') - p.Define('add_tgt_embedding_layer', True, - 'Set True if layer embeds tgt instead of src.') - p.Define('packed_input', True, 'Set True to support packed inputs.') - p.Define('max_seq_len', 300, 'Max. seq len for decoding.') - p.Define('target_vocab_size', 0, 'Target vocab size, if different.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - p.token_emb.name = 'src_token_emb' - p.position_emb.name = 'src_position_emb' - self.CreateChild('src_token_emb', p.token_emb) - self.CreateChild('src_pos_emb', p.position_emb) - - p.dropout_tpl.keep_prob = (1.0 - p.input_dropout_prob) - p.dropout_tpl.name = 'src_dropout' - self.CreateChild('src_dropout', p.dropout_tpl) - - if p.add_tgt_embedding_layer: - params = p.token_emb.Copy() - if p.target_vocab_size: - params.vocab_size = p.target_vocab_size - params.name = 'tgt_token_emb' - self.CreateChild('tgt_token_emb', params) - params = p.position_emb.Copy() - params.name = 'tgt_position_emb' - self.CreateChild('tgt_pos_emb', params) - - params = p.dropout_tpl.Copy() - params.keep_prob = (1.0 - p.input_dropout_prob) - params.name = 'tgt_dropout' - self.CreateChild('tgt_dropout', params) - - def GetEmbeddings(self, emb_theta, emb, pos_emb_theta, pos_emb, dropout_theta, - dropout, input_ids, input_segment_pos): - p = self.params - time_dim = 1 - seq_len = tf.shape(input_ids)[time_dim] - input_embs = emb.EmbLookup(emb_theta, input_ids) - if p.packed_input: # Packed inputs. - pos_embs = pos_emb.FPropWithPosition(pos_emb_theta, input_segment_pos) - else: - pos_embs = tf.expand_dims(pos_emb.FProp(pos_emb_theta, seq_len), 0) - - input_embs += pos_embs - input_embs = dropout.FProp(dropout_theta, input_embs) - return input_embs - - # To be used for decoding. - def GetEncoderEmbeddingsDefaultTheta(self, input_ids): - seq_len = tf.shape(input_ids)[1] - input_embs = self.src_token_emb.EmbLookup(self.theta.src_token_emb, - input_ids) - pos_embs = tf.expand_dims( - self.src_pos_emb.FProp(self.theta.src_pos_emb, seq_len), 0) - input_embs += pos_embs - input_embs = self.src_dropout.FProp(self.theta.src_dropout, input_embs) - return input_embs - - # To be used for decoding. - def GetDecoderEmbeddingsDefaultTheta(self, input_ids, t): - seq_len = tf.shape(input_ids)[1] - input_embs = self.tgt_token_emb.EmbLookup(self.theta.tgt_token_emb, - input_ids) - # [target_batch, 1, dim] - # t should be shaped as [target_batch, 1]. - if t is None: - pos_embs = tf.expand_dims( - self.tgt_pos_emb.FProp(self.theta.tgt_pos_emb, seq_len), 0) - else: - pos_embs = self.tgt_pos_emb.FPropWithPosition(self.theta.tgt_pos_emb, t) - input_embs += pos_embs - input_embs = self.tgt_dropout.FProp(self.theta.tgt_dropout, input_embs) - return input_embs - - def FProp(self, theta, source_id, source_paddings, target_id, target_paddings, - encoder_self_atten_segment_mask, decoder_self_atten_segment_mask, - decoder_cross_atten_segment_mask, source_segment_pos, - target_segment_pos): - p = self.params - with tf.name_scope(p.name): - source_vecs = self.GetEmbeddings(theta.src_token_emb, self.src_token_emb, - theta.src_pos_emb, self.src_pos_emb, - theta.src_dropout, self.src_dropout, - source_id, source_segment_pos) - target_vecs = None - if p.add_tgt_embedding_layer: - target_vecs = self.GetEmbeddings(theta.tgt_token_emb, - self.tgt_token_emb, theta.tgt_pos_emb, - self.tgt_pos_emb, theta.tgt_dropout, - self.tgt_dropout, target_id, - target_segment_pos) - rets = (source_vecs, source_paddings, target_vecs, target_paddings, - encoder_self_atten_segment_mask, decoder_self_atten_segment_mask, - decoder_cross_atten_segment_mask) - return rets - - @classmethod - def FPropMeta(cls, p, inputs, *args): - # TODO(ankurbpn): return accurate estimate of flops. - py_utils.CheckShapes((inputs,)) - flops_per_element = 2 # Is this correct? - vocab = p.token_emb.vocab_size - dim = p.token_emb.embedding_dim - src_dim_0, src_dim_1 = inputs - flops = flops_per_element * src_dim_0 * src_dim_1 * dim * vocab - args = args if isinstance(args, tuple) else (args,) - new_inputs = tshape.Shape([src_dim_0, src_dim_1, dim]) - new_args = list(args) - if p.add_tgt_embedding_layer: - tgt_dim_0, tgt_dim_1 = args[1] - new_args[1] = tshape.Shape([tgt_dim_0, tgt_dim_1, dim]) - new_args = new_args[:6] - new_args = tuple(new_args) - return py_utils.NestedMap(flops=flops, out_shapes=(new_inputs,) + new_args) - - -class GPipeBatchMajorTransformerStack(PipeliningLayer): - """Stacked self- multi-head attention and fully connected layers. - - With optional layer normalization applied to the final output. - - See 'Attention Is All You Need' https://arxiv.org/abs/1706.03762 - for details. - - Implements a gipe stack for the batch major transformer variant. - """ - - @classmethod - def Params(cls): - """Configs for TransformerStack.""" - p = super().Params() - - # GPipe Related - p.Define( - 'splits', 1, - 'Number of splits, or list of integers specifying the ending index for ' - 'each split in ascending order. Last index should be num_layers.') - - # Transformer related - p.Define('model_dim', 1024, 'Characteristic depth (dimension).') - p.Define('num_encoder_layers', 0, 'Number of transformer encoder layers.') - p.Define('num_decoder_layers', 0, 'Number of transformer encoder layers.') - p.Define('emb_tpl', GPipeBatchMajorTransformerEmbeddingLayer.Params(), - 'Prepare embeddings for Transformer input.') - p.Define('softmax_tpl', GPipeBatchMajorTransformerSoftmaxLayer.Params(), - 'Optional softmax layer to compute the logits.') - p.Define('label_smoothing', None, 'Label smoothing Params.') - p.Define('encoder_tpl', - batch_major_attention.GPipeBatchMajorTransformerLayer.Params(), - 'TransformerLayer Encoder params tpl.') - p.Define('decoder_tpl', - batch_major_attention.GPipeBatchMajorTransformerLayer.Params(), - 'TransformerLayer Decoder params tpl.') - p.Define('packed_input', True, - 'If True, assumes multiple training samples per input.') - p.encoder_tpl.has_aux_atten = False - p.decoder_tpl.has_aux_atten = True - p.decoder_tpl.mask_self_atten = True - p.batch_dim = 0 - return p - - def __init__(self, params): - p = params.Copy() - num_layers = p.num_encoder_layers + p.num_decoder_layers - - if isinstance(p.splits, (list, tuple)): - assert p.splits[-1] == num_layers - for i, j in zip(p.splits[:-1], p.splits[1:]): - assert i <= j, 'Splits must be in increasing order.' - else: - num_splits = p.splits - layers_per_split = (num_layers - 1) // num_splits + 1 - p.splits = [] - for i in range(num_splits): - p.splits.append((i + 1) * layers_per_split) - p.splits[-1] = num_layers - - p.state_dtype = p.dtype - if p.fprop_dtype: - p.state_dtype = p.fprop_dtype - - transformers = [] - - # Encoder Embedding layer. - if len(p.splits) > 1 or p.num_micro_batches > 1: - p.emb_tpl.dropout_tpl = layers.DeterministicDropoutLayer.Params() - p.emb_tpl.packed_input = p.packed_input - p.emb_tpl.add_tgt_embedding_layer = (p.num_decoder_layers > 0) - p.emb_tpl.name = 'emb' - transformers.append(p.emb_tpl) - if p.softmax_tpl: - p.softmax_tpl.name = 'softmax' - p.softmax_tpl.inputs_from_decoder = p.num_decoder_layers > 0 - # Encoder layers. - for i in range(p.num_encoder_layers): - params = p.encoder_tpl.Copy() - params.name = 'encoder_%d' % i - if i == (p.num_encoder_layers - 1): - params.output_layer_norm = True - params.packed_input = p.packed_input - # Use DeterministicDropoutLayer when used in temp graphs. - if len(p.splits) > 1 or p.num_micro_batches > 1: - params = params.cls.SetupDeterministicDropout(params) - assert not params.has_aux_atten - transformers.append(params) - - # Decoder layers. - for i in range(p.num_decoder_layers): - params = p.decoder_tpl.Copy() - params.name = 'decoder_%d' % i - params.mask_self_atten = True - if i == (p.num_decoder_layers - 1): - params.output_layer_norm = True - params.packed_input = p.packed_input - if len(p.splits) > 1 or p.num_micro_batches > 1: - params = params.cls.SetupDeterministicDropout(params) - assert params.has_aux_atten - transformers.append(params) - cells = [] - cell_start = 0 - # To account for embedding layers in the pipeline. - offset = 1 - for split, cell_end in enumerate(p.splits): - # Layer 0 (embeddings) is always in split 0. - sub = transformers[cell_start:(cell_end + offset)] - if split == len(p.splits) - 1 and p.softmax_tpl: - sub.append(p.softmax_tpl) - cell = FeatureExtractionLayer.Params().Set( - name='cell_{}'.format(split), sub=sub) - cells.append(cell) - cell_start = cell_end + offset - p.cell_tpl = cells - super().__init__(p) - - if p.label_smoothing: - self.CreateChild('smoother', p.label_smoothing) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - if self.params.label_smoothing: - self.smoother.InstantiateVariables() - super()._CreateChildrenVariables() - - def Logits(self, theta, inputs): - num_splits = len(self.params.splits) - softmax = self.children['cell_{}'.format(num_splits - 1)].softmax - softmax_theta = theta['cell_{}'.format(num_splits - 1)].softmax - return softmax.Logits(softmax_theta, inputs) - - def GetEncoders(self): - encoders = [] - p = self.params - cell_start = 0 - for split, cell_end in enumerate(p.splits): - for encoder_id in range(cell_start, cell_end): - if encoder_id >= p.num_encoder_layers: - break - encoder_l = self.children['cell_{}'.format(split)].children[ - 'encoder_{}'.format(encoder_id)] - encoders.append(encoder_l) - cell_start = cell_end - return encoders - - def GetDecoders(self): - decoders = [] - p = self.params - cell_start = 0 - for split, cell_end in enumerate(p.splits): - for layer_id in range(cell_start, cell_end): - decoder_id = layer_id - p.num_encoder_layers - if decoder_id < 0: - continue - decoder_l = self.children['cell_{}'.format(split)].children[ - 'decoder_{}'.format(decoder_id)] - decoders.append(decoder_l) - cell_start = cell_end - assert len(decoders) == p.num_decoder_layers - return decoders - - def EncoderEmbedFPropDefaultTheta(self, source_id): - emb = self.children['cell_0'].children['emb'] - return emb.GetEncoderEmbeddingsDefaultTheta(source_id) - - def DecoderEmbedFPropDefaultTheta(self, tgt_id, t=None): - emb = self.children['cell_0'].children['emb'] - return emb.GetDecoderEmbeddingsDefaultTheta(tgt_id, t) - - def EncoderFPropDefaultTheta(self, - source_vecs, - source_paddings, - encoder_self_atten_segment_mask=None): - for encoder_l in self.GetEncoders(): - encoder_outs = encoder_l.FProp(encoder_l.theta, source_vecs, - source_paddings, None, None, - encoder_self_atten_segment_mask, None, - None) - source_vecs = encoder_outs[0] - return source_vecs - - def FProp(self, - theta, - source_input, - source_paddings, - target_input=None, - target_paddings=None, - source_segment_id=None, - target_segment_id=None, - labels=None, - label_weights=None, - source_segment_pos=None, - target_segment_pos=None): - """Transforms source sequence of Tensors with Transformers layers. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - source_input: A sequence of ints indicating source input ids of [batch, - time]. - source_paddings: A sequence of 0s and 1s indicating input paddings of - [batch, time]. - target_input: A sequence of ints indicating target input ids of [batch, - time]. - target_paddings: [target_batch, target_time]. - source_segment_id: A sequence of ints indicating source segment ids of - [batch, time]. - target_segment_id: A sequence of ints indicating target segment ids of - [batch, time]. - labels: A sequence of ints indicating label ids of [batch, time]. - label_weights: A sequence of floats indicates label weights of [batch, - time]. - source_segment_pos: A sequence of ints indicating source position ids of - [batch, time]. - target_segment_pos: A sequence of ints indicating target position ids of - [batch, time]. - - Returns: - transformer_output with shape [batch, time, dim]. - """ - p = self.params - if p.num_decoder_layers > 0: - assert target_input is not None - assert target_paddings is not None - target_time = tf.shape(target_input)[1] - batch = tf.shape(target_input)[0] - encoder_self_atten_segment_mask = None - decoder_self_atten_segment_mask = None - decoder_cross_atten_segment_mask = None - - # Prepare segment masks from segment ids. - if p.packed_input: - dtype = py_utils.FPropDtype(p) - assert source_segment_id is not None, ( - 'Need to specify src_segment_id if packed input is supported.') - assert source_segment_pos is not None, ( - 'Need to specify src_segment_pos for packed input and embeddings.') - encoder_self_atten_segment_mask = batch_major_attention.SegmentMask( - source_segment_id, source_segment_id, dtype, False) - if target_segment_id is not None: - decoder_self_atten_segment_mask = batch_major_attention.SegmentMask( - target_segment_id, target_segment_id, dtype, False) - causal_padding = tf.expand_dims( - tf.tile( - tf.expand_dims( - batch_major_attention.CausalPadding( - target_time, dtype=dtype), 0), [batch, 1, 1]), 1) - decoder_self_atten_segment_mask = tf.math.maximum( - causal_padding, decoder_self_atten_segment_mask) - decoder_cross_atten_segment_mask = batch_major_attention.SegmentMask( - target_segment_id, source_segment_id, dtype, False) - - # FProp through the gpipe pipeline. - logits = super().FProp(theta, source_input, source_paddings, target_input, - target_paddings, encoder_self_atten_segment_mask, - decoder_self_atten_segment_mask, - decoder_cross_atten_segment_mask, source_segment_pos, - target_segment_pos) - label_weights = tf.reshape(label_weights, [-1]) - target_probs = None - if p.label_smoothing: - target_probs = self.smoother.FProp( - theta.smoother, target_paddings, labels, target_ids=None) - target_probs = tf.reshape(target_probs, [-1, p.softmax_tpl.num_classes]) - reshaped_logits = tf.reshape(logits, [-1, p.softmax_tpl.num_classes]) - tgt_labels = tf.reshape(labels, [-1]) - num_splits = len(p.splits) - softmax = self.children['cell_{}'.format(num_splits - 1)].softmax - softmax_theta = theta['cell_{}'.format(num_splits - 1)].softmax - per_example_xent, _ = softmax.XentLossFromLogits( - softmax_theta, - reshaped_logits, - class_weights=tf.reshape(label_weights, [-1]), - class_ids=tgt_labels, - class_probabilities=target_probs) - xent_shape = tf.shape(logits)[:2] - per_example_xent = tf.reshape(per_example_xent, xent_shape) - return per_example_xent, logits diff --git a/lingvo/core/layers_with_gpipe_test.py b/lingvo/core/layers_with_gpipe_test.py deleted file mode 100644 index afe194b7..00000000 --- a/lingvo/core/layers_with_gpipe_test.py +++ /dev/null @@ -1,990 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for layers_with_gpipe.""" - -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import layers_with_gpipe -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core.layers_with_gpipe import GPipeEvolvedTransformerDecoderLayer -from lingvo.core.layers_with_gpipe import GPipeEvolvedTransformerEncoderLayer -from lingvo.core.layers_with_gpipe import GPipeEvolvedTransformerStack -from lingvo.core.layers_with_gpipe import GPipeTransformerLayer -from lingvo.core.layers_with_gpipe import GPipeTransformerStack -import numpy as np - - -class GPipeTransformerLayersTest(test_utils.TestCase): - - def _testInputs(self, depth=3, dtype=tf.float32): - np.random.seed(505837249) - source_vecs = tf.stack( - [tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(5)]) - source_padding = tf.constant([[0, 0, 1, 1, 0], [1, 0, 0, 0, 1]], - dtype=dtype) - aux_source_vecs = tf.stack( - [tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7)]) - aux_source_paddings = tf.constant( - [[0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]], dtype=dtype) - source_padding = tf.transpose(source_padding) - aux_source_paddings = tf.transpose(aux_source_paddings) - - input_task_arr = np.array([[0] * depth, [0] * depth]) - tgt_task_arr = np.array([[0] * depth] * 3) - input_tasks = tf.constant(input_task_arr.tolist(), dtype=tf.int32) - tgt_tasks = tf.constant(tgt_task_arr.tolist(), dtype=tf.int32) - return (source_vecs, source_padding, aux_source_vecs, aux_source_paddings, - input_tasks, tgt_tasks) - - def testGPipeSoftmaxLayerInputfromEncoder(self, use_task_ids=False): - with self.session(use_gpu=True): - depth = 4 - np.random.seed(6348575) - p = GPipeTransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = False - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - transformer = p.Instantiate() - softmax = layers_with_gpipe.GPipeTransformerSoftmaxLayer.Params() - softmax.name = 'softmax' - softmax.inputs_from_decoder = False - softmax.num_classes = 2 - softmax.input_dim = depth - softmax = softmax.Instantiate() - (source_vecs, _, _, _, _, _) = self._testInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - softmax_inputs = transformer.FPropDefaultTheta(source_vecs, - source_padding, None, None, - None, None, None, None) - softmax_outputs = softmax.FPropDefaultTheta(*softmax_inputs) - self.assertEqual([5, 2, 2], softmax_outputs.shape) - - def testGPipeSoftmaxLayerInputfromDecoder(self): - with self.session(use_gpu=True): - depth = 4 - np.random.seed(6348575) - p = GPipeTransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - transformer = p.Instantiate() - softmax = layers_with_gpipe.GPipeTransformerSoftmaxLayer.Params() - softmax.name = 'softmax' - softmax.inputs_from_decoder = True - softmax.num_classes = 2 - softmax.input_dim = depth - softmax = softmax.Instantiate() - (source_vecs, _, aux_vecs, aux_paddings, _, - _) = self._testInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - softmax_inputs = transformer.FPropDefaultTheta(aux_vecs, aux_paddings, - source_vecs, - source_padding, None, None, - None, None) - softmax_outputs = softmax.FPropDefaultTheta(*softmax_inputs) - self.assertEqual([5, 2, 2], softmax_outputs.shape) - - def testTransformerLayerExtendStep(self): - with self.session(use_gpu=True): - depth = 4 - np.random.seed(6348575) - p = GPipeTransformerLayer.Params() - p.name = 'transformer' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_attention_heads = 2 - transformer = GPipeTransformerLayer(p) - - (source_vecs, _, aux_vecs, aux_paddings, input_tasks, - tgt_tasks) = self._testInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - output1 = transformer.FPropDefaultTheta(aux_vecs, aux_paddings, - source_vecs, source_padding, None, - None, input_tasks, tgt_tasks) - h1 = output1[2] - out_src_task, out_tgt_task = output1[-2], output1[-1] - - h2 = [] - cached_source_vecs = tf.zeros([0, 2, 4]) - cached_source_contexts = tf.zeros([0, 2, 4]) - prefix_states = py_utils.NestedMap( - key=cached_source_vecs, value=cached_source_contexts) - for i in range(5): - h, _, prefix_states = transformer.ExtendStep(transformer.theta, - source_vecs[i, :, :], - prefix_states, aux_vecs, - aux_paddings) - h2.append(h) - - h2 = tf.stack(h2) - - self.evaluate(tf.global_variables_initializer()) - h1_v, h2_v = self.evaluate([h1, h2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(out_src_task, input_tasks) - self.assertAllClose(out_tgt_task, tgt_tasks) - self.assertAllClose(h1_v[2][1], - [1.10429943, -1.64884555, 0.15726769, -0.00250494]) - - def testEvolvedTransformerEncoderLayerConstruction(self): - p = GPipeEvolvedTransformerEncoderLayer.Params() - p.name = 'gpipe_evolved_transformer_encoder' - p.source_dim = 4 - p.transformer_tpl.tr_fflayer_tpl.hidden_dim = 7 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - _ = GPipeEvolvedTransformerEncoderLayer(p) - - def testEvolvedTransformerEncoderLayerFProp(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = GPipeEvolvedTransformerEncoderLayer.Params() - p.name = 'gpipe_evolved_transformer_encoder' - p.source_dim = depth - p.transformer_tpl.tr_fflayer_tpl.hidden_dim = 7 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - transformer = GPipeEvolvedTransformerEncoderLayer(p) - - (source_vecs, source_padding, _, _, _, _) = self._testInputs(depth=depth) - - output = transformer.FPropDefaultTheta(source_vecs, source_padding, None, - None, None, None, None, None, None, - None) - h = output[0] - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate([h])[0] - tf.logging.info(np.array_repr(actual_layer_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[-2.05546284, -1.15067506, -0.00898855, 0.26481438], - [-0.8181392, -1.40835416, 0.47274107, 0.86176264]], - [[-1.43251371, 0.69686228, -0.70026731, -0.47239268], - [-0.39946821, -0.27037358, -0.22701442, 1.33816898]], - [[ 0.89412129, -1.07294774, -0.86541933, -0.21121001], - [-1.55683649, -1.14919782, 0.95287859, 0.11334917]], - [[-1.11910486, -1.01226425, 0.68622279, 0.00536875], - [ 2.33264184, 1.45991778, -0.71802276, -1.77120328]], - [[ 0.52163047, -1.90511549, -0.56069887, 1.1521647 ], - [-1.49890876, 0.20399603, -0.78263998, 1.30187178]]] - - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - - def testEvolvedTransformerDecoderLayerConstruction(self): - p = GPipeEvolvedTransformerDecoderLayer.Params() - p.name = 'gpipe_evolved_transformer_decoder' - p.source_dim = 16 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - p.has_aux_atten = True - p.mask_self_atten = True - _ = GPipeEvolvedTransformerDecoderLayer(p) - - def testEvolvedTransformerDecoderLayerFProp(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = GPipeEvolvedTransformerDecoderLayer.Params() - p.name = 'gpipe_evolved_transformer_decoder' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_double_heads_atten_tpl.num_attention_heads = 2 - p.tr_atten_tpl.num_attention_heads = 2 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - transformer = GPipeEvolvedTransformerDecoderLayer(p) - - (source_vecs, source_padding, aux_vecs, aux_paddings, _, - _) = self._testInputs(depth=depth) - - output = transformer.FPropDefaultTheta(aux_vecs, aux_paddings, - source_vecs, source_padding, None, - None, None, None, None, None) - h = output[0] - - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate([h])[0] - tf.logging.info(np.array_repr(actual_layer_output)) - # pylint: disable=bad-whitespace - # pyformat: disable - expected_layer_output = [ - [[ 0.5904724 , 0.05267439, 0.89581013, 0.63010913], - [ 0.79584485, 0.07670615, 0.40381077, 0.26504567]], - [[ 0.35448784, 0.28477612, 0.05394353, 0.06531866], - [ 0.44413447, 0.81940264, 0.98786688, 0.35846332]], - [[ 0.66811442, 0.07942203, 0.56781054, 0.83598584], - [ 0.45858502, 0.44949403, 0.06522893, 0.10947803]], - [[ 0.58166796, 0.94657594, 0.17643142, 0.02062288], - [ 0.40596515, 0.01996579, 0.93727112, 0.97478259]], - [[ 0.34873158, 0.0095871 , 0.34063059, 0.64620447], - [ 0.70584863, 0.69263214, 0.38247514, 0.28985959]], - [[ 0.66496903, 0.20383522, 0.35497066, 0.66646087], - [ 0.0787568 , 0.26172587, 0.23034802, 0.88751978]], - [[ 0.68153989, 0.81061888, 0.90142977, 0.87612331], - [ 0.15129775, 0.56084079, 0.87029755, 0.37908044]]] - # pyformat: enable - # pylint: enable=bad-whitespace - self.assertAllClose(expected_layer_output, actual_layer_output) - - def testEvolvedTransformerDecoderLayerExtendStep(self): - with self.session(use_gpu=True): - np.random.seed(6348575) - depth = 4 - p = GPipeEvolvedTransformerDecoderLayer.Params() - p.name = 'gpipe_evolved_transformer_decoder' - p.source_dim = depth - p.has_aux_atten = True - p.mask_self_atten = True - p.tr_double_heads_atten_tpl.num_attention_heads = 2 - p.tr_atten_tpl.num_attention_heads = 2 - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - et_decoder = GPipeEvolvedTransformerDecoderLayer(p) - - (source_vecs, _, aux_vecs, aux_paddings, input_tasks, - tgt_tasks) = self._testInputs(depth=depth) - source_padding = tf.zeros([5, 2]) - - output1 = et_decoder.FPropDefaultTheta(aux_vecs, aux_paddings, - source_vecs, source_padding, None, - None, input_tasks, tgt_tasks) - h1 = output1[2] - out_src_task, out_tgt_task = output1[-2], output1[-1] - h2 = [] - - double_head_attention_states = py_utils.NestedMap( - key=tf.zeros([0, 2, 4]), value=tf.zeros([0, 2, 4])) - transformer_layer_states = py_utils.NestedMap( - key=tf.zeros([0, 2, 4]), value=tf.zeros([0, 2, 4])) - branched_convs_input = tf.zeros([0, 2, 4]) - - prefix_states = py_utils.NestedMap( - double_head_attention_states=double_head_attention_states, - transformer_layer_states=transformer_layer_states, - branched_convs_input=branched_convs_input) - - for i in range(5): - h, _, prefix_states = et_decoder.ExtendStep(et_decoder.theta, - source_vecs[i, :, :], - prefix_states, aux_vecs, - aux_paddings) - h2.append(h) - - h2 = tf.stack(h2) - - self.evaluate(tf.global_variables_initializer()) - h1_v, h2_v = self.evaluate([h1, h2]) - self.assertAllClose(h1_v, h2_v) - self.assertAllClose(out_src_task, input_tasks) - self.assertAllClose(out_tgt_task, tgt_tasks) - - -def _AddClassesToTestParams(base_parameters_set, class_parameters_set): - output_parameters = [] - for class_parameters in class_parameters_set: - for base_parameters in base_parameters_set: - testcase_name = ( - base_parameters['testcase_name'] + class_parameters['testcase_name']) - new_parameters = base_parameters.copy() - new_parameters.update(class_parameters) - new_parameters['testcase_name'] = testcase_name - output_parameters.append(new_parameters) - return output_parameters - - -def _TransformerParamsWithEmbeddings(num_decoder_layers=0, - num_encoder_layers=4, - splits=1, - num_micro_batches=1, - has_softmax=False, - use_task_ids=False): - model_dim = 4 - params = GPipeTransformerStack.Params() - params.name = 'transformer' - params.model_dim = model_dim - params.num_decoder_layers = num_decoder_layers - params.decoder_tpl.source_dim = model_dim - params.decoder_tpl.tr_atten_tpl.num_attention_heads = 1 - params.decoder_tpl.tr_fflayer_tpl.hidden_dim = model_dim - params.num_encoder_layers = num_encoder_layers - params.encoder_tpl.source_dim = model_dim - params.encoder_tpl.tr_atten_tpl.num_attention_heads = 1 - params.encoder_tpl.tr_fflayer_tpl.hidden_dim = model_dim - params.num_micro_batches = num_micro_batches - params.state_dtype = tf.float32 - if has_softmax: - params.softmax_tpl.input_dim = model_dim - params.softmax_tpl.num_classes = 2 - else: - params.softmax_tpl = None - - emb_params = params.emb_tpl - # Default config for the token embedding. - emb_params.token_emb.use_matmul = True - emb_params.token_emb.use_3d_weight_tensor = False - emb_params.token_emb.vocab_size = 10 - emb_params.token_emb.embedding_dim = model_dim - - # Default config for the position embedding. - emb_params.position_emb.embedding_dim = model_dim - emb_params.position_emb.trainable_scaling = False - - # Task embeddings. - if use_task_ids: - emb_params.enc_task_emb = emb_params.token_emb.Copy() - emb_params.dec_task_emb = emb_params.token_emb.Copy() - params.splits = splits - params.random_seed = 0 - return params - - -def _EvolvedTransformerParamsWithEmbeddings(num_decoder_layers=0, - num_encoder_layers=4, - splits=1, - num_micro_batches=1, - has_softmax=False, - use_task_ids=False): - model_dim = 4 - params = GPipeEvolvedTransformerStack.Params() - params.name = 'evolved_transformer' - params.model_dim = model_dim - params.num_decoder_layers = num_decoder_layers - params.decoder_tpl.source_dim = model_dim - params.decoder_tpl.tr_atten_tpl.num_attention_heads = 1 - params.decoder_tpl.tr_double_heads_atten_tpl.num_attention_heads = 1 - params.decoder_tpl.transformer_tpl.tr_atten_tpl.num_attention_heads = 1 - params.num_encoder_layers = num_encoder_layers - params.encoder_tpl.source_dim = model_dim - params.encoder_tpl.transformer_tpl.tr_atten_tpl.num_attention_heads = 1 - params.num_micro_batches = num_micro_batches - params.state_dtype = tf.float32 - if has_softmax: - params.softmax_tpl.input_dim = model_dim - params.softmax_tpl.num_classes = 2 - else: - params.softmax_tpl = None - - emb_params = params.emb_tpl - # Default config for the token embedding. - emb_params.token_emb.use_matmul = True - emb_params.token_emb.use_3d_weight_tensor = False - emb_params.token_emb.vocab_size = 10 - emb_params.token_emb.embedding_dim = model_dim - - # Default config for the position embedding. - emb_params.position_emb.embedding_dim = model_dim - emb_params.position_emb.trainable_scaling = False - - # Task embeddings. - if use_task_ids: - emb_params.enc_task_emb = emb_params.token_emb.Copy() - emb_params.dec_task_emb = emb_params.token_emb.Copy() - params.splits = splits - params.random_seed = 0 - return params - - -def _TransformerRandomInputs(batch): - input_arr = np.array([ - [[0, 1]] * batch, - [[1, -1]] * batch, - ]) - paddings_arr = np.array([[0] * batch] * 2) - tgt_input_arr = np.array([ - [[1, 2]] * batch, - [[1, -1]] * batch, - [[2, 1]] * batch, - ]) - tgt_paddings_arr = np.array([[0] * batch] * 3) - inputs = tf.constant(input_arr.tolist(), dtype=tf.float32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - tgt_inputs = tf.constant(tgt_input_arr.tolist(), dtype=tf.float32) - tgt_paddings = tf.constant(tgt_paddings_arr.tolist(), dtype=tf.float32) - return inputs, paddings, tgt_inputs, tgt_paddings - - -def _TransformerRandomInputsIds(batch): - input_arr = np.array([[6] * batch, [4] * batch]) - paddings_arr = np.array([[0] * batch] * 2) - input_task_arr = np.array([[0] * batch, [0] * batch]) - tgt_input_arr = np.array([[3] * batch, [7] * batch, [9] * batch]) - tgt_paddings_arr = np.array([[0] * batch] * 3) - tgt_task_arr = np.array([[0] * batch] * 3) - inputs = tf.constant(input_arr.tolist(), dtype=tf.int32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - input_tasks = tf.constant(input_task_arr.tolist(), dtype=tf.int32) - tgt_inputs = tf.constant(tgt_input_arr.tolist(), dtype=tf.int32) - tgt_paddings = tf.constant(tgt_paddings_arr.tolist(), dtype=tf.float32) - tgt_tasks = tf.constant(tgt_task_arr.tolist(), dtype=tf.int32) - return inputs, paddings, tgt_inputs, tgt_paddings, input_tasks, tgt_tasks - - -def _TransformerRandomInputsVecs(batch): - input_arr = np.array([[[0, 1, 1, -1]] * batch, [[0, 2, 7, 1]] * batch]) - paddings_arr = np.array([[0] * batch] * 2) - tgt_input_arr = np.array([[[1, 2, 0, 1]] * batch, [[1, -1, 1, 0]] * batch, - [[2, 1, 2, 1]] * batch]) - tgt_paddings_arr = np.array([[0] * batch] * 3) - inputs = tf.constant(input_arr.tolist(), dtype=tf.float32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - tgt_inputs = tf.constant(tgt_input_arr.tolist(), dtype=tf.float32) - tgt_paddings = tf.constant(tgt_paddings_arr.tolist(), dtype=tf.float32) - return inputs, paddings, tgt_inputs, tgt_paddings - - -def _EvolvedTransformerRandomInputs(batch): - padding_length = 10 - input_padding = [[[0, 0]] * batch] * padding_length - input_arr = np.array([[[0, 1]] * batch] + input_padding + [[[1, -1]] * batch]) - padding_indexes = [[1] * batch] * padding_length - paddings_arr = np.array([[0] * batch] + padding_indexes + [[0] * batch]) - tgt_input_arr = np.array([[[1, 2]] * batch] + input_padding + - [[[1, -1]] * batch] + input_padding + - [[[2, 1]] * batch]) - tgt_paddings_arr = np.array([[0] * batch] + padding_indexes + [[0] * batch] + - padding_indexes + [[0] * batch]) - inputs = tf.constant(input_arr.tolist(), dtype=tf.float32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - tgt_inputs = tf.constant(tgt_input_arr.tolist(), dtype=tf.float32) - tgt_paddings = tf.constant(tgt_paddings_arr.tolist(), dtype=tf.float32) - return inputs, paddings, tgt_inputs, tgt_paddings - - -def _EvolvedTransformerRandomInputsIds(batch): - padding_length = 10 - input_padding = [[0] * batch] * padding_length - input_arr = np.array([[6] * batch] + input_padding + [[4] * batch]) - input_task_arr = np.array([[0] * batch] + input_padding + [[0] * batch]) - padding_indexes = [[1] * batch] * padding_length - paddings_arr = np.array([[0] * batch] + padding_indexes + [[0] * batch]) - tgt_input_arr = np.array([[3] * batch] + input_padding + [[7] * batch] + - input_padding + [[9] * batch]) - tgt_task_arr = np.array([[0] * batch] + input_padding + [[0] * batch] + - input_padding + [[0] * batch]) - tgt_paddings_arr = np.array([[0] * batch] + padding_indexes + [[0] * batch] + - padding_indexes + [[0] * batch]) - inputs = tf.constant(input_arr.tolist(), dtype=tf.int32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - input_tasks = tf.constant(input_task_arr.tolist(), dtype=tf.int32) - tgt_inputs = tf.constant(tgt_input_arr.tolist(), dtype=tf.int32) - tgt_paddings = tf.constant(tgt_paddings_arr.tolist(), dtype=tf.float32) - tgt_tasks = tf.constant(tgt_task_arr.tolist(), dtype=tf.int32) - return inputs, paddings, tgt_inputs, tgt_paddings, input_tasks, tgt_tasks - - -class GPipeTransformerStackTest(test_utils.TestCase, - parameterized.TestCase): # was tf.test.TestCase - """Tests for GPipeTransformerStack layer.""" - - @parameterized.named_parameters({ - 'testcase_name': '_one_split', - 'splits': 1 - }, { - 'testcase_name': '_two_splits', - 'splits': 2 - }) - def testGPipeTransformerBatchMajorConstruction(self, splits=1): - batch = 4 - tf.flags.FLAGS.tpu_compatible = True - with self.session(): - with tf.variable_scope('transformer_test', reuse=tf.AUTO_REUSE): - params = _TransformerParamsWithEmbeddings( - splits=splits, num_decoder_layers=4, has_softmax=True) - params.batch_dim = 0 - params.emb_tpl.batch_dim = 0 - xformer = params.Instantiate() - input_ids, id_paddings, tgt_inputs, tgt_paddings, _, _ = ( - _TransformerRandomInputsIds(batch=batch)) - input_ids = tf.transpose(input_ids) - id_paddings = tf.transpose(id_paddings) - tgt_inputs = tf.transpose(tgt_inputs) - tgt_paddings = tf.transpose(tgt_paddings) - labels = tf.ones([batch, tgt_inputs.shape.as_list()[1]], dtype=tf.int32) - label_weights = tf.ones([batch, tgt_inputs.shape.as_list()[1]]) - tf.random.set_seed(1234) - self.evaluate(tf.global_variables_initializer()) - xent, logits = xformer.FProp(xformer.theta, input_ids, id_paddings, - tgt_inputs, tgt_paddings, None, None, - labels, label_weights, None, None, None, - None) - xent_out, logits_out = self.evaluate([xent, logits]) - print('xent_out={}'.format(xent_out)) - print('logits_out={}'.format(logits_out)) - - @parameterized.named_parameters({ - 'testcase_name': '_one_split', - 'splits': 1 - }, { - 'testcase_name': '_two_splits', - 'splits': 2 - }) - def testGPipeTransformerFPropPackedInputWithEmbeddings(self, splits=1): - batch = 4 - tf.flags.FLAGS.tpu_compatible = True - with self.session(): - with tf.variable_scope('transformer_test', reuse=tf.AUTO_REUSE): - params = _TransformerParamsWithEmbeddings( - splits=splits, num_decoder_layers=2) - params.dtype = tf.float32 - params.fprop_dtype = tf.float32 - packed_params = params.Copy() - packed_params.packed_input = True - xformer = GPipeTransformerStack(params) - packed_xformer = GPipeTransformerStack(packed_params) - # Prepare inputs - inputs, paddings, tgt_inputs, tgt_paddings, _, _ = _TransformerRandomInputsIds( - batch) - packed_inputs = tf.reshape(inputs, [-1, 1]) - packed_tgt_inputs = tf.reshape(tgt_inputs, [-1, 1]) - packed_paddings = tf.reshape(paddings, [-1, 1]) - packed_tg_paddings = tf.reshape(tgt_paddings, [-1, 1]) - segment_ids = tf.transpose( - tf.constant([[0, 1, 2, 3, 0, 1, 2, 3]], dtype=tf.float32)) - tgt_segment_id = tf.transpose( - tf.constant([[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]], - dtype=tf.float32)) - segment_segment_pos = tf.transpose( - tf.constant([[0, 0, 0, 0, 1, 1, 1, 1]], dtype=tf.int32)) - tgt_segment_segment_pos = tf.transpose( - tf.constant([[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]], dtype=tf.int32)) - - output = xformer.FProp(xformer.theta, inputs, paddings, tgt_inputs, - tgt_paddings)[2] - packed_output = packed_xformer.FProp( - packed_xformer.theta, packed_inputs, packed_paddings, - packed_tgt_inputs, packed_tg_paddings, segment_ids, tgt_segment_id, - None, None, segment_segment_pos, tgt_segment_segment_pos)[2] - packed_output = tf.reshape(packed_output, output.shape) - - self.evaluate(tf.global_variables_initializer()) - output, packed_output = self.evaluate([output, packed_output]) - self.assertAllClose(output, packed_output, rtol=1e-05, atol=1e-05) - - @parameterized.named_parameters( - { - 'testcase_name': '_split1', - 'splits': 1, - 'num_micro_batches': 1 - }, { - 'testcase_name': '_split1_nmb2', - 'splits': 1, - 'num_micro_batches': 2 - }, { - 'testcase_name': '_split2_nmb2', - 'splits': 2, - 'num_micro_batches': 2 - }, { - 'testcase_name': '_split_manual_nmb2', - 'splits': [3, 4], - 'num_micro_batches': 2 - }) - def testGPipeTransformerStackTrainTransparentFPropWithEmbeddings( - self, splits=1, num_micro_batches=1): - # time = 2, - batch = 4 - with self.session(): - params = _TransformerParamsWithEmbeddings( - splits=splits, - num_micro_batches=num_micro_batches, - num_decoder_layers=3, - num_encoder_layers=1) - params.is_transparent = True - params.transparent_merger_dropout_prob = 0.0 - xformer = GPipeTransformerStack(params) - - input_ids, id_paddings, tgt_inputs, tgt_paddings, _, _ = _TransformerRandomInputsIds( - batch=batch) - inputs, paddings, _, _ = _TransformerRandomInputsVecs(batch=batch) - tf.random.set_seed(1234) - self.evaluate(tf.global_variables_initializer()) - enc_outputs = xformer.EncoderFPropDefaultTheta(inputs, paddings) - dec_output = xformer.FProp(xformer.theta, input_ids, id_paddings, - tgt_inputs, tgt_paddings)[2] - enc_out_1 = self.evaluate(enc_outputs) - dec_out = self.evaluate(dec_output) - self.assertAllClose( - [[[0.017581, 0.802863, 0.975554, -1.164572]] * batch, - [[-0.549953, 1.196884, 4.910457, -0.102137]] * batch], enc_out_1) - self.assertAllClose( - [[[-1.122128, 1.111972, 4.642949, -2.14831]] * batch, - [[-1.336919, 1.182709, 4.785938, -2.039246]] * batch, - [[-1.335168, 1.297679, 4.720459, -2.111006]] * batch], dec_out) - - # pylint: disable=bad-continuation - # pyformat: disable - @parameterized.named_parameters( - _AddClassesToTestParams(({ - 'testcase_name': '_split1', - 'splits': 1, - 'num_micro_batches': 1 - }, { - 'testcase_name': '_split1_nmb2', - 'splits': 1, - 'num_micro_batches': 2 - }, { - 'testcase_name': '_split2_nmb1', - 'splits': 2, - 'num_micro_batches': 1 - }, { - 'testcase_name': '_split2_nmb2', - 'splits': 2, - 'num_micro_batches': 2 - }), ({ - 'testcase_name': - '_transformer', - 'params_fn': - _TransformerParamsWithEmbeddings, - 'stack_cls': - GPipeTransformerStack, - 'inputs_fn': - _TransformerRandomInputsIds, - 'expected_output': - [[[-2.306972, 2.07353, 5.542715, -2.421023]] * 4, - [[-2.366269, 2.0730767, 5.573641, -2.371158]] * 4, - [[-2.379758, 2.092285, 5.570339, -2.370932]] * 4] - }, { - 'testcase_name': - '_evolved_transformer', - 'params_fn': - _EvolvedTransformerParamsWithEmbeddings, - 'stack_cls': - GPipeEvolvedTransformerStack, - 'inputs_fn': - _EvolvedTransformerRandomInputsIds, - 'expected_output': [ - [[-0.563482, 0.873324, 4.807824, -2.21867]] * 4, - [[-0.69335, 0.275365, 3.85347, -1.25291]] * 4, - [[-0.691535, 0.513666, 3.818152, -1.448952]] * 4, - [[-0.906161, 0.207199, 3.865673, -1.015405]] * 4, - [[-0.779552, 0.199017, 3.864474, -1.115817]] * 4, - [[-0.733586, 0.229975, 3.860421, -1.181348]] * 4, - [[-0.712061, 0.24913, 3.857376, -1.215363]] * 4, - [[-0.697319, 0.267139, 3.854558, -1.242636]] * 4, - [[-0.667346, 0.455872, 3.825749, -1.421671]] * 4, - [[-0.933211, 0.304563, 3.863014, -1.08345]] * 4, - [[-0.804276, 0.187855, 3.865573, -1.084767]] * 4, - [[-0.481617, 0.657398, 4.772114, -2.071473]] * 4, - [[-0.658759, 0.130042, 3.853722, -1.142575]] * 4, - [[-0.641755, 0.148885, 3.851666, -1.173375]] * 4, - [[-0.623731, 0.205668, 3.847772, -1.23985]] * 4, - [[-0.827914, 0.091931, 3.861872, -0.967819]] * 4, - [[-0.74599, 0.066758, 3.858107, -1.010512]] * 4, - [[-0.69355, 0.099376, 3.856509, -1.085727]] * 4, - [[-0.664674, 0.124304, 3.854321, -1.132531]] * 4, - [[-0.646149, 0.143505, 3.852249, -1.164976]] * 4, - [[-0.630899, 0.169327, 3.849945, -1.200752]] * 4, - [[-0.83285, 0.199918, 3.866308, -1.072462]] * 4, - [[-0.440338, 0.562651, 4.749378, -2.003749]] * 4 - ]}))) - # pyformat: enable - # pylint: enable=bad-continuation - def testGPipeTransformerDecoderStackFPropWithEmbeddings( - self, - params_fn, - expected_output, - inputs_fn, - stack_cls, - splits=1, - num_micro_batches=1): - batch = 4 - tf.flags.FLAGS.tpu_compatible = True - with self.session(): - params = params_fn( - num_decoder_layers=4, - num_encoder_layers=0, - splits=splits, - num_micro_batches=num_micro_batches) - params.dtype = tf.float32 - xformer = stack_cls(params) - - inputs, paddings, tgt_inputs, tgt_paddings, _, _ = inputs_fn(batch) - - output = xformer.FProp(xformer.theta, inputs, paddings, tgt_inputs, - tgt_paddings)[2] - - self.evaluate(tf.global_variables_initializer()) - output_val = self.evaluate(output) - self.assertAllCloseAccordingToType( - expected_output, output_val, rtol=1e-05, atol=1e-05) - - @parameterized.named_parameters( - _AddClassesToTestParams(({ - 'testcase_name': '_split1', - 'splits': 1, - 'num_micro_batches': 1 - }, { - 'testcase_name': '_split1_nmb2', - 'splits': 1, - 'num_micro_batches': 2 - }, { - 'testcase_name': '_split2_nmb1', - 'splits': 2, - 'num_micro_batches': 1 - }, { - 'testcase_name': '_split2_nmb2', - 'splits': 2, - 'num_micro_batches': 2 - }), ({ - 'testcase_name': '_transformer', - 'params_fn': _TransformerParamsWithEmbeddings, - 'stack_cls': GPipeTransformerStack, - 'inputs_fn': _TransformerRandomInputsIds - }, { - 'testcase_name': '_evolved_transformer', - 'params_fn': _EvolvedTransformerParamsWithEmbeddings, - 'stack_cls': GPipeEvolvedTransformerStack, - 'inputs_fn': _EvolvedTransformerRandomInputsIds - }))) - def testGPipeTransformerLmModel(self, - params_fn, - stack_cls, - inputs_fn, - splits=1, - num_micro_batches=1): - batch = 4 - tf.flags.FLAGS.tpu_compatible = True - with self.session(): - with tf.variable_scope('transformer_test', reuse=tf.AUTO_REUSE): - params = params_fn( - splits=splits, - num_micro_batches=num_micro_batches, - num_decoder_layers=0, - has_softmax=True) - params.state_dtype = tf.float32 - xformer = stack_cls(params) - - input_ids, id_paddings, _, _, _, _ = inputs_fn(batch=batch) - labels = tf.ones([input_ids.shape.as_list()[0], batch], dtype=tf.int32) - label_weights = tf.ones([input_ids.shape.as_list()[0], batch]) - tf.random.set_seed(1234) - self.evaluate(tf.global_variables_initializer()) - xent, logits = xformer.FProp(xformer.theta, input_ids, id_paddings, None, - None, None, None, labels, label_weights) - xent_out, logits_out = self.evaluate([xent, logits]) - print('xent_out={}'.format(xent_out)) - print('logits_out={}'.format(logits_out)) - - @parameterized.named_parameters( - _AddClassesToTestParams(({ - 'testcase_name': '_split1', - 'splits': 1, - 'num_micro_batches': 1 - }, { - 'testcase_name': '_split1_nmb2', - 'splits': 1, - 'num_micro_batches': 2 - }, { - 'testcase_name': '_split2_nmb1', - 'splits': 2, - 'num_micro_batches': 1 - }, { - 'testcase_name': '_split2_nmb2', - 'splits': 2, - 'num_micro_batches': 2 - }, { - 'testcase_name': '_split1_task_embs', - 'use_task_embs': True - }), ({ - 'testcase_name': '_transformer', - 'params_fn': _TransformerParamsWithEmbeddings, - 'stack_cls': GPipeTransformerStack, - 'inputs_fn': _TransformerRandomInputsIds - }, { - 'testcase_name': '_evolved_transformer', - 'params_fn': _EvolvedTransformerParamsWithEmbeddings, - 'stack_cls': GPipeEvolvedTransformerStack, - 'inputs_fn': _EvolvedTransformerRandomInputsIds - }))) - def testGPipeTransformerMtModel(self, - params_fn, - stack_cls, - inputs_fn, - splits=1, - num_micro_batches=1, - use_task_embs=False): - batch = 4 - tf.flags.FLAGS.tpu_compatible = True - with self.session(): - with tf.variable_scope('transformer_test', reuse=tf.AUTO_REUSE): - params = params_fn( - splits=splits, - num_micro_batches=num_micro_batches, - num_decoder_layers=2, - has_softmax=True, - use_task_ids=use_task_embs) - params.state_dtype = tf.float32 - xformer = stack_cls(params) - - input_ids, id_paddings, tgt_inputs, tgt_paddings, input_task_ids, tgt_task_ids = ( - inputs_fn(batch=batch)) - labels = tf.ones([tgt_inputs.shape.as_list()[0], batch], dtype=tf.int32) - label_weights = tf.ones([tgt_inputs.shape.as_list()[0], batch]) - tf.random.set_seed(1234) - self.evaluate(tf.global_variables_initializer()) - xent, logits = xformer.FProp(xformer.theta, input_ids, id_paddings, - tgt_inputs, tgt_paddings, None, None, labels, - label_weights, None, None, input_task_ids, - tgt_task_ids) - xent_out, logits_out = self.evaluate([xent, logits]) - print('xent_out={}'.format(xent_out)) - print('logits_out={}'.format(logits_out)) - - -def _BatchMajorTransformerParams(splits=1, - num_micro_batches=1, - packed_input=True): - model_dim = 4 - params = layers_with_gpipe.GPipeBatchMajorTransformerStack.Params() - params.name = 'transformer' - params.model_dim = model_dim - params.packed_input = packed_input - params.num_decoder_layers = 4 - params.decoder_tpl.input_dim = model_dim - params.decoder_tpl.tr_atten_tpl.num_heads = 1 - params.decoder_tpl.tr_fflayer_tpl.hidden_dim = model_dim - params.decoder_tpl.mask_self_atten = True - params.decoder_tpl.has_aux_atten = True - params.num_encoder_layers = 2 - params.encoder_tpl.input_dim = model_dim - params.encoder_tpl.tr_atten_tpl.num_heads = 1 - params.encoder_tpl.tr_fflayer_tpl.hidden_dim = model_dim - params.state_dtype = tf.float32 - params.softmax_tpl.input_dim = model_dim - params.softmax_tpl.num_classes = 2 - - emb_params = params.emb_tpl - # Default config for the token embedding. - emb_params.token_emb.use_matmul = True - emb_params.token_emb.use_3d_weight_tensor = False - emb_params.token_emb.vocab_size = 10 - emb_params.token_emb.embedding_dim = model_dim - - # Default config for the position embedding. - emb_params.position_emb.embedding_dim = model_dim - emb_params.position_emb.trainable_scaling = False - params.splits = splits - params.num_micro_batches = num_micro_batches - params.random_seed = 0 - return params - - -def _BatchMajorTransformerRandomInputs(batch): - input_arr = np.array([[6, 4]] * batch) - paddings_arr = np.array([[0] * 2] * batch) - input_seg_arr = np.array([[0, 1]] * batch) - input_pos_arr = np.array([[0, 0]] * batch) - tgt_input_arr = np.array([[3, 7, 9]] * batch) - tgt_paddings_arr = np.array([[0] * 3] * batch) - tgt_seg_arr = np.array([[0, 1, 1]] * batch) - tgt_pos_arr = np.array([[0, 0, 1]] * batch) - inputs = tf.constant(input_arr.tolist(), dtype=tf.int32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - input_seg = tf.constant(input_seg_arr.tolist(), dtype=tf.int32) - input_pos = tf.constant(input_pos_arr.tolist(), dtype=tf.int32) - tgt_inputs = tf.constant(tgt_input_arr.tolist(), dtype=tf.int32) - tgt_paddings = tf.constant(tgt_paddings_arr.tolist(), dtype=tf.float32) - tgt_seg = tf.constant(tgt_seg_arr.tolist(), dtype=tf.int32) - tgt_pos = tf.constant(tgt_pos_arr.tolist(), dtype=tf.int32) - return (inputs, paddings, tgt_inputs, tgt_paddings, input_seg, tgt_seg, - input_pos, tgt_pos) - - -class GPipeBatchMajorTransformerStackTest(test_utils.TestCase, - parameterized.TestCase - ): # was tf.test.TestCase - """Tests for GPipeBatchMajorTransformerStack layer.""" - - @parameterized.named_parameters( - { - 'testcase_name': '_one_split_one_mb_packed', - 'splits': 1, - 'num_micro_batches': 1, - 'packed_input': True - }, { - 'testcase_name': '_two_splits_one_mb_packed', - 'splits': 2, - 'num_micro_batches': 1, - 'packed_input': True - }, { - 'testcase_name': '_two_splits_two_mb_packed', - 'splits': 2, - 'num_micro_batches': 2, - 'packed_input': True - }, { - 'testcase_name': '_one_split_two_mb_packed', - 'splits': 1, - 'num_micro_batches': 2, - 'packed_input': True - }, { - 'testcase_name': '_one_split_one_mb_unpacked', - 'splits': 1, - 'num_micro_batches': 1, - 'packed_input': False - }, { - 'testcase_name': '_two_splits_one_mb_unpacked', - 'splits': 2, - 'num_micro_batches': 1, - 'packed_input': False - }, { - 'testcase_name': '_two_splits_two_mb_unpacked', - 'splits': 2, - 'num_micro_batches': 2, - 'packed_input': False - }, { - 'testcase_name': '_one_split_two_mb_unpacked', - 'splits': 1, - 'num_micro_batches': 2, - 'packed_input': False - }) - def testGPipeBatchMajorTransformerFProp(self, - splits=1, - num_micro_batches=1, - packed_input=True): - batch = 4 - tf.flags.FLAGS.tpu_compatible = True - with self.session(): - with tf.variable_scope('transformer_test', reuse=tf.AUTO_REUSE): - params = _BatchMajorTransformerParams(splits, num_micro_batches, - packed_input) - xformer = params.Instantiate() - (input_ids, id_paddings, tgt_inputs, tgt_paddings, input_seg, tgt_seg, - input_pos, tgt_pos) = ( - _BatchMajorTransformerRandomInputs(batch=batch)) - labels = tf.ones([batch, tgt_inputs.shape.as_list()[1]], dtype=tf.int32) - label_weights = tf.ones([batch, tgt_inputs.shape.as_list()[1]]) - tf.random.set_seed(1234) - self.evaluate(tf.global_variables_initializer()) - xent, logits = xformer.FProp(xformer.theta, input_ids, id_paddings, - tgt_inputs, tgt_paddings, input_seg, - tgt_seg, labels, label_weights, input_pos, - tgt_pos) - xent_out, logits_out = self.evaluate([xent, logits]) - print('xent_out={}'.format(xent_out)) - print('logits_out={}'.format(logits_out)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/learner.py b/lingvo/core/learner.py deleted file mode 100644 index ffa47fba..00000000 --- a/lingvo/core/learner.py +++ /dev/null @@ -1,454 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""A learner optimizes a subset of variables according to a loss. - -It consists of a learning rate schedule, an optimizer, and gradient clipping -mechanisms. A BaseTask can have multiple learners, each optimizing a (usually -disjoint) subset of variables. -""" - -import re -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import summary_utils - - -class Learner(base_layer.BaseLayer): - """A training program layer. - - The layer takes a loss tensor as input and outputs a trainer op. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'l2_regularizer_weight', None, - 'If not None, L2 regularization to apply to the weights. ' - 'Otherwise, disable L2 regularization.') - p.Define( - 'loss_name', None, 'Name(s) of the loss(es) this learner to optimize. ' - 'If not set, use learner name directly. ' - 'If given as a list, the gradients will be combined via a ' - 'GradientCombiner created from p.gradient_combiner, which must be ' - 'specified as well.') - p.Define( - 'gradient_combiner', None, - 'Params of a gradient_combiner.GradientCombiner used to combine ' - 'gradients from multiple losses.') - p.Define( - 'l1_regularizer_weight', None, - 'If not None, L1 regularization to apply to the weights. ' - 'Otherwise, disable L1 regularization.') - p.Define('learning_rate', 0.0, 'learning rate to use.') - p.Define( - 'clip_gradient_norm_to_value', 0.0, - 'Clip gradient by global norm to this value. This is similar to ' - 'the bahaviour of tf.clip_by_global_norm, if you are looking for ' - 'tf.clip_by_norm refer to clip_gradient_single_norm_to_value. Note ' - 'these are mutually exclusive.') - p.Define( - 'clip_gradient_single_norm_to_value', 0.0, - 'Clip gradient by single tensor norm to this value. This is ' - 'similar to the bahaviour of tf.clip_by_norm. Note this is mutually ' - 'exlusive to using clip_gradient_norm_to_value.') - p.Define('grad_norm_to_clip_to_zero', 0.0, - 'Clip gradient to 0 if its norm exceeds this value.') - p.Define('grad_norm_tracker', None, 'Params for GradNormTracker.') - p.Define('optimizer', optimizer.Adam.Params(), 'Params for the optimizer.') - p.Define('lr_schedule', schedule.ContinuousSchedule.Params(), - 'Learning rate decay schedule.') - p.Define( - 'bprop_variable_filter', None, - 'If set, only backprop variables whose names partially match ' - 'this regexp (re.search).') - p.Define( - 'bprop_variable_exclusion', None, - 'If set, do not backprop variables whose names partially match ' - 'this regexp (re.search).') - p.Define( - 'grad_aggregation_method', tf.AggregationMethod.EXPERIMENTAL_TREE, - 'Specifies the method used to combine gradient terms. Accepted ' - 'values are constants defined in the class AggregationMethod.') - p.Define( - 'gate_gradients', False, - 'If True, add a tuple around the gradients returned for an ' - 'operations. This avoids some race conditions.') - p.Define('colocate_gradients_with_ops', True, - 'If True, try colocating gradients with the corresponding op.') - p.Define( - 'skip_zero_gradients', None, - 'If set, skips aggregating zero gradients while computing gradients.' - 'This helps in case where some weights may not be used in forward ' - 'computation, e.g., sparsely activated networks or switchable layers ' - 'in neural architectural search. ' - 'Possible values are: ' - 'None: do not skip zero gradients; ' - '"variable": skip if the entire variable gradients are almost zero; ' - '"weight": skip if the individual weight gradients are almost zero.') - p.Define('scale_gradients', True, - 'Whether to apply gradients adjustment and scaling.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - self._var_grads = None - self._eval_metrics = {} - if p.grad_norm_tracker: - self.CreateChild('grad_norm_tracker', p.grad_norm_tracker) - self.CreateChild('lr_schedule', p.lr_schedule) - self.CreateChild('optimizer', p.optimizer) - if isinstance(p.loss_name, (list, tuple)): - assert p.gradient_combiner - self.CreateChild('gradient_combiner', p.gradient_combiner) - else: - assert p.gradient_combiner is None - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - if self.params.grad_norm_tracker: - self.grad_norm_tracker.InstantiateVariables() - self.lr_schedule.InstantiateVariables() - self.optimizer.InstantiateVariables() - super()._CreateChildrenVariables() - - def GetVarGrads(self): - return self._var_grads - - def GetTrainableVariables(self, vmap): - p = self.params - pos = re.compile( - p.bprop_variable_filter) if p.bprop_variable_filter else None - neg = re.compile( - p.bprop_variable_exclusion) if p.bprop_variable_exclusion else None - - def VariableFilter(v): - """Returns True if variable v should be optimized by this learner.""" - if not v.trainable: - return False - - if pos and not pos.search(v.name): - tf.logging.info('%s: disabled by bprop_variable_filter: %s', p.name, - v.name) - return False - if neg and neg.search(v.name): - tf.logging.info('%s: disabled by bprop_variable_exclusion: %s', p.name, - v.name) - return False - return True - - return vmap.Filter(VariableFilter) - - def ApplyPostTrainingLoop(self, global_step): - """Applies any computation to run after each tpu trainining loop. - - Args: - global_step: Global step variable. - - Returns: - Ops to run after training loop ends. - """ - return self.optimizer.ApplyPostTrainingLoop(global_step) - - def LearningRate(self, step): - p = self.params - lrs = self.lr_schedule.Value(step) - lrs.set_shape([]) - self._AddEvalMetric('lr_schedule', lrs, tf.constant(1.0)) - return p.learning_rate * lrs - - def Apply(self, metrics, vmap, gradient_mask=None, gradient_adjuster=None): - """Computes updates on 'vmap' to optimize 'loss'. - - TODO(rpang): explore merging gradient_mask and gradient_adjuster. - - Args: - metrics: A Dict[str, (value, weight)], from which loss can be extracted - according to p.loss_name. - vmap: A `.NestedMap` object containing variables to optimize. - gradient_mask: if not None, a dict mapping variable names to a 0/1 scalar. - gradient_adjuster: if not None, a function that mutates a given var_grads. - - Returns: - (losses, op, eval_metrics), where - - losses is a list of scalar tensors; - - op is a tf.Operation to update variables; - - eval_metrics is a Dict[str, (value, weight)], where each value/weight - is a scalar tensor. - """ - # We apply gradients outside the name_scope to maintain backwards - # compatibility on variables created by self.optimizer.Apply(). - losses, var_grads = self._ComputeLossesAndGradients(metrics, vmap) - - var_grads, stats = self.AdjustGradients( - var_grads, - gradient_mask=gradient_mask, - gradient_adjuster=gradient_adjuster) - self._var_grads = var_grads - - assert self.theta.global_step is not None, self.theta - lr = self.LearningRate(self.theta.global_step) - - var_update_op = self.optimizer.Apply(lr, var_grads) - return losses, var_update_op, stats - - def _CustomComputeGradientsFn(self): - """Returns the compute_gradients_fn to use for py_utils.ComputeGradients.""" - return None # use the default function - - def _ComputeLossesAndGradients(self, metrics, vmap): - p = self.params - vmap = self.GetTrainableVariables(vmap) - - for v in vmap.Flatten(): - tf.logging.info('%s: bprop variable: %s', p.name, v.name) - - def LossAndGradients(metric_name): - """Returns (loss, var_grads) computed from metrics[metric_name].""" - metric = metrics.get(metric_name, None) - if metric is None: - raise ValueError('Loss %s not found in metrics %s' % - (metric_name, list(metrics.keys()))) - # TODO(b/154785713): pass (loss, loss_weight) to ComputeGradients(). - loss = metric[0] - return metric, self.optimizer.ComputeGradients( - loss, - vmap, - p.grad_aggregation_method, - p.colocate_gradients_with_ops, - p.gate_gradients, - compute_gradients_fn=self._CustomComputeGradientsFn(), - skip_zero_gradients=p.skip_zero_gradients, - skip_none_gradients=False) - - loss_name = p.loss_name or p.name - losses = [] - if isinstance(loss_name, (list, tuple)): - losses_and_grads = {} - variables = None - for metric_name in loss_name: - loss_metric, var_grads = LossAndGradients(metric_name) - losses_and_grads[metric_name] = py_utils.NestedMap( - loss_metric=loss_metric, - grads=tf.nest.map_structure(lambda vg: vg.grad, var_grads)) - current_vars = tf.nest.map_structure(lambda vg: vg.var, var_grads) - if variables is None: - variables = current_vars - else: - tf.nest.assert_same_structure(variables, current_vars) - losses.append(loss_metric[0]) - grads = self.gradient_combiner.Combine(variables, losses_and_grads) - var_grads = tf.nest.map_structure( - lambda v, g: py_utils.VarGrad(var=v, grad=g), variables, grads) - else: - loss_metric, var_grads = LossAndGradients(loss_name) - losses.append(loss_metric[0]) - - return losses, py_utils.SkipNoneGradients(var_grads) - - def AdjustGradients(self, - var_grads, - gradient_mask=None, - gradient_adjuster=None): - """Adjusts gradients according to learner params. - - Args: - var_grads: a `.NestedMap` whose values are (var, grad) pairs. - gradient_mask: if not None, a dict mapping variable names to a 0/1 scalar. - gradient_adjuster: if not None, a function that mutates a given var_grads. - - Returns: - (var_grads, eval_metrics), where var_grads is a `.NestedMap` whose values - (var, grad) pairs representing adjusted gradients. - """ - p = self.params - # L2 regularizer. - if p.l2_regularizer_weight is not None: - l2_loss, var_grads = py_utils.AdjustGradientsWithLpLoss( - var_grads, p.l2_regularizer_weight, p=2.0) - self._AddEvalMetric('l2_loss', l2_loss, tf.constant(1.0)) - - # L1 regularizer. - if p.l1_regularizer_weight is not None: - l1_loss, var_grads = py_utils.AdjustGradientsWithLpLoss( - var_grads, p.l1_regularizer_weight, p=1.0) - self._AddEvalMetric('l1_loss', l1_loss, tf.constant(1.0)) - - # Mask gradients only if the mask is set. - if gradient_mask: - var_grads = py_utils.MaskGradients(var_grads, gradient_mask) - - # Scale gradients, e.g., gradient clipping. - if p.scale_gradients: - scaled_vars = self.ScaleGradients( - var_grads, gradient_adjuster=gradient_adjuster) - var_grads = scaled_vars.final_var_grads - - # Histogram summary. - summary_utils.CollectVarHistogram(var_grads) - return var_grads, self._eval_metrics - - def _GetGlobalGradScale(self, all_grad_norm, has_nan_or_inf): - """Returns a scaling factor for all gradients according to their norm. - - In case there are NaN or Inf values the function will return 0.0. - - Args: - all_grad_norm: A scalar represeting the total norm of all vars. - has_nan_or_inf: A scalar of 0 or 1, indicating whether there is any NaN or - Inf in input gradients. - - Returns: - The gradient scale. 0 if gradient updates should be skipped for the step. - """ - p = self.params - # Computes gradient's scale. - grad_scale = tf.constant(1.0) - if p.clip_gradient_norm_to_value: - # If all_grad_norm > p.clip_gradient_norm_to_value, scales - # all_grads so that the norm is 1.0. - grad_scale = tf.minimum(1.0, - p.clip_gradient_norm_to_value / all_grad_norm) - - if p.grad_norm_to_clip_to_zero: - # If all_grad_norm > p.grad_norm_to_clip_to_zero, treats - # grad_scale as 0. This way, we ignore this step. - grad_scale *= tf.cast(all_grad_norm < p.grad_norm_to_clip_to_zero, - p.dtype) - - if p.grad_norm_tracker: - grad_scale *= self.grad_norm_tracker.FPropDefaultTheta( - all_grad_norm, has_nan_or_inf) - - # Force grad_scale to be 0 if there is any NaN or Inf in gradients. - grad_scale = tf.where(has_nan_or_inf, 0.0, grad_scale) - - return grad_scale - - def ScaleGradients(self, var_grads, gradient_adjuster=None): - """Scales gradients according to training params. - - Args: - var_grads: a `.NestedMap` whose values are (var, grad) pairs. - gradient_adjuster: if not None, a function that mutates a given var_grads. - - Returns: - A `.NestedMap` containing - - - final_var_grads: a `.NestedMap` whose values are (var, grad) pairs, - where gradients have already been scaled. - - grad_scale: the gradient scale. 0 if gradient updates should be skipped - for the step. (Optional, only returned in case global norm clipping is - used.) - """ - p = self.params - - # Computes gradients' norm and adds their summaries. Note that all_grad_norm - # may be nan, which may cause grad_scale to be nan. - for name, vg in var_grads.FlattenItems(): - summary_utils.AddNormSummary( - py_utils.SanitizeScopeKey(name) + '/' + p.name, vg) - flatten = py_utils.Flatten(var_grads) - all_grad_norm = tf.sqrt(py_utils.SumSquared([g for (_, g) in flatten])) - all_var_norm = tf.sqrt(py_utils.SumSquared([v for (v, _) in flatten])) - grad_norm_is_nan_or_inf = tf.math.logical_or( - tf.math.is_nan(all_grad_norm), tf.math.is_inf(all_grad_norm)) - - # Optional gradient adjustment. Note that this happens after computing - # all_grad_norm. - if gradient_adjuster is not None: - tf.logging.info('gradient_adjuster=%s', gradient_adjuster) - var_grads = gradient_adjuster(var_grads) - - # Handles NaN/Inf gradients. - has_nan_or_inf = py_utils.HasNanOrInfGradient(var_grads) - # Grad norm can still be inf even if none of the individual grad is inf. - has_nan_or_inf = tf.math.logical_or(has_nan_or_inf, grad_norm_is_nan_or_inf) - self._AddEvalMetric('has_nan_or_inf', has_nan_or_inf, tf.constant(1.0)) - - return_values = py_utils.NestedMap() - if p.clip_gradient_single_norm_to_value: - # Currently using both types of clipping simultaneously is unsupported. - if p.clip_gradient_norm_to_value: - raise ValueError('Cannot use clip_gradient_single_norm_to_value=%f and ' - 'clip_gradient_norm_to_value=%f.' % - (p.clip_gradient_single_norm_to_value, - p.clip_gradient_norm_to_value)) - final_var_grads = py_utils.ApplyGradNormClipping( - var_grads, p.clip_gradient_single_norm_to_value) - - else: - grad_scale = self._GetGlobalGradScale(all_grad_norm, has_nan_or_inf) - self._AddEvalMetric('grad_norm/all', all_grad_norm, tf.constant(1.0)) - self._AddEvalMetric('var_norm/all', all_var_norm, tf.constant(1.0)) - self._AddEvalMetric('grad_scale_all', grad_scale, tf.constant(1.0)) - final_var_grads = py_utils.ApplyGradMultiplier(var_grads, grad_scale) - return_values.grad_scale = grad_scale - - return_values.final_var_grads = final_var_grads - return return_values - - def _AddEvalMetric(self, key, value, weight): - self._eval_metrics[key] = (value, weight) - -_LEGACY_LEARNER_PARAMS = [ - 'bprop_variable_filter', - 'bprop_variable_exclusion', - 'clip_gradient_norm_to_value', - 'clip_gradient_single_norm_to_value', - 'colocate_gradients_with_ops', - 'gate_gradients', - 'scale_gradients', - 'grad_aggregation_method', - 'grad_norm_to_clip_to_zero', - 'grad_norm_tracker', - 'l1_regularizer_weight', - 'l2_regularizer_weight', - 'learning_rate', - 'lr_schedule', - 'optimizer', -] - - -def ExtractLearnerFromLegacyParams(tp, cls=Learner): - """Extracts legacy learner params from 'tp' to a Learner params. - - Args: - tp: BaseTask training params (p.train). Its legacy params will be cleared to - be None after the conversion. - cls: Learner class where we set the params. - - Returns: - A params for Learner. - """ - lp = cls.Params() - lp.name = 'loss' - for k, v in tp.IterParams(): - if k not in _LEGACY_LEARNER_PARAMS: - tf.logging.info( - 'Ignoring legacy param %s=%s for optimization program', k, v) - continue - setattr(lp, k, v) - setattr(tp, k, None) - for line in lp.ToText().split('\n'): - tf.logging.info('Learner params: %s', line) - return lp diff --git a/lingvo/core/learner_test.py b/lingvo/core/learner_test.py deleted file mode 100644 index 70d2629f..00000000 --- a/lingvo/core/learner_test.py +++ /dev/null @@ -1,154 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.core.learner.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import gradient_combiner -from lingvo.core import learner -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.core import test_utils - - -class TestLayer(base_layer.BaseLayer): - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - pc = py_utils.WeightParams( - shape=[], - init=py_utils.WeightInit.Constant(0), - dtype=self.params.dtype, - collections=self._VariableCollections()) - self.CreateVariable('hello', pc) - self.CreateVariable('world', pc) - self.CreateVariable('moon', pc) - self.CreateVariable('mars', pc, trainable=False) - - def Loss(self, theta): - return self.MainLoss(theta) + self.AuxLoss(theta) - - def MainLoss(self, theta): - return theta.hello - - def AuxLoss(self, theta): - return -2 * theta.world - - -class GradientSum(gradient_combiner.GradientCombiner): - - def Combine(self, vmap, losses_and_grads): - """Computes the sum of gradients on the variables.""" - - def GradSum(v, *gs): - tf.logging.info('GradSum: %s: %s', v, gs) - if all(g is None for g in gs): - return None - return tf.add_n([g for g in gs if g is not None]) - - grads = [l_and_g.grads for l_and_g in losses_and_grads.values()] - tf.logging.info('grads: %s', grads) - return tf.nest.map_structure(GradSum, vmap, *grads) - - -class LearnerTest(test_utils.TestCase): - - def testBasic(self): - learner_p = learner.Learner.Params().Set( - name='learner', learning_rate=.1, optimizer=optimizer.SGD.Params()) - var_grads, updated_vars, _ = self._testLearner(learner_p) - tf.logging.info('var_grads=%s, updated_vars=%s', var_grads, updated_vars) - self.assertAllClose(var_grads, {'hello': (0., 1.), 'world': (0., -2.)}) - self.assertAllClose(updated_vars, { - 'hello': -0.1, - 'world': 0.2, - 'moon': 0., - 'mars': 0. - }) - - def testMultiLoss(self): - learner_p = learner.Learner.Params().Set( - name='learner', learning_rate=.1, optimizer=optimizer.SGD.Params()) - learner_p.loss_name = ('main_loss', 'aux_loss') - learner_p.gradient_combiner = GradientSum.Params() - var_grads, updated_vars, _ = self._testLearner(learner_p) - self.assertAllClose(var_grads, {'hello': (0., 1.), 'world': (0., -2.)}) - self.assertAllClose(updated_vars, { - 'hello': -0.1, - 'world': 0.2, - 'moon': 0., - 'mars': 0. - }) - - def testBPropVariableFilter(self): - learner_p = learner.Learner.Params().Set( - name='learner', - learning_rate=.1, - optimizer=optimizer.SGD.Params(), - bprop_variable_filter='ello') - var_grads, updated_vars, eval_metrics = self._testLearner(learner_p) - # Only 'hello' is updated. - self.assertAllClose(var_grads, {'hello': (0., 1.)}) - self.assertAllClose(updated_vars, { - 'hello': -0.1, - 'world': 0., - 'moon': 0., - 'mars': 0. - }) - self.assertIn('grad_scale_all', eval_metrics) - - def testBPropVariableExclusion(self): - learner_p = learner.Learner.Params().Set( - name='learner', - learning_rate=.1, - optimizer=optimizer.SGD.Params(), - bprop_variable_filter='o', - bprop_variable_exclusion='ello') - var_grads, updated_vars, _ = self._testLearner(learner_p) - # Only 'world' is updated. - self.assertAllClose(var_grads, {'world': (0., -2.)}) - self.assertAllClose(updated_vars, { - 'hello': 0., - 'world': 0.2, - 'moon': 0., - 'mars': 0. - }) - - def _testLearner(self, learner_p): - tf.train.get_or_create_global_step() # needed for lr_schedule - lrnr = learner_p.Instantiate() - layer = TestLayer.Params().Set(name='test').Instantiate() - if isinstance(learner_p.loss_name, (list, tuple)): - main_loss = layer.MainLoss(layer.theta) - aux_loss = layer.AuxLoss(layer.theta) - metrics = {'main_loss': (main_loss, 1.), 'aux_loss': (aux_loss, 1.)} - expected_losses = [main_loss, aux_loss] - else: - loss = layer.Loss(layer.theta) - metrics = {learner_p.name: (loss, 1.)} - expected_losses = [loss] - losses, update_op, eval_metrics = lrnr.Apply(metrics, layer.vars) - self.assertAllEqual(losses, expected_losses) - with self.session(): - self.evaluate(tf.global_variables_initializer()) - var_grads = self.evaluate(lrnr.GetVarGrads().Transform(tuple)) - update_op.run() - updated_vars = self.evaluate(layer.vars) - return var_grads, updated_vars, eval_metrics - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/lstm_frnn_layer.py b/lingvo/core/lstm_frnn_layer.py deleted file mode 100644 index 28a7cfd9..00000000 --- a/lingvo/core/lstm_frnn_layer.py +++ /dev/null @@ -1,253 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -"""Functional LSTM RNN layer with LSTM cells fused into FRNN to run input ... - -projections in parallel. -""" - -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core import recurrent -from lingvo.core import rnn_cell -from lingvo.core import rnn_layers - - -class LSTMCellExt: - """Extends LSTM-based cell classes with extra methods for parallelizing ... - - input projections across steps. - """ - - def ProjectInputSequence(self, theta, inputs): - """Applies input projection for the entire sequence. - - Args: - theta: a NestedMap of layer weights. Notably, it's expected to contain - separate weight tensors for input and hidden state projections, for - performance reasons, under the key 'wm_i' (input) and 'wm_h' - inputs: A NestedMap with the following fields: - - act: A list of Tensors of shape [seqlen, batch, input_dim]. - - Returns: - A Tensor of shape [seqlen, batch, 4 * hidden_dim]. - """ - assert isinstance(inputs.act, list) - if len(inputs.act) > 1: - x = tf.concat(inputs.act, -1) - else: - x = inputs.act[0] - # [T, B, 4 * H] - proj_inputs = tf.einsum('TBD,DH->TBH', x, theta.wm_i) - return proj_inputs - - def _MixWithProjectedInput(self, theta, state0, inputs): - """Computes _Mix() with inputs already projected. - - Args: - theta: a NestedMap of layer weights. Notably, it's expected to contain - separate weight tensors for input and hidden state projections, for - performance reasons, under the key 'wm_i' (input) and 'wm_h' - state0: A NestedMap with the same structure as return value of - `self.zero_state()`. - inputs: A Tensor of shape [batch, 4 * hidden_dim]. - - Returns: - A Tensor of the same shape as `inputs`. - """ - proj_m = tf.matmul(state0.m, theta.wm_h) - return inputs + proj_m - - def FPropWithProjectedInput(self, theta, state0, inputs): - """FProp with inputs already projected. - - This method is for parallelizing the input projection across time steps to - accelerate training. - - The following are equivalent: - - >>> inputs = - >>> paddings = tf.zeros([T, B]) - >>> theta = cell.theta - >>> state = cell.zero_state(theta, B) - - # a. Use FProp(). - >>> for i in range(T): - ... state, _ = cell.FProp(theta, inputs[i, :, :], paddings, state) - - # b. Use FPropWithProjectedInput(). - >>> proj_inputs = cell.ProjectInputSequence(theta, inputs) - >>> for i in range(T): - ... state, _ = cell.FPropWithProjectedInputs( - ... theta, proj_inputs[i, :, :], paddings, state) - - Args: - theta: a NestedMap of layer weights. Notably, it's expected to contain - separate weight tensors for input and hidden state projections, for - performance reasons, under the key 'wm_i' (input) and 'wm_h' (hidden - state). - state0: A NestedMap with the same structure as return value of - `self.zero_state()`. - inputs: A NestedMap with the following fields: - - proj_inputs: A single Tensors of shape [batch, 4 * hidden_dim]. - - padding: A Tensor of shape [batch, 1]. - - reset_mask: A Tensor of shape [batch, 1]. - - Returns: - state1: A NestedMap of the same structure as `state0`. - extras: Intermediate results to facilitate backprop. A NestedMap. - """ - if self.params.reset_cell_state: - state0_modified = self._ResetState(state0.DeepCopy(), inputs) - else: - state0_modified = state0 - xmw = self._MixWithProjectedInput(theta, state0_modified, - inputs.proj_inputs) - gates_input = inputs.copy() - gates_input.act = [inputs.proj_inputs] - state1 = self._Gates(xmw, theta, state0_modified, gates_input) - return state1, py_utils.NestedMap() - - -class LSTMCellSimpleExt(rnn_cell.LSTMCellSimple, LSTMCellExt): - """Extends LSTMCellSimple with extra methods for parallelizing ... - - input projections across steps. - """ - pass - - -class LayerNormalizedLSTMCellSimpleExt(rnn_cell.LayerNormalizedLSTMCellSimple, - LSTMCellExt): - """Extends LayerNormalizedLSTMCellSimple with extra methods for ... - - parallelizing input projections across steps. - """ - pass - - -class LayerNormalizedLSTMCellLeanExt(rnn_cell.LayerNormalizedLSTMCellLean, - LSTMCellExt): - """Extends LayerNormalizedLSTMCellLean with extra methods for parallelizing ... - - input projections across steps. - """ - pass - - -class LstmFRNN(base_layer.BaseLayer): - """A FRNN for LSTMCellSimple or LayerNormalizedLSTMCellLean cell. - - It exploits the parallelism in input projection across time steps, and is in - general faster than the combination of LayerNormalizedLSTMCellLean and FRNN. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('packed_input', False, 'To reset states for packed inputs.') - p.Define( - 'cell', None, - 'Configs for the RNN cell. Supported classes are LSTMCellSimpleExt, ' - 'LayerNormalizedLSTMCellLeanExt.') - p.Define('reverse', False, - 'Whether or not to unroll the sequence in reversed order.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.cell.cls not in (LSTMCellSimpleExt, LayerNormalizedLSTMCellSimpleExt, - LayerNormalizedLSTMCellLeanExt): - raise ValueError( - 'Only LSTMCellSimpleExt, LayerNormalizedLSTMCellSimpleExt and ' - 'LayerNormalizedLSTMCellLeanExt are supported, got {}.'.format( - p.cell.cls.__name__)) - self.CreateChild('cell', p.cell) - - def zero_state(self, theta, batch_size): - return self.cell.zero_state(theta.cell, batch_size) - - def FProp(self, theta, inputs, paddings, state0=None, segment_id=None): - """Computes LSTM forward pass. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: A single tensor or a tuple of tensors with cardinality equal to - rnn_cell.inputs_arity. For every input tensor, the first dimension is - assumed to be time, second dimension batch, and third dimension depth. - paddings: A tensor. First dim is time, second dim is batch, and third dim - is expected to be 1. - state0: If not None, the initial rnn state in a `.NestedMap`. Defaults to - the cell's zero-state. - segment_id: A tensor to support packed inputs. First dim is time, second - dim is batch, and third dim is expected to be 1. - - Returns: - A tensor of [time, batch, dims]. - The final recurrent state. - """ - p = self.params - rcell = self.cell - assert isinstance(rcell, (rnn_cell.RNNCell)) - - if not isinstance(inputs, (list, tuple)): - inputs = [inputs] - - # Slicing wm to wm_{i,h} outside the loop to get 20% speedup over regular - # LSTM baseline. - # Keeping slicing within the loop gives only < 3% speedup. - cell_theta = theta.cell.copy() - num_input_nodes = p.cell.num_input_nodes - cell_theta['wm_i'] = cell_theta.wm[:num_input_nodes, :] - cell_theta['wm_h'] = cell_theta.wm[num_input_nodes:, :] - tf.logging.vlog(1, 'cell_theta: %r', cell_theta) - if p.packed_input: - assert segment_id is not None - reset_mask = rnn_layers.GeneratePackedInputResetMask( - segment_id, is_reverse=False) - reset_mask = py_utils.HasShape(reset_mask, tf.shape(paddings)) - else: - reset_mask = tf.zeros_like(paddings) - - if p.reverse: - inputs = [tf.reverse(x, [0]) for x in inputs] - paddings = tf.reverse(paddings, [0]) - reset_mask = tf.reverse(reset_mask, [0]) - - if not state0: - batch_size = py_utils.GetShape(paddings)[1] - state0 = rcell.zero_state(cell_theta, batch_size) - - # [T, B, H] - proj_inputs = rcell.ProjectInputSequence(cell_theta, - py_utils.NestedMap(act=inputs)) - proj_inputs = py_utils.NestedMap( - proj_inputs=proj_inputs, padding=paddings, reset_mask=reset_mask) - - acc_state, final_state = recurrent.Recurrent( - theta=cell_theta, - state0=state0, - inputs=proj_inputs, - cell_fn=rcell.FPropWithProjectedInput, - cell_type=rcell.layer_type, - accumulator_layer=self, - allow_implicit_capture=p.allow_implicit_capture) - - act = rcell.GetOutput(acc_state) - if p.reverse: - act = tf.reverse(act, [0]) - return act, final_state diff --git a/lingvo/core/lstm_frnn_layer_test.py b/lingvo/core/lstm_frnn_layer_test.py deleted file mode 100644 index b7ab9fc6..00000000 --- a/lingvo/core/lstm_frnn_layer_test.py +++ /dev/null @@ -1,231 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -"""Tests for lingvo.core.lstm_frnn_layer.""" - -from absl.testing import parameterized - -from lingvo import compat as tf -from lingvo.core import lstm_frnn_layer -from lingvo.core import py_utils -from lingvo.core import rnn_cell -from lingvo.core import rnn_layers -from lingvo.core import test_utils - -import numpy as np - -_INIT_RANDOM_SEED = 2020 -_RANDOM_SEED = 2019 - - -class LSTMCellExtTest(test_utils.TestCase, parameterized.TestCase): - - def _GetParams(self, num_hidden_nodes=None): - params = lstm_frnn_layer.LayerNormalizedLSTMCellLeanExt.Params() - params.name = 'lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED) - params.vn.global_vn = False - params.vn.per_step_vn = False - params.num_input_nodes = 2 - params.num_output_nodes = 2 - if num_hidden_nodes: - params.num_hidden_nodes = num_hidden_nodes - params.zo_prob = 0.0 - params.random_seed = _RANDOM_SEED - return params - - def testLayerNormalizedLSTMCellLeanExt(self): - cell_p = self._GetParams() - - seqlen, batch, input_dim = 4, 2, 2 - inputs = tf.convert_to_tensor( - np.random.rand(seqlen, batch, input_dim).astype(np.float32)) - input_lens = np.random.randint(1, seqlen + 1, size=batch) - paddings = 1. - tf.sequence_mask( - input_lens, maxlen=seqlen, dtype=tf.float32) - paddings = tf.transpose(paddings) - reset_mask = tf.zeros((seqlen, batch), tf.float32) - m0 = tf.convert_to_tensor( - np.random.rand(batch, input_dim).astype(np.float32)) - c0 = tf.convert_to_tensor( - np.random.rand(batch, input_dim).astype(np.float32)) - state0 = py_utils.NestedMap(m=m0, c=c0) - - with self.session(): - cell = cell_p.Instantiate() - self.evaluate(tf.global_variables_initializer()) - - # The canonical path - state = state0 - for i in range(seqlen): - state, _ = cell.FPropDefaultTheta( - state, - py_utils.NestedMap( - act=[inputs[i, :, :]], - padding=paddings[i, :, tf.newaxis], - reset_mask=reset_mask[i, :, tf.newaxis])) - expected_state = self.evaluate(state) - - # Taking input projection outside of the loop. - cell_theta = cell.theta.copy() - cell_theta.wm_i = cell_theta.wm[:cell.params.num_input_nodes, :] - cell_theta.wm_h = cell_theta.wm[cell.params.num_input_nodes:, :] - proj_inputs = cell.ProjectInputSequence(cell_theta, - py_utils.NestedMap(act=[inputs])) - state = state0 - for i in range(seqlen): - state, _ = cell.FPropWithProjectedInput( - cell_theta, state, - py_utils.NestedMap( - proj_inputs=proj_inputs[i, :, :], - padding=paddings[i, :, tf.newaxis], - reset_mask=reset_mask[i, :, tf.newaxis])) - actual_state = self.evaluate(state) - - tf.logging.info('expected_state:{}'.format(expected_state)) - tf.logging.info('actual_state:{}'.format(actual_state)) - self.assertAllClose(expected_state.m, actual_state.m) - self.assertAllClose(expected_state.c, actual_state.c) - - -class LstmFRNNTest(test_utils.TestCase, parameterized.TestCase): - - def _SetCellParams(self, cell_p, num_hidden_nodes=None): - params = cell_p - params.name = cell_p.cls.__name__ - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED) - params.vn.global_vn = False - params.vn.per_step_vn = False - params.num_input_nodes = 7 - params.num_output_nodes = 9 - if num_hidden_nodes: - params.num_hidden_nodes = num_hidden_nodes - params.zo_prob = 0.0 - params.random_seed = _RANDOM_SEED - - def _GetTestInputs(self, packed_input): - seqlen, batch, input_dim, output_dim = 4, 5, 7, 9 - inputs = tf.convert_to_tensor( - np.random.rand(seqlen, batch, input_dim).astype(np.float32)) - input_lens = np.random.randint(1, seqlen + 1, size=batch) - padding = 1. - tf.sequence_mask(input_lens, maxlen=seqlen, dtype=tf.float32) - padding = tf.transpose(padding)[:, :, tf.newaxis] - segment_id = None - if packed_input: - segment_id = tf.convert_to_tensor( - np.random.randint(0, seqlen, (seqlen, batch, 1), np.int32)) - - m = tf.convert_to_tensor( - np.random.rand(batch, output_dim).astype(np.float32)) - c = tf.convert_to_tensor( - np.random.rand(batch, output_dim).astype(np.float32)) - return inputs, padding, m, c, segment_id - - def _testHelper(self, base_frnn_p, frnn_p, packed_input=False): - inputs, padding, m0, c0, segment_id = self._GetTestInputs(packed_input) - base_frnn = base_frnn_p.Instantiate() - frnn = frnn_p.Instantiate() - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - - state0 = py_utils.NestedMap(m=m0, c=c0) - act, state = base_frnn.FPropDefaultTheta( - inputs, padding, state0=state0, segment_id=segment_id) - # Compute grads - loss = -tf.math.log( - tf.sigmoid((tf.reduce_sum(tf.math.square(act)) + - tf.reduce_sum(state.m * state.c * state.c)))) - grads = tf.gradients(loss, base_frnn.vars.Flatten()) - - expected_act, expected_state, expected_grads = self.evaluate( - [act, state, grads]) - - act, state = frnn.FPropDefaultTheta( - inputs, padding, state0=state0, segment_id=segment_id) - # Compute grads - loss = -tf.math.log( - tf.sigmoid((tf.reduce_sum(tf.math.square(act)) + - tf.reduce_sum(state.m * state.c * state.c)))) - grads = tf.gradients(loss, frnn.vars.Flatten()) - - actual_act, actual_state, actual_grads = self.evaluate( - [act, state, grads]) - - tf.logging.info('expected_act:{}'.format(expected_act)) - tf.logging.info('actual_act:{}'.format(actual_act)) - - tf.logging.info('expected_state:{}'.format(expected_state)) - tf.logging.info('actual_state:{}'.format(actual_state)) - - tf.logging.info('expected_grads:{}'.format(expected_grads)) - tf.logging.info('actual_grads:{}'.format(actual_grads)) - - self.assertAllClose(expected_act, actual_act) - self.assertAllClose(expected_state.m, actual_state.m) - self.assertAllClose(expected_state.c, actual_state.c) - for (vname, _), expected, actual in zip(frnn.vars.FlattenItems(), - expected_grads, actual_grads): - self.assertAllClose(expected, actual, msg=vname) - - @parameterized.named_parameters( - ('HasBias', True), - ('NoBias', False), - ('HasBiasPackedInputs', True, True), - ('NoBiasPackedInputs', False, True), - ) - def testLSTMCellSimple(self, enable_lstm_bias, packed_inputs=False): - base_cell_p = rnn_cell.LSTMCellSimple.Params() - base_cell_p.enable_lstm_bias = enable_lstm_bias - self._SetCellParams(base_cell_p) - base_frnn_p = rnn_layers.FRNN.Params().Set( - name='base_frnn', cell=base_cell_p) - - cell_p = lstm_frnn_layer.LSTMCellSimpleExt.Params() - cell_p.enable_lstm_bias = enable_lstm_bias - self._SetCellParams(cell_p) - frnn_p = lstm_frnn_layer.LstmFRNN.Params().Set(name='frnn', cell=cell_p) - self._testHelper(base_frnn_p, frnn_p, packed_inputs) - - def testLayerNormalizedLSTMCellSimple(self): - base_cell_p = rnn_cell.LayerNormalizedLSTMCellSimple.Params() - base_cell_p.enable_lstm_bias = True - self._SetCellParams(base_cell_p) - base_frnn_p = rnn_layers.FRNN.Params().Set( - name='base_frnn', cell=base_cell_p) - - cell_p = lstm_frnn_layer.LayerNormalizedLSTMCellSimpleExt.Params() - cell_p.enable_lstm_bias = True - self._SetCellParams(cell_p) - frnn_p = lstm_frnn_layer.LstmFRNN.Params().Set(name='frnn', cell=cell_p) - self._testHelper(base_frnn_p, frnn_p) - - def testLayerNormalizedLSTMCellLean(self): - base_cell_p = rnn_cell.LayerNormalizedLSTMCellLean.Params() - base_cell_p.enable_lstm_bias = True - self._SetCellParams(base_cell_p) - base_frnn_p = rnn_layers.FRNN.Params().Set( - name='base_frnn', cell=base_cell_p) - - cell_p = lstm_frnn_layer.LayerNormalizedLSTMCellLeanExt.Params() - cell_p.enable_lstm_bias = True - self._SetCellParams(cell_p) - frnn_p = lstm_frnn_layer.LstmFRNN.Params().Set(name='frnn', cell=cell_p) - self._testHelper(base_frnn_p, frnn_p) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/matrix_functions.py b/lingvo/core/matrix_functions.py deleted file mode 100644 index f2f9f5c8..00000000 --- a/lingvo/core/matrix_functions.py +++ /dev/null @@ -1,175 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Matrix functions contains iterative methods for M^p.""" - -import functools -import lingvo.compat as tf - - -def matrix_square_root(mat_a, mat_a_size, iter_count=100, ridge_epsilon=1e-4): - """Iterative method to get matrix square root. - - Stable iterations for the matrix square root, Nicholas J. Higham - - Page 231, Eq 2.6b - http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.6.8799&rep=rep1&type=pdf - - Args: - mat_a: the symmetric PSD matrix whose matrix square root be computed - mat_a_size: size of mat_a. - iter_count: Maximum number of iterations. - ridge_epsilon: Ridge epsilon added to make the matrix positive definite. - - Returns: - mat_a^0.5 - """ - - def _iter_condition(i, unused_mat_y, unused_old_mat_y, unused_mat_z, - unused_old_mat_z, err, old_err): - """This method require that we check for divergence every step.""" - return tf.math.logical_and(i < iter_count, err < old_err) - - def _iter_body(i, mat_y, unused_old_mat_y, mat_z, unused_old_mat_z, err, - unused_old_err): - """Iterative method to compute the square root of matrix.""" - current_iterate = 0.5 * (3.0 * identity - tf.matmul(mat_z, mat_y)) - current_mat_y = tf.matmul(mat_y, current_iterate) - current_mat_z = tf.matmul(current_iterate, mat_z) - # Compute the error in approximation. - mat_sqrt_a = current_mat_y * tf.sqrt(norm) - mat_a_approx = tf.matmul(mat_sqrt_a, mat_sqrt_a) - residual = mat_a - mat_a_approx - current_err = tf.sqrt(tf.reduce_sum(residual * residual)) / norm - return i + 1, current_mat_y, mat_y, current_mat_z, mat_z, current_err, err - - identity = tf.eye(tf.cast(mat_a_size, tf.int32)) - mat_a = mat_a + ridge_epsilon * identity - norm = tf.sqrt(tf.reduce_sum(mat_a * mat_a)) - mat_init_y = mat_a / norm - mat_init_z = identity - init_err = norm - - _, _, prev_mat_y, _, _, _, _ = tf.while_loop(_iter_condition, _iter_body, [ - 0, mat_init_y, mat_init_y, mat_init_z, mat_init_z, init_err, - init_err + 1.0 - ]) - return prev_mat_y * tf.sqrt(norm) - - -def inlined_matrix_inverse_pth_root(mat_g, - mat_g_size, - alpha, - iter_count=100, - error_tolerance=1e-6, - ridge_epsilon=1e-6): - """Computes mat_g^alpha, where alpha = -1/p, p is one of 2, 4, or 8. - - We use an iterative Schur-Newton method from equation 3.2 on page 9 of: - - A Schur-Newton Method for the Matrix p-th Root and its Inverse - by Chun-Hua Guo and Nicholas J. Higham - SIAM Journal on Matrix Analysis and Applications, - 2006, Vol. 28, No. 3 : pp. 788-804 - https://pdfs.semanticscholar.org/0abe/7f77433cf5908bfe2b79aa91af881da83858.pdf - - Args: - mat_g: the symmetric PSD matrix whose power it to be computed - mat_g_size: size of mat_g. - alpha: exponent, must be -1/p for p a positive integer. - iter_count: Maximum number of iterations. - error_tolerance: Error indicator, useful for early termination. - ridge_epsilon: Ridge epsilon added to make the matrix positive definite. - - Returns: - mat_g^alpha - """ - alpha = tf.cast(alpha, tf.float64) - neg_alpha = -1.0 * alpha - exponent = 1.0 / neg_alpha - identity = tf.eye(tf.cast(mat_g_size, tf.int32), dtype=tf.float64) - - def _unrolled_mat_pow_2(mat_m): - """Computes mat_m^2.""" - return tf.matmul(mat_m, mat_m) - - def _unrolled_mat_pow_4(mat_m): - """Computes mat_m^4.""" - mat_pow_2 = _unrolled_mat_pow_2(mat_m) - return tf.matmul(mat_pow_2, mat_pow_2) - - def _unrolled_mat_pow_8(mat_m): - """Computes mat_m^4.""" - mat_pow_4 = _unrolled_mat_pow_4(mat_m) - return tf.matmul(mat_pow_4, mat_pow_4) - - def mat_power(mat_m, p): - """Computes mat_m^p, for p == 2 or 4 or 8. - - Args: - mat_m: a square matrix - p: a positive integer - - Returns: - mat_m^p - """ - log2_p = tf.math.log(p) / tf.math.log(tf.constant(2.0, dtype=p.dtype)) - return tf.switch_case( - tf.cast(tf.math.round(log2_p), tf.int32), { - 0: functools.partial(_unrolled_mat_pow_2, mat_m), - 1: functools.partial(_unrolled_mat_pow_4, mat_m), - 2: functools.partial(_unrolled_mat_pow_8, mat_m), - }) - - def _iter_condition(i, unused_mat_m, unused_mat_h, unused_old_mat_h, error, - run_step): - return tf.math.logical_and( - i < iter_count, tf.math.logical_or(error > error_tolerance, run_step)) - - def _iter_body(i, mat_m, mat_h, unused_old_mat_h, error, unused_run_step): - mat_m_i = (1 - alpha) * identity + alpha * mat_m - new_mat_m = tf.matmul(mat_power(mat_m_i, exponent), mat_m) - new_mat_h = tf.matmul(mat_h, mat_m_i) - new_error = tf.reduce_max(tf.abs(new_mat_m - identity)) - return (i + 1, new_mat_m, new_mat_h, mat_h, new_error, - new_error < error * 1.2) - - if mat_g_size == 1: - mat_h = tf.pow(mat_g + ridge_epsilon, alpha) - else: - damped_mat_g = mat_g + ridge_epsilon * identity - z = (1 - 1 / alpha) / (2 * tf.norm(damped_mat_g)) - # The best value for z is - # (1 - 1/alpha) * (c_max^{-alpha} - c_min^{-alpha}) / - # (c_max^{1-alpha} - c_min^{1-alpha}) - # where c_max and c_min are the largest and smallest singular values of - # damped_mat_g. - # The above estimate assumes that c_max > c_min * 2^p. (p = -1/alpha) - # Can replace above line by the one below, but it is less accurate, - # hence needs more iterations to converge. - # z = (1 - 1/alpha) / tf.trace(damped_mat_g) - # If we want the method to always converge, use z = 1 / norm(damped_mat_g) - # or z = 1 / tf.trace(damped_mat_g), but these can result in many - # extra iterations. - new_mat_m_0 = damped_mat_g * z - new_error = tf.reduce_max(tf.abs(new_mat_m_0 - identity)) - new_mat_h_0 = identity * tf.pow(z, neg_alpha) - _, mat_m, mat_h, old_mat_h, error, convergence = tf.while_loop( - _iter_condition, _iter_body, - [0, new_mat_m_0, new_mat_h_0, new_mat_h_0, new_error, True]) - error = tf.reduce_max(tf.abs(mat_m - identity)) - is_converged = tf.cast(convergence, old_mat_h.dtype) - resultant_mat_h = is_converged * mat_h + (1 - is_converged) * old_mat_h - return resultant_mat_h, error diff --git a/lingvo/core/matrix_functions_test.py b/lingvo/core/matrix_functions_test.py deleted file mode 100644 index 7d078ab3..00000000 --- a/lingvo/core/matrix_functions_test.py +++ /dev/null @@ -1,61 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Functional tests for Matrix functions.""" - -import lingvo.compat as tf -from lingvo.core import matrix_functions - -import numpy as np - -TOLERANCE = 1e-3 - - -def np_power(mat_g, alpha): - """Computes mat_g^alpha for a square symmetric matrix mat_g.""" - - mat_u, diag_d, mat_v = np.linalg.svd(mat_g) - diag_d = np.power(diag_d, alpha) - return np.dot(np.dot(mat_u, np.diag(diag_d)), mat_v) - - -class MatrixFunctionTests(tf.test.TestCase): - - def testMatrixSquareRootFunction(self): - """Tests for matrix square roots.""" - - size = 20 - mat_a = np.random.rand(size, size) - mat = np.dot(mat_a, mat_a.T) - expected_mat_root = np_power(mat, 0.5) - mat_root = matrix_functions.matrix_square_root(mat, size) - self.assertAllCloseAccordingToType( - expected_mat_root, mat_root, atol=TOLERANCE, rtol=TOLERANCE) - - def testMatrixInversePthRootFunction(self): - """Tests for matrix inverse pth roots.""" - - size = 4 - mat_a = np.random.rand(size, size) - mat = np.dot(mat_a, mat_a.T) - expected_mat_root = np_power(mat, -0.125) - mat_root, _ = matrix_functions.inlined_matrix_inverse_pth_root( - mat, size, -0.125, ridge_epsilon=0.0) - self.assertAllCloseAccordingToType( - expected_mat_root, mat_root, atol=TOLERANCE, rtol=TOLERANCE) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/metrics.py b/lingvo/core/metrics.py deleted file mode 100644 index 32e46d7d..00000000 --- a/lingvo/core/metrics.py +++ /dev/null @@ -1,443 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Helper classes for computing performance metrics.""" - -import lingvo.compat as tf -from lingvo.core import hyperparams -from lingvo.core import plot -from lingvo.core import py_utils -from lingvo.core import scorers -import numpy as np -try: - import sklearn.metrics # pylint: disable=g-import-not-at-top - HAS_SKLEARN = True -except ImportError: - HAS_SKLEARN = False -try: - import scipy.stats # pylint: disable=g-import-not-at-top - HAS_SCIPY_STATS = True -except ImportError: - HAS_SCIPY_STATS = False - - -def CreateScalarSummary(name, simple_value): - return tf.Summary( - value=[tf.Summary.Value(tag=name, simple_value=simple_value)]) - - -class BaseMetric: - """Base class for aggregating statistics to compute a performance metric.""" - - def Update(self, *args, **kwargs): - """Updates this metric (e.g. accumulates statistics) from the arguments.""" - pass - - @property - def value(self): - """Current value of this metric.""" - return None - - def Summary(self, name): - """Converts the current state of this metric to a `tf.Summary`. - - Args: - name: A string to use as the summary value tag. - - Returns: - A `tf.Summary` proto. - """ - return CreateScalarSummary(name, self.value) - - -class ConfigurableMetric(BaseMetric): - """A Metric class with configurable params.""" - - @classmethod - def Params(cls): - p = hyperparams.InstantiableParams(cls) - return p - - def __init__(self, params): - self.params = params - - -class AverageMetric(BaseMetric): - """Class to compute a weighted (arithmetic) average value metric.""" - - def __init__(self): - self._total_value = 0.0 - self._total_weight = 0.0 - - def Update(self, value, weight=1.0): - if weight < 0.0: - raise ValueError('weight must be non-negative. Got: %f' % weight) - self._total_value += value * weight - self._total_weight += weight - - # We may want both a getter and a setter method for total_value and - # total_weight, respectively. - def GetTotalValue(self): - return self._total_value - - def SetTotalValue(self, val): - self._total_value = val - - total_value = property(GetTotalValue, SetTotalValue) - - def GetTotalWeight(self): - return self._total_weight - - def SetTotalWeight(self, val): - self._total_weight = val - - total_weight = property(GetTotalWeight, SetTotalWeight) - - @property - def value(self): - return (self._total_value / - self._total_weight if self._total_weight > 0 else 0) - - -class F1Metric(BaseMetric): - """Class to compute F1 metrics.""" - - def __init__(self): - self._true_pos = 0.0 - self._false_pos = 0.0 - self._false_neg = 0.0 - - def UpdateTruePositive(self, count=1.0): - self._true_pos += count - - def UpdateFalsePositive(self, count=1.0): - self._false_pos += count - - def UpdateFalseNegative(self, count=1.0): - self._false_neg += count - - @property - def value(self): - if (self._true_pos + self._false_pos) > 0: - precision = self._true_pos / (self._true_pos + self._false_pos) - else: - precision = 0.0 - if (self._true_pos + self._false_neg) > 0: - recall = self._true_pos / (self._true_pos + self._false_neg) - else: - recall = 0.0 - if (precision + recall) > 0: - return 2.0 * precision * recall / (precision + recall) - else: - return 0.0 - - -class CorpusBleuMetric(BaseMetric): - """Metric class to compute the corpus-level BLEU score.""" - - def __init__(self, **kwargs): - self._scorer = scorers.BleuScorer(**kwargs) - - def Update(self, ref_str, hyp_str): - self._scorer.AddSentence(ref_str, hyp_str) - - @property - def unsegmenter(self): - return self._scorer.unsegmenter - - @property - def value(self): - return self._scorer.ComputeOverallScore() - - -class TpuEvalMetrics: - """Manages computation of metrics during TPU execution. - - TPU execution runs a training loop on device. To get eval metrics out of this, - metric values and weights must be carried through the loop. This requires - passing initial values to the loop setup, updated the values during the loop, - and doing a final aggregation after the loop. This class wraps the metrics - dictionary so that the needed ops can be built at the right time as the - training loop is built. - - Note that because the model is not constructed until the loop body function is - called, the initial values must be known statically. This is done currently by - hardcoding a limit on the number of metrics and casting each metric and value - count to float32, regardless of the number of actual metrics the model - produced. - - Note that this implementation computes the metrics over all replicas, for the - last step of the loop only (could be changed to average over all loop steps - instead). - """ - - def __init__(self): - self._metrics = None - self._max_metrics = 256 - - # Loop-carried values alternate value and weight; all values are scalars. - self._initial_values = (2 * - self._max_metrics) * [tf.constant(0, tf.float32)] - - def SetMetrics(self, metric_dict, step_args): - """Sets the metrics to evaluate and the per-step output tensors. - - Args: - metric_dict: dict of (name -> (tensor of values, tensor of weights)) - step_args: the tensors being passed to the training loop body. These share - the same structure of alternating value and weight scalars as the - initial values and the output of this function. - - Returns: - The tensors to return from the training loop body. For entries that are - for metrics in self._metrics, returns the value computed within the loop - (the step_args value passed in); for all others, the value will never be - used at the end and so the step_args value is passed through (which has - the effect of passing the initial values through every iteration of the - loop). - """ - num_metrics = len(metric_dict) - assert num_metrics <= self._max_metrics, ('Increase _max_metrics to >= %d' % - num_metrics) - self._metrics = metric_dict - - # self._metrics contains a map of (metric_value, - # metric_weight). We convert it into [metric_value * - # metric_weight, metric_weight] to make it easier to aggregate - # metric values across steps and TPU replicas. - ret = [] - for _, (value, weight) in sorted(self._metrics.items()): - assert value.shape.is_fully_defined(), ('%s' % value) - assert weight.shape.is_fully_defined(), ('%s' % weight) - weight = tf.cast(weight, tf.float32) - value = tf.cast(value, tf.float32) * weight - ret += [value, weight] - # Each metric has two tensors: value and weight. - assert len(ret) == 2 * num_metrics - ret += list(step_args)[len(ret):] - return ret - - @property - def initial_values(self): - """Returns the initial loop values.""" - return self._initial_values - - @property - def metrics(self): - return self._metrics - - def _Zip(self, values): - assert isinstance(values, list) - return list(zip(values[::2], values[1::2])) - - def FinalizeMetrics(self, loop_result): - """Compute final average of the metrics, given loop_result tensors. - - To be called outside the training loop body , but still in the scope of - tpu.batch_parallel. - - Args: - loop_result: Result of the training loop. - - Returns: - The tensors of the final avg values and total weights. - """ - # Each metric has two tensors in the loop carrying result. - metrics = loop_result[:2 * len(self._metrics.items())] - # Aggregate across tpu replicas. - metrics = [tf.tpu.cross_replica_sum(x) for x in metrics] - ret = [] - for (value, weight) in self._Zip(metrics): - value, weight = py_utils.WeightedAvg( - tf.math.divide_no_nan(value, weight), weight) - ret += [value, weight] - return ret - - def PackMetricsValues(self, values): - """Packs numpy values into a dict of metrics.""" - for k, v in zip(sorted(self._metrics.keys()), self._Zip(values)): - self._metrics[k] = v - - -class AUCMetric(BaseMetric): - """Class to compute the AUC score for binary classification.""" - - def __init__(self, mode='roc', samples=-1): - """Constructor of the class. - - Args: - mode: Possible values: 'roc' or 'pr'. - samples: The number of sample points to compute the AUC. If -1, include - all points seen thus far. - - Raises: - ImportError: If user has not installed sklearn, raise an ImportError. - """ - if not HAS_SKLEARN: - raise ImportError('AUCMetric depends on sklearn.') - self._mode = mode - self._samples = samples - self._label = [] - self._prob = [] - self._weight = [] - if self._mode == 'roc': - self._curve_fn = sklearn.metrics.roc_curve - self._score_fn = sklearn.metrics.roc_auc_score - self._plot_labels = ['False Positive Rate', 'True Positive Rate'] - elif self._mode == 'pr': - self._curve_fn = sklearn.metrics.precision_recall_curve - self._score_fn = sklearn.metrics.average_precision_score - self._plot_labels = ['Recall', 'Precision'] - else: - raise ValueError('mode in AUCMetric must be one of "roc" or "pr".') - - def Update(self, label, prob, weight=None): - """Updates the metrics. - - Args: - label: An array to specify the groundtruth binary labels. Values must be - either 0 or 1. - prob: An array to specify the prediction probabilities. Values must be - within [0, 1.0]. - weight: An array to specify the sample weight for the auc computation. - """ - self._label += label - self._prob += prob - if weight: - self._weight += weight - else: - self._weight += [1 for _ in range(len(label))] - - if self._samples > 0: - self._label = self._label[-self._samples:] - self._prob = self._prob[-self._samples:] - self._weight = self._weight[-self._samples:] - - @property - def value(self): - return self._score_fn(self._label, self._prob, sample_weight=self._weight) - - def Summary(self, name): - - def _Setter(fig, axes): - # 20 ticks betweein 0 and 1. - ticks = np.arange(0, 1.05, 0.05) - axes.grid(b=True) - axes.set_xlabel(self._plot_labels[0]) - axes.set_xticks(ticks) - axes.set_ylabel(self._plot_labels[1]) - axes.set_yticks(ticks) - fig.tight_layout() - - xs, ys, _ = self._curve_fn( - self._label, self._prob, sample_weight=self._weight) - if self._mode == 'pr': - # Swap because sklearn returns <'precision', 'recall'>. - xs, ys = ys, xs - ret = plot.Curve(name=name, figsize=(12, 12), xs=xs, ys=ys, setter=_Setter) - ret.value.add(tag=name, simple_value=self.value) - return ret - - -class CorrelationMetric(BaseMetric): - """Class to compute correlation.""" - - def __init__(self, mode='pearson', samples=-1): - """Constructor of the class. - - Args: - mode: Possible values: 'pearson', 'spearman', 'kendalltau'. - samples: The number of sample points to compute the correlation. If -1, - include all points seen thus far. - - Raises: - ImportError: If user has not installed scipy.stats, raise an ImportError. - """ - if not HAS_SCIPY_STATS: - raise ImportError('CorrelationMetric depends on scipy.stats.') - - assert mode in ['pearson', 'spearman', 'kendalltau'] - self._mode = mode - self._samples = samples - self._target = [] - self._pred = [] - - def Update(self, target, pred): - """Updates the metrics. - - Args: - target: An array to specify the groundtruth float target. - pred: An array to specify the prediction. - """ - self._target += target - self._pred += pred - - if self._samples > 0: - self._target = self._target[-self._samples:] - self._pred = self._pred[-self._samples:] - - @property - def value(self): - # only use the correlation, p-value is ignored. - if self._mode == 'pearson': - return scipy.stats.pearsonr(self._target, self._pred)[0] - elif self._mode == 'spearman': - return scipy.stats.spearmanr(self._target, self._pred)[0] - else: - return scipy.stats.kendalltau(self._target, self._pred)[0] - - -class SamplingMetric(ConfigurableMetric): - """Sampling metric base class. - - Subclasses must implement _CreateSummary(); sampling will be handled - by this base class. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_samples', 8, 'The number of samples to store uniformly.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._sampler = py_utils.UniformSampler(num_samples=p.num_samples) - self._summary = None - - @property - def samples(self): - """Returns an iterable of sampled decoded outputs to compute Summaries.""" - return self._sampler.samples - - def Update(self, decoded_outputs): - """Samples the input decoded_outputs NestedMap. - - Args: - decoded_outputs: A `.NestedMap`. - """ - self._sampler.Add(decoded_outputs) - # Invalidate cache. - self._summary = None - - def Summary(self, name): - if self._summary is None: - self._summary = self._CreateSummary(name) - return self._summary - - def _CreateSummary(self, name): - """Returns a tf.Summary for this metric.""" - raise NotImplementedError() diff --git a/lingvo/core/metrics_test.py b/lingvo/core/metrics_test.py deleted file mode 100644 index 7bf1ff74..00000000 --- a/lingvo/core/metrics_test.py +++ /dev/null @@ -1,114 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for metrics.""" - -import lingvo.compat as tf -from lingvo.core import metrics -from lingvo.core import py_utils -from lingvo.core import test_utils -import numpy as np - - -class MetricsTest(test_utils.TestCase): - - def testAverageMetric(self): - m = metrics.AverageMetric() - m.Update(1.0) - m.Update(2.0, 10.0) - - self.assertEqual(1.0 + 2.0*10.0, m.total_value) - expected_average = (1.0 + 2.0*10.0) / (1.0 + 10.0) - self.assertEqual(expected_average, m.value) - - name = 'metric_name' - self.assertEqual( - tf.Summary(value=[tf.Summary.Value(tag=name, - simple_value=expected_average)]), - m.Summary(name)) - - # Calling m.Summary() does not reset statistics. - m.Update(1.0) - self.assertEqual(1.0 + 2.0*10.0 + 1.0, m.total_value) - - def testF1Metric(self): - m = metrics.F1Metric() - m.UpdateTruePositive(count=2.0) - m.UpdateFalsePositive() - m.UpdateFalseNegative() - - precision = 2.0 / 3.0 - recall = 2.0 / 3.0 - expected_f1 = 2 * precision * recall / (precision + recall) - self.assertAlmostEqual(expected_f1, m.value) - - name = 'my_f1_metric' - self.assertEqual( - tf.Summary(value=[tf.Summary.Value(tag=name, - simple_value=expected_f1)]), - m.Summary(name)) - - def testCorpusBleuMetric(self): - m = metrics.CorpusBleuMetric() - m.Update('a b c d', 'a b c d') - m.Update('a b c', 'a b c') - - self.assertEqual(1.0, m.value) - - name = 'corpus_bleu' - self.assertEqual( - tf.Summary(value=[tf.Summary.Value(tag=name, simple_value=1.0)]), - m.Summary(name)) - - def testCorrelationMetric(self): - m = metrics.CorrelationMetric() - m.Update([1.0, 2.0, 3.0], [0.1, 0.2, 0.3]) - m.Update([1.0, 2.0, 3.0], [0.1, 0.2, 0.3]) - self.assertEqual(1.0, m.value) - - def testSamplingMetric(self): - - class TestSamplingMetric(metrics.SamplingMetric): - - def _CreateSummary(self, name): - ret = tf.Summary() - for sample in self.samples: - value = sample.value - ret.value.add(tag=name, simple_value=value) - return ret - - np.random.seed(1337) - p = TestSamplingMetric.Params() - p.num_samples = 2 - m = p.Instantiate() - m.Update(py_utils.NestedMap(value=1)) - summary = m.Summary('test') - self.assertEqual(1, summary.value[0].simple_value) - - # Add four more updates. - m.Update(py_utils.NestedMap(value=2)) - m.Update(py_utils.NestedMap(value=3)) - m.Update(py_utils.NestedMap(value=4)) - m.Update(py_utils.NestedMap(value=5)) - summary = m.Summary('test') - # Reservoir sampling will sample values 5 and 3 to remain with the current - # seed. - self.assertEqual(2, len(summary.value)) - self.assertEqual(5, summary.value[0].simple_value) - self.assertEqual(3, summary.value[1].simple_value) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ml_perf_bleu_metric.py b/lingvo/core/ml_perf_bleu_metric.py deleted file mode 100644 index 4e5f2a9d..00000000 --- a/lingvo/core/ml_perf_bleu_metric.py +++ /dev/null @@ -1,228 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""The MLPerf reference implementation of BLEU.""" - -import collections -import math -import re -import sys -import unicodedata - -import lingvo.compat as tf -from lingvo.core import metrics -import numpy as np -import six - - -def is_unicode(s): - if isinstance(s, str): - return True - return False - - -def to_unicode(s, ignore_errors=False): - if is_unicode(s): - return s - error_mode = "ignore" if ignore_errors else "strict" - return s.decode("utf-8", errors=error_mode) - - -def native_to_unicode(s): - if is_unicode(s): - return s - try: - return to_unicode(s) - except UnicodeDecodeError: - res = to_unicode(s, ignore_errors=True) - return res - - -def _get_ngrams(segment, max_order): - """Extracts all n-grams up to a given maximum order from an input segment. - - Args: - segment: text segment from which n-grams will be extracted. - max_order: maximum length in tokens of the n-grams returned by this methods. - - Returns: - The Counter containing all n-grams up to max_order in segment - with a count of how many times each n-gram occurred. - """ - ngram_counts = collections.Counter() - for order in range(1, max_order + 1): - for i in range(0, len(segment) - order + 1): - ngram = tuple(segment[i:i + order]) - ngram_counts[ngram] += 1 - return ngram_counts - - -def compute_bleu(reference_corpus, - translation_corpus, - max_order=4, - use_bp=True): - """Computes BLEU score of translated segments against one or more references. - - Args: - reference_corpus: list of references for each translation. Each reference - should be tokenized into a list of tokens. - translation_corpus: list of translations to score. Each translation should - be tokenized into a list of tokens. - max_order: Maximum n-gram order to use when computing BLEU score. - use_bp: boolean, whether to apply brevity penalty. - - Returns: - BLEU score. - """ - reference_length = 0 - translation_length = 0 - bp = 1.0 - geo_mean = 0 - - matches_by_order = [0] * max_order - possible_matches_by_order = [0] * max_order - precisions = [] - - for (references, translations) in zip(reference_corpus, translation_corpus): - reference_length += len(references) - translation_length += len(translations) - ref_ngram_counts = _get_ngrams(references, max_order) - translation_ngram_counts = _get_ngrams(translations, max_order) - - overlap = dict((ngram, min(count, translation_ngram_counts[ngram])) - for ngram, count in ref_ngram_counts.items()) - - for ngram in overlap: - matches_by_order[len(ngram) - 1] += overlap[ngram] - for ngram in translation_ngram_counts: - possible_matches_by_order[len(ngram) - - 1] += translation_ngram_counts[ngram] - precisions = [0] * max_order - smooth = 1.0 - for i in range(0, max_order): - if possible_matches_by_order[i] > 0: - precisions[i] = matches_by_order[i] / possible_matches_by_order[i] - if matches_by_order[i] > 0: - precisions[i] = matches_by_order[i] / possible_matches_by_order[i] - else: - smooth *= 2 - precisions[i] = 1.0 / (smooth * possible_matches_by_order[i]) - else: - precisions[i] = 0.0 - - if max(precisions) > 0: - p_log_sum = sum(math.log(p) for p in precisions if p) - geo_mean = math.exp(p_log_sum / max_order) - - if use_bp: - if not reference_length: - bp = 1.0 - else: - ratio = translation_length / reference_length - if ratio <= 0.0: - bp = 0.0 - elif ratio >= 1.0: - bp = 1.0 - else: - bp = math.exp(1 - 1. / ratio) - bleu = geo_mean * bp - return np.float32(bleu) - - -def bleu_score(predictions, labels, **unused_kwargs): - """BLEU score computation between labels and predictions. - - An approximate BLEU scoring method since we do not glue word pieces or - decode the ids and tokenize the output. By default, we use ngram order of 4 - and use brevity penalty. Also, this does not have beam search. - - Args: - predictions: tensor, model predictions - labels: tensor, gold output. - - Returns: - bleu: int, approx bleu score - """ - outputs = tf.cast(tf.argmax(predictions, axis=-1), dtype=tf.int32) - # Convert the outputs and labels to a [batch_size, input_length] tensor. - outputs = tf.squeeze(outputs, axis=[-1, -2]) - labels = tf.squeeze(labels, axis=[-1, -2]) - - bleu = tf.py_func(compute_bleu, (labels, outputs), tf.float32) - return bleu, tf.constant(1.0) - - -class UnicodeRegex: - """Ad-hoc hack to recognize all punctuation and symbols.""" - - def __init__(self): - punctuation = self.property_chars("P") - self.nondigit_punct_re = re.compile(r"([^\d])([" + punctuation + r"])") - self.punct_nondigit_re = re.compile(r"([" + punctuation + r"])([^\d])") - self.symbol_re = re.compile("([" + self.property_chars("S") + "])") - - def property_chars(self, prefix): - return "".join( - six.unichr(x) - for x in range(sys.maxunicode) - if unicodedata.category(six.unichr(x)).startswith(prefix)) - - -uregex = UnicodeRegex() - - -def bleu_tokenize(string): - """Tokenize a string following the official BLEU implementation. - - Args: - string: the input string - - Returns: - a list of tokens - """ - string = uregex.nondigit_punct_re.sub(r"\1 \2 ", string) - string = uregex.punct_nondigit_re.sub(r" \1 \2", string) - string = uregex.symbol_re.sub(r" \1 ", string) - return string.split() - - -def bleu_wrapper(ref_lines, hyp_lines, case_sensitive=False): - """Compute BLEU for two files (reference and hypothesis translation).""" - assert len(ref_lines) == len(hyp_lines), ("{} != {}".format( - len(ref_lines), len(hyp_lines))) - ref_lines = [native_to_unicode(x) for x in ref_lines] - hyp_lines = [native_to_unicode(x) for x in hyp_lines] - if not case_sensitive: - ref_lines = [x.lower() for x in ref_lines] - hyp_lines = [x.lower() for x in hyp_lines] - ref_tokens = [bleu_tokenize(x) for x in ref_lines] - hyp_tokens = [bleu_tokenize(x) for x in hyp_lines] - return compute_bleu(ref_tokens, hyp_tokens) - - -class MlPerfBleuMetric(metrics.BaseMetric): - """Use the MLPerf reference impelmentation.""" - - def __init__(self, **kwargs): - self._ref_lines = [] - self._hyp_lines = [] - - def Update(self, ref_str, hyp_str): - self._ref_lines.append(ref_str) - self._hyp_lines.append(hyp_str) - - @property - def value(self): - return bleu_wrapper(self._ref_lines, self._hyp_lines) diff --git a/lingvo/core/ml_perf_bleu_metric_test.py b/lingvo/core/ml_perf_bleu_metric_test.py deleted file mode 100644 index 1714e127..00000000 --- a/lingvo/core/ml_perf_bleu_metric_test.py +++ /dev/null @@ -1,33 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for metrics.""" - -import lingvo.compat as tf -from lingvo.core import ml_perf_bleu_metric -from lingvo.core import test_utils - - -class MlPerfMetricsTest(test_utils.TestCase): - - def testMlPerfBleuMetric(self): - m = ml_perf_bleu_metric.MlPerfBleuMetric() - m.Update(u"a b a z", u"a b a c") - m.Update(u"y f g d k l m", u"e f \u2028 d") - self.assertAllClose(0.2638, m.value, atol=1e-03) - - -if __name__ == "__main__": - tf.test.main() diff --git a/lingvo/core/ml_perf_log.py b/lingvo/core/ml_perf_log.py deleted file mode 100644 index d499f5b8..00000000 --- a/lingvo/core/ml_perf_log.py +++ /dev/null @@ -1,80 +0,0 @@ -# Lint as: python3 - -# Copyright 2018 MLBenchmark Group. 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. -# ============================================================================== -"""Convenience function for logging compliance tags to stdout.""" - -import inspect -import json -import logging -import os -import re -import sys -import time - -PATTERN = re.compile('[a-zA-Z0-9]+') - -LOG_FILE = os.getenv('COMPLIANCE_FILE') -# create logger with 'spam_application' -LOGGER = logging.getLogger('mlperf_compliance') -LOGGER.setLevel(logging.DEBUG) - -_STREAM_HANDLER = logging.StreamHandler(stream=sys.stdout) -_STREAM_HANDLER.setLevel(logging.INFO) -LOGGER.addHandler(_STREAM_HANDLER) - -if LOG_FILE: - _FILE_HANDLER = logging.FileHandler(LOG_FILE) - _FILE_HANDLER.setLevel(logging.DEBUG) - LOGGER.addHandler(_FILE_HANDLER) -else: - _STREAM_HANDLER.setLevel(logging.DEBUG) - - -def get_caller(stack_index=2, root_dir=None): - caller = inspect.getframeinfo(inspect.stack()[stack_index][0]) - - # Trim the filenames for readability. - filename = caller.filename - if root_dir is not None: - filename = re.sub('^' + root_dir + '/', '', filename) - return (filename, caller.lineno) - - -# :::MLL 1556733699.71 run_start: {"value": null, -# "metadata": {"lineno": 77, "file": main.py}} -LOG_TEMPLATE = ':::MLL {:.3f} {}: {{"value": {}, "metadata": {}}}' - - -def mlperf_format(key, value, now, stack_offset=0, metadata=None): - """Format a message for MLPerf.""" - if metadata is None: - metadata = {} - - if 'lineno' not in metadata: - filename, lineno = get_caller(2 + stack_offset, root_dir=None) - metadata['lineno'] = lineno - metadata['file'] = filename - - msg = LOG_TEMPLATE.format(now, key, json.dumps(value), json.dumps(metadata)) - return msg - - -def mlperf_print(key, value, stack_offset=0, metadata=None): - now = time.time() - LOGGER.info( - mlperf_format( - key, value, now, stack_offset=stack_offset + 1, metadata=metadata)) - return now diff --git a/lingvo/core/ml_perf_log_test.py b/lingvo/core/ml_perf_log_test.py deleted file mode 100644 index 3a4d8d32..00000000 --- a/lingvo/core/ml_perf_log_test.py +++ /dev/null @@ -1,39 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.core.tshape.""" - -import json -import lingvo.compat as tf -from lingvo.core import ml_perf_log as mlp_log - - -class TestMLPerfLog: - """Test mlperf log.""" - - def testFormat(self): - msg = mlp_log.mlperf_format('foo_key', {'whiz': 'bang'}) - parts = msg.split() - assert parts[0] == ':::MLL' - assert float(parts[1]) > 10 - assert parts[2] == 'foo_key:' - j = json.loads(' '.join(parts[3:])) - assert j['value'] == {'whiz': 'bang'} - assert j['metadata']['lineno'] == 21 - assert 'test_mlp_log' in j['metadata']['file'] - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ml_perf_tokenizer.py b/lingvo/core/ml_perf_tokenizer.py deleted file mode 100644 index fe764ade..00000000 --- a/lingvo/core/ml_perf_tokenizer.py +++ /dev/null @@ -1,34 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tokenizer for use for the MLPerf transformer benchmark.""" - -from lingvo.core import ops -from lingvo.core import tokenizers - - -class MlPerfTokenizer(tokenizers.BaseTokenizer): - """Id->String only for MLPerf decoding.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define("vocab_filepath", None, "Specifies a filepath to the vocab.") - return p - - def IdsToStrings(self, ids, lens): - p = self.params - return ops.ml_perf_subword_id_to_string( - ids, lens, vocab_filepath=p.vocab_filepath) diff --git a/lingvo/core/model_helper.py b/lingvo/core/model_helper.py deleted file mode 100644 index 018aec15..00000000 --- a/lingvo/core/model_helper.py +++ /dev/null @@ -1,79 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Some utilities for configuring models.""" - -import lingvo.compat as tf -from lingvo.core import rnn_layers - -FLAGS = tf.flags.FLAGS - - -def CreateUnidirectionalRNNParams(layer_params, cell_params): - """Creates parameters for uni-directional RNN layer. - - Based on `layer_params.unidi_rnn_type`. - - Args: - layer_params: parameters for the layers, has to contain unidi_rnn_type. - cell_params: parameters for the RNN cell. - Returns: - Parameters for uni-directional RNN layer. - Raises: - ValueError: if `unidi_rnn_type` is invalid. - """ - p = layer_params - assert hasattr(p, - 'unidi_rnn_type'), 'layer params must contain unidi_rnn_type' - unidi_rnn_type = p.unidi_rnn_type - if unidi_rnn_type == 'func': - unidi_cls = rnn_layers.FRNN - elif unidi_rnn_type == 'quasi_ifo' or unidi_rnn_type == 'sru': - unidi_cls = rnn_layers.FRNN - else: - raise ValueError('Invalid unidi_rnn_type: %s', unidi_rnn_type) - params = unidi_cls.Params() - params.cell = cell_params - return params - - -def CreateBidirectionalRNNParams(layer_params, forward_cell_params, - backward_cell_params): - """Creates parameters for bi-directional RNN layer. - - Based on `layer_params.bidi_rnn_type`. - - Args: - layer_params: parameters for the layers, has to contain bidi_rnn_type. - forward_cell_params: parameters for the forward RNN cell. - backward_cell_params: parameters for the backward RNN cell. - Returns: - Parameters for bi-directional RNN layer. - Raises: - ValueError: if `bidi_rnn_type` is invalid. - """ - p = layer_params - assert hasattr(p, 'bidi_rnn_type'), 'layer params must contain bidi_rnn_type' - bidi_rnn_type = p.bidi_rnn_type - if bidi_rnn_type == 'func': - bidi_cls = rnn_layers.BidirectionalFRNN - elif bidi_rnn_type == 'quasi_ifo' or bidi_rnn_type == 'sru': - bidi_cls = rnn_layers.BidirectionalFRNNQuasi - else: - raise ValueError('Invalid bidi_rnn_type: %s', bidi_rnn_type) - params = bidi_cls.Params() - params.fwd = forward_cell_params - params.bak = backward_cell_params - return params diff --git a/lingvo/core/moe_layers.py b/lingvo/core/moe_layers.py deleted file mode 100644 index 559a9d72..00000000 --- a/lingvo/core/moe_layers.py +++ /dev/null @@ -1,1373 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Layers and utilities that facilitate building MOE models.""" - -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core import tpu_summary - -# pylint: disable=g-direct-tensorflow-import -from tensorflow.compiler.xla.experimental.xla_sharding import xla_sharding -# pylint: enable=g-direct-tensorflow-import - - -def Split(x, - split_dimension, - num_devices, - use_sharding_op=True, - input_shape=None): - """Wrapper for xla_sharding.split. - - Args: - x: Tensor to annotate. - split_dimension: xla_sharding.split arg. - num_devices: xla_sharding.split arg. - use_sharding_op: If true, adds a sharding op to set the sharding: - tensor = gen_xla_ops.xla_sharding(tensor) - See http://cs/search/?q=XlaSharding+file:xla_ops.cc - - hyouklee@: use_sharding_op=False - "It adds the sharding attribute to the op itself. The outcome is that, - that information could be lost by TF graph transformations. Also, - directly attaching the sharding annotation to the op caused some - compilation failures in the past (due to incompatible shardings), so the - plan is to make use_sharding_op to be the default." - - "The only case I would set it to False today is when annotating weights. - Weight annotation does some special handling, so there may be some - changes needed in that logic if we add separate sharding op." - input_shape: The shape of the original tensor. - - Returns: - Tensor conditionally annotated with sharding. - """ - if not py_utils.use_tpu() or num_devices is None or not num_devices > 1: - return x - return xla_sharding.split( - x, - split_dimension, - num_devices, - input_shape=input_shape, - use_sharding_op=use_sharding_op, - ) - - -class VarLayer(base_layer.BaseLayer): - """Container for variables.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('weights', None, '[(name, WeightParams)..] list.') - p.name = p.name or 'w' - return p - - def __init__(self, params): - super().__init__(params) - for k, v in self.params.weights: - vp = v.Copy() - if vp.init is None: - vp.init = self.params.params_init - self.CreateVariable(k, vp) - - def FProp(self, theta, *args, **kwargs): - - def MaybeCastToFPropDtype(x): - if x is None or not x.dtype.is_floating or x.dtype == self._params.fprop_dtype: - return x - if self._params.fprop_dtype is None: - return x - return tf.cast(x, self._params.fprop_dtype) - - # TODO(lepikhin): MoEBuilder.Embedding can not use '->emb' rule without - # returning single element of list of one element below. - retval = [MaybeCastToFPropDtype(theta[k]) for k, _ in self.params.weights] - return retval[0] if len(retval) == 1 else retval - - -class ShardedVarLayer(VarLayer): - """Container for variables whose values shared across different devices.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('split_dimension', 0, 'Dimension to split') - p.Define('num_devices', None, - 'The number of cores to split weights and computation over.') - p.Define('cast_to_fprop_dtype', True, - 'Whether to cast variables to fprop_dtype') - return p - - def FProp(self, theta, *args, **kwargs): - p = self.params - - # TODO(huangyp, lepikhin): Maybe cast to fprop dtype as well. - def MaybeWeightSplitAndCastToFPropDtype(k): - x = self.vars[k].read_value() - if x is None: - return None - x = Split(x, p.split_dimension, p.num_devices, use_sharding_op=True) - if (p.cast_to_fprop_dtype and x.dtype.is_floating and - x.dtype != p.fprop_dtype and p.fprop_dtype): - x = tf.cast(x, p.fprop_dtype) - return x - - retval = [MaybeWeightSplitAndCastToFPropDtype(k) for k, _ in p.weights] - return retval[0] if len(retval) == 1 else retval - - -class StateLayer(base_layer.BaseLayer): - """Container for recurrent state for incremental decoding. - - It has two operation modes. - - During training, it does nothing. - It expects that FProp(x, t) is called with t=None, and returns x unchanged. - - During decoding, it expects: - - t: an int32 scalar and - x: a tensor of shape `[batch, 1, ...]`. - - It updates state `x_full[:, t, :] <- x[:, 0, :]` and returns x_full. - The shape of x_full is then `[batch, time, ...]`. - - The state is stored as theta.state attribute. - - To construct initial state, call InitState classmethod on the root layer. - InitState() will traverse root layer children recursively, will initialize - internal state for each StateLayer instance, and will return a nested - tuple of states. - - For incremental iteration the static methods work as follows:: - - dec = builder.DecoderLayerStack(...).Instantiate() - state0 = StateLayer.InitState(dec, shape=[tgt_batch, max_len]) - theta0 = StateLayer.UpdateTheta(dec, dec.theta, state0, t=0) - # (FProp in nested StateLayer now has access to 'state0' and 't') - dec.FProp(theta0, ...) - # FProp will modify theta0 in-place - state1 = state0.copy() - state1 = StateLayer.UpdateState(dec, theta0, state1) - - (see `moe_builder_test.py` for a more complete example.) - """ - _use_flat_beam_search = False - - @classmethod - def Params(cls): - p = super().Params() - p.Define('shape', [None, None], 'batch, time, etc...') - return p - - def NewState(self, shape): - """Returns initial state. - - Args: - shape: [batch, time] for beam_search_tpu_helper or [batch, beam, time] for - flat_beam_search. - - Returns: - zero-initialized state tensor with shape [batch, time, ...] for - beam_search_tpu_helper or [time, batch, beam, ...] for flat_beam_search. - - Raises: - ValueError: the length of shape is not 2 or 3. - """ - fprop_dtype = py_utils.FPropDtype(self.params) - - if len(shape) == 2: - # For use with beam_search_tpu_helper batch_major_compute=1 - p = self.params - shape = tuple(shape) + tuple(p.shape[2:]) - state = tf.zeros(shape, fprop_dtype) - return state - elif len(shape) == 3: - # For use with flat_beam_search - self._use_flat_beam_search = True - p = self.params - batch, beam, max_steps = shape - state = tf.Empty( - [max_steps, batch, beam] + p.shape[2:], fprop_dtype, init=True) - return state - else: - raise ValueError('bad shape: %r' % shape) - - def FProp(self, theta, x): - p = self.params - if not hasattr(theta, 't'): - return x - t = theta.t - if t is None: - return x - assert hasattr(theta, 'state') - state = theta.state - - tf.logging.info('p.name=%r', p.name) - tf.logging.info('state=%r', state) - tf.logging.info('x=%r', x) - tf.logging.info('t=%r', t) - - with tf.name_scope(p.name): - if not self._use_flat_beam_search: - # For tpu_beam_search_helper - z = tf.one_hot(t, tf.shape(state)[1]) - z = tf.expand_dims(z, 0) - while len(z.shape) < len(x.shape): - z = tf.expand_dims(z, -1) - y = state = (1 - z) * state + z * x - if self._use_flat_beam_search: - state_slice_size = int(state.shape[2]) - update_slice_size = int(x.shape[1]) - if update_slice_size == state_slice_size: - state = tf.InplaceUpdate(state, t, tf.cast(x, state.dtype)) - else: - # With prefix decoding the first call to decoder can have - # sequence length (N * beam_size) with N > 1. - # In this special case state tensor update is implemented as multiple - # InplaceUpdate ops each for a slice [batch_size, beam_size]. - div = int(update_slice_size / state_slice_size) - assert update_slice_size == state_slice_size * div, ( - update_slice_size, state_slice_size) - for i, x_i in enumerate(tf.split(x, div, 1)): - state = tf.InplaceUpdate(state, t + i, tf.cast(x_i, state.dtype)) - tf.logging.info('state*=%r', state) - # [T,B,L,...] - y = tf.cast(state, x.dtype) - # [T, B, L, ...] -> [B, T, L, ...] - perm = list(range(len(y.shape))) - perm[:2] = [1, 0] - y = tf.transpose(y, perm) - # [B, T, L, ...] -> [B, T*L, ...] - y_shape = list(y.shape) - y_shape[1:3] = [int(y_shape[1]) * int(y_shape[2])] - y = tf.reshape(y, y_shape) - theta.state = state - - tf.logging.info('y=%r', y) - return y - - @classmethod - def InitState(cls, layer, shape): - """Returns new state with leading shape=[batch, time].""" - - def Rec(layer): # pylint: disable=missing-docstring - state = None - if isinstance(layer, StateLayer): - assert not layer.children - state = layer.NewState(shape) - return state - for c_name, c in layer.children.items(): - c_state = Rec(c) - if c_state is not None: - if state is None: - state = py_utils.NestedMap() - state[c_name] = c_state - return state - - state = Rec(layer) - assert state is not None - return state - - @classmethod - def UpdateTheta(cls, layer, theta, state, t=None): - """Returns theta with state.""" - - def Rec(layer, theta, state): # pylint: disable=missing-docstring - if isinstance(layer, StateLayer): - theta.state = state - theta.t = t - return - for c_name, c in layer.children.items(): - if c_name in state: - Rec(c, theta[c_name], state[c_name]) - - Rec(layer, theta, state) - return theta - - @classmethod - def UpdateState(cls, layer, theta, state): - """Returns updated state from theta.""" - - def Rec(layer, theta, state): # pylint: disable=missing-docstring - for c_name, c in layer.children.items(): - if isinstance(c, StateLayer): - state[c_name] = theta[c_name].state - elif c_name in state: - Rec(c, theta[c_name], state[c_name]) - - Rec(layer, theta, state) - return state - - -class OverrideLayer(base_layer.BaseLayer): - """Allows to override arbitrary tensors in the graph. - - If key is not set in the global context, FProp does nothing. - Otherwise it returns value associated to 'key'. - - To override a tensor during my_layer.FProp: - - OverrideLayer.Set(key, value) - out_with_override = my_layer.FProp(...) - OverrideLayer.Clear() - """ - - _OVERRIDE = {} - - @classmethod - def Params(cls): - p = super().Params() - p.Define('key', None, 'Context key') - return p - - def FProp(self, theta, x): - p = self.params - if p.key and p.key in self._OVERRIDE: - return self._OVERRIDE[p.key] - else: - return x - - @classmethod - def Set(cls, k, v): - cls._OVERRIDE[k] = v - - @classmethod - def Clear(cls): - cls._OVERRIDE.clear() - - -class SharedEmbeddingSoftmaxLayer(base_layer.BaseLayer): - """Shared weights for embemdding lookup and softmax.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('vocab_size', 0, 'Num tokens in vocab.') - p.Define('max_len', 0, 'Num of token in the sequence.') - p.Define('embedding_dim', 0, 'Depth of the output.') - p.Define('z_loss_coef', 1e-4, 'Label smoothing.') - p.Define('num_devices', 1, 'Number of devices for sharding.') - p.Define('logits_abs_max', None, 'Logits clipping.') - p.Define('label_smoothing', 0.1, 'Label smoothing.') - p.Define( - 'use_tgt_labels_size_as_loss_denominator', True, - 'False to use total number of non-padding tokens instead of ' - 'fixed tgt_labels tensor size.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - emb_p = py_utils.WeightParams( - init=py_utils.WeightInit.Gaussian(), - shape=[p.vocab_size, p.embedding_dim]) - pos_emb_p = py_utils.WeightParams( - init=py_utils.WeightInit.Gaussian(), shape=[p.max_len, p.embedding_dim]) - self.CreateVariable('embedding', emb_p) - self.CreateVariable('pos_emb', pos_emb_p) - - def _MaybeSplit(self, x): - if True or self.params.num_devices <= 1: - return x - return Split(x, 0, self.params.num_devices) - - def FProp(self, theta, ids, segment_pos): - p = self.params - fprop_dtype = py_utils.FPropDtype(p) - - ids = self._MaybeSplit(ids) - segment_pos = self._MaybeSplit(segment_pos) - - one_hot_ids = tf.one_hot(ids, p.vocab_size, dtype=fprop_dtype) - one_hot_ids = self._MaybeSplit(one_hot_ids) - - one_hot_pos = tf.one_hot(segment_pos, p.max_len, dtype=fprop_dtype) - one_hot_pos = self._MaybeSplit(one_hot_pos) - - token_emb = tf.einsum('VH,BLV->BLH', theta.embedding, one_hot_ids) - token_emb = self._MaybeSplit(token_emb) - - pos_emb = tf.einsum('VH,BLV->BLH', theta.pos_emb, one_hot_pos) - pos_emb = self._MaybeSplit(pos_emb) - return self._MaybeSplit(token_emb + pos_emb) - - def ComputeLoss(self, theta, activation, labels, segment_ids): - p = self.params - activation = self._MaybeSplit( - self._MaybeSplit(activation) * (p.embedding_dim**-0.5)) - softmax_weights = theta.embedding - if activation.dtype != softmax_weights.dtype: - softmax_weights = tf.cast(softmax_weights, activation.dtype) - logits = self._MaybeSplit( - tf.einsum('BLM,VM->BLV', activation, softmax_weights)) - if p.logits_abs_max is not None: - logits = self._MaybeSplit( - py_utils.clip_by_value(logits, -p.logits_abs_max, p.logits_abs_max)) - - off_value = p.label_smoothing / p.vocab_size - on_value = 1.0 - p.label_smoothing + off_value - soft_targets = self._MaybeSplit( - tf.one_hot( - labels, p.vocab_size, on_value=on_value, off_value=off_value)) - xent = self._MaybeSplit( - tf.nn.softmax_cross_entropy_with_logits( - labels=tf.one_hot(labels, p.vocab_size), logits=logits)) - loss = self._MaybeSplit( - tf.nn.softmax_cross_entropy_with_logits( - labels=soft_targets, logits=logits)) - soft_targets_xent = loss - - if p.z_loss_coef > 0.0: - log_z = tf.math.reduce_logsumexp(logits, -1) - z_loss_inc = p.z_loss_coef * tf.math.square(log_z) - loss += z_loss_inc - - non_padding = self._MaybeSplit( - tf.cast(tf.not_equal(segment_ids, 0), py_utils.FPropDtype(p))) - - per_token_loss = self._MaybeSplit(loss * non_padding) - if p.z_loss_coef > 0.0: - per_token_z_loss_inc = self._MaybeSplit(z_loss_inc * non_padding) - - if p.use_tgt_labels_size_as_loss_denominator: - # E.g. loss is going to be tiny if inputs are not packed and only a - # fraction of tgt_labels are non-padding. - loss_denom = tf.reduce_sum(tf.ones_like(non_padding)) - per_example_loss_denom = tf.reduce_sum(tf.ones_like(non_padding), 1) - else: - loss_denom = tf.reduce_sum(non_padding) - per_example_loss_denom = tf.reduce_sum(non_padding, 1) - avg_loss = tf.reduce_sum(per_token_loss) / loss_denom - avg_z_loss_inc = (tf.reduce_sum(per_token_z_loss_inc) / - loss_denom) if p.z_loss_coef > 0.0 else 0.0 - - soft_targets_xent = ( - tf.reduce_sum(self._MaybeSplit(soft_targets_xent * non_padding)) / - tf.reduce_sum(non_padding)) - - # TODO(lepikhin): consider returning - # {'loss': (unnormalized per_token_loss, tf.reduce_sum(non_padding))} - per_example_loss = { - 'loss': tf.reduce_sum(per_token_loss, 1) / per_example_loss_denom - } - return { - 'mean_xent': (tf.reduce_sum(self._MaybeSplit(xent * non_padding)) / - tf.reduce_sum(non_padding), tf.reduce_sum(non_padding)), - 'soft_targets_xent': (soft_targets_xent, tf.reduce_sum(non_padding)), - 'weight': (tf.reduce_sum(non_padding), 1.0), - 'loss': (avg_loss, 1.0), - 'avg_z_loss_inc': (avg_z_loss_inc, 1.0), - }, per_example_loss - - -def Top2GatingOnLogits(inputs, - paddings, - logits, - num_devices, - experts_dim, - expert_capacity_dim, - fprop_dtype, - use_xla_sharding=True, - second_expert_policy='all', - second_expert_threshold=0.0, - legacy_mtf_behavior=True, - capacity_factor=None): - """Computes Top-2 gating for Mixture-of-Experts. - - There are two expected usages of this function: - - 1. used with xla_sharding. In this case, 'inputs' corresponds to a sharded - tensor across multiple tpu cores. The operations within this function are - automatically sharded/replicated across tpu cores. - 2. used within ML-Pathways. In this case, 'inputs' is always local to one tpu - core. All computations below are carried out on one tpu core only. This - function tries to dispatch examples across tpu cores in such a way that - each expert is assigned no more than 'expert_capacity_dim' number of - examples. - - Below ` indicates common way of splitting along mesh dimension. - - Dimensions cheat sheet: - - G: group_dim - S: group_size_dim - E: number of experts - C: capacity per expert - M: model_dim (same as input_dim, same as output_dim) - B: original batch_dim - L: original sequence_length_dim - - Note that for local_dispatch original batch BLM is reshaped into GSM, each - group `g = 0...G-1` is being dispatched independently. - - Args: - inputs: G`SM Tensor. - paddings: G`S Tensor. - logits: G`SE Tensor. - num_devices: number of MoE devices for local dispatch - experts_dim: number of experts. - expert_capacity_dim: number of examples per minibatch(group) per expert. - Each example is typically a vector of size input_dim, representing - embedded token or an element of Transformer layer output. - fprop_dtype: activations datatype to use. - use_xla_sharding: bool, True if this function is used for the xla_sharding - case. - second_expert_policy: 'all', 'sampling' or 'random'. - - - 'all': we greedily pick the 2nd expert. - - 'sampling': we sample the 2nd expert from the softmax. - - 'random': we optionally 'random'-ize dispatch to second-best expert - proportional to (weight / second_expert_threshold). - - second_expert_threshold: threshold for probability normalization for - second_expert_policy == 'random'. - legacy_mtf_behavior: bool, True if to match legacy mtf behavior exactly. - capacity_factor: if set, increases expert_capacity_dim to at least - (group_size * capacity_factor) / experts_dim - where `group_size` is the size of G dimension of `inputs`. If the - value of expert_capacity_dim is already big enough no change is made. - - TODO(lepikhin): get rid of the legacy_mtf_behavior flag. - - Returns: - A tuple (aux_loss, combine_tensor, dispatch_tensor). - - - aux_loss: auxiliary loss, for equalizing the expert assignment ratios. - - combine_tensor: G`SEC Tensor for combining expert outputs. - - dispatch_tensor: G`SEC Tensor, scattering/dispatching inputs to - experts. - """ - del inputs # inputs is currently not used. - raw_gates = tf.nn.softmax(logits) # along E dim - - if capacity_factor is not None: - # Determine expert capacity automatically depedning on the input size. - group_size_dim = int(logits.shape[1]) - auto_expert_capacity = int((group_size_dim * capacity_factor) / experts_dim) - if expert_capacity_dim < auto_expert_capacity: - expert_capacity_dim = auto_expert_capacity - # Round up to a multiple of 4 to avoid possible padding. - while expert_capacity_dim % 4: - expert_capacity_dim += 1 - tf.logging.info( - 'Setting expert_capacity_dim=%r (capacity_factor=%r ' - 'group_size_dim=%r experts_dim=%r name_scope=%r)', - expert_capacity_dim, capacity_factor, group_size_dim, experts_dim, - tf.get_default_graph().get_name_scope()) - tpu_summary.scalar('expert_capacity', expert_capacity_dim) - - # top first and second gate value and expert index for each input - # - # GSK Tensors, K=2 - def _MaybeSplit(x): - if use_xla_sharding: - return Split(x, 0, num_devices) - else: - return x - - def _CreateOverCapacityRatioSummary(mask, position_in_expert, capacity, name): - with tf.name_scope('over_capacity'): - over_capacity = tf.reduce_sum( - tf.cast( - tf.greater_equal(mask * position_in_expert, capacity), - mask.dtype)) - over_capacity_ratio = over_capacity / tf.maximum( - tf.constant(1.0, dtype=mask.dtype), tf.reduce_sum(mask)) - py_utils.AddTpuSummaryTensor(name, over_capacity_ratio) - tpu_summary.scalar(name, over_capacity_ratio, while_loop_reduce='mean') - - # As pointed out by zhifengc@ this method needs to be refactored. lepikhin@ - # and krikun@ will: - # - expand moe_spmd_test to compare Adafactor updates, slots on TPU - # including 2x2 with sharding - # - # - add more tests for policy="random" - # - # - add single step test for full size WMT model on CPU - # - # and then break this function into modules. - # - # GS - index_1 = tf.math.argmax(raw_gates, axis=-1, output_type=tf.int32) - index_1 = _MaybeSplit(index_1) - tpu_summary.tensor('index_1', index_1) - - # GSE - mask_1 = tf.one_hot(index_1, experts_dim, dtype=fprop_dtype) - mask_1 = _MaybeSplit(mask_1) - density_1_proxy = raw_gates - - importance = tf.ones_like(mask_1[:, :, 0]) - - if paddings is not None: - importance = 1.0 - paddings - mask_1 *= tf.expand_dims(importance, -1) - density_1_proxy *= tf.expand_dims(importance, -1) - - gate_1 = tf.einsum('GSE,GSE->GS', raw_gates, mask_1) - gates_without_top_1 = raw_gates * (1.0 - mask_1) - - if second_expert_policy == 'sampling': - # We directly sample the 2nd expert index from the softmax over of the 2nd - # expert by getting rid of the 1st expert already selected above. To do so, - # we set a very negative value to the logit corresponding to the 1st expert. - # Then we sample from the softmax (categorical) distribution using the - # Gumbel max trick. - noise = _MaybeSplit(tf.random.uniform(logits.shape, dtype=logits.dtype)) - # Generates standard Gumbel(0, 1) noise, GSE Tensors - noise = -tf.math.log(-tf.math.log(noise)) - very_negative_logits = _MaybeSplit( - (tf.ones_like(logits) * logits.dtype.max * - tf.constant(-0.7, dtype=logits.dtype))) - # Gets rid of the first expert by setting its logit to be very negative - updated_logits = _MaybeSplit( - tf.where(mask_1 > 0.0, very_negative_logits, logits)) - # Adds the Gumbel noise to the updated logits - noised_logits = _MaybeSplit(updated_logits + noise) - # Picks the index of the largest noised logit as the 2nd expert. This is - # equivalent to sampling from the softmax over the 2nd experts. - index_2 = tf.math.argmax(noised_logits, axis=-1, output_type=tf.int32) - else: - index_2 = tf.math.argmax(gates_without_top_1, axis=-1, output_type=tf.int32) - - index_2 = _MaybeSplit(index_2) - mask_2 = tf.one_hot(index_2, experts_dim, dtype=fprop_dtype) - mask_2 = _MaybeSplit(mask_2) - if paddings is not None: - mask_2 *= tf.expand_dims(importance, -1) - gate_2 = tf.einsum('GSE,GSE->GS', gates_without_top_1, mask_2) - - if legacy_mtf_behavior: - # cl/298510175 moved this branch for gate_{1,2} denom calculation here. - # - # For policy=random, it's better to nomalize gate_{1,2} before taking - # capacity into account and before potentially dropping second expert. - # - # According to mean_xent (http://short/_NzbZ5rINr5): - # MoE_512_102xen_PolicyAll_298510175 - # MoE_512_102xen_PolicyRandom_298510175 - # - # vs pre-cl/298510175 - # MoE_512_102xen_PolicyRandom - # MoE_512_102xen_PolicyAll - # - # it substantially improves policy=random with threshold=0.5 which - # historically was better than policy="all" - # - # Also confirmed this by decoding - # nmt_train/m4/data/es_en/test.txt - # nmt_train/m4/data/ru_en/test.txt - # nmt_train/m4/data/zh_en/test.txt - # and improving BLEU - # - # moe_decode.MoE_512_102xen_PolicyRandom_298510175-160000.batch1024.beam4.c_dim4.ln0.8.rkv.mteval102 - # 0.421443 - # 0.327102 - # 0.315693 - # vs - # moe_decode.feb18_non_fig_snapshot_2626_MoE_512_102xen_PolicyRandom-190000.batch1024.beam4.c_dim4.ln0.8.rkv.mteval102 - # 0.399232 - # 0.310606 - # 0.288229 - # - # Additional comparison, see mean_xent http://short/_YHccOhQtdu with - # legacy_mtf_behavior=False models - # 3 - MoE_512_102xen_PolicyAll_LegacyFalse - # 6 - MoE_512_102xen_PolicyRandom_LegacyFalse - # shows that policy="random" gets worse with legacy_mtf_behavior=False, and - # is similar to pre-cl/298510175 - # 4 - MoE_512_102xen_PolicyRandom - # - # gate_1 can become 0 due to Expert being out of capacity. - # - # gate_2 can become 0 due to - # second_expert_policy == 'random' - # or "out of capacity" scenario. - # - # Here we renormalize regardless of cases above. - denom = gate_1 + gate_2 + 1e-9 - gate_1 /= denom - gate_2 /= denom - - # We reshape the mask as [X*S, E], and compute cumulative sums of - # assignment indicators for each expert index e \in 0..E-1 independently. - # First occurrence of assignment indicator is excluded, see exclusive=True - # flag below. - position_in_expert_1 = tf.cumsum(mask_1, exclusive=True, axis=1) - - # GS Tensor - capacity = tf.cast(expert_capacity_dim, dtype=position_in_expert_1.dtype) - - # GE Tensor (reducing S out of GSE tensor mask_1) - # density_1[:, e] represents assignment ratio (num assigned / total) to - # expert e as top_1 expert without taking capacity into account. - if legacy_mtf_behavior: - density_denom = 1.0 - else: - density_denom = tf.reduce_mean( - importance, axis=(1))[:, tf.newaxis] + 1e-6 - density_1 = tf.reduce_mean(mask_1, axis=(1)) / density_denom - # density_1_proxy[:, e] represents mean of raw_gates for expert e, including - # those of examples not assigned to e with top_k. - density_1_proxy = tf.reduce_mean(density_1_proxy, axis=1) / density_denom - - with tf.name_scope('aux_loss'): - # The MoE paper (https://arxiv.org/pdf/1701.06538.pdf) uses an aux loss of - # reduce_mean(density_1_proxy * density_1_proxy). Here we replace one of - # the density_1_proxy with the discrete density_1 following - # mesh_tensorflow/transformer/moe.py?rcl=283569345. - aux_loss = tf.reduce_mean(density_1_proxy * density_1) # element-wise - aux_loss *= experts_dim * experts_dim # const coefficient - - # Add the over capacity ratio for expert 1 - _CreateOverCapacityRatioSummary(mask_1, position_in_expert_1, capacity, - 'over_capacity_1_ratio') - - mask_1 *= tf.cast(tf.less(position_in_expert_1, capacity), dtype=mask_1.dtype) - position_in_expert_1 = tf.einsum('GSE,GSE->GS', position_in_expert_1, mask_1) - - # How many examples in this sequence go to this expert - mask_1_count = tf.einsum('GSE->GE', mask_1) - # [batch, group] - mostly ones, but zeros where something didn't fit - mask_1_flat = tf.einsum('GSE->GS', mask_1) - - if second_expert_policy == 'all' or second_expert_policy == 'sampling': - pass - elif second_expert_policy == 'random': - # gate_2 is between 0 and 1, reminder: - # - # raw_gates = tf.nn.softmax(logits) - # index_1 = tf.math.argmax(raw_gates, axis=-1, output_type=tf.int32) - # mask_1 = tf.one_hot(index_1, experts_dim, dtype=fprop_dtype) - # gate_1 = tf.einsum('GSE,GSE->GS', raw_gates, mask_1) - # - # E.g. if gate_2 exceeds second_expert_threshold, then we definitely - # dispatch to second-best expert. Otherwise we dispatch with probability - # proportional to (gate_2 / threshold). - # - sampled_2 = tf.less( - _MaybeSplit(tf.random.uniform(gate_2.shape, dtype=gate_2.dtype)), - (gate_2 / max(second_expert_threshold, 1e-9))) - gate_2 *= tf.cast(sampled_2, gate_2.dtype) - mask_2 *= tf.cast(tf.expand_dims(sampled_2, -1), mask_2.dtype) - else: - raise ValueError(second_expert_policy) - - position_in_expert_2 = tf.cumsum( - mask_2, exclusive=True, axis=1) + tf.expand_dims(mask_1_count, 1) - - # Add the over capacity ratio for expert 2 - _CreateOverCapacityRatioSummary(mask_2, position_in_expert_2, capacity, - 'over_capacity_2_ratio') - - mask_2 *= tf.cast(tf.less(position_in_expert_2, capacity), mask_2.dtype) - position_in_expert_2 = tf.einsum('GSE,GSE->GS', position_in_expert_2, mask_2) - mask_2_flat = tf.reduce_sum(mask_2, axis=-1) - - # Equivalent non-einsum implementation: - # - # position_in_expert_2 *= mask_2 - # position_in_expert_2 = tf.reduce_sum( - # position_in_expert_2, axis=-1, name='position_in_expert_2') - - gate_1 *= mask_1_flat - gate_2 *= mask_2_flat - - if not legacy_mtf_behavior: - denom = gate_1 + gate_2 - # To avoid divide by 0. - denom = tf.where(denom > 0, denom, tf.ones_like(denom)) - gate_1 /= denom - gate_2 /= denom - - # GSC Tensor - b = tf.one_hot( - tf.cast(position_in_expert_1, dtype=tf.int32), - expert_capacity_dim, - dtype=fprop_dtype, - name='one_hot_b_0') - # GSE Tensor - a = tf.expand_dims(gate_1 * mask_1_flat, -1) * tf.one_hot( - index_1, experts_dim, dtype=fprop_dtype) - # GSEC Tensor - first_part_of_combine_tensor = tf.einsum( - 'GSE,GSC->GSEC', a, b, name='first_part_of_combine_tensor') - - # GSC Tensor - b = tf.one_hot( - tf.cast(position_in_expert_2, dtype=tf.int32), - expert_capacity_dim, - dtype=fprop_dtype, - name='one_hot_b_1') - # GSE Tensor - a = tf.expand_dims(gate_2 * mask_2_flat, -1) * tf.one_hot( - index_2, experts_dim, dtype=fprop_dtype) - second_part_of_combine_tensor = tf.einsum( - 'GSE,GSC->GSEC', a, b, name='second_part_of_combine_tensor') - - # GSEC Tensor - combine_tensor = ( - first_part_of_combine_tensor + second_part_of_combine_tensor) - combine_tensor = _MaybeSplit(combine_tensor) - - # GSEC Tensor - dispatch_tensor = tf.cast(tf.cast(combine_tensor, tf.bool), fprop_dtype) - dispatch_tensor = _MaybeSplit(dispatch_tensor) - - # TODO(yonghui): compute and return per-group aux_loss. - return aux_loss, combine_tensor, dispatch_tensor - - -def Top2Gating(w, - inputs, - paddings, - num_devices, - experts_dim, - expert_capacity_dim, - local_dispatch, - fprop_dtype, - use_xla_sharding=True, - second_expert_policy='all', - second_expert_threshold=0.0, - legacy_mtf_behavior=True, - capacity_factor=None): - """Computes Top-2 gating for Mixture-of-Experts. - - See Top2GatingOnLogits for more details. - - Note that for local_dispatch original batch BLM is reshaped into GSM, each - group `g = 0...G-1` is being dispatched independently. - - Args: - w: gating weights for each experts. - inputs: G`SM Tensor. - paddings: G`S Tensor. - num_devices: number of MoE devices for local dispatch - experts_dim: number of experts. - expert_capacity_dim: number of examples per minibatch(group) per expert. - Each example is typically a vector of size input_dim, representing - embedded token or an element of Transformer layer output. - local_dispatch: whether dispatch is local to the group (G dim) - fprop_dtype: activations datatype to use. - use_xla_sharding: bool, True if this function is used for the xla_sharding - case. - second_expert_policy: 'all' or 'random', we optionally 'random'-ize dispatch - to second-best expert proportional to (weight / second_expert_threshold). - second_expert_threshold: threshold for probability normalization for - second_expert_policy == 'random'. - legacy_mtf_behavior: True for legacy behavior with no re-normalization of - expert assignment weights if we go over capacity or randomly decide to not - dispatch to second expert. - capacity_factor: if set, increases expert_capacity_dim to at least - `(group_size * capacity_factor) / experts_dim` - where `group_size` is the size of G dimension of `inputs`. If the - value of expert_capacity_dim is already big enough no change is made. - - Returns: - A tuple (dispatch_tensor, combine_tensor, aux_loss). - - - dispatch_tensor: G`SEC Tensor, scattering/dispatching inputs to - experts. - - combine_tensor: G`SEC Tensor. - combining expert outputs. - - aux_loss: auxiliary loss, equalizing the expert assignment ratios. - """ - orig_inputs = inputs - if not local_dispatch: - inputs = tf.reshape(inputs, [1, inputs.shape[0] * inputs.shape[1], -1]) - if paddings is not None: - paddings = tf.reshape(paddings, [1, -1]) - - logits = tf.einsum('GSM,ME->GSE', inputs, w) - - top1_expert_per_example = tf.math.argmax(logits, -1) - - tpu_summary.tensor('top1_expert', top1_expert_per_example) - - aux_loss, combine_tensor, dispatch_tensor = Top2GatingOnLogits( - inputs, paddings, logits, num_devices, experts_dim, expert_capacity_dim, - fprop_dtype, use_xla_sharding, second_expert_policy, - second_expert_threshold, legacy_mtf_behavior, capacity_factor) - - if not local_dispatch: - dispatch_tensor = tf.reshape( - dispatch_tensor, orig_inputs.shape[:2] + dispatch_tensor.shape[2:]) - combine_tensor = tf.reshape( - combine_tensor, orig_inputs.shape[:2] + combine_tensor.shape[2:]) - - return py_utils.NestedMap( - combine_tensor=combine_tensor, - dispatch_tensor=dispatch_tensor, - aux_loss=aux_loss) - - -def FeedForwardNetworksApplyGating(gating, - inputs, - reshaped_inputs, - wi_split, - wo_split, - num_devices, - num_groups, - bi_split=None, - bo_split=None, - dropout_rate=0.0): - """Apply top_2 gating to feedforward networks. - - Args: - gating: returns from Top2Gating consisting of: dispatch_tensor, G`SEC - Tensor, scattering/dispatching inputs to experts. combine_tensor, G`SEC - Tensor, combining expert outputs. aux_loss. auxiliary loss, equalizing the - expert assignment ratios - inputs: G`SM Tensor. - reshaped_inputs: [G*S/E, M] Tensor. - wi_split: First projection weights [E, M, H] of the feedforward networks. - wo_split: Last projection weights [E, H, M] of the feedforward networks. - num_devices: number of devices. - num_groups: number of groups (generally matches to or proportional to - num_devices). - bi_split: First projection bias [E, 1, H] of the feedforward networks. - bo_split: Last projection bias [E, 1, M] of the feedforward networks. - dropout_rate: Dropout rate. - - Returns: - outputs: G`SM Tensor. - aux_loss: scalar auxilliar loss. - """ - # dispatch_tensor G`SEC - expert_inputs = tf.einsum('GSEC,GSM->EGCM', - Split(gating.dispatch_tensor, 0, num_devices), - Split(reshaped_inputs, 0, num_devices)) - expert_inputs = Split(expert_inputs, 0, num_devices) - - M = reshaped_inputs.shape[-1] # pylint: disable=invalid-name - E = expert_inputs.shape[0] # pylint: disable=invalid-name - - # combine_tensor: G`SEC - # pylint: disable=invalid-name - G = gating.combine_tensor.shape[0] - assert num_groups == tf.compat.dimension_value(G) - C = gating.combine_tensor.shape[-1] # pylint: disable=invalid-name - A = G * C - # pylint: enable=invalid-name - - # Reshaping EGCM => EAM where A = G*C, e.g. - # - # with E=512, G=1024 - # - # (512, 1024, 4, 1024) => (512, 4096, 1024) - expert_inputs = tf.reshape(expert_inputs, [expert_inputs.shape[0], A, M]) - expert_inputs = Split(expert_inputs, 0, num_devices) - - h = tf.einsum('EAM,EMH->EAH', expert_inputs, wi_split) - h = Split(h, 0, num_devices) - if bi_split is not None: - h += Split(bi_split, 0, num_devices) - h = Split(h, 0, num_devices) - - h = tf.nn.relu(h) - - if dropout_rate: - # we generally do not use stateless dropout in MoE since it introduces - # large uint32 tensor broadcast (per dehao@ study) - h = tf.nn.dropout(h, dropout_rate) - - expert_outputs = tf.einsum('EAH,EHM->EAM', Split(h, 0, num_devices), wo_split) - if bo_split is not None: - expert_outputs = Split(expert_outputs, 0, num_devices) - expert_outputs += Split(bo_split, 0, num_devices) - expert_outputs = Split(expert_outputs, 0, num_devices) - expert_outputs = tf.reshape(expert_outputs, [E, G, C, M]) - - # same as tf.transpose - expert_outputs = tf.einsum('EGCM->GECM', expert_outputs) - - expert_outputs = Split(expert_outputs, 0, num_devices) - - # split by G dim - combined_outputs = tf.einsum('GSEC,GECM->GSM', - Split(gating.combine_tensor, 0, num_devices), - expert_outputs) - - outputs = Split(tf.reshape(combined_outputs, inputs.shape), 0, num_devices) - aux_loss = gating.aux_loss - return outputs, aux_loss - - -def GatherK(selected_pos, values, k, num_devices=1): - """Gather up to k elements from given tensors at selected pos under SPMD. - - Example:: - - # Input - k = 3 - - selected_pos = [ - [0, 0, 1, 1], - [0, 1, 1, 0], - [0, 0, 0, 0], - [1, 1, 1, 0], - [1, 1, 1, 1], # topk(k=3) largest indices are selected in this row. - ] - - value_2d = [ - [1, 3, 5, 7], - [9, 11, 13, 15], - [17, 19, 21, 23], - [25, 27, 29, 31], - [33, 35, 37, 39], - ] - - # Output: - output = [ - [0, 5, 7], - [0, 11, 13], - [0, 0, 0], - [25, 27, 29], - [35, 37, 39], - ] - - # Output padding: - output_padding = [ - [1, 0, 0], - [1, 0, 0], - [1, 1, 1], - [0, 0, 0], - [0, 0, 0], - ] - - Args: - selected_pos: a 0/1 2D tf.int32 tensor of shape [batch, time]. - values: a list of tensors, the rank of each is at least rank=2. [batch, - time, ...]. - k: a scalar tf.int32 tensor or a Python int. On TPU, k must be a - compile-time constant. - num_devices: number of TPU devices used in xla_sharding SPMD. - - Returns: - A tuple (output, padding). - - - output: a list of tensors of shape [batch, k, ...]. - - padding: a 2D 0/1 tensor of shape [batch, k], '1's are padded locations. - """ - global_batch, seq_len = py_utils.GetShape(selected_pos, 2) - if num_devices: - device_batch = global_batch // num_devices - else: - device_batch = global_batch - - for i in range(len(values)): - # Assert the first 2 dim of values[i] is [global_batch, seq_len] - values[i] = py_utils.HasShape(values[i], [global_batch, seq_len], 2) - # indices are 1-based for now, to distinguish between padding and selected - # locations. - indices = 1 + tf.range(tf.shape(values[0])[1], dtype=tf.int32) - # [1, seq_len] - indices = tf.expand_dims(indices, axis=0) - - # if 0, the position is not selected. - # [1, seq_len] * [global_batch, seq_len] => [global_batch, t] - # -- topk --> [global_batch, k] - topk_indices, _ = tf.math.top_k( - indices * tf.cast(selected_pos, indices.dtype), k) - - # [global_batch, k], sorted in ascending order. - indices = tf.reverse(topk_indices, [-1]) - # [global_batch, k], padded positions are '1's. - padding = tf.cast(tf.equal(indices, 0), values[0].dtype) - padding = Split(padding, 0, num_devices) - - # [global_batch, k], zero_based_indices - mp_idx = tf.maximum(0, indices - 1) - mp_idx = Split(mp_idx, 0, num_devices) - - # [device_batch, k] - if num_devices > 1 and py_utils.use_tpu(): - mp_idx = xla_sharding.auto_to_manual_spmd_partition( - mp_idx, xla_sharding.get_op_sharding(mp_idx.op)) - # [device_batch, k, 1] - mp_idx = tf.expand_dims(mp_idx, -1) - - # [device_batch] - batch_ids = tf.range(device_batch, dtype=tf.int32) - # [device_batch, 1, 1] - batch_ids = tf.reshape(batch_ids, [device_batch, 1, 1]) - # [device_batch, k, 1] - batch_ids = tf.broadcast_to(batch_ids, [device_batch, k, 1]) - - # [device_batch, k, 2] - final_indices = tf.concat([batch_ids, mp_idx], axis=-1) - - output = [] - for v in values: - # Begin manually partition gather. - v = Split(v, 0, num_devices) - v_shape = v.shape.as_list() - if num_devices > 1 and py_utils.use_tpu(): - op_sharding = xla_sharding.get_op_sharding(v.op) - v = xla_sharding.auto_to_manual_spmd_partition(v, op_sharding) - # Returns [global_batch, k, ...] - v_out = tf.gather_nd(v, final_indices) - - if num_devices > 1 and py_utils.use_tpu(): - v_shape[1] = k - v_out = xla_sharding.manual_to_auto_spmd_partition( - v_out, op_sharding, full_shape=tf.TensorShape(v_shape)) - output.append(v_out) - - return output, padding - - -def GetSentenceEmbeddings(inputs, segment_id): - """Returns the average sentence embedding to gate by. - - Example:: - - inputs: - segment_id: - - Args: - inputs: G`SM Tensor. - segment_id: G`S Tensor. - - Returns: - sentence_embeddings: GSM Tensor that is an average of the input embeddings - per segment. - """ - reshaped_inputs = tf.reshape(inputs, [-1, inputs.shape[-1]]) - - # We set num_segments to a large value so that shape is known at compile time. - max_segments = py_utils.GetShape(reshaped_inputs)[0] - # We change the padding to be max_segments - 1 instead of 0 because - # tf.math.unsorted_segment_mean because it only accepts values between 1 and - # max_segments. - modified_segment_id = tf.cast( - segment_id + max_segments * tf.cast( - tf.equal(segment_id, 0), dtype=tf.dtypes.as_dtype(segment_id.dtype)) - - 1, - dtype=tf.int32) - reshaped_segment_id = tf.reshape(modified_segment_id, [-1]) - - # Takes the mean of all segments, w/ 0s for the padding. - params = tf.concat([ - tf.math.unsorted_segment_mean(reshaped_inputs, reshaped_segment_id, - max_segments)[:-1], - tf.zeros([1, reshaped_inputs.shape[-1]], dtype=reshaped_inputs.dtype) - ], - axis=0) - raw_sentence_embeddings = tf.gather(params, modified_segment_id) - - # sentence_embedding: - sentence_embeddings = tf.reshape(raw_sentence_embeddings, inputs.shape) - - return sentence_embeddings - - -def SentenceTop2Gating(w, - inputs, - paddings, - segment_id, - num_devices, - experts_dim, - expert_capacity_dim, - local_dispatch, - fprop_dtype, - use_xla_sharding=True, - second_expert_policy='all', - second_expert_threshold=0.0, - legacy_mtf_behavior=True, - embedding_type='sentence', - capacity_factor=None): - """Computes Top-2 sentence gating for Mixture-of-Experts. - - Instead of using the each token, this function uses embedding_type to return a - sentence-wise embedding to create dispatch and combine tensors that gate - the entire sentence. - - See SentenceTop2GatingOnLogits for more details. - - Note that for local_dispatch original batch BLM is reshaped into GSM, each - group `g = 0...G-1` is being dispatched independently. - - Args: - w: gating weights for each experts. - inputs: G`SM Tensor. - paddings: G`S Tensor. - segment_id: G`SM Tensor used for differentiating different sentences in an - input example. - num_devices: number of MoE devices for local dispatch - experts_dim: number of experts. - expert_capacity_dim: number of examples per minibatch(group) per expert. - Each example is typically a vector of size input_dim, representing - embedded token or an element of Transformer layer output. - local_dispatch: whether dispatch is local to the group (G dim) - fprop_dtype: activations datatype to use. - use_xla_sharding: bool, True if this function is used for the xla_sharding - case. - second_expert_policy: 'all' or 'random', we optionally 'random'-ize dispatch - to second-best expert proportional to (weight / second_expert_threshold). - second_expert_threshold: threshold for probability normalization for - second_expert_policy == 'random'. - legacy_mtf_behavior: True for legacy behavior with no re-normalization of - expert assignment weights if we go over capacity or randomly decide to not - dispatch to second expert. - embedding_type: 'sentence' by default. Options: 'sentence'. Setting this - option calls GetSentenceEmbeddings. - capacity_factor: if set, increases expert_capacity_dim to at least - (group_size * capacity_factor) / experts_dim where `group_size` is the - size of G dimension of `inputs`. If the value of expert_capacity_dim is - already big enough no change is made. - - Returns: - A tuple (dispatch_tensor, combine_tensor, aux_loss). - - - dispatch_tensor: G`SEC Tensor, scattering/dispatching inputs to - experts. - - combine_tensor: G`SEC Tensor. - combining expert outputs. - - aux_loss: auxiliary loss, equalizing the expert assignment ratios. - """ - assert embedding_type in ['sentence'] - orig_inputs = inputs - - if not local_dispatch: - inputs = tf.reshape(inputs, [1, inputs.shape[0] * inputs.shape[1], -1]) - if paddings is not None: - paddings = tf.reshape(paddings, [1, -1]) - - if embedding_type == 'sentence': - sentence_embeddings = GetSentenceEmbeddings(inputs, segment_id) - - logits = tf.einsum('GSM,ME->GSE', sentence_embeddings, w) - aux_loss, combine_tensor, dispatch_tensor = Top2GatingOnLogits( - sentence_embeddings, paddings, logits, num_devices, experts_dim, - expert_capacity_dim, fprop_dtype, use_xla_sharding, second_expert_policy, - second_expert_threshold, legacy_mtf_behavior, capacity_factor) - - if not local_dispatch: - dispatch_tensor = tf.reshape( - dispatch_tensor, orig_inputs.shape[:2] + dispatch_tensor.shape[2:]) - combine_tensor = tf.reshape( - combine_tensor, orig_inputs.shape[:2] + combine_tensor.shape[2:]) - - return py_utils.NestedMap( - combine_tensor=combine_tensor, - dispatch_tensor=dispatch_tensor, - aux_loss=aux_loss) - - -def TaskTop2Gating(w, - inputs, - paddings, - task_embeddings, - num_devices, - experts_dim, - expert_capacity_dim, - local_dispatch, - fprop_dtype, - use_xla_sharding=True, - second_expert_policy='all', - second_expert_threshold=0.0, - legacy_mtf_behavior=True): - """Computes Top-2 sentence gating for Mixture-of-Experts. - - Instead of using the each token, this function uses embedding_type to return a - sentence-wise embedding to create dispatch and combine tensors that gate - the entire sentence. - - See SentenceTop2GatingOnLogits for more details. - - Note that for local_dispatch original batch BLM is reshaped into GSM, each - group `g = 0...G-1` is being dispatched independently. - - Args: - w: gating weights for each experts. - inputs: G`SM Tensor. - paddings: G`S Tensor. - task_embeddings: G`SM Tensor. - num_devices: number of MoE devices for local dispatch - experts_dim: number of experts. - expert_capacity_dim: number of examples per minibatch(group) per expert. - Each example is typically a vector of size input_dim, representing - embedded token or an element of Transformer layer output. - local_dispatch: whether dispatch is local to the group (G dim) - fprop_dtype: activations datatype to use. - use_xla_sharding: bool, True if this function is used for the xla_sharding - case. - second_expert_policy: 'all' or 'random', we optionally 'random'-ize dispatch - to second-best expert proportional to (weight / second_expert_threshold). - second_expert_threshold: threshold for probability normalization for - second_expert_policy == 'random'. - legacy_mtf_behavior: True for legacy behavior with no re-normalization of - expert assignment weights if we go over capacity or randomly decide to not - dispatch to second expert. - - Returns: - A tuple (dispatch_tensor, combine_tensor, aux_loss): - - - dispatch_tensor: G`SEC Tensor, scattering/dispatching inputs to - experts. - - combine_tensor: G`SEC Tensor. - combining expert outputs. - - aux_loss: auxiliary loss, equalizing the expert assignment ratios. - """ - orig_inputs = inputs - if not local_dispatch: - inputs = tf.reshape(inputs, [1, inputs.shape[0] * inputs.shape[1], -1]) - task_embeddings = tf.reshape( - task_embeddings, - [1, task_embeddings.shape[0] * task_embeddings.shape[1], -1]) - if paddings is not None: - paddings = tf.reshape(paddings, [1, -1]) - - logits = tf.einsum('GSM,ME->GSE', task_embeddings, w) - aux_loss, combine_tensor, dispatch_tensor = Top2GatingOnLogits( - task_embeddings, paddings, logits, num_devices, experts_dim, - expert_capacity_dim, fprop_dtype, use_xla_sharding, second_expert_policy, - second_expert_threshold, legacy_mtf_behavior) - - if not local_dispatch: - dispatch_tensor = tf.reshape( - dispatch_tensor, orig_inputs.shape[:2] + dispatch_tensor.shape[2:]) - combine_tensor = tf.reshape( - combine_tensor, orig_inputs.shape[:2] + combine_tensor.shape[2:]) - - return py_utils.NestedMap( - combine_tensor=combine_tensor, - dispatch_tensor=dispatch_tensor, - aux_loss=aux_loss) diff --git a/lingvo/core/multitask_model.py b/lingvo/core/multitask_model.py deleted file mode 100644 index ab4876d9..00000000 --- a/lingvo/core/multitask_model.py +++ /dev/null @@ -1,127 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Multitask models.""" - -from lingvo import compat as tf -from lingvo.core import base_model -from lingvo.core import py_utils - - -class SharedEncoderModel(base_model.MultiTaskModel): - """Multitask model that shares encoder between tasks.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('encoder_to_share', None, - 'The task name whose encoder should be shared.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.encoder_to_share in self.task_names - - # Assign the encoder from p.encoder_to_share task to all other tasks. - encoder = self.GetTask(p.encoder_to_share).encoder - for name in self.task_names: - if name != p.encoder_to_share: - task = self.GetTask(name) - assert 'encoder' not in task.children - task.AddChild('encoder', encoder) - - def _CreateChildrenVariables(self): - # Ensure p.encoder_to_share is created first. - task_name = self.params.encoder_to_share - with tf.name_scope(self.params.name): - if self.params.task_name_var_scope: - with tf.variable_scope(task_name): - self.GetTask(task_name).InstantiateVariables() - else: - self.GetTask(task_name).InstantiateVariables() - super()._CreateChildrenVariables() - - -class SharedEncoderDecoderModel(base_model.MultiTaskModel): - """Multitask model that shares both encoder and decoder between tasks.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('encoder_to_share', None, - 'The task name whose encoder should be shared.') - p.Define('decoder_to_share', None, - 'The task name whose decoder should be shared.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.encoder_to_share in self.task_names - assert p.decoder_to_share in self.task_names - - # Assign the encoder from p.encoder_to_share task to all other tasks, and - # assign the decoder from p.decoder_to_share task to all other tasks. - encoder = self.GetTask(p.encoder_to_share).encoder - decoder = self.GetTask(p.decoder_to_share).decoder - for name in self.task_names: - - if name != p.encoder_to_share: - task = self.GetTask(name) - assert 'encoder' not in task.children - task.AddChild('encoder', encoder) - - if name != p.decoder_to_share: - task = self.GetTask(name) - assert 'decoder' not in task.children - task.AddChild('decoder', decoder) - - -class RegExSharedVariableModel(base_model.MultiTaskModel): - """Multitask models that share variables across different tasks. - - Note, do NOT use this model unless you know exactly what you are trying to do - and you have verified that it indeed achieves what you would have expected. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'variable_renaming_rules', None, - 'A list/tuple of variable renaming rules. Each element in the' - ' list is a pair of strings. The first element is a regex' - ' expression while the second element is a python format string.') - return p - - def __init__(self, params): - # Enable variable sharing. - with py_utils.OpportunisticVariableReuseScope(): - with py_utils.VariableRenameScope(params.variable_renaming_rules): - super().__init__(params) - - def InstantiateVariables(self): - # Enable variable sharing. - with py_utils.OpportunisticVariableReuseScope(): - with py_utils.VariableRenameScope(self.params.variable_renaming_rules): - super().InstantiateVariables() - - def ConstructFPropBPropGraph(self): - # We need to override this since constructing the BPropGraph - # creates slot variables. - with py_utils.OpportunisticVariableReuseScope(): - with py_utils.VariableRenameScope(self.params.variable_renaming_rules): - super().ConstructFPropBPropGraph() diff --git a/lingvo/core/multitask_model_test.py b/lingvo/core/multitask_model_test.py deleted file mode 100644 index 3279f360..00000000 --- a/lingvo/core/multitask_model_test.py +++ /dev/null @@ -1,138 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for multitask_model.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_layer -from lingvo.core import base_model -from lingvo.core import base_model_params -from lingvo.core import hyperparams -from lingvo.core import multitask_model -from lingvo.core import py_utils -from lingvo.core import test_utils - - -class MultiTaskModelTest(test_utils.TestCase): - - class _TestTask(base_model.BaseTask): - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'test_task' - p.encoder = base_layer.BaseLayer.Params() - p.encoder.name = 'enc' - p.decoder = base_layer.BaseLayer.Params() - p.decoder.name = 'dec' - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.encoder: - self.CreateChild('encoder', p.encoder) - if p.decoder: - self.CreateChild('decoder', p.decoder) - - def testSharedEncoderModel(self): - p = multitask_model.SharedEncoderModel.Params() - p.name = 'test' - p.encoder_to_share = 'p0' - - p0 = MultiTaskModelTest._TestTask.Params() - p1 = MultiTaskModelTest._TestTask.Params() - p1.encoder = None - - p.input = base_model_params.MultiTaskModelParams().Train() - p.input.Define('p0', base_input_generator.BaseInputGenerator.Params(), '') - p.input.Define('p1', base_input_generator.BaseInputGenerator.Params(), '') - p.task_params = hyperparams.Params() - p.task_params.Define('p0', p0, '') - p.task_params.Define('p1', p1, '') - p.task_probs = hyperparams.Params() - p.task_probs.Define('p0', 0.5, '') - p.task_probs.Define('p1', 0.5, '') - - model = p.Instantiate() - self.assertEqual(model.p0.encoder, model.p1.encoder) - - def testSharedEncoderDecoderModel(self): - p = multitask_model.SharedEncoderDecoderModel.Params() - p.name = 'test' - p.encoder_to_share = 'p0' - p.decoder_to_share = 'p0' - - p0 = MultiTaskModelTest._TestTask.Params() - p1 = MultiTaskModelTest._TestTask.Params() - p1.encoder = None - p1.decoder = None - - p.input = base_model_params.MultiTaskModelParams().Train() - p.input.Define('p0', base_input_generator.BaseInputGenerator.Params(), '') - p.input.Define('p1', base_input_generator.BaseInputGenerator.Params(), '') - p.task_params = hyperparams.Params() - p.task_params.Define('p0', p0, '') - p.task_params.Define('p1', p1, '') - p.task_probs = hyperparams.Params() - p.task_probs.Define('p0', 0.5, '') - p.task_probs.Define('p1', 0.5, '') - - model = p.Instantiate() - self.assertEqual(model.p0.encoder, model.p1.encoder) - self.assertEqual(model.p0.decoder, model.p1.decoder) - - class _TestTaskWithVars(base_model.BaseTask): - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'test_task' - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - pc = py_utils.WeightParams(shape=[10, 10], dtype=tf.float32) - self.CreateVariable('weight', pc) - - def testRegExSharedVariableModel(self): - p = multitask_model.RegExSharedVariableModel.Params() - p.name = 'test' - p.variable_renaming_rules = [('p./(.*)', 'shared/%s')] - - p0 = MultiTaskModelTest._TestTaskWithVars.Params() - p1 = MultiTaskModelTest._TestTaskWithVars.Params() - - p.input = base_model_params.MultiTaskModelParams().Train() - p.input.Define('p0', base_input_generator.BaseInputGenerator.Params(), '') - p.input.Define('p1', base_input_generator.BaseInputGenerator.Params(), '') - - p.task_params = hyperparams.Params() - p.task_params.Define('p0', p0, '') - p.task_params.Define('p1', p1, '') - p.task_probs = hyperparams.Params() - - p.task_probs.Define('p0', 0.5, '') - p.task_probs.Define('p1', 0.5, '') - - model = p.Instantiate() - all_vars = model.vars - self.assertEqual('shared/test_task/weight/var:0', all_vars.p0.weight.name) - self.assertEqual('shared/test_task/weight/var:0', all_vars.p1.weight.name) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/BUILD b/lingvo/core/ops/BUILD deleted file mode 100644 index 960d3e76..00000000 --- a/lingvo/core/ops/BUILD +++ /dev/null @@ -1,518 +0,0 @@ -load( - "//lingvo:lingvo.bzl", - "custom_kernel_library", - "gen_op_cclib", - "gen_op_pylib", - "lingvo_cc_library", - "lingvo_cc_test", - "lingvo_cc_test_library", - "lingvo_proto_cc", - "lingvo_proto_py", -) - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -cc_library( - name = "x_ops_helper", - hdrs = ["x_ops_helper.h"], -) - -# Op definitions to be used from python. -gen_op_cclib( - name = "x_ops", - srcs = ["x_ops.cc"], - deps = [ - ":x_ops_helper", - # Implicit tensorflow C++ proto dependency. - ], -) - -gen_op_pylib( - name = "py_x_ops", - srcs = ["__init__.py"], - cc_lib_name = "x_ops", - kernel_deps = [ - ":op_kernels", - ], - py_deps = [ - "//lingvo:compat", - ], - visibility = ["//visibility:private"], -) - -py_library( - name = "ops", - srcs = ["__init__.py"], - srcs_version = "PY3", - deps = [ - ":py_x_ops", # buildcleaner: keep - "//lingvo:compat", - ], -) - -# Op kernel deps. -cc_library( - name = "op_kernels", - deps = [ - ":assert_kernels", - ":beam_search_step_op_kernels", - ":best_step_op_kernels", - ":functional_ops_kernels", - ":generic_input_op_kernels", - ":mass_op", - ":ml_perf_subword_op", - ":pack_ops", - ":preconditioner_op_kernels", - ":random_ops_kernels", - ":static_map_op", - ":tokenizer_ops_kernels", - ], -) - -lingvo_cc_library( - name = "rope", - hdrs = ["rope.h"], - deps = [ - # Implicit rope dependency. - ], -) - -lingvo_cc_library( - name = "ascii_tokenizer", - srcs = ["ascii_tokenizer.cc"], - hdrs = ["ascii_tokenizer.h"], -) - -lingvo_cc_library( - name = "simple_vocab", - srcs = ["simple_vocab.cc"], - hdrs = ["simple_vocab.h"], -) - -custom_kernel_library( - name = "ml_perf_subword_op", - srcs = ["ml_perf_subword_op.cc"], - hdrs = ["ml_perf_subword_op.h"], - op_def_lib = [":x_ops"], - deps = [ - "@icu//:common", - ], -) - -py_test( - name = "simple_vocab_test", - srcs = ["simple_vocab_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo/core:test_utils", - # Implicit tensorflow dependency. - ], -) - -lingvo_cc_library( - name = "record", - srcs = [ - "chain_record_yielder.cc", - "record_batcher.cc", - "record_debug.cc", - "record_yielder.cc", - "sequential_record_yielder.cc", - "weighted_mix_record_yielder.cc", - ], - hdrs = [ - "chain_record_yielder.h", - "record_batcher.h", - "record_yielder.h", - "sequential_record_yielder.h", - "weighted_mix_record_yielder.h", - ], - deps = [ - ":rope", - ":versioned_file_set_proto_cc", - # Implicit absl.synchronization dependency. - ], -) - -lingvo_cc_test( - name = "record_yielder_test", - srcs = ["record_yielder_test.cc"], - deps = [ - ":input_common", - ":record", - ":yielder_test_helper", - ], -) - -lingvo_cc_test( - name = "weighted_mix_record_yielder_test", - srcs = ["weighted_mix_record_yielder_test.cc"], - deps = [ - ":input_common", - ":record", - ":yielder_test_helper", - ], -) - -lingvo_cc_test( - name = "chain_record_yielder_test", - srcs = ["chain_record_yielder_test.cc"], - deps = [ - ":input_common", - ":record", - ":yielder_test_helper", - ], -) - -lingvo_cc_test( - name = "record_batcher_test", - srcs = ["record_batcher_test.cc"], - deps = [ - ":input_common", - ":record", - ], -) - -lingvo_cc_library( - name = "input_common", - srcs = ["input_common.cc"], - hdrs = ["input_common.h"], - deps = [ - ":record", - ], -) - -lingvo_cc_test_library( - name = "yielder_test_helper", - srcs = [ - "yielder_test_helper.cc", - ], - hdrs = [ - "yielder_test_helper.h", - ], - deps = [ - ":record", - ], -) - -lingvo_cc_library( - name = "text_packing", - srcs = ["text_packing.cc"], - hdrs = ["text_packing.h"], -) - -lingvo_cc_test( - name = "text_packing_test", - srcs = ["text_packing_test.cc"], - deps = [ - ":text_packing", - ], -) - -###################### Op kernel implementations. - -custom_kernel_library( - name = "static_map_op", - srcs = ["static_map_op.cc"], - op_def_lib = [":x_ops"], -) - -py_test( - name = "static_map_op_test", - srcs = ["static_map_op_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:test_utils", - ], -) - -custom_kernel_library( - name = "assert_kernels", - srcs = ["assert_kernels.cc"], - op_def_lib = [":x_ops"], -) - -py_test( - name = "assert_ops_test", - srcs = ["assert_ops_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo/core:test_utils", - # Implicit tensorflow dependency. - ], -) - -custom_kernel_library( - name = "beam_search_step_op_kernels", - srcs = ["beam_search_step_op_kernels.cc"], - hdrs = ["beam_search_step_op_kernels.h"], - op_def_lib = [":x_ops"], - deps = [ - ":hyps_proto_cc", - ":simple_vocab", - ], -) - -lingvo_cc_test( - name = "beam_search_step_op_top_k_test", - srcs = ["beam_search_step_op_top_k_test.cc"], - deps = [ - ":beam_search_step_op_kernels", - ], -) - -py_test( - name = "beam_search_step_op_test", - srcs = ["beam_search_step_op_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":hyps_py_pb2", - ":ops", - # Implicit python proto dependency. - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - # Implicit tensorflow dependency. - ], -) - -custom_kernel_library( - name = "best_step_op_kernels", - srcs = ["best_step_op_kernels.cc"], - op_def_lib = [":x_ops"], - deps = [ - # Implicit tensorflow C++ proto dependency. - ], -) - -py_test( - name = "best_step_op_test", - srcs = ["best_step_op_test.py"], - data = [ - "//lingvo/core/ops/testdata:best_step_testdata", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - # Implicit tensorflow dependency. - ], -) - -custom_kernel_library( - name = "functional_ops_kernels", - srcs = ["functional_ops_kernels.cc"], - op_def_lib = [":x_ops"], - deps = [ - # Implicit absl.synchronization dependency. - ], -) - -py_test( - name = "functional_ops_test", - srcs = ["functional_ops_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - # Implicit tensorflow dependency. - # Implicit tensorflow python framework_for_generated_wrappers dependency. - ], -) - -custom_kernel_library( - name = "generic_input_op_kernels", - srcs = ["generic_input_op_kernels.cc"], - op_def_lib = [":x_ops"], - deps = [":input_common"], -) - -custom_kernel_library( - name = "random_ops_kernels", - srcs = ["random_ops_kernels.cc"], - op_def_lib = [":x_ops"], - deps = [ - # Implicit absl.synchronization dependency. - ], -) - -py_test( - name = "random_ops_test", - srcs = ["random_ops_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit tensorflow dependency. - ], -) - -lingvo_cc_library( - name = "preconditioner_captain", - srcs = ["preconditioner_captain.cc"], - hdrs = ["preconditioner_captain.h"], - deps = [ - # Implicit tensorflow core_cpu dependency. - # Implicit tensorflow C++ proto dependency. - ], -) - -custom_kernel_library( - name = "preconditioner_op_kernels", - srcs = ["preconditioner_op_kernels.cc"], - op_def_lib = [":x_ops"], - deps = [ - ":preconditioner_captain", - ], -) - -py_test( - name = "preconditioner_op_kernels_test", - srcs = ["preconditioner_op_kernels_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - # Implicit tensorflow dependency. - ], -) - -lingvo_cc_library( - name = "tokenizer_op_headers", - hdrs = ["tokenizer_op_headers.h"], -) - -custom_kernel_library( - name = "tokenizer_ops_kernels", - srcs = ["tokenizer_ops_kernels.cc"], - hdrs = ["tokenizer_op_headers.h"], - op_def_lib = [":x_ops"], - deps = [ - ":ascii_tokenizer", - ":simple_vocab", - ], -) - -py_test( - name = "tokenizer_ops_test", - srcs = ["tokenizer_ops_test.py"], - data = [ - "//lingvo/core/ops/testdata:bpe_codes_vocab", - "//lingvo/core/ops/testdata:bpe_words_vocab", - "//lingvo/core/ops/testdata:mlperf_vocab", - "//lingvo/core/ops/testdata:test_ngrams", - "//lingvo/core/ops/testdata:test_vocab", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - # Implicit tensorflow dependency. - ], -) - -custom_kernel_library( - name = "pack_ops", - srcs = ["pack_ops.cc"], - op_def_lib = [":x_ops"], - deps = [ - ":text_packing", - # Implicit absl.container.flat_hash_map dependency. - # Implicit absl.strings dependency. - # Implicit absl.synchronization dependency. - ], -) - -py_test( - name = "pack_ops_test", - srcs = ["pack_ops_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - # Implicit tensorflow dependency. - ], -) - -custom_kernel_library( - name = "mass_op", - srcs = ["mass_op.cc"], - op_def_lib = [":x_ops"], -) - -py_test( - name = "mass_op_test", - srcs = ["mass_op_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - # Implicit tensorflow dependency. - ], -) - -lingvo_proto_cc( - name = "hyps_proto", - src = "hyps.proto", -) - -lingvo_proto_py( - name = "hyps_py_pb2", - src = "hyps.proto", - deps = [":hyps_proto"], -) - -lingvo_proto_cc( - name = "record_proto", - src = "record.proto", - deps = [ - # Implicit tensorflow proto dependency. - ], -) - -lingvo_proto_cc( - name = "versioned_file_set_proto", - src = "versioned_file_set.proto", -) - -lingvo_proto_py( - name = "versioned_file_set_py_pb2", - src = "versioned_file_set.proto", - deps = [":versioned_file_set_proto"], -) - -lingvo_proto_py( - name = "record_py_pb2", - src = "record.proto", - deps = [":record_proto"], -) diff --git a/lingvo/core/ops/__init__.py b/lingvo/core/ops/__init__.py deleted file mode 100644 index e7ca7006..00000000 --- a/lingvo/core/ops/__init__.py +++ /dev/null @@ -1,67 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Operations.""" - -from lingvo import compat as tf - -# Try static linking: -try: - from lingvo.core.ops import gen_x_ops # pylint: disable=g-import-not-at-top -except ImportError: - gen_x_ops = tf.load_op_library( - tf.resource_loader.get_path_to_datafile('x_ops.so')) - -# Set gen_x_ops function module so sphinx generates documentation. -for v in gen_x_ops.__dict__.values(): - try: - v.__module__ = 'lingvo.core.ops' - except: # pylint: disable=bare-except - pass - -assert_shape_match = gen_x_ops.assert_shape_match -assert_same_dim0 = gen_x_ops.assert_same_dim0 -random_permutation_sequence = gen_x_ops.random_permutation_sequence - -best_step = gen_x_ops.best_step - -beam_search_step = gen_x_ops.beam_search_step -top_k_terminated_hyps = gen_x_ops.top_k_terminated_hyps -unpack_hyp = gen_x_ops.unpack_hyp -hyps_from_beam_search_outs = gen_x_ops.hyps_from_beam_search_outs - -cached_call = gen_x_ops.cached_call - -vocab_token_to_id = gen_x_ops.vocab_token_to_id -vocab_id_to_token = gen_x_ops.vocab_id_to_token -token_in_vocab = gen_x_ops.token_in_vocab -ascii_to_token_id = gen_x_ops.ascii_to_token_id -str_to_vocab_tokens = gen_x_ops.str_to_vocab_tokens -id_to_ascii = gen_x_ops.id_to_ascii -ml_perf_subword_id_to_string = gen_x_ops.ml_perf_subword_id_to_string -ngram_id_to_token = gen_x_ops.ngram_id_to_token -bpe_ids_to_words = gen_x_ops.bpe_ids_to_words -bpe_words_to_ids = gen_x_ops.bpe_words_to_ids - -static_map_string_int = gen_x_ops.static_map_string_int -static_map_int_string = gen_x_ops.static_map_int_string -static_map_int_int = gen_x_ops.static_map_int_int - -get_preconditioners = gen_x_ops.get_preconditioners -compute_preconditioners = gen_x_ops.compute_preconditioners - -pack_sequences = gen_x_ops.pack_sequences -apply_packing = gen_x_ops.apply_packing -mass = gen_x_ops.mass diff --git a/lingvo/core/ops/ascii_tokenizer.cc b/lingvo/core/ops/ascii_tokenizer.cc deleted file mode 100644 index 661ee878..00000000 --- a/lingvo/core/ops/ascii_tokenizer.cc +++ /dev/null @@ -1,180 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/ascii_tokenizer.h" - -#include -#include -#include - -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/logging.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -const string& FindOrDie(const std::unordered_map& m, int32 k) { - const auto it = m.find(k); - CHECK(it != m.end()); - return it->second; -} - -const int32 kUnkId = 0; -const int32 kSOSId = 1; -const int32 kEOSId = 2; -const int32 kEOWId = 3; -const int32 kNoiseId = 4; -const int32 kEpsilonId = 73; -const int32 kTextOnlyId = 74; -const int32 kSORWId = 75; // indicator for start of rare word -const int32 kMaxTokenId = 75; - -struct CharTokenizer { - std::unordered_map token_to_id; - std::unordered_map id_to_token; - - string epsilon_token; - string unk_token; - string noise_token; - string eos_token; - string sos_token; - string text_only_token; - string sorw_token; - - string IdToToken(int32 id) const { - const auto it = id_to_token.find(id); - if (it != id_to_token.end()) return it->second; - return unk_token; - } - - int32 TokenToId(const string& tok) const { - const auto it = token_to_id.find(tok); - if (it != token_to_id.end()) return it->second; - return kUnkId; - } -}; - -const CharTokenizer* CreateTokenizer() { - CharTokenizer* ct = new CharTokenizer(); - ct->id_to_token = {{0, ""}, {1, ""}, {2, ""}, - {3, " "}, {4, ""}, {5, "a"}, - {6, "b"}, {7, "c"}, {8, "d"}, - {9, "e"}, {10, "f"}, {11, "g"}, - {12, "h"}, {13, "i"}, {14, "j"}, - {15, "k"}, {16, "l"}, {17, "m"}, - {18, "n"}, {19, "o"}, {20, "p"}, - {21, "q"}, {22, "r"}, {23, "s"}, - {24, "t"}, {25, "u"}, {26, "v"}, - {27, "w"}, {28, "x"}, {29, "y"}, - {30, "z"}, {31, "."}, {32, "\'"}, - {33, "-"}, {34, ":"}, {35, "!"}, - {36, "~"}, {37, "`"}, {38, ";"}, - {39, "0"}, {40, "1"}, {41, "2"}, - {42, "3"}, {43, "4"}, {44, "5"}, - {45, "6"}, {46, "7"}, {47, "8"}, - {48, "9"}, {49, "\""}, {50, "#"}, - {51, "$"}, {52, "%"}, {53, "&"}, - {54, "("}, {55, ")"}, {56, "*"}, - {57, "+"}, {58, ","}, {59, "/"}, - {60, "<"}, {61, "="}, {62, ">"}, - {63, "?"}, {64, "@"}, {65, "["}, - {66, "\\"}, {67, "]"}, {68, "^"}, - {69, "_"}, {70, "{"}, {71, "|"}, - // 73 - {72, "}"}, {73, ""}, {74, ""}, - {75, ""}}; - // What is a "block" for an RNN-T? It is presumably what is emitted - // by the RNN-T model to signal that it is time to switch from - // prediction to encoder. - - // kEpsilonWord: end-of-block for neural transducer. - for (const std::pair& p : ct->id_to_token) { - CHECK_LE(p.first, kMaxTokenId); - CHECK(ct->token_to_id.insert({p.second, p.first}).second); - } - ct->unk_token = FindOrDie(ct->id_to_token, kUnkId); - ct->noise_token = FindOrDie(ct->id_to_token, kNoiseId); - ct->epsilon_token = FindOrDie(ct->id_to_token, kEpsilonId); - ct->sos_token = FindOrDie(ct->id_to_token, kSOSId); - ct->eos_token = FindOrDie(ct->id_to_token, kEOSId); - ct->sorw_token = FindOrDie(ct->id_to_token, kSORWId); - ct->text_only_token = FindOrDie(ct->id_to_token, kTextOnlyId); - return ct; -} - -const CharTokenizer* GetTokenizer() { - static const CharTokenizer* tokenizer = CreateTokenizer(); - return tokenizer; -} - -} // namespace - -string AsciiTokenizer::ConvertString(const string& transcript) { - string result = transcript; - std::transform(result.begin(), result.end(), result.begin(), ::tolower); - return result; -} - -int32 AsciiTokenizer::NumTokens() { return kMaxTokenId + 1; } - -std::vector AsciiTokenizer::StringToIds(const string& label) { - const CharTokenizer* tokenizer = GetTokenizer(); - const string converted = ConvertString(label); - const StringPiece converted_view(converted); - - std::vector ids; - const std::vector> special_token_ids{ - {tokenizer->unk_token, kUnkId}, - {tokenizer->noise_token, kNoiseId}, - {tokenizer->sos_token, kSOSId}, - {tokenizer->eos_token, kEOSId}, - {tokenizer->epsilon_token, kEpsilonId}, - {tokenizer->text_only_token, kTextOnlyId}, - {tokenizer->sorw_token, kSORWId}, - }; - - for (int i = 0; i < converted.size(); ++i) { - bool is_special_token = false; - for (const auto& token_id : special_token_ids) { - if (str_util::StartsWith(converted_view.substr(i), token_id.first)) { - ids.push_back(token_id.second); - i += token_id.first.size() - 1; - is_special_token = true; - break; - } - } - if (!is_special_token) { - ids.push_back(tokenizer->TokenToId(string(1, converted[i]))); - } - } - return ids; -} - -std::vector AsciiTokenizer::IdToStrings(const std::vector& ids) { - const CharTokenizer* tokenizer = GetTokenizer(); - std::vector out_strings(ids.size()); - for (int i = 0; i < ids.size(); ++i) { - out_strings[i] = tokenizer->IdToToken(ids[i]); - } - return out_strings; -} - -string AsciiTokenizer::JoinLabels(const std::vector& labels) { - return str_util::Join(labels, ""); -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/ascii_tokenizer.h b/lingvo/core/ops/ascii_tokenizer.h deleted file mode 100644 index 1f19be75..00000000 --- a/lingvo/core/ops/ascii_tokenizer.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_ASCII_TOKENIZER_H_ -#define LINGVO_CORE_OPS_ASCII_TOKENIZER_H_ - -#include -#include - -#include "tensorflow/core/platform/types.h" - -namespace tensorflow { -namespace lingvo { - -// A static simple tokenizer that maps a small vocabulary of character -// tokens for (lower case) letters, digits, and punctuation symbols. -class AsciiTokenizer { - public: - // Convert string to a set of graphemes. - static string ConvertString(const string& transcript); - - // Returns the number of tokens. - static int32 NumTokens(); - - // Splits 'label' into tokens and returns their token ids. - static std::vector StringToIds(const string& label); - - // Convert 'ids' back into tokens. - static std::vector IdToStrings(const std::vector& ids); - - // Joins the token labels into a string. - static string JoinLabels(const std::vector& labels); -}; - -} // namespace lingvo -} // namespace tensorflow -#endif // LINGVO_CORE_OPS_ASCII_TOKENIZER_H_ diff --git a/lingvo/core/ops/assert_kernels.cc b/lingvo/core/ops/assert_kernels.cc deleted file mode 100644 index aca98e30..00000000 --- a/lingvo/core/ops/assert_kernels.cc +++ /dev/null @@ -1,113 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -static const int kUnknown = -1; - -class AssertShapeMatchOp : public OpKernel { - public: - explicit AssertShapeMatchOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("msg", &msg_)); - } - - ~AssertShapeMatchOp() override {} - - void Compute(OpKernelContext* ctx) override { - const Tensor& x = ctx->input(0); - const Tensor& y = ctx->input(1); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(x.shape()), - errors::InvalidArgument("x must be a vector.")); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(y.shape()), - errors::InvalidArgument("y must be a vector.")); - bool match = true; - if (x.NumElements() != y.NumElements()) { - match = false; - } else { - auto Tx = x.flat(); - auto Ty = y.flat(); - for (int i = 0; i < x.NumElements(); ++i) { - if ((Tx(i) != kUnknown) && (Ty(i) != kUnknown) && (Tx(i) != Ty(i))) { - match = false; - } - } - } - OP_REQUIRES(ctx, match, - errors::InvalidArgument(msg_, " mismatch shape: x=[", - x.SummarizeValue(10), "] y=[", - y.SummarizeValue(10), "]")); - } - - private: - string msg_; -}; -REGISTER_KERNEL_BUILDER(Name("AssertShapeMatch").Device(DEVICE_CPU), - AssertShapeMatchOp); - -#if GOOGLE_CUDA -REGISTER_KERNEL_BUILDER( - Name("AssertShapeMatch").Device(DEVICE_GPU).HostMemory("x").HostMemory("y"), - AssertShapeMatchOp); -#endif - -class AssertSameDim0Op : public OpKernel { - public: - explicit AssertSameDim0Op(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("msg", &msg_)); - } - - ~AssertSameDim0Op() override {} - - void Compute(OpKernelContext* ctx) override { - if (ctx->num_inputs() == 0) { - // A no-op for empty list of inputs. - return; - } - const auto& x = ctx->input(0); - OP_REQUIRES(ctx, !TensorShapeUtils::IsScalar(x.shape()), - errors::InvalidArgument(msg_, " 0-th input is a scalar.")); - const auto dim0 = x.dim_size(0); - for (int i = 1; i < ctx->num_inputs(); ++i) { - const auto& y = ctx->input(i); - OP_REQUIRES( - ctx, !TensorShapeUtils::IsScalar(y.shape()), - errors::InvalidArgument(msg_, " ", i, "-th input is a scalar.")); - OP_REQUIRES(ctx, dim0 == y.dim_size(0), - errors::InvalidArgument( - msg_, " ", i, "-th input has a different dim0: ", dim0, - " ", y.dim_size(0))); - } - } - - private: - string msg_; -}; -REGISTER_KERNEL_BUILDER(Name("AssertSameDim0").Device(DEVICE_CPU), - AssertSameDim0Op); - -#if GOOGLE_CUDA -REGISTER_KERNEL_BUILDER(Name("AssertSameDim0").Device(DEVICE_GPU), - AssertSameDim0Op); -#endif - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/assert_ops_test.py b/lingvo/core/ops/assert_ops_test.py deleted file mode 100644 index c8626f56..00000000 --- a/lingvo/core/ops/assert_ops_test.py +++ /dev/null @@ -1,66 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for assert_ops.""" - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_utils - -# Disable lint error: The name of ops.assert_shape_match triggers the -# lint which doesn't apply in this case. -# pylint:disable=g-error-prone-assert-raises - - -class AssertOpsTest(test_utils.TestCase): - - def testBasic(self): - with self.session(): - ops.assert_shape_match([10, 20, 30, 40], [-1, -1, -1, -1]).run() - ops.assert_shape_match([10, 20, 30, 40], [-1, 20, -1, -1]).run() - ops.assert_shape_match([10, 20, 30, 40], [-1, 20, -1, 40]).run() - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, - "Yo mismatch"): - ops.assert_shape_match([10, 20, 30, 40], [10, 20, 40], "Yo").run() - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, - "Foo mismatch"): - ops.assert_shape_match([10, 20, 30, 40], [8, 20, -1, 40], "Foo").run() - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, "mismatch"): - ops.assert_shape_match([10, 20, 30, 40], [10, 20, 30, 44]).run() - - def testSameBatchSize(self): - - def t(*args): - return tf.zeros(shape=args, dtype=tf.float32) - - with self.session(): - ops.assert_same_dim0([t(2, 3, 4), t(2, 3, 4)]).run() - ops.assert_same_dim0([t(2, 3), t(2, 8)]).run() - ops.assert_same_dim0([t(2, 3)]).run() - ops.assert_same_dim0([t(2, 3)] * 100).run() - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, "a scalar"): - ops.assert_same_dim0([t(), t(2, 3)]).run() - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, "a scalar"): - ops.assert_same_dim0([t(2, 3), t()]).run() - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, - "different dim0"): - ops.assert_same_dim0([t(2, 3), t(3, 2)]).run() - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, - "different dim0"): - ops.assert_same_dim0([t(2, 3)] * 10 + [t(3, 2)]).run() - - -if __name__ == "__main__": - tf.test.main() diff --git a/lingvo/core/ops/beam_search_step_op_kernels.cc b/lingvo/core/ops/beam_search_step_op_kernels.cc deleted file mode 100644 index 11253859..00000000 --- a/lingvo/core/ops/beam_search_step_op_kernels.cc +++ /dev/null @@ -1,1071 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/beam_search_step_op_kernels.h" - -#include - -#include "lingvo/core/ops/hyps.pb.h" -#include "lingvo/core/ops/simple_vocab.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/util/work_sharder.h" - -namespace tensorflow { -namespace lingvo { - -namespace { -constexpr int kNumWorkers = 8; -} // namespace - -namespace debug { -static string IdsToStr(const google::protobuf::RepeatedField& ids) { - return debug::IdsToStr(std::vector(ids.begin(), ids.end())); -} -} // namespace debug - -bool IdsMatchUpToIndex(const std::vector& cur_hyp_ids, - const std::vector& other_hyp_ids, const int index) { - CHECK_LE(index, cur_hyp_ids.size()); - CHECK_LE(index, other_hyp_ids.size()); - return std::equal(cur_hyp_ids.begin(), cur_hyp_ids.begin() + index, - other_hyp_ids.begin()); -} - -bool IsDuplicateHyp(const Hyp& cur_hyp, const Hyp& other_hyp, - const int epsilon_id) { - const std::vector& cur_hyp_ids = cur_hyp.prev_ids; - const std::vector& other_hyp_ids = other_hyp.prev_ids; - // Note word_id refers to id of current label, which could be grapheme, - // phoneneme, wordpiece, ectc. - if (cur_hyp.word_id == other_hyp.word_id) { - // If the cur step is the same (epsilon or otherwise), just need to compare - // prev ids which already has epsilons stripped. - return ( - cur_hyp_ids.size() == other_hyp_ids.size() && - IdsMatchUpToIndex(cur_hyp_ids, other_hyp_ids, other_hyp_ids.size())); - } else if (cur_hyp.word_id == epsilon_id) { - // If exactly one of the hyps has a cur step of epsilon, then need to - // compare that hyp's final prev id to other hyp's current step id, - // then compare the rest of the prev ids. - return ( - cur_hyp_ids.size() == other_hyp_ids.size() + 1 && - cur_hyp_ids[cur_hyp_ids.size() - 1] == other_hyp.word_id && - IdsMatchUpToIndex(cur_hyp_ids, other_hyp_ids, other_hyp_ids.size())); - } else if (other_hyp.word_id == epsilon_id) { - return (other_hyp_ids.size() == cur_hyp_ids.size() + 1 && - other_hyp_ids[other_hyp_ids.size() - 1] == cur_hyp.word_id && - IdsMatchUpToIndex(other_hyp_ids, cur_hyp_ids, cur_hyp_ids.size())); - } else { - // If the cur step is not the same for the two hyps and neither is an - // epsilon then this cannot be a duplicate hyp. - return false; - } -} - -float LogSumExp(float a, float b) { - const float m = std::max(a, b); - return m + std::log(std::exp(a - m) + std::exp(b - m)); -} - -#ifdef __AVX__ -// AVX version all_less_than. -bool all_less_than(const float* p, float threshold) { - __m256 kth_logp = _mm256_set1_ps(threshold); - __m256 logp_vec = _mm256_loadu_ps(p); - // Skip this 8 elements if all of them are worst than 'kth_logp'. - // 'OQ' in '_CMP_LE_OQ' means comparison against NaN fails - // quietly (no crash). - __m256 mask = _mm256_cmp_ps(logp_vec, kth_logp, _CMP_LT_OQ); - return _mm256_movemask_ps(mask) == 0xFF; -} -#endif - -// Given the current partial hypothesis in 'hyps' for all beams in a batch and -// the predicted next step scores 'scores', return the best scored 'k+m' -// hypotheses where the first 'k' hypotheses are used for search in the next -// step while the remaining 'm' hypotheses are kept as alternatives to increase -// diversity. 'eos_id' is the end of beam id of the target language. -// -// eos_in_topk is filled with true/false to indicate whether or not the eos -// symbol is among the topk candidate for a hyp. -void ComputeTopKPlusM(const std::vector& hyps, const Tensor& scores, - const int32 k, const int32 m, const int32 eos_id, - const int32 eoc_id, const int32 num_beams, - const float valid_eos_max_logit_delta, - const float local_eos_threshold, bool is_first_step, - bool is_last_decoder_step, const Tensor& is_last_chunk, - bool merge_paths, bool allow_empty_terminated_hyp, - // Note that this is functionally a bool, however - // vector is not safe to parallel write into - // since it's underlying storage is at the byte-level. - std::vector* eos_in_topk, std::vector* top_k, - std::vector* extra_m, std::vector* eos_hyps, - std::vector* terminal_syms) { - VLOG(1) << "Topk clear, num_beams: " << num_beams; - CHECK_EQ(hyps.size(), num_beams * k); - CHECK_GE(m, 0); - CHECK(eos_in_topk && top_k && extra_m && eos_hyps && terminal_syms); - CHECK_EQ(hyps.size(), scores.dim_size(0)); - CHECK_LT(eos_id, scores.dim_size(1)); - int hyps_size = hyps.size(); - eos_in_topk->clear(); - top_k->clear(); - extra_m->clear(); - eos_in_topk->resize(hyps_size); - eos_hyps->resize(hyps_size); - terminal_syms->resize(hyps_size); - static thread::ThreadPool* workers = - new thread::ThreadPool(Env::Default(), "topk", kNumWorkers); - const int num_ids = scores.dim_size(1); - const auto scores_matrix = scores.matrix(); - const int epsilon_id_for_path_merging = merge_paths ? eoc_id : -1; - std::vector< - TopK> - merged_topk_vec(num_beams, TopK( - m + k, epsilon_id_for_path_merging)); - // Each mutex is used to protect corresponding merged_topk_vec. - std::vector mu_vec(num_beams); - // The thread sharding is along the hyps_size. - Shard( - kNumWorkers, workers, hyps_size, num_ids, [&](int64 start, int64 limit) { - for (int32 hyp_id = start; hyp_id < limit; ++hyp_id) { - if (is_first_step && hyp_id >= num_beams) { - // For first step, we only consider the first hyp of each beam, as - // otherwise we will be continuing k identical hyps along the way. - continue; - } - // +1 to make sure that at least top-k hypotheses survive even with - // the special treatment for eos. +2 if we are also using eoc. - const int topk_size = eoc_id >= 0 ? k + 2 : k + 1; - TopK - topk(topk_size, epsilon_id_for_path_merging, eos_id, - is_last_decoder_step); - float bottom_of_topk = -INFINITY; - int32 id = 0; - const float current_global_score = hyps[hyp_id].global_score; - // TODO(xbing): Try AVX512 if it is supported by machine. -#ifdef __AVX__ - const int STRIDE = - sizeof(__m256) / - sizeof(std::result_of::type); - // We read STRIDE float values at a single iteration and compare - // them with this k-th best value. STRIDE - 1 not to read outside - // the row. - for (; id + STRIDE - 1 < num_ids; id += STRIDE) { - if (!all_less_than(&scores_matrix(hyp_id, id), - bottom_of_topk - current_global_score)) { - for (int i = 0; i < STRIDE; ++i) { - const float score = scores_matrix(hyp_id, id + i); - const float global_score = - current_global_score + score; - if (global_score >= bottom_of_topk) { - bottom_of_topk = - topk.Add({hyps[hyp_id].beam_id, hyp_id, id + i, score, - global_score, hyps[hyp_id].prev_ids}); - } - } - } - } - // Non-AVX code below handles the remaining elements. -#endif - for (; id != num_ids; ++id) { - const float score = scores_matrix(hyp_id, id); - const float global_score = current_global_score + score; - if (global_score >= bottom_of_topk) { - bottom_of_topk = - topk.Add({hyps[hyp_id].beam_id, hyp_id, id, score, - global_score, hyps[hyp_id].prev_ids}); - } - } - - auto entries = topk.Get(); - CHECK(!entries.empty()) << "No entries in TopK. This typically " << - "happens if your model is producing NaNs in the output."; - std::sort(entries.begin(), entries.end(), HigherScore()); - const float eos_score_threshold = - entries[0].global_score - valid_eos_max_logit_delta; - VLOG(3) << "Best_score=" << entries[0].global_score - << " eos_score_threshold=" << eos_score_threshold; - { - const int beam_id = hyps[hyp_id].beam_id; - mutex_lock l(mu_vec[beam_id]); - for (const auto& e : entries) { - if (e.word_id == eos_id) { - VLOG(3) << "EOS hyp score=" << e.global_score - << " toks=" << debug::IdsToStr(e.prev_ids); - // We move terminated hyps off of the beam. - if (is_last_decoder_step || - (e.global_score > eos_score_threshold && - e.local_score > local_eos_threshold)) { - (*eos_in_topk)[hyp_id] = true; - (*eos_hyps)[hyp_id] = e; - (*terminal_syms)[hyp_id] = eos_id; - } - } else if (eoc_id >= 0 && is_last_chunk.vec()(hyp_id) && - e.word_id == eoc_id) { - VLOG(3) << "last chunk hyp score=" << e.global_score - << " toks=" << debug::IdsToStr(e.prev_ids); - // At the last chunk and output . We terminate the - // hypothesis, even though was not predicted, and - // indicate that the final symbol for the hypothesis is - // , not . - if (e.global_score > eos_score_threshold && - // Only allow an empty hyp (all s) to be - // considered terminated, if explicitly permitted. - // 'prev_ids' contains only non-epsilons. - (allow_empty_terminated_hyp || !e.prev_ids.empty())) { - (*eos_in_topk)[hyp_id] = true; - (*eos_hyps)[hyp_id] = e; - (*terminal_syms)[hyp_id] = eoc_id; - } - } else { - merged_topk_vec[beam_id].Add(e); - } - } - } - } - }); - - const int hyps_per_beam = k; - top_k->resize(hyps_per_beam * num_beams); - for (int i = 0; i < num_beams; ++i) { - auto ith_topk = merged_topk_vec[i].Get(); - std::sort(ith_topk.begin(), ith_topk.end(), HigherScore()); - const int num_hyps = - std::min(static_cast(ith_topk.size()), hyps_per_beam); - for (int j = 0; j < num_hyps; ++j) { - (*top_k)[j * num_beams + i] = ith_topk[j]; - } - for (int j = hyps_per_beam; j < ith_topk.size(); ++j) { - extra_m->push_back(ith_topk[j]); - } - } - VLOG(1) << "Topk done"; -} - -class BeamSearchStepOp : public OpKernel { - public: - explicit BeamSearchStepOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("eos_id", &eos_id_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("eoc_id", &eoc_id_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("beam_size", &beam_size_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("num_hyps_per_beam", &num_hyps_per_beam_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("valid_eos_max_logit_delta", - &valid_eos_max_logit_delta_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("local_eos_threshold", - &local_eos_threshold_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("merge_paths", &merge_paths_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("allow_empty_terminated_hyp", - &allow_empty_terminated_hyp_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("ensure_full_beam", &ensure_full_beam_)); - OP_REQUIRES_OK( - ctx, ctx->GetAttr("force_eos_in_last_step", &force_eos_in_last_step_)); - - CHECK_GE(eos_id_, 0); - CHECK_GT(beam_size_, 0.0); - CHECK_GT(num_hyps_per_beam_, 0); - } - - private: - Tensor* ForwardOrCopyInputToOutput(OpKernelContext* ctx, int input_idx, - int output_idx) { - Tensor* output = nullptr; - const Tensor& input = ctx->input(input_idx); - CHECK(ctx->forward_input_or_allocate_output({input_idx}, output_idx, - input.shape(), &output) - .ok()); - if (!output->SharesBufferWith(input)) { - // Copy the input data if we were unable to forward the underlying buffer. - if (DataTypeCanUseMemcpy(input.dtype())) { - if (input.NumElements() > 0) { - StringPiece input_data = input.tensor_data(); - StringPiece output_data = output->tensor_data(); - memcpy(const_cast(output_data.data()), input_data.data(), - input_data.size()); - } - } else if (input.dtype() == DT_STRING) { - output->flat() = input.flat(); - } - } - return output; - } - - string AssembleDoneHyp(const Hyp& hyp, const int32 terminal_sym, - const TTypes::Matrix& t_out_prev_hyps, - const TTypes::Matrix& t_out_hyps, - const TTypes::Matrix& t_out_scores, - const TTypes::Tensor t_out_atten_probs, - const Tensor& atten_probs, int t) const { - std::vector hyp_ids(t); - int hyp_id = hyp.hyp_id; - for (int i = t - 1; i >= 0; --i) { - hyp_ids[i] = hyp_id; - hyp_id = t_out_prev_hyps(i, hyp_id); - } - Hypothesis hypothesis; - hypothesis.set_beam_id(hyp.beam_id); - // Add one to account for t-th step (terminal sym). - const float average_step_score = hyp.global_score / (t + 1); - for (int i = 0; i < t; i++) { - const int hyp_id = hyp_ids[i]; - hypothesis.add_ids(t_out_hyps(i, hyp_id)); - // If this is a model with epsilons (RNN-T or NT), then this hyp - // may represent many possible paths that have been merged. The - // recorded per-step scores only are valid for one of these paths, so - // they are not meaningful for the merged path. For the merged path - // we simpy take an average per-step score. - const float score_this_step = - (merge_paths_ ? average_step_score : t_out_scores(i, hyp_id)); - hypothesis.add_scores(score_this_step); - auto* att_vec = hypothesis.add_atten_vecs(); - for (int j = 0; j < atten_probs.dim_size(1); ++j) { - att_vec->add_prob(t_out_atten_probs(i, hyp_id, j)); - } - } - // Now add the terminal symbol. - hypothesis.add_ids(terminal_sym); - // As above, use the average per-step score for RNN-T and NT. - const float score_this_step = - merge_paths_ ? average_step_score : hyp.local_score; - hypothesis.add_scores(score_this_step); - auto* att_vec = hypothesis.add_atten_vecs(); - auto t_atten_probs = atten_probs.matrix(); - for (int j = 0; j < atten_probs.dim_size(1); ++j) { - att_vec->add_prob(t_atten_probs(hyp.hyp_id, j)); - } - return hypothesis.SerializeAsString(); - } - - public: - void Compute(OpKernelContext* ctx) override { - const Tensor& scores = ctx->input(0); - const Tensor& atten_probs = ctx->input(1); - const Tensor& best_scores = ctx->input(2); - const Tensor& cumulative_scores = ctx->input(3); - const Tensor& in_scores = ctx->input(4); - const Tensor& in_hyps = ctx->input(5); - const Tensor& in_prev_hyps = ctx->input(6); - const Tensor& in_done_hyps = ctx->input(7); - const Tensor& in_atten_probs = ctx->input(8); - const Tensor& is_last_chunk = ctx->input(9); - const Tensor& cur_step = ctx->input(10); - - OP_REQUIRES( - ctx, scores.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. scores.dims() == 2. Got ", - scores.dims())); - OP_REQUIRES( - ctx, atten_probs.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. atten_probs.dims() == 2. Got ", - atten_probs.dims())); - OP_REQUIRES( - ctx, best_scores.dims() == 1, - errors::InvalidArgument( - "Failed tensor shape sanity check. best_scores.dims() == 1. Got ", - best_scores.dims())); - OP_REQUIRES(ctx, cumulative_scores.dims() == 1, - errors::InvalidArgument("Failed tensor shape sanity check. " - "cumulative_scores.dims() == 1. Got ", - cumulative_scores.dims())); - OP_REQUIRES( - ctx, in_scores.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. in_scores.dims() == 2. Got ", - in_scores.dims())); - OP_REQUIRES(ctx, in_hyps.dims() == 2, - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dims() == 2. Got ", - in_hyps.dims())); - - OP_REQUIRES( - ctx, in_prev_hyps.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. in_prev_hyps.dims() == 2. Got ", - in_prev_hyps.dims())); - OP_REQUIRES( - ctx, in_done_hyps.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. in_done_hyps.dims() == 2. Got ", - in_done_hyps.dims())); - OP_REQUIRES(ctx, in_atten_probs.dims() == 3, - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_atten_probs.dims() == 3. Got ", - in_atten_probs.dims())); - OP_REQUIRES( - ctx, cur_step.dims() == 0, - errors::InvalidArgument( - "Failed tensor shape sanity check. cur_step.dims() == 0. Got ", - cur_step.dims())); - OP_REQUIRES(ctx, scores.dim_size(0) == atten_probs.dim_size(0), - errors::InvalidArgument("Failed tensor shape sanity check. " - "scores.dim_size(0) == " - "atten_probs.dim_size(0). Got ", - scores.dim_size(0), " and ", - atten_probs.dim_size(0))); - OP_REQUIRES(ctx, scores.dim_size(0) == cumulative_scores.dim_size(0), - errors::InvalidArgument("Failed tensor shape sanity check. " - "scores.dim_size(0) == " - "cumulative_scores.dim_size(0). Got ", - scores.dim_size(0), " and ", - cumulative_scores.dim_size(0))); - OP_REQUIRES(ctx, scores.dim_size(0) % best_scores.dim_size(0) == 0, - errors::InvalidArgument("Failed tensor shape sanity check. " - "scores.dim_size(0) % " - "best_scores.dim_size(0) == 0. Got ", - scores.dim_size(0), " and ", - best_scores.dim_size(0))); - OP_REQUIRES( - ctx, scores.dim_size(0) / best_scores.dim_size(0) == num_hyps_per_beam_, - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "scores.dim_size(0) / best_scores.dim_size(0) " - "== num_hyps_per_beam_. Got ", - scores.dim_size(0), " and ", best_scores.dim_size(0), - " where num_hyps_per_beam_ = ", num_hyps_per_beam_)); - OP_REQUIRES(ctx, scores.dim_size(0) == in_hyps.dim_size(1), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "scores.dim_size(0) == in_hyps.dim_size(1). Got ", - scores.dim_size(0), " and ", in_hyps.dim_size(1))); - OP_REQUIRES(ctx, in_hyps.dim_size(0) == in_scores.dim_size(0), - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dim_size(0) == " - "in_scores.dim_size(0). Got ", - in_hyps.dim_size(0), " and ", - in_scores.dim_size(0))); - OP_REQUIRES(ctx, in_hyps.dim_size(0) == in_prev_hyps.dim_size(0), - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dim_size(0) == " - "in_prev_hyps.dim_size(0). Got ", - in_hyps.dim_size(0), " and ", - in_prev_hyps.dim_size(0))); - OP_REQUIRES(ctx, in_hyps.dim_size(0) == in_done_hyps.dim_size(0), - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dim_size(0) == " - "in_done_hyps.dim_size(0). Got ", - in_hyps.dim_size(0), " and ", - in_done_hyps.dim_size(0))); - OP_REQUIRES(ctx, in_hyps.dim_size(0) == in_atten_probs.dim_size(0), - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dim_size(0) == " - "in_atten_probs.dim_size(0). Got ", - in_hyps.dim_size(0), " and ", - in_atten_probs.dim_size(0))); - OP_REQUIRES(ctx, in_hyps.dim_size(1) == in_scores.dim_size(1), - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dim_size(1) == " - "in_scores.dim_size(1). Got ", - in_hyps.dim_size(1), " and ", - in_scores.dim_size(1))); - OP_REQUIRES(ctx, in_hyps.dim_size(1) == in_prev_hyps.dim_size(1), - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dim_size(1) == " - "in_prev_hyps.dim_size(1). Got ", - in_hyps.dim_size(1), " and ", - in_prev_hyps.dim_size(1))); - OP_REQUIRES(ctx, in_hyps.dim_size(1) == in_done_hyps.dim_size(1), - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dim_size(1) == " - "in_done_hyps.dim_size(1). Got ", - in_hyps.dim_size(1), " and ", - in_done_hyps.dim_size(1))); - OP_REQUIRES(ctx, in_hyps.dim_size(1) == in_atten_probs.dim_size(1), - errors::InvalidArgument("Failed tensor shape sanity check. " - "in_hyps.dim_size(1) == " - "in_atten_probs.dim_size(1). Got ", - in_hyps.dim_size(1), " and ", - in_atten_probs.dim_size(1))); - OP_REQUIRES( - ctx, atten_probs.dim_size(1) == in_atten_probs.dim_size(2), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "atten_probs.dim_size(1) == in_atten_probs.dim_size(2). Got ", - atten_probs.dim_size(1), " and ", in_atten_probs.dim_size(2))); - - if (merge_paths_) { - OP_REQUIRES( - ctx, eoc_id_ >= 0, - errors::InvalidArgument( - "Paths can only be merged for an epsilon-emitting model (RNN-T " - "or NT). Epsilon id must be non-negative, but got: ", - eoc_id_)); - } - int num_beams = best_scores.dim_size(0); - int num_hyps = cumulative_scores.dim_size(0); - CHECK_EQ(num_hyps_per_beam_, num_hyps / num_beams); - const int t = cur_step.scalar()(); - CHECK_LT(t, in_hyps.dim_size(0)); - - VLOG(2) << "BeamSearchStepOp(" << num_hyps_per_beam_ << ") step=" << t; - auto t_cumulative_scores = cumulative_scores.vec(); - std::vector hyps(num_hyps); - for (int i = 0; i < num_hyps; ++i) { - hyps[i].beam_id = i % num_beams; - hyps[i].hyp_id = i; - hyps[i].global_score = t_cumulative_scores(i); - // Determines the sequence of prev ids that this hypothesis represents. - std::vector hyp_id_at_step(t); - int hyp_id = i; - for (int j = t - 1; j >= 0; --j) { - hyp_id_at_step[j] = hyp_id; - hyp_id = in_prev_hyps.matrix()(j, hyp_id); - } - for (int j = 0; j < t; ++j) { - const int prev_id = in_hyps.matrix()(j, hyp_id_at_step[j]); - if (prev_id != eoc_id_) { - hyps[i].prev_ids.push_back(prev_id); - } - } - VLOG(3) << "Step " << t << " hyp " << i - << " score=" << hyps[i].global_score - << " toks=" << debug::IdsToStr(hyps[i].prev_ids); - } - std::vector top_k_hyps; - std::vector extra_m_hyps; - std::vector eos_hyps; - std::vector eos_in_topk; - std::vector terminal_syms; - const bool is_last_decoder_step = - (t == (in_hyps.dim_size(0) - 1)) && force_eos_in_last_step_; - ComputeTopKPlusM(hyps, scores, /*k=*/num_hyps_per_beam_, /*m=*/0, - /*eos_id=*/eos_id_, /*eoc_id=*/eoc_id_, num_beams, - valid_eos_max_logit_delta_, local_eos_threshold_, - /*is_first_step=*/t == 0, is_last_decoder_step, - is_last_chunk, merge_paths_, allow_empty_terminated_hyp_, - &eos_in_topk, &top_k_hyps, &extra_m_hyps, &eos_hyps, - &terminal_syms); - - Tensor* out_best_scores = NULL; - Tensor* out_cumulative_scores = NULL; - OP_REQUIRES_OK( - ctx, ctx->allocate_output(0, best_scores.shape(), &out_best_scores)); - OP_REQUIRES_OK(ctx, ctx->allocate_output(1, cumulative_scores.shape(), - &out_cumulative_scores)); - Tensor* out_scores = ForwardOrCopyInputToOutput(ctx, 4, 2); - Tensor* out_hyps = ForwardOrCopyInputToOutput(ctx, 5, 3); - Tensor* out_prev_hyps = ForwardOrCopyInputToOutput(ctx, 6, 4); - Tensor* out_done_hyps = ForwardOrCopyInputToOutput(ctx, 7, 5); - Tensor* out_atten_probs = ForwardOrCopyInputToOutput(ctx, 8, 6); - Tensor* all_done; - OP_REQUIRES_OK(ctx, ctx->allocate_output(7, TensorShape({}), &all_done)); - - auto t_out_best_scores = out_best_scores->vec(); - auto t_out_cumulative_scores = out_cumulative_scores->vec(); - auto t_out_scores = out_scores->matrix(); - auto t_out_hyps = out_hyps->matrix(); - auto t_out_prev_hyps = out_prev_hyps->matrix(); - auto t_out_done_hyps = out_done_hyps->matrix(); - auto t_out_atten_probs = out_atten_probs->tensor(); - auto t_all_done = all_done->scalar(); - - // To initialize the two vectors. - t_out_best_scores = best_scores.vec(); - t_out_cumulative_scores = cumulative_scores.vec(); - - // Fill in all the output tensors. - for (int i = 0; i < num_hyps; ++i) { - const Hyp& hyp = top_k_hyps[i]; - t_out_scores(t, i) = hyp.local_score; - t_out_cumulative_scores(i) = hyp.global_score; - t_out_hyps(t, i) = hyp.word_id; - t_out_prev_hyps(t, i) = hyp.hyp_id; - t_out_atten_probs.chip(t, 0).chip(i, 0) = - atten_probs.matrix().chip(hyp.hyp_id, 0); - if (eos_in_topk[i]) { - // We have a good terminated hyp. - const int beam_id = eos_hyps[i].beam_id; - CHECK_EQ(beam_id, i % num_beams); - VLOG(2) << "Top EOS hyp @step " << t - << " score=" << eos_hyps[i].global_score - << " toks=" << debug::IdsToStr(eos_hyps[i].prev_ids); - // Update the best scores. - if (eos_hyps[i].global_score > t_out_best_scores(beam_id)) { - t_out_best_scores(beam_id) = eos_hyps[i].global_score; - } - string done_hyp = AssembleDoneHyp( - eos_hyps[i], terminal_syms[i], t_out_prev_hyps, t_out_hyps, - t_out_scores, t_out_atten_probs, atten_probs, t); - t_out_done_hyps(t, i) = done_hyp; - } - } - - // Now check for all_done - t_all_done() = true; - if (ensure_full_beam_) { - // First check how many EOS hyps we have. If we have fewer than - // num_hyps_per_beam for any beam, we are NOT done. - for (int beam_id = 0; beam_id < num_beams; ++beam_id) { - int num_done_hyps = 0; - for (int index_in_beam = 0; index_in_beam < num_hyps_per_beam_; - ++index_in_beam) { - for (int time_step = 0; time_step < t; ++time_step) { - int index = beam_id * num_hyps_per_beam_ + index_in_beam;; - if (!t_out_done_hyps(time_step, index).empty()) { - ++num_done_hyps; - } - } - } - if (num_done_hyps < num_hyps_per_beam_) { - t_all_done() = false; - break; - } - } - if (t_all_done() == false) return; - } - // Now check for hyp quality. If for any beam we still have hyps within - // 'beam_size' of best score, we are NOT done. - for (int i = 0; i < num_hyps; ++i) { - const Hyp& hyp = top_k_hyps[i]; - const int beam_id = hyp.beam_id; - CHECK_EQ(beam_id, i % num_beams); - VLOG(3) << "Hyp score=" << hyp.global_score - << " beam best=" << t_out_best_scores(beam_id) - << " beam size=" << beam_size_; - if (hyp.global_score > t_out_best_scores(beam_id) - beam_size_) { - t_all_done() = false; - break; - } - } - } - - private: - int eos_id_ = 0; - int eoc_id_ = -1; - float beam_size_ = 0.0; - int num_hyps_per_beam_ = 0; - float valid_eos_max_logit_delta_ = 0.0; - float local_eos_threshold_ = 0.0; - bool merge_paths_ = false; - bool allow_empty_terminated_hyp_ = true; - bool ensure_full_beam_ = false; - bool force_eos_in_last_step_ = false; -}; - -REGISTER_KERNEL_BUILDER(Name("BeamSearchStep").Device(DEVICE_CPU), - BeamSearchStepOp); - -class TopKTerminatedHypsOp : public OpKernel { - public: - explicit TopKTerminatedHypsOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("k", &k_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("num_hyps_per_beam", &num_hyps_per_beam_)); - OP_REQUIRES_OK( - ctx, ctx->GetAttr("length_normalization", &length_normalization_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("coverage_penalty", &coverage_penalty_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("target_seq_length_ratio", - &target_seq_length_ratio_)); - // TODO(anjuli): Remove eoc_id_ which is no longer used. - OP_REQUIRES_OK(ctx, ctx->GetAttr("eoc_id", &eoc_id_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("merge_paths", &merge_paths_)); - CHECK_GE(length_normalization_, 0.0); - CHECK_GE(target_seq_length_ratio_, 0.0); - CHECK_GE(coverage_penalty_, 0); - CHECK_GT(num_hyps_per_beam_, 0); - CHECK_GT(k_, 0); - } - - void ComputeTopK(const Tensor& in_done_hyps, - const std::vector src_seq_lengths, const int32 k, - const int32 num_beams, Tensor* topk_hyps) { - VLOG(1) << "Topk clear, num_beams: " << num_beams; - int hyps_size = in_done_hyps.dim_size(1); - int num_steps = in_done_hyps.dim_size(0); - static thread::ThreadPool* workers = new thread::ThreadPool( - Env::Default(), "topk_terminated_hyps", kNumWorkers); - // No Insert struct is provided, so we use DefaultInsert, which inserts - // without deduping. No deduping is necessary here because we dedupe - // partial hyps at each step of beam search. - std::vector> - topk_vec(num_beams, - TopK( - k, /* unused epsilon id */ -1)); - // Each mutex is used to protect corresponding topk_vec. - std::vector mu_vec(num_beams); - auto t_done_hyps = in_done_hyps.matrix(); - // The thread sharding is along hyps_size. - Shard(kNumWorkers, workers, hyps_size, 1000 * num_steps, - [&](int64 start, int64 limit) { - Hypothesis hypothesis; - for (int32 hyp_id = start; hyp_id < limit; ++hyp_id) { - // The topk for this beam. - TopK* - topk = &topk_vec[hyp_id % num_beams]; - for (int32 step_id = 0; step_id < num_steps; ++step_id) { - const string& str_hyps = t_done_hyps(step_id, hyp_id); - if (!str_hyps.empty()) { - hypothesis.ParseFromString(str_hyps); - if (hypothesis.has_beam_id()) { - // This hypothesis is a real terminated hyps. - int src_size = src_seq_lengths[hypothesis.beam_id()]; - float normalized_score = - NormalizedScore(hypothesis, src_size); - hypothesis.set_normalized_score(normalized_score); - VLOG(2) << "Add to terminated top-k " - << " score=" << hypothesis.normalized_score() - << " toks=" << debug::IdsToStr(hypothesis.ids()); - // TODO(xbing): avoid acquiring a mutex for each record. - mutex_lock l(mu_vec[hyp_id % num_beams]); - topk->Add(hypothesis); - } - } - } - } - }); - - auto t_topk_hyps = topk_hyps->matrix(); - for (int i = 0; i < num_beams; ++i) { - auto ith_topk = topk_vec[i].Get(); - CHECK_LE(ith_topk.size(), k); - std::sort(ith_topk.begin(), ith_topk.end(), BetterTerminatedHyp()); - for (int j = 0; j < ith_topk.size(); ++j) { - t_topk_hyps(i, j) = ith_topk[j].SerializeAsString(); - VLOG(2) << "TopK(" << i << ", " << j << ") = " - << debug::IdsToStr(ith_topk[j].ids()); - } - } - } - - float NormalizedScore(const Hypothesis& hypothesis, - const int src_size) const { - int length = hypothesis.atten_vecs_size(); - Tensor cumulative_atten_prob(DT_FLOAT, {src_size}); - auto cumulative_atten_prob_vec = cumulative_atten_prob.vec(); - cumulative_atten_prob_vec.setZero(); - for (int step = 0; step < hypothesis.atten_vecs_size(); ++step) { - const int hyp_prob_size = hypothesis.atten_vecs(step).prob_size(); - for (int src_id = 0; src_id < src_size; ++src_id) { - if (src_id < hyp_prob_size) { - cumulative_atten_prob_vec(src_id) += - hypothesis.atten_vecs(step).prob(src_id); - } else { - // This can happen e.g. for RNNT model. Here we simply assume - // atten_prob for those source positions are 0.0 - VLOG(5) << "Missing atten_prob for source position " << src_id - << ". Total available positions are " << hyp_prob_size << "."; - cumulative_atten_prob_vec(src_id) += 0.0; - } - } - } - // Coverage is capped at 0.5 so that so long as a word is - // reasonably covered, it is not penalized anymore. - Tensor penalty(DT_FLOAT, {}); - penalty.scalar() = - (cumulative_atten_prob_vec / target_seq_length_ratio_) - .cwiseMax(0.001f) - .cwiseMin(0.5f) - .log() - .sum().eval(); - const float coverage_penalty = penalty.scalar()(); - const float length_norm = std::pow(length + 5.0, length_normalization_) / - std::pow(5.0, length_normalization_); - - float global_score = 0.0; - for (const auto& score : hypothesis.scores()) { - global_score += score; - } - const float normalized_score = - global_score / length_norm + - (target_seq_length_ratio_ * coverage_penalty_ * coverage_penalty); - return normalized_score; - } - - void Compute(OpKernelContext* ctx) override { - const Tensor& in_done_hyps = ctx->input(0); - // Some sanity check on the tensor shapes. - OP_REQUIRES(ctx, in_done_hyps.dim_size(1) % num_hyps_per_beam_ == 0, - errors::InvalidArgument("Failed tensor shape sanity check.")); - - int num_beams = in_done_hyps.dim_size(1) / num_hyps_per_beam_; - - const Tensor& in_src_seq_lens = ctx->input(1); - OP_REQUIRES( - ctx, in_src_seq_lens.dim_size(0) == num_beams, - errors::InvalidArgument( - "src_seq_lengths should be a 1-d Tensor of length num_beams. Got ", - in_src_seq_lens.dim_size(0), " vs ", num_beams)); - std::vector src_seq_lengths(num_beams); - for (int i = 0; i < num_beams; ++i) { - src_seq_lengths[i] = in_src_seq_lens.flat()(i); - } - // Set the output tensors. - Tensor* out_topk_hyps = nullptr; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape{num_beams, k_}, - &out_topk_hyps)); - ComputeTopK(in_done_hyps, src_seq_lengths, k_, num_beams, out_topk_hyps); - VLOG(1) << "TopKTerminatedHypsOp(" << num_hyps_per_beam_ << ") done"; - } - - private: - int32 num_hyps_per_beam_; - float length_normalization_; - float coverage_penalty_; - float target_seq_length_ratio_; - int32 k_; - int32 eoc_id_; - bool merge_paths_ = false; -}; - -REGISTER_KERNEL_BUILDER(Name("TopKTerminatedHyps").Device(DEVICE_CPU), - TopKTerminatedHypsOp); - -class UnpackHypOp : public OpKernel { - public: - explicit UnpackHypOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("max_seq_length", &max_seq_length_)); - CHECK_GE(max_seq_length_, 0); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor& in_hyps = ctx->input(0); - const auto& t_in_hyps = in_hyps.flat(); - const int batch_size = t_in_hyps.size(); - std::vector hyps(batch_size); - for (int i = 0; i < batch_size; ++i) { - // TODO(yonghui): parallelize this loop. - const tstring& t_in_hyps_i = t_in_hyps(i); - if (!t_in_hyps(i).empty()) { - hyps[i].ParseFromArray(t_in_hyps_i.data(), t_in_hyps_i.size()); - } - } - int max_seq_length = max_seq_length_; - if (max_seq_length <= 0) { - // Derive max_seq_length from input hyps. - for (int i = 0; i < batch_size; ++i) { - if (hyps[i].ids_size() > max_seq_length) { - max_seq_length = hyps[i].ids_size(); - } - } - } - Tensor* out_ids; - OP_REQUIRES_OK( - ctx, ctx->allocate_output(0, TensorShape({batch_size, max_seq_length}), - &out_ids)); - Tensor* out_seq_lens; - OP_REQUIRES_OK( - ctx, ctx->allocate_output(1, TensorShape({batch_size}), &out_seq_lens)); - Tensor* out_scores; - OP_REQUIRES_OK( - ctx, ctx->allocate_output(2, TensorShape({batch_size}), &out_scores)); - auto t_out_ids = out_ids->matrix(); - auto t_out_seq_lens = out_seq_lens->vec(); - auto t_out_scores = out_scores->vec(); - t_out_ids.setZero(); - t_out_seq_lens.setZero(); - t_out_scores.setZero(); - for (int i = 0; i < batch_size; ++i) { - const Hypothesis& hyp = hyps[i]; - // TODO(yonghui): parallelize this loop. - if (hyp.ids_size() > 0) { - for (int j = 0; j < hyp.ids_size() && j < max_seq_length; ++j) { - t_out_ids(i, j) = hyp.ids(j); - } - t_out_seq_lens(i) = std::min(hyp.ids_size(), max_seq_length); - t_out_scores(i) = hyp.normalized_score(); - } - } - } - - private: - int32 max_seq_length_ = 0; -}; - -REGISTER_KERNEL_BUILDER(Name("UnpackHyp").Device(DEVICE_CPU), UnpackHypOp); - -template -class HypsFromBeamSearchOuts : public OpKernel { - public: - explicit HypsFromBeamSearchOuts(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("eos_id", &eos_id_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("num_hyps_per_beam", &num_hyps_per_beam_)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor& hyps = ctx->input(0); - const Tensor& prev_hyps = ctx->input(1); - const Tensor& done_hyps = ctx->input(2); - const Tensor& scores = ctx->input(3); - const Tensor& atten_probs = ctx->input(4); - const Tensor& eos_scores = ctx->input(5); - const Tensor& eos_atten_probs = ctx->input(6); - - OP_REQUIRES(ctx, hyps.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. hyps.dims() == 2. Got ", - hyps.dims())); - OP_REQUIRES( - ctx, prev_hyps.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. prev_hyps.dims() == 2. Got ", - prev_hyps.dims())); - OP_REQUIRES( - ctx, done_hyps.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. done_hyps.dims() == 2. Got ", - done_hyps.dims())); - OP_REQUIRES( - ctx, scores.dims() == 2, - errors::InvalidArgument( - "Failed tensor shape sanity check. scores.dims() == 2. Got ", - scores.dims())); - OP_REQUIRES( - ctx, atten_probs.dims() == 3, - errors::InvalidArgument( - "Failed tensor shape sanity check. atten_probs.dims() == 2. Got ", - atten_probs.dims())); - - OP_REQUIRES(ctx, atten_probs.dim_size(1) == scores.dim_size(1), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "atten_probs.dim_size(1) == scores.dim_size(1). Got ", - atten_probs.dim_size(1), " and ", scores.dim_size(1))); - - OP_REQUIRES(ctx, hyps.IsSameSize(prev_hyps), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "hyps and prev_hyps should have the same shape. Got ", - hyps.shape().DebugString(), " and ", - prev_hyps.shape().DebugString())); - - OP_REQUIRES(ctx, hyps.IsSameSize(done_hyps), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "hyps and done_hyps should have the same shape. Got ", - hyps.shape().DebugString(), " and ", - done_hyps.shape().DebugString())); - - OP_REQUIRES( - ctx, hyps.IsSameSize(scores), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "hyps and scores should have the same shape. Got ", - hyps.shape().DebugString(), " and ", scores.shape().DebugString())); - - OP_REQUIRES(ctx, hyps.IsSameSize(done_hyps), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "hyps and done_hyps should have the same shape. Got ", - hyps.shape().DebugString(), " and ", - done_hyps.shape().DebugString())); - - OP_REQUIRES(ctx, hyps.IsSameSize(eos_scores), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "hyps and eos_scores should have the same shape. Got ", - hyps.shape().DebugString(), " and ", - eos_scores.shape().DebugString())); - OP_REQUIRES( - ctx, atten_probs.IsSameSize(eos_atten_probs), - errors::InvalidArgument( - "Failed tensor shape sanity check. " - "atten_probs and eos_atten_probs should have the same shape. Got ", - atten_probs.shape().DebugString(), " and ", - eos_atten_probs.shape().DebugString())); - - auto t_hyps = hyps.matrix(); - auto t_prev_hyps = prev_hyps.matrix(); - auto t_done_hyps = done_hyps.matrix(); - auto t_scores = scores.matrix(); - auto t_atten_probs = atten_probs.tensor(); - auto t_eos_scores = eos_scores.matrix(); - auto t_eos_atten_probs = eos_atten_probs.tensor(); - const int seq_length = hyps.dim_size(0); - const int num_hyps = hyps.dim_size(1); - - Tensor* out_hyps; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, hyps.shape(), &out_hyps)); - auto out_hyps_t = out_hyps->matrix(); - - // Use the same thread pool as topk operator. - static thread::ThreadPool* workers = - new thread::ThreadPool(Env::Default(), "topk", kNumWorkers); - - Shard( - kNumWorkers, workers, num_hyps, seq_length * seq_length, - [&](int64 start, int64 end) { - std::vector hyp_token_ids; - std::vector hyp_local_scores; - std::vector hyp_ids; - Hypothesis terminated_hyps; - const int num_beams = (num_hyps / num_hyps_per_beam_); - for (int i = 0; i < seq_length; ++i) { - for (int j = start; j < end; ++j) { - // If the hyp is terminated, then assemble the output proto. - if (t_done_hyps(i, j)) { - // Reuse the buffers. - hyp_token_ids.clear(); - hyp_local_scores.clear(); - hyp_ids.clear(); - terminated_hyps.Clear(); - - // Walk through the token id matrix, and assemble id, score, and - // prev_hyp_id for each step. - hyp_token_ids.push_back(eos_id_); - hyp_local_scores.push_back(t_eos_scores(i, j)); - int prev_hyp_id = j; - hyp_ids.push_back(prev_hyp_id); - for (int k = i - 1; k >= 0; --k) { - hyp_token_ids.push_back(t_hyps(k, prev_hyp_id)); - hyp_local_scores.push_back(t_scores(k, prev_hyp_id)); - prev_hyp_id = t_prev_hyps(k, prev_hyp_id); - hyp_ids.push_back(prev_hyp_id); - } - - // Assemble terminated hyp. - terminated_hyps.set_beam_id(j % num_beams); - for (int l = hyp_local_scores.size() - 1; l >= 0; --l) { - terminated_hyps.add_scores(float(hyp_local_scores[l])); - terminated_hyps.add_ids(hyp_token_ids[l]); - const int cur_step = hyp_local_scores.size() - 1 - l; - auto* att_vec = terminated_hyps.add_atten_vecs(); - for (int d = 0; d < atten_probs.dim_size(2); ++d) { - if (l == 0) { - att_vec->add_prob( - float(t_eos_atten_probs(cur_step, hyp_ids[l], d))); - } else { - att_vec->add_prob( - float(t_atten_probs(cur_step, hyp_ids[l], d))); - } - } - } - out_hyps_t(i, j) = terminated_hyps.SerializeAsString(); - } - } - } - }); - } - - private: - int32 eos_id_ = 0; - int32 num_hyps_per_beam_ = 0; -}; - -REGISTER_KERNEL_BUILDER(Name("HypsFromBeamSearchOuts") - .Device(DEVICE_CPU) - .TypeConstraint("T"), - HypsFromBeamSearchOuts); -REGISTER_KERNEL_BUILDER(Name("HypsFromBeamSearchOuts") - .Device(DEVICE_CPU) - .TypeConstraint("T"), - HypsFromBeamSearchOuts); - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/beam_search_step_op_kernels.h b/lingvo/core/ops/beam_search_step_op_kernels.h deleted file mode 100644 index 446d5dd6..00000000 --- a/lingvo/core/ops/beam_search_step_op_kernels.h +++ /dev/null @@ -1,244 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_BEAM_SEARCH_STEP_OP_KERNELS_H_ -#define LINGVO_CORE_OPS_BEAM_SEARCH_STEP_OP_KERNELS_H_ - -#include // std::sort -#include // std::greater -#include - -#include "lingvo/core/ops/hyps.pb.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/lib/strings/strcat.h" - -namespace tensorflow { -namespace lingvo { - -// Simple tuple for book keeping during beam pruning. -struct Hyp { - int32 beam_id; // The beam that this hyp belongs to. - int32 hyp_id; // The hypothesis id. - int32 word_id; // The id for the predicted next word. - float local_score; // Local score from the current step. - float global_score; // Cumulative score till the current step. - std::vector prev_ids; // The (non-epsilon) token ids up to this step. - - string DebugString() const { - return strings::StrCat(beam_id, " ", hyp_id, " ", word_id, " ", local_score, - " ", global_score); - } -}; - -struct HigherScore { - bool operator()(const Hyp& x, const Hyp& y) const { - // We only compare hyps belonging to the same beams. - CHECK_EQ(x.beam_id, y.beam_id); - if (x.global_score > y.global_score) return true; - if (x.global_score < y.global_score) return false; - if (x.word_id < y.word_id) return true; - if (x.word_id > y.word_id) return false; - return x.hyp_id < y.hyp_id; - } -}; - -// Similar to HigherScore, but optionally return Hyps with eos_id in it -// as the one with the higher score. -struct HigherScoreWithEos { - int eos_id_; - // Determines whether or not to prioritize Hyps with eos_id. If False, - // this will behave exactly like HigherScore. - bool include_eos_; - - HigherScoreWithEos(int eos_id, bool include_eos) - : eos_id_(eos_id), include_eos_(include_eos) {} - - bool operator()(const Hyp& x, const Hyp& y) const { - // We only compare hyps belonging to the same beams. - CHECK_EQ(x.beam_id, y.beam_id); - // Note that we revert to HigherScore's behavior when _both_ paths contain - // eos_id_ as the word_id. - if (!(x.word_id == eos_id_ && y.word_id == eos_id_)) { - if (x.word_id == eos_id_ && include_eos_) return true; - if (y.word_id == eos_id_ && include_eos_) return false; - } - // The following behavior is the same as that of HigherScore's. - if (x.global_score > y.global_score) return true; - if (x.global_score < y.global_score) return false; - if (x.word_id < y.word_id) return true; - if (x.word_id > y.word_id) return false; - return x.hyp_id < y.hyp_id; - } -}; - -struct BetterTerminatedHyp { - bool operator()(const Hypothesis& x, const Hypothesis& y) const { - // We only compare hyps belonging to the same beams. - CHECK_EQ(x.beam_id(), y.beam_id()); - if (x.normalized_score() > y.normalized_score()) return true; - if (x.normalized_score() < y.normalized_score()) return false; - return x.ids_size() < y.ids_size(); - } -}; - -struct ExtractGlobalScore { - float operator()(const Hyp& x) const { return x.global_score; } -}; - -struct ExtractNormalizedScore { - float operator()(const Hypothesis& x) const { return x.normalized_score(); } -}; - -template -struct Id { - const T& operator()(const T& t) const { return t; } -}; - -template -struct DefaultInsert { - explicit DefaultInsert(int unused_epsilon_id) {} - void operator()(const T& t, std::vector* items) const { - items->push_back(t); - } -}; - -// Returns true if 'cur_hyp' ad 'other_hyp' represent the same label sequence -// when epsilons are ignored, and false otherwise. -bool IsDuplicateHyp(const Hyp& cur_hyp, const Hyp& other_hyp, - const int epsilon_id); - -float LogSumExp(float a, float b); - -// An insertion operator that first checks whether 'hyp' is a duplicate of any -// hyp already in 'items'. If so, these two hyps are merged. -// This check is only performed if we are using a model that emits epsilons -// (NT or RNN-T). For models that do not emit epsilons (ie epsilon_id < 0) -// 'hyp' is always added to 'items', identical to DefaultInsert. -struct InsertHypWithEpsilonDedupe { - explicit InsertHypWithEpsilonDedupe(int _epsilon_id) - : epsilon_id(_epsilon_id), better_hyp() {} - void operator()(const Hyp& hyp, std::vector* items) const { - if (epsilon_id < 0) { - items->push_back(hyp); - return; - } - for (int i = 0; i < items->size(); ++i) { - const Hyp& old_hyp = (*items)[i]; - if (IsDuplicateHyp(hyp, old_hyp, epsilon_id)) { - Hyp combined_hyp = better_hyp(hyp, old_hyp) ? hyp : old_hyp; - combined_hyp.global_score = - LogSumExp(hyp.global_score, old_hyp.global_score); - (*items)[i] = combined_hyp; - return; - } - } - items->push_back(hyp); - } - int epsilon_id; - const HigherScore better_hyp; -}; - - -// A helper class keeps track of top K highest ranked elements added. -// Comp(x, y) returns true iff x is ranked higher than y. -// Epsilon id should be set to -1 for models which do not use epsilon (e.g. LAS -// and any non-speech model), or to the id used for epsilon (end of chunk) -// for epsilon emitting models (RNN-T, NT). -// -// E.g., -// TopK topk> (100, /* epsilon id */ -1); -// topk.Add(100); -// topk.Add(-100); -// ... -// result = topk.Get(); -// // results contains the smallest 100 int added to topk. -template , typename Extract = Id, - typename Insert = DefaultInsert> -class TopK { - public: - explicit TopK(int k, int epsilon_id) - : k_(k), comp_(), extract_(), insert_(epsilon_id), selected_(false) {} - // eos_id and inlclude_eos flag will be passed on to the Comparator. - explicit TopK(int k, int epsilon_id, int eos_id, bool include_eos) - : k_(k), - comp_(Comp(eos_id, include_eos)), - extract_(), - insert_(epsilon_id), - selected_(false) {} - - using U = typename std::result_of::type; - - // Return an element that is less than or equal to the least element - // of the top k. - U Add(const T& e) { - if (!selected_ || comp_(e, items_[k_ - 1])) { - insert_(e, &items_); - if (items_.size() >= 2 * k_) Shrink(); - } - if (!selected_) return std::numeric_limits::lowest(); - return extract_(items_[k_ - 1]); - } - - const std::vector& Get() { - if (items_.size() > k_) Shrink(); - return items_; - } - - void Clear() { - selected_ = false; - items_.clear(); - } - - private: - const int k_; - const Comp comp_; - const Extract extract_; - const Insert insert_; - bool selected_; // Becomes true if k-th top element so far is known. - std::vector items_; - - void Shrink() { - // Pivot is the k-th element, i.e., items_[k_-1]. - std::nth_element(items_.begin(), items_.begin() + k_ - 1, items_.end(), - comp_); - items_.resize(k_); - selected_ = true; - } -}; - -// Exposed for benchmarking purposes. -// Given the current partial hypothesis in 'hyps' for all beams in a batch and -// the predicted next step scores 'scores', return the best scored 'k+m' -// hypotheses where the first 'k' hypotheses are used for search in the next -// step while the remaining 'm' hypotheses are kept as alternatives to increase -// diversity. 'eos_id' is the end of beam id of the target language. -// -// eos_in_topk is filled with true/false to indicate whether or not the eos -// symbol is among the topk candidate for a hyp. -void ComputeTopKPlusM(const std::vector& hyps, const Tensor& scores, - const int32 k, const int32 m, const int32 eos_id, - const int32 eoc_id, const int32 num_beams, - const float valid_eos_max_logit_delta, - const float local_eos_threshold, bool is_first_step, - bool is_last_decoder_step, const Tensor& is_last_chunk, - bool merge_paths, bool allow_empty_terminated_hyp, - std::vector* eos_in_topk, std::vector* top_k, - std::vector* extra_m, std::vector* eos_hyps, - std::vector* terminal_symbol); - -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_BEAM_SEARCH_STEP_OP_KERNELS_H_ diff --git a/lingvo/core/ops/beam_search_step_op_test.py b/lingvo/core/ops/beam_search_step_op_test.py deleted file mode 100644 index 8d2bbf1f..00000000 --- a/lingvo/core/ops/beam_search_step_op_test.py +++ /dev/null @@ -1,655 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for beam_search_op.""" - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_utils -from lingvo.core.ops import hyps_pb2 -import numpy as np - -from google.protobuf import text_format - -_MIN_SCORE = -1e36 - - -class BeamSearchOpTest(test_utils.TestCase): - - def setUp(self): - super().setUp() - np.random.seed(12345) - tf.random.set_seed(398849988) - - def _runBeamSearchOpHelper(self, - b_size, - num_beams, - seq_len, - init_best_score, - probs, - init_atten_probs, - atten_probs, - beam_size=3.0, - ensure_full_beam=False, - force_eos_in_last_step=False, - local_eos_threshold=-100.0): - eos_id = 2 - num_hyps_per_beam = b_size / num_beams - - best_scores = tf.zeros([num_beams]) - cumulative_scores = tf.zeros([b_size]) - scores = tf.zeros([seq_len, b_size]) - hyps = tf.zeros([seq_len, b_size], dtype=tf.int32) - prev_hyps = tf.zeros([seq_len, b_size], dtype=tf.int32) - done_hyps = tf.as_string(tf.zeros([seq_len, b_size], dtype=tf.int32)) - best_scores += init_best_score - - for i, prob in enumerate(probs): - (best_scores, cumulative_scores, scores, hyps, prev_hyps, done_hyps, - atten_probs, done) = ops.beam_search_step( - prob, - init_atten_probs, - best_scores, - cumulative_scores, - scores, - hyps, - prev_hyps, - done_hyps, - atten_probs, [], - i, - eos_id=eos_id, - beam_size=beam_size, - ensure_full_beam=ensure_full_beam, - num_hyps_per_beam=num_hyps_per_beam, - valid_eos_max_logit_delta=0.1, - force_eos_in_last_step=force_eos_in_last_step, - local_eos_threshold=local_eos_threshold) - - with self.session(use_gpu=False): - (best_scores, cumulative_scores, scores, hyps, prev_hyps, done_hyps, - atten_probs, done, scores, atten_probs) = self.evaluate([ - best_scores, cumulative_scores, scores, hyps, prev_hyps, done_hyps, - atten_probs, done, scores, atten_probs - ]) - - return (best_scores, cumulative_scores, scores, hyps, prev_hyps, done_hyps, - atten_probs, done, scores, atten_probs) - - def _testBeamSearchOpHelper(self, - b_size, - num_beams, - seq_len, - init_best_score, - probs, - init_atten_probs, - atten_probs, - best_scores_expected, - cum_scores_expected, - scores_expected, - hyps_expected, - prev_hyps_expected, - atten_probs_expected, - force_eos_in_last_step=False, - local_eos_threshold=-100.0): - - (best_scores, cumulative_scores, scores, hyps, prev_hyps, done_hyps, - atten_probs, done, scores, atten_probs) = self._runBeamSearchOpHelper( - b_size, - num_beams, - seq_len, - init_best_score, - probs, - init_atten_probs, - atten_probs, - force_eos_in_last_step=force_eos_in_last_step, - local_eos_threshold=local_eos_threshold) - - tf.logging.info(np.array_repr(best_scores)) - tf.logging.info(np.array_repr(cumulative_scores)) - tf.logging.info(np.array_repr(scores)) - tf.logging.info(np.array_repr(hyps)) - tf.logging.info(np.array_repr(prev_hyps)) - tf.logging.info(np.array_repr(done_hyps)) - tf.logging.info(np.array_repr(atten_probs)) - tf.logging.info(np.array_repr(done)) - tf.logging.info(np.array_repr(scores)) - tf.logging.info(np.array_repr(atten_probs)) - - self.assertAllClose(best_scores_expected, best_scores) - self.assertAllClose(cum_scores_expected, cumulative_scores) - self.assertAllClose(scores_expected, scores) - self.assertAllClose(hyps_expected, hyps) - self.assertAllClose(prev_hyps_expected, prev_hyps) - self.assertAllClose(atten_probs_expected, atten_probs) - self.assertEqual(False, done) - - return done_hyps - - def testBeamSearchOp(self): - b_size = 8 - num_beams = 2 - seq_len = 6 - num_classes = 5 - - best_scores_expected = [1.769434, 1.640316] - cum_scores_expected = [ - 1.823942, 1.609159, 1.610366, 1.454234, 1.348811, 1.3167, 1.346274, - 1.045735 - ] - scores_expected = [ - [ - 0.86230338, 0.84442794, 0.45372832, 0.38127339, 0.42067075, - 0.25818801, 0.38612545, 0.18693292 - ], - [ - 0.96163845, 0.76473117, 0.74806261, 0.60980642, 0.9281404, - 0.47227204, 0.89254606, 0.20130682 - ], - [0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 0.], - ] - hyps_expected = [[1, 0, 0, 3, 4, 1, 3, 4], [1, 4, 4, 1, 1, 3, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]] - prev_hyps_expected = [[0, 1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 4, 1, 2, 1], - [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]] - - hyp_str_expected = """ - beam_id: 1 - ids: 1 - ids: 2 - scores: 0.25818801 - scores: 0.65319967 - atten_vecs { - prob: 0.38612545 - prob: 0.42067075 - prob: 0.84442794 - } - atten_vecs { - prob: 0.45298624 - prob: 0.53518069 - prob: 0.57700801 - } - """ - atten_probs_expected = [ - [ - [0.45372832, 0.86230338, 0.65504861], - [0.38612545, 0.42067075, 0.84442794], - [0.45372832, 0.86230338, 0.65504861], - [0.38612545, 0.42067075, 0.84442794], - [0.45372832, 0.86230338, 0.65504861], - [0.38612545, 0.42067075, 0.84442794], - [0.45372832, 0.86230338, 0.65504861], - [0.38612545, 0.42067075, 0.84442794], - ], - [ - [0.45372832, 0.86230338, 0.65504861], - [0.38612545, 0.42067075, 0.84442794], - [0.45372832, 0.86230338, 0.65504861], - [0.38612545, 0.42067075, 0.84442794], - [0.0532794, 0.53777719, 0.07609642], - [0.38612545, 0.42067075, 0.84442794], - [0.25818801, 0.03645897, 0.38127339], - [0.38612545, 0.42067075, 0.84442794], - ], - [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], - [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], - ] - - scores = [ - tf.random.uniform([b_size, num_classes], seed=12345), - tf.random.uniform([b_size, num_classes], seed=12346), - ] - init_atten_probs = tf.random.uniform([b_size, 3], seed=12345) - atten_probs = tf.zeros([seq_len, b_size, 3]) - done_hyps = self._testBeamSearchOpHelper( - b_size, num_beams, seq_len, 0., scores, init_atten_probs, atten_probs, - best_scores_expected, cum_scores_expected, scores_expected, - hyps_expected, prev_hyps_expected, atten_probs_expected) - - self._SameHyp(hyp_str_expected, done_hyps[1, 5]) - - # The following 3 tests, test each step of this decoding tree. - # Test that beam search finds the most probable sequence. - # These probabilities represent the following search - # - # G0 (0) - # / \ - # / \ - # / \ - # / \ - # 0(0.6) 1(0.4) - # / \ / \ - # / \ / \ - # / \ / \ - # 0(0.55) 1(0.45) 0(0.05) 1(0.95) - # - # and these decoding probabilities - # 000 - 0.6 * 0.55 = 0.33 - # 001 - 0.6 * 0.45 = 0.27 - # 010 - 0.4 * 0.05 = 0.02 - # 011 - 0.4 * 0.95 = 0.38 - # - # Greedy would decode 000 since the first 0 is the most probable, but beam - # should decode 011 since it's the highest probability then followed by 000. - def _test_single_step_small_vocab_3(self): - b_size = 2 - num_beams = 1 - seq_len = 3 - - probs = [np.log([[0.6, 0.4, 0.0000001], [0.6, 0.4, 0.0000001]])] - done_hyps = self._testBeamSearchOpHelper( - b_size, - num_beams, - seq_len, - _MIN_SCORE, - probs, - init_atten_probs=tf.zeros([b_size, 0]), - atten_probs=np.zeros([seq_len, b_size, 0]), - best_scores_expected=[_MIN_SCORE], - cum_scores_expected=np.log([0.6, 0.4]), - scores_expected=[np.log([0.6, 0.4]), [0, 0], [0, 0]], - hyps_expected=[[0, 1], [0, 0], [0, 0]], - prev_hyps_expected=[[0, 0], [0, 0], [0, 0]], - atten_probs_expected=np.zeros([seq_len, b_size, 0])) - - np.testing.assert_array_equal([['0', '0'], ['0', '0'], ['0', '0']], - done_hyps) - - def test_two_steps_small_vocab_3(self): - b_size = 2 - num_beams = 1 - seq_len = 3 - - probs = [ - np.log([[0.6, 0.4, 0.0000001], [0.6, 0.4, 0.0000001]]), - np.log([[0.55, 0.45, 0.0000001], [0.05, 0.95, 0.0000001]]), - ] - done_hyps = self._testBeamSearchOpHelper( - b_size, - num_beams, - seq_len, - _MIN_SCORE, - probs, - init_atten_probs=tf.zeros([b_size, 0]), - atten_probs=np.zeros([seq_len, b_size, 0]), - best_scores_expected=[_MIN_SCORE], - # Note, probabilites are swapped due to beams being swapped. - cum_scores_expected=np.log([0.4 * 0.95, 0.6 * 0.55]), - scores_expected=[np.log([0.6, 0.4]), - np.log([0.95, 0.55]), [0, 0]], - hyps_expected=[[0, 1], [1, 0], [0, 0]], - prev_hyps_expected=[[0, 0], [1, 0], [0, 0]], - atten_probs_expected=np.zeros([seq_len, b_size, 0])) - - np.testing.assert_array_equal([[b'0', b'0'], [b'0', b'0'], [b'0', b'0']], - done_hyps) - - def test_three_steps_eos(self): - b_size = 2 - num_beams = 1 - seq_len = 3 - - probs = [ - np.log([[0.6, 0.4, 0.0000001], [0.6, 0.4, 0.0000001]]), - np.log([[0.55, 0.45, 0.0000001], [0.05, 0.95, 0.0000001]]), - # Finish the beams with EOS - np.log([[0.05, 0.05, 0.9], [0.05, 0.05, 0.9]]), - ] - - done_hyps = self._testBeamSearchOpHelper( - b_size, - num_beams, - seq_len, - _MIN_SCORE, - probs, - init_atten_probs=tf.zeros([b_size, 0]), - atten_probs=np.zeros([seq_len, b_size, 0]), - best_scores_expected=np.log([0.4 * 0.95 * 0.9]), - cum_scores_expected=np.log([0.4 * 0.95 * 0.05, 0.4 * 0.95 * 0.05]), - scores_expected=[ - np.log([0.6, 0.4]), - np.log([0.95, 0.55]), - np.log([0.05, 0.05]) - ], - hyps_expected=[[0, 1], [1, 0], [0, 1]], - prev_hyps_expected=[[0, 0], [1, 0], [0, 0]], - atten_probs_expected=np.zeros([seq_len, b_size, 0])) - - expected_for_beam_0 = """ - beam_id: 0 - ids: 1 - ids: 1 - ids: 2 - scores: -0.916290700436 # = log 0.4 - scores: -0.0512933060527 # = log 0.95 - scores: -0.105360545218 # = log 0.9 - atten_vecs { - } - atten_vecs { - } - atten_vecs { - } - """ - - expected_for_beam_1 = """ - beam_id: 0 - ids: 0 - ids: 0 - ids: 2 - scores: -0.510825574398 # = log 0.6 - scores: -0.597836971283 # = log 0.55 - scores: -0.105360545218 # = log 0.9 - atten_vecs { - } - atten_vecs { - } - atten_vecs { - } - """ - - self._SameHyp(expected_for_beam_0, done_hyps[2, 0]) - self._SameHyp(expected_for_beam_1, done_hyps[2, 1]) - - def test_three_steps_force_eos(self): - b_size = 2 - num_beams = 1 - seq_len = 3 - - probs = [ - np.log([[0.6, 0.4, 0.0000001], [0.6, 0.4, 0.0000001]]), - np.log([[0.55, 0.45, 0.0000001], [0.05, 0.95, 0.0000001]]), - # EOS probability is still very low, so unless it is forced it will - # not be in the beam. - np.log([[0.45, 0.44, 0.01], [0.5, 0.5, 0.01]]), - ] - - # Set expected values - cum_scores_expected = np.log([0.4 * 0.95 * 0.45, 0.4 * 0.95 * 0.44]) - scores_expected = [ - np.log([0.6, 0.4]), - np.log([0.95, 0.55]), - np.log([0.45, 0.44]) - ] - hyps_expected = [[0, 1], [1, 0], [0, 1]] - prev_hyps_expected = [[0, 0], [1, 0], [0, 0]] - - # If force EOS is false, the we get empty hyps after beam search. - done_hyps = self._testBeamSearchOpHelper( - b_size, - num_beams, - seq_len, - _MIN_SCORE, - probs, - init_atten_probs=tf.zeros([b_size, 0]), - atten_probs=np.zeros([seq_len, b_size, 0]), - best_scores_expected=[_MIN_SCORE], - cum_scores_expected=cum_scores_expected, - scores_expected=scores_expected, - hyps_expected=hyps_expected, - prev_hyps_expected=prev_hyps_expected, - atten_probs_expected=np.zeros([seq_len, b_size, 0]), - force_eos_in_last_step=False) - np.testing.assert_array_equal([[b'0', b'0'], [b'0', b'0'], [b'0', b'0']], - done_hyps) - - # If force eos is true, we get valid results as in test_three_step_eos, - # but with lower probabilities (because of lower eos probs). - done_hyps = self._testBeamSearchOpHelper( - b_size, - num_beams, - seq_len, - _MIN_SCORE, - probs, - init_atten_probs=tf.zeros([b_size, 0]), - atten_probs=np.zeros([seq_len, b_size, 0]), - best_scores_expected=np.log([0.4 * 0.95 * 0.01]), - cum_scores_expected=cum_scores_expected, - scores_expected=scores_expected, - hyps_expected=hyps_expected, - prev_hyps_expected=prev_hyps_expected, - atten_probs_expected=np.zeros([seq_len, b_size, 0]), - force_eos_in_last_step=True) - - expected_for_beam_0 = """ - beam_id: 0 - ids: 1 - ids: 1 - ids: 2 - scores: -0.916290700436 # = log 0.4 - scores: -0.0512933060527 # = log 0.95 - scores: -4.605170185988 # = log 0.01 - atten_vecs { - } - atten_vecs { - } - atten_vecs { - } - """ - - expected_for_beam_1 = """ - beam_id: 0 - ids: 0 - ids: 0 - ids: 2 - scores: -0.510825574398 # = log 0.6 - scores: -0.597836971283 # = log 0.55 - scores: -4.605170185988 # = log 0.01 - atten_vecs { - } - atten_vecs { - } - atten_vecs { - } - """ - - self._SameHyp(expected_for_beam_0, done_hyps[2, 0]) - self._SameHyp(expected_for_beam_1, done_hyps[2, 1]) - - def _testBeamSearchStoppingHelper(self, - beam_size, - ensure_full_beam, - local_eos_threshold=-100): - b_size = 2 - num_beams = 1 - seq_len = 3 - probs = [ - # Only finish one beam with EOS. - np.log([[0.05, 0.05, 0.9], [0.05, 0.9, 0.05]]), - ] - - results = self._runBeamSearchOpHelper( - b_size, - num_beams, - seq_len, - _MIN_SCORE, - probs, - init_atten_probs=tf.zeros([b_size, 0]), - atten_probs=np.zeros([seq_len, b_size, 0]), - beam_size=beam_size, - ensure_full_beam=ensure_full_beam, - local_eos_threshold=local_eos_threshold) - all_done = results[7] - return all_done - - def test_beam_size_large(self): - # With default beam size, we are not yet all done, because we still have an - # active hyp within 3.0 of best done hyp. - all_done = self._testBeamSearchStoppingHelper(3.0, False) - self.assertEqual(False, all_done) - - def test_beam_size_small(self): - # With small beam size, we are all done, because the active hyp is not - # within such a narrow margin of best done hyp. - all_done = self._testBeamSearchStoppingHelper(0.1, False) - self.assertEqual(True, all_done) - - def test_ensure_full_beam(self): - # With small beam size and ensure_full_beam, we are _not_ yet done, - # because we require to have two done hyps before stopping, regardless of - # beam size. - all_done = self._testBeamSearchStoppingHelper(0.1, True) - self.assertEqual(False, all_done) - - def test_small_eos_threshold(self): - # With a small eos_threshold, we are done because the active hyp produced, - # , independent of small beam size. - all_done = self._testBeamSearchStoppingHelper(0.1, False, -100.0) - self.assertTrue(all_done) - - def test_large_eos_threshold(self): - # With larger eos_threshold, we are _not_ yet done, because we do not hit - # criteria we we require to have two done hyps before stopping, - # regardless of beam size. - all_done = self._testBeamSearchStoppingHelper(0.1, False, 0.01) - self.assertFalse(all_done) - - def _SameHyp(self, expected_hyp_str, real_serialized_hyp): - hyp1 = hyps_pb2.Hypothesis() - text_format.Parse(expected_hyp_str, hyp1) - hyp2 = hyps_pb2.Hypothesis() - hyp2.ParseFromString(real_serialized_hyp) - - self.assertEqual(hyp1.beam_id, hyp2.beam_id) - self.assertEqual(hyp1.ids, hyp2.ids) - self.assertNear(hyp1.normalized_score, hyp2.normalized_score, 1e-6) - self.assertAllClose(hyp1.scores, hyp2.scores) - self.assertEqual(len(hyp1.atten_vecs), len(hyp2.atten_vecs)) - for av1, av2 in zip(hyp1.atten_vecs, hyp2.atten_vecs): - self.assertAllClose(av1.prob, av2.prob) - - def testTopKTerminatedHypsOp(self): - with self.session(use_gpu=False): - b_size = 8 - num_beams = 2 - num_hyps_per_beam = b_size / num_beams - seq_len = 6 - scores = tf.random.uniform([b_size, 5], seed=12345) - atten_probs = tf.random.uniform([b_size, 3], seed=12345) - src_seq_lengths = [3, 3] - best_scores = tf.zeros([num_beams]) - cumulative_scores = tf.zeros([b_size]) - in_scores = tf.zeros([seq_len, b_size]) - in_hyps = tf.zeros([seq_len, b_size], dtype=tf.int32) - in_prev_hyps = tf.zeros([seq_len, b_size], dtype=tf.int32) - in_done_hyps = tf.as_string(tf.zeros([seq_len, b_size], dtype=tf.int32)) - in_atten_probs = tf.zeros([seq_len, b_size, 3]) - - (out_best_scores_0, out_cumulative_scores_0, out_scores_0, out_hyps_0, - out_prev_hyps_0, out_done_hyps_0, out_atten_probs_0, - _) = ops.beam_search_step( - scores, - atten_probs, - best_scores, - cumulative_scores, - in_scores, - in_hyps, - in_prev_hyps, - in_done_hyps, - in_atten_probs, [], - 0, - eos_id=2, - beam_size=3.0, - num_hyps_per_beam=num_hyps_per_beam) - - outputs = ops.beam_search_step( - scores, - atten_probs, - out_best_scores_0, - out_cumulative_scores_0, - out_scores_0, - out_hyps_0, - out_prev_hyps_0, - out_done_hyps_0, - out_atten_probs_0, [], - 1, - eos_id=2, - beam_size=3.0, - num_hyps_per_beam=num_hyps_per_beam) - - # Get the topk terminated hyps. - in_done_hyps = outputs[5] - topk_hyps = ops.top_k_terminated_hyps( - in_done_hyps, - src_seq_lengths, - k=2, - num_hyps_per_beam=num_hyps_per_beam, - length_normalization=0.2, - coverage_penalty=0.2, - target_seq_length_ratio=1.0) - seq_ids, seq_lens, seq_scores = ops.unpack_hyp( - tf.reshape(topk_hyps, [-1]), max_seq_length=5) - - k1, k2, k3, k4 = self.evaluate([topk_hyps, seq_ids, seq_lens, seq_scores]) - print(np.array_repr(k1)) - assert k1.size == 4 - - expected_top1_for_beam_0 = """ - beam_id: 0 - ids: 1 - ids: 2 - scores: 0.86230338 - scores: 0.65504861 - atten_vecs { - prob: 0.45372832 - prob: 0.86230338 - prob: 0.65504861 - } - atten_vecs { - prob: 0.45372832 - prob: 0.86230338 - prob: 0.65504861 - } - normalized_score: 1.002714 - """ - expected_top2_for_beam_1 = """ - beam_id: 1 - ids: 3 - ids: 2 - scores: 0.38127339 - scores: 0.57700801 - atten_vecs { - prob: 0.38612545 - prob: 0.42067075 - prob: 0.84442794 - } - atten_vecs { - prob: 0.18693292 - prob: 0.17821217 - prob: 0.66380036 - } - normalized_score: 0.480028 - """ - self._SameHyp(expected_top1_for_beam_0, k1[0, 0]) - self._SameHyp(expected_top2_for_beam_1, k1[1, 1]) - - self.assertAllClose( - k2, - [[1, 2, 0, 0, 0], [4, 2, 0, 0, 0], [4, 2, 0, 0, 0], [3, 2, 0, 0, 0]]) - self.assertAllClose(k3, [2, 2, 2, 2]) - self.assertAllClose(k4, [1.002714, 0.684296, 0.522484, 0.480028]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/beam_search_step_op_top_k_test.cc b/lingvo/core/ops/beam_search_step_op_top_k_test.cc deleted file mode 100644 index e4b906cc..00000000 --- a/lingvo/core/ops/beam_search_step_op_top_k_test.cc +++ /dev/null @@ -1,172 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include -#include "lingvo/core/ops/beam_search_step_op_kernels.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -using ::testing::Eq; -using ::testing::FloatEq; -using ::testing::FloatNear; -using ::testing::SizeIs; - -template -void Populate(T* top_k) { - // Add 3 distinct hyps. - float bottom_of_topk; - // Hyp struct consists of: - // beam_id, hyp_id, word_id, local_score, global_score, prev_ids - bottom_of_topk = top_k->Add({0, 2, 3, -0.4, -2.0, {1, 2, 3}}); - EXPECT_THAT(bottom_of_topk, FloatEq(std::numeric_limits::lowest())); - bottom_of_topk = top_k->Add({0, 1, 8, -0.7, -1.3, {1, 7, 2}}); - EXPECT_THAT(bottom_of_topk, FloatEq(std::numeric_limits::lowest())); - bottom_of_topk = top_k->Add({0, 6, 2, -0.1, -1.7, {1, 9, 5}}); - // No resize yet, since we haven't gotten to k * 2 = 4 elements yet. - EXPECT_THAT(bottom_of_topk, FloatEq(std::numeric_limits::lowest())); - bottom_of_topk = top_k->Add({0, 4, 3, -0.5, -2.5, {1, 2, 4}}); - // After fourth element we resize down to two best elements. - EXPECT_THAT(bottom_of_topk, FloatNear(-1.7, 0.001)); - - const auto& hyps = top_k->Get(); - EXPECT_THAT(hyps, SizeIs(2)); - EXPECT_THAT(hyps[0].hyp_id, Eq(1)); - EXPECT_THAT(hyps[0].global_score, FloatNear(-1.3, 0.001)); - EXPECT_THAT(hyps[1].hyp_id, Eq(6)); - EXPECT_THAT(hyps[1].global_score, FloatNear(-1.7, 0.001)); - - // Add a dupe. - top_k->Add({0, 5, 8, -0.7, -1.5, {1, 7, 2}}); -} - -template -void PopulateWithEpsilons(T* top_k) { - // Add 3 distinct hyps. - float bottom_of_topk; - bottom_of_topk = top_k->Add({0, 2, 3, -0.4, -2.0, {1, 2, 3}}); - EXPECT_THAT(bottom_of_topk, FloatEq(std::numeric_limits::lowest())); - bottom_of_topk = top_k->Add({0, 1, 8, -0.7, -1.3, {1, 7, 2}}); - EXPECT_THAT(bottom_of_topk, FloatEq(std::numeric_limits::lowest())); - bottom_of_topk = top_k->Add({0, 6, 2, -0.1, -1.7, {1, 3, 4, 9, 5}}); - // No resize yet, since we haven't gotten to k * 2 = 4 elements yet. - EXPECT_THAT(bottom_of_topk, FloatEq(std::numeric_limits::lowest())); - bottom_of_topk = top_k->Add({0, 4, 3, -0.5, -2.5, {1, 2, 4, 3}}); - // After fourth element we resize down to two best elements. - EXPECT_THAT(bottom_of_topk, FloatNear(-1.7, 0.001)); - - const auto& hyps = top_k->Get(); - EXPECT_THAT(hyps, SizeIs(2)); - EXPECT_THAT(hyps[0].hyp_id, Eq(1)); - EXPECT_THAT(hyps[0].global_score, FloatNear(-1.3, 0.001)); - EXPECT_THAT(hyps[1].hyp_id, Eq(6)); - EXPECT_THAT(hyps[1].global_score, FloatNear(-1.7, 0.001)); - - // Add a dupe. Doing the dedupe requires comparing last prev id of this hyp - // with cur id of candidate hyp since cur id for this hyp is epsilon. - const int epsilon_id = 0; - bottom_of_topk = top_k->Add({0, 5, epsilon_id, -0.7, -1.5, {1, 7, 2, 8}}); -} - -bool IsDupe(const Hyp& hyp1, const Hyp& hyp2) { - if (hyp1.word_id != hyp2.word_id) { - return false; - } - if (hyp1.prev_ids.size() != hyp2.prev_ids.size()) { - return false; - } - return std::equal(hyp1.prev_ids.begin(), hyp1.prev_ids.end(), - hyp2.prev_ids.begin()); -} - -// Tests that when we use the default Insert, there is NO deduping. -TEST(TopKTest, TestInsertDefault) { - const int k = 2; - TopK top_k(k, /* epsilon id */ -1); - Populate>(&top_k); - // Check contents: The two items in the TopK are duplicates. - const auto& new_hyps = top_k.Get(); - EXPECT_THAT(new_hyps, SizeIs(2)); - EXPECT_THAT(new_hyps[0].hyp_id, Eq(1)); - EXPECT_THAT(new_hyps[1].hyp_id, Eq(5)); - EXPECT_TRUE(IsDupe(new_hyps[0], new_hyps[1])); -} - -// Tests that when we use InsertHypWithEpsilonDedupe but set the epsilon id -// to be less than zero, there is NO deduping. -TEST(TopKTest, TestInsertNoDedupe) { - const int k = 2; - TopK top_k( - k, /* epsilon_id */ -1); - Populate< - TopK>( - &top_k); - // Check contents: The two items in the TopK are duplicates. - const auto& new_hyps = top_k.Get(); - EXPECT_THAT(new_hyps, SizeIs(2)); - EXPECT_THAT(new_hyps[0].hyp_id, Eq(1)); - EXPECT_THAT(new_hyps[1].hyp_id, Eq(5)); - EXPECT_TRUE(IsDupe(new_hyps[0], new_hyps[1])); -} - -// Tests that when we use InsertHypWithEpsilonDedupe and set the epsilon id -// to be greater than or equal to zero, there IS deduping. -TEST(TopKTest, TestInsertWithDedupe) { - const int k = 2; - const int epsilon_id = 0; - TopK top_k( - k, epsilon_id); - Populate< - TopK>( - &top_k); - // Check contents: The two items in the TopK are not duplicates. The dupe - // has been merged into one of them. - const auto& new_hyps = top_k.Get(); - EXPECT_THAT(new_hyps, SizeIs(2)); - EXPECT_THAT(new_hyps[0].hyp_id, Eq(1)); - // Merging has happened so combined global score is: - // log(exp(-1.5) + exp(-1.3)) - EXPECT_THAT(new_hyps[0].global_score, FloatNear(-0.70186, 0.001)); - EXPECT_THAT(new_hyps[1].hyp_id, Eq(6)); - EXPECT_THAT(new_hyps[1].global_score, FloatNear(-1.7, 0.001)); - EXPECT_TRUE(!IsDupe(new_hyps[0], new_hyps[1])); -} - -// Tests that the deduping ignores epsilons. -TEST(TopKTest, TestInsertWithDedupeEpsilon) { - const int k = 2; - const int epsilon_id = 0; - TopK top_k( - k, epsilon_id); - PopulateWithEpsilons< - TopK>( - &top_k); - // Check contents: The two items in the TopK are not duplicates. The dupe - // has been merged into one of them. - const auto& new_hyps = top_k.Get(); - EXPECT_THAT(new_hyps, SizeIs(2)); - EXPECT_THAT(new_hyps[0].hyp_id, Eq(1)); - // Merging has happened so combined global score is: - // log(exp(-1.5) + exp(-1.3)) - EXPECT_THAT(new_hyps[0].global_score, FloatNear(-0.70186, 0.001)); - EXPECT_THAT(new_hyps[1].hyp_id, Eq(6)); - EXPECT_THAT(new_hyps[1].global_score, FloatNear(-1.7, 0.001)); - EXPECT_TRUE(!IsDupe(new_hyps[0], new_hyps[1])); -} - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/best_step_op_kernels.cc b/lingvo/core/ops/best_step_op_kernels.cc deleted file mode 100644 index c5e38fec..00000000 --- a/lingvo/core/ops/best_step_op_kernels.cc +++ /dev/null @@ -1,169 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/summary.pb.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/io/buffered_inputstream.h" -#include "tensorflow/core/lib/io/random_inputstream.h" -#include "tensorflow/core/lib/io/record_reader.h" -#include "tensorflow/core/lib/strings/numbers.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/util/event.pb.h" - -namespace tensorflow { -namespace lingvo { -using ::tensorflow::io::RecordReader; -namespace { - -// Reads a text file containing 'step value' records, and finds the step that -// corresponds to the lowest-value record, within a given tolerance. -class BestStepOp : public OpKernel { - public: - explicit BestStepOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("hist_file", &hist_file_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("tol", &tol_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("minimize", &minimize_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("metric", &metric_)); - CHECK_GE(tol_, 0.0); - } - - void ExtractValueFromOneTfEvent(OpKernelContext* ctx, const string& filename, - std::map* step_value, - const string& metric, bool minimize) { - const Status status = ctx->env()->FileExists(filename); - if (status.ok()) { - ::std::unique_ptr file; - OP_REQUIRES_OK(ctx, ctx->env()->NewRandomAccessFile(filename, &file)); - ::std::unique_ptr reader(new RecordReader(file.get())); - - uint64 offset = 0; - tstring raw_proto; - while (reader->ReadRecord(&offset, &raw_proto).ok()) { - Event event; - CHECK(::tensorflow::ParseProtoUnlimited(&event, raw_proto.data(), - raw_proto.size())); - if (event.what_case() != Event::WhatCase::kSummary) { - continue; - } - if (event.has_summary()) { - for (const auto& value : event.summary().value()) { - // Look for the tag that matches the metric. - if (value.tag() == metric) { - if (minimize) { - step_value->insert( - std::pair(event.step(), value.simple_value())); - } else { - step_value->insert( - std::pair(event.step(), -value.simple_value())); - } - break; - } - } - } - } - } else { - LOG(WARNING) << "tf events file '" << filename << "' doesn't exist."; - } - } - - void ExtractValueFromTfEvents(OpKernelContext* ctx, const string& filename, - std::map* step_value) { - std::vector tf_events; - const Status status = ctx->env()->GetMatchingPaths(filename, &tf_events); - if (!tf_events.empty()) { - for (const auto& fname : tf_events) { - // Loop through all found tf events files. - ExtractValueFromOneTfEvent(ctx, fname, step_value, metric_, minimize_); - } - } else { - LOG(WARNING) << "Couldn't find tf events files that match pattern: '" - << filename; - } - } - - void ExtractValueFromTxt(OpKernelContext* ctx, const string& filename, - std::map* step_value) { - const Status status = ctx->env()->FileExists(filename); - if (status.ok()) { - std::unique_ptr file; - OP_REQUIRES_OK(ctx, ctx->env()->NewRandomAccessFile(filename, &file)); - std::unique_ptr input_stream( - new io::RandomAccessInputStream(file.get())); - io::BufferedInputStream in(input_stream.get(), 4 << 10); - string line; - while (true) { - const Status s = in.ReadLine(&line); - if (errors::IsOutOfRange(s)) break; - TF_CHECK_OK(s); - std::vector split_line = str_util::Split(line, ' '); - CHECK_EQ(split_line.size(), 2); - - int x; - CHECK(strings::safe_strto32(split_line[0], &x)); - float y; - CHECK(strings::safe_strtof(split_line[1], &y)); - - if (minimize_) { - step_value->insert(std::pair(x, y)); - } else { // Negate the value if it's the larger the better. - step_value->insert(std::pair(x, -y)); - } - } - } else { - LOG(WARNING) << "hist_file '" << &filename << "' doesn't exist."; - } - } - - void Compute(OpKernelContext* ctx) override { - int64 best_step = 0, last_step = 0; - float best_val = 0.0; - std::map step_value; - if (hist_file_.find("events.out.tfevents") != std::string::npos) { - // History file are tf events. - ExtractValueFromTfEvents(ctx, hist_file_, &step_value); - } else { // History file is a txt. - ExtractValueFromTxt(ctx, hist_file_, &step_value); - } - std::map::iterator itr; - for (itr = step_value.begin(); itr != step_value.end(); ++itr) { - last_step = itr->first; - const float val = itr->second; - if (best_step == 0 || val + tol_ < best_val) { - best_step = last_step; - best_val = val; - } - } - - Tensor* res; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({2}), &res)); - res->vec()(0) = best_step; - res->vec()(1) = last_step; - } - - private: - string hist_file_; - string metric_; - float tol_ = 0.0; - bool minimize_ = true; -}; - -REGISTER_KERNEL_BUILDER(Name("BestStep").Device(DEVICE_CPU), BestStepOp); - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/best_step_op_test.py b/lingvo/core/ops/best_step_op_test.py deleted file mode 100644 index 09df4383..00000000 --- a/lingvo/core/ops/best_step_op_test.py +++ /dev/null @@ -1,85 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for best_step_op.""" - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_helper -from lingvo.core import test_utils - -FLAGS = tf.flags.FLAGS - - -class BestStepOp(test_utils.TestCase): - - def _HistFile(self): - return test_helper.test_src_dir_path('core/ops/testdata/history.txt') - - def _BleuFile(self): - return test_helper.test_src_dir_path('core/ops/testdata/history_bleu.txt') - - def _TfEventFile(self): - return test_helper.test_src_dir_path( - 'core/ops/testdata/events.out.tfevents.test') - - def testTol0(self): - g = tf.Graph() - with g.as_default(): - output = ops.best_step(self._HistFile()) - with self.session(graph=g): - best_step, last_step = self.evaluate(output) - self.assertEqual(best_step, 42122) - self.assertEqual(last_step, 42792) - - def testTolNon0(self): - g = tf.Graph() - with g.as_default(): - output = ops.best_step(self._HistFile(), 0.1) - with self.session(graph=g): - best_step, last_step = self.evaluate(output) - self.assertEqual(best_step, 37553) - self.assertEqual(last_step, 42792) - - def testNoFile(self): - g = tf.Graph() - with g.as_default(): - output = ops.best_step('') - with self.session(graph=g): - best_step, last_step = self.evaluate(output) - self.assertEqual(best_step, 0) - self.assertEqual(last_step, 0) - - def testAscendingValTol0(self): - g = tf.Graph() - with g.as_default(): - output = ops.best_step(self._BleuFile(), 0.0, False) - with self.session(graph=g): - best_step, last_step = self.evaluate(output) - self.assertEqual(best_step, 41500) - self.assertEqual(last_step, 46800) - - def testTfEventAscendingValTol0(self): - g = tf.Graph() - with g.as_default(): - output = ops.best_step(self._TfEventFile(), 0.0, False, 'bleu/dev') - with self.session(graph=g): - best_step, last_step = self.evaluate(output) - self.assertEqual(best_step, 102600) - self.assertEqual(last_step, 185200) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/chain_record_yielder.cc b/lingvo/core/ops/chain_record_yielder.cc deleted file mode 100644 index 77922962..00000000 --- a/lingvo/core/ops/chain_record_yielder.cc +++ /dev/null @@ -1,77 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/chain_record_yielder.h" - -#include - -namespace tensorflow { -namespace lingvo { - -ChainRecordYielder::ChainRecordYielder( - const std::vector& yielder_options) - : current_yielder_idx_(0), - current_yielder_(nullptr), - yielder_options_(yielder_options) { - if (yielder_options.empty()) { - LOG(FATAL) << "There should be at least one set of options provided."; - } - current_yielder_ = BasicRecordYielder::New(yielder_options_.front()); -} - -ChainRecordYielder* ChainRecordYielder::New( - const std::vector& yielder_options) { - return new ChainRecordYielder(yielder_options); -} - -ChainRecordYielder::~ChainRecordYielder() {} - -void ChainRecordYielder::Close() { - { - absl::MutexLock l(&mu_); - if (current_yielder_) { - current_yielder_->Close(); - } - } - LOG(INFO) << this << "Chain record yielder exit"; - delete this; -} - -Status ChainRecordYielder::Yield(Record* record) { - absl::MutexLock l(&mu_); - - if (current_yielder_->current_epoch() > 1) { - int new_idx = (current_yielder_idx_ + 1) % yielder_options_.size(); - if (current_yielder_) { - current_yielder_->Close(); - } - current_yielder_idx_ = new_idx; - current_yielder_ = BasicRecordYielder::New(yielder_options_.at(new_idx)); - } - while (true) { - // Retry indefinitely until we get an Ok status from the specific yielder. - // This will stall the training if there is any unrecoverable error with - // the child yielder. - Status s = current_yielder_->Yield(record); - if (!s.ok()) { - LOG(WARNING) << s; - continue; - } - return s; - } -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/chain_record_yielder.h b/lingvo/core/ops/chain_record_yielder.h deleted file mode 100644 index b3223a95..00000000 --- a/lingvo/core/ops/chain_record_yielder.h +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ -#ifndef LINGVO_CORE_OPS_CHAIN_RECORD_YIELDER_H_ -#define LINGVO_CORE_OPS_CHAIN_RECORD_YIELDER_H_ - -#include "lingvo/core/ops/record_yielder.h" - -namespace tensorflow { -namespace lingvo { - -// ChainRecordYielder is a special RecordYielder that consecutively yields -// examples based on a list of yielder options consuming those options in order. -// -// Usage example: -// BasicRecordYielder::Options opts1; -// opts1.file_pattern = ; -// opts1.seed = 301; -// opts1.bufsize = 1000000; // A randomized buffer with 1M records. -// opts1.parallelism = 8; // Use 8 iterators. -// BasicRecordYielder::Options opts2; -// opts2.file_pattern = ; -// opts2.seed = 301; -// opts2.bufsize = 1000000; // A randomized buffer with 1M records. -// opts2.parallelism = 8; // Use 8 iterators. -// // Yield all records defined by opts1, then all records defined by opts2. -// ChainRecordYielder* yielder = ChainRecordYielder::New({opts1, opts2}); -// Record record; -// while (true) { -// yielder->Yield(&record); -// // process record. -// } -// yielder->Close(); -// -// ChainRecordYielder can be accessed by multiple threads concurrently. -class ChainRecordYielder : public RecordYielder { - public: - ~ChainRecordYielder() override; - void Close() override; - Status Yield(Record* record) override; - - // Creates new ChainRecordYielder that will be creating child yielders using - // options provided. Caller is responsible for closing the ChainRecordYielder - // returned by this function. Caller should not delete the yielder as it will - // be handled internally. - static ChainRecordYielder* New( - const std::vector& yielder_options); - - protected: - ChainRecordYielder( - const std::vector& yielder_options); - - private: - mutable absl::Mutex mu_; - int current_yielder_idx_ ABSL_GUARDED_BY(mu_); - BasicRecordYielder* current_yielder_ ABSL_GUARDED_BY(mu_); - - std::vector yielder_options_; -}; - -} // namespace lingvo -} // namespace tensorflow -#endif // LINGVO_CORE_OPS_CHAIN_RECORD_YIELDER_H_ diff --git a/lingvo/core/ops/chain_record_yielder_test.cc b/lingvo/core/ops/chain_record_yielder_test.cc deleted file mode 100644 index eee88191..00000000 --- a/lingvo/core/ops/chain_record_yielder_test.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/chain_record_yielder.h" - -#include - -#include -#include -#include "lingvo/core/ops/input_common.h" -#include "lingvo/core/ops/record_yielder.h" -#include "lingvo/core/ops/yielder_test_helper.h" -#include "tensorflow/core/lib/core/stringpiece.h" -#include "tensorflow/core/lib/io/path.h" -#include "tensorflow/core/lib/strings/stringprintf.h" -#include "tensorflow/core/platform/env.h" - -namespace tensorflow { -namespace lingvo { - -TEST(RecordYielderTest, ChainBasicTest) { - const int N = 10; - const int M = 1000; - GeneratePlainTextTestData("chain_yielder1", N, M); - GeneratePlainTextTestData("chain_yielder2", N, M); - - BasicRecordYielder::Options opts1; - opts1.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", - "chain_yielder1.*")); - opts1.seed = 301; - opts1.bufsize = 2000; - opts1.parallelism = 1; - - BasicRecordYielder::Options opts2; - opts2.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", - "chain_yielder2.*")); - opts2.seed = 301; - opts2.bufsize = 2000; - opts2.parallelism = 1; - ChainRecordYielder* yielder = ChainRecordYielder::New({opts1, opts2}); - - std::vector vals; - Record record; - record.source_id = kDefaultSourceId; - // Consume first yielder entirely. - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - } - ASSERT_NEAR(ComputeInputSourceDistribution(vals)["chain_yielder1"], - 1.0, 0.001); - vals.clear(); - // Consume second yielder entirely. - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - } - ASSERT_NEAR(ComputeInputSourceDistribution(vals)["chain_yielder2"], - 1.0, 0.001); - vals.clear(); - // Consume first yielder again. - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - } - ASSERT_NEAR(ComputeInputSourceDistribution(vals)["chain_yielder1"], - 1.0, 0.001); - - yielder->Close(); -} - -TEST(RecordYielderDeathTest, ChainNoYielders) { - ASSERT_DEATH(ChainRecordYielder::New({}), - "There should be at least one set of options provided"); -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/functional_ops_kernels.cc b/lingvo/core/ops/functional_ops_kernels.cc deleted file mode 100644 index bdb4e8ff..00000000 --- a/lingvo/core/ops/functional_ops_kernels.cc +++ /dev/null @@ -1,127 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include - -#include "absl/synchronization/mutex.h" -#include "tensorflow/core/common_runtime/function.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/platform/thread_annotations.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -typedef FunctionLibraryRuntime::Handle FHandle; - -void SetRunOptions(OpKernelContext* ctx, FunctionLibraryRuntime::Options* opts, - bool always_collect_stats) { - opts->rendezvous = ctx->rendezvous(); - opts->cancellation_manager = ctx->cancellation_manager(); - if (always_collect_stats) { - opts->stats_collector = ctx->stats_collector(); - } - opts->runner = ctx->runner(); -} - -class CachedCallOp : public AsyncOpKernel { - public: - explicit CachedCallOp(OpKernelConstruction* ctx) - : AsyncOpKernel(ctx), not_initing_(this, &ME::NotIniting) { - flib_ = ctx->function_library(); - OP_REQUIRES(ctx, flib_ != nullptr, errors::Internal("No function library")); - const NameAttrList* func; - OP_REQUIRES_OK(ctx, ctx->GetAttr("f", &func)); - OP_REQUIRES_OK(ctx, flib_->Instantiate(func->name(), - AttrSlice(&func->attr()), &handle_)); - } - - ~CachedCallOp() override {} - - void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override { - mu_.Lock(); - - while (true) { - // First call. - if (state_ == UNINIT) { - break; - } - - // Has called f and cached the result. - if (state_ == INITED) { - ctx->SetStatus(status_); - for (int i = 0; i < rets_.size(); ++i) { - ctx->set_output(i, rets_[i]); - } - mu_.Unlock(); - done(); - return; - } - - // Another call is being executed. - mu_.Await(not_initing_); - } - - state_ = INITING; - mu_.Unlock(); - - // Call f once and cache the result. - SetRunOptions(ctx, &opts_, true /* always_collect_stats */); - flib_->Run(opts_, handle_, args_, &rets_, - // Done callback - [this, ctx, done](Status s) { - ctx->SetStatus(s); - for (int i = 0; i < rets_.size(); ++i) { - ctx->set_output(i, rets_[i]); - } - done(); - - absl::MutexLock l(&mu_); - status_ = s; - state_ = INITED; - }); - } - - private: - typedef CachedCallOp ME; - - bool NotIniting() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - return state_ != INITING; - } - - FunctionLibraryRuntime* flib_ = nullptr; - FunctionLibraryRuntime::Options opts_; - FHandle handle_; - - absl::Mutex mu_; - absl::Condition not_initing_; - enum State { - UNINIT, - INITING, - INITED, - }; - State state_ = UNINIT; - Status status_; - std::vector args_; - std::vector rets_; -}; - -REGISTER_KERNEL_BUILDER(Name("CachedCall").Device(DEVICE_CPU), CachedCallOp); - -} // namespace -} // end namespace lingvo -} // end namespace tensorflow diff --git a/lingvo/core/ops/functional_ops_test.py b/lingvo/core/ops/functional_ops_test.py deleted file mode 100644 index 23449ac1..00000000 --- a/lingvo/core/ops/functional_ops_test.py +++ /dev/null @@ -1,55 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for functional ops.""" - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_utils -import numpy as np - - -class FunctionalOpsTest(test_utils.TestCase): - - def testCachedCall(self): - # A generator returns different values for each invocation. - def Gen(): - for i in range(1, 1000): - yield np.array([[0, i], [i, 0]]).astype(np.float32), np.array( - [[i, 0], [0, -i]]).astype(np.float32) - - it = Gen() - - # Wraps gen() in a defun. - @tf.function(autograph=False) - def MyFn(): - return tf.py_func(lambda: next(it), [], [tf.float32, tf.float32]) - - # A graph calls MyFn via CachedCall. - g = tf.Graph() - with g.as_default(): - fn = MyFn.get_concrete_function() - fn.add_to_graph() - u, v = ops.cached_call(fn, [tf.float32, tf.float32]) - - with self.session(graph=g): - for _ in range(10): - x, y = self.evaluate([u, v]) - self.assertAllEqual(x, [[0, 1], [1, 0]]) - self.assertAllEqual(y, [[1, 0], [0, -1]]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/generic_input_op_kernels.cc b/lingvo/core/ops/generic_input_op_kernels.cc deleted file mode 100644 index 7143b90f..00000000 --- a/lingvo/core/ops/generic_input_op_kernels.cc +++ /dev/null @@ -1,317 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include - -#include "tensorflow/core/lib/strings/strcat.h" -#include "lingvo/core/ops/input_common.h" -#include "tensorflow/core/common_runtime/function.h" -#include "tensorflow/core/framework/function.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/framework/types.h" -#include "tensorflow/core/lib/core/threadpool.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/util/work_sharder.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -typedef std::function Closure; -typedef std::function Runner; - -// ThreadLocalRunner::PerThread() is a thread local object which owns a thread -// pool with one thread. That thread is configured to disable as much -// TensorFlow runtime parallelism as we can. -// -// NOTE: Maybe a cpu-local object will work better, and the thread in -// ThreadLocalRunner can be affined to one cpu. -class ThreadLocalRunner { - public: - static ThreadLocalRunner& PerThread() { - thread_local ThreadLocalRunner tl_runner; - return tl_runner; - } - - ThreadLocalRunner() : pool_(Env::Default(), "single", 1) { - runner_ = [this](Closure c) { pool_.Schedule(Wrapper(c)); }; - } - - Runner* runner() { return &runner_; } - - private: - thread::ThreadPool pool_; - Runner runner_; - - class Wrapper : Closure { - public: - explicit Wrapper(Closure c) : c_(std::move(c)) {} - - void operator()() const { - ScopedPerThreadMaxParallelism scope(1); - c_(); - } - - private: - Closure c_; - }; -}; - -class GenericInputProcessor : public RecordProcessor { - public: - explicit GenericInputProcessor(OpKernelConstruction* ctx) { - auto flib = ctx->function_library(); - OP_REQUIRES(ctx, flib != nullptr, errors::Internal("No function library")); - OP_REQUIRES_OK(ctx, flib->Clone(&fld_, &pflr_, &flib_)); - const NameAttrList* func; - OP_REQUIRES_OK(ctx, ctx->GetAttr("processor", &func)); - OP_REQUIRES_OK(ctx, flib_->Instantiate(func->name(), - AttrSlice(&func->attr()), &handle_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("num_threads", &num_merger_threads_)); - num_merger_threads_ = std::max(4, num_merger_threads_ / 4); // An estimate. - merger_ = new thread::ThreadPool( - Env::Default(), ThreadOptions(), "generic_input_merger", - num_merger_threads_, /* low_latency_hint */ false); - merger_runner_ = [this](Closure c) { merger_->Schedule(c); }; - OP_REQUIRES_OK(ctx, ctx->GetAttr("dynamic_padding_dimensions", - &dynamic_padding_dimensions_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("dynamic_padding_constants", - &dynamic_padding_constants_)); - } - - ~GenericInputProcessor() { delete merger_; } - - Status Process(const Record& record, int64* bucket_key, - TensorVec* sample) override { - // We expect that this input processor is used in conjunction with - // RecordBatcher, which uses multiple threads to call this input - // processor's Process(). Therefore, there is not much need for - // processing each individual record using multiple threads - // (tf_compute). - FunctionLibraryRuntime::Options opts; - // Create a step container that uses resource manager to cleanup state - // after the step is complete. - ScopedStepContainer step_container( - step_id_counter_.fetch_add(1), - [this](const string& name) { - auto status = flib_->device()->resource_manager()->Cleanup(name); - if (!status.ok()) { - LOG(ERROR) << "Error cleaning up resources:" << status; - } - }, - "GenericInputProcessor"); - opts.step_container = &step_container; - opts.runner = ThreadLocalRunner::PerThread().runner(); - - // Generates pair as the resulting Tensors. - TensorVec args(2); - args[0] = Tensor(DT_INT32, {}); - args[0].scalar()() = record.source_id; - args[1] = Tensor(DT_STRING, {}); - args[1].scalar()().append(std::string(record.value)); - *bucket_key = 1; - sample->clear(); - Status status; - Notification done; - flib_->Run(opts, handle_, args, sample, [&](const Status& s) { - status = s; - done.Notify(); - }); - done.WaitForNotification(); - TF_RETURN_IF_ERROR(status); - if (sample->size() < 2) { - LOG(FATAL) - << "Generic input processor must return at least 2 tensors. but got " - << sample->size(); - } - const auto& bucket_key_tensor = (*sample)[sample->size() - 1]; - if (bucket_key_tensor.dtype() != DT_INT32 || - !TensorShapeUtils::IsScalar(bucket_key_tensor.shape())) { - LOG(FATAL) << "Bucket key tensor is not an int32 scalar: " - << DataTypeString(bucket_key_tensor.dtype()); - } - *bucket_key = bucket_key_tensor.scalar()(); - if (*bucket_key < 0) { - return tensorflow::errors::Cancelled( - strings::StrCat("Batch has negative bucket key: ", *bucket_key)); - } - sample->pop_back(); - return Status::OK(); - } - - Status Merge(int64 bucket_size, const std::vector& samples, - TensorVec* batch) override { - CHECK(!samples.empty()); - const auto num_samples = samples.size(); - const auto num_outs = samples[0].size(); - - std::vector padded_samples(samples.begin(), samples.end()); - if (!dynamic_padding_dimensions_.empty()) { - CHECK(dynamic_padding_dimensions_.size() == num_outs); - CHECK(dynamic_padding_constants_.size() == num_outs); - - for (int j = 0; j < num_outs; ++j) { - const int pad_dim = dynamic_padding_dimensions_[j]; - if (pad_dim == -1) { - continue; - } - const int pad_value = dynamic_padding_constants_[j]; - - int64 max_length = 0; - for (int i = 0; i < samples.size(); ++i) { - max_length = std::max(max_length, samples[i][j].dim_size(pad_dim)); - } - - for (int i = 0; i < samples.size(); ++i) { - const auto& src = samples[i][j]; - if (src.dims() > 0 && src.dim_size(pad_dim) < max_length) { - DataType dtype = src.dtype(); - TensorShape dst_shape(src.shape()); - dst_shape.set_dim(pad_dim, max_length); - Tensor dst(dtype, dst_shape); - switch (dtype) { -#define CASE(T) \ - case DataTypeToEnum::value: \ - dst.flat().setConstant(pad_value); \ - if (src.NumElements() > 0) { \ - auto src_t = src.flat_inner_outer_dims(pad_dim - 1); \ - auto dst_t = dst.flat_inner_outer_dims(pad_dim - 1); \ - typedef Eigen::DSizes DSizes; \ - dst_t.slice(DSizes(), DSizes(src_t.dimensions())) = src_t; \ - } \ - break - - CASE(float); - CASE(int32); - CASE(int64); -#undef CASE - default: - LOG(FATAL) << "Unexpected " << DataTypeString(dtype); - } - std::swap(padded_samples[i][j], dst); - } - } - } - } - - // Validate that samples can be merged: samples[:][i] has the same - // type and shape. - for (int i = 1; i < padded_samples.size(); ++i) { - if (padded_samples[i].size() != num_outs) { - LOG(FATAL) << "Samples have different sizes: " << samples[i].size() - << " vs. " << num_outs; - } - for (int j = 0; j < num_outs; ++j) { - if (padded_samples[i][j].dtype() != padded_samples[0][j].dtype()) { - LOG(FATAL) << "Mismatch data types of samples (" << i << "/" << j - << "): " << samples[i][j].dtype() << " vs. " - << samples[0][j].dtype(); - } - if (padded_samples[i][j].shape() != padded_samples[0][j].shape()) { - LOG(FATAL) << "Mismatch shape of samples (" << i << "/" << j - << "): " << samples[i][j].shape().DebugString() << " vs. " - << samples[0][j].shape().DebugString(); - } - } - } - - batch->clear(); - for (int i = 0; i < num_outs; ++i) { - const Tensor& src = padded_samples[0][i]; - DataType dtype = src.dtype(); - switch (dtype) { - case DT_FLOAT: - case DT_UINT8: - case DT_INT32: - case DT_INT64: - case DT_STRING: - case DT_BFLOAT16: - case DT_COMPLEX64: - case DT_COMPLEX128: - case DT_BOOL: - break; - default: - LOG(FATAL) << DataTypeString(dtype) << " is not supported."; - } - TensorShape shape = src.shape(); - shape.InsertDim(0, num_samples); - // The merged tensor is 1-rank higher and its 1st dimension - // is the num_samples. - if (num_samples == 1) { - // Avoid memcpy if there is just one sample. - Tensor reshaped(dtype); - CHECK(reshaped.CopyFrom(src, shape)); - batch->push_back(reshaped); - } else { - batch->push_back(Tensor(dtype, shape)); - } - } - // If there is just one sample, 'batch' already has the copy. - if (num_samples == 1) return Status::OK(); - - Sharder::Do(num_samples /* total */, 1000 /* cost_per_unit */, - [&](int64 start, int64 limit) { - for (int i = 0; i < num_outs; ++i) { - DataType dtype = padded_samples[0][i].dtype(); - Tensor* merged = &(*batch)[i]; - for (int j = start; j < limit; ++j) { - switch (dtype) { -#define CASE(T) \ - case DataTypeToEnum::value: \ - merged->flat_outer_dims().chip<0>(j) = padded_samples[j][i].flat(); \ - break - CASE(float); - CASE(int32); - CASE(int64); - CASE(tstring); - CASE(uint8); - CASE(bfloat16); - CASE(complex64); - CASE(complex128); - CASE(bool); -#undef CASE - default: - LOG(FATAL) << "Unexpected " << DataTypeString(dtype); - } - } - } - }, - merger_runner_, 1 + num_merger_threads_); - return Status::OK(); - } - - private: - std::unique_ptr fld_; - std::unique_ptr pflr_; - FunctionLibraryRuntime* flib_ = nullptr; // Not owned. - FunctionLibraryRuntime::Handle handle_; - std::atomic_int_fast64_t step_id_counter_; - - int num_merger_threads_ = -1; - thread::ThreadPool* merger_ = nullptr; - Runner merger_runner_; - std::vector dynamic_padding_dimensions_; - std::vector dynamic_padding_constants_; - - TF_DISALLOW_COPY_AND_ASSIGN(GenericInputProcessor); -}; - -REGISTER_KERNEL_BUILDER(Name("GenericInput").Device(DEVICE_CPU), - InputOp); -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/hyps.proto b/lingvo/core/ops/hyps.proto deleted file mode 100644 index d34da4f9..00000000 --- a/lingvo/core/ops/hyps.proto +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -syntax = "proto2"; - -package tensorflow.lingvo; - -message Hypothesis { - // The beam to which this hyp belongs. - optional int32 beam_id = 1; - repeated int32 ids = 2; - repeated float scores = 3; - - message AttenVec { - repeated float prob = 1; - } - repeated AttenVec atten_vecs = 4; - optional float normalized_score = 5; -} diff --git a/lingvo/core/ops/input_common.cc b/lingvo/core/ops/input_common.cc deleted file mode 100644 index 9b99a1ec..00000000 --- a/lingvo/core/ops/input_common.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/input_common.h" - -#include "lingvo/core/ops/sequential_record_yielder.h" -#include "lingvo/core/ops/chain_record_yielder.h" -#include "lingvo/core/ops/weighted_mix_record_yielder.h" - -namespace tensorflow { -namespace lingvo { - -RecordYielder* ConstructYielder(const string& file_pattern, - const std::vector& input_source_weights, - int64 file_random_seed, int64 file_buffer_size, - int64 file_buffer_size_in_seconds, - int64 file_parallelism, - bool require_sequential_order, - int64 repeat_count, bool use_chaining) { - std::vector file_patterns; - if (input_source_weights.empty()) { - LOG(INFO) << "Input source weights are empty, fall back to legacy " - << "behavior."; - file_patterns.push_back(file_pattern); - } else { - file_patterns = str_util::Split(file_pattern, ','); - CHECK_EQ(file_patterns.size(), input_source_weights.size()) - << "There should be exactly one " - << "input_source_weight per coma-separated value " - << "in file_pattern."; - } - if (require_sequential_order) { - CHECK_EQ(file_patterns.size(), 1) - << "require_sequential_order does not support record mixing or " - << "chaining."; - return SequentialRecordYielder::New(file_patterns.front(), repeat_count); - } else { - CHECK_EQ(repeat_count, -1) << "Repeat count must not be set unless " - "require_sequential_order is true."; - } - std::vector yielder_options; - - for (int i = 0; i < file_patterns.size(); ++i) { - BasicRecordYielder::Options yopts; - yopts.file_pattern = file_patterns[i]; - if (file_random_seed == 0) { - yopts.seed = 0; // Let the yielder pick a random seed. - } else { - yopts.seed = - (file_random_seed + i) % (std::numeric_limits::max() - 1); - if (yopts.seed == 0) { - // Add 1 to avoid 0. - ++yopts.seed; - } - } - yopts.bufsize = file_buffer_size; - yopts.bufsize_in_seconds = file_buffer_size_in_seconds; - yopts.parallelism = file_parallelism; - yopts.source_id = i; - yielder_options.push_back(yopts); - } - - RecordYielder* yielder = nullptr; - if (yielder_options.size() == 1) { - yielder = BasicRecordYielder::New(yielder_options.front()); - } else if (use_chaining) { - yielder = ChainRecordYielder::New(yielder_options); - } else { - std::vector yielders; - yielders.reserve(yielder_options.size()); - for (const auto& yopts : yielder_options) { - yielders.push_back(BasicRecordYielder::New(yopts)); - } - yielder = WeightedMixRecordYielder::New( - file_random_seed, - yielders, - input_source_weights); - } - return yielder; -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/input_common.h b/lingvo/core/ops/input_common.h deleted file mode 100644 index cd16ecf9..00000000 --- a/lingvo/core/ops/input_common.h +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_INPUT_COMMON_H_ -#define LINGVO_CORE_OPS_INPUT_COMMON_H_ - -#include - -#include "lingvo/core/ops/record_batcher.h" -#include "lingvo/core/ops/record_yielder.h" -#include "tensorflow/core/framework/op_kernel.h" - -namespace tensorflow { -namespace lingvo { - -// Constructs single Yielder for a given file pattern or mixes multiple yielders -// with weights. -RecordYielder* ConstructYielder(const string& file_pattern, - const std::vector& input_source_weights, - int64 file_random_seed, int64 file_buffer_size, - int64 file_buffer_size_in_seconds, - int64 file_parallelism, - bool require_sequential_order, - int64 repeat_count, bool use_chaining); - -// Base class for op kernels that emit training examples. -template -class InputOp : public OpKernel { - static_assert( - std::is_base_of::value, - "InputOp requires a RecordProcessor subclass as the template arg."); - - public: - explicit InputOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - typedef std::vector Int64Vec; -#define GETATTR(TYPE, FIELD) \ - TYPE FIELD; \ - OP_REQUIRES_OK(ctx, ctx->GetAttr(#FIELD, &FIELD)); - - GETATTR(string, file_pattern); - GETATTR(std::vector, input_source_weights); - GETATTR(int64, file_random_seed); - GETATTR(int64, file_buffer_size); - GETATTR(int64, file_buffer_size_in_seconds); - GETATTR(int64, file_parallelism); - GETATTR(Int64Vec, bucket_upper_bound); - GETATTR(Int64Vec, bucket_batch_limit); - GETATTR(int64, bucket_adjust_every_n); - GETATTR(int64, flush_every_n); - GETATTR(int64, num_threads); - GETATTR(bool, require_sequential_order); - GETATTR(int64, repeat_count); - GETATTR(bool, use_chaining); -#undef GETATTR - OP_REQUIRES( - ctx, - std::is_sorted(bucket_upper_bound.begin(), bucket_upper_bound.end()), - errors::InvalidArgument("Bucket_upper_bound is not sorted")); - if (require_sequential_order) { - num_threads = 1; - } - LOG(INFO) << "Create RecordProcessor"; - processor_ = new RecordProcessorClass(ctx); - RecordYielder* yielder = CHECK_NOTNULL(ConstructYielder( - file_pattern, input_source_weights, file_random_seed, file_buffer_size, - file_buffer_size_in_seconds, file_parallelism, require_sequential_order, - repeat_count, use_chaining)); - LOG(INFO) << "Create batcher"; - RecordBatcher::Options bopts; - bopts.bucket_upper_bound = bucket_upper_bound; - bopts.bucket_batch_limit = bucket_batch_limit; - bopts.bucket_adjust_every_n = bucket_adjust_every_n; - bopts.flush_every_n = flush_every_n; - bopts.num_threads = num_threads; - batcher_ = new RecordBatcher(bopts, yielder, processor_); - } - - ~InputOp() override { delete batcher_; } - - void Compute(OpKernelContext* ctx) override { - int64 bucket_id; - TensorVec batch; - OP_REQUIRES_OK(ctx, batcher_->GetNext(ctx, &bucket_id, &batch)); - VLOG(1) << "Produce a batch from bucket : " << bucket_id; - OP_REQUIRES(ctx, static_cast(batch.size()) == ctx->num_outputs(), - errors::Internal("Unexpected batch: ", batch.size())); - for (int i = 0; i < batch.size(); ++i) { - ctx->set_output(i, batch[i]); - } - } - - protected: - // Not owned. - RecordProcessorClass* processor_ = nullptr; - - private: - // Owned. - RecordBatcher* batcher_ = nullptr; -}; - -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_INPUT_COMMON_H_ diff --git a/lingvo/core/ops/mass_op.cc b/lingvo/core/ops/mass_op.cc deleted file mode 100644 index c246c962..00000000 --- a/lingvo/core/ops/mass_op.cc +++ /dev/null @@ -1,256 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include -#include -#include - -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/core/errors.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -static const int kBOS = 1; -static const int kEOS = 2; - -class MassOp : public OpKernel { - public: - explicit MassOp(OpKernelConstruction* ctx); - void Compute(OpKernelContext* ctx) override; - - private: - float mask_ratio_; - int mask_minlen_; - int mask_id_; - int span_len_; - float random_start_prob_; - float keep_prob_; - float rand_prob_; - float mask_prob_; - bool mask_target_; - int vocab_size_; - int first_unreserved_id_; - - std::mt19937 rng_; - - // Populates a vector of length seq_len with 1's in positions where - // the corresponding token will be masked, and 0's elsewhere. - void GenerateMask(std::vector* mask); - void ValidateInput(OpKernelContext* ctx, const Tensor& ids, - const Tensor& weights, const Tensor& actual_seq_len); - - template - void CopyTensorToMutableOutput(const TensorShape& shape, const string& name, - const Tensor& source, OpKernelContext* ctx, - Tensor** out) { - OP_REQUIRES_OK(ctx, ctx->allocate_output(name, shape, out)); - (*out)->flat() = source.flat(); - } -}; - -MassOp::MassOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("mask_ratio", &mask_ratio_)); - CHECK_GT(mask_ratio_, 0); - CHECK_LT(mask_ratio_, 1); - OP_REQUIRES_OK(ctx, ctx->GetAttr("mask_minlen", &mask_minlen_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("mask_id", &mask_id_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("span_len", &span_len_)); - CHECK_GT(span_len_, 0); - OP_REQUIRES_OK(ctx, ctx->GetAttr("random_start_prob", &random_start_prob_)); - CHECK_GE(random_start_prob_, 0); - CHECK_LE(random_start_prob_, 1); - OP_REQUIRES_OK(ctx, ctx->GetAttr("keep_prob", &keep_prob_)); - CHECK_GE(keep_prob_, 0); - CHECK_LE(keep_prob_, 1); - OP_REQUIRES_OK(ctx, ctx->GetAttr("rand_prob", &rand_prob_)); - CHECK_GE(rand_prob_, 0); - CHECK_LE(rand_prob_, 1); - OP_REQUIRES_OK(ctx, ctx->GetAttr("mask_prob", &mask_prob_)); - CHECK_GE(mask_prob_, 0); - CHECK_LE(mask_prob_, 1); - CHECK_EQ(keep_prob_ + rand_prob_ + mask_prob_, 1); - OP_REQUIRES_OK(ctx, ctx->GetAttr("mask_target", &mask_target_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("vocab_size", &vocab_size_)); - OP_REQUIRES_OK(ctx, - ctx->GetAttr("first_unreserved_id", &first_unreserved_id_)); - - rng_.seed(7743); // seed the random generator -} - -void MassOp::ValidateInput(OpKernelContext* ctx, const Tensor& ids, - const Tensor& weights, - const Tensor& actual_seq_len) { - // Verify shapes and sizes - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(ids.shape()), - errors::InvalidArgument("ids must be matrix, but got ", - ids.shape().DebugString())); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(weights.shape()), - errors::InvalidArgument("weights must be matrix, but got ", - weights.shape().DebugString())); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(actual_seq_len.shape()), - errors::InvalidArgument("actual_seq_len must be vector, but got ", - actual_seq_len.shape().DebugString())); - OP_REQUIRES(ctx, ids.dim_size(0) > 0, - errors::InvalidArgument("batch size must be > 0")); - OP_REQUIRES(ctx, ids.dim_size(1) > 0, - errors::InvalidArgument("max seq length must be > 0")); - OP_REQUIRES(ctx, ids.dim_size(0) == weights.dim_size(0), - errors::InvalidArgument("inconsistent batch size")); - OP_REQUIRES(ctx, ids.dim_size(0) == actual_seq_len.dim_size(0), - errors::InvalidArgument("inconsistent batch size")); - OP_REQUIRES(ctx, ids.dim_size(1) == weights.dim_size(1), - errors::InvalidArgument("inconsistent seq length")); -} - -void MassOp::GenerateMask(std::vector* mask) { - int seq_len = mask->size(); - int mask_len = seq_len * mask_ratio_; - - // skip short sentences - if (mask_len < mask_minlen_) return; - - int num_segments = std::ceil(static_cast(mask_len) / span_len_); - int non_mask_len = seq_len - mask_len; - // segments = [span_len, span_len, ..., span_len] - std::vector segments(num_segments + non_mask_len, span_len_); - // if there is a remainder, last segment will be a different length - if (mask_len % span_len_ > 0) { - // now segments are [span_len, span_len, ..., remaining_mask_len] - segments.back() = mask_len % span_len_; - } - // non-masked segments of length 1 are represented by 0 - // now segments are [0, 0, ..., span_len, span_len, ... remaining_mask_len] - std::fill(segments.begin(), segments.begin() + non_mask_len, 0); - std::uniform_real_distribution<> realdistr(0.0, 1.0); - float px = realdistr(rng_); - float fixed_start_prob = 1 - random_start_prob_; - if (px >= random_start_prob_ + fixed_start_prob / 2) { - // place a segment at position 0, shuffle the rest - std::swap(segments.front(), segments.back()); - if (num_segments > 1) { - std::shuffle(segments.begin() + 1, segments.end(), rng_); - } - } else if (px >= random_start_prob_) { - // keep a segment at final position, shuffle the rest - if (num_segments > 1) { - std::shuffle(segments.begin(), segments.end() - 1, rng_); - } - } else { - // shuffle all segments - std::shuffle(segments.begin(), segments.end(), rng_); - } - int idx = 0; - for (int n : segments) { - if (n == 0) { - // Each 0 in segments represents a non-masked token; i.e. mask will be 0 - (*mask)[idx++] = 0; - } else { - // Each n in segments represents a masked segment of length n tokens - for (int i = 0; i < n; i++) { - (*mask)[idx++] = 1; - } - } - } -} - -void MassOp::Compute(OpKernelContext* ctx) { - // get inputs - const Tensor& ids = ctx->input(0); - const Tensor& weights = ctx->input(1); - const Tensor& actual_seq_len = ctx->input(2); - auto Tactual_seq_len = actual_seq_len.vec(); - ValidateInput(ctx, ids, weights, actual_seq_len); - OP_REQUIRES_OK(ctx, ctx->status()); - - int batch_size = ids.dim_size(0); - int max_seq_len = ids.dim_size(1); - - // create outputs - Tensor* src_ids; - Tensor* tgt_ids; - Tensor* tgt_weights; - Tensor* tgt_labels; - CopyTensorToMutableOutput(TensorShape({batch_size, max_seq_len}), - "src_ids", ids, ctx, &src_ids); - CopyTensorToMutableOutput(TensorShape({batch_size, max_seq_len}), - "tgt_ids", ids, ctx, &tgt_ids); - CopyTensorToMutableOutput(TensorShape({batch_size, max_seq_len}), - "tgt_weights", weights, ctx, &tgt_weights); - CopyTensorToMutableOutput(TensorShape({batch_size, max_seq_len}), - "tgt_labels", ids, ctx, &tgt_labels); - auto Tsrc_ids = src_ids->matrix(); - auto Ttgt_ids = tgt_ids->matrix(); - auto Ttgt_weights = tgt_weights->matrix(); - - // for each example, mask the source and target to implement MASS. - for (int i = 0; i < batch_size; i++) { - int seq_len = Tactual_seq_len(i); - if (seq_len <= 0) { - LOG(WARNING) << "Skipping zero-length sequence"; - continue; - } - if (seq_len > max_seq_len) seq_len = max_seq_len; - std::vector mask(seq_len, 0); - GenerateMask(&mask); - - // Right shift tgt and drop EOS - for (int j = max_seq_len - 1; j > 0; j--) { - if (Ttgt_ids(i, j - 1) == kEOS) { - Ttgt_ids(i, j) = 0; - Ttgt_weights(i, j) = 0.0; - } else { - Ttgt_ids(i, j) = Ttgt_ids(i, j - 1); - Ttgt_weights(i, j) = Ttgt_weights(i, j - 1); - } - } - Ttgt_ids(i, 0) = kBOS; - Ttgt_weights(i, 0) = 1.0; - - std::uniform_int_distribution<> vocabdistr(first_unreserved_id_, - vocab_size_); - - // mask source and target tokens. - if (std::accumulate(mask.begin(), mask.end(), 0) > 0) { - for (int j = 0; j < mask.size(); j++) { - if (mask[j] == 1) { - std::uniform_real_distribution<> realdistr(0.0, 1.0); - float tx = realdistr(rng_); - if (tx >= mask_prob_ + rand_prob_) { - // Keep token - } else if (tx >= mask_prob_) { - Tsrc_ids(i, j) = vocabdistr(rng_); // replace with random token - } else { - Tsrc_ids(i, j) = mask_id_; // replace with mask id - } - } else if (mask_target_) { - // either source is masked or target is masked. - Ttgt_ids(i, j) = mask_id_; - // where target is masked, target weights are zero - Ttgt_weights(i, j) = 0; - } - } - } - } -} - -REGISTER_KERNEL_BUILDER(Name("Mass").Device(DEVICE_CPU), MassOp); - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/mass_op_test.py b/lingvo/core/ops/mass_op_test.py deleted file mode 100644 index 712b6855..00000000 --- a/lingvo/core/ops/mass_op_test.py +++ /dev/null @@ -1,929 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for mass_op.""" - -import collections -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_utils -import numpy as np - -FLAGS = tf.flags.FLAGS - -BOS = 1 -EOS = 2 - -MassOutput = collections.namedtuple( - 'MassOutput', ['src_ids', 'tgt_ids', 'tgt_labels', 'tgt_weights']) - -# Set default to empty. -MassOutput.__new__.__defaults__ = (None,) * len(MassOutput._fields) - - -def FindResultFromList(result, expected_results): - """Find the given result from a list of expected results. - - Args: - result: A MassOutput tuple, from running ops.mass(). - expected_results: A list of MassOutput. The test asserts `result` is equal - to at least one result from `expected_results`. - - Returns: - The index of first match found, or None for not found. - - We use this when the specific output from ops.mass() is not stable across - different platforms. Specifically, the implementation currently uses - std::shuffle(), which have different implementations between libc++ and - stdlibc++. - """ - for idx, expected in enumerate(expected_results): - match = True - for attr in MassOutput._fields: - if not np.array_equal(getattr(result, attr), getattr(expected, attr)): - match = False - break - if match: - return idx - - tf.logging.error('Found unexpected output from op.mass that fails to match' - ' any expected result.') - for attr in MassOutput._fields: - tf.logging.info('%s = %s', attr, np.array_repr(getattr(result, attr))) - return None - - -class MassOpTest(test_utils.TestCase): - - def testFixedStart(self): - ids = np.array( - [[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32) - weights = np.array( - [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32) - actual_seq_len = np.array([14, 10, 14, 10], dtype=np.int32) - - g = tf.Graph() - with g.as_default(): - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - ids, - weights, - actual_seq_len, - mask_id=3, - mask_ratio=0.5, - mask_minlen=1, - span_len=8, - random_start_prob=0, - keep_prob=0, - rand_prob=0, - mask_prob=1, - mask_target=True, - vocab_size=9) - - with self.session(graph=g) as sess: - (src_ids, tgt_ids, tgt_labels, - tgt_weights) = sess.run([src_ids, tgt_ids, tgt_labels, tgt_weights]) - self.assertAllEqual( - src_ids, - np.array([[ - 3, 3, 3, 3, 3, 3, 3, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, - 0 - ], [ - 3, 3, 3, 3, 3, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [4, 5, 6, 7, 8, 9, 10, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 - ], [4, 5, 6, 7, 8, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32)) - self.assertAllEqual( - tgt_ids, - np.array([ - [BOS, 4, 5, 6, 7, 8, 9, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0], - [BOS, 4, 5, 6, 7, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 3, 3, 3, 3, 3, 3, 3, 10, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, - 0, 0 - ], - [3, 3, 3, 3, 3, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - dtype=np.int32)) - self.assertAllEqual( - tgt_labels, - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, - 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32)) - self.assertAllEqual( - tgt_weights, - np.array( - [[1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - def testRandomStart(self): - ids = np.array( - [[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32) - weights = np.array( - [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32) - actual_seq_len = np.array([14, 10, 14, 10], dtype=np.int32) - - g = tf.Graph() - with g.as_default(): - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - ids, - weights, - actual_seq_len, - mask_id=3, - mask_ratio=0.5, - mask_minlen=1, - span_len=100000, - random_start_prob=1, - keep_prob=0, - rand_prob=0, - mask_prob=1, - mask_target=True, - vocab_size=9) - - with self.session(graph=g) as sess: - (src_ids, tgt_ids, tgt_labels, tgt_weights) = sess.run([ - src_ids, - tgt_ids, - tgt_labels, - tgt_weights, - ]) - result = MassOutput(src_ids, tgt_ids, tgt_labels, tgt_weights) - expected_output1 = MassOutput( - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 - ], [ - 4, 3, 3, 3, 3, 3, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 4, 5, 6, 7, 3, 3, 3, 3, 3, 3, 3, 15, 16, EOS, 0, 0, 0, 0, 0, 0 - ], [4, 5, 3, 3, 3, 3, 3, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - dtype=np.int32), - np.array([[ - 3, 3, 3, 3, 3, 3, 3, 10, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, - 0 - ], [ - 3, 4, 5, 6, 7, 8, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [3, 3, 3, 3, 7, 8, 9, 10, 11, 12, 13, 3, 3, 3, 0, 0, 0, 0, 0, 0 - ], [3, 3, 5, 6, 7, 8, 9, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, - 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - expected_output2 = MassOutput( - np.array([[ - 3, 3, 3, 3, 3, 3, 3, 11, 12, 13, 14, 15, 16, 2, 0, 0, 0, 0, 0, 0 - ], [ - 3, 3, 3, 3, 3, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 4, 5, 6, 7, 3, 3, 3, 3, 3, 3, 3, 15, 16, 2, 0, 0, 0, 0, 0, 0 - ], [4, 5, 3, 3, 3, 3, 3, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 1, 4, 5, 6, 7, 8, 9, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 - ], [ - 1, 4, 5, 6, 7, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [3, 3, 3, 3, 7, 8, 9, 10, 11, 12, 13, 3, 3, 3, 0, 0, 0, 0, 0, 0 - ], [3, 3, 5, 6, 7, 8, 9, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, 0, 0, 0, - 0 - ], [4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, - 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - match_idx = FindResultFromList(result, - [expected_output1, expected_output2]) - self.assertIsNotNone(match_idx, - '{} is not a valid result'.format(result)) - - def testSegmented(self): - ids = np.array( - [[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32) - weights = np.array( - [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32) - actual_seq_len = np.array([14, 10, 14, 10], dtype=np.int32) - - g = tf.Graph() - with g.as_default(): - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - ids, - weights, - actual_seq_len, - mask_id=3, - mask_ratio=0.5, - mask_minlen=1, - span_len=3, - keep_prob=0, - rand_prob=0, - mask_prob=1, - mask_target=True, - vocab_size=9) - - with self.session(graph=g) as sess: - (src_ids, tgt_ids, tgt_labels, tgt_weights) = sess.run([ - src_ids, - tgt_ids, - tgt_labels, - tgt_weights, - ]) - - result = MassOutput(src_ids, tgt_ids, tgt_labels, tgt_weights) - expected_output1 = MassOutput( - np.array([[ - 4, 3, 3, 3, 3, 3, 3, 11, 12, 13, 14, 15, 16, 3, 0, 0, 0, 0, 0, 0 - ], [ - 4, 3, 3, 7, 8, 9, 10, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [4, 5, 6, 7, 8, 3, 3, 3, 12, 3, 14, 3, 3, 3, 0, 0, 0, 0, 0, 0], - [ - 3, 3, 6, 3, 3, 3, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32), - np.array([[ - 3, 4, 5, 6, 7, 8, 9, 3, 3, 3, 3, 3, 3, 16, 0, 0, 0, 0, 0, 0 - ], [ - 3, 4, 5, 3, 3, 3, 3, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 3, 3, 3, 3, 3, 8, 9, 10, 3, 12, 3, 14, 15, 16, 0, 0, 0, 0, 0, 0 - ], [BOS, 4, 3, 6, 7, 8, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, - 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - expected_output2 = MassOutput( - np.array([[ - 4, 5, 6, 3, 3, 3, 10, 3, 3, 3, 14, 15, 16, 3, 0, 0, 0, 0, 0, 0 - ], [ - 3, 3, 6, 7, 8, 9, 10, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [4, 3, 3, 3, 8, 9, 10, 11, 12, 3, 3, 3, 16, 3, 0, 0, 0, 0, 0, 0 - ], [4, 5, 3, 3, 8, 9, 3, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 3, 3, 3, 6, 7, 8, 3, 10, 11, 12, 3, 3, 3, 16, 0, 0, 0, 0, 0, 0 - ], [ - 1, 4, 3, 3, 3, 3, 3, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 3, 4, 5, 6, 3, 3, 3, 3, 3, 12, 13, 14, 3, 16, 0, 0, 0, 0, 0, 0 - ], [3, 3, 5, 6, 3, 3, 9, 10, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, 0, 0, 0, - 0 - ], [4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, - 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]], - dtype=np.int32), - np.array([[ - 0., 0., 0., 1., 1., 1., 0., 1., 1., 1., 0., 0., 0., 1., 0., 0., - 0., 0., 0., 0. - ], - [ - 1., 1., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0., - 0., 0., 0., 0., 0., 0., 0. - ], - [ - 0., 1., 1., 1., 0., 0., 0., 0., 0., 1., 1., 1., 0., - 1., 0., 0., 0., 0., 0., 0. - ], - [ - 0., 0., 1., 1., 0., 0., 1., 1., 1., 0., 0., 0., 0., - 0., 0., 0., 0., 0., 0., 0. - ]], - dtype=np.float32)) - match_idx = FindResultFromList(result, - [expected_output1, expected_output2]) - self.assertIsNotNone(match_idx, - '{} is not a valid result'.format(result)) - - def testNoMaskTarget(self): - ids = np.array( - [[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32) - weights = np.array( - [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32) - actual_seq_len = np.array([14, 10, 14, 10], dtype=np.int32) - - g = tf.Graph() - with g.as_default(): - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - ids, - weights, - actual_seq_len, - mask_id=3, - mask_ratio=0.5, - mask_minlen=1, - span_len=100000, - random_start_prob=0, - keep_prob=0, - rand_prob=0, - mask_prob=1, - mask_target=False, - vocab_size=9) - - with self.session(graph=g) as sess: - (src_ids, tgt_ids, tgt_labels, tgt_weights) = sess.run([ - src_ids, - tgt_ids, - tgt_labels, - tgt_weights, - ]) - - self.assertAllEqual( - src_ids, - np.array([[ - 3, 3, 3, 3, 3, 3, 3, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, - 0 - ], [ - 3, 3, 3, 3, 3, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [4, 5, 6, 7, 8, 9, 10, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 - ], [4, 5, 6, 7, 8, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32)) - self.assertAllEqual( - tgt_ids, - np.array([[ - 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, - 0 - ], [ - BOS, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, - 0, 0, 0, 0 - ], - [ - 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]], - dtype=np.int32)) - self.assertAllEqual( - tgt_labels, - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, - 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32)) - self.assertAllEqual( - tgt_weights, - np.array( - [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - def testKeepOrRandMaskedTokens(self): - ids = np.array( - [[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32) - weights = np.array( - [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32) - actual_seq_len = np.array([14, 10, 14, 10], dtype=np.int32) - - g = tf.Graph() - with g.as_default(): - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - ids, - weights, - actual_seq_len, - mask_id=3, - mask_ratio=0.5, - mask_minlen=1, - span_len=100000, - random_start_prob=0, - keep_prob=0.5, - rand_prob=0.5, - mask_prob=0, - mask_target=True, - vocab_size=9) - - with self.session(graph=g) as sess: - (src_ids, tgt_ids, tgt_labels, tgt_weights) = sess.run([ - src_ids, - tgt_ids, - tgt_labels, - tgt_weights, - ]) - - result = MassOutput(src_ids, tgt_ids, tgt_labels, tgt_weights) - expected_output1 = MassOutput( - np.array([[ - 4, 7, 6, 9, 5, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 8, 9, 8, 12, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 7, 12, 5, 7, 15, 16, EOS, 0, 0, 0, 0, 0, 0 - ], [4, 5, 6, 7, 8, 9, 6, 11, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([ - [BOS, 4, 5, 6, 7, 8, 9, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0], - [3, 3, 3, 3, 3, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 3, 3, 3, 3, 3, 3, 3, 10, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, - 0, 0 - ], - [3, 3, 3, 3, 3, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, - 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - expected_output2 = MassOutput( - np.array([[ - 4, 8, 6, 7, 6, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, 0, 0, 0, - 0 - ], [4, 5, 6, 7, 8, 4, 9, 4, 12, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 5, 7, 6, 4, 15, 16, 2, 0, 0, 0, 0, 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 6, 11, 12, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ]], - dtype=np.int32), - np.array([ - [1, 4, 5, 6, 7, 8, 9, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0], - [3, 3, 3, 3, 3, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 3, 3, 3, 3, 3, 3, 3, 10, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, - 0, 0 - ], - [3, 3, 3, 3, 3, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, 0, 0, 0, - 0 - ], [4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, - 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - match_idx = FindResultFromList(result, - [expected_output1, expected_output2]) - self.assertIsNotNone(match_idx, - '{} is not a valid result'.format(result)) - - def testKeepMaskedTokens(self): - ids = np.array( - [[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32) - weights = np.array( - [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32) - actual_seq_len = np.array([14, 10, 14, 10], dtype=np.int32) - - g = tf.Graph() - with g.as_default(): - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - ids, - weights, - actual_seq_len, - mask_id=3, - mask_ratio=0.5, - mask_minlen=1, - span_len=3, - keep_prob=1, - rand_prob=0, - mask_prob=0, - mask_target=True, - vocab_size=9) - - with self.session(graph=g) as sess: - (src_ids, tgt_ids, tgt_labels, tgt_weights) = sess.run([ - src_ids, - tgt_ids, - tgt_labels, - tgt_weights, - ]) - - result = MassOutput(src_ids, tgt_ids, tgt_labels, tgt_weights) - expected_output1 = MassOutput( - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, - 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32), - np.array([[ - 3, 4, 5, 6, 7, 8, 9, 3, 3, 3, 3, 3, 3, 16, 0, 0, 0, 0, 0, 0 - ], [ - 3, 4, 5, 3, 3, 3, 3, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 3, 3, 3, 3, 3, 8, 9, 10, 3, 12, 3, 14, 15, 16, 0, 0, 0, 0, 0, 0 - ], [BOS, 4, 3, 6, 7, 8, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, - 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - expected_output2 = MassOutput( - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, 0, 0, 0, - 0 - ], [4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, - 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]], - dtype=np.int32), - np.array([[ - 3, 3, 3, 6, 7, 8, 3, 10, 11, 12, 3, 3, 3, 16, 0, 0, 0, 0, 0, 0 - ], [ - 1, 4, 3, 3, 3, 3, 3, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 3, 4, 5, 6, 3, 3, 3, 3, 3, 12, 13, 14, 3, 16, 0, 0, 0, 0, 0, 0 - ], [3, 3, 5, 6, 3, 3, 9, 10, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, 0, 0, 0, - 0 - ], [4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, - 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - match_idx = FindResultFromList(result, - [expected_output1, expected_output2]) - self.assertIsNotNone(match_idx, - '{} is not a valid result'.format(result)) - - def testSpanLen1(self): - ids = np.array( - [[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, 0, 0], - [4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32) - weights = np.array( - [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32) - actual_seq_len = np.array([14, 10, 14, 10], dtype=np.int32) - - g = tf.Graph() - with g.as_default(): - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - ids, - weights, - actual_seq_len, - mask_id=3, - mask_ratio=0.5, - mask_minlen=1, - span_len=1, - keep_prob=0, - rand_prob=0, - mask_prob=1, - mask_target=True, - vocab_size=9) - - with self.session(graph=g) as sess: - (src_ids, tgt_ids, tgt_labels, tgt_weights) = sess.run([ - src_ids, - tgt_ids, - tgt_labels, - tgt_weights, - ]) - - result = MassOutput(src_ids, tgt_ids, tgt_labels, tgt_weights) - expected_output1 = MassOutput( - np.array([ - [4, 3, 3, 7, 8, 9, 3, 3, 3, 13, 14, 3, 16, 3, 0, 0, 0, 0, 0, 0], - [3, 3, 6, 3, 3, 9, 10, 11, 12, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [4, 5, 3, 3, 8, 9, 10, 3, 3, 13, 3, 15, 3, 3, 0, 0, 0, 0, 0, 0], - [3, 5, 6, 3, 8, 3, 10, 3, 12, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - dtype=np.int32), - np.array([[ - 3, 4, 5, 3, 3, 3, 9, 10, 11, 3, 3, 14, 3, 16, 0, 0, 0, 0, 0, 0 - ], [ - 1, 4, 3, 6, 7, 3, 3, 3, 3, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 3, 3, 5, 6, 3, 3, 3, 10, 11, 3, 13, 3, 15, 16, 0, 0, 0, 0, 0, 0 - ], [BOS, 3, 3, 6, 3, 8, 3, 10, 3, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, 0, 0, 0, - 0, 0 - ], [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, EOS, 0, - 0, 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, EOS, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0], - [1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - expected_output2 = MassOutput( - np.array([[ - 4, 5, 6, 3, 3, 3, 10, 3, 12, 13, 3, 3, 16, 3, 0, 0, 0, 0, 0, 0 - ], [ - 3, 5, 3, 7, 3, 9, 3, 3, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 3, 5, 3, 3, 8, 9, 3, 3, 12, 3, 3, 15, 16, 2, 0, 0, 0, 0, 0, 0 - ], [3, 3, 3, 7, 8, 9, 3, 11, 12, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.int32), - np.array([[ - 3, 3, 3, 6, 7, 8, 3, 10, 3, 3, 13, 14, 3, 16, 0, 0, 0, 0, 0, 0 - ], [1, 3, 5, 3, 7, 3, 9, 10, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [ - 1, 3, 5, 6, 3, 3, 9, 10, 3, 12, 13, 3, 3, 3, 0, 0, 0, 0, 0, 0 - ], [1, 4, 5, 3, 3, 3, 9, 3, 3, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - dtype=np.int32), - np.array([[ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, 0, 0, 0, - 0 - ], [4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 0, 0, - 0, 0, 0, 0 - ], - [ - 4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]], - dtype=np.int32), - np.array( - [[0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0], - [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], - dtype=np.float32)) - - match_idx = FindResultFromList(result, - [expected_output1, expected_output2]) - self.assertIsNotNone(match_idx, - '{} is not a valid result'.format(result)) - - def testZeroLengthSeq(self): - ids = np.array([[0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], - dtype=np.int32) - weights = np.array([[0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], - dtype=np.float32) - actual_seq_len = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=np.int32) - - g = tf.Graph() - with g.as_default(): - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - ids, - weights, - actual_seq_len, - mask_id=3, - mask_ratio=0.5, - mask_minlen=0, - span_len=8, - random_start_prob=0, - keep_prob=0, - rand_prob=0, - mask_prob=1, - mask_target=True, - vocab_size=9) - - with self.session(graph=g) as sess: - (src_ids, tgt_ids, tgt_labels, - tgt_weights) = sess.run([src_ids, tgt_ids, tgt_labels, tgt_weights]) - - self.assertAllEqual( - src_ids, - np.array([[0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], - dtype=np.int32)) - self.assertAllEqual( - tgt_ids, - np.array([[0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], - dtype=np.int32)) - self.assertAllEqual( - tgt_labels, - np.array([[0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], - dtype=np.int32)) - self.assertAllEqual( - tgt_weights, - np.array([[0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], - dtype=np.float32)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/ml_perf_subword_op.cc b/lingvo/core/ops/ml_perf_subword_op.cc deleted file mode 100644 index 645c2d01..00000000 --- a/lingvo/core/ops/ml_perf_subword_op.cc +++ /dev/null @@ -1,139 +0,0 @@ -/* Copyright 2020 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/ml_perf_subword_op.h" - -#include "unicode/uchar.h" -#include "unicode/utf8.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/platform/logging.h" - -namespace tensorflow { -namespace lingvo { - -Status MlPerfSubword::Load(const string& vocab_glob) { - std::vector vocab_filenames; - TF_CHECK_OK(Env::Default()->GetMatchingPaths(vocab_glob, &vocab_filenames)) - << "Unable to match vocab pattern: " << vocab_glob; - CHECK_EQ(vocab_filenames.size(), 1) - << "Did not match exactly one file with pattern: " << vocab_glob; - const string& vocab_filename = vocab_filenames[0]; - - string content; - TF_RETURN_IF_ERROR( - ReadFileToString(Env::Default(), vocab_filename, &content)); - - return LoadLines(str_util::Split(content, '\n')); -} - -Status MlPerfSubword::LoadLines(const std::vector& lines) { - for (StringPiece line : lines) { - if (line.empty()) continue; - // Strip surrounding single quotes. - auto len = line.size(); - CHECK_GT(line.size(), 2); - auto subtoken = string(line.substr(1, len - 2)); - id_to_token_.push_back(subtoken); - } - return Status::OK(); -} - -// This is a direct port of the tokenizer decode method in the MLPerf -// reference implementation for Translate/Transformer. -void MlPerfSubword::Decode(const std::vector& ids, string* out) { - std::vector subtokens_raw(ids.size()); - for (const auto& id : ids) { - subtokens_raw.emplace_back(id_to_token_[id]); - } - string inter = absl::StrJoin(subtokens_raw, ""); - - std::vector subtokens = absl::StrSplit(inter, '_'); - - std::vector token_is_alnum; - for (const auto& token : subtokens) { - int token_end = 0; - UChar32 c; - U8_NEXT(token, token_end, token.length(), c); - token_is_alnum.push_back(u_isalnum(c)); - } - std::vector ret; - - for (int i = 0; i < subtokens.size(); ++i) { - const auto& token = subtokens[i]; - if (i > 0 && token_is_alnum[i - 1] & token_is_alnum[i]) { - ret.push_back(" "); - } - ret.push_back(token); - } - *out = absl::StrJoin(ret, ""); -} - -class MlPerfSubwordIdToStringOp : public OpKernel { - public: - explicit MlPerfSubwordIdToStringOp(OpKernelConstruction* ctx) - : OpKernel(ctx) { - string vocab_filepath; - OP_REQUIRES_OK(ctx, ctx->GetAttr("vocab_filepath", &vocab_filepath)); - OP_REQUIRES_OK(ctx, vocab_.Load(vocab_filepath)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* token_ids; - const Tensor* seq_lengths; - OP_REQUIRES_OK(ctx, ctx->input("token_ids", &token_ids)); - OP_REQUIRES_OK(ctx, ctx->input("seq_lengths", &seq_lengths)); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(token_ids->shape()), - errors::InvalidArgument("token_ids must be a matrix, but get ", - token_ids->shape().DebugString())); - OP_REQUIRES( - ctx, TensorShapeUtils::IsVector(seq_lengths->shape()), - errors::InvalidArgument("seq_lengths must be a vector, but get ", - seq_lengths->shape().DebugString())); - - const int batch = seq_lengths->NumElements(); - OP_REQUIRES( - ctx, batch == token_ids->dim_size(0), - errors::InvalidArgument("batch size has to match between token_ids and " - "seq_lengths")); - Tensor* out; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({batch}), &out)); - const auto& t_ids = token_ids->matrix(); - const auto& t_seq_lens = seq_lengths->vec(); - auto t_out = out->template vec(); - - for (int i = 0; i < batch; ++i) { - const int len_i = std::max(0, t_seq_lens(i)); - std::vector ids_i(len_i); - for (int j = 0; j < len_i; ++j) { - ids_i[j] = t_ids(i, j); - } - string decode_output; - vocab_.Decode(ids_i, &decode_output); - t_out(i) = decode_output; - } - } - - private: - MlPerfSubword vocab_; -}; - -REGISTER_KERNEL_BUILDER(Name("MlPerfSubwordIdToString").Device(DEVICE_CPU), - MlPerfSubwordIdToStringOp); - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/ml_perf_subword_op.h b/lingvo/core/ops/ml_perf_subword_op.h deleted file mode 100644 index d8b31deb..00000000 --- a/lingvo/core/ops/ml_perf_subword_op.h +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright 2020 The TensorFlow 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. -==============================================================================*/ - -#ifndef THIRD_PARTY_PY_LINGVO_CORE_OPS_ML_PERF_SUBWORD_OP_H_ -#define THIRD_PARTY_PY_LINGVO_CORE_OPS_ML_PERF_SUBWORD_OP_H_ - -#include -#include - -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/platform/macros.h" - -namespace tensorflow { -namespace lingvo { - -// Subword vocabulary class. -class MlPerfSubword { - public: - MlPerfSubword() {} - ~MlPerfSubword() {} - - Status Load(const string& vocab_glob); - Status LoadLines(const std::vector& lines); - - void Decode(const std::vector& ids, string* out); - - private: - std::vector id_to_token_; -}; - -} // namespace lingvo -} // namespace tensorflow - -#endif // THIRD_PARTY_PY_LINGVO_CORE_OPS_MLPERF_SUBWORD_OP_H diff --git a/lingvo/core/ops/pack_ops.cc b/lingvo/core/ops/pack_ops.cc deleted file mode 100644 index e2f2c5ec..00000000 --- a/lingvo/core/ops/pack_ops.cc +++ /dev/null @@ -1,502 +0,0 @@ -/* Copyright 2020 The TensorFlow 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. -==============================================================================*/ - -#include -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/strings/str_join.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" -#include "lingvo/core/ops/text_packing.h" -#include "tensorflow/core/framework/bounds_check.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/register_types.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/platform/threadpool.h" - -namespace tensorflow { -namespace lingvo { - -namespace { -struct PackSequencesOutputs { - Tensor* src_segment_ids = nullptr; - Tensor* src_segment_pos = nullptr; - Tensor* src_indices_in_input = nullptr; - Tensor* tgt_segment_ids = nullptr; - Tensor* tgt_segment_pos = nullptr; - Tensor* tgt_indices_in_input = nullptr; -}; - -// The record of packing one input sequence, i.e. a (src, tgt) pair. -struct PackRecord { - int index_in_input; - TextPacking::PackingIndex packing; -}; -} // namespace - -// An op that outputs a packing pattern based on actual sequence lengths. -class PackSequencesOp : public OpKernel { - public: - explicit PackSequencesOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - int64 seed; - OP_REQUIRES_OK(ctx, ctx->GetAttr("seed", &seed)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("packed_batch_size", &packed_batch_size_)); - OP_REQUIRES_OK(ctx, - ctx->GetAttr("packed_src_seq_len", &packed_src_seq_len_)); - OP_REQUIRES_OK(ctx, - ctx->GetAttr("packed_tgt_seq_len", &packed_tgt_seq_len_)); - if (seed == 0) { - // If seed is unspecified, use completely random seed. - std::random_device device("/dev/urandom"); - seed = std::mt19937_64(device())(); - } - rnd_.seed(seed); - } - - ~PackSequencesOp() override {} - - void Compute(OpKernelContext* ctx) override { - ValidateInputs(ctx); - if (!ctx->status().ok()) { - return; - } - - const auto input_num = ctx->input(0).vec().size(); - std::vector pack_records; - pack_records.reserve(input_num); - const int output_num = PackEntireInputs(ctx, &pack_records); - - bool dropping_inputs; - absl::flat_hash_map new_indices; - PackSequencesOutputs outputs; - if (packed_batch_size_ == 0) { - AllocateOutputs(ctx, &outputs, output_num); - dropping_inputs = false; - } else { - AllocateOutputs(ctx, &outputs, packed_batch_size_); - dropping_inputs = DropPackedRows(ctx, output_num, &new_indices); - } - WriteOutputs(ctx, pack_records, dropping_inputs, &new_indices, &outputs); - } - - private: - // Validates the shapes and types of inputs. - void ValidateInputs(OpKernelContext* ctx); - - // Allocates (and zero initializes) all outputs. - void AllocateOutputs(OpKernelContext* ctx, PackSequencesOutputs* outputs, - const int32 packed_batch_size); - - // Pack entire inputs. Returns the number of rows needed to pack all of input - // sequences. Also outputs the packing records. - int PackEntireInputs(OpKernelContext* ctx, - std::vector* pack_records); - - // Returns whether we need to drop packed rows, and if so, returns a mapping - // from the original output row index to its new index after dropping. - bool DropPackedRows(OpKernelContext* ctx, int num_rows, - absl::flat_hash_map* new_indices); - - // Writes the packing results to the output tensors. - void WriteOutputs(OpKernelContext* ctx, - const std::vector& pack_records, - bool dropping_inputs, - const absl::flat_hash_map* new_indices, - PackSequencesOutputs* outputs); - - int packed_batch_size_; - int packed_src_seq_len_; - int packed_tgt_seq_len_; - mutable absl::Mutex mu_; - // Used for randomizing the dropping of input rows when needed. - std::mt19937 rnd_ ABSL_GUARDED_BY(mu_); - - TF_DISALLOW_COPY_AND_ASSIGN(PackSequencesOp); -}; - -void PackSequencesOp::ValidateInputs(OpKernelContext* ctx) { - const Tensor& src_actual_seq_len = ctx->input(0); - OP_REQUIRES(ctx, - TensorShapeUtils::IsVector(src_actual_seq_len.shape()) && - (src_actual_seq_len.dtype() == DataType::DT_INT32), - errors::InvalidArgument( - "src_actual_seq_len must be a vector of int32, got: ", - src_actual_seq_len.DebugString())); - const Tensor& tgt_actual_seq_len = ctx->input(1); - OP_REQUIRES(ctx, - TensorShapeUtils::IsVector(tgt_actual_seq_len.shape()) && - (tgt_actual_seq_len.dtype() == DataType::DT_INT32), - errors::InvalidArgument( - "tgt_actual_seq_len must be a vector of int32, got: ", - tgt_actual_seq_len.DebugString())); - OP_REQUIRES(ctx, src_actual_seq_len.shape() == tgt_actual_seq_len.shape(), - errors::InvalidArgument( - "src_actual_seq_len must be the same shape as " - "tgt_actual_seq_len, got: src shape ", - src_actual_seq_len.shape().DebugString(), " vs. tgt shape ", - tgt_actual_seq_len.shape().DebugString())); -} - -void PackSequencesOp::AllocateOutputs(OpKernelContext* ctx, - PackSequencesOutputs* outputs, - const int32 packed_batch_size) { - TensorShape packed_src_shape({packed_batch_size, packed_src_seq_len_}); - TensorShape packed_tgt_shape({packed_batch_size, packed_tgt_seq_len_}); - - int output_id = 0; - OP_REQUIRES_OK(ctx, ctx->allocate_output(output_id++, packed_src_shape, - &outputs->src_segment_ids)); - OP_REQUIRES_OK(ctx, ctx->allocate_output(output_id++, packed_src_shape, - &outputs->src_segment_pos)); - OP_REQUIRES_OK(ctx, ctx->allocate_output(output_id++, packed_src_shape, - &outputs->src_indices_in_input)); - outputs->src_segment_ids->matrix().setZero(); - outputs->src_segment_pos->matrix().setZero(); - outputs->src_indices_in_input->matrix().setZero(); - - OP_REQUIRES_OK(ctx, ctx->allocate_output(output_id++, packed_tgt_shape, - &outputs->tgt_segment_ids)); - OP_REQUIRES_OK(ctx, ctx->allocate_output(output_id++, packed_tgt_shape, - &outputs->tgt_segment_pos)); - OP_REQUIRES_OK(ctx, ctx->allocate_output(output_id++, packed_tgt_shape, - &outputs->tgt_indices_in_input)); - outputs->tgt_segment_ids->matrix().setZero(); - outputs->tgt_segment_pos->matrix().setZero(); - outputs->tgt_indices_in_input->matrix().setZero(); -} - -int PackSequencesOp::PackEntireInputs(OpKernelContext* ctx, - std::vector* pack_records) { - const auto& src_actual_seq_len = ctx->input(0).vec(); - const auto& tgt_actual_seq_len = ctx->input(1).vec(); - const auto input_num = src_actual_seq_len.size(); - - // We ask for a sufficiently large output batch size to pack all input - // sequences in its entirety. We drop input sequences if needed afterward, - // We also ensure that the first `packed_batch_size` rows are never empty - // by packing into them first. - TextPacking packing(/*columns=*/2, /*batch=*/input_num, - {packed_src_seq_len_, packed_tgt_seq_len_}, - /*align=*/1, - /*pack=*/true, /*spread_first_n=*/packed_batch_size_); - int max_output_batch_idx = 0; - for (int i = 0; i < input_num; ++i) { - int src_seq_len = src_actual_seq_len(i); - int tgt_seq_len = tgt_actual_seq_len(i); - if (src_seq_len < 1 || tgt_seq_len < 1) { - LOG_EVERY_N(WARNING, 500) - << "Input sequence with lengths (src=" << src_seq_len - << ", tgt=" << tgt_seq_len - << ") dropped as only positive actual lengths are allowed."; - continue; - } - PackRecord p; - p.index_in_input = i; - if (!packing.Add({src_seq_len, tgt_seq_len}, &p.packing)) { - // This only happens when the actual sequence length of the input is - // larger than the packed sequence length specified on the output. - LOG_EVERY_N(WARNING, 500) - << "Input sequence with lengths (src=" << src_seq_len - << ", tgt=" << tgt_seq_len - << ") dropped as its lengths exceed the output sequence length."; - continue; - } - max_output_batch_idx = std::max(p.packing.batch, max_output_batch_idx); - pack_records->push_back(p); - } - return max_output_batch_idx + 1; -} - -bool PackSequencesOp::DropPackedRows( - OpKernelContext* ctx, int num_rows, - absl::flat_hash_map* new_indices) { - if (num_rows <= packed_batch_size_) { - return false; - } - - // Simple reservoir sampling to pick `packed_batch_size` items out of - // `num_rows`: - // https://en.wikipedia.org/wiki/Reservoir_sampling#Simple_algorithm - std::vector indices_kept(packed_batch_size_); - for (int i = 1; i < packed_batch_size_; ++i) { - indices_kept[i] = i; - } - for (int i = packed_batch_size_; i < num_rows; ++i) { - std::uniform_int_distribution<> distribution(0, i); - int j; // Uniformly picked on [0, i]. - { - absl::MutexLock l(&mu_); - j = distribution(rnd_); - } - if (j < packed_batch_size_) { - indices_kept[j] = i; - } - } - for (int i = 0; i < packed_batch_size_; ++i) { - new_indices->insert({indices_kept[i], i}); - } - return true; -} - -void PackSequencesOp::WriteOutputs( - OpKernelContext* ctx, const std::vector& pack_records, - bool dropping_inputs, const absl::flat_hash_map* new_indices, - PackSequencesOutputs* outputs) { - const auto& src_actual_seq_len = ctx->input(0).vec(); - const auto& tgt_actual_seq_len = ctx->input(1).vec(); - - auto src_segment_ids = outputs->src_segment_ids->matrix(); - auto src_segment_pos = outputs->src_segment_pos->matrix(); - auto src_indices_in_input = outputs->src_indices_in_input->matrix(); - auto tgt_segment_ids = outputs->tgt_segment_ids->matrix(); - auto tgt_segment_pos = outputs->tgt_segment_pos->matrix(); - auto tgt_indices_in_input = outputs->tgt_indices_in_input->matrix(); - - for (const auto& p : pack_records) { - int output_idx = p.packing.batch; - if (dropping_inputs) { - auto new_idx_iter = new_indices->find(output_idx); - if (new_idx_iter == new_indices->end()) { - // This row is being dropped. - continue; - } - output_idx = new_idx_iter->second; - } - const int src_seq_idx = p.packing.time[0]; - for (int i = 0; i < src_actual_seq_len(p.index_in_input); ++i) { - src_segment_ids(output_idx, src_seq_idx + i) = p.packing.seq; - src_segment_pos(output_idx, src_seq_idx + i) = i; - src_indices_in_input(output_idx, src_seq_idx + i) = p.index_in_input; - } - const int tgt_seq_idx = p.packing.time[1]; - for (int i = 0; i < tgt_actual_seq_len(p.index_in_input); ++i) { - tgt_segment_ids(output_idx, tgt_seq_idx + i) = p.packing.seq; - tgt_segment_pos(output_idx, tgt_seq_idx + i) = i; - tgt_indices_in_input(output_idx, tgt_seq_idx + i) = p.index_in_input; - } - } -} - -REGISTER_KERNEL_BUILDER(Name("PackSequences").Device(DEVICE_CPU), - PackSequencesOp); - -// An op that applies a packing pattern on an input data. -template -class ApplyPackingOp : public OpKernel { - public: - explicit ApplyPackingOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} - - ~ApplyPackingOp() override {} - - void Compute(OpKernelContext* ctx) override { - ValidateInputs(ctx); - if (!ctx->status().ok()) { - return; - } - Tensor* output = nullptr; - // Allocates output and initializes with padding. - const Tensor& segment_ids = ctx->input(2); - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, segment_ids.shape(), &output)); - const T padding = ctx->input(1).scalar()(); - output->matrix().setConstant(padding); - Apply(ctx, output); - } - - private: - // Validates the shapes and types of inputs. - void ValidateInputs(OpKernelContext* ctx) { - const Tensor& input = ctx->input(0); - OP_REQUIRES( - ctx, TensorShapeUtils::IsMatrix(input.shape()), - errors::InvalidArgument("input must be a matrix, got input shape: ", - input.shape().DebugString())); - - const Tensor& padding = ctx->input(1); - OP_REQUIRES( - ctx, TensorShapeUtils::IsScalar(padding.shape()), - errors::InvalidArgument("padding must be a scalar, got padding shape: ", - padding.shape().DebugString())); - - const Tensor& segment_ids = ctx->input(2); - const Tensor& indices_in_input = ctx->input(3); - OP_REQUIRES( - ctx, - segment_ids.shape() == indices_in_input.shape() && - TensorShapeUtils::IsMatrix(segment_ids.shape()), - errors::InvalidArgument("segment_ids and indices_in_input must be " - "matrices of the same shape, got: ", - segment_ids.shape().DebugString(), " vs. ", - indices_in_input.shape().DebugString())); - } - - void Apply(OpKernelContext* ctx, Tensor* output) { - const auto& input = ctx->input(0).matrix(); - const auto input_rows = ctx->input(0).dim_size(0); - const auto input_columns = ctx->input(0).dim_size(1); - const auto& segment_ids = ctx->input(2).matrix(); - const auto& indices_in_input = ctx->input(3).matrix(); - auto output_matrix = output->matrix(); - const int64 rows = output->dim_size(0); - const int64 columns = output->dim_size(1); - // The CPU cost per each row is linear in the number of elements in that row - // (`columns`). We need to read segment_ids to discern each segment, and - // copy from input to output, plus a few extra cycles (e.g. checking - // bounds). The constant is guesstimated to be 4. - const int64 cost_per_unit = columns << 2; - ctx->device()->tensorflow_cpu_worker_threads()->workers->ParallelFor( - rows, cost_per_unit, [&](int64 begin, int64 end) { - for (int i = begin; i < end; ++i) { - for (int j = 0; j < columns; ++j) { - if (segment_ids(i, j) <= 0) { - // We do not skip the rest of the row because we cannot assume 0 - // segment ids only occur at end of row. For example, it might - // occur due to alignment requirements during packing. - continue; - } - const int start = j; - for (; j + 1 < columns && - segment_ids(i, j) == segment_ids(i, j + 1); - ++j) { - } - // [start, j] have the same, positive segment_ids. - const int actual_seq_len = j - start + 1; - // At output position (i, start), we need to copy over - // `actual_seq_len` T values from the input. - const int index_in_input = indices_in_input(i, start); - OP_REQUIRES( - ctx, - tensorflow::FastBoundsCheck(index_in_input, input_rows) && - tensorflow::FastBoundsCheck(actual_seq_len, - input_columns + 1), - errors::InvalidArgument( - "out of bound found packing at (", i, ", ", start, - ") for input index ", index_in_input, " with length ", - actual_seq_len, " where input shape is ", - ctx->input(0).shape().DebugString())); - std::memcpy(&output_matrix(i, start), &input(index_in_input, 0), - actual_seq_len * sizeof(T)); - } - } - }); - } - - TF_DISALLOW_COPY_AND_ASSIGN(ApplyPackingOp); -}; - -template <> -class ApplyPackingOp<::tensorflow::tstring> : public OpKernel { - public: - explicit ApplyPackingOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} - - ~ApplyPackingOp() override {} - - void Compute(OpKernelContext* ctx) override { - ValidateInputs(ctx); - if (!ctx->status().ok()) { - return; - } - Tensor* output = nullptr; - TensorShape output_shape({ctx->input(3).shape().dim_size(0)}); - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, output_shape, &output)); - Apply(ctx, output); - } - - private: - // Validates the shapes and types of inputs. - void ValidateInputs(OpKernelContext* ctx) { - const Tensor& input = ctx->input(0); - OP_REQUIRES( - ctx, TensorShapeUtils::IsVector(input.shape()), - errors::InvalidArgument("input must be a vector, got input shape: ", - input.shape().DebugString())); - - const Tensor& padding = ctx->input(1); - OP_REQUIRES( - ctx, TensorShapeUtils::IsScalar(padding.shape()), - errors::InvalidArgument("padding must be a scalar, got padding shape: ", - padding.shape().DebugString())); - - const Tensor& segment_ids = ctx->input(2); - const Tensor& indices_in_input = ctx->input(3); - OP_REQUIRES( - ctx, - segment_ids.shape() == indices_in_input.shape() && - TensorShapeUtils::IsMatrix(segment_ids.shape()), - errors::InvalidArgument("segment_ids and indices_in_input must be " - "matrices of the same shape, got: ", - segment_ids.shape().DebugString(), " vs. ", - indices_in_input.shape().DebugString())); - } - - void Apply(OpKernelContext* ctx, Tensor* output) { - const auto& input = ctx->input(0).vec<::tensorflow::tstring>(); - const auto num_input_rows = ctx->input(0).dim_size(0); - const auto& segment_ids = ctx->input(2).matrix(); - const auto& indices_in_input = ctx->input(3).matrix(); - auto output_vec = output->vec<::tensorflow::tstring>(); - const auto& sep = ctx->input(1).scalar<::tensorflow::tstring>()(); - for (int i = 0; i < output->dim_size(0); ++i) { - // input_rows condenses row i of indices_in_input, e.g. from - // [0, 0, 0, 3, 3, 4, 4, 4, 4, 0, 0] to [0, 3, 4]. - std::vector input_rows; - for (int j = 0; j < ctx->input(3).dim_size(1); ++j) { - auto row = indices_in_input(i, j); - if (segment_ids(i, j) && - (input_rows.empty() || input_rows.back() != row)) { - OP_REQUIRES(ctx, tensorflow::FastBoundsCheck(row, num_input_rows), - errors::InvalidArgument( - "out of bound found packing at (", i, ", ", j, - ") for input index ", row, " where input shape is ", - ctx->input(0).shape().DebugString())); - input_rows.push_back(row); - } - } - std::vector strs; - strs.reserve(input_rows.size()); - for (auto row : input_rows) { - strs.push_back(input(row)); - } - // Output on row i is the joined input strings for that row. - output_vec(i) = absl::StrJoin(strs, sep); - } - } - - TF_DISALLOW_COPY_AND_ASSIGN(ApplyPackingOp); -}; - -#define REGISTER(TYPE) \ - REGISTER_KERNEL_BUILDER( \ - Name("ApplyPacking").Device(DEVICE_CPU).TypeConstraint("T"), \ - ApplyPackingOp); - -TF_CALL_float(REGISTER); -TF_CALL_double(REGISTER); -TF_CALL_int32(REGISTER); -TF_CALL_int64(REGISTER); -TF_CALL_uint32(REGISTER); -TF_CALL_uint64(REGISTER); -TF_CALL_bool(REGISTER); -TF_CALL_string(REGISTER); - -#undef REGISTER - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/pack_ops_test.py b/lingvo/core/ops/pack_ops_test.py deleted file mode 100644 index c3c374a4..00000000 --- a/lingvo/core/ops/pack_ops_test.py +++ /dev/null @@ -1,379 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for pack_ops.""" - -import collections - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_utils -import numpy as np - -PackSequenceTestCase = collections.namedtuple('PackSequenceTestCase', [ - 'src_actual_seq_len', 'tgt_actual_seq_len', 'packed_batch_size', - 'packed_src_seq_len', 'packed_tgt_seq_len', 'src_segment_ids', - 'src_segment_pos', 'src_indices_in_input', 'tgt_segment_ids', - 'tgt_segment_pos', 'tgt_indices_in_input' -]) - -# Set default to empty. -PackSequenceTestCase.__new__.__defaults__ = (None,) * len( - PackSequenceTestCase._fields) - -ApplyPackingTestCase = collections.namedtuple( - 'ApplyPackingTestCase', - ['input', 'padding', 'segment_ids', 'indices_in_input', 'output']) - -# Set default to empty. -ApplyPackingTestCase.__new__.__defaults__ = (None,) * len( - ApplyPackingTestCase._fields) - - -def FindResultFromList(result, test_cases): - """Whether a result is among a list of possible results. - - Args: - result: A ops.PackSequences object that is the result of running - ops.pack_sequences(). - test_cases: A list of PackSequenceTestCase. - - Returns: - The index of first match found, or None for not found. - """ - for idx, test_case in enumerate(test_cases): - match = True - for attr in [ - 'src_segment_ids', 'src_segment_pos', 'src_indices_in_input', - 'tgt_segment_ids', 'tgt_segment_pos', 'tgt_indices_in_input' - ]: - if not np.array_equal(getattr(result, attr), getattr(test_case, attr)): - match = False - break - if not match: - continue - return idx - return None - - -class PackSequencesOpTest(test_utils.TestCase): - - def testPackSequences(self): - test_cases = { - 'Basic': - PackSequenceTestCase([1, 2, 1], [1, 2, 1], 2, 5, 5, - [[1, 2, 0, 0, 0], [1, 1, 0, 0, 0]], - [[0, 0, 0, 0, 0], [0, 1, 0, 0, 0]], - [[0, 2, 0, 0, 0], [1, 1, 0, 0, 0]], - [[1, 2, 0, 0, 0], [1, 1, 0, 0, 0]], - [[0, 0, 0, 0, 0], [0, 1, 0, 0, 0]], - [[0, 2, 0, 0, 0], [1, 1, 0, 0, 0]]), - 'SpreadFirstN': - PackSequenceTestCase([3, 1, 2], [4, 2, 1], 2, 5, 5, - [[1, 1, 1, 2, 2], [1, 0, 0, 0, 0]], - [[0, 1, 2, 0, 1], [0, 0, 0, 0, 0]], - [[0, 0, 0, 2, 2], [1, 0, 0, 0, 0]], - [[1, 1, 1, 1, 2], [1, 1, 0, 0, 0]], - [[0, 1, 2, 3, 0], [0, 1, 0, 0, 0]], - [[0, 0, 0, 0, 2], [1, 1, 0, 0, 0]]), - 'DifferentSrcTgtLengths': - PackSequenceTestCase([3, 2, 1], [4, 1, 5], 2, 4, 6, - [[1, 1, 1, 0], [1, 1, 2, 0]], - [[0, 1, 2, 0], [0, 1, 0, 0]], - [[0, 0, 0, 0], [1, 1, 2, 0]], - [[1, 1, 1, 1, 0, 0], [1, 2, 2, 2, 2, 2]], - [[0, 1, 2, 3, 0, 0], [0, 0, 1, 2, 3, 4]], - [[0, 0, 0, 0, 0, 0], [1, 2, 2, 2, 2, 2]]), - 'Padding': - PackSequenceTestCase([1], [2], 3, 3, 3, - [[1, 0, 0], [0, 0, 0], [0, 0, 0]], - [[0, 0, 0], [0, 0, 0], [0, 0, 0]], - [[0, 0, 0], [0, 0, 0], [0, 0, 0]], - [[1, 1, 0], [0, 0, 0], [0, 0, 0]], - [[0, 1, 0], [0, 0, 0], [0, 0, 0]], - [[0, 0, 0], [0, 0, 0], [0, 0, 0]]), - 'DroppingInputsTooLong': - PackSequenceTestCase([1, 3, 1], [4, 1, 2], 2, 2, 3, - [[1, 0], [0, 0]], [[0, 0], [0, 0]], - [[2, 0], [0, 0]], [[1, 1, 0], [0, 0, 0]], - [[0, 1, 0], [0, 0, 0]], - [[2, 2, 0], [0, 0, 0]]), - 'DroppingNonPositiveLengths': - PackSequenceTestCase([1, 0, 1], [0, 1, 3], 2, 2, 3, - [[1, 0], [0, 0]], [[0, 0], [0, 0]], - [[2, 0], [0, 0]], [[1, 1, 1], [0, 0, 0]], - [[0, 1, 2], [0, 0, 0]], - [[2, 2, 2], [0, 0, 0]]), - 'PackedBatchSize0': - PackSequenceTestCase( - [3, 1, 2, 0, 1, 6, 2, 3, 4, 1, 1], - [4, 2, 1, 1, 0, 2, 6, 1, 1, 4, 3], 0, 5, 5, - [[1, 1, 1, 2, 2], [1, 2, 2, 2, 0], [1, 1, 1, 1, 2], - [1, 0, 0, 0, 0]], [[0, 1, 2, 0, 1], [0, 0, 1, 2, 0], - [0, 1, 2, 3, 0], [0, 0, 0, 0, 0]], - [[0, 0, 0, 2, 2], [1, 7, 7, 7, 0], [8, 8, 8, 8, 9], - [10, 0, 0, 0, 0]], [[1, 1, 1, 1, 2], [1, 1, 2, 0, 0], - [1, 2, 2, 2, 2], [1, 1, 1, 0, 0]], - [[0, 1, 2, 3, 0], [0, 1, 0, 0, 0], [0, 0, 1, 2, 3], - [0, 1, 2, 0, 0]], [[0, 0, 0, 0, 2], [1, 1, 7, 0, 0], - [8, 9, 9, 9, 9], [10, 10, 10, 0, 0]]) - } - for name, test in test_cases.items(): - with self.session() as sess: - r = sess.run( - ops.pack_sequences( - tf.constant(test.src_actual_seq_len, tf.int32), - tf.constant(test.tgt_actual_seq_len, - tf.int32), test.packed_batch_size, - test.packed_src_seq_len, test.packed_tgt_seq_len)) - self.assertEqual(6, len(r), name) - self.assertAllEqual(r[0], test.src_segment_ids, name) - self.assertAllEqual(r[1], test.src_segment_pos, name) - self.assertAllEqual(r[2], test.src_indices_in_input, name) - self.assertAllEqual(r[3], test.tgt_segment_ids, name) - self.assertAllEqual(r[4], test.tgt_segment_pos, name) - self.assertAllEqual(r[5], test.tgt_indices_in_input, name) - - def testPackSequencesErrors(self): - test_cases = { - 'actual_seq_len must be the same shape': - PackSequenceTestCase([1, 1, 1], [1, 1], 2, 2, 2), - 'actual_seq_len must be a vector': - PackSequenceTestCase([[1], [1]], [[1], [1]], 2, 2, 2) - } - for name, test in test_cases.items(): - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, name): - with self.session() as sess: - sess.run( - ops.pack_sequences(test.src_actual_seq_len, - test.tgt_actual_seq_len, - test.packed_batch_size, - test.packed_src_seq_len, - test.packed_tgt_seq_len)) - - # seq_len must be a scalar. - test = PackSequenceTestCase([1, 1], [1, 1], 2, [2, 2], 2) - with self.assertRaisesRegex(TypeError, 'Expected int'): - with self.session() as sess: - sess.run( - ops.pack_sequences(test.src_actual_seq_len, test.tgt_actual_seq_len, - test.packed_batch_size, test.packed_src_seq_len, - test.packed_tgt_seq_len)) - - def testDroppingInputsFixedSeed(self): - # Packing 3 rows into 2, where we need to drop one row. - inputs = [[2, 1, 2], [1, 2, 1], 2, 2, 2] - test_cases = [ - # (seed, test_case) - ( - 45, - # dropping the last row - PackSequenceTestCase(*( - inputs + - [[[1, 1], [1, 0]], [[0, 1], [0, 0]], [[0, 0], [1, 0]], - [[1, 0], [1, 1]], [[0, 0], [0, 1]], [[0, 0], [1, 1]]]))), - ( - (1 << 37) + 1, - # dropping the second row - PackSequenceTestCase(*( - inputs + - [[[1, 1], [1, 1]], [[0, 1], [0, 1]], [[0, 0], [2, 2]], - [[1, 0], [1, 0]], [[0, 0], [0, 0]], [[0, 0], [2, 0]]]))), - ] - for seed, test in test_cases: - with self.session() as sess: - r = sess.run( - ops.pack_sequences( - tf.constant(test.src_actual_seq_len, tf.int32), - tf.constant(test.tgt_actual_seq_len, tf.int32), - test.packed_batch_size, - test.packed_src_seq_len, - test.packed_tgt_seq_len, - seed=seed)) - name = 'test case with seed {}'.format(seed) - self.assertEqual(6, len(r), name) - self.assertAllEqual(r[0], test.src_segment_ids, name) - self.assertAllEqual(r[1], test.src_segment_pos, name) - self.assertAllEqual(r[2], test.src_indices_in_input, name) - self.assertAllEqual(r[3], test.tgt_segment_ids, name) - self.assertAllEqual(r[4], test.tgt_segment_pos, name) - self.assertAllEqual(r[5], test.tgt_indices_in_input, name) - - def testDroppingInputsNonDeterministic(self): - # Packing 3 rows into 1, where we need to drop two rows. - inputs = [[2, 1, 2], [1, 2, 1], 1, 2, 2] - test_cases = [ - # keeping only the first row - PackSequenceTestCase(*( - inputs + - [[[1, 1]], [[0, 1]], [[0, 0]], [[1, 0]], [[0, 0]], [[0, 0]]])), - # keeping only the second row - PackSequenceTestCase(*( - inputs + - [[[1, 0]], [[0, 0]], [[1, 0]], [[1, 1]], [[0, 1]], [[1, 1]]])), - # keeping only the last row - PackSequenceTestCase(*( - inputs + - [[[1, 1]], [[0, 1]], [[2, 2]], [[1, 0]], [[0, 0]], [[2, 0]]])), - ] - counts = [0] * 3 - with self.session() as sess: - test = test_cases[0] - for _ in range(100): - r = sess.run( - ops.pack_sequences( - tf.constant(test.src_actual_seq_len, tf.int32), - tf.constant(test.tgt_actual_seq_len, - tf.int32), test.packed_batch_size, - test.packed_src_seq_len, test.packed_tgt_seq_len)) - match_idx = FindResultFromList(r, test_cases) - self.assertIsNotNone(match_idx, '{} is not a valid result'.format(r)) - counts[match_idx] += 1 - # We test that all possible outcomes occur sufficiently often to ensure that - # dropping is not biased. - # The probability of this test failing due to chance is less than 1 in a - # million runs, as scipy.stats.binom.cdf(10, 100, 0.3333) ~= 5e-8 - for idx, count in enumerate(counts): - self.assertGreater( - count, 10, - 'test case {} does not occur sufficiently often: {}'.format( - idx, counts)) - - -class ApplyPackingOpTest(test_utils.TestCase): - - def testApplyPacking(self): - test_cases = { - 'Basic': - ApplyPackingTestCase([[0, 1], [2, 3]], 0, [[1, 1], [1, 1]], - [[1, 1], [0, 0]], [[2, 3], [0, 1]]), - 'Padding': - ApplyPackingTestCase([[0, 1], [2, 3]], -1, [[1, 1, 0], [1, 1, 0]], - [[1, 1, 0], [0, 0, 0]], - [[2, 3, -1], [0, 1, -1]]), - 'Tiny': - ApplyPackingTestCase([[0, 1], [2, 3]], 0, [[1], [1]], [[0], [1]], - [[0], [2]]), - '5x2 input to 2x4': - ApplyPackingTestCase( - np.reshape(np.arange(10), - [5, 2]), -1, [[1, 1, 2, 2], [0, 1, 0, 2]], - [[2, 2, 3, 3], [0, 4, 0, 0]], [[4, 5, 6, 7], [-1, 8, -1, 0]]), - '6x4 input to 3x4': - ApplyPackingTestCase( - np.reshape(np.arange(24), [6, 4]), -1, - [[0, 0, 0, 1], [1, 1, 1, 1], [1, 2, 3, 4]], - [[0, 0, 0, 0], [1, 1, 1, 1], [2, 3, 4, 5]], - [[-1, -1, -1, 0], [4, 5, 6, 7], [8, 12, 16, 20]]), - '6x4 input to 3x5': - ApplyPackingTestCase( - np.reshape(np.arange(24), [6, 4]), -1, - [[0, 0, 0, 0, 0], [0, 1, 1, 0, 0], [0, 1, 1, 0, 2]], - [[0, 0, 0, 0, 0], [0, 1, 1, 0, 0], [0, 0, 0, 0, 3]], - [[-1, -1, -1, -1, -1], [-1, 4, 5, -1, -1], [-1, 0, 1, -1, 12]]), - '100x4 input to 3x5': - ApplyPackingTestCase( - np.reshape(np.arange(400), [100, 4]), -1, - [[1, 1, 1, 2, 2], [0, 1, 1, 1, 1], [2, 2, 0, 0, 3]], - [[99, 99, 99, 1, 1], [0, 50, 50, 50, 50], [90, 90, 0, 0, 3]], - [[396, 397, 398, 4, 5], [-1, 200, 201, 202, 203], - [360, 361, -1, -1, 12]]), - } - for name, test in test_cases.items(): - with self.session(): - output = ops.apply_packing( - tf.constant(test.input, tf.int32), - tf.constant(test.padding, tf.int32), - tf.constant(test.segment_ids, tf.int32), - tf.constant(test.indices_in_input, tf.int32)).eval() - self.assertAllEqual(output, test.output, name) - - def testApplyPackingTypes(self): - test = ApplyPackingTestCase([[0, 1], [2, 3]], 99, [[1, 1, 0], [1, 1, 0]], - [[1, 1, 0], [0, 0, 0]], - [[2, 3, 99], [0, 1, 99]]) - for dtype in [ - tf.int32, tf.int64, tf.float32, tf.float64, tf.uint32, tf.uint64 - ]: - with self.session(): - output = ops.apply_packing( - tf.constant(test.input, dtype), tf.constant(test.padding, dtype), - tf.constant(test.segment_ids, tf.int32), - tf.constant(test.indices_in_input, tf.int32)).eval() - expected = tf.constant(test.output, dtype).eval() - self.assertAllEqual(output, expected, dtype) - - def testApplyPackingStrings(self): - test_cases = { - 'Basic': - ApplyPackingTestCase(['a', 'b'], ',', [[1, 1]], [[1, 0]], [b'b,a']), - 'Repeated': - ApplyPackingTestCase(['a', 'b'], ',', [[1, 1, 1]], [[1, 1, 1]], - [b'b']), - 'Separator': - ApplyPackingTestCase(['a', 'b', 'c', 'd'], '=', [[1, 1, 1, 0]], - [[1, 0, 3, 2]], [b'b=a=d']), - 'MultiRows': - ApplyPackingTestCase(['a', 'b', 'c', 'd'], ';', - [[1, 1, 1, 0], [0, 1, 1, 1]], - [[2, 2, 1, 0], [2, 0, 1, 1]], - [b'c;b', b'a;b']), - 'SingleString': - ApplyPackingTestCase(['a', 'b', 'c', 'd'], ',', - [[0, 0, 1], [0, 1, 0]], [[0, 1, 2], [0, 1, 2]], - [b'c', b'b']), - 'EmptyRow': - ApplyPackingTestCase(['a', 'b', 'c', 'd'], ',', - [[0, 0, 0], [1, 1, 1]], [[0, 1, 2], [0, 0, 2]], - [b'', b'a,c']), - } - for name, test in test_cases.items(): - with self.session(): - output = ops.apply_packing( - tf.constant(test.input, tf.string), - tf.constant(test.padding, tf.string), - tf.constant(test.segment_ids, tf.int32), - tf.constant(test.indices_in_input, tf.int32)).eval() - self.assertAllEqual(output, test.output, name) - - def testApplyPackingErrors(self): - test_cases = { - 'out of bound': - ApplyPackingTestCase([[0, 1], [2, 3]], 0, [[1, 1, 1], [1, 0, 0]], - [[1, 1, 1], [0, 0, 0]]), - 'out of bound ': - ApplyPackingTestCase([[0, 1], [2, 3]], 0, [[1, 1], [1, 0]], - [[0, 0], [2, 0]]), - 'out of bound.': - ApplyPackingTestCase(['a', 'b'], ',', [[1, 2]], [[1, 2]]), - 'segment_ids and indices_in_input must be matrices': - ApplyPackingTestCase([[0, 1], [2, 3]], 0, [1, 1], [0, 0]), - 'segment_ids and indices_in_input must be matrices of the same shape': - ApplyPackingTestCase([[0, 1], [2, 3]], 0, [[1, 1], [1, 0]], - [[0, 0], [0, 0], [0, 0]]), - 'input must be a matrix': - ApplyPackingTestCase([0, 1], 0, [[1]], [[0]]), - 'padding must be a scalar': - ApplyPackingTestCase([[0, 1], [2, 3]], [-1], [[1]], [[0]]), - } - for name, test in test_cases.items(): - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, name): - with self.session(): - ops.apply_packing(test.input, test.padding, test.segment_ids, - test.indices_in_input).eval() - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/preconditioner_captain.cc b/lingvo/core/ops/preconditioner_captain.cc deleted file mode 100644 index cfdfc589..00000000 --- a/lingvo/core/ops/preconditioner_captain.cc +++ /dev/null @@ -1,133 +0,0 @@ -/* Copyright 2020 The TensorFlow 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. -==============================================================================*/ -#include "lingvo/core/ops/preconditioner_captain.h" - -#include "tensorflow/core/framework/graph.pb.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -tensorflow::Session* CreateSessionForPreconditioning( - const PreconditionerCaptainOptions& options) { - tensorflow::SessionOptions session_options; - session_options.target = ""; - session_options.config.set_log_device_placement(true); - auto* session = NewSession(session_options); - GraphDef gdef; - CHECK(!options.preconditioner_compute_graphdef.empty()); - gdef.ParseFromString(options.preconditioner_compute_graphdef); - TF_CHECK_OK(session->Create(gdef)); - return session; -} - -const float kEpsilon = 5e-2; - -} // namespace - -PreconditionerCaptain::PreconditionerCaptain( - const PreconditionerCaptainOptions& options) - : options_(options) { - workers_ = absl::make_unique( - tensorflow::Env::Default(), "preconditioners-threads", - options.num_compute_threads); - // NOTE: For distributing svd across all host machines, you may add many - // sessions against other CPU hosts. - sessions_.emplace_back(CreateSessionForPreconditioning(options)); -} - -PreconditionerCaptain::~PreconditionerCaptain() {} - -Tensor PreconditionerCaptain::GetPreconditioner(const std::string& key, - bool* ok) { - mutex_lock l(mu_); - if (preconditioners_.find(key) == preconditioners_.end()) { - *ok = false; - return {}; - } - *ok = true; - return preconditioners_[key]; -} - -void PreconditionerCaptain::InsertGradientStatistics(const std::string& key, - Tensor statistics, - Tensor exponent, - int global_step, - bool sync) { - const int session_to_use = std::hash{}(key) % sessions_.size(); - bool should_calculate_preconditioner = true; - { - mutex_lock l(mu_); - if (gradient_statistics_.find(key) != gradient_statistics_.end()) { - if (gradient_statistics_[key].global_step == global_step) { - should_calculate_preconditioner = false; - } - } - if (should_calculate_preconditioner) { - gradient_statistics_[key] = {global_step, statistics}; - } - } - if (should_calculate_preconditioner) { - auto run_preconditioner = [this, key, session_to_use, global_step, - statistics, exponent] { - { - mutex_lock l(mu_); - ++active_preconditioners_; - } - - Status status; - std::vector outputs; - std::vector> inputs; - inputs.push_back(std::make_pair("input", statistics)); - inputs.push_back(std::make_pair("exponent", exponent)); - do { - outputs.clear(); - LOG(INFO) << "START: inverse pth root for " << key << " @ " - << global_step << " " << statistics.shape().DebugString(); - status = sessions_[session_to_use]->Run(inputs, {"output", "diff"}, {}, - &outputs); - LOG(INFO) << "DONE: Inverse pth root for " << key << " @ " - << global_step << " " << statistics.shape().DebugString(); - LOG(INFO) << status.error_message(); - } while (!status.ok()); - - // Certain matrices cause SVD to have less precision with its calculation - // of inverse pth root. We handle that case by ignoring preconditioners - // for those updates. - if (outputs[1].scalar()() < kEpsilon) { - mutex_lock l(mu_); - preconditioners_[key] = outputs[0]; - LOG(INFO) << "For " << key << " @ " << global_step - << " with diff (u-v):" << outputs[1].scalar()(); - } else { - LOG(INFO) << "Skipping preconditioner update for " << key << " @ " - << global_step - << " with diff (u-v):" << outputs[1].scalar()(); - } - { - mutex_lock l(mu_); - --active_preconditioners_; - } - }; - if (!sync) { - workers_->Schedule(run_preconditioner); - } else { - run_preconditioner(); - } - } -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/preconditioner_captain.h b/lingvo/core/ops/preconditioner_captain.h deleted file mode 100644 index 990f5adb..00000000 --- a/lingvo/core/ops/preconditioner_captain.h +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright 2020 The TensorFlow 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. -==============================================================================*/ -#ifndef LINGVO_CORE_OPS_PRECONDITIONER_CAPTAIN_H_ -#define LINGVO_CORE_OPS_PRECONDITIONER_CAPTAIN_H_ - -#include -#include -#include - -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/platform/threadpool.h" -#include "tensorflow/core/public/session.h" - -namespace tensorflow { -namespace lingvo { - -// PreconditionerCaptain service options. -struct PreconditionerCaptainOptions { - // Number of threads to run; - int32 num_compute_threads = 64; - // Graph that computes the inverse pth root. - string preconditioner_compute_graphdef; -}; - -struct StatisticsValue { - // Global step for entry for the statistics. - int global_step; - // Value of the tensor statistics. - Tensor value; -}; - -class PreconditionerCaptain { - public: - explicit PreconditionerCaptain(const PreconditionerCaptainOptions& options); - ~PreconditionerCaptain(); - - // Disable copy (and move) semantics. - PreconditionerCaptain(const PreconditionerCaptain&) = delete; - PreconditionerCaptain& operator=(const PreconditionerCaptain&) = delete; - - // Insert gradient statistics. - void InsertGradientStatistics(const std::string& key, Tensor statistics, - Tensor exponent, int global_step, bool sync); - - // Get preconditioner with status. - Tensor GetPreconditioner(const std::string& key, bool* ok); - - private: - // Options for the captain. - const PreconditionerCaptainOptions options_; - - // Options to throttle preconditioning. - int32 active_preconditioners_ = 0; - - // Executor used to serve the requests and compute preconditioners. - std::unique_ptr workers_; - // Mutex protecting the statistics, and preconditioners. - mutex mu_; - // A map name to preconditioners - std::unordered_map gradient_statistics_; - std::unordered_map preconditioners_; - // Internal sessions used to compute preconditioners. - std::vector> sessions_; -}; - -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_PRECONDITIONER_CAPTAIN_H_ diff --git a/lingvo/core/ops/preconditioner_op_kernels.cc b/lingvo/core/ops/preconditioner_op_kernels.cc deleted file mode 100644 index f275e71e..00000000 --- a/lingvo/core/ops/preconditioner_op_kernels.cc +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright 2020 The TensorFlow 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. -==============================================================================*/ -#include -#include -#include - -#include "lingvo/core/ops/preconditioner_captain.h" -#include "tensorflow/core/framework/kernel_def_builder.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/types.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/lib/core/status.h" -#include "tensorflow/core/platform/logging.h" - - -const int kShampooComputeThreads = 64; - -namespace tensorflow { -namespace lingvo { -namespace { - -void MakePreconditionerCaptainOptionsFromContext( - OpKernelConstruction* context, PreconditionerCaptainOptions* options) { - OP_REQUIRES_OK(context, - context->GetAttr("preconditioner_compute_graphdef", - &options->preconditioner_compute_graphdef)); - options->num_compute_threads = kShampooComputeThreads; -} - -} // namespace - -PreconditionerCaptain* global_preconditioner_captain = nullptr; - -void PreconditionerCaptainServiceInit( - const PreconditionerCaptainOptions& options) { - global_preconditioner_captain = new PreconditionerCaptain(options); -} - -PreconditionerCaptain* get_or_create_preconditioner_captain( - const PreconditionerCaptainOptions& options) { - static std::once_flag global_preconditioner_captain_init_once; - std::call_once(global_preconditioner_captain_init_once, - &PreconditionerCaptainServiceInit, options); - return global_preconditioner_captain; -} - -class GetPreconditioners : public OpKernel { - public: - explicit GetPreconditioners(OpKernelConstruction* context) - : OpKernel(context) { - OP_REQUIRES_OK(context, context->GetAttr("keys", &keys_)); - MakePreconditionerCaptainOptionsFromContext(context, &options_); - } - - void Compute(OpKernelContext* context) override { - auto* preconditioner_captain = - get_or_create_preconditioner_captain(options_); - std::vector statuses(keys_.size()); - OpInputList shapes; - OP_REQUIRES_OK(context, context->input_list("shapes", &shapes)); - - OpOutputList output_list; - OP_REQUIRES_OK(context, context->output_list("outputs", &output_list)); - - OpOutputList status_list; - OP_REQUIRES_OK(context, context->output_list("statuses", &status_list)); - - for (int i = 0; i < keys_.size(); ++i) { - bool ok; - Tensor output = preconditioner_captain->GetPreconditioner(keys_[i], &ok); - if (ok) { - output_list.set(i, output); - } else { - auto shape_t = shapes[i].flat(); - TensorShape shape; - TF_CHECK_OK(TensorShapeUtils::MakeShape(shape_t.data(), shape_t.size(), - &shape)); - Tensor zero_output(DT_FLOAT, shape); - zero_output.flat().setZero(); - output_list.set(i, zero_output); - } - Tensor status_t(DT_BOOL, TensorShape({})); - status_t.scalar()() = ok; - status_list.set(i, status_t); - } - } - - private: - // Options for preconditioner. - PreconditionerCaptainOptions options_; - // Keys for the tensors. - std::vector keys_; -}; - -REGISTER_KERNEL_BUILDER(Name("GetPreconditioners").Device(DEVICE_CPU), - GetPreconditioners); - -class ComputePreconditionersOp : public OpKernel { - public: - explicit ComputePreconditionersOp(OpKernelConstruction* context) - : OpKernel(context) { - OP_REQUIRES_OK(context, context->GetAttr("keys", &keys_)); - MakePreconditionerCaptainOptionsFromContext(context, &options_); - OP_REQUIRES_OK(context, context->GetAttr("sync", &sync_)); - } - - void Compute(OpKernelContext* context) override { - auto* preconditioner_captain = - get_or_create_preconditioner_captain(options_); - OpInputList inputs; - OP_REQUIRES_OK(context, context->input_list("inputs", &inputs)); - OpInputList exponents; - OP_REQUIRES_OK(context, context->input_list("exponents", &exponents)); - const Tensor* global_step_t; - OP_REQUIRES_OK(context, context->input("global_step", &global_step_t)); - const int global_step = global_step_t->scalar()(); - - for (int i = 0; i < inputs.size(); ++i) { - Tensor statistics = inputs[i]; - Tensor exponent = exponents[i]; - preconditioner_captain->InsertGradientStatistics( - keys_[i], statistics, exponent, global_step, sync_); - } - } - - private: - // Options for preconditioner. - PreconditionerCaptainOptions options_; - // Keys for the tensors. - std::vector keys_; - // Whether to run preconditioner synchronously. - bool sync_ = false; -}; - -REGISTER_KERNEL_BUILDER(Name("ComputePreconditioners").Device(DEVICE_CPU), - ComputePreconditionersOp); - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/preconditioner_op_kernels_test.py b/lingvo/core/ops/preconditioner_op_kernels_test.py deleted file mode 100644 index 9c75f7a7..00000000 --- a/lingvo/core/ops/preconditioner_op_kernels_test.py +++ /dev/null @@ -1,88 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for preconditioner driver.""" - -import lingvo.compat as tf -from lingvo.core import ops -import numpy as np - - -class PreconditionerTest(tf.test.TestCase): - - def inverse_pth_root(self, input_t, exponent, epsilon=1e-12): - input_t_f64 = tf.cast(input_t, tf.float64) - s, u, v = tf.linalg.svd( - input_t_f64 + - tf.eye(tf.shape(input_t_f64)[0], dtype=tf.float64) * epsilon, - full_matrices=True) - val = tf.matmul( - tf.matmul( - u, - tf.linalg.tensor_diag( - tf.pow(tf.maximum(s, epsilon), tf.cast(exponent, tf.float64)))), - tf.transpose(v)) - return tf.cast(val, tf.float32), tf.reduce_max(tf.abs(u - v)) - - def inverse_pth_root_graph(self, epsilon=1e-12): - graph = tf.Graph() - with graph.as_default(): - exponent_t = tf.placeholder(dtype=tf.float32, name='exponent', shape=None) - input_t = tf.placeholder(dtype=tf.float32, name='input', shape=None) - output, diff = self.inverse_pth_root(input_t, exponent_t, epsilon) - tf.identity(output, 'output') - tf.identity(tf.cast(diff, tf.float32), 'diff') - return graph.as_graph_def().SerializeToString() - - def testPreconditioning(self): - preconditioner_compute_graphdef = self.inverse_pth_root_graph() - with tf.Session(): - global_step = tf.train.get_or_create_global_step() - self.evaluate(tf.global_variables_initializer()) - rand_input_1_t = np.random.rand(4, 4) - rand_input_2_t = np.random.rand(4, 4) - exponents = [-0.25, -0.25] - symmetric_input_1_t = np.dot(rand_input_1_t, rand_input_1_t.transpose()) - symmetric_input_2_t = np.dot(rand_input_2_t, rand_input_2_t.transpose()) - outputs, statuses = ops.get_preconditioners( - [tf.shape(symmetric_input_1_t), - tf.shape(symmetric_input_2_t)], - keys=['a', 'b'], - preconditioner_compute_graphdef=preconditioner_compute_graphdef) - self.assertFalse(any(self.evaluate(statuses))) - preconditioner = ops.compute_preconditioners( - [symmetric_input_1_t, symmetric_input_2_t], - exponents, - tf.cast(global_step, tf.int32), - keys=['a', 'b'], - sync=True, - preconditioner_compute_graphdef=preconditioner_compute_graphdef) - self.assertAllClose(outputs[0].eval(), np.zeros((4, 4)), atol=1e-4) - self.assertAllClose(outputs[1].eval(), np.zeros((4, 4)), atol=1e-4) - preconditioner.run() - self.assertTrue(any(self.evaluate(statuses))) - expected_output_1_t = self.inverse_pth_root(symmetric_input_1_t, - exponents[0]) - expected_output_2_t = self.inverse_pth_root(symmetric_input_2_t, - exponents[1]) - outputs_np = self.evaluate(outputs) - self.assertAllClose( - outputs_np[0], expected_output_1_t[0].eval(), atol=1e-1) - self.assertAllClose( - outputs_np[1], expected_output_2_t[0].eval(), atol=1e-1) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/random_ops_kernels.cc b/lingvo/core/ops/random_ops_kernels.cc deleted file mode 100644 index 15e981f8..00000000 --- a/lingvo/core/ops/random_ops_kernels.cc +++ /dev/null @@ -1,96 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include - -#include "absl/synchronization/mutex.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -class RandomPermutationSequenceOp : public OpKernel { - public: - explicit RandomPermutationSequenceOp(OpKernelConstruction* ctx) - : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("num", &num_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("batch", &batch_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("repeat", &repeat_)); - int64 seed; - OP_REQUIRES_OK(ctx, ctx->GetAttr("seed", &seed)); - if (seed == 0) { - std::random_device device("/dev/urandom"); - seed = std::mt19937_64(device())(); - } - rnd_.seed(seed); - Fill(); - } - - void Compute(OpKernelContext* ctx) override { - absl::MutexLock l(&mu_); - OP_REQUIRES(ctx, !ids_.empty() || repeat_, - errors::OutOfRange("Epoch ended.")); - if (ids_.empty()) Fill(); - - int start = 0; - int n = std::min(batch_, ids_.size()); - - Tensor* out; - const int out_size = repeat_ ? batch_ : n; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({out_size}), &out)); - for (int i = 0; i < n; ++i) { - out->flat()(i) = ids_[ids_.size() - 1 - i]; - } - ids_.resize(ids_.size() - n); - start += n; - - while (repeat_ && ids_.empty()) { - Fill(); - n = std::min(batch_ - start, ids_.size()); - for (int i = 0; i < n; ++i) { - out->flat()(start + i) = ids_[ids_.size() - 1 - i]; - } - ids_.resize(ids_.size() - n); - start += n; - } - } - - private: - int32 num_; - int32 batch_; - bool repeat_; - - absl::Mutex mu_; - std::mt19937 rnd_; - std::vector ids_; - - void Fill() { - CHECK(ids_.empty()); - ids_.resize(num_); - for (int i = 0; i < num_; ++i) ids_[i] = i; - for (int i = num_ - 1; i > 0; --i) { - const int32 pos = rnd_() % i; - std::swap(ids_[i], ids_[pos]); - } - } -}; -REGISTER_KERNEL_BUILDER(Name("RandomPermutationSequence").Device(DEVICE_CPU), - RandomPermutationSequenceOp); -} // namespace -} // end namespace lingvo -} // end namespace tensorflow diff --git a/lingvo/core/ops/random_ops_test.py b/lingvo/core/ops/random_ops_test.py deleted file mode 100644 index 0ca00fe5..00000000 --- a/lingvo/core/ops/random_ops_test.py +++ /dev/null @@ -1,62 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for random_ops.""" - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_utils - -FLAGS = tf.flags.FLAGS - - -class RandomOpsTest(test_utils.TestCase): - - def testRandomPermutationSequenceRepeat(self): - with self.session(): - out = ops.random_permutation_sequence(num=20, batch=7, repeat=True) - - remaining = list(range(20)) - for _ in range(10): - # Each epoch takes exactly 3 steps. - vals = self.evaluate(out).tolist() + self.evaluate( - out).tolist() + self.evaluate(out).tolist() - self.assertEqual(len(vals), 21) - - # Contains all the remaining values from previous epoch. - for x in remaining: - vals.remove(x) # Raises exception if x is not in vals. - - # Remaining items have no duplicates. - self.assertEqual(len(vals), len(set(vals))) - - remaining = list(set(range(20)) - set(vals)) - - def testRandomPermutationSequenceNoRepeat(self): - with self.session(): - out = ops.random_permutation_sequence(num=20, batch=7, repeat=False) - - # Each epoch takes exactly 3 steps. - vals = self.evaluate(out).tolist() + self.evaluate( - out).tolist() + self.evaluate(out).tolist() - self.assertEqual(list(range(20)), sorted(vals)) - - # repeat=False. We should see OutOfRange error. - with self.assertRaises(tf.errors.OutOfRangeError): - self.evaluate(out) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/record.proto b/lingvo/core/ops/record.proto deleted file mode 100644 index 3d6e7ad3..00000000 --- a/lingvo/core/ops/record.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; - -package tensorflow.lingvo; - -import "tensorflow/core/framework/tensor.proto"; - -// A message Record is just a few named tensors. -message Record { - map fields = 1; -} diff --git a/lingvo/core/ops/record_batcher.cc b/lingvo/core/ops/record_batcher.cc deleted file mode 100644 index 376692be..00000000 --- a/lingvo/core/ops/record_batcher.cc +++ /dev/null @@ -1,485 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -// Implementation notes: -// -// * RecordBatcher uses two threadpools. processor_thread_ and -// merger_thread_. -// -// * Processor threads -// -// * call the yielder to get a Record and process it into a TensorVec. -// -// * Processed TensorVec are put into buckets according to the -// bucket key returned by processor->Process(). -// -// * When one bucket is full (according to bucket_batch_limit), all -// TensorVec accumulated in that bucket is handed off to -// to_flush_. -// -// * If to_flush_ is non-empty, the processor thread blocks. -// -// * The only merger thread pulls off TensorVecs from to_flush_ and -// calls processor->Merge() to merge samples into a single batch. It -// then hands the merged batch into curr_. If there is already one -// unconsumed batch, the merger thread blocks. -// -// NOTE: merger_thread_ itself is single-threaded. We expect that if -// processor->Merge() becomes bottleneck (memory copy bounded), we -// can change the implementation of processor->Merge() to leverage -// multi-threaded merge. -// -// -// Peak memory usage estimate given the above algorithm: -// yielder buffer memory // RecordYielder.file_buffer_size -// + all TensorVecs in buckets // Sum of RecordBatcher.bucket_batch_limit -// + to_flush_ // The batch to be merged. -// + to_flush // The batch being merged. -// + merged // The batch being merged. -// + curr_ // The batch to be consumed. -// -// If we assume each sample is roughly M bytes (in its string format -// or tensor format), each output batch has the batch size B, we can -// estimate the peak memory usage of one RecordYielder+RecordBatcher -// is roughly -// -// M * (file_buffer_size + sum(bucket_batch_limit) * 2 + B * 2) - -#include "lingvo/core/ops/record_batcher.h" - -#include -#include - -#include "absl/synchronization/mutex.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/lib/strings/strcat.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/platform/errors.h" - -namespace tensorflow { -namespace lingvo { - -RecordBatcher::RecordBatcher(const Options& opts, RecordYielder* yielder, - RecordProcessor* processor) - : opts_(opts), - yielder_(yielder), - processor_(processor), - processor_thread_(new thread::ThreadPool( - Env::Default(), ThreadOptions(), "record_batcher_processor", - opts_.num_threads, /* low_latency_hint */ false)), - merger_thread_(new thread::ThreadPool(Env::Default(), ThreadOptions(), - "record_batcher_merger", 1, - /* low_latency_hint */ false)), - curr_empty_(this, &ME::CurrEmpty), - curr_non_empty_(this, &ME::CurrNonEmpty), - to_flush_empty_(this, &ME::ToFlushEmpty), - to_flush_non_empty_(this, &ME::ToFlushNonEmpty), - bucket_upper_bound_(opts_.bucket_upper_bound) { - CHECK_EQ(opts_.bucket_upper_bound.size(), opts_.bucket_batch_limit.size()); - buckets_.resize(opts_.bucket_upper_bound.size()); - length_histogram_.resize(opts_.bucket_upper_bound.back() + 1, 0); - start_time_ = std::time(nullptr); - { - absl::MutexLock l(&mu_); - last_log_update_time_ = start_time_; - } - - merger_thread_->Schedule([this]() { - MergerLoop(); - absl::MutexLock l(&mu_); - merger_loop_done_ = true; - }); -} - -RecordBatcher::~RecordBatcher() { - { - absl::MutexLock l(&mu_); - stop_ = true; - } - delete processor_thread_; - delete merger_thread_; - yielder_->Close(); - delete processor_; -} - -Status RecordBatcher::EnsureInitialized(OpKernelContext* ctx) { - if (is_initialized_) { - return Status::OK(); - } - TF_RETURN_IF_ERROR(processor_->Initialize(ctx)); - for (int i = 0; i < opts_.num_threads; i++) { - processor_thread_->Schedule([this]() { - ProcessorLoop(); - absl::MutexLock l(&mu_); - processor_loop_done_count_++; - }); - } - is_initialized_ = true; - LOG(INFO) << "batcher initialized"; - return Status::OK(); -} - -Status RecordBatcher::GetNext(OpKernelContext* ctx, int64* bucket, - TensorVec* batch) { - absl::MutexLock l(&mu_); - TF_RETURN_IF_ERROR(EnsureInitialized(ctx)); - // Wait for either curr to be non-empty, or for the merger thread to be - // complete. - WaitForCurrNonEmpty(); - - // If the buffer is still empty, it must be because the merger loop is done - // due to an EoF. - if (curr_.empty()) { - CHECK(merger_loop_done_); - return stop_status_; - } - - *bucket = curr_bucket_; - curr_bucket_ = -1; - using std::swap; - swap(*(batch), curr_); - curr_.clear(); - return Status::OK(); -} - -void RecordBatcher::IncrementHistogram(int64 bucket) { - if (bucket > bucket_upper_bound_.back()) return; - length_histogram_[bucket]++; -} - -void RecordBatcher::AdjustBuckets() { - // The length histogram is too big to compute with quickly. - // We distill it down to a histogram with bins of equal cost (equal area). - int64 ideal_cost = 0; - for (int i = 0; i < length_histogram_.size(); i++) { - ideal_cost += length_histogram_[i] * i; - } - if (ideal_cost == 0) { - return; - } - - // The histogram is pairs of (upper_length_bound, items_in_bucket). - std::vector> compact_histogram; - int64 cost_so_far = 0; - int64 count_in_bucket = 0; - int bucket_index = 0; - const int kCompactBuckets = 500; - for (int i = 0; i < length_histogram_.size(); i++) { - cost_so_far += length_histogram_[i] * i; - count_in_bucket += length_histogram_[i]; - int64 cost_target = ideal_cost * ((bucket_index + 1.0) / kCompactBuckets); - if (cost_so_far >= cost_target && - compact_histogram.size() != kCompactBuckets) { - compact_histogram.push_back(std::make_pair(i, count_in_bucket)); - count_in_bucket = 0; - bucket_index++; - } - } - // Make sure that the last bucket ends at the user-specified upper bound. - compact_histogram.push_back( - std::make_pair(bucket_upper_bound_.back(), count_in_bucket)); - - // Clear the fine-grained histogram to prepare for the next cycle. - length_histogram_.assign(opts_.bucket_upper_bound.back() + 1, 0); - - const int num_lengths = compact_histogram.size(); - const int num_buckets = bucket_upper_bound_.size(); - - // Now we shrink the compact histogram even further using dynamic programming. - // c[i, j, k]: the cumulative cost for the first i histogram groups, if the - // next bucketing point is at index j (j > i), and there are k more bucketing - // points available to use for the remaining elements. - Tensor cost(DT_INT64, TensorShape({num_lengths, num_lengths, num_buckets})); - auto c = cost.tensor(); - - // s(i, j) is the total cost of computing the items in histogram bucket i - // when padding them out to the size of bucket j. - auto s = [&compact_histogram](int i, int j) { - const int64 bucket_j_width = compact_histogram[j].first; - const int64 bucket_i_count = compact_histogram[i].second; - return bucket_i_count * bucket_j_width; - }; - - for (int i = 0; i < num_lengths; i++) { - for (int j = i + 1; j < num_lengths; j++) { - // If there are no buckets left and j is the next bucket, the - // cost is \sum_i 0) { - c(i, j, 0) += c(i - 1, j, 0); - } - // When we have some buckets to use, we can choose to insert bucket - // boundaries to reduce computation. - for (int k = 1; k < num_buckets; k++) { - if (i > 0) { - // When we choose to put a bucket boundary here at position i, we - // can compute these new items with minimal padding [s(i, i)]. - int64 cost_choose = c(i - 1, i, k - 1) + s(i, i); - // If we don't put a bucket boundary here, we have to wait until - // position j, which means extra padding. [s(i, j)]. - int64 cost_not_choose = c(i - 1, j, k) + s(i, j); - c(i, j, k) = std::min(cost_choose, cost_not_choose); - } else { - c(i, j, k) = s(i, j); - } - } - } - } - - std::vector buckets; - buckets.push_back(num_lengths - 1); - const int64 best_cost = c(num_lengths - 2, num_lengths - 1, num_buckets - 1); - int64 remaining_cost = best_cost; - for (int i = num_lengths - 2; i > 0; i--) { - int buckets_left = num_buckets - buckets.size(); - if (buckets_left <= 0) break; - int prev_bucket = buckets.back(); - int64 cost_choose = c(i - 1, i, buckets_left - 1) + s(i, i); - int64 cost_not_choose = - c(i - 1, prev_bucket, buckets_left) + s(i, prev_bucket); - if (remaining_cost == cost_choose) { - buckets.push_back(i); - remaining_cost -= s(i, i); - } else if (remaining_cost == cost_not_choose) { - remaining_cost -= s(i, prev_bucket); - } else { - // This didn't make sense; keep the buckets as they are. - LOG(WARNING) << "AdjustBuckets: backtrace failed."; - return; - } - } - - std::reverse(buckets.begin(), buckets.end()); - - // We keep the same maximum value that the user entered, but all other - // boundaries are updated. - std::vector bucket_strings; - for (int i = 0; i < buckets.size() - 1; i++) { - bucket_upper_bound_[i] = compact_histogram[buckets[i]].first; - bucket_strings.push_back(strings::StrCat(bucket_upper_bound_[i])); - } - bucket_strings.push_back(strings::StrCat(bucket_upper_bound_.back())); - - // Compute the amount of padding waste from choosing this bucket assignment. - LOG(INFO) << "Buckets: [" << str_util::Join(bucket_strings, ", ") << "] " - << "Waste: " - << (best_cost - static_cast(ideal_cost)) / best_cost; -} - -void RecordBatcher::FlushAllBuckets() { - for (int i = 0; i < buckets_.size(); ++i) { - if (!buckets_[i].empty()) { - CHECK_LE(static_cast(buckets_[i].size()), - opts_.bucket_batch_limit[i]); - to_flush_.push_back({i, std::move(buckets_[i])}); - buckets_[i].clear(); - } - } -} - -void RecordBatcher::ProcessorLoop() { - // Multiply next_status_update_duration_seconds_ by 2 every update. - const int64 status_update_duration_multiplier = 2; - std::vector out_of_range_buckets; - while (true) { - { - absl::MutexLock l(&mu_); - if (stop_) { - return; - } - } - - // Get the next record. - Record record; - record.source_id = kDefaultSourceId; - Status s = yielder_->Yield(&record); - // If yielder returns OutOfRange, set - // the out status appropriately and return. - if (errors::IsOutOfRange(s)) { - absl::MutexLock l(&mu_); - stop_status_ = s; - stop_ = true; - return; - } - - if (!s.ok()) { - LOG(WARNING) << s; - continue; - } - - // Parse the record. - int64 bucket; - TensorVec sample; - s = processor_->Process(record, &bucket, &sample); - if (!s.ok()) { - // Print error message. Some example processors use CANCELLED for data - // that are filtered out. Print only first 10 such errors. - if (errors::IsCancelled(s)) { - { - absl::MutexLock l(&mu_); - ++total_records_skipped_; - - static int log_counter = 0; - if (log_counter < 10) { - log_counter++; - LOG(WARNING) << s; - } - } - } else if (errors::IsNotFound(s) || errors::IsPermissionDenied(s)) { - // Terminates program if an unregistered custom op is used by - // the processor, or any access permission denied error. - // - // Consider setting *out_status with s and returning, instead - // of killing program? - LOG(FATAL) << s; - } else { - LOG(WARNING) << s; - } - continue; - } - - absl::MutexLock l(&mu_); - - if (opts_.bucket_adjust_every_n > 0) { - const int64 records_processed = - total_records_yielded_ + total_records_skipped_; - if (records_processed % opts_.bucket_adjust_every_n == 0 && - total_records_yielded_ > 0) { - AdjustBuckets(); - } - IncrementHistogram(bucket); - } - - // Figure out which bucket it belongs to. - auto iter = std::lower_bound(bucket_upper_bound_.begin(), - bucket_upper_bound_.end(), bucket); - - if (iter == bucket_upper_bound_.end()) { - VLOG(1) << "Skip. bucket out-of-range " << bucket; - if (out_of_range_buckets.size() < 10) { - out_of_range_buckets.push_back(bucket); - } - ++total_records_skipped_; - } else { - // Figure out which buckets we should return to the consumer. - // A bucket (id-th) is full. - const int id = iter - bucket_upper_bound_.begin(); - const int64 batch_limit = opts_.bucket_batch_limit[id]; - if (buckets_[id].size() + 1 == batch_limit) { - WaitForToFlushEmpty(); - if (stop_) { - return; - } - } - // Invariant is either we don't need to flush this bucket after adding a - // new element to it, or to_flush_ is empty and we can flush this bucket. - CHECK(buckets_[id].size() + 1 < batch_limit || to_flush_.empty()); - buckets_[id].push_back({bucket, std::move(sample)}); - if (buckets_[id].size() == batch_limit) { - to_flush_.push_back({id, std::move(buckets_[id])}); - buckets_[id].clear(); - } - CHECK_LT(buckets_[id].size(), batch_limit); // invariant. - - ++records_yielded_; - ++total_records_yielded_; - - if (opts_.flush_every_n > 0 && records_yielded_ >= opts_.flush_every_n) { - FlushAllBuckets(); - records_yielded_ = 0; - } - } - - std::time_t current_time = std::time(nullptr); - if (current_time - last_log_update_time_ > - next_status_update_duration_seconds_) { - LOG(INFO) << current_time - start_time_ - << " total seconds passed. Total records yielded: " - << total_records_yielded_ - << ". Total records skipped: " << total_records_skipped_; - for (auto bucket : out_of_range_buckets) { - LOG(INFO) << "Out-of-range sample: " << bucket; - } - CHECK_GT(total_records_yielded_, 0) - << "No records were yielded. Most likely the input is misconfigured!"; - out_of_range_buckets.clear(); - last_log_update_time_ = current_time; - next_status_update_duration_seconds_ *= status_update_duration_multiplier; - } - } -} - -void RecordBatcher::MergerLoop() { - FlushList to_flush; - std::vector samples; - TensorVec merged; - bool continue_loop = true; - while (continue_loop) { - { - absl::MutexLock l(&mu_); - WaitForToFlushNonEmpty(); - if (stop_ && stop_status_.ok()) { - // The object is being destroyed, just exit. - return; - } else if (ProcessorsDone()) { - // The yielder hit EOF and all processors are done. - // Flush all buckets, and then signal to exit the merger - // loop once all items are flushed to curr_. - FlushAllBuckets(); - continue_loop = false; - } - - to_flush = std::move(to_flush_); - to_flush_.clear(); - } - - // Now, flush out batches we accumulated. Typically, to_flush has only 1 - // batch unless flush_every_n is > 0. - for (auto& p : to_flush) { - const int64 id = p.first; - const int32 num = p.second.size(); - Tensor bucket_keys(DT_INT32, {num}); - auto t_bucket_keys = bucket_keys.flat(); - for (int i = 0; i < num; ++i) { - auto processed = p.second[i]; - t_bucket_keys(i) = processed.bucket_key; - samples.push_back(std::move(processed.sample)); - } - merged.clear(); - Status s = processor_->Merge(bucket_upper_bound_[id], samples, &merged); - samples.clear(); - if (!s.ok()) { - LOG(WARNING) << "Failed to create a batch: " << s; - } else { - merged.push_back(bucket_keys); - absl::MutexLock l(&mu_); - WaitForCurrEmpty(); - - // If stopped due to destructor, just exit, since there should be no - // further calls to GetNext(). - if (stop_ && stop_status_.ok()) { - return; - } - curr_bucket_ = id; - curr_ = std::move(merged); - } - } - to_flush.clear(); - } -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/record_batcher.h b/lingvo/core/ops/record_batcher.h deleted file mode 100644 index a8d30727..00000000 --- a/lingvo/core/ops/record_batcher.h +++ /dev/null @@ -1,205 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_RECORD_BATCHER_H_ -#define LINGVO_CORE_OPS_RECORD_BATCHER_H_ - -#include -#include - -#include "absl/synchronization/mutex.h" -#include "lingvo/core/ops/record_yielder.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/lib/core/status.h" -#include "tensorflow/core/lib/core/threadpool.h" -#include "tensorflow/core/platform/macros.h" -#include "tensorflow/core/platform/thread_annotations.h" - -namespace tensorflow { -namespace lingvo { - -// We use a vector of Tensor to represent a training example or a -// batch of merged training examples. In the latter case, by -// convention, the 1st dimension of every Tensor represents the batch -// dimension. -typedef std::vector TensorVec; - -// An interface for processing records yielded by RecordYielder into a -// training example (single one or batched). -class RecordProcessor { - public: - virtual ~RecordProcessor() {} - - // Parses 'record' (typically a protocol buffer) and fills - // in 'sample' a vector of Tensor representing a training example. - // - // 'bucket_key' is an auxilary annotation extracted from the record - // used by RecordBatcher to bucketize training examples. Typically, - // 'bucket_key' is just a length measure about the record. - virtual Status Process(const Record& record, int64* bucket_key, - TensorVec* sample) = 0; - - // Gives a list of training 'samples' returned by Process() and bucketed into - // a bucket with size `bucket_size`, merges them into a single 'batch'. - virtual Status Merge(int64 bucket_size, const std::vector& samples, - TensorVec* batch) = 0; - - // Initializes the RecordProcessor. This should be invoked only once before - // calling 'Process', since some processors expect some initialization with an - // OpKernelContext before running. - virtual Status Initialize(OpKernelContext* ctx) { return Status::OK(); } -}; - -// RecordBatcher takes a RecordYielder, batches the record yielded and -// converts them into training examples. -class RecordBatcher { - public: - // Uses 'yielder' to produce records and 'processor' to produce - // training example batches. - // - // The bucketing scheme is specified by 'bucket_upper_bound' and - // 'bucket_batch_limit'. Records with bucket_key within - // (bucket_upper_bound[i-1], bucket_upper_bound[i]] - // is put into i-th bucket and as soon as i-th bucket contains - // more than bucket_batch_limit[i] samples, RecordBatcher yields - // one training batch. - struct Options { - // REQUIRES: bucket_upper_bound.size() == bucket_batch_limit.size() - std::vector bucket_upper_bound; - std::vector bucket_batch_limit; - - // If non-zero, optimize bucket_upper_bound values (except the last one) - // every n records based on input lengths. - int64 bucket_adjust_every_n = 0; - - // If non-zero, flushes all batches buffered so far every these - // many records are yielded. - int64 flush_every_n = 0; - - // Number of threads to use for record batcher, each thread - // fills separate batches based on bucket limits. - int64 num_threads = 1; - }; - RecordBatcher(const Options& opts, RecordYielder* yielder, - RecordProcessor* processor); - - virtual ~RecordBatcher(); - - // Returns the a training batch in 'batch' and the batch comes out - // from 'bucket_id'-th bucket. - // Processor threads will be launched on the first call to this function. - Status GetNext(OpKernelContext* ctx, int64* bucket_id, TensorVec* batch); - - private: - // Initializes the batcher and launches the processor threads, if not done. - // This should be invoked on the first call of 'GetNext', since some - // processors expect some initialization with an OpKernelContext before - // running. - Status EnsureInitialized(OpKernelContext* ctx); - - typedef RecordBatcher ME; - struct Processed { - int64 bucket_key; - TensorVec sample; - }; - typedef std::vector Batch; - - // FlushList is a list of bucket id and one batch for that bucket. - typedef std::vector> FlushList; - - // Owned. - Options opts_; - RecordYielder* yielder_ = nullptr; - RecordProcessor* processor_ = nullptr; - thread::ThreadPool* processor_thread_ = nullptr; - thread::ThreadPool* merger_thread_ = nullptr; - absl::Mutex mu_; - int64 curr_bucket_ ABSL_GUARDED_BY(mu_) = -1; - TensorVec curr_ ABSL_GUARDED_BY(mu_); - - // True if either the yielder hits EOF or the destructor triggers. - bool stop_ ABSL_GUARDED_BY(mu_) = false; - - // Status is not OK when a yielder hits an EOF. - Status stop_status_ ABSL_GUARDED_BY(mu_); - - // True when the merger thread is finished. - bool merger_loop_done_ ABSL_GUARDED_BY(mu_) = false; - - bool is_initialized_ = false; - absl::Condition curr_empty_; - absl::Condition curr_non_empty_; - int64 records_yielded_ ABSL_GUARDED_BY(mu_) = 0; - int64 total_records_yielded_ ABSL_GUARDED_BY(mu_) = 0; - int64 total_records_skipped_ ABSL_GUARDED_BY(mu_) = 0; - std::vector buckets_ ABSL_GUARDED_BY(mu_); - int64 processor_loop_done_count_ ABSL_GUARDED_BY(mu_) = 0; - FlushList to_flush_ ABSL_GUARDED_BY(mu_); - absl::Condition to_flush_empty_; - absl::Condition to_flush_non_empty_; - std::time_t start_time_; // Not necessary to guard. - std::time_t last_log_update_time_ ABSL_GUARDED_BY(mu_); - int64 next_status_update_duration_seconds_ ABSL_GUARDED_BY(mu_) = 60; - - std::vector length_histogram_; - std::vector bucket_upper_bound_; - - // Conditions. - bool CurrEmpty() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - return ((stop_ && stop_status_.ok()) || // The object is being destroyed - curr_.empty()); // We can push work onto curr_. - } - - bool CurrNonEmpty() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - return (!curr_.empty() || // There is data to deliver to GetNext(). - merger_loop_done_); // There merger loop is done (no more data). - } - - bool ToFlushEmpty() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - return stop_ || to_flush_.empty(); - } - - bool ToFlushNonEmpty() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - return ((stop_ && stop_status_.ok()) || // The object is being destroyed. - !to_flush_.empty() || // There is work to flush. - ProcessorsDone()); // All processor threads have exited. - } - - bool ProcessorsDone() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - return processor_loop_done_count_ == opts_.num_threads; - } - - void ProcessorLoop(); - void MergerLoop(); - - void AdjustBuckets() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); - void FlushAllBuckets() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); - void IncrementHistogram(int64 bucket) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); - - // For performance debugging. - void WaitForCurrEmpty() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); - void WaitForCurrNonEmpty() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); - void WaitForToFlushEmpty() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); - void WaitForToFlushNonEmpty() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); - - TF_DISALLOW_COPY_AND_ASSIGN(RecordBatcher); -}; - -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_RECORD_BATCHER_H_ diff --git a/lingvo/core/ops/record_batcher_test.cc b/lingvo/core/ops/record_batcher_test.cc deleted file mode 100644 index 4620fcf6..00000000 --- a/lingvo/core/ops/record_batcher_test.cc +++ /dev/null @@ -1,316 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/record_batcher.h" - -#include -#include "lingvo/core/ops/input_common.h" -#include "lingvo/core/ops/sequential_record_yielder.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/lib/io/path.h" -#include "tensorflow/core/lib/io/record_writer.h" -#include "tensorflow/core/lib/strings/stringprintf.h" - -namespace tensorflow { -namespace lingvo { - -void GenerateTestData(const string& filename, int n, bool random_value) { - std::unique_ptr file; - TF_CHECK_OK(Env::Default()->NewWritableFile(filename, &file)); - io::RecordWriter writer(file.get()); - for (int i = 0; i < n; ++i) { - if (random_value) { - const string val(1 + (i % 100), 'x'); // Length of [1 .. 100] - TF_CHECK_OK(writer.WriteRecord(val)); - } else { - const string val = strings::Printf("%010d", i); - TF_CHECK_OK(writer.WriteRecord(val)); - } - } -} - -class TestRP : public RecordProcessor { - public: - TestRP() {} - - ~TestRP() override {} - - Status Process(const Record& record, int64* bucket_key, - TensorVec* sample) override { - const string val = string(record.value); - *bucket_key = val.size(); - Tensor t(DT_STRING, {}); - t.scalar()().append(std::string(record.value)); - Tensor ids(DT_STRING, {1}); - auto lab = ids.flat(); - lab(0) = absl::StrCat(record.source_id); - sample->clear(); - sample->push_back(std::move(t)); - sample->push_back(std::move(ids)); - return Status::OK(); - } - - Status Merge(int64 bucket_size, const std::vector& samples, - TensorVec* batch) override { - const int64 n = samples.size(); - Tensor t(DT_STRING, {n}); - Tensor source_ids(DT_STRING, {n}); - for (int i = 0; i < samples.size(); ++i) { - t.flat()(i) = samples[i][0].scalar()(); - source_ids.flat()(i) = samples[i][1].scalar()(); - } - batch->clear(); - batch->push_back(std::move(t)); - batch->push_back(std::move(source_ids)); - return Status::OK(); - } -}; - -TEST(RecordBatcher, Basic) { - const string filename = io::JoinPath("/tmp", "basic"); - GenerateTestData(filename, 1000, true /* random_value */); - - BasicRecordYielder::Options yopts; - yopts.file_pattern = strings::StrCat("tfrecord:", filename); - yopts.seed = 301; - yopts.bufsize = 10; - yopts.parallelism = 1; - - RecordBatcher::Options bopts; - bopts.bucket_upper_bound = {20, 50, 90, 95}; - bopts.bucket_batch_limit = {8, 4, 2, 1}; - - RecordBatcher batcher(bopts, BasicRecordYielder::New(yopts), new TestRP()); - int64 bucket_id; - TensorVec batch; - for (int i = 0; i < 1000; ++i) { - TF_CHECK_OK(batcher.GetNext(/*ctx=*/nullptr, &bucket_id, &batch)); - ASSERT_LE(0, bucket_id); - ASSERT_LT(bucket_id, bopts.bucket_upper_bound.size()); - const Tensor& t = batch[0]; - ASSERT_EQ(t.dims(), 1); - ASSERT_LE(t.dim_size(0), bopts.bucket_batch_limit[bucket_id]); - const Tensor& source_ids = batch[1]; - ASSERT_EQ(source_ids.dims(), 1); - ASSERT_LE(source_ids.dim_size(0), bopts.bucket_batch_limit[bucket_id]); - int maxlen = 0; - for (int j = 0; j < t.dim_size(0); ++j) { - auto len = t.vec()(j).size(); - EXPECT_LE(len, bopts.bucket_upper_bound[bucket_id]); - if (bucket_id != 0) { - EXPECT_LT(bopts.bucket_upper_bound[bucket_id - 1], len); - } - maxlen = std::max(maxlen, len); - ASSERT_EQ(source_ids.vec()(j), "0"); - } - VLOG(1) << bucket_id << " " << t.dim_size(0) << " " << maxlen; - } -} - -TEST(RecordBatcher, BasicMultiThread) { - const string filename = io::JoinPath("/tmp", "basic"); - GenerateTestData(filename, 1000, true /* random_value */); - - BasicRecordYielder::Options yopts; - yopts.file_pattern = strings::StrCat("tfrecord:", filename); - yopts.seed = 301; - yopts.bufsize = 10; - yopts.parallelism = 1; - - RecordBatcher::Options bopts; - bopts.bucket_upper_bound = {20, 50, 90, 95}; - bopts.bucket_batch_limit = {8, 4, 2, 1}; - bopts.num_threads = 4; - - RecordBatcher batcher(bopts, BasicRecordYielder::New(yopts), new TestRP()); - int64 bucket_id; - TensorVec batch; - for (int i = 0; i < 1000; ++i) { - TF_CHECK_OK(batcher.GetNext(/*ctx=*/nullptr, &bucket_id, &batch)); - ASSERT_LE(0, bucket_id); - ASSERT_LT(bucket_id, bopts.bucket_upper_bound.size()); - const Tensor& t = batch[0]; - ASSERT_EQ(t.dims(), 1); - ASSERT_LE(t.dim_size(0), bopts.bucket_batch_limit[bucket_id]); - int maxlen = 0; - for (int j = 0; j < t.dim_size(0); ++j) { - auto len = t.vec()(j).size(); - EXPECT_LE(len, bopts.bucket_upper_bound[bucket_id]); - if (bucket_id != 0) { - EXPECT_LT(bopts.bucket_upper_bound[bucket_id - 1], len); - } - maxlen = std::max(maxlen, len); - } - VLOG(1) << bucket_id << " " << t.dim_size(0) << " " << maxlen; - } -} - -TEST(RecordBatcher, LearnBuckets) { - const string filename = io::JoinPath("/tmp", "basic"); - GenerateTestData(filename, 1000, true /* random_value */); - - BasicRecordYielder::Options yopts; - yopts.file_pattern = strings::StrCat("tfrecord:", filename); - yopts.seed = 301; - yopts.bufsize = 10; - yopts.parallelism = 1; - - RecordBatcher::Options bopts; - bopts.bucket_upper_bound = {100, 100, 100, 100}; - bopts.bucket_batch_limit = {8, 8, 8, 8}; - bopts.bucket_adjust_every_n = 550; - - RecordBatcher batcher(bopts, BasicRecordYielder::New(yopts), new TestRP()); - int64 bucket_id; - TensorVec batch; - - // For the first 500 batches we just make sure the batches are the right - // size. - for (int i = 0; i < 500; ++i) { - TF_CHECK_OK(batcher.GetNext(/*ctx=*/nullptr, &bucket_id, &batch)); - ASSERT_LE(0, bucket_id); - const Tensor& t = batch[0]; - ASSERT_EQ(8, t.dim_size(0)); - } - - // For the next 1000 batches we measure the max length distribution. - std::vector maxlens; - std::vector batches; - maxlens.resize(4, 0); - batches.resize(4, 0); - for (int i = 0; i < 1000; ++i) { - TF_CHECK_OK(batcher.GetNext(/*ctx=*/nullptr, &bucket_id, &batch)); - const Tensor& t = batch[0]; - int maxlen = 0; - for (int j = 0; j < t.dim_size(0); ++j) { - int len = t.vec()(j).size(); - maxlen = std::max(maxlen, len); - } - maxlens[bucket_id] += maxlen; - batches[bucket_id]++; - } - - // The data has a uniform distribution of [1 .. 100]. So we expect - // bucket boundaries around 25, 50, 75, 100, and roughly equal numbers of - // batches of each ID. - EXPECT_NEAR(250, batches[0], 25); - EXPECT_NEAR(250, batches[1], 25); - EXPECT_NEAR(250, batches[2], 25); - EXPECT_NEAR(250, batches[3], 25); - - EXPECT_NEAR(25, maxlens[0] / batches[0], 5); - EXPECT_NEAR(50, maxlens[1] / batches[1], 5); - EXPECT_NEAR(75, maxlens[2] / batches[2], 5); - EXPECT_NEAR(100, maxlens[3] / batches[3], 5); -} - -TEST(RecordBatcher, FullEpoch) { - const int N = 1000; - const string filename = - io::JoinPath("/tmp", "full_epoch"); - GenerateTestData(filename, N, false /* random_value */); - - BasicRecordYielder::Options yopts; - yopts.file_pattern = strings::StrCat("tfrecord:", filename); - yopts.seed = 301; - yopts.bufsize = 10; - yopts.parallelism = 1; - - RecordBatcher::Options bopts; - bopts.bucket_upper_bound = {20, 50, 90, 120}; - bopts.bucket_batch_limit = {8, 4, 2, 1}; - bopts.flush_every_n = N; // Same number of records in the data file. - - RecordBatcher batcher(bopts, BasicRecordYielder::New(yopts), new TestRP()); - int64 bucket_id; - TensorVec batch; - std::vector records; - while (records.size() < N) { - TF_CHECK_OK(batcher.GetNext(/*ctx=*/nullptr, &bucket_id, &batch)); - const Tensor& t = batch[0]; - for (int j = 0; j < t.dim_size(0); ++j) { - records.push_back(t.vec()(j)); - } - } - ASSERT_EQ(N, records.size()); - // We expect to see exactly non-duplicated N records. - std::sort(records.begin(), records.end()); - for (int i = 0; i < N; ++i) { - EXPECT_EQ(strings::Printf("%010d", i), records[i]); - } -} - -TEST(RecordBatcher, CaptureYielderStatus) { - const int N = 50; - const string filename = io::JoinPath("/tmp", "full_epoch"); - GenerateTestData(filename, N, false /* random_value */); - - RecordBatcher::Options bopts; - bopts.bucket_upper_bound = {1000000000}; - bopts.bucket_batch_limit = {1}; - bopts.num_threads = 1; - - const string file_pattern = strings::StrCat("tfrecord:", filename); - const int num_epochs = 2; - RecordBatcher batcher(bopts, - SequentialRecordYielder::New(file_pattern, num_epochs), - new TestRP()); - int64 bucket_id; - TensorVec batch; - std::vector records; - // Fetch N * num_epochs worth of data, which should all be there. - // Note that when there are multiple streams, we need repeat it - // (batch.size() - 1) times. - while (records.size() < num_epochs * N * (batch.size() - 1)) { - TF_CHECK_OK(batcher.GetNext(/*ctx=*/nullptr, &bucket_id, &batch)); - for (int i = 0; i < batch.size(); ++i) { - const Tensor& t = batch[i]; - if (t.dtype() == DT_STRING) { - for (int j = 0; j < t.dim_size(0); ++j) { - records.push_back(t.vec()(j)); - } - } - } - } - - // With a sequential record yielder, the next call will exhaust the - // repeat count of the iterator, and so we should expect that no more - // data can be yielded. - Status s = batcher.GetNext(/*ctx=*/nullptr, &bucket_id, &batch); - ASSERT_TRUE(errors::IsOutOfRange(s)); -} - -TEST(RecordBatcher, SequentialEoFImmediately) { - const string filename = io::JoinPath("/tmp", "full_epoch"); - // Generate no data. - GenerateTestData(filename, 0, false /* random_value */); - - RecordBatcher::Options bopts; - bopts.bucket_upper_bound = {1000000000}; - bopts.bucket_batch_limit = {1}; - bopts.num_threads = 1; - const string file_pattern = strings::StrCat("tfrecord:", filename); - const int num_epochs = 1; - RecordBatcher batcher(bopts, - SequentialRecordYielder::New(file_pattern, num_epochs), - new TestRP()); - int64 bucket_id; - TensorVec batch; - Status s = batcher.GetNext(/*ctx=*/nullptr, &bucket_id, &batch); - ASSERT_TRUE(errors::IsOutOfRange(s)); -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/record_debug.cc b/lingvo/core/ops/record_debug.cc deleted file mode 100644 index e42592ad..00000000 --- a/lingvo/core/ops/record_debug.cc +++ /dev/null @@ -1,76 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/record_batcher.h" -#include "lingvo/core/ops/record_yielder.h" -#include "tensorflow/core/platform/env.h" - -namespace tensorflow { -namespace lingvo { - -void BasicRecordYielder::WaitForBufEnough() { - if (!BufEnough()) { - auto start = Env::Default()->NowMicros(); - mu_.Await(buf_enough_); - VLOG(1) << "Wait for buf containing enough records: " - << (Env::Default()->NowMicros() - start) * 1e-6 - << " Hint: Check network condition (e.g., are files in the same " - << "data center) and/or increase file_parallelism."; - } -} - -void RecordBatcher::WaitForCurrEmpty() { - if (!CurrEmpty()) { - auto start = Env::Default()->NowMicros(); - mu_.Await(curr_empty_); - VLOG(2) - << "Wait for curr empty: " - << (Env::Default()->NowMicros() - start) * 1e-6 - << " Hint: Processing is not fast enough to consume example batches."; - } -} - -void RecordBatcher::WaitForCurrNonEmpty() { - if (!CurrNonEmpty()) { - auto start = Env::Default()->NowMicros(); - mu_.Await(curr_non_empty_); - VLOG(1) << "Wait for curr non empty: " - << (Env::Default()->NowMicros() - start) * 1e-6 - << " Hint: Consider improving Merge() method."; - } -} - -void RecordBatcher::WaitForToFlushEmpty() { - if (!ToFlushEmpty()) { - auto start = Env::Default()->NowMicros(); - mu_.Await(to_flush_empty_); - VLOG(3) << "Wait for to_flush empty: " - << (Env::Default()->NowMicros() - start) * 1e-6 - << " Hint: Expected to be the common case."; - } -} - -void RecordBatcher::WaitForToFlushNonEmpty() { - if (!ToFlushNonEmpty()) { - auto start = Env::Default()->NowMicros(); - mu_.Await(to_flush_non_empty_); - VLOG(1) << "Wait for to_flush non empty: " - << (Env::Default()->NowMicros() - start) * 1e-6 - << " Hint: Increase num_batcher_thread."; - } -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/record_yielder.cc b/lingvo/core/ops/record_yielder.cc deleted file mode 100644 index e30e1803..00000000 --- a/lingvo/core/ops/record_yielder.cc +++ /dev/null @@ -1,584 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/record_yielder.h" - -#include // NOLINT(build/c++11) -#include -#include -#include // NOLINT(build/c++11) -#include - -#include "absl/synchronization/mutex.h" -#include "lingvo/core/ops/versioned_file_set.pb.h" -#include "tensorflow/core/lib/core/status.h" -#include "tensorflow/core/lib/hash/hash.h" -#include "tensorflow/core/lib/io/buffered_inputstream.h" -#include "tensorflow/core/lib/io/compression.h" -#include "tensorflow/core/lib/io/path.h" -#include "tensorflow/core/lib/io/random_inputstream.h" -#include "tensorflow/core/lib/io/record_reader.h" -#include "tensorflow/core/lib/strings/numbers.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/lib/strings/strcat.h" -#include "tensorflow/core/lib/strings/stringprintf.h" -#include "tensorflow/core/platform/env.h" - -namespace tensorflow { -namespace lingvo { - -namespace { - -// Number of records to batch for a single call to Add. -const int kRecordsPerAdd = 16; - -struct Factory { - absl::Mutex mu; - std::unordered_map creators; - std::unordered_map - pattern_parsers; -}; - -Factory* GetFactory() { - static Factory* factory = new Factory; - return factory; -} - -// A sharded file pattern looks like /path/name@100, which is expanded to -// a glob pattern /path/name-?????-of-00100 by this function. The number of -// shards shouldn't exceed 5 digits. A wildcard is also support for the shards -// number: /path/name@* is expanded to /path/name-?????-of-?????. -Status MaybeExpandShardedFilePattern(const string& file_pattern, - string* expanded) { - const auto pos = file_pattern.find('@'); - if (pos == string::npos) { // not a sharded file pattern - *expanded = file_pattern; - return Status::OK(); - } - - const string prefix = file_pattern.substr(0, pos); - const string suffix = file_pattern.substr(pos + 1); - - uint32 num_shards = 0; - if (suffix == "*") { - *expanded = strings::StrCat(prefix, "-\?\?\?\?\?-of-\?\?\?\?\?"); - return Status::OK(); - } else if (strings::safe_strtou32(suffix, &num_shards)) { - if (num_shards > 99999) { - return errors::InvalidArgument(strings::StrCat( - "The number of shards should not exceed 5 digits: ", num_shards)); - } - *expanded = - strings::Printf("%s-\?\?\?\?\?-of-%05d", prefix.c_str(), num_shards); - return Status::OK(); - } else { - return errors::InvalidArgument( - strings::StrCat("Invalid sharded file pattern: ", file_pattern)); - } -} - -// ParallelFilePatterns look like this -// /a-*-of-10;/b-*-of-10,/c-*-of-10;/d-*-of-10 -// Each "," separated pattern is a parallel file pattern. -// In a given parallel file pattern, all the ";" separated file patterns -// should have the same number of shards. This method creates ";" separated -// filenames aligning the shards. e,g. /a-01-of-10;/b-01-of-10 -// /a-02-of-10;/b-02-of-10 -// /c-01-of-10;/d-01-of-10 -// /c-02-of-10;/d-02-of-10 -Status MatchParallelFilePattern(const string& parallel_file_pattern, - std::vector* filenames) { - std::vector parallel_filenames; - for (const auto& file_pattern : str_util::Split(parallel_file_pattern, ';')) { - string expanded_file_pattern; - TF_RETURN_IF_ERROR( - MaybeExpandShardedFilePattern(file_pattern, &expanded_file_pattern)); - std::vector filenames_per_pattern; - TF_RETURN_IF_ERROR(Env::Default()->GetMatchingPaths( - expanded_file_pattern, &filenames_per_pattern)); - if (parallel_filenames.empty()) { - parallel_filenames.swap(filenames_per_pattern); - continue; - } - if (parallel_filenames.size() != filenames_per_pattern.size()) { - return Status(tensorflow::error::INVALID_ARGUMENT, - "All file patterns in the parallel file pattern do not " - "have the same number of elements."); - } - for (auto it1 = parallel_filenames.begin(), - it2 = filenames_per_pattern.begin(); - it1 != parallel_filenames.end(); ++it1, ++it2) { - strings::StrAppend(&(*it1), ";", *it2); - } - } - filenames->insert(filenames->end(), - std::make_move_iterator(parallel_filenames.begin()), - std::make_move_iterator(parallel_filenames.end())); - - return Status::OK(); -} - -// Input to CkptFilePattern should look like /ckpt -// This pattern parser will read the ckpt file, decode it into a -// tensorflow::CheckpointState proto and use the latest model_checkpoint_path -// as the actual file-pattern. -// model_checkpoint_path should be a legitimate file-pattern, for eg. -// /a@* or /a or /a-?????-of-????? etc. -Status GetFilePatternsFromCkptFile(const string& fileset_file, - std::vector* filenames) { - string fileset_str; - VersionedFileSet fileset; - TF_RETURN_IF_ERROR( - ReadFileToString(Env::Default(), fileset_file, &fileset_str)); - - CHECK(google::protobuf::TextFormat::ParseFromString(fileset_str, &fileset)); - - string prefix = std::string(io::Dirname(fileset_file)); - for (const auto& file_pattern : fileset.current().file_pattern()) { - TF_RETURN_IF_ERROR(MatchParallelFilePattern( - io::JoinPath(prefix, file_pattern), filenames)); - } - return Status::OK(); -} - -} // end namespace - -bool RecordIterator::Register(const string& type_name, FactoryMethod method) { - return RegisterWithPatternParser(type_name, std::move(method), - RecordIterator::PatternParserMethod()); -} - -bool RecordIterator::RegisterWithPatternParser( - const string& type_name, FactoryMethod method, - PatternParserMethod parser_method) { - Factory* factory = GetFactory(); - absl::MutexLock l(&factory->mu); - bool ret = factory->creators.insert({type_name, std::move(method)}).second; - CHECK(ret) << "Possibly duplicated registration: " << type_name; - if (parser_method) { - factory->pattern_parsers.insert({type_name, std::move(parser_method)}); - } - return ret; -} - -RecordIterator* RecordIterator::New(const string& type_name, - const string& filename) { - Factory* factory = GetFactory(); - RecordIterator::FactoryMethod method; - { - absl::MutexLock l(&factory->mu); - const auto iter = factory->creators.find(type_name); - CHECK(iter != factory->creators.end()) - << "Unable to create RecordIterator for format \"" << type_name << "\""; - method = iter->second; - } - return method(filename); -} - -// Returns the prefix in a file pattern, or an empty string if not exist. -// Example: "tfrecord:data_dir/data.tfrecord" => "tfrecord" -string RecordIterator::GetFilePatternPrefix(const string& file_pattern) { - const auto prefix_end = file_pattern.find(':'); - if (prefix_end == string::npos) { - return ""; - } - // The prefix should not contain '/'. If so, it's likely part of the path. - const auto first_slash = file_pattern.find('/'); - if (first_slash != string::npos && first_slash < prefix_end) { - return ""; - } - return file_pattern.substr(0, prefix_end); -} - -string RecordIterator::StripPrefixFromFilePattern(string* file_pattern) { - const string prefix = RecordIterator::GetFilePatternPrefix(*file_pattern); - if (!prefix.empty()) file_pattern->erase(0, prefix.size() + 1); - return prefix; -} - -Status RecordIterator::ParsePattern(const string& type_name, - const string& file_pattern_list, - std::vector* filenames) { - Factory* factory = GetFactory(); - RecordIterator::PatternParserMethod parser_method; - { - absl::MutexLock l(&factory->mu); - const auto iter = factory->pattern_parsers.find(type_name); - if (iter != factory->pattern_parsers.end()) { - parser_method = iter->second; - } - } - if (parser_method) { - return parser_method(file_pattern_list, filenames); - } - for (const auto& file_pattern : str_util::Split(file_pattern_list, ',')) { - std::vector files_per_glob; - TF_RETURN_IF_ERROR( - MatchParallelFilePattern(file_pattern, &files_per_glob)); - filenames->insert(filenames->end(), files_per_glob.begin(), - files_per_glob.end()); - } - return Status::OK(); -} - -RandomAccessFile* OpenOrDie(const string& filename) { - std::unique_ptr file; - TF_CHECK_OK(Env::Default()->NewRandomAccessFile(filename, &file)); - return file.release(); -} - -class PlainTextIterator : public RecordIterator { - public: - PlainTextIterator(const string& filename) - : file_(OpenOrDie(filename)), - stream_(file_.get()), - buf_(&stream_, 2 << 20) {} - - bool Next(string* key, Rope* value) override { - Status s = buf_.ReadLine(&line_); - if (errors::IsOutOfRange(s)) return false; - TF_CHECK_OK(s); - *key = strings::Printf("%08lld", static_cast(num_++)); - *value = line_; - return true; - } - - private: - std::unique_ptr file_; - io::RandomAccessInputStream stream_; - io::BufferedInputStream buf_; - int64 num_ = 0; - string line_; -}; - -class TFRecordIterator : public RecordIterator { - public: - TFRecordIterator(const string& filename, const string& compression_type) - : file_(OpenOrDie(filename)), - reader_(file_.get(), ReaderOptions(compression_type)) {} - - bool Next(string* key, Rope* value) override { - Status s = reader_.ReadRecord(&record_); - if (errors::IsOutOfRange(s)) return false; - *key = strings::Printf("%08lld", static_cast(num_++)); - *value = Rope(record_); - return true; - } - - private: - std::unique_ptr file_; - io::SequentialRecordReader reader_; - int64 num_ = 0; - tstring record_; - - io::RecordReaderOptions ReaderOptions(const string& compression_type) { - auto opts = - io::RecordReaderOptions::CreateRecordReaderOptions(compression_type); - opts.buffer_size = 2LL << 20; // 2MB. - return opts; - } -}; - -// An iterator generates [0 .. max_). -class IotaIterator : public RecordIterator { - public: - IotaIterator(const string& filename) { - if (filename.empty() || !strings::safe_strto64(filename, &max_)) { - max_ = kint64max; - } - } - - bool Next(string* key, Rope* value) override { - if (num_ >= max_) { - return false; - } - *key = strings::Printf("%010lld", static_cast(num_++)); - *value = *key; - return true; - } - - private: - int64 max_ = kint64max; - int64 num_ = 0; -}; - -namespace { - -bool register_text_iterator = RecordIterator::Register( - "text", - [](const string& filename) { return new PlainTextIterator(filename); }); - -// Iterator for plain text files that checks indirect "ckpt" file for latest -// pointer to data files. -// For example, if filename is .../checkpoint, the pattern parser will open the -// checkpoint file and read file-patterns that it points to. If empty, it will -// return error and die. -bool register_indirect_text_iterator = - RecordIterator::RegisterWithPatternParser( - "text_indirect", - [](const string& filename) { return new PlainTextIterator(filename); }, - [](const string& pattern, std::vector* outs) { - TF_RETURN_IF_ERROR(GetFilePatternsFromCkptFile(pattern, outs)); - return Status::OK(); - }); - -bool register_tf_record_iterator = - RecordIterator::Register("tfrecord", [](const string& filename) { - return new TFRecordIterator(filename, io::compression::kNone); - }); - -bool register_tf_record_gzip_iterator = - RecordIterator::Register("tfrecord_gzip", [](const string& filename) { - return new TFRecordIterator(filename, io::compression::kGzip); - }); - -bool register_iota_iterator = RecordIterator::RegisterWithPatternParser( - "iota", [](const string& filename) { return new IotaIterator(filename); }, - [](const string& pattern, std::vector* outs) { - // The pattern is just a stringified number. - *outs = {pattern}; - return Status::OK(); - }); - -} // namespace - -RecordYielder::~RecordYielder() {} - -BasicRecordYielder* BasicRecordYielder::New(Options opts) { - auto yielder = new BasicRecordYielder(opts); - yielder->Start(); - return yielder; -} - -BasicRecordYielder::BasicRecordYielder(const Options& opts) - : opts_(opts), - thread_(new thread::ThreadPool(Env::Default(), ThreadOptions(), - "record_yielder", 2 + opts.parallelism, - /* low_latency_hint */ false)), - epoch_(1), - rnd_(opts.seed), - yields_(0), - buf_empty_(this, &ME::BufEmpty), - buf_not_full_(this, &ME::BufNotFull), - buf_enough_(this, &ME::BufEnough) { - LOG(INFO) << this << " Record yielder start"; - if (opts_.seed == 0) { - LOG(INFO) << "Randomly seed RecordYielder."; - rnd_.seed(std::random_device{}()); - } - file_type_ = RecordIterator::StripPrefixFromFilePattern(&opts_.file_pattern); - if (opts_.bufsize_in_seconds > 0) { - bufsize_ = kRecordsPerAdd * opts_.parallelism; - } else { - bufsize_ = opts_.bufsize; - } -} - -BasicRecordYielder::BasicRecordYielder() - : buf_empty_(this, &ME::BufEmpty), - buf_not_full_(this, &ME::BufNotFull), - buf_enough_(this, &ME::BufEnough) {} // USED ONLY FOR TESTS. -BasicRecordYielder::~BasicRecordYielder() {} - -void BasicRecordYielder::Start() { - thread_->Schedule([this]() { MainLoop(); }); -} - -void BasicRecordYielder::Close() { - { - absl::MutexLock l(&mu_); - stop_ = true; - } - main_loop_done_.WaitForNotification(); - delete thread_; - thread_ = nullptr; - LOG(INFO) << this << "Basic record yielder exit"; - delete this; -} - -Status BasicRecordYielder::Yield(Record* record) { - absl::MutexLock l(&mu_); - WaitForBufEnough(); - ++yields_; - - if (status_.ok()) { - CHECK(!stop_ && !buf_.empty()); - ExtractValue(&record->value); - if (epoch_end_ && buf_.empty()) { - ++epoch_; - } - record->source_id = static_cast(opts_.source_id); - ++num_records_yielded_in_epoch_; - } - return status_; -} - -bool BasicRecordYielder::ShouldFinish(const Status& s) { - absl::MutexLock l(&mu_); - status_.Update(s); - return stop_ || !status_.ok(); -} - -void BasicRecordYielder::AdjustBufferSizeLoop() { - if (opts_.bufsize_in_seconds == 0) { - // Nothing to do for a fixed buffer size. - return; - } - - // Wake up each second and adjust the buffer size. - while (true) { - { - absl::MutexLock l(&mu_); - - // Quit if requested. - if (stop_) break; - - // Smoothed bufsize_ estimate based on current yields_ requests. - // With this set of parameters, the contribution of the current buffer - // size decays as follows: - // 10 seconds -> 90% (.99^10) - // 1 minute -> 50% (.99^60) - // 5 minutes -> 5% (.99^300) - bufsize_ = 0.99 * static_cast(bufsize_) + - 0.01 * yields_ * opts_.bufsize_in_seconds; - - // Make sure the buffer is large enough to hold one batch of Add records - // per thread. - bufsize_ = std::max(opts_.parallelism * kRecordsPerAdd, bufsize_); - - // Make sure the buffer is not larger than the bufsize parameter in the - // options. - if (opts_.bufsize > 0) { - bufsize_ = std::min(opts_.bufsize, bufsize_); - } - VLOG(1) << "Yields:" << yields_ << " Bufsize:" << bufsize_ - << " Pattern:" << opts_.file_pattern; - - // Reset yields_ to zero to count another second of requests. - yields_ = 0; - } - std::this_thread::sleep_for(std::chrono::seconds(1)); - } -} - -void BasicRecordYielder::MainLoop() { - absl::Notification adjust_done; - thread_->Schedule([this, &adjust_done]() { - AdjustBufferSizeLoop(); - adjust_done.Notify(); - }); - - while (true) { - num_records_yielded_in_epoch_ = 0; - LOG(INFO) << "Epoch " << current_epoch() << " " << opts_.file_pattern; - - // Finds all files. - std::vector filenames; - Status s = RecordIterator::ParsePattern(file_type_, opts_.file_pattern, - &filenames); - if (ShouldFinish(s)) break; - - if (filenames.empty()) { - LOG(FATAL) << "Found no files at " << opts_.file_pattern; - } - - int shuffle_seed = opts_.seed; - if (opts_.seed == 0) { - absl::MutexLock l(&mu_); - shuffle_seed = rnd_(); - } - - // Shuffles these files according to the epoch # and random seed. - std::mt19937_64 shuffle_rnd(Hash64Combine(current_epoch(), shuffle_seed)); - std::shuffle(filenames.begin(), filenames.end(), shuffle_rnd); - - // Shards files and use one thread to go through each shard. - const int N = opts_.parallelism; - std::vector shards(N); - for (int i = 0; i < N; ++i) { - Shard* shard = &shards[i]; - shard->index = i; - for (int j = i; j < filenames.size(); j += N) { - shard->filenames.push_back(filenames[j]); - } - thread_->Schedule([this, shard]() { ShardLoop(shard); }); - } - for (int i = 0; i < N; ++i) { - shards[i].done.WaitForNotification(); - s.Update(shards[i].status); - } - if (ShouldFinish(s)) break; - - // Do not start the next epoch until all buffered records are consumed. - { - absl::MutexLock l(&mu_); - epoch_end_ = true; - mu_.Await(buf_empty_); - epoch_end_ = false; - } - - LOG(INFO) << "Epoch " << current_epoch() << ": total records " - << num_records_yielded_in_epoch_; - } - - adjust_done.WaitForNotification(); - main_loop_done_.Notify(); -} - -bool BasicRecordYielder::Add(std::vector* values) { - absl::MutexLock l(&mu_); - mu_.Await(buf_not_full_); - while (BufNotFull() && !values->empty()) { - // Adds values->back(). Swaps its position with another random - // element. - auto index = rnd_() % (buf_.size() + 1); - if (index == buf_.size()) { - buf_.push_back(std::move(values->back())); - } else { - buf_.push_back(std::move(buf_[index])); - buf_[index] = std::move(values->back()); - } - values->pop_back(); - } - return stop_; -} - - -void BasicRecordYielder::ShardLoop(Shard* shard) { - std::vector values; - for (const string& filename : shard->filenames) { - if (ShouldFinish(Status::OK())) break; - VLOG(1) << "Shard " << shard->index << " " << filename; - std::unique_ptr iter( - RecordIterator::New(file_type_, filename)); - string key; - Rope val; - while (iter->Next(&key, &val)) { - values.emplace_back(val); - if (values.size() >= kRecordsPerAdd && Add(&values)) { - shard->status = errors::Aborted("stopped"); - break; - } - } - } - // Adds the remaining values of this shard to buf_. - while (!values.empty()) { - Add(&values); - } - shard->done.Notify(); -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/record_yielder.h b/lingvo/core/ops/record_yielder.h deleted file mode 100644 index 06e120f9..00000000 --- a/lingvo/core/ops/record_yielder.h +++ /dev/null @@ -1,311 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_RECORD_YIELDER_H_ -#define LINGVO_CORE_OPS_RECORD_YIELDER_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "absl/synchronization/mutex.h" -#include "absl/synchronization/notification.h" -#include "lingvo/core/ops/rope.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/lib/core/status.h" -#include "tensorflow/core/lib/core/threadpool.h" -#include "tensorflow/core/platform/macros.h" -#include "tensorflow/core/platform/thread_annotations.h" - -namespace tensorflow { -namespace lingvo { - -// TODO(oday): Separate some constants and Record to other file. -// TODO(oday): Change "source_id" to another appropriate name, because this is -// one of the typically used symbols in some tasks (e.g., NMTExample has such -// field for source tokens). - -constexpr int kDefaultSourceId = 0; - -// A data structure representing a record with its context information. -// TODO(oday): The typename may sound too simple to represent itself. Consider -// to revise it if it may become really problematic. -struct Record { - // Byte sequence representing the record value. - Rope value; - - // ID of the source of the record, typically an index of the given file - // pattern list. This field is used to determine where the record comes from, - // and is useful to specify the source when reading from multiple input - // sources. - int source_id; -}; - -// An interface to iterate sequentially a set of record (Rope). -class RecordIterator { - public: - virtual ~RecordIterator() {} - - // Get the next record. If EOF, returns false. Otherwise returns true and - // fills in 'key' and 'value'. - virtual bool Next(string* key, Rope* value) = 0; - - // Resets this iterator, if possible. Sub-classes should return true on a - // successful reset. If 'randomize_location' is true, then the iterator is - // moved to point to a random record (if supported). - virtual bool ResetIterator(bool randomize_location) { - return false; - } - - // Register a method to create a RecordIterator for the 'type_name'. - typedef std::function FactoryMethod; - static bool Register(const string& type_name, FactoryMethod method); - - // As above, but also register a custom method for parsing file_pattern - // strings into lists of shards. - typedef std::function*)> - PatternParserMethod; - static bool RegisterWithPatternParser(const string& type_name, - FactoryMethod method, - PatternParserMethod parser_method); - - // Returns a record iterator for 'filename' of 'type_name'. - static RecordIterator* New(const string& type_name, const string& filename); - - // Returns the prefix in a file pattern, or an empty string if not exist. - // Example: "tfrecord:data_dir/data.tfrecord" => "tfrecord" - static string GetFilePatternPrefix(const string& file_pattern); - - // Similar to the function above, but in addition to returning the prefix also - // modifies the file_pattern by stripping away the prefix portion. - static string StripPrefixFromFilePattern(string* file_pattern); - - // Parse a file pattern into a list of matching files. - static Status ParsePattern(const string& type_name, - const string& file_pattern_list, - std::vector* filenames); -}; - -// RecordYielder defines an interface that should be used for producing value -// records from files in a random order. Most users should use -// BasicRecordYielder and BasicRecordYielder::New (see example below). -// -// RecordYielder guarantees that the order in which records are yielded are -// highly randomized. -// -// Usage example: -// BasicRecordYielder::Options opts; -// opts.file_pattern = ; -// opts.seed = 301; -// opts.bufsize = 1000000; // A randomized buffer with 1M records. -// opts.parallelism = 8; // Use 8 iterators to iterate through all files. -// RecordYielder* yielder = BasicRecordYielder::New(opts); -// Record record; -// while (true) { -// yielder->Yield(&record); -// // process record. -// } -// yielder->Close(); -// -// RecordYielder can be accessed by multiple threads concurrently. -class RecordYielder { - public: - virtual ~RecordYielder(); - - // Yields one Record. - // To read from multiple input sources and keep track of the source id, create - // a WeightedMixRecordYielder and create a BasicRecordYielder for each - // source. Each BasicRecordYielder can assign some fields in 'record' to - // indicate some characteristics of the data source. - virtual Status Yield(Record* record) = 0; - - // Stop this yielder and then delete it. - virtual void Close() = 0; -}; - -// BasicRecordYielder is a RecordYielder that implements a main loop and makes -// it possible to write a custom RecordYielder by only defining a shard loop. -// Most of the record yielders should inherit from this class. -// -// BasicRecordYielder guarantees that: -// 1) all records are yielded within every epoch; -// 2) each record is yielded only once within every epoch; -// 3) the order in which records are yielded are highly randomized. -// 4) the peak memory usage is roughly avg record size * -// (opts.bufsize + opts.parellelism * 16). -class BasicRecordYielder : public RecordYielder { - public: - struct Options { - // The set of files to yield records from. file_pattern follows: - // [:], where must have an - // associated factory method through registration via New(). - // - // TODO(zhifengc): Document it better the current support format. - string file_pattern; - - // Random seed. It determines how data files are shuffled. - int64 seed = 0; - - // Randomization buffer keeps these many records. - int64 bufsize = 1; - - // If non-zero, attempt to keep this many seconds of records in the - // randomization buffer. The buffer size will never exceed bufsize. - int64 bufsize_in_seconds = 0; - - // Uses this many concurrent iterators to iterate through files. - int32 parallelism = 1; - - // Source id to be supplied with yield. - int32 source_id = 0; - }; - - // Returns a record yielder according to 'opts'. A caller is responsible for - // calling Close when this yielder is no longer required. A caller shouldn't - // delete the yielder. - static BasicRecordYielder* New(Options opts); - - // Yields one 'record' from which the value was read. - Status Yield(Record* record) override; - - // Stop this yielder and then delete it. - void Close() override; - - // Returns the current epoch number. Epoch number starts from 1 and reflects - // the epoch number of the record returned by the next Yield() call. - virtual int64 current_epoch() const { - // TODO(tilarids): Use ReaderMutexLock here. - absl::MutexLock l(&mu_); - return epoch_; - } - - // Returns the current buffer size. - int64 bufsize() const { - absl::MutexLock l(&mu_); - return bufsize_; - } - - protected: - explicit BasicRecordYielder(const Options& opts); - explicit BasicRecordYielder(); // USED ONLY FOR TESTS. - - ~BasicRecordYielder() override; - - // Subclass should implement ShardLoop which processes all records - // in the 'shard'. - struct Shard { - int index; // Shard index. - std::vector filenames; // File names given to this shard. - absl::Notification done; // Notified when this shard is done. - Status status; // Shard status. - }; - void ShardLoop(Shard* shard); - - // Returns true iff 's' indicates the yielder should stop. - bool ShouldFinish(const Status& s); - - // Adds 'values' into the random shuffling buffer buf_. - bool Add(std::vector* values); - - private: - typedef BasicRecordYielder ME; - - Options opts_; - string file_type_; - - // Background threads. Owned. - thread::ThreadPool* thread_; - - mutable absl::Mutex mu_; - - // Epoch number. - int64 epoch_ ABSL_GUARDED_BY(mu_); - - // Turned to true when the yielder is deleted. - bool stop_ ABSL_GUARDED_BY(mu_) = false; - Status status_ ABSL_GUARDED_BY(mu_); - - // PRG used for randomization. - std::mt19937_64 rnd_ ABSL_GUARDED_BY(mu_); - - // Randomization buffer. - std::vector buf_ ABSL_GUARDED_BY(mu_); - - // True iff we are draining an epoch. - bool epoch_end_ ABSL_GUARDED_BY(mu_) = false; - - int64 num_records_yielded_in_epoch_ = 0; - - // Dynamically adjusted buffer size. - double bufsize_ ABSL_GUARDED_BY(mu_); - - // Number of Yield calls in the current adjustment interval. - int64 yields_ ABSL_GUARDED_BY(mu_); - - // Trigger when the main loop has exited. - absl::Notification main_loop_done_; - - // Conditions. - absl::Condition buf_empty_; - bool BufEmpty() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - return stop_ || buf_.empty(); - } - - absl::Condition buf_not_full_; - bool BufNotFull() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - return stop_ || static_cast(buf_.size()) < bufsize_; - } - - absl::Condition buf_enough_; - bool BufEnough() const ABSL_SHARED_LOCKS_REQUIRED(mu_) { - // NOTE: Unless we are finishing an epoch, we want to make sure - // the buf_ contains enough randomized elements before yielding any. - return stop_ || !status_.ok() || (epoch_end_ && !buf_.empty()) || - (!epoch_end_ && static_cast(buf_.size()) >= - std::max(1, bufsize_ / 2)); - } - - void ExtractValue(Rope* value) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) { - if (opts_.seed == 0) { - // Randomize at the consumer side as well. - const auto index = rnd_() % buf_.size(); - *value = std::move(buf_[index]); - if (index != buf_.size() - 1) { - buf_[index] = std::move(buf_.back()); - } - } else { - *value = std::move(buf_.back()); - } - buf_.pop_back(); - } - - void Start(); - void MainLoop(); - void AdjustBufferSizeLoop(); - - // For performance debugging. - void WaitForBufEnough() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); - - TF_DISALLOW_COPY_AND_ASSIGN(BasicRecordYielder); -}; - -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_RECORD_YIELDER_H_ diff --git a/lingvo/core/ops/record_yielder_test.cc b/lingvo/core/ops/record_yielder_test.cc deleted file mode 100644 index b258f44a..00000000 --- a/lingvo/core/ops/record_yielder_test.cc +++ /dev/null @@ -1,538 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/record_yielder.h" - -#include // NOLINT(build/c++11) - -#include -#include "lingvo/core/ops/input_common.h" -#include "lingvo/core/ops/sequential_record_yielder.h" -#include "lingvo/core/ops/yielder_test_helper.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/lib/core/stringpiece.h" -#include "tensorflow/core/lib/io/compression.h" -#include "tensorflow/core/lib/io/path.h" -#include "tensorflow/core/lib/io/record_writer.h" -#include "tensorflow/core/lib/strings/strcat.h" -#include "tensorflow/core/lib/strings/stringprintf.h" -#include "tensorflow/core/platform/env.h" - -namespace tensorflow { -namespace lingvo { - -TEST(RecordYielderTest, PlainTextYielderBasicTest) { - const int N = 10; - const int M = 1000; - GeneratePlainTextTestData("basic", N, M); - BasicRecordYielder::Options opts; - opts.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "basic.*")); - opts.seed = 301; - opts.bufsize = 2000; - opts.parallelism = 1; - - BasicRecordYielder* yielder = BasicRecordYielder::New(opts); - std::vector vals; - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - } - std::sort(vals.begin(), vals.end()); - auto new_end = std::unique(vals.begin(), vals.end()); - - // One epoch should have no duplicates. - ASSERT_EQ(new_end, vals.end()); - - // Iterates another two epochs. - for (int i = 0; i < 2 * N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - } - - // Iterates another 34 epochs. - for (int i = 0; i < 2 * 17 * N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - } - - // End of the 37th epoch | start of the 38th epoch. - EXPECT_TRUE(yielder->current_epoch() == 37 || yielder->current_epoch() == 38); - yielder->Close(); -} - -TEST(SequentialRecordYielderTest, SequentialRecordYielderBasicTest) { - const int N = 10; - const int M = 1000; - GeneratePlainTextTestData("basic", N, M); - const string& file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "basic.*")); - - SequentialRecordYielder* yielder = - SequentialRecordYielder::New(file_pattern, -1); - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - ASSERT_EQ(string(record.value), strings::Printf("basic:%010d", i)); - } - - // Iterate another epoch. - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - ASSERT_EQ(string(record.value), strings::Printf("basic:%010d", i)); - } - - yielder->Close(); -} - -TEST(SequentialRecordYielderTest, SequentialRecordYielderRepeatCount) { - const int N = 10; - const int M = 1000; - GeneratePlainTextTestData("basic", N, M); - const string& file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "basic.*")); - - // Yield two epochs. - SequentialRecordYielder* yielder = - SequentialRecordYielder::New(file_pattern, 2); - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - ASSERT_EQ(string(record.value), strings::Printf("basic:%010d", i)); - } - - // Iterate another epoch. - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - ASSERT_EQ(string(record.value), strings::Printf("basic:%010d", i)); - } - - // Trying to yield one more element should throw an out of range error. - Status s = yielder->Yield(&record); - ASSERT_TRUE(errors::IsOutOfRange(s)) << s; - - yielder->Close(); -} - -void GenerateTfRecordTestData(const string& prefix, int n, int m, - const string& compression_type) { - for (int i = 0; i < n; ++i) { - std::unique_ptr file; - TF_CHECK_OK(Env::Default()->NewWritableFile( - io::JoinPath("/tmp", strings::StrCat(prefix, ".", i)), - &file)); - io::RecordWriter writer( - file.get(), - io::RecordWriterOptions::CreateRecordWriterOptions(compression_type)); - for (int j = 0; j < m; ++j) { - TF_CHECK_OK(writer.WriteRecord(strings::Printf("%010d", m * i + j))); - } - } -} - -void GenerateShardedTfRecordTestData(const string& prefix, int n, int m) { - for (int i = 0; i < n; ++i) { - std::unique_ptr file; - const string filename = - strings::Printf("%s-%05d-of-%05d", prefix.c_str(), i, n); - TF_CHECK_OK(Env::Default()->NewWritableFile( - io::JoinPath("/tmp", filename), &file)); - io::RecordWriter writer(file.get()); - for (int j = 0; j < m; ++j) { - TF_CHECK_OK(writer.WriteRecord(strings::Printf("%010d", m * i + j))); - } - } -} - -typedef testing::TestWithParam TfRecordYielderTest; - -string PrefixFromCompressionType(const string& compression_type) { - if (compression_type == io::compression::kGzip) { - return "tfrecord_gzip:"; - } else if (compression_type != io::compression::kNone) { - LOG(ERROR) << "Unknown compression type, using no compression"; - } - return "tfrecord:"; -} - -TEST_P(TfRecordYielderTest, TfRecordYielderBasicTest) { - const int N = 10; - const int M = 1000; - GenerateTfRecordTestData("basic", N, M, GetParam()); - BasicRecordYielder::Options opts; - opts.file_pattern = - strings::StrCat(PrefixFromCompressionType(GetParam()), - io::JoinPath("/tmp", "basic.*")); - opts.seed = 301; - opts.bufsize = 2000; - opts.parallelism = 1; - - BasicRecordYielder* yielder = BasicRecordYielder::New(opts); - std::vector vals; - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - } - std::sort(vals.begin(), vals.end()); - auto new_end = std::unique(vals.begin(), vals.end()); - - // One epoch should have no duplicates. - ASSERT_EQ(new_end, vals.end()); - - // Iterates another two epochs. - for (int i = 0; i < 2 * N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - } - - // Iterates another 34 epochs. - for (int i = 0; i < 2 * 17 * N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - } - - // End of the 37th epoch | start of the 38th epoch. - EXPECT_TRUE(yielder->current_epoch() == 37 || yielder->current_epoch() == 38); - yielder->Close(); -} - -int NumMatches(const std::vector& vals1, - const std::vector& vals2) { - CHECK_EQ(vals1.size(), vals2.size()); - int num_matches = 0; - for (int i = 0; i < vals1.size(); ++i) { - num_matches += vals1[i] == vals2[i]; - } - return num_matches; -} - -TEST_P(TfRecordYielderTest, ShufflesShard) { - const int M = 32; - GenerateTfRecordTestData("oneshard", 1 /* num_shards */, M, GetParam()); - - BasicRecordYielder::Options opts; - opts.file_pattern = - strings::StrCat(PrefixFromCompressionType(GetParam()), - io::JoinPath("/tmp", "oneshard.0")); - opts.bufsize = M; - opts.parallelism = 1; - - // Subsequent epochs should be yielded in different orders. - std::vector epoch1, epoch2; - { - opts.seed = 301; - auto yielder = BasicRecordYielder::New(opts); - for (int i = 0; i < M; ++i) { - Record record; - record.source_id = kDefaultSourceId; - TF_CHECK_OK(yielder->Yield(&record)); - epoch1.push_back(record.value); - } - for (int i = 0; i < M; ++i) { - Record record; - record.source_id = kDefaultSourceId; - TF_CHECK_OK(yielder->Yield(&record)); - epoch2.push_back(record.value); - } - yielder->Close(); - EXPECT_LT(NumMatches(epoch1, epoch2), M); - } - - // Ordering should change if the seed changes. - std::vector epoch1_different_seed; - { - opts.seed = 103; - auto yielder = BasicRecordYielder::New(opts); - for (int i = 0; i < M; ++i) { - Record record; - record.source_id = kDefaultSourceId; - TF_CHECK_OK(yielder->Yield(&record)); - epoch1_different_seed.push_back(record.value); - } - yielder->Close(); - EXPECT_LT(NumMatches(epoch1, epoch1_different_seed), M); - } -} - -TEST(RecordYielderDeathTest, Error) { - EXPECT_DEATH([](){ - BasicRecordYielder::Options opts; - opts.file_pattern = strings::StrCat( - "tfrecord:", io::JoinPath("/tmp", "nothing.*")); - auto yielder = BasicRecordYielder::New(opts); - Record record; - record.source_id = kDefaultSourceId; - auto unused = yielder->Yield(&record); - }(), "Found no files at .*nothing"); -} - -TEST_P(TfRecordYielderTest, MatchFilesFromMultiplePatterns) { - const int N = 2; - const int M = 32; - GenerateTfRecordTestData("twoshard", N /* num_shards */, - M /* record per shard */, GetParam()); - BasicRecordYielder::Options opts; - const string path0 = io::JoinPath("/tmp", "twoshard.0"); - const string path1 = io::JoinPath("/tmp", "twoshard.1"); - opts.file_pattern = - strings::StrCat(PrefixFromCompressionType(GetParam()), path0, ",", path1); - opts.bufsize = M; - opts.parallelism = 1; - std::vector epoch; - auto yielder = BasicRecordYielder::New(opts); - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - epoch.emplace_back(string(record.value)); - } - auto new_end = std::unique(epoch.begin(), epoch.end()); - // If we iterated through both shards (rather than 1 shard twice), there - // should be no duplicates, and we should be at the end of the first epoch. - EXPECT_EQ(new_end, epoch.end()); - // End of the 1st epoch | start of the 2nd epoch. - EXPECT_TRUE(yielder->current_epoch() == 1 || yielder->current_epoch() == 2); - TF_CHECK_OK(yielder->Yield(&record)); - // End of the 2st epoch | start of the 3nd epoch. - EXPECT_TRUE(yielder->current_epoch() == 2 || yielder->current_epoch() == 3); - yielder->Close(); -} - -INSTANTIATE_TEST_CASE_P(All, TfRecordYielderTest, - testing::Values(io::compression::kNone, - io::compression::kGzip)); - -TEST(RecordYielder, MatchShardedFilePattern) { - const int num_shards = 16; - const int records_per_shard = 8; - GenerateShardedTfRecordTestData("sharded_data", num_shards, - records_per_shard); - - BasicRecordYielder::Options opts; - opts.file_pattern = strings::StrCat( - "tfrecord:", io::JoinPath("/tmp", "sharded_data@16")); - opts.bufsize = records_per_shard; - opts.parallelism = 1; - std::vector epoch; - auto yielder = BasicRecordYielder::New(opts); - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < num_shards * records_per_shard; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - epoch.emplace_back(string(record.value)); - } - auto new_end = std::unique(epoch.begin(), epoch.end()); - // If we iterated through all shards (rather than 1 shard twice), there - // should be no duplicates, and we should be at the end of the first epoch. - EXPECT_EQ(new_end, epoch.end()); - // End of the 1st epoch | start of the 2nd epoch. - EXPECT_TRUE(yielder->current_epoch() == 1 || yielder->current_epoch() == 2); - TF_CHECK_OK(yielder->Yield(&record)); - // End of the 2st epoch | start of the 3nd epoch. - EXPECT_TRUE(yielder->current_epoch() == 2 || yielder->current_epoch() == 3); - yielder->Close(); -} - -TEST(RecordYielder, MatchWildcardShardedFilePattern) { - const int num_shards = 9; - const int records_per_shard = 8; - GenerateShardedTfRecordTestData("sharded_data2", num_shards, - records_per_shard); - - BasicRecordYielder::Options opts; - opts.file_pattern = strings::StrCat( - "tfrecord:", io::JoinPath("/tmp", "sharded_data2@*")); - opts.bufsize = records_per_shard; - opts.parallelism = 1; - std::vector epoch; - auto yielder = BasicRecordYielder::New(opts); - Record record; - record.source_id = kDefaultSourceId; - - for (int i = 0; i < num_shards * records_per_shard; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - epoch.emplace_back(string(record.value)); - } - auto new_end = std::unique(epoch.begin(), epoch.end()); - // If we iterated through all shards (rather than 1 shard twice), there - // should be no duplicates, and we should be at the end of the first epoch. - EXPECT_EQ(new_end, epoch.end()); - // End of the 1st epoch | start of the 2nd epoch. - EXPECT_TRUE(yielder->current_epoch() == 1 || yielder->current_epoch() == 2); - TF_CHECK_OK(yielder->Yield(&record)); - // End of the 2st epoch | start of the 3nd epoch. - EXPECT_TRUE(yielder->current_epoch() == 2 || yielder->current_epoch() == 3); - yielder->Close(); -} - -TEST(RecordYielder, MatchIndirectFilePattern) { - const int records_per_shard = 100; - GenerateCheckpointPlainTextTestData("checkpoint", records_per_shard); - - BasicRecordYielder::Options opts; - opts.file_pattern = strings::StrCat( - "text_indirect:", io::JoinPath("/tmp", "checkpoint")); - opts.bufsize = records_per_shard; - opts.parallelism = 1; - std::vector epoch; - auto yielder = BasicRecordYielder::New(opts); - Record record; - record.source_id = kDefaultSourceId; - - // Iterate over all but 1 record in the entire data file. - for (int i = 0; i < records_per_shard - 1; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - epoch.emplace_back(string(record.value)); - } - // Update checkpoint file to point to new data and iterate over final file. - UpdateCheckpointPlainTextTestData("checkpoint", records_per_shard); - TF_CHECK_OK(yielder->Yield(&record)); - epoch.emplace_back(string(record.value)); - auto new_end = std::unique(epoch.begin(), epoch.end()); - // If we iterated through only the first version of the file, there - // should be no duplicates, and we should be at the end of the first epoch. - EXPECT_EQ(new_end, epoch.end()); - // End of the 1st epoch | start of the 2nd epoch. - EXPECT_TRUE(yielder->current_epoch() == 1 || yielder->current_epoch() == 2); - - // Now we should be iterating over the new file. - TF_CHECK_OK(yielder->Yield(&record)); - epoch.emplace_back(string(record.value)); - new_end = std::unique(epoch.begin(), epoch.end()); - EXPECT_EQ(new_end, epoch.end()); - yielder->Close(); -} - -namespace { - -class FakeIterator : public RecordIterator { - public: - FakeIterator(const string& pattern) : RecordIterator(), pattern_(pattern) {} - bool Next(string* key, Rope* value) { - if (pattern_.empty()) return false; - *key = pattern_; - *value = pattern_; - pattern_ = ""; - return true; - } - - private: - std::string pattern_; -}; - -bool register_fake_iterator = RecordIterator::RegisterWithPatternParser( - "fakeiter", [](const string& pattern) { return new FakeIterator(pattern); }, - [](const string& file_pattern, std::vector* shards) { - shards->push_back(file_pattern); - return Status::OK(); - }); - -} // namespace - -TEST(RecordYielder, RegisterFakeIterator) { - ASSERT_TRUE(register_fake_iterator); - BasicRecordYielder::Options options; - options.file_pattern = "fakeiter:hello1"; - BasicRecordYielder* yielder = BasicRecordYielder::New(options); - EXPECT_TRUE(yielder != nullptr); - Record record; - record.source_id = kDefaultSourceId; - EXPECT_TRUE(yielder->Yield(&record).ok()); - EXPECT_EQ("hello1", record.value); - EXPECT_TRUE(yielder->Yield(&record).ok()); - EXPECT_EQ("hello1", record.value); - yielder->Close(); -} - -TEST(RecordYielder, Iota) { - BasicRecordYielder::Options opts; - opts.file_pattern = "iota:100"; - opts.bufsize = 16; - opts.parallelism = 1; - BasicRecordYielder* yielder = BasicRecordYielder::New(opts); - std::vector vals; - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < 100; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - } - std::sort(vals.begin(), vals.end()); - auto new_end = std::unique(vals.begin(), vals.end()); - EXPECT_EQ(new_end, vals.end()); - yielder->Close(); -} - -TEST(RecordYielder, AdjustStaysLow) { - BasicRecordYielder::Options opts; - opts.file_pattern = "iota:100"; - opts.bufsize = 100; - opts.bufsize_in_seconds = 1; - opts.parallelism = 1; - BasicRecordYielder* yielder = BasicRecordYielder::New(opts); - - // We didn't read anything for 5 seconds, so we expect that the buffer size - // has stayed at the initial value of 16. - std::this_thread::sleep_for(std::chrono::seconds(5)); - EXPECT_EQ(16, yielder->bufsize()); - yielder->Close(); -} - -TEST(RecordYielder, AdjustUp) { - BasicRecordYielder::Options opts; - opts.file_pattern = "iota:100"; - opts.bufsize = 100; - opts.bufsize_in_seconds = 1; - opts.parallelism = 1; - BasicRecordYielder* yielder = BasicRecordYielder::New(opts); - - // We are reading 1000 records/second, so we expect that the buffer size - // will be much higher than the initial value of 16. - for (int i = 0; i < 50; i++) { - for (int j = 0; j < 100; j++) { - Record record; - record.source_id = kDefaultSourceId; - LOG(INFO) << "yield " << j; - TF_CHECK_OK(yielder->Yield(&record)); - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - EXPECT_LT(30, yielder->bufsize()); - EXPECT_GE(100, yielder->bufsize()); - yielder->Close(); -} - -TEST(RecordIterator, GetFilePatternPrefix) { - EXPECT_EQ("", RecordIterator::GetFilePatternPrefix("/foo/bar/*")); - EXPECT_EQ("baz", RecordIterator::GetFilePatternPrefix("baz:/foo/bar/*")); - - string file_pattern; - - file_pattern = "/foo/bar/*"; - EXPECT_EQ("", RecordIterator::StripPrefixFromFilePattern(&file_pattern)); - EXPECT_EQ("/foo/bar/*", file_pattern); - - file_pattern = "baz:/foo/bar/*"; - EXPECT_EQ("baz", RecordIterator::StripPrefixFromFilePattern(&file_pattern)); - EXPECT_EQ("/foo/bar/*", file_pattern); -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/rope.h b/lingvo/core/ops/rope.h deleted file mode 100644 index ae82a72c..00000000 --- a/lingvo/core/ops/rope.h +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_ROPE_H_ -#define LINGVO_CORE_OPS_ROPE_H_ - -#include - -namespace tensorflow { -namespace lingvo { - -class Rope : public std::string { - public: - Rope() = default; - Rope(const std::string& s) : std::string(s) {} - Rope(std::string&& s) : std::string(std::move(s)) {} - - void AppendTo(std::string* dst) const { - dst->append(*this); - } - - std::string ToString() const { return *this; } -}; - -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_ROPE_H_ diff --git a/lingvo/core/ops/sequential_record_yielder.cc b/lingvo/core/ops/sequential_record_yielder.cc deleted file mode 100644 index 758c3fb2..00000000 --- a/lingvo/core/ops/sequential_record_yielder.cc +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/sequential_record_yielder.h" - -#include - -#include "lingvo/core/ops/record_yielder.h" -#include "tensorflow/core/lib/core/errors.h" - -namespace tensorflow { -namespace lingvo { - -namespace { -constexpr int kInfinite = -1; -} // namespace - -SequentialRecordYielder::SequentialRecordYielder(const string& file_pattern, - const int64_t repeat_count) - : file_type_(RecordIterator::GetFilePatternPrefix(file_pattern)), - repeat_count_(repeat_count) { - LOG(INFO) << this << "Sequential record yielder start"; - string mutable_file_pattern(file_pattern); - if (!file_type_.empty()) { - mutable_file_pattern.erase(0, file_type_.size() + 1); - } - // Finds all files. - TF_CHECK_OK(RecordIterator::ParsePattern(file_type_, mutable_file_pattern, - &filenames_)); - std::sort(filenames_.begin(), filenames_.end()); - if (filenames_.empty()) { - LOG(FATAL) << "Found no files at " << file_pattern; - } - - CHECK(repeat_count == kInfinite || repeat_count > 0) - << "Repeat count must either be -1 (infinite) or a positive integer."; - - record_iterator_ = std::unique_ptr( - RecordIterator::New(file_type_, filenames_[0])); -} - -SequentialRecordYielder* SequentialRecordYielder::New( - const string& file_pattern, const int64_t repeat_count) { - return new SequentialRecordYielder(file_pattern, repeat_count); -} - -SequentialRecordYielder::~SequentialRecordYielder() {} - -void SequentialRecordYielder::Close() { - LOG(INFO) << this << "Sequential record yielder exit"; - delete this; -} - -Status SequentialRecordYielder::Yield(Record* record) { - string key; - if (record_iterator_->Next(&key, &record->value)) { - return Status::OK(); - } - - // No more records from current iterator, advance to next iterator. - cur_file_index_ = (cur_file_index_ + 1) % filenames_.size(); - if (cur_file_index_ == 0) { - ++num_repeats_; - LOG(INFO) << "SequentialRecordYielder finished " << num_repeats_ - << " repeats."; - if (repeat_count_ != kInfinite && num_repeats_ == repeat_count_) { - return errors::OutOfRange("SequentialRecordYielder reached ", - repeat_count_, " repeats."); - } - } - record_iterator_ = std::unique_ptr( - RecordIterator::New(file_type_, filenames_[cur_file_index_])); - return Yield(record); -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/sequential_record_yielder.h b/lingvo/core/ops/sequential_record_yielder.h deleted file mode 100644 index 7c1a8960..00000000 --- a/lingvo/core/ops/sequential_record_yielder.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ -#ifndef LINGVO_CORE_OPS_SEQUENTIAL_RECORD_YIELDER_H_ -#define LINGVO_CORE_OPS_SEQUENTIAL_RECORD_YIELDER_H_ - -#include "lingvo/core/ops/record_yielder.h" - -namespace tensorflow { -namespace lingvo { - -// SequentialRecordYielder processes records in order. -class SequentialRecordYielder : public RecordYielder { - public: - ~SequentialRecordYielder() override; - void Close() override; - Status Yield(Record* record) override; - - // Returns a sequential record yielder. The caller is responsible for calling - // Close when this yielder is no longer required. The caller shouldn't delete - // the yielder. - static SequentialRecordYielder* New(const string& file_pattern, - const int64_t repeat_count); - - protected: - explicit SequentialRecordYielder(const string& file_pattern, - const int64_t repeat_count); - - private: - const string file_type_; - // Target number of repetitions of the dataset. -1 means to repeat - // forever. - const int64_t repeat_count_; - - std::vector filenames_; - int cur_file_index_ = 0; - std::unique_ptr record_iterator_; - // Current number of repetitions of the dataset. - int64_t num_repeats_ = 0; -}; - -} // namespace lingvo -} // namespace tensorflow -#endif // LINGVO_CORE_OPS_SEQUENTIAL_RECORD_YIELDER_H_ diff --git a/lingvo/core/ops/simple_vocab.cc b/lingvo/core/ops/simple_vocab.cc deleted file mode 100644 index 79eb678d..00000000 --- a/lingvo/core/ops/simple_vocab.cc +++ /dev/null @@ -1,258 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/simple_vocab.h" - -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/platform/logging.h" - -namespace { - -constexpr char kSosToken[] = ""; -constexpr char kEosToken[] = ""; -constexpr char kUnkToken[] = ""; -constexpr char kSowToken[] = ""; -constexpr char kEowToken[] = ""; -constexpr char kSosTokenUpper[] = ""; -constexpr char kEosTokenUpper[] = ""; -constexpr char kUnkTokenUpper[] = ""; - -} // end namespace - -namespace tensorflow { -namespace lingvo { - -namespace debug { - -static Vocab* vocab = nullptr; - -void SetUpVocab(const string& vocab_filename) { - if (vocab == nullptr) { - vocab = new Vocab(); - TF_CHECK_OK(vocab->Load(vocab_filename)); - } -} - -string IdsToStr(const std::vector& ids) { - if (vocab != nullptr) { - const std::vector toks = vocab->IdsToTokens(ids); - return str_util::Join(toks, " "); - } else { - return str_util::Join(ids, " "); - } -} -} // namespace debug - -Status Vocab::Load(const string& vocab_glob, bool load_token_ids) { - std::vector vocab_filenames; - TF_RETURN_IF_ERROR( - Env::Default()->GetMatchingPaths(vocab_glob, &vocab_filenames)); - if (vocab_filenames.size() != 1) { - return errors::InvalidArgument( - "Did not match exactly one file with pattern: ", vocab_glob); - } - const string& vocab_filename = vocab_filenames[0]; - - debug::SetUpVocab(vocab_filename); - - string content; - TF_RETURN_IF_ERROR( - ReadFileToString(Env::Default(), vocab_filename, &content)); - - return Load(str_util::Split(content, '\n'), load_token_ids); -} - -Status Vocab::Load(const std::vector& lines, bool load_token_ids) { - id_to_token_.clear(); - token_to_id_.clear(); - int32 next_id = 0; - for (StringPiece line : lines) { - if (line.empty()) continue; - const std::vector parts = str_util::Split(line, '\t'); - CHECK_GE(parts.size(), 1); - const string tok = parts[0]; - if (!load_token_ids) { - token_to_id_[tok] = next_id; - id_to_token_[next_id] = tok; - next_id++; - } else { - CHECK_GE(parts.size(), 2); - const int32 id = std::stoi(parts[1]); - token_to_id_[tok] = id; - id_to_token_[id] = tok; - } - VLOG(2) << "Vocab " << token_to_id_[tok] << " " << tok; - } - use_upper_token_symbols_ = false; - std::vector expected_tokens = {kSosToken, kEosToken, kUnkToken}; - std::vector unexpected_tokens = {kSosTokenUpper, kEosTokenUpper, - kUnkTokenUpper}; - if (token_to_id_.find(sos_token()) == token_to_id_.end()) { - use_upper_token_symbols_ = true; - expected_tokens.swap(unexpected_tokens); - } - sos_id_ = token_to_id_[sos_token()]; - - for (const auto& token : expected_tokens) { - if (token_to_id_.find(token) == token_to_id_.end()) { - return errors::InvalidArgument(token, " is not found in the vocab."); - } - } - for (const auto& token : unexpected_tokens) { - if (token_to_id_.find(token) != token_to_id_.end()) { - return errors::InvalidArgument("Invalid token ", token, - " is found in the vocab."); - } - } - unk_id_ = -1; - sos_id_ = TokenToId(sos_token()); - eos_id_ = TokenToId(eos_token()); - sow_id_ = TokenToId(sow_token()); - eow_id_ = TokenToId(eow_token()); - unk_id_ = TokenToId(unk_token()); - return Status::OK(); -} - -const char* Vocab::sos_token() const { - return use_upper_token_symbols_ ? kSosTokenUpper : kSosToken; -} - -const char* Vocab::eos_token() const { - return use_upper_token_symbols_ ? kEosTokenUpper : kEosToken; -} - -const char* Vocab::unk_token() const { - return use_upper_token_symbols_ ? kUnkTokenUpper : kUnkToken; -} - -const char* Vocab::sow_token() const { return kSowToken; } - -const char* Vocab::eow_token() const { return kEowToken; } - -namespace { - -class VocabTokenToIdOp : public OpKernel { - public: - explicit VocabTokenToIdOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - std::vector vocab; - OP_REQUIRES_OK(ctx, ctx->GetAttr("vocab", &vocab)); - bool load_token_ids_from_vocab; - OP_REQUIRES_OK(ctx, ctx->GetAttr("load_token_ids_from_vocab", - &load_token_ids_from_vocab)); - OP_REQUIRES_OK(ctx, vocab_.Load(vocab, load_token_ids_from_vocab)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* token; - OP_REQUIRES_OK(ctx, ctx->input("token", &token)); - Tensor* id; - OP_REQUIRES_OK(ctx, ctx->allocate_output("id", token->shape(), &id)); - if (token->dims() == 0) { - id->scalar()() = vocab_.TokenToId(token->scalar()()); - } else { - OP_REQUIRES( - ctx, token->dims() == 1, - errors::InvalidArgument("Input must be a scalar or 1D tensor.")); - for (int i = 0; i < token->dim_size(0); i++) { - id->vec()(i) = vocab_.TokenToId(token->vec()(i)); - } - } - } - - private: - Vocab vocab_; -}; - -REGISTER_KERNEL_BUILDER(Name("VocabTokenToId").Device(DEVICE_CPU), - VocabTokenToIdOp); - -class VocabIdToTokenOp : public OpKernel { - public: - explicit VocabIdToTokenOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - std::vector vocab; - OP_REQUIRES_OK(ctx, ctx->GetAttr("vocab", &vocab)); - bool load_token_ids_from_vocab; - OP_REQUIRES_OK(ctx, ctx->GetAttr("load_token_ids_from_vocab", - &load_token_ids_from_vocab)); - OP_REQUIRES_OK(ctx, vocab_.Load(vocab, load_token_ids_from_vocab)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* id; - OP_REQUIRES_OK(ctx, ctx->input("id", &id)); - Tensor* token; - OP_REQUIRES_OK(ctx, ctx->allocate_output("token", id->shape(), &token)); - if (id->dims() == 0) { - token->scalar()() = vocab_.IdToToken(id->scalar()()); - } else { - OP_REQUIRES( - ctx, id->dims() == 1, - errors::InvalidArgument("Input must be a scalar or 1D tensor.")); - for (int i = 0; i < id->dim_size(0); i++) { - token->vec()(i) = vocab_.IdToToken(id->vec()(i)); - } - } - } - - private: - Vocab vocab_; -}; - -REGISTER_KERNEL_BUILDER(Name("VocabIdToToken").Device(DEVICE_CPU), - VocabIdToTokenOp); - -class TokenInVocabOp : public OpKernel { - public: - explicit TokenInVocabOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - std::vector vocab; - OP_REQUIRES_OK(ctx, ctx->GetAttr("vocab", &vocab)); - bool load_token_ids_from_vocab; - OP_REQUIRES_OK(ctx, ctx->GetAttr("load_token_ids_from_vocab", - &load_token_ids_from_vocab)); - OP_REQUIRES_OK(ctx, vocab_.Load(vocab, load_token_ids_from_vocab)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* token; - OP_REQUIRES_OK(ctx, ctx->input("token", &token)); - Tensor* result; - OP_REQUIRES_OK(ctx, - ctx->allocate_output("result", token->shape(), &result)); - if (token->dims() == 0) { - result->scalar()() = vocab_.InVocab(token->scalar()()); - } else { - OP_REQUIRES( - ctx, token->dims() == 1, - errors::InvalidArgument("Input must be a scalar or 1D tensor.")); - for (int i = 0; i < token->dim_size(0); i++) { - result->vec()(i) = vocab_.InVocab(token->vec()(i)); - } - } - } - - private: - Vocab vocab_; -}; - -REGISTER_KERNEL_BUILDER(Name("TokenInVocab").Device(DEVICE_CPU), - TokenInVocabOp); - -} // namespace - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/simple_vocab.h b/lingvo/core/ops/simple_vocab.h deleted file mode 100644 index db599f90..00000000 --- a/lingvo/core/ops/simple_vocab.h +++ /dev/null @@ -1,129 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_SIMPLE_VOCAB_H_ -#define LINGVO_CORE_OPS_SIMPLE_VOCAB_H_ -// TODO(zhifengc): Add comments for this class. - -#include -#include -#include - -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/platform/macros.h" - -namespace tensorflow { -namespace lingvo { - -class Vocab { - public: - Vocab() {} - - ~Vocab() {} - - Status Load(const string& vocab_filename, bool load_token_ids = false); - Status Load(const std::vector& lines, bool load_token_ids = false); - - int32 sos_id() const { return sos_id_; } - int32 eos_id() const { return eos_id_; } - int32 unk_id() const { return unk_id_; } - int32 sow_id() const { return sow_id_; } - int32 eow_id() const { return eow_id_; } - - const char* sos_token() const; - const char* eos_token() const; - const char* unk_token() const; - const char* sow_token() const; - const char* eow_token() const; - - bool InVocab(const string& tok) const { return token_to_id_.count(tok) > 0; } - - int32 TokenToId(const string& tok) const { - auto it = token_to_id_.find(tok); - if (it != token_to_id_.end()) return it->second; - return unk_id_; - } - - int GetVocabSize() const { return id_to_token_.size(); } - - void GreedyMatchStringToTokenId(StringPiece text, int32* token_id, - int* token_size) const { - // This finds the longest prefix of the "text" in the given list of tokens - // and returns the ID of the found token (if nothing found, unk_id_ is - // returned) and the length of the found token through input argument - // pointers. - *token_id = unk_id_; - *token_size = 1; // For , the input is of length 1 char, but output is - // (length of 5). - for (const auto kv : id_to_token_) { - if (str_util::StartsWith(text, kv.second)) { - // Find the longest matching token. - if (*token_id == unk_id_ || *token_size < kv.second.size()) { - *token_id = kv.first; - *token_size = kv.second.size(); - } - } - } - } - - std::vector TokensToIds(const std::vector& toks) const { - std::vector ids; - ids.reserve(toks.size()); - for (const string& tok : toks) { - ids.push_back(TokenToId(tok)); - } - return ids; - } - - const string IdToToken(const int32 id) const { - const auto it = id_to_token_.find(id); - if (it != id_to_token_.end()) { - return it->second; - } else { - return unk_token(); - } - } - - std::vector IdsToTokens(const std::vector& ids) const { - std::vector toks; - toks.reserve(ids.size()); - for (const int32 id : ids) { - toks.push_back(IdToToken(id)); - } - return toks; - } - - private: - int32 sos_id_ = -1; - int32 eos_id_ = -1; - int32 unk_id_ = -1; - int32 sow_id_ = -1; - int32 eow_id_ = -1; - bool use_upper_token_symbols_ = false; - std::unordered_map id_to_token_; - std::unordered_map token_to_id_; - - TF_DISALLOW_COPY_AND_ASSIGN(Vocab); -}; - -namespace debug { -string IdsToStr(const std::vector& ids); -} // namespace debug - -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_SIMPLE_VOCAB_H_ diff --git a/lingvo/core/ops/simple_vocab_test.py b/lingvo/core/ops/simple_vocab_test.py deleted file mode 100644 index 2a14e8cb..00000000 --- a/lingvo/core/ops/simple_vocab_test.py +++ /dev/null @@ -1,163 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for simple_vocab.""" - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_utils - - -class VocabOpsTest(test_utils.TestCase): - - def testVocabTokenToId(self): - with self.session(use_gpu=False): - vocab = [ - '', - '', - '', - '', - 'a', - 'b c d e', - 'øut', - 'über', - '♣', - '愤青', - '←', - ] - self.assertEqual(0, ops.vocab_token_to_id('', vocab=vocab).eval()) - self.assertEqual(4, ops.vocab_token_to_id('a', vocab=vocab).eval()) - self.assertAllEqual([5, 8], - ops.vocab_token_to_id(['b c d e', '♣'], - vocab=vocab).eval()) - self.assertEqual(2, ops.vocab_token_to_id('unknown', vocab=vocab).eval()) - - def testVocabTokenToIdLoadId(self): - with self.session(use_gpu=False): - vocab = [ - ' 3', - ' 5', - ' 7', - ' 9', - 'a 2', - 'b c d e 4', - 'øut 8', - 'über 10', - '♣ -1', - '愤青 -3', - '← -5', - ] - self.assertEqual( - 3, - ops.vocab_token_to_id( - '', vocab=vocab, load_token_ids_from_vocab=True).eval()) - self.assertEqual( - 2, - ops.vocab_token_to_id( - 'a', vocab=vocab, load_token_ids_from_vocab=True).eval()) - self.assertAllEqual([4, -1], - ops.vocab_token_to_id( - ['b c d e', '♣'], - vocab=vocab, - load_token_ids_from_vocab=True).eval()) - self.assertEqual( - 7, - ops.vocab_token_to_id( - 'unknown', vocab=vocab, load_token_ids_from_vocab=True).eval()) - - def testVocabIdToToken(self): - with self.session(use_gpu=False): - vocab = [ - '', - '', - '', - '', - 'a', - 'b c d e', - 'øut', - 'über', - '♣', - '愤青', - '←', - ] - self.assertEqual(b'', ops.vocab_id_to_token(0, vocab=vocab).eval()) - self.assertEqual(b'a', ops.vocab_id_to_token(4, vocab=vocab).eval()) - self.assertAllEqual([b'b c d e', u'♣'.encode('utf-8')], - ops.vocab_id_to_token([5, 8], vocab=vocab).eval()) - self.assertEqual(b'', ops.vocab_id_to_token(2, vocab=vocab).eval()) - self.assertEqual(b'', ops.vocab_id_to_token(-1, vocab=vocab).eval()) - self.assertEqual(b'', ops.vocab_id_to_token(11, vocab=vocab).eval()) - - def testVocabIdToTokenLoadId(self): - with self.session(use_gpu=False): - vocab = [ - ' 3', - ' 5', - ' 7', - ' 9', - 'a 2', - 'b c d e 4', - 'øut 8', - 'über 10', - '♣ -1', - '愤青 -3', - '← -5', - ] - self.assertEqual( - b'', - ops.vocab_id_to_token(3, vocab=vocab, - load_token_ids_from_vocab=True).eval()) - self.assertEqual( - b'a', - ops.vocab_id_to_token(2, vocab=vocab, - load_token_ids_from_vocab=True).eval()) - self.assertAllEqual([b'b c d e', u'♣'.encode('utf-8')], - ops.vocab_id_to_token( - [4, -1], - vocab=vocab, - load_token_ids_from_vocab=True).eval()) - self.assertEqual( - b'', - ops.vocab_id_to_token(7, vocab=vocab, - load_token_ids_from_vocab=True).eval()) - self.assertEqual( - b'', - ops.vocab_id_to_token(0, vocab=vocab, - load_token_ids_from_vocab=True).eval()) - - def testTokenInVocab(self): - with self.session(use_gpu=False): - vocab = [ - '', - '', - '', - '', - 'a', - 'b c d e', - 'øut', - 'über', - '♣', - '愤青', - '←', - ] - self.assertTrue(ops.token_in_vocab('a', vocab=vocab).eval()) - self.assertTrue(ops.token_in_vocab('', vocab=vocab).eval()) - self.assertTrue( - ops.token_in_vocab(['b c d e', '♣'], vocab=vocab).eval().all()) - self.assertFalse(ops.token_in_vocab('unknown', vocab=vocab).eval()) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/static_map_op.cc b/lingvo/core/ops/static_map_op.cc deleted file mode 100644 index cf3abe6f..00000000 --- a/lingvo/core/ops/static_map_op.cc +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2019 The TensorFlow 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. -==============================================================================*/ -#include -#include -#include - -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/gtl/flatmap.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/logging.h" -#include "tensorflow/core/util/work_sharder.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -template -void Iota(std::vector* vec) { - std::iota(vec->begin(), vec->end(), T()); -} - -template <> -void Iota(std::vector* vec) { - // Do nothing. -} - -template -class StaticMapOp : public OpKernel { - public: - explicit StaticMapOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - std::vector keys; - OP_REQUIRES_OK(ctx, ctx->GetAttr("keys", &keys)); - std::vector vals; - OP_REQUIRES_OK(ctx, ctx->GetAttr("vals", &vals)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("unk", &unk_)); - - if (keys.empty() && !vals.empty()) { - keys.resize(vals.size()); - Iota(&keys); - } - - if (!keys.empty() && vals.empty()) { - vals.resize(keys.size()); - Iota(&vals); - } - - OP_REQUIRES(ctx, keys.size() == vals.size(), - errors::InvalidArgument("keys and vals are different sizes: ", - keys.size(), " / ", vals.size())); - - for (int i = 0; i < keys.size(); ++i) { - OP_REQUIRES(ctx, dict_.insert({keys[i], vals[i]}).second, - errors::InvalidArgument("keys have duplicates: ", keys[i])); - } - } - - void Compute(OpKernelContext* ctx) override { - const Tensor& x = ctx->input(0); - Tensor* y; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, x.shape(), &y)); - - auto tx = x.flat(); - auto ty = y->flat(); - int total = tx.size(); - auto workers = ctx->device()->tensorflow_cpu_worker_threads(); - Shard(workers->num_threads, workers->workers, total, 250, - [this, &tx, &ty](int64 start, int64 limit) { - for (int64 i = start; i < limit; ++i) { - auto it = dict_.find(tx(i)); - if (it != dict_.end()) { - ty(i) = it->second; - } else { - ty(i) = unk_; - } - } - }); - } - - protected: - tensorflow::gtl::FlatMap dict_; - V unk_; -}; - -REGISTER_KERNEL_BUILDER(Name("StaticMapStringInt").Device(DEVICE_CPU), - StaticMapOp); -REGISTER_KERNEL_BUILDER(Name("StaticMapIntString").Device(DEVICE_CPU), - StaticMapOp); -REGISTER_KERNEL_BUILDER(Name("StaticMapIntInt").Device(DEVICE_CPU), - StaticMapOp); - -#if GOOGLE_CUDA -REGISTER_KERNEL_BUILDER(Name("StaticMapStringInt") - .Device(DEVICE_GPU) - .HostMemory("x") - .HostMemory("y"), - StaticMapOp); - -REGISTER_KERNEL_BUILDER(Name("StaticMapIntString") - .Device(DEVICE_GPU) - .HostMemory("x") - .HostMemory("y"), - StaticMapOp); - -REGISTER_KERNEL_BUILDER( - Name("StaticMapIntInt").Device(DEVICE_GPU).HostMemory("x").HostMemory("y"), - StaticMapOp); -#endif - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/static_map_op_test.py b/lingvo/core/ops/static_map_op_test.py deleted file mode 100644 index 6fbd7c04..00000000 --- a/lingvo/core/ops/static_map_op_test.py +++ /dev/null @@ -1,60 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for static_map_op.""" - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_utils - -FLAGS = tf.flags.FLAGS - - -class StaticMapOpsTest(test_utils.TestCase): - - def testStaticMap(self): - with self.session(): - self.assertAllEqual([[-1, -2, -3], [-1, -5, -6]], - ops.static_map_int_int( - x=[[1, 2, 3], [8, 5, 6]], - keys=[1, 2, 3, 4, 5, 6], - vals=[-1, -2, -3, -4, -5, -6]).eval()) - self.assertAllEqual([[1, 3, 5], [7, 9, 11]], - ops.static_map_string_int( - x=[['a', 'b', 'c'], ['d', 'e', 'f']], - keys=['d', 'e', 'f', 'a', 'b', 'c'], - vals=[7, 9, 11, 1, 3, 5]).eval()) - self.assertAllEqual([[3, 4, 5], [0, 1, 2]], - ops.static_map_string_int( - x=[['a', 'b', 'c'], ['d', 'e', 'f']], - keys=['d', 'e', 'f', 'a', 'b', 'c']).eval()) - self.assertAllEqual([[2, -1, -1], [0, -1, 1]], - ops.static_map_string_int( - x=[['a', 'b', 'c'], ['d', 'e', 'f']], - keys=['d', 'f', 'a']).eval()) - - # Error cases. - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'sizes'): - ops.static_map_string_int( - x=[['a', 'b', 'c'], ['d', 'e', 'f']], - keys=['d', 'f', 'a'], - vals=[1, 2]).eval() - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'duplicates'): - ops.static_map_string_int( - x=[['a', 'b', 'c'], ['d', 'e', 'f']], keys=['d', 'f', 'd']).eval() - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/testdata/BUILD b/lingvo/core/ops/testdata/BUILD deleted file mode 100644 index b66733b7..00000000 --- a/lingvo/core/ops/testdata/BUILD +++ /dev/null @@ -1,44 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) # Apache 2.0 - -filegroup( - name = "best_step_testdata", - data = [ - "events.out.tfevents.test", - "history.txt", - "history_bleu.txt", - ], -) - -filegroup( - name = "test_vocab", - data = ["test_vocab.txt"], -) - -filegroup( - name = "test_ngrams", - data = ["test_ngrams.txt"], -) - -filegroup( - name = "bpe_words_vocab", - data = ["bpe_words.vocab"], -) - -filegroup( - name = "bpe_codes_vocab", - data = ["bpe_codes.vocab"], -) - -# Data from "Findings of the 2014 Workshop on Statistical Machine Translation" -# http://www.aclweb.org/anthology/W14-3302 -filegroup( - name = "wmt", - data = glob(["wmt/*"]), -) - -filegroup( - name = "mlperf_vocab", - data = ["mlperf.ende.subwords.vocab"], -) diff --git a/lingvo/core/ops/testdata/bpe_codes.vocab b/lingvo/core/ops/testdata/bpe_codes.vocab deleted file mode 100644 index 8599b2fe..00000000 --- a/lingvo/core/ops/testdata/bpe_codes.vocab +++ /dev/null @@ -1,67 +0,0 @@ - - - - -E@@ 44 -O@@ 35 -N@@ 32 -A@@ 31 -S@@ 31 -I@@ 28 -Y 26 -P@@ 26 -L@@ 23 -S 22 -E 21 -H@@ 20 -TH@@ 18 -T 18 -N 17 -B@@ 17 -U@@ 17 -R@@ 15 -F@@ 15 -W@@ 14 -ED 13 -ER@@ 13 -THE 13 -G@@ 13 -M@@ 12 -ER 12 -V@@ 11 -TOM 11 -HE 11 -OU@@ 10 -D 10 -AT 10 -WAS 10 -IN@@ 10 -HI@@ 9 -Y@@ 9 -AD 9 -C@@ 9 -D@@ 9 -T@@ 9 -TO 8 -'@@ 8 -LA@@ 8 -O 8 -AND 8 -ON@@ 8 -K@@ 7 -WA@@ 7 -A 7 -G 7 -AN@@ 5 -L 5 -R 5 -U 4 -K 3 -F 3 -W 3 -H 3 -TO@@ 2 -Q@@ 1 -X@@ 1 -I 1 -M 1 diff --git a/lingvo/core/ops/testdata/bpe_words.vocab b/lingvo/core/ops/testdata/bpe_words.vocab deleted file mode 100644 index 6baa4758..00000000 --- a/lingvo/core/ops/testdata/bpe_words.vocab +++ /dev/null @@ -1,143 +0,0 @@ -A 52 -ACT 7,41,17 -AFTER 7,22,43,29 -ALL 7,12,55 -ALONE 7,12,49,14 -ALTHOUGH 7,12,16,33,27,61 -AND 48 -ANSWERED 54,8,23,25,24 -ANY 54,10 -AROUND 7,21,33,6,34 -ASKED 7,8,50,24 -AT 35 -AWAY 7,51,10 -BABY 19,7,19,10 -BAD 19,40 -BADLY 19,7,42,12,10 -BARNEY 19,7,21,6,4,10 -BEAT 19,4,35 -BEEN 19,4,4,18 -BEGUN 19,4,27,20,18 -BOWSER'S 19,5,23,8,25,45,13 -BOY 19,5,10 -BUN 19,20,18 -BUT 19,20,17 -CAUGHT 41,7,20,27,15,17 -CAUTIOUS 41,7,20,43,9,33,13 -CRYING 41,21,39,37,53 -DAY 42,7,10 -DID 42,9,34 -DIED 42,9,24 -DO 42,47 -DOWN 42,5,23,18 -EAT 4,35 -END 4,6,34 -EVER 4,30,29 -EXCEPT 4,64,41,4,11,17 -FALLEN 22,7,12,12,4,18 -FARMER 22,7,21,28,29 -FATHER 22,7,16,29 -FATHER'S 22,7,16,25,45,13 -FELL 22,4,12,55 -FEW 22,4,60 -FOR 22,5,56 -FOUND 22,33,6,34 -GIVE 27,9,30,14 -GO 27,47 -GOOD 27,5,5,34 -GUIDANCE 27,20,9,42,54,41,14 -HAD 15,40 -HE 32 -HEAD 15,4,40 -HILL 38,12,55 -HIM 38,66 -HIS 38,13 -HONEST 15,49,4,8,17 -HOUSE 15,33,8,14 -HUNGRY 15,20,6,27,21,10 -HUT 15,20,17 -I 65 -IF 9,59 -IN 9,18 -INTO 37,44 -IT 9,17 -KNOW 50,6,5,60 -LAST 46,8,17 -LEARNED 12,4,7,21,6,24 -LITTLE 12,9,43,43,12,14 -LIVED 12,9,30,24 -MAN'S 28,54,45,13 -MANY 28,54,10 -MAY 28,7,10 -ME 28,14 -MISSED 28,9,8,8,24 -MORNING 28,5,21,6,37,53 -MOTHER 28,5,16,29 -MUST 28,20,8,17 -MY 28,10 -NAMED 6,7,28,24 -NEITHER 6,4,9,16,29 -NEVER 6,4,30,29 -NO 6,47 -NONE 6,49,14 -NOT 6,5,17 -OF 5,59 -OLD 5,12,34 -ONE 49,14 -OUT 33,17 -OVER 5,30,29 -PENNIES 11,4,6,6,9,4,13 -PENNY 11,4,6,6,10 -PIG 11,9,53 -PIPER'S 11,9,11,25,45,13 -PIPES 11,9,11,4,13 -PLAY 11,46,10 -PLAYED 11,46,39,24 -PLAYING 11,46,39,37,53 -POUCH 11,33,41,61 -QUEER 63,20,4,29 -RAN 21,7,18 -REPLIED 21,4,11,12,9,24 -SHAKING 8,15,7,50,37,53 -SHALL 8,15,7,12,55 -SINCE 8,37,41,14 -SLY 8,12,10 -SO 8,47 -SON 8,5,18 -SOON 8,5,5,18 -STEALING 8,43,4,7,12,37,53 -STREET 8,43,21,4,4,17 -SUCH 8,20,41,61 -SUPPOSE 8,20,11,11,5,8,14 -TAKE 43,7,50,14 -THAT 16,35 -THE 26 -THEIR 16,4,9,56 -THERE 16,25,14 -THEY 16,4,10 -THINGS 16,37,27,13 -TO 44 -TOM 31 -TOM'S 62,28,45,13 -TOOK 62,5,58 -UNDER 20,6,42,29 -UNLESS 20,6,12,4,8,13 -VERY 30,25,10 -VILLAGE 30,9,12,46,27,14 -WALKED 51,12,50,24 -WANT 51,6,17 -WANTED 51,6,43,24 -WAS 36 -WAY 51,10 -WAYS 51,39,13 -WE 23,14 -WEEK 23,4,4,58 -WENT 23,4,6,17 -WERE 23,25,14 -WHAT 23,15,35 -WHEN 23,15,4,18 -WHISPERED 23,38,8,11,25,24 -WILL 23,9,12,55 -WORK 23,5,21,58 -YOU 39,5,57 -YOUNG 39,33,6,53 diff --git a/lingvo/core/ops/testdata/events.out.tfevents.test b/lingvo/core/ops/testdata/events.out.tfevents.test deleted file mode 100644 index a59f35f4773525b572013df595a1ab951409d5b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80291 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 YhFFHz(u13ai5TA6_' -'_' -', _' -'._' -'the_' -'_' -'in_' -'of_' -'and_' -'to_' -'die_' -'der_' -'und_' -'a_' -'s_' -'-_' -'is_' -'that_' -'zu_' -'for_' -'den_' -'von_' -'n_' -'on_' -'ist_' -'an_' -'für_' -'. _' -'be_' -'The_' -'with_' -'en_' -'es_' -'are_' -'das_' -'as_' -'e_' -'des_' -'auf_' -'mit_' -'it_' -'eine_' -'dass_' -'nicht_' -'I_' -'im_' -'not_' -'have_' -'by_' -'this_' -' (_' -' – _' -'sich_' -'was_' -'ein_' -'werden_' -'Die_' -'will_' -'from_' -'we_' -'dem_' -'’_' -'t_' -': _' -'at_' -'or_' -'Sie_' -'which_' -'has_' -'er_' -'als_' -'auch_' -'you_' -'wir_' -'r_' -'In_' -'um_' -'sind_' -'wird_' -') _' -'so_' -'can_' -'sie_' -'ing_' -'all_' -''_' -' - _' -'einer_' -'hat_' -'wie_' -'also_' -'their_' -'European_' -'haben_' -'d_' -'would_' -'ed_' -'oder_' -'its_' -'more_' -'über_' -'but_' -'?_' -'einen_' -'ich_' -'y_' -'zur_' -'our_' -'they_' -'aus_' -'bei_' -'Das_' -'one_' -'been_' -'; _' -'nur_' -'Union_' -'should_' -'It_' -'EU_' -'einem_' -'/_' -'nach_' -'durch_' -'This_' -'können_' -'diese_' -'ung_' -'other_' -'zum_' -'noch_' -'only_' -'there_' -' , _' -'do_' -'am_' -'de_' -'countries_' -'1_' -'kann_' -'dieser_' -'war_' -'than_' -'We_' -'new_' -'o_' -'your_' -'Europe_' -'Der_' -'must_' -'Mr_' -'no_' -'vor_' -'were_' -'2_' -'like_' -'wenn_' -'man_' -'US_' -'Ich_' -'wurde_' -'- _' -'about_' -' "_' -'us_' -'President_' -'m_' -'time_' -'Es_' -'these_' -'if_' -'aber_' -'te_' -'sein_' -'who_' -'up_' -'very_' -'Hotel_' -'world_' -' ._' -'uns_' -'Commission_' -'when_' -'such_' -'A_' -'But_' -'Wir_' -'people_' -'müssen_' -' “_' -'into_' -'ten_' -'ng_' -'China_' -'out_' -'3_' -'mehr_' -'ihre_' -'his_' -'5_' -'now_' -'most_' -'some_' -'what_' -'sehr_' -'Kommission_' -'many_' -'!_' -'i_' -')._' -'l_' -'he_' -'any_' -'% _' -'had_' -' „_' -'States_' -'them_' -',_' -'eines_' -'4_' -'well_' -'Herr_' -'), _' -'" _' -'economic_' -'diesem_' -'need_' -'unter_' -'years_' -'political_' -'between_' -'ly_' -'zwischen_' -'first_' -'hotel_' -'alle_' -'even_' -'policy_' -'make_' -'bis_' -'two_' -'muss_' -'could_' -'over_' -'anderen_' -'use_' -'Parliament_' -'keine_' -'my_' -'work_' -'may_' -'way_' -'important_' -'Council_' -'gegen_' -'report_' -'Präsident_' -'0_' -'system_' -'Europäischen_' -'Europa_' -'gibt_' -'because_' -'If_' -'those_' -'just_' -'support_' -'vom_' -'seine_' -'sowie_' -'k_' -'country_' -'year_' -'much_' -'Wenn_' -'dieses_' -'after_' -'government_' -'Member_' -'al_' -'made_' -' _' -'ungen_' -'able_' -'take_' -'h_' -'möchte_' -'market_' -'being_' -'immer_' -'“ _' -'” _' -'public_' -'own_' -'long_' -'' _' -'Welt_' -'dies_' -'sondern_' -'Zeit_' -'Menschen_' -'Jahren_' -'international_' -'(_' -'where_' -'right_' -'good_' -'financial_' -'how_' -'ihrer_' -'da_' -'diesen_' -'USA_' -'wurden_' -'andere_' -'For_' -':_' -'g_' -'As_' -'Diese_' -'Jahr_' -'both_' -'information_' -'against_' -'Länder_' -'part_' -'same_' -'last_' -'Bericht_' -'unsere_' -'global_' -'dann_' -'z_' -'through_' -'würde_' -'re_' -'6_' -'then_' -'sollte_' -'There_' -'jedoch_' -'hier_' -'high_' -'does_' -'end_' -'damit_' -'Im_' -'10_' -'seiner_' -'heute_' -'S_' -'United_' -'Und_' -'under_' -'social_' -'too_' -'ion_' -'7_' -'national_' -'order_' -'growth_' -'8_' -'example_' -'still_' -'see_' -'me_' -'le_' -'neue_' -'ation_' -'ohne_' -'free_' -'europäischen_' -'Parlament_' -'Land_' -'number_' -'That_' -'Mitgliedstaaten_' -'rights_' -'place_' -'könnte_' -'development_' -'area_' -'And_' -'within_' -'power_' -'her_' -'course_' -'room_' -'point_' -'fact_' -'before_' -'bereits_' -'used_' -'Frage_' -'’ _' -'neuen_' -'while_' -'denen_' -'far_' -'possible_' -'Entwicklung_' -'Ein_' -'20_' -'selbst_' -'wieder_' -'economy_' -' [[_' -'want_' -'la_' -'future_' -'sollten_' -'You_' -'zwei_' -'dazu_' -'Europäische_' -'say_' -'Regierung_' -'great_' -'already_' -' | _' -'without_' -'c_' -'set_' -'Aber_' -'less_' -'9_' -'large_' -'während_' -'human_' -'here_' -'! _' -'weil_' -'today_' -'jetzt_' -'ihren_' -'30_' -'ihr_' -'So_' -'view_' -'se_' -'machen_' -'wäre_' -'therefore_' -'cannot_' -'believe_' -'problem_' -'liegt_' -'wo_' -'since_' -'go_' -'Lage_' -' '_' -'u_' -'crisis_' -'three_' -'state_' -'per_' -'find_' -'few_' -'down_' -'America_' -'et_' -'Ländern_' -'viele_' -'services_' -'000_' -'..._' -'st_' -'process_' -'issue_' -'help_' -'Unternehmen_' -'trade_' -'including_' -'available_' -'15_' -'level_' -'case_' -'Maßnahmen_' -'viel_' -'know_' -'geht_' -'einige_' -'Eine_' -'means_' -'darauf_' -'denn_' -'dafür_' -'00_' -'next_' -'mich_' -'different_' -'Jahre_' -'seit_' -'city_' -'change_' -'areas_' -'rs_' -'real_' -'get_' -'problems_' -'When_' -'Staaten_' -'health_' -'el_' -'Politik_' -'E_' -'C_' -'   _' -'making_' -'best_' -'Mit_' -'Ihnen_' -'however_' -'clear_' -'better_' -'allem_' -'politischen_' -'lassen_' -'finden_' -'x_' -'why_' -'habe_' -'gut_' -'Frau_' -'ers_' -'Dies_' -'B_' -'ne_' -'D_' -'energy_' -'during_' -'become_' -'allen_' -'They_' -'service_' -'access_' -'waren_' -'ganz_' -'Japan_' -'2009_' -'unserer_' -'etwas_' -'daß_' -'Committee_' -'current_' -'wollen_' -'question_' -'doch_' -'stellen_' -'politische_' -'com_' -'back_' -'To_' -'yang_' -'particular_' -'small_' -'ob_' -'did_' -'day_' -'Rat_' -'think_' -'These_' -'Israel_' -'based_' -'bar_' -'ve_' -'interest_' -'debate_' -'common_' -'beim_' -'Doch_' -'Commissioner_' -'letzten_' -'each_' -'again_' -'bietet_' -'Germany_' -'At_' -'security_' -'nun_' -'measures_' -'business_' -'Zimmer_' -'situation_' -'schon_' -'put_' -'offers_' -'p_' -'markets_' -'Wie_' -'ts_' -'tion_' -'taken_' -'seinen_' -'military_' -'major_' -'Ihre_' -'ge_' -'come_' -'another_' -'Bereich_' -'Arbeit_' -'stay_' -'Iran_' -'rate_' -'provide_' -'Ende_' -'tun_' -'result_' -'rather_' -'law_' -'continue_' -'citizens_' -'ce_' -'50_' -'said_' -'action_' -'page_' -'might_' -'House_' -'term_' -'American_' -'2008_' -'worden_' -'recent_' -'given_' -'Unterstützung_' -'Internet_' -'Euro_' -'whether_' -'ies_' -'every_' -'Probleme_' -'|_' -'ment_' -'give_' -'einfach_' -'2000_' -'steht_' -'rooms_' -'governments_' -'ersten_' -'debt_' -'called_' -'Teil_' -'Auch_' -'besteht_' -'Problem_' -'sten_' -'proposal_' -'needs_' -'gen_' -'budget_' -'Bank_' -'12_' -'private_' -'ns_' -'Herrn_' -'weniger_' -'lich_' -'institutions_' -'full_' -'erhalten_' -'On_' -'New_' -'Fall_' -'11_' -'non_' -'P_' -'vote_' -'terms_' -'os_' -'issues_' -'f_' -'data_' -'away_' -'Nach_' -'sehen_' -'little_' -'line_' -'least_' -'further_' -'around_' -'always_' -'Ziel_' -'Sicherheit_' -'Als_' -'que_' -'million_' -'großen_' -'du_' -'ch_' -'France_' -'ter_' -'open_' -'eigenen_' -'Russia_' -'Rolle_' -'systems_' -'life_' -'insbesondere_' -'hatte_' -'group_' -'geben_' -'close_' -'World_' -'sagen_' -'hope_' -'With_' -'Art_' -'All_' -'zwar_' -'using_' -'quality_' -'policies_' -'main_' -'legal_' -'ern_' -'deren_' -'ons_' -'off_' -'located_' -'ab_' -'System_' -'Bürger_' -'money_' -'mir_' -'kommen_' -'bin_' -'age_' -'What_' -'Namen_' -'risk_' -'investment_' -'foreign_' -'drei_' -'ble_' -'allerdings_' -'Zusammenarbeit_' -'Wirtschaft_' -'He_' -'Bedeutung_' -'2005_' -'role_' -'position_' -'große_' -']] _' -'Was_' -'Informationen_' -'würden_' -'women_' -'möglich_' -'form_' -'certain_' -'T_' -'2006_' -'etwa_' -'einmal_' -'done_' -'German_' -'weiter_' -'sector_' -'agreement_' -'2007_' -'rules_' -'increase_' -'ihnen_' -'dan_' -'companies_' -'basis_' -'b_' -'unseren_' -'particularly_' -'members_' -'local_' -'führen_' -'di_' -'create_' -'century_' -'öffentlichen_' -'pro_' -'old_' -'necessary_' -'erreichen_' -'Er_' -'Deutschland_' -'Bei_' -'.”_' -'start_' -'second_' -'once_' -'davon_' -'ago_' -'O_' -'yet_' -'together_' -'ry_' -'going_' -'soll_' -'others_' -'könnten_' -'dort_' -'staff_' -'\u_' -'Für_' -' & _' -'stehen_' -'needed_' -'location_' -'ensure_' -'dabei_' -'conditions_' -'Millionen_' -'Community_' -'Auf_' -'2001_' -'weitere_' -'though_' -'rn_' -'often_' -'key_' -'ins_' -'alles_' -'short_' -'price_' -'meisten_' -'him_' -'fully_' -'especially_' -'September_' -'Fragen_' -'rates_' -'control_' -'central_' -'Weise_' -'Weg_' -'One_' -'low_' -'kein_' -'itself_' -'greater_' -'gegenüber_' -'capital_' -'Rahmen_' -'nt_' -'modern_' -'decision_' -'aller_' -' _' -'sogar_' -'cooperation_' -'Recht_' -'However_' -'innerhalb_' -'costs_' -'Vorschlag_' -'reform_' -'led_' -'food_' -'Zukunft_' -'Wachstum_' -'Stadt_' -'Indeed_' -'2004_' -' $_' -'wissen_' -'third_' -'region_' -'account_' -' ..._' -'leaders_' -'among_' -'Seite_' -'M_' -'Beispiel_' -'whole_' -'tax_' -'protection_' -'present_' -'ic_' -'early_' -'aid_' -'Um_' -'Europas_' -'100_' -'vielen_' -'schen_' -'name_' -'meine_' -'ischen_' -'income_' -'following_' -'excellent_' -'darüber_' -'children_' -'banks_' -'25_' -'welche_' -'several_' -'reason_' -'nen_' -'having_' -'UN_' -'Seiten_' -'weit_' -'progress_' -'centre_' -'Chinese_' -'wirklich_' -'really_' -'möchten_' -'macht_' -'kommt_' -'ja_' -'europäische_' -'value_' -'resources_' -'environment_' -'bringen_' -'bieten_' -'Region_' -'Möglichkeit_' -'16_' -'water_' -'something_' -'left_' -'internationalen_' -'home_' -'democratic_' -'Zusammenhang_' -'Grund_' -'Frauen_' -'2003_' -'sowohl_' -'include_' -'democracy_' -'daher_' -'weiterhin_' -'stellt_' -'states_' -'production_' -'kan_' -'fast_' -'Bush_' -'wirtschaftlichen_' -'single_' -'shall_' -'nden_' -'longer_' -'efforts_' -'Mrs_' -'India_' -'working_' -'until_' -'ty_' -'poor_' -'matter_' -'land_' -'force_' -'chen_' -'French_' -'society_' -'indem_' -'2002_' -'unterstützen_' -'regard_' -'offer_' -'nd_' -'nationalen_' -'list_' -'likely_' -'halten_' -'No_' -'Group_' -'Central_' -'period_' -'never_' -'meiner_' -'gentlemen_' -'entfernt_' -'difficult_' -'beiden_' -'Kinder_' -'18_' -'un_' -'prices_' -'look_' -'fiscal_' -'besonders_' -'ar_' -'approach_' -'Parlaments_' -'Mittel_' -'views_' -'verschiedenen_' -'standards_' -'results_' -'respect_' -'resolution_' -'research_' -'les_' -'industry_' -'developing_' -'cost_' -'L_' -'Iraq_' -'International_' -'G_' -'Dollar_' -' % _' -'wichtig_' -'special_' -'member_' -'hand_' -'created_' -'U_' -'N_' -'Investitionen_' -'40_' -'wirtschaftliche_' -'show_' -'nehmen_' -'management_' -'interests_' -'enough_' -'breakfast_' -'State_' -'24_' -'wenig_' -'proposals_' -'parties_' -'nichts_' -'ihm_' -'experience_' -'etc_' -'almost_' -'Vereinigten_' -'R_' -'Obama_' -'Leben_' -' ' -'transport_' -'taking_' -'remain_' -'programme_' -'play_' -'near_' -'general_' -'family_' -'erste_' -'aufgrund_' -'address_' -'Ziele_' -'Fraktion_' -'Daten_' -'side_' -'ren_' -'products_' -'men_' -'ihn_' -'history_' -'hinaus_' -'easy_' -'billion_' -'Service_' -'Richtlinie_' -'Krise_' -'Grundlage_' -'thus_' -'things_' -'site_' -'sei_' -'questions_' -'ous_' -'nuclear_' -'known_' -'ierung_' -'feel_' -'call_' -'building_' -'Thema_' -'Russland_' -'Regierungen_' -'Kollegen_' -'towards_' -'seinem_' -'ner_' -'iert_' -'ieren_' -'hin_' -'dessen_' -'befindet_' -'Verfügung_' -'Landes_' -'An_' -'wish_' -'technology_' -'rt_' -'proposed_' -'natürlich_' -'keinen_' -'demand_' -'darin_' -'Lösung_' -'Kommissar_' -'F_' -'14_' -'workers_' -'themselves_' -'solution_' -'projects_' -'pay_' -'months_' -'ity_' -'Programm_' -'Macht_' -'version_' -'regional_' -'program_' -'past_' -'ness_' -'lichen_' -'ische_' -'hard_' -'ever_' -'eren_' -'ask_' -'adopted_' -'Gemeinschaft_' -'East_' -'By_' -'60_' -'strategy_' -'points_' -'personal_' -'lead_' -'ive_' -'ihrem_' -'ia_' -'higher_' -'gute_' -'directive_' -'cultural_' -'beispielsweise_' -'agree_' -'Man_' -'welcome_' -'ted_' -'software_' -'sicher_' -'serious_' -'sche_' -'liche_' -'levels_' -'gilt_' -'gehen_' -'found_' -'either_' -'effective_' -'education_' -'above_' -'Situation_' -'After_' -'19_' -'vielleicht_' -'various_' -'specific_' -'schnell_' -'schaffen_' -'related_' -'freedom_' -'deal_' -'che_' -'besser_' -'bedeutet_' -'South_' -'Institutionen_' -'21_' -'rapporteur_' -'ls_' -'enjoy_' -'economies_' -'ebenfalls_' -'direkt_' -'bleiben_' -'big_' -'authorities_' -'allow_' -'November_' -'Meinung_' -'Even_' -'Ansicht_' -'. _' -'à_' -'user_' -'seen_' -'remains_' -'reforms_' -'ra_' -'provided_' -'plan_' -'opportunity_' -'ladies_' -'keit_' -'impact_' -'groups_' -'framework_' -'ens_' -'comes_' -'Schutz_' -'th_' -'strong_' -'simply_' -'significant_' -'quite_' -'let_' -'leading_' -'language_' -'em_' -'concerns_' -'climate_' -'behalf_' -'X_' -'Restaurant_' -'Kosten_' -'Dieser_' -'Da_' -'Africa_' -'town_' -'ting_' -'party_' -'pages_' -'makes_' -'globalen_' -'due_' -'Our_' -'Hilfe_' -'Demokratie_' -'2010_' -'upon_' -'seems_' -'relations_' -'peace_' -'online_' -'oil_' -'forward_' -'effect_' -'W_' -'Treaty_' -'Of_' -'Menschenrechte_' -'Form_' -'zurück_' -'true_' -'total_' -'subject_' -'safety_' -'later_' -'jeder_' -'growing_' -'face_' -'appropriate_' -'amendments_' -'West_' -'Minister_' -'Geschichte_' -'Alle_' -';_' -'young_' -'top_' -'solche_' -'she_' -'recently_' -'kind_' -'internationale_' -'individual_' -'ig_' -'euro_' -'environmental_' -'ebenso_' -'daran_' -'concerned_' -'Ukraine_' -'Strategie_' -'Madam_' -'Gesellschaft_' -'GDP_' -'Dieses_' -'w_' -'spending_' -'share_' -'positive_' -'opinion_' -'ma_' -'light_' -'führt_' -'eurozone_' -'ert_' -'community_' -'care_' -'attention_' -'Some_' -'Rates_' -'Milliarden_' -'EUR_' -'sure_' -'success_' -'son_' -'sollen_' -'sen_' -'outside_' -'km_' -'improve_' -'huge_' -'half_' -'genau_' -'funds_' -'four_' -'content_' -'changes_' -'addition_' -'Western_' -'é_' -'words_' -'traditional_' -'text_' -'station_' -'setzen_' -'rise_' -'nächsten_' -'nutzen_' -'nothing_' -'ions_' -'idea_' -'gab_' -'fundamental_' -'bekannt_' -'V_' -'T' -'Krieg_' -'Italy_' -'Beziehungen_' -'stand_' -'legislation_' -'ging_' -'field_' -'currently_' -'art_' -'Turkey_' -'Paris_' -'Mitglieder_' -'London_' -'Ihr_' -'Hotels_' -'Geld_' -'Frankreich_' -'Bevölkerung_' -'v_' -'times_' -'thank_' -'sense_' -'restaurant_' -'required_' -'population_' -'person_' -'house_' -'held_' -'heart_' -'gemacht_' -'fall_' -'developed_' -'deutlich_' -'bzw_' -'amerikanischen_' -'Während_' -'Members_' -'British_' -'"_' -' . _' -'ur_' -'minutes_' -'living_' -'inflation_' -'ie_' -'forces_' -'facilities_' -'arbeiten_' -'actually_' -'Zugang_' -'UK_' -'Moreover_' -'M' -'Dienstleistungen_' -'Bedingungen_' -'Auswirkungen_' -'Aus_' -'"._' -'sea_' -'run_' -'product_' -'principle_' -'nämlich_' -'model_' -'majority_' -'ll_' -'jobs_' -'hätte_' -'hours_' -'gesagt_' -'erreicht_' -'date_' -'certainly_' -'alten_' -'across_' -'statt_' -'sch_' -'reduce_' -'potential_' -'los_' -'indeed_' -'hatten_' -'game_' -'exchange_' -'employment_' -'einigen_' -'deshalb_' -'days_' -'cases_' -'benefits_' -'Windows_' -'Schritt_' -'National_' -'Markt_' -'Greece_' -'Bezug_' -'1999_' -')_' -'wichtige_' -'whose_' -'via_' -'unterstützt_' -'unserem_' -'tes_' -'stability_' -'similar_' -'range_' -'keyword_' -'igen_' -'directly_' -'consider_' -'company_' -'beautiful_' -'along_' -'air_' -'Umsetzung_' -'Interesse_' -'Erfolg_' -'Entscheidung_' -'Asia_' -',” _' -'si_' -'self_' -'regulation_' -'pool_' -'parts_' -'natural_' -'media_' -'gerade_' -'five_' -'enden_' -'elections_' -'ca_' -'application_' -'anti_' -'TV_' -'Office_' -'Liste_' -'Ihrer_' -'Haus_' -'Berlin_' -'understand_' -'lot_' -'lediglich_' -'job_' -'ende_' -'emerging_' -'War_' -'Tatsache_' -'Personen_' -'April_' -' : _' -'size_' -'prevent_' -'opportunities_' -'nde_' -'na_' -'mean_' -'lange_' -'involved_' -'cy_' -'conflict_' -'co_' -'capacity_' -'bring_' -'bleibt_' -'bed_' -'achieve_' -'While_' -'Tag_' -'Nähe_' -'January_' -'Ergebnis_' -'Ebene_' -'Debatte_' -'&_' -'wide_' -'thing_' -'sprechen_' -'rule_' -'regions_' -'project_' -'meeting_' -'handelt_' -'eher_' -'complete_' -'body_' -'Deshalb_' -'13_' -'violence_' -'verwendet_' -'verhindern_' -'table_' -'ss_' -'response_' -'politik_' -'je_' -'heißt_' -'ger_' -'focus_' -'dürfen_' -'competition_' -'clearly_' -'check_' -'car_' -'ben_' -'ant_' -'add_' -'Verantwortung_' -'Präsidenten_' -'La_' -'Jahres_' -'Interessen_' -'Government_' -'Afghanistan_' -'17_' -' ( _' -'unser_' -'scheint_' -'move_' -'lässt_' -'jeden_' -'increasingly_' -'image_' -'großer_' -'gleichzeitig_' -'gemeinsame_' -'features_' -'existing_' -'everything_' -'event_' -'ent_' -'ds_' -'computer_' -'clean_' -'civil_' -'bereit_' -'amount_' -'administration_' -'Park_' -'Irak_' -'Gewalt_' -'ways_' -'ste_' -'sozialen_' -'provides_' -'popular_' -'obwohl_' -'nie_' -'negotiations_' -'nature_' -'fight_' -'direct_' -'del_' -'culture_' -'center_' -'came_' -'Z' -'Kampf_' -'II_' -'G' -'23_' -'verfügt_' -'training_' -'six_' -'road_' -'night_' -'network_' -'live_' -'internal_' -'instead_' -'file_' -'decisions_' -'betrifft_' -'balance_' -'ary_' -'Türkei_' -'North_' -'Hier_' -'Bereichen_' -'Banken_' -'Aussprache_' -' -_' -'ya_' -'space_' -'meet_' -'keep_' -'extremely_' -'effects_' -'ck_' -'chinesischen_' -'below_' -'activities_' -'Zu_' -'San_' -'Partei_' -'Möglichkeiten_' -'I' -'Einsatz_' -'BIP_' -'Allerdings_' -'", _' -' ''_' -'weiß_' -'weiteren_' -'week_' -'wahrscheinlich_' -'values_' -'unternehmen_' -'simple_' -'return_' -'rest_' -'perhaps_' -'notwendig_' -'net_' -'infrastructure_' -'increased_' -'included_' -'il_' -'contains_' -'commitment_' -'besten_' -'Staat_' -'Spain_' -'Richtung_' -'Ort_' -'Booking_' -'   – _' -'vergangenen_' -'turn_' -'try_' -'tragen_' -'toward_' -'took_' -'tatsächlich_' -'step_' -'reviews_' -'responsible_' -'poverty_' -'negara_' -'möglicherweise_' -'late_' -'importance_' -'ideal_' -'hätten_' -'hohen_' -'former_' -'favour_' -'essential_' -'doing_' -'design_' -'customers_' -'currency_' -'] _' -'Software_' -'80_' -'“, _' -'zusammen_' -'ziehen_' -'wären_' -'spielen_' -'soziale_' -'performance_' -'oft_' -'moment_' -'lack_' -'kleinen_' -'klar_' -'fünf_' -'erst_' -'derzeit_' -'dar_' -'brauchen_' -'befinden_' -'beach_' -'ally_' -'Wert_' -'Party_' -'Großbritannien_' -'Grenzen_' -'Chinas_' -' ‘_' -'wichtigen_' -'weltweit_' -'wegen_' -'responsibility_' -'require_' -'reasons_' -'ngen_' -'negative_' -'liegen_' -'integration_' -'ings_' -'ian_' -'hen_' -'größten_' -'geführt_' -'external_' -'develop_' -'credit_' -'bank_' -'York_' -'Vor_' -'Today_' -'Preis_' -'Außerdem_' -'-, _' -'ta_' -'successful_' -'red_' -'protect_' -'president_' -'places_' -'largest_' -'implementation_' -'heit_' -'friendly_' -'double_' -'decades_' -'darf_' -'challenges_' -'Von_' -'Tatsächlich_' -'S' -'O' -'My_' -'Aufgabe_' -'Am_' -'ure_' -'sub_' -'stark_' -'soon_' -'rich_' -'pressure_' -'option_' -'neu_' -'jedes_' -'ien_' -'glaube_' -'events_' -'established_' -'despite_' -'comfortable_' -'cause_' -'built_' -'board_' -'benefit_' -'although_' -'Wahl_' -'Verfahren_' -'Regionen_' -'May_' -'Mal_' -'K_' -'Ihren_' -'Enterprise_' -'Britain_' -'Behörden_' -'Ausschuss_' -'Amerika_' -'ändern_' -'verschiedene_' -'takes_' -'strategic_' -'steps_' -'status_' -'len_' -'labor_' -'guarantee_' -'gehört_' -'einzelnen_' -'designed_' -'ces_' -'card_' -'cal_' -'behind_' -'agenda_' -'Website_' -'Verbindung_' -'Russian_' -'Rechte_' -'Presidency_' -'Initiative_' -'F' -'Court_' -'City_' -'Abstimmung_' -'�_' -'wichtigsten_' -'walk_' -'video_' -'type_' -'terrorism_' -'stop_' -'standard_' -'schließlich_' -'risks_' -'regime_' -'post_' -'nya_' -'nor_' -'internet_' -'gehören_' -'ermöglichen_' -'bisher_' -'beginning_' -'became_' -'Zum_' -'Verfassung_' -'Uhr_' -'Spanien_' -'Platz_' -'First_' -'Fed_' -'D' -'Bemühungen_' -'Armut_' -'. - (_' -'zeit_' -'zeigen_' -'wohl_' -'visit_' -'verbunden_' -'threat_' -'thought_' -'receive_' -'reach_' -'probably_' -'practice_' -'official_' -'nice_' -'mal_' -'lower_' -'looking_' -'ler_' -'gemeinsam_' -'gar_' -'findet_' -'fen_' -'complex_' -'committee_' -'closed_' -'angesichts_' -'ability_' -'Zahl_' -'Yet_' -'Vorschläge_' -'Version_' -'Verhandlungen_' -'Politiker_' -'More_' -'Mehrheit_' -'How_' -'Führung_' -'Eurozone_' -'Ergebnisse_' -'Bar_' -'B' -'Ansatz_' -'70_' -'200_' -'ze_' -'wobei_' -'study_' -'started_' -'rund_' -'reality_' -'purpose_' -'programs_' -'plans_' -'music_' -'monetary_' -'limited_' -'konnte_' -'ke_' -'isch_' -'highly_' -'guests_' -'falls_' -'enable_' -'confidence_' -'bad_' -'according_' -'accept_' -'V' -'Reformen_' -'Prozess_' -'Nationen_' -'NATO_' -'Kunden_' -'K' -'Indien_' -'Handel_' -'From_' -'Druck_' -'Dabei_' -'Antwort_' -'..." _' -' / _' -'vier_' -'sustainable_' -'style_' -'shown_' -'raise_' -'previous_' -'matters_' -'lives_' -'ken_' -'industrial_' -'helfen_' -'creating_' -'context_' -'consumers_' -'consequences_' -'con_' -'basic_' -'answer_' -'Prozent_' -'Only_' -'June_' -'English_' -'Development_' -', “_' -'zehn_' -'werde_' -'unique_' -'ton_' -'setting_' -'seines_' -'presented_' -'ors_' -'lost_' -'konnten_' -'knowledge_' -'ihres_' -'gegeben_' -'gebracht_' -'gas_' -'erforderlich_' -'effort_' -'creation_' -'cht_' -'choose_' -'caused_' -'categories_' -'bus_' -'beyond_' -'asked_' -'active_' -'Wort_' -'Seit_' -'Punkt_' -'Now_' -'Gruppe_' -'Entscheidungen_' -'Berichterstatter_' -'Artikel_' -'Arab_' -'öffentliche_' -'zen_' -'zeigt_' -'ties_' -'seem_' -'saying_' -'politicians_' -'partner_' -'note_' -'nahe_' -'latest_' -'ks_' -'hohe_' -'guten_' -'gewährleisten_' -'gesamten_' -'finance_' -'failure_' -'evidence_' -'entwickeln_' -'enthält_' -'darum_' -'dadurch_' -'challenge_' -'alone_' -'act_' -'Spiel_' -'Putin_' -'P' -'Hinblick_' -'General_' -'Gelegenheit_' -'Gefahr_' -'Gebiet_' -'Förderung_' -'Europeans_' -'Darüber_' -'Dank_' -'Damit_' -'Beginn_' -'Barcelona_' -'August_' -'Abkommen_' -'Öffentlichkeit_' -'verwenden_' -'unemployment_' -'treatment_' -'source_' -'sound_' -'sometimes_' -'solutions_' -'quickly_' -'programmes_' -'please_' -'objective_' -'lines_' -'larger_' -'ker_' -'guest_' -'damage_' -'build_' -'aware_' -'average_' -'aktuellen_' -'agricultural_' -'achieved_' -'University_' -'St_' -'Schlusselwortern_' -'Regeln_' -'Produkte_' -'Middle_' -'March_' -'H' -'Datei_' -'CD_' -'Bildung_' -'500_' -'1990_' -' " _' -'“._' -'äußerst_' -'zone_' -'ves_' -'v' -'throughout_' -'t' -'ssen_' -'request_' -'politics_' -'movement_' -'mentioned_' -'leben_' -'jede_' -'independent_' -'gleichen_' -'gleich_' -'ganzen_' -'fragen_' -'fest_' -'fair_' -'failed_' -'ermöglicht_' -'equal_' -'enlargement_' -'distribution_' -'direction_' -'ding_' -'danken_' -'coming_' -'choice_' -'cally_' -'Terrorismus_' -'Palestinian_' -'Minuten_' -'IMF_' -'Herren_' -'Funktion_' -'Anfang_' -'Abgeordneten_' -'zed_' -'völlig_' -'verstehen_' -'test_' -'supported_' -'shows_' -'setzt_' -'recht_' -'procedure_' -'principles_' -'lt_' -'lose_' -'ini_' -'includes_' -'ht_' -'hold_' -'gestellt_' -'gemeinsamen_' -'final_' -'fear_' -'e' -'domestic_' -'deficit_' -'consumer_' -'cher_' -'charge_' -'book_' -'base_' -'anything_' -'akan_' -'advanced_' -'X' -'W' -'Since_' -'Ressourcen_' -'Notwendigkeit_' -'Natürlich_' -'Kraft_' -'Korea_' -'Kontrolle_' -'Israeli_' -'Hand_' -'Fortschritte_' -'Erweiterung_' -'Debian_' -'Ausdruck_' -'Aufmerksamkeit_' -'übernachten_' -'x' -'web_' -'verfügen_' -'submitted_' -'speed_' -'reached_' -'produce_' -'perfect_' -'objectives_' -'mind_' -'ments_' -'initiative_' -'i' -'hoffe_' -'ground_' -'goods_' -'giving_' -'famous_' -'fallen_' -'entwickelt_' -'don_' -'considered_' -'class_' -'ck' -'cities_' -'bekommen_' -'additional_' -'accommodation_' -'Y_' -'Wann_' -'Viele_' -'Tage_' -'Security_' -'Rights_' -'Many_' -'Lisbon_' -'Folgen_' -'Federal_' -'E' -'Damen_' -'Blick_' -'Bild_' -'Bekämpfung_' -'Ausgaben_' -'Anwendung_' -'Angesichts_' -'Americans_' -'90_' -'45_' -'27_' -'22_' -'%._' -' % _' -'zudem_' -'wrong_' -'worked_' -'weder_' -'ut_' -'untuk_' -'tell_' -'später_' -'speak_' -'situated_' -'richtig_' -'restaurants_' -'res_' -'produced_' -'p' -'news_' -'m' -'ige_' -'häufig_' -'größte_' -'globale_' -'est_' -'enthalten_' -'emissions_' -'decided_' -'death_' -'completely_' -'brought_' -'au_' -'annual_' -'added_' -'Veränderungen_' -'Umwelt_' -'Services_' -'Schaffung_' -'Reihe_' -'Reform_' -'Instead_' -'Here_' -'Gesamt' -'Fund_' -'Finally_' -'Einfluss_' -'Durch_' -'December_' -'Dazu_' -'1791_' -'Änderungsantrag_' -'zahlen_' -'weapons_' -'voted_' -'technologies_' -'target_' -'secure_' -'requirements_' -'partners_' -'package_' -'options_' -'massive_' -'ism_' -'increasing_' -'goal_' -'files_' -'extent_' -'erung_' -'erster_' -'eigene_' -'contact_' -'consumption_' -'ber_' -'allows_' -'aim_' -'agreements_' -'Zentrum_' -'Text_' -'Schließlich_' -'Qualität_' -'Mitglied_' -'L' -'Kosovo_' -'Its_' -'Frieden_' -'During_' -'Chance_' -'300_' -'zweite_' -'won_' -'trotz_' -'tions_' -'technical_' -'students_' -'send_' -'prepared_' -'original_' -'mobile_' -'mail_' -'item_' -'function_' -'front_' -'f' -'extra_' -'entire_' -'election_' -'eben_' -'dialogue_' -'critical_' -'changed_' -'ang_' -'allowed_' -'Wettbewerb_' -'Verwendung_' -'So' -'Nutzung_' -'Nations_' -'Märkte_' -'Kultur_' -'Jahrhundert_' -'Italien_' -'Gästebewertungen_' -'Furthermore_' -'Erklärung_' -'Daher_' -'DE_' -'Beitrag_' -': „_' -'28_' -'1781_' -'zweiten_' -'wenige_' -'website_' -'wealth_' -'voll_' -'versuchen_' -'team_' -'supply_' -'stärker_' -'sorgen_' -'solidarity_' -'scale_' -'ring_' -'providing_' -'players_' -'paid_' -'opposition_' -'ling_' -'lang_' -'kam_' -'influence_' -'ier_' -'geworden_' -'genug_' -'gain_' -'ft_' -'forms_' -'follow_' -'erte_' -'erklären_' -'einschließlich_' -'distance_' -'concern_' -'concept_' -'carried_' -'campaign_' -'borders_' -'began_' -'ate_' -'aspects_' -'allein_' -'[_' -'WTO_' -'Server_' -'Programme_' -'Meer_' -'July_' -'Forschung_' -'Fehler_' -'Familie_' -'Ausschusses_' -'Although_' -'African_' -'? _' -': ' -'story_' -'stage_' -'server_' -'officials_' -'office_' -'offered_' -'nis_' -'legen_' -'leave_' -'jene_' -'insgesamt_' -'immigration_' -'hinter_' -'genannten_' -'fördern_' -'ful_' -'erwartet_' -'erwarten_' -'erneut_' -'doubt_' -'digital_' -'dari_' -'concerning_' -'bitte_' -'bevor_' -'apartment_' -'anderer_' -'Verbraucher_' -'Unsere_' -'Portugal_' -'Person_' -'Pakistan_' -'Organisation_' -'Opfer_' -'Ko' -'Idee_' -'H_' -'Griechenland_' -'Gesundheit_' -'EN_' -'DVD_' -'C' -' [_' -' ) _' -'zumindest_' -'z' -'writing_' -'worldwide_' -'verbessern_' -'uses_' -'users_' -'treffen_' -'tidak_' -'sed_' -'search_' -'save_' -'reports_' -'quiet_' -'professional_' -'privaten_' -'parking_' -'month_' -'map_' -'kosten_' -'jedem_' -'historical_' -'head_' -'gt_' -'generation_' -'funding_' -'einzige_' -'disease_' -'d' -'construction_' -'connection_' -'committed_' -'code_' -'child_' -'airport_' -'Werte_' -'Wasser_' -'Vergangenheit_' -'Unter_' -'Themen_' -'Stunden_' -'Prime_' -'Obwohl_' -'Most_' -'Mediterranean_' -'Linux_' -'Le_' -'Italian_' -'Information_' -'Herausforderung_' -'Flughafen_' -'Dialog_' -'Anti' -'Afrika_' -'übernehmen_' -'Änderungsanträge_' -'ying_' -'usually_' -'tic_' -'seek_' -'practical_' -'nimmt_' -'mus_' -'ms_' -'morning_' -'meinen_' -'material_' -'links_' -'kleine_' -'ja' -'implemented_' -'hoch_' -'helpful_' -'glauben_' -'getan_' -'geschaffen_' -'fishing_' -'erklärt_' -'effectively_' -'dollar_' -'deutschen_' -'demokratischen_' -'demands_' -'decline_' -'communication_' -'ch' -'benötigen_' -'applied_' -'angenommen_' -'amerikanische_' -'alternative_' -'Zeitpunkt_' -'Wahlen_' -'Tat_' -'Stelle_' -'Room_' -'Risiken_' -'People_' -'Parteien_' -'Lösungen_' -'Let_' -'Industrie_' -'Ihrem_' -'Hamas_' -'Fällen_' -'Frühstück_' -'Erstens_' -'Einkommen_' -'Dinge_' -'Dezember_' -'Center_' -'Austria_' -'Affairs_' -': "_' -'   ._' -'   . _' -'zunehmend_' -'zentrale_' -'works_' -'warum_' -'wants_' -'wanted_' -'vermeiden_' -'ver_' -'statement_' -'served_' -'series_' -'safe_' -'relationship_' -'provisions_' -'police_' -'neuer_' -'neben_' -'nce_' -'leadership_' -'leader_' -'join_' -'illegal_' -'gewesen_' -'ess_' -'eigentlich_' -'cs_' -'cken_' -'businesses_' -'border_' -'avoid_' -'authority_' -'applications_' -'appears_' -'agreed_' -'actions_' -'Zudem_' -'Schwierigkeiten_' -'Republic_' -'Präsidentin_' -'Please_' -'Juni_' -'J' -'IT_' -'Höhe_' -'Heute_' -'Greek_' -'Google_' -'George_' -'Egypt_' -'Economic_' -'Convention_' -'Amerikas_' -'2013_' -'1998_' -'160_' -'.  _' -' "..._' -'tra' -'structural_' -'star_' -'stable_' -'speech_' -'somit_' -'solchen_' -'schützen_' -'regards_' -'received_' -'read_' -'property_' -'powerful_' -'politischer_' -'path_' -'overall_' -'nearly_' -'n' -'method_' -'meinem_' -'lle_' -'legislative_' -'ine_' -'igkeit_' -'ideas_' -'getting_' -'folgt_' -'everyone_' -'establish_' -'ell_' -'drive_' -'cut_' -'competitive_' -'compared_' -'chinesische_' -'bit_' -'beitragen_' -'bare_' -'b' -'ations_' -'Zeiten_' -'Tagen_' -'Such_' -'Station_' -'Sozial' -'R' -'Position_' -'Nachfrage_' -'Management_' -'Latin_' -'Kingdom_' -'Integration_' -'Herzen_' -'Globalisierung_' -'Financial_' -'Club_' -'Bestimmungen_' -'Aktivitäten_' -'31_' -'3' -''' _' -'с' -'Änderungen_' -'zahlreiche_' -'word_' -'variety_' -'union_' -'trading_' -'talk_' -'serve_' -'rising_' -'requires_' -'reducing_' -'reduced_' -'mehrere_' -'leicht_' -'jüngsten_' -'joint_' -'instruments_' -'immediately_' -'ierte_' -'hinsichtlich_' -'geschlossen_' -'folgen_' -'erhöhen_' -'ere_' -'equipped_' -'elsewhere_' -'efficient_' -'durchgeführt_' -'discussion_' -'difference_' -'developments_' -'comprehensive_' -'bringt_' -'bewusst_' -'beide_' -'attacks_' -'anders_' -'Vertrauen_' -'Revolution_' -'Plan_' -'PC_' -'Konferenz_' -'Japanese_' -'Ireland_' -'Great_' -'Centre_' -'CO2_' -'Bitte_' -'Anzahl_' -'.' -'wichtiger_' -'weltweiten_' -'types_' -'train_' -'tools_' -'thousands_' -'suggest_' -'stock_' -'sectors_' -'school_' -'sagte_' -'representatives_' -'reichen_' -'promote_' -'productivity_' -'priority_' -'possibility_' -'park_' -'nationale_' -'mit' -'message_' -'medical_' -'las_' -'instrument_' -'initiatives_' -'ierten_' -'ial_' -'genommen_' -'ga_' -'frei_' -'farmers_' -'expected_' -'elements_' -'elected_' -'easily_' -'degree_' -'deficits_' -'chance_' -'bestehen_' -'ausgestattet_' -'attack_' -'ated_' -'affected_' -'Woche_' -'Web_' -'Vertrag_' -'Tu' -'Syria_' -'Stabilität_' -'Pro' -'Preise_' -'Policy_' -'Nicht_' -'Neu' -'März_' -'Microsoft_' -'Markt' -'Mail_' -'Lissabon_' -'Land' -'Jo' -'His_' -'Global_' -'Finanz' -'Energie_' -'Design_' -'Constitution_' -'Brazil_' -'Besuch_' -'Bereiche_' -'Bad_' -'A' -'. ' -'whom_' -'ums_' -'ue_' -'tät_' -'turned_' -'relativ_' -'refugees_' -'reduction_' -'played_' -'para_' -'nächste_' -'ning_' -'ni_' -'middle_' -'mein_' -'letzte_' -'leider_' -'kaum_' -'k' -'ismus_' -'institutional_' -'forced_' -'expect_' -'erfolgreich_' -'enter_' -'diejenigen_' -'crucial_' -'commercial_' -'circumstances_' -'carry_' -'becoming_' -'bald_' -'aufgenommen_' -'activity_' -'Why_' -'Vereinten_' -'Verbesserung_' -'Technologie_' -'Te' -'Systems_' -'Standards_' -'Site_' -'Personal_' -'Osten_' -'Oktober_' -'October_' -'Not_' -'Küche_' -'Just_' -'Infrastruktur_' -'High_' -'Guest_' -'Grand_' -'Freiheit_' -'Free_' -'Finanzierung_' -'Directive_' -'CA' -'Auffassung_' -'According_' -'35_' -'29_' -'1980_' -'., _' -'* _' -')' -'® _' -'y' -'went_' -'travel_' -'ten' -'task_' -'sieht_' -'ship_' -'review_' -'religious_' -'relevant_' -'record_' -'procedures_' -'precisely_' -'pleased_' -'paar_' -'minute_' -'minister_' -'mention_' -'maintain_' -'leisten_' -'jeweiligen_' -'island_' -'investors_' -'improving_' -'hour_' -'hotels_' -'h' -'größere_' -'gesamte_' -'gekommen_' -'firms_' -'ence_' -'dringend_' -'dangerous_' -'conference_' -'colleagues_' -'c' -'break_' -'betrachtet_' -'bereich_' -'apply_' -'ance_' -'akzeptieren_' -'Währung_' -'Waffen_' -'Umgebung_' -'Trade_' -'Therefore_' -'Star_' -'Sicht_' -'N' -'IWF_' -'England_' -'Einführung_' -'Do_' -'Conference_' -'Co' -'Auswahl_' -'Asien_' -'Arbeitnehmer_' -'4' -', ' -'). _' -'zusätzliche_' -'ze' -'written_' -'white_' -'weise_' -'walking_' -'unbedingt_' -'trust_' -'tor_' -'tabled_' -'sts_' -'sse_' -'sign_' -'schaft_' -'sa_' -'s' -'round_' -'reserves_' -'regulations_' -'raised_' -'presence_' -'ped_' -'organisation_' -'neither_' -'namely_' -'mag_' -'länger_' -'ku' -'justice_' -'holiday_' -'historischen_' -'hands_' -'gives_' -'genießen_' -'ganze_' -'feature_' -'facing_' -'equipment_' -'draw_' -'documents_' -'denke_' -'deine_' -'boost_' -'banking_' -'attempt_' -'atmosphere_' -'assistance_' -'aimed_' -'agriculture_' -'advantage_' -'Verordnung_' -'Transparenz_' -'Tagesordnung_' -'Spanish_' -'Sorge_' -'Social_' -'Secondly_' -'Sea_' -'Rooms_' -'Robert_' -'Restaurants_' -'Nur_' -'Mai_' -'Linie_' -'Gründen_' -'Erfahrung_' -'Den_' -'Code_' -'Asian_' -'Also_' -'2' -'.)_' -'--_' -'weeks_' -'voting_' -'votes_' -'unterschiedlichen_' -'trying_' -'stets_' -'sm_' -'shift_' -'section_' -'sechs_' -'recovery_' -'programm_' -'pro' -'press_' -'pre_' -'phone_' -'ory_' -'oben_' -'networks_' -'ned_' -'nations_' -'nation_' -'modified_' -'merely_' -'membership_' -'meines_' -'lösen_' -'ley_' -'largely_' -'keiten_' -'kannst_' -'implement_' -'historic_' -'happen_' -'gs_' -'grounds_' -'goes_' -'gesetzt_' -'friends_' -'fort' -'floor_' -'eten_' -'establishment_' -'erkennen_' -'erfordert_' -'efficiency_' -'draft_' -'der' -'daily_' -'conclusion_' -'ches_' -'changing_' -'carbon_' -'buy_' -'burden_' -'bathroom_' -'assessment_' -'Wettbewerbsfähigkeit_' -'Schulden_' -'Rezession_' -'Regierungs' -'Raum_' -'Punkte_' -'Per' -'Museum_' -'Monaten_' -'Methode_' -'Jahrhunderts_' -'Islam_' -'Inter' -'Innovation_' -'Human_' -'Gruppen_' -'Groß' -'Europäer_' -'Diskussion_' -'Both_' -'Bedrohung_' -'Arbeitslosigkeit_' -'Al_' -'Airport_' -'8' -'2012_' -'Änderung_' -'zuletzt_' -'ys_' -'unten_' -'sta' -'sing_' -'ses_' -'scientific_' -'schwierig_' -'running_' -'rten_' -'regarding_' -'plus_' -'plant_' -'participation_' -'output_' -'normal_' -'nbsp_' -'mission_' -'lag_' -'ko' -'j_' -'ität_' -'innovation_' -'innen_' -'improved_' -'impossible_' -'hält_' -'hol' -'haus_' -'gezeigt_' -'ges_' -'gen' -'financing_' -'fe_' -'express_' -'export_' -'entsprechende_' -'ei_' -'deep_' -'decade_' -'contribution_' -'considerable_' -'competitiveness_' -'bodies_' -'bilden_' -'begin_' -'außerhalb_' -'Zweitens_' -'Zweifel_' -'Wirtschaftswachstum_' -'Wer_' -'Warum_' -'Vorteile_' -'Unterkategorien_' -'She_' -'Resort_' -'Republik_' -'Ra' -'Projekt_' -'Produktion_' -'Partner_' -'No' -'Mitte_' -'Lo' -'Investoren_' -'Forum_' -'Erfahrungen_' -'Energie' -'Einigung_' -'Du' -'Article_' -'Angebot_' -'... _' -'überzeugt_' -'ß_' -'zing_' -'za' -'vision_' -'versucht_' -'up' -'treten_' -'transparent_' -'told_' -'ti' -'spend_' -'speaking_' -'sites_' -'shopping_' -'sh_' -'screen_' -'says_' -'refer_' -'reading_' -'rd_' -'raum_' -'post' -'policymakers_' -'outcome_' -'operations_' -'operation_' -'opening_' -'ol' -'nach' -'multi_' -'mass_' -'manufacturing_' -'lies_' -'king_' -'ir_' -'intended_' -'insurance_' -'hu' -'hin' -'highest_' -'happened_' -'handeln_' -'gewinnen_' -'film_' -'families_' -'exports_' -'erfüllen_' -'ellen_' -'easier_' -'document_' -'derartige_' -'defense_' -'darstellt_' -'darstellen_' -'controls_' -'congratulate_' -'compromise_' -'clients_' -'braucht_' -'betrachten_' -'bestimmten_' -'bar' -'au' -'appear_' -'ans_' -'ale_' -'addressed_' -'Westen_' -'Welt' -'Wasser' -'Vielfalt_' -'Technologien_' -'Su' -'Street_' -'Spa_' -'RE' -'Public_' -'Privat' -'Poland_' -'Online_' -'Musik_' -'Kommissarin_' -'Kolleginnen_' -'John_' -'Inflation_' -'Handels' -'Folge_' -'Erholung_' -'ECB_' -'Da' -'Costa_' -'Because_' -'A5_' -'26_' -'2011_' -'überhaupt_' -'überall_' -'ßen_' -'zunächst_' -'ya' -'wählen_' -'wing_' -'wider_' -'waste_' -'vital_' -'victims_' -'useful_' -'urban_' -'theory_' -'structure_' -'ster_' -'staatlichen_' -'schwer_' -'saw_' -'sales_' -'relation_' -'rapidly_' -'profitieren_' -'primary_' -'presidency_' -'pre' -'powers_' -'planning_' -'offen_' -'numerous_' -'neues_' -'muß_' -'moral_' -'mainly_' -'lagen_' -'kurz_' -'ka_' -'investments_' -'inter' -'innovative_' -'heard_' -'gern_' -'generally_' -'gelegen_' -'fund_' -'freien_' -'finally_' -'establishing_' -'entspricht_' -'entscheiden_' -'eiten_' -'eindeutig_' -'details_' -'desire_' -'dengan_' -'core_' -'calls_' -'bestimmte_' -'bessere_' -'analysis_' -'amendment_' -'alt_' -'aktuelle_' -']], _' -'Zentralbank_' -'Vorschriften_' -'Volkswirtschaften_' -'Unser_' -'Tri' -'Time_' -'Team_' -'Strand_' -'Stimme_' -'Sinne_' -'Sicherheits' -'Sektor_' -'See' -'Schritte_' -'Reserve_' -'Re' -'Maße_' -'Juli_' -'Installation_' -'Herausforderungen_' -'Haushalts' -'Euro' -'En' -'Despite_' -'Dateien_' -'Congress_' -'Bundes' -'Brexit_' -'Brasilien_' -'Berichte_' -'Benutzer_' -'Arbeitsplätze_' -'Anteil_' -'Annahme_' -'Angelegenheiten_' -'Amsterdam_' -'Amendment_' -'worth_' -'worse_' -'vorgeschlagen_' -'transfer_' -'tool_' -'territory_' -'taxes_' -'steigen_' -'stated_' -'spirit_' -'spa' -'sent_' -'richtige_' -'release_' -'reference_' -'ping_' -'phase_' -'paper_' -'o' -'numbers_' -'moderne_' -'met_' -'menu_' -'me' -'linked_' -'limits_' -'learn_' -'interesting_' -'interested_' -'icht_' -'hoch' -'helped_' -'halte_' -'gesehen_' -'genuine_' -'gefunden_' -'g' -'freie_' -'fordern_' -'fixed_' -'fine_' -'fail_' -'extension_' -'examples_' -'erzielt_' -'enormous_' -'endlich_' -'encourage_' -'else_' -'eit_' -'dly_' -'district_' -'criteria_' -'continued_' -'consensus_' -'candidate_' -'buffet_' -'britischen_' -'books_' -'bonds_' -'bestimmt_' -'becomes_' -'baren_' -'apartments_' -'animals_' -'adopt_' -'accepted_' -'Worten_' -'Weltwirtschaft_' -'Wege_' -'Vor' -'Vertreter_' -'Urlaub_' -'Um' -'Turkish_' -'Ste' -'Status_' -'Selbst_' -'Sache_' -'Red_' -'RI' -'Nice_' -'NEW_' -'Muslim_' -'Meine_' -'Lebens' -'Last_' -'Konzept_' -'Januar_' -'Golf_' -'Gaza_' -'Einige_' -'Behandlung_' -'Alternative_' -'Agenda_' -'400_' -'0' -', „_' -'“_' -'у' -'Über' -'wo' -'willing_' -'weisen_' -'warm_' -'verbundenen_' -'understanding_' -'ul_' -'tried_' -'traffic_' -'tomorrow_' -'to' -'ter' -'television_' -'targets_' -'suggests_' -'sufficient_' -'stärken_' -'stronger_' -'spread_' -'signed_' -'shared_' -'separate_' -'seeking_' -'scope_' -'ro' -'respond_' -'released_' -'regionale_' -'ready_' -'putting_' -'published_' -'pass_' -'owing_' -'org_' -'modernen_' -'mittel_' -'minimum_' -'managed_' -'lu' -'log_' -'lernen_' -'kommenden_' -'kennen_' -'integrated_' -'improvement_' -'immediate_' -'identity_' -'hear_' -'green_' -'governance_' -'got_' -'games_' -'flight_' -'fellow_' -'exactly_' -'evening_' -'europäischer_' -'el' -'ehemaligen_' -'earlier_' -'difficulties_' -'damals_' -'dagegen_' -'cross_' -'crime_' -'comfort_' -'character_' -'camera_' -'box_' -'bezüglich_' -'beste_' -'behavior_' -'aten_' -'approved_' -'anti' -'acht_' -'abge' -'Zustimmung_' -'Za' -'Vielleicht_' -'USS_' -'Tra' -'Sterne_' -'Solidarität_' -'Sinn_' -'Risiko_' -'Regime_' -'Rechts' -'Prodi_' -'Nachbarn_' -'Monat_' -'Modell_' -'Ku' -'J_' -'In' -'III_' -'Her' -'Health_' -'Eindruck_' -'EZB_' -'Do' -'Clinton_' -'Business_' -'Bis_' -'Bilder_' -'Bau' -'Barack_' -'Au' -'+_' -'о' -'ät_' -'Überwachung_' -'www_' -'wirtschaftlicher_' -'wert_' -'welfare_' -'voters_' -'vo' -'verringern_' -'verpflichtet_' -'van_' -'unge' -'une_' -'unable_' -'umgesetzt_' -'ultimately_' -'summer_' -'street_' -'specifically_' -'sort_' -'sicherzustellen_' -'ser_' -'revolution_' -'resolve_' -'rer_' -'reflect_' -'quote_' -'ps_' -'protected_' -'port_' -'planet_' -'placed_' -'pada_' -'otherwise_' -'ones_' -'offering_' -'morgen_' -'millions_' -'mer' -'measure_' -'machine_' -'licher_' -'letztlich_' -'konzentrieren_' -'je' -'j' -'io_' -'host_' -'holding_' -'größeren_' -'greatest_' -'gleiche_' -'gelangen_' -'ga' -'führte_' -'faces_' -'expressed_' -'expensive_' -'era_' -'entweder_' -'entsprechend_' -'ensuring_' -'durch' -'download_' -'divided_' -'discussions_' -'discussed_' -'described_' -'dennoch_' -'deliver_' -'continues_' -'continent_' -'conclude_' -'comments_' -'color_' -'click_' -'broad_' -'bestand_' -'berg_' -'begrüße_' -'beginnen_' -'bedarf_' -'außerdem_' -'aus' -'arbeitet_' -'anderem_' -'alte_' -'accession_' -'abzu' -'Vorteil_' -'Video_' -'Versuch_' -'Trotz_' -'Tre' -'Standard_' -'Saudi_' -'Polen_' -'Pe' -'Or' -'Open_' -'Nachdem_' -'NA' -'Mo' -'Mit' -'Michael_' -'James_' -'Ist_' -'Haltung_' -'Gäste_' -'Gegenteil_' -'Entschließung_' -'Ent' -'El_' -'Bürgern_' -'Ben' -'Beitritt_' -'Arbeits' -'Anstieg_' -'75_' -'33_' -'32_' -'30' -'.._' -'%, _' -'” – _' -'ı' -'Ä' -'­_' -'window_' -'widely_' -'west_' -'wer_' -'vollständig_' -'veröffentlicht_' -'ve' -'ufen_' -'tte_' -'tradition_' -'thereby_' -'tan_' -'spent_' -'southern_' -'sources_' -'skills_' -'sanctions_' -'rural_' -'root_' -'reception_' -'profit_' -'priorities_' -'player_' -'partly_' -'oc' -'obligations_' -'nachdem_' -'militärische_' -'mar' -'läuft_' -'loss_' -'lo_' -'llen_' -'liberal_' -'ite_' -'it' -'industrie_' -'individuals_' -'höhere_' -'himself_' -'heutigen_' -'granted_' -'gi' -'format_' -'firm_' -'ff_' -'fand_' -'fahren_' -'expectations_' -'exclusive_' -'erlaubt_' -'entsprechenden_' -'eln_' -'einiger_' -'dürfte_' -'doesn_' -'detailed_' -'denken_' -'default_' -'cuts_' -'cover_' -'communities_' -'claim_' -'britische_' -'außer_' -'associated_' -'article_' -'ahead_' -'actual_' -'absolutely_' -'Wohn' -'Uni' -'UNO_' -'Trump_' -'Teilen_' -'Systeme_' -'Strategien_' -'Square_' -'Secretary_' -'Schulden' -'Regel_' -'Präsidentschaft_' -'Po' -'Männer_' -'Ma' -'Leute_' -'Kindern_' -'Kapital' -'Justice_' -'Ja' -'Islamic_' -'Homepage_' -'Geschäfts' -'Ger' -'Gebäude_' -'Frankfurt_' -'Firmen_' -'Erachtens_' -'Einrichtungen_' -'Ebenso_' -'Di' -'Christian_' -'Breakfast_' -'Bio' -'Ausland_' -'Argentina_' -'Ad' -'2014_' -'" (_' -' -, _' -'—_' -'ó' -'ä' -'Öl' -'wieder' -'whatever_' -'vorhanden_' -'ures_' -'unver' -'uner' -'ul' -'tt_' -'treated_' -'sun_' -'suchen_' -'stra' -'someone_' -'so' -'smaller_' -'slow_' -'sides_' -'seven_' -'ro_' -'represents_' -'relating_' -'regionalen_' -'rapid_' -'r' -'pour_' -'permanent_' -'pe_' -'payments_' -'parliamentary_' -'oren_' -'operating_' -'ons' -'nie' -'ne' -'möglichkeiten_' -'monitoring_' -'miteinander_' -'mark_' -'loans_' -'lo' -'listed_' -'link_' -'limit_' -'leads_' -'languages_' -'land' -'kt_' -'ju' -'ji' -'ization_' -'iten_' -'inzwischen_' -'introduced_' -'ians_' -'hören_' -'höher_' -'geschehen_' -'gel' -'garden_' -'funktioniert_' -'fuel_' -'französischen_' -'ff' -'expansion_' -'enti' -'discuss_' -'cutting_' -'corporate_' -'contemporary_' -'connected_' -'combination_' -'causes_' -'benutzt_' -'begann_' -'bear_' -'battle_' -'bars_' -'auszu' -'ating_' -'as' -'ana' -'Zur_' -'Zinsen_' -'Währungs' -'Volk_' -'U' -'Thus_' -'Their_' -'Standpunkt_' -'Se' -'Realität_' -'Prioritäten_' -'Nutzen_' -'Netherlands_' -'Natur_' -'MySQL_' -'Mer' -'Leistungen_' -'Krankheiten_' -'Klima' -'Klicken_' -'Insel_' -'Hoffnung_' -'Gre' -'God_' -'Gesellschaften_' -'Gegensatz_' -'Film_' -'Fe' -'Falle_' -'Eastern_' -'Dr_' -'Denn_' -'Democrats_' -'Car' -'Bewegung_' -'Best_' -'Augen_' -'Atmosphäre_' -'Abschluss_' -'2015_' -'. (_' -', "_' -'„_' -'über' -'äu' -'  _' -'wrote_' -'write_' -'wodurch_' -'win_' -'wesentlich_' -'weg_' -'verlieren_' -'verfahren_' -'va' -'unto_' -'unless_' -'ungefähr_' -'unacceptable_' -'umfassende_' -'turning_' -'trägt_' -'truth_' -'transparency_' -'transition_' -'traditionellen_' -'thinking_' -'thanks_' -'terrorist_' -'technological_' -'talks_' -'swimming_' -'suffer_' -'strategies_' -'stimmen_' -'stellte_' -'starke_' -'st' -'schließen_' -'russischen_' -'resulting_' -'represent_' -'relate_' -'regulatory_' -'ran_' -'punkt_' -'presidential_' -'picture_' -'oni' -'ok_' -'offiziellen_' -'offensichtlich_' -'ns' -'nis' -'nachhaltige_' -'models_' -'migration_' -'mid_' -'meaning_' -'maßnahmen_' -'materials_' -'lebih_' -'laut_' -'laid_' -'komplett_' -'ing' -'industries_' -'ical_' -'ha' -'großes_' -'gleich' -'gezwungen_' -'getroffen_' -'geo' -'functions_' -'followed_' -'folgenden_' -'figures_' -'faced_' -'fa' -'ey_' -'extensive_' -'eu' -'erhöht_' -'equally_' -'enz' -'entstehen_' -'ele' -'einander_' -'directives_' -'determined_' -'ded_' -'debates_' -'deaths_' -'daten_' -'contain_' -'closer_' -'cheap_' -'che' -'besondere_' -'berücksichtigt_' -'behandelt_' -'auf' -'arms_' -'arbeit_' -'announced_' -'Wochen_' -'Vergleich_' -'Unabhängigkeit_' -'Un' -'Umständen_' -'Two_' -'Stimmen_' -'Steuer' -'Sta' -'Spieler_' -'Shi' -'Second_' -'SA' -'Royal_' -'Pri' -'Op' -'Old_' -'Ohne_' -'Nun_' -'Nachfolgend_' -'Mitteilung_' -'Me' -'Lu' -'Le' -'Kritik_' -'Ke' -'Is_' -'Instrument_' -'ID_' -'Home_' -'Hinsicht_' -'Haupt' -'Gra' -'Gold_' -'Go' -'Given_' -'Gesetz_' -'Forschungs' -'Engagement_' -'Einrichtung_' -'EADS_' -'Dennoch_' -'Bildungs' -'Beschäftigung_' -'Beschreibung_' -'Apartments_' -'Amt_' -'Alliance_' -'Air_' -'AIDS_' -'9' -'5' -'1997_' -'1996_' -'1995_' -'150_' -'01_' -'и' -'е' -'á_' -'Über_' -'zimmer_' -'wollte_' -'weltweite_' -'vertreten_' -'verloren_' -'unseres_' -'unlikely_' -'track_' -'tischen_' -'the' -'supporting_' -'suffering_' -'sub' -'stress_' -'strengthen_' -'starting_' -'stands_' -'standing_' -'signal_' -'selbstverständlich_' -'sea' -'saving_' -'rt' -'ries_' -'restrictions_' -'radical_' -'proper_' -'politisch_' -'piece_' -'physical_' -'persönlichen_' -'perspective_' -'per' -'olitik_' -'older_' -'moving_' -'mis' -'min_' -'medium_' -'manage_' -'maintained_' -'laws_' -'keits' -'ked_' -'kaufen_' -'jährlich_' -'ischer_' -'introduction_' -'introduce_' -'inside_' -'independence_' -'increases_' -'imports_' -'ik_' -'humanitarian_' -'housing_' -'historische_' -'guidelines_' -'gs' -'gold_' -'gerecht_' -'gave_' -'gar' -'fo' -'flexible_' -'fire_' -'fields_' -'falsch_' -'expression_' -'exist_' -'except_' -'eventually_' -'euch_' -'erten_' -'ep' -'entry_' -'employees_' -'emphasis_' -'eingeführt_' -'ee_' -'duty_' -'dir_' -'dia' -'delegation_' -'criminal_' -'collapse_' -'coffee_' -'claims_' -'chi' -'chaft_' -'cat' -'carefully_' -'car' -'bottom_' -'bestehenden_' -'begrüßen_' -'barkeit_' -'ausge' -'armed_' -'anyone_' -'angeht_' -'ah_' -'ad_' -'Wissenschaft_' -'Wein' -'Verb' -'Ungleichheit_' -'Teile_' -'THE_' -'Stärkung_' -'Staats' -'Staates_' -'Sehr_' -'Sehenswürdigkeiten_' -'Rotary_' -'Ro' -'Reaktion_' -'Produktions' -'Ph' -'Ne' -'Name_' -'Na' -'NI_' -'Mi' -'La' -'Kunst_' -'Kompromiss_' -'Ka' -'Israelis_' -'Irish_' -'Initiativen_' -'IS' -'Hintergrund_' -'Forderung_' -'Ereignisse_' -'Copenhagen_' -'Chi' -'Can_' -'Bur' -'Binnenmarkt_' -'Beispiele_' -'Bau_' -'Basis_' -'Barroso_' -'Bar' -'Aufbau_' -'Aspekt_' -'Anfrage_' -'36_' -'14' -''._' -' = _' -' ... _' -'ö' -'ée_' -'zusätzlich_' -'zation_' -'welches_' -'weiterer_' -'victory_' -'vergessen_' -'ver' -'unbe' -'ub' -'truly_' -'teilweise_' -'tar' -'suffered_' -'struggle_' -'ski_' -'shops_' -'seriously_' -'selected_' -'riesigen_' -'resort_' -'remember_' -'pursue_' -'purchase_' -'playing_' -'phrase_' -'ourselves_' -'ort_' -'oral_' -'on' -'no' -'möglichen_' -'ming_' -'mehreren_' -'lt' -'looks_' -'lichkeit_' -'leistungen_' -'ld_' -'ld' -'launch_' -'laufen_' -'lan' -'laden_' -'kürzlich_' -'kle' -'jo' -'ium_' -'ish_' -'ire_' -'intervention_' -'implementing_' -'he' -'hauptsächlich_' -'happy_' -'grundlegende_' -'geändert_' -'gerne_' -'fähigkeit_' -'fresh_' -'flexibility_' -'fish_' -'erinnern_' -'erhält_' -'equivalent_' -'enterprises_' -'ene_' -'email_' -'dynamic_' -'diplomatic_' -'declaration_' -'database_' -'counter_' -'contrast_' -'conflicts_' -'completed_' -'combat_' -'collective_' -'calling_' -'ber' -'benutzen_' -'automatisch_' -'asylum_' -'asset_' -'anstatt_' -'animal_' -'angezeigt_' -'ach_' -'Zi' -'Y' -'Volks' -'Vo' -'Vereinbarung_' -'Verbindungen_' -'Unter' -'Soviet_' -'Sorgen_' -'Ri' -'Projekte_' -'Pro_' -'Private_' -'Post' -'Pi' -'Organisationen_' -'Mitarbeiter_' -'Krieges_' -'Korruption_' -'Investitions' -'Institute_' -'Informations' -'IP_' -'He' -'Haushalt_' -'Gesetze_' -'Front_' -'Foundation_' -'Fortschritt_' -'Fort' -'February_' -'Familien' -'Entwicklungsländern_' -'Dra' -'Computer_' -'Ca' -'CAMBRIDGE_' -'Bre' -'Board_' -'Bo' -'Beziehung_' -'Aufgrund_' -'Another_' -'48_' -'13' -' /_' -'”, _' -'т' -'р' -'és_' -'är' -'Ökonomen_' -'zufolge_' -'zahlreichen_' -'warming_' -'wa' -'verlangen_' -'ven_' -'under' -'tz_' -'tu' -'translation_' -'tests_' -'terrace_' -'tasks_' -'ständig_' -'stations_' -'starken_' -'staatliche_' -'spacious_' -'sofort_' -'sin' -'siehe_' -'shower_' -'selection_' -'seemed_' -'science_' -'rn' -'ri_' -'responsibilities_' -'relax_' -'relatively_' -'prosperity_' -'promoting_' -'por_' -'platz_' -'partnership_' -'parliament_' -'opened_' -'ongoing_' -'obvious_' -'nf' -'nennen_' -'methods_' -'meetings_' -'mechanism_' -'les' -'langen_' -'labour_' -'ise_' -'internationaler_' -'installation_' -'ina' -'ill_' -'ierungs' -'ier' -'ide_' -'ice_' -'houses_' -'ha_' -'gutes_' -'größer_' -'goals_' -'gemäß_' -'gegenwärtig_' -'französische_' -'faster_' -'erzielen_' -'ergreifen_' -'erfolgen_' -'entwickelten_' -'entirely_' -'entered_' -'eingesetzt_' -'economists_' -'du' -'driving_' -'dollars_' -'display_' -'defined_' -'darunter_' -'danger_' -'danach_' -'dalam_' -'crimes_' -'corruption_' -'contract_' -'constitution_' -'charged_' -'cer' -'cancer_' -'bu' -'bre' -'bly_' -'biggest_' -'beruht_' -'benötigt_' -'believed_' -'beds_' -'ausschließlich_' -'assets_' -'ans' -'agen_' -'advance_' -'administrative_' -'ade' -'achten_' -'accordance_' -'a' -']] [[_' -'\\_' -'Zunächst_' -'Your_' -'Würde_' -'Wissen_' -'Waren_' -'Vietnam_' -'Verpflichtungen_' -'Verpflichtung_' -'Verhältnis_' -'Verfassungs' -'Unterschied_' -'Unfortunately_' -'TA' -'Syrien_' -'Straße_' -'San' -'SI' -'SE' -'Ru' -'Q_' -'Pre' -'Pacific_' -'Neben_' -'Mor' -'Monetary_' -'Miss' -'Mexiko_' -'Mexico_' -'Men' -'Medien_' -'Mal' -'Live_' -'Landwirtschaft_' -'Königreich_' -'Kultur' -'Kopf_' -'Je' -'Irland_' -'Internationalen_' -'Hotel' -'Hong_' -'Hoch' -'Hause_' -'Han' -'HIV_' -'HA' -'Geld' -'Formen_' -'Fahr' -'Every_' -'Einklang_' -'EC_' -'Dar' -'DI' -'Click_' -'Cha' -'Ce' -'Bus' -'Bra' -'Bi' -'Atom' -'Arten_' -'Angriff_' -'Abend_' -'95_' -'64_' -'1989_' -'', _' -'”._' -'ст' -'а' -'ür' -'ß' -'zuvor_' -'wi' -'weg' -'wages_' -'w' -'verfolgen_' -'umzusetzen_' -'trend_' -'tre' -'tly_' -'ti_' -'tel_' -'teilen_' -'summit_' -'significantly_' -'sets_' -'sektor_' -'scha' -'sagt_' -'sa' -'rr' -'rin_' -'reservation_' -'reported_' -'rely_' -'rejected_' -'recognize_' -'rechts' -'rasch_' -'qua' -'prime_' -'pri' -'plants_' -'pictures_' -'persons_' -'peaceful_' -'par' -'ou_' -'ou' -'opposite_' -'op' -'obviously_' -'nu' -'north_' -'ni' -'ng' -'nes_' -'nder_' -'nationaler_' -'nar' -'named_' -'moved_' -'mm_' -'mer_' -'manchmal_' -'machte_' -'ll' -'lessons_' -'learning_' -'krise_' -'ki_' -'initial_' -'igung_' -'iger_' -'ied_' -'hn_' -'helping_' -'hei' -'guaranteed_' -'gesprochen_' -'gender_' -'genannt_' -'gelten_' -'geleistet_' -'formal_' -'fisheries_' -'finanziellen_' -'finanzielle_' -'figure_' -'fat' -'extended_' -'extend_' -'explain_' -'experts_' -'enen_' -'dy_' -'durchaus_' -'drug_' -'dra' -'do' -'diseases_' -'deutsche_' -'cuisine_' -'courses_' -'couple_' -'cor' -'contrary_' -'constitutional_' -'commitments_' -'charges_' -'cast_' -'capable_' -'candidates_' -'bound_' -'beachten_' -'ban_' -'balanced_' -'außerordentlich_' -'argue_' -'appeal_' -'anzu' -'ang' -'allowing_' -'alliance_' -'allgemeine_' -'ages_' -'absolute_' -'abhängig_' -'Wähler_' -'Wegen_' -'Verhalten_' -'Umwelt' -'Transport_' -'Tradition_' -'Städte_' -'Stadt' -'Sol' -'Si' -'Schäden_' -'Schule_' -'SS_' -'Rezeption_' -'Report_' -'Q' -'Perhaps_' -'Paul_' -'Pa' -'PHP_' -'Niveau_' -'Ni' -'Nahen_' -'NE' -'Mu' -'Mitgliedschaft_' -'Militär' -'Merkel_' -'Mat' -'Located_' -'Lin' -'Leistung_' -'Las_' -'LI' -'Kong_' -'Klein' -'Kern' -'Jetzt_' -'Instrumente_' -'Hälfte_' -'Generation_' -'Gegen' -'Flug' -'Finanzkrise_' -'Far' -'Familien_' -'Erde_' -'Du_' -'Daten' -'Chancen_' -'Cameron_' -'Berichts_' -'BMW_' -'Auto' -'Ausweitung_' -'Ausbildung_' -'Aufenthalt_' -'Anstrengungen_' -'Anforderungen_' -'Am' -'Altstadt_' -'AR' -'25' -'15' -'11' -' — _' -' –, _' -'ы' -'é' -'è' -'Öl_' -'Ägypten_' -'yourself_' -'wine_' -'wiederum_' -'wenigen_' -'welt' -'weight_' -'vulnerable_' -'voice_' -'verlassen_' -'verantwortlich_' -'vast_' -'urgent_' -'ual_' -'te' -'tan' -'tahun_' -'supports_' -'studies_' -'structures_' -'ss' -'species_' -'south_' -'solve_' -'smoking_' -'sitting_' -'sion_' -'sierung_' -'ships_' -'sharing_' -'severe_' -'session_' -'select_' -'seien_' -'season_' -'schlagen_' -'remove_' -'relative_' -'recommend_' -'recession_' -'reaching_' -'race_' -'pu' -'provision_' -'proved_' -'prospects_' -'promotion_' -'promise_' -'practices_' -'positions_' -'photos_' -'photo_' -'pension_' -'owned_' -'out' -'organisations_' -'nisse_' -'names_' -'nahme_' -'mutual_' -'mountain_' -'minority_' -'micro' -'memory_' -'love_' -'ln_' -'lesen_' -'langfristige_' -'la' -'kitchen_' -'ker' -'kar' -'ir' -'improvements_' -'images_' -'hy' -'hundreds_' -'honourable_' -'gre' -'garantiert_' -'führenden_' -'fällt_' -'funktionieren_' -'frühen_' -'founded_' -'fighting_' -'felt_' -'eye_' -'exists_' -'exercise_' -'ethnic_' -'essentially_' -'equality_' -'entschieden_' -'entscheidender_' -'enk' -'elegant_' -'einzigen_' -'einge' -'dritten_' -'dinner_' -'defend_' -'defence_' -'currencies_' -'criticism_' -'crises_' -'compatible_' -'closely_' -'cha' -'budgetary_' -'bt_' -'ble' -'berücksichtigen_' -'automatically_' -'austerity_' -'arrangements_' -'arabischen_' -'anderes_' -'and' -'amp' -'ag_' -'Wunsch_' -'Worte_' -'West' -'Washington_' -'Wahl' -'Wachstums' -'Voraussetzungen_' -'Vielzahl_' -'Verwaltungs' -'Untersuchung_' -'Treffen_' -'Times_' -'Teil' -'Süd' -'Sweden_' -'Steuern_' -'Stand_' -'Sprache_' -'Sohn_' -'Serbia_' -'See_' -'Schlüssel' -'Safety_' -'Rome_' -'Regulation_' -'Rechtsvorschriften_' -'Phase_' -'PT_' -'Ost' -'NT' -'Monate_' -'Mindest' -'MO' -'Liberalisierung_' -'Krisen' -'Kontakt_' -'Kind_' -'Kar' -'Ju' -'Jean_' -'Iraqi_' -'Inseln_' -'Größe_' -'Grund' -'Grenze_' -'Gleichzeitig_' -'Gi' -'Gesundheits' -'Friedens' -'Erhöhung_' -'El' -'Ei' -'Durchführung_' -'Drittens_' -'De_' -'David_' -'Dann_' -'Buch_' -'Bel' -'Bank' -'Bal' -'Bahn_' -'Aspekte_' -'Anwendungen_' -'Anspruch_' -'Angst_' -'Anerkennung_' -'Al' -'Agreement_' -'65_' -'39_' -'з' -'überprüfen_' -'ón_' -'í' -'á' -'   . – _' -'za_' -'wor' -'wirksam_' -'weak_' -'war' -'wagen_' -'vieler_' -'verstanden_' -'usw_' -'us' -'universal_' -'unabhängig_' -'uf' -'tz' -'turns_' -'tung_' -'tourist_' -'touch_' -'tive_' -'tis' -'testing_' -'surrounding_' -'sugar_' -'subsidies_' -'stre' -'sti' -'ssi' -'sonst_' -'soft_' -'societies_' -'serves_' -'ser' -'schreiben_' -'schneller_' -'scheme_' -'russische_' -'run' -'rit' -'richten_' -'representative_' -'remained_' -'reich_' -'recognise_' -'rechts_' -'ra' -'prüfen_' -'productive_' -'pra' -'po' -'pick_' -'peoples_' -'pen' -'payment_' -'participate_' -'parliaments_' -'parents_' -'over' -'origin_' -'organizations_' -'onen_' -'ogen_' -'occur_' -'musste_' -'mu' -'motion_' -'mode_' -'mo' -'maximum_' -'manner_' -'mandate_' -'mal' -'lä' -'losses_' -'lokalen_' -'lity_' -'lesson_' -'legt_' -'leaving_' -'le' -'kurzem_' -'killed_' -'kept_' -'jener_' -'ized_' -'ionen_' -'institution_' -'ig' -'ici' -'ia' -'hostels_' -'hit_' -'gung_' -'griechischen_' -'gla' -'gestimmt_' -'gegenwärtigen_' -'focused_' -'father_' -'factors_' -'extreme_' -'expense_' -'expenditure_' -'exit_' -'enorme_' -'emergency_' -'element_' -'einfache_' -'ehen_' -'east_' -'drugs_' -'discovered_' -'differences_' -'destruction_' -'demokratische_' -'dank_' -'da' -'cu' -'coordination_' -'consideration_' -'confirmed_' -'command_' -'choices_' -'ce' -'buildings_' -'brings_' -'bility_' -'bi' -'bestätigen_' -'belief_' -'bath_' -'außen_' -'ausreichend_' -'ausdrücklich_' -'aufzu' -'aufnehmen_' -'attractive_' -'arrival_' -'ard_' -'apart_' -'aims_' -'ably_' -'abe_' -'Ziel' -'Zeitraum_' -'Weltbank_' -'War' -'Vom_' -'Verwaltung_' -'Venezuela_' -'Var' -'Umfang_' -'Tour_' -'Tor' -'To' -'Thank_' -'Stu' -'Stil_' -'Stellung_' -'Sat' -'Santa_' -'Sanktionen_' -'Sand' -'Saddam_' -'SQL_' -'Rück' -'Russlands_' -'Roman_' -'Roma_' -'Richtlinien_' -'Rede_' -'Rechts_' -'Quelle_' -'Prozesses_' -'Pen' -'Patienten_' -'Other_' -'Option_' -'Mas' -'Mar' -'Luft' -'Leit' -'Leistungs' -'Lebanon_' -'Kyoto_' -'Krankheit_' -'Konflikt_' -'Klimawandel_' -'Kinder' -'Kan' -'Jeder_' -'Hel' -'Hauptstadt_' -'Ha' -'Gründe_' -'Green_' -'Gespräche_' -'Gemeinsamen_' -'Gebieten_' -'Führer_' -'Fähigkeit_' -'Funktionen_' -'Fonds_' -'Find_' -'Faktoren_' -'FR_' -'Emissionen_' -'Ein' -'Each_' -'Dadurch_' -'Charakter_' -'Brüssel_' -'Berichterstatterin_' -'Beide_' -'Begriff_' -'Beach_' -'BA' -'Auf' -'Administration_' -'80' -'23' -'21' -'1' -'.“ _' -' (' -'ären_' -'äre_' -'äge_' -'Übereinstimmung_' -'zählen_' -'zuge' -'yesterday_' -'wirtschaftlich_' -'wichtigste_' -'westlichen_' -'weiteres_' -'wachsende_' -'vous_' -'verstärken_' -'verfolgt_' -'van' -'unter' -'uni' -'ungs' -'und' -'umfassenden_' -'um' -'tzen_' -'trotzdem_' -'tor' -'talking_' -'systeme_' -'surrounded_' -'supposed_' -'super' -'succeed_' -'substances_' -'su' -'stru' -'spielt_' -'sovereignty_' -'sor' -'sit' -'sell_' -'seitens_' -'schools_' -'ru' -'rte' -'rs' -'rly_' -'rental_' -'remote_' -'reicht_' -'referred_' -'records_' -'recognition_' -'radio_' -'quick_' -'properly_' -'producers_' -'processes_' -'prevention_' -'pp' -'patients_' -'pan' -'packages_' -'pa' -'ot_' -'osi' -'ordinary_' -'müssten_' -'myself_' -'mittlerweile_' -'miss' -'militärischen_' -'mas' -'markt_' -'malaria_' -'losing_' -'looked_' -'ler' -'len' -'legitimate_' -'langsam_' -'l' -'kulturelle_' -'kt' -'konkrete_' -'keinerlei_' -'keiner_' -'intellectual_' -'informiert_' -'informed_' -'imposed_' -'impose_' -'import_' -'immigrants_' -'imagine_' -'households_' -'her' -'har' -'hand' -'han_' -'guide_' -'gruppe_' -'grow_' -'golf_' -'ght_' -'geschützt_' -'geb' -'gap_' -'ft' -'früher_' -'freiheit_' -'flows_' -'flat_' -'fit_' -'fell_' -'explanation_' -'ex_' -'ets_' -'est' -'eri' -'ended_' -'ek_' -'eight_' -'economics_' -'dunia_' -'division_' -'discover_' -'devices_' -'device_' -'detail_' -'derzeitigen_' -'depends_' -'del' -'definition_' -'deeply_' -'cycle_' -'cri' -'covered_' -'consultation_' -'conducted_' -'concluded_' -'compensation_' -'colleague_' -'coal_' -'cies_' -'cars_' -'bringing_' -'born_' -'bor' -'bon' -'blocks_' -'block_' -'bildet_' -'beziehen_' -'bezeichnet_' -'bestimmen_' -'beschlossen_' -'bemüht_' -'beigetragen_' -'beaches_' -'ban' -'ball_' -'back' -'ba' -'ausgesetzt_' -'attempts_' -'ati' -'at' -'assume_' -'asking_' -'arguments_' -'appeared_' -'andererseits_' -'an' -'allgemeinen_' -'allgemein_' -'al' -'ai' -'ahmen_' -'agency_' -'Wohlstand_' -'Will_' -'Widerstand_' -'Villa_' -'Very_' -'VO' -'Trek_' -'Ton' -'Tod_' -'Test' -'Ta' -'Streit' -'Straßen' -'Standard' -'Sprachen_' -'Speicher' -'Skype_' -'Sieg_' -'Sa' -'Rückgang_' -'Risiko' -'Regelung_' -'Real' -'Que' -'Pu' -'Produkt' -'Problemen_' -'Praxis_' -'Partnerschaft_' -'Ordnung_' -'OS_' -'OR_' -'Not' -'Nobel_' -'Nevertheless_' -'Media_' -'Mann_' -'Macht' -'MI' -'Leider_' -'Lei' -'Lebens_' -'Kriterien_' -'Kommunikation_' -'Kombination_' -'Karte_' -'Inhalt_' -'Industrie' -'Identität_' -'IT' -'Hä' -'Hostel_' -'Handels_' -'Geschäftsordnung_' -'Geldpolitik_' -'Geb' -'Fra' -'Foto_' -'Foreign_' -'Forderungen_' -'Februar_' -'Fax_' -'Experten_' -'Entwurf_' -'Entwicklungs' -'End' -'Ed' -'ER' -'Download_' -'Direkt' -'Dimension_' -'DE' -'Control_' -'Bos' -'Balkan_' -'Austrian_' -'Aussicht_' -'Aufgaben_' -'Arm' -'Analyse_' -'Allgemeinen_' -'Ale' -'Ala' -'Absch' -'AN' -'192' -'™ _' -'– _' -'м' -'zurückge' -'zero_' -'work' -'weit' -'vorge' -'virtually_' -'village_' -'ur' -'unterschiedliche_' -'unternommen_' -'unmittelbar_' -'tut_' -'ts' -'trifft_' -'trans' -'title_' -'temporary_' -'telephone_' -'substantial_' -'stance_' -'square_' -'sprach_' -'sports_' -'spa_' -'sovereign_' -'sized_' -'sieben_' -'sicherlich_' -'sha' -'sensitive_' -'senior_' -'schönen_' -'sage_' -'returns_' -'represented_' -'relaxing_' -'registered_' -'reflects_' -'referendum_' -'reden_' -'rag' -'quantitative_' -'profits_' -'producing_' -'print_' -'pi' -'perfectly_' -'pan_' -'overcome_' -'ord' -'or' -'onto_' -'olo' -'nts_' -'ngs_' -'newly_' -'nan' -'mussten_' -'multi' -'mor' -'ministers_' -'meist_' -'match_' -'marketing_' -'macroeconomic_' -'länder_' -'lten_' -'lovely_' -'lim' -'launched_' -'kraft_' -'klare_' -'kla' -'ki' -'keeping_' -'itu' -'isierung_' -'ise' -'ip' -'instance_' -'install_' -'inequality_' -'il' -'identify_' -'ian' -'hot_' -'ho' -'hinweisen_' -'heiten_' -'head' -'hardly_' -'groß_' -'globalization_' -'gli' -'gewählt_' -'gewisse_' -'gestalten_' -'ged_' -'ge' -'furniture_' -'formed_' -'forget_' -'flow_' -'fel' -'federal_' -'farming_' -'et' -'erstellt_' -'ernst_' -'erle' -'ergeben_' -'erfahren_' -'entschlossen_' -'enabling_' -'emphasise_' -'elle_' -'ek' -'ehr' -'edi' -'ear' -'distributed_' -'disputes_' -'destroyed_' -'deserves_' -'demanding_' -'decide_' -'dealing_' -'crew_' -'contribute_' -'continuing_' -'concrete_' -'comment_' -'combined_' -'combating_' -'cohesion_' -'cards_' -'button_' -'bul' -'broader_' -'briefly_' -'boom_' -'blood_' -'bezahlen_' -'bewegen_' -'bee' -'background_' -'auto' -'ausgaben_' -'aufge' -'atau_' -'argument_' -'ara_' -'ar' -'angeboten_' -'ancient_' -'ana_' -'am' -'aktiv_' -'afternoon_' -'ae' -'ada_' -'ad' -'ach' -'accounts_' -'accompanied_' -'accessible_' -'Zusammenbruch_' -'Zentralbanken_' -'Ze' -'Wo' -'Wirkung_' -'Verlust_' -'Unternehmens' -'Texte_' -'TI' -'Studie_' -'Sprach' -'Sport_' -'Spar' -'Sonder' -'Selbst' -'Sein_' -'School_' -'Schluss_' -'Schaden_' -'Runde_' -'Reform' -'Priorität_' -'Politik' -'Over_' -'Nord' -'Nach' -'Musik' -'Menschenrechts' -'Menge_' -'Madrid_' -'MA' -'Li' -'Law_' -'Lateinamerika_' -'Kredit' -'Kon' -'Justiz_' -'IN' -'Hinweis_' -'Hill_' -'Grundrechte_' -'Grunde_' -'Grad_' -'Good_' -'Gerichtshof_' -'Gemeinschafts' -'Enjoy_' -'Earth_' -'EL' -'Deutsche_' -'Dass_' -'Dan' -'DA' -'Cre' -'Con' -'Cho' -'Charta_' -'Cap' -'CO' -'Bus_' -'Budget_' -'Book_' -'Bon' -'Beihilfen_' -'Bay_' -'BR' -'Ausnahme_' -'Armee_' -'Apple_' -'Antrag_' -'Anreize_' -'Akteure_' -'Airbus_' -'AG_' -'@_' -': “_' -'44_' -'42_' -'1945_' -'194' -'190' -'188' -'12' -' €_' -'“' -'де' -'д' -'übernommen_' -'ör' -'zurück' -'zufrieden_' -'zeit' -'ysteme_' -'yo' -'wünschen_' -'wirtschaft_' -'wind_' -'wie' -'werk_' -'vorschlagen_' -'vorher_' -'vorgelegt_' -'vor' -'verändert_' -'verdient_' -'verbessert_' -'usual_' -'usi' -'una_' -'umfasst_' -'ue' -'täglich_' -'tting_' -'tten_' -'transfers_' -'ther_' -'th' -'tera' -'technische_' -'survive_' -'super_' -'sun' -'suite_' -'stone_' -'statements_' -'spring_' -'sold_' -'sobald_' -'sit_' -'secret_' -'seats_' -'schwe' -'runs_' -'roads_' -'rein_' -'regardless_' -'refugee_' -'recognized_' -'rch' -'rat_' -'rat' -'railway_' -'rage_' -'purposes_' -'protecting_' -'promised_' -'processing_' -'primarily_' -'precise_' -'politisches_' -'platform_' -'permitted_' -'paragraph_' -'organization_' -'offizielle_' -'occasions_' -'ob' -'nötig_' -'nten_' -'nte_' -'nta' -'notwendigen_' -'normalerweise_' -'nord' -'nit' -'niemand_' -'nge_' -'nearby_' -'ndo_' -'naturally_' -'na' -'museums_' -'mostly_' -'mini_' -'mini' -'metro_' -'metres_' -'menschlichen_' -'mechanisms_' -'luxurious_' -'liquidity_' -'leisure_' -'learned_' -'lay_' -'latter_' -'lage_' -'kh' -'journalists_' -'itt_' -'issued_' -'involve_' -'initially_' -'incentives_' -'ina_' -'impressive_' -'implications_' -'ik' -'id_' -'höheren_' -'höchsten_' -'ht' -'household_' -'hostel_' -'hoher_' -'hn' -'hip_' -'hinzu' -'heits' -'guter_' -'gun' -'gu' -'go' -'gewährleistet_' -'gewa' -'gespielt_' -'gelungen_' -'gel_' -'gegründet_' -'gegenwärtige_' -'gefallen_' -'garantieren_' -'gained_' -'führung_' -'fun_' -'forum_' -'fordert_' -'for' -'finding_' -'finanziert_' -'fied_' -'feststellen_' -'festgelegt_' -'feed_' -'fantastic_' -'existence_' -'exclusively_' -'excessive_' -'erwiesen_' -'erleben_' -'erklärte_' -'ering_' -'erfolgreichen_' -'erfolg' -'engineering_' -'endorse_' -'end' -'electronic_' -'electricity_' -'einzusetzen_' -'einsetzen_' -'eingerichtet_' -'eingehen_' -'effectiveness_' -'dé' -'dri' -'diversity_' -'disaster_' -'determine_' -'danke_' -'correct_' -'convenient_' -'communications_' -'coast_' -'club_' -'cho' -'chief_' -'chen' -'centuries_' -'cation_' -'category_' -'bur' -'brachte_' -'booking_' -'bla' -'bitten_' -'besseren_' -'bedroom_' -'availability_' -'aufzunehmen_' -'ationen_' -'army_' -'ari' -'appreciate_' -'apa' -'ante_' -'anbieten_' -'ama' -'ai_' -'agencies_' -'af' -'addressing_' -'ace' -'aba' -'ab' -']]_' -'Zweiten_' -'Zeit' -'Z_' -'YORK_' -'Within_' -'Willen_' -'Wieder' -'White_' -'Wahrheit_' -'WI' -'Verteidigung_' -'Vereinbarungen_' -'Verbrechen_' -'Val' -'Ur' -'Unterschiede_' -'Trans' -'Those_' -'Test_' -'Tau' -'Taiwan_' -'Tages_' -'TNG_' -'Städten_' -'Studien_' -'Spiele_' -'Son' -'Sommer_' -'Sind_' -'Sin' -'She' -'Sha' -'Seine_' -'Schul' -'Sarkozy_' -'SP' -'Regimes_' -'RA' -'Prä' -'Problems_' -'Prinzipien_' -'Premierminister_' -'Preis' -'Plaza_' -'Pin' -'Perspektive_' -'Page_' -'PS' -'Opposition_' -'ON' -'Nor_' -'Nein_' -'Neben' -'Muslims_' -'Multi' -'Meiner_' -'Meanwhile_' -'Location_' -'Libanon_' -'Lassen_' -'Lang' -'Kriegs' -'Kredite_' -'Kor' -'Kontroll' -'Konsens_' -'Klimaanlage_' -'Kirk_' -'King_' -'Kenntnis_' -'Jedes_' -'JavaScript_' -'Jahrzehnten_' -'Jahres' -'Island_' -'Iranian_' -'Ideen_' -'IC' -'Holz' -'Gefahren_' -'Finanzierungs' -'Fest' -'Facilities_' -'FI' -'Executive_' -'Erwartungen_' -'Erd' -'Entwicklungsländer_' -'Einzel' -'Einwohner_' -'Eigen' -'Doha_' -'Des' -'Depression_' -'Data_' -'Cuba_' -'Chile_' -'Bä' -'Bri' -'Ber' -'Bas' -'Ban' -'BE' -'AP' -'AM' -'> _' -'800_' -'52_' -'1991_' -'10' -'05_' -'.” _' -'."_' -') ' -' € _' -' > _' -'қа' -'я' -'к' -'в' -'б' -'č_' -'übertragen_' -'überrascht_' -'Österreich_' -'zugleich_' -'zo' -'zer' -'zentralen_' -'yr' -'winter_' -'widespread_' -'wesentlichen_' -'welcher_' -'weiter' -'wars_' -'warning_' -'wage_' -'volle_' -'vic' -'verwende_' -'verurteilt_' -'vehicles_' -'vehicle_' -'uss_' -'update_' -'unit_' -'unde' -'u' -'tä' -'troops_' -'tro' -'traditionelle_' -'tourism_' -'tori' -'tisch_' -'tend_' -'temp' -'technischen_' -'taste_' -'tal' -'swa' -'sustainability_' -'surplus_' -'sur' -'strengthening_' -'store_' -'sterben_' -'sport_' -'spoke_' -'spectrum_' -'sought_' -'solcher_' -'smus_' -'sme' -'situations_' -'sing' -'signs_' -'sel' -'schwierigen_' -'sar' -'sam' -'ré' -'rung_' -'rum' -'rti' -'rio' -'rin' -'rf' -'returned_' -'resource_' -'resolved_' -'replaced_' -'rei' -'regular_' -'raten_' -'push_' -'ption_' -'propose_' -'proc' -'prior_' -'preis' -'pleasure_' -'planned_' -'pie' -'phi' -'persönlich_' -'personally_' -'ought_' -'ora' -'opposed_' -'opinions_' -'ome' -'ok' -'offenen_' -'of' -'oe' -'nte' -'niemals_' -'ngs' -'nen' -'müsste_' -'möglichst_' -'mö' -'motor' -'mont' -'mon' -'mix_' -'mental_' -'meant_' -'mat' -'ma' -'luxury_' -'lp' -'li' -'letzter_' -'law' -'künftige_' -'kulturellen_' -'kostenlos_' -'ko_' -'knows_' -'kleiner_' -'kamen_' -'jeweils_' -'jam' -'intensive_' -'im' -'ile_' -'ie' -'ichen_' -'hrt_' -'hof_' -'hel' -'gruppen_' -'grundlegenden_' -'greenhouse_' -'greatly_' -'gra' -'gone_' -'glo' -'gewissen_' -'gan' -'führende_' -'fruit_' -'freue_' -'fi' -'fairly_' -'eyes_' -'essen_' -'erscheinen_' -'ersch' -'erg' -'erforderlichen_' -'era' -'enormen_' -'eng_' -'enabled_' -'elite_' -'electoral_' -'durchzuführen_' -'drei' -'dishes_' -'dining_' -'dienen_' -'derart_' -'depend_' -'definiert_' -'ct_' -'convinced_' -'condition_' -'cing_' -'chl' -'cher' -'cas' -'ca' -'burg_' -'budgets_' -'break' -'black_' -'bie' -'beschäftigt_' -'beobachten_' -'bela' -'bekämpfen_' -'basiert_' -'ay_' -'author_' -'ange' -'ambitious_' -'ale' -'ain_' -'afrikanischen_' -'aff' -'adoption_' -'acts_' -'acceptable_' -'absence_' -'abgeschlossen_' -'[[_' -'Zug' -'Ziffer_' -'Zer' -'Zentral' -'Wirklichkeit_' -'Win' -'Weitere_' -'Wandel_' -'WLAN_' -'Völker_' -'Vorbereitung_' -'Volkes_' -'Vienna_' -'Verkehrs' -'Verkehr_' -'Us' -'Umfeld_' -'Tele' -'Teilnahme_' -'Tar' -'Swedish_' -'Summit_' -'Suche_' -'Strukturen_' -'Straf' -'Stellen_' -'Stadtzentrum_' -'Sport' -'Spitze_' -'Spiel' -'Special_' -'Sollte_' -'Sel' -'Science_' -'Sche' -'Samsung_' -'Saint_' -'Saa' -'Rou' -'Rest' -'Research_' -'Rahmen' -'Protokoll_' -'Pay' -'Pan' -'Paket_' -'Optionen_' -'ON_' -'Nutzer_' -'Mon' -'Mitteln_' -'Menschen' -'Maß_' -'Martin_' -'Mac' -'MEPs_' -'Los_' -'LONDON_' -'Körper_' -'Kräfte_' -'Konvent_' -'Kommunikations' -'Kil' -'Kandidaten_' -'Java_' -'Japans_' -'Gu' -'GmbH_' -'General' -'Gefühl_' -'Gedanken_' -'Gebiete_' -'Gas' -'Garten_' -'Fähigkeiten_' -'For' -'Firstly_' -'Fach' -'Export' -'Ex' -'Erklärungen_' -'Eisenbahn' -'ES' -'EA' -'Druck' -'Dor' -'Der' -'DS' -'Czech_' -'Ci' -'Chris_' -'Chinesen_' -'Check_' -'Che' -'CIA_' -'CH' -'Bor' -'Beweis_' -'Beschäftigungs' -'Bei' -'Ball' -'Bade' -'BI' -'Atlantic_' -'Argentinien_' -'Apartment_' -'Angelegenheit_' -'Amerikaner_' -'Aktien' -'Ag' -'Absicht_' -'Ab' -'AC' -'?' -'47_' -'16' -'06_' -'03_' -'00' -'...' -',' -'!' -'і' -'п' -'ни' -'н' -'л' -'ück' -'übrigen_' -'öl' -'öffentlich_' -'ía_' -'än' -'Überprüfung_' -'Ü' -'zweifellos_' -'zentrum_' -'zahl' -'ystem_' -'xi' -'wirken_' -'welt_' -'wall_' -'waiting_' -'wait_' -'wachsenden_' -'wa_' -'vorgesehenen_' -'vorgeschlagenen_' -'visitors_' -'vir' -'verstärkt_' -'verleihen_' -'urs_' -'unmöglich_' -'unity_' -'unfortunately_' -'ug' -'uer' -'uel' -'typical_' -'tt' -'tit' -'tionen_' -'threats_' -'threatened_' -'tet_' -'teile_' -'sustained_' -'sur_' -'suitable_' -'submit_' -'strongly_' -'strength_' -'stimulus_' -'steuer' -'ster' -'steigern_' -'stattfinden_' -'sozial' -'son' -'sky_' -'ski' -'sinnvoll_' -'sight_' -'settlement_' -'sen' -'selbst' -'seite_' -'sei' -'seat_' -'schriftlich_' -'schlecht_' -'scher_' -'scheinen_' -'rou' -'rk' -'ride_' -'rge' -'reply_' -'rep' -'rend' -'remaining_' -'relief_' -'reliable_' -'regimes_' -'reb' -'reagieren_' -'raising_' -'quarter_' -'prove_' -'prospect_' -'proposing_' -'prison_' -'praktisch_' -'possibly_' -'plenty_' -'pattern_' -'outdoor_' -'operate_' -'ola' -'obtain_' -'nsi' -'nommen_' -'nom' -'nk_' -'neighbors_' -'necessarily_' -'nda' -'mögen_' -'mp' -'movements_' -'monitor_' -'mm' -'meter_' -'med_' -'managing_' -'maintenance_' -'lor' -'loo' -'lokale_' -'lly_' -'liked_' -'lier' -'lie_' -'leu' -'length_' -'legitimacy_' -'lad' -'kti' -'kostet_' -'konzentriert_' -'ite' -'islands_' -'invest_' -'interface_' -'ini' -'ingly_' -'immt_' -'ih' -'iel' -'identified_' -'hundred_' -'hr' -'hoffen_' -'hing_' -'highlight_' -'hi' -'heißen_' -'hearing_' -'harus_' -'han' -'halt_' -'globalisation_' -'gerichtet_' -'geraten_' -'generated_' -'gelegt_' -'gehalten_' -'freundlich_' -'frei' -'foot_' -'fonds_' -'festgestellt_' -'false_' -'falling_' -'experienced_' -'estate_' -'eröffnet_' -'erwähnt_' -'erstellen_' -'erfolgt_' -'ered_' -'entscheidenden_' -'entscheidende_' -'enten_' -'ent' -'enhance_' -'ene' -'ending_' -'enables_' -'eg' -'edited_' -'ed' -'ect' -'drop_' -'drink_' -'dramatic_' -'didn_' -'demonstrated_' -'delivered_' -'delay_' -'dal' -'customer_' -'ction_' -'credibility_' -'counter' -'controlled_' -'contracts_' -'considering_' -'confirm_' -'conclusions_' -'client_' -'cit' -'church_' -'chosen_' -'chain_' -'browser_' -'bo' -'bin' -'bezahlt_' -'betroffen_' -'besuchen_' -'besonderen_' -'besitzt_' -'beschränkt_' -'bequem_' -'behalten_' -'begonnen_' -'bedeuten_' -'bas' -'bahwa_' -'aut_' -'ausgesprochen_' -'audio_' -'ative_' -'association_' -'argued_' -'approximately_' -'applies_' -'applicable_' -'ano' -'alt' -'allies_' -'alb' -'akzeptiert_' -'aka' -'aircraft_' -'afford_' -'advantages_' -'adults_' -'adjustment_' -'absch' -']]._' -'Zahlen_' -'Yu' -'Währungsunion_' -'Women_' -'Without_' -'Wien_' -'Webseite_' -'Web' -'Vorsitzenden_' -'Vorschlägen_' -'Voraussetzung_' -'Verhandlungs' -'Verg' -'Verbreitung_' -'Verbraucher' -'Unternehmens_' -'USD_' -'USB_' -'UR' -'Tro' -'Then_' -'Thailand_' -'Termin' -'Ter' -'Technik_' -'Tal' -'Tages' -'TE' -'Symbol_' -'Sub' -'Start_' -'Sitzung_' -'Sim' -'Schutz' -'Schau' -'Reise_' -'Rechnung_' -'Ratsvorsitz_' -'Pool_' -'Player_' -'Plan' -'Partei' -'Par' -'Palästinenser_' -'Palace_' -'Pact_' -'PPE_' -'Ob' -'OECD_' -'Nr_' -'Note_' -'Nos_' -'Nigeria_' -'Nacht' -'Märkten_' -'Myanmar_' -'Munich_' -'Mittelpunkt_' -'Mitgliedern_' -'Mission_' -'Ministerpräsident_' -'Minderheiten_' -'Micro' -'Market_' -'Mag' -'Len' -'Laufe_' -'Labour_' -'Kunst' -'Kooperation_' -'Kontinent_' -'Kim_' -'Israels_' -'Indian_' -'Hussein_' -'Hostelsclub_' -'Handeln_' -'Hamburg_' -'Hal' -'Gründung_' -'Glaubwürdigkeit_' -'Gesch' -'Geh' -'Frühstücksbuffet_' -'Frei' -'Fi' -'Fernsehen_' -'Fal' -'Faktor_' -'Express_' -'Environment_' -'Entwicklungen_' -'Energy_' -'Eltern_' -'Due_' -'Dienste_' -'Die' -'Dep' -'Dem_' -'Datenbank_' -'Cyprus_' -'Cor' -'Company_' -'Cold_' -'Charter_' -'Charles_' -'Char' -'CI' -'CAN' -'Buch' -'Brussels_' -'Blog_' -'Bild' -'Bewertung_' -'Belarus_' -'Before_' -'Bedenken_' -'Back' -'Audio_' -'Association_' -'Arbeitskräfte_' -'Alt' -'Aktionen_' -'Adresse_' -'Abgeordnete_' -'AT' -'3D_' -'37_' -'24' -'20' -'193' -'191' -'09_' -'03' -'/ _' -'/' -') - _' -'() _' -'"' -' -- _' -'” (_' -'н_' -'е_' -'überzeugen_' -'Öffnung_' -'zusätzlichen_' -'zi' -'yn' -'yes_' -'ye_' -'wächst_' -'wood_' -'wonderful_' -'wishes_' -'wireless_' -'weist_' -'weather_' -'wave_' -'wahr' -'vorzu' -'vorgesehen_' -'visible_' -'vis_' -'verändern_' -'verteidigen_' -'vert' -'verlangt_' -'verhindert_' -'ven' -'uren_' -'una' -'uk' -'tzung_' -'tur' -'trouble_' -'trip' -'trillion_' -'treaty_' -'totally_' -'top' -'tele' -'teil_' -'tas_' -'symbol_' -'swi' -'sum_' -'successfully_' -'streets_' -'strategischen_' -'strategische_' -'ste' -'stag' -'sso' -'spricht_' -'spot_' -'somewhat_' -'solely_' -'sodass_' -'sichern_' -'scho' -'schlägt_' -'schi' -'sch' -'sbe' -'sauna_' -'sale_' -'rta' -'roughly_' -'rischen_' -'rg_' -'restore_' -'residential_' -'rescue_' -'removed_' -'religion_' -'regularly_' -'reflection_' -'ree' -'rechte_' -'reaction_' -'rare_' -'qualität_' -'preparation_' -'preise_' -'populist_' -'pollution_' -'pli' -'pleasant_' -'pit' -'pin' -'personnel_' -'perform_' -'patent_' -'passiert_' -'passed_' -'outstanding_' -'ott' -'orm' -'op_' -'ology_' -'olin' -'ol_' -'od' -'noise_' -'nke' -'nights_' -'nicht' -'ndi' -'muslimischen_' -'mpe' -'motor_' -'momentan_' -'ml' -'mindestens_' -'meaningfully_' -'mba' -'markt' -'manche_' -'maintaining_' -'lla' -'lived_' -'linie_' -'liches_' -'liberalisation_' -'let' -'lar' -'lan_' -'kur' -'kontrollieren_' -'kom' -'kin' -'ken' -'karte_' -'jenen_' -'jemand_' -'jegliche_' -'japanische_' -'ita' -'iso' -'installed_' -'ins' -'ingen_' -'inform_' -'inevitable_' -'index_' -'inden_' -'incomes_' -'imbalances_' -'ien' -'ida' -'hä' -'humanity_' -'http_' -'hoo' -'hingegen_' -'hi_' -'heavy_' -'hardware_' -'guarantees_' -'grammatically_' -'gradually_' -'gr' -'gezogen_' -'gestärkt_' -'genügend_' -'genutzt_' -'generations_' -'gene' -'gem' -'gefordert_' -'gan_' -'fördert_' -'free' -'frage_' -'forest_' -'flo' -'firstly_' -'fest' -'fer' -'fen' -'female_' -'fehlt_' -'fees_' -'fears_' -'fe' -'ez_' -'explained_' -'excess_' -'exception_' -'ex' -'estimated_' -'es' -'erreichbar_' -'erinnert_' -'erf' -'er' -'entstanden_' -'entlang_' -'entitled_' -'enst' -'eni' -'ender_' -'ende' -'ences_' -'en' -'eliminate_' -'ektor_' -'einerseits_' -'ei' -'egen_' -'echte_' -'dro' -'driver_' -'driven_' -'door_' -'discussing_' -'discrimination_' -'dis' -'direkten_' -'diplomacy_' -'dimension_' -'dent' -'deiner_' -'declared_' -'daraus_' -'dapat_' -'dans_' -'cru' -'cou' -'copy_' -'convergence_' -'conventional_' -'contextually_' -'containing_' -'contained_' -'cons' -'connections_' -'conce' -'computers_' -'col' -'co' -'classic_' -'ckt_' -'civilian_' -'chten_' -'charming_' -'channels_' -'cash_' -'capitalism_' -'cad' -'blue_' -'bislang_' -'bildung_' -'bewältigen_' -'betreffen_' -'beschleunigen_' -'ben' -'bel' -'bei' -'beginnt_' -'bef' -'barriers_' -'bal' -'baby_' -'ba_' -'ausgezeichneten_' -'aufzubauen_' -'aufgezahlt_' -'attitude_' -'assure_' -'asi_' -'arbeits' -'appointed_' -'amounts_' -'alter_' -'alle' -'album_' -'ala' -'ag' -'affects_' -'adi' -'acknowledge_' -'achieving_' -'acce' -'abuse_' -'Zustand_' -'Zeichen_' -'Ya' -'Wohl' -'Wird_' -'Wiki' -'Wettbewerbs' -'Wa' -'Volkswirtschaft_' -'Veränderung_' -'Verträge_' -'Verf' -'Verbot_' -'Verbesserungen_' -'Up' -'Universität_' -'Under_' -'Tun' -'Ti' -'The' -'Süden_' -'Sy' -'Sur' -'Stärke_' -'Straßen_' -'Stellungnahme_' -'Stability_' -'Staats_' -'Spe' -'Slo' -'Ski' -'Sitz_' -'Schulen_' -'SM' -'SA_' -'Rund' -'Rom' -'Regional_' -'Regierungschefs_' -'Ratschlag_' -'Quellen_' -'Puerto_' -'Programms_' -'Prinzip_' -'Pra' -'Port' -'Para' -'Paper_' -'Palestine_' -'PL' -'Os' -'Orten_' -'Open' -'OK_' -'Norden_' -'Non_' -'Netz_' -'Nacht_' -'NO' -'Mont' -'Ministers_' -'Mel' -'Mehr_' -'May' -'Man' -'Malaysia_' -'Lord_' -'Likewise_' -'Like_' -'Lebensmittel' -'LE' -'LA' -'Konsum_' -'Komfort_' -'Klima_' -'KDE_' -'Jahrzehnt_' -'Islands_' -'Innen' -'Herstellung_' -'Hersteller_' -'Herrschaft_' -'Grün' -'Gro' -'Gesetzgebung_' -'Gerichts' -'Geist_' -'Gegner_' -'Ge' -'Gast' -'Gang_' -'GE' -'Fotos_' -'Foto' -'Flüchtlinge_' -'Finland_' -'Finde_' -'Falls_' -'Est' -'Einwanderung_' -'Einstellung_' -'EC' -'Does_' -'Dienstleistungs' -'Dia' -'Demokraten_' -'Deb' -'Datei' -'DO' -'DC_' -'Class_' -'Canada_' -'C5_' -'Botschaft_' -'Beim_' -'BO' -'Avi' -'Auto_' -'Aufstieg_' -'As' -'Arbeitsmarkt_' -'Arbeiter_' -'Amendments_' -'Alter_' -'Ai' -'Agency_' -'Adobe_' -'Action_' -'Abschließend_' -'AU' -'://_' -'700_' -'49_' -'250_' -'1979_' -'1970_' -'196' -'186' -'08_' -'07_' -'.“_' -'-/_' -'," _' -'): _' -') (_' -'(' -'' ' -''' -' ….. _' -' )._' -'š' -'überlassen_' -'ös' -'öffnen_' -'ès_' -'ätzen_' -'äng' -'ähnlich_' -'Übersetzung_' -'Übersch' -'Ökonomie_' -'Ö' -'zy' -'zustimmen_' -'zusammenge' -'zusammen' -'zugrunde_' -'zuerst_' -'zeichen_' -'ys' -'yp' -'yer_' -'ws_' -'worst_' -'wonder_' -'wollten_' -'windows_' -'willkommen_' -'wesentliche_' -'warten_' -'wal' -'wahren_' -'vors' -'visited_' -'vis' -'versions_' -'verschärft_' -'verehrte_' -'vere' -'va_' -'ux_' -'ute' -'ut' -'ust' -'use' -'ursprünglichen_' -'ursprünglich_' -'uan_' -'twenty_' -'tti' -'tter_' -'travel' -'towns_' -'topic_' -'time' -'tersebut_' -'teachers_' -'taxpayers_' -'tag_' -'ta' -'sz' -'substance_' -'stun' -'stocks_' -'stem_' -'staying_' -'stayed_' -'statistics_' -'stadt_' -'sse' -'speakers_' -'spe' -'soil_' -'sla' -'sis_' -'sicherheit_' -'shed_' -'shares_' -'serviert_' -'sensible_' -'seg' -'see' -'schä' -'savings_' -'saved_' -'satellite_' -'sal' -'ry' -'rund' -'rst' -'rre' -'rot' -'roll_' -'ris' -'richtlinie_' -'richtigen_' -'revolutionary_' -'retten_' -'retirement_' -'reserve_' -'remind_' -'religiösen_' -'reject_' -'ref' -'reasonable_' -'ram' -'que' -'prä' -'prepare_' -'praktische_' -'pr' -'ports_' -'porta' -'politically_' -'phenomenon_' -'pem' -'pass' -'parallel_' -'par_' -'pal' -'oy' -'operational_' -'oma' -'offices_' -'offene_' -'obstacles_' -'näher_' -'nz' -'ntr' -'nto' -'nt' -'notwendige_' -'noted_' -'nochmals_' -'nc' -'nahm_' -'nachhaltig_' -'my' -'mut' -'mother_' -'mixed_' -'mittel' -'mic' -'messen_' -'marks_' -'mand' -'manager_' -'machines_' -'ländlichen_' -'ländern_' -'ls' -'listen_' -'lin_' -'ließen_' -'li_' -'lei' -'lea' -'lawyers_' -'langfristig_' -'lam' -'kurze_' -'kov' -'kor' -'knew_' -'kinds_' -'kel' -'jungen_' -'jpg_' -'joined_' -'items_' -'interessiert_' -'intention_' -'input_' -'innovations_' -'indicated_' -'inde' -'inc' -'in' -'ili' -'igt_' -'ified_' -'ideological_' -'ich' -'ible_' -'höchste_' -'hren_' -'hole_' -'hm' -'hilft_' -'hier' -'hervor_' -'heraus_' -'heil' -'heavily_' -'ham_' -'gäbe_' -'gro' -'gets_' -'gesellschaft_' -'ges' -'gera' -'ger' -'genauso_' -'früheren_' -'frag' -'ford_' -'flights_' -'fla' -'finde_' -'ffer' -'fertig_' -'failures_' -'expand_' -'ese' -'escape_' -'ern' -'erheblich_' -'erb' -'epi' -'entwickelte_' -'ents_' -'ente_' -'enhanced_' -'engagement_' -'engage_' -'encouraging_' -'emi' -'emerged_' -'ema' -'eite_' -'einzu' -'einzigartigen_' -'einfachen_' -'eigener_' -'ef' -'echten_' -'eat_' -'drawn_' -'down' -'disc' -'died_' -'democracies_' -'dee' -'court_' -'corner_' -'convert_' -'contributions_' -'consequence_' -'conditioning_' -'components_' -'collection_' -'cold_' -'coalition_' -'ci_' -'ci' -'careful_' -'cal' -'cable_' -'bri' -'brand_' -'betreiben_' -'besitzen_' -'berg' -'beinahe_' -'behaupten_' -'behandeln_' -'begrenzt_' -'bathrooms_' -'attractions_' -'ator_' -'ates_' -'applying_' -'angesprochen_' -'angen_' -'amenities_' -'alen_' -'ah' -'affairs_' -'advertising_' -'adequate_' -'abroad_' -'Zugriff_' -'Zug_' -'Zu' -'Yes_' -'Währungsfonds_' -'Wor' -'Wirtschaftspolitik_' -'Who_' -'Where_' -'Weltkrieg_' -'Wall_' -'Videos_' -'Verh' -'Ve' -'Valencia_' -'Ursachen_' -'UN' -'Truppen_' -'Todes' -'Tiere_' -'Though_' -'Tests_' -'Terroristen_' -'Telefon' -'Tab' -'TER' -'System' -'Stimm' -'Stein' -'Stan' -'Sony_' -'Sho' -'Set_' -'Scho' -'Ruhe_' -'Rest_' -'Reise' -'Regulierung_' -'Regelungen_' -'Ratspräsidentschaft_' -'Rats' -'RD' -'Prüfung_' -'Produkt_' -'Polizei_' -'Polish_' -'Place_' -'Pla' -'Peter_' -'Palestinians_' -'Pala' -'Or_' -'Ol' -'OS' -'Nas' -'My' -'Moscow_' -'Moo' -'Moment_' -'Mittelmeer_' -'Meeres' -'Medien' -'Mass' -'Marktes_' -'Mad' -'MS_' -'MP' -'Licht_' -'Kurz_' -'Kr' -'Konsum' -'Konsequenzen_' -'Konjunktur' -'Komponenten_' -'Kilometer_' -'Ker' -'Ken' -'Keine_' -'Katastrophe_' -'Kap' -'Joseph_' -'Institution_' -'Insbesondere_' -'Inn_' -'Immobilien' -'Ihres_' -'IA' -'Ho' -'Having_' -'Haushalte_' -'Hauptbahnhof_' -'Gut' -'Golden_' -'Glück_' -'Gleichgewicht_' -'Gipfel_' -'Gew' -'Gemeinden_' -'Gegenstand_' -'GNU_' -'Fälle_' -'Fu' -'Freunde_' -'Freuen_' -'Fla' -'Fisch' -'Firma_' -'Fer' -'Fenster_' -'FA' -'Einheit_' -'Einhaltung_' -'ES_' -'Dutch_' -'Deutschlands_' -'Details_' -'Definition_' -'Day_' -'Dam' -'Cu' -'Common_' -'Comm' -'College_' -'Cam' -'CE_' -'CE' -'CD' -'Burg' -'Bulgaria_' -'Boden_' -'Bla' -'Bis' -'Betriebs' -'Besides_' -'Beschluss_' -'Berücksichtigung_' -'Berufs' -'Bedarf_' -'Bars_' -'Banken' -'Ba' -'Auge_' -'Auftrag_' -'Aufst' -'Aufnahme_' -'Are_' -'Arbeiten_' -'Ar' -'Ant' -'Anpassung_' -'Angela_' -'Angaben_' -'Anbetracht_' -'Amtszeit_' -'Ambiente_' -'All' -'AS' -'A3' -':' -'85_' -'60' -'34_' -'26' -'1993_' -'1992_' -'02' -'-' -') – _' -'({{_' -'  _' -'’._' -'у_' -'ж' -'во' -'ün' -'übertr' -'überaus_' -'ña_' -'ätze_' -'änder_' -'älteren_' -'Übergang_' -'Ärzte_' -'´_' -' – _' -'zuständig_' -'zin' -'ziemlich_' -'zieht_' -'zer_' -'zar' -'ystems_' -'wissenschaftlichen_' -'whereas_' -'western_' -'wel' -'wei' -'wachsen_' -'vorstellen_' -'vorlegen_' -'voller_' -'ving_' -'vieles_' -'vi' -'vern' -'verfügbar_' -'verbindet_' -'urge_' -'unterzeichnet_' -'unt' -'une' -'undoubtedly_' -'uncertainty_' -'unab' -'umzu' -'ult' -'uh' -'ug_' -'uf_' -'uen_' -'tätig_' -'twice_' -'tri' -'trends_' -'tours_' -'tough_' -'tle' -'tische_' -'tin' -'thy_' -'terrorists_' -'telah_' -'tea_' -'tackle_' -'syn' -'swe' -'surface_' -'subsequent_' -'su_' -'storage_' -'stimmt_' -'stic_' -'stein_' -'spaces_' -'soweit_' -'solid_' -'sm' -'sion' -'sh' -'se' -'schweren_' -'sche' -'san' -'rück' -'rte_' -'route_' -'robust_' -'rm' -'rkt_' -'rip' -'ringen_' -'rig' -'revenue_' -'reu' -'respected_' -'rent_' -'renewable_' -'rem' -'regeln_' -'reduzieren_' -'raw_' -'ratio_' -'rap' -'ral_' -'qualified_' -'puts_' -'pun' -'pt_' -'providers_' -'promises_' -'profile_' -'produktion_' -'pred' -'potentially_' -'possibilities_' -'pose_' -'pointed_' -'piel_' -'pic' -'ph' -'periods_' -'percentage_' -'percent_' -'passengers_' -'passenger_' -'para' -'oz' -'ow_' -'our' -'organised_' -'ordered_' -'ona' -'occurred_' -'obligation_' -'object_' -'nötigen_' -'nst' -'nsch' -'novel_' -'none_' -'nk' -'nine_' -'nic' -'ney_' -'newspaper_' -'neu' -'nder' -'nces_' -'ms' -'mountains_' -'mn' -'miles_' -'mes' -'mere_' -'menschliche_' -'medicines_' -'meals_' -'marquis_' -'lug' -'los' -'logical_' -'locations_' -'lobby_' -'ller_' -'lit' -'leaves_' -'layer_' -'lac' -'ky_' -'krieg_' -'klein_' -'kit' -'ize_' -'ists_' -'iron_' -'integriert_' -'informieren_' -'ine' -'incentive_' -'ij' -'ignored_' -'ifi' -'if' -'ics_' -'hs_' -'hot' -'hoped_' -'herrscht_' -'heim' -'happens_' -'handle_' -'haften_' -'grown_' -'griechische_' -'gri' -'grateful_' -'grad' -'gg' -'geöffnet_' -'gestaltet_' -'gering_' -'genannte_' -'gegen' -'gedr' -'gardens_' -'gang_' -'fying_' -'fu' -'fri' -'fourth_' -'foundation_' -'finanzieren_' -'financed_' -'feeling_' -'facto_' -'exciting_' -'everywhere_' -'eur_' -'eta' -'esta' -'erte' -'erscheint_' -'ero' -'erm' -'erlauben_' -'erk' -'erfüllt_' -'erfordern_' -'entw' -'enr' -'engine_' -'engaged_' -'enb' -'ely_' -'ell' -'eli' -'ein' -'eht_' -'eh' -'educational_' -'eco' -'eau_' -'eastern_' -'earth_' -'earned_' -'ea' -'don' -'distinguish_' -'difficulty_' -'dient_' -'di' -'describes_' -'deflation_' -'dedicated_' -'de' -'dates_' -'creative_' -'cre' -'constantly_' -'consistent_' -'clubs_' -'cleaning_' -'cla' -'ción_' -'citizen_' -'chal' -'centres_' -'causing_' -'castle_' -'cas_' -'bs' -'breath' -'boot_' -'boot' -'blind_' -'bild_' -'bilateral_' -'betroffenen_' -'betonen_' -'beschäftigen_' -'beschränken_' -'besch' -'berühmten_' -'beraten_' -'behörde_' -'begins_' -'beendet_' -'bedeutenden_' -'bea' -'bau_' -'backed_' -'awareness_' -'ausschuss_' -'art' -'ari_' -'architecture_' -'anzunehmen_' -'ani' -'angemessene_' -'angegebenen_' -'anc' -'anb' -'amongst_' -'alp' -'aks' -'air' -'affordable_' -'affect_' -'admit_' -'ade_' -'adalah_' -'accepting_' -']]' -'Zwischen_' -'Zweck_' -'Zwar_' -'Zusammenhalt_' -'Zivil' -'Zinssätze_' -'Zimmern_' -'Zei' -'Year_' -'Wirtschafts' -'William_' -'Wesentlichen_' -'Werk_' -'Weiß' -'Vol' -'Vision_' -'Vice_' -'Verd' -'Vel' -'Va' -'VI' -'Trotzdem_' -'Titel_' -'Thanks_' -'Terror' -'Terrasse_' -'Technology_' -'TO' -'Swiss_' -'Support_' -'Suite_' -'Such' -'Steuer_' -'Statt_' -'Staff_' -'Souveränität_' -'Sogar_' -'Signal_' -'Sierra_' -'Serie_' -'Sektoren_' -'Schwellenländer_' -'Schiff_' -'Sc' -'Sar' -'Resolution_' -'Referendum_' -'Ram' -'Rad' -'Profi' -'Produkten_' -'Poker_' -'Past' -'Pap' -'Pakete_' -'PRO' -'PE' -'Organe_' -'Oder_' -'Ob_' -'Nicht' -'News_' -'Mädchen_' -'Mubarak_' -'Mou' -'Module_' -'Mitgliedsstaaten_' -'Met' -'Menü_' -'Menschheit_' -'Maria_' -'Mac_' -'Life_' -'Lern' -'Küsten' -'Kur' -'Kra' -'Koordinierung_' -'Konvents_' -'Konflikte_' -'Kommunismus_' -'Komm' -'Kluft_' -'Kla' -'Kapital_' -'Kampagne_' -'Ji' -'Jerusalem_' -'Jede_' -'Is' -'IM' -'Haus' -'Hand' -'Haftung_' -'Großteil_' -'Gri' -'Grenz' -'Gewinne_' -'Geschäfte_' -'Georgia_' -'Garden_' -'GA' -'Fuß_' -'Form' -'Fischerei' -'Film' -'Ferner_' -'Federation_' -'Fair' -'Entscheidungsträger_' -'Entscheidungs' -'Ents' -'Effizienz_' -'EN' -'Dynamik_' -'Distribution_' -'Din' -'Digital_' -'Desk_' -'Delegation_' -'Del' -'Darum_' -'Cro' -'Colo' -'Climate_' -'Chairman_' -'Cat' -'Card_' -'Captain_' -'Call' -'CF' -'CC' -'Bug' -'Blue_' -'Blair_' -'Black_' -'Bewältigung_' -'Bevölkerungs' -'Beschlüsse_' -'Berg' -'Bereitschaft_' -'Bemerkungen_' -'Bemerkung_' -'Beiträge_' -'Beh' -'Baltic_' -'Balkans_' -'Außenpolitik_' -'Authority_' -'Arabia_' -'Any_' -'Anst' -'Anleger_' -'Anlagen_' -'Agrar' -'Agentur_' -'AI' -'; ' -'98_' -'97_' -'79' -'63_' -'59_' -'55_' -'41_' -'38_' -'237' -'2020_' -'182' -'.  _' -'...._' -'''_' -'''' -' !_' -'‘_' -'ра' -'но' -'ме' -'ма' -'ла' -'й' -'übers' -'ú' -'ände_' -'zugunsten_' -'zierung_' -'zieren_' -'zeug' -'yt' -'ye' -'yar' -'wunder' -'wu' -'woman_' -'wirksame_' -'wir' -'wife_' -'wiederholen_' -'weitgehend_' -'waters_' -'vorliegenden_' -'vorbei_' -'volume_' -'vollständige_' -'vollen_' -'violent_' -'vin' -'verursacht_' -'versetzt_' -'verpflichten_' -'verk' -'veri' -'verabschiedet_' -'variable_' -'unterstütze_' -'untergraben_' -'ungsp' -'underway_' -'understood_' -'unbea' -'ums' -'umgeben_' -'ular_' -'uk_' -'tte' -'trip_' -'tions' -'tief_' -'terror_' -'tennis_' -'tea' -'tat' -'stärkere_' -'stä' -'stylish_' -'stu' -'stellung_' -'speichern_' -'sou' -'solches_' -'smooth_' -'sleep_' -'showed_' -'shape_' -'securities_' -'secular_' -'schöne_' -'schu' -'schrieb_' -'schnelle_' -'schie' -'sake_' -'sah_' -'ruling_' -'ruhig_' -'rl' -'rich' -'ric' -'rg' -'resulted_' -'restructuring_' -'rest' -'ress_' -'respects_' -'respective_' -'resistance_' -'requirement_' -'renowned_' -'regionaler_' -'regarded_' -'reflected_' -'reduziert_' -'red' -'recorded_' -'reconstruction_' -'recommendations_' -'realen_' -'rc' -'raph' -'rant' -'ranging_' -'rain' -'rah' -'purchases_' -'ps' -'präsentiert_' -'proportion_' -'profound_' -'produktiv' -'produces_' -'previously_' -'preis_' -'practically_' -'positiv_' -'pol' -'plenary_' -'ple' -'plays_' -'piel' -'persistent_' -'peri' -'pea' -'pays_' -'password_' -'passieren_' -'partnerships_' -'palästinensischen_' -'ow' -'overlooking_' -'ous' -'ote' -'orts_' -'organized_' -'organisiert_' -'organisationen_' -'organis' -'ordnung_' -'opponents_' -'ond' -'odu' -'observers_' -'oa' -'nz_' -'ny_' -'nse' -'notice_' -'notably_' -'nnen_' -'nn' -'nken_' -'niveau_' -'niedrigen_' -'new' -'ner' -'neighbouring_' -'nch' -'nb' -'nab' -'mpo' -'mount_' -'modest_' -'mod' -'mil' -'mi' -'mend' -'marked_' -'mani' -'male_' -'ly' -'lounge_' -'lots_' -'logic_' -'lle' -'lität_' -'lin' -'liefern_' -'leg' -'langfristigen_' -'landscape_' -'lands_' -'konkreten_' -'komp' -'kin_' -'keineswegs_' -'ka' -'jährigen_' -'justified_' -'ji_' -'japanischen_' -'isn_' -'ions' -'involvement_' -'introducing_' -'installiert_' -'ino_' -'informal_' -'ines_' -'illi' -'id' -'ichts' -'iche_' -'ica' -'hör' -'hr_' -'home' -'hl_' -'hl' -'hilfreich_' -'herzlich_' -'hed_' -'harm_' -'happening_' -'größerer_' -'großem_' -'groß' -'graphics_' -'gh' -'gestiegen_' -'gestern_' -'geschieht_' -'geringere' -'gemein' -'gele' -'geeignet_' -'gains_' -'furnished_' -'freuen_' -'frequently_' -'fort_' -'folgende_' -'floors_' -'finanzierung_' -'fill_' -'fewer_' -'feld_' -'fate_' -'fashion_' -'fare_' -'faith_' -'failing_' -'fach' -'explore_' -'existieren_' -'exi' -'evaluation_' -'euros_' -'esti' -'erstmals_' -'erst' -'ers' -'error_' -'erhielt_' -'ergriffen_' -'entsprechen_' -'enterprise_' -'enf' -'enc' -'ements_' -'einzig_' -'einzelne_' -'einheitlichen_' -'edge_' -'economically_' -'ebene_' -'eas' -'durchführen_' -'druck_' -'drivers_' -'dor_' -'dnung_' -'diverse_' -'dite' -'diskutieren_' -'disco' -'discipline_' -'directed_' -'dig' -'dien' -'destination_' -'designs_' -'demonstrate_' -'demokratischer_' -'define_' -'decisive_' -'deals_' -'dead_' -'dea' -'dam' -'cti' -'ct' -'creates_' -'cosy_' -'cop' -'contributing_' -'constitute_' -'conse' -'commodity_' -'com' -'colours_' -'collaboration_' -'clo' -'cin' -'chtlich_' -'certainty_' -'cameras_' -'bs_' -'broken_' -'brief_' -'blu' -'beteiligt_' -'bet' -'besorgt_' -'bers' -'bericht_' -'bemühen_' -'bell' -'bekommt_' -'beinhaltet_' -'behaviour_' -'beha' -'begun_' -'begr' -'begegnen_' -'bedrooms_' -'bed' -'baru_' -'ay' -'avoided_' -'ausgezeichnete_' -'ausger' -'ausgehen_' -'ausgegeben_' -'aufmerksam_' -'assi' -'aspect_' -'aside_' -'asiatischen_' -'arrested_' -'array_' -'aro' -'arkt' -'are' -'arc' -'ara' -'apparent_' -'ap_' -'ant' -'ank' -'angel' -'amo' -'ament' -'ambi' -'altung_' -'ail' -'agi' -'aggressive_' -'adapt_' -'abkommen_' -'Zuständigkeit_' -'Zusammen' -'Zivilgesellschaft_' -'Zins' -'Zeitung_' -'Wikitravel_' -'Whi' -'Whether_' -'Welche_' -'Wald' -'Wachstums_' -'Vorsch' -'Vorbe' -'View_' -'Vier' -'Verständnis_' -'Vater_' -'Van_' -'Update_' -'Untersuchungen_' -'Universitäten_' -'Ums' -'Transport' -'Tat' -'TH' -'Switzerland_' -'Super' -'Stunde_' -'Studio_' -'Strukturreformen_' -'Strukturfonds_' -'Struktur_' -'Strom' -'Strategy_' -'Strasbourg_' -'Steigerung_' -'Spezialitäten_' -'Sowjetunion_' -'Similarly_' -'Siege' -'Show_' -'Set' -'Sep' -'Selbstverständlich_' -'Sec' -'Sea' -'Schweden_' -'Schlussfolgerungen_' -'Schicksal_' -'Schi' -'Schatten_' -'Sau' -'SO' -'Rückkehr_' -'Ruh' -'Romania_' -'Rob' -'Road_' -'River_' -'Richard_' -'Renten' -'Ren' -'Religion_' -'Regierungskonferenz_' -'Regi' -'Red' -'Raum' -'Projekt' -'Premier_' -'Point_' -'PSE_' -'PL_' -'Nu' -'Nonetheless_' -'Nie' -'Nero_' -'Nazi_' -'National' -'Nachrichten_' -'Nachricht_' -'Monopol' -'Mol' -'Mitgliedstaat_' -'Minute_' -'Mikro' -'Methoden_' -'Mein_' -'Maßnahme_' -'Massen' -'ME' -'MB_' -'League_' -'Laut_' -'LabVIEW_' -'Küste_' -'Künstler_' -'König_' -'Kä' -'Kulturen_' -'Krisen_' -'Kit' -'Kirche_' -'Kategorie_' -'Kas' -'Karten_' -'Kampf' -'Jones_' -'Je_' -'Ins' -'Innenstadt_' -'Indiens_' -'Index_' -'IR' -'IL' -'IG' -'IF' -'Has' -'Har' -'Hai' -'HTML_' -'Grundsatz_' -'Glauben_' -'Gerechtigkeit_' -'Gegens' -'Gas_' -'GS' -'GI' -'Fun' -'Früh' -'Format_' -'Fo' -'Flugzeuge_' -'Finnish_' -'Festlegung_' -'Fat' -'Errichtung_' -'Erm' -'Entw' -'Einbeziehung_' -'Ehe' -'Effekt_' -'Education_' -'ET' -'EP' -'Dritte_' -'Donald_' -'Diskussionen_' -'Diskriminierung_' -'Disk' -'Dis' -'Demokratien_' -'Clo' -'Chávez_' -'Cas' -'Cal' -'Cab' -'Budgets_' -'Budapest_' -'Box_' -'Block' -'Bill_' -'Betr' -'Besucher_' -'Besch' -'Belgium_' -'Beijing_' -'Be' -'Bahnhof_' -'B5_' -'Außen' -'Ausnahme' -'Arbeitgeber_' -'Anlass_' -'Anders_' -'An' -'Alm' -'Alles_' -'Alexander_' -'Abe_' -'Abe' -'AB' -'78_' -'7' -'28' -'2030_' -'1990er_' -'189' -'183' -'… _' -'• _' -'я_' -'ы_' -'ч' -'те' -'п_' -'не' -'ž' -'üsse_' -'üge_' -'üche_' -'überwinden_' -'überprüft_' -'ön' -'ó_' -'ét' -'ég' -'Ökonomien_' -'· _' -' ' -'{{_' -'zählt_' -'zurückzu' -'zunehmende_' -'wünsche_' -'wre' -'wohnen_' -'wohn' -'wohl' -'widerspr' -'wider' -'werte_' -'wer' -'wenden_' -'welcoming_' -'weiterge' -'web' -'water' -'wart' -'ware_' -'vorgenommen_' -'voraus_' -'virus_' -'verspricht_' -'verkaufen_' -'vent' -'veni' -'vel' -'valid_' -'ution' -'uss' -'usa' -'upgrade_' -'unlike_' -'university_' -'universities_' -'ungsm' -'umfangreiche_' -'ultra' -'ud' -'uch_' -'twin_' -'twe' -'tude_' -'tru' -'treat_' -'tobacco_' -'threshold_' -'tha' -'ters_' -'teilnehmen_' -'tech_' -'teaching_' -'targeted_' -'tap' -'tabl' -'supporters_' -'supplies_' -'sul' -'suites_' -'suggested_' -'successes_' -'studio_' -'string_' -'stories_' -'stor' -'stops_' -'sser' -'spüren_' -'spezifische_' -'spezielle_' -'solange_' -'sofern_' -'sma' -'sichtig' -'showing_' -'shortage_' -'shock_' -'sheet_' -'sharp_' -'sexual_' -'setzte_' -'serving_' -'senken_' -'selten_' -'seeks_' -'schön_' -'schnellen_' -'sb' -'sala' -'rä' -'ruhigen_' -'rob' -'rib' -'resolutions_' -'requested_' -'representation_' -'reporting_' -'replace_' -'repeatedly_' -'repeated_' -'repeat_' -'remarks_' -'reli' -'rein' -'regulated_' -'regret_' -'registration_' -'refused_' -'recommended_' -'rechtlichen_' -'recall_' -'rd' -'rb' -'rational_' -'ration_' -'rapporteurs_' -'ran' -'quo_' -'q_' -'q' -'proposes_' -'produziert_' -'privileged_' -'privacy_' -'premi' -'prefer_' -'ppe' -'populations_' -'pon' -'plu' -'phon' -'peak_' -'patterns_' -'parks_' -'oth' -'ose_' -'oro' -'orders_' -'opt_' -'ong_' -'oli' -'olen_' -'oh' -'offiziell_' -'ock' -'occasion_' -'nunmehr_' -'notion_' -'noti' -'not' -'nnte' -'nds_' -'natürlichen_' -'natur' -'nan_' -'namen_' -'mü' -'multiple_' -'multilateral_' -'mpa' -'movie_' -'moralische_' -'mon_' -'moments_' -'mo_' -'minimal_' -'mine_' -'meters_' -'merk' -'medi' -'meal_' -'mbi' -'mber' -'mas_' -'maritime_' -'mann_' -'man' -'magnificent_' -'lte_' -'losigkeit_' -'literature_' -'lis' -'lebt_' -'lebens' -'lat' -'kriege' -'kop' -'konfrontiert_' -'klicken_' -'killing_' -'key' -'kernel_' -'kern_' -'kation_' -'junge_' -'joining_' -'jahr_' -'itte' -'italienischen_' -'istischen_' -'islamischen_' -'is' -'ious_' -'ior' -'involving_' -'invested_' -'inva' -'intentions_' -'intelligence_' -'insufficient_' -'ino' -'inis' -'inf' -'indischen_' -'indi' -'impr' -'implies_' -'ils_' -'igkeiten_' -'ight_' -'idi' -'ica_' -'hängt_' -'humans_' -'hoping_' -'homes_' -'holds_' -'hit' -'hinter' -'hingewiesen_' -'hero' -'hergestellt_' -'helps_' -'hat' -'handel_' -'halbe' -'größeres_' -'grant_' -'grand' -'gor' -'gle' -'glass_' -'gewähren_' -'gesetz' -'gesellschaften_' -'gese' -'geplant_' -'geltenden_' -'gefährdet_' -'gefährden_' -'gebunden_' -'gal' -'fühlen_' -'fy_' -'fte_' -'fs_' -'forthcoming_' -'foot' -'follows_' -'fleet_' -'fitness_' -'fication_' -'fall' -'factor_' -'extraordinary_' -'extra' -'exclusion_' -'eva' -'etz' -'ete_' -'estr' -'esse' -'esi' -'ese_' -'erweisen_' -'erlangen_' -'erheblichen_' -'erhebliche_' -'equity_' -'entscheidend_' -'entgegen_' -'endo' -'emerge_' -'elt_' -'ela' -'eist' -'eintr' -'einfacher_' -'eilung_' -'eil' -'ehe' -'effektiv_' -'eer' -'eda' -'ea_' -'dus' -'dt_' -'drawing_' -'dos_' -'dominant_' -'dl' -'dit' -'disk_' -'direkte_' -'devoted_' -'det' -'derer_' -'dependent_' -'departure_' -'dep' -'den' -'delicious_' -'dealt_' -'cur' -'ctive_' -'corresponding_' -'copyright_' -'converted_' -'contents_' -'conta' -'constant_' -'considerably_' -'conservative_' -'concentrate_' -'con' -'composed_' -'component_' -'comply_' -'comp' -'colour' -'class' -'claimed_' -'cker_' -'cker' -'cht' -'chemical_' -'chel' -'charm' -'caught_' -'capabilities_' -'can' -'calendar_' -'bä' -'bun' -'buchen_' -'bubble_' -'bridge_' -'breiten_' -'breite_' -'boo' -'boa' -'bo_' -'blieb_' -'birth_' -'bild' -'betreffenden_' -'bestehende_' -'beseitigen_' -'beschließen_' -'bekannten_' -'bek' -'beige' -'befassen_' -'beauty_' -'beantworten_' -'bat' -'basieren_' -'badly_' -'autumn_' -'autonomy_' -'auswählen_' -'auswirken_' -'ausländische_' -'ause' -'aufrechtzuerhalten_' -'atz_' -'atz' -'attempted_' -'ath' -'articles_' -'arten_' -'arrived_' -'armen_' -'arise_' -'arian_' -'arge' -'anzuzeigen_' -'anywhere_' -'anf' -'ami' -'amerikanischer_' -'alo' -'alcohol_' -'ak_' -'agr' -'adopting_' -'acting_' -'aci' -'abwe' -']' -'Zusammens' -'Zo' -'Yo' -'Währungen_' -'Would_' -'Work_' -'Wladimir_' -'Winter_' -'Wild' -'Wikicars_' -'Wi_' -'Wel' -'Vorstellung_' -'Vorausschau_' -'Vielmehr_' -'Vielen_' -'Video' -'Verteidigungs' -'Versuche_' -'Vermi' -'Verm' -'Verlauf_' -'Verfolgung_' -'Verein' -'Valley_' -'VE_' -'Use_' -'Uns_' -'Umgang_' -'Treaties_' -'Tochter_' -'Tip_' -'Tim' -'Tier' -'Ticket_' -'Texas_' -'Syn' -'Sudan_' -'Stä' -'Stattdessen_' -'Start' -'Spannungen_' -'Sor' -'Sonnen' -'Sid' -'Shanghai_' -'Several_' -'Serbien_' -'Sei' -'Schweiz_' -'Schuld' -'Sauna_' -'SD' -'Roman' -'Republicans_' -'Rec' -'Punkten_' -'Presse' -'Preisen_' -'Pop' -'Politikern_' -'Pläne_' -'Planung_' -'PP' -'PI' -'Otherwise_' -'Opera_' -'Once_' -'Ober' -'Noch_' -'Niemand_' -'Natur' -'Nat' -'Mur' -'Mos' -'Morgen_' -'Mobile_' -'Migranten_' -'Messe' -'Mensch_' -'Mechanismen_' -'Marketing_' -'Mark_' -'Mario_' -'Mari' -'Manhattan_' -'Mangel_' -'Mala' -'Luc' -'Links_' -'Libya_' -'Lea' -'LateRooms_' -'Ladies_' -'Lab' -'LL' -'LE_' -'Können_' -'Kurz' -'Kurs_' -'Kunden' -'Kuba_' -'Konf' -'Klasse_' -'Keynes_' -'Key' -'Kauf_' -'Kat' -'Kal' -'KO' -'Journalisten_' -'Ja_' -'Interventionen_' -'Internationale_' -'Insgesamt_' -'Ing' -'Infrastruktur' -'Info' -'IS_' -'ISO_' -'Hu' -'Helsinki_' -'Hauses_' -'Handelss' -'Ham' -'Haft' -'Guests_' -'Großen_' -'Grab' -'Ges' -'Gentoo_' -'Generationen_' -'Gelder_' -'Gef' -'Garantie_' -'Ga' -'GB_' -'Führungs' -'Funds_' -'Frank_' -'Formular_' -'Florida_' -'Fischerei_' -'Fi_' -'Feld_' -'Farb' -'Europa' -'Empire_' -'Empfehlungen_' -'Emissions' -'Em' -'Elemente_' -'Element_' -'Einst' -'Eg' -'Dorf_' -'Disc' -'Deutschen_' -'Des_' -'Demo' -'Declaration_' -'Darin_' -'DER_' -'Cra' -'Constitutional_' -'Chu' -'CON' -'Bud' -'Branchen_' -'Bl' -'Bestandteil_' -'Bes' -'Beitritts' -'Beispielsweise_' -'Band' -'Ave' -'Ava' -'Australia_' -'Ausführungen_' -'Ausführung_' -'Arc' -'Arbeitslosen' -'Anschluss_' -'Ann' -'Anlage_' -'Anlage' -'Andernfalls_' -'Alli' -'Alle' -'Aktion_' -'Airlines_' -'Afrikas_' -'Abhängigkeit_' -'ASEAN_' -'AL' -'72_' -'600_' -'6' -'50' -'22' -'200' -'1994_' -'1971_' -'1967_' -'187' -'184' -'120_' -'1000_' -'04_' -'01' -'. „_' -''' (_' -'$_' -'’, _' -'қ' -'да' -'ł' -'ütte' -'üh' -'übersch' -'überdenken_' -'üb' -'öffentlicher_' -'äten_' -'änge_' -'ähnliche_' -'ão_' -'» _' -'  ' -'}} **{{_' -'zweit' -'zwei' -'zunehmenden_' -'zun' -'zugänglich_' -'zuges' -'zione_' -'zentren_' -'youth_' -'yma' -'yi' -'yard_' -'wonach_' -'wis' -'wirkt_' -'wirkliche_' -'whilst_' -'whi' -'wesen_' -'we' -'wan' -'wake_' -'voran' -'voluntary_' -'vollkommen_' -'vier' -'vielmehr_' -'verteilt_' -'verringert_' -'vermutlich_' -'vermitteln_' -'ury_' -'urgently_' -'urf_' -'unte' -'unsch' -'uno' -'united_' -'ungss' -'ungs_' -'undermine_' -'unange' -'umwelt' -'umso_' -'ultimate_' -'uct' -'uchen_' -'uche_' -'tzt_' -'typically_' -'ture_' -'tritt_' -'trial_' -'trag' -'tik_' -'tier' -'ticket_' -'territories_' -'tern' -'tely_' -'teilt_' -'tall' -'tal_' -'tag' -'süd' -'sé' -'sätze_' -'surprised_' -'surely_' -'sure' -'stützen_' -'ständigen_' -'struck_' -'strike_' -'stri' -'strategie_' -'sto' -'stie' -'stell' -'steel_' -'starts_' -'stammen_' -'stal' -'staatlicher_' -'staaten_' -'sst_' -'spoken_' -'specified_' -'sp' -'sozialer_' -'sons_' -'some' -'soldiers_' -'sna' -'sn' -'ska' -'shop_' -'sho' -'shadow_' -'senden_' -'scu' -'sci' -'schr' -'schla' -'schemes_' -'schaden_' -'sauber_' -'san_' -'rweise_' -'rop' -'rooted_' -'ron' -'rle' -'ris_' -'rightly_' -'rie_' -'ria' -'revealed_' -'reta' -'ret' -'reputation_' -'repair_' -'renoviert_' -'ren' -'reg' -'rede' -'recover_' -'recommendation_' -'rechten_' -'reas' -'reactions_' -'rea' -'rating_' -'radi' -'qui_' -'qui' -'question' -'quest_' -'quel' -'qu' -'punkte_' -'psycho' -'prose' -'prop' -'promoted_' -'proceedings_' -'pris' -'presents_' -'posed_' -'poorest_' -'poly' -'play' -'pho' -'pf' -'perubahan_' -'pert' -'permanently_' -'path' -'participants_' -'pace_' -'ously_' -'os' -'orte' -'organ' -'optional_' -'optimal_' -'oppose_' -'opi' -'oo' -'ona_' -'off' -'ode' -'occupation_' -'obtained_' -'objects_' -'ntu' -'nti' -'nswerte' -'normalen_' -'niedriger_' -'nger' -'neun_' -'netz_' -'nent_' -'ndes_' -'narrow_' -'nachhaltigen_' -'nachge' -'müsse_' -'mächtig' -'mä' -'myth' -'modell_' -'mitten_' -'mittels_' -'mistake_' -'missing_' -'minorities_' -'mid' -'messages_' -'mess' -'mes_' -'menye' -'menu' -'menjadi_' -'mela' -'mein' -'mea' -'manufacturers_' -'manchen_' -'makers_' -'lung_' -'lunch_' -'loved_' -'logy_' -'lm' -'lling_' -'lla_' -'lk' -'lists_' -'linguistic_' -'ling' -'light' -'ließ_' -'lieb' -'lev' -'lets_' -'lem' -'leistung_' -'lee' -'lde' -'lau' -'lasting_' -'last' -'lake_' -'kümmern_' -'kämpfen_' -'kä' -'kurzfristig_' -'kurzer_' -'ksi' -'kosten' -'kontrolliert_' -'kon' -'komme_' -'km' -'klär' -'kita_' -'ket' -'ke' -'kan' -'justify_' -'jemals_' -'jederzeit_' -'iter' -'irre' -'ique_' -'interessen_' -'intend_' -'instability_' -'ink' -'indicate_' -'inder' -'inadequate_' -'impression_' -'impa' -'imagin' -'ika' -'ignore_' -'ign' -'iges_' -'iesen_' -'ide' -'ichten_' -'icher' -'ib' -'holidays_' -'hohem_' -'hinder' -'hielt_' -'heritage_' -'herauszu' -'hence_' -'hau' -'handling_' -'haft_' -'gy_' -'gy' -'gui' -'granting_' -'gn' -'gh_' -'gewiss_' -'getragen_' -'geschrieben_' -'geringe_' -'genetic_' -'gelöst_' -'gefa' -'geboren_' -'gallery_' -'gaben_' -'förder' -'functioning_' -'ften_' -'friend_' -'freundliche_' -'franc' -'fossil_' -'fore' -'fon' -'focusing_' -'flying_' -'fly_' -'flug' -'flu' -'filled_' -'fic' -'favor_' -'farm_' -'familiar_' -'exploitation_' -'expert_' -'exceptional_' -'ewa' -'evi' -'eure_' -'etzen_' -'essi' -'erlassen_' -'erkennt_' -'erkannt_' -'eric' -'eo' -'entsteht_' -'entf' -'enge' -'enforcement_' -'ends_' -'empfangen_' -'els_' -'eit' -'eins_' -'einh' -'eingegangen_' -'eingebracht_' -'eignet_' -'eigenes_' -'egal_' -'een_' -'educated_' -'ede' -'economist_' -'echen_' -'duties_' -'dul' -'dte' -'dritte_' -'doctors_' -'doctor_' -'displayed_' -'disasters_' -'ding' -'dialog_' -'des' -'derzeitige_' -'dens' -'demo' -'demi' -'delighted_' -'defeat_' -'decorated_' -'debian_' -'deadline_' -'dd' -'davor_' -'dat' -'dar' -'dahin_' -'cted_' -'creditors_' -'covering_' -'countless_' -'correctly_' -'coordinated_' -'cooperate_' -'compact_' -'codes_' -'cle' -'clause_' -'classes_' -'chef_' -'checking_' -'channel_' -'chances_' -'centr' -'centers_' -'cast' -'cart' -'career_' -'cam' -'buses_' -'bug_' -'bright_' -'bond_' -'bol' -'bit' -'bisa_' -'bis' -'bill_' -'bilateralen_' -'bi_' -'bewirken_' -'bewahren_' -'besar_' -'beruhen_' -'bert' -'bel_' -'behoben_' -'behauptet_' -'beg' -'beantwortet_' -'bauen_' -'basi' -'bahn' -'aver' -'ausländischen_' -'ausgedrückt_' -'aufgeb' -'auff' -'aub' -'ate' -'assen_' -'assa' -'artists_' -'artistic_' -'appropriations_' -'annehmen_' -'angewiesen_' -'anerkannt_' -'anda' -'amp_' -'ambition_' -'alts' -'aktive_' -'ak' -'agen' -'aged_' -'ado_' -'adjustments_' -'actor_' -'achen_' -'acc' -'Zwei' -'Zuge_' -'Zen' -'Zahlungen_' -'Wit' -'Werke_' -'Wal' -'WE' -'Vorstellungen_' -'Vorsitz_' -'Vors' -'Vorlage_' -'Vorgehen_' -'Viertel_' -'Vertretern_' -'Verteidigungspolitik_' -'Verst' -'Using_' -'Unterkunft_' -'Ukrainian_' -'Tunisia_' -'Tru' -'Traum_' -'Transfer' -'Traditionen_' -'Town_' -'Tonnen_' -'Tok' -'Teilnehmer_' -'Tausende_' -'Taliban_' -'TO_' -'Sunday_' -'Straßburg_' -'Stra' -'Stimmung_' -'Steuerzahler_' -'Star' -'Staatsanleihen_' -'Soldaten_' -'Ska' -'Simbabwe_' -'Schätzungen_' -'Schnell' -'Schlüssel_' -'Schlag' -'Schie' -'Satz_' -'SU' -'SG' -'Russians_' -'Rot' -'Roll' -'Rock_' -'Regional' -'Ree' -'Rand_' -'Radio_' -'Qualitäts' -'Professor_' -'Power_' -'Portuguese_' -'Politiken_' -'Playa_' -'Planeten_' -'Pl' -'Pis' -'Philosophie_' -'Pf' -'Personal' -'Performance_' -'Pas' -'Partnership_' -'Partnerschaften_' -'PA' -'Orte_' -'Orient' -'Ok' -'Offenheit_' -'Notes_' -'Northern_' -'Normen_' -'Nizza_' -'Nation_' -'NGOs_' -'NG' -'NCC_' -'Mot' -'Mobil' -'Mittelmeer' -'Mitglieds' -'Missbrauch_' -'Minutes_' -'Mess' -'Menschenhandel_' -'Mei' -'Mau' -'Mark' -'Mallorca_' -'Mach' -'Maastricht_' -'Lö' -'Luft_' -'Ltd_' -'Lounge_' -'Lockerung_' -'Lizenz' -'Lim' -'Liberal' -'Legitimität_' -'Lee_' -'Landwirte_' -'Lake_' -'Lag' -'LO' -'Kritiker_' -'Korean_' -'Klimawandels_' -'Kir' -'Ki' -'Kara' -'Jugend' -'Jahrzehnte_' -'Invasion_' -'Internet' -'Indonesien_' -'Indonesia_' -'Indem_' -'Imp' -'Image_' -'Il_' -'Höchst' -'Hostels_' -'Hor' -'Hit' -'Hin' -'Hilfs' -'Hezbollah_' -'Have_' -'Hall_' -'Hafen_' -'Gua' -'Gil' -'Gewicht_' -'Geschwindigkeit_' -'Germans_' -'Gerichte_' -'Gericht_' -'Gegend_' -'Gard' -'Ganz_' -'Gal' -'GH' -'Friendly_' -'Freude_' -'Freiheiten_' -'Fre' -'Franc' -'Ford_' -'Flüchtlings' -'Fluss_' -'Fischer_' -'Finanzmärkte_' -'Fin' -'Fil' -'Festival_' -'Ferienwohnung_' -'Feld' -'FO' -'Erstellung_' -'Entschließungsantrag_' -'Ele' -'Einsch' -'Einkommens' -'Einer_' -'Eigenschaften_' -'EL_' -'Dun' -'Drogen_' -'Dreh' -'Dokument_' -'Di_' -'Description_' -'Democracy_' -'Dec' -'Dauer_' -'DS9_' -'Cons' -'Conf' -'Com' -'Cit' -'Church_' -'Christ_' -'Chamber_' -'Canon_' -'Camp' -'Cambridge_' -'CS' -'Bürgerinnen_' -'Bürger' -'Börse_' -'Bulgarien_' -'Briten_' -'Blut' -'Blo' -'Ble' -'Betrieb_' -'Beteiligung_' -'Besitz' -'Bereitstellung_' -'Beratung_' -'Begleit' -'Bedürfnisse_' -'Bed' -'Be_' -'Bauern_' -'BS' -'BB' -'Außenminister_' -'Aussch' -'Ausl' -'Assad_' -'Armen_' -'Ari' -'Arch' -'Anzeichen_' -'Anti_' -'Annehmlichkeiten_' -'Anl' -'Anhänger_' -'Angebote_' -'Andere_' -'Ana' -'Amb' -'Ak' -'Air' -'Af' -'Acc' -'Abwe' -'Abr' -'Abd' -'Ab_' -'AV' -'92_' -'68_' -'51_' -'43_' -'40' -'32' -'31' -'19th_' -'02_' -'" ' -' -' -' ,,_' -' ), _' -'ң_' -'та' -'й_' -'и_' -'ды_' -'ε' -'übernimmt_' -'ökonomischen_' -'éa' -'ätig' -'ändische' -'ägen_' -'án_' -'ßt_' -'Überzeugung_' -'Überwachungs' -'Übers' -'Überleben_' -'Übergangs' -'Überblick_' -'zusammenarbeiten_' -'zug_' -'zuf' -'zub' -'zerstört_' -'zerstören_' -'zel' -'zeiten_' -'zahl_' -'younger_' -'wr' -'worrying_' -'win' -'wiederholt_' -'wichtiges_' -'welchen_' -'weiten_' -'watch_' -'wasser' -'warn' -'wahr_' -'wahl_' -'vorauss' -'voices_' -'vit' -'viertel_' -'viable_' -'verwaltung_' -'verr' -'verkehr_' -'verhalten_' -'verboten_' -'vely_' -'var' -'ute_' -'urn' -'untersuchen_' -'unterscheiden_' -'unta' -'unc' -'unan' -'unabhängigen_' -'umge' -'umb' -'uit' -'uar' -'uan' -'türkischen_' -'tsche' -'tsch' -'tro_' -'trib' -'treiben_' -'trees_' -'transportation_' -'transitional_' -'transferred_' -'tranquil' -'tragic_' -'tracks_' -'tra_' -'tour_' -'topics_' -'tone_' -'ton' -'tlich_' -'tion' -'ting' -'till_' -'tig' -'tifi' -'tie' -'tickets_' -'tic' -'tes' -'territorial_' -'terra' -'teils_' -'tehen_' -'taxi_' -'tables_' -'systemen_' -'systematic_' -'sy' -'switch_' -'sustain_' -'surf' -'supplementary_' -'summe' -'suited_' -'sucht_' -'subs' -'strict_' -'stream_' -'strange_' -'stood_' -'sting_' -'stimme_' -'steigenden_' -'stei' -'stars_' -'stammt_' -'stad' -'staat_' -'spät_' -'spiel_' -'spezifischen_' -'socio' -'sli' -'sis' -'significance_' -'sicheren_' -'sicher' -'shocks_' -'shel' -'settlements_' -'seri' -'selben_' -'sehe_' -'seh' -'seconds_' -'schutz_' -'schrittweise_' -'schme' -'schm' -'satz_' -'sation_' -'sand' -'safer_' -'sad' -'sab' -'rv' -'rut' -'rum_' -'row_' -'roots_' -'rme' -'risk' -'rie' -'rian_' -'ri' -'rhetoric_' -'residence_' -'rese' -'reproduction_' -'rene' -'remo' -'remarkable_' -'refers_' -'reductions_' -'recognised_' -'rechtliche_' -'rec' -'realistic_' -'realer_' -'react_' -'re' -'rbe' -'rau' -'rati' -'rail_' -'ragen_' -'racism_' -'quent' -'purchasing_' -'pur' -'prozess_' -'provider_' -'proven_' -'progressive_' -'professor_' -'prisoners_' -'pride_' -'predicted_' -'praktischen_' -'ppi' -'potenzielle_' -'pledge' -'plan' -'pipeline_' -'pioneer' -'pil' -'photograph' -'phones_' -'persönliche_' -'personenbezogene' -'perfekt_' -'pen_' -'painful_' -'pain_' -'overn' -'ov_' -'ost' -'ort' -'originally_' -'organiz' -'ore' -'optimale_' -'ommen_' -'omi' -'om_' -'oll' -'og' -'oftmals_' -'offen' -'odi' -'nü' -'nter' -'notes_' -'northern_' -'nj' -'nischen_' -'nische_' -'niedrig_' -'nieder' -'ngi' -'negativen_' -'nci' -'nche' -'natürliche' -'mögliche_' -'märkten_' -'murder_' -'mou' -'motiv' -'mona' -'mol' -'modify_' -'moderate' -'mode' -'mittleren_' -'minor_' -'minds_' -'min' -'migrants_' -'mi_' -'mete' -'mereka_' -'memper' -'mel' -'medicine_' -'meat_' -'measured_' -'mail' -'magic_' -'lös' -'lum' -'llo' -'lled_' -'lig' -'lift_' -'lieber_' -'lid' -'library_' -'lenken_' -'leidet_' -'leich' -'legte_' -'legally_' -'leak' -'lbe' -'lav' -'lateral' -'langem_' -'lang' -'lai' -'kürze' -'künftigen_' -'könne_' -'kö' -'ktor' -'korrekt_' -'klaren_' -'kill_' -'kesehatan_' -'kas' -'kana' -'jüngste_' -'jedenfalls_' -'iz' -'iv' -'ition_' -'itali' -'isation_' -'ional_' -'involves_' -'invite_' -'investigation_' -'intervene_' -'inten' -'institutionellen_' -'inl' -'incredible_' -'incon' -'immense_' -'illegalen_' -'illegale_' -'illa' -'ility_' -'iel_' -'ideale_' -'hyper' -'hundert_' -'hopes_' -'hood_' -'holders_' -'histori' -'hinzufügen_' -'hinge' -'hierbei_' -'hes' -'hervorragende_' -'heraus' -'hera' -'hee' -'heads_' -'handels' -'halt' -'hal' -'hab' -'günstig_' -'gültige' -'gä' -'gte_' -'größtenteils_' -'grundsätzlich_' -'gramm' -'governing_' -'gos' -'gleichermaßen_' -'gkeit_' -'gi_' -'gesunde' -'gesch' -'genuinely_' -'geni' -'gelingt_' -'gefährliche_' -'gee' -'gebiet_' -'gate' -'fällen_' -'fur' -'fundamental' -'fuels_' -'fro' -'freely_' -'fre' -'fortsetzen_' -'forschung_' -'forests_' -'football_' -'folge' -'flee' -'flash_' -'flag_' -'fire' -'finish_' -'fin' -'figur' -'ffe' -'festgelegten_' -'fanden_' -'fam' -'fails_' -'fahrt_' -'expenses_' -'expanded_' -'exp' -'examine_' -'exa' -'eti' -'esch' -'erzeugt_' -'erwähnen_' -'erreichte_' -'erne' -'erl' -'erfolgreiche_' -'entre' -'enthalt_' -'enth' -'entertainment_' -'entering_' -'ensch' -'ens' -'ener_' -'encouraged_' -'ena' -'employed_' -'emergence_' -'ement_' -'embrace_' -'em' -'eller_' -'elektronischen_' -'electric_' -'eld_' -'eite' -'einzuführen_' -'einverstanden_' -'eintreten_' -'einr' -'einl' -'einheitliche_' -'eingerichtete_' -'ego' -'egen' -'efe' -'ec' -'easing_' -'dw' -'durchzusetzen_' -'dung_' -'dun' -'ds' -'drängen_' -'dream_' -'dot' -'dor' -'doors_' -'distinction_' -'dispute_' -'disposal_' -'diskutiert_' -'dise' -'directory_' -'director_' -'dim' -'dieselbe_' -'dictatorship_' -'determination_' -'deswegen_' -'destroy_' -'dependence_' -'departments_' -'department_' -'demonstrations_' -'definitely_' -'dating_' -'dark_' -'cz' -'cut' -'critique_' -'credits_' -'crat' -'cra' -'count_' -'corporations_' -'conventions_' -'convenience_' -'contributed_' -'contextual_' -'constructive_' -'considerations_' -'conduct_' -'cond' -'compete_' -'coherent_' -'codecision_' -'chtig' -'chte' -'chau' -'characterized_' -'cel' -'cc' -'catch_' -'carrying_' -'busy_' -'bre_' -'bought_' -'bot_' -'boards_' -'black' -'bio' -'binding_' -'bigger_' -'bezieht_' -'beträgt_' -'bestätigt_' -'bes_' -'bere' -'bera' -'bene' -'bele' -'beings_' -'begründet_' -'beglückwünschen_' -'begl' -'beeinflussen_' -'bedroh' -'bedeutende_' -'barer_' -'barely_' -'band_' -'backing_' -'az' -'aw' -'authoritarian_' -'ausb' -'auftreten_' -'aufgeführt_' -'atte' -'att_' -'att' -'ato_' -'asi' -'arte' -'aria' -'argumentieren_' -'apparently_' -'api' -'ap' -'ants_' -'ano_' -'anhaltenden_' -'angebracht_' -'ands_' -'anderswo_' -'alternatives_' -'alongside_' -'ali' -'aktuell_' -'ahm' -'aga' -'adverse_' -'adds_' -'adaptation_' -'acknowledged_' -'ack' -'accused_' -'acceptance_' -'Zwecke_' -'Zusätzlich_' -'Zusatz' -'Zimmerservice_' -'Zielen_' -'Zerstörung_' -'You' -'Wirksamkeit_' -'Wir' -'Werten_' -'Werkzeuge_' -'Wellness_' -'Welcome_' -'Weiter' -'We' -'Wander' -'Wall' -'Wahrscheinlichkeit_' -'WHO_' -'Vorbild_' -'Virus_' -'Vir' -'Vic' -'Verz' -'Versprechen_' -'Verringerung_' -'Vermögenswerte_' -'Verkauf_' -'Verhaltens' -'Vereinigte_' -'Vene' -'Urteil_' -'Umge' -'UE' -'Tätigkeit_' -'Typ_' -'True_' -'Tool_' -'Tibet_' -'Three_' -'Ten' -'Temple_' -'Tee' -'Teams_' -'Südkorea_' -'Sus' -'Sul' -'Structural_' -'Stre' -'Still_' -'Stahl' -'Spo' -'Spielen_' -'Spezial' -'Spa' -'Sound_' -'Sky' -'Situationen_' -'Sit' -'Sir' -'Siehe_' -'Sicherheitspolitik_' -'Service' -'Section_' -'Scotland_' -'Schwäche_' -'Schwerpunkt_' -'Schreiben_' -'Schon_' -'Schli' -'Schle' -'Schiff' -'Scheitern_' -'Schalt' -'Sach' -'SE_' -'Rä' -'Ruf_' -'Rub' -'Rom_' -'Ret' -'Republican_' -'Rechtsstaatlichkeit_' -'Rechten_' -'Rechnungs' -'Rat' -'Rand' -'RO' -'RM' -'Qui' -'Prozess' -'Projekts_' -'Procedure_' -'Presse_' -'Potenzial_' -'Pos' -'Pol' -'Pflicht_' -'Passwort_' -'Parlaments' -'Pakt_' -'PRI' -'PR' -'PO' -'PD' -'Out_' -'Orl' -'Organization_' -'Offen' -'Off' -'Od' -'Nichtraucherzimmer_' -'Netzwerk_' -'Net' -'NI' -'Modern_' -'Mittel' -'Mir_' -'Mini' -'Minderheit_' -'Min' -'Mil' -'Mechanismus_' -'Mc' -'Maschinen_' -'Martin' -'Mar_' -'Mandat_' -'Manchmal_' -'Mak' -'Mai' -'MPEG_' -'MP3_' -'Löhne_' -'Long_' -'Lie' -'Leitung_' -'Lehr' -'Later_' -'Lac' -'LIN' -'Kru' -'Konvention_' -'Konto_' -'Kons' -'Kongress_' -'Kompromiss' -'Kompetenzen_' -'Komp' -'Kernel_' -'Kauf' -'Kai' -'KORE_' -'KE' -'KA' -'Jung' -'Jak' -'Jackson_' -'Jack' -'Internal_' -'Instruments_' -'Innovationen_' -'Inf' -'IP' -'ION_' -'ING_' -'ID' -'Hinzu' -'Herz' -'Henry_' -'Heil' -'Harvard_' -'HI' -'Gästen_' -'Gott_' -'Gor' -'Gleich' -'Glas' -'Gipfeltreffen_' -'Gh' -'Gewerkschaften_' -'Gener' -'Gem' -'Gel' -'Gehminuten_' -'Gebrauch_' -'Gar' -'Fuß' -'Fur' -'Friedens_' -'Fremd' -'Free' -'Frankreichs_' -'Frank' -'Framework_' -'Flughäfen_' -'Flash_' -'Firefox_' -'Feier' -'Fehl' -'Fast_' -'Farben_' -'Fahrzeuge_' -'Fa' -'Exp' -'Esta' -'Er' -'Energies' -'Elite_' -'Einwanderer_' -'Einstellungen_' -'Einnahmen_' -'Einb' -'Egyptian_' -'Edition_' -'EX' -'ER_' -'EM' -'EIB_' -'EI' -'EG_' -'ED' -'Dy' -'Dublin_' -'Drogen' -'Drive' -'Dritten_' -'Drittel_' -'Don_' -'Deswegen_' -'Department_' -'Denmark_' -'Dek' -'Debatten_' -'Cur' -'Cruz_' -'Country_' -'Cou' -'Colombia_' -'Col' -'Client_' -'Chef_' -'Chal' -'Ch' -'Card' -'Can' -'California_' -'Burma_' -'Bu' -'Brok_' -'Bro' -'Br' -'Beweise_' -'Beurteilung_' -'Bett' -'Berichten_' -'Beobachter_' -'Befürworter_' -'Befugnisse_' -'Beachtung_' -'Bea' -'Based_' -'BU' -'BC_' -'Ausz' -'Ausstattung_' -'Ausst' -'Ausr' -'Aus' -'At' -'Asyl' -'Aspekten_' -'Architektur_' -'Arafat_' -'Ansichten_' -'Ans' -'Anonymous_' -'Anliegen_' -'Ange' -'Andererseits_' -'Allein_' -'Act' -'Ac' -'Abschnitt_' -'Absatz_' -'Abf' -'AD' -'66_' -'62_' -'54_' -'46_' -'35' -'29' -'1985_' -'1980s_' -'180_' -'17' -'.) _' -'. - _' -'. &#_' -''', _' -' ?_' -' ; _' -' +_' -' + _' -' "' -'…"..._' -'” ' -'’' -'ül' -'üg' -'ü' -'év' -'éc' -'äußern_' -'äts' -'ät' -'ähl' -'äh' -'Übrigen_' -'Übertragung_' -'Übernachtung_' -'Übereinkommen_' -'Überdies_' -'Ära_' -'zweitens_' -'zuzu' -'zusch' -'zung_' -'zulassen_' -'zug' -'zu' -'ziel_' -'zie' -'zeigte_' -'zb' -'zahlungen_' -'yu' -'you' -'ym' -'yield_' -'yc' -'yan' -'würdigen_' -'wü' -'worthy_' -'wonder' -'wise_' -'willingness_' -'werke_' -'werfen_' -'welcomes_' -'weis' -'wards_' -'ward_' -'waffen_' -'vulnerability_' -'vorhandenen_' -'vollständigen_' -'viv' -'vil' -'vielfältige' -'vid' -'vessels_' -'veröffentlichte_' -'verä' -'verst' -'versorgung_' -'versichern_' -'versch' -'verlor_' -'verkauft_' -'verg' -'verbreitet_' -'venture_' -'ved_' -'vas' -'vacation_' -'uung_' -'uta' -'unterstützte_' -'unterge' -'unsustainable_' -'unions_' -'unin' -'ungsf' -'ung' -'unf' -'unentgeltlich_' -'underlying_' -'underground_' -'umfassen_' -'uis' -'ui' -'ugs' -'uga' -'ues_' -'ude' -'uch' -'tätigen_' -'täglichen_' -'tw' -'ture' -'trick' -'tras' -'trained_' -'traditions_' -'trace' -'tr' -'tolerance_' -'tip' -'thumbnail_' -'throw_' -'texts_' -'teuer_' -'tested_' -'termin' -'tene' -'tendency_' -'techniques_' -'tec' -'tau' -'tari' -'tab' -'sämtliche_' -'sympathy_' -'sver' -'survival_' -'surveillance_' -'suppliers_' -'supervision_' -'superior_' -'succ' -'subsequently_' -'strategi' -'straightforward_' -'str' -'stored_' -'steigt_' -'starten_' -'stand' -'stan_' -'stake_' -'stabiliz' -'stab' -'ssu' -'ssion_' -'sr' -'späten_' -'spl' -'spiegelt_' -'spiegel' -'speziell_' -'sole_' -'ske' -'situ' -'silence_' -'sil' -'side' -'sicherstellen_' -'shot_' -'sf' -'settings_' -'seperti_' -'sens' -'sending_' -'selling_' -'sekarang_' -'sek' -'screens_' -'score_' -'scientists_' -'schätzen_' -'schwer' -'schutz' -'schlimmsten_' -'schlechten_' -'schaften_' -'sama_' -'sai' -'rücken_' -'rü' -'rze' -'ruktur' -'rp' -'rose_' -'ront' -'rken_' -'rium_' -'rif' -'rier' -'rid' -'rh' -'rgi' -'revenues_' -'reve' -'restricted_' -'responses_' -'respect' -'reserved_' -'rer' -'render' -'religiöse_' -'rel' -'reich' -'regulate_' -'regionen_' -'reforme' -'recruit' -'receiving_' -'rece' -'reagiert_' -'read' -'rder' -'rans' -'rai' -'rage' -'queries_' -'qualifi' -'qi' -'pue' -'publicly_' -'proximity_' -'province_' -'promising_' -'prominent_' -'professionals_' -'producer_' -'problem' -'prinzip_' -'preserve_' -'preferred_' -'port' -'politi' -'poli' -'planen_' -'pillar_' -'performed_' -'perceived_' -'pe' -'paying_' -'past' -'parameters_' -'pala' -'pack' -'pa_' -'ov' -'orn' -'orit' -'ores_' -'order' -'ora_' -'opti' -'onne' -'onia_' -'ong' -'omm' -'oleh_' -'oke' -'ogen' -'og_' -'officers_' -'offens' -'offenbar_' -'occupied_' -'observed_' -'observations_' -'obliged_' -'nützlich_' -'nutz' -'nung_' -'nowadays_' -'novel' -'noticed_' -'nominal_' -'nne' -'nn_' -'nh' -'ngan_' -'net' -'neo' -'negotiate_' -'ndung_' -'ncy_' -'nati' -'nant_' -'nal' -'nachfrage_' -'märkte_' -'mächtigen_' -'má' -'mus' -'mot' -'mono' -'moderate_' -'mobility_' -'mmer_' -'minder' -'mier' -'met' -'merc' -'mente_' -'menc' -'mena' -'men' -'mema' -'melt' -'mehr' -'medieval_' -'maßen_' -'max_' -'mate' -'mam' -'machten_' -'lösung_' -'läge_' -'lut' -'lon_' -'logi' -'load_' -'llte_' -'listings_' -'lip' -'life' -'lic' -'leichter_' -'leib' -'legacy_' -'leb' -'lautet_' -'laufenden_' -'langer_' -'lah' -'lag' -'künftig_' -'kü' -'ktion_' -'kritisiert_' -'kri' -'kostenlose_' -'kontextuellen_' -'kontextuell_' -'konst' -'konse' -'komplexe_' -'kommunistischen_' -'komm' -'kne' -'kli' -'kleineren_' -'klarer_' -'kette_' -'kes_' -'kers_' -'kennt_' -'kem' -'kel_' -'keen_' -'kat' -'jährige_' -'jara' -'jar' -'ivi' -'ively_' -'iti' -'israelischen_' -'isi' -'isen_' -'iri' -'iranische_' -'ira' -'investiert_' -'interpretation_' -'internationales_' -'intends_' -'intelligent_' -'instructions_' -'instantly_' -'inspired_' -'inspection_' -'initiated_' -'inhabitants_' -'inflows_' -'indicators_' -'inclusion_' -'importantly_' -'ille' -'ill' -'ification_' -'ideology_' -'ic' -'hältnis' -'hydro' -'hor' -'honour_' -'honest_' -'hoff' -'hire_' -'hinzufugen_' -'hilfe_' -'hidden_' -'hes_' -'hervorragenden_' -'hervorheben_' -'hervorgehoben_' -'herunterladen_' -'herunter' -'heat_' -'healthy_' -'hd' -'hast_' -'hart_' -'harmful_' -'harder_' -'hamm' -'halte' -'hack' -'guided_' -'gua' -'gründen_' -'gru' -'grati' -'grammatisch_' -'governmental_' -'gne_' -'gis' -'gier' -'ghe' -'gewählten_' -'gewonnen_' -'gewicht' -'get' -'gesto' -'gest' -'geschw' -'gerü' -'geprüft_' -'gep' -'gens' -'generous_' -'generell_' -'gend' -'gelang_' -'gefördert_' -'gefragt_' -'gebildet_' -'gear' -'gat' -'gaining_' -'ful' -'fueled_' -'friedlichen_' -'frequency_' -'fran' -'frame_' -'fra' -'foundations_' -'foster_' -'fordere_' -'forcing_' -'fli' -'fle' -'flaw' -'flags_' -'fix_' -'firmly_' -'finanz' -'films_' -'fie' -'festival_' -'ferner_' -'fee_' -'fans_' -'falsche_' -'facts_' -'facilitate_' -'extremists_' -'expressing_' -'explo' -'experiences_' -'expenditures_' -'expe' -'expanding_' -'executive_' -'ew' -'evident_' -'everybody_' -'estimates_' -'ess' -'erzeugen_' -'ery_' -'erwä' -'erweitern_' -'ersetzen_' -'erkunden_' -'erhöhte' -'erholen_' -'erforderliche_' -'erd' -'erarbeitet_' -'eo_' -'environmentally_' -'entst' -'entdeckst_' -'ensures_' -'ensi' -'enn' -'enjoying_' -'enjoyed_' -'ening_' -'englische' -'enger_' -'enge_' -'enemy_' -'endet_' -'endes_' -'emotional_' -'eme' -'embark' -'eingel' -'eingehalten_' -'ege' -'ega' -'effektive_' -'echt' -'ebe' -'eb' -'eate' -'earn_' -'ean' -'durchs' -'dungen_' -'dru' -'drives_' -'dramatically_' -'dol' -'dm' -'disa' -'dil' -'dignity_' -'digit' -'die' -'df' -'desktop_' -'desk_' -'designer_' -'deserve_' -'description_' -'describe_' -'ders_' -'dern_' -'derl' -'derartigen_' -'depth_' -'depending_' -'denk' -'dem' -'def' -'das' -'dak' -'customs_' -'cua' -'creat' -'courts_' -'count' -'corrupt_' -'cope_' -'conveniently_' -'consult' -'constraints_' -'connect_' -'confronted_' -'confrontation_' -'confront_' -'concentration_' -'complicated_' -'comparison_' -'communism_' -'commit_' -'combine_' -'collect_' -'closing_' -'clarity_' -'civilians_' -'cit_' -'circle_' -'chtung_' -'chne' -'chant' -'chan' -'challenging_' -'cepti' -'cell_' -'cand' -'camp_' -'bö' -'broadcast' -'brechen_' -'brauch' -'bot' -'bon_' -'blog_' -'blind' -'billig' -'bevölkerung_' -'betrieben_' -'bes' -'bert_' -'berge' -'berat' -'belegt_' -'beitr' -'beibehalten_' -'behe' -'beenden_' -'bau' -'band' -'ball' -'bail' -'bai' -'bags_' -'bagi_' -'bag' -'bad' -'backs_' -'awa' -'authors_' -'aut' -'ausschusses_' -'aussch' -'ausgeschlossen_' -'ausgeführt_' -'ausführliche' -'ausführ' -'aufgegeben_' -'aufgefordert_' -'asso' -'ass_' -'arti' -'arrest_' -'aris' -'arg' -'arbitrary_' -'appearance_' -'app' -'antr' -'annt' -'anlagen_' -'anischen_' -'angs_' -'angewandt_' -'angest' -'anger_' -'angepasst_' -'anerkennen_' -'anerkannte' -'ander' -'andel' -'all' -'alism_' -'alisierung_' -'akt' -'aki' -'ake' -'aine' -'aft_' -'advances_' -'adv' -'achtet_' -'accurate_' -'abzusch' -'abst' -'abi' -'aben_' -'abandon_' -'Zucker' -'Zone_' -'Zimmer' -'Zimbabwe_' -'Ye' -'Wr' -'Wort' -'Wissenschaft' -'Widerstands' -'Wi' -'Werbung_' -'Wende' -'Weg' -'Wechselkurs_' -'Webseiten_' -'Watson_' -'Water' -'Ware_' -'Wahr' -'Waffen' -'WASHINGTON_' -'WAR' -'Vorgehensweise_' -'Voll' -'Vi' -'Vert' -'Verluste_' -'Verl' -'Verge' -'Veranstaltungen_' -'Veranstaltung_' -'Vera' -'Ver' -'VER' -'User_' -'Up_' -'Umweltschutz_' -'US' -'UM' -'Transa' -'Trag' -'Tourismus_' -'Todesstrafe_' -'Thu' -'Tha' -'Textil' -'Temperaturen_' -'Tas' -'Tarifa_' -'Tan' -'TE_' -'Sä' -'Subventionen_' -'Str' -'Stor' -'Stockholm_' -'Stell' -'Stau' -'Stat' -'Sports_' -'Spielraum_' -'Spi' -'Space_' -'Sound' -'Sol_' -'Sof' -'Sign' -'Siemens_' -'Sicherheitsrat_' -'Shar' -'Serb' -'Ser' -'Senkung_' -'Sen' -'Sektors_' -'Schwer' -'Schwellenländern_' -'Schwei' -'Schw' -'Schu' -'Schlusselwort_' -'Schlusselphrase_' -'Schloss_' -'Rücks' -'Russ' -'Rumänien_' -'Rules_' -'Rubrik_' -'Rose' -'Right_' -'Ric' -'Rela' -'Rei' -'Ref' -'Rather_' -'RT' -'Provence_' -'Project_' -'Prize_' -'Praktiken_' -'Positionen_' -'Porto_' -'Pod' -'Plus_' -'Plat' -'Phänomen_' -'Pers' -'Pen_' -'Peace_' -'Park' -'Papa' -'Pana' -'Palma_' -'Palm' -'Pal' -'Pad' -'PC' -'PARIS_' -'Original' -'On' -'OP' -'OF_' -'Null' -'Notenbank_' -'Nordkorea_' -'Nord_' -'Niederlage_' -'Netto' -'Nationalismus_' -'Nar' -'Mutter_' -'Mut_' -'Muslime_' -'Morocco_' -'Monitor' -'Modelle_' -'Milosevic_' -'Mid' -'Metro_' -'Meter_' -'Medizin_' -'Material_' -'Marina_' -'Mani' -'Manager_' -'Manage' -'Mana' -'Malaria_' -'Mah' -'Magi' -'MM' -'Ly' -'Logik_' -'Lit' -'Listings_' -'Link_' -'Light' -'Liefer' -'Lib' -'Let' -'Lernen_' -'Leitlinien_' -'Lehren_' -'Laun' -'Lastly_' -'Laeken_' -'Kürze_' -'Kö' -'Kopenhagen_' -'Komplex' -'Kle' -'Kie' -'Kata' -'Kapazitäten_' -'Kandidat' -'Kanada_' -'Jun' -'Jer' -'Iran' -'Irak' -'Interessen' -'Install' -'Industrieländern_' -'Indi' -'Import' -'Immobilien_' -'IBM_' -'IB' -'Hunger_' -'Hunde' -'Hongkong_' -'Hol' -'Hitler_' -'Histori' -'Hei' -'Haushaltsdefizit' -'Haupts' -'Halb' -'Had' -'HD' -'Güter_' -'Gulf_' -'Guide_' -'Grünbuch_' -'Große_' -'Großbritanniens_' -'Growth_' -'Griechenlands_' -'Governments_' -'Golf' -'Gleichstellung_' -'Gla' -'Get_' -'Gestaltung_' -'Gesicht_' -'Genu' -'Gene' -'Geber' -'Games_' -'GP' -'Funktions' -'Fro' -'Friday_' -'Fri' -'Freunden_' -'Freizügigkeit_' -'Fraktionen_' -'Fortunately_' -'Force_' -'Food_' -'Fol' -'Florence_' -'Fle' -'Fischler_' -'Fisch_' -'Finnland_' -'Finanzsystem_' -'Finanzs' -'Finanzen_' -'Fernseh' -'FT' -'FS' -'FE' -'Export_' -'Existenz_' -'Exchange_' -'Eta' -'Este' -'Erwachsene_' -'Ep' -'Entwicklungsp' -'Einfluss' -'Eigentum_' -'ED_' -'Dä' -'Durchsetzung_' -'Dort_' -'Dokumente_' -'Dim' -'Dienst_' -'Det' -'Design' -'Democratic_' -'Def' -'Dea' -'De' -'Davos_' -'Darfur_' -'Daniel_' -'Cy' -'Creati' -'Content_' -'Consumer_' -'Comp' -'Communist_' -'Clubs_' -'Cla' -'Civil_' -'Chan' -'Casino_' -'Café_' -'CT' -'CA_' -'Bürokratie_' -'Bun' -'Bucht_' -'Bol' -'Black' -'Billionen_' -'Bez' -'Bewohner_' -'Betten_' -'Berlusconi_' -'Berg_' -'Belgien_' -'Bele' -'Beifall_' -'Beha' -'Bedürfnissen_' -'Bearbeitung_' -'Bay' -'Baum' -'Base_' -'Band_' -'Bahn' -'Az' -'Außen_' -'Autonomie_' -'Automobil' -'Ausmaß_' -'Ausgangspunkt_' -'Aufforderung_' -'Ass' -'Argument_' -'Antworten_' -'Ansätze_' -'Anleihen_' -'Angriffe_' -'Angl' -'Andr' -'And' -'Ami' -'Alters' -'Alta' -'Ali' -'Album_' -'Aktien_' -'Ah' -'Agrarpolitik_' -'Age_' -'Ablehnung_' -'Ablauf_' -'AS_' -'AC_' -'? ' -'96_' -'88_' -'86_' -'67_' -'61_' -'57_' -'53' -'33' -'27' -'236' -'1990s_' -'1960_' -'185' -'04' -'001' -',/_' -', ‘_' -'+ _' -'%-_' -'%' -'#_' -'! ' -' ("_' -'�' -'“ – _' -'–_' -'ш' -'по' -'м_' -'ли' -'ко' -'ка' -'ер' -'ва' -'ар' -'üstung_' -'ührung_' -'übrigens_' -'überwiegend_' -'überw' -'übernahm_' -'österreichischen_' -'öffentlich' -'ô' -'í_' -'ë' -'è_' -'ç' -'ätz' -'ärmsten_' -'änden_' -'ält_' -'ähr' -'äd' -'Übernahme_' -'°_' -' –, _' -'}}) _' -'| _' -'{_' -'zö' -'zust' -'zukommen_' -'zivil' -'zins' -'ziert_' -'zi_' -'zes_' -'zentral_' -'zen' -'zahlt_' -'yields_' -'yea_' -'xo' -'xe' -'xa' -'wort_' -'worried_' -'worker_' -'witness_' -'wissenschaftliche_' -'winning_' -'will' -'wild_' -'widmen_' -'whereby_' -'wh' -'weshalb_' -'werken_' -'welchem_' -'weite_' -'weigh' -'weapon_' -'weaker_' -'was' -'warme' -'wander' -'walt' -'walls_' -'wahlen_' -'vs' -'vorzunehmen_' -'vorsieht_' -'vorschlag_' -'vorn' -'vore' -'voran_' -'vollst' -'voll' -'volatility_' -'villages_' -'via' -'vez_' -'verz' -'versuch' -'verse' -'verschiedener_' -'verschaffen_' -'verme' -'verliert_' -'verletzt_' -'verhe' -'verh' -'vergeben_' -'verge' -'verfügbaren_' -'verd' -'verbr' -'verbinden_' -'vegeta' -'vari' -'valuable_' -'vak' -'uto' -'uti' -'urs' -'uri' -'urf' -'updated_' -'unzureichend_' -'unw' -'unusual_' -'unterwegs_' -'untern' -'unprecedented_' -'unpa' -'ungew' -'ungen' -'undi' -'unders' -'undergo' -'unden_' -'unau' -'un' -'umr' -'ump' -'uli' -'ukan_' -'ui_' -'uge' -'ucht_' -'ucht' -'ually_' -'ua_' -'ua' -'u0027s_' -'türkische_' -'tät' -'tänd' -'tube_' -'tter' -'très_' -'träge' -'triumph' -'treaties_' -'travellers_' -'transformation_' -'trafficking_' -'tow' -'tors_' -'tons_' -'toll_' -'tol' -'tl' -'tionally_' -'tional_' -'tim' -'til' -'thumb_' -'ths_' -'thousand_' -'thirds_' -'thir' -'thi' -'theo' -'tensions_' -'tens_' -'tellung_' -'teams_' -'taxation_' -'tax' -'tas' -'tana' -'sus_' -'surroundings_' -'surprising_' -'suddenly_' -'substantially_' -'stü' -'studied_' -'student_' -'stoppen_' -'stoff' -'stimulate_' -'stimmung' -'stil' -'stic' -'stelle_' -'steigende_' -'stehenden_' -'steer' -'sted_' -'stattfindet_' -'stages_' -'stabilize_' -'sta_' -'sste' -'ssa' -'spra' -'spor' -'spiel' -'spi' -'spect' -'specifications_' -'speaker_' -'spart' -'sparen_' -'spar' -'spanischen_' -'sow' -'sounds_' -'sorgt_' -'som' -'sol' -'slowdown_' -'slopes_' -'slight_' -'slave' -'skin_' -'sixt' -'simultaneously_' -'sieren_' -'sichere_' -'si' -'shrink' -'sharply_' -'sex' -'ses' -'servers_' -'sent' -'semi' -'segment' -'sec' -'sd' -'schönsten_' -'schwieriger_' -'schwerer_' -'schwach_' -'scht_' -'schri' -'schlu' -'schlimm' -'schlicht_' -'schl' -'schau' -'scene_' -'sav' -'saubere_' -'sat' -'sant' -'sand_' -'rz' -'rufen_' -'rse' -'rsch' -'rov' -'routes_' -'ros' -'rom' -'roll' -'roles_' -'rma' -'rku' -'reverse_' -'retain_' -'resse' -'res' -'requiring_' -'requests_' -'rent' -'rena' -'rek' -'reinforcing_' -'reine_' -'reiben_' -'regulat' -'regelung_' -'refuse_' -'referring_' -'rechtzeitig_' -'reali' -'rasche' -'rar' -'racing_' -'quin' -'quil' -'quantities_' -'qualitative_' -'pä' -'put' -'pursued_' -'punkt' -'proud_' -'protest' -'prote' -'profession' -'private' -'prim' -'preventing_' -'prevented_' -'prevailing_' -'pressures_' -'presenting_' -'predict_' -'ppl' -'ppen_' -'pou' -'posts_' -'positiven_' -'pools_' -'polnischen_' -'polls_' -'pm_' -'plötzlich_' -'ple_' -'platforms_' -'planung_' -'plant' -'pick' -'pflicht' -'pet_' -'pers' -'permit_' -'periphery_' -'pens' -'pel_' -'patio_' -'passing_' -'pas_' -'pas' -'partition_' -'part' -'palästinensische_' -'pakete_' -'pag' -'oß_' -'owners_' -'overseas_' -'oto' -'ossen_' -'ose' -'ories_' -'orientierte' -'oriented_' -'org' -'orc' -'ops_' -'operated_' -'opera' -'ony_' -'ono' -'ole' -'ois' -'oi' -'oga' -'officially_' -'od_' -'nos_' -'normale_' -'non' -'nobody_' -'nnung_' -'nni' -'nin' -'nig' -'nem' -'nell' -'neighbourhood_' -'negotiation_' -'nau' -'native_' -'nai' -'nahmen_' -'m²_' -'museum_' -'moderner_' -'mobilis' -'mmi' -'mme_' -'missions_' -'missile_' -'miss_' -'minu' -'milk_' -'militärischer_' -'merit' -'menja' -'mengen' -'mene' -'medizinischen_' -'mediat' -'meaningful_' -'mb' -'mati' -'massiven_' -'maschine_' -'mart' -'mar_' -'manu' -'mano' -'mals_' -'m2_' -'lässig' -'lur' -'lter_' -'lter' -'lst' -'lowest_' -'low' -'losen_' -'loi' -'logische' -'llt_' -'llers_' -'llen' -'linear_' -'lie' -'lichkeiten_' -'leng' -'leiden_' -'legend' -'lament' -'lalu_' -'lak' -'lacks_' -'lab' -'kund' -'kun' -'kredit' -'konzipiert_' -'kontra' -'konf' -'kommerziellen_' -'klima' -'kek' -'kehren_' -'kata' -'kat_' -'kandidat' -'juga_' -'judicial_' -'journey_' -'jointly_' -'jer' -'jahr' -'itu_' -'ith' -'isten_' -'ising_' -'isiert_' -'isch' -'isa' -'irgend' -'ione' -'investing_' -'interven' -'internen_' -'interessante_' -'institut' -'inso' -'insist_' -'inos_' -'inn' -'ining_' -'incidents_' -'inci' -'impos' -'ime_' -'iklim_' -'ika_' -'igra' -'iger' -'iew' -'ierungen_' -'iegen_' -'ieben_' -'hur' -'hung_' -'human' -'htig' -'hte' -'hro' -'host' -'hom' -'hochwertige' -'hochge' -'hlt_' -'hinaus' -'hill_' -'heu' -'herstell' -'hen' -'harten_' -'harmonisation_' -'happiness_' -'hall_' -'hair_' -'hai' -'guard_' -'gt' -'gründe' -'grew_' -'grenzen_' -'greifen_' -'gratulieren_' -'gran' -'grab' -'gon' -'gol' -'glaub' -'gk' -'geäußert_' -'gewinn' -'gewachsen_' -'getötet_' -'gestr' -'gestartet_' -'gesellschaftliche_' -'geschäft' -'geräumige' -'gerät_' -'gers_' -'geringer_' -'gepr' -'genes_' -'generate_' -'gemeinschaftlichen_' -'gemeinsamer_' -'geli' -'gelassen_' -'gek' -'geistigen_' -'gegangen_' -'gefährlich_' -'gefolgt_' -'gefe' -'gebühr' -'geboten_' -'gd' -'gari' -'gam' -'führer_' -'führe' -'fte' -'frische' -'fried' -'fragment' -'forums_' -'forth_' -'formation_' -'forge_' -'folder_' -'fläche_' -'fiskal' -'fine' -'finds_' -'finances_' -'fft_' -'ffic' -'ffi' -'fei' -'feet_' -'featuring_' -'fault_' -'fan' -'ey' -'expos' -'exporting_' -'exhibit' -'execution_' -'exe' -'excuse_' -'exceptions_' -'exc' -'exact_' -'everyday_' -'eun' -'eth' -'eten' -'essen' -'esca' -'erzielte_' -'ersi' -'ersetzt_' -'erse' -'err' -'erode' -'ernst' -'erlebt_' -'erhoben_' -'erh' -'ergänzen_' -'erer_' -'erende' -'erei' -'ere' -'enze' -'entr' -'entdecken_' -'ense' -'enha' -'energie_' -'enemies_' -'endgültigen_' -'empfängt_' -'empfehlen_' -'emp' -'emo' -'emission' -'embedded_' -'elli' -'elegante_' -'electrical_' -'ekonomi_' -'einzusch' -'einstellen_' -'einst_' -'einrichtungen_' -'einkommen_' -'eingeschränkt_' -'eingesch' -'eindeutige_' -'egte' -'effizienter_' -'ees_' -'eba' -'dürften_' -'dynamism_' -'durchge' -'dur' -'dramatisch_' -'dr' -'downturn_' -'dorthin_' -'domin' -'dle' -'dism' -'discovery_' -'disagree_' -'disabled_' -'direkt' -'dige' -'differ' -'dieselben_' -'dienste_' -'dic' -'deutliche_' -'deuten_' -'desto_' -'denkt_' -'deni' -'demanded_' -'delivery_' -'defining_' -'defi' -'deckt_' -'deck' -'decent_' -'deb' -'dau' -'darstell' -'cycli' -'cyber' -'curs' -'cro' -'credit' -'credible_' -'covers_' -'costly_' -'controlling_' -'contract' -'consolidation_' -'consists_' -'conscious' -'conc' -'como_' -'committees_' -'comme' -'colors_' -'cm_' -'clock_' -'cler' -'cken' -'cke_' -'cia_' -'chts' -'chs' -'chri' -'ched_' -'characters_' -'chaos_' -'champion' -'chair_' -'cet' -'ceme' -'cara_' -'capita_' -'calm_' -'büro' -'bus' -'bury_' -'bungen_' -'broadly_' -'brit' -'bric' -'breed' -'bracht_' -'boundaries_' -'boosting_' -'boat_' -'blow' -'bing_' -'binde' -'big' -'bha' -'bezahl' -'bez' -'bewiesen_' -'beweisen_' -'betont_' -'best' -'besonderer_' -'benutzer' -'benefited_' -'believes_' -'bekannte_' -'bekam_' -'behindert_' -'bedroht_' -'beding' -'bedeutsame' -'be' -'batt' -'bah' -'award_' -'avi' -'avec_' -'ava' -'aux_' -'auszuüben_' -'aussehen_' -'ausgewogene' -'ausgew' -'ausgeb' -'auseinander_' -'aufw' -'auftr' -'aufb' -'attraktive_' -'attract_' -'attended_' -'atr' -'asymmetri' -'ast' -'aspirations_' -'ash' -'ase_' -'artificial_' -'arn' -'arme_' -'arm' -'argues_' -'ard' -'archive_' -'arat' -'appl' -'apo' -'anzuwenden_' -'anzupassen_' -'anwenden_' -'answers_' -'ansehen_' -'anonym' -'anis' -'angesehen_' -'angemessen_' -'angeb' -'andes_' -'amt_' -'ame_' -'allzu_' -'allerg' -'alität_' -'alis' -'ales_' -'ald' -'aku' -'aj' -'airline_' -'ahn' -'ahme' -'aha' -'agieren_' -'afrika_' -'affecting_' -'aer' -'advised_' -'advise' -'adult_' -'ada' -'actors_' -'acted_' -'accounting_' -'accelerating_' -'aca' -'abgesehen_' -'abgelehnt_' -'abg' -'abend' -'aan_' -']], [[_' -'] ' -'Zust' -'Zus' -'Zur' -'Zugleich_' -'Youth_' -'Yas' -'XP_' -'XI' -'Wäscheservice_' -'Wärme' -'Wä' -'Wunder' -'Wissenschaftler_' -'Wirtschaftsp' -'Wirtschaftsf' -'Wirtschafts_' -'Wireless_' -'Wiederherstellung_' -'Wie' -'Widerspruch_' -'Wichtigkeit_' -'Wettbewerbs_' -'Wesen_' -'Werbe' -'Wer' -'Wen' -'Weit' -'Wein_' -'Wei' -'Wea' -'WC_' -'Vorschlags_' -'Vork' -'Vorhaben_' -'Vorf' -'Vollbeschäftigung_' -'Vertrags' -'Verteilung_' -'Versorgung_' -'Verletzung_' -'Verle' -'Verla' -'Verk' -'VI_' -'Urlaubs' -'Unternehmer_' -'Unterkünfte_' -'Unt' -'Typ' -'Twitter_' -'Trend_' -'Tot' -'Top' -'Thursday_' -'Third_' -'Thai_' -'Telefon_' -'Take_' -'Tagung_' -'Systemen_' -'Sun' -'Summe_' -'Suites_' -'Sturz_' -'Studenten_' -'Strom_' -'Strand' -'Stoffe_' -'Stabilitäts_' -'Spring_' -'Spitzen' -'Solange_' -'Smoking_' -'Situated_' -'Sie' -'Sicherheits_' -'Sicher' -'Shuttle_' -'Should_' -'Shop_' -'Sevilla_' -'Seba' -'Schrift' -'Schmerz' -'Schlag_' -'Schl' -'Schei' -'Salz' -'Salo' -'Sala' -'Sai' -'Russen_' -'Ross' -'Rit' -'Ris' -'Rhetorik_' -'Rettungs' -'Rese' -'Reg' -'Real_' -'Reagan_' -'Ratifizierung_' -'Rap' -'Radio' -'RER_' -'Provinz_' -'Programm' -'Prognosen_' -'Produzenten_' -'Produktivität_' -'Press_' -'Presidents_' -'Post_' -'Pont' -'Poll' -'Pole' -'Poker' -'Plenum_' -'Piazza_' -'Patten_' -'Patri' -'Pat' -'Papier' -'Panorama' -'Pale' -'Pak' -'Out' -'Oh' -'OC' -'Nummer_' -'Nuklear' -'None_' -'Nielson_' -'Nichtraucherzonen_' -'Next_' -'Netzwerk' -'Net_' -'Nei' -'Natura_' -'NS' -'NC' -'München_' -'Mul' -'Much_' -'Moskau' -'Mona' -'Moment' -'Modernisierung_' -'Modell' -'Mio_' -'Mini_' -'Millennium_' -'Mill' -'Migration_' -'Mic' -'Meinungs' -'Med' -'Mart' -'Marktwirtschaft_' -'Marg' -'Mao_' -'Mail' -'MD' -'Lü' -'Lärm' -'Lobby' -'Liu_' -'Libyen_' -'Les_' -'Lehrer_' -'Legal_' -'Lebensmittel_' -'Lau' -'Lap' -'Language_' -'Labor_' -'LU' -'LS_' -'LG' -'Kriminalität_' -'Kriege_' -'Kremlin_' -'Kontrollen_' -'Kontext_' -'Kont' -'Konkurrenz_' -'Kollegin_' -'Klaus_' -'Kl' -'Key_' -'Kern_' -'Kenntnisse_' -'Kart' -'Karriere_' -'Kann_' -'KI' -'Jü' -'Justiz' -'Jur' -'Juan_' -'Jordanien_' -'Jordan_' -'Jahrestag' -'Investition_' -'Institut_' -'Ingenieur' -'Industry_' -'Industrieländer_' -'Inc_' -'Ill' -'Id' -'IK' -'Hände_' -'Hypotheken' -'Human' -'Hour_' -'Holiday_' -'Hohe_' -'Hir' -'Hind' -'Herzlich' -'Heraus' -'Haut' -'Hau' -'Hart' -'Harmonisierung_' -'Haar' -'HO' -'HD_' -'Güter' -'Guan' -'Grundsätze_' -'Grundlagen_' -'Gross' -'Governance_' -'Gottes_' -'Gold' -'Global' -'Gestatten_' -'Geschlechter' -'Gen' -'Gemeinschaften_' -'Gefängnis_' -'Gebi' -'Gay_' -'Gates_' -'Gat' -'Gast_' -'Gara' -'GN' -'Führungsrolle_' -'Fü' -'Freizeit' -'Fr' -'Fou' -'Flu' -'Fire' -'Ferienwohnungen_' -'Ferien' -'Feinde_' -'Farm' -'Family_' -'Fallout_' -'FL' -'FC_' -'Excellent_' -'Everything_' -'Europol_' -'Etwa' -'Et' -'Essen_' -'Esp' -'Erwerbs' -'Erne' -'Erk' -'Erfolge_' -'Engine_' -'Energien_' -'Empf' -'Einschätzung_' -'Einschränkung_' -'Eingabe' -'Effektivität_' -'Economy_' -'Ebola_' -'Eb' -'EP_' -'Doppel' -'Dom' -'Dol' -'District_' -'Disp' -'Diplomatie_' -'Deutsch_' -'Deposit_' -'Denken_' -'Defence_' -'Deep_' -'Danach_' -'DL' -'DD' -'DA_' -'Culture_' -'Cru' -'Corb' -'Consensus_' -'Commissioners_' -'Come_' -'Code' -'Coa' -'Churchill_' -'Children_' -'Chat' -'Chancellor_' -'Catal' -'Castle_' -'Cast' -'Cand' -'CP' -'CM' -'Büro' -'Böse' -'Börsen' -'Butt' -'Bru' -'Brand' -'Boy' -'Bord_' -'Boot_' -'Bolkestein_' -'Blin' -'Binnen' -'Bilanz' -'Big_' -'Bie' -'Bibliothek_' -'Bewegungen_' -'Bew' -'Beu' -'Betracht_' -'Beteiligten_' -'Bet' -'Best' -'Besondere' -'Besitz_' -'Benjamin_' -'Bene' -'Ben_' -'Begründung_' -'Beendigung_' -'Bee' -'Bedrohungen_' -'Bedien' -'Beamten_' -'Basel_' -'Barón_' -'Badezimmer_' -'Back_' -'BRICS_' -'Avenue_' -'Automati' -'Ausgangs' -'Ausgabe_' -'Ausflüge_' -'Augen' -'Aufzug_' -'Aufnahme' -'Ast' -'Assembly_' -'Arte' -'Argumente_' -'Arabs_' -'Aqua' -'Ap' -'Anwendungs' -'Anträge_' -'Allianz_' -'Alex' -'Aktivität_' -'Aktionäre_' -'Act_' -'Absichten_' -'Abschaffung_' -'Abg' -'Abendessen_' -'AGE' -'AAA_' -'= _' -'900_' -'90' -'56_' -'2050_' -'1986_' -'1950_' -'195' -'181' -'176' -'.&_' -'. “_' -'+' -'))._' -'", "_' -' {{_' -' ,_' -' &_' -' $ _' -'”' -'“-_' -'“ (_' -'א' -'ө' -'ұ' -'с_' -'ры' -'ны' -'нд' -'в_' -'še' -'üssen_' -'üssel' -'ühren_' -'überschuss_' -'überlegen_' -'überf' -'öm' -'én' -'él' -'äus' -'äufe' -'äst' -'äss' -'ändert_' -'ämpf' -'äl' -'Überlegungen_' -'Überl' -'Überg' -'Är' -'Änderungs' -'zzi' -'zwingen_' -'zweiter_' -'zweifel' -'zutiefst_' -'zuständigen_' -'zus' -'zurückzuführen_' -'zurückf' -'zunehmen_' -'zul' -'zugute_' -'zue' -'zt_' -'zog_' -'zige' -'ziele_' -'zial' -'zentraler_' -'würdig_' -'worry_' -'wood' -'withdrawal_' -'wirklichen_' -'wherever_' -'whenever_' -'wheel_' -'wettbewerbsfähige' -'westliche_' -'wesens_' -'werde' -'wenigsten_' -'wealthy_' -'waves_' -'warrant' -'wann_' -'wand' -'wai' -'wahrscheinliche' -'wachstum_' -'wachs' -'vorzulegen_' -'vorw' -'vorn_' -'vorgeschlagene_' -'vorbe' -'volatile_' -'vist' -'visa_' -'virtue_' -'virtual_' -'villa_' -'videos_' -'victim_' -'vest' -'verwiesen_' -'verwei' -'verwe' -'verwandelt_' -'verwandeln_' -'verwa' -'vertr' -'versteht_' -'versetzen_' -'versa' -'vermittelt_' -'verkehrs_' -'verifi' -'verf' -'verbieten_' -'verbesserte_' -'verband_' -'vera' -'vene' -'varia' -'val_' -'val' -'utz_' -'uted_' -'uste' -'ust_' -'usage_' -'ure' -'uranium_' -'ura' -'upaya_' -'unterstützten_' -'unterst' -'unterschiedlich_' -'unterbreitet_' -'unstable_' -'uns' -'unre' -'unn' -'universe_' -'ungsbe' -'ungsa' -'ungeachtet_' -'unemployed_' -'undenen_' -'unclear_' -'uncertain_' -'unce' -'unabhängige_' -'umgekehrt_' -'umgehen_' -'umen_' -'ull' -'ulat' -'ula_' -'uj' -'uck' -'uchs_' -'uc' -'ubi' -'tätigkeit_' -'tyr' -'typisch_' -'twentieth_' -'tut' -'turno' -'tun' -'tum_' -'tua' -'tta' -'tsunami_' -'träger_' -'tragedy_' -'traf_' -'toxi' -'tot' -'tom' -'tn' -'tliche' -'tigen_' -'tige' -'tien' -'tiefer_' -'tie_' -'threaten_' -'thin' -'thes' -'thermal_' -'there' -'theme_' -'thek' -'theater_' -'tf' -'terrible_' -'terr' -'teri' -'terhadap_' -'tent' -'temperature_' -'tells_' -'teiln' -'teil' -'teh' -'technologischen_' -'techno' -'teacher_' -'tatsächlichen_' -'taten_' -'taste' -'südlichen_' -'sus' -'surprise_' -'supp' -'suggestions_' -'suggestion_' -'sufficiently_' -'succeeded_' -'suc' -'stärksten_' -'stunning_' -'strukturelle_' -'strictly_' -'strengthened_' -'streit' -'stores_' -'stopp' -'stimulat' -'sth' -'steiger' -'steady_' -'station' -'startet_' -'star' -'ssp' -'ssl' -'ssel' -'ssch' -'split_' -'spielte_' -'specialities_' -'sowjetischen_' -'sonn' -'songs_' -'solch_' -'soa' -'smo' -'slow' -'sle' -'sko' -'sk' -'sitze' -'sische' -'sinken_' -'sin_' -'shipping_' -'shing_' -'sher' -'sheer_' -'shareholders_' -'sey_' -'setz' -'sett' -'servi' -'sene' -'sem' -'sely_' -'seits_' -'seit' -'seemingly_' -'seeing_' -'sed' -'sebagai_' -'seas' -'script_' -'schwere_' -'schwachen_' -'schuld' -'schlimmer_' -'schiff_' -'schien_' -'scher' -'sau' -'sarge' -'sanit' -'saf' -'sache' -'räume_' -'rus' -'ruins_' -'ruck' -'rub' -'rts_' -'rtet_' -'roof_' -'rolle_' -'rock_' -'roc' -'rnen_' -'rmo' -'rkte' -'river_' -'riv' -'ring' -'rik_' -'rien' -'rieg' -'restored_' -'responsi' -'reso' -'resist_' -'resign' -'researchers_' -'repression_' -'renewed_' -'relevante' -'reiten_' -'reite' -'reif' -'reichsten_' -'rei_' -'referen' -'redu' -'reco' -'reck' -'realise_' -'real' -'rchi' -'rauch' -'rated_' -'rami' -'radical' -'rab' -'quit' -'quer' -'quelle_' -'pushing_' -'pus' -'pursuing_' -'pure_' -'pto' -'präg' -'provo' -'propaganda_' -'projekte_' -'prohibited_' -'programming_' -'produzieren_' -'proceed_' -'privatis' -'printed_' -'principal_' -'prevail_' -'prev' -'preserv' -'pres' -'preference_' -'preferable_' -'prefer' -'precious_' -'prec' -'popula' -'poorer_' -'politician_' -'pola' -'plä' -'plug_' -'plat' -'plane_' -'pla' -'pilot_' -'pig' -'pieces_' -'physi' -'phy' -'perm' -'performing_' -'penalty_' -'pemerintah_' -'ped' -'peacefully_' -'parteien_' -'parlamentarischen_' -'param' -'pane' -'paket_' -'pak' -'pain' -'overwhelming_' -'overview_' -'overs' -'overr' -'ours_' -'ots_' -'oten_' -'ost_' -'oss' -'osa' -'orthodox' -'orn_' -'orge' -'organic_' -'ore_' -'ordnungsgemäß_' -'ordnete' -'oran' -'ont' -'oned_' -'one' -'onder' -'omis' -'om' -'oliti' -'oldest_' -'ohn' -'ohl_' -'ograph' -'officer_' -'occurs_' -'obe' -'nächster_' -'nze' -'num' -'ntly_' -'nth' -'nso' -'nr' -'nowhere_' -'normally_' -'nm' -'nissen_' -'nga' -'neig' -'nehme_' -'neck' -'ndel' -'nd' -'nationalism_' -'national' -'nat' -'nahe' -'nah' -'nachzu' -'nachh' -'nachdrücklich_' -'multimedia_' -'mul' -'mouth_' -'mounting_' -'mos' -'mood_' -'monopoly_' -'modification_' -'modes_' -'mk' -'mittelalterliche' -'mite' -'mist' -'missbrauch' -'mild' -'mie' -'mehrs' -'mega' -'mee' -'medizinische' -'measur' -'mble_' -'maß' -'maybe_' -'maximi' -'mant' -'manage' -'mana' -'mala' -'mailing_' -'mage' -'mach' -'lö' -'lé' -'lusi' -'loyalty_' -'loyal' -'lowering_' -'lou' -'loose_' -'log' -'locker' -'loan_' -'llig' -'literally_' -'linken_' -'line' -'like' -'liefert_' -'lick' -'lichsten_' -'lich' -'liation_' -'lgen' -'letter_' -'leiten_' -'legitime' -'lays_' -'laste' -'lass' -'lant' -'lance_' -'labelling_' -'kurs' -'ktur' -'ktive' -'kte_' -'ks' -'kritische_' -'konsum' -'konstruktive_' -'komplette' -'kommuni' -'kol' -'kno' -'kni' -'knapp_' -'kleinere_' -'klar' -'kit_' -'kilo' -'kehrt_' -'keb' -'kau' -'jä' -'jum' -'jo_' -'jetzigen_' -'jet' -'ject' -'jan_' -'jah' -'izing_' -'its' -'iter_' -'iss' -'israelische_' -'isolation_' -'ishing_' -'ish' -'ised_' -'ironi' -'iro_' -'irischen_' -'invi' -'investieren_' -'intr' -'internasional_' -'interm' -'intensi' -'intense_' -'installieren_' -'insta' -'innocent_' -'initiat' -'ingung_' -'infolge_' -'info_' -'inflation' -'infected_' -'iner' -'indoor_' -'individuellen_' -'individuelle_' -'individuali' -'indig' -'indicates_' -'ind_' -'incorporated_' -'inad' -'implied_' -'imperative_' -'imper' -'imo' -'imme' -'iller' -'ile' -'iko' -'igen' -'ically_' -'höchst_' -'hy_' -'hs' -'hren' -'hou' -'holes_' -'hob' -'hne' -'hly_' -'hinnehmen_' -'hide_' -'hide' -'heutige_' -'hervorgebracht_' -'herum' -'herself_' -'herausge' -'hem' -'hegemony_' -'headquarters_' -'harte' -'harsh_' -'halben_' -'hal_' -'günstige_' -'gues' -'grünen_' -'grund' -'grosse_' -'gross_' -'grand_' -'graf' -'grade_' -'governed_' -'golden_' -'gnen_' -'gm' -'gio' -'gil' -'ggl' -'gewählte_' -'getr' -'gespeichert_' -'gespa' -'geräte_' -'geringen_' -'geregelt_' -'genießt_' -'geniessen_' -'geneti' -'generi' -'geholfen_' -'geführten_' -'gefährlichen_' -'geeigneten_' -'gedacht_' -'gebi' -'gas' -'ganze' -'fünf' -'führten_' -'füg' -'fü' -'fähr' -'funded_' -'functional_' -'fs' -'freundliches_' -'freige' -'freier_' -'fraud_' -'fragt_' -'formuliert_' -'format' -'forderte_' -'focuses_' -'flicht' -'flexib' -'fits_' -'finished_' -'finanzieller_' -'fina' -'fin_' -'fil' -'fifth_' -'festzustellen_' -'festen_' -'fertig' -'ferr' -'fern_' -'feelings_' -'fan_' -'fak' -'fait' -'fahr' -'factory_' -'fac' -'extremen_' -'extrem_' -'explicitly_' -'experiment_' -'existed_' -'exce' -'ewi' -'ever' -'eut' -'europäisches_' -'etzung_' -'ett' -'etic_' -'ethnischen_' -'ethical_' -'essa' -'esc' -'erwerben_' -'erste' -'ernsthafte_' -'ernsthaft_' -'erleichtern_' -'erhältlich_' -'erhielten_' -'erfreut_' -'erbe' -'erba' -'erati' -'episode_' -'eous_' -'enve' -'entwurf_' -'enthaltenen_' -'entfernen_' -'entdeckt_' -'entar' -'enta' -'engen_' -'empty_' -'employers_' -'empire_' -'emb' -'elte' -'elo' -'eliminating_' -'elf' -'elect' -'ekt' -'einziges_' -'einschl' -'einladende' -'eingestellt_' -'eing' -'eine' -'eind' -'eig' -'eib' -'ehemalige_' -'egung_' -'egt_' -'egi' -'effektiven_' -'edition_' -'ech' -'ebo' -'ease_' -'dynami' -'dscha' -'dry_' -'druck' -'drew_' -'downtown_' -'doubts_' -'donor_' -'domain_' -'disp' -'disorder_' -'disk' -'discharge_' -'disastrous_' -'direct' -'diplomats_' -'dine' -'dienst_' -'dian' -'diam' -'diagnose' -'detaillierte' -'desirable_' -'desi' -'deri' -'depart' -'deny_' -'denselben_' -'denied_' -'demonstrates_' -'demnächst_' -'deli' -'delegate' -'dein' -'dei_' -'degradation_' -'defin' -'deemed_' -'decrease_' -'deco' -'declining_' -'debts_' -'debating_' -'daz' -'dauert_' -'dauerhafte_' -'dasselbe_' -'dangers_' -'dance_' -'damaged_' -'cyclical_' -'cuti' -'ctions_' -'crowd' -'crack' -'countryside_' -'cos' -'cook' -'conte' -'constructed_' -'connect' -'confusion_' -'confirmation_' -'configuration_' -'confident_' -'condemned_' -'comprises_' -'compliance_' -'complement' -'commande' -'coloni' -'collaborat' -'coincide' -'codi' -'coc' -'clip' -'clarify_' -'cks_' -'cio_' -'cio' -'cian' -'cia' -'choosing_' -'chet_' -'cheaper_' -'char' -'centrally_' -'cells_' -'celebrate_' -'ced_' -'catalog' -'capitalist_' -'cancelled_' -'campaigns_' -'by' -'buying_' -'brutale' -'brutal_' -'bru' -'bro' -'breit' -'breakdown_' -'brain_' -'bra' -'boy' -'bou' -'bold_' -'bod' -'boasts_' -'blo' -'blic' -'bles_' -'blank' -'bitter' -'bisherigen_' -'bir' -'biodiversity_' -'bill' -'bic' -'bewegt_' -'betreu' -'betre' -'besuchte_' -'beste' -'beschä' -'beschreibt_' -'beri' -'berechtig' -'berechnet_' -'bemerkenswert_' -'beliebte' -'beizutragen_' -'begrüßt_' -'begab_' -'beeinträchtigt_' -'bedürf' -'bedingten_' -'bedienen_' -'bedauer' -'beda' -'bec' -'bearing_' -'beantragen_' -'battery_' -'bankers_' -'bahn_' -'bab' -'aß' -'az_' -'awarded_' -'aute' -'ausste' -'ausgestattete' -'ausgesch' -'ausgerichtet_' -'ausgelöst_' -'aufweist_' -'aufs' -'aufn' -'aufh' -'aufgerufen_' -'aufgeh' -'aufbauen_' -'audience_' -'attraction_' -'attend_' -'atten' -'attempting_' -'ator' -'ath_' -'atas_' -'assurance_' -'assuming_' -'assumed_' -'assist_' -'assigned_' -'asiatische_' -'ars_' -'arrogant_' -'arro' -'arin' -'argumentiert_' -'approve_' -'approaches_' -'appreciated_' -'appr' -'appe' -'anzeigen_' -'anwe' -'anste' -'anscheinend_' -'annimmt_' -'anne' -'anlage_' -'anhand_' -'angefangen_' -'angef' -'ane_' -'ane' -'amm' -'amend_' -'ama_' -'allocated_' -'allgemeinem_' -'ality_' -'alist' -'alismus_' -'aktivieren_' -'airports_' -'ahl' -'aggregate_' -'ager' -'agents_' -'age' -'ado' -'adequately_' -'aden_' -'addresses_' -'addict' -'activists_' -'achievements_' -'achievement_' -'accus' -'accelerate_' -'abzielt_' -'abt' -'abstimmen_' -'absolut_' -'abrupt' -'abr' -'abo' -'abhängen_' -'abgew' -'abgestimmt_' -'abgest' -'abf' -'abd' -'abandoned_' -'aa' -'Zuständigkeiten_' -'Zugeständnisse_' -'Zugangs' -'Zoll' -'Zeitalter_' -'Zeichen' -'Yugoslavia_' -'Yan' -'Word_' -'Wo_' -'Westens_' -'Wes' -'Weltk' -'Weil_' -'Websites_' -'Watch_' -'Wart' -'Waren' -'Warcraft_' -'Vorz' -'Vorsitzende_' -'Volksgesundheit_' -'Voi' -'Vista_' -'Veröffentlichung_' -'Versionen_' -'Verschl' -'Versammlung_' -'Verkehrsmittel_' -'Verfügbarkeit_' -'Verfahrens' -'Verbündeten_' -'Vac' -'Until_' -'Unterzeichnung_' -'Unterstütz' -'Unterhaltungs' -'Ungarn_' -'Unf' -'Umweltfragen_' -'Umf' -'UT' -'UNG_' -'UB' -'Tö' -'Turn' -'Turk' -'Turb' -'Tschetschenien_' -'Ts' -'Trends_' -'Tourism_' -'Tour' -'Torre' -'Todesfälle_' -'Thomas_' -'Theorie_' -'Tempo_' -'TR' -'TB_' -'Syrian_' -'Swi' -'Swa' -'Störungen_' -'Studium_' -'Struktur' -'Strei' -'Stream' -'Storage_' -'Starfleet_' -'Standort_' -'Spur' -'Spieler' -'Speisen_' -'Sowohl_' -'Southern_' -'Sonne_' -'Sommer' -'Sollten_' -'Smith_' -'Singapore_' -'Sicherheitsa' -'Shin' -'Serbi' -'Select_' -'Sekunde_' -'Scott_' -'Sco' -'Schüler_' -'Schwarz' -'Schulz_' -'Schr' -'Schne' -'Schm' -'Schlafzimmer_' -'Schlaf' -'Sad' -'Sachen_' -'SPA' -'SER' -'Rü' -'Roosevelt_' -'Ronald_' -'Roh' -'Richter_' -'Rhein' -'Rettung_' -'Respekt_' -'Resi' -'Reservierung_' -'Rent' -'Renaissance_' -'Rena' -'Remo' -'Reduzierung_' -'Reaktionen_' -'Ratspräsident_' -'Railway_' -'Rahmens_' -'Rahmenbedingungen_' -'Raf' -'Rab' -'ROM_' -'RG' -'Quick' -'Question_' -'Queen_' -'Qua' -'Qaeda_' -'Pul' -'Prozesse_' -'Protocol_' -'Proteste_' -'Projekten_' -'Privatisierung_' -'Prague_' -'Pr' -'Port_' -'Por' -'Play' -'Pierre_' -'Pha' -'Pflanzen' -'Pfa' -'Peking_' -'Partnern_' -'Paradi' -'Panel_' -'Pack_' -'Pac' -'PS_' -'Over' -'Orts' -'Organ' -'Opti' -'Oper' -'Olympic_' -'Oc' -'OU' -'OT' -'OD' -'Nä' -'Nutz' -'Nor' -'Nokia_' -'Nikon_' -'Niederlanden_' -'Niederlande_' -'Nicolas_' -'Nichts' -'New' -'Never_' -'Napoleon_' -'Nachhaltigkeit_' -'NT_' -'NICHT_' -'ND' -'Mö' -'Muster_' -'Mugabe_' -'Mozilla_' -'Motto_' -'Motor' -'Motiv' -'Moral' -'Monte' -'Mond' -'Modus_' -'Mode_' -'Mitt' -'Milliarde_' -'Mili' -'Michel' -'Metro' -'Merk' -'Ment' -'Mengen_' -'Medic' -'Media' -'Maßstab_' -'Marx_' -'Mars' -'Marokko_' -'Marine_' -'Marc' -'Luxembourg_' -'Love' -'Lou' -'Log' -'List_' -'Liebe_' -'Library_' -'Less' -'Lese' -'Leicht' -'Lehre_' -'Late' -'Lange' -'Lama_' -'Lam' -'LA_' -'Ky' -'Kreuz' -'Kreditkarte_' -'Krediten_' -'Krebs' -'Kranken' -'Kosten' -'Konzept' -'Konvergenz_' -'Kontakt' -'Kongo_' -'Konflikten_' -'Kommissionspräsident_' -'Kommissions' -'Kol' -'Kohlen' -'Koalition_' -'Klar' -'Kei' -'Katastrophen_' -'Katalog_' -'Karls' -'Kamera_' -'Kalten_' -'Jewish_' -'Jesus_' -'Jere' -'Jen' -'Jan_' -'Jacques_' -'Jac' -'Isa' -'Irr' -'Ironi' -'Intervention_' -'Internetzugang_' -'Intergovernmental_' -'Intel_' -'Installer_' -'Innerhalb_' -'Inha' -'Inflationsrate_' -'Immo' -'Images_' -'Ima' -'Identifi' -'Ibiza_' -'IE' -'IC_' -'Hü' -'Hy' -'Hungary_' -'Hum' -'Hub' -'Hot' -'Host' -'Hon' -'Hollande_' -'Hilfen_' -'Hi' -'Herb' -'Heizung_' -'Hea' -'Hauptver' -'Hass' -'Hardware_' -'Harbour_' -'Handlungs' -'HT' -'HS' -'Gö' -'Gäste' -'Gun' -'Griff_' -'Graf' -'Gläubiger_' -'Gläubige' -'Glo' -'Gewiss' -'Geschäft_' -'Geräte_' -'Gepäckraum_' -'Georgien_' -'Geo' -'Gender_' -'Gegenstände_' -'Gefolge_' -'Garantien_' -'Gan' -'Galileo_' -'Freund_' -'Freund' -'Freiheits' -'Frauen' -'Franzosen_' -'Francisco_' -'Forscher_' -'Flugzeug' -'Flor' -'Flo' -'Fleisch_' -'Five_' -'File_' -'Fett' -'Festplatte_' -'Fern' -'Feed' -'Fas' -'Farbe_' -'Fang' -'Fahrrad' -'Extra' -'Exporte_' -'Explorer_' -'Ever' -'Euros_' -'Erz' -'Erst_' -'Erst' -'Erreichung_' -'Erinnerung_' -'Ergebnissen_' -'Englisch_' -'Energiequellen_' -'Energieeffizienz_' -'Enc' -'Emi' -'Elevator_' -'Einsatz' -'Eines_' -'Effekte_' -'Eco' -'EU' -'ECH' -'EB' -'Dubai_' -'Dro' -'Drittländern_' -'Dos' -'Domin' -'Display_' -'Director_' -'Dir' -'Dingen_' -'Dilemma_' -'Diesel' -'Diagnose' -'Desktop_' -'Den' -'Dem' -'Defizit' -'Dau' -'Darstellung_' -'Danish_' -'DAT' -'Custom' -'Crespo_' -'Create_' -'Count' -'Corporation_' -'Consider_' -'Computers' -'Computer' -'Clearly_' -'Cle' -'Christmas_' -'Christine_' -'Chin' -'Child_' -'Cher' -'Chen' -'Chaos_' -'Change_' -'Chancengleichheit_' -'Cent' -'Celsius_' -'Cardassian' -'Cannes_' -'Cala_' -'Cai' -'CU' -'CR' -'COM_' -'CC_' -'CB' -'Bü' -'Brust' -'Brief_' -'Brennstoffe_' -'Brennstoff' -'Branche_' -'Brad' -'Botschaften_' -'Borg_' -'Boo' -'Blue' -'Bilanz_' -'Bil' -'Bier' -'Bezahlung_' -'Betreiber' -'Bestimm' -'Beste' -'Besorgnis_' -'Beschäftigten_' -'Beschr' -'Bern' -'Bericht' -'Bereits_' -'Beobachtung' -'Behörde_' -'Bef' -'Balkon_' -'Bai' -'Bag' -'BM' -'Ayatollah_' -'Ay' -'Autor_' -'Austr' -'Auslands' -'Ausf' -'Ausbruch_' -'Ausbeutung_' -'Aufklärung_' -'Aufhebung_' -'Aufenthalts' -'Att' -'Atomwaffen_' -'Ash' -'Arzneimittel' -'Arti' -'Area_' -'Architekt' -'Arbeitsplätzen_' -'Arbeitsmarkt' -'Ara' -'Apo' -'Anspr' -'Ansehen_' -'Anschläge_' -'Anschl' -'Annan_' -'Ank' -'Anfragen_' -'Andre' -'Anbindung_' -'Among_' -'Ameri' -'Alpen_' -'Alp' -'Allow_' -'Allah_' -'Albert_' -'Albani' -'Aktions' -'Adria' -'Abu_' -'Abteilung_' -'About_' -'Abk' -'Abb' -'AVI_' -'AT_' -'AM_' -'AF' -'ACP_' -'?”_' -'== _' -'="_' -': '_' -'99_' -'93_' -'77_' -'76_' -'68' -'65' -'64' -'58_' -'53_' -'51' -'47' -'350_' -'1st_' -'1956_' -'1930_' -'150' -'.&#_' -'. )' -'. "_' -'--' -'- (_' -', '_' -'); _' -'( _' -'% ' -'!!_' -' – ' -' »_' -' «_' -' - ' -' * _' -' %._' -' %' -'€' -'ә' -'ғ' -'ір' -'э' -'ты' -'ки' -'жа' -'ен' -'ге' -'г' -'ал' -'а_' -'α_' -'α' -'ğ' -'č' -'ültig' -'ückte' -'ücher_' -'überz' -'überwachen_' -'überst' -'übermäßige' -'überge' -'überd' -'ø' -'öße' -'ötig' -'ösen_' -'ökonomische_' -'öh' -'öf' -'ín' -'éta' -'ér' -'ça_' -'å_' -'å' -'äußerster_' -'ärkte' -'är_' -'änglich' -'änger' -'änd' -'ällig' -'äft' -'ächtig' -'án' -'ßig' -'ße_' -'Äußerungen_' -'Änderungsanträgen_' -'®' -'« _' -'«' -'}} ' -'zügig' -'züge' -'zz' -'zusammenbr' -'zurzeit_' -'zum' -'zukünftige_' -'zuh' -'zogen_' -'zl' -'zier' -'zia' -'zh' -'zerstör' -'zers' -'zeichne' -'zei' -'zehn' -'zan' -'ypt' -'yl_' -'yacht' -'xp' -'wünscht_' -'wünschenswert_' -'wäch' -'wusste' -'wur' -'worte' -'word' -'wohin_' -'woch' -'wn_' -'wn' -'with' -'wit' -'wisdom_' -'wirk' -'wines_' -'wettbewerbs' -'wett' -'wes' -'wertung_' -'wellness_' -'wellbeing_' -'weißen_' -'weiterzu' -'weitem_' -'watching_' -'watch' -'wasn_' -'warf' -'wahre_' -'wahl' -'wachsende' -'vorschl' -'vornehmen_' -'vorliegt_' -'vorgeschrieben' -'voranzutreiben_' -'voneinander_' -'volunteers_' -'vollz' -'vol' -'virtuelle' -'vin_' -'vielf' -'vie' -'vi_' -'veto_' -'veränderte' -'verweisen_' -'vertrete' -'verständlich_' -'versi' -'verschwinden_' -'verschle' -'verschieden_' -'verschieben_' -'verschie' -'vermieden_' -'vermi' -'verlängern_' -'verlust' -'verletz' -'verlager' -'verkehrs' -'verha' -'verglichen_' -'vergleich' -'verfassungs' -'verdienen_' -'verdi' -'verbindlich_' -'verbesserten_' -'verantwort' -'veran' -'variations_' -'uve' -'usse' -'usel' -'usch' -'urm' -'url' -'uring_' -'urgency_' -'ups_' -'upcoming_' -'uous_' -'unzählige_' -'unweit_' -'unwahrscheinlich_' -'untersucht_' -'unterm' -'unpro' -'unmittelbaren_' -'unmi' -'unknown_' -'univers' -'unhe' -'ungssystem' -'ungsre' -'ungan_' -'unft' -'unfair_' -'unen' -'uneingeschränkt_' -'understands_' -'underscore' -'unbr' -'unanimously_' -'ume' -'ulen' -'ule' -'uer_' -'ud_' -'tür' -'té' -'tungen_' -'tum' -'tub' -'ttel' -'tron' -'tremendous_' -'treatments_' -'traten_' -'transmission_' -'transit' -'transformed_' -'transc' -'transatlantischen_' -'transactions_' -'transaction_' -'trains_' -'trad' -'tou' -'total' -'tory_' -'tolle' -'todo' -'tod' -'tive' -'tischer_' -'tire' -'tir' -'tieren_' -'tiefe_' -'tib' -'throw' -'thor' -'tho' -'tge' -'teuer' -'terminal_' -'teria' -'tens' -'tende' -'tema' -'technische' -'techni' -'tc' -'tasty_' -'tar_' -'tankers_' -'talent_' -'tak' -'tai' -'tage_' -'tage' -'systematically_' -'symbolic_' -'sym' -'sy_' -'suspension_' -'surpluses_' -'surg' -'supplement' -'suff' -'sudah_' -'subway_' -'substan' -'stück_' -'stö' -'stuff_' -'stuf' -'studie' -'struktur_' -'stro' -'strikte' -'strikes_' -'stretch' -'streng_' -'straight_' -'straf' -'stoffe_' -'stle' -'stieg_' -'stick_' -'stet_' -'stes_' -'steam_' -'steadily_' -'stea' -'statt' -'statisti' -'stamm' -'stakes_' -'stabile_' -'ssen' -'sprachliche' -'sprachen_' -'spell' -'speed' -'speeches_' -'spectacular_' -'specify_' -'sound' -'soul_' -'sorgfältige' -'sorgfältig_' -'sophisticated_' -'solved_' -'sogenannten_' -'sofa_' -'smi' -'smart_' -'slowly_' -'sive_' -'sinnvolle' -'simpli' -'sim' -'sili' -'signing_' -'signature_' -'sierungs' -'sierte' -'sieg' -'sie' -'sic' -'shut_' -'shortcomings_' -'ship' -'shifts_' -'shee' -'sge' -'sexuelle_' -'settled_' -'sema' -'secondly_' -'seba' -'scrutin' -'screening_' -'scrap' -'schwächere' -'schwäch' -'schwedischen_' -'scholar' -'schließt_' -'sches_' -'schauen_' -'sca' -'saubere' -'satisfied_' -'sak' -'safeguard_' -'rückg' -'rö' -'räge_' -'rui' -'ruh' -'ruch_' -'rter' -'roo' -'romantic_' -'roman' -'roa' -'rnähr' -'rna' -'rm_' -'rka' -'ritten_' -'risiko_' -'rige_' -'richtet_' -'ria_' -'rhe' -'reward_' -'revive' -'revers' -'reveal_' -'returning_' -'retro' -'retr' -'restoration_' -'ress' -'responded_' -'residents_' -'reside' -'reproduc' -'repr' -'repli' -'repa' -'renov' -'renminbi_' -'removing_' -'remind' -'remark' -'relocat' -'reinforce_' -'reiche_' -'register_' -'regierung_' -'regelmäßig_' -'refusal_' -'redistribution_' -'recon' -'recht' -'rechnen_' -'receives_' -'reben_' -'realistische' -'realisier' -'realised_' -'reader_' -'rator' -'ratifiziert_' -'ratification_' -'rapide_' -'ranks_' -'rank' -'rall' -'rahmen' -'ract' -'quoten_' -'quisit' -'quir' -'quie' -'ques_' -'quanti' -'qualifizierte_' -'qualifications_' -'pushed_' -'pup' -'präsentier' -'prozesses_' -'prove' -'protektionistische' -'prosperous_' -'proof_' -'prol' -'projekt_' -'programmen_' -'professionelle' -'prob' -'prize_' -'privilege_' -'printing_' -'preventive_' -'prevail' -'prestigious_' -'preserved_' -'presentation_' -'prescri' -'premature_' -'pragmatic_' -'potenziellen_' -'poss' -'poses_' -'pollut' -'pole' -'polar_' -'po_' -'plural' -'pill' -'pier' -'philosophy_' -'phas' -'pflege' -'pfl' -'pfel' -'pfe_' -'petit' -'pes_' -'persönliche' -'persuade_' -'persone' -'persist' -'perpet' -'permi' -'periode_' -'pere' -'perat' -'pensions_' -'penda' -'pemba' -'pel' -'peacekeeping_' -'patient_' -'passes_' -'partitions_' -'parlamentarische_' -'parity_' -'paren' -'papers_' -'panel_' -'pana' -'painting_' -'own' -'owe_' -'overw' -'oversight_' -'overe' -'outs_' -'outs' -'outl' -'oti' -'ote_' -'oste' -'osen_' -'osc' -'ori' -'operates_' -'opens_' -'openly_' -'opa' -'onym' -'onat' -'onal_' -'ome_' -'ologie_' -'ologi' -'oil' -'ofi' -'offensichtliche' -'odie' -'ock_' -'ochen_' -'och_' -'occupation' -'oca' -'obst' -'observation_' -'obli' -'ober' -'nzi' -'nutrition_' -'ntw' -'nsu' -'nous_' -'nost' -'nos' -'nomin' -'noi' -'nnt_' -'nner' -'nne_' -'nlage' -'nko' -'niu' -'nity_' -'nio' -'nik_' -'nightlife_' -'nien_' -'niedrigere' -'niedrige_' -'ngst' -'ngn' -'nglich_' -'ngel' -'neuro' -'neuesten_' -'nett' -'ness' -'nesian' -'ners_' -'neglig' -'neglect_' -'negativ_' -'necessity_' -'ndete' -'nden' -'naval_' -'namens_' -'nah_' -'nada_' -'männ' -'mutige' -'musik_' -'multilateralen_' -'mpin' -'mpf' -'mp_' -'movies_' -'mov' -'mounted_' -'mortgage_' -'monument' -'moment' -'modules_' -'moderni' -'modern' -'mixture_' -'mitge' -'missi' -'mism' -'misc' -'mir' -'minist' -'mining_' -'minimi' -'mina' -'militärisch' -'militant' -'meta' -'mengu' -'mengak' -'menga' -'membu' -'med' -'mechanismen_' -'measurement_' -'matche' -'master' -'massiv_' -'massage_' -'marine_' -'marginal_' -'mapp' -'mali' -'makroökonomische' -'mak' -'mai' -'mah' -'magne' -'magazine_' -'ländliche_' -'lve' -'lv' -'lungs' -'lue' -'luc' -'lua' -'lu_' -'loses_' -'logo_' -'loc' -'lobb' -'lize' -'liza' -'litik' -'liti' -'literar' -'listening_' -'liste' -'list' -'limiting_' -'liebe_' -'licherweise_' -'license_' -'lib' -'lf' -'leute_' -'letting_' -'lending_' -'lem_' -'leitung_' -'leitete_' -'leitet_' -'leis' -'legislat' -'leere' -'lect' -'laute' -'lati' -'latein' -'lager_' -'künstlich' -'käm' -'kta' -'kritisch' -'kris' -'kre' -'kopieren_' -'kontrollierte' -'kontrolle_' -'konkret_' -'konflikt' -'komplette_' -'kommunistische_' -'kolle' -'kenne_' -'kebijakan_' -'karten_' -'kampf' -'kam' -'kali' -'kale' -'kai' -'jüngere' -'jährliche_' -'judgment_' -'judge_' -'judge' -'jegliche' -'jed' -'jas' -'itze' -'itution_' -'ities_' -'istisch' -'ister' -'issen_' -'irt' -'irgendwann_' -'iranischen_' -'ira_' -'ip_' -'iot' -'io' -'invade' -'interpreti' -'interprete' -'interim_' -'interessanten_' -'intellektuelle' -'integrierten_' -'integrieren_' -'integral' -'institutionelle_' -'installer_' -'insch' -'inner_' -'inj' -'inie' -'inglich' -'ingen' -'infringement_' -'informati' -'influential_' -'ineffective_' -'industrielle_' -'induce' -'incredibly_' -'inan' -'imposing_' -'immer' -'ily_' -'illo' -'ildung_' -'ihrerseits_' -'ignoriert_' -'ignorieren_' -'ift' -'iete' -'iere_' -'iere' -'ielen_' -'iehen_' -'iegel' -'idor' -'identical_' -'idealer_' -'icul' -'ichte' -'ices_' -'ican' -'ias_' -'hü' -'hôtel_' -'hunting_' -'hunderte' -'hrung_' -'hre' -'hosted_' -'hospitality_' -'hone' -'holen_' -'hol_' -'hochwertige_' -'hne_' -'hme' -'hm_' -'hle' -'historisch_' -'his' -'hinzugefügt_' -'hint' -'hil' -'hike' -'hierzu_' -'hic' -'heutzutage_' -'hersteller_' -'heri' -'here' -'herausragende' -'height_' -'hearts_' -'health' -'heading_' -'hbo' -'haushalt' -'has' -'harg' -'hard' -'handled_' -'handed_' -'ham' -'hall' -'halb' -'hafte_' -'had' -'habit' -'gutem_' -'gten_' -'greift_' -'grasp_' -'good' -'gni' -'gn_' -'globale' -'gleicher_' -'git' -'gewünschten_' -'gespe' -'geru' -'gericht_' -'gerei' -'gerechter' -'gerechten_' -'geprägt_' -'gepflegt' -'geographical_' -'gent' -'gena' -'gemäßigte' -'gelo' -'geklärt_' -'geist' -'gehandelt_' -'gehabt_' -'gegens' -'geblieben_' -'gebeten_' -'geber' -'gation_' -'ganis' -'gangen_' -'fäll' -'fä' -'fut' -'fus' -'fung_' -'friedliche_' -'freundlicher_' -'freu' -'freez' -'fossile' -'formula_' -'formats_' -'formally_' -'forever_' -'fores' -'foods_' -'fond' -'fließen_' -'fl' -'finnische' -'findings_' -'fig' -'fiel_' -'fi_' -'ffs' -'ffen_' -'fet' -'fern' -'fer_' -'fel_' -'feind' -'fehler' -'federa' -'favourite_' -'favour' -'fassen_' -'fascinating_' -'fantas' -'fals' -'fairen_' -'faire_' -'fahrzeuge_' -'facility_' -'eßen_' -'extremism_' -'externen_' -'externe_' -'express' -'explicit_' -'expertise_' -'experimental_' -'exhibition_' -'executi' -'executed_' -'excluded_' -'exclude' -'ewe' -'evol' -'evo' -'eventuelle' -'eve' -'etzt_' -'eter_' -'ete' -'este_' -'esan' -'erweiterten_' -'erwe' -'ervi' -'ertrag' -'erstklassige' -'erson' -'erschienen' -'errichtete' -'errichten_' -'erra' -'erp' -'ernste_' -'erneuer' -'ermutigen_' -'erla' -'erka' -'erin' -'erheben_' -'ergibt_' -'erfü' -'erarbeiten_' -'entscheide' -'entl' -'ensu' -'enm' -'eng' -'ened_' -'enco' -'emphasis' -'emm' -'els' -'ellt_' -'elit' -'eliminated_' -'elf_' -'einse' -'einsch' -'eins' -'einmalige_' -'einig' -'eingereicht_' -'eingeg' -'eingeb' -'einf' -'einbr' -'einbezogen_' -'eigentlichen_' -'eigentliche_' -'eich' -'egel' -'effektiver_' -'ee' -'ecological_' -'echn' -'eche' -'ebu' -'eat' -'earn' -'durchd' -'dunkle' -'duc' -'drittens_' -'dre' -'draw' -'drag_' -'dos' -'donat' -'dominate_' -'dlich' -'dle_' -'disturb' -'distribut' -'distr' -'diss' -'disrupti' -'disi' -'discretion_' -'disappear' -'dip' -'dina' -'digitalen_' -'digitale_' -'dig_' -'differently_' -'dier' -'dich_' -'dge_' -'dge' -'devastating_' -'dete' -'destabili' -'desp' -'design' -'dero' -'derjenigen_' -'derive_' -'dera' -'deposit_' -'deport' -'deployment_' -'deployed_' -'denjenigen_' -'demonstrators_' -'demographic_' -'delle_' -'delicate_' -'defending_' -'defect' -'defeat' -'declined_' -'declare_' -'declar' -'decken_' -'dad_' -'dac' -'cus' -'cultures_' -'cts_' -'crystal' -'cry_' -'courage_' -'coordinat' -'convey' -'convert' -'conv' -'controller_' -'contaminated_' -'consistently_' -'cong' -'conf_' -'comput' -'compulsory_' -'complaints_' -'competent_' -'competences_' -'compatibility_' -'command' -'column' -'colo' -'collected_' -'clothing_' -'clim' -'clicking_' -'clarification_' -'citizenship_' -'cil' -'cigarette' -'cien' -'cial_' -'chung_' -'chte_' -'chs_' -'chronische' -'chro' -'christliche' -'chlich' -'chlag' -'chir' -'chic' -'chemische_' -'chee' -'chart' -'chairman_' -'chafts' -'ces' -'cent_' -'cen' -'cement_' -'cea' -'carrier_' -'cap' -'camp' -'cali' -'burned_' -'bung_' -'buch' -'bt' -'brother_' -'broker' -'breiter_' -'breast_' -'bran' -'brachten_' -'boy_' -'boom' -'booked_' -'blow_' -'blis' -'blick_' -'bless' -'blame_' -'bili' -'bike_' -'bien_' -'bewährte' -'beweist_' -'bewaffneten_' -'bevorzugte' -'betragen_' -'betrachte_' -'bet_' -'beständig' -'bestre' -'beschrieben_' -'beschleunigt' -'besagt_' -'berl' -'berichtet_' -'bereitgestellt_' -'bereich' -'berechtigt_' -'beneficial_' -'benefi' -'benannt' -'benachrichtigt_' -'bemerkt_' -'beliebtes' -'bekämpfung_' -'behörden_' -'befürwortet_' -'befreien_' -'befehl' -'befasst_' -'beeinflusst_' -'bedingungen_' -'bede' -'bedanken_' -'beck_' -'beberapa_' -'bbi' -'baut_' -'bathing_' -'bath' -'basically_' -'banyak_' -'bankr' -'bahan_' -'azi' -'aya_' -'automatic_' -'auto_' -'authorise' -'auszusch' -'auswärtige_' -'ausschl' -'ausreichende_' -'ausreichen_' -'ausgi' -'ausgel' -'aufweisen_' -'aufste' -'aufrecht_' -'aufges' -'auffordern_' -'auber' -'attribute' -'attending_' -'attacking_' -'attached_' -'attach' -'ats_' -'atis' -'atic_' -'aster' -'aste' -'assessments_' -'arts_' -'armo' -'ark' -'aries_' -'ards_' -'archa' -'arbeiter_' -'approval_' -'approaching_' -'appointment_' -'ape' -'anzus' -'anzug' -'anxiety_' -'anu' -'anticipated_' -'anschließend_' -'anschl' -'ansa' -'anr' -'annually_' -'annten_' -'anle' -'anhalten' -'angka' -'anger' -'angenehme' -'anfangen_' -'ando' -'andern' -'ande' -'ances_' -'analyze' -'analyst' -'amtierende' -'ame' -'ambitions_' -'amazing_' -'albeit_' -'alarm' -'ala_' -'akzept' -'aktionen_' -'airlines_' -'ahren_' -'ahr' -'ahl_' -'agte' -'aging_' -'affi' -'advocate_' -'advice_' -'adidas_' -'adhere' -'adapti' -'adapted_' -'adapt' -'acy_' -'actively_' -'act' -'acr' -'acquired_' -'acknowledg' -'achts' -'accura' -'accountable_' -'accountability_' -'accomplished_' -'accidents_' -'accident_' -'academic_' -'ac' -'abzulehnen_' -'absolute' -'ablehnen_' -'abh' -'abges' -'abe' -'abba' -'abb' -']] | _' -'Zwischenzeit_' -'Zwei_' -'Zustellbetten_' -'Zusammenfassung_' -'Zusagen_' -'Zugriff' -'Zuge' -'Zimmerbeschreibung_' -'Zertifikat' -'Zeitungen_' -'Zeitplan_' -'Zauber' -'Zahlreiche_' -'Young_' -'Xi' -'XML_' -'Wur' -'Wu' -'Wohnungs' -'Wissenschaftlern_' -'Wirtschaftss' -'Wirtschaftsr' -'Wirtschaftsm' -'Wirtschaftskrise_' -'Wirtschaftsa' -'Wirtschaftlich' -'Winter' -'Wilhelm_' -'Wiederg' -'Wichtig_' -'Who' -'Werte' -'Weltraum' -'Wellness' -'Well_' -'Weiteren_' -'Wechselkurs' -'Wachstumspakt' -'WO' -'WM' -'Voyager_' -'Votum_' -'Vorsorge' -'Vorgänger' -'Vordergrund_' -'Vorbereitungen_' -'Visionen_' -'Vin' -'Village_' -'Vil' -'Viertens_' -'Vid' -'Verwe' -'Vertretung_' -'Versorgungs' -'Versicherungen_' -'Versi' -'Verr' -'Verordnungen_' -'Vern' -'Veri' -'Verheugen_' -'Vergnügen_' -'Vereinigung_' -'Verbrauch_' -'Verantwortungs' -'Ven' -'VP_' -'Ut' -'Untersuchungs' -'Unterricht' -'Unterdrückung_' -'Unsere' -'Unruhen_' -'Unr' -'Unm' -'Unlike_' -'Ungl' -'Una' -'Umstrukturierung_' -'UNESCO_' -'Trä' -'Tower_' -'Touristen_' -'Tourismus' -'Tos' -'Tools_' -'Tom' -'Together_' -'Tisch_' -'Tickets_' -'Throughout_' -'Through_' -'Thro' -'Thi' -'Theater_' -'Terror_' -'Tempora' -'Tel_' -'Tech_' -'Tea' -'Taxi' -'Taten_' -'Tap' -'Tao' -'Tak' -'Tag' -'Tabelle_' -'TU' -'TC' -'Sü' -'Szen' -'Swoboda_' -'Sun_' -'Suche' -'Subject_' -'Stü' -'Sturm_' -'Stri' -'Streben_' -'Stiftung_' -'Steuerung_' -'Sternen' -'Staatsverschuldung_' -'Staatsschulden_' -'Staatss' -'Spiels' -'Spezie' -'Speci' -'Spani' -'Spaltung_' -'Sp' -'Sometimes_' -'Solid' -'Solche_' -'Solarium_' -'Solar' -'Soci' -'Sm' -'Slow' -'Six_' -'Sisko_' -'Single_' -'Singapur_' -'Similar_' -'Signale_' -'Shops_' -'Server' -'Series_' -'Sende' -'Senate_' -'Senat' -'Semi' -'Scien' -'Schöne' -'Schweizer_' -'Schre' -'Schlu' -'Scha' -'Say' -'Saturday_' -'SanDisk_' -'Sam' -'Salzburg_' -'ST_' -'STO' -'STE' -'ST' -'Rüstungs' -'Rule_' -'Rohstoff' -'Rock' -'Robert' -'River' -'Rica_' -'Republikaner_' -'Repo' -'Rental_' -'Renn' -'Rem' -'Rekord' -'Registrierung_' -'Regions_' -'Reduc' -'Rede' -'Recent_' -'Rea' -'RS_' -'RI_' -'RC_' -'Quo' -'Quest' -'QE_' -'Putins_' -'Publik' -'Prävention_' -'Program_' -'Political_' -'Polit' -'Poi' -'Plu' -'Platz' -'Picard_' -'Photo' -'Philip' -'Phasen_' -'Pflege_' -'Petr' -'Pet' -'Pes' -'Personen' -'Perl' -'Pent' -'Peer_' -'Patt' -'Patent' -'Patch' -'Parlament' -'Parks_' -'Parag' -'Palästina_' -'Palacio_' -'Paar_' -'PXI_' -'PM' -'PH' -'PDF_' -'Ozean' -'Ot' -'Oslo_' -'Original_' -'Ordner_' -'Ora' -'Operation_' -'Online' -'Ombudsman_' -'Olympus_' -'Og' -'Offizier' -'Obamas_' -'ODER_' -'Nue' -'Nova' -'Normal' -'Nob' -'Nik' -'Niger' -'Nichts_' -'Neue_' -'Netzwerke' -'Neigung' -'Nehmen_' -'Nan' -'Nahrungsmittel_' -'Nachrichten' -'Nachfolger_' -'Nachf' -'NP' -'NH' -'NF' -'Mü' -'Möglich' -'Männern_' -'Mund' -'Multi_' -'Mountain' -'Motion_' -'Mord_' -'Monats_' -'Moldova_' -'Moham' -'Modul_' -'Mode' -'Mod' -'Mittag' -'Mitgliedsländer_' -'Mitentscheidung' -'Michel_' -'Messen' -'Messe_' -'Mes' -'Mem' -'Meinungsumfragen_' -'Medi' -'Mea' -'McC' -'Mazedonien_' -'Maus' -'Materialien_' -'Marco_' -'Map' -'Manu' -'Manche_' -'Male' -'Make_' -'Maha' -'MH' -'ME_' -'MED' -'Lösungs' -'Länge_' -'Län' -'Luxus_' -'Lula_' -'Lui' -'Luftverschmutzung_' -'Luftraum' -'Low_' -'Louis_' -'Look_' -'Logo_' -'Loc' -'Lobby_' -'Lis' -'Linke_' -'Lif' -'Leuten_' -'Lesung_' -'Leone_' -'Lektion_' -'Leiden' -'Leid_' -'Leader' -'Lauf' -'Last' -'Lands' -'Lah' -'LT' -'LD' -'Körper' -'König' -'Kurd' -'Kreis' -'Kre' -'Kontinents_' -'Kontin' -'Konsumenten_' -'Konst' -'Konflikts_' -'Konferenz' -'Kom' -'Kolonial' -'Koll' -'Kohle' -'Know_' -'Know' -'Klin' -'Keynesian_' -'Kel' -'Kein_' -'Kau' -'Kath' -'Kapitalismus_' -'Kanal' -'Kam' -'Kaffee' -'Kabel_' -'KON' -'Juli' -'Jugendlichen_' -'Jud' -'Jews_' -'Jakob' -'Ist' -'Islamist_' -'Ir' -'Investment_' -'Intera' -'Instanz_' -'Instan' -'Installations' -'Inhalte_' -'Inflations' -'Industriep' -'Impl' -'Impf' -'Immunität_' -'Imagin' -'Illusion' -'Il' -'IV_' -'ISIS_' -'IN_' -'ICEcat_' -'IA_' -'Höhen' -'Häufig' -'Häfen_' -'Hyatt_' -'Hop' -'Hollywood_' -'Holland_' -'Hohen_' -'Hisbollah_' -'Hinweise_' -'Hinter' -'Hindernis_' -'Hil' -'High' -'Herz_' -'Herrsch' -'Herbst_' -'Her_' -'Hen' -'Heinrich_' -'Hat_' -'Hard' -'Hannover_' -'HI_' -'Guinea_' -'Guatemala_' -'Grupp' -'Greens_' -'Greeks_' -'Glücklicherweise_' -'Gibt_' -'Gewinner_' -'Gewinn_' -'Gewalt' -'Gesetzes' -'Gerichten_' -'Generalsekretär_' -'Genau_' -'Gemeinw' -'Gemeinschaftsp' -'Gemein' -'Geiste' -'Gegenwärtig_' -'Gegenwart_' -'Gee' -'Gaz' -'Gaulle_' -'Ganze_' -'Gall' -'G20_' -'Förder' -'Föderation_' -'Fußball' -'Funktionsweise_' -'Fundamental_' -'Frie' -'Freundschaft_' -'Freitag_' -'Freihandelsabkommen_' -'Freihandels' -'Franklin_' -'Format' -'Folgendes_' -'Folge' -'Flug_' -'Flexibilität_' -'Fis' -'Find' -'Finance_' -'Fehler' -'Fee' -'Fau' -'Fass' -'Fam' -'Fakten_' -'Fahrer_' -'Face' -'FM' -'FD' -'Explosi' -'Experience_' -'Evans_' -'Eva' -'Eu' -'Ethi' -'Estonia_' -'Esc' -'Erwärmung_' -'Erste' -'Erscheinung_' -'Erl' -'Erkenntnis_' -'Erfind' -'Erdgas' -'Erbe_' -'Entschlossenheit_' -'Entr' -'Entlastung_' -'Ens' -'End_' -'Empfänger_' -'Emp' -'Elf' -'Electronic' -'Einzelnen_' -'Einw' -'Einladung_' -'Einigkeit_' -'Eigentümer' -'Ehre_' -'Ehr' -'Ecuador_' -'Ebenen_' -'EWG_' -'ENT_' -'Dé' -'Dusche_' -'Durch' -'Dur' -'Drei_' -'Dre' -'Donnerstag_' -'Dona' -'Don' -'Disabled_' -'Differenzen_' -'Differenz_' -'Diesen_' -'Diag' -'Demonstranten_' -'Demokrati' -'Deflation_' -'Defense_' -'Deck' -'Davi' -'Darau' -'DP' -'Cup_' -'Crew_' -'Countries_' -'Cooperation_' -'Converter_' -'Continental_' -'Cont' -'Congo_' -'Conce' -'Compa' -'Come' -'Colon' -'Citizens_' -'Cin' -'Christ' -'Chr' -'Chicago_' -'Chemi' -'Cer' -'Cau' -'Carolin' -'Carlo_' -'Car_' -'CEO_' -'CDs_' -'Bühne_' -'Bushs_' -'Bull' -'Bulgarian_' -'Buchung_' -'Brut' -'Browser_' -'Brit' -'Boutique_' -'Bot' -'Boh' -'Binnenmarktes_' -'Bin' -'Beweg' -'Betrug_' -'Betroffenen_' -'Besteuerung_' -'Beschwerden_' -'Besatzung_' -'Bernanke_' -'Berliner_' -'Belo' -'Bekanntlich_' -'Beit' -'Beförderung_' -'Beamte_' -'Basic_' -'Bara' -'BL' -'Azer' -'Autor' -'Australien_' -'Austausch_' -'Ausgaben' -'Ausb' -'Augenmerk_' -'Auftr' -'Aufs' -'Aufr' -'Assa' -'Asiens_' -'Asi' -'Archer_' -'Arbeitss' -'Arbeitskräften_' -'Arabischen_' -'Arabien_' -'Arabi' -'Araber_' -'Appe' -'Anstatt_' -'Anna' -'Anh' -'Angola_' -'Ander' -'Amts' -'Amm' -'American' -'Alternativen_' -'Alpe' -'Akzeptanz_' -'Aktionsplan_' -'Akte' -'Airways_' -'Addi' -'Achtung_' -'Account' -'Abz' -'Abst' -'Abs' -'Able' -'Abgesehen_' -'Abfall' -'Abenteuer_' -'Abbau_' -'AND_' -'AG' -'AB_' -'A6_' -'>' -'==' -'87_' -'83_' -'76' -'74' -'69_' -'66' -'44' -'41' -'38' -'360_' -'1988_' -'1984_' -'1980er_' -'198' -'1978_' -'1975_' -'1970er_' -'1940_' -'1920' -'178' -'002' -') ._' -'%_' -'") _' -' “ _' -' » _' -' ``_' -' = [[_' -' --> _' -'…' -'”)' -'ү' -'ын' -'ыл' -'ті' -'ти' -'са' -'рі' -'ро' -'ре' -'ос' -'ле' -'ке' -'ес' -'да_' -'бол' -'ба' -'ай' -'Б' -'ι' -'üs' -'ürd' -'ündig' -'üllung' -'ühr' -'üge' -'ückt_' -'übt_' -'üblichen_' -'überzeugende' -'überwacht_' -'überschreiten_' -'überleben_' -'überh' -'überb' -'öss' -'öse_' -'ór' -'ña' -'ê' -'éri' -'äußere' -'äuft_' -'ätzung_' -'ätte' -'ärk' -'älteste_' -'älter' -'ält' -'äisch' -'ähnlichen_' -'ächte' -'äc' -'ä_' -'â' -'ár' -'ße' -'Übersetzungs' -'Überschuss_' -'Übereinkunft_' -'° _' -'}} _' -'{' -'zza_' -'zwölf_' -'zweite' -'zusätzliche' -'zustellen_' -'zustande_' -'zurückz' -'zurecht' -'zukünftig' -'zufrieden' -'zte' -'zones_' -'zon' -'zis' -'zip_' -'zio' -'zil' -'zig_' -'zierungs' -'zielen_' -'ziel' -'zeug_' -'zersch' -'zent' -'zeitge' -'zauber' -'yo_' -'yn_' -'xy' -'wäh' -'wunderschönen_' -'writ' -'worauf_' -'wooden_' -'wm' -'witnessing_' -'witnessed_' -'withdraw_' -'wissens' -'wirtschaftspolitische' -'wirtschaftliches_' -'wirt' -'winner_' -'wind' -'width_' -'widerspiegelt_' -'wi_' -'whol' -'westlich_' -'wertvollen_' -'werten_' -'wert' -'weltweite' -'welle' -'welcomed_' -'weiß' -'weich' -'wede' -'wed_' -'wechseln_' -'weakened_' -'weak' -'wd' -'wartet_' -'warned_' -'wan_' -'wage' -'wachstums_' -'vr' -'vorschlägt_' -'vorschläge_' -'vorsch' -'vorr' -'vorherige_' -'vorgegeben' -'vorgeb' -'vorg' -'vora' -'volks' -'void' -'vity_' -'visits_' -'visitor_' -'violations_' -'violation_' -'ville_' -'viewed_' -'view' -'vierte' -'vet' -'veröffentlichten_' -'verwu' -'verwirklichen_' -'verwendeten_' -'verträge' -'vertraut_' -'vertrauen_' -'vertrag_' -'verteilen_' -'verstr' -'versammlung_' -'verp' -'vernichte' -'vermögen_' -'verlä' -'verliehen_' -'verheerende' -'vergi' -'vereint_' -'vereinfacht' -'verde' -'verbreiteten_' -'verbreite' -'verbrauche' -'verbrauch' -'verbl' -'verbindung' -'verarbeitung' -'verantwortungsvolle' -'verabschieden_' -'vention' -'veness_' -'vegetables_' -'vat' -'vary_' -'variant' -'valley_' -'uß_' -'uß' -'utzte' -'utiliz' -'utilit' -'uten_' -'usu' -'uropa_' -'uro' -'urg_' -'urce' -'uran' -'ural' -'upt' -'upgrading_' -'upgraded_' -'updates_' -'unvermeidlich_' -'unv' -'unum' -'unterliegt_' -'unsp' -'unmittelbare_' -'unm' -'unli' -'unl' -'unktion' -'unkt' -'unkonventionelle' -'unklar_' -'unk' -'universelle' -'unif' -'ungsw' -'ungsr' -'ungsprogramm' -'ungsmaßnahmen_' -'ungsk' -'ungleiche' -'unfa' -'unein' -'undertaking_' -'undertaken_' -'understandable_' -'underp' -'undermining_' -'uncon' -'unch' -'unbekannte' -'umu' -'umi' -'umfassende' -'umer' -'umbu' -'ult_' -'uldig' -'uku' -'uin' -'uh_' -'uft_' -'uen' -'uble_' -'uber' -'uat' -'uali' -'ual' -'tzten_' -'typ' -'tv' -'turm_' -'tune' -'tu_' -'tst' -'trum' -'truck_' -'trocken' -'triggered_' -'trigger_' -'tric' -'treffen' -'treat' -'trea' -'traum' -'transportier' -'transnational_' -'translated_' -'translate_' -'transform_' -'tragische' -'traditionell_' -'traditionally_' -'tract' -'tour' -'tos_' -'tm' -'tlin' -'tipp' -'tili' -'tighte' -'tight' -'thriv' -'threatening_' -'thread_' -'thought' -'thick' -'therapie' -'theoretische' -'themes_' -'thanking_' -'testen_' -'teru' -'terk' -'terie' -'tere' -'tends_' -'tenden' -'tell' -'tel' -'teilzunehmen_' -'technisch_' -'technik_' -'teach_' -'tausende_' -'taus' -'tatsächliche_' -'tation_' -'tat_' -'tant' -'tank_' -'tangible_' -'tande' -'tand_' -'tam' -'taatliche' -'szen' -'symptom' -'sympath' -'switched_' -'swee' -'suspicion_' -'surveys_' -'superb_' -'summ' -'suit_' -'suicide_' -'sui' -'subsid' -'stärkt_' -'städte' -'stunde_' -'strukturen_' -'strongest_' -'stressed_' -'strengths_' -'strecke' -'streben_' -'stischen_' -'stige' -'stete' -'stern_' -'steri' -'stereo' -'stepp' -'stens_' -'stellten_' -'stel' -'stehende_' -'state' -'starb' -'stands' -'stair' -'stagnation_' -'stabilen_' -'sst' -'ssions' -'ssion' -'squa' -'squ' -'späte' -'spre' -'spo' -'spite_' -'spezifisch' -'speziellen_' -'spezi' -'specific' -'specialize' -'spec' -'spare_' -'soziales_' -'south' -'somewhere_' -'sociali' -'sno' -'smart' -'sman' -'slu' -'slogan' -'slo' -'slightly_' -'sleeping_' -'sku' -'situationen_' -'sions_' -'sinkende' -'simulation_' -'simi' -'signat' -'sicherer_' -'shu' -'shortly_' -'shoe' -'shelter' -'shell_' -'shame' -'shake' -'sg' -'sexuellen_' -'sex_' -'sevent' -'sessions_' -'separate' -'sep' -'sentence_' -'sende' -'sel_' -'sektors_' -'sees_' -'sections_' -'secondary_' -'secara_' -'script' -'schützt_' -'schwächen_' -'schwi' -'schwarz' -'schnelles_' -'schloss_' -'schließe_' -'schlich' -'schlechte_' -'schle' -'schic' -'schem_' -'schein' -'schei' -'schalte' -'schaftliche' -'schafft_' -'scare' -'sc' -'sba' -'sat_' -'santa_' -'sang' -'sammeln_' -'samkeit' -'salt_' -'sailing_' -'sagten_' -'safely_' -'safeguard' -'saat_' -'räng' -'räger' -'räft' -'rup' -'rud' -'rtung_' -'rteil' -'rounds_' -'ros_' -'rop_' -'rogramm' -'rod' -'robot' -'robe' -'road' -'rne' -'rke' -'riskier' -'riots_' -'rim' -'right' -'rig_' -'richte' -'rian' -'rez' -'reveals_' -'retail' -'resum' -'restrict_' -'restraint_' -'resso' -'ression' -'respektieren_' -'resiste' -'resilien' -'reservations_' -'republic' -'representing_' -'renovated_' -'reno' -'remuneration_' -'remit' -'reminiscent_' -'reminded_' -'reliability_' -'relative' -'relationships_' -'relat' -'rejection_' -'reit' -'reisen_' -'reine' -'reihe' -'reifen_' -'reichlich' -'reha' -'regen' -'regelungen_' -'rega' -'reformist' -'reformi' -'reflecting_' -'refle' -'referendums_' -'recourse_' -'reconciliation_' -'reconcile' -'reckung' -'recipient' -'rechung' -'reche' -'recal' -'rebel' -'reasonably_' -'reap' -'ream' -'realistische_' -'reale_' -'readily_' -'rbeit_' -'ratione' -'rational' -'ras_' -'ras' -'rangi' -'raises_' -'radar_' -'rachte' -'rach' -'race' -'rac' -'rable_' -'quotas_' -'quip' -'quest' -'py_' -'py' -'pursuit_' -'punish' -'pun_' -'pull' -'pte' -'psychische' -'prüf' -'präsident' -'protest_' -'protects_' -'pros' -'prophe' -'properties_' -'produkte_' -'produkt' -'produk' -'proceed' -'prisons_' -'printer_' -'prevents_' -'pretty_' -'presidents_' -'preparing_' -'premium_' -'prejudice_' -'predictable_' -'prechen_' -'preach' -'power' -'poten' -'pot_' -'possession_' -'posi' -'populär' -'popularity_' -'pop' -'polo' -'poll' -'polic' -'poker_' -'pointing_' -'pocket' -'poc' -'plätze_' -'pläne_' -'pity_' -'pis' -'pipe' -'pine' -'pilot' -'pi_' -'physici' -'pharmaceutical_' -'phan' -'pha' -'ph_' -'pflanz' -'personen_' -'personali' -'permission_' -'perception_' -'perc' -'pent' -'pene' -'pemimpin_' -'peer_' -'pedia_' -'pear' -'pazi' -'patr' -'pat_' -'participated_' -'partei_' -'park' -'panoramic_' -'panisch' -'panic_' -'ows_' -'owner_' -'owed_' -'ove' -'outset_' -'outline_' -'outcomes_' -'outbreak_' -'ound' -'otr' -'otel_' -'ota' -'ossene' -'osition' -'orth' -'orm_' -'orium_' -'orientiert_' -'organisieren_' -'ordnungsgemäße' -'ordert_' -'ord_' -'orbit' -'oppress' -'opp' -'operator_' -'opera_' -'ont_' -'onisch' -'omp' -'omin' -'olt' -'ological_' -'olge' -'ole_' -'oku' -'oin' -'oid_' -'oi_' -'ohner' -'ogi' -'offset_' -'odo' -'oci' -'obtaining_' -'obi' -'oat' -'nützliche_' -'näh' -'näch' -'nza' -'nutzt_' -'nutzbar_' -'nummer_' -'nuklearen_' -'nuestr' -'ntsch' -'nting' -'ntin' -'notification_' -'nor' -'nomi' -'noisy_' -'nna_' -'nka' -'nitte' -'nisses_' -'ning' -'niederländischen_' -'ngly_' -'ngka' -'nger_' -'nge' -'newspapers_' -'newsletter_' -'neue' -'nera' -'neo_' -'nel_' -'nein' -'neighbor' -'neigen_' -'nehm' -'negotiating_' -'negotiated_' -'nego' -'neg' -'nec' -'ndige' -'nders' -'nded_' -'ndan' -'nca' -'nba' -'navi' -'nationalist_' -'nary_' -'nal_' -'nacht' -'nachfolgenden_' -'nac' -'müt' -'mündlichen_' -'müh' -'möge_' -'mäßige' -'mäßig_' -'myster' -'mysql' -'music' -'muscle' -'mung' -'multinational_' -'multinational' -'muda' -'mt' -'mpt' -'mouse_' -'mos_' -'moon_' -'moo' -'monopol' -'monitored_' -'moni' -'modula' -'modernisier' -'mmt_' -'mmen' -'mma' -'mix' -'mitzu' -'mitteln_' -'mitteilen_' -'mitte' -'mitglieder_' -'misu' -'mischen_' -'mirror' -'ming' -'mineral_' -'mind' -'mill' -'migra' -'mien' -'midst_' -'micro_' -'methoden_' -'mesis' -'ment' -'mening' -'menge' -'memba' -'melalui_' -'mel_' -'meister' -'meistens_' -'mei' -'mehrfach_' -'mehrerer_' -'media' -'mechanismus_' -'meantime_' -'mbur' -'mayor_' -'mature_' -'master_' -'masse' -'maschinen_' -'masa_' -'market' -'marken' -'march' -'maps_' -'manufacturer_' -'manipulation_' -'manifest' -'mangelnde_' -'mangel' -'mandatory_' -'mall' -'malig' -'maler' -'mais_' -'maintain' -'mainstream_' -'magni' -'mac' -'lüge' -'lü' -'löst_' -'längst_' -'läng' -'ländische' -'länd' -'lz' -'lw' -'luxuriöse_' -'lust' -'lungen_' -'luen' -'luar_' -'ltung_' -'loud_' -'lone' -'lon' -'lokal_' -'loka' -'logisti' -'logie_' -'logg' -'locked_' -'lock_' -'lob' -'loaded_' -'ln' -'lli_' -'lively_' -'lis_' -'liquid_' -'liquid' -'likelihood_' -'lik' -'liegenden_' -'liefer' -'liederung_' -'licht_' -'libr' -'liberalization_' -'liability_' -'lfe' -'leverage_' -'level' -'lette_' -'lend_' -'leichter' -'leicht' -'legitimier' -'lec' -'lebenslange' -'lds_' -'lava' -'laud' -'lateralism' -'lasse' -'lase' -'lar_' -'langjährigen_' -'langfristige' -'lamp' -'label_' -'kürz' -'künstlerische' -'kühne' -'kör' -'kuli' -'ktions' -'kräftig' -'krä' -'kritischen_' -'kraft' -'kr' -'kos' -'korrigieren_' -'konzept' -'kontrollen_' -'kontro' -'kontinuierliche' -'kont' -'konsolidier' -'konservativen_' -'konkurr' -'konkreter_' -'kong' -'kondi' -'komplizierte' -'kommerzielle_' -'kommen' -'komfortablen_' -'komfortable_' -'kombiniert_' -'kollektiven_' -'knowing_' -'klingt_' -'klassischen_' -'klassische_' -'klasse_' -'kki' -'kis_' -'kirche_' -'kins_' -'kingdom_' -'kilometers_' -'kie' -'kg_' -'keys_' -'keu' -'kes' -'kere' -'kenn' -'kemajuan_' -'keinesfalls_' -'keinem_' -'keeps_' -'kb' -'kate' -'kapazität' -'kap' -'kalte' -'jüng' -'jährlichen_' -'just' -'jurisdiction' -'junger_' -'junct' -'jug' -'judiciary_' -'judges_' -'ju_' -'jekt' -'jak' -'jahres' -'ié' -'izier' -'ix_' -'ives_' -'iven_' -'itten_' -'ito_' -'ition' -'istung' -'ista' -'iss_' -'isla' -'isierte' -'isches_' -'irtschaft' -'irakischen_' -'ionary_' -'ional' -'ion' -'investitionen_' -'investigate_' -'invention' -'inv' -'inu' -'interp' -'interna' -'intern' -'interinstitutional_' -'interact' -'intensiven_' -'integrity_' -'integrierte_' -'integrate_' -'insurgents_' -'insure' -'insu' -'insti' -'installi' -'inspections_' -'insecurity_' -'innovat' -'inmitten_' -'inkl' -'inhalt' -'inhal' -'inhaftiert' -'inge' -'influenced_' -'inen_' -'inefficient_' -'industri' -'indispensable_' -'indirekt_' -'indebted' -'ind' -'incident_' -'inch_' -'inacti' -'improv' -'imported_' -'immune_' -'immo' -'immens' -'imm' -'imi' -'ilt' -'illusion' -'ille_' -'ilig' -'ilfe_' -'ild_' -'ild' -'ila' -'iki' -'ikan_' -'ii' -'iha' -'igung' -'igu' -'ignoring_' -'igi' -'iff' -'iet' -'iest_' -'ies' -'ierter_' -'ierende' -'ieht_' -'ieb' -'ids' -'ideologi' -'icon_' -'ico' -'ickl' -'ichtung_' -'ichtung' -'ibt_' -'iati' -'iPod_' -'iPhone_' -'höhere' -'hängige' -'händler_' -'hypo' -'hybrid_' -'hunger_' -'hung' -'humanitäre_' -'hum' -'hub' -'hu_' -'hrte_' -'hospital_' -'horror' -'hop_' -'honor' -'hon' -'holdings_' -'ho_' -'hnung' -'hlen_' -'hle_' -'hir' -'hinweg_' -'hinein_' -'hind' -'hiking_' -'hielten_' -'hidup_' -'herzustellen_' -'herunter_' -'herrliche' -'herr' -'herbei' -'herb' -'heran' -'henden_' -'hell_' -'heli' -'heiz' -'heim_' -'heed_' -'heating_' -'heart' -'hazardous_' -'harmoni' -'happi' -'handful_' -'hamper' -'hak_' -'gänger' -'guard' -'gründlich_' -'größter_' -'grundlegender_' -'grundlegend_' -'grouping' -'grobe' -'grip' -'grenzüberschreitenden_' -'grenzt_' -'grenze_' -'greifende' -'gratis_' -'graphi' -'graph_' -'got' -'gna' -'gly' -'glu' -'globally_' -'globaler_' -'glieder' -'gliche' -'glaubt_' -'glad_' -'gipfel' -'gingen_' -'ging' -'gien' -'ghter_' -'ghte' -'ggi' -'gezielte' -'gewährt_' -'gewo' -'gewisser' -'gewidmet_' -'gewer' -'gew' -'gesundheitliche' -'gestü' -'gestattet_' -'gestatten_' -'gesorgt_' -'gesetzgeb' -'geschwächt_' -'geschlossene' -'geschafft_' -'gesamt' -'gentl' -'genetische' -'generating_' -'genera' -'genauer_' -'gemütliche_' -'gemi' -'gemeinschaft_' -'geln_' -'gelegentlich_' -'gelder_' -'gelangt_' -'geko' -'gekennzeichnet_' -'gegenseitige' -'gefährlicher_' -'gefr' -'gebühren_' -'gebra' -'gebaut_' -'gases_' -'gard' -'garage_' -'gangs_' -'gabe_' -'fünfzig_' -'fühl' -'fällig_' -'fähig_' -'futures_' -'funktion_' -'funk' -'functionality_' -'frühere_' -'früh_' -'freilich_' -'freedoms_' -'frau' -'fraction_' -'fr' -'founding_' -'fought_' -'fossilen_' -'forming_' -'formen_' -'formation' -'form' -'forgotten_' -'forge' -'foreigners_' -'followers_' -'folg' -'fol' -'flood_' -'fizier' -'fixes_' -'fitt' -'fit' -'firm' -'finali' -'film' -'file' -'fh' -'fg' -'fetch' -'festzulegen_' -'fests' -'fenster_' -'felder_' -'feier' -'fehlen_' -'feels_' -'feedback_' -'fax_' -'favourable_' -'faszinierend' -'fas' -'farms_' -'far' -'familiengeführte' -'famili' -'falschen_' -'faktor' -'eye' -'extr' -'extending_' -'expulsion_' -'export' -'exploit_' -'expla' -'experiment' -'existiert_' -'exhaust' -'exemption_' -'exacerbate' -'evolution_' -'eventuell_' -'evan' -'evaluate_' -'euren_' -'eu_' -'etu' -'eto' -'etch' -'eta_' -'esten_' -'este' -'ession_' -'espa' -'esk' -'esen_' -'esen' -'erö' -'erzeug' -'erz' -'erwi' -'erweitert_' -'ert' -'ersucht_' -'ersuchen_' -'erstreckt_' -'erstre' -'erschw' -'erregend' -'erre' -'ernannt_' -'ermutigt_' -'erleb' -'erlangt_' -'erklärung_' -'erkennbar' -'erke' -'eria' -'ergänzt_' -'erge' -'erg_' -'erfolgte_' -'erfa' -'erbringen_' -'equi' -'equ' -'enw' -'entwicklung_' -'entstand_' -'entsprechende' -'entspannen_' -'entsch' -'ently_' -'entlich' -'entlassen_' -'entgegenzu' -'entertain' -'ente' -'ental' -'enor' -'eno' -'enme' -'enjoys_' -'enjoyable_' -'enheit_' -'engi' -'enforce_' -'endgültig_' -'endeavour' -'enda' -'encourages_' -'enburg_' -'employ' -'emie_' -'ement' -'embr' -'embo' -'ember' -'embargo_' -'ella_' -'ella' -'elites_' -'eligible_' -'eless' -'elektrische' -'electronic' -'elb' -'eise_' -'einzelner_' -'eint' -'einnehmen_' -'einiges_' -'eingetr' -'eingebe' -'eile' -'eight' -'eidung' -'eide' -'ehnen_' -'eg_' -'efizit' -'effizienten_' -'een' -'eel' -'edu' -'edl' -'eden_' -'ecken_' -'ecke' -'eben' -'earnings_' -'ean_' -'eager_' -'dys' -'dy' -'dt' -'dropped_' -'droht_' -'dringende' -'dream' -'drau' -'drafted_' -'downward_' -'dow' -'doubled_' -'donors_' -'donations_' -'dokument' -'doctrine_' -'dit_' -'districts_' -'distortion' -'distant_' -'disruption_' -'displays_' -'dismantl' -'dish' -'disen' -'discret' -'discredit' -'diplomatische_' -'din_' -'din' -'differentia' -'diesbezüglichen_' -'did' -'dicht_' -'dial' -'dhi' -'dha' -'dez_' -'deux_' -'deutet_' -'deteriorati' -'detected_' -'desired_' -'descent_' -'desa' -'derselben_' -'derivative' -'deregulation_' -'deprived_' -'deno' -'dene' -'dema' -'delivers_' -'delivering_' -'defende' -'deepening_' -'deckung' -'dec' -'debtor' -'debatte' -'deadl' -'daten' -'dateien_' -'databases_' -'dara' -'dan' -'damp' -'damages_' -'daf' -'cy' -'cult' -'cul' -'criticized_' -'criminals_' -'creature_' -'cow' -'counterparts_' -'counterfeit' -'council_' -'cost' -'coole' -'cool_' -'conversion_' -'convention_' -'contradict' -'contin' -'conten' -'construct_' -'constrain' -'consiste' -'considers_' -'conservation_' -'consequently_' -'confused_' -'confront' -'config' -'confi' -'conduc' -'condemn_' -'condemn' -'concludes_' -'conci' -'comprehen' -'compound' -'compli' -'complexity_' -'completion_' -'complain' -'compile' -'competing_' -'competence_' -'compari' -'compare_' -'compar' -'compa' -'communist_' -'communicate_' -'communi' -'commonly_' -'comment' -'combines_' -'college_' -'coherence_' -'cog' -'coa' -'closest_' -'cliff' -'cleane' -'clau' -'classical_' -'clara_' -'claiming_' -'cks' -'ckel' -'cked_' -'citi' -'circum' -'ciones_' -'cier' -'cide' -'chä' -'chsen' -'chose_' -'chnungen_' -'chil' -'chief' -'chest' -'chemicals_' -'checks_' -'chauen' -'chaften_' -'ceremon' -'cer_' -'cease_' -'caution_' -'catastrophe_' -'casinos_' -'cari' -'care' -'capt' -'capitals_' -'capitali' -'cano' -'cac' -'cabin' -'bürgerliche' -'butt' -'bust' -'burn_' -'bureaucrats_' -'bull' -'broke_' -'brochure' -'brilliant_' -'bread_' -'branche_' -'bour' -'borrowing_' -'bombing' -'bombe' -'bolster' -'blocked_' -'block' -'bl' -'biot' -'billions_' -'bezug_' -'bewusste' -'bewegung_' -'bewa' -'bevorzug' -'beu' -'betriebe_' -'betrieb_' -'betrachtete' -'betra' -'besucht_' -'bestä' -'bestimmter_' -'besticht_' -'beso' -'beschaff' -'berufen_' -'berp' -'bereiten_' -'bereiche_' -'berei' -'bemerkenswerte' -'beliebige' -'belie' -'bekannter' -'beizu' -'beis' -'behold_' -'beh' -'begleiten_' -'beginne' -'begehen_' -'began' -'befr' -'beeinträchtigen_' -'bedeutendsten_' -'beauftragt' -'bears_' -'beanspruch' -'bble' -'bay_' -'basierte_' -'bargain' -'baltischen_' -'balcony_' -'balancing_' -'bak' -'bach' -'ax_' -'avo' -'außer' -'außenpolitische' -'automo' -'automatische' -'auszur' -'auszuführen_' -'auswe' -'auslösen_' -'ausgewählt_' -'ausgenutzt_' -'auft' -'aufrechterhalten_' -'aufl' -'aufkommen_' -'aufget' -'aufgebaut_' -'auen_' -'audit' -'attracti' -'ats' -'ata' -'astr' -'assignment' -'asser' -'asia' -'ase' -'ars' -'arrive_' -'arre' -'arme' -'arit' -'arises_' -'arische' -'aren_' -'architekt' -'architect' -'arche' -'arb' -'appro' -'appar' -'apologi' -'anzuh' -'anz' -'anyway_' -'any' -'anxi' -'anwesend_' -'antwortete_' -'antworten_' -'anten_' -'anteil_' -'ante' -'anstreben_' -'ansteigen_' -'ansprechen_' -'anschließen_' -'ansch' -'annu' -'announce_' -'anno' -'anme' -'anlässlich_' -'anl' -'ani_' -'angenehm_' -'angemessenen_' -'angelegt_' -'angegriffen_' -'angebot_' -'angebliche' -'angan_' -'anes' -'anen' -'anderweitig_' -'anden_' -'andauern' -'anbelangt_' -'analyse_' -'amplif' -'amme' -'ami_' -'amended_' -'amen_' -'amb' -'alter' -'alte' -'allocat' -'allo' -'alliances_' -'alleged_' -'alleg' -'alla' -'alit' -'alike_' -'alig' -'alie' -'alia' -'alg' -'ain' -'agog' -'agent_' -'aft' -'afrikanische' -'affirm' -'advertisement' -'adventur' -'admit' -'activat' -'aco' -'achung_' -'accr' -'accord_' -'accompanying_' -'accommodati' -'ac_' -'abw' -'absurd_' -'abse' -'abschließen' -'abs' -'able' -'abl' -'abhängt_' -'abhängig' -'abgele' -'abgehalten_' -'abgeben_' -'aat' -'`_' -']]''' -'] ._' -'Zypern_' -'Zwangs' -'Zunahme_' -'Zulassung_' -'Zukunfts' -'Zivilisten_' -'Zie' -'Zh' -'Zentrala' -'Zeita' -'Zealand_' -'Zahlungs' -'Zahlung_' -'YO' -'Xi_' -'XL' -'Würden_' -'Wünsche_' -'Wäre' -'Wurzel' -'Wri' -'Works' -'Wood' -'Wolf_' -'Wol' -'Wohl_' -'Wis' -'Wirtschaftsl' -'Wind' -'Willi' -'Wies' -'Wiederaufbau_' -'Wider' -'WiFi_' -'Wetter_' -'Wett' -'Werk' -'Werden_' -'Weltb' -'Weiterhin_' -'Weis' -'Wechselkurse_' -'Wechsel_' -'Wars' -'Wand' -'Walt' -'Wai' -'Wah' -'Wach' -'WA' -'Vorw' -'Vorteil' -'Vorr' -'Vorfeld_' -'Volle' -'Vogel' -'Vit' -'Visual_' -'Visit' -'Ville' -'Villa' -'Viel' -'Vet' -'Verzögerungen_' -'Verzeichnis_' -'Verwa' -'Vertrauens' -'Versicherungs' -'Versand' -'Versa' -'Vermögens' -'Vermittlungs' -'Verlängerung_' -'Vergleich' -'Vergessen' -'Verfasser' -'Vereinfachung_' -'Verda' -'Veranstaltungsräume_' -'Vegas_' -'Varia' -'Van' -'Vale' -'VAT_' -'VA' -'Users_' -'Ursprung_' -'Urheber' -'Unterschied' -'Untere' -'Unsicherheit_' -'Uns' -'Universum' -'Unge' -'Unbe' -'Unabhängig_' -'Un_' -'Umstände_' -'Uganda' -'Ub' -'UL' -'UC' -'Tür_' -'Tätigkeiten_' -'Twin' -'Tul' -'Tuesday_' -'Treiber_' -'Treib' -'Travel_' -'Translat' -'Track' -'Total_' -'Tor_' -'Top_' -'Tochter' -'Tob' -'Tir' -'Tibe' -'Theater' -'Text' -'Teufel' -'Territori' -'Terminal_' -'Techno' -'Tarif' -'Tanz' -'TT' -'TS' -'TEC' -'Südafrika_' -'Szenario_' -'Synchron' -'Suppo' -'Suiten_' -'Stütz' -'Studie' -'Strände_' -'Strahlung_' -'Straftat' -'Store_' -'Steuersystem' -'Statistik_' -'Stand' -'Stalin_' -'Stagnation_' -'Spri' -'Sprech' -'Spre' -'Spl' -'Spiegel' -'Sozialversicherung' -'Sozialdemokrati' -'Source_' -'Sou' -'Sonnens' -'Solo' -'Solana_' -'Software' -'Soft_' -'Socialist_' -'Sno' -'Sn' -'Small_' -'Sla' -'Sky_' -'SilverFast_' -'Sigma' -'Sicherung_' -'Sicherheitsfragen_' -'Sh' -'Seve' -'Settings_' -'Sem' -'Sek' -'Seen_' -'Seele_' -'Script' -'Schwan' -'Schulungs' -'Schuld_' -'Schuh' -'Schriftsteller_' -'Schriften_' -'Schreib' -'Schluss' -'Schiffs' -'Schic' -'Schach' -'Satellite' -'Santiago_' -'Sanc' -'Sammlung_' -'Sale' -'SW' -'SV_' -'STA' -'SS' -'SP_' -'SLR_' -'SL' -'SH' -'SD_' -'SCO' -'SCH' -'Rö' -'Routine' -'Route' -'Round_' -'Roten_' -'Rosa' -'Rod' -'Richtlinien' -'Rich' -'Rezept' -'Revolution' -'Revision_' -'Reserven_' -'Res' -'Repr' -'Renminbi_' -'Religio' -'Relax_' -'Reisende' -'Reisen_' -'Reinigung_' -'Reichen_' -'Regulierungs' -'Registr' -'Redner_' -'Record' -'Rechtsg' -'Rechtsetzung' -'Ratsvorsitzes_' -'Rate' -'Randlage_' -'Rande_' -'Ramblas_' -'Rahmenprogramm' -'RSS_' -'RGB_' -'RA_' -'Quer' -'Quark' -'Pun' -'Prüf' -'Präsidenten' -'Präsenz_' -'Prozent' -'Protest' -'Protection_' -'Pros' -'Promo' -'Progress' -'Programmen_' -'Prof_' -'Product_' -'Process_' -'Prob' -'Priv' -'Princes' -'Prince' -'Premi' -'Prad' -'Power' -'Pou' -'Portal' -'Polizei' -'Poli' -'Plätze_' -'Pil' -'Pie' -'Physi' -'Photos' -'Phone' -'Philosoph' -'Philadelphia_' -'Petersburg_' -'Peru_' -'Pensions' -'Pel' -'Paul' -'Partners' -'Parliamentary_' -'Parlamentswahlen_' -'Parkplätze_' -'Parameter_' -'Papier_' -'Pakistani_' -'Ox' -'Outs' -'Others_' -'Ost_' -'Os_' -'Ori' -'Options' -'Optimismus_' -'Olive' -'Oli' -'Offensi' -'Objektiv' -'Oberfläche_' -'OLAF_' -'OL' -'OG' -'Nü' -'Nuevo_' -'Nothing_' -'Notenbanken_' -'Norm' -'Nordic_' -'Nin' -'Nieder' -'News' -'Nepal' -'Nelson_' -'Neg' -'Near_' -'Nap' -'Nahrungsmittel' -'Nachdruck_' -'NS_' -'NRO_' -'NOT' -'NE_' -'NB' -'Müll' -'Mä' -'Mut' -'Music_' -'Mus' -'Monte_' -'Mitter' -'Mittelschicht_' -'Mitarbeitern_' -'Ministeri' -'Mine' -'Milo' -'Mille' -'Militar' -'Milan_' -'Migrations' -'Miet' -'Mexik' -'Metall' -'Meta' -'Meinungsäußerung_' -'Meines_' -'Meilen' -'Mehrere_' -'McCa' -'Maz' -'Mauer_' -'Master_' -'Marken' -'Marke_' -'Mang' -'Malta_' -'MC' -'M5' -'Lösch' -'Luxemburg_' -'Lux' -'Lunch' -'Luggage_' -'Lud' -'Lot' -'Lor' -'Lohn_' -'Lohn' -'Lock' -'Loca' -'Lizenz_' -'Literatur_' -'Linien_' -'Line_' -'Lind' -'Liikanen_' -'Lich' -'Liberalen_' -'Leu' -'Letzte' -'Leonardo_' -'Leistungsbilanz' -'Leica_' -'Leh' -'Lega' -'Lebensst' -'Lebensqualität_' -'Learn_' -'Las' -'Lar' -'Landes' -'Lan' -'Lama' -'Lai' -'Lager_' -'Laden_' -'Kürz' -'Königs' -'Kämpfe_' -'Kurse' -'Kub' -'Kräften_' -'Kroatien_' -'Kro' -'Kreditgeber_' -'Krankenh' -'Kraftstoff' -'Korr' -'Kopie' -'Konservativen_' -'Kompon' -'Komplettpreise_' -'Kollege_' -'Kohäsions' -'Kohlenstoff' -'Kohle_' -'Knoten_' -'Kn' -'Klon' -'Kleid' -'Kis' -'Kinders' -'Ket' -'Kenya_' -'Kennzeichnung' -'Kathedrale_' -'Kaffee_' -'Jose' -'Jon' -'Jobs_' -'Jin' -'Jh' -'Jas' -'Jam' -'Jagd' -'Jacuzzi_' -'JA' -'Italia' -'Inzwischen_' -'Investi' -'Inve' -'Intelligen' -'Intellektuelle' -'Integrität_' -'Inte' -'Instabilität_' -'Insp' -'Insofern_' -'Innere' -'Inland' -'Impfung' -'Imperial' -'Imm' -'Ice' -'INI_' -'ICA' -'Höhepunkt_' -'Händen_' -'Hyper' -'Hurri' -'Hungers' -'How' -'Hos' -'Homo' -'Holocaust_' -'Holl' -'Hold' -'Hoffnungen_' -'Hof' -'Hindernisse_' -'Hilton_' -'Hillary_' -'Hierzu_' -'Hier' -'Hes' -'Heran' -'Hence_' -'Help_' -'Helm' -'Heimatl' -'Heb' -'Heads_' -'Haushalts_' -'Hauptb' -'Hast' -'Hans_' -'Hall' -'HP_' -'Gü' -'Göteborg_' -'Gute_' -'Gui' -'Greenspan_' -'Greater_' -'Grad' -'Gr' -'Gordon_' -'God' -'Goals_' -'Gn' -'Glaubens' -'Gipfels_' -'Gewährleistung_' -'Gesellschafts' -'Gesan' -'Gerät_' -'Gere' -'Gerade_' -'Gepäck' -'Geno' -'Genf' -'Gelegenheiten_' -'Gelds' -'Gegen_' -'Gefähr' -'Gebühr_' -'Game' -'Gam' -'Gad' -'Gabriel' -'Gab' -'GR' -'Fünfte' -'Fünf' -'Further_' -'Fuji' -'Fris' -'Friedensprozess_' -'Freib' -'Freedom_' -'Four_' -'Fotokopiereinrichtungen_' -'Fond' -'Fokus_' -'Flughafen' -'Fluggesellschaften_' -'Flexi' -'Fleisch' -'Fitness_' -'Fischereipolitik_' -'Finanzsystems_' -'Finanzminister_' -'Finanzau' -'Filter' -'Fertig' -'Fernseher_' -'Fehlen_' -'Features_' -'Fabriken_' -'FR' -'FDI_' -'FA_' -'FARC_' -'Extrem' -'Expo' -'Exi' -'Excel_' -'Evolution_' -'Events_' -'Event_' -'Europäisches_' -'Eth' -'Ess' -'Erö' -'Erweiterungs' -'Ersch' -'Erre' -'Eropa_' -'Ern' -'Ermittlung' -'Erkrankung' -'Ericsson_' -'Erfüllung_' -'Erfolgs' -'Erdöl' -'Episode' -'Entwicklungsst' -'Entwicklungsa' -'Enth' -'Enron_' -'Enl' -'Enk' -'Eng' -'Elektronik' -'Elektro' -'Eisen' -'Einzelh' -'Einkaufss' -'Einhei' -'Einh' -'Eing' -'Einfuhr' -'Einf' -'Einerseits_' -'Eine' -'Eigenschaft_' -'Eigenheim' -'Edin' -'Eden_' -'Eck' -'Easy_' -'ESS' -'ENT' -'Dörfer' -'Dutzend_' -'Drittstaaten_' -'Drei' -'Draghi_' -'Dow_' -'Dou' -'Dominion_' -'Dominica' -'Doc' -'Diktator' -'Digi' -'Dienstes_' -'Dienste' -'Did' -'Dick' -'Dez' -'Dev' -'Dest' -'Denk' -'Demand_' -'Defi' -'Days_' -'Damals_' -'Dach' -'DR_' -'DR' -'DPJ_' -'DB' -'DAM' -'Crystal_' -'Croatia_' -'Cri' -'Court' -'Could_' -'Corr' -'Conver' -'Contact_' -'Consult' -'Consequently_' -'Conc' -'Comple' -'Commerce_' -'Commander_' -'Coll' -'Coffee_' -'Chirac_' -'Chip' -'Chechnya_' -'Chat_' -'Charlie_' -'Champs_' -'Chai' -'Centr' -'Cell' -'Cath' -'Carr' -'Carmen_' -'Cari' -'CL' -'Büro_' -'Büchern_' -'Bäume_' -'By' -'Business' -'Bundesrepublik_' -'Bundeskanzlerin_' -'Building_' -'Buck' -'Brüder_' -'Bruttoinlandsprodukt' -'Brown_' -'Bridge_' -'Brandenburg' -'Bour' -'Borde' -'Bonus_' -'Bonn_' -'Bomb' -'Bog' -'Bob' -'Blut_' -'Blockade_' -'Bilde' -'Bibliotheken_' -'Bezeichnung_' -'Bewe' -'Bevor_' -'Between_' -'Betriebssystem_' -'Betrachtung_' -'Besuche' -'Bestätigung_' -'Bestände_' -'Bestellung_' -'Bestand' -'Besser' -'Beruf' -'Berechnung_' -'Bere' -'Benutzern' -'Benach' -'Bem' -'Bell_' -'Beli' -'Belgian_' -'Bekenntnis_' -'Bekannt' -'Beg' -'Befürchtungen_' -'Befür' -'Beau' -'Bearbeit' -'Bath_' -'Baj' -'Bahrain_' -'Bac' -'BSE_' -'BERLIN_' -'BD' -'B6_' -'Award' -'Automobilindustrie_' -'Auswahl' -'Austritt_' -'Australian_' -'Austin_' -'Ausstellung_' -'Aussichten_' -'Ausschluss_' -'Aussagen_' -'Aussage_' -'Ausrüstung_' -'Ausbr' -'Ausblick_' -'Ausbildungs' -'Ausarbeitung_' -'Aufwertung_' -'Aufl' -'Aufg' -'Aufb' -'Atlantik' -'Ate' -'Astro' -'Arzneimittel_' -'Arth' -'Arr' -'Arms' -'Armenia_' -'Armeni' -'Arme' -'Archiv' -'Arbeitsplatz_' -'Arbeitnehmern_' -'Arbeitnehmer' -'Applikation' -'Appl' -'Appart' -'Appar' -'Apart_' -'Apa' -'Any' -'Anschlag_' -'Ansch' -'Anruf' -'Anreiz_' -'Anr' -'Anpassungs' -'Annäherung_' -'Anne' -'Anmeld' -'Angabe_' -'Anf' -'Anderson_' -'Anbieter_' -'Analysen_' -'Anal' -'Amerikanern_' -'Alon' -'Algeria_' -'Alge' -'Alf' -'Alc' -'Albu' -'Aktienm' -'Aero' -'Admi' -'Acro' -'Acqu' -'Acht_' -'Access' -'Abänderung' -'Abw' -'Absicherung_' -'Abschwung' -'Absa' -'Abkommens_' -'Abbas_' -'ASE' -'AL_' -'ALDE_' -'AKVIS_' -'89_' -'82_' -'240_' -'215' -'1982_' -'1973_' -'1948_' -'1900_' -'18th_' -'177' -'175' -'1701_' -'160' -'128_' -'/-_' -'. ({{_' -'*_' -'**' -')]' -''']]' -'%) _' -'">' -' �_' -' – _' -' ==' -' "._' -' ", _' -'€ _' -'’ – _' -'– ' -'ע' -'өз' -'і_' -'ю' -'х_' -'то' -'т_' -'ри' -'про' -'ор' -'на' -'мо' -'ль' -'лы' -'к_' -'ин' -'ем' -'ве' -'ο_' -'μ' -'ž_' -'ši' -'Č' -'ützen' -'üte' -'ügen_' -'üchter' -'üch' -'übrig_' -'üblicherweise_' -'überzeugend_' -'überwältigende' -'übertrag' -'überraschen' -'übermitteln_' -'überla' -'übergeben_' -'überarbeitet' -'ør' -'öt' -'ört_' -'örper' -'örigkeit_' -'ören_' -'örder' -'öpf' -'ökonomisch' -'ökologischen_' -'ökologische_' -'ök' -'ño' -'és' -'éo' -'ées_' -'ça' -'äßig_' -'änk' -'ängt_' -'änen_' -'ändig' -'äm' -'ältig' -'älle_' -'ährung_' -'ähnliches_' -'ächtnis' -'ál' -'Übertr' -'Übel' -'Äthiopien_' -'Ägypte' -'» | _' -'»' -'}: _' -'|''_' -'zügig_' -'zähl' -'zynisch' -'zyklus_' -'zwing' -'zweckmäßig' -'zwanzig_' -'zw' -'zuzus' -'zuzug' -'zuwe' -'zutage_' -'zunichte_' -'zukünftigen_' -'zuha' -'zugeg' -'zugeben_' -'zona_' -'zo_' -'zn' -'zle' -'zk' -'zivile_' -'zim' -'zierte' -'zid' -'zessi' -'zens' -'zeitlich' -'zahlreicher_' -'ystemen_' -'yss' -'yle' -'yh' -'yen_' -'yed_' -'ycl' -'xio' -'xen' -'würdig' -'wür' -'wählt' -'wä' -'wunderbare' -'wun' -'worm' -'womöglich_' -'wol' -'wiss' -'wished_' -'wische' -'wirkungen_' -'wirksamer_' -'winzige' -'winds_' -'winding_' -'willig_' -'wiederholte' -'widerspiegel' -'widen' -'wid' -'wichtig' -'wich' -'wheel' -'wesentlicher_' -'werte' -'werk' -'wenigstens_' -'wen_' -'weithin_' -'weil' -'weigert' -'wechselt' -'wechsel_' -'websites_' -'wear_' -'wear' -'watched_' -'warrant_' -'warnen_' -'ware' -'wandeln_' -'wandel' -'waktu_' -'wak' -'wahrgenommen_' -'wag' -'vu' -'vre' -'vot' -'vorzus' -'vorteil' -'vorsichtig' -'vorsehen_' -'vorläufige' -'vorliegende_' -'vorgez' -'vorgestellt_' -'vorgehen_' -'voraussichtlich_' -'volunteer_' -'volle' -'vo_' -'vital' -'visual_' -'visiting_' -'vice_' -'veterinar' -'vessel_' -'verzeichnet' -'verzeichnen_' -'verwunde' -'verwi' -'verw' -'verursachten_' -'verursachte_' -'vertrieb' -'vertrau' -'verteidig' -'verstärkte_' -'versich' -'versehen' -'verschw' -'verschli' -'verschlechtert' -'verschl' -'verpflichtend' -'vermute' -'vermis' -'verlängert_' -'verlorene' -'verle' -'verlangsamt_' -'verkündet_' -'verknüpft_' -'verhäng' -'verhältnis_' -'vergangen' -'verfüg' -'verbundene_' -'verbringen_' -'verbreitete_' -'verbot' -'verbi' -'verbe' -'verbal' -'verb' -'verantwortlichen_' -'verans' -'verab' -'vede' -'vate' -'varied_' -'valued_' -'vall' -'validate' -'vacuum_' -'vaccination_' -'uver' -'uu' -'utz' -'uts_' -'utm' -'usted_' -'usst' -'ussi' -'usr' -'usion' -'user' -'ursprüngliche_' -'uropäischen_' -'urh' -'urges_' -'urge' -'upload_' -'upholding_' -'uo' -'unzulä' -'unwilling_' -'unwi' -'unverzüglich_' -'unus' -'untr' -'unterz' -'unterstreicht_' -'unterschr' -'unterscheidet_' -'unterrichte' -'unternehme' -'unterliegen_' -'unterhalt' -'untereinander_' -'unterbrochen' -'unterb' -'unsu' -'unsicheren_' -'unschuldige' -'unsa' -'unres' -'unr' -'unnecessary_' -'unle' -'unjust' -'universali' -'uniti' -'union' -'unified_' -'ungst' -'ungspro' -'ungsge' -'ungsfrei' -'ungl' -'unequ' -'underw' -'underli' -'unbest' -'umgehend_' -'umgeb' -'ulation' -'ulan' -'uhig' -'uhe' -'ugh' -'ufung_' -'ufte' -'udi' -'uche' -'uc_' -'tück' -'töten_' -'tö' -'té_' -'tätige_' -'tägliche_' -'tze' -'typischer' -'typischen_' -'typische_' -'tures_' -'tungs' -'tts' -'ttl' -'tse' -'trü' -'träum' -'truktur' -'troubled_' -'troll' -'tril' -'tribunal' -'trenn' -'trauma' -'trate' -'trap_' -'transpos' -'transmitted_' -'trail' -'trade' -'toxic_' -'tower_' -'towels_' -'tot_' -'tonn' -'toffe' -'tnis' -'tliche_' -'tles' -'tiv_' -'tisa' -'tionary_' -'tionali' -'tiny_' -'timely_' -'tile' -'tide_' -'tics_' -'tically_' -'tical' -'tial_' -'tial' -'thro' -'thou' -'thirty_' -'thinks_' -'therapy_' -'thee_' -'tern_' -'termasuk_' -'terb' -'tep' -'tension_' -'tener' -'tena' -'tempora' -'tempo_' -'tem' -'telling_' -'telecommunications_' -'teilig' -'technologi' -'td' -'tche' -'tch_' -'taucht' -'tati' -'tapi_' -'tang' -'tane' -'tain_' -'tain' -'tailored_' -'tail' -'tags' -'tac' -'tab_' -'szi' -'systematische_' -'system' -'sys_' -'synthesi' -'synchroniz' -'symboli' -'symbol' -'swor' -'sweise' -'svoll' -'sverfahren_' -'sv' -'suspect' -'survey_' -'surprisingly_' -'surge_' -'suprem' -'suppressi' -'supplier_' -'supplied_' -'supermarkets_' -'suns' -'sunnitische' -'sumber_' -'suche_' -'successor_' -'substanzielle' -'subst' -'subsidiz' -'subsidiar' -'subjects_' -'stürzen_' -'stärkeren_' -'stärke' -'stupid_' -'struggle' -'strophen' -'strive_' -'strik' -'stric' -'strengere' -'streng' -'strebt_' -'stream' -'strategisch' -'strat' -'strand' -'strahl' -'strafrechtlich' -'ston' -'stolz_' -'stoff_' -'stin' -'stim' -'steuern_' -'sten' -'stellend_' -'steile' -'steckt_' -'stecken_' -'stec' -'stays_' -'statute_' -'starv' -'starters_' -'starkes_' -'standen_' -'stande' -'standardis' -'stakeholder' -'stabili' -'staats' -'staatlich_' -'staate' -'ssta' -'ssin' -'sses_' -'sserung' -'ssene_' -'ssend' -'ssed_' -'ssal' -'srecht' -'squeeze_' -'sque' -'sprin' -'spreche_' -'spreading_' -'spoil' -'spirit' -'sphere_' -'spends_' -'spell_' -'specialist_' -'special' -'speaks_' -'spatial' -'spann' -'sos' -'sorti' -'sorry_' -'sore' -'sooner_' -'sont_' -'sonder' -'sollt' -'solide_' -'solid' -'sold' -'solar_' -'socially_' -'socialism_' -'smokers_' -'smit' -'slos' -'slim' -'slide_' -'slee' -'slan' -'skri' -'skr' -'skiing_' -'skie' -'sket' -'skepticism_' -'skat' -'sir_' -'sir' -'sip' -'sins' -'simul' -'silver_' -'sika' -'sik' -'signals_' -'sights_' -'sig' -'siert_' -'sier' -'sichtbar_' -'sible_' -'sian_' -'sia_' -'sia' -'shutt' -'show' -'shortc' -'shores_' -'shaping_' -'shaped_' -'shal' -'sgr' -'seu' -'setzung_' -'set' -'separation_' -'sentiment_' -'sensi' -'sensation' -'sends_' -'sement' -'seltene' -'seller' -'sell' -'seiner' -'sebuah_' -'sebe' -'sco_' -'sco' -'scienti' -'schüre' -'schö' -'schwerwiegende' -'schweigen_' -'schwank' -'schung_' -'schulden' -'schuh' -'schreib' -'schottische' -'schnitt' -'schnellere' -'schneide' -'schmerzhaft' -'schlechteste' -'schlechter_' -'schlag_' -'schickt' -'schicken_' -'schenken_' -'scheinlich' -'scheinbar_' -'schafts' -'scarc' -'scan' -'sberei' -'satu_' -'satisfactor' -'sani' -'sanct' -'sample_' -'sal_' -'saja_' -'sacrifice_' -'rät' -'ränk' -'rzt_' -'rungen_' -'ruled_' -'ruhige_' -'ruct' -'rth' -'rro' -'royal' -'ron_' -'roi' -'rodukt' -'rocket' -'roblem' -'rla' -'rk_' -'rist' -'risik' -'risen_' -'rine' -'rily_' -'rigoro' -'rigid' -'ried' -'riding_' -'rid_' -'rice_' -'rgen_' -'rf_' -'revolutionäre' -'reviv' -'revis' -'rever' -'resur' -'resultierende' -'restrictive_' -'restr' -'respekt' -'resorts_' -'resolving_' -'reserven_' -'reserv' -'republik' -'repressive_' -'reporte' -'reor' -'renz' -'rende' -'relies_' -'relaxed_' -'relaxation_' -'rej' -'reitung' -'reiterate_' -'reiches_' -'reichende' -'reibung' -'regulierung' -'registrierte' -'registriert_' -'regiert_' -'regelmäßige' -'refusing_' -'recurr' -'recover' -'recording' -'recipe_' -'reci' -'rechtliche' -'rechtfertigen_' -'rechnung' -'rech' -'recap' -'reasoning_' -'realized_' -'reaches_' -'rdl' -'rdin' -'rche' -'raus' -'rats_' -'ratified_' -'rase' -'rasant' -'rarely_' -'rapi' -'ramp' -'ramm' -'rale' -'rak' -'rail' -'raft_' -'raff' -'radiation_' -'rad_' -'query_' -'quellen_' -'quarte' -'quantitativen_' -'purely_' -'puls' -'pull_' -'pulat' -'publish_' -'pts_' -'pta' -'pré' -'proxy_' -'provinc' -'proto' -'protests_' -'protecti' -'protagonist' -'propriet' -'proportional_' -'propo' -'prope' -'pron' -'promptly_' -'proliferation_' -'projected_' -'profit' -'profi' -'proclaimed_' -'problemlos_' -'prinzipie' -'prin' -'primäre' -'pricing_' -'pric' -'pretext_' -'prediction' -'precedent_' -'precautionary_' -'preca' -'prachigen_' -'ppo' -'pping_' -'potenziell_' -'potato' -'pot' -'postponed_' -'positioned_' -'portray' -'porte' -'por' -'poorly_' -'politischem_' -'polariz' -'pod' -'plum' -'plot' -'plo' -'plai' -'placing_' -'pita' -'pit_' -'piscin' -'pin_' -'pilla' -'pielen_' -'piele_' -'pie_' -'pian' -'photograph_' -'phe' -'pfer' -'pfen_' -'pets_' -'petition_' -'pers_' -'permits_' -'peripheral_' -'penu' -'peni' -'pende' -'pek' -'pedestrian_' -'patron' -'paths_' -'patent' -'patch_' -'passport_' -'passive_' -'passiv' -'passionate' -'partnerschaftliche' -'participating_' -'parl' -'parent_' -'paraly' -'paradi' -'pap' -'pang' -'pad_' -'packaging_' -'owe' -'overha' -'outsourc' -'outlined_' -'outlet' -'outf' -'outd' -'ount' -'ounce' -'oul_' -'oui' -'oud_' -'otis' -'osten_' -'osk' -'orti' -'ork' -'origins_' -'orient' -'ori_' -'organisms_' -'orf' -'ordne' -'ordentliche' -'orat' -'orang_' -'oran_' -'optimize' -'optimism_' -'optimier' -'optical_' -'oppo' -'opoulo' -'opol' -'opo' -'operators_' -'openness_' -'oon_' -'ood_' -'ontr' -'onist' -'onic' -'ones' -'onds_' -'ond_' -'ommene' -'ologis' -'oli_' -'oka' -'oing_' -'oid' -'ogramm' -'ogn' -'ogg' -'ofs_' -'offene' -'odell' -'occup' -'observer_' -'observe_' -'oberste_' -'oberfläch' -'obere' -'oasis_' -'nütz' -'nördlichen_' -'nächstes_' -'nä' -'nwe' -'nver' -'nur' -'null' -'ntion_' -'ntie' -'nted_' -'nstein_' -'nsh' -'nsa' -'np' -'notorious' -'nostalgi' -'normali' -'nominat' -'noc' -'nkt_' -'nineteenth_' -'nifi' -'niederge' -'nian' -'neueste_' -'netze_' -'nest' -'nese_' -'nern_' -'nente' -'neiden' -'neglect' -'neben' -'ndu' -'nds' -'ndr' -'ndo' -'ndl' -'ndet_' -'nde' -'ncia' -'nationales_' -'nas' -'nant' -'nano' -'nami' -'nahezu_' -'nad' -'nacht_' -'nachhaltiger' -'mé' -'mäßigen_' -'mäß' -'mächte_' -'muy_' -'muti' -'muster' -'murdered_' -'murder' -'mund' -'mt_' -'mst' -'mse' -'mpi' -'moves_' -'motive' -'mortgages_' -'morph' -'morat' -'moralischen_' -'monatlich' -'molecul' -'modernization_' -'modernis' -'modernes_' -'mobile' -'mmern_' -'mium_' -'mittlere' -'mistakes_' -'misleading_' -'miracle_' -'ministeri' -'minded_' -'mili' -'mie_' -'mh' -'mexikanische' -'metr' -'method' -'metal_' -'messe' -'menya' -'menta' -'menschen' -'meme' -'melden_' -'md' -'mba_' -'maximize_' -'maximal' -'mau' -'matic_' -'mathematics_' -'materielle' -'massa' -'mask_' -'marginali' -'margin_' -'marble_' -'mann' -'manipulier' -'mangelnde' -'manc' -'managers_' -'malt' -'maker_' -'maj' -'magi' -'maga' -'mad' -'macro_' -'mache' -'läßt_' -'läufig' -'lze' -'lying_' -'lush_' -'luck' -'ltungs' -'lous_' -'loss' -'lokaler_' -'lohnt_' -'logistics_' -'llusion' -'llung_' -'llschaft' -'lli' -'ller' -'llel' -'live' -'listened_' -'lism' -'lisi' -'lische' -'lio' -'link' -'lings_' -'liness_' -'lighting_' -'lifetime_' -'ließ' -'lien' -'licensing_' -'liberat' -'liberalisi' -'liberali' -'liberalen_' -'liberale' -'liat' -'lian' -'liabilities_' -'letztendlich_' -'lens_' -'lengthy_' -'lender' -'leistungs' -'leh' -'left' -'lebte' -'leas' -'lding_' -'ldet_' -'lba' -'lb' -'laureate' -'laundry_' -'laufende' -'lauf_' -'latz' -'laser_' -'laptop_' -'landwirtschaftlichen_' -'landschaft_' -'lana' -'lakes_' -'lah_' -'lage' -'ladi' -'lacking_' -'könnt_' -'käme_' -'kw' -'kurzfristigen_' -'kurzfristige_' -'kurzen_' -'kurse_' -'kup' -'kunden' -'kultur_' -'kula' -'kua' -'ktivi' -'kten_' -'kreuz' -'kret' -'kreise' -'krei' -'kratisch' -'krank_' -'kow' -'kostenfreien_' -'kostenfreiem_' -'korre' -'koreanische' -'kontinentale' -'kontaktieren_' -'konstitutionelle' -'konservative' -'komplizierten_' -'komplexen_' -'kompet' -'kompakt' -'komfortabel_' -'kolonial' -'koll' -'kok_' -'knappe' -'kluge' -'klinische' -'klich' -'kleines_' -'klassische' -'klassi' -'klapp' -'kl' -'kka' -'kische' -'kinder' -'kilometre' -'kid' -'kf' -'kett' -'kerja_' -'kehr_' -'kehr' -'kamera' -'kal' -'jö' -'justification_' -'juristische' -'jung' -'jun' -'journalist_' -'journal' -'jou' -'join' -'jeu' -'jeni' -'jeglicher_' -'jan' -'izi' -'iza' -'ix' -'ivat' -'iva' -'iv_' -'ium' -'ité_' -'itz' -'itts' -'itis' -'ita_' -'istic_' -'isti' -'iste' -'issuing_' -'issi' -'ison_' -'isolated_' -'isolat' -'islamistische' -'islamische_' -'isi_' -'irrev' -'irr' -'irgendeiner_' -'ires_' -'irc' -'iplin' -'ios_' -'ioni' -'iona' -'inviting_' -'investasi_' -'invasion_' -'intuitive' -'intra' -'intolera' -'interview_' -'interventions_' -'interv' -'interrupt' -'internationally_' -'interior_' -'interg' -'intensive' -'intensiv_' -'intelligente_' -'inte' -'intak' -'inta' -'institutionalis' -'instances_' -'inst' -'inspiri' -'inspectors_' -'insofern_' -'insist' -'inse' -'inquir' -'innehat' -'inne' -'injection' -'iniert' -'inhibit' -'inhaltlich' -'infra' -'inflict' -'infi' -'infectious_' -'infe' -'inex' -'inevitably_' -'iness_' -'ineffiziente' -'industriellen_' -'industrialis' -'indu' -'indische_' -'indirect_' -'indication_' -'incur' -'incorrect' -'incorporate_' -'inclusive_' -'incl' -'incapable_' -'inca' -'inat' -'inander_' -'inability_' -'impl' -'imperial' -'impedi' -'impede' -'impair' -'immigrant_' -'imba' -'ilte' -'illnesses_' -'ilde' -'iken_' -'ignor' -'igli' -'ight' -'ifor' -'ifel' -'ieß' -'iev' -'iess' -'ient' -'ieg_' -'ieden' -'ied' -'ido_' -'idio' -'identification_' -'identifi' -'ideale' -'ick' -'icial' -'ichtig' -'icht' -'icate' -'ibility_' -'ibi' -'iba' -'hübsch' -'höf' -'höchste' -'häus' -'häufiger_' -'hängen_' -'hw' -'hus' -'hurr' -'hunt' -'hts_' -'htm' -'hten_' -'hrung' -'hospitals_' -'hos' -'hors' -'hop' -'homo' -'homeland_' -'holt' -'hohes_' -'hochrangige' -'hnte' -'hmig' -'hmen_' -'hija' -'highway_' -'hierfür_' -'herz' -'hervorrufen_' -'hervorragende' -'herkömmlichen_' -'herausf' -'hende_' -'hemm' -'heb' -'heav' -'heated_' -'headlines_' -'header' -'headed_' -'hb' -'hav' -'hauses_' -'hause' -'haus' -'hate_' -'hast' -'harmonise_' -'harm' -'harde' -'harass' -'hanya_' -'handlungen_' -'halb_' -'haft' -'haf' -'habt_' -'günstige' -'gängig' -'gym' -'gura' -'gul' -'guitar' -'guide' -'guidance_' -'guest' -'guardian_' -'gste' -'grüne' -'gründet_' -'größerem_' -'größe' -'grösste' -'großartige_' -'grou' -'grie' -'gravierende' -'grausam' -'gog' -'god' -'gnet' -'gma' -'glücklich_' -'globalis' -'glichen_' -'girls_' -'gion' -'gin_' -'gin' -'gie_' -'giant_' -'geäußerten_' -'gewöhnlich' -'gewi' -'gewec' -'gewe' -'gewalt' -'getau' -'gesti' -'gesteckt_' -'gestaltete' -'geson' -'gesenkt_' -'gesellschaftlichen_' -'geschäft_' -'geschwindigkeit_' -'geschm' -'gerufen_' -'geriet' -'geri' -'gerechtfertigt_' -'gerechte_' -'geprägten_' -'geopolitische' -'geopolitical_' -'genom' -'gener' -'genen_' -'gende' -'gence_' -'gemessen_' -'gemeins' -'geldpolitische' -'geld' -'gehe_' -'geha' -'gegr' -'gegner' -'gegenseitige_' -'gefü' -'gefo' -'gefangen_' -'gef' -'geeignete_' -'geehrt' -'gedenkt_' -'gebiete_' -'geber_' -'gebe' -'gea' -'gather_' -'gastro' -'gasse_' -'gaps_' -'gall' -'gage' -'füllen_' -'förderung_' -'fähigen_' -'fungier' -'fung' -'fundamentally_' -'func' -'ftig' -'fter_' -'fst' -'früh' -'fruit' -'fronts_' -'frontier' -'fristig' -'frisch_' -'freut_' -'freundlichen_' -'frequent' -'fragile_' -'founder_' -'foun' -'fortzu' -'fortune_' -'fortgeschrittene' -'formulierte_' -'formali' -'formale' -'forg' -'foresee' -'forecast_' -'forder' -'forci' -'font' -'folgte' -'fold_' -'fn' -'flü' -'flugzeuge' -'flowing_' -'flower' -'flexibler' -'flexible' -'fled' -'flavo' -'flag' -'fizi' -'fix' -'fisch' -'finger' -'fiction_' -'fica' -'ffl' -'ffene' -'ffekt' -'feststellt' -'feste_' -'feiern_' -'fehlenden_' -'fec' -'featured_' -'feasible_' -'fea' -'favorite_' -'favored_' -'fau' -'fasc' -'farbe' -'fantastische' -'fangen_' -'faire' -'fahrts' -'fahrer' -'fahren' -'facilitating_' -'facilitat' -'fache_' -'fache' -'fabric_' -'fab' -'exzellenten_' -'extensi' -'exquisite_' -'exposure_' -'exploration_' -'exploiting_' -'experiencing_' -'exklusiv' -'existierende' -'exert_' -'exercise' -'exempl' -'exclu' -'exchange' -'excellence_' -'even' -'euro' -'euer' -'ette_' -'eting' -'eth_' -'eternal_' -'etabliert' -'etablieren_' -'esu' -'esto' -'estig' -'esta_' -'esses_' -'eso' -'erwecken_' -'erupt' -'erungen_' -'erta' -'erstens_' -'erstatte' -'erschl' -'errors_' -'errichtet_' -'erreich' -'erpr' -'ernen' -'ermä' -'erma' -'erläutern_' -'erleichter' -'erklär' -'erit' -'eris' -'eries_' -'erholsame' -'ergi' -'erge_' -'ergab' -'erfe' -'ereich_' -'erbe_' -'erbaut_' -'erbar' -'eran' -'equip' -'equat' -'epidemic_' -'epidemi' -'envi' -'entziehen_' -'entworfen_' -'enttäuscht' -'entspr' -'entschied_' -'entscheidung' -'entity_' -'entgegens' -'entgegenge' -'entfernten_' -'entfallen_' -'entail' -'ensured_' -'enorm_' -'enli' -'enh' -'engineer' -'energisch' -'ener' -'endanger' -'enact' -'employee_' -'empfäng' -'empfunden' -'empf' -'emotions_' -'emble' -'ellung_' -'elles_' -'electr' -'ekte' -'ej' -'eitig_' -'eir' -'einzust' -'einzuräumen_' -'einzurichten_' -'einzul' -'einzuf' -'einseitig' -'einräumen_' -'eingerichteten_' -'eingeh' -'einführen_' -'einfü' -'einbezieh' -'einbar' -'einb' -'eilt_' -'eilen_' -'eigne' -'eigenständige' -'eigens' -'eigenem_' -'eid' -'eichn' -'eho' -'ehl' -'eful_' -'efi' -'effizientere' -'effiziente_' -'effizient_' -'effizien' -'effekt_' -'editor' -'ede_' -'ecu' -'eck' -'echo' -'ebung_' -'ebnen_' -'dü' -'durchsetzen_' -'durchschnittliche_' -'durchschnittlich_' -'durchgesetzt_' -'duration_' -'duk' -'dst' -'dry' -'drucken' -'dron' -'drinking_' -'drift' -'dreh' -'drastisch_' -'drastic_' -'drasti' -'drag' -'dou' -'dorf_' -'dop' -'dog_' -'document' -'divisions_' -'dividing_' -'divide_' -'divi' -'diversen_' -'div' -'dition' -'distress_' -'distort' -'dist' -'dispose' -'dismissed_' -'disg' -'discre' -'discourse_' -'discourage' -'dische' -'disappointed_' -'disabilit' -'diper' -'dioxide_' -'dik' -'dih' -'digung' -'digkeit' -'diesbezügliche_' -'dib' -'diak' -'dg' -'devote_' -'devi' -'deutlichen_' -'detriment_' -'deten' -'det_' -'destru' -'despair_' -'designation_' -'desde_' -'derived_' -'dere' -'derartiger_' -'depressed_' -'deposits_' -'deploy' -'dense' -'denounce' -'demonstrieren_' -'demokratisch_' -'demograph' -'democrat' -'dementsprechend_' -'dell_' -'delightful_' -'delete_' -'degrees_' -'ded' -'decor' -'declines_' -'debe' -'debated_' -'dde' -'dba' -'dauerhafte' -'dare' -'dad' -'dacht' -'cycles_' -'curtail' -'cultivat' -'culminat' -'cue' -'ctur' -'ction' -'crush' -'crude_' -'critici' -'cript' -'crash_' -'cras' -'craft_' -'craft' -'cr' -'cozy_' -'couple' -'coup_' -'cotton_' -'cosmetic' -'correspond_' -'corps' -'copie' -'convince_' -'convicted_' -'convict' -'conversation_' -'controversial_' -'contagion_' -'contacts_' -'consult_' -'constitutes_' -'constituenc' -'constitu' -'consist' -'conquer_' -'connecting_' -'coni' -'confo' -'confined_' -'configure_' -'confe' -'conf' -'conciliation_' -'concentrated_' -'compre' -'compr' -'compliment_' -'complaint_' -'complain_' -'comparative_' -'common' -'commodities_' -'commission' -'commercial' -'comer' -'come' -'colonial_' -'collectively_' -'collections_' -'cola' -'cock' -'coat' -'coastal_' -'clu' -'closure_' -'clinical_' -'clin' -'cli' -'ckig' -'cket_' -'cke' -'cis' -'cinema' -'chtigen_' -'chsel' -'child' -'checke' -'chas' -'charisma' -'charakter_' -'characteristics_' -'characteristic_' -'change' -'chair' -'cere' -'censorship_' -'ced' -'cce' -'cca' -'cave' -'cautious_' -'cau' -'casual' -'casino_' -'carries_' -'capture_' -'captur' -'capability_' -'cap_' -'cant' -'cans_' -'cana' -'came' -'calculat' -'cafe' -'caci' -'bürokratische' -'bösartige' -'byl' -'but' -'burning_' -'burn' -'bureaucracy_' -'bum' -'bullet' -'builds_' -'buffer' -'brü' -'browsing_' -'brothers_' -'brake' -'boxe' -'bottle' -'borrowers_' -'borne_' -'bora' -'bookings_' -'bombard' -'boli' -'boil' -'bn' -'bloße_' -'bloß' -'blocking_' -'blockiert_' -'bloc' -'bliche' -'blase_' -'blam' -'birds_' -'billig_' -'bilität' -'bid_' -'bias' -'bia' -'bezi' -'bezeichnen_' -'bewert' -'beverages_' -'beunruhig' -'bett_' -'bett' -'betrü' -'beträchtliche' -'betrieb' -'betreiber' -'bete' -'bestrafen_' -'bestehender_' -'bestanden_' -'besitze' -'beside' -'besetzte' -'beset' -'beschw' -'beschreiben_' -'beschrei' -'berühmte_' -'beru' -'bereitstellen_' -'bereitet_' -'benötigten_' -'benötigte_' -'benen' -'benchmark' -'benachteiligt' -'benachbarten_' -'bemerken_' -'belong' -'beliefs_' -'beke' -'bekannteste' -'beit' -'beides_' -'behinder' -'begünstig' -'begriffen_' -'begrenzten_' -'begleitet_' -'begeben_' -'bege' -'begangen_' -'beförder' -'befriedigen_' -'befri' -'befrei' -'befa' -'bedeutete_' -'bedding_' -'bedauerlich_' -'bearbeiten_' -'bb' -'bat_' -'bases_' -'base' -'barri' -'banner' -'banned_' -'bang_' -'bailout_' -'bai_' -'ays_' -'aya' -'aw_' -'avoiding_' -'aviation_' -'avel' -'außergewöhnlichen_' -'automati' -'author' -'auszuw' -'auszul' -'auszuarbeiten_' -'ausstatt' -'aussi' -'ausser' -'auss' -'ausreichende' -'ausr' -'ausp' -'ausn' -'ausgest' -'ausges' -'ausgehend_' -'ausgedehnt' -'ausfallen_' -'ausbr' -'aum_' -'ault_' -'auli' -'aufzuh' -'aufrufen_' -'aufri' -'aufregende' -'aufgez' -'aufgel' -'aufgegriffen_' -'aufgef' -'auferleg' -'aufbe' -'auer_' -'audit_' -'aucht' -'ature_' -'aturan_' -'attr' -'atta' -'atra' -'atori' -'atm' -'atla' -'ativ' -'ata_' -'asy' -'astu' -'ast_' -'assumption_' -'assum' -'assessing_' -'assess_' -'asserti' -'assembly_' -'assembl' -'asse_' -'aspir' -'asks_' -'asa' -'artige' -'artif' -'arsen' -'arrangement_' -'arranged_' -'arm_' -'arität' -'arist' -'arising_' -'arde' -'archives_' -'arbeitende' -'arabische_' -'aqua' -'apt' -'approv' -'appoint' -'apart' -'anzust' -'anzukurbeln_' -'anzugehen_' -'anzuerkennen_' -'anzi' -'antis' -'antiqu' -'antic' -'anta' -'anstieg' -'anstehende' -'anspruchsvolle' -'ansi' -'anny' -'announcement' -'anni' -'anna' -'ann' -'ankomm' -'anische_' -'angry_' -'angre' -'angestrebte' -'angesprochene' -'anges' -'angene' -'angelegte' -'angekündigte' -'angekündigt_' -'angehör' -'angehen_' -'angegeben_' -'angebot' -'angeblich_' -'anfä' -'anen_' -'ands' -'anat' -'analysier' -'analyses_' -'amin' -'amer' -'ambiance_' -'aman' -'alu' -'altogether_' -'alternat' -'allu' -'alls' -'algo' -'aler_' -'alem' -'akzeptabel_' -'aktivist' -'aktiviert_' -'aktiven_' -'aktiv' -'aktion' -'ais' -'aim' -'ahlung_' -'ahlen_' -'ahe' -'ags_' -'agon' -'aggressive' -'aggre' -'after' -'afraid_' -'afi' -'afford' -'afflict' -'advocating_' -'advocates_' -'adventure_' -'adu' -'ads_' -'admitted_' -'administrat' -'adjust' -'adj' -'add' -'acute_' -'actress_' -'acquisition_' -'acle' -'acke' -'aches_' -'ache_' -'ace_' -'accumulati' -'accounted_' -'accessories_' -'accesse' -'abzust' -'abzule' -'abuses_' -'abstain_' -'abstain' -'absor' -'abschl' -'aboard_' -'ablen' -'ablauf' -'abkommens_' -'abgez' -'abgegeben_' -'abandoning_' -'Zwischen' -'Zwi' -'Zweig' -'Zut' -'Zuschauer' -'Zuf' -'Zucker_' -'Zit' -'Zell' -'Zeitschrift_' -'Zeitr' -'Zeilen' -'Zehn_' -'Zar' -'Yuk' -'Yen_' -'Yemen_' -'Yam' -'Xa' -'XVI' -'XLS' -'Wüsten' -'Wür' -'Wälder_' -'Wy' -'Worf_' -'Word' -'Woods_' -'Wissens_' -'Wissens' -'Wirtschaftswachstums_' -'Winters' -'Winds' -'Will' -'Wiener_' -'Widersprüche_' -'Wide' -'Whenever_' -'Wettbewerbe' -'Wertpapiere_' -'Wertpapier' -'Werkzeug_' -'Werkst' -'Werde_' -'Wenige' -'Weltwirtschafts' -'Wellnessbereich_' -'Weiterentwicklung_' -'Weihnachten_' -'Weich' -'Wed' -'Weber_' -'Wave' -'Watt' -'Wasch' -'Warnung' -'Wandels_' -'Wan' -'Wahrnehmung_' -'Wahlkampf' -'Wag' -'Wachstumss' -'WP' -'Völkerrecht' -'Vs_' -'Vorredner' -'Vorre' -'Vorrang_' -'Vorherrschaft_' -'Voraus_' -'Voraus' -'Volume' -'Vitorino_' -'Visu' -'Visa_' -'Vis' -'Vinc' -'Victoria_' -'Via_' -'Verwirklichung_' -'Vertrieb' -'Vertreter' -'Vertrags_' -'Vertrages_' -'Verteidigungsminister' -'Vermögen_' -'Verletz' -'Verlagerung_' -'Verkehrsnetz' -'Verkehrsa' -'Verkaufs' -'Verhältnis' -'Vereinig' -'Verbraucherschutz' -'Verbrauchern_' -'Verantwortlichkeit' -'Vario' -'VIC' -'VE' -'VD' -'Ura' -'Updates_' -'Unterscheidung_' -'Unters' -'Unternehmer' -'Unterfangen_' -'Unst' -'Universal_' -'Unionsbürger' -'Unfälle' -'Underground_' -'Unde' -'Umst' -'Umsatz_' -'Umbr' -'Ultimate' -'Ul' -'Uf' -'USE_' -'UP' -'UNM' -'Türen_' -'Tät' -'Twe' -'Turni' -'Turm' -'Turi' -'Tunnel' -'Tud' -'Tsi' -'Tschech' -'Truppe' -'Troi' -'Tric' -'Tradi' -'Tr' -'Toyota_' -'Ton_' -'Tomo' -'Tom_' -'Toleranz_' -'Tode_' -'Tod' -'Thor' -'Thom' -'Thirdly_' -'Thinking_' -'Theor' -'Theatre_' -'Thal' -'Th' -'Terrace_' -'Terra' -'Tenn' -'Tendenz_' -'Ten_' -'Temp' -'Tell' -'Tehran_' -'Technologie' -'Tay' -'Tausend' -'Tatsachen_' -'Task_' -'Take' -'Table' -'Tabak' -'TP' -'TOS_' -'TION' -'Süde' -'Südamerika' -'Säule' -'Sydney_' -'Superma' -'Sum' -'Sud' -'Subve' -'Substanz' -'Subsidiarität_' -'Stück_' -'Stuttgart_' -'Stufe_' -'Studierende' -'Student' -'Stress_' -'Stock_' -'Sto' -'Stil' -'Stig' -'Stift' -'Sti' -'Steuererhöhungen_' -'Stereo' -'Steigen' -'Stay' -'Statut' -'Statistiken_' -'Station' -'Starts' -'Standort' -'Stamm' -'Stal' -'Stabilitäts' -'Staatsb' -'Staatsa' -'Sri_' -'Sponsor' -'Spenden' -'Spekulation' -'Speed_' -'Spaziergang_' -'Sozials' -'Sozialpartner' -'Souvenir' -'Sonic_' -'Songs' -'Somit_' -'Solutions_' -'Sobald_' -'Slowakei_' -'Slideshows_' -'Sk' -'Sina' -'Simpl' -'Silver_' -'Silv' -'Sil' -'Siedlungen_' -'Sichtweise_' -'Sich' -'Shopping_' -'Sharon_' -'Sex_' -'Seuche' -'Session_' -'Serikat_' -'Seri' -'Sensor' -'Selbstver' -'Selbstbe' -'Sekunden_' -'Sekt' -'Seitens' -'Segel' -'Seg' -'Schü' -'Schönheit_' -'Schä' -'Schwerpunkt' -'Schulb' -'Schra' -'Schmidt_' -'Schlacht_' -'Schiffe_' -'Schichten_' -'Schengen_' -'Schauspieler_' -'Scandinavia' -'Save_' -'Sav' -'Sat_' -'Sanierung_' -'Samu' -'Samstag_' -'Same' -'Saharan_' -'Sah' -'Sag' -'Safe_' -'Sac' -'Sab' -'Saatgut_' -'SOL' -'SC_' -'Rückzug_' -'Rücken_' -'Roth' -'Rollen' -'Ring' -'Rig' -'Ries' -'Richtig' -'Rice_' -'Ria' -'Review_' -'Reu' -'Result' -'Ressourcen' -'Residenz_' -'Residence_' -'Reparatur' -'Rennen_' -'Renditen_' -'Rek' -'Reit' -'Reinh' -'Reihenfolge_' -'Reife' -'Reichtum_' -'Reichs' -'Reich' -'Regulierungsbehörden_' -'Regen' -'Reformp' -'Refle' -'Referen' -'Redebeitr' -'Recovery_' -'Rechtsvorschrift_' -'Rechtsgrundlage_' -'Rechnungshof' -'Rechner_' -'Rechn' -'Recently_' -'Read_' -'Read' -'Raumfahrt' -'Rauch' -'Ras' -'Rang' -'Radisson_' -'RS' -'RP' -'REACH_' -'RC' -'Quin' -'Quart' -'Qi' -'Pä' -'Pyr' -'Putsch' -'Ps' -'Präsidentschafts' -'Provid' -'Protokolls_' -'Prost' -'Promi' -'Produktp' -'Produktivitäts' -'Prinz' -'Print' -'Primär' -'Prima' -'Price_' -'Pres' -'Prag_' -'Posten_' -'Portfolio' -'Populis' -'Polizist' -'Polizeia' -'Poettering_' -'Poe' -'Plugin' -'PlayStation_' -'Plattform_' -'Pir' -'Pipe' -'Philippines_' -'Phil' -'Pfe' -'Persönlichkeiten_' -'Persian_' -'Pec' -'Pazifik' -'Passag' -'Partition' -'Part_' -'Part' -'Parlamente_' -'Parking_' -'Palästinensern_' -'Paketen_' -'Paa' -'PNR_' -'PCs_' -'PA_' -'PAR' -'Otto' -'Osteuropa_' -'Ostasien_' -'Oscar_' -'Ort' -'Oro' -'Orange_' -'Oppositions' -'Operationen_' -'Olympischen_' -'Olympi' -'Office' -'Ocean_' -'Obs' -'Obl' -'Oberflächen' -'OSZE_' -'OM' -'Nähr' -'Nous_' -'Nixon_' -'Nicaragua_' -'Nic' -'Neus' -'Netz' -'Netanyahu_' -'Nes' -'Nenn' -'Navigation_' -'Nau' -'Natural_' -'Nationalen_' -'Namens' -'Nahrung_' -'Nag' -'Nad' -'Nachweis_' -'Nachk' -'NU' -'NPT_' -'NN_' -'NL_' -'NL' -'NET_' -'NAFTA_' -'Mühl' -'Mächte' -'Must' -'Motors' -'Motor_' -'Moro' -'Morgan_' -'Morg' -'Monti_' -'Mont_' -'Mone' -'Monday_' -'Moderni' -'Mittelmeerraum_' -'Mittela' -'Mitgliedsländern_' -'Mis' -'Minister' -'Mind' -'Migu' -'Mexican_' -'Meth' -'Mercosur_' -'Menschenrechten_' -'Meldung' -'Mehrheit' -'Meeting_' -'Medikament' -'Mayo' -'Maximum_' -'Materi' -'Masse_' -'Maschine_' -'Marktk' -'Marken_' -'Marine' -'Marin' -'Mandrake' -'Mandela_' -'Mandel' -'Mand' -'Manchester_' -'Main' -'Maes' -'MU' -'MT' -'MIT_' -'MIL' -'Lyon_' -'Lun' -'Luftverkehr_' -'Los' -'Londoner_' -'Liv' -'Little_' -'Lithuania_' -'Liquiditäts' -'Linz_' -'Linken_' -'Line' -'Limit_' -'Lig' -'Licht' -'Libert' -'Liberia_' -'Liberal_' -'Level_' -'Lev' -'Les' -'Leiter_' -'Leib' -'Legislat' -'Legi' -'Lebensmittelsicherheit_' -'Lebensmitteln_' -'Leb' -'Lay' -'Lauf_' -'Large_' -'Lanzarote_' -'Lane_' -'Landschaft_' -'Lad' -'Labora' -'Labor' -'LL_' -'LCD_' -'LAN_' -'Kurs' -'Kura' -'Kuch' -'Kreis_' -'Kredit_' -'Kosm' -'Kopf' -'Kooperations' -'Konzert' -'Konzentration_' -'Kontakte_' -'Konsultation_' -'Konsolidierung' -'Konse' -'Kongress' -'Konflikt' -'Konfiguration' -'Kompl' -'Kommen' -'Kommando_' -'Kommando' -'Komit' -'Kohä' -'Kofi_' -'Koch' -'Kob' -'Knopf' -'Klä' -'Klick' -'Klarheit_' -'Klang' -'Kirchen' -'Kirch' -'Ki_' -'Khamenei_' -'Kernkraft' -'Kennedy_' -'Kay' -'Kasse' -'Kartell' -'Karibik_' -'Kapitel_' -'Kapitalm' -'Kapazität_' -'Kanten_' -'Kampa' -'Kaiser_' -'KT_' -'KT' -'KP' -'KOM_' -'Jungen_' -'Jugoslawien_' -'Jugendliche_' -'Jugend_' -'Journal_' -'José_' -'Joint_' -'Joe' -'Jia' -'Jeff' -'Jedoch_' -'Jede' -'Jardin_' -'Jane' -'Jahrzehnts_' -'Jahr' -'Isle' -'Islamist' -'Investmentbank' -'Investitionsbank_' -'Interpret' -'Internetseite' -'Interinstitution' -'Integrationsp' -'Instrument' -'Instituts_' -'Institution' -'Insi' -'Innovat' -'Innenhof_' -'Innen_' -'Ingenieure_' -'Informationsschalter_' -'Infolgedessen_' -'Indians_' -'Index' -'Independen' -'Impulse_' -'Importe' -'Immigration_' -'Ig' -'Ideal_' -'Ib' -'IST' -'ION' -'IDE' -'ICC_' -'Händler_' -'Hyde_' -'Hung' -'Hotelsafe_' -'Horn' -'Horde_' -'Hochschule' -'Hochschul' -'History_' -'Hintergr' -'Him' -'Heu' -'Herkunft_' -'Herausgeber_' -'Heim' -'Heat' -'Haushaltsp' -'Haushaltsl' -'Hat' -'Harbor_' -'Handt' -'Handlungen_' -'Handelspartner_' -'Handelsb' -'Handb' -'Halle_' -'Halle' -'Halbinsel_' -'Haiti_' -'Hag' -'Hafen' -'Hab' -'HN' -'HE' -'Gy' -'Gul_' -'Grünen_' -'Gründer_' -'Grundst' -'Grill' -'Graz' -'Gras' -'Gran' -'Grafik' -'Governor_' -'Gouverneur' -'Go_' -'Gleichw' -'Gleichheit_' -'Gleiche' -'Gift_' -'Gewissen_' -'Gett' -'Get' -'Gesundheitsp' -'Gesprächen_' -'Gespräch' -'Gesetzgeb' -'Geschäftsle' -'Geschäftsb' -'Geschäfts_' -'Gerhard_' -'Gent' -'Geni' -'Geneva_' -'Genehmigung_' -'Gene_' -'Gemüse' -'Gemeinschaftsm' -'Gemeinde_' -'Geltung' -'Geis' -'Geheimdienst' -'Gegenzug_' -'Gefühle_' -'Gefä' -'Gefangenen_' -'Geduld_' -'Gebäuden_' -'Gebäude' -'Geburt_' -'Gazastreifen_' -'Garten' -'Garni' -'Gare_' -'GNOME_' -'GM_' -'GEN' -'GC' -'GAP_' -'G8_' -'Führungskräfte' -'Födera' -'Future_' -'Futtermittel' -'Fusion' -'Furcht_' -'Funktion' -'Fuku' -'Fuerte' -'Frühling_' -'Frühjahr_' -'Friedrich' -'Friedman_' -'Friedensnobelpreis' -'Fremden' -'Freie_' -'Fran' -'Frageb' -'Fracht' -'Former_' -'Forge' -'Foot' -'Fon' -'Following_' -'Flüchtlingen_' -'Flächen_' -'Fläche' -'Flugzeug_' -'Fluggäste_' -'Flugg' -'Flotte' -'Florenz_' -'Fli' -'Fisher' -'Fine_' -'Finanzr' -'Finanzinstitute_' -'Finanzielle_' -'Finanz_' -'Files_' -'Fift' -'Few_' -'Fests' -'Festplatten_' -'Festland' -'Ferien_' -'Fels' -'Felder' -'Feind' -'Fei' -'Fea' -'Fav' -'Fasc' -'Fantas' -'Fall' -'Fahrzeug_' -'Fachw' -'FU' -'FPGA_' -'FP' -'FOR_' -'FF_' -'Extremisten_' -'External_' -'Ex_' -'Evo' -'Ev' -'Euros' -'Euch_' -'Eti' -'Etage_' -'Erwerb_' -'Erw' -'Ersten_' -'Ersatz_' -'Ero' -'Erneuerung_' -'Eri' -'Erforder' -'Erdoğan_' -'Erdbeben' -'Erbe' -'Entwicklungsb' -'Entschädigung' -'Entschuldigung' -'Entschließungen_' -'Enhance' -'Engl' -'Energieb' -'Energiea' -'Endes_' -'Employment_' -'Empfang_' -'Electric' -'Eisb' -'Eis' -'Einzig' -'Einver' -'Eintritt' -'Einsparungen_' -'Einschränkungen_' -'Eins' -'Einkaufs' -'Einheiten_' -'Eingreif' -'Einfa' -'Eigentumsrechte' -'Eigenkapital_' -'Eiffel_' -'Eif' -'Ef' -'Economi' -'Ecke_' -'Ec' -'Dänemark_' -'Dut' -'Durban_' -'Drug' -'Drohungen_' -'Dritt' -'Dringlichkeit_' -'Dresden_' -'Drama' -'Download' -'Double_' -'Doll' -'Dokument' -'Document' -'Division_' -'Dist' -'Diskurs' -'Disco' -'Direktinvestitionen_' -'Diplom' -'Dies' -'Dialog' -'Dha' -'Devisen' -'Denkens_' -'Deng' -'Demokratischen_' -'Deli' -'Delhi_' -'Deg' -'Defizite_' -'Decision_' -'Datum_' -'Datenschutz' -'Dat' -'Danke_' -'Dan_' -'Dali' -'DT' -'DNA_' -'Cz' -'Currently_' -'Curren' -'Cubase_' -'Cs_' -'Crown' -'Cross_' -'Crisis_' -'Criminal_' -'Cove' -'Cost' -'Corporate_' -'Corn' -'Cori' -'Copyright_' -'Convenient' -'Contra' -'Continu' -'Connect' -'Competiti' -'Columbia_' -'Color_' -'Colla' -'Cocktail' -'Client' -'Clear' -'Claudi' -'Clar' -'Civi' -'Choose_' -'Chemie' -'Chef' -'Check' -'Charakter' -'Channel_' -'Chame' -'Certain' -'Catholic_' -'Cathedral_' -'Castel' -'Cash_' -'Case_' -'Casa_' -'Casa' -'Carolyn_' -'Carne' -'Cara' -'Capital_' -'Cance' -'Cala' -'Cafés_' -'CS_' -'CSS_' -'COM' -'COD_' -'CNS_' -'CN' -'CHI' -'CAS' -'Burk' -'Bundesregierung_' -'Bui' -'Buche' -'Brutto' -'Brun' -'Bruch' -'Brothers_' -'Brot' -'Broadway_' -'Bring' -'Brid' -'Brea' -'Brazilian_' -'Bou' -'Boris_' -'Bombe' -'Bolivien_' -'Blume' -'Blizzard_' -'Blitz' -'Bisc' -'Bir' -'Biokraftstoffe' -'Bildungss' -'Bib' -'Bh' -'Bezug' -'Bezirk' -'Bevölkerungen_' -'Betriebe' -'Betre' -'Betrag_' -'Bestrebungen_' -'Beste_' -'Besonders_' -'Beseitigung_' -'Beschränkungen_' -'Bergen_' -'Berechtigung' -'Berater_' -'Berat' -'Benzin' -'Benutzer' -'Benutz' -'Bemühen_' -'Belle' -'Bell' -'Beleidigung' -'Beitrittsverhandlungen_' -'Behinderte' -'Behauptung_' -'Begriffe_' -'Begriff' -'Begleiter' -'Begin' -'Befehl' -'Bedauerlicherweise_' -'Bed_' -'Beam' -'Bavaria_' -'Baust' -'Battle' -'Basi' -'Bashir_' -'Bart' -'Barrier' -'Barnier_' -'Barcode_' -'Barcelon' -'Barc' -'Barbara_' -'Banglades' -'Bang' -'Ban_' -'Balkan' -'Baker' -'Bahnh' -'BT' -'BES' -'BA_' -'Außenministeri' -'Autorit' -'Autonom' -'Ausw' -'Ausse' -'Ausmaße' -'Auslös' -'August' -'Augenblick_' -'Auftritt' -'Auftrags' -'Aufsichtsrat' -'Aufsichts' -'Aufschwung_' -'Aufruf' -'Aufpreis_' -'Auflösung_' -'Audio' -'Ath' -'Astrium_' -'Asp' -'Argumentation_' -'Arbeitsweise_' -'Arbeitsk' -'Arbeitsbedingungen_' -'Arbeits_' -'App' -'Anwe' -'Antonio_' -'Anton' -'Anth' -'Anreisedatum_' -'Ano' -'Anhang_' -'Angriffs' -'Angestellte' -'Angehörige' -'Ang' -'Andrew_' -'Andreas' -'Anb' -'Alten' -'Alt_' -'Alpha_' -'Alltag_' -'Allgemein' -'Allen_' -'Alco' -'Alan_' -'Akteur_' -'Akku' -'Aix_' -'Ahn' -'Ahmadinejad_' -'Agr' -'Again_' -'Afrikan' -'Affi' -'Aff' -'Adv' -'Admiral_' -'Adi' -'Add_' -'Add' -'Activ' -'Achse_' -'Academy_' -'Abstand_' -'Abend' -'Abbildung' -'ATT' -'ASPs_' -'API' -'AN_' -'AKP_' -'A4' -'=' -'94' -'91_' -'89' -'86' -'77' -'75' -'74_' -'73' -'61' -'58' -'55' -'500' -'48' -'400' -'34' -'225' -'20th_' -'1976_' -'1970s_' -'197' -'1962_' -'1933_' -'1929_' -'1914_' -'18' -'169' -'168' -'163' -'154' -'152' -'111_' -'108' -'0s_' -'05' -'006' -'.. _' -'.-_' -', [_' -'*' -'), "_' -'))_' -') , _' -'''' ' -'&#_' -'"-_' -'")' -' :' -' // _' -' ...' -' ,' -' *' -' (* _' -' ''[[_' -' #' -' _' -'„' -'“ ' -'— _' -'ь_' -'ше' -'ци' -'х' -'ф' -'тт' -'сти' -'от' -'он' -'ол' -'о_' -'за' -'же' -'ді' -'ды' -'ан' -'С' -'И' -'τ' -'ša' -'Š' -'ław_' -'če' -'ć_' -'ütz' -'üt' -'üstung' -'üste_' -'üste' -'ürzung' -'ürzt' -'ünst' -'ünder' -'ünde' -'üllen_' -'ührt_' -'ühre' -'ügliche' -'üchte_' -'ücht' -'übl' -'überwunden_' -'überwiegen' -'übertrieben_' -'übersteig' -'überraschend_' -'übern' -'übermäßig_' -'übermittelt_' -'überlegt_' -'übergreifende' -'überga' -'überein_' -'üben_' -'ún_' -'úl' -'ösung_' -'östlich' -'österreichische' -'öpfen_' -'öne' -'öhne' -'öhn' -'ögen' -'öfe_' -'öder' -'ño_' -'ère_' -'çais' -'äuter' -'äumt_' -'ätzlich' -'äte_' -'äte' -'ässer' -'äse' -'ärts' -'ärs' -'ärme' -'äni' -'ängen' -'ändigkeit_' -'ändig_' -'änderungen_' -'änderung_' -'äme' -'älteste' -'ältere_' -'äle' -'ähnlicher_' -'ähnel' -'ähigkeit_' -'ägyptische_' -'äglich' -'ägige' -'ât' -'ßte' -'ßnahmen' -'Überw' -'Übersetzungen_' -'Überschw' -'Überraschung' -'Überprüf' -'Überlegen' -'Überein' -'Üb' -'Ölpreise_' -'Ökosystem' -'Ökonom' -'Öffentliche_' -'Öff' -'Ängste_' -'Än' -'Ähnlich' -'©' -'    ' -'}}) {{_' -'}}' -'}' -'zögern_' -'zzo_' -'zze' -'zy_' -'zwischenstaatlichen_' -'zwecke' -'zwangsläufig_' -'zuwei' -'zuversichtlich_' -'zuverlässig' -'zuteil_' -'zut' -'zuständige_' -'zuste' -'zusammensetz' -'zusammengebr' -'zurückzukehren_' -'zurückl' -'zurückgreifen_' -'zurückgeh' -'zurückgeg' -'zur' -'zuläss' -'zuk' -'zugestimmt_' -'zugefü' -'zt' -'zqu' -'zoom_' -'zna' -'zitier' -'zini' -'zin_' -'zig' -'zielle' -'ziell_' -'ziehungen_' -'ziehung' -'zeugen_' -'zeuge' -'zentrums_' -'zellen' -'zeitwei' -'zeitung' -'zeichnet_' -'zehnte' -'zed' -'zahlung_' -'zahler' -'zad' -'yw' -'yto' -'ypto' -'yl' -'ybo' -'xw' -'xt' -'xit' -'xin' -'xenophob' -'xe_' -'xamp' -'würdigkeit_' -'wünschte' -'wöhnlich' -'wäsche_' -'wärtig' -'wunderschöne_' -'wound' -'worsen_' -'wors' -'workforce_' -'wora' -'woody_' -'wollend' -'woll' -'wohlhabende' -'wle' -'wl' -'wishing_' -'wirtschafts' -'wirkungs' -'wirksamere' -'wirksamen_' -'wirksam' -'wing' -'willi' -'wil' -'wig' -'wiederherzustellen_' -'widerst' -'widening_' -'wick_' -'whit' -'westliche' -'weste' -'west' -'wertvoll' -'werb' -'wendet' -'wem' -'weiße' -'weitreichende' -'weitergehen_' -'weig' -'weifel' -'wehr' -'week' -'wedding' -'weakness_' -'weaken_' -'wea' -'wast' -'washing_' -'wary_' -'warnings_' -'wandel_' -'walke' -'wald_' -'wahrnehmen_' -'waffen' -'wad' -'wab' -'völker' -'vé' -'vä' -'vy_' -'vri' -'vou' -'vorübergehend_' -'vorzubereiten_' -'vorstellung' -'vorste' -'vorsitzende' -'vorschriften_' -'vorliegen_' -'vorkommen_' -'vork' -'vorige' -'vorhers' -'vorher' -'vord' -'vorbereitet' -'vorbereiten_' -'vorbei' -'vorange' -'vons' -'volu' -'voltage_' -'vollem_' -'volcan' -'vocational_' -'voc' -'visuali' -'visor' -'vision' -'visib' -'vine' -'vigor' -'vielerlei_' -'vici' -'vibrant_' -'veränderten_' -'verzögert' -'verzweifelt' -'verzichte' -'verweist_' -'verweigern_' -'verwalten_' -'veru' -'vertreter_' -'vertreib' -'vertraue' -'vertrags_' -'vertraglich' -'verteilung_' -'verstärkten_' -'verständ' -'verstoßen_' -'verstorben' -'versprochen_' -'versprechen' -'verspr' -'versicherung' -'verschärfen_' -'verschwunden_' -'verschwende' -'verschuld' -'verscho' -'verschmutz' -'verschi' -'verringerte' -'vernünftigen_' -'vernünftige_' -'vernünftig_' -'vermeid' -'vermehrt' -'verma' -'verm' -'verleite' -'verleg' -'verlaufen_' -'verla' -'verkörpert_' -'verkn' -'verhandeln_' -'vergrößern_' -'vergr' -'vergleichen_' -'vergeb' -'verfolgten_' -'verfasst' -'verfallen_' -'vereinen_' -'vereinbarung' -'vereinbart_' -'vereinbar' -'verei' -'verehrte' -'verda' -'verbot_' -'verbleibenden_' -'verbindlichen_' -'verarbeitet' -'verarbeiten' -'verantwortungs' -'veranstaltungen_' -'veranlassen_' -'veraltet' -'verabschiedeten_' -'venue' -'ventu' -'vehement' -'vea' -'variables_' -'valu' -'validity_' -'valid' -'vai' -'vague_' -'vage' -'vaccine' -'vaca' -'vac' -'uzz' -'uum' -'utze' -'utung' -'utuh' -'utu' -'utter_' -'utter' -'utt' -'utilis' -'utig' -'utan' -'ustan' -'ussion' -'usschuss_' -'uso' -'usly_' -'usher' -'ush' -'usan' -'urteile' -'urte' -'urt_' -'urso' -'urlaub_' -'uris' -'urie' -'ured_' -'uras_' -'uptc' -'upper_' -'uppe' -'upl' -'uph' -'unüber' -'unwind_' -'unvorher' -'unverzichtbar_' -'unvers' -'unvergessliche' -'unterziehen_' -'unterschiedlicher_' -'unterl' -'unterhält_' -'untere' -'unterbreite' -'unsicher' -'unsi' -'unse' -'unrec' -'unrealisti' -'unpopul' -'unp' -'unnötige' -'unke' -'units_' -'unit' -'unilateral_' -'uniform_' -'unification_' -'ungswe' -'ungsv' -'ungsl' -'ungsgr' -'ungsan' -'ungewöhnliche' -'ungene_' -'ungeb' -'unga' -'unfähig_' -'unfo' -'unerlässlich_' -'undin' -'undertakings_' -'undertake_' -'underpinn' -'undermined_' -'underline_' -'unda' -'unconventional_' -'uncom' -'unchanged_' -'unberührt' -'unanimity_' -'unal' -'unabhängiges_' -'umweltfreundlich' -'umsetzen_' -'umg' -'umfeld' -'umfassender_' -'umfassend_' -'umfangreicher' -'umfang_' -'umd' -'umben' -'ultur' -'ultimative' -'uls' -'ule_' -'uldung' -'ular' -'uing_' -'ugh_' -'ufhin_' -'ufer' -'ued_' -'uder' -'uda' -'uci' -'uation_' -'tü' -'töt' -'tödliche_' -'täusch' -'tändig' -'tzte_' -'typen_' -'two' -'twist' -'twelve_' -'twar' -'turn' -'turbulen' -'tums' -'tumor' -'tue' -'tuc' -'tual_' -'ttung' -'tto' -'ttler_' -'ttet_' -'tsu' -'träglich' -'trust' -'trump' -'trugen_' -'tropical_' -'triple' -'tries_' -'trial' -'trend' -'treibende' -'treib' -'trei' -'tre_' -'travelling_' -'traveller_' -'trav' -'traurige' -'trategie_' -'transported_' -'transporte' -'transport' -'transparenter' -'transpar' -'transnationale' -'translations_' -'translat' -'transform' -'transferring_' -'trans_' -'train' -'tragi' -'trafe' -'traditione' -'traders_' -'trademark_' -'traded_' -'tracking_' -'toute' -'tout_' -'tourists_' -'touristi' -'toughe' -'touching_' -'touched_' -'totalitäre' -'torture_' -'toren_' -'tooth' -'toleran' -'toc' -'tne' -'tle_' -'tland' -'tk' -'tious' -'tionier' -'tine_' -'timetable_' -'tigt_' -'tigkeit' -'tiert' -'tiefe' -'tiat' -'thrill' -'thre' -'thr' -'therm' -'therapeuti' -'theorie_' -'theme' -'theit_' -'theatre_' -'theatr' -'thal' -'texte' -'teure' -'teu' -'tete' -'tet' -'testi' -'ters' -'terrasse_' -'terp' -'tenure_' -'tentang_' -'tenen_' -'tender' -'tendenziell_' -'temptation_' -'temples_' -'tem_' -'tellt' -'tellen_' -'telefoni' -'tein' -'teilnehmenden_' -'teilgenommen_' -'teigerung' -'teg' -'teen' -'tee' -'technologische_' -'technike' -'tched_' -'tausche' -'tariff_' -'tante' -'tanta' -'tanding' -'talked_' -'talis' -'talent' -'tad' -'tackl' -'sämtliche' -'sze' -'systemische' -'synt' -'synchronisier' -'switching_' -'swer' -'swell' -'sw' -'svor' -'survive' -'surro' -'surrender' -'supranational_' -'supposedly_' -'supervisor' -'superf' -'sung' -'summari' -'suits_' -'sue' -'sudden_' -'sud' -'successive_' -'subversi' -'subscribe' -'submi' -'sua' -'stützt_' -'stür' -'stät' -'ständige_' -'ständige' -'styles_' -'studierte_' -'stud' -'strukturellen_' -'structured_' -'strom_' -'stroll_' -'stricte' -'stress' -'strengen_' -'strafe_' -'stoßen_' -'stom' -'stol' -'stoffen_' -'stock' -'stl' -'stitch' -'stirbt_' -'stilvolle' -'still' -'steuerung_' -'steuerlichen_' -'stetig' -'sters_' -'steril' -'stenz' -'stehe' -'statu' -'stattgefunden_' -'stattfindenden_' -'statistische' -'statistical_' -'stationen_' -'stat' -'start' -'starker_' -'stando' -'standardiz' -'stan' -'staltung_' -'stagnieren' -'stagnat' -'stagn' -'stabiler_' -'stabil_' -'staatlich' -'ssysteme' -'ssungs' -'sstsein' -'ssing_' -'ssige' -'ssan' -'ssa_' -'sreg' -'sre' -'sq' -'späteren_' -'spyware_' -'spur_' -'spur' -'sprü' -'sprache_' -'spr' -'sporting_' -'spontane' -'sponsor' -'spiral_' -'spin' -'sphäre' -'spen' -'spektakulär' -'speedi' -'speculat' -'spars' -'sozialistische' -'souveränen_' -'sorte' -'soph' -'sonstige_' -'solving_' -'solide' -'sole' -'sola' -'sogenannte_' -'soft' -'soeben_' -'socialist_' -'soc' -'sob' -'soared_' -'snowb' -'snow_' -'snack' -'smooth' -'sly_' -'slower_' -'slot' -'slos_' -'slippe' -'slides_' -'slic' -'sl' -'sky' -'skill_' -'skill' -'skand' -'ska_' -'sk_' -'siz' -'sive' -'siv' -'sitzung' -'sition' -'sinnlos' -'sinn' -'singles_' -'sine' -'simplifi' -'simple' -'signifikant' -'sightseeing_' -'sighted_' -'sien' -'sid_' -'sicht_' -'sicht' -'sichert_' -'shoulder_' -'shou' -'shortages_' -'short' -'shooting_' -'shoot' -'shocked_' -'shock' -'shim' -'shifted_' -'shi' -'shar' -'sew' -'severely_' -'sever' -'settle' -'seti' -'servant' -'serta_' -'sers_' -'seren' -'serbische' -'sequenc' -'seperate' -'senti' -'sensors_' -'sensor_' -'senk' -'selecting_' -'sele' -'selber_' -'seja' -'seize_' -'seekers_' -'securing_' -'secured_' -'sect' -'secretar' -'seating_' -'seasons_' -'scre' -'scra' -'score' -'schöner_' -'schätzungsweise_' -'schätzt_' -'schädliche' -'schwierige_' -'schwache' -'schw' -'schrä' -'schreckliche' -'schreck' -'schnell' -'schn' -'schmutz' -'scheitern_' -'scheduled_' -'schedule_' -'scharfe' -'schaftung_' -'schaftl' -'schaff' -'sceptic' -'scenes_' -'scenario_' -'scape' -'scann' -'scandal_' -'scan_' -'sber' -'sati' -'samt_' -'samples_' -'sam_' -'salaries_' -'saison' -'sahen_' -'sade' -'saddle' -'sacrific' -'sack' -'sach' -'röme' -'räumen_' -'räum' -'räsident' -'räfte_' -'räch' -'rá' -'rust_' -'runde_' -'rulers_' -'ruk' -'ruin' -'ruhiger_' -'ruhe' -'ructi' -'rtuni' -'rtu' -'rtain' -'rows_' -'row' -'route' -'rot_' -'rosy_' -'ropriate' -'romantische' -'rom_' -'rojekt' -'rohstoff' -'roduc' -'robuste' -'rmen_' -'rlich_' -'rlich' -'rland' -'rke_' -'rivers_' -'rival_' -'rival' -'rity_' -'rism' -'riskante' -'risch' -'ringt' -'rimier' -'ril' -'rike' -'rika' -'rift_' -'ries' -'riert_' -'rieren_' -'rien_' -'rider' -'richtig' -'richtete_' -'richt_' -'riches' -'rial' -'rhythm' -'rfen_' -'rfe' -'revised_' -'review' -'reuni' -'rette' -'retre' -'retired_' -'retire' -'reti' -'restriktive' -'restricti' -'restoring_' -'restaur' -'respon' -'resid' -'resc' -'repu' -'repräsentieren_' -'reproductive_' -'reprodu' -'repri' -'replacement_' -'replace' -'repetiti' -'repeal' -'repar' -'repai' -'renovierte_' -'renn' -'reng' -'renamed_' -'removal_' -'rement' -'remember' -'reme' -'rema' -'relying_' -'reluctant_' -'rejects_' -'rejecting_' -'reise_' -'reis' -'reint' -'reinsta' -'reinforced_' -'reicht' -'reicher_' -'regulieren' -'regulators_' -'regrettabl' -'registrieren_' -'regierung' -'regieren' -'refund' -'refuge_' -'refrain' -'refor' -'redirect' -'redefin' -'recycle' -'recreational_' -'recoveri' -'reconst' -'recognizes_' -'recognis' -'rechtmäßig' -'rechtl' -'rebalancing_' -'reassure_' -'realm_' -'realize_' -'realist' -'realis' -'readers_' -'reactor' -'rdi' -'rde' -'rce' -'rca' -'rben_' -'rbeiten_' -'rba' -'ray_' -'raw' -'rativ' -'rategie_' -'rate' -'rar_' -'ranking' -'rane' -'random_' -'rand_' -'rand' -'rance' -'ralis' -'ral' -'raft' -'radioa' -'radikalen_' -'radikale_' -'rade' -'rada' -'quota' -'quot_' -'quet_' -'quet' -'quem' -'quarters_' -'quantity_' -'qualitativ_' -'qualit' -'qualifizierten_' -'qualifiziert' -'quaint' -'pursu' -'puede' -'publik' -'publici' -'public' -'pub_' -'ptu' -'ptions_' -'psychiatris' -'pse' -'prüfung_' -'präsent' -'provin' -'prototype' -'protectionism_' -'prospe' -'proportiona' -'prompt' -'promote' -'prom' -'prolonged_' -'projection' -'prohibiti' -'programm' -'profitiert_' -'profitable_' -'profitability_' -'professionellen_' -'profess' -'prof' -'produzierende' -'produkti' -'produ' -'problematisch_' -'problematic_' -'privileg' -'prioriti' -'print' -'pries' -'pretend_' -'presu' -'prest' -'pressing_' -'pressed_' -'preside' -'present' -'prerequisite_' -'prer' -'preparatory_' -'preparations_' -'prematurely_' -'preliminary_' -'preiswerte' -'preferences_' -'predict' -'preci' -'prech' -'precarious_' -'praxis' -'praktizieren' -'praktikable' -'prakti' -'praise_' -'pragmatische' -'practic' -'pph' -'pped_' -'ppe_' -'pp_' -'powered_' -'pour' -'postpone' -'posted_' -'possess' -'positiv' -'portugiesische' -'portrait' -'portion_' -'porn' -'populistischen_' -'pok' -'pois' -'poet' -'plötzliche' -'plate_' -'plastic' -'plas' -'planes_' -'plain_' -'plag' -'placem' -'plac' -'pixels_' -'pir' -'pio' -'pilgrim' -'pid' -'pia' -'physicians_' -'philosophi' -'phenomena_' -'pf_' -'pez' -'peu' -'petani_' -'pet' -'pest' -'perver' -'pertan' -'personnes_' -'personality_' -'permissi' -'perlu_' -'perl' -'perish' -'perf' -'perba' -'pera' -'penge' -'penetration_' -'penduduk_' -'pendidikan_' -'pendi' -'penalties_' -'pembe' -'pei' -'peg' -'pec' -'pd' -'pav' -'patents_' -'pate' -'pat' -'paste' -'passt_' -'passion_' -'partic' -'parti' -'pari' -'parameter_' -'pand' -'pal_' -'pair' -'painter' -'paint_' -'pact' -'packen_' -'pac' -'oßen' -'oxida' -'oxi' -'ox' -'ovi' -'overt' -'overlooks_' -'overlooked_' -'overlook_' -'overflow_' -'overco' -'outweigh' -'outlook_' -'outermost_' -'otic_' -'oster' -'orsch' -'ors' -'orische' -'oris' -'orin' -'organize_' -'organisierte' -'orene' -'ordnungsp' -'ordnet_' -'orderly_' -'orde' -'oration_' -'opu' -'optionen_' -'optimiert_' -'oppos' -'opia' -'oph' -'opfer' -'operier' -'operativen_' -'oot_' -'ook_' -'onsk' -'onna' -'omy_' -'omo' -'ologischen_' -'ollst' -'olla' -'olis' -'olic' -'oler_' -'olat' -'oh_' -'offs_' -'offre' -'offizieller_' -'offenbar' -'odds_' -'oda' -'och' -'ocean_' -'oce' -'obsess' -'obs' -'oblem_' -'objecti' -'obersten_' -'oberst' -'oberh' -'obat' -'oad' -'nützliche' -'nötige_' -'nördlich' -'nö' -'nähere' -'nya' -'nw' -'nuts_' -'nut' -'ntwicklung_' -'ntl' -'ntif' -'nta_' -'nswert_' -'nste' -'nsp' -'nschl' -'nsche' -'nre' -'nqu' -'npr' -'nov' -'notable_' -'north' -'norms_' -'norm_' -'nommene' -'nomen' -'nod' -'nnen' -'nme' -'nly_' -'nle' -'nkt' -'nji' -'niños_' -'niv' -'nisch_' -'nießer' -'niert' -'nico' -'nick' -'ngun' -'ngt_' -'ngg' -'nges_' -'ngeb' -'nfalls_' -'newer_' -'nevertheless_' -'neutrali' -'neutral_' -'neuartige' -'netzwerk' -'nes' -'neoliberal' -'nennt_' -'neighborhood_' -'nehmbar_' -'negotiators_' -'near' -'ndt' -'ndre' -'ndlung' -'ndere' -'ncier' -'nchi' -'navigati' -'navigate_' -'nav' -'nationals_' -'nationalists_' -'nata' -'nast' -'nas_' -'narrative' -'nament' -'nam' -'nachweis' -'nachteilig' -'nachkommen_' -'nachgewiesen' -'nachdenken_' -'nable_' -'mündliche_' -'mäßigkeit_' -'mäler' -'mysti' -'mutual' -'musik' -'musical_' -'multipl' -'multilaterale' -'multic' -'mption_' -'mpfe' -'moti' -'monst' -'monet' -'momentum_' -'module_' -'modu' -'modernem_' -'modell' -'mobiliz' -'mmungen_' -'mml' -'mmer' -'mmen_' -'mmel' -'mliche' -'mle' -'mittelfristig' -'mitigat' -'mitget' -'mission' -'missed_' -'misg' -'mische' -'mins' -'ministry_' -'minimize_' -'mines_' -'miner' -'mik' -'mig' -'mia_' -'mia' -'mg' -'mers_' -'mern_' -'merkwürdig' -'mercury_' -'merchan' -'merat' -'mera' -'meny' -'menti' -'menschlicher_' -'mengha' -'meng' -'memu' -'memo' -'memi' -'meldungen_' -'meister_' -'meist' -'meint' -'mehrmals_' -'meets_' -'medication' -'mechanisch' -'mean' -'mbl' -'maß_' -'matik' -'materiellen_' -'mate_' -'masih_' -'masalah_' -'marriage_' -'marri' -'marktor' -'markieren_' -'marke' -'marit' -'marina_' -'mare' -'manufactured_' -'manual_' -'mankind_' -'manipulati' -'manipulated_' -'manifest_' -'manden_' -'mammals_' -'mainst' -'magneti' -'machine' -'löschen_' -'läufe' -'längerfristige' -'längere_' -'lz_' -'lys' -'luxu' -'lun' -'luggage_' -'luft' -'ludi' -'lud' -'ltig' -'lth' -'lte' -'lovers_' -'lof' -'locken' -'lock' -'locally_' -'loca' -'loads_' -'llung' -'llin' -'llige' -'llia' -'lizi' -'litera' -'lisiert_' -'lion_' -'liner_' -'lineare' -'linder' -'lige' -'lifestyle_' -'lif' -'lieferte' -'lieben_' -'lichste_' -'licence_' -'libysche' -'liberty_' -'liaison_' -'lh' -'lg' -'letzte' -'letz' -'letters_' -'lessly_' -'lernt' -'lerat' -'lep' -'leistungsfähig' -'leistet_' -'leiden' -'leichte_' -'lehnt' -'legitim' -'legislator' -'lebend' -'lear' -'league_' -'lder_' -'lde_' -'laying_' -'laya' -'laus' -'launch' -'lauf' -'lation_' -'las' -'landm' -'landing_' -'lain' -'laime' -'labeled_' -'label' -'künft' -'köstliche' -'könig' -'ky' -'kwa' -'kungen_' -'kultur' -'ktr' -'ktivitäten_' -'ktionen_' -'kter' -'kst' -'ksch' -'kreativ' -'krati' -'krat' -'krank' -'kra' -'kostspielige' -'kostenloses_' -'kostenlose' -'kostengünstig' -'kostenfreie' -'korrekte_' -'koordinierte' -'koordinieren_' -'konzer' -'konvertier' -'konvention' -'konte' -'konstruktiv' -'konsequente_' -'konsequent_' -'kons' -'kono' -'konferenz_' -'komplizierter_' -'kompatib' -'kombinier' -'kollektive' -'kohlenstoffarme' -'kod' -'knüpf' -'know' -'klu' -'kleinste' -'klargestellt_' -'klare' -'kis' -'kirch' -'kir' -'kill' -'kidnapp' -'ketten_' -'kese' -'kepe' -'kepada' -'kens' -'kende' -'kemu' -'keln' -'kei' -'kea' -'kaya' -'kategori' -'kare' -'kapital_' -'kant' -'kanis' -'kand' -'kanadische' -'kamer_' -'kali_' -'kala' -'kah_' -'jut' -'junt' -'jud' -'jos' -'joga' -'jin' -'jeweilige_' -'jeti' -'jes' -'jeopardi' -'jenseits_' -'jenige_' -'jenem_' -'jat' -'jalan' -'jahre' -'jadi' -'jack_' -'ière' -'ivo' -'ivit' -'ius_' -'itäten_' -'ität' -'itting_' -'itter' -'itra' -'itor' -'itive' -'itier' -'itet_' -'ites_' -'italienische_' -'isung_' -'ister_' -'isten' -'ist' -'isoliert_' -'isk' -'isieren_' -'ished_' -'irrational' -'irgendeinem_' -'irgendeine' -'ipati' -'iones_' -'invoke' -'invites_' -'invited_' -'invitation_' -'investor_' -'investigat' -'intri' -'intra_' -'intl' -'intimate_' -'interpret_' -'interne' -'intermediar' -'interiors_' -'interinstitutionelle' -'interfere_' -'interessante' -'interessant_' -'interess' -'interaktive' -'inter_' -'intent_' -'intensivier' -'intelligente' -'insula' -'instrumente' -'instructi' -'institutionen_' -'institute_' -'instan' -'installations_' -'instabile' -'inspiration' -'insisting_' -'insisted_' -'insig' -'insert_' -'insect' -'inoffiziell' -'innovativen_' -'inneren_' -'innere_' -'inner' -'inklusive_' -'injured_' -'inige' -'inhuman' -'inherent_' -'inhaber_' -'inh' -'ings' -'ingredient' -'ingl' -'inger_' -'inga' -'infrastruktur' -'informellen_' -'influences_' -'infla' -'infer' -'ined' -'industrializ' -'indlich' -'individuell_' -'individually_' -'indis' -'indirectly_' -'indigenous_' -'indifferen' -'indicating_' -'indicat' -'inconsisten' -'incompeten' -'incom' -'inal' -'inakzeptabel_' -'importiert' -'impo' -'implement' -'impetus_' -'immunity_' -'imen_' -'imate' -'imaging_' -'ima_' -'ima' -'ilung' -'ilm' -'illustr' -'illiberal' -'illegale' -'illegal' -'ilität' -'ilita' -'ilie' -'ili_' -'ilen_' -'ileg' -'ilat' -'ilan' -'ikation' -'ige' -'iga' -'ifft_' -'iffen_' -'ifen_' -'ießen_' -'ieß_' -'ierungsa' -'iens_' -'ielt_' -'iele' -'iegs' -'iede' -'iebene' -'ieb_' -'idle' -'idier' -'ider_' -'ider' -'identit' -'identisch' -'identifiziert_' -'ideally_' -'idad_' -'icon' -'icken_' -'ick_' -'ichtigt' -'iche' -'ibut' -'ibu' -'iat' -'iali' -'höhe' -'höh' -'höchstens_' -'hö' -'hän' -'häl' -'hypocrisy_' -'hydrocarbon' -'hungry_' -'humo' -'humanitären_' -'hts' -'hrs' -'house' -'hostile_' -'hostage_' -'horse_' -'horn' -'hore_' -'hopeful' -'honor_' -'homosexual' -'hofft' -'hof' -'hoc_' -'hnen' -'hman' -'hkan_' -'historically_' -'hip' -'hing' -'hinein' -'hindern_' -'hinder_' -'hinausgeh' -'hilfsbereit_' -'hilfs' -'hilfen_' -'hila' -'highlight' -'hierarch' -'hful' -'heti' -'hetero' -'hervorr' -'hervor' -'hers' -'herrschaft_' -'herein_' -'herd' -'herbeizuführen_' -'herausstellen_' -'heran_' -'herab' -'hent' -'hens' -'hend' -'hemi' -'heme' -'heirate' -'hedge' -'hed' -'hebt_' -'healing_' -'hea' -'hazard' -'haushaltspolitische' -'haupt' -'haul_' -'hath_' -'hase_' -'hars' -'harmonisiert' -'harmonis' -'hari_' -'harbour_' -'har_' -'hang' -'handl' -'handicap' -'hag' -'hace' -'haber_' -'habe' -'güter_' -'gänge' -'gw' -'guy' -'gur' -'gungs' -'gsa' -'gründ' -'grundsätzliche' -'grundlegend' -'großzügige_' -'groundwater_' -'grossen_' -'grosse' -'griffen_' -'griff_' -'grenzübergreifende' -'grenz' -'green' -'greed' -'grave_' -'grat' -'grass_' -'graphic_' -'graph' -'gram_' -'grafische' -'graduate' -'grade' -'glä' -'glich' -'gleichberechtigt' -'glaubten_' -'glas_' -'gische_' -'girl' -'giga' -'gifts_' -'gian' -'ghan' -'ggen' -'geza' -'gewor' -'gewohn' -'gewissem_' -'gewinnt_' -'getä' -'getro' -'getre' -'getestet_' -'geta' -'gesunken_' -'gesund' -'gestützt_' -'gestellten_' -'geste' -'gespräche_' -'gesp' -'geschätzte' -'geschäfte_' -'geschä' -'geschenkt_' -'geschah_' -'gescha' -'gesamte' -'geräten_' -'gerisch' -'gerichtete_' -'gerech' -'gere' -'geplanten_' -'gepa' -'geordnet' -'geographische' -'gent_' -'genocide_' -'genk' -'genia' -'generieren_' -'generator' -'genehmigt' -'gene_' -'gende_' -'genaue_' -'gemütliche' -'gemeldet' -'gemeinsames_' -'gemachte' -'geltend_' -'gelenk' -'geleitete' -'gelegene_' -'geleg' -'gelb' -'gekü' -'gekämpft_' -'geiz' -'gei' -'geho' -'geheim' -'gefällt_' -'gefährlich' -'gefä' -'gefasst_' -'geeignet' -'geehrte_' -'gedie' -'gede' -'ged' -'gebraucht_' -'gebot' -'gebor' -'gebildete' -'gebene' -'gb' -'gau_' -'garr' -'garette' -'garantierte' -'gant' -'gang' -'gaming_' -'galt_' -'galax' -'gag' -'fürchten_' -'fünfte' -'fühlte' -'fähige' -'fähig' -'furthermore_' -'furt' -'furnishings_' -'funktionierende' -'funktionen_' -'fund' -'fun' -'fulfilling_' -'fuer' -'ftl' -'frustrated_' -'fru' -'frischen_' -'friend' -'friedlich_' -'freundschaft' -'freundlich' -'fres' -'freight_' -'freies_' -'französischer_' -'frankly_' -'fotografi' -'foster' -'fortgeführt_' -'formulier' -'formulate' -'formul' -'formel' -'foreg' -'forecasts_' -'foo' -'folk' -'foc' -'fläch' -'fluss_' -'flourish_' -'flour' -'flotte' -'flos' -'florier' -'flora_' -'flop' -'flood' -'fliegen_' -'fleisch' -'flas' -'fishermen_' -'fischen_' -'fis' -'firma' -'firewall' -'fires_' -'finest_' -'find' -'financially_' -'filter_' -'filter' -'fika' -'fighters_' -'fifteen_' -'fierce' -'fielen_' -'ffnung' -'ffende' -'ffel' -'ffe_' -'festlegen_' -'festivals_' -'festi' -'festges' -'festgehalten_' -'fes' -'fertigte' -'fertige' -'fere' -'fens' -'feine' -'feindlichen_' -'fehlge' -'fed' -'fast' -'fassung' -'fass' -'fashion' -'fascis' -'fare' -'farb' -'faktisch' -'fairer_' -'fad' -'factories_' -'ezieh' -'ez' -'exzellente' -'extremist' -'extraordinarily_' -'extracti' -'extern' -'extends_' -'exposed_' -'exporter_' -'exploring_' -'explains_' -'expire' -'experiments_' -'expedi' -'expectation_' -'existent' -'execute_' -'exe_' -'excite' -'excessively_' -'excel' -'examined_' -'evil_' -'eventual_' -'eve_' -'evade' -'europaweite' -'europaweit_' -'europa' -'europ' -'eur' -'etzungs' -'ette' -'ett_' -'etr' -'etisch' -'eties_' -'etie' -'etho' -'ethnische_' -'ethischen_' -'etan' -'estimat' -'ester_' -'espo' -'esh' -'esco' -'escalati' -'erörtert_' -'erörtern_' -'eröffnete' -'erzähl' -'erzeugte' -'erwähnte_' -'erwies_' -'erweiterte_' -'erweise_' -'erwartete' -'erw' -'erve' -'erto' -'ertig' -'erstes_' -'erstaunliche' -'erstaun' -'erstattung' -'erspar' -'erschien_' -'erschaffen_' -'eros' -'erobe' -'ernsthaften_' -'erni' -'erneute_' -'erneuerbaren_' -'erneuerbare_' -'ermu' -'erledigen_' -'erlan' -'erj' -'erinner' -'erier' -'erhöhungen_' -'erhobene' -'erhalte' -'ergänzende' -'ergen' -'ergebnisse' -'ergeb' -'erfreu' -'erfolgreicher_' -'erfahrene' -'erfahr' -'ereigne' -'erect' -'erbr' -'eras' -'erarbeitete' -'equal' -'episodes_' -'env' -'entspannten_' -'entsende' -'entschuldig' -'entries_' -'entrant' -'entrance_' -'entm' -'entlich_' -'entities_' -'entirety_' -'enthusiasm_' -'entha' -'entgehen_' -'entgegen' -'enswerte_' -'ension' -'ensh' -'enriche' -'enp' -'enorm' -'enl' -'englis' -'engineers_' -'engaging_' -'engagieren_' -'enforced_' -'energi_' -'energi' -'enduring_' -'endung' -'endorsed_' -'endi' -'endete' -'endemi' -'ency' -'encryption_' -'encouragement_' -'encountered_' -'encompassing_' -'encompass' -'ena_' -'empower' -'empl' -'empiri' -'empha' -'empfind' -'empfiehlt_' -'empfehle' -'empe' -'emotional' -'emer' -'embryon' -'embass' -'embarrass' -'eman' -'eln' -'ellungs' -'ellte' -'elle' -'elin' -'elig' -'elevated_' -'elevat' -'elemente' -'elem' -'eleganten_' -'elegan' -'elan' -'ektiv' -'ektion' -'eks' -'eke' -'eitige' -'eitet_' -'eisung' -'eisen' -'eis_' -'einzutreten_' -'einzuh' -'einzugehen_' -'einzub' -'einzigartige_' -'einzigartig' -'einz' -'einw' -'eintritt_' -'einstimmig_' -'einsti' -'einstellungen_' -'einste' -'einsa' -'einrichten_' -'einkommens' -'einhergehen' -'einher' -'einheimischen_' -'eingest' -'eingesp' -'eingeschl' -'eingeräumt_' -'eingeleitet_' -'eingeladen_' -'eingehe' -'einfl' -'eindruck' -'eindeutige' -'einander' -'eih' -'eigen_' -'eigen' -'eift' -'eien_' -'eichnet_' -'ehrung' -'ehrgeizige_' -'ehrgeizige' -'ehn' -'ehmen_' -'ehens' -'ehe_' -'egg_' -'efor' -'eff' -'ef_' -'editing_' -'edit_' -'eding' -'eder' -'ection_' -'eci' -'echse' -'ebenen_' -'dämm' -'dynamische' -'dyna' -'dwindl' -'dust_' -'durchschnitt' -'dumping_' -'dumm' -'dum' -'dua_' -'drohende' -'droh' -'dritter_' -'dring' -'dres' -'dramatische_' -'dox' -'downs_' -'downloading_' -'downloade' -'dose_' -'dore' -'doppelt_' -'doppel' -'dong' -'dominiert_' -'dominated_' -'domains_' -'dom' -'dogs_' -'dog' -'documentation_' -'dn' -'dk' -'diving_' -'diversi' -'divergen' -'disziplin' -'distributions_' -'distract' -'distinct_' -'disso' -'disre' -'dispo' -'displa' -'dispe' -'dispa' -'diskriminierung' -'discriminat' -'discovering_' -'discount_' -'disclose_' -'disciplin' -'disappointing_' -'disagreement' -'disadvantage' -'dire_' -'diplomatische' -'dip_' -'dioxid' -'dio' -'diminish' -'dimensions_' -'dili' -'dilemma_' -'dikti' -'digt_' -'dif' -'diet_' -'dies' -'dienste' -'dick' -'dichte' -'diagnosti' -'dez' -'devo' -'devis' -'developer' -'devaluation' -'deutscher_' -'deutsch' -'deut' -'determining_' -'determin' -'deter_' -'deter' -'detection_' -'detai' -'destinations_' -'designated_' -'deserv' -'ders' -'derogations_' -'derartig' -'depression_' -'depreciation_' -'depreciat' -'depre' -'depos' -'dependency_' -'depan_' -'denomination' -'dende' -'demonstration_' -'demonstr' -'delte' -'deliberate' -'delegati' -'delays_' -'deko' -'dek' -'deinem_' -'deflation' -'definitive' -'definitions_' -'definierte' -'definieren_' -'defer' -'defensive_' -'defa' -'deeper_' -'deepen' -'decreas' -'decoupling_' -'deciding_' -'decides_' -'debattiert' -'deal' -'dauernd' -'date' -'dargelegt' -'dankbar_' -'dana_' -'damaligen_' -'damalige_' -'damaging_' -'dahin' -'cynic' -'customize' -'custom_' -'curr' -'cure' -'cula' -'ctable_' -'crown' -'crossing' -'cron' -'criticise' -'criterion_' -'criminali' -'cove' -'coupon' -'coun' -'couldn_' -'cott' -'corresponds_' -'correlation' -'corrections_' -'correction_' -'correct' -'corr' -'corpora' -'cornerstone' -'corn' -'cooperative_' -'cooked_' -'coo' -'convers' -'contribu' -'contradiction' -'contra' -'continuously_' -'continuous_' -'continents_' -'contest_' -'contemp' -'cont' -'consume' -'consultations_' -'constructi' -'constitution' -'conso' -'consent_' -'connectivity_' -'conjunction_' -'congress_' -'congratulations_' -'confort_' -'conform' -'conflict' -'configured_' -'confess' -'conductors_' -'conditione' -'concessions_' -'concert_' -'concert' -'composit' -'composer' -'complied_' -'complementary_' -'complement_' -'comple' -'complacen' -'compensat' -'compelling_' -'compe' -'comparable_' -'communa' -'commu' -'commend' -'comf' -'comb' -'colorful_' -'color' -'colony_' -'collecti' -'collect' -'collapsed_' -'coi' -'coe' -'cob' -'coastline' -'coach_' -'cluster_' -'clus' -'cloth' -'clos' -'climb_' -'clearer_' -'clean' -'clamp' -'cking_' -'cited_' -'cisi' -'circles_' -'circ' -'cipa' -'cion_' -'cil_' -'cic' -'chw' -'chuld' -'chu' -'chtung' -'chtigt' -'chter_' -'chtbar' -'chste' -'chst' -'chor' -'choi' -'chk' -'chips_' -'ching' -'chi_' -'chei' -'cheat' -'chat_' -'chat' -'chase' -'charit' -'charakteristisch' -'chapter' -'challenge' -'chairs_' -'cerca' -'centrali' -'center' -'cens' -'celebration_' -'cease' -'cci' -'categori' -'cated_' -'cate_' -'catalyst_' -'carri' -'card' -'capa' -'cancell' -'camps_' -'cals_' -'call' -'calculation' -'calculated_' -'caf' -'cabl' -'bürge' -'busi' -'bureaucratic_' -'bureaucra' -'burdens_' -'bundle' -'bukan' -'bud' -'buck' -'buchs' -'brücke' -'bräu' -'broaden' -'broad' -'bridge' -'brid' -'breites_' -'breakthrough_' -'breaking_' -'breach_' -'brat' -'brand' -'branche' -'brai' -'brac' -'boutique_' -'bout' -'borrow_' -'bone_' -'bond' -'bombs_' -'bold' -'boh' -'board' -'blä' -'blutigen_' -'blutig' -'blue' -'bloods' -'blo_' -'blier' -'bliebe' -'blicken' -'blick' -'blen_' -'bkommen_' -'bj' -'bits_' -'biss' -'bish' -'bip' -'biologischen_' -'biologis' -'biolog' -'bination' -'billigen_' -'bilie' -'bile' -'bilaterale' -'bila' -'bicycle' -'bezogene' -'beziehungsweise_' -'bezeichnete' -'bewä' -'bewussten_' -'bewirkt_' -'bewerten_' -'beweg' -'bew' -'bevorstehende' -'bev' -'beurteilen_' -'beträchtliche_' -'beträ' -'betrug_' -'betreffend_' -'betray' -'besuchten_' -'bestätig' -'bestraft_' -'bestmögliche' -'bestimmungen_' -'bester_' -'bestehe' -'besseres_' -'besonderem_' -'besitz_' -'besiege' -'besichtig' -'beseitig' -'bese' -'beschädigt_' -'beschränkung' -'beschränkte' -'beschl' -'berühr' -'berühmteste' -'berüc' -'beruh' -'berufliche_' -'berufliche' -'bern_' -'berkembang_' -'berk' -'berichterstatt' -'berichten_' -'bericht' -'bereitzustellen_' -'bereite' -'berda' -'berb' -'bequeme' -'beobacht' -'bent' -'bemerkenswerte_' -'beme' -'bem' -'belonging' -'believ' -'beliebige_' -'belgischen_' -'beleb' -'belastet_' -'belast' -'bekämpft' -'bekräftigt_' -'bekräftig' -'bekanntlich_' -'beiträgt_' -'beitreten_' -'beiten_' -'beit_' -'beispiellose' -'beispielhaft' -'bein' -'behält_' -'behindertenfreundliche_' -'behi' -'beherrsch' -'beher' -'behandlung' -'begrenzte_' -'begreifen_' -'begeistert' -'befolgt_' -'befand' -'beer_' -'beeindruckende' -'beein' -'bedien' -'bedenke' -'bedenk' -'beautifully_' -'beating' -'beat_' -'beamte' -'beam_' -'beam' -'beachtet_' -'beabsichtigt_' -'bd' -'baute' -'baue' -'basket' -'basierende' -'barometer' -'bares_' -'barem_' -'bao_' -'bankruptcy_' -'banken_' -'bands_' -'ballot_' -'balances_' -'baik_' -'backward_' -'backd' -'awful_' -'awaken' -'await_' -'aven' -'avant' -'autoritären_' -'autor' -'automated_' -'autob' -'authorize' -'authentic' -'ausüben_' -'auszugleichen_' -'auszud' -'auszubauen_' -'ausspr' -'ausse' -'ausschließ' -'ausmachen_' -'auslös' -'ausgezeichnet_' -'ausgez' -'ausgewählten_' -'ausgewogenen_' -'ausgestellt' -'ausgeprägte' -'ausgelegt_' -'ausgehende' -'ausgeh' -'ausgebildete' -'ausg' -'ausführlich_' -'ausf' -'ausd' -'ausbe' -'augment' -'aufzuz' -'aufwa' -'auftragten_' -'aufstrebende' -'aufle' -'aufhören_' -'aufgestellt_' -'aufen_' -'aufeinander' -'aue' -'audiovisual_' -'auchen_' -'atur' -'attraktiv_' -'attitudes_' -'attent' -'attacked_' -'ato' -'atively_' -'ations' -'ational_' -'ation' -'atie' -'atemberaubende' -'asur' -'astronom' -'astonish' -'asti' -'aster_' -'assung_' -'assiste' -'assist' -'asses' -'ass' -'asil' -'ash_' -'asch' -'asc' -'aru' -'artner' -'articulate' -'arte_' -'arrives_' -'arri' -'arkt_' -'ark_' -'aring_' -'ardo' -'architectural_' -'arbe' -'approximat' -'appreciation_' -'applicant_' -'appease' -'appear' -'appeal' -'appalling_' -'app_' -'aper' -'anzusehen_' -'anze' -'anza' -'antwort' -'antra' -'antly_' -'antin' -'antik' -'antibiotics_' -'antara_' -'answered_' -'anstr' -'anstelle_' -'ansta' -'anspruch' -'anspr' -'anor' -'announc' -'anniversary_' -'annex' -'ankurbel' -'ankung' -'animation' -'anima' -'ania' -'anhe' -'anhaltende_' -'anh' -'angu' -'angle_' -'angez' -'angewendet_' -'angewa' -'angeschlossen_' -'angeh' -'angeführt' -'angebotenen_' -'anfällig_' -'aneinander_' -'anderung' -'andern_' -'andards_' -'anbieter' -'anbet' -'analog' -'ams' -'amount' -'amerikanische' -'aly' -'aluminum_' -'altige' -'alti' -'alor' -'allocation_' -'allgemeiner' -'allge' -'aller' -'allen' -'alkohol' -'aliz' -'alische' -'algorithms_' -'ald_' -'alas' -'alarming' -'alarmier' -'aktuelle' -'akibat' -'akh' -'aket' -'aken_' -'ais_' -'ains' -'aining_' -'ahrt_' -'agu' -'ags' -'agree' -'agne' -'agn' -'aggressi' -'aggravat' -'agentur' -'afts' -'aften_' -'affiliate' -'affe' -'advoca' -'advisor' -'advisers_' -'advanc' -'adr' -'adore' -'ador' -'admi' -'adjusted_' -'additi' -'adding_' -'acu' -'activate_' -'acquire_' -'acion' -'acies_' -'achse' -'achievable_' -'achi' -'accumulate' -'accordingly_' -'accord' -'accomplish_' -'accompany_' -'accom' -'accidental' -'access' -'abzuwe' -'abz' -'abweichen' -'abus' -'abstract' -'absti' -'abschließend_' -'abra' -'abolish' -'ables_' -'ablehn' -'abit' -'abgeschnitten_' -'abger' -'abgeb' -'aber' -']]), ' -'].' -'Zö' -'Zweit' -'Zwangsv' -'Zuwanderer' -'Zusätzlich' -'Zurich_' -'Zun' -'Zuerst_' -'Zube' -'Zor' -'Zoom' -'Zoo_' -'Zionis' -'Zielsetzung' -'Ziels' -'Zentrali' -'Zel' -'Zehn' -'Yugoslav_' -'YouTube_' -'Yi' -'Yel' -'Yacht' -'Xinjiang_' -'Xin' -'Xe' -'Wörter_' -'Wört' -'Wählern_' -'Worse_' -'Working_' -'Worker' -'Wonder' -'Wollen_' -'Woll' -'Wochenende_' -'Wirtschaftsw' -'Wirtschaftse' -'Wirkung' -'Willkommen_' -'Williams_' -'Wild_' -'Wil' -'Wiederbelebung_' -'Whatever_' -'Wette_' -'Westjordanland_' -'Werke' -'Wen_' -'Weltmarkt' -'Weltkriegs_' -'Weltgesundheitsorganisation_' -'Wellen' -'Welle_' -'Weißbuch_' -'Weiterver' -'Wehr' -'Week' -'Wasserstoff' -'Wassers' -'Want_' -'Wales_' -'Wahrscheinlich' -'Waf' -'WWII_' -'WE_' -'Vulkan' -'Vr' -'Vorwand_' -'Vortr' -'Vorsicht_' -'Vormittag_' -'Vorm' -'Vorhan' -'Vorg' -'Vorb' -'Vollst' -'Voice_' -'Vita' -'Visa' -'Vila' -'Vig' -'Vietnam' -'Vie' -'Victor' -'Veto' -'Verwundbarkeit' -'Verwend' -'Verv' -'Verurteilung_' -'Vertriebs' -'Vertreibung' -'Vertiefung_' -'Versuchen_' -'Versu' -'Verstöße' -'Verstärkung_' -'Verständ' -'Verschärf' -'Verschuldung_' -'Verschmutzung_' -'Versch' -'Versagen_' -'Vermögenswerten_' -'Vermittlung_' -'Verlierern_' -'Verknüpfung' -'Verkehrsanbindung_' -'Veridian_' -'Verhältnisse_' -'Verhinderung_' -'Verhaftung_' -'Verfechter_' -'Verfassungsvertrag_' -'Verdienst' -'Verbrennung' -'Verbreche' -'Verbindungs' -'Verarbeitung' -'Verabschiedung_' -'Vend' -'Vat' -'Vas' -'Vall' -'Vali' -'Valent' -'VIP_' -'VII' -'VC_' -'Uruguay_' -'Ursprungs' -'Ursache_' -'Urb' -'Unzufriedenheit_' -'Unz' -'Unw' -'Unver' -'Untu' -'Unternehmenss' -'Untergang' -'Unterg' -'Unterb' -'Unless_' -'Ungleichgewichte_' -'Ungeachtet_' -'Unemployment_' -'Unein' -'Unabhängig' -'Umweltbe' -'Umweltausschuss_' -'Umwelta' -'Umsch' -'Ultra_' -'Ult' -'Ukrain' -'Uhr' -'Ufer_' -'Ud' -'UNI' -'UNHCR_' -'UND_' -'UA' -'Tür' -'Täuschung' -'Ty' -'Tube' -'Tschechische' -'Tsa' -'Trü' -'Trump' -'Truc' -'Trop' -'Trock' -'Trip' -'Trinkwasser' -'Trin' -'Tribu' -'Trial' -'Trennung_' -'Traum' -'Transparen' -'Tran' -'Tram' -'Trainings' -'Training_' -'Train_' -'Trail' -'Touristen' -'Toulouse_' -'Touch' -'Toten_' -'Tol' -'Titel' -'Titan' -'Tit' -'Tip' -'Tin' -'Tierschutz' -'Tiefe' -'Tie' -'Tibetan' -'Tia' -'Thyssen' -'Threa' -'Thr' -'Tho' -'Therma' -'Thema' -'Textes_' -'Termine_' -'Tennis_' -'Templates_' -'Telekommunikations' -'Techniken_' -'Tauche' -'Tastatur' -'Tank' -'Tang' -'Tamp' -'Tam' -'Tai' -'Tahrir_' -'Tabellen_' -'TTIP_' -'TS_' -'TRA' -'TG' -'TFT' -'Sünden_' -'Südwest' -'Sá' -'Synth' -'Symptom' -'Symp' -'Symbol' -'Swe' -'Sustainable_' -'Surf' -'Supp' -'Superior_' -'Super_' -'Sunni_' -'Subsidiarit' -'Stö' -'Sty' -'Student_' -'Stro' -'Stress' -'Strecke_' -'Strategi' -'Strat' -'Strassen_' -'Stran' -'Strafgericht' -'Strafe' -'Stol' -'Stoffen_' -'Stipendi' -'Still' -'Stick' -'Stich' -'Steve_' -'Steuersenkungen_' -'Stern_' -'Sterb' -'Steph' -'Step' -'Stellvertreter_' -'Stellungnahmen_' -'Stellenwert_' -'Steinberg_' -'Steel' -'Statisti' -'Stadtteil_' -'Stadium_' -'Stable_' -'Stabilisierung' -'Staatsanw' -'Staat' -'Später_' -'Spyware_' -'Spy' -'Spr' -'Sporta' -'Spion' -'Speak' -'Spannung' -'Spanische' -'Spaniens_' -'South' -'Sonntag_' -'Song' -'Sonders' -'Sonderbe' -'Somet' -'Solution_' -'Solidarity_' -'Sofi' -'Socialists_' -'Smart' -'Slovakia' -'Sli' -'Skript' -'Ski_' -'Skepti' -'Ske' -'Sixt' -'Sitzung' -'Sis' -'Singh' -'Simply_' -'Silva_' -'Silicon' -'Signal' -'Sig' -'Sicherheitsst' -'Sicherheitsrates_' -'Sicherheitskräfte_' -'Sich_' -'Show' -'Short_' -'Sheraton_' -'Sham' -'Shaf' -'Shadow' -'Sev' -'Setz' -'Sensibili' -'Sender_' -'Seminar_' -'Select' -'Segment' -'Securit' -'Screen' -'Scottish_' -'Schätz' -'Schäd' -'Schwung_' -'Schwimm' -'Schwelle' -'Schwed' -'Schwarzen_' -'Schutzmaßnahmen_' -'Schutze' -'Schur' -'Schuldner_' -'Schuldenerlass_' -'Schottland_' -'Schnittstelle_' -'Schn' -'Schmu' -'Schmit' -'Schmi' -'Schlussfolgerung_' -'Schließung_' -'Schim' -'Schiffen_' -'Schar' -'Schan' -'Schall' -'Scar' -'Scanne' -'Scal' -'Saudis_' -'Satelliten_' -'Sant_' -'Sample' -'Sammel' -'Sal' -'Saison' -'Saf' -'Sacr' -'Sachver' -'Sachs_' -'SY' -'SV' -'SSL' -'SSE' -'SR' -'SN' -'SK' -'SITE_' -'SIM' -'SDR' -'SARS_' -'SAP_' -'Rückz' -'Rückschlag_' -'Rückg' -'Röm' -'Räume' -'Russische' -'Rus' -'Rumsfeld_' -'Rov' -'Route_' -'Rotarian' -'Rota' -'Romulan' -'Romans_' -'Romano' -'Rol' -'Rog' -'Roc' -'Road' -'Rival' -'Risk' -'Rindfleisch' -'Rin' -'Rif' -'Ride' -'Richtungen_' -'Rh' -'Revolutionary_' -'Revol' -'Resultate_' -'Restaura' -'Resources_' -'Resol' -'Reso' -'Repu' -'Representative' -'Rentner' -'Renov' -'Reli' -'Release' -'Relati' -'Rein' -'Reichweite_' -'Reich_' -'Regulierungen_' -'Regulat' -'Regionalpolitik_' -'Regierungsvertreter' -'Regierungsführung_' -'Regierungse' -'Regent' -'Regens' -'Regel' -'Rega' -'Referenz' -'Redner' -'Recon' -'Rechtsp' -'Rechtsakt' -'Rechtfertigung_' -'Recht' -'Rechenschaft_' -'Rebellen' -'Realis' -'Reakt' -'Rav' -'Raus' -'Ratspräsident' -'Rassismus_' -'Rasse' -'Range' -'Ramada' -'Rak' -'Rai' -'Rag' -'Radi' -'Rabatt' -'RU' -'ROM' -'RL' -'Quit' -'Quartet' -'Quartal' -'Quality_' -'Qualifi' -'Quadra' -'QU' -'Py' -'Purvis_' -'Pur' -'Puffer' -'Präventi' -'Prämi' -'Prozessor_' -'Provokation' -'Provi' -'Protest_' -'Protektionismus_' -'Prophet' -'Prope' -'Prog' -'Profite_' -'Production' -'Produ' -'Probe' -'Privatsphäre_' -'Privathaus' -'Price' -'Preventi' -'Prestige_' -'Prese' -'Preiss' -'Portug' -'Porti' -'Popular' -'Poor_' -'Poly' -'Politis' -'Polic' -'Points_' -'Plug_' -'Ple' -'Platte' -'Platform_' -'Plasma' -'Pix' -'Pitt' -'Pira' -'Picture' -'PiS_' -'Photo_' -'Pflege' -'Pferde' -'Pfei' -'Petitions_' -'Petitions' -'Petition' -'Pesti' -'Peru' -'Persönlichkeit' -'Peripherie_' -'Period' -'Pere' -'Pentax_' -'Pensionen_' -'Penis' -'Peninsula_' -'Pear' -'Pav' -'Pauls' -'Patente_' -'Passi' -'Passagiere_' -'Partition_' -'Partie' -'Parlamentsabgeordnete' -'Pare' -'Panzer' -'Panasonic_' -'Palästin' -'Palais_' -'Paket' -'Pai' -'Pack' -'PV_' -'PU' -'PT' -'POS' -'PM_' -'PE_' -'Oz' -'Outdoor_' -'Organismen_' -'Optimierung' -'Omniture_' -'Om' -'Okt' -'Ohren_' -'Oft' -'Offs' -'Official_' -'Occ' -'Obst' -'Obr' -'Objekte_' -'Objekt_' -'Objekt' -'Obers' -'OR' -'ONE_' -'OE' -'Nächte' -'Ny' -'Nuclear_' -'Novo' -'Nov' -'Nots' -'Noti' -'Noten' -'Norwege' -'Norway_' -'Norw' -'Normalerweise_' -'Norde' -'Nordamerika' -'Nikotin' -'Nieders' -'Nicholas_' -'Ng' -'Neuigkeiten_' -'Neug' -'Neue' -'Neube' -'Neub' -'Neuan' -'Netze' -'Network' -'Nephi_' -'Need_' -'Need' -'Nazis_' -'Navy_' -'Navig' -'Natursch' -'Native_' -'Nationalstaaten_' -'Nationalist' -'Nationale_' -'Namun_' -'Nai' -'Nahverkehr' -'Nachteil_' -'Nachbarschafts' -'Münzen_' -'Möchte' -'Mé' -'Mär' -'Mängel_' -'Muss' -'Musk' -'Musiker_' -'Musi' -'Muni' -'Mun' -'Mum' -'Movi' -'Movement_' -'Mosc' -'Montp' -'Montenegro_' -'Montage' -'Monaco_' -'Modells_' -'Mobili' -'Mits' -'Mitgliedstaat' -'Mitgefühl_' -'Mischung_' -'Ministr' -'Minimum_' -'Minderheiten' -'Million' -'Millennium' -'Militär_' -'Miles_' -'Mig' -'Messung_' -'Merkmale' -'Mercur' -'Menschenrechtsverletzungen_' -'Meinungsverschiedenheiten_' -'Mehrwert_' -'Mehr' -'Megapixel' -'Meg' -'Meer' -'Medit' -'Medikamenten_' -'Medikamente_' -'Medicine' -'Maur' -'Matth' -'Matte' -'Mathematik_' -'Mathe' -'Massenvernichtungswaffen_' -'Massen_' -'Massagen_' -'Massage' -'Massachusetts_' -'Maschinen' -'Mary' -'Marshall_' -'Marra' -'Maritime_' -'Marie' -'Margaret_' -'Marco' -'Marbella_' -'Mao' -'Mant' -'Mano' -'Mannschaft_' -'Mann' -'Mandat' -'Malay' -'Mahm' -'Magne' -'Magn' -'Maf' -'Madr' -'Made' -'Maci' -'Machthaber_' -'Machi' -'Macedonia_' -'Maca' -'MID' -'MDGs_' -'MAR' -'MADRID_' -'Luftfahrt' -'Luca' -'Lub' -'Lore' -'Looking_' -'Loo' -'Lond' -'Lodge_' -'Lob' -'Ll' -'Lith' -'Lita' -'List' -'Liquidität_' -'Lip' -'Lion' -'Linu' -'Limited_' -'Lieferungen_' -'Lieferant' -'Lieblings' -'Liebe' -'Lichte_' -'License' -'Letztere_' -'Letter' -'Lek' -'Leistungsfähigkeit_' -'Leipzig_' -'Legitimation_' -'Legislative_' -'Lef' -'Led' -'Lebensbedingungen_' -'Learning_' -'Laur' -'Laufwe' -'Laser_' -'Laos_' -'Lanka_' -'Langstrecken' -'Landschaft' -'Lande_' -'Lance' -'Lamanites_' -'Lack' -'LS' -'LP' -'LOS_' -'LICH' -'LES' -'LDP_' -'Kürzungen_' -'Künstler' -'Könnte_' -'Kurzu' -'Kurve' -'Kun' -'Kumari_' -'Krugman_' -'Krone' -'Krist' -'Krim' -'Kriegsverbreche' -'Kreditk' -'Kreativität_' -'Krat' -'Krank' -'Kraftfahr' -'Korrekt' -'Koordination_' -'Kooperationsabkommen' -'Konzern' -'Konzepte_' -'Konve' -'Kontroverse' -'Kontrast_' -'Konstruktion' -'Konkur' -'Kondition' -'Kompromisse_' -'Kompe' -'Kommunistische' -'Kommissare_' -'Kommentare_' -'Kode' -'Kne' -'Klimasch' -'Klassen_' -'Klassen' -'Klagen' -'Kissinger' -'Kind' -'Kha' -'Kenne' -'Keep_' -'Kaz' -'Katzen' -'Kategorien_' -'Kaste' -'Kasach' -'Karten' -'Karr' -'Karl_' -'Kare' -'Kapitalst' -'Kapell' -'Kanzle' -'Kandidatenländer' -'Kamera' -'Kambodscha_' -'Kalt' -'Kalifornien' -'Kalif' -'Kad' -'Kabine' -'KB_' -'Jörg_' -'Jup' -'Juncker_' -'Julian_' -'Jugendherberge_' -'Jos' -'Johannes_' -'Jiang_' -'Jel' -'Jazz' -'Jarzembowski_' -'Jame' -'JPEG_' -'JP' -'Iss' -'Isolation_' -'Islamischen_' -'Iron' -'Iri' -'Ira' -'Ion' -'Inva' -'Intr' -'Interview' -'Interv' -'Intern' -'Interi' -'Interess' -'Inten' -'Integrat' -'Inspektor' -'Insel' -'Inn' -'Inkrafttreten_' -'Ink' -'Ini' -'Informations_' -'Industrien' -'Indikatoren_' -'Indikat' -'Indian' -'Inde' -'Inc' -'Implementi' -'Imperi' -'Ih' -'If' -'Identitäten_' -'Iber' -'IX' -'ISS' -'INS' -'IND' -'INC' -'Hürde' -'Höh' -'Häusern_' -'Häuser_' -'Hut_' -'Hut' -'Hus' -'Humanit' -'Hug' -'Hu_' -'Hoste' -'Hospi' -'Honor' -'Home' -'Hoheit' -'Hohe' -'Hochzeit' -'Hob' -'Hirsch' -'Hip' -'Hinterl' -'Hinsichtlich_' -'Himm' -'Highlight' -'Hierbei_' -'Herzego' -'Herunter' -'Herstell' -'Herkunftsl' -'Herberge_' -'Hell' -'Held' -'Hektar_' -'Heimat_' -'Heid' -'Hegemonie_' -'Heer' -'Heating_' -'Head_' -'Head' -'Haza' -'Haw' -'Havel_' -'Haushaltsk' -'Haushalten_' -'Hauptziel' -'Hauptg' -'Harry_' -'Hardliner' -'Handy_' -'Hands' -'Handelsd' -'Halbjahr_' -'Haft_' -'Had_' -'Habr' -'Gut_' -'Gus' -'Gul' -'Guid' -'Guer' -'Guantánamo_' -'Gründungs' -'Grö' -'Grä' -'Grundwerte_' -'Grundv' -'Grundge' -'Gru' -'Green' -'Grap' -'Grant_' -'Grand' -'Granada_' -'Gran_' -'Gou' -'Got' -'Goe' -'Glückw' -'Globalis' -'Globale_' -'Glaube_' -'Glacier_' -'Gl' -'Giu' -'Gitarren' -'Gir' -'Ghana_' -'Gewässer' -'Gewi' -'Gewebe' -'Getreide' -'Gesundheitswesen_' -'Gesundheitsschutz' -'Gestern_' -'Gestalt_' -'Gesta' -'Gesichtspunkt' -'Gesetzen' -'Geschäftsreise' -'Geschäftsführ' -'Geschmack' -'Geschichts' -'Geschenk_' -'Gescheh' -'Geräten_' -'Georgian_' -'Gemeinschaftsrecht' -'Gemeinsame_' -'Gemeinsam_' -'Gegebenheiten_' -'Gefangene' -'Gefa' -'Gedanke' -'Ged' -'Gebühren_' -'Gebietskörperschaften_' -'Gaul' -'Gau' -'Gastl' -'Garc' -'Ganzes_' -'Gandhi_' -'Galic' -'Galax' -'Gai' -'GT' -'GMO_' -'GM' -'GIMP_' -'GF' -'GBP_' -'Fürsten' -'Fürs' -'Führungsp' -'Führungen_' -'Führer' -'Fäl' -'Fäh' -'Fuss_' -'Funktionieren_' -'Funktionalität_' -'Full_' -'Fue' -'Früher' -'Frucht' -'Fronte' -'Frist_' -'Friedh' -'Freilassung_' -'FreeBSD_' -'Frattini_' -'Frassoni_' -'Franz_' -'Franz' -'Fox_' -'Fourth_' -'Fotograf' -'Fortschritts' -'Forst' -'Formulierung_' -'Forme' -'Forex_' -'Folter_' -'Folgende' -'Flüge_' -'Flü' -'Flugver' -'Flucht' -'Flach' -'Fl' -'Fitnessraum_' -'Fitnesscenter_' -'Fitness' -'Fiskalp' -'Fischf' -'Finn' -'Finanzt' -'Finanzministeri' -'Finanzma' -'Finanzinstitut' -'Finanziellen_' -'Finanzi' -'Finanzhilfe' -'Finanzdienstleistungen_' -'Filip' -'Figu' -'Field' -'Fie' -'Feu' -'Ferrer' -'Ferr' -'Fernsehs' -'Fen' -'Feli' -'Feinds' -'Fehlern_' -'Fehle' -'Faz' -'Fax' -'Fanati' -'Fan' -'Fail' -'Fahnen' -'Fah' -'FOR' -'FI_' -'FC' -'FBI_' -'Extremismus_' -'Exporte' -'Expansion' -'Exce' -'Exa' -'Everyone_' -'Eurosta' -'Europäer' -'Europe' -'Europarat' -'Eure' -'Eto' -'Ethiopia_' -'Es' -'Erzeug' -'Erwägung_' -'Erwe' -'Ersparnisse_' -'Ernährungs' -'Erlös' -'Erlebnis_' -'Erla' -'Erkenntnisse_' -'Erkenn' -'Erinnerungen_' -'Erika_' -'Eric' -'Erhalt_' -'Erfordernissen_' -'Erfa' -'Erf' -'Ereignissen_' -'Ereignis_' -'Erb' -'Era' -'Equally_' -'Equal' -'Epidemi' -'Entwicklungszusammenarbeit_' -'Entwicklungshilfe_' -'Entspannen_' -'Entschl' -'Entscheidungsfindung_' -'Entscheide' -'Entlassung' -'Entfernung_' -'Entdeckung_' -'Entdecke' -'Engine' -'Engel' -'Energieversorgung_' -'Energieverbrauch' -'Energietr' -'Empfehlung_' -'Emm' -'Emb' -'Eman' -'Elys' -'Eliten_' -'Eli' -'Element' -'Elektrizität' -'Elect' -'Elb' -'Einwohnern_' -'Eintrag_' -'Einstei' -'Einnahme' -'Einla' -'Einig' -'Einheitswährung_' -'Eingriff' -'Eingang_' -'Eindr' -'Eight' -'Eigentums' -'Ehren' -'Effe' -'Edward' -'Economists_' -'Ecol' -'Echt' -'Eben' -'East' -'Earl' -'EV' -'EQ' -'EOS_' -'ENE' -'EME' -'EFSF_' -'EF' -'EEC_' -'EE' -'EA_' -'Durchschnitt_' -'Durchs' -'Duke' -'Duc' -'Dua' -'Dry_' -'Drogenh' -'Drac' -'Dosi' -'Domain' -'Dollars_' -'Dokumenten_' -'Dokt' -'Dod' -'Doctor_' -'Divi' -'Disziplin' -'Distri' -'Disney_' -'Discover' -'Direktor_' -'Direkt_' -'Diamant' -'Devisenwechsel_' -'Device' -'Develope' -'Dess' -'Demonstrationen_' -'Demonstration_' -'Dei' -'Defizit_' -'Definitionen_' -'Deep' -'Deco' -'Deborah_' -'Deal_' -'Davon_' -'Dav' -'Datenbank' -'Darstell' -'Dark_' -'Danube_' -'Dalai_' -'Daily' -'Dafür_' -'Dach_' -'DU' -'DSLR_' -'DF' -'DEL' -'Cyp' -'Cus' -'Cub' -'Crow' -'Cristi' -'Cris' -'Cr' -'Coven' -'Course' -'Coup' -'Cort' -'Corp' -'Cookies_' -'Cookie_' -'Coo' -'Control' -'Conte' -'Contain' -'Const' -'Conservative_' -'Configur' -'Cond' -'Compo' -'Compli' -'Communities_' -'Communis' -'Commonwealth_' -'Commons_' -'Commerc' -'Collection_' -'Co_' -'Cluster' -'Cli' -'Claude_' -'Citi' -'Christi' -'Chief_' -'Chic' -'Chian' -'Chest' -'Chelsea_' -'Charme_' -'Charlotte' -'Champions' -'Catherine_' -'Catalunya_' -'Cart' -'Carol_' -'Cap_' -'Cana' -'Campi' -'Camp_' -'Came' -'Cale' -'Cadiz_' -'CV' -'CK' -'CF_' -'CER' -'C6_' -'Bürgerrecht' -'Bügelservice_' -'Byrne' -'Button_' -'Burning_' -'Bureau_' -'Bul' -'Built_' -'Buffe' -'Buen' -'Budget' -'Buddhist_' -'Bucharest_' -'Brüder' -'Brücken' -'Brücke_' -'Brook' -'Broad' -'Bristol_' -'Brief' -'Brasiliens_' -'Brand_' -'Bow' -'Boutique' -'Borr' -'Born' -'Boote' -'Boom_' -'Bolivia_' -'Boliv' -'Bod' -'Blase' -'Blanch' -'Blan' -'Bit' -'Binnenmarkts_' -'Bind' -'Bildschirm' -'Bic' -'Bibl' -'Bewusstsein_' -'Beton' -'Besuchen_' -'Bestec' -'Bestands' -'Besichtigung' -'Besi' -'Beschl' -'Besatzungs' -'Berna' -'Berichterstatters_' -'Berge' -'Bergbau' -'Berei' -'Beratungs' -'Benalmadena_' -'Beleg' -'Belange_' -'Bek' -'Beitrittsländer_' -'Behinderung' -'Begr' -'Befreiung' -'Befehl_' -'Beck' -'Beantwortung_' -'Bauern' -'Batterie' -'Bat' -'Bass' -'Barre' -'Barr' -'Baron' -'Barba' -'Bankr' -'Bala' -'Bak' -'Bagdad_' -'Baby_' -'Baby' -'BP' -'BEA' -'BBC_' -'Außens' -'Außenhandel' -'Autovermietung_' -'Autos_' -'Autorität_' -'Autok' -'Auth' -'Ausstellung' -'Ausser' -'Ausschüsse_' -'Auss' -'Ausrichtung_' -'Ausländer' -'Auslegung' -'Ausge' -'Ausgang_' -'Ausg' -'Ausfuhr' -'Auseinandersetzungen_' -'Auseinandersetzung_' -'Ausdruck' -'Auschecken_' -'Ausbreitung_' -'Ausbau' -'Auktion' -'Aufwand_' -'Aufw' -'Aufschub_' -'Aufrechterhaltung_' -'Auflagen_' -'Aufla' -'Auditor' -'Attrakti' -'Athens_' -'Assozi' -'Asc' -'Arts_' -'Artikels_' -'Arra' -'Aro' -'Arn' -'Argentiniens_' -'Arg' -'Archive_' -'Arbeitszeit' -'Arbeitsplatz' -'Arbeitsmärkte' -'Arbeiter' -'Aqu' -'Application' -'Applause_' -'Appartement_' -'Apollo_' -'Aparthotel' -'Apartamentos_' -'Apache_' -'Anwende' -'Anweisungen_' -'Antw' -'Antrieb' -'Ansta' -'Anschuldigungen_' -'Anschluss' -'Anrei' -'Anleitung_' -'Ankündigung_' -'Ankara_' -'Animat' -'Anhä' -'Angreifer' -'Angeli' -'Anfä' -'Anfa' -'Andy_' -'Analyst' -'Amat' -'Alvaro_' -'Alternative' -'Alte' -'Almost_' -'Allgemeine' -'Alkohol_' -'Ali_' -'Alexanderplatz_' -'Alba' -'Aktiv' -'Akt_' -'Ahmed' -'Ahmadinedschad_' -'Agriculture_' -'Agent' -'Against_' -'Afrikanischen_' -'Afghan_' -'Advi' -'Advent' -'Adress' -'Adam_' -'Ach' -'Aca' -'Abwärts' -'Abstände' -'Absp' -'Abschreckung_' -'Abschnitt' -'Abschluss' -'Abre' -'Abraham_' -'Above_' -'Abn' -'Abhol' -'AX' -'ATION' -'ANY_' -'ALL_' -'AK' -'AE' -'ABAP_' -'AA_' -'A350_' -'A1_' -'; • _' -';' -'93' -'92' -'91' -'84_' -'84' -'825' -'81_' -'78' -'73_' -'71_' -'70' -'57' -'450' -'42' -'320' -'3000_' -'2nd_' -'226' -'204' -'2018_' -'2017_' -'2016_' -'1981_' -'1974_' -'1969_' -'1958_' -'1957_' -'1955_' -'1951_' -'1930er_' -'1918_' -'179' -'158' -'140_' -'13th_' -'125' -'110_' -'10th_' -'104' -'103_' -'100' -'0ern_' -'011' -'010' -'// _' -'.  ' -'.: _' -'...) _' -'.)._' -'. – ' -'.   _' -'. .' -'. ) _' -'. " _' -', ..._' -')|_' -'): «' -'() , _' -'%), _' -'": _' -'")._' -'!”' -'!!!!' -' „ _' -' –&' -' –' -' « _' -'  ' -' ..' -' ($_' -' ''' -'™-_' -'€_' -'…_' -'”) _' -'“) _' -'‘' -'ا' -'י' -'ң' -'қ_' -'ін' -'ында' -'ші' -'ть_' -'сын' -'со' -'р_' -'пр' -'пар' -'ных_' -'на_' -'кономи' -'ка_' -'ит' -'ел' -'гі' -'го_' -'га' -'бе' -'ас' -'Т' -'К' -'Г' -'ρ' -'ο' -'Ž' -'ż' -'ška_' -'ý_' -'üße' -'üß' -'ütung' -'ütlich' -'ütige' -'üstet_' -'ürze' -'ürt' -'ürge' -'ürfe' -'ürdige' -'üpf' -'üng' -'ündete' -'ünde_' -'üllt_' -'ührten_' -'ührende' -'ühmt' -'üf' -'ücken_' -'üblich_' -'überzogen' -'übersetz' -'überschü' -'überschreitende' -'überra' -'übernahme' -'überleb' -'überholt' -'übergehen' -'überflü' -'übereinstimmen_' -'übereinkommen_' -'ößt_' -'ött' -'östlichen_' -'öster' -'öst_' -'öse' -'örtlichen_' -'örtliche' -'örte' -'örig' -'ör_' -'ökologisch' -'öhnlich_' -'öhe' -'öglichkeiten_' -'öge' -'öffnete' -'öffnet_' -'öffentliches_' -'öd' -'öcke' -'ôte_' -'ò' -'ï' -'î' -'être_' -'ête' -'ém' -'èr' -'ège_' -'äßig' -'äußerte_' -'äußert_' -'äuser' -'äume_' -'äum' -'ässig' -'ärz' -'ärt' -'ärmeren_' -'ärer_' -'ändler' -'ändiger_' -'änderte' -'ämte' -'ällt_' -'äle_' -'ähle' -'ähig' -'ägyptischen_' -'ägt_' -'äger' -'äg' -'ächtige' -'äche' -'ás' -'ßlich' -'ßer_' -'ßer' -'ßb' -'Überwindung_' -'Übersetzer' -'Überna' -'Überlebens' -'Übergriffe' -'Überflu' -'Übereinkommens_' -'Überarbeitung_' -'Österreich' -'Öls' -'Ölpreis' -'Öle' -'Ökolo' -'Äußer' -'Äquivalen' -'Ähnliche' -'Ã_' -'·      ' -'²_' -'®_' -'®, _' -' – ' -' %, _' -'}}) ==' -'}})' -'}{_' -'}, _' -'|' -'zünd' -'zzle' -'zykli' -'zwischenstaatliche' -'zweitgrößte_' -'zweimal_' -'zweige' -'zweier_' -'zweie' -'zwec' -'zwang' -'zuwider' -'zuvorkommende' -'zuverlässiger_' -'zuv' -'zutreffend' -'zusammenzuf' -'zusammenzuarbeiten_' -'zusammentre' -'zusammenhä' -'zusammenf' -'zurückzuh' -'zurückkehren_' -'zurückk' -'zurückbl' -'zunahm' -'zulässig_' -'zula' -'zukomm' -'zugr' -'zugewiesen' -'zugeschnitten' -'zugesa' -'zugelassen_' -'zugehen_' -'zufügen_' -'zufällig' -'zubereitet_' -'zte_' -'zst' -'zoo_' -'zonen_' -'zol' -'zitä' -'zitiere_' -'zit' -'zipation_' -'zines' -'zien' -'zheimer_' -'zerr' -'zero' -'zerbrech' -'zentri' -'zentral' -'zend' -'zeitweilige' -'zeitl' -'zeitiger_' -'zeitig' -'zeita' -'zeilen_' -'zeil' -'zeigten_' -'zeige' -'zeichnung_' -'zar_' -'yte' -'yment' -'yla' -'yie' -'yg' -'yev' -'yell' -'yea' -'ydr' -'yak' -'xts_' -'xon_' -'xn' -'xist' -'xim' -'xes_' -'würdige_' -'wört' -'wöchentlich' -'wärt' -'wäl' -'währung' -'wski_' -'wron' -'writer_' -'wrap' -'wozu_' -'wovon_' -'wounded_' -'worthwhile_' -'worries_' -'workshop' -'workplace' -'womit_' -'wofür_' -'wling' -'withstand_' -'withdrawn_' -'withdraw' -'wissenschaftliche' -'wissenschaftl' -'wirkte' -'wins_' -'winners_' -'winkel' -'willkürlich' -'willen_' -'wilde' -'wiese' -'wiederher' -'wiederge' -'widmet' -'widersetz' -'wick' -'whatsoever_' -'wettbewerbsfähig_' -'wettbewerb_' -'wertig' -'werkzeug' -'wende_' -'wend' -'wen' -'welding_' -'welders_' -'weites' -'weiterf' -'weiterentwickel' -'weilen_' -'weiche_' -'wege_' -'weekend' -'weck' -'wechsels' -'wechs' -'weakening_' -'wc' -'way' -'wav' -'watt' -'watershed_' -'waterfalls_' -'wasted_' -'warrior' -'ward' -'wanting_' -'wang' -'wandte' -'wahrha' -'wach' -'völligen_' -'völ' -'vá' -'vulnerabilities_' -'vul' -'votre_' -'vorzugehen_' -'vorzei' -'vorz' -'vorsorge_' -'vorrangige' -'vorne_' -'vorle' -'vorlag' -'vorhin_' -'vorhersehbar' -'vorherrschende' -'vorherigen_' -'vorherge' -'vorhandene_' -'vorhaben' -'vorgetragen_' -'vorgesehene_' -'vorgeleg' -'vorgebracht_' -'vorb' -'voraus' -'vorantreib' -'vorangehen' -'vorangegangenen_' -'voranbringen_' -'voor_' -'von' -'volunt' -'volumes_' -'vollziehen_' -'vollendet' -'voli' -'vole_' -'voic' -'vivid' -'vität_' -'vita' -'visit' -'viru' -'viou' -'violate_' -'violate' -'viol' -'vio' -'villa' -'vigorously_' -'vigilant' -'viewing_' -'viewer_' -'viet' -'vierz' -'vielversprechend' -'viels' -'vielfach' -'vidue' -'vide' -'vib' -'viat' -'vete' -'vet_' -'verzögern_' -'verzerr' -'verzeichnis_' -'verzauber' -'verwöhn' -'verwirr' -'verwir' -'verwendete_' -'verweigert' -'verweh' -'verwand' -'verwaltung' -'verwaltet_' -'verurteilen_' -'verursachen_' -'vertritt_' -'vertretene' -'vertretende' -'vertiefen_' -'vertie' -'verteil' -'verteidigt_' -'versäume' -'versuchten_' -'versu' -'verstümmel' -'verstehe_' -'versteckt' -'verstecken_' -'versta' -'versp' -'versorgen_' -'versorg' -'versicherung_' -'verschwenderische' -'verschre' -'verschr' -'versatil' -'versammel' -'versagt_' -'vers_' -'verpflichtung' -'verordn' -'vernünftige' -'vernetz' -'vernehm' -'vernachlässigt' -'vernachlässigen_' -'vermindert' -'verlässliche' -'verließ' -'verlie' -'verletzen' -'verleih_' -'verlangsam' -'verlang' -'verlagerung' -'verkünde' -'verkl' -'verkehr' -'verkaufte' -'verhältnisse' -'verhält' -'verheirat' -'verhandl' -'verhandelt' -'verhaftet_' -'vergift' -'verfügbare' -'verfolgte_' -'verfe' -'verfa' -'vereinte' -'vereinig' -'vereinfachen_' -'vereinbarten_' -'vereinbarte' -'verein' -'verdreifach' -'verdoppelt_' -'verdoppel' -'verdan' -'verbu' -'verbrenn' -'verbrachte' -'verborgen' -'verbleiben' -'verban' -'veranstaltet' -'veranlasst_' -'verankert_' -'verabscheu' -'venture' -'vent_' -'vendor' -'veil' -'vec' -'vd' -'vastly_' -'variier' -'variat' -'var_' -'valve' -'vale' -'vald' -'ußen' -'ux' -'uv' -'utsbe' -'utr' -'utin' -'uth' -'utenant' -'ustausch_' -'usgaben_' -'urz' -'uru' -'urt' -'ursa' -'urging_' -'urg' -'urch' -'urbaniz' -'urban' -'urate' -'ura_' -'upte' -'uphold' -'upheaval' -'upa' -'uon' -'unzähligen_' -'unzwe' -'unzureichende' -'unz' -'unwillingness_' -'unweigerlich' -'unwanted_' -'unverä' -'unu' -'unthink' -'unterzeichnete' -'unterzeichn' -'unterworfen_' -'unterteilt_' -'untersuch' -'unterstrichen_' -'unterstreiche' -'unterschätzt' -'unterschiedlich' -'unterschied' -'untersch' -'unterminieren_' -'untergräbt_' -'unteren_' -'unterbrechen_' -'unsc' -'unresolved_' -'unrea' -'unqu' -'unpredictab' -'unmittelbarer_' -'unmen' -'unlängst_' -'unkomp' -'unko' -'unite_' -'unis' -'unilateralism_' -'unica' -'uni_' -'unha' -'ungü' -'ungsze' -'ungsverfahren' -'ungso' -'ungser' -'unglück' -'unglaublich' -'ungerecht' -'ungenügend' -'ungehe' -'unfr' -'unfortunate_' -'unforgettable_' -'unfolding_' -'unfair' -'unexp' -'uneven' -'unes_' -'unerwünscht' -'unerwartete' -'undurch' -'undung' -'undi_' -'underestimate' -'underc' -'undeniabl' -'unden' -'unconditional' -'uncomfortable_' -'uncle' -'unbegrenzt' -'unaufhaltsam' -'unat' -'unam' -'umstr' -'umst' -'umsetz' -'umm' -'umin' -'umh' -'umgest' -'umgeh' -'umgebung' -'umfassendere_' -'umfangreichen_' -'umfangreich' -'umfa' -'umf' -'ume_' -'umbrella' -'uma_' -'ultra_' -'uls_' -'ulos' -'ulierung' -'uldn_' -'ulde' -'ularly_' -'ukr' -'uko' -'uit_' -'ugu' -'ugi' -'ugge' -'ufs' -'ufl' -'uff' -'uerung_' -'uern_' -'uellen_' -'uelle_' -'udo' -'udia' -'ubt_' -'uba' -'ub_' -'uay' -'uate' -'uas' -'tüt' -'tümer' -'tödlich' -'täts' -'tätigkeit' -'täter' -'tär' -'tzl' -'tzende' -'tze_' -'tyrant' -'typis' -'twin' -'tutt' -'tutor' -'tus_' -'turm' -'tur_' -'tuous' -'tunnel' -'tuna_' -'tuition' -'tuber' -'tti_' -'ttert' -'tters' -'tsp' -'tsi' -'tscheni' -'tsa' -'trö' -'trä' -'trusted_' -'truppen_' -'trupp' -'trug_' -'truc' -'trouble' -'trou' -'trot' -'tropis' -'trophi' -'trivial' -'trink' -'trim' -'trigger' -'trieben_' -'trieb' -'tribute_' -'treu_' -'tres_' -'tres' -'tree_' -'treasure_' -'treas' -'traße_' -'travelers_' -'travail_' -'traue' -'trau' -'trat_' -'trat' -'trapp' -'transparente_' -'transp' -'transmitt' -'translator' -'transformer_' -'transformati' -'transatlantic_' -'tran' -'tram_' -'trali' -'trail_' -'trai' -'tragbar' -'traditioneller_' -'trades_' -'trac' -'tournament_' -'tourismus' -'touri' -'totalit' -'tos' -'tops_' -'topbonus_' -'too' -'toni' -'tone' -'toma' -'tolerate' -'token_' -'toilet_' -'toilet' -'tment' -'tma' -'tkan_' -'titles_' -'titi' -'tist' -'tisch' -'tis_' -'tings_' -'timme' -'timing_' -'timi' -'tilt' -'till' -'tiker' -'tightening_' -'tig_' -'tiere' -'tier_' -'tiefen_' -'tief' -'tied_' -'tid' -'tick' -'tici' -'tiate' -'tian_' -'thwart' -'thun' -'throne_' -'thresholds_' -'threatens_' -'thoroughly_' -'thon_' -'theori' -'theoretical_' -'tgut' -'tformen_' -'textiles_' -'textile_' -'text' -'teurer' -'test' -'terte_' -'tert' -'terroristische_' -'terroris' -'territoriale_' -'territoriale' -'terminology_' -'terminat' -'term' -'terli' -'teren' -'terd' -'terblichkeit' -'terba' -'tent_' -'tendi' -'tempt' -'template' -'temperature' -'temperatur' -'tels' -'teles' -'telephones_' -'tekn' -'teilung_' -'teilnehmer' -'technologie_' -'technologie' -'technically_' -'tching_' -'tbe' -'tbar' -'tav' -'tausende' -'taught_' -'tatte' -'tator' -'tation' -'tate_' -'tasa' -'tariffs_' -'targeting_' -'tape_' -'tandard' -'tand' -'tamb' -'tally_' -'tali' -'takti' -'takeover_' -'tains_' -'tail_' -'tahu' -'taha' -'tactic' -'taat' -'taa' -'südlich_' -'sü' -'säum' -'säu' -'sätz' -'säkulare' -'säch' -'szei' -'systemi' -'systematische' -'syrischen_' -'syrische_' -'synd' -'symbolische' -'swing_' -'swin' -'sweet_' -'sweeping_' -'sust' -'suspended_' -'suspend' -'survivors_' -'surviving_' -'surv' -'surgery_' -'surfing_' -'supr' -'suppress_' -'support' -'superpower_' -'superiority_' -'sunny_' -'sung_' -'sums_' -'sug' -'suffice' -'suchte' -'succeed' -'subtr' -'subti' -'substitute' -'subsistence_' -'subsidiaries_' -'subscription' -'subo' -'subjecti' -'stücke' -'ständnis' -'ständi' -'stände_' -'städtischen_' -'styl' -'sty_' -'stumbl' -'stuhl_' -'studying_' -'studios_' -'studiere' -'stub' -'ströme_' -'strukturierte' -'stroke' -'stritt' -'strip' -'stringen' -'striking_' -'strenge_' -'streiche' -'streaming_' -'straße_' -'strain_' -'strain' -'strafrechtliche_' -'storm_' -'stopped_' -'stones_' -'stole' -'stm' -'stliche' -'stische_' -'stipulate' -'stimm' -'stills' -'stig' -'stian' -'stere' -'stems_' -'stelle' -'stee' -'stattfand_' -'stattdessen_' -'statis' -'stating_' -'starte' -'starship' -'starring_' -'starr' -'standar' -'stam' -'stall_' -'staged_' -'staff' -'stabilisi' -'ssystem_' -'ssung_' -'ssten_' -'sslich' -'ssige_' -'ssert' -'ssenen_' -'ssad' -'srat' -'square' -'spürbar' -'spü' -'spy' -'spu' -'sprung' -'sprozess' -'sprogramm' -'spro' -'spritz' -'sprech' -'spreads' -'sprachig' -'spots_' -'spotlight_' -'spora_' -'spoo' -'sponsor_' -'spolitik_' -'spokes' -'splendid_' -'spitz' -'spiritual' -'spin_' -'spill' -'spielte' -'spiele_' -'spher' -'sph' -'spezialisierte' -'spezialisiert_' -'sperr' -'sper' -'spektrum_' -'spekt' -'speicher' -'speculati' -'spectac' -'specif' -'specially_' -'specialist' -'specialis' -'spark_' -'spannende' -'spanische' -'spalte' -'spac' -'sozioökonomische' -'soz' -'sovi' -'souverän' -'souls_' -'sorgte_' -'sorgan' -'sonstigen_' -'song_' -'sonabl' -'solusi_' -'solo_' -'soll' -'soi' -'sofortige' -'soci' -'soaring_' -'soap' -'sness_' -'sneak' -'smug' -'smoke' -'smitte' -'smell_' -'smallest_' -'small' -'slowenisch' -'slope_' -'slin' -'slide' -'slau' -'sland_' -'skontroll' -'skew' -'skeptisch' -'siu' -'sitzt_' -'sistem_' -'sinkt_' -'sincere_' -'simplify_' -'simo' -'similari' -'simila' -'silent' -'signifikante_' -'signi' -'signalisier' -'signal' -'sige' -'sies_' -'sierende' -'sieb' -'sider' -'sidelin' -'sid' -'sichtlich_' -'sicherge' -'sicherere' -'sica_' -'sibl' -'sibi' -'shri' -'showers_' -'shot' -'shortfall_' -'shorter_' -'shore_' -'shof' -'shir' -'shifting_' -'shie' -'shi_' -'she' -'shatter' -'sharpe' -'sham' -'shadow' -'shade' -'sges' -'sger' -'sgebiet' -'sfähig' -'sfr' -'sfe' -'sfa' -'sexual' -'setzten_' -'setze_' -'setup_' -'settle_' -'servici' -'serv' -'seria' -'sequence_' -'sequen' -'sepe' -'separated_' -'sensitivity_' -'sensibl' -'senh' -'sender_' -'sena' -'seminar' -'semi_' -'selu' -'selige' -'self' -'selecti' -'sela' -'sekitar' -'seitig' -'sein' -'sehingga_' -'sehbare' -'seeds_' -'secu' -'sectarian_' -'secrets_' -'secrecy_' -'sechs' -'sece' -'season' -'searche' -'sdi' -'sdat' -'sda' -'scrib' -'scourge_' -'schönes_' -'schädlich_' -'schwing' -'schwierig' -'schwerwiegende_' -'schweizer' -'schwarzen_' -'schwache_' -'schule' -'schuldig_' -'schte_' -'schs' -'schrittweise' -'schriftliche_' -'schriftliche' -'schon' -'scholarship' -'schnellstmöglich' -'schnellstens_' -'schlä' -'schlosse' -'schlichte' -'schlechte' -'schlage' -'schizophreni' -'schirm' -'schienen_' -'schenk' -'scheine' -'scheiden' -'schadet' -'scenery_' -'scenarios_' -'scen' -'scatter' -'scar' -'scal' -'sburg_' -'saver' -'saudi' -'satisfy' -'satisfaction_' -'sar_' -'sangat_' -'sanf' -'sandy_' -'sammlung' -'sames_' -'salv' -'salon' -'salmon' -'sall' -'sail' -'sah' -'sacred_' -'sabotage' -'saa' -'rüst' -'rüh' -'rüf' -'rückt_' -'rücke_' -'rüch' -'römische' -'rés' -'ränken_' -'räglich' -'räder' -'rwart' -'rva' -'rust' -'russi' -'rush' -'rupulous' -'rupt' -'rumo' -'ruft_' -'rufe' -'rue' -'ruch' -'ru_' -'rting_' -'rtig' -'rthe' -'rter_' -'rsion' -'rse_' -'rren_' -'rozess_' -'rox' -'routine' -'rous_' -'rounde' -'rote_' -'rotat' -'rose' -'ror_' -'rooftop' -'rome_' -'roman_' -'roller' -'roh' -'rogue' -'rogramm_' -'rocks_' -'robie' -'rnt_' -'rmt' -'rmina' -'rmat' -'rman_' -'rlin' -'rkste_' -'riös' -'riu' -'ritte' -'ritt_' -'ritis' -'riter' -'risi' -'rises_' -'rische_' -'ringung_' -'ringe' -'rima' -'rigkeit_' -'rigen_' -'rifft_' -'riffen' -'riff' -'riesiger_' -'riesige_' -'rieben' -'ridge' -'richtungen_' -'richtung' -'richer_' -'ribut' -'riad' -'rgen' -'rfs' -'rfer' -'rey_' -'reward' -'revolution' -'revolt' -'revo' -'revision_' -'revision' -'reva' -'rev' -'rett' -'retreat_' -'retic' -'reth' -'reten_' -'rete' -'retains_' -'retained_' -'ret_' -'result' -'rests_' -'restru' -'reste' -'responds_' -'responding_' -'respi' -'respecting_' -'resolu' -'resistant_' -'resident_' -'resi' -'reservier' -'resentment' -'resemble' -'repudiat' -'republikanischen_' -'repräsentati' -'repro' -'representa' -'repositor' -'replacing_' -'repercussions_' -'repea' -'repay_' -'rente' -'renewal_' -'renc' -'remedy_' -'religiös' -'relian' -'relevan' -'releases_' -'rela' -'reitungs' -'reist' -'reise' -'reinve' -'reinforces_' -'reif_' -'reie' -'reicher' -'reichend_' -'reibungslosen_' -'reh' -'reguläre' -'regulier' -'registr' -'regio' -'regi' -'regel' -'regain_' -'refuses_' -'refurbish' -'refu' -'refrigerat' -'refresh' -'reformiert' -'refine' -'reduzierte' -'reduce' -'redo' -'redis' -'redet' -'recyc' -'rect' -'recreat' -'recommend' -'recom' -'reckt_' -'reciproca' -'rechnet' -'rechen_' -'recepti' -'receiver' -'receipt' -'rebuilt_' -'rebuild' -'rebell' -'reba' -'realm' -'reakti' -'reagierte' -'reaffirm' -'readin' -'reacted_' -'react' -'rds_' -'rdnung' -'rdinate_' -'rde_' -'rda' -'rbu' -'rbi' -'rbeit' -'raums_' -'rations_' -'rationalis' -'ratings_' -'ratify_' -'ratifizieren_' -'rassis' -'raschen_' -'rape_' -'rangig' -'rally_' -'rakete' -'raid_' -'raf' -'rado_' -'radikal' -'rad' -'raci' -'quote' -'quicker_' -'quero' -'quenz' -'quee' -'quasi_' -'quart' -'quar' -'qualities_' -'qualify_' -'pé' -'puri' -'purchased_' -'punishment_' -'punishe' -'pundits_' -'punct' -'puff' -'publishe' -'publications_' -'publication_' -'publi' -'pub' -'ption' -'ptic' -'pti' -'pter_' -'psychologi' -'psychi' -'pson_' -'prüfe' -'präzise_' -'präventive' -'pruden' -'prozessor' -'prozess' -'proz' -'proyecto' -'proximité_' -'provoke' -'provisional_' -'protocol' -'protestier' -'protectionist_' -'prostitution_' -'prosper_' -'prosecutor' -'prosecute' -'propag' -'proofed_' -'prone_' -'prompt_' -'promo' -'promi' -'projekten_' -'proj' -'progress' -'programmier' -'programme' -'prognos' -'profile' -'profession_' -'produz' -'produktiver' -'produktiven_' -'prochen' -'processor_' -'processed_' -'problematische' -'probe' -'probability_' -'prizes_' -'privatization' -'privatiz' -'privat' -'principal' -'preview_' -'presumably_' -'pressu' -'presse' -'presenta' -'prep' -'premises_' -'premier_' -'prejud' -'preisen_' -'predecessor_' -'predator' -'precon' -'precis' -'preceding_' -'preceded_' -'praktischer_' -'practise' -'prachige' -'prach' -'ppt_' -'ppne' -'pper_' -'pparat' -'potenzial_' -'potenti' -'posting_' -'poster' -'possesse' -'positiver_' -'positively_' -'positionier' -'posit' -'portugiesischen_' -'portfolio_' -'pornographi' -'porat' -'populist' -'popu' -'pop_' -'pons' -'poni' -'pone' -'polizeilichen_' -'polio_' -'pointer' -'poet_' -'pneum' -'pluralist_' -'plug' -'pling_' -'plikat' -'plica' -'ples' -'pleas' -'playground_' -'plausible_' -'plaus' -'platzier' -'platz' -'platte' -'plates_' -'planet' -'pl' -'pix' -'piso' -'pirate' -'pira' -'pieler' -'phä' -'physische' -'physics_' -'photographer' -'philosophers_' -'philo' -'pher' -'phen' -'phases_' -'pharma' -'pg' -'pflicht_' -'pfer_' -'pfei' -'pfa' -'pezifische' -'petro' -'pest_' -'pessimist' -'pes' -'perusahaan_' -'perta' -'pert_' -'persuaded_' -'perspectiv' -'personal' -'persona' -'perso' -'persiste' -'persecut' -'perse' -'permanente' -'perja' -'periodi' -'performa' -'perform' -'perfekten_' -'perfekte_' -'perceptions_' -'peny' -'pensioner' -'pengu' -'peng' -'penal' -'pektive' -'pedi' -'peculiar' -'pect' -'pc' -'paßt_' -'payer_' -'paya' -'pay' -'pause_' -'patron_' -'patriot' -'patriarch' -'patience_' -'pati' -'patenti' -'patch' -'patan_' -'passp' -'passenden_' -'passende' -'passen_' -'passage_' -'passa' -'pasa' -'partition' -'partisan' -'particul' -'participa' -'partially_' -'partial_' -'partei' -'parte' -'parque' -'parlam' -'paris' -'parc' -'parasit' -'paran' -'parag' -'paradoxical' -'parado' -'paradise_' -'panne' -'panels_' -'panc' -'palästinensische' -'palm' -'pale' -'palace_' -'pai' -'pack_' -'pable_' -'overwhelmingly_' -'overthrow' -'overs_' -'overlap' -'overl' -'overd' -'ova_' -'ova' -'outright_' -'outrageous_' -'outr' -'outer_' -'outbreaks_' -'oup' -'ough' -'otten_' -'osten' -'ostasiatische' -'osphär' -'osph' -'oso' -'osh' -'orz' -'orum_' -'orten' -'orre' -'ormi' -'origine' -'originated_' -'original' -'orientieren_' -'orientation_' -'orie' -'oria' -'organisierten_' -'organise_' -'organisch' -'ordinat' -'orden' -'orch' -'orb' -'orate' -'oral' -'optimistic_' -'optimist' -'optimis' -'optimalen_' -'optim' -'opfern' -'operative_' -'operationen_' -'open' -'ope_' -'oor' -'oo_' -'onwards_' -'onste' -'onomi' -'onment' -'oniert' -'oner' -'onc' -'onale' -'onal' -'ommt_' -'ommen' -'oming_' -'omen_' -'oman' -'olv' -'olungs' -'ols' -'olli' -'olle' -'oll_' -'oliz' -'olive_' -'oliga' -'okan_' -'oire' -'oint_' -'ohnehin_' -'ohl' -'ogic' -'ogenheit_' -'ogati' -'offsho' -'offenkundig_' -'offe_' -'offe' -'ocken_' -'ocht' -'oceans_' -'occurring_' -'occurrence_' -'occupying_' -'occupies_' -'occasion' -'obsole' -'observ' -'obscure' -'oblige' -'obligator' -'objektive' -'objektiv' -'objection_' -'obgleich_' -'obesity_' -'obacht' -'oba' -'oard' -'nützig' -'nöte' -'nössische' -'näher' -'nzende' -'nym' -'nvi' -'nven' -'nutzung' -'nutri' -'nurse' -'nungsl' -'nungs' -'nukl' -'nuan' -'nty_' -'ntrat' -'ntische' -'ntal' -'nstig' -'nst_' -'nsic_' -'npo' -'nour' -'notwendiger' -'notw' -'notified_' -'notebook' -'normen_' -'normale' -'nop' -'nons' -'nonetheless_' -'nominal' -'noble_' -'nob' -'nnial' -'nlich' -'nli' -'nland' -'nl' -'nitt_' -'nistr' -'niss' -'nischer_' -'nik' -'night' -'nieren' -'niedrigste' -'niederzu' -'nichts' -'nia_' -'nho' -'nheit' -'nhaft' -'ngliche' -'ngle' -'ngl' -'ngkin' -'ngh' -'ngens' -'ngen' -'ngeh' -'nfr' -'neutr' -'neuerliche' -'neueren_' -'neuem_' -'nette_' -'nets_' -'nerv' -'nent' -'nenn' -'nel' -'neighbours_' -'neighbor_' -'neid' -'nei_' -'negeri_' -'negativ' -'need' -'ndten_' -'ndliche_' -'ndli' -'nding_' -'nderung_' -'ndern' -'ndene' -'ndelt' -'ndbar_' -'nbild' -'nberg_' -'nbe' -'nationality_' -'nationalistische' -'nationalis' -'nannten_' -'name' -'naive_' -'nahegeleg' -'nage' -'nachzudenken_' -'nachlassen_' -'nachl' -'nachfo' -'münd' -'möglichkeit_' -'möglich' -'même_' -'mé_' -'männliche' -'mw' -'mußt' -'mutmaßliche' -'mutati' -'muslimische_' -'muslimisch' -'municipality_' -'muni' -'multiplie' -'multinationalen_' -'multilingual' -'mulat' -'mud' -'muc' -'mps' -'mple' -'mpho' -'mp3' -'mour' -'motorways_' -'motivierte' -'motivieren_' -'motivated_' -'mother' -'mosqu' -'mosa' -'mort' -'moreover_' -'moralisch_' -'moralis' -'moral' -'monsters_' -'monopolies_' -'mond_' -'monate' -'mois' -'mog' -'modifications_' -'modif' -'moderati' -'modelle_' -'modele' -'modalit' -'modal' -'mobilisiert_' -'mobil' -'mmu' -'mmission' -'mming_' -'mitzuteilen_' -'mittlere_' -'mittl' -'mitt' -'mithilfe_' -'mitgliede' -'mitb' -'mismanage' -'miser' -'mino' -'minimalis' -'minati' -'minat' -'mimic_' -'millenni' -'militia' -'militarily_' -'militari' -'milia' -'milestone' -'mildern_' -'migrator' -'miete' -'mfa' -'metry_' -'metropolitan' -'metropolis' -'meti' -'metaphor' -'metal' -'merupakan_' -'mering' -'merikas_' -'merika' -'merger_' -'merge_' -'menun' -'mention' -'mentally_' -'mentali' -'menschlich' -'meno' -'meni' -'mengg' -'mendo' -'mendapat' -'memori' -'memiliki_' -'membe' -'melo' -'meld' -'melak' -'meinte_' -'mehrheitlich' -'mediterran' -'medien' -'mbr' -'mbo' -'maßgebliche' -'maßgeblich_' -'mayors_' -'may' -'maturit' -'matt' -'matri' -'matisch' -'matis' -'mathematical_' -'mathemati' -'material' -'matching_' -'masyarakat_' -'massacre' -'mass' -'marvel' -'mars' -'markiert_' -'marki' -'mari' -'manufa' -'mans' -'manifestation' -'mane' -'mandate' -'mancher_' -'managements' -'macht' -'lüsse' -'lösungen_' -'lärung' -'lärm' -'längeren_' -'läh' -'lädt_' -'läche' -'lve_' -'luss' -'lui' -'ltungen_' -'lts' -'lton' -'ltet' -'ltes' -'ltere' -'ltene' -'lpin' -'love' -'lov' -'loser_' -'lop' -'loop' -'looming_' -'longe' -'lohnen' -'logisch_' -'login' -'logen_' -'lod' -'locals_' -'loading_' -'load' -'lls_' -'lligte' -'lles_' -'llect' -'llb' -'lj' -'liziert_' -'livel' -'litä' -'litt' -'lita' -'lit_' -'listing_' -'lish' -'lio_' -'linux' -'linking_' -'linke' -'lining_' -'linien_' -'lini' -'linguist' -'lingeri' -'linge_' -'linen_' -'limitations_' -'limit' -'likes_' -'liga' -'lifts_' -'lifting_' -'liest_' -'liegende_' -'lieferungen_' -'lief' -'lied' -'liebe' -'licht' -'lichem_' -'licenses_' -'licens' -'lic_' -'libert' -'liberalism' -'liable_' -'lia_' -'lia' -'lge_' -'lga' -'lfi' -'leverag' -'leva' -'leut' -'letztere' -'letzt' -'lete' -'lest_' -'lesse' -'lers_' -'lern_' -'leo' -'lent_' -'lende' -'lend' -'lement' -'lek' -'leite' -'leistungsstarke' -'leiste' -'leidenden_' -'leid' -'leichten_' -'lehnte_' -'legung_' -'legu' -'legislative' -'legale' -'lega' -'lecht' -'lebendige' -'lebenden_' -'lean' -'lden' -'lc' -'layo' -'layers_' -'laut' -'launder' -'laugh_' -'laub' -'latte' -'lations' -'lateinamerikanischen_' -'lated_' -'lares_' -'lapse' -'langwierige' -'langsamer' -'langsame' -'landwirtschaftliche' -'landscape' -'landmark_' -'landes_' -'landes' -'landed_' -'lamat' -'lain_' -'laim_' -'lager' -'laboratory_' -'küste_' -'künften_' -'kündig' -'kümmert_' -'kühle' -'körperlich' -'käufe' -'kämpfung_' -'kämpft' -'kus_' -'kurzlebig' -'kurdische' -'kunst_' -'kung_' -'kundig' -'kulturelle' -'kul' -'ktive_' -'ktie' -'ksh' -'kse' -'kräfte_' -'kritisieren_' -'kriti' -'kriminelle' -'krie' -'kreis_' -'krebs_' -'kreative_' -'krankheit' -'kraftwerke_' -'kota' -'kosm' -'korrigiert_' -'korr' -'kopp' -'koordin' -'kooperieren' -'konzipier' -'konzentrierte_' -'konzentr' -'konv' -'kontinu' -'kontakt' -'konsum_' -'konsultier' -'konfigur' -'kompromi' -'komponente' -'komplexer' -'komple' -'kommiss' -'kommene_' -'kommende_' -'kommand' -'komitmen' -'komfort' -'kolo' -'kohärent' -'kna' -'klüg' -'klini' -'kling' -'klick_' -'klein' -'klause' -'kland_' -'kk' -'kipun_' -'king' -'kids_' -'kick' -'keyboards_' -'keyboard_' -'keti' -'kep' -'kennzeichne' -'kenntnis' -'kenne' -'kelo' -'kell' -'kela' -'keitsp' -'kehrten_' -'kehrte_' -'keh' -'keeper' -'ked' -'kd' -'kauft' -'kati' -'katastrophen' -'katastrophalen_' -'katastroph' -'kass' -'kart' -'kapitalistischen_' -'kapitalistisch' -'kapitali' -'kapit' -'kanäle_' -'kanzler_' -'kannte' -'kanische_' -'kane' -'kampagnen_' -'kammer' -'kad' -'kabel' -'kab' -'kW_' -'jüngst_' -'jüdisch' -'jähriger_' -'jähr' -'juta_' -'justizielle' -'justifiable_' -'junior' -'jump_' -'juice' -'judgments_' -'juan' -'jp' -'joy_' -'joy' -'jours_' -'journe' -'journal_' -'jour_' -'jor_' -'jon' -'jm' -'jk_' -'jk' -'jen' -'jemandem_' -'jected_' -'jaz' -'jang' -'jail_' -'jahrzehntelang' -'jahrelange' -'jad' -'jacuzzi_' -'iß' -'izer_' -'iw' -'iver' -'itäre' -'itä' -'itz_' -'ituation' -'itted_' -'itiv' -'itischen_' -'itions' -'itin' -'ithm' -'itglied' -'ited_' -'itas_' -'istische_' -'istans_' -'istan_' -'issen' -'isse_' -'isse' -'issa' -'israel' -'ism' -'ision' -'isier' -'irs' -'irrig' -'irresponsibl' -'irresp' -'irrel' -'irregular' -'iro' -'irku' -'irische_' -'irgendwo_' -'irgendwie_' -'irgendwelche' -'ipt' -'ipp' -'ipl' -'iose' -'ios' -'ionss' -'ionist' -'iologi' -'inz' -'inward_' -'invoice' -'investition' -'investigations_' -'invest' -'inventor' -'invented_' -'invent' -'introduces_' -'inti' -'inters' -'interoperability_' -'interne_' -'internally_' -'interfere' -'interessierte' -'interdi' -'interconnect' -'interactive_' -'intensiver_' -'intensify' -'intellectuals_' -'intell' -'integrative' -'integrati' -'integr' -'int_' -'int' -'insurgen' -'insul' -'instruct' -'institutionelle' -'inste' -'inspirierende' -'inspire' -'inspe' -'insolvenc' -'insight_' -'inser' -'insel' -'inputs_' -'innu' -'innovativer' -'inners' -'innenpolitische_' -'innenpolitisch' -'inne_' -'inm' -'inland_' -'inko' -'injustice_' -'initi' -'iniste' -'inherit' -'inherently_' -'ingt_' -'informi' -'informelle' -'informationen_' -'informa' -'influen' -'infl' -'infizier' -'inferior_' -'infection_' -'infecti' -'inexpe' -'inertia' -'inequalities_' -'inen' -'ineff' -'indung' -'indices_' -'indications_' -'indexe' -'indefinite' -'inda' -'incremental' -'incorporati' -'incor' -'incomplete_' -'incompa' -'incline' -'inciden' -'inbe' -'inau' -'ination_' -'inapp' -'inanzierung' -'inan_' -'inali' -'inacc' -'imstande_' -'impul' -'impu' -'imprisoned_' -'impressi' -'impressed_' -'importi' -'importe' -'implying_' -'implizi' -'impli' -'implant' -'impf' -'imperialist' -'imperial_' -'imperfect_' -'impe' -'immun' -'imminent_' -'immigkeit' -'immerhin_' -'imma' -'imeter' -'imer_' -'iment' -'iman' -'imagination' -'image' -'illustrate' -'illig' -'ilis' -'ilian' -'ilet' -'iles' -'iler' -'ildet_' -'ila_' -'iki_' -'ikan' -'ihood' -'igte_' -'igte' -'igste' -'igert_' -'igenen_' -'igende_' -'igend' -'igat' -'igan' -'ify_' -'ifica' -'ific' -'iffe' -'ifer' -'ießungs' -'ieu' -'iertes_' -'ierte' -'ierli' -'ierenden_' -'ientierte' -'ient_' -'iell' -'iehungs' -'iegende' -'iegen' -'ieferung_' -'iefe' -'idylli' -'idos' -'ido' -'ideologische' -'identifizieren_' -'identi' -'iden_' -'iden' -'icu' -'ickt_' -'ickel' -'ichtliche' -'ichteten_' -'ichtete' -'icherte_' -'icherheit_' -'icherheit' -'ibus' -'ibly_' -'iben' -'ibel' -'ibe' -'iate' -'iar' -'iani' -'iana_' -'iam' -'ially_' -'iale' -'iad' -'höheres_' -'höchstwahrscheinlich_' -'häuser_' -'häufigste' -'härter' -'hysteri' -'hypothes' -'husband_' -'hurt_' -'hunde' -'humiliati' -'humanis' -'humane' -'huk' -'hub_' -'hter' -'hrte' -'hrop' -'hp' -'hov' -'hotele' -'hostilit' -'hostages_' -'hospi' -'horses_' -'horri' -'hormon' -'horizontal' -'horizon' -'honour' -'hones' -'holis' -'holder_' -'hochgradig_' -'hochentwickelte' -'hnya_' -'hns' -'hma' -'hlung_' -'hls' -'hlen' -'hitt' -'historischer_' -'hist' -'hion' -'hinweg' -'hinterla' -'hina' -'hilfe' -'highlights_' -'high' -'hig' -'hieß' -'hie' -'hid' -'hibit' -'hib' -'het_' -'hesitate_' -'hesita' -'herzlichen_' -'hervorgeh' -'hervorge' -'herstellen_' -'herrschende_' -'herrschen_' -'herrsche' -'herrlichen_' -'heroic_' -'hergestell' -'hered' -'heranzu' -'hene_' -'helpful' -'helme' -'hellen' -'helle_' -'helle' -'helicopter_' -'heitlich' -'heilige' -'heikle' -'hegemon' -'heftig' -'heel' -'hectare' -'heblich' -'heat' -'hear' -'headwa' -'haven_' -'hasn' -'harvest' -'harn' -'harmonisch' -'hap' -'hanging_' -'handlung' -'handelte_' -'handelbar' -'haltung_' -'haltig' -'haltestelle' -'halle_' -'half' -'hake_' -'hairdryer' -'hafter_' -'hafte' -'hadi' -'habitacion' -'habita' -'habet' -'güt' -'günstigste' -'günstigen_' -'gültig' -'gé' -'gäste' -'gänzlich_' -'gut' -'gust' -'guise' -'guilty_' -'guiding_' -'gue_' -'guaranteeing_' -'größtmögliche' -'grundlage_' -'großzügige' -'großartigen_' -'grow' -'groundwork_' -'grin' -'grim' -'grid_' -'gres' -'greife' -'greet' -'gray_' -'grave' -'grau' -'gratul' -'grass' -'grants_' -'grande' -'gram' -'grain_' -'graduated_' -'gradual_' -'grac' -'governor_' -'gove' -'gott' -'goldene' -'goa' -'gno' -'gnizing_' -'gnisse' -'gne' -'gnant_' -'gmatis' -'glück' -'glori' -'globe_' -'globalisierte' -'global' -'glied' -'gler' -'gleichg' -'gle_' -'glaubwürdige' -'glanc' -'gl' -'gische' -'gins' -'ginat' -'giganti' -'gift_' -'gift' -'ghts_' -'gha' -'ggf_' -'gger' -'gged' -'gf' -'geänderte' -'gezielt_' -'geze' -'gez' -'gewünschte_' -'gewü' -'gewöhnt_' -'gewährte' -'gewä' -'gewohnt_' -'gewisses_' -'gewerbliche' -'geweiht_' -'gewarnt_' -'gewann_' -'gewandt' -'gewalttätig' -'gewaltsam' -'gewaltigen_' -'gewaltige' -'getreten_' -'getrennt_' -'geteilte' -'geteilt_' -'gesunde_' -'gesucht_' -'gestric' -'gesteuert' -'gestell' -'gestalt' -'gesichert_' -'gesetzlichen_' -'gesetzliche_' -'gesetzlich_' -'geschütz' -'geschu' -'geschränkt' -'geschri' -'geschoss' -'geschn' -'geschmackvoll_' -'geschla' -'geschickt' -'geschichte' -'geschic' -'geschi' -'gescheiterten_' -'gescheitert' -'gerä' -'gert' -'germ' -'geringst' -'geringfügig_' -'geringe' -'gerichteten_' -'gerichte' -'gericht' -'geplante' -'geometri' -'geologi' -'geographic' -'geografisch' -'genügt_' -'gentur' -'generosity_' -'generali' -'genauen_' -'gemischte' -'gemeinschaftliche_' -'geme' -'gelöscht_' -'gelä' -'gelobt_' -'gelingen_' -'gelin' -'geliebt' -'geleistete' -'gelei' -'gelegenen_' -'gelangte' -'geladen_' -'gela' -'gekauft_' -'geka' -'gehörte' -'gehöre' -'gehend' -'geheimnis' -'gehe' -'geh' -'gegl' -'gegenübersteht_' -'gegebenenfalls_' -'gegebene' -'geführte' -'gefühl_' -'gefüg' -'gefälscht' -'gefährdete' -'gefundenen_' -'gefl' -'gefahren_' -'geeinigt' -'geehrter_' -'geda' -'gebun' -'gebro' -'gebnis' -'gebilligt_' -'gebieten_' -'geben' -'gay_' -'gathering' -'gathered_' -'gastronomy_' -'garten_' -'garde_' -'gant_' -'game' -'gamb' -'galow' -'galleries_' -'gale' -'gab' -'fürs' -'fürchtet' -'fünfzehn' -'führend_' -'führ' -'fühle' -'fügt_' -'fügen_' -'förm' -'fälsch' -'fusion_' -'funktions' -'funktional' -'funktion' -'fundamentalism_' -'fulfilled_' -'fulfill_' -'fulfil_' -'fug' -'fueling_' -'fting_' -'ftige' -'frühe' -'frustration' -'fruitful_' -'fruchtbare' -'front' -'froh_' -'frighten' -'frequen' -'fremden' -'freiz' -'freiwillige' -'frameworks_' -'frames_' -'fram' -'fraglich' -'foto' -'fosil_' -'fortunately_' -'fortunate_' -'fortschrittliche' -'fortschritt' -'fortgesetzte' -'fortgesetzt_' -'forsche' -'fors' -'formulation' -'formier' -'formelle_' -'forme' -'forgive' -'foreseeable_' -'foremost_' -'forecast' -'forbidden_' -'foodstuffs_' -'folgender' -'folgend' -'folde' -'flüsse_' -'flüge' -'flächen_' -'flus' -'fluctuations_' -'fluc' -'flow' -'fließ' -'fliehen_' -'flich' -'flex' -'flat' -'flam' -'fk' -'fizierten_' -'fixing_' -'fist' -'fishe' -'fische' -'firmen_' -'finishing_' -'finishes_' -'fing' -'finanzierten_' -'finanzier' -'finanziell_' -'filme' -'fill' -'fies_' -'fid' -'fici' -'fib' -'feuer' -'fett' -'festh' -'festge' -'fester_' -'fertiliz' -'fert' -'fers' -'fernseh' -'ferenz' -'fera' -'feminist' -'felde' -'feindliche_' -'fehlerhafte' -'fehlende_' -'fehl' -'feed' -'fear' -'faz' -'favori' -'favorable_' -'faun' -'fathers_' -'fastest_' -'fashioned_' -'farm' -'fang_' -'fana' -'familien' -'falt' -'fallend' -'fair' -'fahrten_' -'facult' -'faction_' -'faction' -'facet' -'fabricat' -'fabri' -'fa_' -'ezi' -'ezei' -'eze' -'extract_' -'extinction_' -'exte' -'expressions_' -'exportiert' -'exportieren' -'exporters_' -'exported_' -'exponenti' -'explosion' -'exploited_' -'explod' -'explizit' -'explanations_' -'expecting_' -'expectancy_' -'expansive' -'expans' -'expandier' -'exot' -'exklusive_' -'existen' -'exert' -'exer' -'excuse' -'exceeds_' -'exceeded_' -'exceed_' -'examining_' -'examination' -'ewicht' -'evolved_' -'evolve_' -'eviden' -'evaluated_' -'eva_' -'ev_' -'eut_' -'eus' -'europä' -'etung_' -'ets' -'etri' -'etliche' -'ethni' -'ethisch' -'ethics_' -'etet' -'estan' -'establishes_' -'establ' -'esst' -'essourcen_' -'essors_' -'espe' -'esp' -'esisch' -'esie' -'esh_' -'esar' -'esa_' -'esa' -'erzählt_' -'erzwingen_' -'erzw' -'erzig' -'erzielten_' -'erzieh' -'erzi' -'erzeugung_' -'erworben' -'erwirt' -'erweckt_' -'erwach' -'eruf' -'eru' -'ertr' -'erti' -'erteilt_' -'erteilen_' -'ersönlichkeit' -'ersto' -'erstmalig' -'ersorgung_' -'erschütter' -'erschweren_' -'erri' -'erreg' -'ero_' -'ernähr' -'ernsten_' -'erneuten_' -'ermöglichte' -'ermutigend' -'ermordet' -'ermittelt' -'ermitt' -'ermaßen_' -'erläutert' -'erlich' -'erkrank' -'erklärten_' -'erische' -'erin_' -'erhol' -'erho' -'erhebt_' -'erhaltene' -'ergreif' -'erfuhr' -'erforsch' -'erfasst_' -'erfassen_' -'eres_' -'erer' -'erenz' -'erend_' -'eren' -'ereit' -'ereignis' -'erbracht_' -'erate' -'erals' -'eradicati' -'erac' -'eption' -'epl' -'eou' -'envisage' -'environments_' -'entzi' -'entwickelnde' -'entwick' -'entum' -'entsp' -'entschä' -'entschl' -'ents' -'entrepreneurs_' -'entrepreneurial_' -'entrepr' -'entra' -'ento' -'entn' -'entla' -'entko' -'entitlement' -'ention' -'entia' -'enthusiastic' -'enthusiast' -'enthielt_' -'entg' -'entfern' -'entfalten_' -'enter' -'enteil' -'entdeckte' -'entailed_' -'entag' -'ensw' -'enrichment_' -'enpr' -'enormer_' -'ennen_' -'enne' -'enna_' -'enlarged_' -'enke' -'enische' -'enie' -'enhancing_' -'enhancement' -'engst' -'engines_' -'engere_' -'engagiert_' -'engag' -'energies' -'enerati' -'endors' -'endlos' -'endig' -'endg' -'encr' -'encounter' -'enchant' -'enberg_' -'enau' -'enan' -'enade' -'emulat' -'empör' -'empt' -'emphasized_' -'emphasises_' -'empfohlen_' -'empfinden' -'empfan' -'emitte' -'emit_' -'emis' -'eminent' -'emic' -'emergen' -'ementa' -'embraced_' -'embodie' -'emancipation_' -'elz' -'elu' -'elte_' -'elpr' -'elnde' -'elm' -'ellit' -'ellig' -'elimin' -'elektronische_' -'elektroni' -'eleg' -'electorate_' -'elderly_' -'ela_' -'ektors_' -'eiz' -'eiv' -'eise' -'eis' -'einzubringen_' -'einziger_' -'einzigartiger_' -'einzelstaatliche_' -'einzelstaatliche' -'einsetzt_' -'einschließ' -'einnimmt_' -'einn' -'einm' -'einleite' -'einlage' -'einka' -'einig_' -'einheitlich' -'einheit_' -'einheimische_' -'einhalten_' -'eingreif' -'eingesetzte' -'eingereichten_' -'einger' -'eingehende' -'eingefü' -'eingebrachte' -'einführt' -'einfließen' -'einfache' -'eindr' -'eilnehmer' -'eilig' -'eigt' -'eifen_' -'eichnungen_' -'eichnete' -'eichheit' -'eichen_' -'eiche' -'eibungen_' -'ehrliche' -'ehren' -'ehmend' -'ehm' -'ehemaliger_' -'egu' -'egr' -'egio' -'efficiently_' -'effi' -'effektive' -'eein' -'eed' -'educat' -'edoni' -'edly_' -'editorial_' -'editi' -'edingungen_' -'ediate_' -'ectiv' -'ect_' -'ecosystem_' -'economic' -'ecommerce_' -'eckung' -'echter_' -'echnologi' -'echni' -'ece' -'ec_' -'ebi' -'earthquake_' -'eare' -'eagle' -'ead' -'eab' -'eB' -'düstere_' -'dürft' -'dünne' -'dün' -'dó' -'dí' -'dè' -'dänische' -'dämpf' -'dynamisch_' -'dyn' -'dying_' -'durchgeführten_' -'durchführ' -'durable_' -'dura' -'duplicate' -'dule' -'dubio' -'dual_' -'dsch' -'drückt' -'drücke' -'drängt_' -'drink' -'dringender_' -'drige' -'dress_' -'dreim' -'dreie' -'drea' -'dran_' -'dramatische' -'drama_' -'drai' -'drafts' -'dozens_' -'dozen_' -'downs' -'downfall_' -'douche' -'doubl' -'dort' -'doppelten_' -'door' -'doom' -'dominieren_' -'dominance_' -'dom_' -'dokument_' -'doct' -'dock' -'doc' -'dne' -'dlin' -'divide' -'divert' -'diversification_' -'diversif' -'diver' -'dity' -'distributi' -'distinguish' -'distinctive_' -'distin' -'distanzier' -'distan' -'dissi' -'disqualif' -'disproportionate' -'disposition' -'displaced_' -'dispens' -'disparities_' -'disorders_' -'disor' -'diskriminier' -'diskreditier' -'disintegrati' -'discover' -'discontent' -'discl' -'discard' -'disar' -'disappoint' -'disappeared_' -'disadvantage_' -'disabl' -'dirty_' -'diri' -'direkter_' -'directors_' -'dir' -'diplomati' -'diplom' -'dingung' -'dinar' -'dimensionale' -'dimension' -'dilakukan_' -'diktat' -'dika' -'digte' -'diffus' -'differentiated_' -'differ_' -'diesjährigen_' -'diesel_' -'diesbezüglich_' -'diente_' -'dienstleistung' -'dictator_' -'dictator' -'dicta' -'dich' -'dice' -'dicat' -'dibandingkan_' -'dial_' -'diagnostizier' -'diagnosis_' -'dezentral' -'devise_' -'develop' -'devastat' -'deva' -'dev_' -'deutschs' -'deutliche' -'deutig' -'deterrence_' -'determina' -'detect_' -'detaine' -'destroying_' -'destotrotz_' -'destiny_' -'desti' -'destabilisieren' -'dest' -'desse' -'desperate_' -'desk' -'designi' -'designe' -'desert_' -'desert' -'descript' -'describing_' -'desc' -'derte' -'derse' -'derin' -'deput' -'depri' -'depressive' -'deplor' -'depict' -'depe' -'dent_' -'denkbar' -'demselben_' -'demonstrati' -'demoli' -'demokratische' -'demografische' -'democratiz' -'democratically_' -'demis' -'demics_' -'demi_' -'demagogue' -'dell' -'delight' -'delic' -'deleveraging_' -'deleted_' -'delet' -'deleg' -'delay' -'dei' -'deg' -'defizite' -'definite_' -'definierten_' -'deficien' -'defenses_' -'dedicati' -'decree' -'decoration_' -'decisively_' -'decen' -'dece' -'deca' -'debu' -'debat' -'deba' -'dealers' -'deaktiviert' -'deadlock_' -'daya_' -'day' -'davon' -'daughter_' -'dauern_' -'dauerhaft_' -'dauer_' -'datei_' -'dat_' -'darl' -'dark' -'darge' -'dank' -'dangerously_' -'dane' -'dance' -'dairy_' -'dai' -'dage' -'dae' -'dachte_' -'custom' -'curi' -'cup_' -'culos' -'culi' -'culati' -'cui' -'cture_' -'ctor_' -'cto' -'cteri' -'cruel_' -'crop_' -'crop' -'critics_' -'criticise_' -'cription' -'cred' -'creativity_' -'crea' -'cram' -'crafted_' -'covert' -'coverage_' -'courte' -'court' -'courag' -'counting_' -'counterpart' -'cosmo' -'corrupt' -'correspond' -'coordinate_' -'cool' -'cooking_' -'cookies_' -'convinc' -'conviction_' -'convey_' -'controvers' -'contro' -'contributes_' -'contraction' -'continuity_' -'continual' -'continental_' -'conti' -'contempt_' -'contemplate' -'container' -'consum' -'consultant' -'consolidate' -'console' -'consig' -'conservat' -'conscien' -'congratulat' -'confirms_' -'confine' -'conducti' -'condo' -'conditi' -'conceptual' -'concepts_' -'concentrating_' -'conceive' -'conceivable_' -'compromises_' -'comprise_' -'composition_' -'compos' -'complimentary_' -'competitors_' -'compet' -'communicati' -'commission_' -'commi' -'commerce' -'commentator' -'comitology_' -'combination' -'combin' -'colleg' -'collateral_' -'collaps' -'colla' -'coins_' -'cof' -'coexist' -'codec' -'cod_' -'cock_' -'clou' -'cloning_' -'cliente' -'clear' -'classification_' -'classif' -'clash_' -'clarifi' -'ckung_' -'ckten_' -'ckr' -'ckne' -'ckier' -'civiliz' -'civilisation' -'civ' -'circulation_' -'circula' -'circuit_' -'cip' -'ciona' -'ciation' -'ciar' -'chwor' -'chwi' -'chwe' -'chut' -'chuss' -'chur' -'chunk' -'chuh' -'chtliche' -'chtet_' -'chtern' -'chslung' -'chrono' -'chronis' -'chronic_' -'christ' -'chriften_' -'chreib' -'chooses_' -'choc' -'chnik' -'chni' -'chn' -'chme' -'chläge_' -'chli' -'chke' -'chisc' -'chip_' -'chinesischer_' -'chine' -'china_' -'chier' -'chicken_' -'chess' -'cherung' -'chere' -'chemis' -'chem_' -'charta_' -'characteris' -'chara' -'chanis' -'champions_' -'chambres_' -'chamber' -'cham' -'chain' -'chaff' -'ceu' -'cet_' -'certification' -'certificates_' -'certificate_' -'cents_' -'centrist_' -'centraliz' -'cent' -'cen_' -'cema' -'celebrati' -'celebrat' -'cele' -'ceiling_' -'cattle_' -'cations_' -'cater' -'catching_' -'catas' -'carte_' -'caro' -'cardi' -'cape' -'capacities_' -'cane_' -'campaign' -'calme_' -'calibration' -'cache_' -'bürger_' -'bünd' -'bücher' -'bü' -'byte_' -'byp' -'buyers_' -'bustl' -'burst' -'burgh' -'burg' -'bure' -'bundes' -'build' -'bug' -'buf' -'buen' -'bten' -'bte_' -'bsc' -'brut' -'brus' -'bruch' -'browse' -'brow' -'britischer_' -'bringen' -'brig' -'brew' -'brethren_' -'breiteren_' -'brechen' -'brav' -'brasilianische_' -'bras' -'brands_' -'bourne_' -'boundary_' -'boss' -'bos' -'borrowed_' -'bonus_' -'bomb' -'bodi' -'boden_' -'bnis' -'blü' -'blur' -'blood' -'blogs_' -'blogg' -'blockier' -'blin' -'bled_' -'blaue' -'blatt_' -'blasen' -'bisc' -'birthday_' -'birth' -'bird_' -'biom' -'biological_' -'biog' -'biofuel' -'bindung_' -'binary_' -'bilitati' -'bilis' -'bildete' -'bilder_' -'bilanz_' -'bil_' -'bike' -'biete_' -'bid' -'bibliot' -'bias_' -'bia_' -'bezwe' -'bezog' -'bezieh' -'bewunder' -'bewohne' -'bewirt' -'bewi' -'bewerbe' -'bewege' -'bewe' -'bewahr' -'bewaffnet' -'beur' -'beunruhigende' -'betriebs' -'betriebene' -'betr' -'betonte' -'beteiligten_' -'beteiligen_' -'beta' -'bestände_' -'bestr' -'besto' -'bestimmtes_' -'besti' -'bestens_' -'bestellt' -'bestellen_' -'besser' -'besorgniserregende' -'besie' -'besetzt_' -'beschriebenen_' -'beschrieb_' -'beschr' -'beschlossene' -'beschloss_' -'bescheiden' -'besche' -'besa' -'berühren_' -'beruhte_' -'beruhigen' -'berke' -'berichtete_' -'berh' -'bereits' -'bereichen_' -'berater' -'berada_' -'beobachtet_' -'benign_' -'bende' -'benar_' -'bemüh' -'belt_' -'belt' -'belongs_' -'belohn' -'belo' -'beln_' -'beliebt_' -'belegen_' -'beleben' -'bekunde' -'bekomm' -'beklagen_' -'bekl' -'beinhalten_' -'beider_' -'beherrscht_' -'behave' -'behauptete_' -'beharrt' -'begrüß' -'begrenzter' -'begrenzen_' -'beginn' -'begi' -'begegnet' -'befürworten_' -'befürworte_' -'befürchten_' -'befürchte' -'befu' -'befo' -'befindlichen_' -'befindliche' -'beeindruckt' -'beeindruckenden_' -'bedingt_' -'bedeutsam' -'bedeutender' -'bedeutend_' -'bedauere_' -'bedank' -'beauf' -'bearbeitung' -'bearbeitet' -'bear' -'beant' -'beans' -'beachtliche' -'beachte' -'beabsichtig' -'bbe' -'battles_' -'baths_' -'basierte' -'basel' -'baru' -'barre' -'barr' -'baro' -'barbe' -'barbari' -'bant' -'bans_' -'banker' -'bani' -'bana' -'bakar_' -'baj' -'baggage_' -'baden_' -'bacteria' -'backup_' -'backpack' -'backlash_' -'až_' -'año' -'axis_' -'ax' -'awards_' -'await' -'awa_' -'avoid' -'außergewöhnliche_' -'außerge' -'außenst' -'autoritäre' -'automatisier' -'automatischen_' -'authorita' -'authorisation_' -'authentic_' -'ausüb' -'auszusp' -'auszugeben_' -'auswirkt_' -'ausweiten' -'ausweis' -'auswa' -'austausch' -'aust' -'aussp' -'aussichten_' -'aussa' -'ausrüstung' -'ausrichte' -'ausreicht_' -'auslöste' -'ausländischer_' -'ausle' -'ausla' -'ausgewählte_' -'ausgewogen_' -'ausgewertet_' -'ausgetragen_' -'ausgerichteten_' -'ausgenommen_' -'ausgeglichene' -'ausgegli' -'ausgefü' -'ausgebe' -'ausgearbeitet' -'ausgab' -'ausfäll' -'auseinandersetz' -'ausdrückliche' -'ausdrück' -'ausar' -'aur' -'augen' -'aufzut' -'aufzust' -'aufzub' -'aufwer' -'auftrag' -'aufsch' -'aufs_' -'aufr' -'aufnahme_' -'aufkommende' -'aufk' -'aufhalten' -'aufgreif' -'aufgeworfen' -'aufgewe' -'aufgetr' -'aufgeben_' -'aufeinander_' -'auern_' -'audi' -'auction' -'auch' -'auben' -'atu' -'attraktiver_' -'attraktiv' -'attracted_' -'attacker' -'atrocities_' -'aton' -'atom' -'atmospher' -'ative' -'atische' -'atisch_' -'ationsa' -'athlet' -'ater_' -'aten' -'atel' -'atastrophe' -'assured_' -'assur' -'assu' -'associate' -'assert_' -'asse' -'assault' -'assassination' -'asis' -'asin' -'asce' -'artung' -'artist' -'artis' -'artikel_' -'artig' -'arriving_' -'arrange' -'arra' -'arom' -'armies_' -'arl' -'arke' -'aris_' -'arien_' -'arians_' -'argument' -'arguabl' -'arf_' -'arena_' -'ardin' -'archäologische' -'archiv' -'architektonische' -'archipelago' -'archi' -'arbeitung_' -'arbeitslos_' -'arbeitete' -'arabisch' -'appropriati' -'appropriate' -'appreciat' -'applicants_' -'applaud' -'appelliere_' -'apli' -'ao_' -'anzub' -'anzeig' -'anybody_' -'anwendungen_' -'antwortlich' -'antrags_' -'antivirus_' -'antim' -'antiken_' -'anticipate_' -'antibioti' -'anter' -'antag' -'anstrebt' -'ansieht_' -'anschließende' -'anschaue' -'anpassen_' -'annähernd_' -'anktionen_' -'ankl' -'anken_' -'ank_' -'anj' -'angriffe' -'angig' -'angga' -'angetrieben' -'angesp' -'angese' -'angesch' -'angesammelt' -'angenomme' -'angenehme_' -'angemessener' -'angeme' -'angehoben_' -'anga' -'anfü' -'anfängt_' -'anforderung' -'anesische' -'anerkenn' -'aner_' -'andin' -'andi' -'andeu' -'anchor' -'ance' -'analyti' -'analysing_' -'analy' -'analog_' -'anak_' -'ample_' -'amid' -'amending_' -'ambiguous_' -'ambient' -'ambience_' -'amba' -'altr' -'altet_' -'alternative' -'altern' -'alm' -'allo_' -'allmählich' -'alljährlich' -'alli' -'alleine_' -'allegedly_' -'alk' -'alive_' -'alität' -'alition' -'alisieren_' -'alin' -'align_' -'align' -'alienation_' -'alien_' -'alan_' -'aktualisiert' -'aktu' -'aktivitäten_' -'aktive' -'aktion_' -'akti' -'airspace_' -'aire' -'aikan' -'aid' -'ahlung' -'agung_' -'agt_' -'agrees_' -'agierende' -'aggebe' -'agendas_' -'agein' -'agar' -'aftermath_' -'afor' -'afghanischen_' -'afghanische_' -'affen_' -'affair_' -'aerospace_' -'advertise' -'adversaries_' -'adver' -'admissi' -'admira' -'administrator_' -'administrativ' -'administer' -'admin_' -'adjusting_' -'adic' -'ader' -'adan' -'activis' -'activ' -'acquis_' -'acquiring_' -'acqui' -'achtung' -'achte' -'acht' -'ached_' -'accueil_' -'accomodati' -'accommodate_' -'acco' -'accessibility_' -'accede' -'abzuw' -'abzus' -'abzielen_' -'abwechs' -'abundan' -'abstra' -'absorbi' -'absorb_' -'absol' -'absi' -'absent_' -'abschrecken' -'abort' -'abolition_' -'abolished_' -'abnehmen' -'abilität' -'abilities_' -'abili' -'abide_' -'abhalten_' -'abgeschl' -'abgescha' -'abgesch' -'aben' -'abduction' -'aban' -'aat_' -']]. _' -']] ' -'[ _' -'[' -'Zürich_' -'Zü' -'Zyp' -'Zyklus_' -'Zyklen_' -'Zwe' -'Zw' -'Zuwa' -'Zuw' -'Zuverlässigkeit' -'Zutaten_' -'Zuständ' -'Zusch' -'Zusammenschluss_' -'Zusammenhänge' -'Zusammen_' -'Zurückh' -'Zula' -'Zuhöre' -'Zuhause_' -'Zugeh' -'Zufriedenheit_' -'Zuflu' -'Zivilisation_' -'Zinssatz' -'Zin' -'Zimmerman' -'Zielvorgabe' -'Zeug' -'Zentren_' -'Zensur' -'Zem' -'Zellen' -'Zeitungs' -'Zeits' -'Zeitlinie_' -'Zeitge' -'Zahlungsv' -'Zahlungsaus' -'Zah' -'Yor' -'Yale_' -'YA' -'XWB_' -'XT' -'XII' -'XF' -'X1' -'Wünsch' -'Wäldern_' -'Wähler' -'Wähl' -'Wut' -'Wunde' -'Works_' -'Wolfs' -'Wolfgang_' -'Wolfensohn_' -'Wohnungen_' -'Wohnung_' -'Wohnb' -'Wohlstands_' -'Wohlergehen' -'Wohlbefinden_' -'Wochen' -'Wirtschaftsn' -'Wirtschaftsg' -'Wirtschaftsbe' -'Wirts' -'Wirks' -'Wire' -'Wirbelst' -'Wins' -'Wing' -'Winde' -'Wind_' -'Willens' -'Wille_' -'Wilders_' -'Wiedervereinigung' -'Wiederholung_' -'Wiederh' -'Wiederaufnahme_' -'Wieder_' -'Widget' -'Wichtiger' -'Wic' -'Whirlpool_' -'Whereas_' -'Whe' -'Wetter' -'Wettbewerbsvor' -'Wettbewerbsver' -'Wettbewerbspolitik_' -'Westeuropa_' -'Wesentliche' -'Wertschöpfung' -'Werts' -'Wert' -'Weltr' -'Weltor' -'Weltme' -'Weltkultur' -'Weltkrieges_' -'Welthandels' -'Weltg' -'Weltbevölkerung_' -'Weiße_' -'Weish' -'Weine_' -'Weile' -'Weigerung_' -'Wednesday_' -'Wechsel' -'Webserver' -'Way' -'Wasserkraft' -'Washing' -'Wanderung' -'Wallström_' -'Waldbrände' -'Wald_' -'Wake' -'Wahler' -'Wahlbe' -'Wagen_' -'Waffenstillstand_' -'Wachstumsraten_' -'Wachstumsrate_' -'WT' -'WS_' -'WM_' -'WEI' -'WAV_' -'Völkern_' -'Völkermord' -'Vö' -'Vé' -'Vä' -'Vá' -'Vulcan_' -'Vul' -'Vot' -'Vos' -'Vorstands' -'Vorsta' -'Vorst' -'Vorsitzende' -'Vorliebe' -'Vorla' -'Vorhersage' -'Vorgang' -'Vorgabe' -'Vorfall_' -'Vorder' -'Volvo_' -'Vollm' -'Volkswagen_' -'Volkspartei_' -'Volksabstimmung_' -'Vladimir_' -'Vizepräsident_' -'Vize' -'Vitamin' -'Virginia' -'Viol' -'Vik' -'Viewer_' -'View' -'Viertel' -'Vielf' -'Via' -'Verzögerung_' -'Verzweiflung' -'Verzug' -'Verwer' -'Verweise' -'Verw' -'Verträgen_' -'Vertrauens_' -'Vertragsver' -'Verteil' -'Verteidiger' -'Versäum' -'Verste' -'Versp' -'Verschwörung' -'Verschwi' -'Verschwendung_' -'Verschuld' -'Verschlechterung' -'Verschiedene_' -'Verschieb' -'Vers' -'Verpa' -'Verp' -'Vero' -'Vernichtung_' -'Verne' -'Vermeidung_' -'Verlaufe_' -'Verlangsamung_' -'Verkäufe' -'Verkehrst' -'Verkehrss' -'Verhandlung_' -'Vergleichss' -'Vergew' -'Verfassungsentwurf_' -'Verfall' -'Vere' -'Verbü' -'Verbr' -'Verbe' -'Verantwortlichen_' -'Veranstaltungs' -'Veran' -'Venus_' -'Vent' -'Venezia' -'Vec' -'Vaters_' -'Variablen_' -'VS' -'VIN' -'VA_' -'Uti' -'Urteile' -'Ursprünge_' -'Urlaube' -'Uribe_' -'Urheberrechts' -'Urbanis' -'Upgrade_' -'Unterwa' -'Untert' -'Unterschrift' -'Untersch' -'Unternehmensf' -'Unterl' -'Unterhaltung' -'Unterha' -'Unterbrechung' -'Untera' -'Unsinn_' -'Unrecht_' -'Unix_' -'Universitäts' -'Universal' -'Unit_' -'Unglücklicherweise_' -'Unglück_' -'Ungleichgewicht' -'Ungere' -'Une' -'Umweltver' -'Umweltschutz' -'Umwelts' -'Umweltpr' -'Umweltpolitik_' -'Umverteilung' -'Umstellung_' -'Umfragen_' -'UV_' -'URL' -'UNICEF_' -'Türke' -'Tyrol' -'Tyran' -'Tyr' -'Type_' -'Tusk' -'Turnier_' -'Tunis' -'Tunes_' -'Tsunami_' -'Tschad_' -'Träger_' -'Truste' -'Trojan' -'Trichet_' -'Trend' -'Tren' -'Treibhausgasemissionen_' -'Treibhausgas' -'Treasury_' -'Travel' -'Trau' -'Trap' -'Transit_' -'Transform' -'Transaktionen_' -'Trans_' -'Trainer_' -'Tow' -'Tours_' -'Tourist' -'Tool' -'Too' -'Tole' -'Tisch' -'Timor' -'Timo' -'Til' -'Tiger' -'Tiere' -'Tiera' -'Tic' -'Think' -'Thing' -'Therme' -'Therap' -'Ther' -'Theorien_' -'Theodore_' -'Thatcher_' -'Texte' -'Terrorismusbekämpfung_' -'Terroranschl' -'Territorium_' -'Terr' -'Terms_' -'Tendenzen_' -'Temperatur_' -'Temperatur' -'Televis' -'Telekommunikation_' -'Teilung' -'Teilnehmer' -'Technologies_' -'Technis' -'Technical_' -'Tea_' -'Taylor_' -'Tax_' -'Tax' -'Tatatabot_' -'Taste_' -'Tasche' -'Target_' -'Tale' -'Tagung' -'TZ' -'TW' -'TU_' -'TRO' -'TPP_' -'TEN' -'TA_' -'T2' -'Südtirol_' -'Südoste' -'Südostasien' -'Süd_' -'Säug' -'Sän' -'São_' -'Sz' -'Syriza_' -'Syndrom' -'Swim' -'Sustain' -'Surve' -'Surely_' -'Suprem' -'Sup' -'Sunnis_' -'Sunn' -'Summers_' -'Summer_' -'Summen_' -'Summ' -'Suf' -'Suda' -'Substantiv' -'Subsi' -'Subscri' -'Subs' -'Subm' -'Subjekt' -'Stücke_' -'Störun' -'Stärken_' -'Stuf' -'Studi' -'Ström' -'Stric' -'Strau' -'Strategic_' -'Strasse' -'Strafv' -'Stornierung' -'Stop' -'Stone' -'Stock' -'Stimmen' -'Stillstand_' -'Stie' -'Steven' -'Steuerh' -'Steuerer' -'Steuereinnahmen_' -'Stern' -'Stehende_' -'Stefan_' -'Stea' -'Statute_' -'Standpunkten_' -'Standpunkte_' -'Stammzellen' -'Stahl_' -'Stadtk' -'Stadi' -'Stabilitätspakt_' -'Staatsp' -'Staatsbürgerschaft_' -'Sprung' -'Sprin' -'Split' -'Spitzenpo' -'Spit' -'Spirit' -'Spezifikation' -'Spezialist' -'Spend' -'Spen' -'Spektrum' -'Speise' -'Speicherka' -'Spaß_' -'Spazier' -'Sparpolitik_' -'Sparmaßnahmen_' -'Sparen' -'Spam' -'Spalte' -'Sozialp' -'Sozialisten_' -'Sozialismus_' -'Sozialdemokraten_' -'Sowjet' -'Sonnenunterg' -'Sonderg' -'Sommers' -'Somalia_' -'Soft' -'Society_' -'Snowboard' -'Slu' -'Slovenia_' -'Sle' -'Skl' -'Skandal' -'Sitzungsperiode_' -'Sitz' -'Simon_' -'Silber' -'Siena_' -'Siedl' -'Sieben' -'Side' -'Sicherheitsbe' -'Sicherheit' -'Shut' -'Shows' -'Shop' -'Shinzo' -'Shell_' -'Shel' -'Sharia_' -'Sex' -'Seven_' -'Ses' -'Serv' -'Senkaku_' -'Senior' -'Senegal_' -'Sendung_' -'Semit' -'Self' -'Selbstz' -'Selbstvertrauen_' -'Selbstmord' -'Seiten' -'Seit' -'Sehen' -'Seeverkehr' -'Sechste' -'Sech' -'Seattle_' -'Seas' -'Screening' -'Schüt' -'Schönheit' -'Schön' -'Schö' -'Schwächung' -'Schwächen_' -'Schwinde' -'Schwimmbad_' -'Schwie' -'Schwester_' -'Schwellenmärkte' -'Schweigen_' -'Schwe' -'Schwarze' -'Schwachstelle' -'Schutzge' -'Schuss' -'Schuman' -'Schulter' -'Schuldenlast' -'Schuhputzmaschine_' -'Schröder' -'Schritten_' -'Schrei' -'School' -'Schock_' -'Schock' -'Schnitt' -'Schloss' -'Schlo' -'Schlecht' -'Schlacht' -'Schla' -'Schil' -'Schiene_' -'Schied' -'Schein' -'Schauspiele' -'Schatten' -'Schaff' -'Schaf' -'Schadens' -'Schad' -'Saš' -'Savo' -'Savi' -'Sauberkeit_' -'Sauber' -'Sard' -'Saraj' -'Sarah_' -'Santo' -'Sandstr' -'Sand_' -'Samo' -'Sammlungen_' -'Samb' -'Salva' -'Sali' -'Sak' -'Saint' -'Sahara_' -'Sachverhalte' -'Saal_' -'SSI' -'SPE' -'SOEs_' -'SMEs_' -'SL_' -'SIS_' -'SING' -'SDL_' -'SB' -'Rücküberweisung' -'Rücktritt_' -'Rückst' -'Rückhalt_' -'Rückführung_' -'Ryan_' -'Ry' -'Rural_' -'Run_' -'Run' -'Rum' -'Ruine' -'Ruhes' -'Rue' -'Roy' -'Row' -'Rotterdam_' -'Rotarier' -'Rost' -'Ros' -'Root' -'Roo' -'Ron_' -'Ron' -'Rohstoffpreise' -'Rohstoffe_' -'Robot' -'Robin' -'Risikobe' -'Rio' -'Rim' -'Richter' -'Rib' -'Rhythm' -'Rho' -'Rhin' -'Reze' -'Revi' -'Reve' -'Respons' -'Residen' -'Reservierung' -'Reservation' -'Republikan' -'Repräsentanten_' -'Repräsentant' -'Repression' -'Renzi_' -'Renten_' -'Rente_' -'Religions' -'Relevanz_' -'Relation' -'Reisez' -'Reisever' -'Reisetipp_' -'Reis' -'Reha' -'Register' -'Regierungspo' -'Regarding_' -'Refu' -'Reden_' -'Reco' -'Rechtssysteme' -'Rechtssystem' -'Rechtsst' -'Rechtssicherheit_' -'Rechtsr' -'Rechtschreib' -'Rechtsbe' -'Rechtsausschuss' -'Rechtsanw' -'Recherche' -'Rechenschaftspflicht_' -'Recep' -'Rebellion_' -'Realit' -'Realisierung_' -'Reading' -'Reac' -'Raums_' -'Raume' -'Rauchen_' -'Ration' -'Rating_' -'Ratifi' -'Rathaus' -'Rapid_' -'Rangliste' -'Ral' -'Raketen_' -'Rail_' -'Raci' -'RT_' -'RK' -'RANT' -'RAM_' -'RAC' -'Quoten_' -'Quellcode_' -'Quar' -'Quanti' -'Quant' -'Qualifikationen_' -'Qual' -'Quadrat' -'Qu' -'Qatar_' -'Qaddafi_' -'Qa' -'QUI' -'Pv' -'Push' -'Pump' -'Pull' -'Pub' -'Psycho' -'Psych' -'Präzisi' -'Präsidentschaftswahlen_' -'Präsentation' -'Prämien_' -'Präf' -'Provinz' -'Provider_' -'Prototyp' -'Proto' -'Proteine_' -'Protein' -'Protect' -'Propo' -'Propaganda_' -'Promenade_' -'Programmier' -'Profit_' -'Profil_' -'Professional_' -'Produkts' -'Produktionsst' -'Produktionspr' -'Product' -'Proc' -'Problemati' -'Privileg_' -'Privats' -'Privatis' -'Prior_' -'Priester' -'Prev' -'Prepa' -'Premierminister' -'Preisstabilität_' -'Preises_' -'Prakti' -'Prag' -'PowerP' -'Potter' -'Potsdam' -'Poten' -'Postgre' -'Postdienst' -'Posse' -'Position' -'Portugies' -'Portale' -'Porta' -'Population_' -'Pon' -'Politicians_' -'Pola' -'Plo' -'Plenar' -'Play_' -'Plata_' -'Plast' -'Planet_' -'Pizza' -'Pino' -'Pilote' -'Pilot' -'Pier' -'Pick' -'Picc' -'Pic' -'Photocopying_' -'Phoeni' -'Philippi' -'Pharma' -'Pflanz' -'Pfl' -'Pfeiler_' -'Pfad_' -'Peters' -'Perspective_' -'Persi' -'Perm' -'Perfe' -'Pension_' -'Pennsylvania_' -'Pend' -'Pemb' -'Pedro' -'Peak' -'Pax_' -'Paus' -'Pati' -'Patent_' -'Passw' -'Passport_' -'Passei' -'Passe' -'Pass_' -'Partner' -'Partic' -'Parti' -'Parm' -'Parlamentarier_' -'Parkplatz_' -'Parke' -'Paris' -'Parc' -'Paramet' -'Parallelen_' -'Parallel_' -'Paradox' -'Paolo_' -'Pani' -'Pand' -'Pan_' -'Palästinensischen_' -'Pakets_' -'Paint' -'Packag' -'PROGR' -'POL' -'PN' -'PLAYER' -'PIC' -'PAS' -'Oxford_' -'Ow' -'Outlook_' -'Ostse' -'Oster' -'Osborne_' -'Ortho' -'Ortega' -'Organisat' -'Organen_' -'Ordn' -'Order' -'Orden_' -'Optimi' -'Opportuni' -'Opfern_' -'Operation' -'Omni' -'Offi' -'Oe' -'Obersten_' -'Oberste_' -'Oberh' -'Obergrenze_' -'ORT' -'OM_' -'OFI' -'Nöt' -'Nö' -'Nutzungsbedingungen_' -'Nutze' -'Num' -'Now' -'Nove' -'Notwendig' -'Notfall' -'North' -'Normali' -'Nordi' -'Nomin' -'Nomad_' -'Nobody_' -'Nis' -'Nina_' -'Niko' -'Night_' -'Night' -'Niederla' -'Niedergang_' -'Nico' -'Nick' -'Nichte' -'Newsletter_' -'Nevada_' -'Nev' -'Neustart' -'Neuschwanstein_' -'Neuf' -'Neues_' -'Neuen' -'Neuausrichtung_' -'Nerv' -'Ner' -'Neo' -'Neighbo' -'Nego' -'Neb' -'Neapel_' -'Nazi' -'Naturwissenschaft' -'Naturpark' -'Naturk' -'Natural' -'Nationalst' -'Nationalpark_' -'Nationalpar' -'Namib' -'Nam' -'Nak' -'Nahrungs' -'Nahost_' -'Nahe' -'Nachw' -'Nachmittag_' -'Nachhaltige' -'Nachbarschaftspolitik_' -'Nachbarschaft_' -'Nachbarländer' -'Nachbar' -'NY' -'NIC' -'NG_' -'NGO_' -'NEC' -'ND_' -'NDE' -'Mütter_' -'Mün' -'Mönch' -'Möglicherweise_' -'Mythos_' -'MySpace_' -'Mutter' -'Muslim' -'Musical' -'Museums_' -'Museen_' -'Muse' -'Murdoch_' -'Mull' -'Mozart' -'Moz' -'Movielearn_' -'Movie_' -'Mosle' -'Moses_' -'Mosambik_' -'Mord' -'Mora' -'Montreal_' -'Monterrey_' -'Montai' -'Montag' -'Monster_' -'Mons' -'Molda' -'Modi_' -'Moderne_' -'Moder' -'Model' -'Moda' -'Mobiltelefon_' -'Mobilität_' -'Mittleren_' -'Mittelwe' -'Mitteleurop' -'Mitteil' -'Mitleid' -'Mitbe' -'Mitarbeiter' -'Mist' -'Missverständnis' -'Misstrauen_' -'Mission' -'Missb' -'Mira' -'Mir' -'Mins' -'Ministerrat_' -'Ministerpräsidenten_' -'Min_' -'Millions' -'Millia' -'Militära' -'Milchprodukt' -'Milch' -'Mikrof' -'Mike_' -'Metropoli' -'Metalle' -'Metal' -'Messa' -'Mercedes_' -'Menü' -'Menschenrechtsko' -'Mena' -'Memory_' -'Memori' -'Meli' -'Meister' -'Mein' -'Mehrzahl_' -'Mehrwertsteuer' -'Meeresf' -'Meere' -'Medina' -'Mechani' -'McK' -'McG' -'Maßstäbe' -'Maxi' -'Max_' -'Mauri' -'Mauer' -'Massenm' -'Massaker' -'Marí' -'Marta_' -'Mars_' -'Marr' -'Marqu' -'Marktzug' -'Marktt' -'Market' -'Mare' -'Mara' -'Malware_' -'Mali_' -'Maje' -'Mainstream' -'Main_' -'Mailand_' -'Mahlzeiten_' -'Magst_' -'Magazin_' -'Magazin' -'Machtver' -'Machts' -'MX' -'MT_' -'MS' -'MP4_' -'MOV_' -'MOS' -'MIN' -'MIDI_' -'MG' -'MENT' -'MB' -'MAT' -'MAN_' -'MAN' -'M4' -'Lüg' -'Lücke_' -'Lä' -'Luz' -'Lup' -'Luk' -'Lufthansa_' -'Ludwig_' -'Lucas_' -'Loyali' -'Louvre' -'Lohns' -'Logi' -'Lloyd' -'Livi' -'Liverpool_' -'Liter' -'Listen_' -'Lima_' -'Liese' -'Lieferu' -'Lied' -'Libye' -'Libyan' -'Leser_' -'Lesen_' -'Leo' -'Leno' -'Leiche' -'Lehrer' -'Lehman_' -'Legend' -'Leg' -'Lebensstandard_' -'Lebense' -'Lebanese_' -'Leave_' -'Laz' -'Lava' -'Laufzeit_' -'Laufen' -'Latvia' -'Latin' -'Lass' -'Larry_' -'Lannoye_' -'Langzeit' -'Landwirtschafts' -'Landw' -'Lampe' -'Lamaniten_' -'Lager' -'Lagen' -'Lady' -'Label_' -'Lab_' -'LR' -'LC_' -'Küsten_' -'Künst' -'Kün' -'Kühlschra' -'Kü' -'Körperschaft' -'Königreichs_' -'Köln' -'Käufer' -'Käuf' -'Kyr' -'Kyi' -'Kuwait_' -'Kurze' -'Kuro' -'Kurdish_' -'Kunde_' -'Kulissen_' -'Kuli' -'Kriterium_' -'Kriminelle' -'Krieg' -'Krem' -'Kreditvergabe_' -'Kreditv' -'Kreditnehmer' -'Kreditkarten' -'Kreditb' -'Kreditaufnahme_' -'Krebs_' -'Kreaturen' -'Kreat' -'Kraftwerk' -'Kow' -'Kosov' -'Korruptions' -'Kori' -'Koreans_' -'Koran' -'Kora' -'Kopiere' -'Kopfs' -'Koordin' -'Konzerne' -'Konvertier' -'Konvers' -'Kontextmenü' -'Konte' -'Konsultationen_' -'Konserv' -'Konferenzr' -'Konferenze' -'Kompetenz_' -'Kommuni' -'Kommissionsvorschlag_' -'Kommissionsmitglied' -'Kommissar' -'Kommentar_' -'Kolumbien_' -'Kolonie' -'Kollekti' -'Koizumi_' -'Kohlendioxid_' -'Koh' -'Kofinanzierung' -'Knowledge_' -'Kno' -'Kni' -'Klu' -'Klingon' -'Kleine' -'Klein_' -'Klausel' -'Klassifi' -'Klage_' -'Kinnock_' -'King' -'Kindes' -'Kinderar' -'Kin' -'Khomeini_' -'Kho' -'Khan' -'Khal' -'Keys' -'Kernel' -'Kenn' -'Kaukasus_' -'Katastrophenschutz' -'Katalo' -'Kaschmir' -'Karzai_' -'Kapitalmärkte_' -'Kapitalflu' -'Kanäle_' -'Kana' -'Kammer' -'Kamin' -'Kama' -'Kalk' -'Kaliningrad' -'Kale' -'Kade' -'Kaczyński_' -'Kabel' -'KW' -'KU' -'KMU_' -'KLM_' -'KING' -'KEI' -'KB' -'Jury' -'Juris' -'Jugendlicher_' -'Jugendherberg' -'Juden_' -'Journalist' -'Jord' -'Jong_' -'Jonas' -'Johnson' -'Johanne' -'Jog' -'Joan' -'Jim_' -'Jet' -'Jes' -'Jem_' -'Javi' -'Jaro' -'Jar' -'Japaner_' -'Jan' -'Jahrtausend' -'Jahrhunderte' -'Jahresz' -'Jag' -'Jacob_' -'JRE_' -'JE' -'Ivan' -'Iv' -'Ite' -'Italiens_' -'Italien' -'Issu' -'Iraker_' -'Investor' -'Internetverbindung_' -'Interneta' -'International' -'Intensität' -'Int' -'Insur' -'Insti' -'Inse' -'Innovation' -'Innenpoliti' -'Initiati' -'Inhalts' -'Inhaber_' -'Infrastructure_' -'Infos_' -'Informationss' -'Informationsa' -'Informati' -'Inflationsr' -'Infineon' -'Infektions' -'Infekt' -'Infe' -'Industriesta' -'Industrielle' -'Industrial_' -'Industri' -'Indoor_' -'Indo' -'Indiz' -'Individual' -'Indic' -'Increased_' -'Inco' -'Inclu' -'Inci' -'Improvi' -'Impres' -'Impfstoffe_' -'Immerhin_' -'Immer_' -'Ign' -'Ideologie_' -'Ideolog' -'Ideally_' -'Ideale' -'Ideal' -'Icon' -'Ibn_' -'IR_' -'INE' -'INCLUD' -'ILA' -'IFA' -'IES_' -'IEN' -'ICT' -'Hügel_' -'Hör' -'Höl' -'Höf' -'Hôtel_' -'Händler' -'Hypo' -'Hyg' -'Hydr' -'Hybri' -'Hv' -'Hungarian_' -'Hunderttausende' -'Hunderte' -'Hund' -'Hul' -'Hubschrauber_' -'Hua' -'House' -'Hotelzimmer_' -'Hot_' -'Horizonte' -'Horizont' -'Hond' -'Hom' -'Holy_' -'Hoff' -'Hochwasser' -'Hochgeschwindigkeits' -'His' -'Hinr' -'Hindus_' -'Himmel_' -'Hilfsmittel_' -'Hilfe' -'Highway' -'Hig' -'Heut' -'Het_' -'Herzog' -'Herv' -'Herrscher_' -'Herman' -'Herangehensweise_' -'Heilige' -'Hed' -'Hebr' -'Header' -'Hay' -'Hava' -'Haustür' -'Haushaltspolitik_' -'Haushaltsplan' -'Haushaltsmittel' -'Haushaltskon' -'Haushaltsausschusses_' -'Haushaltsaus' -'Hauptt' -'Hauptstr' -'Hauptp' -'Hauptau' -'Hatoyama_' -'Hass_' -'Harris_' -'Harr' -'Harm' -'Hariri' -'Hara' -'Happ' -'Hanse' -'Handl' -'Handelsa' -'Hande' -'Hamm' -'Halte' -'Hack' -'Haben' -'Haag' -'HR' -'HOT' -'HER' -'HEN' -'HC_' -'HAVEN_' -'H1' -'Gültigkeit_' -'Gül' -'Göttin' -'Gän' -'Gutes_' -'Gunsten_' -'Gues' -'Guardi' -'Guard' -'Größen_' -'Größ' -'Grundzüge' -'Grundwasser' -'Grundsätzlich' -'Grundsätzen_' -'Grundsatze' -'Grunds' -'Grundrechte' -'Grundl' -'Großka' -'Große' -'Grou' -'Griechen' -'Grey' -'Grenzwert' -'Gremien_' -'Gregori' -'Grego' -'Gramm' -'Graci' -'Gourmet_' -'Gothic_' -'Goth' -'Gos' -'Gore_' -'Good' -'Golfplätze' -'Golds' -'Gob' -'Glück' -'Gloucester' -'Glied' -'Glen' -'Gleichg' -'Gleichbehandlung_' -'Gle' -'Girl' -'Gio' -'Gibraltar_' -'Gia' -'Gewinne' -'Gewin' -'Gewerkschaft' -'Gewerbe' -'Gewalttaten_' -'Getränke' -'Getränk_' -'Gesundheitsz' -'Gesundheitsv' -'Gesundheitssystem' -'Gesundheitsm' -'Geste' -'Gesicht' -'Gesetz' -'Geschäftsv' -'Geschäftsbereich' -'Geschäften_' -'Geschw' -'Geschlecht' -'Geschirr' -'Geschichten_' -'Gesamth' -'Gesamtbe' -'Geräte' -'Germani' -'Germ' -'Gerichtsh' -'Georgi' -'George' -'Geogra' -'Genießen_' -'Generalsekret' -'Genauigkeit' -'Genau' -'Gen_' -'Gemäß' -'Gemeinschaftsin' -'Gelände_' -'Gele' -'Geldes_' -'Gei' -'Gehälter' -'Gehirn_' -'Gehirn' -'Geheimnis' -'Gehei' -'Gehalt_' -'Gegenden_' -'Gefüh' -'Gefängnisse' -'Gefahren' -'Gedächtnis_' -'Gedicht' -'Geburtstag' -'Geburten' -'Gebot' -'Gate_' -'Gastge' -'Garden' -'Garan' -'Ganz' -'Gamm' -'Gaming_' -'Gallery_' -'Galerie_' -'Gale' -'Gala' -'GW_' -'GUI' -'GUE_' -'GP_' -'GO_' -'GMT_' -'GMOs_' -'GL' -'GE_' -'Fülle' -'Fä' -'Fut' -'Fundament' -'Frühstücks' -'Frustration_' -'Frontier' -'Frist' -'Freud' -'Frequenz' -'Fremdenverkehr_' -'Fremdenfeindlichkeit_' -'Freizeita' -'Freilich' -'Freigabe_' -'Freedoms_' -'Fred_' -'Fred' -'François_' -'Franco' -'Fragment' -'Fragestunde_' -'Frage' -'Founde' -'Fotografie_' -'Fortschr' -'Fort_' -'Forschungss' -'Forschungsergebnisse_' -'Forschungsa' -'Formular' -'Formel_' -'Forest_' -'Fore' -'Football_' -'Fonta' -'Folglich_' -'Focus' -'Flut_' -'Flusse' -'Flus' -'Flie' -'Flemi' -'Flasche' -'Flam' -'Flagg' -'Fixed_' -'Fit' -'Fischereiabkommen_' -'Fische_' -'Firmware_' -'Firm' -'Firew' -'Finger_' -'Finanzwesen_' -'Finanzst' -'Finanzp' -'Finanzmittel_' -'Finanzmi' -'Finanzinstrument' -'Finanzb' -'Fina' -'Filme_' -'Filme' -'Fig' -'Fic' -'Feuerwe' -'Feststellung' -'Festiv' -'Feste' -'Fertigkeit' -'Ferna' -'Ferienhäuser_' -'Fed' -'Fatah_' -'Fans_' -'Familienzimmer_' -'Falle' -'Faktum_' -'Fahrzeug' -'Fahrt_' -'Fahrplan_' -'Fahren_' -'Fact' -'Faci' -'Fachk' -'Fabrik' -'Fab' -'FRE' -'FAQ' -'Extreme_' -'Extras_' -'Exten' -'Exporteure' -'Experts_' -'Experte' -'Experimente' -'Exper' -'Exo' -'Exist' -'Exhibit' -'Exekutiv' -'Except' -'Ew' -'Evidence_' -'Everest_' -'Even' -'Evangeli' -'Eurojust_' -'Eurocopter_' -'Euroc' -'Eurobonds_' -'EuroM' -'Eurasi' -'Eur' -'Euph' -'Establishment_' -'Essential_' -'Esse' -'Especially_' -'Erziehung_' -'Erzeugung' -'Erzeugnisse_' -'Erwähnung_' -'Erwägungen_' -'Erweiterungen_' -'Erwachsenen_' -'Erwach' -'Erträge_' -'Ertr' -'Erstelle' -'Erste_' -'Erstau' -'Ersparnissen_' -'Erscheinungsbild_' -'Ersatz' -'Err' -'Ernähr' -'Ernst_' -'Erleichterung_' -'Erleb' -'Erheb' -'Erha' -'Erh' -'Ergänz' -'Erg' -'Erfolgsgeschichte' -'Erfolg' -'Erbr' -'Eras' -'Equip' -'Equ' -'Entwicklungsziele' -'Entwicklungs_' -'Entstehung' -'Entspann' -'Entscheidungsprozess' -'Entführung' -'Entf' -'Enter' -'Enr' -'Englischen_' -'Englische' -'Energietechnologie' -'Energier' -'Energiepolitik_' -'Endp' -'Endl' -'Ende' -'Employ' -'Empfang' -'Emotion' -'Emirate' -'Embr' -'Email_' -'Elysées_' -'Ell' -'Elizabeth_' -'Elis' -'Elend_' -'Elemente' -'Eleganz_' -'Elefanten' -'Elde' -'Eisenbahnver' -'Einzelpersonen_' -'Einwi' -'Einwanderungspolitik_' -'Einwanderungs' -'Eint' -'Einsp' -'Einse' -'Einrei' -'Einmischung' -'Einmarsch' -'Einm' -'Einkommens_' -'Einkaufszentr' -'Einflüsse' -'Einfach_' -'Eindämmung' -'Einblick_' -'Einbindung_' -'Eid' -'Editor_' -'Edit_' -'Echtzeit_' -'Early_' -'EUR' -'ESM' -'ENI' -'EMAS_' -'Düsseldorf_' -'Dür' -'Dü' -'Dynast' -'Dynamik' -'Dutzende_' -'Dus' -'Durchschnitts' -'Durchführ' -'Durchb' -'Duomo_' -'Duff_' -'Dub' -'Ds_' -'Drücke' -'Drum' -'Drucker' -'Droh' -'Drittl' -'Dritte' -'Dringlichkeits' -'Dri' -'Dream' -'Drago' -'Dr' -'Doyle_' -'Downloads' -'Down' -'Dornik_' -'Dorn_' -'Dorf' -'Doppelzimmer_' -'Doo' -'Dominikan' -'Dolomit' -'Dolmetsch' -'Dokumentation_' -'Divers' -'DivX_' -'Div' -'Distributoren_' -'Disku' -'Diskriminierung' -'Directory_' -'Director' -'Directi' -'Diplomaten_' -'Diktatur_' -'Different_' -'Dienststelle' -'Dienstleistungssektor' -'Dienstleist' -'Diensta' -'Dienst' -'Dictionary_' -'Dichte' -'Dich' -'Dialogs_' -'Diabetes_' -'Deutsche' -'Deut' -'Detail_' -'Designs' -'Designer_' -'Desi' -'Deregul' -'Derartige_' -'Denkweise' -'Denis' -'Demokratisierung_' -'Demogra' -'Demagog' -'Delu' -'Delta_' -'Delo' -'Delegation' -'Dele' -'Dela' -'Deine' -'Deckmantel_' -'Death_' -'Daw' -'Datenschutz_' -'Datenbl' -'Date' -'Dasselbe_' -'Daseins' -'Das' -'Darwi' -'Darlehen' -'Darauf_' -'Dara' -'Dampfb' -'Damas' -'Dalma' -'Dai' -'Dafürhalten_' -'DVDs_' -'DK' -'DJ_' -'DI_' -'DIC' -'DES' -'DEN_' -'DC' -'DAX_' -'Cycl' -'Cyber' -'Curt' -'Cul' -'Cott' -'Cord' -'Copy' -'Cop' -'Cooper' -'Cool' -'Controller_' -'Conti' -'Constant' -'Conservati' -'Congress' -'Confedera' -'Condo' -'Conditions_' -'Conci' -'Concern' -'Computern_' -'Compr' -'Compani' -'Communication' -'Commen' -'Comi' -'Comfort' -'Combi' -'Colomb' -'Collect' -'Cohe' -'Coelho_' -'Coch' -'Cob' -'Clu' -'Close_' -'Clip' -'Clif' -'Cleverl' -'Cleaning_' -'Clean_' -'Classic' -'Clas' -'Clark' -'Circle_' -'Circ' -'Cind' -'Chrom' -'Christus_' -'Christopher_' -'Christo' -'Christie_' -'Christiani' -'Christen_' -'Christdemokraten_' -'Chr_' -'Chev' -'Cherno' -'Chechen' -'Charle' -'Chapel_' -'Chap' -'Champ' -'Ces' -'Center' -'Cav' -'Caucas' -'Castil' -'Cassi' -'Casio' -'Casino' -'Cash' -'Carrie_' -'Carp' -'Carlo' -'Carl_' -'Caribbean_' -'Care' -'Cardi' -'Capt' -'Canc' -'Canari' -'Canal' -'Canadian_' -'Campingpl' -'Camera' -'Cambodia_' -'Calendar_' -'Cairo' -'Caesar' -'COS' -'CONT' -'CHF_' -'CHE' -'CGI_' -'CET_' -'Bürgerkrieg_' -'Bürgerkrieg' -'Bürgerbe' -'Bündnis_' -'Bünd' -'Bücher_' -'Böge_' -'Buy' -'Busse' -'Busc' -'Bundestag_' -'Bundesstaaten_' -'Bundesp' -'Bum' -'Buddh' -'Buchungs' -'Buchst' -'Buchführung' -'Bucher_' -'Bry' -'Brunnen_' -'Bruc' -'Brow' -'Brooklyn_' -'Bronze' -'Broc' -'Britis' -'Brigade' -'Brian_' -'Brew' -'Bretton_' -'Bret' -'Brenner' -'Bremen_' -'Breitband' -'Brei' -'Bree' -'Brav' -'Braun' -'Branc' -'Box' -'Boul' -'Botschafter' -'Boston' -'Bosnien_' -'Bosnia_' -'Boots' -'Boot' -'Bond' -'Bombardier' -'Boeing' -'Boe' -'Bodensch' -'Bode' -'Boar' -'Blog' -'Blingee_' -'Blick' -'Blaž_' -'Birma' -'Biot' -'Biog' -'Binnenm' -'Bindungen_' -'Bin_' -'Billig' -'Billi' -'Bildern_' -'Bhutan_' -'Bezirks' -'Bey' -'Bewä' -'Bewu' -'Bewertungs' -'Bewert' -'Bewerber' -'Better_' -'Betrü' -'Beträge' -'Betreuer' -'Betracht' -'Besuchern_' -'Bestra' -'Besten' -'Bestell' -'Besonderheiten_' -'Besetzung_' -'Beschwerde_' -'Beschlussfassung' -'Beschlusse' -'Beschleunigung' -'Beschaff' -'Berufsbildung' -'Berufe' -'Bert' -'Berl' -'Berichterstattung' -'Berb' -'Beobachtungsst' -'Benz_' -'Belohnung_' -'Beliebt' -'Belgrade_' -'Belastung_' -'Belast' -'Belarus' -'Beitrittsl' -'Beitrittskandidaten_' -'Beis' -'Being_' -'Beine' -'Bein' -'Beihilfe' -'Behau' -'Behandlungs' -'Begrenzung_' -'Bege' -'Beförderungs' -'Befu' -'Befr' -'Bedürf' -'Bedienung_' -'Bede' -'Bec' -'Beauf' -'Beatri' -'Beachten_' -'Bayer' -'Bavarian_' -'Baute' -'Baustein' -'Baum_' -'Batt' -'Bath' -'Basket' -'Basis' -'Base' -'Barry_' -'Barro' -'Baro' -'Bargeld' -'Barbe' -'Bann' -'Banker' -'Bankensystem' -'Bande' -'Banc' -'Ballo' -'Bald_' -'Bajor_' -'Bahnstation_' -'Bah' -'Baghdad' -'Baden_' -'Bachelo' -'Bach_' -'Babys' -'BOJ_' -'BN' -'BJ' -'BF' -'BERKELEY_' -'Azu' -'Ax' -'Aw' -'Avatar' -'Außenbe' -'Autoren_' -'Autonomiebehörde_' -'Autobahn_' -'Ausübung' -'Auswärtige' -'Ausweg_' -'Auswe' -'Austausch' -'Aust' -'Ausspr' -'Ausschü' -'Ausschuß_' -'Ausscheiden' -'Ausscha' -'Auskunft' -'Ausgrenzung_' -'Ausgew' -'Ausgehend_' -'Ausgabe' -'Ausflu' -'Ausd' -'Aurora_' -'Aug' -'Aufträge_' -'Auft' -'Aufstände' -'Aufstand_' -'Aufse' -'Aufge' -'Auffü' -'Auffassungen_' -'Attraktionen_' -'Attent' -'Atta' -'Atlantis' -'Atla' -'Asylsuchende' -'Asylant' -'Assist' -'Asset' -'Asians_' -'Arzt' -'Aru' -'Articles_' -'Arsenal_' -'Ars' -'Arou' -'Armenian_' -'Ark' -'Argumen' -'Argentine' -'Argen' -'Arena' -'Are' -'Arbeitszeit_' -'Arbeitsver' -'Arbeitsrecht' -'Arbeitspro' -'Arbeitsp' -'Arbeitsgruppe_' -'Arbeitsg' -'Arbeitsbe' -'Arbeit' -'Apr' -'Appro' -'Anzeigen' -'Anzeige' -'Anz' -'Anyone_' -'Anwesenheit_' -'Anwendungsbereich_' -'Anwender_' -'Anwa' -'Antrags' -'Anteils' -'Anteile_' -'Ansä' -'Ansprüchen_' -'Ansprech' -'Anson' -'Anschrift' -'Anschließend_' -'Ansatzes_' -'Anreise' -'Anregungen_' -'Anpassungen_' -'Annex' -'Annahmen_' -'Anmerkungen_' -'Anmerkung_' -'Anleihe' -'Ankunft_' -'Anku' -'Anklage_' -'Ani' -'Anhörung_' -'Angr' -'Angeles_' -'Angel' -'Angebots' -'Angeb' -'Ane' -'Andria_' -'Andorra' -'Anden' -'Andalusien' -'Andalusia' -'Anda' -'Anbau_' -'Anato' -'Analys' -'Amu' -'Amo' -'Ambitionen_' -'Amazon_' -'Ama' -'Aly' -'Alum' -'Altern' -'Alta_' -'Alr' -'Alps_' -'Alpi' -'Alltags' -'Alkohol' -'Algor' -'Algerie' -'Algar' -'Alegr' -'Alber' -'Albanian' -'Alb' -'Aktuell' -'Aktualisierung_' -'Aktivisten_' -'Aktionär' -'Aktionsprogramm_' -'Akk' -'Aki' -'Aka' -'Airp' -'Airconditioning_' -'Aid_' -'Agrars' -'Agenturen_' -'Agent_' -'Afri' -'Advanced_' -'Administrat' -'Ade' -'Addis_' -'Ada' -'Active_' -'Acid' -'Acht' -'Achsen' -'Accord' -'Abzug_' -'Abwicklung_' -'Abweichung' -'Abtreibung' -'Abtei' -'Abstimmungs' -'Absti' -'Absolvent' -'Abso' -'Absen' -'Abschl' -'Abl' -'Abhängig' -'Abh' -'Abgeordnete' -'Abfälle_' -'Abdullah' -'Abbe' -'Aa' -'ATM' -'AR_' -'AP_' -'AO' -'AMS' -'AMR_' -'ALE_' -'AD_' -'ADE' -'ACI' -'ACCE' -'ABS' -'A2' -'=_' -';&' -'90er_' -'83' -'750' -'70er_' -'681' -'67' -'63' -'5th_' -'520' -'52' -'4th_' -'45' -'43' -'3G_' -'3G' -'39' -'370' -'37' -'270_' -'220_' -'21s' -'202' -'201' -'199' -'1972_' -'1961_' -'1960s_' -'1960' -'1959_' -'1950er_' -'1946_' -'1939_' -'1936_' -'1907_' -'171' -'170' -'16th_' -'145_' -'142' -'127_' -'124_' -'121_' -'117' -'116' -'105_' -'102' -'101' -'0er_' -'07' -'020' -'007' -'/+_' -'/ ' -'......' -'....' -'.'_' -'->' -',..._' -',- ' -',,_' -', (_' -', $_' -'++' -'* ' -'):_' -'), ' -')) (' -'))' -'() ._' -'': _' -'')' -'''.' -''''_' -'%\\' -'$ ' -'">- _' -'"...' -'". _' -'" ._' -'" -' -'!! _' -'!! !' -' …' -' ”' -' ’_' -' ­' -' £_' -' [...]' -' = {_' -' = ' -' ;' -' -> _' -' ***' -' ). _' -' (“_' -' (.' -' ('' -' &#_' -' !!' -'−' -'ي' -'ט' -'ג' -'ь' -'щ' -'ц' -'σ' -'ş' -'œ' -'ě' -'ę' -'ā' -'õ' -'ñ' -'¿' -'º' -'~' -'$' -'™' -'†' -'–' -'ن' -'ل' -'ف' -'ر' -'ר' -'נ' -'Ж' -'Д' -'υ' -'ν' -'λ' -'ś' -'ń' -'ù' -'ì' -'Ñ' -'É' -'Ã' -'Á' -'§' -'–' -'&' -'ー' -'‚' -'م' -'ק' -'ד' -'Я' -'П' -'О' -'Л' -'Е' -'А' -'π' -'κ' -'θ' -'β' -'ū' -'Ś' -'ō' -'ć' -'æ' -'Ê' -'Â' -'¼' -'·' -'¶' -'´' -'¥' -'`' -'@' -'#' -'' -'년' -'語' -'简' -'本' -'日' -'文' -'年' -'中' -'•' -'ṳ' -'ศ' -'พ' -'ा' -'र' -'ى' -'ه' -'ص' -'ت' -'ب' -'פ' -'ס' -'ן' -'ו' -'ֿ' -'В' -'ω' -'χ' -'δ' -'Ω' -'̤' -'ư' -'ů' -'ř' -'ľ' -'ė' -'ĕ' -'ą' -'û' -'À' -'½' -'¹' -'­' -'¤' -'¡' -'’' -'\' -':' -'' -'fi' -'黵' -'黃' -'鰀' -'鋘' -'鋓' -'遝' -'蒸' -'致' -'美' -'网' -'紙' -'熨' -'斗' -'応' -'女' -'味' -'友' -'信' -'介' -'丨' -'一' -'ャ' -'バ' -'チ' -'ジ' -'カ' -'ん' -'ら' -'め' -'●' -'▼' -'→' -'※' -'ớ' -'ọ' -'ị' -'ẽ' -'ẻ' -'ấ' -'ी' -'ि' -'य' -'ब' -'त' -'छ' -'आ' -'ِ' -'ك' -'غ' -'ع' -'د' -'ج' -'إ' -'،' -'צ' -'ל' -'ה' -'Қ' -'Ғ' -'Э' -'Ш' -'Ц' -'Х' -'Р' -'М' -'φ' -'ζ' -'γ' -'Χ' -'Τ' -'Ι' -'Ε' -'̯' -'̆' -'ː' -'ˈ' -'ɾ' -'ɛ' -'ɐ' -'ſ' -'ű' -'ŭ' -'ő' -'Ő' -'ŏ' -'ň' -'İ' -'ī' -'đ' -'Đ' -'ă' -'ý' -'ã' -'à' -'Ô' -'Ó' -'È' -'Å' -'¾' -'µ' -'³' -'°' -'¬' -'¢' -'' -'™' -'—' -'“' -'' -'^' -'—' -'²' -'£' -'<' diff --git a/lingvo/core/ops/testdata/test_ngrams.txt b/lingvo/core/ops/testdata/test_ngrams.txt deleted file mode 100644 index dc14e9aa..00000000 --- a/lingvo/core/ops/testdata/test_ngrams.txt +++ /dev/null @@ -1,255 +0,0 @@ - 0 - 0 - 0 -{ 32402 -m 32010 -, 31793 -? 31481 -" 31002 -s 30592 -r 28832 -) 28316 -n 27667 -b 26399 -t 26268 -p 25947 -w 25748 -a 24249 -l 23717 -u 21727 -: 21703 -f 20237 -} 20153 -c 18509 -. 16781 -o 15746 -y 14335 -~ 14253 -_ 13262 -q 12548 -> 11036 - 10859 -g 9676 -h 9605 -i 9595 -! 9097 -d 8964 -* 8786 -x 8786 -< 8146 -z 5961 -; 5605 -k 5411 -j 3322 -& 3026 -( 2351 -- 2061 -v 483 -/ 293 -e 200 -` 139 -ed t 32707 -ag 32644 -su 32457 -t th 32396 -es 32072 -un 32012 -os 32003 -gh 31975 -si 31641 -mi 31435 -n t 31226 -op 31038 -le 30862 -wi 30709 -ex 30688 -an 30443 -ter 30325 -mo 30234 -re 29997 -ci 29863 -ie 29788 -to 29761 -id 29697 -rt 29497 -s o 29452 -e a 29440 -de 29423 -one 28763 -ai 28386 -n th 28352 -all 28078 -her 27964 -we 27827 -ee 27687 -ow 27617 -or 27517 -e i 27344 -ss 26988 -ce 26785 -th 26722 -nc 26552 -are 26482 -ve 26415 -eri 26381 -that 26104 -e o 25761 -d a 25713 -am 25334 -ise 25140 -ei 25004 -fo 25004 -ha 24996 -ca 24870 -res 24718 -t t 24698 -ev 24464 -il 24420 -wo 24382 -mp 23652 -ate 23227 -ho 23105 -ine 23024 -ted 23007 -ere 22940 -sh 22918 -ati 22902 -el 22814 -for 22811 -no 22645 -at 22400 -wh 22372 -een 22348 -st 22331 -ct 22329 -he 22229 -la 22150 -e c 22020 -pr 21855 -ot 21724 -ri 21347 -ur 21142 -er 21127 -ive 20825 -so 20732 -in 20702 -ill 20601 -est 20545 -ia 20531 -of 20308 -ts 20244 -ul 20231 -s a 19938 -ly 19877 -en 19810 -con 19726 -bu 19446 -ng 19282 -wa 19158 -ld 18999 -se 18943 -lo 18521 -com 18474 -bl 18425 -ig 18400 -io 18293 -ar 18079 -ic 17858 -ad 17331 -the 17207 -tio 16749 -ne 16721 -vi 16660 -ge 16491 -nt 16147 -ear 16143 -per 16007 -hat 15856 -ra 15502 -ba 15135 -li 14949 -ht 14835 -ion 14689 -al 14674 -ut 14585 -ver 14335 -ov 14333 -ers 13880 -'s 13667 -men 13639 -n a 13603 -ir 13250 -ro 13227 -igh 12875 -ti 12840 -he s 12509 -pl 12297 -iv 12209 -pro 12015 -em 11860 -bo 11836 -us 11771 -e f 11594 -ent 11453 -di 11395 -eve 10976 -d th 10750 -pa 10471 -and 10461 -on 10017 -it 9914 -d t 9875 -qu 9754 -atio 9701 -om 9676 -ll 9662 -te 9538 -ch 9059 -ni 8679 -do 8500 -me 8462 -f t 8088 -co 7770 -rs 7570 -et 7550 -s t 7394 -ma 7284 -ke 6972 -be 6912 -ol 6465 -int 6429 -r t 6249 -hi 6107 -ed 6033 -is 6019 -tha 5714 -nd 5685 -as 5631 -fi 5479 -e th 4577 -s i 4439 -mm 4358 -pe 3914 -t a 3849 -po 3563 -e b 3506 -tion 3371 -na 3247 -e s 2648 -s s 2547 -ta 2341 -ec 2297 -ty 2202 -oi 1988 -ay 1372 -ea 1217 -ght 1002 -s th 924 -sa 818 -ou 794 -e t 792 -ing 646 -tr 578 -ns 353 -e p 124 -ac 71 diff --git a/lingvo/core/ops/testdata/test_vocab.txt b/lingvo/core/ops/testdata/test_vocab.txt deleted file mode 100644 index 6e760b3e..00000000 --- a/lingvo/core/ops/testdata/test_vocab.txt +++ /dev/null @@ -1,14 +0,0 @@ - 0 - 1 - 2 - 3 -the 4 -a 5 -b 6 -c 7 -d 8 -e 9 -øut 10 -über 11 -♣ 12 -愤青 13 diff --git a/lingvo/core/ops/testdata/wmt/sm18.txt b/lingvo/core/ops/testdata/wmt/sm18.txt deleted file mode 100644 index 373b8909..00000000 --- a/lingvo/core/ops/testdata/wmt/sm18.txt +++ /dev/null @@ -1,3003 +0,0 @@ - $ de secours pour la santé Plan de sauvetage de 325 M $ destiné au système de santé de la Tasmanie -" La colline de n' est pas stratégique " , a déclaré le gouverneur du Nord-Kivu , Julien , aux AFP . " Celle de n' est pas très stratégique " , a indiqué a l' AFP le gouverneur du Nord-Kivu , Julien . -" Le soutien de l' opposition a été pris en charge par le Front islamique du Turkestan oriental ( ) basé en Asie centrale et occidentale " , a déclaré le chef des services de sécurité chinois à une chaîne de télévision de Hong Kong , selon une vidéo diffusée en ligne jeudi soir . " Le soutien en coulisses à l' attentat est venu du Mouvement islamique du Turkestan oriental basé en Asie centrale et occidentale " , a déclaré le patron des organes de sécurité chinois à une chaîne de télévision de Hong Kong , selon une vidéo mise en ligne jeudi soir sur un site internet . -" n' a pas été accessible pendant plusieurs heures ce soir en raison d' une erreur interne survenue lors d' une mise à jour programmée " , a déclaré l' agence d' espionnage dans une déclaration envoyée par courriel . « est resté inaccessible pendant quelques heures ce soir en raison d' une erreur interne qui a eu lieu pendant une mise à jour prévue » , a indiqué l' agence d' espionnage dans une déclaration par email . -" Un biométrique empêche la situation dans laquelle une au nom d' une autre " affirme Cyrille d' Accenture . " Une biométrique évite le phénomène du copain qui pointe pour un autre " , justifie Cyrille , d' Accenture . -" Un événement marquant a eu lieu aujourd ' hui : la construction a commencé dans la section bulgare du gazoduc South Stream , le projet le plus important et le plus économique de l' Europe " , a déclaré le président de Gazprom , M. Vincent , dans une déclaration jeudi . « Un évènement historique a eu lieu aujourd' hui : La construction a commencé dans la partie bulgare du gazoduc South Stream , le projet à grande échelle le plus important d' Europe » , a expliqué Alexei Miller , le directeur général de Gazprom , dans une déclaration jeudi . -" Un grand nombre de militants craignent que la règle allemande n' encourage les parents à prendre des décisions rapides et à donner à l' enfant un statut " indéterminé " , " a-t-elle déclaré " . « De nombreux activistes s' inquiètent que la loi allemande encourage les parents à prendre des décisions rapides et donnent à leur enfant un sexe " indéterminé " » , a-t-elle déclaré . -" Les adultes devraient pouvoir prendre leurs propres décisions sur le genre légal " , a-t-elle dit . « Les adultes devraient pouvoir prendre leurs propres décisions à propos d' un genre légal » , a-t-elle ajouté . -" Alex Turner a été diagnostiqué et n' est malheureusement pas capable de faire " . « Alex Turner souffre d' une et regrette de ne pouvoir se produire en concert . » -" Tous les soldats blessés reçoivent les soins appropriés en vue du retour à la vie civile , et aucun n' a été libéré avant qu' il ne soit prêt " , a-t-il déclaré . Tous les soldats blessés reçoivent les soins appropriés en vue de leur retour à la vie civile et aucun d' entre eux n' est libéré avant d' être prêt , a-t-il affirmé . -" Une enquête épidémiologique a été entreprise pour identifier et traiter tous les individus susceptibles d' entrer en contact avec le chaton entre le 30 et le 28 octobre inclusivement " , a déclaré les ministres de la Santé et de l' Agriculture . " Une enquête épidémiologique a été engagée afin d' identifier et prendre en charge les personnes qui auraient pu entrer en contact avec ce chaton entre le 8 octobre et le 28 octobre inclus , " précise les ministères de la santé et de l' agriculture . -Et parce que si ce qui se passe en Irak n' est pas traité , il se , de même que ce qui se passe en Syrie . Et aussi parce que si ce qui se passe en Irak n' est pas réglé , cela va s' étendre , et ce qui se passe en Syrie va s' étendre aussi . -" Et le courrier exprimant le soutien que j' ai reçu depuis hier n' est pas toujours à partir de la banlieue " , dit-il avec le plus grand plaisir . " Et les mails de soutien que je reçois depuis hier n' émanent pas tous de banlieue " se . -" Un autre groupe de les a trouvé dans le piège creux , mais en bonne santé et a reçu un message à l' une des équipes de sauvetage travaillant dans le réseau des tunnels " , a expliqué le journal local , Le . " C' est un autre groupe de qui , après les avoir trouvés épuisés mais en bonne santé dans la cavité , a transmis l' information à l' une des équipes de secours engagées dans le réseau " explique Le . -" Au milieu du XXe siècle , on l' appelait " urgence psychiatrique " , dit M. . « Au milieu de XXe siècle , on appelait cela une urgence psychiatrique » , a indiqué . -" Parce que l' incinération est un développement récent " , dit , présidente de la Société et auteur du livre the Death . " Parce que la crémation est un fait nouveau " , répond , présidente de la Société de et auteur du livre la mort . -" Boeing propose un vol de longue distance dans des sièges plus restreints que les régionaux " , a déclaré John Leahy , Chief Operating Officer Customers d' Airbus . « Boeing propose des vols long-courriers dans des sièges plus étroits que dans les avions turbopropulseurs régionaux » , a indiqué John Leahy , directeur commercial d' Airbus . -" Le fait est que , à mesure que les coûts augmentent , les personnes qui se trouvent dans la tranche inférieure ( à la fin de l' échelle des revenus ) seront en mesure de tirer profit de leur revenu " , a déclaré Song , économiste à , une banque malaisienne . « Le bilan est qu' avec la hausse des coûts , les personnes au milieu ou au bas [ de l' échelle des salaires ] cherchent à compléter leurs revenus comme ils peuvent » , déclare Song , économiste chez , une banque malaisienne . -" Mais cette démarche pourrait inciter certains investisseurs étrangers à acheter des biens à Londres ou aux propriétaires actuels qui hésitent à vendre " , a-t-il ajouté . « Mais ce changement pourrait rendre les investisseurs étrangers réticents à l' idée d' acheter des propriétés dans Londres ou les propriétaires actuels réticents à l' idée de vendre » , a-t-il ajouté . -" À la fin de l' année financière , nous aurons sept nouveaux pharmaciens et trois du congé de maternité . D' ici la fin de l' année financière , nous aurons sept nouveaux pharmaciens et trois retours de maternité . -" À l' heure actuelle , la CASA n' a pas de réglementation spécifique régissant l' utilisation des appareils électroniques dans les avions " , a-t-il déclaré . « La CASA ne dispose pas de réglementations spécifiques régissant l' utilisation des appareils électroniques en avion » , a-t-elle déclaré . -" Les campagnes ne dépensent pas le montant de l' argent dans les sondages et les groupes de discussion , à moins qu' ils ne s' intéressent sérieusement à quelque chose " , a déclaré Martin sur . « Les campagnes ne consacrent pas beaucoup d' argent aux sondages et aux groupes de discussion à moins qu' elles n' envisagent sérieusement de prendre des mesures » , a déclaré Martin sur . -" Les téléphones cellulaires , c' est vraiment une question , non seulement parce que cela pourrait créer des interférences avec les appareils de communication , mais nous savons , d' après la FCC , qu' elle pourrait interférer avec les pylônes de téléphone lorsqu' ils sont dans l' air " , dit M. . « Les téléphones portables sont véritablement un problème , non seulement parce qu' ils pourraient éventuellement créer des interférences avec les instruments de navigation , mais parce que nous savons , d' après la FCC , qu' ils pourraient perturber les de téléphonie mobile s' ils sont utilisés à bord » , a déclaré . -" Les inquiétudes à propos de Big Brother et de ces types de problèmes étaient un problème majeur " , a déclaré Khan , qui dirige la gestion stratégique et des performances au sein du département des Transports du Nevada . « Les inquiétudes au sujet de Big Brother et ce genre de choses sont un gros problème » , a déclaré Khan , qui dirige la gestion stratégique de la performance au département des Transports du Nevada . -" Les clients des biens immobiliers qu' ils possèdent déjà , et les biens personnels ne font pas augmenter la dette des ménages " , affirme-t-elle . « Les clients des biens qu' ils possèdent déjà , et le fait de leurs biens personnels n' augmente pas l' endettement des ménages » , dit-elle . -" Selon l' état du plan , nous pourrions approuver l' utilisation à grande échelle des appareils électroniques plus tôt " , a déclaré la FAA dans une déclaration . « En fonction du plan , nous pourrions approuver une utilisation élargie des appareils électroniques très prochainement » , a indiqué la FAA dans une déclaration . -" Malgré sa perte dans la tentative d' acquisition de l' aux enchères , Google a violé et continue d' enfreindre " , a déclaré la commission . « Bien qu' ayant échoué dans sa tentative d' acquérir les brevets en cause au cours des enchères , Google a violé et continue à violer lesdits brevets » , ont indiqué les conclusions du procès . -" Les empreintes digitales sont stockées dans un serveur central ; l' identité des individus est stockée dans une deuxième base de données . " Les empreintes digitales sont stockées sur un premier serveur ; les identités des personnes sont dans une seconde base : la corrélation entre les deux numéros est cryptée et stockée dans un boîtier hautement sécurisé , qui se bloque si on tente de le déplacer " , énumère Philippe Robin , de Thales . -Les " doubles " ont fait l' éloge de Gates , en dépit de leur " relation personnelle étroite " , avant de décider en fin de compte de leur déplacement , alors que les données fournies par Craig le billet au " ne améliorerait pas sensiblement les chances d' Obama " . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; . « Double Down » indique que a mené la charge pour remplacer Biden , malgré leurs « liens personnels étroits » , avant de finalement se décider contre le changement lorsque les chiffres ont montré qu' avoir Clinton sur la liste présidentielle « n' améliorait pas nettement les chances de réussite d' Obama » . -" L' Egypte aujourd' hui est un État civil , constitutionnel , national et moderne " , a déclaré Mladic , avec un bras bleu et une cravate verte , a dit aux juges de l' atelier de où il avait prêté serment . « L' Égypte est aujourd' hui un État civil , national , constitutionnel et moderne » , a déclaré Mohamed Morsi , en costume bleu et cravate rouge , s' adressant aux juges présents dans la salle ornée de boiseries où il a prêté serment . -" Dix-huit pouces pour la largeur des sièges seraient très élevés pour les passagers , mais la réalité , d' un point de vue de l' objectif opérationnel d' Airbus , est le résultat de la menace de 777 " , a déclaré le spécialiste de l' intérieur , Mary Kirby , fondateur et directeur du " Girl Network " . « Une largeur de siège de 18 pouces serait parfaite pour les passagers , mais la réalité est que , du point de vue commercial , la proposition d' Airbus est motivée par la menace du 777 » , a déclaré l' experte en intérieurs de cabines Mary Kirby , fondatrice et rédactrice en chef de Girl Network . -" Même si la réorganisation de l' a une justification fondamentale , nous ne pouvons y arriver sans un plan acceptable de transfert de patients à d' autres hôpitaux " . Même si la restructuration de l' a sa légitimité sur le fond , nous ne pouvons pas l' admettre sans un schéma acceptable de report vers les autres hôpitaux . -" Tout le monde est " , a déclaré Andrea , l' un des participants . Tout le monde est , raconte une des participantes , Andrea . -" Tout le monde a un temps fou " , ajoute . On s' amuse comme des fous , ajoute . -" Chacun sera bientôt identifiable , partout et à tout moment " , a déclaré David , directeur de la protection des consommateurs de pour le Centre ( Center for Democracy and Technology ) de Washington . " Tout le monde pourra bientôt être identifié , n' importe où et n' importe quand " , s' Justin , responsable de la vie privée des consommateurs au sein du CDT ( Center for Democracy and Technology ) , à Washington . -" Tout a été mis en place afin que le puisse être déployé comme prévu à la fin 2013 " , explique . " Tout est mis en œuvre pour que le puisse faire son déploiement prévu en fin d' année 2013 " , explique cependant . -" Les finances sont et nous espérons voir le 50e anniversaire de l' , un jalon que nous en sept ans " , a déclaré l' administrateur . " Les finances sont à flot et nous espérons durer jusqu' au 50e anniversaire de l' , une étape que nous dans sept ans " , a commenté l' administrateur . -" Cinq personnes qui étaient en contact avec le chaton ont déjà été identifiées " et ont reçu un traitement préventif . " Cinq personnes ayant été en contact avec le chaton ont déjà été identifiées " et ont reçu un traitement préventif . -" Pendant une dizaine d' années , ils ont fait des efforts pour sauver les familles du mauvais esprit de la vie en attendant une heure et demie , sans rien faire , immédiatement après leur remise des cendres " , dit , . " Depuis une dizaine d' années , ils s' efforcent d' éviter aux familles la violence d' une attente , sans rien faire , pendant une heure et demie , immédiatement suivie de la remise des cendres " , observe M. . -" Par exemple , ceux qui travaillent avec du ciment ont des blessures à leurs doigts qui rendent leurs empreintes illisibles " , explique Luc Philippe , directeur technique de l' identification à Thales Communications & Security . " Par exemple , certaines personnes qui travaillent le béton ont les doigts très abîmés et leurs empreintes sont illisibles " , constate Philippe Robin , directeur technique du domaine identitaire chez Thales Communications & Security . -" Pour moi , cela n' indique pas un changement de tendance " , a-t-il noté . Pour moi , ce n' est pas l' indication d' un changement de tendance , a-t-il observé . -" Pendant plusieurs mois , j' ai demandé s' il y avait une façon de rester , et ils ont répondu " non , ajoute-t-il . J' ai demandé pendant des mois s' il y avait un moyen pour que je puisse rester , et ils ont dit non . -" Pour la grande majorité des personnes qui ont une assurance santé , vous pouvez le garder , dit-il dans un discours à Boston . « La grande majorité de personnes détenant une assurance santé qui leur convient peuvent la garder » , a-t-il déclaré lors d' un discours à Boston . -" Pour nous , le nombre de salariés est toujours précaire , car il y a un milieu de travail où beaucoup de jeunes femmes sont employées , c' est pourquoi on peut toujours compter sur trois personnes en congé de maternité quand les choses vont bien " , a-t-elle ajouté . Pour nous [ le nombre d' effectifs ] est toujours précaire , car on a un milieu qui est jeune et on emploie beaucoup de jeunes femmes , donc avec les grossesses , on roule presque toujours à trois congés de maternité quand ça va bien , a-t-elle ajouté . -" La France a été exposée à la rage depuis 2001 , et la mère ou la mère a été importée d' un autre pays où elle est encore présente " , dit le communiqué . " La France étant indemne de rage depuis 2001 , ce chaton ou la mère ont été importés d' un autre pays , non indemne " , selon le communiqué . -" L' Allemagne doit se réveiller " , a déclaré Oliver , président de , qui représente les petites et moyennes entreprises informatiques allemandes . « L' Allemagne doit se réveiller » , déclare Oliver , président de , qui représente les PME allemandes du secteur des TI . -" Il est un héros sans aucun doute " , dit O' Malley . « C' est un héros , il n' y a pas de doute » , a déclaré Katie O' Malley . -" Il a un gros conflit d' intérêts en ce qui concerne le congé parental rémunéré parce que si l' une de ces filles tombe enceintes , il aura un intérêt direct s' il a ou non un congé " , a déclaré M. Palmer . « Il aura un important conflit d' intérêt quand il s' agira de payer le congé parental car si l' une de ses filles est enceinte , il aura un intérêt direct dans le fait qu' elle obtienne ou non un congé » , a déclaré M. Palmer . -" Il ne se pas " , a dit Mme . « Il ne s' est pas rendu » , a ajouté Mme . -" Il est mort dimanche matin , en regardant les arbres et en prenant la célèbre 21e forme du chi , avec ses musiciens jouant l' air " , a écrit Laurie Anderson . " Il est mort dimanche matin , alors qu' il regardait les arbres et effectuait le célèbre 21e mouvement de chi , avec ses mains de musicien qui l' air " , écrit Laurie Anderson . -" Il ne veut pas de démocratie multipartite , il ne veut pas d' élections transparentes , il ne veut pas de la paix parce qu' il ne veut pas quitter la présidence " , a déclaré M. . « Il ne veut pas d' une démocratie multipartite , il ne veut pas d' élections transparentes , il ne veut pas la paix car il ne veut pas quitter la présidence » , a déclaré M. . -" Il n' a toujours pas pu voir un avocat et aucun volontaire n' a pu obtenir une copie des dossiers . Jusqu' ici aucun avocat n' a pu le rencontrer et aucun volontaire n' a pu prendre une copie des documents de l' affaire . -" Il doit être placé devant le peloton d' entraînement " , a déclaré Mme au . « Il devrait être traîné devant le peloton d' exécution » , a déclaré Mme à l' AAP . -" Nous sommes donc en train d' élaborer des projets pour lesquels ils peuvent s' identifier à tous " , explique le président de Vision , . " Donc , on crée des projets de façon à ce qu' ils s' identifient à tout ça " , explique la présidente de Vision diversité , . -" Son corps a été enterré dans une tombe commune dans le cimetière juif de Berlin Mitte en 1945 " , a-t-il confirmé dans le journal populaire quotidien , fondant sa déposition sur les archives . " Son corps a été enterré en 1945 dans une fosse commune du cimetière juif de " , a-t-il assuré au quotidien à grand tirage en s' appuyant sur des documents d' archives . -" Son côté humain est venu " , se . " Son côté humain est ressorti " , . -" Sa relation avec le succès est la nôtre et nous nous sommes : ce qui est un mal de beauté et un candidat qui n' est pas au bon endroit au bon moment , sans être honnête , mais avec une logique d' impunité " , , qui , avec son frère Joel , a reçu plusieurs Oscars et Cannes Awards , a dit à la presse . " Sa relation avec le succès est torturée , c' est cela qui nous intéressait : un mélange de malchance , un gars qui n' est pas là au bon moment , qui n' est pas mais intègre , avec un comportement " , déclarait à la presse , couronné avec son frère Joel de plusieurs Oscars et prix à Cannes . -" Ses papiers de service et sa photo , entre autres , ont été retrouvés dans la poche gauche " , a-t-il poursuivi . " Dans la poche intérieure gauche se trouvaient notamment ses états de service avec une photo " , poursuit-il . -" Cependant , certains États ont refusé d' envoyer ces mêmes cartes à des conjoints de même sexe dans les établissements de la Garde nationale qu' ils ont mis en place dans cette région , les accusant , eux aussi , d' enfreindre la loi fédérale et le principe d' égalité . " Mais certains États refusent de délivrer ces cartes aux conjoints de même sexe dans les installations de la Garde nationale " implantées sur leur territoire , a-t-il dénoncé , reprochant à ces États de violer la loi fédérale et le principe d' égalité . -" Cependant , nous continuerons d' accepter les situations d' urgence " , a-t-il souligné , insistant sur la présence permanente des véhicules de service médical d' urgence sur le site afin de transporter des cas critiques . " Mais nous continuerons à accueillir des patients en urgence " , , faisant valoir la présence permanente de véhicules du sur place pour transférer les cas graves . -" J' espère que le paquet d' aide du gouvernement fédéral contribuera grandement à faire passer le système de santé publique de l' État à la liste critique " , a déclaré M. . « J' espère que le plan d' aide fédéral contribuera grandement à retirer le système de santé public de Tasmanie de la liste noire » , a déclaré M. . -" Je ne dis pas " , Gerard de Villiers a dit dans sa défense : " Je ne fais que des conjectures basées sur des pays que je connais bien et , à l' occasion , certaines de mes régressions sont vraies " . Je ne suis pas , se défendait Gérard de Villiers , je fais simplement des hypothèses à partir de pays que je connais bien et , de temps en temps , certaines de mes hypothèses se réalisent . -" Je ne suis pas sûr quand il dort " , raconte Katie O' Malley , un étudiant en médecine de deuxième classe de l' Université de à Philadelphie et bénévole du groupe . « Je ne sais pas quand il dort » , a déclaré Katie O' Malley , une étudiante en médecine en deuxième année de l' université à Philadelphie et bénévole au sein du groupe du Dr . -" Je suis passé d' un château à un autre " . Bernard l' a dit ironiquement , il est familier non seulement avec les habitudes des six présidents qu' il a , mais aussi avec ceux de ses épouses et de certains de ses illustres hôtes . " Je suis passé d' un château l' autre ! " , Bernard , qui connaît non seulement les des six présidents pour lesquels il a , mais aussi de leurs épouses et de certains de leurs illustres invités . -" Je suis frustré et comme quelqu' un , dit-elle . « Je suis aussi frustrée et irritée que n' importe qui » , a-t-elle ajouté . -" Je suis d' avis qu' il existe une base suffisante pour une enquête officielle " , a-t-il déclaré . « Je considère qu' il existe une base suffisante » pour ouvrir une enquête officielle , a-t-il déclaré . -" Je suis heureux que les États membres aient accepté de lancer et de prendre le contrôle d' un processus visant à établir des objectifs universels de développement durable - " , a-t-il dit . « Je suis heureux que les États membres aient accepté de lancer et de s' approprier le processus visant à fixer des objectifs de développement durable universels ( ) » , a-t-il ajouté . -" Je suis si heureuse lorsque je fais ce travail " , dit . « Je suis si heureux quand je fais ce travail » , a ajouté M. . -" J' ai choisi une carte de la France comme logo " , ajoute-t-il . J' ai choisi une carte de France comme logo , justifie t-il . -" Je trouve merveilleux qu' en Amérique , le mythe et le folklore aient déjà une place dans l' éducation " , a-t-il dit . « Je trouve formidable qu' en Amérique , les mythes et le folklore fassent déjà partie de l' éducation » , a-t-il dit . -" Je suit mes dossiers ( sur l' Afghanistan , la Syrie , etc. ) sans tarder " , a ajouté Marc de Villiers . " Je suis en permanence mes dossiers ( Afghanistan , Syrie , ... ) avant de partir " , ajoutait Gérard de Villiers . -" Je suis sorti et je suis allée enlever les gros blocs qui le couvraient . Je suis débarqué et je suis allé enlever les grosses briques qui le recouvraient . -" J' ai porté un fusil noir SS à deux fois par semaine pendant trois ans " , affirme Paul Dutton , expliquant qu' il souffrait de " troubles mentaux " . " Je porte un noir SS deux fois par semaine depuis trois ans chez " , a affirmé Paul Dutton , expliquant souffrir de " problèmes mentaux " . -" J' espère que le président nous écoutera , et nous l' invitons à rejeter ce projet de loi et à le renvoyer aux députés " , a-t-il dit . « J' espère que le président nous écoutera , et nous lui demandons de rejeter ce projet de loi et de le renvoyer devant les parlementaires » , a-t-il ajouté . -" Je savais que je n' étais pas une fille " , a-t-il dit . « Je savais que je n' étais pas une fille » , a-t-il déclaré sur -" Je sais que les valeurs de l' égalité que je défends dans mon manifeste sont liées à la gauche , mais si les gens de la droite sont prêts à me soutenir , je les aussi " , . Je sais bien que les valeurs d' égalité que je prône dans mon programme sont rattachées à la gauche mais si les gens de droite sont prêts à me soutenir , je les aussi , poursuit-il . -" Je n' ai jamais prétendu être un auteur de grande littérature " , a expliqué Gérard de Villiers . " Je n' ai jamais eu la prétention d' être un auteur littéraire " , expliquait Gérard de Villiers . -" Je salue le courage et le courage dont vous avez fait preuve dans le cadre de la mission la plus ardue jamais entreprise par cette organisation " , a déclaré le directeur général de la sécurité , Ahmet , dans les commentaires de l' OIAC . « Je salue la force morale et le courage dont vous avez tous fait preuve dans le cadre de la mission la plus difficile jamais entreprise par cette organisation » , a déclaré le directeur général de l' organisation Ahmet , dans un commentaire publié par l' OIAC . -" Je passe beaucoup de temps à rassurer les parents -- en général , cela ne témoigne pas d' une progression rapide vers la puberté " , dit . « Je passe beaucoup de temps à rassurer les parents – en général , cela n' indique pas une évolution rapide vers la pleine puberté » , a déclaré . -" Je me considère comme un conteur qui écrit pour divertir les gens , ne lui envoie pas de message . Je me considère comme un conteur qui écrit pour distraire des gens à qui je n' envoie pas de message . -" Je lui ai dit que je n' étais pas prêt " , a-t-il déclaré lors d' une entrevue avec La Presse canadienne mercredi . " Je leur ai dit que je n' étais pas prêt " , a-t-il affirmé lors d' une entrevue avec La Presse Canadienne mercredi . -" Je veux que nous notre " , a-t-il dit . « Je veux que nous nous sur notre talent » , a-t-il déclaré . -" J' ai pu porter le costume que j' avais prévu l' année dernière " . Moi , j' ai pu enfin mettre le costume que j' avais prévu l' an dernier . -" Je dans ma voiture quand j' ai vu le mur , créant un énorme nuage de poussière " , a déclaré Sylvain Jean , qui vit près du site . Je en voiture et j' ai vu le mur s' écrouler avec un gros nuage de fumée , a dit Sylvain Jean , qui demeure à proximité du chantier . -" Je quand j' ai vu une femme qui semblait très perturbée . Je faisais la queue quand j' ai vu une femme qui semblait bouleversée . -" J' ai eu l' occasion de discuter d' une foule de choses , et c' est l' une d' elles " , a dit M. au journal . « J' ai dit que j' avais examiné toute une série de questions , et c' était l' une d' elles » , a déclaré au journal . -" Je dirais même plus loin " , a ajouté le directeur général , Hélène , avec un sourire . " Je dirais même au-delà " , a ajouté la directrice générale , Hélène , en souriant . -" Si un client dépense trente secondes de moins par rapport à la biométrie , c' est beau " , dit le directeur d' un grand magasin . " Si , grâce à la biométrie , un client reste en caisse trente secondes de moins , c' est appréciable " , explique le directeur d' une grande surface . -" Si l' accent était mis sur les soins palliatifs au Canada , l' appel à l' euthanasie deviendrait minime " , a-t-elle ajouté . Si on mettait l' accent sur les soins palliatifs au Canada , l' approche de l' euthanasie deviendrait minime . -" Si tous les malades avaient accès à des soins efficaces pour soulager leur souffrance , en plus de pouvoir demeurer chez eux , ils auraient très peu de chances de terminer leur vie " , a déclaré un porte-parole de l' Association canadienne des soins palliatifs , Maryse . Si tous les malades avaient accès à des soins efficaces pour apaiser leurs souffrances , en plus de pouvoir rester à la maison , très peu d' entre eux voudraient mettre fin à leurs jours , selon une porte-parole de l' Association canadienne des soins palliatifs , Maryse . -" Si d' autres supporters veulent se rendre à Paris par d' autres moyens , la police française a indiqué que des mesures ont été prises pour permettre l' arrestation et la prise en charge de personnes " , a ajouté Mme Marina Verbeke . " Si d' autres supporters avaient une intention de se rendre à Paris par d' autres moyens , la préfecture de police française a fait savoir que des mesures ont été prises permettant de procéder à des arrestations voire des gardes à vue " , a encore annoncé Marie Verbeke . -" Si le ministre ne prend pas un lundi , nous prendrons une approche différente " , Christophe , médecin d' urgence et membre de la HSE . Si la ministre ne prend pas de mesure d' ici lundi , nous allons changer de , prévient Christophe , médecin et membre de la CGT Santé . -" Si le véhicule est percuté à l' arrière , l' appuie-tête ne peut pas protéger adéquatement les occupants , augmentant le risque de blessures " , selon l' avis affiché sur le site Web de l' Administration nationale de la sécurité routière . « Si le véhicule est percuté par l' arrière , il se peut que l' appuie-tête ne protège pas correctement les passagers , augmentant ainsi le risque de blessures » , selon la déclaration affichée sur le site de la National Highway Traffic Safety Administration ( NHTSA ) . -" Dans le domaine de l' aérospatiale , les résultats étaient conformes à notre orientation , mais la faible prise de commandes et les conditions générales du marché étaient décevantes " , a déclaré M. Beaudoin . « En Aéronautique , les résultats étaient conformes à nos prévisions , mais le faible nombre de commandes et les conditions générales du marché ont été décevants » , a déclaré M. Beaudoin . -" Dans certains cas , je reconnais , comme le président , que certaines de ces activités se sont et que nous ne garantissons pas que cela sera possible à l' avenir " , a déclaré John Kerry lors d' une conférence à Londres où il a pris part à Washington via un lien vidéo le jeudi 31 octobre . " Dans certains cas , je vous le concède , comme l' a fait le président , certaines de ces actions sont allées trop loin et nous allons nous assurer que cela n' arrive plus à l' avenir " , a déclaré John Kerry lors d' une conférence à Londres à laquelle il participait depuis Washington jeudi 31 octobre par liaison vidéo . -" Dans le passé , les cibles étaient les missiles transférés au Hezbollah " , a-t-il ajouté . " Par le passé , les cibles ont été des missiles transférés au Hezbollah " , s' est-il contenté d' ajouter . -" Dans ce cas , j' ai infligé des dommages à et à l' ensemble de l' industrie des médias d' information et de sa capacité à gagner la confiance du public " , a déclaré la chaîne de télévision publique . « Dans cette affaire , j' ai nui à ainsi qu' à l' industrie des médias et à sa capacité à gagner la confiance du public » , a-t-il déclaré au réseau télévisé public CCTV . -" Inside Davis " par les frères , le dernier Grand Prix du Festival de Cannes , est un concours de comédie historique qui s' est tenu dans le village de en 1840 et qui se fonde sur la musique folklorique , qui s' est fait juste avant l' arrivée de Bob Dylan . " Inside Davis " des frères , Grand prix du dernier Festival de Cannes , est un film nostalgique et drôle sur le Greenwich village de 1961 et la musique folk qui commençait tout juste à y résonner , avant l' arrivée de Bob Dylan . -" Inside Davis " raconte l' histoire d' une semaine de tribulations pour un chanteur folk qui n' a pas réussi une pause et est en colère contre le monde entier . " Inside Davis " raconte sur une semaine les tribulations d' un chanteur de folk qui n' arrive pas à percer et se avec la terre entière . -" with and est la deuxième solution pour les enfants " , souligne Ken Albert , vice-président des produits numériques au groupe Disney / ABC Television Group , dans une citation dans le communiqué de presse . " avec les smartphones et les tablettes est une seconde nature pour les enfants aujourd' hui " , a commenté Albert Cheng , vice-président chargé des produits numériques chez Disney / ABC Television Group , cité dans le communiqué . -" C' est toujours le chef d' État ou son partenaire ou épouse qui choisit le menu " , . " C' est toujours le chef de l' Etat ou sa compagne ou épouse qui choisit les menus " , . -" C' est manifestement dû à l' avertissement du profit de Nissan " , a déclaré un représentant commercial d' un courtier de Paris . " C' est clairement le profit warning de Nissan " , dit un responsable du trading d' un courtier parisien . -" Ce n' est pas un procès " , a déclaré un militant des Frères musulmans . Ce n' est pas un procès souligne ce militant des Frères musulmans . -" Ce n' est pas facile " , dit . « Ce n' est pas facile » , ajoute M. . -" Il est évident que c' est un moment inquiétant , mais nous avons hâte de la ramener chez nous en toute sécurité au plus vite " , dit . « Nous avons évidemment vécu un moment , mais nous espérons la ramener à la maison en toute sécurité dès que possible » , a déclaré . -" Il est de la plus haute importance que les intérêts des citoyens européens en matière de sécurité pour les Européens soient maintenus avec nous dans le cadre de nos efforts mutuels en matière de lutte antiterroriste " , a déclaré M. Kerry , président et président de la section des Affaires étrangères du Sénat sur les affaires européennes , dans une interview de Washington . « Il est vraiment important pour la protection de la sécurité nationale aux États-Unis que les Européens restent à nos côtés dans l' engagement mutuel dans la lutte contre le terrorisme » , a déclaré Murphy , un démocrate servant son premier mandat , et président de la sous-commission des affaires européennes du Sénat américain , lors d' un entretien depuis Washington . -" Il s' agit de deux grands hommes ( et ) main main " , dit Joseph , professeur à l' Open University et expert en Mozambique . « Ce sont deux grands hommes ( et ) qui n' arrêtent pas de se disputer » , a expliqué Joseph , un conférencier de l' université ouverte et spécialiste du Mozambique . -" Il amène les enfants à prendre la décision et retire l' anxiété qui motive les parents parce qu' ils ne sentent pas qu' ils font ce qu' il faut " , dit-elle . « Cela met les enfants au cœur de la décision et atténue le sentiment d' anxiété qui motive les parents parce qu' ils ont peur de ne pas faire ce qu' il faut » , a-t-elle déclaré . -" C' est un endroit effrayant et c' est une question à défendre : qu' est-ce qui empêche le parlement de simplement l' indépendance de la justice ? " , a déclaré le projet de loi inconstitutionnel . « C' est un pays effrayant et il est légitime de se poser la question suivante : qu' est-ce qui empêchera le Parlement de balayer l' indépendance du système judiciaire demain ? » , a déclaré le journal en contestant le projet de loi et demandant à ce qu' il soit déclaré inconstitutionnel . -" Il est toujours préférable d' examiner , de consulter et de trouver des solutions à ces problèmes " , dit Mme . " C' est toujours mieux de s' en parler , de se concerter et de trouver des solutions " , estime Mme . -" Il est de plus en plus improbable que la stratégie du gouvernement permette de garantir suffisamment de devises étrangères pour répondre à ses engagements financiers extérieurs accrus " , explique l' agence américaine . " Il est de moins en moins probable que la stratégie du gouvernement permette de garantir suffisamment de devises étrangères pour faire face à ses besoins de financement extérieurs élevés " , explique l' agence américaine . -" Il ne s' agit pas de quelque chose que nous pourrions faire " , a déclaré Hasan , directeur exécutif du de la Chine du sud des gouvernements , qui a l' intention de l' État de commencer à franchir les parcourus par tous les Nord-Américains d' ici 2025 . « Ce n' est pas comme si nous avions le choix » , a déclaré Hasan , directeur général de la Southern California Association of Governments , qui prévoit que l' État commence à enregistrer les miles parcourus par chaque automobiliste californien d' ici 2025 . -" Ce n' est que des partis extrémistes que je ne vais pas parler " . Il n' y a qu' aux partis extrémistes que je parle pas . -" Il peut s' agir d' une conversation " , a déclaré . « Cela peut vouloir dire avoir une conversation plus tôt que prévu » , a expliqué . -" Il est vraiment temps de donner de la souplesse aux compagnies aériennes et de leur permettre de faire les choses qu' ils croient devoir faire pour réussir " , a expliqué Andrew , de Boeing . « Il s' agit vraiment d' apporter de la souplesse aux compagnies aériennes et de leur permettre de faire ce qu' elles pensent devoir faire pour mener à bien leurs activités » , a déclaré Kent , en charge de la satisfaction passagers chez Boeing . -" Il varie d' un quart à l' autre face à une concurrence très vive " . Ça varie d' un trimestre à l' autre avec une concurrence très vive . -" C' était un choc " , se souvient d' une femme dont la fille a commencé sa période de dix ans . « Ça a été un choc » , se rappelle une femme dont la fille a eu ses premières règles à 10 ans . -" Il sera intéressant de voir si le nouveau pape proposera des cardinaux du reste du monde pour rétablir l' équilibre " , a-t-il déclaré . « Il sera intéressant de voir si le nouveau pape nommera des cardinaux du reste du monde pour restaurer l' équilibre » , a-t-il ajouté . -" La Nouvelle Star a fait un grand bond en avant en termes de population , avec 1,3 million de téléspectateurs , ce qui représente une part de 5,6 % du lectorat . La Nouvelle Star a réussi un retour en force , côté audiences , avec 1,3 millions de téléspectateurs derrière leur poste de télé , soit 5,6 % de part d' audience . -" La Royale " - la marine française - vient de confirmer que le navire sera " indisponible jusqu' à la mi-novembre " afin de " laisser le temps nécessaire à l' exécution des mesures correctives nécessaires " sur le réacteur . La Royale vient en effet de nous confirmer " l' indisponibilité " du navire jusqu' à " la mi-novembre " , " le temps de prendre les mesures correctrices qui s' imposent " sur le réacteur . -" L' an dernier , il y avait beaucoup plus de gens et il n' y avait plus de pharmaciens disponibles pour les remplacer , ce qui était plus difficile " . La dernière année , on en a eu beaucoup plus et on n' a pas de pharmaciens qui sont disponibles pour faire des remplacements , donc c' était plus difficile . -" Les agents de la force publique sont sur le terrain " , a déclaré l' aéroport sur son compte Twitter , alors que des images télévisées montraient que les gens étaient évacués dans des ambulances . " Les forces de l' ordre sont sur place " , précise l' aéroport sur son compte Twitter , alors que les images des télévisions montraient des personnes évacuées dans des ambulances . -" Le Nouvel Observateur " interviewé Jean-Louis , ancien journaliste senior qui a pris sa retraite en 2008 et est président de l' association " du Monde " . " Le Nouvel Observateur " a questionné Jean-Louis , 62 ans , ex grand reporter , retraité depuis 2008 , président de l' association " du monde " . -" à eux-mêmes , les politiciens le pays et nous à la chasse et à la cueillette " , a-t-il écrit . « à eux-mêmes , les hommes politiques mettraient le pays en faillite et nous à la chasse et à la cueillette . » -" Je tiens à dire directement à ces Américains : vous méritez mieux " , a déclaré lors d' un témoignage devant la commission de l' énergie et du commerce de Washington . « Permettez-moi de m' adresser directement à ces Américains : vous méritez mieux » , a déclaré Kathleen lors de son témoignage devant la commission de l' énergie et du commerce à Washington . -" Lou et moi avons passé beaucoup de temps ici ces dernières années . Lou et moi avons passé beaucoup de temps ici ces dernières années . -" Les déclarations sur les " étrangers qui arrivent à la queue " et les emplois " " sont quelque peu à un moment où les syndicats ont demandé un marché du travail protégé , a déclaré aujourd ' hui à l' Institut australien d' Inde . « Les déclarations sur les " étrangers retournant au bout de la file " et " des emplois pour les Australiens d' abord " sont un retour désagréable à l' époque où les syndicats demandaient un marché du travail protégé » , a-t-elle dit à l' Australia India Institute aujourd' hui . -" Mme Chirac était un peu plus impliquée et dans la cuisine " , au point où elle a interdit aux femmes d' entrer dans les cuisines du Palais de l' Elysée ! " Mme Chirac était un peu plus impliquée , elle venait directement en cuisine " , au point qu' elle avait interdit les femmes dans les cuisines de l' Elysée ! -" L' argent a présenté le cas comme un cas de transition réussie , mais ce n' est pas le cas " , a déclaré M. . « Money a présenté cela comme un cas de transition réussi , mais ce n' était pas le cas » , a déclaré . -" La plupart de ces sont apparus à et sont allés plus loin , au point où ils sont trop grands pour jouer un club " , a déclaré Hirsch . « La plupart des têtes d' affiche ont émergé au , et ont connu un grand succès , jusqu' à ce qu' ils soient devenus trop célèbres pour jouer dans un club » , a déclaré Hirsch . -" Les automobilistes seraient incités à participer en raison de la valeur des avantages qu' ils offrent " , a déclaré un document de planification de la ville . « Les automobilistes seraient incités à participer en raison des avantages que le système leur offre » , a indiqué un document sur l' aménagement urbain . -" Le corps de Muller a été retrouvé en août 1945 par un commando dans une fosse temporaire près de l' ancien ministère de l' aviation du Reich " , dit M. . " Dès août 1945 , le corps de Müller a été retrouvé dans une tombe provisoire près de l' ancien ministère de l' Aviation du Reich par un commando " , selon M. . -" Mes premiers cours de musique m' ont été donnés par mon grand-père qui m' a appris à jouer la guitare " , a-t-il ajouté . " Mes premières leçons de musique m' ont été données par mon grand-père qui m' a appris à jouer de la guitare " , avait-il ajouté . -" Mon mari et moi , je viens d' acheter une maison , de sorte que tout mon argent est bloqué " , dit Mme . « Mon mari et moi venons d' acheter une maison donc tout mon argent est parti dedans » , déclare Mme . -" Ma principale proposition est de créer un ministère du Multiculturalisme " , dit-il . Ma proposition n ° 1 est de créer un ministère du multiculturalisme , indique t-il . -" La gestion éditoriale de New Express a été désordonnée " , a déclaré l' organisme de réglementation dans une déclaration . « La rédaction de New Express était » , a dit le régulateur dans une déclaration . -" Non , M. a répondu . « Non » , a répondu M. . -" Personne ne se soucie de et de , ils veulent juste la paix , ils veulent un accès libre aux routes " , a-t-il dit . « Personne ne se soucie de la ni du , ils veulent juste retrouver la paix , ils veulent avoir un accès libre aux routes » , a-t-il ajouté . -" Personne ne saura jamais " , écrivait le journaliste de la nation , décrivant les médias kényans comme une importante source de contrepoids dans la vie publique . « Personne ne saura jamais » , a écrit , journaliste à The Nation , en décrivant les médias kenyans comme une source de contrôle clé dans la vie publique . -" Personne ne nous dit : " Je veux mourir , me donner une injection " . On ne nous demande jamais : " je veux mourir , une injection " . -" Ne tenant pas compte du fait que les clients de la région de Bruxelles n' aient pas d' argent dans ce domaine en vue de la mise en place d' un de lumière aux couleurs de la région bruxelloise , Brigitte poursuit ses efforts pour transformer la capitale flamande " , a déclaré une dame de De dans un communiqué de presse . " Non content de dépenser l' argent des dans des opérations de des poteaux de signalisation aux couleurs de la Région bruxelloise , Brigitte continue ses opérations de de la Capitale " , s' insurge M. De dans un communiqué . -" Maintenant , M. Francis a choisi ces huit cardinaux pour l' aider " , a déclaré M. . « Désormais , François a choisi ces huit cardinaux pour qu' ils l' aident » , a expliqué . -" Le 4 novembre , les patients ne seront plus amenés par la brigade des pompiers , ce qui signifie qu' ils n' arriveront plus que par leurs propres moyens " , confirme le Professeur Jean-Yves , chef de la direction médicale du nouvel . " Le 4 novembre , il n' y aura plus de patients amenés par les pompiers , uniquement des personnes venant par leur propre moyen " , confirme le professeur Jean-Yves , le responsable médical du nouvel . -" Une fois qu' une compagnie aérienne vérifiera la tolérance de sa flotte , elle peut permettre aux passagers d' utiliser des appareils électroniques légers et portatifs comme des comprimés , des et des toutes les altitudes " , a déclaré la FAA . « Dès qu' une compagnie aérienne a vérifié la tolérance de sa flotte , elle peut autoriser les passagers à utiliser des appareils électroniques portables légers , comme des tablettes , des lecteurs de livres électroniques et des smartphones à toutes les altitudes » , a déclaré la FAA . -" Une fois que les seins commencent à se développer , il faut au moins deux ou trois ans avant les premières règles " , dit , auteur du " Early in Girls : The Essential Guide to This Common Problem " . « Une fois que la poitrine a commencé à se développer , il faut au moins deux ou trois ans avant que les premières règles apparaissent » , a indiqué , également l' auteur de « Early in Girls : The Essential Guide to with This Common Problem » . -" Une fois la nouvelle politique mise en oeuvre de façon sécuritaire - et nous allons travailler étroitement avec le transporteur pour le faire - nous aurons une chance " , a déclaré M. dans une déclaration . « Une fois la nouvelle politique mise en œuvre en toute sécurité – et nous allons travailler en étroite collaboration avec les transporteurs pour cela – ce sera profitable à tout le monde » , a expliqué dans une déclaration . -" Notre politique actuelle est que les appareils électroniques ne peuvent pas être utilisés au décollage et à l' atterrissage et nous n' avons pas de plan immédiat pour le changer " , a-t-il dit . « Conformément à notre politique actuelle , les appareils électroniques ne peuvent pas être utilisés pendant le décollage et l' atterrissage , et nous n' avons pas l' intention dans l' immédiat de changer cela » , a-t-elle déclaré . -" Notre enquête en cours sur cette question se poursuit et nous coopérons pleinement avec la CAF et les autres organismes de réglementation " , a déclaré la banque il y a deux semaines . « L' enquête en cours sur cette affaire se poursuit et nous coopérons pleinement avec la FCA et les autres régulateurs » , a déclaré un représentant de la banque il y a deux semaines . -" Notre vision est de revenir aux négociations , mais avec beaucoup de sérieux " , a déclaré M. . « Nous voulons reprendre les négociations , mais avec le plus grand sérieux » , a indiqué M. . -" Le comportement parental est assez stressant avec des limitations supplémentaires , surtout si tu ne connais pas le sexe de ton enfant " , dit-elle sur le site Web des parents . « Le rôle de parent est suffisamment stressant sans restrictions supplémentaires , en particulier si vous ne connaissez pas le sexe de votre enfant » , a-t-elle déclaré sur le site destiné aux jeunes parents . -" Les gens sont heureux . Les gens sont contents . -" Les gens pensent que les responsables de l' avenir du pays sont le gouvernement et le président , et il devrait être le responsable de trouver des solutions aux problèmes " , dit-il . « Les gens pensent que les responsables de l' avenir du pays sont le gouvernement et le président , et que c' est à ce dernier de trouver des solutions aux problèmes » , a-t-il expliqué . -" Les gens seront plus disposés à le faire si vous ne suivez pas leur rythme et que vous ne suivez pas leur route " , indique Ryan Morrison , directeur général de . « Les gens seront plus enclins à faire ça si vous n' enregistrez pas leur vitesse et ne les pas » , a déclaré Ryan Morrison , directeur général de True . -" Le plan d' acheter Goodyear commence par zéro , a déclaré le CEO de Titan ... Pour le PDG de Titan , " le projet d' achat de Goodyear Amiens démarre avec zéro employé " -" Le Pape a décidé de faire part de sa décision de convoquer les de février à afin de faciliter la planification d' autres réunions impliquant la participation de de différentes parties du monde " , a déclaré . « Le pape François a décidé de communiquer sa décision de convoquer le de février à l' avance afin de faciliter la planification des autres réunions nécessitant la participation des cardinaux de différentes parties du monde » , a déclaré . -" Peut-être " , répondit Martin . « Peut-être » , a répondu Martin . -" Le traitement préventif pour la rage humaine administré après le contact avec l' animal , bien que les symptômes soient très efficaces , est indiqué dans le communiqué de presse . " Chez l' homme , le traitement préventif de la rage humaine , administré après le contact avec l' animal porteur , mais avant l' apparition des symptômes , est très efficace " , précise le communiqué . -" probablement entre 120 et 150 millions , si vous incluez tous les pays " , a-t-il déclaré en mars dernier . " Sans doute entre 120 et 150 millions tous pays confondus " , en mars dernier . -" La rage est une maladie mortelle si elle n' est pas traitée rapidement " , a rappelé les ministères , et elle peut être transmise au cours de la seconde quinzaine avant même les premiers symptômes de la maladie . " La rage est une maladie mortelle si elle n' est pas traitée à temps " , rappellent les ministères et elle est transmissible pendant près de 15 jours avant l' apparition des premiers symptômes de la maladie . -" Des médecins d' urgence de haut rang resteront sur place " , lui aussi . Des médecins seniors resteront sur place , également . -" Partager ce qu' est la culture québécoise , par exemple , le sentiment d' appartenance , le soutien aux personnes qui vivent des changements que nous vivons dans l' organisme étudiant " , . " Sur la transmission , par exemple , de ce qu' est la culture québécoise , le sentiment d' appartenance , le soutien au personnel dans les transformations qu' on vit , au sein de la clientèle étudiante " , . -" La signature de l' accord serait bénéfique pour les entreprises à long terme , mais elle pourrait avoir des conséquences négatives à court terme en réponse à la réaction de la Russie " , a expliqué M. Fernandes et , à ce sujet , que Moscou pourrait introduire des " restrictions commerciales " . " l' accord serait positif pour le commerce à long terme mais il pourrait y avoir des conséquences négatives à court et moyen termes liées à la réaction de la Russie " , a expliqué S & P , qui craint des " restrictions commerciales " de la part de Moscou . -" Comme la viande de cheval n' est pas mentionnée dans la liste des ingrédients , elle n' aurait pas dû être présente dans le produit " , a expliqué l' agence britannique . " Comme la viande de cheval n' est pas mentionnée dans la liste des ingrédients , elle n' aurait pas dû être présente dans le produit " , a expliqué l' agence britannique . -" Les petites et moyennes entreprises manquent souvent d' expérience , de personnel et de ressources financières pour protéger les secrets d' entreprise contre l' accès non autorisé " , a souligné le dans un rapport . « Les PME manquent souvent de l' expérience , du personnel et des ressources financières pour protéger efficacement leurs secrets industriels contre un accès non autorisé » , avertit le dans un rapport . -" Certaines personnes ont des conditions qui nécessitent une intervention chirurgicale , mais la plupart des enfants ne le font pas " , dit-il . « Certaines personnes souffrent de maladies extrêmement graves qui nécessitent un acte chirurgical , mais ce n' est pas le cas de la plupart des enfants » , a-t-il ajouté . -" Certaines femmes dans mes livres sont des objets sexuels , d' autres sont de belles , intelligentes femmes courageuses . Certaines femmes sont des objets sexuels dans mes livres mais d' autres sont des femmes belles , intelligentes et courageuses . -" Il ne tient pas compte des publications de pirates " , a-t-il ajouté , soulignant un tas de livres sur la table basse , extraits des artefacts de bronze et d' ivoire provenant des 130 pays qu' il avait étudiés pour les réglages de ses romans . " Sans compter les éditions pirate " , , désignant une pile de livres sur la table basse entre les ou les rapportés des 130 pays pour y situer ses romans . -" Ce n' est pas le cas " , a déclaré M. , président de la société allemande de logiciels SAP . « Ce n' est pas la bonne façon de faire » , indique , président de la société de logiciels d' entreprise allemande , SAP . -" Cela fait partie de l' enquête " , dit Jacqueline , porte-parole de l' ASFC . Ça fait partie de l' enquête , a indiqué Jacqueline , porte-parole de l' ASFC . -" Cet appui se poursuivra pendant les six prochains mois " , explique le directeur régional des relations de travail dans les hôtels Delta , Felix . " Ce va continuer pour les six prochains mois " , explique le directeur régional des relations de travail des Hôtels Delta , Félix . -" Le Groupe et le Groupe ont annoncé aujourd' hui l' expiration de la période d' examen de la prétendue fusion du Groupe et , par le biais de la Antitrust Act de 1976 , telle que modifiée , les deux groupes annoncés dans un communiqué de presse . " Group et Groupe ont annoncé aujourd' hui l' expiration du délai d' examen de la fusion précédemment annoncée de Groupe et , prévu par le Antitrust Act of 1976 , tel qu' amendé " , annoncent les deux groupes dans un communiqué . -" Le semble proche de sa fin " , a annoncé un expert dans les affaires congolaises , Jason Stearns , sur son blog Congo . " Le semble toucher à sa fin " , prédit un expert des affaires congolaises , Jason Stearns , sur son blog Congo . -" Les devaient revenir à environ 5 heures " , a déclaré Thierry , consultant technique auprès de l' organisation qui a organisé les opérations de sauvetage . " Les auraient dû ressortir aux alentours de 5h du matin " , a dit Thierry , conseiller technique au secours en charge de l' organisation des secours . -" La Commission a proposé un service de médiation et j' ai été intéressée " , a déclaré Mme . " Il y a un service de médiation qui est proposé par la commission et j' étais intéressée " , explique Mme . -" La Fédération allemande des journalistes recommande aux journalistes d' éviter d' utiliser Google et Yahoo Search et les services de messagerie jusqu' à nouvel avis " , a déclaré dans un communiqué de presse . " La Fédération allemande des journalistes recommande aux journalistes d' éviter jusqu' à nouvel ordre d' utiliser les moteurs de recherche et les messageries de Google et Yahoo " , dit-elle dans un communiqué . -" Le gouvernement a présenté un plan de sauvetage d' urgence qui nous permettra de relever les défis uniques auxquels est confronté l' État " , a déclaré Mme aujourd ' hui . « Le gouvernement a proposé un plan de sauvetage d' urgence qui , selon nous , règlera les difficultés uniques auxquelles l' État est confronté » , a déclaré Mme aujourd' hui . -" Le service d' urgence d' doit fermer le plus tôt possible et , pour nous , le 4 novembre " , Loïc , président du comité médical ( CCS ) au qui soutient le projet de gestion , dit sans détour . " Le service d' accueil des urgences de l' doit fermer dans le plus bref délai , et pour nous , c' est le 4 novembre " , assure sans détour Loïc , le président de la commission médicale d' établissement ( CME ) de l' , qui soutient le projet de la direction . -" Les progrès dans le sens de la critique , l' augmentation du nombre de caméras de surveillance et le nombre considérable de photos diffusées sur Flickr , Facebook ou me font craindre le pire : une surveillance accrue " , a déclaré Andrew Patrick du Commissariat à la protection de la vie privée du Canada . " Les progrès de la reconnaissance faciale , la multiplication des caméras de surveillance et l' énorme quantité de photos disponibles sur Facebook , Flickr ou , me font craindre le pire : une surveillance généralisée " , prédit Andrew Patrick , du Commissariat à la protection de la vie privée au Canada . -" Les alarmes ont décollé une fois l' autre parce qu' il y a eu des renforts pour s' occuper de l' immeuble , parce que les bâtiments ici sont proches " , a déclaré Christian , porte-parole du service de lutte contre le feu du Québec . " Alors les alarmes se sont succédé , parce que ça prenait du renfort pour attaquer le bâtiment , parce que c' est des bâtiments collés ici " , a expliqué France , porte-parole des pompiers de Québec . -" Le processus d' embarquement était le plus agréable que j' avais observé dans ma carrière de transporteur aérien " , a-t-il dit . « Je n' avais jamais vu une procédure d' embarquement aussi fluide de toute ma carrière professionnelle » , . -" La ville est incontestablement dans l' univers de l' humour de tous les travaux de Larry " , a déclaré Steinberg . « La ville est définitivement dans l' ADN comique de tout le travail de Larry » , a déclaré Steinberg . -" Le convoi d' autobus sera escorté par la police d' un ancien poste frontière à au stade du PSG " , a déclaré le porte-parole . " Le convoi de bus sera escorté par la police depuis un ancien poste frontière de jusqu' au stade du PSG " , a transmis la porte-parole . -" La réaction rapide de l' agent de police et son aptitude à désamorcer la situation étaient absolument cruciaux pour mettre fin immédiatement à cet incident " , a déclaré le caporal Jamie Paquet Paquet dans un communiqué de presse . " Les réactions rapides des policiers impliqués et leur capacité de désamorcer la situation immédiatement ont été essentielles à la conclusion sécuritaire de cet incident " , a déclaré le caporal Bert Paquet par voie de communiqué . -" Les combats n' ont pas cessé depuis le matin et se sont poursuivis malgré l' obscurité " , un résident de , selon lequel une jeune fille a été blessée par des tirs au cours de la matinée , a indiqué à l' AFP par téléphone au , près de , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , . " Ça n' a pas cessé depuis ce matin , les combats continuent malgré la nuit " , a indiqué à l' AFP un habitant de , joint par téléphone vers 12h30 , et selon qui une fillette a été blessée par balle dans la matinée . -" Les combats ont légèrement diminué , ce qui semble indiquer que les Forces armées de la République démocratique du Congo ( FARDC ) ont repoussé les rebelles un peu " , a-t-il ajouté . " L' intensité des affrontements a diminué un peu " , a-t-il ajouté , " il semble que les FARDC Forces armées de la RDC ont repoussé un peu les rebelles " . -" La première action n' a eu lieu qu' à trois institutions , et lorsque la décision a été rendue publique , plusieurs personnes m' ont dit : " Nous avons été saisis d' une façon ou d' une autre , a-t-il ajouté , puis-je donner mon nom ? " , a déclaré l' avocat Alain Arsenault , qui défend les victimes dans les deux actions . Le premier recours ne visait que trois établissements , et quand on a annoncé le règlement , beaucoup de gens nous ont dit : " moi , j' étais à tel endroit , est-ce que je peux m' inscrire ? " , raconte l' avocat Alain Arsenault , qui défend les victimes dans les deux recours . -" La taxe sur le gaz n' est tout simplement pas viable " , a déclaré M. Lee , expert en politique des transports à l' Université du Minnesota . « La taxe sur l' essence n' est tout simplement pas tenable » , a déclaré Lee , un spécialiste des politiques de transports à l' université du Minnesota . -" Le groupe qui se cache derrière les événements était le Mouvement islamique du Turkestan oriental " , a-t-il ajouté , ses mots ont été relayés par l' agence Xinhua . " Le groupe qui se tenait en coulisses était le mouvement islamique du Turkestan oriental " , a-t-il ajouté , ses propos étant repris par l' agence Chine nouvelle . -" La loi donne aux parents un peu d' espace pour ne pas se précipiter pour prendre des décisions " , a déclaré . « La loi laisse une certaine latitude aux parents pour qu' ils n' aient pas à prendre de décisions hâtives » , a déclaré . -" La question est devenue exagérée " , pense Joël , directeur de bureau . L' affaire a pris des proportions exagérées , estime Joël , le directeur de l' office . -" La mémoire des victimes est dans la pire des situations " , a déclaré dans le document . " On foule grossièrement du pied la mémoire des victimes " , s' est-il dans le journal . -" La fusion est également subordonnée à l' obtention d' autres autorisations réglementaires et à l' approbation des deux actionnaires du groupe " , ajoute-t-il . " La fusion est également conditionnée à l' obtention d' autres autorisations réglementaires et à l' approbation des actionnaires des deux groupes " , . -" Le ministre nous a menti ou a menti " , a déclaré , médecin d' urgence . " La ministre nous a menti ou on lui a menti " , déplore le médecin Gérald . -" Le seul problème qu' ils rencontrent est celui de la pauvreté " , a déclaré . « Le seul problème qu' ils ont , c' est la pauvreté » , a expliqué le Dr . -" Le problème est le temps et l' argent . C' est un problème de temps et d' argent . -" L' accusation indique que M. , qui est actuellement le rédacteur en chef de l' , n' est pas celui qui est à l' extérieur des maisons populaires en attente de prendre les choses , il est l' homme qui a l' intention de raconter l' histoire pour voir ce qu' il dira " , a déclaré M. . « L' accusation suggère que M. , qui est désormais le rédacteur en chef de , n' est pas le genre d' homme à rester devant la maison des gens en espérant les surprendre , c' est le genre d' homme à aimer présenter l' histoire aux gens pour voir ce qu' ils en disent » , a déclaré M. . -" Le besoin réel est d' avoir différentes stratégies éducatives " , . " Des stratégies pédagogiques différentes , c' est ça le véritable besoin " , . -" La raison pour laquelle Boeing est là , c' est qu' ils plus de sièges afin de rendre leur avion plus compétitif à nos produits " , a déclaré Kevin , chef du confort des passagers de l' Airbus européen . « Boeing fait ça pour pouvoir plus de sièges et rendre ses avions plus compétitifs par rapports à nos produits » , a déclaré Kevin , directeur de Confort Passager chez l' avionneur européen Airbus . -" Nous sommes ici parce que le gouvernement du pays d' origine a sauvé 430 millions de dollars de son système de santé " , a-t-il déclaré à la télévision ABC . « La raison pour laquelle nous sommes ici est que le gouvernement travailliste de l' État a retiré 430 M $ de son système de santé » , a-t-il déclaré sur ABC TV . -" La recherche effectuée par les journalistes est tout aussi confidentielle que les détails de leurs sources et la nature de leur communication avec eux " , a ajouté Michael , président du syndicat , qui compte trente-huit membres . " Les recherches effectuées par les journalistes sont aussi confidentielles que les coordonnées de leurs sources et la nature de leurs communications avec elles " , a ajouté Michael , président du syndicat qui revendique 38.000 adhérents . -" La même chose se produira avec les unités de maternité de niveau 3 . Cela va se passer comme pour les maternités de niveau 3 . -" L' appareil d' essai n' est pas resté au sol plus longtemps que prévu " , a déclaré Beaudoin lors d' une conférence téléphonique , ajoutant que les essais sur le terrain et les mises à jour du programme étaient prévus pendant le temps d' immobilisation de l' avion . « L' avion d' essai n' est pas resté au sol plus longtemps que prévu » , a déclaré M. Beaudoin lors d' une conférence téléphonique , ajoutant que les essais au sol et les mises à jour logicielles étaient prévus pendant le temps d' arrêt de l' avion . -" Le procès du président Morsi est un faux procès " , selon un défenseur respecté de Morsi , " il doit faire preuve de , et non l' inverse " . Le procès du président Morsi est un faux procès s' insurge une , c' est lui qui est censé faire juger , pas le contraire . -" Les deux victimes ont été et traitées pendant un certain temps au centre , avant d' être remises au CHU de Liège , par hélicoptère et par ambulance pour la grand-mère et son petit-fils , respectivement " , a déclaré le maire José lors de la visite du site . " Les deux victimes ont été et longuement conditionnées sur place avant d' être emmenées au CHU de Liège , elle en hélicoptère , lui en ambulance " , a précisé le bourgmestre José , descendu sur les lieux . -" L' incident terroriste violent qui a eu lieu à Pékin a été organisé et prémédité " , a déclaré sur la chaîne de télévision Hong Kong Television Phoenix . " Le violent incident terroriste qui est survenu à Pékin a été organisé et prémédité " , a dit à la chaîne Phoenix TV . -" Il y a plusieurs questions à répondre ou à confirmer " , a-t-il dit . « Plusieurs questions méritent encore une réponse ou une confirmation » , a-t-il indiqué . -" Il y a toujours eu des rats dans les villes . Des rats , il y en a toujours eu dans les villes . -" Il n' y a pas grand espoir que des réformes impopulaires aient lieu dans une année préélectorale " , a déclaré l' expert lors d' une interview de l' AFP . Les élections présidentielles sont prévues pour 2015 . " Il y a peu d' espoir que des réformes impopulaires soient lancées dans une année préélectorale " , les élections présidentielles étant prévues en 2015 , relève l' expert , interrogé à l' AFP . -" Il n' est pas nécessaire d' entreprendre une chirurgie radicale lorsque vous avez besoin de prendre une aspirine " , a déclaré Randy , directeur de la politique et des affaires publiques de la Commission . « Il n' est pas nécessaire de recourir à une chirurgie radicale lorsqu ’ on a tout simplement besoin d ’ aspirine » , a déclaré Randy , le directeur de la législation et des affaires publiques auprès de la commission . -" Il y a eu une fusillade " , a déclaré un porte-parole de la US Transportation Security Administration ( TSA ) auprès de l' AFP . " Il y a eu une fusillade " , a déclaré à l' AFP un porte-parole de l' agence américaine de sécurité des transports ( TSA ) . -" Il n' y avait pas de au Cameroun " , dit Georges . « Il n' y avait pas de au Cameroun » , a déclaré Georges . -" Il y aura beaucoup de larmes cette année " , a déclaré Mme . « Il y aura beaucoup de pleurs cette année » , a dit Mme . -" Ces discussions doivent produire un nouveau type de cadre de référence pour la définition de la nouvelle politique culturelle au Canada concernant la télévision " , a déclaré le président-directeur général de . " Il devra émerger de ces discussions un genre de cadre de référence nouveau pour définir notre nouvelle politique culturelle canadienne en ce qui a trait à la télévision " , a dit le de . -" Ces investissements répondent aux idées que les médecins de première ligne ont dit être les meilleurs moyens de faire face au système de santé de la Montérégie " , a déclaré Mme . « Ces investissements répondent à l ’ idée selon laquelle , d ’ après les cliniciens de première ligne , il s ’ agit du meilleur moyen de soigner le système de santé de Tasmanie » , a déclaré Mme . -" Ils pensent tout à fait que leur fils a été assassiné " , a déclaré Benjamin , avocat représentant Kenneth et Johnson . « Ils sont absolument certains que leur fils a été assassiné » , a déclaré Benjamin , l' avocat représentant Kenneth et Johnson , sur -" Ils battent des tambours toute la nuit pour nous et continuer leur travail " , a déclaré M. . « Ils frappent sur des tambours toute la nuit pour nous tenir afin que nous puissions continuer à travailler » , a expliqué M. . -" Ils sont en train d' abandonner et de détruire des unités d' urgence qui ont été réformées il y a moins de cinq ans " , explique-t-il . " Ils sont en train de vider et d' des urgences qui ont été rénovées il y a moins de cinq ans " , estime-t-il . -" Ils croient que cela va à l' encontre de la logique , des lois de la physique et du sens commun " , a déclaré M. . « Ils pensent que cela défie toute logique , les lois de la physique ainsi que le sens commun » , a ajouté . -" Il s' agit de personnes riches qui ont besoin d' emprunter à court terme pour des entreprises commerciales ou des investissements , ou des petites entreprises qui ont besoin de connaître leurs besoins en liquidités " , dit Mme . « Ce peut être des personnes aisées qui ont besoin d' emprunter à court terme pour des activités commerciales ou des investissements , ou des petites entreprises qui ont des besoins de trésorerie pour passer un cap difficile » , ajoute Mlle . -" Ils ont envoyé leur fils à l' école avec un et ils l' ont retourné dans un sac corporel " , a-t-il dit . « Ils ont envoyé leur fils à l' école avec un et on le leur a rendu dans un sac » , a-t-il ajouté . -" Ils vont savoir s' ils commencent à fumer à nouveau , ils ne peuvent pas se faire plaisir à cause du vaccin de la nicotine , et ils peuvent les aider à attraper l' habitude " , dit-il . « Ils sauront que s' ils recommencent à fumer , ils n' y prendront aucun plaisir en raison du vaccin , et cela pourra les aider à perdre leur mauvaise habitude » , a-t-il ajouté . -" Ils se préoccupent du risque de grossesse ou même de la façon dont ils manipulent l' hygiène " , dit . « Ils craignent le risque de grossesse ou même la façon dont ils vont gérer les questions d' hygiène » , a déclaré . -" Les choses vont beaucoup mieux aujourd ' hui " , insiste le pharmacien . Maintenant , ça va beaucoup mieux , a souligné la . -" Les choses bougent progressivement " , déclare Nicolas , porte-parole de l' ARS , pour qui " il n' y aura pas de changement quant au service offert " viendra le 4 novembre . " Les choses se font progressivement " , répond Nicolas , porte-parole de l' ARS pour qui , le 4 novembre , " il n' y aura pas de changement en termes de service rendu . " -" Il s' agit d' un sujet depuis de nombreuses années et n' a pas fondamentalement changé depuis le débat actuel " , déclare Kurt , chef de la direction de BASF Chemical . « C' est un sujet récurrent depuis de nombreuses années et rien n' a fondamentalement changé dans le cadre de la discussion actuelle » , déclare Kurt , directeur général de la société productrice de produits chimiques BASF . -" Ce n' est pas seulement une taxe dans un trou noir " , a déclaré Adrian Moore , vice-président de la politique à la Raison . « Il ne s' agit pas simplement d' une taxe s' dans un grand trou noir » , a déclaré Adrian Moore , vice-président de la politique à la fondation Reason . -" Cette loi est draconienne et draconienne et nous l' interdit " , a déclaré Cyrus , directeur de la direction du groupe Capital - , l' une des radios et des journaux les plus indépendants du Kenya . « Cette loi est draconienne et très punitive , et nous la refusons » , a expliqué Cyrus , directeur général de Capital Group – propriétaire de , l' une des stations de radio et sites Internet d' actualité indépendants les plus respectés au Kenya . -" Cette mission était essentielle en raison du risque que le tunnel terroriste soit utilisé pour des attaques contre des civils israéliens " , a déclaré le porte-parole de l' armée , Peter Lerner . " Cette mission était impérative en raison du risque d' utilisation du tunnel terroriste pour des attaques contre des civils israéliens " , a indiqué par ailleurs le porte-parole de l' armée Peter Lerner . -" Ce matin , nous sommes revenus à la frontière pour rentrer dans nos champs , mais les militaires nous ont dit de retourner . " L' AFP a été dénoncée par , entourée de ses neuf enfants , dont un bébé sur son dos . " Ce matin nous avons pour aller dans nos champs , mais les militaires nous ont dit de nous replier " , a indiqué à l' AFP , entourée de ses neuf enfants , dont un nourrisson sur le dos . -" Cette lenteur des essais de vol - bien que conforme au calendrier interne de Bombardier - renforce notre vision selon laquelle le sera porté à Q1 / 15 " , a déclaré M. . « Cette lenteur des essais en vol – bien qu' elle soit apparemment conforme au calendrier interne de Bombardier – renforce notre opinion selon laquelle l' entrée en service sera repoussée au premier trimestre de 2015 » , a déclaré M. . -" Cela changera mon avenir avec ma famille " , a dit l' homme . « Cela va changer mon avenir et celui de ma famille » , a déclaré l' homme . -" Cela nous permettra de développer des interfaces plus conviviales et un plus grand nombre d' options " , a expliqué le président et chef de la direction de , Louis , lors d' une entrevue . " Cela nous permettra de mettre en valeur des interfaces plus conviviales ainsi que des options plus nombreuses " , a expliqué le président et chef de la direction de , Louis , en entrevue . -" Il s' agit de l' option la plus logique à long terme " , a-t-il dit . « Cela s' avère être l' alternative la plus logique à long terme » , a-t-il dit . -" Aujourd ' hui , le peuple égyptien a jeté les bases d' une nouvelle vie , d' une liberté absolue et d' une démocratie véritable " , a déclaré Ted , un ancien artisan des Frères musulmans , un groupe fondamentaliste qui a occupé la majeure partie des 84 dernières années depuis sa création comme une organisation illégale par les gouvernements successifs . « Aujourd' hui , le peuple égyptien a posé les fondements d' une nouvelle vie – la liberté absolue , une véritable démocratie et la stabilité » , a expliqué M. Morsi , un ingénieur de 60 ans formé aux États-Unis , issu des Frères musulmans , un groupe fondamentaliste considéré pendant plus de 84 ans depuis sa formation comme une organisation illégale et durement réprimé par les gouvernements successifs . -" Aujourd ' hui , nous menons des recherches sur la reconnaissance de la réalité et de la résolution en utilisant des capteurs acoustiques , mais nous avons besoin d' un environnement silencieux " , dit , directeur de la recherche en sécurité chez Intel Labs . " Aujourd' hui , nous menons des recherches sur la reconnaissance de la voix ou de la façon de marcher à l' aide de capteurs sonores , mais il faut un environnement silencieux " , indique , directeur de la recherche sur la sécurité aux Intel Labs . -" La question a été débattue lors de ces conversations téléphoniques à propos de l' élaboration de la politique de location commerciale " , a déclaré le commissaire adjoint Anthony . « Le sujet débattu au cours de ces conversations téléphoniques était-il l' élaboration de la politique relative aux baux commerciaux ? » , a demandé le Commissaire adjoint Anthony à M. . -" Nous sommes heureux que la FAA reconnaisse qu' une expérience de voyage agréable n' est pas incompatible avec la sûreté et la sécurité " , a déclaré Roger Chabot , directeur général de l' Association des transporteurs du Canada . « Nous sommes heureux que la FAA reconnaisse qu' une expérience passager agréable n' est pas incompatible avec la sécurité » , a déclaré Roger Dow , Président et directeur de l' U.S. Travel Association . -" Nous avons pu dans des interventions chirurgicales où nous le sang ou des outils pour le Dr . " , remarque O' Malley . « On a pu participer à des opérations pour aider à éponger le sang ou faire passer les instruments au Dr » , a expliqué Mlle O' Malley . -" Nous sommes confiants que le trader a agi seul et que la question a été posée " , a déclaré le Crédit Suisse . « Nous sommes convaincus que le trader a agi seul et que l' affaire a été maîtrisée » , a indiqué un représentant du Crédit Suisse . -" Nous rassemblons des images de caméras de surveillance et les avec des photographies de célébrités qui sont disponibles sur Internet " , explique Dany , directeur du développement des entreprises pour les solutions d' identification biométrique à NEC France . " Nous les images des caméras de surveillance et les comparons aux photos des célébrités qui circulent librement sur Internet " , explique Dany , chargé , chez NEC France , du développement commercial pour les solutions d' identification biométrique . -" Nous ne demandons pas au monde de nous soutenir et de nous soutenir , mais nous avons le droit de demander le monde parce que nous en avons fait partie " , a déclaré le à Washington ce jeudi . " Nous ne disons pas au monde d' être à nos côtés et de nous soutenir , nous avons le droit de le demander au monde parce que nous en faisons partie " , a déclaré ce jeudi à Washington . -" Nous recevons 500 personnes à chaque voyage " , a déclaré . « Nous recevons 500 personnes à chaque visite » , a déclaré M. . -" Nous espérons que ce type de stratégie de vaccination aide actuellement les millions de fumeurs qui ont essayé d' arrêter , tout en mettant à l' épreuve toutes les méthodes disponibles sur le marché , mais qui trouvent leur dépendance à la nicotine suffisamment pour résister à ces approches actuelles " , a déclaré M. Cornell . « Nous espérons vraiment que ce genre de stratégie vaccinale pourra aider des millions de fumeurs qui ont essayé d' arrêter , épuisant toutes les méthodes aujourd' hui disponibles sur le marché , mais découvrent que leur addiction est suffisamment forte pour surmonter ces différentes approches » , a déclaré le Professeur Cornell . -" Nous aspirons à un avenir meilleur , à une nouvelle Égypte et à une autre république " , a déclaré Morsi lors d' une cérémonie solennelle qui s' est déroulée sur la télévision publique . « Nous aspirons à de meilleurs lendemains , une nouvelle Égypte et une deuxième république » , a déclaré le président Morsi au cours de la cérémonie solennelle diffusée en direct sur la télévision publique . -" Nous pouvons confirmer que a été tué lors d' une attaque par drone " , a déclaré un haut responsable des services de sécurité . " Nous pouvons confirmer que a été tué dans un tir de drone " , a dit un haut responsable de la sécurité . -" Nous pouvons maintenant aller de l' avant et nous concentrer sur l' avenir et sur les 90 % des actifs qui constituent une banque solide , et sur la création d' une grande banque pour nos clients et le Royaume-Uni " , a ajouté le nouveau directeur général , Ross , à la presse . " Nous pouvons maintenant aller de l' avant , nous préoccuper de l' avenir et des 90 % des actifs qui constituent une banque vraiment bonne et construire une grande banque pour la clientèle et pour le Royaume Uni " , a dit le nouveau directeur général Ross à la presse . -" Nous avons consolidé ou réduit certaines de nos activités à l' intérieur de l' institution afin de nous y attarder en attendant que les choses se . On a rationalisé ou diminué certaines de nos activités à l' intérieur de l' établissement pour palier , le temps que les choses se . -" Nous n' avons pas invité les rats " , a confié le père de la famille , qui a quitté sa maison et s' est installé dans un hôtel . " Nous n' avons pas convié les rats chez nous " , se défend le père de famille qui a quitté son logement pour s' installer à l' hôtel . -" Nous n' avons pas eu recours à une solution " , a expliqué le Dr . On n' a pas eu recours au dépannage , a expliqué Mme . -" Nous ne voulons pas le faire " , a-t-elle dit . « Nous ne voulons pas faire payer ce prix » , a-t-elle expliqué . -" Nous avons vécu dans la terreur [ du ] , nous sommes traumatisés " , a ajouté l' homme . " Nous avons vécu dans la terreur ( du ) , nous sommes traumatisés " , a ajouté cet homme . -" Nous vivons avec le pour un an et il semblait inimaginable qu' un jour nous soit libéré par l' armée " , a déclaré un habitant de la ville à la frontière avec l' Ouganda . " Nous avons vécu un an avec le et il nous paraissait inimaginable d' être un jour libérés par l' armée " , témoigne un habitant de cette localité limitrophe de l' Ouganda . -" Nous disons depuis environ deux ans et demi que l' idée de forcer les consommateurs à acheter de grands paquets de canaux ne fonctionne pas " , dit M. . " Ça fait à peu près deux ans et demi que nous disons que l' idée de forcer les consommateurs à acheter d' importants forfaits de chaînes , ça ne fonctionne pas " , a affirmé M. . -" Nous avons terminé les pourparlers , les médiateurs ougandais ont simplement besoin d' organiser la signature " , a déclaré Roger , vice-président de la délégation , bien que ces informations ne puissent être confirmées immédiatement par le gouvernement . " Nous avons terminé les pourparlers , il ne reste plus qu' à la médiation ougandaise à organiser la signature " , a affirmé Roger , vice-président de la délégation du , sans que cette information puisse être confirmée immédiatement du côté gouvernemental . -" Nous avons empêché les avions de décoller , les bâtiments ont été et les personnes assassinées , parce que nous avons pu rester informés avant ces attaques " , a déclaré le chef de la diplomatie américaine . " Nous avons de fait empêché que des avions ne tombent , que des immeubles n' explosent et que des gens soient assassinés , parce que nous étions en mesure d' être au courant en amont de ces projets " , a argumenté le patron de la diplomatie américaine . -" Nous ne devons pas leur permettre de se regrouper parce qu' ils ont fait trop souffrir le peuple congolais . Nous ne devons pas les laisser se réorganiser parce qu' ils le peuple congolais depuis trop longtemps . -" Nous devons nous adapter , changer ou résister au changement et à l' échec " , a souligné M. . " Il faut s' adapter et changer ou résister au changement et échouer " , a souligné M. . -" Nous devons être attentifs à la façon dont nous percevons le véritable départ de la puberté " , a déclaré le Dr Lawrence , pédiatrique à l' hôpital des enfants de , à , dans le New Jersey . « Nous devons être prudents quant à la façon dont nous identifions la véritable apparition de la puberté » , a dit le Dr Lawrence , un travaillant à l' hôpital pour enfants à , dans le New Jersey . -" Nous devons avoir un meilleur système " , a-t-il dit . « Nous avons besoin d' un meilleur système » , a-t-il déclaré . -" Nous avons reçu un certain nombre de plaintes de la part des clients , donc nous lui avons demandé de quitter le magasin " , a expliqué un porte-parole de la chaîne de . " Nous avons reçu plusieurs plaintes de clients , donc nous lui avons demandé de quitter le magasin " , a expliqué une porte-parole de la chaîne de supermarchés . -" Nous ne cherchons pas à obtenir des récompenses , à l' exception de la récompense de l' histoire que nous pouvons , à un moment critique , servir cette communauté " , dit-il . « Nous ne cherchons pas de récompense , excepté la récompense de l' histoire car nous devons servir la communauté à un moment critique » , a-t-il ajouté . -" Nous voulons la paix , nous voulons la stabilité " , a déclaré Vanessa de Sousa , chef de la direction d' une entreprise d' investissement . « Nous voulons que la paix revienne ; nous voulons de la stabilité » , a déclaré Vanessa de Sousa , directrice générale d' une société d' investissement . -" Nous aurons une présence accrue dans les campagnes électorales locales et nous de créer une liste . Nous allons être encore plus présents dans le cadre de la campagne municipale et nous allons réfléchir à présenter une liste . -" Nous allons poursuivre les et nous nous de là où ils se cachent parce qu' ils sont des criminels " , dit le colonel Olivier , porte-parole des FARDC , déclaré à Reuters . " Nous allons poursuivre le et l' partout où il se cache parce qu' il s' agit de criminels " , a déclaré à Reuters le colonel Olivier , porte-parole des FARDC . -" Nous serons bientôt et nous attendons une nouvelle maison au même prix que l' ancienne " . Nous sommes bientôt sans le sou et nous attendons un nouveau logement , au même tarif que l' ancien . -" Nous un examen par la CASA d' autoriser l' utilisation d' appareils électroniques parce que nous croyons vraiment que nous pouvons améliorer l' expérience du client maintenant que nous avons un message sur le vol radio ( sans fil ) dans nos avions " , a déclaré un porte-parole . « Nous favorablement une révision de la CASA qui permettrait l' utilisation des appareils électroniques car nous pensons vraiment que cela améliorerait l' expérience client maintenant que nous disposons ( du système de divertissement à bord utilisant la technologie Wi-Fi ) sur nos avions » , a indiqué un porte-parole . -" Ce que je peux vous dire , c' est qu' elle entrait dans le pays " . Ce que je peux vous dire , c' est qu' elle arrivait au pays . -" Quel est le sens de la vie ? " , se . " Quel est le sens de la vie " , . -" Quand je parle de banlieues , je me réfère à tous ceux qui se sentent exclus de la grande famille nationale " . Quand je parle de banlieue , je m' adresse à tous ceux qui sont exclus de la grande famille nationale . -" Où si nous commençons à demander des personnes à ? " demande Gérald , pour qui " le concept d' urgence " est " dangereux " et marque un " progrès médical à l' arrière " . " Où va-t-on si on demande aux gens de s' " , s' interroge Gérald pour qui " le concept d' urgence pas grave " est " dangereux " et marque une " régression médicale " . -" Alors que les livraisons d' avions les plus faibles étaient principalement prévues , nous sommes généralement déçus par la performance de la marge dans le transport " , a déclaré dans une note de client . « Bien que la baisse des livraisons d' avions ait été en grande partie anticipée , nous sommes vraiment déçus par la marge bénéficiaire dans la division Transport » , a déclaré Cameron dans une note aux clients . -" Bien qu' il existe de nombreuses preuves de la part de nombreux mammifères que les deux côtés du cerveau sont utilisés à des fins différentes , la plupart des détails doivent encore être - et les chiens ne font pas exception " , a-t-il dit . « Bien que de nombreux éléments , relevés chez différents mammifères , indiquent que les deux côtés du cerveau sont utilisés à des fins différentes , bon nombre de détails doivent encore être précisés – et les chiens ne font pas exception » , a-t-il déclaré . -" Bien que nous puissions modifier nos programmes de surveillance afin de mieux protéger les droits des Européens , nous devons également tenir compte du fait qu' ils ne sont pas les seuls à être exploités " , a déclaré M. Murphy . « Nous pouvons modifier nos programmes de surveillance afin de mieux protéger les droits des Européens , mais ils doivent aussi comprendre que nous ne sommes pas les seuls dans le monde à faire de l' espionnage » , a déclaré Murphy . -" Avec l' état actuel des connaissances , il pourrait être considéré comme le plus absolu des données biométriques " , confirme Sophie , directrice de la recherche , de l' innovation et des prévisions à la CNIL . " En l' état actuel des connaissances , il peut être considéré comme la donnée biométrique ultime " , confirme Sophie , directrice des études , de l' innovation et de la prospective à la CNIL . -" À partir de 2009 , nos 10 pays sont rentables et plusieurs d' entre eux sont en mesure de comptabiliser les gains d' année en année avant l' intérêt et les contributions à l' impôt , a-t-il dit . « Depuis le début de l' année , les 10 pays dans lesquels nous sommes présents sont rentables , et plusieurs d' entre eux sont en bonne voie de réaliser des résultats record sur l' ensemble de l' exercice avant déduction des intérêts et impôts » , a-t-il déclaré . -" Oui , dans certains cas , cela va trop loin de manière inappropriée " , a déclaré récemment la secrétaire d' État , qui a déjà fait une déclaration sur le scandale international lors d' une tournée à Paris , Londres et Rome la semaine dernière . " Et oui , dans certains cas , c' est allé trop loin de manière inappropriée " , a encore admis le secrétaire d' Etat , qui avait déjà dû s' exprimer sur ce scandale international lors d' une tournée la semaine dernière à Paris , Londres et Rome . -" Vous allez obtenir un meilleur accord " , dit-il . « Vous en gagnant » , a-t-il déclaré . -1 Date limite fixée par l' organisation à Damas pour détruire ou " " tous les équipements de production d' armes chimiques et les équipements permettant de mélanger des produits chimiques en gaz toxique et de purger des munitions . 1er novembre fixée par l' organisation basée à La Haye ordonnant à Damas de détruire ou « rendre inutilisables » toutes les unités de production d' armes chimiques et les machines destinées à mélanger les gaz toxiques et à remplir les munitions . - s Top Design Quick d' entreprise en Grand Style . Le top model de 22 ans ne commence pas sa carrière au cinéma par la petite porte . -300 pages plus tard , il les mots " The End " et chaque page à la main . 300 pages plus tard , il écrivait le mot " FIN " et chaque page au stylo . -66 % des athées ou des non-croyants y aspirent également . 66 % des athées ou non croyants la désirent aussi . -70 pour cent des établissements offrent aujourd' hui un encadrement du rite pour tenir les cérémonies en présence du corps : saluer la congrégation , nommer le défunt , lier les défunts à ce qui est arrivé , appeler la personne qui a l' air , le sens de la mort , organiser un adieu . , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , etc. 70 % des établissements proposent désormais des maîtres de cérémonies , qui procèdent , en présence du corps , au rituel suivant : accueillir l' assemblée , nommer le défunt , le relier aux autres , l' évoquer , donner sens à sa mort , organiser l' adieu . -77 % des Français veulent qu' ils soient proches , qu' ils soient religieux ( 53 % ) ou civils ( 24 % ) . 77 % des Français en veulent une , pour leurs proches , qu' elle soit religieuse ( 53 % ) , ou civile ( 24 % ) . -964 personnes ( dont 855 civils , 65 policiers et 44 soldats ) ont été tuées au cours d' actes de violence en octobre et 1 600 personnes ont été blessées . 964 personnes sont mortes dans les violences en octobre , dont 855 civils , 65 policiers et 44 soldats , et 1 600 personnes ont été blessées . - Un air de documentaire -La fille de , Katie , est née avec des chromosomes humains , mais a un qui s' appelle le syndrome d' insensibilité totale des . La fille du Dr , Katie , est née avec des chromosomes mâles , mais souffre d' un DDS appelé syndrome d' insensibilité complète aux . -La part du vote du diminue depuis 1992 , tandis qu' un autre , le Mouvement démocratique du Mozambique ( ) , qui a été créé par un ancien membre du , devrait améliorer sa représentation aux élections . La part des suffrages recueillie par la ne cesse de baisser depuis 1992 , tandis qu' un nouveau parti , le Mouvement démocratique du Mozambique ( ) qui fut créé par un ancien membre de la , devrait obtenir un plus grand nombre de suffrages aux élections . - , 42 , des informations aux criminels en raison des craintes que sa toxicomanie ne soit source de confusion , 42 ans , fournissait des informations aux criminels car il craignait que sa toxicomanie ne soit révélée au grand jour . - , et ont admis le piratage de téléphone . , et ont admis avoir effectué du piratage téléphonique . - , par exemple , n' émet que des transmissions minimes lorsqu' elles tournent une page . Les lecteurs de livres électroniques , par exemple , émettent seulement des transmissions minimales lorsqu' on tourne une page . - , Les grands exploits et les prouesses La comédie musicale , cette bête de scène - , qui milite pour des réformes électorales , a déjà déclaré qu' il les élections municipales qui devaient avoir lieu en novembre . La , qui est en faveur de réformes électorales , avait déjà dit qu' elle les élections municipales devant avoir lieu en novembre . - , qui lutte pour l' indépendance du Turkestan oriental - l' ancien nom du Xinjiang chinois - a été classé par l' ONU en 2002 comme l' une des organisations affiliées à Al-Qaida . L' , qui déclare se battre pour l' indépendance du Turkestan oriental , ancien nom du Xinjiang chinois , a été classée par l' ONU en 2002 parmi les organisations affiliées à Al-Qaïda . - , qui a traduit de et écrit le Livre des et des British Folk Stories , a déclaré : " Vous avez peut-être des intentions , mais vous faites mieux de les garder loin de la réalité . , qui a traduit de l' anglo-saxon et a écrit The Penguin Book of et British Folk Tales , a déclaré : « Vous avez peut-être de bonnes intentions , mais vous préférez les garder hors de vue . » - , qui supervise la mise en oeuvre de la Care Act , dit que le lancement du marché en ligne est devenu " " depuis octobre . Mme , qui supervise la mise en œuvre de l' Care Act , a indiqué que le lancement en octobre du marché en ligne avait échoué « lamentablement » . - : d' eau de source Explosion de l' Assemblée Explosion d' une maison à : un chauffe-eau défectueux à l' origine de l' explosion -La mort de a été annoncée plusieurs fois par le passé . La mort d' a été annoncée à plusieurs reprises déjà par le passé . - , 42 ans , l' information au criminel du chef de guerre Neil , 42 ans , donnait des informations au seigneur du crime Neil - , 42 , des informations confidentielles pour le seigneur de crime Neil à propos des enquêtes sur ses activités de trafic de drogue après avoir été compromise par son habitude de cocaïne . , 42 ans , donnait des informations sensibles au seigneur du crime Neil concernant des enquêtes sur ses activités liées au trafic de drogue après s' être compromis à cause de son habitude de consommer de la cocaïne . - , attaché régional à 50 ans , prétend aujourd' hui être " 100 % apolitique " , bien qu' il n' ait pas toujours été neutre . , 50 ans , attaché territorial , se revendique aujourd' hui comme " 100 % apolitique " même s' il n' a pas toujours été neutre . - 9 States of Rights reproche à 9 États américains de violer les droits homosexuels - prétend qu' il y a déjà sept noms sur la liste de candidats en France continentale et en Martinique . assure avoir déjà sept têtes de liste en France métropolitaine et en Martinique . - de l' Université de Montréal sera le directeur scientifique . , de l' Université de Montréal , en sera la directrice scientifique . - a été blessé dans un accident de voiture de 1981 près de Yaoundé , capitale du Cameroun . a été blessé dans un accident de voiture en 1981 près de Yaoundé , la capitale du Cameroun . -L' Église baptiste de , où il a été ministre , a déclaré qu' il était profondément attristé par les nouvelles . L' église baptiste de , où le pasteur , se dit profondément par cette nouvelle . - de la résistance du Gouvernement confiant dans la longévité de son gouvernement -Les enquêteurs du de comté de ont conclu que Johnson était mort dans un accident de type , mais les problèmes de la famille de ces derniers le sont . Les enquêteurs du shérif du Comté de ont conclu que Johnson était décédé dans des circonstances insolites , mais la famille du jeune garçon de 17 ans conteste ces conclusions . - due aux causes des À La Seyne A La Seyne , le relogement pour cause de rats tourne à la polémique -La est préoccupée par les conséquences pour les futures générations si elle est adoptée . S' il est adopté , s' inquiète des conséquences sur les prochaines générations . - du leader pakistanais des talibans Le chef des taliban pakistanais tué par un drone -Le député de , Clive Palmer , affirme que le Premier ministre Tony Abbott a un conflit d' intérêts concernant son régime de congé parental parce que ses filles pourraient s' en sortir et y gagner . Le député milliardaire Clive Palmer avance que le Premier ministre Tony Abbott a un conflit d' intérêt à propos du système de congé parental parce que ses filles pourraient être enceintes et profiter de ce système . - Mohamed Morsi a promis une " nouvelle Egypte " , car il a prêté serment pour devenir le premier président librement élu du pays , succédant à Hosni Moubarak qui avait été chassé il y a 16 mois . L' islamiste Mohamed Morsi a promis l' émergence d' une « nouvelle Égypte » lorsqu' il a prêté le serment d' investiture à la présidence , devenant ainsi le premier président librement élu et succédant à Hosni Moubarak qui fut chassé du pouvoir 16 mois plus tôt . - le portique de démonté -Théâtre à Narbonne le jeudi soir Une salle du de Narbonne évacuée jeudi soir - : Les demandes adressées à Brigitte par téléphone au Feux : un député demande la démission de Brigitte - sur l' avenir de son pays , elle a changé sa marque professionnelle pour un T-shirt avec " demande de sécurité " en portugais lorsqu' elle s' est jointe à la foule sur la Place de l' Indépendance de la capitale , jeudi . pour l' avenir de son pays , elle a échangé ses vêtements de travail contre un tee-shirt portant la mention « nous voulons la sécurité » en portugais avant de rejoindre la foule sur la place de l' Indépendance de la capitale jeudi . - al-Maliki demande l' aide des États-Unis . al-Maliki souhaite une aide des Etats-Unis . - al-Maliki s' est adressé à l' Institut américain de la paix , une institution indépendante créée par le Congrès . al-Maliki s' exprimait à l' Institut des Etats-Unis pour la paix , une institution indépendante créée par le Congrès . - et Brooks ont refusé de avec d' autres pour des téléphones entre le 3 octobre 2000 et le 9 août 2006 . et Brooks nient avoir avec d' autres pour des téléphones entre le 3 octobre 2000 et le 9 août 2006 . -Les et les excès de toutes sortes sont les mots clés de cette histoire folle . et excès en tout genre sont les maîtres mots de cette folle histoire . - et son équipe passent presque tous les week-ends à voir des centaines de patients Dr et son équipe passent presque tous leurs week-ends à voir des centaines de patients - apparaît , une autre caractéristique du travail de Chouinard , qui porte le nez rouge , dans les parades très des hommes et des femmes . Des apparaissent , aussi familiers de Chouinard , arborant le nez rouge , dans des parades amoureuses hommes-femmes très . - comme le " tambour de roue " de , son prestige personnel s' est amélioré depuis sa victoire et la remise d' un discours de la présidence de Singapour qui lui a présenté le candidat non seulement pour les islamistes , mais aussi pour tous ceux qui veulent achever le travail du soulèvement de 2011 contre le régime autoritaire de Moubarak . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; » Considéré comme la « roue de secours » peu charismatique des Frères musulmans , son prestige personnel s' est considérablement renforcé depuis sa victoire et son discours de vendredi au cours duquel il a essayé de se présenter comme le candidat non seulement des mais de tous ceux qui veulent finir le travail entamé lors du soulèvement de 2011 contre l' autoritaire président Moubarak . - attaque un de missiles russes dans la ville portuaire de , un officiel . Des avions de combat attaquent un stock de missiles russes dans la ville portuaire de , indique un responsable . - par son travail , les universitaires s' efforcent depuis longtemps de définir et de placer Pierre Gélinas . , les universitaires ont longtemps à définir et situer Pierre Nora . - par les libéraux et les de la Chambre des communes , M. Nicholson a assuré que , avant leur départ , les militaires avaient fait l' objet d' un plan de transition en collaboration avec leurs supérieurs . par les libéraux et les à la Chambre des communes , M. Nicholson a assuré qu' avant leur libération , les membres de l' armée suivaient un plan de transition en collaboration avec leurs supérieurs . - dit qu' il s' agit d' un frais d' utilisateur . appelle cela des frais d' utilisation au kilométrage . - ne peut pas excuser les problèmes du site , les attaques par drone L' n' excuse pas les difficultés du site , ni les attaques de drones -Les appels téléphoniques par téléphone cellulaire continueront également d' être interdits . Les téléphones portables à bord continueront également à être interdits . -Les enfants posent un dilemme éthique . Les enfants posent un dilemme éthique . - soutient que la prémisse qui retient le plus de poids est l' épidémie d' obésité . affirme que l' hypothèse la plus crédible est celle de l' épidémie d' obésité . - a refusé des actes répréhensibles et a déclaré : " Si je suis un avocat corrompu , que je n' ai pas , et que je voulais fournir des renseignements à M. Scarborough , je n' pas 15 heures , je vais le faire immédiatement " . a nié tout agissement illégal et a déclaré : « Si j' étais un avocat corrompu , ce que je ne suis pas , et si j' avais voulu fournir des informations à M. Scarborough , je n' aurais pas attendu 15 heures , je l' aurais fait immédiatement . » - nie être influencée par les systèmes d' observation de la Terre ( AIS ) nie être influencé par Obeid - Tunnel de la drogue avec son propre chemin de fer Découverte d' un tunnel de la drogue entre les États-Unis et le Mexique disposant de sa propre voie ferrée - combler cette lacune . Les spectacles musicaux comblent ce ­ manque . - de sa position de chef des services d' urgence au début du mois de juillet pour s' opposer au projet d' assainissement , il se qualifie de " " face à une " décision cynique " du " personnel de l' administration médicale technique " . de son poste de chef du début juillet pour avoir mené la contre le projet de réorganisation , il se définit comme un " lanceur d' alerte " face à une décision " cynique " prise par " la " . - se maintient pendant des décennies sous quelque forme que ce soit , mais les arrêts enregistrés ont considérablement augmenté sous l' administration du maire indépendant de Bloomberg Michael à un niveau élevé en 2011 de , majoritairement des hommes noirs et hispaniques . Ce système de contrôle et de fouille existe depuis des décennies sous une certaine forme , mais le nombre de contrôles enregistrés a augmenté de façon spectaculaire sous l' administration du maire indépendant Michael Bloomberg , pour atteindre son plus haut niveau historique en 2011 avec 684 330 contrôles qui concernaient principalement des hommes noirs et d' origine hispanique . -Les se sont adaptés à ce changement de société . Les se sont adaptés à cette évolution de la société . -Les ont pris part à des groupes environnementaux en lobbying afin de permettre au gouvernement d' utiliser les petites cases pour suivre les milles que vous avez tirés et , éventuellement , où vous les conduisez - puis utilisez l' information pour rédiger un projet de loi . Les ont rejoint des groupes écologistes pour faire pression afin que le gouvernement utilise les petites boîtes pour garder la trace des miles que vous parcourez , et éventuellement de la route sur laquelle vous , puis utiliser les informations pour rédiger un projet de loi fiscal . -Les ont considéré ce travail comme un " gigantesque " marché des puces , présenté par un commentateur intelligent et animé par un oeil attentif , mais qui est un " " . Les langues ont pu y voir un gigantesque marché aux puces , présenté par un commentateur intelligent , doté d' un vif coup d' oeil et d' érudition , mais . - en France , elle raconte l' histoire sinistre de Suzanne , un jeune envoyé à un couvent par sa famille . Se déroulant dans la France des années 1760 , elle raconte la sombre histoire de Suzanne , une jeune envoyée dans un couvent par sa famille . - dans Le Children avec Gun à l' occasion de l' Halloween Un accueille des enfants avec un fusil au pour Halloween - est un menteur et un traître . " est un menteur et un traître . - fait appel de la sentence et a bénéficié d' une libération sous caution de 50 000 baht . a fait appel du jugement et a été mise en liberté après le versement d' une caution de 50 000 bahts . -La est prudente en ce qui concerne les médicaments , mais reconnaît que " la suppression de la puberté peut atténuer les problèmes de comportement et les sentiments des filles d' être différents des pairs " . est prudent avec les médicaments mais reconnaît que « le fait de contrôler la puberté peut atténuer certains problèmes de comportement et la sensation pour les jeunes filles d' être différentes des autres » . -Les vendeurs à jet d' encre ont même des mesures de bande pour vérifier la mise en place de dispositifs concurrents . Les vendeurs d' avions qui font le tour du monde emportent même avec eux un mètre pour vérifier les configurations concurrentes . -Les bijoux ne sont pas simplement une solution rapide à l' argent de terre - 1 300 dollars de S dans le cas de Mme - mais sont à peu près aussi bon marché que les prêts bancaires non garantis . Mettre en gage des bijoux n' est pas seulement un moyen rapide d' obtenir de l' argent liquide – 1 300 $ de Singapour dans le cas de Mme – mais c' est presque aussi bon marché que les prêts bancaires non garantis . - avocat qui a M. Big au sujet de l' enquête de la police est emprisonné L' avocat qui a informé un parrain de la drogue d' une enquête de police a été emprisonné -Selon des sources juridiques non officielles , était représentée par un avocat local à Phuket mais elle a prévenu que l' appel pouvait porter le jugement à une peine de prison de deux ans au maximum et l' obliger à se rendre dans une prison pour adultes . Des sources judiciaires thaïlandaises ont indiqué que était représentée par un avocat local de Phuket , mais ont prévenu que l' appel pourrait conduire à un alourdissement de la peine pouvant aller jusqu' à deux ans d' emprisonnement et une incarcération dans une prison pour adultes . -La police d' a fait aujourd' hui l' objet d' un avis d' extradition concernant le fondateur de l' organisation , Julian , qui s' est réfugié dans l' ambassade de l' Équateur à Londres et a demandé l' asile . La police britannique a aujourd' hui délivré un mandat d' extradition à l' encontre du fondateur de WikiLeaks Julian , qui s' est réfugié à l' Ambassade de l' Équateur à Londres et a demandé l' asile politique . - a réuni plus de 1.000 chefs d' entreprise , représentant largement les secteurs , secteurs et régions de la taille de l' entreprise . a interrogé plus de 1 000 chefs d' entreprise , globalement représentatifs de toutes les tailles d' entreprises , des secteurs et régions de Grande-Bretagne . - a déclaré que son organisation appuie la loi australienne parce qu' elle " permet aux adultes de choisir d' être reconnus dans un troisième sexe " . a déclaré que son organisation soutenait la loi australienne car « elle autorise les adultes à choisir d' être reconnu sous l ’ appellation troisième sexe » . -M. a dit que certains médecins sont encore en train de " pratiquer ce modèle " . a déclaré que certains médecins « répètent toujours ce modèle » . - dit que le gouvernement a lancé les derniers affrontements en lançant une attaque contre ses membres dans la province de Sofala , bastion de , le 17 octobre . La déclare que le gouvernement est à l' origine des récents affrontements et qu' il a lancé une attaque contre ses membres dans la province de Sofala , un bastion traditionnel de la , le 17 octobre . - à Singapour comme classe moyenne se sentant Les prêteurs sur gage de Singapour tirent profit des classes moyennes contraintes à se serrer la ceinture -Des histoires sur les contrôles des passeports à et une ligne de fer le long du Mur d' . Et voilà qu' on entend des histoires de contrôle des passeports à et d' une frontière de fils barbelés le long du mur d' . - a dit au Sunday Telegraph à Londres que toute la famille était " extrêmement préoccupée par le sort de sa fille et qu' elle a voulu la retrouver en Australie . a déclaré au Sunday Telegraph de Sydney que toute la famille était « extrêmement préoccupée » du bien-être de sa fille et voulait qu' elle rentre en Australie . - a utilisé le téléphone pirate pour vérifier la précision . a utilisé le piratage téléphonique pour vérifier un tuyau - voulait " avertir la communauté internationale que les choses ne fonctionnaient pas bien au Mozambique " , a déclaré M. . La voulait « avertir la communauté internationale que les choses ne vont pas bien au Mozambique » , a indiqué M. . - a été arrêté plus tard , mais a nié avoir consommé de la cocaïne et a déclaré qu' il s' était adressé au trafiquant présumé parce qu' il était client et que ses discussions avaient fait l' objet d' un " privilège juridique " . a été arrêté plus tard mais a nié consommer de la cocaïne . Il a déclaré qu' il avait parlé au trafiquant de drogue présumé car c' était son client et a fait valoir que leurs discussions étaient assujetties à un « privilège juridique » . - était autrefois un mouvement rebelle notoire , initialement soutenu par la Rhodésie du Sud et maintenant le gouvernement de l' apartheid en Afrique du Sud dans le cadre des efforts visant à déstabiliser le gouvernement indépendant du pays . La était autrefois un mouvement rebelle célèbre , soutenu au départ par la Rhodésie gouvernée par des blancs puis le gouvernement d' apartheid d' Afrique du Sud dans le cadre des efforts pour déstabiliser le gouvernement indépendant du pays . - a été condamné à purger la peine de quinze jours d' emprisonnement dans un centre de détention de courte durée à la périphérie de Phuket , au lieu d' une prison pour adultes . a été condamnée à une peine d' emprisonnement de 15 jours dans un centre de détention de faible sécurité dans la périphérie de Phuket plutôt que dans une prison pour femmes . -Des ont été signalés dans les collines proches de , la dernière ville des rebelles à tomber cette semaine , et autour de , une colline où la rébellion a commencé en 2012 . Des accrochages sont signalés dans les collines qui dominent , la dernière localité aux mains des insurgés à être tombée cette semaine , ainsi qu' aux alentours de , une colline où la rébellion du a vu le jour en 2012 . -Un test " d' authenticité " pour les travailleurs étrangers sur 457 visas est actuellement examiné par le gouvernement alors qu' il envisage d' étendre la répression . Le gouvernement étudie actuellement la possibilité de mettre en place un test « d' authenticité » destiné aux travailleurs étrangers en possession d' un visa 457 alors qu' il envisage d' étendre les mesures de répression . -Une " petite quantité de dommages " , confinée à l' espace de la chambre du réacteur , s' est ensuite produite sur le de la flotte française . Une " petite avarie " , circonscrite à l' espace de confinement du réacteur , s' était alors produite sur le navire amiral de la flotte française . -Une femme de 21 ans de Sydney , condamnée à une peine de 15 jours de prison à Phuket pour avoir faussement déclaré qu' elle avait été agressée par un chauffeur de taxi , a plaidé pour le verdict et a obtenu la libération sous caution . Une de Sydney de 21 ans , condamnée à 15 jours d' emprisonnement à Phuket pour avoir prétendu à tort avoir été agressée par un chauffeur de taxi , a fait appel du jugement et a été mise en liberté sous caution . -Un homme de 38 ans qui a pris un enfant à l' école de Gabrielle Roy , à Surrey , doit faire face à six accusations , selon la Gendarmerie royale du Canada . L' homme de 37 ans qui a pris un enfant en otage à l' école Gabrielle Roy de Surrey fait face à six chefs d' accusation , selon la Gendarmerie royale du Canada . -Un entrepreneur britannique a créé la première de glace à l' échelle planétaire , utilisant les méduses . Un entrepreneur britannique a créé la première glace fluorescente au monde à base de . -Un journal chinois qui lance un appel à la presse pour la libération d' un journaliste accusé de diffamation doit être révisé , un organisme de presse judiciaire . Un journal chinois qui a lancé un appel en première page pour la libération d' un journaliste accusé de diffamation va être remanié , déclare un régulateur de la presse . -Une source du Hamas a expliqué que les combats avaient éclaté dans la région après que des chars israéliens eurent franchi la frontière et avaient été pris pour cible par des tirs de mortier palestiniens . Une source issue du Hamas a expliqué que des combats ont éclaté dans cette région , car des chars israéliens ont franchi la frontière et ont été visé par tes tirs de mortiers palestiniens . -Une équipe de sauvetage technique de la station de sapeurs-pompiers de s' est rendue sur la scène et a pour secourir Ruby et a utilisé un pour le transporter sur la falaise . Une équipe de secours technique de la caserne de pompiers de s' est ruée sur les lieux et est descendue en rappel pour sauver Ruby en utilisant une civière pour animaux pour la transporter jusqu' en haut de la falaise . -Un responsable américain a confirmé qu' il y avait eu une " grève israélienne " , mais n' a pas donné de détails sur l' objectif . Un responsable américain a confirmé une " frappe israélienne " mais n' a pas donné de détails sur la cible . -Une meilleure gestion des affaires britanniques est possible , et il est de plus en plus nécessaire que la zone euro s' ouvre sur la voie d' une union économique et fiscale plus étroite . Il est possible , et de plus en plus nécessaire , de mieux représenter les intérêts des entreprises britanniques , puisque la zone euro s' engage sur la voie d' une union économique et fiscale plus étroite . -Un grand changement se produit dans l' informatique des nuages où les dirigeants européens sont plus conscients que les données stockées aux États-Unis sont soumises à cette compétence et donc potentiellement vulnérables . Un gros changement est en train de se produire dans l' informatique en nuage puisque les responsables européens ont pris conscience du fait que les données conservées aux États-Unis sont régies en vertu des lois de cette juridiction et donc potentiellement vulnérables . -Un homme qui a été abattu à terre après avoir été épuisé par une centrale de forage en mer du Nord a été renvoyé dans la nature . Un oiseau , après avoir été trouvé épuisé sur une plate-forme pétrolière en mer du Nord , a été relâché dans la nature . -Une boîte noire dans votre voiture ? Une boîte noire dans votre voiture ? -Un membre du conseil d' administration d' une société allemande de premier plan a déclaré qu' en ce qui concerne l' espionnage économique , " les Français sont les pires " . Un membre du conseil d' administration d' une société allemande renommée estimait que lorsqu' il s' agit d' espionnage économique , « les Français sont les pires » . -Un chargement de missiles sol-air sol-air à courte portée a été ciblé et détruit . Une cargaison de missiles sol-air de courte portée aurait été ciblée et détruite . -Un cas de rage dans une chienne illégalement importée de Gambie a été signalé en 2008 . Un cas de rage sur une chienne importée illégalement de Gambie avait été enregistré en 2008 . -Un enfant de cinq ans a été gravement blessé à la suite d' un accident survenu à Disneyland Paris . Un enfant de cinq ans a été grièvement blessé à la suite d' un accident sur une attraction à Disneyland Paris . -Un clown revient sur la scène alors que le public quitte . Un clown revient en scène quand le public quitte . -Une combinaison de circonstances nous a mis dans une situation difficile l' année dernière " . Il y a un concours de circonstances qui a fait qu' on était dans une mauvaise situation l' année passée . -Un cours combiné de littérature anglaise et de cours de langue sera mis en échec . Un cours combinant littérature et langue anglaises sera supprimé . -Un ouvrier de la construction est mort après avoir été enseveli sous des dizaines de blocs de béton à Montréal jeudi après-midi . Un travailleur de la construction est mort enseveli sous des dizaines de blocs de béton , jeudi , à Montréal . -Un différend contractuel entre Gazprom et ses homologues en Ukraine , qui abrite la plus grande partie du gaz russe pour l' Europe , ajoute une couche de risque aux itinéraires conventionnels , disent les responsables . Un différend contractuel entre Gazprom et son homologue d ’ Ukraine , pays par lequel transite la plus grande partie du gaz russe destiné à l' Europe , accroît le niveau de risque par rapport aux voies conventionnelles , ajoute le directeur . -Un festin pour les fans Un festin pour ses fans -Une cour d' appel fédérale du Texas a réintroduit certaines restrictions sur le droit à l' interruption volontaire de grossesse , qui avait été bloquée par un juge de ce procès cette semaine . Une cour fédérale d' appel a rétabli au Texas certaines restrictions au droit d' interruption volontaire de grossesse qui avaient été bloquées par un juge de première instance cette semaine . -Une cour d' appel fédérale conteste la décision d' un juge selon laquelle la tactique controversée de la constitue une discrimination à l' encontre des minorités . Une cour d' appel fédérale bloque la décision d' une juge selon laquelle la tactique controversée du est discriminatoire à l' égard des minorités . -Un tribunal d' appel fédéral a suspendu l' ordonnance d' un juge exigeant des modifications du programme de police du de New York et a retiré le juge de l' affaire . Une cour d' appel fédérale a bloqué jeudi la décision d' une juge exigeant des changements dans le programme de contrôle et de fouille du département de police de New York et l' a du dossier . -Un combattant de l' aile armée du Hamas a été tué ce soir et un autre blessé par des tirs israéliens dans la bande de Gaza , des sources médicales et de sécurité dans le rapport de Gaza . Un combattant de la branche armée du Hamas a été tué ce soir et un autre blessé par un tir de char israélien dans la bande de Gaza , a-t-on appris de sources médicales et sécuritaires à Gaza . -Un incendie a causé de graves dommages à un magasin du district de LaSalle , à Montréal , le jeudi soir . Un incendie a gravement endommagé un commerce de l' arrondissement de LaSalle , à Montréal , dans la nuit de jeudi à vendredi . -Un incendie qui a commencé à midi le vendredi dans le Vieux-Québec a été rapidement maîtrisé . Un incendie qui s' est déclenché vendredi midi dans le Vieux-Québec a rapidement été maîtrisé . -Un formulaire qui , sans erreur , est tout aussi restrictif : Mais ne nous y trompons pas , tout aussi contraignante . -Un ancien de la province de , dans le nord-est de la Chine , a été condamné à des peines d' emprisonnement à perpétuité pour corruption le vendredi . Un ancien de la province de , dans le nord-est de la Chine , a été condamné vendredi à la prison à vie pour corruption . - , ancien ministre des Affaires étrangères et du Commerce international ( ) , a été condamné à une peine de trois ans de prison ferme et à une peine de trois ans d' emprisonnement . Un document de travail du gouvernement a été dévoilé aujourd' hui alors que la députée travailliste dénonçait le discours gouvernemental sur les travailleurs étrangers , en disant qu' il pourrait offenser les voisins de l' Australie . -Une maison historique construite en 1850 dans le vieux Québec a été sous les flammes le vendredi midi . Une maison historique construite en 1850 été la proie des flammes dans le Vieux-Québec vendredi midi . -Une action en justice a été intentée en 2004 par quatre hommes , par toutes les minorités , et est devenue un recours collectif . Une action judiciaire a été introduite en 2004 par quatre hommes , tous provenant de minorités , et s ’ est transformée en action collective . -Une fuite de vapeur radioactive , détectée sur l' un des deux nucléaires sur le à la mi-octobre quand le navire était en mer , " n' était pas une menace pour les marins " , mais ce n' est pas une mince affaire pour la Marine . La fuite de vapeur radioactive détectée sur l' une des deux nucléaires du , à la mi-octobre et alors que le bateau se trouvait en mer , était " sans danger pour les marins " , mais n' est pas sans conséquence pour la Marine . -Une lettre de M. , présentée aux médias à Berlin vendredi soir , a déclaré : " Bien que les résultats de mes efforts positifs aient été positifs , mon gouvernement continue de traiter l' opposition comme une farce et tend à criminaliser le discours politique à l' égard de crimes graves qui ne font pas l' objet de défense " . Dans une lettre que le député a présentée aux médias à Berlin vendredi , M. disait : « Bien que le résultat de mes efforts ait été de toute évidence positif , mon gouvernement continue de traiter la différence d' opinion comme une fuite et cherche à pénaliser le discours politique avec des accusations contre lesquelles il est impossible de se défendre . » -Un peu plus tard , sur la route menant à , la frontière avec l' Ouganda , des soldats blessés par des civils ont reçu un lance-roquettes sur un nouveau camion appartenant aux FARDC , destiné à prendre le relais d' un autre appareil qui les positions des dans les collines . Un peu plus tôt , sur la route menant à , avec l' Ouganda , des militaires aidés de civils un lance-roquettes multiple monté sur un camion flambant neuf des FARDC , devant assurer la relève d' un autre engin les positions du sur les collines . -Une petite fille a été blessée par balle et trois autres ont été blessées , l' un d' entre eux ayant été gravement touché et évacué par les FARDC , le témoin cité par les AFP . Une fillette a été blessée par balle , et trois autres personnes ont été blessées , dont une grièvement , évacuée par les FARDC , a ajouté ce témoin cité par l' AFP . -Un commandant local des Brigades , Abou Hamad , et un autre officier de l' aile armée du Hamas , , ont été tués par un obus de char au cours d' une incursion menée par l' armée israélienne à Khan , dans le sud de la bande de Gaza , selon des sources médicales locales . Un commandant local des Brigades , Khaled Abou Bakr , et un autre cadre de la branche armée du Hamas , , ont été tués par un tir d' obus de char lors d' une incursion de l' armée israélienne à l' est de Khan , dans le sud de la bande de Gaza , selon des sources médicales locales . -La majorité des actionnaires d' Oracle ont voté contre le projet de rémunération pour le fondateur et directeur exécutif Larry Ellison jeudi en raison de la performance financière de son groupe . Une majorité d' actionnaires d' Oracle ont voté jeudi contre la proposition de rémunération pour le fondateur et directeur général Larry Ellison , au vu des performances financières de son groupe . -Un homme vêtu d' un uniforme de l' uniforme nazi et d' un a été appelé à déposer un camion anglais après avoir reçu des clients du directeur de la boutique , qui avait appelé la police , selon le magasin le vendredi . Un homme vêtu d' un uniforme nazi barré d' un avec la croix a été prié de quitter un supermarché anglais , des clients s' étant plaints auprès de la direction de l' établissement qui a appelé la police , a indiqué le magasin vendredi . -Un homme de Cambridge a revendiqué la responsabilité de l' acte sur son compte Twitter , où il a posté des photos d' Adolf Hitler . Un homme de Cambridge a revendiqué la responsabilité de cet acte sur son compte Twitter , où il a posté des images d' Adolf Hitler . -Un homme a ouvert le feu à 10 heures , heure locale . Il était 10h00 du matin heure locale quand un homme a ouvert le feu . -Un ministre responsable des enfants dans la région de Halifax , en Nouvelle-Écosse , a été accusé de pornographie juvénile . Un pasteur qui s' occupait des jeunes , dans la région d' Halifax en Nouvelle-Écosse , est accusé de pornographie juvénile . -Une progression plus rapide peut exiger des tests d' un pour écarter de graves problèmes tels que les tumeurs ou les kystes . Une évolution plus rapide peut justifier des tests réalisés par un pour écarter des problèmes graves comme des tumeurs ou des kystes . -Un automobiliste a été condamné à payer une amende de 1,000 livres pour la conduite jusqu' à ( / h ) avec une boisson chaude équilibrée entre ses jambes . Un automobiliste s' est vu infliger une amende de 1 000 £ pour avoir roulé à 210 km / h avec une boisson chaude posée en équilibre entre ses jambes . -Une nouvelle action de revendication collective a été introduite contre les frères de la Congrégation de la Croix de , qui auraient fait l' objet d' agressions sexuelles , ce jour-là , à l' Oratoire Saint-Joseph et à plusieurs orphelinats , collèges et écoles . Une nouvelle demande de recours collectif a été déposée contre les frères Sainte-Croix , concernant des agressions sexuelles qui se seraient déroulées cette fois à l' oratoire Saint-Joseph ainsi que dans plusieurs orphelinats , collèges et écoles . -En février et en août 2007 , M. et M. , M. Steve et M. , ont fait état d' un communiqué de vote , un qui était arrivé au ministère des ports après avoir travaillé sous la responsabilité de M. Valverde dans le secteur de la pêche . La transcription de conversations téléphoniques présentée à l' a montré des appels passés en août et septembre 2007 entre M. Obeid , M. et Steve Dunn , un haut fonctionnaire qui était arrivé au ministère des Ports après avoir travaillé sous les ordres de M Obeid au ministère des Pêches . -Une audition plénière sur la question doit avoir lieu en janvier . Une audience plénière sur cette question est prévue pour le mois de janvier . -Une enquête préliminaire a révélé que le s New News Group avait publié plusieurs faux reportages sur la société cotée au cours de la période de septembre 2012 à août 2013 . Une enquête préliminaire a montré que le New Express , appartenant au groupe News , avait publié plusieurs articles erronés sur la société cotée entre septembre 2012 et août 2013 . -Un chiot a eu une chance de se sauver après que les équipes de pompiers ont été amenées à la afin de lui assurer une sécurité lorsqu' elle s' est retrouvée coincée sur une falaise précaire . Un chiot a eu la chance d' échapper au pire après que les pompiers ont été appelés pour le récupérer en toute sécurité alors qu' il était perché à 15 m au-dessus du sol sur la saillie instable d' une falaise . -Un rapport présenté à la Commission européenne en 2011 a décrit les comme appartenant à des personnes ou transgenres , car leur statut n' est pas spécifiquement lié à la condition de la femme , mais se rapporte à leur maquillage biologique , qui n' est pas exclusivement masculin ou féminin , mais il est également familier d' une manière ou d' une autre seulement clairement définie . Un rapport soumis à la Commission européenne en 2011 décrivait les personnes comme différentes des personnes transsexuelles ou transgenres , puisque leur statut n' est pas lié au sexe mais plutôt à leur constitution biologique , qui n' est ni exclusivement celle d' un homme ni exclusivement celle d' une femme , mais est en général les deux en même temps et pas clairement définie comme étant l' une ou l' autre . -Un représentant du Bureau du shérif du comté de n' a pas été immédiatement disponible pour commenter lorsqu' il a été contacté jeudi . Le représentant du bureau du shérif du comté de n' était pas disponible pour nous donner son opinion lorsque nous l' avons contacté jeudi . -Un accident de la route sur le chemin de Bruxelles , près de , en direction de Waterloo , à , le vendredi soir , a laissé une personne grièvement blessée . Un accident de la route a fait un blessé grave , vendredi matin vers 5h30 , sur le ring intérieur de Bruxelles à hauteur de , en direction de Waterloo . -Une ligne de conduite a été mise en place entre les leaders d' avions sur la taille des sièges de sur les vols à grande distance , donnant le ton pour une confrontation acharnée lors du salon aéronautique de Dubaï en 2009 . La dispute fait rage entre les grands constructeurs aéronautiques à propos de la largeur des sièges de la classe touriste sur les vols long-courriers , ouvrant la voie à une confrontation amère lors du salon aéronautique de Dubaï qui a lieu de mois-ci . -Un scandale concernant la présence de viande de cheval dans les repas préparés a été relevé en Europe au début de l' année , après les tests effectués en Irlande . Un scandale sur la présence de viande de cheval dans des plats cuisinés a éclaté en Europe au début de l' année , à la suite de tests effectués en Irlande . -Cependant , une deuxième autopsie a fait état d' une autre cause de décès , selon Moore . Toutefois , une seconde autopsie a identifié une autre cause de décès , selon Moore . -Un négociateur principal a dit que malgré le volume important de transactions boursières quotidiennes , la dévaluation de la liquidité entre les différentes plates-formes de négociation et l' utilisation croissante des banques par leurs propres plateformes internes , " on peut commencer à avoir un impact sur le marché à des prix très faibles des petits billets " . Un trader senior a déclaré qu' en dépit du volume élevé des transactions de change quotidiennes , la fragmentation de la liquidité entre les plates-formes de gestion des échanges et l' utilisation de plus en plus fréquente par les banques de leurs propres plates-formes internes « explique qu' il est possible de commencer à avoir un impact sur le marché à peu de frais » . -Un accident grave s' est produit entre une moto et une voiture sur la rue à à 15 heures le vendredi . Vendredi , vers 15 heures , une violente collision entre une moto et une voiture s' est produite rue de à . -Un mur de fondation commun avait été construit ces derniers jours . Un mur de fondation venait d' être érigé au cours des derniers jours . -Une technique similaire est appliquée à et à , en compagnie de la société , en partenariat avec les banques et les grands détaillants : - au paiement d' une carte dans la boutique , le client ne passe pas un code - il place un doigt dans le lecteur , qui scanne les veines . Une technique similaire est testée à et à Angoulême par la société Natural Security en partenariat avec des banques et des enseignes de la grande distribution : au moment de payer en magasin avec sa carte bancaire , le client ne tape pas son code , mais introduit son doigt dans un lecteur qui scanne les veines . -Une seule dose du vaccin a été en mesure de protéger les souris contre la dépendance à la nicotine pour la vie . Une seule dose du vaccin a pu protéger à vie des souris de la dépendance à la nicotine . -Un juge du sud de la Géorgie , le mercredi , a ordonné aux autorités de libérer toutes les vidéos de surveillance que les enquêteurs avaient examinées . Un juge de Géorgie du sud a ordonné mercredi aux autorités de remettre tous les enregistrements de vidéosurveillance que les enquêteurs avaient examinés . -Une espèce est considérée menacée ou menacée sur la base du nombre de ses membres vivant sur la planète , connus sous le nom de stocks , et de la possibilité pour les espèces de se reconstituer à l' aide de stocks voisins . Une espèce est considérée menacée ou en voie d' extinction en fonction du nombre d' individus existants sur Terre , c' est-à-dire du stock , et de la possibilité qu' elle a à se rétablir à partir des stocks voisins . -Un porte-parole du annuel a déclaré : " est seulement trois , mais il sait comment composer son " . Un porte-parole de la manifestation équestre annuelle a déclaré : « n' a que 3 ans mais il sait comment conduire son . » -Un porte-parole de la Conférence des évêques catholiques des États-Unis a confirmé que le document était authentique et que chaque évêque déciderait de la manière appropriée d' approcher les paroissiens . Une porte-parole de la Conférence des évêques catholiques des États-Unis a confirmé que le document était authentique et que chaque évêque déciderait de la façon appropriée de sonder les paroissiens . -Une déclaration de l' OIAC , qui travaille étroitement avec l' Organisation des Nations Unies , a indiqué que son équipe était maintenant " convaincue qu' elle avait vérifié - et avait été détruite - toutes les installations de production et d' équipement de production et de transformation déclarées de Syrie " . Une déclaration de l' OIAC , qui travaille en étroite collaboration avec l' ONU , indiquait que son équipe était « maintenant satisfaite d' avoir contrôlé – et vu détruites – toute la production critique déclarée et les machines destinées au mélange et au remplissage » . -Une étude visant à augmenter les avantages pour l' Écosse du projet ferroviaire a été annoncée par le gouvernement britannique . Une étude destinée à multiplier les avantages du projet ferroviaire pour l' Écosse a été lancée par le gouvernement britannique . -Une étude sur la façon dont les chiens réagissent aux vrais chiens a pu être utile , explique-t-il . Une étude sur la façon dont les chiens réagissent face à de vrais chiens pourrait être utile , a-t-il expliqué . -Une enquête publiée par le célèbre public des médias au début de l' année a révélé une explosion dans l' utilisation d' appareils mobiles par les jeunes enfants aux États-Unis : 38 % des enfants de moins de 2 ans utilisent déjà un ou un téléphone portable et 72 % en , contre 10 % et 38 % il y a deux ans respectivement . Un sondage publié en début de semaine par Common Sense Media montrait une explosion de l' usage des appareils mobiles par les jeunes enfants aux États-Unis : 38 % des moins de 2 ans se sont déjà servis d' une tablette ou d' un téléphone , et 72 % des moins de 8 ans , contre respectivement 10 % et 38 % il y a deux ans . -Une bande de , prise de son téléphone à au moins trois occasions , a été prise à la maison de en août 2006 . Une bande , sur laquelle étaient enregistrés des messages vocaux piratés sur son téléphone à au moins trois reprises , a été saisie au domicile de en août 2006 . -Une équipe de biologistes travaillant pour la Société de conservation de la faune de New York a identifié une nouvelle espèce de dauphin à bosse vivant sur la côte nord de l' Australie . Une équipe de biologistes dirigée par la Wildlife Conservation Society de New York a identifié une nouvelle espèce de dauphin à bosse au large des côtes septentrionales de l' Australie . -Une commission de trois personnes sera créée pour veiller à ce que l' État assure les services de la manière la plus efficace et la plus efficiente possible . Une commission composée de trois personnes sera constituée pour garantir que l' État fournit les services de la façon la plus efficace et efficiente possible . -Un avocat de haut rang qui a dit à une drogue M. Big une importante enquête de police , parce qu' il craint que sa drogue secrète ne soit contaminée , a été emprisonnée pendant trois ans . Un grand avocat de la défense , qui avait informé un parrain de la drogue d' une importante enquête de police car il avait peur que son addiction secrète à la drogue ne soit révélée , a été condamné à trois ans de prison . -Un groupe de l' industrie du tourisme s' est réjoui de ces changements , les qualifiant d' arrangements de bon goût pour un public en déplacement qui se avec la technologie . Un groupe de l' industrie du voyage s' est félicité des changements , les appelant des arrangements pleins de bon sens pour des voyageurs équipés de technologies . -Une vidéo envoyée aux autorités semble confirmer ces soupçons . Une vidéo remise à la justice semble appuyer ces soupçons . -Une jeune équipe de visionnaires était dirigée vers la Maison Blanche , et la nation était prête pour le changement . Une jeune équipe de visionnaires avançait vers la Maison-Blanche et la nation était prête pour le changement . -Aaron , âgé de 30 ans , a été arrêté le vendredi matin à la suite d' une perquisition à son domicile et au bureau du Conseil national de la recherche où il travaille . Aaron , âgé de 30 ans , a été arrêté vendredi matin suivant des perquisitions à son domicile et au bureau du Conseil national de la recherche où il travaille . -Le droit en matière d' avortement Remise en cause du droit à l' avortement au Texas -Il y a environ une dizaine d' années , les scientifiques ont reconnu deux espèces qui appartenaient à cette espèce : les dauphins de bosse du Pacifique et les baleines à bosse . Il y a une dizaine d' années , la communauté scientifique reconnaissait deux espèces appartenant à cette : les dauphins à bosse du Pacifique et les baleines à bosse de l' Atlantique . -Environ deux ans après le retrait des troupes américaines , le niveau de violence les craintes d' agitation alors que le voisin du pays , la Syrie , est en proie à la guerre civile . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Deux ans environ après le retrait des troupes américaines , le niveau des violences fait craindre un nouvel embrasement alors que la Syrie voisine est en proie à une guerre civile . -Selon la police chinoise , trois membres d' une Chine ouïghoure appartenant essentiellement à la communauté ouïghoure du Xinjiang , partageant la majorité avec plusieurs pays d' Asie centrale , ont volé une voiture transportant des drogues à l' abri de l' entrée de la Cité interdite à Pékin , dans un attentat-suicide qui a fait deux morts et plusieurs blessés . Selon la police chinoise , trois Ouïghours d' une même famille de la région à dominante musulmane du Xinjiang , frontalière de plusieurs pays d' Asie centrale , ont précipité leur voiture chargée de bidons d' essence contre l' entrée de la Cité interdite à Pékin , dans une qui a fait deux morts et 40 blessés . -Selon Detroit News , la reine de Soul se produira à la Salle Sound du Casino Hotel le 21 décembre . Le journal Detroit News rapporte que la reine du soul se produira le 21 décembre à la salle Sound Board de l' hôtel Casino . -Selon Google , les costumes les plus recherchés sont les zombies , Batman , les pirates et les sorcières . Selon Google , les les plus recherchés sont les zombies , Batman , les pirates et les sorcières . -Selon des sources palestiniennes de sécurité , les quatre menaient une opération de surveillance dans la zone frontalière entre l' enclave palestinienne et Israël . Selon des sources sécuritaires palestiniennes , les quatre combattants conduisaient une opération de surveillance dans la zone frontalière entre l' enclave palestinienne et Israël . -Selon le journal The Star , le nouveau projet le gouvernement sous un coup de pied " les médias " , tandis que la loi stipule que la démocratie et la liberté d' expression au Kenya ont été " un coup dur " et le projet de loi " draconienne " . Selon le journal The Star , le nouveau projet de loi permettra au gouvernement d' avoir « la mainmise sur les médias » . The Standard a , quant à lui , indiqué que la démocratie et la liberté d' opinion au Kenya « avaient subi un coup dur » et a le projet de loi le qualifiant de « » . -Selon une source à la Mission des Nations Unies pour la stabilisation en République démocratique du Congo ( ) , les combats sont entrés dans la " phase finale " : les FARDC ont " encerclé les derniers postes de pour les arracher " . Selon une source à la Mission des Nations unies pour la stabilisation de la RDC , les combats sont entrés " dans une phase finale " , les FARDC ayant " encerclé les positions du résiduelles pour les déloger " . -D' après une enquête réalisée par la Jet Trading Alliance , un organisme de la marine , environ 10 % des membres non américains ont annulé l' utilisation d' un réseau de satellites américains après avoir entendu des informations sur le programme de récupération des diamants de l' Amérique du Nord . Selon une enquête réalisée par la Cloud Security Alliance , un organisme professionnel , quelque 10 % des membres non américains ont renoncé à leurs projets d' utiliser un fournisseur américain de services de stockage dans le cloud après les révélations sur le programme américain d' exploration des données Prism . -D' après les chiffres publiés vendredi par les manufacturiers , cinq des plus de 6 points qui ont enregistré des résultats en termes d' attentes , la baisse de 16 jours de la plupart des services administratifs fédéraux a diminué la croissance des ventes de voitures aux États-Unis en octobre . La fermeture pendant 16 jours de la plupart des administrations fédérales a pesé sur la croissance des ventes de voitures aux Etats-Unis en octobre , montrent les chiffres publiés vendredi par les constructeurs , cinq des six principaux ayant réalisé des performances inférieures aux attentes . -Selon elle , le a été invité à une médiation et a demandé une autre période d' examen . Selon elle , la a été invitée à une médiation et elle a demandé un délai supplémentaire pour y réfléchir . -D' après les enquêtes de la Commission européenne , la France est la plus touchée par la présence de ce type de viande dans des produits qui ne sont destinés à contenir que du boeuf . Selon des examens de la Commission européenne , la France a été le pays le plus touché par la présence de ce type de viande dans des produits censés contenir uniquement du boeuf . -Selon l' un des documents , environ 181 millions d' articles ont été collectés au cours du mois de janvier seulement - allant de la à des courriels jusqu' à des éléments textuels ou à des fichiers audio et vidéo . A en croire un des documents , quelque 181 millions d' éléments avaient été collectés au cours du seul mois de janvier dernier - allant de métadonnées sur des e-mails , à des éléments de texte ou des documents audio ou vidéo . -Selon des informations émanant de quelques médias grecs , des membres de Golden Dawn ont affirmé que les victimes les locaux de la partie . Certains médias grecs ont rapporté que des membres d' Aube doré avaient soutenu que les victimes étaient des gardes de leur local . -Selon le quotidien , " même une amende suffit à paralyser la plupart des stations FM " . Selon le Daily Nation , « même une seule amende suffit à paralyser la plupart des stations FM » . -Selon le député , " il n' y a actuellement aucune différence dans la continuité visuelle entre la Flandre et Bruxelles . Selon le député , il n' y a désormais " plus de différence de continuité visuelle entre la Flandre et Bruxelles " . -Selon la marine , l' équipage n' a été exposé à aucune contamination radioactive . D' après la Marine nationale , l' équipage n' avait pas été exposé à une éventuelle contamination radioactive . -Selon le Gouvernement de l' État , ces nouvelles procédures en grande partie les récentes coupes . Selon le gouvernement de l' État , ces procédures non urgentes supplémentaires en grande partie les récentes coupes . -Selon le correspondant politique du public du billet blanc de Jonathan Brown , le général travaille à l' examen des efforts déployés par les hauts fonctionnaires de la campagne et de la Maison Blanche , l' ancien rédacteur en chef d' un parti d' Etat , Bill , pour mesurer l' effet de l' échange d' un ancien secrétaire d' État Clinton pour le vice-président dans les sondages . Selon le correspondant politique du Times Jonathan Martin , le livre offre une analyse détaillée du travail des hauts responsables de la campagne et de la Maison-Blanche , à savoir l' ancien secrétaire général de la Maison-Blanche Bill , visant à mesurer quel effet le remplacement du vice-président par l' ancienne secrétaire d' État Clinton pourrait avoir sur le scrutin . -D' après les médiateurs ougandais , les pourparlers entre le gouvernement de Kinshasa et le ont repris à Kampala , mercredi . Ces derniers , selon les médiateurs ougandais , ont repris mercredi à Kampala entre le gouvernement de Kinshasa et le . -Selon le Washington Post , la NSA a intercepté des centaines de millions d' utilisateurs de Google et de Yahoo . D' après le Washington Post , la NSA des données de centaines de millions d' utilisateurs de Google et Yahoo . -Selon les autorités , la victime est un homme dans les années cinquante , qui a travaillé pour une entreprise de coffrage . Selon les autorités , la victime est un homme âgé dans la cinquantaine qui travaillait pour une entreprise de coffrage . -Selon les mêmes sources et les mêmes témoins , un char israélien et un bulldozer blindé ont effectué une incursion d' une centaine de mètres sur le territoire avant de se replier . D' après les mêmes sources et des témoins , un char israélien et un bulldozer blindé ont fait une incursion d' une centaine de mètres à l' intérieur du territoire avant de se retirer ensuite . -Selon ces sources , les militants menaient une opération de surveillance dans la zone frontalière entre le territoire palestinien et Israël , lorsqu' ils ont été bombardés par un char israélien . Selon ces sources , ces activistes conduisaient une opération de surveillance dans la zone frontalière entre le territoire palestinien et Israël lorsqu' ils ont essuyé un tir d' obus d' un blindé israélien . -Selon ce témoin , qui a choisi de rester anonymes , les soldats ont " passé la nuit " à , avant de passer à une opération de " " . Selon ce témoin souhaitant rester anonyme , les soldats avaient " passé la nuit " à avant de monter au front pour une " opération de ratissage " . -Dans toute l' UE , les parties qui se sont engagées à s' opposer à l' UE ou à l' euro en tant que monnaie commune gagnent du terrain . À travers l ’ Europe , les partis qui s' opposent à l' UE ou qui militent pour l' abandon de l' euro en tant que devise commune , gagnent du terrain . -En fait , j' ai souvent eu l' habitude d' écouter la langue arabe pendant mon enfance et de parler du Maroc où elle vit depuis des décennies avant de revenir en France quand elle est devenue indépendante . En fait , je l' entendais souvent parler arabe dans mon enfance , et parler du Maroc , là où elle avait vécu pendant des décennies - avant de regagner la France au moment de l' indépendance . -À cela s' ajoute , plus récemment , des contrôles de présence . S' y est ajouté , plus récemment , le contrôle de présence . -De plus , le Défi Defender offrira une journée de formation et d' essai en février , ainsi que la possibilité de participer à des compétitions en Afrique du Nord et au Moyen-Orient . De plus , le Defender Challenge proposera une journée de formation et de tests en février et la possibilité de participer à des événements dans les déserts d ’ Afrique du nord et du Moyen-Orient . -Pour ce qui est de ce qu' il a appelé la " surprise dans les nouvelles " sur les annulations , Obama a invité les Américains à recevoir ces billets pour se procurer une nouvelle couverture sur le marché . Parlant de ce qu' il appelle « une effervescence dans l' actualité » au sujet des , le président Obama a incité les Américains qui recevaient ces avis à chercher une nouvelle assurance sur le marché . -Le bénéfice ajusté par action est demeuré inchangé à 9 cents . Le résultat par action ajusté est resté inchangé à 9 cents . -Les revenus tirés de l' exploitation minière ont chuté de 13 % à 2 milliards de dollars . Les revenus de l' Aéronautique ont baissé de 13 % pour atteindre 2 Md $ . -Les filles d' origine afro-américaine et hispanique ont tendance à atteindre la puberté plus tôt que leurs homologues blancs , les résultats de la recherche . Des études montrent que les jeunes filles et hispaniques ont tendance à atteindre l' âge de la puberté plus tôt que les jeunes filles blanches . -Après Broadway et Londres , Paris trouve enfin sa voix . Après Broadway et Londres , Paris trouve enfin sa voix . -Après un accord de paix de 1992 , il est devenu un parti d' opposition . Après un accord de paix signé en 1992 , elle est devenue un parti d' opposition . -Après avoir reçu des fournitures , on s' attend à ce qu' ils quittent la grotte le soir avec l' aide des sauveteurs . , ils devraient ressortir de la cavité avec l' aide des secouristes dans la soirée . -Après avoir jeté un coup d " œil lourd dans la livraison en janvier dernier de la position exclusive de dans le North Barrington , Olivier , George Smith , directeur général de la famille des pneus américains , Anthony , déclare maintenant qu' il est prêt à sauver 333 des de la société . Après avoir jeté l' éponge avec fracas en janvier pour la reprise partielle du site Goodyear d' promis à la fermeture , Maurice Taylor , le PDG du américain Titan , se déclare , aujourd' hui , prêt à sauver 333 emplois sur les 1 137 que compte l' usine . -Après cinq années de forte croissance depuis la crise financière mondiale , et la faible consommation de crédit alimentée par une politique monétaire laxiste dans les économies avancées , les familles à revenu faible ou moyen se tournent vers les magasins à puces pour faire la différence , alors que leur économie ralentit . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Après cinq années de croissance solide depuis la crise financière mondiale , et le crédit bon marché alimenté par une politique monétaire trop laxiste dans les économies avancées , les familles à revenu faible et moyen se tournent vers les prêteurs sur gage pour combler la différence alors que leurs économies connaissent un ralentissement . -Après avoir abandonné le plan d' acheter l' usine en janvier , aujourd' hui vous êtes revenus . Après avoir renoncé au projet d' achat de l' usine en janvier , vous revenez aujourd' hui . -Après qu' il a été arrêté , son journal a publié deux articles parus dans la presse pour sa libération , affirmant qu' il soutenait son journalisme . Après son arrestation , son journal a publié à deux reprises un appel en première page pour sa libération , écrivant qu' il soutenait son journalisme . -Après ses billets de mangue jaune et de Black , elle a fini par les vêtements à Bruxelles jaune , bleu foncé et noir . Après ses taxis , elle a finalement elle-même en les poteaux de Bruxelles . -Après avoir appris la vérité , il est revenu à un homme , prenant des de testostérone et a retiré ses seins . Après avoir appris la vérité , il est redevenu un homme , en suivant un traitement à la testostérone et s' est fait enlever les seins . -Après avoir perdu sur les marchés de Mega sur les brevets de Nortel , Google a acquis l' acquisition de Motorola Mobility pour 12,5 milliards de dollars , une transaction qui a été en partie prise par la bibliothèque de brevets de Motorola . Après avoir perdu les brevets Nortel face à Rockstar , Google a acquis par la suite Motorola Mobility pour un montant de 12,5 Md $ , un marché en partie basé sur la bibliothèque de brevets de Motorola . -Après dix jours de scandale , de révélations et de entre les États-Unis et ses alliés européens , c' est la première fois qu' un représentant du gouvernement américain reconnaît explicitement les activités controversées de l' interception des communications et des données par la CIA en Europe . Après dix jours de scandale , de révélations et de entre les Etats-Unis et leurs alliés européens , c' est la première fois qu' un responsable gouvernemental américain admet explicitement des pratiques controversées dans l' interception par la NSA de communications et de données en Europe . -Après dix années de silence ( son dernier record , la réalité , sorti en 2003 ) et de très nombreuses apparitions publiques , le musicien britannique a prouvé qu' il pouvait encore éclairer la scène pop . Après dix ans de silence ( le dernier disque , Reality , date de 2003 ) et de trop rares apparitions en public , le musicien anglais prouvait qu' on pouvait encore compter sur lui pour faire la scène pop . -Après l' intervalle , le est plus riche . Les , après l' , est plus riche . -Après la confirmation par la police qu' ils ont remis sur la copie d' une vidéo démontrant qu' il y avait Rob Ford Tobacco , Rod Clayton Wheeler a soutenu qu' il n' avait jamais vu une enquête " si " . Après que la police eut confirmé avoir mis la main sur une copie de la vidéo qui montrerait Rob Ford fumant du crack , Me Clayton Ruby a soutenu n' avoir jamais vu une enquête " aussi " . -Après le premier vol de l' avion d' essai il y a environ un mois et demi , il n' a fait que trois fois plus de temps , soulevant des questions sur la question de savoir si la phase d' essai était sur le point de se dérouler . Après le vol inaugural de l' avion d' essai il y a environ un mois et demi , ce dernier n' a volé que trois fois , et la question s' est alors posée de savoir si la phase d' essai était en bonne voie . -Après la guerre , l' élite politique française considérait l' Europe comme un véhicule pour maintenir l' Allemagne en position de contrôle , et pour donner à Paris la parité dans le monde avec Washington . Après la guerre , l' élite politique française a considéré l' UE comme un moyen de contrôler l' Allemagne et de mettre Paris sur un pied d' égalité avec Washington . -Après cette période difficile , semble être un peu plus facile pour le service de pharmacie du . Après cette période ardue , l' année s' annonce nettement plus facile pour le département de pharmacie du . -Après s' être déplacé dans des zones souvent , il a passé un mois à sa machine à utiliser la roue à Tokyo de 1976 , " toutes les pièces ont été remplacées " . Après ces voyages dans des zones souvent troublées , il s' pour un mois derrière sa machine à écrire IBM à datant de 1976 " dont toutes les pièces ont été changées " . -Raid aérien contre des installations militaires en Syrie Raid aérien contre des installations militaires en Syrie -Airbus et Boeing ne fournissent pas de sièges , mais offrent un catalogue de fournisseurs pour les compagnies aériennes . Airbus et Boeing ne fournissent pas de sièges , mais proposent un catalogue de fournisseurs parmi lesquels les compagnies aériennes doivent choisir . -Airbus propose un A350 , mais affirme qu' il ne l' a pas encore vendu . Airbus offre un A350 configuré avec 10 sièges par rangée mais indique qu' il ne l' a pas encore vendu . -Airbus affirme qu' elle a commandé des travaux de recherche suggérant qu' un pouce supplémentaire dans la largeur du siège améliore la qualité du sommeil de 53 % . Airbus ajoute qu' elle a commandé une étude qui indique qu' un pouce supplémentaire au niveau de la largeur des sièges améliorerait la qualité du sommeil de 53 % . -Airbus affirme que son rival est en phase avec un concept de siège des années 50 , lorsque la moyenne du plus récent " jet set " a été plus restreinte . Airbus déclare que son rival s' accroche à un concept de siège qui date des années 1950 , lorsque la circonférence de l' appareil fraîchement baptisé « jet set » était plus étroite . -Airbus affirme que la version concurrente de son A350 va emporter 350 personnes sur le économique fixé à 9 sur 12 . Airbus indique que la version concurrente de son A350 transportera en classe économique 350 personnes dans des sièges de 18 pouces de large configurés en rangées de 9 . -Airbus a lancé cette semaine pour une norme de l' industrie qui devrait prévoir un siège d' au moins 18 pouces ( 46 cm ) dans les cabines économiques , mais ses US Boeing stipulent que c' est aux compagnies aériennes de décider . Cette semaine , Airbus a appelé l' industrie aéronautique à mettre en place une norme imposant une taille de siège d' au moins 18 pouces ( cm ) dans les classes économiques , mais son grand rival américain Boeing déclare que ce devrait être aux compagnies aériennes de décider . -Règlements électroniques de l' appareil requis pour rester en vigueur en Australie pour l' instant Les règles relatives à l' utilisation d' appareils électroniques à bord restent pour l' instant en vigueur en Australie -Les passagers de la compagnie aérienne pourront utiliser leurs appareils électroniques pour lire , travailler , jouer aux jeux , lire des films et écouter de la musique - mais ne pas parler sur leurs téléphones cellulaires - dans de très nombreuses nouvelles directives annoncées jeudi par la Federal Aviation Administration . Les passagers des compagnies aériennes pourront utiliser leurs appareils électroniques de porte à porte pour lire , travailler , jouer à des jeux , regarder des films et écouter de la musique – mais pas parler sur leur téléphone portable – en vertu des nouvelles directives très attendues publiées jeudi par la Federal Aviation Administration . -Les compagnies aériennes ont commencé à facturer les premiers et deuxièmes bagages enregistrés en 2008 . Les compagnies aériennes ont commencé à faire payer les premier et second bagages enregistrés en 2008 . -Depuis plusieurs années , les compagnies aériennes assurent l' utilisation d' une connexion Wi-Fi à des altitudes de croisière . Les compagnies aériennes permettent à leurs passagers d' utiliser le Wi-Fi aux altitudes de croisière depuis plusieurs années . -Les compagnies aériennes paient des vendeurs de billets en ligne comme les de 10 à 25 $ pour chaque billet vendu . Les compagnies aériennes payent aux vendeurs de voyages en ligne comme 10 $ à 25 $ pour chaque billet vendu . -Les compagnies aériennes devront montrer à la FAA comment elles respectent les nouvelles directives et ont mis à jour les manuels de formation de leurs équipages et les règles relatives à l' arrimage des dispositifs afin de tenir compte des nouvelles lignes directrices . Les compagnies aériennes devront prouver à la FAA que leurs avions respectent les nouvelles directives et qu' elles ont mis à jour les manuels de formation des membres d' équipage et les règles concernant le rangement des appareils pour être en conformité . -Tous les vols à destination et en provenance de l' aéroport ont été suspendus . Tous les vols à l' arrivée et au départ de l' aéroport ont été suspendus . -Les quatre suspects étaient surveillés par la police au cours d' une enquête majeure sur un trafic d' héroïne et de cocaïne qui opérait à travers le , le , le , le et le West Yorkshire . Les quatre suspects étaient surveillés à l' époque par la police dans le cadre d' une importante enquête sur un trafic d' héroïne et de cocaïne se déroulant dans les comtés de , , , et West Yorkshire . -Tout ce qu' il a fallu , c' est mettre l' accent sur ses erreurs et , conformément à , la question sera confiée aux préfets et aux - les interprètes autorisés . Il ne fallait qu' en déployer les accidents , et l' affaire , oblige , était confiée aux préfets et aux , interprètes autorisés . -Tous les candidats nous ont dit qu' ils allaient s' attaquer à cette dette scandaleuse . Tous les candidats nous ont dit qu' ils s' à la dette scandaleuse . -Tous les locataires sont arrivés au moment de l' incendie . Tous les locataires étaient absents lors de l' incendie . -Tous les gels de salaires dans le monde ne pas que l' industrie pétrochimique européenne soit par un gaz de schiste américain bon marché . Tous les gels de salaires au monde n' empêcheront pas l' industrie pétrochimique européenne d' être pénalisée par un gaz de schiste américain bon marché . -Tous les billets sont valables pour ces spectacles . Tous les billets restent valables pour ces concerts . -Presque tous les vendredis , jusqu' à 30 personnes se dans des fourgonnettes , des fournitures médicales aux toits et se déplacent en terrain accidenté pour visiter les villages dans le besoin . Presque tous les vendredis , avec une trentaine de personnes , ils s' dans des fourgonnettes après avoir fixé le matériel médical sur le toit et traversent des terrains difficiles pour se rendre dans les villages qui ont le plus besoin d' aide . -Il y a presque trente ans , mon mari est mort de 33 ans . Voila bientôt trente ans que mon mari est décédé à l' âge de 33 ans . -Déjà , l' Australie et le Népal permettent aux adultes de marquer le nom des hommes , des femmes ou des " troisième sexes " sur leurs documents officiels . L' Australie et le Népal autorisent déjà les adultes à inscrire sexe masculin , féminin ou « troisième sexe » sur leurs documents officiels . -À l' heure actuelle , les droits de timbre pour les maisons de Westminster et de Kensington et de Cork , qui s' élèvent à 6 millions de livres sterling au cours de l' exercice 2012 / 13 , sont supérieurs à ceux de l' ensemble du territoire d' Irlande du Nord , du pays de Galles , de l' Écosse , du Nord-Est , du West Yorkshire et du Yorkshire et du Humber . Le droit de timbre s' applique déjà aux propriétés résidentielles des arrondissements de Westminster et Kensington & Chelsea , et représente 708 M £ sur l' exercice fiscal 2012 / 13 , ce qui dépasse le montant total de l' Irlande du Nord , du pays de Galles , de l' Écosse , du Nord-Est , du Nord-Ouest et de la région Yorkshire et Humber réunis . -De plus , M. a besoin d' un engagement de la part de Titan avant d' essayer pour que la CGT se joigne à Goodyear . Aussi M. a-t-il besoin d' un engagement de Titan avant d' essayer d' amener la CGT à s' asseoir à la table avec Goodyear . -De plus , les investisseurs continuent de faire preuve de prudence , car ils croient que la Réserve fédérale américaine pourrait mettre fin à sa politique d' assouplissement quantitatif plus tôt que prévu . Par ailleurs , les investisseurs restent prudents car ils estiment que la Réserve fédérale américaine pourrait dénouer sa politique d' assouplissement quantitatif plus tôt que prévu . -De même , la France peut , par exemple , transmettre des blocs entiers de données sur la région du Sahel aux Américains et , en retour , comme cela a déjà été mentionné brièvement , les Américains peuvent fournir des informations aux Français sur d' autres parties du monde . La France peut être conduite , par exemple , à transmettre des blocs entiers de données sur la région du Sahel aux services américains , et , en contrepartie - on l' a déjà rapidement dit - , les Américains peuvent donner des informations aux Français sur d' autres régions du monde . -Jeudi également , un groupe de militants syriens a déclaré que plus de 120 000 personnes avaient été tuées depuis le début de la guerre civile du pays il y a près de trois ans . Un groupe de militants syriens a également déclaré jeudi que plus de 120 000 personnes ont été tuées depuis le début de la guerre civile dans le pays il y a près de 3 ans . -Parmi les morts , il y avait également soldats de l' et quelques d' opposition , dont un grand nombre d' étrangers . Parmi les morts , on estime également que 2 202 étaient des déserteurs et quelque 5 375 des combattants de l' opposition , dont bon nombre étaient étrangers . -Bien que Google travaille actuellement sur un modèle de Google Glass équipé de correcteurs correcteurs , aucune date pour un lancement à grande échelle n' a encore été annoncée . Bien que Google travaille actuellement sur un modèle de Google Glass équipé de verres de correction , aucune date de commercialisation à grande échelle n' a encore été confirmée . -American Media and Entertainment Group de Disney a décidé de donner priorité aux PC Tablet sur ses propres chaînes de télévision pour la prochaine émission d' une nouvelle série pour les enfants . Le groupe américain de médias et de divertissement Disney a décidé de privilégier les tablettes à ses propres chaînes de télévision pour la sortie prochaine d' une nouvelle série pour enfants . -Les Américains n' achètent pas autant de gaz qu' ils ne l' ont fait . Les Américains n' achètent plus autant d' essence qu' avant . -Les Américains dépensent 1,9 milliards de dollars pour cette année , selon The Nielsen Company . Les Américains vont dépenser 1,9 Md $ en bonbons cette année , selon le cabinet Nielsen . -Au cours d' une vague de critiques , le président Obama a hier sa promesse répétée et sans équivoque que " si vous aimez votre régime de santé , vous pouvez le garder " . Sous une avalanche de critiques , le président Obama est revenu hier sur sa promesse sans ambiguïté et souvent répétée selon laquelle « si vous êtes satisfait de votre régime de santé , vous pouvez le garder » . -Au cours du tumulte , Murphy a déclaré que son bureau devrait organiser le voyage du Congrès , ce qui devrait se produire cette année , et espère que la délégation comprendra des membres des deux parties et des deux chambres . Dans cette atmosphère de tumulte , Murphy a indiqué que son bureau organisait un voyage du Congrès , qui devrait avoir lieu cette année , et qu' il espérait que la délégation comprendrait des membres des deux partis et des deux chambres . -Parmi les personnes qui militent en faveur d' un assouplissement des restrictions sur l' utilisation par les passagers des appareils ont été . fait partie de ceux qui font pression pour un assouplissement des restrictions concernant l' utilisation des appareils électroniques par les passagers . -De grandes quantités de pétrole brut ont pesé sur le prix au cours des dernières semaines . L' offre abondante de pétrole brut a pesé sur le prix au cours des dernières semaines . -Un dessin d' Henri est projeté sur l' écran . Sur l' écran , une encre d' Henri est projetée . -Le cri de désespoir de l' élection d' Obama Le cri de désespoir d' un électeur d' Obama - partisan de Jean-Pierre en 2002 , il a par la suite soutenu Dominique de Villepin dans le district de 2010 à 2011 . en 2002 aux côtés de Jean-Pierre , il a ensuite soutenu localement Dominique de Villepin entre 2010 et 2011 . -Plus de 2.600 opérations chirurgicales , y compris la chirurgie orthopédique et la cataracte , permettront de résorber l' arriéré . 2 600 opérations supplémentaires , notamment dans le domaine de la chirurgie orthopédique et de la cataracte , aideront à rattraper le retard . -Un privilège d' admission est le droit d' un médecin d' admettre un patient dans un hôpital ou un centre médical pour effectuer un diagnostic ou une procédure , sur la base de son statut de travailleur de santé dans un hôpital . Le privilège d' admission est le droit d' un médecin , en vertu de son statut de membre soignant d' un hôpital , d' admettre un patient dans un hôpital ou un centre médical afin d' y délivrer un diagnostic ou un traitement . -Un assistant du maire est allé à Londres pour demander à la Grande-Bretagne d' approuver cette demande . Un adjoint au maire de l' île est allé à Londres demander à la Grande-Bretagne de valider cette demande . -On estime qu' un enfant de 2 000 ans né chaque année n' est ni un garçon ni une fille . On estime qu' un enfant sur 2 000 nés chaque année , n' est ni un garçon ni une fille . -Un a appelé cette phrase , animée par un chaud , à un assistant qui lui a été assigné à l' audience de la séance d' hier à la suite d' un litige relatif à l' inscription de Foday Chi Long , , si elle devait être obligée de s' inscrire à . . C' est une Kathleen qui a prononcé cette phrase , alors qu' elle croyait son micro coupé , en s' adressant à un assistant assis derrière elle lors de la séance de l' Assemblée d' hier suite à un échange de points de vue litigieux avec Billy Long , représentant républicain du Missouri , pour savoir si elle devrait adhérer à l' . -Un extrait de a suscité beaucoup d' excitation sur Internet : il montre Martin au milieu d' un au plafond avec des . Un extrait de a fait monter la température sur la Toile : Martin y apparaît au lit et en pleine extase avec . -Une taxe supplémentaire ou annuelle pourrait être imposée aux conducteurs d' hybrides et d' autres dont les véhicules n' utilisent pas beaucoup de gaz , et ils paient donc leur juste part . Une taxe unique ou annuelle supplémentaire pourrait être imposée sur les conducteurs de véhicules hybrides et sur d' autres dont les véhicules ne consomment pas beaucoup d' essence , de sorte que chacun paie sa juste part . -Un comité consultatif de l' industrie créé par la FAA pour examiner la question a recommandé le mois dernier que le gouvernement autorise une utilisation plus large des appareils électroniques personnels . Un comité consultatif industriel créé par la FAA pour examiner le problème a recommandé le mois dernier que le gouvernement autorise une utilisation plus large des appareils électroniques personnels . -Un inspecteur de la Commission de la santé et de la sécurité au travail a été envoyé au site pour enquêter sur les circonstances de la tragédie . Un inspecteur de la Commission de la santé et de la sécurité du travail a été dépêché sur les lieux afin d' enquêter sur les circonstances entourant ce drame . -Une enquête est en cours pour trouver la cause de l' incendie . Une enquête est en cours pour trouver la cause de cet incendie . -Une enquête sera menée pour déterminer la cause , bien que la brigade des pompiers ait exclu la possibilité qu' elle soit un acte criminel . Une enquête sera menée pour en déterminer l' origine , mais les sapeurs ont écarté la thèse d' un acte criminelle . -Un magasin en ligne proposant une gamme d' accessoires pour Google Glass vient d' être lancé pour les milliers de développeurs qui possèdent un prototype des lunettes ® . Une boutique en ligne , proposant de multiples accessoires dédiés à Google Glass , vient d' être mise à disposition des milliers de développeurs possédant un prototype des lunettes connectées signées Google . -Un appel verbal sur l' appel de la ville est prévu après le 14 mars 2014 . L' audition de l' appel interjeté par la ville devrait avoir lieu après le 14 mars 2014 . -Selon les analystes , il est peu probable que le pays retombe dans un conflit réel , mais les événements récents ont attiré des investisseurs étrangers et des résidents locaux . Les analystes pensent que le pays ne devrait pas retomber dans un conflit ouvert , mais les récents évènements ont ébranlé les investisseurs étrangers et la population locale . -Les analystes avaient prévu des bénéfices de 10 cents par action et revenus de milliards de dollars , selon Thomson Reuters ( E / B / S ) . Les analystes s' attendaient à un résultat de 10 cents par action et des revenus de Md $ , selon Thomson Reuters I / B / E / S. -Les analystes ont prévu une moyenne des ventes de 15,4 millions d' unités ( taux annualisé , ajusté selon les variations saisonnières ) , mais le marché a atteint 15,3 millions , selon des estimations d' Adam Jonas , analyste à Morgan Stanley . Les analystes en moyenne 15,4 millions de ventes en rythme annualisé et corrigé des variations saisonnières mais le marché s' est établi à 15,3 millions selon les estimations d' Adam Jonas , analyste de Morgan Stanley . -Les analystes interrogés par Reuters comptaient sur un profit moyen de $ par action . Les analystes interrogés par Reuters en moyenne sur un bénéfice par action de dollars . -Et je lutte , à côté d' autres personnes , pour la reconnaissance du statut juridique des otages . Surtout , je me , avec d' autres , pour la reconnaissance du statut juridique de l' otage . -Et , plus récemment , elle ne peut excuser l' incapacité de concevoir un site Web simple depuis plus de trois ans depuis l' adoption de la Loi sur les soins à domicile . Et , plus récemment , elle ne peut pas excuser l' impossibilité de concevoir un simple site Internet pendant plus de trois années après que la loi sur l' Care Act a été ratifiée . -Et , en plus , il y a le portrait de l' historien , qui devrait contenir quelques surprises . Avec , en prime , le portrait de l' historien , qui devrait réserver quelques surprises . -Et , oui , je effectivement un garçon . Et bien sûr , ce fut un petit garçon dans mon ventre . -Et a reçu des notes élevées pour son apparence et sa personnalité . Et a reçu de très bonnes notes pour sa présentation et sa personnalité . -Et je m' adresse à l' Europe pour leur dire clairement que nous devons continuer à travailler ensemble à la lutte contre le terrorisme , en dépit de leur colère vis-à-vis de ces programmes . Et je vais me rendre en Europe pour bien leur faire comprendre que nous devons continuer à travailler ensemble pour lutter contre le terrorisme , malgré la colère provoquée par les programmes de la NSA . -Et je pense à mon père . Et je pense à mon père . -Et il y a un peu plus de dix ans , la communauté médicale considérait le sexe comme une qui pouvait être effacée et . Et il y a à peine une dizaine d' années , la communauté médicale considérait le genre comme une ardoise , que l' on peut effacer et sur laquelle on peut redessiner . -Et même s' ils pouvaient voir un médecin , de nombreux n' en avaient pas les moyens . Et même s' ils pouvaient voir un médecin , de nombreux ne pourraient pas payer la consultation . -Et chaque matin , je regarde dehors . Et tous les matins , je détourne les yeux . -Et enfin , il donne un avant-goût de sa nouvelle piste , , qui est en général dans le style de la prochaine journée , avec des guitares très bien et un métal électrique finement maîtrisé . Et , surtout , il donne un avant-goût de son nouveau titre , typiquement dans la veine de The Next Day , avec des guitares très en avant et une électricité rock savamment maîtrisée . -À partir de là , il y a une nation , divisée entre la gauche et la droite , laïque et catholique , l' adoration et la haine pour la Révolution . Et , de là , une nation binaire , partagée entre gauche et droite , laïcité et catholicisme , adoration et haine de la révolution . -Et il a lancé une association sans but lucratif , , qui se rend dans les zones rurales les fins de semaine pour fournir des soins médicaux gratuits . Et il a créé une association humanitaire à but non lucratif , , qui se rend dans les zones rurales pendant les week-ends pour dispenser des soins de santé gratuits . -Et les appareils plus lourds comme les portables devront continuer à être parce qu' ils risquent de blesser quelqu' un s' ils volent dans la cabine . Et les appareils plus lourds comme les ordinateurs portables devront toujours être rangés car ils pourraient blesser quelqu' un s' ils à travers la cabine . -Dans un autre document de recherche de l' Université de Victoria au Canada , il a dit : " Les chiens sont plus susceptibles de parler avec un chien quand son chien " est venu à la plutôt qu' à la droite , plutôt que de devenir anxieux - le contraire inverse à l' étude italienne . Et dans une autre étude scientifique réalisée par l' université de Victoria au Canada , il a déclaré : « Les chiens allaient plus facilement vers un chien robot lorsque sa queue vers la gauche plutôt que vers la droite , au lieu de devenir anxieux – l' inverse de ce qui figure dans l' étude italienne . » -Et n' est-il pas étonnant de lire dans les pages du Monde , d' une part , la reproduction d' une correspondance diplomatique avec les États-Unis et , d' autre part , la condamnation de l' espionnage du Ministère des affaires étrangères sur le Quai d' Orsay , dans les semaines qui viennent . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; N' est-il pas surprenant de lire dans les colonnes du Monde à quelques semaines d' intervalle d' une part la reproduction de la correspondance diplomatique américaine et d' autre part une condamnation des écoutes du Quai d' Orsay par la NSA ? -Et c' est aussi la première fois depuis 1996 qu' il n' y a pas de groupe armé allié au Rwanda présent dans l' est du RDC . Et ce serait aussi la première fois depuis 1996 qu' aucun groupe armé allié au Rwanda ne serait présent dans l' est de la RDC . -Et ce qui est tellement long dans la dent qu' elle est devenue : je me suis d' abord dit que " l' indépendance est le contrôle des passeports " il il y a quarante ans au moins . Et une histoire si ancienne qu' elle est maintenant figée : J' ai entendu pour la première fois le selon lequel « l' indépendance signifie le contrôle des passeports » il y a plus de 40 ans . -Et elle aime prendre des risques - la jeune fille qui n' a pas hésité à s' habiller nu sur papier nu apparaîtra dans des situations bien pires sur le grand écran . Sans oublier un certain goût du risque : celle qui n' avait pas hésité à poser nue sur du papier glacé va s' afficher dans des situations bien plus sur grand écran . -Et il le fait , mais dans une forme moins formelle , plus explosive et plus subtile . Certes sous une forme moins raide , plus éclatée et plus subtile . -Et les actes réussis tels que " Cabaret " ou " Les " ont eu un air de mystère à leur égard . Et les réussites comme Cabaret ou Les portaient l' du mystère . -Et cela doit être ; il a été révélé par une histoire plutôt que par une déclaration . Et il doit être ; révélé à travers une histoire plutôt que déclaré . -De plus , la Coalition , qui regroupe 17 ministères de la sécurité du littoral le long de la côte est ( y compris le Maryland , la Pennsylvanie , la Virginie et la Floride ) , étudie comment elle pourrait s' appliquer à la mise en oeuvre du changement . Et la coalition , qui comprend 17 agences impliquées dans le transport routier dans des États longeant la côte est américaine ( comprenant le Maryland , la Pennsylvanie , la Virginie et la Floride ) étudie comment elle pourrait mettre en œuvre le changement . -Et la Marine assure que " cela n' a en aucune manière retardé le [ [ ] ] plan d' activités des navires " . Et la Marine nationale de certifier que " ça ne remet nullement en cause le programme d' activité du bateau " à propulsion nucléaire . -Et le président est résolu à essayer de clarifier et de clarifier les choses pour les gens et fait maintenant un examen approfondi pour que personne n' ait le sentiment d' abus . Et le Président est résolu à tenter de clarifier et préciser clairement aux gens qu ’ il procédait maintenant à un réexamen approfondi de ces pratiques afin que personne ne se sente trompé . -Et l' homme qui a rejeté une idée d' une nation plus d' un siècle plus tard , mais qui a échappé à l' exercice de l' exercice du travail philosophique , confie en nous , cum , qu' il a fini par le faire tout . Et celui qui avait déposé , voici plus d' un demi-siècle , un sujet sur l' idée de nation , mais s' était soustrait à l' exercice initiatique de la thèse , nous confie , cum , qu' il aura fini par la faire . -Et le plus emblématique est clairement celui qui a divisé l' histoire nationale en deux : Et la plus emblématique est évidemment celle qui en deux l' histoire nationale . -Et puis je termine le livre . Et puis je ferme le livre . -Et elle se alors en me montrant des aspects de moi-même que je n' avais jamais vus auparavant , cachés par ma relation avec elle . Et puis , elle se révélait tout en me dévoilant mes propres facettes que je n' avais pas encore perçues , masquées par ma relation avec elle . -Et il y avait beaucoup de médias . Et beaucoup de médias . -Et cette forme est définitive de toute la pièce : il s' agit d' une série de représentations physiques des dessins . Et cette forme sera celle de toute la pièce : une d' imitations physiques des dessins . -Et que se passe-t-il quand le virus du terrorisme est vivant . Et que se passe-t-il quand un virus du terrorisme vit ? il se répand . -Et même si le Congrès ne peut s' entendre sur la manière de procéder , plusieurs États ne s' y attendent pas . Et tandis que les membres du Congrès n' arrivent pas à se mettre d' accord pour savoir s' il faut continuer , plusieurs États n' ont pas attendu . -Et avec la pauvreté , ils ne peuvent pas profiter de leur vie . Et en raison de la pauvreté , ils ne peuvent pas profiter de la vie . -Andrew , 35 ans , de , Essex , a été repéré en train de conduire sa Mercedes Benz sur le à le 27 mai . Andrew , 35 ans , de dans l' Essex , a été repéré conduisant sa Mercedes Benz sur l' à hauteur de le 27 mai . -La colère dans les rangs des islamistes d' Égypte . La colère dans les rangs des islamistes égyptiens . - la colère sur la bombe de feu de Bali provoquée par la peine prononcée à l' encontre d' un des de bombe de Bali -Ann Hidalgo , candidate du Parti socialiste , a répété lundi matin à France Inter qu' elle était en faveur d' un moratoire pour empêcher la fermeture le 4 novembre . Anne Hidalgo , la candidate PS , a quant à elle de nouveau rappelé lundi matin sur France Inter qu' elle s' était prononcée en faveur d' un moratoire pour qu' il n' y ait pas de fermeture le 4 novembre . -Anne Hidalgo , candidate socialiste à Paris , devrait assister et présentera ultérieurement ses propositions sur les questions de lors d' une conférence de presse . Anne Hidalgo , candidate socialiste à la mairie de Paris doit y assister , et expliquera ensuite , lors d' une conférence de presse ses propositions en matière de funéraire . -Autre : Bien que les garçons aient des cheveux et à l' âge avancé , la tendance à la puberté précoce n' est pas aussi prononcée que chez les filles . Autre problème : Bien que les garçons aient des poils sur le visage et dans la zone plus jeunes , la tendance envers une puberté précoce véritable n' est pas aussi prononcée que chez les filles . -Une autre journée , une nouvelle histoire d' indépendance de l' aviation . Un autre jour , une autre histoire inquiétante d' indépendance . -Un autre effet pourrait être plus commercial : à la lumière des révélations , de plus en plus de pays d' Amérique du Sud et d' Amérique latine sont invités à accepter la divulgation de leurs données confidentielles aux fournisseurs américains qui sont soumis à la législation américaine et donc à la . Un autre effet peut être lui plus économique : de plus en plus d' entreprises européennes ou rechignent , à la lumière des révélations , à confier leurs données confidentielles à des prestataires américains soumis aux lois américaines , et donc à l' emprise de la NSA . -Un autre dirigeant a indiqué que Barack Obama avait donné l' ordre de mettre fin à ces pratiques ces dernières semaines . Un autre officiel a , lui , indiqué que Barack Obama avait donné ordre de cesser ces pratiques au cours des semaines passées . -Une autre fois , quand je me suis réveillé tranquillement au milieu de la nuit , j' ai regardé devant sa montre , que j' ai toujours avec moi , couché sur la table de chevet . Une autre fois , un doux réveil en pleine nuit pour regarder sa montre qui ne me quitte plus , posée sur la table de nuit . -Une autre utilisation est de prendre une photo d' une personne qui s' exécute dans une boutique , de suivre ses progrès à l' aide de la reconnaissance faciale et de faire le temps d' attente . Autre utilisation : prendre en photo un qui fait la queue dans un magasin , suivre sa progression grâce à la reconnaissance faciale et calculer le temps d' attente . -La police antiterroriste s' est rendue à l' avenue principale de , où l' incident s' est produit et a fermé la zone . Des policiers du service antiterroriste se sont dépêchés sur l' avenue centrale de la banlieue , où l' incident a eu lieu et ont bouclé le quartier . -Plus que je ne le ferai " . Pas plus que moi . -Apparemment , la version finale du film durera 2 heures 45 minutes . Il semblerait que le film dure finalement . -Les singes de l' Arctique le marathon de Vienne en raison de la tragédie d' Alex Turner Arctic reporte un concert à Glasgow en raison de la maladie d' Alex Turner - Franklin Back at Stage en décembre Franklin sur scène en décembre -Arnaud , ministre de la relance industrielle , avait déjà annoncé le lundi 21 octobre . Arnaud , ministre du redressement productif , l' avait déjà annoncé , lundi 21 octobre . -Environ 70 personnes ont été évacuées de l' appareil . 70 personnes environ sont sorties de la séance . -Alors que les planificateurs de la route de l' Amérique tentent de trouver de l' argent pour réparer un barrage en plein air , nombreux sont ceux qui commencent à trouver une solution dans une petite boîte noire qui s' aligne parfaitement sur le tableau de bord de votre voiture . Alors que les planificateurs du réseau routier des États-Unis ont du mal à trouver l' argent nécessaire pour réparer l' infrastructure en , nombreux sont ceux qui une solution sous forme d' une petite boîte noire qui se fixe au-dessus du tableau de bord de votre voiture . -Comme Reuters l' a signalé pour la première fois en juillet , la fixation du siège est exactement ce qui fait la bataille entre les derniers jets . Comme Reuters l' a indiqué pour la première fois en juillet , la configuration des sièges est exactement ce qui alimente la bataille entre les tout derniers appareils . -Par conséquent , des recherches ont été menées au cours des vingt dernières années dans d' autres méthodes , comme la reconnaissance faciale ou l' iris ( partie colorée de l' oeil ) . Conséquence , des recherches sont menées depuis une vingtaine d' années sur d' autres approches , comme la reconnaissance du visage ou de l' iris ( la partie colorée de l' oeil ) . -En conséquence , la révolte se , surtout en France , une fois de plus le principal de l' UE . En conséquence , la révolte , en particulier en France , l' un des grands meneurs de l' UE . -En conséquence , certains de ses livres de James ont été : un mois avant l' attaque d' un centre de commandement du gouvernement syrien , où plusieurs hauts responsables avaient été tués , il avait déjà raconté l' histoire au " Chemin de Damas " . Du coup , nombre de ses SAS sont souvent : ainsi , un mois avant l' attaque d' un centre de commandement du régime syrien ayant tué plusieurs hauts responsables , il avait raconté l' histoire dans " Le Chemin de Damas " . -Par conséquent , les ne respectent pas les normes fédérales de sécurité routière sur les appuie-tête . En conséquence , les dossiers ne respectent pas les normes fédérales de sécurité des véhicules concernant les . -En raison des politiques énergétiques " vertes " imposées par Bruxelles - le code pour subventionner les industries françaises et allemandes de l' énergie pour les consommateurs - l' industrie européenne paie deux fois plus pour l' électricité et quatre fois plus pour le gaz que pour les États-Unis . Suite aux politiques énergétiques « vertes » imposées par Bruxelles – qui sont une raison pour subventionner les sociétés françaises et allemandes du secteur de l' énergie aux frais du consommateur – l' industrie européenne paie deux fois plus pour l' électricité et quatre fois plus pour le gaz que les États-Unis . -En tant qu' expert inquiet d' une , il se promène dans une forêt de symboles , s' arrête après chaque étape pour examiner un objet sous la forme d' un puzzle , par des questions , Analyste inquiet d' une familière , il dans une forêt de symboles , arrêté à chaque pas par un objet en forme de , tourmenté de questions . -Au fur et à mesure que des foules d' experts se réunissaient à Cardiff pour les affronter à cheval pour l' Année , ils savaient que la compétition serait rude . Lorsque de nombreux experts en manifestations équestres se sont réunis à Cardiff pour s' affronter et remporter le trophée du Cheval de l' année , ils savaient que la compétition serait rude . -Au fur et à mesure que les régimes changent , les gens ont plus de sièges , mais les sièges d' avion n' ont pas radicalement changé . Comme les habitudes alimentaires changent , les gens , mais les sièges dans les avions n' ont pas radicalement changé . -Comme chaque année , le premier spectacle d' audition a donné naissance à un grand nombre de jeunes chanteurs dans le mélange , parfois pour les meilleurs et parfois pas . Comme chaque année , les premières émissions d' audition un large choix de chanteurs en herbe , pour le meilleur et parfois pour le pire . -En ce qui concerne les hommes , je pense que nous allons voir beaucoup de zombies , grâce à The Dead Back , et je parie que hommes Mag Space le feront dans nos feed cette année . Pour ce qui est des hommes , je m' attends à voir de nombreux zombies , grâce à la série télévisée The Walking Dead , et je parie que les hommes de l' espace de Punk figureront parmi nos photos cette année . -Dès que les premiers pompiers sont arrivés , ils pouvaient clairement voir la fumée . Aussitôt que nos premiers pompiers sont arrivés , ils ont vraiment vu de la fumée apparente . -Au fur et à mesure que le festival s' élargit , le monde de la comédie . Le festival a pris de l' ampleur , tout comme le monde de la comédie . -Au moment où le procès est en cours , l' du Nevada a mis en garde sur son site Web : " il serait relativement facile de transformer ces appareils en outils de traçage à part entière " . Dès le début de l' essai , l' du Nevada sur son site : « Ce serait relativement facile de transformer ces appareils en dispositifs de localisation à part entière . » - de la façon dont il a développé son personnage , l' acteur et chanteur Justin Timberlake a découvert " qu' il a grandi dans le Tennessee , baigné dans le blues et la musique country " . Interrogé sur la façon dont il a composé son personnage , l' acteur et chanteur Justin Timberlake avait rappelé avoir " grandi dans le Tennessee , baigné par le blues et la country " . -À la question de savoir s' il était retourné au poste de Premier ministre , M. Blair a été cité par la de Londres comme suit : " Oui , bien sûr , mais il n' est pas probable que cela arrive , " ... Quand on lui a demandé s' il aimerait retrouver le poste de Premier ministre , M. Blair aurait répondu , selon l' Standard de Londres : « Oui , bien sûr , mais il peut probable que cela arrive , donc ... » -Les troupes d' Assad ont combattu des rebelles , dont beaucoup sont liés à des groupes d' Al-Qaida , en pendant des semaines . Les troupes d' el-Assad se battent contre les rebelles , dont bon nombre sont liés à des groupes du réseau Al-Qaïda , à depuis des semaines . - est soupçonné d' avoir fait l' objet d' une extradition devant la Suède au sujet d' allégations de crimes sexuels , après avoir épuisé ses moyens en vertu de la loi britannique lorsque la Cour suprême a renversé son appel à l' extradition au début du mois . pourrait être extradé en Suède suite aux allégations de crime sexuel , après avoir épuisé toutes les voies possibles en vertu de la loi britannique après que la Cour suprême a invalidé l' appel qu' il a interjeté contre son extradition au début du mois . - craint qu' il soit extradé de Suède vers les États-Unis pour des accusations d' espionnage , après avoir libéré plus de 250 secrets diplomatiques américains sur le site internet de WikiLeaks . craint d' être extradé de la Suède vers les États-Unis où il pourrait être accusé d' espionnage , après avoir dévoilé plus de 250 000 câbles diplomatiques américains sur le site WikiLeaks luttant contre les informations secrètes . -Les attaques contre les rebelles se sont intensifiées alors que les forces gouvernementales ont attaqué les bases de et ont tenté de tuer Afonso , le dirigeant du groupe , Fernando , porte-parole de , a déclaré au Financial Times . Les assauts sur les anciens rebelles ont alors dégénéré lorsque les forces gouvernementales ont attaqué les bases de la et tenté de tuer Afonso , le leader du groupe , et Fernando , le porte-parole de la , selon le Financial Times . -À 18 ans , il a été mis en place pour un . À 18 ans , il a subi une . -À , sur la partie ougandaise de la frontière , à environ 5 000 personnes qui se sont réfugiées lundi et mercredi après les Nations unies , le processus de retour qui a commencé le matin a été inversé à midi . À , du côté ougandais de la frontière , où s' étaient réfugiées environ 5000 personnes entre lundi et mercredi selon l' ONU , le mouvement de retour vers la RDC qui s' était enclenché au matin s' est inversé à la mi-journée . -À la Cour de justice de Colchester , a reconnu avoir pris des mesures de conduite sans qu' il y ait eu de soins ni d' attention suffisante . Au Tribunal de première instance de Colchester , a admis l' infraction de conduite sans la prudence et l' attention requises . -Dans un du centre commercial à Singapour , un , une bague et deux boucles d' oreilles pour payer les frais de scolarité de ses filles . Chez un prêteur sur gage du centre commercial de à Singapour , dépose un bracelet , une bague et une paire de boucles d' oreille en or pour payer les frais de scolarité de ses filles . -Lors d' une réunion de campagne , il a dit à son personnel qu' il était nécessaire de se remettre sur la route et de rester fidèle à l' objectif de sa cause . Lors d' une réunion d' , il a dit à son équipe qu' il fallait qu' ils se remettent sur la bonne voie et restent fidèles à leur cause . - . Vers mercredi , une personne à la vue qui son chien a repéré Ruby sur une corniche dans la carrière , coincée à 15 m au-dessus du sol . -Au début , j' étais dans le déni de sa mort , et je l' ai parlé dans le présent . Au début j' ai nié sa mort , je parlais de lui au présent , -Lors de son investiture devant la Cour suprême constitutionnelle , Morsi est également devenu le premier président arabe musulman librement élu et le cinquième chef d' État de l' Égypte depuis le renversement de la monarchie il y a environ 60 ans . Lors de son investiture devant la Haute cour constitutionnelle , M. Morsi est également devenu le premier président islamiste librement élu du monde arabe et le cinquième chef d' État d' Égypte depuis le renversement de la monarchie il y a 60 ans . -C' est du moins ce que j' ai pensé lorsque j' ai vu la coquille de son corps fort sous les feuilles du lit d' hôpital . C' est du moins ce que je pensais le jour où je trouvais son corps vide , sur ce lit d ’ hôpital aux draps . -Au moins un groupe en ligne a déclaré qu' il était responsable de la fermeture du site NSA avec une attaque . Au moins un groupe d' en ligne a affirmé être responsable de la panne du site de la NSA par une attaque DSD . -Au moins six autorités européennes - la Commission européenne , , l' autorité de la concurrence suisse , la , le ministère de la Justice des États-Unis et l' Autorité monétaire de Hong Kong - se penchent sur les allégations selon lesquelles les banquiers pour faire bouger le marché des devises . Dans le monde , au moins six autorités – la Commission européenne , , l' autorité de la concurrence suisse , la FCA , le Département américain de la justice et l' Autorité monétaire de Hong Kong – examinent les allégations faites concernant des banquiers qui se seraient entendus pour manipuler les taux sur le marché des changes . -Au moins deux fois plus tôt cette année , Israël a lancé des frappes aériennes sur les livraisons de missiles en Syrie . Au moins deux fois cette année , Israël a mené des frappes aériennes contre des cargaisons de missiles en Syrie . -Au moins deux personnes ont été blessées , selon la police locale . Selon la police locale , il y aurait au moins 2 blessés . -A ce stade , Titan pourrait choisir de transférer les machines à la Pologne ou à tout autre pays de l' Union européenne qui possède encore sa propre monnaie . Dès lors , Titan pourrait déménager les machines vers la Pologne ou vers tout autre pays de l' Union européenne qui a encore sa propre monnaie . -À ce propos , tout porte à croire que le quatrième bout du monde est différent , les galaxies , les danseurs s' , les spectateurs , les voix , la fumée , le passage , l' écoute des applaudissements , le battage autour d' un désordre librement développé qui reste sensuel . Là , tout devient permis : le quatrième mur tombe , les univers explorés se superposent , les danseurs , descendent parmi les spectateurs , , fument , se , jouent des codes des applaudissements , la sauce dans un désordre savamment calculé qui reste sensuel . -À ce moment , je n' ai plus peur de lui et j' ai pu l' aimer . Là , je n' ai plus eu peur de lui et j' ai pu l' aimer . -À cette époque , le président américain , élu sur la promesse de mettre fin à l' engagement militaire des Etats-Unis dans le pays , a brossé un tableau optimiste de la situation . À l' époque , le président américain , élu sur la promesse de mettre fin à l' engagement militaire dans ce pays , avait dressé un tableau optimiste de la situation . -Au , les fonctionnaires préfèrent le terme " transformation " ou " changement de continuité " à la fermeture . Même si , à l' , on préfère employer le terme de " transformation " ou de " changement dans la continuité " . -Au School Board , 62 % des élèves ont une langue maternelle autre que le français . À la commission scolaire , 62 % des élèves ont une langue maternelle autre que le français . -À l' , des stagiaires spécialisés en situations d' urgence se rendront pour cinq médecins généralistes lundi . A l' , les internes spécialisés en urgences laisseront place lundi à cinq internes de médecine générale . -À la Metropolitan Transportation Commission de la baie de San Francisco , les représentants du Congrès ont dit que le Congrès pourrait tout simplement s' occuper du Fonds pour l' interdiction de la route en faisant augmenter les taxes sur le gaz . À la Metropolitan Transportation Commission de la zone de la baie de San Francisco , les responsables disent que le Congrès pourrait très simplement gérer la faillite du Highway Trust Fund en augmentant les taxes sur l' essence . -À la , " La Belle et la " pourrait être l' un des succès de la saison . À , " La Belle et la " pourrait bien être un des succès de la saison . -Lors de la conférence annuelle de l' American Academy of Pediatrics cette semaine à Orlando , en Floride , , le Dr Paul , a expliqué que les premiers changements physiques sont très fréquents chez les filles américaines et représentent une nouvelle norme . Lors de la conférence annuelle de l' American Academy of Pediatrics qui s' est tenue cette semaine à Orlando , en Floride , le Dr Paul , , a expliqué que ces changements physiques précoces sont assez courants parmi les jeunes Américaines et représentent une nouvelle norme . -À la fin de l' après-midi , plusieurs dizaines de soldats bien équipés se dirigeaient vers l' avant . En fin d' après-midi , plusieurs dizaines de soldats , bien approvisionnés en munitions , montaient en direction de la ligne de front . -Pour l' instant , il y a une sorte de parti pris à l' égard de l' Europe et surtout de l' Italie . Pour le moment , il y a une sorte de tendance à privilégier l' Europe et en particulier l' Italie . -Au piano , les danseurs prennent des tours pour jouer les notes . Au piano , des danseurs se relaient pour jouer les partitions . -Au prix d' un paquet de cigarettes , le lecteur reçoit un livre portant la couverture traditionnelle , avec une photo d' une jeune femme dotée d' un grand corps , munie d' un pistolet ou d' un fusil d' assaut . Pour le prix d' un paquet de cigarettes , le lecteur a droit à la traditionnelle couverture avec la photo d' une jeune femme à la poitrine avantageuse , portant un pistolet ou un fusil d' assaut . -Au début de cette semaine , une famille a abandonné son appartement du rez-de-chaussée dans le bâtiment D2 en raison de la présence de rats . En début de semaine , une famille a quitté son appartement en rez-de-chaussée de l' immeuble D2 , en raison de la présence de rats . -À leur sommet de novembre , les insurgés ont envahi Goma , la capitale de la province du Nord-Kivu , en profitant du retrait de la garnison du gouvernement et de l' inaction des bérets rouges . A leur apogée en novembre , les insurgés ont occupé Goma , chef-lieu de la province du Nord-Kivu , profitant de la fuite de la garnison gouvernementale et de la passivité des casques bleus de la . -Attaque sur un tunnel creusé par des Palestiniens Une attaque contre un tunnel creusé par les Palestiniens -Des tentatives de réanimation ont été faites par les services d' urgence , mais sans succès . Des manoeuvres de réanimation ont été tentées par les services d' urgence , mais sans succès . -Les d' Akihabara lancent le " Party de la " . Un habitant d' Aubervilliers lance son Parti de la banlieue - ne s' inquiète pas de cette fluctuation du chiffre d' affaires de la société . M. ne s' inquiète pas de voir le nombre de clients fluctuer de la sorte . -Les passagers des compagnies aériennes australiennes devront continuer à leurs et leurs téléphones intelligents pendant le décollage et l' atterrissage , malgré les mesures prises aux États-Unis pour assouplir la réglementation sur les appareils . Les passagers aériens australiens devront continuer à éteindre leurs tablettes et leurs smartphones pendant le décollage et l' atterrissage malgré le geste des États-Unis visant à assouplir la réglementation relative aux appareils électroniques . -Les autorités australiennes ont précipité l' homme à l' hôpital d' Christmas , où il est décédé . L' homme a été transféré d' urgence par les autorités australiennes à l' hôpital de l' île Christmas , où il est décédé . -Les transporteurs australiens examinent la décision , qui exige des transporteurs américains qu' ils accomplissent un énorme travail pour satisfaire aux exigences , mais ils ont déclaré qu' ils n' avaient pas l' intention de changer leurs procédures . Les transporteurs australiens examinent la décision qui oblige les transporteurs américains à effectuer un travail important en vue de respecter les exigences , mais ont indiqué qu' ils n' ont pas l' intention dans l' immédiat de changer leurs procédures . -Les diplomates australiens ont joué un rôle clé dans la promotion des " objectifs de développement durable " pour remplacer les Objectifs du Millénaire pour le développement , qui sont arrivés à échéance en 2015 , devant le sommet des Nations Unies sur le développement durable qui a commencé à Rio de Janeiro . Les diplomates australiens ont joué un rôle clé dans la mise en avant des « objectifs de développement durable » destinés à remplacer les OMD , qui expireront en 2015 , avant le Sommet des Nations Unies sur le développement durable qui a commencé la veille à Rio de Janeiro . -La australienne fait appel de la peine de prison en Thaïlande Une fait appel de sa peine d' emprisonnement en Thaïlande -L' authentification portait initialement sur les aspects physiques ( aux frontières , dans les lieux protégés , dans une cafétéria , etc. ) ou sur une présence numérique ( se connectant à un ordinateur ) . L' authentification concernait au départ les accès physiques ( frontières , locaux protégés , cantines ... ) ou logiques ( ouverture d' un ordinateur ) . -Les autorités ont saisi tonnes de marijuana et de 327 livres de cocaïne dans le cadre de la découverte du tunnel , selon les registres judiciaires . Les autorités ont saisi 8,5 tonnes de marijuana et 148 kg de cocaïne après la découverte du tunnel , selon les dossiers judiciaires . -Bagdad a publié des chiffres officiels vendredi : 964 personnes ont perdu la vie le mois dernier : 855 civils , 65 policiers et 44 soldats . Bagdad a publié ce vendredi des chiffres officiels : 964 personnes ont perdu la vie le mois dernier : 855 civils , 65 policiers et 44 soldats . -Les banques sont par des messages instantanés et des courriels d' une année à l' autre pour rechercher des exemples d' actes répréhensibles . Les banques les messages instantanés et les emails envoyés depuis des années pour trouver des cas d' actes répréhensibles . -Barack Obama a ordonné à l' Agence de la sécurité nationale ( ) de cesser d' utiliser les grilles du Fonds monétaire international et de la Banque mondiale dans le cadre de ses activités de renseignement , a déclaré un des représentants officiels de l' ONU jeudi . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Barack Obama a donné ordre à l' Agence nationale de sécurité ( NSA ) de mettre fin aux écoutes qu' elle pratiquait sur le Fonds monétaire international et la Banque mondiale dans le cadre de ses activités de renseignement , indique un responsable américain , jeudi . -Barack Obama pour rencontrer le Premier Ministre iraquien en tant que Barack Obama reçoit le premier ministre irakien , en pleine flambée de violence -La baronne Kramer a dit : " Notre objectif pour est d' établir un réseau véritablement national qui rapproche le Royaume-Uni et ses villes . La baronne Kramer a déclaré : « Notre objectif pour porte sur un réseau véritablement national qui rapprochera le Royaume-Uni et ses villes . » -Quoi qu' il en soit , son projet , qui a été présenté lors d' une conférence de presse dans sa ville natale d' Aubervilliers le 8 juin , est le fruit d' une volonté de promouvoir le multiculturalisme - le grand creuset culturel qui , plus que tout , caractérise ces districts . Il n' empêche , son projet , présenté jeudi en conférence de presse dans sa commune natale d' Aubervilliers , est né de la volonté de mieux défendre le multiculturalisme , ce grand pot culturel qui caractérise avant tout les quartiers . -Parce que ses récepteurs sont défectueux , Katie a développé des caractéristiques féminines . En raison de la défaillance de ses récepteurs d' , Katie a développé des caractéristiques féminines . -En raison du coût de ces programmes , les coûts d' investissement annuels ont augmenté de 7 millions de dollars en deux ans et devraient atteindre millions de dollars en 2013 . En raison du coût de ces projets , les dépenses d' investissement annuelles ont augmenté de sept milliards de dollars en deux ans et devraient atteindre 36,7 milliards de dollars en 2013 . -Derrière le différend est une course à l' ordre des commandes avec au moins $ des activités estimées à la cotation des prix dans les prochaines années , dans les temps à attirer la balance des exportations américaines et des exportations européennes . Derrière la dispute se cache une course aux commandes d' avions pour un montant estimé d' au moins 700 Md $ au prix du marché dans les décennies à venir , suffisamment pour faire pencher la balance des exportations américaines et européennes . -Derrière les écrans de télévision , il y a toujours ceux qui sont battus pour des chanteurs réussis , saison après saison . Derrière les écrans de télévision , les vocations de chanteur à succès sont toujours là , saison après saison . -Mouvement de libération du Pakistan ( ) Pékin met en cause un mouvement islamique turkmène -Pékin Groupe d' invasion de Morelos Pékin accuse un groupe de l' attaque de Tiananmen -Les supporters belges de qui veulent se rendre au match de la Ligue des Champions devront utiliser le système de transport fourni par . Les supporters belges du qui voudront se rendre à cette rencontre de la Ligue des Champions devront obligatoirement utiliser le système de déplacement mis en place par le . -Ben : Le dixième anniversaire du New York Festival : The New Ben : Dixième anniversaire du New York Festival : The New -Le chiffre d' affaires brut des États-Unis pour la livraison de décembre a diminué de 14 cents à $ le baril à la fin du mois de février en Europe dans le commerce électronique sur le New York Exchange . Le prix du brut de référence américain pour livraison en décembre a baissé de 14 cents à $ le baril en fin de matinée en Europe lors des échanges électroniques sur le New York Exchange ( ) . -Bernard , ancien directeur de l' agence de renseignement interne française , a été cité dans une interview de ce mois-ci : " Les services savent très bien que tous les pays , même lorsqu' ils coopèrent dans le cadre de la lutte anti-terroriste , à leurs alliés . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Bernard , ancien directeur du renseignement intérieur français , la , a déclaré dans un entretien ce mois-ci : « Les services savent parfaitement bien que tous les pays , même s' ils coopèrent dans la lutte contre le terrorisme , leurs alliés . » -Bernard rappelle également la pression qui s' sur ses épaules , surtout lors des dîners d' État à l' Elysée , où il devait servir " entre 200 et 250 personnes à une heure " . Bernard se souvient aussi de la pression qui pesait sur ses épaules , en particulier lors des dîners d' Etat à l' Elysée : il faut servir " entre 200 et 250 personnes en une heure environ " . -Bernard Didier , vice-président de , pense que c' est le " siècle de la biométrie " . Bernard Didier , directeur général adjoint de , estime même que " le siècle sera biométrique " . -En plus du multiculturalisme , a élaboré son logo , qui peut être consulté sur l' Internet , qui peut être consulté sur divers sujets destinés aux habitants des banlieues , en particulier les jeunes : le droit de voter pour les étrangers , la légalisation du cannabis , l' abaissement de l' âge de 16 ans , le rétablissement de la fonction de gardien , etc. Outre le multiculturalisme , a bâti son programme sur à partir de nombreux thèmes censés parler aux habitants de banlieue et notamment aux jeunes : le droit de vote pour les étrangers , la légalisation du cannabis , la majorité civile à 16 ans , la réhabilitation de la fonction de gardien d' immeuble ... -En plus de ce spectacle , la Société d' Art du Royaume Uni les " Destinations " , un programme qui a été à La à Chicoutimi . Outre ce spectacle , la Société d' art lyrique du Royaume reviendra avec ses Destinations lyriques , une formule qui fait désormais salle comble à la de Chicoutimi . -Entre 1995 et 2001 , , également Président de la Banque de , institution publique , a permis aux entreprises et aux gestionnaires de gagner des contrats , des prêts et des promotions en échange d' investissements ou de cadeaux , le Tribunal primaire intermédiaire de Beijing a déclaré à son . Entre 1995 et 2001 , le , qui était également le président de la Banque de , un établissement public , a aidé entreprises et responsables à obtenir contrats , prêts ou promotions en échange d' argent ou de cadeaux , a déclaré le premier tribunal intermédiaire de Pékin sur son . -Entre le début des années 70 , lorsque le Boeing 747 a défini les voyages long-courriers modernes et le temps du siècle , le poids de l' Américain moyen de 40 à 60 ans a augmenté de 10 % , selon les données du U.S. Department of Health . Entre le début des années 1970 , lorsque le 747 de Boeing a défini le voyage long-courrier moderne , et le tournant du siècle , le poids de l' Américain moyen de 40 à 49 ans a augmenté de 10 % , selon les données du département américain de la Santé . -Au-delà du budget actuel et des problèmes monétaires , il y a un malaise économique croissant pour la santé . Au-delà des problèmes budgétaires et monétaires actuels , il existe un malaise plus profond lié à la productivité en Europe . - a également publié un document de la mairie du quartier de Mitte , indiquant qu' il avait été enterré dans le cimetière juif du district . publie également un document de la mairie d' arrondissement du quartier de Mitte à Berlin indiquant qu' il a été enterré dans le cimetière juif du quartier . -La biométrie sera le seul moyen de garantir l' identité d' une personne effectuant des opérations dans un monde aussi transversal et transnational que Internet . Seule la biométrie permettra de s' assurer de l' identité d' une personne réalisant des transactions dans un univers aussi et transnational qu' Internet . -La biométrie s' appuiera sur de nouvelles technologies qui permettront de fournir de nouveaux services aux citoyens et aux consommateurs . La biométrie va s' appuyer sur de nouvelles techniques , qui permettront de rendre de nouveaux services aux citoyens et aux consommateurs . -Bird to safety from North Sea to be back in Un oiseau d' une plate-forme en mer du Nord a été relâché dans la nature -Les prétentions de rendement de Boeing reposent en partie sur la comparaison de l' avec un design original de 777 . Les performances annoncées par Boeing dépendent en partie de la comparaison entre le configuré avec 10 sièges par rangée et un modèle 777 d' origine configuré en rangées de 9 . -Boeing conteste les chiffres d' Airbus concernant les mesures des sièges et déclare qu' il n' appartient pas aux constructeurs de prendre des décisions sur la manière dont les compagnies aériennes fixent les tarifs et les installations . Boeing conteste les chiffres d' Airbus en ce qui concerne la largeur des sièges et affirme que ce n' est pas aux constructeurs de décider de la façon dont les compagnies aériennes équilibrent le prix des billets et les équipements . -Selon Boeing , son " " 406 personnes sur la base d' une économie de plus de 17 pouces de large et 10 dans chaque rangée . Boeing déclare que son « » réaménagé pourra accueillir en classe économique 406 personnes dans des sièges de plus de 17 pouces de large , et sera configuré avec 10 sièges par rangée . -Bombardier , la plus grande du monde , a déclaré que les revenus de cette division ont augmenté de près de 11 % pour s' établir à 2,1 milliards de dollars . Bombardier , le plus grand constructeur ferroviaire au monde , a déclaré que les revenus de cette division avaient augmenté de près de 11 % à 2,1 Md $ . -Bombardier espère que la famille d' aéronefs pourra la dans le bas d' un marché qui est aujourd' hui dominé par Boeing et Airbus . Bombardier espère que la famille d' avions pourra la dans le segment inférieur d' un marché aujourd' hui dominé par Boeing et Airbus . - des bénéfices de Bombardier à la suite de la livraison d' aéronefs , les commandes prennent fin . Le bénéfice de Bombardier en baisse , alors que les livraisons et les commandes d' avions reculent -Bombardier a déclaré qu' il évaluerait le calendrier de l' ( DIE ) et qu' il fournira une mise à jour dans les prochains mois . Bombardier a indiqué qu' elle était en train d' évaluer le calendrier d' entrée en service et fournira de nouvelles informations au cours des prochains mois . -Des marchés de jets de grève , des mosquées , des mariages et des cérémonies funéraires . Les bombes frappent des marchés , des mosquées , des mariages et des funérailles . -Né le 24 octobre 1953 à La , l' homme avec un visage ouvert , une tête et des verres de l' intellectuel est un titulaire d' un certificat de compétence professionnelle dans le domaine de la confiserie . L' homme au visage ouvert , crâne et lunettes d' intellectuel , né le 24 octobre 1953 à la est titulaire d' un CAP de pâtisserie . -Tant les que les insistent pour qu' ils évitent la guerre . Le et la insistent pour dire qu' ils souhaitent tous les deux éviter la guerre . -Airbus et Boeing demandent 20 % plus d' efficacité par siège dans leurs plus récentes long-courriers que le leader du marché dans ce segment , le Boeing . Airbus et Boeing revendiquent tous les deux un rendement par siège dans leurs derniers modèles de long-courriers 20 % supérieur à celui du leader du marché dans ce segment , le Boeing de 365 sièges . -Tant les que les , ainsi que d' autres jeunes avec des voix étranges ( le Anthony , le , W. Bell ) , sont des produits du monde de la comédie américaine . et , ainsi que d' autres jeunes comédiens à la voix singulière ( le pessimiste surréaliste Anthony , l' ironique défenseur de la justice raciale W. Bell ) , sont des purs produits du monde décentralisé de la comédie américaine . -Les garçons - comme les filles - atteignent la puberté plus tôt . Les garçons , comme les filles , atteignent leur puberté plus tôt . -L' entreprise brésilienne Embraer , la troisième plus importante commerciale au monde et le rival le plus proche de Bombardier , a enregistré une baisse de 10 % du chiffre d' affaires trimestriel . La société brésilienne Embraer SA , troisième constructeur aéronautique au monde et concurrent le plus proche de Bombardier , a indiqué jeudi une baisse de 10 % de son bénéfice trimestriel . -Brent Gross , une référence pour le brut international également utilisé par les raffineries américaines , a chuté de 26 à $ le baril sur l' ICE à Londres . Le brut Brent , une référence pour le brut international déjà utilisé par les raffineries américaines , a perdu 26 cents pour tomber à $ le baril sur l' Exchange ( ICE ) à Londres . -La police britannique avec notification d' extradition La police britannique délivre un mandat d' extradition à l' encontre d' -Les scientifiques britanniques ont dit que les résultats étaient intéressants , mais ont souligné que de nombreuses recherches étaient nécessaires . Des scientifiques britanniques ont déclaré que les résultats étaient intéressants mais ont signalé que des recherches bien plus approfondies étaient nécessaires . -Bruce est né avec un qui a empêché son corps de produire suffisamment de testostérone pour développer correctement ses organes génitaux . Bruce est né avec un désordre du développement sexuel ( DDS ) qui a empêché son corps de produire suffisamment de testostérone pour que ses organes sexuels puissent se développer . -Les consommateurs bulgares recevront du gaz de South Stream à un tarif réduit une fois que le projet aura démarré en 2015 . Les consommateurs bulgares recevront le gaz de South Stream à un tarif réduit dès que l' ensemble du projet sera mis en service en 2015 . -Business for the Britain a été lancé en avril , avec une promesse de partager les affaires et de définir ce que la richesse et les créateurs d' emplois du Royaume-Uni veulent voir changer dans nos relations avec l' UE . La campagne Business for Britain a été lancée en avril avec la promesse de réunir les entreprises et de définir ce que les créateurs de richesses et d' emplois veulent voir changer dans notre relation avec l' UE . -Les dirigeants d' entreprises sont sceptiques à ce sujet . Les dirigeants d' entreprise sont sceptiques à ce sujet . -Les entreprises peuvent également être tenues de prendre en compte le nombre de 457 titulaires de visa après que d' autres entreprises ont commencé à se tourner vers un petit nombre de travailleurs qui ont alors employé des centaines d' autres . Les entreprises pourraient également être tenues de déclarer le nombre de détenteurs de visas 457 après que certaines d ’ entre elles , ayant eu l' intention de un petit nombre de travailleurs , en ont en fait employé des centaines . -Mais , après les innombrables moments où je me suis servi de la défense de l' administration Obama à ses collègues et à ses pairs , j' ai atteint une limite aux explications que je peux fournir . Mais après avoir maintes fois défendu l' administration d' Obama face à mes pairs et mes collègues , je n ’ arrive plus à fournir d' explications . -Mais , comme je vous l' ai dit , la CGT et Goodyear doivent d' abord parvenir à un accord sur l' indemnité de licenciement . Mais , comme je vous l' ai dit , la CGT et Goodyear doivent d' abord se mettre d' accord sur des indemnités de départ . -Mais si le prix est juste et que les travailleurs sont qualifiés , il vaut la peine d' essayer . Mais , avec un prix correct et des travailleurs compétents , cela vaut le coup d' essayer . -Mais dans l' esprit de nos anciens dirigeants , l' identité française était une essence intemporelle . Mais dans l' esprit péremptoire de nos dirigeants d' alors , l' identité française était une essence intemporelle . -Mais , de plus en plus , l' authentification implique également des transactions , en particulier celles effectuées à l' aide de dispositifs connectés à l' Internet . Mais , de plus en plus , l' authentification va concerner aussi les transactions , en particulier celles réalisées à partir d' appareils connectés à Internet . -Mais sur Twitter , les gens veulent un spectacle , l' émotion , l' originalité , la musique ... Or , sur Twitter , on veut du spectacle , de l' émotion , de l' originalité , de la musique ... -Mais Anne , directrice exécutive du groupe juridique californien pour les conseils avisés , s' inquiète de ce que la loi allemande " incite à l' étiquetage et à la stigmatisation " . Mais Anne , directrice générale de l' organisation à but non lucratif basée en Californie Advocates for Choice , craint que la loi allemande « invite à l' étiquetage et à la stigmatisation » . -Mais Berlin n' a plus besoin de Paris en tant que passeport pour la légitimité politique et a imposé sa propre politique économique à l' Europe , laissant l' économie française battue en difficulté . Mais Berlin n' a plus besoin de Paris comme passeport de sa légitimité politique et a imposé sa propre politique économique en Europe , laissant l' économie française mise à mal se . -Mais l' analyse de l' ADN prend encore beaucoup de temps et coûte cher . Mais l' analyse de l' ADN est encore très longue et très coûteuse . -Mais Dr Arlene , un de la Fédération de , qui a une fille qui souffre d' un trouble du développement sexuel et qui a aidé des centaines d' autres membres d' un groupe de soutien , a déclaré que la loi allemande " les parents et les enfants " . Mais le Dr Arlene , spécialisée dans les mammographies à Pittsburgh , dont la fille est atteinte d' un désordre du développement sexuel et qui aide des milliers de personnes dans un groupe de soutien , a déclaré que la loi allemande « » les parents et les enfants . -Mais je n' ai pas de préjugés à propos de la France . Mais je n' ai pas de préjugés sur la France . -Mais M. Chen a par la suite admis à la télévision qu' il avait pris des pots-de-vin pour fabriquer des articles sur une société appartenant à l' État . Mais M. Chen a par la suite admis à la télévision qu' il avait accepté des pots-de-vin pour fabriquer des histoires au sujet d' une société . -Mais M. Chen s' est alors rendu à la télévision de l' État déclarant qu' il avait publié de fausses histoires pour de l' argent . Mais M. Chen est ensuite apparu sur une chaîne de télévision publique où il a admis avoir publié de fausses histoires contre de l' argent . -Mais M. Hare a rejeté la suggestion selon laquelle le YMCA avait un problème culturel qui empêchait le personnel de signaler les manquements à la sécurité de l' enfant commis par lord . Mais M. Hare a rejeté l' idée selon laquelle le YMCA aurait un problème culturel empêchant le personnel de signaler les manquements de Lord envers la sécurité des enfants . -Mais M. Palmer a affirmé que ce ne sont que les ministres qui ont eu un conflit d' intérêts et que les filles d' Abbott ont personnellement profité des politiques . Mais M. Palmer a prétendu que seuls des ministres pourraient avoir un conflit d' intérêt et a indiqué que les filles de M. Abbott étaient prêtes à bénéficier personnellement de ces politiques . -Mais le projet de loi ( ) , président de la commission des transports de la Chambre des communes , a également estimé qu' il s' agissait là de la solution la plus viable à long terme . Mais le représentant Bill ( . ) , président du Comité des transports de la Chambre des représentants , a déclaré qu' il le considérait aussi comme l' alternative la plus viable à long terme . -Mais une proposition d' augmentation de la demande est venue le vendredi de deux rapports sur la fabrication chinoise qui montrent un dans l' activité . Mais deux rapports sur le secteur manufacturier chinois arrivés vendredi , qui montrent une croissance de l' activité , suggèrent une hausse de la demande . -Mais après l' audience , Lee , de , de la police de , a déclaré : " M. a été renvoyé de l' avocat criminel à un criminel dès le moment où il a commencé à recevoir des drogues de groupes criminels " . Mais après l' audience , le Lee de la Police du a déclaré : « M. est passé d' avocat à criminel lui-même dès lors qu' il a commencé à se procurer de la drogue auprès d' organisations criminelles . » -Mais à 38 ans , il s' est suicidé . Mais à 38 ans , il s' est suicidé . -Mais en acceptant de prendre le serment officiel devant le tribunal , plutôt que devant le Parlement comme étant coutumier , il s' accroche à la volonté de l' armée pour montrer que le concours pour le pouvoir se poursuivra . Mais en acceptant de prêter officiellement serment devant la Cour , plutôt que devant le Parlement comme il est d' usage , il s' incline devant la volonté de l' armée en montrant que la lutte pour le pouvoir n ’ est pas terminée . -Mais les inquiétudes se sont accrues après que M. eut dit que avait abandonné l' accord de paix de 1992 . Mais l' inquiétude a grandi après que M. a déclaré que la l' accord de paix de 1992 . -Mais la connexion à Internet pour surfer , échanger des courriels , le texte ou les données de téléchargement sera toujours interdite à moins de 10,000 pieds , a déclaré l' agence . Mais il sera toujours interdit de se connecter à Internet pour surfer , échanger des emails et des SMS ou télécharger des données en dessous de 10 000 pieds , a déclaré l' agence . -Mais déprimé et connaissant quelque chose de mal , il a exigé des dossiers médicaux . Mais déprimé et se rendant compte que quelque chose n' allait pas , il a demandé son dossier médical . -Mais chacune de ces techniques a ses limites . Mais chacune de ces techniques possède ses contraintes . -Mais depuis que Edward , l' homme dirigeant , a commencé à son dernier trésor de secrets de surveillance américains , les gouvernements et les chefs d' entreprise européens ne sont plus tenus de prendre le directeur à sa parole . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Mais depuis qu' Edward , le consultant devenu , a commencé à dévoiler son inépuisable trésor d' informations sur les secrets de la surveillance des États-Unis , les gouvernements européens et les dirigeants d' entreprises ne savent plus s' il faut croire le directeur sur parole . -Mais le porte-parole de la santé publique , Peter McLean , estime que l' annonce d' aujourd ' hui est une " solution " . Mais le porte-parole de l' opposition en matière de santé Peter Dutton pense que l' annonce d' aujourd' hui est une « une solution de fortune » . -Mais les commandes fermes du sont à peu près au point 177 , car les acheteurs potentiels attendent les résultats des essais de vol pour confirmer les affirmations de la société quant à l' efficacité du nouveau système d' alimentation en huile et au potentiel d' économies . Mais le nombre de commandes fermes pour les avions est modeste jusqu' à présent à hauteur de 177 . Il semble que les acheteurs potentiels attendent les résultats des essais en vol pour valider les affirmations de la société concernant l' efficacité énergétique et le potentiel d' économies du nouvel avion de ligne . -Mais il a nié les changements intervenus à la demande de M. Obeid , qui , M. , a reconnu qu' il était urgent de modifier la politique de location du gouvernement . Mais il a nié avoir changé sa politique à la demande de M. Obeid qui , a reconnu M. , réclamait un changement de la politique gouvernementale en matière de baux . -Mais sa prochaine création sera encore plus ambitieuse . Mais sa prochaine création s' annonce encore plus ambitieuse . -Mais dans sa déclaration liminaire à la Commission , elle a déclaré qu' elle n' avait jamais " traité d' incident sexuel impliquant des enfants à l' intérieur de son organisation " , a déclaré la Commission . Mais dans sa déclaration d' ouverture devant la Commission , le YMCA a déclaré qu' il « n' avait jamais eu à traiter d' un problème d' agression sexuelle sur des enfants au sein de son organisation » . -Mais dans un cas de 1966 , connu sous le nom de " John / Joan " , ses théories sont devenues controversées . Mais dans un cas de 1966 , connu sous le nom de « John / Joan » , ses théories ont donné lieu à controverses . -Mais dans les années 1990 , avec l' arrivée de l' Internet , les survivants de ces interventions sexuelles se sont dits " pas contents du résultat " . Mais dans les années 1990 , avec l' arrivée d' Internet , ceux qui ont survécu à ces opérations de changement de sexe disent « [ qu ' ] ils ne sont pas satisfaits du résultat » . -Mais il s' agit aussi de la connectivité , dans tout le Royaume-Uni 18 villes comme Glasgow et Édimbourg seront mieux connectés en raison de . Mais il s' agit également de développer la connectivité entre les 18 grandes villes britanniques , y compris Glasgow et Édimbourg , afin que les liaisons entre elles soient meilleures grâce à . -Mais les souris qui n' avaient pas reçu le vaccin " " , c' est-à-dire les chercheurs , montraient que la nicotine avait atteint leur cerveau . Mais les souris qui n' avaient pas été vaccinées « étaient apaisées » , ont déclaré les chercheurs , signe que la nicotine avait atteint leur cerveau . -Mais il est plus probable qu' il soit utilisé par les fumeurs pour arrêter de fumer . Mais il est plus probable qu' il soit utilisé par les fumeurs qui veulent arrêter de fumer . -Mais personne n' était prêt pour un bébé de 3 ans , Kirkland . Mais personne ne s ’ attendait à voir gagner Kirkland , un enfant de trois ans . -Mais à l' heure actuelle , plusieurs d' entre nous remettent en question le fondement éthique de l' intervention chirurgicale , sachant que l' identité des sexes est très complexe , et que les médecins peuvent parfois se tromper , sans savoir comment un enfant peut se sentir à l' aise lorsqu' il s' agit d' élever sa femme . Mais désormais , nombreux sont ceux qui remettent en cause le fondement éthique de la chirurgie , sachant que l' identité sexuelle est complexe et que parfois les médecins peuvent se tromper , ne sachant pas comment un enfant sa sexuelle lorsqu' il . -Mais aujourd' hui , les scientifiques disent que les autres peuvent frapper et répondre à ces subtiles différences de . Mais désormais les scientifiques disent que les chiens peuvent détecter ces différences subtiles et y répondre . -Mais quand ils arrivent à Yaoundé , leur conseil , transport et instruction sont couverts par . Mais une fois à Yaoundé , leur pension , leur transport et l' enseignement sont pris en charge par le docteur . -Mais une partie de son camp , qui se concentre sur le cercle présidentiel du Parti de la liberté sociale ( ) , , continue de soutenir le gouvernement , comme il l' a montré le 2 octobre lorsqu' il a refusé de suivre les ordres de Berlusconi sur une tentative précédente de mettre le cabinet . Mais une partie de son camp , regroupée autour du secrétaire national du Peuple de la liberté ( PDL ) , continue à soutenir le gouvernement , comme l' a démontré le 2 octobre dernier son refus de céder aux injonctions de Berlusconi qui souhaitait déjà faire chuter le cabinet . -Mais les passagers ne devraient pas s' attendre à ce que les changements surviennent immédiatement . Mais les passagers ne devraient pas s' attendre à des changements immédiats . -Mais les projets en cours comme " Rent " , " Le de la Femme " ou " L' du Printemps " ont des difficultés à trouver des finances . Mais des projets en cours comme " Rent " , " Le de la femme araignée " ou " L' Éveil du ­ printemps " ont du mal à être financés . -Mais elle continue d' influencer ma vie , surtout quand je suis à des moments difficiles , ou quand je dois prendre une décision importante . Mais elle continue d' influencer ma vie , notamment dans les moments où je me trouve en difficulté , ou bien lorsque je dois prendre des décisions importantes . -Mais elle est maintenant bonne . Mais elle va bien maintenant . -Mais les statistiques montrent que je suis un mouton noir quand il s' agit d' Halloween . Mais les stats montrent que je suis la brebis lorsqu' on parle d' Halloween . -Mais le procureur général du Texas , le républicain Greg Abbott , qui travaille pour le rôle du gouverneur , a demandé à la cour d' appel d' annuler le jugement du tribunal qui l' application de la loi . Mais l' Attorney General du Texas , le républicain Greg Abbott , qui le poste de gouverneur , a demandé à la cour d' appel d' annuler le jugement de première instance qui l' application de la loi . -Mais les dirigeants de la Chambre ont pris la tête de cette proposition , invoquant les préoccupations des législateurs ruraux représentant des électeurs dont la vie quotidienne implique souvent des milliers de kilomètres pour se rendre à la ville ou à la ville . Mais la présidence de la Chambre a tué la proposition , répondant aux préoccupations des législateurs ruraux représentant des électeurs qui doivent parcourir de nombreux miles tous les jours pour se rendre à leur travail ou en ville . -Mais le bleu choisi par le ministre est " trop sombre " , selon le député de Bruxelles , Richard De , qui négocie le " " de la capitale et exige la démission de Brigitte . Mais le bleu choisi par la ministre est " trop foncé " , selon le député bruxellois Emmanuel De qui parle de " " de la capitale et exige la " démission " de Brigitte . -Mais l' approche chorégraphique manque de composition . Mais l' approche chorégraphique manque de composition . -Mais la controverse entre les Américains et les Européens a continué de croître cette semaine avec de nouvelles révélations dans la presse . Mais la controverse entre Américains et Européens a continué d' cette semaine avec de nouvelles révélations dans la presse . -Mais le coût du nouveau vaccin sera probablement beaucoup plus bas , car il transforme les cellules du foie en laboratoires d' anticorps . Mais le coût du nouveau vaccin devrait être bien inférieur car il transforme les cellules du foie en usines à anticorps . -Mais le tribunal a entendu dire qu' il des grands acteurs de la chaîne d' approvisionnement en médicaments , dont certains auparavant , après des arrestations clés , lui ce que les détectives savaient de lui . Mais le tribunal a entendu des témoignages selon lesquels il appelait des acteurs majeurs dans le commerce de la drogue , dont certains qu' il avait représentés , après des arrestations clés pour leur dire quels enquêteurs étaient au courant . -Mais la décision de fermer le service d' urgence relève de l' Office régional de la santé ( ARS ) . Mais c' est à l' ARS qu' il appartient de décider la fermeture du service d' accueil des urgences . -Mais les appareils font appel à des tests pour les planificateurs de l' autoroute parce qu' ils n' utilisent pas le GPS et fournissent un nombre limité d' informations , qui sont régulièrement mises à jour par modem . Mais les appareils qu' elle teste ont plu aux planificateurs des grands axes car ils n' utilisent pas le GPS et fournissent une quantité limitée d' informations , téléchargées périodiquement par modem . -Mais la pièce prend vraiment le dessus après l' arc . Mais la pièce prend son réel envol après le salut . -Mais les services secrets étaient totalement erronés . Les services secrets se totalement . -Mais il y a aussi des niveaux élevés de taxation et de chômage en France . Mais la France a un haut niveau d' impôt et de chômage aussi . -Mais il n' y a eu qu' un léger changement dans l' âge des premières règles ( la première période ) au cours des quatre dernières décennies . Mais il n' y a eu qu' une légère avancée de l' âge des premières règles au cours des quatre dernières décennies . -Mais elles sont régulièrement saturées . Or ils sont régulièrement . -Mais cela n' a jamais été . Mais ce n' était jamais . -Mais cela est allé au-delà des limites habituelles d' une relation . Mais cela est allé bien au-delà des limites ordinaires d' une relation entre un avocat et son client . -Mais le temps a atténué la douleur . Puis , le temps a apaisé la peine . -Mais les transmissions sont plus fortes lorsque les appareils téléchargent ou envoient des données . Mais les transmissions sont plus fortes lorsque les appareils téléchargent ou envoient des données . -Mais est-ce le reflet des attentes ? Mais le spectacle était-il au rendez-vous ? -Mais nous nous sommes retrouvés dans la partie la plus difficile . Mais on a fait le plus dur . -Mais nous n' en sommes pas là auparavant - tout est complètement nouveau . Mais nous n' avons jamais connu une telle situation – elle est totalement nouvelle . -Mais nous n' avons pas vu comment la loi va jouer , et tout ce que nous pouvons faire , c' est spéculer . Mais nous n' avons pas vu ce que la loi allait donner , donc nous ne pouvons qu' avancer des hypothèses . -Mais lorsqu' ils arrivent , ils reçoivent un véritable cadeau des héros : un festin , un chant et une danse , et les meilleurs hébergements que la communauté puisse offrir . Mais lorsqu' ils arrivent , ils sont reçus en héros : la communauté organise une fête , chante et danse et leur offre les meilleurs logements disponibles . -Mais lorsqu' ils ont vu une queue se vers la gauche ( avant tout du point de vue du chien ) , leur taux cardiaque s' est élevé et ils ont éveillé l' anxiété . Mais lorsqu' ils ont repérés une queue dévier principalement vers la gauche ( encore une fois du point de vue du chien qui la queue ) , leur fréquence cardiaque s' est accélérée et ils avaient l' air anxieux . -Mais qui dit que les otages peuvent aussi avoir accès à la justice ? Mais qui dit que ces otages pourraient peut-être avoir eux aussi accès à la justice ? -Mais pourquoi un tel optimisme pour certains et le pessimisme pour les autres ? Les raisons d' un tel optimisme , chez les uns , et pessimisme , chez les autres ? -Mais pourquoi ont-ils été si déçus ? Mais pourquoi tant de déception ? -Mais avec Nicolas Sarkozy , qui a interdit le fromage , les plaques sont toujours vides ! Mais avec Nicolas Sarkozy , qui avait supprimé le fromage , les assiettes revenaient vides ! -Par un vote à cinq points , les juges ont adopté la loi sur le service à la personne et à la protection gratuite des patients - qui oblige les citoyens à acheter de l' assurance maladie d' ici 2014 ou à payer une pénalité - relève de la responsabilité du gouvernement en matière d' imposition . Par 5 votes contre 4 , les juges ont validé le mandat individuel du Patient Protection and Care Act ( loi sur la protection des malades et les soins abordables ) – qui exige que les citoyens souscrivent une assurance santé d' ici 2014 , sous peine de devoir payer une amende – et ont déclaré qu' il était constitutionnel en vertu du pouvoir d' imposition du gouvernement . -Dans tous les comptes , les travaux de fondation se sont déroulés sur le site depuis un certain temps . Selon toute vraisemblance , des travaux effectués sur les fondations se déroulaient à cet endroit depuis un certain temps . -À titre de comparaison , les écoles australiennes fournissent en moyenne 143 heures par an et les élèves environ 138 heures à Singapour . En comparaison , les écoles australiennes proposent en moyenne 143 heures par an , et les élèves de Singapour en font environ 138 heures . -En votant , nous envoyons un message clair . En allant voter , nous envoyons un message clair . -En les médias , les politiciens savent qu' ils peuvent faire ce qu' ils veulent , en toute impunité . En réduisant les médias au silence , les hommes politiques savent qu' ils peuvent faire ce qu' ils veulent en toute impunité . -D' ici à la fin de l' année , les médecins généralistes indépendants devraient également participer à la mise en oeuvre d' un " service de soins ambulatoires " . D' ici à la fin de l' année , des médecins généralistes libéraux devraient également venir participer à la mise en place d' une " permanence de soins ambulatoires " . -Les Centers for Disease Control and Prevention ( CDC ) publient des directives sur les allergies à l' intention des enfants . Le CDC publie des conseils sur les allergies chez l' enfant à destination des écoles -Les économistes de la Californie se tournent vers le système , dans la mesure où ils conçoivent des stratégies pour atteindre les objectifs fixés dans les lois ambitieuses sur le réchauffement de la planète adoptées par l' État . Les planificateurs de Californie s' intéressent au système puisqu' ils élaborent des stratégies pour atteindre les objectifs fixés dans les lois ambitieuses de l' État sur le réchauffement climatique . -Cameron , analyste de la Banque Financière Nationale , a réduit sa cote de " performance " de " " plus tard que jeudi , étant donné que le portefeuille a un rendement limité au cours de l' un ou l' autre des trimestres . Cameron , analyste au sein de la National Bank Financial , a abaissé jeudi sa notation , passant de « » à « performance de secteur » en raison du faible potentiel de hausse de la bourse au cours du prochain trimestre ou des deux prochains trimestres . -Pouvons-nous nous passer de collaborer avec les Américains ? Peut-on se priver de la collaboration américaine ? -Pouvez-vous le confirmer ? Le ? -Le fabricant d' avions de transport et de train Bombardier a annoncé une réduction de 15 % du bénéfice net du jeudi , une réduction des commandes d' aéronefs et des livraisons au troisième trimestre et des problèmes contractuels dans le secteur de la construction . Bombardier Inc , constructeur aéronautique et ferroviaire canadien , a signalé une chute de 15 % de son bénéfice net jeudi , sous la pression d' une baisse des commandes et des livraisons d' avions au cours du troisième trimestre et de problèmes contractuels dans sa division Trains . -Les cardinaux sont le plus haut dignitaire religieux de l' Église catholique sous le nom de pape , et c' est eux qui les papes ; c' est pourquoi ils leur premier groupe d' hommes qui , à leur tour , le successeur de l' un de ses successeurs . , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , etc. Les cardinaux sont les hommes d' église les plus haut placés dans l' Église catholique derrière le pape , et ceux qui l ’ élisent , donc François nommera son premier groupe d' hommes qui choisiront en définitive son successeur . -Pour sa part , Brad a remplacé les de Daisy dans le " Grand " pour les vêtements moins . Sa voix vocale est plutôt douce , mais sa langue est très quand elle parle . Quant à Carey , elle a quitté les riches atours de Daisy dans " le magnifique " pour des vêtements moins , une voix douce quand elle chante , mais le débité à la quand elle parle . -Les transporteurs devront prouver que leurs appareils peuvent tolérer l' interférence de la radio par les appareils mobiles et réviser les manuels , les matériaux de formation , les dossiers des bagages de cabine et les avis de passagers . Les transporteurs devront démontrer que leurs avions peuvent tolérer des interférences radio provenant d' appareils électroniques , de même que revoir les manuels , le matériel de formation , les programme de transport de bagages à main et les consignes fournies aux passagers . -Les voitures vont de plus en plus de miles au gallon . Les voitures peuvent parcourir plus de miles avec un gallon . - votre vote ici ou par l' intermédiaire de votre appareil mobile Votez ici ou via votre appareil mobile -Le directeur du centre , Colin , a déclaré : " Ce train d' eau est probablement un immigrant de l' Europe du Nord qui a été pris dans des vents forts en mer du Nord " . Le directeur du centre , Colin , a déclaré : « Ce d' eau était probablement un qui venait d' Europe du nord et s' est retrouvé pris au milieu de vents violents en mer du Nord . » -Cérémonie de Mémoire de la Une cérémonie en mémoire des défunts -Le changement s' est effectivement produit , dans les années qui ont suivi mon premier vote . Le changement s' est vraiment produit dans les années qui ont suivi la première fois où j' ai voté . - Transporteur aérien " " Le porte-avions en " indisponibilité " -L' entreprise expérimentale de Charlie , basée à Bristol , est célèbre pour ses saveurs inhabituelles , dont la bière , le fromage , le boeuf et la feuille d' or . La société à la démarche expérimentale de Charlie , basée à Bristol , est connue pour ses parfums de glace inhabituels tels que la bière , le fromage , le bœuf et la feuille d' or . -Charlie , fondateur de la compagnie " I Cool Cream " , a déclaré : " Il y a des choses incroyables , mais encore très tôt dans le domaine de la production , alors que £ 200 vous donne environ de la matière " . Charlie , le fondateur de la société de glaces « Me I' m Delicious » , a déclaré : « C' est quelque chose d' incroyable mais nous n ’ en sommes encore qu ’ aux débuts en termes de production , et 2 g de ce truc coûte environ 200 £ . » -Charlie Brown nous a présenté la Great quand nous étions des enfants , et la d' un est comme décorer un sapin de Noël - c' est quelque chose que nous avons fait parce que nous étions peu . Charlie Brown nous a présenté La Grosse quand on était , et sculpter une citrouille en forme de lanterne , c' est comme décorer un arbre de Noël – c' est quelque chose qu' on fait depuis qu' on est tout petit . -Charlie Francis a les propriétés de l' animal marin pour développer la . Charlie Francis a tiré parti des propriétés fluorescentes de cet animal marin pour développer une collation . -Jetez un coup d' oeil sur le site de et voyez comment vous aider . Visitez le site pour voir comment apporter votre aide . -Le principal concurrent de Chevron , Exxon , a également annoncé une chute des bénéfices nets jeudi , malgré l' augmentation de sa production pétrolière et gazière . Ainsi le principal concurrent de Chevron , Exxon Mobil a fait état jeudi d' un bénéfice net en baisse , malgré la hausse de sa production de gaz et de pétrole . -Chevron , la deuxième plus grande compagnie pétrolière d' Amérique , a annoncé une baisse des bénéfices trimestriels vendredi , en raison de la réduction de ses marges de raffinage , bien que sa production de pétrole et de gaz ait augmenté et qu' elle reste en deçà de l' objectif du groupe . Chevron , deuxième compagnie pétrolière américaine , a fait état vendredi d' une baisse de son bénéfice trimestriel en raison de la baisse de ses marges de raffinage , alors que sa production de pétrole et de gaz a augmenté tout en restant en retrait par rapport aux objectifs du groupe . -Le chef de la direction , Pierre Beaudoin , s' est assuré que Bombardier sa cible de commande de 300 jours au moment où le premier jet est mis en service . Pierre Beaudoin , président et chef de la direction , est convaincu que Bombardier atteindra son objectif de 300 commandes fermes avant la mise en service commercial du premier avion . -Le juge en chef John Roberts s' est rallié aux quatre membres les plus libéraux du tribunal alors que les juges , Thomas , et Kennedy étaient opposés . Le président de la Cour suprême , John Roberts , s' est rallié aux quatre membres les plus libéraux de la Cour , tandis que les juges , Thomas , et Kennedy ont exprimé leur désaccord . -Le chef de l' exécutif , M. Phillip Hare , a été interrogé sur un cas où un employé du Rotary a été accusé d' avoir commis des infractions à la pornographie juvénile , et un autre enseignant de la salle de gymnastique au YMCA de l' école a été reconnu coupable d' infractions sexuelles à l' égard d' enfants à l' égard de ses enfants en 1991 . Le directeur général Phillip Hare a été interrogé au sujet de deux affaires : un employé du YMCA a été accusé d' infractions relatives à la pornographie enfantine et un moniteur d' éducation physique du YMCA Hall a été reconnu coupable d' agressions sexuelles sur des enfants dans le centre où il travaillait en 1991 . -Enfant gravement blessé au dos de chez Disneyland Paris Un enfant grièvement blessé dans une attraction à Disneyland Paris -Les enfants ont besoin de stabilité et de certitude . Les enfants ont besoin de stabilité et de certitudes . -Les enfants jouent sur un char qui fait partie des rebelles , à , dans l' est de la RDC , le 31 octobre . Des enfants jouent sur un blindé qui appartenait aux rebelles du , à , le 31 octobre 2013 , dans l' Est de la RDC . -Les enfants devraient apprendre les mythes et les légendes comme des " modèles pour un mode de vie " , dit l' auteure . Il faudrait enseigner les mythes et légendes aux enfants comme « des modèles de style de vie » , déclare l' auteur . -Document de presse chinois " à revoir " Un journal chinois qui a lancé un appel « va être remanié » -Chirac , le Premier ministre , était là . Chirac , Premier ministre , est là . -Chris dit que la crème glacée réagit quand elle réagit avec la chaleur de la bouche , ce qui signifie que plus vous , plus il devient brillant . Chris explique que , puisque la crème glacée commence à quand elle réagit à la chaleur de la bouche , plus on lèche , plus elle devient brillante . -Citant les " sources exclusives " qu' il n' a pas mentionnées , le canal a indiqué que " les bombes ont visé l' envoi de missiles sol-air destinés au Hezbollah au Liban " , faisant allusion au puissant mouvement chiite libanais qui combat les rebelles aux côtés des forces syriennes . Citant des " sources exclusives " qu' elle n' a pas nommées , la chaîne à capitaux saoudiens a indiqué que " le bombardement a visé une cargaison de missiles sol-air qui était destinée au Hezbollah au Liban " , en référence au puissant mouvement chiite libanais qui combat les rebelles aux côtés des forces syriennes . -Citant les attaques du 11 septembre 2004 , les attaques menées à Madrid en mars 2004 et celles à Londres en juillet 2005 , John Kerry a assuré que les autorités américaines ont depuis lors évité de nombreuses attaques planifiées en raison de l' interception des communications et de la collecte de données . Invoquant les attentats du 11 Septembre 2001 , les attaques de Madrid en mars 2004 et celles de Londres en juillet 2005 , John Kerry a assuré que les autorités américaines avaient depuis déjoué de nombreux projets d' attentats , grâce à l' interception de communications et la collecte d' informations . -Des citoyens partout dans le monde , de même que des hauts fonctionnaires - y compris aux États-Unis - ont estimé que la mise en place d' un système de surveillance sans surveillance était un service public . Les citoyens du monde entier ainsi que les hauts responsables – y compris aux États-Unis – ont jugé que la révélation d' un système inexplicable de surveillance permanente était d' utilité publique . - que la panne a été causée par une attaque de déni de service distribuée ( ) , ce n' est pas vrai . Les allégations selon lesquelles la panne était due à une attaque de déni de service distribué [ DSD ] sont fausses . -Classique , sophistiquée , populaire , en anglais ou en français , les productions de chansons et de danse commencent à prendre fin . Classiques , sophistiquées , populaires , en anglais ou en français , les productions et prennent leur envol . -Chant classique pour les adolescents Du chant classique pour adolescents -Cliquez sur ci-dessus pour voir la cérémonie . Pour suivre la cérémonie , cliquez ci-dessus . -Clive Reynolds , MP , Tony Abbott a des conflits d' intérêt concernant le congé parental Clive Palmer prétend que le député Tony Abbott a un conflit d' intérêt à propos du système de congé parental . -Près de 50,000 maisons dans toute la province sont demeurées sans électricité peu de temps après midi le vendredi , en raison de vents violents touchant de nombreuses régions le long du fleuve Saint-Laurent . Près de 50 000 foyers sont privés d' électricité sur l' ensemble de la province , peu après midi vendredi , en raison des vents violents touchant de nombreuses régions le long du fleuve Saint-Laurent . -La coopération avec les pays partageant des points de vue similaires sera facilitée dans une Europe non fédérale des Nations Unies . La coopération avec les autres pays aux vues similaires serait plus facile dans une Europe des Nations non fédérale . - Ruby était en panne alors qu' elle se trouvait dans un petit accident de route . Ruby , un , s' était échappée après un accident de la route sans gravité - Brothers " to Folk Music L' hommage au folk des frères - Cable Très bientôt pour offrir Interactive TV ? Câble pourrait bientôt offrir la télé interactive - Cable suit donc le chemin de ses concurrents , comme Bell , même si la filiale de n' a pas encore de date précise de lancement de cette nouvelle plate-forme . Câble ainsi le pas à ses concurrents , comme Bell , même si la filiale de n' a pas encore de date précise en ce qui a trait à cette nouvelle plateforme . - Les abonnés du câble pourraient bientôt avoir accès à des applications comme Facebook , Twitter et , enfin , le service de vidéo vidéo à leur demande par la télévision dans un futur pas trop éloigné . Les abonnés de Câble pourraient bien avoir accès à des applications comme Facebook , Twitter et ultimement le service de vidéo sur demande via leur télévision dans un avenir qui ne semble pas si lointain . - les étapes de l' avion , retrouver mon fils et mes parents , mes amis , c' était tout à fait émotionnel . La descente de l' escalier de l' avion , les retrouvailles avec mon fils et mes parents , mes amis , tout cela est extrêmement fort en émotion . - . Sur la piste , des gens qui ne le connaissaient pas pensaient qu' il était si phénoménal qu' ils voulaient se faire photographier avec lui . -Des affrontements ont éclaté à Alexandrie , où la police a utilisé du gaz lacrymogène et 60 manifestants ont été arrêtés . Des affrontements ont éclaté à Alexandrie , la police a fait usage de gaz lacrymogène , 60 manifestants ont été arrêtés . -Armée congolaise les rebelles L' armée congolaise les rebelles du -À l' inverse , ces spéculations le marché des obligations dans la zone euro . Ces même conjectures soutiennent à l' inverse le marché obligataire de la zone euro . -Cooper a demandé à Martin s' il avait sérieusement pensé que Obama n' était pas au courant de la recherche sur le livre Guinness du billet . Cooper a demandé à Martin s' il pensait sérieusement qu' Obama ne savait pas que l' éviction de Biden de la liste présidentielle était examinée . -Les chefs d' entreprise ne sont généralement pas enclins à se vanter des mesures qu' ils ont prises , en l' occurrence un avantage pour un attaquant . Les dirigeants d' entreprise sont généralement peu enclins à révéler les mesures de prévention qu' ils ont mises en place , au cas où cela laisserait l' avantage à un adversaire . -On s' attend à ce que les costumes représentent 1,2 milliard de dollars sur les 6,9 milliards de dollars dépensés , selon la . Les devraient représenter 1,2 Md $ sur les 6,9 milliards dépensés , selon la NRF . -Les couples suivent la scène comme des paires d' amour , très sensuelle , même érotique . Sur scène , passent les couples , en duos amoureux , très , même érotiques . -Le tribunal n' a pas compétence pour statuer sur la politique de La cour bloque une décision relative à la politique de contrôle et de fouille du -Les grues sont arrivées sur le site juste après 10h , et la circulation sur la route principale a été détournée par la suite . Des grues sont arrivées sur place peu après 10 heures , et la circulation sur la nationale a été détournée dans la foulée . -Créé et supervisé par Caroline Bolton , fondateur de l' institution , ce festival fête son dixième anniversaire cette année avec plus de soixante spectacles dans les petits clubs et les grands théâtres . Créé et supervisé par Caroline Hirsch , la fondatrice du club de , le festival célèbre son dixième anniversaire cette année , avec plus de 60 spectacles présentés dans des petits clubs et des grands théâtres . -La création d' une telle réserve permettrait au Royaume-Uni de protéger les , à condition qu' ils aient les moyens de s' assurer qu' ils sont contrôlés - ce qui n' est pas le cas . La création d' une telle réserve marine permettrait au Royaume-Uni de ces km2 , à condition toutefois d' avoir les moyens d' en assurer la surveillance , ce qui n' est pas le cas . -Des critiques comme Claire , , affirment qu' il n' y a pas de motif de sécurité valide pour les interdictions . Des détracteurs tels que la sénatrice Claire , . , soutiennent qu' il n' existe aucune raison de sécurité valable pour ces interdictions . -Actuellement , les passagers doivent éteindre leurs , leurs et d' autres appareils une fois que la porte d' un avion ferme . Actuellement , les passagers doivent éteindre leurs smartphones , tablettes et autres appareils dès que les portes de l' avion sont fermées . -À l' heure actuelle , l' action combine les plaintes de 25 personnes qui prétendent avoir été agressées par les frères de la Sainte Croix . À l' heure actuelle , le recours réunit les plaintes de 25 personnes qui affirment avoir été agressées par les frères Sainte-Croix . - au présent est un réveil brutal , comme d' un rêve . Le retour au présent est un réveil difficile , c' est comme sortir d' un rêve . -Les attaques sont conçues pour inonder un site cible avec du trafic jusqu' à ce que les serveurs soient surchargés et que le site s' effondre . Les attaques DSD visent à inonder un site ciblé jusqu' à ce que les serveurs soient surchargés et que le site s' effondre . -L' ADN , un marqueur unique et , suscite aussi l' espoir et l' inquiétude . unique et , l' ADN suscite également espoirs et inquiétudes . -RDC : Army s s RDC : l' armée attaque le dernier fief des rebelles -RDC : L' armée prépare de nouveaux attentats RDC : l' armée prépare un nouvel assaut -Danielle , qui était dirigée par la et demandant une formation à la protection de l' enfant , s' est dite préoccupée par son comportement . Elle a indiqué que le gestionnaire des services pour les enfants de qui supervisait le Seigneur " très intimidant et très difficile d' apprendre beaucoup de temps " . Danielle , qui était encadrée par Lord et qui avait demandé une formation en matière de protection des enfants car elle s' inquiétait de son comportement , a indiqué qu' elle trouvait la responsable des services à l' enfance du YMCA , , qui Lord « très intimidante et difficile à approcher la plupart du temps » . -David Bowie : Quatre chansons non signées David Bowie : quatre inédits en écoute -De Villiers , il est régulièrement critiqué par des groupes féministes pour le chauvinisme des hommes et par les organisations de défense des droits de l' homme pour racisme - accusations qu' il en deux phrases : Régulièrement pour par des ligues féministes et pour racisme par des organisations des droits de l' Homme , Gérard de Villiers écartait ces accusations en deux phrases . -Décider d' apprendre l' arabe et de faire de l' intérêt pour le Moyen-Orient , au point de prendre une maîtrise dans une université importante , à l' époque , n' étaient pas des décisions . Apprendre l' arabe et m' intéresser au Moyen-Orient , au point d' en faire un master dans une grande école , n' ont pas été , avec le recul , des choix . -Les défenseurs de l' avortement mettent l' accent sur le fait que le contenu de la loi peut conduire à la fermeture immédiate d' un tiers des cliniques dans l' État , ces cliniques n' ayant pas obtenu le privilège d' admission de leurs praticiens . Les défenseurs de l' IVG font valoir que le contenu de la loi risque de se traduire par une fermeture immédiate d' un tiers des cliniques de l' Etat car ces cliniques n' ont pas réussi à obtenir ce " privilège d' admission " pour leurs praticiens . -Certainement pas entre moi et M. Obeid . En tout cas certainement pas entre moi et M. Obeid . -La mine 21 / 2004 Le Delta Centre-Ville maintenant fermé -L' hôtel Delta Centre-Ville à Montréal a fermé ses portes le jeudi après 36 ans d' existence . L' hôtel Delta Centre-Ville a fermé ses portes jeudi à Montréal , après 36 ans d' existence . -Delta et étaient parmi les avions qui ont déjà présenté des plans . Delta et font partie des compagnies qui ont déjà soumis des plans . -Delta s' est engagé à ne pas abandonner ses employés . Delta promet de ne pas laisser tomber ses employés . -Malgré les instructions du ministre , la fermeture devrait néanmoins prendre effet à cette date . Malgré les consignes ministérielles , la fermeture devrait tout de même être effective à cette date . -Malgré la précarité de la situation dans laquelle de nombreuses institutions culturelles sont confrontées , l' avenir semble prometteur , même à long terme . Malgré la précarité qui est le lot de tant d' institutions culturelles , l' avenir se dessine bien , même à long terme . -Malgré l' intervention rapide des services d' urgence , il a succombé à la collision frontale . Malgré l' intervention rapide des secours , il est décédé lors de cette collision frontale . -La destruction de l' équipement signifie que la Syrie ne pourra plus produire de nouvelles armes chimiques . Grâce à la destruction des équipements , la Syrie ne pourra plus produire de nouvelles armes chimiques . -La découverte de cette nouvelle espèce de dauphin à bosse vous surprendra ? La découverte de cette nouvelle espèce de dauphin à bosse vous ? - difficile pour les pharmaciens Une année difficile pour les pharmaciens -La découverte des quartiers , de notre architecture , de notre environnement , sont des points de référence ; La découverte de quartiers , notre architecture , nos lieux sont des repères . -Disney pour lancer New Series sur Tablet Disney mise sur les tablettes pour lancer une série animée - suffisamment d' artistes capables de chanter , d' agir et de danser , comme sur Broadway ? Avons-nous assez d' artistes capables comme à Broadway de chanteur , jouer et danser ? -Les médecins ne pouvaient pas s' assurer qu' il avait un grand clitoris ou un petit pénis et qu' il était convaincu qu' il ne pouvait jamais vivre une " vie satisfaisante " en tant qu' homme . Les médecins ne pouvaient pas vraiment dire s' il avait un large clitoris ou un petit pénis et étaient convaincus qu' il ne pourrait jamais avoir une « vie satisfaisante » en tant qu' homme . -Est-ce qu' il sait parler de piratage par téléphone ? A-t-il connaissance du piratage téléphonique ? - un arbitre du travail d' autrui à partir de son bureau dans la Rue ? Arbitre des oeuvres d' autrui dans son bureau de la rue ? -La vérité vous ? La vérité vous ? -L' expert en comportement des chiens , John McNamara , professeur invité à l' école de médecine de l' Université de Bristol , a déclaré que ce n' était pas la première étude à examiner la question de la gauche ou du droit . L' expert en comportement John Bradshaw , professeur invité à l' École des sciences vétérinaires de l' université de Bristol , a précisé que ce n' était pas la première étude à examiner l' importance de la gauche et de la droite chez l' espèce canine . -Douglas , de l' Association nationale des passagers aériens , a déclaré qu' il est d' avis que l' interférence des appareils est sincère même si le risque est minime . Douglas de la National Association of Airline a déclaré qu' il pensait que les interférences provenant des appareils étaient réelles , même si le risque était minime . -Des dizaines de personnes sur place Des dizaines de personnes sur place -Le Dr Georges et son équipe de bénévoles ont réalisé 700 chirurgies gratuites au cours de l' année écoulée . Le Dr . Georges et son équipe de bénévoles ont pratiqué plus de 700 actes chirurgicaux gratuitement au cours de l' année dernière . -Le Dr Georges apporte des soins de santé gratuits aux villages ruraux du Cameroun . Le Dr Georges dispense des soins de santé gratuits dans les villages du Cameroun -Le Dr Georges est l' un des 10 meilleurs photographes de MTV de 2010 . Le Dr Georges est l' un des 10 Héros CNN de l' année 2013 . -Drew , directeur général de Motorsport , a déclaré : " Le retour des participants au a changé . Drew , le directeur général de Motorsport a déclaré : « Les amateurs de rallyes qui rejoignent ont changé . » - de vitesse à avec une boisson chaude entre les jambes imposée à une amende de £ 1 000 . Un conducteur roulant à 210 km / h avec une boisson chaude entre les jambes s' est vu infliger une amande de 1 000 £ -En raison des salles du 25 Décembre , sa sortie arrivera juste à temps pour les Oscars . En salle le 25 décembre , il pourra participer de justesse à la course aux . -En raison de la protection de l' environnement et des préoccupations de santé publique , les produits d' du colon sont quatre fois moins efficaces que par le passé , mais nous ne pas tuer un enfant pour deux rats . Pour des raisons de protection de l' environnement et de santé publique , les produits de sont quatre fois moins efficaces que par le passé , certes , mais pour deux rats , on ne va pas tuer un . -Lors de la transition d' Obama en 2008 , il a obtenu une note d' approbation de 82 % . Durant la période de transition d' Obama avant sa prise de fonction en 2008 , il avait 82 % d' opinions favorables . -Lors d' une entrevue avec une radio australienne cette semaine , la pop star a dit avoir chanté pour la chanson de la chanson " Old Die " de 2004 . Au cours d' un entretien lors d' une émission de radio australienne cette semaine , la pop star a déclaré qu' elle était sur la chanson de « Old Die Hard » en 2004 . -Au cours de son arrestation , s' est emparé de son portefeuille et a tenté de retirer plusieurs cartes de crédit , mais toutes ont été saisies et un échantillon de cheveux a été à l' intérieur . Au cours de son arrestation , a ramassé son portefeuille et a tenté de retirer plusieurs cartes de crédit , mais elles ont toutes été saisies et on lui a prélevé un échantillon de cheveux . -Au cours de l' opération , le Hamas a lancé un engin explosif ciblant les " forces " ( l' armée israélienne ) et blessé cinq soldats . Pendant l' opération , le Hamas a déclenché un engin explosif visant les forces de ( l' armée israélienne ) et a blessé 5 soldats . -Pendant la période à l' examen , les grands-parents de Mme ont reçu des appels anonymes demandant des informations à son sujet , a déclaré M. . Pendant la période où le détective a enquêté sur elle , les grands-parents de Mlle ont reçu des appels anonymes où on leur demandait des informations sur elle , a ajouté M. . -Au cours du procès , un agent du FBI a déclaré que des informations fournies par le Service de renseignement financier montraient que était au Pakistan pour rencontrer Ben Laden dans le but de rétablir des liens entre des groupes terroristes d' Asie du Sud-Est et Al-Qaida . Pendant le procès , un agent du FBI a déclaré que des rapports des services de renseignement avaient révélé que était au Pakistan pour rencontrer ben Laden dans le but de rétablir des liens entre les groupes terroristes d' Asie du sud-est et Al-Qaïda . -Au cours des années de discussions entamées en 2005 , le gouvernement a insisté pour que les baux soient soumis à des appels d' offres publics . Au cours des années de discussions entamées en 2005 , le gouvernement a fait pression pour que les baux fassent l' objet d' un appel d' offres . -Chaque livre suit la même formule : une grande dose de géopolitique et d' exotisme , plusieurs scènes , une touche de violence et de torture . A chaque livre , la même recette : une grande dose de géopolitique et d' exotisme , quelques scènes de sexe hard , un zeste de violences et de tortures . -Chaque épisode contient deux histoires . Chaque épisode comprend deux histoires de 11 minutes . -Chacune de ces cliniques de fin de semaine offre un large éventail de soins médicaux . Dans cet hôpital itinérant , divers soins de santé sont prodigués . -Hier , une ONG syrienne , l' Observatoire syrien des droits de l' homme , a signalé que plusieurs explosions avaient été lancées le mercredi à l' aube dans une base aérienne de défense au , près de , sur la côte syrienne . Plus tôt , une ONG syrienne , l' Observatoire syrien des droits de l' Homme , avait fait état de plusieurs explosions entendues mercredi à l' aube dans une base de défense aérienne à , près de , sur la côte syrienne . -Auparavant , un porte-parole de NSA avait déclaré à ABC News que le réseau interne et confidentiel de l' organisation était " pas du tout compromis " . Plus tôt , un porte-parole de la NSA a indiqué à ABC News que le réseau interne sensible de l' agence n' était « pas du tout » compromis . -Des recherches antérieures avaient révélé que les chiens heureux leurs queues plus à droite ( du point de vue du chien ) , alors que les chiens nerveux ont un . De précédentes études avaient révélé que les chiens heureux davantage leur queue vers la droite ( du point de vue du chien ) , tandis que les chiens nerveux la plus vers la gauche . -Plus tôt dans le présent communiqué , les serveurs du site ont constaté que le site Web de la NSA avait été fermé pendant au moins six heures , et que le site continuait d' être inaccessible pour certains utilisateurs . Plus tôt ce soir , les de serveurs en ligne ont noté que le site de la NSA avait été en panne pendant au moins 6 heures , et certains utilisateurs ne peuvent toujours pas accéder au site . -Au début de cette semaine , les inspecteurs ont déclaré avoir achevé leur première série de travaux de vérification , en se rendant dans 21 des 23 lieux déclarés par Damas . Plus tôt dans la semaine , les inspecteurs ont déclaré qu' ils avaient achevé la première étape du travail de vérification , en visitant 21 des 23 sites déclarés par Damas . -Plus tôt cette année , Lord a été reconnu coupable d' infractions sexuelles à l' encontre de 12 garçons au cours des deux années qu' il a passées au YMCA . Plus tôt dans l ’ année , Lord a été condamné pour agressions sexuelles sur douze garçons pendant les deux années où il a travaillé au YMCA . - précoce : vieillir plus tôt que d' habitude . précoce : plus tôt -Tôt ce soir , des dizaines de résidents traversaient la frontière , dont certains ont des matelas sur la tête , pour passer la nuit en Ouganda . En début de soirée , des dizaines d' habitants traversaient la frontière , certains avec un matelas sur la tête , pour passer la nuit en Ouganda . -Les économistes sont également préoccupés par les niveaux élevés de la dette des consommateurs en Thaïlande , qui , cette semaine , se sont rapprochés de la récession technique . Les économistes craignent également le niveau élevé d' endettement des consommateurs en Thaïlande , qui est sorti de peu cette semaine d' une récession technique . -Edward , le de l' intelligence américaine , a déclaré qu' il était prêt à se rendre à Berlin pour faire des déclarations au parlement allemand si l' Agence de la sécurité nationale de l' UE et son directeur Keith Alexander ne parviennent pas à répondre à ses activités . Edward , le des services de renseignements américains , a déclaré qu' il était disposé à se rendre à Berlin pour témoigner devant le parlement allemand si la National Security Agency des États-Unis et son directeur Keith Alexander ne fournissaient pas des réponses sur leurs activités . -Égypte le premier président librement élu L' Égypte demande au premier président librement élu de prêter serment -Les islamistes égyptiens se dirigent vers pour faire échec à Morsi Les islamistes égyptiens dans la rue pour dénoncer le procès de Morsi -Quoi qu' il en soit , la direction du théâtre a décidé de mettre en oeuvre le principe de précaution et de mettre en avant la sécurité de ses clients . Toujours est-il que la direction du cinéma a joué le principe de précaution et la sécurité de ses clients à fond . -Quoi qu' il en soit , avec cette voie joyeuse , le chanteur semble être dans son élément quand ses pieds ne sont plus sur le sol . Avec ce titre , le chanteur semble en tout cas dans son élément quand il n' a plus les pieds sur terre . -Les employeurs sont venus rencontrer les employés sur place et ont rencontré les employés individuellement pour évaluer leurs besoins . Des employeurs sont venus rencontrer des employés directement sur place , on a rencontré individuellement les employés pour évaluer leurs besoins . -Fin de la route pour l' Elysée Head Fin de service pour le chef cuisinier de l' Elysée -Les cours de littérature anglaise permettront aux élèves d' étudier au moins une pièce de Shakespeare , un roman du dix-neuvième siècle , une poésie romantique et une littérature anglaise contemporaine à partir de 1914 . Dans le cadre des cours de littérature anglaise , les élèves devront étudier au moins une pièce de Shakespeare , un roman du XIXème siècle , un poème romantique et une œuvre de fiction britannique contemporaine de 1914 à nos jours . -Surtout le débat sur les rançons . Le débat sur les rançons notamment . -Fondée en 1908 comme bastion de l' enseignement laïque , l' Université du Caire devint par la suite un bastion des groupes d' étudiants islamistes dans les années 1970 . Fondée en 1908 et ancien bastion de l' éducation laïque , l' université du Caire est devenue plus tard un fief des groupes d' étudiants islamistes dans les années 1970 . -Marchés européens , à l' exception de Londres , automne à Les Bourses européennes en baisse à la , sauf Londres -Même les alliés les plus proches gardent les choses les uns contre les autres - et travaillent en divers points pour découvrir ce qui reste à faire . Même les alliés proches se cachent des choses – et regardent sous tous les angles pour découvrir ce qui n' a pas été dit . -Même en Allemagne , le parti eurosceptique alternatif pour l' Allemagne - qui a été fondé cette année - n' est arrivé qu' à partir de près de 5 millions de votes lors des élections fédérales du mois de septembre , faisant ainsi de l' opposition aux démocrates libres ( à l' instar de notre propre ) du Bundestag . Même en Allemagne , le parti eurosceptique Alternative pour l' Allemagne , fondé seulement cette année et arrivé de nulle part , a rassemblé presque cinq millions de voix lors des élections fédérales de septembre et a sorti le Parti libéral démocrate ( équivalent de nos ) du Bundestag . -Malgré tout , le a décidé de ne pas recourir à une main-d ' oeuvre indépendante , ce qui pourrait coûter jusqu' à trois fois plus cher que la prise d' un pharmacien à plein temps . Toutefois , le a décidé de ne pas engager de main-d' œuvre indépendante , qui peut coûter jusqu' à trois fois plus cher qu' un pharmacien engagé à temps plein . -Malgré tout , le nombre de clients de Cable a augmenté de pour l' exercice financier de 2013 . Toutefois , pour l' année financière 2013 , le nombre de clients de Câble est en hausse . -Même si Davis n' a jamais existé , les deux réalisateurs , qui sont des amateurs de musique populaire , ont fondé leurs histoires sur les vraies personnes du temps , comme le musicien folk Dave Van . Si Davis n' a pas existé , les deux cinéastes fans de musique folk ont bâti leur histoire sur de vraies personnalités de l' époque comme le musicien folk Dave Von . -Même si elle gagne 3 000 dollars ( 2 400 dollars ) par mois comme administrateur et que son mari travaille , les revenus familiaux mensuels ne sont pas suffisants , dit-elle . Bien qu' elle gagne 3 000 $ de Singapour ( 2 400 $ ) par mois en tant qu' administratrice et que son mari travaille aussi , le revenu familial mensuel ne suffit pas , dit-elle . -Même s' il y avait des signes et que nous avions parlé de la menstruation , elle n' avait pas été préparée émotionnellement . Même s' il y avait des signes et que nous avions parlé des règles , elle n' était pas préparée émotionnellement . -Même si nous sommes des citadins , c' est notre maison spirituelle . Et même si nous sommes des citadins , c' est notre maison spirituelle . -Chaque fabricant le livre d' une manière différente . Chaque constructeur planifie de façon différente . -Chaque matin , je vois ses regards tournés vers lui , je vois les cernes sombres qui lui donnent un coup , les rides autour des lèvres par les cigarettes , les lignes qui marquent le front sur les jours d' inquiétude . Tous les matins , je vois ses yeux inquiets se poser sur moi , je vois les cernes qui alourdissent son regard , les autour des lèvres que creusent les cigarettes , les plis qui marquent le front les jours d' inquiétude . -Tout , mais l' industrie des technologies de l' information . Tout sauf l' industrie des TI . -Tout ne peut pas être réglé en un seul rendez-vous , mais vous savez que vous pouvez compter sur le psychiatre , lui demander si c' est nécessaire , vous n' avez pas été abandonné à vous-même . Ce n' est pas une séance qui fait tout mais on sait qu' on peut compter sur lui , le rappeler si besoin , qu' on n' est pas livrés à nous-mêmes . -Tout est subventionné en Allemagne , du charbon , aux voitures et aux agriculteurs . Tout est subventionné en Allemagne , du charbon aux voitures , en passant par les agriculteurs . -La preuve auprès de la Commission est que le YMCA a été avisé qu' il avait reçu le deuxième chiffre le plus bas de toutes les quatre évaluations possibles dans le cadre d' un audit du Ministère de la culture et des services publics en août de cette année . Il a été démontré devant la Commission que le YMCA avait été informé qu' il avait reçu les secondes plus mauvaises appréciations sur quatre établissements lors d' un audit qualité du Department of Education and Communities réalisé en août de cette année . -Il y a exactement cinq ans , ma grand-mère est morte d' un cancer . Il y a précisément cinq ans , ma grand-mère des suites d' un cancer . -Sauf ma femme . Sauf à ma femme . -Les dirigeants ont également rassuré les analystes et les médias le jeudi , le programme avance en fonction de l' échéancier . Les dirigeants ont également rassurés les analystes et les médias jeudi en leur indiquant que le programme avançait conformément au calendrier . -Les cadres ont indiqué que de nouvelles orientations seraient fournies au quatrième trimestre . Les dirigeants ont annoncé que de nouvelles indications seraient fournies au cours du quatrième trimestre . -Compte tenu d' un déficit budgétaire , le pays demande de l' aide du Fonds monétaire international pour les mois suivants , mais en l' an 2000 le pays a octroyé un crédit de 15 millions de , mais cette fois il n' en a débloqué que 3 , 3 millions . Le pays , en déficit budgétaire , réclame depuis des mois l' aide du Fonds monétaire international , qui lui avait accordé en 2010 un crédit de 15,3 milliards de dollars et n' en a débloqué pour l' instant que 3,4 milliards . -Les experts affirment que les aveux sont toujours systématiquement détenus , en dépit d' une modification de la loi intervenue au début de cette année interdisant aux autorités de contraindre quiconque à s' incriminer lui-même . Les spécialistes disent que les personnes sont systématiquement contraintes à faire leurs aveux , malgré un changement dans la loi qui a été voté plus tôt dans l' année interdisant aux autorités de forcer quiconque à s' incriminer lui-même . -Les experts disent que la violence a laissé 14 adultes et sept enfants morts , ce n' est rien d' autre qu' un hasard hasard , et non un signe de violence croissante en Amérique . Des experts disent que la violence qui a causé la mort de 14 adultes et 7 enfants n' est rien d' autre qu' un malheureux hasard , et non le signe d' une escalade de violence en Amérique . -Les experts pensent que l' épidémie d' obésité pourrait être un déclencheur de la puberté précoce . Les experts pensent que l' épidémie d' obésité pourrait être un déclencheur de la puberté précoce -FAA : Les passagers aériens peuvent désormais utiliser des gadgets sur des avions ( mais ne prennent pas de téléphone cellulaire ) FAA : Les passagers aériens peuvent désormais utiliser des gadgets à bord des avions ( mais pas passer un appel avec leur téléphone portable ) -Les membres du comité consultatif de la FAA ont exprimé des sentiments partagés quant à la question de savoir si l' utilisation des dispositifs présente des risques . Cela a inspiré aux membres du comité consultatif de la FAA des sentiments mitigés quant à la question de savoir si l' utilisation des appareils présente ou non un risque . -Les FAA peuvent lever l' interdiction de certains dispositifs électroniques au moment du décollage et de l' atterrissage . La FAA peut lever l' interdiction sur certains appareils électroniques pendant le décollage et l' atterrissage -Les pages Facebook pour ces présentations sont gérées habilement par des professionnels qui répondent aux questions . Les pages Facebook de ces spectacles sont savamment ­ tenues par des professionnels qui répondent aux questions . -Une crème glacée ? Vous avez envie d' une glace ? -L' innovation de a été étendue au fil des ans , tout récemment par le New York Festival . L' innovation de s ’ est au fil des ans , jusqu' à être reprise plus récemment par le New York Festival . -Craignant que Stockholm les aux États-Unis , il a demandé refuge à l' ambassade de l' Équateur à Londres le 19 juin , demandant au pays d' asile politique de l' Amérique du Sud . Craignant que Stockholm autorise son transfert vers les États-Unis , il a trouvé refuge à l' Ambassade de l' Équateur à Londres le 19 juin , demandant à ce pays d' Amérique du sud l' asile politique . -La ministre de la Santé fédérale , Tanya , a annoncé que le gouvernement du Burkina Faso devrait prendre des " mesures d' urgence " pour contrer une crise causée par le vieillissement de la population de l' île , des maladies chroniques et des contraintes de système . La ministre fédérale de la Santé Tanya a annoncé que le Commonwealth prend actuellement des « mesures urgentes » pour mettre fin à une crise causée par le vieillissement de la population de l' état insulaire , le taux élevé de maladies chroniques et les contraintes du système . -Les frais sont un moyen de permettre aux passagers d' apporter moins à bord . Ces frais sont un moyen de s' assurer que les passagers transportent moins de choses à bord . - est un animal sauvage - il aime les chevaux , les tracteurs et les fermes et il a reçu deux poulets qu' il a . est fou d' animaux – il adore les chevaux , les tracteurs et le monde rural et a deux poulets dont il s' occupe . - a été en tant que troisième anniversaire d' existence en mars dernier et se produit avec l' des Shetland depuis ce temps . a eu en cadeau pour son 3e anniversaire en mars dernier et s' est entraîné depuis avec son Shetland tous les jours . - a été loué par les juges pour ses compétences de manipulation naturelle bien au-delà de ses années . a été salué par les juges pour ses qualités naturelles de dressage peu communes pour un enfant de son âge . -Peu de productions théâtrales sont en tournée et la plupart sont habituellement des " " , destinées aux adultes et non aux adolescents . Peu de pièces de théâtre partent en tournée et c' est souvent du ­ théâtre de boulevard destiné aux adultes et non aux jeunes adultes . - les larmes , elle a dit que aurait dû être condamnée à mort . Au bord des larmes , elle a déclaré que aurait dû être condamné à mort . -Enfin , et ce qui est le plus important , c' est que nos collègues de l' administration des affaires ont trouvé une grande majorité claire voulant que la Grande-Bretagne poursuive un processus de modification des traités et une relation avec l' UE qui repose sur le commerce , et non sur la politique . & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; Enfin , et ce qui est plus parlant encore , notre enquête auprès des chefs d' entreprise a révélé qu' une importante majorité voulait voir la Grande-Bretagne poursuivre les initiatives en faveur d' un changement du traité et du développement d' une relation avec l' UE basée sur le commerce et non la politique . -Enfin , les . Enfin , parlons de . -Enfin , on peut rassurer le public en mettant l' accent sur les mesures mises en place pour protéger les bases de données . Et , enfin , rassurer en mettant en avant les précautions prises pour protéger les bases de données . -Enfin , l' exercice généralisé des révélations menées par les médias dans le monde entier , qui contribue à la mise en place d' un débat sur les pratiques de surveillance par des services de renseignements qui sont jusqu' à présent quasi invisibles , pourrait obliger les acteurs - y compris ceux de l' Amérique - à reconsidérer les pouvoirs qu' ils ont donnés à leurs services de renseignement . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; ] Dernier élément : le vaste mouvement de révélations engagé par des médias du monde entier , qui contribue à enclencher un débat sur les pratiques de surveillance des services de renseignement jusqu' alors quasiment inexistant , pourrait pousser les législateurs , y compris américains , à reconsidérer les pouvoirs qu' ils ont donnés à leurs services de renseignement . - que l' un des nazis les plus brutaux a été enterré dans un cimetière juif est horrible , a-t-il déclaré . Que l' un des nazis les plus brutaux soit enterré dans un cimetière juif , c' est une énormité de mauvais goût . -Les équipes de lutte contre les incendies ont demandé de sauver des survivants après qu' ils ont derrière le sol sur des précaires dans une carrière Les pompiers ont été appelés pour secourir un chiot perdu après être resté coincé à 15 m au-dessus du sol sur une corniche instable dans une carrière -Incendie en 1850 House of Québec Feu dans une maison construite en 1850 à Québec -Les pompiers vers le bas de la falaise pour blesser le chien à partir de sa mort . Les pompiers sont descendus en rappel le long de la falaise pour récupérer la chienne et la sauver d' une mort certaine -Les pompiers convoqués sur les lieux du drame ont confirmé qu' ils avaient répondu à un appel signalant des " blessures multiples " . Des pompiers appelés sur les lieux du drame ont affirmé avoir répondu à un appel faisant état de " multiples blessés " . -Tout d' abord , il y a les " spectacles musicaux " qui évoluent selon la volonté des producteurs ... Il y a d' abord les " spectacles musicaux " qui évoluent au gré des producteurs ... -Il y a cinq ans , mon père est décédé . Il y a cinq ans déjà que mon père s' est effacé du monde des vivants . -Le centre de vol semble la tendance . Flight Centre semble renverser la tendance . -Les parts du centre de vol étaient à $ hier . Les actions de Flight Centre ont augmenté de 3 cents pour atteindre $ hier . -Des fleurs ont été posées sur deux des trois tombes militaires placées dans le cimetière par le maire Jean-Claude et d' autres personnalités . Des ont été déposées dans deux des trois carrés militaires du cimetière par le Jean-Claude et diverses personnalités . -À la suite des excuses de Chen , New AI a fait des excuses à la première page , affirmant qu' il n' avait pas vérifié correctement ses rapports . Suite aux excuses de M. Chen , New Express a publié ses excuses en première page , écrivant qu' il n' avait pas correctement vérifié ses articles . -Suite à la prochaine journée , publiée en janvier , il a produit une de luxe prévue pour le week-end de novembre , contenant plusieurs séries non publiées . À partir de The Next Day , paru en janvier , il a concocté une réédition de luxe prévue pour le 4 novembre , avec plusieurs inédits . -À la suite d' une mission menée dans le pays , le FMI a noté cette semaine que " les besoins importants en matière de financement extérieur " constituaient une " faiblesse " , même s' il y avait des " signes d' amélioration économique " . A l' issue d' une nouvelle mission infructueuse dans le pays , le FMI a constaté cette semaine que " les importants besoins en financement extérieur " constituaient " une vulnérabilité " , tout en relevant " des signes d' amélioration économique " . -Après des examens médicaux à l' hôpital militaire de mercredi après-midi , ils sont désormais réunis avec leur famille . Après des examens médicaux à l' hôpital militaire du mercredi après-midi , ils ont pu , depuis , retrouver leurs familles . -À la suite du départ de toutes les industries ( principaux consommateurs d' eau ) et de la forte diminution de la population , l' utilisation de l' eau dans l' ensemble du district a considérablement diminué . À la suite du départ de toutes les industries ( grandes consommatrices d' eau ) et au grand déclin de la population , l' utilisation de l' eau a considérablement diminué partout sur le territoire . -À la suite de l' indignation des États européens , et même si les révélations sur la presse américaine indiquent que le président américain n' est pas au courant de ces activités d' espionnage , Barack Obama a refusé de faire des commentaires sur la question en invoquant la sécurité nationale . & # 160 ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Face à la colère d' Etats européens et alors que des fuites dans la presse américaine affirmaient que le président américain n' était pas au courant de telles écoutes , Barack Obama a refusé de s' exprimer à ce sujet , invoquant la sécurité nationale . -Pendant 21 ans , Georges a vu son père malade descendre et sortir de la conscience , voyageant dans des hôpitaux qui n' étaient pas équipés pour l' aider . Pendant 21 ans , Georges a regardé son père malade alterner les périodes de conscience et d' inconscience et se rendre dans des hôpitaux qui n' étaient pas équipés pour lui venir en aide . -Pour , la charge de travail n' est pas une difficulté . Pour Dr , la charge de travail relativement constante n' est pas une difficulté . -Pour François , directeur général des Services de la Ville de Paris , pour faire face à l' incinération , il faut donner à la défunte une cérémonie aussi belle qu' elle aurait pu l' être avec une . la crémation , c' est pour François , directeur général des Services funéraires de la Ville de Paris , prévoir pour le défunt une cérémonie aussi digne que celle à laquelle il aurait droit avec une inhumation . -Pour les transporteurs américains , les répercussions de la décision varieront d' une compagnie aérienne à l' autre et dépendront de l' âge de leur flotte . Pour les transporteurs américains , l' impact de la décision variera d' une compagnie aérienne à l' autre et dépendra de l' âge de leur flotte . -Pendant longtemps , il est resté chez moi - quand j' ai arrêté de fumer , quand j' ai eu peur , quand j' étais malade ... Pendant longtemps , il m' a accompagné : quand j' ai arrêté de fumer , quand j' ai eu peur , quand j' ai eu mal ... -Pendant longtemps , la plupart des tentatives , de " Notre-Dame de Paris " à " Mozart , l' Opéra Rock " , ont été . Longtemps , de Notre-Dame de Paris à Mozart l' opéra rock , la plupart des tentatives s' dans le ridicule . -Par souci de prudence , le rapport aux États-Unis est un médecin pour 413 personnes . À titre de comparaison , le ratio aux États-Unis est d ' 1 médecin pour 413 habitants . -Par exemple , la France a été informée de la présence de Mohammed dans les zones tribales de la par l' intermédiaire des ressources de la NSA . A titre d' exemple , la présence de Mohammed dans les zones tribales à a été signalée aux Français grâce aux moyens de la NSA . -Je pense par exemple à la définition de la résilience . Je m' interroge par exemple sur la définition de la résilience . -Par exemple , il y a parfois neuf heures d' attente à l' unité d' urgence à . Il y a par exemple parfois neuf heures d' attente aux urgences de . -Pour les , il s' agit d' une plus grande marge de , mais pour les fournisseurs , c' est de plus en plus une question qui pourrait affecter les gains . Pour les passagers , il s' agit davantage d' avoir une certaine liberté de mouvement , mais pour les fournisseurs , c' est de plus en plus une question qui pourrait affecter leurs résultats . -Pour lui , " si les autres services d' urgence de Paris pouvaient absorber l' excédent , il n' y aurait pas de problème " . Pour lui , " si les autres services d' urgences parisiens pouvaient absorber le surplus , il n' y aurait pas de problème " . -Pour les grandes entreprises , le message a été depuis longtemps le maison qui tient la possession d' un USB à un salon commercial , ou laisser un ordinateur non portatif dans une chambre d' hôtel est déraisonnable , pour le moins . Les grandes sociétés depuis longtemps le message selon lequel il est pour le moins imprudent de récupérer une clé USB gratuite lors d' un salon professionnel ou de laisser un ordinateur portable sans surveillance dans une chambre d' hôtel . -Pour les étudiants en médecine et en soins infirmiers , comme O' Malley , qui viennent des États-Unis et de l' Europe pour rejoindre sur leurs missions , il s' agit d' une occasion pratique qu' ils ne jamais chez eux . Pour les étudiants en médecine et les étudiants infirmiers comme Katie O' Malley , qui viennent des États-Unis et d' Europe pour rejoindre M. dans ses missions , c' est une occasion d' apprentissage sur le terrain qu' ils n' ont pas chez eux . -Depuis plus d' un an , j' ai remarqué le mécontentement profond de personnes que je rencontre partout : la réévaluation vertigineuse de leurs biens , un déluge d' impôts , le coût de toutes sortes de permis , des frais administratifs illimités , etc. Depuis plus d' un an , partout , je suis à même de constater un fort mécontentement de la part des gens que je rencontre : réévaluation monstrueuse de leurs propriétés , déluge de taxes , coût des permis de toutes sortes , frais administratifs à n' en plus finir . -Depuis plus de 40 ans , nous sommes essentiellement occupés par des costumes de grande qualité , et je ne crois pas qu' un éleveur puisse faire pire . Nous sommes gérés depuis plus de 40 ans par des avec de beaux diplômes pour la plupart et je ne crois pas qu' un éleveur de moutons fasse pire . -En l' espace de dix jours , un grand nombre de journaux importants en France , en Allemagne , en Espagne et en Italie ont fait savoir que la NSA avait intercepté de nombreuses quantités de données et de communications émanant d' alliés des États-Unis et de leurs dirigeants , en particulier la chancelière allemande Angela Merkel . Depuis dix jours , plusieurs grands journaux en France , en Allemagne , en Espagne ou en Italie , ont révélé que la NSA aurait intercepté massivement des données et communications émanant d' alliés des Etats-Unis et de leurs dirigeants , notamment la chancelière allemande Angela Merkel . -Pour le gouvernement écossais , Keith Brown a demandé à M. de " s' opposer " à l' inclusion de l' Écosse dans le réseau . Pour le gouvernement écossais , Keith Brown a appelé M. à soutenir « sans équivoque » l' inclusion de l' Écosse dans le réseau . -Pour la deuxième année consécutive , l' une de ces cérémonies a été retransmise en direct , pour laquelle on n' a pas pu y assister . Pour la deuxième année consécutive , l' une de ces cérémonies est retransmise , à l' attention de ceux qui ne pourraient pas faire le déplacement . -Pour le troisième trimestre terminé le 30 septembre , le bénéfice net de Bombardier est passé à 147 millions de dollars , soit 8 cents par action , soit 172 millions de dollars , soit 9 cents par action un an plus tôt . Au troisième trimestre clos le 30 septembre , le bénéfice net de Bombardier a chuté à 147 M $ , ou 8 cents par action , par rapport à 172 M $ , ou 9 cents par action , un an plus tôt . -Depuis deux semaines , il y a presque quotidiennement des rapports d' affrontements entre les forces gouvernementales et les , certaines des pires escarmouches depuis un accord de paix il y a plus de 20 ans . Pendant deux semaines , il y a eu pratiquement tous les jours des rapports faisant état de conflits entre les forces gouvernementales et la , les pires accrochages depuis l' accord de paix qui a été signé il y a plus de 20 ans . -Ford , qui tire encore profit du succès de ses camionnettes , entre autres , a vu les ventes augmenter de 14 % par rapport à l' année dernière pour atteindre véhicules , contre une attente de . Ford , qui continue de bénéficier entre autres du succès de ses pick-up , a vu ses ventes progresser de 14 % sur un an à 191 985 véhicules , alors que le consensus attendait 194 301 . -Les travailleurs étrangers sur 457 visas pouvaient faire l' objet d' une épreuve d' authenticité . Les travailleurs étrangers en possession d' un visa 457 pourraient avoir à passer un test « d' authenticité » -Oubliez le sens et votre ticket . Oubliez d' être raisonnable et sortez vos billets . -L' ancien reporter de CNN Pat est journaliste spécialisé dans les questions de santé et de famille . L' ancien correspondant de CNN Pat est un journaliste spécialisé dans les questions familiales et de santé chez l' enfant . -Ancien chef de la tribu dans le cimetière juif L' de la enterré dans un cimetière juif -L' ancien au Liban dit que " Getting is difficile to " Un ancien otage au Liban : " Le retour est assez complexe à gérer " -L' ancien ministre de l' Intérieur de la Nouvelle-Galles du Sud , Joe , sera interrogé par le responsable de la lutte contre la corruption de l' État . L' ancien ministre travailliste de Nouvelle-Galles du Sud Joe fera l' objet d' une enquête du régulateur anti-corruption de l' État . -L' ancien ministre de la ville de , Joe , a refusé de modifier le contrat de pêche politique à la demande de son conseiller tactique Eddie , qui avait des intérêts cachés dans trois propriétés sur des terres contrôlées par le gouvernement . L' ancien ministre de Nouvelle-Galles du Sud Joe a nié avoir modifié la politique de baux commerciaux à la demande de son mentor politique Eddie Obeid , qui avait des intérêts cachés dans trois biens situés dans la zone contrôlée par le gouvernement . -L' ancien rédacteur en chef du journal international Andy aurait utilisé " le téléphone , la surveillance et l' affrontement " pour tenter de confirmer un faux argument sur une affaire impliquant le secrétaire de Charles Clarke . L' ancien rédacteur en chef de News of the World Andy aurait utilisé « le piratage téléphonique , la surveillance et la confrontation » pour tenter de confirmer un tuyau bidon sur une liaison impliquant le ministre de l' Intérieur alors en exercice , Charles Clarke . -Heureusement , un petit nombre d' entre eux l' ont fait . Heureusement , quelques uns y ont trouvé leur compte . -Quarante pour cent des catholiques romains sont en Amérique du Sud , mais ils n' ont qu' un nombre infime de cardinaux . 40 % des Catholiques romains vivent en Amérique du Sud , mais un petit nombre de cardinaux les représentent . -Quatre ( d' Al-Qaïda au Maghreb islamique ) , Daniel , Thierry , Pierre Legrand et Marc ont été libérés mardi après avoir passé plus de 1000 jours . Détenus plus de 1 000 jours , les quatre otages d' ( Al-Qaida au Maghreb islamique ) Daniel , Thierry , Pierre Legrand et Marc ont été libérés mardi . -Quatre soldats du Hamas ont été tués et cinq soldats israéliens ont été blessés la nuit du 31 octobre , lors d' un violent affrontement sur la frontière entre Gaza et Israël , l' incident le plus grave sur le territoire palestinien pendant un an . Quatre combattants du Hamas ont été tués et cinq soldats israéliens blessés jeudi 31 octobre 2013 au soir lors d' un violent accrochage à la frontière entre Gaza et Israël , l' incident le plus sérieux dans le territoire palestinien depuis un an . -Quatre d' entre eux ont déjà été publiés sur l' Internet . Quatre ont déjà émergé sur la Toile . -La France , pour sa part , doit déjà procéder à la surveillance aérienne très coûteuse et maritime de 5 millions de kilomètres carrés de la ZEE de la Polynésie française , adjacente à Pitcairn . Quant à la France , elle doit déjà mener la très coûteuse surveillance aérienne et maritime des cinq millions de km2 de la ZEE de Polynésie française , voisine de Pitcairn . -La France , porte-parole du service de protection contre les incendies du Québec , a déclaré que l' intervention rapide des pompiers a permis d' éviter que le feu se déplace vers les bâtiments adjacents , un défi majeur dans cette partie de la ville où les bâtiments sont très proches . France , porte-parole du Service de protection contre les incendies de Québec , a précisé que l' intervention rapide des pompiers a permis d' éviter la propagation des flammes aux bâtiments adjacents , un défi de taille pour ce secteur de la ville où les immeubles sont construits de manière rapprochée . - et , et , catholiques et protestants : les forces de division dans ce pays sont très anciennes . Francs et , et , catholiques et protestants : les forces de division , ici , sont très anciennes . -François Hollande , qui a réintroduit le fromage , est " un homme qui aime manger " et " il n' est pas très " . Quant à François Hollande , qui a rétabli le fromage , " c' est quelqu' un qui aime manger " et " il n' y a pas grand-chose qu' il n' aime pas " . - La police française arrêtera les supporters d' venus autrement qu' avec les cars du -De 2005 à 2007 , il a été l' hôte d' un spectacle sur la télévision terrestre intitulé " with David Steinberg " . Entre 2005 et 2007 , il a animé une émission sur TV Land qui s' appelait « Sit Down with David Steinberg » . -À partir de 2015 , les élèves devront prendre un individuel en langue , avec de fortes incitations à choisir la littérature anglaise comme une qualification distincte . À partir de 2015 , les élèves devront passer un de langue indépendant , et seront fortement incités à choisir une qualification séparée en littérature anglaise . -De à Charlotte par Nicole Kidman , Lars von Trier a tendance à pousser ses actrices jusqu' à la limite , toujours dans le but d' atteindre le meilleur . De à Charlotte , en passant par Nicole Kidman , Lars von Trier a pour habitude de pousser ses actrices dans leurs derniers , toujours pour le meilleur . -De , bastion politique et dernier bastion de la rébellion à tomber le mercredi , un journaliste de l' AFP pourrait entendre parler de l' explosion des armes lourdes . De , fief politique et dernière place forte de la rébellion tombée mercredi , une journaliste de l' AFP pouvait entendre des d' armes lourdes . -À partir du 4 novembre , les unités de lutte contre les incendies , qui constituent le quart des 40 000 personnes envoyées à l' hôpital d' urgence de l' , ont toutes reçu l' instruction de prendre en charge chaque année une trentaine de cas graves à des unités d' urgence d' autres hôpitaux à Paris . A compter du 4 novembre , les pompiers – qui assuraient environ un quart des 40 000 passages annuels aux urgences de l' – auront tous reçu pour consigne d' emmener la trentaine de cas graves par jour vers les urgences d' autres hôpitaux parisiens . -Du point de vue de la conservation , il est essentiel de connaître les différences génétiques pour qu' une espèce et , par conséquent , sa variabilité génétique puissent être protégées . D' un point de vue conservation , il est essentiel de connaître ces différences génétiques , afin de protéger l' espèce et donc sa variabilité génétique . -Le fait de forcer le paiement des frais de port de cabine s' il n' y a pas d' achat direct de la compagnie est le plus récent effort pour orienter les clients vers leur propre site Web . La décision de Frontier de faire payer des frais de bagages à main lorsque les passagers n' achètent pas directement leur billet auprès de la compagnie aérienne constitue son dernier effort pour inciter les clients à aller sur son site . -Les nouveaux frais de cabine ne commenceront pas avant l' été , bien qu' une date n' ait pas été fixée . Les frais de bagages à main de Frontier ne seront pas appliqués avant l' été , bien qu' aucune date n' ait été fixée . -Frontier Airlines a l' intention de facturer jusqu' à 100 $ pour le transport des bagages de cabine à bord de leur vol. Frontier Airlines envisage de faire payer jusqu' à 100 $ aux passagers qui transportent des bagages à main sur ses vols . -Frontier Airlines a l' intention de commencer à facturer jusqu' à 100 $ pour un sac à main et 2 $ pour le café ou le soda , mais son avis de mercredi laisse croire que les passagers pourront conserver l' ensemble . Frontier Airlines envisage de commencer à faire payer jusqu' à 100 $ pour un bagage à main et 2 $ pour un café ou un soda , bien que dans son annonce mercredi , elle indiquait que les passagers pourraient emporter la non ouverte avec eux à la descente de l' avion . - Airlines pour les bagages à main Frontier Airlines envisage de faire payer les bagages à main -Frontier a une base fidèle de clients dans sa ville natale de Denver , mais son activité diminue et perd de l' argent . Frontier a une clientèle fidèle dans sa ville d' origine , Denver , mais son activité recule et elle perd de l' argent . -Cependant , la frontière est la plus avancée dans ce domaine . Toutefois , Frontier est allée plus loin dans ce domaine . -Frontier suit également les frais de 2 $ pour le café , le thé , le soda ou le jus . Frontier fait également comme Spirit et applique des frais de 2 $ pour un café , un thé , un soda ou un jus de fruit . -La frontière dit qu' elle paiera 25 $ si la taxe est payée à l' avance , 100 $ si les voyageurs attendent de payer jusqu' à ce qu' ils soient à la porte . Frontier a déclaré qu' elle ferait payer 25 $ si les frais étaient payés à l' avance et 100 $ si les voyageurs ne payaient qu' une fois à la porte d' embarquement . -Selon la frontière , les passagers qui consomment de la soude ou du jus peuvent garder tout le temps nécessaire , et ils peuvent emporter gratuitement des recharges de café . Frontier a indiqué que les passagers qui prennent un soda ou un jus de fruit peuvent emporter la non ouverte avec eux , et ceux qui prennent du café peuvent être gratuitement . -La direction de la pétition , Kate O' Malley , a dit que les 100 $ de frais nécessaires pour faire payer les passagers à l' avance . La porte-parole de Frontier , Kate O' Malley a indiqué que les 100 $ étaient prévus pour inciter les voyageurs à payer les frais à l' avance . -D' autres tests sont nécessaires avant le début des essais humains , ce qui prendra plusieurs années , mais Michael Crystal Ph . , du Cornell Medical College à New York , a dit que les premiers signes étaient bons . D' autres tests sont nécessaires avant de commencer les essais sur l' être humain , mais le Professeur Ronald Crystal du Cornell Medical College de New York a déclaré que les premiers signes étaient encourageants . -En outre , Israël a attaqué une base militaire du nord-ouest de la Syrie , ciblant un envoi de missiles destinés au mouvement libanais chiite Hezbollah , le satellite , communiqué le jeudi 31 octobre . Par ailleurs , Israël a frappé une base militaire aérienne dans le nord-ouest de la Syrie , visant une cargaison de missiles destinée au mouvement chiite libanais Hezbollah , a rapporté jeudi 31 octobre la chaîne satellitaire . -En outre , John Kerry a déclaré dans son allocution aux Européens : " Je vous assure qu' aucun innocent n' a été induit en erreur au cours de ce processus " . Et , a affirmé John Kerry à l' adresse des Européens , " je vous assure que dans ce processus des personnes innocentes n' ont pas été " . -En outre , à cet égard , la France et les États-Unis ont mis en place des procédures , parfois décrites comme essentielles , pour la coopération et l' échange d' informations d' une manière presque quotidienne . D' ailleurs , dans ce domaine , la France et les Etats-Unis notamment ont mis en place des procédures de coopération et d' échanges d' informations quasi quotidiens et qui sont décrits de part et d' autre comme essentiels . -De plus , la détection et la vérification d' un réseau de veines ou de formes nécessitent un geste volontaire et spécifique de la part des particuliers . Et le recueil ainsi que la vérification du réseau veineux et du contour de la main supposent un geste volontaire et précis des personnes . -À Wall Street , on prévoit que le cours d' ouverture des actions américaines augmentera , après deux sessions consécutives , marquées par une tendance à la baisse . Les futures de Wall Street laissent présager une ouverture des actions américaines en hausse , après deux séances consécutives de repli . -Le lancement du groupe GM a débuté en juin et représente le plus important lancement de véhicules pour le N ° 1 américain depuis sa restructuration en 2009 . Le déploiement des de GM a commencé en juin et représente le lancement de véhicules le plus important pour le premier constructeur automobile américain depuis sa faillite et sa restructuration en 2009 . -GM rappelle qu' un certain nombre de camionnettes neuves ont été mises à la disposition des États-Unis en vue de réduire les émissions de gaz à effet de serre . GM rappelle certains de ses nouveaux aux États-Unis pour réparer le dossier des sièges -Les parts de GM ont augmenté de 1,4 pour cent à $ à la Bourse de New York le vendredi après-midi . Le cours des actions de GM a grimpé de 1,4 % pour atteindre $ à la bourse de New York vendredi après-midi . -GM a informé les propriétaires de camions de la défectuosité du premier semestre d' octobre . GM a informé les propriétaires de du défaut au cours de la première quinzaine d' octobre . -Gazprom de Gazprom dit que le pipeline en Bulgarie commence une nouvelle ère de gaz Alexei Miller de Gazprom déclare que le gazoduc de Bulgarie inaugure une nouvelle ère du gaz -Gazprom a déclaré que la construction devrait commencer dans d' autres pays en aval d' ici la fin de l' année . Gazprom a indiqué que la construction devrait démarrer dans d' autres pays situés en aval d' ici la fin de l' année . -L' égalité des sexes n' est toujours pas bien comprise , mais la plupart des experts des États-Unis pensent que lorsque le sexe n' est pas reconnu , il est préférable d' utiliser les meilleures informations disponibles pour qu' elles parviennent à attendre et à surveiller le développement psychologique et physique de l' enfant avant d' entreprendre une opération de contraception , si c' est le cas . L' identification sexuelle n' est toujours pas bien comprise , mais la plupart des spécialistes aux États-Unis disent que lorsque le sexe ne peut pas être déterminé , il vaut mieux utiliser les meilleures informations disponibles pour l' attribuer , puis attendre et suivre le développement psychologique et physique de l' enfant avant d' envisager un acte chirurgical , le cas échéant . -Les garçons non ont maintenant un camp spécial . Les jeunes garçons de genre ont désormais des camps qui leur sont spécialement destinés . -General Motors Co . General Motors Co rappelle près de 19 000 de ses tout nouveaux Chevrolet et GMC Sierra 2014 pour régler un problème avec les dossiers manuellement , selon une déclaration vendredi de l' organisme américain chargé de la réglementation de la sécurité des véhicules . -George : L' éclatement de l' Europe donne le choix à l' écossais George : L' éclatement de l' Europe donne le choix aux Écossais -Gérard , né le 8 décembre 1950 à Paris , a publié , le 4 décembre 1970 , des livres sterling , et il ne savait pas combien de livres il avait vendus depuis la publication " SAS à Istanbul " , la première de la série , près d' un demi-siècle en 1965 . Avec quatre SAS publiés par an , Gérard de Villiers , né le 8 décembre 1929 à Paris , assurait ignorer le nombre exact de livres vendus depuis 1965 et la publication de " SAS à Istanbul " , le premier de la série , il y a près d' un demi siècle . -Gérard de Villiers , " SAS " Série Auteur , Dies . Gérard de Villiers , l' auteur de " SAS " , est mort . -Gérard de Villiers , un phénomène de la littérature française , qui est décédé le jeudi à 83 heures , vient de publier son 200e livre " " , La du Kremlin . de l' édition française , Gérard de Villiers , décédé jeudi à 83 ans , venait tout juste de publier son 200e SAS , " La du Kremlin " . -Gérard de Villiers a dit lui-même : " Comme tous les héros , est sans âge . Gérard de Villiers l' avait dit : " , comme tous les héros , n' a pas d' âge " . -German Journalists to Google and Yahoo Les journalistes allemands invités à fuir Google et Yahoo -Le député allemand jeudi a rencontré M. en Russie , où il a obtenu l' asile , pour discuter de son président en Allemagne . Le député allemand a rencontré jeudi M. en Russie , où il bénéficie du droit d' asile , pour discuter de son témoignage en Allemagne . -Les entreprises allemandes estiment que les Etats-Unis représentent presque aussi un risque aussi élevé que la Chine en ce qui concerne l' espionnage industriel et le vol des données , selon une enquête publiée en juillet par , la consultation . Les sociétés allemandes pensent que les États-Unis représentent un risque aussi important que la Chine lorsqu' il s' agit d' espionnage industriel et de vol de données , selon une enquête publiée en juillet par le cabinet de conseil . -Le pilote allemand , Sebastian Vettel , Formula 1 Champion du Monde , a reçu un nouveau casque spécial à l' aide d' un américain de 13 jours dans le cadre de la séance de qualifications pour le Grand Prix d' Abu Dhabi le vendredi . L' Allemand Sebastian Vettel , quadruple champion du monde de Formule 1 , a pour les essais libres du Grand Prix d' Abou Dhabi vendredi un casque spécial dont le dessin a été réalisé par un fan mexicain de 21 ans . -La loi allemande a pour but de l' attribuer à la naissance . La loi allemande concerne l' attribution du sexe à la naissance . -Les médias allemands disent que le espère identifier la présence d' antennes d' écoute et l' action déclenche un échange entre les États-Unis et le ministère allemand des Affaires étrangères à Berlin . Les médias allemands expliquent que le espérait identifier la présence d' antennes d' écoute et que l' opération a suscité un échange entre les États-Unis et le ministère des Affaires étrangères allemand à Berlin . -Les passeports allemands auront une troisième désignation autre que M ou F - X , pour , selon le ministère de l' Intérieur . Les passeports allemands comporteront une troisième désignation à côté de M ou F – X pour , selon le ministère de l' Intérieur . -Le de l' Allemagne conseille aux dirigeants d' envisager d' utiliser des téléphones mobiles prépayés en cas de voyages à l' étranger en raison du risque que des téléphones intelligents soient compromis . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Le d' Allemagne conseille à ses dirigeants d' envisager d' utiliser de simples téléphones portables prépayés lors de leurs voyages à l' étranger en raison du risque d' atteinte à l' intégrité des smartphones . -L' Allemagne et quelques économies satellites pourraient conserver l' euro , mais la France et le Sud de l' Europe leur propre monnaie . L' Allemagne et quelques-unes de ses économies satellites pourraient conserver l' euro , mais la France et l' Europe du sud retrouveront leur propre devise . -L' Allemagne est devenue la première nation européenne à reconnaître un troisième sexe pour les enfants nés avec des organes génitaux sensibles . L' Allemagne est devenue la première nation européenne à reconnaître un troisième sexe pour les nourrissons nés avec des organes sexuels ambigus . -Les filles qui ont tôt fait d' être doivent s' assurer que , même lorsqu' elles se présentent à l' avance , le processus est une partie normale de la vie . Les filles dont la puberté est précoce doivent être sur le fait que , même lorsque ça arrive plus tôt que prévu , le processus fait partie de la vie . -À l' échelle mondiale , les formes de puberté précoce semblent influencées par tous les aspects , de la situation économique au climat des gènes . À l' échelle mondiale , les mécanismes de la puberté précoce semblent être influencés par tout , des conditions économiques au climat , en passant par les gènes . -Allez voter , c' est la meilleure façon de s' exprimer et de dire qu' il suffit de le faire . Allez voter , c' est la meilleure façon de vous exprimer et de dire que c' est assez . -Google , Samsung , a intenté des poursuites contre les brevets de Nortel Google , Samsung et font l' objet de poursuites judiciaires concernant les brevets Nortel -Boutique de verre de Namur ( en anglais ) Lancement d' une boutique d' accessoires pour Google Glass -Google a augmenté sa soumission à plusieurs reprises , ce qui lui a permis de réaliser jusqu' à 4,4 milliards de dollars . Google a augmenté son offre à plusieurs reprises , pour finalement proposer 4,4 Md $ . -Google est accusé d' avoir enfreint sept brevets . Google est accusé d' avoir violé sept brevets . -Peu à peu , à cause de petites et légères apparences ou au cours de la journée et de la nuit , elle est revenue de nouveau dans ma vie , après avoir repris sa place dans le paysage de ma pensée qui le toujours en deuil . Peu à peu , par petites apparitions diurnes ou nocturnes , un peu timides , un peu effacées , elle est vite revenue dans mon esprit , évoluant au fur et à mesure qu' elle reprenait sa place dans le paysage de ma pensée qui se croyait en deuil . -Grand opposition à la mort de au Care Committee Beaucoup d' opposition à l' aide médicale à mourir au Congrès des soins palliatifs -Grèce : Two Dead in Shooting Games Project Grèce : deux morts dans une fusillade près d' un local du parti -Le travail de groupe , les petits en déplacement , en groupe , en , rendent la représentation moins . Travail de groupe , petits solos de transitions , ensembles qui se déplacent , choeurs , l' écriture est moins binaire . -L' adaptation de William du roman de Denis présente un design et un temps de production exceptionnels , mais il est aussi plus lourd qu' il ne devrait l' être . L' adaptation par Guillaume du roman de Denis se targue d' une direction artistique remarquable et d ’ une minutieuse reconstitution d' époque , mais elle est également plus laborieuse qu' elle ne devrait l' être . -La HSBC , Citigroup , et le FMI ont également lancé des internes ou reçu des demandes d' information de la part des organismes de réglementation , qui connaissent bien la situation . HSBC , Citigroup , et Crédit Suisse ont également lancé des enquêtes internes ou reçu des demandes d' informations des régulateurs , ont indiqué des sources proches du dossier . -Halloween 2013 : Par le nombre Halloween 2013 : En chiffres -L' Halloween est un festival célébré le jour avant la Toussaint , principalement dans les pays anglophones . Halloween est une fête païenne célébrée à la veille de la Toussaint , essentiellement dans les pays anglo-saxons . -Le Hamas garantit que Gaza sera l' enfer pour l' occupant " , a déclaré le porte-parole . " Le Hamas assure que Gaza sera un enfer pour l' occupant " , a menacé le porte-parole . -Le Hamas a défendu son utilisation des tunnels dans la lutte contre Israël , en affirmant qu' il visait à capturer des soldats israéliens afin qu' ils puissent être échangés contre des prisonniers palestiniens . Le Hamas a revendiqué l' usage de tunnels pour lutter contre Israël , précisant que le but était d' enlever des soldats israéliens pour les échanger contre des prisonniers palestiniens . -Le Hamas a loué les " héros qui sont morts pour défendre le territoire contre une incursion à Khan par l' ennemi " . Le Hamas félicite les héros d' qui sont tombés en défendant le territoire lors d' une incursion de l' occupant sioniste à Khan . -Hans-Peter Friedrich , ministre allemand de l' Intérieur , a déclaré à Time Online : " Si M. est disposé à parler aux autorités allemandes , nous trouverons des moyens pour y parvenir " . Hans-Peter Friedrich , ministre de l' Intérieur allemand , a déclaré au Zeit Online : « Si M. est prêt à parler aux responsables allemands , nous ferons en sorte de rendre cela possible . » -La France a-t-elle bénéficié des informations fournies par la NSA concernant les opérations terroristes à l' encontre de nos intérêts ? La France a-t-elle bénéficié d' informations fournies par la NSA concernant des opérations terroristes visant nos intérêts ? -Ayant été expulsé du Parti communiste en juillet 2012 , Tian a été accusé d' avoir reçu 19 millions de yuan en pots-de-vin , selon des représentants des médias en Chine . du Parti communiste en juillet 2012 , Tian était accusé d' avoir touché plus de 19 millions de yuans de pots-de-vin , rapportent les médias officiels chinois . -Après avoir entré la des cuisines au Palais de l' Elysée comme assistant après avoir travaillé dans les ambassades , le cuisinier a grimpé à l' échelle pour devenir chef cuisinier il y a neuf ans . Entré dans les 500 m2 des cuisines comme commis après avoir travaillé dans des ambassades , le cuisinier a gravi tous les échelons jusqu' à devenir chef , il y a neuf ans . -Il a ajouté : " Nous n' avons pas pu repartir à zéro ; nous avons donc été contactés pour obtenir de l' aide . Il a ajouté : « Il était incapable de s' envoler à nouveau , nous avons donc été contactés pour apporter notre aide . » -Il a ajouté : " Nous sommes dans un nouveau monde où les gens sont prêts à s' enfuir " . Il a ajouté : « Nous vivons dans un monde nouveau où les gens sont prêts à se faire sauter . » -Il a ajouté que les auteurs et les enseignants étaient " " , c' est-à-dire qu' il y avait " abandon complet " pour les enfants , et que les messages étaient " " dans des histoires agréables . Il a ajouté que le fait que les auteurs et les enseignants soient « ouvertement didactiques » fait que les enfants « complètement » , les messages étant « » dans des histoires agréables . -Il a ajouté que , tout comme les humains , pour les chiens , le côté du cerveau était responsable du mouvement et inversement , et les deux hémisphères ont joué des rôles différents dans les émotions . Il a ajouté que de même que chez l' homme , chez les chiens le côté droit du cerveau était responsable du mouvement vers la gauche et vice versa , et que les deux hémisphères jouaient un rôle différent dans les émotions . -Il a reconnu la possession de cocaïne à une audience antérieure . Il avait admis la possession de cocaïne au cours d' une audience précédente . -Il a informé les parents d' un garçon dont le pénis avait été dans une excision pour que l' enfant ait complètement , lui aussi ses testicules , et pour qu' il l' élève comme fille . Il avait conseillé aux parents d' un petit garçon dont le pénis avait été coupé lors d' une circoncision de le faire entièrement , en lui enlevant également les testicules , et de l' élever comme une fille . -Il m' a donné ma vie privée . Il me laissait mon intimité . -Il a également déclaré qu' une femme et un enfant avaient été tués à la suite de la soirée précédente et avaient été enterrés . Selon lui , une femme et son enfant ont été tués par les combats de la veille et ont été enterrées . -Il apparaît dans l' une de leurs publicités , diffusée à partir du 10 novembre . Il apparaîtra dans un de leurs spots publicitaires , diffusé à partir du 10 novembre . -Il est devenu médecin , travaillant comme chirurgien vasculaire à l' hôpital central de Yaoundé . Il est devenu médecin lui-même , travaillant comme chirurgien vasculaire à l' hôpital central de Yaoundé . -Il a rédigé l' idée après avoir lu un document de recherche sur les méduses et a convaincu les scientifiques chinois de recréer chimiquement la protéine . Il en a eu l' idée après avoir lu une étude sur les méduses et a convaincu des scientifiques chinois de recréer chimiquement la protéine brillante . -Il a inventé l' expression " identité sexuelle " et a affirmé que les indices sociaux et environnementaux - comment les parents d' un enfant - ont tissé des liens avec les gènes et les hormones d' un enfant afin de déterminer si la personne était considérée comme mâle ou femelle . Il a consacré le terme « identité sexuelle » et soutenait que les signaux sociaux et environnementaux – la façon dont les parents élèvent un enfant – interagissent avec les gènes et les hormones d' un enfant pour le façonner afin qu' il soit identifié comme étant de sexe masculin ou féminin . -Il a admis que la déclaration liminaire du YMCA à la commission était également inexacte en déclarant qu' il y a eu " des vérifications externes du YMCA qui reconnaissent que le YMCA est à l' avant-garde de la sécurité des enfants " . Il a également admis que la déclaration d' ouverture du YMCA était inexacte en soulignant , « des contrôles externes du YMCA ont été effectués et ont indiqué que la sécurité des enfants était une priorité absolue pour l' organisation . » -Il pourrait être libéré dans les 15 ans s' il obtenait la libération conditionnelle . Il pourrait être libéré au bout de 15 ans sous libération conditionnelle . -Il ne croit pas que l' Ontario ira de l' avant . Il ne croit pas que l' Ontario le pas . -Il ne se considère pas comme un héritier d' un éternel ; il refuse la définition . Il ne se pose pas en héritier d' une éternelle , il se refuse à la définition . -Il a estimé , dans un rapport de décembre 2011 , que les frais de cabine pour les animaux de compagnie s' élèvent à 50 millions de dollars par année . Il a estimé , dans un rapport de décembre 2011 , que les frais de bagages à main de Spirit rapportaient 50 M $ par an . -Il a même attiré l' attention des services de renseignements européens qui auraient pu bénéficier de ces communications avant de les transmettre à la NSA . Il a même renvoyé la balle aux services de renseignement européens qui se seraient saisis de ces communications , avant de les fournir à la NSA . -Il donne sa vie à cette organisation , et son désir d' aider le Cameroun est éternel . Il donne sa vie pour cette association , et son envie d' aider le peuple camerounais n' est pas près de s' éteindre . -Il trop près de certains clients , en particulier Scarborough , et il lui permettait d' être compromis . Il s' est trop rapproché de certains clients , en particulier Scarborough , et il a laissé compromettre son indépendance . -Il avait évité de se cacher pendant près d' une décennie , mais a finalement été arrêté en janvier 2011 dans la ville pakistanaise de , où les forces américaines ont tué l' ancien chef d' Al-Qaïda Osama Ben Laden au moins quatre mois plus tard . Il avait réussi à éviter de se faire capturer pendant presque 10 ans mais a finalement été arrêté en janvier 2011 dans la ville pakistanaise de , où les forces américaines l' ancien chef d' Al-Qaïda , Oussama ben Laden , moins de quatre mois plus tard . -Il était en mauvaise santé parce qu' il avait eu une crise cardiaque majeure en décembre 2010 , et , à ce moment-là , il avait utilisé un cadre de marche . physiquement par un très grave accident cardiaque en décembre 2010 , il se déplaçait durant ce voyage avec un . -Il avait juste passé dix jours en Afghanistan , à la création des numéros 198 et 199 de sa célèbre série d' oeuvres d' art . Il venait alors de passer dix jours en Afghanistan , théâtre des 198 et opus de la célèbre série de romans d' espionnage . -Il a servi six présidents français , Georges Pompidou à Nicolas Sarkozy et François , y compris dans leurs foyers de vacances , et nourri un nombre incalculable de personnes dans le monde entier , entouré d' un nuage d' anonymat qu' il n' a pas vu depuis aujourd' hui , au moment où il prend sa retraite à l' âge de 60 ans . Il a servi six présidents français , de Georges Pompidou à François Hollande en passant par Nicolas Sarkozy , y compris sur leurs lieux de vacances , et nourri un nombre incalculable de grands de ce monde dans un qu' il ne brise qu' aujourd' hui , à l' heure de la retraite , à 60 ans . -Il a aidé à réaliser en 2001 une étude de 6 à 9 ans sur les jeunes filles qui relient la graisse corporelle au moment de la puberté . Il a participé à une étude menée sur des filles âgées de 6 à 9 ans qui lie le pourcentage de masse grasse au déclenchement de la puberté . -Il est toujours à l' hôpital ou essaie de faire de l' argent pour l' organisation afin qu' il puisse faire campagne . Il est toujours à l' hôpital ou en train d' essayer de trouver de l' argent pour son association afin de pouvoir mener ces campagnes . -Il est tombé dans un coma d' un mois à la suite de l' attentat et a été " mort " pendant trois mois dans le cadre de son soutien à la vie . Il a sombré dans le coma au lendemain de l' attentat et est resté inconscient pendant un mois , et il est « mort » trois fois alors qu' il était sous assistance respiratoire . -Il n' a jamais su connaître le sens du mot " retraite " . Il n' aura jamais connu le mot retraite . -Il a néanmoins félicité le Gouvernement pour avoir répondu à sa demande d' aide d' urgence qu' il avait d' abord adressée au Premier Ministre au début du mois de mai . Il a néanmoins félicité le gouvernement pour avoir répondu à la demande d' aide urgente qu' il a présentée au Premier ministre début mai . -Il a réuni un véritable festin pour ses fans pour marquer l' occasion . Pour l' occasion , il a concocté un vrai festin pour ses fans . -Il a accepté avec réticence les derniers rôles de dans les studios . Il accepte à reculons les remplacements au pied levé dans les studios . -Il continue de violer ses conditions de mise en liberté sous caution . Il continue à enfreindre les conditions de sa mise en liberté sous caution . -Il a pris sa retraite en 2008 , mais en 2004 il a participé à la création d' une association de défense des otages , " du monde " ( otage du " monde " ) , qu' il depuis plusieurs années . depuis 2008 , il a participé en 2004 à la création de l' association de défense des otages " du monde " , qu' il préside depuis plusieurs années . -Il a dit : " Nous sommes libres , mais je ne suis pas sûr et j' ai pensé que nous pourrions toujours être tués . Il me dit " on est libérés " , mais je n' en suis pas sûr , et me dis qu' on peut encore être tués . -Il a dit : " Je veux vraiment développer une crème glacée invisible " . Il a ajouté : « Je voudrais vraiment développer une glace invisible . » -Il a dit : " Si plusieurs réunions sont organisées avec d' autres chiens , et que la plupart d' entre elles sont souvent par une attitude de plus en plus forte , le comportement inverse donne un comportement moins amical , et on répond de la même façon " . Il a ajouté : « Si vous rencontrez à plusieurs reprises d' autres chiens et si vous remarquez que , la plupart du temps , le mouvement de leur queue vers un côté est associé à une attitude plus amicale et que le côté droit entraîne une attitude moins amicale , vous en fonction de cette expérience . » -Il a dit : " Dans certains cas , il a été trop mal géré " . Il a déclaré : « Dans certains cas , c' est allé trop loin de manière inappropriée . » -Il a dit que les dirigeants européens doivent être honnêtes avec leur propre peuple au sujet des programmes d' espionnage qu' ils utilisent depuis des années . Il a expliqué que les chefs d' État européens devaient être honnêtes avec leurs citoyens au sujet du type de programmes d' espionnage qu' ils appliquent eux-mêmes depuis des années . -Il a dit qu' il était " tout à fait possible " que François demande aux Cardinaux d' obtenir des conseils . Il a ajouté qu' il était « tout à fait possible » que François demande conseil aux cardinaux . -Il a dit qu' il avait l' intention d' éviter la contamination des éléments de preuve , mais qu' il s' agissait d' un " " excessif et d' une exécution médiocre . Il a déclaré que cette décision était destinée à éviter toute contamination des éléments de preuve mais qu' il s' agissait d' un « excès de zèle » et qu' elle avait été mal mise en œuvre . -Il a dit que le a utilisé trois moyens pour enquêter sur les histoires : le piratage téléphonique , la surveillance et la confrontation . Il a déclaré que utilisait trois moyens pour enquêter sur les sujets : le piratage téléphonique , la surveillance et la confrontation . -Il a dit que les différences peuvent être parce que les chiens dans les différentes études ne pas interpréter complètement les animaux dans les films ou comme . Il a indiqué que les différences pouvaient venir du fait que les chiens ayant participé aux différentes études ne considéraient pas entièrement les animaux des films ou les chiens robots comme des chiens . -Il a dit que l' itinéraire est toujours en cours . Il a indiqué que l' itinéraire restait à définir . -Il a déclaré que le nouveau tribunal des médias " sera toujours partial parce qu' il s' agit d' une extension du gouvernement " , et que les restrictions sur le contenu et la publicité la place du Kenya dans l' économie mondiale . Il a indiqué que le nouveau tribunal des médias « sera toujours partial car il s' agit d' un prolongement du gouvernement » et que les restrictions relatives au contenu et à la publicité à la place du Kenya dans l' économie mondiale . -Il a déclaré qu' il y avait de graves problèmes dans le recrutement , le dépistage , l' induction , la formation et la supervision du personnel . Il a indiqué qu' il y avait des « problèmes graves » dans le recrutement , le contrôle , l' initiation , la formation et la supervision du personnel . -Il a dit en général qu' un pape autant de cardinaux que nécessaire pour augmenter le nombre d' électeurs cardinaux à 120 , et de nombreux cardinaux âgés de plus de 80 ans comme il le souhaitait . Il a déclaré qu' un pape nommait en général autant de cardinaux qu' il le fallait pour que le nombre de cardinaux électeurs atteigne 120 et autant de cardinaux âgés de plus de 80 ans qu' il voulait . -Selon lui , les efforts louables ont fait preuve d' un manque de soutien de la part du gouvernement fédéral face au Il dit qu' Obama a fait des efforts louables par l' du Parti républicain -Il dit qu' il ne le fait pas , dit : " Oh oui , il a fait ça " . Il affirme le contraire , mais nous disons « Bien sûr que si » . -Il comparaîtra devant le tribunal provincial à Halifax en décembre . Il comparaîtra en cour provinciale , à Halifax , en décembre . -Il doit encore trouver des candidats pour dresser des listes et défendre ses idées dans le domaine politique . " Il est probable qu' il sera possible de le faire à temps pour les élections municipales de 2014 " , a-t-il ajouté . Reste à trouver des candidats pour former des listes et défendre ses idées sur la scène politique " les élections municipales de 2014 , cela risque d' être juste , " . -Il n' a eu qu' un bras cassé au premier abord , mais une infection s' est développée et a circulé sur son cerveau , créant un qui l' a affecté pour le reste de sa vie . Au début , il n' a eu qu' un bras cassé , mais il a contracté une infection qui s' est propagée jusqu' à son cerveau , créant un dont il allait porter les séquelles le restant de sa vie . -Il est alors devenu le premier membre de haut rang du gouvernement américain à admettre que l' espionnage américain avait franchi la ligne , mais il a souligné que ses droits n' avaient pas été bafoués . Il est ensuite devenu le premier membre de haut rang du gouvernement des États-Unis à admettre que l' espionnage américain avait dépassé les bornes , mais a insisté sur le fait que les droits de chacun n' avaient pas été violés . -Il a l' enquête de la police autant que possible pour qu' elle puisse poursuivre ses activités criminelles . Il a entravé l' enquête de la police autant qu' il a pu pour lui permettre de poursuivre ses activités criminelles . -Il a dit au FT qu' il voulait dire que l' accord n' était plus respecté par . Il a déclaré au FT qu' il voulait dire que l' accord n' était plus respecté par le . -Il a pris part à la Conférence de en janvier 1942 , où " la solution finale " a été prise et a été notamment placé sous le commandement d' Adolf , responsable de la " logistique " de l' extermination des Juifs et condamnés à mort et exécutés en Israël en 1962 . Il participa à la conférence de en janvier 1942 , qui décida de la " solution finale " et eut notamment sous ses ordres Adolf , responsable de la " logistique " de l' extermination des juifs , condamné à mort et exécuté en Israël en 1962 . -Il a prêté serment devant les 18 juges du tribunal de dans son siège construit pour ressembler à un temple égyptien ancien . Il a prêté serment devant les 18 juges en robe noire de la Cour dont le bâtiment situé en bordure du Nil a été construit pour ressembler à un temple égyptien . -Il porte le voile et le montre en même temps , et s' adresse à des personnalités différentes , comme il l' a fait tout au long de sa carrière , surtout avec ses personnalités : et . Il dissimule et révèle à la fois , joue à adopter différentes personnalités , comme il l' a fait tout au long de sa carrière , notamment avec ses personnages et . -Il est très souvent " " dans mes rêves , d' une manière très particulière , si bien que lorsque je me réveille , je suis triste quand j' ai réalisé que c' était seulement un rêve . Très souvent il " débarque " dans mes rêves d' une façon si précise , si vivante qu' au réveil je suis encore plus triste de constater que ce n' était qu' un rêve . -Il était et , par conséquent , il pouvait descendre en bas de l' escalier qui était resté intact , bien que la paroi arrière de la maison fût entièrement détruite . et sauf , il a pu rejoindre le rez-de-chaussée par l' escalier resté intact tandis que le mur arrière de la maison était totalement détruit . -Il était un peu comme un alter ego , nous n' avions même pas besoin de parler . C' était un peu comme un , il n' y avait pas besoin de parler entre nous . -Il était accompagné de deux journalistes allemands lors de sa visite en Russie . Lors de sa visite en Russie , il était accompagné de deux journalistes allemands . -Il a également été reconnu coupable d' un certain nombre d' autres accusations liées au terrorisme , dont une vague d' attentats à la bombe contre des églises en Indonésie à la veille de Noël en 2000 . Il a également été reconnu coupable de nombreux autres chefs d' accusation en relation avec le terrorisme , notamment une vague d' attentats visant des églises dans toute l' Indonésie à la veille de Noël en 2000 . -Il était plein de louanges . Il a dit des éloges . -Il a reçu un accueil officiel par une troupe de l' armée qui a joué l' hymne national comme s' il s' agissait de l' attention . Il a été officiellement accueilli par une fanfare militaire qui a joué l' hymne national alors qu' il se mettait au . -On lui a donné des hormones féminines à l' âge de 12 ans . On lui a fait prendre des hormones féminines à l' âge de 12 ans . -Il a été emprisonné pour une durée minimale de six ans . Il a été condamné à une peine de 6 ans de prison minimum . -Il sur le sable sur toute la longueur de l' arène , et même s' il avait été si petit qu' il a fait un beau travail . Il a parcouru toute la longueur de la piste en courant et même s' il semblait être tout petit , il a fait un merveilleux travail . -Il m' a dit que , quand il est là , il me parfois une allée entre deux bâtiments et je pense à lui , Hey , si j' ai perdu tout mon argent , peut-être je vais habiter ici . Il me disait que , lorsqu' il était ici , parfois il une ruelle entre deux immeubles et se disait Eh bien , si je perds tout mon argent , peut-être que je vivre ici . -Il portait " un uniforme général " . Il portait " un uniforme de général " . -Il ne mourra pas et ne prendra pas sa retraite . Il ne mourra pas et ne partira pas en retraite . -Il aura probablement un autre casque spécial pour la course du dimanche , destinée à marquer son quatrième titre mondial consécutif . Il est probable qu' il aura un autre casque spécial dimanche , pour la course , destiné à marquer son 4e titre mondial consécutif . -Il a travaillé comme les grands reporters d' avant-guerre , comme Albert Londres , qui s' est rendu sur place et a reçu avec succès des enquêtes sérieuses et longues " . Il travaillait " comme les grands reporters d' avant guerre , du type Albert Londres , qui allaient sur place et revenaient avec de vraies et longues enquêtes " . -Il m' a parlé , m' a constamment encouragé , il a vécu dans mon corps . Il me parlait , m' encourageait constamment , il habitait mon corps . -Heinrich Muller , qui n' a jamais été découvert après avoir disparu à la fin de la Seconde Guerre mondiale , a été enterré dans une tombe commune dans un cimetière juif à Berlin , le chef du Mémorial de la Résistance allemande , le professeur Johannes , confirmé par . Müller , qui a disparu à la fin de la Seconde Guerre mondiale sans qu' on ne retrouve jamais sa trace , a en fait été enterré dans une fosse commune d' un cimetière juif de Berlin , affirme le dirigeant du Mémorial de la résistance allemande , le professeur Johannes , dans . -Heinrich Muller fut l' un des grands personnages du Troisième Reich à ne jamais être capturé . Heinrich Müller fait partie des personnalités importantes du Troisième Reich qui n' ont jamais été capturées . -Aider les autres à vivre une vie plus heureuse , en honorant une promesse qu' il a faite à son père , c' est quelque chose qui lui apporte une grande joie . Aider les autres à vivre heureux en tenant la promesse qu' il a faite à son père est quelque chose qui lui apporte une immense joie . -Par conséquent , en cas de mortalité à grande échelle dans la région , causée par une épidémie virale ou une capture accidentelle , nous savons que nous l' extinction d' une espèce plutôt que la disparition locale d' une population appartenant à une espèce beaucoup plus large . Dès lors , si un problème de mortalité massive causée par une épidémie virale ou de capture accidentelle se pose dans la région , nous pourrions avoir affaire à l' extinction d' une espèce , plutôt qu' à une disparition locale d' une population d' une espèce plus largement répandue sur Terre . -Ainsi , des millions de personnes dans la région se tournent vers parce que les familles se sentent par l' augmentation du coût de la vie et par la flambée de la dette des ménages et des consommateurs . Par conséquent , des millions de personnes à travers la région se tournent vers les prêteurs sur gage alors que les familles ressentent la pression qu' exerce l' augmentation du coût de la vie et le surendettement des ménages et des consommateurs . -D' où la question de savoir si le fond de la question des n' est pas la capacité ou le droit d' un pays d' utiliser des instruments d' interception , à savoir la question de l' absence totale de débat préalable - en particulier au sein des parlements - sur la justification de ces régimes , sur la mesure dans laquelle ils devraient être utilisés et , en fin de compte , sur la violation des libertés . Donc la question de fond derrière cette affaire NSA n' est pas tant la capacité ou le droit des pays de se doter d' outils d' interception , que la question de l' absence totale de débat préalable , notamment au sein des Parlements , sur la justification de tels systèmes , le périmètre qui doit être le leur , et , en fin de compte , la question des atteintes aux libertés . -D' où le choc que sa perte causa dans le jeune diplômé alors j' étais . C' est dire le choc que sa disparition a provoqué chez le jeune que j' étais à l' époque . -C' est pourquoi nous avons effectué 64 opérations depuis l' hiver dernier . Nous avons ainsi effectué , depuis la fin de l' hiver dernier , 64 interventions . -Son nouvel album , " " , a été lancé au de cette semaine . Son nouvel album , « Prism » , a débarqué à la première place des charts cette semaine . -Ses propriétaires Scott , 25 ans , et sa petite amie Hall , 20 ans , dans la carrière de à , dans le Sud-Ouest de l' île , ont été réunis avec Ruby et ont remercié le service de lutte contre l' incendie et le sauvetage de l' ouest du Yorkshire . Ses propriétaires , Scott , 25 ans , et sa petite amie Hall , 20 ans , se sont rendus à la carrière de à , dans le West Yorkshire , pour retrouver Ruby et ont remercié le Service d' incendie et de secours de West Yorkshire . -Voici mon point de vue : un sentiment de plus en plus et de l' euro en Europe se pour tuer l' euro . & # 160 ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Voilà ce que je pense : très bientôt , le sentiment et unique en Europe va s' unifier et finira par tuer l' euro . -Ici , les gens étaient par une vieille puissance indigène , et ils étaient encore plus radicaux pour eux . Ici , des hommes d' un Ancien Régime indigène , et d' autant plus radicaux . -Voici les vraies nouvelles de l' Europe : le grand projet de l' de rejoindre l' Europe est enfin en panne . Voici la véritable actualité concernant l' Europe : le grand plan d' après-guerre visant à unifier l' Europe est définitivement au point mort . -Nous sommes à nouveau confrontés à un débat , secoué par un nouveau type d' immigration , menacé par l' afflux de protestations des minorités , qui est absorbé dans le cadre européen . La voici à nouveau en débat , par une immigration d' un type nouveau , menacée par la montée de minorités , absorbée dans l' espace européen . -Ici , nous avons essayé de l' examiner à d' autres espèces . Ici , nous avons essayé de voir ce qu' il en était chez d' autres espèces . -Sa dépendance à la cocaïne l' a laissé définitivement et a été vulnérable aux motivations des dirigeants des groupes criminels organisés qui lui ont permis d' obtenir des informations précieuses sur les enquêtes policières . Son addiction à la cocaïne l' a compromis à jamais et l' a rendu vulnérable aux des membres dirigeants des groupes criminels organisés qui l' ont chargé d' obtenir des informations précieuses concernant des enquêtes de police . -Son arrivée a été accueillie par des chants , " L' armée et le peuple sont une main " , des centaines de personnes réunies dans la salle principale de l' université . À son arrivée , il a été accueilli par le chant « Le peuple et l' armée , main dans la main ... » par des milliers de personnes rassemblées dans le grand amphithéâtre de l' université . -Son affaire fait suite à l' intervention du caporal Glen Shwe , qui a déclaré devant une commission parlementaire le mois dernier qu' il avait été forcé de s' absenter avant qu' il ne soit prêt parce qu' il n' avait pas respecté la règle de l' universalité du service . Son cas est survenu après celui du caporal Glen Kirkland , qui a déclaré devant une commission parlementaire , le mois dernier , qu' il avait été poussé vers la sortie avant d' être prêt parce qu' il ne répondait pas à la règle d' universalité du service . -Son salaire fixe est de un dollar nominal . Son salaire fixe n' est que d' un dollar symbolique . -Sa vie n' est pas en danger , mais il est toujours hospitalisé . Son pronostic vital n' est plus engagé mais il est toujours hospitalisé . -Sa mère Donna , 30 ans , a déclaré : " et sont un grand double acte . Sa mère Donna , 30 ans , a déclaré : « et font un excellent duo . » -Sa mère était un cuisinier dans un château de la . Sa mère était elle-même cuisinière dans un château de . -Son état a récemment mis en place des dispositifs de suivi sur 500 véhicules pour tester un système . Son État a récemment fait installer des traceurs sur 500 voitures pour tester un système de paiement au mile . -Son arme a été saisie . Son arme a été saisie . -Historiquement , les enfants nés à la fois d' hommes et de femmes ont été appelés , nommés pour le beau dieu grec possédant une double sexualité . Historiquement , les enfants nés sans organes sexuels masculins ou féminins étaient appelés , d' après le nom du magnifique dieu grec qui avait un double sexe . -Historiquement , cela signifiait que c' était le travail blanc qui devait être protégé - et si , dans certaines régions , on voyait les échos de cet artefact historique , je ne serais pas surpris . Historiquement , cela signifiait que le travail des Blancs devait être protégé – et je ne serais pas surprise que certains pays de la région y voient comme un écho à cet historique . -Historiquement , les prix élevés de l' or ces deux dernières années s' ajoutent à la ruée vers l' achat de biens personnels , alors que les gens ont l' occasion d' investir dans la valeur de leurs bijoux familiaux . Les prix historiquement élevés de l' or au cours des deux dernières années ont fait que les gens se sont empressés de mettre en gage leurs effets personnels pour toucher la valeur en espèces de leurs bijoux . - dans les produits du boeuf Du cheval dans des produits au boeuf -La viande de cheval a été détectée dans des produits alimentaires en vente libre vendus par deux petits détaillants de produits laitiers à faible prix , l' Food Standards Agency l' a annoncé aujourd ' hui . De la viande de cheval a été décelée dans des boîtes de conserve de produits à base de boeuf , vendues par deux petites enseignes britanniques de distribution à bas prix , a annoncé aujourd' hui l' agence chargée de l' hygiène alimentaire . -House in in the Old Quebec Une maison la proie des flammes dans le Vieux-Québec -Comment la génétique vie Comment la biométrie va envahir nos vies -Comment les comédies musicales françaises ? Comment évoluent les comédies musicales françaises ? -Comment pouvons-nous offrir des garanties sur la durée de l' emploi lorsqu' il n' y a pas de salariés sur place ? Comment pouvons-nous donner des garanties de durée d' emploi quand il ne reste plus d' employés sur le site ? -Comment les examens médicaux et le compte-rendu avec le ( Direction générale de la sécurité extérieure ) sont-ils allés ? Comment se sont passés les examens médicaux et le ? -Comment pouvez-vous réagir aujourd' hui à la libération des quatre otages au Niger ? Comment , aujourd' hui , à la libération des quatre otages du Niger ? -Le directeur de l' Administration des FAA , M. Philippe , mettra l' accent sur la rapidité de la modification de la nouvelle version à l' occasion d' une conférence de presse . La rapidité avec laquelle le changement sera mis en place variera d' une compagnie aérienne à l' autre , a déclaré l' administrateur de la FAA Michael lors d' une conférence de presse . -La façon dont le dos de l' avion est prévu - en particulier s' il y a des sièges à 9 ou 10 - est le point central des évaluations de performance économique qui s' appliquent à de nouveaux modèles d' avion à réaction " " . La façon dont les sièges seront disposés à l' arrière de l' avion – en particulier , y aura-t-il 9 ou 10 sièges de front – est essentielle pour le rendement économique du segment des « » . -Comment avez-vous été reçu lorsque vous avez atteint le sol français ? Comment s' est déroulée votre arrivée sur le sol français ? -Toutefois , les ont l' avantage de ne pas exiger de chèques de crédit ou de preuve de salaire , et peuvent organiser des prêts plus rapidement que les banques . Cependant , les prêteurs sur gage ont l' avantage de ne pas demander de vérification de solvabilité ou de preuve de salaire , et peuvent débloquer les prêts plus rapidement que les banques . -Cependant , n' est pas la seule voie à avoir été sortie . Mais n' est pas le seul titre à dévoiler ses charmes . -Toutefois , des enregistrements de CCTV ont ensuite révélé qu' elle était retournée à son hôtel en toute sécurité après avoir été séparée de son ami australien . Cependant , les enregistrements de vidéosurveillance ont plus tard révélé qu' elle était rentrée à son hôtel en toute sécurité après s' être séparée de son petit ami australien . -Cependant , Damas doit encore commencer à détruire les armes et les stocks existants . Cependant , Damas doit encore démarrer la destruction des armes et des réserves existantes . -Cependant , je ne peux rien faire ; je perds du sommeil et je me demande constamment ce qu' il pense . Pour moi , c' est impossible d' aller dans un sens contraire , je n' en pas et je me demande toujours ce qu' il en . -Cependant , je me réfère à l' évocation de la transformation des anciens en une sorte d' incubateur industriel , mais à quel coût pour les contribuables . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Je donne cependant une mention honorable à la transformation de l' ancienne en une sorte d' incubateur industriel , mais à quel coût pour les payeurs de taxes ? -Cependant , l' intérêt croissant de Washington dans le Pacifique suggère que la Grande-Bretagne sera laissée dans le froid de l' Atlantique . Toutefois , l' intérêt croissant de Washington pour le Pacifique laisse penser que la Grande-Bretagne sera laissée pour compte dans l' Atlantique . -Cependant , Yahoo et Google ont nié toute implication dans ces pratiques . Mais Yahoo et Google ont nié tout lien avec ces pratiques . -Toutefois , à la suite du meurtre récent d' un agent de voyages australien , Michelle Smith , à Phuket , la Thaïlande pourrait également essayer de réparer son image touristique ternie , ce qui lui a valu un acquittement . Toutefois , après le meurtre récent à Phuket de Michelle Smith , une agente de voyage australienne , la Thaïlande pourrait également chercher à redorer son image détériorée auprès des touristes , ce qui pourrait conduire à un acquittement . -Toutefois , pour l' économiste , , du Centre d' études politiques , " on ne peut s' attendre à une amélioration à court terme " . Cependant , pour l' économiste , du Centre des Etudes politiques , " on ne peut pas attendre une amélioration à court terme " . -Cependant , étant donné la facilité avec laquelle leur comportement peut être enregistré , il ne sera probablement pas assez long avant de comprendre pourquoi leurs queues suivent parfois un chemin , parfois l' autre . Toutefois , étant donné la facilité avec laquelle leurs comportements peuvent être enregistrés , il ne faudra sûrement pas longtemps avant que nous comprenions pourquoi leur queue bouge parfois d' un côté et parfois de l' autre . -Cependant , étant donné l' absence d' études approfondies , il est impossible de savoir si cette ligne de conduite réellement les utilisateurs . Mais , faute d' études approfondies , il est impossible de savoir si ces discours convainquent vraiment les utilisateurs . -Cependant , il veut garder l' usine à Amiens avec au moins 333 employés bien payés . Or , il veut garder l' usine d' Amiens avec au moins 333 emplois bien payés . -Cependant , il souffrait d' une inflammation au genou gauche à la suite de son retour de l' ordre international de ce mois . Il souffrait néanmoins d' une inflammation du genou gauche lors de son retour de sélection , en début de mois . -Cependant , si aucune nouvelle commande n' est annoncée dans les prochains mois , nous que le marché sera plus sceptique sur le programme . Toutefois , si aucune nouvelle commande n' est annoncée au cours des prochains mois , nous nous attendons à ce que le marché devienne plus sceptique au sujet du programme . -Cependant , le personnel du renseignement , le personnel de l' armée et les militants politiques ont confirmé cette fois qu' il avait perdu sa vie lors de la grève qui avait été menée dans la région du . Mais des responsables du renseignement , de l' armée ainsi que de la mouvance activiste ont affirmé cette fois qu' il avait perdu la vie dans cette frappe , menée dans la région du . -Cependant , la chanteuse du chanteur d' enfance , Alex Turner , les a obligés à reporter le spectacle . Cependant , sa a forcé le chanteur Alex Turner a reprogrammer le spectacle . -Toutefois , lorsqu' un cardinal atteint 80 , il n' est plus autorisé à participer à l' élection d' un pape , ce qui revient à un groupe de 120 électeurs " " . Toutefois , dès qu' un cardinal atteint l' âge de 80 ans , il ne peut plus participer à l' élection d' un pape – cela revient donc à un groupe de 120 « cardinaux électeurs » . -Cependant , une fois de plus , il y a une tendance à l' , à l' intellectuel , à la en , comme si les deux pieds sont liés , d' un sujet à l' autre . On y retrouve pourtant , une fois de plus , la tentation de l' , le vagabondage intellectuel , la mise à sauter comme à pieds joints d' un sujet à l' autre . -Toutefois , notre familiarité avec les États-Unis ne fait que souligner encore plus une chose qui nous tient particulièrement à coeur . Mais le américain ne fait que mieux souligner ce qui nous est propre . -Toutefois , parler de vérité n' est pas un crime . Cependant , dire la vérité n' est pas un crime . -Cependant , le parti de la devrait être au moins représenté à Aubervilliers par ma candidature et dans d' autres villes j' espère , M. . Le Parti de la banlieue devrait quand même être représenté à Aubervilliers à travers ma candidature et j' espère aussi dans d' autres villes . -Cependant , le comité a recommandé à la FAA de permettre aux pilotes d' ordonner aux passagers d' éteindre les dispositifs pendant les atterrissages en cas de visibilité faible . Toutefois , le comité a recommandé que la FAA autorise les pilotes à demander aux passagers d' éteindre leurs appareils pendant les atterrissages aux instruments dans des conditions de faible visibilité . -Cependant , la crise économique actuelle s' est avérée un point de mire . Toutefois , la crise économique actuelle a marqué un tournant . -Toutefois , la famille a refusé " pour des raisons financières " et la situation se trouve dans l' impasse . Pourtant , la famille refuse " pour raison financière " et la situation est bloquée . -Toutefois , la situation reste précaire . La situation demeure toutefois délicate . -Cependant , il y a eu une époque où les Français croyaient qu' ils pouvaient réparer les dommages causés à leur histoire et surmonter la malédiction du numéro deux . Il y a pourtant eu une époque où les Français ont cru pouvoir la robe déchirée de leur histoire , et vaincre la malédiction du chiffre deux . -Toutefois , on craint que les petites et moyennes entreprises restent vulnérables au piratage et à la surveillance . Toutefois , on craint que les petites et moyennes entreprises restent vulnérables au piratage et à la surveillance . -Cependant , ils ont aussi critiqué chiites Al , affirmant qu' il est partiellement responsable de la reprise de la violence à cause de ses " politiques sectaires et autoritaires " . Mais ces derniers ont aussi critiqué Nouri Al , un chiite , lui attribuant la responsabilité partielle de la reprise des violences par sa " politique sectaire et autoritaire " . -Cependant , nous sommes toujours intéressés par l' évolution de la réglementation qui pourrait profiter aux passagers et nous suivrons de près la décision de la FAA et les raisons qui l' ont motivé . Toutefois , nous sommes toujours intéressés par les évolutions réglementaires qui pourraient bénéficier aux passagers , et nous examinerons de près la décision de la FAA et les raisons de celle-ci . -Toutefois , il a dit qu' il était encouragé par la réponse mondiale à " mon acte d' expression politique " . Toutefois , il a dit qu' il avait été encouragé par la réaction mondiale face à « mon acte d' expression politique » . -Beaucoup de choses que nous dénonçons les États-Unis , je ne vois pas comment elles peuvent être sanctionnées . On a beau les dénoncer , je ne vois pas de quelle manière ils pourront être punis . - a également été condamné à des frais de £ 90 et à une amende de 100 livres . a également été condamné à payer des frais s' élevant à 90 £ et une surtaxe pour victime de 100 £ . -Des centaines de soldats et de policiers ont le bâtiment , car Morsi est arrivé peu après des heures de dans un petit convoi . Des centaines de soldats et policiers gardaient le bâtiment lorsque M. Morsi est arrivé peu après 11 heures , heure locale , suivi d' un petit convoi . - . Sixième édition du Play ce samedi à Hyères -Je suis 58 et j' ai toujours vécu seul en le perdant , il est et restera l' amour de ma vie . J' ai 58 ans , j' ai toujours vécue seule depuis son départ , il est et restera l' amour de ma vie . -Je suis en train d' écouter ce qui est dit . J' écoute ce qui se dit . -Je n' essaie pas de plaire à quiconque . Je n' essaye de faire plaisir à personne . -Je suis en train de lire un livre terriblement triste ces jours-ci . Je suis en train de lire un livre triste ces jours-ci . -Je suis désolé si mes propos ont offensé quelqu' un . Si j' ai blessé quelqu' un par mes paroles , j' en suis désolé . -Je suis sûr que a été aidé par ses beaux vêtements , il a vraiment regardé la partie . Je suis sûr que ses beaux vêtements ont aidé , il avait vraiment fière allure . -J' ai atteint un point de détresse politique . Je suis au bord du désespoir politique . -Je me suis rendu compte de mes actes répréhensibles . Je me suis rendu compte de mon erreur . -J' ai également rencontré les services de renseignement . J' ai aussi rencontré les services de renseignement . -Je me souviens aussi d' une courte vidéo passée à parler à Roger et . Je me souviens aussi d' une courte nuit à discuter avec Roger et . -J' ai hâte de gagner ma confiance . J' ai hâte de regagner votre confiance . -Je suis comme mon père , " à l' intérieur et à l' extérieur " , semble-t-il . Je ressemble à mon père , " au dedans comme au dehors " . -Je vous assure qu' il n' y a pas de victimes innocentes dans ce processus . Je vous assure que les personnes innocentes ne seront pas victimes d' abus dans le cadre de ce processus . -Je ne me souviens pas de ce qui a été discuté , mais ce n' est certainement pas le cas . Je ne me rappelle pas de quoi nous avons discuté mais en tout cas , ce n' était pas de ça . -Je ne peux que fermer les yeux et les ouvrir à nouveau ... Je ne peux que fermer les yeux et les rouvrir lentement ... -Je le rêve toujours , peut-être pas tous les soirs , mais plusieurs fois par semaine pour tous . Je rêve constamment d' eux , peut-être pas toutes les nuits mais plusieurs fois par semaine c' est certain . -Je ne pouvais pas croire qu' elle était là pour la première fois . Je n' ai d' abord pas cru qu' elle était là-haut . -Je pourrais encore mener une vie personnelle . J' ai pu avoir une vie personnelle . -J' ai du crédit à d' importantes et belles réalisations , à partir de l' adoption de la loi sur les services de secours à la sortie de notre aéroport militaire iraquien , la fin de ce que l' on dit : " ne nous demandez pas de dire " , à propos de l' assassinat d' Oussama Ben Laden . Je mets au crédit d' Obama de grandes réalisations , depuis le passage de l' Care Act ( loi sur les soins abordables ) jusqu' au départ d' Irak de notre armée , en passant par la fin du « ni vu ni connu » et l' assassinat d' Oussama ben Laden . -Je l' ai fait principalement parce que je après l' argent et la renommée . Je l' ai fait principalement parce que je après l' argent et la célébrité . -Je ne sais pas si j' ai adopté sa façon de penser ou si je suis tout simplement le mien , est-ce qu' il est génétique ? Je ne sais pas si j' ai adopté sa façon d' être ou si je suis simplement comme lui : est-ce génétique ? -Je ne sais pas si c' est ce que je devrais attendre . Je ne sais pas si c' est ce à quoi je m' attendais . -Je ne sais pas qui a été dit que ceux qui meurent ne sont pas oubliés , mais invisibles . Je ne sais plus qui disait que les personnes décédées ne sont pas des oubliés mais des invisibles . -J' attends du Royaume-Uni qu' il se de ce projet , espérant profiter aux Etats-Unis . Je m' attends à ce que le Royaume-Uni prenne ses distances par rapport à ce projet , et j' espère qu' il fera les yeux doux aux États-Unis . -Je me sens sa présence et cela me rend heureux . Je ressens sa présence et cela me rend heureuse . -Je ressens sa présence dans chaque salle et cela me rend heureux . Dans toutes les pièces je ressens sa présence et cela me rend heureuse . -J' ai pensé qu' un second mandat pour Obama , libéré des pressions des futures élections , permettrait de réaliser l' espoir que nous avons entendu depuis si longtemps . Je sentais qu' Obama , élu pour un second mandat en étant libéré de la pression d' élections futures , tiendrait les promesses dont on entendait parler depuis si longtemps . -Je pense qu' il n' y a rien de mal à faire de la musique traditionnelle . Je suppose qu' il n' y a pas de mal à rester traditionnel . -J' avais une relation difficile avec lui jusqu' à ce qu' il devienne vieux et malade . J' ai eu des relations difficiles avec lui jusqu' à ce qu' il devienne vieux , malade . -J' ai dû subir un premier examen médical en Corse . J' avais eu droit à un premier contrôle médical en Corse . -On me l' a toujours dit . On me l' a toujours dit . -J' ai toujours été bien accueilli en Afrique , où j' ai un très grand nombre de lecteurs . Je suis toujours bien accueilli en Afrique où je compte de très nombreux lecteurs . -Je le préconise depuis vingt ans . Je plaide en faveur de ce projet depuis 20 ans . -J' ai même eu des problèmes de travail en raison de ces convictions . J' ai d' ailleurs eu des problèmes dans mon travail pour ces convictions . -Je dois vivre chaque jour sans voir un plus grand nombre de petits-enfants et mes filles . Je dois vivre tous les jours sans avoir d' autres petits-enfants , ni voir mes filles . -Je n' ai pas rejoint les deux dernières années pour donner des bonbons dans mon appartement et cela ne sera probablement pas cette année . Ces deux dernières années , je n' ai pas distribué un seul bonbon et je ne le ferai certainement pas plus cette année . -J' espère qu' il verra ce que je fais . J' espère qu' il voit ce que je fais . -Je ne peux tout simplement pas me croire là où elle était . Je n' arrive pas à croire qu' elle était là-haut . -Je sais qu' il aimerait une garantie de quatre ans . Je sais qu' il voudrait une garantie de quatre ans . -Je me réjouis d' examiner le rapport de l' enquête avec les ministres britanniques l' année prochaine et de décider ensemble des prochaines étapes . « Je suis impatient de lire le rapport d' examen avec les ministres britanniques l' année prochaine et nous déciderons alors ensemble des prochaines étapes . » -J' ai perdu mon père le 22 août 2008 au sujet du cancer de l' amiante . J' ai perdu mon père le 22 août 2008 des suites d' un cancer de l' amiante . -J' ai aimé partager ce moment avec lui et il a aimé parler de cette mémoire partagée qui a eu lieu après sa mort . J' ai aimé partager ce moment avec lui et je prends plaisir à raconter ce souvenir commun vécu après sa mort . -Je ne l' ai jamais cru à l' époque . Je ne l' ai jamais cru à l' époque . -Je rêve souvent des derniers moments que j' ai rencontrés avant qu' ils ne soient trop tard , bien qu' il y ait encore une chose qui m' empêche de profiter du moment . Je rêve souvent des derniers instants que je dois partager avec eux avant qu' il soit trop tard , seulement , il y a toujours une chose qui m' empêche d' accomplir ce moment . -Je me réveille souvent en détresse parce qu' elle me frappe et je me sens profondément oublié . Je me réveille souvent lorsque ça m' arrive et je ressens profondément leur absence . -J' ai vraiment le sentiment qu' il devrait suivre les traces des autres . Je pense vraiment qu' il aurait dû suivre le même chemin que les autres hommes . -Je me souviens d' avoir pris un bain d' une heure , Je me souviens avoir pris un bain pendant une heure . -Je vois ses yeux se reposer sur moi . Je vois ses yeux se poser sur moi . -Je dois dire que je suis et que je travaille avec des responsabilités , je suis et , je vais chanter , je suis une grand-mère , j' ai beaucoup d' amis , mais mon coeur et mon âme sont sur lui , je ne parle jamais que de lui , à l' exception de mon fils , et je n' irai jamais au cimetière . Je tiens à préciser que je suis sociable avec un métier à responsabilité , je ris et je chante , je sors , je suis grand-mère , j' ai plein d' amis , mais mon cœur et mon âme lui appartiennent et je ne parle jamais de lui sauf avec mon fils , et je ne vais jamais au cimetière . -Je vis toujours dans la même maison et , souvent , je me sens en train de le voir , je tourne rond et je vois qu' il n' est pas là , mais je sais qu' il le fait , et je lui parle . Je vis toujours dans la même maison , et souvent , je me sens observée et je me retourne pour voir s' il n' est pas là , je sais que c' est lui et je lui parle . -Je soupçonne que l' Écosse pourrait bien s' en tenir à un arrangement européen plus souple , à condition que nous disposions de notre propre monnaie . Je crois que l' Écosse pourrait s' accommoder d' un accord européen plus libre à condition que nous notre propre devise . -Je pense que M. le sait . Je pense que M. sait cela . -Je pense qu' il est concevable que ces données soient utilisées à des fins d' avantages mutuels . J' estime qu' il est concevable que ces données soient utilisées dans leur intérêt mutuel . -Je pense que c' est justifié , légitime , juste . Ça me semble pourtant fondé , légitime , frappé du bon sens . -Je pense qu' il y a un problème de sémantique . Il y a selon moi un problème de sémantique . -Je pensais qu' elle avait disparu . Je qu' elle était partie . -J' essaie de distinguer les questions de mon passé en otage et mon rôle de président de l' association " du Monde " , qui me permet de garder une distance et me semble plus intéressant à traiter . J' essaie de faire la part des choses entre les questions sur mon passé en tant qu' otage et ma casquette de président de l' association " du monde " qui me permet de mettre de la distance , et me semble bien plus intéressante à aborder . -J' ai voté pour Obama à nouveau en 2012 , mais pas parce que j' étais excité par sa candidature . J' ai à nouveau voté pour Obama en 2012 , mais pas parce que j' étais enthousiasmé par sa candidature . -J' ai voté pour le président Obama deux fois , partageant l' espoir dans la possibilité d' un changement J' ai voté deux fois pour le président Obama , croyant en la possibilité d' un changement -J' avais 28 ans et notre fils avait 6 ans . J' avais 28 ans et notre fils 6 ans . -J' avais peur de l' oublier , ou peut-être je ne savais pas comment je pourrais continuer à " passer du temps avec lui " . J' avais tellement peur de l' oublier ou plutôt je ne savais pas comment j' allais continuer à le " fréquenter " . -J' attendais de voir des de dents et un combat se lever à la porte . Je m' attendais à voir des de dents et une bagarre éclater à la porte . -J' étais sur la news cette même soirée , et à la télévision le lendemain aussi . Le , je suis au 20h , le lendemain aussi je suis à la télé . -J' étais stérilisée à la naissance et personne ne m' a jamais dit . J' ai été stérilisé à la naissance – et personne ne me l' avait jamais dit . -Je n' ai pu assister à son enterrement , et trois semaines après sa mort , j' ai donné naissance à un petit garçon . Je n' ai pas pu assister à son inhumation et trois semaines après son décès , je naissance à un petit garçon . -Je n' étais pas satisfait , mais il était vraiment difficile de poser des questions . Je n' étais pas heureux , mais c' était vraiment difficile de poser des questions . -J' étais très proche de lui , j' ai toujours agi selon ce qu' il aurait cru ou aurait aimé . J' étais très proche de lui , j' ai toujours agi en fonction de ce qu' il pensait ou de ce qu' il appréciait . -J' ai traversé toutes les étapes : l' incompréhension , la colère , le chagrin , les larmes qui allaient se produire , n' importe où , à n' importe quel moment , dans des endroits inhabituels , à des moments . Je suis passée par toutes les phases : l' incompréhension , la colère , la douleur , les pleurs qui se déclenchent seuls , n' importe où , n' importe quand , dans des lieux insolites , des moments . -Je vais suivre les faits , où qu' ils aillent . Je vais donner suite aux faits , où qu' ils mènent . -Je resterai le patchwork créé par des médecins , et marqués . Je resterai le patchwork qu' ont créé les médecins , et . -Je n' aucun effort pour remplir le mandat que m' ont confié les États membres de réaliser notre vision des objectifs de développement durable qui s' appuient sur le succès des OMD . Je ferai le maximum pour m' acquitter du mandat qui m' a été confié par les États membres pour réaliser notre vision des objectifs de développement durable qui s' appuient sur le succès des OMD . -J' aurais honte de défendre un tel bilan . J' aurais honte de défendre un tel bilan . -Je les mains quand je la mienne ; je lui mon corps . Je voyais ses mains en regardant les miennes , je lui mon corps . -Je n' ai pas quitté cette maison pour n' importe quoi dans le monde . Pour rien au monde je ne cette maison où nous avons été heureux et ou son esprit vit avec moi . -L' identité est une préoccupation contemporaine . Identité : c' est une préoccupation d' époque . -Si , comme cela est très possible , les partis sont bien parvenus à ces élections , un tel bloc pourrait dominer le Parlement européen pour la première fois . Si les partis font bonne figure à ces élections , ce qui est très possible , un tel bloc pourrait contrôler le Parlement européen pour la première fois . -Si , à l' âge de 18 ans , je pensais que la gouvernance pouvait être en contradiction avec les campagnes politiques qui la précèdent . Si , à 18 ans , j' étais supposé comprendre que la gouvernance pouvait contredire les campagnes politiques qui la précèdent . -Si la personne la plus puissante de l' Europe peut être ciblée , alors les dirigeants d' entreprises sont également des cibles potentielles . Si la personne la plus puissante d' Europe peut être visée , alors les dirigeants d' entreprise sont sûrement aussi des cibles potentielles . -Si Goodyear avait offert aux employés une bonne prime de licenciement après avoir annoncé que l' usine était fermée , je crois que 100 % des employés l' auraient acceptée . Si , depuis l' annonce de la fermeture de l' usine , Goodyear avait offert aux salariés une bonne indemnité de départ , je pense que 100 % des employés l' auraient acceptée . -Si M. a témoigné en Allemagne , il aurait besoin d' être assuré qu' il était " sûr " là-bas , a déclaré le député . Si M. témoignait en Allemagne , il faudrait lui donner l' assurance qu' il serait en « sécurité » là-bas , a déclaré le député . -Si M. obtient la compétence de la CGT et de Goodyear pour parvenir à un accord et que Titan achète l' usine , nous avons toute intention de rester à Amiens Nord depuis plus de quatre ans . Si M. parvient à ce que la CGT et Goodyear se mettent d' accord et que Titan achète l' usine , nous avons bien l' intention de rester à plus de quatre ans . -Si tous les employés le acceptent , le projet d' acheter Goodyear Amiens commencera par n' importe quel employé . Si tous les salariés les acceptent , le projet d' achat de l' usine démarre avec zéro employé . -Si elle est concluante , les lecteurs biométriques pourraient bientôt être vus dans les boutiques en France . Si elle est concluante , les lecteurs biométriques pourraient bientôt arriver chez les commerçants français . -Si elle le faisait , elle son désaccord évident , a souligné Bruno , son porte-parole . Si c' était le cas , elle marquerait son désaccord net , souligne Bruno , son porte-parole . -Si l' on voulait une industrie européenne des technologies de l' information , alors on ne devrait pas lui laisser mourir il y a 20 ans . Si on voulait une industrie européenne des TI forte , alors il ne fallait pas la laisser mourir il y a 20 ans . -Si quelqu' un vous a vraiment aimés , leur absence ne peut pas votre cœur ou vos souvenirs . Si un être vous a aimé très fort , l' absence ne peut vous l' arracher du cœur et des souvenirs . -Si le critère de " l' authenticité " était adopté , un candidat à la naturalisation pourrait être interrogé sur la question de savoir si la nomination serait réelle dans les cas où le candidat serait une relation ou un associé personnel d' un propriétaire ou d' une personne intéressée de l' entreprise de parrainage . Si le critère « d' authenticité » était adopté , un demandeur de visa pourrait être contrôlé pour savoir « si l' offre d' emploi est authentique dans le cas où le candidat est une relation ou un associé personnel du propriétaire ou de la personne concernée au sein de l' entreprise qui le » . -Si les directives actuelles sont respectées , elles représenteront une croissance de 12 à 17 % du résultat de millions de dollars de bénéfices atteint en 2011 / 12 . Si les prévisions actuelles sont atteintes , cela représentera 12 à 17 % de croissance par rapport au bénéfice record de M $ atteint en 2011 / 12 . -Si le propriétaire paie 5,000 $ dans les impôts municipaux , il divise cette somme entre chaque maison . Quand le propriétaire paie 5000 $ en taxes municipales , il divise ce montant entre chaque logement . -S' ils ne satisfont pas aux critères de déploiement outre-mer , ils peuvent être contraints de quitter l' armée . S' ils ne répondent pas aux critères pour les déploiements à l' étranger , ils peuvent être forcés de quitter l' armée . -Si nous le faisons , des centaines de millions de conducteurs seront préoccupés par leur vie privée et par une foule d' autres choses . Si on fait ça , des centaines de millions d' automobilistes s' de la protection de leur vie privée et d' un tas d' autres choses . -Si vous êtes à la recherche d' un crédit supplémentaire , appelez Tim et Susan à Napa , en Californie , et essayez de leur de maïs ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! Si vous cherchez à vous faire remarquer davantage , appelez Tim et Susan à Napa , en Californie , et essayez de sculpter leur citrouille de 922 kg . -Si vous pouvez le faire , dit Khan , le public se sent plus à l' aise . Si on peut faire ça , a ajouté Khan , les citoyens seront plus à l' aise . -Si vous restez calme , votre enfant répondra généralement bien . Si vous restez calme , votre enfant réagira bien en général . -L' Illinois s' y emploie de façon limitée avec des camions . L' Illinois tente un essai sur un nombre limité de camions . -Le ministre de l' Immigration , Brendan O' Connor , hier à Sri Lanka , où il a rencontré des responsables de la traite des personnes , a mis en oeuvre cinq des modifications recommandées avec le reste de l' examen . Le ministre de l' Immigration Brendan O' Connor , qui était au Sri Lanka pour rencontrer des hauts responsables afin de discuter du trafic d' êtres humains , a mis en œuvre cinq des changements recommandés , les autres étant en cours d' examen . -Amélioration de la situation Situation améliorée -Dans " Les de Benghazi " , il a été le premier à révéler l' existence d' un centre secret de la CIA dans la ville , berceau de la révolte libyenne . Dans " Les de Benghazi " , il avait été le premier à révéler l' existence d' un centre de commandement secret de la CIA dans cette ville , berceau de la révolte libyenne . -En 1980 , il a décrit l' assassinat du président égyptien Anouar dans " Le du " un an avant l' attaque . En 1980 , il mettait en scène l' assassinat du président égyptien Anouar dans " Le complot du Caire " , un an avant l' attentat . -En 2009 , les baux pour les entreprises de la Quay , qui ont gagné le environ 2,5 millions de dollars par année , ont été renouvelés sans être soumis à un appel public . En 2009 , les baux concernant les entreprises de Quay , qui rapportaient aux Obeid environ 2,5 M $ par an , ont été renouvelés sans qu ’ il n' y ait d' appel d' offres . -En 2010 , les autorités ont trouvé un passage à deux voies , équipé de voies ferrées qui s' étendait de la cuisine d' une maison de Tijuana à deux entrepôts de San Diego . En 2010 , les autorités ont trouvé un passage d' environ 700 équipé de voies ferrées qui partait de la cuisine d' une maison de Tijuana et allait jusqu ’ à deux entrepôts de San Diego . -En 2011 , les responsables de la compagnie ont commandé un avion de ligne rempli de leur et l' ont pour vérifier les problèmes , mais n' en ont trouvé aucun . En 2011 , les dirigeants de la société ont chargé un avion de ligne de lecteurs de livres électroniques et l' ont fait voler pour voir s' il y avait des problèmes , mais il n' y en a eu aucun . -En 2011 , des dizaines de législateurs français issus de ce pays fortement catholique ont signé une pétition pour que la " théorie des genres " soit retirée des manuels scolaires . En 2011 , des douzaines de législateurs de ce pays foncièrement catholique ont signé une pétition pour que la « théorie des genres » soit retirée des livres scolaires . -En 2011 , Google a réservé une première tranche de 900 millions de dollars pour les brevets de Nortel . En 2011 , Google a fait une offre initiale de 900 M $ pour les brevets de Nortel . -En 2012 , a acheté un câble aux États-Unis , Wide Western Pacific , pour million CAD . a acquis en 2012 le Atlantic Broadband , établi aux États-Unis , pour milliard . -En 2012 , l' Agence des services frontaliers du Québec a procédé à saisies de stupéfiants . En 2012 , les agents des services frontaliers de la région du Québec ont effectué un total de 1653 saisies de stupéfiants . -À , un contact local qui avait déclaré qu' il des combats depuis le jeudi , a déclaré que la situation était " calme depuis ce matin " . A , un habitant qui avait fait état de combats tout proches jeudi pendant toute la journée a indiqué que la situation était " calme depuis ce matin " . -En août , la ville de New York a accepté de mettre un terme à la pratique consistant à stocker les noms et adresses des personnes dont les plaintes sont après un arrêt de la police . En août , la ville de New York a accepté de mettre fin à la pratique consistant à conserver les noms et adresses des personnes dont le cas est rejeté après un contrôle de police . -En Australie , les entreprises de loisirs ont rebondi au cours de la seconde moitié , ce qui a compensé un marché intérieur de la petite entreprise légèrement plus intense . En Australie , le secteur des voyages de loisirs a rebondi pendant le second semestre , ce qui a permis de compenser un marché domestique des voyages d' affaires en légère baisse . -Au Cameroun , il n' y a qu' un seul médecin pour 5 000 habitants , selon l' Organisation mondiale de la santé . Au Cameroun , il y a seulement 1 médecin pour 5 000 habitants , selon l' Organisation mondiale de la santé . -Au Canada , le maire de Toronto est soupçonné d' avoir recours à la drogue , selon plusieurs médias . Au Canada , le maire de Toronto est soupçonné de se , selon plusieurs médias . -En février , le New York Times l' a surnommé " le romancier espion qui connaissait trop " . En février , le New York Times l' avait consacré comme " l' auteur de romans d' espionnage qui en savait trop " . -Rien qu' en France seulement , en 2011 , la Commission Nationale de la Technologie de l' Information et des Libertés Publiques a approuvé 774 modèles de reconnaissance , pour la reconnaissance des empreintes digitales , la forme de la main ou des réseaux de veines dans la réalité , pour les entreprises , les institutions , les cafétérias , etc. Rien qu' en 2011 et en France , la Commission nationale de l' informatique et des libertés a autorisé 774 systèmes de reconnaissance des empreintes , de la forme de la main ou du réseau veineux de la main dans des entreprises , des administrations , des cantines ... -En France aujourd' hui , les données biométriques les plus utilisées sont les empreintes digitales , la géométrie des mains et le réseau de veines dans la paume des doigts ou les doigts . Aujourd' hui , en France , les données biométriques les plus utilisées sont les empreintes digitales , le contour de la main et le réseau veineux de la paume ou des doigts . -En Allemagne , beaucoup de ces entreprises sont des leaders mondiaux du marché dans leur créneau particulier . En Allemagne , bon nombre de PME sont des leaders mondiaux dans leur niche particulière . -En Inde , en Chine et dans bien d' autres pays , les gens travaillent entre 10 et 12 heures par jour . En Inde , en Chine et dans plein d' autres pays , on travaille dix à douze heures par jour . -Au Japon , il est déjà possible de retirer de l' argent de certaines machines automatiques en insérant votre carte et en plaçant votre lecteur sur un lecteur biométrique : ce geste remplace l' utilisation d' un NIP . Au Japon , il est déjà possible de retirer de l' argent à certains distributeurs en introduisant sa carte et en posant sa main sur un lecteur biométrique : ce geste remplace la frappe du code secret . -En juin , Rio a suspendu l' utilisation de son chemin de fer pendant environ une semaine après que ait menacé d' attaquer la ligne . En juin , Rio n' a plus pu utiliser le chemin de fer pendant environ une semaine après que la a menacé d' attaquer la ligne ferroviaire . -En juin , un Australien âgé de 52 ans , , est devenu la première " " reconnue à l' échelle mondiale après avoir reçu un appel à la justice pour avoir un statut " non spécifié " pour la vie . En juin , une de 52 ans , , est devenue la première personne au monde à avoir été reconnue « neutre » après avoir gagné son procès en appel qui l' autorise à garder un sexe « non spécifié » à vie . -En juin , le gouvernement a révisé le coût estimatif de la construction de la liaison à grande vitesse entre Londres et le nord de l' Angleterre , de à livres . En juin , le gouvernement a revu à la hausse le coût estimatif de la construction de la liaison à grande vitesse entre Londres et le nord de l' Angleterre , passant de 32,7 Md £ à 42,6 Md £ . -En mai , ils ont voté pour la levée des sanctions imposées par la Commission nationale des salaires et rétabli leurs salaires les plus élevés d' environ livres par mois à l' abri de l' impôt , classés parmi les plus élevés du monde . En mai , ils ont pris position contre les coupes ordonnées par la Commission nationale en charge des salaires et ont rétabli leurs salaires d' environ 532 000 shillings par mois exonérés d' impôt – qui figurent parmi les plus élevés au monde . -Dans l' équipe de Nathalie , Roger Vincent , un partisan de l' UMP à Paris et du quatrième arrondissement du Bundestag , dit que " même si l' UMP à Paris soutient la poursuite des services d' urgence à l' , il est techniquement et financièrement impossible de les rouvrir si l' on considère les devoirs " . Dans l' équipe de Nathalie , Vincent Roger , conseiller UMP de Paris et élu du 4e arrondissement , annonce clairement que " même si l' UMP parisienne était pour le maintien des urgences à l' , ce serait techniquement et financièrement impossible de les rouvrir si on revient aux responsabilités " . -Au Nevada , où environ 50 voitures bénévoles étaient équipées de ces appareils , les conducteurs n' étaient pas au courant du fait que le gouvernement était en mesure de surveiller chaque mouvement . Dans le Nevada , où environ 50 automobilistes volontaires ont récemment équipé leur voiture d' un appareil , les conducteurs étaient inquiets que le gouvernement puisse suivre leurs moindres faits et gestes . -À New York , des milliers de personnes en costumes ont participé à la parade organisée dans le district de Greenwich Village . A New York , des milliers de personnes ont participé à la parade organisée dans le quartier de " Greenwich village " . -À New York , les représentants des services de transport chercheront à mettre au point un système d' ATS qui serait également utilisé pour payer les frais de stationnement , fournir des assurances " " et créer un pool de données en temps réel sur les coûts des autres conducteurs que les automobilistes pourraient utiliser pour éviter le trafic . Dans la ville de New York , les responsables des transports cherchent à développer un dispositif de taxation qui serait également équipé pour payer les frais de stationnement , fournir une assurance « paiement à la conduite » et créer un ensemble de données sur la vitesse en temps réel en provenance des autres conducteurs que les automobilistes pourraient utiliser pour éviter les embouteillages . -À , en Arizona , les contrebandiers s' approvisionnent dans de vastes réseaux souterrains de drainage . À , en Arizona , les trafiquants exploitent de vastes canaux de drainage souterrains . -En octobre , GM a vendu des camionnettes et Sierra . En octobre , GM a vendu 42 660 et 16 503 Sierra . -En octobre 2012 , dans " à Bamako " , il a mentionné des colonnes de lanceurs de vers la capitale malienne . En octobre 2012 , dans " à Bamako " , il les colonnes de 4X4 de qui fondaient sur la capitale malienne . -En Oregon , les planificateurs essaient de donner aux conducteurs des choix différents . Dans l' Oregon , les planificateurs tentent l' expérience en offrant aux automobilistes différents choix . -A Paris , où il y a huit comédies musicales en saison , c' est une bonne année . À Paris , quand il y a huit spectacles musicaux dans une saison , c' est une grande année . -En septembre , il a commencé à donner la moitié de plus de milles miles parcourus à des clients qui ont acheté par l' intermédiaire d' une agence de voyages en ligne . En septembre , elle a commencé à n' offrir que la moitié de miles de fidélité aux clients qui achetaient leur billet via une agence de voyage en ligne . -À , malgré la fermeture de toutes les grandes entreprises , il n' y a aucune gêne pour les dépenses extravagantes et le maintien des éléphants blancs , tels que le centre culturel fragile , les subventions énormes pour la Cité de l' Énergie , etc. À , malgré la fermeture de toutes les entreprises majeures , on ne se gêne pas quant aux dépenses et à l' entretien des canards boiteux : centre culturel déficitaire , forte subvention à la Cité de l' Énergie , etc. -À Singapour , environ 70 % des points aux 200 points d' du forum sont de l' or . À Singapour , environ 70 % des articles mis en gage dans les 200 boutiques de prêt sur gage de la sont en or . -En Thaïlande , le plus gros opérateur de , , a enregistré une augmentation de 20 % du nombre de clients utilisant ses points de vente au cours des derniers mois . En Thaïlande , le plus grand opérateur de boutiques de prêt sur gage , , a enregistré une hausse de 20 % du nombre de clients utilisant ses boutiques au cours des derniers mois . -Lors d' une grande victoire de l' administration Obama , la Cour suprême des Etats-Unis a décidé aujourd' hui que le gouvernement de Taiwan avait signé la loi sur les soins de santé . Grande victoire pour l' administration Obama : la Cour suprême américaine a déclaré aujourd' hui que la loi de réforme du système de santé ratifiée par Obama était constitutionnelle . -Dans une note aux clients de la banque , il précise que ce léger ralentissement peut s' expliquer principalement par " l' arrêt du gouvernement et son impact sur les sentiments des consommateurs au cours de la première moitié du mois " . Dans une note aux clients de la banque , il estime que ce léger coup de frein s' explique en premier lieu par " le ' ' gouvernemental et son impact sur le sentiment du consommateur au cours de la première quinzaine du mois " . -En un mot , il est facile de faire de grandes choses avec l' argent des autres . En résumé , il est bien facile de réaliser de grandes choses avec l' argent des autres . -Lors d' un entretien avec la police , il a déclaré qu' il avait un bureau à son domicile , ainsi que son lieu de travail et que les clients à sa maison sur des affaires juridiques . Lors d' un interrogatoire , il a déclaré qu' il possédait un bureau à son domicile , de même qu' à son lieu de travail et qu' il arrivait que des clients l' appellent chez lui pour des questions d' ordre juridique . -Lors d' une conférence de presse qui a eu lieu jeudi , M. Blair a déclaré qu' il n' y avait rien dans cette vidéo qui puisse constituer une " motivation raisonnable " susceptible d' entraîner des poursuites pénales contre le maire . En conférence de presse , jeudi , M. Blair a affirmé qu' il n' y avait rien dans cette vidéo qui puisse constituer des " motifs raisonnables " pouvant mener au dépôt d' une accusation criminelle contre le maire . -Dans un communiqué de presse , le club a déclaré qu' il était " très déçu " et que l' appel était tout à fait justifié . Dans un communiqué , son club s' est dit " très déçu " , estimant que son appel était entièrement justifié . -Au cours d' un déménagement séparé , , le régulateur de l' examen , la structure du , avec un nouveau système de notation et moins de cours . Par ailleurs , , le Bureau régulateur des examens , va dévoiler un remaniement de la structure des , avec un nouveau système de notation et moins de cours . -Dans une déclaration , un porte-parole du Hamas , Sami Abou , a rendu hommage aux quatre " héros " et a déclaré que des soldats israéliens étaient morts au cours de l' affrontement . Dans un communiqué , un porte-parole du Hamas , Sami Abou , a rendu hommage aux quatre " héros " et affirmé que des soldats israéliens avaient trouvé la mort lors de la confrontation . -Dans une déclaration , des responsables de l' Église ont déclaré que M. avait démissionné de son poste . Dans un communiqué , les responsables de l' église indiquent que M. a démissionné de son poste . -Dans une déclaration faite jeudi , un représentant de , 70 ans , affirme qu' il " nie catégoriquement qu' il n' ait jamais fait un passage à Perry " . Dans une déclaration jeudi , un représentant de , 70 ans , a déclaré qu' il « nie catégoriquement avoir fait des avances à Perry » . -Dans une déclaration annonçant les nouvelles , le père Federico , porte-parole du Vatican , a déclaré qu' une réunion de tous les Cardinaux inscrits serait organisée avant la cérémonie pour élever les nouveaux cardinaux , connus sous le nom de . Dans une déclaration annonçant la nouvelle , le père Federico , un porte-parole du Vatican , a dit qu' une réunion rassemblant tous les cardinaux existants se tiendrait avant la cérémonie au cours de laquelle des évêques seront élevés à la dignité de cardinal , connue sous le nom de . -Dans une déclaration sur son site Web officiel , les singes de l' Arctique ont déclaré : " Compte tenu de la décision d' ajourner le bureau du LG Arena à partir de ce soir et après avoir consulté les spécialistes de la nature , les singes de l' Arctique doivent aussi reporter le spectacle à la Glasgow Hydro , le vendredi 1er novembre . Dans une déclaration sur leur site officiel , les Arctic ont déclaré : « Suite à la décision de reporter le concert à la LG Arena de Birmingham ce soir et après avoir demandé un avis médical , les Arctic doivent également reporter le spectacle à l' Hydro de Glasgow le vendredi 1er novembre . » -D' une certaine façon , la personne que vous aimez est votre énergie intérieure . D' une certaine façon , l' être aimé devient votre énergie intérieure . -Conformément à la loi , la police , qui a elle-même été victime de transactions suspectes , aurait pu intercepter le véhicule de Ford , arrêter le maire et procéder à une fouille - même sans mandat . En vertu de la loi , la police , ayant été témoin de transactions suspectes en mains propres , aurait très bien pu intercepter le véhicule de Rob Ford , arrêter le maire , et effectuer une perquisition - même sans mandat . -De plus , les efforts déployés par le ministère pour maintenir le personnel ont été payés , puisque les quatre étudiants qui se sont déjà installés à Laval ont décidé de rester au . De plus , les efforts déployés par le département afin de retenir ont porté des fruits , car les quatre élèves présentement en résidence à Laval ont également décidé de rester à l' emploi du . -En plus de tenir ces cliniques de fin de semaine et de travailler comme chirurgien de l' hôpital , travaille aussi des nuits dans des cliniques médicales privées autour de Yaoundé . En plus de faire vivre cet hôpital itinérant et de travailler comme chirurgien à l' hôpital , le Dr travaille aussi la nuit dans des cabinets médicaux privés autour de Yaoundé . -En plus de ces événements , il y a eu le , le concert tenu en août dernier , avec le concours de la soprano , . À ces activités s' est ajouté l' lyrique , le tenu en août dernier , avec le concours de la soprano . -En tout , près de 22 000 femmes seront privées d' accès à ces établissements . Au total , ce serait près de 22.000 femmes qui seraient privées d' un accès à ces établissements . -Dans toute la documentation par M. , cependant , il n' y a jamais eu de preuves jusqu' à ce jour que les États-Unis aient transmis les secrets commerciaux des entreprises étrangères à leurs propres sociétés . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Cependant dans tous les documents dévoilés par M. , rien n' indique à ce jour que les États-Unis aient transmis des secrets industriels d' entreprises étrangères à des sociétés américaines . -Dans une entrevue qui a eu lieu la veille pour marquer le cinquième anniversaire de son départ , le a fait part de ses vues sur diverses politiques nationales . Dans un entretien la nuit dernière marquant le 5e anniversaire de son départ à la fin de son mandat , l' homme de 59 ans a fait connaître son opinion sur diverses politiques nationales . -Lors d' une entrevue avec Martin , a confirmé que l' administration avait en fait décidé de remplacer les avec Hillary Clinton . Dans un entretien avec Martin , a confirmé que l' administration avait effectivement envisagé de remplacer Biden par Clinton . -Dans une interview avec le quotidien La , le chef du centre a déclaré qu' il avait " l' intention " de continuer à gouverner avec sa coalition jusqu' aux prochaines élections législatives de 2015 . Dans une interview au quotidien La , le dirigeant de centre gauche dit avoir " fermement l' intention " de continuer à gouverner avec sa coalition jusqu' aux prochaines élections législatives de 2015 . -En tout cas , Bowie aime jouer le dans ces : à son tour , un , un , peut-être un ... Bowie s' amuse en tout cas à jouer au avec ces titres : tour à tour informateur , homme à bord d' une fusée , potentiel ... -En décrivant les méthodes américaines d' interception des données en rapport avec la représentation diplomatique française aux États-Unis , nous ne voulons pas exprimer l' indignation à propos de cette pratique , mais plutôt décrire le monde tel qu' il est . Lorsque nous décrivons les systèmes d' interception américains à l' encontre de la diplomatie française aux Etats-Unis , ce n' est en aucun cas pour nous de cette pratique , c' est pour décrire le monde tel qu' il est . -En mourant , cette femme avait achevé sa vie et libéré la personne que j' aimais , et elle est maintenant finie , complète et accomplie . En mourant , cette femme avait parfait sa vie et libéré la personne que j' aimais , et voici que je la , pleine et entière . -Dans tous les cas , à chaque instant de ma vie , je sens leur présence , et je me réfère toujours à eux : qu' est-ce qu' ils diraient , ce qu' ils pensent , que ? Chaque événement , chaque instant de ma vie me fait ressentir leur présence car je m' y réfère toujours : qu' auraient-ils dit ? qu' auraient-ils pensé ? qu' ils fait ? -En fait , les Américains dépensent 330 millions de dollars sur les costumes d' artisanat cette année , selon la . En fait , les Américains vont dépenser 330 M $ en pour animaux cette année , selon la NRF . -En fait , il y a eu 1.200 maisons aux États-Unis , qui ont fait des tirages gratuits de 500 millions de dollars US , ce qui est vrai en Amérique latine , ce qui inclut les photos que vous de ce petit ami que vous sur votre ordinateur et que vous ne pouvez pas tomber là , alors que le gars que vous voyez peut voir la photo et laisser un commentaire comme un " beau visage " . , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ... En fait , il existe 1 200 maisons reconnues officiellement aux États-Unis , qui génèrent environ 500 M $ de recettes , selon l' America , et cela inclut ces formidables photos de vous en train de vous dessus que vos amis mettent sur Facebook et que vous ne pouvez pas enlever et sur lesquelles un type que vous aimez bien laisse un commentaire du style « jolie tête » . -En fait , nous achetons et , qui nous , 90 millions de livres de chocolat pendant l' Halloween . En fait , nous allons acheter et , ne nous pas d' histoires , consommer 41 millions de kg de chocolat pendant Halloween . -Dans son livre , La Mort en , le cher Yvon Le philosophe et du Comité National d' du met l' accent sur la " violence " que représente la crémation pour ceux qui sont à l' arrière . Dans son livre La Mort en cendres , Damien Le Guay , philosophe et vice-président du Comité national d' éthique du funéraire , insiste sur la " violence " que constitue la crémation , pour les survivants . -Dans son discours , et en présence de son homologue britannique , William Hague , le chef de la diplomatie américaine a insisté à plusieurs reprises sur les pratiques de renseignement et la collecte de données dans le cadre de la nécessaire lutte contre le terrorisme et la prévention des attaques éventuelles . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; » Dans son intervention retransmise , en présence de son homologue britannique William Hague , le chef de la diplomatie américaine a longuement justifié les pratiques de renseignements et de collecte d' informations par la nécessaire lutte antiterroriste et la prévention contre d' éventuels attentats . -Dans celui-ci , il présente chacun des disques et les accessoires fournis avec eux : des photos exclusives et des , un cahier pour partager vos impressions , un livret de paroles , etc. Il y introduit à la fois chacun des disques , mais aussi les accessoires fournis avec : des photos et pochettes exclusives , un carnet pour partager ses propres impressions , un livret avec les paroles ... -À la suite de la rébellion qui a pris plusieurs mois à l' égard d' un certain nombre de membres du groupe et de plusieurs syndicats , dont la CGT , le ministre de la Santé , Touraine , a décidé le 10 juillet de " remettre le calendrier de la mise en oeuvre du projet et , en particulier , la fermeture de l' unité d' urgence qui n' est pas prévue pour le 4 novembre " . Face à la menée depuis plusieurs mois par une partie des personnels et plusieurs syndicats , dont la CGT , la ministre de la santé , Touraine , avait décidé le 10 juillet de " le calendrier de mise en oeuvre du projet , et en particulier la date de fermeture des urgences qui ne pourra intervenir le 4 novembre " . -Dans la plupart des cas , le processus ralentit ou - quelque chose qu' un pédiatre peut surveiller de près . Dans la plupart des cas , le développement ralentit ou s' arrête – c' est quelque chose qu' un pédiatre peut suivre de près . -Selon moi , il y a deux niveaux de réponse du gouvernement français . Selon moi , il y a deux niveaux de réponse de la part du gouvernement français . -Dans la France voisine , les questions liées à l' égalité des genres sont encore controversées , selon un rapport d' information sur la France 24 . En France voisine , les questions de genre font toujours l' objet de controverses , selon un reportage diffusé sur France 24 . -Dans d' autres , le chef de l' a déclaré qu' il y a eu une forte explosion mercredi à l' intérieur d' une base de défense aérienne dans la province côtière de de . D' autre part , le directeur de l' Observatoire syrien des droits de l' Homme a déclaré qu' il y avait eu une forte explosion mercredi dans une base de défense aérienne dans la province côtière syrienne de . -Dans de rares cas de visibilité réduite , l' équipage ordonne aux passagers d' éteindre leurs appareils pendant l' atterrissage . Dans les rares cas de faible visibilité , l' équipage demandera aux passagers d' éteindre leurs appareils pendant l' atterrissage . -En bref , il est là tous les jours . Bref , il est là au quotidien . -En bref , le illimité de l' argent de nos poches . Bref , sans limite de nos poches . -Malgré tout , j' ai dit mon assez rapidement . J' ai malgré tout réalisé mon deuil assez rapidement . -En ce qui a trait aux résultats , a indiqué qu' elle a enregistré un bénéfice net de 43,8 millions de au quatrième trimestre , soit 82 ¢ par action . Du côté des résultats , a indiqué avoir enregistré un bénéfice net de 43,8 millions au quatrième trimestre , ou 82 ¢ par action . -En matière de revenus , a connu une croissance de 41,5 % au quatrième trimestre , atteignant millions de dollars . Quant aux revenus de , ils ont connu une croissance de 41,5 % au quatrième trimestre pour atteindre millions . -Lors de tests , les souris vaccinées , qui ont ensuite reçu la nicotine , ont poursuivi leur activité normale . Lors des tests , les souris vaccinées à qui on a donné ensuite de la nicotine ont poursuivi leur activité normalement . -Dans les années 1980 , Jordan se lance dans une arnaque qui lui rendra un homme très riche . Dans les années 80 , Jordan Belfort monte alors une arnaque qui fera de lui un homme riche , très riche . -Au Royaume-Uni , les évêques ont publié un sondage sur Internet et demandé aux catholiques de participer . Au Royaume-Uni , des évêques ont publié le sondage sur Internet en demandant aux catholiques de participer . -Aux États-Unis , l' âge moyen est de 12,5 ans , contre en 1970 . Aux États-Unis , l' âge moyen est de 12 ans et demi , alors qu' il était de 12 ans trois quarts en 1970 . -Aux États-Unis , le berceau de la musique , c' est très différent . Aux États-Unis , berceau de la comédie musicale , c' est très différent . -Aux États-Unis , le service vidéo populaire parle de la possibilité de faire de son service par l' intermédiaire de ses plateformes de diffusion avec certaines grandes entreprises de câblodistribution . Aux États-Unis , le populaire service de vidéo dit être en discussions avec certains câblodistributeurs majeurs afin que son service soit disponible via leurs plateformes de diffusion . -Au début du XIXe siècle , animaux classés selon les différences morphologiques entre les espèces . Au début du 19ème siècle , les ont classé les animaux , sur base des différences morphologiques entre les espèces . -En fin de compte , il s' est senti à la fois confortable et rassurant d' être compris , encouragé , conseillé . Finalement , c' était confortable et rassurant d' être comprise , encouragée , conseillée . -Le soir , l' équipe effectuera des chirurgies simples avec l' anesthésie locale . Le soir , l' équipe pratique des actes chirurgicaux simples sous anesthésie locale . -Dans la première action , la Congrégation de la Sainte Croix a accepté de se mettre d' accord sur la question avant de procéder à des auditions préliminaires . Dans le cas du premier recours , la congrégation de Sainte-Croix avait accepté de régler à l' amiable avant que des auditions soient menées sur le fond . -Au cœur du cimetière de , des civils , des religieux et des militaires , ainsi que de nombreuses associations patriotiques , ont rendu hommage à tous les soldats et victimes de toutes les guerres le vendredi après-midi . Au cœur du cimetière du , autorités civiles , religieuses et militaires , ainsi que de nombreuses associations patriotiques , ont rendu , ce vendredi après-midi , un hommage aux soldats et défunts de toutes les guerres . -Dans sa lettre , M. a déclaré que le soutien de la communauté internationale pourrait convaincre le gouvernement américain d' abandonner les poursuites pénales contre lui . Dans sa lettre , M. a écrit qu' il pensait que le soutien de la communauté internationale pourrait persuader le gouvernement américain d' abandonner les charges pénales retenues contre lui . -Dans le local de son petit appartement dans l' immeuble situé sur l' avenue , à quelques pas de l' avenue Leon , avec son chat au ventre et son à dos de poitrine dans les cheveux blancs , il a décrit certaines des langues que les aventures de la SAS ont été enregistrées : italien , allemand , russe , grec , japonais et coréen . , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , etc. Son chat birman sur les genoux , dans le salon de son immense appartement d' un immeuble de l' avenue Foch à deux pas de l' Arc de Triomphe , l' oeil malicieux sous ses cheveux blancs , il citait quelques unes des langues dans lesquelles les aventures de SAS ont été traduites : italien , allemand , russe , grec , japonais ou coréen . -Dans le passé , les espèces ont été décrites et identifiées sur la base de la morphologie , de l' anatomie et de la géographie . Aujourd ' hui , le développement de nouveaux outils comme la génétique permet d' améliorer notre connaissance de la science de la classification des espèces . Si par le passé , les espèces ont été décrites et identifiées sur base morphologique , anatomique et géographique , aujourd' hui , le développement de nouveaux outils tels que la génétique nous permettent d' améliorer nos connaissances en sciences de la classification des espèces . -Dans le passé , le Pape décidait tout seul . Auparavant , le pape décidait tout seul . -Dans ces villages , les soins médicaux gratuits sont vraiment une cause de célébration , et - avec son grand sourire et son énergie infinie - est plus que heureux de se joindre à l' amusement . Dans ces villages , les soins de santé gratuits sont une bonne raison de faire la fête , et le Dr – avec son grand sourire et son énergie débordante – est plus qu' heureux de se joindre aux festivités . -Dans ce contexte , l' euro continue de baisser par rapport au dollar américain et , au cours de la session , a atteint un niveau record de 36 semaines , à dollars . Dans ce contexte , l' euro poursuit son repli contre la monnaie américaine et a touché en séance un plus bas de deux semaines à dollar . -Dans ce pays , les filles d' origine afro-américaine et hispanique tendent à avoir la puberté plus tôt que leurs homologues blancs . Dans ce pays , les jeunes filles et hispaniques ont tendance à atteindre l' âge de la puberté plus tôt que les jeunes filles blanches . -À ce jour , l' âge avec un crayon semble extraordinaire . De nos jours , avoir un crayon de papier semble extraordinaire . -Dans ce film à l' humour , où la musique est un personnage essentiel , le théâtre est rempli d' émissions , et l' écrivain Isaac s' est révélé être un musicien populaire et chanteur prolifique , tandis que Jill , son ami dans le cinéma , s' est mis à abandonner la pop music à la joie . Dans ce film bourré d' humour où la musique est un personnage central et les chansons interprétées en direct , Oscar Isaac se révèle un musicien et chanteur de folk accompli , tandis que Timberlake , son ami dans le film , quitte la pop pour la musique folk avec une contagieuse . -Dans cette performance , la musique est forte , accompagnée de percussions et de guitares électriques . Ici , la musique est forte , percussions et guitares électriques . -À cet égard , le Comité du renseignement du Sénat a approuvé le renforcement des contrôles des programmes de surveillance du Conseil jeudi , mais les a autorisés à poursuivre . Dans ce contexte , la Commission sénatoriale du renseignement a approuvé jeudi un renforcement des contrôles sur les programmes gouvernementaux de surveillance mais a autorisé leur poursuite . -Compte tenu de l' urgence de la situation , le Directeur du Bureau du Sud de la terre rurale a offert d' offrir un logement provisoire exceptionnel au couple et à ses trois enfants dans une nouvelle maison de . Devant l' urgence de la situation , le directeur de l' office Terres du Sud Habitat , a proposé , de manière exceptionnelle et provisoire , de reloger le couple et ses trois enfants dans un T4 neuf . -En effet , c' est la demande dans certaines parties de l' Asie du Sud-Est - où la dette des ménages est en hausse - que , où elle effectue sa transaction , est devenue la troisième à figurer sur la Bourse de Singapour . En fait , la demande est telle dans certaines parties d' Asie du sud-est – où l' endettement des ménages augmente – que , où elle a effectué sa transaction , est devenu cette semaine la troisième société de prêts sur gage à être introduite à la bourse de Singapour . -Les producteurs français indépendants essaient de faire une percée . Les ­ producteurs français indépendants tentent une ­ percée . -L' Indiana , la Géorgie , la Floride , le Mississippi , la Louisiane , l' Oklahoma , la Caroline du Sud et la Virginie occidentale , suivis par le refus , selon un haut responsable de la Défense . Il a été rejoint dans son refus par l' Indiana , la Géorgie , la Floride , le Mississippi , la Louisiane , l' Oklahoma , la Caroline du Sud et la Virginie occidentale , selon un haut responsable de la Défense . -L' Indonésie a convoqué l' ambassadeur de l' Australie , dont la mission a été accusée d' être utilisée par les Américains dans le cadre d' un vaste réseau international d' espionnage , qui a également suscité la colère de la Chine . L' Indonésie a convoqué l' ambassadeur d' Australie , dont la mission est accusée d' être utilisée par les Américains dans le cadre d' un vaste réseau d' espionnage international qui a également suscité l' de la Chine . -Les investigations ont révélé que était un " utilisateur régulier " de la drogue de la classe A après des tests de traces de cocaïne dans ses cheveux , sur son porte-monnaie et sur ses cartes de crédit . Les enquêtes ont révélé que était un « consommateur régulier » de cette drogue de classe A après que des tests ont montré des traces de cocaïne dans ses cheveux , sur son portefeuille et ses cartes de crédit . -A l' intérieur de l' , Son Altesse le prince ( ) , un indépendant et anonyme de l' Autriche pour le financement de la CIA ( pour payer les factures de la maison de la famille ) , est très loin de tous les des communistes - dans les années 70 et 80 , les de la défense depuis les années 1990 & # 160 ; : , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ... A l' intérieur , son ( SAS ) le prince , autrichien et agent contractuel de la CIA pour payer les réparations du château de famille , se lance aux trousses de tous les méchants de la terre , communistes des années 70 et 80 , puis à partir des années 90 . -Au lieu de cela , a passé des années à escorter son père dans des dispensaires et des hôpitaux surpeuplés , obtenant ainsi tout ce qu' ils pouvaient lui offrir . Au lieu de cela , Dr a passé des années à accompagner son père dans des cliniques et des hôpitaux bondés , pour qu' il obtienne n' importe quel traitement dont il pouvait bénéficier . -Au lieu de cela , il croit que les chiens ont appris de l' expérience qu' ils devraient faire et ne devraient pas s' inquiéter . Il pense plutôt que les chiens savent par expérience les mouvements dont ils devraient ou ne devraient pas s' inquiéter . -Enquête sur le maire Rob Ford , juriste en détachement L' enquête sur le maire Rob Ford a été , soutient un avocat -Les enquêteurs ont également trouvé des entrées sur l' ordinateur de l' enquêteur privé qui avait choisi Mme et sa soeur comme " projets " . Les enquêteurs ont également trouvé des saisies sur l' ordinateur du détective privé qui concernaient Mlle et sa sœur rangées dans un dossier « Projets » . -L' élite européenne est-elle prête à faire des affaires avec la Grande-Bretagne ? L' élite européenne est-elle prête à faire des affaires avec la Grande-Bretagne ? -Est-ce qu' il est professeur dans des salles de cours et dans des salles de classe ? Professeur dans les et les salles de cours ? -Est-ce l' héritier de la chaire de à l' Académie Française ? du fauteuil qu' occupa à l' Académie française ? -Est-ce parce qu' ils peuvent le résultat si quelqu' un ne l' aime pas ? Est-ce pour pouvoir effacer le résultat si quelqu' un n' est pas d' accord ? -Est-ce la fin des mots de passe pour accéder à un smartphone ou payer pour les achats ? Fini les mots de passe à taper pour accéder à son smartphone ou payer ses achats ? -N' y a-t-il pas un élément d' hypocrisie de votre part ? N' y aurait-il pas comme une vague hypocrisie de votre part ? -Est-ce que cela veut dire remercier M. ? Est-ce pour faire plaisir à M. ? -Assassinat du Hamas tué par l' armée israélienne à Gaza L' armée israélienne tue un membre du Hamas à -Les avions israéliens sont entrés dans l' espace aérien libanais tôt le mercredi après-midi , mais n' ont pas fait d' attaques jusqu' au soir , selon l' armée libanaise . Selon l' armée libanaise , des avions israéliens sont entrés dans l' espace aérien libanais mercredi en début d' après-midi , mais les frappes ont eu lieu dans la soirée . -Attaque israélienne dans le nord de la Syrie Attaque israélienne dans le nord de la Syrie -Des responsables gouvernementaux israéliens ont refusé de confirmer toute information relative à l' attaque . Des responsables du gouvernement israélien ont eux refusé de confirmer toute information concernant une telle attaque . -Les responsables israéliens n' ont pas tenté de cacher leur colère lorsque Washington a révélé cette attaque , au risque de forcer le président Assad à réagir . Les responsables israéliens n' avaient pas caché leur irritation de voir Washington révéler l' attaque , au risque d' obliger le président Assad à réagir . -Au cours de la journée , des milliers de personnes ont été tuées par des actes de violence contre des civils et des personnes déplacées à l' intérieur de leur pays d' origine . Des tirs de chars israéliens ont tué jeudi un militant palestinien islamiste du Hamas et en a grièvement blessé un autre dans le sud de la bande de Gaza , a-t-on appris après de sources des services de santé . -L' objectif de l' attaque aérienne israélienne à l' intérieur de la Syrie est le suivant : Des avions de guerre israéliens ciblent la Syrie , déclare un responsable -Des avions de combat israéliens ont atteint la cible dans la ville portuaire de vendredi , un haut responsable de l' administration a confirmé à Fox News . Des avions de guerre israéliens ont atteint une cible dans la ville portuaire syrienne de jeudi soir , confirme un responsable militaire à Fox News . -C' est un rythme , mais les musiciens du village aident habituellement à maintenir l' équipe . C' est un rythme , mais les musiciens des villages aident en général l' équipe à rester motivée . -C' est un peu compliqué , un peu de choc , assez difficile à gérer . C' est un peu compliqué , un peu violent , assez complexe à gérer . -C' est un livre que je pensais me au milieu de la terminale de médecine de deuxième classe , et en moi un sentiment d' espérance . C' est un livre qui , je pensais , m' encouragerait pendant la morosité de la 2e année de médecine et me de l' espoir . -Il s' agit d' un retour en noir et blanc pour Marie Chouinard , qui forme Marie Danse , deux de ses nouvelles compositions , la première à partir des dessins d' encre et des poèmes d' Henri , le second inspiré par les solos de piano de de Satie . Retour en noir et blanc pour Marie Chouinard , qui présente à Danse Danse ses deux nouvelles chorégraphies de groupe : la première inspirée des encres et poèmes d' Henri , la deuxième par les fameux solos de piano de Satie . -C' est un mystère que je n' ai pas encore résolu . C' est un mystère que je ne suis pas encore arrivé à élucider . -C' est une révolution : la musique française se donne comme un genre de réussite . C' est une révolution : le musical à la française s' affirme comme un genre à succès . -Il s' agit des clients les plus fidèles de Frontier , qui savent très bien qu' il est devenu de plus en plus difficile de trouver de l' espace . Il s' agit de faire comprendre aux clients de Frontier les plus fidèles qu' il devient de plus en plus difficile de trouver de la place dans les compartiments supérieurs . -Il semble que la poursuite de la campagne d' Israël continue d' empêcher les armes de proliférer au Moyen-Orient . Ce serait apparemment la poursuite de la campagne israélienne visant à empêcher la prolifération des armes dans le Moyen-Orient . -C' est ce que l' on appelle " to Win " , et c' est un des souvenirs de la campagne présidentielle de Barack Obama en 2008 . Il s' appelle « The to Win » et il s ’ agit des mémoires de la campagne présidentielle de Barack Obama de 2008 . -Cinq ans plus tard , Monsieur le Président , je ne suis pas d' accord avec vous . Nous voici 5 ans après , M. le Président , et je ne suis plus d' accord avec vous . -C' est une bonne nouvelle , mais ça ne m' a pas surpris , surtout . C' est une bonne nouvelle , mais cela ne m' étonne pas particulièrement . -Il est difficile de se souvenir de ces jours d' optimisme -- ils semblent une mémoire lointaine , un triste rappel des occasions qui se sont écoulées . Il est difficile de se rappeler ces jours pleins d' optimisme , ce sont comme de lointains souvenirs , un triste rappel des opportunités du passé . -Il est difficile de comprendre ce revirement . On ne comprend pas cette volte-face . -C' est comme le mystère de la raison pour laquelle les gens sont homosexuels . C' est comme le mystère qui entoure les personnes homosexuelles . -Ce n' est pas un procès , c' est une farce " . Ce n' est pas un procès , c' est une farce . -Il s' agit maintenant d' enquêter sur les allégations de M. qui connaissaient l' intérêt secret d' Europol pour les biens , après les témoignages faits par l' ancien adjoint du chef de cabinet , M. Lynne , jeudi . Elle enquête désormais sur les allégations selon lesquelles M. était au courant de l' intérêt secret de M. Obeid dans ces propriétés , suite au témoignage de l' ancienne secrétaire générale adjointe de M. , Lynne , jeudi . -C' est détendu , c' est cool . C' est bon enfant , c' est cool . -C' est la première fois en 20 ans que la belle blonde a eu des cheveux courts , et nous aimons le changement . C' est la première fois en 20 ans que la jolie blonde se fait couper les cheveux , et on est fan du changement discret . -C' est la même chose avec mes opinions : je m' efforce de résister à la générosité et à la justesse de son esprit en tant que fervent partisan . Il en va de même pour mes attitudes : j' essaie d' être à la hauteur de la générosité et de la droiture d' esprit de celle qui était une fervente . -C' est ce deuxième travail , a-t-il dit , qui finance environ 60 % de son ; le reste est pris en charge par des dons privés . C' est ce deuxième emploi , explique-t-il , qui finance à 60 % son association ; le reste est couvert par des dons privés . -Il n' est cependant pas difficile de savoir si Obama savait que son équipe recherchait l' échange . Cependant , on ne sait pas si Obama était au courant que son équipe examinait le remplacement . -Elle a ajouté que " d' autres activités d' inspection ne sont pas prévues actuellement " . Elle a ajouté que « aucune autre activité d' inspection n' était prévue pour le moment » . -Il permet également aux passagers de choisir leur siège à l' avance seulement s' ils achètent directement sur le site de la Frontier Convention . Elle permet également aux passagers de choisir leur siège à l' avance seulement s' ils achètent directement leur billet sur le site de Frontier . -Elle recommande également aux écoles d' être - la marque la plus communément utilisée -- pour réagir à une réaction fatale . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Il recommande également aux écoles d' avoir un stock d' – l' de marque étant le plus couramment utilisé – pour pouvoir réagir rapidement en cas d' potentiellement mortelle . -Il a également dit que les mesures pourraient avoir un effet dévastateur sur ce qu' il décrit comme la " blogosphère animée " du pays . Il dit également que les mesures pourraient avoir un effet dévastateur sur ce qu' il a décrit comme la « blogosphère animée » du Kenya . -Il est également dit que la recherche montre que l' expérience de la cabine dépend plus que de la largeur d' un siège . Il dit également que l' étude montre que l' expérience en cabine ne dépend pas seulement de la largeur des sièges . -Il montre également que les entreprises s' attendent à ce que cette renégociation un changement important dans l' équilibre actuel du pouvoir vers le Royaume-Uni . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Elle montre également que les entreprises espèrent que la renégociation constituera un changement significatif dans l' équilibre actuel du pouvoir en faveur d' un retour aux mains du Royaume-Uni . -Selon les informations contenues dans le rapport britannique de Powell , " les services de la Force de sécurité fédérale en Irlande ( ) et le United States Command and Communications Bureau ( ) en Grande-Bretagne ont rassemblé des renseignements grâce à l' infiltration des réseaux internationaux , ce qui a permis aux deux entités de synchroniser leurs serveurs . Elle juge " scandaleuses " les informations du Washington Post , selon lequel l' Agence nationale de sécurité américaine ( NSA ) et le Government Communications Headquarters ( ) britannique ont collecté des masses d' informations en les réseaux internationaux qui permettent aux deux firmes de synchroniser leurs serveurs . -Elle ne peut justifier l' assassinat d' Anwar , un citoyen américain , sans procès , ni contre le financement public et les limites de dépenses au cours des campagnes présidentielles . Elle ne peut pas justifier l' assassinat d' Anwar , un citoyen américain , sans procès , ni l' évasion des financements publics et le dépassement des dépenses autorisées pendant les campagnes présidentielles . -Il ne peut pas expliquer que l' Agence nationale de sécurité recueille des données sur la vie privée des Américains , ni ne surveille les dénonciateurs d' abus . Elle ne peut pas expliquer que la National Security Agency recueille des données sur la vie privée des Américains , ni la mise en accusation des dénonciateurs qui révèlent les méfaits du gouvernement . -Elle ne peut justifier les conclusions d' un rapport qui dit que les efforts de la Maison Blanche pour couper les médias sont les " les plus agressifs ... depuis l' administration Nixon " . Elle ne peut pas justifier les conclusions d' un rapport qui dit que les efforts de la Maison-Blanche pour réduire les médias au silence sont les « plus agressives ... depuis l' administration de Nixon » . -Cela ne m' a pas dérangé . Ça ne m' a pas gêné . -Il permet aux enfants de prendre la décision pour eux-mêmes . Cela autorise l' enfant à prendre la décision pour lui-même . -Il leur donne le temps de procéder à des tests et de les situer et de passer un peu de temps avant qu' ils n' aient " masculins " ou " féminins " . Cela leur laisse le temps de faire certains tests et de les comprendre , et un délai avant d' inscrire « masculin » ou « féminin » . -Il a également ordonné au Groupe de travail de de procéder à une correction complète de New Express , et a recommandé d' enquêter sur les personnes responsables de New Deal et de faire une nouvelle mise à jour de l' équipe de direction de New Express . Il a également « ordonné au groupe News d' entreprendre un remaniement complet de New Express , et a recommandé qu' il mène une enquête sur les personnes responsables chez New Express et immédiatement l' équipe de direction du journal » . -Il avait quitté Toulon à la mi-octobre pour un stage de formation . Il avait de Toulon à la mi-octobre pour un entraînement . -Elle devait soit réinvestir dans le bâtiment pour continuer à l' utiliser , ce qui exigerait des investissements de plusieurs dizaines de millions de dollars , alors que la concurrence est féroce puisque de nombreux nouveaux hôtels sont nés à Montréal . Soit il dans la bâtisse pour continuer à l' opérer , donc il y avait des investissements de dizaines de millions alors que la compétition est très féroce , il y a beaucoup de nouveaux hôtels qui sont apparus à Montréal . -Elle a toujours eu lieu . Il y en a toujours eu . -Il est britannique depuis son occupation par les mutins de Bounty , un épisode de l' histoire maritime britannique qui a été transformé en trois films . Il est britannique depuis son occupation en 1790 par les du Bounty , épisode de l' histoire de la marine anglaise qui donna lieu à trois adaptations . -Il n' a pas mis à jour ce chiffre depuis . Elle n' a pas actualisé ce chiffre depuis . -C' est un privilège de savoir que les gens que nous aimons ne nous quittent jamais . C' est un privilège de savoir que les gens aimés ne nous quittent jamais . -Tout cela fait partie du processus . Ça fait partie de la procédure . -Il est également regrettable de noter l' achat obligatoire de nombreuses propriétés pour faire place à un grand complexe industriel qui n' est jamais arrivé . Il est navrant aussi de constater l' expropriation de plusieurs propriétés afin d' accueillir une industrie majeure qui n' est jamais venue . -Il est également possible d' obtenir un casque de protection ou un pour le même prix . Pour le même prix , il est aussi possible d' acquérir une housse de protection fabriquée en ou un . -C' est à la fois un mécanisme de sécurité - ces données ne peuvent être saisies sans connaissance - et un inconvénient - la procédure doit parfois se répéter et le temps passe . C' est à la fois une sécurité - on ne peut pas capter ces données à leur insu - et un inconvénient : la procédure doit parfois être répétée et prend donc du temps . -Il est grand temps que le cheval de Troie flamand soit arrêté . Il est grand temps d' arrêter le cheval de Troie de la Flandre . -C' est dans ce contexte que , depuis le début de l' année 2010 , le Père du organise un certain nombre de cérémonies , laïques et , à l' occasion de la Journée des saints , auxquelles il invite les familles de tous ceux qui ont été au cours de l' année . C' est dans ce cadre que le du Père organise , depuis 2010 , le jour de La Toussaint , plusieurs cérémonies du souvenir , laïques et non religieuses , auxquelles il invite les familles des personnes qui ont été dans l' année . -Il est en effet impossible à cause de la réfraction causée par les cristaux de glace qui composent la crème glacée , mais je pense que nous trouverons un moyen de le faire . C ’ est intrinsèquement impossible en raison de la réfraction causée par les cristaux de glace qui constituent la crème glacée , mais je pense que je un moyen d ’ y arriver . -Il n' est pas surprenant que l' idée se tourne vers les libéraux urbains , car les impôts pourraient être utilisés pour modifier les modes de comportement de manière à contribuer à réduire la congestion et les gaz à effet de serre , par exemple . Il n' est pas surprenant que l' idée les libéraux citadins , puisque les taxes pourraient servir à modifier les habitudes de conduite de manière à permettre de réduire les encombrements et les émissions de gaz , par exemple . -Ce n' est pas au Trésor public d' investir dans de tels projets , mais au secteur privé en fonction de la demande des consommateurs . Ce n' est pas aux paliers publics à investir dans de telles entreprises , mais bien au secteur privé en se basant sur la demande du consommateur . -Il est maintenant possible d' identifier un visage ou une iris mobile . Il est désormais possible d' identifier un visage ou un iris en mouvement . -C' est probablement la crème glacée la plus chère que j' ai faite parce que la des méduses est quatre fois plus chère que l' or . C' est probablement la glace la plus chère que j' ai créée car la des méduses vaut quatre fois plus cher que l' or . -Il rappelle le " du matin " , où , sous forme de solos , Chouinard offre une série de spectacles sur des ententes et s' devant l' avant de la scène . On pense à " Gloire du matin " , où , en solo , Chouinard effectuait une série de tâches écrites sur des cartels , posés en ligne à l' avant-scène . -C' est le contraire pour les funérailles d' un être aimé - les Français préfèrent les ( 53 % contre 47 % ) . Pour les obsèques d' un proche , c' est l' inverse : les Français préfèrent l' inhumation ( 53 % contre 47 % ) . -C' est le même principe dans une version PowerPoint . C' est le même principe , version PowerPoint géant . -Il n' est donc pas surprenant qu' il prenne un masque dans la photographie promotionnelle pour L' au Voyage , par Louis Vuitton , dont il est le nouveau visage . Rien d' étonnant à ce qu' il ait donc un masque à la main dans la photographie de la campagne L' Invitation au Voyage , de la marque Louis Vuitton , dont il incarne le nouveau visage . -On ne sait pas très bien quelle est la capacité de , mais il a mené des attaques contre des postes de police et des véhicules sur une autoroute nord-sud centrale . On ne sait pas exactement quelle est la capacité de la , mais elle a mené des attaques éclair perturbatrices sur les commissariats de police et les véhicules sur un axe autoroutier nord-sud majeur . -Cela me rend pour les nombreuses promesses qui ont disparu . Il me fait désirer les nombreuses promesses qui se sont envolées . -Cela signifie qu' ils peuvent maintenant obtenir des écouteurs , un chargeur ou même un boîtier de stockage . Ils peuvent ainsi acquérir un , un chargeur ou bien encore un étui de rangement . -Il s' est récemment demandé ce que pourrait être le début d' une nouvelle guerre d' annonce en montrant à des financiers une puce montrant trois personnes dans un restaurant , intitulé " Vous pouvez souscrire à cette devise " ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? On a pu se rendre compte récemment de ce qui pourrait être le début d' une nouvelle guerre publicitaire avec la présentation aux financiers d ’ une photo montrant trois personnes les unes contre les autres au restaurant , intitulée « Est-ce que vous ça ? » . -Il révèle que les ont aimé rester à la maison . On y découvre que le était un . -Elle a déclaré qu' elle avait décidé " d' imposer une sanction administrative à Chen en suspendant la délivrance de son autorisation de journaliste " . Il a indiqué qu' il avait décidé « d' imposer une sanction de nature administrative à Chen en lui retirant sa carte de presse » . -Elle a indiqué que la Malaisie , la Thaïlande et Singapour avaient le taux d' endettement des ménages le plus élevé par rapport au produit intérieur brut en Asie . Elle a déclaré que la Malaisie , la Thaïlande et Singapour enregistraient le ratio endettement des ménages / produit intérieur brut le plus élevé d' Asie . -Il semble que l' oiseau a été épuisé et a réussi à trouver refuge sur la plate-forme pétrolière . Il semble que l' oiseau épuisé aurait réussi à trouver refuge sur la plate-forme pétrolière . -Il convient de noter que le milieu marin est le moins connu des environnements . Il faut savoir que le milieu marin est le milieu le moins bien connu . -Rappelons que le venait de revenir d' une période de maintenance de six mois . Rappelons que le porte-avions sort d' une période d' entretien intermédiaire de six mois . -Elle précise que " les deux cas identifiés en mai 2013 restent les seuls cas confirmés en France jusqu' à maintenant " . Il précise que " les deux cas identifiés en mai 2013 restent donc les deux seuls cas confirmés en France à ce jour " . -C' est assez beau , mais c' est vrai . Mais elle a plutôt bon goût ! -Il les pompiers de qui se sont précipités sur la scène presque une heure pour faire tomber les flammes . La trentaine de pompiers qui ont été dépêchés sur les lieux ont mis près d' une heure à maîtriser le . -C' était un peu grossier , C' est un peu brutal . -C' était incroyable de le ramener dans nos bras . C' était surprenant de l' avoir à nouveau dans les bras . -Il était au milieu d' une guerre et les gens qui nous ont conduits étaient eux aussi trop nombreux . C' est la guerre , et ceux qui nous conduisent sont eux aussi très tendus . -C' était normal . C' était logique . -Il n' était pas clair si l' installation de était l' un des deux sites que les inspecteurs de l' OIAC n' avaient pas pu visiter . Il était difficile de savoir à première vue si l' unité de était l' un des deux sites que les inspecteurs de l' OIAC n' ont pas pu visiter . -Ce n' était pas quelque chose que les gens voulaient . Ce n' est pas quelque chose que les gens veulent . -Ce n' était que la deuxième fois qu' il était en compétition avec et nous étions tous quand il a gagné . C' était seulement la deuxième fois qu' il avec et nous étions fous de joie qu' il ait gagné . -C' est vraiment ce qu' ils ont fait . Ce qu' ils ont fait était vraiment courageux . -Elle a été reprise par les Forces armées de la République démocratique du Congo ( FARDC ) mercredi . Elle a été reprise mercredi par les Forces armées de la République démocratique du Congo mercredi . -Elle cherchait à s' introduire dans un marché émergent de l' assurance automobile , dans lequel les conducteurs paieraient en fonction de leur kilométrage . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Elle cherchait à percer dans le marché émergent de l' assurance auto , dans lequel les conducteurs paieraient en fonction de leur kilométrage . -C' était tout simplement rien que j' aurais pu imaginer . Cela n' avait rien à voir avec ce que j' avais imaginé . -Il était encore tendu parce qu' il n' y avait aucun moyen d' évacuer l' aéroport de Chypre à Larnaca . La tension est toujours là car nous n' avons pas de moyens pour nous évacuer vers l' aéroport de Larnaca à Chypre . -Il fera des changements dans d' autres matières premières l' an prochain . Il procèdera à des changements dans d' autres matières fondamentales l' année prochaine . -Il ne sera probablement pas long avant que nous comprenions bien pourquoi leurs queues parfois un moyen , parfois l' autre Il ne faudra sûrement pas longtemps avant que nous comprenions pourquoi leur queue va parfois d' un côté et parfois de l' autre . -Il donnera encore de l' eau . Elle distribuera toujours de l' eau gratuitement . -Il serait ridicule pour l' Écosse de voter pour l' indépendance seulement pour accepter l' austérité imposée par Berlin et Bruxelles . Il serait ridicule maintenant pour l' Écosse de voter pour l' indépendance si c' est pour accepter l' austérité imposée par Berlin et Bruxelles . -Il ne s' arrêtait pas et nous étions bien placés pour savoir ... Ça n' arrête pas , nous sommes bien placés pour le savoir ... -L' entreprise américaine a récupéré ses pertes pendant la première moitié de la période t et elle devrait livrer son troisième bénéfice consécutif complet . Sa filiale aux États-Unis a récupéré ses pertes au cours du premier semestre où la demande saisonnière est plus faible et devait tirer un bénéfice sur l' ensemble de l' exercice pour la troisième fois consécutive . -Sa dette , qui est encore relativement modeste , a explosé ces dernières années : le S & P estime à 33,5 % le PIB , contre 10 % avant la crise de 2008-2009 . Son endettement , encore relativement modeste , a explosé ces dernières années et S & P l' estime à 33,5 % du PIB , contre moins de 10 % avant la crise de 2008-2009 . -Son bénéfice net pour le troisième trimestre est passé à millions de dollars , soit $ par action , comparativement à millions de dollars , soit $ par action , l' année précédente . Son bénéfice net du troisième trimestre a diminué à milliards de dollars , soit dollar par action , contre 5,25 milliards , soit dollars par action , un an auparavant . -Son chiffre d' affaires s' élève à 1 800 millions de dollars pour l' exercice en cours . Ils sont de 1,8 milliard pour l' exercice financier en cours . -Jack , de la Voix des catholiques , a dit qu' en février , le nombre d' électeurs cardinaux aurait probablement chuté . Jack des Voix catholiques a déclaré que d' ici février , le nombre de cardinaux électeurs aura probablement diminué . -Jacques Chirac avait certainement un " palais plus raffiné " . Jacques Chirac avait certes " un appétit un peu plus développé " . -James , directeur américain du renseignement national , a réitéré en septembre que les États-Unis n' avaient pas recours à des moyens de renseignement étrangers pour " voler les secrets des entreprises étrangères au nom de sociétés américaines afin de renforcer leur compétitivité internationale ou d' accroître leur rentabilité " . James , le directeur des services de renseignement américains , a insisté à nouveau en septembre sur le fait que les États-Unis n' utilisent pas leurs capacités en matière de renseignement étranger pour « voler les secrets industriels des entreprises étrangères pour le compte de sociétés américaines afin d' accroître leur compétitivité internationale ou augmenter leurs bénéfices » . -Jenny est une vedette de la série " Saturday Night Live " et " Parks and Recreation " , bien qu' elle soit la plus connue pour sa série vidéo " Iris the Shell with Gift On " . Jenny a participé à l' émission « Saturday Night Live » et a fait partie du casting de la série « Parks and Recreation » , mais elle est pourtant plus connue pour sa série de vidéos virales « Marcel the Shell with Shoes On » . -Les sur la largeur des sièges avec des commandes importantes Les se au sujet de la largeur des sièges alors que de grosses commandes sont en jeu -Jim , exécutif à SAP , a déclaré : " Nous avons une nouvelle question à propos des clients qui n' ont pas vu le jour il y a un an - c' est à ce jour que mes données sont stockées et nous vous garantissons qu' elles peuvent demeurer physiquement dans cette catégorie . Jim , de SAP , déclare : « Nos clients nous posent aujourd' hui une question qu' ils ne nous posaient pas il y a un an : où mes données sont-elles conservées et pouvez-vous me garantir qu' elles resteront physiquement dans cette juridiction ? » . -Joel et , dont la a récemment fait l' objet d' une revue à la Cinémathèque française , ont fait la Palme d' Or à Cannes en 1991 avec Barton Labs . Joel et , dont la filmographie vient de faire l' objet d' une rétrospective à la Cinémathèque française , avaient la Palme d' or à Cannes en 1991 pour Barton Fink . -John Kerry a fait preuve d' un assouplissement de la position défensive des États-Unis à l' égard de ses programmes de surveillance avec une admission sans précédent dans laquelle , à son avis , l' espionnage a " atteint un point de vue trop " . John Kerry a indiqué un de l' attitude défensive des États-Unis vis-à-vis de ses programmes de surveillance en admettant , fait sans précédent , qu' à plusieurs occasions , l' espionnage « était allé trop loin de manière inappropriée » . -John Kerry affirme que l' espionnage américain " a atteint un niveau beaucoup trop bas " dans un appel sans précédent John Kerry a admis , fait sans précédent , que l' espionnage américain « était allé trop loin de manière inappropriée » -Le journaliste Jean-Louis a été kidnappé le 6 mars 1984 et , accompagné de trois membres de son équipe de 2 qui étaient venus filmer une manifestation du Hezbollah , il a été libéré 21 mois plus tard , le 27 novembre 1987 . à Beyrouth le 8 mars 1986 avec trois membres de son équipe d ' " Antenne 2 " venue filmer une manifestation du Hezbollah , le journaliste Jean-Louis a été libéré près de 21 mois plus tard , le 27 novembre 1987 . -A peine 10 ans , il a déjà gagné sa place dans les championnats européens de cette année à Copenhague , et il n' a rien à envier à des concurrents presque deux fois sa taille ! A seulement 10 ans , il a déjà conquis cette année sa place pour les championnats d' Europe à Copenhague et il n' aime rien tant qu' en à des concurrents qui le dépassent de trois têtes . -Juste à 4 heures du matin , un conducteur qui se dirigeait vers le nord en direction de a perdu le contrôle de son véhicule et s' est écrasé dans une autre voiture qui circulait dans la direction opposée . Peu avant 4h , un automobiliste qui circulait en direction nord , à , a perdu la maîtrise de son véhicule et a percuté une voiture qui arrivait dans l' autre direction . -Pas plus tard que l' année dernière , Ellison a eu un vote négatif sur son paquet de rémunération . L' an dernier déjà , Ellison avait subi un vote négatif sur sa rémunération . - Johnson , de , Ga , a été retrouvé le 11 janvier devant un tapis rouge appuyé derrière les de son centre de formation secondaire . Johnson , de en Géorgie , a été retrouvé le 11 janvier coincé dans un tapis appuyé debout derrière les gradins du gymnase de son lycée . -Les médias du Kenya ont fait état de la colère des autorités à l' aide de caméras de surveillance de la sécurité des membres de l' armée qui ont été envoyées sur les lieux de l' attentat qui aurait prétendument volé le centre commercial . Les médias kenyans ont suscité la colère des autorités en diffusant des images de caméras de vidéosurveillance sur lesquelles les troupes déployées sur la scène de l' attaque soi-disant le centre commercial de luxe . -Les législateurs kényans ont été la cible de la colère du public dans le passé . Les législateurs kenyans ont été la cible de la colère de la population dans le passé . -La presse kenyane à un procès controversé de la loi sur les médias La presse kenyane s' indigne de la loi controversée sur les médias -Les enfants peuvent dire : " Ce qui me plaît , c' est de parler de ma langue " , " Ma langue me semble qu' il y a des poils " , ou " ma langue est " . Les enfants peuvent dire , « J' ai l' impression que quelque chose me pique la langue » , « J' ai l' impression d' avoir un cheveu sur la langue » ou « J' ai des sur la langue » . -Le groupe chargé des crimes graves et de la criminalité organisée a mené l' enquête à l' encontre de M. , qui l' a également reconnu coupable de trois chefs d' accusation de possession de cocaïne et qui a pratiquement le mode de justice , preuve de notre engagement à traduire les criminels en justice . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; L' unité chargée de la criminalité grave et organisée du a mené l' enquête sur M. , qui a été reconnu coupable des trois chefs d' accusation de possession de cocaïne et maintenant d' entrave à la justice , témoignant de son engagement à poursuivre les criminels en justice . -Le Caporal David Hawkins , un réserviste de London ( Ontario ) , a été diagnostiqué d' un trouble de stress post-traumatique et a été libéré de l' armée , bien qu' il ait demandé à être maintenu pendant une autre année pour obtenir une pension . Le caporal David Hawkins , un réserviste de London , en Ontario , atteint du syndrome de stress post-traumatique , a été libéré de l' armée malgré sa demande de rester un an de plus afin de pouvoir recevoir une retraite pleinement indexée . -Série de véhicules Land Rover annonce Land Rover annonce son parrainage d' une série de rallyes -Le mois dernier , le Conseil national de la sécurité des transports , Mark , un expert de la sécurité nationale de l' alimentation , a déclaré que les téléphones cellulaires sont toujours considérés comme un risque . Le mois dernier , Mark du National Transportation Safety Board , expert national en sécurité des transports sur CBS News , a déclaré que les téléphones portables étaient toujours considérés comme présentant un risque . -Le mois dernier , des personnes proches de la situation ont déclaré que la avait transféré les dossiers des courriers électroniques et des messages instantanés à l' Autorité de régulation britannique , l' Autorité de gestion des marchés , envoyée à un ancien courtier . Le mois dernier , des sources proches du dossier ont déclaré que avait transmis des dossiers d' emails et de messages instantanés , envoyés à et par un ancien trader , au régulateur britannique , la Financial Conduct Authority . -La semaine dernière , Kigali a évoqué la possibilité de représailles militaires après que les obus aient atterri sur le territoire rwandais . La semaine dernière , Kigali avait évoqué la possibilité de représailles militaires à la suite des obus tombés en territoire rwandais . -L' an dernier , les festivités de l' Halloween sur la côte Est des États-Unis ont été annulées parce que la chute de Sandy était en cours . L' an dernier , les festivités d' Halloween sur la côte est des Etats-Unis avaient été annulées en raison du passage de l' ouragan Sandy . -L' an dernier , une équipe de l' Université de Lincoln a découvert que les chiens leur tête à la gauche lorsqu' ils regardaient un chien agressif et à droite lorsqu' ils regardaient un chien heureux . L' année dernière , une équipe de l' université de Lincoln a découvert que les chiens tournaient la tête vers la gauche lorsqu' ils regardaient un chien agressif et vers la droite lorsqu' il s' agissait d' un chien joyeux . -Laura , présidente de l' Association of Professional , s' est félicitée des changements . Laura , présidente de l' Association of Professional Flight , s' est dite heureuse de ces changements . -Les dirigeants des ont déclaré qu' ils les villes sous pression diplomatique , et Bertrand , chef politique de la Coalition , a déclaré que ces reculs militaires ne pas les exigences des pourparlers de paix en aucune manière . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Des responsables du ont expliqué avoir évacué les localités sous la pression diplomatique et Bertrand , responsable politique de la rébellion , a affirmé sur RFI que ces revers militaires ne modifieraient en rien ses revendications aux pourparlers de paix . - sa voix le 18 juin 2005 , lui a dit : " J' ai une histoire que nous avons l' intention de courir demain que je voudrais vraiment parler à Charles " . Dans un message vocal qu' il lui a laissé le 18 juin 2005 , disait : « J' ai une histoire que nous prévoyons de publier demain dont j' aimerais vraiment parler à Charles . » -Leonardo va prendre son souffle dans son rôle de trader fou . Leonardo va vous dans son rôle de trader . -N' oublions pas que Brigitte a été élue par votes , soit 0,5 % des habitants de Bruxelles " . " Pour rappel , Brigitte n' a été élue qu' avec 2 245 voix soit 0,5 % des ! " , conclut M. De . -Que cette affaire serve d' avertissement aux criminels que personne n' est hors de la portée de la loi . Que cette affaire serve d' avertissement aux criminels pour qu' ils sachent que personne n' est au-dessus de la loi . -Peine de mort pour Prison à vie pour un ancien chinois -Comme Davis , qui refuse de transiger sur sa musique , la pop star dit que , dans une carrière , " la chose la plus importante consiste à éviter d' être prise au piège par des choses qui nous empêchent de nous exprimer " . Comme Davis qui refuse tout compromis sur sa musique , la star pop explique que dans une carrière , " le plus important c' est de ne pas se laisser par tout ce qui peut nous empêcher de nous exprimer " . -De même , deux espèces très semblables à la morphologie se distinguent par leur génétique . De la même manière , deux espèces très proches ont été distinguées grâce à la génétique . -M. , qui avait déjà été accusé de trafic de drogue , était souvent en contact avec le maire . Alexander , précédemment accusé de trafic de drogues , était en contact fréquent avec le maire . -Vivre ensemble en France est le défi qui se pose à la Commission ( Marguerite School Board ) . Vivre ensemble en français , le défi de la commission scolaire -La chaîne locale ABC a révélé qu' une personne était évacuée sur une civière et une deuxième dans un fauteuil roulant . L' antenne locale d' ABC montrait une personne évacuée sur un , et une deuxième transportée sur un fauteuil roulant . -Aéroport de Los Angeles L' aéroport de Los Angeles évacué après une fusillade -Lou Reed Masques à l' occasion de la Journée du Tai Chi Lou Reed est mort pendant une séance de chi -Lou Reed avait subi une greffe de foie en mai dernier . Lou Reed avait subi en mai dernier une greffe du foie . -Lou Reed était un maître de l' art chinois martial connu sous le nom de chi . Lou Reed était maître chi , un art martial chinois . -Lou est un prince et un combattant , et je sais que ses chansons sur la douleur et la beauté dans le monde de nombreuses personnes avec la joie de vivre incroyable qu' il ressent " , a-t-elle ajouté . " Lou était un prince et un combattant , et je sais que ses chansons sur la douleur et la beauté du monde transmettront à de nombreuses personnes la formidable joie de vivre qu' il avait " , ajoute-t-elle . - pour nous , le " bébé dans une tendance de la citrouille " a commencé seulement l' année dernière grâce à , de sorte que la plupart d' entre nous avons grandi de ces bandes qui ne se sont pas assis à eux . Heureusement pour nous , la « tendance du » n' a commencé que l' année dernière grâce à , donc la plupart d' entre nous a sculpté ces et n' a pas eu à s' asseoir dedans . -Le vainqueur vainqueur , Jake , a combiné les couleurs de la marque automobile et du drapeau allemand , tout en intégrant les notions de vitesse et de chaleur sur la piste . L' heureux élu , Jake , a combiné les couleurs de la marque automobile et du drapeau allemand , tout en intégrant les notions de vitesse et de chaleur au niveau de la piste . -Ne vous y trompez pas , Ne nous trompons surtout pas . -La Malaisie a dépassé 80 % du PIB , contre 60 % en 2008 . La Malaisie arrive en tête de liste avec 80 % du PIB , alors que ce taux s' élevait à 60 % en 2008 . -Man of Il se rend en uniforme nazi au supermarché , avant d' en être chassé -Le directeur général , Gordon Turner , a déclaré que le centre de ventes de Toronto avait fait un bénéfice de 8 % au cours de la première moitié et a amorcé la deuxième moitié de son activité en particulier en ce qui concerne les voyages non commerciaux en Australie et au Royaume-Uni . Le directeur général , Graham Turner , a déclaré que Flight Centre avait réalisé 8 % de bénéfices au premier semestre et que le second semestre avait démarré en force , en particulier en ce qui concerne les voyages autres que les voyages d' affaires en Australie et au Royaume-Uni . -De nombreux dirigeants allemands soutiennent que les derniers rapports sont simplement la confirmation de ce qu' ils savent déjà : les États puissants veulent voler leurs secrets les plus chers et ces données doivent donc être gardées à tout prix . De nombreux dirigeants allemands indiquent que les derniers rapports ne sont qu' une confirmation de ce qu' ils savaient déjà , à savoir que les états puissants veulent voler leurs secrets les plus précieux et que ces données doivent par conséquent être gardées à tout prix . -De nombreux ennemis ont été tués ou blessés durant l' opération . Plusieurs ennemis ont été tués et blessés au cours de l' opération . -Beaucoup de gens de Wall Street considèrent les frais de port comme un signe que les compagnies aériennes facturent suffisamment d' argent pour couvrir les frais de transport aérien après des années de pertes . Bon nombre de personnes à Wall Street considèrent l' ajout de frais de bagages comme un signe qui prouve que les compagnies aériennes font payer assez cher pour couvrir le coût du voyage aérien après des années de pertes . -De nombreuses personnes sont traitées pour le paludisme , la tuberculose , la malnutrition , le diabète , les parasites et les maladies sexuellement transmissibles . De nombreuses personnes reçoivent un traitement contre le paludisme , la tuberculose , la malnutrition , le diabète , les parasites et les maladies sexuellement transmissibles . -Beaucoup de gens ont essayé " La Nouvelle Star " , et certains ont même réussi . Ils sont nombreux à avoir tenté la Nouvelle Star , et parfois même à être allés jusqu' au bout . -Les mammifères marins sont situés au sommet de la chaîne alimentaire . Les mammifères marins sont situés en haut de la chaîne alimentaire . -Martin a publié des extraits de son prochain film dans lequel le titre de rôle , un ambitieux de New York , est joué par Leonardo . Martin laisse échapper quelques images de son prochain film dont le , celui d' un courtier new-yorkais ambitieux , est incarné par Leonardo . -Martin nous raconte une histoire sincère à propos d' un " courtier de l' Est " qui refuse de collaborer avec les autorités au sujet d' un système de corruption massive sur Wall Street . Martin nous relate l' histoire vraie d' un " courtier en Bourse de Long Island , qui refuse de collaborer avec les autorités dans le cadre d' une vaste affaire de corruption à Wall Street " . -Martin a ajouté que " Double Down " ne répond pas définitivement à la question de savoir si l' analyse politique a atteint le bureau d' Obama . Martin a ajouté que « Double Down » ne répondait pas de manière définitive à la question de savoir si les sondages politiques étaient arrivés sur le bureau d' Obama . -Martin a dit à CNN qu' il avait demandé à M. si son connaissait le . Martin a dit sur CNN qu' il avait demandé à si son patron d' alors était au courant du remaniement éventuel . -Entre-temps , le personnel des FARDC a été remanié et l' armée s' est lancée dans l' offensive contre les , changeant le cours de la guerre . De son côté , l' état-major des FARDC a été remanié et l' armée est passée à l' offensive contre le , faisant changer le cours de la guerre . -Dans le même temps , M. Kerry devrait être présent ce week-end au Moyen-Orient et en Pologne pour aborder la question des stratégies américaines en Syrie , en Égypte et en Iran , ainsi que les activités de surveillance américaines . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Entre-temps , M. Kerry a prévu de se rendre ce week-end au Moyen-Orient et en Pologne pour gérer la engendrée par les stratégies américaines en Syrie , en Égypte et en Iran , ainsi que par les activités de surveillance des États-Unis . -Dans le même temps , les législateurs américains vont venir en aide à l' Europe pour s' attaquer à des problèmes d' espionnage américains et convaincre les Européens de la nécessité de poursuivre les efforts conjoints en matière de lutte contre le terrorisme avec les États-Unis , le président d' un sous-comité du Sénat sur les affaires européennes , jeudi . Entre-temps , les législateurs américains se rendront en Europe pour répondre aux préoccupations concernant l' affaire d ’ espionnage américain présumé et convaincre les Européens de la nécessité de poursuivre les efforts conjoints de lutte contre le terrorisme avec les États-Unis , a expliqué jeudi le président du sous-comité du Sénat sur les affaires européennes . -Dans le même temps , un demandeur d' asile sri-lankais âgé de 35 ans est mort d' une attaque cardiaque imminente après avoir été amenée sur un bateau d' asile à l' île Christmas cette semaine . Entre-temps , un demandeur d' asile sri-lankais de 35 ans serait mort d' une crise cardiaque après être arrivé sur un bateau transportant des demandeurs d' asile à l' île Christmas cette semaine . -Pendant ce temps , l' ancien reporter principal Neville et ancien journaliste James a supervisé la surveillance des mouvements de Mme . Entre-temps , l' ancien reporter en chef Neville et l' ancien reporter James ont supervisé la surveillance des moindres faits et gestes de Mlle . -Entre-temps , quatre projets d' hôtel d' une valeur totale de près de 600 chambres seront mis en oeuvre dans les deux prochaines années . Entre-temps , quatre projets hôteliers totalisant près de 600 chambres verront le jour d' ici deux ans . -Entre-temps , le député libéral de Gatineau , Regina , pense que le concept de fin de vie doit être clarifié avant que le projet de loi puisse être adopté . De son côté , la députée libérale de Gatineau , Stéphanie Vallée , affirme que la notion de fin de vie devra être clarifiée avant que le projet de loi ne soit adopté . - , membre du Bureau des politiques du Parti communiste chinois ( PCC ) , a fait la déclaration à Tachkent lors d' une visite officielle en Ouzbékistan . , qui est membre du Bureau politique du Parti communiste chinois ( PCC ) , s' exprimait à Tachkent lors d' une visite officielle en Ouzbékistan . - , membre du chargé des questions de sécurité intérieure , a accusé le Mouvement islamique du Turkestan oriental d' être l' instigateur de l' attaque . , membre du Bureau politique en charge des questions de sécurité intérieure , a accusé le mouvement islamique du Turkestan oriental d' être l' instigateur de cette attaque . -Menton réduit le coût de la tempête de Noël Menton réduit le coût de ses de Noël -Michel , de l' Institut du Nouveau Monde ( Institut Nouveau Monde ) a identifié un certain nombre de défis pour le conseil scolaire . Michel , de l' Institut du Nouveau Monde , a identifié plusieurs défis pour la commission scolaire . -Mick dit qu' il n' a jamais touché Perry à 18 ans . Mick dit qu' il n' a jamais Perry lorsqu' elle avait 18 ans . -Mike Warren , maire de Pitcairn , estime que la création d' une telle zone protégée serait le premier pas vers une plus grande autonomie financière pour l' île . Mike Warren , le maire de Pitcairn , estime que la création d' une telle zone protégée serait un premier pas vers plus d' autonomie financière pour son île . -Le général Hussein , commandant de la branche militaire , était présent . Le maréchal Hussein a assisté à la cérémonie . -Selon M. Miller , le lien direct avec la Bulgarie , membre de l' Union européenne , signifie que les risques géopolitiques associés aux pays de transit sont éliminés " à jamais " . Miller a ajouté que la jonction directe vers la Bulgarie , membre de l' Union européenne , signifie que les risques géopolitiques associés aux pays de transit sont éliminés « pour toujours » . -Des millions de dollars pourraient être sauvés grâce à l' introduction d' un système fiscal plus respectueux des contribuables . Des millions de dollars pourraient être épargnés afin de permettre une taxation beaucoup respectueuse de ceux qui paient . -Accusé de pornographie juvénile en Nouvelle-Écosse Pasteur accusé de pornographie juvénile en Nouvelle-Écosse -Mlle Hall , de Halifax , dans le West Yorkshire , a dit : " Regardez le sauvetage est terrifiant " . Mlle Hall , d' Halifax dans le West Yorkshire , a déclaré : « J' étais en observant les secours . » - Romney a présenté une alternative confuse et peu qui ne semble pas mettre fin à ses politiques ou à ses positions . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Romney présentait une alternative confuse et peu raffinée qui ne semblait pas permettre de déterminer ses politiques ou ses positions . -Mohamed Morsi prête serment , mais son jour de triomphe n' augure pas de la fin des troubles politiques en Égypte . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Mohamed Morsi a prêté le serment d' investiture , mais il est peu probable que son jour de triomphe marque la fin des conflits politiques en Égypte . -L' argent recueilli de force par les contribuables réduit leur revenu disponible et contribue à leur appauvrissement . Tout argent enlevé de force aux contribuables diminue leur liberté de consommation et contribue à leur appauvrissement . -Bombardier Bombardier n' a pas non plus publié de données d' essais de vol pour ses appareils neufs ou il offre une mise à jour sur la question de savoir si l' appareil atteindra son calendrier ambitieux de se rendre dans le secteur des services commerciaux d' ici septembre prochain . Bombardier , basé à Montréal , n' a pas non plus fourni de données d' essai en vol pour son tout nouvel avion ni d' informations permettant de savoir si l' avion respectera son calendrier ambitieux de mise en service commercial avant septembre prochain . -Moore ajoute que s' il découvre suffisamment de preuves pour justifier l' ouverture d' une enquête pénale ou civile sur la mort de Johnson , il demandera au FBI de le faire . Moore a ajouté que s' il met au jour des éléments de nature à justifier une enquête civile ou pénale sur le décès de Johnson , il demandera au FBI de la mener . -Moore a déclaré lors d' une conférence de presse jeudi après-midi . Moore s' est exprimé lors d' une conférence de presse jeudi après-midi . -D' après Moore , les auteurs de l' autopsie ont déclaré Johnson être morts suite à une " asphyxie " . Moore a déclaré aux journalistes que la première autopsie indiquait que Johnson était décédé par « asphyxie » . -De plus en plus de Français choisissent la crémation pour leurs propres , contre % contre 47 % , selon un sondage effectué les 6 et 7 septembre pour 1 personnes . Pour leurs propres obsèques , de plus en plus de Français choisissent la crémation , plutôt que l' inhumation : 53 % , selon une étude réalisée les 6 et 7 septembre auprès de 1009 personnes , au lieu de 47 % . -Davantage d' argent est consacré à la recherche de la vie sur d' autres planètes que la recherche marine , et il est grand temps que les choses changent . On dépense plus d' argent dans la recherche de vie sur les autres planètes que dans la recherche marine et il est grand temps que cela change . -Plus d' une trentaine de pompiers s' à 9 rue Hébert . Plus d' une trentaine de pompiers ont été dépêchés au 9 de la rue Hébert . -Plus de trois heures à l' audience , a répété à maintes reprises pourquoi l ' " architecte de la Loi sur les soins à domicile " n' a pas volontairement acheté l' assurance gouvernementale pour acheter un plan par le biais de , qu' elle utilise maintenant pour des millions d' Américains . Pendant plus de trois heures , Billy Long n' a cessé de demander avec insistance à Mme pourquoi « l' architecte » de l' Care Act n' avait pas volontairement renoncé à l' assurance parrainée par le gouvernement pour souscrire un régime via , qu' elle vante désormais à des millions d' Américains . -En outre , David Bowie a présenté cette par une vidéo . David Bowie a d' ailleurs fait les présentations de ce beau coffret dans une vidéo . -En outre , je pense que l' obstruction partisane a trop des efforts pour pousser notre nation à l' avenir : réforme de l' immigration , option publique pour les soins de santé , et fermeture de la base de Guantanamo Bay , entre autres . De plus , je crois que l' partisan a détruit de trop nombreux efforts visant à faire avancer notre nation dans le domaine des réformes de l' immigration , des soins de santé publics et de la fermeture de Guantanamo , entre autres choses . -En outre , Kiev a besoin d' argent liquide pour payer ses importations de gaz en Russie , ce qui l' accuse de ne pas avoir payé un billet de 882 millions de dollars . Kiev a besoin par ailleurs de liquidités pour régler ses importations de gaz à la Russie , qui l' accuse de ne pas avoir payé une facture de 882 millions de dollars . -De plus , l' acte reconnaît que la Congrégation de la sainte et de l' Oratoire Saint-Joseph , qui est un " acte isolé " qui permet aux agresseurs sexuels d' avoir des enfants , a " exercé des pressions morales , psychologiques et religieuses sur les victimes " , et a déclaré " qu' ils ont été victimes d' agressions sexuelles , qu' ils restent silencieux " et " qu' ils ont délibérément et consciemment décidé d' ignorer le problème " . Le recours souligne par ailleurs que la congrégation de Sainte-Croix et l' oratoire Saint-Joseph , qui est une entité distincte , " ont permis que des sévices sexuels soient perpétrés à l' encontre d' enfants " , qu' ils " ont exercé une contrainte morale , religieuse et psychologique sur les victimes " , qu' ils " étaient au courant des sévices sexuels perpétrés et les ont néanmoins " , et qu' ils ont " sciemment et consciemment choisi d' ignorer la problématique " . -De plus , il est possible que ces avancées en génétique et en biologie moléculaire remettent en question la classification ou l' appartenance d' une espèce à un genre particulier , à la famille ou même à l' ordre . Il n' est d' ailleurs pas impossible que ces progrès en génétique et en biologie moléculaire remettent en cause la classification ou l' appartenance d' une espèce à un genre , une famille , voire même un ordre . -Le discours de Morsi sur la place Tahrir a été rempli de gestes populistes odieux . Le discours de M. Morsi sur la place Tahrir s' est accompagné de gestes populistes spectaculaires . -Morsi s' est par la suite rendu à l' Université du Caire où il a prononcé son discours d' investiture . Plus tard , M. Morsi s' est rendu à l' université du Caire où il a prononcé son discours d' investiture . -Morsi devrait essayer pour les massacres de , les massacres de et le massacre de la Garde républicaine . Morsi doit juger pour les massacres de , les massacres de , et le massacre de la Garde . -Morsi a prêté un serment symbolique à la place de la place Tahrir , la semaine du carnaval , qui a mis fin au régime autoritaire de l' OLP l' année dernière , et a promis de récupérer les pouvoirs présidentiels démis de ses fonctions par le conseil militaire qui avait pris le pouvoir du dirigeant renversé . & # 160 ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; M. Morsi a fait un serment symbolique vendredi sur la place Tahrir , d' où est parti le soulèvement qui a mis fin au régime autoritaire de Moubarak l' année dernière , et a juré de récupérer les pouvoirs présidentiels dont le conseil militaire , qui a remplacé l' ancien président évincé , l' a . -La plupart n' ont cependant pas touché les frais de bagages de cabine . Pourtant bon nombre d' entre elles n' ont pas touché aux frais de bagages . -La plupart des nouveaux avions de ligne et d' autres avions ont été modifiés de sorte que les passagers puissent utiliser Wifi à des altitudes supérieures doivent satisfaire aux critères . La plupart des nouveaux avions de ligne et des autres avions qui ont été modifiés afin que les passagers puissent utiliser le Wi-Fi à des altitudes plus élevées , devraient satisfaire aux critères . -La plupart des escarmouches ont eu lieu dans la province de Sofala , qui est située à plusieurs centaines de kilomètres au nord de Maputo , mais alimente Beira , le port que des mineurs , y compris Rio Verde et Vale , utilisent pour exporter du charbon . La plupart des accrochages ont eu lieu dans la province de Sofala , qui est située à plusieurs centaines de kilomètres au nord de Maputo , mais où se trouve Beira , le port d' où les sociétés minières , notamment Rio Tinto et Vale , exportent le charbon . -La plupart des gens seront en mesure d' obtenir des plans de soins de meilleure qualité pour le même prix , voire moins bien que prévu . La plupart des gens pourront obtenir des régimes de santé complets et mieux adaptés pour le même prix , voire pour encore moins cher que prévu . - aux vétérans de la guerre à Menton hommage rendu aux anciens combattants à Menton -Les préoccupations en matière de sécurité au Mozambique sont vives en raison de la présence de puissants affrontements L' inquiétude liée à la sécurité au Mozambique grandit en même temps que des conflits éclatent entre des personnalités puissantes -M. , député du parti écologiste allemand , a publié une photo de M. sur son fil Twitter . M. , député Vert allemand , a publié une photo de lui avec M. sur son compte Twitter . -M. , de , a ajouté : " Nous avions peur qu' elle ne tombe en panne , mais elle y est restée " . M. , de , a ajouté : « On a eu peur qu' elle tombe mais elle n' a pas bougé . » -M. , qui était ministre des ports de février 2006 à novembre 2009 , a été initialement en faveur des appels d' offres publics . M. , qui a été ministre des Ports de février 2006 à novembre 2009 , était au départ favorable aux appels d' offres . -M. a trahi cette confiance et tenté de se cacher derrière le vernis de sa profession . M. a trahi cette confiance et tenté de s' abriter derrière le vernis de sa profession . -M. espère être à samedi . M. prévoit de se rendre samedi a . -M. est accusé d' abuser de sa fonction de procureur criminel , qui s' est déjà impliqué dans des clients spécifiques . M. est accusé d' avoir abusé de sa position en tant qu' avocat , et de s' être trop impliqué auprès de clients particuliers . -Selon M. , l' implication de dans l' histoire a suivi le même modèle que les autres grands hommes , comme l' ancien secrétaire d' origine , David . M. a expliqué que l' implication de dans l' histoire a suivi le même schéma qu' avec d' autres hommes importants , tels que l' ancien ministre de l' Intérieur David . -M. a déclaré que , selon l' avocat de l' ancien fournisseur de la NSA , M. ne serait pas en mesure de revenir en Russie s' il quittait son pays . M. a déclaré que , selon l' avocat de l' ancien consultant de la NSA , M. ne pourrait pas retourner en Russie s' il quittait le pays . -M. dit que M. , qui doit se retirer lorsque son second mandat prendra fin l' année prochaine , veut détruire la démocratie du pays . M. a dit que M. – qui devrait se retirer à la fin de son second mandat l' année prochaine – voulait détruire la démocratie dans le pays . -M. a déclaré à l' AFP , l' agence de presse française , mercredi , que M. était considéré comme un " perdant " désireux d' utiliser " tout ce qui restait de ses forces pour essayer de prouver qu' il pourrait imposer au gouvernement ses propres décisions " . M. a déclaré mercredi à l' AFP , l' agence de presse française , que M. se considérait comme un « perdant » qui voulait utiliser « toutes les forces qu' il lui restait pour tenter de prouver qu' il pouvait imposer ses décisions au gouvernement » . -M. a été interrogé sur la question de savoir si cette menace . M. était faussement timide lorsqu' on lui a demandé si la cette menace . -M. ne pas sa profession , mais l' . M. n' a pas fait honneur à sa profession , mais l' a . -M. Brown a déclaré : " Le rail de grande vitesse peut apporter des avantages économiques considérables pour l' Écosse , mais il ajoute aussi le poids économique de l' Écosse à l' ensemble de la réglementation pour les trains à grande vitesse dans toute la Grande-Bretagne " . M. Brown a déclaré : « Le train à grande vitesse a le potentiel d' offrir des avantages économiques considérables à l' Écosse , mais il apporte également le poids économique de l' Écosse dans le projet global de réseau ferroviaire à grande vitesse dans toute la Grande-Bretagne . -M. Chen a écrit plusieurs articles pour le New Express en alléguant des irrégularités financières dans une société dénommée . M. Chen a écrit plusieurs articles pour le New Express sur les irrégularités financières présumées d' un fabricant d' engins de chantier appelé . -M. Cook ajoute : " Compte tenu de l' augmentation des frais de timbre dans les maisons à forte valeur et de l' introduction de la législation modifiée , il est très difficile d' affirmer que les biens à valeur élevée sont , sans égard à l' effet du régime fiscal des conseils de bande " . M. Cook a déclaré : « Suite à l' augmentation du droit de timbre sur les propriétés de grande valeur et l' introduction de la loi en la matière , il est très difficile d' avancer que les propriétés de grande valeur sont indépendamment des effets du système de taxe d' habitation obsolète . » -M. Dutton a demandé à Mme de veiller à ce qu' aucun dollar du budget de sauvetage ne soit affecté à une bureaucratie supplémentaire . M. Dutton a rendu visite à Mme pour garantir qu' aucun dollar du plan de sauvetage ne sera dépensé en bureaucratie supplémentaire . -M. Hare , qui a commencé avec le YMCA lorsqu' il a 21 ans , a accepté la gestion " de moi " parce qu' il n' a pas réussi à recruter du Seigneur et a fait en sorte que le personnel soit certain de ses obligations de signalement des manquements à la sécurité de l' État . M. Hare , qui a commencé à travailler au YMCA à l' âge de 21 ans , a admis que la gestion du personnel a été un échec avec le recrutement de Lord et qu' il avait omis de vérifier que les employés savaient qu' ils étaient tenus de signaler toute violation de la politique de sécurité des enfants . -M. Hare a indiqué qu' il avait dit à l' assemblée du YMCA de Calgary que la formule utilisée par l' organisation " Fox Frank " était " sans rapport avec le signalement " par le personnel et que le conseil était d' accord avec lui . M. Hare a déclaré qu' il avait exprimé son point de vue devant le conseil du YMCA de Nouvelle-Galles du Sud disant que , pour l' organisation , la leçon tirée de « l' incident Jonathan Lord » ne devait pas concerner « le signalement » par le personnel , et le conseil était d' accord avec lui . -Selon M. Hare , la décision de mettre le personnel à signer des accords de confidentialité a peu de temps après que les allégations ont été soulevées par le directeur général des services jeunesse du YMCA , Liam . M. Hare a dit que la décision de faire signer au personnel des accords de confidentialité , peu après que les allégations ont été évoquées , a été prise par le directeur général du YMCA chargé des services à l' enfance , Liam . -M. Hare a dit à Gail , conseiller auprès de la Commission , qu' il connaissait le premier cas mais qu' il ne connaissait pas le second . M. Hare a déclaré à Gail , avocat-conseil auprès de la Commission , qu' il avait eu connaissance de la première affaire mais qu' il n' était pas au courant de la seconde . -M. Hughes a déclaré que aurait dû partager le même sort que trois autres membres de la cellule terroriste responsable du carnage - , et Imam - qui a été exécuté il y a quatre ans . M. Hugues a déclaré que aurait dû connaître le même sort que les trois autres membres de la cellule terroriste de responsable du carnage – , et Imam – qui ont été exécutés il y a 4 ans . -M. Kerry insiste toutefois sur le fait que la NSA est une force de bonne volonté et que ses opérations de surveillance ont sauvé de nombreuses vies . M. Kerry a insisté , toutefois , sur le fait que la NSA était une force agissant pour le bien et que ses opérations de surveillance avaient sauvé de nombreuses vies . -M. Palmer , 59 ans , a déclaré que ses politiques comprenaient un aéroport international pour la Sunshine Coast et qu' il prendrait " très au sérieux " son nouvel emploi . M. Palmer , 59 ans , a déclaré que son projet politique comprenait un aéroport international pour la Sunshine Coast et qu' il prendrait ses nouvelles fonctions « très au sérieux » . -M. Palmer a également critiqué l' utilisation de crayons pour marquer les votes . M. Palmer a aussi critiqué l' utilisation de crayons de papier pour marquer les bulletins de vote . -M. Palmer a demandé une révision du décompte des voix afin d' accélérer le processus . M. Palmer a avancé l' idée d' un remaniement du système de dépouille des suffrages pour accélérer le processus . -M. a dit dans sa lettre qu' il avait été confronté à une " campagne de persécution " sérieuse et soutenue qui l' a forcé de quitter son domicile . M. a écrit dans sa lettre qu' il avait fait l' objet d' une campagne de persécution « grave et soutenue » qui l' avait forcé à quitter son pays . -Le groupe de message instantané de M. était composé de banquiers à Barclays et à Citigroup , des personnes proches de la réalité . Le dossier de M. comprenait des messages instantanés envoyés à et par des banquiers travaillant chez Barclays et Citigroup , ont ajouté des sources proches du dossier . -M. a déclaré aujourd ' hui que les 325 millions de dollars à peu près " à moins qu' il ne soit suivi d' une réforme réelle visant à mettre le système de santé publique de la Tasmanie sur une base plus durable " . M. a dit aujourd' hui que les 325 M $ ne compteront pas pour grand-chose « à moins qu' ils soient suivis d' une réforme véritable permettant de consolider le système de santé public de Tasmanie » . -M. a déclaré que vous étiez prêt à garantir ces 333 emplois pendant quatre ans . M. a dit que vous étiez prêt à garantir ces 333 emplois durant quatre ans . -M. est un charmant jeune homme qui tente de sauver certains des emplois industriels les plus rémunérateurs . M. , est un charmant jeune homme qui tente de sauver des emplois industriels parmi les mieux payés . -M. Hawkins a souligné qu' un soldat pouvait être préparé pour son départ , avec des séances de planification et de consultation , mais qu' il était tout à fait différent de vouloir quitter l' armée . M. Hawkins a souligné qu' un soldat pouvait être préparé à partir , avec des plans et des séances de consultation , mais que cela était totalement différent que le fait de vouloir quitter les rangs . -Mme , qui est également directrice du Home de Gatineau , pense que le service sera sans aucun doute utile . Mme , aussi directrice de la maison à Gatineau , estime que le service serait de toute façon inutile . -Mme Franklin a été occupée en novembre pour un album de Clive Mac et de Sony Music , produite par Don et Kenny " " . Mme Franklin s' affaire en novembre à l' enregistrement d' un album pour Clive Davis et Sony Music , produit par Don Was et Kenny " " . -Mme a dit : " Tout le monde est électrique et tout le monde a et pris des applaudissements " . Mme a déclaré : « Il y avait de l' électricité dans l' air et tout le monde l' encourageait et l' . » -Muller n' a pas survécu à la guerre . Müller n' a pas survécu à la fin de la guerre . -Mes camarades de classe ont ri avec joie , et mes parents ont sauvé tous les journaux qu' ils pouvaient trouver . Mes camarades ont pleuré de joie et mes parents ont conservé tous les journaux qu' ils ont trouvés . -Ma mère est décédée il y a dix-neuf ans . Ma mère est décédée il y a dix-neuf ans maintenant . -Ma mère a pris possession de son visage , et chaque matin , elle me regarde dans le miroir . Ma mère a pris possession de mon visage et elle me regarde tous les matins dans mon miroir . -Mon objectif est de découvrir la vérité . Mon objectif est de découvrir la vérité . -Mes parents ne sont plus là , mais je me sens plus proche de moi . Mes parents ne sont plus mais je les sens constamment près de moi . -Ma soeur et moi avons été autorisés à manger des bonbons , à rester en retard et à jouer pour le quartier . Ma sœur et moi étions autorisés à manger des bonbons , à aller nous coucher tard et à nous déguiser pour aller dans le voisinage . -Ma femme et la mère de mes trois enfants sont morts du cancer à 43 ans . Ma femme et la mère de mes 3 enfants est décédée d' un cancer à 43 ans . -NEC offre à la police judiciaire un sac portable d' une valeur de 90.000 euros qui peut analyser des échantillons d' ADN sur une scène du crime en moins d' une heure . NEC propose aux services de police judiciaire une valise coûtant 90.000 euros pour analyser des échantillons d' ADN sur le lieu d' un crime et en une heure . -NEC propose donc la reconnaissance VIP à l' entrée d' un hôtel ou d' un magasin . NEC propose ainsi la reconnaissance de VIP à l' entrée d' un hôtel ou d' un magasin . -La NHTSA n' a pu examiner la lettre de déclaration des propriétaires en raison de la fermeture de 16 mois du gouvernement , ce qui a provoqué une baisse de la croissance des ventes d' automobiles en octobre . La NHTSA n' a pas pu examiner la lettre d' information aux propriétaires en raison de l' arrêt de 16 jours des activités gouvernementales , ce qui a ralenti la croissance des ventes de véhicules en octobre . -NSA " Internal error " , for crash Internet La NSA met en cause une « erreur interne » et non des pirates informatiques pour la panne de son site - : " Les États-Unis sont allés trop loin " , a déclaré Kerry de la NSA : les Etats-Unis sont " allés trop loin " , admet Kerry -NSA Événements sans fond de débats sur les renseignements L' affaire NSA souligne l' absence totale de débat sur le renseignement -Des révélations de sécurité renforcent la paranoïa des entreprises en ce qui concerne la surveillance de l' État Les révélations de la NSA accroissent la paranoïa des entreprises à l' égard de la surveillance d' État -Les noms d' autres législateurs participants devaient être publiés dans les prochains jours . Les noms des autres législateurs participants devraient être dévoilés dans les jours à venir . - P. Morris est un étudiant de deuxième année à la Harvard Medical School . P. Morris est étudiant en 2e année de médecine à la Faculté de médecine de Harvard . -Près de 100 migrants africains qui espèrent voyager en Algérie meurent de soif après que leurs deux camions se retrouvent au milieu du Sahara . Presque 100 migrants africains espérant se rendre en Algérie sont morts de soif après que leurs deux camions sont tombés en au milieu du Sahara . -Nécessité d' en faire encore plus Besoins toujours croissants -Ni l' un ni l' autre ne sont de bons négociateurs , et aucun d' entre eux n' est prêt à faire le genre de concessions nécessaires . Aucun d' eux ne sait bien négocier et aucun d' eux n' est prêt à faire les concessions nécessaires . -Les commandes nettes sont tombées à 26 avions , à partir de 83 . Les commandes nettes sont tombées à 26 avions , contre 83 auparavant . -Le Nevada a déjà achevé un projet pilote . Le Nevada a déjà mené à terme un projet pilote . -Le Nevada fait partie de plusieurs états qui se aujourd' hui pour trouver des technologies abordables qui permettraient à l' état de surveiller le nombre de kilomètres parcourus par une automobile , mais qui ne sont pas exactement là où et à quel moment . Le Nevada fait partie des quelques États qui se désormais pour trouver une technologie abordable qui permettrait à l' État de savoir combien de miles parcourt une voiture , mais sans connaître le lieu ni l' heure exacts . -Néanmoins , Cable a connu une croissance de 45 % , atteignant millions de CAD . Les revenus de Câble ont cependant progressé de 45 % pour atteindre millions . -Néanmoins , GM a fait état d' une augmentation d' environ 16 % des ventes le mois dernier avec véhicules , contre prévus par consensus . GM a néanmoins fait état d' une progression de près de 16 % de ses ventes le mois dernier à 226 402 véhicules , contre 211 563 attendues par le consensus . -Néanmoins , il espère que les consultations menées par le Conseil de la radiodiffusion et des télécommunications canadiennes ( CRTC ) donneront lieu à des recommandations intéressantes . Il espère cependant que les consultations menées par le Conseil de la radiodiffusion et des télécommunications canadiennes ( CRTC ) vont déboucher sur des recommandations intéressantes . -Néanmoins , on comprend vite que les objets disparates rassemblés dans ce livre très beau conduisent le " vendeur de biens d' occasion " à une passion constante , celle de découvrir le coeur qui incarne l' identité française . Mais on comprend vite que les objets disparates rassemblés dans ce livre au si beau titre ramènent le à une seule et même passion : celle de découvrir le noeud constitutif de l' identité française . -Néanmoins , le gouvernement n' a pas déclaré son intention de réformer son système de lutte contre la corruption , par exemple en créant un organe indépendant de la Partie . Mais le gouvernement n' a aucunement manifesté l' intention de réformer le système de lutte contre la corruption en créant par exemple un organisme indépendant du parti . -Le nouveau vaccin pourrait prendre le plaisir de fumer . Le nouveau vaccin pourrait supprimer le plaisir de fumer -Demande d' indemnisation présentée par une nouvelle classe contre la Croix de la Trinité Nouvelle demande de recours collectif contre les frères Sainte-Croix -La ville de New York en étudie un . La ville de New York en envisage un . -Le psychiatre de la ville de New York , Jack , qui se spécialise dans les questions d' identification des sexes , a déclaré que la nouvelle loi allemande " ressemble à une bonne chose " . Le Dr Jack , psychiatre à New York et spécialiste des questions d' identification sexuelle , a déclaré que la nouvelle loi allemande « semble être une bonne idée » . -Les nouveaux avions de ligne sont beaucoup plus tributaires des systèmes électriques que les générations précédentes d' aéronefs , mais ils sont aussi conçus et approuvés par la FAA pour résister aux interférences électroniques . Les nouveaux avions de ligne dépendent beaucoup plus des systèmes électriques que les générations précédentes d' avions , mais ils sont également conçus pour résister aux interférences électroniques et approuvés par la FAA . -Nouvelles : la Bourse de Tokyo enregistre % La Bourse de Tokyo finit en baisse de % , Actualités -Les nouvelles sur les montres ont les traders dans un secteur qui a été l' un des plus importants atouts des banques de portefeuille des activités commerciales au cours des dernières années , mais qui a été contestée cette année parce que la volatilité faible des devises réduit les possibilités pour les spéculateurs . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Les nouvelles concernant ces enquêtes ont ébranlé les traders dans un secteur qui a été l' un des plus gros moteurs de croissance des unités de trading des banques d' investissement au cours des dernières années , mais qui a été remis en cause cette année en raison de la faible volatilité des devises qui limite les opportunités pour les spéculateurs . -Selon certaines informations , la NSA a maintenu des millions de téléphones aériens en Europe , a entretenu des relations avec certains alliés américains , bien que le chef de l' agence ait déclaré cette semaine qu' ils étaient inexacts et qu' ils ne comprenaient pas une mauvaise compréhension des liens que les alliés de l' OTAN ont recueillis et auxquels ils ont communiqué avec les États-Unis . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; » La presse rapporte que la NSA a capté des millions de communications en Europe , ce qui a mis à mal les relations avec certains alliés des États-Unis , cependant le directeur de l' agence a déclaré cette semaine que c' était inexact et qu' il s' agissait d' un malentendu car cela concernait des métadonnées que les alliés de l' OTAN avaient collectées et partagées avec les États-Unis . -La de l' an prochain serait importante parce qu' elle sera la première depuis la nomination de Francis en mars de cette année , a déclaré . Le de l' année prochaine sera important car ce sera le premier depuis l' élection du pape François en mars de cette année , a dit . -Nick a gagné en importance sur une ( s " The League " ) et a maintenant son propre disque de théâtre public . Nick a fait son apparition sur le devant de la scène dans une sur une chaîne câblée ( « The League » sur le thème du fantasy football joyeusement de la chaîne ) et a désormais son propre spectacle de sur Central . -Neuf Ruby a échoué après avoir été victime d' un accident de la route dimanche après-midi et a survécu trois jours seulement avant d' être secouru d' une carrière mercredi . Ruby , un de 9 mois , s' est échappée après un accident de la route dimanche après-midi et a survécu pendant trois jours seule avant d' être sauvée dans une carrière mercredi . -Nissan , qui a été obligé de procéder à la révision , a également annoncé une restructuration de la direction . Nissan , qui a dû procéder à une telle révision , a également annoncé un remaniement de sa direction . -Aucune information classifiée n' est en danger , a déclaré le porte-parole . Aucune information classifiée n' est en danger , a expliqué le porte-parole . -Aucune contribution financière n' a été demandée avant les changements apportés au dernier budget . Avant les changements apportés au dernier budget , aucune contribution financière n' était réclamée . -Les nouveau-nés ne seront plus assignés de manière rigide à un homme ou à une femme . Le sexe masculin ou féminin des nouveaux nés ne leur sera plus attribué , de manière rigide , à la naissance . -Personne ne peut répondre aux questions sur la raison de cette situation . Personne ne peut répondre à la question de savoir pourquoi cela se produit . -Aucun des candidats ne semble vraiment avoir pris la première place . Aucun candidat ne semble avoir réellement marqué lors de ce premier prime . -Aucun de ces négociants n' a été accusé d' un quelconque acte répréhensible . Aucun de ces traders n' a fait l' objet d' aucune accusation . -Ce ne sont pas non plus les types de personnes qui utilisent le uniquement sur le plan financier . Les personnes qui se rendent chez les prêteurs sur gage ne sont pas non plus seulement ceux qui ont des problèmes financiers . -Il n' a pas non plus fait remarquer que les États membres de l' UE passent beaucoup de temps à plier leurs règles formelles si elles le veulent . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Personne non plus n' a dit que les États membres de l' UE consacrent une grande partie de leur temps à contourner leurs règles formelles lorsque ça les arrange . -Le Nord-Kivu est l' une des zones les plus densément peuplées de la RDC , et ses terres regorgent de richesses minières bien connues . Le Nord-Kivu est l' une des régions les plus densément peuplées de la RDC , et son sous-sol regorge de ressources minières . -Ce n' est pas tout le monde qui se réjouit de la décision du ministre Brigitte de donner un coup d' éclat aux écrans de la circulation à Bruxelles . Le projet de la ministre Brigitte de " " des poteaux des feux de signalisation à Bruxelles ne plaît pas à tout le monde . -Il n' y a pas si longtemps , on nous demandait de le définir collectivement . Voici peu , nous avons été collectivement conviés à en donner une définition . -Non pas que tout doit être simplifié ou à la bonne fin , mais qu' il y ait un sens inné du bien et du mal . Non pas que tout doive être simplifié ou finir bien , mais il existe une sens inné du bien et du mal . -Pas trop fatigué de faire des surprises , David Bowie a eu plus d' un coup de manches lors de la prochaine journée : Pas las des surprises , David Bowie avait plus d' un tour dans son sac avec The Next Day . -Pas encore à l' école et juste quelques mois à la fin de ses premières démarches , le et son Shetland par les trois tours avec le plus facile à prendre le meilleur - laisser ses 30 joueurs d' âge adulte derrière le but . , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ... Pas encore à l' école et seulement quelques mois après avoir fait ses premières armes , le et son Shetland ont pendant les trois tours avec une grande facilité et ont remporté le premier prix , laissant derrière eux les 30 autres concurrents adultes . -Rien n' est plus drôle d' Halloween que les maisons . Rien n' est plus représentatif d' Halloween que les maisons . -Nouri Al veut une " guerre mondiale " contre Al-Qaida . Nouri Al veut une " guerre mondiale " contre Al-Qaida . -Le 4 novembre est également le jour où la formation de l' hôpital reprend . Cette date du 4 novembre correspond également au renouvellement des internats . -Airbus s' adresse aujourd' hui au public avant le salon aéronautique de Dubaï , où le devrait dominer avec plus de 100 commandes . Aujourd' hui , Airbus en appelle directement au public avant le salon aéronautique de Dubaï , où le devrait prendre le pas sur ses concurrents avec plus de 100 commandes . -Maintenant , imaginez que Titan achète une usine fermée de Goodyear . Maintenant , imaginons que Titan achète à Goodyear l' usine fermée . -À l' âge de 29 ans , Katie est mariée et à l' Université de Pennsylvanie , un résident de la psychiatrie infantile . Aujourd' hui âgée de 29 ans , Katie est mariée et interne en psychiatrie infantile à l' université de Pennsylvanie . -Aujourd' hui , au cours de sa troisième année , la guerre civile est à l' origine des principaux rebelles musulmans sunnites contre le gouvernement d' Assad et ses forces de sécurité , qui sont avec des membres de sa secte , une de l' Islam chiite . Maintenant dans sa troisième année , la guerre civile oppose principalement les rebelles musulmans sunnites au gouvernement d' el-Assad et ses forces de sécurité , qui sont soutenus par les membres de la secte des , une branche de l' islam chiite . -Maintenant , le nouveau " Express " doit faire l' objet d' une " rectification complète " , a déclaré l' organisme de réglementation . À présent , le New Express va subir un « remaniement complet » , a déclaré le régulateur . -Maintenant , il y a juste l' écart , le vide silencieux , la nécessité de lui me confier , de me rassurer dans la paume de sa douceur . N' est resté que le manque , ce vide sans réponse , ce besoin d' elle , de me confier , de me rassurer au creux de sa douceur . -Maintenant , ils peuvent tenter graduellement de reprendre leur vie . Et tenter , peu à peu , de reprendre le cours de leur vie . -Aujourd' hui , 73 % des personnes qui choisissent l' incinération veulent qu' une cérémonie soit organisée . Désormais , 73 % des personnes ayant choisi une crémation souhaitent l' organisation d' une cérémonie . -Aujourd' hui , je suis devenu un . Aujourd' hui , je suis devenu plus . -Aujourd' hui , je la considère comme une présence quotidienne , une force bienveillante , un esprit d' épargne . Désormais , je la ressens comme une présence quotidienne , une âme bienveillante , un esprit . -De nombreux Ouïghours , une minorité au Xinjiang , appellent cette province chinoise du Turkestan oriental . De nombreux , une minorité installée au Xinjiang , appellent cette province chinoise le Turkestan oriental . -De nombreuses motos ont suivi notre voiture , conduite par , depuis l' aéroport jusqu' à notre domicile . Entre l' aéroport et chez moi , de nombreuses motos suivent notre voiture que conduit . -D' après le point de contrôle latéral , le site de Frontier City et les procédures d' enregistrement sont modifiés afin de s' assurer que les passagers sont au courant de la taxe avant qu' ils arrivent à la porte d' embarquement . O' Malley a expliqué que le site et les procédures d' enregistrement de Frontier sont en train de changer pour garantir que les passagers connaissent l' existence de ces frais avant d' arriver à la porte . -M. O' Malley a dit que la nouvelle accusation n' est pas de recueillir de l' argent . O' Malley a déclaré que ces nouveaux frais ne serviront pas vraiment à collecter de l' argent . -Le bilan de Pékin en 2007 est un triste rappel de ce qui aurait pu être Le mémoire de la campagne 2008 d' Obama est un triste rappel de ce qui aurait pu se passer -Le retour en arrière de la politique de santé Obama fait marche arrière sur sa réforme du système de santé -Obama sur le FMI et la Banque mondiale Obama met fin aux écoutes visant le FMI et la Banque mondiale -Les signes évidents de développement , tels que les seins , les seins et les aisselles , et l' odeur du corps apparaissent plus tôt chez les filles . Les signes évidents de développement , tels que le des seins , les poils sous les bras et dans la zone et les odeurs corporelles , apparaissent plus tôt chez les filles . -Évidemment , les élections électives ne sont pas un calendrier prévisible , car le parti politique opposé et les événements aléatoires , comme le massacre de , notre conversation publique . Évidemment , la fonction électorale n' est pas prévisible , car le parti politique d' opposition et les évènements qui se produisent par hasard , comme le massacre de Newton , influencent notre conversation publique . -Évidemment , il connaît la seconde , la surveillance , qu' il doit faire . Il est évident qu' il est au courant du deuxième , la surveillance , il ne peut pas en être autrement . -Octobre : Mois d' en Irak en 2008 Irak : octobre a été le mois le plus sanglant depuis 2008 -Le mois d' octobre a été le mois le plus sanglant en Irak cinq ans et demi , selon les statistiques publiées par les autorités irakiennes le vendredi 1er novembre . L' Irak a connu en octobre son mois le plus meurtrier en cinq ans et demi , selon des chiffres publiés vendredi 1er novembre par les autorités irakiennes . -Octobre a été le mois le plus sanglant d' Iraq depuis avril 2008 Octobre a été en Irak le mois le plus meurtrier depuis avril 2008 . -Bien sûr , je lui parle à la nuit morte quand l' absence de trop de choses tombe , et elle arrive dans mes rêves quand mes esprits sont un peu bas . Bien sur , je lui parle dans le creux de la nuit quand l' absence fait trop mal , elle s' invite dans mes rêves quand le moral est un peu atteint . -Bien sûr , mais il met l' accent sur son intérêt pour le , à ( Institut des sciences politiques français ) et les Hautes Etudes ( School for Advanced Studies in Social Sciences ) . Sans doute , mais en soulignant son faible pour les chemins de traverse , , les Hautes Etudes . -De ce nombre , 200 n' ont toujours pas trouvé un nouvel emploi . Parmi elles , 200 n' ont toujours pas trouvé de nouvel emploi . -Parmi eux , il y a eu civils , dont enfants . Parmi eux , on estime qu' il y avait 61 067 civils , dont 6 365 enfants . -En offrant aux jeunes ( souvent talentueux ) des jeunes un rôle de premier plan dans une musique , ils garantissent l' adoration des jeunes filles et souvent leur famille tout entière . Proposer des beaux jeunes hommes ( souvent talentueux ) en tête d' affiche d' un spectacle musical , c' est l' assurance d' un engouement de ces jeunes filles et souvent de leur famille entière . -Des policiers ont , 32 , ont déplacé trois kilos de cocaïne dans le domicile de l' avocat à Blackburn , , alors qu' il était dans un dîner de la société de droit avec des collègues . Les fonctionnaires de police ont repéré , 32 ans , en train de déposer trois sacs de cocaïne au domicile de l' avocat à Blackburn , dans le , alors qu' il assistait à un dîner du Barreau avec des confrères . -Les responsables ont annoncé le jeudi 31 octobre qu' un cas de rage avait été détecté dans un à . Les autorités ont annoncé jeudi 31 octobre qu' un cas de rage avait été détecté chez un chaton dans le Oise , manifestement d' origine étrangère alors que la France est indemne de cas autochtones depuis 2001 . -Le pétrole s' abaisse pour atteindre un baril de 96 dollars Le prix du pétrole continue à baisser et se rapproche de 96 $ le baril -Omar , un démonstrateur , a dit que les gens voulaient juste la stabilité . Omar , un manifestant , a dit que les gens voulaient seulement de la stabilité . -Omar Hassan est toujours en détention et doit comparaître devant le tribunal le vendredi . Omar Hassan est toujours en détention et devra se présenter en cour vendredi . -Omar Moustapha Hassan est accusé de prise d' otage , de provocation , de menaces verbales , d' arme ayant une intention dangereuse , d' enlèvement et de non-respect d' une ordonnance . Omar Moustapha Hassan a été accusé de prise d' otage , séquestration , de menaces , port d' arme dans un dessein dangereux , enlèvement , et défaut de se conformer à une ordonnance . -Le 8 janvier 2013 , date de son anniversaire , il annonce qu' un nouvel album sera publié en mars . Le jour de son anniversaire , le 8 janvier 2013 , il avait déclaré qu' un nouvel album sortirait en mars . -Né d' un , David Bowie une fois de plus son : il aurait pu partir d' une autre planète ? Sur Born In A , David Bowie fait une nouvelle fois référence à son : d' une autre planète ? -Vendredi dernier , les actes de violence les plus récents ont tué quatre personnes dans le nord de l' Iraq , une journée après que 26 personnes ont été tuées dans une série d' attentats , dont cinq voitures piégées qui ont explosé dans le nord de Bagdad . Vendredi , de nouvelles violences ont tué quatre personnes dans le nord de l' Irak , au lendemain de la mort d' au moins 26 personnes dans une série d' attentats , dont l' explosion de cinq voitures piégées au nord de Bagdad . -Vendredi , l' armée congolaise préparait une nouvelle attaque contre les bastions définitifs des rebelles de près de , dans l' est de la République démocratique du Congo . L' armée congolaise se préparait vendredi à un nouvel assaut contre les derniers bastions des rebelles du près de , dans l' Est de la RDC . -Le 25 juillet , l' ONU estime qu' environ 100 000 personnes sont mortes dans le conflit depuis mars 2011 . Le 25 juillet , l' ONU a estimé que 100 000 personnes étaient mortes dans le conflit depuis mars 2011 . -Lundi , 20 000 policiers ont été déployés devant l' Académie de la police du Caire , où Mohamed Morsi sera jugé . Lundi , 20 000 policiers seront déployés devant l' académie de police du Caire , où sera jugé Mohamed Morsi . -Lundi , la veille de l' entrée en vigueur de la loi , un juge de première instance a estimé que les dispositions relatives à l' octroi de privilèges étaient inconstitutionnelles . Lundi , un juge de premier instance avait estimé à la veille de l' entrée en vigueur de la loi que les dispositions traitant du privilège d' admission étaient . -Dimanche , l' équipe revient à la ville , fatiguée , mais fière de leur travail . Le dimanche , l' équipe retourne en ville , fatiguée mais fière du travail qu' elle a accompli . -Jeudi , l' OIAC a déclaré que les deux sites étaient , selon la Syrie , " abandonnés et que les programmes d' armes chimiques qu' ils contenaient avaient été déplacés vers d' autres sites déclarés , qui avaient été inspectés " . Jeudi , l' OIAC a dit que les deux sites étaient , selon la Syrie , « abandonnés et ... que les éléments du programme d' armes chimiques qu' ils contenaient avaient été déplacés vers d' autres sites déclarés , qui ont été inspectés » . -Jeudi , les données montraient une faiblesse persistante des exportations et un affaiblissement de la demande des consommateurs . Jeudi , des données ont montré une faiblesse continue des exportations et un ralentissement de la demande des consommateurs . -Jeudi , il a plaidé que la communauté internationale devait mener une " troisième guerre mondiale " contre Al-Qaida . Jeudi , il a plaidé pour que la communauté internationale mène une " troisième guerre mondiale " contre Al-Qaida . -Le jeudi soir , il a affirmé que le président Obama avait " résolu de clarifier la question et a commencé un réexamen de ces pratiques afin de s' assurer que personne n' ait induit l' erreur " . Il a assuré jeudi soir que le président Obama était " résolu à tenter de clarifier et qu' il procédait à un réexamen de ces pratiques afin que personne ne se sente trompé " . -Mercredi , les Centers for Disease Control and Prevention ont publié un ensemble de lignes directrices pour gérer les allergies alimentaires chez les enfants à l' école . Mercredi , le Centre américain de contrôle et de prévention des maladies a publié une série de directives indiquant comment gérer les allergies alimentaires des enfants à l' école . -Mercredi , les habitants de sont descendus dans les rues de la ville pour se féliciter de l' entrée des FARDC . Mercredi , les habitants de ont envahi les rues de la bourgade pour saluer l' entrée des soldats des FARDC . -Le mercredi , le porte-parole libéral des anciens membres du personnel militaire , Jim , a demandé le retour du caporal Hawkins , tandis que le Jack Harris a demandé une cessation immédiate de cette " pratique honteuse " . Le porte-parole libéral en matière d' anciens combattants , Jim , a demandé mercredi la réintégration du caporal Hawkins , tandis que le Jack Harris a réclamé la fin immédiate de " cette pratique honteuse " . -Le mercredi , elle a réduit de 25 % le kilométrage parcouru . Mercredi , elle a réduit les primes de miles de 25 % sur un voyage . -Un jour léger à la fin août un hélicoptère de police allemand peu au-dessus du consulat des Etats-Unis à Francfort , la capitale financière de l' Allemagne . Par une belle journée de fin août , un hélicoptère de la police allemande a survolé à basse altitude le Consulat britannique de Francfort , la capitale financière de l' Allemagne . -Sur son site web a donné une séance de questions et de réponses sur ce qu' il a dit , son travail de fantaisie serait un avocat qui représente des clients à File Row en Amérique , son ultime invité comme Mohammed Ali et l' inégalité en tant que motivation pour le travail de . Sur son site , a organisé une séance de questions / réponses sur lui-même au cours de laquelle il disait que son job de rêve serait d' être avocat pour représenter des clients dans le couloir de la mort en Amérique , son dernier invité à dîner serait Mohammed Ali et l' inégalité serait ce qui le à travailler . -De son côté , Chrysler , qui est sous le contrôle de Fiat , a annoncé une augmentation de 11 % des ventes de véhicules , contre un prévu . De son côté , Chrysler , contrôlé par Fiat a annoncé une hausse de 11 % de ses ventes à 140 véhicules , contre 143 536 attendues . -De son côté , Vodafone ( + % ) continue de prendre la direction du dans le communiqué en réponse à un communiqué de presse indiquant que AT & T envisageait une éventuelle offre de rachat . De son côté , Vodafone ( + % ) se maintient en tête des hausses de l' en réaction à une information de presse voulant qu' AT & T étudie une éventuelle OPA . -Sur place , je rencontre des journalistes , dont ceux de l' AFP , des diplomates et du personnel des services , dont je connais depuis vingt ou trente ans . Sur place , je rencontre des journalistes , dont ceux de l' AFP , des diplomates , des gens des services que je connais pour certains depuis vingt ou trente ans . -À d' autres étapes , des comédies musicales telles que " 1789 : les de la Bastille " et de petites productions telles que " Disco " ou " Life and Time " sont en train de diminuer avec le succès et la qualité enregistrés jusqu' à présent . Sur les autres scènes , les spectacles musicaux comme " 1789 " , " les amants de la Bastille " et les petites comédies musicales comme " Disco " ou " Life and Times " se mettent au diapason du succès et de la qualité . -Sur certains des camions , les sièges avant peuvent avoir un défaut dans le mécanisme . Sur certains , les sièges avant pouvaient présenter un défaut au niveau du mécanisme d' inclinaison . -À la Bourse de New York , les actions GM ont augmenté de 1,4 % à 16 : 35 GMT , tandis que les actions de Ford ont perdu 1,1 % . A la Bourse de New York , l' action GM gagnait 1,4 % à GMT tandis que Ford 1,1 % . -Sur le tapis , qui est aussi blanc , dans l' immense espace du Théâtre Maisonneuve , un corps , apparaît dans le profil , aussi mince qu' une ligne et habillé en noir . Sur le tapis , aussi blanc , dans l' immense espace qu' est le Théâtre Maisonneuve , s' avance , de profil , fin comme une ligne et vêtu de noir , un corps . -Le lendemain de mon échographie , quand j' ai découvert le genre de bébé que je , je me suis réveillé et je me suis de mon lit , et mon père m' attendait à l' atterrissage : il me sourit et je me réjouis de recevoir un petit garçon . La veille de mon échographie où je devais savoir le sexe de mon bébé , j' ai rêvé que je me , je me hors du lit , mon père m' attendait sur la palier et il me et se réjouissait que j' attende un petit garçon . -Le soir , vous avez été libre , vous êtes apparu sur le news sur 2 . Dès le soir de votre libération , vous étiez sur le plateau du 20h d ' " Antenne 2 " . -Sur le marché des changes , la spéculation sur un changement de la politique monétaire de la BCE est monnaie courante , comme l' a témoigné John Hardy , un stratège de Saxo Bank . Sur le marché des changes , les spéculations sur l' évolution de la politique monétaire de la BCE vont bon train , comme en témoigne John Hardy , stratège chez Saxo Bank . -Du côté gouvernemental , il s' agit de qui sont membres des forces armées du Président Bachar el Assad , sont des combattants et 187 sont des militants du Hezbollah libanais . Du côté du gouvernement , on estime que 29 954 membres des forces armées du président Bachar el-Assad ont trouvé la mort , dont 18 678 étaient des combattants des forces et 187 des militants du Hezbollah libanais . -Sur le terrain , à Beyrouth ? Sur place notamment , à Beyrouth . -Sur instruction du Bureau de la protection de la Constitution ( ) , l' agence allemande de renseignement , sa mission consistait à photographier le toit de l' avant-poste américain situé à moins de 5 km de la Banque centrale européenne et de la Bundesbank . Sur ordre de l' Office fédéral de protection de la Constitution ( ) , l' agence nationale de renseignements allemande , sa mission était de photographier le toit de l' avant-poste américain , situé à moins de 5 km de la Banque centrale européenne et de la Bundesbank . -D' un autre côté , les analystes disent que les cabines sont de plus en plus fréquentes , ce qui laisse à penser que de nombreux passagers ont voté pour la formule plus dense , qui peut aller de pair avec des tarifs moins chers . D' un autre côté , les analystes disent que les cabines configurées avec 10 sièges par rangée dans les 777 existants suggèrent que de nombreux passagers se prononcent en faveur d' une configuration plus dense , qui peut aller de pair avec des tarifs moins chers . -D' autre part , à la suite de l' annonce d' une inflation de 0,7 % dans l' ensemble de la zone euro , l' idée que la Banque centrale européenne ( BCE ) puisse assouplir sa politique monétaire s' est répandue parmi les acteurs du marché . A l' inverse , après l' annonce d' une inflation à 0,7 % pour l' ensemble de la zone euro , l' idée se répand parmi les acteurs du marché que la Banque centrale européenne ( BCE ) pourrait assouplir sa politique monétaire . -D' un autre côté , les écosystèmes plus proches sont favorables à l' isolement génétique , ce qui peut en fin de compte entraîner la création de nouvelles espèces . A l' inverse , les écosystèmes plus fermés favorisent l' isolement génétique , qui peut engendrer à terme la création de nouvelles espèces . -D' un autre côté , le chef de la puissante NSA , le général Keith Alexander , a nié que son service de renseignement ait capturé des dizaines de millions de communications de citoyens européens . Par contre , le patron de la puissante NSA , le général Keith Alexander , a lui démenti que son agence de renseignement ait capté des dizaines de millions de communications de citoyens européens . -Sur le marché des valeurs mobilières , Renault ( % ) a enregistré la plus forte baisse avec le CAC 40 , par l' avertissement de rendement émis par son partenaire , Nissan Motor , vendredi . Aux valeurs , Renault ( % ) enregistre le plus fort repli du CAC 40 , par l' avertissement sur résultat de son partenaire Nissan Motor vendredi . -Sur les murs de son bureau , il y a des fusils d' assaut AK-47 , des photographies érotiques et des photos de l' auteur avec des seigneurs de guerre africains . Aux murs de son bureau sont accrochés des fusils d' assaut AK-47 , des photos érotiques et des photos de l' auteur avec des seigneurs de guerre africains . -Une fois que la Cour l' aura déclarée recevable , elle se rendra à la deuxième étape , à savoir les audiences préliminaires . Une fois la recevabilité admise par la Cour , on procède à la deuxième étape , soit les auditions sur le fond . -Une mère du Maryland d' un nouveau-né a également déclaré à la Zone pour bébés qu' elle aimerait voir des bébés accoucher de sexe à la naissance . Dans le Maryland , la mère d' un nourrisson a également déclaré sur Baby Zone qu' elle préfèrerait qu' un sexe soit attribué aux bébés à leur naissance . -Un blessé dans Accident avec Road Driver sur Bruxelles Ring Route Un blessé grave dans un accident avec un conducteur fantôme sur le ring de Bruxelles -On pourrait penser que la ville de New York a été la naissance d' une comédie en Amérique : il y a près de cent ans , le Frank , qui a été le maître des cérémonies du Palais du cinéma , à Broadway , a commencé à raconter des plaisanteries directement à la foule , de manière . On pourrait dire que la ville de New York est le lieu où est né le en Amérique : il y a presque 100 ans , l' acteur de Frank , qui a tenu le rôle de maître de cérémonie au Palace Theatre , sur Broadway , commençait à raconter des blagues directement devant le public , sur le ton de la conversation . -Un jour , il est mort . Un jour , il est mort . -On a l' impression que , au lieu de chercher au travail de , le chorégraphe s' est mis à l' utiliser avec son . On a l' impression que la chorégraphe , au lieu d' aller à la rencontre de l' oeuvre de , s' est imposée , avec ses . -Un enfant sur 20 aux États-Unis souffre maintenant d' allergies alimentaires . Un enfant sur 20 aux États-Unis souffre aujourd' hui d' une allergie alimentaire . -Selon la BBC , une personne a déclaré ces années plus tard : " Je ne suis ni homme ni femme " . Selon la BBC , une personne aurait dit des années plus tard , « Je ne suis ni un homme ni une femme . » -Un homme a expliqué que la chirurgie qu' il avait reçue lui permettrait de reprendre son travail . Un homme a expliqué que l' opération gratuite qu' il avait subie pour soigner une hernie lui permettrait de travailler à nouveau . -L' un des plus grands succès du festival sera une interview : David Steinberg en relation avec Larry David . L' un des principaux facteurs d' attraction du festival cette année sera un entretien : David Steinberg discutant avec Larry David . -Un des tunnels de contrebande les plus sophistiqués entre les États-Unis et le Mexique a été découvert , de même que ses propres systèmes d' éclairage , de ventilation et de transport électrique . L' un des tunnels les plus sophistiqués servant pour le trafic de drogue entre les États-Unis et le Mexique , disposant de ses propres systèmes d' éclairage , de ventilation et de rails électriques , a été découvert . -Une chose que j' attends avec impatience chaque Halloween est les tendances . Ce que j' attends avec impatience à chaque fête d' Halloween , ce sont les tendances . -L' un était employé à la U.S. Transportation Security Administration ( TSA ) , et l' autre était le tireur . Il s' agirait d' un employé travaillant pour l' Administration pour la sécurité des transports des États-Unis ( TSA ) et de l' auteur des coups de feu . -Un an auparavant , elle avait voyagé avec ma famille à Cuba . Un an auparavant , elle était en voyage avec ma famille à Cuba . -Seulement 15 % de ceux qui perdent un enfant choisissent l' incinération . S' ils ont le malheur de perdre un enfant , ils ne sont que 15 % à choisir la crémation . -Ce n' est qu' à la fin , qui est négative , que les danseurs ont des lumières et deviennent des symboles blancs dans un théâtre sombre , qu' il touche à la magie . Seule la fin , en négatif , avec ses danseurs par une lumière et les signes blancs dans un théâtre soudainement noir , touche à la magie . -Une demi-douzaine seulement des conseillers principaux du président , y compris M. , l' ancien chef de la diplomatie Barack Obama , et les anciens conseillers de la Première Commission , David et David , savaient que le changement était à l' étude . Seule une demi-douzaine de proches conseillers du président – notamment , l' ancien chef de campagne d' Obama Jim et les anciens conseillers à la Maison-Blanche David et David – savaient que le changement était à l' étude . -Une partie seulement de son dos était visible , c' est vraiment triste " . On voyait seulement une partie de son dos , c' est tellement malheureux . -Seules quelques centaines de sympathisants se sont rassemblés pour protester contre le président du pays et , à la faveur de la fin de la présidence libyenne des dernières élections , le couvre-feu n' a été que peu de temps pour permettre à son cortège de voitures de traverser la rue très occupée reliant le centre-ville à ses banlieues sud . Seuls quelques centaines de supporteurs s' étaient rassemblés devant la Cour pour le nouveau président et , se du faste présidentiel des années Moubarak , la circulation n' a été que brièvement interrompue pour permettre le passage de son convoi dans les rues très fréquentées reliant le centre-ville aux banlieues sud . -Les opérations sont habituellement menées dans une école , une école municipale ou une maison ; après la procédure , les patients se rendent à la zone de récupération pour aller chercher la personne suivante . Les opérations sont en général pratiquées dans une école , une mairie ou une maison ; après l' intervention , le patient se lève et se rend dans la salle de réveil pour laisser sa place à la personne suivante . -Les opposants disent que les changements le moral de la police , mais non le crime , le gaspillage de l' argent et ne réglerait pas un problème plus vaste d' une force policière sous pression après avoir diminué de milliers de policiers au cours de la dernière décennie . Les opposants disent que ces changements le moral de la police sans faire baisser la criminalité , représenteraient un gaspillage d' argent et ne pas le problème plus large de forces de police sous pression après que les effectifs de policiers ont été fortement réduits au cours de la dernière décennie . -Parmi les options , on trouve les freins , un système de prestation de services qui permet de relier Works , le soutien logistique et le stockage des véhicules entre les événements . Parmi les options , citons notamment des freins améliorés , un service donnant accès aux mécaniciens Works , au soutien logistique et à l' entreposage du véhicule entre deux évènements . -Ou bien ils peuvent choisir de ne pas disposer d' un dispositif , en choisissant de payer un tarif fixe basé sur le nombre moyen de milles parcourus par les habitants de tous les États . Ils peuvent encore choisir de ne pas avoir d' appareil du tout , préférant à la place payer un forfait basé sur le nombre moyen de miles parcourus par tous les résidents de l' État . -Ou s' agit-il d' un incident involontaire ? Ou incident involontaire ? -Ou bien nous pouvons défendre nos propres intérêts économiques , c' est-à-dire parler à Berlin et à Bruxelles où il faut aller . Ou nous pouvons défendre nos propres intérêts économiques , ce qui inclut d' envoyer promener Berlin et Bruxelles . -Oracle à DM Les actionnaires d' Oracle par le salaire d' Ellison -Les organisations consacrées à la protection de la vie privée prennent note de cette fascination pour la biométrie , mais elles le sont . Les organisations dédiées à la protection de la vie privée confirment cet engouement pour la biométrie , mais s' en inquiètent . -Organisé par la section Unity de l' association , le concours devrait réunir les meilleurs patineurs de la région . Organisée par la section Unity de l' association , la compétition devrait regrouper les meilleurs de la région . -D' autres membres du comité ont dit qu' il n' y a que des cas anecdotiques de rapports de pilotes pour soutenir que les appareils peuvent interférer avec les systèmes d' aéronefs , et la plupart de ces rapports sont très vieux . D' autres membres du comité ont déclaré qu' il n' existe que des rapports anecdotiques émanant de pilotes qui indiquent que les appareils peuvent interférer avec les systèmes d' un avion , et que la plupart d ’ entre eux sont très anciens . -D' autres médecins participant à la conférence du PAA ont renforcé les complexités du sujet . D' autres médecins participant à la conférence de l' AAP ont insisté sur la complexité du sujet . -D' autres conclusions appuient cette conclusion , mais il y a beaucoup d' autres facteurs . D' autres découvertes soutiennent cette conclusion , mais il existe de nombreux autres facteurs influents . -D' autres hôtels à Montréal seront également convertis dans les prochains mois , comme le Crown Plaza , qui deviendra un foyer pour les personnes âgées . D' autres hôtels de Montréal seront aussi convertis dans les prochains mois , comme le Crown Plaza qui deviendra une résidence pour personnes âgées . -D' autres endroits espèrent vendre le concept à un public averti en faisant pousser les dispositifs plus , pas moins . D' autres États espèrent vendre le concept à des citoyens méfiants en permettant aux appareils d' en faire plus , plutôt que pas assez . -D' autres révélations ont révélé des documents dissimulés par que la FSA a contrôlé le téléphone de la chancelière allemande Angela Merkel et celui de jusqu' à 34 autres dirigeants du monde . D' autres révélations ont fait état de documents divulgués par selon lesquels la NSA avait intercepté des données et des communications émanant du téléphone portable de la chancelière allemande Angela Merkel et de ceux de 34 autres chefs d' État . -D' autres sont là pour vous faire rire . D' autres ont juste l' ambition de faire rire . -D' autres reviennent en arrière quinze ans , sous le prétexte que vous pouvez mettre tout ce que vous voulez quand Matt sur le projet de loi . D' autres font un bond de quinze ans en arrière sous prétexte qu' on peut servir à peu près ­ n' importe quoi quand on a Matt ­ en tête d' affiche . -D' autres peuvent recevoir des béquilles , une paire de lunettes aveugles ou des permis de naissance gratuits - documents exigés pour l' école , mais que de nombreuses familles pauvres ne peuvent tout simplement pas se permettre . D' autres peuvent recevoir des béquilles , une paire de lunettes collectée ou un certificat de naissance gratuit – document nécessaire pour aller à l' école , mais de nombreuses familles pauvres ne peuvent tout simplement pas se permettre de l' acheter . -Sinon , nous devrions envisager d' imiter la Norvège et de conserver notre indépendance économique . Sinon , nous devrions imiter la Norvège et conserver notre indépendance économique . -Ottawa veut forcer les fournisseurs de services de télévision par câble et par satellite à offrir aux clients l' option d' avoir accès à des services payants . Ottawa désire forcer les fournisseurs de télévision par câble et satellite à offrir aux clients la possibilité de payer les services à la carte . -Notre mère nous a quittés après une lutte acharnée contre le cancer . Notre mère nous a quittés après son combat épuisant contre le cancer . - , il s' installe dans des partis , des femmes et des médicaments . Sans limites , l' homme enchaîne les fêtes , les femmes , les drogues . -À l' extérieur du bâtiment , des manifestants protestaient contre le dirigeant iraquien . A l' extérieur du bâtiment , des manifestants protestaient contre le dirigeant irakien . -Au cours d' une période de huit mois entre janvier et août 2011 , il a cherché à obtenir des renseignements illégaux sur les arrestations de deux hommes au nom de Scarborough ainsi que d' un de ses associés . Sur une période de huit mois entre janvier et août 2011 , il a cherché à obtenir de manière illicite des informations sur l' arrestation de deux hommes pour le compte de Scarborough ainsi que l' un de ses associés . -Pendant cinq semaines , les approvisionnements ont augmenté de plus de 25 millions de barils . Sur cinq semaines , les approvisionnements ont augmenté de plus de 25 millions de barils . -Là , les gens ont abandonné leurs anciens dirigeants en Angleterre et n' ont plus à s' en préoccuper . Là-bas , des hommes qui avaient laissé leur Ancien Régime en Angleterre et n' avaient plus à s' en soucier . -Là , la révolution a été consensuelle , alors qu' elle a engendré des drames et des conflits . Là une révolution consensuelle quand ici elle a semé le drame et le confit . -Le PSG , qui est le plus haut de la hiérarchie de Monaco à la différence de l' objectif , a publié la grille de l' équipe pour le match , qui est le 12 janvier de la saison , sur laquelle le 32 n' a pas comparu . Le PSG , leader du championnat devant Monaco à la différence de buts , a diffusé la liste des joueurs retenu pour ce match de la 12e journée , sans qu' y figure l' attaquant de 32 ans . -Les Palestiniens et les Israéliens sur la frontière avec la bande de Gaza entre Palestiniens et Israéliens à la frontière de Gaza - s a été fait célèbre par son rôle dans sexy TV show . Les boucles blondes de Pamela sont devenues célèbres grâce à son rôle dans la série télévisée sexy Alerte à Malibu . -Pamela Anderson les griffes de ces superbes , de nouvelles coupes . Pamela Anderson coupe ses emblématiques boucles blondes et adopte une spectaculaire coupe . -Pamela Anderson est la dernière star à avoir les fans avec un nouveau . Pamela Anderson est la dernière célébrité à choquer ses fans avec une nouvelle coiffure spectaculaire . -Parents de Kids Select " Gender " Les parents d' enfants peuvent choisir « sexe indéterminé » -Les parents de la Géorgie qui sont morts dans un " accident " pensent que le fils a été assassiné . Les parents d' un adolescent de Géorgie , qui est décédé dans des circonstances insolites , pensent que leur fils a été assassiné -Les parents ne devraient pas hésiter à recevoir des conseils de leur pédiatre sur la façon de parler avec leur enfant . Les parents ne devraient pas hésiter à demander conseil à leur pédiatre pour savoir comment parler avec leur enfant . -A la veille de l' ouverture de la Coupe du monde de football en France , l' équipe de France s' est engagée en faveur d' un accord sur le changement climatique . Le Paris Saint-Germain recevra Lorient vendredi sans son atout maître , le Suédois , qui est blessé , annonce le club de Ligue 1 dans un communiqué . -Les passagers sont censés remettre leurs appareils en marche , l' avion s' vers la terre et ne les pas jusqu' à ce que l' avion soit sur le sol . Les passagers sont supposés éteindre leurs appareils lors de la descente de l' avion avant l' atterrissage et ne pas les rallumer jusqu' à ce que l' avion se soit posé . -Les passagers peuvent déjà faire cela pendant la majeure partie d' un vol , mais beaucoup de gens trouvent difficile d' être incapables d' accéder à leurs livres électroniques pendant les et les atterrissages . Les passagers peuvent déjà le faire pendant la plus grande partie du vol , mais de nombreuses personnes trouvent pénible de ne pas pouvoir accéder à leurs livres électroniques pendant le décollage et l' atterrissage . -Les passagers se plaignent souvent des frais de bagages et d' autres frais , mais les compagnies aériennes les aiment . Les passagers souvent à propos des frais de bagages et autres frais , mais les compagnies aériennes les adorent . -Les passagers qui essaient d' éviter ces frais sont autant qu' ils peuvent dans les bagages de cabine dissimulés dans les , ce qui signifie que ces bacs sont souvent hors de l' espace . Les passagers qui essayaient d' éviter ces frais mettaient autant de choses qu' ils pouvaient dans les bagages à main rangés dans les compartiments supérieurs , ainsi il n' y avait souvent plus de place dans ces compartiments . -Les passagers qui achètent leurs billets sur le site Internet de la compagnie n' auront pas à payer . Les passagers qui achètent leur billet sur le site de la compagnie aérienne n' auront pas à payer . -On leur demandera de changer leurs smartphones , leurs tablettes et d' autres appareils en mode avion . Les passagers devront mettre leurs smartphones , tablettes et autres appareils en mode avion . - , qui a passé près de dix ans en tant que l' un des plus d' Asie du Sud-Est , a été condamné hier à 20 ans de prison pour son rôle dans la construction des dispositifs explosifs utilisés lors de l' attentat . , qui a passé presque 10 ans en fuite en étant l' un des hommes les plus recherchés d' Asie du sud-est , a été hier condamné à une peine de 20 ans de prison pour son rôle dans la fabrication des engins explosifs utilisés lors des attentats . - est le dernier des de Bali à être soumis à la justice . est le dernier des de bombe de Bali à être traduit en justice . - peut encore interjeter appel de sa sentence . peut toutefois faire appel de sa condamnation . -Patrick , auteur du livre de référence " de " et conférencier au Théâtre du Châtelet , analyse les raisons du succès durement remporté . Patrick , auteur du livre de référence " Histoires de comédies musicales " et conférencier au Théâtre du Châtelet , analyse les raisons du succès si durement conquis . -Des personnes sont attaquées dans la rue et même à la maison , et les forces de sécurité sont souvent les cibles des attaques . Des hommes sont abattus en pleine rue ou même chez eux , et les forces de sécurité sont également la cible d' attaques fréquentes . -Les gens ne sont pas stupides , ils iront là où les meilleurs soins sont offerts " . Les gens ne sont pas fous , ils vont aller là où il y a la meilleure offre de soins . -Les gens paient plus directement dans ce qu' ils gagnent . Les gens paient plus directement pour les avantages qui leur sont procurés . -Les gens disent que " le prix de l' or est bon , la chaîne d' or de Eli et la récupérer le mois prochain . Les gens disent , « le prix de l' or est élevé , mettons en gage la chaîne en or de grand-mère et on reviendra la chercher le mois prochain » . -Les gens entendent le message , mais ne écoutez pas , et cela me choque . Ce message est entendu , mais pas écouté , et cela me choque . -Les gens sont . Les gens étaient bouche . -Peut-être la grande différence entre un auteur adulte auteur d' un adulte et un auteur adulte écrit pour un enfant est la nécessité d' un certain sens de l' espérance . Peut-être que la grande différence entre un auteur adulte écrivant pour les adultes et un auteur adulte écrivant pour les enfants est la nécessité d' offrir de l' espoir . -Perry a déclaré qu' elle avait le dîner avec le vétéran et que " elle m' a frappé quand j' avais 18 ans " . Perry a déclaré avoir avec le vétéran du rock et expliqué , « il m' a quand j' avais 18 ans » . -Perry a été l' un des chanteurs à faire un exposé sur la carrière des Rolling Stones cette année . Perry était l' une des chanteuses à faire une apparition lors de la tournée des Rolling Stones cette année . -Peter Hughes était au barreau de où un kamikaze a fouillé un sac à dos contenant des explosifs au moins 20 secondes plus tôt . Peter Hughes était au s Bar lorsqu' un kamikaze y a fait sauter un sac à dos chargé d' explosifs à peine 20 secondes plus tôt . -La phase 1 consistera en une nouvelle ligne ferroviaire à grande vitesse entre Londres et les Midlands de l' Ouest . La Phase 1 portera sur une nouvelle ligne ferroviaire à grande vitesse entre Londres et les West Midlands . -La police de Phuket a interrogé pendant deux jours avant qu' elle n' ait avoué la fabrication de l' article . La police de Phuket a interrogé pendant deux jours avant qu' elle avoue avoir inventé l' histoire de toutes pièces . -Les changements physiques ne signifient pas que la puberté est imminente Les changements physiques ne signifient pas que la puberté est imminente - : Au moins quatre en Pia : au moins quatre blessés dans une violente bagarre -Pierre s Two Cavendish Les deux France de Pierre Nora -André II a montré à la fois qu' il s' est montré très intéressé par la Nouvelle République et qu' il s' est montré prêt à aider ceux qui ont commencé à réparer le fossé créé par la Révolution en enseignant à tout le monde dans cet ancien pays que la France a ouvert la voie à la France moderne et en la dotant d' une version unifiée de son histoire . A cette IIIe République , moment central et créateur , Pierre Nora a montré beaucoup d' intérêt et même de tendresse : saluant ceux qui se sont alors employés à réparer la fracture révolutionnaire , en enseignant aux écoliers tout ce qui dans l' ancienne France préparait la France moderne et en leur proposant une version unifiée de leur histoire . -L' île de Pitcairn a pour objectif de créer la plus grande réserve marine dans le monde , le journal News Les Nouvelles de Tahiti a annoncé jeudi , suite à des nouvelles de Radio Australia . L' île de Pitcairn souhaite créer la plus grande réserve marine du monde , a annoncé jeudi le quotidien Les Nouvelles de Tahiti , reprenant une information de Radio Australie . -Pitcairn pour créer la réserve marine la plus grande du monde Pitcairn veut créer la plus grande réserve marine du monde -Les géants font souvent du commerce sur des questions techniques par la publicité dans la presse commerciale . Les géants de l' aéronautique échangent souvent des coups sur des questions techniques en faisant de la publicité dans la presse professionnelle . -Les appareils modifiés pour les systèmes Wi-Fi sont aussi plus résistants aux interférences . Les avions modifiés pour accueillir des systèmes Wi-Fi résistent également mieux aux interférences . -Veuillez contacter les services à la clientèle du bureau de poste où vous avez acheté vos billets pour toute autre assistance . Merci de contacter le service client à la billetterie où vous avez acheté vos billets pour toute aide complémentaire . -Le chef de la police , David , a réagi en nommant deux journalistes et un responsable des médias pour interrogatoire , bien que la citation ait été levée suite à un tollé . Le chef de la police , David , a réagi en convoquant deux journalistes et un responsable des médias pour les interroger , bien que la convocation ait été retirée suite à une levée de boucliers dans les médias . -Les policiers disent qu' ils ont une vidéo qui semble indiquer au maire Rob Ford de fumer une pipe de fumée . La police déclare qu' elle est en possession d' une vidéo semblant montrer le Maire Rob Ford en train de fumer du crack . -Les politiciens ne veulent pas augmenter l' impôt même un cent lorsque les prix du gaz sont élevés . Les hommes politiques sont réticents à augmenter la taxe , même d ' 1 cent , alors que les prix de l' essence sont élevés . -Les responsables politiques se sont dits préoccupés par le fait que l' UE manque de capacités en matière d' informatique et de TI et qu' elle devrait s' efforcer de réduire sa dépendance vis-à-vis des États-Unis . Les hommes politiques ont fait part de leur inquiétude à l' idée que l' UE ne dispose pas de capacités informatiques et Internet suffisantes et pensent qu' elle devrait s' efforcer de réduire sa dépendance vis-à-vis des États-Unis . -Pape François pour nommer les premiers cardinaux en février Le pape François nommera ses premiers cardinaux en février -Le pape François va créer de nouvelles cardinaux de l' Église catholique pour la première fois le 22 février , et le Vatican annoncé jeudi . Le pape François nommera de nouveaux cardinaux de l' Église catholique pour la première fois le 22 février , a annoncé le Vatican jeudi . -Préparation à la gestion d' une classe dans un contexte nord-américain et québécois . La préparation à gérer une classe dans un contexte nord-américain , québécois . -Le Président Armando s' est efforcé de faire fi de l' instabilité . Le président Armando a cherché à minimiser les inquiétudes concernant l' instabilité . -Les membres du conseil d' administration du président Barack Obama ont décidé de remplacer le vice-président Joe Biden par Hillary Clinton au sujet du billet de 2012 , selon le New York Times . Les conseillers les plus proches du président Barack Obama ont envisagé , dans le plus grand secret , de remplacer le vice-président Joe Biden par Hillary Clinton sur la liste de 2012 , selon le New York Times . -Le président Xi , qui a pris ses fonctions en mars dernier , a fait de la lutte contre la corruption une priorité nationale , estimant que ce phénomène constitue une menace à l' existence même du parti communiste . Le président Xi , qui a pris ses fonctions en mars dernier , a fait de la lutte contre la corruption une priorité nationale , estimant que le phénomène constituait une menace à l' du Parti communiste . -Les élections présidentielles et parlementaires sont prévues pour l' année prochaine . Les élections présidentielles et législatives sont prévues l' année prochaine . -La pression exercée sur la FAA ces dernières années a contribué à assouplir les restrictions à leur utilisation . La pression pesant sur la FAA s' est renforcée au cours des dernières années pour assouplir les restrictions concernant leur utilisation . -Les pourparlers précédents entre les parties ont fait peu pour calmer les tensions suscitées par une série d' affrontements cette année . Les précédentes discussions entre les parties n' ont pas réussi à apaiser les tensions alimentées par une série de conflits cette année . -Les vaccins antérieurs ont échoué parce qu' ils contenaient des anticorps . Les précédents vaccins contre le tabac se sont avérés inefficaces car ils contenaient des anticorps . -Fonctions non publiées récemment libérées Des inédits déjà en écoute -Les prix au centre-ville de Londres ont continué de représenter une croissance constante de 5,6 % , mais ils ont été par un " marché intérieur " florissant au sud de l' ouest , au nord ( 7,4 % ) et à l' est ( 6,5 % ) tous , selon la recherche de . Les prix dans le centre de Londres affichent une croissance régulière d' année en année de 5,6 % mais ont été par un « marché intérieur » en pleine croissance , le sud-ouest de la ville , le nord ( 7,4 % ) et l' est ( 6,5 % ) connaissant une reprise , selon les études de . -Le bien immobilier - les 5 à 10 % du marché du logement dans la fourchette - dans la grande banlieue londonienne du Sud-Ouest , qui s " étend de à Wimbledon , a augmenté de 11,8 % au cours de l' année écoulée . L' immobilier de prestige – les 5 à 10 % des prix les plus élevés du marché de l' immobilier – dans le secteur prospère situé au sud-ouest de Londres qui s' étire de à Wimbledon , a augmenté d' un pourcentage record de 11,8 % au cours de l' an dernier . -Avant son arrivée en Afghanistan , il était également allé en Libye , en Russie , au Liban et au Mali . Et avant l' Afghanistan , il était aussi reparti en Libye , en Russie , au Liban et au Mali . -Les spécialistes de la protection de la vie privée continuent de s' inquiéter . Les spécialistes de la protection de la vie privée , eux , continuent de s' inquiéter . -Le professeur , de l' Université de Trente , a déclaré : " Il est bien connu de l' homme que la gauche et le côté gauche du cerveau sont impliqués dans des stimuli qui évoquent des émotions positives ou négatives " . Le Professeur , un de l' université de Trente , a déclaré : « Tout le monde sait que , chez l' homme , le côté gauche et le côté droit du cerveau sont impliqués de manière différente dans les stimuli qui éveillent des émotions positives ou négatives . » -Le professeur a dit : " Nous avons présenté des chiens pour des films de chiens , soit une version naturelle et une silhouette pour enlever toutes les autres sortes de délires , et nous sommes en mesure de conseiller le mouvement de la queue et de présenter la queue plus à gauche ou à droite " . Le Professeur a expliqué : « Nous avons présenté à des chiens des films où figuraient d' autres chiens – soit une version naturaliste soit une silhouette pour éviter tout autre facteur de confusion – et nous avons pu le mouvement de la queue et le présenter comme allant plus à gauche ou à droite . » -Le professeur a dit qu' il ne croyait pas que les chiens spontanément entre eux au cours de ces mouvements . Le Professeur a dit qu' il ne pensait pas que les chiens communiquaient délibérément entre eux grâce à ces mouvements . -Le professeur Crystal a dit que si un vaccin à l' avenir était complètement sûr , il pourrait être mis à la disposition des enfants avant qu' ils ne soient tentés d' essayer une cigarette , empêchant ainsi la dépendance à la nicotine . Le Professeur Crystal a déclaré que si le futur vaccin à usage humain était complètement sûr , on pourrait l' administrer aux enfants avant qu' ils soient tentés d' essayer une cigarette , ce qui empêcherait toute addiction à la nicotine . - , Anne a déclaré : " Si quelqu' un doit savoir qu' il ne s' agit pas d' une infraction à la loi , il s' agit d' un avocat criminel . Le procureur Anne a déclaré : « Si quelqu' un doit savoir qu' il ne faut pas violer la loi , c' est bien un avocat . » -Le procureur Andrew a fait savoir au East Bailey que les nouvelles du monde avaient révélé , en mai 2005 , une mauvaise rumeur que M. Clarke voyait son " conseiller spécial " , Hannah . Le Procureur Andrew QC a déclaré devant le tribunal de l' Old Bailey que News of the World avait entendu une fausse rumeur en mai 2005 selon laquelle Clarke avait une relation avec Hannah , sa « jolie conseillère spéciale » . -Les procureurs ont demandé une peine de vie , même s' ils ont peut-être poussé l' homme appelé " Man " à sa réputation de maître condamné à mort . Les procureurs ont demandé une peine de réclusion à perpétuité , pourtant ils auraient pu demander à ce que l' homme , qualifié de « Man » pour sa réputation d' expert en fabrication de bombes , soit condamné à mort . -Les aspects psychosociaux sont également importants . Les aspects psychosociaux sont également importants . -La fonction publique porte sur le service public . L' exercice de fonctions publiques est une question de service public . - et ont déclaré vendredi qu' ils n' avaient pas reçu d' opposition des autorités américaines à leur projet de fusion , rapprochant ainsi la création de la plus grande agence de publicité du monde . et ont dit vendredi n' avoir reçu aucune objection de la part des autorités américaines à leur fusion , se rapprochant ainsi de la création de la première agence de publicité mondiale . -Qantas a déclaré qu' elle respecterait les règles actuelles pour le moment . Qantas a déclaré qu' elle respectait pour le moment les règles actuelles . -La a également déclaré qu' elle une provision de £ 4 millions pour l' amortissement des créances douteuses pour le trimestre , une inscription liée à la mise en place de la structure . a également dit qu' elle inscrirait une provision pour dépréciation des créances douteuses supplémentaire de quatre à 4,5 milliards de livres sur le trimestre , une écriture liée à la constitution de la structure de cantonnement . - a également précisé que cette restructuration interne 10 millions de livres de capital , renforçant ainsi sa capacité de prêt . a précisé aussi que cette restructuration interne de 10 à 11 milliards de livres de capital , renforçant ainsi sa capacité à prêter . - suspend deux vendeurs de devises suspend deux traders sur le marché des changes - in Cat in of Oise ( en anglais ) Un cas de rage détecté chez un chat dans le Oise -Il a plutôt dit que leurs amitiés avec le Seigneur leurs jugements à propos de leur rapport . Au lieu de ça , il a déclaré que les relations amicales qu' ils entretenaient avec Lord ont leur jugement , ce qui fait qu' ils n' ont pas signalés ses agissements . -Au contraire , l' euro est enfermé dans ses pays aux taux de change favorables aux exportateurs allemands - quelque chose que les politiciens allemands veulent garder . Le système européen a plutôt fait peser sur ses membres des taux de change favorables aux exportateurs allemands – ce que les hommes politiques allemands veulent conserver . -Au lieu d' être par le retrait du gouvernement américain , les investisseurs ont concentré leur attention sur ce qui est probablement plus important : la Réserve fédérale . Au lieu d' être par l' arrêt des activités gouvernementales aux États-Unis , les investisseurs sont restés concentrés sur ce qui est probablement le plus important : la Réserve fédérale . -L' agence de notation Standard & Poor' s a réduit sa cote de crédit pour le mois de mai 2009 , ce qui a mis en doute la capacité de l' ancienne république soviétique , qui est en récession depuis un an , de répondre à ses obligations financières . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; L' agence de notation Standard & Poor' s a abaissé vendredi la note de solvabilité de l' Ukraine , mettant en doute la capacité de l' soviétique , en récession depuis plus d' un an , à faire face à ses obligations financières . -Vraiment , ce gars devrait recevoir la peine de mort devant quelqu' un . En réalité , cet homme aurait dû être condamné à mort avant les autres . -Des analyses récentes ont permis de distinguer quatre espèces . Les récentes analyses ont permis de distinguer quatre espèces . -Les recommandations pour la thérapie médicamenteuse ou hormonale sont fondées sur l' âge de l' enfant , le taux de développement , le taux de croissance et la maturité affective . Les recommandations en matière de traitement médicamenteux et hormonal sont basées sur l' âge de l' enfant , le taux de développement , le taux de croissance et la maturité affective . -Les inscriptions auront lieu le samedi matin à partir de 9h et les étapes de qualification seront suivies peu après 10 h , la finale étant fixée à 15h et la à 16h . Les inscriptions sont prises sur place samedi matin à partir de 9 heures et les phases de qualification débuteront dans la foulée , à 10 heures , avant la finale prévue à 15 heures et la remise des prix à 16 heures . -L' autorité réglementaire sur les appels téléphoniques appartient à la Federal Communications Commission , et non à la FAA . Le pouvoir réglementaire concernant les téléphones portables appartient à la Federal Communications Commission et non à la FAA . -Les relations entre les États-Unis et l' Allemagne ont été à la suite des accusations selon lesquelles la avait manipulé le téléphone de la chancelière Angela Merkel . Les relations entre les États-Unis et l' Allemagne ont été mises à rude épreuve à la suite de plaintes selon lesquelles la NSA avait mis sur écoute le téléphone portable de la chancelière allemande Angela Merkel . -Renault Share price Through Tony Controls Renault en Bourse par l' avertissement de Nissan -Renault a annoncé la plus grosse chute sur l' indice 120 à Paris vendredi après son partenaire , Nissan Motors , a annoncé qu' elle avait réduit son avance de bénéfices nette de près de 20 % , le chiffre d' affaires des traders . Renault accuse vendredi la plus forte baisse de l' indice 120 à Paris après que son partenaire Nissan Motor a annoncé avoir diminué de près de 20 % sa prévision de bénéfice net annuel , rapportent des traders . -Rapport : Obama a estimé qu' il s' agissait d' une affaire de scandale pour Hillary Clinton Rapport : La campagne d' Obama considérée comme laissant tomber Biden pour Hillary Clinton -Les rapports de l' Australie faisaient savoir qu' entre-temps , elle faisait des séjours dans la zone de villégiature de Krabi , dans le sud de la Thaïlande . En Australie , la presse a déclaré qu' en attendant , elle était en vacances dans la région de Krabi , au sud de la Thaïlande . -Selon certaines informations , au moins quatre personnes ont été légèrement blessées . Quatre blessés légers au moins seraient à déplorer . -Des informations indiquent que l' agence de la sécurité nationale américaine sur la société mère brésilienne et a profité de l' accès à des données provenant des émetteurs américains de virus , y compris Google et Yahoo , qui ont la paranoïa des entreprises face à la surveillance de l' Etat à de nouveaux sommets . & # 160 ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; Des rapports , selon lesquels la National Security Agency américaine a la compagnie pétrolière brésilienne et a eu accès à des données détenues par des fournisseurs américains de services de stockage dans le Cloud , notamment Google et Yahoo , ont plus que jamais suscité la paranoïa des entreprises à l' égard de la surveillance d' État . -Les représentants de Samsung , de , de HTC et de Rockstar n' ont pas pu être retrouvés . Il n' a pas été possible de joindre les représentants de Samsung , , HTC et Rockstar . -La République a essayé de régler les problèmes financiers de la compagnie aérienne dans le cadre de la vente de la compagnie aérienne . Republic a tenté de redresser les finances de Frontier en vendant la compagnie aérienne . -L' républicain ne peut pas expliquer la possibilité d' écoutes de responsables étrangers , ni de drones qui tuent des enfants innocents à l' étranger . L' obstruction républicaine ne peut pas expliquer la mise sur écoute des dirigeants étrangers , ni les attaques d' enfants étrangers innocents par des drones . -Les chercheurs sont connus depuis longtemps pour cette population , mais ils ne savaient pas qu' ils faisaient partie d' une nouvelle espèce qui ne vit que dans cet endroit . Si cette population était déjà connue depuis longtemps par les chercheurs , ils ne savaient pas qu' elle faisait partie d' une nouvelle espèce , présente seulement à cet endroit . -Résultat : le Front de la Mer du , le Front national du vient d' être élu lors d' une élection partielle de haut niveau , amenant les dirigeants du parti socialiste à la troisième place . Résultat : le parti d' extrême droite de Marine Le Pen , le Front National , vient de remporter une élection partielle cruciale , renvoyant les socialistes au pouvoir à la troisième place . -Les résultats n' ont pas dépassé les prévisions et ont expédié des actions à plus de 8 % sur la Bourse de Toronto . Les résultats n' ont pas été à la hauteur des prévisions et le cours des actions a reculé de 8 % à la Bourse de Toronto . -Les recettes ont légèrement diminué , passant de 4,2 milliards de dollars à 4,1 milliards de dollars . Les revenus ont légèrement reculé de 4,2 à 4,1 Md $ . -Les revenus ont chuté de 9 % et sa capacité de vol a chuté de près de 13 % au premier trimestre , en raison des résultats financiers annoncés mercredi par la compagnie de chemin de fer Virgin Holdings Holdings Inc . Les recettes ont chuté de 9 % et sa capacité de vol a diminué de presque 13 % au cours du premier trimestre , selon les résultats financiers publiés mercredi par sa société mère , Republic Airways Holdings Inc . - . Le groupe de rock Arctic a reporté un concert à Glasgow après que l' on a diagnostiqué une à son chanteur . -Rockstar , le consortium qui a racheté les brevets de Nortel pour 4,5 milliards de dollars , a poursuivi Samsung Electronics Trading Ltd , LG Corporation , et quatre autres compagnies pour contrefaçon de brevet au tribunal de district des États-Unis au Texas . Rockstar , le consortium qui a acquis les brevets Nortel pour 4,5 Md $ , a déposé plainte à l' encontre de Samsung Electronics Co Ltd , HTC Corp , et quatre autres sociétés pour violation de brevet devant la Cour d' assises des États-Unis au Texas . -Rockstar est détenue conjointement par Apple , Microsoft , Blackberry , Ericsson et Sony . Rockstar est détenu conjointement par Apple , Microsoft , Blackberry , Ericsson et Sony . -Rockstar est à la recherche de dommages-intérêts supplémentaires contre Google , car il prétend que la violation par Google des brevets est intentionnelle , selon la plainte . Rockstar réclame des dommages et intérêts plus important à l' encontre de Google , car il prétend que la violation de brevets de Google est obstinée , selon le plaignant . -Rohan , directeur du commerce extérieur européen à , a pris la fuite cette semaine , tandis que Matt Gardiner , ancien grossiste à la Bourse de Barclays et UBS , a été suspendu par la Standard Chartered cette semaine . Rohan , responsable européen du trading de change au comptant chez , a été mis en congé cette semaine , tandis que Matt Gardiner , trader senior de devises chez Barclays et UBS , a été suspendu par Standard Chartered cette semaine . -Des tests systématiques ont révélé que les produits traités en Roumanie en janvier et vendus par des magasins de la chaîne Home et des chaînes de conservation de la qualité couvraient entre 1 et 5 % de l' ADN des chevaux . Des tests de routine ont révélé que les produits , élaborés en janvier en Roumanie et vendus par les chaînes Home et Quality Save , contenaient de l' ADN de cheval à hauteur de 1 à 5 % . - de la clôture des unités d' intervention d' urgence de l' autour de la fermeture des urgences de l' -La Royal Bank of Scotland a suspendu deux agences de change de sa division commerciale en raison de deux cas connus de la part d' un autre , le fait que l' ensemble de la chaîne des régulateurs dans la tentative de manipulation du marché des devises gagne rapidement de la vigueur . Royal Bank of Scotland a suspendu deux traders de sa division marché des changes selon deux sources proches du dossier , ce qui montre une nouvelle fois que l' enquête mondiale des régulateurs sur les suspicions de manipulations du marché des devises commence à porter ses fruits . -Ruby affirme qu' il n' est pas " stupide " que la police ait jamais cherché à fouiller le véhicule ou la maison de Bell , ou qu' elle ait pris contact avec son téléphone , déclarant que le chef de la police de Bill Blair a sciemment décidé de ne pas agir contre le maire . Me Ruby a qualifié d ' " inexplicable " le fait que la police n' ait jamais fouillé le véhicule de Rob Ford ou son domicile , ou mis sur écoute son téléphone , soutenant que le chef de police Bill Blair a choisi en connaissance de cause de ne pas agir contre le maire . -Il a été emmené chez les vétérinaires pour un examen de contrôle et il a été considéré comme étant d' ordre autre que l' épuisement et la déshydratation . Ruby a été conduite chez le vétérinaire pour un contrôle ; son état général était satisfaisant , mis à part l' épuisement et la déshydratation . -Des rumeurs concernant une affaire de Clarke ont été soulevées en premier lieu par les bureaux de la où une personne qui a été sexuellement intéressée par Mme a déclaré : " Ne perdez pas votre temps , elle est avec Charles " . Les rumeurs sur une liaison impliquant Clarke ont été reprises en premier par le bureau de la rédaction de lorsqu' une source , éprouvant une attirance sexuelle envers Mlle , a déclaré qu' on lui avait dit : « Ne perds pas ton temps avec elle , elle est avec Charles . » -La Russie , qui est responsable d' un quart des exportations ukrainiennes , a fait valoir que , si une zone de libre-échange avait été créée entre l' UE et Kiev , elle devrait renforcer ses contrôles des frontières pour les produits importés . La Russie , qui représente le quart des exportations ukrainiennes , a prévenu qu' en cas de création d' une zone de libre échange entre l' UE et Kiev , elle devrait renforcer ses contrôles à la frontière pour les marchandises importées . -Par ailleurs , la note verbale note " l' incertitude " liée à la signature éventuelle d' un accord d' association entre l' Ukraine et l' UE qui , pour Bruxelles , est subordonné à la libération du leader de l' opposition , . S & P relève en outre l ' " incertitude " liée à la possible signature d' un accord d' association entre l' Ukraine et l' UE , que Bruxelles conditionne à la libération de l' opposante Timochenko . -Les ventes des camions et Sierra , qui ont été révisées pour l' année modèle 2014 , ont augmenté d' environ 20 pour cent au cours des 10 premiers mois de l' année , GM a déclaré vendredi . Les ventes de et Sierra , qui ont été pour le modèle 2014 , ont augmenté d' environ 20 % au cours des 10 premiers mois de l' année , a déclaré GM vendredi . -Samsung , et HTC tous les téléphones qui fonctionnent sur le système d' exploitation Android de Google , qui rivalisent fortement avec les produits Apple et les produits mobiles de Microsoft . Samsung , et HTC fabriquent des téléphones qui fonctionnent sous le système d' exploitation Android de Google , qui livre une concurrence féroce aux produits mobiles Apple et Microsoft . -Dire beaucoup de mythes serait " trop sauvage , beaucoup trop scandaleux et , dans bien des cas , trop pour être enseigné dans les écoles " , préconise une " sélection soigneuse " d' activités adaptées à l' âge . que de nombreux mythes seraient « bien trop violents , bien trop scandaleux et dans certains cas , bien trop obscènes pour être enseignés à l' école » , a préconisé une « sélection minutieuse » des œuvres adaptées à l' âge . -Scarborough lui-même a été condamné à quatorze ans d' emprisonnement après avoir plaidé coupable de complot en vue de fournir de l' héroïne , de la cocaïne et du cannabis . Scarborough a lui-même été emprisonné pendant 14 ans après avoir plaidé coupable de complicité de trafic d' héroïne , de cocaïne et de cannabis . -Le transport scolaire : plainte Transport d' écolier : la plainte jugée recevable -Les écoles ont recommandé de se concentrer davantage sur les mathématiques , l' orthographe et la grammaire . Les écoles sont encouragées à se concentrer davantage sur les mathématiques , l' orthographe et la grammaire -Les scientifiques ont développé un vaccin qui pourrait prendre le plaisir de fumer une cigarette . Des scientifiques ont développé un vaccin qui pourrait supprimer le plaisir que l' on prend à fumer une cigarette . -Les scientifiques ont mis plus de lumière sur la façon dont les mouvements de la queue d' un chien sont liés à son humeur . Des scientifiques viennent de mettre en lumière la façon dont les mouvements de la queue d' un chien sont liés à son humeur . -Scotland Yard a " donné un avis de mise en garde à un homme de 40 ans qui lui a demandé d' assister à un poste de police à la date et à l' heure de notre choix " , a déclaré un porte-parole . Scotland Yard a « délivré un avis de remise à l' encontre d' un homme de 40 ans qui lui demande de se présenter à un commissariat de police , à la date et l' heure de son choix » , a déclaré un porte-parole . -Scotland Yard a déclaré qu' il avait reçu un " avis de mise en garde " sur l' autre de l' Australie qui l' avait obligé à comparaître à un poste de police , ajoutant que le fait de ne pas l' avoir fait pourrait l' obliger à s' arrêter . Scotland Yard a déclaré qu' elle avait délivré un « avis de remise » à l' encontre de l' Australien de 40 ans , lui demandant de se présenter à un commissariat de police , ajoutant que s' il ne le faisait pas , il serait susceptible d' être arrêté . -Scott Brown , capitaine de l' équipe celtique de Glasgow , a reçu son appel et n' a pas remporté les deux matchs de qualification de son prochain match de la Ligue , contre Ajax et Milan AC . Scott Brown , le capitaine du Celtic Glasgow , a vu son appel rejeté et sera bien suspendu pour les deux prochains matches de Ligue des champions de son club , contre l' Ajax et l' AC Milan . -Scott Brown Opposition L' appel de Scott Brown rejeté -Le secrétaire écossais a ajouté : " L' annonce d' aujourd' hui est une bonne nouvelle pour l' Écosse " . Le secrétaire d' État britannique pour l' Écosse , , a ajouté : « L' annonce d' aujourd' hui est une bonne nouvelle pour l' Écosse . » -Le ministre des Transports allemand , Keith Brown , a déclaré qu' il était " excité " à travailler avec le gouvernement britannique sur le plan . Le ministre écossais des Transports , Keith Brown s' est dit « excité » à l ’ idée de travailler avec le gouvernement britannique sur le projet . -Voyant son père et un grand nombre de ses concitoyens , est résolu à faire quelque chose . Après avoir vu son père et autant de ses compatriotes souffrir , le Dr était déterminé à essayer de changer le cours des choses . -Le sénateur Chris Murphy du Connecticut a déclaré qu' il s' était entretenu avec les membres du Parlement européen et les autres membres de cette semaine et s' inquiétait de leurs menaces d' arrêter de participer à des organisations antiterroristes en raison de la frustration suscitée par la surveillance de l' Agence nationale de sécurité . Le sénateur Chris Murphy du Connecticut a indiqué qu' il s' était exprimé devant les membres du Parlement européen et d' autres cette semaine et qu' il était inquiet des menaces qu' ils avaient proférées concernant un possible retrait des organisations anti-terroristes en raison du sentiment de frustration que suscite la surveillance de la National Security Agency . -Feu grave à la boutique Important incendie dans un commerce -Sept points ajoutés à sa licence lui ont valu une interdiction de conduire six mois . Les sept points retirés sur son permis ont conduit à un retrait de permis de 6 mois . -Plusieurs dizaines d' habitants , dont la majorité sont des descendants des mutins du Bounty , vivent encore sur Pitcairn , avec 95 % de leurs revenus selon la générosité de Londres . Quelques dizaines d' habitants , en majorité des descendants des du Bounty , habitent encore Pitcairn , et leurs revenus dépendent à 95 % de la générosité de Londres . -Plusieurs facteurs , comme la pénurie de pharmaciens dans les hôpitaux ou la prédominance des jeunes femmes dans la profession , font que des situations comme celles vécues en 2012 sont difficiles à prévoir . Plusieurs facteurs , comme la pénurie de pharmaciens en hôpital ou une prédominance de jeunes femmes dans le métier , rendent des situations telles que celle vécue en 2012 difficiles à prévoir . -Plusieurs suspects notoires ont récemment fait l' objet d' aveux à la télévision . Plusieurs suspects importants ont récemment fait des aveux télévisés . -Plusieurs des comédiens qui ont participé au festival cette année ont vu le jour par des canaux non traditionnels , comme des spectacles sur de petits réseaux , comme Central , Silver et Spike . Plusieurs des comédiens participant au festival de cette année sont passés par des circuits non traditionnels , en présentant par exemple des spectacles sur de petites chaînes de télévision , comme Central , FX et Spike . -Plusieurs associations de soins palliatifs ont profité de l' occasion pour réitérer leur désapprobation . C' est l' occasion pour plusieurs associations de soins palliatifs de réaffirmer leur désaccord . -Plusieurs États et villes sont néanmoins en train de faire marche arrière . Plusieurs États et grandes villes font néanmoins des progrès de leur côté . -Plusieurs commerçants ont indiqué que les actions de Renault avaient été touchées par l' avertissement de ce matin . Plusieurs traders ont rapporté que l' action Renault était affectée ce matin par le " warning " . -Partagez avec vous vos réflexions dans les commentaires ci-dessous . Dites-nous ce que vous en pensez dans les commentaires ci-dessous . -Les parts de Bombardier , qui ont également annoncé que le directeur général de Microsoft , Patrick , s' à ce poste , ont chuté de 8,5 % à $ CA lors de l' ouverture du journal le jeudi . Le cours des actions de Bombardier , qui a également annoncé que Patrick , chef de la direction financière de Google Inc , au conseil d' administration , a chuté de 8,5 % à dollars canadiens dans l' après-midi de jeudi . -Elle a ajouté : " C' était il y a longtemps , et c' est très gentil " . Elle a ajouté , « C' était il y a longtemps , et il a été très gentil . » -Elle a nommé un moniteur extérieur pour surveiller les changements importants , y compris les réformes politiques , la formation et la supervision , et elle a commandé un programme pilote pour tester les caméras dans certaines enceintes où la plupart des arrêts se sont produits . Elle a désigné un contrôleur extérieur pour superviser les modifications , notamment la réforme des politiques , de la formation et de la supervision , et elle a ordonné la mise en place d' un programme pilote pour tester des caméras portées sur le corps dans les quartiers où se déroulent le plus grand nombre de contrôles . -Elle est revenue de l' école à la peur et à l' agitation pour être le premier parmi ses amis . Elle est rentrée de l' école et bouleversée d' être la première parmi ses amies . -Elle n' a pas déplacé de muscle pendant le secours - elle a été gelée solide . Elle n' a absolument pas bougé pendant le sauvetage – elle était gelée . -Elle est décédée après avoir parlé au téléphone . Morte après m' avoir parlé au téléphone . -Elle a un vagin , mais pas d' utérus ou d' ovaires . Elle a un vagin , mais pas d' utérus ni d' ovaires . -Elle est la fille de l' ancien joueur de la ligue australienne Peter , qui a appelé le Ministère des affaires étrangères à Canberra à aider sa fille . Il s ’ agit de la fille de l' ancien joueur de la Ligue australienne de rugby Peter , qui a fait appel au ministère des Affaires étrangères de Canberra pour aider sa fille . -Elle a dit qu' elle était " heureuse de revenir " . Elle affirme être " heureuse d' être de retour " . -Elle veut utiliser les élections de l' année prochaine de l' UE pour créer un bloc à travers le Parlement européen . Elle veut se servir des élections européennes de l' année prochaine pour créer un bloc unique et au sein du Parlement européen . -Elle alors des biscuits . Elle a mangé des biscuits après . -Elle a été détenue dans des cellules de la police locale avant l' audience . Elle a été détenue dans une cellule du commissariat local avant l' audience devant le tribunal . -Elle était juste au milieu de la falaise - du haut et du bas . Elle était en plein milieu de la falaise , à 15 m du sommet et 15 m du sol . -Elle a été repérée trois jours plus tard par un coincé dans la carrière . Elle a été repérée trois jours plus tard par une personne qui son chien coincée dans la carrière -Si une mère est si sa fille commence à pousser des bourgeons du sein et des cheveux à 7 ou 8 ? Une mère doit-elle s' inquiéter si les seins et les poils de sa fille commencent à pousser à 7 ou 8 ans ? -Cette élection doit-elle être décidée deux mois après que nous nous sommes abstenus de voter ? Cette élection devrait-elle être décidée deux mois après que le vote est terminé ? -Silvio Berlusconi menace de retirer son soutien au gouvernement si le Sénat , dans lequel il a plus d' opposition que les partisans , décide de l' alléger de ses fonctions . Silvio Berlusconi menace de retirer son soutien au gouvernement si le Sénat , où il compte plus d' adversaires que de partisans , prononce sa destitution . -De même , ses funérailles étaient un adieu centré sur le peu de lumière des bougies autour de son cercueil . De même , ses funérailles furent un adieu glacial focalisé autour de la maigre lueur des chandelles qui son cercueil . -De même , au Royaume-Uni , les activités du centre de loisirs de vol ont bien fonctionné , tandis que les entreprises clientes ont dépensé moins . De même au Royaume-Uni , le secteur des voyages de loisirs de Flight Centre a enregistré de bons résultats tandis que les clients d' affaires ont dépensé moins . -Depuis 2008 , M. et son groupe de volontaires ont aidé près de 32,000 personnes . Depuis 2008 , lui et son groupe de bénévoles ont aidé près de 32 000 personnes . -Étant donné que l' Écosse n' est pas dans l' espace Schengen , la poursuite de la peine de mort serait une solution de rechange pour que Bruxelles puisse bénéficier de tout ce que l' on attend de l' écossais d' Ulster . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Puisque l' Écosse ne fait pas partie de l' espace Schengen pour le moment , un non-respect continu des règles serait une mince concession que Bruxelles pourrait offrir en contrepartie de tout ce qu' elle veut obtenir des Écossais . -Depuis son arrivée à Washington le mercredi , Nouri Al a rencontré plusieurs membres du gouvernement et des membres du Congrès . Depuis son arrivée mercredi à Washington , Nouri Al a multiplié les rencontres avec l' exécutif , et les élus du Congrès . -Depuis qu' il a pris sa retraite en juin 2007 au terme d' une dizaine d' années comme chef , M. Blair a évité beaucoup de discuter de la politique britannique , limitant la plupart de ses remarques aux affaires étrangères et son rôle d' envoyé du Quartet des de paix au Moyen-Orient . Depuis son départ en juin 2007 après une décennie à la tête du pays , M. Blair a largement évité de discuter de la politique britannique , limitant ses commentaires aux affaires étrangères et à son rôle d' envoyé spécial du Quatuor pour le processus de paix au Moyen-Orient . -Comme le Gouvernement fédéral reconnaît le mariage entre personnes du même sexe , " tous les conjoints de militaires ont droit à un titre de carte d' identité du Ministère de la défense et aux avantages connexes " , a déclaré le secrétaire à la défense à New York , dans un discours à l' League pour la lutte contre l' antisémitisme . Depuis que l' État fédéral a reconnu le mariage homosexuel , " tous les conjoints de militaires ont droit à une carte d' identité du département de la Défense et aux prestations qui viennent avec " , a rappelé le ministre à New York dans une allocution devant l' League de lutte contre l' antisémitisme . -Depuis le début de l' année scolaire , le Conseil scolaire de la région métropolitaine de Sherbrooke ( ) a demandé un montant de 150 $ par élève ( jusqu' à 300 $ par famille ) pour les étudiants qui utilisent le transport scolaire pour se rendre à deux adresses , un service que l' organisation peut offrir lorsqu' elle est en mesure de le faire . La Commission scolaire de la ( ) exige depuis le début de l' année scolaire des frais de 150 $ par élève ( jusqu' à un maximum de 300 $ par famille ) pour les élèves bénéficiant du transport scolaire à deux résidences , un service que l' organisation offre lorsqu' elle est en mesure de le faire . -Depuis la reprise des hostilités entre les FARDC et les rebelles le 25 octobre , les deux parties n' ont donné aucune indication sur les pertes . Depuis la reprise des combats entre FARDC et rebelles le 25 octobre , les deux belligérants n' ont donné aucun bilan des pertes en vies humaines . -Depuis la reprise des affrontements du 23 mars entre le ( ) et l' armée , n' a pas participé directement aux combats , mais elle fournit aux troupes du gouvernement un soutien fort en matière de renseignement , d' observation et de planification . Depuis la reprise , vendredi , des affrontements entre le Mouvement du 23 Mars ( ) et l' armée , la ne participe pas directement aux combats , mais elle fournit aux troupes gouvernementales un soutien déterminant en matière de renseignement , d' observation et de planification . -Depuis le début de 2013 , l' Agence des services frontaliers du Canada à l' aéroport de a procédé à 173 saisies de drogues , dont 10 saisies de cocaïne totalisant 44 kilogrammes . Depuis le début de 2013 , les agents des services frontaliers à l' aéroport ont effectué 173 saisies de drogues , dont 10 saisies de cocaïne pour un total de 44 kilogrammes . -Depuis le début des combats importants en Afghanistan , l' armée s' est efforcée de déterminer quelle latitude elle peut accorder aux soldats blessés qui veulent rester dans l' armée , mais qui ne sont pas capables de se battre . Depuis le début des combats majeurs en Afghanistan , l' armée peine à déterminer quelle latitude elle peut accorder aux soldats blessés qui veulent rester dans les rangs , mais qui ne sont pas aptes au combat . -Depuis lors , plusieurs centaines de ont été dans les collines agricoles de , et , près de et de la ville voisine de , à une altitude de près de 2.000 mètres . Depuis , quelques centaines d' irréductibles du , sont retranchés à près de 2.000 mètres d' altitude sur les collines agricoles de , et , proches de et de la localité voisine de -Depuis qu' ils n' ont pas pu prendre le soleil avec Bob ou Richard , Daniel et Benoît , John et ont choisi un film américain d' avant-garde , Oscar Isaac , âgé de 33 ans , et l' actrice américaine Justin Timberlake . À défaut de pouvoir embaucher Dylan ou le trio Peter , Paul and Mary , Joel et ont jeté leur dévolu sur l' étoile montante du cinéma américain , Oscar Isaac , 33 ans , mais aussi sur la star planétaire pop Justin Timberlake . -Le journaliste Lou Reed a été tué alors que ses camarades de travail , son épouse , Laurie Anderson , publiée dans une lettre publiée par le journal régional East Hampton , pour laquelle il était destiné aux résidents de la ville , la localité où le couple avait une résidence . Le chanteur Lou Reed est mort alors qu' il faisait des exercices de chi , a révélé jeudi son épouse Laurie Anderson dans une lettre publiée par le journal régional " East Hampton Star " et destinée aux habitants de la ville de Springs , où le couple avait une maison . - a fait la transparence de la collaboration intensive entre les services de renseignement américains et les entreprises . a rendu transparente la collaboration intensive entre les services de renseignement américains et les entreprises . - prêts à " coopérer " avec l' Allemagne à propos de la surveillance des États-Unis est prêt à « coopérer » avec l' Allemagne sur la question de la surveillance américaine -Ainsi , un voyage de 1.000 milles acheté auprès d' une agence de voyages en ligne 250 milles . Ainsi , un voyage Frontier 000 miles acheté auprès d' une agence de voyage en ligne permettrait de gagner 250 miles . -Donc , un , alors . Il s' agit donc d' une histoire qui n' en est pas une . -Donc encore , No Words , Friends of The Online Game , acteur Button jouait dans son casino favori en 1912 alors qu' il avait été acclamé par un jet d' American Airlines pour avoir refusé de voler le dispositif alors que l' avion était à la porte . Donc toujours pas de Words With Friends , le jeu en ligne style auquel jouait l' acteur Baldwin sur son smartphone en 2011 lorsqu' il a été expulsé à grand bruit d' un avion d' American Airlines après avoir refusé d' éteindre son appareil alors que l' appareil était garé à la porte . -Ainsi , lorsque la bouche réchauffe la protéine , elle augmentera le niveau de pH et la crème glacée se . Donc lorsque votre bouche réchauffe la protéine , le niveau de pH augmente et la glace se met à briller . -Donc chaque me coûte environ £ 140 . Chaque boule me coûte donc environ 140 £ . -Peu après sa naissance en 1976 , le corps et les testicules de l' ange furent enlevés par chirurgie et il fut élevé en tant que fille . Donc peu de temps après sa naissance en 1976 , Bruce a subi une ablation chirurgicale de l' organe externe et des testicules et a été élevé comme une fille . -Ainsi , nous pourrons combler les lacunes que nous avons connues l' an dernier " . Cela viendra donc combler nos départs de l' année passée . -Donc cette année , le l' a fait . Cette année , les se sont donc . -Nous sommes donc très heureux de travailler en partenariat avec le gouvernement britannique en vue d' étudier les options pour le transport ferroviaire à grande vitesse en Écosse , afin de créer des avantages pour tous et de compléter la ligne que le gouvernement écossais met déjà en place . « Nous sommes donc excités à l' idée de travailler en partenariat avec le gouvernement britannique afin d' examiner les options visant à amener le réseau de train à grande vitesse jusqu' en Écosse , créant un avantage pour tous et complétant la ligne que le gouvernement écossais est déjà en train de planifier . -Alors , que faire si vous avez été capable d' intercepter cela et de l' arrêter avant que cela ne se produise ? Et si vous pouviez intercepter leurs communications et les arrêter avant qu' ils n' agissent ? -Les procureurs doivent respecter les normes les plus rigoureuses en matière d' intégrité et donner confiance en la confiance du public . Les avocats devraient observer les règles d' intégrité les plus strictes et instaurer un climat de confiance auprès du public . -Quelque 10.000 candidats se sont présentés aux auditions pour présenter le jury pour cette 10e saison : le , le Sinclair , le André et Olivier Bas . Quelque 10 000 candidats se sont présentés aux auditions de cette 10e saison , pour affronter le jury : , le méchant Sinclair , le perché André et Olivier Bas . -Certains Français nous ont emmenés à l' Ambassade de France . Des Français nous amènent à l' ambassade de France . -Certains banquiers ont essayé de s' emparer de l' affaire en disant que le vaste marché des changes liquide et presque inexistant est pratiquement impossible à manipuler , mais les traders de haut niveau disent que cela n' est pas nécessairement vrai . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Certains banquiers ont essayé de minimiser l' affaire en disant qu' il est presque impossible de manipuler le vaste marché des changes fortement liquide , mais les traders seniors expliquent que cela n' est pas nécessairement vrai . -Certains commentateurs ont interprété son recours à la force comme la tentative d' un mouvement en difficulté pour gagner des concessions et des profits financiers du gouvernement . Certains commentateurs ont interprété son utilisation de la force comme la tentative d' un mouvement malade d' obtenir des concessions et un trésor de guerre de la part du gouvernement . -Certaines de ses communications ont sans aucun doute été légitimes car il s' agissait de son avocat . Certaines de ses communications seront sans aucun doute des communications légitimes car c' était son avocat . -Certains des plus grands tunnels ont été découverts après la récolte de cannabis du Nicaragua en juillet dernier , qui présente des cartels de la drogue avec un défi de faire passer rapidement leur production aux consommateurs . Certains des plus grands tunnels ont été découverts suite à la récolte de marijuana dans le centre du Mexique en octobre , posant ainsi un défi pour les cartels de la drogue qui doivent rapidement livrer leurs produits aux consommateurs . -Certaines des plus grandes banques du monde , comme UBS , Barclays , la Deutsche Bank et , ont confirmé leur volonté de coopérer avec les régulateurs dans les enquêtes sur le plus grand marché financier au monde , où $ les mains chaque jour . Certaines des plus grandes banques au monde , notamment UBS , Barclays , Deutsche Bank et , ont confirmé qu' elles coopéraient avec les régulateurs dans le cadre des enquêtes menées sur le marché financier le plus important au monde , où 5,3 billions de dollars US changent de main chaque jour . -Certaines personnes peuvent mourir parce qu' elles attendent . Des gens peuvent mourir parce qu' ils attendent trop longtemps . -Certaines études indiquent que plus d' un tiers des passagers oublient ou ignorent les instructions pour éteindre leurs appareils . Certaines études indiquent qu' au moins un tiers des passagers oublient d' éteindre leurs appareils , ou le font délibérément . -Certaines choses me dérangent , d' autres moins . Certaines choses m' , d' autres moins . -Certains planificateurs de transport , cependant , s' interrogent sur le fait que tout ce que l' on peut dire au juste le mille , c' est simplement une distraction géante . Toutefois , certains planificateurs des transports se demandent si tout le débat sur le paiement au mile n ’ est pas simplement une énorme diversion . -Certains utilisent de jolies projections d' images et une vraie histoire , comme " 1789 : les de la Bastille " . Certains utilisent de ­ belles projections d' images et une ­ véritable histoire comme dans 1789 , les de la Bastille . -Quelqu' un du parlement allemand nous dit qu' il faut construire un Allemand Google . Un député du parlement allemand dit que nous devrions créer un Google allemand . -Parfois , certains rêves me touchent différemment et ont une impression sur mon esprit ; en fait , ils continuent de vivre et de faire partie de ma vie . Parfois , certains rêves me portent au contraire et s' inscrivent dans mon histoire et effectivement , ils continuent à vivre et à partager ma vie . -Parfois , mes croyances sont différentes de lui , alors je me demande toujours si ce que je fais est conforme à sa façon de voir les choses . Si j' ai mes convictions parfois différentes des siennes , je me demande toujours si mes agissements sont conformes à sa vision des choses . -Parfois , ils ont tout simplement besoin de l' argent très rapidement . Parfois , ils ont juste besoin d' argent très rapidement . -Sony a chuté de plus de 11 % à yens . Sony a chuté de plus de 11 % à 1 668 yens . -Peu après la descente , Scarborough était en contact régulier avec qui était au dîner dans le stade de curling Red , à Park . Peu de temps après le dépôt , Scarborough a été régulièrement en contact téléphonique avec qui était à un dîner au stade de foot d' Park , enceinte du club des Blackburn . -Sorensen , ancien responsable de la société Midwest Airlines , a récemment pris l' avion et s' est demandé ce qu' il faisait à la porte , car les passagers avaient les frais de bagages inhabituels de l' équipage . Sorensen , un ancien cadre de Midwest Airlines , a récemment voyagé sur Spirit et s' est demandé ce qu' il allait trouver à la porte d' embarquement au moment où les passagers les frais de bagages à main inhabituels imposés par la compagnie . -South Stream est destiné à ajouter de la diversité aux routes d' exportation de la Russie via l' Europe . South Stream devrait permettre de diversifier les voies d' exportation de la Russie vers l' Europe . -L' espace , par comparaison , a été beaucoup plus solennel . Space , à côté , était beaucoup plus grave . -Prenant la parole lors de l' été dernier , Michael , secrétaire de l' éducation , a déclaré qu' il y avait un consensus général selon lequel nous devions réformer notre système d' examen afin de rétablir la confiance du public , ajoutant que " l' " serait plus difficile , plus ambitieux et plus rigoureux . S' exprimant au cours de l' été , Michael , ministre de l' Éducation , a déclaré qu' il y avait un « large consensus quant au besoin de réformer notre système d' examens pour restaurer la confiance du public » , en insistant sur le fait que les seraient « plus stimulants , plus ambitieux et plus rigoureux » . -S' exprimant au nom d' une conférence publique ouverte à Londres en , M. a dit : " Je suis certain que le président et le gouvernement d' un autre pays ont connu des choses qui se sont toutes déroulées dans le cadre d' un pilote automatique , parce que cela a eu lieu à la fin de la Seconde Guerre mondiale et dans les années difficiles de la guerre froide , et bien sûr , 9 / 11 " . S' exprimant lors d' une conférence intergouvernementale à Londres par liaison vidéo , M. Kerry a déclaré : « Il est indéniable que le Président , moi-même et d' autres membres du gouvernement avons pris connaissance de certaines choses en mode pilote automatique parce que nous en avions la possibilité , dès la Seconde guerre mondiale et jusqu' aux années difficiles de la Guerre froide , puis bien sûr le 11 septembre . » -Richard , spécialiste en sauvetage technique , a déclaré : " C' est dans une situation précaire . Le spécialiste des secours techniques Andy Clayton a déclaré : « Elle était dans une situation précaire . » -Le spécialiste des services techniques de sauvetage Peter Lau a déclaré : " Ruby a connu une très grande chance d' évasion " . Le spécialiste des secours techniques Peter Lau a déclaré : « Ruby a eu beaucoup de chance de s' en sortir indemne . » -Une compagnie de défense navale spécialisée a confirmé que ses équipes , avec , ont été envoyées au département de et travaillent actuellement sur le porte-avions . De son côté , l' entreprise , spécialiste du naval de défense , assure que ses équipes et celles d' , dépêchées dans le Var pour l' occasion , " interviennent actuellement sur le porte-avions " . - Jump Plus à Bogota saut en " " au-dessus de Bogota -Spirit Airlines Inc. a commencé les premiers frais de transport de passagers il y a trois ans , et Air l' a suivi plus tard . Spirit Airlines Inc. avait appliqué les premiers frais de bagages à main il y a trois ans , et la compagnie low-cost a suivi un peu plus tard . - a sauté d' un hélicoptère au-dessus de Bogotá , la capitale de la Colombie , jeudi . Le sportif a sauté jeudi d' un hélicoptère au-dessus de Bogota , la capitale colombienne . -La ressemblance de avec sa soeur aînée est frappante : une , une couleur et . La ressemblance de Martin avec son aînée est frappante : une silhouette de , un teint et des origines . - Martin n' est pas fait comme le sujet des conversations . Martin n' a pas fini de faire parler d' elle . -Le personnel a établi des preuves qu' il n' y avait pas de violations , dont le Seigneur , seul , avec des enfants , en les en privé , en les tenant sur le genoux , en faisant savoir qu' il aimait et en leur laissant jouer avec son téléphone portable . Les membres du personnel ont témoigné qu' ils avaient observé certains manquements , notamment le fait que Lord restait seul avec les enfants , qu' il les gardait en privé , les faisait asseoir sur ses genoux , leur disait qu' il les aimait et les laissait jouer avec son téléphone portable . -Stage Animation variété de comédies musicales anglaises en les adaptant au français . Stage ­ Entertainment des comédies musicales de variété en les adaptant en français . -Les organes législatifs de l' État ont récemment mis à jour les règles pour permettre aux écoles d' accroître l' appât du gain . Les assemblées législatives des États ont récemment actualisé les règlements pour permettre aux écoles d' avoir plus facilement de l' en stock . -Steinberg a débuté en tant qu' humoriste , mais est devenu un réalisateur de télévision et de cinéma , ainsi qu' un historien de l' humour non officiel . Steinberg a commencé en tant que comédien de , mais il est devenu un réalisateur de cinéma et de télévision accompli , ainsi qu' un historien officieux de la comédie . - . Rochelle a d' abord été jugée coupable par un Tribunal de Province de Phuket le 15 juin de fausses déclarations après avoir dit à la police thaïlandaise qu' un chauffeur de taxi local , accompagné de deux autres hommes qui la , l' avait agressée tôt le dimanche 10 juin . -Néanmoins , les analystes sont sceptiques : le premier client peut commencer à exploiter un avion 12 mois après son premier vol . Pourtant , les analystes sont sceptiques quant au fait que le premier client puisse faire voler un avion seulement 12 mois après son vol inaugural . -Pourtant , comme la note d' Obama a chuté en dessous de 45 % cette semaine , le retour en 2008 par ce livre est devenu beaucoup plus difficile . Pourtant , alors que l' indice d' opinions favorables a plongé sous les 45 % cette semaine , il est encore plus difficile de revenir à l' année 2008 à travers ce livre . -Néanmoins , la portée et la sophistication technique des agences américaines d' espionnage mises en lumière par les révélations sont devenues un choc pour certaines sociétés qui avaient pensé que le plus grand risque de surveillance était la Chine . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Pourtant , l' ampleur et la sophistication des agences d' espionnage américaines exposées par les révélations de ont été un choc pour certaines entreprises qui pensaient jusqu' alors que la Chine représentait le plus gros risque en termes de surveillance . -Des études montrent que les écoles anglaises consacrent moins de temps aux maths - 116 heures par an ou trois heures par semaine au temps - que dans la plupart des pays . Des études montrent que les écoles anglaises consacrent moins de temps aux maths ( 116 heures par an , soit 3 heures par semaine pendant l' année scolaire ) que la plupart des autres pays . -Une controverse de plus en plus répandue à propos de l' agence a conduit à la conclusion précoce que l' incident de ce soir était le résultat d' une ciblée . Cette controverse croissante autour de l' agence a provoqué beaucoup de spéculations selon lesquelles l' incident de ce soir était le résultat d' une ciblée . -Il s' agit de la croissance de l' entreprise , qui le de et de deux autres à Singapour , a l' intention de travailler non seulement dans la Malaisie voisine - où il y a quatre magasins , mais aussi à l' extérieur de l' Asie , dit M. Hein Jin Lee , directeur exécutif de la société ( ) , de , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , etc. La hausse est telle dans le secteur du prêt sur gage que , opérateur de la boutique à et de 15 autres boutiques comme celle-ci à Singapour , prévoit de se développer non seulement dans la Malaisie voisine – où il compte déjà quatre boutiques – mais également en dehors de l' Asie , déclare Lee Ching , sa directrice générale . -Une telle perte de patrimoine génétique serait beaucoup plus problématique . Cette perte de patrimoine génétique serait beaucoup plus problématique . -Ces personnes ne comprendraient pas si quelque chose s' est mal passé . Ces gens-là ne comprendraient pas qu' il y ait des erreurs . -C' est le cas de Jim Bruce , un écrivain de 36 ans du Montana , qui est né avec des chromosomes mâles , mais qui sont sujets à des ramifications génitales . Tel est le cas de Jim Bruce , un écrivain de 36 ans du Montana , qui est né avec des chromosomes mâles mais des organes sexuels ambigus . -Les partisans de la modification du programme de indiquent que les changements mettront fin aux pratiques déloyales , une police de police plus efficace et plus efficace et pourraient influer sur la façon dont les autres services de police utilisent la police . Les partisans prônant des changements dans le programme de contrôle et de fouille du disent qu' ils mettront fin aux pratiques injustes , permettront d ’ avoir des forces de police plus efficaces et auxquelles on fait confiance , et pourront changer la façon dont les départements de police usent de la politique . -La Cour suprême confirme le droit à la santé d' Obama La Cour suprême valide la loi de réforme du système de santé d' Obama -La chirurgie le rendait stérile . La chirurgie l' a rendu stérile . -Les victimes et les proches des 202 personnes tuées lors de l' attentat à la bombe de 2002 à Seattle ont réagi avec colère face à la dernière condamnation de ceux qui ont répondu à l' appel de la justice , étant donné que Umar doit être considéré comme un peloton d' exécution . & # 160 ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; Les survivants et les proches des 202 personnes tuées dans les attentats de Bali en 2002 ont réagi avec colère à la peine prononcée à l' encontre du dernier des conspirateurs à être traduit en justice , disant qu' Umar devrait être par un peloton d' exécution . -La Syrie a détruit des équipements essentiels pour la production d' armes chimiques et de munitions explosives toxiques , et le Service de lutte contre les armes chimiques a déclaré jeudi des affrontements acharnés dans le nord du pays , près de l' un des endroits où des agents toxiques semblent être stockés . La Syrie a détruit les équipements essentiels à la production d' armes chimiques et de munitions remplies de gaz toxique , a déclaré l' organisme international de surveillance des armes chimiques jeudi , alors que de violents affrontements faisaient rage dans le nord du pays , à proximité de l' un des sites où des agents toxiques sont censés être stockés . -La Syrie a détruit son potentiel en matière d' armes chimiques , a déclaré le Groupe de surveillance . La Syrie a détruit sa capacité de production d' armes chimiques , déclare l' organisme de surveillance -La Syrie a présenté un plan pour la destruction totale de ses armes chimiques qui doivent être approuvées le mois prochain par le Comité exécutif de l' OIAC . La Syrie a soumis un plan de destruction totale de ses armes chimiques qui a été approuvé le mois suivant par le Comité exécutif de l' OIAC . -Profitant de l' Halloween , une femme a tenté de faire sauter deux kilos de cocaïne dans le pays à l' aéroport de jeudi matin . Profitant de la journée de l' Halloween , une femme a tenté de faire entrer au pays deux kilos de cocaïne dissimulés dans des , jeudi matin , à l' aéroport . -Devant la scène vers les hommes et les femmes dans des chapeaux intelligents , il son capuchon plat à un angle et deux enfants de deux ans autour de l' anneau . Se présentant contre des hommes et des femmes en chapeaux melons élégants , il portait sa casquette plate un peu sur le côté de manière décontractée pour faire défiler son âgé de 2 ans autour de la piste . -Tales of Thor montre que " la force brute ne peut pas être comparée à une subtile " , tandis que les légendes révèlent l' importance d' avoir un rêve . La Légende de Thor pourrait montrer que « la force brutale ne fait pas le poids face à la ruse subtile » , tandis que les légendes révèlent l' importance d' avoir un rêve . -Parlant sous le couvert de l' anonymat , le fonctionnaire ne précise pas si cette surveillance a eu lieu dans le passé . S' exprimant sous le sceau de l' anonymat , ce responsable n' a pas précisé si une telle surveillance avait été mise en place par le passé . -Taxe sur les propriétaires étrangers à abattre la bulle de Londres Un impôt sur les propriétaires immobiliers étrangers pour faire éclater la bulle de Londres -Les adolescents ont la possibilité de se familiariser avec le chant classique à travers cinq petits spectacles créés et réalisés par des artistes régionaux - des spectacles qui contiennent une grande dose d' humour . À travers cinq imaginées et interprétées par des artistes de la région , des vignettes comportant une forte dose d' humour , on permet à des adolescents d' apprivoiser le chant classique . -Les locataires doivent se présenter , car chacun paie en moyenne 100 $ par mois pour le coût de son logement qui va à la ville en taxes . Les locataires doivent aller voter , car chacun d' entre eux paient en moyenne 100 $ par mois sur le prix du logement qui va en taxes à la Ville . -Les tests effectués par l' Institut Pasteur sur un patient soupçonné d' être infecté par le coronavirus se sont révélés négatifs , le ministère de la Santé l' a annoncé . Les tests pratiqués par l' Institut Pasteur sur un patient soupçonné d' être infecté par le coronavirus se sont révélés négatifs , a annoncé le ministère de la santé . -Grâce à l' amélioration de la précision des capteurs et de la capacité de calcul des ordinateurs utilisés pour analyser ces données , ces techniques sont devenues pratiques . Grâce à l' augmentation de la précision des capteurs et de la capacité de calcul des ordinateurs nécessaires à l' analyse des données , ces techniques deviennent opérationnelles . -C' est beaucoup de . Ça fait beaucoup de chiens curieux déguisés en . -Il s' agit d' environ 9 milliards de graines de maïs . Cela représente environ 9 milliards de grains de maïs . -C' est environ 600 millions de livres de barres d' Hershey , , Milk , et Clark Bars . Cela représente environ 272 millions de kg de barres Hershey , , Milk , et barres Clark . -C' est une excellente nouvelle pour les 41 millions de à prendre en charge nos quartiers , selon le Département du Commerce des États-Unis . C' est une super nouvelle pour les 41 millions d' enfants qui vont à la chasse aux bonbons dans leur voisinage , selon le département américain du Commerce . -C' est ma principale lutte . C' est mon principal combat . -Ce n' est pas quelque chose que vous pourriez faire en Amérique comme étudiant en médecine de deuxième classe . Ce n' est pas quelque chose que vous avez l' occasion de faire en Amérique lorsque vous êtes étudiant en médecine en deuxième année . -L' espionnage économique n' est pas une surprise . Il n' est pas surprenant qu' il y ait de l' espionnage économique . -Cela a permis à toutes les compagnies aériennes d' inciter les passagers à acheter directement des billets plutôt que de passer par une agence de voyage en ligne . Cela a incité toutes les compagnies aériennes à encourager les passagers à acheter directement auprès d' eux au lieu de s' adresser à une agence de voyage en ligne . -C' est un inconvénient de coût , comme nous l' avons déjà vu à . Il s' agit d' un inconvénient en termes de coûts , comme nous l' avons déjà vu à . -Ce n' est pas un combat que les jeunes enfants devraient prendre à ce stade . Ce n' est pas une bataille que les jeunes enfants devraient mener à cet . -C' est ce qui m' inquiète le plus . C' est ce qui me dérange le plus . -Cela a duré longtemps , puis un jour il est parti . Ça a duré longtemps puis un jour il est parti . -Cela signifie qu' un passager en ligne à la porte de la frontière pourrait se procurer un sac gratuitement , alors que la personne à la ligne suivante pourrait payer 100 $ pour un même sac . Ainsi , un passager faisant la queue à une porte d' embarquement Frontier pourrait transporter un sac gratuitement , tandis que la personne suivante dans la queue pourrait devoir payer 100 $ pour un sac semblable . -Cette personne recevra 250,000 $ pour poursuivre son travail extraordinaire . Cette personne recevra 250 000 $ pour poursuivre son travail extraordinaire . -Cela suggère que la reprise économique de la Chine pourrait se poursuivre après que la croissance s' est redressée pour revenir à 7,8 % au troisième trimestre , à partir d' un creux à la baisse au cours du premier trimestre de l' année précédente . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Cela laisse supposer que la reprise économique de la Chine pourrait continuer à se raffermir après que la croissance a rebondi à 7,8 % au troisième trimestre , après une baisse que l' on n' avait pas connue depuis deux décennies au trimestre précédent . -C' est quand j' ai fait mon premier appel à mes parents , à ma famille , à mes amis , à la presse , etc. C' est le temps des premiers coups de fil aux parents , à la famille , aux amis , à la presse ... -Cela se produira tout au long du programme de vol. Cela se reproduira tout au long du programme de vol. -Le projet de 325 millions de dollars comprend une subvention d' urgence de millions de dollars . Le plan de 325 M $ comprend une campagne de 31 M $ pour les opérations chirurgicales non urgentes . -L' ordre de rectification est venu de l' Administration de la presse et de la publication , de la radio , du cinéma et de la télévision de Guangdong . L' ordre de « remaniement » émane de l' administration de pour la Presse et l' Édition , la Radio , le Film et la Télévision . -La Cour d' appel du deuxième circuit des États-Unis a déclaré que les décisions du juge seront maintenues en attendant le résultat d' un appel de la ville . La cour d' appel des États-Unis ( deuxième circuit ) a indiqué que la décision de la juge était suspendue jusqu' à l' issue de l' appel interjeté par la ville . - . Mardi , ce patient de 43 ans avait été soupçonné d' être atteint , après être rentré d' Arabie saoudite , un pays où la maladie a déjà fait une cinquantaine de morts . -L' homme de 45 ans a été reconnu coupable de meurtre en masse pour l' attaque de deux boîtes de nuit dans le quartier touristique de , qui a fait 202 morts , dont 88 Australiens , et qui a fait plus de blessés . L' homme de 45 ans a été reconnu coupable de tuerie de masse pour les attentats visant deux boîtes de nuit dans le quartier touristique populaire de , qui ont causé la mort de 202 personnes , dont 88 Australiens , et ont fait de nombreux blessés . -Le New Express a fait un rare appel public à la libération du journaliste Chen . Le New Express basé à Guangzhou a publié un rare appel public pour la libération du journaliste Chen . -Le conseil scolaire de a créé un centre de recherche qui fournira des outils aux enseignants qui , eux-mêmes , viennent d' ailleurs d' ailleurs . La commission scolaire a créé un centre de recherche qui donnera des outils aux professeurs qui , eux aussi parfois , viennent d' ailleurs . -L' actrice a révélé son dernier look à l' âge et à la rue à mercredi et a partagé un peu sur sa page Twitter . L' actrice de 46 ans a dévoilé son nouveau look alors qu' elle se dans L.A. mercredi et a partagé une photo sur sa page Twitter . -Les départements d' incendie de et de Namur ainsi que la direction des sont intervenus . Les pompiers d' et de Namur et la police des sont intervenus . -Le semble être au bord de la défaite , après avoir été chassée des villes du nord-est de la République démocratique du Congo ( RDC ) , qu' il contrôlait depuis le début de la rébellion il y a 20 mois . Le apparaît sur le point d' être battu après avoir été chassé des localités du nord-est de la République démocratique du Congo ( RDC ) qu' il contrôlait depuis le début , il y a 20 mois , de l' insurrection . -The babe has her her long blonde salope to for a Chinese . L' ancienne jolie nana d' Alerte à Malibu a laissé tomber ses longs cheveux contre une coupe blonde platine . -Le bâtiment situé à 15 rue Hébert a posé trois alarmes au service d' incendie . L' immeuble de quatre logements , situé au 15 de la rue Hébert , a fait l' objet de trois alarmes auprès du service d' incendie . -Les services de protection civile ont stabilisé les deux bâtiments . La protection civile de a stabilisé les deux bâtiments . -Le duo semble avoir redécouvert la magie qui les a réunis dans l' île . Le couple semble avoir retrouvé la magie qui les avait unis dans Island . -Les ont dû être fréquemment afin de maintenir les niveaux d' anticorps qui se sont révélés être coûteux et peu pratiques . Les injections devaient être répétées si fréquemment pour que les niveaux d' anticorps restent élevés qu' ils étaient trop onéreux et peu pratiques . -L' homme a dit à la police qu' il avait peur . Le vieil homme , âgé de 86 ans , a expliqué aux gendarmes qu' il avait eu peur . -Les du , qui ont plusieurs centaines d' habitants , se sont retrouvés à une altitude de 2 000 mètres carrés dans les collines agricoles de , et , à proximité de et de , deux villes situées à 80 kilomètres au nord de Goma , la capitale de la province du Nord-Kivu . Les irréductibles du , soit quelques centaines de combattants , étaient retranchés à près de 2000 mètres d' altitude sur les collines agricoles de , et , proches de et , deux localités situées à environ 80 km au nord de Goma , la capitale de la province du Nord-Kivu . -Le à , où une manifestation violente contre l' impôt a eu lieu samedi , a été jeudi matin . Le portique de , autour duquel s' est déroulée samedi une violente manifestation d' opposants à cette taxe , a été démonté jeudi matin . -Le lentement sur votre porche avant est probablement venu de l' Illinois , qui a fait pousser 542 millions de livres de citrouille cette année . Les qui se doucement sur le porche de votre maison venaient probablement de l' Illinois , où 542 millions de ont été cultivées cette année . -Le détient encore 25 % du capital du groupe logiciel qu' il il y a 40 ans . Le milliardaire détient encore 25 % du capital du groupe de logiciels qu' il a il y a 40 ans . -La tactique de a été critiquée par un certain nombre de défenseurs des droits civils . La tactique du contrôle et de la fouille a été critiquée par de nombreux défenseurs des droits civils . -Le est né d' une mutinerie d' avril 2008 menée par des anciens Tutsis , principalement des Tutsis , qui ont été incorporés dans l' armée en 2009 à la suite d' un accord de paix . Le est né d' une mutinerie , en avril 2012 , d' anciens rebelles , essentiellement , intégrés dans l' armée en 2009 après un accord de paix . -L' ARS , comme le , défend le " nouveau modèle d' hôpital " qui a débuté le 6 octobre et devrait continuer à faire passer de 30,000 à 35,000 patients par année . A l' ARS comme à l' , on défend le " nouveau modèle hospitalier " déjà mis en route depuis le 7 octobre et qui devrait continuer d' accueillir 30 000 à 35 000 patients par an . -L' ARS souligne que 30 patients " envoyés " à plusieurs endroits n' ont pas le risque de devenir une " avalanche " pour les autres services d' urgence , où les ressources humaines ont été renforcées de toute façon . A l' ARS , on fait valoir que trente patients " " sur plusieurs sites ne risquent pas de représenter une " avalanche " dans les autres services d' urgences , dont les moyens humains auront par ailleurs été renforcés . -L' American Civil Liberties Union est également profondément préoccupée par la question de la protection de la vie privée . L' American Civil Liberties Union est elle aussi très préoccupée et exprime son inquiétude concernant la protection de la vie privée . -Le Président américain recevra le Premier ministre irakien , Nouri al-Maliki , qui cherche à aider les États-Unis dans la lutte contre la plus forte vague de violence en cinq ans , le 1er novembre de cette année . Le président américain devait recevoir vendredi 1er novembre 2013 le premier ministre irakien Nouri Al , en quête d' aide des États-Unis pour lutter contre la plus forte vague de violence depuis cinq ans . -Les Américains nous sur le niveau commercial et industriel parce que nous les aussi , parce qu' il est dans l' intérêt national de défendre nos entreprises . Les Américains nous sur le plan commercial et industriel tout comme nous les , car il est dans l' intérêt national de défendre nos entreprises . -L' Associated Press rapporte que la cible était missiles . L' Associated Press rapporte que la cible était des missiles de fabrication russe . -Le Ministre britannique des affaires étrangères , William Hague , a invité le président du Conseil de l' Europe , M. Paul Kagame , à faire preuve de retenue , un porte-parole du Foreign Office a annoncé son intention de le faire . Le chef de la diplomatie britannique , William Hague , a invité le président rwandais Paul Kagame à faire preuve de retenue , a annoncé un porte-parole du Foreign Office . -Le musicien britannique est plein de surprises cette année . Le musicien anglais n' a pas fini de surprendre cette année . -Les CDC ont constaté que la prévalence des allergies alimentaires chez les enfants a augmenté de 18 % entre 1997 et 2007 . Le CDC a découvert que la prévalence des allergies alimentaires chez l' enfant avait augmenté de 18 % entre 1997 et 2007 . -Le chef de la direction de et Cable s' est également félicité de l' annonce faite par le gouvernement Harper lors de son discours du Trône le 16 octobre . Le de et Câble a également salué l' annonce effectuée par le gouvernement Harper lors de son discours du Trône , le 16 octobre dernier . -Le chef de la direction a déclaré qu' il n' avait pas accepté les témoignages du personnel qu' il était mal à l' aise de faire rapport à ses supérieurs . Le directeur général a dit qu' il rejetait les témoignages du personnel selon lesquels ils étaient réticents à l' idée de signaler ces manquements à leur supérieur hiérarchique . -Le CRTC organise des consultations avec le public depuis la semaine dernière , et celles-ci seront poursuivies avec l' industrie au printemps prochain . Le CRTC mène des consultations auprès du public depuis la semaine dernière et elles devraient se poursuivre avec l' industrie au printemps prochain . -La Californie a l' intention de remettre en question ce qui pourrait être une citation , car les lunettes rendent la navigation plus facile . La envisage de contester ce qui pourrait être la première citation à comparaître en son genre , en disant que les lunettes connectées à Internet rendent la navigation plus facile . -L' Agence des services frontaliers du Canada ( ASFC ) n' a pas révélé où était la femme qui avait voyagé après avoir été interceptée . L' Agence des services frontaliers du Canada ( ASFC ) n' a pas révélé d' où arrivait la femme lorsqu' elle a été interceptée . -Le Congrès canadien des soins palliatifs , qui se tient à Ottawa cette semaine , arrive plusieurs jours après le vote en faveur du projet de loi sur la mort de au Québec . Le Congrès canadien de soins palliatifs , qui se déroule cette semaine à Ottawa , survient quelques jours après le vote en faveur du principe du projet de loi sur l' aide médicale à mourir au Québec . -L' Église catholique n' a commencé à le tolérer qu' en 1963 , ce qui a limité les efforts déployés pour y faire face . Elle n' est tolérée que depuis 1963 par l' Eglise catholique , ce qui a limité les tentatives de l' apprivoiser . -Le président du Réseau québécois des soins palliatifs rejette également le projet de loi sur la mort de . La présidente du Réseau des soins palliatifs du Québec rejette aussi le projet de loi sur l' aide médicale à mourir . -Le gouvernement chinois estime que le mouvement est responsable des fréquentes flambées de violence dans la province , lesquelles sont déclenchées par des exigences d' indépendance . Pour le gouvernement chinois , ce mouvement est responsable des fréquentes éruptions de violence qui s' y produisent animées par des revendications indépendantistes . -La police chinoise a identifié le conducteur du véhicule , dont le nom lui semblait d' origine , et a déclaré que sa femme et sa mère étaient avec lui dans le véhicule . La police chinoise a identifié le chauffeur du véhicule dont le nom suggère qu' il est d' origine ouïghoure et a précisé que sa femme et sa mère se trouvaient avec lui dans la voiture . -L' Administration de la sécurité de l' aviation civile a également indiqué qu' elle avait examiné l' annonce , mais a souligné que des restrictions concernant l' utilisation d' appareils électroniques dans les phases critiques de vol étaient encore en place en Australie . La Civil Aviation Safety Authority a également déclaré qu' elle examinait l' annonce mais a souligné que les restrictions portant sur l' utilisation des appareils électroniques pendant les phases de vol critiques étaient toujours en vigueur en Australie . -La magie des frères continue de travailler en intégrant un personnage qui est toujours debout à l' écran : un merveilleux chat mignon , , qui est le compagnon de choix du de C. Davis . ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! La magie des frères opère encore en intégrant un drôle de personnage qui traverse le film en permanence : un magnifique chat roux au regard , compagnon de fortune de Davis . -La filiale de a indiqué jeudi qu' elle effectue actuellement des tests préliminaires de la version bêta de cette plate-forme avec certains de ses utilisateurs . La filiale de a indiqué jeudi qu' elle menait actuellement des tests préliminaires de la version beta de cette plateforme avec certains de ses usagers . -Le Président congolais , Joseph Kabila , a exhorté les rebelles le mercredi à " volontairement " ou " " par la force , tout en continuant d' ouvrir le processus de Kampala , où Kinshasa et sont toujours en pourparlers depuis le mois de décembre sans qu' aucun résultat n' ait été obtenu à ce jour . Le président congolais , Joseph Kabila , a appelé mercredi soir une nouvelle fois les rebelles à " se démobiliser volontairement " sous peine d' être désarmés " par la force " , mais a laissé ouvert le processus de Kampala , où Kinshasa et le discutent depuis décembre sans résultat pour l' instant . -L' armée congolaise ( FARDC ) a annoncé jeudi que ses unités les rebelles de jusqu' à leurs bases situées dans les forêts et les montagnes du Nord-Kivu , qui le Rwanda et l' Ouganda . L' armée congolaise ( FARDC ) a annoncé jeudi que ses unités allaient les rebelles du jusque dans leurs bases situées dans les forêts et les montagnes du Nord-Kivu limitrophe du Rwanda et de l' Ouganda . -Le Département de l' éducation doit publier les nouveaux programmes en anglais et les mathématiques demain - les premiers sujets à subir un remaniement radical . Le ministère de l' Éducation doit publier demain ses nouveaux programmes d' anglais et de maths – les premières matières à subir une révision radicale . -Le mandat unique de la BCE a toujours tourné autour de l' inflation , donc Mario Draghi et son équipe ont toutes les raisons de prendre des mesures lors de la réunion de la semaine prochaine . Le mandat unique de la BCE a toujours porté sur l' inflation , donc Mario Draghi et son équipe ont davantage de raisons d' agir lors de la réunion de la semaine prochaine . -L' UE ne disparaîtra pas , mais elle reviendra sur quelque chose de plus que la " " de l' Europe ( souveraine ) prônée par le général de Gaulle . L' UE ne disparaîtra pas , mais elle reviendra à quelque chose qui ressemblera davantage à « l' Europe des Nations ( souveraines ) » libre privilégiée par le Général de Gaulle . -Le Mouvement islamique du Turkestan oriental est considéré par les États-Unis et l' Organisation comme une organisation terroriste . Le mouvement islamique du Turkestan oriental est considéré comme une organisation terroriste par les Etats-Unis et les Nations unies . -La Commission électorale a étudié les options de vote électronique et a récemment publié un document de discussion commun avec la Nouvelle-Zélande . La Commission électorale a étudié les options possibles en ce qui concerne le vote électronique et a récemment publié un document de réflexion conjoint avec la Nouvelle-Zélande . -Le ministère de l' Énergie a déclaré mercredi que les approvisionnements américains ont augmenté de 4,1 millions de barils la semaine dernière . Le département américain de l' énergie a indiqué mercredi que les approvisionnements américains avaient augmenté de 4,1 millions de barils la semaine dernière . -La FAA a commencé à restreindre l' utilisation des dispositifs portatifs en 1966 en réponse à des rapports d' interférence dans l' utilisation de la navigation et du matériel de communication lorsque les passagers ont commencé à transporter des radios FM , les gadgets de pointe de leur journée . La FAA a commencé à limiter l' utilisation des appareils électroniques en 1996 suite aux rapports sur la présence d' interférences avec les instruments de navigation et de communication lorsque les passagers transportaient des radios FM , le gadget high-tech d' alors . -La FAA est en train d' assouplir les restrictions à l' utilisation de gadgets électroniques dans les avions - bien que les discussions sur les téléphones cellulaires continueront d' être interdites . La FAA assouplit les restrictions portant sur l' utilisation des gadgets électroniques à bord des avions – mais il sera toujours interdit de passer des appels sur les téléphones portables . -La décision de la FAA s' applique aux compagnies aériennes américaines . La décision de la FAA s' applique aux compagnies aériennes américaines . -La FAA a indiqué qu' elle avait déjà reçu des plans de certaines compagnies aériennes d' étendre l' utilisation de dispositifs électroniques portatifs sur les aéronefs . La FAA a déclaré qu' elle avait déjà reçu des plans de certaines compagnies aériennes visant à élargir l' utilisation des appareils électroniques sur leurs avions . -Le Gouvernement fédéral insiste sur le fait qu' un plan de sauvetage de 325 millions de dollars pour le système de santé en difficulté de la Tasmanie a des conditions difficiles qui permettront à l' État de ne pas gaspiller les fonds . Le gouvernement fédéral maintient que le plan de sauvetage de 325 M $ destiné au système de santé est assorti de conditions strictes visant à garantir que le gouvernement de l' État ne pas les fonds . -La police française a décidé d' imposer des règles strictes avant le match entre le Royal Sporting Club d' et le Paris Saint-Germain , prévu pour jeudi , Frank , porte-parole du district de police de , a déclaré vendredi . La police française a décidé de poser des règles strictes concernant la rencontre du Royal Sporting Club d' contre le Paris Saint-Germain fixée mardi , a communiqué Marie Verbeke , la porte-parole de la zone de police , vendredi . -Le Front est désormais le parti le plus populaire en France avec 24 % du vote - une mise en garde rapide au public britannique qu' ils ne peuvent pas prendre part à une scission sur le droit de s' accorder automatiquement la gauche . & # 160 ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Le Front National est désormais le parti le plus populaire en France avec 24 % des suffrages – un avertissement opportun pour le Parti travailliste britannique qui lui rappelle qu' il ne peut pas partir du principe qu' une scission de la droite favorisera automatiquement la gauche . -Le service secret allemand , le , déclarait à l' été 1949 que Muller était à Karlovy Vary , puis en Tchécoslovaquie , selon un document obtenu par . Les services secrets allemands , le , assuraient ainsi que durant l' été 1949 , Müller se trouvait à Karlovy Vary , alors en Tchécoslovaquie , selon un document obtenu par . -Le FMI refuse de payer des sommes supplémentaires jusqu' à ce que le pays adopte des réformes impopulaires visant à réduire son déficit , en particulier en augmentant le prix du gaz pour la population . Mais le Fonds refuse de verser toute nouvelle tranche tant que le pays n' aura pas adopté de réformes impopulaire pour réduire son déficit , notamment en augmentant les prix du gaz à la population . -La Commission indépendante de la lutte contre la corruption ( ) a ouvert la discussion sur le point de savoir si M. avait demandé à plusieurs ministres d' État de souscrire des concessions à quai de quai de , où les deux avaient un restaurant et un café , renouvelés sans avoir à se présenter à l' avance à l' expiration de leur mandat en août 2005 . Vendredi , l' Independent Commission Against Corruption ( ) a élargi son enquête pour savoir si M. Obeid avait fait pression sur plusieurs ministres de l' État pour renouveler ses baux dans Quay , où les Obeid possèdent deux restaurants et un café , après leur expiration en août 2005 , sans qu ’ il n' y ait eu d' appel d' offres . -L' , Comme A Rocket Man et Born In A est également disponible sur le net . The Informer , Like A Rocket Man , Born In A sont eux aussi disponibles sur la toile . -Le DLC est doublement - une illusion d' intro suivie d' une bonne dose de bruit qui ralentit lentement pour faire une ballade en pop . The Informer est à : une intro inquiétante avant une lumineuse qui ralentit progressivement pour laisser place à une ballade pop . -L' armée israélienne , quant à elle , a déclaré que l' objectif de son opération était initialement d' une section d' un vaste tunnel creusé dans le territoire israélien de l' enclave palestinienne , qui avait été découvert le 7 octobre et que , selon l' armée , elle était destinée à des " activités terroristes " . De son côté , l' armée israélienne a indiqué que la cible de son opération était au départ une section d' un large tunnel creusé en territoire israélien depuis l' enclave palestinienne , découvert le 7 octobre et destiné , selon l' armée , à des " activités terroristes " . -L' armée israélienne a simplement parlé de " mesures ciblées " dans la région proche de ce que l' État juif appelait un tunnel " terroriste " creusé à la frontière , dont la découverte a été signalée à la mi-octobre . L' armée israélienne s' est contentée d' évoquer une " action ciblée " dans la zone proche d' un tunnel qualifié de " terroriste " par l' Etat hébreu , creusé à la frontière et dont la découverte a été annoncée milieu octobre . -Le L.A. rapporte qu' un agent de la US Security Transportation Administration ( TSA ) et un suspect ont été blessés lors d' un échange de coups de feu . Le L.A. Times rapporte qu' un agent de l' Agence américaine de sécurité des Transports , TSA , et un suspect ont été blessés durant l' échange de coups de feu . -La Bourse de Londres a fermé jeudi , les prix étant faibles par mauvais résultats de Shell le lendemain de l' annonce par la Fed qu' elle maintiendrait son soutien à l' économie , comme prévu . La Bourse de Londres a clôturé en baisse jeudi , par les mauvaise résultats de Shell et au lendemain d' une annonce par la Fed du maintien en l' état de son soutien à l' économie , comme prévu . -Le Los Angeles Times a déclaré que les terminaux 2 et 3 étaient évacués . Le Los Angeles Times précise que les 2 et 3 sont en cours d' évacuation . -Les encres , comme les formes de , procurent un degré d' imagination plus vaste et plus riche que la danse , plus poreuse . Les encres de , comme des formes de , ouvrent un imaginaire plus large et que la danse , plus poreux . -Le ministre de la Défense , Rob Nicholson , a insisté sur le fait que les soldats blessés ne sont pas libérés sommairement des Forces armées canadiennes et ont souligné que tous les soldats sont soumis à un processus de transition avant leur retour à la vie civile . Le ministre de la Défense , Rob Nicholson , a insisté pour dire que les soldats blessés n' étaient pas sommairement libérés des Forces armées canadiennes , et a souligné qu' un processus de transition était suivi par tous les soldats avant leur retour à la vie civile . -Le ministre des Travaux publics et des Transports à Bruxelles a lancé un projet pilote dans le centre de Bruxelles le jeudi , à savoir la fabrication de 16 points de dans les couleurs jaunes et bleues de la région bruxelloise . Pour rappel , la ministre bruxelloise des Travaux Publics et des Transports a lancé ce jeudi au centre de Bruxelles un projet test consistant à 16 poteaux de feux de signalisation de la région aux couleurs jaune et bleu de Bruxelles . -Le Ministère de l' agriculture indique que le dernier cas enregistré de " cas d' espèce " a été déclaré en décembre 1998 dans un renard et que la France a été officiellement déclarée indemne de cette maladie par l' Organisation mondiale de la santé animale ( OIE ) en novembre 2001 . Le ministère de l' Agriculture précise que le dernier cas " autochtone " de rage recensé remonte à décembre 1998 sur un renard et que " la France est déclarée officiellement indemne de cette maladie en novembre 2001 par l' Office international des épizooties ( OIE ) " . -La société de Montréal a également fait appel à One Network Enterprises , un fournisseur d' accès Internet basé à Vancouver , pour un coût de 526 millions de dollars canadiens en décembre dernier . En décembre dernier , la société montréalaise a également acheté Peer 1 Network Entreprises , un fournisseur Internet établi à Vancouver , pour la somme de 526 millions . -L' entreprise montréalaise déclare que cette baisse est attribuable aux dépenses d' amortissement liées aux acquisitions récentes . L' entreprise établie à Montréal explique ce recul par des coûts d' amortissements reliés à de nouvelles acquisitions . -La prochaine avait un total de 10 morceaux supplémentaires par rapport à l' album original : les trois chansons de l' édition de la saison , cinq chansons spécialement sélectionnées pour l' occasion , et deux . The Next Day Extra contiendra au total dix titres supplémentaires par rapport à l' album original : les trois morceaux de l' édition Deluxe , cinq chansons spécialement dévoilées pour l' occasion , ainsi que deux . -L' indice a chuté de points ( % ) à et le est de points ( % ) , à . L' indice a perdu points ( % ) à 14 et le a cédé points ( % ) à 1 . -L' Observatoire a déclaré qu' il y avait des victimes tous les deux jours , mais qu' il n' avait pas de détails précis . L' Observatoire a déclaré jeudi qu' il y avait des pertes des deux côtés , mais n' a donné aucune précision . -Le PDG d' Oracle a renoncé à un bonus de 1,2 million de dollars pour l' exercice financier 2013 , qui s' est terminé en mai , en raison des faibles performances du groupe , qui n' ont pas atteint ses objectifs de croissance , mais qui a rapporté un total de 77 millions de dollars liés à des options d' achat d' actions . Le patron d' Oracle a renoncé à un bonus de 1,2 million de dollars pour l' exercice 2013 clos en mai en raison des mauvaises performances du groupe , qui a manqué ses objectifs de croissance , mais il a perçu environ 77 millions de dollars liés à des . -Le groupe Palmer pourrait contrôler jusqu' à quatre voix au Sénat , ce qui serait déterminant pour décider si les taxes sur le carbone et l' exploitation minière seraient éliminées . Le Palmer United Party pourrait contrôler jusqu' à quatre voix au Sénat , ce qui pourrait être crucial au moment de décider si les taxes carbone et minières doivent être supprimées . -Le Pentagone estime que la population concernée par la reconnaissance du mariage homosexuel compte environ personnes , soit 17 000 si la Garde nationale , la réserve et les personnes retraitées sont incluses . Le Pentagone estime la population concernée par la reconnaissance du mariage homosexuel à environ personnes active , 17 000 en y incluant la Garde nationale , la réserve et les retraités . -Le président du Conseil italien , Enrico , pense que son gouvernement restera jusqu' en 2015 , malgré de fortes tensions entre les ailes gauche et droite au sein de la coalition au pouvoir dans le cadre du vote du Sénat sur le licenciement de Silvio Berlusconi de son mandat . Le président du Conseil italien Enrico juge que son gouvernement tiendra jusqu' en 2015 malgré les fortes tensions entre gauche et droite au sein de la coalition au pouvoir à l' approche du vote du Sénat pour Silvio Berlusconi de son mandat . -Le Premier ministre devrait sans aucun doute être présent : ce sondage montre que les milieux britanniques soutiennent son projet de renégocier les termes de l' adhésion de la Grande-Bretagne à l' Union européenne . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Le Premier ministre ne devrait pas douter un seul instant : cette enquête montre que les entreprises britanniques soutiennent ce plan visant à renégocier les termes de l' adhésion de la Grande-Bretagne à l' UE . -Le Père du Père Blanc organise une à 11 heures en souvenir de tous ceux qui ont été à l' établissement cette année . Le du Père organise , à 11 heures , une cérémonie laïque , en mémoire de tous les défunts dans son établissement , au cours de l' année . -La Croix Rouge a été demandée parce que les habitants de l' un des appartements peuvent avoir besoin d' un logement temporaire , selon le Département des incendies . La Croix-Rouge a été demandée , car les résidents d' un des appartements pourraient devoir être logés provisoirement , a indiqué le SIM . -La Royal Bank of Scotland ( ) a enregistré le taux le plus bas de la au cours de la même période , après avoir signalé une baisse des bénéfices de cette journée et annoncé la création d' une banque bancaire intérieure contenant 38 milliards de de son actif à risque le plus élevé . Royal Bank of Scotland ( % ) marque la plus mauvaise performance de l' , après avoir également publié des résultats en baisse ce matin et annoncé la création en interne d' une structure de ( " bad bank " ) regroupant 38 milliards de livres d' actifs les plus risqués . -La Banque Royale de l' Allemagne créera une structure bancaire interne pour couvrir 38 milliards de livres ( 45 millions d' euros ) de ses actifs à risque les plus élevés , un pas qui vise à améliorer ses relations avec la Ville et à accélérer son . Royal Bank of Scotland créera en interne une structure de ( " bad bank " ) regroupant 38 milliards de livres ( 45 milliards d' euros ) d' actifs les plus risqués , mesure destinée à détendre ses relations avec Londres et à accélérer sa . -Le gouvernement des SNP en Écosse est le - le plus fort mouvement politique raciste en Europe - ayant remporté une victoire spectaculaire en 2011 en raison de l' opposition aux réductions proposées ( et mises en œuvre ) par le chancelier travailliste de Darling et la coalition . Le gouvernement SNP d ’ Écosse est – et c' est à noter – le mouvement politique qui a le mieux réussi en Europe , puisqu' il a remporté une majorité spectaculaire en 2011 fondée sur son opposition aux coupes sombres proposées ( et mises en œuvre ) par le Chancelier de l' échiquier travailliste , Darling , et la coalition conservateurs / qui a suivi . -Le Club Sari a été frappé lorsque une bombe massive placée dans un fourgon stationné à l' extérieur a explosé juste après minuit , le 12 octobre 2002 . Le Sari Club a été entièrement détruit lorsqu' une énorme bombe , chargée dans une fourgonnette à l' extérieur , a explosé juste après 23 h le 12 octobre 2002 . -Le conseil scolaire vient de dévoiler sa vision de " Vivre ensemble en français " . La commission scolaire vient de dévoiler sa vision du " Vivre ensemble en français " . -Le match de football en Écosse a été repoussé pour une dispute opposant le au match contre le FC Barcelone , et l' UEFA a suspendu sa mise en place pour trois matchs . contre le pour avoir adressé un coup à , le milieu écossais a vu sa suspension étendue à trois rencontres par l' UEFA . -Le Secrétaire à la défense a déclaré qu' il avait ordonné au chef de la Garde nationale , le général Frank , de veiller à l' application de la loi fédérale . Le secrétaire à la Défense a dit avoir ordonné au chef de la Garde nationale , le général Frank , de s' assurer de la mise en oeuvre de la loi fédérale . -Le Secrétaire d' État a également reconnu qu' il aurait été coupable , avec Barack Obama , d' être " pilote automatique " à la suite de révélations incendiaires faites par Edward sur les activités d' espionnage menées par le NSA . Le Secrétaire d' État a également admis qu' il avait , tout comme Barack Obama , gouverné sur « pilote automatique » lorsqu' une série de révélations incendiaires du Edward au sujet des activités d' espionnage de la NSA a vu le jour . -Le groupe de Sheffield devait se présenter au centre d' Hydro dans la ville le vendredi . Le groupe de Sheffield devait se produire vendredi dans la salle de concert The Hydro au centre-ville . -Le conseil scolaire de la région de Sherbrooke ( ) n' a pas voulu commenter la question . La Commission scolaire de la ( ) n' a pas voulu commenter . -Le porte-parole de la TSA n' a pas pu confirmer sur place si un de ses employés avait été blessé . Le porte-parole de la TSA n' était pas en mesure de confirmer dans l' immédiat si l' un de ses employés était blessé . -Le Times a obtenu un exemplaire du communiqué suivant et a déclaré jeudi matin que les principaux collaborateurs du président avaient " travaillé à fond et à la retraite à la fin de 2011 " pour déterminer si le scandale pouvait aider à renforcer les espoirs de réélection d' Obama . & nbsp ; & nbsp ; & nbsp ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; – & nbsp ; & nbsp ; » Le Times a obtenu une copie du livre à paraître et a raconté jeudi soir que les proches collaborateurs du président avaient mené des « travaux approfondis en groupes et des sondages fin 2011 » pour voir si l' éviction de Biden pourrait aider Obama en vue de sa réélection alors que ses espoirs s' . -La bourse de Tokyo est tombée à la fin du mois de vendredi , malgré les bons chiffres de la production en provenance de Chine . La Bourse de Tokyo a fini en baisse vendredi en dépit de bonnes statistiques manufacturières chinoises . -Le ministère du Trésor a procédé à l' allégement budgétaire de la BCE , mais il a estimé qu' une décision finale de M. Osborne , qui , dans le budget de 1995 , prévoyait un taux de 7 % de droits de timbre pour les ménages de plus de 2 millions de livres sterling et des frais annuels pour les acheteurs qui choisissent de placer des maisons dans une entreprise plutôt que des particuliers . Le Trésor a provisoirement chiffré la mesure de l' impôt sur les gains en capital mais attend une décision définitive de M. Osborne qui , dans le budget de 2012 , a inscrit un droit de timbre de 7 % pour les propriétés coûtant plus de 2 M £ et des frais annuels pour les acheteurs qui choisissent de détenir des propriétés dans le cadre d' une société plutôt qu' à titre individuel . -Le Sénat américain a approuvé un projet pilote de $ l' année dernière qui aurait impliqué environ 10,000 voitures . Le Sénat américain a approuvé un projet pilote de 90 M $ l' année dernière qui aurait porté sur environ 10 000 voitures . -Le site Web allemand English Online s' est également opposé à la loi allemande , qui dit : " Lorsque le monde est poussé dans un nouvel état , où le sexe est un choix , mais l' activité sexuelle n' est pas , nous deux autres piliers de la civilisation " . Le site américain Catholic Online s' est également opposé à la loi allemande , écrivant que « puisque le monde est traîné dans un nouveau règne où le genre est un choix mais où l' activité sexuelle n' en est pas un , on renverse deux autres piliers de la civilisation . » -Le gouvernement britannique , qui a tenu des discussions avec Transports Scotland , a demandé à Ltd de se pencher sur d' autres capacités de transport ferroviaire et d' amélioration du temps pour le nord de l' Angleterre et de l' Écosse . Le gouvernement britannique , qui est en pourparlers avec Transport Scotland , a demandé à Ltd d' étudier le renforcement de la capacité ferroviaire et l' amélioration de la durée des trajets pour le nord de l' Angleterre et l' Écosse . -L' ONU et l' Ouganda accusent régulièrement l' Ouganda et le Rwanda de soutenir le . L' ONU et Kinshasa accusent régulièrement l' Ouganda et le Rwanda de soutenir le , ce que réfutent Kigali et Kampala . -L' ONU a signalé des chiffres encore plus élevés , avec 979 morts et blessés . L' ONU donne un bilan même plus élevé avec 979 morts et 1 902 blessés . -La Federal Aviation Administration ( FAA ) des États-Unis a autorisé les compagnies américaines à modifier leurs procédures afin que les clients puissent lire des livres électroniques , regarder des vidéos ou des jeux sur leurs appareils pendant les phases critiques de vol , à condition de rester en " avion " . La Federal Aviation Administration américaine a laissé la porte ouverte aux transporteurs américains pour un changement de leurs procédures afin que les passagers puissent lire des livres électroniques , regarder des vidéos ou jouer à des jeux sur leurs appareils pendant les phases de vol critiques à condition qu' ils soient en mode « avion » . -L' US National Intelligence a prévu en février la France à la frontière avec la Russie et Israël dans un deuxième niveau de criminels qui se livrent à la désinformation en matière d' intelligence économique , derrière la Chine , selon le Washington Post . Le National Intelligence ( ) réalisé en février par les États-Unis , citait la France , de même que la Russie et Israël , coupables au second degré d' avoir effectué des actes de piratage de renseignements économiques , derrière la Chine , selon The Washington Post . -Les États-Unis mettent en garde leurs propres entreprises contre l' espionnage économique d' autres pays . Les États-Unis leurs propres sociétés sur les activités d' espionnage économiques réalisées par d' autres pays . -L' ONU doit commencer à travailler immédiatement sur un nouvel ensemble d' objectifs visant à remplacer les objectifs du Millénaire pour le développement , qui ont été mis en place il y a 12 ans pour lutter contre la pauvreté dans le monde . Les Nations Unies vont se mettre dès à présent à travailler sur une nouvelle série d' objectifs destinés à remplacer les objectifs du millénaire pour le développement ( OMD ) , qui avaient été mis en place il y a 12 ans pour lutter contre la pauvreté dans le monde . -Les États-Unis ont parfois été " trop loin " à l' occasion de leurs activités d' espionnage , le ministre d' État John Kerry a reconnu , lors de la première entrée en fonction de Washington , une profonde controverse avec l' Europe sur la collecte massive de données par l' Agence de sécurité nationale ( NSA ) . Les Etats-Unis sont parfois allés " trop loin " en matière d' espionnage , a reconnu le secrétaire d' Etat John Kerry , dans ce premier aveu de Washington en pleine polémique avec l' Europe sur la collecte massive de données par l' Agence nationale de sécurité ( NSA ) . -La Chambre haute du parlement italien se réunira ce mois-ci pour décider du sort de l' ancien président qui a été reconnu coupable d' évasion fiscale en août . La chambre haute du parlement italien se réunira dans le courant du mois pour décider du sort de l' ancien président du Conseil condamné pour fraude fiscale en août . -Le Vatican est en train de réaliser un grand sondage dans le monde entier sur la manière dont les paroisses traitent des questions sensibles telles que la contraception , le divorce et les couples de même sexe . Le Vatican a entrepris de mener un grand sondage à travers le monde sur la façon dont les paroisses gèrent les dossiers sensibles comme la contraception , le divorce et les couples formés de personnes de même sexe . -Le Vatican veut savoir comment les paroisses catholiques du monde entier gèrent des questions délicates telles que la contraception , le divorce et les couples homosexuels . Le Vatican veut savoir comment les paroisses catholiques du monde entier gèrent les questions délicates comme la contraception , le divorce et les couples homosexuels . -Le loup de Wall Street : Deuxième Le Loup de Wall Street : sortie du second trailer -L' accident s' est produit juste après que la victime était arrivée sur le site pour recueillir ses outils . L' accident s' est produit au moment où la victime venait d' arriver sur le chantier afin de récupérer ses outils . -La précision de la reconnaissance faciale s' est améliorée au cours des cinq dernières années , dit Cyrille , directeur de recherche de R & D auprès d' Accenture en Europe . La précision de la reconnaissance faciale a été multipliée par dix au cours des cinq dernières années , calcule Cyrille , directeur des laboratoires de R & D d' Accenture en Europe . -Le groupe militant , qui fait le bilan du nombre de victimes par le biais d' un réseau de militants en Syrie , a déclaré jeudi que personnes sont mortes . Le groupe de militants , qui suit l' évolution du bilan humain grâce à un réseau de militants basés en Syrie , a déclaré jeudi que 120 296 personnes étaient mortes . -L' administration a dit qu' il ne faut pas s' étonner que cinq pour cent de la population qui achète des assurances puisse changer de régime parce que leur protection ne répond pas aux nouvelles normes requises en vertu de la Loi sur les soins à la personne abordable . L' Administration a annoncé que l' on ne devrait s' étonner si les 5 % de la population qui contractent eux-mêmes leur assurance se retrouvaient forcés de changer de régime car leur couverture ne satisfait pas aux nouvelles normes exigées en vertu de l' Care Act . -L' Office note que les réserves de change de l' Ukraine ont diminué de 26 % entre septembre 2012 et septembre 2013 , et que la tendance devrait se maintenir . L' agence relève que les réserves de change de l' Ukraine ont chuté de 26 % entre septembre 2012 et septembre 2013 et prévoit que la tendance va continuer . -Le but de l' assaut est de la des collines qui entourent . L' objectif est de déloger le des collines qui surplombent . -Le but de la loi était de faire pression sur les parents qui pourraient prendre des décisions hâtives sur la chirurgie des nouveau-nés et de lutter contre la discrimination à l' encontre des . Le but de la loi était d' enlever la pression qui pesait sur les épaules des parents qui pouvaient prendre des décisions précipitées concernant une chirurgie de sexuelle pour leur nourrisson , et pour lutter contre la discrimination à l' encontre des personnes . -L' objectif est de " renforcer la sécurité " et de " renforcer l' identité de Bruxelles " . Le but étant à la fois " d' accroître la sécurité " et " l' identité bruxelloise " . -Le transporteur aérien a été élevé et sec à Toulon . A Toulon , le porte-avions est en rade . -L' aéroport a également indiqué que l' incident avait eu lieu à environ heure locale ( heure de Montréal ) à l' aérogare 3 de l' aéroport . L' incident a eu lieu vers 9h30 locales ( 12h30 à Montréal ) au terminal 3 de l' aéroport , a également précisé l' aéroport . -L' aéroport est actuellement évacué et le trafic aérien a été suspendu . L' aéroport est en cours d' évacuation et le trafic aérien est interrompu . -Les actions présumées ont eu lieu en général avant celles citées dans la première action , la plupart des institutions concernées étant fermées dans les années 60 . Les faits allégués sont en général plus anciens que ceux mentionnés dans le premier recours , puisque plusieurs des établissements visés ont fermé leurs portes dans les années 1960 . -L' animation , destinée aux enfants de 2 à 7 ans , est consacrée aux aventures du chat , , le shérif d' une ville dans l' Ouest sauvage où elle maintient le droit de l' ordre à l' aide d' une magique . Le dessin animé , destiné aux enfants de 2 à 7 ans , raconte les aventures de la chatte , shérif d' une ville de l' ouest américain où elle fait régner l' ordre avec un magique . -L' annonce faite par l' Organisation pour l' interdiction des armes chimiques est arrivée à la fin de l' année . L' annonce de l' Organisation pour l' interdiction des armes chimiques ( OIAC ) est arrivée avec un jour d' avance sur la date limite du -L' annonce a été marquée par des combats qui ont eu lieu jeudi dans la ville de , où des experts ont dit qu' ils étaient à l' origine d' une installation de production d' armes chimiques , ainsi que des sites de stockage , a déclaré l' Observatoire syrien des droits de l' homme de . L' annonce a été faite alors que les combats faisaient rage jeudi dans la ville de qui , selon les experts , abrite une unité de production d' armes chimiques ainsi que des sites de stockage , a rapporté l' Observatoire syrien des droits de l' Homme basé en Grande-Bretagne . -L' annonce est une mauvaise nouvelle pour le gouvernement ukrainien dans une période de tensions sérieuses avec son voisin russe , qui est par la volonté de Kiev de signer un accord d' association avec l' UE à la fin du mois de novembre . Cette annonce constitue une mauvaise nouvelle pour le pouvoir ukrainien dans une période de vives tensions avec son voisin russe , furieux de la volonté de Kiev de signer un accord d' association avec l' UE fin novembre . -L' annonce que David Bowie avait sorti un nouvel album avait le monde . L' annonce de la parution d' un nouvel album de David Bowie avait laissé tout le monde . -L' enquête annuelle montre la force de l' attachement à la disposition de la cérémonie . L' enquête annuelle d' montre un attachement très fort à l' organisation de la cérémonie . -Les anticorps les molécules de nicotine dans le sang , les avant d' atteindre le cerveau , empêchant ainsi un fumeur de toucher à la nicotine . Les anticorps toutes les molécules de nicotine présentes dans le système sanguin et les avant qu' elles atteignent le cerveau , ce qui empêche le fumeur d' avoir sa dose de nicotine . -La cour d' appel a entendu les plaidoiries le mardi sur le sursis demandé . La cour d' appel a entendu mardi les arguments portant sur la demande de suspension . -La cour d' appel a déclaré que le juge d' appel devait être écarté parce qu' il était nécessaire de revoir le code de conduite des juges américains , en empêchant le juge d' éviter un sens de partialité en partie à cause d' une série d' entretiens avec les médias et de déclarations publiques répondant publiquement à des critiques du tribunal . La cour d' appel a déclaré que la juge devait être du dossier car elle a enfreint le code de conduite des juges en compromettant la nécessité pour un juge d' éviter toute apparence de partialité , en partie en raison d' une série d' entretiens accordés aux médias et de déclarations publiques dans lesquels elle réagissait publiquement aux critiques de la cour . -L' apparence de l' acné et des poils de l' état est fréquente , même chez les nourrissons et les tout-petits . L' apparition de l' acné et des poils est courante , même chez les nourrissons et les tout-petits . -La région n' a pas été abandonnée et les rats ne sont pas dans la ville . Le quartier n' est pas laissé à l' abandon , et les rats ne pas dans la cité . -La zone est remplie d' entrepôts , ce qui facilite la manipulation des camions chargés de la drogue . La zone regorge d' entrepôts , ce qui permet de dissimuler facilement les camions chargés de drogue . -L' arrangement sera le même pour le retour . Le déroulement sera le même pour le retour . -L' arrivée de Jean-Luc au Châtelet et à la a changé tout cela . L' arrivée de Jean-Luc au Châtelet et de Stage ­ ­ Entertainment à a changé la donne . -L' artiste est décédé dimanche dernier à l' âge de 71 ans . L' artiste est décédé dimanche dernier , à l' âge de 71 ans . -L' attaque a eu lieu au moment où l' Egypte n' a jamais reçu de touristes , car l' armée a destitué le président Arafat , Mohamed Morsi , au début du mois de juillet et les manifestations de ses partisans . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Cette attaque survient alors que l' Égypte ne reçoit quasiment plus aucun touriste depuis que l' armée a destitué le président islamiste Mohamed Morsi début juillet et réprime dans le sang les manifestations de ses partisans . -Les assaillants ont fui , selon le porte-parole du Ministère de l' intérieur , le général de police Abdel Latif . Les assaillants ont pris la fuite , a précisé le porte-parole du ministère de l' Intérieur , le général de police Abdel Latif . -The staff were very friendly and helpful . The room was very comfortable and the staff were very friendly and helpful . Le public a aimé , l' accueil fut chaleureux . -L' austérité imposée par Berlin et la Banque centrale européenne , ainsi que le carcan imposé aux économies nationales par l' adhésion à la monnaie commune , ont amené beaucoup de gens à penser que le projet Europe est allé trop loin . L' austérité imposée par Berlin et la Banque centrale européenne , associée au carcan pesant sur les économies nationales par l' intermédiaire de l' adhésion à la monnaie unique , a conduit de nombreuses personnes à penser que le Projet Europe est allé trop loin . -Le fond est un écran blanc . Un écran blanc sert de mur de fond . -L' arriéré de la division aérospatiale était de 32,9 milliards de dollars au 30 septembre , inchangé par rapport au 31 décembre . Le retard dans la division Aéronautique représentait 32,9 Md $ au 30 septembre , chiffre inchangé par rapport au 31 décembre de l' année précédente . -L' annonce du groupe a eu lieu après qu' ils ont été obligés de reporter de nouveau le concert du LG Arena à Birmingham jeudi . L' annonce a été faite après que le groupe a été contraint de reporter de même un concert à la LG Arena de Birmingham jeudi . -La banque , qui a refusé de commenter les suspensions , a confirmé ce mois qu' elle recevait des demandes d' information de la part des organismes de réglementation . La banque , qui a refusé de commenter les suspensions , a confirmé ce mois-ci avoir reçu des demandes d' informations de la part des régulateurs . -La banque et la ville ont souligné que la " mauvaise banque " aurait permis à un du passé , notamment le gouvernement , d' être accusé d' interférer dans la gestion du . La banque et Londres ont souligné que la " bad bank " permettrait de tirer un trait sur le passé , le gouvernement ayant en particulier été accusé de s' ingérer dans la gestion de . -La banque a rapidement avisé les autorités concernées et coopéré avec ses régulateurs . La banque a rapidement informé les autorités compétentes et a coopéré avec les régulateurs . -La banque veut réduire la proportion d' actifs toxiques sur ses livres de 55 % à 70 % au cours des deux prochaines années et espère y nettoyer complètement trois ans . La banque veut réduire de 55 % à 70 % la proportion des actifs douteux dans son bilan dans les deux années à venir et espère avoir nettoyé complètement celui-ci dans les trois ans . -Le procureur a déclaré à la presse canadienne qu' il croyait que la police avait " ignoré ou minimisé les éléments de preuve " contre le maire . Me Ruby a dit croire à La Presse Canadienne que la police avait " ignoré ou minimisé " les éléments de preuve contre le maire . -Les vélos étaient pour s' approcher et prendre des photos , je me suis rendu à la maison , où j' ai dû prendre des mesures de maintien de l' ordre pour empêcher certaines d' entre elles d' escalader . Les motards se tirent la bourre pour faire des photos et me suivent jusqu' à chez moi où je dois faire un peu la police pour empêcher certains de monter . -Le corps , un simple accident , prend sa forme . Le corps , simple trait , prend sa forme . -Les mouvements du corps , le rire , les petits mots admirablement les , la grâce et l' absurdité des corps en , des êtres se mariant les uns avec les autres . La gestuelle , les rires et les petits cris l' archaïque , la grâce et le ridicule des corps en coït , des êtres qui se fondent . -Le livre offre donc un portrait fascinant de la France à la sortie et un plan de la France dans la construction . Le livre offre donc à la fois le portrait fascinant de la France qui s' en va et l' esquisse de la France qui vient . -Le garçon était avec son père sur le bateau " The of boat " quand il est tombé . Au moment de la chute , le garçon était avec son père sur un bateau de l' attraction " Pirates des Caraïbes " . -Le terrain propice au talent est aussi important que le nombre de postes disponibles . Le vivier de talents est aussi conséquent que le nombre de postes à pourvoir . -Le bâtiment endommagé par l' incendie contenait quatre appartements , mais il n' y avait personne à la maison lorsque l' incendie a commencé . La maison incendiée abritait quatre logements , mais aucun locataire ne se trouvait sur les lieux au moment de l' incendie . -Les appels n' interviennent qu' en raison de la nécessité de remédier aux problèmes causés par la discorde dans l' histoire de la France , déclare Nora . Ils ne sont si pressants , suggère Pierre Nora , que pour conjurer le trouble né des de l' histoire française . -La voiture conduite par la femme blessée a été frappée par un chauffeur qui se déplaçait dans la mauvaise direction et a tiré sur . La voiture d' une conductrice a été par un conducteur fantôme , qui lui est indemne . -Les Cardinaux seront également les premiers à être élus depuis la création du Conseil des Cardinaux , un groupe de huit cardinaux de tous les coins du monde , chargé de rechercher les moyens de réformer l' Église . Les cardinaux seront également les premiers à être choisis depuis que François a formé le conseil des cardinaux , un groupe de huit cardinaux du monde entier chargés d' examiner les moyens de réformer l' église . -La cause de l' explosion n' était pas connue , a-t-il dit . La cause de l' explosion reste inconnue , a-t-il ajouté . -La cause du feu n' est pas connue , mais il n' y a personne dans le restaurant lorsque les pompiers sont arrivés sur les lieux . La cause de l' incendie était inconnue , mais il n' y avait personne au restaurant lorsque les pompiers sont arrivés sur la scène . -Les accusations portées par le département de la justice des États-Unis incluent l' espionnage et le vol de biens de l' État . Dans le cadre de la plainte déposée par le ministère américain de la justice , il est accusé d' espionnage et de vol de biens de l' État . -Le chef sera remplacé par son adjoint , âgé de ans , qui est à l' Elysée depuis dix-sept ans . Le chef sera remplacé par son adjoint , Guillaume Gomez , 35 ans , qui est pour sa part depuis dix-sept ans à l' Elysée . -Le chef de la police n' a pas abordé cette question , mais son porte-parole , Mark , a décrit les déclarations de Ruby comme " une tentative évidente et désespérée " de se tourner vers les médias dans une affaire qui ne le concerne pas du tout . Le chef de police n' a pas commenté , mais son porte-parole Mark a qualifié les propos de Me Ruby de " tentative évidente et désespérée " de se faire valoir auprès des médias dans une affaire qui ne le concerne aucunement . -Le choix se fait de plus en plus . L' offre s' est . -Le cinéma a été en mesure de se montrer en bonne forme et les clients peuvent continuer à profiter des événements ... Le cinéma a pu reprendre ses droits et les clients retrouver la plaisir des images . -Le cinéma a été ventilé et tout le monde est revenu en bon ordre . La salle a été aérée et tout est rentré dans l' ordre . -La ville a fait appel de ses conclusions et de ses ordonnances de redressement , y compris la décision d' affecter un surveillant pour aider le service de police à modifier son programme de politiques et de formation . La ville a fait appel de ses conclusions et de ses ordonnances correctives , notamment une décision visant à attribuer un contrôleur pour aider le département de police à modifier sa politique et le programme de formation qui lui est associé . -La réclamation se fonde sur le témoignage d' un " " , connu sous le nom de J. A. , qui avait été dans les années 1950 , d' abord par son , frère de , puis par son , le père Bernard , à l' Oratoire Saint-Joseph , où il aurait été un garçon d' autel et où son père a travaillé comme peintre . La requête s' appuie sur le témoignage du requérant , identifié comme " J. J " . , qui aurait été dans les années 1950 , d' abord à par son professeur le frère Soumis , puis par son , le père Bernard , à l' oratoire Saint-Joseph , où il était servant de messe et où son père travaillait comme peintre . -L' action collective doit d' abord être jugée recevable par la Cour supérieure . Le recours collectif doit d' abord passer l' étape de la recevabilité en Cour supérieure . -L' action collective qui vient d' être introduite a précisément permis d' inclure des plaignants de toute institution où des membres de la Congrégation de la Croix peuvent avoir été impliqués dans des abus . Le recours collectif qui vient d' être déposé a pour particularité de permettre l' ajout de plaignants de n' importe quel établissement où des sévices auraient pu être perpétrés par des membres de la congrégation de Sainte-Croix . -La fermeture du Delta intervient à un moment de grande concurrence sur le marché de l' hôtellerie . La fermeture du Delta intervient dans un marché hôtelier très concurrentiel . -La cocaïne a été entre trois qui avaient été auparavant . La cocaïne avait été répartie dans trois ayant été préalablement . -Le Comité a introduit de nouvelles restrictions sur les données que les services de renseignement ont été autorisées à percevoir et ont imposé une limite de cinq ans à la durée de conservation de ces données . La commission a instauré de nouvelles restrictions sur les données que les agences de renseignement sont autorisées à collecter et a imposé une limite de cinq ans pour la conservation de ces informations . -L' entreprise a amélioré ses prévisions de bénéfices à long terme et s' attend à produire des recettes provenant de vacanciers en Australie et au Royaume-Uni . La société a revu ses prévisions de bénéfice net à la hausse et espère des résultats record grâce aux vacanciers d' Australie et du Royaume-Uni . -L' entreprise cible millions de barils par jour pour cette année , avec une augmentation de 25 % de la production prévue en 2017 . La société vise millions de pour cette année , avec une croissance de 25 % de la production attendue d' ici 2017 . -La société a perdu clients au cours du quatrième trimestre . L' entreprise a perdu 15 237 clients au cours du quatrième trimestre . -La société va financer cette opération en utilisant 60 % de son chiffre d' affaires de $ qu' elle a augmenté cette semaine dans le cadre d' une cotation à la Bourse de Singapour . La société les financera en utilisant 60 % des 66 M $ de Singapour qu' elle a levés cette semaine lors de son introduction à la bourse de Singapour . -L' achèvement de ce qui est essentiellement le stade initial de la destruction est un jalon important dans un calendrier ambitieux qui vise à détruire toutes les armes chimiques de Damas par . L' achèvement de ce qui est essentiellement la phase de destruction initiale est une étape importante du calendrier ambitieux visant à détruire les armes chimiques de Damas d' ici . -Le concept n' est pas un défi universel . Le concept n' est pas un succès universel . -Les conclusions du sondage seront une surprise pour beaucoup . Les conclusions de l' enquête seront une surprise pour beaucoup . -L' état des deux victimes demeure critique . Le pronostic vital des deux victimes est toujours engagé . -Le conflit a forcé quelque 2 millions de personnes à fuir le pays . Quelque 2 millions de personnes ont dû fuir le pays en raison du conflit . -L' affrontement a duré une demi-heure , selon des témoins . L' affrontement a duré une demi-heure , selon des témoins . -Le contrat a chuté de 39 cents le jeudi , ce qui lui a fait baisser de 5,8 % le mois d' octobre . Le contrat a perdu 39 cents jeudi , le laissant avec une baisse de 5,8 % pour le mois d' octobre . -Le pays compterait environ 1 000 tonnes de substances chimiques et d' armes , y compris de gaz moutarde et de sarin . On pense que le pays dispose d' environ 1 000 tonnes de produits et d' armes chimiques , notamment du gaz moutarde et l' agent sarin . -Le tribunal a également confirmé les articles restants de la Loi sur les et a ajouté que la loi sur les soins de santé exige que les États imposent l' admissibilité au supplément de Medicaid ou qu' ils perdent tout le financement du Service Medicaid fédéral n' est pas contraire à la loi contraire à la loi . La Cour a également validé les autres sections de la loi de 2 700 pages et a , par ailleurs , considéré que la disposition de la loi de réforme du système de santé , qui stipule qu' il faut abaisser le seuil d' éligibilité à Medicaid sous peine de perdre tous les financements fédéraux du programme , était coercitive conformément à la constitution . -Le tribunal a entendu un " utilisateur régulier " de cocaïne après avoir découvert des traces de la drogue de la classe A dans ses cheveux , son portefeuille et sur ses cartes de crédit . Le tribunal a entendu que était un « consommateur régulier » de cocaïne après que des tests ont montré des traces de cette drogue de classe A dans ses cheveux , sur son portefeuille et ses cartes de crédit . -Le tribunal de La Haye a été créé à cette fin . Un tribunal à La Haye est fait pour ça . -Le créateur de SAS a déclaré qu' il était en train de " fabriquer une sorte d' avion de comédie géopolitique " . Le père de SAS disait " faire un genre de feuilleton géopolitique " . -Le nous a autorisé à le diffuser . Le nous a autorisés à la relayer . -La crise de l' euro a peu à voir avec les gouvernements nationaux qui ont des déficits budgétaires excessifs - ce n' était vrai que pour la Grèce . La crise de l' euro n' a pas grand-chose à voir avec les gouvernements nationaux qui enregistrent des déficits budgétaires excessifs – cela était uniquement vrai pour la Grèce . -Le chef de l' État actuel , qui a sur le livre qu' il a perdu pendant la campagne présidentielle , " n' a pas d' obligation alimentaire spécifique " . L' actuel chef de l' Etat , qui a regagné les kilos perdus avant la campagne présidentielle , n' a " pas de demande de régime spécifique " . -La est relativement et les attaques ne sont pas censées pénétrer dans le réseau interne du système cible . La n' est pas très sophistiquée et les attaques ne sont pas destinées à pénétrer dans le réseau interne du système visé . -Les dommages étaient assez limités dans l' ensemble , même si l' eau a causé un dommage . Les dommages ont été somme toute limités , même si l' eau a causé des dégâts . -Les danseurs , tous habillés en noir , qui ne portent que les mains et les regards , attendent de chaque côté de la scène ; ils se demandent quand le prochain graphique s' affiche , s' , puis s' . Les danseurs , entièrement vêtus de noir , seuls les mains et le visage découverts , attendent de chaque côté de l' aire de jeu , se précipitent quand la prochaine apparaît , l' incarnent , ressortent en courant . -Les données présentées au Parlement pour l' année dernière indiquent que sur les soldats qui ont été libérés pour des raisons médicales , 199 n' avaient pas atteint la durée de service requise pour obtenir des prestations de retraite . Des données présentées l' an dernier au Parlement indiquent que parmi les soldats libérés pour des raisons médicales , 199 n' avaient pas atteint la durée de service requise pour obtenir des prestations de retraite . -Le jour où j' étais libre , j' étais au décollage d' une voiture où j' ai rencontré Roger - mais je n' ai pas pu le voir parce que c' était sombre . Quand je suis libéré , je me retrouve dans le coffre d' une voiture où je rencontre - sans le voir car il fait noir - Roger . -La décision de la cour d' appel signifie que les médecins pratiquant l' avortement devront avoir un " privilège d' admission " avec les hôpitaux locaux . L' arrêt rendu par la cour d' appel signifie que les médecins qui des IVG devront disposer d' un " privilège d' admission " auprès des hôpitaux locaux . -La décision a ravivé les souvenirs douloureux de la grand-mère de June , qui a perdu sa fille , âgée de 20 ans , Jane et Jenny , lors de la répression de et de son , il y a près de dix ans . La décision a ravivé des souvenirs pénibles pour June , une mère de Perth qui a perdu ses deux jumelles de 39 ans , Jane et Jenny , dans les destructions perpétrées par et les autres conspirateurs il y a près de 10 ans . -La décision signifie qu' un texte juridique adopté dans l' État en juillet dernier , qui met en doute le droit à l' avortement , pourra entrer en vigueur . Ce jugement signifie qu' un texte de loi adopté en juillet dans cet Etat et remettant largement en cause le droit à l' avortement va pouvoir entrer en vigueur . -La décision de prendre le , annoncée par la police du mercredi , a été prise par , l' entreprise gérant le portique . La décision du démantèlement , annoncée par la préfecture du Finistère mercredi , a été prise par la société , gestionnaire du portique . -Le départ de près de 10 pharmaciens du Centre for Health and Social Services ( ) de Laval a causé des troubles au sein des dirigeants de l' hôpital de l' année 2012-2013 . Le départ de près de 10 pharmaciens au Centre de santé et services sociaux ( ) de Laval a causé tout un émoi à la direction de l' Hôpital de la , et ce , lors de l' année 2012-2013 . -Les détracteurs accusent le gouvernement de forcer l' argent à faire des économies parce qu' ils ne permettent pas aux soldats blessés - qui ne respectent pas la règle de l ' " universalité " de l' armée - que le personnel puisse accomplir un certain nombre de tâches différentes , afin de pouvoir atteindre la période de dix ans requise pour les prestations de retraite . Les détracteurs accusent le gouvernement de vouloir économiser de l' argent en ne permettant pas aux militaires blessés - qui ne répondent pas à la règle de " l' universalité du service " de l' armée exigeant que le personnel soit en mesure d' effectuer une série de tâches variées - d' atteindre la période d' admissibilité de 10 ans requise pour les prestations de retraite . -Les dispositifs , qui les automobilistes et transmettent les informations aux bureaucrates , sont au centre d' une tentative controversée des bureaux de l' État et de la planification de l' État de réformer le système dépassé pour le financement des principales routes de l' Amérique . Les appareils , qui enregistrent tous les miles parcourus par un automobiliste et transmettent les informations aux fonctionnaires , sont au centre d' une tentative controversée à Washington et dans les bureaux gouvernementaux de la planification de remanier le système obsolète de financement des principales routes américaines . -Le diagnostic de la rage a été confirmé par l' Institut Pasteur . Le diagnostic de rage a été confirmé par l' institut Pasteur . -Le réalisateur Lars von Trier l' a choisi pour jouer la jeune Charlotte dans son prochain film , . Le réalisateur Lars von Trier l' a choisie pour incarner Charlotte jeune dans , son prochain film . -Le directeur de l' Association des soins palliatifs de l' Ontario , Rick Firth , estime que le projet de Québec est confus pour ce qui est de l' objectif des soins palliatifs . Le directeur de l' Association des soins palliatifs de l' Ontario , Rick Firth , croit que le projet de loi du Québec la population au sujet de la vocation des soins palliatifs . -La discorde s' est propagée à l' Asie vendredi . Et la polémique s' étendait vendredi à l' Asie . -Le document de travail fait état de 12 mesures qui ont été précédemment examinées par l' ancien ministre de l' Immigration , M. Stephen Bowen . Le document de travail fait état des 12 mesures qui avaient précédemment été envisagées par l' ancien ministre de l' Immigration Chris Bowen . -Le litige se produit lorsque les dirigeants de l' avion tentent de vendre des versions de plus en plus longues de leurs avions à grand rayon d' action , ce qui pourrait permettre d' enregistrer les commandes attendues à l' occasion de l' événement du 21 novembre . Le différend a éclaté alors que les cherchent à vendre des versions encore plus grandes de leurs avions long-courriers , en espérant éventuellement un record de commandes lors de l' évènement qui se déroulera du 17 au 21 novembre . -Le différend se concentre sur la largeur des sièges offerts sur les vols long-courriers pour l' économie d' énergie - pas toujours ceux qui sont les plus demandés par les compagnies aériennes , mais dont les places spatiales bénéficient de la garantie d' efficacité pour les jets d' affaires les plus récents offerts par Airbus SAS et Boeing Co . , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , etc. Le conflit porte sur la largeur des sièges proposés sur les vols long-courriers aux passagers de la classe économique – qui ne sont pas toujours les plus par les compagnies aériennes , mais auxquels l' espace alloué est essentiel pour augmenter les gains d' efficacité dans les derniers appareils présentés par Airbus SAS et Boeing Co . -Le conducteur du véhicule , sa femme et sa mère sont tous morts au feu dans la voiture . Le conducteur de la voiture , son épouse et sa mère ont péri dans l' incendie de la voiture . -Les drogues ont été détectées lors de l' inspection des bagages . C' est lors de l' examen des bagages de la passagère que la drogue a pu être détectée . -Les drogues ont été ensuite transmises au bureau de la Gendarmerie royale du Canada ( GRC ) , qui a alors pris l' enquête . La drogue a ensuite été transportée au bureau de la Gendarmerie royale du Canada ( GRC ) qui a pris le relais dans cette enquête . -La gravité profonde des risques de narration fait également de lui un calvaire pour les spectateurs . La consciencieuse solennité de la narration pourrait également devenir une pénitence pour le public . -Le rédacteur en chef est personnellement impliqué dans le troisième . Le rédacteur en chef est personnellement impliqué dans le troisième . -L' élection s' est avérée un glissement de terrain , et les vedettes de l' information se sont pour réfléchir à la nature historique de l' heure . L' élection s' est terminée par une victoire écrasante et les présentateurs des journaux d' actualités ont pris le temps de réfléchir à la nature historique du moment . -L' ambassade a refusé de faire des observations au sujet de l' avis de la police . L' Ambassade a refusé de commenter la délivrance de l' avis par la police . -Les services d' urgence ont été lancés vers 1 heure du matin pour un incendie qui a eu lieu dans le sous-sol d' un restaurant indien à l' avenue , près de la jonction avec la rue . Les secours ont été appelés vers 1h , vendredi , en raison d' un feu qui s' était déclaré au sous-sol d' un restaurant de cuisine indienne sur l' avenue , près de l' intersection avec la rue . -L' énergie est grande , et le rythme rapide . L' énergie est haute , le rythme toujours rapide . -L' examen comportera également des " textes non lus " pour encourager une lecture plus large ; L' examen présentera également des « textes nouveaux » pour encourager une lecture plus diversifiée . -L' excitation de trouver leur programme préféré après avoir diminué , beaucoup sont déçus . l' excitation de retrouver leur programme favori , beaucoup ont été déçus . -L' expérience devrait durer six mois . L' expérimentation est prévue pour durer six mois . -Les expériences sont décrites dans la revue Medicine Science . Les expériences sont décrites dans la revue Science Medicine . -L' expert , qui a été dépêché sur les lieux par le parquet , a déclaré que la cause de l' explosion était purement accidentelle . L' expert dépêché sur place par le parquet assure que l' origine de l' explosion est purement accidentelle . -L' expiration de la période d' enquête prévue par la aux États-Unis et les décisions d' autorisation rendues dans les autres pays répondent à bon nombre des conditions nécessaires à la mise sur le marché . L' expiration du délai d' examen prévu par le aux Etats-Unis et les décisions d' autorisation délivrées dans les autres juridictions satisfont plusieurs des conditions nécessaires à la l' opération . -Le fait que le chien ait été repéré est incroyable . C' est incroyable que quelqu' un ait remarqué le chien . -La chute de Goma a conduit la Mission des Nations Unies en République démocratique du Congo , le pays le plus important du monde , à renforcer son mandat et à constituer une force d' intervention rapide composée de soldats de l' Afrique du Sud , du Malawi et de la Tanzanie . La chute de Goma a amené la Mission des Nations unies en RDC , la plus importante au monde en terme d' effectifs , à renforcer son mandat et à former une brigade rapide d' intervention composée de soldats sud-africains , et tanzaniens . -L' impôt fédéral lui-même , 18,4 cents par gallon , n' a pas augmenté en 20 ans . La taxe fédérale elle-même , qui est de 18,4 cents par gallon , n' a pas augmenté depuis 20 ans . -Les combats ont mis en lumière les dangers que les inspecteurs des armes chimiques doivent affronter lorsqu' ils sont confrontés à des délais serrés dans leur mission de débarrasser la Syrie de l' arsenal toxique au cours d' une guerre civile en cours . Les combats ont souligné l' importance des dangers auxquels sont confrontés les inspecteurs chargés des armes chimiques alors qu' ils doivent achever leur mission , qui consiste à débarrasser la Syrie de son arsenal toxique au milieu d' une guerre civile qui perdure , dans des délais serrés . -Le film a le sentiment d' un documentaire , de l' record de disques et d' une ancienne radio , aux choix musicaux des studios du jour et aux cafés où les chanteurs ont joué . Le film prend alors des allures de documentaire , du label de musique poussiéreux envahi de invendus habité par une vieille secrétaire , aux choix musicaux des studios de l' époque et aux cafés où se produisaient les chanteurs . -Le film ne glisse jamais en ou en - et c' est le problème . Le film ne verse jamais dans la ni le , et c' est bien le problème . -La dernière goutte s' est produite quand on a appris que le téléphone de la chancelière Angela Merkel avait été , peut-être pendant près d' une décennie . La révélation selon laquelle le téléphone de la chancelière allemande Angela Merkel avait été piraté , peut-être même pendant une décennie , a été la goutte d' eau qui a fait déborder le vase . -L' incendie " a causé des dommages importants à la structure du bâtiment " , a déclaré le chef des opérations du département des incendies de Montréal , Richard Breton . Le feu a " causé d' importants dommages à la structure du bâtiment " , a indiqué le chef aux opérations du Service de sécurité incendie de Montréal , Richard Bordeaux . -Le feu a commencé à l' avant , mais s' est étendu aux trois étages du bâtiment . Le feu s' est déclaré à l' avant , mais s' est élevé sur les trois étages de l' immeuble . -Le feu a commencé dans une maison sur quatre étages , située à l' arrière de la maison du Séminaire du Québec . Les flammes se sont déclarées dans une maison de trois logements répartis sur quatre étages située derrière la cour du Séminaire de Québec . -L' incendie a été maîtrisé une heure après que les pompiers arrivés à leur place pour intervenir . Le feu a été maîtrisé une heure après l' arrivée des pompiers , qui ont été nombreux à intervenir . -Les pompiers étaient extraordinaires . Les pompiers ont été . -Les pompiers étaient brillants . Les pompiers ont été super . -L' entreprise n' était pas à l' origine pour aider les contribuables à prendre des mesures fiscales . À l' origine , la société ne se pas à aider les automobilistes qui payent des taxes aux États . -La première est d' expliquer comment elle peut gagner du temps . La première est d' expliquer qu' elle peut faire gagner du temps . -La première est que lorsque leurs alliés - comme cela a été le cas récemment - ont appris que leurs gouvernements étaient , parfois au plus haut niveau . Le premier , c' est lorsque leurs alliés - et ça a été le cas récemment - apprennent que leurs dirigeants , parfois au plus haut sommet de leur Etat , ont été surveillés . -Les neuf premiers épisodes de s Wild West seront disponibles depuis le 24 novembre sur le site ou via sa demande de téléphones cellulaires et de comprimés . Les neuf premiers épisodes de s Wild West seront disponibles à partir du 24 novembre sur le site ou via son application pour téléphones et tablettes . -La première représentation d' un film dans le Mega à Narbonne a été interrompue jeudi de la fin de la récréation après que les auditeurs ont signalé une sensation de dans la gorge . Jeudi soir , la première séance d' un film a été interrompue au de Narbonne , par mesure de précaution , à la suite de à la gorge ressentis par les spectateurs . -La première étape est que la CGT et la un accord sur l' indemnité de départ pour tous les salariés . La première étape est que la CGT et Goodyear un accord sur les indemnités de départ pour tous les employés . -Le premier avion d' essai a été dévoilé en mars et a pris le vol pour la première fois en septembre après des mois de retard . Le premier avion d' essai a été dévoilé en mars et s' est pour la première fois en septembre après des mois de retard . -La première fois que j' ai eu ce sentiment très fort de sa présence , il était dans le siège passager pendant que je . La première fois ce fut le très fort ressenti de sa présence sur le siège passager alors que je . -Le lendemain , j' avais le sentiment qu' il était venu me voir dans mon rêve pour me dire qu' il partageait la joie que je me sentais . Le lendemain , j' avais le sentiment qu' il m' avait rendu visite dans mon sommeil pour me signifier qu' il partageait avec joie ce qui m' arrivait . -La première est son excellent " Fair Lady " ce soir et a annoncé la production mondiale de " Un à Paris " en guise de suivi . La première reprend , ce Noël , son excellent My Fair Lady et annonce pour le suivant la création mondiale de " Un Américain à Paris " . -L' ancienne agence secrète , autrefois connue sous le nom de , a fait preuve d' un manque de transparence et , au cours des derniers jours , de critiques très virulentes à l' égard de programmes de surveillance de l' étranger et de programmes nationaux de grande envergure , elle a été remise en question par les fichiers secrets secrets de l' Agence et par l' arrivée de l' , membre de l' Autorité palestinienne , qui s' est retrouvé à l' insu de l' auteur de la plainte . , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , etc. La NSA autrefois ultra secrète , un temps surnommée la « No Agency » , s' est retrouvée sous le feu des projecteurs et fortement critiquée au cours des derniers mois suite à une vague de révélations à propos de ses vastes programmes de surveillance des communications au niveau national et à l' étranger – qui font partie des fichiers secrets de la NSA volés à l' agence et divulgués par l' ancien consultant de la NSA , Edward . -Les quatre marques du groupe ont vu leur performance commerciale s' améliorer par rapport à octobre 2012 . Les quatre marques du groupe ont vu leurs performances commerciales s' améliorer par rapport à octobre 2012 . -Les quatre occupants des deux véhicules ont été blessés , mais pas . Les quatre occupants des deux véhicules ont été blessés , mais l' on ne craignait pas pour leur vie . -Le gratuit à la Fondation de la Raison est aussi passionné par le paiement des chauffeurs par mile parcouru . Les partisans du marché libre de la fondation Reason apprécient également l' idée de faire payer les conducteurs au mile . -Le financement , sur une période de quatre ans , a été décidé après des consultations gouvernementales avec le député Andrew Andrew de la République de Corée . Le financement , prévu sur 4 ans , a été décidé après des consultations du gouvernement avec le député indépendant de Tasmanie , Andrew . -Le gain dans les coûts unitaires est par rapport aux actuellement utilisés . Le gain en termes de coûts unitaires est faible comparé à l' appareil configuré avec 10 sièges par rangée actuellement en service . -Le lancement mondial sur les chaînes de groupe Disney n' est pas prévu jusqu' en 2014 , selon le communiqué de presse de sa junior division de Disney . Son lancement mondial sur les chaînes du groupe Disney n' est prévu qu' en 2014 , détaille le communiqué de sa division Disney Junior . -Le gouvernement accuse d' avoir déclenché les affrontements , l' accusant d' attaquer des soldats . Le gouvernement rend la responsable d' avoir déclenché les conflits , et l' accuse d' attaquer des soldats . -L' indice de la dette publique a été ramené à , dans la catégorie des investissements spéculatifs . La note des titres de dette publique du pays passe à " " , s' dans la catégorie des investissements spéculatifs . -Le grand-père et le locataire de la maison voisine , qui ont été affaiblis par l' explosion , ont été logés ailleurs par le chef des services sociaux . Le grand-père et le locataire de la maison voisine , fragilisée par l' explosion , ont été par le président du CPAS . -La grand-mère aurait allumé une cigarette alors qu' il y avait une fuite de gaz , La grand-mère aurait allumé une cigarette lors d' une fuite de gaz . -La grand-mère une cigarette lorsqu' il y avait une fuite de gaz et une accumulation de gaz . La grand-mère a allumé une cigarette alors qu' il y avait une fuite et une accumulation de gaz . -La grand-mère a été jetée dans la pièce par l' explosion et gravement brûlée . La grand-mère a été projetée par l' explosion et est grièvement brûlée . -Le groupe - une combinaison de médecins camerounais et d' étudiants en médecine étrangère - a effectué 700 chirurgies gratuites au cours de la dernière année , et ils savent que leur aide peut faire changer le monde de ceux qui en ont besoin . Le groupe , composé de médecins camerounais et d' étudiants en médecine , a pratiqué 700 actes chirurgicaux gratuitement au cours de l' année dernière , et il sait que leur présence peut faire toute la différence pour ceux à qui ils viennent en aide . -Le groupe a également recommandé que les dispositifs plus lourds soient placés en toute sécurité sous des sièges ou dans des au décollage et à l' atterrissage . Le groupe a également recommandé que les appareils plus lourds soient rangés en toute sécurité sous les sièges ou dans les compartiments supérieurs pendant le décollage et l' atterrissage . -Le groupe a produit millions de barils par jour au cours du trimestre , soit une augmentation par rapport au chiffre de millions de barils par jour enregistré un an auparavant . Le groupe a produit millions de barils d' équivalent pétrole par jour au cours du trimestre , en hausse par rapport aux millions de produits un an auparavant . -Le groupe qui détient des milliers d' anciens éditeurs de Nortel a fait l' objet de plusieurs poursuites judiciaires jeudi à l' encontre des fabricants de téléphones cellulaires , dont Google , l' entreprise qu' il a dans la vente aux enchères de Nortel . Le groupe qui possède des milliers d' anciens brevets Nortel a entamé une avalanche de poursuites judiciaires en matière de brevets jeudi à l' encontre de fabricants de téléphones portables , notamment Google , la société qu' il a au poteau lors de la vente aux enchères des brevets qui a eu lieu après la faillite de Nortel . -Le mécontentement croissant de la minorité sunnite , qui détient le pouvoir sous Saddam Hussein et qui se plaint désormais d' être politiquement marginalisée et d' être l' objectif poursuivi d' arrestations arbitraires , est favorable à cette explosion de violence . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Le mécontentement croissant de la minorité sunnite , au pouvoir sous Saddam Hussein , qui se plaint d' être marginalisée politiquement et d' être la cible d' arrestations injustes , a favorisé cette flambée de violences . -Le guide contient de l' information sur les écoles afin de sensibiliser les enseignants et le personnel aux allergies alimentaires des enfants et de les aider à faire face à une réaction allergique . Le guide contient des informations destinées aux écoles sur la façon de sensibiliser le corps professoral et le personnel aux allergies alimentaires chez l' enfant , et sur la façon de les traiter en cas de réaction allergique . -Les tirs semblent se rapprocher de la population . On a l' impression que les coups de feu se rapprochent des populations . -Dans le cadre de l' enquête sur la situation des droits de l' homme au Tibet , le président de la République de Serbie , M. , a fait part de son inquiétude à l' égard de la situation des droits de l' homme dans la région de l' Europe du Sud-Est . Le chef de la sécurité intérieure chinoise a accusé vendredi un groupe de séparatistes du Xinjiang d' être derrière l' attentat à la voiture piégée qui a fait cinq morts sur la place Tiananmen dans le centre de Pékin , lundi . -Le chef d' État a promis que la justice serait tout aussi ferme avec les puissants dictateurs que les " mouches " , les plus faibles , même si seulement une poignée de hauts fonctionnaires auraient été condamnés , y compris les anciens dirigeants du géant pétrolier . & nbsp ; & nbsp ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; – & # 160 ; & # 160 ; & # 160 ; & # 160 ; » Si le chef de l' Etat a promis que la justice serait aussi avec les puissants " tigres " qu' avec les " mouches " - les fonctionnaires subalternes - seule une poignée de responsables de haut rang ont été condamnés , parmi lesquels d' anciens cadres du géant pétrolier . -L' audience a été ajournée jusqu' au 20 décembre . L' audience a été ajournée jusqu' au 20 décembre . -L' hôtel compte trois cent cinquante employés . Trois cent cinquante personnes travaillaient à l' hôtel . -La chasse à cette technologie a conduit certains organismes d' Etat à un petit démarrage de la Californie appelé real . La recherche de cette technologie a amené certaines agences d' État à faire appel à une petite start-up de Californie du nom de True . -La glace les propriétés fluorescentes d' une , synthétisée par des scientifiques chinois La glace utilise les propriétés fluorescentes de la synthétisées par des scientifiques chinois . -La crème glacée réagit avec la langue du patient - augmentant le niveau de pH dans la protéine et la rendre . La crème glacée réagit avec la langue en augmentant le pH de la protéine pour la faire briller . -L' idée est de tous les feux de circulation à Bruxelles , à un coût estimé à un million d' euros . L' idée est , à terme , de tous les feux de signalisation bruxellois , pour un coût estimé d' environ un million d' euros . -L' idée était de présenter le moment précédant l' apparition de Greenwich Village , le quartier de New York qui a commencé à être l' épicentre du " de la ville " qui a créé les stars numériques , selon le journaliste Wald , un ami de Van . L' idée était de montrer le moment précédant l' avènement de Greenwich village , ce quartier de New York appelé à devenir " l' épicentre de l' essor de la musique folk qui engendrerait des stars internationales " comme l' écrit le journaliste Wald , ami de Van . -L' immense douleur qui m' a frappé m' a bien sûr échappé , mais il est toujours proche de moi . La peine immense qui m' a alors envahie s' est bien sûr atténuée mais il est toujours près de moi . -L' incident arrive quelques semaines après que six membres de Golden Dawn , dont le chef et le fondateur du parti , ont été responsables de faire partie d' une " organisation criminelle " , dans le cadre d' une opération dirigée contre le parti à la suite du meurtre d' un musicien par un de ses membres . L' incident intervient quelques semaines après l' inculpation de six députés d' Aube dorée , dont le chef et fondateur du parti , pour participation à " une organisation criminelle " dans le cadre d' une offensive contre ce parti lancée après la mort d' un musicien par un de ses membres . -L' incident a fait 42 victimes . L' incident a fait 42 blessés . -L' incident s' est produit dans le terminal 3 , provoquant une panique . L' incident se serait produit au terminal 3 , provoquant une vague de panique . -L' incident s' est produit jeudi - Halloween - lorsqu' il est habituel de s' habiller comme un monstre , mais il est difficile de croire qu' il s' agissait d' un . Cet incident s' est produit jeudi , jour de la fête d' Halloween , où il est de coutume de se déguiser en monstre , mais il était difficile de savoir s' il s' agissait d' un . -Les informations seront utilisées lors d' une grande assemblée sur la famille que le pape entend organiser l' année prochaine . L' information servira à un important rassemblement sur la famille que le pape François prévoit organiser l' an prochain . -La personne blessée a été immédiatement emmenée à l' hôpital , la même source l' a déclaré . La personne blessée a aussitôt été hospitalisée , selon la même source . -La paire inséparable - qui est la même hauteur - a été félicitée pour l' enregistrement , le style et le comportement lors du concours annuel organisé par le Centre équestre de , à près de Cardiff . Les deux inséparables – qui sont de la même taille – ont été salués pour la présentation , l' attitude et le style lors du concours annuel qui s' est déroulé au centre équestre , à près de Cardiff . -L' instabilité a ajouté aux frustrations avec le gouvernement , a déclaré Fernando Lima , responsable de , une entreprise de médias indépendante , qui a également beaucoup de victimes de la corruption , de la lenteur du développement et d' une vague récente d' enlèvements . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; L' instabilité a renforcé les frustrations que suscite le gouvernement , déclare Fernando Lima , directeur de , une société de médias indépendante , de nombreuses personnes étant également préoccupées par la corruption , la lenteur du développement et la récente vague d' enlèvements . -L' instruction a été donnée presque en même temps que la fin de l' du siège des Nations Unies à New York . Cette instruction a été donnée à peu près au même moment que celle mettant fin aux écoutes du quartier général de l' Onu à New York . -L' intérieur possède des sièges de course et des ceintures de 6 places , ainsi qu' un système d' interphone . L' intérieur dispose de sièges et de ceintures harnais à 6 points , ainsi que d' un système . -Le fonds d' investissement qui appartenait à l' immeuble devait faire un choix . Le fonds d' investissement qui était propriétaire de cette avait des choix à faire . -Le fonds d' investissement qui appartient à l' immeuble l' a vendu aux promoteurs qui la convertissent en résidences d' étudiants . Le fonds d' investissement qui possédait l' édifice l' a vendu à des promoteurs qui vont le transformer en résidences étudiantes . -Le Conseil de l' île a voté à l' unanimité en faveur de la création d' une réserve marine autour du minuscule archipel , qui sera , correspondant à sa zone économique exclusive . Le Conseil de l' île a voté à l' unanimité en faveur de la création d' une réserve marine autour du petit archipel , soit km2 qui correspondent à sa zone économique exclusive . -L' île , qui se trouve à l' est de l' archipel ( Polynésie française ) , est le dernier territoire britannique du Pacifique Sud , à mi-chemin entre la Nouvelle-Zélande et le Chili . Cette île , à l' est de l' archipel des ( Polynésie française ) , est le dernier territoire britannique du Pacifique Sud , à mi-chemin entre la Nouvelle-Zélande et le Chili . -La question est abordée dans les règlements qui exigent que les exploitants d' aéronefs veillent à ce que la sécurité soit maintenue en tout temps et que les passagers soient tenus de se conformer aux consignes de sécurité données par les membres de l' équipage . La question fait l' objet d' une réglementation qui oblige les exploitants d' aéronefs à garantir le maintien de la sécurité en permanence et les passagers à respecter les consignes de sécurité données par les membres d' équipage . -La question sera réglée ce soir . Le problème sera résolu ce soir . -La méthode journalistique n' est pas d' adopter une position morale , mais d' examiner l' importance et la pertinence de l' information et de permettre à chaque citoyen de prendre une opinion . La démarche journalistique n' est pas un positionnement moral , mais la recherche de l' intérêt et de la pertinence d' informations qui permettent à chaque citoyen de se forger une opinion . -Le juge a statué en août , la ville a violé la Constitution dans le cadre de son programme d' arrestation et de mise en examen des personnes . La juge avait statué en août que la ville avait violé la Constitution suite à la mise en œuvre de son programme visant à contrôler et interroger les citoyens . -Le juge a jugé que les agents de police violaient les droits civils de dizaines de milliers de personnes en mettant délibérément en cause des Noirs et des Hispaniques dans le cadre de leur programme . La juge avait statué que les officiers de police violaient les droits civils de dizaines de milliers de personnes en ciblant à tort des hommes noirs et d' origine hispanique dans le cadre de leur programme de contrôle et de fouille . -La juge a souligné qu' elle n' avait pas mis fin à la pratique , qui était constitutionnelle , mais qui a mis un terme à la façon dont le a mis en oeuvre ses arrêts . La juge a souligné qu' elle ne mettait pas fin aux pratiques de contrôle et de fouille , qui sont constitutionnelles , mais qu' elle la façon dont le mettait en œuvre ses contrôles . -Les juges ont marqué et au sujet de la façon dont ils ont été et de la façon dont ils ont été présentés dans l' anneau de présentation . Les juges ont remarqué que et présentaient bien et la façon dont ils ont défilé sur la piste . -Le jury a entendu jeudi que avait rencontré M. à propos d' une affaire avec une femme mariée alors qu' il s' était lui-même retrouvé avec Brooks , qui était mariée à l' époque . Le jury a entendu jeudi que avait confronté M. au sujet d' une liaison avec une femme mariée alors que lui-même fréquentait la Brooks , qui était également mariée à cette époque . -Le chaton a été trouvé à Argenteuil le 25 octobre et est décédé le 28 octobre . Le chaton a été trouvé le 25 octobre à Argenteuil et il est décédé le 28 octobre . -Ces dernières accusations reposent en partie sur des lettres écrites par l' avocat des frères de la Dominique , M. Emile Fournier , dans les années 80 , et sur les recherches effectuées dans ces archives au sujet du témoignage de M. Samuel Wilson Kennedy , ancien frère de la Croix qui a publiquement dénoncé les abus . Ces dernières accusations se basent entre autres sur des lettres rédigées par l' avocat des frères Sainte-Croix , Me Émile Perrin , dans les années 1990 , mais aussi par les recherches faites dans les archives à ce sujet par le frère Wilson Kennedy , un ancien frère de Sainte-Croix qui a dénoncé publiquement les sévices . -La loi a été vigoureusement opposée à la candidature de chacun des principaux candidats républicains à l' élection présidentielle de 2002 , y compris le candidat désigné Romney . Tous les grands candidats républicains à l' élection présidentielle en lice au cours des primaires de 2012 , y compris le candidat désigné Romney , se sont farouchement opposés à la loi . -La chute de l' avocat est survenue après que la police de Scarborough a découvert qu' il était en contact régulier avec en février 2011 . La chute de l' avocat est survenue après que la police , enquêtant sur Scarborough , a découvert qu' il avait été régulièrement en contact téléphonique avec en février 2011 . -L' avocat est retourné chez lui pour découvrir les drogues et il y a eu neuf communications entre eux . L' avocat est retourné chez lui , a découvert la drogue et ils se sont à neuf reprises . -Le chef du mouvement des talibans pakistanais , , a été tué lors d' une frappe aérienne américaine au Pakistan vendredi , selon les services de sécurité . Le chef du mouvement des taliban pakistanais , , a été tué dans une frappe d' un drone américain vendredi au Pakistan , a-t-on appris auprès des services de sécurité . -Les fuites ont placé le gouvernement américain au centre d' une tempête diplomatique avec ses alliés . Les fuites ont mis le gouvernement américain au centre d' une tempête diplomatique avec ses alliés . -Les locataires étaient contre cela et souhaitaient des conditions plus longues . Les locataires étaient contre et voulaient également des durées plus longues . -La lettre étend l' offre de coopérer avec les autorités allemandes " lorsque les difficultés de cette situation humanitaire ont été résolues " . La lettre avance une offre de coopération avec les autorités allemandes « lorsque les difficultés de cette situation humanitaire auront été résolues » . -La liste a souffert de la chute du dollar par rapport au yen , qui est nuisible pour les valeurs à l' exportation , et de l' avertissement du rendement annuel émis par Sony le jeudi . La cote a pâti du recul du dollar face au yen , préjudiciable aux valeurs exportatrices et de l' avertissement sur son résultat annuel lancé par Sony jeudi . -Le petit garçon , qui était resté avec son grand-père et son partenaire , était de son côté et a subi moins de brûlures graves . Le petit garçon , qui chez son grand-père et sa compagne , était à ses côtés et a été brûlé plus légèrement . -L' objectif principal de l' association est de permettre aux otages de porter plainte et d' amener leurs ravisseurs à la Cour pénale internationale . Son objectif principal : obtenir la possibilité pour les otages de porter plainte et d' amener leurs preneurs d' otages devant la Cour pénale internationale . -Les principaux marchés boursiers européens , à l' exception de Londres , sont en chute libre à ce vendredi . A l' exception de Londres , les principales Bourses européennes évoluent en baisse vendredi à , par certaines nouvelles de sociétés décevantes , tandis que est attendue en hausse . -La majorité des Américains - 158 millions d' entre eux en fait - célébreront l' Halloween cette année , réalisant un total de 6,9 milliards de dollars sur les bonbons , les costumes et les décorations , selon la National Retail Federation . La majorité des Américains – 158 millions d' entre eux en fait – Halloween cette année , dépensant un total de 6,9 Md $ en bonbons , et décorations , selon la National Retail Federation . -La majorité des foyers de soins palliatifs refuseront d' aider les patients à mourir , selon le vice-président de l' Alliance of Care Homes , Suzanne . La majorité des maisons de soins palliatifs refuseront d' aider les patients à mourir , selon la vice-présidente de l' Alliance des maisons de soins palliatifs , Suzanne . -La majeure partie de la croissance dans les années à venir viendra de ses systèmes de gaz naturel liquéfié en Australie . La majeur partie de la croissance dans les années à venir proviendra de ses projets de gaz naturel liquéfié en Australie . -La majorité de la violence est attribuée à l' État islamique d' Iraq et au ( ) , groupe affilié à Al-Qaida ( extrémistes sunnites ) , qui est également impliqué dans la guerre civile en Syrie . Une bonne partie des violences a été imputée à l' État islamique en Irak et au Levant ( ) , un groupe affilié à Al-Qaida ( extrémistes sunnites ) , également impliqué dans la guerre civile en Syrie . -Le fils violent d' un homme âgé de neuf ans s' est rendu en Australie avec lui et a été depuis le décès de son père , le mercredi , par un cousin adulte qui était également sur le navire . Le fils de l' homme , âgé de 9 ans , se rendait en Australie avec lui et a trouvé quelque réconfort depuis le décès de son père mercredi auprès d' un cousin adulte qui était également à bord . -L' homme concerné , Rob Ford , a toujours nié avoir pris le crack , mais a admis être en partie du cannabis . L' intéressé , Rob Ford , a toujours nié avoir consommé du crack , tout en reconnaissant avoir un penchant pour le cannabis . -L' homme de nombreuses curiosités est - un peu - centré sur une idée unique . L' homme des curiosités multiples tournait , légèrement , autour d' une même idée . -L' homme a succombé à ses blessures graves . L' homme n' a pas survécu à ses graves blessures . -Les maîtres de la cérémonie sont souvent des personnes qui ont déménagé dans ce nouveau genre d' emploi . Les maîtres de cérémonie sont souvent des gens qui se sont . -La pression des médias était énorme . La pression médiatique est énorme . -La réunion a lieu à l' hôtel de ville , au centre de Manhattan . Le rendez-vous a lieu à l' Hôtel de Ville , dans le centre de Manhattan . -La fusion réunit la deuxième plus grande agence du monde , , et la troisième plus importante , la . La fusion rapproche en effet la deuxième agence mondiale , , et la troisième , . -La région métropolitaine de Montréal connaît également sa part de pannes , avec près de 7,000 maisons dans la ville et à Laval , sans pouvoir . La grande région de Montréal connaît aussi son lot de pannes avec près de 7000 foyers privés de courant dans la métropole et à Laval . -Le géant de l' exploitation minière , qui conteste le paiement d' une taxe de vente au carbone de 6 millions de dollars , a fait la demande en tentant de s' interroger sur la question de savoir s' il avait un conflit . Le magnat de l' extraction minière , qui conteste le paiement d' une taxe carbone d' un montant de 6 M $ , a fait valoir cet argument pour essayer d' écarter les questions concernant un éventuel conflit . -Le ministre a été libéré sous condition . Le pasteur a été relâché sous conditions . -Le ministre a insisté sur le fait que le gouvernement de la Tasmanie serait soumis à un régime strict de déclaration et de responsabilisation . La ministre a affirmé que le gouvernement de Tasmanie serait confronté à un régime strict de préparation des rapports et de reddition de comptes . -Les ministères demandent à toute personne qui a été , , ou à la suite d' une membrane ou d' une peau sur la peau ou qui a été infectée par un animal qui pourrait avoir des contacts entre le 00 et le 28 mars , au contact du groupe de 19 à 95 V 00 entre et 18h du 1er novembre au 31 décembre 2010 . Pour plus de renseignements , veuillez contacter le service de l' assistance à la clientèle au ou par téléphone au ( 514 ) ou par téléphone au ( 514 ) ou par téléphone au ( 514 ) ou par téléphone au ( 514 ) ou par téléphone au ( 514 ) , poste , , , , , , , , , , , , , , , , , , , , , , , , , , , , , et . Les ministères appellent à présent les personnes qui auraient été , , , ou sur une muqueuse ou sur une peau lésée par ce chaton ou dont l' animal aurait été en contact avec ce chaton entre le 8 et le 28 octobre à contacter le entre 10 heures et 18 heures à partir du 1er novembre . -Les mineurs , âgés de 9 à 13 ans , sortaient de la porte de la porte pour réclamer des friandises le soir de la fête nocturne , lorsque : Les mineurs âgés de 9 et 13 ans faisaient du porte à porte pour réclamer des bonbons le soir d' Halloween . -Le monumental " de mémoire " ( sites de mémoire ) , auquel son nom est attaché , contribue également à cette image floue . A cette image incertaine le monument des " Lieux de mémoire " , auquel il a attaché son nom , a aussi contribué . -Les plus enthousiastes sont l' Oregon , qui recrute 5,000 pilotes dans la plus grande expérience du pays . Le plus déterminé est l' Oregon , qui a mobilisé 5 000 conducteurs pour mener l' expérience la plus importante du pays . -L' affaire la plus récente a été celle de l' ancien dirigeant du PCC à Chongqing , Bo , qui a été condamné à la prison pour la vie en septembre pour corruption et abus de pouvoir . Le cas récent le plus spectaculaire a été celui de l' ancien chef du PCC à Chongqing , Bo , condamné à vie en septembre pour corruption et abus de pouvoir après avoir visé les plus hautes fonctions au sein de l' Etat . -Le , Jonathan , âgé de 26 ans , de , ne portait pas de casque . Le , Jonathan , 26 ans , de , ne portait pas de casque . -Le mouvement est également soutenu par deux anciens secrétaires des transports des États-Unis qui , dans un rapport de 2011 , ont exhorté le Congrès à aller de l' avant dans la direction de l' . Le mouvement est également soutenu par deux anciens secrétaires américains aux Transports qui , dans un rapport de 2011 , le Congrès à aller dans la voie du paiement au mile . -Le mouvement est souvent accusé par les autorités chinoises de troubles sporadiques au Xinjiang , bien que de nombreux experts remettent en cause leur véritable influence . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Ce mouvement est souvent désigné par les autorités chinoises comme étant responsable des troubles sporadiques au Xinjiang , mais son influence réelle est mise en doute par plusieurs experts . -La musique recommence , et la séquence se poursuit . Et la musique de repartir , et la séquence de reprendre . -Les comédies musicales sont préparées à Paris et appréciées dans les provinces . La mayonnaise est montée à Paris et par les provinciaux . -Le directeur du service national , James , a défendu l' espionnage à l' encontre des alliés comme il le fallait et a dit qu' il était courant de part et d' autre . Le directeur des services de renseignement , James , a justifié l' espionnage des alliés en indiquant qu' il était nécessaire et qu' il s' agissait d' une pratique courante des deux côtés . -Le bénéfice net de sa filiale principale , Câble , s' est établi à 43,9 M $ , ou à 90 ¢ par action , soit M CAD , soit 93 ¢ par , pour la même période l' an dernier . Le bénéfice net de sa principale filiale , Câble , a été de 43,9 millions , ou 90 ¢ par action , en recul par rapport aux 45,7 millions , ou 93 ¢ par action , de la même période l' an dernier . -La nouvelle parution de est en retard , parce que l' édition a pris plus de temps que prévu . La sortie du nouveau a pris du retard en raison de son montage trop long . -Les nouveaux frais de port sont pour les sacs dans la , de sorte que de petits sacs sous le siège seront toujours libres . Les nouveaux frais de bagages à main concernent les sacs rangés dans le compartiment supérieur , donc les petits sacs installés sous le siège seront toujours gratuits . -La nouvelle loi n' oblige pas les parents à déclarer un sexe de ces enfants , ce qui permet aux parents de déclarer " non valides " ou " non spécifiés " dans leur certificat de naissance . Dans le cadre de la nouvelle loi , les parents ne seront plus tenus de déclarer le sexe de leur enfant , les autorisant à inscrire un sexe « indéterminé » ou « non spécifié » sur son certificat de naissance . -Le nouveau vaccin contient un virus inoffensif qui a été conçu pour transmettre l' information génétique afin de produire des anticorps . Le nouveau vaccin contient un virus inoffensif qui a été modifié pour transporter les informations génétiques nécessaires pour concevoir des anticorps . -La journée s' est terminée le même jour que le Credit Suisse a annoncé qu' il avait donné un coup de fil à son groupe de négociation de fonds de change à Londres cette semaine après qu' il eut subi une perte de près de 6 millions de dollars à la fin de l' année dernière . La nouvelle est arrivée le jour même où le Crédit Suisse a annoncé avoir renvoyé un trader travaillant dans son bureau des fonds négociés la Bourse de Londres cette semaine après qu' il a causé une perte de 6 M $ en fin d' année dernière . -Les nouvelles ont été publiées le jeudi par le Catholic National Reporter , un journal catholique indépendant aux États-Unis . La nouvelle a été rapportée jeudi par le " National Catholic Reporter " , un journal catholique indépendant des États-Unis . -Le journal , qui cite des documents publiés par l' ancien consultant irlandais , a dit que le programme " " , réalisé conjointement avec le bureau britannique de la NSA , les , a permis aux deux organismes de recueillir des données des câbles à fibre optique utilisés par les géants de l' Internet . Le journal , qui cite des documents obtenus auprès de l' de la NSA Edward , affirme que le programme baptisé " " , et mené avec l' homologue britannique de la NSA , le , permet à ces deux agences de récupérer des données depuis les fibres optiques utilisées par les géants d' internet . -Le journal a confié à l' enquêteur privé Glenn à la de et " " lui même , mais a également appelé et a laissé son , la cour a entendu . Il a été signalé à la cour que le journal a chargé Glenn , un détective privé , de la boîte vocale de et de « planter en permanence devant sa porte » , mais l' a également appelée et lui a laissé des messages vocaux . -Le lendemain matin , l' équipe commence à rencontrer des centaines de patients . Le lendemain matin , l' équipe commence à recevoir des centaines de patients . -La nuit précédente serait pratiquement vide . La nuit qui précède est quasiment blanche . -L' ancien chef de l' Elysée , qui a pris un verre le mardi dernier avec son équipe , sa famille et ses amis , a fini par un de douleur " , a déclaré François Hollande . Le désormais ancien chef des cuisines de l' Elysée , qui a pris un verre mardi soir avec son équipe , sa famille et des amis , part avec un pincement au cœur " Hollande est venu me saluer " . -Le fonctionnaire n' a pas précisé ce que signifiait la cible , mais a déclaré qu' il y en avait au moins un . Le responsable n' a pas précisé quelle était la cible , mais a dit qu' il y en avait au moins une . -La vieille fonction consistant à ne pas dire . La base connue de ce que l' on montre et que l' on ne dit pas . -La seule chose que nous ne voulons pas consommer , de maïs , et pourtant près de 35 millions de livres sont vendus à l' Halloween , selon la National Association . Les bonbons maïs sont la seule chose qu' on ne veut pas manger ; et pourtant presque 16 millions de kg sont vendus pour Halloween , selon la National Association . -Celui qui aux bords était debout au centre du centre . Le de se tenait au centre du centre . -Le seul espoir est d' améliorer la situation internationale . Le seul espoir , c' est une amélioration de la conjoncture internationale . -Le seul numéro que j' ai mentionné au ministre est 333 . Le seul nombre que j' ai mentionné au ministre , c' est 333 . -La seule autre compagnie aérienne à avoir une telle redevance est la Air de la Hongrie , a déclaré l' expert-conseil en vol Jay Sorensen , qui suit de près les frais supplémentaires . La seule autre compagnie imposant de tels frais est la compagnie hongroise Air , a déclaré le consultant auprès de compagnies aériennes Jay Sorensen , qui suit de près les frais en supplément . -La seule centrale électrique de la bande de Gaza a cessé de fonctionner le vendredi 1er novembre , à la suite de l' épuisement de sa réserve de carburant , l' Autorité palestinienne pour l' enclave palestinienne annoncée . La seule centrale électrique de la bande de Gaza a cessé de fonctionner vendredi 1er novembre après un épuisement de ses stocks de carburant , a annoncé l' Autorité de l' énergie de l' enclave palestinienne . -Le seul recours consiste à réduire les salaires et les dépenses publiques - encouragés par Berlin . Le seul recours est de faire des coupes sombres dans les salaires et les dépenses publiques – encouragées par Berlin . -L' opération avait pour but d' empêcher les futures attaques terroristes en utilisant ce tunnel , a expliqué une déclaration militaire israélienne . L' opération visait à empêcher de futures attaques terroristes utilisant ce tunnel , a expliqué un communiqué militaire israélien . -Les opinions exprimées dans ce commentaire sont celles de Morris . Les points de vue exprimés dans ce commentaire sont uniquement ceux de Morris . -L' arriéré de commandes de l' unité de transport s' établissait à 32,6 milliards de dollars au 30 septembre , soit une augmentation de 36 % par rapport au 31 décembre . Le carnet de commandes de la division Transport totalisait 32,6 Md $ au 30 septembre , soit une légère hausse par rapport au 31 décembre de l' année précédente . -L' organisation , appelée Vision , organise des écoles au cours de la dernière année pour aider les élèves de toutes les origines à trouver des références communes qui ne se limitent pas à la langue française . L' organisme Vision diversité se promène depuis un an dans les écoles pour aider les élèves de toutes origines à découvrir des repères communs qui ne se limitent pas à la langue française . -L' organisation a simplement indiqué que la médiation faisait partie d' un processus découlant d' une plainte . L' organisation a seulement signifié que la médiation fait partie du processus lors d' une plainte . -L' autre grande question est compréhensible : les parents ne veulent pas que leurs jeunes filles aient des périodes de repos . L' autre grand problème est compréhensible : Les parents ne veulent tout simplement pas que leurs très jeunes filles aient leurs règles . -L' autre soir , il m' a demandé de me dire : je ne sais pas , et je lui ai dit qu' il était " sorti " , mais dans une voix si grave que je me suis réveillé et se , et je me suis tourné vers la lampe de chevet et ai regardé autour de lui , convaincu qu' il allait venir . L' autre nuit , il m' a demandé si j' allais bien , je lui ai dit non , il me réponds " je " mais d' une voix tellement réelle que je me suis en sursaut , et j' ai allumé la lampe de chevet en regardant partout , persuadée qu' il allait arriver . -Les autres tests ont été effectués à dans les jours suivant mon retour : des radiographies , des examens de tous types et une nomination avec un psychiatre . Le reste a été fait au les jours suivants mon retour : radios , examens en tous genres et rencontre avec un psychiatre . -Les parents d' un adolescent de Géorgie , dont le corps a été retrouvé dans un de lutte contre la violence dans son centre d' entraînement de l' école secondaire , ont affirmé que son fils avait été assassiné , a déclaré l' avocat de la famille jeudi . Les parents d' un adolescent de Géorgie , dont le corps a été retrouvé dans un tapis de lutte roulé dans le gymnase de son lycée , pensent que leur fils a été assassiné , a déclaré l' avocat de la famille jeudi . -L' adoption de ce projet de loi s' inscrit dans une série de mesures visant à renforcer la sécurité nationale à la suite de l' attentat perpétré en septembre contre des tireurs islamistes dans le centre commercial de . L' adoption du projet de loi intervient en même temps qu' un train de mesures visant à renforcer la sécurité nationale dans le sillage de l' attaque perpétrée par des islamistes armés dans le centre commercial de en septembre dernier . -Les brevets couvrent la technologie qui aide à faire correspondre les possibilités de recherche Internet avec la publicité pertinente , le procès défendu , qui est le cœur de la recherche de Google . Les brevets contiennent des technologies qui permettent d ’ afficher une publicité en rapport avec des termes de recherche sur Internet , ont indiqué les conclusions du procès , ce qui est le cœur de métier de Google . -Le département des pharmacies a été sérieusement suite à de multiples départs dus à la retraite , au congé de maternité ou , tout simplement , à la démission . En raison de multiples départs à la retraite , de congés de maternité ou de départs tout court , le département de pharmacie s' est trouvé fortement déficitaire en personnel . -Le pipeline est conçu pour une capacité annuelle de 2,2 billions de pieds cubes de gaz naturel . Le gazoduc a été conçu pour une capacité annuelle de 63 milliards de mètres cubes de gaz naturel . -L' avion était complet , a-t-il dit , " il est monté à bord " . L' avion était plein , a-t-il ajouté , « les passagers sont montés à bord à la vitesse de l' éclair » . -La police , qui avait été avertie , a réussi à séparer les belligérants et s' assurer que les blessés reçoivent des soins médicaux . , les gendarmes se sont rendus sur place pour séparer les belligérants et veiller à ce que les blessés soient pris en charge . -La police n' est actuellement pas en mesure de fournir des informations sur l' identité des victimes ni sur leur affiliation politique . La police n' était pas en mesure pour l' instant de donner des informations sur l' identité des victimes et leur éventuelle appartenance politique . -La police est intervenue très rapidement et l' individu suspecté a été arrêté sur le toit d' un parking de l' aéroport . Très vite les forces de police sont intervenues et un homme suspecté d' être le tireur a été appréhendé sur le toit d' un parking de l' aéroport . -La police a indiqué qu' elle avait également indiqué qu' elle des colis à Rob Ford , conformément à de nouveaux documents de la cour . La police a dit avoir aussi observé cet homme délivrant des colis à Rob Ford , indiquent de nouveaux documents de cour . -Les dirigeants politiques de l' espèrent parvenir à un accord . Sur place , les dirigeants politiques du espèrent parvenir à un accord . -Le sondage a été envoyé à chaque conférence nationale des évêques à la mi-octobre , avec des instructions pour assurer un maximum de réponses possibles . Le sondage a été envoyé à la mi-octobre à chaque conférence nationale des évêques , avec la consigne d' obtenir le plus de réponses possible . -Le sondage demandera aux participants comment les prêtres traitent avec les couples gais et leurs enfants , et comment ils traitent les hommes et les femmes qui vivent ensemble sans être unis par des liens de mariage . Le sondage demande aux participants comment les prêtres s' occupent des couples gais et de leurs enfants , et comment ils traitent les hommes et les femmes qui vivent ensemble sans être unis par les liens du mariage . -Le changement possible était un secret bien gardé à l' intérieur de l' infrastructure de campagne de Chicago et à l' intérieur du bureau ovale . La possibilité d' un échange est un secret qui a été farouchement gardé au sein du QG de campagne de Chicago et à l' intérieur du Bureau ovale . -Le potentiel était là où elle aurait pu être gravement blessée ou pire . Elle aurait pu être gravement blessée ou pire . -Les téléphones mobiles prépayés sont ensuite supprimés par la suite . Les téléphones portables prépayés sont ensuite jetés . -Le président du Conseil central des Juifs d' Allemagne , Dieter , a déclaré qu' il avait été choqué par la révélation . Le président du Conseil central des juifs d' Allemagne , Dieter , s' est déclaré choqué par cette révélation . -Le communiqué de presse a simplement déclaré qu' il était " blessé " , sans dire ce qu' il en était . Le communiqué indique simplement qu' il est " blessé " , sans préciser la nature de la blessure . -La théorie générale sur la façon de traiter les enfants atteints d' un trouble est proposée par le Dr John Money , de l' Université Johns Hopkins , qui affirme que le sexe est . La théorie dominante sur la façon de traiter les enfants pourvus d' organes sexuels ambigus a été lancée par le Dr John Money , de l' université , qui considérait que le genre est . -La veille , le Premier ministre pakistanais , Sharif , a informé le gouvernement britannique lors d' un voyage à Londres que des discussions avaient été entamées avec et le mouvement taliban pakistanais ( ) , le mouvement des talibans pakistanais . La veille , le Premier ministre pakistanais Sharif , en déplacement à Londres , avait informé le gouvernement britannique que des discussions avaient été engagées avec le e Taliban Pakistan ( ) , le mouvement des taliban pakistanais . -Le prix du pétrole a continué de chuter le vendredi , en raison des inquiétudes suscitées par les excédents de production , ce qui témoigne d' un rapport que le secteur chinois de la fabrication d' armes à haut risque est en train de renforcer . & nbsp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; & amp ; Le prix du pétrole a continué à baisser vendredi alors que les préoccupations concernant l ’ importance des stocks compensent un rapport montrant que le secteur manufacturier chinois gourmand en électricité est en train de se renforcer . -Le principe de l' aide à la sécurité pour l' Irak est soutenu par des sénateurs républicains et démocrates influents . Le principe d' une aide accrue à l' Irak en matière de sécurité est soutenu par d' influents sénateurs républicains et démocrates . -La priorité doit être l' emploi et la croissance en Grande-Bretagne et , comme le montrent les résultats de nos enquêtes , pour les affaires , cela signifie une nouvelle concentration sur le commerce et une modification fondamentale de l' approche de la politique de Bruxelles . La priorité doit être l' emploi et la croissance en Grande-Bretagne et , comme les conclusions de notre enquête le montrent , pour les entreprises cela signifie mettre à nouveau l' accent sur le commerce et un changement fondamental de l' approche réglementaire de Bruxelles . -Les bénéfices des activités de production ont diminué légèrement au cours du troisième trimestre , tandis que les bénéfices des activités en aval ( y compris le raffinage et la production chimique ) ont chuté de 45 % à 380 millions de dollars . Les bénéfices tirés des activités de production ont légèrement diminué au troisième trimestre , tandis que les profits des activités en aval ( dont le raffinage et la production chimique ) ont chuté de 45 % à 380 millions de dollars . -Le projet devra être ratifié par le gouverneur de Pitcairn , qui est basé en Nouvelle-Zélande , et le gouvernement britannique . Le projet doit être accepté par le gouverneur anglais de Pitcairn , résidant en Nouvelle-Zélande , et par le gouvernement britannique . -La protéine que nous utilisons dans la glace réagit avec votre langue au pH neutre . La protéine que nous utilisons dans la glace réagit avec la langue à pH neutre . -Le cinéaste provocateur a peut-être demandé à ses victimes de s' attaquer à une partie de son visage , mais il a dû s' adresser à des professionnels du cinéma pour les scènes les plus noires , avant de fusionner les corps numériques : la moitié principale est l' étoile , la moitié inférieure est le double . Si le cinéaste provocateur a demandé à ses acteurs de mettre leur pudeur au placard , il a eu recours à des professionnels du X pour les scènes de sexe les plus , avant de fusionner tous les corps numériquement : au-dessus de la ceinture , c' est la star ; au-dessous , la doublure . -Le public pourra profiter des prouesses techniques de jeunes patineurs , dont certains , comme la jeune étoile de , Lorenzo , ont déjà participé à des compétitions de haut niveau . Le public pourra admirer les prouesses techniques de jeunes qui , pour certains , fréquentent déjà les compétitions au plus haut niveau , à l' instar du jeune prodige Lorenzo . -La publication a été publiée le jour où le Premier Ministre iraquien a rencontré le Président américain . Cette publication a lieu le jour où le Premier ministre irakien est reçu par le Président américain . -La marche à suivre est le fonds d' affectation spéciale de la Route du Pacifique , financé par les taxes que les Américains paient à la pompe à gaz , est cassée . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; La pression vient du fait que le Highway Trust Fund du pays , financé avec les taxes que les Américains paient à la pompe , est financièrement à sec. -Le raid a eu lieu dans des circonstances presque identiques à celles du 5 juillet : à cette occasion , il y avait aussi un représentant américain avisé qui avait confirmé à CNN une attaque israélienne qui visait les missiles sol-air à destination de Damas par la Russie . Le raid s' est déroulé dans des circonstances à peu près identiques à celui qui avait eu lieu le 5 juillet : à l' époque , c' est aussi un responsable américain anonyme qui avait confirmé à CNN une attaque israélienne , visant cette fois-ci des missiles , livrés par la Russie à Damas . -La compagnie d' extermination du rat réalise deux opérations par an , et nous prenons en charge chaque fois que nous recevons une plainte de la part de la population . L' entreprise de réalise deux passages par an , et en plus , nous intervenons , à notre charge , à chaque demande des habitants . -Le rythme des progrès des troupes gouvernementales est sans précédent . Le rythme de progression des troupes gouvernementales est aujourd' hui sans précédent . -Le des tirs d' armes légères pourrait bien être entendu à l' arrière-plan . On entendait très nettement derrière lui des nourris d' armes légères . -Les modèles rappelés ont été construits entre le 1er août et le 10 septembre . Les modèles rappelés ont été construits entre le 1er août et le 10 septembre . -La réduction des marges de raffinage affecte l' ensemble du secteur . Cette baisse des marges de raffinage touche l' ensemble du secteur . -Le refus des neuf États a commencé avec le Texas , qui a refusé de mettre en œuvre ces mesures dans les installations de la Garde nationale en raison d' un conflit entre la loi et la loi fédérale sur le mariage entre personnes du même sexe . La des neuf États a débuté par le Texas qui refuse de mettre en oeuvre ces prestations sur les implantations de la Garde nationale , en raison d' un conflit entre la loi et la loi fédérale sur le mariage homosexuel . -La relation que nous parlons n' est pas simplement un trafiquant de drogue , mais un trafiquant de drogue qui donne son propre avocat à la drogue . La relation dont nous parlons n' est pas simplement un trafiquant de drogue , mais un trafiquant de drogue fournissant de la drogue à son propre avocat . -Le représentant ajoute : " Peut-être la confondre avec quelqu' un d' autre . Le représentant a ajouté : « Peut-être qu' elle le confond avec quelqu' un d' autre . » -Le rapport donne également un aperçu de la discrimination dont sont victimes les et les transsexuels dans le domaine de l' emploi , ainsi que les niveaux de harcèlement , de violence et de crimes de discrimination . Le rapport donnait également un aperçu de la discrimination à laquelle étaient confrontées les personnes et transgenres en matière d' emploi , ainsi que l' ampleur des actes de harcèlement et de violence et des crimes motivés par des préjugés . -Le rapport comprend également une liste des symptômes typiques signalés par les enfants qui ont une réaction allergique . Le rapport comprend également une liste des symptômes typiques communiqués par les enfants qui ont une réaction allergique . -La recherche abordera l' inclusion sous tous les angles : la langue , l' éducation , le social et la culture . Les recherches porteront sur l' inclusion sous tous ses angles : linguistique , scolaire , social et culturel . -Les chercheurs disent que les résultats pourraient amener les propriétaires , les vétérinaires et les formateurs à mieux comprendre les émotions de leur animal . Les chercheurs indiquent que les résultats pourraient permettre aux propriétaires , aux vétérinaires et aux de mieux appréhender les émotions de leurs animaux . -Les habitants de Bruxelles méritent mieux que de voir leur argent perdu par un ministre démocrate chrétien et flamand , qui exécute le nouveau programme de l' Alliance elle-même . Les méritent mieux que de voir leur argent gaspillé par une ministre CD & V qui réalise elle-même le programme de la . -Les restrictions sont également devenues de plus en plus difficiles à appliquer à mesure que l' utilisation des dispositifs est devenue omniprésente . Les restrictions sont également de plus en plus difficiles à appliquer car l' utilisation des appareils est devenue omniprésente . -Le résultat est des corps qui semblent disposés en avant des dessins . Résultat : les corps semblent à plat devant les dessins . -La révélation est la plus notable des images de Mark et de la toute première campagne de 2012 de John , " Double Down : Game Change 2012 " . Cette révélation est l' évènement le plus marquant qui figure dans le très attendu livre de la campagne 2012 de Mark et John , « Double Down : Game Change 2012 » . -La course a été fermée jusqu' à nouvel ordre . Elle a été fermée jusqu' à nouvel ordre . -Le risque couru par les Américains pourrait être de deux ordres . Le risque couru par les Américains peut être double . -Le parti politique , qui est la force politique dominante depuis 1975 , et le se sont mutuellement opposés à la tension . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Le , le parti au pouvoir et la principale force politique depuis 1975 , et la se rejettent mutuellement la responsabilité des tensions . -La connaissance de la façon dont un enfant conçoit une identité liée au sexe n' est pas très précise . La science qui consiste à savoir comment un enfant développera une identité sexuelle n' est pas très précise . -La seconde est d' offrir des services personnalisés : dans quelques années , un serveur vocal pourra reconnaître votre voix et vous proposer des solutions personnalisées . La deuxième est d' offrir des services personnalisés : dans quelques années , un serveur vocal pourra reconnaître votre voix et vous proposer des options sur mesure . -Le second des cinq avions d' essai devrait prendre le vol dans les semaines à venir , et les autres suivront peu après , selon les dires de l' entreprise . Le deuxième des cinq avions d' essai devrait s' envoler dans les semaines à venir , les autres suivront peu de temps après , a indiqué la société . -La seconde a essayé de tuer le premier , mais elle n' a pas réussi à l' effacer ; au contraire , elle a su se moquer de son caractère sacré et être si ardemment attachée à l' unité et à l' indivisibilité qu' elle a coupé la tête de la personne qui était l' incarnation puissante et des deux . . La seconde a voulu la mort de la première , mais sans parvenir à la nier ; soucieuse au contraire d' en retrouver la et d' autant plus avide d' unité et d' indivisibilité qu' elle avait coupé la tête à celui qui en était l' évidente et puissante incarnation . -La section du chemin périphérique où l' accident a eu lieu a été fermée à la circulation jusqu' à 9h pour la réouverture de la route et pour qu' un expert puisse déterminer les circonstances de l' accident . Le ring a été fermé à la circulation , jusqu' à 9h00 , à l' endroit de l' accident , le temps que la route soit et qu' un expert détermine les circonstances de l' accident . -Les secteurs les plus touchés sont les Laurentides , la Montérégie et l' Outaouais , dont les , les et les ont respectivement plongé dans les ténèbres . Les secteurs les plus touchés sont les Laurentides avec 15 042 abonnés plongés dans le noir , la Montérégie avec 13 464 et l' Outaouais avec . -La mise en place d' une technologie d' interception à la fine pointe de la technologie au cours des dix dernières années par les États-Unis - et par la France - a été officiellement justifiée par la lutte contre le terrorisme . La mise en place depuis en gros dix ans d' outils technologiques d' interception très puissants par les Etats-Unis , mais aussi par la France , a officiellement été justifiée par la lutte contre le terrorisme . -L' agence de sécurité nationale a déclaré vendredi dernier qu' il s' agissait d' un bug qui a fait passer son site Web public à quelques heures , et non pas des pirates informatiques comme ceux annoncés en ligne . La National Security Agency œuvrant dans l' ombre a déclaré vendredi que c' était un bug qui avait causé la panne de son site public pendant quelques heures , et non des hackers comme certains l' avaient prétendu en ligne . -La boutique , qui est strictement réservé aux développeurs qui ont déjà Google de verre , a quelques accessoires , comme un chargeur et un câble USB , pour 50 $ . Cette boutique , strictement réservée aux développeurs disposant déjà de Google Glass , propose quelques accessoires tels qu' un chargeur et son câble USB , pour 50 $ . -Le magasin appelait la police à l' aide , mais " au moment où il arrivait déjà qu' il avait déjà quitté une scène " , a-t-elle ajouté . Le magasin a appelé la police à la rescousse , mais " le temps qu' elle arrive , il était déjà parti sans " , a-t-elle ajouté . -Le spectacle du LG Arena à Birmingham aura lieu le 20 novembre et le spectacle aura lieu à Glasgow Hydro le 21 novembre . Le spectacle à la LG Arena de Birmingham aura lieu le 20 novembre et celui à l' Hydro de Glasgow se tiendra le 21 novembre . -Le spectacle est conçu pour être présenté dans tout théâtre majeur en France , de la même manière qu' à Paris . Le show est conçu pour entrer dans toutes les grandes salles de France sous la même forme qu' à Paris . -Les spectacles sont très attendus au moment où ils atteignent un provincial ( théâtre ) . Ces spectacles sont très attendus quand ils arrivent dans un Zénith de province . -La fermeture aux États-Unis a ralenti le marché automobile . Le aux USA a freiné la hausse du marché automobile -La femme du chanteur a également déclaré que , une semaine avant sa mort , elle avait promis à son mari qu' elle l' de l' hôpital et le à son domicile à ( Long Island ) . L' épouse du chanteur explique également qu' elle avait promis à son mari , la semaine avant son décès , de le faire sortir de l' hôpital et de l' emmener dans leur maison de Springs ( Long Island ) . -La situation n' a pas beaucoup changé depuis le décès du père en 2002 . La situation n' a pas beaucoup changé depuis le décès du père du Dr en 2002 . -La sixième édition de Play se tiendra au parc de patinage à samedi , le 02 novembre . La sixième édition du Play a lieu ce samedi 2 novembre au skate park à Hyères . -La petite ville de Pia a connu une hauteur inhabituelle de fièvre le jeudi soir . La petite commune de Pia a connu un inhabituel excès de fièvre jeudi soir . -L' image souriante de mon père reste avec moi pendant mes activités quotidiennes . C' est l' image de mon père souriant qui m' accompagne dans mes gestes au quotidien . -L' avocat , surnommé " " et reconnu par les criminels comme étant un " excellent point de départ " , a été arrêté à son domicile en 2011 à la suite d' une opération de surveillance policière à Scarborough , qu' il avait rencontrée dans un essai de drogue antérieur . L' avocat , qui était surnommé « » et considéré par les criminels comme un « dossier de première importance » , a été arrêté chez lui en 2011 à la suite d' opérations d' écoute de la police dirigées vers Scarborough , qu' il avait représenté lors d' un précédent procès en relation avec le trafic de stupéfiants . -L' avocat craignait qu' il ne soit exposé à la toxicomanie secrète . L' avocat avait peur que son addiction secrète à la drogue ne soit révélée au grand jour -Les riffs de guitare font que vous voulez quitter la terre . Les riffs de guitare , , donnent envie de quitter la Terre . -La scène est partiellement en gris , les danseurs et le piano sont couverts en tissu , comme les meubles dans une maison abandonnée . La scène est en partie de gris , les danseurs et un piano sont recouverts de tissus , comme des meubles dans une maison délaissée . -Les enjeux sont importants - l' obtention d' un accord sur les réformes et l' amélioration de la position de la Grande-Bretagne font que le référendum sur l' adhésion à l' UE dans le cadre d' un référendum marque une hausse de 16 % . Les enjeux sont élevés – parvenir à changer le traité et mieux représenter les intérêts de la Grande-Bretagne permettrait de voir un basculement de 16 % des votes en faveur du maintien du pays dans l' UE lors d' un référendum . -L' enjeu est important pour l' essor rapide de l' économie , comme la découverte de grandes réserves de gaz à effet de serre et les gisements de charbon dans le Nord-Ouest d' ici à plus de $ d' investissements dans les prochaines années par des entreprises telles que Rio Tinto , Vale du Brésil , d' Italie et des États-Unis . & nbsp ; & nbsp ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; amp # 160 ; & # 160 ; & # 160 ; & # 160 ; » & # 160 ; » L' enjeu est considérable pour l' économie en pleine croissance puisque la découverte d' énormes réserves de gaz offshore et de gisements de charbon dans le nord-ouest pourrait attirer plus de 50 Md $ d' investissements au cours des prochaines années provenant de sociétés comme Rio Tinto , Vale du Brésil , d' Italie et des États-Unis . -Le lancement de la construction du gazoduc South Stream en Bulgarie marque le lancement de l' un des plus grands projets énergétiques de l' Europe , a déclaré le chef de Gazprom . Le début de la construction du gazoduc South Stream en Bulgarie marque le lancement de l' un des plus grands projets énergétique d' Europe , a déclaré le directeur de Gazprom . -L' État devrait maintenir les niveaux de financement actuels pour recevoir les fonds du Commonwealth et faire rapport chaque mois sur l' affectation des fonds supplémentaires . L' État devra maintenir les niveaux de financement actuels afin de recevoir de l' argent du Commonwealth et préparer des rapports mensuels sur les secteurs où il dépense les fonds supplémentaires . -Le stock des espèces de Sousa , dont la population découverte vient de se reproduire , s' est progressivement affaiblie , ce qui la rend encore plus vulnérable . Pour ce qui est de l' espèce Sousa , dont la population de la nouvelle espèce découverte est issue , son stock est automatiquement devenu plus faible , la rendant ainsi plus vulnérable . -Le corps étudiant a été , à chaque fois , la cible d' une initiative qui a eu beaucoup de succès la saison dernière . À chaque fois , c' est la clientèle scolaire qui sera ciblée , une initiative qui avait connu beaucoup de succès au cours de la saison précédente . -Les étudiants volontaires paient souvent leur propre chemin vers le Cameroun , souvent avec des dons médicaux . Les étudiants bénévoles paient en général leur voyage jusqu' au Cameroun , et arrivent souvent avec du matériel médical collecté . -L' action visant à bloquer cette loi a été lancée par 26 États et la Fédération nationale des entreprises indépendantes . La procédure visant à bloquer la loi a été engagée par 26 États et la National Federation of Independent Business . -Le programme mettra davantage l' accent sur les " problèmes du monde réel " , y compris les mathématiques financières . Le programme mettra davantage l' accent sur les « problèmes du monde réel » , notamment les mathématiques financières . -La fête du thé est . Le Tea Party est . -La technologie est là pour le faire . La technologie est là pour le faire . -Les parents de l' adolescent ont dit espérer que la vidéo des indices de la mort . Les parents de l' adolescent ont déclaré qu' ils espéraient que les images vidéo contiennent des indices sur les circonstances de sa mort . -Le géant des télécommunications Rogers a déjà indiqué qu' il pourrait offrir si certains détails techniques ( qu' il ne cite pas ) pourraient être corrigés . Le géant des télécommunications Rogers a déjà indiqué qu' il pourrait offrir si certains détails techniques , qui n' ont pas été cités , pouvaient être réglés . -Le territoire mesure , dont trois autres petites îles proches . Ce territoire mesure 47 km2 , si l' on compte trois autres petites îles proches . -L' essai , si tel était le cas , serait appliqué à l' aide de critères permettant d' éviter que les servent à occuper des postes non qualifiés ou comme moyen de locomotion pour déplacer la famille et les amis vers l' Australie . Le test , s' il est adopté , serait appliqué selon un critère destiné à empêcher les visas 457 d' être utilisés pour occuper des postes non qualifiés ou comme un moyen détourné pour faire venir sa famille et ses amis en Australie . -Le directeur du théâtre a immédiatement commencé une procédure d' évacuation et a appelé la brigade des pompiers à découvrir une odeur suspecte . Immédiatement , le directeur de l' établissement a fait procéder à l' évacuation de la salle et a prévenu les pompiers pour une odeur suspecte . -Les trois principaux fabricants de Detroit ont réalisé une croissance de plus de 10 points par rapport à octobre 2012 , mais seulement General Motors a fait des progrès meilleurs que prévu . Les trois grands constructeurs de Detroit affichent une croissance à deux chiffres par rapport à octobre 2012 mais seul General Motors a fait mieux qu' attendu . -Le moment est venu de rétablir la paix " . Le temps est venu de ramener la paix . -Le nombre total de morts en octobre est le plus élevé depuis avril 2008 , année où personnes ont été tuées . Le nombre total de morts en octobre est le plus élevé depuis avril 2008 , quand 1 073 personnes avaient été tuées . -La ville , qui est un bastion politique et le dernier bastion de la rébellion , se trouve à la frontière avec l' Ouganda , à environ 80 km au nord de Goma . politique et dernière place forte de la rébellion , cette localité est située à la frontière ougandaise , à environ 80 km au nord de Goma . -La ville a décidé d' utiliser des DEL pour réduire les coûts . Pour alléger ses coûts , la ville a recours à des . -Les habitants ont été heureux d' apprendre que le lac à la pêche et le lac des Cèdres continueront de fournir de l' eau potable à la ville . Les citoyens ont été heureux d' apprendre que les lacs à la Pêche et des Piles continueront d' approvisionner la ville en eau potable . -La tragédie s' est produite à environ 11 heures sur la rue , près de la jonction avec la rue , dans le quartier de . Le drame est survenu vers 11 h , sur la rue , tout près de l' intersection de la rue , dans l' arrondissement de . -Le transfert de lits de médecine intérieure est prévu pour un certain temps au cours du mois . Quant au transfert des lits de médecine interne , il est programmé pour le courant du mois . -Les marges de division du transport ont été affectées par les problèmes d' exécution dans quelques grands contrats . Les marges de la division Transport ont souffert des problèmes d' exécution concernant quelques gros contrats . -Le grand-père traumatisé était à l' étage au moment de l' explosion . , le grand-père était à l' étage au moment de l' explosion . -La compagnie de voyages s' attend maintenant à ce que son investissement à long terme avant impôt s' élève à 325 millions de dollars et 340 millions de dollars , comparativement à un montant de 305 millions de dollars à 315 millions de dollars qu' il avait prévu à l' origine . L' agence de voyage s' attend désormais à réaliser un bénéfice sous-jacent sur l' ensemble de l' exercice situé entre 325 et 340 M $ avant impôts , contre une précédente prévision de 305 à 315 M $ . -La tendance à la puberté précoce n' est pas aussi prononcée que chez les garçons La tendance vers une puberté précoce n' est pas aussi prononcée chez les garçons -Le tribunal a également le pouvoir de saisir les biens d' un délinquant si une amende n' est pas payée . Le tribunal a également le pouvoir de saisir les biens d' un contrevenant si celui-ci n' a pas payé son amende . -Le tunnel , qui la longueur de presque six terrains de football , relie les entrepôts près de Tijuana , Mexico et San Diego , USA . Le tunnel , sur une longueur équivalente à près de six terrains de football de long , relie des entrepôts près de Tijuana , au Mexique , à San Diego , aux États-Unis . -Le tunnel est le huitième grand passage découvert à San Diego depuis 2006 . Le tunnel est le huitième passage important découvert à San Diego depuis 2006 . -Le tunnel a été fermé avant que les médicaments ne soient détectés par les autorités . Le tunnel a été fermé avant que les drogues n' en soient ressorties en passant inaperçues , ont déclaré les autorités . -Les deux ont été trouvés tard le vendredi après-midi . En fin d' après-midi ce vendredi , les deux ont été localisés . -Les deux hommes , un autre , l' inconnu , n' ont pas le métro le jeudi à , dans le but de la des , dans le district de . , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , . Les deux hommes , l' un à la pratique et l' autre non , étaient partis sous terre jeudi vers 21h30 afin d' effectuer une traversée de la de , situé sur la commune de . -Les deux hommes , qui sont des soldats du 13e bataillon des troupes françaises stationnés à Chambéry , ont été trouvés " épuisés mais " . Les deux militaires au 13e bataillon des alpins de Chambéry , ont été retrouvés " épuisés , mais non blessés " . -Les deux commerçants seront les premiers travailleurs du à être suspendus dans l' extension qui se reflète dans le scandale de manipulation des crédits interbancaires . Les deux traders sont les premiers employés de à être suspendus dans le cadre de l' élargissement de l' enquête qui fait écho au scandale des manipulations du taux interbancaire . -Les deux ouvrages publiés par Nora il y a près de deux ans ont déjà prouvé que même s' il ne semblait pas écrire , il a écrit : et bien . Les deux ouvrages publiés par Nora voici bientôt deux ans ont déjà établi que celui qui passait pour ne pas écrire avait beaucoup et bien écrit . -Le but ultime est toujours d' offrir des outils dont nos clients n' ont pas encore accès . Le but ultime demeure d' offrir des outils auxquels nos clients n' ont pas accès actuellement . -L' Union des journalistes allemands a exhorté ses membres à cesser d' utiliser les services de Yahoo et de Google en ligne jeudi , après de nouvelles révélations sur les activités des services de renseignement américains et britanniques . Le syndicat des journalistes allemands a invité jeudi ses adhérents à cesser d' utiliser les services en ligne de Google et de Yahoo au lendemain de nouvelles révélations sur les activités des services de renseignements américains et britanniques . -Les utilisations de la biométrie se divisent en deux groupes : l' identification ( reconnaissance d' une personne parmi d' autres ) et l' authentification ( confirmation d' une personne qu' elle est censée dire ) . Les utilisations de la biométrie se répartissent en deux groupes : l' identification ( reconnaître une personne parmi d' autres ) et l' authentification ( s' assurer que la personne est celle qu' elle prétend être ) . -La scène généralement de la planification de la route a soudainement suscité un débat intense et des alliances colorées . Le secteur généralement sans intérêt de la planification des grands axes a soudain provoqué un débat fort animé et des alliances . -Les valeurs qu' elle à moi sont toujours là , et je me donc de me demander ce que je veux lui faire à un moment donné . Les valeurs qu' elle m' a sont toujours présentes , si bien qu' il m' arrive de penser à comment elle voudrait que j' agisse à un moment donné . -La grande majorité des avions de ligne devraient être admissibles à un plus grand usage de dispositifs électroniques au titre des nouvelles lignes directrices , a déclaré . La vaste majorité des avions de ligne devraient permettre une utilisation plus large des appareils électroniques en vertu des nouvelles directives , a déclaré . -Le véhicule , un , s' est concentré sur la foule dans la fameuse place de la capitale chinoise , symbole de la répression sanglante de 1989 , avant de brûler le feu , tuant ses trois occupants et deux passants . Le véhicule , un SUV , a foncé sur la foule qui se trouvait sur la célèbre place de la capitale chinoise , symbole de la répression meurtrière menée en 1989 , et a pris feu tuant ses trois passagers et deux passants . -Le véhicule portait également des conteneurs remplis d' essence et un drapeau avec des écrits religieux orthodoxes . Cette dernière contenait également des récipients remplis d' essence et un drapeau avec des inscriptions religieuses orthodoxes . -Le verdict vient à l' horizon du dixième anniversaire de l' attaque qui se déroulera cette année , qui sera marquée par des cérémonies à Bali et en Australie . Le verdict a été rendu à la veille du 10e anniversaire des attentats qui sera célébré plus tard cette année et marqué par des cérémonies à Bali et en Australie . -Le vétérinaire a dit que si elle était devenue trop lâche et , elle aurait probablement pu tomber . Le vétérinaire a dit que si elle avait été trop épuisée et qu' elle s' était , elle serait probablement tombée . -Les téléspectateurs ont partagé leurs pensées sur Twitter . Sur Twitter , les téléspectateurs ont partagé leurs commentaires . -La violence est au centre des discussions des États-Unis avec le Premier ministre Nouri Al , qui sera reçu par le Président Barack Obama le 2 novembre , après leur dernière réunion , le 12 décembre 2011 . Les violences sont au centre des entretiens aux États-Unis du premier ministre Nouri Al qui sera reçu vendredi par le président Barack Obama , deux ans après leur précédente rencontre , le 12 décembre 2011 . -La violence est de plus en plus meurtrière à la suite des mesures de sécurité renforcées et des opérations militaires de grande envergure menées ces derniers mois par le gouvernement d' Nouri , dominé par les chiites . Ces violences sont de plus en plus meurtrières en dépit de mesures de sécurité renforcées et d' opérations militaires d' envergure lancées depuis des mois par le gouvernement de Nouri Al , dominé par les chiites . -Le virus infecte de façon sélective les cellules du foie , lesquelles commencent alors à produire un flux régulier des anticorps . Le virus infecte de manière sélective les cellules du foie , qui commence alors à produire un flux régulier d' anticorps . -Le vote n' est pas contraignant , mais il l' image de , le troisième homme le plus riche du monde , qui est encore dans la victoire de son bateau dans la Coupe des Amériques . Le vote n' est pas contraignant mais vient ternir l' image de Larry Ellison , troisième fortune mondiale tout juste de la victoire de son bateau à la Coupe de l' America . -La taille du américain moyen est de pouces , selon les statistiques américaines sur la santé . Le tour de taille de l' Américain moyen du XXIe siècle est de 39,7 pouces , selon les statistiques sanitaires américaines . -Le rail d' eau était convenable et bien au moment où il a été libéré . Le d' eau était en bonne santé lorsqu' il a été relâché . -Le chemin de fer d' eau a été mis sur un hélicoptère à Aberdeen le mois dernier avant d' être repris à la santé par la écossaise dans son centre de sauvetage à . On a fait monter le d' eau à bord d' un hélicoptère en direction d' Aberdeen le mois dernier avant qu' il soit remis sur pieds par la écossaise dans son centre de sauvetage d' . -La façon dont il a commencé à faire son apparition sera au Cheval de l' année avant le long terme - et je suis sûr qu' il fera bien . De la façon dont il a commencé , il participera sûrement bientôt à la manifestation du Cheval de l' année – et je suis sûre qu' il fera une belle prestation . -Le gagnant a été invité au Grand Prix à Abu Dhabi avec un ami et a pu se rapprocher du champion allemand , sur la piste et dans les . Il a été invité suivre le GP d' Abou Dhabi , avec un ami , et peut donc voir de près le champion allemand , sur la piste et dans son stand . -Les travaux de Ltd suggèrent des services à grande vitesse pour l' Écosse et le nord de l' Angleterre dès le lancement de la phase 1 en 2026 . Le travail réalisé par Ltd laisse penser que les services à grande vitesse jusqu' à l' Écosse et le nord de l' Angleterre dès l' ouverture de la Phase 1 en 2026 . -Le quatrième du monde indique qu' il a livré 45 avions au cours du trimestre , contre 57 l' année précédente . Le quatrième constructeur aéronautique au monde a indiqué qu' il avait livré 45 avions au cours du trimestre , contre 57 un an plus tôt . -Le jeune , du village de , près de Vale , au sud de la campagne , suit les traces de sa tante Sharon , qui a fait des courses depuis plus de 10 ans . Ce jeune garçon , du village de , près d' Vale , dans le sud du pays de Galles , suit les traces de sa tante Sharon , qui participe à des concours équestres depuis plus de 10 ans . -Leurs corps ont été découverts plus tard . Leurs corps ont été découverts plus tard . -Leurs sont , ou . Les amours sont , ou joueurs . -Leur chance ne s' arrête pas toujours . La chance ne les suit pas toujours . -Leurs tâches incluent la modification d' une pompe sur les défectueux . Il s' agit pour eux , entre autres , de changer une pompe sur la chaufferie défaillante . -Leur © de la mГ © © sente la vraie © . Leurs en musique , , donnent une vraie fragilité . -Puis , une sorte de réajustement très doux à la vie a commencé , à un ou deux mois qui était un peu comme des vacances . Commence ensuite une sorte de réadaptation à la vie , très doucement , pendant un ou deux mois qui sont un peu comme des vacances . -Ensuite , seule la colline de sera laissée . Il ne restera plus que la colline de . -Puis , les signes ont commencé à apparaître . Puis des signes sont apparus . -Ensuite , il y a les comédies musicales avec les somptueuses productions du Châtelet , qui nous permettent d' abandonner l' âge d' or de Broadway à la vie et de redécouvrir l' univers de Stephen , le plus grand compositeur vivant . Après , on a les comédies musicales avec les ­ productions somptueuses du Châtelet qui nous font revivre l' âge d' or de Broadway et découvrir l' univers de Stephen , le plus grand ­ compositeur vivant . -Ensuite , il n' y aura plus de salariés à l' usine . Dès lors , il n' y aura plus d' employés dans l' usine . -Il y a aussi des fonds pour des cliniques privées à et , des services de soins de santé améliorés , une formation spécialisée , des services de santé mentale et la mise en place de systèmes de santé électroniques personnels dans les hôpitaux locaux . Des fonds sont également prévus pour les cliniques sans rendez-vous à et , de meilleurs soins externes , la formation des cadres médicaux , des services voués à la santé mentale et le déploiement de systèmes de dossiers médicaux électroniques dans les hôpitaux locaux . -Il y a une acceptation sociale accrue de la en tant que moyen de garantir un financement garanti à court terme . La mise en gage est de mieux en mieux acceptée socialement comme moyen d' obtenir un financement garanti à court terme . -Rien n' indique que des hormones ou d' autres produits chimiques soient à blâmer Rien ne prouve que les hormones ou que d' autres substances chimiques sont responsables -Là , les pères fondateurs sont toujours honorés , mais ici , nos ancêtres révolutionnaires ne sont guère utilisés comme modèles , mais ils se sont les uns les autres . Là des pères fondateurs toujours honorés , et ici des ancêtres révolutionnaires dont on peine à faire des modèles , et qui se sont du reste . -Là , il y a cohérence institutionnelle ; il y a eu un torrent de constitutions , autant d' erreurs à corriger et des tests à répéter : Là une constance institutionnelle , et ici une cascade de constitutions , comme autant d' erreurs à rattraper et d' essais à reprendre . -Il y a beaucoup moins de façons coûteuses et moins intrusives de le faire . Il existe des moyens bien moins onéreux et des méthodes bien moins intrusives de procéder . -Il y a beaucoup de patients . Il y a beaucoup de patients . -Il y a aussi des anciens prêtres catholiques . Il y a aussi d' anciens prêtres catholiques . -Il y a actuellement 403 commandes et engagements de 15 clients et opérateurs . À l' heure actuelle , la société a enregistré un total de 403 commandes et engagements d' achat auprès de 15 clients et opérateurs . -Il existe de nombreuses théories sur les causes de la puberté précoce . Il existe de nombreuses théories portées à la connaissance du public sur les causes de la puberté précoce . -Il y a aujourd' hui 201 cardinaux . Il y a à l' heure actuelle 201 cardinaux . -Il y a de nombreux emplois dans les théâtres régionaux , les compagnies de tournée , les théâtres de Broadway , les théâtres de Broadway , etc. Les emplois sont nombreux entre les théâtres régionaux , tournées , Broadway , le ... -Il y a beaucoup de belles - la création d' images a toujours été un des points forts de Chouinard - comme les cheveux qui sont ou le tissu noir qui les lignes . Il y a quelques beaux - la création d' images a toujours été une force chez Chouinard - comme ces cheveux qui s' ou ces tissus noirs qui allongent les lignes . -Il y a trois façons de rendre la biométrie attrayante pour le grand public . Il existe trois manières de rendre la biométrie séduisante aux yeux du grand public . -Il existe des traitements pour retarder les règles précoces et pour interrompre une autre cause : le vieillissement prématuré des os qui peut en fin de compte entraîner un retard de croissance et être comme un adulte . Il existe des traitements pour retarder l' apparition des premières règles et prévenir d' autres conséquences : un vieillissement prématuré des os qui peut freiner la croissance et une petite taille à l' âge adulte . -Il y a des explications différentes . Il existe diverses explications . -Il n' y a pas beaucoup de médecins dans le pays d' Afrique de l' Ouest ; un seul pour 5 000 personnes . Il n' y a pas beaucoup de médecins dans ce pays d' Afrique occidentale , à peine 1 pour 5 000 habitants . -Il y a eu aussi un changement dans la gestion du réseau d' éclairage public et des décorations de Noël . Mais aussi une gestion différenciée du réseau de l' éclairage public et des décorations de Noël . -Il y a toujours eu une opposition interne au projet de création d' une Europe fédérale . Il y a toujours eu une opposition nationale au projet de création d' une Europe fédérale . -Des manifestations ont eu lieu dans toute l' Égypte pour exiger le rétablissement du premier président démocratiquement élu du pays . Une mobilisation dans toute l' Egypte pour réclamer le rétablissement du premier président démocratiquement élu du pays . -Il y a eu de grandes erreurs dans certains de ces programmes pilotes d' État . De grosses erreurs ont été commises dans les programmes pilotes de certains États . -Il y a une pause quand Carol Zola se couche sous le tapis de danse , le microphone , pour ne pas offrir , sous la forme d' une ou d' une réserve , d' un poème , dans la voix rythmique d' une vente aux enchères , continue de danser . Il y aura cassure quand Carol Prieur se sous le tapis de danse , pour livrer , micro en main , sans sobriété ni retenue , une part du poème de , et monotone dans la criée , et poursuivra en dansant . -Il y a une pénurie de personnel de près de 30 % , ce qui fait de l' exercice " très difficile " , selon le chef du département , Gillian . Il manquait près de 30 % des effectifs nécessaires , rendant cette année financière " très difficile " , selon la chef du département , Gillian . -Il y a aussi une " violence symbolique " qui se réfère à " l' effacement de la singularité de la personne et des symboles distinctifs " , qui est réduite à " l' anonymat " des cendres . Il y a aussi " une violence symbolique " , qui tient à l ' " effacement de la singularité et des signes distinctifs " réduits à l ' " anonymat " des cendres . -Il y a débat sur ce qui constitue le début de la puberté , mais il est considéré comme " précoce " lorsque l' élargissement du sein est accompagné d' une poussée de croissance avant l' âge de 8 ans . Il y a un débat concernant ce qui constitue l' apparition réelle de la puberté , mais elle est considérée comme « précoce » lorsque le développement de la poitrine s' accompagne d' une croissance soudaine avant l' âge de 8 ans . -Il y aura un changement dans la façon dont nous allons payer ces taxes . Il va y avoir du changement dans la façon dont nous payons ces taxes . -Il n' y a pas d' aéroport à Pitcairn , ce qui rend impossible de trouver un ou plusieurs appareils pour surveiller la zone , et il n' y a pas de port où l' on puisse installer des patrouilles de défense . Il n' y a pas d' aéroport à Pitcairn , ce qui rend impossible d' y baser un ou plusieurs avions dédiés à la surveillance de la zone , et il n' y a pas de port où placer des navires militaires de surveillance . -Il n' y a pas de formule , pas de méthode pour traverser le mur de l' invisible pour être avec vos êtres chers . Il n' existe pas de recette , de mode d' emploi pour passer le mur de l' invisible et retrouver les siens . -Il n' est pas nécessaire de mettre en place une énorme infrastructure technologique coûteuse , qui sera inévitablement agrandie pour tenir les gens au courant des allées et venues du pays . Il n' est pas nécessaire de construire une infrastructure technologique énorme et qui sera inévitablement élargie pour conserver les enregistrements des allées et venues quotidiennes des gens . -Il y a un extrémisme radical dans le monde qui est et déterminé à essayer de tuer les gens et de frapper des individus et d' attaquer les gouvernements . Il existe un extrémisme radical dans le monde qui est déterminé à tenter de tuer des gens , les faire exploser et attaquer les gouvernements . -Il y a eu un tir à l' aéroport international de Los Angeles . Une fusillade a eu lieu à l' aéroport international de Los Angeles . -Il y a eu un mécontentement général vis-à-vis du marché unique , les entreprises ont déclaré que les coûts du règlement de Bruxelles aujourd' hui les avantages de l' appartenance à la zone commerciale de l' Europe - même si 40 % des grandes entreprises , traditionnellement les plus des entreprises , étaient d' accord . Le marché unique a occasionné un mécontentement général , les entreprises disant que le coût du règlement Bruxelles est disproportionné par rapport aux avantages qu' il y a à faire partie de la zone d' échanges européenne – 40 % des grandes entreprises , traditionnellement les plus , sont même d' accord . -Il n' y a pas eu de nouvelles après cela , a déclaré la même source . On était alors sans nouvelle d' eux depuis , a précisé la même source . -Il y avait des soldats syriens là-bas . Il y a des militaires syriens . -Il n' y a pas eu de victimes , mais près de 20 appartements sur les premier et deuxième étages de ce quartier des magasins ont dû être évacués . Il n' y a pas eu de blessés , mais près de vingt logements , situés au premier et deuxième étages de cette rangée de locaux commerciaux , ont dû être évacués . -Ces s' appuieront sur nos progrès dans le cadre des objectifs du Millénaire pour le développement , et ils feront partie intégrante du cadre de développement . Ces s' appuieront sur les progrès réalisés dans le cadre des objectifs du millénaire pour le développement et feront partie intégrante du cadre de développement . -Ce sont les mêmes jeunes qui aiment la télévision de réalité et les étoiles éphémères qu' il produit ... Ce sont ces mêmes jeunes qui aiment la et ce qu' elle produit comme stars éphémères ... -Ces installations ne sont pas rentables et devraient donc être vendues à des entreprises privées ou démolies . Si ces installations ne sont pas rentables , qu' on les vende à l' entreprise privée ou qu' on les . -Ces caractéristiques peuvent se manifester dans des caractéristiques sexuelles secondaires , telles que la masse musculaire , la distribution de cheveux , les seins et la taille , les caractéristiques sexuelles primaires telles que les organes génitaux et les organes génitaux , ou les structures et les hormones hormonales . Ces caractéristiques peuvent se manifester d' elles-mêmes dans les caractères sexuels secondaires , ( comme la masse musculaire , la , la poitrine et la stature ) , les caractères sexuels primaires ( comme les organes reproducteurs et les organes sexuels ) ou la structure des chromosomes et les hormones . -Ces interceptions auraient apparemment lieu à l' extérieur des États-Unis . Ces interceptions auraient lieu en dehors des Etats-Unis . -Ces réserves , que les autorités ont largement utilisées pour soutenir la devise locale , le , sont en train de s' effondrer , ce qui conduit à une augmentation probable de la charge de la part de la Commission , ce qui la dette extérieure du pays . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Ces réserves , auxquelles les autorités ont recours massivement pour soutenir la monnaie locale , la , s' , l' agence juge de plus en plus probable une dévaluation , ce qui sa dette extérieure . -Ils ne sont pas tous des coureurs expérimentés , mais des gens qui recherchent l' excitation et l' aventure , et un chemin possible vers des événements de classe mondiale . Ils ne sont pas tous expérimentés , certains recherchent l ’ excitation et l ’ aventure et un accès réaliste à des événements internationaux . -Ils ne sont pas censés les reprendre jusqu' à ce que les plans atteignent les 10,000 mètres et que le capitaine donne le feu vert . Ils ne sont pas censés les rallumer avant que l' avion n' atteigne une altitude de 10 000 pieds et que le commandant donne son feu vert . -Ils ont dû pousser les véhicules à travers des rivières et des boues plus d' une fois . Ils ont maintes fois dû pousser les véhicules pour traverser des rivières ou des pistes . -Par la suite , 32 et plus d' autres hommes ont été incarcérés après que la police eut saisi l' héroïne et la cocaïne d' une valeur de livres et d' une valeur de plus de 200 000 livres sterling au cours d' une série de raids . Avec 32 autres hommes , ils ont été par la suite emprisonnés après que la police a saisi de l' héroïne et de la cocaïne pour un montant de 1,5 M £ , ainsi que plus de 200 000 £ en espèces au cours d' une série de raids . -Il s' agit d' une équipe formidable . Ils forment une bonne équipe . -Ils viennent de 60 kilomètres autour du village , et ils viennent à pied . Ils habitent parfois à 60 km du village , et ils viennent à pied . -Ils explorent comment , au cours de la prochaine décennie , ils peuvent se déplacer vers un système où les conducteurs paient le quart des routes qu' ils empruntent . Ils cherchent comment , au cours de la prochaine décennie , ils pourront passer à un système permettant aux conducteurs de payer en fonction du nombre de miles parcourus . -Ils sont , une partie d' une soixantaine de sujets qui sont le résultat d' un diagnostic de troubles du développement sexuel , d' un terme générique pour tous les types de , de ( ovaires ou testicules ) , ou d' organes génitaux mal développés . Ils sont , l' faisant partie du groupe de la soixantaine de maladies diagnostiquées comme désordres du développement sexuel , un terme générique désignant les personnes possédant des chromosomes ou des ( ovaires ou testicules ) atypiques ou des organes sexuels anormalement développés . -Ils affirment que les bagages coûtent de l' argent et que les passagers qui veulent le service devraient payer . Elles estiment que les coûts de manutention des bagages sont importants et que les passagers qui veulent ce service devraient le payer . -Ils m' ont posé de nombreuses questions sur le . Ils m' ont posé des questions sur les preneurs d' otages . -Ils peuvent choisir un appareil avec ou sans GPS . Ils peuvent choisir un appareil avec ou sans GPS . -Ils auraient également pu demander un mandat de perquisition immédiat , si cela était jugé nécessaire . Elle aurait également pu requérir un mandat de perquisition instantané , si jugé nécessaire . -Ils ne veulent pas que nous leur ce qui les rend rentables . Elles ne veulent pas qu' on leur dise ce qui leur permettra d' être rentables . -Ils nous ont sur un trottoir . Ils nous déposent sur le bord d' un trottoir . -Ils la joie et la folie ; c' est l' art de Chouinard qui contamine le public . La joie et la folie , et c' est l' art de Chouinard , là , de contaminer le public . -Ils sont condamnés à des peines de prison allant de 10 ans d' emprisonnement à perpétuité s' ils sont reconnus coupables . Ils encourent une peine de prison allant de 10 ans à la réclusion à perpétuité s' ils sont condamnés . -Ils ont été perdus dans le réseau , mais ils ont repris les marches en attendant d' obtenir de l' aide , a déclaré la police . Ils se sont perdus dans le réseau , sont retournés sur leurs pas en attendant les secours , a précisé la préfecture . -Ils avaient fait des recherches pour leur chien disparu et avaient envoyé des appels sur des sites de réseautage social après qu' ils eurent pénétré dans la carrière à la suite de l' accident mineur . Ils avaient cherché désespérément leur chienne perdue et lancé un appel sur les sites de réseaux sociaux après qu' elle a disparu dans la carrière suite à un accident sans gravité . -Ils ont également demandé à Barack Obama de faire comprendre à Al Thani que " l' influence pernicieuse de l' Iran au sein du gouvernement irakien constitue un sérieux problème dans la relation entre nos deux pays " . Ils ont aussi exigé de Barack Obama qu' il fasse comprendre à Nouri Al que " l' influence pernicieuse de l' Iran au sein du gouvernement irakien constitue un problème sérieux dans notre relation bilatérale " . -Ils ont eu le plaisir de le reconnaître comme un grand , une force motrice , un de leur travail , mais ils ont remarqué qu' il semblait peu disposé à produire ce travail lui-même . Ils ont volontiers reconnu en lui un grand , animateur et fédérateur de leurs travaux , mais en faisant observer qu' il se montrait peu enclin à en produire lui-même . -Ils ont signalé en particulier deux cas signalés dans La Presse Canadienne , un cas impliquant un soldat mis en liberté vendredi dernier . Ils ont notamment cité deux cas rapportés par La Presse Canadienne , dont celui d' un soldat libéré vendredi dernier . -Ils ont les meilleurs noms , comme " derrière les murs " ( qui se trouve dans une prison réelle ) , " " et " The House of " . Elles portent les meilleurs noms , comme « Terror Behind the » ( qui est en fait une vraie prison ) , « » et « The House of » . -Ils connaissent mieux leurs affaires que n' importe qui d' autre . Elles connaissent leur entreprise mieux que personne . -Ils vivent dans des habitats homogènes et ouverts , où il y a de nombreux échanges génétiques entre populations et entre individus . Ils vivent dans des habitats homogènes , ouverts , dans lesquels il y a de nombreux échanges génétiques entre les populations et les individus . -Ils recherchent le bon travail d' équipe entre le et le - et sont les meilleurs dans le ring . Ils recherchent un travail d' équipe efficace entre le et le et ont été les meilleurs sur la piste . -Ils ont mesuré le taux cardiaque chez les animaux et ont analysé leur comportement . Ils ont mesuré la fréquence cardiaque des animaux et analysé leur comportement . -Ils n' ont jamais cru qu' il était mort de la façon dont le shérif avait conclu . Ils n' ont jamais pensé qu' il était mort comme cela est indiqué dans les conclusions du shérif . -Ils ont précisé qu' ils avaient également reçu les autorisations nécessaires du Canada , de l' Inde et de la Turquie , outre celles de l' Afrique du Sud et de la Corée du Sud . Ils précisent qu' ils ont aussi reçu les autorisations nécessaires au Canada , en Inde et en Turquie , après l' Afrique du Sud et la Corée du Sud . -Ils estiment qu' il s' agit d' une dissimulation visant à protéger la personne ou les personnes responsables du décès de leur fils . Ils sont persuadés que ces conclusions ont été livrées dans le seul but de couvrir la ou les personnes responsables de la mort de leur fils . -Ils veulent voir le gouvernement décider de nouveaux liens commerciaux avec les Chinois , l' Inde et le Brésil , plutôt que de s' enliser dans le long et difficile processus de réforme des institutions de l' Union . Ils veulent voir le gouvernement donner la priorité à de nouveaux liens commerciaux avec des pays comme la Chine , l' Inde et le Brésil plutôt que de s' enliser dans le processus long et ardu de réforme des arcanes institutionnels européens . -Ils étaient armés de et de lance-roquettes . Ils étaient armés de et de lance-roquettes . -Ils des pancartes qui l' accusent et d' autres d' être des meurtriers et appelaient les États-Unis à refuser de lui venir en aide . Ils des pancartes l' accusant , entre autres , d' être un meurtrier et appelant les Etats-Unis à refuser de lui fournir de l' aide . -Ils ont été examinés , et un contrôle approfondi du cinéma n' a révélé rien . Les personnes ont été examinées et une reconnaissance complète de la salle n' a rien donné . -Ils ont été retrouvés le vendredi après-midi . Ils ont été localisés ce . -Ils ont été inclus dans la version finale du document , qui sera approuvée par les dirigeants mondiaux , dont Mme au cours du sommet . Ils figuraient dans l' avant-projet final du document , qui sera adopté par les dirigeants mondiaux , y compris Mme , au cours du sommet . -Ils n' ont pu se rendre sur les deux sites en raison de préoccupations en matière de sécurité , a déclaré les inspecteurs . Ils n' ont pas pu visiter les deux derniers sites en raison de préoccupations concernant leur sécurité , ont indiqué les inspecteurs . -Ils étaient contre tous les , mais les deux se sont avec la tasse d' or et la . Ils se sont présentés envers et contre tous , mais les deux ont la coupe en or et la . - , responsable de la mission scientifique à l' Observatoire de la Conservation et de l' Étude des et des ( ) , a discuté de cette découverte avec L' Express et décrit le long processus d' identification des nouvelles espèces . , chargé de mission scientifique à l' Observatoire pour la Conservation et l' Etude des Animaux et ( ) revient pour L' Express sur cette découverte et explique le long processus d' identification de nouvelles espèces . -Trente cinq autres hommes impliqués dans cette course ont été emprisonnés pour un total de 153 ans d' emprisonnement pour infraction à la législation sur les stupéfiants . Trente-cinq autres personnes impliquées dans le trafic ont été condamnées à un total de 153 ans de prison pour trafic de stupéfiants . -Ceci , parallèlement à la dimension artistique , a été un événement bénéfique qui aide l' organisation à but non lucratif à obtenir un bilan financier qui sonne comme apaisant pour l' oreille sous la direction de Die . Il s' agissait d' une , parallèlement à la dimension artistique , et elle a aidé l' organisme sans but lucratif à produire un bilan financier qui est aussi doux aux oreilles qu' un air de La chauve-souris . -Cet ensemble Henri avec du noir et du blanc est constitué d' une série de solos , dans un groupe en tandem ( une messe en solo ) , un grand groupe de douze solos , où chaque individu donne la représentation de son propre signe . Ce jeu noir et blanc de Henri se fait en relais de solos , en groupe à l' unisson ( une masse qui fait solo ) , en grand groupe de douze solos où chacun exécute son signe . -Ce jeudi , la police a arrêté l' ami de Rob Ford et son chauffeur , Alexander , âgé de 35 ans , et l' a accusé d' extorsion en rapport avec la vidéo . Ce jeudi , la police a arrêté l' ami et chauffeur occasionnel de Rob Ford , Alexander , âgé de 35 ans , et l' a accusé d' extorsion relativement à la vidéo . -Ce jeudi , il a conduit un certain nombre de journalistes de sa propriété lorsqu' ils sont venus pour l' interviewer . Ce jeudi , il a chassé de sa propriété plusieurs journalistes venus l' interroger . -Ce mélange de gènes limite la création de nouvelles espèces . Ce brassage de gènes limite la création de nouvelles espèces . -Ce livre devrait définitivement le traduire en justice . Le livre que voici devrait lui rendre définitivement justice . -Cela complique le remboursement du crédit à l' étranger . Cela complique le remboursement des crédits pris à l' étranger . -Cela concerne les " opérations militaires " dans les pays où les alliés de l' OTAN coopèrent avec les États-Unis et qui , en fin de compte , n' ont pas visé l' Europe , selon le Général Alexander . Cela concernerait des " opérations militaires " dans des pays où ces alliés de l' Otan coopèrent avec les Etats-Unis et cela ne viserait absolument pas l' Europe , a affirmé le général Alexander . -Ce jour d' Halloween , Pia a eu un jour trouble marqué par l' apparition d' une émeute impliquant un certain nombre de personnes autour du bureau de poste . En ce jour d' Halloween , la soirée a été agitée du côté de Pia où une impliquant plusieurs personnes a éclaté du côté de la Poste . -Cette décision fait partie des tentatives de la Maison Blanche de reprendre le contrôle de l' affaire NSA à la suite des révélations de l' ancien analyste , Edward , qui s' est réfugié en Russie . Cette décision fait partie de la tentative de la Maison blanche de reprendre la main dans l' affaire des écoutes de la NSA après les révélations faites par l' ancien analyste Edward , réfugié en Russie . -Ce détour dans le chemin de ma vie est toujours la chose la plus inattendue et la plus belle . Ce détour du chemin de ma vie reste le plus inattendu et le plus beau . -Cette constatation , qui a été reflétée dans les tailles et les grands groupes d' entreprises , montre que les entreprises font pression pour un " changement significatif " qui confère des pouvoirs au Royaume-Uni . Cette conclusion , qui se retrouve dans tous les grands groupes et les entreprises de toutes tailles , montre que le monde de l' entreprise insiste pour un « changement significatif » qui redonne le pouvoir au Royaume-Uni . -Cela inclut l' Australie et le Royaume-Uni , qui sont généralement nos plus gros générateurs de profit . Cela concerne l' Australie et le Royaume-Uni , qui sont généralement les pays qui génèrent les plus gros profits . -Il s' agissait de trois véhicules de police , de deux ambulances et d' un groupe d' une trentaine de personnes , selon les rapports . Trois véhicules de gendarmerie , deux ambulances et un d' une trentaine de personnes selon les témoignages recueillis ... -Il s' agit d' une baisse par rapport au bénéfice net de millions de CAD , ou de 83 ¢ par action , enregistré au cours de la même période l' an dernier . Il s' agit d' un recul comparativement au bénéfice net de millions , ou 83 ¢ par action , de la même période l' an dernier . -C' est une soirée qui met en vedette tout le spectre de Chouinard , de l' art à l' et de la noirceur de l' encre à la blancheur . Une soirée qui permet de voir tout le prisme de Chouinard , de l' art aux et du noir d' encre au blanc . -C' est parce que l' Amérique et la France ont toutes deux une révolution , ont établi une déclaration de droits et ont essayé de trouver une nouvelle société . Car l' Amérique et la France ont l' une et l' autre fait une révolution , rédigé une déclaration des droits , voulu fonder une société neuve . -C' est le cas au Brésil et en Allemagne , deux pays où les relations diplomatiques avec les États-Unis sont tendues . C' est le cas du Brésil et de l' Allemagne , deux pays où les relations diplomatiques avec les Etats-Unis se sont tendues . -C' est le cas des nouvelles espèces qui viennent d' être identifiées . C' est le cas de la nouvelle espèce qui vient d' être identifiée . -Il s' agit de la première série de directives que le gouvernement américain a mises en place , car le nombre d' enfants d' âge scolaire atteints d' allergies alimentaires s' accroît . Il s' agit de la première série de lignes directrices que le gouvernement américain publie , puisque le nombre d' enfants en âge scolaire souffrant d' allergies alimentaires a fortement grimpé . -C' est la première fois qu' un haut fonctionnaire chinois a nommé une organisation spécifique à la suite de l' attaque lundi . C' est la première fois qu' un responsable chinois désigne une organisation particulière après l' attentat de lundi . -C' est la première fois que la surveillance du FMI et de la Banque mondiale par l' agence de renseignement est mentionnée depuis le début du scandale . C' est la première fois qu' est mentionnée la surveillance du FMI et de la Banque mondiale par l' agence de renseignement depuis le début du scandale . -Ceci devrait inclure la possibilité de temps de voyage possible de Glasgow et d' Édimbourg à Londres de trois heures ou moins . Cela doit inclure la possibilité de durées de voyage entre Glasgow et Édimbourg de trois heures au plus . -C' est ce qui se passe au du Père depuis 1998 . C' est le cas au du Père , depuis 1998 . -C' est ce qui a inspiré un exercice de consultation entre les parents , les étudiants , les enseignants et le personnel du département de l' éducation il y a un an pour réfléchir sur les moyens d' améliorer l' intégration des élèves qui ont des racines dans plusieurs cultures . C' est ce qui a motivé , il y a un an , une consultation auprès des parents , élèves , professeurs et membres du personnel de l' éducation , pour réfléchir aux moyens de mieux intégrer les élèves qui vivent à cheval entre plusieurs cultures . -Ce jugement signifie qu' il sera possible d' appliquer une loi qui a été adoptée dans cet État en juillet et qui soulève en grande partie la question du droit à l' avortement . Ce jugement signifie qu' un texte de loi adopté en juillet dans cet État et remettant largement en cause le droit à l' avortement va pouvoir entrer en vigueur . -Ce type d' expérience fait partie des efforts de Disney pour " étendre la vie de ses séries et créer de nouvelles relations avec les auditoires par le biais de plateformes numériques qui deviennent de plus en plus importantes " , ajoute-t-il . Ce type d' expérience entre dans le cadre des efforts de Disney pour " étendre la durée de vie de ses séries et construire de nouvelles relations avec son public grâce à des plateformes numériques qui sont de plus en plus importantes " , a-t-il ajouté . -Ce dernier raid effectué par les forces aériennes israéliennes sur le territoire syrien ( le sixième depuis le début de l' année , selon le quotidien israélien Ha' aretz ) n' a été confirmé ni par Israël ni par la Syrie . Ce nouveau raid de l' aviation de en territoire syrien ( le sixième depuis le début de l' année , selon le quotidien israélien ) n' a été confirmé ni par Israël ni par la Syrie . -Cette organisation , qui dépend d' un réseau de militants et de sources médicales , n' a pas été en mesure d' identifier l' origine des explosions . Cette organisation s' appuyant sur un réseau de militants et sources médicales n' avait pas été en mesure d' identifier l' origine des explosions . -Ce drame pornographique dans huit chapitres qui sera publié en deux parties ( le 1er janvier et le 8 ) traite des souvenirs érotiques d' un coincé sur le sexe depuis son adolescence . Ce drame pornographique , en huit chapitres qui sortiront en deux volets ( les 1er et 8 janvier 2014 ) , les souvenirs érotiques d' une accro au sexe depuis son adolescence . -Ce principe de comparaison peut être utilisé à d' autres fins . Ce principe de comparaison peut avoir d' autres applications . -Ce projet est un élément clé de la sécurité énergétique de l' ensemble du continent européen . Ce projet est un élément clé de la sécurité énergétique de l' ensemble du continent européen . -Cette notation est accompagnée d' une perspective négative , avec S & P au moins une chance dans trois d' entre eux de ramener la notation à nouveau dans l' année à venir . Elle est assortie d' une perspective négative , S & P voyant au moins une chance sur trois qu' elle soit de nouveau abaissée d' ici un an . -Cette , intitulée The Next Half Day , a été présentée sous la forme de trois disques : l' album original , les séances de studio non publiées et les , ainsi qu' un DVD contenant les quatre clips qui ont été dévoilés . Cette réédition , intitulée The Next Day Extra , se présentera sous la forme de trois disques : l' album original , des sessions studio inédites et des , ainsi qu' un DVD contenant les quatre clips déjà dévoilés . -C' est vraiment une nécessité pour notre nation . Cela est vraiment indispensable pour notre nation . -Cette révélation vient à 68 ans après la chute du régime nazi d' Adolf Hitler et un des grands mystères de l' après-guerre . Cette révélation , 68 ans après la fin du régime nazi d' Adolf Hitler , répondrait à l' une des grandes énigmes de l' . -Cependant , cette situation n' a pas créé de car l' accident a eu lieu à la sortie de . Cette situation n' a toutefois guère provoqué d' embouteillages car l' accident a eu lieu à hauteur de la sortie . - ( si c' est possible ) le bruit de l' énergie et de l' énergie finit par être assourdissant et , Cette décharge sonore et énergique , longue , si cela se peut , finit par et . -Cette fois , nous sommes prévenus qu' une Écosse indépendante serait obligée d' adhérer à la zone à l' échelle européenne comme condition de l' adhésion à l' UE . Cette fois-ci , on nous prévient qu' une Écosse indépendante serait tenue de rejoindre la zone européenne de libre circulation pour pouvoir adhérer à l' UE . -Ce négociateur , Richard , a quitté en 2010 et est entendu qu' il sera laissé de côté de sa position actuelle en tant que leader européen du commerce de billets d' argent à . Ce trader , Richard , a quitté en 2010 et aurait été mis suspendu de son poste de responsable européen du trading au comptant pour les devises chez . -Il s' agissait d' un appel très inhabituel . C' était un appel très inhabituel . -Il s' agissait d' une demande officielle de " ne pas faire de risque à l' égard des services d' urgence à Paris au début de l' hiver " , mais aussi d' éviter d' entraver la campagne du parti socialiste quelques mois avant les élections locales . Une demande officiellement faite " pour ne courir aucun risque sur la prise en charge des urgences à Paris au début de l' hiver " mais qui visait aussi à ne pas gêner la campagne du PS à quelques mois des municipales . -Il s' agissait de sa promesse sur son lit de mort ; alors , de temps en temps , nous avons appris à nouveau à sourire , et dire que son nom n' est plus tabou , mais un réconfort . C' était sa promesse sur son lit de douleur , alors on a petit à petit à sourire , et prononcer son nom n' est plus tabou mais un réconfort . -Cela a été rendu public le lendemain de la publication de statistiques officielles montrant que l' économie du pays avait connu sa cinquième baisse trimestrielle entre juillet et septembre . Elle a été rendue publique au lendemain de la publication de statistiques officielles montrant que l' économie du pays avait subi entre juillet et septembre son cinquième trimestre de suite de contraction . -Il s' agissait de la première acquisition majeure de l' entreprise après l' échec de sa tentative de au Portugal . Il s' agissait de la première acquisition majeure de l' entreprise après celle qui avait échoué au Portugal . -Il s' agissait de la dernière de trois toujours en service dans le département du Finistère , les deux autres ayant été ou . Il s' agit du dernier des trois portiques en état de fonctionner dans le département du Finistère , les deux autres ayant été démontés ou . -De cette façon , vous êtes OK - élever l' enfant , aimer l' enfant . Ainsi , tout va bien – vous pouvez élever votre enfant et l' aimer . -Cette semaine , j' ai lu la partie du livre expliquant comment Obama a subi une perte considérable à Clinton dans la Pennsylvanie primaire . Cette semaine , j' ai lu la partie du livre qui décrit comment Obama a subi d' énormes pertes au profit de Clinton aux de Pennsylvanie . -Cette semaine Standard & Poor , l' agence de notation , a cité l' augmentation de l' influence des ménages , principalement de la hausse des hypothèques , en tant que facteur de risque pour la solvabilité des banques asiatiques . Cette semaine , l' agence de notation Standard & Poor' s a cité l' endettement croissant des ménages , découlant principalement de la hausse des prêts hypothécaires , comme un facteur de risque pour la solvabilité des banques asiatiques . -Cela garantira que quelqu' un n' essaie pas d' usurper l' identité d' une autre personne . Cela permet de s' assurer qu' une personne ne tente pas d' usurper l' identité de quelqu' un d' autre . -Cela serait historique - c' est la première fois que le gouvernement de Kinshasa serait en mesure d' annuler une importante insurrection . Ce serait historique - ce serait la première fois que le gouvernement de Kinshasa réussit à battre une insurrection majeure . -Cette année , l' événement se déroule sous les noms de , Kathy Griffin et Bill Maher , ainsi que de " for Heroes " , un grand prix pour les vétérans militaires de la région , au Madison Square Garden , qui comprend , entre autres , Bruce , Jon Stewart , Francisco Lois , et Bill . L' évènement de cette année verra la participation de , Kathy Griffin et Bill Maher et la présentation de « Stand Up for Heroes » , une soirée de comédie et de musique au profit des anciens combattants , organisée au Madison Square Garden , à laquelle seront présents , entre autres , Bruce , Jon Stewart , Roger Waters et Bill . -Cette année , les Américains environ 106 millions de dollars sur les , selon le Census Bureau américain . Cette année , les Américains environ 106 M $ en , selon l' U.S. Census Bureau . -Cette année , les objets sexy sont tous de la rage . Cette année , les objets sexy font fureur . -Thomas , député responsable de la commission parlementaire chargée de surveiller le renseignement , a déclaré que s' il était possible d' entendre M. comme un témoin " sans le craindre et risquer complètement de déstabiliser les relations avec les États-Unis " , il faut le faire . Thomas , le député qui dirige le groupe parlementaire supervisant les services secrets étrangers , a indiqué que s' il y avait une chance d' entendre M. à titre de témoin « sans le mettre en danger et complètement ruiner les relations avec les États-Unis » , il fallait en profiter . -Ces conducteurs paieront bientôt les frais de kilométrage plutôt que les taxes sur les gaz à l' État . Ces automobilistes paieront bientôt à l' État des frais au lieu des taxes sur l' essence . -Bien qu' elle soit stérile , elle aspire à devenir mère en adoptant ou en adoptant une maternité . Bien qu' elle soit stérile , elle espère avoir des enfants par l' entremise de services d' adoption ou de mères porteuses . - les agents de voyages était une chose du passé grâce à l' internet ? Vous pensiez que les agences de voyage appartenaient au passé à cause d' Internet ? -Des milliers d' automobilistes ont déjà pris les boîtes noires , dont certaines ont une surveillance GPS , pour un lecteur d' essai . Des milliers d' automobilistes ont déjà embarqué ces boîtes noires , parfois équipées d' un système de surveillance par GPS , pour une virée expérimentale . -Trois hommes qui ont dit qu' ils travaillaient parce que les conducteurs ont été accusés de posséder de la marijuana et de la cocaïne avec l' intention de les distribuer . Trois hommes qui , selon les autorités , travaillaient comme chauffeurs , ont été accusés de possession de marijuana et de cocaïne avec intention de vente . -Trois pharmaciens sont déjà retournés au travail après un congé de maternité et trois autres ont été engagés au cours des derniers mois . Déjà , trois sont de retour au travail après leur congé de maternité et trois autres ont été embauchées au cours des derniers mois . -Au cours de l' exercice 2013 , le bénéfice net de Oracle a progressé de 3,5 % , tandis que son chiffre d' affaires a augmenté de 27,5 % , surpassant l' indice S & qui a augmenté de 24 % au cours de la même période . Sur l' ensemble de l' exercice décalé 2013 , le bénéfice net d' Oracle a progressé de 3,5 % alors que le cours de Bourse à bondi de 27,5 % , l' indice S & qui a pris 24 % dans le même temps . -Ainsi , j' ai appris et j' ai compris que je n' avais pas perdu ma mère seule , une femme que je ne connaissais pas très bien , une femme qui l' a incarné pendant son séjour sur cette terre . J' ai donc appris et compris que je n' avais aucunement perdu la personne de ma mère mais simplement une femme que je ne connaissais pas vraiment , une femme qui incarnait cette personne , durant son séjour dans la vie . -Si vous aimez le travail , vous devez être à la hauteur ... La chance de faire le tour de votre carrière ... et de plus en plus ... " , n' hésitez pas à nous le faire savoir dès aujourd' hui ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! Pour Justin Timberlake , si le travail est nécessaire pour " être considéré comme bon " , " le hasard peut aussi lancer la carrière de quelqu' un " , et " de plus en plus aujourd' hui " . -Le temps est le test le plus précis de la façon dont la puberté va progresser . Le temps est le test le plus précis pour savoir comment va évoluer la puberté . -Titan n' a pas besoin d' acheter cette usine . Titan n' a pas besoin d' acheter cette usine . -Titan a accepté de les recruter parmi les 1200 personnes qui travaillent actuellement pour Goodyear . Titan accepte de les recruter parmi les quelque 1 200 salariés actuels de Goodyear . -Pour être précis , il n' est pas autorisé à communiquer avec des personnes de moins de 18 ans ou à accéder à Internet . Il ne doit notamment pas communiquer avec des personnes de moins de 18 ans et ne pas accéder à Internet . -Pour en savoir plus sur la façon dont les chiens réagissent à la queue d' autres chiens , les chercheurs ont surveillé les animaux pendant qu' ils regardaient des films d' autres chiens . Pour en savoir plus sur la façon dont les chiens réagissent aux mouvements de queue d' autres chiens , les chercheurs ont suivi des animaux en train de regarder des films où figuraient d' autres chiens . -Avoir 20 ans , après avoir tué 202 personnes et blessé plusieurs centaines de personnes , ce n' est pas grand chose . Passer 20 ans en prison après avoir tué 202 personnes et blessé des centaines d' autres , ce n' est pas beaucoup . -Pour le maintenir en vie , il n' y a pas de raison de le garder vivant . Le garder en vie , pourquoi , il n' y a aucune raison de le garder en vie . -Pour que les gens , pour réduire la douleur , c' est pour ça que je fais ça . Faire rire les gens et soulager la douleur sont les raisons qui me poussent à faire cela . -À cette fin , nous avons commandé le plus grand et le plus complet des chefs d' entreprise britanniques qui leur demandaient des idées sur la Grande-Bretagne , les entreprises et l' UE . Pour ce faire , nous avons commandé l' enquête la plus large et la plus complète auprès des chefs d' entreprise britanniques et leur avons demandé ce qu' ils pensaient de la Grande-Bretagne , des entreprises et de l' UE . -Pour comprendre cet aspect unique de l' identité française , il n' y a rien de mieux qu' un court voyage en dehors de la métropole , si bien que la part de l' entreprise de Paul Montana pourrait être le coeur de son livre . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Pour faire sentir cette singularité de l' identité française , rien ne vaut les quatre pas hors de l' , si bien que l' escapade américaine de Pierre Nora pourrait bien être le coeur de son livre . -Les appareils électroniques d' aujourd' hui émettent en général de très faibles charges de transmission radio que les générations précédentes d' appareils . Les appareils électroniques d' aujourd' hui émettent en général des transmissions radio de puissance bien plus faible que les générations précédentes d' appareils . -Aujourd ' hui , on s' attend à ce que tombe aussi . Aujourd' hui , devrait tomber d' elle-même . -Aujourd ' hui , il défend les autres dans une organisation appelée le Projet d' interface , essayant de normaliser la perception de ceux qui sont . Aujourd' hui , il défend la cause des autres dans une organisation appelée Interface Project , qui essaie de normaliser la perception des personnes . -Aujourd' hui , une fois de plus , tout le monde est emballé dans la compassion , l' émotion et se réjouit que les otages soient libres . Aujourd' hui , une fois de plus , tout le monde est dans la compassion , dans l' émotion , et se réjouit de la libération de ces otages . -Aujourd' hui , certaines de ces classifications ont été remises en question à la suite des progrès de la génétique . Aujourd' hui , certains de ces classements sont remis en cause en raison des progrès de la génétique . -Aujourd' hui , ces deux scènes des performances telles que des hits . Aujourd' hui , ces deux scènes les titres comme des tubes à succès . -Aujourd' hui à Liverpool Crown Court , qui travaille en tant que membre du conseil d' administration de , siégeant au conseil de Blackburn , a été victime de honte , après avoir été jugé coupable de deux manquements à l' emprise de la justice , à la suite d' un procès de trois semaines à la Crown Court de Liverpool . Aujourd' hui , à la cour d' assises de Liverpool , , qui travaille dans le cabinet d' avocats Forbes situé à Blackburn , est tombé en disgrâce après avoir été reconnu coupable sur deux chefs d' accusation d' entrave à la justice après un procès de trois semaines . -Les filles d' Adam , Frances et Bridget . Les filles de Tony Abbott , Frances et Bridget . -Tony Blair a déclaré qu' il pourrait saisir la possibilité de revenir en tant que Premier ministre britannique , mais il est peu probable qu' un retour soit perçu . Tony Blair a déclaré qu' il l' occasion de redevenir Premier ministre , mais reconnaît que son retour est peu probable . -Le maire de la ville de Toronto Off Journalists qui cherche à s' entretenir avec lui de l' affaire Drogue Le maire de Toronto chasse des journalistes venus l' interroger sur une affaire de drogue -Toyota et Nissan ont également déclaré des ventes inférieures aux attentes , malgré des hausses de 8,8 % et de 14,2 % respectivement sur une année . Toyota et Nissan affichent également des ventes inférieures au consensus , malgré des hausses sur un an de 8,8 % et 14,2 % respectivement . -Le trafic est retourné à la normale à 6 heures du matin . La circulation était revenue à la normale vers 6h , vendredi . -Le trafic a donc été détourné par la sortie / l' entrée du périphérique . La circulation a dès lors pu être par cette sortie / entrée du ring . -La ministre des Transports , Mme la députée , a déclaré que le projet " le Royaume-Uni " . La ministre des Transports baronne Kramer a déclaré que le projet « rassemblerait le Royaume-Uni » . -Les passagers et le personnel se sont précipités vers les portes ou vers l' aire de trafic . Les voyageurs et le personnel se sont vers les sorties ou sur le . -Faire du trekking dans la boue , les rivières et la jungle pour assurer des soins médicaux gratuits Il marche dans la boue , la jungle et traverse des rivières pour offrir une assistance médicale gratuite -Il est vrai que le document a souligné les avantages économiques potentiels d' une plus grande liberté de circulation avec le reste de l' Europe , bien que prévisible - ce qui n' a pas figuré dans les manchettes . S' il est vrai que le journal de a indiqué les éventuelles retombées économiques d' une plus grande liberté de circulation avec le reste de l' Europe , cela ne figurait pourtant pas dans les gros titres ( et c' était à prévoir ) . -Vingt secouristes , dix membres civils de l' organisation française , ainsi que des membres de la police , des services de secours en montagne et des pompiers étaient présents . Une vingtaine de secouristes , dix civils membres du secours français , ainsi que des gendarmes , CRS et pompiers étaient sur place . -Deux âgés de 23 et 27 ans ont été portés disparus dans une grotte à de jeudi soir , selon un rapport de l' Agence de sauvegarde des Alpes de l' le vendredi . Deux de 23 et 27 ans étaient portés disparus dans un gouffre sous la de depuis jeudi soir , a-t-on appris vendredi auprès du secours de l' Isère . -Deux employés du YMCA de Nouvelle-Galles du Sud ont été accusés d' infractions à l' égard des enfants avant qu' il n' ait été porté à la connaissance de l' agent de la protection de la jeunesse , le fils de Victoria , en 1972 , l' affaire de la violence sexuelle que la Commission royale d' enquête a entendue . Deux employés du YMCA de Nouvelle-Galles du Sud ont été accusés d' agressions sexuelles contre des enfants suite à des allégations formulées à l' encontre de l' éducateur de , Jonathan Lord , en 2011 , lors d' une audience de la Commission royale chargée des abus sexuels sur les enfants . -Deux employés du Ministère de la condition de la femme ayant commis des délits contre le racisme avant l' âge de 13 ans de la part de la Reine , la commission royale s' interroge : Deux employés du YMCA accusés d' agressions sexuelles suite à des allégations formulées à l' encontre de Jonathan Lord , lors d' une audience de la Commission royale . -Deux jours avant le début du procès du président déchu , Mohamed Morsi , ils se sont introduits dans la rue . Deux jours avant l' ouverture du procès du président déchu Mohamed Morsi , ils sont descendus dans les rues . -Deux ont le suspect et l' ont aperçu à la sortie de la maison de et ont vu les drogues qu' il détenait d' une pureté de 60 % sous les vases d' avocat dans un gant de golf noir . Deux enquêteurs ont suivi le suspect et l' ont repéré arrivant au domicile de et plaçant de la drogue , pure à 60 % , sous les poubelles de l' avocat dans un gant de golf noir . -Deux mois après l' élection , la commission électorale a officiellement déclaré M. Palmer , le gagnant du siège de la Sunshine Coast de , par 53 voix , après un . Deux mois après son élection , la commission électorale a officiellement déclaré que M. Palmer a remporté le siège de Sunshine Coast de par 53 voix , après . -Deux autres concerts auront lieu au cours de l' été 2014 , ce qui donnera aux artistes de la région l' occasion de montrer ce qu' ils sont . Deux autres concerts auront lieu à l' été 2014 , ce qui donne la chance à des artistes de la région de montrer de quoi ils sont capables . -Deux autres agents locaux des Brigades , Mohammed et Mohammed , ont été tués lorsqu' un hélicoptère israélien a ouvert le feu dans la même zone . Deux autres responsables locaux des Brigades , Mohammed et Mohammed , ont trouvé la mort lorsqu' un hélicoptère israélien a ouvert le feu dans la même région . -Deux personnes sur cinq dans le pays vivent en dessous du seuil de pauvreté , et près des trois quarts des dépenses de santé du pays sont privées . Deux personnes sur cinq dans le pays vivent en dessous du seuil de pauvreté , et presque trois quarts des dépenses de santé du pays sont réalisées dans le secteur privé . -Deux personnes ont été tuées et un autre grièvement a été tué le lendemain par des coups de feu tirés par deux personnes sur une moto qui avait ouvert un bureau à l' aide d' un tireur d' élite , Golden Dawn , dans la banlieue ouest d' Athènes , une source de police a déclaré . Deux personnes ont été tuées et une autre a été grièvement blessée vendredi soir par des coups de feu tirés par deux personnes qui se trouvaient à bord d' une moto qui passait devant un local du parti Aube dorée dans la banlieue ouest d' Athènes , a-t-on appris de source policière . -Deux véhicules entrent en collision sur la route 131 dans la région de Lanaudière , laissant quatre blessés le vendredi matin . Une collision entre deux véhicules a fait quatre blessés , tôt vendredi , sur la route 131 , dans Lanaudière . -En règle générale , applique à Singapour un taux annuel effectif de 17 % , soit juste au-dessus du niveau de 15,4 % offert à la United Overseas Bank , un prêteur local disposant d' une succursale dans le même centre commercial . En général , les prêteurs sur gage de Singapour prélèvent un taux d' intérêt annuel effectif de 17 % , juste au-dessus des 15,4 % offerts par l' United Overseas Bank , un bailleur de fonds local ayant une succursale dans le même centre commercial . -Le procureur américain Michael Moore a déclaré jeudi qu' il menait une enquête officielle sur la mort de Johnson , notant que plusieurs questions clés demeurent sans réponse . Le procureur des États-Unis Michael Moore a déclaré jeudi qu' il menait une enquête officielle sur le décès de Johnson , soulignant que plusieurs questions importantes restaient sans réponse . -UBS a déclaré cette semaine qu' elle avait agi à l' encontre de certains de ses employés à la suite de l' accord de l' agence suisse de surveillance des marchés , , qui avait fait état d' une enquête sur la manipulation du marché des changes dans plusieurs banques suisses . UBS a expliqué cette semaine qu' elle avait intenté une action à l' encontre de certains de ses employés après que le régulateur suisse , , a déclaré qu' il enquêtait sur des suspicions de manipulations du marché des changes dans un certain nombre de banques suisses . -Le secrétaire général des Nations unies , M. Ban Ki-moon , a déclaré lors du sommet de cette nuit que le moment était venu de " dépasser les intérêts nationaux " . Le secrétaire général de l' ONU Ban Ki-moon a dit à la veille du sommet qu' il était maintenant temps de « dépasser les intérêts nationaux » . -Les Nations Unies saluent de nouveaux objectifs en matière de lutte contre la pauvreté . L' ONU salue les nouveaux objectifs en faveur de la lutte contre la pauvreté -Les chercheurs américains , français et japonais prédisent un avenir où nous serons reconnus par des capteurs biométriques sur les téléphones et les ordinateurs . Les chercheurs américains , français ou japonais nous prédisent un avenir où nous serons reconnus par les capteurs biométriques des téléphones et des ordinateurs . -Fusion par les États-Unis Feu vert des Etats-Unis à la fusion -US Airways a tenté une fois de plus de facturer des boissons en 2008 , mais a finalement refusé sept mois plus tard après que les passagers ont été informés et qu' aucun autre transporteur principal n' a suivi . US Airways a brièvement tenté de faire payer pour les boissons en 2008 mais est revenue en arrière sept mois plus tard après que les passagers se sont plaints et alors qu' aucune autre grande compagnie aérienne n' avait suivi . -Le président américain Barack Obama a célébré la tradition de l' Halloween . Aux Etats-Unis , le président Obama n' a pas dérogé à la tradition d' Halloween . -Les autorités américaines ont décrit les quatre pieds de trois comme l' un des passages les plus secrets jamais découverts . Les autorités américaines ont décrit le tunnel de 4 pieds par 3 comme l' un des passages secrets les plus sophistiqués qu' elles avaient jamais vu . -L \ de l \ de l \ L' Ukraine s' approche de la faillite -Ulrich , membre du conseil d' administration de Audi , déclare qu' il est pratique courante depuis des années d' envoyer des téléphones mobiles avant les réunions du conseil d' administration afin de ne pas être utilisés comme appareils d' écoute à distance . Ulrich , membre du conseil d' administration du constructeur automobile Audi , déclare que la collecte des téléphones portables avant les réunions du conseil , afin qu' ils ne puissent pas être utilisés comme appareils d' écoute à distance , est une pratique courante depuis des années . -En fin de compte , l' enfant décidera de quel sexe il se sent plus à l' aise avec lui - et c' est une chose merveilleuse . Enfin , l' enfant décidera avec quel sexe il / elle se sent le / la plus à l' aise – et c' est quelque chose de formidable . -Dans le cadre de l' alliance Renault-Nissan , Renault détient 43,4 % de la motorisation du Japon et le constructeur japonais , 15 % de la société française , selon des données sur le site de Nissan . Dans le cadre de l' alliance Renault-Nissan , Renault détient 43,4 % du capital de Nissan et le constructeur japonais 15 % du français , selon les données du site internet de Nissan . -Selon les règles actuelles , les soldats gravement blessés ont jusqu' à trois ans pour se rétablir . En vertu des règles actuelles , les soldats grièvement blessés ont jusqu' à trois ans pour se rétablir . -En vertu du nouveau projet de loi , les médias peuvent être condamnés à une amende pouvant aller jusqu' à 20 millions de shillings kényans et à des journalistes d' au moins un million , le risque étant de " " ou de ne pas être autorisés à recevoir la presse écrite . En vertu du nouveau projet de loi , les médias pourront être passibles d' amendes allant jusqu' à 20 millions de shillings kenyans et les journalistes jusqu' à 1 million avec le risque supplémentaire d' être « radié » ou de ne plus pouvoir recevoir d' accréditation presse officielle . -Selon les nouvelles lignes directrices , les compagnies aériennes dont les aéronefs sont parfaitement protégés contre les intrusions électroniques peuvent permettre aux passagers d' utiliser les appareils au cours des , atterrissages et , selon la FAA . En vertu des nouvelles directives , les compagnies aériennes dont les avions sont correctement protégés des interférences électroniques pourront autoriser les passagers à utiliser leurs appareils pendant les , les atterrissages et le roulement au sol , a déclaré la FAA . -Les experts de l' ONU accusent le Rwanda d' avoir fourni un appui militaire à la , qui s' est d' abord constituée de anciens soldats congolais , ce que le Rwanda a nié catégoriquement . Des experts des Nations unies accusent le Rwanda , qui le nie farouchement , de soutenir militairement le , constitué au départ d' anciens militaires congolais . -États-Unis pour l' Halloween Les Etats-Unis aux couleurs d' Halloween - Open Fire on Hotel at Cairo Des inconnus tirent sur un hôtel près des pyramides du Caire -Des inconnus ont ouvert le feu sur un hôtel près des pyramides du Caire , en Égypte , le vendredi , ce qui n' a pas été le cas dans l' incident , ce qui s' explique par un argument impliquant des travailleurs qui ont été licenciés . & # 160 ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Des inconnus ont ouvert le feu ce vendredi sur un hôtel proche des pyramides du Caire en Égypte sans faire de victimes , conséquence d' une apparente querelle avec des salariés licenciés . -Jusqu' à tout récemment , il n' était pas vraiment désolé de la douleur qu' il avait causée à d' autres personnes . Jusqu' à récemment , il se du chagrin qu' il avait pu causer à d' autres personnes . -Jusqu' à présent , l' identification a consisté à émettre des documents d' identité : les systèmes biométriques ( empreintes digitales , photos , iris , etc. ) doivent avoir accès à l' information que l' État détient sur chaque citoyen déjà enregistré . L' identification concernait jusqu' à présent la délivrance de papiers d' identité : il faut confronter les éléments biométriques ( empreintes , photo , iris ) avec les informations que l' Etat possède sur tous les citoyens déjà . -Jusqu' à récemment , Airbus mettait l' accent sur la nécessité d' améliorer la cabine en proposant des sièges plus larges sur certains de ses jets . Jusqu' à récemment , Airbus soulignait la nécessité d' une plus grande personnalisation des cabines en offrant des sièges plus larges côté allée sur certains de ses avions . - a dit que les papes ont généralement élevé les évêques de grands lieux à la position de cardinal , mais que François est " plein de surprises - bien sûr , il ne sait pas qui le nommer " . a indiqué que les papes élevaient en général des évêques représentant de vastes communautés au rang de cardinal , mais que François était « plein de surprises – donc nous ne savons pas qui il nommera » . -Enquête chrétienne sur la contraception , le divorce et l' homosexualité Le Vatican sonde sur la contraception , le divorce et l' homosexualité -Nouveau casque spécial utilisé à Abu Dhabi Vettel un casque spécial à Abou Dhabi - a choisi l' entrée gagnante à partir de 1,500 exemplaires provenant de tous les coins du monde dans le cadre d' un concours organisé par l' un des sponsors , une voiture de marque dans son écurie . Vettel a choisi parmi 1.500 dessins envoyés du monde entier , dans le cadre d' un concours lancé par l' un des sponsors , une marque automobile , de son écurie . -On s' attendait à ce que les pneus portent le casque lors des séances d' essais du vendredi et du samedi . Vettel devait porter ce casque vendredi et samedi , aux essais . -Virgin , qui s' est déjà adressé à la CASA à propos de l' extension de son système de divertissement en ligne sans fil , a pu s' adapter à un changement , mais il a dit qu' il devrait prendre le relais de l' autorité de régulation . Virgin , qui a déjà parlé à la CASA de son intention d' étendre l' utilisation de son système de divertissement à bord utilisant la technologie Wi-Fi , était ouverte à un changement , mais a déclaré qu' elle suivrait le régulateur . -Wallis Simpson a peut-être été . Wallis Simpson aurait pu être . -Est-ce que Bowie essaie de faire référence à John Rocket de l' homme , ou même à la gravité , dans son propre ? Sur Like A Rocket Man , Bowie a-t-il voulu faire une petite référence au Rocket Man d' John , voire même à ? - été condamné à une peine de prison de trois ans à la Crown Court de Liverpool Il a été condamné à une peine d' emprisonnement de trois ans par la cour d' assises de Liverpool -Est-ce que son décès est le résultat d' un crime ? Son décès est-il le résultat d' un acte criminel ? -Est-ce une mauvaise blague impliquant des gaz lacrymogènes ? Mauvaise plaisanterie sur fond de jet lacrymogène ? -Moyens de rassurer les utilisateurs Des pistes pour rassurer les utilisateurs -Nous sommes heureux de pouvoir vous offrir ce mode de partenariat en partenariat avec Land Rover et le CPA , et la preuve est que le nouveau style est un moyen de découvrir d' autres disciplines de rallye au Royaume-Uni et à l' étranger , et de préparer les participants pour les rigueurs et les réalités du rallye de Raid . Nous sommes vraiment heureux d ’ offrir une telle approche en partenariat avec Land Rover et la MSA , et nous sommes convaincus que ce sera une nouvelle façon de s ’ essayer à différentes formes de rallyes au Royaume-Uni et à l ’ international , et de se préparer aux rigueurs et aux réalités des rallyes raids . -Nous sommes tout à fait fatigués de sentir comme des " " lorsqu' il s' agit de ce problème . Nous sommes franchement fatigués de nous sentir comme des « surveillants de salle » lorsqu' il s' agit de cette question . -Nous devons nous souvenir de qui nous sommes " . Nous devons nous rappeler qui nous sommes . -Nous avons vraiment aimé développer le Defender Call Challenge - c' est un championnat vraiment fun . Nous avons eu beaucoup de plaisir à développer le véhicule du Defender Challenge – le championnat va vraiment être palpitant . » -Nous pensons aussi que parfois les photos n' ont pas besoin d' explication ou de commentaire . Nous pensons aussi que parfois les images n' ont pas besoin d' explication ou de commentaire . -Nous sentons toujours qu' elle nous protège , rien de mauvais ne se produira à nous . On sent en permanence sa protection , rien de grave ne peut nous arriver . -Nous craignons que cela n' encourage l' intervention . Nous avons peur qu' elle encourage les interventions . -Nous sommes en train de faire marche arrière , parce que les avantages qu' il apporte sont énormes . Nous le projet car les avantages qu' il présente sont énormes . -Nous sommes contraints d' augmenter le loyer pour nous aider à payer les nouvelles taxes . Nous sommes obligés d' augmenter les loyers pour nous aider à payer les nouvelles taxes . -Nous prévoyons une chute très probable de l' euro . Nous anticipons un fort potentiel de baisse de l' euro . -Nous sommes tout à fait reconnaissants et chacun d' entre eux était absolument extraordinaire . On leur est tellement reconnaissants , chacun d' eux a été absolument formidable . -Nous croyons que Bombardier recevra de nouvelles commandes pour le à mesure que le programme d' essais de vol. Nous pensons que Bombardier enregistrera de nouvelles commandes pour ses avions au fur et à mesure que le programme d' essais en vol avancera . -Nous avons construit ce festival comme un moyen de continuer à travailler avec eux . Nous avons créé ce festival dans le but de pouvoir continuer à travailler avec eux . -Nous pouvons choisir d' être une région de ( essentiellement ) la Petite Angleterre . Nous pouvons décider d' être une région de la petite Angleterre ( essentiellement ) . -Nous avons certainement travaillé dur pour essayer de persuader nos jeunes résidents [ de pharmacie ] de venir ici . C' est sûr qu' on a travaillé fort pour intéresser nos jeunes [ résidents en pharmacie ] pour qu' ils viennent et restent ici . -Nous n' avons pas à nous précipiter dans une chirurgie qui est irréversible . Vous n ’ avez pas à vous précipiter pour une intervention chirurgicale qui est irréversible . -Nous avons nos chiens l' année dernière et , à mon étonnement , nous n' étions pas seuls . Nous avons déguisé nos chiens l' année dernière et , à mon grand étonnement , ils n' étaient pas les seuls . -Nous nous sentons soutenus et protégés dans des moments difficiles , et le passage du temps nous a permis de constater qu' elle était le chef d' orchestre , avec nous pour nous avancer sur la voie qu' elle nous avait tracée . Nous nous sentons portés et protégés dans les moments difficiles , le recul du temps nous fait réaliser combien elle était chef d' orchestre et nous essayons de rester dans la voie qu' elle nous avait tracée . -Nous avons constaté que la grande majorité des entreprises cherchent maintenant à exporter en dehors de l' Europe , en se concentrant sur les pays qui sont en train de se moderniser et de se développer tandis que les pays de l' UE stagnent . Nous avons découvert que la vaste majorité des entreprises cherchent désormais à exporter en dehors de l' Europe , en se concentrant sur les pays qui se et se développent tandis que les États de l' UE stagnent . -Nous sommes arrivés par hélicoptère et avons pris un avion privé vers la France via Corfou et , où Pasqua est arrivé . Arrivés là-bas en hélicoptère , on prend un avion privé qui nous ramène en France via Corfou et , où Pasqua embarque . -Nous avions décidé de faire un premier vol et de faire une mise à jour , et c' est ce que nous avons fait . Nous avions décidé de procéder à un premier vol et de prévoir une période de mise à jour , et c' est ce que nous avons fait . -Nous avons 200 artistes talentueux qui vont du projet au projet . Nous avons 200 artistes de talent qui tournent de projets en projets . -Nous avons effectivement empêché les avions de descendre , les bâtiments sont en train d' exploser et les gens ont été assassinés parce que nous avons été capables d' apprendre à l' avance les plans . Nous avons bel et bien empêché des avions de tomber , des immeubles d' exploser et gens d' être assassinés parce que nous avons pu prendre connaissance des faits à l' avance . -Nous l' avons testé au cours des derniers mois et il semble parfait de le partager avec Halloween parce qu' il a donné cet effet merveilleux de la lumière . Nous l' avons testée au cours des derniers mois et il semblait opportun de la partager avec tous au moment d' Halloween car elle donne un extraordinaire effet brillant . -Nous avons un support plus solide et stable . Nous avons des appuis plus solides et constants . -Nous avons atterri à Orly . On atterrit à Orly . -Nous devons changer les choses dans la ville . Nous avons besoin de faire changer les choses à la Ville . -Nous devons admettre que les prises d' otages sont des prises d' otages politiques - c' est le plus souvent - et permettre aux otages d' avoir accès à la justice , de porter des accusations de presse et d' amener leurs ravisseurs à la Cour pénale internationale . Il faut qualifier la prise d' otages de prise d' otages " politique " - ce qui est le cas la plupart du temps - et permettre aux otages d' avoir accès à la justice , de porter plainte et d' amener leurs preneurs d' otages devant la Cour pénale internationale . -Nous n' avons plus les moyens de payer 100.000 dollars pour les feux d' artifice , mais 4 à 5 millions de dollars pour la terre juste en cas ... Nous n' avons plus les moyens de nous payer des feux d' artifice à 100 000 $ , des terrains de 4 à 5 millions $ pour au cas où ... -Nous préférons envoyer des robots à Mars plutôt que de savoir ce qui se trouve dans la rivière , à seulement 11 kilomètres sous la surface de l' océan . On préfère envoyer des robots sur Mars , que de savoir ce qui vit dans la fosse des Mariannes , à seulement 11 kilomètres de la surface de la mer . -Nous en avons vraiment besoin " . On a vraiment besoin de ça . -Nous voyons des clients de tous les milieux . Nous voyons des clients de toutes les couches de la société . -Nous voyons sa montée fulgurante , mais aussi la chute vertigineuse qui s' ensuit . L' occasion de suivre son incroyable ascension , mais aussi la chute vertigineuse qui s' en suivra . -Nous pensons qu' un meilleur processus consiste à attribuer un sexe masculin ou féminin , puis à attendre . Nous pensons qu' il vaudrait mieux attribuer un sexe masculin ou féminin , et attendre . -Nous avons ensuite été amenés à l' hôtel où la presse s' est rassemblée . On arrive ensuite sur le site de l' hôtel où la presse est présente . -Nous étions très excités , mais aussi très tendue . On est très excités , mais très tendus aussi . -Nous vous trouverons et vous devant les tribunaux . Nous vous trouverons et vous devant les tribunaux . -Nous tenons à nous excuser auprès de tous les détenteurs de billets pour les inconvénients que cela a causé . Nous souhaitons nous excuser auprès de tous les détenteurs de billet pour tout désagrément causé . -Nous l' aurions emmené du Cameroun si nous avions de l' argent . Nous l' aurions emmené à l' étranger si nous avions eu assez d' argent . -Les riches à Singapour utilisent également des , des barres d' or ou des montres Rolex , qui peuvent commander jusqu' à 60 % de leur prix d' achat en liquide . Les gens aisés de Singapour vont également dans les boutiques de prêt sur gage pour mettre en gage des lingots d' or ou des montres Rolex , qui peuvent représenter jusqu' à 60 % de leur prix d' achat en espèces . - une , il sur le célèbre sanctuaire de à / h. Le sanctuaire est situé à une altitude de plus de 3000 mètres et de nombreux spectateurs se sont rassemblés pour admirer son exploit . Equipé d' un ( une combinaison munie d' ailes ) , il est passé à 160 km / h au-dessus du célèbre sanctuaire , situé à plus de 3 000 mètres d' altitude , où de nombreux s' étaient rassemblés pour observer son exploit . -De plus , contrairement à de nombreuses communes , le projet de loi est respecté par les contribuables plutôt que par des associations professionnelles . Facture qui , contrairement à de nombreuses communes , est en plus réglée par les contribuables , et non pas par les associations de commerçants . -Ce que je vois est une usine qui fabrique de bons pneus agricoles , possède un bon équipement , un bon emplacement et beaucoup de place pour la croissance . Ce que je vois , c' est une usine qui fabrique de bons pneus agricoles , a de bons équipements , est bien située et dispose d' espaces pour s' agrandir . -Ce que Pierre Stein cherche , c' est autre chose . Celle que traque Pierre Nora est tout autre . -Qu' en est-il de la première ? Mais qu' en est-il du premier ? -Quels sont vos souvenirs d' être libérés aujourd' hui ? Quels sont aujourd' hui vos souvenirs de votre libération ? -Que pensez-vous des cheveux de ? Que pensez-vous de la coiffure de ? -Ce qu' il a découvert est terrifiant . Ce qu' il a découvert l' a horrifié . -Qu' est-ce que Le Pen fait avec sa nouvelle popularité parmi les classes blanches françaises ? Que fait Marine Le Pen avec sa nouvelle popularité auprès de la classe ouvrière blanche française ? -Quels risques les États-Unis en réalité , leur image ? Que risquent réellement les Etats-Unis ? une dégradation de leur image ? -Quel genre d' accord entre la CGT et Goodyear ? Quel type d' accord attendez-vous entre la CGT et Goodyear ? -Quelle a été la cause de la mort ? Quelle est la cause de la mort ? -Quoi qu' il en soit , il sera toujours mon point de référence . En tout état de cause , il sera toujours ma référence . -Lorsque François Hollande téléphone Barack Obama , ou lorsque le ministre des affaires étrangères , Laurent , convoque l' ambassadeur des États-Unis , il répond à une véritable découverte , celle de l' ampleur de la surveillance américaine des communications en France en général . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Lorsque François Hollande téléphone à Barack Obama ou quand le ministre des affaires étrangères Laurent convoque l' ambassadeur des Etats-Unis , ils réagissent à une vraie découverte , qui est celle de l' ampleur de la surveillance américaine sur l' ensemble des communications en France . -Quand je suis fini avec mon patient à la nuit et se coucher , le livre me retourne à un moment où la politique a inspiré des millions et des discours peuvent sonner . Quand j' ai fini les comptes-rendus médicaux de mes patients le soir et que je me couche , ce livre me à l' époque où les politiques inspiraient des millions de gens et où les discours pouvaient vous couper le souffle . -Quand j' ai quitté le pays , il y avait trois chaînes de télévision : Quand je suis parti il y avait trois chaînes de télévision . -Quand j' étais petite , Halloween était magique . Lorsque j' étais petit , Halloween était magique . -Lorsque la phase 2 sera terminée , les lignes seront acheminées à Manchester et à Leeds . Lorsque la Phase 2 sera achevée , les lignes Manchester et Leeds . -Lorsque WikiLeaks révèle le contrôle de l' administration américaine sur des questions politiques et autres quelque part dans le monde , nous estimons qu' il s' agit là d' une leçon importante pour le gouvernement américain . & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; & # 160 ; Lorsque WikiLeaks lève le voile sur l' analyse par la diplomatie américaine d' enjeux politiques ou autres dans le monde entier , nous considérons en effet que , au regard de la puissance américaine , cela constitue un éclairage important . -Interrogé sur ce point , un responsable de l' administration américaine a répondu : " Les États-Unis ne mènent pas de surveillance électronique aux bureaux de la Banque mondiale et du FMI à Washington " . Interrogé sur le sujet , un responsable de l' administration américaine a répondu : " les Etats-Unis ne mènent pas de surveillance électronique visant les sièges de la Banque mondiale et du FMI à Washington " . -Lorsqu' on leur a demandé des points de vue sur des domaines d' action précis , allant de la réglementation monopolistique aux lois sur les produits , la majorité des chefs d' entreprise pensaient que le contrôle de ces compétences clés devait être renvoyé à Westminster . Lorsqu' on leur demande leur avis sur des points politiques spécifiques – allant de la réglementation des monopoles aux lois sur les produits – la majorité des chefs d' entreprise pensent que le contrôle de ces compétences clés devrait retourner aux mains de Westminster . -Quand il s' agit de bonbons , nous ne nous pas . Lorsqu' il s' agit de bonbons , on ne pas . -Lorsque la police l' a arrêtée , ils ont découvert la boisson à emporter entre les jambes . Lorsque la police l' a arrêté , ils ont découvert la boisson à emporter entre ses jambes . -Lorsqu' elle se rebelle , elle est soumise à la cruauté extrême des mains d' une mère très et devient un objet de fascination érotique pour un autre . Lorsqu' elle se rebelle , elle fait face à la cruauté sans borne d' une Mère Supérieure délibérément et devient un objet de fascination érotique pour une autre . -Quand les animaux ont vu un chien déplacer leur queue vers la droite ( du point de vue du chien ) , ils sont restés parfaitement détendu . Lorsque les animaux ont vu un chien sans expression remuer la queue vers la droite ( du point de vue du chien qui la queue ) , ils sont restés parfaitement . -Lorsque le garçon était âgé d' environ 15 ans , il est retourné à un garçon et a épousé une femme . À l' âge d' environ 15 ans , il est redevenu un garçon et s' est marié avec une femme . -Lorsque ces enfants sont nés , vous n' avez pas appelé le psychiatre , vous avez appelé un chirurgien . Lorsque ces enfants , on n' appelait pas le psychiatre , on au chirurgien . -Lorsqu' ils sont cultivés , ils peuvent prendre des décisions concernant leurs propres corps . Lorsqu' ils , ils pourront prendre des décisions concernant leur propre corps . -Où est-ce que cela quitte l' Écosse ? Et l' Écosse , dans tout ça ? -Alors que avait qualifié la recherche de " diligence raisonnable " , Martin a dit à Anderson Anderson que la semaine dernière , la campagne de souscription avait fait un important investissement pour déterminer si le mouvement pouvait rapporter des dividendes dans les sondages . Tandis que qualifiait l' examen de « concerté » , Martin a indiqué à Anderson Cooper de CNN que la campagne de réélection avait beaucoup investi pour découvrir si le changement serait bénéfique lors du scrutin . -Bien que M. ne dise pas que le président était " conscient du changement potentiel " , l' ancien chef du personnel a admis qu' il était « & # 160 ; possible & # 160 ; » Obama . Bien que ait dit qu' il ne pensait pas que le président « ait été au courant » du changement éventuel , l' ancien secrétaire général a admis qu' il était « possible » qu' Obama ait su . -Alors que Murphy a dit que le but du voyage était d' aider à améliorer les relations , il a dit qu' un " amour difficile " serait également . Murphy a déclaré que le but de ce voyage était de permettre d' améliorer les relations , mais également de faire preuve de « fermeté » . -Tout en bénéficiant du confort , tous les constructeurs offrent également des avions avec des cartes à haute densité pour les compagnies aériennes à bas coûts et les voyages régionaux . Tout en se de proposer des appareils confortables , tous les constructeurs offrent également des avions avec des configurations à forte densité aux compagnies aériennes low-cost et pour les voyages régionaux . -Bien que certains de crédit aient recours à des taux d' intérêt élevés , Mme Yes dit que non seulement les offrent des taux moins élevés que les autres prêteurs , mais qu' ils n' ajoutent pas directement à la dette . Tandis que certains prêteurs à taux réduit ont essuyé les critiques pour les taux d' intérêt exorbitants qu' ils pratiquent , Mlle indique que la mise en gage offre non seulement des taux moins élevés que les autres prêteurs , mais qu ’ en plus elle n' augmente pas directement la dette . -Bien qu' il n' y ait pas d' obligation formelle de consacrer plus de temps au calcul des mathématiques , les membres de la coalition ont déclaré que le grand de mathématiques , combiné à une plus grande pondération pour le sujet dans les tableaux de la ligue , aurait probablement incité les écoles à dispenser un enseignement supplémentaire . Bien qu' il n' y ait aucune obligation formelle de consacrer plus d' heures de l' emploi du temps aux maths , des sources proches du gouvernement de coalition ont indiqué que le de maths approfondi – de même que l' importance plus grande accordée aux matières figurant dans les classements internationaux – encouragerait vraisemblablement les écoles à assurer plus d' heures de cours . -Bien qu' il ne s' agisse pas de gaspillage , la ville ne sera jamais épargnée et la réglementation sévère de son utilisation risque d' être considérablement réduite . Sans vouloir dire de la gaspiller , la ville ne manquera jamais d' eau et la sévère réglementation quant à son utilisation pourrait être grandement . -Qui sont nos constructeurs ? Qui sont nos bâtisseurs ? -Pourquoi les tournées provinciales sont-elles si fructueuses ? Pourquoi les tournées en province ont-elles autant de succès ? -Pourquoi Titan a-t-elle besoin de cette usine aussi ? Pourquoi Titan a-t-il tant besoin de cette usine ? -Pourquoi est-il rare de découvrir de nouvelles espèces de mammifères marins ? Pourquoi est-il rare de découvrir de nouvelles espèces de mammifères marins ? -Pourquoi nous ne devrions pas avoir un système où vous pouvez mettre en marche , vos données dans un ordinateur , votez immédiatement et avoir un résultat à 6.30 la nuit ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Pourquoi ne pourrions-nous pas avoir un système dans lequel vous pouvez entrer , saisir vos coordonnées dans un ordinateur , voter immédiatement et avoir le résultat à 6h30 le lendemain matin ? -Pourquoi est-ce qu' il s' agit d' une découverte importante comme l' a dit la Société de conservation de la faune ? Pourquoi s' agit-il d' une découverte d' importance comme l' a affirmé la Wildlife Conservation Society ? -Pourquoi cela est-il accepté sans difficulté dans certains pays nordiques et protestants , mais reste encore mal en France ? Pourquoi ce qui est accepté sans peine dans certains pays nordiques et protestants , est-il encore mal vécu en France ? -Pourquoi une Ecosse indépendante devrait-elle être en mesure de faire appel à l' Europe ? Pourquoi une Écosse indépendante devrait-elle suivre les instructions de l' Europe , en fait ? -Pourquoi ne pas au : " Vous avez violé les règles de la guerre et celles de toutes les conventions de Genève , nous allons nous tourner vers la justice ? " Pourquoi ne pas aux preneurs d' otages " vous avez bafoué les règles de la guerre , comme celles de toutes les conventions de Genève , on va vous amener en justice " ? -Pourquoi l' attitude du gouvernement français : d' une part , elle publiquement et , d' autre part , l' ambassadeur des États-Unis à l' article 21 et , d' autre part , l' interdiction du parlement de la Grèce d' entrer son espace aérien sur la base d' une rumeur selon laquelle Edward se tenait à bord ? Comment expliquer l' attitude contradictoire du gouvernement français , qui d' un coté s' en public en convoquant l' ambassadeur des Etats-Unis le 21 octobre , et de l' autre interdit le survol du territoire par l' avion présidentiel bolivien , sur la base de la rumeur de la présence à son bord d' Edward ? -Pourquoi ces appels radicaux à l' unité nationale ? Pourquoi ces appels dramatiques à l' unité nationale ? -Pourquoi le libéralisme de Londres pour celui de Bruxelles , surtout aujourd' hui ? Pourquoi échanger le joug de Londres contre celui de Bruxelles , en particulier maintenant ? -Avec ses 420 designs d' inspiration et ses 2 kilomètres de plage de front de mer dans un décor de couleurs , le projet de à Menton pourrait vous donner un petit coup de ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 420 motifs , 2,2 kilomètres de bord de mer recouverts d' un manteau de lumière : on pourrait le coup de chaud à Menton avec la facture des . -Avec une dette brute de plus de 200 millions de dollars , cette ville n' est même plus capable d' acheter un stylo sans passer par un emprunt . Avec une dette brute de plus de 200 millions $ , cette ville n' est même plus capable d' acheter un stylo sans passer par un règlement d' emprunt . -Avec une statue de Machel , la présidente fondatrice du Mozambique , qui les a retrouvés , des milliers de personnes se sont rassemblées dans le centre-ville de Maputo pour des slogans de paix dans une manifestation publique rare . Aux pieds de la statue de Machel , président fondateur du Mozambique , en train de les , des milliers de personnes se sont rassemblées dans le centre de Maputo pour des slogans pacifiques lors d' une rare manifestation publique . -Avec tout le respect des chefs d' État qui semblent relativement indifférents aux fruits de la terre , " ils sont tous de la nourriture " Bernard a rendu volontairement . N' en déplaise à des chefs d' Etat qui affichaient une relative indifférence aux nourritures terrestres , " ils sont tous gourmands " , dénonce gentiment Bernard . -Avec l' incinération , il y a un " sentiment de violence contre le corps d' un être aimé " , qui sera réduit à un " tas de cendres " au plus tard au lieu d' un processus de décomposition qui " accompagnera les moments de la douleur " . Il y a , avec la crémation , " une violence faite au corps aimé " , qui va être " réduit à un tas de cendres " en très peu de temps , et non après un processus de décomposition , qui " accompagnerait les phases du deuil " . -Avec des centaines de milliers de personnes qui reçoivent des avis d' annulation de la part de leurs fournisseurs , les Républicains ont le président ces derniers jours pour tromper le public américain . Après que des centaines de milliers de personnes ont reçu des avis de résiliation de leurs prestataires , les républicains ont critiqué le président au cours des derniers jours en l' accusant de tromper le peuple américain . -N' ayant nulle part où vivre , il dort sur les canapés des amis qui le encore . Sans logement , il fait la tournée des canapés de ses amis qui veulent bien encore le recevoir . -Avec notre aide , le Royaume-Uni et les Pays-Bas ont mis en place des systèmes automatisés de saisie des passeports qui utilisent la reconnaissance faciale . Avec notre aide , le Royaume-Uni et la Hollande ont déployé des automatiques utilisant la reconnaissance faciale . -Avec la crise de l' euro , le projet Europe est officiellement mort . Avec la crise de l' euro , le Projet Europe est officiellement mort . -À l' exception de l' opéra , les provinces demeurent le parent pauvre de la culture en France . À l' exception de l' opéra , la province reste le parent pauvre de la culture en France . -Avec le générateur de groupe qui l' équipement d' exploitation et de nettoyage , et ses bénévoles travaillent les premières heures du dimanche matin . Grâce à l' éclairage du groupe électrogène , la salle d' opération et la stérilisation du matériel , le Dr et ses bénévoles travaillent jusqu' aux premières heures du dimanche matin . -Sans donner de chiffres précis , le président du Conseil d' administration , Yves Bergeron , a pris la parole lors de la conférence de presse tenue hier à Chicoutimi pour obtenir des assurances . Sans livrer de chiffres précis , le président du conseil d' administration , Yves Bergeron , a profité de la rencontre de presse tenue hier , à Chicoutimi , pour se montrer rassurant . -Sans cela , nous sommes confrontés à une crise de capacité sur notre réseau ferroviaire . Sans lui , nous sommes confrontés à un problème de capacité de notre réseau ferroviaire . -Sans préciser la maladie qu' elle souffrait , l' de " Respect " a confirmé aux médias le 16 octobre que les effets secondaires d' un traitement qu' elle recevait étaient " difficiles " à traiter . Sans préciser la maladie dont elle souffrait , la célèbre interprète de Respect avait affirmé aux médias le 16 octobre que les effets secondaires d' un traitement qu' elle recevait étaient " difficiles " . -Sans la possibilité d' une dévaluation de la monnaie nationale , le Sud de l' Europe se trouve confronté à un désavantage de productivité lié à l' Allemagne . Sans la possibilité d' une dévaluation de la devise nationale , les pays d' Europe du sud se retrouvent avec un problème de productivité intrinsèque vis-à-vis de l' Allemagne . -Sans le soutien de la deuxième poignée de grands jets modernes , les experts disent qu' il est peu probable que l' appel à une nouvelle norme de l' industrie soit peu probable , mais qu' il risque de détourner l' attention d' une vague de ventes à l' échelle de la planète . & # 160 ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; Sans le soutien du seul autre grand constructeur de gros appareils modernes , les experts disent qu' il est peu probable que son appel à l' industrie aéronautique en vue de mettre en place une norme de taille pour les sièges aboutisse , mais qu' il pourrait faire oublier la vague de ventes de . -Sans ces cartes , ces personnes ne peuvent bénéficier de nombreux services sociaux ou de soins de santé dans ces bases , ni accéder aux magasins de ces derniers . Sans ces cartes , ces personnes ne peuvent bénéficier des nombreux services sociaux ou de santé fournis dans ces bases , ou accéder à leurs magasins . - DE DE Elle tente d' importer 2 kg de cocaïne dans des -Les femmes n' ont pas à être des professionnels plus sexy ; elles peuvent également être des aliments sexy comme la pizza , les hamburgers et les carottes . Les femmes n' ont plus besoin d' être des professionnelles sexy ; elles peuvent également être des aliments sexy comme des pizzas , des hamburgers et des carottes . - DANS Un travailleur meurt enseveli sous des blocs de béton -Travailler sept heures par jour , lorsque les gens d' autres pays travaillent huit heures , revient à la France . Travailler sept heures par jour quand dans d' autres pays on travaille huit heures handicape la France . -Le YMCA de n' était pas une organisation à but non lucratif alors que Lord God était utilisé entre 2009 et 2011 , un professeur de violence sexuelle à l' égard des enfants , le professeur Stephen , de l' Université Griffith , a déclaré à la commission . Le YMCA de Nouvelle-Galles du Sud n' était pas une organisation sûre pour les enfants à l' époque où Jonathan Lord y était employé , à savoir entre 2009 et 2011 , a déclaré l' expert en matière de violences sexuelles sur les enfants , le Professeur Stephen de l' université de Griffith devant la Commission . -Oui , mais en ignorant son travail moins connu et son écriture souterraine en tant qu' éditeur . En effet , mais en ignorant le travail caché et l' écriture souterraine de l' éditeur . -Oui encore , mais il sait qu' un tel honneur ne peut remplacer le véritable , l' oeuvre philosophique , qu' il a . Certes encore , mais en sachant qu' un tel sacre ne saurait remplacer l' huile sainte véritable , celle de la thèse , dont il s' est passé . -Hier , Obama a modifié son engagement initial . Hier , Barack Obama a ajusté sa promesse d' origine . -Hier , nous avons pris la colline de , qui donne sur la colline de . Hier , nous avons pris la colline de , qui surplombe celle de . -Hier soir , son épouse et lui ont remis des bonbons à des centaines d' enfants invités dans les jardins de la Maison Blanche à Washington . Avec son épouse , hier soir , il a distribué des friandises à des centaines d' enfants invités dans les jardins de la Maison-Blanche à Washington . -Pourtant , de tous les exemples que j' ai énumérés ci-dessus , ils semblent surtout être le choix de l' administration . Cependant , tous les exemples que j' ai répertoriés ci-dessus semblent être en grande partie le choix de l' administration . -Pourtant , elle est là , une présence silencieuse , me regarde . Pourtant , elle est là , présence silencieuse , elle est là et me regarde . -Pourtant , le niveau de l' émotion et de la fatigue l' a rendue difficile à penser clairement . Mais l' état d' émotion et de fatigue est tel qu' il est difficile d' y voir clair . -Pourtant , il n' existe aucune preuve cohérente que les hormones dans le lait ou dans d' autres aliments , les produits chimiques présents dans l' environnement ou les messages sexuels dans les médias sont à blâmer . Pourtant , il n' y a aucune théorie cohérente selon laquelle les hormones contenues dans le lait ou d' autres aliments , les produits chimiques présents dans l' environnement ou les messages sexuels véhiculés par les médias sont responsables . -Pourtant , cette identité a pris sa place . Mais cette identité n' a eu qu' un temps . -Pourtant , l' Amérique s' efforce de rassembler des données . Mais nous nous efforçons de rassembler des informations . -Pourtant , il y a un point intéressant qui a été oublié lors de cette d' un conte . Pourtant , on a oublié un point intéressant dans cette interprétation d' un vieux conte poussiéreux . -Un gouvernement d' État n' épargne pas près de $ et le Commonwealth a investi 300 millions de dollars et fait valoir qu' il s' agit d' un bon jour de bonne nouvelle . & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; & nbsp ; On ne peut pas avoir un gouvernement de l' État qui arrache presque un demi-milliard de dollars et le Commonwealth qui met 300 M $ , et prétendre que c' est un jour faste . -Vous pouvez quitter la maison à 5 heures du matin , à l' hôpital pour être le premier , et vous n' êtes pas le premier . Vous pouvez quitter la maison à 5 h , vous à l' hôpital pour être le premier , et vous n' êtes pas le premier . -Vous pouvez faire une distinction entre les sexes sans chirurgie , puis voir comment l' identité se développe . Il est possible de faire une assignation sexuelle sans opération , et voir comment l' identité se développe . -Vous pouvez voter pour lui , ou l' un des 10 meilleurs héros , pour être un héros de l' animation de l' année . Vous pouvez voter pour lui , ou l' un des 10 autres héros , pour l' élire « Héros CNN de l' année » . -Vous d' un trou et vous êtes soudainement sous le feu des projecteurs . On sort d' un trou et on est tout à coup sous les feux médiatiques . -Vous ne vous pas en public comme si vous ne vouliez pas attirer l' attention " , a déclaré un client , , âgé de 60 ans , aux Cambridge News . " Vous ne sortez pas ainsi dans la rue à moins que vous vouliez attirer l' attention " , a témoigné une cliente , , 60 ans , au journal Cambridge News . -Vous avez provoqué des attaques violentes et des insultes , en parlant de " travailleurs " qui travaillent " trois heures par jour " , et de " fous " , qui ciblent la CGT . Vous aviez prononcé de violentes attaques et des insultes , en parlant de " soi-disant ouvriers " , qui " travaillent trois heures " par jour , de syndicats " fous " , en visant la CGT . -Vous avez un bébé merveilleux et vous amuser . Vous avez un bébé magnifique et profitez du plaisir d' être parent . -Il faut se rappeler , à ce moment-là , que le président était en forme , alors nous étions comme " le Christ saint , qu' est-ce que nous faisons ? " . Rappelez-vous qu' à ce moment-là , le président n' était pas en forme , et donc on se disait , « Seigneur , que pouvons-nous faire ? » . -Les jeunes acteurs sont formés à ces compétences dans de nombreuses écoles . Les jeunes sont formés à ces métiers dans de nombreuses écoles . -et dîner dans un T-shirt à l' Ambassade de France . Et avoir en t-shirt à l' ambassade de France . -comme les noms des grands écrivains et artistes , qu' ils soient d' origine française ou venant d' ailleurs . Des grands noms d' écrivains , d' artistes , qu' ils soient de souche ou qu' ils soient venus d' ailleurs . -aussi bien la régularité du rythme visuel . Comme le fait aussi la régularité du rythme visuel . -parce que , au-delà de la forme imposée au cours de l' université , ce travail philosophique parle de la nécessité intérieure d' une vie . Car bien plus que la figure imposée du cursus universitaire , cette dit la nécessité intérieure d' une vie . -mais seulement à l' écran . Sur écran uniquement . -Dans l' esprit de son fondateur , , le " Parti de la " ne cible pas seulement les résidents des banlieues . Dans l' esprit d' , son fondateur , le " Parti de la banlieue " ne s' adresse pas uniquement aux . -À la suite d' une explosion dans une maison à qui a causé la mort de deux personnes grièvement blessées le vendredi 10 juillet , la grand-mère mineure de Iasi et son fils de 5 ans ont été par une fuite de gaz dans un à mauvais feu , l' avocat du parquet l' a dit vendredi soir . L' explosion d' une habitation à , qui a fait deux blessés graves vendredi matin , une grand-mère de 52 ans et son petit-fils de 5 ans , est due à une fuite de gaz provenant d' un chauffe-eau défectueux , a indiqué vendredi soir la du procureur du Roi . -Un des habitants de la rivière , les enfants appelés dans le quartier de Le ouvrit la porte portant son fusil de chasse . L' un des riverains du quartier du visité par les enfants a ouvert la porte en tenant son fusil de chasse . -Ou la vendre à quelqu' un d' autre , c' est ce qui s' est passé " , explique Paul Arsenault , titulaire de la chaire de Transat au Tourisme à l' École de gestion de l' UQAM . " Ou soit il le vendait à quelqu' un d' autre et c' est ce qui s' est passé " , explique Paul Arsenault , titulaire de la Chaire de tourisme Transat à l' École des sciences de la gestion de l' UQAM . -principalement pour la qualification des nouveaux pilotes de chasse . Ce , notamment , en vue de qualifier les jeunes pilotes de chasse . -Depuis le bouleversement de la révolution , les Français ont eu deux histoires et deux nations - un , l' autre révolutionnaire . A compter du séisme de la Révolution , les Français ont deux histoires et ils ont deux nations , l' une , l' autre révolutionnaire . -comme cela , qui découle de l' engagement lui-même : pourquoi , dans un pays aussi solidement implanté à l' intérieur de ces frontières , doté d' une structure aussi ancienne et si solidement construite , de ces appels massifs pour l' unité & nbsp ; ? & nbsp ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Dont celle-ci , qui naît de l' entreprise elle-même : pourquoi , dans un pays si durablement installé dans ses frontières , si anciennement et solidement , entend-on ces appels dramatiques à l' unité ? -la France à la sortie , la France en train de se faire . La France qui s' en va , la France qui vient -Le White Duke envisageait également de l' album le 04 novembre . Le White Duke a ainsi prévu de cet album , le 4 novembre . -La bataille a été incroyable . Il y a une incroyable . -Quand je suis revenue , il y avait des charges . Je reviens et il y en a plein . diff --git a/lingvo/core/ops/text_packing.cc b/lingvo/core/ops/text_packing.cc deleted file mode 100644 index e51c2505..00000000 --- a/lingvo/core/ops/text_packing.cc +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/text_packing.h" - -#include - -#include "tensorflow/core/platform/logging.h" - -namespace tensorflow { -namespace lingvo { - -TextPacking::TextPacking(int columns, int batch, std::vector times, - int align, bool pack, int spread_first_n, - bool use_last_fit) - : columns_(columns), - batch_(batch), - times_(std::move(times)), - align_(align), - pack_(pack), - spread_first_n_(std::min(spread_first_n, batch)), - use_last_fit_(use_last_fit), - wpos_(batch, std::vector(columns, 0)), - seq_(batch, 0), - last_fit_(0), - counter_(0) { - CHECK_EQ(columns_, times_.size()) << "The size of `times` must be `columns`"; -} - -bool TextPacking::Add(const std::vector& lens, PackingIndex* p) { - CHECK_EQ(columns_, lens.size()); - CHECK(p); - // Start searching for batch position where 'lens' could fit. - // If we find a fit, on next call we start searching from the same position. - // Because if we always start from 0 this loop becomes O(N^2) in batch size. - // TODO(krikun): add a benchmark for very large batch sizes - if (!use_last_fit_) { - last_fit_ = 0; - } - // b is the index of the row on which we see if the current sequence fits. - int b = last_fit_; - for (int i = 0; i < batch_; i++, b++) { - if (counter_ < spread_first_n_) { - b = counter_; - last_fit_ = 0; - } - b %= batch_; - bool fits = true; - for (int c = 0; c < columns_; c++) { - if (wpos_[b][c] + lens[c] > times_[c]) { - fits = false; - break; - } - } - if (fits) { - last_fit_ = b; - p->batch = b; - p->time.resize(columns_); - for (int c = 0; c < columns_; c++) { - p->time[c] = wpos_[b][c]; - wpos_[b][c] += lens[c]; - if (align_ > 1) { - int r = wpos_[b][c] % align_; - if (r) wpos_[b][c] += (align_ - r); - } - if (!pack_) { - wpos_[b][c] = times_[c]; - } - } - seq_[b]++; - p->seq = seq_[b]; - counter_++; - return true; - } - } - return false; -} - -void TextPacking::Reset() { - for (int b = 0; b < batch_; b++) { - seq_[b] = 0; - for (int c = 0; c < columns_; c++) { - wpos_[b][c] = 0; - } - } - last_fit_ = 0; - counter_ = 0; -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/text_packing.h b/lingvo/core/ops/text_packing.h deleted file mode 100644 index d4c8c2d1..00000000 --- a/lingvo/core/ops/text_packing.h +++ /dev/null @@ -1,112 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef THIRD_PARTY_PY_LINGVO_CORE_OPS_TEXT_PACKING_H_ -#define THIRD_PARTY_PY_LINGVO_CORE_OPS_TEXT_PACKING_H_ - -#include - -namespace tensorflow { -namespace lingvo { - -// Text packing. -// -// It takes tuples of variable-length sequences and packs into batches of -// size [batch, time_i] for different time_i per each column. -// -// For example this picture describes packing five 2-tuples of sequences -// into 2-tuple of matrices (batch, time) with batch=2, time=10, and align=2: -// -// column 0 column1 -// batch 0 [ a a a - b b b b b - ] [ A A A A B B B - - -] -// batch 1 [ c c c c c - d - e - ] [ C C - D - E - - - -] -// -// Each tuple is assigned PackedIndex as following: -// (a a a, A A A A) // p.batch=0 p.time=(0, 0) p.seq=1 -// (b b b b b, B B B) // p.batch=0 p.time=(4, 4) p.seq=2 -// (c c c c, C C) // p.batch=1 p.time=(0, 0) p.seq=1 -// (d, D) // p.batch=1 p.time=(6, 4) p.seq=2 -// (e, E) // p.batch=1 p.time=(8, 6) p.seq=3 -class TextPacking { - public: - // columns: number of columns - // batch: batch dimension - // times: time dimension, one element per each column. - // align: align sequence start position modulo n - // pack: set to false to disable packing - // spread_first_n: The first n added sequences will be assigned to the first n - // rows. Note that n is at most `batch`. If a value larger than `batch` is - // provided, `batch` will be used for n instead. - // use_last_fit: Whether to enable an optimization where Add() starts its - // search from the row of the previous successful Add(). When disabled (by - // default), we always start from 0, resulting in O(N^2) in batch size. - TextPacking(int columns, int batch, std::vector times, int align, - bool pack, int spread_first_n, bool use_last_fit = false); - - // Same as above, except that all columns share the same `time` and - // `spread_first_n` is set to 0. - TextPacking(int columns, int batch, int time, int align, bool pack) - : TextPacking(columns, batch, std::vector(columns, time), align, - pack, /*spread_first_n=*/0, /*use_last_fit=*/false) {} - - // Describes the location of a packed item in the batch. - struct PackingIndex { - // The (row) index of this item in the packed batch. - int batch; - - // A vector of size `columns`, one per column, indicating the starting - // positions of this item. - std::vector time; - - // Sequence index: a one-based index indicating how many packed items come - // before this item on the same row. - int seq; - }; - - // Adds a new item with sequence lengths 'lens' to the batch. - // Returns false if the item does not fit. - bool Add(const std::vector& lens, PackingIndex* p); - - // Resets internal state, forgetting any previously packed items. - void Reset(); - - // Current writing position at batch index 'b' column 'c'. - int wpos(int b, int c) const { return wpos_[b][c]; } - - private: - const int columns_; - const int batch_; - const std::vector times_; - const int align_; - const bool pack_; - const int spread_first_n_; - const bool use_last_fit_; - - // Current write position in each column in each row in batch. - std::vector> wpos_; - // Current sequence index in each row in batch. - std::vector seq_; - - // Row index of the last successfully packed segment. - int last_fit_; - - // How many sequences have been added. - int counter_; -}; - -} // namespace lingvo -} // namespace tensorflow - -#endif // THIRD_PARTY_PY_LINGVO_CORE_OPS_TEXT_PACKING_H_ diff --git a/lingvo/core/ops/text_packing_test.cc b/lingvo/core/ops/text_packing_test.cc deleted file mode 100644 index bcdc3bdf..00000000 --- a/lingvo/core/ops/text_packing_test.cc +++ /dev/null @@ -1,282 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/text_packing.h" - -#include - -#include -#include - -namespace tensorflow { -namespace lingvo { -namespace { - -// column 0 column1 -// batch 0 [ a a a - b b b b b - ] [ A A A A B B B - - -] -// batch 1 [ c c c c c - d - e - ] [ C C C - D - E - - -] -TEST(TextPackingTest, TestPacking) { - TextPacking pack(2, 2, 10, 2, true); - std::vector p(5); - EXPECT_TRUE(pack.Add({3, 4}, &p[0])); // (a a a, A A A A) - EXPECT_TRUE(pack.Add({5, 3}, &p[1])); // (b b b b b, B B B) - EXPECT_TRUE(pack.Add({5, 3}, &p[2])); // (c c c c c, C C C) - EXPECT_TRUE(pack.Add({1, 1}, &p[3])); // (d, D) - EXPECT_TRUE(pack.Add({1, 1}, &p[4])); // (e, E) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 0); - EXPECT_EQ(p[1].time, std::vector({4, 4})); - EXPECT_EQ(p[1].seq, 2); - EXPECT_EQ(p[2].batch, 1); - EXPECT_EQ(p[2].time, std::vector({0, 0})); - EXPECT_EQ(p[2].seq, 1); - EXPECT_EQ(p[3].batch, 1); - EXPECT_EQ(p[3].time, std::vector({6, 4})); - EXPECT_EQ(p[3].seq, 2); - EXPECT_EQ(p[4].batch, 1); - EXPECT_EQ(p[4].time, std::vector({8, 6})); - EXPECT_EQ(p[4].seq, 3); -} - -TEST(TextPackingTest, TestReset) { - TextPacking pack(2, 2, 10, 2, true); - std::vector p(5); - EXPECT_TRUE(pack.Add({3, 4}, &p[0])); // (a a a, A A A A) - EXPECT_TRUE(pack.Add({5, 3}, &p[1])); // (b b b b b, B B B) - EXPECT_TRUE(pack.Add({5, 3}, &p[2])); // (c c c c c, C C C) - pack.Reset(); - EXPECT_TRUE(pack.Add({1, 1}, &p[3])); // (d, D) - EXPECT_TRUE(pack.Add({1, 1}, &p[4])); // (e, E) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 0); - EXPECT_EQ(p[1].time, std::vector({4, 4})); - EXPECT_EQ(p[1].seq, 2); - EXPECT_EQ(p[2].batch, 1); - EXPECT_EQ(p[2].time, std::vector({0, 0})); - EXPECT_EQ(p[2].seq, 1); - // after reset - EXPECT_EQ(p[3].batch, 0); - EXPECT_EQ(p[3].time, std::vector({0, 0})); - EXPECT_EQ(p[3].seq, 1); - EXPECT_EQ(p[4].batch, 0); - EXPECT_EQ(p[4].time, std::vector({2, 2})); - EXPECT_EQ(p[4].seq, 2); -} -TEST(TextPackingTest, TestDoesNotFit) { - TextPacking pack(2, 2, 10, 2, true); - TextPacking::PackingIndex p; - - EXPECT_FALSE(pack.Add({11, 1}, &p)); - EXPECT_FALSE(pack.Add({1, 11}, &p)); - - EXPECT_TRUE(pack.Add({3, 4}, &p)); - - EXPECT_TRUE(pack.Add({5, 3}, &p)); - - EXPECT_TRUE(pack.Add({5, 3}, &p)); - EXPECT_FALSE(pack.Add({5, 1}, &p)); - EXPECT_FALSE(pack.Add({1, 7}, &p)); - - EXPECT_TRUE(pack.Add({1, 1}, &p)); - EXPECT_FALSE(pack.Add({3, 1}, &p)); - EXPECT_FALSE(pack.Add({1, 5}, &p)); - - EXPECT_TRUE(pack.Add({1, 1}, &p)); - EXPECT_FALSE(pack.Add({3, 1}, &p)); - - EXPECT_FALSE(pack.Add({1, 1}, &p)); -} - -// column 0 column1 -// batch 0 [ a a a b b b b b d e ] [ A A A A B B B D E -] -// batch 1 [ c c c c c - - - - - ] [ C C C - - - - - - -] -TEST(TextPackingTest, TestDoNotAlign) { - TextPacking pack(2, 2, 10, 1, true); - std::vector p(5); - EXPECT_TRUE(pack.Add({3, 4}, &p[0])); // (a a a, A A A A) - EXPECT_TRUE(pack.Add({5, 3}, &p[1])); // (b b b b b, B B B) - EXPECT_TRUE(pack.Add({5, 3}, &p[2])); // (c c c c c, C C C) - EXPECT_TRUE(pack.Add({1, 1}, &p[3])); // (d, D) - EXPECT_TRUE(pack.Add({1, 1}, &p[4])); // (e, E) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 0); - EXPECT_EQ(p[1].time, std::vector({3, 4})); - EXPECT_EQ(p[1].seq, 2); - EXPECT_EQ(p[2].batch, 1); - EXPECT_EQ(p[2].time, std::vector({0, 0})); - EXPECT_EQ(p[2].seq, 1); - EXPECT_EQ(p[3].batch, 0); - EXPECT_EQ(p[3].time, std::vector({8, 7})); - EXPECT_EQ(p[3].seq, 3); - EXPECT_EQ(p[4].batch, 0); - EXPECT_EQ(p[4].time, std::vector({9, 8})); - EXPECT_EQ(p[4].seq, 4); -} - -TEST(TextPackingTest, TestDoNotPack) { - TextPacking pack(2, 2, 10, 0, false); - std::vector p(5); - EXPECT_TRUE(pack.Add({3, 4}, &p[0])); // (a a a, A A A A) - EXPECT_TRUE(pack.Add({5, 3}, &p[1])); // (b b b b b, B B B) - EXPECT_FALSE(pack.Add({5, 3}, &p[2])); // (c c c c c, C C C) - EXPECT_FALSE(pack.Add({1, 1}, &p[3])); // (d, D) - EXPECT_FALSE(pack.Add({1, 1}, &p[4])); // (e, E) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 1); - EXPECT_EQ(p[1].time, std::vector({0, 0})); - EXPECT_EQ(p[1].seq, 1); -} - -// column 0 column1 -// batch 0 [ a a a - ] [ A A A A - - ] -// batch 1 [ b b c - ] [ B C C C C C ] -TEST(TextPackingTest, TestDifferentTimesPerColumn) { - TextPacking pack(2, 2, {4, 6}, 1, true, 0); - std::vector p(5); - EXPECT_TRUE(pack.Add({3, 4}, &p[0])); // (a a a, A A A A) - EXPECT_TRUE(pack.Add({2, 1}, &p[1])); // (b b, B) - EXPECT_TRUE(pack.Add({1, 5}, &p[2])); // (c, C C C C C) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 1); - EXPECT_EQ(p[1].time, std::vector({0, 0})); - EXPECT_EQ(p[1].seq, 1); - EXPECT_EQ(p[2].batch, 1); - EXPECT_EQ(p[2].time, std::vector({2, 1})); - EXPECT_EQ(p[2].seq, 2); - EXPECT_FALSE(pack.Add({2, 1}, &p[3])); - EXPECT_FALSE(pack.Add({1, 3}, &p[4])); -} - -// column 0 column1 -// batch 0 [ a a d - ] [ A A A A D - ] -// batch 1 [ b b - - ] [ B - - - - - ] -// batch 2 [ c - - - ] [ C C C C - - ] -TEST(TextPackingTest, TestSpreadFirstN) { - // First 3 sequences will be assigned different rows. - TextPacking pack(2, 3, {4, 6}, 1, true, 3); - std::vector p(4); - TextPacking::PackingIndex unused; - EXPECT_TRUE(pack.Add({2, 4}, &p[0])); // (a a a, A A A A) - EXPECT_FALSE(pack.Add({5, 1}, &unused)); - EXPECT_TRUE(pack.Add({2, 1}, &p[1])); // (b b, B) - EXPECT_FALSE(pack.Add({1, 7}, &unused)); - EXPECT_TRUE(pack.Add({1, 4}, &p[2])); // (c, C C C C C) - EXPECT_TRUE(pack.Add({1, 1}, &p[3])); // (d, D) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 1); - EXPECT_EQ(p[1].time, std::vector({0, 0})); - EXPECT_EQ(p[1].seq, 1); - EXPECT_EQ(p[2].batch, 2); - EXPECT_EQ(p[2].time, std::vector({0, 0})); - EXPECT_EQ(p[2].seq, 1); - EXPECT_EQ(p[3].batch, 0); - EXPECT_EQ(p[3].time, std::vector({2, 4})); - EXPECT_EQ(p[3].seq, 2); -} - -// column 0 column1 -// batch 0 [ a a d - ] [ A A A A D - ] -// batch 1 [ b b c - ] [ B C C C C - ] -// batch 2 [ - - - - ] [ - - - - - - ] -TEST(TextPackingTest, TestSpreadFirstNSmall) { - // First 2 sequences will be assigned different rows. - TextPacking pack(2, 3, {4, 6}, 1, true, 2); - std::vector p(4); - EXPECT_TRUE(pack.Add({2, 4}, &p[0])); // (a a, A A A A) - EXPECT_TRUE(pack.Add({2, 1}, &p[1])); // (b b, B) - EXPECT_TRUE(pack.Add({1, 4}, &p[2])); // (c, C C C C C) - EXPECT_TRUE(pack.Add({1, 1}, &p[3])); // (d, D) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 1); - EXPECT_EQ(p[1].time, std::vector({0, 0})); - EXPECT_EQ(p[1].seq, 1); - EXPECT_EQ(p[2].batch, 1); - EXPECT_EQ(p[2].time, std::vector({2, 1})); - EXPECT_EQ(p[2].seq, 2); - EXPECT_EQ(p[3].batch, 0); - EXPECT_EQ(p[3].time, std::vector({2, 4})); - EXPECT_EQ(p[3].seq, 2); -} - -// With use_last_fit set: -// column 0 column1 -// batch 0 [ a d d d ] [ A A A A A D ] -// batch 1 [ b b c - ] [ B B C - - - ] -// -// With use_last_fit unset: -// column 0 column1 -// batch 0 [ a c - - ] [ A A A A A C ] -// batch 1 [ b b - - ] [ B B - - - - ] -TEST(TextPackingTest, TestUseLastFit) { - { - TextPacking pack(2, 2, {4, 6}, 1, true, 0, /*use_last_fit=*/true); - std::vector p(5); - EXPECT_TRUE(pack.Add({1, 5}, &p[0])); // (a, A A A A A) - EXPECT_TRUE(pack.Add({2, 2}, &p[1])); // (b b, B B) - // starts searching in batch 1. - EXPECT_TRUE(pack.Add({1, 1}, &p[2])); // (c, C) - // wraps around to search in batch 0. - EXPECT_TRUE(pack.Add({3, 1}, &p[3])); // (d d d, D) - EXPECT_FALSE(pack.Add({3, 1}, &p[4])); // (e e e, E) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 1); - EXPECT_EQ(p[1].time, std::vector({0, 0})); - EXPECT_EQ(p[1].seq, 1); - EXPECT_EQ(p[2].batch, 1); - EXPECT_EQ(p[2].time, std::vector({2, 2})); - EXPECT_EQ(p[2].seq, 2); - EXPECT_EQ(p[3].batch, 0); - EXPECT_EQ(p[3].time, std::vector({1, 5})); - EXPECT_EQ(p[3].seq, 2); - } - { - TextPacking pack(2, 2, {4, 6}, 1, true, 0, /*use_last_fit=*/false); - std::vector p(4); - EXPECT_TRUE(pack.Add({1, 5}, &p[0])); // (a, A A A A A) - EXPECT_TRUE(pack.Add({2, 2}, &p[1])); // (b b, B B) - // starts search in batch 0. - EXPECT_TRUE(pack.Add({1, 1}, &p[2])); // (c, C) - EXPECT_FALSE(pack.Add({3, 1}, &p[3])); // (d d d, D) - EXPECT_EQ(p[0].batch, 0); - EXPECT_EQ(p[0].time, std::vector({0, 0})); - EXPECT_EQ(p[0].seq, 1); - EXPECT_EQ(p[1].batch, 1); - EXPECT_EQ(p[1].time, std::vector({0, 0})); - EXPECT_EQ(p[1].seq, 1); - EXPECT_EQ(p[2].batch, 0); - EXPECT_EQ(p[2].time, std::vector({1, 5})); - EXPECT_EQ(p[2].seq, 2); - } -} - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/tokenizer_op_headers.h b/lingvo/core/ops/tokenizer_op_headers.h deleted file mode 100644 index c629104c..00000000 --- a/lingvo/core/ops/tokenizer_op_headers.h +++ /dev/null @@ -1,151 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_TOKENIZER_OP_HEADERS_H_ -#define LINGVO_CORE_OPS_TOKENIZER_OP_HEADERS_H_ - -#include -#include -#include -#include - -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/env.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -template -class LabelToTokenIdOp : public OpKernel { - public: - explicit LabelToTokenIdOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("append_eos", &append_eos_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("maxlen", &maxlen_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("pad_to_maxlen", &pad_to_maxlen_)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor& labels = ctx->input(0); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(labels.shape()), - errors::InvalidArgument("labels must be a vector, but get ", - labels.shape().DebugString())); - const int batch = labels.NumElements(); - auto Tlabels = labels.flat(); - Tensor token_ids(DT_INT32, TensorShape({batch, maxlen_})); - auto Ttoken_ids = token_ids.matrix(); - Ttoken_ids.setZero(); // Sanity - Tensor target_ids(DT_INT32, TensorShape({batch, maxlen_})); - auto Ttarget_ids = target_ids.matrix(); - Ttarget_ids.setZero(); // Sanity - Tensor paddings(DT_FLOAT, TensorShape({batch, maxlen_})); - auto Tpaddings = paddings.matrix(); - Tpaddings.setZero(); // Sanity - int actual_maxlen = pad_to_maxlen_ ? maxlen_ : 0; - for (int i = 0; i < batch; ++i) { - VLOG(1) << i << " " << Tlabels(i); - std::vector ids = TokenizerClass::StringToIds(Tlabels(i)); - if (ids.size() + 1 > maxlen_) { - LOG(WARNING) << "Too long target " << ids.size() << " " << Tlabels(i); - ids.resize(maxlen_ - 1); - } - const int id_size = ids.size(); - const int32 kSOS = 1; - const int32 kEOS = 2; - Ttoken_ids(i, 0) = kSOS; - for (int j = 0; j < id_size; ++j) { - Ttoken_ids(i, j + 1) = ids[j]; - Ttarget_ids(i, j) = ids[j]; - Tpaddings(i, j) = 0.0; // padding = false - } - Ttarget_ids(i, id_size) = kEOS; - Tpaddings(i, id_size) = append_eos_ ? 0.0 : 1.0; - actual_maxlen = std::max(actual_maxlen, id_size + 1); - for (int j = id_size + 1; j < maxlen_; ++j) { - Ttoken_ids(i, j) = kEOS; - Ttarget_ids(i, j) = kEOS; - Tpaddings(i, j) = 1.0; // padding = true - } - } - - Tensor out_token_ids(DT_INT32, TensorShape({batch, actual_maxlen})); - Tensor out_target_ids(DT_INT32, TensorShape({batch, actual_maxlen})); - Tensor out_paddings(DT_FLOAT, TensorShape({batch, actual_maxlen})); - - typedef const Eigen::DSizes DSize2; - out_token_ids.matrix() = - Ttoken_ids.slice(DSize2{0, 0}, DSize2{batch, actual_maxlen}); - out_target_ids.matrix() = - Ttarget_ids.slice(DSize2{0, 0}, DSize2{batch, actual_maxlen}); - out_paddings.matrix() = - Tpaddings.slice(DSize2{0, 0}, DSize2{batch, actual_maxlen}); - - OP_REQUIRES_OK(ctx, ctx->set_output("token_ids", out_token_ids)); - OP_REQUIRES_OK(ctx, ctx->set_output("target_ids", out_target_ids)); - OP_REQUIRES_OK(ctx, ctx->set_output("paddings", out_paddings)); - } - - private: - bool append_eos_ = true; - int maxlen_ = 0; - bool pad_to_maxlen_ = true; -}; - -template -class IdToTokenOp : public OpKernel { - public: - explicit IdToTokenOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} - - void Compute(OpKernelContext* ctx) override { - const Tensor& ids = ctx->input(0); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(ids.shape()), - errors::InvalidArgument("token_ids must be a matrix, but get ", - ids.shape().DebugString())); - const Tensor& seq_lens = ctx->input(1); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(seq_lens.shape()), - errors::InvalidArgument("seq_lens must be a vector, but get ", - seq_lens.shape().DebugString())); - const int batch = seq_lens.NumElements(); - OP_REQUIRES(ctx, batch == ids.dim_size(0), - errors::InvalidArgument( - "batch size has to match between token_ids and seq_lens. ", - ids.shape().DebugString(), " vs. ", - seq_lens.shape().DebugString())); - - Tensor* out; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({batch}), &out)); - const auto& t_ids = ids.matrix(); - const auto& t_seq_lens = seq_lens.vec(); - auto t_out = out->template vec(); - for (int i = 0; i < batch; ++i) { - const int len_i = std::max(0, t_seq_lens(i)); - std::vector ids_i(len_i); - for (int j = 0; j < len_i; ++j) { - ids_i[j] = t_ids(i, j); - } - std::vector labels = TokenizerClass::IdToStrings(ids_i); - t_out(i) = TokenizerClass::JoinLabels(labels); - } - } -}; - -} // namespace -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_TOKENIZER_OP_HEADERS_H_ diff --git a/lingvo/core/ops/tokenizer_ops_kernels.cc b/lingvo/core/ops/tokenizer_ops_kernels.cc deleted file mode 100644 index 18d49f80..00000000 --- a/lingvo/core/ops/tokenizer_ops_kernels.cc +++ /dev/null @@ -1,374 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ -#include -#include -#include -#include - -#include "lingvo/core/ops/ascii_tokenizer.h" -#include "lingvo/core/ops/simple_vocab.h" -#include "lingvo/core/ops/tokenizer_op_headers.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/strings/numbers.h" -#include "tensorflow/core/lib/strings/str_util.h" -#include "tensorflow/core/platform/env.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -REGISTER_KERNEL_BUILDER(Name("AsciiToTokenId").Device(DEVICE_CPU), - LabelToTokenIdOp); - -REGISTER_KERNEL_BUILDER(Name("IdToAscii").Device(DEVICE_CPU), - IdToTokenOp); - -class StrToVocabTokensOp : public OpKernel { - public: - explicit StrToVocabTokensOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("append_eos", &append_eos_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("maxlen", &maxlen_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("pad_to_maxlen", &pad_to_maxlen_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("vocab_filepath", &vocab_filepath_)); - bool load_token_ids_from_vocab; - OP_REQUIRES_OK(ctx, ctx->GetAttr("load_token_ids_from_vocab", - &load_token_ids_from_vocab)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("delimiter", &delimiter_)); - CHECK_GT(maxlen_, 0); - OP_REQUIRES_OK(ctx, - vocab_.Load(vocab_filepath_, load_token_ids_from_vocab)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* labels; - OP_REQUIRES_OK(ctx, ctx->input("labels", &labels)); - const auto& t_label = labels->vec(); - const int32 b_size = labels->dim_size(0); - Tensor token_ids(DT_INT32, TensorShape({b_size, maxlen_})); - Tensor target_ids(DT_INT32, TensorShape({b_size, maxlen_})); - Tensor paddings(DT_FLOAT, TensorShape({b_size, maxlen_})); - - auto t_token_ids = token_ids.tensor(); - auto t_target_ids = target_ids.tensor(); - auto t_paddings = paddings.tensor(); - t_token_ids.setZero(); - t_target_ids.setZero(); - t_paddings.setZero(); - - int actual_maxlen = pad_to_maxlen_ ? maxlen_ : 0; - for (int i = 0; i < b_size; ++i) { - t_token_ids(i, 0) = vocab_.sos_id(); - - string label(t_label(i)); - VLOG(1) << "Label " << label; - std::vector tokens; - if (delimiter_.length() > 0) { - tokens = str_util::Split(label, delimiter_, str_util::SkipWhitespace()); - } else { - // Split by the empty delimiter. - for (int i = 0; i < label.size(); ++i) { - tokens.push_back(string(1, label[i])); - } - } - - VLOG(1) << "#Tokens " << tokens.size() << " " - << str_util::Join(tokens, "/"); - int cur_char = 0; - for (const auto& token : tokens) { - const int token_id = vocab_.TokenToId(token); - t_target_ids(i, cur_char) = token_id; - t_paddings(i, cur_char) = 0.0; - // If the number of tokens is longer than the max length - truncate. - if (cur_char + 1 >= maxlen_) { - cur_char++; - LOG(INFO) << "Label: \"" << label << "\" contained " << tokens.size() - << " tokens, and was truncated to size: " << maxlen_ << " (" - << tokens.size() - maxlen_ << " tokens were ignored)."; - break; - } - t_token_ids(i, cur_char + 1) = token_id; - cur_char++; - } - if (cur_char < maxlen_) { - // There was no truncation, t_token_ids is ahead by 1 over t_target_ids - // and t_paddings - t_target_ids(i, cur_char) = vocab_.eos_id(); - t_paddings(i, cur_char) = append_eos_ ? 0.0 : 1.0; - ++cur_char; - } - actual_maxlen = std::max(actual_maxlen, cur_char); - for (; cur_char < maxlen_; ++cur_char) { - t_token_ids(i, cur_char) = vocab_.eos_id(); - t_target_ids(i, cur_char) = vocab_.eos_id(); - t_paddings(i, cur_char) = 1.0; - } - } - - Tensor out_token_ids(DT_INT32, TensorShape({b_size, actual_maxlen})); - Tensor out_target_ids(DT_INT32, TensorShape({b_size, actual_maxlen})); - Tensor out_paddings(DT_FLOAT, TensorShape({b_size, actual_maxlen})); - - typedef const Eigen::DSizes DSize2; - out_token_ids.matrix() = - t_token_ids.slice(DSize2{0, 0}, DSize2{b_size, actual_maxlen}); - out_target_ids.matrix() = - t_target_ids.slice(DSize2{0, 0}, DSize2{b_size, actual_maxlen}); - out_paddings.matrix() = - t_paddings.slice(DSize2{0, 0}, DSize2{b_size, actual_maxlen}); - - OP_REQUIRES_OK(ctx, ctx->set_output("token_ids", out_token_ids)); - OP_REQUIRES_OK(ctx, ctx->set_output("target_ids", out_target_ids)); - OP_REQUIRES_OK(ctx, ctx->set_output("paddings", out_paddings)); - } - - private: - string vocab_filepath_; - bool append_eos_ = true; - int maxlen_ = 0; - bool pad_to_maxlen_ = true; - string delimiter_; - Vocab vocab_; -}; - -REGISTER_KERNEL_BUILDER(Name("StrToVocabTokens").Device(DEVICE_CPU), - StrToVocabTokensOp); - -class NgramIdToTokenOp : public OpKernel { - public: - explicit NgramIdToTokenOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("ngram_vocab_filepath", &vocab_filepath_)); - OP_REQUIRES_OK(ctx, vocab_.Load(vocab_filepath_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("ngram_separator", &ngram_separator_)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* token_ids; - const Tensor* seq_lengths; - OP_REQUIRES_OK(ctx, ctx->input("token_ids", &token_ids)); - OP_REQUIRES_OK(ctx, ctx->input("seq_lengths", &seq_lengths)); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(token_ids->shape()), - errors::InvalidArgument("token_ids must be a matrix, but get ", - token_ids->shape().DebugString())); - OP_REQUIRES( - ctx, TensorShapeUtils::IsVector(seq_lengths->shape()), - errors::InvalidArgument("seq_lengths must be a vector, but get ", - seq_lengths->shape().DebugString())); - const int batch = seq_lengths->NumElements(); - OP_REQUIRES( - ctx, batch == token_ids->dim_size(0), - errors::InvalidArgument("batch size has to match between token_ids and " - "seq_lengths")); - - Tensor* out; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({batch}), &out)); - const auto& t_ids = token_ids->matrix(); - const auto& t_seq_lens = seq_lengths->vec(); - auto t_out = out->template vec(); - for (int i = 0; i < batch; ++i) { - const int len_i = std::max(0, t_seq_lens(i)); - std::vector ids_i(len_i); - for (int j = 0; j < len_i; ++j) { - ids_i[j] = t_ids(i, j); - } - auto labels = vocab_.IdsToTokens(ids_i); - t_out(i) = str_util::Join(labels, ngram_separator_.c_str()); - } - } - - private: - string vocab_filepath_; - Vocab vocab_; - string ngram_separator_; -}; - -REGISTER_KERNEL_BUILDER(Name("NgramIdToToken").Device(DEVICE_CPU), - NgramIdToTokenOp); - -class BpeIdsToWordsOp : public OpKernel { - public: - explicit BpeIdsToWordsOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("vocab_filepath", &vocab_filepath_)); - string contents; - OP_REQUIRES_OK(ctx, ReadFileToString( - Env::Default(), vocab_filepath_, &contents)); - std::vector lines = str_util::Split(contents, '\n', - str_util::SkipEmpty()); - for (const string& line : lines) { - std::vector parts = str_util::Split(line, ' '); - id_to_string_map_.push_back(parts[0]); - } - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* token_ids; - const Tensor* seq_lengths; - OP_REQUIRES_OK(ctx, ctx->input("token_ids", &token_ids)); - OP_REQUIRES_OK(ctx, ctx->input("seq_lengths", &seq_lengths)); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(token_ids->shape()), - errors::InvalidArgument("token_ids must be a matrix, but get ", - token_ids->shape().DebugString())); - OP_REQUIRES( - ctx, TensorShapeUtils::IsVector(seq_lengths->shape()), - errors::InvalidArgument("seq_lengths must be a vector, but get ", - seq_lengths->shape().DebugString())); - const int batch = seq_lengths->NumElements(); - OP_REQUIRES( - ctx, batch == token_ids->dim_size(0), - errors::InvalidArgument("batch size has to match between token_ids and " - "seq_lengths")); - - Tensor* out; - OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({batch}), &out)); - const auto& t_ids = token_ids->matrix(); - const auto& t_seq_lens = seq_lengths->vec(); - auto t_out = out->template vec(); - for (int i = 0; i < batch; ++i) { - const int len_i = std::max(0, t_seq_lens(i)); - std::vector labels; - for (int j = 0; j < len_i; ++j) { - string label = id_to_string_map_[t_ids(i, j)]; - std::size_t pos = label.find("@@"); - if (pos == std::string::npos) - label = label + " "; - else - label.erase(pos, 2); - labels.push_back(label); - } - t_out(i) = str_util::Join(labels, ""); - } - } - - private: - string vocab_filepath_; - std::vector id_to_string_map_; -}; - -REGISTER_KERNEL_BUILDER(Name("BpeIdsToWords").Device(DEVICE_CPU), - BpeIdsToWordsOp); - -class BpeWordsToIdsOp : public OpKernel { - public: - explicit BpeWordsToIdsOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("append_eos", &append_eos_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("maxlen", &maxlen_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("tokenization_filepath", - &tokenization_filepath_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("sos_id", &sos_id_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("eos_id", &eos_id_)); - CHECK_GT(maxlen_, 0); - string contents; - OP_REQUIRES_OK(ctx, ReadFileToString(Env::Default(), tokenization_filepath_, - &contents)); - std::vector lines = str_util::Split(contents, '\n', - str_util::SkipEmpty()); - for (const string& line : lines) { - // Each line: - // string int1,int2,int3,...,intn - std::vector parts = str_util::Split(line, ' '); - std::vector split_parts_1 = str_util::Split(parts[1], ','); - std::vector ids; - for (const string& str_id : split_parts_1) { - int32 id; - strings::safe_strto32(str_id, &id); - ids.push_back(id); - } - string_to_ids_map_[parts[0]] = ids; - } - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* labels; - OP_REQUIRES_OK(ctx, ctx->input("labels", &labels)); - const auto& t_label = labels->vec(); - const int32 b_size = labels->dim_size(0); - Tensor* token_ids = nullptr; - Tensor* target_ids = nullptr; - Tensor* paddings = nullptr; - OP_REQUIRES_OK( - ctx, ctx->allocate_output("token_ids", TensorShape({b_size, maxlen_}), - &token_ids)); - OP_REQUIRES_OK( - ctx, ctx->allocate_output("target_ids", TensorShape({b_size, maxlen_}), - &target_ids)); - OP_REQUIRES_OK( - ctx, ctx->allocate_output("paddings", TensorShape({b_size, maxlen_}), - &paddings)); - - token_ids->flat().setConstant(eos_id_); - target_ids->flat().setConstant(eos_id_); - paddings->flat().setConstant(1.0f); - auto t_token_ids = token_ids->tensor(); - auto t_target_ids = target_ids->tensor(); - auto t_paddings = paddings->tensor(); - - for (int i = 0; i < b_size; ++i) { - t_token_ids(i, 0) = sos_id_; - - string label(t_label(i)); - VLOG(1) << "Label " << label; - std::vector tokens = - str_util::Split(label, ' ', str_util::SkipWhitespace()); - VLOG(1) << "#Tokens " << tokens.size() << " " - << str_util::Join(tokens, "/"); - int cur_char = 0; - for (const auto& token : tokens) { - if (cur_char >= maxlen_) { - break; - } - const std::vector token_ids = string_to_ids_map_[token]; - for (const auto& token_id : token_ids) { - t_target_ids(i, cur_char) = token_id; - t_paddings(i, cur_char) = 0.0f; - // If the number of tokens is longer than the max length - truncate. - if (cur_char + 1 >= maxlen_) { - cur_char++; - int num_token_ids = 0; - for (const auto& t : tokens) { - num_token_ids += string_to_ids_map_[t].size(); - } - LOG(INFO) << "Label: \"" << label << "\" had " << num_token_ids - << " tokens, and was truncated to size: " << maxlen_ - << " (" << num_token_ids - maxlen_ << " tokens ignored)."; - break; - } - t_token_ids(i, cur_char + 1) = token_id; - cur_char++; - } - } - if (cur_char < maxlen_) { - // There was no truncation, t_token_ids is ahead by 1 over t_target_ids - // and t_paddings - t_target_ids(i, cur_char) = eos_id_; - t_paddings(i, cur_char) = append_eos_ ? 0.0f : 1.0f; - } - } - } - - private: - string tokenization_filepath_; - bool append_eos_ = true; - int maxlen_ = 0; - int sos_id_ = 1; - int eos_id_ = 2; - std::unordered_map > string_to_ids_map_; -}; - -REGISTER_KERNEL_BUILDER(Name("BpeWordsToIds").Device(DEVICE_CPU), - BpeWordsToIdsOp); -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/tokenizer_ops_test.py b/lingvo/core/ops/tokenizer_ops_test.py deleted file mode 100644 index 1cc64f31..00000000 --- a/lingvo/core/ops/tokenizer_ops_test.py +++ /dev/null @@ -1,336 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for tokenizer_ops.""" - -from lingvo import compat as tf -from lingvo.core import ops -from lingvo.core import test_helper -from lingvo.core import test_utils -import six - - -class TokenizerOpsTest(test_utils.TestCase): - - def testLabelsToTokenId(self): - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.ascii_to_token_id([ - 'hElLo', 'sIr', 'What a day', 'america\'s', - ' early', '1:00 AM', 'morning' - ], - append_eos=True, - maxlen=10)) - self.assertAllEqual(token_ids, [ - [1, 12, 9, 16, 16, 19, 2, 2, 2, 2], - [1, 23, 13, 22, 73, 2, 2, 2, 2, 2], - [1, 27, 12, 5, 24, 3, 5, 3, 0, 3], - [1, 5, 17, 9, 22, 13, 7, 5, 32, 23], - [1, 4, 3, 9, 5, 22, 16, 29, 2, 2], - [1, 40, 34, 39, 39, 3, 5, 17, 2, 2], - [1, 74, 17, 19, 22, 18, 13, 18, 11, 2], - ]) - self.assertAllEqual( - target_ids, - [[12, 9, 16, 16, 19, 2, 2, 2, 2, 2], [23, 13, 22, 73, 2, 2, 2, 2, 2, 2], - [27, 12, 5, 24, 3, 5, 3, 0, 3, 2], [5, 17, 9, 22, 13, 7, 5, 32, 23, 2], - [4, 3, 9, 5, 22, 16, 29, 2, 2, 2], [40, 34, 39, 39, 3, 5, 17, 2, 2, 2], - [74, 17, 19, 22, 18, 13, 18, 11, 2, 2]]) - self.assertAllEqual( - paddings, - [[0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]) - - def testLabelsToTokenIdAppendEOSFalse(self): - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.ascii_to_token_id([ - 'hElLo', 'sIr', 'What a day', 'america\'s', - ' early', '1:00 AM', '100%' - ], - append_eos=False, - maxlen=10)) - self.assertAllEqual( - token_ids, - [[1, 12, 9, 16, 16, 19, 2, 2, 2, 2], [1, 23, 13, 22, 73, 2, 2, 2, 2, 2], - [1, 27, 12, 5, 24, 3, 5, 3, 0, 3], [1, 5, 17, 9, 22, 13, 7, 5, 32, 23], - [1, 4, 3, 9, 5, 22, 16, 29, 2, 2], [1, 40, 34, 39, 39, 3, 5, 17, 2, 2], - [1, 40, 39, 39, 52, 2, 2, 2, 2, 2]]) - self.assertAllEqual( - target_ids, - [[12, 9, 16, 16, 19, 2, 2, 2, 2, 2], [23, 13, 22, 73, 2, 2, 2, 2, 2, 2], - [27, 12, 5, 24, 3, 5, 3, 0, 3, 2], [5, 17, 9, 22, 13, 7, 5, 32, 23, 2], - [4, 3, 9, 5, 22, 16, 29, 2, 2, 2], [40, 34, 39, 39, 3, 5, 17, 2, 2, 2], - [40, 39, 39, 52, 2, 2, 2, 2, 2, 2]]) - self.assertAllEqual( - paddings, - [[0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1], [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - ], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1]]) - - def testLabelsToTokenIdNoPadToMaxlen(self): - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.ascii_to_token_id([ - 'hElLo', 'sIr', 'What a day', 'america\'s', - ' early', '1:00 AM', 'morning' - ], - append_eos=True, - maxlen=20, - pad_to_maxlen=False)) - self.assertAllEqual(token_ids, [ - [1, 12, 9, 16, 16, 19, 2, 2, 2, 2, 2, 2, 2], - [1, 23, 13, 22, 73, 2, 2, 2, 2, 2, 2, 2, 2], - [1, 27, 12, 5, 24, 3, 5, 3, 0, 3, 8, 5, 29], - [1, 5, 17, 9, 22, 13, 7, 5, 32, 23, 2, 2, 2], - [1, 4, 3, 9, 5, 22, 16, 29, 2, 2, 2, 2, 2], - [1, 40, 34, 39, 39, 3, 5, 17, 2, 2, 2, 2, 2], - [1, 74, 17, 19, 22, 18, 13, 18, 11, 2, 2, 2, 2], - ]) - self.assertAllEqual(target_ids, [ - [12, 9, 16, 16, 19, 2, 2, 2, 2, 2, 2, 2, 2], - [23, 13, 22, 73, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [27, 12, 5, 24, 3, 5, 3, 0, 3, 8, 5, 29, 2], - [5, 17, 9, 22, 13, 7, 5, 32, 23, 2, 2, 2, 2], - [4, 3, 9, 5, 22, 16, 29, 2, 2, 2, 2, 2, 2], - [40, 34, 39, 39, 3, 5, 17, 2, 2, 2, 2, 2, 2], - [74, 17, 19, 22, 18, 13, 18, 11, 2, 2, 2, 2, 2], - ]) - self.assertAllEqual(paddings, [ - [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], - ]) - - def testIdToToken(self): - with self.session(use_gpu=False): - token_ids = [[12, 9, 16, 16, 19, 2, 2, 2, 2, - 2], [23, 13, 22, 73, 2, 2, 2, 2, 2, - 2], [27, 12, 5, 24, 3, 5, 3, 0, 3, - 2], [5, 17, 9, 22, 13, 7, 5, 32, 23, 2], - [4, 3, 9, 5, 22, 16, 29, 2, 2, - 2], [40, 34, 39, 39, 3, 5, 17, 2, 2, - 2], [52, 2, 2, 2, 2, 2, 2, 2, 2, 2]] - seq_lens = [5, 4, 9, 9, 7, 7, 1] - tokens = self.evaluate(ops.id_to_ascii(token_ids, seq_lens)) - - self.assertEqual(tokens.tolist(), [ - b'hello', b'sir', b'what a ', b"america's", - b' early', b'1:00 am', b'%' - ]) - - def testStrToVocabToken(self): - vocab = test_helper.test_src_dir_path('core/ops/testdata/test_vocab.txt') - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.str_to_vocab_tokens([ - 'a b c d e', - ' ', - 'øut über ♣ 愤青 ←', - ], - append_eos=True, - maxlen=10, - vocab_filepath=vocab)) - self.assertEqual( - token_ids.tolist(), - [[1, 5, 6, 7, 8, 9, 2, 2, 2, 2], [1, 0, 1, 2, 3, 2, 2, 2, 2, 2], - [1, 10, 11, 12, 13, 3, 2, 2, 2, 2]]) - self.assertEqual( - target_ids.tolist(), - [[5, 6, 7, 8, 9, 2, 2, 2, 2, 2], [0, 1, 2, 3, 2, 2, 2, 2, 2, 2], - [10, 11, 12, 13, 3, 2, 2, 2, 2, 2]]) - self.assertEqual(paddings.tolist(), - [[0., 0., 0., 0., 0., 0., 1., 1., 1., 1.], [ - 0., 0., 0., 0., 0., 1., 1., 1., 1., 1. - ], [0., 0., 0., 0., 0., 0., 1., 1., 1., 1.]]) - - def testStrToVocabTokenAppendEOSFalse(self): - vocab = test_helper.test_src_dir_path('core/ops/testdata/test_vocab.txt') - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.str_to_vocab_tokens([ - 'a b c d e', - ' ', - 'øut über ♣ 愤青 ←', - ], - append_eos=False, - maxlen=10, - vocab_filepath=vocab)) - self.assertEqual( - token_ids.tolist(), - [[1, 5, 6, 7, 8, 9, 2, 2, 2, 2], [1, 0, 1, 2, 3, 2, 2, 2, 2, 2], - [1, 10, 11, 12, 13, 3, 2, 2, 2, 2]]) - self.assertEqual( - target_ids.tolist(), - [[5, 6, 7, 8, 9, 2, 2, 2, 2, 2], [0, 1, 2, 3, 2, 2, 2, 2, 2, 2], - [10, 11, 12, 13, 3, 2, 2, 2, 2, 2]]) - self.assertEqual(paddings.tolist(), - [[0., 0., 0., 0., 0., 1., 1., 1., 1., 1.], [ - 0., 0., 0., 0., 1., 1., 1., 1., 1., 1. - ], [0., 0., 0., 0., 0., 1., 1., 1., 1., 1.]]) - - def testStrToVocabTokenTruncates(self): - vocab = test_helper.test_src_dir_path('core/ops/testdata/test_vocab.txt') - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.str_to_vocab_tokens(['a b c d e ' * 1000], - append_eos=True, - maxlen=5, - vocab_filepath=vocab)) - self.assertEqual(token_ids.tolist(), [[1, 5, 6, 7, 8]]) - self.assertEqual(target_ids.tolist(), [[5, 6, 7, 8, 9]]) - self.assertEqual(paddings.tolist(), [[0., 0., 0., 0., 0.]]) - - def testStrToVocabTokenNoPadToMaxlen(self): - vocab = test_helper.test_src_dir_path('core/ops/testdata/test_vocab.txt') - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.str_to_vocab_tokens([ - 'a b c d e', - ' ', - 'øut über ♣ 愤青 ←', - ], - append_eos=True, - maxlen=10, - pad_to_maxlen=False, - vocab_filepath=vocab)) - self.assertEqual( - token_ids.tolist(), - [[1, 5, 6, 7, 8, 9], [1, 0, 1, 2, 3, 2], [1, 10, 11, 12, 13, 3]]) - self.assertEqual( - target_ids.tolist(), - [[5, 6, 7, 8, 9, 2], [0, 1, 2, 3, 2, 2], [10, 11, 12, 13, 3, 2]]) - self.assertEqual(paddings.tolist(), - [[0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 1.], - [0., 0., 0., 0., 0., 0.]]) - - def testStrToVocabTokenCustomDelimiter(self): - custom_delimiter = '_' - vocab = test_helper.test_src_dir_path('core/ops/testdata/test_vocab.txt') - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.str_to_vocab_tokens([custom_delimiter.join('abcde')], - append_eos=True, - maxlen=8, - vocab_filepath=vocab, - delimiter=custom_delimiter)) - self.assertEqual(token_ids.tolist(), [[1, 5, 6, 7, 8, 9, 2, 2]]) - self.assertEqual(target_ids.tolist(), [[5, 6, 7, 8, 9, 2, 2, 2]]) - self.assertEqual(paddings.tolist(), [[0., 0., 0., 0., 0., 0., 1., 1.]]) - - def testStrToVocabTokenSplitToCharacters(self): - custom_delimiter = '' - vocab = test_helper.test_src_dir_path('core/ops/testdata/test_vocab.txt') - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - ops.str_to_vocab_tokens(['abcde'], - append_eos=True, - maxlen=8, - vocab_filepath=vocab, - delimiter=custom_delimiter)) - self.assertEqual(token_ids.tolist(), [[1, 5, 6, 7, 8, 9, 2, 2]]) - self.assertEqual(target_ids.tolist(), [[5, 6, 7, 8, 9, 2, 2, 2]]) - self.assertEqual(paddings.tolist(), [[0., 0., 0., 0., 0., 0., 1., 1.]]) - - def testNgramIdToToken(self): - vocab = test_helper.test_src_dir_path('core/ops/testdata/test_ngrams.txt') - with self.session(use_gpu=False): - ngram_ids = [[14, 11, 6, 24, 7, 3, 13, 82, 2, 2], - [57, 3, 73, 17, 22, 9, 2, 2, 2, 2]] - lengths = [8, 6] - scripts = ops.ngram_id_to_token( - ngram_ids, lengths, ngram_vocab_filepath=vocab) - scripts_expected = [b'pn?o"{twe', b'gh{rtlcr'] - self.assertEqual(scripts_expected, scripts.eval().tolist()) - - def testMlPerf(self): - tf.logging.info(dir(ops)) - vocab = test_helper.test_src_dir_path( - 'core/ops/testdata/mlperf.ende.subwords.vocab') - with self.session(use_gpu=False): - lengths = [26, 33] - inputs = [[ - 264, 4, 288, 189, 4138, 30725, 14, 1461, 2761, 243, 28, 2692, 9, 1679, - 2, 1218, 9, 4, 15190, 7, 427, 497, 7, 147, 3, 1, 0, 0, 0, 0, 0, 0, 0 - ], - [ - 275, 52, 11, 1434, 215, 30372, 1546, 6997, 6495, 291, 802, - 10, 9986, 6, 10, 1857, 12726, 13624, 2, 53, 18, 87, 15934, - 5618, 12321, 127, 1565, 4209, 885, 777, 335, 3, 1 - ]] - expected = [ - six.ensure_binary( - 'If the market “misbehaves,” farmers could be reduced to poverty,' - ' leading to the neglect of large areas of Europe.'), - six.ensure_binary( - 'Wenn sich der Markt „daneben benimmt“ könnten die Bauern in die ' - 'Armut abgleiten, was zu einer Vernachlässigung großer Teile ' - 'Europas führen würde.') - ] - decode = ops.ml_perf_subword_id_to_string( - inputs, lengths, vocab_filepath=vocab) - self.assertEqual(expected, decode.eval().tolist()) - - def testNgramIdToTokenSeparator(self): - vocab = test_helper.test_src_dir_path('core/ops/testdata/test_ngrams.txt') - with self.session(use_gpu=False): - ngram_ids = [[14, 11, 6, 24, 7, 3, 13, 82, 2, 2], - [57, 3, 73, 17, 22, 9, 2, 2, 2, 2]] - lengths = [8, 6] - scripts = ops.ngram_id_to_token( - ngram_ids, lengths, ngram_vocab_filepath=vocab, ngram_separator='.') - scripts_expected = [b'p.n.?.o.".{.t.we', b'gh.{.rt.l.c.r'] - self.assertEqual(scripts_expected, scripts.eval().tolist()) - - def testBpeTokenization(self): - word_vocab = test_helper.test_src_dir_path( - 'core/ops/testdata/bpe_words.vocab') - code_vocab = test_helper.test_src_dir_path( - 'core/ops/testdata/bpe_codes.vocab') - sentences = [ - 'GIVE ME A PENNY', 'THEY LIVED ALONE', 'THEY GIVE ME A PENNY ALONE' - ] - expected_sentences = [ - b'GIVE ME A PENNY ', - b'THEY LIVED ALONE ', - b'THEY GIVE ME A PENNY ', - ] - expected_token_ids = [ - [27, 9, 30, 14, 28, 14, 52, 11, 4, 6, 6, 10, 2, 2, 2], - [16, 4, 10, 12, 9, 30, 24, 7, 12, 49, 14, 2, 2, 2, 2], - [16, 4, 10, 27, 9, 30, 14, 28, 14, 52, 11, 4, 6, 6, 10], - ] - with self.session(use_gpu=False): - label_tensor = tf.constant(sentences) - _, token_ids, paddings = ops.bpe_words_to_ids( - label_tensor, tokenization_filepath=word_vocab, maxlen=15) - seq_lens = tf.cast( - tf.round(tf.reduce_sum(1 - paddings, axis=-1)), tf.int32) - - target_string = ops.bpe_ids_to_words( - token_ids, seq_lengths=seq_lens, vocab_filepath=code_vocab) - self.assertEqual(expected_sentences, target_string.eval().tolist()) - self.assertEqual(expected_token_ids, token_ids.eval().tolist()) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/ops/versioned_file_set.proto b/lingvo/core/ops/versioned_file_set.proto deleted file mode 100644 index d9c30ad2..00000000 --- a/lingvo/core/ops/versioned_file_set.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto2"; - -package tensorflow.lingvo; -option cc_enable_arenas = true; - -// Protocol buffer representing the data version. -message VersionedFileSet { - // The latest version of this fileset. - optional FileSet current = 1; - - // A list of recent past versions of this fileset. - repeated FileSet history = 2; -} - -message FileSet { - // Each file_pattern is a file glob pattern or a sharded file spec - // file_pattern is intended to store a relative path, to enable moving - // datasets around without having to update the pbtxt file storing the - // VersionedFileSet information. - repeated string file_pattern = 1; - - // Seconds since epoch indicating when this version of fileset is created. - optional double create_timestamp = 2; -} diff --git a/lingvo/core/ops/weighted_mix_record_yielder.cc b/lingvo/core/ops/weighted_mix_record_yielder.cc deleted file mode 100644 index 341e8534..00000000 --- a/lingvo/core/ops/weighted_mix_record_yielder.cc +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/weighted_mix_record_yielder.h" - -#include - -namespace tensorflow { -namespace lingvo { - -WeightedMixRecordYielder::WeightedMixRecordYielder( - const int64 seed, const std::vector& yielders, - const std::vector& input_source_weights) - : rnd_(seed), - sample_distribution_(input_source_weights.begin(), - input_source_weights.end()), - yielders_(yielders) { - if (yielders.size() != input_source_weights.size()) { - LOG(FATAL) << "Unable to create WeightedMixRecordYielder: every yielder " - << "should have a corresponding weight. " << yielders.size() - << " yielders and " << input_source_weights.size() - << " weights were " - << "provided."; - } - if (yielders.empty()) { - LOG(FATAL) << "There should be at least one yielder provided."; - } - - for (float x : input_source_weights) { - if (x < 0) { - LOG(FATAL) << "All weights should be greater or equal to zero. Got " << x; - } - } -} - -WeightedMixRecordYielder* WeightedMixRecordYielder::New( - const int64 seed, const std::vector& yielders, - const std::vector& input_source_weights) { - WeightedMixRecordYielder* yielder = - new WeightedMixRecordYielder(seed, yielders, input_source_weights); - return yielder; -} - -WeightedMixRecordYielder::~WeightedMixRecordYielder() {} - -void WeightedMixRecordYielder::Close() { - for (RecordYielder* yielder : yielders_) { - yielder->Close(); - } - LOG(INFO) << this << "Weighted mix record yielder exit"; - delete this; -} - -Status WeightedMixRecordYielder::Yield(Record* record) { - size_t yielder_idx = 0; - { - absl::MutexLock l(&mu_); - yielder_idx = sample_distribution_(rnd_); - // Release the lock immediately once we fix which yielder to use. - } - while (true) { - // Retry indefinitely until we get an Ok status from the specific yielder. - // This will stall the training if there is any unrecoverable error with - // the child yielder. - Status s = yielders_.at(yielder_idx)->Yield(record); - if (!s.ok()) { - LOG(WARNING) << s; - continue; - } - return s; - } -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/weighted_mix_record_yielder.h b/lingvo/core/ops/weighted_mix_record_yielder.h deleted file mode 100644 index 19f11b8c..00000000 --- a/lingvo/core/ops/weighted_mix_record_yielder.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ -#ifndef LINGVO_CORE_OPS_WEIGHTED_MIX_RECORD_YIELDER_H_ -#define LINGVO_CORE_OPS_WEIGHTED_MIX_RECORD_YIELDER_H_ - -#include "lingvo/core/ops/record_yielder.h" - -namespace tensorflow { -namespace lingvo { - -// WeightedMixRecordYielder is a special RecordYielder that mixes examples from -// yielders in a random order maintaining the mix ratio specified by -// input_source_weights. -// -// Usage example: -// BasicRecordYielder::Options opts1; -// opts1.file_pattern = ; -// opts1.seed = 301; -// opts1.bufsize = 1000000; // A randomized buffer with 1M records. -// opts1.parallelism = 8; // Use 8 iterators. -// RecordYielder* yielder1 = BasicRecordYielder::New(opts1); -// BasicRecordYielder::Options opts2; -// opts2.file_pattern = ; -// opts2.seed = 301; -// opts2.bufsize = 1000000; // A randomized buffer with 1M records. -// opts2.parallelism = 8; // Use 8 iterators. -// RecordYielder* yielder2 = BasicRecordYielder::New(opts2); -// // Use records from yielder1 for 70% of the yields. -// WeightedMixRecordYielder* yielder = WeightedMixRecordYielder::New( -// opts1.seed, {yielder1, yielder2}, {0.7, 0.3}); -// Record record; -// while (true) { -// yielder->Yield(&record); -// // process record. -// } -// yielder->Close(); -// -// WeightedMixRecordYielder can be accessed by multiple threads concurrently. -class WeightedMixRecordYielder : public RecordYielder { - public: - ~WeightedMixRecordYielder() override; - void Close() override; - Status Yield(Record* record) override; - - // Creates new WeightedMixRecordYielder and takes ownership over yielders - // provided. Those yielders should be properly initialized already and will be - // closed once WeightedMixRecordYielder is closed. Caller is responsible - // closing the WeightedMixRecordYielder returned by this function. Caller - // should not delete the yielder as it will be handled internally. - static WeightedMixRecordYielder* New( - const int64 seed, - const std::vector& yielders, - const std::vector& input_source_weights); - - protected: - WeightedMixRecordYielder( - const int64 seed, - const std::vector& yielders, - const std::vector& input_source_weights); - - - private: - mutable absl::Mutex mu_; - - // PRG used for randomization. - std::mt19937_64 rnd_ ABSL_GUARDED_BY(mu_); - - std::discrete_distribution sample_distribution_; - - // A list of child yielders used as an input to the mixer. - std::vector yielders_; -}; - -} // namespace lingvo -} // namespace tensorflow -#endif // LINGVO_CORE_OPS_WEIGHTED_MIX_RECORD_YIELDER_H_ diff --git a/lingvo/core/ops/weighted_mix_record_yielder_test.cc b/lingvo/core/ops/weighted_mix_record_yielder_test.cc deleted file mode 100644 index f0e7bfb9..00000000 --- a/lingvo/core/ops/weighted_mix_record_yielder_test.cc +++ /dev/null @@ -1,241 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/core/ops/weighted_mix_record_yielder.h" - -#include - -#include -#include -#include "lingvo/core/ops/input_common.h" -#include "lingvo/core/ops/record_yielder.h" -#include "lingvo/core/ops/yielder_test_helper.h" -#include "tensorflow/core/lib/core/stringpiece.h" -#include "tensorflow/core/lib/io/path.h" -#include "tensorflow/core/platform/env.h" - -namespace tensorflow { -namespace lingvo { - -TEST(RecordYielderTest, WeightedMixerBasicTest) { - const int N = 10; - const int M = 1000; - GeneratePlainTextTestData("yielder1", N, M); - GeneratePlainTextTestData("yielder2", N, M); - - BasicRecordYielder::Options opts1; - opts1.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "yielder1.*")); - opts1.seed = 301; - opts1.bufsize = 2000; - opts1.parallelism = 1; - BasicRecordYielder* yielder1 = BasicRecordYielder::New(opts1); - - BasicRecordYielder::Options opts2; - opts2.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "yielder2.*")); - opts2.seed = 301; - opts2.bufsize = 2000; - opts2.parallelism = 1; - BasicRecordYielder* yielder2 = BasicRecordYielder::New(opts2); - WeightedMixRecordYielder* yielder = - WeightedMixRecordYielder::New(301, {yielder1, yielder2}, {0.5, 0.5}); - - std::vector vals; - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < 2 * N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - } - - auto input_source_distribution = ComputeInputSourceDistribution(vals); - ASSERT_NEAR(input_source_distribution["yielder1"], 0.5, 0.01); - ASSERT_NEAR(input_source_distribution["yielder2"], 0.5, 0.01); - - std::sort(vals.begin(), vals.end()); - auto new_end = std::unique(vals.begin(), vals.end()); - - // Duplicates should be rare in the epoch thanks to 0.5/0.5 mix and equally - // sized input sources. - ASSERT_LT(vals.end() - new_end, vals.size() / 100); - - yielder->Close(); -} - -TEST(RecordYielderTest, WeightedMixerUnevenMixTest) { - const int N = 10; - const int M = 1000; - GeneratePlainTextTestData("yielder1", N, M); - GeneratePlainTextTestData("yielder2", N, M); - - BasicRecordYielder::Options opts1; - opts1.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "yielder1.*")); - opts1.seed = 301; - opts1.bufsize = 2000; - opts1.parallelism = 1; - opts1.source_id = 0; - BasicRecordYielder* yielder1 = BasicRecordYielder::New(opts1); - - BasicRecordYielder::Options opts2; - opts2.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "yielder2.*")); - opts2.seed = 301; - opts2.bufsize = 2000; - opts2.parallelism = 1; - opts2.source_id = 1; - BasicRecordYielder* yielder2 = BasicRecordYielder::New(opts2); - WeightedMixRecordYielder* yielder = - WeightedMixRecordYielder::New(301, {yielder1, yielder2}, {0.3, 0.7}); - - std::vector vals; - std::vector source_ids; - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < 2 * N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - source_ids.emplace_back(record.source_id); - } - - auto input_source_distribution = ComputeInputSourceDistribution(vals); - ASSERT_NEAR(input_source_distribution["yielder1"], 0.3, 0.01); - ASSERT_NEAR(input_source_distribution["yielder2"], 0.7, 0.01); - - int32 sum_of_elems = std::accumulate(source_ids.begin(), source_ids.end(), 0); - float ratio = (float)(sum_of_elems) / (float)(source_ids.size()); - ASSERT_NEAR(ratio, 0.7, 0.01); - - // Take couple 1024-sized batches from the vals, they should have roughly the - // same distribution. - for (int i = 0; i < 5; ++i) { - auto batch_input_source_distribution = - ComputeInputSourceDistribution(std::vector( - vals.begin() + i * 1024, vals.begin() + (i + 1) * 1024)); - ASSERT_NEAR(input_source_distribution["yielder1"], 0.3, 0.01); - ASSERT_NEAR(input_source_distribution["yielder2"], 0.7, 0.01); - } - - yielder->Close(); -} - -TEST(RecordYielderTest, WeightedMixerUnevenInputSourcesTest) { - const int N = 10; - const int M = 1000; - GeneratePlainTextTestData("yielder1", N, M); - // Second input source is 4 times larger than the first one. - GeneratePlainTextTestData("yielder2", 4 * N, M); - - BasicRecordYielder::Options opts1; - opts1.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "yielder1.*")); - opts1.seed = 301; - opts1.bufsize = 2000; - opts1.parallelism = 1; - BasicRecordYielder* yielder1 = BasicRecordYielder::New(opts1); - - BasicRecordYielder::Options opts2; - opts2.file_pattern = - strings::StrCat("text:", io::JoinPath("/tmp", "yielder2.*")); - opts2.seed = 301; - opts2.bufsize = 2000; - opts2.parallelism = 1; - BasicRecordYielder* yielder2 = BasicRecordYielder::New(opts2); - WeightedMixRecordYielder* yielder = - WeightedMixRecordYielder::New(301, {yielder1, yielder2}, {0.5, 0.5}); - - std::vector vals; - Record record; - record.source_id = kDefaultSourceId; - // Iterate 8 times the total record count. - for (int i = 0; i < 8 * 5 * N * M; ++i) { - TF_CHECK_OK(yielder->Yield(&record)); - VLOG(1) << i << " " << record.value; - vals.emplace_back(string(record.value)); - } - auto input_source_distribution = ComputeInputSourceDistribution(vals); - ASSERT_NEAR(input_source_distribution["yielder1"], 0.5, 0.01); - ASSERT_NEAR(input_source_distribution["yielder2"], 0.5, 0.01); - - // Each child yielder will be called half the time to maintain 50/50% split. - // So out of (8 * 5 * N * M) total yields (4 * 5 * N * M) will be yielded by - // each yielder. - // Second yielder produces 4 * N * M elements per epoch so it should be at the - // end of the 5th epoch | start of the 6th epoch. - EXPECT_TRUE(yielder2->current_epoch() == 5 || yielder2->current_epoch() == 6); - // First yielder produces 4 times less elements per epoch (N * M) so it - // should be at the end of the 20th epoch | start of the 21st epoch. - EXPECT_TRUE(yielder1->current_epoch() == 20 || - yielder1->current_epoch() == 21); - yielder->Close(); -} - -TEST(RecordYielderTest, RecordYielderRetryLoop) { - MockRecordYielder yielder1; - MockRecordYielder yielder2; - // Yielder1 always returns OK. Yielder2 returns DEADLINE_EXCEEDED 3 times in a - // row and then returns OK. - // Each of them yields max of 5 records and then saturates. - EXPECT_CALL(yielder1, Yield(testing::_)) - .Times(5) - .WillRepeatedly(testing::Return(Status::OK())); - EXPECT_CALL(yielder2, Yield(testing::_)) - .Times(5) - .WillRepeatedly(testing::Return(Status::OK())); - EXPECT_CALL(yielder2, Yield(testing::_)) - .Times(3) - .WillRepeatedly(testing::Return(Status(error::DEADLINE_EXCEEDED, ""))) - .RetiresOnSaturation(); - - WeightedMixRecordYielder* yielder = - WeightedMixRecordYielder::New(304, {&yielder1, &yielder2}, {0.5, 0.5}); - - Record record; - record.source_id = kDefaultSourceId; - for (int i = 0; i < 10; ++i) { - // Thanks to the seed selected every child yielder will be selected exactly - // 5 times. - TF_CHECK_OK(yielder->Yield(&record)); - } - EXPECT_CALL(yielder1, Close()); - EXPECT_CALL(yielder2, Close()); - yielder->Close(); -} - -TEST(RecordYielderDeathTest, WeightedMixerInconsistentYieldersAndWeights) { - RecordYielder* yielder1 = nullptr; // won't ever be used. - RecordYielder* yielder2 = nullptr; // won't ever be used. - ASSERT_DEATH(WeightedMixRecordYielder::New(301, {yielder1, yielder2}, {0.5}), - "2 yielders and 1 weights were provided"); -} - -TEST(RecordYielderDeathTest, WeightedMixerNoYielders) { - ASSERT_DEATH(WeightedMixRecordYielder::New(301, {}, {}), - "There should be at least one yielder provided"); -} - -TEST(RecordYielderDeathTest, WeightedMixerNegativeWeights) { - RecordYielder* yielder1 = nullptr; // won't ever be used. - RecordYielder* yielder2 = nullptr; // won't ever be used. - ASSERT_DEATH( - WeightedMixRecordYielder::New(301, {yielder1, yielder2}, {0.3, -0.1}), - "All weights should be greater or equal to zero. Got -0.1"); -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/x_ops.cc b/lingvo/core/ops/x_ops.cc deleted file mode 100644 index a5def996..00000000 --- a/lingvo/core/ops/x_ops.cc +++ /dev/null @@ -1,1027 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "tensorflow/core/framework/common_shape_fns.h" -#include "tensorflow/core/framework/op.h" -#include "tensorflow/core/framework/shape_inference.h" -#include "tensorflow/core/framework/types.pb.h" -#include "x_ops_helper.h" - -namespace tensorflow { -namespace { - -REGISTER_OP("AssertShapeMatch") - .Input("x: int32") - .Input("y: int32") - .Attr("msg: string = ''") - .SetShapeFn([](shape_inference::InferenceContext* c) { - return Status::OK(); - }) - .Doc(R"doc( -Asserts that shape vector x and y matches. - -The i-th dimension matches iff x[i] == y[i] || x[i] == -1 || y[i] == -1. - -x: A shape vector. -y: A shape vector. -msg: The error message generated when the assertion failed. -)doc"); - -REGISTER_OP("AssertSameDim0") - .Input("x: types") - .Attr("msg: string = ''") - .Attr("types: list(type)") - .SetShapeFn([](shape_inference::InferenceContext* c) { - return Status::OK(); - }) - .Doc(R"doc( -Asserts that all input tensors are non-scalar and have the same 0-th dim size. - -x: The list of tensors. -msg: The error message generated when the assertion failed. -)doc"); - -REGISTER_OP("RandomPermutationSequence") - .Attr("num: int = 1") - .Attr("batch: int = 1") - .Attr("repeat: bool = false") - .Attr("seed: int = 0") - .Output("out: int32") - .SetIsStateful() - .Doc(R"doc( -Generate random samples from [0..num-1] without replacements. - -num: The number of ids. -batch: Each output is a vector of size up to batch. Right now, - the last batch from one epoch is the only one that can be - smaller than a full batch. -repeat: If true, this op keep generating random samples after one - epoch. If false, this op errors with `tf.errors.OutOfRangeError` when an - epoch finishes. -seed: The random seed. -out: Each output is a vector of size up to batch. -)doc"); - -REGISTER_OP("BestStep") - .Output("best_step: int64") - .Attr("hist_file: string") - .Attr("tol: float = 0.0") - .Attr("minimize: bool = true") - .Attr("metric: string = \"\"") - .SetIsStateful() - .Doc(R"doc( - -Determines the best global step from a history file. - -best_step: Shape [2]. best_step[0] is scalar value for best global step. - best_step[1] is scalar value for last global step. -hist_file: A text file containing 'step score' records, or a file pattern that - matches tf event files in the format of /path_to_file/events.out.tfevents*. -tol: Difference between previous best score and current score must be greater -than this amount to trigger update. -minimize: If the metric is being minimized. Recorded in hist_file, smaller - scores are better if True, and bigger scores are better if False. -metric: The name of the metric being tracked. -)doc"); - -REGISTER_OP("BeamSearchStep") - .Input("scores: float32") - .Input("atten_probs: float32") - .Input("best_scores: float32") - .Input("cumulative_scores: float32") - .Input("in_scores: float32") - .Input("in_hyps: int32") - .Input("in_prev_hyps: int32") - .Input("in_done_hyps: string") - .Input("in_atten_probs: float32") - .Input("is_last_chunk: bool") - .Input("cur_step: int32") - .Output("out_best_scores: float32") - .Output("out_cumulative_scores: float32") - .Output("out_scores: float32") - .Output("out_hyps: int32") - .Output("out_prev_hyps: int32") - .Output("out_done_hyps: string") - .Output("out_atten_probs: float32") - .Output("all_done: bool") - .Attr("eoc_id: int = -1") - .Attr("eos_id: int") - .Attr("beam_size: float") - .Attr("num_hyps_per_beam: int") - .Attr("valid_eos_max_logit_delta: float = 5.0") - .Attr("local_eos_threshold: float = -100.0") - .Attr("merge_paths: bool = false") - .Attr("allow_empty_terminated_hyp: bool = true") - .Attr("ensure_full_beam: bool = false") - .Attr("force_eos_in_last_step: bool = false") - .SetShapeFn([](shape_inference::InferenceContext* c) { - c->set_output(0, c->input(2)); - c->set_output(1, c->input(3)); - c->set_output(2, c->input(4)); - c->set_output(3, c->input(5)); - c->set_output(4, c->input(6)); - c->set_output(5, c->input(7)); - c->set_output(6, c->input(8)); - c->set_output(7, c->Scalar()); - return Status::OK(); - }) - .Doc(R"doc( - -Move forward one step in beam search. - -Let "b" be the number of beams, "k" be the number hyps in each beam, "t" be the -maximum decoding steps. - -The following data structures are allocated before the first decoding step and -are passed along from cur step to the next step: - -in_scores - A tensor of shape [t, b * k]. in_scores[i, j] is the local - score of the j-th hyp at the i-th decoding step. -in_hyps - A tensor of shape [t, b * k]. in_hyps[i, j] is the token id of the - j-th hyp at the i-th decoding step. -in_prev_hyps - A tensor of shape [t, b * k]. in_prev_hyps[i, j] stores a - pointer of the j-th hyp at time step i to the hyp at previous timestep - (i - 1). 0 <= in_prev_hyps[i, j] < b * k. -in_done_hyps - A tensor of shape [t, b * k]. in_done_hyps[i, j] can be either an - empty string, or a serialized Hypothesis proto. Terminated hyps are removed - from the beam and are moved to the corresponding in_done_hyps slot. -in_atten_probs - A tensor of shape [t, b * k, s_len]. in_atten_probs[i, j, ...] - is the attention probs over the source words for the j-th hyp at the i-th - timestep. - -Those tensors are modified (with content for the cur_step timestep being filled -in) within this op invocation and are passed to the corresponding output -tensors. - -is_last_chunk: A tensor of shape [b * k]. Used by neural transducer, determine - whether the current hypothesis reaches the last chunk and should treat the - next end-of-chunk symbol as end-of-sentence. -scores: A matrix of shape [b * k, vocab_size], where b is the number of - active beams, and k is the number of hyps in each beam. Local scores for the - current timestep. -atten_probs: A matrix of shape [b * k, source_len]. Attention probabilities - for the current timestep. -best_scores: A vector of size [b], best scores of terminated hyps so far in - each of the beams. -cumulative_scores: A vector of size [b * k]. The cumulative score of each - active hyp before the current step. -in_scores: As explained above. -in_hyps: As explained above. -in_prev_hyps: As explained above. -in_done_hyps: As explained above. -in_atten_probs: As explained above. -cur_step: Current step id. -out_best_scores: - Updated best scores for each of the beams. -out_cumulative_scores: - A vector of size [b * k]. The cumulative score of the new hyps after the - current decoding step. -out_scores: - As explained above. -out_hyps: - As explained above. -out_prev_hyps: - As explained above. -out_done_hyps: - As explained above. -out_atten_probs: - As explained above. -all_done: - A scalar, whether decoding should terminate for all beams. -eoc_id: Token id of the special end of chunk token. -eos_id: Token id of the special end of sequence token. -beam_size: Search terminates if the delta between the scores of the active hyps - in a beam and the best scores exceeds this threashold. -num_hyps_per_beam: Number of hyps in a beam. -valid_eos_max_logit_delta: We allow to terminate a hyp only if its logit - is no more than `valid_eos_max_logit_delta` away from the logit of the best - candidate. -local_eos_threshold: We allow to terminate a hyp if the local score for - is greater than local_eos_threshold. -merge_paths: If true, hyps which are identical when epsilons are removed will - be combined into a single hyp. The probability for that combined hyp will - be the sum of the probabilities of the component hyps. This can only be - applied for epsilon-emitting models (RNN-T and NT). -allow_empty_terminated_hyp: Whether it is okay to consider a hyp that consists - only of epsilons as terminated. By default this is true, as an - utterance may consist of silence. It should be set to false when EMBR - training epsilon-emitting models (e.g., RNN-T), which are prone to emit - all-epsilon hyps even in the absence of silence. Note that a hyp that - terminates in EOS is not considered empty, so this flag has no effect for - non-epsilon-emitting models. -ensure_full_beam: If True, we will not set the all_done output to True until we - have found 'num_hyps_per_beam' terminated hyps AND no active hyps have a - score within 'beam_size' of the best terminated hyp. If False, only the - second condition must be satisfied. Generally this should be False unless - beam search is being run as part of minimum word error rate training. -force_eos_in_last_step: If true, then if decode does not terminate even after - (max - 1) steps, eos symbol is injected into the result and partial - hypotheses (with a valid eos symbol in the end) are returned. all_done - is set to true for these partials. If false, which is the default behavior, - empty hypothesis are returned and all_done is set to false at termination. -)doc"); - -REGISTER_OP("TopKTerminatedHyps") - .Input("in_done_hyps: string") - .Input("src_seq_lengths: int32") - .Output("out_topk_hyps: string") - .Attr("k: int") - .Attr("num_hyps_per_beam: int") - .Attr("length_normalization: float") - .Attr("coverage_penalty: float") - .Attr("target_seq_length_ratio: float=1.0") - .Attr("eoc_id: int=-1") - .Attr("merge_paths: bool = false") - .SetShapeFn([](shape_inference::InferenceContext* c) { - auto batch_size = c->Dim(c->input(1), 0); - int k; - TF_RETURN_IF_ERROR(c->GetAttr("k", &k)); - shape_inference::DimensionOrConstant k_dim = c->UnknownDim(); - if (k > 0) { - k_dim = k; - } - c->set_output(0, c->Matrix(batch_size, k_dim)); - return Status::OK(); - }) - .Doc(R"doc( - -Compute the top k terminated hyps based on normalized score for each beam. - -Let "b" be the number of beams, "h" be the number hyps in each beam, "t" be the -maximum decoding steps. - -in_done_hyps: A tensor of shape [t, h * b]. Each string in in_done_hyps can be - either an empty string, or a serialized Hypothesis proto. If not empty, - in_done_hyps[t, i * num_beams + j] represents the i-th hypothesis for beam - j that terminates at step t. -src_seq_lengths: A tensor of shape [b] of the src sequence lengths. -out_topk_hyps: A string tensor of shape [b, k]. topk_hyps[i: ] contains - top k terminated hyp for beam 'i', each hyp could be either an empty string - or a serialized `Hypothesis` proto. -k: number of highest scoring hyps to be returned for each beam. -num_hyps_per_beam: Number of hyps per beam in the input `in_done_hyps`. -length_normalization: The length normalization ratio. -coverage_penalty: The alpha value for coverage penalty. -target_seq_length_ratio: Ratio of the average target sequence length - over the average source sequence length. -eoc_id: Token id of the special end of chunk or blank (epsilon) token. -1 means - this model does not use epsilon. -merge_paths: If true, hyps which are identical when epsilons are removed will - be combined into a single hyp. The probability for that combined hyp will - be the sum of the probabilities of the component hyps. This can only be - applied for epsilon-emitting models (RNN-T and NT). -)doc"); - -REGISTER_OP("UnpackHyp") - .Input("in_hyps: string") - .Output("out_ids: int32") - .Output("out_seq_lens: int32") - .Output("out_scores: float32") - .Attr("max_seq_length: int = 0") - .SetShapeFn([](shape_inference::InferenceContext* c) { - auto batch_size = c->NumElements(c->input(0)); - int k; - TF_RETURN_IF_ERROR(c->GetAttr("max_seq_length", &k)); - shape_inference::DimensionOrConstant k_dim = c->UnknownDim(); - if (k > 0) { - k_dim = k; - } - c->set_output(0, c->Matrix(batch_size, k_dim)); - c->set_output(1, c->Vector(batch_size)); - c->set_output(2, c->Vector(batch_size)); - return Status::OK(); - }) - .Doc(R"doc( -Unpacks hyps into tensors of ids, seq_len and scores. - -in_hyps: A vector of serialized `Hypothesis` protos. -out_ids: - Output sequences, a matrix of shape (batch_size, max_seq_length). - Sequences shorter than max_seq_length are padded with 0s. If max_seq_length is 0, derive it from the longest sequence in input_hyps. -out_seq_lens: - Length of each of the output sequence, a vector of size `batch_size`. -out_scores: - Scores for each of the output sequence, a vector of `batch_size`. -)doc"); - -REGISTER_OP("HypsFromBeamSearchOuts") - .Input("hyps: int32") - .Input("prev_hyps: int32") - .Input("done_hyps: bool") - .Input("scores: T") - .Input("atten_probs: T") - .Input("eos_scores: T") - .Input("eos_atten_probs: T") - .Output("out_hyps: string") - .Attr("T: {float, bfloat16} = DT_FLOAT") - .Attr("eos_id: int") - .Attr("num_hyps_per_beam: int") - .SetShapeFn([](shape_inference::InferenceContext* c) { - c->set_output(0, c->input(0)); - return ::tensorflow::Status::OK(); - }) - .Doc(R"doc( - -Generates `Hypothesis` protos from output of a beam search step. - -hyps: A tensor of shape [t, b * k] with ids of the token selected. -prev_hyps: A tensor of shape [t, b * k] with index to the previous hyps which - was selected. -done_hyps: A boolean tensor of shape [t, b * k] where value indicates if hyps - was terminated. -scores: A tensor of shape [t, b * k]. in_scores[i, j] is the local score of - the j-th hyp at the i-th decoding step. -atten_probs: A tensor of shape [t, b * k, s_len]. atten_probs[i, j, ...] - is the attention probs over the source words for the j-th hyp at the i-th - timestep. -eos_scores: A tensor of shape [t, b * k]. eos_scores[i, j] is the local - score of the EOS token at the j-th hyp at the i-th decoding step. -eos_atten_probs: A tensor of shape [t, b * k, s_len]. - eos_atten_probs[i, j, ...] is the attention probs over the source words - for the j-th terminated hyp at the i-th timestep. -out_hyps: A tensor of shape [t, b * k] with terminated hyps. -eos_id: Token id of the special end of sequence token. -num_hyps_per_beam: Number of hyps per beam. -)doc"); - -REGISTER_OP("CachedCall") - .Output("output: T") - .Attr("f: func") - .Attr("T: list(type) >= 0") - .SetIsStateful() - .SetShapeFn(shape_inference::UnknownShape) - .Doc(R"doc( -Invokes function f once and memorize its output. - -output: A list of output tensors whose types are T. -f: A function that returns a list of tensors (T). -)doc"); - -REGISTER_OP("VocabTokenToId") - .Input("token: string") - .Output("id: int32") - .Attr("vocab: list(string)") - .Attr("load_token_ids_from_vocab: bool = false") - .SetIsStateful() - .SetShapeFn(shape_inference::UnchangedShape) - .Doc(R"doc( -Looks up the token in the vocab and return its id. - -token: A scalar or list of strings. -id: A scalar or list of ints. -vocab: A list of strings. -load_token_ids_from_vocab: Whether token ids are present in vocab (i.e. vocab - contains two colums, one for IDs and one for words). If false, line numbers - are used. -)doc"); - -REGISTER_OP("VocabIdToToken") - .Input("id: int32") - .Output("token: string") - .Attr("vocab: list(string)") - .Attr("load_token_ids_from_vocab: bool = false") - .SetIsStateful() - .SetShapeFn(shape_inference::UnchangedShape) - .Doc(R"doc( -Looks up the token at the given id from a vocab. - -id: A scalar or list of ints. -token: A scalar or list of strings. -vocab: A list of strings. -load_token_ids_from_vocab: Whether token ids are present in vocab (i.e. vocab - contains two colums, one for IDs and one for words). If false, line numbers - are used. -)doc"); - -REGISTER_OP("TokenInVocab") - .Input("token: string") - .Output("result: bool") - .Attr("vocab: list(string)") - .Attr("load_token_ids_from_vocab: bool = false") - .SetIsStateful() - .SetShapeFn(shape_inference::UnchangedShape) - .Doc(R"doc( -Checks whether the provided token is in the vocab. - -token: A scalar or list of strings. -result: A scalar or list of bools. -vocab: A list of strings. -load_token_ids_from_vocab: Whether token ids are present in vocab (i.e. vocab - contains two colums, one for IDs and one for words). If false, line numbers - are used. -)doc"); - -REGISTER_OP("AsciiToTokenId") - .Input("labels: string") - .Output("token_ids: int32") - .Output("target_ids: int32") - .Output("paddings: float") - .Attr("append_eos: bool = true") - .Attr("maxlen: int = 300") - .Attr("pad_to_maxlen: bool = true") - .SetShapeFn([](shape_inference::InferenceContext* c) { - auto batch_size = c->Dim(c->input(0), 0); - int maxlen; - TF_RETURN_IF_ERROR(c->GetAttr("maxlen", &maxlen)); - c->set_output(0, c->Matrix(batch_size, maxlen)); - c->set_output(1, c->Matrix(batch_size, maxlen)); - c->set_output(2, c->Matrix(batch_size, maxlen)); - return Status::OK(); - }) - .Doc(R"doc( -Converts ASCII label strings into token ids. - -labels: A vector of shape [batch]. -token_ids: - A matrix of shape [batch, maxlen]. - token_ids[i, j] is the i-th sample's j-th token id. - token_ids[i, 0] is always . -target_ids: - A matrix of shape [batch, maxlen]. - target_ids[i, j] is the i-th sample's j-th prediction label id. -paddings: - A matrix of shape [batch, maxlen]. - paddings[i, j] == 1.0 indicates that i-th training example' - j-th target token is padded and should be ignored. -append_eos: Whether to append at the end and treat it as a non-padded - label. -maxlen: an integer, sequence length of the output tensors. -pad_to_maxlen: Whether to pad the output to maxlen. -)doc"); - -REGISTER_OP("StrToVocabTokens") - .Input("labels: string") - .Output("token_ids: int32") - .Output("target_ids: int32") - .Output("paddings: float") - .Attr("append_eos: bool = true") - .Attr("maxlen: int = 300") - .Attr("pad_to_maxlen: bool = true") - .Attr("vocab_filepath: string") - .Attr("load_token_ids_from_vocab: bool = true") - .Attr("delimiter: string = ' '") - .SetShapeFn([](shape_inference::InferenceContext* c) { - auto batch_size = c->Dim(c->input(0), 0); - int maxlen; - TF_RETURN_IF_ERROR(c->GetAttr("maxlen", &maxlen)); - c->set_output(0, c->Matrix(batch_size, maxlen)); - c->set_output(1, c->Matrix(batch_size, maxlen)); - c->set_output(2, c->Matrix(batch_size, maxlen)); - return Status::OK(); - }) - .Doc(R"doc( -Tokenizes string into white space separated tokens according to a vocab file. - -labels: A vector of shape [batch]. -token_ids: - A matrix of shape [batch, maxlen]. - token_ids[i, j] is the i-th sample's j-th token id. - token_ids[i, 0] is always . -target_ids: - A matrix of shape [batch, maxlen]. - target_ids[i, j] is the i-th sample's j-th prediction label id. -paddings: - A matrix of shape [batch, maxlen]. - paddings[i, j] == 1.0 indicates that i-th training example's - j-th target token is padded and should be ignored. -append_eos: Whether to append at the end and treat it as a non-padded - label. -maxlen: an integer, sequence length of the output tensors. -pad_to_maxlen: Whether to pad the output to maxlen. -vocab_filepath: a string, filepath to the vocab file. -load_token_ids_from_vocab: Whether token ids are present in vocab (i.e. vocab - contains two colums, one for IDs and one for words). If false, line numbers - are used. -delimiter: The delimiter to split the labels to tokens by. -)doc"); - -REGISTER_OP("IdToAscii") - .Input("token_ids: int32") - .Input("seq_lengths: int32") - .Output("sequence: string") - .SetShapeFn([](shape_inference::InferenceContext* c) { - c->set_output(0, c->Vector(c->Dim(c->input(0), 0))); - return Status::OK(); - }) - .Doc(R"doc( -Converts sequences from token ids to actual ASCII tokens. - -token_ids: A matrix of shape [batch, seq_len]. -seq_lengths: A vector of shape [batch]. seq_lengths[i] is the length of the - i-th sequence. Only the first seq_lengths[i] tokens in token_ids[i] are - valid tokens for the i-th sequence. -sequence: A vector of shape [batch]. The converted string sequence. -)doc"); - -REGISTER_OP("NgramIdToToken") - .Input("token_ids: int32") - .Input("seq_lengths: int32") - .Output("sequences: string") - .SetShapeFn([](shape_inference::InferenceContext* c) { - c->set_output(0, c->input(1)); - return Status::OK(); - }) - .Attr("ngram_vocab_filepath: string") - .Attr("ngram_separator: string = \"\"") - .Doc(R"doc( -Converts sequences from token ids to actual tokens. - -token_ids: A matrix of shape [batch, seq_len]. -seq_lengths: A vector of shape [batch]. seq_lengths[i] is the length of the - i-th sequence. Only the first seq_lengths[i] tokens in token_ids[i] are - valid tokens for the i-th sequence. -sequences: A vector of shape [batch]. The converted string sequence. -ngram_vocab_filepath: filepath to the ngram vocab file. -ngram_separator: separator to use when joining ngrams into string. -)doc"); - -REGISTER_OP("BpeWordsToIds") - .Input("labels: string") - .Output("token_ids: int32") - .Output("target_ids: int32") - .Output("paddings: float") - .Attr("append_eos: bool = true") - .Attr("maxlen: int = 300") - .Attr("sos_id: int = 1") - .Attr("eos_id: int = 2") - .Attr("tokenization_filepath: string") - .SetShapeFn([](shape_inference::InferenceContext* ctx) { - const auto batch_size = ctx->Dim(ctx->input(0), 0); - int maxlen; - TF_RETURN_IF_ERROR(ctx->GetAttr("maxlen", &maxlen)); - ctx->set_output(0, ctx->Matrix(batch_size, maxlen)); - ctx->set_output(1, ctx->Matrix(batch_size, maxlen)); - ctx->set_output(2, ctx->Matrix(batch_size, maxlen)); - return Status::OK(); - }) - .Doc(R"doc( -A tokenizer to convert string to BPE ids. - -This op is especially convenient for mapping string text to a sequenec of word BPE -ids. The `labels` strings are tokenized via BPE. This op is typically used in conjunction with BpeIdsToWords. -As the vocabulary file it receives the mapping from each word to the series of BPE ids. -An example of lines in the vocabulary file: -... -AARON 10005,16,29 -AARON'S 10005,16,3466 -AARONSON 10005,16,17,447 -... - -The output tensor `token_ids` is a sequence of integer ids with prepended. -The output tensor `target_ids` is a sequence of integer ids with appended. - -labels: The batch of tf.String tensors. Expected shape is [batch_size]. -token_ids: - The ids with . The shape is [batch_size, maxlen]. -target_ids: - The ids with . The shape is [batch_size, maxlen]. -paddings: - The paddings. The shape is [batch_size, maxlen]. -maxlen: Maximum length of token_ids/target_ids/paddings. -tokenization_filepath: A path to a text file where each line is a word separated with space form a list of ids which are separated by ','. -)doc"); - -REGISTER_OP("BpeIdsToWords") - .Input("token_ids: int32") - .Input("seq_lengths: int32") - .Output("sequences: string") - .Attr("vocab_filepath: string") - .SetShapeFn([](shape_inference::InferenceContext* ctx) { - const auto batch_size = ctx->Dim(ctx->input(0), 0); - ctx->set_output(0, ctx->Vector(batch_size)); - return Status::OK(); - }) - .Doc(R"doc( -A tokenizer to map BPE ids to strings. - -This op is to map a sequence of integer ids to a string. The op is typically -used in conjunction with BpeWordsToIds. - -The op will consume `seq_lengths` of tokens from `token_ids` and convert it to -string `sequences`. A space character will be interested inbetween tokens. We do -not filter any tokens (i.e., and are not treated specially). - -token_ids: The ids (can include paddings; length is determined by seq_lengths). The shape is [batch_size, maxlen]. -seq_lengths: The length of the ids. The shape is [batch_size]. -sequences: The string sequences. The shape is [batch_size]. -vocab_filepath: A path to a text file where each line is a BPE string token. -)doc"); - -REGISTER_OP("GenericInput") - .Output("out: out_types") - .INPUT_ATTRS // Common input attributes. - .Attr("out_types: list(type)") - .Attr("processor: func") - .Attr("dynamic_padding_dimensions: list(int) = []") - .Attr("dynamic_padding_constants: list(int) = []") - .Doc(R"doc( -Produces examples from processed from records. - -out: The 1st dimension of every tensor is the batch dimension. -)doc" INPUT_DOCS - R"doc( -out_types: A list of tensor types. -processor: A function that processes a string (one record) and returns - a list of tensors. The last tensor must be a int32 scalar, which is - used in conjunction with `bucket_upper_bound` to bucket all the - samples. The other tensors belongs to one sample. They have the - respective `out_types`. These tensors' first dimension are _not_ the - batch dimension. Instead, when multiple samples are merged into a - batch, GenericInput's implementation expand the batch dimension (dim - 0) and concatenate the corresponding tensors into one tensor. -dynamic_padding_dimensions: If not empty, must be the same length as out. - Specifies the 0-indexed dimension to pad dynamically for each output. - The output is padded to the longest tensor in the batch along the dimension. - The first (0-th) dimension is _not_ the batch dimension. A value of -1 - indicates the specified output should not be padded, eg. if the output is a - scalar rather than a sequence. -dynamic_padding_constants: Must be set if `dynamic_padding_dimension` is - provided. The constant value to use for padding. -)doc"); - -REGISTER_OP("StaticMapStringInt") - .Input("x: string") - .Output("y: int32") - .Attr("keys: list(string)") - .Attr("vals: list(int) = []") - .Attr("unk: int = -1") - .SetShapeFn([](shape_inference::InferenceContext* c) { - c->set_output(0, c->input(0)); - return ::tensorflow::Status::OK(); - }) - .Doc(R"doc( -Maps every element of x according a static mapping. - -x: A Tensor of type string. -y: A Tensor of type int32. Same shape of x. -keys: The list of keys. -vals: The list of values. If empty, defaults to [0 .. len(keys)). -unk: The value when the key is not found. -)doc"); - -REGISTER_OP("StaticMapIntString") - .Input("x: int32") - .Output("y: string") - .Attr("keys: list(int) = []") - .Attr("vals: list(string)") - .Attr("unk: string = ''") - .SetShapeFn([](shape_inference::InferenceContext* c) { - c->set_output(0, c->input(0)); - return ::tensorflow::Status::OK(); - }) - .Doc(R"doc( -Maps every element of x according a static mapping. - -x: A Tensor of type int32. -y: A Tensor of type string. Same shape of x. -keys: The list of keys. If empty, defaults to [0 .. len(keys)). -vals: The list of values. -unk: The value when the key is not found. -)doc"); - -REGISTER_OP("StaticMapIntInt") - .Input("x: int32") - .Output("y: int32") - .Attr("keys: list(int) = []") - .Attr("vals: list(int)") - .Attr("unk: int = -1") - .SetShapeFn([](shape_inference::InferenceContext* c) { - c->set_output(0, c->input(0)); - return ::tensorflow::Status::OK(); - }) - .Doc(R"doc( -Maps every element of x according a static mapping. - -x: A Tensor of type int32. -y: A Tensor of type int32. Same shape of x. -keys: The list of keys. If empty, defaults to [0 .. len(keys)). -vals: The list of values. -unk: The value when the key is not found. -)doc"); - -REGISTER_OP("ComputePreconditioners") - .Input("inputs: num_tensors * float32") - .Input("exponents: num_tensors * float32") - .Input("global_step: int32") - .Attr("preconditioner_compute_graphdef: string") - .Attr("keys: list(string)") - .Attr("sync: bool = false") - .Attr("num_tensors: int >= 1") - .SetIsStateful() - .SetShapeFn([](shape_inference::InferenceContext* c) { - return Status::OK(); - }) - .Doc(R"doc( -Compute preconditioners for Shampoo optimizer. - -inputs: A list of Tensors of type float32, of statistic matrices. -exponents: A list of scalar Tensors of type float32, exponent for matrix power. -global_step: A scalar Tensor of type int32 which indicates the global step. -preconditioner_compute_graphdef: A graphdef which indicates the function to run. -keys: A list of keys indicating the name of preconditioners. -sync: Boolean indicating whether to run preconditioning in synchronous mode. -num_tensors: Number of tensor inputs. -)doc"); - - -REGISTER_OP("GetPreconditioners") - .Input("shapes: num_tensors * Tshape") - .Output("outputs: num_tensors * float32") - .Output("statuses: num_tensors * bool") - .Attr("preconditioner_compute_graphdef: string") - .Attr("keys: list(string)") - .Attr("Tshape: {int32, int64} = DT_INT32") - .Attr("num_tensors: int >= 1") - .SetIsStateful() - .SetShapeFn([](shape_inference::InferenceContext* c) { - std::vector shapes; - if (c->input("shapes", &shapes).ok()) { - for (int i = 0; i < shapes.size(); ++i) { - shape_inference::ShapeHandle out; - TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensor(i, &out)); - c->set_output(i, out); - c->set_output(shapes.size() + i, c->Scalar()); - } - } - return Status::OK(); - }) - .Doc(R"doc( -Get preconditioners for Shampoo optimizer. - -shapes: A list of Tensors of type Tshape indicating the size of preconditioner. -outputs: A list of Tensors of type float32 which are the preconditioners. -statuses: A list of Tensors of type bool which are the preconditioner status. -preconditioner_compute_graphdef: A graphdef which indicates the function to run. -keys: A list of keys indicating the name of preconditioners. -Tshape: The data-type to use for shape. -num_tensors: Number of tensor inputs. -)doc"); - -REGISTER_OP("MlPerfSubwordIdToString") - .Input("token_ids: int32") - .Input("seq_lengths: int32") - .Output("sequences: string") - .SetShapeFn([](shape_inference::InferenceContext* c) { - c->set_output(0, c->input(1)); - return Status::OK(); - }) - .Attr("vocab_filepath: string") - .Doc(R"doc( -Converts sequences from subword token ids to strings - -token_ids: A matrix of shape [batch, seq_len]. -seq_lengths: A vector of shape [batch]. seq_lengths[i] is the length of the - i-th sequence. Only the first seq_lengths[i] tokens in token_ids[i] are - valid tokens for the i-th sequence. -sequences: A vector of shape [batch]. The converted string sequence. -vocab_filepath: filepath to the MLPerf subword vocab file. -)doc"); - -REGISTER_OP("PackSequences") - .Input("src_actual_seq_len: int32") - .Input("tgt_actual_seq_len: int32") - .Attr("packed_batch_size: int") - .Attr("packed_src_seq_len: int") - .Attr("packed_tgt_seq_len: int") - .SetIsStateful() // TODO(navari): disable when packed_batch_size==0? - .Output("src_segment_ids: int32") - .Output("src_segment_pos: int32") - .Output("src_indices_in_input: int32") - .Output("tgt_segment_ids: int32") - .Output("tgt_segment_pos: int32") - .Output("tgt_indices_in_input: int32") - .SetShapeFn([](shape_inference::InferenceContext* c) { - int packed_batch_size, packed_src_seq_len, packed_tgt_seq_len; - TF_RETURN_IF_ERROR(c->GetAttr("packed_batch_size", &packed_batch_size)); - shape_inference::DimensionOrConstant batch_dim = c->UnknownDim(); - if (packed_batch_size > 0) { - batch_dim = packed_batch_size; - } - TF_RETURN_IF_ERROR(c->GetAttr("packed_src_seq_len", &packed_src_seq_len)); - TF_RETURN_IF_ERROR(c->GetAttr("packed_tgt_seq_len", &packed_tgt_seq_len)); - c->set_output(0, c->Matrix(batch_dim, packed_src_seq_len)); - c->set_output(1, c->Matrix(batch_dim, packed_src_seq_len)); - c->set_output(2, c->Matrix(batch_dim, packed_src_seq_len)); - c->set_output(3, c->Matrix(batch_dim, packed_tgt_seq_len)); - c->set_output(4, c->Matrix(batch_dim, packed_tgt_seq_len)); - c->set_output(5, c->Matrix(batch_dim, packed_tgt_seq_len)); - return Status::OK(); - }) - .Attr("seed: int = 0") - .Doc(R"doc( -Produces a packing pattern for the (src, tgt) input pair with the provided -lengths, according to the given packed shape. - -For example, the following input:: - - src_actual_seq_len = [3, 2, 1] - tgt_actual_seq_len = [4, 1, 5] - packed_batch_size = 2 - packed_src_seq_len = 5 - packed_tgt_seq_len = 5 - -will result in:: - - src_segment_ids = [ [1, 1, 1, 2, 2], [1, 0, 0, 0, 0] ] - src_segment_pos = [ [0, 1, 2, 0, 1], [0, 0, 0, 0, 0] ] - src_indices_in_input = [ [0, 0, 0, 1, 1], [2, 0, 0, 0, 0] ] - tgt_segment_ids = [ [1, 1, 1, 1, 2], [1, 1, 1, 1, 1] ] - tgt_segment_pos = [ [0, 1, 2, 3, 0], [0, 1, 2, 3, 4] ] - tgt_indices_in_input = [ [0, 0, 0, 0, 1], [2, 2, 2, 2, 2] ] - -The packed sequence length can be different between src and tgt. For example, -the following input:: - - src_actual_seq_len = [3, 2, 1] - tgt_actual_seq_len = [4, 1, 5] - packed_batch_size = 2 - packed_src_seq_len = 4 - packed_tgt_seq_len = 6 - -will result in:: - - src_segment_ids = [ [1, 1, 1, 0], [1, 1, 2, 0] ] - src_segment_pos = [ [0, 1, 2, 0], [0, 1, 0, 0] ] - src_indices_in_input = [ [0, 0, 0, 0], [1, 1, 2, 0] ] - tgt_segment_ids = [ [1, 1, 1, 1, 0, 0], [1, 2, 2, 2, 2, 2] ] - tgt_segment_pos = [ [0, 1, 2, 3, 0, 0], [0, 0, 1, 2, 3, 4] ] - tgt_indices_in_input = [ [0, 0, 0, 0, 0, 0], [1, 2, 2, 2, 2, 2] ] - -If there are too few input sequences to pack into `output_shape`, the op pads -the remaining elements in the output. - -If there are too many input sequences to pack into `output_shape`, the op drops -input sequences. The dropping is done randomly uniformly on the input sequences -to not bias the distribution of sequence lengths in the packed output. - -src_actual_seq_len: A tensor of shape [N], where N is the input batch size. - This tensor contains the actual lengths for the src sequence. -tgt_actual_seq_len: A tensor of shape [N], where N is the input batch size. - This tensor contains the actual lengths for the tgt sequence. -packed_batch_size: A scalar. The output batch size. The packed output will - be of shape [packed_batch_size, packed_{src,tgt}_seq_len] for src and tgt, - respectively. if this value is set to 0, output will be of variable batch - size, determined by the number of row needed to pack all given inputs. -packed_src_seq_len: A scalar. The output sequence length for src. A src input - with shape [N, src_input_seq_len] will be packed into an output with shape - [packed_batch_size, packed_src_seq_len]. -packed_tgt_seq_len: A scalar. The output sequence length for tgt. A tgt input - with shape [N, tgt_input_seq_len] will be packed into an output with shape - [packed_batch_size, packed_tgt_seq_len]. -src_segment_ids: - A tensor of shape [packed_batch_size, packed_src_seq_len]. Incrementing from 1 - to indicate each segment in the packed output for src. Zero is reserved for - indicating padding at the end of each row. -tgt_segment_ids: - A tensor of shape [packed_batch_size, packed_tgt_seq_len]. Incrementing from 1 - to indicate each segment in the packed output for tgt. Zero is reserved for - indicating padding at the end of each row. -src_segment_pos: - A tensor of shape [packed_batch_size, packed_src_seq_len]. Zero-based index to - indicate relative position within each segment for src. Zero is also used to - indicate padding. When needed, use `src_segment_ids` to disambiguate. -tgt_segment_pos: - A tensor of shape [packed_batch_size, packed_tgt_seq_len]. Zero-based index to - indicate relative position within each segment for tgt. Zero is also used to - indicate padding. When needed, use `tgt_segment_ids` to disambiguate. -src_indices_in_input: - A tensor of shape [packed_batch_size, packed_src_seq_len]. For each segment in - the packed output, it contains the original (zero-based) row index of each - segment found in `src_actual_seq_len`. Zero is also used to indicate padding. - When needed, use `src_segment_ids` to disambiguate. -tgt_indices_in_input: - A tensor of shape [packed_batch_size, packed_tgt_seq_len]. For each segment in - the packed output, it contains the original (zero-based) row index of each - segment found in `tgt_actual_seq_len`. Zero is also used to indicate padding. - When needed, use `tgt_segment_ids` to disambiguate. -seed: Seed for random number generator, which is used when we need to drop - excessive input sequences. If seed is zero, use completely random seed. -)doc"); - -REGISTER_OP("ApplyPacking") - .Input("input: T") - .Input("padding: T") - .Input("segment_ids: int32") - .Input("indices_in_input: int32") - .Output("output: T") - .SetShapeFn([](shape_inference::InferenceContext* c) { - DataType dtype; - TF_RETURN_IF_ERROR(c->GetAttr("T", &dtype)); - if (dtype == DT_STRING) { - const auto batch_size = c->Dim(c->input(2), 0); - c->set_output(0, c->Vector(batch_size)); - } else { - c->set_output(0, c->input(2)); - } - return Status::OK(); - }) - .Attr("T: type") - .Doc(R"doc( -Applies a packing pattern on the input to obtain a packed output. - -A slightly different semantics when T is tf.string type: the output joins the -strings that are packed on the same row, separated by `padding`. - -The inputs `segment_ids` and `indices_in_input` can be obtained from the outputs -of an `PackSequence` op (though only the src or the tgt tensors are needed). - -Note that ApplyPacking is done on a per column basis (either on the src or on -the tgt), as opposed to in PackSequences, when both src and tgt columns must be -processed together within the same op. - -input: A tensor of shape [N, seq_len]. The input to apply the packing to. - For tf.string typed input, a vector of shape [N] is expected. -padding: A scalar to indicate the padding value. This is typically the zero - value of T, but may not always be the case, e.g. when the input is a paddings - tensor, in which case caller should set padding=1. - For tf.string typed input, padding is used as a separator to join all the - strings on the same row in the output. -segment_ids: A rank 2 tensor of shape `output_shape`. -indices_in_input: A rank 2 tensor of shape `output_shape`. - -output: - A tensor of shape `output_shape`. For tf.string typed input, the output - is a vector of strings where its length is the same as the number of rows in - `output_shape`. -)doc"); - -REGISTER_OP("Mass") - .Input("ids: int32") - .Input("weights: float32") - .Input("actual_seq_len: int32") - .Attr("mask_id: int") - .Attr("mask_ratio: float = 0.5") - .Attr("mask_minlen: int = 0") - .Attr("span_len: int = 100000") - .Attr("random_start_prob: float = 0.6") - .Attr("keep_prob: float = 0.1") - .Attr("rand_prob: float = 0.1") - .Attr("mask_prob: float = 0.8") - // TODO(alisonlui): This flag is rarely used; remove after verification. - .Attr("mask_target: bool = True") - .Attr("vocab_size: int") - .Attr("first_unreserved_id: int = 4") - .Output("src_ids: int32") - .Output("tgt_ids: int32") - .Output("tgt_labels: int32") - .Output("tgt_weights: float32") - .Doc(R"doc( -Applies masking to implement MASS. - -ids: Tensor of shape [batch_size, max_seq_len] containing the token ids. - Should include EOS token . -weights: Tensor of shape [batch_size, max_seq_len]. -actual_seq_len: Tensor of shape [batch_size]. - -mask_id: The id to use for the mask token. -mask_ratio: Proportion of src to mask. -mask_minlen: Skip sentences too short to mask at least this many tokens. -span_len: Split mask_len into segments of this size and randomly distribute -those across the src. -random_start_prob: The probability that the placement of masked segments will be - entirely random. The remaining cases are split evenly between masking at the - beginning and at the end of the src. -keep_prob: The probability that a token to be masked will be unchanged. - `keep_prob + rand_prob + mask_prob` must sum to 1. -rand_prob: The probability that a token to be masked will be replaced with a - random token in the vocab. `keep_prob + rand_prob + mask_prob` must sum to 1. -mask_prob: The probability that a token to be masked will be replaced with the - mask_id. `keep_prob + rand_prob + mask_prob` must sum to 1. -mask_target: whether to mask the target (the mask will be the inverse of that of - the src). -vocab_size: Vocab size used when selecting a random token to replace a masked - token. -first_unreserved_id: Tokens greater than or equal to this may be selected at - random to replace a masked token. - -src_ids: - Masked ids. E.g. `s1 s2 s3 m m ` -tgt_ids: - Right-shifted ids with BOS token added, where the mask is the - positional inverse of that of the source unless mask_target=False. - E.g. `m m m s3 s4 m` -tgt_labels: - E.g. `s1 s2 s3 s4 s5 ` -tgt_weights: - weights are zeroed wherever the target is masked. -)doc"); - -} // namespace -} // namespace tensorflow diff --git a/lingvo/core/ops/x_ops_helper.h b/lingvo/core/ops/x_ops_helper.h deleted file mode 100644 index b6552b5d..00000000 --- a/lingvo/core/ops/x_ops_helper.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef LINGVO_CORE_OPS_X_OPS_HELPER_H_ -#define LINGVO_CORE_OPS_X_OPS_HELPER_H_ - -#define INPUT_ATTRS \ - Output("bucket_keys: int32") \ - .Attr("file_pattern: string") \ - .Attr("input_source_weights: list(float) = []") \ - .Attr("file_random_seed: int = 301") \ - .Attr("file_buffer_size: int = 10000") \ - .Attr("file_buffer_size_in_seconds: int = 0") \ - .Attr("file_parallelism: int = 16") \ - .Attr("bucket_upper_bound: list(int)") \ - .Attr("bucket_batch_limit: list(int)") \ - .Attr("bucket_adjust_every_n: int = 0") \ - .Attr("flush_every_n: int = 0") \ - .Attr("num_threads: int = 1") \ - .Attr("require_sequential_order: bool = False") \ - .Attr("repeat_count: int = -1") \ - .Attr("use_chaining: bool = False") \ - .SetIsStateful() - -#define INPUT_DOCS \ - R"( \ -file_pattern: A comma-separated list of glob patterns or sharded file patterns\ - for the data files. A sharded file pattern looks like /path/name@100 or \ - /path/name@*.\ -input_source_weights: A list of input sources weights that control the input\ - example mix. The records will be sampled from inputs proportionally to these\ - weights. When empty list is provided, no mix weighting will be done.\ - Defaults to empty list.\ -file_random_seed: Random seeds used to produce randomized records.\ -file_buffer_size: The randomization shuffling buffer.\ -file_buffer_size_in_seconds: Number of records the shuffling buffer should\ - contain, measured in seconds (the number of records demanded by the trainer\ - in this many seconds).\ -file_parallelism: How many sstables are opened and concurrently iterated over.\ -bucket_upper_bound: Bucketing scheme. Specifies each bucket's upper bound.\ -bucket_batch_limit: Batching scheme. Specifies each bucket's maximum batch\ - size.\ -bucket_adjust_every_n: If non-zero, optimize the values of bucket_upper_bound\ - except the last one after every N records based on the current input length\ - distribution.\ -flush_every_n: If non-zero, flushes all batches buffered so far every these\ - many records are yielded.\ -num_threads: Number of threads to use for the record batcher. Each thread\ - fills separate batches based on bucket limits.\ -require_sequential_order: If true, the input op is required to process the file\ - glob as well as the contents of each file in a deterministic sequential order.\ - Setting this automatically disables file_random_seed, file_buffer_size,\ - file_parallelism, num_threads, and requires a single file_pattern.\ -repeat_count: Number of repetitions of a dataset before throwing OutOfRange\ - error when using require_sequential_order. Must only be set if\ - require_sequential_order is True.)\ -use_chaining: If true, the input op is outputing records from file patterns in \ - order. That is, first all records from first file pattern will be yielded, \ - then all records from the second file pattern and so on. If false, the \ - records from different file patterns will be mixed.\ -)" - -#endif // LINGVO_CORE_OPS_X_OPS_HELPER_H_ diff --git a/lingvo/core/ops/yielder_test_helper.cc b/lingvo/core/ops/yielder_test_helper.cc deleted file mode 100644 index 7ff994e8..00000000 --- a/lingvo/core/ops/yielder_test_helper.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ -#include "lingvo/core/ops/yielder_test_helper.h" - -#include "tensorflow/core/lib/io/path.h" -#include "tensorflow/core/lib/strings/stringprintf.h" - -namespace tensorflow { -namespace lingvo { - -void GeneratePlainTextTestData(const string& prefix, int n, int m) { - for (int i = 0; i < n; ++i) { - std::unique_ptr file; - TF_CHECK_OK(Env::Default()->NewWritableFile( - io::JoinPath("/tmp", strings::StrCat(prefix, ".", i)), - &file)); - for (int j = 0; j < m; ++j) { - TF_CHECK_OK(file->Append( - strings::Printf("%s:%010d\n", prefix.c_str(), m * i + j))); - } - } -} - -void GenerateCheckpointPlainTextTestData(const string& prefix, int m) { - std::unique_ptr ckpt_file; - std::unique_ptr data_file; - TF_CHECK_OK(Env::Default()->NewWritableFile( - io::JoinPath("/tmp", prefix), &ckpt_file)); - TF_CHECK_OK(ckpt_file->Append( - strings::Printf("current: {file_pattern:\"data-0.txt\"}"))); - - string data_file_path = io::JoinPath("/tmp", "data-0.txt"); - TF_CHECK_OK(Env::Default()->NewWritableFile(data_file_path, &data_file)); - for (int j = 0; j < m; ++j) { - TF_CHECK_OK(data_file->Append( - strings::Printf("%s:%010d\n", prefix.c_str(), m + j))); - } -} - -void UpdateCheckpointPlainTextTestData(const string& prefix, int m) { - std::unique_ptr ckpt_file; - std::unique_ptr data_file; - TF_CHECK_OK(Env::Default()->NewWritableFile( - io::JoinPath("/tmp", prefix), &ckpt_file)); - TF_CHECK_OK(ckpt_file->Append( - strings::Printf("current: {file_pattern:\"data-1.txt\"}"))); - - string data_file_path = io::JoinPath("/tmp", "data-1.txt"); - TF_CHECK_OK(Env::Default()->NewWritableFile(data_file_path, &data_file)); - for (int j = 0; j < m; ++j) { - TF_CHECK_OK(data_file->Append( - strings::Printf("%s:%010d\n", prefix.c_str(), 2 * m + j))); - } -} - -std::unordered_map ComputeInputSourceDistribution( - const std::vector& vals) { - std::unordered_map input_source_distribution; - for (const string& val : vals) { - const auto prefix_end = val.find(':'); - if (prefix_end != string::npos) { - input_source_distribution[val.substr(0, prefix_end)] += 1.0; - } - } - for (auto it = input_source_distribution.begin(); - it != input_source_distribution.end(); ++it) { - it->second /= vals.size(); - } - return input_source_distribution; -} - -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/core/ops/yielder_test_helper.h b/lingvo/core/ops/yielder_test_helper.h deleted file mode 100644 index f85e1981..00000000 --- a/lingvo/core/ops/yielder_test_helper.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ -#ifndef LINGVO_CORE_OPS_YIELDER_TEST_HELPER_H_ -#define LINGVO_CORE_OPS_YIELDER_TEST_HELPER_H_ - -#include -#include -#include "lingvo/core/ops/record_yielder.h" - -namespace tensorflow { -namespace lingvo { - -class MockRecordYielder : public BasicRecordYielder { - public: - MockRecordYielder() : BasicRecordYielder() {} - MOCK_METHOD1(Yield, Status(Record* record)); - MOCK_METHOD0(Close, void()); - MOCK_CONST_METHOD0(current_epoch, int64()); -}; - -// Generates n plain text files with m lines each. -void GeneratePlainTextTestData(const string& prefix, int n, int m); - -// Generates checkpoint file and data file for test. -void GenerateCheckpointPlainTextTestData(const string& prefix, int m); -void UpdateCheckpointPlainTextTestData(const string& prefix, int m); - -// Computes input source distribution of lines read from plain text data -// generated by GeneratePlainTextTestData function. -std::unordered_map ComputeInputSourceDistribution( - const std::vector& vals); - -} // namespace lingvo -} // namespace tensorflow - -#endif // LINGVO_CORE_OPS_YIELDER_TEST_HELPER_H_ diff --git a/lingvo/core/optimizer.py b/lingvo/core/optimizer.py deleted file mode 100644 index ad091d18..00000000 --- a/lingvo/core/optimizer.py +++ /dev/null @@ -1,605 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Optimizers.""" - -import re - -import lingvo.compat as tf -from lingvo.core import adagraft -from lingvo.core import base_layer -from lingvo.core import distributed_shampoo -from lingvo.core import py_utils -from lingvo.core import summary_utils - - -class Base(base_layer.BaseLayer): - """Base class for all optimizers.""" - - @classmethod - def Params(cls): - p = super().Params() - p.name = cls.__name__ - p.Define( - 'use_bf16_gradients_ar', False, - 'Whether to use bfloat16 dtype for gradients all-reduce. ' - 'This applies to TPU only.') - return p - - def GetOptimizer(self, lr): - """Returns the TF optimizer object.""" - raise NotImplementedError('Abstract method') - - def AddSummary(self, lr, optimizer, var_grad): - """Adds summary if needed.""" - pass - - def ComputeGradients(self, loss, vmap, *args, **kwargs): - """Allows subclasses control computation of gradients.""" - kwargs['use_bf16_gradients_ar'] = self.params.use_bf16_gradients_ar - return py_utils.ComputeGradients(loss, vmap, *args, **kwargs) - - def VarReuseForSlotVars(self): - """Multi-task models require AUTO_REUSE for var sharing.""" - var_reuse = False - if py_utils.GetOpportunisticVariableReuse(): - var_reuse = tf.AUTO_REUSE - return var_reuse - - def Apply(self, lr, var_grad): - """Applies the gradient to the variable. - - Args: - lr: A scalar. The base learning rate. - var_grad: A `.NestedMap` of (var, grad) pairs. - - Returns: - The variable update op. - """ - optimizer = self.GetOptimizer(lr) - - def _Apply(): - if self.params.use_bf16_gradients_ar: - return optimizer.apply_gradients( - [(tf.cast(g, tf.float32), v) for (v, g) in var_grad.Flatten()], - name='meta_backprop') - else: - return optimizer.apply_gradients( - [(g, v) for (v, g) in var_grad.Flatten()], name='meta_backprop') - - if not py_utils.use_resource_variables(): - var_update_op = _Apply() - else: - # Many optimizers, e.g., Adam, Adagrad, etc., create - # variables. We need to ensure name scope and variable scope are - # cleared. Otherwise, tpu.batch_parallel does not work. - with tf.name_scope(None): - with tf.variable_scope( - tf.VariableScope( - use_resource=True, reuse=self.VarReuseForSlotVars())): - var_update_op = _Apply() - self.AddSummary(lr, optimizer, var_grad) - return var_update_op - - def ApplyPostTrainingLoop(self, global_step): - """Applies any computation to run after each tpu trainining loop. - - Args: - global_step: Global step variable. - - Returns: - Ops to run after training loop ends. - """ - return tf.no_op() - - -class CompositeOptimizer(Base): - """Composite Optimizer. - - A composite optimizer is composed of one or more Lingvo Optimizer objects - where regex specifies which variables should use which optimizer. The - optimizer_map dictionary must specify a default_optimizer regex to a - (Lingvo Optimizer, learning rate) tuple which will be applied to all variables - which do not match an earlier regex. - - For example, - - optimizer_map = {'a': Adam, 'b': Adagrad, 'default_optimizer': SGD} - - will apply Adam to all variables which contain an 'a' in their name, apply - Adagrad to all variables which contain a 'b' in their name, and apply SGD to - the variables which do not contain either 'a' or 'b'. - - If a non-default_optimizer matches more than one variable -- in this example - variables with both 'a' and 'b' in their name -- an exception is thrown. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'optimizer_map', None, - 'Mapping of variable regex to (Lingvo Optimizer, learning rate) tuple.') - return p - - def __init__(self, params): - super().__init__(params) - self._optimizer_map = {} - self._lr_map = {} - for index, regex in enumerate(params.optimizer_map): - sub_optimizer, learning_rate = params.optimizer_map[regex] - self.CreateChild('sub_{}_{}'.format(sub_optimizer.name, index), - sub_optimizer) - self._optimizer_map[regex] = self.children['sub_{}_{}'.format( - sub_optimizer.name, index)] - self._lr_map[regex] = learning_rate - - if 'default_optimizer' not in self._optimizer_map: - raise KeyError('default_optimizer is not found in optimizer_map. Please ' - 'specify a default_optimizer regex and its associated ' - '(Lingvo Optimizer, learning rate) tuple.') - - def GetOptimizer(self, lr): - """Returns a dictionary of regex to TF optimizer objects.""" - return { - k: v.GetOptimizer(self._lr_map[k]) - for k, v in self._optimizer_map.items() - } - - def Apply(self, lr, var_grad): - """For each optimizer, apply the gradient to the variable. - - Args: - lr: A scalar. The base learning rate. - var_grad: A `.NestedMap` of (var, grad) pairs. - - Returns: - The variable update op. - - Raises: - Exception: When the regex overlaps with or does not cover all variables. - """ - # Override inherited GetOptimizer even though learning rate is unused. - tf_optimizer_map = self.GetOptimizer(0) - var_grad_map = {regex: [] for regex in self._optimizer_map} - - for (v, g) in var_grad.Flatten(): - regex_match = 0 - for regex in self._optimizer_map: - if re.match(regex, v.name): - var_grad_map[regex].append((g, v)) - regex_match += 1 - if regex_match == 0: - var_grad_map['default_optimizer'].append((g, v)) - if regex_match > 1: - raise Exception('Variable {} is matched {} times by regex {}'.format( - v.name, regex_match, list(self._optimizer_map.keys()))) - - def _Apply(): - """Use the matched optimizer to apply the gradients.""" - train_ops = [] - non_default_regex = [ - regex for regex in self._optimizer_map if regex != 'default_optimizer' - ] - for regex in self._optimizer_map: - if var_grad_map[regex]: - opt = tf_optimizer_map[regex] - train_ops.append(opt.apply_gradients(var_grad_map[regex])) - # pylint: disable=cell-var-from-loop, g-long-lambda - if regex == 'default_optimizer': - filtered_var_grad = var_grad.FilterKeyVal(lambda k, v: any( - [re.match(i, v.var.name) for i in non_default_regex])) - else: - filtered_var_grad = var_grad.FilterKeyVal( - lambda k, v: (re.match(regex, v.var.name))) - # pylint: enable=cell-var-from-loop, g-long-lambda - self._optimizer_map[regex].AddSummary(self._lr_map[regex], opt, - filtered_var_grad) - return tf.group(*train_ops, name='composite_optimizer_train_op') - - if not py_utils.use_resource_variables(): - var_update_op = _Apply() - else: - # Many optimizers, e.g., Adam, Adagrad, etc., create - # variables. We need to ensure name scope and variable scope are - # cleared. Otherwise, tpu.batch_parallel does not work. - var_reuse = False - if py_utils.GetOpportunisticVariableReuse(): - var_reuse = tf.AUTO_REUSE - with tf.name_scope(None): - with tf.variable_scope( - tf.VariableScope(use_resource=True, reuse=var_reuse)): - var_update_op = _Apply() - return var_update_op - - def ApplyPostTrainingLoop(self, global_step): - """Apply any computation to run after each tpu training loop for each optimizer. - - Args: - global_step: Global step variable. - - Returns: - Ops to run after training loop ends. - """ - post_training_ops = [ - opt.ApplyPostTrainingLoop(global_step) - for _, opt in self._optimizer_map.items() - ] - return tf.group(*post_training_ops) - - -class SGD(Base): - """SGD.""" - - def GetOptimizer(self, lr): - return tf.train.GradientDescentOptimizer(lr) - - def AddSummary(self, lr, optimizer, var_grad): - summary_utils.scalar('sgd_lr', lr) - - -class Momentum(Base): - """Momentum optimizer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'alpha', 0.9, 'The damping factor in the momentum ' - 'optimizer. This controls how the velocity (averaged ' - 'past gradients) is decayed over time.') - p.Define('use_nesterov', False, 'True iff use Nesterov') - return p - - def GetOptimizer(self, lr): - p = self.params - return tf.train.MomentumOptimizer( - learning_rate=lr, momentum=p.alpha, use_nesterov=p.use_nesterov) - - def AddSummary(self, lr, optimizer, var_grad): - summary_utils.scalar('momentum_lr', lr) - - -class RMSProp(Base): - """RMSProp optimizer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('decay', 0.9, 'Discounting factor for the history/coming gradient') - p.Define('momentum', 0.9, 'Momentum in RMSProp.') - p.Define( - 'epsilon', 1.0, - 'Epsilon term for RMSProp. Small value to avoid zero denominator.') - return p - - def GetOptimizer(self, lr): - p = self.params - return tf.train.RMSPropOptimizer( - lr, p.decay, momentum=p.momentum, epsilon=p.epsilon) - - def AddSummary(self, lr, optimizer, var_grad): - summary_utils.scalar('rmsprop_lr', lr) - - -class Adagrad(Base): - """Adagrad.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('initial_accumulator_value', 1.0, - "Adagrad's initial_accumulator_value.") - return p - - def GetOptimizer(self, lr): - p = self.params - return tf.train.AdagradOptimizer( - learning_rate=lr, initial_accumulator_value=p.initial_accumulator_value) - - def AddSummary(self, lr, optimizer, var_grad): - summary_utils.scalar('adagrad_lr', lr) - for v, _ in var_grad.Flatten(): - slot = optimizer.get_slot(v, 'accumulator') - assert slot is not None - summary_utils.scalar('optimizer/adagrad_accum_%s' % v.name, - tf.reduce_mean(slot)) - - -class AdaDelta(Base): - """AdaDelta optimizer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('decay', 0.95, - 'Discounting factor for the history/coming gradient') - p.Define( - 'epsilon', 1e-8, - 'Epsilon term for AdaDelta. Small value to avoid zero denominator.') - return p - - def GetOptimizer(self, lr): - p = self.params - return tf.train.AdadeltaOptimizer( - learning_rate=lr, rho=p.decay, epsilon=p.epsilon) - - def AddSummary(self, lr, optimizer, var_grad): - summary_utils.scalar('adadelta_lr', lr) - - -class Adam(Base): - """Adam.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('beta1', 0.9, 'Beta1 for Adam.') - p.Define('beta2', 0.999, 'Beta2 for Adam.') - p.Define('epsilon', 1e-6, 'Epsilon for Adam.') - p.name = 'Adam' - return p - - @classmethod - def ParamsA(cls): - """Convenient method for a commonly used Adam config.""" - return cls.Params().Set(beta1=0.9, beta2=0.997, epsilon=1e-9) - - @classmethod - def ParamsB(cls): - """Convenient method for another commonly used Adam config.""" - return cls.Params().Set(beta1=0.9, beta2=0.98, epsilon=1e-9) - - def GetOptimizer(self, lr): - p = self.params - return tf.train.AdamOptimizer( - learning_rate=lr, - beta1=p.beta1, - beta2=p.beta2, - epsilon=p.epsilon, - name=p.name) - - def AddSummary(self, lr, optimizer, var_grad): - summary_utils.scalar('adam_lr', lr) - - -class Accumulator(Base): - """Gradient accumulator wrapper.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('optimizer_tpl', Adam.Params(), - 'Params for the wrapped optimizer.') - p.Define( - 'accum_steps', 5, 'Number of gradient accumulation steps' - ' before invoking wrapped optimizer.') - p.name = 'Accumulator' - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self.CreateChild('_opt', p.optimizer_tpl) - - def Apply(self, lr, var_grad): - p = self.params - - def _Acc(vg): - """Updating accumulators.""" - - v, g = vg - with tf.variable_scope(v.op.name): - a = py_utils.CreateVariable( - 'grad_accumulator', - py_utils.WeightParams(v.get_shape(), - py_utils.WeightInit.Constant(0.0), - self.params.dtype), - trainable=False) - a = tf.assign_add(a, g) - - return py_utils.VarGrad(v, a) - - var_grad = var_grad.Transform(_Acc) - - def _ApplyAndReset(): - with tf.control_dependencies([ - self._opt.Apply( - lr, py_utils.ApplyGradMultiplier(var_grad, 1. / p.accum_steps)) - ]): - return tf.group( - *[tf.assign(a, tf.zeros_like(a)) for _, a in var_grad.Flatten()]) - - return tf.cond( - tf.equal( - tf.math.floormod(self.global_step, p.accum_steps), - p.accum_steps - 1), _ApplyAndReset, lambda: tf.group(tf.no_op())) - - def GetOptimizer(self, lr): - return self._opt.GetOptimizer(lr) - - def AddSummary(self, lr, optimizer, var_grad): - return self._opt.AddSummary(lr, optimizer, var_grad) - - -class DistributedShampoo(Base): - """Approximates full-matrix AdaGrad per layer. - - Approximates full-matrix AdaGrad with kronecker-products of two statistics - matrices based on only the first-order gradients of the layer. - - "Second-order optimization made practical.", 2019 - Rohan Anil, Vineet Gupta, Tomer Koren, Kevin Regan, Yoram Singer. - """ - - @classmethod - def Params(cls): - params = super().Params() - params.Define('momentum', 0.9, 'Momentum parameter.') - params.Define('start_preconditioning_steps', 1000, - 'When to start approximate full matrix preconditioning.') - params.Define('initial_accumulator_value', 0.0, - 'Initial accumulator value.') - params.Define('block_size', 4096, 'Block size for partitioning.') - params.Define('block_partition_threshold_size', 1000000, - 'Threshold for block partitioning.') - params.Define('max_any_dim', 8192, - 'max dimension before skipping preconditioning altogether.') - params.Define('matrix_epsilon', 1e-6, - 'Minimum eigen value used to improve the conditioning.') - params.Define( - 'second_moment_averaging', 1.0, - 'Averaging coefficient, with special case of (1.0) means sum ' - 'of squares while less than 1.0 is RMSProp style moving' - ' average modification.') - params.Define( - 'fallback_to_diagonal_dim', 4096, - 'If any dimension is larger than this value, the optimizer falls back' - ' to the diagonal.') - params.Define( - 'statistics_computation_frequency', 1, - 'How often to compute statistics. Greater than 1 speeds up training.') - return params - - def GetOptimizer(self, lr): - params = self.params - return distributed_shampoo.DistributedShampoo( - learning_rate=lr, - momentum=params.momentum, - start_preconditioning_steps=params.start_preconditioning_steps, - initial_accumulator_value=params.initial_accumulator_value, - matrix_epsilon=params.matrix_epsilon, - statistics_computation_frequency=( - params.statistics_computation_frequency), - second_moment_averaging=params.second_moment_averaging, - max_any_dim=params.max_any_dim, - block_size=params.block_size, - global_step=self.global_step) - - def Apply(self, lr, var_grad): - """Applies the gradient to the variable. - - Args: - lr: A scalar. The base learning rate. - var_grad: A `.NestedMap` of (var, grad) pairs. - - Returns: - The variable update op. - """ - self._optimizer = self.GetOptimizer(lr) - - def _Apply(): - return self._optimizer.apply_gradients( - [(g, v) for (v, g) in var_grad.Flatten()], name='meta_backprop') - - if not py_utils.use_resource_variables(): - var_update_op = _Apply() - else: - # Many optimizers, e.g., Adam, Adagrad, etc., create - # variables. We need to ensure name scope and variable scope are - # cleared. Otherwise, tpu.batch_parallel does not work. - with tf.name_scope(None): - with tf.variable_scope( - tf.VariableScope( - use_resource=True, reuse=self.VarReuseForSlotVars())): - var_update_op = _Apply() - self.AddSummary(lr, self._optimizer, var_grad) - return var_update_op - - def ApplyPostTrainingLoop(self, global_step): - """Applies any computation to run after each tpu trainining loop. - - Args: - global_step: Global step variable. - - Returns: - Ops to run after training loop ends. - """ - invoke_async_ops = self._optimizer.invoke_async_preconditioner_computation( - tf.cast(global_step, tf.int32)) - assign_ops = self._optimizer.assign_preconditioner_to_host_vars() - return tf.group(*[invoke_async_ops, assign_ops]) - - def AddSummary(self, lr, optimizer, var_grad): - summary_utils.scalar('distributed_shampoo', lr) - - -class AdaGraft(Base): - """Optimizer which combines step size and direction of two optimizers. - - - Disentangling Adaptive Gradient Methods from Learning Rates - Naman Agarwal, Rohan Anil, Elad Hazan, Tomer Koren, Cyril Zhang - https://arxiv.org/abs/2002.11803 - """ - - @classmethod - def Params(cls): - params = super().Params() - - params.Define('magnitude_optimizer', None, - 'Instantiated Optimizer layer providing the step size.') - params.Define('direction_optimizer', None, - 'Instantiated Optimizer layer providing the step direction.') - params.Define( - 'direction_optimizer_lr', None, - 'Custom constant learning rate passed to direction ' - 'optimizer. If None, then pass scheduled lr for both.') - params.Define('use_global_norm', False, 'Whether to graft global l2 norm.') - params.Define('diagnostic', False, 'Whether to record norm measurements.') - - params.name = 'AdaGraft' - return params - - def GetOptimizer(self, lr): - params = self.params - - if params.direction_optimizer_lr is None: - dir_lr = lr - else: - dir_lr = params.direction_optimizer_lr - - magnitude_tf_optimizer = params.magnitude_optimizer.GetOptimizer(lr=lr) - direction_tf_optimizer = params.direction_optimizer.GetOptimizer(lr=dir_lr) - - return adagraft.AdaGraftOptimizer( - 1.0, - magnitude_tf_optimizer, - direction_tf_optimizer, - use_global_norm=params.use_global_norm, - diagnostic=params.diagnostic) - - def AddSummary(self, lr, optimizer, var_grad): - summary_utils.scalar('adagraft_lr', lr) - - if self.params.diagnostic: # verbose option - m_step_norm_total = 0.0 - d_step_norm_total = 0.0 - - for v, _ in var_grad.Flatten(): # record layer-wise gradient norms - m_step_norm = optimizer.get_slot(v, 'm_step_norm') - d_step_norm = optimizer.get_slot(v, 'd_step_norm') - summary_utils.scalar('optimizer/m_step_norm_%s' % v.name, m_step_norm) - summary_utils.scalar('optimizer/d_step_norm_%s' % v.name, d_step_norm) - m_step_norm_total += m_step_norm**2 - d_step_norm_total += d_step_norm**2 - - # record global gradient norms - m_step_norm_total **= 0.5 - d_step_norm_total **= 0.5 - summary_utils.scalar('optimizer/m_step_norm', m_step_norm_total) - summary_utils.scalar('optimizer/d_step_norm', d_step_norm_total) - summary_utils.scalar('optimizer/norm_correction', - m_step_norm_total / d_step_norm_total) diff --git a/lingvo/core/optimizer_test.py b/lingvo/core/optimizer_test.py deleted file mode 100644 index ea1ce33a..00000000 --- a/lingvo/core/optimizer_test.py +++ /dev/null @@ -1,215 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for optimizer.""" - -import lingvo.compat as tf -from lingvo.core import layers -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.core import test_utils -import numpy as np - - -class OptimizerTest(test_utils.TestCase): - - def testCompositeOptimizer(self): - adam_op = optimizer.Adam.Params() - rmsprop_op = optimizer.RMSProp.Params() - adam_rmsprop_opt = optimizer.CompositeOptimizer.Params().Set( - optimizer_map={ - 'fc/w': (adam_op, 1.), - 'fc/b': (rmsprop_op, 1.), - 'default_optimizer': (adam_op, 1.) - }).Instantiate() - - adam_op_2 = optimizer.Adam.Params().Set(name='adam_2') - unspecified_comp_opt = optimizer.CompositeOptimizer.Params().Set( - optimizer_map={ - 'fc/w': (adam_op_2, 1.), - 'default_optimizer': (adam_op_2, 1.) - }).Instantiate() - - sgd_op = optimizer.SGD.Params() - adagrad_op = optimizer.Adagrad.Params() - overlapping_comp_opt = optimizer.CompositeOptimizer.Params().Set( - optimizer_map={ - 'fc/w': (sgd_op, 1.), - '.': (adagrad_op, 1.), - 'default_optimizer': (adagrad_op, 1.) - }).Instantiate() - - params = layers.FCLayer.Params() - params.name = 'fc' - params.dtype = tf.float64 - params.input_dim = 3 - params.output_dim = 2 - params.batch_norm = False - fc_layer = layers.FCLayer(params) - - inputs = tf.placeholder(shape=[2, 4, 3], dtype=tf.float64) - output = fc_layer.FPropDefaultTheta(inputs) - loss = tf.reduce_sum(output) - var_grads = py_utils.ComputeGradients(loss, fc_layer.vars) - - self.assertIn('composite_optimizer_train_op', - adam_rmsprop_opt.Apply(1e-1, var_grads).name) - self.assertIn('composite_optimizer_train_op', - unspecified_comp_opt.Apply(1e-1, var_grads).name) - with self.assertRaisesRegex( - Exception, - 'Variable fc/w/var:0 is matched 2 times by regex', - ): - overlapping_comp_opt.Apply(1e-1, var_grads) - - def testAccumulator(self): - # testAccumulator compares - # - explicit averaging of independently computed var_grads1 and - # var_grads2, - # - Accumulator(SGD) optimizer effectively doing this over 2 steps. - np.random.seed(12345) - np_input1 = np.random.normal(0.1, 0.5, [2, 4, 3]) - np.random.seed(12346) - np_input2 = np.random.normal(0.1, 0.5, [2, 4, 3]) - - with self.session(use_gpu=True, graph=tf.Graph()) as sess: - tf.random.set_seed(123456) - params = layers.ProjectionLayer.Params() - params.name = 'proj' - params.dtype = tf.float64 - params.input_dim = 3 - params.output_dim = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.01, 123456) - - params.batch_norm = False - proj_layer = layers.ProjectionLayer(params) - inputs1 = tf.placeholder(shape=[2, 4, 3], dtype=tf.float64) - in_padding1 = tf.zeros([2, 4, 1], dtype=tf.float64) - inputs2 = tf.placeholder(shape=[2, 4, 3], dtype=tf.float64) - in_padding2 = tf.zeros([2, 4, 1], dtype=tf.float64) - output1 = proj_layer.FPropDefaultTheta(inputs1, in_padding1) - output2 = proj_layer.FPropDefaultTheta(inputs2, in_padding2) - loss1 = tf.reduce_sum(output1) - loss2 = tf.reduce_sum(output2) - var_grads1 = py_utils.ComputeGradients(loss1, proj_layer.vars) - var_grads2 = py_utils.ComputeGradients(loss2, proj_layer.vars) - op = optimizer.SGD.Params() - opt = op.Instantiate() - lr = 1e-1 - with tf.control_dependencies([loss1, loss2]): - var_update_op1 = opt.Apply( - lr, py_utils.ApplyGradMultiplier(var_grads1, 1. / 2.)) - with tf.control_dependencies([var_update_op1]): - var_update_op2 = opt.Apply( - lr, py_utils.ApplyGradMultiplier(var_grads2, 1. / 2.)) - - self.evaluate(tf.global_variables_initializer()) - vars1 = self.evaluate(proj_layer.vars.Flatten()) - loss1_1, grads1_1, loss1_2, grads1_2 = sess.run( - [ - loss1, - var_grads1.Transform(tuple), loss2, - var_grads2.Transform(tuple) - ], - feed_dict={ - inputs1: np_input1, - inputs2: np_input2, - }, - ) - sess.run([var_update_op2], - feed_dict={ - inputs1: np_input1, - inputs2: np_input2, - }) - vars1_1 = self.evaluate(proj_layer.vars.Flatten()) - - with self.session(use_gpu=True, graph=tf.Graph()) as sess: - tf.random.set_seed(123456) - params = layers.ProjectionLayer.Params() - params.name = 'proj' - params.dtype = tf.float64 - params.input_dim = 3 - params.output_dim = 2 - params.params_init = py_utils.WeightInit.Gaussian(0.01, 123456) - - params.batch_norm = False - proj_layer = layers.ProjectionLayer(params) - in_padding1 = tf.zeros([2, 4, 1], dtype=tf.float64) - inputs1 = tf.placeholder(shape=[2, 4, 3], dtype=tf.float64) - output1 = proj_layer.FPropDefaultTheta(inputs1, in_padding1) - loss = tf.reduce_sum(output1) - var_grads = py_utils.ComputeGradients(loss, proj_layer.vars) - op = optimizer.Accumulator.Params().Set( - accum_steps=2, dtype=tf.float64, optimizer_tpl=optimizer.SGD.Params()) - opt = op.Instantiate() - lr = 1e-1 - var_update_op = opt.Apply(lr, var_grads) - increment_global_step_op = tf.assign_add( - py_utils.GetOrCreateGlobalStepVar(), 1) - - self.evaluate(tf.global_variables_initializer()) - vars2 = self.evaluate(proj_layer.vars.Flatten()) - loss2_1, grads2_1 = sess.run([loss, var_grads.Transform(tuple)], - feed_dict={ - inputs1: np_input1, - }) - loss2_2, grads2_2 = sess.run([loss, var_grads.Transform(tuple)], - feed_dict={ - inputs1: np_input2, - }) - acc_0 = self.evaluate( - [v for v in tf.global_variables() if 'grad_accumulator' in v.name])[0] - sess.run([var_update_op], feed_dict={ - inputs1: np_input1, - }) - acc_1 = self.evaluate( - [v for v in tf.global_variables() if 'grad_accumulator' in v.name])[0] - vars2_intermediate = self.evaluate(proj_layer.vars.Flatten()) - self.evaluate(increment_global_step_op) - sess.run([var_update_op], feed_dict={ - inputs1: np_input2, - }) - acc_2 = self.evaluate( - [v for v in tf.global_variables() if 'grad_accumulator' in v.name])[0] - vars2_1 = self.evaluate(proj_layer.vars.Flatten()) - - self.assertAllClose(vars1, vars2) - - self.assertAllClose(acc_0, np.zeros_like(acc_0)) - self.assertAllClose(acc_1, grads2_1['w'][1]) - self.assertAllClose(acc_2, np.zeros_like(acc_0)) - - self.assertAllClose(loss1_1, loss2_1) - self.assertAllClose(loss1_2, loss2_2) - self.assertAllClose(grads1_1, grads2_1) - self.assertAllClose(grads1_2, grads2_2) - - self.assertAllClose(vars1, vars2_intermediate) - - self.assertAllClose(vars2[0], grads2_1['w'][0]) - self.assertAllClose(vars2[0], grads2_2['w'][0]) - - self.assertAllClose( - vars1[0] - 0.5 * lr * (grads1_1['w'][1] + grads1_2['w'][1]), vars1_1[0]) - - self.assertAllClose( - vars2[0] - 0.5 * lr * (grads2_1['w'][1] + grads2_2['w'][1]), vars2_1[0]) - - self.assertAllClose(vars2, vars2_intermediate) - self.assertAllClose(vars1_1, vars2_1) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/plot.py b/lingvo/core/plot.py deleted file mode 100644 index 9b6dbbb0..00000000 --- a/lingvo/core/plot.py +++ /dev/null @@ -1,498 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Utilities for generating image summaries using matplotlib.""" - -import collections -import functools -import traceback - -import lingvo.compat as tf -from lingvo.core import py_utils -from matplotlib.backends import backend_agg -import matplotlib.gridspec as gridspec -import matplotlib.pyplot as plt -import numpy as np -import six - - -def ToUnicode(text): - if not isinstance(text, str): - text = six.ensure_text(text, 'utf-8') - return text - - -def AddPlot(unused_fig, - axes, - data, - title=u'', - xlabel=u'', - ylabel=u'', - fontsize='small', - xlim=None, - ylim=None, - suppress_xticks=False, - suppress_yticks=False): - """Convenience function to add a plot.""" - axes.plot(data) - axes.set_title(ToUnicode(title), size=fontsize) - axes.set_xlabel(ToUnicode(xlabel), size=fontsize) - axes.set_ylabel(ToUnicode(ylabel), size=fontsize) - if xlim: - axes.set_xlim(xlim) - if ylim: - axes.set_ylim(ylim) - if suppress_xticks: - axes.set_xticks([]) - if suppress_yticks: - axes.set_yticks([]) - - -def AddImage(fig, - axes, - data, - cmap='bone_r', - clim=None, - show_colorbar=True, - title=u'', - xlabel=u'', - ylabel=u'', - fontsize='small', - origin='lower', - suppress_xticks=False, - suppress_yticks=False, - aspect='auto', - vmin=None, - vmax=None): - """Convenience function to plot data as an image on the given axes.""" - image = axes.imshow( - data, - cmap=cmap, - origin=origin, - aspect=aspect, - interpolation='nearest', - vmin=vmin, - vmax=vmax) - if show_colorbar: - fig.colorbar(image) - if clim is not None: - image.set_clim(clim) - axes.set_title(ToUnicode(title), size=fontsize) - axes.set_xlabel(ToUnicode(xlabel), size=fontsize) - axes.set_ylabel(ToUnicode(ylabel), size=fontsize) - if suppress_xticks: - axes.set_xticks([]) - if suppress_yticks: - axes.set_yticks([]) - - -def AddScatterPlot(unused_fig, - axes, - xs, - ys, - title=u'', - xlabel=u'', - ylabel=u'', - fontsize='small', - xlim=None, - ylim=None, - suppress_xticks=False, - suppress_yticks=False, - **kwargs): - """Convenience function to add a scatter plot.""" - # For 3D axes, check to see whether zlim is specified and apply it. - if 'zlim' in kwargs: - zlim = kwargs.pop('zlim') - if zlim: - axes.set_zlim(zlim) - - axes.scatter(xs, ys, **kwargs) - axes.set_title(ToUnicode(title), size=fontsize) - axes.set_xlabel(ToUnicode(xlabel), size=fontsize) - axes.set_ylabel(ToUnicode(ylabel), size=fontsize) - if xlim: - axes.set_xlim(xlim) - if ylim: - axes.set_ylim(ylim) - if suppress_xticks: - axes.set_xticks([]) - if suppress_yticks: - axes.set_yticks([]) - - -_SubplotMetadata = collections.namedtuple('_SubplotMetadata', - ['tensor_list', 'plot_func']) - - -class MatplotlibFigureSummary: - """Helper to minimize boilerplate in creating a summary with several subplots. - - Typical usage:: - - >>> fig_helper = plot.MatplotlibFigureSummary( - ... 'summary_name', shared_subplot_kwargs={'xlabel': 'Time'}) - >>> fig_helper.AddSubplot([tensor1], title='tensor1') - >>> fig_helper.AddSubplot([tensor2], title='tensor2', ylabel='Frequency') - >>> image_summary = fig_helper.Finalize() - - Can also be used as a context manager if the caller does not need the return - value from Finalize(), e.g. - - >>> with plot.MatplotlibFigureSummary('figure') as fig: - ... fig.AddSubplot([tensor1]) - """ - - def __init__(self, - name, - figsize=(8, 10), - max_outputs=3, - subplot_grid_shape=None, - gridspec_kwargs=None, - plot_func=AddImage, - shared_subplot_kwargs=None): - """Creates a new MatplotlibFigureSummary object. - - Args: - name: A string name for the generated summary. - figsize: A 2D tuple containing the overall figure (width, height) - dimensions in inches. - max_outputs: The maximum number of images to generate. - subplot_grid_shape: A 2D tuple containing the height and width dimensions - of the subplot grid. height * width must be >= the number of subplots. - Defaults to (num_subplots, 1), i.e. a vertical stack of plots. - gridspec_kwargs: A dict of extra keyword args to use when initializing the - figure's gridspec, as supported by matplotlib.gridspec.GridSpec. - plot_func: A function shared across all subplots used to populate a single - subplot. See the docstring for AddSubplot for details. - shared_subplot_kwargs: A dict of extra keyword args to pass to the plot - function for all subplots. This is useful for specifying properties - such as 'clim' which should be consistent across all subplots. - """ - self._name = name - self._figsize = figsize - self._max_outputs = max_outputs - self._subplot_grid_shape = subplot_grid_shape - self._gridspec_kwargs = gridspec_kwargs if gridspec_kwargs else {} - self._plot_func = plot_func - self._shared_subplot_kwargs = ( - shared_subplot_kwargs if shared_subplot_kwargs else {}) - self._subplots = [] - - def __enter__(self): - return self - - def __exit__(self, unused_exc_type, unused_exc_value, unused_tb): - self.Finalize() - - def AddSubplot(self, tensor_list, plot_func=None, **kwargs): - r"""Adds a subplot from tensors using plot_fun to populate the subplot axes. - - Args: - tensor_list: A list of tensors to be realized as numpy arrays and passed - as arguments to plot_func. The first dimension of each tensor in the - list corresponds to batch, and must be the same size for each tensor. - plot_func: A function with signature f(fig, axes, data1, data2, ..., - datan, \*\*kwargs) that will be called with the realized data from - tensor_list to plot data on axes in fig. This function is called - independently on each element of the batch. Overrides plot_func passed - in to the constructor. - **kwargs: A dict of additional non-tensor keyword args to pass to - plot_func when generating the plot, overridding any - shared_subplot_kwargs. Useful for e.g. specifying a subplot's title. - """ - merged_kwargs = dict(self._shared_subplot_kwargs, **kwargs) - if plot_func is None: - plot_func = self._plot_func - plot_func = functools.partial(plot_func, **merged_kwargs) - self._subplots.append(_SubplotMetadata(tensor_list, plot_func)) - - def FinalizeImage(self): - """Finishes creation of the overall figure, returning the image tensor.""" - subplot_grid_shape = self._subplot_grid_shape - if subplot_grid_shape is None: - subplot_grid_shape = (len(self._subplots), 1) - - # AddMatplotlibFigureSummary (due to restrictions of py_func) only supports - # flattened list of tensors so we must do some bookkeeping to maintain a - # mapping from _SubplotMetadata object to flattened_tensors. - subplot_slices = [] - flattened_tensors = [] - for subplot in self._subplots: - start = len(flattened_tensors) - subplot_slices.append((start, start + len(subplot.tensor_list))) - flattened_tensors.extend(subplot.tensor_list) - - def PlotFunc(fig, *numpy_data_list): - gs = gridspec.GridSpec(*subplot_grid_shape, **self._gridspec_kwargs) - for n, subplot in enumerate(self._subplots): - axes = fig.add_subplot(gs[n]) - start, end = subplot_slices[n] - subplot_data = numpy_data_list[start:end] - subplot.plot_func(fig, axes, *subplot_data) - - func = functools.partial(_RenderMatplotlibFigures, self._figsize, - self._max_outputs, PlotFunc) - batch_sizes = [tf.shape(t)[0] for t in flattened_tensors] - num_tensors = len(flattened_tensors) - with tf.control_dependencies([ - tf.assert_equal( - batch_sizes, [batch_sizes[0]] * num_tensors, summarize=num_tensors) - ]): - return tf.py_func( - func, flattened_tensors, tf.uint8, name='RenderMatplotlibFigures') - - def Finalize(self): - """Finishes creation of the overall figure, returning the image summary.""" - rendered = self.FinalizeImage() - return tf.summary.image(self._name, rendered, max_outputs=self._max_outputs) - - -def _RenderOneMatplotlibFigure(fig, plot_func, *numpy_data_list): - fig.clear() - plot_func(fig, *numpy_data_list) - fig.canvas.draw() - ncols, nrows = fig.canvas.get_width_height() - image = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8) - return image.reshape(nrows, ncols, 3) - - -def _RenderMatplotlibFigures(figsize, max_outputs, plot_func, *numpy_data_list): - r"""Renders a figure containing several subplots using matplotlib. - - This is an internal implementation detail of MatplotlibFigureSummary.Finalize - and should not be called directly. - - The unconventional function signature is used to work around the behavior of - `tf.py_func` which always passes in different tensors as positional arguments. - - Args: - figsize: A 2D tuple containing the overall figure (width, height) dimensions - in inches. - max_outputs: The maximum number of images to generate. - plot_func: A function with signature f(fig, data1, data2, ..., datan) that - will be called with \*numpy_data_list to plot data in fig. - *numpy_data_list: A list of numpy matrices to plot specified as separate - arguments. - - Returns: - A numpy 4D array of type np.uint8 which can be used to generate a - `tf.math.image_summary` when converted to a tf tensor. - """ - batch_size = numpy_data_list[0].shape[0] - max_outputs = min(max_outputs, batch_size) - images = [] - - # Use plt.Figure instead of plt.figure to avoid a memory leak (matplotlib - # keeps global references to every figure created with plt.figure). When not - # using plt.figure we have to create a canvas manually. - fig = plt.Figure(figsize=figsize, dpi=100, facecolor='white') - backend_agg.FigureCanvasAgg(fig) - - for b in range(max_outputs): - data = [numpy_data[b] for numpy_data in numpy_data_list] - try: - images.append(_RenderOneMatplotlibFigure(fig, plot_func, *data)) - except Exception as e: # pylint: disable=broad-except - tf.logging.warning( - 'Error rendering example %d using matplotlib: %s\n%s', b, e, - traceback.format_exc()) - if len(images) == max_outputs: - break - plt.close(fig) - - # Pad with dummy black images in case there were too many rendering errors. - while len(images) < max_outputs: - image_shape = (1, 1, 1) - if images: - image_shape = images[0].shape - images.append(np.ones(image_shape, dtype=np.uint8)) - - return np.array(images) - - -def FigureToSummary(name, fig): - """Create tf.Summary proto from matplotlib.figure.Figure. - - Args: - name: Summary name. - fig: A matplotlib figure object. - - Returns: - A `tf.Summary` proto containing the figure rendered to an image. - """ - canvas = backend_agg.FigureCanvasAgg(fig) - fig.canvas.draw() - ncols, nrows = fig.canvas.get_width_height() - png_file = six.BytesIO() - canvas.print_figure(png_file) - png_str = png_file.getvalue() - return tf.Summary(value=[ - tf.Summary.Value( - tag='%s/image' % name, - image=tf.Summary.Image( - height=nrows, - width=ncols, - colorspace=3, - encoded_image_string=png_str)) - ]) - - -def Image(name, figsize, image, setter=None, **kwargs): - """Plot an image in numpy and generates tf.Summary proto for it. - - Args: - name: Image summary name. - figsize: A 2D tuple containing the overall figure (width, height) dimensions - in inches. - image: A 2D/3D numpy array in the format accepted by pyplot.imshow. - setter: A callable taking (fig, axes). Useful to fine-tune layout of the - figure, xlabel, xticks, etc. - **kwargs: Additional arguments to AddImage. - - Returns: - A `tf.Summary` proto contains one image visualizing 'image. - """ - assert image.ndim in (2, 3), '%s' % image.shape - fig = plt.Figure(figsize=figsize, dpi=100, facecolor='white') - axes = fig.add_subplot(1, 1, 1) - # Default show_colorbar to False if not explicitly specified. - show_colorbar = kwargs.pop('show_colorbar', False) - # Default origin to 'upper' if not explicitly specified. - origin = kwargs.pop('origin', 'upper') - AddImage( - fig, axes, image, origin=origin, show_colorbar=show_colorbar, **kwargs) - if setter: - setter(fig, axes) - return FigureToSummary(name, fig) - - -def Scatter(name, figsize, xs, ys, setter=None, **kwargs): - """Plot a scatter plot in numpy and generates tf.Summary proto for it. - - Args: - name: Scatter plot summary name. - figsize: A 2D tuple containing the overall figure (width, height) dimensions - in inches. - xs: A set of x points to plot. - ys: A set of y points to plot. - setter: A callable taking (fig, axes). Useful to fine-tune layout of the - figure, xlabel, xticks, etc. - **kwargs: Additional arguments to AddScatterPlot. - - Returns: - A `tf.Summary` proto contains one image visualizing 'image. - """ - fig = plt.Figure(figsize=figsize, dpi=100, facecolor='white') - - # If z data is provided, use 3d projection. - # - # This requires the mplot3d toolkit (e.g., from mpl_toolkits import mplot3d) - # to be registered in the program. - if 'zs' in kwargs: - axes = fig.add_subplot(111, projection='3d') - else: - axes = fig.add_subplot(1, 1, 1) - AddScatterPlot(fig, axes, xs, ys, **kwargs) - if setter: - setter(fig, axes) - return FigureToSummary(name, fig) - - -Matrix = Image # pylint: disable=invalid-name - - -def Curve(name, figsize, xs, ys, setter=None, **kwargs): - """Plot curve(s) to a `tf.Summary` proto. - - Args: - name: Image summary name. - figsize: A 2D tuple containing the overall figure (width, height) dimensions - in inches. - xs: x values for matplotlib.pyplot.plot. - ys: y values for matplotlib.pyplot.plot. - setter: A callable taking (fig, axes). Useful to fine-control layout of the - figure, xlabel, xticks, etc. - **kwargs: Extra args for matplotlib.pyplot.plot. - - Returns: - A `tf.Summary` proto contains the line plot. - """ - fig = plt.Figure(figsize=figsize, dpi=100, facecolor='white') - axes = fig.add_subplot(1, 1, 1) - axes.plot(xs, ys, '.-', **kwargs) - if setter: - setter(fig, axes) - return FigureToSummary(name, fig) - - -def AddMultiCurveSubplot(fig, - tensors, - paddings, - labels, - xlabels=None, - **kwargs): - """Adds a multi curve subplot to Matplotlib figure. - - Plots one line for each entry in tensors and assigns a plot label legend. - - Args: - fig: The Matplotlib figure. - tensors: List of tensors of shape [batch, length] - paddings: Paddings for 'tensors' with shape [batch, length] with 0. in valid - positions and 1. in invalid. - labels: A list of tensor names (strings) of the same length as 'tensors'. - xlabels: A string tensor of shape [batch] with an xlabel per batch. - **kwargs: With optional, title, xlabel, ylabel, fontsize. - """ - data = [] - row_labels = [] - for t, l in zip(tensors, labels): - if t is not None: - data.append(py_utils.ApplyPadding(paddings, t)) - row_labels.append(l) - shape = py_utils.GetShape(data[0], 2) - data = tf.reshape(tf.concat(data, -1), [shape[0], len(data), shape[1]]) - - args = [data, py_utils.LengthsFromPaddings(paddings)] - if xlabels is not None: - args.append(xlabels) - fig.AddSubplot( - args, plot_func=_AddMultiCurveRowPlots, row_labels=row_labels, **kwargs) - - -def _AddMultiCurveRowPlots(fig, - axes, - data, - length, - x_label_override=None, - row_labels=None, - title=u'', - xlabel=u'', - ylabel=u'', - fontsize='small'): - """Add a plot per row in data and cut the plot by the length.""" - del fig - colors = ['b-', 'r-', 'g-', 'm-', 'y-'] - for row in range(data.shape[0]): - label = row_labels[row] if row_labels else '{}'.format(row) - axes.plot(data[row, :length], colors[row % len(colors)], label=label) - axes.set_xlim([0, length]) - axes.legend() - axes.set_title(ToUnicode(title), size=fontsize) - if x_label_override: - axes.set_xlabel(ToUnicode(x_label_override), size='x-small', wrap=True) - else: - axes.set_xlabel(ToUnicode(xlabel), size=fontsize) - axes.set_ylabel(ToUnicode(ylabel), size=fontsize) diff --git a/lingvo/core/plot_test.py b/lingvo/core/plot_test.py deleted file mode 100644 index 9cf305ef..00000000 --- a/lingvo/core/plot_test.py +++ /dev/null @@ -1,275 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for plot.""" - -import lingvo.compat as tf -from lingvo.core import plot -from lingvo.core import test_utils -import numpy as np - - -class PlotTest(test_utils.TestCase): - - def testToUnicode(self): - str_str = 'pójdź kińże tę chmurność w głąb flaszy' - uni_str = u'pójdź kińże tę chmurność w głąb flaszy' - - self.assertEqual(plot.ToUnicode(str_str), uni_str) - self.assertEqual(plot.ToUnicode(str_str), plot.ToUnicode(uni_str)) - - def testMatrix(self): - summary = plot.Matrix('summary', (4, 4), np.random.rand(10, 10)) - self.assertEqual(len(summary.value), 1) - value = summary.value[0] - self.assertGreater(value.image.width, 0) - self.assertGreater(value.image.height, 0) - - def testScatter(self): - summary = plot.Scatter( - 'summary', (4, 4), xs=np.random.rand(10), ys=np.random.rand(10)) - self.assertEqual(len(summary.value), 1) - value = summary.value[0] - self.assertGreater(value.image.width, 0) - self.assertGreater(value.image.height, 0) - - -class MatplotlibFigureSummaryTest(test_utils.TestCase): - - FIGSIZE = (8, 4) - EXPECTED_DPI = 100 - DEFAULT_DATA = tf.ones((5, 4)) - - def setUp(self): - with self.session() as s: - fig = plot.MatplotlibFigureSummary('DEFAULT', self.FIGSIZE, max_outputs=1) - batched_data = tf.expand_dims(self.DEFAULT_DATA, 0) # Batch size 1. - fig.AddSubplot([batched_data]) - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.default_encoded_image = summary.value[0].image.encoded_image_string - - def testBasic(self): - with self.session() as s: - fig = plot.MatplotlibFigureSummary( - 'matplotlib_figure', self.FIGSIZE, max_outputs=1) - batched_data = tf.expand_dims(self.DEFAULT_DATA, 0) # Batch size 1. - fig.AddSubplot([batched_data]) - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), 1) - value = summary.value[0] - self.assertEqual(value.tag, 'matplotlib_figure/image') - self.assertEqual(value.image.width, self.EXPECTED_DPI * self.FIGSIZE[0]) - self.assertEqual(value.image.height, self.EXPECTED_DPI * self.FIGSIZE[1]) - self.assertEqual(value.image.colorspace, 3) - self.assertEqual(value.image.encoded_image_string, - self.default_encoded_image) - - def testCanUseAsContextManager(self): - with self.session() as s: - with plot.MatplotlibFigureSummary( - 'context_manager_figure', self.FIGSIZE, max_outputs=1) as fig: - batched_data = tf.expand_dims(self.DEFAULT_DATA, 0) # Batch size 1. - fig.AddSubplot([batched_data]) - summary_str = s.run(tf.summary.merge_all(scope='context_manager_figure')) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), 1) - value = summary.value[0] - self.assertEqual(value.image.width, self.EXPECTED_DPI * self.FIGSIZE[0]) - self.assertEqual(value.image.height, self.EXPECTED_DPI * self.FIGSIZE[1]) - self.assertEqual(value.image.colorspace, 3) - self.assertEqual(value.image.encoded_image_string, - self.default_encoded_image) - - def testUnicodeText(self): - with self.session() as s: - fig = plot.MatplotlibFigureSummary( - 'matplotlib_uni', self.FIGSIZE, max_outputs=1) - batched_data = tf.expand_dims(self.DEFAULT_DATA, 0) # Batch size 1. - fig.AddSubplot([batched_data], xlabel=u'bździągwa', ylabel='żółć') - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), 1) - value = summary.value[0] - self.assertEqual(value.tag, 'matplotlib_uni/image') - self.assertEqual(value.image.width, self.EXPECTED_DPI * self.FIGSIZE[0]) - self.assertEqual(value.image.height, self.EXPECTED_DPI * self.FIGSIZE[1]) - self.assertEqual(value.image.colorspace, 3) - - def testMultipleSubplots(self): - batch_size = 4 - tensors = [tf.ones((batch_size, 3, 5)), tf.ones((batch_size, 2, 2))] - with self.session() as s: - max_outputs = 3 - fig = plot.MatplotlibFigureSummary('fig', self.FIGSIZE, max_outputs) - for t in tensors: - fig.AddSubplot([t]) - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), max_outputs) - for n, value in enumerate(summary.value): - self.assertEqual(value.tag, 'fig/image/%d' % n) - self.assertEqual(value.image.width, self.EXPECTED_DPI * self.FIGSIZE[0]) - self.assertEqual(value.image.height, self.EXPECTED_DPI * self.FIGSIZE[1]) - self.assertEqual(value.image.colorspace, 3) - self.assertNotEqual(value.image.encoded_image_string, - self.default_encoded_image) - - def testCustomPlotFunc(self): - batch_size = 3 - data = tf.ones((batch_size, 3, 5)) - trim = tf.constant([[3, 2], [2, 5], [3, 3]]) - titles = tf.constant(['batch1', 'batch2', 'batch3']) - - def TrimAndAddImage(fig, axes, data, trim, title, **kwargs): - plot.AddImage(fig, axes, data[:trim[0], :trim[1]], title=title, **kwargs) - - with self.session() as s: - fig = plot.MatplotlibFigureSummary( - 'fig_custom_plotfunc', - max_outputs=batch_size, - plot_func=TrimAndAddImage) - fig.AddSubplot([data, trim, titles]) - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), batch_size) - for n, value in enumerate(summary.value): - self.assertEqual(value.tag, 'fig_custom_plotfunc/image/%d' % n) - self.assertGreater(value.image.width, 1) - self.assertGreater(value.image.height, 1) - self.assertEqual(value.image.colorspace, 3) - self.assertNotEqual(value.image.encoded_image_string, - self.default_encoded_image) - - def testDoesNotDieOnMatplotlibError(self): - invalid_dim_data = tf.ones((5,)) - with self.session() as s: - fig = plot.MatplotlibFigureSummary('summary', self.FIGSIZE, max_outputs=1) - fig.AddSubplot([invalid_dim_data]) - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), 1) - value = summary.value[0] - # Generates dummy 1-pixel image. - self.assertEqual(value.image.width, 1) - self.assertEqual(value.image.height, 1) - - def testLargerBatch(self): - batch_size = 4 - tensors = [tf.ones((batch_size, 3, 5)), tf.ones((batch_size, 2, 2))] - with self.session() as s: - fig = plot.MatplotlibFigureSummary( - 'larger_batch', self.FIGSIZE, max_outputs=batch_size) - for t in tensors: - fig.AddSubplot([t]) - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), batch_size) - for n, value in enumerate(summary.value): - self.assertEqual(value.tag, u'larger_batch/image/%d' % n) - self.assertEqual(value.image.width, self.EXPECTED_DPI * self.FIGSIZE[0]) - self.assertEqual(value.image.height, self.EXPECTED_DPI * self.FIGSIZE[1]) - self.assertEqual(value.image.colorspace, 3) - self.assertNotEqual(value.image.encoded_image_string, - self.default_encoded_image) - - def testCanChangeFigsize(self): - figsize = (self.FIGSIZE[0], 2 * self.FIGSIZE[1]) - with self.session() as s: - fig = plot.MatplotlibFigureSummary('summary', figsize, max_outputs=1) - batched_data = tf.expand_dims(self.DEFAULT_DATA, 0) # Batch size 1. - fig.AddSubplot([batched_data]) - im = fig.Finalize() - summary_str = s.run(im) - - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), 1) - value = summary.value[0] - self.assertEqual(value.image.width, self.EXPECTED_DPI * figsize[0]) - self.assertEqual(value.image.height, self.EXPECTED_DPI * figsize[1]) - self.assertNotEqual(value.image.encoded_image_string, - self.default_encoded_image) - - def testEnforcesConsistentBatchSize(self): - batch_size = 4 - tensors = [tf.ones((batch_size, 3, 5)), tf.ones((batch_size - 2, 2, 2))] - with self.session() as s: - fig = plot.MatplotlibFigureSummary('summary', self.FIGSIZE, max_outputs=1) - for t in tensors: - fig.AddSubplot([t]) - im = fig.Finalize() - with self.assertRaises(tf.errors.InvalidArgumentError): - s.run(im) - - def testOnlyPlotsFirstMaxOutputImages(self): - batch_size = 4 - tensors = [tf.ones((batch_size, 3, 5)), tf.ones((batch_size, 2, 2))] - with self.session() as s: - fig = plot.MatplotlibFigureSummary('summary', self.FIGSIZE, max_outputs=2) - for t in tensors: - fig.AddSubplot([t]) - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), 2) - - def testLimitsOutputImagesIfBatchIsSmall(self): - batch_size = 1 - tensors = [tf.zeros((batch_size, 3, 5)), tf.ones((batch_size, 2, 2))] - with self.session() as s: - fig = plot.MatplotlibFigureSummary('summary', self.FIGSIZE, max_outputs=3) - for t in tensors: - fig.AddSubplot([t]) - im = fig.Finalize() - summary_str = s.run(im) - summary = tf.summary.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), 1) - - def testAddMultiCurveSubplot(self): - with self.session(graph=tf.Graph(), use_gpu=False): - fig = plot.MatplotlibFigureSummary('XXX') - batch_size = 2 - tensor = tf.ones([batch_size, 3]) - paddings = tf.constant([[0., 0., 0.], [0., 1., 1.]]) - plot.AddMultiCurveSubplot( - fig, [tensor, tensor], - paddings, - labels=['label1', 'label2'], - xlabels=tf.constant(['a', 'b']), - title='Title', - ylabel='Ylabel') - summary_str = self.evaluate(fig.Finalize()) - - summary = tf.Summary.FromString(summary_str) - self.assertEqual(len(summary.value), batch_size) - for n, value in enumerate(summary.value): - self.assertEqual(value.tag, 'XXX/image/%d' % n) - self.assertGreater(value.image.width, 0) - self.assertGreater(value.image.height, 0) - self.assertNotEqual(value.image.encoded_image_string, - self.default_encoded_image) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/predictor.py b/lingvo/core/predictor.py deleted file mode 100644 index a35fac6c..00000000 --- a/lingvo/core/predictor.py +++ /dev/null @@ -1,307 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Predictor does inference using a saved inference graph. - -Example:: - - params = model_registry.GetParams('MyModel', 'Test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export( - params) - pred = Predictor(inference_graph=inference_graph) - pred.Load("/tmp/logdir/train/ckpt-00000000") - [topk_hyps] = pred.Run(["topk_hyps"], src_strings=["Hello World"]) -""" -import threading -import time -from lingvo import model_imports -import lingvo.compat as tf -from lingvo.core import inference_graph_pb2 -from lingvo.core import py_utils - -from google.protobuf import text_format - - -def LoadInferenceGraph(path, clear_device_placement=False): - """Parse the given path as an InferenceGraph proto. - - Args: - path: The path to the file to load. - clear_device_placement: If true, clears device field from nodes in graph. - - Returns: - An InferenceGraph object. - """ - inference_graph = inference_graph_pb2.InferenceGraph() - with tf.io.gfile.GFile(path, "r") as f: - text_format.Parse(f.read(), inference_graph) - if clear_device_placement: - for node in inference_graph.graph_def.node: - node.ClearField("device") - for function in inference_graph.graph_def.library.function: - for node_def in function.node_def: - node_def.ClearField("device") - return inference_graph - - -class Predictor: - """Loads a model and does inference. - - See model.Inference() documentation for list of fetches and feeds. - """ - - def __init__(self, - inference_graph, - subgraph_name=None, - checkpoint=None, - device_type="gpu", - tf_master="", - session_config=None, - clear_device_placement=False): - """Constructor. - - Args: - inference_graph: A saved InferenceGraph proto. - subgraph_name: The subgraph to use for prediction. - checkpoint: An optional checkpoint to load. - device_type: Device type string. Either "cpu", "gpu", or "tpu". - tf_master: The tf_master. - session_config: A tf.SessionConfig to use. By default - py_utils.SessionConfig() is used. - clear_device_placement: If set, clears device field of loaded inference - graph. - """ - assert device_type in ["cpu", "gpu", "tpu"] - subgraph_name = subgraph_name or "default" - if isinstance(inference_graph, str): - tf.logging.info("Reading inference graph from %s.", inference_graph) - inference_graph = LoadInferenceGraph(inference_graph, - clear_device_placement) - self._inference_graph = inference_graph - self._checkpoint = checkpoint - self._device_type = device_type - self._tf_master = tf_master - self._session_config = session_config - - self._graph = tf.Graph() - with self._graph.as_default(): - tf.logging.info( - "Loading inference graph for prediction subgraph_name={}.".format( - subgraph_name)) - with tf.device("/%s:0" % "cpu" if device_type == "tpu" else device_type): - tf.import_graph_def(inference_graph.graph_def, name="") - if device_type == "tpu": - # If no tpu init op exists, create it here. - try: - self._graph.get_operation_by_name("tpu_init_op") - except KeyError: - tf.group(tf.tpu.initialize_system(), name="tpu_init_op") - - self._graph.finalize() - - if inference_graph.subgraphs: - if subgraph_name not in inference_graph.subgraphs: - raise ValueError( - "Subgraph %s not defined. Valid subgraphs: %s" % - (subgraph_name, list(inference_graph.subgraphs.keys()))) - subgraph = inference_graph.subgraphs[subgraph_name] - self._fetches = subgraph.fetches - self._feeds = subgraph.feeds - else: - self._fetches = inference_graph.fetches - self._feeds = inference_graph.feeds - - # Lock for creating new sessions. - self._sess_lock = threading.Lock() - self._cur_sess_id = 0 - self._CreateNewSession() - - @property - def fetch_keys(self): - return list(self._fetches.keys()) - - @property - def feed_keys(self): - return list(self._feeds.keys()) - - @property - def feed_shapes(self): - # Conversion from dict to NestedMap required. - return py_utils.Transform( - lambda x: self._graph.get_tensor_by_name(x).shape.as_list(), - py_utils.NestedMap(self._feeds)) - - @property - def fetch_shapes(self): - # Conversion from dict to NestedMap required. - return py_utils.Transform( - lambda x: self._graph.get_tensor_by_name(x).shape.as_list(), - py_utils.NestedMap(self._fetches)) - - @py_utils.RetryOnTransientTfError() - def _CreateNewSession(self): - """Updates self._sess with a new session.""" - config = self._session_config - if not config: - config = py_utils.SessionConfig() - sess = tf.Session(self._tf_master, graph=self._graph, config=config) - - try: - sess.run(self._graph.get_operation_by_name("init_all_tables")) - except KeyError: - tf.logging.info("Could not find tables initializer in graph.") - if self._device_type == "tpu": - sess.run(self._graph.get_operation_by_name("tpu_init_op")) - if self._checkpoint: - sess.run(self._inference_graph.saver_def.restore_op_name, { - self._inference_graph.saver_def.filename_tensor_name: self._checkpoint - }) - - else: - try: - init_op = self._graph.get_operation_by_name("init_all_variables") - sess.run(init_op) - except KeyError: - tf.logging.warning( - "No checkpoint provided and the graph has no default " - "variable_init op.") - tf.logging.info("Created new predictor session.") - self._sess = sess - - def _MaybeCreateNewSession(self, sess_id): - """Create a new session if sess_id is the current session. - - Args: - sess_id: The id of a session that no longer works. - """ - with self._sess_lock: - if sess_id == self._cur_sess_id: - self._CreateNewSession() - self._cur_sess_id += 1 - tf.logging.info("Current session id {}.".format(self._cur_sess_id)) - - @py_utils.RetryOnTransientTfError() - def _RunWithValidSession(self, fn, *args, **kwargs): - """Ensures `fn` is called while self._sess is a valid session.""" - sess_id = self._cur_sess_id - try: - return fn(self._sess, *args, **kwargs) - except py_utils.transient_tf_errors: - # self._sess is invalid, most likely due to the worker being preempted. - # Make sure a new session is created before re-raising the exception and - # triggering the py_utils.Retry loop. - self._MaybeCreateNewSession(sess_id) - raise - - def Load(self, checkpoint): - """Loads parameters from a checkpoint if self._sess is a valid session. - - Args: - checkpoint: The checkpoint path to restore. - """ - if checkpoint != self._checkpoint: - sess_id = self._cur_sess_id - try: - self._sess.run( - self._inference_graph.saver_def.restore_op_name, - {self._inference_graph.saver_def.filename_tensor_name: checkpoint}) - except py_utils.transient_tf_errors: - # self._sess is invalid, most likely due to the worker being preempted. - # Make sure a new session is created before re-raising the exception and - # triggering the py_utils.Retry loop. - self._MaybeCreateNewSession(sess_id) - raise - self._checkpoint = checkpoint - - def Run(self, - fetch_keys, - validate_fetches=True, - session_run_options=None, - run_metadata=None, - time_session_run=False, - **kwargs): - """Runs predictor. - - Args: - fetch_keys: dict_keys object or a list of keys in the fetch dictionary to - fetch. - validate_fetches: if True, raises a KeyError if a specified fetch is - invalid. If False, returns None for invalid fetches instead. - session_run_options: Optional tf.RunOptions() to use in the session. - run_metadata: Optional tf.RunMetadata() to use in the session. - time_session_run: Optional bool, if True, additionally return the - execution time of session.run. Defaults to False. - **kwargs: a dict of inputs to feed. - - Returns: - A list of predictions corresponding to the order of fetch_keys and, if - time_session_run is True, the run time in seconds. - - Raises: - InvalidArgumentError: the number of inputs does not meet requirements. - KeyError: a feed specified in kwargs is invalid, or a fetch in fetch_keys - is invalid and validate_fetches is True. - """ - single_fetch = False - if not isinstance(fetch_keys, (list, type(dict().keys()))): - single_fetch = True - fetch_keys = [fetch_keys] - - if validate_fetches: - for x in fetch_keys: - if x not in self._fetches: - raise KeyError( - "%s is not in the list of available fetches. Available keys: %s" % - (x, list(self._fetches.keys()))) - valid_fetch_idxs, valid_fetches = zip(*[(i, self._fetches[k]) - for i, k in enumerate(fetch_keys) - if k in self._fetches.keys()]) - - for k in kwargs: - if k not in self._feeds: - raise KeyError( - "%s is not in the list of available feeds. Available keys: %s" % - (k, list(self._feeds.keys()))) - feeds = {self._feeds[k]: v for k, v in kwargs.items()} - - run_options = tf.RunOptions(report_tensor_allocations_upon_oom=False) - if session_run_options: - run_options = session_run_options - - start = time.time() - fetched_results = self._RunWithValidSession( - tf.Session.run, - valid_fetches, - feed_dict=feeds, - options=run_options, - run_metadata=run_metadata) - duration = time.time() - start - results = [None] * len(fetch_keys) - for i, fetch in zip(valid_fetch_idxs, fetched_results): - results[i] = fetch - if single_fetch: - results = results[0] - return (results, duration) if time_session_run else results - - -def main(_): - import IPython # pylint: disable=g-import-not-at-top - IPython.start_ipython(argv=["--colors", "NoColor"], user_ns=globals()) - - -if __name__ == "__main__": - tf.disable_eager_execution() - model_imports.ImportAllParams() - tf.app.run(main) diff --git a/lingvo/core/predictor_runner_base.py b/lingvo/core/predictor_runner_base.py deleted file mode 100644 index a16c5130..00000000 --- a/lingvo/core/predictor_runner_base.py +++ /dev/null @@ -1,290 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Interface for binaries built around predictor. - -To use: subclass PredictorRunnerBase, implement the InputGenerator and RunBatch -functions, and call Run(). - -To run on TPU, set: - --device_type=tpu - --xla_device=tpu - --tf_master=url/to/tpu/server - --inference_threads=num_tpu_cores -""" - -import concurrent.futures -import itertools -import os -import re -import threading -import time - -from absl import flags -from lingvo import compat as tf -from lingvo.core import predictor -from lingvo.core import py_utils -import six - -flags.DEFINE_string( - 'checkpoint', None, 'Either a checkpoint file to load,' - ' or a directory containing multiple checkpoints, where' - ' the latest checkpoint will be loaded.') -flags.DEFINE_string( - 'inference_graph', None, 'Path to an inference graph. ' - 'If not specified, will be inferred from the checkpoint path.') -flags.DEFINE_string( - 'inference_subgraph_name', '', 'The name of the inference subgraph to use. ' - 'Defaults to the default subgraph.') -flags.DEFINE_enum('device_type', 'gpu', ['cpu', 'gpu', 'tpu'], 'Device type.') -flags.DEFINE_string('tf_master', 'local', 'tf_master for predictor session.') -flags.DEFINE_integer('inference_threads', '1', 'Number of inference threads.') -flags.DEFINE_integer('batch_size', 64, 'Batch size.') -flags.DEFINE_integer( - 'prediction_step_interval', 3000, 'Number of steps between outputs. ' - 'Only meaningful if FLAGS.checkpoint is a directory.') - -flags.DEFINE_integer('max_inputs', 0, 'Only process the first n inputs.') -flags.DEFINE_list( - 'input_id_filter', [], - 'If not empty, only process the input ids in the given list.') -flags.DEFINE_string( - 'output_dir', None, 'Output directory. ' - 'If FLAGS.checkpoint is a directory, a subdirectory will be created ' - 'for each checkpoint evaluated.') -flags.DEFINE_integer( - 'output_num_shards', 1, 'Each replica generates one shard of output ' - 'according to --output_shard_id.') -flags.DEFINE_integer( - 'output_shard_id', 0, - 'The output shard id in range [0, output_num_shards - 1].') - -FLAGS = flags.FLAGS - -_RETRY_SLEEP_SECONDS = 10 - - -class PredictorRunnerBase: - """Manages state for running predictor. - - Abstract class. Subclasses should override `InputGenerator` and `RunBatch`. - Call `Subclass().Run()` in `main()` function to run. - """ - - def __init__(self, - checkpoint, - output_dir=None, - inference_graph=None, - inference_subgraph_name='', - device_type='cpu', - output_num_shards=1, - output_shard_id=0, - max_inputs=0, - input_id_filter=None, - tf_master='local', - inference_threads=1, - batch_size=64, - prediction_step_interval=3000): - """Constructor. - - Args: - checkpoint: Either a checkpoint file to load, or a directory containing - multiple checkpoints, where the latest checkpoint will be loaded. - output_dir: Output directory. If `checkpoint` is a directory, a - subdirectory will be created for each checkpoint evaluated. - inference_graph: Path to an inference graph. If not specified, will be - inferred from the checkpoint path. - inference_subgraph_name: The name of the inference subgraph to use. - Defaults to the default subgraph. - device_type: Device type, either cpu, gpu, or tpu. - output_num_shards: Each replica generates one shard of output according to - `output_shard_id`. - output_shard_id: The output shard id in range `[0, output_num_shards - - 1]`. - max_inputs: Only process the first n inputs. 0 means process all inputs. - input_id_filter: If not empty, only process the input ids in the given - list. - tf_master: tf_master for predictor session. - inference_threads: Number of inference threads. - batch_size: Batch size. - prediction_step_interval: Number of steps between outputs. Only meaningful - if `checkpoint` is a directory. - """ - self._checkpoint = checkpoint - self._output_dir = output_dir - self._output_num_shards = output_num_shards - self._output_shard_id = output_shard_id - self._max_inputs = max_inputs - input_id_filter = input_id_filter or [] - self._input_id_filter = [str(x) for x in input_id_filter] - self._batch_size = batch_size - self._prediction_step_interval = prediction_step_interval - - if device_type == 'tpu' and FLAGS.xla_device != 'tpu': - raise ValueError('xla_device=tpu should be set with device_type=tpu!') - - if tf.io.gfile.isdir(self._checkpoint): - initial_checkpoint = tf.train.latest_checkpoint(self._checkpoint) - while (not initial_checkpoint or - not tf.io.gfile.exists(initial_checkpoint + '.index')): - tf.logging.log_first_n(tf.logging.INFO, - 'Waiting for checkpoint to be available.', - 10) - time.sleep(_RETRY_SLEEP_SECONDS) - initial_checkpoint = tf.train.latest_checkpoint(self._checkpoint) - else: - initial_checkpoint = self._checkpoint - if not tf.io.gfile.exists(initial_checkpoint + '.index'): - raise ValueError('Could not find checkpoint %s' % initial_checkpoint) - - # Use saved inference graph. - if inference_graph: - self._inference_graph = inference_graph - else: - checkpoint_dir = self._checkpoint - if not tf.io.gfile.isdir(checkpoint_dir): - checkpoint_dir = os.path.dirname(checkpoint_dir) - logdir = os.path.dirname(checkpoint_dir) - inference_graph_filename = 'inference.pbtxt' - if device_type == 'tpu': - inference_graph_filename = 'inference_tpu.pbtxt' - self._inference_graph = os.path.join(logdir, 'inference_graphs', - inference_graph_filename) - self._predictor = predictor.Predictor( - inference_graph=self._inference_graph, - subgraph_name=inference_subgraph_name, - checkpoint=initial_checkpoint, - device_type=device_type, - tf_master=tf_master) - self._threadpool = concurrent.futures.ThreadPoolExecutor(inference_threads) - self._locks = [threading.Lock() for _ in range(inference_threads)] - - @classmethod - def FromFlags(cls, **kwargs): - """Constructs an instance of this class from FLAGS.""" - return cls( - checkpoint=FLAGS.checkpoint, - output_dir=FLAGS.output_dir, - inference_graph=FLAGS.inference_graph, - inference_subgraph_name=FLAGS.inference_subgraph_name, - device_type=FLAGS.device_type, - output_num_shards=FLAGS.output_num_shards, - output_shard_id=FLAGS.output_shard_id, - max_inputs=FLAGS.max_inputs, - input_id_filter=FLAGS.input_id_filter, - tf_master=FLAGS.tf_master, - inference_threads=FLAGS.inference_threads, - batch_size=FLAGS.batch_size, - prediction_step_interval=FLAGS.prediction_step_interval, - **kwargs) - - def _ShouldProcessInputId(self, input_id): - if self._max_inputs > 0 and input_id >= self._max_inputs: - return False - if self._input_id_filter and str(input_id) not in self._input_id_filter: - return False - return input_id % self._output_num_shards == self._output_shard_id - - def _OutputFilename(self, output_dir, name): - assert self._output_shard_id >= 0 - assert self._output_shard_id < self._output_num_shards - return '%s-%.5d-of-%.5d' % (os.path.join( - output_dir, name), self._output_shard_id, self._output_num_shards) - - def InputGenerator(self): - """Generator that yields the next input. - - Must yield in a deterministic order or raise an exception when - self._output_num_shards > 1. - """ - raise NotImplementedError('Abstract method.') - - def RunBatch(self, output_dir, batch): - """Runs predictor on a single batch of data. - - Args: - output_dir: the output directory. - batch: a list of (input_id, element) pairs, where element is yielded from - InputGenerator and input_id is a unique counter starting from 0. - """ - raise NotImplementedError('Abstract method.') - - def _PredictOneCheckpoint(self, checkpoint, output_dir): - """Runs predictor.""" - tf.logging.info('Processing checkpoint %s.', checkpoint) - self._predictor.Load(checkpoint) - - def LockedRunBatch(batch, batch_id): - """TPU inference runs the i-th batch on the i%num_cores-th core. - - Make sure that core is available before scheduling the next batch on it. - - Args: - batch: The input to be passed to RunBatch. - batch_id: The id of this batch, which determins which core it runs on. - """ - with self._locks[batch_id % len(self._locks)]: - self.RunBatch(output_dir, batch) - - batch_id = 0 - batch = [] - futures = [] - # Iterate through the input and process it one batch at a time. - it = self.InputGenerator() - if self._max_inputs > 0: - it = itertools.islice(it, self._max_inputs) - for next_id, element in enumerate(it): - if self._ShouldProcessInputId(next_id): - batch.append((next_id, element)) - if len(batch) == self._batch_size: - futures.append( - self._threadpool.submit(LockedRunBatch, batch, batch_id)) - batch_id += 1 - batch = [] - # Last batch. - if batch: - futures.append(self._threadpool.submit(LockedRunBatch, batch, batch_id)) - # Wait for completion. - for f in futures: - f.result() - - def _PredictContinuously(self): - """Waits for new checkpoints and runs predictor continuously.""" - prev_step = -1000000 - while True: - # TODO(jonathanasdf): how to determine when training finished? - path = tf.train.latest_checkpoint(self._checkpoint) - step_str = re.search(r'ckpt-(\d{8})', six.ensure_str(path)).group(1) - step = int(step_str) - if step - prev_step >= self._prediction_step_interval: - if not self._output_dir: - raise ValueError( - 'output_dir must be specified for _PredictContinuously.') - output_dir = os.path.join(self._output_dir, 'step_' + step_str) - tf.io.gfile.makedirs(output_dir) - self._PredictOneCheckpoint(path, output_dir) - prev_step = step - tf.logging.info('Waiting for next checkpoint...') - time.sleep(_RETRY_SLEEP_SECONDS) - - @py_utils.RetryOnTransientTfError() - def Run(self): - """Monitor checkpoints and runs predictor.""" - if self._output_dir: - tf.io.gfile.makedirs(self._output_dir) - if tf.io.gfile.isdir(self._checkpoint): - self._PredictContinuously() - else: - self._PredictOneCheckpoint(self._checkpoint, self._output_dir) diff --git a/lingvo/core/predictor_test.py b/lingvo/core/predictor_test.py deleted file mode 100644 index b3c8cc62..00000000 --- a/lingvo/core/predictor_test.py +++ /dev/null @@ -1,81 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.core.predictor.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import inference_graph_exporter -from lingvo.core import inference_graph_pb2 -from lingvo.core import predictor -from lingvo.core import test_utils - - -class DummyModel(base_model.BaseTask): - - def Inference(self): - with tf.name_scope('inference'): - feed1 = tf.placeholder(name='feed1_node', dtype=tf.float32, shape=[1]) - fetch1 = tf.identity(feed1, name='fetch1_node') - inference_graph = inference_graph_pb2.InferenceGraph() - subgraph = inference_graph.subgraphs['default'] - subgraph.feeds['feed1'] = feed1.name - subgraph.fetches['fetch1'] = fetch1.name - return inference_graph - - -class PredictorTest(test_utils.TestCase): - - def _testInferenceGraph(self): - p = base_model.SingleTaskModel.Params(DummyModel.Params().Set(name='test')) - p.input = base_input_generator.BaseInputGenerator.Params().Set(name='test') - inference_graph = inference_graph_exporter.InferenceGraphExporter.Export(p) - return inference_graph - - def testPredictorFeedShapes(self): - pred = predictor.Predictor(self._testInferenceGraph()) - self.assertEqual([1], pred.feed_shapes.feed1) - - def testPredictorFetchShapes(self): - pred = predictor.Predictor(self._testInferenceGraph()) - self.assertEqual([1], pred.fetch_shapes.fetch1) - - def testPredictor(self): - pred = predictor.Predictor(self._testInferenceGraph()) - fetch1 = pred.Run('fetch1', feed1=[12345]) - self.assertEqual(12345, fetch1) - - def testMissingFeedRaisesInvalidArgumentError(self): - pred = predictor.Predictor(self._testInferenceGraph()) - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'feed1'): - pred.Run('fetch1') - - def testInvalidFetchRaisesKeyError(self): - pred = predictor.Predictor(self._testInferenceGraph()) - with self.assertRaisesRegex(KeyError, 'nonexistent'): - pred.Run(['fetch1', 'nonexistent'], feed1=[12345]) - - def testInvalidFetchWithoutValidateFetchesReturnsNone(self): - pred = predictor.Predictor(self._testInferenceGraph()) - fetch1, nonexistent = pred.Run(['fetch1', 'nonexistent'], - feed1=[12345], - validate_fetches=False) - self.assertEqual(12345, fetch1) - self.assertIsNone(nonexistent) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/program.py b/lingvo/core/program.py deleted file mode 100644 index 42b8c73d..00000000 --- a/lingvo/core/program.py +++ /dev/null @@ -1,1201 +0,0 @@ -# Lint as: python3 -# Lint as: python2, python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Programs for interleaving execution on TPU.""" - -import multiprocessing.dummy -import os -import time - -import lingvo.compat as tf -from lingvo.core import base_model -from lingvo.core import checkpointer -from lingvo.core import cluster_factory -from lingvo.core import hyperparams -from lingvo.core import metrics -from lingvo.core import ml_perf_log as mlp_log -from lingvo.core import py_utils -from lingvo.core import summary_utils - -# pylint:disable=g-direct-tensorflow-import -from tensorflow.core.protobuf.tpu import compilation_result_pb2 as tpu_compilation_result -from tensorflow.python.tpu import tpu -from tensorflow.python.tpu import tpu_function -from tensorflow.python.tpu import training_loop as tpu_training_loop -from tensorflow.python.tpu.ops import tpu_ops -# pylint:enable=g-direct-tensorflow-import - - -class BaseProgram: - """A Program associated with a Task. - - This is inspired by the "program" multi-tenancy that TPUs - support. Essentially, each program corresponds with a - sub-graph can exist in the same Graph/Session. - - Upon first execution, it is XLA/JIT compiled and is subsequently - available to be executed on demand without significant delay. - - Program's provides the following functionality: - - - Builds a sub-graph - - Writes summaries - - Runs for pre-determined `steps_per_loop` steps with appropriate infeeds - """ - - @classmethod - def Params(cls): - """"Defaults parameters for Programs.""" - p = hyperparams.InstantiableParams(cls) - p.Define('task', None, 'Underlying task') - p.Define('logdir', None, 'Log directory') - p.Define('num_splits_per_client', None, '') - p.Define('steps_per_loop', None, 'Number of steps to run.') - p.Define('dataset_name', None, - 'Dataset the program is operating on, eg: "Test"') - p.Define('name', 'base_program', 'Program name.') - p.Define('task_name', None, - 'If multi-task, what the high-level task name is') - p.Define('num_threads', 1, 'Number of threads in multiprocessing pool.') - p.Define('spmd', False, 'Whether program is running under SPMD mode.') - return p - - def __init__(self, params, shared_model=None): - self.params = params.Copy() - p = self.params - self._task_params = p.task - self._logdir = p.logdir - self._task_name = p.task_name - self._program_name = '' - self._shared_model = shared_model - - # Program dirs are where the summaries are written to. - if p.task_name: - program_dir_name = ( - p.task_name + '_' + p.name + '_' + p.dataset_name.lower()) - else: - program_dir_name = p.name + '_' + p.dataset_name.lower() - self._program_dir = os.path.join(self._logdir, program_dir_name) - # Initialized on use; access via self._summary_writer property only. - self._summary_writer_obj = None - - tf.io.gfile.makedirs(self._logdir) - # Just a standard spot that all programs may restore from. - self._checkpoint_dir = os.path.join(self._logdir, 'train') - tf.io.gfile.makedirs(self._checkpoint_dir) - - self._steps_per_loop = p.steps_per_loop - self.num_splits_per_client = p.num_splits_per_client - self.data_parallelism = p.num_splits_per_client - - # Thread Pool for infeed. - self._infeed_pool = multiprocessing.dummy.Pool(p.num_threads) - - self._compile_op = None - self._status_msg_fn = None - - @property - def _summary_writer(self): - """Returns the FileWriter object to use for summaries.""" - # Initialize on first use, so that subclasses can override the - # implementation without creating a default FileWriter in the constructor. - if self._summary_writer_obj is None: - self._summary_writer_obj = tf.summary.FileWriter(self._program_dir) - return self._summary_writer_obj - - def _SummarizeValue(self, steps, tag, value): - self._summary_writer.add_summary( - metrics.CreateScalarSummary(tag, value), steps) - - def _WriteSummaries(self, job_name, global_step, summaries): - """Write summaries to be viewed by TensorBoard. - - Args: - job_name: The name of this job ('trainer', 'evaler', etc.) - global_step: Integer number of trainer steps (not a tensor). - summaries: Dict of {summary_name: tf.Summary()}. - """ - if not summaries: - return - for unused_name, summary in sorted(summaries.items()): - self._summary_writer.add_summary(summary, global_step) - if summary.value: - for value in summary.value: - if value.HasField('simple_value'): - tf.logging.info('%s summary on checkpoint@%d %s = %.8g', job_name, - global_step, value.tag, value.simple_value) - self._summary_writer.flush() - - def _InfeedLoop(self, sess): - tf.logging.info('_InfeedLoop start') - try: - for i in range(self._steps_per_loop): - tf.logging.vlog(1, '_InfeedLoop %d', i) - sess.run(self._task.input.tpu_infeed_op) - # Hmm... so how does self._steps_per_loop get set? - tf.logging.info('_InfeedLoop done') - except Exception as e: - tf.logging.info('_InfeedLoop exception %r %s', e, e) - raise - - def BuildTpuSubgraph(self): - """Sub classes should construct a model/graph to be executed by Run. - - Specific to TPU execution, this may involve a - @tpu_function.on_device_training_loop etc. - """ - raise NotImplementedError() - - def SetStatusMessageFn(self, fn): - """Workaround since we instantiate programs via Params.""" - self._status_msg_fn = fn - - def SetStatusMessage(self, msg): - """Write to borglet status.""" - tf.logging.info('Status: %s', msg) - if self._status_msg_fn: - self._status_msg_fn(msg) - - def Compile(self, sess): - """Compile the program using the given session handle.""" - if self._compile_op is not None: - self.SetStatusMessage('Compiling %s' % self._program_name) - result = sess.run(self._compile_op) - proto = tpu_compilation_result.CompilationResultProto() - proto.ParseFromString(result) - if proto.status_error_message: - tf.logging.fatal('Compilation failed: {}'.format( - proto.status_error_message)) - tf.logging.info('Compiling %s done.', self._program_name) - - def Run(self, sess): - """Execute the program using the given session handle. - - Args: - sess: TF Session. - - Returns: - done: Whether to end all execution. - """ - raise NotImplementedError() - - def CreateCheckpointer(self): - self._checkpointer = checkpointer.Checkpointer(self._checkpoint_dir, - self._model) - - def RestoreIfNeeded(self, sess): - self._checkpointer.RestoreIfNeeded(sess) - - def _InstantiateTaskModel(self, task_params): - """Instantiates a model object for a particular task. - - MultiTaskModels can accept a shared_model parameter, but SingleTaskModels - cannot, so we handle them separately here. - - Args: - task_params: An params instance that constructs either a SingleTaskModel - or a MultiTaskSubModel. - - Returns: - An instantiated object based on task_params. - """ - if issubclass(task_params.cls, base_model.MultiTaskSubModel): - return task_params.Instantiate(shared_model=self._shared_model) - return task_params.Instantiate() - - -class TrainProgram(BaseProgram): - """TrainProgram trains a single task and handles checkpoints.""" - - def __init__(self, params, shared_model=None): - super().__init__(params, shared_model=shared_model) - self._step_rate_tracker = summary_utils.StepRateTracker() - self._program_name = 'TrainProgram' - - def _OutfeedEnqueue(self, per_example_tensors): - if not per_example_tensors: - return tf.no_op() - per_example_tensors = py_utils.NestedMap(per_example_tensors) - device = tpu.core(0) if self.spmd else '' - with tf.device(device): - return tpu_ops.outfeed_enqueue_tuple(per_example_tensors.Flatten()) - - def _OutfeedDequeueLoop(self, per_example_tensors, num_loops, num_devices): - """Process all per-example tensor outfeed data for a TPU sess.run. - - Args: - per_example_tensors: dict of key -> tensor as generated by TpuTrainStep. - num_loops: number of times that TpuTrainStep will be executed by TpuTrain. - num_devices: number of TPU cores assigned to this process. - - Returns: - A dict of per-example tensors from the latest TpuTrainStep. - """ - if not per_example_tensors: - return tf.no_op() - - tensor_shapes = [ - py_utils.GetShape(per_example_tensors[key]) - for key in sorted(per_example_tensors) - ] - tensor_types = [ - tf.as_dtype(per_example_tensors[key].dtype) - for key in sorted(per_example_tensors) - ] - - def LoopBody(i, *input_arrays): - """Process outfeed data for a single TpuTrainStep. - - Args: - i: current loop index. - *input_arrays: One tf.TensorArray per outfeed tensor. - - Returns: - i+1 (new index) plus post-write tf.TensorArray handles. - """ - # Outfeed ops execute on each JF node, so they must be located on the - # nodes. - outfeed_devices = [] - device_assignment = py_utils.GetTpuDeviceAssignment() - assert device_assignment - for replica in range(device_assignment.num_replicas): - num_cores_per_replica = 1 if self.spmd else ( - device_assignment.num_cores_per_replica) - for core in range(num_cores_per_replica): - with tf.device(device_assignment.host_device(replica, core)): - outfeed_devices.append( - tpu_ops.outfeed_dequeue_tuple( - tensor_types, - tensor_shapes, - device_ordinal=device_assignment.tpu_ordinal(replica, - core))) - offset = i * num_devices - output_arrays = list(input_arrays) - # Each output_array holds a different per-example tensor. We get results - # for each tensor from each TPU for each TpuTrainStep call. - for j in range(len(output_arrays)): - for k in range(len(outfeed_devices)): - output_arrays[j] = output_arrays[j].write(offset + k, - outfeed_devices[k][j]) - - return tuple([i + 1] + output_arrays) - - def LoopCond(i, *output_arrays): - del output_arrays - return i < num_loops - - output_arrays = [] - for i in range(len(tensor_shapes)): - output_arrays.append( - tf.TensorArray( - tensor_types[i], - size=num_loops * num_devices, - element_shape=tensor_shapes[i])) - # Loop once for each time that TpuTrainStep runs. - output_arrays = tf.while_loop( - LoopCond, LoopBody, [0] + output_arrays, parallel_iterations=1)[1:] - concatenated_arrays = [array.concat() for array in output_arrays] - return dict(zip(sorted(per_example_tensors), concatenated_arrays)) - - def BuildTpuSubgraph(self): - tf.logging.info('TrainProgram BuildTpuSubGraph') - self.spmd = ( - self.params.spmd or - self._task_params.input.use_partitioned_infeed_queue) - - self._eval_metrics = metrics.TpuEvalMetrics() - data_parallelism = self.data_parallelism - - with cluster_factory.SetImmediatelyInstantiateVariables(False): - self._model = self._InstantiateTaskModel(self._task_params) - self._task = self._model.GetTask() - self._task.input.InstantiateVariables() - self._task.input.CreateTpuEnqueueOps() - - def TpuTrainStep(*args): - """Train a shard of a batch on a single TPU core. - - Args: - *args: metrics values from previous steps. - - Returns: - New summed metrics values and a train_op. - """ - with tf.name_scope('tpu_train'): - with py_utils.OpportunisticVariableReuseScope(True): - self._model.InstantiateVariables() - self._model.ConstructFPropBPropGraph() - per_step_eval_metrics = self._eval_metrics.SetMetrics( - self._task.eval_metrics, args) - outfeed_op = self._OutfeedEnqueue(self._task.per_example_tensors) - summed_metrics = [] - assert len(per_step_eval_metrics) == len(args) - with tf.control_dependencies([outfeed_op]): - for x, y in zip(per_step_eval_metrics, args): - summed_metrics.append(x + y) - return summed_metrics + [self._task.train_op] - - @tpu_function.on_device_training_loop - def TpuTrain(): - loop_result = tpu_training_loop.repeat( - self._steps_per_loop, - TpuTrainStep, - inputs=self._eval_metrics.initial_values, - name='train_loop') - # Final metrics are the avg across self._steps_per_loop steps. - return self._eval_metrics.FinalizeMetrics(loop_result) - - self._compile_op, batch_parallel_res = tpu.split_compile_and_shard( - TpuTrain, - num_shards=data_parallelism, - device_assignment=py_utils.GetTpuDeviceAssignment()) - outfeed_dequeue_op = self._OutfeedDequeueLoop( - self._task.per_example_tensors, self._steps_per_loop, - self.num_splits_per_client) - - # Get metric result from a single replica; they are all same here. - - def _ConstructPostTrainingLoop(train_loop_op, outfeed_dequeue_op): - """Returns the op for tpu training with tail cpu computation.""" - # Adds a tail computation that is run after the tpu_training loop - # step finishes. This allows us to run certain computation that - # acts on the variable between tpu_train_loop iterations and - # amortizing the cost of the operations. Alternative of running - # tpu.outside_compilation & using tf.cond is expenseive. - with tf.control_dependencies(train_loop_op): - self._model.ConstructPostTrainingLoop() - with tf.control_dependencies([self._task.post_training_loop_op]): - return ([[tf.identity(o) for o in train_loop_op], outfeed_dequeue_op]) - - # Get metric result from a single replica; they are all same here. - all_tpu_ops = [t[0] for t in batch_parallel_res] - self.tpu_ops = (_ConstructPostTrainingLoop(all_tpu_ops, outfeed_dequeue_op)) - self._model_analysis, self._total_num_params = summary_utils.ModelAnalysis( - self._model) - try: - with tf.io.gfile.GFile( - os.path.join(self._program_dir, 'model_analysis.txt'), 'w') as f: - f.write(self._model_analysis) - except tf.errors.NotFoundError as e: - tf.logging.info('Failed to write model analysis %s', e) - - return self.tpu_ops - - def Run(self, sess): - global_step = sess.run(self._model.global_step) - self.SetStatusMessage('Executing train program at step %d' % global_step) - infeed_future = self._infeed_pool.apply_async( - self._InfeedLoop, args=(sess,)) - ary = sess.run(self.tpu_ops) - infeed_future.wait() - - values = ary[0] - outfeeds = ary[1] - - self._eval_metrics.PackMetricsValues(values) - eval_metrics = self._eval_metrics.metrics - - global_step = sess.run(self._model.global_step) - step_rate, example_rate, total_examples = ( - self._step_rate_tracker.ComputeStepRate( - global_step, - eval_metrics['num_samples_in_batch'][0] * self._steps_per_loop)) - self._SummarizeValue(global_step, 'global_step/sec', step_rate) - self._SummarizeValue(global_step, 'examples/sec', example_rate) - self._SummarizeValue(global_step, 'total_samples', total_examples) - self._SummarizeValue(global_step, 'total_num_params', - self._total_num_params) - for key, (val, _) in sorted(eval_metrics.items()): - self._SummarizeValue(global_step, key, val) - - task_global_step = sess.run(self._task.global_step) - summaries = self._task.ProcessFPropResults(sess, task_global_step, - eval_metrics, outfeeds) - self._WriteSummaries( - os.path.basename(self._program_dir), global_step, summaries) - - # Simpler version of _ShouldStop without early stopping. - if task_global_step >= self._task_params.train.max_steps: - tf.logging.info('ShouldStop: step:%6d params.train.max_steps:%6d', - task_global_step, self._task_params.train.max_steps) - return True - - return False - - -class EvalProgram(BaseProgram): - """Evaluation program. - - Note that this currently has different infeed semantics compared to - the existing Evaler as the input generator is not recreated - per-eval. Thus different random samples are selected each - evaluation. - """ - - def __init__(self, params, shared_model=None): - super().__init__(params, shared_model=shared_model) - self._program_name = 'EvalProgram' - - def BuildTpuSubgraph(self): - tf.logging.info('EvalProgram BuildTpuSubGraph') - with cluster_factory.SetEval(True): - self._eval_metrics = metrics.TpuEvalMetrics() - data_parallelism = self.data_parallelism - with cluster_factory.SetImmediatelyInstantiateVariables(False): - self._model = self._InstantiateTaskModel(self._task_params) - self._task = self._model.GetTask() - self._task.input.InstantiateVariables() - self._task.input.CreateTpuEnqueueOps() - - def TpuEvalStep(*args): - """Eval a shard of a batch on a single TPU core. - - Args: - *args: metrics values from previous steps. - - Returns: - Summed eval metrics. - """ - with tf.name_scope('tpu_eval'): - with py_utils.OpportunisticVariableReuseScope(True): - self._model.InstantiateVariables() - self._model.ConstructFPropGraph() - per_step_eval_metrics = self._eval_metrics.SetMetrics( - self._task.eval_metrics, args) - summed_metrics = [] - for x, y in zip(per_step_eval_metrics, args): - summed_metrics.append(x + y) - return summed_metrics - - @tpu_function.on_device_training_loop - def TpuEval(): - loop_result = tpu_training_loop.repeat( - self._steps_per_loop, - TpuEvalStep, - inputs=self._eval_metrics.initial_values, - name='eval_loop') - # Final metrics are the avg across self._steps_per_loop steps. - return self._eval_metrics.FinalizeMetrics(loop_result) - - self._compile_op, batch_parallel_res = tpu.split_compile_and_shard( - TpuEval, - num_shards=data_parallelism, - device_assignment=py_utils.GetTpuDeviceAssignment()) - # Get metric result from a single replica; they are all same here. - self.tpu_ops = [[t[0] for t in batch_parallel_res]] - - return self.tpu_ops - - def Run(self, sess): - global_step = sess.run(self._model.global_step) - self.SetStatusMessage('Executing eval program at step %d' % global_step) - - infeed_future = self._infeed_pool.apply_async( - self._InfeedLoop, args=(sess,)) - ary = sess.run(self.tpu_ops) - infeed_future.wait() - values = ary[0] - self._eval_metrics.PackMetricsValues(values) - for key, (val, _) in sorted(self._eval_metrics.metrics.items()): - self._SummarizeValue(global_step, key, val) - self._summary_writer.flush() - return False - - -class DecodeProgram(BaseProgram): - """DecodeProgram. - - Note that this currently has different infeed semantics compared to - the existing Decoder as the input generator is not recreated - per-eval. Thus different random samples are selected each - decoder run. - """ - - def __init__(self, params, shared_model=None): - super().__init__(params, shared_model=shared_model) - self._program_name = 'DecodeProgram' - - def _CompileDecodeFn(self): - """Wrap the DecodeFn with split_compile_and_shard.""" - with cluster_factory.SetImmediatelyInstantiateVariables(False): - self._model = self._InstantiateTaskModel(self._task_params) - self._task = self._model.GetTask() - self._task.input.InstantiateVariables() - self._task.input.CreateTpuEnqueueOps() - - def _DecodeFn(): - """Decode call to be compiled for TPU.""" - with py_utils.OpportunisticVariableReuseScope(True): - self._model.InstantiateVariables() - input_batch = self._task.input.TpuDequeueBatch() - # Bingo! self._task.Decode. This defers to DecodeWithTheta - metrics_dict = self._task.Decode(input_batch) - self.metrics_nm = py_utils.NestedMap(metrics_dict) - return self.metrics_nm.Flatten() - - self._compile_op, batch_parallel_res = tpu.split_compile_and_shard( - _DecodeFn, - num_shards=self.data_parallelism, - device_assignment=py_utils.GetTpuDeviceAssignment()) - - self.metrics = py_utils.NestedMap(self.metrics_nm) - self.metrics = self.metrics.Pack(batch_parallel_res) - - def BuildTpuSubgraph(self): - tf.logging.info('DecodeProgram BuildTpuSubGraph') - py_utils.ResetStepSeed() - with cluster_factory.SetEval(True): - self._CompileDecodeFn() - return None - - def Run(self, sess): - global_step = sess.run(self._model.global_step) - self.SetStatusMessage('Executing decode program at step %d' % global_step) - infeed_future = self._infeed_pool.apply_async( - self._InfeedLoop, args=(sess,)) - dec_metrics = self._task.CreateDecoderMetrics() - start_time = time.time() - buffered_decode_out = [] - # self._steps_per_loop is tricky to set. - for i in range(self._steps_per_loop): - metrics_values = sess.run(self.metrics) - # It seems like this is the right place to write to disk. PostProcessDecodeOut - decode_out = self._task.PostProcessDecodeOut(metrics_values, dec_metrics) - tf.logging.info('step: %d %f' % - (i, dec_metrics['num_samples_in_batch'].total_value)) - if decode_out: - # So buffered_decode_out should be a list of something. Could cause - buffered_decode_out.extend(decode_out) - infeed_future.wait() - - num_examples_metric = dec_metrics['num_samples_in_batch'] - summaries = {k: v.Summary(k) for k, v in dec_metrics.items()} - elapsed_secs = time.time() - start_time - example_rate = num_examples_metric.total_value / elapsed_secs - summaries['examples/sec'] = tf.Summary( - value=[tf.Summary.Value(tag='examples/sec', simple_value=example_rate)]) - self._WriteSummaries( - os.path.basename(self._program_dir), global_step, summaries) - decode_out_path = os.path.join(self._program_dir, - 'decoder_out_%09d' % global_step) - # Experimental one doesn't have these finalize calls. So let's use non-experimental for now... - decode_finalize_args = base_model.DecodeFinalizeArgs( - decode_out_path=decode_out_path, decode_out=buffered_decode_out) - self._task.DecodeFinalize(decode_finalize_args) - return False - - -# Note: Inherits from DecodeProgram -class ExperimentalDecodeProgram(DecodeProgram): - """DecodeProgram in a tpu loop. - - TODO(huangyp) test this for beam search decoders and replace the - default DecodeProgram. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.num_threads = 2 - return p - - def _CompileDecodeLoop(self): - """Wrap the DecodeLoop with split_compile_and_shard.""" - device_assignment = py_utils.GetTpuDeviceAssignment() - with cluster_factory.SetImmediatelyInstantiateVariables(False): - self._model = self._InstantiateTaskModel(self._task_params) - self._task = self._model.GetTask() - self._task.input.InstantiateVariables() - self._task.input.CreateTpuEnqueueOps() - - def _DecodeStep(): - """Decode call to be compiled for TPU.""" - with py_utils.OpportunisticVariableReuseScope(True): - self._model.InstantiateVariables() - input_batch = self._task.input.TpuDequeueBatch() - # defers to DecodeWithTheta - metrics_dict = self._task.Decode(input_batch) - self.metrics_nm = py_utils.NestedMap(metrics_dict) - device = tpu.core(0) if self.spmd else '' - with tf.device(device): - outfeed_enqueue = tpu_ops.outfeed_enqueue_tuple( - self.metrics_nm.Flatten()) - return [outfeed_enqueue] - - @tpu_function.on_device_training_loop - def DecodeLoopFn(): - return tpu_training_loop.repeat( - self._steps_per_loop, _DecodeStep, inputs=[]) - - self._compile_op, self.decode_loop = tpu.split_compile_and_shard( - DecodeLoopFn, - num_shards=self.data_parallelism, - device_assignment=device_assignment) - - # Get a list of outfeed ops. - self.metrics = self._OutfeedDequeue() - # Pack the list of outfeed ops with structure in self.metrics_nm. - self.metrics = tf.nest.pack_sequence_as(self.metrics_nm, self.metrics) - - def BuildTpuSubgraph(self): - tf.logging.info('DecodeProgram BuildTpuSubGraph') - py_utils.ResetStepSeed() - self.spmd = ( - self.params.spmd or - self._task_params.input.use_partitioned_infeed_queue) - with cluster_factory.SetEval(True): - self._CompileDecodeLoop() - return - - def _OutfeedDequeue(self): - """Collect outfeed dequeue from all devices.""" - num_outfeeds = len(self.metrics_nm.Flatten()) - outfeed_ops = [[]] * num_outfeeds - device_assignment = py_utils.GetTpuDeviceAssignment() - assert device_assignment - for replica in range(device_assignment.num_replicas): - num_cores_per_replica = 1 if self.spmd else ( - device_assignment.num_cores_per_replica) - for core in range(num_cores_per_replica): - with tf.device(device_assignment.host_device(replica, core)): - outfeeds_per_core = tpu_ops.outfeed_dequeue_tuple( - dtypes=[x.dtype for x in self.metrics_nm.Flatten()], - shapes=[x.shape for x in self.metrics_nm.Flatten()], - device_ordinal=device_assignment.tpu_ordinal(replica, core)) - for idx_outfeed, out_feed in enumerate(outfeeds_per_core): - outfeed_ops[idx_outfeed] = outfeed_ops[idx_outfeed] + [out_feed] - return [tf.concat(per_outfeed, 0) for per_outfeed in outfeed_ops] - - def _DecodeLoop(self, sess): - sess.run(self.decode_loop) - - def Run(self, sess): - global_step = sess.run(self._model.global_step) - self.SetStatusMessage('Executing decode program at step %d' % global_step) - infeed_future = self._infeed_pool.apply_async( - self._InfeedLoop, args=(sess,)) - decode_future = self._infeed_pool.apply_async( - self._DecodeLoop, args=(sess,)) - - dec_metrics = self._task.CreateDecoderMetrics() - start_time = time.time() - # set this very high - for _ in range(self._steps_per_loop): - metrics_values = sess.run(self.metrics) - self._task.PostProcessDecodeOut(metrics_values, dec_metrics) - decode_future.wait() - infeed_future.wait() - summaries = {k: v.Summary(k) for k, v in dec_metrics.items()} - elapsed_secs = time.time() - start_time - num_examples_metric = dec_metrics['num_samples_in_batch'] - example_rate = num_examples_metric.total_value / elapsed_secs - summaries['examples/sec'] = tf.Summary( - value=[tf.Summary.Value(tag='examples/sec', simple_value=example_rate)]) - self._WriteSummaries( - os.path.basename(self._program_dir), global_step, summaries) - - return False - - -class MLPerfTrainDecodeProgram(BaseProgram): - """Run train/decode in a single session run.""" - - @classmethod - def Params(cls): - """"Defaults parameters for Programs.""" - p = super().Params() - p.Define('train_task', None, 'Underlying task') - p.Define('decode_task', None, 'Underlying task') - p.Define('train_dataset_name', None, '') - p.Define('decode_dataset_name', None, '') - p.Define('train_steps_per_loop', 0, '') - p.Define('decode_steps_per_loop', 0, '') - p.Define('ml_perf', None, 'MLPerf config') - return p - - def __init__(self, params, shared_model=None): - super().__init__(params, shared_model=shared_model) - p = self.params - if p.ml_perf is not None and p.ml_perf.benchmark_name is not None: - self._ml_perf_log = True - self._ml_perf = p.ml_perf - self._ml_perf_epoch = -1 - else: - self._ml_perf_log = False - self._program_name = 'TrainAndDecodeProgram' - self._train_steps_per_loop = params.train_steps_per_loop - self._decode_steps_per_loop = params.decode_steps_per_loop - assert self._decode_steps_per_loop == 1, ('Only supports a single decode ' - 'step right now.') - self._train_task_params = params.train_task - self._decode_task_params = params.decode_task - self._run_start = None - self._run_stop = None - self._train_pool = multiprocessing.dummy.Pool(1) - self._warmup_seconds = 60 - - def BuildTpuSubgraph(self): - if self._ml_perf_log: - mlp_log.mlperf_print('global_batch_size', self._ml_perf.global_batch_size) - mlp_log.mlperf_print('max_sequence_length', - self._ml_perf.max_sequence_length) - mlp_log.mlperf_print('opt_name', self._ml_perf.optimizer_name) - mlp_log.mlperf_print('opt_base_learning_rate', - self._ml_perf.base_learning_rate) - mlp_log.mlperf_print('opt_learning_rate_warmup_steps', - self._ml_perf.warmup_steps) - - self._eval_metrics = metrics.TpuEvalMetrics() - data_parallelism = self.data_parallelism - with cluster_factory.SetImmediatelyInstantiateVariables(False): - self._train_model = self._train_task_params.Instantiate() - self._train_task = self._train_model.GetTask() - self._train_task.input.InstantiateVariables() - self._train_task.input.CreateTpuEnqueueOps() - self._model = self._train_model - - def TpuTrainStep(): - """Train a shard of a batch on a single TPU core. - - Do not calculate loss metrics. - - Returns: - [train_op]. - """ - with py_utils.OpportunisticVariableReuseScope(True): - self._train_model.InstantiateVariables() - self._train_model.ConstructFPropBPropGraph() - return [self._train_task.train_op] - - def TpuTrain(): - loop_result = tpu_training_loop.repeat( - self._train_steps_per_loop, - TpuTrainStep, - inputs=[], - name='train_loop') - return loop_result - - py_utils.ResetStepSeed() - - with cluster_factory.SetImmediatelyInstantiateVariables(False): - self._decode_model = self._InstantiateTaskModel(self._decode_task_params) - self._decode_task = self._decode_model.GetTask() - self._decode_task.input.InstantiateVariables() - self._decode_task.input.CreateTpuEnqueueOps() - - def _DecodeFn(): - """Decode call to be compiled for TPU.""" - with py_utils.OpportunisticVariableReuseScope(True): - with cluster_factory.SetEval(True): - self._decode_model.InstantiateVariables() - input_batch = self._decode_task.input.TpuDequeueBatch() - metrics_dict = self._decode_task.Decode(input_batch) - self.metrics_nm = py_utils.NestedMap(metrics_dict) - return self.metrics_nm.Flatten() - - @tpu_function.on_device_training_loop - def TrainAndDecode(): - with tf.control_dependencies([TpuTrain()]): - return _DecodeFn() - - self._compile_op, batch_parallel_res = tpu.split_compile_and_shard( - TrainAndDecode, - num_shards=data_parallelism, - device_assignment=py_utils.GetTpuDeviceAssignment()) - - self.metrics = py_utils.NestedMap(self.metrics_nm) - self.metrics = self.metrics.Pack(batch_parallel_res) - return None - - def _InfeedLoop(self, sess): - tf.logging.info('_InfeedLoop start') - try: - for i in range(self._train_steps_per_loop): - tf.logging.vlog(1, '_InfeedLoop %d', i) - sess.run(self._train_task.input.tpu_infeed_op) - if self._ml_perf_log: - mlp_log.mlperf_print( - 'eval_start', - None, - metadata={ - 'first_epoch_num': self._ml_perf_epoch + 1, - 'epoch_count': 1 - }) - for i in range(self._decode_steps_per_loop): - tf.logging.vlog(1, '_InfeedLoop %d', i) - sess.run(self._decode_task.input.tpu_infeed_op) - tf.logging.info('_InfeedLoop done') - except Exception as e: - tf.logging.info('_InfeedLoop exception %r %s', e, e) - raise - - def _TrainAndDecode(self, sess): - metrics_values = sess.run(self.metrics) - self._decode_task.PostProcessDecodeOut(metrics_values, self.dec_metrics) - - def Run(self, sess): - global_step = sess.run(self._model.global_step) - self.dec_metrics = self._decode_task.CreateDecoderMetrics() - # Start TPU program thread. - train_future = self._train_pool.apply_async( - self._TrainAndDecode, args=(sess,)) - - if self._warmup_seconds > 0: - # The first execution of the TPU program has a warm-up - # so we delay feeding data yet as that's when the MLPerf timing - # starts. This way, when we actually infeed, the TPU program - # is immediately ready to execute/dequeue data. - tf.logging.info('Waiting before first infeed.') - time.sleep(self._warmup_seconds) - self._warmup_seconds = 0 - - if self._ml_perf_log: - if not self._run_start: - mlp_log.mlperf_print(key='init_stop', value=None) - self._run_start = mlp_log.mlperf_print(key='run_start', value=None) - steps_per_epoch = self._ml_perf.steps_per_epoch - epoch = int(global_step) // steps_per_epoch - if epoch > self._ml_perf_epoch: - self._ml_perf_epoch = epoch - mlp_log.mlperf_print( - 'block_start', - None, - metadata={ - 'first_epoch_num': epoch + 1, - 'epoch_count': 1 - }) - self.SetStatusMessage('MLPerf epoch: %d' % self._ml_perf_epoch) - # Start infeed thread. - infeed_future = self._infeed_pool.apply_async( - self._InfeedLoop, args=(sess,)) - - infeed_future.wait() - train_future.wait() - - if self._ml_perf_log: - mlp_log.mlperf_print( - 'eval_stop', None, metadata={'epoch_num': (epoch + 1)}) - mlperf_metric = self._ml_perf.decoder_metric_name - mlperf_metric_value = float(self.dec_metrics[mlperf_metric].value) - mlp_log.mlperf_print( - 'eval_accuracy', mlperf_metric_value, metadata={'epoch_num': epoch}) - if mlperf_metric_value > self._ml_perf.decoder_metric_success_threshold: - tf.logging.info('ml_perf_final_threshold: %f exceeded', - self._ml_perf.decoder_metric_success_threshold) - if not self._run_stop: - self._run_stop = mlp_log.mlperf_print( - 'run_stop', None, metadata={'status': 'success'}) - self.SetStatusMessage('MLPerf run_time: %.2f' % - (self._run_stop - self._run_start)) - return True - return False - - -class MultiTaskProgramSchedule: - """Container for ProgramSchedules for a MultiTask model.""" - - @classmethod - def Params(cls): - p = hyperparams.InstantiableParams(cls) - p.Define('program_schedule_dict', None, - 'task_name -> ProgramScheduleParams') - return p - - -class SimpleProgramSchedule: - """A schedule of programs associated with a single task. - - Simple sequence is: - This is the behavior! - Run train_executions_per_eval * train_program - Run all the eval_programs - """ - - @classmethod - def Params(cls): - """Params for a SimpleProgramSchedule.""" - p = hyperparams.InstantiableParams(cls) - p.Define('task_dict', None, 'dataset_name -> task params') - p.Define('task_name', None, 'High level task name') - p.Define('logdir', None, 'Log directory') - p.Define('train_program', None, 'Train program params') - p.Define('train_executions_per_eval', 1, '') - p.Define('eval_programs', [], 'List of eval program params.') - p.Define('num_splits_per_client', None, '') - p.Define('dataset_names', [], 'List of all dataset names.') - - # TODO(blee): Clean these up. - p.Define('ml_perf', hyperparams.Params(), 'MlPerf configuration.') - mlp = p.ml_perf - mlp.Define('benchmark_name', None, 'Benchmark name for compliance log.') - return p - - def __init__(self, params, shared_model=None): - self.params = params.Copy() - p = self.params - self._shared_model = shared_model - - # Propagate run-time parameters to programs: - p.train_program.logdir = p.logdir - if p.train_program.dataset_name not in p.task_dict: - tf.logging.error('could not find %s in %s' % - (p.train_program.dataset_name, p.task_dict)) - print("GALVEZ:task_dict=", list(p.task_dict.keys())) - import sys; sys.stdout.flush() - p.train_program.task = p.task_dict[p.train_program.dataset_name] - p.train_program.num_splits_per_client = p.num_splits_per_client - p.train_program.task_name = p.task_name - - for eval_program_params in p.eval_programs: - eval_program_params.logdir = p.logdir - eval_program_params.task = p.task_dict[eval_program_params.dataset_name] - eval_program_params.task_name = p.task_name - eval_program_params.num_splits_per_client = p.num_splits_per_client - - self.eval_programs = [] - self.train_program = p.train_program.Instantiate(shared_model=shared_model) - for eval_program in p.eval_programs: - self.eval_programs.append( - eval_program.Instantiate(shared_model=shared_model)) - - self._programs = [] - self._programs.append(self.train_program) - self._programs += self.eval_programs - - def Programs(self): - return self._programs - - def Run(self, sess): - p = self.params - for _ in range(p.train_executions_per_eval): - done = self.train_program.Run(sess) - if done: - break - for eval_program in self.eval_programs: - eval_program.Run(sess) - return False - - -def SimpleProgramScheduleForTask(train_dataset_name, - train_steps_per_loop, - eval_dataset_names, - eval_steps_per_loop, - decode_steps_per_loop, - experimental_decoder=False, - train_program_cls=TrainProgram, - eval_program_cls=EvalProgram): - """Convenient helper method for common case. - - Args: - train_dataset_name: Name of the training dataset, eg: 'Train' - train_steps_per_loop: Number of steps to execute the training program. - eval_dataset_names: List of eval dataset_name strings, eg: ['Train']. - eval_steps_per_loop: Number of steps to execute the eval program. - decode_steps_per_loop: Number of steps to execute the decode program. - experimental_decoder: bool. Whether to use experimental deocder which is - placed in a tpu loop. - train_program_cls: The class to use for training programs. Defaults - to TrainProgram. - eval_program_cls: The class to use for eval programs. Defaults to - EvalProgram. - - Returns: - A populated SimpleProgramSchedule.Params() - """ - - program_schedule_params = SimpleProgramSchedule.Params() - train_program_params = train_program_cls.Params() - train_program_params.name = 'train' - train_program_params.steps_per_loop = train_steps_per_loop - train_program_params.dataset_name = train_dataset_name - program_schedule_params.train_program = train_program_params - - program_schedule_params.dataset_names = [] - - for dataset_name in eval_dataset_names: - program_schedule_params.dataset_names.append(dataset_name) - if eval_steps_per_loop > 0: - eval_program_params = eval_program_cls.Params() - eval_program_params.name = 'eval_tpu' - # TODO(blee): This should be derived from the Dataset size. - eval_program_params.steps_per_loop = eval_steps_per_loop - eval_program_params.dataset_name = dataset_name - program_schedule_params.eval_programs.append(eval_program_params) - - if decode_steps_per_loop > 0: - decoder = ( - ExperimentalDecodeProgram if experimental_decoder else DecodeProgram) - decode_program_params = decoder.Params() - decode_program_params.name = 'decode_tpu' - # TODO(blee): This should be derived from the Dataset size. - decode_program_params.steps_per_loop = decode_steps_per_loop - decode_program_params.dataset_name = dataset_name - program_schedule_params.eval_programs.append(decode_program_params) - - return program_schedule_params - -def DecodeProgramSchedule(eval_dataset_names, - decode_steps_per_loop, - experimental_decoder=False, - train_program_cls=TrainProgram): - """Convenient helper method for common case. - - Args: - train_dataset_name: Name of the training dataset, eg: 'Train' - train_steps_per_loop: Number of steps to execute the training program. - eval_dataset_names: List of eval dataset_name strings, eg: ['Train']. - eval_steps_per_loop: Number of steps to execute the eval program. - decode_steps_per_loop: Number of steps to execute the decode program. - experimental_decoder: bool. Whether to use experimental deocder which is - placed in a tpu loop. - train_program_cls: The class to use for training programs. Defaults - to TrainProgram. - eval_program_cls: The class to use for eval programs. Defaults to - EvalProgram. - - Returns: - A populated SimpleProgramSchedule.Params() - """ - - program_schedule_params = SimpleProgramSchedule.Params() - train_program_params = train_program_cls.Params() - train_program_params.name = 'train' - train_program_params.steps_per_loop = 0 - train_program_params.dataset_name = "Train" - program_schedule_params.train_program = train_program_params - program_schedule_params.train_executions_per_eval = 0 - - program_schedule_params.dataset_names = [] - - for dataset_name in eval_dataset_names: - program_schedule_params.dataset_names.append(dataset_name) - if decode_steps_per_loop > 0: - decoder = ( - ExperimentalDecodeProgram if experimental_decoder else DecodeProgram) - decode_program_params = decoder.Params() - # Yes! This is what I want! - decode_program_params.name = 'decode_tpu' - # TODO(blee): This should be derived from the Dataset size. - # GALVEZ: Yes I agree - decode_program_params.steps_per_loop = decode_steps_per_loop - decode_program_params.dataset_name = dataset_name - program_schedule_params.eval_programs.append(decode_program_params) - return program_schedule_params - - -class MLPerfProgramSchedule: - """Program schedule for ML Perf benchmark.""" - - @classmethod - def Params(cls): - """Params for a MLPerfProgramSchedule.""" - p = hyperparams.InstantiableParams(cls) - - p.Define('task_dict', None, 'dataset_name -> task params') - p.Define('task_name', None, 'High level task name') - p.Define('logdir', None, 'Log directory') - p.Define('train_program', None, 'Train program params') - p.Define('train_executions_per_eval', 1, '') - p.Define('dataset_names', [], 'List of all dataset names.') - p.Define('num_splits_per_client', None, '') - - p.Define('ml_perf', hyperparams.Params(), 'MlPerf configuration.') - - mlp = p.ml_perf - mlp.Define('benchmark_name', None, 'Benchmark name for compliance log.') - mlp.Define('decoder_metric_name', None, - 'Name of the decoder metric to report for compliance log.') - mlp.Define('decoder_metric_success_threshold', None, - 'Benchmark run must exceed this value to succeeed.') - mlp.Define('steps_per_epoch', None, 'Number of training steps per epoch.') - mlp.Define('global_batch_size', None, 'Global batch size.') - mlp.Define('max_sequence_length', None, 'Maximum sequence length.') - mlp.Define('optimizer_name', None, 'Optimizer used.') - mlp.Define('base_learning_rate', None, 'Base learning rate.') - mlp.Define('warmup_steps', None, 'Number of warm-up steps.') - - return p - - def __init__(self, params, shared_model=None): - self.params = params.Copy() - p = self.params - self._shared_model = shared_model - - # Propagate run-time parameters to programs: - p.train_program.logdir = p.logdir - if p.train_program.train_dataset_name not in p.task_dict: - tf.logging.error('could not find %s in %s' % - (p.train_program.train_dataset_name, p.task_dict)) - - if p.train_program.decode_dataset_name not in p.task_dict: - tf.logging.error('could not find %s in %s' % - (p.train_program.decode_dataset_name, p.task_dict)) - - p.train_program.train_task = p.task_dict[p.train_program.train_dataset_name] - p.train_program.decode_task = p.task_dict[ - p.train_program.decode_dataset_name] - - p.train_program.num_splits_per_client = p.num_splits_per_client - p.train_program.task_name = p.task_name - p.train_program.ml_perf = p.ml_perf.Copy() - - self.train_program = p.train_program.Instantiate(shared_model=shared_model) - self._programs = [] - self._programs.append(self.train_program) - - def Programs(self): - return self._programs - - def Run(self, sess): - p = self.params - for _ in range(p.train_executions_per_eval): - program_done = self.train_program.Run(sess) - if program_done: - return True - return False - - -def MLPerfProgramScheduleForTask(train_dataset_name, train_steps_per_loop, - decode_dataset_name, decode_steps_per_loop): - """Populate MLPerfProgramSchedule params. - - Args: - train_dataset_name: Name of the training dataset, eg: 'Train'. - train_steps_per_loop: Number of steps to execute the training program. - decode_dataset_name: Eg: 'Test'. - decode_steps_per_loop: Number of steps to execute the decode program. - - Returns: - A populated MLPerfProgramSchedule.Params() - """ - - program_schedule_params = MLPerfProgramSchedule.Params() - train_program_params = MLPerfTrainDecodeProgram.Params() - train_program_params.name = 'train_and_decode' - train_program_params.train_steps_per_loop = train_steps_per_loop - train_program_params.decode_steps_per_loop = decode_steps_per_loop - - train_program_params.dataset_name = train_dataset_name - train_program_params.train_dataset_name = train_dataset_name - train_program_params.decode_dataset_name = decode_dataset_name - - program_schedule_params.train_program = train_program_params - - program_schedule_params.dataset_names = [ - train_dataset_name, decode_dataset_name - ] - - return program_schedule_params diff --git a/lingvo/core/pruning_utils.py b/lingvo/core/pruning_utils.py deleted file mode 100644 index 14a30668..00000000 --- a/lingvo/core/pruning_utils.py +++ /dev/null @@ -1,42 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Utilities for pruning.""" - -import lingvo.compat as tf -from model_pruning.python import pruning - - -def AddToPruningCollections(weight, - mask, - threshold, - gradient=None, - old_weight=None, - old_old_weight=None): - """Add mask, threshold, and weight vars to their respective collections.""" - if mask not in tf.get_collection(pruning.MASK_COLLECTION): - tf.add_to_collection(pruning.WEIGHT_COLLECTION, weight) - tf.add_to_collection(pruning.MASK_COLLECTION, mask) - tf.add_to_collection(pruning.THRESHOLD_COLLECTION, threshold) - - # Add gradient, old_weight, and old_old_weight to collections approximating - # gradient and hessian, where old_weight is the weight tensor one step - # before and old_old_weight is the weight tensor two steps before. - if gradient is not None: - assert old_weight is not None - assert old_old_weight is not None - tf.add_to_collection(pruning.WEIGHT_GRADIENT_COLLECTION, gradient) - tf.add_to_collection(pruning.OLD_WEIGHT_COLLECTION, old_weight) - tf.add_to_collection(pruning.OLD_OLD_WEIGHT_COLLECTION, old_old_weight) diff --git a/lingvo/core/py_utils.py b/lingvo/core/py_utils.py deleted file mode 100644 index ad2a3f46..00000000 --- a/lingvo/core/py_utils.py +++ /dev/null @@ -1,5428 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Common utilities.""" - -# ============================================================================== -# Note: Avoid adding dependencies to py_utils beyond standard python packages -# and tensorflow. -# ============================================================================== - -import collections as py_collections -import contextlib -import functools -import hashlib -import inspect -import math -import numbers -import os -import pkgutil -import re -import threading -import traceback - -import lingvo.compat as tf -from lingvo.core import hyperparams -from lingvo.core import ops -from lingvo.core import retry -from lingvo.core import symbolic -from lingvo.core import tshape -import numpy as np -import six - -# pylint: disable=g-direct-tensorflow-import -from tensorflow.core.framework import node_def_pb2 -from tensorflow.core.protobuf import rewriter_config_pb2 -from tensorflow.python.framework import func_graph -from tensorflow.python.framework import function -from tensorflow.python.ops import init_ops -from tensorflow.python.ops import stateless_random_ops -from tensorflow.python.tpu import topology as tf_topology -from tensorflow.python.tpu import tpu_function -from tensorflow.python.util import deprecation -# pylint: enable=g-direct-tensorflow-import - -tf.flags.DEFINE_bool('enable_asserts', True, - 'If False, we disable all asserts.') - -tf.flags.DEFINE_bool('enable_check_numerics', True, - 'If False, we bypass calls to CheckNumerics.') - -tf.flags.DEFINE_bool('print_debug_tensors', False, - 'Whether to print debug tensors.') - -tf.flags.DEFINE_string( - 'xla_device', '', 'If non-empty, can be cpu, gpu, or tpu (case sensitive)') - -tf.flags.DEFINE_bool( - 'use_resource_var', True, - 'Use ResourceVariable instead of RefVariable; this option is ' - 'enabled by default and will be removed in the future.') - -tf.flags.DEFINE_bool( - 'tpu_compatible', False, 'Create variables in a way compatible with TPU. ' - 'This should be true for any job that will interact ' - 'with variables or a checkpoint that will be produced ' - 'or consumed by TPU') - -tf.flags.DEFINE_bool( - 'tflite_compatible', False, - 'Uses tflite converter-friendly ops at applicable places. This so far ' - '(08/2020) is a only best-effort option.') - -tf.flags.DEFINE_bool( - 'pin_vars_to_cpu', False, - 'Pin variables to cpu:0. This is useful for weight-sharing / multi-core ' - 'inference on TPUs in which TPU core variables are managed via ' - 'TPUPartitionedCallOp.') - -tf.flags.DEFINE_bool( - 'no_identity_on_vars', False, - 'Do not add tf.identity() on vars. This allows TPUPartitionedCallOp to use' - 'variable handles directly for weight-sharing / multi-core ' - 'inference on TPUs.') - -tf.flags.DEFINE_bool('disable_py_utils_debug', False, - 'If True disables all py_utils.Debug() logs.') - -# TODO(laigd): remove these after the migration. -tf.flags.DEFINE_bool('if_use_tf_function', False, - 'If True use tf.function for py_utils.If().') -tf.flags.DEFINE_bool('while_loop_use_tf_function', False, - 'If True use tf.function for py_utils.WhileLoop().') -tf.flags.DEFINE_bool('call_defun_use_tf_function', False, - 'If True use tf.function for py_utils.CallDefun().') - -tf.flags.DEFINE_bool( - 'stateless_vars_init', False, - 'Use stateless TensorFlow random number generators (RNG) (e.g. ' - 'tf.random.stateless_uniform) to initialize variables instead of the ' - 'default ones (e.g. tf.random.uniform). This is useful to make variable ' - 'initialization deterministic on different replicas such as on TPUs, ' - 'since XLA does not fully respect the contract with respect to ' - 'user-specified seeds, when using TensorFlow stateful RNGs.') - -# NOTE: Using absl flags in libraries are frowned upon for several reasons: -# -# 1) They require app.run() or explicit flag parsing, preventing the use of -# these libraries in environments that don't look like normal binaries (colab -# notebooks). -# -# 2) They are process-level globals that cannot be scoped or configured except -# once during binary startup. -# -# Because py_utils is a library, no more flags should be used in this file; the -# existing flags are present for backwards compatibility. Instead, consider -# using a stack-scoped configuration object such as the Cluster object. We guard -# against issue 1 above by using _FromGlobal below, which uses the default value -# of the FLAG even if flags are unparsed. - -FLAGS = tf.flags.FLAGS - - -def _FromGlobal(field_name): - """Get 'field_name' from a global configuration object. - - Currently the global configuration object used is FLAGS, but this may - change to Cluster() or an equivalent stack-scoped config object. - - Args: - field_name: The string field name to look up. - - Returns: - The value associated with the global configuration string 'field_name'. - """ - # TODO(b/145831327): check the field name in the current cluster object. - # If explicitly set, use that value instead of using the FLAG value. - - # Now check the FLAGS object for backwards compatibility. - # - # If not explicitly set, get the field from the FLAGS object. If FLAGS - # have not been parsed yet, the default value of the flag will be used. - return FLAGS[field_name].value - - -ENQUEUE_OPS = '__lingvo_enqueue_ops' - -TPU_EMBEDDING_LOAD_OPS = '__lingvo_tpu_embedding_load_ops' -TPU_EMBEDDING_RETRIEVE_OPS = '__lingvo_tpu_embedding_retrieve_ops' -TPU_EMBEDDING = '__tpu_embedding' -TPU_EMBEDDING_ACTIVATIONS = '__tpu_embedding_activations' - -# pylint: disable=protected-access -deprecation._PRINT_DEPRECATION_WARNINGS = False - -# pylint: enable=protected-access - - -class ThreadLocalStack(threading.local): - - def __init__(self): - super().__init__() - self.stack = [] - - -def Assert(condition, data, *args, **kwargs): - if _FromGlobal('enable_asserts'): - return tf.Assert(condition, data, *args, **kwargs) - else: - return tf.no_op() - - -def assert_equal(*args, **kwargs): # pylint: disable=invalid-name - if _FromGlobal('enable_asserts'): - return tf.assert_equal(*args, **kwargs) - else: - return tf.no_op() - - -def assert_greater_equal(*args, **kwargs): # pylint: disable=invalid-name - if _FromGlobal('enable_asserts'): - return tf.debugging.assert_greater_equal(*args, **kwargs) - else: - return tf.no_op() - - -def assert_greater(*args, **kwargs): # pylint: disable=invalid-name - if _FromGlobal('enable_asserts'): - return tf.assert_greater(*args, **kwargs) - else: - return tf.no_op() - - -def assert_less_equal(*args, **kwargs): # pylint: disable=invalid-name - if _FromGlobal('enable_asserts'): - return tf.debugging.assert_less_equal(*args, **kwargs) - else: - return tf.no_op() - - -def assert_less(*args, **kwargs): # pylint: disable=invalid-name - if _FromGlobal('enable_asserts'): - return tf.assert_less(*args, **kwargs) - else: - return tf.no_op() - - -def assert_between(x, l, r, *args, **kwargs): # pylint: disable=invalid-name - x = tf.convert_to_tensor(x) - l = tf.cast(tf.convert_to_tensor(l), x.dtype) - r = tf.cast(tf.convert_to_tensor(r), x.dtype) - return tf.group([ - assert_greater_equal(x, l, *args, **kwargs), - assert_less(x, r, *args, **kwargs) - ]) - - -def assert_shape_match(*args, **kwargs): # pylint: disable=invalid-name - if _FromGlobal('enable_asserts'): - filepath, line, func, _ = traceback.extract_stack(limit=3)[-2] - kwargs['msg'] = 'LINGVO ASSERT %s:%s(%s)' % (re.sub( - r'.*/', '', filepath), line, func) - return ops.assert_shape_match(*args, **kwargs) - else: - return tf.no_op() - - -def assert_same_dim0(xs, *args, **kwargs): # pylint: disable=invalid-name - if _FromGlobal('enable_asserts'): - return ops.assert_same_dim0(xs, *args, **kwargs) - else: - return tf.no_op() - - -def assert_even_divide(denorm, num): # pylint: disable=invalid-name - """Asserts that denorm is evenly divided by num.""" - denorm = tf.convert_to_tensor(denorm) - num = tf.convert_to_tensor(num) - - if denorm.dtype not in (tf.int32, tf.int64): - raise ValueError('denorminator.dtype is not tf.int32 or tf.int64.') - if num.dtype not in (tf.int32, tf.int64): - raise ValueError('numerator.dtype is not tf.int32 or tf.int64.') - - num = HasShape(num, GetShape(denorm)) - - quo = denorm // num - return assert_equal(quo * num, denorm) - - -def AssertIdShape(expected_ids_shape_pattern, ids_shape, *args): - """Asserts shape expected_ids_shape_pattern matches all other input shapes.""" - - def AssertFn(inputs): - dependencies = [ - assert_shape_match(inputs.ids_shape, inputs.expected_ids_shape_pattern) - ] + [ - assert_shape_match(inputs.ids_shape, x_shape) for x_shape in inputs.args - ] - return with_dependencies(dependencies, inputs.ids_shape) - - inputs = NestedMap( - expected_ids_shape_pattern=expected_ids_shape_pattern, - ids_shape=ids_shape, - args=args) - return CallDefun(AssertFn, Transform(tf.convert_to_tensor, inputs)) - - -def _CheckNumerics(x, message=None, *args, **kwargs): - if x.dtype.is_floating: - if 'name' not in kwargs: - kwargs['name'] = re.sub(r':\d+', '', x.name) + '_CheckNumerics' - return tf.debugging.check_numerics(x, message if message else x.name, *args, - **kwargs) - else: - return x - - -def CheckNumerics(inp, message=None, *args, **kwargs): - """Check numerics for tensors in inp.""" - if not _FromGlobal('enable_check_numerics'): - return inp - if isinstance(inp, list): - return [_CheckNumerics(x, message, *args, **kwargs) for x in inp] - if isinstance(inp, tuple): - return tuple(_CheckNumerics(x, message, *args, **kwargs) for x in inp) - return _CheckNumerics(inp, message, *args, **kwargs) - - -def with_dependencies(dependencies, output_tensor): # pylint: disable=invalid-name - with tf.control_dependencies(dependencies): - return tf.identity(output_tensor) - - -@contextlib.contextmanager -def _PrintOptions(*args, **kwargs): - original = np.get_printoptions() - np.set_printoptions(*args, **kwargs) - try: - yield - finally: - np.set_printoptions(**original) - - -def _Print(name, x): - with _PrintOptions(linewidth=1000): - tf.logging.info('%s = %s', name, np.array_repr(x)) - - -def Log(value, prefix, **kwargs): - """Prints out values of tensors. - - Useful for debugging. E.g., - x = ... a tf.Tensor ... - y = ... a tf.Tensor ... - z = compute(x, y) - z = Log(z, 'debug compute()', x=x, y=y) - - Args: - value: A Tensor. Log happens after this tensor's computed. - prefix: Every tensor is logged with this prefix. - **kwargs: keywords and tensors. Tensors are logged in the sort order of - these keywards. - - Returns: - value is returned. - """ - - # Ensures tensors are printed in order. - last = value - for k in sorted(kwargs): - with tf.control_dependencies([last]): - last = tf.py_func(_Print, [prefix + ' : ' + k, kwargs[k]], []) - with tf.control_dependencies([last]): - return tf.identity(value) - - -def Debug(tensor, message='', enabled=True, summarize=100, more=None): - """Wrapper around tf.Print() and tf.logging.info() to simplify debug printing. - - x = py_utils.Debug(x) - - When the graph is built a regular log info line will be printed: - -DBG- py_utils_test.py:429 x=Tensor(... - - Then when the tensor node is evaluated it will print lines like: - -DBG- py_utils_test.py:429 x Const:0[x.shape=][2 2][x=][[1 2][3 4]] - - WARNING: The code that parses local variable names can fail. E.g. don't write - two Debug() calls on one line or a Debug() call that spans more than one line. - - Args: - tensor: A tensor to print. - message: A message to print. - enabled: To enable the debugging. - summarize: Integer with number of tensor values to print. - more: An optional list of additional tensors. - - Returns: - The tensor. - """ - if not enabled or _FromGlobal('disable_py_utils_debug'): - return tensor - - if more is None: - more = [] - - stack = inspect.stack()[1][0] - caller = inspect.getframeinfo(stack) - - caller_var = '' - caller_more_vars = [] - if caller.code_context: - # Rough and likely to fail. But better than nothing. - caller_var = re.compile(r'Debug\((.*?)(\)|,).*$').search( - caller.code_context[0]).groups()[0] - if more: - more_vars = re.compile(r'more=\[(.*?)\].*$').search( - caller.code_context[0]).groups()[0] - caller_more_vars = more_vars.split(',') - - the_class = '' - if 'self' in stack.f_locals: - the_class = stack.f_locals['self'].__class__.__name__ - header = '-DBG- {}:{}:{}:{} {} '.format( - os.path.basename(caller.filename), the_class, caller.function, - caller.lineno, message) - - info = '{}{}={}'.format(header, caller_var, tensor) - for name, val in zip(caller_more_vars, more): - info += ' {}={}'.format(name.strip(), val) - tf.logging.info(info) - - if isinstance(tensor, tf.Tensor): - tensors = [] - tensors += [tf.constant('{}.shape='.format(caller_var)), tf.shape(tensor)] - for name, val in zip(caller_more_vars, more): - tensors += [tf.constant('{}.shape='.format(name.strip())), tf.shape(val)] - - tensors += [tf.constant('{}='.format(caller_var)), tensor] - for name, val in zip(caller_more_vars, more): - tensors += [tf.constant('{}='.format(name.strip())), val] - - info = '{}{} {}'.format(header, caller_var, tensor.name) - return tf.Print(tensor, tensors, info, summarize=summarize) - - return tensor - - -def _Save(steps, prefix, key, val): - filename = '%s.%08d.%s.npy' % (six.ensure_text(prefix), steps, - six.ensure_text(key)) - with tf.io.gfile.GFile(filename, 'w') as outfile: - np.save(outfile, val) - - -def Save(value, filename_prefix, **kwargs): - """Saves values of tensors into files. - - Useful for debugging. E.g., - x = ... a tf.Tensor ... - y = ... a tf.Tensor ... - z = compute(x, y) - z = Save(z, '/path/tmp', x=x, y=y, z=z) - - Args: - value: A Tensor. Saving happens after this tensor is computed. - filename_prefix: Every tensor is saved with this filename prefix. - **kwargs: keywords and tensors. Tensors are logged in the sort order of - these keywards. - - Returns: - value is returned. - """ - last = value - steps = GetGlobalStep() - for k in sorted(kwargs): - with tf.control_dependencies([last]): - last = tf.py_func(_Save, [steps, filename_prefix, k, kwargs[k]], []) - with tf.control_dependencies([last]): - return tf.identity(value) - - -def HasRank(tensor, expected_rank): - """Syntactic sugar for asserting that tensor has the expected rank.""" - if tensor.shape.ndims is not None and isinstance(expected_rank, int): - assert tensor.shape.ndims == expected_rank, ( - 'Ranks did not match, got %d, ' - 'expected %d') % (tensor.shape.ndims, expected_rank) - return tensor - if _FromGlobal('enable_asserts'): - return with_dependencies([tf.assert_equal(tf.rank(tensor), expected_rank)], - tensor) - else: - return tensor - - -def HasAtLeastRank(tensor, expected_rank): - """Syntactic sugar for asserting that tensor has rank >= expected_rank.""" - if tensor.shape.ndims is not None and isinstance(expected_rank, int): - assert tensor.shape.ndims >= expected_rank, ( - 'Rank of tensor %d did not exceed the expected value %d.') % ( - tensor.shape.ndims, expected_rank) - return tensor - if _FromGlobal('enable_asserts'): - return with_dependencies( - [tf.debugging.assert_greater_equal(tf.rank(tensor), expected_rank)], - tensor) - else: - return tensor - - -def GetRank(tensor): - """Returns tensor's rank as an int if it's available, otherwise a Tensor. - - Args: - tensor: The input tensor. - - Returns: - Either an int or a Tensor for the rank of the input tensor. - """ - if tensor.shape.ndims is not None: - return tensor.shape.ndims # int - else: - return tf.rank(tensor) # Tensor - - -def GetShape(tensor, ndims=None): - """Returns tensor's shape as a list which can be unpacked, unlike tf.shape. - - Tries to return static shape if it's available. Note that this means - some of the outputs will be ints while the rest will be Tensors. - - Args: - tensor: The input tensor. - ndims: If not None, returns the shapes for the first `ndims` dimensions. - """ - tensor = tf.convert_to_tensor(tensor) - dynamic_shape = tf.shape(tensor) - - # Early exit for unranked tensor. - if tensor.shape.ndims is None: - if ndims is None: - return dynamic_shape - else: - return [dynamic_shape[x] for x in range(ndims)] - - # Ranked tensor. - if ndims is None: - ndims = tensor.shape.ndims - else: - ndims = min(ndims, tensor.shape.ndims) - - # Return mixture of static and dynamic dims. - static_shape = tensor.shape.as_list() - shapes = [ - static_shape[x] if static_shape[x] is not None else dynamic_shape[x] - for x in range(ndims) - ] - return shapes - - -def HasShape(tensor, expected_shape, ndims=None): - """Syntactic sugar for asserting that tensor has the expected shape. - - Args: - tensor: A Tensor. - expected_shape: A Python list or a 1D tensor. Elements of expected_shape can - be -1 which indicate that any size is valid for that dimension. - ndims: If not None, check only the first `ndims` dimensions of `tensor`. - Must be equal to the length of `expected_shape` if not None. - - Returns: - The input `tensor` with control dependencies that will raise a runtime - error if dynamic shape checks fail. - - Raises: - ValueError: A value error if the assertion fails at static shape checks. - """ - if not _FromGlobal('enable_asserts'): - return tensor - - filepath, line, func, _ = traceback.extract_stack(limit=3)[-2] - msg = 'LINGVO ASSERT %s:%s(%s)' % (re.sub(r'.*/', '', - filepath), line, func) - - tensor_shape = GetShape(tensor) - if ndims is not None: - tensor_shape = tensor_shape[:ndims] - - # TODO(jngiam): Attempt to switch back to tf.Assert after it has better - # support on GPUs. - assert_op = ops.assert_shape_match(tensor_shape, expected_shape, msg=msg) - - # If expected_shape is a Tensor, then we are unable to perform static checks. - # In this case, we can do a dynamic check and return. - if isinstance(expected_shape, tf.Tensor): - return with_dependencies([assert_op], tensor) - - # Infer ranks from the inputs. - expected_rank = len(expected_shape) - if isinstance(tensor_shape, tf.Tensor): - tensor_rank = tensor.shape.ndims - else: - tensor_rank = len(tensor_shape) - - # If ndims is None, then either one of the ranks should not be None, or they - # should both match. If both ranks are None, then they are both tensors and - # should be caught by the earlier short-circuit. - if ndims is None: - if (tensor_rank is not None) and (expected_rank != tensor_rank): - raise ValueError('Tensor does not match rank of expected shape.\n' - 'Tensor shape: {} Expected shape: {}'.format( - tensor_shape, expected_shape)) - # Both tensors can be assumed to be of same rank. - ndims = expected_rank - else: - if (tensor_rank is not None) and (tensor_rank < ndims): - raise ValueError('Tensor has fewer dimensions than ndims.\n' - 'Tensor shape: {} ndims: {}'.format(tensor_shape, ndims)) - if expected_rank != ndims: - raise ValueError( - 'Expected shape must have number of dimensions equal to ndims.\n' - 'Expected shape: {} ndims: {}'.format(expected_shape, ndims)) - - # Ensure that both tensor_shape and expected_shape are both lists. - tensor_shape = tensor_shape[:ndims] - if isinstance(tensor_shape, tf.Tensor): - tensor_shape = tf.unstack(tensor_shape, num=ndims) - - # Map tf.Dimension values to their held values. - tensor_shape = [ - v.value if isinstance(v, tf.Dimension) else v for v in tensor_shape - ] - expected_shape = [ - v.value if isinstance(v, tf.Dimension) else v for v in expected_shape - ] - - all_static_checks = True - for idx, (dim, expected_dim) in enumerate(zip(tensor_shape, expected_shape)): - if expected_dim == -1: - continue - if isinstance(dim, tf.Tensor) or isinstance(expected_dim, tf.Tensor): - all_static_checks = False - elif dim != expected_dim: - raise ValueError('Tensor does not match expected shape on dimension {}.\n' - 'Tensor shape: {} Expected shape: {}'.format( - idx, tensor_shape, expected_shape)) - - if all_static_checks: - return tf.convert_to_tensor(tensor) - else: - return with_dependencies([assert_op], tensor) - - -def GetSize(tensor): - shape = GetShape(tensor) - if (isinstance(shape, tf.Tensor) or - any([isinstance(x, tf.Tensor) for x in shape])): - return tf.size(tensor) - return np.prod(shape) - - -def CausalSelfAttenPadding(seqlen, dtype): - """Wraps tf.linalg.band_part() for tflite compatibility.""" - if FLAGS.tflite_compatible: - # [N, 1] - rows = tf.expand_dims(tf.range(seqlen), -1) - # [1, N] - cols = tf.expand_dims(tf.range(seqlen), 0) - row_cols = rows - cols - return tf.where(row_cols < 0, tf.ones([seqlen, seqlen], dtype), - tf.zeros([seqlen, seqlen], tf.float32)) - else: - return 1.0 - tf.linalg.band_part( - tf.ones([seqlen, seqlen], dtype=dtype), -1, 0) - - -def use_xla(): # pylint: disable=invalid-name - res = _FromGlobal('xla_device') - if res: - assert res in ('', 'cpu', 'gpu', 'tpu') - return res - - -def use_tpu(): # pylint: disable=invalid-name - res = _FromGlobal('xla_device') == 'tpu' - if res: - assert not _FromGlobal('enable_asserts') # asserts not supported on tpu - return res - - -def tpu_compat(): # pylint: disable=invalid-name - return use_tpu() or _FromGlobal('tpu_compatible') - - -def use_resource_variables(): # pylint: disable=invalid-name - return _FromGlobal('use_resource_var') or tpu_compat() - - -def outside_all_rewrites(): # pylint: disable=invalid-name - return tf.control_dependencies(None) - - -def use_stateless_vars_init(): # pylint: disable=invalid-name - return _FromGlobal('stateless_vars_init') - - -# TODO(jamesqin): remove once b/147439702 is fixed. -_OUTSIDE_COMPILATION = threading.local() - - -def RunOnTpuHost(func, *args, **kwargs): - r"""Runs the given function call on TPU host. - - Invokes func(\*args, \*\*kwargs) directly if not running on tpu. - - Args: - func: the function to invoke. - *args: args of func - **kwargs: kwargs of func - - Returns: - The function return value. - """ - if use_tpu() and not getattr(_OUTSIDE_COMPILATION, 'on', False): - _OUTSIDE_COMPILATION.on = True - res = tf.tpu.outside_compilation(func, *args, **kwargs) - _OUTSIDE_COMPILATION.on = False - else: - res = func(*args, **kwargs) - return res - - -def tpu_host(func): # pylint: disable=invalid-name - r"""Decorates a python function to only run on TPU hosts. - - This function has no effect when running on CPU/GPU. - - Example:: - - @py_utils.tpu_host() - def ComputeWER(self): - # Call a custom op computing WER. - - Args: - func: the function to invoke - - Returns: - A TPU-host only function - """ - - def Wrapped(*args, **kwargs): - return RunOnTpuHost(func, *args, **kwargs) - - return Wrapped - - -_tpu_device_assignment = None - - -def SetTpuDeviceAssignment(tpu_device_assignment): - global _tpu_device_assignment - if _tpu_device_assignment is not None: - tf.logging.warning('tpu_device_assignment was already set, ' - 'overwriting with new assignment.') - _tpu_device_assignment = tpu_device_assignment - - -# This function should called in unittest only. -def ClearTpuDevice(): - global _tpu_device_assignment - _tpu_device_assignment = None - - -def GetTpuDeviceAssignment(): - return _tpu_device_assignment - - -def SessionConfig(soft_placement=True, - inline=True, - cluster_def=None, - disable_meta_optimizer=False): - """Returns a session config proto. - - Args: - soft_placement: Turns allow_soft_placement on iff True. - inline: Turns do_function_inlining on iff True. - cluster_def: A tf.train.ClusterDef describing the cluster. - disable_meta_optimizer: Turns off grappler/metagraph optimizer. - - Returns: - A TF session config proto. - """ - session_config = tf.config_pb2.ConfigProto( - allow_soft_placement=soft_placement, - graph_options=tf.GraphOptions( - optimizer_options=tf.OptimizerOptions( - opt_level=tf.OptimizerOptions.L1, do_function_inlining=inline)), - cluster_def=cluster_def) - - if disable_meta_optimizer: - # Useful if start-up time is critical. - session_config.graph_options.rewrite_options.disable_meta_optimizer = True - # Disable layout optimizer which increases GPU memory usage. - session_config.graph_options.rewrite_options.layout_optimizer = ( - rewriter_config_pb2.RewriterConfig.OFF) - return session_config - - -def AssertIsCompatible(a, b): - assert a.IsCompatible(b), ('%s vs %s' % (a, b)) - - -def SetShapes(dst_nmap, src_nmap): - """Set shapes in dst_nmap using those in src_nmap.""" - AssertIsCompatible(src_nmap, dst_nmap) - for src, dst in zip(src_nmap.Flatten(), dst_nmap.Flatten()): - dst.set_shape(src.shape) - - -def Dtypes(nmap_list): - """Returns all tensors' data types in a list.""" - return [v.dtype for v in Flatten(nmap_list)] - - -def Flatten(x): - """Flattens 'x' by extracting tensors from nested structures to a list.""" - return tf.nest.flatten(x) - - -def Pack(tmpl, values): - """Packs 'values' according to 'tmpl'.""" - return tf.nest.pack_sequence_as(tmpl, values) - - -def Transform(fn, *v): - """Replaces every nested value x in 'v' with fn(x) and returns the result.""" - return tf.nest.map_structure(fn, *v) - - -def ConvertNoneGradientToZeros(xs, dxs): - """Sanitize dxs so that None becomes zeros appropriately. - - Args: - xs: A list of tensors. - dxs: A list of tensors. dxs[i] corresponds to xs[i]'s gradient. - - Returns: - A `.NestedMap` same as dxs with None replaced by a zero tensor. - """ - fn = lambda x, dx: tf.zeros_like(x) if dx is None else dx - return Transform(fn, xs, dxs) - - -def IsCompatible(lhs, rhs): - """Returns true if lhs and rhs are compatible.""" - try: - tf.nest.assert_same_structure(lhs, rhs) - return True - except (ValueError, TypeError): - return False - - -_NAME_PATTERN = re.compile('[A-Za-z_][A-Za-z0-9_]*') - - -class NestedMap(dict): - """A simple helper to maintain a dict. - - It is a sub-class of dict with the following extensions/restrictions: - - It supports attr access to its members (see examples below). - - Member keys have to be valid identifiers. - - E.g.:: - - >>> foo = NestedMap() - >>> foo['x'] = 10 - >>> foo.y = 20 - >>> assert foo.x * 2 == foo.y - """ - - # Disable pytype attribute checking. - _HAS_DYNAMIC_ATTRIBUTES = True - # keys in this list are not allowed in a NestedMap. - _RESERVED_KEYS = frozenset(dir(dict)) - # sentinel value for deleting keys used in Filter. - _DELETE = object() - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - for key in self.keys(): - assert isinstance(key, str), ( - 'Key in a NestedMap has to be a six.string_types. Currently type: %s,' - ' value: %s' % (str(type(key)), str(key))) - NestedMap.CheckKey(key) - assert key not in NestedMap._RESERVED_KEYS, ('%s is a reserved key' % key) - - def __setitem__(self, key, value): - # Make sure key is a valid expression and is not one of the reserved - # attributes. - assert isinstance(key, str), ( - 'Key in a NestedMap has to be a six.string_types. Currently type: %s, ' - 'value: %s' % (str(type(key)), str(key))) - NestedMap.CheckKey(key) - assert key not in NestedMap._RESERVED_KEYS, ('%s is a reserved key' % key) - super().__setitem__(key, value) - - def __setattr__(self, name, value): - self.__setitem__(name, value) - - def __getattr__(self, name): - try: - return self[name] - except KeyError as e: - raise AttributeError('%s; available attributes: %s' % - (e, sorted(list(self.keys())))) - - def __delattr__(self, name): - try: - del self[name] - except KeyError as e: - raise AttributeError('%s; available attributes: %s' % - (e, sorted(list(self.keys())))) - - def copy(self): # Don't delegate w/ super: dict.copy() -> dict. - return NestedMap(self) - - def __deepcopy__(self, unused_memo): - """Deep-copies the structure but not the leaf objects.""" - return self.DeepCopy() - - def DeepCopy(self): - """Deep-copies the structure but not the leaf objects.""" - return self.Pack(self.Flatten()) - - @staticmethod - def FromNestedDict(x): - """Converts every dict in nested structure 'x' to a NestedMap.""" - if isinstance(x, dict): - res = NestedMap() - for k, v in x.items(): - res[k] = NestedMap.FromNestedDict(v) - return res - elif isinstance(x, (list, tuple)): - return type(x)(NestedMap.FromNestedDict(v) for v in x) - else: - return x - - @staticmethod - def CheckKey(key): - """Asserts that key is valid NestedMap key.""" - if not (isinstance(key, str) and _NAME_PATTERN.match(key)): - raise ValueError('Invalid NestedMap key \'{}\''.format(key)) - - def GetItem(self, key): - """Gets the value for the nested `key`. - - Note that indexing lists is not supported, names with underscores will be - considered as one key. - - Args: - key: str of the form - `([A-Za-z_][A-Za-z0-9_]*)(.[A-Za-z_][A-Za-z0-9_]*)*.`. - - Returns: - The value for the given nested key. - - Raises: - KeyError if a key is not present. - """ - current = self - # Note: This can't support lists. List keys are ambiguous as underscore is - # not reserved for list indexing but also allowed to be used in keys. - # E.g., this is a valid nested map where the key 'a_0' is not well defined - # {'a_0': 3, 'a': [4]}. - for k in key.split('.'): - current = current[k] - return current - - def Get(self, key, default=None): - """Gets the value for nested `key`, returns `default` if key does not exist. - - Note that indexing lists is not supported, names with underscores will be - considered as one key. - - Args: - key: str of the form - `([A-Za-z_][A-Za-z0-9_]*)(.[A-Za-z_][A-Za-z0-9_]*)*.`. - default: Optional default value, defaults to None. - - Returns: - The value for the given nested key or `default` if the key does not exist. - """ - try: - return self.GetItem(key) - # TypeError is raised when an intermediate item is a list and we try to - # access an element of it with a string. - except (KeyError, TypeError): - return default - - def Set(self, key, value): - """Sets the value for a nested key. - - Note that indexing lists is not supported, names with underscores will be - considered as one key. - - Args: - key: str of the form - `([A-Za-z_][A-Za-z0-9_]*)(.[A-Za-z_][A-Za-z0-9_]*)*.`. - value: The value to insert. - - Raises: - ValueError if a sub key is not a NestedMap or dict. - """ - current = self - sub_keys = key.split('.') - for i, k in enumerate(sub_keys): - self.CheckKey(k) - # We have reached the terminal node, set the value. - if i == (len(sub_keys) - 1): - current[k] = value - else: - if k not in current: - current[k] = NestedMap() - if not isinstance(current[k], (dict, NestedMap)): - raise ValueError('Error while setting key {}. Sub key "{}" is of type' - ' {} but must be a dict or NestedMap.' - ''.format(key, k, type(current[k]))) - current = current[k] - - def _RecursiveMap(self, fn, flatten=False): - """Traverse recursively into lists, dicts, and NestedMaps applying `fn`. - - Args: - fn: The function to apply to each item (leaf node). - flatten: If true, the result should be a single flat list. Otherwise the - result will have the same structure as this NestedMap. - - Returns: - The result of applying fn. - """ - - def Recurse(v, key=''): - """Helper function for _RecursiveMap.""" - if isinstance(v, dict): - ret = [] if flatten else type(v)() - deleted = False - for k in sorted(v.keys()): - res = Recurse(v[k], key + '.' + k if key else k) - if res is self._DELETE: - deleted = True - continue - elif flatten: - ret += res - else: - ret[k] = res - if not ret and deleted: - return self._DELETE - return ret - elif isinstance(v, list): - ret = [] - deleted = False - for i, x in enumerate(v): - res = Recurse(x, '%s[%d]' % (key, i)) - if res is self._DELETE: - deleted = True - continue - elif flatten: - ret += res - else: - ret.append(res) - if not ret and deleted: - return self._DELETE - return ret - else: - ret = fn(key, v) - if flatten: - ret = [ret] - return ret - - res = Recurse(self) - if res is self._DELETE: - return [] if flatten else NestedMap() - return res - - def Flatten(self): - """Returns a list containing the flattened values in the `.NestedMap`. - - Unlike py_utils.Flatten(), this will only descend into lists, dicts, and - NestedMaps and not tuples, or namedtuples. - """ - return self._RecursiveMap(lambda _, v: v, flatten=True) - - def FlattenItems(self): - """Flatten the `.NestedMap` and returns pairs in a list. - - Returns: - A list of pairs, where keys for nested entries will be - represented in the form of `foo.bar[10].baz`. - """ - return self._RecursiveMap(lambda k, v: (k, v), flatten=True) - - def Pack(self, lst): - """Returns a copy of this with each value replaced by a value in lst.""" - assert len(self.FlattenItems()) == len(lst), f"{lst} vs {self.FlattenItems()}" - v_iter = iter(lst) - return self._RecursiveMap(lambda unused_k, unused_v: next(v_iter)) - - def Transform(self, fn): - """Returns a copy of this `.NestedMap` with fn applied on each value.""" - return self._RecursiveMap(lambda _, v: fn(v)) - - def TransformWithKey(self, fn): - """Returns a copy of this `.NestedMap` with fn applied on each key/value.""" - return self._RecursiveMap(fn) - - def IsCompatible(self, other): - """Returns true if self and other are compatible. - - If x and y are two compatible `.NestedMap`, `x.Pack(y.Flatten())` produces y - and vice versa. - - Args: - other: Another `.NestedMap`. - """ - items = self._RecursiveMap(lambda k, _: k, flatten=True) - other_items = other._RecursiveMap(lambda k, _: k, flatten=True) # pylint: disable=protected-access - return items == other_items - - def Filter(self, fn): - """Returns a copy with entries where fn(entry) is True.""" - return self.FilterKeyVal(lambda _, v: fn(v)) - - def FilterKeyVal(self, fn): - """Returns a copy of this `.NestedMap` filtered by fn. - - If fn(key, entry) is True, the entry is copied into the returned NestedMap. - Otherwise, it is not copied. - - Args: - fn: a callable of (string, entry)->boolean. - - Returns: - A `.NestedMap` contains copied entries from this `'.NestedMap`. - """ - return self._RecursiveMap(lambda k, v: v if fn(k, v) else self._DELETE) - - def _ToStrings(self): - """Returns debug strings in a list for this `.NestedMap`.""" - kv = self.FlattenItems() - maxlen = max([len(k) for k, _ in kv]) if kv else 0 - return sorted([k + ' ' * (4 + maxlen - len(k)) + str(v) for k, v in kv]) - - def DebugString(self): - """Returns a debug string for this `.NestedMap`.""" - return '\n'.join(self._ToStrings()) - - def VLog(self, level=None, prefix=None): - """Logs the debug string at the level.""" - if level is None: - level = 0 - if prefix is None: - prefix = 'nmap: ' - for l in self._ToStrings(): - tf.logging.vlog(level, '%s %s', prefix, l) - - def __dir__(self): - """dir() that includes flattened keys in returned output.""" - keys = self._RecursiveMap(lambda k, v: k, flatten=True) - return keys + super().__dir__() - - -class _Unique: - """A helper to uniqify variables in a NestedMap.""" - - def __init__(self): - self._vset = set() - - def __call__(self, v): - if (v is None) or (id(v) in self._vset): - return False - else: - self._vset.add(id(v)) - return True - - -def ToUniqueList(nmap): - """Returns the flattened `nmap` with duplicates removed.""" - return nmap.Filter(_Unique()).Flatten() - - -def ReadOnlyAttrDictView(backing): - """Wraps a dict to provide a read-only view of its contents. - - Dict keys can also be accessed by attribute. - - Args: - backing: Dict-like object to wrap. - - Returns: - Read-only Mapping that can be accessed by index (['foo']) or attr (d.foo). - """ - - class Wrapper: - """Wrapper object.""" - - # Disable pytype attribute checking. - _HAS_DYNAMIC_ATTRIBUTES = True - - def __getitem__(self, key): - return backing[key] - - def __len__(self): - return len(backing) - - def __iter__(self): - return iter(backing) - - def __getattr__(self, key): - return backing[key] - - def __hasattr__(self, key): - return key in backing - - def __setattr__(self, key, value): - raise AttributeError('Dictionary is read-only.') - - def __setitem__(self, key, value): - raise AttributeError('Dictionary is read-only.') - - return Wrapper() - - -def ToStaticShape(shape): - """Converts 'shape' to a static shape.""" - if isinstance(shape, (list, tuple)): - shape = [ - dim.value if isinstance(dim, tf.Dimension) else dim for dim in shape - ] - static_shape = [] - for dim in shape: - if symbolic.IsExpr(dim): - static_shape.append(symbolic.ToStatic(dim)) - else: - static_shape.append(dim) - return static_shape - else: - return shape.value if isinstance(shape, tf.Dimension) else shape - - -def Zeros(shape, *args, **kwargs): - return tf.zeros(ToStaticShape(shape), *args, **kwargs) - - -class UniformSampler: - """A reservoir sampler. - - This class implements reservoir sampling: Given a limit of `num_samples` total - samples, this class maintains a uniform probability (1 / `num_samples`) of - keeping any item dynamically added to the sampler. - - See https://en.wikipedia.org/wiki/Reservoir_sampling for details. - """ - - def __init__(self, num_samples): - assert num_samples > 0 - self._num_samples = num_samples - self._num_seen_items = 0 - self._samples = [] - - def Add(self, item): - """Add item to sampler.""" - self._num_seen_items += 1 - - if len(self._samples) < self._num_samples: - self._samples.append(item) - return - - index = np.random.randint(0, self._num_seen_items) - if index < self._num_samples: - self._samples[index] = item - - @property - def samples(self): - """Fetch the current samples from the sampler.""" - return self._samples - - -class RNNCellStateInit: - """State initialization functions for RNN cell init state.""" - - @staticmethod - def _Params(method, seed): - p = hyperparams.Params() - p.Define('method', method, - 'Initialization method. Should be one of zeros, random_normal.') - p.Define('seed', seed, 'Random seed used to generate initial values.') - p.Freeze() - return p - - @staticmethod - def Zeros(): - """tf.zeros().""" - return RNNCellStateInit._Params('zeros', seed=None) - - @staticmethod - def RandomNormal(seed=None): - """tf.random.normal().""" - return RNNCellStateInit._Params('random_normal', seed) - - -def DefaultRNNCellStateInit(): - return RNNCellStateInit.Zeros() - - -def InitRNNCellState(shape, init=None, dtype=None, name=None, is_eval=False): - """Initial state definitions for RNN cell implementations. - - Args: - shape: A array of ints/symbols for specifying the shape of the state. - init: Hyperparameters as returned by one of the static implemetaitons in - RNNCellStateInit. - dtype: The dype of the states. Defaults to tf.float32. - name: A name for the operation. If --stateless_vars_init is set, this name - is used to generate a seed on a per-variable basis. Otherwise, this name - is optional. - is_eval: Bool, set to True if we need special behavior in eval mode. - - Returns: - A Tensor of the specified shape, and sampled from the distribution as - defined by the init parameters. - """ - shape = ToStaticShape(shape) - - if init is None: - init = DefaultRNNCellStateInit() - if dtype is None: - dtype = tf.float32 - - method = init.method - if ((method in ['zeros']) or (method in ['random_normal'] and is_eval)): - init_state = tf.zeros(shape=shape, dtype=dtype, name=name) - elif method in ['random_normal']: - if use_stateless_vars_init(): - if name is None: - raise ValueError('InitRNNCellState() requires a `name` argument when ' - '--stateless_vars_init is enabled.') - seed = _GenerateStatelessRngSeed(name, init.seed) - init_state = stateless_random_ops.stateless_random_normal( - shape=shape, dtype=dtype, name=name, seed=seed) - else: - init_state = tf.random.normal( - shape=shape, dtype=dtype, name=name, seed=init.seed) - else: - raise ValueError('Initialization method (%s) not supported.' % method) - - return init_state - - -class WeightInit: - """Static class providing weight initialization config params.""" - - @staticmethod - def _Params(method, scale, seed): - """Parameters of this class.""" - p = hyperparams.Params() - p.Define('method', method, 'Initialization method.') - p.Define('scale', scale, 'Initialization scale.') - p.Define('seed', seed, 'Random seed used to generate initial values.') - p.Freeze() - return p - - @staticmethod - def Gaussian(scale=1.0, seed=None): - """scale * tf.random.normal(0, 1.0).""" - return WeightInit._Params('gaussian', scale, seed) - - @staticmethod - def Uniform(scale=1.0, seed=None): - """scale * tf.random.uniform(-1.0, 1.0).""" - return WeightInit._Params('uniform', scale, seed) - - @staticmethod - def UniformPositive(scale=1.0, seed=None): - """scale * tf.random.uniform(0., 1.0).""" - return WeightInit._Params('uniform_positive', scale, seed) - - @staticmethod - def Xavier(scale=1.0, seed=None): - """Xavier initialization (x = sqrt(6. / (in + out)); [-x, x]).""" - return WeightInit._Params('xavier', scale, seed) - - @staticmethod - def XavierGaussian(scale=1.0, seed=None): - """tf.random.normal(0, sqrt(2.0 / (in + out))).""" - return WeightInit._Params('xavier_gaussian', scale, seed) - - @staticmethod - def XavierWithFixupParams(scale=1.0, - depth=1.0, - layers_per_residual_block=1.0, - seed=None): - """Xavier initialization with Fixup.""" - scale = scale * math.pow(depth, (-1.0 / (2 * layers_per_residual_block))) - return WeightInit._Params('xavier', scale, seed) - - @staticmethod - def GeoMeanXavier(scale=1.0, seed=None): - """A variant of Xavier (x = sqrt(3. / sqrt(in * out)); [-x, x]).""" - return WeightInit._Params('geo_mean_xavier', scale, seed) - - @staticmethod - def Constant(scale=1.0): - """scale.""" - return WeightInit._Params('constant', scale, 0) - - @staticmethod - def TruncatedGaussian(scale=1.0, seed=None): - """scale * tf.random.truncated_normal(0, 1.0).""" - return WeightInit._Params('truncated_gaussian', scale, seed) - - @staticmethod - def GaussianSqrtDim(scale=1.0, seed=None): - """scale * tf.random.normal(0, 1 / sqrt(dim0)).""" - return WeightInit._Params('gaussian_sqrt_dim', scale, seed) - - @staticmethod - def GaussianSqrtFanIn(scale=1.0, seed=None): - """scale * tf.random.normal(0, 1 / sqrt(fan_in)).""" - return WeightInit._Params('gaussian_sqrt_fanin', scale, seed) - - @staticmethod - def GaussianSqrtFanOut(scale=1.0, seed=None): - """scale * tf.random.normal(0, 1 / sqrt(fan_out)).""" - return WeightInit._Params('gaussian_sqrt_fanout', scale, seed) - - @staticmethod - def UniformSqrtDim(scale=1.0, seed=None): - """scale * tf.uniform(-1 / sqrt(dim0), 1 / sqrt(dim0)).""" - return WeightInit._Params('uniform_sqrt_dim', scale, seed) - - @staticmethod - def UniformUnitScaling(scale=1.0, seed=None): - """scale * sqrt(3) / sqrt(dim0) * tf.uniform(-1, 1).""" - return WeightInit._Params('uniform_unit_scaling', scale, seed) - - @staticmethod - def UniformUnitScalingFanAvg(scale=1.0, seed=None): - """Same as tf.variance_scaling_initializer() ... - - Samples are drawn from a uniform distribution within [-limit, limit], with - limit = sqrt(3 * scale / n) - - where - n = max(1., (fan_in + fan_out) / 2). - See tf.keras.initializers.VarianceScaling for details. - - Args: - scale: A Python float. - seed: A Python int or None. - - Returns: - A WeightInit param. - """ - return WeightInit._Params('uniform_unit_scaling_fan_avg', scale, seed) - - @staticmethod - def TruncatedGaussianSqrtDim(scale=1.0, seed=None): - """scale * tf.random.truncated_normal(0, 1 / sqrt(dim0)).""" - return WeightInit._Params('truncated_gaussian_sqrt_dim', scale, seed) - - @staticmethod - def TruncatedGaussianSqrtFanIn(scale=1.0, seed=None): - """scale * tf.random.truncated_normal(0, 1 / sqrt(fan_in)).""" - return WeightInit._Params('truncated_gaussian_sqrt_fanin', scale, seed) - - @staticmethod - def TruncatedGaussianSqrtFanOut(scale=1.0, seed=None): - """scale * tf.random.truncated_normal(0, 1 / sqrt(fan_out)).""" - return WeightInit._Params('truncated_gaussian_sqrt_fanout', scale, seed) - - @staticmethod - def KaimingUniformFanInRelu(scale=1.0, seed=None): - return WeightInit._Params('kaiming_uniform_fanin_relu', scale, seed) - - @staticmethod - def KaimingUniformFanInLeakyRelu(scale=np.sqrt(5.), seed=None): - return WeightInit._Params('kaiming_uniform_fanin_leakyrelu', scale, seed) - - -_DEFAULT_XAVIER_INIT = 1.000001 - - -def DefaultParamInit(): - # Here we use 1.000001 as a signature for user picking up the - # default param initializer. - return WeightInit.Xavier(_DEFAULT_XAVIER_INIT) - - -# TODO(rpang, jonathanasdf): explore adding _is_default to hyperparams.Param. -def IsDefaultParamInit(p): - return (p.method == 'xavier' and - abs(p.scale - _DEFAULT_XAVIER_INIT) < 1e-7 and p.seed is None) - - -def WeightParams(shape, init=None, dtype=None, collections=None): - """Returns a hyperparams for a weight variable given the shape/init/dtype.""" - if init is None: - init = WeightInit.Xavier(_DEFAULT_XAVIER_INIT) - if dtype is None: - dtype = tf.float32 - if collections is None: - collections = [] - p = hyperparams.Params() - p.Define('dtype', dtype, 'The weight data type.') - p.Define('shape', shape, 'The weight shape.') - p.Define('init', init, 'Initialization method.') - p.Define('collections', collections, - 'Variable collections this weight belongs to.') - return p - - -def FindNeeded(endpoints): - """List names of tensors and operations required to compute endpoints.""" - names_seen = set() - queue = [] - for e in Flatten(endpoints): - if isinstance(e, tf.Operation): - queue.append(e) - else: - queue.append(e.op) - while queue: - op = queue.pop() - name = op.name - if name not in names_seen: - names_seen.add(name) - names_seen.update((o.name for o in op.outputs)) - queue.extend(i.op for i in op.inputs) - queue.extend(op.control_inputs) - return names_seen - - -def FindNeededInList(tensor_list, endpoints): - """Return tensors from tensor_list needed to compute any of endpoints.""" - all_needed = FindNeeded(endpoints) - return [t for t in tensor_list if t.name in all_needed] - - -class _CollectionGetter: - """Get graph local value from a defined collection.""" - - def __init__(self, key, default_factory): - self._key = key - self._default_factory = default_factory - - def __call__(self): - collection = tf.get_collection(self._key) - if collection: - assert len(collection) == 1 - return collection[0] - value = self._default_factory() - tf.add_to_collection(self._key, value) - return value - - -def SanitizeScopeKey(key): - """Removes invalid symbols from name_scope keys.""" - if key.startswith('_'): - key = key[1:] - return key.replace('[', '_').replace(']', '') - - -# Global variable to control multitask variable reuse -# If False (default) the default tf.get_variable is used, that is: -# - Reusing scopes only allow getting existing variables -# - Non-reusing scopes only allow getting new variables -# With GetOpportunisticVariableReuse() == True: -# - Reusing scopes only allow getting existing variables, as usual -# - Non-reusing scopes reuse new variables or get new ones -_OPPORTUNISTIC_VARIABLE_REUSE = ThreadLocalStack() - - -@contextlib.contextmanager -def OpportunisticVariableReuseScope(enable_opportunistic_reuse=True): - _OPPORTUNISTIC_VARIABLE_REUSE.stack.append(enable_opportunistic_reuse) - try: - yield - finally: - _OPPORTUNISTIC_VARIABLE_REUSE.stack.pop() - - -def GetOpportunisticVariableReuse(): - """Get the current variable reuse setting.""" - return (_OPPORTUNISTIC_VARIABLE_REUSE.stack[-1] - if _OPPORTUNISTIC_VARIABLE_REUSE.stack else False) - - -_VARIABLE_RENAME_RULES = ThreadLocalStack() - - -@contextlib.contextmanager -def VariableRenameScope(renames): - """Append the renaming rules to the stack of renames. - - Args: - renames: pairs of (regexp, new_name_format). If the regexp matches, the - new_name_format will be interpolated using the matched groups. - - Yields: - scope in which the renaming rules are applied - """ - _VARIABLE_RENAME_RULES.stack.append(renames) - try: - yield - finally: - _VARIABLE_RENAME_RULES.stack.pop() - - -def GetVariableName(name): - """Get variable name after application of all renaming rules. - - Args: - name: untransformed variable name with scope_name prepended - - Returns: - name possibly modified using renaming rules - """ - matched = False - new_name = name - for renames in _VARIABLE_RENAME_RULES.stack: - for regexp, name_format in renames: - match = re.match(regexp, name) - if match: - if matched: - tf.logging.warning('Multiple matches for: %s', name) - matched = True - new_name = name_format % match.groups() - if new_name != name: - tf.logging.info("WARNING!!! Renaming variable '%s' to '%s'", name, new_name) - return new_name - - -def GenerateSeedFromName(name): - """Generate a random seed from a name string.""" - md5 = hashlib.md5() - md5.update(six.ensure_binary(name)) - return int(md5.hexdigest(), 16) % (2**31 - 1) - - -# To keep track of all the variables ever gets created by the CreateVariable -# routine below. -_ALL_VARS_KEY = ('__lingvo_all_vars',) - -_get_all_vars = _CollectionGetter(_ALL_VARS_KEY, lambda: {}) - -_VARIABLE_SHAPE_PREFIXES = ThreadLocalStack() - - -@contextlib.contextmanager -def VariableShapePrefixContext(shape_prefix): - """Add a shape prefix to variable created by CreateVariable(). - - Args: - shape_prefix: a positive integer of shape prefix. - - Yields: - None. - """ - assert shape_prefix > 0, ('%s' % shape_prefix) - _VARIABLE_SHAPE_PREFIXES.stack.append(shape_prefix) - try: - yield - finally: - _VARIABLE_SHAPE_PREFIXES.stack.pop() - - -def GetVariableShapePrefixes(): - """Return the list of shape prefixes for CreateVariable().""" - return _VARIABLE_SHAPE_PREFIXES.stack - - -def GetFanInFanOut(shape): - """Returns (fan_in, fan_out) of a weight variable of the give shape.""" - if not shape: - return None, None - if len(shape) < 1: - return 1, 1 - elif len(shape) == 1: - # Following _compute_fans() from TF's init_ops.py. - return shape[0], shape[0] - else: - receptive_field_size = 1 - for s in shape[:-2]: - receptive_field_size *= s - fan_in = shape[-2] * receptive_field_size - fan_out = shape[-1] * receptive_field_size - return fan_in, fan_out - - -_VARIABLE_CREATOR_STACK = ThreadLocalStack().stack - - -def _DefaultVariableCreator(**kwargs): - kwargs.pop('var_name', None) - kwargs.pop('var_params', None) - return tf.get_variable(**kwargs) - - -def _GetVariableCreator(): - fn = _DefaultVariableCreator - for wrapper in reversed(_VARIABLE_CREATOR_STACK): - fn = functools.partial(wrapper, fn) - return fn - - -@contextlib.contextmanager -def VariableCreatorScope(variable_creator): - """Yields a context around a variable_creator, used by `CreateVariable()`. - - The function must have the following signature:: - - def variable_creator(next_creator, **kwargs) - - The function may delegate variable creation to the next variable creator, or - return its own tf.Variable. - - This differs from tf.variable_creator_scope in that tf.variable_creator_scope - modifies a tf.Variable() call while this modifies a tf.get_variable() call. As - the code is migrated to TF2 and tf.get_variable() is deprecated, this may be - upgraded to using tf.variable_creator_scope instead. - - This differs from tf.variable_scope(custom_getter=variable_creator) in that - the kwargs passed can be manipulated. - - Variable creators are resolved from the outermost towards the innermost. - - The innermost variable creator function is tf.get_variable. - - The passed in kwargs must conform to what tf.get_variable accepts, with the - addition of `var_name` and `var_params`. - - Args: - variable_creator: A variable creator function. - """ - _VARIABLE_CREATOR_STACK.append(variable_creator) - try: - yield - finally: - _VARIABLE_CREATOR_STACK.pop() - - -# TODO(yonghui): Add support for partitioned Variables. -def CreateVariable(name, - params, - reuse=None, - trainable=True, - collections=None, - default_seed=None, - synchronization=tf.VariableSynchronization.AUTO, - aggregation=tf.VariableAggregation.NONE): - """Creates tf.Variable according to param_config. - - Args: - name: A string, name of the variable. - params: A WeightParams specifying the details of how this variable should be - constructed and initialized. - reuse: Whether or not to reuse an existing variable. It has the same - semantics as the reuse arg in tf.variable_scope. - trainable: Whether or not the variable is trainable. - collections: Override the default variable collection ( - tf.GraphKeys.GLOBAL_VARIABLES). - default_seed: Seed to use for initialization if not specified in params. - Used for deterministic initialization in tests. - synchronization: Indicates when a distributed a variable will be aggregated. - Accepted values are constants defined in the class - tf.VariableSynchronization. By default the synchronization is set to AUTO - and the current DistributionStrategy chooses when to synchronize. - aggregation: Indicates how a distributed variable will be aggregated. - Accepted values are constants defined in the class tf.VariableAggregation. - - Returns: - The created variable. - """ - if use_stateless_vars_init(): - return _CreateVariableStateless(name, params, reuse, trainable, collections, - default_seed, synchronization, aggregation) - else: - return _CreateVariableStateful(name, params, reuse, trainable, collections, - default_seed, synchronization, aggregation) - - -def _CreateVariableStateful(name, - params, - reuse=None, - trainable=True, - collections=None, - default_seed=None, - synchronization=tf.VariableSynchronization.AUTO, - aggregation=tf.VariableAggregation.NONE): - """Creates tf.Variable using TF stateful RNGs according to param_config. - - Args: - name: A string, name of the variable. - params: A WeightParams specifying the details of how this variable should be - constructed and initialized. - reuse: Whether or not to reuse an existing variable. It has the same - semantics as the reuse arg in tf.variable_scope. - trainable: Whether or not the variable is trainable. - collections: Override the default variable collection ( - tf.GraphKeys.GLOBAL_VARIABLES). - default_seed: Seed to use for initialization if not specified in params. - Used for deterministic initialization in tests. - synchronization: Indicates when a distributed a variable will be aggregated. - Accepted values are constants defined in the class - tf.VariableSynchronization. By default the synchronization is set to AUTO - and the current DistributionStrategy chooses when to synchronize. - aggregation: Indicates how a distributed variable will be aggregated. - Accepted values are constants defined in the class tf.VariableAggregation. - - Returns: - The created variable. - """ - p = params.Copy() - shape = tf.TensorShape(ToStaticShape(p.shape)).as_list() - if shape: - assert all([dim_size > 0 for dim_size in shape]), shape - dim0 = shape[0] - else: - dim0 = 1 - assert p.init.method == 'constant' or np.all(np.asarray(p.init.scale) >= 0) - method = p.init.method - scale = p.init.scale - seed = p.init.seed - - if IsDefaultParamInit(p.init): - tf.logging.warning( - 'WARNING!!! var %s is using the default xavier initializer.' - ' Make sure this is intended.', name) - - with tf.variable_scope(name) as scope: - var_name = GetVariableName(scope.name) - - if tf.get_default_graph().seed is not None: - # We are in a program/test which need determistic randomization. - if seed is None: - if default_seed is not None: - seed = default_seed - else: - # We are not given a per-variable random seed. We use hash of - # variable name as a stable random seed. - seed = GenerateSeedFromName(var_name) - - init_dtype = p.dtype.real_dtype - v_init = _CreateVarInitStateful(name, method, shape, dim0, seed, scale, - init_dtype) - - if p.dtype == tf.complex64: - - def ComplexWrapper(init): - - def _Wrapper(shape, dtype, partition_info): - del dtype - # A more complex alternative may be to use the init function for - # magnitudes and uniform random for phases instead. - shape = [2] + shape - value = init(shape, init_dtype, partition_info) - return tf.complex(value[0], value[1]) - - return _Wrapper - - v_init = ComplexWrapper(v_init) - - # Variable creators. - def MaybePinVarsToCpu(next_creator, **kwargs): - if _FromGlobal('pin_vars_to_cpu'): - with tf.device('/cpu:0'): - return next_creator(**kwargs) - return next_creator(**kwargs) - - def MaybeOpportunisticVariableReuse(next_creator, **kwargs): - try: - return next_creator(**kwargs) - except ValueError: # Possibly the variable already exists - if GetOpportunisticVariableReuse(): - with tf.variable_scope(tf.get_variable_scope(), reuse=tf.AUTO_REUSE): - return next_creator(**kwargs) - else: - raise - - def LingvoVariableCreator(next_creator, **kwargs): - """Lingvo variable creator.""" - # TODO(yonghui): Possibly get away from variable_scope and implement our own - # variable sharing mechanism. - with tf.variable_scope(name) as scope: - var_scope = tf.VariableScope( - scope.reuse, - custom_getter=scope.custom_getter, - caching_device=scope.caching_device, - use_resource=scope.use_resource or use_resource_variables()) - with tf.variable_scope(var_scope), tf.variable_scope(var_name, reuse=reuse): - var = next_creator(**kwargs) - - var_ref = var.experimental_ref() # For key in dict/set. - all_vars = _get_all_vars() - if var_ref in all_vars: - tf.logging.info('Reusing var %s', var.name) - cached = all_vars[var_ref] - assert cached == p.ToText(), ('Cached config:\n %s vs new config:\n %s' % - (cached, p.ToText())) - else: - tf.logging.info('Creating var %s shape=%s on device %s', var.name, - var.shape, var.device) - all_vars[var_ref] = p.ToText() - for col in p.collections: - tf.add_to_collection(col, var) - return var - - with VariableCreatorScope(LingvoVariableCreator): - with VariableCreatorScope(MaybeOpportunisticVariableReuse): - with VariableCreatorScope(MaybePinVarsToCpu): - var = _GetVariableCreator()( - var_name=var_name, - var_params=p, - name='var', - shape=GetVariableShapePrefixes() + list(shape), - dtype=p.dtype, - initializer=v_init, - collections=collections, - trainable=trainable, - validate_shape=True, - synchronization=synchronization, - aggregation=aggregation) - - return var - - -def _CreateVariableStateless(name, - params, - reuse=None, - trainable=True, - collections=None, - default_seed=None, - synchronization=tf.VariableSynchronization.AUTO, - aggregation=tf.VariableAggregation.NONE): - """Creates tf.Variable using TF stateless RNGs according to `params`. - - Args: - name: A string, name of the variable. - params: A WeightParams specifying the details of how this variable should be - constructed and initialized. - reuse: Whether or not to reuse an existing variable. It has the same - semantics as the reuse arg in tf.variable_scope. - trainable: Whether or not the variable is trainable. - collections: Override the default variable collection ( - tf.GraphKeys.GLOBAL_VARIABLES). - default_seed: Seed to use for initialization if not specified in params. - Used for deterministic initialization in tests. - synchronization: Indicates when a distributed a variable will be aggregated. - Accepted values are constants defined in the class - tf.VariableSynchronization. By default the synchronization is set to AUTO - and the current DistributionStrategy chooses when to synchronize. - aggregation: Indicates how a distributed variable will be aggregated. - Accepted values are constants defined in the class tf.VariableAggregation. - - Returns: - The created variable. - """ - p = params.Copy() - shape = tf.TensorShape(ToStaticShape(p.shape)).as_list() - if shape: - assert all([dim_size > 0 for dim_size in shape]), shape - dim0 = shape[0] - else: - dim0 = 1 - assert p.init.method == 'constant' or np.all(np.asarray(p.init.scale) >= 0) - method = p.init.method - scale = p.init.scale - seed = p.init.seed - - if IsDefaultParamInit(p.init): - tf.logging.warning( - 'WARNING!!! var %s is using the default xavier initializer.' - ' Make sure this is intended.', name) - - with tf.variable_scope(name) as scope: - var_name = GetVariableName(scope.name) - - user_seed = seed if seed is not None else default_seed - seed = _GenerateStatelessRngSeed(var_name, user_seed) - - init_dtype = p.dtype.real_dtype - v_init = _CreateVarInitStateless(name, method, shape, dim0, seed, scale, - init_dtype) - - if p.dtype == tf.complex64: - raise TypeError( - 'Stateless variable initialization does not support tf.complex64.') - - def LingvoVariableCreator(next_creator, **kwargs): - """Lingvo variable creator.""" - # TODO(yonghui): Possibly get away from variable_scope and implement our own - # variable sharing mechanism. - with tf.variable_scope(name) as scope: - var_scope = tf.VariableScope( - scope.reuse, - custom_getter=scope.custom_getter, - caching_device=scope.caching_device, - use_resource=scope.use_resource or use_resource_variables()) - with tf.variable_scope(var_scope), tf.variable_scope(var_name, reuse=reuse): - var = next_creator(**kwargs) - - var_ref = var.experimental_ref() # For key in dict/set. - all_vars = _get_all_vars() - if var_ref in all_vars: - tf.logging.info('Reusing var %s', var.name) - cached = all_vars[var_ref] - assert cached == p.ToText(), ('Cached config:\n %s vs new config:\n %s' % - (cached, p.ToText())) - else: - tf.logging.info('Creating var %s shape=%s on device %s', var.name, - var.shape, var.device) - all_vars[var_ref] = p.ToText() - for col in p.collections: - tf.add_to_collection(col, var) - return var - - with VariableCreatorScope(LingvoVariableCreator): - var = _GetVariableCreator()( - var_name=var_name, - var_params=p, - name='var', - shape=GetVariableShapePrefixes() + list(shape), - dtype=p.dtype, - initializer=v_init, - collections=collections, - trainable=trainable, - validate_shape=True, - synchronization=synchronization, - aggregation=aggregation) - - return var - - -def _RandomXavierUniformInitializer(method, scale, seed): - """Creates a random Xavier uniform initializer.""" - - def XavierUniform(shape, dtype, partition_info): - """Xavier initialization (x = sqrt(6. / (in + out)); scale*[-x, x]).""" - del partition_info # Unused. - if not shape: - raise ValueError('\'shape\' must not be \'None\' or 0 for XavierUniform') - fan_in, fan_out = GetFanInFanOut(shape) - if method == 'xavier': - limit = math.sqrt(6. / (fan_in + fan_out)) - elif method == 'geo_mean_xavier': - limit = math.sqrt(3. / math.sqrt(fan_in * fan_out)) - return scale * tf.random.uniform(shape, -limit, limit, dtype, seed) - - return XavierUniform - - -def _CreateVarInitStateful(name, method, shape, dim0, seed, scale, init_dtype): - """Creates variable initialization function for a stateful RNG.""" - if (method in [ - 'gaussian_sqrt_dim', 'uniform_sqrt_dim', 'truncated_gaussian_sqrt_dim' - ]): - if len(shape) > 2: - # This is probably not the right method to use when len(shape) > 2, - # e.g. dim0 will be 3 with a 3x3 conv2d kernel. - tf.logging.warning( - 'Initializing %s of shape %s with method %s: dim0=%s. ' - 'Make sure that it is intended.', name, shape, method, dim0) - scale *= 1.0 / math.sqrt(dim0) - - if method in ['gaussian_sqrt_fanin', 'truncated_gaussian_sqrt_fanin']: - fan_in, _ = GetFanInFanOut(shape) - if fan_in is not None: - scale *= 1.0 / math.sqrt(fan_in) - if method in ['gaussian_sqrt_fanout', 'truncated_gaussian_sqrt_fanout']: - _, fan_out = GetFanInFanOut(shape) - if fan_out is not None: - scale *= 1.0 / math.sqrt(fan_out) - if method == 'xavier_gaussian': - fan_in, fan_out = GetFanInFanOut(shape) - if fan_in is not None and fan_out is not None: - scale *= math.sqrt(2.0 / (fan_in + fan_out)) - - if method in [ - 'gaussian', 'gaussian_sqrt_dim', 'gaussian_sqrt_fanin', - 'gaussian_sqrt_fanout', 'xavier_gaussian' - ]: - v_init = init_ops.random_normal_initializer( - mean=0.0, stddev=scale, seed=seed, dtype=init_dtype) - elif method in ['uniform', 'uniform_sqrt_dim']: - v_init = init_ops.random_uniform_initializer( - minval=-scale, maxval=scale, seed=seed, dtype=init_dtype) - elif method in ['uniform_positive']: - v_init = init_ops.random_uniform_initializer( - minval=0.0, maxval=scale, seed=seed, dtype=init_dtype) - elif method in ['uniform_unit_scaling']: - v_init = init_ops.uniform_unit_scaling_initializer( - factor=scale, seed=seed, dtype=init_dtype) - elif method in ['uniform_unit_scaling_fan_avg']: - v_init = tf.variance_scaling_initializer( - scale=scale, - mode='fan_avg', - distribution='uniform', - seed=seed, - dtype=init_dtype) - elif method in [ - 'truncated_gaussian', 'truncated_gaussian_sqrt_dim', - 'truncated_gaussian_sqrt_fanin', 'truncated_gaussian_sqrt_fanout' - ]: - v_init = init_ops.truncated_normal_initializer( - mean=0.0, stddev=scale, seed=seed, dtype=init_dtype) - elif method in ['constant']: - v_init = init_ops.constant_initializer(value=scale, dtype=init_dtype) - elif method in ['xavier', 'geo_mean_xavier']: - def XavierUniform(shape, dtype, partition_info): - """Xavier initialization (x = sqrt(6. / (in + out)); scale*[-x, x]).""" - del partition_info # Unused. - if not shape: - raise ValueError( - '\'shape\' must not be \'None\' or 0 for XavierUniform') - fan_in, fan_out = GetFanInFanOut(shape) - if method == 'xavier': - limit = math.sqrt(6. / (fan_in + fan_out)) - elif method == 'geo_mean_xavier': - limit = math.sqrt(3. / math.sqrt(fan_in * fan_out)) - return scale * tf.random.uniform(shape, -limit, limit, dtype, seed) - - v_init = XavierUniform - elif method in [ - 'kaiming_uniform_fanin_relu', 'kaiming_uniform_fanin_leakyrelu' - ]: - fan_in = np.prod(shape[:-1]) - if method == 'kaiming_uniform_fanin_leakyrelu': - # Assume the 'a' parameter is the 'scale' argument. - gain = np.sqrt(2. / (1 + scale**2)) - else: - gain = np.sqrt(2.) - std_dev = gain / np.sqrt(fan_in) - bound = np.sqrt(3.0) * std_dev - v_init = init_ops.random_uniform_initializer( - minval=-bound, maxval=bound, seed=seed, dtype=init_dtype) - else: - assert False, 'init_type `%s` not supported.' % method - - return v_init - - -def _GenerateStatelessRngSeed(name, seed): - """Generates a 2-tuple seed for a stateless variable initializer. - - We want to ensure that different variables end up with different random values - even when they are passed the same seed and shape. To this aim, this function - generates a pseudo-unique seed by hashing the variable name and mapping it - into a scalar seed. More specifically, the returned value is a 2-tuple of - tf.int32 scalar, where the first element is the user-provided seed and the - second element is obtained by hashing the variable name. - - Args: - name: The variable name for which to generate a stateless-like seed. - seed: The user-specified scalar seed. - - Returns: - A 2-tuple seed of tf.int32 values (for TPU compatibility). - """ - seed0 = seed or 0 - seed1 = GenerateSeedFromName(name) - return tf.constant([seed0, seed1], dtype=tf.int32) - - -def _DeterministicRandomNormalInitializer(seed, mean, stddev): - """Creates a random normal initializer.""" - - def DeterministicNormal(shape, dtype, partition_info): - del partition_info # Unused. - return stateless_random_ops.stateless_random_normal( - shape=shape, seed=seed, mean=mean, stddev=stddev, dtype=dtype) - - return DeterministicNormal - - -def _DeterministicRandomUniformInitializer(seed, minval, maxval): - """Creates a random uniform initializer.""" - - def DeterministicUniform(shape, dtype, partition_info): - del partition_info # Unused. - return stateless_random_ops.stateless_random_uniform( - shape=shape, seed=seed, minval=minval, maxval=maxval, dtype=dtype) - - return DeterministicUniform - - -def _DeterministicRandomTruncatedNormalInitializer(seed, mean, stddev): - """Creates a random truncated normal initializer.""" - - def DeterministicTruncatedNormal(shape, dtype, partition_info): - del partition_info # Unused. - return stateless_random_ops.stateless_truncated_normal( - shape=shape, seed=seed, mean=mean, stddev=stddev, dtype=dtype) - - return DeterministicTruncatedNormal - - -def _DeterministicRandomUniformUnitScalingInitializer(seed, factor): - """Creates a random uniform unit scaling initializer.""" - - def DeterministicUniformUnitScaling(shape, dtype, partition_info): - # The following logic is originally from (UniformUnitScaling.__call__()) - # in TensorFlow: python/ops/init_ops.py - scale_shape = shape - if partition_info is not None: - scale_shape = partition_info.full_shape - - input_size = 1.0 - # Estimating input size is not possible to do perfectly, but we try. - # The estimate, obtained by multiplying all dimensions but the last one, - # is the right thing for matrix multiply and convolutions (see above). - for dim in scale_shape[:-1]: - input_size *= float(dim) - # Avoid errors when initializing zero-size tensors. - input_size = max(input_size, 1.0) - maxval = math.sqrt(3 / input_size) * factor - return stateless_random_ops.stateless_random_uniform( - shape=shape, seed=seed, minval=-maxval, maxval=maxval, dtype=dtype) - - return DeterministicUniformUnitScaling - - -def _DeterministicRandomVarianceScalingInitializer(scale, mode, distribution, - seed): - """Creates a variance scaling initializer.""" - - if scale <= 0.: - raise ValueError('`scale` must be positive float.') - if mode not in {'fan_in', 'fan_out', 'fan_avg'}: - raise ValueError('Invalid `mode` argument:', mode) - distribution = distribution.lower() - if distribution not in { - 'normal', 'uniform', 'truncated_normal', 'untruncated_normal' - }: - raise ValueError('Invalid `distribution` argument:', distribution) - - def DeterministicVarianceScaling(shape, dtype, partition_info): - # This is originally from TensorFlow: python/ops/init_ops.py - scale_shape = shape - if partition_info is not None: - scale_shape = partition_info.full_shape - # Handle special case of empty list as shape, since fan_in and fan_out - # are numerically added below. Without this, GetFanInFanOut() would - # return None, None instead. - if isinstance(scale_shape, (list, tuple)) and not scale_shape: - fan_in, fan_out = 1, 1 - else: - fan_in, fan_out = GetFanInFanOut(scale_shape) - if mode == 'fan_in': - scale_inner = scale / max(1., fan_in) - elif mode == 'fan_out': - scale_inner = scale / max(1., fan_out) - else: - scale_inner = scale / max(1., (fan_in + fan_out) / 2.) - if distribution == 'normal' or distribution == 'truncated_normal': - # constant taken from scipy.stats.truncnorm.std( - # a=-2, b=2, loc=0., scale=1.) - stddev = math.sqrt(scale_inner) / .87962566103423978 - return stateless_random_ops.stateless_truncated_normal( - shape=shape, seed=seed, mean=0.0, stddev=stddev, dtype=dtype) - elif distribution == 'untruncated_normal': - stddev = math.sqrt(scale_inner) - return stateless_random_ops.stateless_random_normal( - shape=shape, seed=seed, mean=0.0, stddev=stddev, dtype=dtype) - else: - limit = math.sqrt(3.0 * scale_inner) - return stateless_random_ops.stateless_random_uniform( - shape=shape, seed=seed, minval=-limit, maxval=limit, dtype=dtype) - - return DeterministicVarianceScaling - - -def _DeterministicRandomXavierUniformInitializer(method, scale, seed): - """Creates a variance scaling initializer.""" - - def XavierUniform(shape, dtype, partition_info): - """Xavier initialization (x = sqrt(6. / (in + out)); scale*[-x, x]).""" - del partition_info # Unused. - if not shape: - raise ValueError('\'shape\' must not be \'None\' or 0 for XavierUniform') - fan_in, fan_out = GetFanInFanOut(shape) - if method == 'xavier': - limit = math.sqrt(6. / (fan_in + fan_out)) - elif method == 'geo_mean_xavier': - limit = math.sqrt(3. / math.sqrt(fan_in * fan_out)) - return scale * stateless_random_ops.stateless_random_uniform( - shape, seed, -limit, limit, dtype) - - return XavierUniform - - -def _CreateVarInitStateless(name, method, shape, dim0, seed, scale, init_dtype): - """Creates variable initialization function for a stateless RNG.""" - if (method in [ - 'gaussian_sqrt_dim', 'uniform_sqrt_dim', 'truncated_gaussian_sqrt_dim' - ]): - if len(shape) > 2: - # This is probably not the right method to use when len(shape) > 2, - # e.g. dim0 will be 3 with a 3x3 conv2d kernel. - tf.logging.warning( - 'Initializing %s of shape %s with method %s: dim0=%s. ' - 'Make sure that it is intended.', name, shape, method, dim0) - scale *= 1.0 / math.sqrt(dim0) - - if method in ['gaussian_sqrt_fanin', 'truncated_gaussian_sqrt_fanin']: - fan_in, _ = GetFanInFanOut(shape) - if fan_in is not None: - scale *= 1.0 / math.sqrt(fan_in) - if method in ['gaussian_sqrt_fanout', 'truncated_gaussian_sqrt_fanout']: - _, fan_out = GetFanInFanOut(shape) - if fan_out is not None: - scale *= 1.0 / math.sqrt(fan_out) - if method == 'xavier_gaussian': - fan_in, fan_out = GetFanInFanOut(shape) - if fan_in is not None and fan_out is not None: - scale *= math.sqrt(2.0 / (fan_in + fan_out)) - - if method in [ - 'gaussian', 'gaussian_sqrt_dim', 'gaussian_sqrt_fanin', - 'gaussian_sqrt_fanout', 'xavier_gaussian' - ]: - v_init = _DeterministicRandomNormalInitializer( - seed=seed, mean=0., stddev=scale) - elif method in ['uniform', 'uniform_sqrt_dim']: - v_init = _DeterministicRandomUniformInitializer( - seed=seed, minval=-scale, maxval=scale) - elif method in ['uniform_positive']: - v_init = _DeterministicRandomUniformInitializer( - seed=seed, minval=0., maxval=scale) - elif method in ['uniform_unit_scaling']: - v_init = _DeterministicRandomUniformUnitScalingInitializer( - seed=seed, factor=scale) - elif method in ['uniform_unit_scaling_fan_avg']: - v_init = _DeterministicRandomVarianceScalingInitializer( - scale=scale, mode='fan_avg', distribution='uniform', seed=seed) - elif method in [ - 'truncated_gaussian', 'truncated_gaussian_sqrt_dim', - 'truncated_gaussian_sqrt_fanin', 'truncated_gaussian_sqrt_fanout' - ]: - v_init = _DeterministicRandomTruncatedNormalInitializer( - seed=seed, mean=0., stddev=scale) - elif method in ['constant']: - v_init = init_ops.constant_initializer(value=scale, dtype=init_dtype) - elif method in ['xavier', 'geo_mean_xavier']: - v_init = _DeterministicRandomXavierUniformInitializer(method, scale, seed) - elif method in [ - 'kaiming_uniform_fanin_relu', 'kaiming_uniform_fanin_leakyrelu' - ]: - fan_in = np.prod(shape[:-1]) - if method == 'kaiming_uniform_fanin_leakyrelu': - # Assume the 'a' parameter is the 'scale' argument. - gain = np.sqrt(2. / (1 + scale**2)) - else: - gain = np.sqrt(2.) - std_dev = gain / np.sqrt(fan_in) - bound = np.sqrt(3.0) * std_dev - v_init = _DeterministicRandomUniformInitializer( - seed=seed, minval=-bound, maxval=bound) - else: - assert False, 'init_type %s not supported.' % method - - return v_init - - -_global_variable_scope = None - - -def GetGlobalVariableScope(): - """Gets the global variable scope (as if no variable_scope has been set). - - Returns: - The VariableScope corresponding to as if no tf.variable_scope is in effect. - """ - if not _global_variable_scope: - # Each thread gets its own default global variable scope, and we take - # advantage of that in order to get a top-level scope. This avoids the - # need to call tf.get_variable_scope() at the module level, which allows - # this module to be imported without modifying global state (i.e. creating - # the default graph). It is important to not mutate the global state at - # module load time, because it let's us flip flags after import that affect - # core TensorFlow behavior. - def Initialize(): - global _global_variable_scope - _global_variable_scope = tf.get_variable_scope() - - t = threading.Thread(target=Initialize) - t.start() - t.join() - return _global_variable_scope - - -_GLOBAL_STEP_STACK = ThreadLocalStack() - - -@contextlib.contextmanager -def GlobalStepContext(global_step_tensor): - _GLOBAL_STEP_STACK.stack.append(global_step_tensor) - try: - yield - finally: - _GLOBAL_STEP_STACK.stack.pop() - - -def GetGlobalStep(): - """Return the global_step.""" - if _GLOBAL_STEP_STACK.stack: - return _GLOBAL_STEP_STACK.stack[-1] - return tf.train.get_global_step() - - -def GetOrCreateGlobalStepVar(): - """Return the global_step variable, creating it if it does not exist. - - Prefer GetGlobalStep if a tensor rather than a tf.Variable is sufficient. - - Returns: - The global_step variable, or a new created one if it does not exist. - """ - with tf.variable_scope( - GetGlobalVariableScope(), use_resource=use_resource_variables()): - return tf.train.get_or_create_global_step() - - -def LogMultiLines(label, lines): - if not isinstance(lines, (list, tuple)): - lines = lines.split('\n') - for line in lines: - tf.logging.info('%s: %s', label, line) - - -def _LogPlacement(label, theta, copy): - """Logs theta and its copy's device placement.""" - - def GetDevices(m): - """Flatten a `.NestedMap` m and extracts each value's device.""" - return [x.device for x in m.Flatten()] - - tf.logging.info('=== %s ===', label) - LogMultiLines( - label, - theta.Pack([('%s -> %s' % (x[0], x[1])) - for x in zip(GetDevices(theta), GetDevices(copy)) - ]).DebugString()) - tf.logging.info('==========') - - -def CreateLocalTheta(theta, device_list=None, label=None): - """Creates local copy of theta and shards across devices device list. - - Leaves variables intact. - - Args: - theta: a `.NestedMap` of variables. - device_list: list of devices to shard across. If None, defaults to a list - ['']. - label: Logging label. - - Returns: - A `.NestedMap` of identity() wrapped theta - """ - - class AddIdentity: - """Helper class.""" - - def __init__(self, device_list): - self._list = device_list if device_list else [''] - self._index = 0 - - def __call__(self, x): - if isinstance(x, tf.Variable): - return x - with tf.device(self._list[self._index % len(self._list)]): - self._index += 1 - return tf.identity(x) - - copy = theta.Transform(AddIdentity(device_list)) - _LogPlacement(label, theta, copy) - return copy - - -def _GetVarsToLoad(all_vars, variable_loading_rules, var_ignore_rules): - """Determines variables to load and their names in checkpoint.""" - # This list contains mappings from var names as they appear in the checkpoint - # to the vars in our model they correspond to. - vars_to_load = [] - for model_var in all_vars: - for regexp, name_format in variable_loading_rules: - match = re.match(regexp, model_var.name) - # Skip if var doesn't match the loading rules, or if it should be ignored. - if not match or any( - re.match(r, model_var.name) for r in var_ignore_rules): - continue - checkpoint_var_name = name_format % match.groups() - if checkpoint_var_name.endswith(':0'): - checkpoint_var_name = checkpoint_var_name[:-2] - tf.logging.info('Loading %s from %s', model_var, checkpoint_var_name) - vars_to_load.append((checkpoint_var_name, model_var)) - break - return vars_to_load - - -def OverrideVarsFromCheckpoint(sess, all_vars, checkpoint_path, - variable_loading_rules, var_ignore_rules): - """Overrides variables from a provided checkpoint.""" - vars_to_load = _GetVarsToLoad(all_vars, variable_loading_rules, - var_ignore_rules) - if not vars_to_load: - raise ValueError(('Variable loading rules did not match any vars. ' - 'All known: %r') % [v.name for v in all_vars]) - load_var_names = sorted([v.name for _, v in vars_to_load]) - tf.logging.info('Overriding vars from checkpoint: %r', load_var_names) - - while vars_to_load: - # When restoring, it's possible the same value in the checkpoint - # can be restored to multiple variables (e.g. during - # distillation). However, tf.train.Saver, since it's used for - # both saving and restoring, requires the name in the checkpoint - # to be unique for each variable. So, we call it multiple times - # with a unique set of names each time. - unique_vars_to_load = {} - remaining_vars_to_load = [] - for k, v in vars_to_load: - if k not in unique_vars_to_load: - unique_vars_to_load[k] = v - else: - remaining_vars_to_load.append((k, v)) - tf.train.Saver(var_list=unique_vars_to_load).restore(sess, checkpoint_path) - vars_to_load = remaining_vars_to_load - - -def OverrideVarsFromCheckpoints(session, all_vars, ckpts_loading_rules): - """Overrides model variables from checkpoints. - - Args: - session: Tensorflow session. - all_vars: List of all the parameters in the model. - ckpts_loading_rules: A dictionary of checkpoint path: loading rules. - Checkpoint path must be a path to a pretrained model, and loading rules is - expected to be a tuple of two lists. The first consisting of tuples of - strings defining (regex to match parameter names in the model to override, - format string to determine the corresponding var in the checkpoint), and - the second list consisting of a list of regexes to match parameter names - in the model which should not be overridden, even if they match those in - the loading rules. - - Returns: - A list of overwritten variables. - - Raises: - ValueError: if colliding vars exist or loading rules is not a list. - """ - if len(ckpts_loading_rules) > 1: - tf.logging.info('Overriding vars from multiple checkpoints.') - - var_refs_overridden = set() - var_names_overridden = set() - for ckpt_path, loading_rules in ckpts_loading_rules.items(): - tf.logging.info('Overriding vars from checkpoint: %s', ckpt_path) - - if not isinstance(loading_rules, tuple): - raise ValueError('Loading rules for %s must be a tuple of two lists!' % - ckpt_path) - if len(loading_rules) != 2 or not all( - isinstance(l, list) for l in loading_rules): - raise ValueError('Loading rules for %s must be a tuple of two lists!' % - ckpt_path) - - # Filter the model variables to be overridden. - var_refs_to_override = [ - var[1].experimental_ref() - for var in _GetVarsToLoad(all_vars, loading_rules[0], loading_rules[1]) - ] - var_names_to_override = [ - var[1].name - for var in _GetVarsToLoad(all_vars, loading_rules[0], loading_rules[1]) - ] - - overlap_refs = set.intersection(var_refs_overridden, var_refs_to_override) - if overlap_refs: - raise ValueError('Colliding variables to override: %s' % overlap_refs) - - OverrideVarsFromCheckpoint(session, all_vars, ckpt_path, loading_rules[0], - loading_rules[1]) - var_refs_overridden.update(var_refs_to_override) - var_names_overridden.update(var_names_to_override) - tf.logging.info('Model variables overridden: %s', var_refs_overridden) - return var_names_overridden - - -def ComputeGradientsSimple(loss, all_vars, grad_aggregation_method, - colocate_gradients_with_ops, gate_gradients): - return tf.gradients( - loss, - all_vars, - aggregation_method=grad_aggregation_method, - colocate_gradients_with_ops=colocate_gradients_with_ops, - gate_gradients=gate_gradients) - - -def ComputeTpuEmbeddingGradients(loss, activation_dict, tpu_embedding): - """Returns a TpuEmbedding SendGradient op. - - Args: - loss: The loss to backprop from. - activation_dict: String feature -> embedding activations dict. - tpu_embedding: TPUEmbedding instance. - """ - - # Scale the loss to account for the full batch size. - shards = tpu_function.get_tpu_context().number_of_shards - loss *= tf.constant(1.0 / shards, dtype=loss.dtype) - - grads = tf.gradients(loss, list(activation_dict.values())) - feature_to_gradient_dict = py_collections.OrderedDict( - zip(list(activation_dict.keys()), grads)) - send_gradient_op = tpu_embedding.generate_send_gradients_op( - feature_to_gradient_dict) - return send_gradient_op - - -def _ComputeGradientsTpu(loss, - all_vars, - grad_aggregation_method, - colocate_gradients_with_ops, - gate_gradients, - skip_zero_gradients=None, - use_bf16_gradients_ar=False, - defer_crs_to_apply_grad=False): - """Computes gradients for local loss across whole TPU cluster. - - This implementation specializes for the case where weight params maybe used - for different number of times in the forward computation, so that gradients - should be normalized by the actual number of times they are being computed. - - TODO(yonghui): Maybe merge this implementation with the _ComputeGradientsTpu - one. - - Args: - loss: The loss to backprop from. - all_vars: Vars with respect to which gradients are to be computed. - grad_aggregation_method: aggregation method to use when calling - tf.gradients. - colocate_gradients_with_ops: boolean, whether or not to colocate gradient op - with the original op. - gate_gradients: boolean, flag to be passed to tf.gradients. - skip_zero_gradients: whether to skip zero gradients during aggregation. - use_bf16_gradients_ar: Whether to use bfloat16 dtype for gradients - all-reduce. - defer_crs_to_apply_grad: Whether to defer gradient cross replica sum to - apply_gradient. This helps reducing the number of gradient all-reduces - when doing gradient accumulation, which does gradient cross replica sum - only every k steps in a tf.cond. Currently this works only when - skip_zero_gradients is None. - - Returns: - Gradients to be passed back. - - Raises: - ValueError: upon invalid arguments. - """ - if not skip_zero_gradients: - # Scale the loss to account for the full batch size. - shards = tpu_function.get_tpu_context().number_of_shards - assert shards - loss *= tf.constant(1.0 / shards, dtype=loss.dtype) - - # Computes the gradients. - # Sum the grads so that we can compute statistics across the whole batch. - all_grads = ComputeGradientsSimple(loss, all_vars, grad_aggregation_method, - colocate_gradients_with_ops, - gate_gradients) - - # NOTE: We can't use tpu_optimizer.CrossShardOptimizer since - # we need to scale the grads *after* the cross_replica_sum to - # match GPU version! - - # TODO(cwhipkey): should we do something different here? - we could do - # some operations on the gradients before the aggregation (see comments in - # tensorflow/contrib/tpu/python/tpu/tpu_optimizer.py - see compute_gradients - - # for some more details). - - aggregated_grads = [] - for g in all_grads: - if g is None: - aggregated_grads.append(None) - continue - if use_bf16_gradients_ar: - g = tf.cast(g, tf.bfloat16) - with tf.ops.colocate_with(g): - if skip_zero_gradients is None: - # loss is already scaled by 1/shards. - if defer_crs_to_apply_grad: - normalized_g = tf.convert_to_tensor(g) - else: - normalized_g = tf.tpu.cross_replica_sum(g) - else: - # Compute the cross-replica mean of 'g', skipping zero gradients. - - # Q(yonghui): Is there a better way to detect a non-zero gradient? - # Note(yonghui): gradient of a weight can be zero if that - # weight is not used in the forward computation, e.g. as in - # switchable layers in neural architecture search, pruned by channel - # mask, or sparsified. - if skip_zero_gradients == 'weight': - # Same shape as 'g'. - g_is_non_zero = tf.cast(tf.math.abs(g) > 1e-8, g.dtype) - elif skip_zero_gradients == 'variable': - # A variable-wide 0/1 scalar. - g_is_non_zero = tf.cast( - tf.reduce_sum(tf.math.abs(g)) > 1e-24, g.dtype) - else: - raise ValueError('Unknown skip_zero_gradients: %s' % - skip_zero_gradients) - num_updates = tf.maximum(tf.tpu.cross_replica_sum(g_is_non_zero), 1.0) - normalized_g = tf.tpu.cross_replica_sum(g) / num_updates - aggregated_grads.append(normalized_g) - return aggregated_grads - - -class VarGrad: - """A class that holds a variable and a gradient.""" - - _VAR_GRAD = py_collections.namedtuple('VarGradNamedTuple', ['var', 'grad']) - - def __init__(self, *args, **kwargs): - self._var_grad = self._VAR_GRAD(*args, **kwargs) - - def __getitem__(self, key): - return self._var_grad[key] - - def __getattr__(self, key): - return getattr(self._var_grad, key) - - def __iter__(self): - return iter(self._var_grad) - - def __repr__(self): - return 'VarGrad(%r, %r)' % (self._var_grad.var, self._var_grad.grad) - - -def SkipNoneGradients(var_grads): - """Removes pairs whose grad is None.""" - for key, (_, g) in var_grads.FlattenItems(): - if g is None: - tf.logging.info('ComputeGradients drops %s', key) - return var_grads.Filter(lambda var_grad: var_grad.grad is not None) - - -def ComputeGradients( - loss, - vmap, - grad_aggregation_method=tf.AggregationMethod.EXPERIMENTAL_TREE, - colocate_gradients_with_ops=True, - gate_gradients=False, - compute_gradients_fn=None, - skip_zero_gradients=None, - use_bf16_gradients_ar=False, - skip_none_gradients=True, - defer_crs_to_apply_grad=False): - """Computes gradients of variables in vmap w.r.t loss. - - Args: - loss: A scalar Tensor. - vmap: A `.NestedMap` of variables. - grad_aggregation_method: Specifies the method used to combine gradient - terms. Accepted values are constants defined in the class - AggregationMethod. - colocate_gradients_with_ops: If True, try colocating gradients with the - corresponding op. - gate_gradients: If True, add a tuple around the gradients returned for an - operations. This avoids some race conditions. - compute_gradients_fn: Function to use to compute gradients. If None, use - default. compute_gradients_fn should have the same signature as this - function, but without the last argument. - skip_zero_gradients: Whether to skip aggregating zero gradients. This helps - in case where some weights may not be used in forward computation, e.g., - sparsely activated networks or switchable layers in neural architectural - search. Only applicable on TPU. - Possible values are: - - * None: do not skip zero gradients; - * `variable`: skip if the entire variable's gradients are almost zero; - reduce_sum(abs(grads)) < 1e-8. - * `weight`: skip if the individual weight's gradients are almost zero: - abs(grad) < 1e-8. - use_bf16_gradients_ar: Whether to use bfloat16 dtype for gradients - all-reduce. This applies to TPU only. - skip_none_gradients: Whether to skip gradients that are None. - defer_crs_to_apply_grad: Whether to defer gradient cross replica sum to - apply_gradient. This applies to TPU only. - - Returns: - var_grad - a `.NestedMap` of VarGrad. You can view - var_grad as an ordered list of (key, (var, grad)) tuples. Every - key of var_grad exists in vmap. Every variable in vmap that - contributes to loss must exist in var_grad. Every var of var_grad - must exist in vmap. grad is the corresponding gradient computed - for var. grad is guaranteed to be not None. - """ - loss = HasRank(loss, 0) - assert isinstance(vmap, NestedMap) - assert skip_zero_gradients in (None, 'variable', 'weight') - - # Uniqify and remove None. - filtered_vmap = vmap.Filter(_Unique()) - assert filtered_vmap is not None - - # Filter out variables not contributing to 'loss'. - trainable_variables = set(tf.trainable_variables()) - dependent_ops_and_tensors = set(FindNeeded([loss])) - - def Needed(v): - if isinstance(v, tf.Variable): - if v not in trainable_variables: - # Skip non-trainable variables. Otherwise, - # tf.Optimizer.apply_gradients throws up an exception instead - # of skipping the update. - return False - return True - - filtered_vmap = filtered_vmap.Filter(Needed) - assert filtered_vmap is not None - filtered_vlist = filtered_vmap.Flatten() - - # Use caller-supplied gradient function if supplied. - if compute_gradients_fn is not None: - take_grad = compute_gradients_fn - else: - # tpu vs non-tpu is slightly different. - if use_tpu(): - take_grad = functools.partial( - _ComputeGradientsTpu, - skip_zero_gradients=skip_zero_gradients, - use_bf16_gradients_ar=use_bf16_gradients_ar, - defer_crs_to_apply_grad=defer_crs_to_apply_grad) - else: - take_grad = ComputeGradientsSimple - - grads = take_grad(loss, filtered_vlist, grad_aggregation_method, - colocate_gradients_with_ops, gate_gradients) - - # Formulate pairs of (var, grad) and pack them into the same - # structure as filtered_vmap. - var_grads = filtered_vmap.Pack( - [VarGrad(v, g) for v, g in zip(filtered_vlist, grads)]) - - # TPU training is not compatible with the variable name check below when - # control flow v2 is enabled. The main reason is the body function will be - # encapsulated as a TF function while variables will be lifted out, and as a - # result dependent_ops_and_tensors will not contain any variables. See - # b/150689507 for more info. - if not tf.compat.v1.control_flow_v2_enabled(): - # Check that gradients for variables that are not needed by current task is - # empty. - def CheckGrad(vg): - if vg.var.name not in dependent_ops_and_tensors and vg.grad is not None: - err_msg = ('Variable %s is not a dependent of %s, expect ' - 'gradient be None, but got %s. This should not happen, ' - 'please contact the owner of b/150689507 for further ' - 'investigation.' % (str(vg.var), str(loss), str(vg.grad))) - assert False, err_msg - return True - - var_grads = var_grads.Filter(CheckGrad) - - if skip_none_gradients: - var_grads = SkipNoneGradients(var_grads) - - return var_grads - - -def MaskGradients(var_grad, grad_mask): - """Computes gradients of non-masked variables in vmap w.r.t loss. - - Args: - var_grad: A `.NestedMap` of (variable, gradient) - grad_mask: A dict of (variable name, mask). - - Returns: - var_grad - a `.NestedMap` of (variable, mask * gradient). - """ - - def ApplyMask(entry): - var, grad = entry - mask = grad_mask[var.name] - if isinstance(grad, tf.IndexedSlices): - return VarGrad(var, tf.IndexedSlices(grad.values * mask, grad.indices)) - else: - return VarGrad(var, grad * mask) - - return var_grad.Transform(ApplyMask) - - -def ApplyGradMultiplier(vs_gs, grad_scale=None): - """Scale gradients by grad_scale on same device as corresponding variables. - - Args: - vs_gs: A `.NestedMap` of VarGrad. - grad_scale: If None, each vs_gs entry has the scale. Otherwise, grad_scale - applies to every entry. - - Returns: - A `.NestedMap` of (variable, gradient * grad_scale). In particular, if - grad_scale is 0, the result gradient is always 0, even if the input - gradient is inf or nan. - """ - - def ScaleOrZero(var, grad, scale): - grad = CheckNumerics(grad, 'Gradient for %s is not finite.' % var.name) - return tf.where( - tf.equal(scale, 0.), tf.zeros_like(grad), - tf.cast(scale, grad.dtype) * grad) - - def Scale(item): - """Scales the gradient.""" - var, grad = item - assert grad is not None, ('No grad found for ', var.name) - if grad_scale is None: - scale = item.scale - else: - scale = grad_scale - with tf.device(var.device): - if isinstance(grad, tf.IndexedSlices): - grad = tf.IndexedSlices( - ScaleOrZero(var, grad.values, scale), grad.indices, - grad.dense_shape) - else: - grad = ScaleOrZero(var, grad, scale) - return VarGrad(var, grad) - - return vs_gs.Transform(Scale) - - -def HasNanOrInfGradient(var_grads): - """Returns a bool tensor to indicate if `var_grads` contains NaNs or Infs. - - Args: - var_grads: A `.NestedMap` with (var, grad) tuple as the map value. - - Returns: - A bool scalar tensor to indicate if the `var_grads` contains NaNs or Infs. - """ - - def HasNanOrInf(x): - if isinstance(x, tf.IndexedSlices): - x = x.values - with tf.device(x.device): - if x.dtype.is_complex: - return tf.reduce_any( - [HasNanOrInf(tf.math.real(x)), - HasNanOrInf(tf.math.imag(x))]) - return tf.reduce_any( - tf.math.logical_or(tf.math.is_nan(x), tf.math.is_inf(x))) - - return tf.reduce_any([HasNanOrInf(g) for (_, g) in var_grads.Flatten()]) - - -def ApplyGradNormClipping(vs_gs, norm=1.0): - """Clip gradients to norm on same device as corresponding variables. - - Args: - vs_gs: A `.NestedMap` of VarGrad. - norm: Each tensor's gradient will be scaled down to have a maximum L2-norm - value of `norm`. - - Returns: - A `.NestedMap` of VarGrad(variable, scaled_gradient). In particular, if - grad_scale is 0, the result gradient is always 0, even if the input - gradient is inf or nan. - """ - - def ClipByNorm(var, grad, norm): - grad = CheckNumerics(grad, 'Gradient for %s is not finite.' % var.name) - return tf.clip_by_norm(grad, norm) - - def Clip(item): - """Scales the gradient.""" - var, grad = item - assert grad is not None, ('No grad found for ', var.name) - with tf.device(var.device): - if isinstance(grad, tf.IndexedSlices): - grad = tf.IndexedSlices( - ClipByNorm(var, grad.values, norm), grad.indices, grad.dense_shape) - else: - grad = ClipByNorm(var, grad, norm) - return VarGrad(var, grad) - - return vs_gs.Transform(Clip) - - -SKIP_LP_REGULARIZATION = '__lingvo_skip_lp_regularization' - - -def AdjustGradientsWithLpLoss(var_grads, lp_regularizer_weight, p=2.0): - """Adjusts the map of (var, grad) with Lp regularization, where p=1.0 or 2.0. - - Args: - var_grads: a `.NestedMap` or list of (variable, gradient). - lp_regularizer_weight: Lp regularization weight. - p: For now we support 1.0 or 2.0. - - Returns: - A tuple (lp_loss, var_grads). - - - lp_loss: A scalar. The lp loss. - - var_grads: a `.NestedMap` or list of (variable, gradient) regulated by Lp. - """ - # TODO(yuancao): For now we support p=1 or 2, but this can be extended to - # lp-norm in general. - - assert p in [2.0, 1.0], 'For now we only support L1/L2 regularization.' - - def GetVar(item): - var, grad = item - if isinstance(grad, tf.IndexedSlices): - with tf.device(var.device): - ids = HasRank(grad.indices, 1) - uniq_ids = tf.unique(ids).y - return tf.gather(var, uniq_ids) - else: - return var - - def ShouldAdjust(v): - return v not in tf.get_collection(SKIP_LP_REGULARIZATION) - - filtered_var_grads = [ - var_grad for var_grad in Flatten(var_grads) if ShouldAdjust(var_grad.var) - ] - filtered_vars = Transform(GetVar, filtered_var_grads) - for v in filtered_vars: - tf.logging.info('AdjustGradientsWithLpLoss: %s', v.name) - - if p == 2.0: - lp_loss = 0.5 * lp_regularizer_weight * SumSquared(filtered_vars) - elif p == 1.0: - lp_loss = lp_regularizer_weight * SumAbs(filtered_vars) - - def LpGrad(var_grad): - """Adjusts item's grad w/ Lp loss term.""" - var, grad = var_grad - if isinstance(grad, tf.IndexedSlices): - # Question(rpang): do we apply Lp loss here even if 'var' is in - # SKIP_LP_REGULARIZATION? - # - # Note: IndexedSlces appears for embedding lookups. - # Embedding lookup ids can have duplicate. For duplicated ids, we - # only want to consider once for each ids. - with tf.device(var.device): - emb = HasRank(var, 2) - vocab_size = tf.shape(emb)[0] - ids = HasRank(grad.indices, 1) - values = tf.gather(emb, ids) # [#ids, dims] - with tf.device(grad.device): - # Counts is a vector of size vocab_size. counts[i] is i-th words - # occurances in 'ids'. - counts = tf.math.unsorted_segment_sum( - tf.ones_like(ids, dtype=values.dtype), ids, vocab_size) - - # Gradients for duplicated ids will be summed when they get - # applied, and hence we account for that by first dividing - # gradient resulting from lp loss by how many times the id is - # duplicated. - # - # For each id in 'ids', we know counts[id] is non-zero, - # hence, it's always safe to take reciprocal. - weights = tf.math.reciprocal(tf.gather(counts, ids)) - weights = tf.expand_dims(weights, -1) # [#ids, 1] - if p == 2.0: - grad_v = values - elif p == 1.0: - grad_v = tf.sign(values) - delta = lp_regularizer_weight * weights * grad_v - grad = tf.IndexedSlices(grad.values + delta, ids) - elif var not in tf.get_collection(SKIP_LP_REGULARIZATION): - with tf.device(var.device): - if p == 2.0: - grad_v = var - elif p == 1.0: - grad_v = tf.sign(var) - delta = lp_regularizer_weight * grad_v - with tf.device(grad.device): - grad += delta - return VarGrad(var, grad) - - return lp_loss, Transform(LpGrad, var_grads) - - -def SplitRecursively(x, num_splits, axis=-1): - """Splits Tensors in 'x' recursively. - - Args: - x: a Tensor, or a list or NestMap containing Tensors to split. - num_splits: number of splits per Tensor. - axis: the split axis. - - Returns: - A list of split values of length 'num_splits'. - - - If 'x' is a Tensor, a list of split Tensors. - - If 'x' is a list, a list of lists, where each sublist has the same length - as 'x' and the k'th element in each sublist corresponds to a split of the - k'th element from 'x'. - - If 'x' is a `.NestedMap`, a list of `.NestedMap`, where each field - corresponds to a split from the same field of 'x'. - """ - if isinstance(x, tf.Tensor): - return tf.split(x, num_splits, axis=axis) - elif isinstance(x, list): - splits = [SplitRecursively(element, num_splits, axis) for element in x] - splits = list(zip(*splits)) - return [list(t) for t in splits] - elif isinstance(x, NestedMap): - results = [NestedMap() for _ in range(num_splits)] - for key, val in x.items(): - val_splits = SplitRecursively(val, num_splits, axis) - for i in range(num_splits): - results[i][key] = val_splits[i] - return results - else: - raise TypeError('Unexpected type for SplitRecursively: %s' % type(x)) - - -def ConcatRecursively(splits, axis=-1): - """Concatenates tensors from 'splits'. - - This is the inverse function of SplitRecursively. - - Args: - splits: a list of splits to concatenate, where elements can be Tensors, - lists, or `.NestedMap`. The elements must share the same type and - structure. For example, list elements must have the same length; - `.NestedMap` must have the same set of fields. - axis: the concatenation axis. - - Returns: - Concatenated data. - - - If input 'splits' are Tensors, returns a concatenated Tensor. - - If input 'splits' are lists, returns a list of the same length where the - k'th element represents concatenated data of the k'th element from each - split. - - If input 'splits' are `.NestedMap`, returns a `.NestedMap` with each field - concatenated from corresponding fields of input splits. - - Raises: - TypeError: if 'splits' is not a list or elements of 'splits' do not have - known or matching types. - ValueError: if 'splits' is empty or elements of 'splits' do not have - matching structures. - """ - if not isinstance(splits, list): - raise TypeError('Non-list inputs for ConcatRecursively: %s' % splits) - if not splits: - raise ValueError('Empty inputs for ConcatRecursively: %s' % splits) - - tmpl = splits[0] - - if isinstance(tmpl, tf.Tensor): - return tf.concat(splits, axis=axis) - elif isinstance(tmpl, list): - if not all(isinstance(split, list) for split in splits): - raise TypeError('Type mismatch for ConcatRecursively: %s' % splits) - if not all(len(split) == len(tmpl) for split in splits): - raise ValueError('Length mismatch for ConcatRecursively: %s' % splits) - return [ - ConcatRecursively([split[i] - for split in splits], axis) - for i in range(len(tmpl)) - ] - elif isinstance(tmpl, NestedMap): - if not all(isinstance(split, NestedMap) for split in splits): - raise TypeError('Type mismatch for ConcatRecursively: %s' % splits) - results = NestedMap() - for key in tmpl: - results[key] = ConcatRecursively([split[key] for split in splits], axis) - return results - else: - raise TypeError('Unexpected type for ConcatRecursively: %s' % type(splits)) - - -def WeightedAvg(values, weights, sum_reduction_fn=tf.reduce_sum, name=''): - """Computes weighted average of values from a tensor. - - Args: - values: a tensor of values - weights: a tensor of weights - sum_reduction_fn: called to reduce the values and weights to single value - name: name of metric. - - Returns: - A tuple (avg, total_weight). - - - avg: weighted average value - - total_weight: sum of all weights - """ - msg = 'shape of values and weights tensors must match for metric ' + name - values = with_dependencies( - [assert_equal(tf.shape(values), tf.shape(weights), message=msg)], values) - total_weight = sum_reduction_fn(weights) - # divide_no_nan only supports tf.{float,complex}*. - dtype = values.dtype if values.dtype is tf.float64 else tf.float32 - avg = tf.math.divide_no_nan( - sum_reduction_fn(tf.cast(values, dtype) * tf.cast(weights, dtype)), - tf.cast(total_weight, dtype)) - return tf.cast(avg, values.dtype), total_weight - - -def WeightedAvgOfMetrics(metrics): - """Computes the weighted average of metrics in the list. - - Args: - metrics: list of dictionaries of metrics - - Returns: - ret_dict - dictionary of weighted averages of each metrics. - """ - ret_dict = {} - lists_of_metrics = {} - for m in metrics: - for name, (value, weight) in m.items(): - if name not in lists_of_metrics: - lists_of_metrics[name] = [] - lists_of_metrics[name].append((value, weight)) - - for name, values_and_weights in sorted(lists_of_metrics.items()): - values = tf.stack([x[0] for x in values_and_weights]) - weights = tf.stack([x[1] for x in values_and_weights]) - ret_dict[name] = WeightedAvg(values, weights, tf.reduce_sum, name) - - return ret_dict - - -def ConcatPerExampleTensors(per_example): - """Concatenate per-example tensors from many hosts into one large block. - - Args: - per_example: list of dictionaries of per-example tensors. - - Returns: - ret_dict - string -> concatenated tensors. - """ - ret_dict = {} - lists_of_per_example = {} - for m in per_example: - for name, value in m.items(): - if name not in lists_of_per_example: - lists_of_per_example[name] = [] - lists_of_per_example[name].append(value) - - for name, values in sorted(lists_of_per_example.items()): - ret_dict[name] = tf.concat(values, 0) - - return ret_dict - - -def CombineMetrics(loss_metric_weight_pairs): - """Combines metrics from `loss_metric_weight_pairs` according to weights. - - Keys must either exist in all metrics, in which it will be processed as a - weighted sum, or exist in only one metrics, in which case it will be copied. - - Args: - loss_metric_weight_pairs: a list of (metrics, weight) pairs, where each - weight is a float and each metrics is a dict with str keys and - (metric_value, target_weight) values. - - Returns: - A dict with the same set of keys as input metrics and values of - (weighted_sum(metric_value), weighted_sum(target_weight)). - - Raises: - ValueError: if there exists a metric that exists in more than one element - of `loss_metric_weight_pairs` but not in all of them. - """ - all_keys = set( - [k for loss_metrics, _ in loss_metric_weight_pairs for k in loss_metrics]) # pylint: disable=g-complex-comprehension - result = {} - for k in all_keys: - count = 0 - for loss_metrics, weight in loss_metric_weight_pairs: - if k in loss_metrics: - count += 1 - if count > 1 and count != len(loss_metric_weight_pairs): - raise ValueError('Found metric %s which exists in more than one' - 'but not all loss metrics.' % k) - - total_val = 0 - total_target_weight = 0 - for loss_metrics, weight in loss_metric_weight_pairs: - if k in loss_metrics: - val, target_weight = loss_metrics[k] - if count == 1: - # Single metric, don't multiply by weight. - total_val = val * target_weight - total_target_weight = target_weight - else: - # Total weighted sum of all predictions. - total_val += weight * val * target_weight - total_target_weight += weight * target_weight - - result[k] = (total_val / total_target_weight, total_target_weight) - return result - - -def _AddVN(p, x, step=None): - assert p.vn.scale is not None - seed = p.vn.seed - if seed and step: - seed += step * 203984 - # So a seed is provided based on the step. So how does TPU generate - # random numbers then? Would be good to dig intot he xla implementation. - noises = tf.cast(p.vn.scale, x.dtype) * tf.random.normal( - tf.shape(x), stddev=1.0, seed=seed, dtype=x.dtype) - return x + noises - - -def AddGlobalVN(params, weights): - """Adds variational noise to weights if specified by params.""" - p = params - if p.vn.global_vn: - weights = _AddVN(p, weights) - return weights - - -def AddPerStepVN(params, weights, step=None): - """Adds per-setp variational noise to weights if specified by params.""" - p = params - if p.vn.per_step_vn: - weights = _AddVN(p, weights, step) - return weights - - -def VariationalNoiseParams(scale, - global_vn=False, - per_step_vn=False, - seed=None): - """Returns a hyperparams for variational noise.""" - p = hyperparams.Params() - p.Define( - 'scale', scale, - 'Std of the variational noise to apply . This can be a scalar,' - ' or a scalar tensor.') - p.Define('global_vn', global_vn, - 'Adds global variational noise every training setp iff True.') - p.Define('per_step_vn', per_step_vn, - 'Adds per-timesetp variational noise iff True.') - p.Define('seed', seed, 'Random seed used to generate noise.') - return p - - -def DefaultVN(): - return VariationalNoiseParams(None, False, False) - - -# To disable VN of a layer, we use 1.0 in the first input parameter -# of the following function because otherwise it is the same to DefaultVN() -# which will be updated by parent configuration in CopyBaseParams() -def DisableVN(): - return VariationalNoiseParams(1.0, False, False) - - -def GetStepSeed(): - """Gets step_seed.""" - step_seed_tensors = tf.get_default_graph().get_collection_ref('step_seed') - if not step_seed_tensors: - ResetStepSeed() - return GetStepSeed() - elif len(step_seed_tensors) == 1: - return step_seed_tensors[0] - else: - raise ValueError('Multiple tensors in step_seed collection.') - - -def ResetStepSeed(seed=0): - """Resets step_seed to specified value.""" - new_step_seed = tf.convert_to_tensor(seed, dtype=tf.int64) - step_seed_tensors = tf.get_default_graph().get_collection_ref('step_seed') - if len(step_seed_tensors) == 1: - step_seed_tensors[0] = new_step_seed - elif not step_seed_tensors: - tf.add_to_collection('step_seed', new_step_seed) - else: - raise ValueError('Multiple tensors in step_seed collection.') - - -def GetIncStepSeed(): - """Returns and increments the step_seed.""" - step_seed = GetStepSeed() - # TODO(lepikhin): introduce a routine filling a queue of uint32 random seeds - # independent of underlying PRNG used by tensorflow. - ResetStepSeed(step_seed + 1) - return step_seed - - -def GenerateStepSeedPair(p, global_step, op_seed=None): - """Generates a seed pair for deterministic random operations in ... - - functional loops. - - This function retrieves a unique seed pair on each call, based off the current - global step and step seed. The step seed ensures this function returns a - unique seed pair on each call: calling this function automatically increments - the step seed. The step seed is automatically reset at the beginning of each - global step in the model's FProp and works transparently through recurrent.py. - - Args: - p: A hyperparams.Params object, containing keys 'random_seed' and - 'is_inference'. - global_step: The global step. - op_seed: An additional operation-level seed to apply. - - Returns: - A size 2 tensor of op seeds to use for stateless_random ops. - """ - seed_dtype = tf.int32 if use_tpu() else tf.int64 - if p.is_inference and p.random_seed is None: - # Ensure GetIncStepSeed is called even inside the shortcut. - # This ensures if p.random_seed is set for other ops that use this function - # that they will get the same seed pair whether or not p.random_seed is set - # for this specific call. - GetIncStepSeed() - # Unlike tf.random*, stateless random ops are completely determined by the - # passed-in seeds. This means at inference time the same inputs will produce - # the same outputs, even if the model is supposed to have randomness such as - # dropout during inference. We inject additional randomness only during - # inference if the graph is exported with random_seed=None as a workaround. - return tf.random.uniform([2], maxval=seed_dtype.max, dtype=seed_dtype) - - global_step = tf.cast(global_step, seed_dtype) - step_seed = tf.cast(GetIncStepSeed(), seed_dtype) - seeds = tf.stack([global_step, step_seed]) - - if p.random_seed is not None: - seeds += p.random_seed - if op_seed is not None: - seeds += op_seed - return seeds - - -def DeterministicDropout(x, keep_prob, seeds, noise_shape=None, name=None): - """Similar to `tf.nn.dropout()`, but fully deterministic. - - Args: - x: A float Tensor on which to apply dropout. - keep_prob: A scalar `Tensor` of keep probability. - seeds: A Tensor of shape [2]. 2 seeds for deterministic random number - generator. - noise_shape: A 1-D `Tensor` of type `int32`, representing the shape for - randomly generated keep/drop flags. - name: An optional name for this operation. - - Returns: - A Tensor with the same shape as `x`. - - Raises: - InvalidArgumentError: if keep_prob is invalid. - """ - if isinstance(keep_prob, numbers.Real): - if keep_prob <= 0 or keep_prob > 1: - raise tf.errors.InvalidArgumentError( - 'keep_prob must be in range (0, 1]. Value: {}'.format(keep_prob)) - - if keep_prob == 1: - return x - with tf.name_scope(name, 'dropout', [x]) as name: - if use_tpu(): - seeds = tf.cast(seeds, tf.int32) - keep_prob = tf.convert_to_tensor( - keep_prob, dtype=tf.float32, name='keep_prob') - # uniform in [keep_prob, 1.0 + keep_prob) - # StatelessRandomUniform op does not support non-float (e.g. bfloat16) dtype - # and non-int32 seed types. - noise_shape = noise_shape or GetShape(x) - random_tensor = keep_prob + tf.random.stateless_uniform( - noise_shape, seed=seeds, dtype=tf.float32) - # 0. if [keep_prob, 1.0) and 1. if [1.0, 1.0 + keep_prob) - binary_tensor = tf.floor(random_tensor) - if x.dtype != tf.float32: - binary_tensor = tf.cast(binary_tensor, x.dtype) - keep_prob = tf.cast(keep_prob, dtype=x.dtype) - result = tf.div(x, keep_prob) * binary_tensor - result.set_shape(x.get_shape()) - return result - - -def DeterministicVN(params, seeds, noise_shape, mean=0.0, std=1.0, name=None): - """Produces Fully deterministic Gaussian noise from shape, mean and std. - - Args: - params: Nested map of params. - seeds: A Tensor of shape [2]. 2 seeds for deterministic random number - generator. - noise_shape: A 1-D `Tensor` of type `int32`, representing the shape for - randomly generated Gaussian noise. - mean: Mean for the Gaussian noise. - std: Standard deviation for noise. - name: An optional name for this operation. - - Returns: - A Tensor with the shape noise_shape and type fprop_dtype. - """ - - with tf.name_scope(name, 'gaussian_noise') as name: - if use_tpu(): - seeds = tf.cast(seeds, tf.int32) - random_tensor = mean + ( - std * tf.random.stateless_normal(noise_shape, seed=seeds)) - if FPropDtype(params) != tf.float32: - random_tensor = tf.cast(random_tensor, FPropDtype(params)) - return random_tensor - - -BATCH_NORM_UPDATES = 'batch_norm_updates' - -_BATCH_NORM_UPDATES_DICT = '__batch_norm_update_dict' -_get_batch_norm_updates_dict = _CollectionGetter(_BATCH_NORM_UPDATES_DICT, - lambda: {}) - - -def UpdateBatchNormVars(batch_norm_var, batch_norm_stats, decay): - """Update batch normalization moving averages.""" - with tf.name_scope( - 'AssignMovingAvg', values=[ - batch_norm_var, - batch_norm_stats, - decay, - ]) as scope: - with tf.ops.colocate_with(batch_norm_var): - decay = tf.convert_to_tensor( - 1.0 - decay, dtype=batch_norm_var.dtype.base_dtype) - update_delta = (batch_norm_var - tf.cast( - batch_norm_stats, batch_norm_var.dtype.base_dtype)) * decay - has_nan_or_inf = tf.reduce_any( - tf.math.logical_or( - tf.math.is_nan(update_delta), tf.math.is_inf(update_delta))) - update_delta = tf.cond(has_nan_or_inf, - lambda: tf.zeros_like(update_delta), - lambda: update_delta) - bn_update = tf.assign_sub(batch_norm_var, update_delta, name=scope) - tf.add_to_collection(BATCH_NORM_UPDATES, bn_update) - bn_update_dict = _get_batch_norm_updates_dict() - assert bn_update.name not in bn_update_dict - bn_update_dict[bn_update.name] = (batch_norm_var, batch_norm_stats) - return bn_update - - -def FindRelevantBatchNormUpdates(loss, batch_norm_updates): - """Finds and returns a list of relevant batch-normalization updates. - - Args: - loss: The loss that is being optimized for. A tensor or a list of tensors. - batch_norm_updates: A list of batch normalization updates. - - Returns: - A pair of lists. The first list contains all the batch normalization updates - that are relevant to the loss being optimized, and the second list contains - all in batch_norm_updates but not in the first list. - """ - dependent_ops_and_tensors = set(FindNeeded(loss)) - relevant_updates = [] - irrelevant_updates = [] - - bn_update_dict = _get_batch_norm_updates_dict() - for bn_update in batch_norm_updates: - assert bn_update.name in bn_update_dict, ( - '%s is probably not a valid batch normalization update op.' - ' Make sure batch normalization is done through calling' - ' the py_utils.UpdateBatchNormVars helper routine.') - bn_stat_name = bn_update_dict[bn_update.name][1].name - if bn_stat_name in dependent_ops_and_tensors: - # If a batch normalization stat is computed in the forward pass in - # computing loss, then the corresponding batch normalization update is - # relevant. Otherwise, it is not. - relevant_updates.append(bn_update) - else: - irrelevant_updates.append(bn_update) - return relevant_updates, irrelevant_updates - - -_SAMPLE_STEP_STACK = ThreadLocalStack() - - -@contextlib.contextmanager -def SampleStep(step): - """A context for a sample step during decoding. - - Example usage:: - - with py_utils.SampleStep(step): - sample = self.DecodeOneStep() - - Args: - step: the step tensor. - - Yields: - a context manager for the step scope. - """ - try: - _SAMPLE_STEP_STACK.stack.append(step) - yield step - finally: - _SAMPLE_STEP_STACK.stack.pop() - - -def _GetSampleStep(): - return _SAMPLE_STEP_STACK.stack[-1] if _SAMPLE_STEP_STACK.stack else None - - -def AddDebugTensor(tensor, summarize=None, name=None): - """Adds `tensor` to the debug collection. - - Prints the tensor if `--print_debug_tensors` is True. - - Args: - tensor: A tensor. - summarize: Only print this many entries of each tensor. If None, then a - maximum of 3 elements are printed per input tensor. - name: An optional name for the tensor. - - Returns: - A Tensor that evaluates to the same value as the input tensor. - """ - if _FromGlobal('print_debug_tensors'): - step = _GetSampleStep() - tensors_to_print = ([] if step is None else [step]) + [tensor] - with tf.name_scope(name) as s: - tensor = tf.Print( - tensor, - tensors_to_print, - message='DEBUG tensor %s' % s, - name=name, - summarize=summarize) - return tensor - - -def ArgMax(inputs): - """tf.argmax wrapper. - - Args: - inputs: A tensor, whose last dimension is being reduced on. - - Returns: - A tensor of rank tf.rank(logits)-1. If i == ret[indices], - logits[indices, i] is the maximum among logits[indices, :]. - """ - if use_tpu(): - return tf.argmax(inputs, axis=-1, output_type=tf.int32) - else: - return tf.argmax(inputs, axis=-1) - - -def _EnsureMatrixShape(x): - if x.shape.ndims is None: - x.set_shape([None, None]) - else: - assert x.shape.ndims == 2 - return x - - -def Matmul(x, y, *args, **kwargs): - """tf.matmul wrapper expecting x and y are actually matrices.""" - x = _EnsureMatrixShape(x) - y = _EnsureMatrixShape(y) - return tf.matmul(x, y, *args, **kwargs) - - -def clip_by_value(t, clip_value_min, clip_value_max, name=None): # pylint: disable=invalid-name - if t.dtype.is_complex: - return tf.complex( - tf.clip_by_value( - tf.math.real(t), clip_value_min, clip_value_max, '%s_real' % name), - tf.clip_by_value( - tf.math.imag(t), clip_value_min, clip_value_max, '%s_imag' % name)) - return tf.clip_by_value(t, clip_value_min, clip_value_max, name) - - -def _TransformAndSum(tensor_list, transform): - with tf.name_scope('TransformAndSum'): - sum_transform = [] - for t in tensor_list: - with tf.device(t.device): - if isinstance(t, tf.IndexedSlices): - sum_transform += [tf.reduce_sum(transform(t.values))] - else: - sum_transform += [tf.reduce_sum(transform(t))] - return tf.add_n(sum_transform) - - -def SumSquared(tensor_list): - return _TransformAndSum(tensor_list, lambda v: tf.abs(v)**2) - - -def SumAbs(tensor_list): - return _TransformAndSum(tensor_list, tf.abs) - - -def PiecewiseConstant(x_in, boundaries, values, vdtype): - """Returns the piecewise value of x_in.""" - x_in = tf.cast(tf.convert_to_tensor(x_in), tf.float32) - assert len(values) == len(boundaries) + 1 - assert sorted(boundaries) == list(boundaries) - bs = tf.convert_to_tensor(boundaries, dtype=tf.float32) - vs = tf.convert_to_tensor(values, dtype=vdtype) - # The following is equivalent to 'return vs[index]'. - index = tf.reduce_sum(tf.cast(tf.greater_equal(x_in, bs), tf.int32)) - one_hot_vec = tf.one_hot( - tf.expand_dims(index, 0), depth=len(values), dtype=vdtype) - return Matmul(tf.reshape(vs, (1, -1)), tf.transpose(one_hot_vec))[0][0] - - -def PadBatchDimension(x, batch_size, pad_val): - rank = tf.rank(x) - with tf.control_dependencies([assert_greater_equal(rank, 1)]): - current_batch_size = tf.shape(x)[0] - with tf.control_dependencies([assert_less_equal(current_batch_size, batch_size)]): - fake_samples_in_batch = batch_size - current_batch_size - pad = tf.scatter_nd([[0, 1]], [fake_samples_in_batch], [rank, 2]) - x = tf.pad(x, pad, constant_values=pad_val) - return x - - -def PadSequenceDimension(x, length, pad_val, shape=None): - """Pads x to `length` using `pad_val` along the second dim. - - Assumes `x` is a tensor with rank >= 2, and it only pads `x` to `length` - along the second dim. Explicitly sets the returned tensor shape to `shape` if - given. Raises runtime errors if x.shape[1] > length or x.shape[i] != shape[i] - where i != 1. - - Args: - x: the tensor to be padded with shape [batch, seq_len, ...]. - length: an int to specify the length to pad x to. - pad_val: an int or float used to pad x. - shape: an int array specifying the shape of the padded tensor if specified. - - Returns: - The padded tensor with shape [batch, seq_len, ...], where - ret[:, :seq_len, ...] == x. - """ - if x.shape.ndims is not None: - rank = x.shape.ndims - assert rank >= 2 - slen = GetShape(x, rank)[1] - pad_len = length - slen - pad = [[0, 0] for _ in range(rank)] - pad[1][1] = pad_len - else: - rank = tf.rank(x) - with tf.control_dependencies([assert_greater_equal(rank, 2)]): - slen = tf.shape(x)[1] - pad_len = length - slen - pad = tf.scatter_nd([[1, 1]], [pad_len], [rank, 2]) - x = tf.pad(x, pad, constant_values=pad_val) - if x.shape.ndims is not None and isinstance(length, int): - static_shape = x.shape.as_list() - static_shape[1] = length - x.set_shape(static_shape) - - if shape: - if not isinstance(shape, (list, tuple)): - raise TypeError('Shape must be a list or tuple.') - x = HasRank(x, len(shape)) - x = tf.ensure_shape(x, shape) - return x - - -def PadSequenceTo(xs, padding, length, pad_val): - """Pads `xs` and `padding` to `length` using `pad_val` along the 2nd dim. - - Pads `xs` to `length` using `pad_val`, and `padding` using 1. - Raise error if `x.shape[:2]` and `padding.shape` are not the same. - - Args: - xs: A Tensor or a list of Tensors of shape [batch, seqlen] or [batch, - seqlen, ...]. - padding: A 0/1 Tensor of shape [batch, seqlen]. 1 is for padded locations. - length: A Python int, the length to pad to. - pad_val: A Python numeric, used for padding x. - - Returns: - A tuple of padded xs and padding. - """ - if not isinstance(xs, (list, tuple)): - new_xs = [xs] - else: - new_xs = xs - - res = [] - for x in new_xs: - batch, slen = GetShape(x, 2) - - padding = HasRank(padding, 2) - padding = HasShape(padding, [batch, slen]) - - new_x = PadSequenceDimension(x, length, pad_val) - res.append(new_x) - padding = PadSequenceDimension(padding, length, tf.cast(1, padding.dtype)) - - if not isinstance(xs, (list, tuple)): - assert len(res) == 1 - return res[0], padding - else: - return tuple(res), padding - - -def ApplyPadding(padding, x, padded=None, broadcast=True, use_select=True): - """Applies padding to a tensor. - - This is preferable to using arithmetic means for masking out padded values - such as:: - - # Equiv to ApplyPadding(padding, x)) - x *= 1.0 - padding - # Equiv to ApplyPadding(padding, new, old) - new = old * padding + new * (1 - padding) - - Aside from just being easier to read and reason about, using this function - is friendly to quantized representations because it does not mix arithmetic - on the padding values with the values in the tensor being padded (which can - have a very different range than the 0..1 padding tensor). - - In addition, this works around issues in quantized schemes where we are - guaranteed to have an exact 0 but not necessarily any other number (i.e. 1). - - Args: - padding: Tensor of padding values where 0 == keep and 1 == pad. - x: Tensor to apply padding to. - padded: Optional. Values to include for padded elements. Defaults to zeros. - Must be the same shape as 'x' if specified. - broadcast: Whether to broadcast the padding shape to the shape of 'x'. You - almost certainly want this to be true as it matches how padding would be - expanded if applied arithmetically. - use_select: Controls whether padding is applied with a select-mask - (True/default) or arithmetically (False). Some platforms have a - sensitivity to one or the other and this is used to work around such - issues. - - Returns: - A tensor with the same shape as x with padded values masked. - """ - # Interesting dependencies usage - padding = with_dependencies([ - Assert( - tf.reduce_all( - tf.math.logical_or( - tf.equal(padding, 0.0), tf.equal(padding, 1.0))), [padding]) - ], padding) - if use_select: - if padded is None: - padded = tf.zeros_like(x) - if broadcast: - # Broadcast padding to the full shape. - padding = tf.cast(padding, x.dtype) * tf.ones_like(x) - return tf.where(padding > tf.zeros_like(padding), padded, x) - else: - result = x * tf.cast(1.0 - padding, x.dtype) - if padded is not None: - result += padded * tf.cast(padding, padded.dtype) - return result - - -def LengthsFromPaddings(paddings): - """Computes lengths of each sequence in a batch, ignoring trailing padding. - - Args: - paddings: a tensor with shape [batch, length]. - - Returns: - lengths tensor shaped [batch] containing the unpadded length of each - sequence in the batch. - """ - paddings = HasRank(paddings, 2) - paddings = tf.cast(paddings, tf.int32) - # Find the last unpadded value. - # Cannot just use tf.reduce_sum because there might be leading paddings. - # Everything after the last unpadded value has 1.0 - paddings == 0.0, so in - # the cumsum below they will have the same value. - cumsum = tf.cumsum(1 - paddings, axis=1) - same_as_last_element = tf.equal(cumsum, cumsum[:, -1:]) - # Counting the number of elements with the same value gives us num_padded + 1 - # and so counting the number that differs gives us num_padded - 1. - length = tf.reduce_sum( - 1 - tf.cast(same_as_last_element, tf.int32), axis=1) + 1 - # Special case for all 0 paddings. - all_zero_paddings = tf.equal(tf.reduce_sum(1 - paddings, axis=1), 0) - return tf.where(all_zero_paddings, tf.zeros_like(length), length) - - -def TrimTrailingPaddings(inputs, paddings): - """Trims trailing paddings from inputs. - - Since the number of dimensions is not fixed, this will not work on TPU. - - Args: - inputs: a tensor with shape [batch, length, ...]. - paddings: a tensor with shape [batch, length]. - - Returns: - Trimmed inputs and paddings. For compatibility reasons, the trimmed tensors - will always have length at least 1. - """ - paddings = HasRank(paddings, 2) - max_length = tf.maximum(tf.reduce_max(LengthsFromPaddings(paddings)), 1) - output_shape = tf.shape(inputs) - output_shape = tf.concat([[output_shape[0], max_length], output_shape[2:]], - axis=0) - outputs = tf.slice(inputs, tf.zeros_like(output_shape), output_shape) - out_paddings = tf.slice(paddings, [0, 0], - tf.stack([output_shape[0], max_length])) - return outputs, out_paddings - - -def ReversePaddedSequence(inputs, paddings): - """Reverse inputs based on paddings. - - Only reverse the unpadded portion of `inputs`. It assumes inputs are only - padded in the end. - - Args: - inputs: a tensor of [seq_length, batch_size, num_input_nodes]. - paddings: a tensor of float32/float64 zero or one of shape [seq_length, - batch_size, 1]. - - Returns: - A reversed tensor of the same shape as `inputs`. - """ - inversed_paddings = 1.0 - tf.squeeze(paddings, 2) - inputs_length = tf.cast( - tf.math.rint(tf.reduce_sum(inversed_paddings, axis=0)), tf.int32) - return tf.reverse_sequence(inputs, inputs_length, seq_axis=0, batch_axis=1) - - -def ConcatenatePaddedSequences(input0, input1, padding0, padding1, seq_dim=1): - """Concatenates input sequences with varying lenghts as defined by paddings. - - This is a helper function for concatenating 2 batches of input sequences, - where each example in the batch can have different lengths, as defined by - the corresponding paddings. To concatenate correctly, it makes use of - tf.reverse_sequence to partially reverse the sequences before - concatenating them together. - - NOTE: We assume that the tensors have no leading paddings. - - Args: - input0: A tensor of size [batch, max_length, ...] or [max_length, batch, - ...] depending on the value set for axis. - input1: A tensor of size [batch, max_length, ...] or [max_length, batch, - ...] depending on the value set for axis. - padding0: A Tensor of size [batch, max_length] or [max_length, batch] - corresponding to the padding for input0. - padding1: A Tensor of size [batch, max_length] or [max_length, batch] - corresponding to the padding for input1. - seq_dim: int, the time axis along which the tensors will be concatenated. - Should be 0 or 1. Assumes that batch_dim is 1 - seq_dim. - - Returns: - The concatenation of input0 and input1, and the corresponding padding. - - Raises: - tf.errors.InvalidArgumentError when seq_dim is not 0 or 1. - """ - if seq_dim != 0 and seq_dim != 1: - raise tf.errors.InvalidArgumentError(None, None, 'seq_dim must be 0 or 1.') - batch_dim = 1 - seq_dim - # inpu0 and input1 should have the same batch size and same rank. - input0 = with_dependencies([ - assert_equal(GetShape(input0)[batch_dim], - GetShape(input1)[batch_dim]), - assert_equal(GetRank(input0), GetRank(input1)) - ], input0) - - batch_size = GetShape(padding0)[batch_dim] - # batch dimension of inputs and paddings should match. - input0 = with_dependencies([ - assert_equal(GetShape(input0)[batch_dim], batch_size), - assert_equal(GetShape(padding1)[batch_dim], batch_size) - ], input0) - input0_seq_dim = tf.cast( - tf.tile([tf.shape(padding0)[seq_dim]], [batch_size]), dtype=tf.int32) - input1_seq_dim = tf.cast( - tf.tile([tf.shape(padding1)[seq_dim]], [batch_size]), dtype=tf.int32) - # LengthsFromPaddings assumes that paddings is of size [batch, max_length]. - if seq_dim == 1: - seq_length0 = LengthsFromPaddings(padding0) - seq_length1 = LengthsFromPaddings(padding1) - else: - seq_length0 = LengthsFromPaddings(tf.transpose(padding0)) - seq_length1 = LengthsFromPaddings(tf.transpose(padding1)) - # We assume that the tensors have no leading paddings. - # TODO(arunnt): Concatenate tensors with leading paddings correctly. - seq_length0 = with_dependencies([ - assert_equal( - seq_length0, - tf.cast(tf.reduce_sum(1.0 - padding0, seq_dim), dtype=tf.int32)) - ], seq_length0) - seq_length1 = with_dependencies([ - assert_equal( - seq_length1, - tf.cast(tf.reduce_sum(1.0 - padding1, seq_dim), dtype=tf.int32)) - ], seq_length1) - # Concatenate input sequences. - reversed_input0 = tf.reverse_sequence( - input0, seq_length0, seq_axis=seq_dim, batch_axis=batch_dim) - reversed_input1 = tf.reverse_sequence( - input1, input1_seq_dim, seq_axis=seq_dim, batch_axis=batch_dim) - reversed_concat = tf.concat([reversed_input1, reversed_input0], axis=seq_dim) - concat_inputs = tf.reverse_sequence( - reversed_concat, - seq_length0 + input1_seq_dim, - seq_axis=seq_dim, - batch_axis=batch_dim) - # Concatenate paddings. Note that paddings are always a Tensor of 0s and 1s, - # so, unlike the inputs, we don't have to reverse padding1, we can simply - # concatenate reversed padding0 and padding1. - reversed_padding0 = tf.reverse_sequence( - padding0, input0_seq_dim, seq_axis=seq_dim, batch_axis=batch_dim) - reversed_concat_padding = tf.concat([reversed_padding0, padding1], - axis=seq_dim) - concat_paddings = tf.reverse_sequence( - reversed_concat_padding, - input0_seq_dim + seq_length1, - seq_axis=seq_dim, - batch_axis=batch_dim) - return concat_inputs, concat_paddings - - -def ShiftLeft(tensor, shift_size, pad_val=0): - """Shifts the values in a tensor to the left along the time dimension (dim 1). - - The first shift_size values are dropped, and the tensor is padded on the - right with pad_val. - - Args: - tensor: the input tensor to modify shaped [batch, time, ...]. - shift_size: the number of frames >= 0 to shift. - pad_val: the value to pad on the right of the tensor. - - Returns: - A left shifted tensor. - """ - with tf.control_dependencies([ - assert_greater_equal(tensor.shape.rank, 2), - assert_greater_equal(shift_size, 0) - ]): - time = GetShape(tensor)[1] - return PadSequenceDimension(tensor[:, shift_size:], time, pad_val) - - -def Retry(*args, **kwargs): - return retry.Retry(*args, **kwargs) - - -# FailedPreconditionError: variables are not initialized. -# AbortedError: processes restarts. -# UnavailableError: Bad hardware status: 0x1 -transient_tf_errors = (tf.errors.FailedPreconditionError, - tf.errors.AbortedError, tf.errors.UnavailableError) - - -def RetryOnTransientTfError(*args, **kwargs): - return Retry(transient_tf_errors, *args, **kwargs) - - -def PadOrTrimTo(x, shape, pad_val=0, pad_after_contents=True): - """Pad and slice x to the given shape. - - Args: - x: A tensor. - shape: The shape of the returned tensor. - pad_val: An int or float used to pad x. - pad_after_contents: Whether to pad and trim after the original contents - of each dimension. - - Returns: - 'x' is padded with pad_val and sliced so that the result has the given - shape. - - Raises: - ValueError: if shape is a tf.TensorShape and not fully defined. - """ - if isinstance(shape, (list, tuple)): - expected_rank = len(shape) - elif isinstance(shape, tf.TensorShape): - if not shape.is_fully_defined(): - raise ValueError('shape %s padding %s must be fully defined.' % - (shape, x)) - expected_rank = shape.rank - else: - shape = HasRank(shape, 1) - expected_rank = tf.size(shape) - x = HasRank(x, expected_rank) - - pad = shape - tf.minimum(tf.shape(x), shape) - zeros = tf.zeros_like(pad) - if pad_after_contents: - # If dim_i is less than shape[i], pads after contents. - paddings = tf.stack([zeros, pad], axis=1) - # If dim_i is larger than shape[i], we slice [0:shape[i]] for dim_i. - slice_begin = zeros - else: - # If dim_i is less than shape[i], pads before contents. - paddings = tf.stack([pad, zeros], axis=1) - # If dim-i is larger than shape[i], we slice [dim_i - shape[i]:dim_i] - # for dim_i. - slice_begin = tf.shape(x) + pad - shape - - x = tf.pad(x, paddings, constant_values=pad_val) - x = tf.slice(x, slice_begin, shape) - - return tf.reshape(x, shape) - - -def RepeatDim(tensor, multiple, axis): - """Copies elements in tensor's axis "multiple" times, like np.repeat.""" - # x = [[1, 2, 3], [4, 5, 6]] - # RepeatDim(x, multiple=2, axis=1) gives: - # [[1, 1, 2, 2, 3, 3]. [4, 4, 5, 5, 6, 6]] - # As a comparison tf.tile(x, multiples=[1, 2]) gives:\ - # [[1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6]] - - if multiple == 1: - return tensor - t_shape = tf.shape(tensor) - tensor_dims = tf.concat( - [t_shape[:axis], [t_shape[axis] * multiple], t_shape[axis + 1:]], 0) - multiple_dims = tf.concat([ - tf.fill([axis + 1], 1), [multiple], - tf.fill([tf.rank(tensor) - axis - 1], 1) - ], 0) - return tf.reshape( - tf.tile(tf.expand_dims(tensor, axis + 1), multiple_dims), tensor_dims) - - -def StackTensorsRecursively(values): - """Recursively stacks Tensors in a list of `.NestedMap`. - - Args: - values: a list of `.NestedMap` or Tensors to stacks. - - Returns: - A `.NestedMap` with stacked values or a stacked Tensor. - """ - flatten = [w.Flatten() for w in values] - stacked = [] - for i in range(len(flatten[0])): - stacked += [tf.stack([flatten[j][i] for j in range(len(flatten))])] - ret = values[0].Pack(stacked) - return ret - - -def MixByWeight(inputs, weights, seed=None): - """Returns a weighted random choice and bprop type from the give inputs. - - Args: - inputs: a list of callables, where each callable returns a tf.Tensor or a - nested structure containing tf.Tensor. Function return types must be - consistent across elements. The tf.Operation to compute the result tensor - will only be invoked for one input at a time. For example, if each fn - represents an input record stream, a record will be drawn only from a - selected stream while the other streams will remain unchanged. - weights: a 1D tensor of float > 0 of the same length as inputs. - seed: random seed. - - Returns: - A probablistic sample from the inputs proportional to the weights. The - return type will be the same as return type of individual 'fn' from the - inputs. - A one-hot vector of the source selected. - """ - weights = tf.convert_to_tensor(weights, dtype=tf.float32) - weights = with_dependencies([ - assert_equal(tf.shape(weights), [len(inputs)]), - assert_greater_equal(tf.reduce_min(weights), 0.0) - ], weights) - - lower = tf.cumsum(weights, exclusive=True) - upper = tf.cumsum(weights, exclusive=False) - r = tf.random.uniform(shape=[], maxval=upper[-1], seed=seed) - return_input = tf.case( - [(tf.math.logical_and(lower[i] <= r, r < upper[i]), inputs[i]) - for i in range(len(inputs))], - exclusive=True) - selected_index = tf.case( - [(tf.math.logical_and(lower[i] <= r, r < upper[i]), lambda i=i: i) - for i in range(len(inputs))], - exclusive=True) - bprop_index = tf.one_hot(selected_index, len(inputs), dtype=tf.float32) - return return_input, bprop_index - - -def CheckShapes(shapes): - """Asserts that shapes is a tuple of NestedMap or tshape.Shape.""" - assert isinstance(shapes, tuple), str(shapes) - for s in shapes: - if isinstance(s, NestedMap): - assert all([isinstance(t, tshape.Shape) for t in Flatten(s) - ]), '{} contains non-tensor value.'.format(s) - else: - assert isinstance(s, tshape.Shape), '{}: {}'.format(type(s), s) - - -def FPropDtype(params): - return params.fprop_dtype if params.fprop_dtype is not None else params.dtype - - -def UpdateFpropDtype(params, fprop_dtype): - """Recursively update the fprop_dtype of the Params.""" - # Handle the case when the input "params" is not an instance of hyperparams - # For example, when UpdateDtype is called recursively for all the items in - # the "sub" list of SequentialLayer (see 1st elif below) - if not isinstance(params, hyperparams.Params): - return - - for key, val in params.IterParams(): - if isinstance(val, hyperparams.Params): - UpdateFpropDtype(val, fprop_dtype) - elif isinstance(val, (list, tuple)): - for item in val: - UpdateFpropDtype(item, fprop_dtype) - elif key == 'fprop_dtype': - params.fprop_dtype = fprop_dtype - - -def UpdateDtype(params, dtype): - """Recursively update the dtype of the Params.""" - # Handle the case when the input "params" is not an instance of hyperparams - # For example, when UpdateDtype is called recursively for all the items in - # the "sub" list of SequentialLayer (see 1st elif below) - if not isinstance(params, hyperparams.Params): - return - - for key, val in params.IterParams(): - if isinstance(val, hyperparams.Params): - UpdateDtype(val, dtype) - elif isinstance(val, (list, tuple)): - for item in val: - UpdateDtype(item, dtype) - elif key == 'dtype': - params.dtype = dtype - - -def NameScopeDecorator(name_scope): - """Decorates a python function to introduce a tf.name_scope. - - Example:: - - @py_utils.NameScopeDecorator('foobar') - def MyFoobarMethod(self): - # ... Do TF things - - Args: - name_scope: The name scope to introduce. - - Returns: - A function decorator. - """ - - def Decorator(f): - - def Wrapped(*args, **kwargs): - with tf.name_scope(name_scope): - return f(*args, **kwargs) - - return Wrapped - - return Decorator - - -def SequencesToDebugStrings(ids, lens, summarize=5): - """Returns debug strings for the given sequences. - - Args: - ids: int32 of [batch, len]. - lens: int32 of [batch]. - summarize: number of ids to summarize per sequence. - - Returns: - A string tensor of [batch]. - """ - num_seqs = tf.shape(lens)[0] - - def _Body(i, result): - line = tf.strings.format('{}', ids[i, :lens[i]], summarize=summarize) - return i + 1, tf.concat([result, tf.reshape(line, [1])], axis=0) - - i0 = tf.zeros(shape=[], dtype=tf.int32) - result0 = tf.constant('', shape=[0], dtype=tf.string) - _, strs = tf.while_loop( - lambda i, result: i < num_seqs, - _Body, (i0, result0), - shape_invariants=(i0.shape, tf.TensorShape([None]))) - return strs - - -def RematerializeFn(fn, *xs): - """Calls fn and rematerializes fn in the backward pass. - - `fn(*xs) -> ys`, where xs and ys can be a single tensor or a tuple of tensors. - - Args: - fn: A python function to be rematerialized in the backprop pass. - *xs: A single tensor or a list/tuple of tensors. `xs` are input args to the - fn function. - - Returns: - `fn(*xs)` - """ - initial_step_seed = GetStepSeed() - final_step_seed = GenerateSeedFromName(tf.no_op(name='new_step_seed').name) - - def Backward(fwd_xs, fwd_ys, d_fwd_ys): - """The backward function that rematerializes forward outputs.""" - del fwd_ys - always_true = tf.random.uniform([]) < 2.0 - # Alternatively, can do this: - # tf.where(tf.math.is_nan(x), - # tf.constant(float('nan'), dtype=x.dtype) * tf.ones_like(x), - # x) - bak_xs = [tf.where(always_true, x, tf.zeros_like(x)) for x in fwd_xs.xs] - for dst, src in zip(bak_xs, xs): - dst.set_shape(src.shape) - ResetStepSeed(initial_step_seed) - ys = fn(*bak_xs) - ResetStepSeed(final_step_seed) - dxs = tf.gradients(ys, bak_xs, grad_ys=d_fwd_ys) - dxs_final = [] - for dx, x in zip(dxs, bak_xs): - if dx is None: - dxs_final.append(tf.zeros_like(x)) - else: - dxs_final.append(dx) - assert len(dxs_final) == len(bak_xs) - return NestedMap( - initial_step_seed=tf.zeros_like(initial_step_seed), xs=dxs_final) - - ys_shapes = [] - - # TODO(huangyp, yonghui): Check Forward doesn't use any stateful random ops. - def Forward(fwd_xs): - """Forward function plus sanity checks.""" - for dst, src in zip(fwd_xs.xs, xs): - dst.set_shape(src.shape) - ResetStepSeed(fwd_xs.initial_step_seed) - ys = fn(*fwd_xs.xs) - # Some sanity check. - assert not GetExtraInputs() - assert not GetExtraArgs() - assert not GetExtraVars() - if isinstance(ys, tuple): - for y in ys: - assert isinstance(y, tf.Tensor) - ys_shapes.append(y.shape) - else: - assert isinstance(ys, tf.Tensor) - ys_shapes.append(ys.shape) - return ys - - ys = CallDefun( - Forward, - NestedMap(initial_step_seed=initial_step_seed, xs=xs), - bak=Backward) - if isinstance(ys, tuple): - for y, s in zip(ys, ys_shapes): - y.set_shape(s) - else: - ys.set_shape(ys_shapes[0]) - # TODO(b/129159299): The ResetStepSeed below is needed to work around this - # bug, which is a problem with global tensors being shared by different - # inference graphs. It should be replaced with the new step seed value - # returned from the Forward function when the bug is fixed. - ResetStepSeed(final_step_seed) - return ys - - -# A set of names of stateful random number generator ops. -# See tensorflow/core/ops/random_ops.cc -_STATEFUL_RANDOM_OPS = frozenset({ - # pyformat: disable - 'RandomUniform', - 'RandomUniformInt', - 'RandomStandardNormal', - 'ParameterizedTruncatedNormal', - 'TruncatedNormal', - 'RandomShuffle', - 'Multinomial', - 'RandomGamma', - 'RandomPoisson', - 'RandomPoissonV2', - # pyformat: enable -}) - - -def StatefulRandomOpsInDefun(func, graph=None): - """Checks whether the Defun depends on stateful random number ops. - - Stateful random number generator ops should be avoid in Recurrent() call. - Otherwise, these ops produce inconsistent values between FProp and BProp. - - Args: - func: a _DefinedFunction or ConcreteFunction to check. - graph: a Graph. Set None to use the default graph. - - Returns: - A list of names of the stateful random ops. - - Raises: - InvalidArgumentError: if the input func/graph is invalid. - """ - if graph is None: - graph = tf.get_default_graph() - func.add_to_graph(graph) - graph_def = graph.as_graph_def() - - # A dict from function name to FunctionDef. - func_defs = {x.signature.name: x for x in graph_def.library.function} - - if isinstance(func, function._DefinedFunction): # pylint: disable=protected-access - if func.definition.signature.name not in func_defs: - raise tf.errors.InvalidArgumentError( - None, None, 'Defun {} is not in the graph .'.format( - func.definition.signature.name)) - nodes = py_collections.deque(func.definition.node_def) - else: - nodes = py_collections.deque(func.function_def.node_def) - - stateful_ops = [] - - # Recursively search for stateful random op. - while nodes: - node = nodes.pop() - assert isinstance(node, node_def_pb2.NodeDef), node - - if node.op in _STATEFUL_RANDOM_OPS: - stateful_ops.append(node.op) - continue - - def _AddDefunNodes(func_name): - """If the given func_name is a Defun, add its sub-nodes into nodes.""" - if func_name in func_defs: - nodes.extend(func_defs[func_name].node_def) - - # For functional.{While|For|If} ops, add their Defun attr into search. - if node.op == 'While': - _AddDefunNodes(node.attr['body'].func.name) - _AddDefunNodes(node.attr['cond'].func.name) - elif node.op == 'For': - _AddDefunNodes(node.attr['body'].func.name) - elif node.op == 'If': - _AddDefunNodes(node.attr['then_branch'].func.name) - _AddDefunNodes(node.attr['else_branch'].func.name) - elif node.op == 'StatefulPartitionedCall': - _AddDefunNodes(node.attr['f'].func.name) - elif node.op != 'PartitionedCall': - # For other op, check whether itself is a Defun op. - _AddDefunNodes(node.op) - - return stateful_ops - - -def ToPlaceholders(nmap, dtype=None): - """Converts every Tensor in nmap to a placeholder.""" - - def _ToPlacerholder(x): - shape = [None for _ in x.shape[:-1]] + [x.shape[-1]] - return tf.placeholder(dtype=dtype or x.dtype, shape=shape) - - return nmap.Transform(_ToPlacerholder) - - -def SoftmaxCrossEntropyFocalLoss(logits, - label_ids=None, - label_probs=None, - alpha=None, - gamma=None): - u"""Focal loss for multinomial (softmax) logistic loss. - - [1] Focal loss https://arxiv.org/abs/1708.02002 - - Args: - logits: [..., C]. Logits for the multinomial logistic regression. C is the - number of classes. - label_ids: [...]. Each entry in labels must be an index in [0, C). - label_probs: [..., C]. Each vector along last dimension must be a valid - probability distribution. - alpha: [C]. The weighting factor alpha. Eq (3) in [1]. - gamma: []. Tunable focusing parameter. Eq (4) in [1]. - - Returns: - loss[i..., j] = FL(pₜ) = - αₜ(1-pₜ)ˠlog(pₜ) Eq (5) in [1]. - """ - if label_probs is not None: - log_probs = tf.nn.log_softmax(logits) - loss = -(label_probs * log_probs) - if gamma is not None and gamma != 0: - probs = tf.exp(log_probs) - loss *= tf.pow(1.0 - probs, gamma) - if alpha is not None: - loss *= tf.reshape( - alpha, tf.concat([tf.ones(tf.rank(loss) - 1, tf.int32), [-1]], - axis=0)) - loss = tf.reduce_sum(loss, axis=-1) - else: - loss = tf.nn.sparse_softmax_cross_entropy_with_logits( - labels=label_ids, logits=logits) - if gamma is not None and gamma != 0: - probs = tf.exp(-loss) - loss *= tf.pow(1.0 - probs, gamma) - if alpha is not None: - loss *= tf.gather(alpha, label_ids) - return loss - - -def SigmoidCrossEntropyFocalLoss(logits, labels, alpha=None, gamma=None): - u"""Focal loss for binary (sigmoid) logistic loss. - - [1] Focal loss https://arxiv.org/abs/1708.02002 - - Args: - logits: [..., C]. Logits for the sigmoid logistic regression. - labels: [..., C]. 0/1 labels. - alpha: The weighting factor alpha. Eq (3) in [1]. - gamma: Tunable focusing parameter. Eq (4) in [1]. - - Returns: - loss[i..., j] = FL(pₜ) = - αₜ(1-pₜ)ˠlog(pₜ) Eq (5) in [1]. - """ - - # [1] Eq (4). - # - # The numerically-stable way to compute - # log(p) for positives; - # log(1 - p) for negatives. - loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=labels, logits=logits) - - if gamma is not None and gamma != 0: - # The modulating factor. Note that - # (1 - p)ˠ = [1 - σ(x)]ˠ = [σ(-x)]ˠ, for positives. - # pˠ = [σ(x)]ˠ, for negatives. - loss *= tf.pow(tf.sigmoid(logits * (1 - labels * 2)), gamma) - - if alpha is not None: - # [1] Eq (3) - loss *= (alpha * labels + (1 - alpha) * (1 - labels)) - - return loss - - -_RECORD_FORMAT_RE = re.compile('(^[A-Za-z]+):(.*)') - - -def RecordFormatFromFilePattern(file_pattern): - """Return the record format string for a Lingvo file pattern. - - Lingvo file patterns take the form of: - tfrecord:/path/to/bar -> tfrecord is the record_format. - - This function takes a file pattern and returns a string indicating - which format the filepattern implies. - - Args: - file_pattern: String file pattern. - - Returns: - Tuple (string, string): - - - record_format: String record format, e.g., "tfrecord", etc. - - file_pattern: The file pattern without any prefixes. - """ - result = re.match(_RECORD_FORMAT_RE, file_pattern) - - if result is None: - # TODO(vrv): Fix all callers so that file_pattern must contain - # the record format prefix. - return 'sstable', file_pattern - - # regexp ensures that a match implies there are two groups: - # the record format and then the file pattern. - return result.groups() - - -def ReadFileLines(file_path): - """Read a text file and return the lines. - - If the file cannot be found at the given path, attempt to load it from the - Lingvo package (useful for data dependencies in par files). - - Args: - file_path: path to file, either absolute or relative to the bazel workspace. - - Returns: - A list of lines from the file. - """ - if not tf.io.gfile.exists(file_path): - try: - lines = pkgutil.get_data( - 'lingvo', file_path.replace('lingvo/', '', - 1)).splitlines(True) - except IOError: - # If pkgutil can't find the file, continue and let GFile raise the error. - lines = None - else: - lines = None - - if not lines: - with tf.io.gfile.GFile(file_path, 'r') as f: - lines = f.readlines() - - return lines - - -# Partially borrowed from -# https://github.com/tensorflow/tensor2tensor/blob/32929305e1a4ec926eff24123758b794df35492b/tensor2tensor/layers/common_layers.py#L349 -def CumSum(x, axis=0, exclusive=False): - """A TPU efficient implementation of tf.cumsum(). - - This is equivalent to tf.cumsum and is faster on TPU as of 08/2019 unless - the axis dimension is very large. The current Tensorflow implementation is - based on scanning and reducing which is not efficient on TPU. - - Args: - x: An input Tensor. - axis: An int for the axis. - exclusive: A bool for performing exclusive cumsum. - - Returns: - A Tensor of the same shape as x. - - Raises: - ValueError: if the input axis is invalid. - """ - if x.dtype not in (tf.float32, tf.bfloat16) or not use_tpu(): - # Fallback to tf.cumsum when inputs are not floats or not running on TPU. - return tf.cumsum(x, axis=axis, exclusive=exclusive) - - rank = GetRank(x) - # Needs to know the rank for the final transpose if axis is not the last - # dimension. Otherwise, falls back to tf.cumsum. - if not isinstance(rank, int) and axis != -1: - return tf.cumsum(x, axis=axis, exclusive=exclusive) - - if axis < -1: - if axis + rank < 0: - raise ValueError('Unexpected axis: %d (rank = %d)' % (axis, rank)) - axis += rank - - length = GetShape(x)[axis] - my_range = tf.range(length) - comparator = tf.less if exclusive else tf.less_equal - mask = tf.cast( - comparator(tf.expand_dims(my_range, 1), tf.expand_dims(my_range, 0)), - x.dtype) - result = tf.tensordot(x, mask, axes=[[axis], [0]]) - if axis != -1 and axis != rank - 1: - result = tf.transpose( - result, - list(range(axis)) + [rank - 1] + list(range(axis, rank - 1))) - return result - - -def ProjectLastDim(inputs, weight, input_dim, output_dim): - """Linear projection on the last dim of the input tensor. - - This is a TPU efficient implementation to avoid reshaping inputs to Rank-2 - tensor by using Einsum for the compute. - - Args: - inputs: An input Tensor, the last dimension of which is input_dim. - weight: A weight matrix with shape [input_dim, output_dim]. - input_dim: An integer or a symbolic dim, the last dimension of the inputs. - output_dim: An integer or a symbolic dim, the last dimension of the outputs. - - Returns: - An output Tensor of the same rank as inputs, the last dimension is - output_dim. - """ - input_dim = int( - symbolic.ToStatic(input_dim) if symbolic.IsExpr(input_dim) else input_dim) - output_dim = int( - symbolic.ToStatic(output_dim) if symbolic.IsExpr(output_dim - ) else output_dim) - - # Assert input_dim and output_dim - inputs = with_dependencies([assert_equal(GetShape(inputs)[-1], input_dim)], - inputs) - weight = with_dependencies([ - assert_equal(GetShape(weight)[0], input_dim), - assert_equal(GetShape(weight)[-1], output_dim) - ], weight) - - if (use_tpu() and inputs.shape is not None and - inputs.shape.rank is not None and inputs.shape.rank < 26): - # Avoids reshape if feasible and uses Einsum. - if inputs.shape.rank == 2: - outputs = tf.matmul(inputs, weight) - else: - s = ''.join([chr(x) for x in range(97, 123)]) # abc...xyz - r = inputs.shape.rank - outputs = tf.einsum('{0}y,yz->{0}z'.format(s[:r - 1]), inputs, weight) - else: - outputs = Matmul(tf.reshape(inputs, ToStaticShape([-1, input_dim])), weight) - outputs = tf.reshape( - outputs, - tf.concat([ - tf.cast(GetShape(inputs)[:-1], tf.int32), - ToStaticShape([output_dim]) - ], - axis=0)) - - return outputs - - -@contextlib.contextmanager -def RemoveAssertContext(remove=True): - """Hacks to replace certain unwanted tensorflow ops.""" - # TODO(zhifengc/huangyp): Consider implementing assert_equal - # op replacement for lingvo. As assert_equal doesn't support String on GPUs. - # Hack to replace tf.assert_equal - # TODO(b/136040013): Remove this after migration to tf.function. - if remove: - saved_assert_equal = tf.check_ops.assert_equal - - def NoOP(*args, **kwargs): # pylint: disable=unused-argument - return tf.no_op() - - tf.check_ops.assert_equal = NoOP # Make assert_equal a no op. - try: - yield - finally: - tf.check_ops.assert_equal = saved_assert_equal - else: - yield - - -def _AssertInputsMatch(op, args, implicit_captures): - """Assert that op's inputs match with args and implicit_captures. - - Args: - op: The operation to check. - args: A nested structure representing the explicit arguments of 'op'. - implicit_captures: A nested structure representing the implicitly captured - inputs of 'op'. - - Raises: - ValueError: if the number of inputs mismatch. - """ - expected_inputs = Flatten([args, implicit_captures]) - expected_num_inputs = len(expected_inputs) - if len(op.inputs) > expected_num_inputs: - raise ValueError(('Too many inputs. The most likely cause is that fwd ' - 'captures additional tensors: extra inputs %r vs %r ' - 'captures=%r') % (list(op.inputs), list(expected_inputs), - list(Flatten(implicit_captures)))) - if len(op.inputs) < expected_num_inputs: - raise ValueError(('Mismatched inputs to fwd: Found %d vs expected %d: %r' - '. Implicit captures(%d) = %r') % - (len(op.inputs), expected_num_inputs, list(op.inputs), - len(Flatten(implicit_captures)), implicit_captures)) - - -def _TensorSpecs(nmap): - """Transforms tensors in the input nested structure to TensorSpecs.""" - return Transform(lambda t: tf.TensorSpec(t.shape, t.dtype), nmap) - - -def _DefineDefun(fwd, - fwd_sig, - bak=None, - bak_as_function=False, - device=None): - """Wraps fwd in a defun with custom gradient bak. - - Args: - fwd: A callable xs: Nested Structure -> ys: Nested Structure. - fwd_sig: A Nested Structure of tf.Tensor representing the input signature of - fwd. - bak: A callable xs, ys, dys: Nested Structure -> dxs[, dcapture]: Nested - Structure. The custom backprop function for fwd. bak needs to return - dcapture if fwd uses any implicitly captured tensors, whose gradients are - dcapture. - bak_as_function: Whether to create a TF graph function for bak. - device: the device on which to run fwd and bak. - - Returns: - A function that wraps fwd. - """ - assert fwd is not None - - # fwd signature (tf.Tensor dtypes). - get_dtype = lambda x: x.dtype - arg_dtypes = Flatten(Transform(get_dtype, fwd_sig)) - - get_shape = lambda x: x.shape - arg_shapes = Flatten(Transform(get_shape, fwd_sig)) - - compiled = use_xla() - noinline = not compiled - - # Used to hold: - # - the output dtypes/shapes of fwd, which will be set in Forward. - # - the real Backward function used by Grad, which will be defined later. - sigs = NestedMap() - - python_grad_func = None - implicit_captures = NestedMap() # Will be set after Forward is defined. - if bak: - - def Grad(op, *args): - """Gradient function for the forward function. - - Args: - op: The forward operation. - *args: Gradients wrt op.outputs. - - Returns: - Tuple of derivatives. - """ - _AssertInputsMatch(op, fwd_sig, implicit_captures) - # Ensure dys contains no None. - args = ConvertNoneGradientToZeros(list(op.outputs), list(args)) - xs = op.inputs[:len(arg_dtypes)] # The rest are captures. - return sigs.backward(*Flatten([xs, op.outputs, args])) - - python_grad_func = Grad - - def _SetShape(dst_list, shape_list): - for dst, shape in zip(dst_list, shape_list): - if isinstance(dst, tf.Tensor): - dst.set_shape(shape) - - @tf.Defun(*arg_dtypes, python_grad_func=python_grad_func, noinline=noinline) - def Forward(*args): - """The forward function.""" - _SetShape(args, arg_shapes) - with RemoveAssertContext(remove=noinline), tf.device(device): - rets = fwd(Pack(fwd_sig, args)) - sigs.ret_dtypes = Transform(get_dtype, rets) - sigs.ret_shapes = Transform(get_shape, rets) - return Flatten(rets) - - # Invokes fwd() to get sigs.ret_dtypes and sigs.ret_shapes. - Forward.add_to_graph(tf.get_default_graph()) - implicit_captures.captured = Forward.captured_inputs - - if bak: - - def Backward(*args): - """The backward function.""" - xs_len = len(arg_dtypes) - ys_len = len(Flatten(sigs.ret_dtypes)) - xs = args[:xs_len] - ys = args[xs_len:(xs_len + ys_len)] - dys = args[xs_len + ys_len:] - assert len(dys) == ys_len - - _SetShape(xs, arg_shapes) - _SetShape(ys, Flatten(sigs.ret_shapes)) - _SetShape(dys, Flatten(sigs.ret_shapes)) - - xs = Pack(fwd_sig, xs) - ys = Pack(sigs.ret_dtypes, ys) - dys = Pack(sigs.ret_dtypes, dys) - with RemoveAssertContext(remove=noinline), tf.device(device): - dxs = bak(xs, ys, dys) - return Flatten(dxs) - - if bak_as_function: - sigs.backward = tf.Defun( - *Flatten([arg_dtypes, sigs.ret_dtypes, sigs.ret_dtypes]), - noinline=noinline)( - Backward) - - sigs.backward.add_to_graph(tf.get_default_graph()) - else: - sigs.backward = Backward - - def Call(args): - """Wrapper of fwd.""" - flat_rets = Forward(*Flatten(args)) - if not isinstance(flat_rets, (tuple, list)): - flat_rets = [flat_rets] - _SetShape(flat_rets, Flatten(sigs.ret_shapes)) - return Pack(sigs.ret_dtypes, flat_rets) - - return Call - - -# Global variable to control rendezvous sharing in tf.function. -# If False (default) rendezvous sharing is disabled in tf.function, that is, the -# function body use a separate rendezvous and can't communicate with parent -# graph via send/recv. -# With _GetSharedRendezvous() == True, the function body share the same -# rendezvous with the parent graph and can talk to it using send/recv. This is -# useful for layers like StackedRecurrent. -_SHARED_RENDEZVOUS = ThreadLocalStack() - - -@contextlib.contextmanager -def _SharedRendezvousScope(shared_rendezvous=True): - _SHARED_RENDEZVOUS.stack.append(shared_rendezvous) - try: - yield - finally: - _SHARED_RENDEZVOUS.stack.pop() - - -def _GetSharedRendezvous(): - """Get the current rendezvous sharing setting.""" - return _SHARED_RENDEZVOUS.stack[-1] if _SHARED_RENDEZVOUS.stack else False - - -def _ApplySharedRendezvous(func): - """Apply the rendezvous sharing setting on the given tf.function func.""" - # pylint: disable=protected-access - func._shared_rendezvous = _GetSharedRendezvous() - # pylint: enable=protected-access - - -def _WrapFunction(func=None, input_signature=None): - """Wraps func as a tf.function to be used in If/WhileLoop/CallDefun.""" - if input_signature is None: - input_signature = [] - - def Decorated(fn): - - @tf.function(input_signature=input_signature, autograph=False) - def Fn(*args): - # TODO(b/163904067): mimic Defun' behavior and reset the step seed to - # avoid it being used as an implicit capture. This is not a desired - # behavir, it should take the step seed from parent graph instead. - ResetStepSeed() - return fn(*args) - - _ApplySharedRendezvous(Fn) - - # Add the function to the graph so it'll be traced under the current - # context. This is necessary if the function body captures any non-tensor - # values from the environment, like symbolic maps. - cf = Fn.get_concrete_function() - cf.add_to_graph() - return cf - - # For the `foo = _WrapFunction(foo, ...)` use case. - if func is not None: - return Decorated(func) - - # For the `@_WrapFunction(...)` use case. - return Decorated - - -def _DefineFunction(fwd, - fwd_sig, - bak=None, - bak_as_function=False, - device=None): - """Wraps fwd in a defun with custom gradient bak. - - Args: - fwd: A callable xs: Nested Structure -> ys: Nested Structure. - fwd_sig: A Nested Structure of tf.Tensor representing the input signature of - fwd. - bak: A callable xs, ys, dys: Nested Structure -> dxs[, dcapture]: Nested - Structure. The custom backprop function for fwd. bak needs to return - dcapture if fwd uses any implicitly captured tensors, whose gradients are - dcapture. - bak_as_function: Whether to create a TF graph function for bak. - device: the device on which to run fwd and bak. - - Returns: - A function that wraps fwd. - """ - assert fwd is not None - noinline = not use_xla() - fwd_sig = _TensorSpecs(fwd_sig) - - # Only used to hold the output signature of fwd in sigs.rets, which will be - # set in Forward. - sigs = NestedMap() - - @_WrapFunction(input_signature=Flatten(fwd_sig)) - def Forward(*args): - """The forward function.""" - with RemoveAssertContext(remove=noinline), tf.device(device): - xs = Pack(fwd_sig, args) - rets = fwd(xs) - sigs.rets = _TensorSpecs(rets) - return Flatten(rets) - - shared_rendezvous = _GetSharedRendezvous() - implicit_captures = Forward.captured_inputs - - if not bak: - forward = Forward - else: - - def Backward(*args): - xs_len = len(Flatten(fwd_sig)) - ys_len = len(Flatten(sigs.rets)) - xs = Pack(fwd_sig, args[:xs_len]) - ys = Pack(sigs.rets, args[xs_len:(xs_len + ys_len)]) - dys = Pack(sigs.rets, args[xs_len + ys_len:]) - with RemoveAssertContext(remove=noinline), tf.device(device): - dxs = bak(xs, ys, dys) - return Flatten(dxs) - - if bak_as_function: - backward_cf = _WrapFunction( - Backward, input_signature=Flatten([fwd_sig, sigs.rets, sigs.rets])) - else: - - def BackwardWithSharedRendezvous(*args): - with _SharedRendezvousScope(shared_rendezvous): - return Backward(*args) - - backward_cf = BackwardWithSharedRendezvous - - @tf.custom_gradient - def ForwardWithGrad(*args): - """Forward function and its custom gradient.""" - # Note that `args` includes implicit_captures. This is required by - # tf.custom_gradient so that when the Grad() outputs include gradients to - # implicit captures, they match the inputs to ForwardWithGrad(). - # - # However, Forward doesn't take implicit_captures as input, so we exclude - # them here. - fwd_args = args[:(len(args) - len(Flatten(implicit_captures)))] - op = NestedMap(inputs=args, outputs=Forward(*fwd_args)) - - def Grad(*args, **kwargs): - """Gradient function for the forward function. - - Args: - *args: Gradients wrt op.outputs. - **kwargs: Additional arguments from tf.custom_gradient. - - Returns: - Tuple of derivatives. - """ - if kwargs: - tf.logging.warning( - 'Ignoring additional arguments used by tf.custom_gradient: %s', - str(kwargs)) - - _AssertInputsMatch(op, fwd_sig, implicit_captures) - - # Ensure dys contains no None. - args = ConvertNoneGradientToZeros(list(op.outputs), list(args)) - - xs, _ = Pack([fwd_sig, implicit_captures], op.inputs) - return backward_cf(*Flatten([xs, op.outputs, args])) - - return op.outputs, Grad - - forward = lambda *args: ForwardWithGrad(*Flatten([args, implicit_captures])) - - def Call(args): - """Wrapper of fwd.""" - flat_rets = forward(*Flatten(args)) - if not isinstance(flat_rets, (tuple, list)): - flat_rets = [flat_rets] - return Pack(sigs.rets, flat_rets) - - return Call - - -def CallDefun(fwd, - args, - bak=None, - bak_as_function=False, - device=None): - """Wraps fwd in a defun with custom gradient bak and calls it with args. - - Args: - fwd: A callable xs: Nested Structure -> ys: Nested Structure. - args: A Nested Structure of tf.Tensor. - bak: A callable xs, ys, dys: Nested Structure -> dxs[, dcapture]: Nested - Structure. The custom backprop function for fwd. bak needs to return - dcapture if fwd uses any implicitly captured tensors, whose gradients are - dcapture. - bak_as_function: Whether to create a TF graph function for bak. - device: the device on which to run fwd and bak. - - Returns: - A Nested Structure equivalent to what fwd(args) computes. - """ - if _FromGlobal('call_defun_use_tf_function'): - fn = _DefineFunction - else: - fn = _DefineDefun - call = fn(fwd, args, bak, bak_as_function, device) - return call(args) - - -def If(cond, inputs, then_branch, else_branch): - """Helper to construct an if/else statement. - - Args: - cond: A scalar `Tensor` that can be converted to boolean. - inputs: A flattenable representing the input tensors of the if/else - statement. - then_branch: A callable 'inputs' -> flattenable. The returned value - should be compatible with what 'else_branch' returns. - else_branch: A callable 'inputs' -> flattenable. The returned value - should be compatible with what 'then_branch' returns. - - Returns: - Output returned by the call to either 'then_branch' or 'else_branch'. - """ - ret_dtypes = NestedMap() - get_dtype = lambda x: x.dtype - - if _FromGlobal('if_use_tf_function'): - - @_WrapFunction(input_signature=Flatten(_TensorSpecs(inputs))) - def ThenBranch(*args): - inp = Pack(inputs, args) - out = then_branch(inp) - ret_dtypes.then_out = Transform(get_dtype, out) - return Flatten(out) - - @_WrapFunction(input_signature=Flatten(_TensorSpecs(inputs))) - def ElseBranch(*args): - inp = Pack(inputs, args) - out = else_branch(inp) - ret_dtypes.else_out = Transform(get_dtype, out) - return Flatten(out) - - ret = tf.If( - cond=cond, - inputs=Flatten(inputs), - then_branch=ThenBranch, - else_branch=ElseBranch) - else: - dtypes = Flatten(Transform(lambda x: x.dtype, inputs)) - - @tf.Defun(*dtypes) - def ThenBranch(*args): - for dst, src in zip(args, Flatten(inputs)): - dst.set_shape(src.shape) - inp = Pack(inputs, args) - out = then_branch(inp) - ret_dtypes.then_out = Transform(get_dtype, out) - return Flatten(out) - - @tf.Defun(*dtypes) - def ElseBranch(*args): - for dst, src in zip(args, Flatten(inputs)): - dst.set_shape(src.shape) - inp = Pack(inputs, args) - out = else_branch(inp) - ret_dtypes.else_out = Transform(get_dtype, out) - return Flatten(out) - - ret = tf.If( - cond=cond, - inputs=Flatten(inputs), - then_branch=ThenBranch, - else_branch=ElseBranch) - - assert IsCompatible(ret_dtypes.then_out, ret_dtypes.else_out), ( - 'Outputs of then_branch and else_branch are not compatible: {} vs {}' - .format(ret_dtypes.then_out, ret_dtypes.else_out)) - return Pack(ret_dtypes.then_out, ret) - - -def _Itype(): - """Loop iterator data type.""" - return tf.int32 if use_xla() else tf.int64 - - -def WhileLoop(cond, body, loop_state): - """Helper to construct a while loop. - - Args: - cond: A callable NestedMap -> tf.bool. - body: A callable NestedMap -> NestedMap. - loop_state: A flattenable (NestedMap, list, tuple, etc.) representing the - loop state. - - Returns: - The final loop state in the same structure as loop_state. - """ - state = NestedMap(loop_state=loop_state) - - if _FromGlobal('while_loop_use_tf_function'): - - @_WrapFunction(input_signature=Flatten(_TensorSpecs(state))) - def LoopCond(*args): - s = state.Pack(args) - return cond(s.loop_state) - - @_WrapFunction(input_signature=Flatten(_TensorSpecs(state))) - def LoopBody(*args): - s = state.Pack(args) - s.loop_state = body(s.loop_state) - return s.Flatten() - - new_state = tf.While(input_=state.Flatten(), cond=LoopCond, body=LoopBody) - else: - dtypes = state.Transform(lambda x: x.dtype).Flatten() - - @tf.Defun(*dtypes) - def LoopCond(*args): - s = state.Pack(args) - return cond(s.loop_state) - - @tf.Defun(*dtypes) - def LoopBody(*args): - s = state.Pack(args) - s.loop_state = body(s.loop_state) - return s.Flatten() - - new_state = tf.While(input_=state.Flatten(), cond=LoopCond, body=LoopBody) - return state.Pack(new_state).loop_state - - -def ForLoop(body, start, limit, delta, loop_state): - """Helper to construct a for loop. - - Args: - body: A callable (tf.int, NestedMap) -> NestedMap. - start: Loop variable's initial value. - limit: Loop variable's limit value. - delta: Loop variable's change per iteration. - loop_state: A flattenable (NestedMap, list, tuple, etc.) representing the - loop state. - - Returns: - The final loop state in the same structure as loop_state. - """ - state = NestedMap( - iter=tf.cast(start, _Itype()), - limit=tf.cast(limit, _Itype()), - delta=tf.cast(delta, _Itype()), - loop_state=loop_state) - - def LoopCond(state): - return tf.less(state.iter, state.limit) - - def LoopBody(state): - state.loop_state = body(state.iter, state.loop_state) - state.iter = tf.add(state.iter, state.delta) - return state - - return WhileLoop(LoopCond, LoopBody, state).loop_state - - -def TopK(x_in, k): - """Equivalent to tf.math.top_k(x_in, k) but more efficient on tpu.""" - assert k <= 2, 'This implementation is only efficient for small k.' - # TODO(yonghui): Try out an alternative idea where we first reshape x_in as a - # 2d tensor, then call tf.math.top_k, and then reshape back. - x_in_shape = x_in.shape - x_rank = x_in_shape.rank - assert x_rank and x_in_shape.as_list()[x_rank - 1] > 0 - last_dim_size = x_in_shape.as_list()[x_rank - 1] - min_value = tf.math.reduce_min(x_in) - 1.0 - - out_indices = [] - out_values = [] - - for unused_i in range(k): - index_i = tf.math.argmax(x_in, axis=-1, output_type=tf.int32) - mask_i = tf.one_hot(index_i, last_dim_size) - # TODO(yonghui): Would tf.gather be more efficient and numerically stable - # here? - value_i = tf.reduce_sum(mask_i * x_in, -1, keepdims=True) - x_in = (1.0 - mask_i) * x_in + mask_i * min_value - out_indices.append(tf.expand_dims(index_i, -1)) - out_values.append(value_i) - - if k == 1: - return out_values[0], out_indices[0] - else: - return tf.concat(out_values, x_rank - 1), tf.concat(out_indices, x_rank - 1) - - -def ReadVariable(var_op): - """Returns the value of the given variable operation. - - Args: - var_op: The variable's TF `Operation`. It could be one of VarHandleOp, - Variable and VariableV2. - - Returns: - A `Tensor` containing the value of the variable. - """ - if var_op.type == 'VarHandleOp': - # Filter out the ReadVariableOps that have control dependencies to avoid - # side-effects when the user runs it. - filter_fn = lambda op: op.type == 'ReadVariableOp' and not op.control_inputs - var_readers = list(filter(filter_fn, var_op.outputs[0].consumers())) - assert var_readers - return var_readers[0].outputs[0] - - assert var_op.type in ['Variable', 'VariableV2'] - return var_op.outputs[0] - - -_TPU_SUMMARY_TENSORS_KEY = ('__lingvo_tpu_summary_tensors') - -_get_tpu_summary_tensors = _CollectionGetter(_TPU_SUMMARY_TENSORS_KEY, - lambda: []) - - -def AddTpuSummaryTensor(name, value, weight=1.0): - """Adds tensor to global collection of summaries. - - This needs to be used in situations where tf.summary() could be used but - currently tf.summary is not supported. Use py_utils.AddTpuSummaryTensor() in - low level code to add summary tensors to global collection of summaries. - Then recover all summary tensors from global collection by calling - py_utils.GetTpuSummaryTensors() from top level code (for example from - ComputeLoss method of BaseTask). - - In addition to 'name' argument, current tensorflow name scope is also - captured and added to the metric name. This way for example summaries from - a repeated layer will appear as separate graphs in the tensorboard. - - Weight argument is optional and defaults to 1.0. See BaseTask.ComputeLoss for - the exact definition of weight for eval metrics. - - Args: - name: metric name - value: metric value tensor - weight: weight tensor for weighted metrics - """ - tpu_summary_tensors = _get_tpu_summary_tensors() - x = NestedMap() - x.name = name - x.value = value, tf.convert_to_tensor(weight) - x.name_scope = tf.get_default_graph().get_name_scope() - tpu_summary_tensors.append(x) - - -def GetTpuSummaryTensors(): - """Returns summary tensors from global collection. - - Returns: - A dict containing str keys and (metric, weight) pairs as values - """ - tpu_summary_tensors = _get_tpu_summary_tensors() - return { - '%s/%s' % (x.name, SanitizeScopeKey(x.name_scope)): x.value - for x in tpu_summary_tensors - } - - -def ClearTpuSummaryTensors(): - tpu_summary_tensors = _get_tpu_summary_tensors() - del tpu_summary_tensors[:] - - -def ComputationShape(split_size, topology=None): - """Decides the computation shape based on the split_size.""" - if topology: - topology_info = tf_topology.Topology(serialized=topology) - computation_shape = None - if split_size == 1: - # GALV: Lingvo assumes you're running on a group of TPUs that is a - # power of four. We are running on 8 TPUv3's, so we reduce the - # shape to length 3. - computation_shape = [1, 1, 1] - elif split_size == 2: - computation_shape = [1, 1, 1, 2] - elif split_size == 4: - computation_shape = [1, 2, 1, 2] - elif split_size == 8: - computation_shape = [2, 2, 1, 2] - elif split_size == 16: - computation_shape = [4, 2, 1, 2] - elif split_size == 32: - computation_shape = [4, 4, 1, 2] - elif split_size == 64: - if topology and topology_info.mesh_shape[1] == 32: - # Fwd within-replica all-reduces is performed along column; - # Bwd gradient cross-replica all-reduces is performed along row. - # This currently has better performance than the strided patten. - computation_shape = [1, 32, 1, 2] - else: - computation_shape = [4, 8, 1, 2] - elif split_size == 128: - computation_shape = [8, 8, 1, 2] - elif split_size == 256: - computation_shape = [8, 16, 1, 2] - elif split_size == 512: - computation_shape = [16, 16, 1, 2] - elif split_size == 1024: - computation_shape = [16, 32, 1, 2] - elif split_size == 2048: - computation_shape = [32, 32, 1, 2] - elif split_size == 4096: - computation_shape = [64, 32, 1, 2] - elif split_size == 8192: - computation_shape = [128, 32, 1, 2] - else: - assert False, ('Model parallelism with %d devices is currently not' - ' supported.' % split_size) - assert computation_shape is not None - return computation_shape - - -def GetExtraVars(): - """Returns the captured variables by the function.""" - g = tf.get_default_graph() - if isinstance(g, func_graph.FuncGraph): - return g.variable_captures - return function.get_extra_vars() - - -def GetExtraInputs(): - """Returns the captured input tensors by the function.""" - g = tf.get_default_graph() - if isinstance(g, func_graph.FuncGraph): - return g.external_captures - return function.get_extra_inputs() - - -def GetExtraArgs(): - """Returns the corresponding function arguments for the captured inputs.""" - g = tf.get_default_graph() - if isinstance(g, func_graph.FuncGraph): - return g.internal_captures - return function.get_extra_args() - -def LengthsFromBitMask(padding_bitmask, time_axis: int): - # T, B - assert padding_bitmask.dtype == tf.float32, \ - "This assert is not necessary, but I would like to know when the condition isn't true." - lengths = tf.cast(tf.reduce_sum(1.0 - padding_bitmask, axis=time_axis), tf.int32) - return lengths - -def BitMaskFromLengths(padding_lengths): - max_length = tf.shape(padding_lengths)[0] - padding = 1.0 - tf.sequence_mask(padding_lengths, max_length, tf.float32) - return padding - -def ShardedFilePatternToGlob(file_pattern): - """Converts a file pattern path@shards to path-?????-of-shards.""" - if ',' in file_pattern: - raise ValueError( - 'ShardedFilePatternToGlob does not support multiple file patterns.') - if '@' not in file_pattern: - return file_pattern - path, shards = file_pattern.split('@') - if shards == '*': - return f'{path}-?????-of-*' - return f'{path}-?????-of-{int(shards):05}' diff --git a/lingvo/core/py_utils_test.py b/lingvo/core/py_utils_test.py deleted file mode 100644 index cffd5c3c..00000000 --- a/lingvo/core/py_utils_test.py +++ /dev/null @@ -1,3340 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for py_utils.""" - -import collections -import copy -import itertools -import os -import sys - -from absl.testing import flagsaver -from absl.testing import parameterized -from lingvo import model_registry -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import builder_layers -from lingvo.core import cluster_factory -from lingvo.core import hyperparams -from lingvo.core import py_utils -from lingvo.core import recurrent -from lingvo.core import symbolic -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tasks.image.params import mnist # pylint: disable=unused-import - -import mock -import numpy as np - -from tensorflow.python.ops import functional_ops # pylint:disable=g-direct-tensorflow-import - -FLAGS = tf.flags.FLAGS - - -class PyUtilsTest(test_utils.TestCase, parameterized.TestCase): - - def testIsDefaultParamInit(self): - p = py_utils.DefaultParamInit() - self.assertTrue(py_utils.IsDefaultParamInit(p)) - p = hyperparams.Params.FromProto(p.ToProto()) - self.assertTrue(py_utils.IsDefaultParamInit(p)) - p = py_utils.WeightInit.Xavier(scale=1.) - self.assertFalse(py_utils.IsDefaultParamInit(p)) - - def testNestedMapFromNestedDict(self): - a = {'a1': 1, 'a2': 2} - b = {'b1': 1, 'b2': 2} - c = {'a': a, 'b': b, 'ab': [a, b]} - d = py_utils.NestedMap(c) - e = py_utils.NestedMap.FromNestedDict(c) - self.assertIsInstance(d, py_utils.NestedMap) - self.assertNotIsInstance(d.a, py_utils.NestedMap) - self.assertIsInstance(e.a, py_utils.NestedMap) - self.assertIsInstance(e.ab[0], py_utils.NestedMap) - self.assertEqual(e.ab[0], e.a) - self.assertEqual(e.ab[1], e.b) - - def testNestedMapGetItem(self): - nested_map = py_utils.NestedMap() - nested_map['a'] = py_utils.NestedMap({'x': 3}) - nested_map['b'] = py_utils.NestedMap({'y_0': 4, 'y': [3]}) - self.assertEqual(nested_map.GetItem('a.x'), nested_map.a.x) - self.assertEqual(nested_map.GetItem('b.y_0'), 4) - with self.assertRaises(KeyError): - nested_map.GetItem('o') - - def testNestedMapGet(self): - nested_map = py_utils.NestedMap({'a': {'b': 0}}) - self.assertEqual(nested_map.Get('a.b'), 0) - self.assertIsNone(nested_map.Get('a.b.c')) - self.assertIsNone(nested_map.Get('x')) - self.assertEqual(nested_map.Get('x', 0), 0) - - def testNestedMapDir(self): - nested_map = py_utils.NestedMap({'a': {'b': 0}}) - nested_map.c = '1' - nested_map_dir = dir(nested_map) - # Flattened keys are in the dir - self.assertIn('a.b', nested_map_dir) - self.assertIn('c', nested_map_dir) - # So are method APIs. - self.assertIn('Get', nested_map_dir) - - def testNestedMapSet(self): - nested_map = py_utils.NestedMap.FromNestedDict({'a': {'b': 0}}) - self.assertEqual(nested_map.a.b, 0) - # Test if overriding an existing value works. - nested_map.Set('a.b', 1) - self.assertEqual(nested_map.a.b, 1) - # Test if ValueError is raised if an existing intermediate value is not a - # NestedMap. - with self.assertRaises(ValueError): - nested_map.Set('a.b.c', 2) - nested_map.Set('a.b', py_utils.NestedMap()) - # Verify that non-existing intermediate keys are set to NestedMap. - nested_map.Set('a.b.x.y', 2) - self.assertIsInstance(nested_map.a.b.x, py_utils.NestedMap) - self.assertEqual(nested_map.a.b.x.y, 2) - - @parameterized.named_parameters( - ('_stateful', False, [tf.float32, tf.float64, tf.complex64]), - ('_stateless', True, [tf.float32, tf.float64])) - def testCreateVariableBasics(self, stateless_vars_init, dtypes): - with flagsaver.flagsaver(stateless_vars_init=stateless_vars_init): - with self.session(use_gpu=False, graph=tf.Graph()): - methods = [ - py_utils.WeightInit.Gaussian, - py_utils.WeightInit.Uniform, - py_utils.WeightInit.Constant, - py_utils.WeightInit.TruncatedGaussian, - py_utils.WeightInit.GaussianSqrtDim, - py_utils.WeightInit.UniformSqrtDim, - py_utils.WeightInit.UniformUnitScaling, - py_utils.WeightInit.UniformUnitScalingFanAvg, - py_utils.WeightInit.TruncatedGaussianSqrtDim, - py_utils.WeightInit.TruncatedGaussianSqrtFanIn, - py_utils.WeightInit.TruncatedGaussianSqrtFanOut, - py_utils.WeightInit.XavierGaussian, - ] - shapes = [[], [3], [2, 4], [3, 3, 2, 4]] - col = ['col1', 'col2'] - - all_vars = [] - for i, (m, dt, - sp) in enumerate(itertools.product(methods, dtypes, shapes)): - pc = py_utils.WeightParams(sp, m(), dt, col) - all_vars.append(py_utils.CreateVariable('var_%d' % i, pc)) - - # To reuse existing variables - tf.get_variable_scope().reuse_variables() - - self.assertEqual(len(tf.trainable_variables()), len(all_vars)) - - all_vars_copy = [] - for i, (m, dt, - sp) in enumerate(itertools.product(methods, dtypes, shapes)): - pc = py_utils.WeightParams(sp, m(), dt, col) - all_vars_copy.append(py_utils.CreateVariable('var_%d' % i, pc)) - - self.evaluate(tf.global_variables_initializer()) - for v1, v2 in zip(all_vars, all_vars_copy): - v1_v = v1.eval() - v2_v = v2.eval() - self.assertAllEqual(v1_v, v2_v) - - def testCreateVariableWithSymbols(self): - with self.session(use_gpu=False, graph=tf.Graph()): - dim_symbol = symbolic.Symbol('dim') - shape = [2, 3, dim_symbol * 2] - - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, {dim_symbol: 2}): - pc = py_utils.WeightParams(shape, py_utils.WeightInit.Gaussian(), - tf.float32, ['col1', 'col2']) - var = py_utils.CreateVariable('var', pc) - - # To reuse existing variables - tf.get_variable_scope().reuse_variables() - - new_dim_symbol = symbolic.Symbol('new_dim') - # Same shape as above but from different symbol. - shape = [2, 3, new_dim_symbol * 2] - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, - {new_dim_symbol: 2}): - pc = py_utils.WeightParams(shape, py_utils.WeightInit.Gaussian(), - tf.float32, ['col1', 'col2']) - var_copy = py_utils.CreateVariable('var', pc) - - self.evaluate(tf.global_variables_initializer()) - self.assertAllEqual(var.eval(), var_copy.eval()) - - def testCreateVariableUniform(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(12345678) - methods = [ - py_utils.WeightInit.Uniform, - py_utils.WeightInit.UniformSqrtDim, - py_utils.WeightInit.UniformUnitScaling, - ] - dtypes = [tf.float32, tf.complex64] - shapes = [[2, 3]] - all_vars = [] - for i, (dt, m, - sp) in enumerate(itertools.product(dtypes, methods, shapes)): - pc = py_utils.WeightParams(sp, m(0.1), dt) - all_vars.append(py_utils.CreateVariable('var_%d' % i, pc)) - - v1_v_expted = [[0.069674, -0.072278, -0.021777], - [-0.052155, -0.050274, 0.086218]] - v2_v_expted = [[0.005361, 0.036109, -0.036575], - [0.058314, 0.031438, 0.049196]] - v4_v_expted = [ - [0.015448 + 0.068295j, -0.098710 - 0.054435j, 0.037030 - 0.048017j], - [-0.047435 + 0.035301j, 0.041994 + 0.000279j, -0.029097 + 0.084902j], - ] - - self.evaluate(tf.global_variables_initializer()) - v1_v = all_vars[0].eval() - v2_v = all_vars[1].eval() - v4_v = all_vars[3].eval() - self.assertAllClose(v1_v_expted, v1_v.tolist()) - self.assertAllClose(v2_v_expted, v2_v.tolist()) - self.assertAllClose(v4_v_expted, v4_v.tolist()) - - def testCreateVariableNormal(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(832124) - methods = [ - py_utils.WeightInit.Gaussian, - py_utils.WeightInit.GaussianSqrtDim, - ] - dtypes = [tf.float32, tf.complex64] - shapes = [[2, 3]] - all_vars = [] - for i, (dt, m, - sp) in enumerate(itertools.product(dtypes, methods, shapes)): - pc = py_utils.WeightParams(sp, m(), dt) - all_vars.append(py_utils.CreateVariable('var_%d' % i, pc)) - - v1_v_expted = [[-1.472208, 0.960204, -0.192588], - [-0.461884, 1.018134, 0.063719]] - v2_v_expted = [[-0.862255, -0.688153, 0.82515], - [-0.07671, 0.613031, -0.020327]] - v3_v_expted = [ - [1.005469 + 0.827639j, 1.249896 + 0.802671j, -0.026286 - 0.813836j], - [0.865386 + 0.301172j, 0.876698 - 0.907293j, 1.996337 + 1.840192j], - ] - - self.evaluate(tf.global_variables_initializer()) - v1_v = all_vars[0].eval() - v2_v = all_vars[1].eval() - v3_v = all_vars[2].eval() - self.assertAllClose(v1_v_expted, v1_v.tolist()) - self.assertAllClose(v2_v_expted, v2_v.tolist()) - self.assertAllClose(v3_v_expted, v3_v.tolist()) - - def testCreateVariableSqrtFanInOut(self): - with self.session(): - tf.random.set_seed(832124) - methods = [ - py_utils.WeightInit.GaussianSqrtFanIn, - py_utils.WeightInit.TruncatedGaussianSqrtFanIn, - py_utils.WeightInit.GaussianSqrtFanOut, - py_utils.WeightInit.TruncatedGaussianSqrtFanOut, - py_utils.WeightInit.XavierGaussian, - ] - dtypes = [tf.float32] - shapes = [[1, 1, 2, 3]] - all_vars = [] - for i, (dt, m, - sp) in enumerate(itertools.product(dtypes, methods, shapes)): - pc = py_utils.WeightParams(sp, m(scale=2), dt) - all_vars.append(py_utils.CreateVariable('var_%d' % i, pc)) - - self.evaluate(tf.global_variables_initializer()) - var_values = self.evaluate(all_vars) - tf.logging.info('var_values=%s', var_values) - self.assertAllClose( - [ - # GaussianSqrtFanIn. - [[[[-2.08201575, 1.35793388, -0.27236053], - [-0.65320235, 1.43985856, 0.09011276]]]], - # TruncatedGaussianSqrtFanIn. - [[[[-1.72450912, -1.37630582, 1.65029943], - [-0.15342039, -0.7636584, -0.97026265]]]], - # GaussianSqrtFanOut. - [[[[1.16101539, 1.4432559, -0.03035267], - [0.9992612, 1.01232362, 2.30517101]]]], - # TruncatedGaussianSqrtFanOut. - [[[[-0.049076, -0.25183302, -1.79192507], - [0.93166995, -0.83121753, -1.40264213]]]], - # XavierGaussian. - [[[[-0.59545106, 1.232773, -0.2630677], - [0.11635535, 0.9317614, 0.16670291]]]], - ], - var_values) - - def testCreateVariableException(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(832124) - pc = py_utils.WeightParams([2, 3], py_utils.WeightInit.Gaussian()) - var1 = py_utils.CreateVariable('var1', pc) - - tf.get_variable_scope().reuse_variables() - # Reuses an existing variable. - var2 = py_utils.CreateVariable('var1', pc) - - # An exception should be thrown in this case. - pc = py_utils.WeightParams([2, 3], py_utils.WeightInit.Gaussian(2.0)) - with self.assertRaises(AssertionError): - py_utils.CreateVariable('var1', pc) - - self.evaluate(tf.global_variables_initializer()) - self.assertAllEqual(var1.eval(), var2.eval()) - - def testCreateVariableDifferentSeed(self): - with self.session(use_gpu=False): - tf.random.set_seed(3251343) - pc = py_utils.WeightParams([2, 3], py_utils.WeightInit.Gaussian()) - with tf.variable_scope('layer0'): - w0 = py_utils.CreateVariable('w', pc) - with tf.variable_scope('layer1'): - w1 = py_utils.CreateVariable('w', pc) - self.evaluate(tf.global_variables_initializer()) - - # w0_val, w1_val should be sufficient different. - w0_val, w1_val = self.evaluate([w0, w1]) - print(['diff = ', w0_val - w1_val]) - self.assertGreater(np.max(np.abs(w0_val - w1_val)), 0.1) - - def testXavier(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1618) - methods = [py_utils.WeightInit.Xavier] - dtypes = [tf.float32, tf.float16, tf.complex64] - shapes = [[2, 3]] - all_vars = [] - for i, (m, dt, - sp) in enumerate(itertools.product(methods, dtypes, shapes)): - pc = py_utils.WeightParams(sp, m(), dt) - all_vars.append(py_utils.CreateVariable('var_%d' % i, pc)) - - v1_v_expted = [[1.051236, -0.959198, 0.796091], - [-0.685691, 0.230933, -1.006293]] - v3_v_expted = [ - [0.149996 - 0.064369j, 0.689145 + 0.017257j, -0.502070 - 0.367683j], - [0.519782 + 0.470412j, 0.738902 - 0.054006j, 0.028603 + 0.471832j], - ] - - self.evaluate(tf.global_variables_initializer()) - v1_v = all_vars[0].eval() - v3_v = all_vars[2].eval() - self.assertAllClose(v1_v_expted, v1_v.tolist()) - self.assertAllClose(v3_v_expted, v3_v.tolist()) - - def testXavier1D(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1618) - methods = [py_utils.WeightInit.Xavier] - dtypes = [tf.float32, tf.float16, tf.complex64] - shapes = [[2]] - all_vars = [] - for i, (m, dt, - sp) in enumerate(itertools.product(methods, dtypes, shapes)): - pc = py_utils.WeightParams(sp, m(), dt) - all_vars.append(py_utils.CreateVariable('var_%d' % i, pc)) - - v1_v_expted = [1.175317, -1.072416] - - self.evaluate(tf.global_variables_initializer()) - v1_v = all_vars[0].eval() - self.assertAllClose(v1_v_expted, v1_v.tolist()) - - def testXavier3D(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1618) - methods = [py_utils.WeightInit.Xavier] - dtypes = [tf.float32, tf.float16, tf.complex64] - shapes = [[1, 1, 2]] - all_vars = [] - for i, (m, dt, - sp) in enumerate(itertools.product(methods, dtypes, shapes)): - pc = py_utils.WeightParams(sp, m(), dt) - all_vars.append(py_utils.CreateVariable('var_%d' % i, pc)) - - v1_v_expted = [[[1.357139, -1.23832]]] - - self.evaluate(tf.global_variables_initializer()) - v1_v = all_vars[0].eval() - self.assertAllClose(v1_v_expted, v1_v.tolist()) - - def testVariableShapePrefix(self): - with self.session(use_gpu=False, graph=tf.Graph()): - shape = [3, 2] - pc = py_utils.WeightParams( - shape=shape, init=py_utils.WeightInit.Constant(0.0), dtype=tf.float32) - with py_utils.VariableShapePrefixContext(5): - with py_utils.VariableShapePrefixContext(4): - var = py_utils.CreateVariable('var', pc) - self.assertEqual([5, 4, 3, 2], var.shape.as_list()) - - def testGeoMeanXavier(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1618) - methods = [py_utils.WeightInit.GeoMeanXavier] - dtypes = [tf.float32, tf.float16, tf.complex64] - shapes = [[2, 3]] - all_vars = [] - for i, (m, dt, - sp) in enumerate(itertools.product(methods, dtypes, shapes)): - pc = py_utils.WeightParams(sp, m(), dt) - all_vars.append(py_utils.CreateVariable('var_%d' % i, pc)) - - v1_v_expted = [[1.062019, -0.969037, 0.804257], - [-0.692724, 0.233301, -1.016615]] - v3_v_expted = [[ - 0.151534 - 0.065029j, 0.696214 + 0.017434j, -0.507220 - 0.371455j - ], [0.525114 + 0.475238j, 0.746481 - 0.05456j, 0.028896 + 0.476672j]] - - self.evaluate(tf.global_variables_initializer()) - v1_v = all_vars[0].eval() - v3_v = all_vars[2].eval() - self.assertAllClose(v1_v_expted, v1_v.tolist()) - self.assertAllClose(v3_v_expted, v3_v.tolist()) - - def testCheckNumerics(self): - xv = [[1, 2], [3, 4]] - yv = [10] * 4 - with self.session(): - x = tf.constant(xv, tf.float32) - y = tf.constant(yv) - z = tf.reduce_mean(tf.constant([], tf.float32)) - self.assertAllClose(xv, self.evaluate(py_utils.CheckNumerics(x))) - self.assertAllClose(yv, self.evaluate(py_utils.CheckNumerics(y))) - actual_xv, actual_yv = self.evaluate(py_utils.CheckNumerics([x, y])) - self.assertAllClose(xv, actual_xv) - self.assertAllClose(yv, actual_yv) - actual_xv, actual_yv = self.evaluate(py_utils.CheckNumerics((x, y))) - self.assertAllClose(xv, actual_xv) - self.assertAllClose(yv, actual_yv) - - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'NaN'): - self.evaluate(py_utils.CheckNumerics(z)) - - def testLog(self): - with self.session(): - x = tf.constant([[1, 2], [3, 4]]) - y = tf.constant([10] * 4) - x = py_utils.Log(x, 'testLog', x=x, y=y) - self.assertAllEqual(x.eval(), [[1, 2], [3, 4]]) - - def testDebug(self): - with self.session(): - x = tf.constant([[1, 2], [3, 4]]) - y = tf.constant([11] * 4) - z = tf.constant([22] * 4) - x = py_utils.Debug(x, 'msg') - self.assertAllEqual(x.eval(), [[1, 2], [3, 4]]) - - x = py_utils.Debug(x, 'msg', more=[y, z]) - self.assertAllEqual(x.eval(), [[1, 2], [3, 4]]) - - def testSave(self): - with self.session(): - x = tf.constant([[1, 2], [3, 4]]) - y = tf.constant([10] * 4) - x = py_utils.Save(x, '%s/test' % self.get_temp_dir(), x=x, y=y) - self.evaluate(tf.global_variables_initializer()) - self.assertAllEqual(self.evaluate(x), [[1, 2], [3, 4]]) - - # Reads npy files and check the values. - read_x = np.load('%s/test.%08d.x.npy' % (self.get_temp_dir(), 0)) - read_y = np.load('%s/test.%08d.y.npy' % (self.get_temp_dir(), 0)) - self.assertAllEqual(read_x, [[1, 2], [3, 4]]) - self.assertAllEqual(read_y, [10] * 4) - - def testTensorRank(self): - a = tf.constant([1]) - self.assertIsInstance(py_utils.HasRank(a, 1), tf.Tensor) - self.assertIsInstance(py_utils.HasAtLeastRank(a, 1), tf.Tensor) - - b = tf.constant([[1, 2]]) - self.assertIsInstance(py_utils.HasRank(b, 2), tf.Tensor) - self.assertIsInstance(py_utils.HasAtLeastRank(b, 1), tf.Tensor) - self.assertIsInstance(py_utils.HasAtLeastRank(b, 2), tf.Tensor) - with self.assertRaises(Exception): - py_utils.HasAtLeastRank(b, 3) - - c = tf.placeholder(tf.int32, shape=None) - d = py_utils.HasAtLeastRank(c, 3) - with self.session() as sess: - d_v = sess.run(d, feed_dict={c: np.array([[[1, 2]]])}) - self.assertAllEqual([[[1, 2]]], d_v) - with self.assertRaises(Exception): - sess.run(d, feed_dict={c: np.array([[1, 2]])}) - - def testTensorRankDisableAsserts(self): - with flagsaver.flagsaver(enable_asserts=False): - c = tf.placeholder(tf.int32, shape=None) - d = py_utils.HasAtLeastRank(c, 3) - with self.session() as sess: - d_v = sess.run(d, feed_dict={c: np.array([[1, 2]])}) - self.assertAllEqual([[1, 2]], d_v) - - def testGetShape(self): - a = tf.constant([1]) - self.assertEqual(py_utils.GetShape(a), [1]) - self.assertEqual(py_utils.GetShape(a, 1), [1]) - self.assertEqual(py_utils.GetShape(a, 3), [1]) - - b = tf.constant([[1, 2]]) - self.assertEqual(py_utils.GetShape(b), [1, 2]) - self.assertEqual(py_utils.GetShape(b, 1), [1]) - self.assertEqual(py_utils.GetShape(b, 2), [1, 2]) - self.assertEqual(py_utils.GetShape(b, 3), [1, 2]) - - c = tf.zeros([1, a[0], a.shape[0], tf.shape(a)[0]]) - self.assertEqual(py_utils.GetShape(c)[0], 1) - self.assertEqual(py_utils.GetShape(c)[1], 1) - self.assertEqual(py_utils.GetShape(c)[2], 1) - self.assertEqual(py_utils.GetShape(c)[3], 1) - - d = tf.placeholder(tf.float32, shape=(1, None)) - self.assertEqual(py_utils.GetShape(d)[0], 1) - self.assertIsInstance(py_utils.GetShape(d)[1], tf.Tensor) - - e = tf.zeros([d.shape[0], tf.shape(d)[0], tf.shape(d)[1]]) - self.assertEqual(py_utils.GetShape(e)[0], 1) - self.assertIsInstance(py_utils.GetShape(e)[1], tf.Tensor) - self.assertIsInstance(py_utils.GetShape(e)[2], tf.Tensor) - - f = tf.placeholder(tf.float32) - self.assertIsNone(f.shape.ndims) - # GetShape() will return a Tensor. - self.assertIsInstance(py_utils.GetShape(f), tf.Tensor) - - def testGetSize(self): - a = tf.constant([1]) - self.assertEqual(py_utils.GetSize(a), 1) - - b = tf.constant([[1, 2]]) - self.assertEqual(py_utils.GetSize(b), 2) - - d = tf.placeholder(tf.float32, shape=(1, None)) - self.assertIsInstance(py_utils.GetSize(d), tf.Tensor) - - shape = tf.placeholder(tf.int32) - f = py_utils.GetSize(tf.reshape(d, shape)) - self.assertIsInstance(f, tf.Tensor) - - with self.session() as sess: - f_v = sess.run(f, feed_dict={d: np.array([[1, 2]]), shape: [2]}) - self.assertEqual(2, f_v) - - def testUpdateFpropDtype(self): - network_p = builder_layers.SequentialLayer.Params() - linear_layer_p = builder_layers.LinearLayer.Params() - linear_layer_p.input_dims = 5 - linear_layer_p.output_dims = 6 - network_p.sub.append(linear_layer_p) - - py_utils.UpdateFpropDtype(network_p, tf.bfloat16) - self.assertEqual(network_p.sub[0].fprop_dtype, tf.bfloat16) - - def testUpdateDtype(self): - network_p = builder_layers.SequentialLayer.Params() - linear_layer_p = builder_layers.LinearLayer.Params() - linear_layer_p.input_dims = 5 - linear_layer_p.output_dims = 6 - network_p.sub.append(linear_layer_p) - - py_utils.UpdateDtype(network_p, tf.bfloat16) - self.assertEqual(network_p.sub[0].dtype, tf.bfloat16) - - def testGetRank(self): - a = tf.constant([1]) - self.assertEqual(py_utils.GetRank(a), 1) - - b = tf.constant([[1, 2]]) - self.assertEqual(py_utils.GetRank(b), 2) - - c = tf.zeros([1, a[0], a.shape[0], tf.shape(a)[0]]) - self.assertEqual(py_utils.GetRank(c), 4) - - d = tf.placeholder(tf.float32, shape=(1, None)) - self.assertEqual(py_utils.GetRank(d), 2) - - e = tf.zeros([d.shape[0], tf.shape(d)[0], tf.shape(d)[1]]) - self.assertEqual(py_utils.GetRank(e), 3) - - f = tf.placeholder(tf.float32) - self.assertIsNone(f.shape.ndims) - # GetRank() will return a Tensor. - self.assertIsInstance(py_utils.GetRank(f), tf.Tensor) - - def testRenamingRules(self): - pc = py_utils.WeightParams([3, 3]) - with tf.variable_scope('model'): - v1 = py_utils.CreateVariable('v1', pc) - with py_utils.VariableRenameScope([('model/(.*)', 'data/%s')]): - v2 = py_utils.CreateVariable('v2', pc) - v3 = py_utils.CreateVariable('v3', pc) - - self.assertEqual(v1.name, 'model/v1/var:0') - self.assertEqual(v2.name, 'data/v2/var:0') - self.assertEqual(v3.name, 'model/v3/var:0') - - def testOpportunisticReuse(self): - pc = py_utils.WeightParams([3, 3]) - v1 = py_utils.CreateVariable('v1', pc) - with self.assertRaises(Exception): - py_utils.CreateVariable('v1', pc) - with py_utils.OpportunisticVariableReuseScope(True): - v2 = py_utils.CreateVariable('v1', pc) - x1 = py_utils.CreateVariable('x1', pc) - with py_utils.OpportunisticVariableReuseScope(False): - with self.assertRaises(Exception): - py_utils.CreateVariable('v1', pc) - v3 = py_utils.CreateVariable('v1', pc) - with self.assertRaises(Exception): - py_utils.CreateVariable('v1', pc) - - for v in [v2, v3]: - self.assertIs(v1, v) - self.assertIsNot(v1, x1) - - def testGetOrCreateGlobalStepVar(self): - with tf.variable_scope('s1'): - with tf.name_scope('s2'): - gs1 = py_utils.GetOrCreateGlobalStepVar() - gs2 = tf.train.get_global_step() - gs3 = py_utils.GetOrCreateGlobalStepVar() - gs4 = tf.train.get_global_step() - gs5 = py_utils.GetOrCreateGlobalStepVar() - gs6 = tf.train.get_global_step() - for gs in [gs2, gs3, gs4, gs5, gs6]: - self.assertIs(gs1, gs) - self.assertEqual(gs1.name, 'global_step:0') - - def testCreateLocalTheta(self): - methods = [py_utils.WeightInit.Gaussian, py_utils.WeightInit.Uniform] - dtypes = [tf.float32, tf.complex64] - shapes = [[2, 4], [3]] - - test_vars = py_utils.NestedMap() - for i, (m, dt, sp) in enumerate(itertools.product(methods, dtypes, shapes)): - pc = py_utils.WeightParams(sp, m(), dt, 'col1') - var = py_utils.CreateVariable('var_%d' % i, pc) - with tf.device(var.device): - test_vars['var_%d' % i] = tf.identity(var) - - test_devices = [ - '/job:worker/replica:0/device:GPU:0', - '/job:worker/replica:0/device:GPU:1' - ] - - sharded_local_vars = py_utils.CreateLocalTheta(test_vars, test_devices) - sharded_local_vars_list = sharded_local_vars.Flatten() - - # assert proper device placement - for i, v in enumerate(sharded_local_vars_list): - expected_device = test_devices[i % len(test_devices)] - self.assertEqual(v.device, expected_device) - - def testComputeGradient(self): - with self.session(use_gpu=False): - a = tf.get_variable('a', []) - b = tf.get_variable('b', [], trainable=False) - c = tf.get_variable('c', []) - e = tf.get_variable('e', []) - l = a + b + tf.stop_gradient(c) - vmap = py_utils.NestedMap( - a=a, b=b, c=c, d=None, n=py_utils.NestedMap(aa=a, e=e)) - var_grads = py_utils.ComputeGradients(l, vmap) - print('var_grads = ', var_grads.DebugString()) - # Only 'a' matters. b is not trainable; c has stop_gradient; d - # is None; e is not computed by l and aa is a duplicated. - self.assertEqual([_[0] for _ in var_grads.FlattenItems()], ['a']) - self.assertEqual(var_grads.a.var.name, 'a:0') - - def testVarGradNestFlatten(self): - a = tf.get_variable('a', []) - b = tf.get_variable('b', []) - vs_gs = py_utils.NestedMap( - a=py_utils.VarGrad(a, - tf.ones_like(a) * 10.0), - b=py_utils.VarGrad(b, - tf.ones_like(b) * 0.5)) - flattened = tf.nest.flatten(vs_gs) - self.assertLen(flattened, 2) - for x in flattened: - self.assertIsInstance(x, py_utils.VarGrad) - - def testClipSingleTensorGradients(self): - a = tf.get_variable('a', []) - b = tf.get_variable('b', []) - vs_gs = py_utils.NestedMap( - a=py_utils.VarGrad(a, - tf.ones_like(a) * 10.0), - b=py_utils.VarGrad(b, - tf.ones_like(b) * 0.5)) - clipped = py_utils.ApplyGradNormClipping(vs_gs, norm=1.0) - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - clipped_np = self.evaluate(clipped.Transform(tuple)) - # Each variable is clipped indipendently to grad scale of 1. - self.assertAllClose(clipped_np.a[1], 1.0) - self.assertAllClose(clipped_np.b[1], 0.5) - - def testMaskGradient(self): - with self.session(use_gpu=False): - a = tf.get_variable('a', []) - b = tf.get_variable('b', []) - c = tf.get_variable('c', []) - d = tf.get_variable('d', []) - e = tf.get_variable('e', []) - l = a + b + c + d - zeros = tf.zeros(3, dtype=tf.float32) - select = tf.one_hot(1, 3, dtype=tf.float32) - vmap = py_utils.NestedMap( - a=a, b=b, c=c, d=d, n=py_utils.NestedMap(aa=a, e=e)) - grad_mask = py_utils.NestedMap() - grad_mask['a:0'] = zeros - grad_mask['b:0'] = zeros - grad_mask['c:0'] = select - grad_mask['d:0'] = select - grad_onehot = tf.one_hot(1, 3, dtype=tf.float32) - grad_mask = { - k: tf.tensordot(v, grad_onehot, 1) for k, v in grad_mask.items() - } - var_grads = py_utils.ComputeGradients(l, vmap) - var_grads_mask = py_utils.MaskGradients(var_grads, grad_mask) - self.evaluate(tf.global_variables_initializer()) - _, var_grads_mask_vals = self.evaluate( - [var_grads.Transform(tuple), - var_grads_mask.Transform(tuple)]) - # 'a' and 'b' are masked, while 'c' and 'd' are not. - self.assertEqual(var_grads_mask_vals['a'][1], 0) - self.assertEqual(var_grads_mask_vals['b'][1], 0) - self.assertEqual(var_grads_mask_vals['c'][1], 1) - self.assertEqual(var_grads_mask_vals['d'][1], 1) - - def testSkipL2Regularization(self): - with self.session(use_gpu=False): - beta = tf.get_variable( - 'beta', - initializer=tf.constant(np.arange(10).reshape([1, 10]), tf.float32)) - tf.add_to_collection(py_utils.SKIP_LP_REGULARIZATION, beta) - gamma = tf.get_variable( - 'gamma', - initializer=tf.constant(np.arange(10).reshape([1, 10]), tf.float32)) - act = tf.constant(np.arange(10).reshape([1, 10]), tf.float32) - pred = act * gamma + beta - loss = tf.reduce_sum(pred) - vmap = py_utils.NestedMap(beta=beta, gamma=gamma) - var_grads = py_utils.ComputeGradients(loss, vmap) - self.assertCountEqual(var_grads.keys(), ['beta', 'gamma']) - l2_loss, var_grads_with_l2 = py_utils.AdjustGradientsWithLpLoss( - var_grads, 0.1, p=2.0) - - self.evaluate(tf.global_variables_initializer()) - var_grads_vals, l2_loss_val, var_grads_with_l2_vals = self.evaluate([ - var_grads.Transform(tuple), l2_loss, - var_grads_with_l2.Transform(tuple) - ]) - print('var_grads_vals = ', var_grads_vals) - print('var_grads_with_l2_vals = ', var_grads_with_l2_vals) - self.assertAllEqual(var_grads_vals.beta[0], - var_grads_with_l2_vals.beta[0]) - self.assertAllEqual(var_grads_vals.gamma[0], - var_grads_with_l2_vals.gamma[0]) - self.assertAllEqual( - l2_loss_val, 0.5 * 0.1 * np.sum(np.square(var_grads_vals.gamma[0]))) - - # With l2, gradients of be gamma are adjusted. - self.assertAllClose( - var_grads_with_l2_vals.gamma[1], - var_grads_vals.gamma[1] + 0.1 * var_grads_vals.gamma[0]) - self.assertAllClose(var_grads_with_l2_vals.beta[1], - var_grads_vals.beta[1]) - - def testAdjustGradientsWithL2Loss(self): - with self.session(use_gpu=False): - emb = tf.get_variable( - 'emb', - initializer=tf.constant(np.arange(100).reshape([10, 10]), tf.float32)) - act = tf.gather(emb, [2, 5, 2, 2, 5]) - weight = tf.get_variable( - 'w', initializer=tf.constant(np.ones([10, 1]), tf.float32)) - bias = tf.get_variable('b', initializer=tf.constant([0.217])) - pred = tf.matmul(act, weight) + tf.stop_gradient(bias) - loss = tf.reduce_sum(pred) - vmap = py_utils.NestedMap(emb=emb, weight=weight, bias=bias) - var_grads = py_utils.ComputeGradients(loss, vmap) - self.assertCountEqual(var_grads.keys(), ['emb', 'weight']) - for mode in ('NestedMap', 'list'): - if mode == 'NestedMap': - l2_loss, var_grads_with_l2 = py_utils.AdjustGradientsWithLpLoss( - var_grads, 0.1, p=2.0) - else: - l2_loss, var_grads_with_l2 = py_utils.AdjustGradientsWithLpLoss( - var_grads.Flatten(), 0.1, p=2.0) - var_grads_with_l2 = py_utils.Pack(var_grads, var_grads_with_l2) - - self.evaluate(tf.global_variables_initializer()) - var_grads_vals, l2_loss_val, var_grads_with_l2_vals = self.evaluate([ - var_grads.Transform(tuple), l2_loss, - var_grads_with_l2.Transform(tuple) - ]) - print('var_grads_vals = ', var_grads_vals) - print('var_grads_with_l2_vals = ', var_grads_with_l2_vals) - self.assertAllEqual(var_grads_vals.emb[0], - var_grads_with_l2_vals.emb[0]) - self.assertAllEqual(var_grads_vals.weight[0], - var_grads_with_l2_vals.weight[0]) - self.assertAllEqual( - l2_loss_val, - 0.5 * 0.1 * (np.sum(np.square(var_grads_vals.weight[0])) + - np.sum(np.square(var_grads_vals.emb[0][2, :])) + - np.sum(np.square(var_grads_vals.emb[0][5, :])))) - - # With l2, gradients of emb and weight are adjusted. - self.assertAllClose( - var_grads_with_l2_vals.weight[1], - var_grads_vals.weight[1] + 0.1 * var_grads_vals.weight[0]) - self.assertAllClose(var_grads_with_l2_vals.emb[1].indices, - var_grads_vals.emb[1].indices) - self.assertAllClose(var_grads_with_l2_vals.emb[1].indices, - [2, 5, 2, 2, 5]) - self.assertAllClose( - var_grads_with_l2_vals.emb[1].values, var_grads_vals.emb[1].values + - 0.1 * np.array([[1 / 3.], [1 / 2.], [1 / 3.], [1 / 3.], [1 / 2.]]) * - var_grads_vals.emb[0][[2, 5, 2, 2, 5], :]) - - def testSkipL1Regularization(self): - with self.session(use_gpu=False): - beta = tf.get_variable( - 'beta', - initializer=tf.constant(np.arange(10).reshape([1, 10]), tf.float32)) - tf.add_to_collection(py_utils.SKIP_LP_REGULARIZATION, beta) - gamma = tf.get_variable( - 'gamma', - initializer=tf.constant(np.arange(10).reshape([1, 10]), tf.float32)) - act = tf.constant(np.arange(10).reshape([1, 10]), tf.float32) - pred = act * gamma + beta - loss = tf.reduce_sum(pred) - vmap = py_utils.NestedMap(beta=beta, gamma=gamma) - var_grads = py_utils.ComputeGradients(loss, vmap) - self.assertCountEqual(var_grads.keys(), ['beta', 'gamma']) - l1_loss, var_grads_with_l1 = py_utils.AdjustGradientsWithLpLoss( - var_grads, 0.1, p=1.0) - - self.evaluate(tf.global_variables_initializer()) - var_grads_vals, l1_loss_val, var_grads_with_l1_vals = self.evaluate([ - var_grads.Transform(tuple), l1_loss, - var_grads_with_l1.Transform(tuple) - ]) - print('var_grads_vals = ', var_grads_vals) - print('var_grads_with_l1_vals = ', var_grads_with_l1_vals) - self.assertAllEqual(var_grads_vals.beta[0], - var_grads_with_l1_vals.beta[0]) - self.assertAllEqual(var_grads_vals.gamma[0], - var_grads_with_l1_vals.gamma[0]) - self.assertAllEqual(l1_loss_val, - 0.1 * np.sum(np.abs(var_grads_vals.gamma[0]))) - - def testAdjustGradientsWithL1Loss(self): - with self.session(use_gpu=False): - emb = tf.get_variable( - 'emb', - initializer=tf.constant(np.arange(100).reshape([10, 10]), tf.float32)) - act = tf.gather(emb, [2, 5, 2, 2, 5]) - weight = tf.get_variable( - 'w', initializer=tf.constant(np.ones([10, 1]), tf.float32)) - bias = tf.get_variable('b', initializer=tf.constant([0.217])) - pred = tf.matmul(act, weight) + tf.stop_gradient(bias) - loss = tf.reduce_sum(pred) - vmap = py_utils.NestedMap(emb=emb, weight=weight, bias=bias) - var_grads = py_utils.ComputeGradients(loss, vmap) - self.assertCountEqual(var_grads.keys(), ['emb', 'weight']) - l1_loss, var_grads_with_l1 = py_utils.AdjustGradientsWithLpLoss( - var_grads, 0.1, p=1.0) - - self.evaluate(tf.global_variables_initializer()) - var_grads_vals, l1_loss_val, var_grads_with_l1_vals = self.evaluate([ - var_grads.Transform(tuple), l1_loss, - var_grads_with_l1.Transform(tuple) - ]) - print('var_grads_vals = ', var_grads_vals) - print('var_grads_with_l1_vals = ', var_grads_with_l1_vals) - self.assertAllEqual(var_grads_vals.emb[0], var_grads_with_l1_vals.emb[0]) - self.assertAllEqual(var_grads_vals.weight[0], - var_grads_with_l1_vals.weight[0]) - self.assertAllEqual( - l1_loss_val, 0.1 * (np.sum(np.abs(var_grads_vals.weight[0])) + - np.sum(np.abs(var_grads_vals.emb[0][2, :])) + - np.sum(np.abs(var_grads_vals.emb[0][5, :])))) - - # With l1, gradients of emb and weight are adjusted. - self.assertAllClose( - var_grads_with_l1_vals.weight[1], - var_grads_vals.weight[1] + 0.1 * var_grads_vals.weight[0]) - self.assertAllClose(var_grads_with_l1_vals.emb[1].indices, - var_grads_vals.emb[1].indices) - - def testSplitAndConcat(self): - with self.session(): - # Split a Tensor. - m3x4 = tf.constant(np.arange(12).reshape([3, 4])) - splits = py_utils.SplitRecursively(m3x4, 2) - self.assertLen(splits, 2) - for split in splits: - self.assertIsInstance(split, tf.Tensor) - self.assertAllClose([[0, 1], [4, 5], [8, 9]], splits[0].eval()) - self.assertAllClose([[2, 3], [6, 7], [10, 11]], splits[1].eval()) - concatenated = py_utils.ConcatRecursively(splits) - self.assertAllClose(m3x4.eval(), concatenated.eval()) - - # Split along axis 0. - splits = py_utils.SplitRecursively(m3x4, 3, axis=0) - self.assertLen(splits, 3) - concatenated = py_utils.ConcatRecursively(splits, axis=0) - self.assertAllClose(m3x4.eval(), concatenated.eval()) - self.assertAllClose([[0, 1, 2, 3]], splits[0].eval()) - - # Split a list. - list_3 = [m3x4] * 3 - splits = py_utils.SplitRecursively(list_3, 2) - for split in splits: - self.assertIsInstance(split, list) - for x in splits[0]: - self.assertAllClose([[0, 1], [4, 5], [8, 9]], x.eval()) - for x in splits[1]: - self.assertAllClose([[2, 3], [6, 7], [10, 11]], x.eval()) - concatenated = py_utils.ConcatRecursively(splits) - self.assertAllClose([x.eval() for x in list_3], - [x.eval() for x in concatenated]) - - # Split a NestedMap. - map_ab = py_utils.NestedMap(a=m3x4, b=list_3) - splits = py_utils.SplitRecursively(map_ab, 2) - for split in splits: - self.assertIsInstance(split, py_utils.NestedMap) - self.assertIsInstance(split.a, tf.Tensor) - self.assertIsInstance(split.b, list) - for x in splits[0].b: - self.assertAllClose([[0, 1], [4, 5], [8, 9]], x.eval()) - concatenated = py_utils.ConcatRecursively(splits) - self.assertAllClose(map_ab.a.eval(), concatenated.a.eval()) - self.assertAllClose([x.eval() for x in map_ab.b], - [x.eval() for x in concatenated.b]) - - def testFindNeeded(self): - phs = [ - tf.placeholder('float32', shape=(), name='p%d' % (i + 1)) - for i in range(4) - ] - p1, p2, p3, p4 = phs - - z1 = p1 + p2 - z2 = z1 * p3 - - z1_needed = set(py_utils.FindNeededInList(phs, z1)) - z2_needed = set(py_utils.FindNeededInList(phs, [z2])) - z2_p4_needed = set(py_utils.FindNeededInList(phs, [z2, p4])) - - self.assertEqual(set([p1, p2]), z1_needed) - self.assertEqual(set([p1, p2, p3]), z2_needed) - self.assertEqual(set([p1, p2, p3, p4]), z2_p4_needed) - - def testArgMax(self): - - def Compute(x): - with self.session(graph=tf.Graph()): - x = tf.constant(x) - y = py_utils.ArgMax(x) - return self.evaluate([x, y]) - - np.random.seed(426421) - x, y = Compute(np.random.uniform(size=(3, 5, 10))) - self.assertAllEqual(np.argmax(x, axis=-1), y) - - x, y = Compute(np.array([[1, 5, 3, 4, 5], [1, 5, 3, 5, 0]])) # Has dups. - self.assertAllEqual(np.argmax(x, axis=-1), y) - - def testPiecewiseConstant(self): - boundaries = (1000, 2000, 3000) - values = (1e-3, 2e-4, 3e-5, 4e-6) - - def _Eval(x): - with self.session(use_gpu=False): - result = py_utils.PiecewiseConstant( - x, boundaries, values, vdtype=tf.float32) - return self.evaluate(result) - - self.assertAlmostEqual(1e-3, _Eval(0)) - self.assertAlmostEqual(1e-3, _Eval(999)) - self.assertAlmostEqual(2e-4, _Eval(1000)) - self.assertAlmostEqual(2e-4, _Eval(1001)) - self.assertAlmostEqual(2e-4, _Eval(1999)) - self.assertAlmostEqual(3e-5, _Eval(2000)) - self.assertAlmostEqual(4e-6, _Eval(3000)) - self.assertAlmostEqual(4e-6, _Eval(4000)) - - def testRepeatDim(self): - # Create a tensor shaped [time (2), batch(2), depth(3)] - x = tf.constant([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) - # [batch, time, depth] - y = tf.transpose(x, [1, 0, 2]) - # [depth, batch, time] - z = tf.transpose(x, [2, 1, 0]) - repeat_inner_dim0 = py_utils.RepeatDim(x, 2, 0) - repeat_inner_dim1 = py_utils.RepeatDim(y, 2, 1) - repeat_inner_dim2 = py_utils.RepeatDim(z, 2, 2) - - with self.session(use_gpu=False): - [repeat_inner_dim0, repeat_inner_dim1, repeat_inner_dim2] = self.evaluate( - [repeat_inner_dim0, repeat_inner_dim1, repeat_inner_dim2]) - self.assertAllEqual( - repeat_inner_dim0, - [[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]], - [[7, 8, 9], [10, 11, 12]], [[7, 8, 9], [10, 11, 12]]]) - repeat_inner_dim = np.transpose(repeat_inner_dim0, [1, 0, 2]) - self.assertAllEqual(repeat_inner_dim1, repeat_inner_dim) - repeat_inner_dim = np.transpose(repeat_inner_dim0, [2, 1, 0]) - self.assertAllEqual(repeat_inner_dim2, repeat_inner_dim) - - def testStackTensorsRecursively(self): - with self.session(use_gpu=False, graph=tf.Graph()): - stacked = py_utils.StackTensorsRecursively([ - py_utils.NestedMap( - x=tf.constant([1, 2]), - y=py_utils.NestedMap(), - z=py_utils.NestedMap(a=tf.constant([1, 2]),), - ), - py_utils.NestedMap( - x=tf.constant([3, 4]), - y=py_utils.NestedMap(), - z=py_utils.NestedMap(a=tf.constant([10, 20]),), - ), - ]) - self.evaluate(tf.global_variables_initializer()) - self.assertAllEqual(stacked.x, tf.constant([[1, 2], [3, 4]])) - self.assertAllEqual(stacked.z.a, tf.constant([[1, 2], [10, 20]])) - - def testCumSum(self): - with self.session(use_gpu=False), mock.patch( - 'lingvo.core.py_utils.use_tpu', return_value=True): - np.random.seed(12345) - x = tf.constant(np.random.rand(2, 4, 8), dtype=tf.float32) - - # If rank is non-static, py_utils.CumSum falls back to tf.cumsum. Make - # sure it's not the case. - rank = py_utils.GetRank(x) - self.assertIsInstance(rank, int) - - self.assertAllClose(py_utils.CumSum(x, 0).eval(), tf.cumsum(x, 0).eval()) - self.assertAllClose(py_utils.CumSum(x, 1).eval(), tf.cumsum(x, 1).eval()) - self.assertAllClose(py_utils.CumSum(x, 2).eval(), tf.cumsum(x, 2).eval()) - self.assertAllClose( - py_utils.CumSum(x, -1).eval(), - tf.cumsum(x, -1).eval()) - self.assertAllClose( - py_utils.CumSum(x, -2).eval(), - tf.cumsum(x, -2).eval()) - self.assertAllClose( - py_utils.CumSum(x, -3).eval(), - tf.cumsum(x, -3).eval()) - with self.assertRaises(ValueError): - py_utils.CumSum(x, -4).eval() - - def testProjectLastDim(self): - np.random.seed(12345) - input_dim = 4 - output_dim = 6 - inputs_p = np.random.rand(2, 5, input_dim) - weight_p = np.random.rand(input_dim, output_dim) - - with self.session(use_gpu=False), mock.patch( - 'lingvo.core.py_utils.use_tpu', return_value=True): - inputs = tf.constant(inputs_p, dtype=tf.float32) - weight = tf.constant(weight_p, dtype=tf.float32) - outputs = py_utils.ProjectLastDim(inputs, weight, input_dim, output_dim) - - self.assertAllClose(outputs.eval(), - np.einsum('bti,io->bto', inputs_p, weight_p)) - - def testAssertEvenDivide(self): - with self.session(): - op = py_utils.assert_even_divide(4, 2) - self.evaluate(op) - - op = py_utils.assert_even_divide(4, 3) - with self.assertRaises(tf.errors.InvalidArgumentError): - self.evaluate(op) - - @mock.patch.object(tf.tpu, 'outside_compilation', autospec=True) - def testTpuHostDecorator(self, mock_outside_compilation): - - with self.session(use_gpu=False), mock.patch( - 'lingvo.core.py_utils.use_tpu', return_value=True): - - def noop_outside_compilation(func, *args, **kwargs): # pylint:disable=invalid-name - return func(*args, **kwargs) - - mock_outside_compilation.side_effect = noop_outside_compilation - - @py_utils.tpu_host - def foo(x): # pylint:disable=invalid-name - return bar(x) * x - - @py_utils.tpu_host - def bar(x): # pylint:disable=invalid-name - return tf.math.log(x) - - x = tf.random.uniform([]) - y = foo(x) - unused_z = y * y - - self.assertTrue(py_utils.use_tpu()) - self.assertEqual(1, mock_outside_compilation.call_count) - - def testRemoveAssertContext(self): - - @tf.Defun(tf.float32, noinline=True) - def Op(x): - with py_utils.RemoveAssertContext(remove=True): - x = py_utils.with_dependencies( - [tf.assert_equal(0, 1, message='assert not removed')], x) - x = py_utils.with_dependencies( - [tf.check_ops.assert_equal(0, 1, message='assert not removed')], x) - return x - - with self.session(use_gpu=True): - - x = tf.ones((2, 2)) - y = Op(x) - _ = self.evaluate(y) - - def testDefaultVnParams(self): - default_vn = py_utils.DefaultVN() - disable_vn = py_utils.DisableVN() - self.assertNotEqual(default_vn, disable_vn) - - def testShardedFilePatternToGlob(self): - file_pattern = '/some/path/to/file@8' - self.assertEqual('/some/path/to/file-?????-of-00008', - py_utils.ShardedFilePatternToGlob(file_pattern)) - - file_pattern = '/some/path/to/file@000008' - self.assertEqual('/some/path/to/file-?????-of-00008', - py_utils.ShardedFilePatternToGlob(file_pattern)) - - file_pattern = '/some/path/to/file@888888' - self.assertEqual('/some/path/to/file-?????-of-888888', - py_utils.ShardedFilePatternToGlob(file_pattern)) - - file_pattern = '/some/path/to/file' - self.assertEqual('/some/path/to/file', - py_utils.ShardedFilePatternToGlob(file_pattern)) - - file_pattern = '/some/path/to/file*' - self.assertEqual('/some/path/to/file*', - py_utils.ShardedFilePatternToGlob(file_pattern)) - - file_pattern = '/some/path/to/file1@8,/some/path/to/file2@8' - with self.assertRaises(ValueError): - py_utils.ShardedFilePatternToGlob(file_pattern) - - -class DeterministicDropoutTest(test_utils.TestCase): - - def testDeterministicDropoutTest(self): - x = tf.ones([4, 6], dtype=tf.float32) - x = py_utils.DeterministicDropout(x, keep_prob=0.7, seeds=[1234, 5678]) - with self.session(): - x_val = self.evaluate(x) - self.assertAllClose([ - [1.0 / 0.7, 0.0000000, 0.0000000, 0.0000000, 1.0 / 0.7, 1.0 / 0.7], - [1.0 / 0.7, 1.0 / 0.7, 1.0 / 0.7, 1.0 / 0.7, 1.0 / 0.7, 1.0 / 0.7], - [1.0 / 0.7, 0.0000000, 0.0000000, 1.0 / 0.7, 1.0 / 0.7, 0.0000000], - [1.0 / 0.7, 0.0000000, 0.0000000, 1.0 / 0.7, 1.0 / 0.7, 1.0 / 0.7], - ], x_val) - self.assertAllClose(22.85714, np.sum(x_val)) - self.assertEqual(x_val.dtype, np.float32) - - -class DeterministicVNTest(test_utils.TestCase): - - def testDeterministicVNTest(self): - x = py_utils.DeterministicVN( - py_utils.NestedMap({'fprop_dtype': tf.float32}), - seeds=[1234, 5678], - noise_shape=np.asarray([3, 3])) - with self.session(): - x_val = self.evaluate(x) - self.assertAllClose( - [[0.686219, 1.360352, 0.197617], [1.866415, 0.326923, -0.781743], - [1.130218, -0.726336, 0.667041]], x_val) - self.assertAllClose(4.726707, np.sum(x_val)) - self.assertEqual(x_val.dtype, np.float32) - - -class WeightedAvgTest(test_utils.TestCase): - - def testWeightedAvg(self): - with self.session(use_gpu=False): - losses = tf.constant([5.6, 4.6, 1.5, 3.4]) - weights = tf.constant([10, 9, 2, 8]) - loss, weight = py_utils.WeightedAvg(losses, weights) - expected = [4.4, 29] - actual = self.evaluate([loss, weight]) - self.assertAllClose(actual, expected) - - def testWeightedAvgOfMetrics(self): - with self.session(use_gpu=False): - metrics = [{ - 'a': (2.0, 0.5), - 'b': (5.0, 1.5) - }, { - 'a': (9.0, 3.0), - 'b': (4.0, 0.5) - }] - expected = {'a': (8.0, 3.5), 'b': (4.75, 2.0)} - weighted_avg = py_utils.WeightedAvgOfMetrics(metrics) - actual = self.evaluate(weighted_avg) - self.assertDictEqual(actual, expected) - - def testConcatPerExampleTensors(self): - with self.session(use_gpu=False): - per_example_1 = { - 'a': - tf.constant([[1.0, 2.0, 3.0], [12.0, 13.0, 14.0]], - dtype=tf.float32), - 'b': - tf.constant([[1.5, 2.5, 3.5, 4.5]], dtype=tf.float32), - } - per_example_2 = { - 'a': - tf.constant([[3.0, 4.0, 5.0], [9.0, 10.0, 11.0]], - dtype=tf.float32), - 'b': - tf.constant([[3.5, 4.5, 5.5, 6.5]], dtype=tf.float32), - } - expected = { - 'a': [[1.0, 2.0, 3.0], [12.0, 13.0, 14.0], [3.0, 4.0, 5.0], - [9.0, 10.0, 11.0]], - 'b': [[1.5, 2.5, 3.5, 4.5], [3.5, 4.5, 5.5, 6.5]] - } - stacked = py_utils.ConcatPerExampleTensors([per_example_1, per_example_2]) - actual = self.evaluate(stacked) - self.assertAllClose(actual['a'], expected['a']) - self.assertAllClose(actual['b'], expected['b']) - self.assertEqual(2, len(actual)) - - def testCombineMetrics(self): - a = py_utils.NestedMap() - a['a'] = (1, 1) - a['loss'] = (100, 10) - b = py_utils.NestedMap() - b['b'] = (2, 2) - b['loss'] = (50, 20) - c = py_utils.NestedMap() - c['loss'] = (60, 15) - combined = py_utils.CombineMetrics([(a, 0.7), (b, 0.3), (c, 1.5)]) - self.assertEqual(combined['a'], (1, 1)) - self.assertEqual(combined['b'], (2, 2)) - total_loss = combined['loss'][0] * combined['loss'][1] - self.assertEqual(total_loss, 100 * 10 * 0.7 + 50 * 20 * 0.3 + 60 * 15 * 1.5) - - def testCombineMetricsKeyNotInAllMetrics(self): - a = py_utils.NestedMap() - a['a'] = (1, 1) - b = py_utils.NestedMap() - b['b'] = (2, 2) - b['loss'] = (50, 20) - c = py_utils.NestedMap() - c['loss'] = (60, 15) - with self.assertRaises(ValueError): - py_utils.CombineMetrics([(a, 0.7), (b, 0.3), (c, 1.5)]) - - -class OverrideVarsFromCheckpointsTest(test_utils.TestCase): - - def _GetLeNetVarsFirstVal(self, sess): - with tf.variable_scope('lenet5', reuse=True): - conv0 = tf.get_variable('conv0/w/var') - conv1 = tf.get_variable('conv1/w/var') - fc_bias = tf.get_variable('fc/b/var') - conv0_val, conv1_val, fc_bias_val = self.evaluate([conv0, conv1, fc_bias]) - return conv0_val[0][0][0][0], conv1_val[0][0][0][0], fc_bias_val[0] - - def testOverrideVarsFromCheckpoint(self): - - with self.session(use_gpu=False) as sess: - tf.random.set_seed(8372749040) - cfg = model_registry.GetParams('image.mnist.LeNet5', 'Train') - with cluster_factory.ForTestingWorker(mode='sync', job='trainer_client'): - cfg.Instantiate() - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose( - # These are initialized values before overriding with checkpoint. - self._GetLeNetVarsFirstVal(sess), - [-0.005945, -0.036722, 0.0]) - checkpoint_path = test_helper.test_src_dir_path( - 'core/testdata/lenet_test_model') - variable_loading_rules = [('lenet5/conv0/w/var', 'lenet5/conv0/w/var'), - ('lenet5/conv1/w/var', 'lenet5/conv1/w/var')] - variable_ignore_rules = [] - py_utils.OverrideVarsFromCheckpoint(sess, tf.all_variables(), - checkpoint_path, - variable_loading_rules, - variable_ignore_rules) - self.assertAllClose( - # Now conv weights have been overwritten but fc bias has not. - self._GetLeNetVarsFirstVal(sess), - [0.043092, -0.024082, 0.0]) - - def testOverrideVarsFromCheckpointWithIgnoreRules(self): - - with self.session(use_gpu=False) as sess: - tf.random.set_seed(8372749040) - cfg = model_registry.GetParams('image.mnist.LeNet5', 'Train') - with cluster_factory.ForTestingWorker(mode='sync', job='trainer_client'): - cfg.Instantiate() - self.evaluate(tf.global_variables_initializer()) - self.assertAllClose( - # These are initialized values before overriding with checkpoint. - self._GetLeNetVarsFirstVal(sess), - [-0.005945, -0.036722, 0.0]) - checkpoint_path = test_helper.test_src_dir_path( - 'core/testdata/lenet_test_model') - variable_loading_rules = [('lenet5/conv0/w/var', 'lenet5/conv0/w/var'), - ('lenet5/conv1/w/var', 'lenet5/conv1/w/var')] - variable_ignore_rules = ['lenet5/conv1/w/var'] - py_utils.OverrideVarsFromCheckpoint(sess, tf.all_variables(), - checkpoint_path, - variable_loading_rules, - variable_ignore_rules) - self.assertAllClose( - # Now only conv0 weights have been overridden. - self._GetLeNetVarsFirstVal(sess), - [0.043092, -0.036722, 0.0]) - - -def _AddOne(x): - return None if x is None else x + type(x)(1) - - -class NestedMapTest(test_utils.TestCase): - - _TUPLE = collections.namedtuple('Tuple', ['x', 'y']) - - def _get_basic_test_inputs(self): - m = py_utils.NestedMap() - m.foo = [1, 20, [32]] - m.bar = py_utils.NestedMap() - m.bar.x = 100 - m.bar.y = [200, py_utils.NestedMap(z='abc')] - return m - - def _get_advanced_test_inputs(self): - m = py_utils.NestedMap() - m.w = None - m.y = (200, py_utils.NestedMap(z='abc')) - m.x = {'foo': 1, 'bar': 'def'} - m.z = self._TUPLE(5, 'xyz') - m.zz = [] - return m - - def testBasic(self): - x = py_utils.NestedMap() - self.assertLen(x, 0) - x['foo'] = 100 - self.assertEqual(100, x.foo) - self.assertEqual(100, x['foo']) - x.bar = py_utils.NestedMap({'baz': 200}) - self.assertEqual(200, x.bar.baz) - self.assertNotIn('flatten', x) - - def testPrint(self): - self.assertEqual(py_utils.NestedMap().DebugString(), '') - - expected = """bar.x 100 -bar.y[0] 200 -bar.y[1].z abc -foo[0] 1 -foo[1] 20 -foo[2][0] 32""" - m = self._get_basic_test_inputs() - self.assertEqual(m.DebugString(), expected) - - m = self._get_advanced_test_inputs() - res = m.DebugString() - w, x1, x2, y, z = res.split('\n') - self.assertEqual(w, 'w None') - self.assertEqual(x1, 'x.bar def') - self.assertEqual(x2, 'x.foo 1') - self.assertEqual(y, "y (200, {'z': 'abc'})") - self.assertEqual(z, "z Tuple(x=5, y='xyz')") - - def testTransformBasic(self): - n = py_utils.Transform(_AddOne, py_utils.NestedMap()) - self.assertEqual(n.DebugString(), '') - n = py_utils.NestedMap().Transform(_AddOne) - self.assertEqual(n.DebugString(), '') - - expected = """bar.x 101 -bar.y[0] 201 -bar.y[1].z abc1 -foo[0] 2 -foo[1] 21 -foo[2][0] 33""" - m = self._get_basic_test_inputs() - n = py_utils.Transform(_AddOne, m) - self.assertEqual(n.DebugString(), expected) - n = m.Transform(_AddOne) - self.assertEqual(n.DebugString(), expected) - - # Original has not been modified. - expected = """bar.x 100 -bar.y[0] 200 -bar.y[1].z abc -foo[0] 1 -foo[1] 20 -foo[2][0] 32""" - self.assertEqual(m.DebugString(), expected) - - def testTransformAdvanced(self): - m = self._get_advanced_test_inputs() - original = [ - ('w', None), - ('x.bar', 'def'), - ('x.foo', 1), - ('y', (200, { - 'z': 'abc' - })), - ('z', self._TUPLE(x=5, y='xyz')), - ] - self.assertEqual(m.FlattenItems(), original) - - expected = [ - ('w', None), - ('x.bar', 'def1'), - ('x.foo', 2), - ('y', (201, { - 'z': 'abc1' - })), - ('z', self._TUPLE(x=6, y='xyz1')), - ] - n = py_utils.Transform(_AddOne, m) - self.assertEqual(n.zz, []) - self.assertNotEqual(expected, original) - self.assertEqual(n.FlattenItems(), expected) - - with self.assertRaises(TypeError): - m.Transform(_AddOne) - - def _AddOneIgnoreError(x): - try: - return _AddOne(x) - except TypeError: - return x - - expected = [ - ('w', None), - ('x.bar', 'def1'), - ('x.foo', 2), - ('y', (200, { - 'z': 'abc' - })), - ('z', self._TUPLE(x=5, y='xyz')), - ] - n = m.Transform(_AddOneIgnoreError) - self.assertEqual(n.zz, []) - self.assertNotEqual(expected, original) - self.assertEqual(n.FlattenItems(), expected) - - # Original has not been modified. - self.assertEqual(m.FlattenItems(), original) - - def testFlattenBasic(self): - self.assertEqual(py_utils.Flatten(py_utils.NestedMap()), []) - self.assertEqual(py_utils.NestedMap().Flatten(), []) - self.assertEqual(py_utils.NestedMap().FlattenItems(), []) - - expected = [100, 200, 'abc', 1, 20, 32] - m = self._get_basic_test_inputs() - self.assertEqual(py_utils.Flatten(m), expected) - self.assertEqual(m.Flatten(), expected) - - expected_keys = [ - 'bar.x', 'bar.y[0]', 'bar.y[1].z', 'foo[0]', 'foo[1]', 'foo[2][0]' - ] - self.assertEqual(m.FlattenItems(), list(zip(expected_keys, expected))) - - def testFlattenAdvanced(self): - m = self._get_advanced_test_inputs() - - expected = [None, 'def', 1, 200, 'abc', 5, 'xyz'] - self.assertEqual(py_utils.Flatten(m), expected) - - expected = [ - None, - 'def', - 1, - (200, { - 'z': 'abc' - }), - self._TUPLE(x=5, y='xyz'), - ] - self.assertEqual(m.Flatten(), expected) - - expected = [ - ('w', None), - ('x.bar', 'def'), - ('x.foo', 1), - ('y', (200, { - 'z': 'abc' - })), - ('z', self._TUPLE(x=5, y='xyz')), - ] - self.assertEqual(m.FlattenItems(), expected) - - def testPackBasic(self): - n = py_utils.Pack(py_utils.NestedMap(), []) - self.assertEqual(n.DebugString(), '') - n = py_utils.NestedMap().Pack([]) - self.assertEqual(n.DebugString(), '') - - expected = """bar.x 0 -bar.y[0] 1 -bar.y[1].z 2 -foo[0] 3 -foo[1] 4 -foo[2][0] 5""" - m = self._get_basic_test_inputs() - n = py_utils.Pack(m, list(range(6))) - self.assertEqual(n.DebugString(), expected) - n = m.Pack(list(range(6))) - self.assertEqual(n.DebugString(), expected) - - # Original has not been modified. - expected = """bar.x 100 -bar.y[0] 200 -bar.y[1].z abc -foo[0] 1 -foo[1] 20 -foo[2][0] 32""" - self.assertEqual(m.DebugString(), expected) - - def testPackAdvanced(self): - m = self._get_advanced_test_inputs() - - expected = [ - ('w', 0), - ('x.bar', 1), - ('x.foo', 2), - ('y', (3, { - 'z': 4 - })), - ('z', self._TUPLE(x=5, y=None)), - ] - n = py_utils.Pack(m, list(range(6)) + [None]) - self.assertEqual(n.zz, []) - self.assertEqual(n.FlattenItems(), expected) - - expected = [('w', 0), ('x.bar', 1), ('x.foo', 2), ('y', 3), ('z', None)] - n = m.Pack(list(range(4)) + [None]) - self.assertEqual(n.zz, []) - self.assertEqual(n.FlattenItems(), expected) - - # Original has not been modified. - expected = [ - ('w', None), - ('x.bar', 'def'), - ('x.foo', 1), - ('y', (200, { - 'z': 'abc' - })), - ('z', self._TUPLE(x=5, y='xyz')), - ] - self.assertEqual(m.FlattenItems(), expected) - - def testIsCompatible(self): - empty = py_utils.NestedMap() - self.assertTrue(empty.IsCompatible(empty)) - self.assertTrue(py_utils.IsCompatible(empty, empty)) - self.assertTrue(empty.IsCompatible(py_utils.NestedMap(x=[]))) - self.assertFalse(py_utils.IsCompatible(empty, py_utils.NestedMap(x=[]))) - self.assertTrue(empty.IsCompatible(py_utils.NestedMap(x=empty))) - self.assertFalse(py_utils.IsCompatible(empty, py_utils.NestedMap(x=empty))) - self.assertTrue(empty.IsCompatible(py_utils.NestedMap(x={}))) - self.assertFalse(py_utils.IsCompatible(empty, py_utils.NestedMap(x={}))) - x = py_utils.NestedMap( - a='a', b='b', c=py_utils.NestedMap(d='d', e=[1, 2, 4])) - y = py_utils.NestedMap(a=1, b=2, c=py_utils.NestedMap(d=3, e=[10, 20, 30])) - z = py_utils.NestedMap( - a=1, b=[10, 20, 30], c=py_utils.NestedMap(d=3, e=['x', 'y', 'z'])) - self.assertTrue(x.IsCompatible(y)) - self.assertTrue(py_utils.IsCompatible(x, y)) - self.assertFalse(x.IsCompatible(z)) - self.assertFalse(py_utils.IsCompatible(x, z)) - - def testFilter(self): - x = py_utils.NestedMap( - a=100, - b=200, - c=300, - d=py_utils.NestedMap(foo=38, bar=192, ok=[200, 300], ko=[10, 20])) - y = x.Filter(lambda v: v > 150) - self.assertEqual(y.FlattenItems(), [('b', 200), ('c', 300), ('d.bar', 192), - ('d.ok[0]', 200), ('d.ok[1]', 300)]) - self.assertNotIn('ko', y.d) - - y = x.Filter(lambda v: v > 500) - self.assertLen(y.FlattenItems(), 0) - - def testFilterKeyVal(self): - x = py_utils.NestedMap( - a=100, - b=200, - c=300, - d=py_utils.NestedMap(foo=38, bar=192, ok=[200, 300], ko=[10, 20])) - selected = {'a', 'd.foo', 'd.ok[1]'} - - def Sel(k, _): - return k in selected - - y = x.FilterKeyVal(Sel) - self.assertEqual(y.FlattenItems(), [('a', 100), ('d.foo', 38), - ('d.ok[0]', 300)]) - - def testCopy(self): - # This is not a copy. - x = py_utils.NestedMap( - a='a', b='b', c=py_utils.NestedMap(d='d', e=[1, 2, 4])) - y = x - y.a = 'y' - self.assertEqual('y', y.a) - self.assertEqual('y', x.a) - - # This is a (shallow) copy. - x = py_utils.NestedMap( - a='a', b='b', c=py_utils.NestedMap(d='d', e=[1, 2, 4])) - y = py_utils.NestedMap(x) - self.assertNotEqual(id(x), id(y)) - y.a = 'y' - y.c.d = 'z' - self.assertEqual('y', y.a) - self.assertEqual('a', x.a) - self.assertEqual('z', y.c.d) - self.assertEqual('z', x.c.d) - - # This is also a (shallow) copy. - x = py_utils.NestedMap( - a='a', b='b', c=py_utils.NestedMap(d='d', e=[1, 2, 4])) - y = x.copy() - self.assertNotEqual(id(x), id(y)) - y.a = 'y' - y.c.d = 'z' - self.assertEqual('y', y.a) - self.assertEqual('a', x.a) - self.assertEqual('z', y.c.d) - self.assertEqual('z', x.c.d) - - def testDeepCopy(self): - - class SomeObj: - - def __init__(self): - self.foo = 'foo' - - x = py_utils.NestedMap( - a='a', - b='b', - c=py_utils.NestedMap(d='d', e=[1, 2, 4], obj=SomeObj()), - f=[], - g={}, - h=py_utils.NestedMap(), - i=None) - # Perform a deep copy. - y = copy.deepcopy(x) - # Objects are different. - self.assertNotEqual(id(x), id(y)) - - # modify deep copy, even nested version. - y.a = 'x' - y.c.e[0] = 'y' - y.c.obj.foo = 'bar' - y.f.append(5) - y.h.foo = 'bar' - - # x values are the originals. - self.assertEqual('a', x.a) - self.assertEqual(1, x.c.e[0]) - self.assertEqual([], x.f) - self.assertEqual({}, x.g) - self.assertLen(x.h, 0) - - # y values are updated. - self.assertEqual('x', y.a) - self.assertEqual('y', y.c.e[0]) - self.assertEqual([5], y.f) - self.assertEqual({}, y.g) - self.assertLen(y.h, 1) - self.assertEqual('bar', y.h.foo) - self.assertIsNone(y.i) - - # but leaf objects are the shared. - self.assertEqual('bar', x.c.obj.foo) - self.assertEqual('bar', y.c.obj.foo) - self.assertEqual(id(x.c.obj), id(y.c.obj)) - - def testAttrAccess(self): - a = py_utils.NestedMap() - a.a1 = 10 - self.assertEqual(10, a.a1) - self.assertEqual(10, a['a1']) - self.assertEqual(10, a.get('a1')) - self.assertEqual(10, a.get(u'a1')) - self.assertEqual(10, getattr(a, 'a1')) - - a[u'a1'] = 20 - self.assertEqual(20, a.a1) - self.assertEqual(20, a['a1']) - self.assertEqual(20, a.get('a1')) - self.assertEqual(20, a.get(u'a1')) - self.assertEqual(20, getattr(a, 'a1')) - - with self.assertRaisesRegex(AttributeError, 'available attributes'): - print(a.a2) - with self.assertRaises(KeyError): - print(a['a2']) - - # 'get' is a reserved key. - with self.assertRaisesRegex(AssertionError, 'is a reserved key'): - a.get = 10 - with self.assertRaisesRegex(AssertionError, 'is a reserved key'): - a['get'] = 10 - with self.assertRaisesRegex(AssertionError, 'is a reserved key'): - _ = py_utils.NestedMap(get=2) - - del a.a1 - with self.assertRaisesRegex(AttributeError, 'available attributes'): - print(a.a1) - - with self.assertRaisesRegex(AttributeError, 'available attributes'): - del a.a2 - - -class ReadOnlyAttrDictViewTest(test_utils.TestCase): - - def testWrapping(self): - backing = dict() - view = py_utils.ReadOnlyAttrDictView(backing) - backing['test'] = 1 - - self.assertEqual(1, view['test']) - self.assertEqual(1, view.test) - # Item assign. - with self.assertRaises(AttributeError): - view['test'] = 2 - self.assertEqual(1, view['test']) - # Attr assign. - with self.assertRaises(AttributeError): - view.test = 2 - self.assertEqual(1, view['test']) - # Delete attr. - with self.assertRaises(AttributeError): - del view.test - self.assertEqual(1, view['test']) - # Delete item. - with self.assertRaises(AttributeError): - del view['test'] - self.assertEqual(1, view['test']) - - -class PadPadSequenceToTest(test_utils.TestCase): - - def test2DInputs(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(3, 3), seed=123456) - padding = tf.constant([[0, 0, 0], [0, 0, 1], [0, 1, 1]], tf.float32) - length = 6 - new_xs, new_padding = py_utils.PadSequenceTo([x, x], padding, length, 0) - - real_xs, real_padding = self.evaluate([new_xs, new_padding]) - expected_x = [ - [0.38615, 2.975221, -0.852826, 0., 0., 0.], - [-0.571142, -0.432439, 0.413158, 0., 0., 0.], - [0.255314, -0.985647, 1.461641, 0., 0., 0.], - ] - expected_padding = [ - [0., 0., 0., 1., 1., 1.], - [0., 0., 1., 1., 1., 1.], - [0., 1., 1., 1., 1., 1.], - ] - self.assertAllClose([expected_x, expected_x], real_xs) - self.assertAllClose(expected_padding, real_padding) - - def testSingleInput(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(3, 3), seed=123456) - padding = tf.constant([[0, 0, 0], [0, 0, 1], [0, 1, 1]], tf.float32) - length = 6 - new_x, new_padding = py_utils.PadSequenceTo(x, padding, length, 0) - - real_x, real_padding = self.evaluate([new_x, new_padding]) - expected_x = [ - [0.38615, 2.975221, -0.852826, 0., 0., 0.], - [-0.571142, -0.432439, 0.413158, 0., 0., 0.], - [0.255314, -0.985647, 1.461641, 0., 0., 0.], - ] - expected_padding = [ - [0., 0., 0., 1., 1., 1.], - [0., 0., 1., 1., 1., 1.], - [0., 1., 1., 1., 1., 1.], - ] - self.assertAllClose(expected_x, real_x) - self.assertAllClose(expected_padding, real_padding) - - -class PadSequenceDimensionTest(test_utils.TestCase): - - def testPadSequenceDimension_2D(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(3, 3), seed=123456) - length = 6 - padded_x = py_utils.PadSequenceDimension(x, length, 0) - self.assertEqual(padded_x.shape.as_list(), [3, 6]) - real_x = self.evaluate(padded_x) - expected_x = [ - [0.38615, 2.975221, -0.852826, 0., 0., 0.], - [-0.571142, -0.432439, 0.413158, 0., 0., 0.], - [0.255314, -0.985647, 1.461641, 0., 0., 0.], - ] - self.assertAllClose(expected_x, real_x) - - def testPadSequenceDimension_2D_UnknownShape(self): - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - shape = tf.placeholder(tf.int32) - x = tf.random.normal(shape=shape, seed=123456) - length = 6 - padded_x = py_utils.PadSequenceDimension(x, length, 0) - self.assertEqual(padded_x.shape, tf.TensorShape(None)) - real_x = sess.run(padded_x, feed_dict={shape: [3, 3]}) - expected_x = [ - [0.38615, 2.975221, -0.852826, 0., 0., 0.], - [-0.571142, -0.432439, 0.413158, 0., 0., 0.], - [0.255314, -0.985647, 1.461641, 0., 0., 0.], - ] - self.assertAllClose(expected_x, real_x) - - def testPadSequenceDimension_ShortPaddingLength(self): - x = tf.random.normal(shape=(3, 8), seed=123456) - length = 6 - with self.assertRaisesRegex(ValueError, 'Paddings must be non-negative'): - py_utils.PadSequenceDimension(x, length, 0) - - def testPadSequenceDimension_4D(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(2, 2, 2, 2), seed=123456) - length = 4 - padded_x = py_utils.PadSequenceDimension(x, length, 1) - real_x = self.evaluate(padded_x) - expected_x = [ - [[[0.38614973, 2.97522092], [-0.85282576, -0.57114178]], - [[-0.43243945, 0.41315758], [0.2553139, -0.98564667]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1.46164131, 0.12003655], [-0.0986772, 0.60644895]], - [[0.03092973, -0.96897006], [-1.27853918, -0.44018385]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - ] - self.assertAllClose(expected_x, real_x) - - def testPadSequenceDimension_UnmatchedShape(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(2, 2, 2, 2), seed=123456) - length = 4 - self.assertRaises(ValueError, py_utils.PadSequenceDimension, x, length, 0, - (32, 3, 4, 5)) - - -class PadOrTrimToTest(test_utils.TestCase): - - def test2DConstantShapePad(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(3, 3), seed=123456) - shape = [4, 6] - padded_x_right = py_utils.PadOrTrimTo(x, shape, pad_val=0) - padded_x_left = py_utils.PadOrTrimTo( - x, shape, pad_val=0, pad_after_contents=False) - self.assertEqual(padded_x_right.shape.as_list(), [4, 6]) - self.assertEqual(padded_x_left.shape.as_list(), [4, 6]) - real_x_right, real_x_left = self.evaluate([padded_x_right, padded_x_left]) - expected_x_right = [ - [0.38615, 2.975221, -0.852826, 0., 0., 0.], - [-0.571142, -0.432439, 0.413158, 0., 0., 0.], - [0.255314, -0.985647, 1.461641, 0., 0., 0.], - [0., 0., 0., 0., 0., 0.], - ] - self.assertAllClose(expected_x_right, real_x_right) - expected_x_left = [ - [0., 0., 0., 0., 0., 0.], - [0., 0., 0., 0.38615, 2.975221, -0.852826], - [0., 0., 0., -0.571142, -0.432439, 0.413158], - [0., 0., 0., 0.255314, -0.985647, 1.461641], - ] - self.assertAllClose(expected_x_left, real_x_left) - - def test2DConstantShapeTrim(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(3, 3), seed=123456) - shape = [1, 3] - trimmed_x_right = py_utils.PadOrTrimTo(x, shape, pad_val=0) - trimmed_x_left = py_utils.PadOrTrimTo( - x, shape, pad_val=0, pad_after_contents=False) - self.assertEqual(trimmed_x_right.shape.as_list(), [1, 3]) - self.assertEqual(trimmed_x_left.shape.as_list(), [1, 3]) - real_x_right, real_x_left = self.evaluate( - [trimmed_x_right, trimmed_x_left]) - expected_x_right = [[0.38615, 2.975221, -0.852826]] - self.assertAllClose(expected_x_right, real_x_right) - expected_x_left = [[0.255314, -0.985647, 1.461641]] - self.assertAllClose(expected_x_left, real_x_left) - - def test2DStaticShape(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(3, 3), seed=123456) - y = tf.zeros(shape=(4, 6)) - padded_x = py_utils.PadOrTrimTo(x, y.shape, pad_val=0) - self.assertEqual(padded_x.shape.as_list(), [4, 6]) - real_x = self.evaluate(padded_x) - expected_x = [ - [0.38615, 2.975221, -0.852826, 0., 0., 0.], - [-0.571142, -0.432439, 0.413158, 0., 0., 0.], - [0.255314, -0.985647, 1.461641, 0., 0., 0.], - [0., 0., 0., 0., 0., 0.], - ] - self.assertAllClose(expected_x, real_x) - - def test2DDynamicShape(self): - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - x = tf.random.normal(shape=(3, 3), seed=123456) - y = tf.placeholder(dtype=tf.float32) - padded_x = py_utils.PadOrTrimTo(x, tf.shape(y), pad_val=0) - self.assertEqual(padded_x.shape, tf.TensorShape(None)) - real_x = sess.run(padded_x, feed_dict={y: np.zeros((4, 6))}) - expected_x = [ - [0.38615, 2.975221, -0.852826, 0., 0., 0.], - [-0.571142, -0.432439, 0.413158, 0., 0., 0.], - [0.255314, -0.985647, 1.461641, 0., 0., 0.], - [0., 0., 0., 0., 0., 0.], - ] - self.assertAllClose(expected_x, real_x) - - def testDynamicTensorShapeRaises(self): - tensor = tf.zeros(shape=[3, 2]) - shape = tf.TensorShape([3, None]) - with self.assertRaises(ValueError): - py_utils.PadOrTrimTo(tensor, shape) - - def test4D(self): - with self.session(use_gpu=False, graph=tf.Graph()): - x = tf.random.normal(shape=(2, 2, 2, 2), seed=123456) - shape = (1, 1, 3, 3) - padded_x = py_utils.PadOrTrimTo(x, shape, pad_val=1) - real_x = self.evaluate(padded_x) - expected_x = [[[ - [0.38615, 2.975221, 1.], - [-0.852826, -0.571142, 1.], - [1., 1., 1.], - ]]] - self.assertAllClose(expected_x, real_x) - - -class ApplyPaddingTest(test_utils.TestCase): - - def testApplyPaddingToZeroWithBroadcast(self): - with self.session(): - y = py_utils.ApplyPadding( - tf.convert_to_tensor([[0.0], [1.0], [0.0]]), - tf.convert_to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])).eval() - self.assertAllClose(y, [[1.0, 2.0], [0.0, 0.0], [5.0, 6.0]]) - - def testApplyPaddingToConstWithBroadcast(self): - with self.session(): - y = py_utils.ApplyPadding( - tf.convert_to_tensor([[0.0], [1.0], [0.0]]), - tf.convert_to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]), - tf.convert_to_tensor([[1.0, 2.0], [9.0, 10.0], [5.0, 6.0]])).eval() - self.assertAllClose(y, [[1.0, 2.0], [9.0, 10.0], [5.0, 6.0]]) - - def testApplyPaddingToZeroWithoutBroadcast(self): - with self.session(): - y = py_utils.ApplyPadding( - tf.convert_to_tensor([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]), - tf.convert_to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])).eval() - self.assertAllClose(y, [[1.0, 2.0], [0.0, 4.0], [5.0, 0.0]]) - - def testApplyPaddingToZeroWithBroadcastArithmetic(self): - with self.session(): - y = py_utils.ApplyPadding( - tf.convert_to_tensor([[0.0], [1.0], [0.0]]), - tf.convert_to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]), - use_select=False).eval() - self.assertAllClose(y, [[1.0, 2.0], [0.0, 0.0], [5.0, 6.0]]) - - def testApplyPaddingToZeroWithoutBroadcastArithmetic(self): - with self.session(): - y = py_utils.ApplyPadding( - tf.convert_to_tensor([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]), - tf.convert_to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]), - use_select=False).eval() - self.assertAllClose(y, [[1.0, 2.0], [0.0, 4.0], [5.0, 0.0]]) - - -class LengthsFromPaddingsTest(test_utils.TestCase): - - def testBasic(self): - with self.session(): - paddings = np.array([ - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 0.0, 0.0, 0.0, 1.0], - [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], - ]) - lengths = py_utils.LengthsFromPaddings( - tf.convert_to_tensor(paddings)).eval() - self.assertAllEqual([6, 3, 5, 0], lengths) - - def testZeroLength(self): - with self.session(): - paddings = np.zeros([4, 0]) - lengths = py_utils.LengthsFromPaddings( - tf.convert_to_tensor(paddings)).eval() - self.assertAllEqual([0, 0, 0, 0], lengths) - - def testBFloat16(self): - with self.session(): - actual_lengths = [1, 255, 256, 1024, 2048] - paddings = 1.0 - tf.sequence_mask( - actual_lengths, maxlen=actual_lengths[-1], dtype=tf.bfloat16) - lengths = py_utils.LengthsFromPaddings(paddings).eval() - self.assertAllEqual(actual_lengths, lengths) - - -class TrimTrailingPaddingsTest(test_utils.TestCase): - - def test2D(self): - with self.session(use_gpu=False, graph=tf.Graph()): - np.random.seed(123456) - x = np.random.normal(size=(3, 6)) - padding = np.array([ - [1.0, 1.0, 0.0, 0.0, 0.0, 1.0], - [1.0, 0.0, 0.0, 0.0, 1.0, 1.0], - [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], - ]) - trimmed_x, trimmed_padding = self.evaluate( - py_utils.TrimTrailingPaddings(x, tf.convert_to_tensor(padding))) - self.assertAllEqual(x[:, :5], trimmed_x) - self.assertAllEqual(padding[:, :5], trimmed_padding) - - def test2D_UnknownShape(self): - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - shape = tf.placeholder(tf.int32) - x = tf.random.normal(shape=shape, seed=123456) - padding = np.array([ - [1.0, 1.0, 0.0, 0.0, 0.0, 1.0], - [1.0, 0.0, 0.0, 0.0, 1.0, 1.0], - [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], - ]) - actual_x, (trimmed_x, trimmed_padding) = sess.run( - [x, - py_utils.TrimTrailingPaddings(x, tf.convert_to_tensor(padding))], - feed_dict={shape: [3, 6]}) - self.assertAllEqual(actual_x[:, :5], trimmed_x) - self.assertAllEqual(padding[:, :5], trimmed_padding) - - def test4D(self): - with self.session(use_gpu=False, graph=tf.Graph()): - np.random.seed(123456) - x = np.random.normal(size=(3, 6, 3, 3)) - padding = np.array([ - [1.0, 1.0, 0.0, 0.0, 0.0, 1.0], - [1.0, 0.0, 0.0, 0.0, 1.0, 1.0], - [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], - ]) - trimmed_x, trimmed_padding = self.evaluate( - py_utils.TrimTrailingPaddings(x, tf.convert_to_tensor(padding))) - self.assertAllEqual(x[:, :5], trimmed_x) - self.assertAllEqual(padding[:, :5], trimmed_padding) - - def testNoPadding(self): - with self.session(use_gpu=False, graph=tf.Graph()): - np.random.seed(123456) - x = np.random.normal(size=(3, 6)) - padding = np.array([ - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - ]) - trimmed_x, trimmed_padding = self.evaluate( - py_utils.TrimTrailingPaddings(x, tf.convert_to_tensor(padding))) - self.assertAllEqual(x, trimmed_x) - self.assertAllEqual(padding, trimmed_padding) - - def testLeadingPaddingOnly(self): - with self.session(use_gpu=False, graph=tf.Graph()): - np.random.seed(123456) - x = np.random.normal(size=(3, 6)) - padding = np.array([ - [1.0, 1.0, 0.0, 0.0, 0.0, 0.0], - [1.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - ]) - trimmed_x, trimmed_padding = self.evaluate( - py_utils.TrimTrailingPaddings(x, tf.convert_to_tensor(padding))) - self.assertAllEqual(x, trimmed_x) - self.assertAllEqual(padding, trimmed_padding) - - def testAllPadded(self): - with self.session(use_gpu=False, graph=tf.Graph()): - np.random.seed(123456) - x = np.random.normal(size=(3, 6)) - padding = np.array([ - [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], - ]) - trimmed_x, trimmed_padding = self.evaluate( - py_utils.TrimTrailingPaddings(x, tf.convert_to_tensor(padding))) - self.assertAllEqual([3, 1], trimmed_x.shape) - self.assertAllEqual(padding[:, :1], trimmed_padding) - - -class ReversePaddedSequenceTest(test_utils.TestCase): - - def testReversePaddedSequence(self): - with self.session(use_gpu=False): - # inputs is [seq_length, batch_size, input_dim] = [4, 3, 2] - # The length of each batch is [2, 3, 4] - inputs = tf.constant( - [[[1, 2], [3, 4], [5, 6]], [[11, 12], [13, 14], [15, 16]], - [[0, 0], [23, 24], [25, 26]], [[0, 0], [0, 0], [35, 36]]], - dtype=tf.float32) - paddings = tf.constant( - [[[0], [0], [0]], [[0], [0], [0]], [[1], [0], [0]], [[1], [1], [0]]], - dtype=tf.float32) - actual_output = py_utils.ReversePaddedSequence(inputs, paddings).eval() - expected_output = np.array([[[11, 12], [23, 24], [35, 36]], - [[1, 2], [13, 14], [25, 26]], - [[0, 0], [3, 4], [15, 16]], - [[0, 0], [0, 0], [5, 6]]]).astype('float32') - self.assertAllClose(expected_output, actual_output) - - -class ConcatenatePaddedSequencesTest(test_utils.TestCase): - - def _ComputeFloatOutputAndVerify(self, - input0, - input1, - seq_lens0, - seq_lens1, - tranpose_input=False): - with self.session(use_gpu=False): - expected_output_seq_lens = seq_lens0 + seq_lens1 - batch_size, input0_seq_dim = input0.shape - input1_seq_dim = input1.shape[1] - padding0 = 1.0 - tf.sequence_mask( - seq_lens0, maxlen=input0_seq_dim, dtype=tf.float32) - padding1 = 1.0 - tf.sequence_mask( - seq_lens1, maxlen=input1_seq_dim, dtype=tf.float32) - - if tranpose_input: - seq_dim = 0 - tf_input0 = tf.constant(np.transpose(input0)) - tf_input1 = tf.constant(np.transpose(input1)) - tf_padding0 = tf.transpose(padding0) - tf_padding1 = tf.transpose(padding1) - else: - seq_dim = 1 - tf_input0 = tf.constant(input0) - tf_input1 = tf.constant(input1) - tf_padding0 = padding0 - tf_padding1 = padding1 - - actual_outputs = self.evaluate( - py_utils.ConcatenatePaddedSequences( - tf_input0, - tf_input1, - padding0=tf_padding0, - padding1=tf_padding1, - seq_dim=seq_dim)) - - if tranpose_input: - actual_outputs = (np.transpose(actual_outputs[0]), - np.transpose(actual_outputs[1])) - - for batch in range(batch_size): - expected_output = np.concatenate((input0[batch, :seq_lens0[batch]], - input1[batch, :seq_lens1[batch]])) - self.assertAllClose( - expected_output, - actual_outputs[0][batch, :expected_output_seq_lens[batch]]) - expected_padding = np.ones( - (input0_seq_dim + input1_seq_dim,)).astype('float32') - expected_padding[:(seq_lens0[batch] + seq_lens1[batch])] = 0.0 - self.assertAllClose(expected_padding, actual_outputs[1][batch, :]) - - def testConcatenateFloatFeatures(self): - input0 = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]).astype('float32') - seq_lens0 = np.array([2, 3]) - input1 = np.array([[11, 12, 13, 14, 15, 16], [16, 17, 18, 19, 20, - 21]]).astype('float32') - seq_lens1 = np.array([4, 5]) - batch_size, input0_seq_dim = input0.shape - input1_seq_dim = input1.shape[1] - - no_padding_seq_lens0 = np.array([input0_seq_dim] * batch_size) - no_padding_seq_lens1 = np.array([input1_seq_dim] * batch_size) - - self._ComputeFloatOutputAndVerify(input0, input1, no_padding_seq_lens0, - no_padding_seq_lens1, False) - self._ComputeFloatOutputAndVerify(input0, input1, no_padding_seq_lens0, - no_padding_seq_lens1, True) - - self._ComputeFloatOutputAndVerify(input0, input1, seq_lens0, seq_lens1, - False) - self._ComputeFloatOutputAndVerify(input0, input1, seq_lens0, seq_lens1, - True) - - -class RetryTest(test_utils.TestCase): - - def testRetry(self): - max_retries = 5 - - @py_utils.Retry(max_retries=max_retries) - def Foo(state): - tf.logging.error('foo retried %s', state) - state['count'] += 1 - raise ValueError('test') - - try: - state = {'count': 0, 'msg': 'test'} - Foo(state) - except Exception as e: # pylint: disable=broad-except - tf.logging.error('%s', e) - - self.assertEqual(1 + max_retries, state['count']) - - -class MixByWeightTest(test_utils.TestCase): - - def testMixByWeight(self): - var_a = tf.get_variable('a', trainable=False, initializer=0) - var_b = tf.get_variable('b', trainable=False, initializer=0) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - - def _AddFn(var): - return lambda: tf.assign_add(var, 1) - - op, _ = py_utils.MixByWeight([_AddFn(var_a), _AddFn(var_b)], [0.7, 0.3], - seed=12345) - for _ in range(100): - self.evaluate(op) - a, b = self.evaluate([var_a, var_b]) - self.assertEqual(100, a + b) - self.assertGreater(a, 50) - self.assertLess(b, 50) - - def testMixByWeightWithDynamicWeights(self): - var_a = tf.get_variable('a', trainable=False, initializer=0) - var_b = tf.get_variable('b', trainable=False, initializer=0) - var_w = tf.get_variable('w', trainable=False, dtype=tf.float32, shape=[2]) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - - def _AddFn(var): - return lambda: tf.assign_add(var, 1) - - op, _ = py_utils.MixByWeight([_AddFn(var_a), _AddFn(var_b)], var_w) - - # all weight goes to 'a' - self.evaluate([tf.assign(var_w, [1.0, 0.0])]) - for _ in range(10): - self.evaluate(op) - a, b = self.evaluate([var_a, var_b]) - self.assertEqual(10, a) - self.assertEqual(0, b) - - # all weight goes to 'b' - self.evaluate([tf.assign(var_w, [0.0, 1.0])]) - for _ in range(10): - self.evaluate(op) - a, b = self.evaluate([var_a, var_b]) - self.assertEqual(10, a) - self.assertEqual(10, b) - - def testMixByWeightAndBpropType(self): - var_a = tf.get_variable('a', trainable=False, initializer=0) - var_b = tf.get_variable('b', trainable=False, initializer=0) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - - def _AddFn(var): - return lambda: tf.assign_add(var, 1) - - op, bprop = py_utils.MixByWeight( - [_AddFn(var_a), _AddFn(var_b)], [1.0, 0.0]) - for _ in range(10): - self.evaluate(op) - bprop_v, a, b = self.evaluate([bprop, var_a, var_b]) - self.assertEqual(10, a) - self.assertEqual(0, b) - self.assertAllClose(np.array([1, 0]), np.squeeze(bprop_v)) - - op, bprop = py_utils.MixByWeight( - [_AddFn(var_a), _AddFn(var_b)], [0.0, 1.0]) - for _ in range(10): - self.evaluate(op) - bprop_v, a, b = self.evaluate([bprop, var_a, var_b]) - self.assertEqual(10, a) - self.assertEqual(10, b) - self.assertAllClose(np.array([0, 1]), np.squeeze(bprop_v)) - - -class SequencesToDebugStrings(test_utils.TestCase): - - def testSequencesToDebugStrings(self): - with self.session(): - self.assertAllEqual([b'[1 2 3]', b'[100 200]'], - py_utils.SequencesToDebugStrings( - tf.constant([[1, 2, 3], [100, 200, 300]], - dtype=tf.int32), - tf.constant([3, 2], dtype=tf.int32)).eval()) - - -class StepSeedTest(test_utils.TestCase): - - def _testStepSeedHelper(self, sess, step_fn, expected_starting_step_seed): - state0 = py_utils.NestedMap( - input=tf.constant(0, dtype=tf.int64), - seed_pair=tf.zeros(2, dtype=tf.int64)) - inputs = py_utils.NestedMap(input=tf.range(10, dtype=tf.int64)) - - p = base_layer.BaseLayer.Params().Set(name='test') - accumulated_states, _ = recurrent.Recurrent(p.Instantiate().theta, state0, - inputs, step_fn) - - self.evaluate(tf.global_variables_initializer()) - accumulated_states = accumulated_states.Pack( - self.evaluate(accumulated_states.Flatten())) - self.assertAllEqual(np.arange(10), accumulated_states.input) - expected_step_seeds = expected_starting_step_seed + np.arange(10) - self.assertAllEqual( - np.stack((np.zeros(10), expected_step_seeds), axis=1), - accumulated_states.seed_pair) - - def testStepSeed(self): - p = base_layer.BaseLayer.Params() - - def RecurrentStep(theta, unused_state0, inputs): - state1 = py_utils.NestedMap() - state1.input = inputs.input - state1.seed_pair = py_utils.GenerateStepSeedPair(p, theta.global_step) - return state1, py_utils.NestedMap() - - with self.session(graph=tf.Graph()) as sess: - self._testStepSeedHelper(sess, RecurrentStep, 0) - # Second recurrent inside the same graph has different step_seeds. - self._testStepSeedHelper(sess, RecurrentStep, 111107348) - - # After a reset, the step_seeds are the same even with a slightly - # different RecurrentStep function. - def RecurrentStep2(theta, state0, inputs): - with tf.control_dependencies([tf.no_op()]): - return RecurrentStep(theta, state0, inputs) - - with self.session(graph=tf.Graph()) as sess: - self._testStepSeedHelper(sess, RecurrentStep2, 0) - self._testStepSeedHelper(sess, RecurrentStep2, 111107348) - - with self.session(graph=tf.Graph()) as sess: - # But a different name_scope changes it. - with tf.name_scope('test'): - self._testStepSeedHelper(sess, RecurrentStep2, 0) - self._testStepSeedHelper(sess, RecurrentStep2, 1553244033) - - -class WeightParamsTest(test_utils.TestCase): - - def testShapeModification(self): - """Tests that WeightParams.shape can be modified.""" - pc = py_utils.WeightParams([20, 30], - py_utils.WeightInit.UniformPositive(1.0), - tf.float32) - pc.shape = [10, 30] - var = py_utils.CreateVariable('var', pc) - self.assertEqual(var.shape, [10, 30]) - - -class WeightInitTest(test_utils.TestCase): - - def testModification(self): - """Tests that WeightInit cannot be modified.""" - w_init = py_utils.WeightInit.UniformPositive(1.0) - with self.assertRaisesRegex(TypeError, 'immutable'): - w_init.scale = 2.0 - - def testUniformPositive(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(12345678) - pc = py_utils.WeightParams([20, 30], - py_utils.WeightInit.UniformPositive(1.0), - tf.float32) - var = py_utils.CreateVariable('var', pc) - self.evaluate(tf.global_variables_initializer()) - var_v = var.eval() - self.assertTrue(np.all(var_v >= 0.0)) - self.assertTrue(np.all(var_v <= 1.0)) - - def testKaimingUniformRelu(self): - with self.session(use_gpu=False, graph=tf.Graph()): - pc = py_utils.WeightParams( - [2, 10, 30], py_utils.WeightInit.KaimingUniformFanInRelu(1.0), - tf.float32) - var = py_utils.CreateVariable('var', pc) - self.evaluate(tf.global_variables_initializer()) - var_v = var.eval() - # With Relu initialization, uniform bounds are - # sqrt(3) * sqrt(2) / sqrt(fan_in) - bound = np.sqrt(3.) * np.sqrt(2.) / np.sqrt(20) - self.assertTrue(np.all(var_v >= -bound)) - self.assertTrue(np.all(var_v <= bound)) - - def testKaimingUniformLeakyRelu(self): - with self.session(use_gpu=False, graph=tf.Graph()): - pc = py_utils.WeightParams( - [2, 10, 30], py_utils.WeightInit.KaimingUniformFanInLeakyRelu(), - tf.float32) - var = py_utils.CreateVariable('var', pc) - self.evaluate(tf.global_variables_initializer()) - var_v = var.eval() - # With LeakyRelu initialization, uniform bounds are - # sqrt(3) * sqrt(2 / (1 + scale**2)) / sqrt(fan_in) - # - # scale = sqrt(5) by default. - bound = np.sqrt(3.) * np.sqrt(2. / 6.) / np.sqrt(20) - self.assertTrue(np.all(var_v >= -bound)) - self.assertTrue(np.all(var_v <= bound)) - - -class RNNCellStateInitTest(test_utils.TestCase): - - def testZeros(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(12345678) - zero_state = py_utils.InitRNNCellState( - [2, 3], init=py_utils.RNNCellStateInit.Zeros(), dtype=tf.float32) - self.evaluate(tf.global_variables_initializer()) - zero_state_v = zero_state.eval() - expected_zero_state = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - self.assertAllClose(zero_state_v, expected_zero_state) - - def testRandomNormal(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(12345678) - zero_state = py_utils.InitRNNCellState( - [2, 3], - init=py_utils.RNNCellStateInit.RandomNormal(seed=12345), - dtype=tf.float32) - self.evaluate(tf.global_variables_initializer()) - zero_state_v = zero_state.eval() - expected_zero_state = [[1.621003, -1.097501, 0.493424], - [-1.048426, 2.73048, 0.091445]] - self.assertAllClose(zero_state_v, expected_zero_state) - - @flagsaver.flagsaver(stateless_vars_init=True) - def testRandomNormalStatelessVarsInit(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(12345678) - zero_state = py_utils.InitRNNCellState( - [2, 3], - name='RNNCellStateInit', - init=py_utils.RNNCellStateInit.RandomNormal(seed=12345), - dtype=tf.float32) - self.evaluate(tf.global_variables_initializer()) - zero_state_v = zero_state.eval() - expected_zero_state = [[-0.887855, 0.993745, -0.439152], - [0.312563, 0.923067, -1.952364]] - self.assertAllClose(zero_state_v, expected_zero_state) - zero_state_v_bis = zero_state.eval() - self.assertAllClose(zero_state_v_bis, expected_zero_state) - - def testRandomNormalInEval(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(12345678) - zero_state = py_utils.InitRNNCellState( - [2, 3], - init=py_utils.RNNCellStateInit.RandomNormal(seed=12345), - dtype=tf.float32, - is_eval=True) - self.evaluate(tf.global_variables_initializer()) - zero_state_v = zero_state.eval() - expected_zero_state = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - self.assertAllClose(zero_state_v, expected_zero_state) - - -class RematerializeFnTest(test_utils.TestCase): - - def testRandomNormal(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(12345678) - a = tf.random.normal([2, 3]) - b = tf.random.normal([3, 4]) - - def Fn(a, b): - c = tf.matmul(a, b) - d = tf.nn.sigmoid(c) - e = tf.nn.tanh(c) - return d, e - - d1, e1 = Fn(a, b) - d2, e2 = py_utils.RematerializeFn(Fn, a, b) - self.assertEqual(d2.shape.as_list(), [2, 4]) - self.assertEqual(e2.shape.as_list(), [2, 4]) - da1, db1 = tf.gradients([d1, e1], [a, b]) - da2, db2 = tf.gradients([d2, e2], [a, b]) - self.evaluate(tf.global_variables_initializer()) - v1, v2, v3, v4 = self.evaluate([da1, db1, da2, db2]) - self.assertAllEqual(v1, v3) - self.assertAllEqual(v2, v4) - - -def WrapFunction(use_tf_function, *dtypes): - """Wrap a python function as a Defun or tf.function.""" - if use_tf_function: - - def Decorated(fn): - - @tf.function( - input_signature=[tf.TensorSpec(shape=None, dtype=t) for t in dtypes]) - def Fn(*args): - return fn(*args) - - return Fn.get_concrete_function() - - return Decorated - else: - return tf.Defun(*dtypes) - - -def StatefulRandomOpsInDefunTestParameters(test_fn): - - def WrappedTestFn(self, use_tf_function): - # TODO(laigd): remove this check when 312743821 is in the release. - if use_tf_function and tf.compat.v1.__version__ < '2.3.0': - return - test_fn(self, use_tf_function) - - decorator = parameterized.named_parameters( - ('_defun', False), - ('_function', True), - ) - return decorator(WrappedTestFn) - - -class StatefulRandomOpsInDefunTest(test_utils.TestCase, parameterized.TestCase): - - @StatefulRandomOpsInDefunTestParameters - def testFunctionWithStatelessOp(self, use_tf_function): - - @WrapFunction(use_tf_function) - def FunctionWithStatelessOp(): - return tf.constant(42.0) - - self.assertAllEqual( - [], py_utils.StatefulRandomOpsInDefun(FunctionWithStatelessOp)) - - @StatefulRandomOpsInDefunTestParameters - def testFunctionWithStatefulOp(self, use_tf_function): - - @WrapFunction(use_tf_function) - def FunctionWithStatefulOp(): - return tf.random.uniform([100], maxval=10, dtype=tf.int32) - - self.assertAllEqual( - ['RandomUniformInt'], - py_utils.StatefulRandomOpsInDefun(FunctionWithStatefulOp)) - - @StatefulRandomOpsInDefunTestParameters - def testFunctionWithStatelessFunctionCall(self, use_tf_function): - - @WrapFunction(use_tf_function) - def FunctionWithStatelessOp(): - return tf.constant(42.0) - - @WrapFunction(use_tf_function) - def FunctionWithStatelessFunctionCall(): - return FunctionWithStatelessOp() - - self.assertAllEqual( - [], - py_utils.StatefulRandomOpsInDefun(FunctionWithStatelessFunctionCall)) - - @StatefulRandomOpsInDefunTestParameters - def testFunctionWithStatefulFunctionCall(self, use_tf_function): - - @WrapFunction(use_tf_function) - def FunctionWithStatefulOp(): - return tf.random.uniform([100], maxval=10, dtype=tf.int32) - - @WrapFunction(use_tf_function) - def FunctionWithStatefulFunctionCall(): - return FunctionWithStatefulOp() - - self.assertAllEqual( - ['RandomUniformInt'], - py_utils.StatefulRandomOpsInDefun(FunctionWithStatefulFunctionCall)) - - @StatefulRandomOpsInDefunTestParameters - def testFunctionWithStatefulFunctionalWhile(self, use_tf_function): - - @WrapFunction(use_tf_function) - def FunctionWithStatefulFunctionalWhile(): - - @WrapFunction(use_tf_function, tf.float32, tf.int32) - def Cond(result, i): - del result - return tf.less(i, 4) - - @WrapFunction(use_tf_function, tf.float32, tf.int32) - def Body(result, i): - return (result + tf.random.uniform(tf.shape(result)), i + 1) - - return functional_ops.While([tf.zeros([2, 2]), 0], cond=Cond, body=Body) - - self.assertAllEqual( - ['RandomUniform'], - py_utils.StatefulRandomOpsInDefun(FunctionWithStatefulFunctionalWhile)) - - @StatefulRandomOpsInDefunTestParameters - def testFunctionWithStatefulFunctionalIf(self, use_tf_function): - - @WrapFunction(use_tf_function) - def FunctionWithStatefulFunctionalIf(): - - @WrapFunction(use_tf_function, tf.float32) - def ThenFn(x): - return tf.abs(x) - - @WrapFunction(use_tf_function, tf.float32) - def ElseFn(x): - return tf.random.uniform(tf.shape(x)) - - return functional_ops.If( - tf.greater(tf.eye(2), 0.5), [tf.eye(2)], ThenFn, ElseFn) - - self.assertAllEqual( - ['RandomUniform'], - py_utils.StatefulRandomOpsInDefun(FunctionWithStatefulFunctionalIf)) - - @StatefulRandomOpsInDefunTestParameters - def testFunctionWithStatefulFunctionalFor(self, use_tf_function): - - @WrapFunction(use_tf_function) - def FunctionWithStatefulFunctionalFor(): - - @WrapFunction(use_tf_function, tf.float32) - def Body(result): - return [ - result + tf.random.uniform(tf.shape(result)) + - tf.random.poisson(shape=tf.shape(result), lam=[0.5, 1.5]) - ] - - return functional_ops.For( - start=0, limit=4, delta=1, inputs=[tf.eye(2)], body=Body) - - self.assertAllEqual(['RandomPoissonV2', 'RandomUniform'], - sorted( - py_utils.StatefulRandomOpsInDefun( - FunctionWithStatefulFunctionalFor))) - - @StatefulRandomOpsInDefunTestParameters - def testFunctionWithStatelessFunctionalFor(self, use_tf_function): - - @WrapFunction(use_tf_function) - def FunctionWithStatelessFunctionalFor(): - - @WrapFunction(use_tf_function, tf.float32) - def Body(result): - return [ - result + - tf.random.stateless_normal(tf.shape(result), seed=tf.stack([0, 1])) - ] - - return functional_ops.For( - start=0, limit=4, delta=1, inputs=[tf.eye(2)], body=Body) - - self.assertAllEqual( - [], - py_utils.StatefulRandomOpsInDefun(FunctionWithStatelessFunctionalFor)) - - -class RecordFormatTest(test_utils.TestCase): - - def testRecordFormatFromFilePattern(self): - record_format, path = py_utils.RecordFormatFromFilePattern( - 'tfrecord:/path/to/bar') - self.assertEqual(record_format, 'tfrecord') - self.assertEqual(path, '/path/to/bar') - - record_format, path = py_utils.RecordFormatFromFilePattern( - 'custom:/path/to/baz') - self.assertEqual(record_format, 'custom') - self.assertEqual(path, '/path/to/baz') - - -class ReadFileLinesTest(test_utils.TestCase): - - def testReadFileLines(self): - contents = [ - 'hello', - 'world', - 'foo', - 'bar', - ] - outpath = os.path.join(tf.test.get_temp_dir(), 'test.txt') - with tf.io.gfile.GFile(outpath, 'w') as f: - f.write('\n'.join(contents)) - - lines = [line.strip() for line in py_utils.ReadFileLines(outpath)] - self.assertAllEqual(lines, contents) - - def testReadFilesLinesFromPackage(self): - # py_utils.py is at lingvo/core relative to the working - # directory, so it will load from the lingvo package instead - lines = py_utils.ReadFileLines('core/py_utils.py') - self.assertIsNotNone(lines) - - def testReadFileLinesWithInvalidFile(self): - path = os.path.join(tf.test.get_temp_dir(), 'fake.txt') - - with self.assertRaises(tf.errors.NotFoundError): - py_utils.ReadFileLines(path) - - -class FocalLossTest(test_utils.TestCase): - - def _testNpFL(self, logits, labels, alpha, gamma): - self.assertEqual(logits.shape, labels.shape) - shape = labels.shape - logits = logits.reshape([-1]) - labels = labels.reshape([-1]) - - def _Sigmoid(x): - return 1.0 / (1.0 + np.exp(-x)) - - def _CrossEntropy(prob, label): - if label > 0: - return -np.log(prob) - else: - return -np.log(1 - prob) - - probabilities = _Sigmoid(logits) - ans = np.empty(probabilities.shape) - for i, (l, p) in enumerate(zip(labels, probabilities)): - ce = _CrossEntropy(p, l) - pt = (l * p) + ((1 - l) * (1 - p)) - if alpha is not None: - ce *= (l * alpha) + ((1 - l) * (1 - alpha)) - if gamma is not None: - ce *= np.power(1 - pt, gamma) - ans[i] = ce - return ans.reshape(shape) - - def _testTfFL(self, logits, labels, alpha, gamma): - g = tf.Graph() - with g.as_default(): - x = tf.placeholder(tf.float32) - y = tf.placeholder(tf.float32) - z = py_utils.SigmoidCrossEntropyFocalLoss(x, y, alpha, gamma) - with self.session(graph=g) as sess: - return sess.run(z, feed_dict={x: logits, y: labels}) - - def testSigmoidCrossEntropyFocalLoss(self): - logits = np.random.normal(scale=10, size=(2, 3, 5)) - labels = np.floor(np.random.uniform(size=(2, 3, 5)) + 0.2) - for (alpha, gamma) in [(None, None), (0.25, 2), (0.1, 0), (1, 5)]: - np_result = self._testNpFL(logits, labels, alpha, gamma) - tf_lingvo_result = self._testTfFL(logits, labels, alpha, gamma) - self.assertAllClose(np_result, tf_lingvo_result) - - def _testNpSCEFL(self, logits, labels, alpha, gamma): - probs = np.exp(logits - np.max(logits, axis=-1, keepdims=True)) - probs = probs / np.sum(probs, axis=-1, keepdims=True) - - shape = probs.shape[:-1] - probs = probs.reshape([-1, probs.shape[-1]]) - ans = np.empty(probs.shape[:-1]) - - if labels.shape != logits.shape: - # convert labels to class probabilities - label_probs = np.zeros(probs.shape) - label_probs[np.arange(labels.size), labels.reshape([-1])] = 1.0 - else: - label_probs = labels.reshape([-1, labels.shape[-1]]) - for i, (lp, p) in enumerate(zip(label_probs, probs)): - ce = lp * -np.log(p) - if alpha is not None: - ce *= alpha - if gamma is not None: - ce *= np.power(1 - p, gamma) - ans[i] = ce.sum() - ans = ans.reshape(shape) - return ans - - def _testTfSCEFLLabelIds(self, logits, labels, alpha, gamma): - g = tf.Graph() - with g.as_default(): - x = tf.placeholder(tf.float32) - y = tf.placeholder(tf.int32) - z = py_utils.SoftmaxCrossEntropyFocalLoss( - x, label_ids=y, alpha=alpha, gamma=gamma) - with self.session(graph=g) as sess: - return sess.run(z, feed_dict={x: logits, y: labels}) - - def _testTfSCEFLLabelProbs(self, logits, labels, alpha, gamma): - g = tf.Graph() - with g.as_default(): - x = tf.placeholder(tf.float32) - y = tf.placeholder(tf.float32) - z = py_utils.SoftmaxCrossEntropyFocalLoss( - x, label_probs=y, alpha=alpha, gamma=gamma) - with self.session(graph=g) as sess: - return sess.run(z, feed_dict={x: logits, y: labels}) - - def testSoftmaxCrossEntropyFocalLoss(self): - num_classes = 7 - logits = np.random.normal(scale=10, size=(2, 3, 5, num_classes)) - label_ids = np.random.randint(num_classes, size=(2, 3, 5)) - label_probs = np.random.uniform(size=(2, 3, 5, num_classes)) - label_probs /= label_probs.sum(axis=-1, keepdims=True) - for (alpha, gamma) in [ - (None, None), - (np.random.uniform(size=[num_classes]).astype(np.float32), 2), - (np.random.uniform(size=[num_classes]).astype(np.float32), 0), - (np.random.uniform(size=[num_classes]).astype(np.float32), 5) - ]: - self.assertAllClose( - self._testNpSCEFL(logits, label_ids, alpha, gamma), - self._testTfSCEFLLabelIds(logits, label_ids, alpha, gamma)) - self.assertAllClose( - self._testNpSCEFL(logits, label_probs, alpha, gamma), - self._testTfSCEFLLabelProbs(logits, label_probs, alpha, gamma)) - - -class UniformSamplerTest(test_utils.TestCase): - - def testUniformSamplerSamples(self): - sampler = py_utils.UniformSampler(5) - for i in range(5): - sampler.Add(i) - # Up to the total number of samples, no sampling is performed. - self.assertEqual([0, 1, 2, 3, 4], sampler.samples) - - def testUniformSampler(self): - # Run a bunch of trials sampling 10 items out of 100 ids. - np.random.seed(123456) - state_space = 100 - num_samples = 10 - num_trials = 10000 - counts = np.zeros([state_space]) - for _ in range(num_trials): - sampler = py_utils.UniformSampler(num_samples) - # Add an element for each item in the state space. - for i in range(state_space): - sampler.Add(i) - samples = sampler.samples - self.assertEqual(num_samples, len(samples)) - for value in samples: - counts[value] += 1 - distribution = counts / np.sum(counts) - - # We expect that over the course of many trials, each item in - # the state space gets selected roughly an equal number of times, - # implying that the reservoir sampler is not biased based on the order - # in which items were added to the sampler. - self.assertGreater(min(distribution), 0.009) - self.assertLess(max(distribution), 0.011) - - -class FromGlobalTest(test_utils.TestCase): - - def testAccessAssertFlagWhenUnparsed(self): - tf.flags.FLAGS.unparse_flags() - # Accessing the flag value directly fails. - with self.assertRaises(tf.flags._exceptions.UnparsedFlagAccessError): - result = FLAGS.enable_asserts - result = py_utils._FromGlobal('enable_asserts') - # Default value of this flag is True. - self.assertTrue(result) - # Reparse args. - tf.flags.FLAGS(sys.argv) - - -def CallDefunTestParameters(test_fn): - - def WrappedTestFn(self, use_tf_function, bak_as_function): - # TODO(laigd): remove this check when 312743821 is in the release. - if use_tf_function and tf.compat.v1.__version__ < '2.3.0': - return - with flagsaver.flagsaver(call_defun_use_tf_function=use_tf_function): - test_fn(self, bak_as_function) - - decorator = parameterized.named_parameters( - ('_defun', False, False), - ('_defun_bakasfunction', False, True), - ('_function', True, False), - ('_function_bakasfunction', True, True), - ) - return decorator(WrappedTestFn) - - -class CallDefunTest(test_utils.TestCase, parameterized.TestCase): - - @CallDefunTestParameters - def testSimple(self, bak_as_function): - with self.session(): - - def Bak(xs, ys, dys): - del ys - w, x = xs - return (tf.matmul(dys, tf.transpose(x)) + 100., - tf.matmul(tf.transpose(w), dys) + 200.) - - def Fwd(args): - w, x = args - return tf.matmul(w, x) - - a = np.array([[1.0, 2.0], [0.0, -3.0]]) - b = np.array([[2.0, 0.0], [1.0, 1.0]]) - xs = [tf.constant(a), tf.constant(b)] - ys = py_utils.CallDefun(Fwd, xs, bak=Bak, bak_as_function=bak_as_function) - loss = tf.reduce_sum(tf.square(ys)) - dw, dx, dy = tf.gradients(xs=xs + [ys], ys=loss) - y, dw, dx, dy = self.evaluate([ys, dw, dx, dy]) - self.assertAllEqual(y, a.dot(b)) - self.assertAllEqual(dy, 2 * y) - self.assertAllEqual(dw, (2 * y).dot(b.T) + 100) - self.assertAllEqual(dx, a.T.dot(2 * y) + 200) - - @CallDefunTestParameters - def testPreserveStaticShape(self, bak_as_function): - with self.session(): - - def Bak(x, y, dy): - del x, y - return dy - - def Fwd(args): - x = args - shape = py_utils.GetShape(x) - if isinstance(shape, tf.Tensor): - return tf.ones_like(x) - else: - for dim in shape: - if isinstance(dim, tf.Tensor): - return tf.ones_like(x) + 1 - return tf.zeros_like(x) - - a = np.array([[1.0, 2.0], [0.0, -3.0]]) - x = tf.constant(a) - y = self.evaluate( - py_utils.CallDefun(Fwd, x, bak=Bak, bak_as_function=bak_as_function)) - self.assertAllEqual(y, np.zeros_like(a)) - - @CallDefunTestParameters - def testNestedMap(self, bak_as_function): - with self.session(): - - def Bak(xs, ys, dys): - del ys - return py_utils.NestedMap( - w=tf.matmul(dys.y, tf.transpose(xs.x)) + 100., - x=tf.matmul(tf.transpose(xs.w), dys.y) + 200.) - - def Fwd(xs): - return py_utils.NestedMap(y=tf.matmul(xs.w, xs.x)) - - a = np.array([[1.0, 2.0], [0.0, -3.0]]) - b = np.array([[2.0, 0.0], [1.0, 1.0]]) - xs = py_utils.NestedMap(w=tf.constant(a), x=tf.constant(b)) - ys = py_utils.CallDefun(Fwd, xs, bak=Bak, bak_as_function=bak_as_function) - loss = tf.reduce_sum(tf.square(ys.y)) - dw, dx, dy = tf.gradients(xs=xs.Flatten() + ys.Flatten(), ys=loss) - y, dw, dx, dy = self.evaluate([ys.y, dw, dx, dy]) - self.assertAllEqual(y, a.dot(b)) - self.assertAllEqual(dy, 2 * y) - self.assertAllEqual(dw, (2 * y).dot(b.T) + 100) - self.assertAllEqual(dx, a.T.dot(2 * y) + 200) - - @CallDefunTestParameters - def testImplicitInput(self, bak_as_function): - with self.session() as sess: - w = tf.placeholder(tf.float32) - - def Fwd(xs): - ret = py_utils.NestedMap(y=tf.matmul(w, xs.x)) - assert py_utils.GetExtraArgs() - return ret - - def Bak(xs, ys, dys): - del ys - dw = tf.matmul(dys.y, tf.transpose(xs.x)) + 100. - ret = py_utils.NestedMap(x=tf.matmul(tf.transpose(w), dys.y) + 200.) - if bak_as_function: - assert py_utils.GetExtraArgs() - return ret, dw - - a = np.array([[1.0, 2.0], [0.0, -3.0]]) - b = np.array([[2.0, 0.0], [1.0, 1.0]]) - xs = py_utils.NestedMap(x=tf.constant(b, dtype=tf.float32)) - ys = py_utils.CallDefun(Fwd, xs, bak=Bak, bak_as_function=bak_as_function) - loss = tf.reduce_sum(tf.square(ys.y)) - dw, dx, dy = tf.gradients(xs=[w] + xs.Flatten() + ys.Flatten(), ys=loss) - y, dw, dx, dy = sess.run([ys.y, dw, dx, dy], feed_dict={w: a}) - self.assertAllEqual(y, a.dot(b)) - self.assertAllEqual(dy, 2 * y) - self.assertAllEqual(dw, (2 * y).dot(b.T) + 100) - self.assertAllEqual(dx, a.T.dot(2 * y) + 200) - - -def IfTestParameters(test_fn): - - def WrappedTestFn(self, use_tf_function): - # TODO(laigd): remove this check when 313682500 is in the release. - if use_tf_function and tf.compat.v1.__version__ < '2.3.0': - return - with flagsaver.flagsaver(if_use_tf_function=use_tf_function): - test_fn(self) - - decorator = parameterized.named_parameters( - ('_defun', False), - ('_tf_function', True), - ) - return decorator(WrappedTestFn) - - -class IfTest(test_utils.TestCase, parameterized.TestCase): - - @IfTestParameters - def testNestedMapInput(self): - g = tf.Graph() - with g.as_default(): - - def ThenBody(nmap): - nmap.value -= 1. - return nmap - - def ElseBody(nmap): - nmap.value += 1. - return nmap - - inputs = py_utils.NestedMap(value=tf.constant(0.)) - true_out = py_utils.If(True, inputs, ThenBody, ElseBody) - false_out = py_utils.If(False, inputs, ThenBody, ElseBody) - - with self.session(graph=g): - true_out = self.evaluate(true_out) - false_out = self.evaluate(false_out) - - self.assertEqual(-1., true_out.value) - self.assertEqual(1., false_out.value) - - @IfTestParameters - def testScalarInput(self): - g = tf.Graph() - with g.as_default(): - - def ThenBody(value): - return value - 1. - - def ElseBody(value): - return value + 1. - - inputs = tf.constant(0.) - true_out = py_utils.If(True, inputs, ThenBody, ElseBody) - false_out = py_utils.If(False, inputs, ThenBody, ElseBody) - - with self.session(graph=g): - true_out = self.evaluate(true_out) - false_out = self.evaluate(false_out) - - self.assertEqual(-1., true_out) - self.assertEqual(1., false_out) - - @IfTestParameters - def testListInput(self): - g = tf.Graph() - with g.as_default(): - - def ThenBody(values): - return values[0] - 1., values[1] + 1. - - def ElseBody(values): - return values[0] + 1., values[1] - 1. - - inputs = [tf.constant(0.), tf.constant(0.)] - true_out = py_utils.If(True, inputs, ThenBody, ElseBody) - false_out = py_utils.If(False, inputs, ThenBody, ElseBody) - - with self.session(graph=g): - true_out = self.evaluate(true_out) - false_out = self.evaluate(false_out) - - self.assertEqual((-1., 1.), true_out) - self.assertEqual((1., -1.), false_out) - - -class ForLoopTest(test_utils.TestCase): - - def testSimple(self): - g = tf.Graph() - with g.as_default(): - - # Basel problem. \sum_1 1/i^2 = pi ^ 2 / 6. A slow convergent series. - def Body(i, state): - state.value = state.value + 1.0 / tf.square(tf.cast(i, tf.float32)) - return state - - state = py_utils.NestedMap(value=tf.constant(0.)) - state = py_utils.ForLoop(Body, 1, 10000, 1, state) - - with self.session(graph=g): - value = self.evaluate(state.value) - - self.assertAllClose(np.pi * np.pi / 6, value, rtol=1e-3) - - -class TopKTest(test_utils.TestCase): - - def test_top_2(self): - with self.session(): - x_in = tf.random.normal([4, 5, 6, 8]) - top2_value_a, top2_index_a = py_utils.TopK(x_in, 2) - top2_value_b, top2_index_b = tf.math.top_k(x_in, 2) - v1, v2 = self.evaluate([top2_value_a, top2_value_b]) - v3, v4 = self.evaluate([top2_index_a, top2_index_b]) - self.assertAllEqual(v1, v2) - self.assertAllEqual(v3, v4) - - def test_top_1(self): - with self.session(): - x_in = tf.random.normal([4, 5, 6, 8]) - top1_value_a, top1_index_a = py_utils.TopK(x_in, 1) - top1_value_b, top1_index_b = tf.math.top_k(x_in, 1) - v1, v2 = self.evaluate([top1_value_a, top1_value_b]) - v3, v4 = self.evaluate([top1_index_a, top1_index_b]) - self.assertAllEqual(v1, v2) - self.assertAllEqual(v3, v4) - - -class TpuSummaryTensorsTest(test_utils.TestCase): - - def testTpuSummaryTensors(self): - with self.session(): - with tf.name_scope('fprop'): - with tf.name_scope('tower_0_0'): - with tf.name_scope('fprop'): - with tf.name_scope('my_model'): - with tf.name_scope('layer_001'): - with tf.name_scope('fprop'): - x = tf.constant(0., name='inputs') - py_utils.AddTpuSummaryTensor('mean_x', tf.reduce_mean(x)) - with tf.name_scope('layer_002'): - with tf.name_scope('fprop'): - x = tf.identity(x, name='inputs') - py_utils.AddTpuSummaryTensor('mean_x', tf.reduce_mean(x)) - tpu_summary_tensors = py_utils.GetTpuSummaryTensors() - actual_value = self.evaluate([tpu_summary_tensors]) - expected_value = [{ - 'mean_x/fprop/tower_0_0/fprop/my_model/layer_001/fprop': (0., 1.), - 'mean_x/fprop/tower_0_0/fprop/my_model/layer_002/fprop': (0., 1.), - }] - self.assertAllEqual(expected_value, actual_value) - - -class HasShapeTest(test_utils.TestCase): - - def testFullyDynamicShapesMatchesOk(self): - x_pl = tf.placeholder(tf.float32) - y_pl = tf.placeholder(tf.float32) - x = py_utils.HasShape(x_pl, py_utils.GetShape(y_pl)) - with self.session() as sess: - sess.run( - x, - feed_dict={ - x_pl: np.random.rand(1, 2, 3), - y_pl: np.random.rand(1, 2, 3), - }) - - def testFullyDynamicShapesMismatchRaisesError(self): - x_pl = tf.placeholder(tf.float32) - y_pl = tf.placeholder(tf.float32) - x = py_utils.HasShape(x_pl, py_utils.GetShape(y_pl)) - with self.session() as sess: - with self.assertRaisesRegexp(tf.errors.InvalidArgumentError, - r'.*mismatch shape:.*'): - sess.run( - x, - feed_dict={ - x_pl: np.random.rand(1, 2, 3), - y_pl: np.random.rand(4, 5, 6), - }) - - def testFullyDynamicRankMismatchRaisesError(self): - x_pl = tf.placeholder(tf.float32) - y_pl = tf.placeholder(tf.float32) - x = py_utils.HasShape(x_pl, py_utils.GetShape(y_pl)) - with self.session() as sess: - with self.assertRaisesRegexp(tf.errors.InvalidArgumentError, - r'.*mismatch shape:.*'): - sess.run( - x, - feed_dict={ - x_pl: np.random.rand(1, 2), - y_pl: np.random.rand(4, 5, 6), - }) - - def testFullyConstantShapesMatchesOk(self): - x_pl = tf.placeholder(tf.float32) - x = py_utils.HasShape(x_pl, tf.constant([1, 2, -1])) - with self.session() as sess: - sess.run( - x, feed_dict={ - x_pl: np.random.rand(1, 2, 3), - }) - - def testFullyConstantShapesMismatchRaisesError(self): - x_pl = tf.placeholder(tf.float32) - x = py_utils.HasShape(x_pl, tf.constant([1, 2, -1])) - with self.session() as sess: - with self.assertRaisesRegexp(tf.errors.InvalidArgumentError, - r'.*mismatch shape:.*'): - sess.run( - x, feed_dict={ - x_pl: np.random.rand(2, 2, 3), - }) - - def testRankMismatchRaisesError(self): - with self.assertRaisesRegexp( - ValueError, r'Tensor does not match rank of expected shape.*'): - py_utils.HasShape(tf.random.uniform((1, 2, 3)), [1, 2]) - - def testTensorRankLessThanNDimsRaisesError(self): - with self.assertRaisesRegexp(ValueError, - r'Tensor has fewer dimensions than ndims.*'): - py_utils.HasShape(tf.random.uniform((1, 2, 3)), [1, 2, 3, 4], ndims=4) - - def testExpectedShapeRankLessThanNDimsRaisesError(self): - with self.assertRaisesRegexp( - ValueError, - r'Expected shape must have number of dimensions equal to ndims.*'): - py_utils.HasShape(tf.random.uniform((1, 2, 3, 4)), [1, 2, 3], ndims=4) - - def testTensorStaticShapeMismatchRaisesError(self): - x_pl = tf.placeholder(tf.float32, (None, 2, 3)) - y_pl = tf.placeholder(tf.float32, (3, 1, None)) - with self.assertRaisesRegexp( - ValueError, r'Tensor does not match expected shape on dimension 1.*'): - py_utils.HasShape(x_pl, py_utils.GetShape(y_pl)) - - def testTensorShapeMatchesOk(self): - x_pl = tf.placeholder(tf.float32, (None, 2, 3, None)) - y_pl = tf.placeholder(tf.float32, (3, 2, None, None)) - x = py_utils.HasShape(x_pl, py_utils.GetShape(y_pl)) - with self.session() as sess: - sess.run( - x, - feed_dict={ - x_pl: np.random.rand(3, 2, 3, 4), - y_pl: np.random.rand(3, 2, 3, 4), - }) - - def testTensorShapeMatchesWithMinus1Ok(self): - x_pl = tf.placeholder(tf.float32, (None, 2, 3, None)) - x = py_utils.HasShape(x_pl, [-1, -1, 3, -1]) - with self.session() as sess: - sess.run( - x, feed_dict={ - x_pl: np.random.rand(3, 2, 3, 4), - }) - - def testTensorShapeWithMinus1MismatchRaises(self): - x_pl = tf.placeholder(tf.float32, (None, 2, 3, None)) - x = py_utils.HasShape(x_pl, [-1, -1, 3, 5]) - with self.session() as sess: - with self.assertRaisesRegexp(tf.errors.InvalidArgumentError, - r'.*mismatch shape:.*'): - sess.run( - x, feed_dict={ - x_pl: np.random.rand(3, 2, 3, 4), - }) - - def testTensorShapeMatchesWithTensorExpectedShape(self): - x_pl = tf.placeholder(tf.float32, (None, 2, 3, None)) - x = py_utils.HasShape(x_pl, tf.constant([-1, -1, 3, -1])) - with self.session() as sess: - sess.run( - x, feed_dict={ - x_pl: np.random.rand(3, 2, 3, 4), - }) - - def testTensorShapeMismatchWithTensorExpectedShapeRaises(self): - x_pl = tf.placeholder(tf.float32, (None, 2, 3, None)) - x = py_utils.HasShape(x_pl, [-1, tf.constant(-1), 3, tf.constant(5)]) - with self.session() as sess: - with self.assertRaisesRegexp(tf.errors.InvalidArgumentError, - r'.*mismatch shape:.*'): - sess.run( - x, feed_dict={ - x_pl: np.random.rand(3, 2, 3, 4), - }) - - def testTensorStaticShapeMatchDynamicMismatchRaises(self): - x_pl = tf.placeholder(tf.float32, (None, None, 2, 3, None)) - y_pl = tf.placeholder(tf.float32, (None, 3, 2, None, None)) - x = py_utils.HasShape(x_pl, py_utils.GetShape(y_pl)) - with self.session() as sess: - with self.assertRaisesRegexp(tf.errors.InvalidArgumentError, - r'.*mismatch shape:.*'): - sess.run( - x, - feed_dict={ - x_pl: np.random.rand(1, 3, 2, 3, 4), - y_pl: np.random.rand(2, 3, 2, 5, 4), - }) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/quant_utils.py b/lingvo/core/quant_utils.py deleted file mode 100644 index 428fef70..00000000 --- a/lingvo/core/quant_utils.py +++ /dev/null @@ -1,1186 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Utilities for model quantization.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import hyperparams -from lingvo.core import py_utils -from lingvo.core import summary_utils -import numpy as np - - -class QuantizableLayer(base_layer.BaseLayer): - """A layer that supports various forms of quantization. - - It is always safe to extend QuantizableLayer instead of BaseLayer (i.e. at - the base of layer inheritance hierarchies) if any layer in the hierarchy - may be quantized. Unless if configured/used, all quantization behavior - is disabled by default. - - Most quantization strategies employed at training time fall into the - "fake quantization" category, where we add various constraints in the - forward propagation to quantify and simulate the effect of quantization. - Within that, we have two major approaches: - - - Active clipping: Usually via a schedule, tensors are actively - clipped to fall into ranges that we know apriori that the model should - be able to deal with. - - Passive tracking and simulation: Passively track the min/max ranges - of tensors and insert special ops at training and eval time that - constrain to those ranges. - - The tensors of interest for both approaches are top-level inputs (or - embeddings), outputs of arithmetic operations (add, mul, tanh, etc) and - weights. While the actual process of quantizing can be quite complex and - involve an end to end view of the system, from a modeling perspective, it - can be thought of as providing tags/decorators to arithmetic inputs/outputs. - It would be appropriate to think of these as casts which alter the way that - the arithmetic operation is tracked and quantize (if Python/Tensorflow were - a more strongly typed environment, they would indeed represent types in the - type system but given the loose typing, it is just an honor system). - - The "decorators" are: - - - QWeight: Tags a tensor (typically a var) as a weight quantized type. - - QR* (QRTanh, QRSigmoid, QRSoftmax, etc): Tags a tensor as the result - of a fixed activation function with a known output range (the range - is implied in the name). - - QRPadding: Tags a tensor as containing a padding value (as we define - them as 0..1). While such values are numeric, they generally exist with - very different ranges from the rest of the graph and should not be - arithmetically combined with tensors that may have a different/variable - range. - - QTensor: Tags a tensor as a generic quantized intermediate value. - These are also tagged with a layer-unique name. All QTensors with the - same name will be considered the same from a numerical range/precision - perspective. - - Tagging things in this way allows us to, via hyperparameters, associate - one or more quantization domains (QDomain) with the layer that will - actually perform the necessary tracking and transformations needed at - training and inference time to ensure that the layer can operate in low - bit inference engines that only have quantized numeric representations. - See the SampleQuantizedProjectionLayer in the unit test for an example layer - that has had these tags applied. - - As a note on terminology, domain/QDomain here refers to a distinct set of - quantization rules to apply to a subset of tensors. Most layers will only - have one QDomain (default). The concept exists for layers which have been - specially created to operate in more than one quantized precision (i.e. an - RNN cell that uses 8bit quantization for inputs/outputs and 16bit - quantization for internal state arithmetic). Such uses should be rare. - - - **Convenience functions:** - - The layer adds a number of convenience functions to the layer's 'fns' - function library. These mirror similarly named functions in TensorFlow but - automatically add the necessary annotations. All such functions take the - following named parameters: - - - qt: Name of QTensor (setup with TrackQTensor) for dynamic range tracking. - - qmin/qmax/qdomain: Constant min/max range plus optional QDomain name to - resolve against. Typically, only qmin/qmax are used. - - Functions that have a natural output range will have default values for - qmin/qmax so that they just work. Functions that do not have a natural - output range must have either qt or qmin/qmax specified manually. - - Natural range functions - - - qtanh - - qsigmoid - - qsoftmax - - Dynamic range functions: - - - qadd - - qmultiply - - qmatmul (defers to `.py_utils.Matmul` and only accepts rank-2 tensors) - - qbatchmatmul (defers to `tf.matmul` directly) - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('qdomain', hyperparams.Params(), - 'Container for quantization domains.') - p.qdomain.Define('default', None, 'Default quantization domain.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - self._tracked_tensors = dict() # tracked t_name -> (QDomain) - self._qstate = None # t_name -> Tensor - - # Instantiate quantization domains. - self._qdomains = dict() # Dict of qdname -> QDomain or None - for qdname in dir(p.qdomain): - qdparams = p.qdomain.Get(qdname) - if qdparams is None: - continue - assert issubclass( - qdparams.cls, - QDomain), ('Expected quantized domain %s to extend QDomain' % qdname) - qdchild_name = 'qdomain_' + qdname - self.CreateChild(qdchild_name, qdparams) - self._qdomains[qdname] = self.children[qdchild_name] - self._AddQuantizationFunctions() - - def _CreateChildrenVariables(self): - # Backwards compatibility: child.InstantiateVariables() in custom scope. - p = self.params - with tf.variable_scope(p.name + '/q'): - for qdomain in self._qdomains.values(): - qdomain.InstantiateVariables() - super()._CreateChildrenVariables() - - def QRTanh(self, t, domain='actf'): - """Quantizes the output of a tanh (-1.0, 1.0).""" - qd = self.GetQDomain(domain) - return qd.QuantizeNaturalRange(t, -1.0, 1.0) if qd else t - - def QRSigmoid(self, t, domain='actf'): - """Quantizes the output of a sigmoid (0, 1.0).""" - qd = self.GetQDomain(domain) - return qd.QuantizeNaturalRange(t, 0.0, 1.0) if qd else t - - def QRSoftmax(self, t, domain='softmax', narrow_to_asym_bit_depth=False): - """Quantizes the output of a softmax (0, 1.0 - 1.0/2^-bits).""" - qd = self.GetQDomain(domain) - # Override based on TFLite softmax support. - softmax_max = 1.0 - if qd is not None and narrow_to_asym_bit_depth: - softmax_max = (2**qd.bits - 1) / (2**qd.bits) - return qd.QuantizeNaturalRange(t, 0.0, softmax_max) if qd else t - - def QRRelu(self, t, domain='relu'): - """Quantizes the output of a relu (0, 1.0).""" - qd = self.GetQDomain(domain) - return qd.QuantizeNaturalRange(t, 0.0, 1.0) if qd else t - - def QRRelu6(self, t, domain='relu6'): - """Quantizes the output of a relu6 (0, 6.0).""" - qd = self.GetQDomain(domain) - return qd.QuantizeNaturalRange(t, 0.0, 6.0) if qd else t - - def QRPadding(self, t, domain='padding'): - """Quantizes the padding.""" - qd = self.GetQDomain(domain) - return qd.QuantizeConstantRange(t, 0.0, 1.0) if qd else t - - def TrackQTensor(self, *t_names, **kwargs): - r"""Creates one or more QTensors for later use. - - Any tensor that will later be quantized must be created first, preferably - in _CreateLayerVariables(). - - Along with a list of tensor names to create, they can be associated with - a 'domain'. Most layers are simple enough to only have a single quantization - domain (QDomain), typically 'default'. However, additional QDomains can - be defined as parameters to control fine grained aspects of quantization. - - If no explicit domain is passed, then the domain ('tensor\_' + t_name) is - tried. If that is not defined, then 'default'. - - Args: - *t_names: Positional parameters are taken to be QTensor names to create. - **kwargs: Can contain an explicit 'domain'. Written this way due to - python2 limitations. - """ - domain_override = kwargs['domain'] if 'domain' in kwargs else None - for t_name in t_names: - domain = domain_override - if domain is None: - domain = 'tensor_' + t_name - qd = self.GetQDomain(domain) - self._tracked_tensors[t_name] = qd - if qd: - qd.CreateTensor(t_name) - - def QTensor(self, t_name, t, eval_only=False): - """Quantizes a general tensor input/output in one step. - - t_name must have been previously created via TrackQTensor. - - Args: - t_name: Previously created QTensor t_name to quantize to. - t: Tensor to quantize. - eval_only: Whether to only apply quantization pressure at eval time. - Returns: - The tensor, quantized. - """ - return self.QTensorMulti(t_name, t, eval_only=eval_only)[0] - - def QTensorMulti(self, t_name, *ts, **kwargs): - """Quantizes multiple tensors simultaneously. - - t_name must have been previously created via TrackQTensor. - - This is different from multiple calls to QTensor because each of the - tensors will contribute to the min/max of the same constraint. - Typically used for tensors that are being added together. - - Args: - t_name: Previously created QTensor t_name to quantize to. - *ts: Tensor to quantize. - **kwargs: Additional kwargs as per QTensor. - Returns: - Tuple of quantized tensors. - """ - assert t_name in self._tracked_tensors, ( - ('Call to QTensor without first calling TrackQTensor: %s ' - '(all known = %r)') % (t_name, list(self._tracked_tensors.keys()))) - eval_only = kwargs['eval_only'] if 'eval_only' in kwargs else False - qd = self._tracked_tensors[t_name] - if not qd: - return ts - return qd.QuantizeTensors(t_name, ts, eval_only=eval_only) - - def GetQTensorRange(self, t_name, ts): - """Returns the range for a quantized tensor. - - t_name must have been previously create via TrackQTensor and t should be - previously quantized. - - Args: - t_name: Preivously created QTensor t_name to fetch range from. - t: Tensor to retrieve range from. - - Returns: - The (min, max) range of the quantized tensor. - """ - qd = self._tracked_tensors[t_name] - return qd.GetTensorRange(t_name, ts) - - def QWeight(self, w, domain='weight'): - """Quantizes a weight. - - Args: - w: The weight tensor. - domain: Custom domain to match (defaults to 'weight' or 'default'). - Returns: - The weights quantized. - """ - qd = self.GetQDomain(domain) - return qd.QuantizeWeight(w) if qd else w - - def GetQDomain(self, domain): - """Gets the QDomain matching a given domain name. - - Args: - domain: User specified domain name. - Returns: - The requested QDomain, the 'default' QDomain or None. - """ - qd = self._qdomains.get(domain) - if qd: - return qd - qd = self._qdomains.get('default') - return qd - - def _AddQuantizationFunctions(self): - """Adds standard quantization functions against the given layer.""" - - def WrapOp(fnname, f, default_qmin=None, default_qmax=None): - """Adds a wrapper op to the layer's fns.""" - - def Wrapped(*args, **kwargs): - """Wraps a native op.""" - # Validate and pop args 'qt', 'qmin', 'qmax' and 'qdomain'. - qt = kwargs.get('qt') - if qt is not None: - del kwargs['qt'] - qmin = kwargs.get('qmin') - if qmin is not None: - del kwargs['qmin'] - qmax = kwargs.get('qmax') - if qmax is not None: - del kwargs['qmax'] - qdomain = kwargs.get('qdomain') - if qdomain is not None: - del kwargs['qdomain'] - narrow_to_asym_bit_depth = kwargs.get('narrow_to_asym_bit_depth') - if narrow_to_asym_bit_depth is not None: - del kwargs['narrow_to_asym_bit_depth'] - if qmin is None: - qmin = default_qmin - if qmax is None: - qmax = default_qmax - assert qt is not None or (qmin is not None and qmax is not None), ( - ('Quantized function "%s" requires either qt (QTensor name) or ' - 'qmin/qmax to be set.') % fnname) - - # Provide a better default name if none provided. - if 'name' not in kwargs and qt is not None: - kwargs['name'] = '%s_%s' % (fnname, qt) - - # Invoke original. - y = f(*args, **kwargs) - - # Handle the output. - if qt is not None: - y = self.QTensor(qt, y) - else: - qd = self.GetQDomain(qdomain) - if qd: - if narrow_to_asym_bit_depth: - qrange = qmax - qmin - qmax = qmin + qrange * (2**qd.bits - 1) / (2**qd.bits) - y = qd.QuantizeNaturalRange(y, qmin, qmax) - return y - - self.AddFunction(fnname, Wrapped) - - # Supported quantized functions. - WrapOp('qadd', tf.add) - WrapOp('qsubtract', tf.subtract) - WrapOp('qmultiply', tf.multiply) - WrapOp('qmatmul', py_utils.Matmul) - WrapOp('qbatchmatmul', tf.matmul) - WrapOp('qconv1d', tf.nn.conv1d) - WrapOp('qtanh', tf.tanh, default_qmin=-1.0, default_qmax=1.0) - WrapOp('qsigmoid', tf.sigmoid, default_qmin=0.0, default_qmax=1.0) - WrapOp('qsoftmax', tf.nn.softmax, default_qmin=0.0, default_qmax=1.0) - WrapOp('qlogsoftmax', tf.nn.log_softmax) - WrapOp('qrelu', tf.nn.relu) - WrapOp('qrelu6', tf.nn.relu6, default_qmin=0.0, default_qmax=6.0) - WrapOp( - 'qrandom_uniform', - tf.random.uniform, - default_qmin=0.0, - default_qmax=1.0) - - # Convenience for quantizing weights. - self.AddFunction('qweight', self.QWeight) - - -class BaseClippingCapSchedule(base_layer.BaseLayer): - """Base class for clipping cap schedules.""" - - def __init__(self, params): - super().__init__(params) - - @property - def is_quantized(self): - return False - - def GetEndRange(self): - """Public method to get the final range as a constant. - - Note that this returns the "ideal" end range (i.e. -1..1) as opposed to - the actual range, which has its upper bound slightly adjusted based on - the bit depth of the quantized type. In this sense, this value is a lie, - but it is a consistent lie that can be corrected for downstream by the - inference engine once it has inferred the actual quantized types being - used. - - Note that this also assumes the default start/end caps. Some internal - parts may use altered caps or bit depths. - - Returns: - Tuple of (min, max) for the final range. - """ - raise NotImplementedError('Abstract Method: GetEndRange') - - def GetQuantizedEndRange(self): - """Gets the quantized ending range. - - Unlike GetEndRange(), this takes quantization effects into account. - The default implementation just returns self.GetEndRange(). Subclasses - can include additional keyword arguments, tightly coupling them to callers - of specific types. - - Returns: - Tuple of (min, max) for the final range. - """ - assert not self.is_quantized - return self.GetEndRange() - - def ApplyConstantClip(self, x, min_value, max_value): - """Applies a constant clip with the clipping op for the implementation. - - This is a special case which allows applying a custom clipping range to - constants that are not used arithmetically. This exists to support padding. - - Args: - x: Tensor to clip. - min_value: Minimum value. - max_value: Maximum value. - Returns: - Tensor clipped. - """ - raise NotImplementedError('Abstract method: ApplyConstantClip') - - def GetState(self, theta): - """Gets a state tensor that can be used to calculate clipping. - - The state will be a float32 tensor that is safe to pass to TF functions. - - Args: - theta: Layer theta. - Returns: - An opaque tensor to be passed to ApplyClippingWithState(). - """ - raise NotImplementedError('Abstract method: GetState') - - def ApplyClipping(self, theta, x, **kwargs): - """Applies clipping to x. - - Args: - theta: Layer theta. - x: Input tensor to clip. - **kwargs: Additional implementation specific kwargs. - Returns: - Clipped (or identity) x. - """ - return self.ApplyClippingWithState(self.GetState(theta), x, **kwargs) - - def ApplyClippingWithState(self, state, x): - """Applies clipping to x. - - Args: - state: A previously obtained value of GetState(). - x: Input tensor to clip. - Returns: - Clipped (or identity) x. - """ - raise NotImplementedError('Abstract Method: ApplyClippingWithState') - - -class IdentityClippingCapSchedule(BaseClippingCapSchedule): - """Dummy cc schedule (useful in some cases instead of None).""" - - def GetEndRange(self): - np_dtype = self.params.dtype.as_numpy_dtype - np_info = np.finfo(np_dtype) - return (np_info.min, np_info.max) - - def ApplyConstantClip(self, x, min_value, max_value): - return x - - def GetState(self, theta): - return tf.zeros([1], tf.float32) - - def ApplyClippingWithState(self, state, x): - return x - - -class LinearClippingCapSchedule(BaseClippingCapSchedule): - """Class for linear clipping cap decay.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('start_step', 0, - 'We start gradually narrowing clipping cap from start_step.') - p.Define('end_step', 15000, - 'We reach end_cap by end_step.') - p.Define('start_cap', 8.0, - 'We gradually narrow the clipping range over the course of ' - 'training. This is the clipping range we apply when training ' - 'starts.') - p.Define('end_cap', 1.0, - 'We gradually narrow the clipping range over the course of ' - 'training. This is the clipping range we apply towards the end ' - 'of training.') - p.name = 'CCSchedule' - return p - - @property - def is_quantized(self): - return False - - def ApplyConstantClip(self, x, min_value, max_value): - return tf.clip_by_value(x, min_value, max_value) - - def GetState(self, theta): - return self._Value(theta.global_step) - - def ApplyClippingWithState(self, state, x): - """Applies clipping to x. - - Args: - state: Clipping state. - x: Input tensor to clip. - Returns: - Clipped (or identity) x. - """ - cap = tf.cast(state, x.dtype) - return tf.clip_by_value(x, -cap, cap) - - def GetEndRange(self): - """Returns the range of values that are clipped towards the end of training. - - This is always a constant and is used by downstream systems. - - Returns: - Tuple of (min, max). - """ - return (-self.params.end_cap, self.params.end_cap) - - def _Value(self, current_step): - """Returns the current clipping cap.""" - p = self.params - start_step = tf.cast(p.start_step, tf.float32) - end_step = tf.cast(p.end_step, tf.float32) - current_step = tf.cast(current_step, tf.float32) - steps_ratio = ( - tf.minimum(end_step - start_step, current_step - start_step)/ - (end_step - start_step)) - rmax_tensor = ( - steps_ratio * p.end_cap + (1.0 - steps_ratio) * p.start_cap) - return tf.cond( - tf.less(current_step, - p.start_step), lambda: tf.cast(p.start_cap, tf.float32), - lambda: tf.cast(rmax_tensor, tf.float32)) - - def PostTrainingStepUpdate(self, global_step): - summary_utils.scalar('cap', self._Value(global_step)) - return tf.no_op() - - -class FakeQuantizationSchedule(BaseClippingCapSchedule): - """Manages application of fake quantization via a schedule. - - This implementation is a general-purpose clipping cap schedule but also - works with the Fake Quantization approach used by mobile inference engines. - It is tightly coupled to the FakeQuantizedLSTMCell. See more exhaustive - documentation and links there. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'FQSchedule' - p.Define('clip_start_step', 0, - 'We start gradually narrowing clipping cap from start_step.') - p.Define('clip_end_step', 15000, 'We reach end_cap by end_step.') - p.Define('quant_start_step', 15000, - 'Step at which we begin to apply quantization.') - p.Define('start_cap', 8.0, 'Default clipping/quant start cap.') - p.Define('end_cap', 1.0, 'Default clipping/quant end cap.') - p.Define('bits', 8, 'Default quantized bit depth.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - # We may relax this constraint at some point to allow gradual quantization - # but enforce for now as it is easy to mess up and we have not evaluated - # how it would work otherwise. - assert p.quant_start_step >= p.clip_end_step, ( - 'quant_start_step must be >= clip_end_step') - - @property - def is_quantized(self): - return True - - @property - def bits(self): - p = self.params - return p.bits - - def GetEndRange(self): - """Public method to get the final range as a constant. - - Note that this returns the "ideal" end range (i.e. -1..1) as opposed to - the actual range, which has its upper bound slightly adjusted based on - the bit depth of the quantized type. In this sense, this value is a lie, - but it is a consistent lie that can be corrected for downstream by the - inference engine once it has inferred the actual quantized types being - used. - - Note that this also assumes the default start/end caps. Some internal - parts may use altered caps or bit depths. - - Returns: - Tuple of (min, max) for the final range. - """ - p = self.params - return (-p.end_cap, p.end_cap) - - def GetQuantizedEndRange(self, end_cap=None, bits=None): - """Gets the quantized ending range. - - Unlike GetEndRange(), this takes quantization effects into account. - - Args: - end_cap: Override end_cap value. - bits: Override bits value. - Returns: - Tuple of (min, max) for the final range. - """ - p = self.params - if end_cap is None: - end_cap = p.end_cap - if bits is None: - bits = p.bits - return self._GetQuantizedRangeForCap(end_cap, bits) - - def ApplyConstantClip(self, x, min_value, max_value): - return tf.quantization.fake_quant_with_min_max_vars( - x, min_value, max_value, num_bits=self.params.bits) - - def GetState(self, theta): - """Gets the state from theta.""" - p = self.params - if p.is_inference: - # State is not used for inference. Just return dummy. - return tf.zeros([1], tf.float32) - else: - # Calculations/vars need to be float but these can be ints in the params. - clip_end_step = tf.cast(p.clip_end_step, tf.float32) - clip_start_step = tf.cast(p.clip_start_step, tf.float32) - quant_start_step = tf.cast(p.quant_start_step, tf.float32) - global_step = tf.cast(theta.global_step, tf.float32) - - # Will be negative if before clipping starts. - clip_ratio = ( - tf.minimum(clip_end_step - clip_start_step, - global_step - clip_start_step) / - tf.maximum(1.0, clip_end_step - clip_start_step)) - # Currently fq is either on (1.0) or off (-1.0). Progressive quantization - # may later occupy 0..1.0. - fq_ratio = tf.where(global_step < quant_start_step, -1.0, 1.0) - - return tf.stack([clip_ratio, fq_ratio]) - - def _GetQuantizedRangeForCap(self, current_cap, bits): - """Gets the range for the given cap and number of bits. - - Args: - current_cap: Cap to compute against. - bits: Number of bits (8, 16, etc). - Returns: - If current_cap is a python float, the result will be a float. If a Tensor - scalar, then a Tensor scalar. - """ - dt_max = 2**(bits - 1) # i.e. 8bit = 128, 16bit = 32768 - return -current_cap, current_cap * (dt_max - 1) / dt_max - - def _GetCurrentMinMax(self, - state, - start_cap, - end_cap, - bits, - fixate_to_end_state=False): - """Gets the current min/max for the bit depth and caps. - - Args: - state: Clipping state. - start_cap: Starting cap. - end_cap: Ending cap once clipping saturates. - bits: Number of bits of the quantized datatype. - fixate_to_end_state: Whether to fixate the cap to the end state. - Returns: - (min_value, max_value) as python scalars or 0D Tensors ( - if not fixate_to_end_state). - """ - if fixate_to_end_state: - current_cap = end_cap - else: - clip_ratio = state[0] if not fixate_to_end_state else 1.0 - current_cap = clip_ratio * end_cap + (1.0 - clip_ratio) * start_cap - return self._GetQuantizedRangeForCap(current_cap, bits) - - def ApplyClippingWithState(self, - state, - x, - start_cap=None, - end_cap=None, - bits=None): - """Applies clipping. - - The start_cap, end_cap and bits can be set explicitly and take the default - if None. - - Args: - state: Clipping state. - x: Tensor to clip. - start_cap: Clipping value at the start of the ramp. - end_cap: Clipping value at the end of the ramp. - bits: Number of bits to quantize to. - Returns: - x with clipping applied. - """ - p = self.params - if start_cap is None: - start_cap = p.start_cap - if end_cap is None: - end_cap = p.end_cap - if bits is None: - bits = p.bits - if p.is_inference: - # For inference, we assume that both clipping and quantization have - # saturated and just output a saturated quant op. - min_value, max_value = self._GetCurrentMinMax( - state, start_cap, end_cap, bits, fixate_to_end_state=True) - # Note that the inference version uses the *_args variant, which requires - # constants for min/max. The _GetCurrentMinMax will return (python) - # constants if fixating. This is fragile but works around a Toco bug - # if trying to run on the *_vars form because it can't seem to read - # 0D tensors. This form has the benefit of blowing up at export time - # if the min/max aren't constant. - return _CopyShape( - x, - tf.quantization.fake_quant_with_min_max_args( - x, min_value, max_value, num_bits=bits)) - - # Non-inference. - def Clipped(): - clip_ratio = state[0] - min_value, max_value = self._GetCurrentMinMax(state, start_cap, end_cap, - bits) - min_value = tf.stop_gradient(min_value) - max_value = tf.stop_gradient(max_value) - return tf.where(clip_ratio >= 0.0, - (lambda: tf.clip_by_value(x, min_value, max_value))(), - (lambda: x)()) - - def Quantized(): - min_value, max_value = self._GetCurrentMinMax(state, start_cap, end_cap, - bits) - min_value = tf.stop_gradient(min_value) - max_value = tf.stop_gradient(max_value) - return tf.quantization.fake_quant_with_min_max_vars( - x, min_value, max_value, num_bits=bits) - - # Quantization will implicitly clip, so if we are in the quant phase, just - # do that. Otherwise, clip (which will return identity if not in that - # phase yet). - fq_ratio = state[1] - # return _CopyShape(x, Clipped()) - return _CopyShape(x, tf.where(fq_ratio <= 0.0, Clipped(), Quantized())) - - -class QDomain(base_layer.BaseLayer): - """Base class for a quantization domain layer. - - This implementation doubles as a no-op quantization domain. - """ - - @property - def bits(self): - """Retrieves the bits used by this quantization layer. - - Returns: - The number of bits available to this qdomain or None if unquantized. - """ - return None - - def QuantizeWeight(self, w): - """Quantizes a weight. - - Args: - w: Weight tensor to quantize. - Returns: - Quantized weight. - """ - return w - - def QuantizeConstantRange(self, t, min_value, max_value): - """Quantizes a true-constant range that is not used for arithmetic. - - This supports special values like padding that should have a precise - range that we do not deviate from. - - Args: - t: Tensor to quantize. - min_value: Min of the range. - max_value: Max of the range. - Returns: - Quantized tensor. - """ - return t - - def QuantizeNaturalRange(self, t, min_value, max_value): - """Quantizes a tensor with a known, natural range. - - Args: - t: Tensor to quantize. - min_value: Min value of the range. - max_value: Max value of the range. - Returns: - Quantized tensor. - """ - return t - - def CreateTensor(self, t_name): - """Creates a QTensor with t_name. - - Args: - t_name: Unique name (within layer) for this tensor. - """ - pass - - def QuantizeTensors(self, t_name, ts, eval_only=False): - """Quantizes a tensor with t_name previously created with CreateTensor. - - If applicable, each of the passed tensors contributes to a shared - range. - - Args: - t_name: Tensor name. - ts: List of tensors to quantize. - eval_only: Whether to only apply quantization pressure at eval time. - Returns: - Quantized tensors. - """ - return ts - - def GetTensorRange(self, t_name, ts): - """Retrieves the range of a tensor given the t_name used by CreateTensor. - - Note, this computes the batch range across the list of tensors at training - time but fetches the stored tensor over time. This depends on - QuantizeTensors updating the appropriate value. - - Args: - t_name: Tensor name. - ts: Tensor to determine the range for. - - Returns: - A min-max pair that represents the tensor range. - """ - raise NotImplementedError('Abstract method: NormalizeTensors') - - -class SymmetricScheduledClipQDomain(QDomain): - """A quantization domain that does symmetric scheduled clipping. - - This contains a BaseClippingCapSchedule which handles the actual clipping. It - defaults to a FakeQuantizationSchedule. - - This clipping domain will aid in quantizing layers that are known to tolerate - operation within known ranges (such as LSTM cells). The clipping range will - converge over a range of steps and is setup to match ideal, symmetric ranges - for quantized types. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('cc_schedule', FakeQuantizationSchedule.Params(), - 'Quantization clipping schedule.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - self.CreateChild('cc_schedule', p.cc_schedule) - - @property - def bits(self): - return self.cc_schedule.bits - - def QuantizeWeight(self, w): - return self.cc_schedule.ApplyClipping(self.theta.cc_schedule, w) - - def QuantizeNaturalRange(self, t, min_value, max_value): - # Note: We apply the scheduled clip here, completely overriding the - # known natural range. This is intentional and assumes that when this - # layer is used for symmetric clipping, it is applied uniformly to all - # active elements. - return self.cc_schedule.ApplyClipping(self.theta.cc_schedule, t) - - def QuantizeConstantRange(self, t, min_value, max_value): - # Constant ranges, such as padding are handled separately. They are merely - # constrained to the given range and assumed to be quantizable as-is. - # This is used for padding. - return tf.clip_by_value(t, min_value, max_value) - - def QuantizeTensors(self, t_name, ts, eval_only=False): - if eval_only and not self.do_eval: - return ts - else: - return [ - self.cc_schedule.ApplyClipping(self.theta.cc_schedule, t) for t in ts - ] - - -class _CountedMinMaxAccumulator(base_layer.Accumulator): - """Accumulator for a counted min/max. - - Represented as a tensor of shape [count, min, max]. Every update - increases the count and expands the min/max (initially zeros). - """ - - def __init__(self, dtype): - super().__init__() - self.dtype = dtype - - def DefaultValue(self): - return tf.zeros([3], dtype=self.dtype, name='qstate_zero') - - def Update(self, new_value): - state0 = self.GetValue() - state1 = tf.stack([ - state0[0] + new_value[0], - tf.minimum(state0[1], new_value[1]), - tf.maximum(state0[2], new_value[2]), - ]) - self.SetValue(state1) - - -class PassiveAsymQDomain(QDomain): - """A quantization domain that does passive, asymmetric quantization. - - See: https://arxiv.org/abs/1712.05877 - - This quantization domain will adjust to min/max ranges during training - time, recording them into vars via an exponential moving average and then - applying them at eval/inference time. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('bits', 8, 'Default quantized bit depth.') - p.Define('ema_decay', 0.99, 'Moving average decay.') - p.Define('default_min', -1.0, - 'Default minimum value (so initial graphs are valid).') - p.Define('default_max', 1.0, - 'Default maximum value (so initial graphs are valid).') - p.Define('quantize_weight_epsilon', 0.0, - 'Default epsilon for weight quantization to prevent zero range.') - p.Define( - 'delay_start_steps', 0, - 'Delays applying quantization at training time until after ' - 'this many steps. 0 = start immediately. -1 = start never. ' - 'This is often needed to allow the model to reach some level ' - 'of convergence prior to applying quantization. Only affects ' - 'training (not eval/inference).') - return p - - def __init__(self, params): - super().__init__(params) - - self._t_names = set() # set of known t_name (from CreateTensor) - self._qvars = py_utils.NestedMap() # var_name -> tf.Variable - - def _CreateLayerVariables(self): - # Save a scope for lazily created variables. - with tf.variable_scope('q'): - self._qvars_scope = tf.get_variable_scope() - - def _MaybeFakeQuant(self, inputs, min_v, max_v, num_bits): - p = self.params - - def Apply(): - return tf.quantization.fake_quant_with_min_max_vars( - inputs, min_v, max_v, num_bits=num_bits) - - if p.delay_start_steps != 0 and not self.do_eval: - if p.delay_start_steps == -1: - return inputs - return tf.where(self.theta.global_step >= p.delay_start_steps, Apply(), - inputs) - else: - return Apply() - - @property - def bits(self): - p = self.params - return p.bits - - def QuantizeWeight(self, w): - p = self.params - w_min = tf.reduce_min(w) - w_max = tf.reduce_max(w) - # NOTE: We force a small, non-zero range because otherwise, zero weights - # can cause downstream inference engines to blow up. - w_min = tf.minimum(w_min, -p.quantize_weight_epsilon) - w_max = tf.maximum(w_max, p.quantize_weight_epsilon) - quant_w = self._MaybeFakeQuant(w, w_min, w_max, num_bits=p.bits) - if self.do_eval: - return quant_w - else: - # If quantizing during training, skip quantization if it produces - # NANs. Sometimes early in the training process, things are unstable - # and ranges can produce numerical instability that makes it - # impossible to perform a fake_quant. - quant_w_has_nans = tf.math.is_nan(quant_w) - return tf.where(quant_w_has_nans, w, quant_w) - - def QuantizeNaturalRange(self, t, min_value, max_value): - p = self.params - return self._MaybeFakeQuant(t, min_value, max_value, num_bits=p.bits) - - def QuantizeConstantRange(self, t, min_value, max_value): - p = self.params - return self._MaybeFakeQuant(t, min_value, max_value, num_bits=p.bits) - - def CreateTensor(self, t_name): - p = self.params - assert t_name not in self._t_names, ( - 'QTensor already registered: %s' % t_name) - self._t_names.add(t_name) - - # Create accumulator - accumulator_name = self._GetAccumulatorNameForTensor(t_name) - self.RegisterAccumulator(accumulator_name, - _CountedMinMaxAccumulator(p.dtype)) - # Register vars. - min_pc = py_utils.WeightParams((), - py_utils.WeightInit.Constant(p.default_min), - p.dtype) - max_pc = py_utils.WeightParams((), - py_utils.WeightInit.Constant(p.default_max), - p.dtype) - self._CreateQStateVar(t_name, 'min', min_pc) - self._CreateQStateVar(t_name, 'max', max_pc) - - def QuantizeTensors(self, t_name, ts, eval_only=False): - p = self.params - # Always straddle a real zero point. - if self.do_eval: - # At eval/inference time, use the memorized range. - # Important: Don't capture these variables in training mode so as to - # avoid extra/unnecessary captures. - min_var = self._GetQStateVar(t_name, 'min') - max_var = self._GetQStateVar(t_name, 'max') - return [ - self._MaybeFakeQuant(t, min_var, max_var, num_bits=p.bits) for t in ts - ] - else: - # At training time, use the batch calculated min/max. - accumulator_name = self._GetAccumulatorNameForTensor(t_name) - # Calculate min/max for all tensors. - batch_min = 0.0 - batch_max = 0.0 - for t in ts: - batch_min = tf.minimum(tf.reduce_min(t), batch_min) - batch_max = tf.maximum(tf.reduce_max(t), batch_max) - - # New state. - state1 = tf.stack([1.0, batch_min, batch_max]) - self.accumulators[accumulator_name].Update(state1) - - # Results. - ts_out = [] - for i, t in enumerate(ts): - if eval_only: - # If only quantizing at eval time, still record ranges as above - # but don't quantize. - quant_t = t - else: - # If quantizing during training, skip quantization if it produces - # NANs. Sometimes early in the training process, things are unstable - # and ranges can produce numerical instability that makes it - # impossible to perform a fake_quant. - quant_t = self._MaybeFakeQuant( - t, batch_min, batch_max, num_bits=p.bits) - # TODO(laurenzo): Plumb quant_t_has_nans through state and report. - quant_t_has_nans = tf.math.is_nan(quant_t) - quant_t = tf.where(quant_t_has_nans, t, quant_t) - ts_out.append(quant_t) - summary_utils.histogram( - '%s/%s_%d' % (self._qvars_scope.name, t_name, i), t) - return ts_out - - def GetTensorRange(self, t_name, ts): - # Always straddle a real zero point. - if self.do_eval: - # At eval/inference time, use the memorized range. - # Important: Don't capture these variables in training mode so as to - # avoid extra/unnecessary captures. - min_var = tf.stop_gradient(self._GetQStateVar(t_name, 'min')) - max_var = tf.stop_gradient(self._GetQStateVar(t_name, 'max')) - return (min_var, max_var) - # Calculate min/max for all tensors. - batch_min = tf.minimum(tf.reduce_min(ts), 0.0) - batch_max = tf.maximum(tf.reduce_max(ts), 0.0) - return (tf.stop_gradient(batch_min), tf.stop_gradient(batch_max)) - - def PostTrainingStepUpdate(self, global_step): - ops = [super().PostTrainingStepUpdate(global_step)] - for t_name in self._t_names: - ops.extend(self._RecordTensor(t_name)) - self._SummarizeTensor(t_name) - return tf.group(ops) - - def _CreateQStateVar(self, t_name, suffix, params): - name = t_name + '_' + suffix - assert name not in self._qvars, 'QState var already exists: %s' % name - var_name = self._qvars_scope.name + '/' + name - with tf.variable_scope(py_utils.GetGlobalVariableScope()): - v = py_utils.CreateVariable(var_name, params, trainable=False) - self._qvars[name] = v - return v - - def _GetAccumulatorNameForTensor(self, t_name): - return 'qtensor_' + t_name - - def _GetQStateVar(self, t_name, suffix): - v = self._qvars[t_name + '_' + suffix] - return v - - def _SummarizeTensor(self, t_name): - min_var = self._GetQStateVar(t_name, 'min') - max_var = self._GetQStateVar(t_name, 'max') - # foo/q/somet_min:0 -> foo/q/somet_min - summary_name_min = min_var.name.split(':')[0] - summary_name_max = max_var.name.split(':')[0] - summary_utils.scalar(summary_name_min, min_var) - summary_utils.scalar(summary_name_max, max_var) - - def _RecordTensor(self, t_name): - p = self.params - if self.do_eval: - return [] - - accumulator_name = self._GetAccumulatorNameForTensor(t_name) - accumulator = self.accumulators[accumulator_name] - min_var = self._GetQStateVar(t_name, 'min') - max_var = self._GetQStateVar(t_name, 'max') - - # Unpack state tensor. - current_value = accumulator.GetValue() - count = current_value[0] - min_value = current_value[1] - max_value = current_value[2] - accumulator.Reset() - - def Ema(variable, value): - return (1.0 - p.ema_decay) * (variable - value) - - # Note that small floating point issues can cause ranges that naturally - # begin or end at zero to move slightly past, causing hard failures - # downstream (checks that all ranges straddle zero). We therefore repeat - # the straddling constraint here. - return [ - tf.assign( - min_var, - tf.minimum( - 0., - min_var - tf.where(count > 0., Ema(min_var, min_value), 0.))), - tf.assign( - max_var, - tf.maximum( - 0., - max_var - tf.where(count > 0., Ema(max_var, max_value), 0.))), - ] - - -def _CopyShape(from_t, to_t): - if isinstance(from_t, tf.Tensor) and isinstance(to_t, tf.Tensor): - to_t.set_shape(from_t.shape) - return to_t diff --git a/lingvo/core/quant_utils_test.py b/lingvo/core/quant_utils_test.py deleted file mode 100644 index b0a6e4d6..00000000 --- a/lingvo/core/quant_utils_test.py +++ /dev/null @@ -1,444 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for quant_utils.""" - -# pylint: disable=bad-whitespace -# pylint: disable=bad-continuation - - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import test_utils -import numpy as np - - -class SampleQuantizedProjectionLayer(quant_utils.QuantizableLayer): - """Simple projection layer to demonstrate quantization.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', 2, 'Depth of the input.') - p.Define('output_dim', 3, 'Depth of the output.') - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - w_pc = py_utils.WeightParams( - shape=[p.input_dim, p.output_dim], - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - self.CreateVariable('w', w_pc) - - self.TrackQTensor('inputs', 'transformed') - - def FProp(self, theta, inputs, paddings): - p = self.params - fns = self.fns - - # It is the most important that weights and top-level activations - # be tagged for quantization: - # - Weights use the self.QWeight() decorator - # - Inputs/activations are decorated with self.QTensor(). In general, - # the provided name should match a call to self.TrackQTensor in the - # constructor. This creates an tensor that is individually accounted - # for. - w = fns.qweight(theta.w) - inputs = self.QTensor('inputs', inputs) - - # Note the use of the qmatmul from the function library. This will - # automatically track the output against the qtensor 'transformed'. - out = fns.qmatmul( - tf.reshape(inputs, [-1, p.input_dim]), w, qt='transformed') - out = tf.reshape(out, tf.concat([tf.shape(inputs)[:-1], [p.output_dim]], 0)) - - # Decorate outputs of simple activation functions with their corresponding - # range decorator. This will ensure that the result does not exceed the - # precision of the underlying representation. - out = fns.qtanh(out) - - # Perform padding manipulation via booleans instead of: - # out *= 1.0 - paddings - # Because the paddings can exist in entirely different numeric ranges than - # the tensor they are being applied to, it is best to not perform - # arithmetic directly between them. Instead, broadcast them to the needed - # size (if different) and perform an exact mask with tf.where. - # For added numeric range protection, the QRPadding decorator ensures - # the correct range. This is mostly needed for cases where padding is - # dynamic at inference time. - paddings = self.QRPadding(paddings) - paddings *= tf.ones_like(out) # Broadcast to 'out' size. - out = tf.where(paddings > 0.0, tf.zeros_like(out), out) - - return out - - -class QuantizableLayerTest(test_utils.TestCase): - # pyformat: disable - NO_QDOMAIN_EXPECTED = [ - [[ 0.00071405, -0.03868543, -0.01999986, -0.00994987], - [ 0.08905827, 0.13636404, -0.03180931, 0.06056439], - [ 0. , 0. , 0. , 0. ], - [-0.0208858 , -0.17595209, -0.05192588, 0.02618068]], - [[ 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. ], - [-0.02125708, -0.10454545, -0.01147466, 0.06903321], - [ 0.0276652 , -0.14823943, -0.09726462, 0.01415125]]] - # pyformat: enable - - def testOpWrapperArgChecking(self): - with self.session(): - p = SampleQuantizedProjectionLayer.Params() - p.name = 'test' - l = p.Instantiate() - l.TrackQTensor('test') - fns = l.fns - - # Just testing one dynamic and one const op. - # Dynamic. - fns.qadd(1, 1, qt='test') - fns.qadd(1, 1, qmin=-1.0, qmax=1.0) - with self.assertRaises(AssertionError): - fns.qadd(1, 1) # No range args. - with self.assertRaises(AssertionError): - fns.qadd(1, 1, qmin=-1.0) # Incomplete range args. - with self.assertRaises(AssertionError): - fns.qadd(1, 1, qmax=-1.0) # Incomplete range args. - with self.assertRaisesRegex(AssertionError, 'first calling TrackQTensor'): - fns.qadd(1, 1, qt='non_existing') # Test that qt is resolved. - - # Const. - fns.qtanh(6.0) # No min/max. - fns.qtanh(6.0, qmin=-5.0, qmax=6.0) # Min/max - fns.qtanh(6.0, qt='test') - with self.assertRaisesRegex(AssertionError, 'first calling TrackQTensor'): - fns.qtanh(6.0, qt='non_existing') # Test that qt has precedence. - - def testLayerWithNoQDomain(self): - with self.session(): - p = SampleQuantizedProjectionLayer.Params() - self._testLayerHelper('testLayerWithNoQDomain', p, - self.NO_QDOMAIN_EXPECTED) - - def testLayerWithIdentityQDomain(self): - with self.session(): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.QDomain.Params() - self._testLayerHelper('testLayerWithIdentityQDomain', p, - self.NO_QDOMAIN_EXPECTED) - - def testLayerWithPassiveAsymQDomain(self): - # pyformat: disable - expected = [ - [[ 0. , -0.03921568, -0.02352941, -0.00784314], - [ 0.0862745 , 0.13333333, -0.03137255, 0.06274509], - [ 0. , 0. , 0. , 0. ], - [-0.02352941, -0.17254901, -0.05490196, 0.02352941]], - [[ 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. ], - [-0.02352941, -0.10196078, -0.00784314, 0.07058823], - [ 0.02352941, -0.1490196 , -0.09411764, 0.01568627]]] - # pyformat: enable - - with self.session(): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - l = self._testLayerHelper( - 'testLayerWithPassiveAsymQDomain', p, expected=expected) - init_minmax_vars = l.qdomain_default._qvars.Transform(lambda x: x.eval()) - print('Initial Minmax vars:', init_minmax_vars) - # Record. - self.evaluate([l.PostTrainingStepUpdate(16)]) - minmax_vars = l.qdomain_default._qvars.Transform(lambda x: x.eval()) - print('Minmax vars:', minmax_vars) - - # Make sure that the vars have moved from their defaults. - for k in minmax_vars: - self.assertNotEqual(init_minmax_vars[k], minmax_vars[k]) - - def testLayerWithPassiveAsymQDomainTrainQuantDisabledInital(self): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - p.qdomain.default.delay_start_steps = -1 - with self.session(): - self._testLayerHelper( - 'testLayerWithPassiveAsymQDomainTrainQuantDisabledInital', - p, - expected=self.NO_QDOMAIN_EXPECTED) - - def testLayerWithPassiveAsymQDomainTrainQuantDisabledStep16(self): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - p.qdomain.default.delay_start_steps = -1 - with self.session(): - self._testLayerHelper( - 'testLayerWithPassiveAsymQDomainTrainQuantDisabledStep16', - p, - expected=self.NO_QDOMAIN_EXPECTED, - global_step=16) - - def testLayerWithPassiveAsymQDomainEvalQuantDisabled(self): - with self.session(), self.SetEval(True): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - p.qdomain.default.delay_start_steps = -1 - self._testLayerHelper( - 'testLayerWithPassiveAsymQDomainEvalQuantDisabled', - p, - not_expected=self.NO_QDOMAIN_EXPECTED) - - def testLayerWithPassiveAsymQDomainTrainQuantDelayNotSatisfied(self): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - p.qdomain.default.delay_start_steps = 8 - with self.session(): - self._testLayerHelper( - 'testLayerWithPassiveAsymQDomainTrainQuantDelayNotSatisfied', - p, - expected=self.NO_QDOMAIN_EXPECTED, - global_step=3) - - def testLayerWithPassiveAsymQDomainTrainQuantDelaySatisfied(self): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - p.qdomain.default.delay_start_steps = 8 - with self.session(): - self._testLayerHelper( - 'testLayerWithPassiveAsymQDomainTrainQuantDelaySatisfied', - p, - not_expected=self.NO_QDOMAIN_EXPECTED, - global_step=8) - - def testLayerWithPassiveAsymQDomainTrainQuantDelaySatisfiedPlusOne(self): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.PassiveAsymQDomain.Params() - p.qdomain.default.delay_start_steps = 8 - with self.session(): - self._testLayerHelper( - 'testLayerWithPassiveAsymQDomainTrainQuantDelaySatisfied', - p, - not_expected=self.NO_QDOMAIN_EXPECTED, - global_step=9) - - def testLayerWithSymmetricScheduledClipQDomain(self): - # pyformat: disable - expected = [ - [[ 0. , -0.0390625, -0.015625 , -0.0078125], - [ 0.0859375, 0.140625 , -0.0234375, 0.0625 ], - [ 0. , 0. , 0. , 0. ], - [-0.0234375, -0.171875 , -0.0546875, 0.0234375]], - [[ 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. ], - [-0.0234375, -0.1015625, -0.015625 , 0.0703125], - [ 0. , -0.125 , -0.0625 , 0. ]]] - # pyformat: enable - - with self.session(): - p = SampleQuantizedProjectionLayer.Params() - p.qdomain.default = quant_utils.SymmetricScheduledClipQDomain.Params() - p.qdomain.default.cc_schedule.Set( - clip_start_step=0, - clip_end_step=5, - quant_start_step=10, - ) - self._testLayerHelper( - 'testLayerWithSymmetricScheduledClipQDomain', - p, - expected=expected, - global_step=16) - - def _testLayerHelper(self, - test_case, - p, - expected=None, - not_expected=None, - global_step=-1): - tf.random.set_seed(398847392) - np.random.seed(12345) - p.name = 'proj' - p.input_dim = 3 - p.output_dim = 4 - p.params_init = py_utils.WeightInit.Gaussian(0.1) - l = p.Instantiate() - in_padding = tf.zeros([2, 4, 1], dtype=tf.float32) - in_padding = tf.constant( - [[[0], [0], [1], [0]], [[1], [1], [0], [0]]], dtype=tf.float32) - inputs = tf.constant( - np.random.normal(0.1, 0.5, [2, 4, 3]), dtype=tf.float32) - output = l.FPropDefaultTheta(inputs, in_padding) - self.evaluate(tf.global_variables_initializer()) - - if global_step >= 0: - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), global_step)) - - output = output.eval() - print('QuantizableLayerTest output', test_case, ':\n', - np.array_repr(output)) - if expected is not None: - self.assertAllClose(output, expected) - if not_expected is not None: - self.assertNotAllClose(output, not_expected) - return l - - -class ClippingCapScheduleTest: - - def testLinearClippingCapSchedule(self): - p = quant_utils.LinearClippingCapSchedule.Params() - p.start_step = 50 - p.end_step = 100 - p.start_cap = 6.0 - p.end_cap = 1.0 - cc_schedule = p.Instantiate() - with self.session(): - self.assertAllClose(cc_schedule._Value(25).eval(), 6.0) - self.assertAllClose(cc_schedule._Value(50).eval(), 6.0) - self.assertAllClose(cc_schedule._Value(60).eval(), 5.0) - self.assertAllClose(cc_schedule._Value(70).eval(), 4.0) - self.assertAllClose(cc_schedule._Value(80).eval(), 3.0) - self.assertAllClose(cc_schedule._Value(90).eval(), 2.0) - self.assertAllClose(cc_schedule._Value(100).eval(), 1.0) - self.assertAllClose(cc_schedule._Value(110).eval(), 1.0) - - def _ClipExample(self, cc_schedule, v): - """Returns a tuple of (neg, pos) for clipped neg/pos values of v.""" - v = float(v) - clipped = ( - cc_schedule.ApplyClipping(cc_schedule.theta, -v).eval(), - cc_schedule.ApplyClipping(cc_schedule.theta, v).eval(), - ) - print('Clipped +-', v, ' ->', clipped) - return clipped - - def testFakeQuantizationScheduleFromDefun(self): - p = quant_utils.FakeQuantizationSchedule.Params() - p.clip_start_step = 5 - p.clip_end_step = 10 - p.quant_start_step = 15 - p.start_cap = 6.0 - p.end_cap = 1.0 - with self.session(): - cc_schedule = p.Instantiate() - self.evaluate(tf.global_variables_initializer()) - # Move to fully quantized part of schedule - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 16)) - - @tf.function(autograph=False) - def ExampleFunction8(x, cc_state): - return cc_schedule.ApplyClippingWithState(cc_state, x, bits=8) - - @tf.function(autograph=False) - def ExampleFunction16(x, cc_state): - return cc_schedule.ApplyClippingWithState(cc_state, x, bits=16) - - a = tf.constant(1.0) - b = tf.constant(0.5) - - # 8bit value. - v = ExampleFunction8(a * b, cc_schedule.GetState(cc_schedule.theta)) - self.assertAllClose(v.eval(), 0.5) - - # 16bit value. - v = ExampleFunction16(a * b, cc_schedule.GetState(cc_schedule.theta)) - self.assertAllClose(v.eval(), 0.5) - - # An incomplete implementation requires special case gradient logic. - # This tests it, specifically in a Defun, which caused issues. - # 8bit gradient. - g = tf.gradients( - ExampleFunction8(a * b, cc_schedule.GetState(cc_schedule.theta)), - [a, b]) - g = [t.eval() for t in g] - print('Gradient8:', g) - self.assertAllClose(g, (0.5, 1.0)) - - # 16bit gradient. - g = tf.gradients( - ExampleFunction16(a * b, cc_schedule.GetState(cc_schedule.theta)), - [a, b]) - g = [t.eval() for t in g] - print('Gradient16:', g) - self.assertAllClose(g, (0.5, 1.0)) - - def testFakeQuantizationScheduleTraining(self): - p = quant_utils.FakeQuantizationSchedule.Params() - p.clip_start_step = 5 - p.clip_end_step = 10 - p.quant_start_step = 15 - p.start_cap = 6.0 - p.end_cap = 1.0 - with self.session(): - cc_schedule = p.Instantiate() - self.evaluate(tf.global_variables_initializer()) - # Step 0: No clipping. - self.assertAllClose( - self._ClipExample(cc_schedule, 100.0), (-100.0, 100.0)) - self.assertAllClose( - self._ClipExample(cc_schedule, 0.123456), - (-0.123456, 0.123456)) # Not Quantized. - - # Step 5: Clipping active but not yet quantizing. - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 5)) - self.assertAllClose( - self._ClipExample(cc_schedule, 100.0), - (-6.0, 5.953125)) # 6 * 127/128 - self.assertAllClose( - self._ClipExample(cc_schedule, 0.123456), - (-0.123456, 0.123456)) # Not Quantized. - - # Step 7: Middle of clipping range. - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 7)) - self.assertAllClose( - self._ClipExample(cc_schedule, 100.0), (-4.0, 3.96875)) # 4 * 127/128 - self.assertAllClose( - self._ClipExample(cc_schedule, 0.123456), - (-0.123456, 0.123456)) # Not Quantized. - - # Step 10: End of clipping range. - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 10)) - self.assertAllClose( - self._ClipExample(cc_schedule, 100.0), - (-1.0, 0.9921875)) # 1 * 127/128 - self.assertAllClose( - self._ClipExample(cc_schedule, 0.123456), - (-0.123456, 0.123456)) # Not Quantized. - - # Step 11: No more clipping but not yet quantizing. - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 11)) - self.assertAllClose( - self._ClipExample(cc_schedule, 100.0), - (-1.0, 0.9921875)) # 1 * 127/128 - self.assertAllClose( - self._ClipExample(cc_schedule, 0.123456), - (-0.123456, 0.123456)) # Not Quantized. - - # Step 15-16: Quantizing at full clip. - for step in (15, 16): - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), step)) - self.assertAllClose( - self._ClipExample(cc_schedule, 100.0), - (-1.0, 0.9921875)) # 1 * 127/128 - self.assertAllClose( - self._ClipExample(cc_schedule, 0.123456), - (-0.125, 0.125)) # Quantized. - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/recurrent.py b/lingvo/core/recurrent.py deleted file mode 100644 index f7e41106..00000000 --- a/lingvo/core/recurrent.py +++ /dev/null @@ -1,1634 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Recurrent neural nets. - -The main interface of this module is Recurrent(). -This expects the caller to describe the recurrent neural net by specifying: - - - theta: the "weights" each RNN uses. - - state0: the initial state of each RNN. - - cell_fn: A python function describing RNN cell. It must have the following - signature:: - - cell_fn: (theta, state0, inputs) -> (state1, extras) - - state1 is the next RNN state, extras are computed by cell_fn - and the library forwards extras to cell_fn's gradient function. - - cell_grad: An optional python function describing the backprop gradient - function for the RNN cell. It must have the following signature:: - - cell_grad: (theta, state0, inputs, extras, dstate1) -> - (dtheta, dstate0, dinputs) - - dstate1 is what the backprop algorithm provides representing - gradients of the final loss w.r.t. state1. - -All of `theta`, `state0`, `inputs`, `extras` and `dstate1` are -`.NestedMap` so that they can carry a bunch of tensors around. - -Recurrent computes, roughly:: - - state = state0 - for t in inputs' sequence length: - state = cell_fn(theta, state, inputs[t, :]) - accumulate_state[t, :] = state - return accumulate_state, state - -The main advantage to using Recurrent instead of tf.while_loop is in -memory savings. In order to compute the gradient for cell_fn, a tf.while_loop -implementation will try to save all of the intermediate tensor values in the -forward pass. For long input sequences this can add up to a very large amount -of memory space. - -Recurrent saves only the state output from cell_fn, not any of the intermediate -tensors generated within cell_fn. This saves lots of memory in the forward -pass, but there is a cost: we have to recompute those intermediate tensors -in the backward pass in order to compute the gradient. This recomputation -is why we require that cell_fn be stateless: Recurrent calls cell_fn both -in the forward pass and in the backward pass, and both of those invocations -need to be the same in order for training to work properly. - -When using Recurrent, then, we need to store state for the whole training -sequence (in accumulate_state), as well as all of the intermediate tensors -for a single step of cell_fn. Without Recurrent, we would store all of the -intermediate tensors for all of the steps. - -We prefer that all of the inputs to cell_fn be passed in using theta, state0, -or inputs. But sometimes you may have code with other inputs; for instance, this -cell_fn references tensor my_tensor, even though it was never passed in as an -input:: - - my_tensor = tf.constant(5) - def cell_fn(inputs): - return inputs.input * my_tensor - -We say that my_tensor was implicitly captured by cell_fn. By default, -Recurrent doesn't allow this, but you can change that behavior by setting the -allow_implicit_captures flag. -""" - -import collections -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import py_utils -from lingvo.core import sendrecv -from lingvo.core import symbolic - -DevicePair = collections.namedtuple('DevicePair', ['send', 'recv']) - - -def _AssertSameTensors(list_a, list_b): - """Asserts that two lists of tensors are the same tensors.""" - assert len(list_a) == len(list_b), ( - 'Expected equal tensor lists but different lengths: %r vs %r' % - (list_a, list_b)) - for a, b in zip(list_a, list_b): - assert a is b, ( - 'Expected equal tensor lists but at least one differs: %r vs %r' % - (list_a, list_b)) - - -def _Index(nmap, index): - """Returns a `.NestedMap` with x[index, :] for each tensor x in nmap. - - Args: - nmap: A `.NestedMap` of tensors. - index: A tf scalar integer. Performance is better if 'index' is on the host - memory. - - Returns: - A `.NestedMap` of tensors. For each key in nmap:: - - rets.key = nmap.key[index, :] - """ - index = tf.convert_to_tensor(index) - index.get_shape().assert_has_rank(0) - return nmap.Transform(lambda x: tf.gather(x, index)) - - -def _Update(nmap_acc, nmap_x, t): - """Updates t-th row in accumulators. - - Args: - nmap_acc: A `.NestedMap` of tensors. The accumulators. - nmap_x: A `.NestedMap` of tensors. The update values. - t: A scalar integer. Performance is better if 't' is on the device memory. - - Returns: - A `.NestedMap` of tensors. Say, ret is returned. For each key, we have:: - - ret[key] = nmap_acc[key]; - ret[key][t, :] = nmap_x[key] - """ - acc_lst = nmap_acc.Flatten() - kx_lst = nmap_x.FlattenItems() - t = tf.cast([t], tf.int32) # tf.cast casts on-device tensors. - lst = [] - for acc, (key, x) in zip(acc_lst, kx_lst): - with tf.name_scope('update_%s' % py_utils.SanitizeScopeKey(key)): - lst += [tf.InplaceUpdate(acc, t, tf.expand_dims(x, 0))] - return nmap_acc.Pack(lst) - - -def _SeqLenDim(nmap): - """Returns the 0-th dim size of tensors in nmap. - - This is the max sequence length according to the shape of the inputs. - - Args: - nmap: A `.NestedMap` of tensors. Every tensor's 0-th dim has the same size. - - Returns: - A scalar tensor which is the size of 0-th dim of every tensors in nmap. - """ - keys, values = zip(*nmap.FlattenItems()) - assert values, 'nmap is empty.' - with tf.control_dependencies([ - py_utils.assert_same_dim0( - values, msg='recurrent._SeqLen: %s' % list(keys)) - ]): - return tf.shape(values[0])[0] - - -def FlattenPadding(padding): - """Returns padding reduced to have only the time dimension.""" - if padding is None: - return padding - r = tf.rank(padding) - return tf.reduce_min(padding, axis=tf.range(1, r)) - - -def _SeqPaddingLength(inputs_nmap): - """Returns the lengths of paddings at the beginning and end of the sequence. - - Args: - inputs_nmap: A `.NestedMap` of tensors that may have 'padding' Every - tensor's 0-th dim has the same size. - - Returns: - padding length at the beginning, padding length at the end - """ - padding = inputs_nmap.get('padding') - if padding is None: - return [0, 0] - time = tf.shape(padding)[0] - pad_1d = FlattenPadding(padding) - mask = tf.cast(tf.equal(pad_1d, 0), tf.int32) # [time], 1s/0s - mask_reverse = tf.cast(tf.equal(tf.reverse(pad_1d, [0]), 0), tf.int32) - numbers = tf.range(1, time + 1) - padding_end = time - tf.reduce_max(mask * numbers) - padding_begin = tf.where( - tf.equal(padding_end, time), 0, - time - tf.reduce_max(mask_reverse * numbers)) - return [padding_begin, padding_end] - - -def _EmptyAcc(slen, nmap): - """Creates a set of accumulators for tensors in nmap. - - Args: - slen: A scalar tensor. - nmap: A `.NestedMap` of tensors. - - Returns: - A `.NestedMap` with the same keys as nmap. ret.key, a tensor, has the - same dtype as nmap.key. The tensor's shape has 1 more dimension - than the tensor nmap.key. The extra 0-th dimension is of size - slen. E.g., if slen=10 and nmap.key's shape is [3, 5], then, - ret.key's shape is [10, 3, 5]. - """ - - def Fill(x): - return tf.Empty( - tf.concat([[slen], tf.shape(x)], axis=0), x.dtype, init=True) - - return nmap.Transform(Fill) - - -def _EmptyWithFixShape(shape, nmap): - """Creates a set of empty initialized tensors with fixed shape. - - Args: - shape: A list of integers to describe the output tensor shape. - nmap: A `.NestedMap` of tensors. - - Returns: - A `.NestedMap` with the same keys as nmap. ret.key, a tensor, has the - same dtype as nmap.key, but with the fixed shape. - """ - - return nmap.Transform(lambda x: tf.Empty(shape, dtype=x.dtype, init=True)) - - -def _EmptyLike(nmap): - """Creates a set of empty initialized tensors. - - Args: - nmap: A `.NestedMap` of tensors. - - Returns: - A `.NestedMap` of tensors. Each tensor has the same shape and dtype as - its corresponding tensor in nmap. And each tensor is initialized. - """ - return nmap.Transform(lambda x: tf.EmptyLike(x, init=True)) - - -def _Add(nmap_x, nmap_y): - """Adds tensors in nmap_x with respective tensors in nmap_y. - - Args: - nmap_x: A `.NestedMap` of tensors. - nmap_y: A `.NestedMap` of tensors. - - Returns: - A `.NestedMap` of tensors. ret.key = nmap_x.key + nmap_y.key for every key. - """ - return py_utils.Transform(tf.add, nmap_x, nmap_y) - - -def _TransformDType(nmap): - return nmap.Transform(lambda x: tf.cast(x, tf.int64) - if x.dtype == tf.int32 else x) - - -class _Recurrent: - """A helper class to construct a recurrent neural net.""" - - def __init__(self, - cell_fn, - cell_grad, - stop_fn, - theta, - state0, - inputs, - extras, - cell_type=None, - accumulator_layer=None, - implicit_captures=None, - unused_acc_state=None): - """RNN helper class. - - Args: - cell_fn: A python function which computes: - state1, extras = cell_fn(theta, state0, inputs[t, :]) - cell_grad: A python function which computes: - dtheta, dstate0, dinputs[t, :] = cell_grad( - theta, state0, inputs[t, :], extras, dstate1) - stop_fn: A python function which computes: should_stop = stop_fn(t, theta, - state0) - theta: weights. A `.NestedMap`. - state0: initial state. A `.NestedMap`. - inputs: inputs. A `.NestedMap`. - extras: A `.NestedMap` of Tensors. The 2nd return value of every - invocation of cell_fn is a `.NestedMap` with matching keys and shapes of - this 'extras'. - cell_type: Cell type used in this class. - accumulator_layer: If provided, then accumulators on this layer will be - managed such that they carry to the final state in `FProp` and are - disabled for gradients. Uses the state key `accumulators`. - implicit_captures: A `.NestedMap` corresponding to implicit captures of - the cell_fn. If empty/None, implicit captures are either not present or - disallowed. - unused_acc_state: If None, we assume every field of acc_state is consumed - in the following timestamps. If True, None of the acc_state is consumed. - And we reduce_sum each timestep's new state into a scalar. Note, this - feature should be used with StackedRecurrent where we send out the new - state to the other devices. - """ - self._theta = theta - self._state = state0 - self._inputs = inputs - self._cell_fn = _DecorateCellFn(cell_fn, accumulator_layer) - self._cell_grad = _DecorateCellGrad(cell_grad, accumulator_layer) - self._stop_fn = stop_fn - self._extras = extras - if cell_type is not None: - self._cell_type = cell_type - else: - self._cell_type = 'UnknownType' - self._accumulator_layer = accumulator_layer - self._implicit_captures = implicit_captures - self._unused_acc_state = unused_acc_state - - # NOTE: TF Function (Fwd, Bak, ForwardLoopBody, BackwardLoopBody, - # Forward and Backward defined below) simply takes a list of - # Tensors and returns a list of Tensors. When we pass in a - # structure (a list of NestedMap of Tensors), we use Flatten to - # convert the structure into a list of tensor. Conversely, the - # following code often uses Pack to formulate a structure from a - # list of tensors based on a "template". - - compiled = py_utils.use_xla() - noinline = not compiled - - # state1, extras = cell_fn(theta, state0, inputs) - def Fwd(theta, state0, inputs): - py_utils.SetShapes(theta, self._theta) - state1, extras = self._cell_fn(theta, state0, inputs) - py_utils.AssertIsCompatible(state1, self._state) - py_utils.AssertIsCompatible(extras, self._extras) - return state1, extras - - # Wraps cell_fn in a TF Function as a for-loop's body. - # - # The loop state is composed of: - # t: The loop variable on the device. Timestep id. - # theta: the recurrent net's weights. - # state0: the previous recurrent state. - # inputs: inputs to the recurrent net. inputs[t, :] are for the timestep t. - # acc_state: Each timestep's computed new state is also stashed into - # acc_state. - # acc_extras: Each timestep's computed extras is stashed into acc_extras - - def ForwardLoopCond(loop_state): - """The condition of forward loop.""" - should_continue = loop_state.t < loop_state.limit - if self._stop_fn: - should_continue = tf.math.logical_and( - should_continue, - tf.reduce_any( - tf.math.logical_not( - self._stop_fn(loop_state.t, loop_state.theta, - loop_state.state0)))) - return should_continue - - def ForwardLoopBody(loop_state): - """The body of forward loop.""" - t = loop_state.t - # external input at time step t. - inputs_t = _Index(loop_state.inputs, t) - loop_state.state0, extras = Fwd(loop_state.theta, loop_state.state0, - inputs_t) - # Saves state1 and extras in their accumulators. - if not self._unused_acc_state: - loop_state.acc_state = _Update(loop_state.acc_state, loop_state.state0, - t) - loop_state.acc_extras = _Update(loop_state.acc_extras, extras, t) - loop_state.t = tf.add(t, 1) - return loop_state - - # Forward calls ForwardLoopBody n times. Each time computes one - # time step of the recurrent net. - def Forward(args): - """Forward pass of the recurrent net.""" - # The sequence length. - pad_begin, pad_end = _SeqPaddingLength(args.inputs) - slen_dim = _SeqLenDim(args.inputs) - limit = slen_dim - pad_end - - # Creates accumulators for state0 and extras. - if self._unused_acc_state: - acc_state = _EmptyWithFixShape([slen_dim], args.state0) - else: - acc_state = _EmptyAcc(slen_dim, args.state0) - acc_extras = _EmptyAcc(slen_dim, args.extras) - - if compiled: - t = tf.cast(pad_begin, tf.int32) - limit = tf.cast(limit, tf.int32) - else: - t = tf.cast(pad_begin, tf.int64) - limit = tf.cast(limit, tf.int64) - - with py_utils.RemoveAssertContext(remove=noinline): - run = py_utils.WhileLoop( - ForwardLoopCond, - ForwardLoopBody, - loop_state=py_utils.NestedMap( - t=t, - limit=limit, - theta=args.theta, - state0=args.state0, - inputs=args.inputs, - acc_state=acc_state, - acc_extras=acc_extras)) - return py_utils.NestedMap( - limit=run.t, - final_state=run.state0, - acc_state=run.acc_state, - acc_extras=run.acc_extras) - - # The per-step backward computes: - # d_theta, d_state0, d_inputs = cell_grad( - # theta, state0, inputs, extras, d_state1) - # where d_state1 is the backprop-ed gradient for state1, and - # extras is the computed by the forward step to facilitate the - # backward step. - def Bak(theta, state0, inputs, extras, d_state1): - """Backward step.""" - py_utils.SetShapes(theta, self._theta) - (dtheta, dstate0, dinputs, - dcaptures) = self._cell_grad(theta, state0, inputs, extras, d_state1) - py_utils.AssertIsCompatible(dtheta, self._theta) - py_utils.AssertIsCompatible(dstate0, self._state) - py_utils.AssertIsCompatible(dinputs, self._inputs) - if dcaptures is None: - # NOTE: Custom gradient fns can return None if they do not support - # captured tensors. The return value is reserved for the future when - # that may be supported. - dcaptures = _EmptyLike(self._implicit_captures) - py_utils.AssertIsCompatible(dcaptures, self._implicit_captures) - - # Make sure this function didn't capture anything different than the - # cell_fn when reflected on at the beginning. Must come after the call - # to cell_grad() which adds to the captured list. - _AssertSameTensors(py_utils.GetExtraInputs(), - self._implicit_captures.Flatten()) - - return [dtheta, dstate0, dinputs, dcaptures] - - # Wraps cell_grad gradient function in a TF Function as a - # for-loop's body for the Backward pass. - # - # The loop state is composed of: - # t: The loop variable on the device. Timestep id. - # state0: the initial state for the entire backward loop. - # theta: the recurrent net's weights. - # inputs: inputs to the recurrent net. inputs[t, :] are for the timestep t. - # acc_state: Each timestep's computed new state was stashed into - # acc_state by the Forward pass. - # acc_extras: Each timestep's computed extras was stashed into - # acc_extras by the Forward pass. - # d_theta: All timestep's gradient for theta is accumulated (added) into - # d_theta. - # d_state1: The backprop-ed gradient for the new stated computed by - # timestep t. - # d_inputs: d_inputs[t, :] is populated by the backward time step t. - # d_acc_state: The backprop-ed gradient for acc_state. - # d_captured: All timestep's gradient for theta is accumulated (added) - # into d_captured. - - def BackwardLoopCond(loop_state): - """Backward loop condition function.""" - return loop_state.t >= loop_state.limit - - def BackwardLoopBody(loop_state): - """Backward loop body function.""" - t = loop_state.t - # The input recurrent state for time step t is previous time step's - # output, or the original state0 when on time step 0. - state_from_acc = _Index(loop_state.acc_state, - tf.maximum(tf.constant(0, t.dtype), t - 1)) - state0 = py_utils.If( - tf.equal(t, tf.constant(0, t.dtype)), - inputs=py_utils.NestedMap( - orig_state0=loop_state.state0, state_from_acc=state_from_acc), - then_branch=lambda nmap: nmap.orig_state0, - else_branch=lambda nmap: nmap.state_from_acc) - - # The external inputs for time step t. - inputs_t = _Index(loop_state.inputs, t) - # The extras for time step t. - extras_t = _Index(loop_state.acc_extras, t) - - d_state1 = _Add(_Index(loop_state.d_acc_state, t), loop_state.d_state1) - (d_theta_t, loop_state.d_state1, d_inputs_t, - d_captured_t) = Bak(loop_state.theta, state0, inputs_t, extras_t, - d_state1) - - if self._unused_acc_state: - # XLA IF op requires the same shape for if and else branches. - loop_state.d_state1 = loop_state.d_state1.Transform(tf.reduce_sum) - loop_state.d_theta = _Add(loop_state.d_theta, d_theta_t) - loop_state.d_inputs = _Update(loop_state.d_inputs, d_inputs_t, t) - loop_state.d_captured = _Add(loop_state.d_captured, d_captured_t) - loop_state.t = tf.subtract(t, 1) - - # Make sure this function didn't capture anything different than the - # cell_fn when reflected on at the beginning. Must come after the call - # to Bak() which adds to the captured list. - _AssertSameTensors(py_utils.GetExtraInputs(), - self._implicit_captures.Flatten()) - - return loop_state - - # Backward calls BackwardLoopBody n times. Each time computes the backprop - # for one time step of the recurrent net. - def Backward(xs, ys, dys): - """Backward pass for the recurrent net. - - Args: - xs: inputs to the forward operation. - ys: outputs of the forward operation. - dys: gradients to the outputs of the forward operation. - - Returns: - Gradients to the inputs of the forward operation. - """ - # Accumulators for gradients. - d_theta = _EmptyLike(xs.theta) - d_inputs = _EmptyLike(xs.inputs) - d_captured = _EmptyLike(self._implicit_captures) - - # The sequence length. - pad_begin, _ = _SeqPaddingLength(xs.inputs) - limit = pad_begin - - if compiled: - limit = tf.cast(limit, tf.int32) - else: - limit = tf.cast(limit, tf.int64) - - state0 = xs.state0 - d_state1 = dys.final_state - if self._unused_acc_state: - # XLA While op requires the same shape for the init and carry on - # values. - state0 = state0.Transform(tf.reduce_sum) - d_state1 = d_state1.Transform(tf.reduce_sum) - - with py_utils.RemoveAssertContext(remove=noinline): - run = py_utils.WhileLoop( - cond=BackwardLoopCond, - body=BackwardLoopBody, - loop_state=py_utils.NestedMap( - t=ys.limit - 1, - limit=limit, - theta=xs.theta, - state0=state0, - inputs=xs.inputs, - acc_state=ys.acc_state, - acc_extras=ys.acc_extras, - d_theta=d_theta, - d_state1=d_state1, - d_inputs=d_inputs, - d_acc_state=dys.acc_state, - d_captured=d_captured)) - - d_state0 = run.d_state1 - if self._unused_acc_state: - # Match the shape of gradient of the init_state. - d_state0 = self._state.Transform(tf.zeros_like) - - # The `extra` input in the Forward function is actually an output of the - # function. It was supplied as an input only to create acc_extras with - # proper shape, so its gradients should be zero. - return py_utils.NestedMap( - d_theta=run.d_theta, - d_state0=d_state0, - d_inputs=run.d_inputs, - d_extras=_EmptyLike(self._extras)), run.d_captured - - # Forward arguments. - self._fwd_args = py_utils.NestedMap( - theta=self._theta, - state0=self._state, - inputs=self._inputs, - extras=self._extras) - - # pylint: disable=protected-access - device_funcs = tf.get_default_graph()._device_functions_outer_to_inner - self._caller_device = device_funcs[-1] if device_funcs else None - # pylint: enable=protected-access - - self._forward = Forward - self._backward = Backward - - def Compute(self): - """Run the computation.""" - run = py_utils.CallDefun( - self._forward, - self._fwd_args, - bak=self._backward, - device=self._caller_device) - - if self._accumulator_layer: - # Restore the accumulators from the final recurrent state. - self._accumulator_layer.SetAccumulatorValues(run.final_state.accumulators) - del run.acc_state.accumulators - del run.final_state.accumulators - - del run.acc_state['_step_seed'] - py_utils.ResetStepSeed(run.final_state.pop('_step_seed')) - - return run.acc_state, run.final_state - - -def _ReflectOnCellFn(cell_fn, - theta, - state0, - inputs, - accumulator_layer=None, - check_stateful_ops=False, - allow_implicit_capture=False): - """Reflects on the cell_fn, applying asserts and returning needed info. - - Args: - cell_fn: A python function that computes: - state1, extras = cell_fn(theta, state0, inputs[t, :]) - theta: weights. A `.NestedMap`. - state0: initial state. A `.NestedMap`. - inputs: inputs. A `.NestedMap`. - accumulator_layer: Whether the cell function must be run in the context of - the given accumulator layer. - check_stateful_ops: if True, raise a `ValueError` if cell_fn is stateful. - allow_implicit_capture: Whether to allow the `cell_fn` to implicitly capture - tensors. - - Returns: - `.NestedMap` of implicit captures that the cell_fn takes. - Raises: - ValueError: cell_fn is stateful. - """ - # Reset the augmented state entries as we may be running in a special - # disabled context and we want state0 to reflect that. - state0 = _AugmentState( - state0.DeepCopy(), accumulator_layer, allow_overwrite=True) - - fwd_sig = [theta, state0, inputs] - input_signature = py_utils.Transform(lambda t: tf.TensorSpec(None, t.dtype), - fwd_sig) - - @py_utils._WrapFunction(input_signature=py_utils.Flatten(input_signature)) # pylint: disable=protected-access - def Fwd(*args): - # tf.function inherits the step seed collection from parent graph, we need - # to reset it to mimic Defun's behavior. - py_utils.ResetStepSeed() - - (theta, state0, inputs) = py_utils.Pack(fwd_sig, args) - py_utils.SetShapes(theta, fwd_sig[0]) - state1, extras = cell_fn(theta, state0, inputs) - return py_utils.Flatten([state1, extras]) - - # Get the stateful ops used in cell_fn. Logic borrowed from - # _EagerDefinedFunction.__init__(). - input_ops = set(arg.op for arg in Fwd.graph.inputs) - operations = [op for op in Fwd.graph.get_operations() if op not in input_ops] - stateful_ops = tuple(op for op in operations if op._is_stateful) # pylint: disable=protected-access - - # Asserts about the function. - if stateful_ops: - if check_stateful_ops: - raise ValueError('cell_fn contains stateful ops: %s' % stateful_ops) - else: - tf.logging.warning('cell_fn contains stateful ops: %s', stateful_ops) - - if cluster_factory.Current().job in {'trainer', 'trainer_client'}: - stateful_random_ops = py_utils.StatefulRandomOpsInDefun(Fwd) - if stateful_random_ops: - raise tf.errors.InvalidArgumentError( - None, None, 'cell_fn depends on stateful random ops: {}'.format( - stateful_random_ops)) - - ret = py_utils.NestedMap() - captured_inputs = list(Fwd.captured_inputs) - if captured_inputs: - if not allow_implicit_capture: - raise ValueError('Recurrent cell_fn implicitly captures tensors but ' - 'implicit capture is disabled or a custom cell_grad fn ' - 'is in use. Captured tensors: %r' % captured_inputs) - ret.captured = captured_inputs - return ret - - -def _GetCellGrad(cell_fn, - cell_grad, - theta, - state0, - inputs, - accumulator_layer, - check_stateful_ops=False, - allow_implicit_capture=False): - """Returns the gradient function for cell_fn. - - Args: - cell_fn: The recurrent neural net's cell function. - cell_grad: If not None, cell_fn's gradient function. - theta: weights. A `.NestedMap`. - state0: initial state. A `.NestedMap`. - inputs: inputs. A `.NestedMap`. - accumulator_layer: Whether the cell function must be run in the context of - the given accumulator layer. - check_stateful_ops: if True, raise a `ValueError` if cell_fn is stateful. - allow_implicit_capture: Whether to allow the `cell_fn` to implicitly capture - tensors. - - Returns: - Returns (cell_grad, implicit_captures). The passed in cell_grad is returned - as-is if not None. Otherwise, assume cell_fn is a python function - representing the recurrent neural net's cell function, i.e.:: - - cell_fn: (theta, state0, inputs) -> (state1, extra) - - returns its default gradient python function, i.e.:: - - cell_grad: (theta, state0, inputs, extras, captured, dstate1) -> - (dtheta, dstate0, dinputs) - """ - implicit_captures = _ReflectOnCellFn(cell_fn, theta, state0, inputs, - accumulator_layer, check_stateful_ops, - allow_implicit_capture) - - if not cell_grad: - - def CellGrad(theta, state0, inputs, unused_extras, dstate1): - """Default gradient function for cell_fn.""" - # NOTE: The default grad function recomputes the forward - # function and does not take advantage of 'extras' returned by - # the forward function. - state1, _ = cell_fn(theta, state0, inputs) - assert isinstance(state1, py_utils.NestedMap), ('%s' % state1) - - # Assert that if captured inputs were given, they match the actual - # tensors passed to the function we are compiled into. Must come after - # the call to cell_fn, which does the capture. - _AssertSameTensors(py_utils.GetExtraInputs(), implicit_captures.Flatten()) - - # Extract the internal captured tensor placeholders within the Defun - # we are running in. - captured = py_utils.Pack(implicit_captures, py_utils.GetExtraArgs()) - ys = py_utils.Flatten(state1) - xs = py_utils.Flatten([theta, state0, inputs, captured]) - grad_ys = py_utils.Flatten(dstate1) - grads = tf.gradients(ys=ys, xs=xs, grad_ys=grad_ys) - grads = py_utils.ConvertNoneGradientToZeros(xs, grads) - return py_utils.Pack([theta, state0, inputs, captured], grads) - - cell_grad = CellGrad - - return cell_grad, implicit_captures - - -def _AugmentState(state0, accumulator_layer, allow_overwrite=False): - """Augments state0 with additional state.""" - if accumulator_layer: - if 'accumulators' in state0 and not allow_overwrite: - raise ValueError('accumulators is a private state key used by Recurrent.') - state0.accumulators = accumulator_layer.GetAccumulatorValues() - - # _step_seed is used for seeding stateless random ops. - # See py_utils.GenerateStepSeedPair for more details. - if '_step_seed' in state0 and not allow_overwrite: - raise ValueError('_step_seed is a private state key used by Recurrent.') - state0['_step_seed'] = py_utils.GetStepSeed() - - return state0 - - -def _WrapAccumulatorCellFn(accumulator_layer, cell_fn): - """Wrap a cell_fn to propagate accumulators.""" - - def WrappedCellFn(theta, state0, inputs): - """cell_fn wrapped to propagate accumulators.""" - accumulator_layer.SetAccumulatorValues(state0.accumulators) - # The underlying cell_fn has no knowledge of accumulator state so - # delete it. - state0_accumulators = state0.pop('accumulators') - state1, extras = cell_fn(theta, state0, inputs) - state0.accumulators = state0_accumulators - # Propagate new accumulator state forward. - state1.accumulators = accumulator_layer.GetAccumulatorValues() - # Reset: make sure nothing escapes. - accumulator_layer.accumulators.Transform(lambda x: x.Reset()) - return state1, extras - - return WrappedCellFn - - -def _WrapAccumulatorCellGradFn(accumulator_layer, cell_grad): - """Wrap a cell grad function to disable accumulators.""" - - def WrappedCellGradFn(theta, state0, inputs, extras, dstate1): - """cell_grad wrapped to disable accumulators.""" - # Compute the cell grad function with accumulators disabled. - accumulator_layer.accumulators.Transform(lambda x: x.Disable()) - # The underlying cell_grad has no knowledge of accumulator state so - # delete it. - state0_accumulators = state0.pop('accumulators') - dstate1_accumulators = dstate1.pop('accumulators') - dtheta, dstate0, dinputs, dcaptures = cell_grad(theta, state0, inputs, - extras, dstate1) - state0.accumulators = state0_accumulators - dstate0.accumulators = dstate1_accumulators - dstate1.accumulators = dstate1_accumulators - accumulator_layer.accumulators.Transform(lambda x: x.Enable()) - return dtheta, dstate0, dinputs, dcaptures - - return WrappedCellGradFn - - -def _WrapCellFnWithStepSeed(cell_fn): - """Wrap a cell_fn to initialize the step seed.""" - - def WrappedCellFn(theta, state0, *args, **kwargs): - """The wrapper function.""" - # The _step_seed state should be transparent to cell_fn. - state0_step_seed = state0.pop('_step_seed') - py_utils.ResetStepSeed(state0_step_seed) - state1, extras = cell_fn(theta, state0, *args, **kwargs) - state0['_step_seed'] = state0_step_seed - state1['_step_seed'] = py_utils.GetStepSeed() - return state1, extras - - return WrappedCellFn - - -def _WrapCellGradFnWithStepSeed(cell_grad): - """Wrap a cell grad function to handle step seed in state.""" - - def WrappedCellGradFn(theta, state0, inputs, extras, dstate1): - """The wrapper function.""" - # The _step_seed state should be transparent to cell_grad. - state0_step_seed = state0.pop('_step_seed') - dstep_seed = dstate1.pop('_step_seed') - py_utils.ResetStepSeed(state0_step_seed) - dtheta, dstate0, dinputs, dcaptures = cell_grad(theta, state0, inputs, - extras, dstate1) - state0['_step_seed'] = state0_step_seed - dstate0['_step_seed'] = dstep_seed - dstate1['_step_seed'] = dstep_seed - return dtheta, dstate0, dinputs, dcaptures - - return WrappedCellGradFn - - -def _WrapCellFnWithSymbolValues(cell_fn, symbol_to_tensor_map): - """Wrap a cell_fn to propagate symbol values.""" - - def WrappedCellFn(theta, state0, inputs): - """cell_fn wrapped to propagate accumulators.""" - theta = theta.copy() - symbols = list(symbol_to_tensor_map.keys()) - symbol_values = theta.pop('_symbol_values') - inner_symbol_to_tensor_map = dict(zip(symbols, symbol_values)) - if symbols: - tf.logging.info('_WrapCellFnWithSymbolValues: %s', symbols) - with symbolic.SymbolToValueMap(symbolic.TENSOR_VALUES, - inner_symbol_to_tensor_map): - state1, extras = cell_fn(theta, state0, inputs) - return state1, extras - - return WrappedCellFn - - -def _WrapCellGradFnWithSymbolValues(cell_grad, cell_fn, symbol_to_tensor_map): - """Wrap a cell grad function to propagate symbol values.""" - - def WrappedCellGradFn(theta, state0, inputs, extras, dstate1): - """The wrapper function.""" - symbols = list(symbol_to_tensor_map.keys()) - symbol_values = theta['_symbol_values'] - inner_symbol_to_tensor_map = dict(zip(symbols, symbol_values)) - if symbols: - tf.logging.info('_WrapCellGradFnWithSymbolValues: %s', symbols) - with symbolic.SymbolToValueMap(symbolic.TENSOR_VALUES, - inner_symbol_to_tensor_map): - dtheta, dstate0, dinputs, dcaptures = cell_grad(theta, state0, inputs, - extras, dstate1) - # cell_grad may have populated dtheta by applying tf.gradients() on - # theta.Flatten(). - if '_symbol_values' not in dtheta: - state1, _ = cell_fn(theta, state0, inputs) - dxs = tf.gradients( - ys=state1.Flatten(), xs=symbol_values, grad_ys=dstate1.Flatten()) - dtheta['_symbol_values'] = py_utils.ConvertNoneGradientToZeros( - symbol_values, dxs) - return dtheta, dstate0, dinputs, dcaptures - - return WrappedCellGradFn - - -def _DecorateCellFn(cell_fn, accumulator_layer): - """Decorates cell_fn with additional state information.""" - if accumulator_layer: - # Wrap the cell_fn so that it knows how to propagate accumulators. - cell_fn = _WrapAccumulatorCellFn(accumulator_layer, cell_fn) - cell_fn = _WrapCellFnWithStepSeed(cell_fn) - return cell_fn - - -def _DecorateCellGrad(cell_grad, accumulator_layer): - """Decorates cell_grad with additional state information.""" - if accumulator_layer: - # Wrap the cell_grad so it disables accumulators. - cell_grad = _WrapAccumulatorCellGradFn(accumulator_layer, cell_grad) - cell_grad = _WrapCellGradFnWithStepSeed(cell_grad) - return cell_grad - - -def _IsSingleTimeStep(inputs): - """Returns True only if the time dimension of inputs is 1.""" - for x in inputs.Flatten(): - if x.shape.dims is None or x.shape.as_list()[0] != 1: - return False - return True - - -def _RecurrentSingleTimeStep(theta, state0, inputs, cell_fn): - """Short-cut for the single timestep without explicit cell_grad case.""" - # The seqlen length is staticly known as 1. Hence, we just need to - # call cell_fn once without putting it into a loop. - # Since we are not looping, there is no need to specially manage - # accumulators. - inputs = inputs.Transform(lambda x: tf.squeeze(x, axis=0)) - state1, _ = cell_fn(theta, state0, inputs) - acc_state = state1.Transform(lambda x: tf.expand_dims(x, axis=0)) - return acc_state, state1 - - -def Recurrent(theta, - state0, - inputs, - cell_fn, - cell_grad=None, - cell_type=None, - stop_fn=None, - extras=None, - check_stateful_ops=False, - accumulator_layer=None, - allow_implicit_capture=False): - """Compute a recurrent neural net. - - Roughly, `Recurrent()` computes the following:: - - state = state0 - for t in inputs' sequence length: - state = cell_fn(theta, state, inputs[t, :]) - accumulate_state[t, :] = state - return accumulate_state, state - - `theta`, `state`, `inputs` are all `.NestedMap` objects. - - `inputs[t, :]` means taking a slice out from every tensor in the - `.NestedMap` `inputs`. - - `accumulate_state[t, :] = state` means that we stash every tensor in - `state` into a slice of the corresponding tensor in - `accumulate_state`. - - `cell_fn` is a python callable computing (building up a TensorFlow - graph) the recurrent neural network's one forward step. `cell_fn` must not - contain any stateful ops. Two calls of `cell_fn` must describe two identical - computations. - - By construction, `Recurrent()`'s backward computation does not access - any intermediate values computed by `cell_fn` during forward - computation. We may extend `Recurrent()` to support that by taking a - customized backward function of `cell_fn`. - - Args: - theta: weights. A `.NestedMap`. - state0: initial state. A `.NestedMap`. - inputs: inputs. A `.NestedMap`. - cell_fn: A python function which computes:: - state1, extras = cell_fn(theta, state0, inputs[t, :]) - cell_grad: A python function which computes:: - dtheta, dstate0, dinputs[t, :], dcaptured = cell_grad( - theta, state0, inputs[t, :], extras, dstate1) If there are no - captured tensors in `cell_fn`, `dcaptured` can be returned as - None. Captured tensors with custom `cell_grad` is currently - unsupported so this return value is reserved for future expansion. - cell_type: Cell name to be used. - stop_fn: If not None, a python function which computes:: should_stop = - stop_fn(t, theta, state0) The function determines whether the recurrent - loop should terminate. - extras: A `.NestedMap` of Tensors. The 2nd return value of every invocation - of `cell_fn` is a `.NestedMap` with matching keys and shapes of `extras`. - check_stateful_ops: if True, raise a `ValueError` if `cell_fn` is stateful. - accumulator_layer: If provided, then accumulators on this layer will be - managed such that they carry to the final state in `FProp` and are - disabled for gradients. Uses the state key `accumulators`. - allow_implicit_capture: Whether to allow the `cell_fn` to implicitly capture - tensors. Only allowed if an explicit `cell_grad` is not given. - - Returns: - `accumulate_state` and the final state. - """ - symbol_to_tensor_map = symbolic.SymbolToValueMap.Get(symbolic.TENSOR_VALUES) - if symbol_to_tensor_map: - theta = theta.copy() # Do not modify the caller's 'theta'. - theta['_symbol_values'] = list( - tf.convert_to_tensor(v) for v in symbol_to_tensor_map.values()) - cell_fn = _WrapCellFnWithSymbolValues(cell_fn, symbol_to_tensor_map) - if cell_grad: - cell_grad = _WrapCellGradFnWithSymbolValues(cell_grad, cell_fn, - symbol_to_tensor_map) - - inputs = _TransformDType(inputs) - if cell_grad is not None: - allow_implicit_capture = False - - # Short-cut for the single timestep with default grad function case. - if cell_grad is None and _IsSingleTimeStep(inputs): - return _RecurrentSingleTimeStep(theta, state0, inputs, cell_fn) - - # Disable accumulators since cell_fn needs to be called a few times and those - # aren't real calls to cell_fn. They will be re-enabled just prior to - # calling _Recurrent. - if accumulator_layer: - accumulator_layer.accumulators.Transform(lambda x: x.Disable()) - - cell_grad, implicit_captures = _GetCellGrad(cell_fn, cell_grad, theta, state0, - inputs, accumulator_layer, - check_stateful_ops, - allow_implicit_capture) - - with tf.name_scope('recurrent_cellfn_extras'): - # Derives 'extras' so that we can allocate extras' accumulator. - # Not a real call to cell_fn, so make sure it doesn't affect step_seed. - step_seed = py_utils.GetStepSeed() - # Make sure not to modify the original state0. - _, actual_extras = cell_fn(theta, state0.DeepCopy(), _Index(inputs, 0)) - py_utils.ResetStepSeed(step_seed) - if extras is None: - extras = actual_extras.Transform(tf.zeros_like) - else: - if not extras: - # Forces the extras to be an empty map if an empty 'extras' is provided. - extras = py_utils.NestedMap() - py_utils.AssertIsCompatible(extras, actual_extras) - - # Enable accumulators. Note that this must happen prior to the initial - # _AugmentState() below or it will initialize with defaults. - if accumulator_layer: - accumulator_layer.accumulators.Transform(lambda x: x.Enable()) - - acc_state, final_state = _Recurrent( - cell_fn=cell_fn, - cell_grad=cell_grad, - cell_type=cell_type, - stop_fn=stop_fn, - theta=theta, - state0=_AugmentState(state0.DeepCopy(), accumulator_layer), - inputs=inputs, - extras=extras, - accumulator_layer=accumulator_layer, - implicit_captures=implicit_captures).Compute() - - # TODO(b/129159299): The ResetStepSeed below is needed to work around this - # bug, which is a problem with global tensors being shared by different - # inference graphs. It should be removed once the bug is fixed. - py_utils.ResetStepSeed( - py_utils.GenerateSeedFromName(tf.no_op(name='new_step_seed').name)) - - return acc_state, final_state - - -class _Link: - """A link is a pair of channels.""" - - def __init__(self, t, dpair): - # Uses a unique name scope to name the channel. - with tf.name_scope('fwd') as scope: - self.fwd = sendrecv.Channel(t.dtype, t.shape, dpair.send, dpair.recv, - scope) - with tf.name_scope('bak') as scope: - self.bak = sendrecv.Channel(t.dtype, t.shape, dpair.recv, dpair.send, - scope) - - -def _CreateLinks(nmap, dpair): - """Creates links between the send/recv devices for every tensor in nmap.""" - return nmap.Transform(lambda t: _Link(t, dpair)) - - -def _Join(nmap_x, nmap_y, fn): - return py_utils.Transform(fn, nmap_x, nmap_y).Flatten() - - -class _Input: - """Input layers.""" - - def __init__(self, - cell_fn, - cell_out, - cell_grad, - cell_out_grad, - theta, - state0, - accumulator_layer, - inputs, - extras, - out_links, - unused_acc_state=False): - self._cell_fn = cell_fn - self._cell_out = cell_out - self._cell_grad, self._implicit_captures = _GetCellGrad( - cell_fn, - cell_grad, - theta, - state0, - inputs, - accumulator_layer, - allow_implicit_capture=True) - self._cell_out_grad = cell_out_grad - self._theta = theta - self._state0 = state0 - self._accumulator_layer = accumulator_layer - self._inputs = inputs - self._extras = extras - self._out_links = out_links - self._unused_acc_state = unused_acc_state - assert self._extras is not None - - def Compute(self): - """Compute the input layer.""" - - def InputFn(theta, state0, inputs): - state1, extras = self._cell_fn(theta, state0, inputs) - py_utils.AssertIsCompatible(state1, state0) - py_utils.AssertIsCompatible(extras, self._extras) - out = self._cell_out(state1) - sends = _Join(self._out_links, out, lambda l, x: l.fwd.Send(x)) - with tf.control_dependencies(sends): - return state1.Transform(tf.identity), extras.Transform(tf.identity) - - def InputGrad(theta, state0, inputs, extras, dstate1): - """Gradient function for InputFn.""" - recv_dout = self._out_links.Transform(lambda l: l.bak.Recv()) - dstate1 = _Add(dstate1, self._cell_out_grad(recv_dout)) - dtheta, dstate0, dinputs, dcaptures = self._cell_grad( - theta, state0, inputs, extras, dstate1) # pylint: disable=unbalanced-tuple-unpacking - py_utils.AssertIsCompatible(dtheta, self._theta) - py_utils.AssertIsCompatible(dstate0, state0) - py_utils.AssertIsCompatible(dinputs, self._inputs) - if dcaptures is None: - # NOTE: Custom gradient fns can return None if they do not support - # captured tensors. The return value is reserved for the future when - # that may be supported. - dcaptures = _EmptyLike(self._implicit_captures) - py_utils.AssertIsCompatible(dcaptures, self._implicit_captures) - return dtheta, dstate0, dinputs, dcaptures - - return _Recurrent( - cell_fn=InputFn, - cell_grad=InputGrad, - stop_fn=None, - theta=self._theta, - state0=self._state0, - inputs=self._inputs, - extras=self._extras, - accumulator_layer=self._accumulator_layer, - implicit_captures=self._implicit_captures, - unused_acc_state=self._unused_acc_state).Compute() - - -class _Middle: - """Middle layers.""" - - def __init__(self, cell_fn, cell_out, cell_grad, cell_out_grad, theta, state0, - accumulator_layer, in_links, padding, slen_dim, per_step_inputs, - extras, out_links, unused_acc_state): - self._cell_fn = cell_fn - self._cell_out = cell_out - self._cell_grad, self._implicit_captures = _GetCellGrad( - cell_fn, - cell_grad, - theta, - state0, - per_step_inputs, - accumulator_layer, - allow_implicit_capture=True) - self._cell_out_grad = cell_out_grad - self._theta = theta - self._state0 = state0 - self._accumulator_layer = accumulator_layer - self._in_links = in_links - self._padding = padding - self._slen_dim = slen_dim - self._per_step_inputs = per_step_inputs - self._extras = extras - assert self._extras is not None - self._out_links = out_links - self._unused_acc_state = unused_acc_state - - def Compute(self): - """Compute the middle layer.""" - - def MiddleFn(theta, state0, inputs): - del inputs - inputs = self._in_links.Transform(lambda l: l.fwd.Recv()) - state1, extras = self._cell_fn(theta, state0, inputs) - py_utils.AssertIsCompatible(state1, state0) - py_utils.AssertIsCompatible(extras, self._extras) - out = self._cell_out(state1) - sends = _Join(self._out_links, out, lambda l, x: l.fwd.Send(x)) - with tf.control_dependencies(sends): - return (state1.Transform(tf.identity), - py_utils.NestedMap(inputs=inputs, - cell_fn_extras=extras).Transform( - tf.identity)) - - def MiddleGrad(theta, state0, inputs, extras, dstate1): - """Gradient function for MiddleFn.""" - recv_dout = self._out_links.Transform(lambda l: l.bak.Recv()) - dstate1 = _Add(dstate1, self._cell_out_grad(recv_dout)) - dtheta, dstate0, dinputs, dcaptures = self._cell_grad( - theta, state0, extras.inputs, extras.cell_fn_extras, dstate1) # pylint: disable=unbalanced-tuple-unpacking - py_utils.AssertIsCompatible(dtheta, self._theta) - py_utils.AssertIsCompatible(dstate0, state0) - py_utils.AssertIsCompatible(dinputs, self._per_step_inputs) - if dcaptures is None: - # NOTE: Custom gradient fns can return None if they do not support - # captured tensors. The return value is reserved for the future when - # that may be supported. - dcaptures = _EmptyLike(self._implicit_captures) - py_utils.AssertIsCompatible(dcaptures, self._implicit_captures) - sends = _Join(self._in_links, dinputs, lambda l, x: l.bak.Send(x)) - with tf.control_dependencies(sends): - return (dtheta.Transform(tf.identity), dstate0.Transform(tf.identity), - inputs.Transform(tf.zeros_like), - dcaptures.Transform(tf.identity)) - - fake_inputs = py_utils.NestedMap( - fake_input=tf.zeros([self._slen_dim], tf.float32)) - if self._padding is not None: - fake_inputs['padding'] = self._padding - - return _Recurrent( - cell_fn=MiddleFn, - cell_grad=MiddleGrad, - stop_fn=None, - theta=self._theta, - state0=self._state0, - inputs=fake_inputs, - extras=py_utils.NestedMap( - inputs=self._per_step_inputs, cell_fn_extras=self._extras), - accumulator_layer=self._accumulator_layer, - implicit_captures=self._implicit_captures, - unused_acc_state=self._unused_acc_state).Compute() - - -class _Output: - """Output layers.""" - - def __init__(self, cell_fn, cell_grad, theta, state0, accumulator_layer, - in_links, padding, slen_dim, per_step_inputs, extras): - self._cell_fn = cell_fn - self._cell_grad, self._implicit_captures = _GetCellGrad( - cell_fn, - cell_grad, - theta, - state0, - per_step_inputs, - accumulator_layer, - allow_implicit_capture=True) - self._theta = theta - self._state0 = state0 - self._accumulator_layer = accumulator_layer - self._in_links = in_links - self._padding = padding - self._slen_dim = slen_dim - self._per_step_inputs = per_step_inputs - self._extras = extras - assert self._extras is not None - - def Compute(self): - """Compute the output layer.""" - - def OutputFn(theta, state0, inputs): - del inputs - inputs = self._in_links.Transform(lambda l: l.fwd.Recv()) - state1, extras = self._cell_fn(theta, state0, inputs) - py_utils.AssertIsCompatible(state1, state0) - py_utils.AssertIsCompatible(extras, self._extras) - return state1, py_utils.NestedMap(inputs=inputs, cell_fn_extras=extras) - - def OutputGrad(theta, state0, inputs, extras, dstate1): - """Gradient function for OutputFn.""" - dtheta, dstate0, dinputs, dcaptures = self._cell_grad( - theta, state0, extras.inputs, extras.cell_fn_extras, dstate1) # pylint: disable=unbalanced-tuple-unpacking - py_utils.AssertIsCompatible(dtheta, self._theta) - py_utils.AssertIsCompatible(dstate0, state0) - py_utils.AssertIsCompatible(dinputs, self._per_step_inputs) - if dcaptures is None: - # NOTE: Custom gradient fns can return None if they do not support - # captured tensors. The return value is reserved for the future when - # that may be supported. - dcaptures = _EmptyLike(self._implicit_captures) - py_utils.AssertIsCompatible(dcaptures, self._implicit_captures) - sends = _Join(self._in_links, dinputs, lambda l, x: l.bak.Send(x)) - with tf.control_dependencies(sends): - return (dtheta.Transform(tf.identity), dstate0.Transform(tf.identity), - inputs.Transform(tf.zeros_like), - dcaptures.Transform(tf.identity)) - - fake_inputs = py_utils.NestedMap( - fake_input=tf.zeros([self._slen_dim], tf.float32)) - if self._padding is not None: - fake_inputs['padding'] = self._padding - - return _Recurrent( - cell_fn=OutputFn, - cell_grad=OutputGrad, - stop_fn=None, - theta=self._theta, - state0=self._state0, - inputs=fake_inputs, - extras=py_utils.NestedMap( - inputs=self._per_step_inputs, cell_fn_extras=self._extras), - accumulator_layer=self._accumulator_layer, - implicit_captures=self._implicit_captures, - unused_acc_state=False).Compute() - - -def _DependsOn(xs, ys): - """Every x in xs should depend on every y in ys via a data edge.""" - - # TODO(zhifengc): Using the following ops is likely more robust because - # algebra simplifier may remove s - s, t + 0, etc. - # nil: list -> 0 - # first: x, list -> x - # - # If we have nil & first, we can write - # zero = nil(py_utils.Flatten(ys)) - # return [x.Transform(lambda t: first(t, zero)) for x in xs] - def MakeZero(x): - s = tf.reduce_sum(x) - return tf.cast(s - s, tf.float32) - - def SumToZero(nmap_list): - return tf.add_n([MakeZero(x) for x in py_utils.Flatten(nmap_list)]) - - ys_zero = SumToZero(ys) - return [x.Transform(lambda t: t + tf.cast(ys_zero, t.dtype)) for x in xs] - - -def StackedRecurrent(devices, - cell_fns, - cell_grads, - cell_outs, - cell_out_grads, - thetas, - init_states, - inputs, - accumulator_layers=None, - unused_acc_state=False): - """Computes stacked recurrent neural nets placed on various devices. - - Conceptually, StackedRecurrent() computes the following:: - - for (device, cell_fn, cell_out, cell_grad, theta, state0) in zip( - (devices, cell_fns, cell_outs, cell_grads, thetas, init_states): - with tf.device(device): - state1, _ = Recurrent(theta, state0, inputs, cell_fn, cell_grad) - outputs = cell_out(state1) - inputs = outputs # Next layer's input is this layer's output - return outputs - - The only difference is that StackedRecurrent implements a model parallelism - so that all layers computation can happen concurrently. - - Args: - devices: A list of N tensorflow device names. - cell_fns: If a list of N recurrent cell function, cell_fns[i] must meet the - same requirement as Recurrent() requires its cell_fn argument. Otherwise, - applies to all layers. - cell_grads: If a list of N recurrent cell gradient function, cell_grads[i] - must meet the same requirement as Recurrent() requires its cell_grad - argument. Otherwise, applies to all layers. - cell_outs: If a list of N function, cell_outs[i] takes the state computed by - cell_fns[i] and returns the input for the next layer. These functions are - expected to be simple and just do renaming of fields. Otherwise, applies - to all layers. - cell_out_grads: If a list of N function, cell_out_grads[i] is often the - reverse of cell_outs[i]. Otherwise, applies to all layers. - thetas: A list of N weights NestedMap. thetas[i] must meet the same - requirement as Recurrent() requires its theta argument. - init_states: A list of N initial state NestedMap. init_states[i] must meet - the same requirement as Recurrent() requires its state0 argument. - inputs: Inputs to the 1st layer of the stacked recurrent neural nets. A - NestedMap. - accumulator_layers: A list of layers whose accumulators will be managed such - that they carry to the output state in `FProp` and are disabled for - gradients. Uses the state key `accumulators`. Default to None where no - accumulator values will be carried. - unused_acc_state: If True, we shink all the layer's acc_state to [num_ts] - except the last layer(_Output). - - Returns: - Tuple (output, states): - - - The last layer's output (accumulated states). - - The list of final state NestedMap. One for each layer. - """ - # Enable rendezvous sharing when using tf.function, since it needs to do - # send/recv across function boundary. - # pylint: disable=protected-access - with py_utils._SharedRendezvousScope(shared_rendezvous=True): - return _StackedRecurrent(devices, cell_fns, cell_grads, cell_outs, - cell_out_grads, thetas, init_states, inputs, - accumulator_layers, unused_acc_state) - # pylint: enable=protected-access - - -def _StackedRecurrent(devices, cell_fns, cell_grads, cell_outs, cell_out_grads, - thetas, init_states, inputs, accumulator_layers, - unused_acc_state): - """Implementation of StackedRecurrent, see StackedRecurrent for details.""" - num_layers = len(devices) - assert num_layers - - def _MakeList(fns): - if not isinstance(fns, (list, tuple)): - return [fns] * num_layers - else: - assert num_layers == len(fns) - return fns - - cell_fns = _MakeList(cell_fns) - cell_grads = _MakeList(cell_grads) - cell_outs = _MakeList(cell_outs) - cell_out_grads = _MakeList(cell_out_grads) - accumulator_layers = accumulator_layers or [None] * num_layers - assert num_layers == len(thetas) - assert all(isinstance(x, py_utils.NestedMap) for x in thetas) - assert num_layers == len(init_states) - assert all(isinstance(x, py_utils.NestedMap) for x in init_states) - assert isinstance(inputs, py_utils.NestedMap) - - if py_utils.use_tpu(): - # If this error happens, the number of splits must be increased (e.g. - # worker_split_size in trainer/tpu.sh), or the number of rnn layers - # decreased. - # TODO(cwhipkey): lift this restriction by grouping layers by device and - # having a device handle a contiguous run of layers, and have them loop - # over the layers in the cell fns. - assert len(devices) == len(set(devices)), ( - 'StackedRecurrent must provide a different device for each layer ' - 'when run on TPU. devices passed were: %s' % str(devices)) - - if num_layers == 1: - # Simple case, just use Recurrent() directly. - with tf.device(devices[0]): - acc_states, final = Recurrent( - theta=thetas[0], - state0=init_states[0], - inputs=inputs, - cell_fn=cell_fns[0], - cell_grad=cell_grads[0], - accumulator_layer=accumulator_layers[0]) - # Just the accumulated states. - return cell_outs[0](acc_states), final - - # We add explicit data dependencies between layer-i's theta/state0 - # and layer-(i-1)'s theta/state0, layer-0's theta/state0 has an - # explicit data dependency on inputs. These extra data dependencies - # ensure that if layer-i's theta/state0 is used in tf.gradient, all - # layers above's backprop are triggered. - prev = [inputs] - for i in range(num_layers): - with tf.device(devices[i]): - thetas[i], init_states[i] = _DependsOn([thetas[i], init_states[i]], prev) - prev = [thetas[i], init_states[i]] - - def ExpectedOutputOfLayers(): - """Estimate what tensor dtypes and shapes output by each layer.""" - - def ZerosLikeRequireShape(t): - assert t.shape.is_fully_defined() - return tf.zeros_like(t) - - if py_utils.use_tpu(): - transform_fn = ZerosLikeRequireShape - else: - transform_fn = tf.zeros_like - - expected_output_by_layers = [] - xs = _Index(inputs, 0) - for i in range(num_layers): - # Disable accumulators and step_seed since this is not a real call to - # cell_fns[i]. They will be re-enabled in _Recurrent. - if accumulator_layers[i]: - accumulator_layers[i].accumulators.Transform(lambda x: x.Disable()) - step_seed = py_utils.GetStepSeed() - state1, extras = cell_fns[i](thetas[i], init_states[i], xs) - py_utils.ResetStepSeed(step_seed) - # only dtype and shape is needed. - xs = cell_outs[i](state1) - expected_output_by_layers += [ - py_utils.NestedMap( - xs=xs.Transform(transform_fn), - extras=extras.Transform(transform_fn)) - ] - return expected_output_by_layers - - expected_output_by_layers = ExpectedOutputOfLayers() - - # Sequence length. We assume it's a grid we are building. - slen_dim = _SeqLenDim(inputs) - - assert num_layers >= 2 - layers = [] - - padding = FlattenPadding(inputs.get('padding', None)) - - # Builds the input layer. - out_links = _CreateLinks(expected_output_by_layers[0].xs, - DevicePair(devices[0], devices[1])) - - # Enable accumulators. Note that this must happen prior to the initial - # _AugmentState() below or it will initialize with defaults. - for accumulator_layer in accumulator_layers: - if accumulator_layer: - accumulator_layer.accumulators.Transform(lambda x: x.Enable()) - - inp_l = _Input( - cell_fn=cell_fns[0], - cell_grad=cell_grads[0], - cell_out=cell_outs[0], - cell_out_grad=cell_out_grads[0], - theta=thetas[0], - state0=_AugmentState(init_states[0].DeepCopy(), accumulator_layers[0]), - accumulator_layer=accumulator_layers[0], - inputs=inputs, - extras=expected_output_by_layers[0].extras, - out_links=out_links, - unused_acc_state=unused_acc_state) - layers += [inp_l] - - # Builds the intermediate layers. - for i in range(1, num_layers - 1): - in_links = out_links - out_links = _CreateLinks(expected_output_by_layers[i].xs, - DevicePair(devices[i], devices[i + 1])) - mid_l = _Middle( - cell_fn=cell_fns[i], - cell_grad=cell_grads[i], - cell_out=cell_outs[i], - cell_out_grad=cell_out_grads[i], - theta=thetas[i], - state0=_AugmentState(init_states[i].DeepCopy(), accumulator_layers[i]), - accumulator_layer=accumulator_layers[i], - in_links=in_links, - padding=padding, - slen_dim=slen_dim, - per_step_inputs=expected_output_by_layers[i - 1].xs, - extras=expected_output_by_layers[i].extras, - out_links=out_links, - unused_acc_state=unused_acc_state) - layers += [mid_l] - - # Builds the final output layer. - in_links = out_links - del out_links - out_l = _Output( - cell_fn=cell_fns[-1], - cell_grad=cell_grads[-1], - theta=thetas[-1], - state0=_AugmentState(init_states[-1].DeepCopy(), accumulator_layers[-1]), - accumulator_layer=accumulator_layers[-1], - in_links=in_links, - padding=padding, - slen_dim=slen_dim, - per_step_inputs=expected_output_by_layers[-2].xs, - extras=expected_output_by_layers[-1].extras) - layers += [out_l] - - assert len(layers) == num_layers - - anchor = 0 - final_states = [] - for (dev, layer) in zip(devices, layers): - # Computes each layer on their designated device. - with tf.device(dev): - acc_states, final = layer.Compute() # Don't care of final state yet. - final_states.append(final) - - # We add every number output by the layer (s) and computes a - # zero scalar: (s - s), as an anchor. Anchors are added - # sequentially and added to the final layer's output. This way, - # we ensure that the final output depends on every previous - # layer through data dependencies. This is a hack to ensure that - # tf.gradient will follow some data dependencies path to start - # the Backward loop for each layer. - # - # TODO(zhifengc): We can write, if we have nil & first ops: - # anchor += [nil(py_utils.Flatten(acc_states))] - # And finally, - # return acc_states.Transform(lambda x: first(x, anchor)) - def ComputeAnchor(x): - # For each - s = tf.add_n([tf.reduce_sum(_) for _ in x.Flatten()]) - return s - s - - anchor = ComputeAnchor(acc_states) + anchor - - # The last layer's output is the real output that matters. However, - # to make the previous layers backprop work, we need to make sure - # the returned value has data dependencies on the previous layers. - # 'anchor' is guaranteed to be a scalar 0 and hence adding it to the - # final output does not change its numerical value. - with tf.device(devices[-1]): - outputs = cell_outs[-1](acc_states.Transform(lambda x: x + anchor)) - - # TODO(b/129159299): The ResetStepSeed below is needed to work around this - # bug, which is a problem with global tensors being shared by different - # inference graphs. It should be removed once the bug is fixed. - py_utils.ResetStepSeed( - py_utils.GenerateSeedFromName(tf.no_op(name='new_step_seed').name)) - - return outputs, final_states diff --git a/lingvo/core/recurrent_gpu_test.py b/lingvo/core/recurrent_gpu_test.py deleted file mode 100644 index be778c2f..00000000 --- a/lingvo/core/recurrent_gpu_test.py +++ /dev/null @@ -1,59 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo recurrent on gpu.""" - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import recurrent -from lingvo.core import test_utils - - -class RecurrentGpuTest(test_utils.TestCase): - - def testRecurrent(self): - """Run on GPU locally with --test_output=streamed --config=cuda.""" - - def Sum(theta, state, inputs): - next_state = py_utils.NestedMap() - v = tf.reduce_sum(tf.one_hot(inputs.one_hot, depth=2) * theta.x, axis=0) - next_state.sum = state.sum + v - return next_state, py_utils.NestedMap() - - with self.session(use_gpu=True): - - theta = py_utils.NestedMap() - theta.x = tf.constant([-1.0, 2.0]) - state = py_utils.NestedMap() - state.sum = tf.constant(0.0) - inputs = py_utils.NestedMap() - inputs.one_hot = tf.constant([0, 1, 1], dtype=tf.int32) - - # sum = -1 + 2 + 2 - ret = recurrent.Recurrent(theta, state, inputs, Sum) - - acc, state = self.evaluate(ret) - self.assertAllClose(acc.sum, [-1., 1., 3.]) - self.assertAllClose(state.sum, 3.) - - y = ret[1].sum - dx, d_inputs = tf.gradients(ys=[y], xs=[theta.x, inputs.one_hot]) - tf.logging.info('d(inputs) = %s', d_inputs) - dx_val = self.evaluate(dx) - self.assertAllClose(dx_val, [1, 2]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/recurrent_test.py b/lingvo/core/recurrent_test.py deleted file mode 100644 index f8302d98..00000000 --- a/lingvo/core/recurrent_test.py +++ /dev/null @@ -1,911 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for recurrent.""" - -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core import recurrent -from lingvo.core import symbolic -from lingvo.core import test_utils -import numpy as np - -FLAGS = tf.flags.FLAGS - - -def _ApplyPadding(padding, v_no_pad, v_pad): - if padding is not None: - padding = tf.cast(padding, v_no_pad.dtype) - return (1 - padding) * v_no_pad + padding * v_pad - return v_no_pad - - -def _ReferenceStaticUnroll(theta, state0, inputs, cell_fn): - """Statically unrolls a `cell_fn` wrt inputs as `recurrent.Recurrent()` does. - - This is not a complete implementation but should work reasonably for - calls that do not have a custom cell_grad, extras or accumulators and - can help check expectations. - - Args: - theta: weights. A `.NestedMap`. - state0: initial state. A `.NestedMap`. - inputs: inputs. A `.NestedMap`. Tensors must have static shape. - cell_fn: A python function, which computes:: - state1, extras = cell_fn(theta, state0, inputs[t, :]) - - Returns: - accumulate and final state. - """ - flat_inputs = inputs.Flatten() - flat_state = state0.Flatten() - step_count = flat_inputs[0].shape[0] - acc_state = [[None] * step_count for t in flat_state] - for i in range(step_count): - flat_step_inputs = [t[i] for t in flat_inputs] - step_inputs = inputs.Pack(flat_step_inputs) - state1, unused_extras = cell_fn(theta, state0, step_inputs) - for j, state_step in zip(range(len(acc_state[0])), state1.Flatten()): - acc_state[j][i] = state_step - state0 = state1 - return state0.Pack(acc_state), state1 - - -def _Poly(theta, state, inputs): - next_state = py_utils.NestedMap() - next_state.value = state.value + inputs.coeff * state.x_power - next_state.x_power = state.x_power * theta.x - return next_state, py_utils.NestedMap() - - -class _IncrementAccumulator(base_layer.Accumulator): - - def DefaultValue(self): - return tf.convert_to_tensor(0.0) - - def Update(self, increment_by): - initial = self.GetValue() - self.SetValue(initial + tf.convert_to_tensor(increment_by)) - - -class _SampleAccumulatorLayer(base_layer.BaseLayer): - - def __init__(self, params): - super().__init__(params) - self.accumulator_name = 'sample_accumulator' - self.RegisterAccumulator(self.accumulator_name, _IncrementAccumulator()) - - -def Elman(theta, state0, inputs): - h0, w, b, x = state0.h, theta.w, theta.b, inputs.x - xw = py_utils.Matmul(tf.concat([x, h0], axis=1), w) # 1st part - # 2nd part - padding = inputs.get('padding', None) - h1 = _ApplyPadding(padding, v_no_pad=tf.sigmoid(xw + b), v_pad=state0.h) - - state1 = py_utils.NestedMap(h=h1) - if padding is not None: - state1.padding = inputs.padding - - return (state1, py_utils.NestedMap(h=h1)) - - -def ElmanGrad(theta, state0, inputs, extras, dstate1): - - @tf.function(autograph=False) - def Grad(h0, w, b, x, padding, h1, dh1): - del b - dh1_orig = dh1 - dh1 = _ApplyPadding(padding, dh1, tf.zeros_like(dh1, dtype=dh1.dtype)) - - # We hand-roll the gradient for the 2nd half of the cell as a demo. - # h1 = tf.sigmoid(xw + b) - # 𝛔'(x) = ((1 - 𝛔(x)) * 𝛔(x)) - dxwb = (dh1 * (1 - h1) * h1) - dxw, db = dxwb, tf.reduce_sum(dxwb, axis=0) - - # Uses tf.gradient for the 1nd half of the cell as a demo. - xw = py_utils.Matmul(tf.concat([x, h0], axis=1), w) - dh0, dx, dw = tf.gradients(ys=[xw], xs=[h0, x, w], grad_ys=[dxw]) - - dh0 = _ApplyPadding(padding, dh0, dh1_orig) - - return dh0, dx, dw, db - - dh0, dx, dw, db = Grad(state0.h, theta.w, theta.b, inputs.x, - inputs.get('padding', 0), extras.h, dstate1.h) - dstate0 = py_utils.NestedMap(h=dh0) - dinputs = py_utils.NestedMap(x=dx) - if 'padding' in dstate1: - dstate0.padding = dstate1.padding - dinputs.padding = dstate1.padding - return (py_utils.NestedMap(w=dw, b=db), dstate0, dinputs, None) - - -def ElmanOut(state1): - return py_utils.NestedMap(x=state1.h, padding=state1.padding) - - -def ElmanOutGrad(dout): - return py_utils.NestedMap(h=dout.x, padding=dout.padding) - - -def RecurrentTestParameters(test_fn): - - def WrappedTestFn(self, use_tf_function): - # TODO(laigd): remove this check when 312743821 and 313682500 are in the - # release. - if use_tf_function and tf.compat.v1.__version__ < '2.3.0': - return - FLAGS.if_use_tf_function = use_tf_function - FLAGS.while_loop_use_tf_function = use_tf_function - FLAGS.call_defun_use_tf_function = use_tf_function - test_fn(self) - - decorator = parameterized.named_parameters( - ('_defun', False), - ('_function', True), - ) - return decorator(WrappedTestFn) - - -class RecurrentTest(test_utils.TestCase, parameterized.TestCase): - - @RecurrentTestParameters - def testBasic(self): - - with self.session(): - - theta = py_utils.NestedMap() - theta.x = tf.constant(2.0) - state = py_utils.NestedMap() - state.value = tf.constant(0.0) - state.x_power = tf.constant(1.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3.]) - - # x = 2 - # 1 + 2*x + 3*x^2 - ret = recurrent.Recurrent(theta, state, inputs, _Poly) - - acc, state = self.evaluate(ret) - self.assertAllClose(acc.value, [1., 5., 17.]) - self.assertAllClose(acc.x_power, [2., 4., 8.]) - self.assertAllClose(state.value, 17.) - self.assertAllClose(state.x_power, 8.) - - y = ret[1].value - dx, d_coeff = tf.gradients(ys=[y], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - - # 2 + 6*x - self.assertAllClose(dx_val, 14.) - self.assertAllClose(d_coeff_val, [1., 2., 4.]) - - # acc = [1, 1+2x, 1+2x+3x^2] - # sum(acc) = 3 + 4x + 3x^2 - acc = ret[0].value - dx, d_coeff = tf.gradients( - ys=[tf.reduce_sum(acc)], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - # 4 + 6*x - self.assertAllClose(dx_val, 16.) - self.assertAllClose(d_coeff_val, [3., 4., 4.]) - - @RecurrentTestParameters - def testBasicWithAccumulator(self): - - with self.session(): - - p = _SampleAccumulatorLayer.Params() - p.name = 'sample' - accum_layer = _SampleAccumulatorLayer(p) - accum_obj = accum_layer.accumulators[accum_layer.accumulator_name] - - theta = py_utils.NestedMap() - theta.x = tf.constant(2.0) - state = py_utils.NestedMap() - state.value = tf.constant(0.0) - state.x_power = tf.constant(1.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3.]) - - def _CellFn(theta, state, inputs): - print('TEST ACCUM WITHIN CellFn = ', accum_obj.GetValue()) - accum_obj.Update(inputs.coeff) - return _Poly(theta, state, inputs) - - # By doing one accumulate prior to recurrent, we ensure that incoming - # recurrent state is preserved. - accum_obj.Update(10.) - - # x = 2 - # 1 + 2*x + 3*x^2 - ret = recurrent.Recurrent( - theta, state, inputs, _CellFn, accumulator_layer=accum_layer) - - # Verify bprop. - y = ret[1].value - dx, d_coeff = tf.gradients(ys=[y], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - - # 2 + 6*x - self.assertAllClose(dx_val, 14.) - self.assertAllClose(d_coeff_val, [1., 2., 4.]) - - # acc = [1, 1+2x, 1+2x+3x^2] - # sum(acc) = 3 + 4x + 3x^2 - acc = ret[0].value - dx, d_coeff = tf.gradients( - ys=[tf.reduce_sum(acc)], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - # 4 + 6*x - self.assertAllClose(dx_val, 16.) - self.assertAllClose(d_coeff_val, [3., 4., 4.]) - - # Verify fprop. - (acc, state), accum_obj_value = self.evaluate((ret, accum_obj.GetValue())) - - # Verify that accumulators don't change fprop results. - self.assertAllClose(acc.value, [1., 5., 17.]) - self.assertAllClose(acc.x_power, [2., 4., 8.]) - self.assertAllClose(state.value, 17.) - self.assertAllClose(state.x_power, 8.) - - # Verify accumulator (should be 10 (initial increment) + 1 + 2 + 3). - self.assertEqual(0, accum_obj._disable_count) - self.assertAllClose([accum_obj_value], [16.0]) - - @RecurrentTestParameters - def testTimeBasedStopFn(self): - - with self.session(): - - def StopFn(t, unused_theta, unused_state): - # This stops after 3 iterations. - return t >= 3 - - theta = py_utils.NestedMap() - theta.x = tf.constant(2.0) - state = py_utils.NestedMap() - state.value = tf.constant(0.0) - state.x_power = tf.constant(1.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3., 4.]) - - # x = 2 - # 1 + 2*x + 3*x^2 - ret = recurrent.Recurrent(theta, state, inputs, _Poly, stop_fn=StopFn) - - acc, state = self.evaluate(ret) - self.assertAllClose([1., 5., 17., 0.], acc.value) - self.assertAllClose([2., 4., 8., 0.], acc.x_power) - self.assertAllClose(17., state.value) - self.assertAllClose(8., state.x_power) - - y = ret[1].value - dx, d_coeff = tf.gradients(ys=[y], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - - # 2 + 6*x - self.assertAllClose(14., dx_val) - self.assertAllClose([1., 2., 4., 0.], d_coeff_val) - - # acc = [1, 1+2x, 1+2x+3x^2] - # sum(acc) = 3 + 4x + 3x^2 - acc = ret[0].value - dx, d_coeff = tf.gradients( - ys=[tf.reduce_sum(acc)], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - # 4 + 6*x - self.assertAllClose(16., dx_val) - self.assertAllClose([3., 4., 4., 0.], d_coeff_val) - - @RecurrentTestParameters - def testStateBasedStopFn(self): - - with self.session(): - - def StopFn(unused_t, unused_theta, state): - # This stops after 3 iterations. - return state.value >= 15. - - theta = py_utils.NestedMap() - theta.x = tf.constant(2.0) - state = py_utils.NestedMap() - state.value = tf.constant(0.0) - state.x_power = tf.constant(1.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3., 4.]) - - # x = 2 - # 1 + 2*x + 3*x^2 - ret = recurrent.Recurrent(theta, state, inputs, _Poly, stop_fn=StopFn) - - acc, state = self.evaluate(ret) - self.assertAllClose([1., 5., 17., 0.], acc.value) - self.assertAllClose([2., 4., 8., 0.], acc.x_power) - self.assertAllClose(17., state.value) - self.assertAllClose(8., state.x_power) - - y = ret[1].value - dx, d_coeff = tf.gradients(ys=[y], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - - # 2 + 6*x - self.assertAllClose(14., dx_val) - self.assertAllClose([1., 2., 4., 0.], d_coeff_val) - - # acc = [1, 1+2x, 1+2x+3x^2] - # sum(acc) = 3 + 4x + 3x^2 - acc = ret[0].value - dx, d_coeff = tf.gradients( - ys=[tf.reduce_sum(acc)], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - # 4 + 6*x - self.assertAllClose(16., dx_val) - self.assertAllClose([3., 4., 4., 0.], d_coeff_val) - - @RecurrentTestParameters - def testStopFnNotTriggeredBeforeEOS(self): - - with self.session(): - - def StopFn(t, unused_theta, unused_state): - # The input sequence is only length 4, so this is never true. - # However, the Recurrent call should still terminate after iteration 4. - return t >= 5 - - theta = py_utils.NestedMap() - theta.x = tf.constant(2.0) - state = py_utils.NestedMap() - state.value = tf.constant(0.0) - state.x_power = tf.constant(1.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3., 4.]) - - # x = 2 - # 1 + 2*x + 3*x^2 + 4*x^3 - ret = recurrent.Recurrent(theta, state, inputs, _Poly, stop_fn=StopFn) - - acc, state = self.evaluate(ret) - self.assertAllClose([1., 5., 17., 49.], acc.value) - self.assertAllClose([2., 4., 8., 16.], acc.x_power) - self.assertAllClose(49., state.value) - self.assertAllClose(16., state.x_power) - - y = ret[1].value - dx, d_coeff = tf.gradients(ys=[y], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - - # 2 + 6*x + 12*x^2 - self.assertAllClose(62., dx_val) - self.assertAllClose([1., 2., 4., 8.], d_coeff_val) - - # acc = [1, 1+2x, 1+2x+3x^2, 1+2x+3x^2+4x^3] - # sum(acc) = 4 + 6x + 6x^2 + 4x^3 - acc = ret[0].value - dx, d_coeff = tf.gradients( - ys=[tf.reduce_sum(acc)], xs=[theta.x, inputs.coeff]) - dx_val, d_coeff_val = self.evaluate([dx, d_coeff]) - # 6 + 12*x + 12*x^2 - self.assertAllClose(78., dx_val) - self.assertAllClose([4., 6., 8., 8.], d_coeff_val) - - @RecurrentTestParameters - def testCapture(self): - - with self.session(): - - theta = py_utils.NestedMap() - theta.x = tf.constant(2.0) - state0 = py_utils.NestedMap() - state0.value = tf.constant(0.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3.]) - captured = tf.constant(1.2, name='captured_const') - - def CellFn(theta, state, inputs): - next_state = py_utils.NestedMap() - # Captured is pulled from outside of the function and captured via the - # internal Defun. - next_state.value = state.value + inputs.coeff * captured * theta.x - return next_state, py_utils.NestedMap() - - # Run static fprop reference implementation. - ref_acc, ref_staten = _ReferenceStaticUnroll(theta, state0, inputs, - CellFn) - ref_acc_values, ref_staten_values = self.evaluate((ref_acc, ref_staten)) - print('Ref fprop: acc =', ref_acc, ', stateN =', ref_staten) - print('Ref fprop values: acc =', ref_acc_values, ', stateN =', - ref_staten_values) - self.assertAllClose(ref_acc_values.value, [2.4, 7.2, 14.4]) - self.assertAllClose(ref_staten_values.value, 14.4) - - # Run real fprop implementation. - real_acc, real_staten = recurrent.Recurrent( - theta, state0, inputs, CellFn, allow_implicit_capture=True) - real_acc_values, real_staten_values = self.evaluate( - (real_acc, real_staten)) - print('Real fprop: acc =', real_acc, ', stateN =', real_staten) - print('Real fprop values: acc =', real_acc_values, ', stateN =', - real_staten_values) - self.assertAllClose(ref_acc_values.value, real_acc_values.value) - self.assertAllClose(ref_staten_values.value, real_staten_values.value) - - # BProp real vs ref of stateN. - ref_dx, ref_dcaptured = tf.gradients( - ys=[ref_staten.value], xs=[theta.x, captured]) - ref_dx_values, ref_dcaptured_values = self.evaluate( - [ref_dx, ref_dcaptured]) - real_dx, real_dcaptured = tf.gradients( - ys=[real_staten.value], xs=[theta.x, captured]) - real_dx_values, real_dcaptured_values = self.evaluate( - [real_dx, real_dcaptured]) - print('Ref Dstate/[dx,dcaptured] =', ref_dx_values, ', ', - ref_dcaptured_values) - print('Real Dstate/[dx,dcaptured] =', real_dx_values, ', ', - real_dcaptured_values) - self.assertAllClose(ref_dx_values, 7.2) - self.assertAllClose(ref_dcaptured_values, 12.0) - self.assertAllClose(ref_dx_values, real_dx_values) - self.assertAllClose(ref_dcaptured_values, real_dcaptured_values) - - @RecurrentTestParameters - def testCaptureDisallowed(self): - - with self.session() as unused_sess: - - theta = py_utils.NestedMap() - theta.x = tf.constant(2.0) - state0 = py_utils.NestedMap() - state0.value = tf.constant(0.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3.]) - captured = tf.constant(1.2, name='captured_const') - - def CellFn(theta, state, inputs): - next_state = py_utils.NestedMap() - # Captured is pulled from outside of the function and captured via the - # internal Defun. - next_state.value = state.value + inputs.coeff * captured * theta.x - return next_state, py_utils.NestedMap() - - # Run real fprop implementation. - with self.assertRaisesRegex(ValueError, 'implicit capture is disabled'): - unused_real_acc, unused_real_staten = recurrent.Recurrent( - theta, state0, inputs, CellFn, allow_implicit_capture=False) - - @RecurrentTestParameters - def testSymbolToTensorMap(self): - """Tests that cell_fn can rely on the contextual symbol-to-tensor map.""" - - x = symbolic.Symbol('x') - y = symbolic.Symbol('y') - - def PlusWXT(theta, state, inputs): - """state.value += theta.w * x * inputs.t.""" - next_state = py_utils.NestedMap() - x_tensor = symbolic.EvalExpr(symbolic.TENSOR_VALUES, x) - next_state.value = state.value + theta.w * x_tensor * inputs.t - return next_state, py_utils.NestedMap() - - def PlusWXTGrad(theta, state0, inputs, extras, dstate1): - """Gradient function for PlusWXT.""" - del state0, extras - x_tensor = symbolic.EvalExpr(symbolic.TENSOR_VALUES, x) - dtheta = py_utils.NestedMap(w=dstate1.value * x_tensor * inputs.t) - dstate0 = py_utils.NestedMap(value=dstate1.value) - dinputs = py_utils.NestedMap(t=dstate1.value * theta.w * x_tensor) - return dtheta, dstate0, dinputs, None - - with self.session(): - theta = py_utils.NestedMap(w=tf.constant(1., name='w')) - state0 = py_utils.NestedMap(value=tf.constant(0., name='value')) - inputs = py_utils.NestedMap(t=tf.constant([1., 2., 3.], name='t')) - - # With automatic cell_grad. - with symbolic.SymbolToValueMap(symbolic.TENSOR_VALUES, { - x: tf.constant(7., name='x7'), - y: 8 - }): - x_tensor = symbolic.EvalExpr(symbolic.TENSOR_VALUES, x) - _, state1 = recurrent.Recurrent(theta, state0, inputs, PlusWXT) - dw = tf.gradients(ys=[state1.value], xs=[theta.w])[0] - dx = tf.gradients(ys=[state1.value], xs=[x_tensor])[0] - final_value, x_val, dx_val, dw_val = self.evaluate( - [state1.value, x_tensor, dx, dw]) - self.assertEqual(x_val, 7) - self.assertEqual(final_value, x_val * (1. + 2. + 3.)) - self.assertEqual(dw_val, x_val * (1. + 2. + 3.)) - self.assertEqual(dx_val, (1. + 2. + 3.)) - - # With manual cell_grad. - with symbolic.SymbolToValueMap(symbolic.TENSOR_VALUES, - {x: tf.constant(5., name='x5')}): - x_tensor = symbolic.EvalExpr(symbolic.TENSOR_VALUES, x) - _, state1 = recurrent.Recurrent( - theta, state0, inputs, PlusWXT, cell_grad=PlusWXTGrad) - dw = tf.gradients(ys=[state1.value], xs=[theta.w])[0] - dx = tf.gradients(ys=[state1.value], xs=[x_tensor])[0] - final_value, x_val, dx_val, dw_val = self.evaluate( - [state1.value, x_tensor, dx, dw]) - self.assertEqual(x_val, 5) - self.assertEqual(final_value, x_val * (1. + 2. + 3.)) - self.assertEqual(dw_val, x_val * (1. + 2. + 3.)) - self.assertEqual(dx_val, (1. + 2. + 3.)) - - @RecurrentTestParameters - def testStatefulCellFn(self): - - def Rand(theta, state, inputs): - del theta - next_state = py_utils.NestedMap() - next_state.value = ( - state.value + - inputs.coeff * tf.random.uniform(shape=[], dtype=state.value.dtype)) - return next_state, py_utils.NestedMap() - - with self.session(): - theta = py_utils.NestedMap() - state = py_utils.NestedMap() - state.value = tf.constant(0.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3.]) - - with self.assertRaisesRegex(ValueError, 'stateful.*random_uniform'): - recurrent.Recurrent(theta, state, inputs, Rand, check_stateful_ops=True) - - @RecurrentTestParameters - def testNestedCellFn(self): - """Tests when cell_fn calls another function.""" - - @tf.function(autograph=False) - def RandWithCoeff(coeff): - return coeff * tf.random.uniform(shape=[], dtype=coeff.dtype) - - def Rand(theta, state, inputs): - del theta - next_state = py_utils.NestedMap() - next_state.value = state.value + RandWithCoeff(inputs.coeff) - return next_state, py_utils.NestedMap() - - @tf.function(autograph=False) - def Coeff(coeff): - return coeff * 2 - - def Deterministic(theta, state, inputs): - del theta - next_state = py_utils.NestedMap() - next_state.value = state.value + Coeff(inputs.coeff) - return next_state, py_utils.NestedMap() - - with self.session(): - theta = py_utils.NestedMap() - state = py_utils.NestedMap() - state.value = tf.constant(0.0) - inputs = py_utils.NestedMap() - inputs.coeff = tf.constant([1., 2., 3.]) - - recurrent.Recurrent( - theta, state, inputs, Deterministic, check_stateful_ops=True) - with self.assertRaisesRegex(ValueError, 'cell_fn contains stateful ops:'): - recurrent.Recurrent(theta, state, inputs, Rand, check_stateful_ops=True) - - @RecurrentTestParameters - def testSeqLenActual(self): - for value, expected in [([[1.0], [1.0], [1.0]], - [0, 3]), ([[1.0], [1.0], [0.0]], - [2, 0]), ([[1.0], [0.0], [1.0]], [1, 1]), - ([[1.0], [0.0], [0.0]], - [1, 0]), ([[0.0], [1.0], [1.0]], - [0, 2]), ([[0.0], [1.0], [0.0]], [0, 0]), - ([[0.0], [0.0], [1.0]], [0, 1]), ([[0.0], [0.0], - [0.0]], [0, 0])]: - with self.session(): - inputs = py_utils.NestedMap() - inputs.padding = tf.constant(value) - - slen_op = recurrent._SeqPaddingLength(inputs) - slen, = self.evaluate([slen_op]) - self.assertEqual(expected, slen) - - @staticmethod - def Rand(shape): - return tf.random.uniform(shape, minval=-0.2, maxval=0.2, dtype=tf.float64) - - def _testElmanHelper(self, seqlen, use_grad, stop_fn=None): - with self.session(): - tf.random.set_seed(342462) - - batch = 3 - dims = 4 - theta = py_utils.NestedMap() - theta.w = self.Rand([2 * dims, dims]) - theta.b = self.Rand([dims]) - state0 = py_utils.NestedMap() - state0.h = self.Rand([batch, dims]) - inputs = py_utils.NestedMap() - inputs.x = self.Rand([seqlen, batch, dims]) - - # Static unrolled. - s = state0 - out = [] - for i in range(seqlen): - inp = py_utils.NestedMap() - inp.x = inputs.x[i, :] - s, _ = Elman(theta, s, inp) - out += [s.h] - if stop_fn and stop_fn(i + 1, theta, s): - out += [tf.zeros_like(out[-1]) for _ in range(seqlen - i - 1)] - break - acc0, final0 = tf.stack(out), s.h - loss0 = tf.reduce_sum(acc0) + tf.reduce_sum(final0) - (dw0, db0, dh0, - di0) = tf.gradients(loss0, [theta.w, theta.b, state0.h, inputs.x]) - - # Uses the Recurrent() library. - acc1, final1 = recurrent.Recurrent( - theta=theta, - state0=state0, - inputs=inputs, - cell_fn=Elman, - cell_grad=ElmanGrad if use_grad else None, - stop_fn=stop_fn) - acc1, final1 = acc1.h, final1.h - loss1 = tf.reduce_sum(acc1) + tf.reduce_sum(final1) - (dw1, db1, dh1, - di1) = tf.gradients(loss1, [theta.w, theta.b, state0.h, inputs.x]) - - # Fetches a bunch of values and compare them. - (acc0, acc1, final0, final1, dw0, dw1, db0, db1, dh0, dh1, di0, - di1) = self.evaluate( - [acc0, acc1, final0, final1, dw0, dw1, db0, db1, dh0, dh1, di0, di1]) - self.assertAllClose(acc0, acc1) - self.assertAllClose(final0, final1) - self.assertAllClose(dw0, dw1) - self.assertAllClose(db0, db1) - self.assertAllClose(dh0, dh1) - self.assertAllClose(di0, di1) - - @RecurrentTestParameters - def testElman(self): - self._testElmanHelper(1, False) - self._testElmanHelper(1, True) - self._testElmanHelper(7, False) - self._testElmanHelper(7, True) - - def StopFn(t, unused_theta, unused_state): - return t >= 4 - - self._testElmanHelper(7, False, StopFn) - self._testElmanHelper(7, True, StopFn) - - @RecurrentTestParameters - def testSetShape(self): - dst = py_utils.NestedMap( - a=tf.placeholder(tf.int32, shape=None), - b=py_utils.NestedMap( - b1=tf.placeholder(tf.int32, shape=None), - b2=tf.placeholder(tf.int32, shape=None))) - src = py_utils.NestedMap( - a=tf.constant(0, shape=[2, 4], dtype=tf.int32), - b=py_utils.NestedMap( - b1=tf.constant(0, shape=[1, 3], dtype=tf.int32), - b2=tf.constant(0, shape=[5, 8], dtype=tf.int32))) - py_utils.SetShapes(dst, src) - self.assertAllClose( - [2, 4], - py_utils.GetShape(dst.a, 2), - ) - self.assertAllClose([1, 3], py_utils.GetShape(dst.b.b1, 2)) - self.assertAllClose([5, 8], py_utils.GetShape(dst.b.b2, 2)) - - -class StackedRecurrentTest(test_utils.TestCase, parameterized.TestCase): - - @staticmethod - def Poly(theta, state0, inputs): - x = theta.x - s = state0.s - c = inputs.c - return py_utils.NestedMap(s=s * x + c), py_utils.NestedMap() - - @staticmethod - def Identity(theta, state0, inputs): - del theta, state0 - return py_utils.NestedMap(s=inputs.s), py_utils.NestedMap() - - @RecurrentTestParameters - def testSimpleStacked(self): - g = tf.Graph() - with g.as_default(): - devices = ['/cpu:0'] * 3 - cell_fns = [self.Poly, self.Identity, self.Identity] - cell_grads = [None] * 3 - cell_outs = [lambda x: x] * 3 - cell_out_grads = [lambda x: x] * 3 - w0 = tf.constant(2.) - w1 = tf.constant(0.) - w2 = tf.constant(0.) - thetas = [ - py_utils.NestedMap(x=w0), - py_utils.NestedMap(x=w1), - py_utils.NestedMap(x=w2) - ] - init_states = [py_utils.NestedMap(s=tf.constant(0.))] * 3 - inputs = py_utils.NestedMap( - c=tf.constant([1., 2., 1., 0.]), - padding=tf.constant([0., 0., 0., 1.])) - output, _ = recurrent.StackedRecurrent( - devices=devices, - cell_fns=cell_fns, - cell_grads=cell_grads, - cell_outs=cell_outs, - cell_out_grads=cell_out_grads, - thetas=thetas, - init_states=init_states, - inputs=inputs) - dw0, dw1, dw2 = tf.gradients(tf.reduce_sum(output.s), [w0, w1, w2]) - - with self.session(graph=g): - (output, dw0, dw1, dw2) = self.evaluate([output.s, dw0, dw1, dw2]) - - self.assertAllClose(output, [1., 4., 9., 0.]) - self.assertAllClose(dw2, 0.) - self.assertAllClose(dw1, 0.) - self.assertAllClose(dw0, 7.) - - def _BuildStackedRecurrentElman(self, seqlen, trailing_pad_len, batch, dims, - layers): - tf.random.set_seed(342462) - np.random.seed(32540) - - seqlen += trailing_pad_len - dtype = tf.float64 - - def CreateTheta(): - return py_utils.NestedMap( - w=tf.constant( - np.random.uniform(0, 0.2, (2 * dims, dims)), dtype=dtype), - b=tf.constant(np.random.uniform(0, 0.2, (dims,)), dtype=dtype)) - - def CreateState0(): - return py_utils.NestedMap( - h=tf.constant(np.random.uniform(0, 0.2, (batch, dims)), dtype=dtype), - padding=tf.constant([[0]] * batch, dtype=dtype)) - - devices = ['/cpu:0'] * layers - cell_fns = [Elman] * layers - cell_grads = [ElmanGrad] * layers - cell_outs = [ElmanOut] * layers - cell_out_grads = [ElmanOutGrad] * layers - thetas = [CreateTheta() for _ in range(layers)] - init_states = [CreateState0() for _ in range(layers)] - padding = np.zeros((seqlen, batch, 1)) - padding[-trailing_pad_len:, :, :] = 1. - padding[-trailing_pad_len - 3:-trailing_pad_len - 1, :, :] = 1. - inputs = py_utils.NestedMap( - x=tf.constant( - np.random.uniform(0, 0.2, (seqlen, batch, dims)), dtype=dtype), - padding=tf.constant(padding, dtype=dtype)) - output, _ = recurrent.StackedRecurrent( - devices=devices, - cell_fns=cell_fns, - cell_grads=cell_grads, - cell_outs=cell_outs, - cell_out_grads=cell_out_grads, - thetas=thetas, - init_states=init_states, - inputs=inputs) - o = output.x - if 'padding' in inputs: - o *= (1 - inputs.padding) - loss = tf.reduce_sum(tf.square(o)) - - xs = py_utils.Flatten(thetas + [py_utils.NestedMap(x=inputs.x)]) - dxs = tf.gradients(ys=loss, xs=xs) - - # Reference implementation using Recurrent(). - ref = inputs - for i in range(layers): - ref = ElmanOut( - recurrent.Recurrent( - cell_fn=cell_fns[i], - cell_grad=cell_grads[i], - theta=thetas[i], - state0=init_states[i], - inputs=ref)[0]) - return ref.x, output.x, loss, xs, dxs - - def _LogDiff(self, x, y): - tf.logging.info('max(abs(x - y)) = %s', np.max(np.abs(x - y))) - - def _CompareStackedElman(self, seqlen, batch, dims, layers): - """Tests that StackedRecurrent computest the same output as Recurrent().""" - trailing_pad_len = 2 - g = tf.Graph() - with g.as_default(): - ref, output, _, _, _ = self._BuildStackedRecurrentElman( - seqlen, trailing_pad_len, batch, dims, layers) - ref = ref[:-trailing_pad_len] - output = output[:-trailing_pad_len] - with self.session(graph=g): - ref_val, out_val = self.evaluate([ref, output]) - self._LogDiff(ref_val, out_val) - self.assertAllClose(ref_val, out_val) - - @RecurrentTestParameters - def testStackedElman_2(self): - self._CompareStackedElman(4, 3, 8, 2) - - @RecurrentTestParameters - def testStackedElman_4(self): - self._CompareStackedElman(8, 5, 8, 4) - - @RecurrentTestParameters - def testStackedElman_8(self): - self._CompareStackedElman(11, 1, 4, 8) - - def _TestStackedElmanGradient(self, num, seqlen=7, batch=5): - """Tests a stacked Elman recurrent network with num layers.""" - g = tf.Graph() - with g.as_default(): - # Sequence length, batdh size, hidden dimension - trailing_pad_len, dims, layers = 2, 8, num - _, _, loss, xs, dxs = self._BuildStackedRecurrentElman( - seqlen, trailing_pad_len, batch, dims, layers) - - # Fetches all gradients (dxs) in one session run and compare - # them with their respective numerical gradient. - with self.session(graph=g) as sess: - s_dxs = self.evaluate(dxs) - for (x, s_dx) in zip(xs, s_dxs): - n_dx = test_utils.ComputeNumericGradient(sess, loss, x) - self._LogDiff(n_dx, s_dx) - self.assertAllClose(n_dx, s_dx) - - # Randomly pick a few (x, dx) pairs, and fetch dx via one self.evaluate - # and compare with its numerical gradient. - xs_dxs = list(zip(xs, dxs)) - np.random.shuffle(xs_dxs) - with self.session(graph=g) as sess: - for (x, dx) in xs_dxs[:4]: - s_dx = self.evaluate(dx) - n_dx = test_utils.ComputeNumericGradient(sess, loss, x) - self._LogDiff(n_dx, s_dx) - self.assertAllClose(n_dx, s_dx) - - @RecurrentTestParameters - def testStackedElmanGrad_1(self): - self._TestStackedElmanGradient(1) - - @RecurrentTestParameters - def testStackedElmanGrad_2(self): - self._TestStackedElmanGradient(2) - - @RecurrentTestParameters - def testStackedElmanGrad_4(self): - self._TestStackedElmanGradient(4) - - @RecurrentTestParameters - def testStackedElmanGrad_8(self): - self._TestStackedElmanGradient(8, seqlen=5, batch=3) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/retry.py b/lingvo/core/retry.py deleted file mode 100644 index f3232e0b..00000000 --- a/lingvo/core/retry.py +++ /dev/null @@ -1,71 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Retry on exception.""" - -import functools -import itertools -import random -import sys -import time -import traceback - -import lingvo.compat as tf - - -def Retry(retry_value=Exception, - max_retries=None, - initial_delay_sec=1.0, - delay_growth_factor=1.5, - delay_growth_fuzz=0.1, - max_delay_sec=60): - """Returns a retry decorator.""" - if max_retries is None: - max_retries = 2**30 # Effectively forever. - - if delay_growth_factor < 1.0: - raise ValueError("Invalid delay_growth_factor: %f" % delay_growth_factor) - - def _Retry(func): - - @functools.wraps(func) - def Wrapper(*args, **kwargs): - """Decorator wrapper.""" - delay = initial_delay_sec - for retries in itertools.count(0): - try: - return func(*args, **kwargs) - except retry_value as e: - if retries >= max_retries: - raise - time.sleep(delay) - fuzz_factor = 1.0 + random.random() * delay_growth_fuzz - delay += delay * (delay_growth_factor - 1) * fuzz_factor - delay = min(delay, max_delay_sec) - - e_desc_str = "".join(traceback.format_exception_only(e.__class__, e)) - stack_traceback_str = "".join(traceback.format_stack()[:-2]) - e_traceback = sys.exc_info()[2] - e_traceback_str = "".join(traceback.format_tb(e_traceback)) - tf.logging.info( - "Retry: caught exception: %s while running %s. " - "Call failed at (most recent call last):\n%s" - "Traceback for above exception (most recent call last):\n%s" - "Waiting for %.2f seconds before retrying.", func.__name__, - e_desc_str, stack_traceback_str, e_traceback_str, delay) - - return Wrapper - - return _Retry diff --git a/lingvo/core/retry_test.py b/lingvo/core/retry_test.py deleted file mode 100644 index d7f660c4..00000000 --- a/lingvo/core/retry_test.py +++ /dev/null @@ -1,90 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for retry.""" - -import lingvo.compat as tf -from lingvo.core import retry -from lingvo.core import test_utils - - -class RetryTest(test_utils.TestCase): - - def testRetry(self): - - @retry.Retry(max_retries=5) - def _TestFunc(): - _TestFunc.calls += 1 - raise ValueError() - - _TestFunc.calls = 0 - - with self.assertRaises(ValueError): - _TestFunc() - self.assertEqual(_TestFunc.calls, 6) - - def testZeroMaxRetries(self): - - @retry.Retry(max_retries=0) - def _TestFunc(): - _TestFunc.calls += 1 - raise ValueError() - - _TestFunc.calls = 0 - - with self.assertRaises(ValueError): - _TestFunc() - self.assertEqual(_TestFunc.calls, 1) - - def testArgsAndReturnValue(self): - - @retry.Retry() - def _TestFunc(val): - if _TestFunc.calls > 2: - return val - else: - _TestFunc.calls += 1 - raise ValueError() - - _TestFunc.calls = 0 - - self.assertEqual(_TestFunc(2), 2) - self.assertEqual(_TestFunc.calls, 3) - - def testSpecificExceptions(self): - - @retry.Retry(max_retries=1, retry_value=(ValueError, KeyError)) - def _TestFunc(e): - _TestFunc.calls += 1 - raise e() - - _TestFunc.calls = 0 - with self.assertRaises(ValueError): - _TestFunc(ValueError) - self.assertEqual(_TestFunc.calls, 2) - - _TestFunc.calls = 0 - with self.assertRaises(KeyError): - _TestFunc(KeyError) - self.assertEqual(_TestFunc.calls, 2) - - _TestFunc.calls = 0 - with self.assertRaises(AttributeError): - _TestFunc(AttributeError) - self.assertEqual(_TestFunc.calls, 1) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/reversible_layers.py b/lingvo/core/reversible_layers.py deleted file mode 100644 index 710829ff..00000000 --- a/lingvo/core/reversible_layers.py +++ /dev/null @@ -1,233 +0,0 @@ -# Lint as: python3 -# coding=utf-8 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Reversible layers for memory efficient Backpropagation. - -[1] The Reversible Residual Network: Backpropagation Without Storing Activations - https://arxiv.org/abs/1707.04585 -""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils - - -class RevNetLayer(base_layer.BaseLayer): - """A reversible residual block. - - Computes y1 = x1 + f(x2), y2 = x2 + g(y1), where f and g can be arbitrary - functions that retain the input tensor shape. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('f_params', None, 'Layer params for the f block.') - p.Define('g_params', None, 'Layer params for the g block.') - return p - - def __init__(self, params): - super().__init__(params) - p = params - assert p.name - assert p.f_params - assert p.g_params - self.CreateChild('f_block', p.f_params) - self.CreateChild('g_block', p.g_params) - - def ReverseAndGrad(self, theta, outputs, d_outputs, f_seed, g_seed, - *extra_inputs): - """Implements Algorithm 1 in the revnet paper. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - outputs: A NestedMap: .split1 and .split2 corresponding to y1 and y2. - d_outputs: A NestedMap: .split1 and .split2 corresponding to dy1 and dy2, - the total derivatives. - f_seed: Scalar tensor. The step seed used in forward for the f block. - g_seed: Scalar tensor. The step seed used in forward for the g block. The - step seeds are needed for deterministic randomness, e.g. to ensure - dropout generate the same random mask in forward and reverse_grad. - *extra_inputs: additional inputs that will be passed to both f and g. No - gradient will be computed for these inputs. - - Returns: - A tuple of NestedMaps - - - inputs: .split1 and .split2 corresponding to x1 and x2. - - d_inputs: .split1 and .split2 corresponding to dx1 and dx2, the total - derivatives with respect to inputs. - - d_theta: has the same structure as theta. The total derivatives with - respect to weights. - - """ - - # Stop gradient on the outputs to avoid circular symbolic dependency. - y1 = tf.stop_gradient(outputs.split1) - y2 = tf.stop_gradient(outputs.split2) - dy1 = d_outputs.split1 - dy2 = d_outputs.split2 - - # Computes the reverse. - z1 = y1 - py_utils.ResetStepSeed(g_seed) - gz1 = self.g_block.FProp(theta.g_block, z1, *extra_inputs) - x2 = y2 - gz1 - py_utils.ResetStepSeed(f_seed) - fx2 = self.f_block.FProp(theta.f_block, x2, *extra_inputs) - x1 = z1 - fx2 - - # Computes the gradients. - dz1 = dy1 + tf.gradients(gz1, z1, dy2)[0] - dx2 = dy2 + tf.gradients(fx2, x2, dz1)[0] - - dgw = tf.gradients( - gz1, - theta.g_block.Flatten(), - dy2, - unconnected_gradients=tf.UnconnectedGradients.ZERO) - dgw = theta.g_block.Pack(dgw) - - dfw = tf.gradients( - fx2, - theta.f_block.Flatten(), - dz1, - unconnected_gradients=tf.UnconnectedGradients.ZERO) - dfw = theta.f_block.Pack(dfw) - - return (py_utils.NestedMap(split1=x1, split2=x2), - py_utils.NestedMap(split1=dz1, split2=dx2), - py_utils.NestedMap( - f_block=dfw, - g_block=dgw, - global_step=tf.zeros_like(theta.global_step))) - - def FProp(self, theta, inputs, *extra_inputs): - """Forward pass. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - inputs: A NestedMap: .split1 and .split2 corresponding to x1 and x2. - *extra_inputs: additional inputs that will be passed to both f and g. No - gradient will be computed for these inputs. - - Returns: - outputs: A NestedMap: .split1 and .split2 corresponding to y1 and y2. - f_seed: Scalar tensor. The step seed used in forward for the f block. - g_seed: Scalar tensor. The step seed used in forward for the g block. - - """ - - f_seed = py_utils.GetStepSeed() - f_out = self.f_block.FProp(theta.f_block, inputs.split2, *extra_inputs) - z1 = inputs.split1 + f_out - g_seed = py_utils.GetStepSeed() - g_out = self.g_block.FProp(theta.g_block, z1, *extra_inputs) - y2 = inputs.split2 + g_out - # This is essential to make dy1 independent to y2. - y1 = tf.identity(z1) - return py_utils.NestedMap(split1=y1, split2=y2), f_seed, g_seed - - -class StackedRevNetLayer(base_layer.BaseLayer): - """Stacked RevNet layers with custom gradient. - - The standard backpropagation has peak memory footprint of - - Θ(num_layers x (activation_size_per_layer + param_size_per_layer)), - - which is reduced to - - Θ(activation_size_per_layer + num_layers x param_size_per_layer)), - - in the custom gradient implmentation, at the cost of extra computation for the - reverse and potential accumulated numerical errors. See Section 3.2 in the - revnet paper for the full discussion. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('sub_layer_params', [], 'A list of RevNetLayer params.') - p.Define( - 'custom_gradient', True, 'If True, use the custom gradient over' - 'the standard TF gradient. Useful for unit test and benchmarks') - return p - - def __init__(self, params): - super().__init__(params) - p = params - assert p.name - assert p.sub_layer_params - self.CreateChildren('sub_layers', p.sub_layer_params) - - def FProp(self, theta, inputs, *extra_inputs): - - initial_step_seed = py_utils.GetStepSeed() - final_step_seed = py_utils.GenerateSeedFromName( - tf.no_op(name='new_step_seed').name) - num_layers = len(self.sub_layers) - - def Bak(inputs, outputs, d_outputs): - """Backward step.""" - del inputs # unused - output_acts, step_seeds = outputs - d_outputs = d_outputs[0] - - d_layer_thetas = [] - for layer_idx in reversed(range(num_layers)): - f_seed, g_seed = step_seeds[layer_idx] - layer = self.sub_layers[layer_idx] - layer_theta = theta.sub_layers[layer_idx] - - input_acts, d_inputs, d_theta = layer.ReverseAndGrad( - layer_theta, output_acts, d_outputs, f_seed, g_seed, *extra_inputs) - - d_layer_thetas.append(d_theta) - # Passes reconstructed inputs to the previous layer. - output_acts = input_acts - d_outputs = d_inputs - py_utils.ResetStepSeed(final_step_seed) - d_theta = py_utils.NestedMap(global_step=tf.zeros_like(initial_step_seed)) - d_theta.sub_layers = list(reversed(d_layer_thetas)) - - extra_grads = [tf.zeros_like(t) for t in extra_inputs] - return [tf.zeros_like(initial_step_seed), d_theta, d_inputs, extra_grads] - - def Fwd(xs): - """Forward pass.""" - initial_step_seed, theta, acts, extra_inputs = xs - - py_utils.ResetStepSeed(initial_step_seed) - layer_step_seeds = [] - - for layer_theta, layer in zip(theta.sub_layers, self.sub_layers): - acts, f_seed, g_seed = layer.FProp(layer_theta, acts, *extra_inputs) - layer_step_seeds += [(f_seed, g_seed)] - return [acts, layer_step_seeds] - - if self.params.custom_gradient: - acts, _ = py_utils.CallDefun( - Fwd, [initial_step_seed, theta, inputs, extra_inputs], Bak) - py_utils.ResetStepSeed(final_step_seed) - return acts - else: - acts = inputs - for layer_theta, layer in zip(theta.sub_layers, self.sub_layers): - acts, _, _ = layer.FProp(layer_theta, acts, *extra_inputs) - return acts diff --git a/lingvo/core/reversible_layers_test.py b/lingvo/core/reversible_layers_test.py deleted file mode 100644 index 2fc78f99..00000000 --- a/lingvo/core/reversible_layers_test.py +++ /dev/null @@ -1,191 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for reversible layers.""" - -from absl.testing import parameterized -from lingvo import compat as tf -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import reversible_layers -from lingvo.core import test_utils - -import numpy as np - -FLAGS = tf.flags.FLAGS - - -class RevNetLayerTest(test_utils.TestCase): - """Test single revnet layer.""" - - def testRevNetLayerFProp(self): - with self.session(): - tf.random.set_seed(321) - input_1 = tf.random.normal([5, 3], seed=89122) - input_2 = tf.random.normal([5, 3], seed=19438) - p = reversible_layers.RevNetLayer.Params() - p.name = 'revnet_simple' - p.f_params = layers.FCLayer.Params().Set(input_dim=3, output_dim=3) - p.g_params = layers.FCLayer.Params().Set(input_dim=3, output_dim=3) - revnet_layer = p.Instantiate() - - h, _, _ = revnet_layer.FPropDefaultTheta( - py_utils.NestedMap(split1=input_1, split2=input_2)) - self.evaluate(tf.global_variables_initializer()) - actual_layer_output = self.evaluate(h) - expected_split1 = np.array([[-0.7262997, 0.9276514, -0.20907314], - [-0.7089523, 0.24923629, 2.5001974], - [1.6766014, 0.26847264, -0.2510258], - [0.9629222, -0.57908165, 0.0485389], - [2.7580009, 0.17540382, 1.6282884]], - dtype=np.float32) - expected_split2 = np.array([[1.1282716, 1.4266306, -0.16530532], - [-0.3836313, 0.04922554, 0.25543338], - [0.03718817, 1.5488712, 2.1594636], - [-2.1252284, 3.2059612, 0.1118355], - [3.4058936, -0.63690275, -0.95291173]], - dtype=np.float32) - - self.assertAllClose(expected_split1, actual_layer_output.split1) - self.assertAllClose(expected_split2, actual_layer_output.split2) - - def testRevNetLayerReverseGrad(self): - with self.session() as sess: - tf.random.set_seed(321) - input_1 = np.random.normal(size=[5, 3]) - input_2 = np.random.normal(size=[5, 3]) - x1 = tf.placeholder(dtype=tf.float32) - x2 = tf.placeholder(dtype=tf.float32) - p = reversible_layers.RevNetLayer.Params() - p.name = 'revnet_simple' - p.f_params = layers.FCLayer.Params().Set(input_dim=3, output_dim=3) - p.g_params = layers.FCLayer.Params().Set(input_dim=3, output_dim=3) - revnet_layer = p.Instantiate() - - outputs, f_seed, g_seed = revnet_layer.FPropDefaultTheta( - py_utils.NestedMap(split1=x1, split2=x2)) - loss = tf.reduce_sum(outputs.split1 + outputs.split2) - - # Computes tensorflow gradients. - dy1, dy2 = tf.gradients(loss, [outputs.split1, outputs.split2]) - dx1, dx2 = tf.gradients(loss, [x1, x2]) - dw = tf.gradients( - loss, - revnet_layer.theta.Flatten(), - unconnected_gradients=tf.UnconnectedGradients.ZERO) - - # Computes custom gradients. - inputs_reconstruct, dinputs, d_theta = revnet_layer.ReverseAndGrad( - revnet_layer.theta, - outputs, - py_utils.NestedMap(split1=dy1, split2=dy2), - f_seed, - g_seed, - ) - - self.evaluate(tf.global_variables_initializer()) - - # Tests the reverse. - x1r, x2r = sess.run( - [inputs_reconstruct.split1, inputs_reconstruct.split2], { - x1: input_1, - x2: input_2 - }) - self.assertAllClose(input_1, x1r) - self.assertAllClose(input_2, x2r) - - # Tests the gradient. - dx1_tf, dx2_tf, dx1_custom, dx2_custom, dw_tf, dw_custom = sess.run( - [dx1, dx2, dinputs.split1, dinputs.split2, dw, - d_theta.Flatten()], { - x1: input_1, - x2: input_2 - }) - self.assertAllClose(dx1_tf, dx1_custom) - self.assertAllClose(dx2_tf, dx2_custom) - self.assertAllClose(dw_tf, dw_custom) - - -class StackedRevNetLayerTest(test_utils.TestCase, parameterized.TestCase): - """Test stacked layers.""" - - def _SimpleRevNetParams(self, name, dropout, custom_gradient): - """Construct a simple 3-layers RevNet.""" - layer_tpl = reversible_layers.RevNetLayer.Params() - layer_tpl.f_params = layers.FeedForwardNet.Params().Set( - input_dim=3, hidden_layer_dims=[3, 3]) - layer_tpl.g_params = layers.FeedForwardNet.Params().Set( - input_dim=3, hidden_layer_dims=[3, 3]) - if dropout: - layer_tpl.f_params.dropout = layers.DeterministicDropoutLayer.Params() - layer_tpl.f_params.dropout.keep_prob = 0.7 - layer_tpl.g_params.dropout = layers.DeterministicDropoutLayer.Params() - layer_tpl.g_params.dropout.keep_prob = 0.8 - - stacked_p = reversible_layers.StackedRevNetLayer.Params() - stacked_p.name = name - stacked_p.custom_gradient = custom_gradient - for idx in range(3): - layer_p = layer_tpl.Copy() - layer_p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - layer_p.name = 'layer_{}'.format(idx) - stacked_p.sub_layer_params.append(layer_p) - return stacked_p - - def _RunModel(self, input_1_val, input_2_val, dropout, custom_grad): - self._ClearCachedSession() - tf.reset_default_graph() - with self.session() as sess: - tf.random.set_seed(321) - input_1 = tf.placeholder(tf.float32) - input_2 = tf.placeholder(tf.float32) - - revnet_params = self._SimpleRevNetParams('revnet', dropout, custom_grad) - revnet_params.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - revnet = revnet_params.Instantiate() - - h = revnet.FPropDefaultTheta( - py_utils.NestedMap(split1=input_1, split2=input_2)) - - dfx = tf.gradients(h.Flatten(), [input_1, input_2]) - dfw = tf.gradients( - h.Flatten(), - revnet.theta.Flatten(), - unconnected_gradients=tf.UnconnectedGradients.ZERO) - - self.evaluate(tf.global_variables_initializer()) - dfx_val, dfw_val, h_val = sess.run([dfx, dfw, h], - feed_dict={ - input_1: input_1_val, - input_2: input_2_val, - }) - return h_val, dfx_val, dfw_val - - @parameterized.named_parameters(('nodropout', False), ('dropout', True)) - def testStackedRevNetLayer(self, dropout): - input_1_val = np.random.normal(size=[5, 3]) - input_2_val = np.random.normal(size=[5, 3]) - # input_padding_val = np.random.randint(low=0, high=2, size=[5, 1]) - - h, dfx, dfw = self._RunModel(input_1_val, input_2_val, dropout, True) - h2, dfx2, dfw2 = self._RunModel(input_1_val, input_2_val, dropout, False) - - self.assertAllClose(h, h2) - self.assertAllClose(dfx, dfx2) - self.assertAllClose(dfw, dfw2) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/rnn_cell.py b/lingvo/core/rnn_cell.py deleted file mode 100644 index 07beb4ab..00000000 --- a/lingvo/core/rnn_cell.py +++ /dev/null @@ -1,2586 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""RNN cells (e.g., LSTM, GRU) that the Lingvo model uses.""" - -import math -import lingvo.compat as tf -from lingvo.core import hyperparams -from lingvo.core import layers -from lingvo.core import pruning_utils -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import summary_utils - -from tensorflow.python.util import deprecation as tf_deprecation # pylint: disable=g-direct-tensorflow-import - - -def _HistogramSummary(p, name, v): - """Adds a histogram summary for 'v' into the default tf graph.""" - summary_utils.histogram(name, tf.cast(v, tf.float32)) - - -RNN_CELL_WT = 'rnn_cell_weight_variable' - - -class RNNCell(quant_utils.QuantizableLayer): - # pylint: disable=line-too-long - """RNN cells. - - RNNCell represents recurrent state in a `.NestedMap`. - - `zero_state(theta, batch_size)` returns the initial state, which is defined - by each subclass. From the state, each subclass defines `GetOutput()` - to extract the output tensor. - - `RNNCell.FProp` defines the forward function:: - - (theta, state0, inputs) -> state1, extras - - All arguments and return values are `.NestedMap`. Each subclass defines - what fields these `.NestedMap` are expected to have. `extras` is a - `.NestedMap` containing some intermediate results `FProp` computes to - facilitate the backprop. - - `zero_state(theta, batch_size)`, `state0` and `state1` are all compatible - `.NestedMap` (see `.NestedMap.IsCompatible`). - I.e., they have the same keys recursively. Furthermore, the corresponding - tensors in these `.NestedMap` have the same shape and dtype. - """ - # pylint: enable=line-too-long - - @classmethod - def Params(cls): - p = super().Params() - p.Define('inputs_arity', 1, - 'number of tensors expected for the inputs.act to FProp.') - p.Define('num_input_nodes', 0, 'Number of input nodes.') - p.Define( - 'num_output_nodes', 0, - 'Number of output nodes. If num_hidden_nodes is 0, also used as ' - 'cell size.') - p.Define( - 'reset_cell_state', False, - ('Set True to support resetting cell state in scenarios where multiple ' - 'inputs are packed into a single training example. The RNN layer ' - 'should provide reset_mask inputs in addition to act and padding if ' - 'this flag is set.')) - p.Define( - 'zero_state_init_params', py_utils.DefaultRNNCellStateInit(), - 'Parameters that define how the initial state values are set ' - 'for each cell. Must be one of the static functions defined in ' - 'py_utils.RNNCellStateInit.') - return p - - def __init__(self, params): - """Initializes RnnCell.""" - super().__init__(params) - assert not self.params.vn.per_step_vn, ( - 'We do not support per step VN in RNN cells.') - - def _VariableCollections(self): - return [RNN_CELL_WT, '%s_vars' % self.__class__.__name__] - - def zero_state(self, theta, batch_size): - """Returns the initial state given the batch size.""" - raise NotImplementedError('Abstract method') - - def GetOutput(self, state): - """Returns the output value given the current state.""" - raise NotImplementedError('Abstract method') - - def batch_size(self, inputs): - """Given the inputs, returns the batch size.""" - raise NotImplementedError('Abstract method') - - def FProp(self, theta, state0, inputs): - """Forward function. - - The default implementation here assumes the cell forward - function is composed of two functions:: - - _Gates(_Mix(theta, state0, inputs), theta, state0, inputs) - - The result of `_Mix` is stashed in `extras` to facilitate backprop. - - `_ResetState` is optionally applied if `reset_cell_state` is True. The RNN - layer should provide `reset_mask` inputs in addition to other inputs. - `reset_mask` inputs are expected to be 0 at timesteps where state0 should be - reset to default (zeros) before running `_Mix()` and `_Gates()`, and 1 - otherwise. This is meant to support use cases like packed inputs, where - multiple samples are fed in a single input example sequence, and need to be - masked from each other. For example, if the two examples packed together - are ['good', 'day'] -> ['guten-tag'] and ['thanks'] -> ['danke'] - to produce ['good', 'day', 'thanks'] -> ['guten-tag', 'danke'], the - source reset_masks would be [1, 1, 0] and target reset masks would be - [1, 0]. These ids are meant to enable masking computations for - different examples from each other. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - state0: The previous recurrent state. A `.NestedMap`. - inputs: The inputs to the cell. A `.NestedMap`. - - Returns: - A tuple (state1, extras). - - state1: The next recurrent state. A `.NestedMap`. - - extras: Intermediate results to faciliate backprop. A `.NestedMap`. - """ - assert isinstance(inputs.act, list) - assert self.params.inputs_arity == len(inputs.act) - if self.params.reset_cell_state: - state0_modified = self._ResetState(state0.DeepCopy(), inputs) - else: - state0_modified = state0 - xmw = self._Mix(theta, state0_modified, inputs) - state1 = self._Gates(xmw, theta, state0_modified, inputs) - return state1, py_utils.NestedMap() - - def _ZoneOut(self, - prev_v, - cur_v, - padding_v, - zo_prob, - is_eval, - random_uniform, - qt=None, - qdomain=''): - """Apply ZoneOut regularlization to cur_v. - - Implements ZoneOut regularization as described in - https://arxiv.org/abs/1606.01305 - - Args: - prev_v: A tensor, values from the previous timestep. - cur_v: A tensor, values from the current timestep. - padding_v: A tensor, the paddings vector for the cur timestep. - zo_prob: A float, probability at which to apply ZoneOut regularization. - is_eval: A bool, whether or not in eval mode. - random_uniform: a tensor of random uniform numbers. This can be None if - zo_prob=0.0 - qt: A string, name of the qtensor for zone out math. - qdomain: A string, name of the qdomain for quantized zone out math. - - Returns: - cur_v after ZoneOut regularization has been applied. - """ - prev_v = tf.convert_to_tensor(prev_v) - cur_v = tf.convert_to_tensor(cur_v) - padding_v = tf.convert_to_tensor(padding_v) - if zo_prob == 0.0: - # Special case for when ZoneOut is not enabled. - return py_utils.ApplyPadding(padding_v, cur_v, prev_v) - - if is_eval: - # We take expectation in the eval mode. - # - fns = self.fns - # This quantized mixed operation should probably occur as fused kernel to - # avoid quantized-math rounding errors. Current accuracy has not been - # verified. - prev_weight = self.QWeight(zo_prob, domain=qdomain) - new_weight = self.QWeight(1.0 - prev_weight, domain=qdomain) - if qt is None: - mix_prev = tf.multiply(tf.fill(tf.shape(prev_v), prev_weight), prev_v) - mix_curr = tf.multiply(tf.fill(tf.shape(cur_v), new_weight), cur_v) - mix = tf.add(mix_prev, mix_curr) - else: - mix_prev = fns.qmultiply( - self.QWeight( - tf.fill(tf.shape(prev_v), prev_weight), domain=qdomain), - prev_v, - qt=qt) - mix_curr = fns.qmultiply( - self.QWeight(tf.fill(tf.shape(cur_v), new_weight), domain=qdomain), - cur_v, - qt=qt) - mix = fns.qadd(mix_prev, mix_curr, qt=qt) - - # If padding_v is 1, it always carries over the previous state. - return py_utils.ApplyPadding(padding_v, mix, prev_v) - else: - assert random_uniform is not None - random_uniform = py_utils.HasShape(random_uniform, tf.shape(prev_v)) - zo_p = tf.cast(random_uniform < zo_prob, padding_v.dtype) - zo_p += padding_v - # If padding_v is 1, we always carry over the previous state. - zo_p = tf.minimum(zo_p, 1.0) - zo_p = tf.stop_gradient(zo_p) - return py_utils.ApplyPadding(zo_p, cur_v, prev_v) - - -class LSTMCellSimple(RNNCell): - """Simple LSTM cell. - - theta: - - - wm: the parameter weight matrix. All gates combined. - - b: the combined bias vector. - - state: - - - m: the lstm output. [batch, cell_nodes] - - c: the lstm cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - Shape [batch, 1] - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'num_hidden_nodes', 0, 'Number of projection hidden nodes ' - '(see https://arxiv.org/abs/1603.08042). ' - 'Set to 0 to disable projection.') - p.Define( - 'cell_value_cap', 10.0, 'LSTM cell values are capped to be within ' - ' [-cell_value_cap, +cell_value_cap] if the value is not None. ' - 'It can be a scalar, a scalar tensor or None. When set to None, ' - 'no capping is applied.') - p.Define('forget_gate_bias', 0.0, 'Bias to apply to the forget gate.') - p.Define('output_nonlinearity', True, - 'Whether or not to apply tanh non-linearity on lstm output.') - p.Define('zo_prob', 0.0, - 'If > 0, applies ZoneOut regularization with the given prob.') - p.Define('enable_lstm_bias', True, 'Enable the LSTM Cell bias.') - p.Define( - 'couple_input_forget_gates', False, - 'Whether to couple the input and forget gates. Just like ' - 'tf.contrib.rnn.CoupledInputForgetGateLSTMCell') - p.Define('apply_pruning', False, 'Whether to prune the weights while ' - 'training') - p.Define('apply_pruning_to_projection', False, - 'Whether to prune the projection matrix while ' - 'training') - p.Define('gradient_pruning', False, 'Whether to gradient prune the model') - p.Define('bias_init', py_utils.WeightInit.Constant(0.0), - 'Initialization parameters for bias') - - # Non-default quantization behaviour. - p.qdomain.Define('weight', None, 'Quantization for the weights') - p.qdomain.Define('c_state', None, 'Quantization for the c-state.') - p.qdomain.Define('m_state', None, 'Quantization for the m-state.') - p.qdomain.Define('fullyconnected', None, - 'Quantization for fully connected node.') - return p - - def __init__(self, params): - """Initializes LSTMCellSimple.""" - super().__init__(params) - assert isinstance(params, hyperparams.Params) - p = self.params - assert isinstance(p.cell_value_cap, - (int, float)) or p.cell_value_cap is None - - assert p.cell_value_cap is None or p.qdomain.default is None - - self._timestep = -1 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - # Define weights. - wm_pc = py_utils.WeightParams( - shape=[ - p.num_input_nodes + self.output_size, - self.num_gates * self.hidden_size - ], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('wm', wm_pc, self.AddGlobalVN) - if p.apply_pruning: - mask_pc = py_utils.WeightParams(wm_pc.shape, - py_utils.WeightInit.Constant(1.0), - p.dtype) - threshold_pc = py_utils.WeightParams([], - py_utils.WeightInit.Constant(0.0), - tf.float32) - self.CreateVariable('mask', mask_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'threshold', threshold_pc, theta_fn=None, trainable=False) - # for gradient based pruning - # gradient and weight snapshots - grad_pc = py_utils.WeightParams(wm_pc.shape, - py_utils.WeightInit.Constant(0.0), - p.dtype) - if p.gradient_pruning: - self.CreateVariable('gradient', grad_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'old_weight', grad_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'old_old_weight', grad_pc, theta_fn=None, trainable=False) - - if p.num_hidden_nodes: - w_proj = py_utils.WeightParams( - shape=[self.hidden_size, self.output_size], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('w_proj', w_proj, self.AddGlobalVN) - if p.apply_pruning_to_projection: - proj_mask_pc = py_utils.WeightParams(w_proj.shape, - py_utils.WeightInit.Constant(1.0), - p.dtype) - proj_threshold_pc = py_utils.WeightParams( - [], py_utils.WeightInit.Constant(0.0), tf.float32) - self.CreateVariable( - 'proj_mask', proj_mask_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'proj_threshold', proj_threshold_pc, trainable=False) - # for gradient based pruning - # gradient and weight snapshots - proj_grad_pc = py_utils.WeightParams(w_proj.shape, - py_utils.WeightInit.Constant(0.0), - p.dtype) - if p.gradient_pruning: - self.CreateVariable('proj_gradient', proj_grad_pc, trainable=False) - self.CreateVariable('proj_old_weight', proj_grad_pc, trainable=False) - self.CreateVariable( - 'proj_old_old_weight', proj_grad_pc, trainable=False) - - if p.enable_lstm_bias: - bias_pc = py_utils.WeightParams( - shape=[self.num_gates * self.hidden_size], - init=p.bias_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('b', bias_pc, self.AddGlobalVN) - - if p.apply_pruning: - if p.gradient_pruning: - pruning_utils.AddToPruningCollections(self.vars.wm, self.vars.mask, - self.vars.threshold, - self.vars.gradient, - self.vars.old_weight, - self.vars.old_old_weight) - else: - pruning_utils.AddToPruningCollections(self.vars.wm, self.vars.mask, - self.vars.threshold) - if p.num_hidden_nodes: - if p.apply_pruning_to_projection: - if p.gradient_pruning: - pruning_utils.AddToPruningCollections(self.vars.w_proj, - self.vars.proj_mask, - self.vars.proj_threshold, - self.vars.proj_gradient, - self.vars.proj_old_weight, - self.vars.proj_old_old_weight) - else: - pruning_utils.AddToPruningCollections(self.vars.w_proj, - self.vars.proj_mask, - self.vars.proj_threshold) - - self.TrackQTensor( - 'zero_m', - 'm_output', - 'm_output_projection', - 'm_zoneout', - domain='m_state') - self.TrackQTensor( - 'zero_c', - 'mixed', - 'c_couple_invert', - 'c_input_gate', - 'c_forget_gate', - 'c_output_gate', - 'c_zoneout', - domain='c_state') - self.TrackQTensor('add_bias', domain='fullyconnected') - - # Collect some stats. - scope = tf.get_variable_scope() - w = self.vars.wm - if p.couple_input_forget_gates: - i_i, f_g, o_g = tf.split( - value=w, num_or_size_splits=self.num_gates, axis=1) - else: - i_i, i_g, f_g, o_g = tf.split( - value=w, num_or_size_splits=self.num_gates, axis=1) - _HistogramSummary(p, scope.name + '/wm_i_g', i_g) - _HistogramSummary(p, scope.name + '/wm_i_i', i_i) - _HistogramSummary(p, scope.name + '/wm_f_g', f_g) - _HistogramSummary(p, scope.name + '/wm_o_g', o_g) - - @property - def output_size(self): - return self.params.num_output_nodes - - @property - def hidden_size(self): - return self.params.num_hidden_nodes or self.params.num_output_nodes - - @property - def num_gates(self): - return 3 if self.params.couple_input_forget_gates else 4 - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - def zero_state(self, theta, batch_size): - p = self.params - zero_m = py_utils.InitRNNCellState((batch_size, self.output_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - zero_c = py_utils.InitRNNCellState((batch_size, self.hidden_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - if p.is_inference: - zero_m = self.QTensor('zero_m', zero_m) - zero_c = self.QTensor('zero_c', zero_c) - return py_utils.NestedMap(m=zero_m, c=zero_c) - - def _ResetState(self, state, inputs): - state.m = inputs.reset_mask * state.m - state.c = inputs.reset_mask * state.c - return state - - def GetOutput(self, state): - return state.m - - def _GetBias(self, theta): - """Gets the bias vector to add. - - Includes adjustments like forget_gate_bias. Use this instead of the 'b' - variable directly as including adjustments in this way allows const-prop - to eliminate the adjustments at inference time. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - - Returns: - The bias vector. - """ - p = self.params - if p.enable_lstm_bias: - b = theta.b - else: - b = tf.zeros([self.num_gates * self.hidden_size], dtype=p.dtype) - if p.forget_gate_bias != 0.0: - # Apply the forget gate bias directly to the bias vector. - if not p.couple_input_forget_gates: - # Normal 4 gate bias (i_i, i_g, f_g, o_g). - adjustment = ( - tf.ones([4, self.hidden_size], dtype=p.dtype) * tf.expand_dims( - tf.constant([0., 0., p.forget_gate_bias, 0.], dtype=p.dtype), - axis=1)) - else: - # 3 gates with coupled input/forget (i_i, f_g, o_g). - adjustment = ( - tf.ones([3, self.hidden_size], dtype=p.dtype) * tf.expand_dims( - tf.constant([0., p.forget_gate_bias, 0.], dtype=p.dtype), - axis=1)) - adjustment = tf.reshape(adjustment, [self.num_gates * self.hidden_size]) - b = b + adjustment - - return b - - def _Mix(self, theta, state0, inputs): - assert isinstance(inputs.act, list) - if self.params.apply_pruning: - wm = self.QWeight(tf.multiply(theta.wm, theta.mask, 'masked_weights')) - else: - wm = self.QWeight(theta.wm) - concat = tf.concat(inputs.act + [state0.m], 1) - # Defer quantization until after adding in the bias to support fusing - # matmul and bias add during inference. - return tf.matmul(concat, wm) - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - i_i, i_g, f_g, o_g = self._RetrieveAndSplitGates(xmw, theta) - return self._GatesInternal(theta, state0, inputs, i_i, i_g, f_g, o_g) - - def _RetrieveAndSplitGates(self, xmw, theta): - p = self.params - b = self.QWeight(tf.expand_dims(self._GetBias(theta), 0), domain='fc') - xmw = self.fns.qadd(xmw, b, qt='add_bias') - gates = tf.split(value=xmw, num_or_size_splits=self.num_gates, axis=1) - if p.couple_input_forget_gates: - gates = gates[0], None, gates[1], gates[2] - return gates - - def _GatesInternal(self, theta, state0, inputs, i_i, i_g, f_g, o_g): - p = self.params - fns = self.fns - if not p.couple_input_forget_gates: - assert i_g is not None - forget_gate = fns.qmultiply(tf.sigmoid(f_g), state0.c, qt='c_input_gate') - # Sigmoid / tanh calls are not quantized under the assumption they share - # the range with c_input_gate and c_forget_gate. - input_gate = fns.qmultiply( - tf.sigmoid(i_g), tf.tanh(i_i), qt='c_forget_gate') - new_c = fns.qadd(forget_gate, input_gate, qt='c_output_gate') - else: - assert i_g is None - # Sigmoid / tanh calls are not quantized under the assumption they share - # the range with c_input_gate and c_forget_gate. - forget_gate = fns.qmultiply(tf.sigmoid(f_g), state0.c, qt='c_input_gate') - - # input_gate = tanh(i_i) - tanh(i_i) * tf.sigmoid(f_g) - # equivalent to (but more stable in fixed point): - # (1.0 - sigmoid(f_g)) * tanh(i_i) - tanh_i_i = tf.tanh(i_i) - input_gate = fns.qsubtract( - tanh_i_i, - fns.qmultiply(tanh_i_i, tf.sigmoid(f_g), qt='c_couple_invert'), - qt='c_forget_gate') - - new_c = fns.qadd(forget_gate, input_gate, qt='c_output_gate') - - new_c = self._ProcessNewC(theta, new_c) - - # Clip the cell states to reasonable value. - if p.cell_value_cap is not None: - new_c = py_utils.clip_by_value(new_c, -p.cell_value_cap, p.cell_value_cap) - if p.output_nonlinearity: - new_m = fns.qmultiply(tf.sigmoid(o_g), tf.tanh(new_c), qt='m_output') - else: - new_m = fns.qmultiply(tf.sigmoid(o_g), new_c, qt='m_output') - if p.num_hidden_nodes: - if p.apply_pruning_to_projection: - w_proj = self.QWeight( - tf.multiply(theta.w_proj, theta.proj_mask, 'masked_projection'), - domain='m_state') - else: - w_proj = self.QWeight(theta.w_proj, domain='m_state') - - new_m = fns.qmatmul(new_m, w_proj, qt='m_output_projection') - - # Apply Zoneout. - return self._ApplyZoneOut(state0, inputs, new_c, new_m) - - def _ProcessNewC(self, theta, new_c): - return new_c - - def _ApplyZoneOut(self, state0, inputs, new_c, new_m): - """Apply Zoneout and returns the updated states.""" - p = self.params - if p.zo_prob > 0.0: - assert not py_utils.use_tpu(), ( - 'LSTMCellSimple does not support zoneout on TPU. Switch to ' - 'LSTMCellSimpleDeterministic instead.') - c_random_uniform = tf.random.uniform(tf.shape(new_c), seed=p.random_seed) - m_random_uniform = tf.random.uniform(tf.shape(new_m), seed=p.random_seed) - else: - c_random_uniform = None - m_random_uniform = None - - new_c = self._ZoneOut( - state0.c, - new_c, - self.QRPadding(inputs.padding), - p.zo_prob, - self.do_eval, - c_random_uniform, - qt='c_zoneout', - qdomain='c_state') - new_m = self._ZoneOut( - state0.m, - new_m, - self.QRPadding(inputs.padding), - p.zo_prob, - self.do_eval, - m_random_uniform, - qt='m_zoneout', - qdomain='m_state') - new_c.set_shape(state0.c.shape) - new_m.set_shape(state0.m.shape) - return py_utils.NestedMap(m=new_m, c=new_c) - - -class LSTMCellGrouped(RNNCell): - """LSTM cell with groups. - - Grouping: based on "Factorization tricks for LSTM networks". - https://arxiv.org/abs/1703.10722. - - Shuffling: adapted from "ShuffleNet: An Extremely Efficient Convolutional - Neural Network for Mobile Devices". https://arxiv.org/abs/1707.01083. - - theta: - - - groups: a list of child LSTM cells. - - state: - - A `.NestedMap` containing 'groups', a list of `.NestedMap`, each with: - - - m: the lstm output. [batch, cell_nodes // num_groups] - - c: the lstm cell state. [batch, cell_nodes // num_groups] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - Shape [batch, 1] - """ - - @classmethod - def Params(cls, child_cell_cls=LSTMCellSimple): - p = super().Params() - p.Define('child_lstm_tpl', child_cell_cls.Params(), - 'Template of child LSTM cells.') - p.Define('num_hidden_nodes', 0, 'Number of hidden nodes.') - p.Define( - 'split_inputs', True, 'If true, split the inputs into N groups. ' - 'If false, each group gets all inputs.') - p.Define('num_groups', 0, 'Number of LSTM cell groups.') - p.Define('num_shuffle_shards', 1, - 'If > 1, number of shards for cross-group shuffling.') - return p - - def __init__(self, params): - """Initializes LSTMCellGrouped.""" - super().__init__(params) - assert isinstance(params, hyperparams.Params) - p = self.params - assert p.num_input_nodes > 0 - assert p.num_output_nodes > 0 - assert p.num_groups > 0 - assert p.num_shuffle_shards > 0 - assert p.num_input_nodes % p.num_groups == 0 - assert p.num_output_nodes % (p.num_shuffle_shards * p.num_groups) == 0 - - child_params = [] - for i in range(p.num_groups): - child_p = self.params.child_lstm_tpl.Copy() - child_p.name = 'group_%d' % i - assert child_p.num_input_nodes == 0 - assert child_p.num_output_nodes == 0 - if p.split_inputs: - child_p.num_input_nodes = p.num_input_nodes // p.num_groups - else: - child_p.num_input_nodes = p.num_input_nodes - child_p.num_output_nodes = p.num_output_nodes // p.num_groups - child_p.num_hidden_nodes = p.num_hidden_nodes // p.num_groups - child_p.reset_cell_state = p.reset_cell_state - child_params.append(child_p) - self.CreateChildren('groups', child_params) - - def batch_size(self, inputs): - return self.groups[0].batch_size(inputs) - - def zero_state(self, theta, batch_size): - return py_utils.NestedMap(groups=[ - child.zero_state(child_theta, batch_size) - for child, child_theta in zip(self.groups, theta.groups) - ]) - - # TODO(rpang): avoid split and concat between layers with the same number of - # groups, if necessary. - def GetOutput(self, state): - p = self.params - # Assuming that GetOutput() is stateless, we can just use the first child. - outputs = [ - child.GetOutput(child_state) - for child, child_state in zip(self.groups, state.groups) - ] - split_output = [] - # Split each output to num_shuffle_shards. - for output in outputs: - split_output.extend( - py_utils.SplitRecursively(output, p.num_shuffle_shards)) - # Shuffle and concatenate shards. - return py_utils.ConcatRecursively(self._ShuffleShards(split_output)) - - def FProp(self, theta, state0, inputs): - """Forward function. - - Splits state0 and inputs into N groups (N=num_groups), runs child - LSTM cells on each group, and concatenates the outputs with optional - shuffling between groups. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - state0: The previous recurrent state. A `.NestedMap`. - inputs: The inputs to the cell. A `.NestedMap`. - - Returns: - A tuple (state1, extras). - - state1: The next recurrent state. A list. - - extras: An empty `.NestedMap`. - """ - p = self.params - if p.split_inputs: - split_inputs_act = py_utils.SplitRecursively(inputs.act, p.num_groups) - else: - split_inputs_act = [inputs.act] * p.num_groups - state1 = py_utils.NestedMap(groups=[]) - for child, child_theta, child_state0, child_inputs_act in zip( - self.groups, theta.groups, state0.groups, split_inputs_act): - child_inputs = inputs.copy() - child_inputs.act = child_inputs_act - child_state1, child_extras = child.FProp(child_theta, child_state0, - child_inputs) - assert not child_extras - state1.groups.append(child_state1) - return state1, py_utils.NestedMap() - - def _ShuffleShards(self, shards): - """Shuffles shards across groups. - - Args: - shards: a list of length num_shuffle_shards (S) * num_groups (G). The - first S shards belong to group 0, the next S shards belong to group 1, - etc. - - Returns: - A shuffled list of shards such that shards from each input group are - scattered across output groups. - - For example, if we have 3 groups, each with 4 shards: - - | Group 0: 0_0, 0_1, 0_2, 0_3 - | Group 1: 1_0, 1_1, 1_2, 1_3 - | Group 2: 2_0, 2_1, 2_2, 2_3 - - The shuffled output will be: - - | Group 0: 0_0, 1_1, 2_2, 0_3 - | Group 1: 1_0, 2_1, 0_2, 1_3 - | Group 2: 2_0, 0_1, 1_2, 2_3 - """ - p = self.params - assert len(shards) == (p.num_shuffle_shards * p.num_groups) - shuffled_shards = [] - for group_i in range(p.num_groups): - for shuffle_i in range(p.num_shuffle_shards): - shuffled_shards.append(shards[( - (group_i + shuffle_i) % p.num_groups) * p.num_shuffle_shards + - shuffle_i]) - return shuffled_shards - - -# TODO(yonghui): Merge this cell with the LSTMCellSimple cell. -class LSTMCellSimpleDeterministic(LSTMCellSimple): - """Same as LSTMCellSimple, except this cell is completely deterministic.""" - - @classmethod - def Params(cls): - p = super().Params() - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - self.CreateVariable( - name='lstm_step_counter', - var_params=py_utils.WeightParams([], py_utils.WeightInit.Constant(0), - tf.int64), - trainable=False) - vname = self.vars.lstm_step_counter.name - self._prng_seed = tf.constant( - py_utils.GenerateSeedFromName(vname), dtype=tf.int64) - if p.random_seed: - self._prng_seed += p.random_seed - - def zero_state(self, theta, batch_size): - p = self.params - zero_m = tf.zeros((batch_size, self.output_size), - dtype=py_utils.FPropDtype(p)) - zero_c = tf.zeros((batch_size, self.hidden_size), - dtype=py_utils.FPropDtype(p)) - if p.is_inference: - zero_m = self.QTensor('zero_m', zero_m) - zero_c = self.QTensor('zero_c', zero_c) - - # The first random seed changes for different layers and training steps. - random_seed1 = self._prng_seed + theta.lstm_step_counter - # The second random seed changes for different unroll time steps. - random_seed2 = tf.constant(0, dtype=tf.int64) - random_seeds = tf.stack([random_seed1, random_seed2]) - return py_utils.NestedMap(m=zero_m, c=zero_c, r=random_seeds) - - def _ApplyZoneOut(self, state0, inputs, new_c, new_m): - """Apply Zoneout and returns the updated states.""" - p = self.params - random_seed1 = state0.r[0] - random_seed2 = state0.r[1] - if p.zo_prob > 0.0: - # Note(yonghui): It seems that currently TF only supports int64 as the - # random seeds, however, TPU will support int32 as the seed. - # TODO(yonghui): Fix me for TPU. - c_seed = tf.stack([random_seed1, 2 * random_seed2]) - m_seed = tf.stack([random_seed1, 2 * random_seed2 + 1]) - if py_utils.use_tpu(): - c_random_uniform = tf.random.stateless_uniform( - py_utils.GetShape(new_c, 2), tf.cast(c_seed, tf.int32)) - m_random_uniform = tf.random.stateless_uniform( - py_utils.GetShape(new_m, 2), tf.cast(m_seed, tf.int32)) - else: - c_random_uniform = tf.random.stateless_uniform( - py_utils.GetShape(new_c, 2), c_seed) - m_random_uniform = tf.random.stateless_uniform( - py_utils.GetShape(new_m, 2), m_seed) - else: - c_random_uniform = None - m_random_uniform = None - - new_c = self._ZoneOut( - state0.c, - new_c, - inputs.padding, - p.zo_prob, - self.do_eval, - c_random_uniform, - qt='zero_c', - qdomain='c_state') - new_m = self._ZoneOut( - state0.m, - new_m, - inputs.padding, - p.zo_prob, - self.do_eval, - m_random_uniform, - qt='zero_m', - qdomain='m_state') - # TODO(yonghui): stop the proliferation of tf.stop_gradient - r = tf.stop_gradient(tf.stack([random_seed1, random_seed2 + 1])) - new_c.set_shape(state0.c.shape) - new_m.set_shape(state0.m.shape) - r.set_shape(state0.r.shape) - return py_utils.NestedMap(m=new_m, c=new_c, r=r) - - def PostTrainingStepUpdate(self, global_step): - """Update the global_step value.""" - p = self.params - with tf.name_scope(p.name): - summary_utils.scalar('step_counter', self.vars.lstm_step_counter) - return self.vars.lstm_step_counter.assign(tf.cast(global_step, tf.int64)) - - -class QuantizedLSTMCell(RNNCell): - """Simplified LSTM cell used for quantized training. - - There is no forget_gate_bias, no output_nonlinearity and no bias. Right now - only clipping is performed. - - theta: - - - wm: the parameter weight matrix. All gates combined. - - cap: the cell value cap. - - state: - - - m: the lstm output. [batch, cell_nodes] - - c: the lstm cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - [batch, 1] - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('cc_schedule', quant_utils.LinearClippingCapSchedule.Params(), - 'Clipping cap schedule.') - return p - - def __init__(self, params): - """Initializes QuantizedLSTMCell.""" - super().__init__(params) - assert isinstance(params, hyperparams.Params) - p = self.params - - self.CreateChild('cc_schedule', p.cc_schedule) - - self._timestep = -1 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - # Define weights. - wm_pc = py_utils.WeightParams( - shape=[p.num_input_nodes + p.num_output_nodes, 4 * p.num_output_nodes], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('wm', wm_pc, self.AddGlobalVN) - - scope = tf.get_variable_scope() - # Collect some stats - i_i, i_g, f_g, o_g = tf.split( - value=self.vars.wm, num_or_size_splits=4, axis=1) - _HistogramSummary(p, scope.name + '/wm_i_i', i_i) - _HistogramSummary(p, scope.name + '/wm_i_g', i_g) - _HistogramSummary(p, scope.name + '/wm_f_g', f_g) - _HistogramSummary(p, scope.name + '/wm_o_g', o_g) - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - def zero_state(self, theta, batch_size): - p = self.params - zero_m = py_utils.InitRNNCellState((batch_size, p.num_output_nodes), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - zero_c = py_utils.InitRNNCellState((batch_size, p.num_output_nodes), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - return py_utils.NestedMap(m=zero_m, c=zero_c) - - def GetOutput(self, state): - return state.m - - def _ResetState(self, state, inputs): - state.m = inputs.reset_mask * state.m - state.c = inputs.reset_mask * state.c - return state - - def _Mix(self, theta, state0, inputs): - assert isinstance(inputs.act, list) - return py_utils.Matmul(tf.concat(inputs.act + [state0.m], 1), theta.wm) - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - i_i, i_g, f_g, o_g = tf.split(value=xmw, num_or_size_splits=4, axis=1) - - new_c = tf.sigmoid(f_g) * state0.c + tf.sigmoid(i_g) * tf.tanh(i_i) - new_c = self.cc_schedule.ApplyClipping(theta.cc_schedule, new_c) - new_m = tf.sigmoid(o_g) * new_c - - # Respect padding. - new_m = state0.m * inputs.padding + new_m * (1 - inputs.padding) - new_c = state0.c * inputs.padding + new_c * (1 - inputs.padding) - - new_c.set_shape(state0.c.shape) - new_m.set_shape(state0.m.shape) - return py_utils.NestedMap(m=new_m, c=new_c) - - -class LayerNormalizedLSTMCell(RNNCell): - """DEPRECATED: use LayerNormalizedLSTMCellSimple instead. - - Simple LSTM cell with layer normalization. - - Implements normalization scheme as described in - https://arxiv.org/pdf/1607.06450.pdf - - theta: - - - wm: the parameter weight matrix. All gates combined. - - b: the combined bias vector. - - state: - - - m: the lstm output. [batch, cell_nodes] - - c: the lstm cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - Shape [batch, 1] - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'cell_value_cap', 10.0, 'LSTM cell values are capped to be within ' - ' [-cell_value_cap, +cell_value_cap]. This can be a' - ' scalar or a scalar tensor.') - p.Define('forget_gate_bias', 0.0, 'Bias to apply to the forget gate.') - p.Define('output_nonlinearity', True, - 'Whether or not to apply tanh non-linearity on lstm output.') - p.Define('zo_prob', 0.0, - 'If > 0, applies ZoneOut regularization with the given prob.') - p.Define('layer_norm_epsilon', 1e-8, 'Tiny value to guard rsqr against.') - p.Define('cc_schedule', None, 'Clipping cap schedule.') - p.Define('use_fused_layernorm', False, 'Whether to use fused layernorm.') - return p - - @tf_deprecation.deprecated( - date=None, - instructions='New models should use LayerNormalizedLSTMCellSimple.') - def __init__(self, params): - """Initializes LayerNormalizedLSTMCell.""" - super().__init__(params) - params = self.params - if not isinstance(params.cell_value_cap, (int, float)): - raise ValueError('Cell value cap must of type int or float!') - - if params.cc_schedule: - self.CreateChild('cc_schedule', params.cc_schedule) - - self._timestep = -1 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - # Define weights. - wm_pc = py_utils.WeightParams( - shape=[p.num_input_nodes + p.num_output_nodes, 4 * p.num_output_nodes], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('wm', wm_pc, self.AddGlobalVN) - # This bias variable actually packs the initial lstm bias variables as - # well as various layer norm scale and bias variables. We pack multiple - # variables into one so that we can still unroll this lstm using the FRNN - # layer defined in layers.py. - bias_pc = py_utils.WeightParams( - shape=[4 * p.num_output_nodes + 4 * p.num_output_nodes], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('b', bias_pc, self.AddGlobalVN) - - # Collect some stats - scope = tf.get_variable_scope() - i_i, i_g, f_g, o_g = tf.split( - value=self.vars.wm, num_or_size_splits=4, axis=1) - _HistogramSummary(p, scope.name + '/wm_i_i', i_i) - _HistogramSummary(p, scope.name + '/wm_i_g', i_g) - _HistogramSummary(p, scope.name + '/wm_f_g', f_g) - _HistogramSummary(p, scope.name + '/wm_o_g', o_g) - # TODO(yonghui): Add more summaries here. - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - @property - def output_size(self): - return self.params.num_output_nodes - - @property - def hidden_size(self): - return self.params.num_output_nodes - - def zero_state(self, theta, batch_size): - p = self.params - return py_utils.NestedMap( - m=py_utils.InitRNNCellState([batch_size, p.num_output_nodes], - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval), - c=py_utils.InitRNNCellState([batch_size, p.num_output_nodes], - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval)) - - def GetOutput(self, state): - return state.m - - def _ResetState(self, state, inputs): - state.m = inputs.reset_mask * state.m - state.c = inputs.reset_mask * state.c - return state - - def _Mix(self, theta, state0, inputs): - if not isinstance(inputs.act, list): - raise ValueError('Input activations must be of list type!') - return py_utils.Matmul(tf.concat(inputs.act + [state0.m], 1), theta.wm) - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - # Unpack the variables (weight and bias) into individual variables. - params = self.params - - def BiasSlice(dim, num_dims, start_ind): - s = [] - for i in range(num_dims): - s.append(theta.b[start_ind + i * dim:start_ind + (i + 1) * dim]) - start_ind += dim * num_dims - return s, start_ind - - # Unpack the bias variable. - slice_start = 0 - bias_lstm, slice_start = BiasSlice(params.num_output_nodes, 4, slice_start) - ln_scale, slice_start = BiasSlice(params.num_output_nodes, 4, slice_start) - assert slice_start == 8 * params.num_output_nodes - - def _LayerNorm(x, last_dim): - """Normalize the last dimension.""" - if params.use_fused_layernorm: - counts, means_ss, variance_ss, _, = tf.nn.sufficient_statistics( - x, axes=[last_dim], keepdims=True) - mean, variance = tf.nn.normalize_moments(counts, means_ss, variance_ss, - None) - else: - mean = tf.reduce_mean(x, axis=[last_dim], keepdims=True) - variance = tf.reduce_mean( - tf.square(x - mean), axis=[last_dim], keepdims=True) - return (x - mean) * tf.math.rsqrt(variance + params.layer_norm_epsilon) - - state_split = tf.split(xmw, num_or_size_splits=4, axis=1) - for i in range(4): - state_split[i] = _LayerNorm(state_split[i], 1) * tf.expand_dims( - ln_scale[i] + 1.0, 0) + tf.expand_dims(bias_lstm[i], 0) - - i_i, i_g, f_g, o_g = state_split - - if params.forget_gate_bias != 0.0: - f_g += params.forget_gate_bias - new_c = tf.sigmoid(f_g) * state0.c + tf.sigmoid(i_g) * tf.tanh(i_i) - - # Clip the cell states to reasonable value. - if params.cc_schedule: - cap = self.cc_schedule.GetState(theta.cc_schedule) - else: - cap = params.cell_value_cap - new_c = py_utils.clip_by_value(new_c, -cap, cap) - - if params.output_nonlinearity: - new_m = tf.sigmoid(o_g) * tf.tanh(new_c) - else: - new_m = tf.sigmoid(o_g) * new_c - - if params.zo_prob > 0.0: - c_random_uniform = tf.random.uniform( - tf.shape(new_c), seed=params.random_seed) - m_random_uniform = tf.random.uniform( - tf.shape(new_m), seed=params.random_seed) - else: - c_random_uniform = None - m_random_uniform = None - - new_c = self._ZoneOut(state0.c, new_c, inputs.padding, params.zo_prob, - self.do_eval, c_random_uniform) - new_m = self._ZoneOut(state0.m, new_m, inputs.padding, params.zo_prob, - self.do_eval, m_random_uniform) - new_c.set_shape(state0.c.shape) - new_m.set_shape(state0.m.shape) - return py_utils.NestedMap(m=new_m, c=new_c) - - -class LayerNormalizedLSTMCellSimple(LSTMCellSimple): - """An implementation of layer normalized LSTM based on LSTMCellSimple. - - Implements normalization scheme as described in - https://arxiv.org/pdf/1607.06450.pdf - - theta: - - - wm: the parameter weight matrix. All gates combined. - - b: the combined bias vector. - - state: - - - m: the lstm output. [batch, cell_nodes] - - c: the lstm cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - Shape [batch, 1] - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('layer_norm_epsilon', 1e-8, 'Tiny value to guard rsqr against.') - return p - - def _CreateLayerVariables(self): - """Initializes LayerNormalizedLSTMCellSimple.""" - super()._CreateLayerVariables() - p = self.params - - add_biases = ['add_bias_{}'.format(i) for i in range(self.num_gates)] - self.TrackQTensor(*add_biases, domain='fullyconnected') - - ln_scale_pc = py_utils.WeightParams( - shape=[self.num_gates * self.hidden_size], - init=py_utils.WeightInit.Constant(1.0), - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('ln_scale', ln_scale_pc, self.AddGlobalVN) - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - - def _LayerNorm(x): - """Applies layer normalization on the last dimension of 'x'. - - Args: - x: activation tensor, where the last dimension represents channels. - - Returns: - Layer normalized 'x', with the same shape as the input. - """ - mean = tf.reduce_mean(x, axis=-1, keepdims=True) - variance = tf.reduce_mean(tf.square(x - mean), axis=-1, keepdims=True) - return (x - mean) * tf.math.rsqrt(variance + p.layer_norm_epsilon) - - p = self.params - b = self.QWeight(tf.expand_dims(self._GetBias(theta), 0), domain='fc') - - bs = tf.split(b, num_or_size_splits=self.num_gates, axis=1) - ln_scales = tf.split( - theta.ln_scale, num_or_size_splits=self.num_gates, axis=0) - gates = tf.split(xmw, num_or_size_splits=self.num_gates, axis=1) - - for i in range(self.num_gates): - # i_g is None when p.couple_input_forget_gates is True. - if gates[i] is not None: - gates[i] = _LayerNorm(gates[i]) * tf.expand_dims(ln_scales[i], 0) - gates[i] = self.fns.qadd(gates[i], bs[i], qt='add_bias_{}'.format(i)) - - if not p.couple_input_forget_gates: - i_i, i_g, f_g, o_g = gates - else: - i_i, i_g, f_g, o_g = gates[0], None, gates[1], gates[2] - return self._GatesInternal(theta, state0, inputs, i_i, i_g, f_g, o_g) - - -class NormalizedLSTMCellSimple(LSTMCellSimple): - """LSTM Cell that allows customzied normalization. - - theta: - - - wm: the parameter weight matrix. All gates combined. - - b: the combined bias vector. - - state: - - - m: the lstm output. [batch, cell_nodes] - - c: the lstm cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - Shape [batch, 1] - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('norm_layer_tpl', - layers.LayerNorm.Params().Set(epsilon=1e-8), - 'The normalization layer param') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - gates_name = ['i_i', 'i_g', 'f_g', 'o_g'] - for gate in gates_name: - norm_layer_p = p.norm_layer_tpl.Copy().Set(input_dim=self.hidden_size) - self.CreateChild('norm_' + gate, norm_layer_p) - - def _Gates(self, xmw, theta, state0, inputs): - # Retrieve i_i, i_g, f_g, o_g - gates = self._RetrieveAndSplitGates(xmw, theta) - gates_name = ['i_i', 'i_g', 'f_g', 'o_g'] - for i, gate_name in enumerate(gates_name): - norm_layer_name = 'norm_' + gate_name - if gates[i] is not None: - gates[i] = self.children.get(norm_layer_name).FProp( - theta.get(norm_layer_name), gates[i]) - - i_i, i_g, f_g, o_g = gates - return self._GatesInternal(theta, state0, inputs, i_i, i_g, f_g, o_g) - - -class LayerNormalizedLSTMCellLean(RNNCell): - """A very lean layer normalized LSTM cell. - - This version is around 20% faster on TPU than LayerNormalizedLSTMCellSimple as - it avoids certain reshape ops which are not free on TPU. - - Note, this version doesn't support all the options as implemented in - LayerNormalizedLSTMCellSimple, such as quantization, zoneout regularization - and etc. - - For the overlapping options, an incomplete list of differences from - LayerNormalizedLSTMCellSimple include: - - c_state is layer-normalized for computing new_m (if enable_ln_on_c=True) - - ln_scale has a fixed offset of 1. - - Please use the other version if you even need those options. - - state: - - - m: the lstm output. [batch, cell_nodes] - - c: the lstm cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - Shape [batch, 1] - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'num_hidden_nodes', 0, 'Number of projection hidden nodes ' - '(see https://arxiv.org/abs/1603.08042). ' - 'Set to 0 to disable projection.') - p.Define('layer_norm_epsilon', 1e-8, 'Tiny value to guard rsqrt against.') - p.Define( - 'enable_ln_on_c', True, - 'Whether to apply layer normalization on state.c. ' - 'If false, LayerNormalizedLSTMCellLean will behave exactly as ' - 'LayerNormalizedLSTMCellSimple.') - p.Define( - 'cell_value_cap', None, 'LSTM cell values are capped to be within ' - ' [-cell_value_cap, +cell_value_cap] if the value is not None. ' - 'It can be a scalar, a scalar tensor or None. When set to None, ' - 'no capping is applied.') - p.Define('enable_lstm_bias', False, 'Enable the LSTM Cell bias.') - p.Define('bias_init', py_utils.WeightInit.Constant(0.0), - 'Initialization parameters for bias') - p.Define('use_ln_bias', True, 'If to include a bias term for layer norm.') - - # TODO(yonghui): Get rid of the following two params. - p.Define('output_nonlinearity', True, - 'Whether or not to apply tanh non-linearity on lstm output.') - p.Define('zo_prob', 0.0, - 'If > 0, applies ZoneOut regularization with the given prob.') - return p - - def __init__(self, params): - """Initializes LayerNormalizedLSTMCellLean.""" - super().__init__(params) - assert isinstance(params, hyperparams.Params) - p = self.params - - assert p.output_nonlinearity - assert p.zo_prob == 0.0 - if p.cell_value_cap is not None and not isinstance(p.cell_value_cap, - (int, float)): - raise ValueError( - 'p.cell_value_cap should be a int/float if not None, but got {}' - .format(p.cell_value_cap)) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - # Define weights. - wm_pc = py_utils.WeightParams( - shape=[p.num_input_nodes + self.output_size, 4 * self.hidden_size], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('wm', wm_pc, self.AddGlobalVN) - - if p.num_hidden_nodes: - w_proj = py_utils.WeightParams( - shape=[self.hidden_size, self.output_size], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('w_proj', w_proj, self.AddGlobalVN) - - if p.enable_lstm_bias: - bias_pc = py_utils.WeightParams( - shape=[4 * self.hidden_size], - init=p.bias_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('b', bias_pc, self.AddGlobalVN) - - pc = py_utils.WeightParams( - shape=[self.hidden_size], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=self._VariableCollections()) - ln_gates = ['i_g', 'i_i', 'f_g', 'o_g'] - if p.enable_ln_on_c: - ln_gates += ['c'] - for ln_name in ln_gates: - self.CreateVariable('ln_scale_' + ln_name, pc, self.AddGlobalVN) - if p.use_ln_bias: - self.CreateVariable('bias_' + ln_name, pc, self.AddGlobalVN) - - @property - def output_size(self): - return self.params.num_output_nodes - - @property - def hidden_size(self): - return self.params.num_hidden_nodes or self.params.num_output_nodes - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - def zero_state(self, theta, batch_size): - p = self.params - zero_m = py_utils.InitRNNCellState((batch_size, self.output_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - zero_c = py_utils.InitRNNCellState((batch_size, self.hidden_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - return py_utils.NestedMap(m=zero_m, c=zero_c) - - def _ResetState(self, state, inputs): - state.m = inputs.reset_mask * state.m - state.c = inputs.reset_mask * state.c - return state - - def GetOutput(self, state): - return state.m - - def _Mix(self, theta, state0, inputs): - assert isinstance(inputs.act, list) - mixed = tf.matmul(tf.concat(inputs.act + [state0.m], 1), theta.wm) - return mixed - - def _LayerNormGate(self, theta, gate_name, x): - """Applies layer normalization on the last dimension of 'x'. - - Args: - theta: a NestedMap of layer params. - gate_name: the name of the gate, e.g., 'i_i', 'f_g', 'c', etc. - x: activation tensor, where the last dimension represents channels. - - Returns: - Layer normalized 'x', with the same shape as the input. - """ - p = self.params - if gate_name == 'c' and not p.enable_ln_on_c: - return x - mean = tf.reduce_mean(x, axis=[1], keepdims=True) - centered = x - mean - variance = tf.reduce_mean(tf.square(centered), axis=[1], keepdims=True) - normed = centered * tf.math.rsqrt(variance + p.layer_norm_epsilon) - scale = theta['ln_scale_%s' % gate_name] + 1.0 - if p.use_ln_bias: - bias = theta['bias_%s' % gate_name] - return normed * scale + bias - else: - return normed * scale - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - p = self.params - i_i, i_g, f_g, o_g = tf.split(value=xmw, num_or_size_splits=4, axis=1) - i_i = self._LayerNormGate(theta, 'i_i', i_i) - i_g = self._LayerNormGate(theta, 'i_g', i_g) - f_g = self._LayerNormGate(theta, 'f_g', f_g) - o_g = self._LayerNormGate(theta, 'o_g', o_g) - if p.enable_lstm_bias: - # LayerNormalizedLSTMCellLean applies biases after LN. - biases = tf.split(theta.b, num_or_size_splits=4) - i_i += biases[0] - i_g += biases[1] - f_g += biases[2] - o_g += biases[3] - new_c = tf.sigmoid(f_g) * state0.c + tf.sigmoid(i_g) * tf.tanh(i_i) - if p.cell_value_cap is not None: - new_c = py_utils.clip_by_value(new_c, -p.cell_value_cap, p.cell_value_cap) - # new_c_normed is only used for computing 'new_m'. We use the un-normalized - # new_cc as cell state to keep the residual property of lstm cell. - new_c_normed = self._LayerNormGate(theta, 'c', new_c) - new_m = tf.sigmoid(o_g) * tf.tanh(new_c_normed) - - if p.num_hidden_nodes: - new_m = tf.matmul(new_m, theta.w_proj) - - # Now take care of padding. - padding = inputs.padding - new_m = py_utils.ApplyPadding(padding, new_m, state0.m) - new_c = py_utils.ApplyPadding(padding, new_c, state0.c) - - return py_utils.NestedMap(m=new_m, c=new_c) - - -class DoubleProjectionLSTMCell(RNNCell): - """A layer normalized LSTM cell that support input and output projections. - - Note, this version doesn't support all the options as implemented in - LayerNormalizedLSTMCellSimple, like quantization, zoneout regularization, - etc. Please use the other version if you need those options and do not need - input projection. - - It also uses separate variables for weight matrices between gates - ('wm_{i_i, i_g, f_g, o_g}') instead of a single variable ('wm'). This allows - the initialization to use the default GeoMeanXavier(). - - state: - - - m: the lstm output. [batch, cell_nodes] - - c: the lstm cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - Shape [batch, 1] - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'num_input_hidden_nodes', 0, - 'Project all inputs, include m, to a hidden vector this size before ' - 'projecting to num_gates * |c|. Must be > 0.') - p.Define( - 'num_hidden_nodes', 0, 'Number of projection hidden nodes ' - '(see https://arxiv.org/abs/1603.08042). ' - 'Set to 0 to disable projection.') - p.Define('layer_norm_epsilon', 1e-8, 'Tiny value to guard rsqrt against.') - p.Define('enable_ln_on_c', True, - 'Whether to apply layer normalization on state.c.') - p.params_init = py_utils.WeightInit.GeoMeanXavier() - return p - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - def _WeightInit(shape): - return py_utils.WeightParams( - shape=shape, - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - - self.CreateVariable( - 'w_input_proj', - _WeightInit( - [p.num_input_nodes + self.output_size, p.num_input_hidden_nodes]), - self.AddGlobalVN) - - self.CreateVariable('w_output_proj', - _WeightInit([self.hidden_size, self.output_size]), - self.AddGlobalVN) - - for gate_name in self.gates: - self.CreateVariable( - 'wm_%s' % gate_name, - _WeightInit([p.num_input_hidden_nodes, self.hidden_size]), - self.AddGlobalVN) - - pc = py_utils.WeightParams( - shape=[self.hidden_size], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=self._VariableCollections()) - ln_gates = self.gates - if p.enable_ln_on_c: - ln_gates += ['c'] - for ln_name in ln_gates: - self.CreateVariable('ln_scale_' + ln_name, pc, self.AddGlobalVN) - self.CreateVariable('bias_' + ln_name, pc, self.AddGlobalVN) - - @property - def output_size(self): - return self.params.num_output_nodes - - @property - def hidden_size(self): - return self.params.num_hidden_nodes - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - @property - def gates(self): - return ['i_g', 'i_i', 'f_g', 'o_g'] - - def zero_state(self, theta, batch_size): - p = self.params - zero_m = py_utils.InitRNNCellState((batch_size, self.output_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - zero_c = py_utils.InitRNNCellState((batch_size, self.hidden_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - return py_utils.NestedMap(m=zero_m, c=zero_c) - - def _ResetState(self, state, inputs): - state.m = inputs.reset_mask * state.m - state.c = inputs.reset_mask * state.c - return state - - def GetOutput(self, state): - return state.m - - def _ProcessInputProj(self, theta, input_proj): - return input_proj - - def _Mix(self, theta, state0, inputs): - assert isinstance(inputs.act, list) - concat = tf.concat(inputs.act + [state0.m], 1) - input_proj = tf.matmul(concat, theta.w_input_proj) - input_proj = self._ProcessInputProj(theta, input_proj) - gate_map = {} - for gate_name in self.gates: - g = tf.matmul(input_proj, theta.get('wm_%s' % gate_name)) - g = self._LayerNormGate(theta, gate_name, g) - gate_map[gate_name] = g - return gate_map - - def _LayerNormGate(self, theta, gate_name, x): - """Applies layer normalization on the last dimension of 'x'. - - Args: - theta: a NestedMap of layer params. - gate_name: the name of the gate, e.g., 'i_i', 'f_g', 'c', etc. - x: activation tensor, where the last dimension represents channels. - - Returns: - Layer normalized 'x', with the same shape as the input. - """ - p = self.params - if gate_name == 'c' and not p.enable_ln_on_c: - return x - mean = tf.reduce_mean(x, axis=[1], keepdims=True) - centered = x - mean - variance = tf.reduce_mean(tf.square(centered), axis=[1], keepdims=True) - normed = centered * tf.math.rsqrt(variance + p.layer_norm_epsilon) - scale = theta['ln_scale_%s' % gate_name] + 1.0 - bias = theta['bias_%s' % gate_name] - return normed * scale + bias - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - new_c = tf.sigmoid(xmw['f_g']) * state0.c + tf.sigmoid( - xmw['i_g']) * tf.tanh(xmw['i_i']) - new_c_normed = self._LayerNormGate(theta, 'c', new_c) - new_m = tf.sigmoid(xmw['o_g']) * tf.tanh(new_c_normed) - new_m = tf.matmul(new_m, theta.w_output_proj) - - # Now take care of padding. - padding = inputs.padding - new_m = py_utils.ApplyPadding(padding, new_m, state0.m) - new_c = py_utils.ApplyPadding(padding, new_c, state0.c) - - return py_utils.NestedMap(m=new_m, c=new_c) - - -class ConvLSTMCell(RNNCell): - """Convolution LSTM cells. - - theta: - - - wm: the parameter weight matrix. All gates combined. - - b: the combined bias vector. - - state: - - - m: the lstm output. cell_shape - - c: the lstm cell state. cell_shape - - inputs: - - - act: a list of input activations. input_shape. - - padding: the padding. [batch]. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'inputs_shape', [None, None, None, None], - 'The shape of the input. It should be a list/tuple of size four.' - ' Elements are in the order of batch, height, width, channel.') - p.Define( - 'cell_shape', [None, None, None, None], - 'The cell shape. It should be a list/tuple of size four.' - ' Elements are in the order of batch, height, width, channel.' - ' Height and width of cell_shape should match that of' - ' inputs_shape.') - p.Define( - 'filter_shape', [None, None], - 'Shape of the convolution filter. This should be a pair, in the' - ' order height and width.') - p.Define( - 'cell_value_cap', 10.0, 'LSTM cell values are capped to be within ' - ' [-cell_value_cap, +cell_value_cap]. This can be a' - ' scalar or a scalar tensor.') - p.Define('output_nonlinearity', True, - 'Whether or not to apply tanh non-linearity on lstm output.') - p.Define('zo_prob', 0.0, - 'If > 0, applies ZoneOut regularization with the given prob.') - - return p - - def __init__(self, params): - """Initializes ConvLSTMCell.""" - assert isinstance(params, hyperparams.Params) - super().__init__(params) - - p = self.params - assert p.reset_cell_state is False, ('ConvLSTMCell currently doesnt ' - 'support resetting cell state.') - assert p.inputs_shape[1] == p.cell_shape[1] - assert p.inputs_shape[2] == p.cell_shape[2] - assert isinstance(p.cell_value_cap, (int, float)) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - in_channels = p.inputs_shape[3] + p.cell_shape[3] - out_channels = p.cell_shape[3] - # Define weights. - var_shape = [ - p.filter_shape[0], p.filter_shape[1], in_channels, 4 * out_channels - ] - wm_pc = py_utils.WeightParams( - shape=var_shape, - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('wm', wm_pc, self.AddGlobalVN) - - bias_pc = py_utils.WeightParams( - shape=[4 * out_channels], - init=py_utils.WeightInit.Constant(0.0), - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('b', bias_pc, self.AddGlobalVN) - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - @property - def output_size(self): - # There is no projection. - return self.hidden_size - - @property - def hidden_size(self): - p = self.params - return p.cell_shape[1:] - - def zero_state(self, theta, batch_size): - p = self.params - height = p.inputs_shape[1] - width = p.inputs_shape[2] - out_channels = p.cell_shape[3] - return py_utils.NestedMap( - m=py_utils.InitRNNCellState( - tf.stack([batch_size, height, width, out_channels]), - init=p.zero_state_init_params, - dtype=p.dtype, - is_eval=self.do_eval), - c=py_utils.InitRNNCellState( - tf.stack([batch_size, height, width, out_channels]), - init=p.zero_state_init_params, - dtype=p.dtype, - is_eval=self.do_eval)) - - def GetOutput(self, state): - return state.m - - def _Mix(self, theta, state0, inputs): - assert isinstance(inputs.act, list) - # Concate on channels. - xm = tf.concat(inputs.act + [state0.m], 3) - # TODO(yonghui): Possibly change the data_format to NCHW to speed - # up conv2d kernel on gpu. - xmw = tf.nn.conv2d(xm, theta.wm, [1, 1, 1, 1], 'SAME', data_format='NHWC') - return xmw - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - p = self.params - # Bias is applied to channels. - bias = tf.reshape(theta.b, [1, 1, 1, -1]) - i_i, i_g, f_g, o_g = tf.split( - value=xmw + bias, num_or_size_splits=4, axis=3) - new_c = tf.sigmoid(f_g) * state0.c + tf.sigmoid(i_g) * tf.tanh(i_i) - # Clip the cell states to reasonable value. - new_c = py_utils.clip_by_value(new_c, -p.cell_value_cap, p.cell_value_cap) - if p.output_nonlinearity: - new_m = tf.sigmoid(o_g) * tf.tanh(new_c) - else: - new_m = tf.sigmoid(o_g) * new_c - padding = tf.reshape(inputs.padding, [-1, 1, 1, 1]) - new_c = state0.c * padding + new_c * (1.0 - padding) - new_m = state0.m * padding + new_m * (1.0 - padding) - if p.zo_prob > 0.0: - c_random_uniform = tf.random.uniform(tf.shape(new_c), seed=p.random_seed) - m_random_uniform = tf.random.uniform(tf.shape(new_m), seed=p.random_seed) - else: - c_random_uniform = None - m_random_uniform = None - new_c = self._ZoneOut(state0.c, new_c, padding, p.zo_prob, self.do_eval, - c_random_uniform) - new_m = self._ZoneOut(state0.m, new_m, padding, p.zo_prob, self.do_eval, - m_random_uniform) - new_c.set_shape(state0.c.shape) - new_m.set_shape(state0.m.shape) - return py_utils.NestedMap(m=new_m, c=new_c) - - -class SRUCell(RNNCell): - """SRU cell. - - From this paper: https://arxiv.org/abs/1709.02755 - - This is a simple implementation that can be used as a drop-in replacement for - another RNN. It doesn't do the performance tricks that an SRU is capable of, - like unrolling matrix computations over time. This is just a basic - implementation. It does the 4-matrix implementation found in appendix C. - - theta: - - - wm: the parameter weight matrix. All gates combined. - - b: the combined bias vector. - - state: - - - m: the sru output. [batch, cell_nodes] - - c: the sru cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'num_hidden_nodes', 0, 'Number of projection hidden nodes ' - '(see https://arxiv.org/abs/1603.08042). ' - 'Set to 0 to disable projection.') - p.Define( - 'cell_value_cap', 10.0, 'SRU cell values are capped to be within ' - ' [-cell_value_cap, +cell_value_cap]. This can be a' - ' scalar or a scalar tensor.') - p.Define('zo_prob', 0.0, - 'If > 0, applies ZoneOut regularization with the given prob.') - p.Define('couple_input_forget_gates', True, - 'Whether to couple the input and forget gates.') - p.Define('apply_layer_norm', False, 'Apply layer norm to the variables') - p.Define( - 'layer_norm_epsilon', 1e-8, 'Tiny value to guard rsqr against.' - 'value is necessary only if apply_layer_norm is True') - p.Define('apply_pruning', False, 'Whether to prune the weights while' - 'training') - p.Define('apply_pruning_to_projection', False, - 'Whether to prune the weights in the projection layer') - p.Define('gradient_pruning', False, 'Whether to gradient prune the model') - p.Define('bias_init', py_utils.WeightInit.Constant(0.0), - 'Initialization parameters for bias') - # Add cell-recursive vector into the SRU cells (arxiv.org/abs/1709.02755). - p.Define( - 'pointwise_peephole', False, 'Whether c_{t-1} should be used to' - 'calculate gate values by aggregating gate calculations with its ' - 'point-wise dot product with a weight vector.') - p.Define( - 'hidden_scaling_factor', False, - 'scaling factor alpha for hidden layer. See details on alpha in' - 'section 3.2 of https://arxiv.org/pdf/1709.02755.pdf') - p.Define( - 'uniform_heuristic_init', False, - 'When set to True, initialize the weight params with uniform ' - 'distribution of [-sqrt(3/hidden_nodes), +sqrt(3/hidden_nodes)], aka' - 'UniformUnitScaling. This initialization has proven to help NLP tasks' - 'in arxiv.org/abs/1709.02755. This impacts 1) input weight matrices for' - 'gates, 2) projection weight matrices, and 3) cell recursion vectors.') - return p - - def __init__(self, params): - """Initializes SRUCell.""" - super().__init__(params) - assert isinstance(params, hyperparams.Params) - p = self.params - assert p.reset_cell_state is False, ('SRUCell currently doesnt support ' - 'resetting cell state.') - assert isinstance(p.cell_value_cap, (int, float)) - if p.uniform_heuristic_init: - # Setting init = sqrt(3) / sqrt(hidden) * tf.uniform(-1, 1). - p.params_init = py_utils.WeightInit.Uniform( - scale=(math.sqrt(3.0 / float(self.hidden_size)))) - - self._timestep = -1 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - # Define weights. - wm_pc = py_utils.WeightParams( - shape=[p.num_input_nodes, self.num_gates * self.hidden_size], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - - self.CreateVariable('wm', wm_pc, self.AddGlobalVN) - if p.apply_pruning: - mask_pc = py_utils.WeightParams(wm_pc.shape, - py_utils.WeightInit.Constant(1.0), - p.dtype) - threshold_pc = py_utils.WeightParams([], - py_utils.WeightInit.Constant(0.0), - tf.float32) - self.CreateVariable('mask', mask_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'threshold', threshold_pc, theta_fn=None, trainable=False) - - # for gradient based pruning - # gradient and weight snapshots - grad_pc = py_utils.WeightParams(wm_pc.shape, - py_utils.WeightInit.Constant(0.0), - p.dtype) - if p.gradient_pruning: - self.CreateVariable('gradient', grad_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'old_weight', grad_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'old_old_weight', grad_pc, theta_fn=None, trainable=False) - - bias_pc = py_utils.WeightParams( - shape=[self.num_gates * self.hidden_size], - init=p.bias_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('b', bias_pc, self.AddGlobalVN) - - if p.num_hidden_nodes: - w_proj = py_utils.WeightParams( - shape=[self.hidden_size, self.output_size], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('w_proj', w_proj, self.AddGlobalVN) - if p.apply_pruning_to_projection: - proj_mask_pc = py_utils.WeightParams(w_proj.shape, - py_utils.WeightInit.Constant(1.0), - p.dtype) - proj_threshold_pc = py_utils.WeightParams( - [], py_utils.WeightInit.Constant(0.0), tf.float32) - self.CreateVariable( - 'proj_mask', proj_mask_pc, theta_fn=None, trainable=False) - self.CreateVariable( - 'proj_threshold', proj_threshold_pc, trainable=False) - # for gradient based pruning - # gradient and weight snapshots - proj_grad_pc = py_utils.WeightParams(w_proj.shape, - py_utils.WeightInit.Constant(0.0), - p.dtype) - if p.gradient_pruning: - self.CreateVariable('proj_gradient', proj_grad_pc, trainable=False) - self.CreateVariable('proj_old_weight', proj_grad_pc, trainable=False) - self.CreateVariable( - 'proj_old_old_weight', proj_grad_pc, trainable=False) - - # TODO(yuansg): b/136014373 investigate the layer norm initialization and - # implementation, try skipping LP regularization on layer norm and bias. - if p.apply_layer_norm: - f_t_ln_scale = py_utils.WeightParams( - shape=[self.hidden_size], - init=py_utils.WeightInit.Constant(1.0), - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('f_t_ln_scale', f_t_ln_scale, self.AddGlobalVN) - r_t_ln_scale = py_utils.WeightParams( - shape=[self.hidden_size], - init=py_utils.WeightInit.Constant(1.0), - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('r_t_ln_scale', r_t_ln_scale, self.AddGlobalVN) - c_t_ln_scale = py_utils.WeightParams( - shape=[self.hidden_size], - init=py_utils.WeightInit.Constant(1.0), - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('c_t_ln_scale', c_t_ln_scale, self.AddGlobalVN) - if not p.couple_input_forget_gates: - i_t_ln_scale = py_utils.WeightParams( - shape=[self.hidden_size], - init=py_utils.WeightInit.Constant(1.0), - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('i_t_ln_scale', i_t_ln_scale, self.AddGlobalVN) - - if p.pointwise_peephole: - f_t_vector_cell = py_utils.WeightParams( - shape=[self.hidden_size], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('f_t_vector_cell', f_t_vector_cell, self.AddGlobalVN) - r_t_vector_cell = py_utils.WeightParams( - shape=[self.hidden_size], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('r_t_vector_cell', r_t_vector_cell, self.AddGlobalVN) - if not p.couple_input_forget_gates: - i_t_vector_cell = py_utils.WeightParams( - shape=[self.hidden_size], - init=p.params_init, - dtype=p.dtype, - collections=self._VariableCollections()) - self.CreateVariable('i_t_vector_cell', i_t_vector_cell, - self.AddGlobalVN) - - if p.apply_pruning: - if p.gradient_pruning: - pruning_utils.AddToPruningCollections(self.vars.wm, self.vars.mask, - self.vars.threshold, - self.vars.gradient, - self.vars.old_weight, - self.vars.old_old_weight) - else: - pruning_utils.AddToPruningCollections(self.vars.wm, self.vars.mask, - self.vars.threshold) - if p.num_hidden_nodes: - if p.apply_pruning_to_projection: - if p.gradient_pruning: - pruning_utils.AddToPruningCollections(self.vars.w_proj, - self.vars.proj_mask, - self.vars.proj_threshold, - self.vars.proj_gradient, - self.vars.proj_old_weight, - self.vars.proj_old_old_weight) - else: - pruning_utils.AddToPruningCollections(self.vars.w_proj, - self.vars.proj_mask, - self.vars.proj_threshold) - - scope = tf.get_variable_scope() - # Collect some stats. - if p.couple_input_forget_gates: - x_t2, resized, f_t, r_t = tf.split( - value=self.vars.wm, num_or_size_splits=self.num_gates, axis=1) - else: - x_t2, resized, i_t, f_t, r_t = tf.split( - value=self.vars.wm, num_or_size_splits=self.num_gates, axis=1) - _HistogramSummary(p, scope.name + '/wm_i_t', i_t) - _HistogramSummary(p, scope.name + '/wm_x_t2', x_t2) - _HistogramSummary(p, scope.name + '/wm_resized', resized) - _HistogramSummary(p, scope.name + '/wm_f_t', f_t) - _HistogramSummary(p, scope.name + '/wm_r_t', r_t) - - @property - def output_size(self): - return self.params.num_output_nodes - - @property - def hidden_size(self): - return self.params.num_hidden_nodes or self.params.num_output_nodes - - @property - def num_gates(self): - return 4 if self.params.couple_input_forget_gates else 5 - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - def zero_state(self, theta, batch_size): - p = self.params - zero_m = py_utils.InitRNNCellState((batch_size, self.output_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - zero_c = py_utils.InitRNNCellState((batch_size, self.hidden_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - return py_utils.NestedMap(m=zero_m, c=zero_c) - - def GetOutput(self, state): - return state.m - - def LayerNorm(self, theta, gate_name, x, bias): - """Applies layer normalization on the last dimension of 'x'. - - Args: - theta: a NestedMap of layer params. - gate_name: the name of the gate, e.g., 'i_i', 'f_g', 'c', etc. - x: activation tensor, where the last dimension represents channels. - bias: the bias tensor of the gate. - - Returns: - Layer normalized 'x', with the same shape as the input. - """ - p = self.params - if p.apply_layer_norm: - mean = tf.reduce_mean(x, axis=[1], keepdims=True) - centered = x - mean - variance = tf.reduce_mean(tf.square(centered), axis=[1], keepdims=True) - normed = centered * tf.math.rsqrt(variance + p.layer_norm_epsilon) - scale = theta['%s_ln_scale' % gate_name] - x = normed * scale - return x + bias - - def _Mix(self, theta, state0, inputs): - assert isinstance(inputs.act, list) - if self.params.apply_pruning: - wm = tf.multiply(theta.wm, theta.mask) - else: - wm = theta.wm - return py_utils.Matmul(tf.concat(inputs.act, 1), wm) - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - p = self.params - if p.couple_input_forget_gates: - x_t2, resized, f_t, r_t = tf.split( - value=xmw, num_or_size_splits=4, axis=1) - b_t2, b_resized, b_f, b_r = tf.split( - value=tf.expand_dims(theta.b, 0), num_or_size_splits=4, axis=1) - if p.pointwise_peephole: - f_t = f_t + tf.multiply(state0.c, theta.f_t_vector_cell) - f_t = self.LayerNorm(theta, 'f_t', f_t, b_f) - f_t = tf.nn.sigmoid(f_t) - i_t = 1.0 - f_t - else: - x_t2, resized, i_t, f_t, r_t = tf.split( - value=xmw + tf.expand_dims(theta.b, 0), num_or_size_splits=5, axis=1) - b_t2, b_resized, b_i, b_f, b_r = tf.split( - value=tf.expand_dims(theta.b, 0), num_or_size_splits=5, axis=1) - if p.pointwise_peephole: - f_t = f_t + tf.multiply(state0.c, theta.f_t_vector_cell) - i_t = i_t + tf.multiply(state0.c, theta.i_t_vector_cell) - f_t = self.LayerNorm(theta, 'f_t', f_t, b_f) - f_t = tf.nn.sigmoid(f_t) - i_t = self.LayerNorm(theta, 'i_t', i_t, b_i) - i_t = tf.nn.sigmoid(i_t) - - if p.pointwise_peephole: - r_t = r_t + tf.multiply(state0.c, theta.r_t_vector_cell) - r_t = self.LayerNorm(theta, 'r_t', r_t, b_r) - r_t = tf.nn.sigmoid(r_t) - - c_t = f_t * state0.c + i_t * x_t2 - c_t = self.LayerNorm(theta, 'c_t', c_t, 0) - - resized = tf.add(resized, b_resized) - x_t2 = tf.add(x_t2, b_t2) - # Clip the cell states to reasonable value. - if p.cell_value_cap is not None: - c_t = py_utils.clip_by_value(c_t, -p.cell_value_cap, p.cell_value_cap) - # Calculate state outputs. - g_c_t = tf.nn.tanh(c_t) - # Apply scaling factor if needed. - alpha = 1.0 - if p.hidden_scaling_factor: - # For the derivations of alpha please refer to variance computation of - # hidden cells h with respect to the variance of input x in appendix A.3 - # https://arxiv.org/pdf/1709.02755.pdf. - alpha = tf.sqrt(1.0 + tf.exp(p.bias_init.scale * 2.0)) - h_t = r_t * g_c_t + (1.0 - r_t) * resized * alpha - - if p.num_hidden_nodes: - if p.apply_pruning_to_projection: - w_proj = tf.multiply(theta.w_proj, theta.proj_mask) - else: - w_proj = theta.w_proj - h_t = tf.matmul(h_t, w_proj) - - return self._ApplyZoneOut(state0, inputs, c_t, h_t) - - def _ApplyZoneOut(self, state0, inputs, new_c, new_m): - """Apply ZoneOut and returns updated states.""" - p = self.params - if p.zo_prob > 0.0: - assert not py_utils.use_tpu(), ( - 'SRUCell does not support zoneout on TPU yet.') - c_random_uniform = tf.random.uniform(tf.shape(new_c), seed=p.random_seed) - m_random_uniform = tf.random.uniform(tf.shape(new_m), seed=p.random_seed) - else: - c_random_uniform = None - m_random_uniform = None - - new_c = self._ZoneOut(state0.c, new_c, inputs.padding, p.zo_prob, - self.do_eval, c_random_uniform) - new_m = self._ZoneOut(state0.m, new_m, inputs.padding, p.zo_prob, - self.do_eval, m_random_uniform) - new_c.set_shape(state0.c.shape) - new_m.set_shape(state0.m.shape) - return py_utils.NestedMap(m=new_m, c=new_c) - - -class QRNNPoolingCell(RNNCell): - """This implements just the "pooling" part of a quasi-RNN or SRU. - - From these papers: - - - https://arxiv.org/abs/1611.01576 - - https://arxiv.org/abs/1709.02755 - - The pooling part implements gates for recurrence. These architectures split - the transform (conv or FC) from the gating/recurrent part. This cell can - do either the quasi-RNN style or SRU style pooling operation based on params. - - If you want all of the functionality in one RNN cell, use `SRUCell` instead. - - theta: - - Has the trainable zero state. Other weights are done outside the recurrent - loop. - - state: - - - m: the qrnn output. [batch, cell_nodes] - - c: the qrnn cell state. [batch, cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes * num_rnn_matrices] - - padding: the padding. [batch, 1]. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'cell_value_cap', 10.0, 'LSTM cell values are capped to be within ' - ' [-cell_value_cap, +cell_value_cap] if the value is not None. ' - 'It can be a scalar, a scalar tensor or None. When set to None, ' - 'no capping is applied.') - p.Define('zo_prob', 0.0, - 'If > 0, applies ZoneOut regularization with the given prob.') - p.Define('pooling_formula', 'INVALID', - 'Options: quasi_ifo, sru. Which pooling math to use') - return p - - def __init__(self, params): - """Initializes quasi-RNN Cell.""" - super().__init__(params) - assert isinstance(params, hyperparams.Params) - p = self.params - assert p.reset_cell_state is False, ('QRNNPoolingCell currently doesnt ' - 'support resetting cell state.') - assert p.pooling_formula in ('quasi_ifo', 'sru') - assert isinstance(p.cell_value_cap, - (int, float)) or p.cell_value_cap is None - - self._timestep = -1 - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - def zero_state(self, theta, batch_size): - p = self.params - zero_m = py_utils.InitRNNCellState((batch_size, p.num_output_nodes), - init=p.zero_state_init_params, - dtype=p.dtype, - is_eval=self.do_eval) - zero_c = py_utils.InitRNNCellState((batch_size, p.num_output_nodes), - init=p.zero_state_init_params, - dtype=p.dtype, - is_eval=self.do_eval) - return py_utils.NestedMap(m=zero_m, c=zero_c) - - def GetOutput(self, state): - return state.m - - def _Mix(self, theta, state0, inputs): - assert isinstance(inputs.act, list) - # Just do identity. The convolution part of the QRNN has to be done earlier. - return inputs.act - - def _Gates(self, xmw, theta, state0, inputs): - """Compute the new state.""" - p = self.params - if p.pooling_formula == 'quasi_ifo': - z_t, i_t, f_t, o_t = tf.split( - value=tf.concat(xmw, 1), num_or_size_splits=4, axis=1) - # Quasi-RNN "ifo" pooling - c_t = f_t * state0.c + i_t * z_t - h_t = o_t * c_t - elif p.pooling_formula == 'sru': - x_t2, resized, f_t, r_t = tf.split( - value=tf.concat(xmw, 1), num_or_size_splits=4, axis=1) - c_t = f_t * state0.c + (1.0 - f_t) * x_t2 - # TODO(otaviogood): Optimization - Since state doesn't depend on these - # ops, they can be moved outside the loop. - g_c_t = tf.nn.tanh(c_t) - h_t = r_t * g_c_t + (1.0 - r_t) * resized - else: - raise ValueError('Invalid pooling_formula: %s', p.pooling_formula) - - new_c = c_t - new_m = h_t - - # Clip the cell states to reasonable value. - if p.cell_value_cap is not None: - new_c = py_utils.clip_by_value(new_c, -p.cell_value_cap, p.cell_value_cap) - - # Apply Zoneout. - return self._ApplyZoneOut(state0, inputs, new_c, new_m) - - def _ApplyZoneOut(self, state0, inputs, new_c, new_m): - """Apply Zoneout and returns the updated states.""" - p = self.params - if p.zo_prob > 0.0: - c_random_uniform = tf.random.uniform(tf.shape(new_c), seed=p.random_seed) - m_random_uniform = tf.random.uniform(tf.shape(new_m), seed=p.random_seed) - else: - c_random_uniform = None - m_random_uniform = None - - new_c = self._ZoneOut(state0.c, new_c, inputs.padding, p.zo_prob, - self.do_eval, c_random_uniform) - new_m = self._ZoneOut(state0.m, new_m, inputs.padding, p.zo_prob, - self.do_eval, m_random_uniform) - new_c.set_shape(state0.c.shape) - new_m.set_shape(state0.m.shape) - return py_utils.NestedMap(m=new_m, c=new_c) - - -class GRUCell(RNNCell): - """ Gated Recurrent Unit cell. - - implemented: layer normalization, gru_biasing, gru_cell cap, - not yet implemented: pruning, quantization, zone-out (enforced to 0.0 now) - reference: https://arxiv.org/pdf/1412.3555.pdf - - theta: - - - w_n: the parameter weight matrix for the input block. - - w_u: the parameter weight matrix for the update gate - - w_r: the parameter weight matrix for the reset gate - - b_n: the bias vector for the input block - - b_u: the bias vector for the update gate - - b_r: the bias vector for the reset gate - - state: - - - m: the GRU output. [batch, output_cell_nodes] - - c: the GRU cell state. [batch, hidden_cell_nodes] - - inputs: - - - act: a list of input activations. [batch, input_nodes] - - padding: the padding. [batch, 1]. - - reset_mask: optional 0/1 float input to support packed input training. - Shape [batch, 1] - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'num_hidden_nodes', 0, 'Number of projection hidden nodes ' - '(see https://arxiv.org/abs/1603.08042). ' - 'Set to 0 to disable projection.') - p.Define( - 'cell_value_cap', 10.0, 'GRU cell values are capped to be within ' - ' [-cell_value_cap, +cell_value_cap] if the value is not None. ' - 'It can be a scalar, a scalar tensor or None. When set to None, ' - 'no capping is applied.') - p.Define('enable_gru_bias', False, 'Enable the GRU Cell bias.') - p.Define('bias_init', py_utils.WeightInit.Constant(0.0), - 'Initialization parameters for GRU Cell bias') - p.Define('zo_prob', 0.0, - 'If > 0, applies ZoneOut regularization with the given prob.') - p.Define('apply_layer_norm', True, 'Apply layer norm to the variables') - p.Define( - 'layer_norm_epsilon', 1e-8, 'Tiny value to guard rsqr against.' - 'value is necessary only if apply_layer_norm is True') - return p - - def __init__(self, params): - """Initializes GRUCell.""" - super().__init__(params) - assert isinstance(params, hyperparams.Params) - p = self.params - assert isinstance(p.cell_value_cap, - (int, float)) or p.cell_value_cap is None - assert p.zo_prob == 0.0 - - self._timestep = -1 - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - p = self.params - - def CreateVarHelper(variable_name, shape_to_init, params_to_init): - """Utility function to initialize variables. - - Args: - variable_name: the name of the variable - shape_to_init: shape of the variables to be initialized. - params_to_init: p.params_init, p.bias_init, or otherwise specified - returns: initialized variable with name "$variable_name" - """ - return self.CreateVariable( - variable_name, - py_utils.WeightParams( - shape=shape_to_init, - init=params_to_init, - dtype=p.dtype, - collections=self._VariableCollections()), self.AddGlobalVN) - - # Define weights. - # Weight for block input - CreateVarHelper('w_n', - [p.num_input_nodes + self.output_size, self.hidden_size], - p.params_init) - # Weight for update gate - CreateVarHelper('w_u', - [p.num_input_nodes + self.output_size, self.hidden_size], - p.params_init) - # Weight for reset gate - CreateVarHelper('w_r', - [p.num_input_nodes + self.output_size, self.output_size], - p.params_init) - - if p.num_hidden_nodes: - # Set up projection matrix - CreateVarHelper('w_proj', [self.hidden_size, self.output_size], - p.params_init) - CreateVarHelper('b_proj', [self.output_size], p.bias_init) - - if p.enable_gru_bias: - # Bias for the block input - CreateVarHelper('b_n', [self.hidden_size], p.bias_init) - # Bias for update gate - CreateVarHelper('b_u', [self.hidden_size], p.bias_init) - # Bias for the reset gate - CreateVarHelper('b_r', [self.output_size], p.bias_init) - - if p.apply_layer_norm: - assert p.layer_norm_epsilon is not None - ln_unit = py_utils.WeightInit.Constant(0.0) - CreateVarHelper('bn_ln_scale', [self.hidden_size], ln_unit) - CreateVarHelper('bu_ln_scale', [self.hidden_size], ln_unit) - CreateVarHelper('br_ln_scale', [self.output_size], ln_unit) - - @property - def output_size(self): - return self.params.num_output_nodes - - @property - def hidden_size(self): - return self.params.num_hidden_nodes or self.params.num_output_nodes - - def batch_size(self, inputs): - return tf.shape(inputs.act[0])[0] - - def zero_state(self, theta, batch_size): - p = self.params - zero_m = py_utils.InitRNNCellState((batch_size, self.output_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - zero_c = py_utils.InitRNNCellState((batch_size, self.hidden_size), - init=p.zero_state_init_params, - dtype=py_utils.FPropDtype(p), - is_eval=self.do_eval) - return py_utils.NestedMap(m=zero_m, c=zero_c) - - def _ResetState(self, state, inputs): - state.m = inputs.reset_mask * state.m - state.c = inputs.reset_mask * state.c - return state - - def GetOutput(self, state): - return state.m - - def LayerNorm(self, x, scale): - """Applies layer normalization on the last dimension of 'x'. - - Args: - x: activation tensor, where the last dimension represents channels. - scale: the scale tensor of the layer normalization - - Returns: - Layer normalized 'x', with the same shape as the input. - """ - p = self.params - mean = tf.reduce_mean(x, axis=[1], keepdims=True) - centered = x - mean - variance = tf.reduce_mean(tf.square(centered), axis=[1], keepdims=True) - normed = centered * tf.math.rsqrt(variance + p.layer_norm_epsilon) - return normed * scale - - def FProp(self, theta, state0, inputs): - """Forward function. - - GRU has coupled reset gate in the candidate actiavation function for output. - See equation 5 and above in https://arxiv.org/pdf/1412.3555.pdf. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - state0: The previous recurrent state. A `.NestedMap`. - inputs: The inputs to the cell. A `.NestedMap`. - - Returns: - A tuple (state1, extras). - - state1: The next recurrent state. A `.NestedMap`. - - extras: Intermediate results to faciliate backprop. A `.NestedMap`. - """ - - p = self.params - assert isinstance(inputs.act, list) - - # Update all gates - # Compute r_g. r_g has size [batch, output] - r_g = tf.matmul(tf.concat(inputs.act + [state0.m], 1), theta.w_r) - if p.apply_layer_norm: - r_g = self.LayerNorm(r_g, theta.br_ln_scale + 1.0) - if p.enable_gru_bias: - r_g = r_g + theta.b_r - r_g = tf.sigmoid(r_g) - - # Compute u_g and n_g. Both have size [batch, hidden]. - # u_g has size [batch, hidden] - u_g = tf.matmul(tf.concat(inputs.act + [state0.m], 1), theta.w_u) - # size of n_g is [batch, hidden] - n_g = tf.matmul( - tf.concat(inputs.act + [tf.multiply(r_g, state0.m)], 1), theta.w_n) - if p.apply_layer_norm: - u_g = self.LayerNorm(u_g, theta.bu_ln_scale + 1.0) - n_g = self.LayerNorm(n_g, theta.bn_ln_scale + 1.0) - if p.enable_gru_bias: # Add biases to u_g and n_g if needed - u_g = u_g + theta.b_u - n_g = n_g + theta.b_n - - u_g = tf.sigmoid(u_g) - n_g = tf.tanh(n_g) - - new_c = (1.0 - u_g) * (state0.c) + u_g * n_g - - # Clip the cell states to reasonable value. - if p.cell_value_cap is not None: - new_c = py_utils.clip_by_value(new_c, -p.cell_value_cap, p.cell_value_cap) - - # Apply non-linear output is necessary - new_m = new_c - # Apply projection matrix if necessary - if p.num_hidden_nodes: - new_m = tf.matmul(new_m, theta.w_proj) + theta.b_proj - # Apply padding. - new_m = py_utils.ApplyPadding(inputs.padding, new_m, state0.m) - new_c = py_utils.ApplyPadding(inputs.padding, new_c, state0.c) - return py_utils.NestedMap(m=new_m, c=new_c), py_utils.NestedMap() diff --git a/lingvo/core/rnn_cell_test.py b/lingvo/core/rnn_cell_test.py deleted file mode 100644 index 9992c02c..00000000 --- a/lingvo/core/rnn_cell_test.py +++ /dev/null @@ -1,1447 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for rnn_cell.""" - -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import rnn_cell -from lingvo.core import test_utils -import numpy as np - -_INIT_RANDOM_SEED = 429891685 -_NUMPY_RANDOM_SEED = 12345 -_RANDOM_SEED = 98274 - - -class RNNCellTest(test_utils.TestCase, parameterized.TestCase): - - # pyformat: disable - @parameterized.named_parameters( - ('_NoInlineNoGruBias', False, False, - [[-1.085402, 2.161964], [-0.933972, 1.995606], [-0.892969, 2.059967]], - [[0.500292, 0.732436], [0.34267, 0.732542], [0.341799, 0.815305]]), - ('_NoInlineGruBias', False, True, - [[-1.206088, 2.558667], [-1.024555, 2.359131], [-1.006608, 2.385566]], - [[0.726844, 0.932083], [0.537847, 0.932127], [0.536803, 0.967041]]), - ('_InlineNoGruBias', True, False, - [[-1.085402, 2.161964], [-0.933972, 1.995606], [-0.892969, 2.059967]], - [[0.500292, 0.732436], [0.34267, 0.732542], [0.341799, 0.815305]]), - ('_InlineGruBias', True, True, - [[-1.206088, 2.558667], [-1.024555, 2.359131], [-1.006608, 2.385566]], - [[0.726844, 0.932083], [0.537847, 0.932127], [0.536803, 0.967041]])) - # pyformat: enable - def testGRUCell(self, inline, enable_gru_bias, m_expected, c_expected): - params = rnn_cell.GRUCell.Params().Set( - name='gru_rnn', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - bias_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - num_hidden_nodes=2, - enable_gru_bias=enable_gru_bias) - params.vn.global_vn = False # do not set variational noise - params.vn.per_step_vn = False # do not set step wise noise - - gru = rnn_cell.GRUCell(params) - - tf.logging.info('gru vars = %s', gru.vars) - self.assertIn('w_r', gru.vars.w_r.name) - self.assertIn('w_u', gru.vars.w_u.name) - self.assertIn('w_n', gru.vars.w_n.name) - - if enable_gru_bias: - self.assertIn('b_n', gru.vars.b_n.name) - self.assertIn('b_r', gru.vars.b_r.name) - self.assertIn('b_u', gru.vars.b_u.name) - - self.assertEqual( - gru.theta.w_n.get_shape(), - tf.TensorShape([ - params.num_input_nodes + params.num_output_nodes, - params.num_hidden_nodes, - ])) - self.assertEqual( - gru.theta.w_u.get_shape(), - tf.TensorShape([ - params.num_input_nodes + params.num_output_nodes, - params.num_hidden_nodes, - ])) - self.assertEqual( - gru.theta.w_r.get_shape(), - tf.TensorShape([ - params.num_input_nodes + params.num_output_nodes, - params.num_output_nodes, - ])) - - if enable_gru_bias: - self.assertEqual(gru.theta.b_n.get_shape(), - tf.TensorShape([params.num_hidden_nodes])) - self.assertEqual(gru.theta.b_u.get_shape(), - tf.TensorShape([params.num_hidden_nodes])) - self.assertEqual(gru.theta.b_r.get_shape(), - tf.TensorShape([params.num_output_nodes])) - - # Start feeding in inputs. - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=tf.zeros([3, 1])) - c_values = tf.constant(np.random.uniform(size=(3, 2)), tf.float32) - m_values = tf.constant(np.random.uniform(size=(3, 2)), tf.float32) - state0 = py_utils.NestedMap(c=c_values, m=m_values) - state1, _ = gru.FPropDefaultTheta(state0, inputs) - - with self.session( - use_gpu=False, config=py_utils.SessionConfig(inline=inline)): - self.evaluate(tf.global_variables_initializer()) - - variable_count = 11 if enable_gru_bias else 8 - wts = tf.get_collection('GRUCell_vars') - self.assertLen(wts, variable_count) - - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - - # pyformat: disable - @parameterized.named_parameters( - ('_NoInlineNoCIFGNoLSTMBias', False, False, False, - [[0.095727, 0.476658], [0.04662, 0.180589], [0.001656, 0.374141]], - [[0.241993, 0.820267], [0.086863, 0.349722], [0.003176, 0.655448]]), - ('_NoInlineNoCIFGLSTMBias', False, False, True, - [[0.007753, 0.66843], [-0.029904, 0.485617], [-0.026663, 0.654127]], - [[0.033096, 1.013467], [-0.086807, 0.748031], [-0.08087, 1.04254]]), - ('_NoInlineCIFGNoLSTMBias', False, True, False, - [[0.22088, 0.244225], [0.123647, 0.25378], [0.163328, 0.214796]], - [[0.355682, 0.711696], [0.313728, 0.633475], [0.485248, 0.961122]]), - ('_NoInlineCIFGLSTMBias', False, True, True, - [[0.342635, 0.182102], [0.140832, 0.210234], [0.224034, 0.155077]], - [[0.499417, 0.701774], [0.278458, 0.697437], [0.51618, 0.964456]]), - ('_InlineNoCIFGNoLSTMBias', True, False, False, - [[0.095727, 0.476658], [0.04662, 0.180589], [0.001656, 0.374141]], - [[0.241993, 0.820267], [0.086863, 0.349722], [0.003176, 0.655448]]), - ('_InlineNoCIFGLSTMBias', True, False, True, - [[0.007753, 0.66843], [-0.029904, 0.485617], [-0.026663, 0.654127]], - [[0.033096, 1.013467], [-0.086807, 0.748031], [-0.08087, 1.04254]]), - ('_InlineCIFGNoLSTMBias', True, True, False, - [[0.22088, 0.244225], [0.123647, 0.25378], [0.163328, 0.214796]], - [[0.355682, 0.711696], [0.313728, 0.633475], [0.485248, 0.961122]]), - ('_InlineCIFGLSTMBias', True, True, True, - [[0.342635, 0.182102], [0.140832, 0.210234], [0.224034, 0.155077]], - [[0.499417, 0.701774], [0.278458, 0.697437], [0.51618, 0.964456]])) - # pyformat: enable - def testLSTMSimple_P1(self, inline, couple_input_forget_gates, - enable_lstm_bias, m_expected, c_expected): - params = rnn_cell.LSTMCellSimple.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - bias_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - couple_input_forget_gates=couple_input_forget_gates, - enable_lstm_bias=enable_lstm_bias) - params.vn.global_vn = False - params.vn.per_step_vn = False - - lstm = rnn_cell.LSTMCellSimple(params) - tf.logging.info('lstm vars = %s', lstm.vars) - self.assertIn('wm', lstm.vars.wm.name) - - if enable_lstm_bias: - self.assertIn('b', lstm.vars.b.name) - - num_param_vectors = 6 if couple_input_forget_gates else 8 - self.assertEqual(lstm.theta.wm.get_shape(), - tf.TensorShape([4, num_param_vectors])) - - if enable_lstm_bias: - self.assertEqual(lstm.theta.b.get_shape(), - tf.TensorShape([num_param_vectors])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, 2)), tf.float32), - m=tf.constant(np.random.uniform(size=(3, 2)), tf.float32)) - - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session( - use_gpu=False, config=py_utils.SessionConfig(inline=inline)): - self.evaluate(tf.global_variables_initializer()) - - variable_count = 2 if enable_lstm_bias else 1 - wts = tf.get_collection('LSTMCellSimple_vars') - self.assertLen(wts, variable_count) - - # pyformat: disable - # xmw_expected = [ - # [-0.74310219, 1.10182762, 0.67478961, 0.62169313, 0.77394271, - # -0.1691505, -0.39185536, 0.87572402], - # [-0.78952235, 0.04464795, 0.00245538, -0.34931657, 0.22463873, - # 0.02745318, 0.15253648, 0.14931624], - # [-1.58246589, 0.03950393, 0.18513964, -0.25745165, 0.73317981, - # 0.68082684, 0.08576801, 0.62040436]] - # pyformat: enable - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - - # pyformat: disable - @parameterized.named_parameters( - ('_Masked', 0, 2, True, False, - [[0.095727, 0.476658], [0.04662, 0.180589], [0.001656, 0.374141]], - [[0.241993, 0.820267], [0.086863, 0.349722], [0.003176, 0.655448]]), - ('_MaskedProjections', 2, 1, True, True, - [[0.414049], [0.076521], [0.356313]], - [[0.270425, 0.840373], [0.349856, 0.440421], [0.261243, 0.889804]]), - ('_Projections', 2, 1, False, False, [[0.414049], [0.076521], [0.356313]], - [[0.270425, 0.840373], [0.349856, 0.440421], [0.261243, 0.889804]])) - # pyformat: enable - def testLSTMSimple_P2(self, num_hidden_nodes, num_output_nodes, apply_pruning, - apply_pruning_to_projection, m_expected, c_expected): - params = rnn_cell.LSTMCellSimple.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_hidden_nodes=num_hidden_nodes, - num_output_nodes=num_output_nodes, - apply_pruning=apply_pruning, - apply_pruning_to_projection=apply_pruning_to_projection) - params.vn.global_vn = False - params.vn.per_step_vn = False - lstm = rnn_cell.LSTMCellSimple(params) - - tf.logging.info('lstm vars = %s', lstm.vars) - self.assertIn('wm', lstm.vars.wm.name) - self.assertIn('b', lstm.vars.b.name) - if apply_pruning: - self.assertIn('mask', lstm.vars.mask.name) - self.assertIn('threshold', lstm.vars.threshold.name) - if apply_pruning_to_projection: - self.assertIn('w_proj', lstm.vars.w_proj.name) - self.assertIn('proj_mask', lstm.vars.proj_mask.name) - self.assertIn('proj_threshold', lstm.vars.proj_threshold.name) - - num_io = params.num_input_nodes + num_output_nodes - num_param_vectors = 8 - self.assertEqual(lstm.theta.wm.get_shape(), - tf.TensorShape([num_io, num_param_vectors])) - self.assertEqual(lstm.theta.b.get_shape(), - tf.TensorShape([num_param_vectors])) - if apply_pruning_to_projection: - self.assertEqual(lstm.theta.w_proj.get_shape(), tf.TensorShape([2, 1])) - self.assertEqual(lstm.theta.proj_mask.get_shape(), tf.TensorShape([2, 1])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, 2)), tf.float32), - m=tf.constant( - np.random.uniform(size=(3, num_output_nodes)), tf.float32)) - - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session( - use_gpu=False, config=py_utils.SessionConfig(inline=False)): - self.evaluate(tf.global_variables_initializer()) - - if num_hidden_nodes > 0: - variable_count = 3 # weights, biases, projection. - else: - variable_count = 2 # weights, biases. - wts = tf.get_collection('LSTMCellSimple_vars') - self.assertLen(wts, variable_count) - - if apply_pruning: - num_vars = 2 if apply_pruning_to_projection else 1 - masks = tf.get_collection('masks') - self.assertLen(masks, num_vars) - - threshold = tf.get_collection('thresholds') - self.assertLen(threshold, num_vars) - - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - - # pyformat: disable - @parameterized.named_parameters( - ('_GlobalNoise', True, False, tf.float32, True, False, 2, 0., - [[0.080182, 0.4585], [0.050852, 0.245296], [0.023557, 0.382329]], - [[0.387777, 0.819644], [-0.160634, 0.513716], [-0.179584, 0.862915]]), - ('_Double', True, False, tf.float64, True, False, 2, 0., - [[0.3472136, 0.11880029], [0.14214374, 0.33760977], - [0.1168568, 0.32053401]], - [[1.46477364, 0.43743008], [0.57051592, 0.14892339], - [0.6949858, 0.16326128]]), - ('_NoOutputNonlinearity', False, False, tf.float64, False, False, 2, 0., - [[0.532625, 0.083511], [0.118662, 0.110532], [0.121542, 0.084161]], - [[0.789908, 0.312811], [0.192642, 0.207369], [0.167591, 0.172713]]), - ('_ByPass', False, False, tf.float64, False, True, 2, 0., None, None), - ('_WithForgetGateBias', True, False, tf.float64, True, False, 2, -1., - [[0.19534954, 0.10979363], [0.02134449, 0.2821926], - [-0.02530111, 0.25382254]], - [[1.29934979, 0.31769676], [0.41655035, 0.05172589], - [0.58909841, -0.00438461]])) - # pyformat: enable - def testLSTMSimple_P3(self, global_vn, couple_input_forget_gates, dtype, - output_nonlinearity, bypass, num_output_nodes, - forget_gate_bias, m_expected, c_expected): - params = rnn_cell.LSTMCellSimple.Params().Set( - name='lstm', - couple_input_forget_gates=couple_input_forget_gates, - output_nonlinearity=output_nonlinearity, - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=num_output_nodes, - dtype=dtype, - forget_gate_bias=forget_gate_bias) - params.vn.seed = 8820495 - params.vn.global_vn = global_vn - params.vn.per_step_vn = False - params.vn.scale = 0.5 - - lstm = rnn_cell.LSTMCellSimple(params) - - np.random.seed(_NUMPY_RANDOM_SEED) - padding = tf.ones([3, 1]) if bypass else tf.zeros([3, 1]) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), dtype)], - padding=padding) - state0 = py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, 2)), dtype), - m=tf.constant(np.random.uniform(size=(3, 2)), dtype)) - - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - - wts = tf.get_collection('LSTMCellSimple_vars') - self.assertLen(wts, 2) - - if bypass: - m_expected = state0.m.eval() - c_expected = state0.c.eval() - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - - # pyformat: disable - @parameterized.named_parameters( - ('', False, - [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 2.1, 2.1, 2.1, 0.1, 0.1, 0.1]), - ('_CoupledInputForget', True, - [0.1, 0.1, 0.1, 2.1, 2.1, 2.1, 0.1, 0.1, 0.1])) - # pyformat: enable - def testLSTMSimpleWithForgetGateInitBias(self, couple_input_forget_gates, - b_expected): - params = rnn_cell.LSTMCellSimple.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Constant(0.1), - couple_input_forget_gates=couple_input_forget_gates, - num_input_nodes=2, - num_output_nodes=3, - forget_gate_bias=2.0, - bias_init=py_utils.WeightInit.Constant(0.1), - dtype=tf.float64) - - lstm = rnn_cell.LSTMCellSimple(params) - - np.random.seed(_NUMPY_RANDOM_SEED) - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - b_value = lstm._GetBias(lstm.theta).eval() - tf.logging.info('testLSTMSimpleWithForgetGateInitBias b = %s', - np.array_repr(b_value)) - self.assertAllClose(b_value, b_expected) - - # pyformat: disable - @parameterized.named_parameters( - ('_NoLSTMBias', False, - [[0.09375, 0.460938], [0.046875, 0.179688], [0.039062, 0.375]], - [[0.234375, 0.789062], [0.09375, 0.351562], [0.078125, 0.664062]]), - ('_LSTMBias', True, - [[0.039062, 0.476562], [0.007812, 0.445312], [0.015625, 0.546875]], - [[0.148438, 0.78125], [0.023438, 0.710938], [0.039062, 0.984375]])) - # pyformat: enable - def testQuantizedLSTMSimple(self, enable_lstm_bias, m_expected, c_expected): - params = rnn_cell.LSTMCellSimple.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - bias_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - couple_input_forget_gates=False, - enable_lstm_bias=enable_lstm_bias) - params.vn.global_vn = False - params.vn.per_step_vn = False - - cc_schedule = quant_utils.FakeQuantizationSchedule.Params().Set( - clip_start_step=0, # Step 0 is unclipped. - clip_end_step=0, - quant_start_step=0, - start_cap=1.0, - end_cap=1.0) - params.qdomain.default = quant_utils.SymmetricScheduledClipQDomain.Params( - ).Set(cc_schedule=cc_schedule.Copy()) - params.qdomain.c_state = quant_utils.SymmetricScheduledClipQDomain.Params( - ).Set(cc_schedule=cc_schedule.Copy()) - params.qdomain.m_state = quant_utils.SymmetricScheduledClipQDomain.Params( - ).Set(cc_schedule=cc_schedule.Copy()) - params.qdomain.fullyconnected = ( - quant_utils.SymmetricScheduledClipQDomain.Params().Set( - cc_schedule=cc_schedule.Copy())) - - params.cell_value_cap = None - - lstm = rnn_cell.LSTMCellSimple(params) - - tf.logging.info('lstm vars = %s', lstm.vars) - self.assertIn('wm', lstm.vars.wm.name) - - if enable_lstm_bias: - self.assertIn('b', lstm.vars.b.name) - - num_param_vectors = 8 - self.assertEqual(lstm.theta.wm.get_shape(), - tf.TensorShape([4, num_param_vectors])) - - if enable_lstm_bias: - self.assertEqual(lstm.theta.b.get_shape(), - tf.TensorShape([num_param_vectors])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, 2)), tf.float32), - m=tf.constant(np.random.uniform(size=(3, 2)), tf.float32)) - - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session( - use_gpu=False, config=py_utils.SessionConfig(inline=False)): - self.evaluate(tf.global_variables_initializer()) - - variable_count = 2 if enable_lstm_bias else 1 - wts = tf.get_collection('LSTMCellSimple_vars') - self.assertLen(wts, variable_count) - - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - - @parameterized.named_parameters(('_WithoutOutputShuffling', 1), - ('_WithOutputShuffling', 2)) - def testLSTMCellGrouped(self, num_shuffle_shards): - params = rnn_cell.LSTMCellGrouped.Params().Set( - name='lstm', - num_input_nodes=8, - num_output_nodes=8, - num_groups=4, - num_shuffle_shards=num_shuffle_shards) - child_p = params.child_lstm_tpl - child_p.output_nonlinearity = True - child_p.params_init = py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED) - child_p.vn.global_vn = False - child_p.vn.per_step_vn = False - - lstm = params.Instantiate() - - tf.logging.info('lstm vars = %s', lstm.vars) - for child_lstm in lstm.groups: - self.assertIn('wm', child_lstm.vars.wm.name) - self.assertIn('b', child_lstm.vars.b.name) - - self.assertEqual(child_lstm.theta.wm.get_shape(), tf.TensorShape([4, 8])) - self.assertEqual(child_lstm.theta.b.get_shape(), tf.TensorShape([8])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 8)), tf.float32)], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - groups=py_utils.SplitRecursively( - py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, 8)), tf.float32), - m=tf.constant(np.random.uniform( - size=(3, 8)), tf.float32)), params.num_groups)) - - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - self.assertLen(state1.groups, params.num_groups) - out1 = lstm.GetOutput(state1) - - with self.session( - use_gpu=False, config=py_utils.SessionConfig(inline=False)): - self.evaluate(tf.global_variables_initializer()) - - variable_count = 2 * params.num_groups # one for weights, one for biases. - wts = tf.get_collection('LSTMCellSimple_vars') - self.assertLen(wts, variable_count) - - state1 = py_utils.ConcatRecursively(state1.groups) - m_actual = state1.m.eval() - c_actual = state1.c.eval() - out_actual = out1.eval() - - tf.logging.info('m_actual = %s', np.array_repr(m_actual)) - tf.logging.info('c_actual = %s', np.array_repr(c_actual)) - tf.logging.info('out_actual = %s', np.array_repr(out_actual)) - - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - m_expected = [ - [ - -0.07857136, 0.43932292, 0.11373602, 0.16337454, - 0.01618987, 0.09685542, -0.20168062, 0.52612996, - ], - [ - 0.07929622, 0.18910739, -0.11084013, 0.32307294, - 0.03500029, -0.05823045, 0.16963124, 0.27039385, - ], - [ - 0.11623365, 0.38104215, 0.00935007, 0.22124135, - -0.17368057, 0.10859803, -0.06948104, 0.10925373, - ], - ] - c_expected = [ - [ - -0.23670214, 0.66260374, 0.24650344, 0.28946888, - 0.03051668, 0.15143034, -0.52736223, 0.88325077, - ], - [ - 0.16262427, 0.28568456, -0.19542629, 0.52116692, - 0.06872599, -0.1123996, 0.31477568, 0.49881396, - ], - [ - 0.19667494, 0.68746102, 0.02078706, 0.30816019, - -0.36376655, 0.16003416, -0.16141629, 0.16648693, - ], - ] - # pylint: enable=bad-whitespace,bad-continuation - # pyformat: enable - out_expected = m_expected - if num_shuffle_shards > 1: - - def _ShuffleShards(x): - return [[row[i] for i in (0, 3, 2, 5, 4, 7, 6, 1)] for row in x] - - assert num_shuffle_shards == 2 - out_expected = _ShuffleShards(out_expected) - self.assertAllClose(m_expected, m_actual) - self.assertAllClose(c_expected, c_actual) - self.assertAllClose(out_expected, out_actual) - - def testLSTMCellGroupedNoInputSplit(self): - params = rnn_cell.LSTMCellGrouped.Params().Set( - name='lstm', - num_input_nodes=8, - num_output_nodes=8, - num_hidden_nodes=16, - num_groups=4, - num_shuffle_shards=1, - split_inputs=False) - child_p = params.child_lstm_tpl - child_p.output_nonlinearity = True - child_p.params_init = py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED) - child_p.vn.global_vn = False - child_p.vn.per_step_vn = False - - lstm = params.Instantiate() - - tf.logging.info('lstm vars = %s', lstm.vars) - for child_lstm in lstm.groups: - self.assertIn('wm', child_lstm.vars.wm.name) - self.assertIn('b', child_lstm.vars.b.name) - self.assertIn('w_proj', child_lstm.vars.w_proj.name) - - # 10 = 8 layer inputs + 2 recurrent - # 16 = 4 gates * 4 hidden units/group - self.assertEqual(child_lstm.theta.wm.get_shape(), tf.TensorShape([10, - 16])) - self.assertEqual(child_lstm.theta.b.get_shape(), tf.TensorShape([16])) - # Projection from 4 hidden units (16 total / 4 groups) to 2 outputs - # (8 total / 4 groups) - self.assertEqual(child_lstm.theta.w_proj.get_shape(), - tf.TensorShape([4, 2])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 8)), tf.float32)], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - groups=py_utils.SplitRecursively( - py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, 16)), tf.float32), - m=tf.constant(np.random.uniform( - size=(3, 8)), tf.float32)), params.num_groups)) - - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - self.assertLen(state1.groups, params.num_groups) - out1 = lstm.GetOutput(state1) - - with self.session( - use_gpu=False, config=py_utils.SessionConfig(inline=False)): - self.evaluate(tf.global_variables_initializer()) - - variable_count = 3 * params.num_groups # [wm, b, w_proj] for each group. - wts = tf.get_collection('LSTMCellSimple_vars') - self.assertLen(wts, variable_count) - - state1 = py_utils.ConcatRecursively(state1.groups) - m_actual = state1.m.eval() - c_actual = state1.c.eval() - out_actual = out1.eval() - - tf.logging.info('m_actual = %s', np.array_repr(m_actual)) - tf.logging.info('c_actual = %s', np.array_repr(c_actual)) - tf.logging.info('out_actual = %s', np.array_repr(out_actual)) - - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - m_expected = [ - [ - 0.61734521, 0.02338588, 0.19424279, 0.31576008, - 0.18000039, 0.1672723, 0.44075012, -0.06824636, - ], - [ - 0.44694018, -0.01717547, 0.49302083, -0.27330822, - 0.35382932, -0.1967615, 0.44225505, -0.04489155, - ], - [ - 0.66018867, 0.09434807, 0.643556, 0.0383133, - 0.74754262, -0.01860991, 0.48671043, 0.29460859, - ], - ] - c_expected = [ - [ - -0.52246463, 0.67389512, 0.58692968, 0.75484836, - -0.21763092, 0.45671225, -0.33593893, 1.03087521, - -0.15525842, 0.31072262, 0.14663902, 0.64976436, - -0.40176213, 0.36785093, 0.52653724, 0.73124039, - ], - [ - -0.27722716, 0.90508962, 0.39852297, 0.01676523, - -0.7724061, 0.40351537, 0.20194794, 0.08798298, - -0.39136624, 0.26601788, 0.21635406, -0.05538163, - -0.36326468, 0.64099556, 0.25886536, -0.09711652, - ], - [ - -0.63169837, 0.99831283, 0.53726614, 0.77321815, - -0.67881596, 1.01512539, 0.38799196, 0.26393941, - -0.87696433, 1.29881907, 0.60203284, 0.42675141, - -0.24902672, 1.15422893, 0.70180357, 0.12213309, - ], - ] - out_expected = [ - [ - 0.61734521, 0.02338588, 0.19424279, 0.31576008, - 0.18000039, 0.1672723, 0.44075012, -0.06824636, - ], - [ - 0.44694018, -0.01717547, 0.49302083, -0.27330822, - 0.35382932, -0.1967615, 0.44225505, -0.04489155, - ], - [ - 0.66018867, 0.09434807, 0.643556, 0.0383133, - 0.74754262, -0.01860991, 0.48671043, 0.29460859, - ], - ] - # pylint: enable=bad-whitespace,bad-continuation - # pyformat: enable - self.assertAllClose(m_expected, m_actual) - self.assertAllClose(c_expected, c_actual) - self.assertAllClose(out_expected, out_actual) - - # pyformat: disable - @parameterized.named_parameters( - ('_NoInline', False, False, - [0.4144063, 0.88831079, 0.56665027, 0.30154669, 0.2818037], - [4.72228432, 3.9454143, 3.77556086, 2.76972866, 1.87397099]), - ('_Inline', True, False, - [0.4144063, 0.88831079, 0.56665027, 0.30154669, 0.2818037], - [4.72228432, 3.9454143, 3.77556086, 2.76972866, 1.87397099]), - ('_GlobalNoise', True, True, - [0.21634784, 0.40635043, 0.12228709, 0.51806468, 0.02064975], - [5.21427298, 4.5560832, 4.24992609, 3.85193706, 2.35372424])) - # pyformat: enable - def testConvLSTM(self, inline, global_vn, m_expected, c_expected): - params = rnn_cell.ConvLSTMCell.Params().Set( - name='conv_lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - inputs_shape=[None, 4, 2, 3], - cell_shape=[None, 4, 2, 2], - filter_shape=[3, 2]) - params.vn.seed = 8820495 - params.vn.scale = 0.5 - params.vn.global_vn = global_vn - params.vn.per_step_vn = False - - lstm = rnn_cell.ConvLSTMCell(params) - lstm_vars = lstm.vars - tf.logging.info('lstm vars = %s', lstm_vars) - self.assertIn('wm', lstm_vars.wm.name) - self.assertIn('b', lstm_vars.b.name) - - w = lstm.theta.wm - b = lstm.theta.b - - self.assertEqual(w.get_shape(), tf.TensorShape([3, 2, 5, 8])) - self.assertEqual(b.get_shape(), tf.TensorShape([8])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(5, 4, 2, 3)), tf.float32)], - padding=tf.zeros([5, 1])) - state0 = py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(5, 4, 2, 2)), tf.float32), - m=tf.constant(np.random.uniform(size=(5, 4, 2, 2)), tf.float32)) - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - m1 = tf.reduce_sum(state1.m, [1, 2, 3]) - c1 = tf.reduce_sum(state1.c, [1, 2, 3]) - - with self.session( - use_gpu=False, config=py_utils.SessionConfig(inline=inline)): - self.evaluate(tf.global_variables_initializer()) - - wts = tf.get_collection('ConvLSTMCell_vars') - self.assertLen(wts, 2) - - self.assertAllClose(m_expected, m1.eval()) - self.assertAllClose(c_expected, c1.eval()) - - @parameterized.named_parameters( - ('_Disabled', 0., False, [[0.2, 0.], [0., 2.4], [0.2, 3.4]], False), - ('_Enabled', 0.5, True, [[0.2, 0.], [0., 0.4], [0.2, 0.5]], False), - ('_Eval', 0.5, False, [[0.2, 0.], [0.05, 1.4], [0.15, 1.95]], True)) - def testZoneOut(self, zo_prob, enable_random_uniform, v_expected, is_eval): - params = rnn_cell.LSTMCellSimple.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2) - lstm = rnn_cell.LSTMCellSimple(params) - if enable_random_uniform: - random_uniform = tf.random.uniform([3, 2], seed=98798202) - else: - random_uniform = None - prev_v = [[0.2, 0.0], [0.1, 0.4], [0.1, 0.5]] - cur_v = [[0.3, 1.0], [0.0, 2.4], [0.2, 3.4]] - padding_v = [[1.0], [0.0], [0.0]] - new_v = lstm._ZoneOut( - prev_v, - cur_v, - padding_v, - zo_prob=zo_prob, - is_eval=is_eval, - random_uniform=random_uniform) - - with self.session(use_gpu=False): - # In eval mode, if padding[i] == 1, new_v equals prev_v. - # Otherwise, new_v = zo_prob * prev_v + (1.0 - zo_prob) * cur_v - new_v_evaled = new_v.eval() - tf.logging.info('new_v_evaled = %s', np.array_repr(new_v_evaled)) - self.assertAllClose(v_expected, new_v_evaled) - - # pyformat: disable - @parameterized.named_parameters( - ('_LSTMSimple', rnn_cell.LSTMCellSimple, True, - [[0.0083883, 0.10644437], [0.04662009, 0.18058866], - [0.0016561, 0.37414068]], - [[0.96451449, 0.65317708], [0.08686253, 0.34972212], - [0.00317609, 0.6554482]]), - ('_LSTMSimpleDeterministic', rnn_cell.LSTMCellSimpleDeterministic, False, - [[-0.145889, 0.], [-0.008282, 0.073219], [-0.041057, 0.]], - [[0., 0.532332], [-0.016117, 0.13752], [0., 0.]])) - # pyformat: enable - def testCellWithZoneOut(self, cell_cls, manual_state, m_expected, c_expected): - params = cell_cls.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - zo_prob=0.5, - random_seed=_RANDOM_SEED) - params.vn.global_vn = False - params.vn.per_step_vn = False - lstm = cell_cls(params) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=tf.zeros([3, 1])) - if manual_state: - state0 = py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, 2)), tf.float32), - m=tf.constant(np.random.uniform(size=(3, 2)), tf.float32)) - else: - state0 = lstm.zero_state(lstm.theta, 3) - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - - m_v = state1.m.eval() - c_v = state1.c.eval() - - tf.logging.info('m_v = %s', np.array_repr(m_v)) - tf.logging.info('c_v = %s', np.array_repr(c_v)) - self.assertAllClose(m_expected, m_v) - self.assertAllClose(c_expected, c_v) - - # pyformat: disable - @parameterized.named_parameters( - ('LSTMCell', rnn_cell.LayerNormalizedLSTMCell, None, - [[0.03960676, 0.26547235], [-0.00677715, 0.09782403], - [-0.00272907, 0.31641623]], - [[0.14834785, 0.3804915], [-0.00927538, 0.38059634], - [-0.01014781, 0.46336061]]), - ('LSTMCellSimple', rnn_cell.LayerNormalizedLSTMCellSimple, None, - [[0.03960676, 0.26547235], [-0.00677715, 0.09782403], - [-0.00272907, 0.31641623]], - [[0.14834785, 0.3804915], [-0.00927538, 0.38059634], - [-0.01014781, 0.46336061]]), - ('NormLSTMCellSimple', rnn_cell.NormalizedLSTMCellSimple, None, - [[0.03960676, 0.26547235], [-0.00677715, 0.09782403], - [-0.00272907, 0.31641623]], - [[0.14834785, 0.3804915], [-0.00927538, 0.38059634], - [-0.01014781, 0.46336061]]), - ('LSTMCellLean', rnn_cell.LayerNormalizedLSTMCellLean, None, - [[-0.20482419, 0.55676991], [-0.55648255, 0.20511301], - [-0.20482422, 0.55676997]], - [[0.14834785, 0.3804915], [-0.00927544, 0.38059637], - [-0.01014781, 0.46336061]]), - ('LSTMCellProj', rnn_cell.LayerNormalizedLSTMCellSimple, 4, - [[0.39790073, 0.28511256], [0.41482946, 0.28972796], - [0.47132283, 0.03284446]], - [[-0.3667627, 1.03294277, 0.24229962, 0.43976486], - [-0.15832338, 1.22740746, 0.19910297, -0.14970526], - [-0.57552528, 0.9139322, 0.41805002, 0.58792269]]), - ('NormLSTMCellProj', rnn_cell.NormalizedLSTMCellSimple, 4, - [[0.39790073, 0.28511256], [0.41482946, 0.28972796], - [0.47132283, 0.03284446]], - [[-0.3667627, 1.03294277, 0.24229962, 0.43976486], - [-0.15832338, 1.22740746, 0.19910297, -0.14970526], - [-0.57552528, 0.9139322, 0.41805002, 0.58792269]]), - ('LSTMCellLeanProj', rnn_cell.LayerNormalizedLSTMCellLean, 4, - [[0.51581347, 0.22646663], [0.56025136, 0.16842051], - [0.58704823, -0.07126484]], - [[-0.36676273, 1.03294277, 0.24229959, 0.43976486], - [-0.15832338, 1.22740746, 0.19910295, -0.14970522], - [-0.57552516, 0.9139322, 0.41805002, 0.58792269]])) - # pyformat: enable - def testLN(self, cell_cls, num_hidden_nodes, m_expected, c_expected): - m_v, c_v = self._testLNLSTMCell(cell_cls.Params(), num_hidden_nodes) - self.assertAllClose(m_expected, m_v) - self.assertAllClose(c_expected, c_v) - - def testLNLSTMCellLeanNoLnOnC(self): - """LayerNormalizedLSTMCellLean without normalization on 'c'.""" - m_v, c_v = self._testLNLSTMCell( - rnn_cell.LayerNormalizedLSTMCellLean.Params().Set(enable_ln_on_c=False)) - m_expected = [[0.039607, 0.265472], [-0.006777, 0.097824], - [-0.002729, 0.316416]] - c_expected = [[0.14834785, 0.3804915], [-0.00927544, 0.38059637], - [-0.01014781, 0.46336061]] - self.assertAllClose(m_expected, m_v) - self.assertAllClose(c_expected, c_v) - - @parameterized.named_parameters(('Enable', True), ('Disable', False)) - def testLNLSTMCellLeanLSTMBias(self, enable): - m_expected, c_expected = self._testLNLSTMCell( - rnn_cell.LayerNormalizedLSTMCellSimple.Params().Set( - cell_value_cap=None, - enable_lstm_bias=enable, - bias_init=py_utils.WeightInit.Constant(1.0))) - m, c = self._testLNLSTMCell( - rnn_cell.LayerNormalizedLSTMCellLean.Params().Set( - enable_ln_on_c=False, - enable_lstm_bias=enable, - bias_init=py_utils.WeightInit.Constant(1.0))) - self.assertAllClose(m_expected, m) - self.assertAllClose(c_expected, c) - - @parameterized.named_parameters(('HighThreshold', 0.5), - ('LowThreshold', 5e-4)) - def testLNLSTMCellLeanCellValueCap(self, cell_value_cap): - m_expected, c_expected = self._testLNLSTMCell( - rnn_cell.LayerNormalizedLSTMCellSimple.Params().Set( - enable_lstm_bias=False, cell_value_cap=cell_value_cap)) - m, c = self._testLNLSTMCell( - rnn_cell.LayerNormalizedLSTMCellLean.Params().Set( - enable_ln_on_c=False, - enable_lstm_bias=False, - cell_value_cap=cell_value_cap)) - self.assertAllClose(m_expected, m) - self.assertAllClose(c_expected, c) - - def testLNLSTMCellLeanFeatureParity(self): - """Tests feature parity with LayerNormalizedLSTMCellSimple ... - - under the same configuration. - """ - m_expected, c_expected, grads_expected = self._testLNLSTMCellFPropBProp( - rnn_cell.LayerNormalizedLSTMCellSimple.Params().Set( - enable_lstm_bias=True, cell_value_cap=5e-4)) - m, c, grads = self._testLNLSTMCellFPropBProp( - rnn_cell.LayerNormalizedLSTMCellLean.Params().Set( - enable_lstm_bias=True, - cell_value_cap=5e-4, - enable_ln_on_c=False, - use_ln_bias=False)) - self.assertAllClose(m_expected, m) - self.assertAllClose(c_expected, c) - tf.logging.info('grads_expected: %r', grads_expected) - tf.logging.info('grads_actual: %r', grads) - self.assertAllClose(grads_expected.wm, grads.wm) - self.assertAllClose(grads_expected.b, grads.b) - self.assertAllClose( - grads_expected.ln_scale, - np.concatenate([ - grads.ln_scale_i_i, grads.ln_scale_i_g, grads.ln_scale_f_g, - grads.ln_scale_o_g - ])) - - def _testLNLSTMCellHelper(self, params, num_hidden_nodes): - params = params.Copy().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - random_seed=_RANDOM_SEED) - if num_hidden_nodes is not None: - params.num_hidden_nodes = num_hidden_nodes - params.vn.global_vn = False - params.vn.per_step_vn = False - lstm = params.Instantiate() - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - c=tf.constant( - np.random.uniform(size=(3, lstm.hidden_size)), tf.float32), - m=tf.constant( - np.random.uniform(size=(3, lstm.output_size)), tf.float32)) - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - return lstm, state0, state1 - - def _testLNLSTMCell(self, params, num_hidden_nodes=None): - tf.reset_default_graph() - _, _, state1 = self._testLNLSTMCellHelper(params, num_hidden_nodes) - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - m_v = state1.m.eval() - c_v = state1.c.eval() - tf.logging.info('m_v = %s', np.array_repr(m_v)) - tf.logging.info('c_v = %s', np.array_repr(c_v)) - return m_v, c_v - - def _testLNLSTMCellFPropBProp(self, params, num_hidden_nodes=None): - tf.reset_default_graph() - lstm, _, state1 = self._testLNLSTMCellHelper(params, num_hidden_nodes) - loss = -tf.math.log( - tf.sigmoid( - tf.reduce_sum(tf.square(state1.m)) + - tf.reduce_sum(state1.m * state1.c * state1.c))) - grads = tf.gradients(loss, lstm.vars.Flatten()) - - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - m_v, c_v, grads_v = self.evaluate([state1.m, state1.c, grads]) - - tf.logging.info('m_v = %s', np.array_repr(m_v)) - tf.logging.info('c_v = %s', np.array_repr(c_v)) - grads_val = py_utils.NestedMap() - for (n, _), val in zip(lstm.vars.FlattenItems(), grads_v): - tf.logging.info('%s : %s', n, np.array_repr(val)) - grads_val[n] = val - return m_v, c_v, grads_val - - # pyformat: disable - @parameterized.named_parameters( - ('_NoLnOnC', False, [[-0.606178], [0.599713], [0.657852]], - [[1.261887, -0.029158], [-0.00341, 1.034558], [-0.003731, 1.259534]]), - ('_LnOnC', True, [[-0.751002], [0.784634], [0.784634]], - [[1.261887, -0.029158], [-0.00341, 1.034558], [-0.003731, 1.259534]])) - # pyformat: enable - def testDoubleProjectionLSTMCell(self, enable_ln_on_c, m_expected, - c_expected): - params = rnn_cell.DoubleProjectionLSTMCell.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - enable_ln_on_c=enable_ln_on_c, - num_input_nodes=2, - num_output_nodes=1, - num_hidden_nodes=2, - num_input_hidden_nodes=2) - params.vn.global_vn = False - params.vn.per_step_vn = False - - lstm = params.Instantiate() - tf.logging.info('lstm vars = %s', lstm.vars) - - # Input projection. - self.assertEqual( - lstm.theta.w_input_proj.get_shape(), - tf.TensorShape([ - params.num_input_nodes + params.num_output_nodes, - params.num_input_hidden_nodes, - ])) - # W, LN, bias for the gates. - for gate in ['i_i', 'i_g', 'f_g', 'o_g']: - self.assertEqual( - lstm.theta.get('wm_%s' % gate).get_shape(), - tf.TensorShape( - [params.num_input_hidden_nodes, params.num_hidden_nodes])) - self.assertEqual( - lstm.theta.get('ln_scale_%s' % gate).get_shape(), - tf.TensorShape([params.num_hidden_nodes])) - self.assertEqual( - lstm.theta.get('bias_%s' % gate).get_shape(), - tf.TensorShape([params.num_hidden_nodes])) - # LN and bias for 'c'. - if enable_ln_on_c: - self.assertEqual(lstm.theta.ln_scale_c.get_shape(), - tf.TensorShape([params.num_hidden_nodes])) - self.assertEqual(lstm.theta.bias_c.get_shape(), - tf.TensorShape([params.num_hidden_nodes])) - else: - self.assertNotIn('ln_scale_c', lstm.theta) - self.assertNotIn('bias_c', lstm.theta) - # Output projection. - self.assertEqual( - lstm.theta.w_output_proj.get_shape(), - tf.TensorShape([params.num_hidden_nodes, params.num_output_nodes])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[ - tf.constant( - np.random.uniform(size=(3, params.num_input_nodes)), - tf.float32), - ], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - c=tf.constant( - np.random.uniform(size=(3, params.num_hidden_nodes)), tf.float32), - m=tf.constant( - np.random.uniform(size=(3, params.num_output_nodes)), tf.float32)) - - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session( - use_gpu=False, config=py_utils.SessionConfig(inline=False)): - self.evaluate(tf.global_variables_initializer()) - - wts = tf.get_collection('DoubleProjectionLSTMCell_vars') - if enable_ln_on_c: - self.assertLen(wts, 2 + 3 * 4 + 2) - else: - self.assertLen(wts, 2 + 3 * 4) - - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - - # pyformat: disable - @parameterized.named_parameters( - ('', tf.zeros, - lambda: tf.constant(np.random.uniform(size=(3, 2)), tf.float32), - [[0.097589, 0.579055], [0.046737, 0.187892], [0.001656, 0.426245]], - [[0.241993, 0.820267], [0.086863, 0.349722], [0.003176, 0.655448]]), - ('_Padding', tf.ones, lambda: tf.zeros([3, 2], tf.float32), - [[0., 0.], [0., 0.], [0., 0.]], [[0., 0.], [0., 0.], [0., 0.]])) - # pyformat: enable - def testQuantizedLSTMCell(self, padding_fn, state0_fn, m_expected, - c_expected): - params = rnn_cell.QuantizedLSTMCell.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - cc_schedule=quant_utils.LinearClippingCapSchedule.Params().Set( - start_step=0, end_step=2, start_cap=5.0, end_cap=1.0)) - params.vn.global_vn = False - params.vn.per_step_vn = False - - lstm = rnn_cell.QuantizedLSTMCell(params) - lstm_vars = lstm.vars - tf.logging.info('lstm vars = %s', lstm_vars) - self.assertIn('wm', lstm_vars.wm.name) - - wm = lstm.theta.wm - self.assertEqual(wm.get_shape(), tf.TensorShape([4, 8])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=padding_fn([3, 1])) - state0 = py_utils.NestedMap(c=state0_fn(), m=state0_fn()) - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - self.assertEqual(5.0, - lstm.cc_schedule.GetState(lstm.theta.cc_schedule).eval()) - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 1)) - self.assertEqual(3.0, - lstm.cc_schedule.GetState(lstm.theta.cc_schedule).eval()) - - def testQuantizedLayerNormalizedLSTMCell(self): - params = rnn_cell.LayerNormalizedLSTMCell.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - random_seed=_RANDOM_SEED, - cc_schedule=quant_utils.LinearClippingCapSchedule.Params().Set( - start_step=0, end_step=2, start_cap=5.0, end_cap=1.0)) - params.vn.global_vn = False - params.vn.per_step_vn = False - - lstm = rnn_cell.LayerNormalizedLSTMCell(params) - lstm_vars = lstm.vars - tf.logging.info('lstm vars = %s', lstm_vars) - self.assertIn('wm', lstm_vars.wm.name) - - wm = lstm.theta.wm - self.assertEqual(wm.get_shape(), tf.TensorShape([4, 8])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, 2)), tf.float32), - m=tf.constant(np.random.uniform(size=(3, 2)), tf.float32)) - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - m_expected = [[0.03960676, 0.26547235], [-0.00677715, 0.09782403], - [-0.00272907, 0.31641623]] - c_expected = [[0.14834785, 0.3804915], [-0.00927538, 0.38059634], - [-0.01014781, 0.46336061]] - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - - self.assertEqual(5.0, - lstm.cc_schedule.GetState(lstm.theta.cc_schedule).eval()) - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 1)) - self.assertEqual(3.0, - lstm.cc_schedule.GetState(lstm.theta.cc_schedule).eval()) - - # pyformat: disable - @parameterized.named_parameters( - ('_TrainingUnclipped', False, False, 0, None, - [[0.097589, 0.579055], [0.046737, 0.187892], [0.001656, 0.426245]], - [[0.241993, 0.820267], [0.086863, 0.349722], [0.003176, 0.655448]]), - ('_Training', False, True, 0, [[0.0], [0.0], [1.0]], - [[0.09375, 0.5625], [0.046875, 0.1875], [0.809813, 0.872176]], - [[0.23288, 0.806], [0.090057, 0.355591], [0.747715, 0.961307]]), - ('_HiddenNodes', False, True, 4, None, - [[0.382812, 0.296875], [0.164062, 0.171875], [0.3125, -0.039062]], - [[-0.160339, 0.795929, 0.449707, 0.347534], - [-0.049194, 0.548279, -0.060852, -0.106354], - [-0.464172, 0.345947, 0.407349, 0.430878]]), - ('_Inference', True, False, 0, None, - [[0.09375, 0.5625], [0.046875, 0.1875], [0., 0.429688]], - [[0.23288, 0.806], [0.090057, 0.355591], [-0.003937, 0.662567]])) - # pyformat: enable - def testLSTMCellSimpleQuantized(self, is_inference, set_training_step, - num_hidden_nodes, padding, m_expected, - c_expected): - params = rnn_cell.LSTMCellSimple.Params().Set( - name='lstm', - is_inference=is_inference, - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - num_hidden_nodes=num_hidden_nodes, - output_nonlinearity=False, - cell_value_cap=None, - enable_lstm_bias=False) - params.vn.global_vn = False - params.vn.per_step_vn = False - - cc_schedule = quant_utils.FakeQuantizationSchedule.Params().Set( - clip_start_step=1, # Step 0 is unclipped. - clip_end_step=2, - quant_start_step=2, - start_cap=5.0, - end_cap=1.0) - # Default quantization. - qdomain = quant_utils.SymmetricScheduledClipQDomain.Params().Set( - cc_schedule=cc_schedule) - params.qdomain.default = qdomain - # M state uses the default 8-bit quantziation. - cc_schedule = cc_schedule.Copy() - qdomain = quant_utils.SymmetricScheduledClipQDomain.Params().Set( - cc_schedule=cc_schedule) - params.qdomain.m_state = qdomain - # C state uses 16 bit quantization. - cc_schedule = cc_schedule.Copy().Set(bits=16) - qdomain = quant_utils.SymmetricScheduledClipQDomain.Params().Set( - cc_schedule=cc_schedule) - params.qdomain.c_state = qdomain - # Fully connected layer clips slightly differently. - cc_schedule = cc_schedule.Copy().Set(start_cap=64.0, end_cap=8.0) - qdomain = quant_utils.SymmetricScheduledClipQDomain.Params().Set( - cc_schedule=cc_schedule) - params.qdomain.fullyconnected = qdomain - - lstm = rnn_cell.LSTMCellSimple(params) - lstm_vars = lstm.vars - tf.logging.info('lstm vars = %s', lstm_vars) - self.assertIn('wm', lstm_vars.wm.name) - if num_hidden_nodes: - self.assertIn('w_proj', lstm_vars.w_proj.name) - else: - self.assertNotIn('w_proj', lstm_vars) - - np.random.seed(_NUMPY_RANDOM_SEED) - if padding is None: - padding = tf.zeros([3, 1]) - else: - padding = tf.constant(padding, dtype=tf.float32) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=padding) - state0 = py_utils.NestedMap( - c=tf.constant( - np.random.uniform(size=(3, lstm.hidden_size)), tf.float32), - m=tf.constant( - np.random.uniform(size=(3, lstm.output_size)), tf.float32)) - state1, _ = lstm.FPropDefaultTheta(state0, inputs) - - with self.session(use_gpu=False), self.SetEval(is_inference): - self.evaluate(tf.global_variables_initializer()) - if set_training_step: - # Get it into the fully clipped/quantized part of the schedule. - self.evaluate(tf.assign(py_utils.GetOrCreateGlobalStepVar(), 5)) - - # Outputs. - self.assertAllClose(m_expected, state1.m.eval()) - self.assertAllClose(c_expected, state1.c.eval()) - - # Cell reported zeros. - cell_zero_state = lstm.zero_state(lstm.theta, batch_size=3) - self.assertAllEqual(cell_zero_state.m.eval(), - tf.zeros_like(state0.m).eval()) - self.assertAllEqual(cell_zero_state.c.eval(), - tf.zeros_like(state0.c).eval()) - - # pyformat: disable - @parameterized.named_parameters( - ('_Regular', None, True, 0, False, False, False, False, - [[0.58657289, 0.70520258], [0.32375532, 0.29133356], - [0.58900255, 0.58398587]], - [[0.45297623, 0.88433027], [0.42112729, 0.47023624], - [0.50483131, 0.89583319]]), - ('_WithCellCap', 0.4, True, 0, False, False, False, False, - [[0.569023, 0.472208], [0.314541, 0.260022], [0.543097, 0.379774]], - [[0.4, 0.4], [0.4, 0.4], [0.4, 0.4]]), - ('_WithInputGates', None, False, 0, False, False, False, False, - [[0.18558595, 0.81267989], [0.30404502, 0.35851872], - [0.51972485, 0.77677751]], - [[-0.19633068, 0.78370988], [0.29389063, 0.39952573], - [0.1221304, 0.67298377]]), - ('_WithProjection', None, True, 3, False, False, False, False, - [[0.04926362, 0.54914111], [-0.0501487, 0.32742232], - [-0.19329719, 0.28332305]], - [[-0.101138, 0.8266117, 0.75368524], - [0.31730127, 0.58325875, 0.64149243], - [0.09471729, 0.48504758, 0.53909004]]), - ('_WithNonZeroBiasInit', None, True, 3, True, False, False, False, - [[-0.012749, 0.788564], [-0.215866, 0.891125], [-0.420648, 0.713777]], - [[-0.30021, 0.728136, 0.751739], [0.211836, 0.665222, 0.755629], - [0.112793, 0.281477, 0.44823]]), - ('_WithLayerNormalization', None, True, 0, False, True, False, False, - [[0.301535, 0.744214], [0.38422, -0.524064], [0.328908, 0.658833]], - [[-1., 1.], [0.999999, -0.999999], [-1., 1.]]), - ('_Masked', None, True, 0, False, False, True, False, - [[0.586573, 0.705203], [0.323755, 0.291334], [0.589002, 0.583986]], - [[0.452976, 0.88433], [0.421127, 0.470236], [0.504831, 0.895833]]), - ('_MaskedProjection', None, True, 3, False, False, True, True, - [[0.049264, 0.549141], [-0.050149, 0.327422], [-0.193297, 0.283323]], - [[-0.10113806, 0.8266117, 0.75368524], - [0.31730127, 0.58325875, 0.64149243], - [0.0947173, 0.48504758, 0.53909004]])) - # pyformat: enable - def testSRUCell(self, cell_value_cap, couple_input_forget_gates, - num_hidden_nodes, enable_bias_init, apply_layer_norm, - apply_pruning, apply_pruning_to_projection, m_expected, - c_expected): - params = rnn_cell.SRUCell.Params().Set( - name='sru', - couple_input_forget_gates=couple_input_forget_gates, - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_hidden_nodes=num_hidden_nodes, - num_output_nodes=2, - random_seed=_RANDOM_SEED, - apply_layer_norm=apply_layer_norm, - apply_pruning=apply_pruning, - apply_pruning_to_projection=apply_pruning_to_projection) - if cell_value_cap is not None: - params.cell_value_cap = 0.4 # cell cap set to low level - if enable_bias_init: - params.bias_init = py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED) - sru = rnn_cell.SRUCell(params) - - self.assertIn('wm', sru.vars.wm.name) - self.assertIn('b', sru.vars.b.name) - if apply_pruning: - self.assertIn('mask', sru.vars.mask.name) - self.assertIn('threshold', sru.vars.threshold.name) - if params.apply_pruning_to_projection: - self.assertIn('w_proj', sru.vars.w_proj.name) - self.assertIn('proj_mask', sru.vars.proj_mask.name) - self.assertIn('proj_threshold', sru.vars.proj_threshold.name) - - num_gates = 4 if couple_input_forget_gates else 5 - if num_hidden_nodes > 0: - num_nodes = num_hidden_nodes - else: - num_nodes = params.num_output_nodes - num_param_vectors = num_gates * num_nodes - self.assertEqual(sru.theta.wm.get_shape(), - tf.TensorShape([2, num_param_vectors])) - self.assertEqual(sru.theta.b.get_shape(), - tf.TensorShape([num_param_vectors])) - if params.apply_pruning_to_projection: - self.assertEqual(sru.theta.w_proj.get_shape(), tf.TensorShape([3, 2])) - self.assertEqual(sru.theta.proj_mask.get_shape(), tf.TensorShape([3, 2])) - - np.random.seed(_NUMPY_RANDOM_SEED) - inputs = py_utils.NestedMap( - act=[tf.constant(np.random.uniform(size=(3, 2)), tf.float32)], - padding=tf.zeros([3, 1])) - if params.num_hidden_nodes > 0: - c_dim = params.num_hidden_nodes - else: - c_dim = params.num_output_nodes - state0 = py_utils.NestedMap( - c=tf.constant(np.random.uniform(size=(3, c_dim)), tf.float32), - m=tf.constant(np.random.uniform(size=(3, 2)), tf.float32)) - state1, _ = sru.FPropDefaultTheta(state0, inputs) - - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - m_v = state1.m.eval() - c_v = state1.c.eval() - - tf.logging.info('m_v = %s', np.array_repr(m_v)) - tf.logging.info('c_v = %s', np.array_repr(c_v)) - self.assertAllClose(m_expected, m_v) - self.assertAllClose(c_expected, c_v) - - # pyformat: disable - @parameterized.named_parameters( - ('', 'quasi_ifo', - [[0.40564698, 0.32611847], [0.16975531, 0.45970476], - [0.60286027, 0.24542703]], - [[0.42057115, 0.49928033], [0.56830668, 0.7003305], - [1.28926766, 0.75380397]]), - ('_InSRUMode', 'sru', - [[0.55884922, 0.40396619], [0.71426249, 0.70820922], - [0.823457, 0.60304904]], - [[0.65144706, 0.56252223], [0.75096267, 0.65605044], - [0.79761195, 0.36905324]])) - # pyformat: enable - def testQRNNPoolingCell(self, pooling_formula, m_expected, c_expected): - params = rnn_cell.QRNNPoolingCell.Params().Set( - name='QuasiRNN', - params_init=py_utils.WeightInit.Uniform(1.24, _INIT_RANDOM_SEED), - num_input_nodes=2, - num_output_nodes=2, - zo_prob=0.0, - random_seed=_RANDOM_SEED, - pooling_formula=pooling_formula) - qrnn = rnn_cell.QRNNPoolingCell(params) - - np.random.seed(_NUMPY_RANDOM_SEED) - num_rnn_matrices = 4 - inputs = py_utils.NestedMap( - act=[ - tf.constant( - np.random.uniform( - size=(3, params.num_input_nodes * num_rnn_matrices)), - tf.float32) - ], - padding=tf.zeros([3, 1])) - state0 = py_utils.NestedMap( - c=tf.constant( - np.random.uniform(size=(3, params.num_output_nodes)), tf.float32), - m=tf.constant( - np.random.uniform(size=(3, params.num_output_nodes)), tf.float32)) - state1, _ = qrnn.FPropDefaultTheta(state0, inputs) - - with self.session(use_gpu=False): - self.evaluate(tf.global_variables_initializer()) - m_v = state1.m.eval() - c_v = state1.c.eval() - - tf.logging.info('m_v = %s', np.array_repr(m_v)) - tf.logging.info('c_v = %s', np.array_repr(c_v)) - self.assertAllClose(m_expected, m_v) - self.assertAllClose(c_expected, c_v) - - # pyformat: disable - @parameterized.named_parameters( - ('_FnZeros', False, False, [[0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0]]), - ('_FnRandomNormal', True, False, [[-0.630551, -1.208959, -0.348799]], - [[-0.630551, -1.208959, -0.348799]]), - ('_FnRandomNormalInEval', True, True, [[0.0, 0.0, 0.0]], - [[0.0, 0.0, 0.0]])) - # pyformat: enable - def testLSTMZeroState(self, random_zero_state, is_eval, m_expected, - c_expected): - if random_zero_state: - zero_state_init = py_utils.RNNCellStateInit.RandomNormal(seed=12345) - else: - zero_state_init = py_utils.RNNCellStateInit.Zeros() - params = rnn_cell.LSTMCellSimple.Params().Set( - name='lstm', - params_init=py_utils.WeightInit.Constant(0.1), - num_input_nodes=2, - num_output_nodes=3, - forget_gate_bias=2.0, - bias_init=py_utils.WeightInit.Constant(0.1), - dtype=tf.float64, - zero_state_init_params=zero_state_init) - lstm = rnn_cell.LSTMCellSimple(params) - - with self.session(use_gpu=False), self.SetEval(is_eval): - self.evaluate(tf.global_variables_initializer()) - init_state_value = self.evaluate(lstm.zero_state(lstm.theta, 1)) - - tf.logging.info('testLSTMSimpleWithStateInitializationFn m = %s', - np.array_repr(init_state_value['m'])) - tf.logging.info('testLSTMSimpleWithStateInitializationFn c = %s', - np.array_repr(init_state_value['c'])) - self.assertAllClose(init_state_value['m'], m_expected) - self.assertAllClose(init_state_value['c'], c_expected) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/rnn_layers.py b/lingvo/core/rnn_layers.py deleted file mode 100644 index d1e18ab7..00000000 --- a/lingvo/core/rnn_layers.py +++ /dev/null @@ -1,1436 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Lingvo RNN layers.""" - -import math -import lingvo.compat as tf -from lingvo.core import attention -from lingvo.core import base_layer -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import recurrent -from lingvo.core import rnn_cell - - -def GeneratePackedInputResetMask(segment_id, is_reverse=False): - """Generates mask inputs for RNN cells from segment_id. - - Args: - segment_id: A tensor of shape [time, batch_size, 1]. - is_reverse: True if inputs are fed to the RNN in reverse order. - - Returns: - reset_mask - a tensor of shape [time, batch_size, 1]. Set to 0 for samples - where state needs to be reset (at example boundaries), and 1 otherwise. - """ - segment_id_left = segment_id[:-1] - segment_id_right = segment_id[1:] - - # Mask is a [t-1, bs, 1] tensor. - reset_mask = tf.cast( - tf.equal(segment_id_left, segment_id_right), dtype=segment_id.dtype) - mask_padding_shape = tf.concat( - [tf.ones([1], dtype=tf.int32), - tf.shape(segment_id)[1:]], axis=0) - mask_padding = tf.ones(mask_padding_shape, dtype=segment_id.dtype) - if is_reverse: - reset_mask = tf.concat([reset_mask, mask_padding], axis=0) - else: - reset_mask = tf.concat([mask_padding, reset_mask], axis=0) - return reset_mask - - -class IdentitySeqLayer(base_layer.BaseLayer): - """A no-op sequence layer.""" - - def __init__(self, params): - super().__init__(params) - - def zero_state(self, theta, batch_size): - return py_utils.NestedMap() - - def FPropFullSequence(self, theta, inputs, paddings): - return inputs - - -class RNN(base_layer.BaseLayer): - """Statically unrolled RNN.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('cell', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the RNN cell.') - p.Define( - 'sequence_length', 0, - 'Sequence length to unroll. If > 0, then will unroll to this fixed ' - 'size. If 0, then will unroll to accommodate the size of the inputs ' - 'for each call to FProp.') - p.Define('reverse', False, - 'Whether or not to unroll the sequence in reversed order.') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert not p.packed_input, ('Packed inputs are currently not supported by ' - 'Static RNN') - p.cell.reset_cell_state = p.packed_input - assert p.sequence_length >= 0 - self.CreateChild('cell', p.cell) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.cell.InstantiateVariables() - super()._CreateChildrenVariables() - - def zero_state(self, theta, batch_size): - return self.cell.zero_state(theta.cell, batch_size) - - def FProp(self, theta, inputs, paddings, state0=None): - """Compute RNN forward pass. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: A single tensor or a tuple of tensors with cardinality equal to - rnn_cell.inputs_arity. For every input tensor, the first dimension is - assumed to be time, second dimension batch, and third dimension depth. - paddings: A tensor. First dim is time, second dim is batch, and third dim - is expected to be 1. - state0: If not None, the initial rnn state in a `.NestedMap`. Defaults - to the cell's zero-state. - - Returns: - A tensor of [time, batch, dims]. - The final recurrent state. - """ - p = self.params - rcell = self.cell - assert isinstance(rcell, (rnn_cell.RNNCell)) - if p.sequence_length == 0: - if isinstance(inputs, (tuple, list)): - sequence_length = len(inputs) - else: - sequence_length = py_utils.GetShape(inputs)[0] - else: - sequence_length = p.sequence_length - assert sequence_length >= 1, ('Sequence length must be defined or inputs ' - 'must have fixed shapes.') - with tf.name_scope(p.name): - inputs_sequence = tf.unstack(inputs, num=sequence_length) - paddings_sequence = tf.unstack(paddings, num=sequence_length) - # We start from all 0 states. - if state0: - state = state0 - else: - inputs0 = py_utils.NestedMap( - act=[inputs_sequence[0]], padding=paddings_sequence[0]) - state = rcell.zero_state(theta.cell, rcell.batch_size(inputs0)) - outputs = [None] * sequence_length - if p.reverse: - sequence = range(sequence_length - 1, -1, -1) - else: - sequence = range(0, sequence_length, 1) - for idx in sequence: - cur_input = py_utils.NestedMap(act=[inputs[idx]], padding=paddings[idx]) - state, _ = rcell.FProp(theta.cell, state, cur_input) - outputs[idx] = rcell.GetOutput(state) - return tf.stack(outputs), state - - -class StackedRNNBase(base_layer.BaseLayer): - """Stacked RNN base class.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_layers', 1, 'The number of RNN layers.') - p.Define( - 'num_input_nodes', -1, - 'If >= 0, overrides cell_tpl.num_input_nodes for the first layer.') - p.Define( - 'num_output_nodes', -1, - 'If >= 0, overrides cell_tpl.num_output_nodes for the last layer.') - p.Define('skip_start', 1, 'The first layer start skip connection.') - p.Define( - 'cell_tpl', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the RNN cell(s). ' - 'If cell_tpl is not a list/tuple, the same cell config is used ' - 'for all layers. Otherwise, cell_tpl[i] is the config for ' - 'i-th layer and cell_tpl[-1] is used for the rest of layers.') - p.Define('dropout', layers.DropoutLayer.Params(), - 'Dropout applied to each layer.') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert not p.packed_input, ('Packed inputs are currently not supported by ' - 'Static RNN Base') - - def _GetCellTpls(self): - p = self.params - if not isinstance(p.cell_tpl, (tuple, list)): - cell_tpls = [p.cell_tpl] * p.num_layers - else: - cell_tpls = list(p.cell_tpl) - assert len(p.cell_tpl) <= p.num_layers - last = cell_tpls[-1] - while len(cell_tpls) < p.num_layers: - cell_tpls.append(last) - for cell_tpl in cell_tpls: - cell_tpl.reset_cell_state = p.packed_input - return cell_tpls - - -class StackedFRNNLayerByLayer(StackedRNNBase, quant_utils.QuantizableLayer): - """An implemention of StackedRNNBase which computes layer-by-layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('rnn_tpl', FRNN.Params(), 'Rnn cell default params.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - rnn_params = [] - with tf.name_scope(p.name): - for (i, cell_tpl) in enumerate(self._GetCellTpls()): - params = p.rnn_tpl.Copy() - params.packed_input = p.packed_input - params.allow_implicit_capture = p.allow_implicit_capture - params.name = 'frnn_%d' % i - params.cell = cell_tpl.Copy() - params.cell.name = '%s_%d' % (p.name, i) - if p.num_input_nodes > 0 and i == 0: - params.cell.num_input_nodes = p.num_input_nodes - if p.num_output_nodes > 0 and i == p.num_layers - 1: - params.cell.num_output_nodes = p.num_output_nodes - rnn_params.append(params) - - for i in range(len(rnn_params) - 1): - # Because one layer's output needs to be fed into the next layer's - # input, hence, we have this assertion. We can relax it later by - # allowing more parameterization of the layers. - assert (rnn_params[i].cell.num_output_nodes == rnn_params[i + 1].cell - .num_input_nodes) - - self.CreateChildren('rnn', rnn_params) - self.CreateChild('dropout', p.dropout) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.TrackQTensor('residual') - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - for rnn in self.rnn: - rnn.InstantiateVariables() - self.dropout.InstantiateVariables() - super()._CreateChildrenVariables() - - def zero_state(self, theta, batch_size): - p = self.params - ret = py_utils.NestedMap(rnn=[]) - for i in range(p.num_layers): - state0 = self.rnn[i].zero_state(theta.rnn[i], batch_size) - ret.rnn.append(state0) - return ret - - def FProp(self, theta, inputs, paddings, state0=None): - """Compute RNN forward pass. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: A single tensor of shape [time, batch, dims]. - paddings: A single tensor of shape [time, batch, 1]. - state0: If not None, the initial rnn state in a `.NestedMap`. Defaults - to the init state. - - Returns: - (outputs, state1) - outputs: A tensor of [time, batch, dims]. - state1: The final state. - """ - p = self.params - if not state0: - state0 = self.zero_state(theta, tf.shape(inputs)[1]) - xs = inputs - state1 = py_utils.NestedMap(rnn=[None] * p.num_layers) - for i in range(p.num_layers): - ys, state1.rnn[i] = self.rnn[i].FProp(theta.rnn[i], xs, paddings, - state0.rnn[i]) - ys = self.dropout.FProp(theta.dropout, ys) - if (p.skip_start >= 0 and i >= p.skip_start and - (p.num_input_nodes <= 0 or i != 0) and - (p.num_output_nodes <= 0 or i != p.num_layers - 1)): - ys = self.fns.qadd(ys, xs, qt='residual') - xs = ys - return xs, state1 - - def FPropFullSequence(self, theta, inputs, paddings): - return self.FProp(theta, inputs, paddings)[0] - - -class StackedBiFRNNLayerByLayer(StackedRNNBase, quant_utils.QuantizableLayer): - """An implemention of StackedRNNBase with bidirection RNN layers.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('frnn_tpl', BidirectionalFRNN.Params(), 'Rnn cell default params.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - rnn_params = [] - feature_dim = None - with tf.name_scope(p.name): - for (i, cell_tpl) in enumerate(self._GetCellTpls()): - rnn_p = cell_tpl.Copy() - if i > 0: - rnn_p.num_input_nodes = feature_dim - if p.num_input_nodes > 0 and i == 0: - rnn_p.num_input_nodes = p.num_input_nodes - if p.num_output_nodes > 0 and i == p.num_layers - 1: - rnn_p.num_output_nodes = p.num_output_nodes // 2 - frnn_param = p.frnn_tpl.Copy() - frnn_param.name = 'bidi_rnn_%d' % i - frnn_param.fwd = rnn_p.Copy().Set(name='f_rnn_%d' % i) - frnn_param.bak = rnn_p.Copy().Set(name='b_rnn_%d' % i) - rnn_params.append(frnn_param) - feature_dim = 2 * rnn_p.num_output_nodes - - self.CreateChildren('rnn', rnn_params) - self.CreateChild('dropout', p.dropout) - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.TrackQTensor('residual') - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - for rnn in self.rnn: - rnn.InstantiateVariables() - self.dropout.InstantiateVariables() - super()._CreateChildrenVariables() - - def FProp(self, theta, inputs, paddings): - """Compute the forward pass. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: A single tensor of shape [time, batch, dims]. - paddings: A single tensor of shape [time, batch, 1]. - - Returns: - A tensor of [time, batch, dims]. - """ - p = self.params - xs = inputs - for i in range(p.num_layers): - ys = self.rnn[i].FProp(theta.rnn[i], xs, paddings) - ys = self.dropout.FProp(theta.dropout, ys) - if (p.skip_start >= 0 and i >= p.skip_start and - (p.num_input_nodes <= 0 or i != 0) and - (p.num_output_nodes <= 0 or i != p.num_layers - 1)): - ys = self.fns.qadd(ys, xs, qt='residual') - xs = ys - return xs - - def FPropFullSequence(self, theta, inputs, paddings): - return self.FProp(theta, inputs, paddings) - - -class FRNN(base_layer.BaseLayer): - """Functional while based RNN.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('cell', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the RNN cell.') - p.Define('reverse', False, - 'Whether or not to unroll the sequence in reversed order.') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - p.cell.reset_cell_state = p.packed_input - self.CreateChild('cell', p.cell) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.cell.InstantiateVariables() - super()._CreateChildrenVariables() - - @property - def rnn_cell(self): - return self.cell - - def zero_state(self, theta, batch_size): - return self.cell.zero_state(theta.cell, batch_size) - - def FProp(self, theta, inputs, paddings, state0=None, segment_id=None): - """Compute RNN forward pass. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: A single tensor or a tuple of tensors with cardinality equal to - rnn_cell.inputs_arity. For every input tensor, the first dimension is - assumed to be time, second dimension batch, and third dimension depth. - paddings: A tensor. First dim is time, second dim is batch, and third dim - is expected to be 1. - state0: If not None, the initial rnn state in a `.NestedMap`. Defaults - to the cell's zero-state. - segment_id: A tensor to support packed inputs. First dim is time, second - dim is batch, and third dim is expected to be 1. - - Returns: - A tensor of [time, batch, dims]. - The final recurrent state. - """ - p = self.params - rcell = self.cell - assert isinstance(rcell, (rnn_cell.RNNCell)) - - def FlipUpDown(x): - # Reverse the first dimension (time) - return tf.reverse(x, [0]) - - if not isinstance(inputs, (list, tuple)): - inputs = [inputs] - - if p.packed_input: - assert segment_id is not None - reset_mask = GeneratePackedInputResetMask( - segment_id, is_reverse=p.reverse) - reset_mask = py_utils.HasShape(reset_mask, tf.shape(paddings)) - else: - reset_mask = tf.zeros_like(paddings) - - if p.reverse: - inputs = [FlipUpDown(x) for x in inputs] - paddings = FlipUpDown(paddings) - reset_mask = FlipUpDown(reset_mask) - - if not state0: - inputs0 = py_utils.NestedMap( - act=[x[0] for x in inputs], - padding=paddings[0, :], - reset_mask=reset_mask[0, :]) - state0 = rcell.zero_state(theta.cell, rcell.batch_size(inputs0)) - - inputs = py_utils.NestedMap( - act=inputs, padding=paddings, reset_mask=reset_mask) - - acc_state, final_state = recurrent.Recurrent( - theta=theta.cell, - state0=state0, - inputs=inputs, - cell_fn=rcell.FProp, - cell_type=rcell.layer_type, - accumulator_layer=self, - allow_implicit_capture=p.allow_implicit_capture) - - act = rcell.GetOutput(acc_state) - if p.reverse: - act = FlipUpDown(act) - return act, final_state - - -class BidirectionalFRNN(base_layer.BaseLayer): - """Bidirectional functional RNN.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('fwd', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the forward RNN cell.') - p.Define('bak', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the backward RNN cell.') - p.Define('rnn', FRNN.Params(), 'Config for underlying RNNs') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - def __init__(self, params): - super().__init__(params) - p = params - - params_forward = p.rnn.Copy() - params_forward.name = 'fwd' - params_forward.dtype = p.dtype - params_forward.reverse = False - params_forward.packed_input = p.packed_input - params_forward.cell = p.fwd.Copy() - self.CreateChild('fwd_rnn', params_forward) - - params_backward = p.rnn.Copy() - params_backward.name = 'bak' - params_backward.dtype = p.dtype - params_backward.reverse = True - params_backward.packed_input = p.packed_input - params_backward.cell = p.bak.Copy() - self.CreateChild('bak_rnn', params_backward) - - def _CreateChildrenVariables(self): - if py_utils.use_tpu() and self.cluster.num_devices_per_split > 1: - fwd_device = self.cluster.WorkerDeviceInModelSplit(0) - bwd_device = self.cluster.WorkerDeviceInModelSplit(1) - else: - fwd_device = '' - bwd_device = '' - with tf.device(fwd_device): - self.fwd_rnn.InstantiateVariables() - with tf.device(bwd_device): - self.bak_rnn.InstantiateVariables() - super()._CreateChildrenVariables() - - def FProp(self, theta, inputs, paddings, segment_id=None): - """Compute bidi-RNN forward pass. - - `rcell_forward` unroll the sequence in the forward direction and - `rcell_backward` unroll the sequence in the backward direction. The - outputs are concatenated in the last output dim. - - See `FRNN.FProp` for more details. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: A single tensor or a tuple of tensors with cardinality equal to - rnn_cell.inputs_arity. For every input tensor, the first dimension is - assumed to be time, second dimension batch, and third dimension depth. - paddings: A tensor. First dim is time, second dim is batch, and third dim - is expected to be 1. - segment_id: A tensor to support packed inputs. First dim is time, second - dim is batch, and third dim is expected to be 1. - - Returns: - A tensor of [time, batch, dims]. - """ - p = self.params - with tf.name_scope(p.name): - - def Fwd(): - """Run the forward pass.""" - output_forward, _ = self.fwd_rnn.FProp( - theta.fwd_rnn, inputs, paddings, segment_id=segment_id) - return output_forward - - def Bwd(): - """Run the backward pass. - - Returns: - A tensor of [time, batch, dims]. The final recurrent state. - """ - output_backward, _ = self.bak_rnn.FProp( - theta.bak_rnn, inputs, paddings, segment_id=segment_id) - # TODO(yonghui/zhifengc): In the current implementation, we copy - # output_forward from gpu:0 to gpu:1, and then copy the concatenated - # output from gpu:1 to gpu:0 to enable next layer computation. It might - # be more efficient to only copy output_backward from gpu:1 to gpu:0 to - # reduce cross-gpu data transfer. - return output_backward - - # On TPU, we run both direction's RNNs on one device to reduce memory - # usage. - if self.cluster.num_devices_per_split > 1: - fwd_device = self.cluster.WorkerDeviceInModelSplit(0) - bwd_device = self.cluster.WorkerDeviceInModelSplit(1) - else: - fwd_device = '' - bwd_device = '' - with tf.device(fwd_device): - output_forward = Fwd() - with tf.device(bwd_device): - output_backward = Bwd() - with tf.device(fwd_device): - return tf.concat([output_forward, output_backward], -1) - - -class BidirectionalRNN(base_layer.BaseLayer): - """Statically unrolled bidirectional RNN.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('fwd', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the forward RNN cell.') - p.Define('bak', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the backward RNN cell.') - p.Define('sequence_length', 0, 'Sequence length.') - p.Define('rnn', RNN.Params(), 'Config for underlying RNNs') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert not p.packed_input, ('Packed input is currently not supported by ' - 'BiDirectionalRNN') - params_forward = p.rnn.Copy() - params_forward.name = '%s_forward' % p.name - params_forward.cell = p.fwd.Copy() - params_forward.sequence_length = p.sequence_length - self.CreateChild('fwd_rnn', params_forward) - params_backward = p.rnn.Copy() - params_backward.name = '%s_backward' % p.name - params_backward.cell = p.bak.Copy() - params_backward.sequence_length = p.sequence_length - params_backward.reverse = True - self.CreateChild('bak_rnn', params_backward) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.fwd_rnn.InstantiateVariables() - self.bak_rnn.InstantiateVariables() - super()._CreateChildrenVariables() - - def FProp(self, theta, inputs, paddings): - """Compute bidi-RNN forward pass. - - `rcell_forward` is responsible for unrolling the sequence in the forward - direction and `rcell_backward` in the backward direction. Output from - forward and backward rnns are concatenated on the last output dim. - - See `RNN.FProp()` for more details. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: A single tensor or a tuple of tensors with cardinality equal to - rnn_cell.inputs_arity. For every input tensor, the first dimension is - assumed to be time, second dimension batch, and third dimension depth. - paddings: A tensor. First dim is time, second dim is batch, and third dim - is expected to be 1. - - Returns: - A tensor of [time, batch, dims]. - """ - p = self.params - - with tf.name_scope(p.name): - outputs_forward, _ = self.fwd_rnn.FProp(theta.fwd_rnn, inputs, paddings) - outputs_backward, _ = self.bak_rnn.FProp(theta.bak_rnn, inputs, paddings) - return tf.concat([outputs_forward, outputs_backward], axis=-1) - - -class BidirectionalRNNV2(base_layer.BaseLayer): - """Statically unrolled bidirectional RNN.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('fwd', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the forward RNN cell.') - p.Define('bak', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the backward RNN cell.') - p.Define('sequence_length', 0, 'Sequence length.') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - def __init__(self, params): - super().__init__(params) - assert not self.params.packed_input, ('Packed input is currently not ' - 'supported by BiDirectionalRNNV2') - p = BidirectionalRNN.Params() - p.packed_input = self.params.packed_input - p.name = '%s_brnn' % self.params.name - p.fwd = self.params.fwd.Copy() - p.bak = self.params.bak.Copy() - p.sequence_length = self.params.sequence_length - self.CreateChild('brnn', p) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.brnn.InstantiateVariables() - super()._CreateChildrenVariables() - - def _PadSequenceToLength(self, t_input, length, pad_value): - t_input = py_utils.with_dependencies( - [py_utils.assert_less_equal(tf.shape(t_input)[0], length)], t_input) - pad_shape = tf.concat([[length - tf.shape(t_input)[0]], - tf.shape(t_input)[1:]], 0) - padding = tf.zeros(shape=pad_shape, dtype=t_input.dtype) + pad_value - return tf.concat([t_input, padding], 0) - - def FProp(self, theta, inputs, paddings): - """Compute bidi-RNN forward pass. - - rcell_forward is responsible for unrolling the sequence in the forward - direction and rcell_backward in the backward direction. Output from forward - and backward rnns are concatenated on the last output dim. - - See RNN.FProp() for more details. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: A single tensor or a tuple of tensors with cardinality equal to - rnn_cell.inputs_arity. For every input tensor, the first dimension is - assumed to be time, second dimension batch, and third dimension depth. - paddings: A tensor. First dim is time, second dim is batch, and third dim - is expected to be 1. - - Returns: - A tensor of [time, batch, dims]. - """ - p = self.params - if not isinstance(inputs, (list, tuple)): - inputs = [inputs] - seq_len = tf.shape(paddings)[0] - inputs = [ - self._PadSequenceToLength(x, p.sequence_length, 0) for x in inputs - ] - inputs_sequence = [ - list(tf.unstack(x, num=p.sequence_length)) for x in inputs - ] - if len(inputs_sequence) > 1: - inputs_sequence = list(zip(inputs_sequence)) - else: - inputs_sequence = inputs_sequence[0] - paddings_sequence = list( - tf.unstack( - self._PadSequenceToLength(paddings, p.sequence_length, 1.0), - num=p.sequence_length)) - assert len(inputs_sequence) == p.sequence_length - assert len(paddings_sequence) == p.sequence_length - out = self.brnn.FProp(theta.brnn, inputs_sequence, paddings_sequence) - return tf.stack(out)[:seq_len,] - - -def _ConcatLastDim(*args): - """Concatenates all args along the last dimension.""" - return tf.concat(args, tf.rank(args[0]) - 1) - - -def _ShiftRight(x0, xs): - """Shifts xs[:-1] one step to the right and attaches x0 on the left.""" - return tf.concat([[x0], xs[:-1]], axis=0) - - -def _ShiftRightWithMasking(x0, xs, mask): - """Shifts xs[:-1] one step to the right and attaches x0 on the left.""" - return tf.concat([[x0], xs[:-1] * mask[1:]], axis=0) - - -class FRNNWithAttention(base_layer.BaseLayer): - """An RNN layer intertwined with an attention layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('cell', rnn_cell.LSTMCellSimple.Params(), - 'Configs for the RNN cell.') - p.Define('attention', attention.AdditiveAttention.Params(), - 'Attention used by this layer.') - p.Define( - 'output_prev_atten_ctx', False, - 'If True, output previous attention context for each position.' - 'Otherwise, output current attention context.') - p.Define( - 'input_prev_atten_ctx', True, - 'If True, concat previous attention context and user input as input.' - 'Otherwise, this layer will still produce attention context but will ' - 'not use previous context as input.') - p.Define( - 'use_zero_atten_state', False, - 'To use zero attention state instead of computing attention with ' - 'zero query vector.') - p.Define('atten_context_dim', 0, 'Size of attention context.') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.use_zero_atten_state: - assert p.atten_context_dim > 0, ( - 'atten_context_dim needs to be set when ' - 'initializing attention state and context with 0.') - if p.packed_input: - assert p.use_zero_atten_state, ( - 'Packed input is only supported when ' - 'training with zero initial attention states.') - p.cell.reset_cell_state = p.packed_input - self.CreateChild('cell', p.cell) - p.attention.packed_input = p.packed_input - # Set p.attention.atten_dropout_deterministic to True by default. - p.attention.atten_dropout_deterministic = True - self.CreateChild('atten', p.attention) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.cell.InstantiateVariables() - self.atten.InstantiateVariables() - super()._CreateChildrenVariables() - - @property - def rnn_cell(self): - return self.cell - - @property - def attention(self): - return self.atten - - def InitForSourcePacked(self, - theta, - src_encs, - src_paddings, - src_contexts=None, - src_segment_id=None): - """A wrapper of InitForSourcePacked of child attention layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - src_encs: A tensor of shape [source_seq_length, batch_size, source_dim]. - src_paddings: A tensor of shape [source_seq_length, batch_size]. - src_contexts: [Optional] If specified, must be a tensor of shape - [source_seq_length, batch_size, some_dim]. When specified, this tensor - will be used as the source context vectors when computing attention - context, and src_ends will be only used to compute the attention score - for each context. If set to None, the 'src_encs' will be used as - source context. - src_segment_id: A tensor of shape [source_seq_length, batch_size], to - support packed inputs. - - Returns: - packed_src - A `.NestedMap` containing packed source. - """ - atten = self.atten - - if src_contexts is None: - src_contexts = src_encs - - if self.params.packed_input: - assert src_segment_id is not None - - # Initial attention state. - return atten.InitForSourcePacked( - theta=theta.atten, - source_vecs=src_encs, - source_contexts=src_contexts, - source_padding=src_paddings, - source_segment_id=src_segment_id) - - def zero_state(self, - theta, - src_encs, - packed_src, - batch_size, - atten_state_dim=1): - """Initial state of this layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - src_encs: A tensor of shape [source_seq_length, batch_size, source_dim]. - packed_src: A `.NestedMap` containing packed source. - batch_size: Batch size. - atten_state_dim: Attention state dim when using zero_atten_state - - Returns: - state0 - A `.NestedMap` containing initial states of RNN and attention. - """ - - p = self.params - atten = self.atten - # Initial RNN states. - state0 = py_utils.NestedMap( - rnn=self.cell.zero_state(theta.cell, batch_size)) - - s_seq_len = tf.shape(src_encs)[0] - - zero_atten_state = atten.ZeroAttentionState(s_seq_len, batch_size) - if p.use_zero_atten_state: - zero_atten_context = tf.zeros([batch_size, p.atten_context_dim], - dtype=py_utils.FPropDtype(p)) - state0.atten = zero_atten_context - state0.atten_state = zero_atten_state - state0.atten_probs = tf.zeros([batch_size, s_seq_len], - dtype=py_utils.FPropDtype(p)) - else: - state0.atten, state0.atten_probs, state0.atten_state = ( - atten.ComputeContextVectorWithSource( - theta.atten, packed_src, - tf.zeros([batch_size, p.cell.num_output_nodes], - dtype=py_utils.FPropDtype(p)), zero_atten_state)) - return state0 - - def reset_atten_state(self, theta, state, inputs): - state.atten = inputs.reset_mask * state.atten - state.atten_state = inputs.reset_mask * state.atten_state - state.atten_probs = inputs.reset_mask * state.atten_probs - return state - - def AccumulateStates(self, - theta, - src_encs, - src_paddings, - inputs, - paddings, - src_contexts=None, - state0=None, - src_segment_id=None, - segment_id=None): - """Sets up and runs the recurrence, returning the raw accumulated states. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - src_encs: A tensor of shape [source_seq_length, batch_size, source_dim]. - src_paddings: A tensor of shape [source_seq_length, batch_size]. - inputs: A tensor of [time, batch, dims]. - paddings: A tensor of [time, batch, 1]. - src_contexts: [Optional] If specified, must be a tensor of shape - [source_seq_length, batch_size, some_dim]. When specified, this tensor - will be used as the source context vectors when computing attention - context, and src_ends will be only used to compute the attention score - for each context. If set to None, the 'src_encs' will be used as source - context. - state0: [Optional] If not None, the initial rnn state and attention - context in a `.NestedMap`. Defaults to the cell's zero-state. - src_segment_id: A tensor of shape [source_seq_length, batch_size] to - support masking with packed inputs. - segment_id: A tensor of [time, batch, 1]. - - Returns: - Tuple of 3 NestedMaps: - - - accumulated_state: a NestedMap of accumulated states from recurrence. - - final_state: final_state: The final recurrent state. - - side_info: NestedMap of intermediate results needed for post-processing. - """ - p = self.params - dtype = p.dtype - rcell = self.cell - atten = self.atten - assert dtype == rcell.params.dtype - assert dtype == atten.params.dtype - assert rcell.params.inputs_arity == 1 or rcell.params.inputs_arity == 2 - if segment_id is None: - segment_id = tf.zeros_like(paddings) - - inputs_shape = tf.shape(inputs) - _, batch = inputs_shape[0], inputs_shape[1] - input_dim = tf.shape(inputs)[2] - - packed_src = self.InitForSourcePacked(theta, src_encs, src_paddings, - src_contexts, src_segment_id) - if state0 is None: - zero_atten_state_dim = p.cell.num_input_nodes - input_dim - state0 = self.zero_state(theta, src_encs, packed_src, batch, - zero_atten_state_dim) - else: - assert not p.packed_input, ('packed input is only supported with default ' - 'initial states.') - - def CellFn(theta, state0, inputs): - """Computes one step forward.""" - if p.packed_input: - state0_mod = state0.DeepCopy() - state0_mod = self.reset_atten_state(theta, state0_mod, inputs) - else: - state0_mod = state0 - state1 = py_utils.NestedMap() - if p.input_prev_atten_ctx: - if rcell.params.inputs_arity == 1: - act = [_ConcatLastDim(inputs.act, state0_mod.atten)] - else: - act = [inputs.act, state0_mod.atten] - else: - act = [inputs.act] - state1.rnn, _ = rcell.FProp( - theta.rnn, state0_mod.rnn, - py_utils.NestedMap( - act=act, padding=inputs.padding, reset_mask=inputs.reset_mask)) - - state1.atten, state1.atten_probs, state1.atten_state = ( - atten.ComputeContextVectorWithSource( - theta.atten, - theta.packed_src, - rcell.GetOutput(state1.rnn), - state0_mod.atten_state, - query_segment_id=tf.cast( - tf.squeeze(inputs.segment_id, 1), py_utils.FPropDtype(p)))) - return state1, py_utils.NestedMap() - - if p.packed_input: - reset_mask = GeneratePackedInputResetMask(segment_id) - else: - reset_mask = tf.zeros_like(paddings) - - acc_state, final_state = recurrent.Recurrent( - theta=py_utils.NestedMap( - rnn=theta.cell, - packed_src=packed_src, - atten=theta.atten, - global_step=theta.global_step), - state0=state0, - inputs=py_utils.NestedMap( - act=inputs, - padding=paddings, - reset_mask=reset_mask, - segment_id=segment_id), - cell_fn=CellFn, - accumulator_layer=self, - allow_implicit_capture=p.allow_implicit_capture) - - side_info = py_utils.NestedMap(state0=state0, reset_mask=reset_mask) - return acc_state, final_state, side_info - - def PostProcessStates(self, acc_state, side_info): - """Post-process accumulated states to fulfill FProp's interface. - - Args: - acc_state: a NestedMap of the raw accumulated states from the recurrence. - side_info: side-information collected by AccumulateStates. - - Returns: - A tuple (atten_context, rnn_output, atten_probs). - - - atten_context: a tensor of [time, batch, attention.context_dim]. - - rnn_output: a tensor of [time, batch, rcell.num_output_nodes]. - - atten_probs: a tensor of [time, batch, source_seq_length]. - """ - - p = self.params - rcell = self.cell - state0 = side_info.state0 - reset_mask = side_info.reset_mask - if p.output_prev_atten_ctx: - # Add the initial attention context in and drop the attention context - # in the last position so that the output atten_ctx is previous - # attention context for each target position. - if p.packed_input: - # Note: Assumes first element of mask is padding, as generated by - # GeneratePackedInputResetMask - atten_ctx = _ShiftRightWithMasking(state0.atten, acc_state.atten, - reset_mask) - atten_probs = _ShiftRightWithMasking(state0.atten_probs, - acc_state.atten_probs, reset_mask) - else: - atten_ctx = _ShiftRight(state0.atten, acc_state.atten) - atten_probs = _ShiftRight(state0.atten_probs, acc_state.atten_probs) - else: - atten_ctx = acc_state.atten - atten_probs = acc_state.atten_probs - - return atten_ctx, rcell.GetOutput(acc_state.rnn), atten_probs - - def FProp(self, - theta, - src_encs, - src_paddings, - inputs, - paddings, - src_contexts=None, - state0=None, - src_segment_id=None, - segment_id=None): - """Forward propagate through a rnn layer with attention. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - src_encs: A tensor of shape [source_seq_length, batch_size, source_dim]. - src_paddings: A tensor of shape [source_seq_length, batch_size]. - inputs: A tensor of [time, batch, dims]. - paddings: A tensor of [time, batch, 1]. - src_contexts: [Optional] If specified, must be a tensor of shape - [source_seq_length, batch_size, some_dim]. When specified, this tensor - will be used as the source context vectors when computing attention - context, and src_ends will be only used to compute the attention score - for each context. If set to None, the 'src_encs' will be used as source - context. - state0: [Optional] If not None, the initial rnn state and attention - context in a `.NestedMap`. Defaults to the cell's zero-state. - src_segment_id: A tensor of shape [source_seq_length, batch_size] to - support masking with packed inputs. - segment_id: A tensor of [time, batch, 1]. - - Returns: - A tuple (atten_context, rnn_output, atten_probs, final_state). - - - atten_context: a tensor of [time, batch, attention.context_dim]. - - rnn_output: a tensor of [time, batch, rcell.num_output_nodes]. - - atten_probs: a tensor of [time, batch, source_seq_length]. - - final_state: The final recurrent state. - """ - - acc_state, final_state, side_info = self.AccumulateStates( - theta, src_encs, src_paddings, inputs, paddings, src_contexts, state0, - src_segment_id, segment_id) - - atten_context, rnn_output, atten_probs = self.PostProcessStates( - acc_state, side_info) - return atten_context, rnn_output, atten_probs, final_state - - -class MultiSourceFRNNWithAttention(base_layer.BaseLayer): - """RNN layer intertwined with an attention layer for multiple sources. - - Allows different attention params per source, if attention is not shared. - """ - - @classmethod - def Params(cls): - """Params for this MultiSourceFRNNWithAttention class.""" - p = super().Params() - p.Define( - 'cell', - rnn_cell.LSTMCellSimple.Params().Set( - params_init=py_utils.WeightInit.Uniform(0.04)), - 'Configs for the RNN cell.') - p.Define( - 'attention_tpl', attention.AdditiveAttention.Params(), - 'Attention used by this attention layer, can be overridden by ' - 'source_name_to_attention_params.') - p.Define( - 'atten_merger', attention.MergerLayer.Params(), - 'Merger layer config for combining context vectors computed for ' - 'different source encodings.') - p.Define('source_names', None, 'List of source names.') - p.Define('share_attention', False, 'If set single attention layer shared.') - p.Define( - 'source_name_to_attention_params', None, - 'Can be set if share_attention is False. Allows defining ' - 'different attention params per source in a dictionary, eg. ' - '{"src1": atten_tpl1, "src2": atten_tpl2}') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - @property - def rnn_cell(self): - """Reference to the RNN cell of this layer.""" - return self.cell - - @property - def attention(self): - """Reference to the attention layer(s) of this layer.""" - return self.attentions - - def __init__(self, params): - """Constructs a MultiSourceFRNNWithAttention layer with params.""" - super().__init__(params) - p = self.params - assert not p.packed_input, ('packed input is not supported for ' - 'MultiSourceFRNNWithAttention') - if p.atten_merger is None: - raise ValueError('Merger layer cannot be none!') - if not isinstance(p.source_names, list) or not p.source_names: - raise ValueError('Source names must be a non-empty list.') - if p.share_attention and p.source_name_to_attention_params: - raise ValueError( - 'Cant specify source_name_to_attention_params with share_attention.') - self.CreateChild('cell', p.cell) - - # Initialize attention layer(s). - params_atten = [] - self._source_dims = [] - src_to_att = p.source_name_to_attention_params - for src_name in p.source_names: - if src_to_att and src_name in src_to_att: - att_params = src_to_att[src_name] - att_params.name = 'atten_%s' % src_name - else: - att_params = p.attention_tpl.Copy() - if p.share_attention: - att_params.name = 'atten_shared' - else: - att_params.name = 'atten_%s' % src_name - if att_params.params_init is None: - att_params.params_init = py_utils.WeightInit.Gaussian( - 1. / math.sqrt(att_params.source_dim + att_params.query_dim), - seed=p.random_seed) - att_params.atten_dropout_deterministic = True - params_atten.append(att_params) - self._source_dims.append(att_params.source_dim) - if p.share_attention: - break - self.CreateChildren('attentions', params_atten) - - # Initialize merger layer for attention layer(s). - params = p.atten_merger.Copy() - params.name = 'atten_merger' - self.CreateChild('atten_merger', params) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.cell.InstantiateVariables() - for atten in self.attentions: - atten.InstantiateVariables() - self.atten_merger.InstantiateVariables() - super()._CreateChildrenVariables() - - def InitAttention(self, theta, src_encs, src_paddings, batch_size): - """Computes initial states for attention layer(s). - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - src_encs: A `.NestedMap` object containing source encoding tensors, - each of shape [source_seq_length, batch_size, source_dim]. Children - names of the `.NestedMap` is defined by source_names. - src_paddings: A `.NestedMap` object contraining source padding tensors, - each of shape [source_seq_length, batch_size]. Children names of the - `.NestedMap` is defined by source_names. - batch_size: Scalar Tensor of type int, for initial state shape. - - Returns: - state0 - Initial attention-rnn state in a `.NestedMap`. Zeros for the rnn - initial state, and merger output for attention initial state. - - Transformed source vectors and transposed source vectors. - """ - p = self._params - rcell = self.cell - - # Initial RNN states, theta and auxiliary variables. - state0 = py_utils.NestedMap(rnn=rcell.zero_state(theta.cell, batch_size)) - query_vec0 = tf.zeros([batch_size, p.cell.num_output_nodes], - py_utils.FPropDtype(p)) - - ctxs0 = [] - packed_srcs = py_utils.NestedMap() - for i, src_name in enumerate(p.source_names): - att_idx = (0 if p.share_attention else i) - - packed_srcs[src_name] = self.attentions[att_idx].InitForSourcePacked( - theta.attentions[att_idx], src_encs[src_name], src_encs[src_name], - src_paddings[src_name]) - - # Initial attention state. - s_seq_len = tf.shape(src_encs[src_name])[0] - zero_atten_state = self.attentions[att_idx].ZeroAttentionState( - s_seq_len, batch_size) - ctxs0.append(self.attentions[att_idx].ComputeContextVectorWithSource( - theta.attentions[att_idx], packed_srcs[src_name], query_vec0, - zero_atten_state)[0]) - - # Initial attention state is the output of merger-op. - state0.atten = self.atten_merger.FProp(theta.atten_merger, ctxs0, - query_vec0) - return state0, packed_srcs - - def FProp(self, theta, src_encs, src_paddings, inputs, paddings): - """Forward propagate through a RNN layer with attention(s). - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - src_encs: A `.NestedMap` object containing source encoding tensors, each - of shape [source_seq_length, batch_size, source_dim]. Children names of - the `.NestedMap` is defined by source_names. - src_paddings: A `.NestedMap` object contraining source padding tensors, - each of shape [source_seq_length, batch_size]. Children names of the - `.NestedMap` is defined by source_names. - inputs: A tensor of [time, batch, dims]. - paddings: A tensor of [time, batch, 1]. - - Returns: - A tuple (atten_context, rnn_output) - - - atten_context: a tensor of [time, batch, attention.hidden_dim]. - - rnn_output: a tensor of [time, batch, p.cell.num_output_nodes]. - - Raises: - ValueError: dtype mismatch of attention layers. - """ - p = self.params - dtype = p.dtype - rcell = self.cell - attentions = self.attentions - assert rcell.params.inputs_arity == 1 or rcell.params.inputs_arity == 2 - assert dtype == rcell.params.dtype - for atten in attentions: - if dtype != atten.params.dtype: - raise ValueError('Data type mismatch!') - - # Check if all batch sizes and depths match for source encs and paddings. - src_name_0 = p.source_names[0] - src_encs[src_name_0] = py_utils.with_dependencies([ - py_utils.assert_shape_match( - [tf.shape(src_encs[src_name_0])[1], source_dim], - tf.shape(src_encs[src_name_i])[-2:]) - for src_name_i, source_dim in zip(p.source_names, self._source_dims) - ], src_encs[src_name_0]) - src_paddings[src_name_0] = py_utils.with_dependencies([ - py_utils.assert_equal( - tf.shape(src_paddings[src_name_0])[-1], - tf.shape(src_paddings[src_name_i])[-1]) - for src_name_i in p.source_names[1:] - ], src_paddings[src_name_0]) - - # Compute source transformations and initial rnn states. - state0, packed_src = self.InitAttention(theta, src_encs, src_paddings, - tf.shape(inputs)[1]) - - # Collect individual attention parameters for CellFn. - attens_theta = py_utils.NestedMap({ - src_name: theta.attentions[0 if p.share_attention else i] - for i, src_name in enumerate(p.source_names) - }) - - def CellFn(theta, state0, inputs): - """Computes one step forward.""" - state1 = py_utils.NestedMap() - state1.rnn, _ = rcell.FProp( - theta.rnn, state0.rnn, - py_utils.NestedMap( - act=[_ConcatLastDim(inputs.act, state0.atten)], - padding=inputs.padding)) - - # The ordering in local_ctxs follows p.source_names. - local_ctxs = [] - query_vec = rcell.GetOutput(state1.rnn) - for i, src_name in enumerate(p.source_names): - att_idx = (0 if p.share_attention else i) - local_ctxs.append(attentions[att_idx].ComputeContextVectorWithSource( - theta.attens[src_name], - theta.packed_src[src_name], - query_vec, - state0.atten, - )[0]) - state1.atten = self.atten_merger.FProp(theta.atten_merger, local_ctxs, - query_vec) - return state1, py_utils.NestedMap() - - # Note that, we have a NestedMap for each parameter. - acc_state, _ = recurrent.Recurrent( - theta=py_utils.NestedMap( - rnn=theta.cell, - attens=attens_theta, - packed_src=packed_src, - atten_merger=theta.atten_merger), - state0=state0, - inputs=py_utils.NestedMap(act=inputs, padding=paddings), - cell_fn=CellFn, - accumulator_layer=self, - allow_implicit_capture=p.allow_implicit_capture) - - return acc_state.atten, rcell.GetOutput(acc_state.rnn) - - -class BidirectionalFRNNQuasi(base_layer.BaseLayer): - """Bidirectional functional Quasi-RNN. - - This is very similar to BidirectionalFRNN except the input is a list of the - forward and backward inputs. It is split because quasi-rnns do the - matrix/convolution unrolled over time outside of the recurrent part. Also, - this uses quasi-rnn instead of LSTM. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('fwd', rnn_cell.QRNNPoolingCell.Params(), - 'Configs for the forward RNN cell.') - p.Define('bak', rnn_cell.QRNNPoolingCell.Params(), - 'Configs for the backward RNN cell.') - p.Define('packed_input', False, 'To reset states for packed inputs.') - return p - - def __init__(self, params): - super().__init__(params) - p = params - assert not p.packed_input, ('packed input is not supported for ' - 'BidirectionalFRNNQuasi') - params_forward = FRNN.Params() - params_forward.name = 'fwd' - params_forward.dtype = p.dtype - params_forward.reverse = False - params_forward.cell = p.fwd.Copy() - self.CreateChild('fwd_rnn', params_forward) - - params_backward = FRNN.Params() - params_backward.name = 'bak' - params_backward.dtype = p.dtype - params_backward.reverse = True - params_backward.cell = p.bak.Copy() - self.CreateChild('bak_rnn', params_backward) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.fwd_rnn.InstantiateVariables() - self.bak_rnn.InstantiateVariables() - super()._CreateChildrenVariables() - - def FProp(self, theta, inputs, paddings): - """Compute bidi-quasi-RNN forward pass. - - fwd_rnn unroll the sequence in the forward direction and - bak_rnn unroll the sequence in the backward direction. The - outputs are concatenated in the last output dim. - - See `FRNN.FProp` for more details. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: A list of the fwd and bak tensors. Each item in the list should be - A single tensor or a tuple of tensors with cardinality equal to - rnn_cell.inputs_arity. For every input tensor, the first dimension is - assumed to be time, second dimension batch, and third dimension depth. - paddings: A tensor. First dim is time, second dim is batch, and third dim - is expected to be 1. - - Returns: - A tensor of [time, batch, dims]. - """ - p = self.params - with tf.name_scope(p.name): - fwd_device = self.cluster.WorkerDeviceInModelSplit(0) - bwd_device = self.cluster.WorkerDeviceInModelSplit(1) - with tf.device(fwd_device): - output_forward, _ = self.fwd_rnn.FProp(theta.fwd_rnn, inputs[0], - paddings) - with tf.device(bwd_device): - output_backward, _ = self.bak_rnn.FProp(theta.bak_rnn, inputs[1], - paddings) - output_forward = py_utils.HasShape(output_forward, - tf.shape(output_backward)) - out_rank = tf.rank(output_forward) - 1 - # TODO(yonghui/zhifengc): In the current implementation, we copy - # output_forward from gpu:0 to gpu:1, and then copy the concatenated - # output from gpu:1 to gpu:0 to enable next layer computation. It might - # be more efficient to only copy output_backward from gpu:1 to gpu:0 to - # reduce cross-gpu data transfer. - return tf.concat([output_forward, output_backward], out_rank) diff --git a/lingvo/core/rnn_layers_test.py b/lingvo/core/rnn_layers_test.py deleted file mode 100644 index 1fc7f8de..00000000 --- a/lingvo/core/rnn_layers_test.py +++ /dev/null @@ -1,1560 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for rnn_layers.""" - -import types -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import attention -from lingvo.core import base_layer -from lingvo.core import cluster_factory -from lingvo.core import py_utils -from lingvo.core import rnn_cell -from lingvo.core import rnn_layers -from lingvo.core import test_utils -import numpy as np - -from tensorflow.python.ops import inplace_ops - -FLAGS = tf.flags.FLAGS - - -class TimestepAccumulator(base_layer.Accumulator): - """Simple accumulator for counting timesteps.""" - - def DefaultValue(self): - return tf.convert_to_tensor(0.0) - - def Increment(self): - self.SetValue(self.GetValue() + 1.0) - - -def AddTimestepAccumulator(layer): - orig_fprop = layer.FProp - - def WrappedFProp(*args, **kwargs): - layer.accumulators.ts_count.Increment() - return orig_fprop(*args, **kwargs) - - layer.FProp = WrappedFProp - - layer.RegisterAccumulator('ts_count', TimestepAccumulator()) - - -class LayersTestBase(test_utils.TestCase): - - def _testStackedFRNNHelper(self, - cls, - dtype, - trailing_pad_len=0, - keep_prob=1.0, - bi_directional=False, - input_dim=-1, - output_dim=-1): - tf.random.set_seed(123456) - batch = 3 - dims = 16 - slen = 10 + trailing_pad_len - num_layers = 4 - with tf.Graph().as_default() as g: - with self.session(use_gpu=True, graph=g): - params = rnn_cell.LSTMCellSimple.Params() - params.name = 'lstm' - params.output_nonlinearity = True - params.dtype = dtype - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.vn.global_vn = True - params.vn.per_step_vn = False - params.vn.seed = 2938482 - params.vn.scale = 0.1 - params.num_input_nodes = dims - params.num_output_nodes = dims // 2 if bi_directional else dims - - sfrnn_params = cls.Params() - sfrnn_params.name = 'sfrnn' - sfrnn_params.dtype = dtype - sfrnn_params.random_seed = 123456 - sfrnn_params.cell_tpl = params - sfrnn_params.num_layers = num_layers - sfrnn_params.skip_start = 2 - sfrnn_params.dropout.keep_prob = keep_prob - sfrnn_params.num_input_nodes = input_dim - sfrnn_params.num_output_nodes = output_dim - with tf.name_scope('sfrnn'): - sfrnn = sfrnn_params.Instantiate() - - np.random.seed(12345) - input_dim = input_dim if input_dim > 0 else dims - output_dim = output_dim if output_dim > 0 else dims - inputs = tf.constant( - np.random.uniform(size=(slen, batch, input_dim)), dtype) - paddings = np.zeros([slen, batch, 1]) - if trailing_pad_len > 0: - paddings[-trailing_pad_len:, :] = 1.0 - paddings[-trailing_pad_len - 3:-trailing_pad_len - 1, :] = 1.0 - paddings = tf.constant(paddings, dtype) - - self.evaluate(tf.global_variables_initializer()) - if bi_directional: - sfrnn_outputs = sfrnn.FPropFullSequence(sfrnn.theta, inputs, paddings) - sfrnn_outputs = py_utils.HasShape(sfrnn_outputs, - [slen, batch, output_dim]) - return self.evaluate(sfrnn_outputs) - else: - sfrnn_outputs, sfrnn_final = sfrnn.FPropDefaultTheta(inputs, paddings) - sfrnn_outputs = py_utils.HasShape(sfrnn_outputs, - [slen, batch, output_dim]) - return self.evaluate([sfrnn_outputs, sfrnn_final]) - - def _testStackedFRNNGradHelper(self, cls, bi_directional=False): - trailing_pad_len = 2 - dtype = tf.float64 - batch = 3 - dims = 16 - slen = 10 + trailing_pad_len - num_layers = 4 - with self.session(use_gpu=True, graph=tf.Graph()) as sess: - params = rnn_cell.LSTMCellSimple.Params() - params.name = 'lstm' - params.output_nonlinearity = True - params.dtype = dtype - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.vn.global_vn = False - params.vn.per_step_vn = False - params.vn.seed = 2938482 - params.vn.scale = 0.1 - params.num_input_nodes = dims - params.num_output_nodes = dims // 2 if bi_directional else dims - - sfrnn_params = cls.Params() - sfrnn_params.name = 'sfrnn' - sfrnn_params.dtype = dtype - sfrnn_params.cell_tpl = params - sfrnn_params.num_layers = num_layers - sfrnn_params.skip_start = 2 - with tf.name_scope('sfrnn'): - sfrnn = sfrnn_params.Instantiate() - - np.random.seed(12345) - inputs = tf.constant(np.random.uniform(size=(slen, batch, dims)), dtype) - paddings = np.zeros([slen, batch, 1]) - paddings[-trailing_pad_len:, :] = 1.0 - paddings[-trailing_pad_len - 3:-trailing_pad_len - 1, :] = 1.0 - paddings = tf.constant(paddings, dtype) - - if bi_directional: - sfrnn_outputs = sfrnn.FPropDefaultTheta(inputs, paddings) - loss = tf.reduce_sum(sfrnn_outputs) - else: - sfrnn_outputs, sfrnn_final = sfrnn.FPropDefaultTheta(inputs, paddings) - loss = tf.reduce_sum(sfrnn_outputs) - for fin in sfrnn_final.rnn: - loss += tf.reduce_sum(fin.m) + tf.reduce_sum(fin.c) - xs = sfrnn.vars.Flatten() + [inputs] - dxs = tf.gradients(loss, xs) - - # Compares the sym grad against the numeric grads. - self.evaluate(tf.global_variables_initializer()) - grad_step = 17 - sym_grads = self.evaluate(dxs) - sym_grads = [test_utils.PickEveryN(_, grad_step) for _ in sym_grads] - num_grads = [ - test_utils.PickEveryN( - test_utils.ComputeNumericGradient( - sess, loss, v, delta=1e-4, step=grad_step), grad_step) - for v in xs - ] - for (sym, num) in zip(sym_grads, num_grads): - self.assertFalse(np.any(np.isnan(sym))) - self.assertFalse(np.any(np.isnan(num))) - print('max = ', np.max(np.abs(sym))) - self.assertAllClose(sym, num) - - -class LayersTest(LayersTestBase, parameterized.TestCase): - - def testIdentitySeqLayer(self): - with self.session(use_gpu=False): - rnn_params = rnn_layers.IdentitySeqLayer.Params() - rnn_params.name = 'no_op' - rnn = rnn_params.Instantiate() - - np.random.seed(12345) - inputs_sequence = [] - paddings_sequence = [] - for _ in range(5): - inputs_sequence.append( - tf.constant(np.random.uniform(size=(3, 2)), tf.float32)) - paddings_sequence.append(tf.zeros([3, 1])) - - paddings_sequence[-1] = tf.constant([[1.0], [1.0], [1.0]]) - paddings_sequence[-2] = tf.constant([[1.0], [1.0], [1.0]]) - - inputs, paddings = tf.stack(inputs_sequence), tf.stack(paddings_sequence) - outputs = rnn.FPropFullSequence(rnn.theta, inputs, paddings) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - - inputs_v, outputs_v = self.evaluate([inputs, outputs]) - self.assertAllEqual(inputs_v, outputs_v) - - def testRNN(self): - with self.session(use_gpu=False): - params = rnn_cell.LSTMCellSimple.Params() - params.name = 'lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.num_input_nodes = 2 - params.num_output_nodes = 2 - - rnn_params = rnn_layers.RNN.Params() - rnn_params.name = 'rnn' - rnn_params.vn.global_vn = True - rnn_params.vn.per_step_vn = False - rnn_params.vn.seed = 2938482 - rnn_params.vn.scale = 0.1 - rnn_params.cell = params - rnn_params.sequence_length = 10 - rnn = rnn_layers.RNN(rnn_params) - - np.random.seed(12345) - inputs_sequence = [] - paddings_sequence = [] - for _ in range(rnn_params.sequence_length): - inputs_sequence.append( - tf.constant(np.random.uniform(size=(3, 2)), tf.float32)) - paddings_sequence.append(tf.zeros([3, 1])) - - paddings_sequence[-1] = tf.constant([[1.0], [1.0], [1.0]]) - paddings_sequence[-2] = tf.constant([[1.0], [1.0], [1.0]]) - - inputs, paddings = tf.stack(inputs_sequence), tf.stack(paddings_sequence) - outputs, final = rnn.FPropDefaultTheta(inputs, paddings) - - outputs *= paddings - sum_outputs = tf.reduce_sum(outputs, axis=0) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - - actual = self.evaluate(py_utils.NestedMap(sum=sum_outputs, **final)) - - # In case this test ever breaks, you can uncomment the line below, copy - # out the golden values, and then comment out the line below again. - # - sum_expected = [[-0.46614376, 0.86599183], [-0.63463444, 0.57043159], - [-0.64659989, 0.72516292]] - c_expected = [[-0.63455635, 0.76446551], [-0.59985822, 0.6631192], - [-0.63043576, 0.77522433]] - m_expected = [[-0.23307188, 0.43299592], [-0.31731722, 0.2852158], - [-0.32329994, 0.36258146]] - self.assertAllClose(sum_expected, actual.sum) - self.assertAllClose(m_expected, actual.m) - self.assertAllClose(c_expected, actual.c) - - def testRNNGradientChecker(self): - with self.session(use_gpu=False) as sess: - params = rnn_cell.LSTMCellSimple.Params() - params.name = 'lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.num_input_nodes = 2 - params.num_output_nodes = 2 - - rnn_params = rnn_layers.RNN.Params() - rnn_params.name = 'rnn' - rnn_params.dtype = tf.float64 - rnn_params.vn.scale = 0.1 - rnn_params.vn.global_vn = False - rnn_params.vn.per_step_vn = False - rnn_params.cell = params - rnn_params.sequence_length = 10 - rnn = rnn_layers.RNN(rnn_params) - - np.random.seed(12345) - inputs_sequence = [] - paddings_sequence = [] - for _ in range(rnn_params.sequence_length): - inputs_sequence.append( - tf.constant(np.random.uniform(size=(3, 2)), tf.float64)) - paddings_sequence.append(tf.zeros([3, 1], dtype=tf.float64)) - - paddings_sequence[-1] = tf.constant( - [[1.0], [1.0], [1.0]], dtype=tf.float64) - paddings_sequence[-2] = tf.constant( - [[1.0], [1.0], [1.0]], dtype=tf.float64) - - inputs, paddings = tf.stack(inputs_sequence), tf.stack(paddings_sequence) - outputs, final = rnn.FPropDefaultTheta(inputs, paddings) - outputs *= paddings - sum_outputs = tf.reduce_sum(outputs, axis=0) - - loss = tf.reduce_sum(sum_outputs) + tf.reduce_sum(final.m + final.c) - all_vars = tf.get_collection('LSTMCellSimple_vars') - assert len(all_vars) == 2 - - grads = tf.gradients(loss, all_vars) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - - symbolic_grads = [gd.eval() for gd in grads] - numerical_grads = [] - for v in all_vars: - numerical_grads.append(test_utils.ComputeNumericGradient(sess, loss, v)) - for x, y in zip(symbolic_grads, numerical_grads): - self.assertAllClose(x, y) - - def testRNNReversed(self): - """Test an RNN layer with reverse=true. - - This should yield the same output as feeding reversed input into - the same RNN with reverse=false (except the output is in reversed order). - """ - - timesteps = 10 - padding_steps = 2 - batch_size = 2 - depth = 3 - with self.session(use_gpu=True): - lstm_params = rnn_cell.LSTMCellSimple.Params() - lstm_params.output_nonlinearity = True - lstm_params.num_input_nodes = depth - lstm_params.num_output_nodes = depth - - rnn_params = rnn_layers.RNN.Params() - rnn_params.vn.global_vn = False - rnn_params.vn.per_step_vn = False - rnn_params.vn.seed = 2938482 - rnn_params.vn.scale = 0.1 - rnn_params.cell = lstm_params - rnn_params.sequence_length = timesteps - - fwd_rnn_params = rnn_params.Copy() - fwd_rnn_params.name = 'fwd' - fwd_rnn_params.cell.name = 'fwd_lstm' - fwd_rnn = rnn_layers.RNN(fwd_rnn_params) - - bak_rnn_params = rnn_params.Copy() - bak_rnn_params.name = 'bak' - bak_rnn_params.cell.name = 'bak_lstm' - bak_rnn_params.reverse = True - bak_rnn = rnn_layers.RNN(bak_rnn_params) - - # Create 8 timesteps of random input, 2 timesteps of zeros, and paddings - # to match. - fwd_inputs = tf.constant( - np.concatenate( - (np.random.uniform( - size=(timesteps - padding_steps, batch_size, depth)), - np.zeros(shape=(padding_steps, batch_size, depth))), - axis=0), tf.float32) - fwd_paddings = tf.concat( - (tf.zeros(shape=(timesteps - padding_steps, batch_size, depth)), - tf.ones(shape=(padding_steps, batch_size, depth))), - axis=0) - bak_inputs = tf.reverse(fwd_inputs, [0]) - bak_paddings = tf.reverse(fwd_paddings, [0]) - - # Run the forward rnn with reversed inputs - reversed_outputs, _ = fwd_rnn.FProp(fwd_rnn.theta, bak_inputs, - bak_paddings) - reversed_outputs = tf.reverse(reversed_outputs, [0]) - - # Run the backward rnn with forward inputs. Note we reuse the fwd_rnn - # theta so the results should match - bak_outputs, _ = bak_rnn.FProp(fwd_rnn.theta, fwd_inputs, fwd_paddings) - - self.evaluate(tf.global_variables_initializer()) - actual_reversed_outputs, actual_bak_outputs = self.evaluate( - [reversed_outputs, bak_outputs]) - self.assertAllClose(actual_reversed_outputs, actual_bak_outputs) - - def testRNNWithConvLSTMCell(self): - with self.session(use_gpu=False): - params = rnn_cell.ConvLSTMCell.Params() - params.name = 'conv_lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.inputs_shape = [None, 4, 2, 3] - params.cell_shape = [None, 4, 2, 2] - params.filter_shape = [3, 2] - - rnn_params = rnn_layers.RNN.Params() - rnn_params.name = 'rnn' - rnn_params.vn.global_vn = True - rnn_params.vn.per_step_vn = False - rnn_params.vn.seed = 2938482 - rnn_params.vn.scale = 0.1 - rnn_params.cell = params - rnn_params.sequence_length = 10 - rnn = rnn_layers.RNN(rnn_params) - - np.random.seed(12345) - inputs_sequence = [] - paddings_sequence = [] - for _ in range(rnn_params.sequence_length): - inputs_sequence.append( - tf.constant(np.random.uniform(size=(3, 4, 2, 3)), tf.float32)) - paddings_sequence.append(tf.zeros([3, 1])) - - paddings_sequence[-1] = tf.constant([[1.0], [1.0], [1.0]]) - paddings_sequence[-2] = tf.constant([[1.0], [1.0], [1.0]]) - - inputs, paddings = tf.stack(inputs_sequence), tf.stack(paddings_sequence) - outputs, final = rnn.FPropDefaultTheta(inputs, paddings) - outputs *= tf.reshape(1.0 - paddings, - [rnn_params.sequence_length, -1, 1, 1, 1]) - - sum_outputs = tf.reduce_sum(tf.reduce_sum(outputs, axis=0), [1, 2, 3]) - sum_final_m = tf.reduce_sum(final.m, [1, 2, 3]) - sum_final_c = tf.reduce_sum(final.c, [1, 2, 3]) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - actual = self.evaluate( - py_utils.NestedMap(sum=sum_outputs, m=sum_final_m, c=sum_final_c)) - - # In case this test ever breaks, you can uncomment the line below, copy - # out the golden values, and then comment out the line below again. - # - # print('sum_outputs', np.array_repr(actual.sum)) - # print('final_m', np.array_repr(actual.m)) - # print('final_c', np.array_repr(actual.c)) - - sum_expected = [1.59282923, 0.85964835, -1.09797788] - m_expected = [0.32634005, 0.38098311, 0.23331133] - c_expected = [2.72942424, 2.72087693, 2.97179723] - self.assertAllClose(sum_expected, actual.sum) - self.assertAllClose(m_expected, actual.m) - self.assertAllClose(c_expected, actual.c) - - def _testFRNNWithConvLSTMCell(self): - with self.session(use_gpu=True): - params = rnn_cell.ConvLSTMCell.Params() - params.name = 'conv_lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.inputs_shape = [None, 4, 2, 3] - params.cell_shape = [None, 4, 2, 2] - params.filter_shape = [3, 2] - - rnn_params = rnn_layers.RNN.Params() - rnn_params.name = 'rnn' - rnn_params.vn.global_vn = True - rnn_params.vn.per_step_vn = False - rnn_params.vn.seed = 2938482 - rnn_params.vn.scale = 0.1 - rnn_params.cell = params - rnn_params.sequence_length = 10 - - with tf.variable_scope('rnn'): - rnn = rnn_layers.RNN(rnn_params) - - np.random.seed(12345) - inputs_sequence = [] - paddings_sequence = [] - for _ in range(rnn_params.sequence_length): - inputs_sequence.append( - tf.constant(np.random.uniform(size=(3, 4, 2, 3)), tf.float32)) - paddings_sequence.append(tf.zeros([3, 1])) - - paddings_sequence[-1] = tf.constant([[1.0], [1.0], [1.0]]) - paddings_sequence[-2] = tf.constant([[1.0], [1.0], [1.0]]) - - inputs, paddings = tf.stack(inputs_sequence), tf.stack(paddings_sequence) - outputs, final = rnn.FPropDefaultTheta(inputs, paddings) - outputs *= tf.reshape(1.0 - paddings, - [rnn_params.sequence_length, -1, 1, 1, 1]) - rnn_outputs = outputs - - frnn_params = rnn_layers.FRNN.Params() - frnn_params.name = 'frnn' - frnn_params.cell = params - frnn_params.vn = rnn_params.vn - - with tf.variable_scope('frnn'): - frnn = frnn_params.Instantiate() - - frnn_outputs, frnn_final = frnn.FPropDefaultTheta( - tf.stack(inputs_sequence), tf.stack(paddings_sequence)) - paddings = tf.stack(paddings_sequence) - frnn_outputs *= tf.reshape(1.0 - paddings, - tf.concat([tf.shape(paddings), [1, 1]], 0)) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - - (rnn_outputs_v, rnn_final_v, frnn_outputs_v, - frnn_final_v) = self.evaluate( - [rnn_outputs, final, frnn_outputs, frnn_final]) - - self.assertAllClose(rnn_outputs_v, frnn_outputs_v) - self.assertAllClose(rnn_final_v.m, frnn_final_v.m) - self.assertAllClose(rnn_final_v.c, frnn_final_v.c) - - def testFRNNWithConvLSTMCell4(self): - self._testFRNNWithConvLSTMCell() - - def testRNNWithConvLSTMCellGradientChecker(self): - with self.session(use_gpu=True) as sess: - params = rnn_cell.ConvLSTMCell.Params() - params.name = 'conv_lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.inputs_shape = [None, 4, 2, 3] - params.cell_shape = [None, 4, 2, 2] - params.filter_shape = [3, 2] - - rnn_params = rnn_layers.RNN.Params() - rnn_params.name = 'rnn' - rnn_params.vn.global_vn = False - rnn_params.vn.per_step_vn = False - rnn_params.vn.seed = 2938482 - rnn_params.vn.scale = 0.1 - rnn_params.cell = params - rnn_params.sequence_length = 10 - rnn = rnn_layers.RNN(rnn_params) - - np.random.seed(12345) - inputs_sequence = [] - paddings_sequence = [] - for _ in range(rnn_params.sequence_length): - inputs_sequence.append( - tf.constant(np.random.uniform(size=(3, 4, 2, 3)), tf.float32)) - paddings_sequence.append(tf.zeros([3, 1])) - - paddings_sequence[-1] = tf.constant([[1.0], [1.0], [1.0]]) - paddings_sequence[-2] = tf.constant([[1.0], [1.0], [1.0]]) - - inputs, paddings = tf.stack(inputs_sequence), tf.stack(paddings_sequence) - outputs, final = rnn.FPropDefaultTheta(inputs, paddings) - outputs *= tf.reshape(1.0 - paddings, - [rnn_params.sequence_length, -1, 1, 1, 1]) - loss = tf.reduce_sum(tf.reduce_sum( - outputs, axis=0)) + tf.reduce_sum(final.m + final.c) - all_vars = tf.trainable_variables() - assert len(all_vars) == 2 - - grads = tf.gradients(loss, all_vars) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - - symbolic_grads = [gd.eval() for gd in grads] - numerical_grads = [] - for v in all_vars: - numerical_grads.append(test_utils.ComputeNumericGradient(sess, loss, v)) - for x, y in zip(symbolic_grads, numerical_grads): - self.assertAllClose(x, y, rtol=0.1, atol=0.1) - - def _testFRNNWithConvLSTMCellGradientChecker(self): - with self.session(use_gpu=True) as sess: - params = rnn_cell.ConvLSTMCell.Params() - params.name = 'conv_lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.inputs_shape = [None, 4, 2, 3] - params.cell_shape = [None, 4, 2, 2] - params.filter_shape = [3, 2] - - frnn_params = rnn_layers.FRNN.Params() - frnn_params.name = 'rnn' - frnn_params.vn.global_vn = False - frnn_params.vn.per_step_vn = False - frnn_params.vn.seed = 2938482 - frnn_params.vn.scale = 0.1 - frnn_params.cell = params - frnn = rnn_layers.FRNN(frnn_params) - - np.random.seed(12345) - inputs_sequence = tf.constant( - np.random.uniform(size=(10, 3, 4, 2, 3)), tf.float32) - paddings = inplace_ops.inplace_update( - tf.zeros([10, 3, 1]), 1, [[1.0], [0.0], [1.0]]) - - outputs, final = frnn.FPropDefaultTheta(inputs_sequence, paddings) - outputs *= tf.reshape(paddings, [10, 3, 1, 1, 1]) - loss = tf.reduce_sum(outputs) + tf.reduce_sum(final.m + final.c) - all_vars = tf.trainable_variables() - assert len(all_vars) == 2 - - grads = tf.gradients(loss, all_vars) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - - symbolic_grads = [gd.eval() for gd in grads] - numerical_grads = [] - for v in all_vars: - numerical_grads.append(test_utils.ComputeNumericGradient(sess, loss, v)) - for x, y in zip(symbolic_grads, numerical_grads): - self.assertAllClose(x, y, rtol=0.1, atol=0.1) - - def testFRNNWithConvLSTMCellGradientChecker(self): - self._testFRNNWithConvLSTMCellGradientChecker() - - def testFRNNWithLSTMCellSimpleDeterministicGradientChecker(self): - with self.session(use_gpu=True) as sess: - params = rnn_cell.LSTMCellSimpleDeterministic.Params() - params.name = 'conv_lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.zo_prob = 0.25 - params.num_input_nodes = 4 - params.num_output_nodes = 6 - params.dtype = tf.float64 - - frnn_params = rnn_layers.FRNN.Params() - frnn_params.dtype = tf.float64 - frnn_params.name = 'rnn' - frnn_params.cell = params - old_enable_asserts = FLAGS.enable_asserts - FLAGS.enable_asserts = False - frnn = rnn_layers.FRNN(frnn_params) - FLAGS.enable_asserts = old_enable_asserts - - np.random.seed(12345) - inputs_sequence = tf.constant( - np.random.uniform(size=(10, 3, 4)), tf.float64) - paddings = inplace_ops.inplace_update( - tf.zeros([10, 3, 1], tf.float64), 1, [[1.0], [0.0], [1.0]]) - - outputs, _ = frnn.FPropDefaultTheta(inputs_sequence, paddings) - outputs *= (1.0 - tf.reshape(paddings, [10, 3, 1])) - loss = tf.reduce_sum(outputs) - - all_vars = tf.trainable_variables() - assert len(all_vars) == 2 - - grads = tf.gradients(loss, all_vars) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - - symbolic_grads = [gd.eval() for gd in grads] - numerical_grads = [] - for v in all_vars: - numerical_grads.append(test_utils.ComputeNumericGradient(sess, loss, v)) - for x, y in zip(symbolic_grads, numerical_grads): - self.assertAllClose(x, y, rtol=0.00001, atol=0.00001) - - def _testFRNNHelper(self, config=None): - dtype = tf.float32 - batch = 3 - dims = 16 - slen = 10 - with self.session(use_gpu=True, config=config): - params = rnn_cell.LSTMCellSimple.Params() - params.name = 'lstm' - params.output_nonlinearity = True - params.dtype = dtype - params.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - params.vn.global_vn = True - params.vn.per_step_vn = False - params.vn.seed = 2938482 - params.vn.scale = 0.1 - params.num_input_nodes = dims - params.num_output_nodes = dims - - frnn_params = rnn_layers.FRNN.Params() - frnn_params.name = 'frnn' - frnn_params.dtype = dtype - frnn_params.cell = params - with tf.variable_scope('frnn'): - frnn = rnn_layers.FRNN(frnn_params) - AddTimestepAccumulator(frnn.cell) - - rnn_params = rnn_layers.RNN.Params() - rnn_params.name = 'rnn' - rnn_params.dtype = dtype - rnn_params.sequence_length = slen - rnn_params.cell = params - with tf.variable_scope('rnn'): - rnn = rnn_layers.RNN(rnn_params) - - np.random.seed(12345) - inputs = tf.constant( - np.random.uniform(size=(slen, batch, dims)), tf.float32) - paddings = np.zeros([slen, batch, 1]) - paddings[-3:-1, :] = 1.0 - paddings = tf.constant(paddings, tf.float32) - - frnn_outputs, frnn_final = frnn.FPropDefaultTheta(inputs, paddings) - rnn_outputs, rnn_final = rnn.FPropDefaultTheta( - tf.unstack(inputs), tf.unstack(paddings)) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - frnn_out, frnn_final, rnn_out, rnn_final = self.evaluate( - [frnn_outputs, frnn_final, rnn_outputs, rnn_final]) - self.assertAllClose(frnn_out, rnn_out) - self.assertAllClose(frnn_final.m, rnn_final.m) - self.assertAllClose(frnn_final.c, rnn_final.c) - - def testFRNNNoInline(self): - self._testFRNNHelper(py_utils.SessionConfig(inline=False)) - - def testFRNNInline(self): - self._testFRNNHelper(py_utils.SessionConfig(inline=True)) - - def _testFRNNGradHelper(self, config): - dtype = tf.float64 # More stable using float64. - batch = 3 - dims = 16 - slen = 10 - with self.session(use_gpu=True, config=config) as sess: - params = rnn_cell.LSTMCellSimple.Params() - params.name = 'lstm' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(0.02, 429891685) - params.vn.global_vn = False - params.vn.per_step_vn = False - params.num_input_nodes = dims - params.num_output_nodes = dims - params.dtype = dtype - - frnn_params = rnn_layers.FRNN.Params() - frnn_params.name = 'frnn' - frnn_params.dtype = dtype - frnn_params.cell = params - frnn = rnn_layers.FRNN(frnn_params) - AddTimestepAccumulator(frnn.cell) - w, b = frnn.theta.cell.wm, frnn.theta.cell.b - - np.random.seed(12345) - inputs = tf.constant( - np.random.uniform(-0.02, 0.02, size=(slen, batch, dims)), dtype) - paddings = np.zeros([slen, batch, 1]) - paddings[-3:-1, :] = 1.0 - paddings = tf.constant(paddings, dtype) - frnn_outputs, frnn_final = frnn.FPropDefaultTheta(inputs, paddings) - loss = tf.reduce_sum(frnn_outputs) + tf.reduce_sum( - frnn_final.m) + tf.reduce_sum(frnn_final.c) - dw, db, dinputs = tf.gradients(loss, [w, b, inputs]) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - grad_step = 7 - sym_grads = self.evaluate([db, dw, dinputs]) - sym_grads = [test_utils.PickEveryN(_, grad_step) for _ in sym_grads] - num_grads = [ - test_utils.PickEveryN( - test_utils.ComputeNumericGradient( - sess, loss, v, delta=1e-4, step=grad_step), grad_step) - for v in [b, w, inputs] - ] - for (sym, num) in zip(sym_grads, num_grads): - self.assertFalse(np.any(np.isnan(sym))) - self.assertFalse(np.any(np.isnan(num))) - print('max = ', np.max(np.abs(sym))) - self.assertAllClose(sym, num) - - def testFRNNGradNoInline(self): - self._testFRNNGradHelper(py_utils.SessionConfig(inline=False)) - - def testFRNNGradInline(self): - self._testFRNNGradHelper(py_utils.SessionConfig(inline=True)) - - def testStackedFRNNDropout(self): - v1_out, _ = self._testStackedFRNNHelper( - rnn_layers.StackedFRNNLayerByLayer, - tf.float32, - trailing_pad_len=0, - keep_prob=0.5) - if tf.test.is_gpu_available(): - rtol = 1e-5 - else: - rtol = 1e-6 - self.assertAllClose([175.974121], [np.sum(v1_out * v1_out)], rtol=rtol) - - def testStackedFRNNInputOutputDims(self): - v1_out, _ = self._testStackedFRNNHelper( - rnn_layers.StackedFRNNLayerByLayer, - tf.float32, - trailing_pad_len=0, - keep_prob=0.5, - input_dim=5, - output_dim=7) - if tf.test.is_gpu_available(): - rtol = 1e-5 - else: - rtol = 1e-6 - self.assertAllClose([32.743263], [np.sum(v1_out * v1_out)], rtol=rtol) - - def testStackedFRNNLayerByLayerGrad(self): - self._testStackedFRNNGradHelper(rnn_layers.StackedFRNNLayerByLayer) - - def testStackedBiFRNNDropout(self): - v1_out = self._testStackedFRNNHelper( - rnn_layers.StackedBiFRNNLayerByLayer, - tf.float32, - trailing_pad_len=0, - keep_prob=0.5, - bi_directional=True) - if tf.test.is_gpu_available(): - rtol = 1e-5 - else: - rtol = 1e-6 - self.assertAllClose([305.774384], [np.sum(v1_out * v1_out)], rtol=rtol) - - def testStackedBiFRNNInputOutputDims(self): - v1_out = self._testStackedFRNNHelper( - rnn_layers.StackedBiFRNNLayerByLayer, - tf.float32, - trailing_pad_len=0, - keep_prob=0.5, - bi_directional=True, - input_dim=5, - output_dim=8) - if tf.test.is_gpu_available(): - rtol = 1e-5 - else: - rtol = 1e-6 - self.assertAllClose([8.116007], [np.sum(v1_out * v1_out)], rtol=rtol) - - def testStackedBiFRNNLayerByLayerGrad(self): - self._testStackedFRNNGradHelper( - rnn_layers.StackedBiFRNNLayerByLayer, bi_directional=True) - - def _testBidirectionalFRNNHelper(self, - trailing_pad_len=0, - cluster_params=None): - batch = 3 - dims = 16 - slen = 10 + trailing_pad_len - with self.session( - use_gpu=True, - config=tf.config_pb2.ConfigProto(allow_soft_placement=True)): - params = rnn_cell.LSTMCellSimple.Params() - params.name = 'lstm_forward' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(0.02, 429891685) - params.vn.global_vn = True - params.vn.per_step_vn = False - params.vn.seed = 2938482 - params.vn.scale = 0.1 - params.num_input_nodes = dims - params.num_output_nodes = dims - lstm_forward = params.Copy() - params.name = 'lstm_backward' - params.params_init = py_utils.WeightInit.Uniform(0.02, 83820209838) - lstm_backward = params.Copy() - - frnn_params = rnn_layers.BidirectionalFRNN.Params() - frnn_params.name = 'bifrnn' - frnn_params.fwd = lstm_forward.Copy() - frnn_params.bak = lstm_backward.Copy() - with tf.variable_scope('frnn'): - frnn = rnn_layers.BidirectionalFRNN(frnn_params) - - rnn_params = rnn_layers.BidirectionalRNN.Params() - rnn_params.name = 'rnn' - rnn_params.fwd = lstm_forward.Copy() - rnn_params.bak = lstm_backward.Copy() - rnn_params.sequence_length = slen - - with cluster_factory.Cluster(cluster_params if cluster_params else - cluster_factory.Cluster.Params()): - with tf.variable_scope('rnn'): - rnn = rnn_layers.BidirectionalRNN(rnn_params) - - np.random.seed(12345) - inputs = tf.constant( - np.random.uniform(size=(slen, batch, dims)), tf.float32) - paddings = np.zeros([slen, batch, 1]) - paddings[-trailing_pad_len:, :] = 1.0 - paddings[-trailing_pad_len - 3:-trailing_pad_len - 1, :] = 1.0 - paddings = tf.constant(paddings, tf.float32) - - frnn_outputs = frnn.FPropDefaultTheta(inputs, paddings) - rnn_outputs = rnn.FPropDefaultTheta( - tf.unstack(inputs), tf.unstack(paddings)) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - rnn_outputs_val, frnn_outputs_val = [ - x[:-trailing_pad_len] - for x in self.evaluate([rnn_outputs, frnn_outputs]) - ] - self.assertAllClose(rnn_outputs_val, frnn_outputs_val) - - def testBidirectionalFRNN(self): - self._testBidirectionalFRNNHelper() - - def testBidirectionalFRNNTrailingPadding(self): - self._testBidirectionalFRNNHelper(trailing_pad_len=2) - - def testBidirectionalFRNNSplit(self): - cluster_params = cluster_factory.Cluster.Params() - cluster_params.worker.Set( - gpus_per_replica=2, devices_per_split=2, name='/job:localhost') - self._testBidirectionalFRNNHelper(cluster_params=cluster_params) - - def _testBidirectionalFRNNGradientHelper(self): - dtype = tf.float64 # More stable using float64. - batch = 3 - dims = 16 - slen = 10 - with self.session(use_gpu=True) as sess: - params = rnn_cell.LSTMCellSimple.Params() - params.name = 'lstm_forward' - params.output_nonlinearity = True - params.params_init = py_utils.WeightInit.Uniform(0.02, 429891685) - params.vn.global_vn = False - params.vn.per_step_vn = False - params.vn.scale = 0.1 - params.dtype = dtype - params.num_input_nodes = dims - params.num_output_nodes = dims - lstm_forward = params.Copy() - params.name = 'lstm_backward' - params.params_init = py_utils.WeightInit.Uniform(0.02, 83820209838) - params.dtype = dtype - lstm_backward = params.Copy() - - frnn_params = rnn_layers.BidirectionalFRNN.Params() - frnn_params.name = 'bifrnn' - frnn_params.dtype = dtype - frnn_params.fwd = lstm_forward.Copy() - frnn_params.bak = lstm_backward.Copy() - frnn = rnn_layers.BidirectionalFRNN(frnn_params) - w0, b0 = (frnn.theta.fwd_rnn.cell.wm, frnn.theta.fwd_rnn.cell.b) - w1, b1 = (frnn.theta.bak_rnn.cell.wm, frnn.theta.bak_rnn.cell.b) - - np.random.seed(12345) - inputs = tf.constant(np.random.uniform(size=(slen, batch, dims)), dtype) - paddings = np.zeros([slen, batch, 1]) - paddings[-3:-1, :] = 1.0 - paddings = tf.constant(paddings, dtype) - frnn_outputs = frnn.FPropDefaultTheta(inputs, paddings) - loss = tf.reduce_sum(frnn_outputs) - - dw0, db0, dw1, db1, dinputs = tf.gradients(loss, [w0, b0, w1, b1, inputs]) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - grad_step = 13 - sym_grads = self.evaluate([dw0, db0, dw1, db1, dinputs]) - sym_grads = [test_utils.PickEveryN(_, grad_step) for _ in sym_grads] - num_grads = [ - test_utils.PickEveryN( - test_utils.ComputeNumericGradient( - sess, loss, v, delta=1e-4, step=grad_step), grad_step) - for v in [w0, b0, w1, b1, inputs] - ] - for (sym, num) in zip(sym_grads, num_grads): - self.assertFalse(np.any(np.isnan(sym))) - self.assertFalse(np.any(np.isnan(num))) - print('max = ', np.max(np.abs(sym))) - self.assertAllClose(sym, num) - - def testBidirectionalFRNNGrad(self): - self._testBidirectionalFRNNGradientHelper() - - def _MultiSourceFRNNWithAttentionInputs(self, - single_source=False, - single_source_length=True, - dtype=tf.float32): - np.random.seed(12345) - if single_source: - src_names = ['en'] - slens = [10] - sdepths = [4] - elif single_source_length: - src_names = ['en1', 'en2', 'de'] - slens = [11, 10, 9] - sdepths = [4, 4, 4] - else: - src_names = ['en1', 'en2', 'de'] - slens = [11, 10, 9] - sdepths = [4, 4, 3] - sbatch = 3 - tlen = 7 - tbatch = 6 - dims = 4 - - src_encs = py_utils.NestedMap() - src_paddings = py_utils.NestedMap() - for sdepth, slen, sname in zip(sdepths, slens, src_names): - src_encs[sname] = tf.constant( - np.random.uniform(size=[slen, sbatch, sdepth]), dtype) - src_paddings[sname] = tf.constant(np.zeros([slen, sbatch]), dtype) - inputs = tf.constant(np.random.uniform(size=(tlen, tbatch, dims)), dtype) - paddings = tf.constant(np.zeros([tlen, tbatch, 1]), dtype) - return (src_encs, src_paddings, inputs, paddings) - - def _MultiSourceFRNNWithAttentionParams(self, - single_source=False, - single_source_length=True, - dtype=tf.float32): - dims = 4 - alt_depth = 3 - if single_source: - src_names = ['en'] - else: - src_names = ['en1', 'en2', 'de'] - - p = rnn_cell.LSTMCellSimple.Params() - p.name = 'lstm' - p.dtype = dtype - p.output_nonlinearity = True - p.params_init = py_utils.WeightInit.Uniform(0.02, 429891685) - p.vn.global_vn = False - p.vn.per_step_vn = False - p.num_input_nodes = dims * 2 - p.num_output_nodes = dims - lstm_params = p - - p = attention.AdditiveAttention.Params() - p.name = 'atten' - p.dtype = dtype - p.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - p.source_dim = dims - p.query_dim = dims - p.hidden_dim = dims - p.vn.global_vn = False - p.vn.per_step_vn = False - attention_tpl = p - - p = attention.MergerLayer.Params() - p.name = 'merger' - p.dtype = dtype - p.merger_op = ('mean' if single_source else 'atten') - p.source_dim = dims - p.query_dim = dims - p.hidden_dim = dims - merger_tpl = p - - p = rnn_layers.MultiSourceFRNNWithAttention.Params() - p.name = 'msrc_frnn_with_atten' - p.dtype = dtype - p.cell = lstm_params - p.attention_tpl = attention_tpl - p.atten_merger = merger_tpl - p.source_names = src_names - - if not single_source_length: - de_atten = attention_tpl.Copy() - de_atten.source_dim = alt_depth - p.source_name_to_attention_params = {'de': de_atten} - merger_tpl.pre_proj_input_dims = [dims, dims, alt_depth] - merger_tpl.pre_proj_output_dims = [dims, dims, dims] - merger_tpl.proj_tpl.batch_norm = False - merger_tpl.proj_tpl.weight_norm = True - - return p - - def testMultiSourceFRNNWithAttention(self): - with self.session(use_gpu=True): - p = self._MultiSourceFRNNWithAttentionParams() - msrc_frnn = p.Instantiate() - - (src_encs, src_paddings, inputs, - paddings) = self._MultiSourceFRNNWithAttentionInputs() - a, m = msrc_frnn.FPropDefaultTheta(src_encs, src_paddings, inputs, - paddings) - msrc_frnn_out = tf.concat([a, m], 2) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - ys = self.evaluate([msrc_frnn_out])[0] - self.assertEqual(ys.shape, (7, 6, 8)) - print(np.sum(ys, axis=(1, 2)), np.sum(ys, axis=(0, 1)), - np.sum(ys, axis=(0, 2))) - # pyformat: disable - # pylint: disable=bad-whitespace - self.assertAllClose( - np.sum(ys, axis=(1, 2)), [ - 11.87568951, 11.8436203 , 11.80368233, 11.80167198, - 11.82034779, 11.80246162, 11.80818748 - ]) - self.assertAllClose( - np.sum(ys, axis=(0, 1)), [ - 21.41802788, 20.86244965, 21.48164749, 19.95701981, - -0.54706949, 0.07046284, -0.50449395, 0.0176318 - ]) - self.assertAllClose( - np.sum(ys, axis=(0, 2)), [ - 13.29822254, 14.01552773, 14.04851151, 13.28098106, - 14.05391502, 14.0585041 - ]) - # pyformat: enable - # pylint: enable=bad-whitespace - - def testMultiSourceFRNNWithAttentionMultiDepth(self): - with self.session(use_gpu=True): - p = self._MultiSourceFRNNWithAttentionParams(single_source_length=False) - msrc_frnn = p.Instantiate() - - (src_encs, src_paddings, inputs, paddings - ) = self._MultiSourceFRNNWithAttentionInputs(single_source_length=False) - a, m = msrc_frnn.FPropDefaultTheta(src_encs, src_paddings, inputs, - paddings) - msrc_frnn_out = tf.concat([a, m], 2) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - ys = self.evaluate([msrc_frnn_out])[0] - self.assertEqual(ys.shape, (7, 6, 8)) - print(np.sum(ys, axis=(1, 2)), np.sum(ys, axis=(0, 1)), - np.sum(ys, axis=(0, 2))) - # pyformat: disable - # pylint: disable=bad-whitespace - self.assertAllClose( - np.sum(ys, axis=(1, 2)), [ - 5.976197, 5.932313, 5.917447, 5.907898, 5.907385, 5.90272 , - 5.890248 - ]) - self.assertAllClose( - np.sum(ys, axis=(0, 1)), [ - 2.635296e+01, 3.177989e+00, 1.024462e+01, 2.403777e+00, - -4.908564e-01, 1.006475e-01, -3.303704e-01, -2.455414e-02 - ]) - self.assertAllClose( - np.sum(ys, axis=(0, 2)), [ - 6.610287, 6.657996, 7.452699, 6.626875, 6.60216 , 7.484191 - ]) - # pyformat: enable - # pylint: enable=bad-whitespace - - def testMultiSourceFRNNWithAttentionSingleSource(self, dtype=tf.float32): - with self.session( - use_gpu=True, config=py_utils.SessionConfig(inline=False)): - p = self._MultiSourceFRNNWithAttentionParams( - single_source=True, dtype=dtype) - frnn = p.Instantiate() - - (src_encs, src_paddings, inputs, - paddings) = self._MultiSourceFRNNWithAttentionInputs( - single_source=True, dtype=dtype) - - a, m = frnn.FPropDefaultTheta(src_encs, src_paddings, inputs, paddings) - frnn_out = tf.concat([a, m], 2) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - ys, = self.evaluate([frnn_out]) - self.assertEqual(ys.shape, (7, 6, 8)) - print(np.sum(ys, axis=(1, 2)), np.sum(ys, axis=(0, 1)), - np.sum(ys, axis=(0, 2))) - - # These values are identical with FRNNWithAttention. - expected_sum12 = [ - 13.07380962, 13.03321552, 12.99956226, 13.00612164, 13.01202011, - 12.99347878, 12.98680687 - ] - expected_sum01 = [ - 2.41238327e+01, 2.11899853e+01, 2.45926647e+01, 2.22827835e+01, - -5.62886238e-01, 2.42760777e-02, -5.79716980e-01, 3.40666063e-02 - ] - expected_sum02 = [ - 12.74695969, 16.13114548, 16.66101837, 12.74922562, 16.16581345, - 16.65085411 - ] - - self.assertAllClose(np.sum(ys, axis=(1, 2)), expected_sum12) - self.assertAllClose(np.sum(ys, axis=(0, 1)), expected_sum01) - self.assertAllClose(np.sum(ys, axis=(0, 2)), expected_sum02) - - def testMultiSourceFRNNWithAttentionGradSingleSource(self, dtype=tf.float64): - with self.session( - use_gpu=True, config=py_utils.SessionConfig(inline=False)) as sess: - - p = self._MultiSourceFRNNWithAttentionParams( - single_source=True, dtype=dtype) - frnn = p.Instantiate() - - (src_encs, src_paddings, inputs, - paddings) = self._MultiSourceFRNNWithAttentionInputs( - single_source=True, dtype=dtype) - - # Fetch all the parameters. - w0, b0 = (frnn.theta.cell.wm, frnn.theta.cell.b) - att0h, att0q, att0s = (frnn.theta.attentions[0].hidden_var, - frnn.theta.attentions[0].query_var, - frnn.theta.attentions[0].source_var) - - out, _ = frnn.FPropDefaultTheta(src_encs, src_paddings, inputs, paddings) - loss = tf.reduce_sum(out) - - parameters = [w0, b0, inputs, att0h, att0q, att0s] - grads = tf.gradients(loss, parameters) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - sym_grads = self.evaluate(grads) - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v, delta=1e-5) - for v in parameters - ] - for i, (sym, num) in enumerate(zip(sym_grads, num_grads)): - print([ - i, sym.shape, num.shape, - np.max(np.abs(sym)), - np.max(np.abs(sym - num)), - np.max(np.abs(sym - num) / np.abs(sym)) - ]) - - def Compare(name, sym, num, rtol=1e-5): - print(['name = ', name]) - self.assertFalse(np.any(np.isnan(sym))) - self.assertFalse(np.any(np.isnan(num))) - self.assertAllClose(sym, num, rtol=rtol, atol=1e-8) - - for i, (sym, num) in enumerate(zip(sym_grads, num_grads)): - Compare(parameters[i].name, sym, num) - - def testMultiSourceFRNNWithAttentionGrad(self, dtype=tf.float64): - with self.session( - use_gpu=True, config=py_utils.SessionConfig(inline=False)) as sess: - - p = self._MultiSourceFRNNWithAttentionParams(dtype=dtype) - frnn = p.Instantiate() - - # Fetch all the parameters. - w0, b0 = (frnn.theta.cell.wm, frnn.theta.cell.b) - mh, mq, ms = (frnn.theta.atten_merger.atten.hidden_var, - frnn.theta.atten_merger.atten.query_var, - frnn.theta.atten_merger.atten.source_var) - att0h, att0q, att0s = (frnn.theta.attentions[0].hidden_var, - frnn.theta.attentions[0].query_var, - frnn.theta.attentions[0].source_var) - att1h, att1q, att1s = (frnn.theta.attentions[1].hidden_var, - frnn.theta.attentions[1].query_var, - frnn.theta.attentions[1].source_var) - att2h, att2q, att2s = (frnn.theta.attentions[2].hidden_var, - frnn.theta.attentions[2].query_var, - frnn.theta.attentions[2].source_var) - - (src_encs, src_paddings, inputs, - paddings) = self._MultiSourceFRNNWithAttentionInputs(dtype=dtype) - - out, _ = frnn.FPropDefaultTheta(src_encs, src_paddings, inputs, paddings) - loss = tf.reduce_sum(out) - - parameters = [ - w0, b0, inputs, mh, mq, ms, att0h, att0q, att0s, att1h, att1q, att1s, - att2h, att2q, att2s - ] - grads = tf.gradients(loss, parameters) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - sym_grads = self.evaluate(grads) - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v, delta=1e-5) - for v in parameters - ] - for i, (sym, num) in enumerate(zip(sym_grads, num_grads)): - print([ - i, sym.shape, num.shape, - np.max(np.abs(sym)), - np.max(np.abs(sym - num)), - np.max(np.abs(sym - num) / np.abs(sym)) - ]) - - def Compare(name, sym, num, rtol=1e-5): - print(['name = ', name]) - self.assertFalse(np.any(np.isnan(sym))) - self.assertFalse(np.any(np.isnan(num))) - self.assertAllClose(sym, num, rtol=rtol, atol=1e-8) - - for i, (sym, num) in enumerate(zip(sym_grads, num_grads)): - Compare(parameters[i].name, sym, num) - - def testMultiSourceFRNNWithAttentionGradMultiDepth(self, dtype=tf.float64): - with self.session( - use_gpu=True, config=py_utils.SessionConfig(inline=False)) as sess: - - p = self._MultiSourceFRNNWithAttentionParams( - single_source_length=False, dtype=dtype) - frnn = p.Instantiate() - - # Fetch all the parameters. - w0, b0 = (frnn.theta.cell.wm, frnn.theta.cell.b) - mh, mq, ms, mw0, mw1, mw2 = (frnn.theta.atten_merger.atten.hidden_var, - frnn.theta.atten_merger.atten.query_var, - frnn.theta.atten_merger.atten.source_var, - frnn.theta.atten_merger.pre_proj[0].w, - frnn.theta.atten_merger.pre_proj[1].w, - frnn.theta.atten_merger.pre_proj[2].w) - att0h, att0q, att0s = (frnn.theta.attentions[0].hidden_var, - frnn.theta.attentions[0].query_var, - frnn.theta.attentions[0].source_var) - att1h, att1q, att1s = (frnn.theta.attentions[1].hidden_var, - frnn.theta.attentions[1].query_var, - frnn.theta.attentions[1].source_var) - att2h, att2q, att2s = (frnn.theta.attentions[2].hidden_var, - frnn.theta.attentions[2].query_var, - frnn.theta.attentions[2].source_var) - - (src_encs, src_paddings, inputs, - paddings) = self._MultiSourceFRNNWithAttentionInputs( - single_source_length=False, dtype=dtype) - - out, _ = frnn.FPropDefaultTheta(src_encs, src_paddings, inputs, paddings) - loss = tf.reduce_sum(out) - - parameters = [ - w0, b0, inputs, mh, mq, ms, att0h, att0q, att0s, att1h, att1q, att1s, - att2h, att2q, att2s, mw0, mw1, mw2 - ] - grads = tf.gradients(loss, parameters) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - sym_grads = self.evaluate(grads) - num_grads = [ - test_utils.ComputeNumericGradient(sess, loss, v, delta=1e-5) - for v in parameters - ] - for i, (sym, num) in enumerate(zip(sym_grads, num_grads)): - print([ - i, sym.shape, num.shape, - np.max(np.abs(sym)), - np.max(np.abs(sym - num)), - np.max(np.abs(sym - num) / np.abs(sym)) - ]) - - def Compare(name, sym, num, rtol=1e-5): - print(['name = ', name]) - self.assertFalse(np.any(np.isnan(sym))) - self.assertFalse(np.any(np.isnan(num))) - self.assertAllClose(sym, num, rtol=rtol, atol=1e-8) - - for i, (sym, num) in enumerate(zip(sym_grads, num_grads)): - Compare(parameters[i].name, sym, num) - - def _CreateFRNNWithAttentionParams(self, - dtype, - dims, - slen, - sbatch, - tlen, - tbatch, - input_prev_atten_ctx=True, - output_prev_atten_ctx=False): - # Create RNN Layer. - p = rnn_cell.LSTMCellSimple.Params() - p.name = 'lstm' - p.dtype = dtype - p.output_nonlinearity = True - p.params_init = py_utils.WeightInit.Uniform(0.02, 429891685) - p.vn.global_vn = False - p.vn.per_step_vn = False - p.num_input_nodes = dims * 2 if input_prev_atten_ctx else dims - p.num_output_nodes = dims - lstm_params = p - - # Create Attention Layer. - p = attention.AdditiveAttention.Params() - p.name = 'atten' - p.dtype = dtype - p.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - p.source_dim = dims - p.query_dim = dims - p.hidden_dim = dims - p.vn.global_vn = False - p.vn.per_step_vn = False - atten = p - - p = rnn_layers.FRNNWithAttention.Params() - p.name = 'frnn_with_atten' - p.dtype = dtype - p.cell = lstm_params - p.attention = atten - p.input_prev_atten_ctx = input_prev_atten_ctx - p.output_prev_atten_ctx = output_prev_atten_ctx - return p - - @parameterized.parameters((False, False), (False, True), (True, False), - (True, True)) - def testFRNNWithAttentionSeparateSourceContextIdenticalToSourceEnc( - self, input_prev_atten_ctx, output_prev_atten_ctx): - dtype = tf.float32 - dims = 4 - slen = 10 - sbatch = 3 - tlen = 7 - tbatch = 6 - - with self.session(use_gpu=True, config=py_utils.SessionConfig(inline=True)): - np.random.seed(12345) - p = self._CreateFRNNWithAttentionParams( - dtype=dtype, - dims=dims, - slen=slen, - sbatch=sbatch, - tlen=tlen, - tbatch=tbatch, - input_prev_atten_ctx=input_prev_atten_ctx, - output_prev_atten_ctx=output_prev_atten_ctx) - - frnn = p.Instantiate() - - src_encs = tf.constant( - np.random.uniform(size=[slen, sbatch, dims]), dtype) - src_paddings = tf.constant(np.zeros([slen, sbatch]), dtype) - - inputs = tf.constant(np.random.uniform(size=(tlen, tbatch, dims)), dtype) - paddings = tf.constant(np.zeros([tlen, tbatch, 1]), dtype) - - # Run without specifying source context vectors. - atten_ctx, rnn_out, atten_prob, _ = frnn.FPropDefaultTheta( - src_encs, src_paddings, inputs, paddings) - frnn_out = tf.concat([atten_ctx, rnn_out, atten_prob], 2) - - # Run after providing separate source context vectors set to the src_encs - # should provide the same answer. - (atten_ctx_src_ctx, rnn_out_src_ctx, atten_prob_src_ctx, - _) = frnn.FPropDefaultTheta( - src_encs, src_paddings, inputs, paddings, src_contexts=src_encs) - frnn_out_src_ctx = tf.concat( - [atten_ctx_src_ctx, rnn_out_src_ctx, atten_prob_src_ctx], 2) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - frnn_out_v, frnn_out_src_ctx_v = self.evaluate( - [frnn_out, frnn_out_src_ctx]) - - # Expected last dimensions for atten_ctx_src_ctx, rnn_out_src_ctx, - # atten_prob_src_ctx are respectively, (dims, dims, slen). - self.assertEqual(frnn_out_v.shape, (tlen, tbatch, 2 * dims + slen)) - self.assertEqual(frnn_out_src_ctx_v.shape, frnn_out_v.shape) - - self.assertAllClose(frnn_out_v, frnn_out_src_ctx_v) - - @parameterized.parameters((False, False), (False, True), (True, False), - (True, True)) - def testFRNNWithAttentionSeparateSourceContextDifferentFromSourceEnc( - self, input_prev_atten_ctx, output_prev_atten_ctx): - dtype = tf.float32 - dims = 4 - slen = 10 - sbatch = 3 - tlen = 7 - tbatch = 6 - - with self.session(use_gpu=True, config=py_utils.SessionConfig(inline=True)): - np.random.seed(12345) - p = self._CreateFRNNWithAttentionParams( - dtype=dtype, - dims=dims, - slen=slen, - sbatch=sbatch, - tlen=tlen, - tbatch=tbatch, - input_prev_atten_ctx=input_prev_atten_ctx, - output_prev_atten_ctx=output_prev_atten_ctx) - - frnn = p.Instantiate() - - src_encs = tf.constant( - np.random.uniform(size=[slen, sbatch, dims]), dtype) - src_paddings = tf.constant(np.zeros([slen, sbatch]), dtype) - - # We create src_contexts with even dimensions (0, 2) set to all zero, the - # rest are set randomly. - src_contexts = np.random.uniform(size=[slen, sbatch, dims]) - src_contexts[:, :, 0:dims:2] = 0.0 - src_contexts = tf.constant(src_contexts, dtype=dtype) - - inputs = tf.constant(np.random.uniform(size=(tlen, tbatch, dims)), dtype) - paddings = tf.constant(np.zeros([tlen, tbatch, 1]), dtype) - - # Run after providing separate source context vectors set to the src_encs - # should provide the same answer. - atten_ctx, _, _, _ = frnn.FPropDefaultTheta( - src_encs, src_paddings, inputs, paddings, src_contexts=src_contexts) - - # Initialize all the variables, and then run one step. - self.evaluate(tf.global_variables_initializer()) - atten_ctx_v = self.evaluate(atten_ctx) - - self.assertEqual(atten_ctx_v.shape, (tlen, tbatch, dims)) - # Verify that the output also has zeros in the locations that the - # source context has zeros. - self.assertAllClose( - np.zeros(shape=(tlen, tbatch, dims // 2)), - atten_ctx_v[:, :, 0:dims:2]) - - def _testFRNNWithAttentionUseZeroAttenState(self, zero_atten_state_fn): - dtype = tf.float32 - dims = 5 - slen = 4 - tlen = 3 - sbatch = 2 - tbatch = 6 - - with self.session(use_gpu=True): - p = self._CreateFRNNWithAttentionParams( - dtype=dtype, - dims=dims, - slen=slen, - sbatch=sbatch, - tlen=tlen, - tbatch=tbatch) - p.use_zero_atten_state = True - p.atten_context_dim = dims - frnn = p.Instantiate() - - # Override the ZeroAttentionState to have the desired output type - frnn.atten.ZeroAttentionState = types.MethodType(zero_atten_state_fn, - frnn.atten) - - src_encs = tf.constant( - np.random.uniform(size=[slen, sbatch, dims]), dtype) - src_paddings = tf.constant(np.zeros([slen, sbatch]), dtype) - - inputs = tf.constant(np.random.uniform(size=(tlen, tbatch, dims)), dtype) - paddings = tf.constant(np.zeros([tlen, tbatch, 1]), dtype) - - atten_ctx, rnn_out, atten_prob, _ = frnn.FPropDefaultTheta( - src_encs, src_paddings, inputs, paddings) - - self.evaluate(tf.global_variables_initializer()) - atten_ctx, rnn_out, atten_prob = self.evaluate( - [atten_ctx, rnn_out, atten_prob]) - - # Check shapes - self.assertEqual(atten_ctx.shape, (tlen, tbatch, dims)) - self.assertEqual(rnn_out.shape, (tlen, tbatch, dims)) - self.assertEqual(atten_prob.shape, (tlen, tbatch, slen)) - - def testFRNNWithAttentionUseZeroAttenStateTensor(self): - - def _TensorZeroAttenState(self, source_seq_length, decoder_batch_size): - del source_seq_length - p = self.params - zs = tf.zeros([decoder_batch_size, 1], dtype=py_utils.FPropDtype(p)) - return zs - - self._testFRNNWithAttentionUseZeroAttenState(_TensorZeroAttenState) - - def testFRNNWithAttentionUseZeroAttenStateNestedMap(self): - - def _NestedMapZeroAttenState(self, source_seq_length, decoder_batch_size): - del source_seq_length - p = self.params - zs = tf.zeros([decoder_batch_size, 1], dtype=py_utils.FPropDtype(p)) - return py_utils.NestedMap(z=zs) - - self._testFRNNWithAttentionUseZeroAttenState(_NestedMapZeroAttenState) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/saver.py b/lingvo/core/saver.py deleted file mode 100644 index 9e16042f..00000000 --- a/lingvo/core/saver.py +++ /dev/null @@ -1,333 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -"""A saver that we use to save and restore variables. - -The implementation mimics tf.train.Saver. Meanwhile, it allows us -to carry out extra sanity checks on the checkpoint. -""" - -import re -import time -from lingvo import compat as tf -# pylint: enable=g-direct-tensorflow-import -from lingvo.core import py_utils -import numpy as np -from google.protobuf import text_format -# pylint: disable=g-direct-tensorflow-import -from tensorflow.python.lib.io import file_io -from tensorflow.python.ops import io_ops -from tensorflow.python.training.checkpoint_state_pb2 import CheckpointState - - -class SanityCheck: - - def Check(self, *args): - """Returns true iff the sanity check passes.""" - raise NotImplementedError() - - -class InRange(SanityCheck): - """Sanity check a value is within [low, high].""" - - def __init__(self, low, high): - self._low = low - self._high = high - - def __str__(self): - return "InRange({}, {})".format(self._low, self._high) - - def Check(self, value): - return self._low <= value <= self._high - - -class IsFinite(SanityCheck): - - def __str__(self): - return "IsFinite" - - def Check(self, value): - return np.all(np.isfinite(value)) - - -def _VarKey(var): - return var.name[:-2] # strip :0 - - -class Saver: - """Simpler version of tf.train.Saver with extra sanity checks.""" - - def __init__(self, - logdir, - variables, - sanity_checks=None, - keep_latest_n=None, - keep_every_n_hours=None): - self._logdir = logdir - self._state_file = "{}/checkpoint".format(self._logdir) - self._vars = variables - assert not sanity_checks or all( - isinstance(x[1], SanityCheck) for x in sanity_checks) - self._sanity_checks = sanity_checks - self._keep_latest_n = keep_latest_n - self._keep_every_n_hours = keep_every_n_hours - self._re_pattern = re.compile(r"^.*/ckpt-(\d+).*$") - self._logdir_ph = tf.placeholder(tf.string, shape=[]) - self._restore_prefix_ph = tf.placeholder(tf.string, shape=[]) - self._BuildSave() - self._BuildRestore() - tf.logging.info("Saver: %s %s %s", self._logdir, self._keep_latest_n, - self._keep_every_n_hours) - - def _BuildSave(self): - """Builds save ops.""" - self._save_global_step = py_utils.GetGlobalStep() - self._save_prefix = tf.strings.join([ - self._logdir_ph, "/ckpt-", - tf.as_string(self._save_global_step, width=8, fill="0") - ]) - self._save_op = io_ops.save_v2( - prefix=self._save_prefix, - tensor_names=[_VarKey(v) for v in self._vars], - tensors=[v.read_value() for v in self._vars], - shape_and_slices=[""] * len(self._vars)) - - def _BuildRestore(self): - """Builds restore ops.""" - assign_ops = [] - for var in self._vars: - val, = io_ops.restore_v2( - prefix=self._restore_prefix_ph, - tensor_names=[_VarKey(var)], - shape_and_slices=[""], - dtypes=[var.dtype]) - assign_ops.append(var.assign(val)) - self._restore_op = tf.group(*assign_ops) - - def _GetState(self): - """Returns the latest checkpoint id.""" - state = CheckpointState() - if file_io.file_exists(self._state_file): - content = file_io.read_file_to_string(self._state_file) - text_format.Parse(content, state) - return state - - def _SetState(self, state): - file_io.atomic_write_string_to_file(self._state_file, - text_format.MessageToString(state)) - - def _GetCheckpointId(self, filename_prefix): - match = self._re_pattern.match(filename_prefix) - assert match, "Unexpected {} does not match re({})".format( - filename_prefix, self._re_pattern.pattern) - return int(match.group(1)) - - def _GarbageCollect(self): - """Garbage collect obsolete checkpoint files.""" - state = self._GetState() - - valid_ids = set() - if state.model_checkpoint_path: - valid_ids.add(self._GetCheckpointId(state.model_checkpoint_path)) - for path in state.all_model_checkpoint_paths: - valid_ids.add(self._GetCheckpointId(path)) - - existing_files = tf.io.gfile.glob(r"{}/ckpt-*".format(self._logdir)) - # Filter to make sure we catch only the ckpt files. - existing_files = [f for f in existing_files if self._re_pattern.match(f)] - for filename in existing_files: - if self._GetCheckpointId(filename) not in valid_ids: - # TODO(zhifengc): May need to find a bulk delete method. - tf.logging.info("Garbage collecting %s", filename) - tf.io.gfile.remove(filename) - - def _DoSanityCheck(self, prefix): - """Sanity-check the content of the checkpoint.""" - if not self._sanity_checks: - return - reader = tf.train.NewCheckpointReader(prefix) - content = {} - for variables, rule in self._sanity_checks: - args = [] - for v in variables: - key = _VarKey(v) - if key in content: - args.append(content[key]) - else: - value = reader.get_tensor(key) - content[key] = value - args.append(value) - if not rule.Check(*args): - # TODO(zhifengc): Maybe should return an explicit signal - # so that the caller (the controller loop) can Restore() - # the latest checkpoint before raise the error. - msg = "Checkpoint sanity check failed: {} {} {}\n".format( - prefix, ",".join([_VarKey(v) for v in variables]), rule) - # Also saves the error messge into a file. - file_io.write_string_to_file("{}.failed".format(prefix), msg) - raise tf.errors.AbortedError(None, None, msg) - - def Save(self, sess): - """Generate a new checkpoint. - - Args: - sess: A session with tf.Graph under which this object is constructred. - - Returns: - If the checkpoint is successfully generated, returns its global step - and file prefix. Otherwise, raises an Aborted error. - """ - # Garbage collect. Do so before generates the checkpoint - # in case we repeatedly fails the sanity checks. - self._GarbageCollect() - - _, global_step, prefix = sess.run( - fetches=[self._save_op, self._save_global_step, self._save_prefix], - feed_dict={self._logdir_ph: self._logdir}) - prefix = tf.compat.as_text(prefix) - - # Many users expect this as the tf.train.Saver does this by default. - meta_graph_filename = prefix + ".meta" - tf.train.export_meta_graph(filename=meta_graph_filename) - - # We can do extra sanity checks. - self._DoSanityCheck(prefix) - - # Commit new state. - self._UpdateState(prefix) - - tf.logging.info("Saved %d %s", global_step, prefix) - return global_step, prefix - - def _UpdateState(self, prefix): - """Updates the checkpoint state with the new checkpoint prefix.""" - # The checkpoint looks OK. Commit it to the state. - state = self._GetState() - - # If a previous checkpoint exists and it was generated long - # after the checkpoint before it, we preserve it in - # all_model_checkpoint_{paths,timestamps}. - if state.model_checkpoint_path and ( - not state.all_model_checkpoint_timestamps or - not self._keep_every_n_hours or - (state.last_preserved_timestamp - - state.all_model_checkpoint_timestamps[-1] > - 3600. * self._keep_every_n_hours)): - state.all_model_checkpoint_paths.append(state.model_checkpoint_path) - state.all_model_checkpoint_timestamps.append( - state.last_preserved_timestamp) - - # Record the checkpoint we just generated. - state.model_checkpoint_path = prefix - state.last_preserved_timestamp = time.time() - - # Applies the count-based GC policy. - if self._keep_latest_n: - if self._keep_latest_n == 1: - # Use [:] to prevent "Assignment not allowed to repeated field" error. - state.all_model_checkpoint_paths[:] = [] - state.all_model_checkpoint_timestamps[:] = [] - else: - n = self._keep_latest_n - 1 - state.all_model_checkpoint_paths[:] = ( - state.all_model_checkpoint_paths[-n:]) - state.all_model_checkpoint_timestamps[:] = ( - state.all_model_checkpoint_timestamps[-n:]) - self._SetState(state) - - def Restore(self, sess, checkpoint_id=None): - """Restore variables from a checkpoint. - - Args: - sess: A session with tf.Graph under which this object is constructred. - checkpoint_id: If None, restore from the latest checkpoint. Otherwise, - restore from the specific checkpoint. - - Returns: - If no checkpoint has been generated, returns (0, ""). Otherwise, try to - restore from the specified or the latest checkpoint. If the checkpoint is - successfully restored, returns the checkpoint's global step and file - prefix. Otherwise, raises an error. - """ - - if checkpoint_id: - prefix = "{}/ckpt-{:08d}".format(self._logdir, checkpoint_id) - else: - prefix = self._GetState().model_checkpoint_path - if not prefix: - return 0, "" - - sess.run( - fetches=[self._restore_op], feed_dict={self._restore_prefix_ph: prefix}) - global_step = self._GetCheckpointId(prefix) - tf.logging.info("Restored %d %s", global_step, prefix) - return global_step, prefix - - -def WriteNpArrays(file_prefix, nmap): - """Writes a NestedMap of numpy arrays into a TF checkpoint. - - Args: - file_prefix: A TF checkpoint filename prefix. - nmap: A NestedMap of numpy arrays. - """ - g = tf.Graph() - with g.as_default(): - - def Wrap(val): - dtype = tf.as_dtype(val.dtype) - assert dtype != tf.string # tf.string is not supported by py_func. - return tf.py_func(lambda: val, [], dtype) - - names, values = [], [] - for k, v in nmap.FlattenItems(): - names.append(k) - assert isinstance(v, np.ndarray) - values.append(Wrap(v)) - - save = io_ops.save_v2( - prefix=file_prefix, - tensor_names=names, - tensors=values, - shape_and_slices=[""] * len(names)) - - with tf.Session(graph=g) as sess: - sess.run(save) - - -def ReadNpArrays(file_prefix, nmap): - """Reads from a tf checkpoint to fill in values of a NesteMap. - - Args: - file_prefix: A TF checkpoint filename prefix. - nmap: A NestedMap of numpy dtypes. - - Returns: - A NestedMap with numpy arrays compatible w/ nmap. - """ - g = tf.Graph() - with g.as_default(): - reads = [] - for name, dtype in nmap.FlattenItems(): - reads.append( - io_ops.restore_v2( - prefix=file_prefix, - tensor_names=[name], - shape_and_slices=[""], - dtypes=[dtype])[0]) - - with tf.Session(graph=g) as sess: - vals = sess.run(reads) - - return nmap.Pack(vals) diff --git a/lingvo/core/saver_test.py b/lingvo/core/saver_test.py deleted file mode 100644 index a61d6822..00000000 --- a/lingvo/core/saver_test.py +++ /dev/null @@ -1,123 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -"""Tests for saver.""" - -import os -import tempfile -from lingvo import compat as tf -from lingvo.core import cluster_factory -from lingvo.core import py_utils -from lingvo.core import saver -from lingvo.core import test_utils -from lingvo.tasks.image.params import mnist -import numpy as np - - -class SaverTest(test_utils.TestCase): - - def testBasic(self): - logdir = tempfile.mkdtemp() - # Create a dummy file that looks like a checkpoint that shouldn't - # be touched. - with tf.io.gfile.GFile(logdir + '/ckpt-foo', 'w') as f: - f.write('contents') - - g = tf.Graph() - with g.as_default(): - p = mnist.LeNet5().Task() - p.input = mnist.LeNet5().Train() - with cluster_factory.ForTestingWorker(mode='sync', job='controller'): - _ = p.Instantiate() - gsv = py_utils.GetOrCreateGlobalStepVar() - inc = gsv.assign_add(1) - variables = tf.all_variables() - sanity_checks = [([gsv], saver.InRange(0, 10))] - for var in variables: - sanity_checks.append(([var], saver.IsFinite())) - sav = saver.Saver( - logdir, - variables, - sanity_checks, - keep_latest_n=5, - keep_every_n_hours=1e-9) - - with self.session(graph=g) as sess: - # Creates a few checkpoints. - sess.run(tf.global_variables_initializer()) - for _ in range(10): - sess.run(inc) - _ = sav.Save(sess) - - # Restore to the latest. - sess.run(tf.global_variables_initializer()) - _ = sav.Restore(sess) - - # Restore to a specific checkpoint. - sess.run(tf.global_variables_initializer()) - _ = sav.Restore(sess, 6) - - # Increments global_step out of range, Save() fails. - for _ in range(5): - sess.run(inc) - with self.assertRaises(tf.errors.AbortedError): - _ = sav.Save(sess) - - filenames = tf.io.gfile.glob('{}/*'.format(logdir)) - filenames = [x[len(logdir) + 1:] for x in filenames] - print('\n'.join(filenames)) - self.assertIn('checkpoint', filenames) - - meta_files = [] - for f in filenames: - if f.endswith('.meta'): - meta_files.append(f) - # A .meta for each checkpoint. - self.assertEqual(len(meta_files), 6) - - # 1 for checkpoint. 3 files per checkpoint. 5 good checkpoints, 1 bad. - # 1 extra file contains the error message, and 1 dummy file - self.assertEqual(len(filenames), 1 + (5 + 1) * 3 + 1 + 1) - - def testSingleCheckpoint(self): - logdir = tempfile.mkdtemp() - g = tf.Graph() - with g.as_default(): - _ = py_utils.GetOrCreateGlobalStepVar() - sav = saver.Saver(logdir, tf.all_variables(), [], keep_latest_n=1) - with self.session(graph=g) as sess: - sess.run(tf.global_variables_initializer()) - _ = sav.Save(sess) - - def testWriteReadNpArrays(self): - prefix = os.path.join(tempfile.mkdtemp(), 'nptest') - nmap = py_utils.NestedMap() - nmap.train = np.random.normal(size=(3, 3)) - nmap.test = np.random.normal(size=(1, 3)) - nmap.foo = py_utils.NestedMap() - nmap.foo.bar = np.arange(10).astype(np.int32).reshape([2, 5]) - saver.WriteNpArrays(prefix, nmap) - files = tf.io.gfile.glob(prefix + '*') - self.assertEqual(len(files), 2) - self.assertEqual(files[0], prefix + '.data-00000-of-00001') - self.assertEqual(files[1], prefix + '.index') - read_nmap = saver.ReadNpArrays(prefix, nmap.Transform(lambda x: x.dtype)) - self.assertTrue(nmap.IsCompatible(read_nmap)) - self.assertAllEqual(nmap.train, read_nmap.train) - self.assertAllEqual(nmap.test, read_nmap.test) - self.assertAllEqual(nmap.foo.bar, read_nmap.foo.bar) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/schedule.py b/lingvo/core/schedule.py deleted file mode 100644 index 6a14cd71..00000000 --- a/lingvo/core/schedule.py +++ /dev/null @@ -1,824 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Learning rate schedule.""" - -import math -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import early_stop -from lingvo.core import ops -from lingvo.core import py_utils - - -class BaseSchedule(base_layer.BaseLayer): - """Base class for learning rate decay algorithms.""" - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'LRSched' - return p - - def __init__(self, params): - super().__init__(params) - self.SetVariableFree() - - def Value(self, current_step=None): - """Returns the current learning rate schedule value. - - Args: - current_step: The current global step value. - - Returns: - Returns the current learning rate schedule value given the - current training global step. Typically, the base learning rate - multiplied by the returned schedule value is used as the - effective learning rate. - """ - if current_step is None: - current_step = self.global_step - return self.FPropDefaultTheta(current_step) - - -class Constant(BaseSchedule): - """A schedule that always returns a constant value.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('value', 1., 'The constant value.') - return p - - def FProp(self, theta, current_step): - del theta, current_step - return tf.constant(self.params.value, self.params.dtype) - - -class ConstantOne(Constant): - """A lr schedule remains constant 1.""" - pass - - -class PiecewiseConstantSchedule(BaseSchedule): - """Piecewise constants rate decay.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('boundaries', None, 'Boundaries at which learning rate drops.') - p.Define('values', None, 'Values in each interval.') - return p - - def FProp(self, theta, current_step): - p = self.params - return py_utils.PiecewiseConstant(current_step, p.boundaries, p.values, - p.dtype) - - -class ContinuousSchedule(BaseSchedule): - """Continuous learning rate decay.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('initial_value', 1.0, 'Initial decay value.') - p.Define('start_step', 400000, - 'Starts to decay the learning rate from this step.') - p.Define('half_life_steps', 100000, - 'Halve the learning rate every this many steps after start_step.') - p.Define('min', 0.01, 'Minimum relative learning rate.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - q = ExponentialSchedule.Params().Set( - start=(p.start_step, 1.0), - limit=(p.start_step + - p.half_life_steps * math.log(p.min) / math.log(0.5), p.min)) - self.CreateChild('exp', q) - - def FProp(self, theta, current_step): - """Returns the current learning rate decay.""" - return self.params.initial_value * self.exp.Value(current_step) - - -class PolynomialSchedule(BaseSchedule): - """Polynomial learning rates. - - If x < x0, returns y0. If x >= x1, returns y1. Otherwise, - interpolate with a polynomial between (x0, y0) and (x1, y1). - - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('power', 1, 'Polynomial power.') - p.Define('start', (0, 1.), '(x0, y0)') - p.Define('limit', (1, 1.), '(x1, y1)') - p.Define('origin', 'start', 'Origin of the polynomial. Can be "start" or ' - '"limit".') - return p - - def __init__(self, params): - super().__init__(params) - - def Polynomial(x): - """Polynomial function of x.""" - p = self.params - x0, y0 = p.start - x1, y1 = p.limit - - assert x0 < x1, '%s must be < %s' % (x0, x1) - - x0 = tf.cast(x0, dtype=x.dtype) - x1 = tf.cast(x1, dtype=x.dtype) - y0 = tf.cast(y0, dtype=x.dtype) - y1 = tf.cast(y1, dtype=x.dtype) - - ratio = (x - x0) / (x1 - x0) - if p.origin == 'start': - f_x = ratio**p.power - elif p.origin == 'limit': - f_x = 1 - (1 - ratio)**p.power - else: - raise ValueError('Invalid parameter origin: %s' % p.origin) - y = y0 + f_x * (y1 - y0) - return tf.where(x < x0, y0, tf.where(x >= x1, y1, y)) - - self._polynomial = Polynomial - - def FProp(self, theta, current_step): - return py_utils.CallDefun(self._polynomial, - tf.cast(current_step, dtype=self.params.dtype)) - - -class LinearSchedule(PolynomialSchedule): - """Linear learning rate schedule. - - If x < x0, returns y0. If x >= x1, returns y1. Otherwise, - interpolate linearly between (x0, y0) and (x1, y1). - - """ - - @classmethod - def Params(cls): - p = super().Params().Set(power=1) - return p - - -class ExponentialSchedule(BaseSchedule): - """Linear learning rate schedule. - - If x < x0, returns y0. If x >= x1, returns y1. Otherwise, - interpolate exponentially between (x0, y0) and (x1, y1). - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('start', (0, 1.), '(x0, y0)') - p.Define('limit', (1, 0.5), '(x1, y1)') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - x0, y0 = p.start - x1, y1 = p.limit - assert x0 < x1, '%s must be < %s' % (x0, x1) - assert y0 > 0, '%s must be > 0' % y0 - assert y1 > 0, '%s must be > 0' % y1 - - self.CreateChild( - 'linear', - LinearSchedule.Params().Set( - start=(x0, math.log(y0)), limit=(x1, math.log(y1)))) - - def Exp(x): - return tf.exp(self.linear.Value(x)) - - self._exp = Exp - - def FProp(self, theta, current_step): - return py_utils.CallDefun(self._exp, - tf.cast(current_step, dtype=self.params.dtype)) - - -class StepwiseExponentialSchedule(BaseSchedule): - """Exponential decay every N steps.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('decay', 0.99, 'Decay factor.') - p.Define('num_steps_per_decay', 1000, 'Number of steps between decays.') - return p - - def FProp(self, theta, current_step): - p = self.params - num_decays = tf.floor( - tf.div(tf.cast(current_step, tf.float32), float(p.num_steps_per_decay))) - return tf.pow(p.decay, num_decays) - - -class CombinedMinimumSchedule(BaseSchedule): - """Combine a few learning rate decay schedules and takes the min.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('schedules', [LinearSchedule.Params()], - 'A list of learning rate schedule params.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self.CreateChildren('schedules', p.schedules) - - def Combined(x): - ys = [s.Value(x) for s in self.schedules] - return tf.reduce_min(tf.stack(ys), axis=0) - - self._combined = Combined - - def FProp(self, theta, current_step): - return py_utils.CallDefun(self._combined, - tf.convert_to_tensor(current_step)) - - -class TransformerSchedule(BaseSchedule): - """Inverse-decay learning rate until warmup_steps, then decay.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'warmup_steps', 4000, 'Increase the learning rate linearly for ' - 'the first warmup_steps training steps.') - p.Define( - 'model_dim', 512, 'Model dimension that applies to embedding ' - 'layers and all Transformer layers.') - p.Define('worker_replicas', 1, 'Number of worker replicas.') - p.Define('decay_end', None, 'Ends the learning rate decay at ' - 'decay_end-th step.') - return p - - def FProp(self, theta, current_step): - """Returns the current learning rate decay.""" - p = self.params - current_step = tf.cast(current_step, tf.float32) - warmup_steps = tf.cast(p.warmup_steps * p.worker_replicas, tf.float32) - if p.decay_end is not None: - current_step = tf.where(current_step < p.decay_end, current_step, - tf.cast(p.decay_end, tf.float32)) - return p.model_dim**-0.5 * tf.minimum( - (current_step + 1) * warmup_steps**-1.5, (current_step + 1)**-0.5) - - -class TransformerMLPerfSchedule(BaseSchedule): - """learning rate with linear warmup, then rsqrt decay.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'warmup_steps', 4000, 'Increase the learning rate linearly for ' - 'the first warmup_steps training steps.') - p.Define( - 'model_dim', 512, 'Model dimension that applies to embedding ' - 'layers and all Transformer layers.') - return p - - def FProp(self, theta, current_step): - """Returns the current learning rate decay.""" - p = self.params - current_step = tf.cast(current_step, tf.float32) - warmup_steps = tf.cast(p.warmup_steps, tf.float32) - linear_warmup = tf.minimum(1.0, current_step / warmup_steps) - rsqrt_decay = tf.math.rsqrt(tf.maximum(current_step, warmup_steps)) - return p.model_dim**-0.5 * linear_warmup * rsqrt_decay - - -class TransformerScheduleNoWarmUp(BaseSchedule): - """Fixed learning rate until decay_start, then decay. - - This learning rate schedule is identical to TransformerSchedule - except in the warm-up phase, where this learning rate schedule uses a fixed - learning rate (peak-learning rate of TransformerSchedule) for the - original warm-up phase. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('decay_start', 4000, 'It is used to estimate peak-lr.') - p.Define('decay_end', None, 'Ends the learning rate decay at ' - 'decay_end-th step.') - p.Define( - 'model_dim', 512, 'Model dimension that applies to embedding ' - 'layers and all Transformer layers.') - p.Define('worker_replicas', 1, 'Number of worker replicas.') - return p - - def __init__(self, params): - super().__init__(params) - tf.logging.info('Peak lr: %f', (self.params.decay_start * - self.params.worker_replicas)**-0.5) - - def FProp(self, theta, current_step): - """Returns the current learning rate decay.""" - params = self.params - warmup_steps = tf.cast(params.decay_start * params.worker_replicas, - tf.float32) - current_step = tf.cast(current_step, tf.float32) - if params.decay_end is not None: - current_step = tf.where(current_step < params.decay_end, current_step, - tf.cast(params.decay_end, tf.float32)) - peak_learning_rate = (warmup_steps**-0.5) - return (params.model_dim**-0.5) * tf.minimum( - tf.minimum((current_step + 1), - (current_step + 1)**-0.5), peak_learning_rate) - - -class LinearRampupExponentialDecayScaledByNumSplitSchedule(BaseSchedule): - """A learning rate schedule that does the following. - - 1. The peak learning rate multiplier is scaled by num splits, - (often the same as #replicas during batch splitting synchronous - training). - 2. The multiplier ramps up linearly from 1 to the peak initially. - 3. The multiplier stays constant until the exponential decay starts. - 4. The multiplier is capped at max. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'warmup', 300, 'Increases the learning rate linearly ' - 'before warmup * num_splits steps.') - p.Define('warmup_init', 1.0, 'The initial value of the warm-up phase.') - p.Define('decay_start', 70000, 'Starts the learning rate decay at ' - 'decay_start-th step.') - p.Define('decay_end', 100000, 'Ends the learning rate decay at ' - 'decay_end-th step.') - p.Define('min', 0.01, 'After decay_end, the multiplier stays at min.') - p.Define( - 'max', 1e8, 'The schedule is never larger than this value. ' - 'By default, 1e8 effectively means there is no cap.') - p.Define( - 'num_splits', 0, 'Specifies the intended number of splits for the ' - 'LR. Overrides num_splits_per_client if non-zero.') - return p - - def __init__(self, params): - super().__init__(params) - - p = self.params - - # We always compute lr schedule from the trainer's perspective. - # Also note that this schedule makes sense to sync training only. - if p.num_splits: - splits = p.num_splits - else: - # Infer num_splits from cluster. - cluster_params = self.cluster.params.Copy() - cluster_params.task = 0 - assert cluster_params.mode == 'sync' - cluster_params.job = 'trainer_client' - my_cluster = cluster_params.Instantiate() - splits = my_cluster.num_splits_per_client - - warmup_end = p.warmup * splits - decay_start = max(warmup_end + 1.0, p.decay_start / splits) - peak = 1.0 * splits - tf.logging.info('Peak lr: %f', peak) - decay_end = max(decay_start + 1.0, p.decay_end / splits) - schedules = [ - LinearSchedule.Params().Set( - start=(warmup_end, peak), limit=(decay_start, peak)), - ExponentialSchedule.Params().Set( - start=(decay_start, peak), limit=(decay_end, p.min)), - LinearSchedule.Params().Set(start=(0, p.max), limit=(decay_end, p.max)), - ] - # Only include a warm up schedule if the warmup_end exceeds 0.0. Note that - # linear schedules must have x1 > x0 strictly. - if warmup_end > 0.0: - schedules = [ - LinearSchedule.Params().Set( - start=(0., p.warmup_init), limit=(warmup_end, peak)) - ] + schedules - self.CreateChild('combine', - CombinedMinimumSchedule.Params().Set(schedules=schedules)) - - def FProp(self, theta, current_step): - return self.combine.Value(current_step) - - -class LinearRampupExponentialDecay( - LinearRampupExponentialDecayScaledByNumSplitSchedule): - """A schedule that does the following... - - 1. Linearly ramps up from `p.warmup_init` to 1.0 initially; - 2. Stays at constant 1.0 until the decay starts; - 3. Exponential decays from 1.0 to `p.min`. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.num_splits = 1 - p.warmup_init = 0.0 - return p - - def __init__(self, params): - assert params.num_splits == 1 - super().__init__(params) - - -class LinearRampupSqrtDecayByBatchSizeAndReplicas(BaseSchedule): - """Linearly increase learning rate until warmup_examples, then sqrt decay. - - Same as the Transformer learning schedule, except that learning rate - multiplier and time scales are adjusted according to the aggregate batch size - (batch_size * num_replicas). - - The implied peak learning rate multiplier given by this schedule is: - sqrt(batch_size * num_replicas / warmup_examples). - - Can only be used with synchronized trainers. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'warmup_examples', 256 * 2**20, - 'Increase the learning rate linearly for the first warmup_examples ' - 'training examples, and then starts square-root decay.') - p.Define('batch_size', None, - 'Norminal (per-replica) batch size. Must be provided.') - p.Define( - 'num_replicas', None, 'Number of worker replicas. If None, ' - 'determined automatically (and error if this fails).') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.batch_size > 0 - if p.num_replicas: - self._num_replicas = p.num_replicas - else: - # Infer from cluster. - cluster_params = self.cluster.params.Copy() - cluster_params.task = 0 - assert cluster_params.mode == 'sync' - cluster_params.job = 'trainer_client' - my_cluster = cluster_params.Instantiate() - self._num_replicas = my_cluster.num_splits_per_client - assert self._num_replicas > 0 - - def FProp(self, theta, current_step): - """Returns the current learning rate decay.""" - p = self.params - current_step = tf.cast(current_step, tf.float32) - warmup_steps = tf.cast( - p.warmup_examples / (p.batch_size * self._num_replicas), tf.float32) - return tf.minimum((current_step + 1) * warmup_steps**-1.5, - (current_step + 1)**-0.5) - - -class LinearRampupPiecewiseConstantSchedule(BaseSchedule): - """A learning rate schedule that does the following. - - 1. The learning rate is scaled by #split * lrs[i] - (often #split is the same as #replicas during batch splitting synchronous - training). - 2. The multiplier ramps up linearly from 0 to the peak(lrs[0]) at - boundaries[0]. - 3. After peak, the multiplier stays lrs[i] when step falls into - [boundaries[i], boundaries[i+1]) - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('boundaries', [], 'Boundaries at which learning rate changes.') - p.Define('lrs', [], 'A list of learning rate multiplers.') - p.Define( - 'num_splits', 0, 'Specifies the intended number of num_splits for ' - 'LR. Overrides num_splits if non-zero.') - return p - - def __init__(self, params): - super().__init__(params) - - p = self.params - assert len(p.boundaries) >= 2 and len(p.boundaries) == len(p.lrs) - # We always compute lr schedule from the trainer's perspective. - # Also note that this schedule makes sense to sync training only. - if p.num_splits: - splits = p.num_splits - else: - # Infer num_splits from cluster. - cluster_params = self.cluster.params.Copy() - cluster_params.task = 0 - assert cluster_params.mode == 'sync' - cluster_params.job = 'trainer_client' - my_cluster = cluster_params.Instantiate() - splits = my_cluster.num_splits_per_client - - assert splits >= 1 - splits = float(splits) - boundaries = [step / splits for step in p.boundaries] - lrs = [step * splits for step in p.lrs] - - tf.logging.info('splits: {}\n boundaries: {}\n lrs: {} '.format( - splits, boundaries, lrs)) - - schedules = [ - LinearSchedule.Params().Set( - start=(0., 0.), limit=(boundaries[0], lrs[0])), - PiecewiseConstantSchedule.Params().Set( - boundaries=boundaries, values=[1e8] + lrs) - ] - self.CreateChild('combine', - CombinedMinimumSchedule.Params().Set(schedules=schedules)) - - def FProp(self, theta, current_step): - return self.combine.Value(current_step) - - -class LinearRampupCosineSchedule(BaseSchedule): - """A cosine decaying learning rate schedule with a linear rampup phase.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('warmup_init', 0, 'The initial lr value of the warm-up phase.') - p.Define('warmup_steps', 0, 'Number of warm up steps.') - p.Define('initial_value', 1.0, 'Initial decay value.') - p.Define('final_value', 0., 'Final decay value.') - p.Define('total_steps', 0, 'Number of steps to reach full decay.') - p.Define( - 'num_splits', 1, 'Specifies the intended number of splits for the ' - 'LR. Overrides num_splits_per_client if non-zero. Uses ' - 'num_splits_per_client as num_splits if zero or negative.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - # We always compute lr schedule from the trainer's perspective. - # Also note that this schedule makes sense to sync training only. - if p.num_splits > 0: - splits = p.num_splits - else: - # Infer num_splits from cluster. - cluster_params = self.cluster.params.Copy() - cluster_params.task = 0 - assert cluster_params.mode == 'sync' - cluster_params.job = 'trainer_client' - my_cluster = cluster_params.Instantiate() - splits = my_cluster.num_splits_per_client - - schedules = [ - LinearSchedule.Params().Set( - start=(0., p.warmup_init * splits), - limit=(p.warmup_steps // splits, p.initial_value * splits)), - CosineSchedule.Params().Set( - initial_value=p.initial_value * splits, - final_value=p.final_value * splits, - total_steps=p.total_steps // splits), - ] - self.CreateChild('combine', - CombinedMinimumSchedule.Params().Set(schedules=schedules)) - - def FProp(self, theta, current_step): - return self.combine.Value(current_step) - - -class DevBasedSchedule(BaseSchedule): - """Decay triggered by lack of improvement on the dev set. - - This reads a file containing a history of values of a selected metric versus - global step (file is recorded by the evaler loop in the trainer). Decay - depends on these variables: - - - best_step - step at which optimum metric value occurred in history file - - last_step - last step recorded in history file - - ref_step - most recent decay step or best_step - - cur_factor - current multiplier on initial learning rate - - The decay algorithm is:: - - ref_step = max(ref_step, best_step) - if last_step - ref_step > window: - cur_factor = max(cur_factor * decay, min_factor) - ref_step = last_step - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('metric_history', early_stop.MetricHistory.Params(), - 'Metric to monitor for stopping.') - p.Define('tolerance', 0.0, 'Minimum significant difference in metric.') - p.Define('window', 10000, - 'Steps since most recent decay or best_step before decaying.') - p.Define('decay', 0.5, - 'Factor by which learning rate multiplier is decayed.') - p.Define('min_factor', 0.01, 'Minimum learning rate multiplier.') - - return p - - def __init__(self, params): - super().__init__(params) - - p = self.params - - with tf.variable_scope(p.name): - wp = py_utils.WeightParams( - shape=[], - init=py_utils.WeightInit.Constant(1.0), - collections=['DevBasedSchedule_vars'], - dtype=tf.float32) - self._cur_factor = py_utils.CreateVariable( - 'cur_factor', wp, trainable=False) - wp = py_utils.WeightParams( - shape=[], - init=py_utils.WeightInit.Constant(0), - collections=['DevBasedSchedule_vars'], - dtype=tf.int64) - self._ref_step = py_utils.CreateVariable('ref_step', wp, trainable=False) - self._metric_history = early_stop.MetricHistory(p.metric_history) - self._best_step = ops.best_step(self._metric_history.hist_file, - p.tolerance) - - def FProp(self, theta, current_step): - p = self.params - with tf.name_scope(p.name): - - steps = self._best_step - best_step = steps[0] - last_step = steps[1] - - ref_step = tf.maximum(self._ref_step, best_step) - f = self._cur_factor - - # Decay if no improvement within window. - new_factor = tf.where(last_step - ref_step < p.window, f, - tf.maximum(p.min_factor, f * p.decay)) - # Update ref_step if we decayed. - new_step = tf.where(tf.equal(new_factor, f), ref_step, last_step) - update_step = tf.assign(self._ref_step, new_step) - with tf.control_dependencies([update_step]): - return tf.assign(self._cur_factor, new_factor) - - -class CosineSchedule(BaseSchedule): - """Cosine learning rate decay. - - First proposed in https://arxiv.org/pdf/1608.03983.pdf, which only uses - multiple cycles with angle from 0 to pi/2. Later people use only one cycle - with angle from 0 to pi (e.g., https://arxiv.org/pdf/1711.09224.pdf), which - is implemented here. - - where: - angle = pi * min(1, current_step / total_steps) - decay_gap = initial_value - final_value - value = final_value + decay_gap * (1 + cosine(angle)) / 2 - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('initial_value', 1.0, 'Initial decay value.') - p.Define('final_value', 0., 'Final decay value.') - p.Define('total_steps', 0, 'Number of steps to reach full decay.') - return p - - def FProp(self, theta, current_step): - p = self.params - assert p.total_steps > 0 - assert p.initial_value > p.final_value - with tf.name_scope(p.name): - decay_gap = p.initial_value - p.final_value - return p.final_value + 0.5 * decay_gap * (1 + tf.cos(math.pi * tf.minimum( - 1.0, - tf.cast(current_step, tf.float32) / p.total_steps))) - - -class PiecewiseSchedule(BaseSchedule): - """Piecewise schedule composed of sub-schedules.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('boundaries', None, 'Boundaries between subschedules.') - p.Define( - 'schedules', None, 'A list of sub-schedules. ' - 'The length must be len(boundaries) + 1. ' - 'schedules[i] starts at boundaries[i-1] (inclusive) and ends at ' - 'boundaries[i] (exclusive). ' - 'The *relative* step in each interval will be passed to the ' - 'sub-schedule for FProp.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - prev_boundary = 0 - for boundary in p.boundaries: - if boundary < prev_boundary: - raise ValueError('Invalid boundary %s < %s' % (boundary, prev_boundary)) - prev_boundary = boundary - if len(p.schedules) != len(p.boundaries) + 1: - raise ValueError('len(schedules) != len(boundaries) + 1: %s vs %s' % - (len(p.schedules), len(p.boundaries))) - self.CreateChildren('schedules', p.schedules) - - def FProp(self, theta, current_step): - p = self.params - current_step = tf.cast(current_step, tf.int64) - interval_starts = [0] + p.boundaries - values = [] - for interval_start, schedule, schedule_theta in zip( - interval_starts, self.schedules, theta.schedules): - relative_step = tf.maximum( - tf.cast(0, current_step.dtype), - current_step - tf.cast(interval_start, current_step.dtype)) - values.append(schedule.FProp(schedule_theta, relative_step)) - - return py_utils.PiecewiseConstant(current_step, p.boundaries, values, - values[0].dtype) - - -class SqrtDecay(BaseSchedule): - """Sqrt decay schedule.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('warmup_steps', 10000, 'Number of warm up steps.') - p.Define('multiplier', 1.0, 'Multiplier.') - return p - - def FProp(self, theta, current_step): - p = self.params - step_num = tf.cast(current_step, tf.float32) - learning_rate = tf.math.rsqrt(tf.maximum(step_num, p.warmup_steps)) - learning_rate *= p.multiplier - return learning_rate - - -class CycleSchedule(BaseSchedule): - """Piecewise schedule composed of sub-schedules in a cycle.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'schedules', None, 'A list of sub-schedules. Unlike PiecewiseSchedule, ' - 'the absolute step is passed to the sub-schedule.') - p.Define('steps', None, 'The number of steps to run each sub-schedule.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if len(p.schedules) != len(p.steps): - raise ValueError('len(schedules) != len(steps): %s vs %s' % - (len(p.schedules), len(p.steps))) - self.CreateChildren('schedules', p.schedules) - boundaries = [0] - for step in p.steps: - boundaries.append(boundaries[-1] + step) - self._period = boundaries[-1] - self._boundaries = boundaries[1:-1] - - def FProp(self, theta, current_step): - values = [] - for schedule, schedule_theta in zip(self.schedules, theta.schedules): - values.append(schedule.FProp(schedule_theta, current_step)) - relative_step = tf.math.mod(current_step, self._period) - return py_utils.PiecewiseConstant(relative_step, self._boundaries, values, - values[0].dtype) diff --git a/lingvo/core/schedule_test.py b/lingvo/core/schedule_test.py deleted file mode 100644 index 83acdabd..00000000 --- a/lingvo/core/schedule_test.py +++ /dev/null @@ -1,572 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for lr_schedule.""" - -import math -import os -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import early_stop -from lingvo.core import schedule -from lingvo.core import test_utils - - -class LearningRateScheduleTest(test_utils.TestCase): - - def testConstant(self): - with self.session(use_gpu=False): - p = schedule.Constant.Params().Set(value=5) - lrs = p.Instantiate() - for x in [0, 10, 100, 1000000]: - self.assertAllClose(lrs.Value(x).eval(), 5.0) - - def testConstantOne(self): - with self.session(use_gpu=False): - p = schedule.ConstantOne.Params() - lrs = p.Instantiate() - for x in [0, 10, 100, 1000000]: - self.assertAllClose(lrs.Value(x).eval(), 1.0) - - def testPiecewiseConstant(self): - cls = schedule.PiecewiseConstantSchedule - with self.session(use_gpu=False): - bs = [300000, 400000, 500000] - vs = [1.0, 0.1, 0.01, 0.001] - x_ins = [tf.constant(x) for x in [299999, 399999, 499999, 599999]] - outs = [] - for x in x_ins: - lrs = cls.Params().Set(boundaries=bs, values=vs).Instantiate() - outs.append(lrs.Value(x).eval()) - self.assertAllClose([1.0, 0.1, 0.01, 0.001], outs) - - def testContinuousSchedule(self): - p = schedule.ContinuousSchedule.Params() - p.start_step = 1000 - p.half_life_steps = 100 - p.min = 0.1 - decay = p.Instantiate() - with self.session(): - self.assertAllClose(decay.Value(0).eval(), 1.0) - self.assertAllClose(decay.Value(500).eval(), 1.0) - self.assertAllClose(decay.Value(1000).eval(), 1.0) - self.assertAllClose(decay.Value(1100).eval(), 0.5) - self.assertAllClose(decay.Value(1200).eval(), 0.25) - self.assertAllClose(decay.Value(1300).eval(), 0.125) - self.assertAllClose(decay.Value(1400).eval(), 0.1) - self.assertAllClose(decay.Value(2000).eval(), 0.1) - - # Tests that the decay consistently decreases by half per 100 - # steps. - for step in range(1000, 1200, 25): - self.assertGreater( - decay.Value(step).eval(), - decay.Value(step + 10).eval()) - self.assertAllClose( - decay.Value(step).eval(), - decay.Value(step + 100).eval() * 2.) - - def testContinuousSchedule_CanOverrideStart(self): - p = schedule.ContinuousSchedule.Params() - p.initial_value = 2.0 - p.start_step = 1000 - p.half_life_steps = 100 - decay = p.Instantiate() - with self.session(): - self.assertAllClose(decay.Value(0).eval(), 2.0) - self.assertAllClose(decay.Value(1000).eval(), 2.0) - self.assertAllClose(decay.Value(1100).eval(), 1.0) - self.assertAllClose(decay.Value(1200).eval(), 0.5) - self.assertAllClose(decay.Value(1300).eval(), 0.25) - - def testStepwiseExponentialSchedule(self): - p = schedule.StepwiseExponentialSchedule.Params() - p.decay = 0.5 - p.num_steps_per_decay = 1000 - decay = p.Instantiate() - with self.session(): - self.assertAllClose(decay.Value(0).eval(), 1.0) - self.assertAllClose(decay.Value(999).eval(), 1.0) - self.assertAllClose(decay.Value(1000).eval(), 0.5) - self.assertAllClose(decay.Value(1999).eval(), 0.5) - self.assertAllClose(decay.Value(2000).eval(), 0.25) - - def testTransformerSchedule(self): - p = schedule.TransformerSchedule.Params() - p.warmup_steps = 4000 - p.model_dim = 512 - lrs = p.Instantiate() - with self.session(): - print(lrs.Value(0).eval()) - print(lrs.Value(1000).eval()) - print(lrs.Value(2000).eval()) - print(lrs.Value(3000).eval()) - print(lrs.Value(4000).eval()) - print(lrs.Value(4500).eval()) - print(lrs.Value(5000).eval()) - self.assertAllClose(lrs.Value(0).eval(), 1.74693e-07) - self.assertAllClose(lrs.Value(1000).eval(), 0.000174867) - self.assertAllClose(lrs.Value(2000).eval(), 0.00034956) - self.assertAllClose(lrs.Value(3000).eval(), 0.000524253) - self.assertAllClose(lrs.Value(4000).eval(), 0.000698684) - self.assertAllClose(lrs.Value(4500).eval(), 0.000658735) - self.assertAllClose(lrs.Value(5000).eval(), 0.000624937) - # Tests that the schedule peaks at 4000 steps. - self.assertGreater(lrs.Value(4000).eval(), lrs.Value(3990).eval()) - self.assertGreater(lrs.Value(4000).eval(), lrs.Value(4010).eval()) - - # Tests that the schedule increases linearly before 4000 steps. - for step in range(300, 4000, 200): - self.assertAllClose( - lrs.Value(step).eval() * 2., - lrs.Value(step + 10).eval() + lrs.Value(step - 10).eval()) - - def testTransformerScheduleWithDecayEnd(self): - p = schedule.TransformerSchedule.Params() - p.warmup_steps = 4000 - p.model_dim = 512 - p.decay_end = 5000 - lrs = p.Instantiate() - with self.session(): - self.assertAllClose(lrs.Value(0).eval(), 1.74693e-07) - self.assertAllClose(lrs.Value(3000).eval(), 0.000524253) - self.assertAllClose(lrs.Value(5000).eval(), 0.000624937) - - # Tests that the schedule peaks at 4000 steps. - self.assertGreater(lrs.Value(4000).eval(), lrs.Value(3990).eval()) - self.assertGreater(lrs.Value(4000).eval(), lrs.Value(4010).eval()) - - # Tests that the schedule increases linearly before 4000 steps. - for step in range(300, 4000, 200): - self.assertAllClose( - lrs.Value(step).eval() * 2., - lrs.Value(step + 10).eval() + lrs.Value(step - 10).eval()) - - print(lrs.Value(4999).eval()) - print(lrs.Value(5000).eval()) - print(lrs.Value(5001).eval()) - print(lrs.Value(6000).eval()) - # Tests that the schedule is fixed after decay end steps. - self.assertGreater(lrs.Value(4999).eval(), lrs.Value(5000).eval()) - self.assertAllClose(lrs.Value(5000).eval(), lrs.Value(5001).eval()) - self.assertAllClose(lrs.Value(5000).eval(), lrs.Value(6000).eval()) - - def testTransformerScheduleNoWarmUp(self): - params = schedule.TransformerScheduleNoWarmUp.Params().Set( - decay_start=4000, model_dim=512) - lrs = params.Instantiate() - - base_params = schedule.TransformerSchedule.Params().Set( - warmup_steps=4000, model_dim=512) - base_lrs = base_params.Instantiate() - - with self.session(): - - # Tests that the schedule is flat up until 4000 steps. - self.assertAllClose(lrs.Value(0).eval(), 0.000698684) - self.assertAllClose(lrs.Value(1000).eval(), 0.000698684) - self.assertAllClose(lrs.Value(2000).eval(), 0.000698684) - self.assertAllClose(lrs.Value(3000).eval(), 0.000698684) - self.assertAllClose(lrs.Value(4000).eval(), 0.000698684) - self.assertAllClose(lrs.Value(4500).eval(), 0.000658735) - self.assertAllClose(lrs.Value(5000).eval(), 0.000624937) - - # Test that the schedule is identical with transformer-lr after 4k steps - self.assertAllClose(base_lrs.Value(4000).eval(), lrs.Value(4000).eval()) - self.assertAllClose(base_lrs.Value(4010).eval(), lrs.Value(4010).eval()) - self.assertAllClose(base_lrs.Value(5000).eval(), lrs.Value(5000).eval()) - - def testPolynomialLRSchedule(self): - p = schedule.PolynomialSchedule.Params().Set( - power=2, start=(0, 0.), limit=(20000, 2.)) - with self.session(): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in [0, 10000, 20000]] - self.assertAllClose( - pts, - [ - [0, 0.0], - [10000, 0.5], # 2 * (0.5 ** 2) - [20000, 2.0], - ]) - self.assertEqual(len(lrs.Value(42).shape), 0) - - def testPolynomialLimitOriginLRSchedule(self): - p = schedule.PolynomialSchedule.Params().Set( - power=2, start=(0, 0.), limit=(20000, 2.), origin='limit') - with self.session(): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in [0, 5000, 10000, 15000, 20000]] - self.assertAllClose( - pts, - [ - [0, 0.0], - [5000, 0.875], # 2 * (1 - (1 - 0.25) ** 2) - [10000, 1.5], # 2 * (1 - (1 - 0.5) ** 2) - [15000, 1.875], # 2 * (1 - (1 - 0.75) ** 2) - [20000, 2.0], - ]) - self.assertEqual(len(lrs.Value(42).shape), 0) - - def testCombinedLRSchedule(self): - p = schedule.CombinedMinimumSchedule.Params().Set(schedules=[ - schedule.LinearSchedule.Params().Set( - start=(0., 1.), limit=(2000000, 8.)), - schedule.LinearSchedule.Params().Set( - start=(2000000., 8.), limit=(4000000, 8.)), - schedule.ExponentialSchedule.Params().Set( - start=(4000000., 8.), limit=(8000000, 0.5)) - ]) - with self.session(): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 10000000, 1000000)] - self.assertAllClose( - pts, - [ - # Linear increasing. - [0, 1.0], - [1000000, 4.5], - # Constant - [2000000, 8.0], - [3000000, 8.0], - # Exponentially decreasing. - [4000000, 8.0], - [5000000, 4.0], - [6000000, 2.0], - [7000000, 1.0], - [8000000, 0.5], - [9000000, 0.5] - ]) - - def testLinearRampupExponentialDecayScaledByNumSplitSchedule(self): - p = schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params( - ).Set( - warmup=250000, decay_start=32000000, decay_end=64000000, min=0.5) - with self.session(), cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', gpus=8): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 10000000, 1000000)] - self.assertAllClose( - pts, - [ - # Linear increasing. - [0, 1.0], - [1000000, 4.5], - # Constant - [2000000, 8.0], - [3000000, 8.0], - # Exponentially decreasing. - [4000000, 8.0], - [5000000, 4.0], - [6000000, 2.0], - [7000000, 1.0], - [8000000, 0.5], - [9000000, 0.5] - ]) - - def testLinearRampupExponentialDecayScaledByNumSplitScheduleWarmUpInit(self): - p = schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params( - ).Set( - warmup_init=0, - warmup=250000, - decay_start=32000000, - decay_end=64000000, - min=0.5) - with self.session(), cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', gpus=8): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 10000000, 1000000)] - self.assertAllClose( - pts, - [ - # Linear increasing from warmup_init=0. - [0, 0], - [1000000, 4.0], - # Constant - [2000000, 8.0], - [3000000, 8.0], - # Exponentially decreasing. - [4000000, 8.0], - [5000000, 4.0], - [6000000, 2.0], - [7000000, 1.0], - [8000000, 0.5], - [9000000, 0.5] - ]) - - def testLinearRampupExponentialDecayScaledByNumSplitScheduleWithCap(self): - p = schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params( - ).Set( - warmup=250000, - decay_start=32000000, - decay_end=64000000, - min=0.5, - max=5.0) - with self.session(), cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', gpus=8): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 10000000, 1000000)] - self.assertAllClose( - pts, - [ - # Linear increasing. - [0, 1.0], - [1000000, 4.5], - # Constant - [2000000, 5.0], - [3000000, 5.0], - # Exponentially decreasing. - [4000000, 5.0], - [5000000, 4.0], - [6000000, 2.0], - [7000000, 1.0], - [8000000, 0.5], - [9000000, 0.5] - ]) - - def testLinearRampupExponentialDecayScaledByNumSplitScheduleWithNumSplits( - self): - p = schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params( - ).Set( - warmup=250000, - decay_start=32000000, - decay_end=64000000, - min=0.5, - max=5.0, - num_splits=8) - # Increases the number of splits to 32. - with self.session(), cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', gpus=8, split_size=4): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 10000000, 1000000)] - # Values are copied from - # testLinearRampupExponentialDecayScaledByNumSplitScheduleWithCap. - self.assertAllClose( - pts, - [ - # Linear increasing. - [0, 1.0], - [1000000, 4.5], - # Constant - [2000000, 5.0], - [3000000, 5.0], - # Exponentially decreasing. - [4000000, 5.0], - [5000000, 4.0], - [6000000, 2.0], - [7000000, 1.0], - [8000000, 0.5], - [9000000, 0.5] - ]) - - def testLinearRampupExponentialDecayScaledByNumSplitScheduleNoWarmUp(self): - p = schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params( - ).Set( - warmup=0, decay_start=32000000, decay_end=64000000, min=0.5) - with self.session(), cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', gpus=8): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 10000000, 1000000)] - self.assertAllClose( - pts, - [ - # Constant - [0, 8.0], - [1000000, 8.0], - [2000000, 8.0], - [3000000, 8.0], - # Exponentially decreasing. - [4000000, 8.0], - [5000000, 4.0], - [6000000, 2.0], - [7000000, 1.0], - [8000000, 0.5], - [9000000, 0.5] - ]) - - def testLinearRampupExponentialDecayScaledByNumSplitScheduleExpOnly(self): - p = schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params( - ).Set( - warmup=0, decay_start=0, decay_end=32000000, min=0.5) - with self.session(), cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', gpus=8): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 6000000, 1000000)] - self.assertAllClose( - pts, - [ - # Exponentially decreasing. - [0, 8.0], - [1000000, 4.0], - [2000000, 2.0], - [3000000, 1.0], - [4000000, 0.5], - [5000000, 0.5] - ]) - - def testLinearRampupSqrtDecayByBatchSizeAndReplicasSchedule(self): - p = schedule.LinearRampupSqrtDecayByBatchSizeAndReplicas.Params().Set( - warmup_examples=100000, batch_size=100) - with self.session(), cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', gpus=10): - lrs = p.Instantiate() - self.assertAllClose(lrs.Value(-1).eval(), 0.0) - self.assertAllClose(lrs.Value(49).eval(), 0.05) - self.assertAllClose(lrs.Value(99).eval(), 0.1) - self.assertAllClose(lrs.Value(399).eval(), 0.05) - self.assertAllClose(lrs.Value(1599).eval(), 0.025) - - def testDevBasedSchedule(self): - logdir = tf.test.get_temp_dir() - tf.io.gfile.mkdir(os.path.join(logdir, 'eval_dev')) - - p = schedule.DevBasedSchedule.Params() - p.tolerance = 1.0 - p.window = 2 - p.decay = 0.5 - p.min_factor = 0.20 - early_stop.MetricHistory.SetLogdirInMetricHistories(p, logdir) - - lrs = p.Instantiate() - mh = lrs._metric_history - mh.params.local_filesystem = True - with self.session(): - self.evaluate(tf.global_variables_initializer()) - mh.ConditionalAppend(mh.params.jobname, mh.params.metric, 1, 10.0) - # best = 1 - self.assertAllClose(lrs.Value(0).eval(), 1.0) - - mh.ConditionalAppend(mh.params.jobname, mh.params.metric, 2, 5.0) - # best = 2 - self.assertAllClose(lrs.Value(0).eval(), 1.0) - - mh.ConditionalAppend(mh.params.jobname, mh.params.metric, 5, 4.0) - # best = 2, out of window - self.assertAllClose(lrs.Value(0).eval(), 0.5) - - mh.ConditionalAppend(mh.params.jobname, mh.params.metric, 6, 4.0) - # best = 2, ref = 5, in window - self.assertAllClose(lrs.Value(0).eval(), 0.5) - - mh.ConditionalAppend(mh.params.jobname, mh.params.metric, 9, 4.0) - # best = 2, ref = 5, out of window - self.assertAllClose(lrs.Value(0).eval(), 0.25) - - mh.ConditionalAppend(mh.params.jobname, mh.params.metric, 10, 3.9) - # best = 10 - self.assertAllClose(lrs.Value(0).eval(), 0.25) - - mh.ConditionalAppend(mh.params.jobname, mh.params.metric, 13, 3.0) - # best = 10, out of window, min factor - self.assertAllClose(lrs.Value(0).eval(), 0.20) - - def testLinearRampupPiecewiseConstantSchedule(self): - p = schedule.LinearRampupPiecewiseConstantSchedule.Params().Set( - boundaries=[40, 64, 80, 96], - lrs=[1.0, 0.1, 0.01, 0.001], - ) - with self.session(), cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', tpus=8): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 15, 1)] - - self.assertAllClose( - pts, [[0, 0.0], [1, 1.6], [2, 3.2], [3, 4.8], [4, 6.4], [5, 8.0], - [6, 8.0], [7, 8.0], [8, 0.8], [9, 0.8], [10, 0.08], [11, 0.08], - [12, 0.008], [13, 0.008], [14, 0.008]]) - - def testCosineSchedule(self): - p = schedule.CosineSchedule.Params().Set( - initial_value=3.0, final_value=1.0, total_steps=400000) - with self.session(): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 600000, 100000)] - self.assertAllClose( - pts, - [ - [0, 3.0], - [100000, math.cos(math.pi / 4) + 2.], # angle=pi/4 - [200000, 2.0], # angle=pi/2, half-way - [300000, math.cos(math.pi * 3 / 4) + 2.], # angle=pi*3/4 - [400000, 1.0], - [500000, 1.0], - ]) - - def testLinearRampupCosineSchedule(self): - p = schedule.LinearRampupCosineSchedule.Params().Set( - warmup_steps=200, - initial_value=3.0, - final_value=1.0, - total_steps=400000, - num_splits=1) - with self.session(): - lrs = p.Instantiate() - - pts = [[i, lrs.Value(i).eval()] - for i in [0, 100, 200, 100000, 200000, 300000, 400000]] - self.assertAllClose( - pts, - [ - [0, 0.0], - [100, 1.5], - [200, 3.0], - [100000, math.cos(math.pi / 4) + 2.], # angle=pi/4 - [200000, 2.0], # angle=pi/2, half-way - [300000, math.cos(math.pi * 3 / 4) + 2.], # angle=pi*3/4 - [400000, 1.0], - ]) - - def testPiecewiseSchedule(self): - # Linear ramp-up in 20000 steps, cosine decay in 40000 steps. - p0 = schedule.LinearSchedule.Params().Set(start=(0, 0.), limit=(20000, 2.)) - p1 = schedule.CosineSchedule.Params().Set( - initial_value=2.0, total_steps=40000) - p = schedule.PiecewiseSchedule.Params().Set( - boundaries=[20000], schedules=[p0, p1]) - with self.session(): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in range(0, 70000, 10000)] - self.assertAllClose( - pts, - [ - [0, 0.0], - [10000, 1.0], # half-way in linear ramp-up. - [20000, 2.0], # completed linear ramp-up. - [30000, math.cos(math.pi / 4) + 1.], # pi/4. - [40000, 1.0], # pi/2. - [50000, math.cos(math.pi * 3 / 4) + 1.], # pi*3/4. - [60000, 0.0], # pi. - ]) - - def testCycleSchedule(self): - p0 = schedule.LinearSchedule.Params().Set(start=(0, 0.), limit=(1000, 1.)) - p1 = schedule.Constant.Params().Set(value=5.0) - p = schedule.CycleSchedule.Params().Set(schedules=[p0, p1], steps=[4, 1]) - with self.session(): - lrs = p.Instantiate() - pts = [[i, lrs.Value(i).eval()] for i in [0, 1, 4, 5, 998, 999, 1000]] - self.assertAllClose(pts, [ - [0, 0.0], - [1, 1.0 / 1000.0], - [4, 5.0], - [5, 5.0 / 1000.0], - [998, 998.0 / 1000.0], - [999, 5.0], - [1000, 1.0], - ]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/scorers.py b/lingvo/core/scorers.py deleted file mode 100644 index cd962428..00000000 --- a/lingvo/core/scorers.py +++ /dev/null @@ -1,132 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Helper classes for computing scores.""" - -import collections -import math -import six - - -def _ToUnicode(line): - return six.ensure_text(line, 'utf-8') - - -def _Tokenize(string): - return _ToUnicode(string).split() - - -def NGrams(lst, order): - """Generator that yields all n-grams of the given order present in lst.""" - return (lst[i:i + order] for i in range(len(lst) - order + 1)) - - -class Unsegmenter: - """Un-segments (merges) segmented strings. - - Used to retain back the original surface form of strings that are encoded - using byte-pair-encoding (BPE), word-piece-models (WPM) or - sentence-piece-models (SPM). - """ - - _BPE_SEPARATOR = _ToUnicode('@@ ') - _WPM_SEPARATOR = _ToUnicode('\xe2\x96\x81') # Same for SPM. - - def __init__(self, separator_type=None): - self._separator_type = separator_type - - def _UnsegmentWpm(self, line): - return _ToUnicode(line).replace(' ', '').replace(self._WPM_SEPARATOR, - ' ').strip() - - def _UnsegmentBpe(self, line): - return _ToUnicode(line).replace(self._BPE_SEPARATOR, '').strip() - - def __call__(self, line): - if self._separator_type == 'bpe': - return self._UnsegmentBpe(line) - elif self._separator_type in ['wpm', 'spm']: - return self._UnsegmentWpm(line) - else: - return line - - -class BleuScorer: - """Scorer to compute BLEU scores to measure translation quality. - - The BLEU score is the geometric average precision of all token n-grams of - order 1 to max_ngram across all sentences. - - Successive calls to AddSentence() accumulate statistics which are converted to - an overall score on calls to ComputeOverallScore(). - - Example usage: - >>> scorer = BleuScorer(max_ngram=4) - >>> scorer.AddSentence("hyp matches ref str", "hyp matches ref str") - >>> scorer.AddSentence("almost right", "almost write") - >>> print(scorer.ComputeOverallScore()) - 0.6687... - """ - - def __init__(self, max_ngram=4, separator_type=None): - self._max_ngram = max_ngram - self._hyp_ngram_matches = [0 for _ in range(max_ngram)] - self._hyp_ngram_counts = [0 for _ in range(max_ngram)] - self._num_ref_tokens = 0 - self._num_hyp_tokens = 0 - self._unsegmenter = Unsegmenter(separator_type) - - @property - def unsegmenter(self): - return self._unsegmenter - - def AddSentence(self, ref_str, hyp_str): - """Accumulates ngram statistics for the given ref and hyp string pair.""" - ref_tokens = tuple(_Tokenize(self._unsegmenter(ref_str))) - self._num_ref_tokens += len(ref_tokens) - hyp_tokens = tuple(_Tokenize(self._unsegmenter(hyp_str))) - self._num_hyp_tokens += len(hyp_tokens) - for order_idx in range(self._max_ngram): - ref_counts = collections.Counter(NGrams(ref_tokens, order_idx + 1)) - hyp_matches = collections.Counter() - hyp_count = 0 - for x in NGrams(hyp_tokens, order_idx + 1): - hyp_count += 1 - count = ref_counts[x] - if count: - # Clip hyp_matches so ngrams that are repeated more frequently in hyp - # than ref are not double counted. - hyp_matches[x] = min(hyp_matches[x] + 1, count) - self._hyp_ngram_matches[order_idx] += sum(hyp_matches.values()) - self._hyp_ngram_counts[order_idx] += hyp_count - - def ComputeOverallScore(self): - """Computes overall BLEU score from the statistics accumulated so far.""" - score = 0.0 - num_nonzero_orders = 0 - for order_idx in range(self._max_ngram): - matches = self._hyp_ngram_matches[order_idx] - total = self._hyp_ngram_counts[order_idx] - if matches > 0.0 and total > 0.0: - score += math.log(matches / total) - num_nonzero_orders += 1 - if not num_nonzero_orders: - return 0.0 - precision = math.exp(score / num_nonzero_orders) - - brevity_penalty = 1.0 - if self._num_hyp_tokens < self._num_ref_tokens: - brevity_penalty = math.exp(1 - self._num_ref_tokens/self._num_hyp_tokens) - return brevity_penalty * precision diff --git a/lingvo/core/scorers_test.py b/lingvo/core/scorers_test.py deleted file mode 100644 index b888cee8..00000000 --- a/lingvo/core/scorers_test.py +++ /dev/null @@ -1,98 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for scorers.""" - -import math -import lingvo.compat as tf -from lingvo.core import scorers -from lingvo.core import test_helper -from lingvo.core import test_utils - - -class BleuScorerTest(test_utils.TestCase): - - def testNGrams(self): - words = 'a b c d e'.split(' ') - self.assertEqual([['a'], ['b'], ['c'], ['d'], ['e']], - list(scorers.NGrams(words, 1))) - - self.assertEqual([['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'e']], - list(scorers.NGrams(words, 2))) - - self.assertEqual([['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'e']], - list(scorers.NGrams(words, 3))) - - def testBleuScorerDocTest(self): - scorer = scorers.BleuScorer(max_ngram=4) - scorer.AddSentence('hyp matches ref str', 'hyp matches ref str') - self.assertAlmostEqual((4/4 * 3/3 * 2/2 * 1/1) ** (1/4), - scorer.ComputeOverallScore()) - scorer.AddSentence('almost right', 'almost write') - self.assertAlmostEqual((5/6 * 3/4 * 2/2 * 1/1) ** (1/4), - scorer.ComputeOverallScore()) - - def testBleuScorerClipsExtraHypNGrams(self): - scorer = scorers.BleuScorer(max_ngram=4) - scorer.AddSentence('a b c d', 'a a b c d') - self.assertAlmostEqual((4/5 * 3/4 * 2/3 * 1/2) ** (1/4), - scorer.ComputeOverallScore()) - - def testBleuScorerSentencesShorterThanMaxNGram(self): - scorer = scorers.BleuScorer(max_ngram=4) - scorer.AddSentence('', '') - self.assertAlmostEqual(0.0, scorer.ComputeOverallScore()) - scorer.AddSentence('a', 'a') - self.assertAlmostEqual(1.0, scorer.ComputeOverallScore()) - scorer.AddSentence('a b', 'a b') - self.assertAlmostEqual(1.0, scorer.ComputeOverallScore()) - scorer.AddSentence('a b c', 'a b c') - self.assertAlmostEqual(1.0, scorer.ComputeOverallScore()) - scorer.AddSentence('a b c d', 'a b c d') - self.assertAlmostEqual(1.0, scorer.ComputeOverallScore()) - - def testBleuScorerBrevityPenalty(self): - scorer = scorers.BleuScorer(max_ngram=4) - scorer.AddSentence('1 2 3 4 5', '1 2 3 4 -2') - # No penalty. - expected_score = (4/5 * 3/4 * 2/3 * 1/2) ** (1/4) - self.assertAlmostEqual(0.6687403, expected_score) - self.assertAlmostEqual(expected_score, scorer.ComputeOverallScore()) - - scorer = scorers.BleuScorer(max_ngram=4) - scorer.AddSentence('1 2 3 4 5', '1 2 3 4') - expected_score = math.exp(1 - 5/4) * (4/4 * 3/3 * 2/2 * 1/1) ** (1/4) - self.assertAlmostEqual(0.7788008, expected_score) - self.assertAlmostEqual(expected_score, scorer.ComputeOverallScore()) - - def testBleuScorerMatchesCppImplementationOneExample(self): - scorer = scorers.BleuScorer() - scorer.AddSentence( - 'It is obvious that this will have a certain influence .', - 'It is clear that this will have a certain influence .') - self.assertAlmostEqual(0.74194467, scorer.ComputeOverallScore()) - - def testBleuScorerMatchesCppImplementation(self): - filename = test_helper.test_src_dir_path('core/ops/testdata/wmt/sm18.txt') - scorer = scorers.BleuScorer() - with open(filename, 'rb') as fp: - for line in fp: - hyp, ref = line[:-1].split(b'\t') - scorer.AddSentence(ref, hyp) - self.assertAlmostEqual(0.313776, scorer.ComputeOverallScore(), places=5) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/self_attention_layer.py b/lingvo/core/self_attention_layer.py deleted file mode 100644 index bd19951a..00000000 --- a/lingvo/core/self_attention_layer.py +++ /dev/null @@ -1,101 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Self-attention layers. - -[1] Attention is all you need. - https://arxiv.org/pdf/1706.03762.pdf Section 3. -[2] Pay Less Attention with Lightweight and Dynamic Convolutions. - https://arxiv.org/abs/1901.10430 -""" - -from lingvo.core import base_layer -from lingvo.core import batch_major_attention -from lingvo.core import py_utils - -MultiHeadedSelfAttention = batch_major_attention.MultiHeadedAttention - - -# pyformat: disable -class Builder(batch_major_attention.Builder): - """Builder for self-attention layers.""" - - def SelfAttention(self, name): - p = self.params - input_to_add = ( - 'i.vec' if p.selfatten_add_unnormalized_input else 'after_ln') - - attention_inputs = 'after_ln,after_ln,after_ln,i.paddings' - if p.packed_input: - attention_inputs += ',i.segment_mask' - - sub_list = [ - ('i.vec->after_ln', self._LN('LN', p.model_dim)), - ('{}->after_att,unused_prob'.format(attention_inputs), self._MultiHeadedAtten('atten')), - ('after_att->after_dropout', - self._Dropout('dropout', p.residual_dropout_prob)), - ('{},after_dropout->o.vec'.format(input_to_add), self._Add('add')), - ('i.paddings->o.paddings', self._Id('id')), - ] - - if p.packed_input: - sub_list.append( - ('i.segment_mask->o.segment_mask', self._Id('mask'))) - - return self._Graph( - name, - ['i'], # input NestedMap with {vec, paddings, segment_mask} - ['o'], # output NestedMap with {vec, paddings, segment_mask} - *sub_list) - - def _TransformerLayerBlock(self, name): - """(inputs, paddings) -> (encoded, paddings).""" - return self._Seq( - name, - self.SelfAttention('self_atten'), - self.Feedforward('ff')) - - def TransformerStack(self, name, num_layers=1): - """Returns a stack of num_layers self-attention layers.""" - blocks = [self._TransformerLayerBlock( - 'block_{}'.format(d)) for d in range(num_layers)] - return self._MaybeSplit(name, blocks) or ( - self._Rep(name, num_layers, self._TransformerLayerBlock('block'))) -# pyformat: enable - - -# TODO(huangyp): remove this layer after transition to nested maps is complete. -class StackedTransformerEncoderLayers(base_layer.BaseLayer): - """Wrapper class for layers returned by Builder.TransformerStack.""" - - @classmethod - def Cast(cls, params): - # Cast params returned from the builder to params in this class. - params.Define('base_cls', params.cls, 'Store the base class in params.') - params.cls = cls - return params - - def __init__(self, params): - # Make this class a sub-class of params.base_cls - self.__class__ = type(self.__class__.__name__, (params.base_cls, object), - dict(self.__class__.__dict__)) - super(self.__class__, self).__init__(params) # pylint: disable=bad-super-call - - def FProp(self, theta, vec, paddings, segment_mask=None): - outputs = super(self.__class__, self).FProp( # pylint: disable=bad-super-call - theta, - py_utils.NestedMap( - vec=vec, paddings=paddings, segment_mask=segment_mask)) - return outputs.vec, outputs.paddings diff --git a/lingvo/core/self_attention_layer_test.py b/lingvo/core/self_attention_layer_test.py deleted file mode 100644 index dcce2e0e..00000000 --- a/lingvo/core/self_attention_layer_test.py +++ /dev/null @@ -1,167 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for batch_major self attention.""" - -from absl.testing import parameterized -from lingvo import compat as tf -from lingvo.core import batch_major_attention as mt_attention -from lingvo.core import py_utils -from lingvo.core import self_attention_layer as self_attention -from lingvo.core import test_utils -import numpy as np - - -class BuilderTest(test_utils.TestCase, parameterized.TestCase): - - @parameterized.named_parameters( - { - 'testcase_name': '_baseline', - 'num_splits': 1, - 'num_micro_batches': 1 - }, { - 'testcase_name': '_two_splits', - 'num_splits': 2, - 'num_micro_batches': 2 - }, { - 'testcase_name': '_one_split_two_micro_batches', - 'num_splits': 1, - 'num_micro_batches': 2 - }) - - def testTransformerStack(self, num_splits, num_micro_batches): - with self.session(use_gpu=False) as sess: - bs = 2 - sl = 21 - d = 16 - tf.random.set_seed(12345) - deterministic_dropout = num_splits > 1 or num_micro_batches > 1 - atten_builder = self_attention.Builder.Params().Set( - model_dim=d, - num_heads=2, - ff_hidden_dim=5, - deterministic_dropout=deterministic_dropout, - num_splits=num_splits, - num_micro_batches=num_micro_batches) - p = atten_builder.Instantiate().TransformerStack('atten', 6) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - input_embs = tf.constant( - np.random.random(size=[bs, sl, d]), dtype=np.float) - paddings = tf.zeros([bs, sl]) - segment_mask = tf.zeros([bs, 1, sl, sl]) - - out = l.FPropDefaultTheta( - py_utils.NestedMap( - vec=input_embs, paddings=paddings, segment_mask=segment_mask)) - enc_out = out.vec - tf.logging.info('enc_out={}'.format(enc_out.shape)) - enc_out_sum = tf.reduce_sum(enc_out) - - tf.global_variables_initializer().run() - actual_enc_out, actual_enc_out_sum = sess.run([enc_out, enc_out_sum]) - print('actual_enc_out_sum=', actual_enc_out_sum) - - self.assertAllEqual(actual_enc_out.shape, [bs, sl, d]) - self.assertAllClose(386.16741943359375, actual_enc_out_sum, atol=1e-5) - - -class TransformerLayerTest(test_utils.TestCase, parameterized.TestCase): - - def _TransformerAttentionLayerInputs(self, input_dim=4, dtype=tf.float32): - np.random.seed(6348575) - query_vec = tf.transpose( - tf.stack([ - tf.constant(np.random.rand(2, input_dim), dtype=dtype) - for _ in range(5) - ]), [1, 0, 2]) - paddings = tf.constant([[0, 0, 1, 1, 0], [1, 0, 0, 0, 1]], dtype=dtype) - aux_vec = tf.transpose( - tf.stack([ - tf.constant(np.random.rand(2, input_dim), dtype=dtype) - for _ in range(7) - ]), [1, 0, 2]) - aux_paddings = tf.constant([[0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]], - dtype=dtype) - - segment_mask = tf.zeros([2, 1, 5, 5]) - return query_vec, paddings, aux_vec, aux_paddings, segment_mask - - def testTransformerAttentionLayerFPropCaseEncoder(self): - with self.session(use_gpu=True) as sess: - query_vec, paddings, _, _, segment_mask = self._TransformerAttentionLayerInputs( - ) - - expected_p = mt_attention.TransformerAttentionLayer.Params().Set( - name='transformer_self_atten', - input_dim=4, - is_masked=False, - num_heads=2) - expected_p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - expected_l = expected_p.Instantiate() - expected_ctx_vec, _ = expected_l.FProp(expected_l.theta, query_vec, None, - paddings) - - atten_builder = self_attention.Builder.Params().Set( - model_dim=4, num_heads=2, selfatten_add_unnormalized_input=True) - p = atten_builder.Instantiate().SelfAttention('self_atten') - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - l_out = l.FProp( - l.theta, - py_utils.NestedMap( - vec=query_vec, paddings=paddings, segment_mask=segment_mask)) - ctx_vec = l_out.vec - tf.global_variables_initializer().run() - actual_ctx = sess.run(ctx_vec) - expected_ctx = sess.run(expected_ctx_vec) - self.assertAllClose(expected_ctx, actual_ctx) - - def testTransformerLayerFProp(self): - with self.session(use_gpu=True) as sess: - src_vec, _, _, _, segment_mask = self._TransformerAttentionLayerInputs() - paddings = tf.zeros([2, 5]) - p = mt_attention.TransformerLayer.Params() - p.name = 'transformer_encoder_layer' - p.input_dim = 4 - p.tr_fflayer_tpl.hidden_dim = 7 - p.tr_atten_tpl.num_heads = 2 - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - l = p.Instantiate() - enc_vec, _ = l.FProp(l.theta, src_vec, paddings, None, None) - - self_attention_p = self_attention.Builder.Params().Set( - model_dim=4, - num_heads=2, - ff_hidden_dim=7, - selfatten_add_unnormalized_input=True) - self_attention_p = self_attention_p.Instantiate().TransformerStack( - name='transformer_encoder_layer', num_layers=1) - self_attention_p.params_init = py_utils.WeightInit.Xavier( - scale=1.0, seed=0) - self_attention_l = self_attention_p.Instantiate() - self_attention_enc_out = self_attention_l.FProp( - self_attention_l.theta, - py_utils.NestedMap( - vec=src_vec, paddings=paddings, segment_mask=segment_mask)) - self_attention_enc_vec = self_attention_enc_out.vec - tf.global_variables_initializer().run() - enc_vec = sess.run(enc_vec) - self_attention_enc_vec = sess.run(self_attention_enc_vec) - self.assertAllClose(enc_vec, self_attention_enc_vec) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/sendrecv.py b/lingvo/core/sendrecv.py deleted file mode 100644 index f489a41c..00000000 --- a/lingvo/core/sendrecv.py +++ /dev/null @@ -1,112 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Send/Recv ops. - -The following _Send()/_Recv() are adapted from python op wrappers -generated by python_op_gen_main. python_op_gen_main.cc's -PrintAllPythonOps needs to be updated to export internal ops. -""" - -from lingvo import compat as tf - -# pylint: disable=g-direct-tensorflow-import -from tensorflow.compiler.tf2xla.python import xla -# pylint: enable=g-direct-tensorflow-import - - -def _TpuCore(device): - """Returns the TPU core represented by , or -1 if not TPU.""" - prefix = "device:TPU_REPLICATED_CORE:" - if prefix in device: - return int(device[len(prefix):]) - return -1 - - -class Channel: - """A communication channel to transfer tensors in order.""" - - def __init__(self, dtype, shape, send_device, recv_device, name=None): - """Construct a channel. - - Args: - dtype: The dtype of tensors sent through the channel. - shape: The shape of tensors sent through the channel. Must be a fully - defined shape for TPUs. - send_device: A fully-specified tensorflow device. - recv_device: A fully-specified tensorflow device. - name: A name for the channel (optional). - """ - current_graph = tf.get_default_graph() - assert current_graph, "A channel is scoped within a tf.Graph" - self._dtype = dtype - self._send_device = send_device - self._recv_device = recv_device - self._name = current_graph.unique_name(name if name else "channel") - - assert shape is not None - shape = tf.TensorShape(shape) - - self._shape = shape - self._send_tpu_core = _TpuCore(send_device) - self._recv_tpu_core = _TpuCore(recv_device) - self._send_called = False - self._recv_op = None - assert ((self._send_tpu_core == -1) == (self._recv_tpu_core == -1)), ( - "Mixing TPU and non-TPU: %s and %s" % (send_device, recv_device)) - if self._send_tpu_core >= 0: - assert self._shape.is_fully_defined(), ( - "TPU channel must have fully defined shape. Name: %s, shape: %s" % - (self._name, self._shape)) - assert self._send_tpu_core != self._recv_tpu_core, ( - "TPU send/recv must be cross-core: %s and %s" % - (send_device, recv_device)) - - def Send(self, tensor): - """Sends a tensor through the channel.""" - assert tensor.dtype == self._dtype - assert not self._send_called, ("Send called multiple times for %s" % - self._name) - self._send_called = True - if self._send_tpu_core == -1: - return tf.raw_ops.Send( - tensor=tensor, - tensor_name=self._name, - send_device=self._send_device, - send_device_incarnation=0, - recv_device=self._recv_device) - else: - with tf.device(self._send_device): - return xla.send( - tensor, tensor_name=self._name, name="Send_" + self._name) - - def Recv(self): - """Receives a tensor from the channel.""" - if self._send_tpu_core == -1: - received = tf.raw_ops.Recv( - tensor_type=self._dtype, - tensor_name=self._name, - send_device=self._send_device, - send_device_incarnation=0, - recv_device=self._recv_device) - received.set_shape(self._shape) - return received - else: - with tf.device(self._recv_device): - return xla.recv( - self._dtype, - tensor_name=self._name, - shape=self._shape, - name="Recv_" + self._name) diff --git a/lingvo/core/sendrecv_test.py b/lingvo/core/sendrecv_test.py deleted file mode 100644 index 91637a1c..00000000 --- a/lingvo/core/sendrecv_test.py +++ /dev/null @@ -1,105 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for sendrecv.""" - -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import sendrecv -from lingvo.core import test_utils -import numpy as np - -FLAGS = tf.flags.FLAGS - - -def _ListDevices(target): - with tf.Session(target) as sess: - devices = sess.list_devices() - return [_.name for _ in devices] - - -def _Target(): - return "" - - -class SendrecvTest(test_utils.TestCase, parameterized.TestCase): - - def testBasic(self): - devices = _ListDevices(_Target()) - print("\n".join(devices)) - sender, recver = devices[0], devices[-1] - shape = [] - - for dtype in tf.float32, tf.complex64: - to_send = np.array(3.1415 + 2j).astype(dtype.as_numpy_dtype) - g = tf.Graph() - with g.as_default(): - ch = sendrecv.Channel(dtype, shape, sender, recver, "test") - with tf.device(sender): - src_val = tf.constant(to_send) - send_op = ch.Send(src_val) - with tf.device(recver): - recv_val = ch.Recv() - - with tf.Session(_Target(), graph=g): - _, val = self.evaluate([send_op, recv_val]) - - self.assertAllClose(to_send, val) - - @parameterized.named_parameters( - ("_defun", False), - ("_function", True), - ) - def testInsideFunction(self, use_tf_function): - FLAGS.call_defun_use_tf_function = use_tf_function - devices = _ListDevices(_Target()) - sender, recver = devices[0], devices[-1] - shape = [] - - def SendRecv(graph, dtype): - to_send = np.array(3.1415 + 2j).astype(dtype.as_numpy_dtype) - with graph.as_default(): - ch = sendrecv.Channel(dtype, shape, sender, recver, "test") - with tf.device(sender): - - # py_utils.CallDefun requires non-empty inputs. Same below. - def Send(_): - src_val = tf.constant(to_send) - ch.Send(src_val) - return tf.convert_to_tensor(1.0) - - send_op = py_utils.CallDefun(Send, tf.convert_to_tensor(0)) - - with tf.device(recver): - - def Recv(_): - return ch.Recv() - - recv_val = py_utils.CallDefun(Recv, tf.convert_to_tensor(0)) - return send_op, recv_val, to_send - - for dtype in tf.float32, tf.complex64: - g = tf.Graph() - send_op, recv_val, sent_val = SendRecv(g, dtype) - - with tf.Session(_Target(), graph=g): - _, val = self.evaluate([send_op, recv_val]) - - self.assertAllClose(sent_val, val) - - -if __name__ == "__main__": - tf.test.main() diff --git a/lingvo/core/spectrum_augmenter.py b/lingvo/core/spectrum_augmenter.py deleted file mode 100644 index 18623328..00000000 --- a/lingvo/core/spectrum_augmenter.py +++ /dev/null @@ -1,845 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Lingvo layers that used for spectrum augmentation.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils - -_SPECAUGMENT_ARGS = ( - 'freq_mask_max_bins', - 'freq_mask_count', - 'use_dynamic_time_mask_max_frames', - 'time_mask_max_frames', - 'time_mask_count', - 'time_mask_max_ratio', - 'time_masks_per_frame', - 'time_warp_bound', - 'time_warp_max_frames', - 'time_warp_max_ratio', -) - - -def _random_uniform_op(use_stateless_op): - return tf.random.stateless_uniform if use_stateless_op else tf.random.uniform - - -def _random_normal_op(use_stateless_op): - return tf.random.stateless_normal if use_stateless_op else tf.random.normal - - -def _global_seed_from_inputs(input_floats): - """Generates a random seed tensor based on input floats and mode key. - - Args: - input_floats: a set of float input tensors that are derived from the input - data (for example, input tokens). The important thing is that these are - usually different for each batch. - - Returns: - A tensor of shape=[2] with integer seed tensors derived from the inputs. - """ - timestamp = tf.math.floormod( - tf.cast(tf.timestamp(), dtype=tf.int64), 10000000) - input_sum = tf.cast(tf.reduce_sum(tf.math.abs(input_floats)), dtype=tf.int64) - return tf.stack([timestamp + input_sum, timestamp - input_sum], axis=-1) - - -def _hat(x): - """Hat function. - - The hat function is a piecewise linear function defined such that - 1) x < -1: _hat(x) = 0 - 2) -1 <= x < 0: _hat(x) = x + 1 - 3) 0 <= x < 1: _hat(x) = -x + 1 - 4) x > 1 : _hat(x) = 0 - - Args: - x: A tensor. - - Returns: - Tensor obtained by element-wise application of the hat function. - """ - return tf.nn.relu(x + 1) - 2 * tf.nn.relu(x) + tf.nn.relu(x - 1) - - -class SpectrumAugmenter(base_layer.BaseLayer): - """Performs data augmentation as according to the SpecAug paper. - - https://arxiv.org/pdf/1904.08779.pdf - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('freq_mask_max_bins', 15, - 'Maximum number of frequency bins of frequency masking.') - p.Define('freq_mask_count', 1, - 'Number of times we apply masking on the frequency axis.') - # TODO(danielspark): Deprecate 'use_dynamic_time_mask_max_frames' and - # introduce enum parameter to replace it. - p.Define( - 'use_dynamic_time_mask_max_frames', False, - 'If true, time_mask_max_frames is determined by ' - 'time_mask_max_ratio * utterance_length.') - p.Define( - 'time_mask_max_frames', 50, 'Maximum number of frames of time masking. ' - 'Overridden when use_dynamic_time_mask_max_frames = True.') - p.Define( - 'time_mask_count', 1, - 'Number of times we apply masking on the time axis. ' - 'Acts as upper-bound when time_masks_per_frame > 0.') - p.Define('time_mask_max_ratio', 1.0, - 'Maximum portion allowed for time masking.') - p.Define( - 'time_masks_per_frame', 0.0, - 'Ratio of number of time masks to be applied against the number ' - 'of frames. If > 0, multiplicity of the time mask is determined by ' - 'min(time_masks_per_frame * utterance_length, time_mask_count).') - p.Define( - 'time_warp_bound', 'static', - 'To be set to either `dynamic` or `static`. ' - 'If `dynamic`, time warp bound is determined by ' - 'time_warp_max_ratio * utterance_length. ' - 'If `static`, time warp bound is determined by ' - 'min(time_warp_max_frames, time_warp_max_ratio * utterance_length).') - p.Define('time_warp_max_frames', 0, - 'Maximum number of frames for shifting in time warping.') - p.Define('time_warp_max_ratio', 0.0, - 'Maximum portion of frames for shifting in time warping.') - p.Define('use_noise', False, 'Whether to noisify the time masked region.') - p.Define('gaussian_noise', False, 'Use Gaussian distribution for noise.') - p.Define('unstack', False, - 'Whether to unstack features before applying SpecAugment.') - p.Define('stack_height', 3, - 'Number of frames stacked on top of each other.') - p.Define( - 'domain_ids', [0], - 'If domain ids was given, this parameters describe which domain ' - 'will be augmented, e.g. ' - 'p.domain_ids = [2, 7, 1] ' - 'p.time_mask_count = [1, 2, 0] ' - 'implies domain 2 will have 1, 7 has 2 and 1 has 0 time masks. ' - 'All other domain will not augmented if it exists.') - p.Define( - 'use_input_dependent_random_seed', False, - 'Whether to use stateless random TensorFlow ops, with seeds' - 'determined by the input features. This feature is necessary for' - 'applications including federated learning.') - return p - - def __init__(self, params): - super().__init__(params) - self.SetVariableFree() - p = self.params - num_domains = len(p.domain_ids) - for field in _SPECAUGMENT_ARGS: - v = getattr(p, field) - if isinstance(v, (list, tuple)): - assert len(v) == num_domains, ( - 'Length: %d of field: %s does not match total domains: %d' % - (len(v), field, num_domains)) - else: - setattr(p, field, [v] * num_domains) - assert p.freq_mask_max_bins[0] > -1 - assert p.time_mask_max_frames[0] > -1 - assert p.time_warp_max_frames[0] > -1 - - def EinsumBBmBm(self, a, b, name=None): - return tf.einsum('b,bm->bm', a, b, name=name) - - def EinsumBmtBmBt(self, a, b, name=None): - return tf.einsum('bmt,bm->bt', a, b, name=name) - - def EinsumBxycByBxyc(self, a, b, name=None): - return tf.einsum('bxyc,by->bxyc', a, b, name=name) - - def EinsumBxycBxBxyc(self, a, b, name=None): - return tf.einsum('bxyc,bx->bxyc', a, b, name=name) - - def EinsumBxyBxBxy(self, a, b, name=None): - return tf.einsum('bxy,bx->bxy', a, b, name=name) - - def EinsumBxycBzxBzyc(self, a, b, name=None): - return tf.einsum('bxyc,bzx->bzyc', a, b, name=name) - - def _GetMask(self, - batch_size, - choose_range, - mask_size, - global_seed, - max_length=None, - masks_per_frame=0.0, - multiplicity=1, - dtype=tf.float32, - max_ratio=1.0): - """Returns fixed size multi-masks starting from random positions. - - A multi-mask is a mask obtained by applying multiple masks. - - This function when max_length is given: - 1) Sample random mask lengths less than max_length with shape - (batch_size, multiplicity). - 2) Truncate lengths to a max of (choose_range * max_ratio), - so that each mask is fully contained within the corresponding sequence. - 3) Random sample start points of shape (batch_size, multiplicity) - with in (choose_range - lengths). - 4) For each batch, multiple masks (whose number is given by the - multiplicity) are constructed. - 5) Return a mask of shape (batch_size, mask_size) where masks are - obtained by composing the masks constructed in step 4). - If masks_per_frame > 0, the number is given by - min(masks_per_frame * choose_range, multiplicity). - If not, all the masks are composed. The masked regions are set to zero. - - This function when max_length is not given: - 1) Sample random mask lengths less than (choose_range * max_ratio) - with shape (batch_size, multiplicity). - 2) Proceed to steps 3), 4) and 5) of the above. - - Args: - batch_size: Batch size. Integer number. - choose_range: Range within which the masked entries must lie. Tensor of - shape (batch_size,). - mask_size: Size of the mask. Integer number. - global_seed: an integer seed tensor for stateless random ops. - max_length: Maximum number of allowed consecutive masked entries. Integer - number or None. - masks_per_frame: Number of masks per frame. Float number. If > 0, the - multiplicity of the mask is set to be masks_per_frame * choose_range. - multiplicity: Maximum number of total masks. Integer number. - dtype: Data type. - max_ratio: Maximum portion of the entire range allowed to be masked. Float - number. - - Returns: - mask: a fixed size multi-mask starting from a random position with shape - (batch_size, mask_size). - """ - p = self.params - # Non-empty random seed values are only used for testing or when using - # stateless random ops. seed_1 and seed_2 are set separately to avoid - # correlation of mask size and mask position. - if p.use_input_dependent_random_seed: - seed_1 = global_seed + 1 - seed_2 = global_seed + 2 - elif p.random_seed: - seed_1 = p.random_seed + 1 - seed_2 = 2 * p.random_seed - else: - seed_1 = p.random_seed - seed_2 = p.random_seed - # Sample lengths for multiple masks. - if max_length and max_length > 0: - max_length = tf.broadcast_to(tf.cast(max_length, dtype), (batch_size,)) - else: - max_length = tf.cast(choose_range, dtype=dtype) * max_ratio - random_uniform = _random_uniform_op(p.use_input_dependent_random_seed) - masked_portion = random_uniform( - shape=(batch_size, multiplicity), - minval=0.0, - maxval=1.0, - dtype=dtype, - seed=seed_1) - masked_frame_size = self.EinsumBBmBm(max_length, masked_portion) - masked_frame_size = tf.cast(masked_frame_size, dtype=tf.int32) - # Make sure the sampled length was smaller than max_ratio * length_bound. - # Note that sampling in this way was biased - # (shorter sequence may over-masked.) - choose_range = tf.expand_dims(choose_range, -1) - choose_range = tf.tile(choose_range, [1, multiplicity]) - length_bound = tf.cast(choose_range, dtype=dtype) - length_bound = tf.cast(max_ratio * length_bound, dtype=tf.int32) - length = tf.minimum(masked_frame_size, tf.maximum(length_bound, 1)) - - # Choose starting point. - random_start = random_uniform( - shape=(batch_size, multiplicity), maxval=1.0, seed=seed_2) - start_with_in_valid_range = random_start * tf.cast( - (choose_range - length + 1), dtype=dtype) - start = tf.cast(start_with_in_valid_range, tf.int32) - end = start + length - 1 - - # Shift starting and end point by small value. - delta = tf.constant(0.1) - start = tf.expand_dims(tf.cast(start, dtype) - delta, -1) - start = tf.tile(start, [1, 1, mask_size]) - end = tf.expand_dims(tf.cast(end, dtype) + delta, -1) - end = tf.tile(end, [1, 1, mask_size]) - - # Construct pre-mask of shape (batch_size, multiplicity, mask_size). - diagonal = tf.expand_dims( - tf.expand_dims(tf.cast(tf.range(mask_size), dtype=dtype), 0), 0) - diagonal = tf.tile(diagonal, [batch_size, multiplicity, 1]) - pre_mask = tf.cast( - tf.math.logical_and(diagonal < end, diagonal > start), dtype=dtype) - - # Sum masks with appropriate multiplicity. - if masks_per_frame > 0: - multiplicity_weights = tf.tile( - tf.expand_dims(tf.range(multiplicity, dtype=dtype), 0), - [batch_size, 1]) - multiplicity_tensor = masks_per_frame * tf.cast(choose_range, dtype=dtype) - multiplicity_weights = tf.cast( - multiplicity_weights < multiplicity_tensor, dtype=dtype) - pre_mask = self.EinsumBmtBmBt(pre_mask, multiplicity_weights) - else: - pre_mask = tf.reduce_sum(pre_mask, 1) - mask = tf.cast(1.0 - tf.cast(pre_mask > 0, dtype=dtype), dtype=dtype) - - if p.fprop_dtype is not None and p.fprop_dtype != p.dtype: - mask = tf.cast(mask, p.fprop_dtype) - - return mask - - def _GetWarpMatrix(self, - batch_size, - choose_range, - matrix_size, - global_seed, - max_warp_frames=None, - dtype=tf.float32, - max_ratio=1.0): - """Returns warp matrices starting from random positions. - - In this function when max_warp_frames != None: - 1) Sample random warp displacements from the interval - [-max_warp_frames, max_warp_frames) to yield shift tensor - with shape (batch_size,). - 2) Truncate lengths to a maximum magnitude of (choose_range * max_ratio), - so that each shift is fully contained within the - corresponding sequence. - 3) Random sample origin points of shape (batch_size, multiplicity) - with in [shift, choose_range - shift). - 4) Return a batch of 1-D linear maps that fix the boundary points and - shift the origin point by the shift. - - When max_warp_frames == None: - 1) Sample random warp displacements with magnitudes less than - (choose_range * max_ratio) to yield shift tensor with - shape (batch_size,). - 2) Proceed through steps 3), 4). - - Args: - batch_size: Batch size. Integer number. - choose_range: Range within which the warp reference points must lie. - Tensor of shape (batch_size,). - matrix_size: Dimension of vector space warp matrix is applied to. Integer - number. - global_seed: an integer seed tensor for stateless random ops. - max_warp_frames: Upper-bound on the warp distance. Integer or None. - dtype: Data type. - max_ratio: Maximum ratio between the shift distance and choose_range. - Float number. - - Returns: - warp_matrix: An array of fixed size warp matrices with shape - (batch_size, matrix_size, matrix_size). - """ - p = self.params - # Non-empty random seed values are only used for testing or when using - # stateless random ops. seed_3, seed_4, and seed_5 are set separately to - # avoid correlation of warp magnitude and origin position. - if p.use_input_dependent_random_seed: - seed_3 = global_seed + 3 - seed_4 = global_seed + 4 - seed_5 = global_seed + 5 - elif p.random_seed: - seed_3 = p.random_seed - 1 - seed_4 = p.random_seed - 1 - seed_5 = 2 * p.random_seed + 1 - else: - seed_3 = p.random_seed - seed_4 = p.random_seed - seed_5 = p.random_seed - - choose_range_dtype = tf.cast(choose_range, dtype=dtype) - length_upper_bound = tf.cast(max_ratio * choose_range_dtype, dtype=tf.int32) - # Set shift length. - - random_uniform = _random_uniform_op(p.use_input_dependent_random_seed) - - if max_warp_frames and max_warp_frames > 0: - shift = random_uniform( - shape=(batch_size,), - minval=-1 * max_warp_frames, - maxval=max_warp_frames + 1, - dtype=tf.int32, - seed=seed_3) - else: - random_ratio = random_uniform( - shape=(batch_size,), - minval=-1.0, - maxval=1.0, - dtype=dtype, - seed=seed_4) - shift = tf.cast(random_ratio * tf.cast(length_upper_bound, dtype=dtype), - tf.int32) - # Make sure the sampled length was smaller than max_ratio * length_bound. - # Note that sampling in this way is biased. - # (Shorter sequence may over-masked.) - final_shift = tf.maximum(-length_upper_bound, - tf.minimum(shift, length_upper_bound)) - # Choose origin anchor point. - mid_range = tf.cast(choose_range, dtype=tf.int32) - mid_range = tf.maximum(choose_range - 2, 0) - random_origin = random_uniform(shape=(batch_size,), maxval=1.0, seed=seed_5) - origin_with_in_valid_range = random_origin * tf.cast(mid_range, dtype=dtype) - origin = tf.cast(origin_with_in_valid_range, tf.int32) + 1 - # Set destination point of the origin anchor point under the warp map. - destination = origin + final_shift - # Cast origin and destination. - origin = tf.cast(origin, dtype=dtype) - destination = tf.cast(destination, dtype=dtype) - - return self._ConstructWarpMatrix( - batch_size=batch_size, - matrix_size=matrix_size, - origin=origin, - destination=destination, - choose_range=choose_range_dtype, - dtype=dtype) - - def _ConstructWarpMatrix(self, batch_size, matrix_size, origin, destination, - choose_range, dtype): - """Returns warp matrices according to origin, destination and choose_range. - - This function constructs a batch of warp matrices which maps the batch - of origin points to the batch of destination points with fixed boundary - coordinates at 0 and choose_range. - - The warping function, defined by the origin anchor point `origin`, - the destination of the origin anchor point `destination` and the - length of the domain in the warping axis `choose_range` is a piecewise - linear map that fixes the points 0 and `choose_range` and maps - `origin` to `destination`. - - For the warping matrix to be non-singular, destination must lie in the - range 1<= destination <= choose_range - 1, so a destination - out of this range is adjusted to be in this range before the warping - matrix is constructed. - - The warping map can be explicitly written by first defining the slopes: - 1) slope_0 = origin / destination. - 2) slope_1 = (choose_range - origin) / (choose_range - destination). - 3) slope_2 = 1.0. - - Then the origin point orig_i of the mapped coordinate i is given by: - 1) i < destination: orig_i = slope_0 * i. - 2) destination <= i < choose_range: - orig_i = slope_1 * i - (slope_1 - slope_0) * destination. - 3) i >= choose_range: orig_i = i. - - Denoting n_i = ceil(orig_i), the warp matrix element warp[i][j] is given by: - 1) j = n_i: 1 - n_i + orig_i. - 2) j = n_i - 1: n_i - orig_i. - 3) Otherwise: 0. - - Applying the warp matrix to an array of pixels, i.e., - warped_pixel[i] = sum_j warp[i][j] * pixel[j], one would get - warped_pixel[i] = (n_i-orig_i) pixel[n_i-1] + (1-n_i+orig_i) pixel[n_i]. - - Args: - batch_size: Batch size. Integer number. - matrix_size: Dimension of the vector space the warp matrix is applied to. - Integer number. - origin: Origin anchor point for warping. Tensor of shape (batch_size,) and - data type dtype. - destination: Destination of the origin anchor point upon warping. Tensor - of shape (batch_size,) and data type dtype. - choose_range: Range within which the warp reference points must lie. - Tensor of shape (batch_size,) data type dtype. - dtype: Data type of origin, destination, choose_range and the output warp - matrix. - - Returns: - warp_matrix: An array of fixed size warp matrices with shape - (batch_size, matrix_size, matrix_size). - """ - p = self.params - - # Entries of destination must be in the range - # 1 <= destination <= choose_range - 1 - # for warp matrix to have non-singular values. - destination = tf.minimum(tf.maximum(destination, 1.0), choose_range - 1.0) - - # Construct piece-wise linear function fixing boundary points - # specified by zero, choose_range and matrix size and maps - # the origin anchor point to the destination. - destination_bc = tf.broadcast_to(destination, (matrix_size, batch_size)) - destination_bc = tf.transpose(destination_bc) - choose_range_bc = tf.broadcast_to(choose_range, (matrix_size, batch_size)) - choose_range_bc = tf.transpose(choose_range_bc) - - # Slopes of piece-wise linear function. - slope_0 = origin / destination - slope_1 = (choose_range - origin) / (choose_range - destination) - slope_2 = 1.0 - - # x is a batch of origin matrices. - # The origin matrix is the matrix such that - # origin[i][j] = Origin coordinate of coordinate i for the warp map. - # Denoting the destination of the origin anchor point in the - # warp map as "dest," the origin coordinate of point i is given by: - # 1) i < dest: slope_0 * i. - # 2) dest <= i < choose_range: slope_1 * i - (slope_1 - slope_0) * dest. - # 3) i >= choose_range: i. - x = tf.broadcast_to( - tf.cast(tf.range(matrix_size), dtype=dtype), (batch_size, matrix_size)) - x = ( - self.EinsumBBmBm(slope_0, x) + - self.EinsumBBmBm(slope_1 - slope_0, tf.nn.relu(x - destination_bc)) + - self.EinsumBBmBm(slope_2 - slope_1, tf.nn.relu(x - choose_range_bc))) - x = tf.broadcast_to(x, (matrix_size, batch_size, matrix_size)) - x = tf.transpose(x, perm=[1, 2, 0]) - - # y is a batch of coordinate matrices. - # A coordinate matrix is a matrix such that - # coordinate[i][j] = j. - y = tf.broadcast_to( - tf.cast(tf.range(matrix_size), dtype=dtype), - (batch_size, matrix_size, matrix_size)) - # Warp matrix is obtained by applying hat function element-wise to (x-y). - # Denoting the origin point of i under the warp map as orig_i, - # and n_i = ceil(orig_i), the warp matrix element warp[i][j] is given by: - # 1) j = n_i: 1 - n_i + orig_i. - # 2) j = n_i - 1: n_i - orig_i. - # 3) Otherwise: 0. - # Applying the warp matrix to pixels, i.e., - # warped_pixel[i] = sum_j warp[i][j] * original_pixel[j], one would get - # warped_pixel[i] = (n_i - orig_i) * original_pixel[n_i-1] - # + (1 - n_i + orig_i) * original_pixel[n_i]. - warp_matrix = x - y - warp_matrix = _hat(warp_matrix) - if p.fprop_dtype is not None and p.fprop_dtype != dtype: - warp_matrix = tf.cast(warp_matrix, p.fprop_dtype) - - return warp_matrix - - def _FrequencyMask(self, - inputs, - global_seed, - dtype=tf.float32, - domain_id_index=0): - """Applies frequency masking with given degree to inputs. - - Args: - inputs: Batch of input features of shape (batch_size, time_length, - num_freq, channels). - global_seed: an integer seed tensor for stateless random ops. - dtype: Data type. - domain_id_index: domain id index. - - Returns: - Inputs with random frequency masking applied. - """ - p = self.params - - # Mask parameters. - freq_mask_max_bins = p.freq_mask_max_bins[domain_id_index] - multiplicity = p.freq_mask_count[domain_id_index] - - # If masking length or count is zero, do nothing. - if freq_mask_max_bins == 0 or multiplicity == 0: - return inputs - - # Arguments to pass to mask generator. - batch_size, _, num_freq, _ = py_utils.GetShape(inputs) - choose_range = tf.cast( - tf.broadcast_to(num_freq, (batch_size,)), dtype=tf.int32) - # Create masks in frequency direction and apply. - block_arrays = self._GetMask( - tf.shape(inputs)[0], - choose_range=choose_range, - mask_size=num_freq, - global_seed=global_seed, - max_length=freq_mask_max_bins, - masks_per_frame=0.0, - multiplicity=multiplicity, - dtype=dtype, - max_ratio=1.0) - return self.EinsumBxycByBxyc(inputs, block_arrays) - - def _TimeMask(self, - inputs, - seq_lengths, - global_seed, - noisify=False, - gaussian_noise=False, - dtype=tf.float32, - domain_id_index=0): - """Applies time masking with given degree to inputs. - - Args: - inputs: Batch of input features of shape (batch_size, time_length, - num_freq, channels). - seq_lengths: The actual sequence lengths which mask been sampled of shape - (batch_size,). - global_seed: an integer seed tensor for stateless random ops. - noisify: Whether to noisify the masked out regions. - gaussian_noise: Whether to use gaussian noise when noisifying. - dtype: Data type. - domain_id_index: domain id index. - - Returns: - Inputs with random time masking applied. - """ - p = self.params - - # Get time masking parameters. - time_mask_max_frames = p.time_mask_max_frames[domain_id_index] - time_masks_per_frame = p.time_masks_per_frame[domain_id_index] - use_dynamic_time_mask_max_frames = \ - p.use_dynamic_time_mask_max_frames[domain_id_index] - multiplicity = p.time_mask_count[domain_id_index] - max_ratio = p.time_mask_max_ratio[domain_id_index] - - # If maximum mask length is zero, do nothing. - if ((time_mask_max_frames == 0 and not use_dynamic_time_mask_max_frames) or - max_ratio <= 0.0): - return inputs - if multiplicity == 0: - return inputs - seq_lengths = tf.cast(seq_lengths, tf.int32) - batch_size, time_length, _, _ = py_utils.GetShape(inputs) - - # When using dynamic time mask size, discard upper-bound on - # maximum allowed frames for time mask. - if use_dynamic_time_mask_max_frames: - time_mask_max_frames = None - # Create masks in time direction and apply. - block_arrays = self._GetMask( - batch_size, - choose_range=seq_lengths, - mask_size=time_length, - global_seed=global_seed, - max_length=time_mask_max_frames, - masks_per_frame=time_masks_per_frame, - multiplicity=multiplicity, - dtype=dtype, - max_ratio=max_ratio) - - # Non-empty random seed values are only used for testing or when using - # stateless random ops. seed_6 and seed_7 are set separately to avoid - # correlation of warp magnitude and origin position. - if p.use_input_dependent_random_seed: - seed_6 = global_seed + 6 - seed_7 = global_seed + 7 - else: - seed_6 = p.random_seed - seed_7 = p.random_seed - - outputs = self.EinsumBxycBxBxyc( - inputs, block_arrays, name='einsum_formasking') - if noisify: - # Sample noise with standard deviation with factor * 0.1 + 0.0001 - # TODO(ngyuzh): Make sure this won't affect EOS. - if gaussian_noise: - stddev = 1.0 - else: - random_uniform = _random_uniform_op(p.use_input_dependent_random_seed) - factor = random_uniform( - shape=(), minval=1.0, maxval=2.0, dtype=dtype, seed=seed_6) - stddev = factor * 0.1 + 0.0001 - random_normal = _random_normal_op(p.use_input_dependent_random_seed) - noise = random_normal( - shape=[tf.shape(inputs)[0], - tf.shape(inputs)[1], - tf.shape(inputs)[2]], - stddev=stddev, - seed=seed_7) - if p.fprop_dtype is not None and p.fprop_dtype != p.dtype: - noise = tf.cast(noise, p.fprop_dtype) - outputs_mask = self.EinsumBxyBxBxy( - noise, 1.0 - block_arrays, name='einsum_fornoisymasking') - outputs = outputs + tf.expand_dims(outputs_mask, -1) - - return outputs - - def _TimeWarp(self, - inputs, - seq_lengths, - global_seed, - dtype=tf.float32, - domain_id_index=0): - """Applies time warping with given degree to inputs. - - Args: - inputs: Batch of input features of shape (batch_size, time_length, - num_freq, channels). - seq_lengths: The actual sequence lengths which mask been sampled of shape - (batch_size,). - global_seed: an integer seed tensor for stateless random ops. - dtype: Data type. - domain_id_index: Domain ID index. - - Returns: - Inputs with random time warping applied. - """ - p = self.params - batch_size, time_length, _, _ = py_utils.GetShape(inputs) - - # Get parameters for warping. - time_warp_max_frames = p.time_warp_max_frames[domain_id_index] - max_ratio = p.time_warp_max_ratio[domain_id_index] - time_warp_bound = p.time_warp_bound[domain_id_index] - assert time_warp_bound in ('static', 'dynamic') - - # If maximum warp length is zero, do nothing. - if ((time_warp_max_frames == 0 and time_warp_bound == 'static') or - max_ratio <= 0.0): - return inputs - seq_lengths = tf.cast(seq_lengths, tf.int32) - - # Discard upper-bound on time-warp frames when - # dynamic time warping is used. - if time_warp_bound == 'dynamic': - time_warp_max_frames = None - - # Create warping matrix in time direction and apply - warp_matrix = self._GetWarpMatrix( - batch_size, - choose_range=seq_lengths, - matrix_size=time_length, - global_seed=global_seed, - max_warp_frames=time_warp_max_frames, - dtype=dtype, - max_ratio=max_ratio) - - return self.EinsumBxycBzxBzyc(inputs, warp_matrix, name='einsum_forwarping') - - def UnstackFeatures(self, src_inputs, src_paddings): - """Unstacks src_input and src_paddings based off stack height.""" - sh = self.params.stack_height - bs, old_series_length, _, channels = py_utils.GetShape(src_inputs) - unstacked_series_length = old_series_length * sh - src_inputs = tf.reshape(src_inputs, - [bs, unstacked_series_length, -1, channels]) - content = 1 - src_paddings - lengths = tf.cast(sh * tf.reduce_sum(content, axis=1), tf.int32) - mask = tf.sequence_mask(lengths, maxlen=unstacked_series_length) - src_paddings = 1 - tf.cast(mask, tf.int32) - return src_inputs, src_paddings - - def _AugmentationNetwork(self, - series_length, - inputs, - paddings, - global_seed, - domain_id_index=0): - """Returns augmented features. - - Args: - series_length: Total length of time series. - inputs: Batch of input features of shape (batch_size, time_length, - num_freq, channels). - paddings: Batch of padding vectors of shape (batch_size, time_length). - global_seed: an integer seed tensor for stateless random ops. - domain_id_index: domain id index. - - Returns: - Batch of output features of shape (batch_size, time_length, num_freq, - channels) obtained by applying random augmentations to inputs. - """ - p = self.params - dtype = p.dtype - - # Unstack the features. - if p.unstack: - inputs, paddings = self.UnstackFeatures(inputs, paddings) - - lengths = tf.reduce_sum(1 - paddings, 1) - inputs = self._TimeWarp( - inputs, - lengths, - global_seed=global_seed, - dtype=dtype, - domain_id_index=domain_id_index) - inputs = self._TimeMask( - inputs, - lengths, - global_seed=global_seed, - noisify=p.use_noise, - gaussian_noise=p.gaussian_noise, - dtype=dtype, - domain_id_index=domain_id_index) - inputs = self._FrequencyMask( - inputs, - global_seed=global_seed, - dtype=dtype, - domain_id_index=domain_id_index) - - # Restack the features after applying specaugment. - if p.unstack: - inputs = tf.reshape( - inputs, [tf.shape(inputs)[0], series_length, -1, - tf.shape(inputs)[3]]) - - return inputs - - def FProp(self, theta, inputs, paddings, domain_ids=None): - """Applies data augmentation by randomly mask spectrum in inputs. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - inputs: A tensor of shape [batch, time, freq, num_channels]. - paddings: A 0/1 tensor of shape [batch, time]. - domain_ids: input domain_ids of shape [batch, time]. - - Returns: - A pair of 2 tensors: - - - augmented_inputs: A tensor of shape [batch, time, freq, num_channels]. - - paddings: A 0/1 tensor of shape [batch, time]. - """ - p = self.params - - global_seed = None # A tensor seed in case stateless random ops are needed. - if p.use_input_dependent_random_seed: - global_seed = _global_seed_from_inputs(inputs) - - batch_size, series_length, _, _ = py_utils.GetShape(inputs) - if len(p.domain_ids) > 1: - augmented_inputs = tf.zeros_like(inputs) - original_inputs = inputs - for i, domain_id in enumerate(p.domain_ids): - augmented_domain = self._AugmentationNetwork( - series_length, - inputs, - paddings, - global_seed=global_seed, - domain_id_index=i) - target_domain = tf.cast( - tf.expand_dims(tf.tile([domain_id], [batch_size]), -1), - dtype=p.dtype) - # [batch, time]. - domain_mask = tf.cast( - tf.equal(domain_ids, target_domain), dtype=p.dtype) - augmented_domain = self.EinsumBxycBxBxyc( - augmented_domain, domain_mask, name='einsum_domainmasking') - original_inputs = self.EinsumBxycBxBxyc( - original_inputs, 1.0 - domain_mask, name='einsum_domainmasking2') - augmented_inputs = augmented_domain + augmented_inputs - augmented_inputs = original_inputs + augmented_inputs - else: - augmented_inputs = self._AugmentationNetwork( - series_length, - inputs, - paddings, - global_seed=global_seed, - domain_id_index=0) - return augmented_inputs, paddings diff --git a/lingvo/core/spectrum_augmenter_on_device.py b/lingvo/core/spectrum_augmenter_on_device.py deleted file mode 100644 index 6be9a8b9..00000000 --- a/lingvo/core/spectrum_augmenter_on_device.py +++ /dev/null @@ -1,57 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Lingvo layers that are used for spectrum augmentation on-device.""" - -import lingvo.compat as tf -from lingvo.core import spectrum_augmenter - - -class SpectrumAugmenterOnDevice(spectrum_augmenter.SpectrumAugmenter): - """Performs data augmentation as according to the SpecAug paper. - - This implementation uses portable replacements for the tf.einsum ops. - - https://arxiv.org/pdf/1904.08779.pdf - """ - - def EinsumBBmBm(self, a, b, name=None): - """Portable replacement for tf.einsum('b,bm->bm', a, b).""" - return tf.math.multiply(tf.expand_dims(a, axis=-1), b, name=name) - - def EinsumBmtBmBt(self, a, b, name=None): - """Portable replacement for tf.einsum('bmt,bm->bt', a, b).""" - return tf.linalg.matvec(a, b, transpose_a=True, name=name) - - def EinsumBxycByBxyc(self, a, b, name=None): - """Portable replacement for tf.einsum('bxyc,by->bxyc', a, b).""" - expanded_b = tf.expand_dims(tf.expand_dims(b, axis=1), axis=3) - return tf.math.multiply(a, expanded_b, name=name) - - def EinsumBxycBxBxyc(self, a, b, name=None): - """Portable replacement for tf.einsum('bxyc,bx->bxyc', a, b).""" - expanded_b = tf.expand_dims(tf.expand_dims(b, axis=2), axis=3) - return tf.math.multiply(a, expanded_b, name=name) - - def EinsumBxyBxBxy(self, a, b, name=None): - """Portable replacement for tf.einsum('bxy,bx->bxy', a, b).""" - return tf.math.multiply(a, tf.expand_dims(b, axis=2), name=name) - - def EinsumBxycBzxBzyc(self, a, b, name=None): - """Portable replacement for tf.einsum('bxyc,bzx->bzyc', a, b).""" - expanded_a = tf.expand_dims(a, axis=1) - expanded_b = tf.expand_dims(tf.expand_dims(b, axis=-1), axis=-1) - return tf.reduce_sum( - tf.math.multiply(expanded_a, expanded_b, name=name), axis=2) diff --git a/lingvo/core/spectrum_augmenter_on_device_test.py b/lingvo/core/spectrum_augmenter_on_device_test.py deleted file mode 100644 index 8547be3f..00000000 --- a/lingvo/core/spectrum_augmenter_on_device_test.py +++ /dev/null @@ -1,492 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for spectrum augmenter layer.""" - -import lingvo.compat as tf -from lingvo.core import spectrum_augmenter -from lingvo.core import spectrum_augmenter_on_device -from lingvo.core import test_utils -import numpy as np - - -class SpectrumAugmenterTest(test_utils.TestCase): - - def testSpectrumAugmenterWithTimeMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 5 - inputs = tf.ones([batch_size, 20, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, i + 12]), - tf.ones([1, 8 - i])], axis=1)) - paddings = tf.concat(paddings, axis=0) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 5 - p.time_mask_count = 2 - p.time_mask_max_ratio = 1.0 - p.random_seed = 23456 - specaug_layer = p.Instantiate() - - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterDynamicSizeTimeMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 3 - inputs = tf.ones([batch_size, 20, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 8 * i + 3]), - tf.ones([1, 17 - 8 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_ratio = 0.4 - p.time_mask_count = 1 - p.use_dynamic_time_mask_max_frames = True - p.random_seed = 12345 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterDynamicMultiplicityTimeMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 4 - inputs = tf.ones([batch_size, 22, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 5 * i + 5]), - tf.ones([1, 16 - 5 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 5 - p.time_mask_count = 10 - p.time_masks_per_frame = 0.2 - p.random_seed = 67890 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterDynamicSizeAndMultiplicityTimeMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 4 - inputs = tf.ones([batch_size, 22, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 5 * i + 5]), - tf.ones([1, 16 - 5 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 5 - p.time_mask_count = 10 - p.time_masks_per_frame = 0.2 - p.time_mask_max_ratio = 0.4 - p.use_dynamic_time_mask_max_frames = True - p.random_seed = 67890 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterWithFrequencyMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.ones([3, 5, 10, 1], dtype=tf.float32) - paddings = tf.zeros([3, 5]) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 6 - p.freq_mask_count = 2 - p.time_mask_max_frames = 0 - p.random_seed = 34567 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterWarpMatrixConstructor(self): - with self.session(use_gpu=False, graph=tf.Graph()): - inputs = tf.broadcast_to(tf.cast(tf.range(10), dtype=tf.float32), (4, 10)) - origin = tf.cast([2, 4, 4, 5], dtype=tf.float32) - destination = tf.cast([3, 2, 6, 8], dtype=tf.float32) - choose_range = tf.cast([4, 8, 8, 10], dtype=tf.float32) - outputs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - specaug_layer = p.Instantiate() - warp_matrix = specaug_layer._ConstructWarpMatrix( - batch_size=4, - matrix_size=10, - origin=origin, - destination=destination, - choose_range=choose_range, - dtype=tf.float32) - output = tf.einsum('bij,bj->bi', warp_matrix, inputs) - outputs.append(output) - layer_output, layer_output_on_device = self.evaluate(outputs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterWithTimeWarping(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.broadcast_to(tf.cast(tf.range(10), dtype=tf.float32), (3, 10)) - inputs = tf.expand_dims(tf.expand_dims(inputs, -1), -1) - paddings = [] - for i in range(3): - paddings.append( - tf.concat([tf.zeros([1, i + 7]), - tf.ones([1, 3 - i])], axis=1)) - paddings = tf.concat(paddings, axis=0) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 0 - p.time_warp_max_frames = 8 - p.time_warp_max_ratio = 1.0 - p.time_warp_bound = 'static' - p.random_seed = 34567 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterWithDynamicTimeWarping(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.broadcast_to(tf.cast(tf.range(10), dtype=tf.float32), (3, 10)) - inputs = tf.expand_dims(tf.expand_dims(inputs, -1), -1) - paddings = [] - for i in range(3): - paddings.append( - tf.concat([tf.zeros([1, 2 * i + 5]), - tf.ones([1, 5 - 2 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 0 - p.time_warp_max_ratio = 0.5 - p.time_warp_bound = 'dynamic' - p.random_seed = 34567 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterUnstacking(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.ones([3, 5, 10, 1], dtype=tf.float32) - paddings = tf.zeros([3, 5]) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.unstack = True - p.stack_height = 2 - p.freq_mask_max_bins = 5 - p.time_mask_max_frames = 8 - p.random_seed = 12345 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterWithPerDomainPolicyFreqMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.ones([6, 5, 4, 2], dtype=tf.float32) - input_domain_ids = tf.constant( - [[1] * 5, [2] * 5, [0] * 5, [2] * 5, [0] * 5, [1] * 5], - dtype=tf.float32) - paddings = tf.zeros([3, 5]) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.domain_ids = [0, 1, 2] - p.freq_mask_max_bins = [0, 3, 8] - p.time_mask_max_frames = 0 - p.random_seed = 1234 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta( - inputs, paddings, domain_ids=input_domain_ids) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterNoisify(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 2 - inputs = tf.ones([batch_size, 20, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 8 * i + 3]), - tf.ones([1, 17 - 8 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_ratio = 0.4 - p.time_mask_count = 1 - p.use_dynamic_time_mask_max_frames = True - p.use_noise = True - p.gaussian_noise = False - p.random_seed = 12345 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterGaussianNoisify(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 2 - inputs = tf.ones([batch_size, 20, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 8 * i + 3]), - tf.ones([1, 17 - 8 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - hs = [] - for p in [ - spectrum_augmenter.SpectrumAugmenter.Params(), - spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - ]: - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_ratio = 0.4 - p.time_mask_count = 1 - p.use_dynamic_time_mask_max_frames = True - p.use_noise = True - p.gaussian_noise = True - p.random_seed = 12345 - specaug_layer = p.Instantiate() - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - hs.append(h) - layer_output, layer_output_on_device = self.evaluate(hs) - self.assertAllClose(layer_output, layer_output_on_device) - - def testSpectrumAugmenterWithStatelessRandomOps(self): - with self.session(use_gpu=False, graph=tf.Graph()): - batch_size = 5 - inputs1 = tf.random.uniform( - shape=[batch_size, 20, 2, 2], minval=0, maxval=1, dtype=tf.float32) - inputs2 = tf.random.uniform( - shape=[batch_size, 20, 2, 2], minval=0, maxval=1, dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, i + 12]), - tf.ones([1, 8 - i])], axis=1)) - paddings = tf.concat(paddings, axis=0) - - p = spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - p.name = 'specAug_layers' - p.freq_mask_count = 1 - p.freq_mask_max_bins = 1 - p.time_mask_max_frames = 5 - p.time_mask_count = 2 - p.time_mask_max_ratio = 1.0 - p.use_input_dependent_random_seed = True - specaug_layer = p.Instantiate() - h1, _ = specaug_layer.FPropDefaultTheta(inputs1, paddings) - h2, _ = specaug_layer.FPropDefaultTheta(inputs2, paddings) - actual_layer_output1, actual_layer_output2 = self.evaluate([h1, h2]) - self.assertAllEqual( - np.shape(actual_layer_output1), np.array([5, 20, 2, 2])) - self.assertNotAllEqual(actual_layer_output1, actual_layer_output2) - - def testGraphContainsOnDeviceOps(self): - """Checks that einsum and stateful random ops are not used on-device.""" - model_graph = tf.Graph() - with model_graph.as_default(): - batch_size = 5 - inputs = tf.random.stateless_uniform( - shape=[batch_size, 20, 2, 2], - minval=0, - maxval=1, - seed=tf.constant([123, 123]), - dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, i + 12]), - tf.ones([1, 8 - i])], axis=1)) - paddings = tf.concat(paddings, axis=0) - p = spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - p.name = 'specAug_layers' - p.freq_mask_count = 1 - p.freq_mask_max_bins = 1 - p.time_mask_max_frames = 5 - p.time_mask_count = 2 - p.use_noise = True - p.gaussian_noise = True - p.time_mask_max_ratio = 1.0 - p.use_input_dependent_random_seed = True - specaug_layer = p.Instantiate() - _, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - # A list of ops that are not compatible with on-device training. - unsupported_on_device_nodes = [ - 'RandomUniform', 'RandomStandardNormal', 'Einsum' - ] - for node in model_graph.as_graph_def().node: - self.assertNotIn(node.op, unsupported_on_device_nodes) - - def testEinsumReplacementBBmBm(self): - with self.session(use_gpu=False, graph=tf.Graph()): - a = tf.random.uniform(shape=[20], minval=0, maxval=1, dtype=tf.float32) - b = tf.random.uniform( - shape=[20, 10], minval=0, maxval=1, dtype=tf.float32) - einsum = tf.einsum('b,bm->bm', a, b) - p = spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - p.name = 'specAug_layers' - specaug_layer = p.Instantiate() - replacement = specaug_layer.EinsumBBmBm(a, b) - einsum, replacement = self.evaluate([einsum, replacement]) - self.assertAllClose(einsum, replacement) - - def testEinsumReplacementBxycByBxyc(self): - with self.session(use_gpu=False, graph=tf.Graph()): - a = tf.random.uniform( - shape=[20, 5, 7, 4], minval=0, maxval=1, dtype=tf.float32) - b = tf.random.uniform(shape=[20, 7], minval=0, maxval=1, dtype=tf.float32) - einsum = tf.einsum('bxyc,by->bxyc', a, b) - p = spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - p.name = 'specAug_layers' - specaug_layer = p.Instantiate() - replacement = specaug_layer.EinsumBxycByBxyc(a, b) - einsum, replacement = self.evaluate([einsum, replacement]) - self.assertAllClose(einsum, replacement) - - def testEinsumReplacementBxycBxBxyc(self): - with self.session(use_gpu=False, graph=tf.Graph()): - a = tf.random.uniform( - shape=[20, 5, 7, 4], minval=0, maxval=1, dtype=tf.float32) - b = tf.random.uniform(shape=[20, 5], minval=0, maxval=1, dtype=tf.float32) - einsum = tf.einsum('bxyc,bx->bxyc', a, b) - p = spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - p.name = 'specAug_layers' - specaug_layer = p.Instantiate() - replacement = specaug_layer.EinsumBxycBxBxyc(a, b) - einsum, replacement = self.evaluate([einsum, replacement]) - self.assertAllClose(einsum, replacement) - - def testEinsumReplacementBxyBxBxy(self): - with self.session(use_gpu=False, graph=tf.Graph()): - a = tf.random.uniform( - shape=[20, 7, 4], minval=0, maxval=1, dtype=tf.float32) - b = tf.random.uniform(shape=[20, 7], minval=0, maxval=1, dtype=tf.float32) - einsum = tf.einsum('bxy,bx->bxy', a, b) - p = spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - p.name = 'specAug_layers' - specaug_layer = p.Instantiate() - replacement = specaug_layer.EinsumBxyBxBxy(a, b) - einsum, replacement = self.evaluate([einsum, replacement]) - self.assertAllClose(einsum, replacement) - - def testEinsumReplacementBxycBzxBzyc(self): - with self.session(use_gpu=False, graph=tf.Graph()): - a = tf.random.uniform( - shape=[20, 7, 4, 3], minval=0, maxval=1, dtype=tf.float32) - b = tf.random.uniform( - shape=[20, 5, 7], minval=0, maxval=1, dtype=tf.float32) - einsum = tf.einsum('bxyc,bzx->bzyc', a, b) - p = spectrum_augmenter_on_device.SpectrumAugmenterOnDevice.Params() - p.name = 'specAug_layers' - specaug_layer = p.Instantiate() - replacement = specaug_layer.EinsumBxycBzxBzyc(a, b) - einsum, replacement = self.evaluate([einsum, replacement]) - self.assertAllClose(einsum, replacement) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/spectrum_augmenter_test.py b/lingvo/core/spectrum_augmenter_test.py deleted file mode 100644 index a8fa6286..00000000 --- a/lingvo/core/spectrum_augmenter_test.py +++ /dev/null @@ -1,785 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for spectrum augmenter layer.""" - -import lingvo.compat as tf -from lingvo.core import spectrum_augmenter -from lingvo.core import test_utils -import numpy as np - - -class SpectrumAugmenterTest(test_utils.TestCase): - - def testSpectrumAugmenterWithTimeMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 5 - inputs = tf.ones([batch_size, 20, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, i + 12]), - tf.ones([1, 8 - i])], axis=1)) - paddings = tf.concat(paddings, axis=0) - - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 5 - p.time_mask_count = 2 - p.time_mask_max_ratio = 1.0 - p.random_seed = 23456 - specaug_layer = p.Instantiate() - expected_output = np.array([[[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]]]) - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterDynamicSizeTimeMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 3 - inputs = tf.ones([batch_size, 20, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 8 * i + 3]), - tf.ones([1, 17 - 8 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_ratio = 0.4 - p.time_mask_count = 1 - p.use_dynamic_time_mask_max_frames = True - p.random_seed = 12345 - specaug_layer = p.Instantiate() - expected_output = np.array([[[[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]]]) - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterDynamicMultiplicityTimeMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 4 - inputs = tf.ones([batch_size, 22, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 5 * i + 5]), - tf.ones([1, 16 - 5 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 5 - p.time_mask_count = 10 - p.time_masks_per_frame = 0.2 - p.random_seed = 67890 - specaug_layer = p.Instantiate() - expected_output = np.array([[[[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]]]) - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterDynamicSizeAndMultiplicityTimeMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 4 - inputs = tf.ones([batch_size, 22, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 5 * i + 5]), - tf.ones([1, 16 - 5 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 5 - p.time_mask_count = 10 - p.time_masks_per_frame = 0.2 - p.time_mask_max_ratio = 0.4 - p.use_dynamic_time_mask_max_frames = True - p.random_seed = 67890 - specaug_layer = p.Instantiate() - expected_output = np.array([[[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]], - [[[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]], - [[0., 0.], [0., 0.]], [[1., 1.], [1., 1.]], - [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]]]) - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterWithFrequencyMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.ones([3, 5, 10, 1], dtype=tf.float32) - paddings = tf.zeros([3, 5]) - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 6 - p.freq_mask_count = 2 - p.time_mask_max_frames = 0 - p.random_seed = 34567 - specaug_layer = p.Instantiate() - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - expected_output = np.array( - [[[[1.], [1.], [1.], [0.], [0.], [0.], [0.], [0.], [0.], [1.]], - [[1.], [1.], [1.], [0.], [0.], [0.], [0.], [0.], [0.], [1.]], - [[1.], [1.], [1.], [0.], [0.], [0.], [0.], [0.], [0.], [1.]], - [[1.], [1.], [1.], [0.], [0.], [0.], [0.], [0.], [0.], [1.]], - [[1.], [1.], [1.], [0.], [0.], [0.], [0.], [0.], [0.], [1.]]], - [[[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [1.], [1.]], - [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [1.], [1.]], - [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [1.], [1.]], - [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [1.], [1.]], - [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [1.], [1.]]], - [[[1.], [1.], [0.], [0.], [1.], [1.], [0.], [1.], [1.], [1.]], - [[1.], [1.], [0.], [0.], [1.], [1.], [0.], [1.], [1.], [1.]], - [[1.], [1.], [0.], [0.], [1.], [1.], [0.], [1.], [1.], [1.]], - [[1.], [1.], [0.], [0.], [1.], [1.], [0.], [1.], [1.], [1.]], - [[1.], [1.], [0.], [0.], [1.], [1.], [0.], [1.], [1.], [1.]]]]) - # pylint: enable=bad-whitespace,bad-continuation - # pyformat: enable - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterWarpMatrixConstructor(self): - with self.session(use_gpu=False, graph=tf.Graph()): - inputs = tf.broadcast_to(tf.cast(tf.range(10), dtype=tf.float32), (4, 10)) - origin = tf.cast([2, 4, 4, 5], dtype=tf.float32) - destination = tf.cast([3, 2, 6, 8], dtype=tf.float32) - choose_range = tf.cast([4, 8, 8, 10], dtype=tf.float32) - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - specaug_layer = p.Instantiate() - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - expected_output = np.array( - [[0.0000000, 0.6666667, 1.3333333, 2.0000000, 4.0000000, - 5.0000000, 6.0000000, 7.0000000, 8.0000000, 9.0000000], - [0.0000000, 2.0000000, 4.0000000, 4.6666667, 5.3333333, - 6.0000000, 6.6666667, 7.3333333, 8.0000000, 9.0000000], - [0.0000000, 0.6666667, 1.3333333, 2.0000000, 2.6666667, - 3.3333333, 4.0000000, 6.0000000, 8.0000000, 9.0000000], - [0.0000000, 0.6250000, 1.2500000, 1.8750000, 2.5000000, - 3.1250000, 3.7500000, 4.3750000, 5.0000000, 7.5000000]]) - # pylint: enable=bad-whitespace,bad-continuation - # pyformat: enable - warp_matrix = specaug_layer._ConstructWarpMatrix( - batch_size=4, - matrix_size=10, - origin=origin, - destination=destination, - choose_range=choose_range, - dtype=tf.float32) - outputs = tf.einsum('bij,bj->bi', warp_matrix, inputs) - actual_layer_output = self.evaluate(outputs) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterWithTimeWarping(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.broadcast_to(tf.cast(tf.range(10), dtype=tf.float32), (3, 10)) - inputs = tf.expand_dims(tf.expand_dims(inputs, -1), -1) - paddings = [] - for i in range(3): - paddings.append( - tf.concat([tf.zeros([1, i + 7]), - tf.ones([1, 3 - i])], axis=1)) - paddings = tf.concat(paddings, axis=0) - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 0 - p.time_warp_max_frames = 8 - p.time_warp_max_ratio = 1.0 - p.time_warp_bound = 'static' - p.random_seed = 34567 - specaug_layer = p.Instantiate() - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - expected_output = np.array( - [[[[0.0000000]], [[0.6666667]], [[1.3333334]], [[2.0000000]], - [[2.6666667]], [[3.3333335]], [[4.0000000]], [[7.0000000]], - [[8.0000000]], [[9.0000000]]], - [[[0.0000000]], [[3.0000000]], [[6.0000000]], [[6.3333334]], - [[6.6666665]], [[7.0000000]], [[7.3333334]], [[7.6666667]], - [[8.0000000]], [[9.0000000]]], - [[[0.0000000]], [[0.5000000]], [[1.0000000]], [[1.5000000]], - [[2.0000000]], [[3.4000000]], [[4.8000000]], [[6.2000000]], - [[7.6000000]], [[9.0000000]]]]) - # pylint: enable=bad-whitespace,bad-continuation - # pyformat: enable - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterWithDynamicTimeWarping(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.broadcast_to(tf.cast(tf.range(10), dtype=tf.float32), (3, 10)) - inputs = tf.expand_dims(tf.expand_dims(inputs, -1), -1) - paddings = [] - for i in range(3): - paddings.append( - tf.concat([tf.zeros([1, 2 * i + 5]), - tf.ones([1, 5 - 2 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_frames = 0 - p.time_warp_max_ratio = 0.5 - p.time_warp_bound = 'dynamic' - p.random_seed = 34567 - specaug_layer = p.Instantiate() - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - expected_output = np.array( - [[[[0.0000000]], [[1.0000000]], [[2.0000000]], [[3.0000000]], - [[4.0000000]], [[5.0000000]], [[6.0000000]], [[7.0000000]], - [[8.0000000]], [[9.0000000]]], - [[[0.0000000]], [[0.8333333]], [[1.6666666]], [[2.5000000]], - [[3.3333333]], [[4.1666665]], [[5.0000000]], [[7.0000000]], - [[8.0000000]], [[9.0000000]]], - [[[0.0000000]], [[2.0000000]], [[2.8750000]], [[3.7500000]], - [[4.6250000]], [[5.5000000]], [[6.3750000]], [[7.2500000]], - [[8.1250000]], [[9.0000000]]]]) - # pylint: enable=bad-whitespace,bad-continuation - # pyformat: enable - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterUnstacking(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.ones([3, 5, 10, 1], dtype=tf.float32) - paddings = tf.zeros([3, 5]) - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.unstack = True - p.stack_height = 2 - p.freq_mask_max_bins = 5 - p.time_mask_max_frames = 8 - p.random_seed = 12345 - specaug_layer = p.Instantiate() - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - expected_output = np.array( - [[[[1.], [1.], [0.], [1.], [1.], [1.], [1.], [0.], [1.], [1.]], - [[1.], [1.], [0.], [1.], [1.], [0.], [0.], [0.], [0.], [0.]], - [[0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.]], - [[1.], [1.], [0.], [1.], [1.], [1.], [1.], [0.], [1.], [1.]], - [[1.], [1.], [0.], [1.], [1.], [1.], [1.], [0.], [1.], [1.]]], - [[[1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.]], - [[1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.]], - [[1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.]], - [[1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.]], - [[1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.], [1.]]], - [[[1.], [1.], [0.], [1.], [1.], [1.], [1.], [0.], [1.], [1.]], - [[1.], [1.], [0.], [1.], [1.], [1.], [1.], [0.], [1.], [1.]], - [[1.], [1.], [0.], [1.], [1.], [0.], [0.], [0.], [0.], [0.]], - [[0.], [0.], [0.], [0.], [0.], [1.], [1.], [0.], [1.], [1.]], - [[1.], [1.], [0.], [1.], [1.], [1.], [1.], [0.], [1.], [1.]]]]) - # pylint: enable=bad-whitespace,bad-continuation - # pyformat: enable - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterWithPerDomainPolicyFreqMask(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(1234) - inputs = tf.ones([6, 5, 4, 2], dtype=tf.float32) - input_domain_ids = tf.constant( - [[1] * 5, [2] * 5, [0] * 5, [2] * 5, [0] * 5, [1] * 5], - dtype=tf.float32) - paddings = tf.zeros([3, 5]) - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.domain_ids = [0, 1, 2] - p.freq_mask_max_bins = [0, 3, 8] - p.time_mask_max_frames = 0 - p.random_seed = 1234 - specaug_layer = p.Instantiate() - expected_output = np.array([[[[0., 0.], [0., 0.], [1., 1.], [1., 1.]], - [[0., 0.], [0., 0.], [1., 1.], [1., 1.]], - [[0., 0.], [0., 0.], [1., 1.], [1., 1.]], - [[0., 0.], [0., 0.], [1., 1.], [1., 1.]], - [[0., 0.], [0., 0.], [1., 1.], [1., 1.]]], - [[[1., 1.], [0., 0.], [0., 0.], [0., 0.]], - [[1., 1.], [0., 0.], [0., 0.], [0., 0.]], - [[1., 1.], [0., 0.], [0., 0.], [0., 0.]], - [[1., 1.], [0., 0.], [0., 0.], [0., 0.]], - [[1., 1.], [0., 0.], [0., 0.], [0., 0.]]], - [[[1., 1.], [1., 1.], [1., 1.], [1., 1.]], - [[1., 1.], [1., 1.], [1., 1.], [1., 1.]], - [[1., 1.], [1., 1.], [1., 1.], [1., 1.]], - [[1., 1.], [1., 1.], [1., 1.], [1., 1.]], - [[1., 1.], [1., 1.], [1., 1.], [1., 1.]]], - [[[0., 0.], [0., 0.], [0., 0.], [0., 0.]], - [[0., 0.], [0., 0.], [0., 0.], [0., 0.]], - [[0., 0.], [0., 0.], [0., 0.], [0., 0.]], - [[0., 0.], [0., 0.], [0., 0.], [0., 0.]], - [[0., 0.], [0., 0.], [0., 0.], [0., 0.]]], - [[[1., 1.], [1., 1.], [1., 1.], [1., 1.]], - [[1., 1.], [1., 1.], [1., 1.], [1., 1.]], - [[1., 1.], [1., 1.], [1., 1.], [1., 1.]], - [[1., 1.], [1., 1.], [1., 1.], [1., 1.]], - [[1., 1.], [1., 1.], [1., 1.], [1., 1.]]], - [[[1., 1.], [0., 0.], [0., 0.], [1., 1.]], - [[1., 1.], [0., 0.], [0., 0.], [1., 1.]], - [[1., 1.], [0., 0.], [0., 0.], [1., 1.]], - [[1., 1.], [0., 0.], [0., 0.], [1., 1.]], - [[1., 1.], [0., 0.], [0., 0.], [1., 1.]]]]) - h, _ = specaug_layer.FPropDefaultTheta( - inputs, paddings, domain_ids=input_domain_ids) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterNoisify(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 2 - inputs = tf.ones([batch_size, 20, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 8 * i + 3]), - tf.ones([1, 17 - 8 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_ratio = 0.4 - p.time_mask_count = 1 - p.use_dynamic_time_mask_max_frames = True - p.use_noise = True - p.gaussian_noise = False - p.random_seed = 12345 - specaug_layer = p.Instantiate() - expected_output = np.array([[[[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[-0.00113627, -0.00113627], - [0.08975883, 0.08975883]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]]], - [[[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[0.09341543, 0.09341543], - [-0.11914382, -0.11914382]], - [[0.04238122, 0.04238122], - [0.115249, 0.115249]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]]]]) - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterGaussianNoisify(self): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(127) - batch_size = 2 - inputs = tf.ones([batch_size, 20, 2, 2], dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, 8 * i + 3]), - tf.ones([1, 17 - 8 * i])], - axis=1)) - paddings = tf.concat(paddings, axis=0) - - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_max_bins = 0 - p.time_mask_max_ratio = 0.4 - p.time_mask_count = 1 - p.use_dynamic_time_mask_max_frames = True - p.use_noise = True - p.gaussian_noise = True - p.random_seed = 12345 - specaug_layer = p.Instantiate() - expected_output = np.array([[[[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[-0.00798237, -0.00798237], - [0.6305642, 0.6305642]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]]], - [[[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[0.6562522, 0.6562522], - [-0.83699656, -0.83699656]], - [[0.29773206, 0.29773206], - [0.8096351, 0.8096351]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]], - [[1.00000000, 1.00000000], - [1.00000000, 1.00000000]]]]) - h, _ = specaug_layer.FPropDefaultTheta(inputs, paddings) - actual_layer_output = self.evaluate(h) - print(np.array_repr(actual_layer_output)) - self.assertAllClose(actual_layer_output, expected_output) - - def testSpectrumAugmenterWithStatelessRandomOps(self): - with self.session(use_gpu=False, graph=tf.Graph()): - batch_size = 5 - inputs1 = tf.random.uniform( - shape=[batch_size, 20, 2, 2], minval=0, maxval=1, dtype=tf.float32) - inputs2 = tf.random.uniform( - shape=[batch_size, 20, 2, 2], minval=0, maxval=1, dtype=tf.float32) - paddings = [] - for i in range(batch_size): - paddings.append( - tf.concat([tf.zeros([1, i + 12]), - tf.ones([1, 8 - i])], axis=1)) - paddings = tf.concat(paddings, axis=0) - - p = spectrum_augmenter.SpectrumAugmenter.Params() - p.name = 'specAug_layers' - p.freq_mask_count = 1 - p.freq_mask_max_bins = 1 - p.time_mask_max_frames = 5 - p.time_mask_count = 2 - p.time_mask_max_ratio = 1.0 - p.use_input_dependent_random_seed = True - specaug_layer = p.Instantiate() - h1, _ = specaug_layer.FPropDefaultTheta(inputs1, paddings) - h2, _ = specaug_layer.FPropDefaultTheta(inputs2, paddings) - actual_layer_output1, actual_layer_output2 = self.evaluate([h1, h2]) - self.assertAllEqual( - np.shape(actual_layer_output1), np.array([5, 20, 2, 2])) - self.assertNotAllEqual(actual_layer_output1, actual_layer_output2) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/step.py b/lingvo/core/step.py deleted file mode 100644 index 1a2f631f..00000000 --- a/lingvo/core/step.py +++ /dev/null @@ -1,693 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -"""An abstract layer for processing sequences step-by-step. - -E.g.:: - - def ProcessSeq(step, external_inputs, input_batch): - prepared_inputs = step.PrepareExternalInputs( - step.theta, external_inputs) - batch_size, T = tf.shape(input_batch.paddings)[:2] - state = step.ZeroState( - step.theta, prepared_inputs, batch_size) - for t in range(T): - step_inputs = input_batch.Transform(lambda x: x[:, i, ...]) - step_outputs, state = step.FProp( - step.theta, prepared_inputs, step_inputs, state) - (processing step_outputs...) -""" - -import collections - -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import builder_layers -from lingvo.core import py_utils -from lingvo.core import recurrent - - -class Step(base_layer.BaseLayer): - """A layer that processes input sequences step-by-step. - - This can be seen as an RNNCell extended with optional external inputs. - """ - - def PrepareExternalInputs(self, theta, external_inputs): - """Returns the prepared external inputs, e.g., packed_src for attention.""" - if not external_inputs: - external_inputs = py_utils.NestedMap() - packed = external_inputs.DeepCopy() - for name, child in self.children.items(): - child_external_inputs = external_inputs.get(name, py_utils.NestedMap()) - if isinstance(child, (tuple, list)): - output = [] - for i, sub in enumerate(child): - if isinstance(sub, Step): - output.append( - sub.PrepareExternalInputs(theta[name][i], - child_external_inputs)) - if output: - if len(output) != len(child): - raise ValueError('Expecting child list to be instances of Step.') - packed[name] = type(child)(output) - elif isinstance(child, Step): - packed[name] = child.PrepareExternalInputs(theta[name], - child_external_inputs) - return packed - - def ZeroState(self, theta, prepared_inputs, batch_size): - """Returns the initial state given external inputs and batch size. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: External inputs returned by PrepareExternalInputs(). - batch_size: An int scalar representing the batch size of per-step inputs. - - Returns: - A `.NestedMap` representing the initial state, which can be passed to - FProp() for processing the first time step. - """ - state0 = py_utils.NestedMap() - for name, child in self.children.items(): - if isinstance(child, (tuple, list)): - output = [] - for i, sub in enumerate(child): - if isinstance(sub, Step): - output.append( - sub.ZeroState(theta[name][i], prepared_inputs[name][i], - batch_size)) - if output: - if len(output) != len(child): - raise ValueError('Expecting child list to be instances of Step.') - state0[name] = type(child)(output) - elif isinstance(child, Step): - state0[name] = child.ZeroState(theta[name], prepared_inputs[name], - batch_size) - return state0 - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Forward function. - - step_inputs, state0, step_outputs, and state1 should each be a `.NestedMap` - of tensor values. Each tensor must be of shape [batch_size ...]. The - structure of NestedMaps are determined by the implementation. state0 and - state1 must have exactly the same structure and tensor shapes. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: External inputs returned by PrepareExternalInputs(). - step_inputs: The inputs for this time step. - padding: A 0/1 float tensor of shape [batch_size]; 1.0 means that this - batch element is empty in this step. - state0: The previous recurrent state. - - Returns: - A tuple (step_outputs, state1). - - - outputs: The outputs of this step. - - state1: The next recurrent state. - """ - raise NotImplementedError(type(self)) - - -class StatelessLayerStep(Step): - """Allows BaseLayer subclasses to be used as Steps. - - Layers used with this class should be stateless: they should not return - anything that must be passed back in the next invocation. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('layer', None, 'Params for the layer that this step wraps.') - return p - - def __init__(self, params): - super().__init__(params) - p = params - self.CreateChild('layer', p.layer) - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Perform inference on a stateless layer. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: unused. - step_inputs: A NestedMap containing 'inputs', which are passed directly to - the layer. - padding: A 0/1 float tensor of shape [batch_size]; 1.0 means that this - batch element is empty in this step. - state0: unused. - - Returns: - (output, state1), where output is the output of the layer, and - state1 is an empty NestedMap. - """ - del state0 - del prepared_inputs - args = {} - if padding is not None: - args['padding'] = padding - output = self.layer.FProp(theta.layer, step_inputs.inputs, **args) - return output, py_utils.NestedMap() - - -class StackStep(Step): - """A stack of steps. - - Each sub-step is assumed to accept step_inputs of type NestedMap(inputs=[]) - and return a primary output of type NestedMap(output=tensor). The - output of layer n-1 is sent to input of layer n. - - Per-step context vectors and per-sequence context vectors can also be - supplied; see FProp for more details. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'sub', [], 'A list of sub-stack params. Each layer is ' - 'expected to accept its input as NestedMap(inputs=[]), and ' - 'produce output as NestedMap(output=tensor). ' - 'The external_inputs parameter is passed directly to the ' - 'PrepareExternalInputs method of each sub-step. ') - p.Define( - 'residual_start', -1, 'An index of the layer where residual ' - 'connections start. Setting this parameter to a negative value turns ' - 'off residual connections.' - 'More precisely, when i >= residual_start, the output of each step ' - 'is defined as: ' - 'output[i] = output[i - residual_stride] + sub[i](output[i - 1]) ' - 'where output[-1] is the step input.') - p.Define( - 'residual_stride', 1, 'If residual connections are active, this ' - 'is the number of layers that each connection skips. For ' - 'instance, setting residual_stride = 2 means the output of layer ' - 'n is added to layer n + 2') - return p - - def __init__(self, params): - super().__init__(params) - p = params - self.sub_steps = [] - self.CreateChildren('sub', p.sub) - - def PrepareExternalInputs(self, theta, external_inputs): - """Delegates external inputs preparation to sub-layers. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - external_inputs: A `.NestedMap` object. The structure of the internal - fields is defined by the sub-steps. - - Returns: - A `.NestedMap` containing a pre-processed version of the external_inputs, - one per sub-step. - """ - packed = py_utils.NestedMap(sub=[]) - for i in range(len(self.sub)): - packed.sub.append(self.sub[i].PrepareExternalInputs( - theta.sub[i], external_inputs)) - return packed - - def ZeroState(self, theta, prepared_inputs, batch_size): - """Computes a zero state for each sub-step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: An output from PrepareExternalInputs. - batch_size: The number of items in the batch that FProp will process. - - Returns: - A `.NestedMap` containing a state0 object for each sub-step. - """ - state = py_utils.NestedMap(sub=[]) - for i in range(len(self.sub)): - state.sub.append(self.sub[i].ZeroState(theta.sub[i], prepared_inputs, - batch_size)) - return state - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Performs inference on the stack of sub-steps. - - There are three possible ways to feed input to the stack: - - * step_inputs.inputs: These tensors are fed only to the lowest layer. - * step_inputs.context: [Optional] This tensor is fed to every layer. - * prepared_inputs: [Optional] This tensor is fed to every layer and - is assumed to stay constant over all steps. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: An output from PrepareExternalInputs. - step_inputs: A `.NestedMap` containing a list called 'inputs', an - optionally a tensor called 'context'. - padding: A 0/1 float tensor of shape [batch_size]; 1.0 means that this - batch element is empty in this step. - state0: The previous recurrent state. - - Returns: - A tuple (output, state1): - - - output: A `.NestedMap` containing the output of the top-most step. - - state1: The recurrent state to feed to next invocation of this graph. - """ - state1 = py_utils.NestedMap(sub=[]) - inputs = list(step_inputs.inputs) - # We pretend that the input is the output of layer -1 for the purposes - # of residual connections. - residual_inputs = [tf.concat(inputs, axis=1)] - additional = [] - if 'context' in step_inputs: - additional.append(step_inputs.context) - for i in range(len(self.sub)): - sub_inputs = py_utils.NestedMap(inputs=inputs + additional) - sub_output, state1_i = self.sub[i].FProp(theta.sub[i], - prepared_inputs.sub[i], - sub_inputs, padding, - state0.sub[i]) - state1.sub.append(state1_i) - output = sub_output.output - if i >= self.params.residual_start >= 0: - # residual_inputs contains the step input at residual_inputs[0]. - assert i + 1 - self.params.residual_stride < len(residual_inputs) - output += residual_inputs[i + 1 - self.params.residual_stride] - residual_inputs.append(output) - inputs = [output] - return py_utils.NestedMap(output=output), state1 - - -class ParallelStep(Step): - """Runs many steps on the same input and concatenates their outputs.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'sub', [], 'A list of step params. Each step is ' - 'expected to accept its input as NestedMap(inputs=[]), and ' - 'produce output as NestedMap(output=tensor). ' - 'The external_inputs parameter is passed directly to the ' - 'PrepareExternalInputs method of each sub-step. ') - return p - - def __init__(self, params): - super().__init__(params) - p = params - self.CreateChildren('sub', p.sub) - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Performs inference on N steps at once and concatenates the result. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: An output from PrepareExternalInputs. - step_inputs: A `.NestedMap` containing a list called 'inputs'. - padding: A 0/1 float tensor of shape [batch_size]; 1.0 means that this - batch element is empty in this step. - state0: The previous recurrent state. - - Returns: - A tuple (output, state1): - - - output: A `.NestedMap` containing the output of the top-most step. - - state1: The recurrent state to feed to next invocation of this graph. - """ - state1 = py_utils.NestedMap(sub=[None] * len(self.sub)) - outputs = [None] * len(self.sub) - - for i in range(len(self.sub)): - outputs[i], state1.sub[i] = self.sub[i].FProp(theta.sub[i], - prepared_inputs.sub[i], - step_inputs, padding, - state0.sub[i]) - - output = py_utils.NestedMap(output=tf.concat(outputs, axis=1)) - return output, state1 - - -# signature: A GraphSignature string defining the input and output parameters -# of this step. For example, (inputs=[a,b])->c means that step_inputs -# should be NestedMap(inputs=[a,b]), and the output of FProp should be -# stored in c. -# external_signature: A GraphSignature string defining the input to -# PrepareExternalInputs. For example, 'external_inputs.foo' means that -# the tensor external_inputs.foo should be the 'external_inputs' parameter -# when calling PrepareExternalInputs on this sub-step. -# params: The parameters to use when constructing the sub-step. -SubStep = collections.namedtuple('SubStep', - ['signature', 'external_signature', 'params']) - - -class GraphStep(Step): - r"""A step that connects sub-steps in a simple data flow graph. - - This is an adaptation of builder_layers.GraphLayer to support steps. - - Params.sub specifies a list of Specs that define each sub-step. - - A spec contains: - - * step_inputs: The signature describing how to assemble the input and output - for this step. The input part describes the 'step_inputs' parameter, - while the output part describes the name of the output. The state0 - input and state1 output are handled automatically and should not be - specified. - * external_inputs: if this Step requires external_inputs, this - is the signature describing how to find those inputs. - This value can also be set to None. - * params: the params used to construct the sub-step. - - The format of signature strings is defined in detail in the GraphSignature - class documentation. - - All inputs to a layer must have been produced by some previous layer. No - cycles are allowed. All outputs must be uniquely named; no overwriting - of previous names is allowed. - - Example - ('(act=[layer_0.output,step_inputs.context])->layer_1', - 'external_inputs.extra', - step_params) - - This constructs the step defined by step_params. Its FProp method will be - called with {act=[layer_0.output,step_inputs.context]} as the step_inputs - parameter. Its PrepareExternalInputs method will be called with - 'external_inputs.extra' as the external_inputs parameter. The output of that - method will be passed to ZeroState and FProp. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('output_signature', '', 'Signature of the step output.') - p.Define('sub', [], 'A list of SubSteps (defined above).') - p.Define('dict_type', py_utils.NestedMap, 'Type of nested dicts.') - return p - - _seq = collections.namedtuple( - '_Seq', ['name', 'signature', 'external_signature', 'step']) - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - self._seq = [] - for i, (signature, external_signature, sub_params) in enumerate(p.sub): - assert signature - sig = builder_layers.GraphSignature(signature) - assert len(sig.inputs) == 1 - assert sig.outputs - external_sig = None - if external_signature: - external_sig = builder_layers.GraphSignature(external_signature) - assert len(external_sig.inputs) == 1 - assert not external_sig.outputs - name = sub_params.name - if not name: - name = '%s_%02d' % (sig.outputs[0], i) - sub_params.name = name - self.CreateChild(name, sub_params) - self._seq.append( - GraphStep._seq(name, sig, external_sig, self.children[name])) - self.output_signature = builder_layers.GraphSignature(p.output_signature) - - def PrepareExternalInputs(self, theta, external_inputs): - """Prepares external inputs for each sub-step. - - The external_inputs parameter of this method is processed by the - external_inputs of each sub-step, then processed by the sub-step's - PrepareExternalInputs method. - - Args: - theta: variables used by sub-steps. - external_inputs: A NestedMap of [n_batch, ...] tensors. - - Returns: - A NestedMap of prepared inputs, where the keys are the names of - each sub-step. - """ - graph_tensors = builder_layers.GraphTensors() - graph_tensors.StoreTensor('external_inputs', external_inputs) - prepared_inputs = py_utils.NestedMap() - with tf.name_scope(self.params.name): - for seq in self._seq: - if seq.external_signature: - template = py_utils.NestedMap(inputs=seq.external_signature.inputs) - packed = template.Transform(graph_tensors.GetTensor) - seq_external_inputs = packed.inputs[0] - prepared_inputs[seq.name] = seq.step.PrepareExternalInputs( - theta[seq.name], seq_external_inputs) - else: - prepared_inputs[seq.name] = py_utils.NestedMap() - return prepared_inputs - - def ZeroState(self, theta, prepared_inputs, batch_size): - """Creates a zero state NestedMap for this step. - - Args: - theta: variables used by sub-steps. - prepared_inputs: Output from a call to PrepareExternalInputs. - batch_size: The number of items in the batch that FProp will process. - - Returns: - A NestedMap of ZeroState results for each sub-step. - """ - state0 = py_utils.NestedMap() - with tf.name_scope(self.params.name): - for seq in self._seq: - state0[seq.name] = seq.step.ZeroState(theta[seq.name], - prepared_inputs[seq.name], - batch_size) - return state0 - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """A single inference step for this step graph. - - Args: - theta: variables used by sub-steps. - prepared_inputs: A NestedMap containing external_inputs that were - pre-processed by the PrepareExternalInputs method of each sub-step. The - keys are the names of the sub-steps. - step_inputs: A NestedMap of [batch, ...] tensors. The structure of this - depends on the graph implementation. - padding: A 0/1 float tensor of shape [batch_size]; 1.0 means that this - batch element is empty in this step. - state0: A NestedMap of state variables produced by either ZeroState or a - previous invocation of this FProp step. The keys are the names of the - sub-steps. - - Returns: - (output, state1), both of which are NestedMaps. - output is implementation-dependent and is defined by the output_signature - parameter. - state1 is a NestedMap where the keys are names of sub-steps and the values - are state outputs from their FProp methods. - """ - p = self.params - graph_tensors = builder_layers.GraphTensors() - graph_tensors.StoreTensor('prepared_inputs', prepared_inputs) - graph_tensors.StoreTensor('step_inputs', step_inputs) - state1 = py_utils.NestedMap() - with tf.name_scope(p.name): - for seq in self._seq: - tf.logging.vlog(1, 'GraphStep: call %s', seq.name) - external = None - if seq.external_signature: - external = prepared_inputs[seq.name] - template = py_utils.NestedMap(inputs=seq.signature.inputs) - packed = template.Transform(graph_tensors.GetTensor) - input_args = packed.inputs[0] - out, seq_state1 = seq.step.FProp(theta[seq.name], external, input_args, - padding, state0[seq.name]) - graph_tensors.StoreTensor(seq.signature.outputs[0], out) - state1[seq.name] = seq_state1 - template = py_utils.NestedMap(inputs=self.output_signature.inputs) - output_tensors = template.Transform(graph_tensors.GetTensor).inputs[0] - return output_tensors, state1 - - -class IteratorStep(Step): - """An iterator over the time dimension of some tensors. - - It's common to have a tensor of shape [batch, time, ...] or - [time, batch, ...]. This object will step through the time dimension, - producing tensors of shape [batch, ...] in succession. - - The input tensors are passed to PrepareExternalInputs. The step_inputs - argument of FProp is unused. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('axis', 1, 'The time dimension of the tensors.') - return p - - def PrepareExternalInputs(self, theta, external_inputs): - """Prepares the input for iteration. - - Args: - theta: unused. - external_inputs: A NestedMap containing tensors. The time axis of each - tensor should be params.axis. - - Returns: - A prepared NestedMap (current the same as the input). - """ - return external_inputs - - def ZeroState(self, theta, prepared_inputs, batch_size): - """Returns the initial iterator state. - - Args: - theta: unused. - prepared_inputs: Output from a call to PrepareExternalInputs. - batch_size: The number of items in the batch that FProp will process. - - Returns: - An initial state NestedMap. - """ - return py_utils.NestedMap(t=tf.constant(0, dtype=tf.int32)) - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Returns a A single inference step for this step graph. - - Args: - theta: unused. - prepared_inputs: Output from a call to PrepareExternalInputs. - step_inputs: unused. - padding: unused. - state0: A NestedMap of state variables produced by either ZeroState or a - previous invocation of this FProp step. - - Returns: - (output, state1), both of which are NestedMaps. - output is implementation-dependent and is defined by the output_signature - parameter. - state1 is a NestedMap where the keys are names of sub-steps and the values - are state outputs from their FProp methods. - """ - del theta - del step_inputs - del padding - - def _Slice(tensor): - """Return a slice of this tensor at time=state0.t.""" - shape = py_utils.GetShape(tensor) - # All zeros except for t in the time dimension. - # e.g. if params.axis=1, begin is [0, t, 0, 0, 0, ...] - begin = tf.one_hot(self.params.axis, tf.rank(tensor), on_value=state0.t) - # Same as shape, but with a 1 in the time dimension. - # e.g. if params.axis=1, shape is [shape[0], 1, shape[2], shape[3], ...] - size = tf.concat([ - shape[0:self.params.axis], - tf.constant([1], dtype=tf.int32), shape[self.params.axis + 1:] - ], - axis=0) - # Make a slice where the time dimension is fixed at state0.t. - time_slice = tf.slice(tensor, begin, size) - # Remove the time dimension. - return tf.squeeze(time_slice, axis=self.params.axis) - - output = prepared_inputs.Transform(_Slice) - state1 = py_utils.NestedMap(t=state0.t + 1) - return output, state1 - - -class RecurrentStepWrapper(base_layer.BaseLayer): - """A layer that wraps a step in a recurrent.Recurrent call.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('step', None, 'The step params that this class wraps.') - return p - - def __init__(self, params): - super().__init__(params) - self.CreateChild('step', self.params.step) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.step.InstantiateVariables() - super()._CreateChildrenVariables() - - def PrepareExternalInputs(self, theta, external_inputs): - """See Step.PrepareExternalInputs.""" - return self.step.PrepareExternalInputs(theta.step, external_inputs) - - def ZeroState(self, theta, prepared_inputs, batch_size): - """See Step.ZeroState.""" - return self.step.ZeroState(theta.step, prepared_inputs, batch_size) - - def FProp(self, theta, prepared_inputs, inputs, padding, state0, **kwargs): - """Runs a Step layer over multiple timesteps using Recurrent. - - Args: - theta: A NestedMap containing weights' values of this layer and its - children layers. - prepared_inputs: External inputs returned by Step.PrepareExternalInputs(). - inputs: A NestedMap of inputs of shape [time, batch_size, dim]. - padding: A 0/1 float tensor of shape [time, batch_size]; 1.0 means that - this batch element is empty in this step. - state0: A NestedMap containing the initial recurrent state. - **kwargs: Additional kwargs to pass to Recurrent. - - Returns: - A tuple (outputs, state1). - - - outputs: A NestedMap containing the accumulated outputs of all steps, - containing Tensors shaped [time, batch_size, dim]. - - state1: A NestedMap containing the accumulated recurrent states, - containing Tensors shaped [time, batch_size, dim]. - """ - - def RnnStep(recurrent_theta, recurrent_state0, recurrent_inputs): - """Compute a single timestep.""" - output, state1 = self.step.FProp( - theta=recurrent_theta.theta, - prepared_inputs=recurrent_theta.prepared_inputs, - step_inputs=recurrent_inputs.inputs, - padding=recurrent_inputs.padding, - state0=recurrent_state0.state) - recurrent_state1 = py_utils.NestedMap(output=output, state=state1) - return recurrent_state1, py_utils.NestedMap() - - # In order to pass Step outputs through Recurrent, they need to be - # included as part of state. - output0, _ = self.step.FProp(theta.step, prepared_inputs, - inputs.Transform(lambda x: x[0]), padding[0], - state0) - - accumulated_states, _ = recurrent.Recurrent( - theta=py_utils.NestedMap( - theta=theta.step, prepared_inputs=prepared_inputs), - state0=py_utils.NestedMap(output=output0, state=state0), - inputs=py_utils.NestedMap(inputs=inputs, padding=padding), - cell_fn=RnnStep, - **kwargs) - - return accumulated_states.output, accumulated_states.state diff --git a/lingvo/core/step_test.py b/lingvo/core/step_test.py deleted file mode 100644 index 23d80483..00000000 --- a/lingvo/core/step_test.py +++ /dev/null @@ -1,220 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -"""Tests for lingvo.core.step.""" - -from lingvo import compat as tf -from lingvo.core import builder_layers -from lingvo.core import py_utils -from lingvo.core import step -from lingvo.core import test_utils - - -class TextStep(step.Step): - """TextStep is a fake step used for testing. - - A real step would perform numeric operations on tensors. Instead, we return - text strings from these functions. This causes the result of our tests - to be long text strings that encode the sequence of operations that - were performed. - """ - - def PrepareExternalInputs(self, theta, external_inputs): - return external_inputs - - def ZeroState(self, theta, prepared_inputs, batch_size): - return self.params.name - - def FProp(self, theta, external_inputs, step_inputs, padding, state0): - return py_utils.NestedMap( - output=':'.join(step_inputs.inputs + [external_inputs]) + state0), ( - state0 + ':'.join(step_inputs.inputs)) - - -class StepTest(test_utils.TestCase): - - def testStatelessLayerStep(self): - with self.session(): - p = step.StatelessLayerStep.Params() - p.name = 'sum' - p.layer = builder_layers.FnLayer.Params().Set(fn=lambda x: x[0] + x[1]) - s = p.Instantiate() - - self.assertEqual(py_utils.NestedMap(), - s.PrepareExternalInputs(s.theta, None)) - self.assertEqual(py_utils.NestedMap(), s.ZeroState(s.theta, None, 8)) - - out, state1 = s.FProp( - s.theta, None, - py_utils.NestedMap(inputs=[ - tf.constant([4], dtype=tf.int32), - tf.constant([11], dtype=tf.int32) - ]), None, None) - self.assertEqual(py_utils.NestedMap(), state1) - value = self.evaluate(out) - self.assertEqual([15], value) - - def testGraphStep(self): - p = step.GraphStep.Params() - p.name = 'graphtest' - p.sub = [ - ('(inputs=[step_inputs.a])->output0', 'external_inputs.x', - TextStep.Params().Set(name='step0')), - ('(inputs=[output0.output])->output1', 'external_inputs.y', - TextStep.Params().Set(name='step1')), - ] - p.output_signature = 'output1' - s = p.Instantiate() - - prepared = s.PrepareExternalInputs(s.theta, - py_utils.NestedMap(x='1', y='2')) - self.assertEqual({'step0': '1', 'step1': '2'}, prepared) - - state0 = s.ZeroState(s.theta, prepared, 8) - self.assertEqual({'step0': 'step0', 'step1': 'step1'}, state0) - - output, state1 = s.FProp(s.theta, prepared, - py_utils.NestedMap(a='aa', b='bb'), - tf.constant([0.0], dtype=tf.float32), state0) - self.assertEqual({'output': 'aa:1step0:2step1'}, output) - self.assertEqual({'step1': 'step1aa:1step0', 'step0': 'step0aa'}, state1) - - def testStackStep(self): - p = step.StackStep.Params() - p.name = 'stack' - p.sub = [ - TextStep.Params().Set(name='text0'), - TextStep.Params().Set(name='text1'), - TextStep.Params().Set(name='text2'), - ] - s = p.Instantiate() - - prepared = s.PrepareExternalInputs(s.theta, 'z') - self.assertEqual({'sub': ['z', 'z', 'z']}, prepared) - state0 = s.ZeroState(s.theta, prepared, 1) - self.assertEqual({'sub': ['text0', 'text1', 'text2']}, state0) - output, state1 = s.FProp(s.theta, prepared, - py_utils.NestedMap(inputs=['in']), [0.0], state0) - # This output encodes the computations that were performed in the stack: - # input = in - # layer0: state=text0, external_inputs=z - # in -> in:ztext0 - # layer1: state=text1, external_inputs=z - # in:ztext0 -> in:ztext0:ztext1 - # layer2: state=text2, external_inputs=z - # in:ztext0:ztext1 -> in:ztext0:ztext1:ztext2 - self.assertEqual({'output': 'in:ztext0:ztext1:ztext2'}, output) - # The state1 of each sub-step is equal to its input plus its current state. - # layer0: state=text0, input=in - # layer1: state=text1, input=in:ztext0 - # layer2: state=text2, input=in:ztext0:ztext1 - self.assertEqual( - {'sub': ['text0in', 'text1in:ztext0', 'text2in:ztext0:ztext1']}, state1) - - def testStackStepWithResidualConnections(self): - p = step.StackStep.Params() - p.name = 'stack' - p.sub = [ - TextStep.Params().Set(name='text0'), - TextStep.Params().Set(name='text1'), - TextStep.Params().Set(name='text2'), - ] - p.residual_start = 1 - s = p.Instantiate() - - prepared = s.PrepareExternalInputs(s.theta, 'z') - self.assertEqual({'sub': ['z', 'z', 'z']}, prepared) - state0 = s.ZeroState(s.theta, prepared, 1) - self.assertEqual({'sub': ['text0', 'text1', 'text2']}, state0) - output, state1 = s.FProp(s.theta, prepared, - py_utils.NestedMap(inputs=['in']), [0.0], state0) - # This output encodes the computations that were performed in the stack: - # input=in - # layer0: state=text0, external_inputs=z - # in -> in:ztext0 - # layer1: state=text1, external_inputs=z - # in:ztext0 -> in:ztext0:ztext1 + in:ztext0 (residual) - # layer2: state=text2, external_inputs=z - # in:ztext0:ztext1in:ztext0 -> in:ztext0:ztext1in:ztext0:ztext2 + - # in:ztext0:ztext1in:ztext0 (residual) - self.assertEqual( - {'output': 'in:ztext0:ztext1in:ztext0:ztext2in:ztext0:ztext1in:ztext0'}, - output) - # The state1 of each sub-step is equal to its input plus its current state. - # layer0: state=text0, input=in - # layer1: state=text1, input=in:ztext0 - # layer2: state=text2, input=in:ztext0:ztext1in:ztext0 - self.assertEqual( - { - 'sub': - ['text0in', 'text1in:ztext0', 'text2in:ztext0:ztext1in:ztext0'] - }, state1) - - def testParallelStep(self): - - def PlusConstantParams(n): - p = step.StatelessLayerStep.Params() - p.name = 'sum' - p.layer = builder_layers.FnLayer.Params().Set(fn=lambda x: x + n) - return p - - with self.session(): - p = step.ParallelStep.Params() - p.name = 'concat' - p.sub = [PlusConstantParams(1), PlusConstantParams(2)] - concat = p.Instantiate() - - prepared = concat.PrepareExternalInputs(concat.theta, - py_utils.NestedMap()) - state0 = concat.ZeroState(concat.theta, prepared, 3) - step_inputs = py_utils.NestedMap( - inputs=tf.constant([[5], [10], [15]], dtype=tf.float32)) - output, _ = concat.FProp(concat.theta, prepared, step_inputs, None, - state0) - output = self.evaluate(output) - # Input is batch size 3: [[5], [10], [15]]. - # Two separate steps run on each input, +1 and +2, and the result is - # concatenated. - self.assertAllClose(output.output, [[6, 7], [11, 12], [16, 17]]) - - def testIteratorStep(self): - with self.session(): - p = step.IteratorStep.Params() - p.name = 'iterator' - iterator = p.Instantiate() - - prepared = iterator.PrepareExternalInputs( - iterator.theta, - py_utils.NestedMap( - a=tf.constant( - [[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 1], [2, 3]]], - dtype=tf.int32))) - state = iterator.ZeroState(iterator.theta, prepared, 2) - - outputs = [] - for _ in range(3): - out, state = iterator.FProp(iterator.theta, prepared, - py_utils.NestedMap(), None, state) - outputs.append(out) - outputs = self.evaluate(outputs) - self.assertAllClose([{ - 'a': [[1, 2], [7, 8]] - }, { - 'a': [[3, 4], [9, 1]] - }, { - 'a': [[5, 6], [2, 3]] - }], outputs) - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/steps/BUILD b/lingvo/core/steps/BUILD deleted file mode 100644 index 8892207b..00000000 --- a/lingvo/core/steps/BUILD +++ /dev/null @@ -1,85 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "attention_steps", - srcs = ["attention_steps.py"], - srcs_version = "PY3", - deps = [ - ":rnn_steps", - "//lingvo:compat", - "//lingvo/core:attention", - "//lingvo/core:py_utils", - "//lingvo/core:step", - ], -) - -py_test( - name = "attention_steps_test", - size = "small", - srcs = ["attention_steps_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":attention_steps", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "embedding_steps", - srcs = ["embedding_steps.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:layers", - "//lingvo/core:py_utils", - "//lingvo/core:step", - ], -) - -py_test( - name = "embedding_steps_test", - size = "small", - srcs = ["embedding_steps_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":embedding_steps", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "rnn_steps", - srcs = ["rnn_steps.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:rnn_cell", - "//lingvo/core:step", - ], -) - -py_test( - name = "rnn_steps_test", - size = "small", - srcs = ["rnn_steps_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":rnn_steps", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:step", - "//lingvo/core:test_utils", - ], -) diff --git a/lingvo/core/steps/__init__.py b/lingvo/core/steps/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/core/steps/attention_steps.py b/lingvo/core/steps/attention_steps.py deleted file mode 100644 index 5d81717a..00000000 --- a/lingvo/core/steps/attention_steps.py +++ /dev/null @@ -1,277 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -"""Steps for attention computation.""" - -from lingvo import compat as tf -from lingvo.core import attention -from lingvo.core import py_utils -from lingvo.core import step -from lingvo.core.steps import rnn_steps - - -class AttentionStep(step.Step): - """AttentionStep wraps an attention layer in the Step interface. - - An attention algorithm outputs a targeted summary of a set of input vectors. - - At each step, the query vector (input to FProp as step_inputs.input) - describes what data should be returned. The attention algorithm compares the - query vector with the source vectors (external_inputs.src) to compute - weights (attention_probs). - - The result is the weighted sum (using attention_probs) of a set of vectors. - By default that set of vectors is also external_inputs.src, but it can - optionally be external_inputs.context if a context tensor is specified. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'attention_step' - p.Define('atten', attention.AdditiveAttention.Params(), - 'Params of a subclass of BaseAttentionLayer.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self.CreateChild('atten', p.atten) - - def PrepareExternalInputs(self, theta, external_inputs): - """Prepare encoded source data for processing. - - In some attention algorithms, this step will pre-process the source - feature data so that FProp runs faster. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - external_inputs: A NestedMap containing tensors: - - - src: a [time, batch, depth] tensor that forms the input to the - attention layer. - - padding: a [time, batch] 0/1 tensor indicating which parts of - src contain useful information. - - context: Optional. See the class documentation for more details. - - Returns: - state0, a state parameter to pass to FProp on its first invocation. - """ - packed_inputs = external_inputs.DeepCopy() - if 'context' in external_inputs: - del packed_inputs['context'] - context = external_inputs.context - else: - context = external_inputs.src - packed_inputs.packed_src = self.atten.InitForSourcePacked( - theta.atten, external_inputs.src, context, external_inputs.padding) - return packed_inputs - - def _GetMaxSeqLength(self, src_encs): - """Compute the maximum sequence length of the encoded source sequence. - - Args: - src_encs: Encoded source sequence pre-processed by using - PrepareExternalInputs. It can be either a [time, batch, depth] tensor - (when there is only one source) or a NestedMap of [time, batch, depth] - tensors (when there are more than one source). - - Returns: - max_seq_length: the maximum sequence length of the encoded source - sequence. It can be either a scalar (when there is only one source) or - a NestedMap of scalars (when there are more than one source). - """ - # TODO(shaojinding): Create a MultiSourceAttentionStep class for the - # scenarios when there are multiple sources to avoid the use of if/else. - if isinstance(src_encs, py_utils.NestedMap): - max_seq_length = py_utils.NestedMap() - for key in src_encs: - max_seq_length[key] = py_utils.GetShape(src_encs[key], 3)[0] - else: - max_seq_length = py_utils.GetShape(src_encs, 3)[0] - return max_seq_length - - def ZeroState(self, theta, prepared_inputs, batch_size): - """Produce a zero state for this step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: A set of inputs pre-processed by using - PrepareExternalInputs. - batch_size: Number of elements in the batched input. - - Returns: - state0, a state parameter to pass to FProp on its first invocation. - """ - max_seq_length = self._GetMaxSeqLength(prepared_inputs.src) - atten_state = self.atten.ZeroAttentionState(max_seq_length, batch_size) - (new_atten_context, _, - new_atten_states) = self.atten.ComputeContextVectorWithSource( - theta.atten, - prepared_inputs.packed_src, - tf.zeros([batch_size, self.params.atten.query_dim], - dtype=py_utils.FPropDtype(self.params)), - attention_state=atten_state) - return py_utils.NestedMap( - atten_context=new_atten_context, atten_state=new_atten_states) - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Produces a context vector from the attention algorithm. - - The context vector is a summary of the inputs from external_inputs - which the attention algorithm has determined would be useful for decoding - the next output. - - Args: - theta: A NestedMap containing weights' values of this layer and its - children layers. - prepared_inputs: A set of encoded tensors that have been pre-processed by - PrepareExternalInputs. - step_inputs: A NestedMap containing an 'inputs' tensor with the query - vector to use. - padding: A [batch, 1] 0/1 float tensor, where 1.0 means that this batch - slot is not used. - state0: A NestedMap of state, either produced by ZeroState or a previous - invocation of this graph. - - Returns: - output, state1, defined as follows: - - output: a NestedMap containing a query tensor, a context tensor, and - cum_atten_probs, the log of attention probabilities for each input - vector. - - state1: a NestedMap of state to be used in subsequent invocations of - this graph. - """ - (new_atten_context, new_atten_probs, - new_atten_states) = self.atten.ComputeContextVectorWithSource( - theta.atten, - prepared_inputs.packed_src, - tf.concat(step_inputs.inputs, axis=1), - attention_state=state0.atten_state) - new_atten_probs = py_utils.ApplyPadding(padding, new_atten_probs) - output = py_utils.NestedMap( - context=new_atten_context, probs=new_atten_probs) - state1 = py_utils.NestedMap( - atten_context=new_atten_context, atten_state=new_atten_states) - return output, state1 - - -class AttentionBlockStep(step.Step): - """Computes attention queries and context vectors. - - An attention algorithm produces a summary of a set of input vectors. - A query vector is used as an input to the process; we can think of this as - describing what information we're hoping to retrieve in the summary. - The summary output is called a context vector. - - This class uses attention as a way to view the input to a decoder. - In each step, we hope to read a summary of the encoded input that would be - most useful for generating the next output from the decoder. - - To do this, we combine a query generator and an attention algorithm. - The query generator's job is to build a query vector that represents the - current decoder state. The attention algorithm then uses that query vector - as a key to decide which encoded inputs are most important, and it combines - those into a context vector. - - The query generator takes two inputs: one is the context vector output by - the attention algorithm in the previous step, and the other is the label - output by the decoder in the previous step. It combines this information - in an implementation-dependent way to produce a query vector. - - In previous implementations, the query generator was just the first layer - of a stack of RNN layers (rnn_cell[0]). In this implementation, the query - generator can be any suitable Step, but in practice is one or more RNN layers. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'attention_block' - p.Define('query_generator', rnn_steps.RnnStackStep.Params(), - 'Query generator params.') - p.Define( - 'attention', AttentionStep.Params(), - 'Attention params. This can be either an AttentionStep.Params()' - 'or a list of them.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self.CreateChild('query_generator', p.query_generator) - self.CreateChild('attention', p.attention) - - def ZeroState(self, theta, prepared_inputs, batch_size): - """Produce a zero state for this step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: A set of inputs pre-processed by using - PrepareExternalInputs. - batch_size: Number of elements in the batched input. - - Returns: - state0, a state parameter to pass to FProp on its first invocation. - """ - query_state0 = self.query_generator.ZeroState( - theta.query_generator, prepared_inputs.query_generator, batch_size) - atten_state0 = self.attention.ZeroState(theta.attention, - prepared_inputs.attention, - batch_size) - state0 = py_utils.NestedMap( - query_state=query_state0, atten_state=atten_state0) - return state0 - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Produces a query vector and a context vector for the next decoder step. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: A set of encoded tensors that have been pre-processed by - PrepareExternalInputs. - step_inputs: Unused. All of the input for this step comes from - external_inputs and previous step state. - padding: A [batch, 1] 0/1 float tensor, where 1.0 means that this batch - slot is not used. - state0: A NestedMap of state, either produced by ZeroState or a previous - invocation of this graph. - - Returns: - output, state1, are defined as follows. - output, a NestedMap containing an atten_query tensor, - an atten_context tensor, and atten_probs, attention probabilities for each - input vector. - state1, a NestedMap of state to be used in subsequent invocations of this - graph. - """ - query_output, query_state1 = self.query_generator.FProp( - theta.query_generator, prepared_inputs.query_generator, - py_utils.NestedMap(inputs=[state0.atten_state.atten_context]), padding, - state0.query_state) - atten_input = py_utils.NestedMap(inputs=[query_output.output]) - atten_output, atten_state1 = self.attention.FProp(theta.attention, - prepared_inputs.attention, - atten_input, padding, - state0.atten_state) - state1 = py_utils.NestedMap( - atten_state=atten_state1, query_state=query_state1) - return py_utils.NestedMap( - atten_context=atten_output.context, - atten_query=query_output.output, - atten_probs=atten_output.probs), state1 diff --git a/lingvo/core/steps/attention_steps_test.py b/lingvo/core/steps/attention_steps_test.py deleted file mode 100644 index ff9b547b..00000000 --- a/lingvo/core/steps/attention_steps_test.py +++ /dev/null @@ -1,581 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -"""Tests for third_party.py.lingvo.core.steps.attention_steps.""" - -from lingvo import compat as tf -from lingvo.core import attention -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core.steps import attention_steps -import numpy as np - - -class AttentionStepsTest(test_utils.TestCase): - - def testAttentionStep(self): - with self.session(use_gpu=False): - np.random.seed(12345) - src_batch_size = 3 - target_batch_size = 6 - src_length = 5 - src_context_dim = 4 - query_dim = 5 - src_dim = 4 - source_vecs = tf.constant( - np.random.rand(src_length, src_batch_size, src_dim), dtype=tf.float32) - source_contexts = tf.constant( - np.random.rand(src_length, src_batch_size, src_context_dim), - dtype=tf.float32) - source_padding = tf.zeros([src_length, target_batch_size], - dtype=tf.float32) - query_vec = tf.constant( - np.random.rand(target_batch_size, query_dim), dtype=tf.float32) - - p = attention_steps.AttentionStep.Params() - p.atten.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - p.atten.source_dim = src_dim - p.atten.query_dim = query_dim - p.atten.hidden_dim = query_dim - p.atten.vn.global_vn = False - p.atten.vn.per_step_vn = False - p.atten.packed_input = True - step = p.Instantiate() - - external_inputs = py_utils.NestedMap( - src=source_vecs, - context=source_contexts, - padding=source_padding) - packed = step.PrepareExternalInputs(step.theta, external_inputs) - state0 = step.ZeroState(step.theta, packed, target_batch_size) - - step_inputs = py_utils.NestedMap(inputs=[query_vec]) - step_padding = tf.zeros([target_batch_size, 1], dtype=tf.float32) - output, state1 = step.FProp(step.theta, packed, step_inputs, step_padding, - state0) - - self.evaluate(tf.global_variables_initializer()) - output, state1 = self.evaluate([output, state1]) - - self.assertAllClose( - output, { - 'context': [[0.41788787, 0.5865286, 0.58267754, 0.21218117], - [0.42178467, 0.5067202, 0.5413259, 0.6616881], - [0.71586907, 0.6303425, 0.52290946, 0.694283], - [0.41789612, 0.58647645, 0.5826333, 0.21220288], - [0.421697, 0.5068262, 0.5411844, 0.66167986], - [0.7156511, 0.63033843, 0.5228955, 0.69437]], - 'probs': - [[0.20118009, 0.19332525, 0.20120151, 0.2022583, 0.20203482], - [0.20019522, 0.20133461, 0.19572362, 0.2025276, 0.2002189], - [0.20116101, 0.20004824, 0.20221081, 0.19645905, 0.20012087], - [0.20123273, 0.19319996, 0.20131132, 0.20220752, 0.2020485], - [0.2002011, 0.2015253, 0.19534773, 0.20260131, 0.20032457], - [0.20097165, 0.19993119, 0.20225787, 0.19671878, 0.20012051]] - }) - self.assertAllClose( - state1, { - 'atten_state': [[0.], [0.], [0.], [0.], [0.], [0.]], - 'atten_context': [[0.41788787, 0.5865286, 0.58267754, 0.21218117], - [0.42178467, 0.5067202, 0.5413259, 0.6616881], - [0.71586907, 0.6303425, 0.52290946, 0.694283], - [0.41789612, 0.58647645, 0.5826333, 0.21220288], - [0.421697, 0.5068262, 0.5411844, 0.66167986], - [0.7156511, 0.63033843, 0.5228955, 0.69437]] - }) - - def testAttentionStepMultiSourceSame(self): - with self.session(use_gpu=False): - np.random.seed(12345) - src_batch_size = 3 - target_batch_size = 6 - src_length = 5 - query_dim = 5 - src_dim = 4 - source_vecs_0 = tf.constant( - np.random.rand(src_length, src_batch_size, src_dim), dtype=tf.float32) - source_vecs_1 = tf.constant( - np.random.rand(src_length, src_batch_size, src_dim), dtype=tf.float32) - sources = py_utils.NestedMap( - source_0=source_vecs_0, source_1=source_vecs_1) - - source_padding_0 = tf.zeros([src_length, src_batch_size], - dtype=tf.float32) - source_padding_1 = tf.zeros([src_length, src_batch_size], - dtype=tf.float32) - source_paddings = py_utils.NestedMap( - source_0=source_padding_0, source_1=source_padding_1) - query_vec = tf.constant( - np.random.rand(target_batch_size, query_dim), dtype=tf.float32) - - p = attention_steps.AttentionStep.Params() - - # Setup MultiSourceAttention - p.atten = attention.MultiSourceAttention.Params() - p.atten.source_dim = src_dim - p.atten.query_dim = query_dim - - add_atten_params = attention.AdditiveAttention.Params() - add_atten_params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - add_atten_params.source_dim = src_dim - add_atten_params.query_dim = query_dim - add_atten_params.hidden_dim = query_dim - add_atten_params.vn.global_vn = False - add_atten_params.vn.per_step_vn = False - add_atten_params.packed_input = True - - p.atten.source_atten_tpls = [('source_0', add_atten_params), - ('source_1', add_atten_params)] - - step = p.Instantiate() - - external_inputs = py_utils.NestedMap(src=sources, padding=source_paddings) - packed = step.PrepareExternalInputs(step.theta, external_inputs) - state0 = step.ZeroState(step.theta, packed, target_batch_size) - - step_inputs = py_utils.NestedMap(inputs=[query_vec]) - step_padding = tf.zeros([target_batch_size, 1], dtype=tf.float32) - output, state1 = step.FProp(step.theta, packed, step_inputs, step_padding, - state0) - - self.evaluate(tf.global_variables_initializer()) - output, state1 = self.evaluate([output, state1]) - - self.assertAllClose( - output, { - 'context': [[0.9590156, 0.8653384, 1.1668519, 0.697219], - [1.175648, 1.1199431, 1.2219069, 1.1452408], - [1.3191833, 1.0350775, 1.1315871, 1.3297331], - [0.95910096, 0.86546516, 1.1669571, 0.6971649], - [1.175647, 1.1201943, 1.222264, 1.1451368], - [1.3188481, 1.034915, 1.1314276, 1.3297772]], - 'probs': - [[0.20118009, 0.19332525, 0.20120151, 0.2022583, 0.20203482], - [0.20019522, 0.20133461, 0.19572362, 0.2025276, 0.2002189], - [0.20116101, 0.20004824, 0.20221081, 0.19645905, 0.20012087], - [0.20123273, 0.19319996, 0.20131132, 0.20220752, 0.2020485], - [0.2002011, 0.2015253, 0.19534773, 0.20260131, 0.20032457], - [0.20097165, 0.19993119, 0.20225787, 0.19671878, 0.20012051]] - }) - self.assertAllClose( - state1, { - 'atten_state': { - 'source_0': [[0.], [0.], [0.], [0.], [0.], [0.]], - 'source_1': [[0.], [0.], [0.], [0.], [0.], [0.]] - }, - 'atten_context': [[0.9590156, 0.8653384, 1.1668519, 0.697219], - [1.175648, 1.1199431, 1.2219069, 1.1452408], - [1.3191833, 1.0350775, 1.1315871, 1.3297331], - [0.95910096, 0.86546516, 1.1669571, 0.6971649], - [1.175647, 1.1201943, 1.222264, 1.1451368], - [1.3188481, 1.034915, 1.1314276, 1.3297772]] - }) - - def testAttentionStepMultiSourceSameWithGmmAttention(self): - with self.session(use_gpu=False): - np.random.seed(12345) - src_batch_size = 3 - target_batch_size = 6 - src_length = 5 - query_dim = 5 - src_dim = 4 - source_vecs_0 = tf.constant( - np.random.rand(src_length, src_batch_size, src_dim), dtype=tf.float32) - source_vecs_1 = tf.constant( - np.random.rand(src_length, src_batch_size, src_dim), dtype=tf.float32) - sources = py_utils.NestedMap( - source_0=source_vecs_0, source_1=source_vecs_1) - - source_padding_0 = tf.zeros([src_length, src_batch_size], - dtype=tf.float32) - source_padding_1 = tf.zeros([src_length, src_batch_size], - dtype=tf.float32) - source_paddings = py_utils.NestedMap( - source_0=source_padding_0, source_1=source_padding_1) - query_vec = tf.constant( - np.random.rand(target_batch_size, query_dim), dtype=tf.float32) - - p = attention_steps.AttentionStep.Params() - - # Setup MultiSourceAttention - p.atten = attention.MultiSourceAttention.Params() - p.atten.source_dim = src_dim - p.atten.query_dim = query_dim - - gmm_atten_params = attention.GmmMonotonicAttention.Params() - gmm_atten_params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - gmm_atten_params.source_dim = src_dim - gmm_atten_params.query_dim = query_dim - gmm_atten_params.hidden_dim = query_dim - gmm_atten_params.vn.global_vn = False - gmm_atten_params.vn.per_step_vn = False - gmm_atten_params.packed_input = True - - p.atten.source_atten_tpls = [('source_0', gmm_atten_params), - ('source_1', gmm_atten_params)] - - step = p.Instantiate() - - external_inputs = py_utils.NestedMap(src=sources, padding=source_paddings) - packed = step.PrepareExternalInputs(step.theta, external_inputs) - state0 = step.ZeroState(step.theta, packed, target_batch_size) - - step_inputs = py_utils.NestedMap(inputs=[query_vec]) - step_padding = tf.zeros([target_batch_size, 1], dtype=tf.float32) - output, state1 = step.FProp(step.theta, packed, step_inputs, step_padding, - state0) - - self.evaluate(tf.global_variables_initializer()) - output, state1 = self.evaluate([output, state1]) - - self.assertAllClose( - output, { - 'context': [[0.8048796, 0.9554154, 1.2422264, 0.82598877], - [1.1976988, 0.9226365, 1.1311831, 1.1287751], - [1.2583418, 0.96984935, 0.8972859, 1.2939383], - [0.8055052, 0.9545301, 1.2421954, 0.824931], - [1.1980952, 0.9227077, 1.1313919, 1.13009], - [1.2582378, 0.96980226, 0.8973369, 1.2938937]], - 'probs': - [[0.05302628, 0.20965888, 0.3661108, 0.26998273, 0.08293614], - [0.05321905, 0.20958655, 0.36570197, 0.270003, 0.08308904], - [0.05327733, 0.20919749, 0.36514452, 0.27033207, 0.08349889], - [0.05328987, 0.20906723, 0.3648241, 0.27042356, 0.08376145], - [0.05301215, 0.21013679, 0.36650375, 0.26960865, 0.08261178], - [0.05328071, 0.20917267, 0.36505368, 0.27032903, 0.08357814]] - }) - self.assertAllClose( - state1, { - 'atten_state': { - 'source_0': [[[2.4243412, 1.2218076, 1.0122609, 0.18427502], - [1.9546769, 0.9721461, 1.0469768, 0.19244196], - [1.7934805, 0.8947478, 1.2158467, 0.18101364], - [2.2727895, 1.1433213, 0.969053, 0.21098366], - [2.1986299, 1.0997422, 1.3713341, 0.23128569]], - [[2.4298353, 1.227302, 1.0116383, 0.18391277], - [1.9476058, 0.96507514, 1.0462759, 0.19275317], - [1.793545, 0.89481235, 1.220826, 0.1817861], - [2.2800756, 1.1506072, 0.96794796, 0.21093304], - [2.194984, 1.0960963, 1.3741415, 0.23061496]], - [[2.4273272, 1.2247936, 1.0106387, 0.18302175], - [1.9522938, 0.96976304, 1.0510013, 0.19241981], - [1.7976122, 0.8988795, 1.2246737, 0.18208173], - [2.2875524, 1.1580843, 0.97309643, 0.21170339], - [2.1904838, 1.0915961, 1.3786552, 0.23077331]], - [[2.4339817, 1.2314482, 1.0118915, 0.18239658], - [1.9538436, 0.9713129, 1.050209, 0.19243228], - [1.7997689, 0.90103614, 1.2248727, 0.18208562], - [2.286818, 1.15735, 0.9776513, 0.2125737], - [2.1872034, 1.0883157, 1.3807379, 0.23051178]], - [[2.4258854, 1.223352, 1.0136935, 0.18414007], - [1.9573982, 0.9748675, 1.0445031, 0.19239089], - [1.7965381, 0.89780533, 1.2112961, 0.18159895], - [2.2637806, 1.1343125, 0.9743988, 0.21178932], - [2.1948628, 1.0959752, 1.366173, 0.23008086]], - [[2.435421, 1.2328876, 1.0118036, 0.18307444], - [1.9479709, 0.96544015, 1.0476727, 0.19277772], - [1.795729, 0.8969963, 1.224472, 0.18180896], - [2.2865427, 1.1570745, 0.9713619, 0.211611], - [2.1911612, 1.0922736, 1.3791639, 0.2307278]]], - 'source_1': [[[2.4243412, 1.2218076, 1.0122609, 0.18427502], - [1.9546769, 0.9721461, 1.0469768, 0.19244196], - [1.7934805, 0.8947478, 1.2158467, 0.18101364], - [2.2727895, 1.1433213, 0.969053, 0.21098366], - [2.1986299, 1.0997422, 1.3713341, 0.23128569]], - [[2.4298353, 1.227302, 1.0116383, 0.18391277], - [1.9476058, 0.96507514, 1.0462759, 0.19275317], - [1.793545, 0.89481235, 1.220826, 0.1817861], - [2.2800756, 1.1506072, 0.96794796, 0.21093304], - [2.194984, 1.0960963, 1.3741415, 0.23061496]], - [[2.4273272, 1.2247936, 1.0106387, 0.18302175], - [1.9522938, 0.96976304, 1.0510013, 0.19241981], - [1.7976122, 0.8988795, 1.2246737, 0.18208173], - [2.2875524, 1.1580843, 0.97309643, 0.21170339], - [2.1904838, 1.0915961, 1.3786552, 0.23077331]], - [[2.4339817, 1.2314482, 1.0118915, 0.18239658], - [1.9538436, 0.9713129, 1.050209, 0.19243228], - [1.7997689, 0.90103614, 1.2248727, 0.18208562], - [2.286818, 1.15735, 0.9776513, 0.2125737], - [2.1872034, 1.0883157, 1.3807379, 0.23051178]], - [[2.4258854, 1.223352, 1.0136935, 0.18414007], - [1.9573982, 0.9748675, 1.0445031, 0.19239089], - [1.7965381, 0.89780533, 1.2112961, 0.18159895], - [2.2637806, 1.1343125, 0.9743988, 0.21178932], - [2.1948628, 1.0959752, 1.366173, 0.23008086]], - [[2.435421, 1.2328876, 1.0118036, 0.18307444], - [1.9479709, 0.96544015, 1.0476727, 0.19277772], - [1.795729, 0.8969963, 1.224472, 0.18180896], - [2.2865427, 1.1570745, 0.9713619, 0.211611], - [2.1911612, 1.0922736, 1.3791639, 0.2307278]]] - }, - 'atten_context': [[0.8048796, 0.9554154, 1.2422264, 0.82598877], - [1.1976988, 0.9226365, 1.1311831, 1.1287751], - [1.2583418, 0.96984935, 0.8972859, 1.2939383], - [0.8055052, 0.9545301, 1.2421954, 0.824931], - [1.1980952, 0.9227077, 1.1313919, 1.13009], - [1.2582378, 0.96980226, 0.8973369, 1.2938937]] - }) - - def testAttentionStepMultiSourceDifferent(self): - with self.session(use_gpu=False): - np.random.seed(12345) - src_batch_size = 3 - target_batch_size = 6 - src_length = 5 - query_dim = 5 - src_dim = 4 - source_vecs_0 = tf.constant( - np.random.rand(src_length, src_batch_size, src_dim), dtype=tf.float32) - source_vecs_1 = tf.constant( - np.random.rand(src_length, src_batch_size, src_dim), dtype=tf.float32) - sources = py_utils.NestedMap( - source_0=source_vecs_0, source_1=source_vecs_1) - - source_padding_0 = tf.zeros([src_length, src_batch_size], - dtype=tf.float32) - source_padding_1 = tf.zeros([src_length, src_batch_size], - dtype=tf.float32) - source_paddings = py_utils.NestedMap( - source_0=source_padding_0, source_1=source_padding_1) - query_vec = tf.constant( - np.random.rand(target_batch_size, query_dim), dtype=tf.float32) - - p = attention_steps.AttentionStep.Params() - - # Setup MultiSourceAttention - p.atten = attention.MultiSourceAttention.Params() - p.atten.source_dim = src_dim - p.atten.query_dim = query_dim - - add_atten_params = attention.AdditiveAttention.Params() - add_atten_params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - add_atten_params.source_dim = src_dim - add_atten_params.query_dim = query_dim - add_atten_params.hidden_dim = query_dim - add_atten_params.vn.global_vn = False - add_atten_params.vn.per_step_vn = False - add_atten_params.packed_input = True - - gmm_atten_params = attention.GmmMonotonicAttention.Params() - gmm_atten_params.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - gmm_atten_params.source_dim = src_dim - gmm_atten_params.query_dim = query_dim - gmm_atten_params.hidden_dim = query_dim - gmm_atten_params.vn.global_vn = False - gmm_atten_params.vn.per_step_vn = False - gmm_atten_params.packed_input = True - - p.atten.source_atten_tpls = [('source_0', add_atten_params), - ('source_1', gmm_atten_params)] - - step = p.Instantiate() - - external_inputs = py_utils.NestedMap(src=sources, padding=source_paddings) - packed = step.PrepareExternalInputs(step.theta, external_inputs) - state0 = step.ZeroState(step.theta, packed, target_batch_size) - - step_inputs = py_utils.NestedMap(inputs=[query_vec]) - step_padding = tf.zeros([target_batch_size, 1], dtype=tf.float32) - output, state1 = step.FProp(step.theta, packed, step_inputs, step_padding, - state0) - - self.evaluate(tf.global_variables_initializer()) - output, state1 = self.evaluate([output, state1]) - - self.assertAllClose( - output, { - 'context': [[0.9140804, 0.8979037, 1.1033492, 0.70460725], - [1.1748682, 1.0488822, 1.2771418, 1.0938747], - [1.2568944, 1.0808113, 0.9878455, 1.4196949], - [0.9142588, 0.8978502, 1.1039352, 0.7042637], - [1.174994, 1.0493405, 1.2779118, 1.0942582], - [1.2567302, 1.0806134, 0.98783255, 1.4195559]], - 'probs': - [[0.20118009, 0.19332525, 0.20120151, 0.2022583, 0.20203482], - [0.20019522, 0.20133461, 0.19572362, 0.2025276, 0.2002189], - [0.20116101, 0.20004824, 0.20221081, 0.19645905, 0.20012087], - [0.20123273, 0.19319996, 0.20131132, 0.20220752, 0.2020485], - [0.2002011, 0.2015253, 0.19534773, 0.20260131, 0.20032457], - [0.20097165, 0.19993119, 0.20225787, 0.19671878, 0.20012051]] - }) - self.assertAllClose( - state1, { - 'atten_state': { - 'source_0': [[0.], [0.], [0.], [0.], [0.], [0.]], - 'source_1': [[[2.4243412, 1.2218076, 1.0122609, 0.18427502], - [1.9546769, 0.9721461, 1.0469768, 0.19244196], - [1.7934805, 0.8947478, 1.2158467, 0.18101364], - [2.2727895, 1.1433213, 0.969053, 0.21098366], - [2.1986299, 1.0997422, 1.3713341, 0.23128569]], - [[2.4298353, 1.227302, 1.0116383, 0.18391277], - [1.9476058, 0.96507514, 1.0462759, 0.19275317], - [1.793545, 0.89481235, 1.220826, 0.1817861], - [2.2800756, 1.1506072, 0.96794796, 0.21093304], - [2.194984, 1.0960963, 1.3741415, 0.23061496]], - [[2.4273272, 1.2247936, 1.0106387, 0.18302175], - [1.9522938, 0.96976304, 1.0510013, 0.19241981], - [1.7976122, 0.8988795, 1.2246737, 0.18208173], - [2.2875524, 1.1580843, 0.97309643, 0.21170339], - [2.1904838, 1.0915961, 1.3786552, 0.23077331]], - [[2.4339817, 1.2314482, 1.0118915, 0.18239658], - [1.9538436, 0.9713129, 1.050209, 0.19243228], - [1.7997689, 0.90103614, 1.2248727, 0.18208562], - [2.286818, 1.15735, 0.9776513, 0.2125737], - [2.1872034, 1.0883157, 1.3807379, 0.23051178]], - [[2.4258854, 1.223352, 1.0136935, 0.18414007], - [1.9573982, 0.9748675, 1.0445031, 0.19239089], - [1.7965381, 0.89780533, 1.2112961, 0.18159895], - [2.2637806, 1.1343125, 0.9743988, 0.21178932], - [2.1948628, 1.0959752, 1.366173, 0.23008086]], - [[2.435421, 1.2328876, 1.0118036, 0.18307444], - [1.9479709, 0.96544015, 1.0476727, 0.19277772], - [1.795729, 0.8969963, 1.224472, 0.18180896], - [2.2865427, 1.1570745, 0.9713619, 0.211611], - [2.1911612, 1.0922736, 1.3791639, 0.2307278]]] - }, - 'atten_context': [[0.9140804, 0.8979037, 1.1033492, 0.70460725], - [1.1748682, 1.0488822, 1.2771418, 1.0938747], - [1.2568944, 1.0808113, 0.9878455, 1.4196949], - [0.9142588, 0.8978502, 1.1039352, 0.7042637], - [1.174994, 1.0493405, 1.2779118, 1.0942582], - [1.2567302, 1.0806134, 0.98783255, 1.419555]] - }) - - def testAttentionBlockStep(self): - with self.session(use_gpu=False): - np.random.seed(12345) - src_batch_size = 3 - target_batch_size = 6 - src_length = 5 - query_dim = 5 - context_dim = 8 - hidden_dim = 7 - src_dim = context_dim - source_vecs = tf.constant( - np.random.rand(src_length, src_batch_size, src_dim), dtype=tf.float32) - source_padding = tf.zeros([src_length, target_batch_size], - dtype=tf.float32) - - p = attention_steps.AttentionBlockStep.Params() - p.attention.atten.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - p.attention.atten.source_dim = src_dim - p.attention.atten.query_dim = query_dim - p.attention.atten.hidden_dim = hidden_dim - p.attention.atten.vn.global_vn = False - p.attention.atten.vn.per_step_vn = False - p.attention.atten.packed_input = True - p.query_generator.step_input_dim = context_dim - p.query_generator.rnn_cell_dim = query_dim - step = p.Instantiate() - - external_inputs = py_utils.NestedMap( - attention=py_utils.NestedMap(src=source_vecs, padding=source_padding)) - packed = step.PrepareExternalInputs(step.theta, external_inputs) - state0 = step.ZeroState(step.theta, packed, target_batch_size) - - step_padding = tf.zeros([target_batch_size, 1], dtype=tf.float32) - output, state1 = step.FProp(step.theta, packed, None, step_padding, - state0) - - self.evaluate(tf.global_variables_initializer()) - output, state1 = self.evaluate([output, state1]) - - self.assertAllClose( - output, { - 'atten_query': - np.array([ - [ - 0.1142175, 0.00020437, 0.02718649, -0.06030316, - 0.02916641 - ], - [ - 0.09362462, 0.07093287, 0.10184045, -0.0228882, - 0.06189567 - ], - [ - 0.12866478, 0.0121689, 0.05557573, -0.04107622, - 0.0543875 - ], - [ - 0.1142175, 0.00020437, 0.02718649, -0.06030316, - 0.02916641 - ], - [ - 0.09362462, 0.07093287, 0.10184045, -0.0228882, - 0.06189567 - ], - [ - 0.12866478, 0.0121689, 0.05557573, -0.04107622, - 0.0543875 - ], - ]), - 'atten_context': - np.array([ - [ - 0.55453926, 0.55162865, 0.62239933, 0.26001987, - 0.51269007, 0.555924, 0.54857075, 0.51340824 - ], - [ - 0.6495046, 0.42096642, 0.605386, 0.79519784, - 0.39852753, 0.30938083, 0.53797, 0.43651274 - ], - [ - 0.66645885, 0.56522155, 0.67393464, 0.6224826, - 0.66094846, 0.6098963, 0.52270895, 0.5319694 - ], - [ - 0.55453926, 0.55162865, 0.62239933, 0.26001987, - 0.51269007, 0.555924, 0.54857075, 0.51340824 - ], - [ - 0.6495046, 0.42096642, 0.605386, 0.79519784, - 0.39852753, 0.30938083, 0.53797, 0.43651274 - ], - [ - 0.66645885, 0.56522155, 0.67393464, 0.6224826, - 0.66094846, 0.6098963, 0.52270895, 0.5319694 - ], - ]), - 'atten_probs': - np.array([ - [ - 0.20132412, 0.19545832, 0.20277032, 0.19362292, - 0.20682438 - ], - [ - 0.20172212, 0.20001633, 0.20166671, 0.20218876, - 0.19440602 - ], - [ - 0.20540778, 0.20792785, 0.19377577, 0.19288684, - 0.20000176 - ], - [ - 0.20132412, 0.19545832, 0.20277032, 0.19362292, - 0.20682438 - ], - [ - 0.20172212, 0.20001633, 0.20166671, 0.20218876, - 0.19440602 - ], - [ - 0.20540778, 0.20792785, 0.19377577, 0.19288684, - 0.20000176 - ], - ]) - }) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/steps/embedding_steps.py b/lingvo/core/steps/embedding_steps.py deleted file mode 100644 index 1d122449..00000000 --- a/lingvo/core/steps/embedding_steps.py +++ /dev/null @@ -1,65 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -"""Step classes for embedding tables.""" - -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import step - - -class EmbeddingStep(step.Step): - """A simple wrapper around EmbeddingLayer and its subclasses. - - This class can be used to insert an embedding lookup at the input side - of a GraphStep or StackStep. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'emb_step' - p.Define('emb', layers.EmbeddingLayer.Params(), 'Embedding layer params.') - return p - - def __init__(self, params): - super().__init__(params) - p = params - self.CreateChild('emb', p.emb) - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Looks up a list of embeddings from an EmbeddingLayer. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - prepared_inputs: unused. - step_inputs: A NestedMap containing a list called inputs. This list should - contain a single integer tensor of shape [batch], where each integer - represents an index into the embedding table. (By convention, all Steps - that can be used with StackStep must store inputs in - step_inputs.inputs[], but in this step it does not make sense for that - list to have more than one tensor in it). - padding: unused. - state0: unused. - - Returns: - A params.dtype tensor of shape [batch, embedding_dim]. - """ - del prepared_inputs - del state0 - assert len(step_inputs.inputs) == 1 - - output = self.emb.EmbLookup(theta.emb, step_inputs.inputs[0]) - return py_utils.NestedMap(output=output), py_utils.NestedMap() diff --git a/lingvo/core/steps/embedding_steps_test.py b/lingvo/core/steps/embedding_steps_test.py deleted file mode 100644 index 6980d873..00000000 --- a/lingvo/core/steps/embedding_steps_test.py +++ /dev/null @@ -1,57 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -"""Tests for lingvo.core.steps.embedding_steps.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core.steps import embedding_steps - - -class EmbeddingStepsTest(test_utils.TestCase): - - def testEmbeddingStep(self): - with self.session(use_gpu=False): - tf.random.set_seed(398847392) - p = embedding_steps.EmbeddingStep.Params() - p.name = 'emb_step' - p.emb.vocab_size = 10 - p.emb.embedding_dim = 2 - p.emb.max_num_shards = 1 - p.emb.params_init = py_utils.WeightInit.Gaussian(0.01) - p.emb.vn.global_vn = False - p.emb.vn.per_step_vn = False - emb = p.Instantiate() - - # Verify that nothing bad happens when these methods are called. - packed = emb.PrepareExternalInputs(None, None) - state0 = emb.ZeroState(None, None, None) - - out1, state1 = emb.FProp( - emb.theta, packed, - py_utils.NestedMap(inputs=[tf.constant([4, 3], tf.int32)]), - tf.constant([0.0], dtype=tf.float32), state0) - - self.evaluate(tf.global_variables_initializer()) - out1, state1 = self.evaluate([out1, state1]) - - self.assertEqual({}, state1) - self.assertAllClose( - out1.output, - [[-5.9790569e-03, -8.7367110e-03], [-2.1643407e-06, 1.4426162e-02]]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/steps/rnn_steps.py b/lingvo/core/steps/rnn_steps.py deleted file mode 100644 index d905f368..00000000 --- a/lingvo/core/steps/rnn_steps.py +++ /dev/null @@ -1,260 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -"""Step APIs for RNN layers.""" - -from lingvo.core import py_utils -from lingvo.core import rnn_cell -from lingvo.core import step - - -class RnnStep(step.Step): - """A step containing an RNNCell.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('cell', rnn_cell.LSTMCellSimple.Params(), - 'Params for the RNN cell.') - return p - - def __init__(self, params): - super().__init__(params) - p = params - self.CreateChild('cell', p.cell) - - def PrepareExternalInputs(self, theta, external_inputs): - """Does not modify the external_inputs parameter. - - This parameter, if provided, is assumed to be a vector that should be - concatenated with the other vectors in step_inputs.inputs. - - Args: - theta: unused. - external_inputs: Either a tensor or None. - - Returns: - external_inputs, unmodified. - """ - return external_inputs - - def ZeroState(self, theta, prepared_inputs, batch_size): - """Returns the zero_state for the RNN cell. - - Args: - theta: Variables used by the RNNCell. - prepared_inputs: unused. - batch_size: An int scalar representing the batch size of per-step inputs. - - Returns: - The zero state of the RNNCell. - """ - return self.cell.zero_state(theta.cell, batch_size) - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Performs one inference step on the RNN cell. - - If external_inputs is not None, it is added as another act input - to the RNNCell. - - Args: - theta: Variables used by the RNNCell. - prepared_inputs: If not None, concatenated with step_inputs.input. A - tensor of shape [batch_size, external_input_dim]. - step_inputs: A NestedMap containing an 'input' list of [batch_size, dim] - where the sum of dim (including external_inputs) is - p.cell.num_input_nodes. - padding: A 0/1 float tensor of shape [batch_size]; 1.0 means that this - batch element is empty in this step. - state0: A NestedMap of state, either produced by ZeroState or a previous - invocation of FProp. - - Returns: - (output, state1), where output is the cell output (GetOutput(state1)) - of shape [batch_size, p.cell.num_output_nodes], and state1 is the cell's - recurrent state. - """ - cell_inputs = py_utils.NestedMap(act=step_inputs.inputs) - # An empty NestedMap can act as a None value here. - if prepared_inputs is not None and not isinstance(prepared_inputs, - py_utils.NestedMap): - cell_inputs.act.append(prepared_inputs) - cell_inputs.padding = padding - state1, extra = self.cell.FProp(theta.cell, state0, cell_inputs) - return py_utils.NestedMap( - output=self.cell.GetOutput(state1), extra=extra, - padding=padding), state1 - - -class RnnStackStep(step.Step): - """A stack of RnnSteps. - - Three types of inputs are supported: - step_inputs.input: This is the standard input. It is expected to change - on every step of the sequence, and it is fed only to the first layer. - step_inputs.context: This input changes for each step of the sequence, but - is fed to every layer. - external_inputs: This input is fixed at the beginning of the sequence. - It is fed to every layer. - - Residual connections are also supported. When residual_start >= 0, the output - of layer i (i >= residual_start) is added to the output of layer - i - residual_stride. - """ - - @classmethod - def Params(cls): - """Constructs Params for an RnnStackStep.""" - p = super().Params() - p.Define( - 'rnn_cell_tpl', rnn_cell.LSTMCellSimple.Params(), - 'RNNCell params template. ' - 'Can be a single param or ' - 'a list of rnn_layers params, one for each layer.') - p.Define( - 'external_input_dim', 0, 'Size of the external input. ' - 'The external input is given at the start of the sequence ' - 'and is given to every layer at every step.') - p.Define( - 'step_input_dim', 0, 'Size of the step input. ' - 'This input is only given to the first layer and is expected to ' - 'be different for each step.') - p.Define( - 'context_input_dim', 0, 'Size of the context input. ' - 'This input is given to every layer and is expected to be ' - 'different for each step.') - p.Define( - 'rnn_cell_dim', 0, 'Size of the rnn cells. ' - 'This may be overridden by parameters set in rnn_cell_tpl.') - p.Define( - 'rnn_cell_hidden_dim', 0, 'internal size of the rnn cells. When ' - 'set to > 0 it enables a projection layer at the output of the ' - 'rnn cell. This may be overridden by parameters set in rnn_cell_tpl.') - p.Define('rnn_layers', 1, 'Number of rnn layers.') - p.Define( - 'residual_start', -1, - 'Start residual connections from this layer. For this and higher ' - 'layers, the layer output is the sum of the RNN cell output and ' - 'input; if the layer also normalizes its output, then the ' - 'normalization is done over this sum. Set to -1 to disable ' - 'residual connections.') - p.Define('residual_stride', 1, - 'Number of lstm layers to skip per residual connection.') - return p - - def __init__(self, params): - super().__init__(params) - p = params - sub = [] - - # Users can either provide a single rnn_cell_tpl or one per layer. - # If only one is provided, we replicate it for each layer. - rnn_cell_tpls = p.rnn_cell_tpl - if not isinstance(rnn_cell_tpls, list): - rnn_cell_tpls = [p.rnn_cell_tpl] * p.rnn_layers - - # We may provide up to three tensors as input to the RnnStep: - # the normal input, the context input (from step_inputs.context), - # and the external input (from external_inputs). - arity = 1 - if p.context_input_dim: - arity += 1 - if p.external_input_dim: - arity += 1 - extra_dim = p.context_input_dim + p.external_input_dim - - # The first layer's input comes from step_inputs.input. Later layers - # will get their inputs from the previous layer's output. - input_nodes = p.step_input_dim - for i in range(p.rnn_layers): - step_i = RnnStep.Params() - step_i.name = 'rnn_%d' % i - step_i.cell = rnn_cell_tpls[i].Copy() - step_i.cell.num_input_nodes = input_nodes + extra_dim - step_i.cell.inputs_arity = arity - # The dimensions of each cell may be specified in the cell template - # but most users will specify them in the stack params. - if step_i.cell.num_output_nodes == 0: - step_i.cell.num_output_nodes = p.rnn_cell_dim - if step_i.cell.num_hidden_nodes == 0: - step_i.cell.num_hidden_nodes = p.rnn_cell_hidden_dim - input_nodes = step_i.cell.num_output_nodes - sub.append(step_i) - - stack_params = step.StackStep.Params() - stack_params.name = p.name - stack_params.sub = sub - stack_params.residual_start = p.residual_start - stack_params.residual_stride = p.residual_stride - self.CreateChild('stack', stack_params) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.stack.InstantiateVariables() - super()._CreateChildrenVariables() - - def PrepareExternalInputs(self, theta, external_inputs): - """Delegates external inputs preparation to sub-layers. - - Args: - theta: A `.NestedMap` object containing weight values of this layer and - its children layers. - external_inputs: A `.NestedMap` object. The structure of the internal - fields is defined by the sub-steps. - - Returns: - A `.NestedMap` containing a pre-processed version of the external_inputs, - one per sub-step. - """ - return self.stack.PrepareExternalInputs(theta.stack, external_inputs) - - def ZeroState(self, theta, prepared_inputs, batch_size): - """Computes a zero state for each sub-step. - - Args: - theta: A `.NestedMap` object containing weight values of this layer and - its children layers. - prepared_inputs: An output from PrepareExternalInputs. - batch_size: The number of items in the batch that FProp will process. - - Returns: - A `.NestedMap` containing a state0 object for each sub-step. - """ - return self.stack.ZeroState(theta.stack, prepared_inputs, batch_size) - - def FProp(self, theta, prepared_inputs, step_inputs, padding, state0): - """Performs inference on the stack of sub-steps. - - See the documentation for StackStep for the particulars of passing context - information to layers. - - Args: - theta: A `.NestedMap` object containing weight values of this layer and - its children layers. - prepared_inputs: An output from PrepareExternalInputs. - step_inputs: A `.NestedMap` containing a list called 'inputs', an - optionally a tensor called 'context'. - padding: A 0/1 float tensor of shape [batch_size]; 1.0 means that this - batch element is empty in this step. - state0: The previous recurrent state. - - Returns: - (output, state1): - - - output: A `.NestedMap` containing the output of the top-most step. - - state1: The recurrent state to feed to next invocation of this graph. - """ - return self.stack.FProp(theta.stack, prepared_inputs, step_inputs, padding, - state0) diff --git a/lingvo/core/steps/rnn_steps_test.py b/lingvo/core/steps/rnn_steps_test.py deleted file mode 100644 index c79b0290..00000000 --- a/lingvo/core/steps/rnn_steps_test.py +++ /dev/null @@ -1,233 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -"""Tests for lingvo.core.steps.rnn_steps.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import step -from lingvo.core import test_utils -from lingvo.core.steps import rnn_steps - - -class RnnStepsTest(test_utils.TestCase): - - def testRnnStep(self): - with self.session(use_gpu=False): - p = rnn_steps.RnnStep.Params() - p.name = 'rnn_step' - p.cell.name = 'rnn_step_cell' - p.cell.output_nonlinearity = True - p.cell.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.cell.bias_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.cell.vn.global_vn = False - p.cell.vn.per_step_vn = False - p.cell.num_input_nodes = 2 - p.cell.num_output_nodes = 2 - p.cell.inputs_arity = 2 - - rnn_step = p.Instantiate() - external = tf.constant([[3]], tf.float32) - packed = rnn_step.PrepareExternalInputs(rnn_step.theta, external) - - state0 = rnn_step.ZeroState(rnn_step.theta, packed, 1) - _, state1 = rnn_step.FProp( - rnn_step.theta, packed, - py_utils.NestedMap(inputs=[tf.constant([[4]], tf.float32)]), - tf.constant([0.0], dtype=tf.float32), state0) - out2, state2 = rnn_step.FProp( - rnn_step.theta, packed, - py_utils.NestedMap(inputs=[tf.constant([[4]], tf.float32)]), - tf.constant([0.0], dtype=tf.float32), state1) - - self.evaluate(tf.global_variables_initializer()) - out2, state2 = self.evaluate([out2, state2]) - - self.assertAllClose(state2.m, [[-0.32659757, 0.87739915]]) - self.assertAllClose(state2.c, [[-1.9628618, 1.4194499]]) - self.assertAllClose(out2.output, [[-0.32659757, 0.87739915]]) - self.assertAllClose(out2.padding, [0.0]) - - def testRnnStepRecurrent(self): - with self.session(use_gpu=False): - p = rnn_steps.RnnStep.Params() - p.name = 'rnn_step' - p.cell.name = 'rnn_step_cell' - p.cell.output_nonlinearity = True - p.cell.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.cell.bias_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.cell.vn.global_vn = False - p.cell.vn.per_step_vn = False - p.cell.num_input_nodes = 2 - p.cell.num_output_nodes = 2 - p.cell.inputs_arity = 2 - - recurrent_p = step.RecurrentStepWrapper.Params().Set( - name='recurrent_wrapper', step=p) - recurrent_step = recurrent_p.Instantiate() - - external = tf.constant([[3]], tf.float32) - packed = recurrent_step.PrepareExternalInputs(recurrent_step.theta, - external) - inputs = py_utils.NestedMap( - inputs=[tf.constant([[[4]], [[4]]], tf.float32)]) - padding = tf.constant([[0.0], [0.0]], dtype=tf.float32) - state0 = recurrent_step.ZeroState(recurrent_step.theta, packed, 1) - - output, state2 = recurrent_step.FProp( - recurrent_step.theta, - prepared_inputs=packed, - inputs=inputs, - padding=padding, - state0=state0) - - self.evaluate(tf.global_variables_initializer()) - output, state2 = self.evaluate([output, state2]) - self.assertAllClose(state2.m, - [[[-0.20144, 0.741861]], [[-0.326598, 0.877399]]]) - self.assertAllClose(state2.c, - [[[-0.980946, 0.973391]], [[-1.962862, 1.41945]]]) - self.assertAllClose(output.output, - [[[-0.20144, 0.741861]], [[-0.326598, 0.877399]]]) - - def testRnnStackStep(self): - with self.session(use_gpu=False): - p = rnn_steps.RnnStackStep.Params() - p.name = 'rnn_stack_step' - p.rnn_cell_tpl.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.rnn_cell_tpl.bias_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.rnn_cell_tpl.vn.global_vn = False - p.rnn_cell_tpl.vn.per_step_vn = False - p.external_input_dim = 2 - p.step_input_dim = 1 - p.context_input_dim = 1 - p.rnn_cell_dim = 3 - p.rnn_cell_hidden_dim = 3 - p.rnn_layers = 2 - rnn_stack = p.Instantiate() - - external = tf.constant([[1, 2]], tf.float32) - packed = rnn_stack.PrepareExternalInputs(rnn_stack.theta, external) - state0 = rnn_stack.ZeroState(rnn_stack.theta, packed, 1) - output1, state1 = rnn_stack.FProp( - rnn_stack.theta, packed, - py_utils.NestedMap( - inputs=[tf.constant([[4]], tf.float32)], - context=tf.constant([[5]], tf.float32)), - tf.constant([0.0], dtype=tf.float32), state0) - - self.evaluate(tf.global_variables_initializer()) - output1, state1 = self.evaluate([output1, state1]) - - self.assertAllClose(output1.output, - [[0.43175745, -0.39472747, -0.36191428]]) - self.assertAllClose( - state1, { - 'sub': [{ - 'm': [[-0.4587491, 0.56409806, 0.23025148]], - 'c': [[5.6926787e-01, 6.3084178e-02, 4.1969700e-04]] - }, { - 'm': [[0.43175745, -0.39472747, -0.36191428]], - 'c': [[0.00158867, 0.57818687, -0.98025495]] - }] - }) - - def testRnnStackStepResidual(self): - with self.session(use_gpu=False): - p = rnn_steps.RnnStackStep.Params() - p.name = 'rnn_stack_step' - p.rnn_cell_tpl.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.rnn_cell_tpl.bias_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.rnn_cell_tpl.vn.global_vn = False - p.rnn_cell_tpl.vn.per_step_vn = False - p.external_input_dim = 2 - p.step_input_dim = 1 - p.context_input_dim = 1 - p.rnn_cell_dim = 3 - p.rnn_cell_hidden_dim = 3 - p.rnn_layers = 3 - p.residual_start = 0 - rnn_stack = p.Instantiate() - - external = tf.constant([[1, 2]], tf.float32) - packed = rnn_stack.PrepareExternalInputs(rnn_stack.theta, external) - state0 = rnn_stack.ZeroState(rnn_stack.theta, packed, 1) - output1, state1 = rnn_stack.FProp( - rnn_stack.theta, packed, - py_utils.NestedMap( - inputs=[tf.constant([[4]], tf.float32)], - context=tf.constant([[5]], tf.float32)), - tf.constant([0.0], dtype=tf.float32), state0) - - self.evaluate(tf.global_variables_initializer()) - output1, state1 = self.evaluate([output1, state1]) - - # Because there are residual connections, we expect the output to - # be equal to sum(rnn_state.m for rnn_state in state1.sub). - self.assertAllClose(output1.output, [[3.5457525, 4.576743, 4.2290816]]) - self.assertAllClose( - state1, { - 'sub': [{ - 'm': [[-0.4587491, 0.56409806, 0.23025148]], - 'c': [[5.6926787e-01, 6.3084178e-02, 4.1969700e-04]] - }, { - 'm': [[0.00226134, 0.00633766, -0.00058896]], - 'c': [[0.00088014, 0.07547389, 0.00625527]] - }, { - 'm': [[0.00224019, 0.00630755, -0.00058099]], - 'c': [[0.00088081, 0.07505893, 0.00621293]] - }] - }) - - def testRnnStackStepNoContext(self): - with self.session(use_gpu=False): - p = rnn_steps.RnnStackStep.Params() - p.name = 'rnn_stack_step' - p.rnn_cell_tpl.params_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.rnn_cell_tpl.bias_init = py_utils.WeightInit.Uniform(1.24, 429891685) - p.rnn_cell_tpl.vn.global_vn = False - p.rnn_cell_tpl.vn.per_step_vn = False - p.step_input_dim = 1 - p.rnn_cell_dim = 3 - p.rnn_cell_hidden_dim = 3 - p.rnn_layers = 2 - p.residual_start = 0 - rnn_stack = p.Instantiate() - - packed = rnn_stack.PrepareExternalInputs(rnn_stack.theta, - py_utils.NestedMap()) - state0 = rnn_stack.ZeroState(rnn_stack.theta, packed, 1) - output1, state1 = rnn_stack.FProp( - rnn_stack.theta, packed, - py_utils.NestedMap(inputs=[tf.constant([[4]], tf.float32)]), - tf.constant([0.0], dtype=tf.float32), state0) - - self.evaluate(tf.global_variables_initializer()) - output1, state1 = self.evaluate([output1, state1]) - - self.assertAllClose(output1.output, [[5.900284, 3.0231729, 3.0207822]]) - self.assertAllClose( - state1, { - 'sub': [{ - 'm': [[1.1416901, -0.32166323, -0.5909376]], - 'c': [[-0.98086286, 0.9052862, 0.10041453]] - }, { - 'm': [[0.7585938, -0.655164, -0.3882802]], - 'c': [[-8.3011830e-01, 1.8685710e-01, 1.0723456e-04]] - }] - }) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/summary_utils.py b/lingvo/core/summary_utils.py deleted file mode 100644 index 6b0efa49..00000000 --- a/lingvo/core/summary_utils.py +++ /dev/null @@ -1,381 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Common utility functions for generating summaries.""" - -import time -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import plot -from lingvo.core import py_utils -import numpy as np - - -def _ShouldAddSummary(): - return cluster_factory.Current().add_summary - - -def scalar(*args, **kwargs): # pylint: disable=invalid-name - if _ShouldAddSummary(): - tf.summary.scalar(*args, **kwargs) - - -def histogram(*args, **kwargs): # pylint: disable=invalid-name - if _ShouldAddSummary(): - tf.summary.histogram(*args, **kwargs) - - -def image(*args, **kwargs): # pylint: disable=invalid-name - if _ShouldAddSummary(): - tf.summary.image(*args, **kwargs) - - -def SequenceLength(padding): - """Computes the length of a sequence based on binary padding. - - Args: - padding: A tensor of binary paddings shaped [batch, seqlen]. - - Returns: - seq_lens, A tensor of shape [batch] containing the non-padded length of each - element of plot_tensor along the batch dimension. - """ - seq_lens = tf.cast(tf.round(tf.reduce_sum(1 - padding, axis=1)), tf.int32) - # Get rid of any extra dimensions. - batch_size = tf.shape(padding)[0] - seq_lens = tf.reshape(seq_lens, [batch_size], name='seq_lens') - return seq_lens - - -def TrimPaddingAndPlotSequence(fig, axes, seq_matrix, seq_len, **kwargs): - """Trims the time axis of seq_matrix with shape (dim, time) and plots it. - - For use as a plot function with MatplotlibFigureSummary. - - Args: - fig: A matplotlib figure handle. - axes: A matplotlib axes handle. - seq_matrix: A 2D ndarray shaped (num_rows, time). - seq_len: Integer length to use to trim the time axis of seq_matrix. - **kwargs: Additional keyword args to pass to plot.AddImage. - """ - plot.AddImage(fig, axes, seq_matrix[:, :seq_len], **kwargs) - - -def TrimPaddingAndPlotAttention(fig, - axes, - atten_matrix, - src_len, - tgt_len, - transcript=None, - **kwargs): - """Trims axes of atten_matrix with shape (tgt_time, src_time) and plots it. - - For use as a plot function with MatplotlibFigureSummary. - - Args: - fig: A matplotlib figure handle. - axes: A matplotlib axes handle. - atten_matrix: A 2D ndarray shaped (tgt_time, src_time). - src_len: Integer length to use to trim the src_time axis of atten_matrix. - tgt_len: Integer length to use to trim the tgt_time axis of atten_matrix. - transcript: transcript for the target sequence. - **kwargs: Additional keyword args to pass to plot.AddImage. - """ - plot.AddImage( - fig, axes, atten_matrix[:tgt_len, :src_len], clim=(0, 1), **kwargs) - if transcript is not None: - if isinstance(transcript, np.ndarray): - transcript = ' '.join(transcript[:src_len]) - axes.set_xlabel(plot.ToUnicode(transcript), size='x-small', wrap=True) - - -def AddAttentionSummary(name, - attention_tensors, - src_paddings, - tgt_paddings, - transcripts=None, - max_outputs=3): - """Adds an image summary showing the attention probability matrix and state. - - Tensors are in sequence tensor format with the batch dimension in axis 1. - - Args: - name: Summary name. - attention_tensors: A list of 3D tensors shaped [target_len, batch_size, - source_len] where attention[i, j, k] is the probability for the i-th - output attending to the k-th input for element j in the batch. - src_paddings: A tensor of binary paddings shaped [source_len, batch] for the - source sequence. Or a list of tensors of the same length as - attention_tensors with a separate paddings for each entry in - attention_tensors. - tgt_paddings: A tensor of binary paddings shaped [target_len, batch] for the - target sequence. Or a list of tensors of the same length as - attention_tensors with a separate paddings for each entry in - attention_tensors. - transcripts: Optional, transcripts shaped [batch, source_len] for the source - sequence. - max_outputs: Integer maximum number of elements of the batch to plot. - """ - - def Transpose(paddings): - paddings = paddings if isinstance(paddings, list) else [paddings] - return [tf.transpose(p) for p in paddings] - - AddAttentionSummaryBatchMajor( - name, [tf.transpose(a, [1, 0, 2]) for a in attention_tensors], - Transpose(src_paddings), Transpose(tgt_paddings), transcripts, - max_outputs) - - -def AddAttentionSummaryBatchMajor(name, - attention_tensors, - src_paddings, - tgt_paddings, - transcripts=None, - max_outputs=3): - """Adds an image summary showing the attention probability matrix and state. - - As opposed to AddAttentionSummary() takes all tensors with batch dimension in - axis 0. - - Args: - name: Summary name. - attention_tensors: A list of 3D tensors shaped [batch_size, target_len, - source_len] where attention[b, i, j] is the probability for the i-th - output attending to the j-th input for element b in the batch. - src_paddings: A tensor of binary paddings shaped [batch, source_len] for the - source sequence. Or a list of tensors of the same length as - attention_tensors with a separate paddings for each entry in - attention_tensors. - tgt_paddings: A tensor of binary paddings shaped [batch, target_len] for the - target sequence. Or a list of tensors of the same length as - attention_tensors with a separate paddings for each entry in - attention_tensors. - transcripts: Optional, transcripts shaped [batch, source_len] for the source - sequence. - max_outputs: Integer maximum number of elements of the batch to plot. - """ - - def VerifyLen(paddings): - length = len(paddings) if isinstance(paddings, list) else 1 - if length != 1 and length != len(attention_tensors): - raise ValueError('Bad length of paddings list {}'.format(length)) - - VerifyLen(src_paddings) - VerifyLen(tgt_paddings) - - if not _ShouldAddSummary(): - return - - def ToLengths(paddings): - paddings = paddings if isinstance(paddings, list) else [paddings] - return [SequenceLength(p) for p in paddings] - - def Get(lengths, i): - return lengths[0 if len(lengths) == 1 else i] - - src_lens = ToLengths(src_paddings) - tgt_lens = ToLengths(tgt_paddings) - - with plot.MatplotlibFigureSummary( - name + '/Attention', - max_outputs=max_outputs, - gridspec_kwargs={'hspace': 0.3}) as fig: - for n, atten in enumerate(attention_tensors): - # Diagnostic metric that decreases as attention picks up. - max_entropy = tf.math.log(tf.cast(Get(src_lens, n), tf.float32)) - max_entropy = tf.expand_dims(tf.expand_dims(max_entropy, -1), -1) - atten_normalized_entropy = -atten * tf.math.log(atten + - 1e-10) / max_entropy - scalar(name + '/Attention/average_normalized_entropy/%d' % n, - tf.reduce_mean(atten_normalized_entropy)) - args = [atten, Get(src_lens, n), Get(tgt_lens, n)] - if transcripts is not None and n == 0: - args.append(transcripts) - fig.AddSubplot( - args, - TrimPaddingAndPlotAttention, - title=name, - xlabel='Input', - ylabel='Output') - - -def AddNormSummary(name, vs_gs): - """"Returns and creates summary for norms of vs and their gradients gs. - - Args: - name: A name string for summary. - vs_gs: A `.NestedMap` or a list of `.NestedMap` of (variable, gradient). - - Returns: - norm of variables, and norm of gradients. - """ - flatten = py_utils.Flatten(vs_gs) - v_norm = tf.sqrt(py_utils.SumSquared([v for (v, _) in flatten])) - scalar('var_norm/%s' % name, v_norm) - g_norm = tf.sqrt(py_utils.SumSquared([g for (_, g) in flatten])) - scalar('grad_norm/%s' % name, g_norm) - return v_norm, g_norm - - -def CollectVarHistogram(vs_gs): - """Adds histogram summaries for variables and gradients.""" - - for name, (var, grad) in vs_gs.FlattenItems(): - name = py_utils.SanitizeScopeKey(name) - with tf.device(var.device), tf.name_scope(name + '/summary'): - if isinstance(grad, tf.IndexedSlices): - var = tf.gather(var, grad.indices) - grad = grad.values - if var.dtype.is_complex: - var = tf.abs(var) - grad = tf.abs(grad) - - histogram('var_hist/' + name, var) - histogram('grad_hist/' + name, grad) - - -def PrepareSequenceForPlot(tensor, padding, name): - """Prepares a sequence feature for plotting. - - The sequence feature is transposed and channels are flattened. - - Args: - tensor: A n-D Tensor of shape [batch, time, ...]. - padding: A Tensor of shape [batch, time]. - name: A string as the name of the reshaped Tensor, which will be used as the - subcaption for plotting. - - Returns: - A tuple of: - reshaped_tensor: A 3-D Tensor of shape [batch, dim, time]. - sequence_length: A 1-D Tensor of shape [batch]. - """ - # Flatten any dimensions beyond the third into the third. - batch_size, max_len = py_utils.GetShape(tensor, 2) - plot_tensor = tf.reshape(tensor, [batch_size, max_len, -1]) - plot_tensor = tf.transpose(plot_tensor, [0, 2, 1], name=name) - return (plot_tensor, SequenceLength(padding)) - - -def PlotSequenceFeatures(plots, name, **kwargs): - """Plots a stack of sequence features. - - Args: - plots: A list of tuple (tensor, seq_len), as returned by - PrepareSequenceForPlot(). - name: A string for the caption of the plot. - **kwargs: Keyword arguments passed to AddSubplot(). - """ - if not _ShouldAddSummary(): - return - - with plot.MatplotlibFigureSummary(name, figsize=(8, len(plots) * 3.5)) as fig: - for tensor, seq_len in plots: - fig.AddSubplot([tensor, seq_len], - TrimPaddingAndPlotSequence, - title=tensor.name, - **kwargs) - - -class StatsCounter: - """A single counter in TF.""" - - def __init__(self, name): - self._name = name - self._var = py_utils.CreateVariable( - name=name, - params=py_utils.WeightParams([], py_utils.WeightInit.Constant(0), - tf.int64), - trainable=False) - self._value = self._var.value() + 0 # Makes a copy. - - def Value(self): - """Returns the current counter value.""" - return self._value - - def IncBy(self, delta): - """Increment the counter by delta and return the new value.""" - # NOTE: We must ensure _value is computed (_var + 0) before - # updating _var with delta. - delta = tf.cast(delta, tf.int64) - with tf.control_dependencies([self._value]): - scalar(self._name, self._value) - return tf.identity(tf.assign_add(self._var, delta)) - - -class StepRateTracker: - """A class that tracks step/example rate.""" - - def __init__(self): - self._time_steps = [] # History of (timestamp, global_step, total_examples) - - def ComputeStepRate(self, current_steps, total_examples): - """Computes the overall step rate.""" - if self._time_steps: - total_examples += self._time_steps[-1][-1] - self._time_steps.append((time.time(), current_steps, total_examples)) - # Keeps a relative long history to compute a smooth steps/second. - # Removes duplicate stats for step = 0 to get rid of the warm-up period. - while (self._time_steps[-1][1] - self._time_steps[0][1] > 10000 or - (len(self._time_steps) > 1 and - self._time_steps[0][1] == self._time_steps[1][1])): - del self._time_steps[0] - (t0, s0, e0), (t1, s1, e1) = self._time_steps[0], self._time_steps[-1] - rate = 0.0 - example_rate = 0.0 - if t1 > t0 + 1: - elapsed_secs = t1 - t0 - rate = (s1 - s0) / elapsed_secs - example_rate = (e1 - e0) / elapsed_secs - tf.logging.info('Steps/second: %f, Examples/second: %f', rate, - example_rate) - return rate, example_rate, total_examples - - -def ModelAnalysis(model): - """Returns a text showing variable sizes and their total size.""" - - class Analyzer: - """Helper class.""" - - def __init__(self): - self._seen_var = {} - self.total = 0 - - def __call__(self, v): - assert isinstance(v, tf.Variable) - # pylint: disable=protected-access - if not v.shape.is_fully_defined(): - # Only Cudnn RNN params lack static shapes. - if hasattr(v, 'approx_size'): - size = v.approx_size - else: - return '%-20s %10s %s' % (v.shape, 'n/a', v._shared_name) - else: - size = v.shape.num_elements() - if v._shared_name not in self._seen_var: - self._seen_var[v._shared_name] = size - self.total += size - return '%-20s %10d %s' % (v.shape, size, v._shared_name) - - analyzer = Analyzer() - output = '\n' - output += model.vars.Transform(analyzer).DebugString() - output += '\n' - output += '=' * 100 - output += f'\ntotal #params: {analyzer.total:,}\n' - return output, analyzer.total diff --git a/lingvo/core/summary_utils_test.py b/lingvo/core/summary_utils_test.py deleted file mode 100644 index 0050a001..00000000 --- a/lingvo/core/summary_utils_test.py +++ /dev/null @@ -1,44 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for summary_utils.""" - -from lingvo import compat as tf -from lingvo.core import cluster_factory -from lingvo.core import summary_utils -from lingvo.core import test_utils - - -class SummaryUtilsTest(test_utils.TestCase): - - def testStatsCounter(self): - with self.session(): - with cluster_factory.ForTestingWorker(add_summary=True): - foo = summary_utils.StatsCounter('foo') - val = foo.Value() - inc = foo.IncBy(100) - - self.evaluate(tf.global_variables_initializer()) - self.assertAllEqual(0, val.eval()) - self.assertAllEqual(100, self.evaluate(inc)) - self.assertAllEqual(100, val.eval()) - self.assertAllEqual([100, 200], self.evaluate([val, inc])) - self.assertAllEqual([200, 300], self.evaluate([val, inc])) - summary = tf.Summary.FromString(self.evaluate(tf.summary.merge_all())) - self.assertTrue(any('foo' in v.tag for v in summary.value)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/symbolic.py b/lingvo/core/symbolic.py deleted file mode 100644 index e9ee1e28..00000000 --- a/lingvo/core/symbolic.py +++ /dev/null @@ -1,127 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Utilities for symbolic computation.""" - -import threading -import sympy - - -class Symbol(sympy.Dummy): - pass - - -def IsSymbol(x): - return isinstance(x, Symbol) - - -def IsExpr(x): - return isinstance(x, sympy.Expr) - - -STATIC_VALUES = 'static' -TENSOR_VALUES = 'tensor' -VALUE_TYPES = (STATIC_VALUES, TENSOR_VALUES) - - -class _LocalSymbolToValueStack(threading.local): - """A thread-local stack of symbol-to-value dicts.""" - - def __init__(self): - super().__init__() - self.stack = {} - for value_type in VALUE_TYPES: - self.stack[value_type] = [{}] - - -class SymbolToValueMap: - """A symbol-to-value mapping. - - Usage: - - with SymbolToValueMap('static', {symbol1: value1, symbol2: value2, ...}): - with SymbolToValueMap('tensor', {symbol1: value1, symbol2: value2, ...}): - ... = EvalExpr(value_type, symbolic_expr) - - Multiple SymbolToValueMap context can be nested inside one another. The inner - contexts take precedence over outer ones when multiple contexts provide - values for the same symbol. - """ - - _local_stack = _LocalSymbolToValueStack() - - def __init__(self, value_type, symbol_to_value_map): - """Creates a new symbol to value map. - - Args: - value_type: the type of values in 'symbol_to_value_map'. - symbol_to_value_map: a dict from Symbol to values. - """ - assert value_type in VALUE_TYPES - self.value_type = value_type - self.merged = dict(self.Stack(value_type)[-1]) - self.merged.update(symbol_to_value_map) - - @staticmethod - def Stack(value_type): - return SymbolToValueMap._local_stack.stack[value_type] - - def __enter__(self): - self.Stack(self.value_type).append(self.merged) - - def __exit__(self, type_arg, value_arg, traceback_arg): - stack = self.Stack(self.value_type) - assert stack - assert stack[-1] is self.merged - stack.pop() - - @staticmethod - def Get(value_type): - """Returns a symbol-to-value mapping merged from Stack().""" - return SymbolToValueMap.Stack(value_type)[-1] - - -def EvalExpr(value_type, x): - """Evaluates x with symbol_to_value_map within the current context. - - Args: - value_type: the target value type (see VALUE_TYPE). - x: a sympy.Expr, an object, or a list/tuple of Exprs and objects. - - Returns: - Evaluation result of 'x'. - """ - if isinstance(x, (list, tuple)): - return type(x)(EvalExpr(value_type, y) for y in x) - elif isinstance(x, sympy.Expr): - symbol_to_value_map = SymbolToValueMap.Get(value_type) - if not symbol_to_value_map: - return x - # In theory the below should be equivalent to: - # y = x.subs(symbol_to_value_map). - # In practice subs() doesn't work for when values are Tensors. - k, v = list(zip(*(list(symbol_to_value_map.items())))) - y = sympy.lambdify(k, x)(*v) - return y - else: - return x - - -def ToStatic(expr): - return EvalExpr(STATIC_VALUES, expr) - - -def ToTensor(expr): - return EvalExpr(TENSOR_VALUES, expr) diff --git a/lingvo/core/symbolic_test.py b/lingvo/core/symbolic_test.py deleted file mode 100644 index fc364a68..00000000 --- a/lingvo/core/symbolic_test.py +++ /dev/null @@ -1,68 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.core.symbolic.""" - -import lingvo.compat as tf -from lingvo.core import symbolic -from lingvo.core import test_utils -import sympy - - -class SymbolicTest(test_utils.TestCase): - - def testGetSymbol(self): - x = symbolic.Symbol('x') - self.assertIsInstance(x, sympy.Expr) - - def testEvalExpr(self): - x = symbolic.Symbol('x') - y = symbolic.Symbol('y') - xy = x * y - - # Without symbol-to-value map. - self.assertEqual(xy, symbolic.ToStatic(xy)) - self.assertEqual(xy, symbolic.ToTensor(xy)) - - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, {x: 2, y: 3}): - self.assertEqual(symbolic.ToStatic(xy), 6) - # The inner map overrides the outer map. - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, {x: 5, y: 6}): - self.assertEqual(symbolic.ToStatic(xy), 30) - # Back to the outer map. - self.assertEqual(symbolic.ToStatic(xy), 6) - - # EvalExpr can also evaluate a symbolic expression to a - # Tensor. - a = tf.placeholder(tf.float32) - b = tf.placeholder(tf.float32) - with symbolic.SymbolToValueMap(symbolic.TENSOR_VALUES, {x: a, y: b}): - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, {x: 2, y: 3}): - # Value maps of different types do not affect each other. - self.assertEqual(symbolic.ToStatic(xy), 6) - ab = symbolic.ToTensor(xy) - self.assertIsInstance(ab, tf.Tensor) - with self.session() as sess: - self.assertEqual(12, sess.run(ab, {a: 3, b: 4})) - - # EvalExpr supports partial evaluation. - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, {y: 3}): - x3 = symbolic.ToStatic(xy) - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, {x: 9}): - self.assertEqual(27, symbolic.ToStatic(x3)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/target_sequence_sampler.py b/lingvo/core/target_sequence_sampler.py deleted file mode 100644 index aed6ac3c..00000000 --- a/lingvo/core/target_sequence_sampler.py +++ /dev/null @@ -1,168 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""A library to sample target sequences given a decoder and decoder inputs. - -The sampled sequences can be used for training, e.g., with scheduled sampling, -OCD, second-pass deliberation. -""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core import recurrent - - -def _ComputePaddings(ids, eos_id): - is_eos = tf.cast(tf.equal(ids, eos_id), tf.int32) - # eos_in_prefix[i, j] = any(ids[i, k] == eos_id for k in range(j)) - eos_in_prefix = tf.cumsum(is_eos, axis=-1, exclusive=True) - return tf.where( - tf.equal(eos_in_prefix, 0), tf.zeros_like(ids), tf.ones_like(ids)) - - -class TargetSequenceSampler(base_layer.BaseLayer): - """Helper class for sampling target sequences with a decoder from inputs.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('target_sos_id', 1, 'Id of the start of sentence token.') - p.Define('target_eos_id', 2, 'Id of the end of sentence token.') - p.Define('target_eoc_id', -1, 'Id of the end of chunk token.') - p.Define('target_seq_len', 0, 'Maximum allowed target seq length.') - p.Define( - 'temperature', 1., 'If > 1, a smoother distribution than logits; ' - 'if < 1, a sharper distribution than logits. ' - 'Must be > 0.') - p.Define( - 'use_stop_fn', False, 'If True, use a stop_fn that causes the ' - 'sampler to early terminate when all samples in the batch end with ' - 'the target_eos_id token.') - p.name = 'target_sequence_sampler' - return p - - def Sample(self, decoder_theta, encoder_outputs, random_seed, - init_state_callback, pre_step_callback, post_step_callback): - """Samples target sequences, one target sequence per source sequence. - - (Please see beam_search_helper.py for description of decoder callbacks.) - - Args: - decoder_theta: A NestedMap object containing weights' values of the - decoder layer and its children layers, to be passed to decoder - callbacks. - encoder_outputs: the outputs of the encoder, to be passed to callbacks. - random_seed: a scalar int32 tensor representing the random seed. - init_state_callback: decoder._InitBeamSearchStateCallback. - pre_step_callback: decoder._PreBeamSearchStepCallback. - post_step_callback: decoder._PostBeamSearchStepCallback. - - Returns: - A NestedMap containing the following tensors - - - 'logits': [batch, max_target_length, vocab_size], representing the - distribution from which target sequences are sampled. - - 'ids': [batch, max_target_length] of int32, representing the target - sequence ids, not including target_sos_id, but maybe ending with - target_eos_id if end-of-sequence is reached before target_seq_len. - - 'paddings': [batch, max_target_length] of 0/1, where 1 represents - a padded timestep. - """ - p = self.params - assert p.temperature > 0 - if getattr(encoder_outputs, 'segment_id', 1) is None: - # Remove None values, which are not supported by recurrent. - del encoder_outputs['segment_id'] - # init_state_callback may modify 'encoder_outputs', e.g., by inserting - # 'packed_src'. - bs_result, bs_state = init_state_callback( - decoder_theta, encoder_outputs, num_hyps_per_beam=1) - # 'recurrent_theta' represents all cross-timestep information used by the - # recurrent loop below, including layer theta and encoder outputs. - recurrent_theta = py_utils.NestedMap( - theta=decoder_theta, - random_seed=random_seed, - encoder_outputs=encoder_outputs) - batch = tf.shape(bs_result.log_probs)[0] - recurrent_state0 = py_utils.NestedMap( - timestep=tf.zeros(shape=[], dtype=tf.int32), - logits=bs_result.log_probs, - # Start with target_sos_id. - ids=tf.fill([batch], tf.cast(p.target_sos_id, tf.int32)), - bs_state=bs_state) - inputs = py_utils.NestedMap(dummy=tf.zeros([p.target_seq_len, batch])) - - def Step(recurrent_theta, state0, inputs): - """Computes one decoder step.""" - del inputs - with tf.name_scope('single_sampler_step'): - # Compute logits and states. - bs_result, bs_state1 = pre_step_callback( - recurrent_theta.theta, - recurrent_theta.encoder_outputs, - tf.expand_dims(state0.ids, 1), # [batch, 1]. - state0.bs_state, - num_hyps_per_beam=1) - batch = tf.shape(bs_result.log_probs)[0] - state1 = py_utils.NestedMap(timestep=state0.timestep + 1) - state1.logits = bs_result.log_probs - # Sample ids from logits. [batch]. - state1.ids = tf.reshape( - tf.random.stateless_categorical( - state1.logits / p.temperature, - num_samples=1, - seed=tf.stack([recurrent_theta.random_seed, state0.timestep]), - dtype=state0.ids.dtype, - name='sample_next_id'), [batch]) - if 'is_last_chunk' in bs_result and p.target_eoc_id >= 0: - state1.ids = tf.where( - tf.math.logical_and(bs_result.is_last_chunk, - tf.equal(state1.ids, p.target_eoc_id)), - tf.fill(tf.shape(state1.ids), p.target_eos_id), state1.ids) - state1.bs_state = post_step_callback(recurrent_theta.theta, - recurrent_theta.encoder_outputs, - state1.ids, bs_state1) - return state1, py_utils.NestedMap() - - def StopFn(t, theta, state): - del t, theta # Unused: this stop function only uses the state ids. - return tf.equal(state.ids, p.target_eos_id) - - if p.use_stop_fn: - stop_fn = StopFn - else: - stop_fn = None - - accumulated_states, _ = recurrent.Recurrent( - recurrent_theta, - recurrent_state0, - inputs, - Step, - stop_fn=stop_fn, - allow_implicit_capture=True) - result = py_utils.NestedMap( - logits=tf.transpose(accumulated_states.logits, [1, 0, 2]), - ids=tf.transpose(accumulated_states.ids)) - result.paddings = tf.cast( - _ComputePaddings(result.ids, p.target_eos_id), result.logits.dtype) - # Force ids to be eos_id if the timestep is padded. - result.ids = tf.where( - tf.equal(result.paddings, 0), result.ids, - tf.fill(tf.shape(result.ids), p.target_eos_id)) - static_batch_size = bs_result.log_probs.shape[0] - result.ids.set_shape([static_batch_size, p.target_seq_len]) - result.paddings.set_shape([static_batch_size, p.target_seq_len]) - return result diff --git a/lingvo/core/target_sequence_sampler_test.py b/lingvo/core/target_sequence_sampler_test.py deleted file mode 100644 index b8a6c144..00000000 --- a/lingvo/core/target_sequence_sampler_test.py +++ /dev/null @@ -1,258 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for target_sequence_sampler.""" - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import target_sequence_sampler -from lingvo.core import test_utils -import numpy as np - - -class TargetSequenceSamplerTest(test_utils.TestCase): - - def testTargetSequenceSampler(self): - with self.session(use_gpu=False): - np.random.seed(9384758) - tf.random.set_seed(8274758) - vocab_size = 12 - src_len = 5 - tgt_len = 7 - batch_size = 2 - - def InitBeamSearchCallBack(unused_theta, unused_encoder_outputs, - num_hyps_per_beam): - self.assertEqual(1, num_hyps_per_beam) - logits = tf.zeros((batch_size, vocab_size), dtype=tf.float32) - return (py_utils.NestedMap(log_probs=logits), - py_utils.NestedMap(step=tf.constant(0))) - - def PreBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_step_ids, states, num_hyps_per_beam): - self.assertEqual(1, num_hyps_per_beam) - logits = tf.random.stateless_normal([batch_size, vocab_size], - seed=[8273747, 9]) - return (py_utils.NestedMap(log_probs=logits), - py_utils.NestedMap(step=states.step + 1)) - - def PostBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_new_step_ids, states): - return states - - src_enc = tf.random.stateless_normal([src_len, batch_size, 8], - seed=[982774838, 9]) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float32) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - - theta = py_utils.NestedMap() - random_seed = tf.constant(123) - p = target_sequence_sampler.TargetSequenceSampler.Params().Set( - name='bsh', target_seq_len=tgt_len) - seq_sampler = p.Instantiate() - decoder_output = seq_sampler.Sample( - theta, encoder_outputs, random_seed, InitBeamSearchCallBack, - PreBeamSearchStepCallback, PostBeamSearchStepCallback) - - ids, lens = self.evaluate([ - decoder_output.ids, - tf.reduce_sum(1 - decoder_output.paddings, 1), - ]) - print(np.array_repr(ids)) - print(np.array_repr(lens)) - expected_ids = [[9, 0, 2, 2, 2, 2, 2], [0, 0, 11, 8, 1, 0, 7]] - expected_lens = [3, 7] - self.assertAllEqual(expected_ids, ids) - self.assertAllEqual(expected_lens, lens) - - p = target_sequence_sampler.TargetSequenceSampler.Params().Set( - name='bsh', target_seq_len=tgt_len, temperature=0.2) - seq_sampler = p.Instantiate() - decoder_output = seq_sampler.Sample( - theta, encoder_outputs, random_seed, InitBeamSearchCallBack, - PreBeamSearchStepCallback, PostBeamSearchStepCallback) - - ids, lens = self.evaluate([ - decoder_output.ids, - tf.reduce_sum(1 - decoder_output.paddings, 1), - ]) - print(np.array_repr(ids)) - print(np.array_repr(lens)) - expected_ids = [[0, 0, 0, 0, 0, 0, 9], [0, 0, 11, 7, 1, 0, 7]] - expected_lens = [7, 7] - self.assertAllEqual(expected_ids, ids) - self.assertAllEqual(expected_lens, lens) - - def testTargetSequenceSamplerWithEOC(self): - with self.session(use_gpu=False): - np.random.seed(9384758) - tf.random.set_seed(8274758) - vocab_size = 4 - src_len = 5 - tgt_len = 20 - batch_size = 2 - p = target_sequence_sampler.TargetSequenceSampler.Params().Set( - name='bsh', target_seq_len=tgt_len, target_eoc_id=0) - seq_sampler = p.Instantiate() - - def InitBeamSearchCallBack(unused_theta, unused_encoder_outputs, - num_hyps_per_beam): - self.assertEqual(1, num_hyps_per_beam) - logits = tf.zeros((batch_size, vocab_size), dtype=tf.float32) - is_last_chunk = tf.constant(False, shape=[batch_size]) - result = py_utils.NestedMap( - log_probs=logits, is_last_chunk=is_last_chunk) - states = py_utils.NestedMap( - step=tf.constant(0), - src_step=tf.zeros([batch_size], dtype=tf.int32)) - return result, states - - def PreBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_step_ids, states, num_hyps_per_beam): - self.assertEqual(1, num_hyps_per_beam) - logits = tf.random.stateless_normal([batch_size, vocab_size], - seed=[8273747, 9]) - # Make it never predict . - logits -= tf.one_hot([p.target_eos_id], vocab_size, 1e30) - is_last_chunk = tf.equal(states.src_step, src_len - 1) - result = py_utils.NestedMap( - log_probs=logits, is_last_chunk=is_last_chunk) - return result, states - - def PostBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - new_step_ids, states): - return py_utils.NestedMap( - step=states.step + 1, - src_step=states.src_step + tf.cast( - tf.equal(new_step_ids, p.target_eoc_id), dtype=tf.int32)) - - src_enc = tf.random.stateless_normal([src_len, batch_size, 8], - seed=[982774838, 9]) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float32) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - - theta = py_utils.NestedMap() - random_seed = tf.constant(123) - decoder_output = seq_sampler.Sample( - theta, encoder_outputs, random_seed, InitBeamSearchCallBack, - PreBeamSearchStepCallback, PostBeamSearchStepCallback) - - ids, lens = self.evaluate([ - decoder_output.ids, - tf.reduce_sum(1 - decoder_output.paddings, 1), - ]) - print(np.array_repr(ids)) - print(np.array_repr(lens)) - expected_ids = [ - [0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [0, 0, 3, 3, 1, 0, 3, 0, 1, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - ] - expected_lens = [5, 11] - self.assertAllEqual(expected_ids, ids) - self.assertAllEqual(expected_lens, lens) - - # Now do the same, except with use_stop_fn=True. - p = target_sequence_sampler.TargetSequenceSampler.Params().Set( - name='bsh', target_seq_len=tgt_len, target_eoc_id=0, use_stop_fn=True) - seq_sampler = p.Instantiate() - decoder_output = seq_sampler.Sample(theta, encoder_outputs, random_seed, - InitBeamSearchCallBack, - PreBeamSearchStepCallback, - PostBeamSearchStepCallback) - - ids, lens = self.evaluate([ - decoder_output.ids, - tf.reduce_sum(1 - decoder_output.paddings, 1), - ]) - print(np.array_repr(ids)) - print(np.array_repr(lens)) - expected_ids = [ - [0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [0, 0, 3, 3, 1, 0, 3, 0, 1, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - ] - expected_lens = [5, 11] - self.assertAllEqual(expected_ids, ids) - self.assertAllEqual(expected_lens, lens) - - def testTargetSequenceSamplerWithVariables(self): - with self.session(use_gpu=False): - np.random.seed(9384758) - tf.random.set_seed(8274758) - hidden_dim = 8 - vocab_size = 12 - src_len = 5 - tgt_len = 7 - batch_size = 2 - keras_layer = py_utils.NestedMap(dense=None) - - def InitBeamSearchCallBack(unused_theta, unused_encoder_outputs, - num_hyps_per_beam): - self.assertEqual(1, num_hyps_per_beam) - logits = tf.zeros((batch_size, vocab_size), dtype=tf.float32) - return (py_utils.NestedMap(log_probs=logits), - py_utils.NestedMap(step=tf.constant(0))) - - def PreBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_step_ids, states, num_hyps_per_beam): - self.assertEqual(1, num_hyps_per_beam) - hidden = tf.random.stateless_normal([batch_size, hidden_dim], - seed=[8273747, 9]) - # A stateful 'dense' layer. - if not keras_layer.dense: - keras_layer.dense = tf.keras.layers.Dense( - vocab_size, input_shape=hidden.shape) - log_probs = tf.nn.log_softmax(keras_layer.dense(hidden)) - return (py_utils.NestedMap(log_probs=log_probs), - py_utils.NestedMap(step=states.step + 1)) - - def PostBeamSearchStepCallback(unused_theta, unused_encoder_outputs, - unused_new_step_ids, states): - return states - - src_enc = tf.random.stateless_normal([src_len, batch_size, 8], - seed=[982774838, 9]) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float32) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - - theta = py_utils.NestedMap() - random_seed = tf.constant(123) - p = target_sequence_sampler.TargetSequenceSampler.Params().Set( - name='bsh', target_seq_len=tgt_len) - seq_sampler = p.Instantiate() - decoder_output = seq_sampler.Sample(theta, encoder_outputs, random_seed, - InitBeamSearchCallBack, - PreBeamSearchStepCallback, - PostBeamSearchStepCallback) - - self.evaluate(tf.global_variables_initializer()) - ids, lens = self.evaluate([ - decoder_output.ids, - tf.reduce_sum(1 - decoder_output.paddings, 1), - ]) - print(np.array_repr(ids)) - print(np.array_repr(lens)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/task_scheduler.py b/lingvo/core/task_scheduler.py deleted file mode 100644 index 94597e33..00000000 --- a/lingvo/core/task_scheduler.py +++ /dev/null @@ -1,324 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Multi-task task sampling schedules.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import early_stop -import numpy as np - - -class TaskScheduler(base_layer.BaseLayer): - """Generic multi-task scheduler. - - Subclasses should override the `Sample` method to return a task string given - a step. All of the task strings as well as additional hyperparameters needed - by `Sample` should be exposed and stored in the params. `Sample` should also - update `cur_probs`. - """ - - @classmethod - def Params(cls): - """Parameters for this task scheduler.""" - p = super().Params() - p.name = 'task_scheduler' - return p - - def __init__(self, params): - super().__init__(params) - self.cur_probs = None - self.SetVariableFree() - - def Sample(self, current_step): - raise NotImplementedError('Abstract method') - - -class AdaptiveScheduler(TaskScheduler): - """Tasks with low scores will be sampled more often. - - Scores are expected to be non-negative. Larger scores are better.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('tasks', [], 'List of tasks') - p.Define('expected', [], 'List of final expected scores') - p.Define('mh_a', early_stop.MetricHistory.Params(), '') - p.Define('mh_b', early_stop.MetricHistory.Params(), '') - p.Define( - 'epsilon', 0.05, 'Regularizarion term. A large epsilon will lead' - 'to a more uniform task distribution.') - p.Define('alpha', 1.0, 'Normalized task scores are raised to this power.') - return p - - def __init__(self, params): - super().__init__(params) - if len(self.params.tasks) != 2 or len(self.params.expected) != 2: - raise ValueError('Only two tasks are supported by this scheduler.') - - if self.params.epsilon < 0: - raise ValueError('Epsilon should be positive.') - - self.tasks = self.params.tasks - - self.last_scores = [0.0] * 2 - - self._metric_histories = [ - early_stop.MetricHistory(self.params.mh_a), - early_stop.MetricHistory(self.params.mh_b) - ] - - def getMetricHistories(self): - # If too slow, consider another implementation. - # TODO(sebjean) Time file reading and change behaviour if too long. - for index, mh in enumerate(self._metric_histories): - try: - with tf.io.gfile.GFile(mh.hist_file) as f: - lines = f.readlines() - except tf.errors.NotFoundError: - tf.logging.warning('File not found. ' - 'Expected at start of training only.') - score, lines = 0.0, [] - if lines: - try: - score = lines[-1].split()[-1] - except IndexError: - tf.logging.warning( - 'IndexError. Your history file may be corrupted.') - score = 0.0 - self.last_scores[index] = float(score) - - -class SimpleAdaptiveScheduler(AdaptiveScheduler): - """Simple adaptive scheduler. - - A task with a normalized score of `s` is approximately weighted as `1 - s`. - """ - - def Sample(self, current_step): - """Sample a task. - - The unnormalized probability of a task if given by - 1 + epsilon - min(1, score / expected)**alpha. - - Args: - current_step: Unused. - - Returns: - str, the name of the sampled task. - """ - del current_step # Unused - - self.getMetricHistories() - - alpha, eps = self.params.alpha, self.params.epsilon - probs = [ - 1 + eps - min(1, score / self.params.expected[index])**alpha - for index, score in enumerate(self.last_scores) - ] - probs = tuple(probs / np.sum(probs)) - sampled_task = np.random.choice(self.params.tasks, p=probs) - self.cur_probs = probs - return sampled_task - - -class InverseRatioAdaptiveScheduler(AdaptiveScheduler): - """Inverse ratio adaptive scheduler. - - Tasks are approximately weighed as the inverse of their normalized scores. - """ - - def Sample(self, current_step): - """Sample a task. - - The unnormalized probability of a task if given by - 1 / (min(1, score / expected)**alpha + epsilon) - - Args: - current_step: Unused. - - Returns: - str, the name of the sampled task. - """ - del current_step # Unused - - self.getMetricHistories() - - alpha, eps = self.params.alpha, self.params.epsilon - probs = [ - 1.0 / (min(1, score / self.params.expected[index])**alpha + eps) - for index, score in enumerate(self.last_scores) - ] - probs = tuple(probs / np.sum(probs)) - sampled_task = np.random.choice(self.params.tasks, p=probs) - self.cur_probs = probs - return sampled_task - - -class ShiftedExponentialScheduler(TaskScheduler): - """The unnormalized score of each task follows a shifted exponential function. - - Generalizes the constant, exponential and sigmoid - schedules described in "Scheduled Multi-Task Learning: From Syntax to - Translation" (Kiperwasser and Ballesteros). - https://arxiv.org/pdf/1804.08915.pdf - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'alpha', 0, 'Controls the rate at which the schedule changes. ' - 'A large alpha will lead to fast convergence toward final values.') - p.Define( - 'task_probs', [], 'List of 2-tuples (task, prob). For non-constant' - 'schedulers, prob is a tuple of the form (init_prob, final_prob).') - return p - - def __init__(self, params): - super().__init__(params) - assert isinstance(self.params.task_probs, list) - self.tasks = [] - self._descriptors = [] - - def Sample(self, current_step): - """Sample a task. - - Given an input [a, b] and a rate `alpha`, the unnormalized - score of eack task is a + b * exp(-alpha * t). - - Args: - current_step: int. Current time step. - - Returns: - str, the name of the sampled task. - """ - probs = [ - a + b * np.exp(-self.params.alpha * current_step) - for a, b in self._descriptors - ] - probs = tuple(probs / np.sum(probs)) - sampled_task = np.random.choice(self.tasks, p=probs) - self.cur_probs = probs - return sampled_task - - -class ConstantScheduler(ShiftedExponentialScheduler): - """Constant schedule. Tasks are sampled from a fixed probability distribution. - """ - - def __init__(self, params): - super().__init__(params) - - for key, value in self.params.task_probs: - self.tasks.append(key) - self._descriptors.append((value, 0)) - - -class ExponentialScheduler(ShiftedExponentialScheduler): - """Exponential schedule. - - For a task with initial and final probabilities p_0 and p_1 respectively, - its unnormalized score is given by - `p_1 + (p_0 - p_1) * exp(-alpha * current_step)`. - """ - - def __init__(self, params): - super().__init__(params) - - for key, value in self.params.task_probs: - self.tasks.append(key) - self._descriptors.append((value[1], value[0] - value[1])) - - -class SigmoidScheduler(ShiftedExponentialScheduler): - """Sigmoid schedule. - - For a task with initial and final probabilities p_0 and p_1 respectively, - its unnormalized score is given by - `p_1 + (2 * p_0 - p_1) * exp(-alpha * current_step)`. - """ - - def __init__(self, params): - super().__init__(params) - - for key, value in self.params.task_probs: - self.tasks.append(key) - self._descriptors.append((value[1], 2 * value[0] - value[1])) - - -class RoundRobinScheduler(TaskScheduler): - """Deterministic sequential schedule.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('tasks', [], 'List of task names. No repetitions allowed.') - return p - - def __init__(self, params): - super().__init__(params) - assert isinstance(self.params.tasks, list) - self.tasks = sorted(self.params.tasks) - self.n_tasks = len(self.tasks) - self.cur_probs = [1. / self.n_tasks] * self.n_tasks # For summary - self.next_task_idx = 0 - - def Sample(self, current_step): - """Sample a task.""" - sampled_task = self.tasks[self.next_task_idx] - self.next_task_idx = (self.next_task_idx + 1) % self.n_tasks - return sampled_task - - -class SequentialScheduler(TaskScheduler): - """Deterministic schedule that stays a fixed number of steps on each task.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'task_steps', [], 'List of tuples of (task_name, steps_for_task). Goes ' - 'through list sequentially in the specified order, staying ' - 'steps_for_task steps on task_name. On completing the schedule, ' - 'remains on the final task for the rest of the time. Assumes ' - 'p.task_global_step is False.') - return p - - def __init__(self, params): - super().__init__(params) - assert isinstance(self.params.task_steps, list) - assert self.params.task_steps - self.task_steps = [] - for (name, steps) in self.params.task_steps: - assert steps > 0 - if self.task_steps: - self.task_steps.append((name, steps + self.task_steps[-1][1])) - else: - self.task_steps.append((name, steps)) - self.n_tasks = len(self.task_steps) - self.task_idx = 0 - self.cur_probs = [1] + [0] * (self.n_tasks - 1) # For summary - - def Sample(self, current_step): - """Sample a task.""" - sampled_task, to_step = self.task_steps[self.task_idx] - if current_step >= to_step and self.task_idx < self.n_tasks - 1: - self.task_idx += 1 - sampled_task = self.task_steps[self.task_idx][0] - self.cur_probs[self.task_idx - 1] = 0 - self.cur_probs[self.task_idx] = 1 - return sampled_task diff --git a/lingvo/core/task_scheduler_test.py b/lingvo/core/task_scheduler_test.py deleted file mode 100644 index e892ea75..00000000 --- a/lingvo/core/task_scheduler_test.py +++ /dev/null @@ -1,198 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for task_scheduler.""" - -import os -import lingvo.compat as tf -from lingvo.core import early_stop -from lingvo.core import task_scheduler -from lingvo.core import test_utils -import numpy as np - -_NUMPY_RANDOM_SEED = 9885784 - - -class SchedulerTests(test_utils.TestCase): - - def _TestSchedulerHelper(self, schedule, global_step, count_a): - np.random.seed(_NUMPY_RANDOM_SEED) - - task_counts = {'a': 0, 'b': 0} - for _ in range(100): - task = schedule.Sample(global_step) - task_counts[task] += 1 - self.assertEqual(task_counts['a'], count_a) - self.assertEqual(task_counts['b'], 100 - count_a) - - def testConstantScheduler(self): - """Approximate expected probabilities: (a:0.8, b:0.2).""" - p = task_scheduler.ConstantScheduler.Params() - p.task_probs = [('a', 0.8), ('b', 0.2)] - - schedule = p.Instantiate() - - self._TestSchedulerHelper(schedule, 0, 83) - - def testExponentialScheduler(self): - """Test exponential scheduler. - - Approximate probabilities: - t=0: (a:0, b:1) - t=1e5: (a:0.63, b:0.37) - t=1e10: (a:1, b:0) - """ - p = task_scheduler.ExponentialScheduler.Params() - p.alpha = 1e-5 - p.task_probs = [('a', (0, 1)), ('b', (1, 0))] - - schedule = p.Instantiate() - - self._TestSchedulerHelper(schedule, global_step=0, count_a=0) - self._TestSchedulerHelper(schedule, global_step=1e5, count_a=63) - self._TestSchedulerHelper(schedule, global_step=1e10, count_a=100) - - def testSigmoidScheduler(self): - """Test sigmoid scheduler. - - Approximate probabilities: - t=0: (a:0.5, b:0.5) - t=1e5: (a:0.73, b:0.27) - t=1e10: (a:1, b:0) - """ - p = task_scheduler.SigmoidScheduler.Params() - p.alpha = 1e-5 - p.task_probs = [('a', (0.5, 1)), ('b', (0.5, 0))] - - schedule = p.Instantiate() - - self._TestSchedulerHelper(schedule, global_step=0, count_a=54) - self._TestSchedulerHelper(schedule, global_step=1e5, count_a=73) - self._TestSchedulerHelper(schedule, global_step=1e10, count_a=100) - - def _setupTestAdaptiveScheduler(self, p): - logdir = tf.test.get_temp_dir() - tf.io.gfile.mkdir(os.path.join(logdir, 'decoder_dev_a')) - tf.io.gfile.mkdir(os.path.join(logdir, 'decoder_dev_b')) - - early_stop.MetricHistory.SetLogdirInMetricHistories(p, logdir) - - p.epsilon = 0.05 - p.tasks = ['a', 'b'] - p.expected = [0.3, 0.5] - - mh_a = early_stop.MetricHistory.Params() - mh_a.jobname = 'decoder_dev_a' - mh_a.metric = 'corpus_bleu' - mh_a.logdir = logdir - mh_a.local_filesystem = True - - mh_b = early_stop.MetricHistory.Params() - mh_b.jobname = 'decoder_dev_b' - mh_b.metric = 'corpus_bleu' - mh_b.logdir = logdir - mh_b.local_filesystem = True - - p.mh_a = mh_a - p.mh_b = mh_b - - schedule = p.Instantiate() - - early_stop.MetricHistory.ConditionalAppend(mh_a.jobname, mh_a.metric, 1, - 0.05) - early_stop.MetricHistory.ConditionalAppend(mh_b.jobname, mh_b.metric, 1, - 0.25) - - return schedule - - def testSimpleAdaptiveScheduler(self): - """Test simple adaptive schedule. - - Probability of task a: - (1.05 - 0.05/0.3) / ((1.05 - 0.05/0.3) + (1.05 - 0.25/0.5)) /approx 0.616 - """ - np.random.seed(_NUMPY_RANDOM_SEED) - - p = task_scheduler.SimpleAdaptiveScheduler.Params() - schedule = self._setupTestAdaptiveScheduler(p) - - self._TestSchedulerHelper(schedule, 0, 63) - - def testInverseRatioAdaptiveScheduler(self): - """Test simple adaptive schedule. - - Probability of task a: - 1.05/(13/60.) / (1.05/(13/60.) + 1.05/(11/20.)) /approx 0.717 - """ - np.random.seed(_NUMPY_RANDOM_SEED) - - p = task_scheduler.InverseRatioAdaptiveScheduler.Params() - schedule = self._setupTestAdaptiveScheduler(p) - self._TestSchedulerHelper(schedule, 0, 71) - - def testRoundRobinScheduler(self): - """Test round-robin scheduler.""" - p = task_scheduler.RoundRobinScheduler.Params() - p.tasks = ['a', 'b'] - - schedule = p.Instantiate() - for global_step in range(20): - task = schedule.Sample(global_step) - if global_step % 2 == 0: - self.assertEqual('a', task) - else: - self.assertEqual('b', task) - - def testRoundRobinSchedulerEvenStep(self): - """Should work regardless of step increment.""" - p = task_scheduler.RoundRobinScheduler.Params() - p.tasks = ['a', 'b'] - - schedule = p.Instantiate() - tasks = [] - - for global_step in [0, 2, 10, 17]: - tasks.append(schedule.Sample(global_step)) - self.assertEqual(['a', 'b', 'a', 'b'], tasks) - - def testSequentialScheduler(self): - """Test sequential scheduler.""" - p = task_scheduler.SequentialScheduler.Params() - p.task_steps = [('a', 8), ('b', 10), ('c', 2)] - - schedule = p.Instantiate() - tasks = [] - - for global_step in range(25): - tasks.append(schedule.Sample(global_step)) - expected_tasks = ['a'] * 8 + ['b'] * 10 + ['c'] * 2 + ['c'] * 5 - self.assertEqual(expected_tasks, tasks) - - def testSequentialSchedulerUnevenStep(self): - """Sequential schedule uses global_step even with uneven step increments.""" - p = task_scheduler.SequentialScheduler.Params() - p.task_steps = [('a', 8), ('b', 10), ('c', 2)] - - schedule = p.Instantiate() - tasks = [] - - for global_step in [0, 2, 10, 17, 21]: - tasks.append(schedule.Sample(global_step)) - expected_tasks = ['a', 'a', 'b', 'b', 'c'] - self.assertEqual(expected_tasks, tasks) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/test_helper.py b/lingvo/core/test_helper.py deleted file mode 100644 index ba80e3c2..00000000 --- a/lingvo/core/test_helper.py +++ /dev/null @@ -1,23 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Helpers for unittests.""" - -import os - - -def test_src_dir_path(relative_path): - return os.path.join(os.environ['TEST_SRCDIR'], '__main__/lingvo', - relative_path) diff --git a/lingvo/core/test_trainer_utils.py b/lingvo/core/test_trainer_utils.py deleted file mode 100644 index a2f1665a..00000000 --- a/lingvo/core/test_trainer_utils.py +++ /dev/null @@ -1,83 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Helpers for trainer-based tests.""" - -import os - -from absl.testing import parameterized -from lingvo import trainer as trainer_lib -import lingvo.compat as tf -from lingvo.core import test_utils - -FLAGS = tf.flags.FLAGS - - -def _ModelTuples(model_classes): - return [(model_class.__name__, model_class) for model_class in model_classes] - - -def MakeModelValidatorTestCase(model_classes): - """Returns a TestCase that validates the training for `model_classes`. - - Usage in a test: - - MakeModelValidatorTestCase([path.to.module.MyRegisteredModel1, - path.to.module.MyRegisteredModel2]) - - Args: - model_classes: A list of model classes. - """ - - class _ModelValidator(parameterized.TestCase, test_utils.TestCase): - """TestCase template for validating training and decoding models.""" - - def TrainerBuilds(self, model): - tmpdir = os.path.join(FLAGS.test_tmpdir, model.__name__) - - # Trainer should probably create these directories in the future. - tf.io.gfile.makedirs(os.path.join(tmpdir, 'train')) - - model_params = model() - cfg = model_params.Model() - cfg.input = model_params.GetDatasetParams('Train') - cfg.cluster.mode = 'sync' - cfg.cluster.job = 'trainer_client' - _ = trainer_lib.Trainer(cfg, '', tmpdir, tf_master='') - - def DecoderBuilds(self, model): - tmpdir = os.path.join(FLAGS.test_tmpdir, model.__name__) - tf.io.gfile.makedirs(tmpdir) - - model_params = model() - cfg = model_params.Model() - cfg.input = model_params.GetDatasetParams('Train') - cfg.cluster.mode = 'sync' - cfg.cluster.job = 'decoder' - cfg.cluster.task = 0 - cfg.cluster.decoder.replicas = 1 - _ = trainer_lib.Decoder('train', cfg, '', tmpdir, tf_master='') - - # Each of these take about 10-20 seconds to run, to build the model and - # execute the forward and backward pass building. - @parameterized.named_parameters(_ModelTuples(model_classes)) - def testTrain(self, model): - self.TrainerBuilds(model) - - @parameterized.named_parameters(_ModelTuples(model_classes)) - def testDecoder(self, model): - self.DecoderBuilds(model) - - return _ModelValidator diff --git a/lingvo/core/test_utils.py b/lingvo/core/test_utils.py deleted file mode 100644 index ffe8cd56..00000000 --- a/lingvo/core/test_utils.py +++ /dev/null @@ -1,212 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Helpers for unittests.""" - -import inspect -import re - -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import py_utils -import numpy as np - -FLAGS = tf.flags.FLAGS - -# Enable tf.function when eager execution is on-by-default, which is the case -# when: -# - the test target doesn't depend on the disable_tf2 target, and -# - --define=tf_api_version=1 is not specified during the build. -# -# TODO(laigd): remove TF version check when 312743821 and 313682500 are in the -# release. -if tf.executing_eagerly() and tf.compat.v1.__version__ >= '2.3.0': - try: - FLAGS.if_use_tf_function = True - FLAGS.while_loop_use_tf_function = True - FLAGS.call_defun_use_tf_function = True - except tf.flags.UnrecognizedFlagError: - pass - -# Disable eager execution for all tests. -tf.disable_eager_execution() - -tf.flags.DEFINE_boolean( - 'update_goldens', False, - 'Update the goldens, rather than diffing against them.') - - -class TestCase(tf.test.TestCase): - """TestCase that performs Lingvo-specific setup.""" - - def setUp(self): - super().setUp() - # Ensure the global_step variable is created in the default graph. - py_utils.GetOrCreateGlobalStepVar() - - def _create_session(self, *args, **kwargs): - sess = super()._create_session(*args, **kwargs) - with sess.graph.as_default(): - # Ensure the global_step variable is created in every new session. - py_utils.GetOrCreateGlobalStepVar() - return sess - - def SetEval(self, mode): - return cluster_factory.SetEval(mode=mode) - - -def _ReplaceOneLineInFile(fpath, linenum, old, new): - """Replaces a line for the input file.""" - lines = [] - lines = open(fpath).readlines() - assert lines[linenum] == old, ( - 'Expected "%s" at line %d in file %s, but got "%s"' % - (lines[linenum], linenum + 1, fpath, old)) - tf.logging.info('Replacing {}:{}.'.format(fpath, linenum)) - lines[linenum] = new - with open(fpath, 'w') as f: - for l in lines: - f.write(l) - - -def ReplaceGoldenSingleFloat(old, float_value): - m = re.match( - r'(?P.*)\bCompareToGoldenSingleFloat\(' - r'(?P[^,]+), *' - r'[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?, *' - r'(?P.*)\)(?P.*)\n', old) - assert m - return ('%sCompareToGoldenSingleFloat(%s, %f, %s)%s\n' % - (m.group('prefix'), m.group('testobj'), float_value, m.group('v2'), - m.group('postfix'))) - - -def ReplaceGoldenStackAnalysis(new_float_value): - """Analyze the stack trace to figure out how to update the golden value.""" - src_file_frame = None - for frame in inspect.stack(): - if frame[-2] and 'CompareToGoldenSingleFloat' in frame[-2][0]: - src_file_frame = frame - break - assert src_file_frame - runfiles_pattern = 'runfiles/[^/]+/' - fpath = src_file_frame[1].split(runfiles_pattern)[-1] - line_num = src_file_frame[2] - 1 - old_line = src_file_frame[4][0] - new_line = ReplaceGoldenSingleFloat(old_line, new_float_value) - return fpath, line_num, old_line, new_line - - -def CompareToGoldenSingleFloat(testobj, v1, v2, *args, **kwargs): - """Compare golden value with real value. - - When running the bazel tests with FLAGS.update_goldens to be True, this - function automatically updates the golden value in the test file if there is a - mismatch and the calling site of CompareToGoldenSingleFloat is a 1-liner. E.g. - Code:: - - test_utils.CompareToGoldenSingleFloat(self, 0.3232, input_batch.label) - - works but this will not:: - - test_utils.CompareToGoldenSingleFloat(self, - 0.3232, - input_batch.label) - - Args: - testobj: A test object, such as tf.test.TestCase or test_utils.TestCase. - v1: the golden value to compare against. - v2: the returned value. - *args: extra args - **kwargs: extra args - """ - if not FLAGS.update_goldens: - testobj.assertAllClose(v1, v2, *args, **kwargs) - else: - _ReplaceOneLineInFile(*ReplaceGoldenStackAnalysis(v2)) - - -def PickEveryN(np_arr, step=1): - """Flattens `np_arr` and keeps one value every step values.""" - return np_arr.flatten()[::step] - - -def ComputeNumericGradient(sess, - y, - x, - delta=1e-4, - step=1, - extra_feed_dict=None): - """Compute the numeric gradient of y wrt to x. - - Args: - sess: The TF session constructed with a graph containing x and y. - y: A scalar TF Tensor in the graph constructed in sess. - x: A TF Tensor in the graph constructed in sess. - delta: Gradient checker's small perturbation of x[i]. - step: Only compute numerical gradients for a subset of x values. I.e. - dy/dx[i] is computed if i % step == 0. - extra_feed_dict: Additional feed_dict of tensors to keep fixed during the - gradient checking. - - Returns: - A Tensor of the same shape and dtype as x. If x[i] is not chosen - to compute the numerical gradient dy/x[i], the corresponding - value is set to 0. - """ - - x_data = sess.run(x) - x_size = x_data.size - x_shape = x_data.shape - - numeric_grad = np.zeros(x_size, dtype=x_data.dtype) - - # For variables we need to issue an assignment operation in order to update - # the value of the variable. This is because with resource variables x will be - # pointing to the handle rather than its value. - feed_dict = extra_feed_dict or {} - ph = tf.placeholder(x_data.dtype, x_shape) - x_assign = x.assign(ph) if isinstance(x, tf.Variable) else None - - for i in range(0, x_size, step): - x_pos = x_data.copy() - if x_size == 1: - x_pos += delta - else: - x_pos.flat[i] += delta - if x_assign is None: - feed_dict.update(dict([(x, x_pos)])) - else: - sess.run(x_assign, feed_dict={ph: x_pos}) - y_pos = sess.run(y, feed_dict=feed_dict) - - x_neg = x_data.copy() - if x_size == 1: - x_neg -= delta - else: - x_neg.flat[i] -= delta - if x_assign is None: - feed_dict.update(dict([(x, x_neg)])) - else: - sess.run(x_assign, feed_dict={ph: x_neg}) - y_neg = sess.run(y, feed_dict=feed_dict) - numeric_grad[i] = (y_pos - y_neg) / (2 * delta) - - # Restore the variable back to its original value to avoid breaking any - # further test code that operates on the graph. - if x_assign is not None: - sess.run(x_assign, feed_dict={ph: x_data}) - - return numeric_grad.reshape(x_shape) diff --git a/lingvo/core/test_utils_test.py b/lingvo/core/test_utils_test.py deleted file mode 100644 index 7325c0b9..00000000 --- a/lingvo/core/test_utils_test.py +++ /dev/null @@ -1,53 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for test_utils.""" - -import lingvo.compat as tf -from lingvo.core import test_utils - - -class TestUtilsTest(test_utils.TestCase): - - def testReplaceGoldenSingleFloat(self): - old_line = ' CompareToGoldenSingleFloat(self, 1.489712, vs[0])\n' - expected = ' CompareToGoldenSingleFloat(self, 1.000000, vs[0])\n' - actual = test_utils.ReplaceGoldenSingleFloat(old_line, 1.0) - self.assertEqual(expected, actual) - - old_line = ('test_utils.CompareToGoldenSingleFloat(self, -2.e-3, vs[0])' - ' # pylint: disable=line-too-long\n') - expected = ('test_utils.CompareToGoldenSingleFloat(self, 1.000000, vs[0])' - ' # pylint: disable=line-too-long\n') - actual = test_utils.ReplaceGoldenSingleFloat(old_line, 1.0) - self.assertEqual(expected, actual) - - def CompareToGoldenSingleFloat(self, unused_v1, v2): - return test_utils.ReplaceGoldenStackAnalysis(v2) - - def testReplaceGoldenStackAnalysis(self): - v2 = 2.0 - result = TestUtilsTest.CompareToGoldenSingleFloat(self, 1.0, v2) - self.assertTrue(result[0].endswith('test_utils_test.py')) - old_line = (' result = TestUtilsTest.CompareToGoldenSingleFloat(' - 'self, 1.0, v2)\n') - new_line = (' result = TestUtilsTest.CompareToGoldenSingleFloat(' - 'self, 2.000000, v2)\n') - self.assertEqual(old_line, result[2]) - self.assertEqual(new_line, result[3]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/testdata/BUILD b/lingvo/core/testdata/BUILD deleted file mode 100644 index 825ef81d..00000000 --- a/lingvo/core/testdata/BUILD +++ /dev/null @@ -1,10 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) # Apache 2.0 - -filegroup( - name = "lenet_test_model", - data = glob([ - "lenet_test_model*", - ]), -) diff --git a/lingvo/core/testdata/lenet_test_model.data-00000-of-00001 b/lingvo/core/testdata/lenet_test_model.data-00000-of-00001 deleted file mode 100644 index 9ba4d73b3e79e151a0ee54ddcaeee72f9553610d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9165752 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

MA>X(>{JLfyW_BTf4j=vF}wNfK{ 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%0D{5(s&3%cUTHi?UIDA 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໻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%0A42C}S7AYQ9zN?_$Q$TP!KH^^;GbLz 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 *#B9XToPSaxHiv zdM|$t77=w^O_@EY{5j5*$#vtqtN-xG5^u6+XCnT(?@FTIRp7wkRcN_~`G1a%qNfZV z=C00AN59{xTwb~)T~;VYJ4gNpn{6^6S<#jnb*7=e?^)0`euJ;3IT2r%Uc9t$BG`E8 zq2@|YI>_?2hxh-5rM2vpS#%fX+)o36&2`R*vAJ`V??CcpUrd>G7CD_;oasgO&Yb&~ z`&85qPYRph{x)->pY26m1Ee9@wHHntcP8#$nP?Ew4YT%s7bxjJ0*$#|G^p$m-sv@> zRVM9%XH$Q};jcmXEVK@v%=&@J3sUiilpdAkJm|evbDX_64z4OZhMhM$LF$(T&8bU- zy?ckDD^i6tO_+lj1>Pj$x&x_-{~8UGK;IL&S_i%d|?C>qXx)e9+>}86-7Ysm`gFLzU;~82$-icZf>zKb)ggohJ#G#x8 zc*WbAB))zEPxfAfQS!y;x9ceEUVa+?9QCGW$10OFtsQ7RL5((8gaG&QF;>Zsg6w{6 z5_P{tQ0yqmuU;%p9G7~MZy&SpcDgbRt2{0E`r8^ST6?hiyau_j$D3~5S%A@uOVTE9 zN6p*fA&_yFR{nLPn`I;Uk{KHC_Jaxe$KS-$vx0;Vzny{f|3-td|0b_4GJ-19greMT zLkL>18I+vsJYSfzynoF&h&Xi|e}sw*?IbMo@M{!DYS;ts(m{KoT#~h^U;iq8~ zbEJj`W#Q5nw$z4YT;3(c!Cl?=5Tjm=}~F9D{k^VWYhsT{Jk7j{lR!3l*)%-PP~mQ;9iLnp#nyzvC^;_T2}MI)d4mJ&s!#szU$%9nG!S+7IK7 zC8N$gS^6*A6tlNH#5V!f5OicKtgFzcZ!})Q-ZT?B6O`#G!9#(oPCV$H5~pJV{$hQq zK3VQNjgu{k5L{$;)V0m#q^|uPE;#xc_p=__Ruav2_Wj%PX zjBQT4GTF1{3M3@NLU@oKzi)IA%G@4}^Hx9MZPoVS%K$Z!Gb{mSKHt%Dk_~pBjs~yR z9#Aj7j;fE@%&pcAuc+*YGq-(V+t+43t0W7HZ-$^zKo{?nV9uP*f!Ik6NZbKmtO|aP z9c-2qc|rxJuwGoqBgQU#=*%BKqz-}YGx@|6SrSs82cNoM;^?96+*8RwELFKE=z&A1 zP(G2bW___H^Dfww$GCPyK4@dBN`8M8p$$!rB+BjtPEPZNkm-yq@9xdTHjE+>tpD^N zpJm$)>X3-MN4zk}f&5*O2ziAP^vCKlq4Y#Q9BF(E-8RMJhnh2RzHdIL1PnZs{-Q^H zcUyz!a!=4~uH~AFEQ!h^8RA!{Nd$(=;9^(=W-QEx6H_c`?3?u%G$I_99D52|9$$ik zye-{3EKbAMnleGN9?`8*rTbO9h{kst@?hvQf7!g8-$vEQ*P3tqx6Tth_b&y7eumWi zrZ|q6KZ@FHY{OaecJhy_zi{oZZsV55(fq;9p)k2eOt4X}hrf1Lhgik=;H_;(&^)06 zH{Pg*r<>|w>+~Aoe@2p|O(Ghu%ufM}2fAp%vTJ<`jLVO^Vey9=Ec5mzu zQFQ*!sc7Aj0rJfE!gW8u-RG-O!b*$y23xaCxif8<@*JnxrE~e3g#L2;hz9RkVR`)* ztW}?eA7h-z`luXGJt#t>12zk4r0j5dha`p@^x)=2deoJZBW3d>$u-en%n#&IjtfCa z-x&}vz6Vu`Dsk}hPb{myDV%qC0M!nyfx(v#IWsAJqCNTonxw>|YwQRj)clQIUj(SQ znz0FMkyCY?hW;h4kSjh79J+sDciaoC6Vs*7GdsC)lk#BBJfKx$HR$Jo9B$y+8vGHI zgtz>AAwTT`H|yLR_|`Cn&jh3N9U^1`HKx`x$ihm>wqKiQ1u6In^kDW2}sf+ zN4(}^MdJs$v2RsAE-UdM<{a{I%{h?bY)Vdzt7hEZI|!qhx6-{1hGtY@+}rEe`9+Ml zCSSp}|IA2By$Wr+{szhqJr>qb1zMsp4@Cct!1X0pFtWWuVA<&ozK0ofWTP0p@IsWV zc)p1Lz4|q-==z4Ki*3jehb~Y!?epm6tA4OrdkA*BijaFO-`(Hahi9``F0f}DUe7>W z){uZj*?MsH!3NN=egFX*n&I4$%g`8B!5go5gi5>Rh=KAqI6SHt%g&AxW~`aRyZ`Bj zEx)f|#)dxdV>ifPR|T@)H4CC->-dIQ;?y+U2v6r978v(3#%y5+PCl(b;>>d3fL1Cu z)7zc&nF1BvX+X#CHKyH4>~6Q?1_lHMf`Obgu~JTjIoj=DCh0}qPGru9sRy84&XcaK ztj0gf|MF*UhjX|`$Zh>-M1ra|bC*VTpf+<6uU{xldOmTOCgMuhgkI-u21XE@o$v9Z z!%^O^N|TJcLNPjM55IQkHC){6NaAf%IPt2@sDJM*n&T7RM9G_!&bTHp84{sNtCk3b z$A+Qpa|xa_v1J*eF3xqAEpbt}h$?ws!81#o`dJOZ{{N;!RjC_&`}qOy_g0Ji+#pUw z8l9++J-=KM?Wj`bNjNya9JXjY=MPv((?XU_U!SZDM;o@op-m{@=0(_0 zZ^0}7?t3IZ%?(t9L!K?wX7tsfA|B?MQd@I3*k7qfl6HEL?kQ5Fz}SgEg9I*96~WD8 zWT7B9iLo4I=$^8JxK15#(@+vbe-|Y$AAH1%8^_`lm%GsXdnWeG_M~SXP64-OZ@R|) zFNB1hz{O$rK+tAS{;~hB=Ia^U6wd)J@r1M4Dx~J45e&9AX1Kc}}VApfjv0;o|`Ho6Zo~BO>YCqyYp#?9yQ5}-jb>NSdW(*b) zq4VS~!==OWbaS~e8UI;{u6VKrcE)Ss)?YoaHmDflUPt4ZUIlF3)B_uAn2NSSlL)P@ zb3af#?4c`sb%4 z?{9oxcu$;VcPf zyORTW!A?tj0EXacFUjvsGX{mNVz6{xDRhOKP}N(f`I;O@Iy}J&0+@3kq12V8WE&BU zZ!**;9q=2=PC_8}V2`k9exltZHu>+TlW!$8E)V zE{VA8ixe99b#g{-Kq@wlpvvjp+{A?{FA zVpb$_Sq5w$JOyL!$dO_OS5La!!D)+1^SyRi;CK8V*t72kF6U6Z%?(M8G3jL;m~~J8 zV9VC=6?m(Nn9p`SlXZL^a{-zzi$Zt1r7)Nki+i6f0eR=GC}L_u zt~~pK(X%AUvoT6^=+rz=3QEUJdB%rx%LIJZfOVB-v>`}>*8eghgI-2NVDKE?{AW#* z|2xkg4|b#B!49AfCeUdyZLSL?V3IU>5q42cMo;&fp6DS=JrSh2I9~@tm70sfPyadAMTUO1NMnU{3Ei5ROXXV@2F4Dh1*@Yf+GUep!&` zAx1jQfAVAfThQ)WzF@}4bV#uyZbrj|>d z@)L@eYLbpVHp@&66nv3%CJqiFbVJktsBaM^Ij8HP{KFH$hpU@0`cN6n%v%cq2mazb zwPvuc6C*mKd!S$R0-EktCWRBF=*Y~VLKZI>s|r9>*fuMiAOd_ijw2?=(hfEI5EQ*BhE4R)SnmdRc$Yf zI{gPr5B$V?Y{q!2_B?<6$R(T<@CHsBj3CjAcH(-|pD^h38}%m{Qr!|uShAMFn7FZ6 zoqrGKNhr_;CeoR|!iX$l9$lq@Mow8V2gB6O$m|Qp_!9;ORN;~+6^tzaOt=VJ zJvIg^XS~LFo|ZHzz?mk5XY*;(tHIbc7u(o=<>)Q}jVaTkGe=m`Z3U*dA*P=H&&ZdZ zg$2amxj8vAy#>QY7}F^(W~6n>Y2mN$a^&0l8!&jtmHU1>MQ~?i1URN`#-gHj+*0a9 zXlM;@Q)y2Yx~kK0Vb5W{*mPVoMTSfcv!(XCShu|<17Ec$$o7-^U%O65;r^B4ot;7Hqw#Lbu0RP@_RmPYLax~_(D zc^kU;*In)CUAci@b6<|^!2*79iZlJYcoG*bx)BHC#HsVTRIX-TE-nyl;+AQQg*97Q zPP8H)9c}m-@h!vIOuw;7(J04`Z9;O-{S;4?p9w3cUUG0u-mSOigelI$HH( z)a)mavV9A@wXKH!Lq?=tUV>KIYU8llRzBh(Fxc&R)SKZ=l9q*F$!j%;?R(GXO~}Ku zZ~~Sa_h9m4H+mxZ1J^va19wmX?cAW~|-AWa8m9w-8_uJ8CYMT!24e-0Ww zP4L9D3yp#$sLx&nQjyL&$O$KLOq?BwhVzSjcZSVDMU^1nj6DhEqy!<`nHNuYBCeRF z16nHmz=ry?eXTta5fftVX*F7-7zt`OpTNzQ(O~^GihHq&y_;R`fsykMJ|K6H?|Lte zL5>#WoY{LwS2+a6Pn)sPIt_CrGtek;6y5fF72nqo4IAP=bI)8ky5yY#os*@3H@aU# z(nx-=GbRmxp|3#`elY8!HOzXUdxZvAT)U3P-C;ZgF(*{apdR!FR zetN*i9$yF7FGu3n>tb~IJ`=LB^)-Al@gSRJZ^P?`%Q!L2ojiEKa!7N(@?Uf$X+>2T zA7CrZ|2+K*s`ajc&6@KU6M9I+&ANDQ6=$W#UYXnYa_j-d+gV8)CrXnjRSVr^9h$ zNwP9=8{aYLPXD8?`Km=_aK7ssI6UIP>)!%+uueeJ-M_-kLQSd^wHz;r*F(Rz1YKV6 zg7X-yLyq&2SSjH^$E!<_bmkRM_u2`@m)?O#O*{YP^L{w{%b9#$a+j+tGNz|R>tW}W zQ+!RqS#X43+@WJXaQiO_V$i_;wpXLj`{QE>TEtksxrX!}!hg)k#OsbIeG!BwnktAh6~&AM(S5>iZu+7dso&6#W4QyJEpwB3!WjjWs;WVWSDcezg`eF`~Z?f5m$rJp$$(1MsCd0o29%N+CJt$zl zqxhl?(7ap~6=sil)H$t-OS;g?|7_?0Bj!b#5j;h(rSd(@j`#w4lU=yo75OZ0#yVn8 zF5qD+Eh=DZpW;87G(e;O0IK?#&^`P0Y1tDm+L~cTx)vuxj_oJ7kZQ|gkRGUE*fJ(JE2QJw$*Zf4zhXjG<(R%T7c5AQBa_;63_Q|(e}F%)KZ(d z+=dxvXn`T^o7%(IFy3`LJJ0n;%)$uS9ya@t;6no6;qj>zeC%{Y*=SX-E(?7c@e944 z?UF^2>8^C|>~wtaZV)og+=HIPe17YsTku#l6Hk_CQu*a-wEhOmbFsVHzOkmH;<^!i zx}E3#vTWk-f5+jw(;B!qV>BHYQYGf+uR_?UOsJ3rVio^~+u}Nocb^pk(UzOhx4s-;9CY;=O z8MNQ?I9t08&m=zp^%mAgH+Lh$y?gj68^Z)?I=4B2!cxY7mLdDsFt4mg0iSfzn%aEz zAQvKZi0_@fIQP&M-l@)-)SKQ#O*0M1GCac-oim{G*luX`egl&E=@0mcJ%KecOVPB5 z%_ILSc#ay4E-Lw)!m+!2pmOoHMHaoRLHhBp}YpqUSy=rV)L=-gEV^`s9< zPKFBGf=6MJ&NuA)!5ClX-{7w-J33Y82LE-xH<^$iAXVP}keRg+a^gON4dW05q_#l3 zj{)@AGakNzJK~v=E7eUPC@2gRHE9$zu5+>nownbSj4S(yqRPt9HQA9doGj@9@ljM&-jbfI_P_^?#*pL+ z#A1d4CjUNV4_7d$bdIv;7-ax>bR0pWBQ(2H&CVp%^}>atP zxxmzgTw7;2*pAK;7@ZIySl^CUwv=Q4%9$7~(uaps$DzVjZ+cVrBHYf;1v0xAryC(( zwQ7gZ^;JFadH4C+LR(t?_mR-eX&&a&Z_wy)9|H0kAjbMU?1Tgu?mKW#j)iuKmQ_rU4gOK5(;gZ`A$ zCc8b|@WLKx>f)hGeA<{p&b$J2mj1+8)=@wBGo8;q5sLaNJ?J+TQP_O`JeR~tP}_D3 z^7+^(mUmr+dK!$Sm2F3s^qJ5Su}8Qtqymcvu0cL}(Tlc~DE(*)+NC(r#f2kDsm@bS z7)QO@_d3#nO;@<|3I+OLNe23aD%0TFX7F@$qX*Wk$I6~jG;X#kJ^#L%_i$nTJ)c{+ z_;nUm1&EQ4CgV^ydkwr;wE$j*USbTJR&Zngeo^5lQmZ6Ra&;_8WwJNj*I@%2C!fLb zMmv~~z8sDYx={KxOkZG9n=zF&{ssrO7T{-zG)`G71;xD8=>`)W49s+=OWk>- z3uJ*`tV*A4(}eAbBIIe04avW&PcAl;qWS$?Zr{2-eiGww9Pl>e?)>Q!t}`*DQa4XR z8Jj(CjtJviP1re5^aI?>T?DJmCgaPp4PaAmPdr*pi4Mze#>*H%eix6^GOW4x98193 zOas;2-?02UA!9bP--GpUJPn=bI_ybt^=Pi>@;@-0WJh-%S%}NW3dnEi^%(W)53Hxf ze7?6FoD2yA!_CKG(Yi_$-aG>O>K~vsONl;7)1_5HUDBPCk4nQIxKG#r0hf6OGT$uc z-p+OBX6-o*BJJ#XEES1uYhL54!e-{OAH=6;or&{DYcjPlAJl#`Z=RA1&h8U|Ti5Jp zSjrPj>(Am^&uQU^2RmT(f1zB#q5;Ouo(ucRI{C+gw#3738}1!jg4<&+fYSPRUa5Z^ zh};-N;~re$AHI1Bj=pWYL`fY?u(-$O2G-=w$!MS^vh?=weT?5~io?O}+^i`o)G2o& zY-{|)Iht(6rxPcEB0tM3F?TI@SgQ`>mSlnTra7>U^@LUweBmOsd%LPhyWc z*`&&Tm*bBLJ^Pi3cBU59Rtg5WJ_|&tJU(vmF*IQ;)DM5%VW3-yR5W>$=hHk%>$Z_J zuy7&g@?MT6bo)WYs1fk%;V@|Ke8p`(rwkHn1n83EL@(<)QX?0Z+4Gj67Y?6+k_m%Y z)w++5n!8A#b67x|uZoag8tl8;aRbcO*5mgkUHCU3Ls}n&@`pVYNL9)f{Lfc`&fO`^ za~bD!ZnE8hAObH&rNDGfiEP+qiEr-K!lGjmsMmdkJ&X6EXr>8SV|4`Y zxZ2RXfMLv)Vtt%b*8Jh7CA`dGX+7}mf4&)HJ66N6{BJEFL+JM$q-B%?I<`svW__%L`hn_5v^}#XVr;S`1!{v zkg{yyRhaX-EB=qb?{yMax$iG14raqH;6Z8pAed@w1nEOSR=By-;3>{DZmTZY;B5we zhb8EO#1L3!DuyNp)8X*kwY>G>Q=IU&DpAj_gq&<;BHSfHmCt?8t&P43mamzAN8$u* zjr@)$c;?KjkHMXGqiGTA7K};NrOHZjwAXtMx_r6Ig_cVRKdFp_FUIenV%7u5dtD2@ z_IhMy(lSobwN&WoBukz;b5L$MjH|nX8COV;z|Efy3t07KFk49226{rc45^F;uO?NSZm~JRbTOg_nFZ z$df07=sQc4c)xgr$9v0py&D;PEz3rn=zata8j4h_ryT7QYlX50!gHr*-M}4BN07m( zCe(gR4`!Br6uS3DU>;*^WUn`(wcjc^_|gJ;oHg%eZp>U6rsPuVI8e)72C?^=Flxqa zyq4IC_in6&cnveY_^TT!txXlg9NY$zvc1XX>%Jgj^A>hFoPcAQ9#m1RMi@HGh~G_L zdH<6apeL4j+GJ9M$5|)5wLAl4-6Or~ePVgLJX?CI-wjTw?gizi=H%eYEl|lT(2?>M zG-g6B&WX#0;Q$>n$Eh0M$ntSddH2;;6Fd2=}E%0^jGZgzID0pyiW0IKD!H zN_j8FNcCwD@Sq-goB&r})hF>UoQPL*A|?bof=k#4%2jX1keSy^d400isTP8>GFRx#rdA!&pA5dBvz?!;gmvK1kEBX_#Z z@UX^$lu4;j|BqtiWQ89dQXUWU!!)RV+*nw-a2r>6+a1#XIns~e&ZM2?5o20o;pDL* z+}IcnzwZ45_u6!v6@MCD#tEqAW-(ee(VOjr&T`HbgTU`KrB?HUg{MBd@Lx~Xz)tqg zarKLX1yU`X%#FX?5$jW2hL$85J3NwHTAcvy3WH$qJRRo$(j(gI#JJifJ@WYOVw8Pw z6XgD+6?iodd6pI$Q`#+{NQXyig5%WUO{TSFGSUho9RHnAD+$C31pj}gs#5(Sjaf16{1 z@zed2V7RCdhECjv9Rp%GZ}|rpuGFDZZmWQS*bf*HpikVH$NSc5XIk}Qn?U*?5P7f@ zgp@yr8M#VOUwjLkCmMp}w_ec78-j{jWmsx1AQvPYaq>?&(jF{MW!xI^hekSFGc_hd zx9#Yxk0NCHe8MM4rgQ!SuCR25J>OHsx*XCY$s47MXn*e(JiDe#zcId;zo|Km_cg>H z4u-^}P6H==>xS!RT&SS-IQ&p8;r~Q=qvn?~&NQ$9qVM%_FG~lYGyj)x`lb`8{w5s0 z_Ir{*JsH}!w~e1{77vr=GKW^X9@!cphXNaSa_J}QaF2Gw(JLgV-#{uiEN4viIOkzv zyA*9sPy{QtL5wW!!*WfYyShS}R(~JOGE)YgjpjI*b44$Psr)tUCkOottT%scH6%|Ea2<l(~g>2t*LHz`3>}Z5l`}(=EYB5r6Y)K!yW@qX5fO zPc~m>thkquzM~G3j6`UMXg=KDp9jGUZ@?Ud81CdT3DP)KgeV*La@#Ujb4dziIBc*B z-dNj$^Q5cXjR%s1m*0*zna8WRv z%Jg-#9eKw7X3mE-$rEc=ay;W6y5@SpoGix93(1CxO~t~~vHCQsGz4XqB|*PSFKA>o zf_{?)-RU_MH+bekqT4cDscS*LZEM0KB0tbHk9F6#T|%sCgz-xyNyeSyXu172&SQ+* zb&>(#0?}xgXbw(i{s=?wyOSdkiu8HNIjE7Xh5Y#zq)u>@Gl()GTDup+S-F#7mScs5 zr!?p}p#WRPHsK9vQ?ksa2})U?e}FO6o7T?9rdnBQpC%7EJ<3!+(Ub4jyai7^q`*WV zN$yOOrv)h{c*~Ui9xSflpX3~jmaK-Iy*FWq5sFWJbl~zt2}z$QO{a{%AoP_g!NM*^X9=);o+OztlX4&BWh0KF)=zb!fwImj@N=wk6_s z2|c^#3O9d&7fCtffpga%hy8jA81~qSdX-3!lkA`8WQX6v9v$=?+c0|`{F?w~d@951fpqO|X1$rU4 zTl+t}c_snZd^`#BV|$_c{a^mhqs`p52wl*aE&{WkN>aZg@^lTo#!Gy@2-RYN@Oa{7 zR0`;agI_GkGd2Soe|7}T3Q`2+4Q-wWhF9U^XD$4W+%ssq&jy{dtjV_DdT?=p51LGw zj4k&y8QZa)D@$P9&CX}JUUH{6nS4>|UTH)h98f2LE=Ht8FdG}MA4O%qBbY4ii@0VY zR@LqnWW1S+ZwmXk_iB!0^UhoxY0kVZWBYO50UKfwvmW1v9>dCSpZNRAo+RXfDp}C0 zMa%D+(Y`V(v{JPp%fEdG-<1)tk@;Y!j|zmZ3uI{Fg>!JbsSqw@9)Pv?i%@C#F$l?h z21~b}f*vbX8s>Tr{9ox3`-8<;yk3FGt6O=!nRy!|5AibPWB!oWl=u{a|;; zc6`&&hD5gmm6*@vm?87c&(+3uk55qi*PdQ9RU@h126R`hIiv|(@z4Wrm{&Ch{cmRT zb;CpG64Q!4lfYgpo=%W=5^8eO$bSbsVb*0+Cyg|Cb7&{(cQ48Ta~oF$VU+$#^YCU zA$ilxiLhcMBn}zlY!4a2K1*7C=p#JY{)b!p{0(%sUB$XRUi7NEIw`;Q1pCczf&Joz z{L?}&EQ(@zjDkhN4J><`kU5BfPGJa33b|vu$Kut+%i#DVD>`FR8CR-nOOGsw;EoQI z;nG{%PPuHGBLwh?iGjSzEt!J};X;~PWFoi!F^$LVKCj9<=K$2GN~X|UIy6ZM8xh~<8QB6#8!Z1 zXc-m*_9kl3k>8JyGal=5Cc-?`udw)t3cajlN=ov1bW~^;#yqs6dv`y>&!tX~;cQOy z_FTfv=4}uhXGSM%YXeQ`5?(BG2)w^u!EXu8Fm6WyZcER=hJYm)`B93LE-gmi23N)d zzl3^0Z*k^-UwMPa*>KC}BTg8}{GvXU@bs=HjgIrjtv%`(Ia&qxFS>#gGCA_w)`PTK z=uq7m9;C~{ithTp1Y&;KVMp^ZD4n_l4?H)ZYu~%l@TUm^uVo(eV%;SCsbxr3i?yTt z9_!qt=XbDW%XN61Jb=^JHGzfKaS-Ni#6|Igyw}7oNUM7wOqI#!^$&i)(|^8T-l9Dy z`1luM)P7^T?N6*eJ1pE5w}_XWAw$Kxov7NgWngL;hR4P^673a#Fw0q*JkQl5^`Djb zyQlPMp1leU9E!#Azn*+*X)gcD>3XbAnz?+@^Ld3hf9ea>}#KJWJ)P-l6Z_d>QqHCKkD zExvsEx`kY-5s-05f>7R37T@0Bz&%%w_(z?A59^dk*C!A9Q@$8db8msCpFCRch!?(W z`3FT!Pq9jG6(5)DPA6xiV{d~tnKySAnC_+Ajx2YutCJ*?#?-)uXnZy`zoh7Z7n@&}kZ%7AVQzm0cWHOLIZ zJ=kL_V61_g_`HPBEWrUBZeN23B(B5NXdmF;C1d`{zud#k7Px#&F6^6E2*>tl>IkwrxvQx9iqFTph}ur7bgfOg9uE$sYp(oE#V(D1;Tn$GrTQ#LAayz4AkG> zfzKCEPWa7$IX?qg2C_)l`uP#p{`@;y`wro7l@645snNO(6b{z{-P3Xjs&N;#8Ck$e z^FHj=YDAA5D>Brk(7^k>JaVCZfuX+xvE`TfFbGRSs z&P1W(DOg-$`57HW(t1>r92_d;hJ^EArt}w_>8k*$2BXOJ-RdN}teOkh9}f+V>9E}( z2)*w;gQd0}WJT#+u9ju+#v9rbg_sLyD7KqB-7f-R4#PM$g)y-9vYP!KHQE_ajxSp( z(c5Jg1nWP?rS2X?p!^A?J3_H<&jb)N&?H*lCc#eUvuM#OLi1WxiG7w5$-QGw+8}8n;OCGpNag>u}<5?S?S*->M!_1^r;KhNz>h`S;KHsG zu<^SlXH21o3i z$(-Q;Jo@J^hDIvUD@R;0(BKMZE3zI)2k@_wkc?G@yitpVrX1K{Kz&ZqBa5_ViD7RG=J-7lTPulg&8M*8wJ zGA|#@0{d`ce+Ve=7Nw>ii+PcugW!>M5O-(RW42#8H#k$APJS)GiwRa_@{GSc&-&+o zVl9|IL!2fpnZkK)h~_^X^o4Uh0T_C?5Wg2#(b>zj$qWX6G$|Yndw% z4;>3f8j?UZU_HuO3+Uh3)_l^zdwBKkSQJgjf$LS)p>aUct&oSSMAt!|NVU0>XJ$5Z4Pz9}DKR3|-5xAs<3h}~^`PbqOOUm+6Fg)bzgYzbDnEhQgnN)2OX17$RE%3bz{~jtH2Q8l%p2Fm{jGPv3-S5T=s235Q=J3vm+t0E zcB_zuqYcUK{1?#iH5c{XY171NTmJdUWK@jZil25ChD&nRpn+;j*cnC^nkIUYYQ{XhG@9Kd8a&Cd%Xd+7 zMKRo}I?bh&*MaIxHzbYLbnZ51;;&K)JIpy^%)MK0)p-)wt3`p=pR~jF(2j#V<^R8kEu$j#( zerZR-^Uts0ESr1X)E`aH4*6i{Vges*3s}yC@soAjXpprrG4$xlsOYU%U4CVG=` z6>;cfX+*4^Mha^`pW@W#OOogv4M3my@kVE_!`&T4g0TnAqPM*RjvIf2pU&xG#ZWWc zY#rbW7Tg7^6VbwY%Pdmfn6Y-L62mvRRu zZD*Y%%WH7@7ZFNE+L7oKz||%v!Ln8owvPMFo$MV&BE|oqY_uJxTPIHLPizC}YIUgm zmIV=WwdgMkU1Ate3)U-gasJamPR1}H_mI&~9GvrrJ1(t5?G~Am<1fO%x;dGD^X3A- z_o5dOiAaLO?nPKG?j+#<*+62E5qV&34F5v9I60#tVZxrdFnrboQtre;!QOUAD=p*X zH_pL~h)AsWXLC(npDbZB>COyAhzfd&OOJ2luWo39uqvqFN@cRk?o*BiLX@~{DcZScA06nKxQ0b$H)xV|YI z8+NI4-9N^{)7&B4e8HMtk+(&Wi;6_))&M7O)C1z?i7+u&g*;XSI(WbX_GuNs_vl0% zac4bbFZlu@72;IT>Bh2-gckmLj%SsWC@t!P_EnPfq}6>qcE^dx8rp#E-a_tWl^5Oh zbSh~6499`xsW_-T$_MzA6xPkrP2;hzgsb?}2x{(>4=U|o^WkrJ zc|tN5I}1qK7y~l!B9RxoH{#8gtOj?3D9HXJMkg%jfisPF@Sw{aJh{(|$o+SOv9Hu< z8T0iX9KkZFxvE6TcW?5e_T@kB*%MuU z;R#0wX)=SaW%3YLs7zKr{)W~+M#1+vuZ1y5Mubla2gAB&aC*NnecN>w$4@Eb_nR1z zC;ney?)D-mQohT3t+ygOUY4Te8OB`l@8$X@wu9v^Y2ttJ3qR|_V)U_b#;p@|scmSk z&^+-Y*BmBK-i(qZcUH;L3>_V^RYw$#^#sCu8F7-AphFKI)FV=m=4S$;Rr z-taZ1UA}=jEDPNc{sT>1viLJPgOGT@g;OeXAQ_8H$e)KAyx&|qe*Hc|ZNY}TGm#;u z*?VShbqZR&97Mm;8eDHzmz9D~>^*GUPz|#a zmFWkgrRY_A5!WBd1ABPSKlDvQnLoIobmmv^yE>F}sdr+~Nu}R+1+_?@R`0IiMv5abh^reL$JI!7Y_uZK|6qwW6e;Rn_-BHXG|A~E* z6zJz49F_W&3KboPAS3Pv6#pw?4gp=FT`oaB?e~QTr|;t4ks7#e@l4#h!jTvpmnIE2 zA41*8<8V!_4{9R2@zWG#+BbSU#J_og(QCbEOQ|V6KcN=iF)oR?!BLDEjO5B%)i83P zNnms7EgsaV;?GajBD$=X((j{3-_#bfebEQuR$VDl`>zLthn9juj4DYhuH|MWErJ_r zUHq?;+i?8Wa8y_C1Zw(%%V{^EM=DQpD;obpXG?i2({u++*DvB7qiuKOIlK3D4 zU)b=b9NwR`MfaR;l+Y3-z6BOAAxA*z#al4NRgDwL8^&%TPYx!};H+hPaf10*xNa;% zvc?0=nQucCnoq#$^h~_}?>P7M?KF^WcFj3@?H+oxjes+8%b?%;B@W55GqTER2-+w` zD=ZX9&r=f`Rka58@ zAAf^_Yb$&2G{gCVt=L(wOOLQTa>8LV#&UBJD8%W*xNA=#PVj*{cl#yR%^a(R)mG&8 z-V?C-N-Gy_JOQNcSQ3qK@?^%YRQ$7EnoqKt%vCI>t?Zxz=9BPh~mmt5t)j@WhtP z9H&o5Gu{H|Xprs*A2`K GTz;JceK&iZ}=?L}^*^}1By-c7wwa`Zhvca7&xjB?#8>(jw}H^En2{G<37JuVVC1Y38qIpFvC;52(CtlfJ8J0gy2j>!m$50d*+Ux0V;1LsjPZ2rnu1Fbytke9^n$%b^s zm1dwXYB3X@W6CK5V#RoO=_;=UzgVBQE2s@0zEUO!t8PJLLj{g?HDi9C4oG0xIeZOK2FwW)GYX zXE~)2rm9Jik3Dy|nzxM8IZ_WUe0z>{|E}Y%M;x6gYDC6JO~jJ11MtqM5R{M9;oh4$ zK*f}3bA^CPe;-L3EZ?GgT^S7DHph{_5-{xDEZp;QKMvHHVD3)|ay9|^2_r;^%W^wx zKP*pnuHz8bsnG+&E@W|lIVsT`gwn;A;nsid^v7ixI&nrWeAfTMn_Lhlrfb-~L0$^S zANzyGv-Ihk1|9k*N}t9LN|Es0dIE>wLOgWMg0A1T2!CAE#)&6_z*8>;7vwAPUe&Ak z#5Pwjo^_7DYAKG=*W_r6Q~+H1>Ox8yS3;J(BVF$M5589&g{AvBx@Z&O##I+V&_xaS zNn80lDyQH?UmpL+@hvLu-2BK#CXF|cw4maHV%YUJ3+;Z7rk{uEU|?!92L6^MhF2ZI z=U^tv&KA(V7ANA`ISI}-#-V%4H!g6!DPxrA)91SKWR2}dENdP`e{Xdo`S&>JJ;gYw zb2{-^hz`BgI0N4w-2qK@s(5_H3C!DVOLomyMNks(=?_&He@2wXXq%EW89&Z9Nrm~* zJ-{?i0z4OrlN#L+?9A?i(%=Z>gLJ7VRY}BG9#+?G~0Ww%_m%7~3@q?bcQD zix>-j)UP~Hky(Kv4kKCKHWz+YG8UrmPtaWBNlwj7)ZNjo4Dbcg~xIzvws4 z=x`Peu55*^djswfl*2sy#t)48|RdEMP&q|B!sf9`(GEx#^H&-Wk4KZSRp zZ!ih8&D205h&=;$E`txbS`fW9n3ob!AhpM4!~BY4TyTUc4s6Q@b7gH9yGE66JY`GO z1;5cOn>mNuQlZe8&;yeapk={1e$E>)qOf8Wmu}jF_F@B2{Zx{q48;LNW#E?9P57$H zl8$)40~_wh(?p~D_@z4;-bU`h(e5tvm%2G=(r3^5$th@QC=M$y8Vi=F0Yx`>T+f=lX>kWZe(zw?ipb7^%3qf>q2aG?F&+p`w=)T$$*b-w) zs}3)P>5J6J;&iSv#svV3@9OV8(UDjJsr1pA@al6N!#AvJF zz$7(t%1WAgjR*nxpmwa)R7Nj)3?3KP!MW%4_&T5fS2%=$aJvd!Y&eYz*}n`@vMRuA zl?Qb%bq3M6uQ2J#fAIRMEz#2b$4@TypwqQkhH;BOjw=KzwN#9+Fv`I(_nG_plQMbw z{tZs7bRcsdJOP*6y5vIX4>WR;BaVSX82dwvlmG=kQ+NKp@HdZ(Ix3og zOL!04x7*Vx?@#mBKQ{5>@)?utRUc-3IRIY+yFvPlF?n|_4d2Ro)9w2;h~sQeP<`nH z`6^E^`_oAbIuXU~{rnN#+l*+Fk}oe65rtb{JCd+zCZtTQ1`B68QhUqQ+&y*%rYj}s zvk`Z(hy2Cghb?KVsUmq8`<^?XC(ULDEucPY2q$Lc!E!xY5}MKg*^;GfR%8w*K+mi zH868^rjK>D^S@Gc$ObSZFWYpn`Tc9aDPy@S%WXhu(EzWI>q;u4M^e8?SL*Daiwgn- zB!4f#{hdZcHg-Es3|1!v#_CY~m4iJ0<+w;o$Wx~~5U(7MqvLD2Sk_0``L&4Wk4O@a zQy%zcJiDhnR;TqVwW-J=5&SCfhnZ%hX=~tNFbl5ZGwwuiS&_1ISd0hxea|`Rz3RDA z!GMQWn1b%Kbguf-My~v>Jk2OKCQCF|am7azSkIUB%bF5E#?k-?M2PJwMf*e^XR?M5gnf~{+%ac%I{a;`0pKl3yj3*FM4z+^f@==k^}t0 z7tkCZ%#B~qJl!lu5teWb4GLU{W}6!%r!Iv@pIgDAT%X+8XH9#{hw#c=eX@LAAn3ZA z(XzOQ0ELB^(BcO_W{)PejQ12>BZ(%TlW}iS3;w#~fa%L$3l%Qg5~YRabm(shHXd@M zvyZHY`c+%-l9d>nQ_ItlXF51f!2?*poU(aFZlt(qJnD#C=Ayhd^O4KliQ0Z0I?J88 zr0WUY610^czTCurubQB~B`ffb;%l zq$82B9`$YM3Li)K<1a!g^Xdfyck3|Ra49|-z6uAL6-e@dH89hib#^|5!u~n|Sv42P zK0hN)bZQR1IbQ(d_jTcLmNNOLr9{Jb9RvF@#pvsiB0MX33KP2Jc~NH-EMJ!)Ec)oc zc40DDe9W0PA8Y|#9gZAYy8xGUU4?7R6SsarAVv)*V)3?km}bxkm%{dQ-p`Flt!5v~ zWZIJOzuLl}>hCBocMJI`c68>KH0VF^3qF}1<>`OzX&BXCjhLrG+)ti`nD(0xI%OYB;d~+R9Gg2jiP2`YVSZ?oG%S*q zCMDe(B*JAFBkjEC)#tLLDMJ;)kLq*hbH4D0HmlR)T9&vY(SiP%IS&Klw9uibLm)Lp znrvEL%OB~{q|?W^5ZU!s0%`AD3^#G4uzDh@WLOj5dn@oijbd16GKiH+JxTh~j{*fY zkB*9~0(s{zxMt4)&R0rC?M?OgQ2sagJ$8m|kJ?c2*jG%5(WZxgZAO{2qj+THVQ%s~ zac=%RF?xAi0tT|VwerRhba>TBdfnTFoN5~_y!B24pS9k_`w0iQI(9a)N{-|9*sBq@ zuZHZix{#|Q=)=TNdwPU@N98Z*li#;5v3EGz0Va+nS4uocrR6txwCgHZdi>*GH@?Dk zIx<|x=SPBzy|(1~I}@l@FNMtwLb&T*AV_Wt#h5)x^wcapV)e|1u2nh$y{wlk&gObA zH!p&0 zOgA;Wcvr|(%T7ce<~mJYstu<-)v5SxN7B=F2O?KAd(s1C z4`R82CL6MB=1nvb9Og_L9;2UugP>335vScFPer3m$ctaka3WDAo^DMTcWenpRVveC zIm00KD;P{7zhJW7A>6;nj=v!K2UnDopu{qDxcZj4&l0UkW41g0#99eD{?|Fkm;26PxFwbbT5e2x1JuGXk{XjfrfDE?E;R zM#3trsnoDDVL3QxycP-#`y+Aj0xdGvBOhe0>0seC6L6m>LFzV*qB2GcAWOapJ(qlg z^Aqb}UQaFDDHJDRwF)?WtSk8xpn{{WKZ4TL4PZ7}mwYzp=1#O=B~=gk=mUWq-0QqykE&W7%cPeaZ-iMaB5;5FcI?M z{&k#Q_>qqcGe^_s)a zN|tC`5T*OqCZew?q0al%NYvUdINnHtY6Zn((CB`g5;7fEt#_lpyGL>`U4b@gsnEh2 zDSC&^Xp9e9(@6oK9+;EId&8L%dJc9!|I7Dk)xoLPIl|cTdf4uEiLW^1N(ED|@Gl*M zF+ElnZ*5fM^S-%|*7-Hqzt0zwj?Cj4w#D!oR~e&!(FJ_!MyQCyCAbcYu|hVFYnXcn zWFDS`CA*67nTj&C^YLUk5)rPaXF86GHw8ndBAgid7fv2@dH`9qqPWc%YHH1U(C zDWyBn_1GBrS>j2pp0fVZ$|=}BSc)2gd|dZw5M!T)!k)8kG@Et9L)SA#2MP>U`#{*6^L-|(^pBgvE%l4SOc z^Z1E%y=|iHc`b9q#V(S>`=$eR`|Ut>stEZjmt3iKw+P+uBTl27L$GA`Vq9h7&DbKh zVEpN07@2qk6%R#$*YS^h!KZ%MasD`N@zy6%{pVpgdNm)lcqbO9iqYwFRtx`=FvnKg z1L)#$9=d|Q^L7)iVWv?ijNaD=<8NMpihLhDdaWJe3oB4&$^Z->8iECz)o4IM0^)op z`ZOvGWHSGO?N3oaZFREuyca!r#~I_ZY=}+ETJW67?krvI)IjPO$jxw~=IlGyQau@b zD(>+5ZFALU6SGd>99 zwqJxvd#tGJga7b{(nCDv&Def3yx`eYCECxx_o(a@UT6NPfzU)gF?0i} z#Iaoh<5q_^juPUuBd}Q4l(b5S(gj--u#d!5DIt6R%!9#o`CD&=T+kg>*cL}{H_IUZG0 zrEAUty%3fMh7We(m99z%Ut}SqnNx9P&}VqxcM1k8ZOLS|cUdaEjawsl26M*^fkLJ$ zDZJ)HecLP`dbc}OVQ0i^KQ$RA-;Q)_a3Ci0GWpvPj^z473$%9%0<91mI{C0DEKIC~ zBF>xE=&2F&PYb|GOb&hR4TQV9N7A{k{z8)bH$3h6m|K!@4;K#&;E&Rfe;dA)@#f+AevcA{CrFeG4 zXZZZzT9kYtPm>M@;~zFZ{8em5a?pS@cQ(VfyViW{n*wy;Ov%&<0^x=RbF%b>JMv?X zU|(G_WOsdsQ8)A$mrMxJ1>v}1UIGSW5OgXvCqeSQV71(aM*Z#*=-zXsUMv2={Vk#} zW1kKAqA0;%{hNp(kFCgjb1iBla3IA9f5uo?5(xX27C`SD2Y7MgDs!tUFh<`qyu14%Fr*M$% z&0{?AOxXCzmJF9Sb17O8;Q8$j?$l_4J)PF1{n}s1^suD?r=;nrrJgiqqbj{0vyW@= zJC1f|EU3<#RVZ^aB}aJcI_J@H2~WpgfE!6a@%z_CtaXj#s;_Jit~WH}*M01UBgfb~ zf0io!6fg%vE{>#L%70u9@;4ZY`_(W*m?}Pg#$=^YUD_e-p zHc#Mj%sbSK?&DH^ZpL}bb%?rKJ6Io!#u#8TaCLcdsX-j;IxO&y)nw*=v7!-Q)Zyhv z2bvxp0)f$bylw)JXN(Q2B;rbXhQ-L9`V{c~{Rm%4+JS3TDdrr}CIT5D{Cxfrm1R$H z`EQ#c+WjNwzRKV>(-oMm%Y%4KHR@IvkXOwvAe|~Bh?W@QD<%$OcF0Sv`KTLdJDvv* z4+di9-F}>_$wN`p6&y&jCA02HleG>L@q5v7PD%<;WxPHeA+;5!{P4%h@A9yHiac>; z@5Z_nqEu@$+YO&f#s#y|aN?3!oGSem8iM|yLdaf8o;UHW0Zy z2o0xJ;|oh){LXkAn{AwFY@7!@4eGR7=|4f_pCJg_p(@O}-HW%{6;Q9imFQ1ZN4Fv7 zg}9pv3r4Ddb>MNdHZtR>ESuSmE5fRc|G+A24x}!ggKk$cv0%a}Tu?X{lGj=hWyYS0 zTUdfNuLxc1{+riJX0wPr`Dk2bLbkiH>^I}q+zxXh!6T=_h_Cqv_ukF}hEdG5Jc>2}atGiWX17!EqfBGV~r}J5OWx zt#RmQN2o(h1pM7)PrcR@qVIzsAp2!d#LJzCKWha?DGkPEcETq|WO3=HpZwen85sUO znd_Y3$4%eA8sF?zp--+cj-#b9&Dm0f^9FZekiI4jo}iA={fymwCK#tYQh~-kRXWx{ zf~fP)c{_De@`7b*K6)$D#n19k=V==5H8dr6%Zh~t5-a(_Z*TBsvNdttun+Q$UFoX& z6S&%}2=@w=LCERx;RYvxOf2IL+_0wMJ~sqLqMmT8)s@yXc+mTOCbX+}EpOkolIxw} zM%~}8MT3`y)XaSb8X6eTI`*^XeFgB~iZkI0-S8Ci(O#@%F~dAnQ0czFYt%e}Pa`@Z zaUcq;R;uuUgT?5waTDZd|H5(E&vEd(JXw5eI_S>K=3gc{k*Mbsy zFJ6JtTO))?)qdQEB*v}XQ3n5%qcC!EtH3pd0MYM*Vfe_K{gndy>$)Hk+zKYm&FI&) z1-fPnC!&FKGPEf<~qN0`xjmeaJd zAV+gXkqKj@XsbgLq#aVF5mi85ohXBsyG*e4_Fw!It4S-fzQNB^*5uql6Ouf^oq8^L ziKi?hAxA6}KK(HuscU|LL=M~AqdHZ7$XF-y`?wo--=X51et~Je51f^dA+IFZxp7Ar zFa9qc(zm#fEz|2kZ?igSnH|f0Y7i&eXSfr6)=TW~d&n3l79>Ug1_tKl!vhUh;vm&8 zsQ#LSqB2q3d*4#{R+G)YGW^ONE(n7*{}%4|_X}|ATrV2#wL%*SIjXo*gvjb=3pB5l zLBX!upz(Dn?mtXOMBsGp^9uud#-4E>w>#qP<5t9JE9=9Lk){_;xG~m=G?^Iwfm>wx z3QOb-P@-uzu9cD^5f2~2{#EDTMU^&LdteZcOsK@#YE4L9m4qMXIn!d-(^zxOmC8li zlm4UkAWDj{{yJG^!Mq>meP?+EmW$Y6oy~g%eZtH|@^s^scW{2rR=oW98dt>j(@Am2``_5RYsx+;y%W8fM3Ezvdf|HBZ7|+f6aj zc_j<%Or2=KZ(TZmoeNp^$ce~rl%nr^7QwKsK0Oq049X15X}Wa~`rNXCx7W0((@YC` z_R%GF2V$(aBMvmD^CniTeT3R8ZbJEn``pD<;$Sm5L%3DPnJoKbOMdG#L&QjqsGon! znAE|z;p}|@+ScKe@yZaCcN^??E#)sIkAQPO)u2X00vBgb$2pgbnHSKFX8O0|k4>t? zn%%uS6er@eh&1SIdc~Dyj}%N+X+fuvd3e-MCk+fW@!%OGHU}wf_IJ7Dc zzNZ-D=*yDCA#n?*Y$T7<=H7yN%boE{ygVI0WI&ZmPII3FCCSLUwVnMJ62o<&vYfKUQovMh;+fLPb0|a+rLrsq!-B@t2nSB56IFPeCq}fuL1eZQ!zE;MOH#H;*{>r57AY%jTI*_ei9}r)D zg4{lT^z##;9}fdrFPFpx%(Wr!pGeZN38CD$zA8M3{&-LA8R|T`$_HHxKY5 zDnzXkSZH~WtSt`IyfPo^d(Oez@riJ3*o+EP46){TCGOoKPQ0BlAx1RD6dzeqR#f7?zjs&ejYx4JS15Rw!14aKZ40;~QqBXPybD5!3Cq26Er<0nfThA(U1!mAZKu!V?`wQZ+St7|Ws zOWRWxB!Iun1$JB7lH5$a4#cPprOc&Bw{bl9?8@d1uf4^JEn+k@E*>vm7>4c*V`19* z8qV4FA}8DZoUu0F;TF~*JW+QU#llKJ?gA&AyW4<@d0pUpH|tVvQ8sV2TpgZ?CvtCQ zlwsnc)6kjl0w29?fX$p65q&E`N7){O2PFkC^wX2(hg}7Qh7Y-SrS{_2jI%5|Gsu@) zTTQnh!1@XJCDGQ-{3@9Ke%y?Z!? z$d~xl-;4O=8W8O-zhGgWE|q**2tQxilAvq4#N@RJk;vAeGaX{M!6D{KXt$)nvN!Qz zf--qhk^%ozG^xxpeZExp9VkBWq60q^iNRYbvia^(ZgZ(N!m&miwcrNc933JIw%4MW z^Vh-AQh6AE{}3Ku0-#RCfQ~+2kt0qlW2c$cvX7=ukX~aPQF23T?cyNz#H7JX2q4%qV7cIaZ|OqJMfsq5r_oW%zNTHLHf^E&M5Mq}pwd(Y9aHv>@A_cWin zq!z6o>GI*P3J!)cafBvlW&tVxWnta zcs&^{+I(M)Jl!Ts|HZvxeQrXPAq|VaJmoJ}YSWARexuZmczEBsgWqarLr4E(3;|zf zy1gWY&$l_qWzDfAKO&yMn=zMp!zH~a*t-no)xQEi-wjY~{+LrJs>Qa?IXF%GEFN_s zq;`=s<|jGONY-QWi@l7$jXZJH3=OKfllg0WV=?%09G?7K3QfcPa6)?$7GaIWYqN$c`X)dyb*}qIm+*nKHEU;cUp+^o{G1c7t;r$8hDhJ!mj011CjOTHwa1)((8{^J-R%D9L7Z|n7k;+xe6WI#KQPNaFiI*pKs!&B)aNzR0R-Z-fP zgwvg9)_^LRK-%#CzNlBNFF|hic!A<&Ym8gezz^jpP>Ta!xdpNc{FMpz)VAgxih?N> zF}VzNB_V8fz7y}K31Hi=->9Zt%(*v<(Pz3YWNV=uUBAMOSTUdBg6|wHuD2(0?LMHF ztwyT$^y7`#E+nG5r1WbNtO|9d(>KRprTtViT|W*6&D%g@(Qz2}V=*{3AA%9;yP=`M znI4UO0HMqurgrZK-WzL2t$yf}UE|xKd|5R}E2;_QI>vbfY~07(+h^d$g)Q*n_jee6 zs0Ei&4suB(4l{&QP*$9_5jm6Ky<>2dVC|JGQf-ZEl=Z}b#a}5E{b`Uju%kn?2WI|i$2p=_bVjZtxor6a$LwIv zxt905=Q~64rSGILZAuNdG^dy2uXzz^%USp@z7htakAW5Id%p1P!rmEXq;k|t;U)Kd zyu86PkAsiuxecNYn5_oHF2IyJw@DG}pS?IWJWud@OFd4tY{T9uQ4p^=4(@$h&&4_! zkeXLL_;v9MZ0KfPsS(wRqRh zEpC3rt>4;+6%}4w#f;;)$-5g|efEMdTaOzb=S&X2wxFkn1f1&1Fy zF(!o{AMO4cHDx-uh==}o<69q>$s87c{3hc~w?Y19mpX|d3iw8y-K)>Z!J03Q=r`dk z2-zNiwudv<_b1+F9^+GtQ577VHjNJpc?iKxtjDmfhkHGLEKVA22ZtO?pr2}!H*rR| zUiLEfJXEK4QTK2L>zwYtW6zx!ngF}!ckl^6zGED_Z`*6h5%c7cbo)#(y7#9fDXUY5 zyXpB{u=EHjS$hj^ktx|ce<^%! z96`*2WWmu-g&6(#h6nbEL-E1a;Qz#)KK$TD3fqQ3^xJ)ym!6FMDn``Di;z2)3`pOW z5!kFIM#UzueZ2H7xGyeEcBg;D$(ipV_W?)WPD_D=5jM2wt0S!}=>=iA3Abp>6BOaD z;v)Aq;M#BluBVQsZ-*3#osl_tCcJ
F40(fG$nD#Td!|F+cMILn_=bk{aHM1;;tA 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

    I*Kxfh1LKBNsCk6S?sK> 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 z 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%0Ao<6eMy zaWMDu|gU4P0DqYnLr z*o3))^Gz1?j3J>rjCY}P@NHPd?oYR9F?fF;hTpG@=oo z`U4jp8Nwi|NMVog6)dyeih3U(LzT+}zFyyvtbDWxJ(nECE#5{%_RtAb-{(a(j*%e_ zVlmh_bpgIV$Dw$uHRX&w_>+fgu;fK2VsE~pyvS(MNo~<)A!GB`_JX=Fmk%8>K=Zf;xb}+mTwW@{JZDLo zG0BNOG!Tc>s~@wu{U|&_yf6XUvT*7 z5b9Oya@vm*;X&VVjOsUn;f=AN#C;ZC?mrCX6U9j4xe@5p{0ih67h=j{YdY$z0k(>5 z#L>nMptx@ee!W%Uk*6ao<^W_at6Hqo(c&e%1}@tLlRGZ!zsGXBspX%)GlpD zp>{t=y63WYjWZcBAVur;0(G7xPGuY!V_>%(?M@7bXBJ{K-q`^Uy;l%kMgo?w2Ao?9oGfS0Txq=}BOXYEbWa z0bIttMb7{>r+m2@9cTU&bUhx8Q~sL3RCP~oMUox~nVQR0r`?59u1C?r)Cv!QhlcKH8`qi1^v4mx-M5MK7$EUM=aJ@RF9X*C_Lvul)K$(PE zF2!cybbP%_z~5B&#?j)Z;F-~ATF?FsUpNucF>(M-ee zgw@R|B;f#$KBHFS8io5X6rK-i=LYfK?l$yIkfqX5wp3Cr8_I+RD5yRL6>p5lkLlLL z`X|dYddX0EQw`RsUBmlWEy3p!(cIZw}bDn4x2O{Qe4k# zOf{#iF}q;ui%+nvOpjD*{Dm(QMiUK5Il6MC7wdob3Y|lBNy@z`80J|EVe6b}|20`U zIp8nfpV|WAog?VDcIJGO>gCVfeS+P&Y*!QT7|Nbp^JuMsEDrZfp<)`as;e*1Zi+oQ#%GylGHG>Xbx#dQl}F~7%^=E!N&`gtE=@FT$)1s0@r%y#@`Wu&A4{1tCXG27eJjDMHkQA+;W9YJpFsp5Nx7bR|NXFTN2NsW}C>z7>M zB(6wc(|tW6+^NSWe4l|c(h4E#V>Lg~#fma?AiCBX(M@`DF=(_bFMRzMlAOgz%HBCR zB5VZRoVA4Werr$OpB&`+Cp1IZVOw(R9b*zcZstzT1e&?3oLhKLmX3b=3?kUx)>iKt zuDoSO47^@J>Dj~RHsUA9MTVhI&mr9SyA)qO^dRS=tw`a!9{B$Fr||A>1#5uwyDwT?jsOzLYXKt_woGlX1Ksz16wn5s=(M}Jv&oj>6}cQs-;R4 z?CuM?!VT!mCH?R&!5SxxE5z5I53*3kjfu9j zMvQrDXKB$RPrl$P=C!RNj&zn;9PImW7-0zCPM~&E`6sQfg;noWy*SGuS?Otum~pkMnlYZ1#+`gpB}ke0PV>!LbD&E$S8K+dvPG0D_a-{ zmlz)~@HXXd9{R_B32>#0EiS=bBNY;=bA_w5mjWg+waboyQ;3pl%<#J)1c%T8rF66%)djgoP zMZ1|Jt@-Ob++6aB8?jA*$^TCCbMG_X6>~p#mfgpX&(%rEWHah9vygkR#gZJFphqU1upkmwTj6$b65n(GG*0O=Vmp({PB}GPG%G>F+GPx zFC?g6yDR6l!I&(bmWwlHe1$Sjo~~VX1s4%V?yspiia{h03GcbaSt+_6can zGNwcON081PxdK!50Elv0h-I_QVZ*|B2t3U)J+Gx{Qf?I<8C0g$Z~Wk!Xg8h?@uFuB zNzrcSx14`Pt*|Rm64O+-;5UmoP(8(*8tE?K-+C|MM$VLjHw)yc@1-oT4q?5}q&BdV zks+dkzIZG9GGAg~K|@5Gs9cT&E&SvUZhO3`$UnwLFq{GDg+?URHV=$;oW`Xr3vD*_ z0vC5&nO1(b5IBjulHb#hL12jkdGn(eTK?M5-*QTnX<142rY~H@_)|iImSCJX)rlH2 z*SL(@5xl(Jm`?hxLlje0SYCY%#NWGusrP&#=qVveR&D&B)#{w=@g9(x@)muk$8tZ8 zABV~tI;c1}i~F9?CyWt}=a-wdz}CIe_+*g{F-$Ne8*MW&Qz{K4#z>;F9giJ%jOeUh zHLCxv0w%WB@|Qw(;F6HSd%^%tkXh6;3%ojw^`(*^(S zQ>Mb5=cJ@5@&uiBF3El=?NgxRoMOB9`(El96W z5>}Ae>C8XgwF1Q8cHYvn2Uu(TR`mKxIwe%>&36YJBdARhZz3L!9xN;p{2<&3TF zdpZ-W){8*I^Gi@ucN*r;d=H9O1~FlWDlVMJy2UJaWH=!c{<{}}{muvBevChJRCrP8 zy*9MgWfIz-XMbPqBReGO&H(@HCf7TC0ADS3B4_JtNz~5AAW}GtdKMOJ{;~*O8|;VYUPZ8wxqddE zI0gkm6>@sf0E+Iqk2_vR!hNTa#4RBSn+d;% zEWr0fA1q+*%W-~bDCR$kw8nGHx6IyyovI|<<`!r!R>rw{gW%$s%}EXQgR9v%)EfN6 zSCsn0LU9w`RG>rN+);z`pKD+w~#aPeg zp&fB^Pe-34xwzh07ju&}$yA{oo#wRwy2eznEKe?{o*jglPdrGwx`4!P$^knO8B(91 zN*eZdz{c;EwB}tOrZWaqMrZ|Cs_j7ejvQgA#YLPx=R5bK@**ENL!GG4+yhH1hM|28 zkG7l>#CDzU#HoDPCtO)g!Le~GKTER@BcJ^T8B)^J z-?mE7*!YE?*dRsrK0Cwa@CuMI-<7`Hl?vV44Djh{md9|>7kp&>tqNhRaN^r^9JEUX zyPwiDDlHsX>=@=@79;QdB*mnQ@-P=t9dypjs?XXSHpFxFtm3{HY=AJii+{cA3E;CBg!mK+6dzYN2kSUs}2Q;9tKP>k6q zF2lF*_wYB|YQ%iMXiq20r=#{$XQ(v3AqEiTw4AHlvxytXsh}}td zBo%U*Z&2X5c|1tVdC*t=%uBm*DmvK7laZvJcX&eKkWUUSp3T^qY0C81?JT(YSb&`| z31Imp0-^$M6!$%=g3?Qoc&5aiTtCR1N{upH{KDs;wCXODU3iHl8ZS|EUk=15l?oKU ztJ3FpZ$j9geBNQQ0V=!fDc-+>U~%ho@SiS85)5+TzwaGz`ama`$d%!@{NJ!B@&x>9 z(xCm}qj8_rJuaCioS2KU>AuKs1ze z<>ALFZ4#BOFNpg!ii|v^f>W}~p`kYy4)6R3ukY0180Q0cymB)p>uTWn=%=8v`=2mD z#fA>BJLXxI7mb(Q0jCoQksWmh^bUHE4~|peYuYivw^xNQiutLo?RKE%eG3Z`<%qVVxM>`7PoSGb|ezg+MU&-Z-Lk-DB{-LlmmF*42j-W?wYct1P4GanX zgSV!O@#`AKiWp9Xt=kpI-YF$`WWp?LnV>;su7q*-mi)&BAL;@Bd&)F=?FLlHKY`Pa z?-Dp(?GmO?x&o-Tz7DE~ zyvfybn#AfuxbT{ZHJx$#4lKL58ADzg5oN~Z2~dBHW5`5ES;l%eH_S-v{9cGsNG%>t zHljKI7!T@~I{j-ZL9RY^rG*ZxyIK{7`YpTh+30FPeZ@tTJ*rk5H@E=T)TxpF`U8xi z>`5bv!njc?`Y=iN8=sYU6g`&x;%wZD;bM;&l?{^vg*nbdyJ{<(4AvnBjRJY6#{~Az z6GdH?qs~1bP3L}-rgb6@;Zl4eD4NxJSue_g<9VV~->V*^AFRha4yVBQ#weIRlkFyk zQ_=0+T=;al54U!TvCe%FW4k=WmBDKC;G%L^eC-3uNe)42!W&FvESDJOggE~-2@;E! z;?2+tXfr+({8!Zq^+Q~^3+O`hF58fC%#qn?HH=OT-!Xe*4$iw9g%eL1)BJPkT#o5U z*zeqfVqSU}aZHx%zpYN<6C9zhjqNp(J?WEM*I~AfEd3o~1Q&j4(CijF^07k)=blg` zcev9aZEZ@HU?Uc>7{pCPxIWAiQrDsVO9QdBE@fE&1p9p&z4T!j2C3LFjk(eo>kaXFLY+;4&L9Eg5kAh{994xLib(epo?hPTDR`Oh$>U``HGHo~PMN*@}zIG9s48 zG-=u7HQ2h5<)`CxNQB)8dV}Q>OAH=CYpoZ3s4#*SYUF@O&RKX{TmpX#E}`i9pYY_5 zIW0MM7C*2qpHMCwjBRwursS#oGVVYA_h1_zD)SfIy)B@b?Sz*v*$=mE1K{7~hY;H6 zKsp0$`Mz0ZwEMURIe03TeP=iDK4H%=BrcN=WR7#Q=Ryc<1xPM3A}K)%C}Sxt>yf(Jf_Pa9s098abu_ zGQzh(#7JAl8&xB6<_1E_m|Qtu_i(rFB;zBK-*7%96H9j8gG#Tb_-jrB_xs}@_I0fX zrQ{@R{v$_6PK&|^^QGyOg%&v8U4v@wYT#cSO2K(%3beoT287Kn;GS;T1z`pn^!G#y zI8gNs{_Ire&P}ey=gJRIcaAbYaBwyFEO(;oXP1JhR6e+04u^tMB6u`G0xmp%2jR;I z$)@jw$8Qo^wOYW*8JVK;f0k^omdlF;CG&gPUUiJ6Je_IlLJkMLhPm_zd^zR7_GOl2 zhe!elvmU?~@t^p%vze3GaTX^GUcVvr2o0WQ!TUXOhP^Y)4pr{Ynhx!ZtD$bY~u zR=tUQkTT70F(8*Lo7)x@^vTRO;OF`sC*7|DyMvy*?1yMU#_CAO z5MguX8*87baf;i#^t4eBg6fd@L1~$hy#<3s1mg zet_SU0(7eE7w}MUgFmFoW$tKRs|HK9k?@zERR7sqh6p`SA)OmU_e3${E0|X#)9MYBVTI zj6}AvGnI=w3I5fAA3Xzc<;Y^tdjAM?Wly5DTmVk!x&WV8ZsOQ;DVpv1o>QNE2Hq+e zqxV)h(h|cMRpFnxSK1!5v($j`$i1=oNH6{ka01^*S)45K89J~1LCLY=5AUkjR2yemajCLH8vzIHKO#q z!Qb@rAWf$f=!1Gg5*s*ISQRr4GLF^2{UJ-_Q5x;AKVXTVFy-eHGUj;zGX8IL;qA%(y-Qj3X8Q77u2dqvG!h zes1O)PN~I(yw|x52S#?lQNLOza;6>kn=mxvO|ylOnd_W%*w#hi}e_G=wo|N0EVlWky<_0S(?@e``?X+ z8?o**$V5Oq+r_BM76~%ZND0PmzYB++MuL0YN8Wmz8s4;&f@g-+!u*G~_(?^Mxa)d0 zYCbk1=ho}ud0GdTy%>j0`3>$%PUDXE9Q~H^9pt)oVZl%4_uel=izF3VkSm4L7@KF(C&rzB+7B_CTUZy% z9jlp#&0ufcBSjfJkO9a%$<{x~C`GP5DmC?Q~3+$rH`L$=HNJ2^y zAAICLF#VUvMGPMETIzld6~0zt*Pr{s#I1)=W#V9Q-1A?!Uh*2>nX5-uU<8-_S%vnm zU|zaCiu`;#f*FB1!p;-{>0n*d^z*0rf+s$(5$(vqkw>BEWisx_S&z=qMUeL9C!dvV zK(?y?;g9*NlRBqv+}sro>Pe!+tk<2SsQ1E0#(eWo(x64LWzZUC#aLj?+`PMvw8bt7 zUd3n-pH=U9lhx|v{w5cC(HPMhLuh z4fI*A>RFW^H`}HSUp#OGk?wtPVdWv%yR@Iv_j2UMZV;mh!QHS_djbF8uMs(Q`Z(r5 zAFuGpk?j{4A7|fJyg#W3xWm$9^Oa@XhE2nmZ?_XhS({V!+m`gDPbj}B+LHR$kD~iF z-QWV6{_qYhvNVU~^vhqbLWh`4v|=6JD_MlTRR4haN#<0M<-Zo!Q0{JS9=;c0eYej) zAv9Z)q&^j=TN|&!(QB{ZmVpbsvq6>0Hx>vzdp^K4_MS?=ZbY#EJAV3jp52{p!-Y0O z8vWFhjJPHVLeWDQb9f))V;x~PhHw^^ccNmi|H1I+zxXi8g}Brj5=|*%QeHR=x9+C# z`Yt-8=W#OzIZj2z0aIcUGXbxh>cN~;Ln1abAG1D(iU=rU$B(Hv~z4wgsZ%2$$<=BH!ntzmQwHXyo5ERXf9 z1e=%(c}k28>lUzga+VdjuG0*nW25*v=h@E5T8_?}d;-)H*W(KPvzWF-ig=%2#*h2X zGM4#9Wb#8j*|khHC8dI|fT;`~%+~>|Px&ORk8RllJ0wShjNwoE#ek zr!|wY%i}yWs1?EE%dQ~i{{z=qIgm=j8{DHk8qnMk$-S-(f$DD7fBta1DEpMu`~WyUIRp&o(=pu9OI| z%D3Ut!Do;icNALsjY($h1FomN61@)H!*}Jfbhm07_i4zS?wQ=fPiPaP+@#U;kNYn; zH}L|$HpH9wDUKkH`~Ts?BPyio`~-pdjSN&D*oON3dc>&jE6y83Xh^&%Ra{_67g%cY z6?lSobrpfEOh3Lr`ZLUQ|AbSb&Iy$dZGzI-{d~}kcW8HFFDis&;Ii==bp2n}Ll^U) zKXdg-6x+9aHY|Y3L!Z$vi|rgfwDHC>wn28)Ul{3F$@M8mK(lQfhC1H|VVMQ4UlNG( z7a7v>-DQxj!;yxo$+&xa4|dG5rA6Xzc$<6^2zHzaaotMv-=co(IxK+&SH=Q&WCso% zs^OfSXF_sa5w!1`3D4Mb`<%-v%v&fyb1OXQMcV-oRnn&krxkF`tNF~2^Z?SH*TL$* zCjP8PBmQ_Wk{&;=L31Kbz=?Gts2v)KqyIF*(9n1ESw^URr#P{f@FYI_($He-Th2?; zgant(f!w>zka^aeGQ%_-x7`#(eyEY`$Rik>q=m*29(3aUxv+VTDIET1O(k=+aq!y} zynVtCIyyw?zzKDl{Us1?Zm^&Q{>~&iERU118H-1L-ULzOA~2Qw1`o#`!h&0)NR;vg z+;_~FzD}BeTaItVgXU(`rg0DkPDbIpT-FPhwVR|QIT}UjNk7>Fdpz$*mhx*-V z!uSaG+;cM^lkclG!739I8e=t)6V3_; znPMrSeM=AAb2K5%vF#wk<_awaj-1s6Bl^g$hQFx%nF~@=0BKcya%h$nK2ec|)r>1! z-;s^7`gh=KbP{e{rA@a~ZNT#qxhNAC0^RLlAbClHw#wh+J7(SxK0Dwq>}(aG<6qU` z$dj_55-bH_*%~xrq9okNUyJjjd7RbZf))3S7)!#HnvB_xF~#~+$zPWId|b%OfBGMa z&O02-w~gcW%-(xuXV2$8FSLv1uf0o)O0<-QlBOaJ8YHBWN@*z1eO{qKHf1D{M6yz( zE${XI>p1-J9M8C~>pZ{T&*x#Tit{1-Fw>FBZcxGH+Z4(AC(^{%!1pnw zu0NhwZ9>-X)S*MiCv%GPZD@;T9f}v*(7zf7aA%Pze_4TfJ5HFA8-*{xctawZ^u%(r zmWa^zaSC`S`v7?4yhfeH(foXM9qR1QI__F~S-z(dg744f3k?>6dQ}8)^K$rUWr|D? zuS?1#kK%HHByI2P#K#_)nABIul^!x9zMd*{c$x=fBza?b!ArsJEzD^<)0|iw9Kh#? zHdoLjOnic*`yeGH+Tb%5GNo%Bj->!vs5$)vyc-Z)lKW1x32A zz6CCw{=}a=bA(%KXipVJR_A&$zxAKyFjNWa$G#{x+?is@Ka19(g#!hcneRb*{YR3h z6D1&KZAWC+Mtk*U48Vzm0q*2U2@-It9G9(mhoyTn1d~fM;8Q~pj$@9I+d+MJs4xJP z|9H{i(v7^ck0R-K;s_DrpTpm3U;aWnp{b+Ka*vw-@@-cl;9iX(aX(@X#mwQNqB0dQ z!j)XH_=S%qi*XH}Wq5ek5y*IRfZM=0;X4#LESS;>M=M59&6BO%sni3IG{_$LQe)t@ z=~q5+$5FWYv7?#JjT<$yKdO*!=1zcob?;gHRLNmmxv-J-q~qn=P;> z{2E$$cfbSvhj75rj@U2Jq|y=0OYkU_f6G-NMFo8OM~zCvIgq778L~RUmM$z=3?sHX zkc89*fG4gN)ZxIS##e~ z5^>jb9qRpW0IM>UiFqc^e`G%6BTbRQ_(lJ)KsT1pvQZ*uDk9*ygB0oMf6i-u9f^t^ z)}*hX9G)!KpxaNr#AJ&AZs$w|I#KB+j%RxVGbhG?zN|-aBG-mB-<^+jaH1?Z(ZBWZ*O@cC;^9`ogafWbx^N4qi@bwpaYyh4<6vE0W=LENIbyTllur0hmu^=d zNetGRP|@aIJUw7Rf_ufurC41e#qN1=lXeOmxDTj!vIeJ3dne2rcB7pejfmqfNAiE4 z)+pm?p!R(`E_GcCeGfki`e#Yd8I?X*C~^?m?n!|BWk*OK)s2w_(_r^Rwg;&!z@TMk z1T~Sl7_`@e`bLW4ok^vrWd(b?&otjq|sJ|<4YW)QDHit6m0g57d;c!e=0L$^v(*M2Rsaf}!pekxCk%Jk?pu~HCe z5C`)cfam}7gWW!(dB;K|6lSaMFBwo(&3w{--0ZrBZ5=D%cj89Oq1?J-_Bq=U`7nDbqgVE6cd1U(&Q#%$r30ErX8Se%|f7H(7JinYPcKg^nWW za9n00zs#-zT6`loGWHI*>HYvS*RwF$c@~K5^u}>#+-T%SAigrp-RdnMs~ji77sfx_ za%dZ__6IU#%h;z$7vbjMMd)ig4YIXL!d(?cwDMuUVDdZ(V!zlF+m<>L$I1_|eExfQ z6eUA$muv&E33*`7`W@%vW%x4-C16mL&FL&3LUEd!AgtdIMmBhp7|l6?pR501Z~Fy2 zS2xTBOwGeCon)S6jJf(bb@)5kk{eaSykvt77@cZJ9n-w1mh5pzn5#{LULO^%9SDNQ zcB@cc`@ZKKFKznarUG?362o`e*wOJ8EHAZIpDbL-_`Eq?aP5l``SVJYh2eeBM!W;$T8dCJkxgj&q6lBi z8IN}-Ig|XAw$!?R4v4UB#)Nuf9Fb2wj?_65OCQFJMnl2hF?syqkEq9^zgBrvxG7a88 zkfwL$O~mUGtf?43j7!&bp$7Y#`NwBCzoZSPOqwXL-?N%~v&WNKsubdCi^;g7dm{d+ z>%ox~rX;R%m#`|xnnWv{^WuUw;hS%VfTxDciK9Swo>U?&Af`Ed;V3LJc{O6cie_$h-Mid*Cr9M?`|c!fMa0H}m+&`EPJ;yaiR#4a}Wk*AC++`H<4+GMKh+nc(KE zCz$?l7iNh{P|v<0ZukCYm@``!6->@!ut7GAeb$8?b>DdTCE8@C@e?R|UIIf`H8J9D zJD%Sj&*g5gB<_bDNX;4-GKzJO7GHKHa?OsUKbQUf&AklwtIF`S-Ce%e#fPrfX8SAM zOPGK54cuw9R7ZiH19Z&}AL1236s(Pg!{7#QD;llhx)EXrPZ#ng;^VXmo)2xE+T zWJQC2euHBNZ20ojO3pT#`AUKvD5Rx<#4!y~R;0LpKhx$*#U+D^b%7$d{t5+c%Vq!$KNr-~#)%c=h zC!_>Q(Cfn65Id&LYxHds(yi=8>|VCPH2qa*{aYCp%r+p2jh1xnA!Vv|*OYGdm!KI2 zVpJzjhO=(-N9$SUbYaS2;4+=)=(K2VQLYgU9&r;qPmhMc&|Iwi8V$^q0}0$cv{W@E z+Z*(W>?s#|?YApVId~6;gEn!z)F|$Elp2|GRfTkYTZk@BCd5}lo;1(3z%||%P*d+9 zWZWH%R*cIrCuk-tTfT*7m_d}vY2zEB#OUFkv(V^t4FB((l$*T_UA9!<28AZr;U&Zk zzndXEtrk0GUB>@(vr*=y1m}HFh4l8_gdGvX_{4P-jbZ)LSoslzg)BKSzx}B4Bbt{w zK8Oa_{rNZg23&nz8N|4VkW)_$!TNbNp3aU#o0q@fulqP&E2bR8vVY)sp(|aWpv6t= z6~b7VlhD{~L03+8fQ}R?5?!Ff8%yu!%`S#NKXZY<-+l;Yb&nB=H9yX2FE%2)~mKz>4Sn zcyrx3I9h%eG#G~#-tm~U@*wJn#_&}NdxdgS9z(m829~fKjJWi6c++4 zWP&H{cJLu-Pn=1gb`a8>Uhtgy5IdM-VCA^$ylJc)`4i(weHs5G4#jKw;O+8@@b^#?n68&6Ns}8e z^@Sgvm+8Zy5I@djtP0)=uH*xDQK~tAOlXD@iEOR%%FA0>|zjLuV z`@v+r4$<7(#$~!C;Mrv& zukXnm4`P@07E+foZt%h;JfAPakM$KNZeONi%E~>wx}qDMG1v}oAKk%x=^pI*U_)lD zjDeQJhIDt;7_2D%i*M(NOdvxIBp*olbD1db3)+8ymB<&BMy@P z{eV%GT4?uOnb?0k3^ij$&~2;k!QeDwdU}>3bxc>G|C(J%q@4vxJ7$VDJG4mNt1sNL zM4;Wj>Y>kf8z@V=lk2;xF?rAus=52{G-)$*uQ(2c^GrZBA)Ol)@=_pmflw~QkTA|6 zEIe*TZS>dTi`S#!W#BvfyrmFEWJJI^$cN^Id+_L2S@bSi!etKGkq0~G!A&j~yW{Q) z+EsPPCV?lplB$fGjtoF$usOSbR$!A;Cfs&A#deLR^t$dSYNpeO9W#PZbH4!{7!wB{ za=nS)NnLQZ&?2`Bx;at%1e7Qp!Zm5e_}P95Um^RB`}*JjW-qj-XO$Uyh%uaxYvu7J zD~)OTv2zfzY6P8r(UbU3t^k{X5*(1Tp|Gn7RNyFoApQY1G#}yncE{n)5M3m*MaZjd zFJOG&C0x|%&V?F?!tRoh^k>l!Z+f1s&+4qv&hH8=3;ND`Ny*TtuWoqML43dH}Vk>@+OaFNVD$kw)k$g82Q1=Ve-gIH*b5D9; z7< zdf$W^y_2H_t;%rH@GC4f9K)SB{0d8NjiNajsw5!%46ZvhfU=8@@KJx2X~#)dI`wG= z+D4Bgi+4Mcf(z>O-B^w^p1p`&Gqm{?(W*FhnJ)Edk);b;tVqE#S(yB^7v23^d2hD6 zTs_f++C>Vvl(Y}H>AWod{ijY&2^ZpBHqVu`F(*z1wls5Cog{5Pi{C#el2uC!xQXMA z;7SL^-@U>1p^VF9o&{hx^9gFHc#@W{!+h&f1(KO}40949(XYXc=uG+sLvgauFsuY= zTIP6n#47lzbejobYWb{}i}?BB;TS9~2lG5+Xvk6X>_A2u`c<2wWnNir~`f7$XHAd_hHCSM+h>H z#~$-ME|hU$s=JJ7m*X!89n%J7Qr2|2?o%`qx1q-V%A|5Mo7eu(Aw*P#j{lko#ffsl z6~zkl|6TbA0|8?a3kBPCy@=!>(yICSwYZZoUlI{NzfxhdY%>6Io` zoScuQG1>U~{dsuWVT-+insm=BOY&U3A1fEiQr(eyRK?qXoPVXsA5WQx8$O%TT`TW! z^5-8464P_>T5KQZ$6iz0cM;4@c*45jMY-R~nxLFzLjRqA&&@izALH&9VMDbN%^z(; z97nm3sxmpU=!OB6dz9)Cyk3la{ckSlOuq|-f`y>s^cAM$IFM-5b6$}p(R?rCnzoHf zhF-oxuxI{VSfV0Ef+t_V@;0C+7JL#obXCHaR6APt^9k6wSQDukillQ>KZZmYkmk*+ zv5fgVespfY@xgu=`Ev>eNUXuqB`$(|SB7hoQiEZ}N69U1!|>r`+|}QOi>_Icmr;&n z(veEM;SdWkVX`=Ha~2wMA2}K@nH&1gpHpJ|t=X+(SXZD8=YP74zWMU>U&=p-7H4ja zdDYl8e-WIUCr1{ye}wm{kUxrChg-FI|NO8lr>j`&D$Ao0by~623>jb;@ zYyKrio&?QdB^l+}{MPsTu zNs%r-`v6@758?FG?ZN|prO@k74meLzB#HH&G?uOu>h>ja_A?x51#_qj1Yf}Qje&4; znhH@jkVLaeH`wrYBDb-^kpzj9plFK(O{z^nxkDyiJu4)^^U^hp{Gy0b4{K4h&6A8< zTMjR_-)`#_4+n^3j=|`9YTS;dE!(^wXHi`*GyQB5@L3VZcwD-YVR{ zJ>eH*jKG5OVys-%$$cqoK?6NM^iR1j9EeQEtB);c>&<_t!g8Xn{&HkgEMx9gNDv3P ztuPqh1HQ`PoM84ru)n*OH%qdIklVpr*ZtLCzsr%7xAeo(4bPbG!;SxE^%N}bUB#cj z6XDm5)73F4RBs6(w?`S!+6YyUfA$J2%2bH+{`YA3Rg#4$PJ(O0 zHK^?Q&ZS?FBlu$iD2UF&fQ!mxbdfoEWM@Mjyzar1)e5NGw3q8`nToeU??TDhf4FG2 zBHdghPxilMUR&34{AlJ%x`#Kwy=`6-qpB1UYuP7a;C*zk(c<9A4ao1tgM<5I}o$|;a*#+AjaI&sZ!)Q z%Sq5^J)+Fc^9?R=G}9=5&Qoz&N8EY=3Q^VuHZT!mn`vEF$Ve;D`ig?@>6 zBH*(Z-{paCXBTiXB_dSI^9N_$Nyz_aiq0m9+|UBXihtsPYbR{OSpFX*vomeB!zjAs zxFq#`{R+oibEQ{WCFqL~Q9;V;5mc;q0q$MWkG;X4;QD?YT5PDu&#<}5`a-#wKk+8| zHywf5;RF?nd9ZCWBJ;aXq0TgMvcS3@d>&MS=iF9E@3ki9*vulzrVb>*75<1)r{?B#DGR_s7UNz%^Vhtj?tN{<1s*q>V_C%k}Y*R80 zaYUd=AFJ$x${0m124wzBIU;u}#Vc=YC^~40kc?jf+UM|D zU|r)%w_jU_8k0-8#I@e^SnW-r)#|aBpQc2!THf*T&A;*N#d5fKWD=CG(IH9u#posH zV18Ue3rsX*JpM!G^wUZY(qyECr`xU3HeH)+P8$OA<4$z{Q6Wx-BmBK6FXGdrN`oW5 zPuanb4;qMyq5;gVKv&E_mw< zOx3T(Vb4)CPEDP;DDp7Xc{c1;)grHtWT2pO7~U^Y!%^2pK*tmxddd46*Ew2}oWGTe zibHL}J$56gxXMeYsDCeP_bcZH>)Af@yf@vkgJtY%UxTvqA^uQd5-uoExvjKhTj!=6rNTGfyQbxvNtmY8y_Eo z3$Yr+GVc@bpX`pm+w&mSOb3P!)qsK5bbhC}DZk|M9H?Dd%qhBzB7*6fwDF`X|MK2H z_!2)PR2KMPUsD%1BY6n_mZreT?6dssQ#Ej<@dZC}TP45v(_1jt=DAA}<@k4G6Aa9$ z$InjhgvO84z&uWqR@%DbST`AZmbsjx``rld!*Z}g(sb;tD2#jd7E(U9GQN5Z{9Cmc z&Ts#ValTC6`8R$GFox8eCnUpMWATI(gA(6X> zA5O%;Owm)^hW$3g!L*N)X^zUBKRW`vZA7W8h`PJ{+`HU9oza*VY)f~KOof??Ia0{7 z47Lroc*m_WL~esNcSG%=aKuUw7N44dqbci*o_~XbspY&nr$U1nv$J#KbqJC;%GroG zka3?qh|wPpdP-auo*1*8vB*XIe;1~Go;2%QMc|(HYF_zgB3v@70Pk#T(ixz_2cMH9 zLEo;Ud-hd~QY?Wa5g)8%v)k^)cX*FHMY>}^idrgd0mC6hvajqM1Uy&`*6&pKLmNHl zhLby>J;8(y73h)XOFGmkgd-bPzJ|1PO=|YQirT%RsI>VQ^e-BTCl(*Y0*_f}NkvJX zpEFjR$V2zBvDl@N%vE*|a$dRejDje&aP;qx^7WT8yF+l`s;uNhwd>Bqrm=5()_ z8IezYgGK&cB>1TjnF`lI?_VF5oMJq%w;VY(LfS)~;p<&vaOyHY zXpWU7ol`}~K=lLu!l->X}U(G0RGh)P~Q`pWPy|#*8mF8u=q3z4*!F% zQZo3`dO4i2dWqU?`|##5Wg_aX4y^`4IKJ&Zx-d3sUEX4pWF74NPFJ~v?>00hXr|yp z{~^3_EDp>JKjX1_X=+!}4DKPi)M=j_RnK_~BIfTPGVCobyw`<04vTOJEYC~T(s4oQ z75v(BgqyhME$*ookcw}%WNgGs_+6?@9o__TiyTTIKHHeAJfT9r{M4m!yd0^1eVUto z<|Hibdxl>=UuH9oZQwFwNX{Merbjg|;M#N2WJ{7h(P*8HV?F9I!xNwNZrP#>px)V$7);=bqzb0bi$g|7T9D_$1l!&#wQpbgy7n4 zaI_Q$5t}_wATbm7dKj{0uQ9kJb#glg^Kl*jCWymcq=wTT)D4V?(DpCv)`0#*9NoZ`1@GGu#DElzcw zCA7b=3JgZiWi;XniG)cMK}W_9AU^P>#^xZSrusy$M&HwYWbe*9`pSNm`@tZ zabhFvX|Ad#^PdSwu(l#xQ{l+FF}-}j<;n2n!A|^tW-2m41)Vwu(0EEXrd|IBLTv@| zB;XAOZ8jz^l*BmgyN$4M{1DV0yp7L~z2My9rXv5{jW=AslB>@D1_rIx^!G^v?$!K9 z*sP{TR+K8yp6#P~en5;pFZCcf(UsaLXv6l|N_4H?c9e7HCS(t;?SXZN%J z6r`xxj(DgN*bzahCfTJeN~CxvGDRYn8{6-K*K8xv!iHt!W-j4gxiB`|=>UjL)1lqF zL*aR)B3uubCL0_%#y38N=B#5g{^1XBD5`~P!*cZXG*uE<2DI~@1$j}fK|TdZpk+-z zXIi`#PBeE54hD>X=Bj+KKITP?d{4o++2cU|=qBX8WpFc}+{8H()S!QDAxyjXn!leg z9)bglxg`T1Fu;8uEL@cf>q^XMKiHFH;{Dw3r#jqQ^GncX@rZ9LwV-N=9&|+Jmsql_I6wQ5@GILU)V) z!oMfw$+W>DXj+*KGVAsW-t~lFL2w-ZX!}Hv8mEi8QHD&&n}M%S>(YSQ9Q-=lo%Uy> z@^Q;D{c>~6RKOX7@$If}7(D`^Dv8GjN+GoFij7JGg( z7Mi&BZ7#mN0=MsBJyw4a*v*Xv+nx6W5_(N2pLN?)zp)i=F$dH|Q*k=YK^yLgiPN?~ zSu*-~5^Amg#uqZqMu1ZQj(c<-Tp8!&HMa+X?L2AP6gzV3pBApZUCO&SpTp8B5A;%E z44+>tKdwm8<$@Uv40{c)BN!oF-T}8;5AgTSC{m>}r{U!$ck+<67)n@wtn7`auyw-% zd=^aTqU=-PmGl|v=X~dMw7;S9@*SLHvI_3hSOE^By9GOCj3Ha!gcMk+W7n|@++^9g zm~h<;584;={VcD%E?9=#Z`=i691^koV+p_T=_TBfb%Rq23d8RS=CsSb5(UA!@N=0c zzf{tOtiQ&%*BLRmR7`}{9=r<|>-C8F1J-S`Qzw4mi{QCNB|A4Ee4f1zZiE@p+jp(# zq}mh+_L0FXaYLB2KmcyP(=kdA2G6M$tujf**$>0fzu*`stBnIsjYK>$w9iX@$ps|Sz38C-52z7;i;8^(;49IC6%j`Cr*0}(u^n&W9CdO=?*fuWHK-N)3O*xaF)zdh z{6kI2#Tkll`JWlBtareP#?LVK&L=qKtV?x6^3nduVd#+%!{A5*vLH;6Zu0KM;_s@$ zQS&+KHERIMmZpJaVk7FlS0q1QsL?C)tf;PTEe^+MP~At$L@6K&Glz^}&g7rCu{#RR zc%OsCVOOEDl6^i$jl_nrqHHgrL&h991e3TFOmNns`K)jBUQdkdwgWnO!7+iX#vCYW zFsF-?^LTUSNA|400xx{Tx%K3>aLQzB@<`+&*acpK#}U6EIOzrq?v%r>?-_VO?Jz`X z4`SWEJ-FtG4w(BCa=~YM7%%Dy=&xq{*rKI^;Fn+EQLF@Qm+*%x!}q{IRhrzN5e_T6 z1EH2!kdn>wAvj+Fdq33)WGhv@cDIe@&C**i^tBtE6QjoF?p{>;w+Y?$T0rApX_H_J zD{8p$E&iQkM1weUqP4{xq?+Z(b4@Wa#%}@IFa8A|jvA3$2Qs0h^$dP&`@(jE*5q(f z4Vafb;f|ap&^bwtOBKq{&N2;dNzrWl=93P8&RFx)ud{oHZUIW<33-Rse*Wv8F4nXA zg<}<+VYTXElnFh^Ie2`*v0hHF{L=xhD!h$(6l_WR`|D7=U6&|)_RFo-evir<{s!*n;@a)IKJ1-g3WB#EmmPg7Oshc`p(M;i{C-f>(89Lw+by^=|QKf_XuCsnNYRz zyZj6j=2qC{LDEal;<3(bp?K~waLF?zk4{B$eis@swsa*g!8oE%t7;%&K#0fm85_Kh z<1Q{pMLN!&u6hMDdXpCMTBb?&-4;N%g&6rS&77L_^g*inO*HxN9g?12g;?W5kan7f z>yMQArVSQk`nW(YV#|Df+384hy!w}Sx$uyW3A>K(#bbHD@~2Q;pn(rJi~`R>5&mUw zF-A`4gMn#hAUIP2k6xbzz4z<+&Pq|TrIzK4dRJkr>0xY(?ttmHVj*sW4Y?5g7!rJ} z$%vsZSZy86zuLvTsXKE(u)Yv}ezYallg{vAVcj^dMx6@g-ooWM7r}YgWv*27ElyDx z%N2Vlb8CM8fy&$FWcP9<(*A*?BTY}DXGkObex$>xhZWuMLfi+)%Xc zD5gC1rg2Z4X?l4L@~cCzYsVtY*f@Yw-}E!HEX&sT%aX`bKcF(UU)X)O3=9~rBrn2? z7>@h{RwWleabFP_-P{g0m8EFSSxegD*#gl+=Q-)ql~|_bMSm4Oge6-h^8+Kzh=g4h zW(ByDGy5jMv-5fUq}l4!c-<%5Wqglw%V(^IAW!o4mQuDK5j$R%`ek>8Jd6%|48@w57+-@j_kytK78y7J-hRC+UWD znEXS8hQ?inq+Z5r>q|uE8EYXzia994O~|GtH=#P~7C4OgDsX$y2csniAog4tZc#8K zJHDqvcKvDo@}z?}Lob38dg@ZIY#s7YU4=fFSI3QWai(_a`ZRY?m7gdxl8VQ@!#4KZ zGhKX+cpEM7&(%cJf;P*n>?tSm=Z16tx2bd19bq3+UL(>k|jrTLT#zzfHj#Z z@S^#wXML-=itE*!&BHlqv>tJSFWu$@E=$is?$#^_xg||9d*#W9<4v&1C!YW5R*q_I z0y1Na4ePIUz>~(2RB7o3fh~Vs7;$nb$mS>tlCDnV8d)#EQNIOWcj!^!?n|67$Cfw#kvPiem5}Q?13#`+LF0ov+}(?7aZZl{ZJy7v6q~fE z5>=qDp5EnDoz|eI*=*R&5W(>!w=42x!-PMs`fAjOxgux z#J_w&%{3>oSotb=Zj@r2wUeOQCB|6aGBoG)LFWBp*+;$w&wQ!Di!yTb#YQ{QbCJ-v z03XPjcNC8tYlVgVOW@|ki%|F7g>Va<%rWM$d0`U&^ZiS-Ij|g0 z79Hol$?nB^ylgE`{Q=@P#IT|B3g3>Ays3jVBp0V7j>;2m=};#=%Q&Z%|42-9)= zv{QfJSeBc>vqzb60ngy=I3*gx)#T=#`OK@aGm!j>P!wUap}Ha=v|e7soCaR-t?@7Z zlaGQ;Cv51vYD*gaqaP0Tzr`Y56Plo~0M-wG#O;0S;q_8UNPA=mEy1qzk(vilnpKCF z_+*?}JSZ-m|)jK|)1dK17st5oPeF zh8a<{IS1OR?nn=>>A^``J;?Oy>+!hq1ffw-DIAw-h0en# z1U@-2P$e18M}`|v4+Dyk!}Gx8$vyC2Y6U&=`N9(m;z2jS920)Xk=LJPX~eD-;I-6@ zD8{b#^62@>e`KE9-4-2?T=)rwuEUpBR z;46^5@;~fSy@GF?G>D0~BAkpljek*!Ryu#@gMQ9~%oqh?GQyH*jFEwtd-K`%{0(~j z^2ftGp=!b~kic=+IIA9HpT$1%+-pUAWbNSQ_3?1_hB#?VsDoRp-s7eh%<)jmG7TqW z=+xa(WXrslklNpbA?(>UIqWfx*wP05KI}JpGRu~{Hm55)1A%V+4~v2}LRFPMeUgbdFHMY0(JS~M^4m3iVFLQ;ld5l8+7f@l(n5Nl^*t3G=tYhBc&Rak6-)TpB z)0MFmAE=T+=6l|iScxxEfhu)w0iSkLvU^V&_%D1Y*zxf`IyCnP9lILQV39ddD;&lJ z-AA~o*0*7g{B-`}BV#I~O~6KQjq6?d1jFZLfz0t?zGqxMZ#<5pRi5tj#~%}N>&`TO z-Pw1zF5@@!`CGx|hd;49%$TN4n~%H4=+ob>zu=y#4eT1PM17V9;q&sZxcQqq-J)d& zO6v!5uh_oe$JSMXT!c~X-yhHTJ-?;MwuV%^-4}(*8zXX$_CH6r{de%|=RRoOu!8%& z+>Lnt@d4xdk8tTiAFry-JR~Yl1Sb|-khN?kWbjCWypdUgf%cNPFD;w(BTMm6@&e#Z zkAi*>WAN9{;ZhBbbCSFs=}4)Difh^EKjI~Qrh)im_m4hN9cNACZWQ93d(EJJb0mFRI)T%F z=f#iC=!29m;oOj<5)?kPp+_H_5PA+A;ODLPz`17gVS1=2nI9nqQ!IC&oX`vxT*`)E zc1}?FZA_|3GCp+Zz#BdGoWQso11Vumc^_-6=xPd-Flt9(p$peX9s1Mfdo;v-i{ ze)FYVkUhm%*IFV(;;fMWF~b2jJ~xK@az)rnux-Uv9{bF`wbXXbcff`n}?Z^=FnX9 z1+5)2V8T&zT6!guD>!RPt-E`$(?N-EIlBX#LX)v`=23y&J_Vwq+Ycwcc@cvy6SAt* zk@<2B=&X%fAy}Qw*phq%$!}Iec9AU3>b1qb`YiN5901>{DDK~#!Uy_!;onC?Sm)?W zW`ym>>c8>)%Zd|df2ffU{SyvhEZJj{c9LIr+mNbyEQ2qtyLp4@Gq^ME`;k*&eI&I? z2%1@ra|&*P{+DcA`QtQxm70O_-9NF2cH$MmH;DgL#kFGwZ1A1H>3T31M#p!Ysec4D zoOH;BXU~LZ78uZHUE}cS()*lEWjZ$7w(u|WY^eLE4*pBVu)wtL3G0?U;}S1;l7)7E zVLRh#XWwU?<^yN(V(d-O2vMa^8T&<}*O02GTC&;vIjE|WpjX}~(_LyhWD$-egQ;Ip zNx$8%qhbWlLMl+nQ;d9I`3lXFa<+eQpz}_7(YjF!VfwrtK@z(QhOwD+zVZad z7&`&|7EZ)!8rw;|x1tNA{^I@dh4`bXlN)e!LV>L|m0f=y4DHltoR$O~`@0&RFBk+{ zUlsc5^-pZIa3k9lEQrS+JG^C@iC1$>XxuANy6cuBKkld*U2SeiQZ9{0>Bb}Q-#cp> zA4!PS%)gj_HwW8`m8hM~ZZz>7Npsh8^hcj0S$;kSlMPzA0QFk*E^#G~8f4jd?g1|L zy^5pGG~G*TG?1W zSSeib7f7ymB07940m=OjAbVZ{#-tYu9EXSaJN_=@?&p1Mmce)!mj7|L0+~y9?R!jo z??i{>2obMm4%}7h;Bj7peAW8IklU>&vxxa?oI^k^?lSLtZ2~-0|AZeyN6}%?t!V0} zLSi=Df*>1RQlI07>5QX1cJyydmqIRAEeG!Tijsp4ehZf{;qQW1d6Ij71llAY8fai%s`18^o4qAK#&F#$9nRpfd z-O#6(OKYH%^T2)0J!<>DD?w=HZotgXD zE(Bqg z$n9OQfg5;nAA84?2{L3O1z$Zx$e#;Z^y!MDu*tOxWBMy|&&<%H9aBaU<3;88qxui3 z*S!NxubUXR=?31{QzUhr2hi#MZ#*(^P%w782iiQY#7!1=plh!!8IlyCsW1=MR*8{C z<$>s7rA^O`_{FVC^QQZ}C!*-ZsTeK29Coa;pw~xx(JVi6qJPc)N+CBgQGwDVgFRuT>212d2Zm z%l|m*b007>J{^M(DB}9?yTZ^oLKAkk@b`Yzf?Sw1&0VKQE-IeF>pg~KoH5Tl2*Yr3 z>O0(Ye+pbl(jikPmkAs9X_M8Vvas)mB+i%?F8s411#?qx!1VQ9d}XUN>0TI(U-tb) z1LhRcpZT0GrRzXEPe8Wc0rIlShMbyp7=k3`+1deSi#)LTX$5MhRAYOtJ6+w8 ziQg9+;kSvF^ab0?v_xG1ohgcRYY>NjE{KtpirJA4%6bWTADrDj!?B7)K`iW9r3!xT92u zX1!R&8?IT1{#V@uIURBY{j%|fVLEc!M_@|mCCEH(LLmG>Gtv7RVdV@P%vE~<+f152d zo?t{r7ar%&uv~4|#|wg~c~gYN?-e0hGCkKkvK3bE%!Fy4O+txoX-si;q|es@4J@;u zOA-cPI8Z>2#}s-+o27d#brFKgfjC(C%9SqLU_-m2(gh8lV)?)}T{_}*BKWl}!Gg{L z#`;?U`H}Wy!kph=Ba_J8ku#u)>G3#q>o?eN^cvI!J;o=tsk~CZHvM`31R5T1;0Ln* zA<{g)K2wGslYaqi8ayxGV^4jP)-Px;zy}-R@L2sQ z&VJc6`0G@GbE4cpA@L)B>2Vd`y4jdaxvxNaB(7m1o4Zn}cNqQV6DE3k zqW?!on5%s{|E*!4#E2w+fm?imS5Mn%$eXf`ld6h9}^6>XVdz#Tv1hJ+y;G$#! z{x?*~bh!pzAfW^A`e)+8-h22wr3~j+dC&*xhaoFwGT2>Y@9VJtKr_}JTi)eEv#=5evtw{oMD&!NRcb9y0_?csKI<6is?<;|OE_L-?fB9W&eTXMx#ay4GMZ8Pnxmb30o-r~SE-`=10;yqM=IK~Pr6Em38>s$U*t z$|@DoqVW#1ZFHb~*J;j4T9w>V%|XM?7f`ad6tAn@hsL)q7_@aPu9+c9{DOpB&GAT> z7!iic|NI9Z7TD6Pn>X>WQ3D!A=ix5>c=!{hMYZl-f{_zUgm<-#Nau)nT$U+9Y`QNA z@_w*=&rBN{tgA@Il(|u_OAe$Y;Ra+b&cm6_5wOEQ95cLasP)gYsQBMMj5+5n>}r-E zxl!w&{iP1g7GDZi9HZGYp^DRN`e4xuZFu#j4J0D>z`Xj$=&Qs$t`n5;eB&^0aX|=P zVHwa9rbEHq2Mr%vLsqdrUXRx$(^9ehW8S zjj8>HV^~uqOCO0TlPljOi9?S)Jv{C)?5PwXe*0o^TSy!Cuu7aBH1eTOPJQ5$Tq?0~ zzZc0el*YKS8Z4OH0qboNFjn^wO1ZG#Y~2cO_IXEgZrtT*m48Os@cKw25dtRc^%@zpA7AIaFnRx12D~K$xCOV@%iD~mDuy`s* zWJlx{T$)LUMpyv+T-^xPK`zwSZwOt!T}OxRVtDJSz@7y+Ix^~laJx5imFTTU9nE4$ zoBazDWlrOVUtVF)8nW~ViBxqi{oQP%@Sd*%6^=2mrB0t}{ijU*hwK@rY(3WIzv9QTy}HqpgM5N)JM!N* zg5n4!uSvTI^AgKBOc{^V$bcvt9>>8n5fV9KC+_jnrb+3Dop!f`uZ7ofz1$BFwKOLo zXDsMmr6n-4{U7FdM}RY%aaiA%qlco(`5BU3{9u+H#4NqVkMywg-ogAw%C~Pqp^hW* zwKt?^b~4|4Y9aT3|I?z7g%C(B>A6LAH1&%njoZRlLfa}pJjRK-zg404m;XfNL0j5j zs!Qu8j-sEof9J+taG_rw$Z>n~rO60~O2{=2g^k0(2-9}JwrU;JTvh|8U1sB-QMzDc zuS=afJZNIEwIH0@9N1kSNc4F10$;2S2CYF0I!l4|NRnouMWr#7V626O$}=$~p$6n;`@+7PM&!MU zE#2_1UbxxCoLtW1Xnj)@9$It@S841A>j@g1f1V*#dGrfYS7&i1H4k8ph6(ux_h6lr zBU!IG5>Jg94e#e(N5dg^6yFyE`o$X5<@0jyg!CVK=rfbpL8!zywOQp#2G#mQuy&`?UTo2=HA9HKR zxk7lL3z;3W4C80_!8)-5LB`#~@O*BAV7TxF6gXz$f-9ojSD#M2H`a<2d#6FA=r8cK z@t^}Mzrygp6i`xXhtzXU-0cN2B>2xbJZ)=2qK=IqZ=Z1xTs{`gPT+8&`dZwvB!VmO zOX9U|{^iH^+R&Qum-r!$Ir^Lip=QD`oO`50Wj0ID@Gm3jRplr_^RGUfS`-Gy$LFwo zsS3+Jn9&7UjwHNaj`CV!MCg20|Dj{;U zeAA2%7_9S$GmHu2L{529)!%MVGGB>qcj@M2^pphA_ZX|ST$@x(yd|tVPyl@!CSi2; zd>HxZFQ^zO6Q`~(cwn>-4b)K~-bRYN=NAXc;Bs7yR530WI}FO@@%SiXFBXvm#_B%; zhsu@cf_x8pFzhDRr|<-GBa*;Gk2!;%GKR|umYb}g_$=f!Saute=#l*}95sxt zs0 zxbOFwKYAai-Tfkg{NZ$LN?OP_#Qz7$=j!>9=4McNcm^MBA1m~k9mL5rYvQBCFKBUk z1zbHU2EJ2mU{S0VFE!7e8uvTWDk;WbVCTSXYZ&{Uak%HLv!;r3dj(QLZCYsUNv~=& zgG_o4xOU#gxLp~r+Vl&*X~r)6KZkF5|3NT4z8~E_6!W)E`N1|(ThjArFK3qi2~LL` znzndi-zIzJf>tr8GH%r6WLm;kZOKLnv+P9(g~g*s?8333)%lLO&R z*wLdzwvXKi3!7y5mNBJJ|9mVi42|JT57(gSLJPWVffXOt>_2Yc{tKN3ypK{ zg1hYxcJ@(Y{rNB86F&@HCnZSYW_dDHl8E%hbpGoS3tIiM2G_*>fQIA|G??8tmN+QV zn$z#mBF~;ob*To^?paL84giNo&MG?!zuGdZ1H|$hW&v z_xN0x-)Kvl-QIwGfe5j3QKajfd^knsCS5Fh7!=A5f(OgB>Dx_$nd8NAYg7z1}wj)0o;N0jR<;2rO7;6iSd!qMeU%nvhyNT&iUI}?xg4<~W`o7cei zmxRPWQ%BSJ+tBpmB{V$mj3SC-LBG(6wtK$7|IU~5N)H`~>7FE9n-U94r%2L&;h(Xv zv7P%U<4phR8PmIenDg2rvtVZHAEB0#B+fGT!KO7R9H?^l+K`4M_T9 zIWllk9o%QB!<}A7NZdvF>wQYpL8h4X-pa9(u@ch$(?p#iWA>fvfbdU4VA>=`U060b z!^sf$JU5_g)&o7>!I9%@)3ErJ1U;p)8!xmdK>2w^N`ee&;nQJw7Mp~6U&ZN@{wB^N z*P8e&P@yf(mNce67?ynei#}uO`Ba&5$SP5Q;exwZEhszJAU$i!o~6xLQ+}1&$=HV-LUi-(AQwd4^|_b8(5| zXC}Ms0OyYuL^P`rw^T63i2P~H87~H6udi@9rylb+CH8>)joUE#VGC3bvTStIZJg<| zPx!BN1Ud4f5Z}7DLfX_B+!HZ#9AC(MZK0LkBTKcoS9L|m+^5{KJALRamB?rGr(t1s z28LJg@DHEyE}4g6X&aj(-@5_N`p?2x#%X&Qtwarc704KKe<+#!sc z9B4=UJK0XeEepL`Y=}vpC4Cb<1MSwxka_+pxaHPCkiVqDd~nxr!deqLZgMx4?qqWw z>wc(b?o8F79t>`O2UaV8;LCtDxK(UDm_F2?z7GwER_kt*))5mVz3zkO%UgM|N9_J; zZ9-#|tnupYQ&83`gYNrhgOfmvKGIbocX~bOUH%TJy-tHSTHm4j!UjCW`VMD}?C2=d zO%V1~lQxAfg?R~A&|rEt=CA$BjelrLlEk`TcdQiP#W9?;X=K64n*9(bznfDGH6o*b zs*rV>Y9uI7jK-<&6r7ybgY^^TX~<1^8rW(}J};MqKZnF=?Cy=cw%a_6js1(9E=Ph^ z{=&-|L14625+>h2j0?_A1oJe;VSEw_4VmY8rI_=e;N?VOI&I-t3!B$S{^W=E$7^}{p^rBzz<%a;-t|*Jj=a0b zy0n*(pOV8Zp6Wn8*v|&YYekw$$XfYxxU(AAJx2o$ho8p&mS=eLuRW2yXGZ1@6mU~^ z8F7jtYwoIL?!m_rEcZEl8DH1y>K2Uk5C}>#P*FejpFTt3RTM zy%Kd()uUo7hA`>dFQ`6Pg_SRRLG^wD>|_oYk8h&%RQ_-DXRZ;S1D7%9??Y}>##{6X z8AgRl5qe_bf86Q;7ZCikpb?|@@?#drqu%Mq=oxBCmNdHH4K~lUdo~J1R1fj3F9dXi zz?;Y(ON7siJHKu30Z>^|1YH9+;o#WqXps0DUS|r(hLbAfCuP55*G|BB)|b&^e)#B< z@%+V3RbuGjicd5PkZzTQk{3!)!E%#Y6G!1Y?U|gag$U74t>8I%drsd-mlR2Q!fW-X zc&xZo;M%zbS3kOfwU^4cT(3j8DA|cl`)EtoX}*J5d$ozutbKxC3q48rA#)hP`11Yx zbjet!Mr@W;CBFK>;5R)Noo1+$F()O7^=@^pdct{h2(7`h>vzGVp7ZeGX$(I-aS*?} zk)RD{{$S(fV<_z{TCkz_6K?of0+~`0By$T#4L2OZja@%^e$sY`swxMy-=dJZ+zrk4 zn9)tYjQ-!{W!&VQX!4;Dy7wMMdBf?@7&n9i8)WI><`mqL=R~88+c0V32;zGs9X~9i zIBQ=A+<0$EcjUQ~9%luzW12pl!Q2S@#xmZ=#JBimsua1js*XFi-GFq2nDLk7WT8Jj z8G1U|c{g(eaU5R*LsOQb<)37jx>JvY$#sCr(VzGv@IJ-|9OHuy+7lt0cUy)l(TINb z%*eN~&+h;~D7PK74&TPtzBY)ftmq!cA6OS@Ko_!kk@@GZ%mtxK-|sObJr&ku`lJC^ z+0Xa^4@HXB_8lyF)r~T*2#tE6 zPwbOB;Ch!P>t6OknOLIGd6zma{}>6T)3vEZ);Y9c{7}>0J{%fb3UAAv;&&Mz>cChx zk9?%5Fi3d6yUx1xrj+~>?H-3zbJU;4piXZow;KJEzkiEbRf@JdW zGxJuxC<$R=vp@KXy?f4vNzjkh7f@k}BVlECsOWX$ES9ky$zw}l_LOs6vEN77UH=DF zB27TRIyLed3UHjcs%9+i7B1skk&pg}BL+P&FGmawct?8dL<&mOGvB3{JdqO;!JfB2 z!J|!+ydS|_T-WY!2JhYJkK_X&{auHanZ92kl@1QUmU zVM5y_+$%Z&HA)X*irq@M$L8Rd59y&-=_j~$>bf9jaV%yY34@)lVz}Y(5dPr9ZOCi7 z!Nt~37`3z--p#b6?aR%H%+BN9Pe-Lei@=K18feq_97_rZo#>BAt|Ya+9ycotq5r{V z?pL=hVRI_F<&q^;2=B-5PvSW@??jxgDMHtN%@KB9@uUTX_hI48DNyk*9pAj|0K0rI zn%uP#J#-8~M79lb;t8#cpUjW2xQa5%=HaVJSMhG~S)9at&7(faQC-tuY++p>-PcB> zM5_m$zOW%8o21F9^2?B#bDs}fd=@WjS&@ZJitu(hq3=&^=iN72QyU9=QhU>zTrsc# z0|%BD=#6Jt9WN@JX~=cyRtcBs=#Zv2?Kp7Nhn!w~8!BeTq3EtR`18U@+Erg8n8&&@ zjjdU{@Ad@jjk%A}PI6EYBTs8r{Dk_ijN@2(0O~deLeF0pYO~?2@bnTDuN3j4IfS!#Dr2 zl$;se?fM>qPxN8MT`96Iy-<+dk;&CN9^l#$`$h9ChgBF(|t>5)uqLU_w#2quZCk~26WL`88RVL zovb@2O)j%6X{M2WPI`&aCqwlMv|AapBy8)86RuhxtzD+tBs1zTwc>1>7o$c77{!yIUr7asj+M zcm0h43DMNzpJl(swakmx67(02e)qxEDy+Q?A+R9WE@s3&q+K@mQcdz0g&QyA*P`G}pP% z)BPML-7CX0V=rTVICEfROA?*nbZ)xx2DfF7Fp~-TVgO-5JiUBcgxX7aumsna+dv;cSI*HIHr?tUz zPaN2vtrRS4W9O4AJGl90I`DV)F>K#B2LoISP^;=J2u2Ag=i$kRD-#;*#dd8IH0h*Q z;^1cKK~Bti!w0b3YdpK31YEv=wVsS=)bR>s{Yz2HeH4vflFXaTJC8?)hu}-ZH&`H9 zjbW~LKy78)U*jYMsisg_9jf!y`Q|lFiT! z!sN;6o${R0v5Pok??&i4XhF=R)4|@hT39rhd7JI5;bh|#Tt9)M2@RVtT6I1SP&qta zb`Rnd|M79(b_lLMG^V2;?!%Pj5>%ymnDyml$eKYdQeJHc&1cyTC{GC`y`S=hE3JsU z{}0ewImipgGp`2o0?mIKir!m)LLg|;-b?yK;nH4I?{=h0_Zwk(W-7)nx(=x<=UP9? zi5L&|!S41;aQqR=TNntzXzX72Cp?BvY8VI5>5ixOXAQdjKU1 zfzijJ3u=v3ps89L$ItEoO+Ey*nIn0FLlB-?uSh(HeQ4P9sqiY_hM3)J15u$JO^P2u z?yOCLYT*q?+jt+;h2L@efGu?{l*TzqY|bx8g!~}p8M;@3rLP*WZl*aN6q=G7%u&!d z*A{Y)o&s^j8i;G%!Li*p_p&(}n(w4Ta^+H--t-T)+iiz;j8Q0i(5_&t=oLOK>Ml(0 zu_717RN=nPjeOjpJ+KP(fnOT~-ntr?`7WAeq7C3stuiY1UqQ`2Yb-l-l50L$$>tQ& z>@MI8h0FdxRarMK-seGYem#ccv*v=MP8kf1SjD#lpW*vdMTx$7EmsxETq9S#=;zmB zoY6iGT@KpP&*(wa>!iq*(e5;GV*wX1<0O<9nZe~QBQTjOgp116) zb-s$vOiK7eg%x~dnIxUPh~@V`bi>Us58|{~htBMX!H{A=~FU;El2fFrH;ZpS-ce4Yznu zu&PIKdmuXLhBTUtB15tFaqyixN|x%A*S_``^~DuCU0=X{*GSCS%`z%6cJ%i6R{T`c z$g-k$Ilp8j8nB{_@v2&2!6y#KWGm2lRk<+ss5)7+TbEOLYyd**LI`ou;YJ246So8L zsQ8NUbzc62zTE5V3=l1t^iW)|VN4sZxzr8(8pjEq<`M8xti{$t?qF+>0@|;Xxzv;H zG(e{hO+=UD;)93b=dO>~@zxw)5^=I+jVrBH><0PFCY(QWJ4je&z~7DO;2LxvJ(QKn zt;3>3RQ>=Snp?%yi+w>E8&!UW);BKV^ltvnHCqr}l#Se>O{k~(9&ZJ(dDilBUSj4O zxEk^nI`^28#tG~nl`V#MMsEkbJmzbDnS?F=X}IKiBUb(?$DH@$c^^LovRKQL+?vq| z)5glP42T2CXFZ*7z3Mb2n2^!WM`I3*<{eje@pI=2+3(N*TzRksei@5Wk8fu5kJD3J z<^2IaCIzBYZwxNlVung%_Va_Xzi}W)2|N0R*qxh@?xH@yF!NUaE=h&G>)nW4==g$) z)kY*b>n>DYFlL!=SE~E!8N{6%5}f`148uVXQ zkN$~ew(7#fB^*6b{s0CT>nV6nC*B^VK&(5xiC_9emQOAcq$%Bjqq=6i$TWg}%XooW z`(-d#YJv5(Y#t-JSU8~)A;WDU99uRP#}|EtXMtAq@RKK?H;@L=_Y#FiT6W@CDo)R4 zvG3I=J@WPa1nBuMk_$hrNfiv233aUvsrO4oD)43hld-16tLqtBJ{KX4Up}#1`CFtc zF=P9HeQvgz!ujL!P&3Yr`Y1JXM^mn$!`eo{0gqd(W2eTeWBt^XoVeus`lxgWlq!iM)a&9w^?$BWZ}f<*4UAPnud zbbx}e7&est24fRKp>zO0m3HygzCU5)p)-Qc3Fq+4Od}}xbcE|}?&3c!>SmtAWN(EF zZGyNP|GYJ7r09o5O0YHS7$mJ@`=hb})QWFFDZ+nE7p=;*JZ!k1IMwj&5lIvj)rs39Pm}xdB~L+z&6h`mXrSh z8u1FGzUL1HpJJ@?<;x-8z={h0TJo*iGI4(@o9}K^A`@Ab^mYFMToNinC1?1f?{N{b zYNrBeQBWY9ix3U=^Jp{0jhKY|0Ss>hZN>tVSsY(5*5Mz#=zRrS*8KthTQ1~I`aMo} z77(SMZ6I}6oW8EqrD6+>K*HCMrd=UKvHq)ZV4Xn8&n%Y#cm-(d!(jyi|GwtT|~-8wwNWF=p>u>4DQCigLB z5Qa7dp-SB&#{SeNUT@r~%G`yxnYn4TG{v~X%NzKG5&n2+#w6(a;Z0&*D$!|dhc2@s z!P^aZ-tdhJ=7t=FB`d=~-lLcG^=0tn5@pi5kPzMf+#%E!)c7DsrH6M$6MAtr=hu^oA3TRVK?_DVV%m4ri?_>0tbGu>9`_ zANy#t@cFMdyiJD{`K=noJKpOQl*RkuqI*)bP2v&v=xzqQZ4!ksE01ywK{|YhSpnEA z)#aYoxX@dtb;wi60d7lXB8mu9h&b!_Ui!~~_;tt;$NM6rxvB{)-Z+q@TFDqxB!>TW z&*yip&?8yy%6#bswtt%~%j>L=ptrkpVJCZ@ANzRF4YQktmzaw$p~MY+IeqfD(VYBS zUCYZQih(#fiQ<_zSkY z{0lGA*9hEw^~lG?tMFQi54koj1lFV`a*rcxc&BSV;8-n0neEdcaE>Br$^H*h^PXbw zCu{nvAp*v~tK|Ni&?H^MgSd8;6sfILp*uImayCEH_`oWjj~L;Fv%h-KkIZjqQnLcq z4wXT$%@~Z)up~1B1+aGNH7-HF2Fw+-8Nd58?DlEGgAcTc%pFax{DL<<9daJclt zn_@&E@hvyEvjs%|%)`yA`k*N=3cyi>#_vACP4?5FXy+#2rVQ>M+`(H!x`<3u& z%vcySUXOnc*pbC%?Y!&FW6*IdQ!s6(ABOk`f`#u#uG+B!vnHx@ud|Lb=lgT?f4CT> zEDOMCo*{jweg~d@Dq{D1JF@gdChTaD#5YPv;-e24pL+v34xSNiQD*bqqNni3 z(u*dl9Kmb<)aj|`zj5tPTSyvT#UDMi5M@Fp3Y#-h8L8ChvJl2*vMb7ul|Ir&Esc)jB~e65rOVm}SK=ljs4fma+CWeuqp z>}lf_56-tQ2IgJ9g}Oa8(C>c~W&~^&%J?y_jjI} z?-S_ka3$7l{t!C1fEPStZkp{~5Zw@6P;O)auFRDwQ6qun9;)Qypc7f&P$iI6+zyh# zTGVt?9Blh%L6@kQkQ>hstl7JJnaF*Vcm0a$mEvTpnkac;`%UnmCj-BByoH8mMmWJy zK=pnIdFAZMIBxqHm|nX8iY`8ddd=Tlw#s>K$EP=J^ytLy$8z-XsvNj^eh7QsJixU@ zIeg*yCvZUZ0o)#20OBq2pmlT)_^C4=d|)Ps1uM~IpW^XE=Lexq{UyF=(2G8^S0OS% z8l1tJ6Zpur50ia^;A6ZwWNWyRff-t4#s@2C`?Ck6m_K7(&kwBL8ppp$k3j55#|Mv$ zSf)J`vow2g%wCECyS&I}tFthjbu08f3Aj~1?5UYXKe|tsp;OK+fiu5YE@jq45dY46 zbX8e=tG5g2Ep{Mt4lR`d##CH)qnS`J}%Zn^7Zs#MxhO{iW!aWM!i37R)@NoAF2(3B~QhjQqdb$=N zdqn7Zs~mW4Xh7%wmV^}j7}%6khceb*;O_kCU|cOiw#Ey{v#+Xj?ZihY6z0Poxw~Mj zZAw@D5JJ}9ADAVShO%^T+sq7M-x+*a=+lPL%&8 znS-fg9l1wm?C8=E87c?L#ruj|{Z`Gaq-_ z>JU%X@7pOVNgo$ev>ES4^wLW>s}&>Ykg7TzzicM+&pVOe!33Q6M2sY!@530W5WF{~ z4EOEvrYCNlW6raa=xTBnQ|5Zp&~K&uCq);`zoJ2ohMo3qmk}ju(S=;aKWl!mWNW2!}%8&MZhtaJmc=Fh9eDmNdc+;P7b+IPZb=!&uovo?X-Vt2$EH4Ur zqaj6X1PT5v#;@HL1N-AW=p(PO=)GDN)ICSi{iF*_&gj#e@FsA{)29U|<1k}d1}|KZ z4{A#%pykwbyfIaUoXU$q|Cv9r&qA9@42wWu=tS&#@{0d=mN_(5lyLzQMvzD2J25o$ z5w|PPiY7d`iCZQVLPO4R^!KzSyQVpCRg4GzdAtg-h+BxVN3QZm)ph9PELDD}E`U>t z^&oAhi5kAi95 z*)Vgr5$#ejB^K|UNb@QIURE!_^>4JP)xl)ccYlv_7AjLy{x)B^`wN_{d5mvX%JC~D z#mFwFH7Ic+pO<@EBxv;70&+Lx$v4SGT)d}%$_xAo&gW75Dj$wxofPQ8Qs(q+=*P?T zEg0gNjxC=Fef;0(ml{8 z+|!@}iv`^%+njfAN}Mq){~Ux~L#o8VI0`=AVA&+CFKiFkg_9nOaC4X+ZfWBVE^v4v ze=k@I`<+J+CGTBuP1XSo<2`VRqz9d@o)2YgUOwsQVcZ#~MW!4sgh01uv>*Q&Rr6R+ z)GrcKn_0eg!!o@2XFu;g+wmvN zbv2>#X?23>WpDVi(Hcan_%lvf76_y4V<0G(=bf)B(hEbgQKvDL7u7Z)7w1?}^3aVc z&D@QXg>uLw8~AvV1l2yP#6OtE^6@qI_!yO=xM_DQoVfh}VmEK))_2NadY}P(o3?~c zn$`^u%=Bn(>rwo;Pm8vk-HWRXqhQH}pDd4i1gy6*Ux=y~Nl)*?%9hV)U6I1C@t3A= zul8bcb^$!AY=PA#_RN)=fZwKN;Dsrwcz%yNvD(XKGlqjuki_zDk1a{^)kPqqG8=?5 z3P8TDhEp7~sbC$y1JwqcY4Id43}R02DaMBUomrl!)=I$J!H3A0N>XX3DB-G#-!Q%6 z5$nb8$76SXVjas$oRNGCa~+zYWJ?Xp(2GEo8DkDR#e#6|Zy3L>8;Td1(3E&7a_#1G zh}STqD`iZmL|ZX8{Jo<{cjR-D``zhBRSHfMB;oohW71dN3yv%k*nM>~bci?*^Q3a_ zq{BrX{LaJ7CoeESVfk849P`Zk$R`|!D3+tLrXXs;#Yta2ec zjH7;b|zn}cn3OB)KJ00>u@+9=E$$)Re8stll4jFn7fOS&syjt`psEE9U z+szUfGrtsr3nV~w=}E{G#6nm58^(1>!^TEm!zqzK#fKyH^Q$XF{+sn zhr#2UVYhq+XkD3z4@CFk*6X5V>Ni7@5O{}IoNGjadf!9#@PDZ3`4ldvnn1kF4Ga!d zAhS-n(z#&|`Isy3@SZl?fp_?UqRAG)5v>v680tb};`PW&mJR5eqD*;{VU%Z#LvBR^ zY+3I?PPa&qlIc}=I`{_keAA#d8Oo$NR)Y50xzNLRYBB1*5jo+ygezZ_hv7APXs#ha zD%XpVLTU*?W8a}kt|E!amLSs?h|o?=Pngh^fN?q+B%)4_+O^f9ZP;;;VfW3hfytcm zSWo&hK#h)$bRzu^I68SFMZs+yK4PUhJ#21AZ=7xw7ANlIKFL3Y!6~-%5aVYihcE~G zmjrl!NrL>zPR6Y9i2|$4M%cHk3C(t$f$n>jWV!qx20s(f=jUz6VL=e~)mHO^2Urhy zi!wQ?VJA00o3wy$k2<_wZtvncyR)ZE# zk-#H6Yw=C}cgBS2hm6eCpgZv{@Axndly;0H#fdl3yr_(?ic=+rCk4Pu#wnIdYJ)}1 zomg?gn66ezf$2h7GWS9gY?>X(J*yfA(&g>&zT^dD=E_m0DW+66&=#7s1k}&jlUkUV z;g$1S;bqK7YIyN0ztPklYbWZHOu{+`Uu=k7kSzHYV20mPBEj#lHzXMlg3}a82itL* zRJhRT#z9~fXhnp}^|>>*8Zfn*UL85h41yiM6VVSxq89&j7 zw)E{o?ceTn*Y@rB>#I8Hept?ClD1%V+?kwHs)1wA9|^WEDa3{!MHr{qh8r%d~>z_L0NVO9kp6x>w#=Xlgams<$ie21&uUI&i{2mr8Zii9Y zj9{P(n^Xb>^$@E!`y!v=q@ZGfr!xZvx;M^DRyYIz+cZx+-A9Ly&egy8Vl%&bN z#ta>+PQm*fkhO5`a zsQ&~Zu1mTM|2h~0xOEQBpJq*;cFGdtwLP$U+D07E^`aw3Yh&c-cAzVMz(GeI<$X=b z>xm<&)^TxcWcd;Q2{QC#ND)j^p3HgPA;iSS06w5Bob+Fg24PFNKmYxPGPPSU6g~uV zoDIprwb}fyBX2m1f>g{*_NLP3lxTFd9UUja!PNOTxHU&^!Erw+63w3w?&|u1!>#?C zV}K<&v%rCl+GcAoI{mZNZGv=U{@B(T0V5B#0GA`s0Pd74KM&Xr?>u zyC+IOkuio>9OCA%o%rV7a$FIf%O~|u!G6XuT;C{8&`un8e#pbH3_l2O8AKJG>2B(zQN9A0?U3a%EBVN2<-LkWp^-!;6mE*d z-aS?dwBJ*poPR)u1^tqf^RzRN8( zU&xKmks?OXEvONfi|?1-#ma^1RCg$gKccBkQ|3#Mzg}8&>xnp=UcFR!UGF#EW#^K0 zzehu2(nq-biSg^Ae{k#13Ncr_8bTMk5s$)r`0COQ<4&ho!6Yc1t3 zq|L;Crz7#t`O`Imv( zwO7z*!UGr-TGO{8k2&j6$FTLX13Y6chl3Mu@kUuvFfZyC*tI|AH)1d!H-Ct?xevlAJ6tgE@6V-^`F_u++??H>$zR&0N zDqb&Bj+*&aqm}d}*gxk!Se~kay+PTSFsB7?HZq^-(^D84=t4K$dRlmOmuLrF_HqqAGBkmwL77n82bW5^6RF`him7#vUN;KuF zHi_Prg59cN+|(y281e2Y4t%^0UIA~R_h%i5XhrZhKCI?9_%WWo9D-qqB$26C%2!

    _O0f4)2S>47CV@?n3G$vzLF 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℞Gp7&2WYy8U1$2XGb}}cY zZXmeu-if%UJ>U~eXMxLtR#e%TgBzL|yQp&>Ul_w!y3Fy~FKbL)L|8s}r6D-w5pI{i;*I0qL+#q_d~CQh#GGk_W=|Eal=G$awnI>KrvzhIclL0V8Hs)C zN)C)!3}d^Pe>y}LqALgul>CAM!2y8GhrG@$4?4L1JT^6~fY+w4`Ny5Lc;iG3=d(-& zM8eJJxBel}^z&l-!bcFcT%RNrT*2uVJjk)UBJ2x`hdRa^d2ek-lOMUFd5!`(_bwH* z*Z$$Yc8KBeDXL`g<~aUNiZzis;y`6Xn{e4L5!^Ff6%#a7;Z&0g5qPSR)~Slbsrv_B zZe#AdPb*=~-8?Qd^cPcR0kG*j+aue~l?f-M1w#-zbol`wlesO+0^Z#4wy$%bfMc zyn^N>Hk=P zGADOom7ObbI_3ezzADsk@q7Gb>`L&712JCH3hJxH=&573L3sKsSE%nmTATl3bhC)i zYkm`4@jQ#gY+p@h6oTY>Q&MR12=fb1!|6vO_#(MzoZ>Z^|L@-bZrt@6isL>4r(@5} ze_=uA*jeJ^BYI%*K#o4&szK!!DbU+4jK_Fd51N<{2F?1=I(i@cz2!+xTy4Yk>u!VW z2FB8pJq6b+HOUDLO>%DM7yOVo8-BI1F5kFPxWM)-cdm}a2~n?5V+ng6{6D~254OJu zcn2tNL^+2XKIDKl-8*tG{!tx7vCqsA*xCU6v`kzr=R^1Kj2CnLFn@cJG>yuSf|{IQ zfsM8g&0muV4?nvQw+T*|rsYj9vG-R@NRePl{bl$o=S;?T4DgHoU4SDQ%6wq{Xe{+r zrL+DPq95z5uJiIGcIi41ve!Xh>_AdXn4yw zmAxOZUFIDUUyeu>rJ~!J%`my54UCIlLu{HSwbyhbUS-x;zp4P{|COexvl}^!i0Kg9 ze-_*wA8`L+B2Myh7tT~vr{&^4%$93IE(eQTImnlgvTb>t^00rt|eUe-8$^_^3 zRzjuRRR{~Y0n_Do3a7Pf6nOW@(~z?ocy@IX#2S?g#HMSK+N<}m*h+ys;S7o5#tEgL zzg+_RV`^lQ@>LxArcGD+o`k6F^Ks)vS2En*50℞JLXOQD_u_gp1!msp)m;xu#s4 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 aX8 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 zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U 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 at the end and treat ' - 'it as a non-padded label.') - p.Define('pad_to_max_length', True, - 'If True, output ids will be padded to max_length.') - # TODO(ciprianchelba): there should be a check in __init__ that the ids - # below are consistent with the ones assigned by the vocabulary. - p.Define('target_unk_id', 0, 'Target unknown token id.') - p.Define('target_sos_id', 1, 'Target start of sequence id.') - p.Define('target_eos_id', 2, 'Target end of sequence id.') - p.Define('target_wb_id', -1, 'Target word boundary id.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - self.sos_id = p.target_sos_id # - self.eos_id = p.target_eos_id # - self.unk_id = p.target_unk_id # - - self.SetVariableFree() - - def StringsToIds(self, - strs, - max_length, - external_append_eos=None, - languages=None): - """Tokenize strs into vocab ids. - - Args: - strs: A vector of strings. - max_length: An int providing the max_length for strs. - external_append_eos: Bool or None. If None, will be ignored and - `params.append_eos` will be used. If bool, will determine if an eos - symbol will be added to tokens. - languages: A vector of strings with the same length as `strs`. - - Returns: - A tuple (ids, labels, paddings) with the same shape [batch, maxlen]. - - - ids[i, j] is the input token id of i-th sample for j-th step. - - labels[i, j] is the target token id of i-th sample for j-th step. - - paddings[i, j] is 1 iff i-th sample's j-th step is padded. - - Raises: - ValueError: If unknown token type. - """ - p = self.params - - if external_append_eos is None: - append_eos = p.append_eos - else: - append_eos = external_append_eos - - return self._StringsToIdsImpl(strs, max_length, append_eos, languages) - - def _StringsToIdsImpl(self, strs, max_length, append_eos, languages): - raise NotImplementedError('Abstract method.') - - def IdsToStrings(self, ids, lens, languages=None): - """Converts ids back to strings. - - Args: - ids: A matrix of shape [batch, seqlen]. ids[i, :] is the i-th sample's - ids. - lens: A vector of shape [batch]. lens[i] is the sequence length of the - i-th sample. Only the first lens[i] tokens in ids[i, :] are valid tokens - for the i-th sequence. - languages: A vector of strings of shape [batch]. - - Returns: - sequences - A vector of shape [batch]. The converted string sequence. - - Raises: - ValueError: If unknown token type. - """ - raise NotImplementedError('Abstract method.') - - -class AsciiTokenizer(BaseTokenizer): - """A simple grapheme tokenizer. - - Maps a small vocabulary of character tokens for (lower case) letters, digits, - and punctuation symbols. - """ - - def _StringsToIdsImpl(self, strs, max_length, append_eos, languages): - p = self.params - return ops.ascii_to_token_id( - strs, - maxlen=max_length, - pad_to_maxlen=p.pad_to_max_length, - append_eos=append_eos) - - def IdsToStrings(self, ids, lens): - return ops.id_to_ascii(ids, lens) - - -class VocabFileTokenizer(BaseTokenizer): - """Tokenizers that use vocab files for look-up.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('token_vocab_filepath', None, - 'If set, specifies a filepath to the token vocab file.') - p.Define('ngram_vocab_filepath', None, - 'If set, specifies a filepath to the Ngram vocab file.') - p.Define('ngram_separator', '', - 'string separator to use when joining ngrams.') - p.Define('tokens_delimiter', ' ', - 'The delimiter to split a string to tokens with.') - p.Define( - 'load_token_ids_from_vocab', True, - 'Whether token ids are present in vocab (i.e. vocab contains two ' - 'colums, one for IDs and one for words). If false, line numbers ' - 'are used.') - return p - - @property - def _vocab_file_params(self): - return ['token_vocab_filepath', 'ngram_vocab_filepath'] - - def _CheckParams(self): - p = self.params - num_params_specified = sum( - [getattr(p, x) is not None for x in self._vocab_file_params]) - if num_params_specified != 1: - raise ValueError('Exactly one vocab file should be specified!') - - def _StringsToIdsImpl(self, strs, max_length, append_eos, languages): - self._CheckParams() - p = self.params - - if p.token_vocab_filepath: - return ops.str_to_vocab_tokens( - strs, - maxlen=max_length, - pad_to_maxlen=p.pad_to_max_length, - append_eos=append_eos, - vocab_filepath=p.token_vocab_filepath, - load_token_ids_from_vocab=p.load_token_ids_from_vocab, - delimiter=p.tokens_delimiter) - elif p.ngram_vocab_filepath: - raise NotImplementedError('ngram vocab StringsToIds is not supported.') - - def IdsToStrings(self, ids, lens): - self._CheckParams() - p = self.params - if p.token_vocab_filepath: - ngram_vocab_filepath = p.token_vocab_filepath - ngram_separator = p.tokens_delimiter - elif p.ngram_vocab_filepath: - ngram_vocab_filepath = p.ngram_vocab_filepath - ngram_separator = p.ngram_separator - - return ops.ngram_id_to_token( - token_ids=ids, - seq_lengths=lens, - ngram_vocab_filepath=ngram_vocab_filepath, - ngram_separator=ngram_separator) - - -class BpeTokenizer(BaseTokenizer): - """Tokenizers that use BPE vocab files and word to id lists for look-up.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('codes_filepath', None, - 'Specifies a filepath to the list of bpe codes vocab file.') - p.Define('words_to_ids_filepath', None, - 'Specifies a filepath to the word bpe vocab file.') - return p - - def _StringsToIdsImpl(self, strs, max_length, append_eos, languages): - p = self.params - - return ops.bpe_words_to_ids( - strs, - maxlen=max_length, - append_eos=append_eos, - tokenization_filepath=p.words_to_ids_filepath) - - def IdsToStrings(self, ids, lens): - p = self.params - - return ops.bpe_ids_to_words( - token_ids=ids, seq_lengths=lens, vocab_filepath=p.codes_filepath) - - -class WpmTokenizer(BaseTokenizer): - """Tokenizer for word-piece models.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'vocab_filepath', None, - 'Specifies a filepath to the WPM vocab. The vocab is sorted by ' - 'descending merge score.') - p.Define( - 'merge_prob', 1., - 'Probability of merging WPMs. If less than 1, then decomposition ' - 'of words into wordpieces will no longer be deterministic, and ' - 'result in longer ID sequences. At 0, it will be graphemes.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._wpm_encoder = wpm_encoder.WpmEncoder(p.vocab_filepath, p.merge_prob) - assert p.target_unk_id == self._wpm_encoder.unk_id - assert p.target_sos_id == self._wpm_encoder.sentence_start_id - assert p.target_eos_id == self._wpm_encoder.sentence_end_id - - def _StringsToIdsImpl(self, strs, max_length, append_eos, languages): - """Takes a tensor of strings and returns id/padding tensors. - - This generates `token_ids`, `target_ids`, and `paddings` in the format that - is expected for tokenizers. This performs padding to a fixed length and - appends the end-of-sentence token as appropriate. - - Args: - strs: a string Tensor. - max_length: a python integer. The second dimension of the returned arrays. - All sequences are padded or truncated to that length. - append_eos: a python bool. See `BaseTokenizer` for explanation. - languages: A vector of strings with the same length as `strs`. - - Returns: - A tuple of 3 tensors: - - - token_ids: a tensor of sequences of WPM ids starting with SOS. Sequences - always end with EOS unless the sequence exceeds the maximum length. - Always padded with EOS. - - target_ids: a tensor of sequences of WPM ids not starting with SOS - but ending with EOS. Always padded with EOS. - - paddings: a tensor of floats indicating, at each position, whether - the corresponding position is padded. - """ - p = self.params - if append_eos is None: - append_eos = p.append_eos - - batch_size = py_utils.GetShape(strs)[0] - token_ids_ta = tf.TensorArray(tf.int32, batch_size) - target_ids_ta = tf.TensorArray(tf.int32, batch_size) - paddings_ta = tf.TensorArray(tf.float32, batch_size) - - def _TokenizeOneSentence(i, strs, token_ids_ta, target_ids_ta, paddings_ta): - """Tokenizes a single sentence.""" - ids, _ = self._wpm_encoder.Encode(strs[i]) - - if append_eos: - ids = tf.concat([ids, [self.eos_id]], axis=0) - - # This truncates after the eos is added, so some sentences might - # not have at the end. - token_ids_ta = token_ids_ta.write( - i, - py_utils.PadOrTrimTo( - tf.concat([[self.sos_id], ids], axis=0), [max_length], - self.eos_id)) - target_ids_ta = target_ids_ta.write( - i, py_utils.PadOrTrimTo(ids, [max_length], self.eos_id)) - paddings_ta = paddings_ta.write( - i, - py_utils.PadOrTrimTo( - tf.zeros_like(ids, dtype=tf.float32), [max_length], 1.)) - - return i + 1, strs, token_ids_ta, target_ids_ta, paddings_ta - - _, _, token_ids_ta, target_ids_ta, paddings_ta = tf.while_loop( - lambda i, *_: i < batch_size, - _TokenizeOneSentence, - loop_vars=(tf.constant(0, tf.int32), strs, token_ids_ta, target_ids_ta, - paddings_ta), - parallel_iterations=30, - back_prop=False) - - token_ids = token_ids_ta.stack() - target_ids = target_ids_ta.stack() - paddings = paddings_ta.stack() - - if not p.pad_to_max_length: - maxlen = tf.cast( - tf.round(tf.reduce_max(tf.reduce_sum(1.0 - paddings, axis=1))), - tf.int32) - token_ids = token_ids[:, :maxlen] - target_ids = target_ids[:, :maxlen] - paddings = paddings[:, :maxlen] - - return token_ids, target_ids, paddings - - def IdsToStrings(self, ids, lens): - """Takes integer matrices and returns vectors of strings.""" - ids = py_utils.with_dependencies([py_utils.assert_same_dim0([ids, lens])], - ids) - return tf.map_fn( - lambda inputs: self._wpm_encoder.Decode(inputs[0][:inputs[1]]), - (ids, lens), - dtype=tf.string, - parallel_iterations=30, - back_prop=False) diff --git a/lingvo/core/tpu_embedding_layers.py b/lingvo/core/tpu_embedding_layers.py deleted file mode 100644 index 8d63c2c2..00000000 --- a/lingvo/core/tpu_embedding_layers.py +++ /dev/null @@ -1,547 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""TPU embedding layers.""" - -import math -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils - -# pylint:disable=g-direct-tensorflow-import -from tensorflow.python.tpu import tpu_embedding as tpu_embedding_lib -# pylint:enable=g-direct-tensorflow-import - - -class _TPUEmbeddingOptimizer(base_layer.BaseLayer): - """Base class for TPUEmbeddingLayer, TPUEmbeddingTable optimizers.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('learning_rate', None, 'Used for updating embedding table.') - p.Define('clip_weight_min', None, - 'The minimum value to clip by; None means -infinity.') - p.Define('clip_weight_max', None, - 'The maximum value to clip by; None means +infinity.') - p.Define( - 'weight_decay_factor', None, - 'Amount of weight decay to apply; None means that the weights are not ' - 'decayed.') - p.Define( - 'multiply_weight_decay_factor_by_learning_rate', None, - 'If true, weight_decay_factor is multiplied by the current learning ' - 'rate.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - - @property - def tpu_embedding_optimizer_parameters(self): - return self._tpu_embedding_optimizer_parameters - - def CreateSlotVariablesAndOps(self, table_vars, tpu_embedding_table): - """Create slot variables and infeed/retrieval ops. - - Args: - table_vars: A list of all embedding table shard variables. - tpu_embedding_table: Parent TPUEmbeddingTable layer. - - Returns: - List of load ops - List of retrieve ops - """ - return NotImplementedError() - - -class TPUEmbeddingSGDOptimizer(_TPUEmbeddingOptimizer): - """SGD optimizer for TPUEmbeddingLayer, TPUEmbeddingTable.""" - - def __init__(self, params): - super().__init__(params) - p = self.params - self._tpu_embedding_optimizer_parameters = ( - tpu_embedding_lib.StochasticGradientDescentParameters( - learning_rate=p.learning_rate, - clip_weight_min=p.clip_weight_min, - clip_weight_max=p.clip_weight_max, - weight_decay_factor=p.weight_decay_factor, - multiply_weight_decay_factor_by_learning_rate=p - .multiply_weight_decay_factor_by_learning_rate)) - - def CreateSlotVariablesAndOps(self, table_vars, tpu_embedding_table): - load_op_list = [] - retrieve_op_list = [] - - num_tpu_hosts = tpu_embedding_table.params.num_tpu_hosts - table_name = tpu_embedding_table.table_name - - for host_id, table_var in zip(range(num_tpu_hosts), table_vars): - # The slot vars should be on the same device as the table var. - device_name = tpu_embedding_table.GetDeviceName(host_id) - with tf.device(device_name), py_utils.outside_all_rewrites(): - # Only the Trainer needs these ops. - if py_utils.use_tpu(): - # TPU Embedding load/retrieve ops need to be in the outer graph scope. - with tf.init_scope(): - tf.logging.info('creating load and retrieve ops.') - load_parameters_op = ( - tpu_embedding_lib.tpu_ops - .load_tpu_embedding_stochastic_gradient_descent_parameters( - parameters=table_var, - table_name=table_name, - num_shards=num_tpu_hosts, - shard_id=host_id)) - load_op_list.append(load_parameters_op) - - retrieved_table = ( - tpu_embedding_lib.tpu_ops - .retrieve_tpu_embedding_stochastic_gradient_descent_parameters( - table_name=table_name, - num_shards=num_tpu_hosts, - shard_id=host_id)) - retrieve_parameters_op = tpu_embedding_lib.control_flow_ops.group( - tf.assign(table_var, retrieved_table)) - retrieve_op_list.append(retrieve_parameters_op) - - return load_op_list, retrieve_op_list - - -class TPUEmbeddingAdagradOptimizer(_TPUEmbeddingOptimizer): - """Adagrad optimizer for TPUEmbeddingLayer, TPUEmbeddingTable.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('initial_accumulator', 0.1, - 'Initial value of Adagrad accumulator.') - p.Define( - 'use_gradient_accumulation', True, - 'Setting this to False makes embedding gradients calculation less ' - 'accurate but faster. See tpu_embedding_lib for more details.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._tpu_embedding_optimizer_parameters = ( - tpu_embedding_lib.AdagradParameters( - learning_rate=p.learning_rate, - initial_accumulator=p.initial_accumulator, - clip_weight_min=p.clip_weight_min, - clip_weight_max=p.clip_weight_max, - weight_decay_factor=p.weight_decay_factor, - multiply_weight_decay_factor_by_learning_rate=p - .multiply_weight_decay_factor_by_learning_rate)) - - def CreateSlotVariablesAndOps(self, table_vars, tpu_embedding_table): - p = self.params - - load_op_list = [] - retrieve_op_list = [] - - num_tpu_hosts = tpu_embedding_table.params.num_tpu_hosts - table_name = tpu_embedding_table.table_name - slot_var_collections = [tpu_embedding_table.__class__.__name__ + '_vars'] - - for host_id, table_var in zip(range(num_tpu_hosts), table_vars): - # The slot vars should be on the same device as the table var. - device_name = tpu_embedding_table.GetDeviceName(host_id) - with tf.device(device_name), py_utils.outside_all_rewrites(): - w_ada = py_utils.WeightParams( - shape=table_var.shape.as_list(), - init=py_utils.WeightInit.Constant(p.initial_accumulator), - dtype=p.dtype, - collections=slot_var_collections) - var_name = tpu_embedding_table.GetVariableName(host_id) - tpu_embedding_table.CreateVariable( - '%s/Adagrad' % var_name, w_ada, trainable=False) - accumulator_var = tpu_embedding_table.vars['%s/Adagrad' % var_name] - - # Only the Trainer needs these ops. - if py_utils.use_tpu(): - # TPU Embedding load/retrieve ops need to be in the outer graph scope. - with tf.init_scope(): - tf.logging.info('creating load and retrieve ops.') - load_parameters_op = ( - tpu_embedding_lib.tpu_ops.load_tpu_embedding_adagrad_parameters( - parameters=table_var, - accumulators=accumulator_var, - table_name=table_name, - num_shards=num_tpu_hosts, - shard_id=host_id)) - load_op_list.append(load_parameters_op) - - retrieved_table, retrieved_accumulator = ( - tpu_embedding_lib.tpu_ops - .retrieve_tpu_embedding_adagrad_parameters( - table_name=table_name, - num_shards=num_tpu_hosts, - shard_id=host_id)) - retrieve_parameters_op = tpu_embedding_lib.control_flow_ops.group( - tf.assign(table_var, retrieved_table), - tf.assign(accumulator_var, retrieved_accumulator)) - retrieve_op_list.append(retrieve_parameters_op) - - return load_op_list, retrieve_op_list - - -class TPUEmbeddingTable(base_layer.BaseLayer): - """An embedding table controlled by TPUEmbeddingLayer. - - Note that all input_keys needs to be declared upfront. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('vocab_size', 0, 'Depth of the input.') - p.Define('embedding_dim', 0, 'Depth of the output.') - p.Define('input_keys', None, 'Name of inputs in InputBatch.') - p.Define( - 'combiner', 'mean', - 'Must be "sum", "sqrtn", "mean" or None in the case of a ' - '"sequence embedding "') - p.Define( - 'max_sequence_length', None, - 'If not None or 0, embedding lookup will return a ' - '"sequence embedding" of shape ' - '`[batch, max_sequence_length, embedding_dim]` without applying a ' - 'sequence reducing combiner') - p.Define('num_tpu_hosts', 0, 'Total number of TPU hosts.') - p.Define( - 'optimizer', None, - 'Table optimizer parameters. Will override the optimizer parameters ' - 'defined in this table\'s TPUEmbeddingLayer.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.vocab_size > 0 - assert p.embedding_dim > 0 - assert p.input_keys - assert p.name - assert p.num_tpu_hosts > 0 - if p.combiner is None: - assert p.max_sequence_length - if p.max_sequence_length is not None and p.max_sequence_length > 0: - assert p.combiner is None - - self._ids_per_shard = int(math.ceil(float(p.vocab_size) / p.num_tpu_hosts)) - self._padded_vocab_size = self._ids_per_shard * p.num_tpu_hosts - self._input_keys = p.input_keys - - self._max_sequence_length = 0 - if p.max_sequence_length: - self._max_sequence_length = p.max_sequence_length - - self.CreateChild('optimizer', p.optimizer) - - self._table_name = '{}_table'.format(p.name) - self._table_config = tpu_embedding_lib.TableConfig( - self._padded_vocab_size, - p.embedding_dim, - combiner=p.combiner, - optimization_parameters=self.optimizer - .tpu_embedding_optimizer_parameters) - - self._load_op_list = [] - self._retrieve_op_list = [] - - def _CreateLayerVariables(self): - p = self.params - w_pc = py_utils.WeightParams( - shape=[self._ids_per_shard, p.embedding_dim], - init=p.params_init, - dtype=p.dtype, - collections=[self.__class__.__name__ + '_vars']) - - embedding_table_vars = [] - for i in range(p.num_tpu_hosts): - device_name = self.GetDeviceName(i) - with tf.device(device_name), py_utils.outside_all_rewrites(): - var_name = self.GetVariableName(i) - self.CreateVariable(var_name, w_pc) - embedding_var = self.vars[var_name] - embedding_table_vars.append(embedding_var) - # Remove from _private_vars / _private_thetas to be added later as wm. - del self._private_vars[var_name] - del self._private_theta[var_name] - - if not py_utils.use_tpu(): - # We don't want to add this for TrainerTpu, otherwise the identity - # reference leads to copying the embedding to the TPU for no reason. - # However, this is needed for CPU (eval/decode/controller). - self._private_vars['wm'] = embedding_table_vars - self._private_theta['wm'] = [tf.identity(v) for v in embedding_table_vars] - - # Only trainer and controller need slot variables and load/retrieve ops. - if not self.do_eval: - self._load_op_list, self._retrieve_op_list = ( - self.optimizer.CreateSlotVariablesAndOps(embedding_table_vars, self)) - - # Return device to place sharded variables on. - def GetDeviceName(self, host_id): - if self.do_eval: - return None - else: - return '{}/replica:0/task:{}/device:CPU:0'.format( - self.cluster.params.worker.name, host_id) - - # Return variable name for embedding table shards. - def GetVariableName(self, host_id): - return 'var_%d' % host_id - - @property - def table_config(self): - return self._table_config - - @property - def table_name(self): - return self._table_name - - @property - def retrieve_op_list(self): - return self._retrieve_op_list - - @property - def load_op_list(self): - return self._load_op_list - - @property - def input_keys(self): - return self._input_keys - - @property - def max_sequence_length(self): - return self._max_sequence_length - - def CpuEmbLookup(self, ids_map): - """CPU evaluation embedding lookup. - - Args: - ids_map: A dict of `input_key` string -> [batch, sequence] int32 Tensor. - -1 is used as a padding id. - - Returns: - An activations dict of string -> float32 Tensor. - For non-sequence embeddings: [batch, 1, embedding_dim] - For sequence embeddings: [batch, max_sequence_length, embedding_dim] - - """ - p = self.params - rets = py_utils.NestedMap() - if self.max_sequence_length > 0: - # "Sequence embedding", no combiner case - for k, ids in ids_map.items(): - embs = tf.nn.embedding_lookup(self.theta.wm, tf.reshape(ids, [-1])) - out_shape = tf.concat([tf.shape(ids), [p.embedding_dim]], 0) - rets[k] = tf.reshape(embs, out_shape) - else: - # Non-"Sequence embedding", combiner case - for k, ids in ids_map.items(): - # Dense to sparse. - dense_shape = tf.shape(ids, out_type=tf.int64) - sample_indices = tf.cast(tf.where(tf.not_equal(ids, -1)), tf.int64) - embedding_indices = tf.cast(tf.gather_nd(ids, sample_indices), tf.int64) - sparse_ids = tf.SparseTensor( - indices=sample_indices, - values=embedding_indices, - dense_shape=dense_shape) - # [?, embedding_dim] - # For tf.nn.embedding_lookup_sparse, output.dim0 might be different from - # sparse_ids.dense_shape.dim0. - # In fact, the '?' is the smallest span starting from the index=0 that - # covers all the results. - embs = tf.nn.embedding_lookup_sparse( - self.theta.wm, - sparse_ids, - None, # sp_weights - combiner=p.combiner) - batch_size = dense_shape[0] - # Explicitly pad results to maintain dim0=batch. - dim0_padlen = tf.cast(batch_size, tf.int32) - tf.shape(embs)[0] - embs = tf.pad(embs, [[0, dim0_padlen], [0, 0]]) - # [batch, 1, embedding_dim] - embs = py_utils.HasShape(embs, [batch_size], ndims=1) - rets[k] = tf.expand_dims(embs, 1) - return rets - - -class TPUEmbeddingLayer(base_layer.BaseLayer): - """Monolithic interface to TPU embedding. - - This layer has some important caveats, due to the interface of the - TPU embedding hardware. Its behavior most closely mimics that of - tf.nn.embedding_lookup_sparse. - - Supports multiple tables and multiple input_keys per table. - Requires its own optimizer parameters. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('tables', None, 'TPUEmbeddingTables') - p.Define('pipeline_execution_with_tensor_core', False, - 'Set to True to be faster. See tpu_embedding.py for details.') - p.Define('batch_size', 0, 'Per-core batch size.') - p.Define( - 'optimizer', TPUEmbeddingAdagradOptimizer.Params(), - 'Layer optimizer parameters. Will be used for any TPUEmbeddingTables ' - 'with None optimizer parameters.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - assert p.tables - assert p.batch_size > 0 - assert p.name - - num_tpu_hosts = p.tables[0].num_tpu_hosts - assert all([t.num_tpu_hosts == num_tpu_hosts for t in p.tables]) - - # Stop if a table has no optimizer parameters and the layer also has no - # optimizer parameters - table_optimizer_missing = any( - table_params.optimizer is None for table_params in p.tables) - if not p.optimizer and table_optimizer_missing: - raise ValueError( - 'A table is missing optimizer parameters, and no layer-level ' - 'optimizer parameters were given.') - elif table_optimizer_missing: - for table_params in p.tables: - if table_params.optimizer is None: - table_params.optimizer = p.optimizer.Copy() - - self.CreateChildren('tables', p.tables) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - for table in self.tables: - table.InstantiateVariables() - super()._CreateChildrenVariables() - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - - load_op_list = [] - retrieve_op_list = [] - - # At the feature level, track which are associated - # with "sequence embeddings". - self._sequence_features = {} - - if py_utils.use_tpu(): - num_cores = self.cluster.params.worker.tpus_per_replica - global_batch_size = ( - self.params.batch_size * self.cluster.num_splits_per_client) - table_to_config_dict = {} - feature_to_config_dict = {} - for table in self.tables: - table_to_config_dict[table.table_name] = table.table_config - load_op_list += table.load_op_list - retrieve_op_list += table.retrieve_op_list - for feature in table.input_keys: - if table.max_sequence_length > 0: - self._sequence_features[feature] = True - feature_to_config_dict[feature] = tpu_embedding_lib.FeatureConfig( - table.table_name, max_sequence_length=table.max_sequence_length) - mode = tpu_embedding_lib.TRAINING - device_config = tpu_embedding_lib.DeviceConfig( - num_cores=num_cores, - num_hosts=self.params.tables[0].num_tpu_hosts, - job_name=self.cluster.params.worker.name) - self._tpu_embedding = tpu_embedding_lib.TPUEmbedding( - table_to_config_dict, - feature_to_config_dict, - global_batch_size, - mode, - master=None, - pipeline_execution_with_tensor_core=( - self.params.pipeline_execution_with_tensor_core), - device_config=device_config) - tf.add_to_collection(py_utils.TPU_EMBEDDING, self._tpu_embedding) - - tf.logging.info('adding load and retrieve ops to collection.') - tf.add_to_collection(py_utils.TPU_EMBEDDING_LOAD_OPS, load_op_list) - tf.add_to_collection(py_utils.TPU_EMBEDDING_RETRIEVE_OPS, - retrieve_op_list) - - def EmbLookup(self, ids_map): - """Looks up embedding vectors for each entry in ids_map. - - Since the TPUEmbedding is monolothic, and consulted once per - FProp/BPRop, we must centralize the lookup. Thus, for multiple - features, we contain them into a single-lookup rather than allowing - the caller to call Lookup multiple times. - - Currently, there's also an implied combination step which combines - the sequence into a single set of activations by sum, mean or - sqrtn. - - Args: - ids_map: A dict of `input_key` string -> [batch, sequence] int32 Tensor. - -1 is used as a padding id. - - Returns: - Activations dict of string -> - For non-sequence embeddings: [batch, 1, embedding_dim], - For sequence embeddings: [batch, max_sequence_length, embedding_dim] - float32 Tensor. - """ - - def TpuEmbLookup(ids_map): - """TPU Embedding lookup.""" - del ids_map - activations = self._tpu_embedding.get_activations() - tf.add_to_collection(py_utils.TPU_EMBEDDING_ACTIVATIONS, activations) - ret = py_utils.NestedMap() - for k, v in activations.items(): - if k in self._sequence_features: - ret[k] = v - else: - # Non-sequence embeddings, we fill the "time" dimension with 1. - ret[k] = tf.expand_dims(v, axis=[1]) - return ret - - def CpuEmbLookup(ids_map): - """CPU evaluation embedding lookup.""" - rets = py_utils.NestedMap() - for table in self.tables: - table_id_map = {} - for key in table.input_keys: - table_id_map[key] = ids_map[key] - table_rets = table.CpuEmbLookup(table_id_map) - # Merge table_rets with rets - for k, v in table_rets.items(): - rets[k] = v - return rets - - if self.do_eval: - return CpuEmbLookup(ids_map) - elif not py_utils.use_tpu(): - # Contoller - return CpuEmbLookup(ids_map) - else: - # TPU Trainer - return TpuEmbLookup(ids_map) diff --git a/lingvo/core/tpu_summary.py b/lingvo/core/tpu_summary.py deleted file mode 100644 index 97a345ae..00000000 --- a/lingvo/core/tpu_summary.py +++ /dev/null @@ -1,227 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Alternative to tf.summary for TPU without host call and outside compilation. - -To add a summary, in layer implementation code call:: - - tpu_summary.scalar('name', value) - -Then in trainer code:: - - my_model = my_model_params.Instantiate() - with tpu_summary.context(): - my_model.ConstructFPropBPropGraph() - summaries = tpu_summary.merge_all() - -If some summaries are created inside a while loop:: - - my_model = my_model_params.Instantiate() - with tpu_summary.context(rewrite_while_loop=True): - output = my_model.GreedyDecodeIds() - summaries = tpu_summary.merge_all() - -Note that you must call tpu_summary.merge_all() inside tpu_summary.context(), -or else it will return nothing. - -Note also that merge_all() returns a dict of tensors, not a serialized proto. -As such, merge_all() can be used inside tpu rewrite context. -It is left up to the caller to decide how to return summary tensors from TPU -and what to do next. -""" - -import contextlib - -from lingvo import compat as tf - - -class TpuSummaryScalar: - """Plain data object. Much better than namedtuple.""" - name = None - value = None - name_scope = None - while_loop_reduce = None - - -class TpuSummaryContext: - """Non-reentrant context that holds the list of summary tensors.""" - - _global_stack = [] - - @classmethod - def current(cls): - """Returns current context or None.""" - if cls._global_stack: - return cls._global_stack[-1] - else: - return None - - def __init__(self): - self.summary_tensors = [] - - def __enter__(self): - assert not self._global_stack, 'no re-entry' - self._global_stack.append(self) - - def __exit__(self, *args): - self._global_stack.pop() - - -class RewriteLoopContext: - """Context manager. Rewrites tf.while_loop to propagate summary tensors.""" - - def __init__(self, max_loop_vars=16, dtype=tf.float32): - self.max_loop_vars = max_loop_vars - self.dtype = dtype - self.tf_while_loop = None - - def __enter__(self): - self.tf_while_loop = tf.while_loop - tf.while_loop = self.while_loop - - def __exit__(self, *args): - tf.while_loop = self.tf_while_loop - self.tf_while_loop = None - - def while_loop(self, cond, body, loop_vars, **kwargs): - """Wrapper for tf.while_loop that adds summary_tensors to loop vars.""" - ctx = TpuSummaryContext.current() - assert ctx is not None, 'must be inside TpuSummaryContext' - assert self.tf_while_loop, 'must be inside self RewriteLoopContext' - - outer_summary_tensors = ctx.summary_tensors - ctx.summary_tensors = [] - - accumulators = [ - tf.constant(0, dtype=self.dtype) for _ in range(self.max_loop_vars) - ] - size = kwargs['maximum_iterations'] # as if not None - arrays = [ - tf.TensorArray(self.dtype, size) for _ in range(self.max_loop_vars) - ] - loop_count = tf.constant(0, dtype=tf.int32) - - def loop_body(loop_vars, accumulators, arrays, loop_count): - loop_vars = body(*loop_vars) - del ctx.summary_tensors[self.max_loop_vars:] - for i, x in enumerate(ctx.summary_tensors): - if x.while_loop_reduce == 'stack': - arrays[i] = arrays[i].write(loop_count, tf.cast(x.value, self.dtype)) - else: - accumulators[i] += tf.cast(x.value, self.dtype) - loop_count += 1 - return loop_vars, accumulators, arrays, loop_count - - def loop_cond(loop_vars, accumulators, arrays, loop_count): - del accumulators, arrays, loop_count - return cond(*loop_vars) - - loop_vars, accumulators, arrays, loop_count = self.tf_while_loop( - cond=loop_cond, - body=loop_body, - loop_vars=(loop_vars, accumulators, arrays, loop_count), - **kwargs) - - for i, x in enumerate(ctx.summary_tensors): - if x.while_loop_reduce == 'stack': - x.value = arrays[i].stack() - elif x.while_loop_reduce == 'mean': - denominator = tf.cast(tf.math.maximum(1, loop_count), self.dtype) - x.value = accumulators[i] / denominator - else: - x.value = accumulators[i] - - ctx.summary_tensors = outer_summary_tensors + ctx.summary_tensors - return loop_vars - - -def scalar(name, value, while_loop_reduce='mean'): - """Adds summary scalar. - - Outside of tpu_summary.context() does nothing. - - Args: - name: string name - value: scalar tensor value - while_loop_reduce: optional argument, determines what to do when this - summary appears inside a tf.while_loop. Can be 'mean' or 'sum'. - """ - assert while_loop_reduce in ('mean', 'sum') - ctx = TpuSummaryContext.current() - if ctx is None: - return - x = TpuSummaryScalar() - x.name = str(name) - x.value = tf.convert_to_tensor(value) - if x.value.shape != (): # pylint: disable=g-explicit-bool-comparison - raise ValueError('use tpu_summary.tensor() instead: %r' % value) - x.name_scope = tf.get_default_graph().get_name_scope() - x.while_loop_reduce = while_loop_reduce - ctx.summary_tensors.append(x) - - -def tensor(name, value): - """Adds summary tensor. Similar to scalar() but allows other shapes.""" - ctx = TpuSummaryContext.current() - if ctx is None: - return - x = TpuSummaryScalar() - x.name = str(name) - x.value = tf.convert_to_tensor(value) - x.name_scope = tf.get_default_graph().get_name_scope() - x.while_loop_reduce = 'stack' - ctx.summary_tensors.append(x) - - -def merge_all(): - """Returns all summary tensors as a dict of {name: tensor}. - - Note that this is not the same return type as tf.summary.merge_all - which returns a serialized proto string. - - Outside of tpu_summary.context() returns {} - """ - ctx = TpuSummaryContext.current() - if ctx is None: - return {} - g = tf.get_default_graph() - ret = {} - for x in ctx.summary_tensors: - if x.value.graph is not g: - raise ValueError('Tensor %r %r is not an element of this graph.' % - (x.name, x.value)) - ret['%s/%s' % (x.name, x.name_scope)] = x.value - return ret - - -# pylint: disable=g-doc-return-or-yield -@contextlib.contextmanager -def context(rewrite_while_loop=False, - max_loop_vars=16, - loop_vars_dtype=tf.float32): - """TPU summary context. - - Args: - rewrite_while_loop: rewrite tf.while_loop to propagate summaries - max_loop_vars: number of loop vars added by rewrite_while_loop - loop_vars_dtype: dtype of loop vars added by rewrite_while_loop - """ - if not rewrite_while_loop: - with TpuSummaryContext(): - yield - else: - with TpuSummaryContext(), RewriteLoopContext(max_loop_vars, - loop_vars_dtype): - yield diff --git a/lingvo/core/tpu_summary_test.py b/lingvo/core/tpu_summary_test.py deleted file mode 100644 index f07a29ba..00000000 --- a/lingvo/core/tpu_summary_test.py +++ /dev/null @@ -1,224 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for tpu_summary.""" - -from lingvo import compat as tf - -from lingvo.core import test_utils -from lingvo.core import tpu_summary - - -class MockTransformer: - - def FProp(self, x, y): - for i in range(3): - with tf.name_scope('encoder%03d' % i): - x = tf.identity(x) - y = tf.identity(y) - x = x + 1 - tpu_summary.scalar('x_mean', tf.reduce_mean(x)) - tpu_summary.scalar('y_mean', tf.reduce_mean(y)) - for i in range(3): - with tf.name_scope('decoder%03d' % i): - x = tf.identity(x) - y = tf.identity(y) - y = y + 1 - tpu_summary.scalar('x_mean', tf.reduce_mean(x)) - tpu_summary.scalar('y_mean', tf.reduce_mean(y)) - return x, y - - def BeamSearch(self, x, y, decoder_reduce_sum=False): - for i in range(3): - with tf.name_scope('encoder%03d' % i): - x = tf.identity(x) - y = tf.identity(y) - x = x + 1 - tpu_summary.scalar('x_mean', tf.reduce_mean(x)) - tpu_summary.scalar('y_mean', tf.reduce_mean(y)) - - def DecoderStep(x, y): - for i in range(3): - with tf.name_scope('decoder%03d' % i): - x = tf.identity(x) - y = tf.identity(y) - y = y + 1 - if decoder_reduce_sum: - tpu_summary.scalar( - 'x_mean', tf.reduce_mean(x), while_loop_reduce='sum') - tpu_summary.scalar( - 'y_mean', tf.reduce_mean(y), while_loop_reduce='sum') - else: - tpu_summary.scalar('x_mean', tf.reduce_mean(x)) - tpu_summary.scalar('y_mean', tf.reduce_mean(y)) - return x, y - - def DecoderCond(x, y): - del x, y - return True - - (x, y) = tf.while_loop( - cond=DecoderCond, - body=DecoderStep, - loop_vars=(x, y), - maximum_iterations=10) - return x, y - - -class TpuSummaryTest(test_utils.TestCase): - - def testNoContext(self): - with self.session() as sess: - model = MockTransformer() - x = tf.constant(0, dtype=tf.float32) - y = tf.constant(0, dtype=tf.int64) - x, y = model.FProp(x, y) - x, y = sess.run((x, y)) - self.assertEqual((3.0, 3), (x, y)) - - def _CanonicalizeSummaryName(self, summaries): - ret = dict() - for k in summaries: - ret[k.replace('/while', '')] = summaries[k] - return ret - - def testMergeAll(self): - with self.session() as sess: - model = MockTransformer() - x = tf.constant(0, dtype=tf.float32) - y = tf.constant(0, dtype=tf.int64) - with tpu_summary.context(): - x, y = model.FProp(x, y) - summaries = tpu_summary.merge_all() - x, y, summaries = sess.run((x, y, summaries)) - self.assertEqual((3.0, 3), (x, y)) - expected = { - 'x_mean/decoder000': 3.0, - 'x_mean/decoder001': 3.0, - 'x_mean/decoder002': 3.0, - 'x_mean/encoder000': 1.0, - 'x_mean/encoder001': 2.0, - 'x_mean/encoder002': 3.0, - 'y_mean/decoder000': 1, - 'y_mean/decoder001': 2, - 'y_mean/decoder002': 3, - 'y_mean/encoder000': 0, - 'y_mean/encoder001': 0, - 'y_mean/encoder002': 0, - } - self.assertEqual(expected, self._CanonicalizeSummaryName(summaries)) - - def testWhileLoopNoMergeAll(self): - with self.session() as sess: - model = MockTransformer() - x = tf.constant(0, dtype=tf.float32) - y = tf.constant(0, dtype=tf.int64) - with tpu_summary.context(): - x, y = model.BeamSearch(x, y) - x, y = sess.run((x, y)) - self.assertEqual((3.0, 30), (x, y)) - - def testWhileLoopNoRewrite(self): - with self.session() as sess: - model = MockTransformer() - x = tf.constant(0, dtype=tf.float32) - y = tf.constant(0, dtype=tf.int64) - with tpu_summary.context(): - x, y = model.BeamSearch(x, y) - # ValueError: Tensor decoder000/Mean:0 is not an element of this graph. - with self.assertRaises(ValueError): - summaries = tpu_summary.merge_all() - x, y, summaries = sess.run((x, y, summaries)) - - def testWhileLoopRewrite(self): - with self.session() as sess: - model = MockTransformer() - x = tf.constant(0, dtype=tf.float32) - y = tf.constant(0, dtype=tf.int64) - with tpu_summary.context(rewrite_while_loop=True): - x, y = model.BeamSearch(x, y) - summaries = tpu_summary.merge_all() - tf.logging.info('summaries=%r', summaries) - x, y, summaries = sess.run((x, y, summaries)) - self.assertEqual((3.0, 30), (x, y)) - expected = { - 'x_mean/encoder000': 1.0, - 'x_mean/encoder001': 2.0, - 'x_mean/encoder002': 3.0, - 'y_mean/encoder000': 0, - 'y_mean/encoder001': 0, - 'y_mean/encoder002': 0, - 'x_mean/decoder000': 3.0, - 'x_mean/decoder001': 3.0, - 'x_mean/decoder002': 3.0, - 'y_mean/decoder000': 14.5, - 'y_mean/decoder001': 15.5, - 'y_mean/decoder002': 16.5, - } - self.assertEqual(expected, self._CanonicalizeSummaryName(summaries)) - - def testWhileLoopRewriteMaxVarsLimit(self): - with self.session() as sess: - model = MockTransformer() - x = tf.constant(0, dtype=tf.float32) - y = tf.constant(0, dtype=tf.int64) - with tpu_summary.context(rewrite_while_loop=True, max_loop_vars=2): - x, y = model.BeamSearch(x, y) - summaries = tpu_summary.merge_all() - tf.logging.info('summaries=%r', summaries) - x, y, summaries = sess.run((x, y, summaries)) - self.assertEqual((3.0, 30), (x, y)) - expected = { - 'x_mean/encoder000': 1.0, - 'x_mean/encoder001': 2.0, - 'x_mean/encoder002': 3.0, - 'y_mean/encoder000': 0, - 'y_mean/encoder001': 0, - 'y_mean/encoder002': 0, - 'x_mean/decoder000': 3.0, - 'y_mean/decoder000': 14.5, - } - self.assertEqual(expected, self._CanonicalizeSummaryName(summaries)) - - def testWhileLoopReduceSum(self): - with self.session() as sess: - model = MockTransformer() - x = tf.constant(0, dtype=tf.float32) - y = tf.constant(0, dtype=tf.int64) - with tpu_summary.context(rewrite_while_loop=True): - x, y = model.BeamSearch(x, y, decoder_reduce_sum=True) - summaries = tpu_summary.merge_all() - tf.logging.info('summaries=%r', summaries) - x, y, summaries = sess.run((x, y, summaries)) - self.assertEqual((3.0, 30), (x, y)) - expected = { - 'x_mean/encoder000': 1.0, - 'x_mean/encoder001': 2.0, - 'x_mean/encoder002': 3.0, - 'y_mean/encoder000': 0, - 'y_mean/encoder001': 0, - 'y_mean/encoder002': 0, - 'x_mean/decoder000': 30.0, - 'x_mean/decoder001': 30.0, - 'x_mean/decoder002': 30.0, - 'y_mean/decoder000': 145.0, - 'y_mean/decoder001': 155.0, - 'y_mean/decoder002': 165.0, - } - self.assertEqual(expected, self._CanonicalizeSummaryName(summaries)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/trainer_test_utils.py b/lingvo/core/trainer_test_utils.py deleted file mode 100644 index 1939bea4..00000000 --- a/lingvo/core/trainer_test_utils.py +++ /dev/null @@ -1,137 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Toy models and input generation tools for testing trainer code.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import py_utils -from lingvo.core import summary_utils - - -class CountingInputGenerator(base_input_generator.BaseInputGenerator): - """Produces deterministic inputs for IdentityRegressionModel. - - src_ids increment by 1, so a 2x2 batch would look like: - [[0, 1], [2, 3]] - and the next batch would be: - [[4, 5], [6, 7]] - - Targets are the sum of the src_ids: - [1, 5] - next batch: - [9, 13] - - Since `sum(src_ids) = target`, we expect that the regression model of - `target = sum(m * src_ids) + b` will learn `m = 1` and `b = 0`. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Delete('batch_size') - p.Define('batch_size', 2, 'batch size') - p.Define('shape', [2, 2], 'source shape.') - return p - - def __init__(self, params): - super().__init__(params) - self.shape = params.shape - - def _InputBatch(self): - length = tf.reduce_prod(self.shape) - counter = summary_utils.StatsCounter('CountingInputGenerator') - new_value = tf.cast(counter.IncBy(length), dtype=tf.int32) - length - new_value = tf.stop_gradient(new_value) - values = new_value + tf.range(length) - shaped_values = tf.reshape(tf.cast(values, dtype=tf.float32), self.shape) - targets = tf.reduce_sum(shaped_values, axis=0) - return py_utils.NestedMap(src_ids=shaped_values, tgt_ids=targets) - - -class IdentityRegressionTask(base_model.BaseTask): - """A simple regression task for testing.""" - - def __init__(self, params): - super().__init__(params) - self.global_steps = [] - self.metrics = [] - self.result_per_example_tensors = [] - - def _CreateLayerVariables(self): - super()._CreateLayerVariables() - self.CreateVariable( - 'm', - py_utils.WeightParams(shape=[], init=py_utils.WeightInit.Uniform())) - self.CreateVariable( - 'b', - py_utils.WeightParams(shape=[], init=py_utils.WeightInit.Uniform())) - - def ComputePredictions(self, theta, input_batch): - """sum(m * x) + b.""" - return tf.reduce_sum(theta.m * input_batch.src_ids, axis=1) + theta.b - - def ComputeLoss(self, theta, predicted, input_batch): - diff = predicted - input_batch.tgt_ids - per_example_loss = diff * diff - batch_dim = py_utils.GetShape(per_example_loss)[0] - - def replicate_var(name): - return tf.convert_to_tensor( - [self._private_vars[name]] * batch_dim, dtype=tf.float32) - - metrics = {'loss': (tf.reduce_sum(per_example_loss), batch_dim)} - per_example_tensors = { - 'input': input_batch.src_ids, - 'loss': per_example_loss, - 'diff': diff, - 'm': replicate_var('m'), - 'b': replicate_var('b'), - } - return metrics, per_example_tensors - - def FilterPerExampleTensors(self, per_example): - return per_example - - def ProcessFPropResults(self, sess, global_step, metrics, - per_example_tensors): - self.global_steps.append(global_step) - self.metrics.append(metrics) - self.result_per_example_tensors.append(per_example_tensors) - - -class IdentityRegressionModel(base_model.SingleTaskModel): - """Simple regression model for testing.""" - - def __init__(self, params): - super().__init__(params) - self.global_steps = [] - self.metrics = [] - self.result_per_example_tensors = [] - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'IdentityRegressionModel' - p.input = CountingInputGenerator.Params() - p.task = IdentityRegressionTask.Params() - return p - - def ProcessFPropResults(self, sess, global_step, metrics, - per_example_tensors): - self.global_steps.append(global_step) - self.metrics.append(metrics) - self.result_per_example_tensors.append(per_example_tensors) diff --git a/lingvo/core/tshape.py b/lingvo/core/tshape.py deleted file mode 100644 index cd34d1d0..00000000 --- a/lingvo/core/tshape.py +++ /dev/null @@ -1,117 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Symbolic representation of tensor shapes.""" - -import lingvo.compat as tf -import sympy - - -class Shape: - """Shape represents a tensor's symbolic shape.""" - - def __init__(self, dims): - """Constructs a shape whose i-th dim is dims[i]. - - Each dim can be one of the following types: - integer: represents the dimension is a known and fixed. - string: represents the dimension is an unknown and a sympy dummy symbol is - used to represent it. Also note that contents of strings only matter for - logging/printing. Even if the same string is given on multiple - dimensions, it doesn't mean that they are the same. - sympy expression: represents a dimension which possibly - depends on dimensions of other shapes. - - Args: - dims: A list of either integer, string or sympy.Symbol. - """ - self._shape = [] - for x in dims: - assert x is not None, str(dims) - if isinstance(x, str): - # NOTE: Dummy(x) creates a unique symbol. I.e., the value of x has no - # meaning except for printing, etc. - self._shape.append(sympy.Dummy(x, integer=True)) - else: - # Converts x to a sympy type. E.g., int to sympy.Integer. - self._shape.append(sympy.sympify(x)) - self._size = sympy.prod(self._shape) - - @property - def rank(self): - """Returns the rank of the tensor.""" - return len(self._shape) - - @property - def size(self): - """Returns the size (num of elements) of the tensor.""" - return self._size - - def num_elements(self): # pylint: disable=invalid-name - """Returns the size (num of elements) of the tensor.""" - return self.size - - def __getitem__(self, key): - """Returns one dimension or a shape from a slice of dimensions.""" - if isinstance(key, int): - return self._shape[key] - elif isinstance(key, slice): - return Shape(self._shape[key]) - else: - raise TypeError("Invalid argument type.") - - def __add__(self, other): - """Concatenates two shapes into one.""" - # pylint: disable=protected-access - if isinstance(other, Shape): - return Shape(self._shape + other._shape) - elif isinstance(other, list): - return Shape(self._shape + Shape(other)._shape) - else: - raise NotImplementedError - - def __radd__(self, other): - """Concatenates two shapes into one.""" - # pylint: disable=protected-access - if isinstance(other, Shape): - return Shape(other._shape + self._shape) - elif isinstance(other, list): - return Shape(Shape(other)._shape + self._shape) - else: - raise NotImplementedError - - def __str__(self): - return str(self._shape) - - def Subs(self, bindings): - """Substitute symbols with new values. - - Args: - bindings: key/value items correspond to old/new pairs for substitution. - - Returns: - The Shape with symbols substituted according to bindings. - """ - return Shape([x.subs(bindings) for x in self._shape]) - - def ToTensorShape(self): - """Converts to a possibly partially specified tf.TensorShape.""" - dims = [] - for d in self._shape: - if d.is_number and d.is_integer: - dims.append(int(d)) - else: - dims.append(None) - return tf.TensorShape(dims) diff --git a/lingvo/core/tshape_test.py b/lingvo/core/tshape_test.py deleted file mode 100644 index 5c2e4020..00000000 --- a/lingvo/core/tshape_test.py +++ /dev/null @@ -1,49 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.core.tshape.""" - -import lingvo.compat as tf -from lingvo.core import test_utils -from lingvo.core.tshape import Shape - - -class TshapeTest(test_utils.TestCase): - - def testShape(self): - w1 = Shape([3, 3, 'd0', 'd1']) - w2 = Shape([5, 5, w1[2], 'd2']) - self.assertEqual(w2.ToTensorShape().as_list(), [5, 5, None, None]) - s2 = w2.Subs({w1[2]: 8, w1[3]: 16, w2[3]: 32}) - self.assertEqual(s2.ToTensorShape().as_list(), [5, 5, 8, 32]) - - # __getitem__ - inner = w1[-2:] - self.assertIsInstance(inner, Shape) - - # unpack - d0, d1 = w1[-2:] - self.assertEqual((d0 * d1).subs({d0: 3, d1: 5}), 15) - - # __add__ - self.assertEqual(str(w1 + w2), '[3, 3, _d0, _d1, 5, 5, _d0, _d2]') - - # __radd_ - self.assertEqual(str([7] + w1), '[7, 3, 3, _d0, _d1]') - self.assertEqual(str(w1[-2:] + w2[-1:]), '[_d0, _d1, _d2]') - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/wpm_encoder.py b/lingvo/core/wpm_encoder.py deleted file mode 100644 index 131b4b60..00000000 --- a/lingvo/core/wpm_encoder.py +++ /dev/null @@ -1,197 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Encode using wordpiece models. - -Implements the segmentation algorithm described in the last paragraph of -p. 5150, in the following publication: - -M. Schuster and K. Nakajima, "Japanese and Korean voice -search," 2012 IEEE International Conference on Acoustics, -Speech and Signal Processing, 2012 - -https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/37842.pdf -""" -import lingvo.compat as tf -from lingvo.core import ops -from lingvo.core import py_utils -import six - -# Must be a large ID. -NO_TOKEN = 1 << 31 - 1 -NO_TOKEN_STRING = '' - -SENTENCE_START_STRING = '' -SENTENCE_END_STRING = '' - -BOW_STR = '▁' - - -class WpmEncoder: - """WPM encoder.""" - - def __init__(self, wpm_filepath, merge_prob=1.): - """Create a WPM encoder. - - Args: - wpm_filepath: a path to the file containing the vocabulary. - merge_prob: the probability of merging tokens while encoding. - """ - # Load vocabulary file. - lines = py_utils.ReadFileLines(wpm_filepath) - - self._pieces = [] - for line in lines: - if isinstance(line, bytes): - line = six.ensure_text(line, 'utf-8') - piece = line.strip().split('\t')[0] - self._pieces.append(piece) - self._merge_prob = merge_prob - - def _TokenToString(self, token): - return ops.vocab_id_to_token(token, vocab=self._pieces) - - def _StringToToken(self, tokstr): - return tf.where( - ops.token_in_vocab(tokstr, vocab=self._pieces), - ops.vocab_token_to_id(tokstr, vocab=self._pieces), - tf.broadcast_to(NO_TOKEN, tf.shape(tokstr))) - - def _MergeTokens(self, tokens): - return self._StringToToken( - self._TokenToString(tokens[0]) + self._TokenToString(tokens[1])) - - def _EncodeToIds(self, word): - # Below: - # * a token is a wordpiece ID. - # * the tokens array will be merged in-place. - # * the candidates array is an array of size len(tokens) - 1. - # It contains the token for the merged wordpiece, if it exists, - # -1 otherwise. For instance, candidate[3] = id(token[3] + token[4]). - # First, split into basic UTF-8 characters (letters). - chars = tf.strings.unicode_split(word, 'UTF-8') - tokens = self._StringToToken(chars) - tokens = tf.where( - tf.equal(tokens, NO_TOKEN), - # Unseen character. - tf.broadcast_to(self.unk_id, tf.shape(tokens)), - tokens) - # Create initial candidate list. - candidates = tf.map_fn( - self._MergeTokens, (tokens[:-1], tokens[1:]), dtype=tokens.dtype) - - def _ShouldMerge(unused_tokens, candidates): - """Merge until not possible, or we abort early according to merge_prob.""" - return tf.math.logical_and( - tf.reduce_any(tf.not_equal(candidates, NO_TOKEN)), - tf.random.uniform([]) < self._merge_prob) - - def _MergeOneToken(tokens, i): - return tf.expand_dims( - self._MergeTokens((tokens[i], tokens[i + 1])), axis=-1) - - def _MergeCandidates(tokens, candidates): - """Merge in the reverse binary tree.""" - best_id = tf.argmin(candidates, output_type=tf.int32) - # Perform the merge at position best_id. - tokens = tf.concat( - [tokens[:best_id], [candidates[best_id]], tokens[best_id + 2:]], - axis=0) - # Recompute the merge candidates. - # Only the neighbors of best_id need to be recomputed. - empty = tf.zeros([0], dtype=candidates.dtype) - - def _MergeLeft(): - return tf.concat( - [candidates[:best_id - 1], - _MergeOneToken(tokens, best_id - 1)], - axis=0) - - left_candidates = tf.cond(tf.equal(best_id, 0), lambda: empty, _MergeLeft) - - def _MergeRight(): - return tf.concat( - [_MergeOneToken(tokens, best_id), candidates[best_id + 2:]], axis=0) - - right_candidates = tf.cond( - tf.greater_equal(best_id, - tf.size(tokens) - 1), lambda: empty, _MergeRight) - - candidates = tf.concat([left_candidates, right_candidates], axis=0) - return tokens, candidates - - return tf.while_loop( - _ShouldMerge, - _MergeCandidates, (tokens, candidates), - parallel_iterations=1, - back_prop=False)[0] - - def Encode(self, text): - """Converts string `text` to integer ids and the encoded string. - - Encoding includes prefixing the beginning-of-word token to each word. - - Returns: - (ids, tokens) where ids is the encoded integer ids and tokens is the - encoded string. - """ - words = tf.sparse.to_dense(tf.strings.split([text]), default_value='')[0] - num_words = tf.size(words) - ids_ta = tf.TensorArray(tf.int32, 0, dynamic_size=True) - - def _WordsToIds(i, words, ids_ta): - encoded_ids = self._EncodeToIds(BOW_STR + words[i]) - ids_ta = ids_ta.scatter( - tf.range(ids_ta.size(), - ids_ta.size() + tf.size(encoded_ids)), encoded_ids) - return i + 1, words, ids_ta - - _, _, ids_ta = tf.while_loop( - lambda i, *_: i < num_words, - _WordsToIds, - loop_vars=(tf.constant(0, tf.int32), words, ids_ta), - parallel_iterations=30, - back_prop=False) - - ids = ids_ta.stack() - return ids, self._TokenToString(ids) - - def Decode(self, ids): - txt = tf.strings.reduce_join(self._TokenToString(ids)) - txt = tf.strings.regex_replace(txt, BOW_STR, ' ') - # Note that this strips spaces from the end of the input as well. - # We assume no inputs rely on the existence of trailing whitespace. - txt = tf.strings.strip(txt) - return txt - - @property - def sentence_start_id(self): - return self._pieces.index(SENTENCE_START_STRING) - - @property - def sentence_start_string(self): - return SENTENCE_START_STRING - - @property - def sentence_end_id(self): - return self._pieces.index(SENTENCE_END_STRING) - - @property - def sentence_end_string(self): - return SENTENCE_END_STRING - - @property - def unk_id(self): - return self._pieces.index(NO_TOKEN_STRING) diff --git a/lingvo/core/wpm_encoder_test.py b/lingvo/core/wpm_encoder_test.py deleted file mode 100644 index 5a57d56b..00000000 --- a/lingvo/core/wpm_encoder_test.py +++ /dev/null @@ -1,104 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for wpm_encoder.""" - -import os -import lingvo.compat as tf -from lingvo.core import test_utils -from lingvo.core import wpm_encoder - - -class WpmEncoderTest(test_utils.TestCase): - - def _CreateVocab(self): - outpath = os.path.join(tf.test.get_temp_dir(), 'wpm.voc') - with tf.io.gfile.GFile(outpath, 'w') as f: - contents = [ - '', - '', - '', - 't', - 'i', - 'o', - 'f', - 'r', - 'D', - 'it', - 'or', - 'for', - 'itt', - 'to', - 'i-', - 'tt', - 'f.', - 'o-', - 'o.', - 'fo', - 'ø', # \xC3\xB8 - 'ö', # \xC3\xB6 - '\\', - '▁', - ] - f.write('\n'.join(contents)) - return outpath - - def setUp(self): - voc = self._CreateVocab() - self._enc = wpm_encoder.WpmEncoder(voc) - - def testDitto(self): - with tf.Session(): - ids, strs = self._enc.Encode('Ditto') - self.assertEqual(u'▁ D itt o'.encode('utf-8'), - tf.strings.reduce_join(strs, separator=' ').eval()) - self.assertEqual(b'Ditto', self._enc.Decode(ids).eval()) - ids, strs = self._enc.Encode('Ditto Ditto') - self.assertEqual(u'▁ D itt o ▁ D itt o'.encode('utf-8'), - tf.strings.reduce_join(strs, separator=' ').eval()) - self.assertEqual(b'Ditto Ditto', self._enc.Decode(ids).eval()) - - def testMergeProb(self): - voc = self._CreateVocab() - enc = wpm_encoder.WpmEncoder(voc, merge_prob=0.) - with tf.Session(): - ids, strs = enc.Encode('Ditto') - self.assertEqual(u'▁ D i t t o'.encode('utf-8'), - tf.strings.reduce_join(strs, separator=' ').eval()) - self.assertEqual(b'Ditto', self._enc.Decode(ids).eval()) - - def testEmpty(self): - with tf.Session(): - ids, strs = self._enc.Encode('') - self.assertEqual(b'', tf.strings.reduce_join(strs, separator=' ').eval()) - self.assertEqual(b'', self._enc.Decode(ids).eval()) - - def testWithBackslash(self): - with tf.Session(): - ids, strs = self._enc.Encode('\\') - self.assertEqual(u'▁ \\'.encode('utf-8'), - tf.strings.reduce_join(strs, separator=' ').eval()) - self.assertEqual(b'\\', self._enc.Decode(ids).eval()) - - def testWithUnicode(self): - with tf.Session(): - ids, strs = self._enc.Encode('føö') - self.assertEqual(u'▁ f ø ö'.encode('utf-8'), - tf.strings.reduce_join(strs, separator=' ').eval()) - self.assertEqual(u'føö'.encode('utf-8'), self._enc.Decode(ids).eval()) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/core/wpm_tokenizer_test.py b/lingvo/core/wpm_tokenizer_test.py deleted file mode 100644 index 69bfc14d..00000000 --- a/lingvo/core/wpm_tokenizer_test.py +++ /dev/null @@ -1,66 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for WPM tokenizer (which is not in ops test).""" - -import lingvo.compat as tf -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.core import tokenizers -import numpy as np - - -class WpmTokenizerTest(test_utils.TestCase): - - def testStringsTokenIds(self): - p = tokenizers.WpmTokenizer.Params() - p.vocab_filepath = test_helper.test_src_dir_path('tasks/mt/wpm-ende.voc') - p.vocab_size = 32000 - wpm_tokenizer = p.Instantiate() - with self.session(use_gpu=False): - token_ids, target_ids, paddings = self.evaluate( - wpm_tokenizer.StringsToIds( - tf.constant(['would that it were so simple', 'this is it', ''], - dtype=tf.string), 6, True)) - self.assertAllEqual( - token_ids, [[1, 926, 601, 560, 1273, 721], [1, 647, 470, 560, 2, 2], - [1, 2, 2, 2, 2, 2]]) - self.assertAllEqual(target_ids, - [[926, 601, 560, 1273, 721, 5490], - [647, 470, 560, 2, 2, 2], [2, 2, 2, 2, 2, 2]]) - self.assertAllEqual(paddings, - [[0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 1., 1.], - [0., 1., 1., 1., 1., 1.]]) - - def testIdsToStrings(self): - p = tokenizers.WpmTokenizer.Params() - p.vocab_filepath = test_helper.test_src_dir_path('tasks/mt/wpm-ende.voc') - p.vocab_size = 32000 - wpm_tokenizer = p.Instantiate() - with self.session(use_gpu=False): - ref = tf.constant([ - 'would that it were so simple', - 'this is it', - '', - ]) - _, target_ids, paddings = self.evaluate( - wpm_tokenizer.StringsToIds(ref, 100, True)) - lens = np.argmax(paddings > 0.0, axis=1) - 1 - found = self.evaluate(wpm_tokenizer.IdsToStrings(target_ids, lens)) - self.assertAllEqual(ref, found) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/datasets.py b/lingvo/datasets.py deleted file mode 100644 index f551ab33..00000000 --- a/lingvo/datasets.py +++ /dev/null @@ -1,28 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Utilities for dataset information.""" -import inspect - - -def GetDatasets(cls): - """Returns the list of dataset functions (e.g. Train, Dev, ...).""" - datasets = [] - for name, _ in inspect.getmembers( - cls, lambda x: inspect.isfunction(x) or inspect.ismethod(x)): - if name not in ['GetDatasetParams', 'Model', 'Task', 'ProgramSchedule' - ] and not name.startswith('_'): - datasets += [name] - return datasets diff --git a/lingvo/executor.py b/lingvo/executor.py deleted file mode 100644 index 6f7000e3..00000000 --- a/lingvo/executor.py +++ /dev/null @@ -1,347 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""An experimental new unified TPU executor.""" - -import os - -from lingvo import compat as tf -from lingvo.core import base_model -from lingvo.core import checkpointer -from lingvo.core import cluster_factory -from lingvo.core import ml_perf_log as mlp_log -from lingvo.core import multitask_model -from lingvo.core import py_utils -from lingvo.core import task_scheduler - -from lingvo import base_runner -from tensorflow.python.tpu import device_assignment as device_assignment_lib # pylint: disable=g-direct-tensorflow-import - -tf.flags.DEFINE_bool( - 'cluster_placer_in_executor', False, - 'If True, cluster.GetPlacer() is used in Executor. ' + - 'When running on TPU model weights can be distributed ' + - 'across TPU hosts, for outrageously large models this ' + - 'enables sharded checkpointing and reduces host memory ' + - 'requirements, see _LeastLoadedPlacer in cluster.py.') - -tf.flags.DEFINE_bool( - 'disable_meta_optimizer_in_executor', False, - 'Disabling the grappler meta_optimizer improves start-up time.') - -tf.flags.DEFINE_bool( - 'lingvo_executor_skip_saving_upon_stop', False, - 'When we are just running decoding or inference, saving is not necessary,' - 'since no training has occurred.' -) - -FLAGS = tf.flags.FLAGS - - -def GetExecutorParams(model_name, cluster_params, model_registry): - """Get the params needed to instantiate the Executor. - - Args: - model_name: A model name regsitered in the ModelRegistry. - cluster_params: A cluster hyperparams object. - model_registry: A ModelRegistry object. - - Returns: - A tuple (dict, Params): - - - ps_params_dict: High-level task name -> ProgramScheduleParams - - train_cfg: A SingleTaskModelParams or MultiTaskModelParams. - """ - - ps_params_dict = {} - with cluster_factory.Cluster(cluster_params): - ps_cfg = model_registry.GetProgramSchedule(model_name) - train_cfg = model_registry.GetParams(model_name, 'Train') - train_cfg.cluster = cluster_params - - if issubclass(train_cfg.cls, base_model.MultiTaskModel): - multi_task_train_cfg = train_cfg - # Create MultiTaskSubModel params from a MultiTaskModelParams. - for k, _ in multi_task_train_cfg.task_params.IterParams(): - train_task_params = base_model.MultiTaskSubModel.Params() - train_task_params.name = k + '_executor_train_task' - train_task_params.task_name = k - train_task_params.cluster = multi_task_train_cfg.cluster - train_task_params.input = multi_task_train_cfg.input.Get(k).Copy() - train_task_params.train = multi_task_train_cfg.task_params.Get(k).train - if k not in ps_cfg.program_schedule_dict: - tf.logging.fatal( - 'Could not find %s in ps_cfg.program_schedule_dict: %s', k, - ps_cfg) - program_schedule_params = ps_cfg.program_schedule_dict[k] - - program_schedule_params.task_dict = {'Train': train_task_params} - - for eval_dataset_name in program_schedule_params.dataset_names: - multi_task_eval_cfg = model_registry.GetParams( - model_name, eval_dataset_name) - eval_task_params = base_model.MultiTaskSubModel.Params() - eval_task_params.name = ( - k + '_' + eval_dataset_name + '_executor_eval_task') - eval_task_params.task_name = k - eval_task_params.cluster = multi_task_eval_cfg.cluster - eval_task_params.input = multi_task_eval_cfg.input.Get(k).Copy() - program_schedule_params.task_dict[ - eval_dataset_name] = eval_task_params - ps_params_dict[k] = program_schedule_params - else: - program_schedule_params = ps_cfg - # So, what if I just remove the Train dictionary? - program_schedule_params.task_dict = {'Train': train_cfg} - for eval_dataset_name in program_schedule_params.dataset_names: - task_eval_params = model_registry.GetParams(model_name, - eval_dataset_name) - task_eval_params.cluster = train_cfg.cluster - program_schedule_params.task_dict[eval_dataset_name] = task_eval_params - ps_params_dict[''] = program_schedule_params - - return ps_params_dict, train_cfg - - -class ExecutorTpu(base_runner.BaseRunner): - """An experimental runner that does arbitrary multi-program execution on TPU. - - Overview of operation: - - - During construction, all programs construct their sub-graphs, in a sense - creating a mega-graph. - - A sequence of programs is then executed in-whole associated with that task. - eg: [train x 1000 steps, checkpoint, eval 4 steps, decode 2 steps] - - In this manner, programs and higher-level tasks cooperatively time-slice - share the TPU. - """ - - def __init__(self, train_cfg, ps_params_dict, model_task_name, logdir, - tf_master, **kwargs): - """Construct an ExecutorTpu BaseRunner. - - Args: - train_cfg: SingleTaskModelParams or MultiTaskModelParams - ps_params_dict: A dict of top-level task name -> ProgramSchedule params, - if train_cfg is a SingleTaskModelParams, we expect only one entry. - model_task_name: An override for multi-task models, currently unused. - logdir: String path to the log directory to output to. - tf_master: String path to the master job, e.g. 'local'. - **kwargs: keyword args to pass through to BaseRunner. - """ - super().__init__(train_cfg, model_task_name, logdir, tf_master, **kwargs) - - self._cluster_def = self._cluster.worker_cluster_def - - # There is a single Executor task - assert self._cluster.num_replicas == 1 - data_parallelism = self._cluster.num_splits_per_client - - assert data_parallelism - num_devices_per_split = self._cluster.num_devices_per_split - tf.logging.info('data_parallelism: %d, num_devices_per_split: %d', - data_parallelism, num_devices_per_split) - - self.task_scheduler = None - self._checkpoint_dir = os.path.join(logdir, 'train') - - self._variable_renaming_rules = [] - - self._ml_perf = None - - # If this is a multi-task model, grab the params for the TaskScheduler. - if issubclass(train_cfg.cls, base_model.SingleTaskModel): - tf.logging.info('single_task_model') - assert len(ps_params_dict) == 1 - self._model_task_name = list(ps_params_dict.keys())[0] - self._single_task_mode = True - elif issubclass(train_cfg.cls, base_model.MultiTaskModel): - tf.logging.info('multi_task_model') - - if issubclass(train_cfg.cls, multitask_model.RegExSharedVariableModel): - self._variable_renaming_rules = train_cfg.variable_renaming_rules - - if train_cfg.task_schedule is None: - task_schedule_params = task_scheduler.ConstantScheduler.Params() - task_schedule_params.task_probs = sorted( - list(train_cfg.task_probs.IterParams())) - else: - task_schedule_params = train_cfg.task_schedule - self.task_scheduler = task_schedule_params.Instantiate() - self._single_task_mode = False - else: - tf.logging.fatal( - 'Model %s is not a sub-class of SingleTaskModel or MultiTaskModel', - train_cfg.cls) - - tf.logging.info('train_cfg.cls: %s', train_cfg.cls) - - self._WriteToLog(train_cfg.ToText(), self._checkpoint_dir, - 'trainer_params.txt') - if self._ml_perf is not None: - self._ml_perf_log = True - mlp_log.mlperf_print(key='benchmark', value=self._ml_perf.benchmark_name) - else: - self._ml_perf_log = False - - # BaseRunner legacy - self.enqueue_ops = None - - @py_utils.RetryOnTransientTfError() - def _WaitTillInit(): - """Wait until the model is ready.""" - try: - with self._graph.as_default(), self._GetSession( - cluster_def=self._cluster_def, - disable_meta_optimizer=FLAGS.disable_meta_optimizer_in_executor - ) as sess: - topology = sess.run( - tf.tpu.initialize_system(embedding_config=None, job=None)) - device_assignment = device_assignment_lib.device_assignment( - topology, - computation_shape=py_utils.ComputationShape( - num_devices_per_split, topology), - num_replicas=data_parallelism) - py_utils.SetTpuDeviceAssignment(device_assignment) - tf.logging.info('device_assignment.core_assignment: %s', - str(device_assignment.core_assignment)) - tf.logging.info( - 'device_assignment.topology.device_coordinates: %s', - str(device_assignment.topology.device_coordinates)) - except py_utils.transient_tf_errors as e: - tf.logging.info('TPU initialization failed: %s', e) - raise - - if self._ml_perf_log: - mlp_log.mlperf_print(key='init_start', value=None) - _WaitTillInit() - - train_cfg = self.params - shared_model = self._MaybeConstructSharedModel(train_cfg) - - self._program_schedule_dict = {} - self._programs = [] - - for task_string, program_schedule_params in ps_params_dict.items(): - program_schedule_params.logdir = logdir - program_schedule_params.num_splits_per_client = data_parallelism - program_schedule_params.task_name = task_string - # If the model was created above, we'll inject it here as a shared_model. - ps = program_schedule_params.Instantiate(shared_model=shared_model) - self._program_schedule_dict[task_string] = ps - tf.logging.info('program_schedule_params: %s', - program_schedule_params.ToText()) - self._programs += ps.Programs() - if program_schedule_params.ml_perf.benchmark_name is not None: - self._ml_perf = program_schedule_params.ml_perf - - tf.logging.info('num_programs: %d', len(self._programs)) - - with self._graph.as_default(), tf.container(self._container_id): - with self._cluster, tf.device( - self._cluster.job_spec.name if not FLAGS.cluster_placer_in_executor - else self._cluster.GetPlacer()): - with py_utils.VariableRenameScope(self._variable_renaming_rules): - _ = py_utils.GetOrCreateGlobalStepVar() - for program in self._programs: - program.BuildTpuSubgraph() - py_utils.ClearTpuSummaryTensors() - for program in self._programs: - program.SetStatusMessageFn(self._SetStatusMessage) - program.CreateCheckpointer() - self._initialize_tables = tf.tables_initializer() - self._initialize_local_vars = tf.local_variables_initializer() - - self.save_only_checkpointer = checkpointer.Checkpointer( - self._checkpoint_dir, - model=None, - train_params=train_cfg.train, - save_only=True) - - def _MaybeConstructSharedModel(self, train_cfg): - """Construct a single shared copy of the model if this is a MultiTaskModel. - - For MultiTaskModels, we create a MultiTaskSubModel for each task, but - construct the model only once. - - Args: - train_cfg: The params for a SingleTaskModel or MultiTaskModel. - - Returns: - A MultiTaskModel, if train_cfg is a MultiTaskModel params object. - """ - if not issubclass(train_cfg.cls, base_model.MultiTaskModel): - return None - - with self._graph.as_default(), tf.container(self._container_id): - with self._cluster, tf.device( - self._cluster.job_spec.name if not FLAGS.cluster_placer_in_executor - else self._cluster.GetPlacer()): - with py_utils.VariableRenameScope(self._variable_renaming_rules): - _ = py_utils.GetOrCreateGlobalStepVar() - shared_model = train_cfg.Instantiate() - shared_model.InstantiateVariables() - - return shared_model - - def Start(self): - # Run training. - self._RunLoop('executor_tpu', self._Loop) - - def _Loop(self): - with tf.container(self._container_id), self._GetSession( - cluster_def=self._cluster_def, - disable_meta_optimizer=FLAGS.disable_meta_optimizer_in_executor - ) as sess: - # Initialize the variables first, if needed. - for program in self._programs: - program.RestoreIfNeeded(sess) - program.Compile(sess) - sess.run(self._initialize_tables) - sess.run(self._initialize_local_vars) - - # How to stop? - while True: - global_step = sess.run(py_utils.GetGlobalStep()) - if self._ShouldStop(sess, global_step): - tf.logging.info('Training finished.') - if not self._ml_perf_log and not FLAGS.lingvo_executor_skip_saving_upon_stop: - self.save_only_checkpointer.Save(sess, global_step) - return - - # If a task is explicitly selected, only run the programs associated - # with that task. - if self._single_task_mode or self._model_task_name: - tf.logging.info('Single task mode: %s', self._model_task_name) - program_schedule = self._program_schedule_dict[self._model_task_name] - else: - # Otherwise, sample a task. - model_task = self.task_scheduler.Sample(global_step) - tf.logging.info('Sampled %s', model_task) - program_schedule = self._program_schedule_dict[model_task] - - done = program_schedule.Run(sess) - if done: - tf.logging.info('Program schedule told us to stop.') - return - - # global_step local variable above is a result of sess.run, not a - # tf variable, so when we do save_only_checkpointer.Save(...) here - # py_utils.GetGlobalStep() is ahead of it by - # (train_executions_per_eval * train_steps_per_loop) - # steps ahead already, due to program_schedule.Run(sess). - # - if not self._ml_perf_log and not FLAGS.lingvo_executor_skip_saving_upon_stop: - self.save_only_checkpointer.MaybeSave(sess, py_utils.GetGlobalStep()) diff --git a/lingvo/ipython_kernel.py b/lingvo/ipython_kernel.py deleted file mode 100644 index 28e72913..00000000 --- a/lingvo/ipython_kernel.py +++ /dev/null @@ -1,44 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Starts an IPython kernel with Lingvo deps. - -jupyter_http_over_ws must be installed and activated. See -https://research.google.com/colaboratory/local-runtimes.html for more details. - -To use: - bazel run -c opt //lingvo:ipython_kernel -""" - -from absl import app -from IPython.html.notebookapp import NotebookApp - - -def main(_): - notebookapp = NotebookApp.instance() - notebookapp.open_browser = False - notebookapp.ip = "0.0.0.0" - notebookapp.port = 8888 - notebookapp.port_retries = 0 - notebookapp.allow_origin_pat = "https://colab\\.[^.]+\\.google.com" - notebookapp.allow_root = True - notebookapp.token = "" - notebookapp.disable_check_xsrf = True - notebookapp.initialize() - notebookapp.start() - - -if __name__ == "__main__": - app.run(main) diff --git a/lingvo/lingvo.bzl b/lingvo/lingvo.bzl deleted file mode 100644 index 7c9d0ee1..00000000 --- a/lingvo/lingvo.bzl +++ /dev/null @@ -1,173 +0,0 @@ -"""Implements custom rules for Lingvo.""" - -load("@subpar//:subpar.bzl", "par_binary") - -def tf_copts(): - # TODO(drpng): autoconf this. - return ["-D_GLIBCXX_USE_CXX11_ABI=0", "-Wno-sign-compare", "-mavx"] + select({ - "//lingvo:cuda": ["-DGOOGLE_CUDA=1"], - "//conditions:default": [], - }) - -def lingvo_cc_library(name, srcs = [], hdrs = [], deps = [], testonly = 0): - native.cc_library( - name = name, - copts = tf_copts(), - srcs = srcs, - hdrs = hdrs, - deps = [ - "@tensorflow_includes//:includes", - ] + deps, - testonly = testonly, - ) - -def lingvo_cc_test_library(name, srcs = [], hdrs = [], deps = []): - lingvo_cc_library( - name = name, - srcs = srcs, - hdrs = hdrs, - deps = deps + ["@com_google_googletest//:gtest"], - testonly = 1, - ) - -def lingvo_cc_binary(name, srcs = [], deps = []): - native.cc_binary( - name = name, - copts = tf_copts(), - srcs = srcs, - deps = [ - "@tensorflow_includes//:includes", - "@tensorflow_solib//:framework_lib", - ] + deps, - ) - -def lingvo_cc_test(name, srcs, deps = [], **kwargs): - native.cc_test( - name = name, - copts = tf_copts(), - srcs = srcs, - deps = [ - "@tensorflow_includes//:includes", - "@tensorflow_solib//:framework_lib", - "@com_google_googletest//:gtest_main", - ] + deps, - **kwargs - ) - -def lingvo_py_binary(*args, **kwargs): - # Extract all files to a temporary directory is required to allow access to - # the shared object containing lingvo ops. - kwargs["zip_safe"] = False - par_binary(*args, **kwargs) - -def custom_kernel_library(name, op_def_lib, srcs, hdrs = [], deps = []): - native.cc_library( - name = name, - srcs = srcs, - hdrs = hdrs, - copts = tf_copts(), - deps = [ - "@tensorflow_includes//:includes", - ] + deps + op_def_lib, - alwayslink = 1, - ) - -def gen_op_cclib(name, srcs, deps = []): - # TODO(drpng): gpu. - native.cc_library( - name = name, - srcs = srcs, - deps = [ - "@tensorflow_includes//:includes", - "@tensorflow_solib//:framework_lib", - ] + deps, - alwayslink = 1, - copts = tf_copts(), - ) - -def gen_op_pylib(name, cc_lib_name, srcs, kernel_deps, py_deps = [], **kwargs): - native.cc_binary( - name = cc_lib_name + ".so", - deps = [cc_lib_name] + kernel_deps, - linkshared = 1, - copts = tf_copts(), - **kwargs - ) - - native.py_library( - name = name, - srcs = srcs, - srcs_version = "PY3", - data = [cc_lib_name + ".so"], - deps = py_deps, - **kwargs - ) - -def lingvo_cuda_py_test(name, tags = [], deps = [], **kwargs): - native.py_test( - name = name, - tags = tags + ["requires-gpu"], - deps = deps, - **kwargs - ) - -def _proto_gen_cc_src(name, basename): - native.genrule( - name = name, - srcs = [basename + ".proto"], - outs = [basename + ".pb.cc", basename + ".pb.h"], - tools = [ - "@protobuf_protoc//:protoc_bin", - "//lingvo:tf_dot_protos", - ], - # TODO(drpng): only unpack if tf_proto dependency is requested. - cmd = """ - mkdir -p $(@D)/tf_proto.$$$$; - tar -C $(@D)/tf_proto.$$$$ -xf $(location //lingvo:tf_dot_protos); - $(location @protobuf_protoc//:protoc_bin) --proto_path=$(@D)/tf_proto.$$$$ --proto_path=. --cpp_out=$(GENDIR) $(<); - rm -rf $(@D)/tf_proto.$$$$ - """, - ) - -def _proto_gen_py_src(name, basename): - native.genrule( - name = name, - srcs = [basename + ".proto"], - outs = [basename + "_pb2.py"], - tools = [ - "@protobuf_protoc//:protoc_bin", - "//lingvo:tf_dot_protos", - ], - # TODO(drpng): only unpack if tf_proto dependency is requested. - cmd = """ - mkdir -p $(@D)/tf_proto.$$$$; - tar -C $(@D)/tf_proto.$$$$ -xf $(location //lingvo:tf_dot_protos); - $(location @protobuf_protoc//:protoc_bin) --proto_path=$(@D)/tf_proto.$$$$ --proto_path=. --python_out=$(GENDIR) $(<); - rm -rf $(@D)/tf_proto.$$$$ - """, - ) - -def lingvo_proto_cc(name, src, deps = []): - # TODO(drpng): only works with proto with no deps within lingvo. - _unused = [deps] - basename = src.replace(".proto", "") - _proto_gen_cc_src(name + "_gencc", basename) - lingvo_cc_library( - name = name, - srcs = [basename + ".pb.cc"], - hdrs = [basename + ".pb.h"], - ) - lingvo_cc_library( - name = "%s_cc" % name, - deps = [":%s" % name], - ) - -def lingvo_proto_py(name, src, deps = []): - # TODO(drpng): only works with proto with no deps within lingvo. - _unused = [deps] - basename = src.replace(".proto", "") - _proto_gen_py_src(name + "_genpy", basename) - native.py_library( - name = name, - srcs = [basename + "_pb2.py"], - ) diff --git a/lingvo/model_import_test.py b/lingvo/model_import_test.py deleted file mode 100644 index f9948002..00000000 --- a/lingvo/model_import_test.py +++ /dev/null @@ -1,23 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Test model imports.""" - -from lingvo import model_imports -import lingvo.compat as tf - -if __name__ == '__main__': - model_imports.ImportAllParams() - tf.test.main() diff --git a/lingvo/model_imports.py b/lingvo/model_imports.py deleted file mode 100644 index 58ad0439..00000000 --- a/lingvo/model_imports.py +++ /dev/null @@ -1,104 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Global import for model hyper-parameters. - -Using this module any ModelParams can be accessed via GetParams. -""" - -import importlib -import re -import sys - - -def _Import(name): - """Imports the python module of the given name.""" - print('model_imports.py: Importing %s' % name, file=sys.stderr) - try: - importlib.import_module(name) - return True - except ModuleNotFoundError as e: - missing_module = re.match("No module named '(.*?)'", e.msg).group(1) - if not name.startswith(missing_module): - raise - return False - - -def _InsertParams(module): - """Try inserting 'params' everywhere in the module.""" - left = [] - right = module.split('.') - while right: - left.append(right.pop(0)) - yield '.'.join(left + ['params'] + right) - - -_TASK_ROOT = 'lingvo.tasks' - - -# LINT.IfChange(task_dirs) -_TASK_DIRS = ( - 'asr', - 'car', - 'image', - 'lm', - 'mt', - 'punctuator', -) -# LINT.ThenChange(tasks/BUILD:task_dirs) - - -def ImportAllParams(task_root=_TASK_ROOT, - task_dirs=_TASK_DIRS, - require_success=False): - """Import all ModelParams to add to the global registry.""" - success = False - for task in task_dirs: - # By our code repository convention, there is a params.py under the task's - # params directory. params.py imports _all_ modules that may registers a - # model param. - success = _Import('{}.{}.params.params'.format(task_root, task)) or success - if require_success and not success: - raise LookupError('Could not import any task params. Make sure task params ' - 'are linked into the binary.') - return success - - -def ImportParams(model_name, - task_root=_TASK_ROOT, - require_success=True): - """Attempts to only import the files that may contain the model.""" - # 'model_name' follows .. - if '.' not in model_name: - raise ValueError('Invalid model name %s' % model_name) - model_module = model_name.rpartition('.')[0] - # Try importing the module directly, in case it's a local import. - success = _Import(model_module) - # Try all locations of inserting params. - for module_with_params in _InsertParams(model_module): - success = _Import(module_with_params) or success - - # Try built-in tasks imports. - task_model_module = f'{task_root}.{model_module}' - success = _Import(task_model_module) or success - # Try all locations of inserting params. - for module_with_params in _InsertParams(task_model_module): - success = _Import(module_with_params) or success - - if require_success and not success: - raise LookupError( - f'Could not find any valid import paths for module {model_module}. ' - f'Make sure the relevant params files are linked into the binary.') - return success diff --git a/lingvo/model_registry.py b/lingvo/model_registry.py deleted file mode 100644 index dd06bb10..00000000 --- a/lingvo/model_registry.py +++ /dev/null @@ -1,272 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Convenience helpers for managing Params for datasets and models. - -Typical usage will be to define and register a subclass of ModelParams -for each dataset. -""" - -import inspect -from lingvo import model_imports -import lingvo.compat as tf -from lingvo.core import base_model_params - -tf.flags.DEFINE_string( - 'model_params_override', '', 'Optional text specifying' - ' model_class.Model() params to be overridden in' - ' Params.FromText format. Each param must be separated by' - ' a newline or a semicolon. This is only intended for' - ' quick experimentation. Only one of' - ' --model_params_override and --model_params_file_override' - ' may be specified.') -tf.flags.DEFINE_string( - 'model_params_file_override', '', 'Optional text file for' - ' overwriting model_class.Model() params in Params.FromText' - ' format. Each param must occur on a single line. Only one' - ' of --model_params_override and' - ' --model_params_file_override may be specified.') - -FLAGS = tf.flags.FLAGS - - -class _ModelRegistryHelper: - """Helper class.""" - - _MODEL_PARAMS_ALLOW_REDEF = False - - # Global dictionary mapping subclass name to registered ModelParam subclass. - _MODEL_PARAMS = {} - # Global set of modules from which ModelParam subclasses have been registered. - _REGISTERED_MODULES = set() - - @classmethod - def _ClassPathPrefix(cls): - return 'lingvo.tasks.' - - @classmethod - def _ModelParamsClassKey(cls, src_cls): - """Returns a string key used for `src_cls` in the model registry. - - The returned key is a period separated string. E.g., image.mnist.LeNet5. It - roughly reflects how params files are organized. We put some of the - directory information into the key to avoid future model name conflicts. - - Args: - src_cls: A subclass of `~.base_model.BaseModel`. - """ - path = src_cls.__module__ - # Removes the prefix. - path_prefix = cls._ClassPathPrefix() - path = path.replace(path_prefix, '') - - # Removes 'params.' if exists. - if 'params.' in path: - path = path.replace('params.', '') - if inspect.getfile(src_cls).endswith('test.py'): - return 'test.{}'.format(src_cls.__name__) - return '{}.{}'.format(path, src_cls.__name__) - - @classmethod - def _GetSourceInfo(cls, src_cls): - """Gets a source info string given a source class.""" - return '%s@%s:%d' % (cls._ModelParamsClassKey(src_cls), - inspect.getsourcefile(src_cls), - inspect.getsourcelines(src_cls)[-1]) - - @classmethod - def _RegisterModel(cls, wrapper_cls, src_cls): - """Registers a ModelParams subclass in the global registry.""" - key = cls._ModelParamsClassKey(src_cls) - module = src_cls.__module__ - if not cls._MODEL_PARAMS_ALLOW_REDEF and key in cls._MODEL_PARAMS: - raise ValueError('Duplicate model registered for key {}: {}.{}'.format( - key, module, src_cls.__name__)) - - tf.logging.debug('Registering model %s', key) - # Log less frequently (once per module) but at a higher verbosity level. - if module not in cls._REGISTERED_MODULES: - tf.logging.info('Registering models from module: %s', module) - cls._REGISTERED_MODULES.add(module) - - # Decorate param methods to add source info metadata. - cls._MODEL_PARAMS[key] = wrapper_cls - return key - - @classmethod - def _CreateWrapperClass(cls, src_cls): - """Creates a wrapper class for model params that adds source info.""" - - # Python2 class decorators that return a different class are fundamentally - # broken (technically, they are fine but canonical use of super() is - # broken). Also, fallback mechanisms don't exist in python3. So, we only - # decorate the version of the class that we register, but any decorators - # should return the original class for maximum compatibility. - # When the python3 super() is used, it should be possible to return this - # from the decorators too. - - registered_source_info = cls._GetSourceInfo(src_cls) - - class Registered(src_cls): - """Registered model wrapper.""" - - @property - def _registered_source_info(self): - return registered_source_info - - # Extend model to annotate source information. - def Model(self): - """Wraps BaseTask params into SingleTaskModel params.""" - p = super().Model() - p.model = self._registered_source_info - return p - - # So things show up in messages well. - Registered.__name__ = src_cls.__name__ - return Registered - - @classmethod - def MaybeUpdateParamsFromFlags(cls, cfg): - """Updates Model() Params from flags if set.""" - if FLAGS.model_params_override and FLAGS.model_params_file_override: - raise ValueError('Only one of --model_params_override and' - ' --model_params_file_override may be specified.') - - if FLAGS.model_params_override: - params_override = FLAGS.model_params_override.replace(';', '\n') - tf.logging.info('Applying params overrides:\n%s\nTo:\n%s', - params_override, cfg.ToText()) - cfg.FromText(params_override) - if (FLAGS.model_params_file_override and - tf.io.gfile.exists(FLAGS.model_params_file_override)): - params_override = tf.io.gfile.GFile(FLAGS.model_params_file_override, - 'r').read() - tf.logging.info('Applying params overrides from file %s:\n%s\nTo:\n%s', - FLAGS.model_params_file_override, params_override, - cfg.ToText()) - # Okay, there we go. This is where parameters are applied - cfg.FromText(params_override) - - @classmethod - def RegisterSingleTaskModel(cls, src_cls): - """Class decorator that registers a `.SingleTaskModelParams` subclass.""" - if not issubclass(src_cls, base_model_params.SingleTaskModelParams): - raise TypeError('src_cls %s is not a SingleTaskModelParams!' % - src_cls.__name__) - cls._RegisterModel(cls._CreateWrapperClass(src_cls), src_cls) - return src_cls - - @classmethod - def RegisterMultiTaskModel(cls, src_cls): - """Class decorator that registers a `.MultiTaskModelParams` subclass.""" - if not issubclass(src_cls, base_model_params.MultiTaskModelParams): - raise TypeError('src_cls %s is not a MultiTaskModelParams!' % - src_cls.__name__) - cls._RegisterModel(cls._CreateWrapperClass(src_cls), src_cls) - return src_cls - - @staticmethod - def GetAllRegisteredClasses(): - """Returns global registry map from model names to their param classes.""" - all_params = _ModelRegistryHelper._MODEL_PARAMS - if not all_params: - tf.logging.warning('No classes registered.') - return all_params - - @classmethod - def GetClass(cls, class_key): - """Returns a ModelParams subclass with the given `class_key`. - - Args: - class_key: string key of the ModelParams subclass to return. - - Returns: - A subclass of `~.base_model_params._BaseModelParams`. - - Raises: - LookupError: If no class with the given key has been registered. - """ - all_params = cls.GetAllRegisteredClasses() - if class_key not in all_params: - for k in sorted(all_params): - tf.logging.info('Known model: %s', k) - raise LookupError('Model %s not found from list of above known models.' % - class_key) - return all_params[class_key] - - @classmethod - def GetParams(cls, class_key, dataset_name): - """Constructs a `Params` object for given model and dataset, obeying flags. - - In case of default model, params may be updated based on the flags - `--model_params_override` or `--model_params_file_override`. - - Args: - class_key: String class key (i.e. `image.mnist.LeNet5`). - dataset_name: Method to generate dataset params (i.e. 'Test'). - - Returns: - Full `~.hyperparams.Params` for the model class. - """ - model_params_cls = cls.GetClass(class_key) - model_params = model_params_cls() - cfg = model_params.Model() - cfg.input = model_params.GetDatasetParams(dataset_name) - - cls.MaybeUpdateParamsFromFlags(cfg) - return cfg - - @classmethod - def GetProgramSchedule(cls, class_key): - """Retrieve the ProgramSchedule and a dict of task params. - - Args: - class_key: String class key (i.e. `image.mnist.LeNet5`). - - Returns: - ProgramSchedule.Params() - """ - model_params_cls = cls.GetClass(class_key) - model_params = model_params_cls() - program_schedule_cfg = model_params.ProgramSchedule() - return program_schedule_cfg - - -# pyformat: disable -# pylint: disable=invalid-name -RegisterSingleTaskModel = _ModelRegistryHelper.RegisterSingleTaskModel -RegisterMultiTaskModel = _ModelRegistryHelper.RegisterMultiTaskModel -# pylint: enable=invalid-name -# pyformat: enable - - -def GetAllRegisteredClasses(): - model_imports.ImportAllParams() - return _ModelRegistryHelper.GetAllRegisteredClasses() - - -def GetClass(class_key): - model_imports.ImportParams(class_key) - return _ModelRegistryHelper.GetClass(class_key) - - -def GetParams(class_key, dataset_name): - model_imports.ImportParams(class_key) - return _ModelRegistryHelper.GetParams(class_key, dataset_name) - - -def GetProgramSchedule(class_key): - model_imports.ImportParams(class_key) - return _ModelRegistryHelper.GetProgramSchedule(class_key) diff --git a/lingvo/model_registry_test.py b/lingvo/model_registry_test.py deleted file mode 100644 index 28be6630..00000000 --- a/lingvo/model_registry_test.py +++ /dev/null @@ -1,149 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for model_registry.""" - -from lingvo import model_registry -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import base_model_params -from lingvo.core import test_utils - -FLAGS = tf.flags.FLAGS - - -@model_registry.RegisterSingleTaskModel -class DummyModel(base_model_params.SingleTaskModelParams): - - def Train(self): - p = base_input_generator.BaseInputGenerator.Params() - p.name = 'Train' - return p - - def Dev(self): - p = base_input_generator.BaseInputGenerator.Params() - p.name = 'Dev' - return p - - def Test(self): - p = base_input_generator.BaseInputGenerator.Params() - p.name = 'Test' - return p - - def Task(self): - p = base_model.BaseTask.Params() - p.name = 'DummyModel' - return p - - -class ModelRegistryTest(test_utils.TestCase): - - def setUp(self): - FLAGS.model_params_override = '' - - def testGetClass(self): - - mp_cls = model_registry.GetClass('test.DummyModel') - mp = mp_cls() - self.assertEqual('Train', mp.Train().name) - self.assertEqual('Dev', mp.Dev().name) - self.assertEqual('Test', mp.Test().name) - self.assertIsNotNone(mp.Task()) - self.assertIsNotNone(mp.Model()) - - with self.assertRaises(LookupError): - # Not yet registered. - model_registry.GetClass('something.does.not.exist') - - def testGetParams(self): - cfg = model_registry.GetParams('test.DummyModel', 'Test') - self.assertIsNotNone(cfg) - self.assertEqual(DummyModel().Test(), cfg.input) - cfg.input = None - # Registered version adds model source info but direct does not. - cfg.model = None - self.assertEqual(DummyModel().Model(), cfg) - - with self.assertRaises(LookupError): - # Not yet registered. - cfg = model_registry.GetParams('something.does.not.exist', 'Test') - - with self.assertRaises(base_model_params.DatasetError): - cfg = model_registry.GetParams('test.DummyModel', 'UnknownDataset') - - def testGetParamsCanOverrideWithFlags(self): - cfg = model_registry.GetParams('test.DummyModel', 'Train') - - FLAGS.model_params_override = ( - 'train.max_steps: 10; train.ema_decay: 0.9\n' - 'train.init_from_checkpoint_rules : {"ckpt": (["abc", "def"], [])}\n') - cfg2 = model_registry.GetParams('test.DummyModel', 'Train') - - self.assertNotEqual(cfg.train.max_steps, 10) - self.assertEqual(cfg2.train.max_steps, 10) - self.assertNotEqual(cfg.train.ema_decay, 0.9) - self.assertEqual(cfg2.train.ema_decay, 0.9) - self.assertNotEqual(cfg.train.init_from_checkpoint_rules, - {'ckpt': (['abc', 'def'], [])}) - self.assertEqual(cfg2.train.init_from_checkpoint_rules, - {'ckpt': (['abc', 'def'], [])}) - - def testGetParamsCanOverrideWithFlagsRaises(self): - FLAGS.model_params_override = 'task.SOME_UNKNOWN_PARAM : 10' - with self.assertRaises(AttributeError): - _ = model_registry.GetParams('test.DummyModel', 'Train') - - def testGetParamsCanOverrideWithFlagsBadSyntax(self): - FLAGS.model_params_override = 'task.SOME_UNKNOWN_PARAM=10' - with self.assertRaises(ValueError): - _ = model_registry.GetParams('test.DummyModel', 'Train') - - def testGetParamsCanOverrideInputParamsWithFlags(self): - cfg = model_registry.GetParams('test.DummyModel', 'Train') - FLAGS.model_params_override = 'input.num_samples: 100' - - cfg2 = model_registry.GetParams('test.DummyModel', 'Train') - self.assertNotEqual(cfg.input.num_samples, 100) - self.assertEqual(cfg2.input.num_samples, 100) - - def testModelParamsIncludeSourceInfo(self): - path = 'lingvo/model_registry_test.py' - # NOTE: Only the registered version has source info. - self.assertIn(path, - model_registry.GetParams('test.DummyModel', 'Test').model) - - def testDoubleRegister(self): - - def CreateDuplicate(): - # pylint: disable=unused-variable - # pylint: disable=function-redefined - @model_registry.RegisterSingleTaskModel - class DummyDupl(DummyModel): - pass - - @model_registry.RegisterSingleTaskModel - class DummyDupl(DummyModel): - pass - - # pylint: enable=unused-variable - # pylint: enable=function-redefined - - with self.assertRaises(ValueError): - CreateDuplicate() - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/models_test.py b/lingvo/models_test.py deleted file mode 100644 index ef309985..00000000 --- a/lingvo/models_test.py +++ /dev/null @@ -1,39 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for models.""" - -from lingvo import model_imports # pylint: disable=unused-import -from lingvo import model_registry -# Import DummyModel -from lingvo import model_registry_test # pylint: disable=unused-import -from lingvo import models_test_helper -import lingvo.compat as tf -from lingvo.core import base_model - - -class ModelsTest(models_test_helper.BaseModelsTest): - - def testGetModelParams(self): - name = 'test.DummyModel' - p = model_registry.GetParams(name, 'Train') - self.assertTrue(issubclass(p.cls, base_model.SingleTaskModel)) - - -ModelsTest.CreateTestMethodsForAllRegisteredModels(model_registry) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/models_test_helper.py b/lingvo/models_test_helper.py deleted file mode 100644 index 5db6e708..00000000 --- a/lingvo/models_test_helper.py +++ /dev/null @@ -1,181 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Helper for models_test.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import bn_layers -from lingvo.core import py_utils -from lingvo.core import test_utils - - -def _StubOutCreateVariable(variable_cache): - """Stub out py_utils.CreateVariable to not spend time creating variables. - - Args: - variable_cache: a dict from unique shapes to a dummy tensor of that shape. - """ - - def _CreateVariableStub(name, - params, - reuse=None, - trainable=True, - collections=None, - default_seed=None, - synchronization=None, - aggregation=None): - """Return a zero tensor of the right shape instead of creating variable.""" - del reuse - del default_seed - del synchronization - del aggregation - dtype = params.dtype - shape = py_utils.ToStaticShape(params.shape) - # For total samples counters we have to actually create variables so that - # we can access the 'value' attribute during construction. - if 'total_samples' in name: - var = tf.get_variable( - name, - shape, - dtype, - tf.constant_initializer(0), - collections=collections, - trainable=trainable, - validate_shape=True) - else: - key = (tf.get_default_graph(), tuple(shape)) - if key in variable_cache: - var = variable_cache[key] - else: - var = tf.zeros(shape, dtype) - variable_cache[key] = var - return var - - py_utils.CreateVariable = _CreateVariableStub - - -def TraverseLayer(layer, fn): - """Traverses the layer tree and invokes fn(node) on each node. - - Args: - layer: a BaseLayer. - fn: a function of (layer, layer_theta) -> None. - """ - if isinstance(layer, (list, tuple)): - for layer_i in layer: - TraverseLayer(layer_i, fn) - return - - with tf.name_scope(layer.params.name): - fn(layer) - # Traverse all children in alphabetical order. - for _, child in sorted(layer.children.items()): - TraverseLayer(child, fn) - - -class BaseModelsTest(test_utils.TestCase): - """Base model test class which does not define any test methods of its own.""" - - def setUp(self): - super().setUp() - self._variable_cache = {} - _StubOutCreateVariable(self._variable_cache) - - def _ValidateEMA(self, name, mdl): - if not mdl.ema: - return - self.assertIsInstance(mdl, base_model.SingleTaskModel) - for task in mdl.tasks: - tp = task.params.train - # If the model has explicitly specified ema_decay_moving_vars to - # True or False, then we assume they understand the implication - # of that choice. - if tp.ema_decay_moving_vars is not None: - # ema_decay_moving_vars is set explicitly. - continue - # Otherwise the model should not contain any BatchNormLayer. - # - # If a model fails this test, the user should explicitly specify - # ema_decay_moving_vars, or ensure no BatchNormLayers are in their model. - all_layers = [] - TraverseLayer(task, all_layers.append) - batch_norm_layers = [ - layer.path - for layer in all_layers - if isinstance(layer, bn_layers.BatchNormLayer) - ] - self.assertEqual([], batch_norm_layers) - - def _testOneModelParams(self, registry, name): - with tf.Graph().as_default(): - p = registry.GetParams(name, 'Train') - self.assertTrue(issubclass(p.cls, base_model.BaseModel)) - self.assertIsNot(p.model, None) - p.cluster.mode = 'sync' - p.cluster.job = 'decoder' - p.cluster.decoder.replicas = 1 - with p.cluster.Instantiate(): - # Instantiate the params class, to help catch errors in layer - # constructors due to misconfigurations. - mdl = p.Instantiate() - self._ValidateEMA(name, mdl) - p = mdl.params - - for dataset in ('Train', 'Dev', 'Test'): - input_p = registry.GetParams(name, dataset).input - if issubclass(p.cls, base_model.SingleTaskModel): - self.assertTrue( - issubclass(input_p.cls, base_input_generator.BaseInputGenerator), - 'Error in %s' % dataset) - if (dataset != 'Train') and issubclass( - input_p.cls, - base_input_generator.BaseSequenceInputGenerator) and ( - input_p.num_samples != 0): - self.assertEqual( - input_p.num_batcher_threads, 1, - 'num_batcher_threads too large in %s. Decoder ' - 'or eval runs over this set might not span ' - 'exactly one epoch.' % dataset) - else: - self.assertTrue(issubclass(p.cls, base_model.MultiTaskModel)) - for _, v in input_p.IterParams(): - self.assertTrue( - issubclass(v.cls, base_input_generator.BaseInputGenerator), - 'Error in %s' % dataset) - - @classmethod - def CreateTestMethodsForAllRegisteredModels(cls, - registry, - task_prefix_filter='', - exclude_prefixes=None): - """Programmatically defines test methods for each registered model.""" - model_names = list(registry.GetAllRegisteredClasses().keys()) - for model_name in sorted(model_names): - if task_prefix_filter and not model_name.startswith(task_prefix_filter): - tf.logging.info('Skipping tests for registered model: %s', - model_name) - continue - if exclude_prefixes and any( - [model_name.startswith(prefix) for prefix in exclude_prefixes]): - tf.logging.info( - 'Explicitly excluding tests for registered model: %s', model_name) - continue - - def _Test(self, name=model_name): - self._testOneModelParams(registry, name) # pylint: disable=protected-access - - setattr(cls, 'testModelParams_%s' % model_name, _Test) diff --git a/lingvo/repo.bzl b/lingvo/repo.bzl deleted file mode 100644 index 0a59eb27..00000000 --- a/lingvo/repo.bzl +++ /dev/null @@ -1,243 +0,0 @@ -"""Setup autoconf repo for tensorflow.""" - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -load("//third_party:repo.bzl", "third_party_http_archive") - -def _find_tf_include_path(repo_ctx): - exec_result = repo_ctx.execute( - [ - "python3", - "-c", - "import tensorflow as tf; import sys; " + - "sys.stdout.write(tf.sysconfig.get_include())", - ], - quiet = True, - ) - if exec_result.return_code != 0: - fail("Could not locate tensorflow installation path.") - return exec_result.stdout.splitlines()[-1] - -def _find_tf_lib_path(repo_ctx): - exec_result = repo_ctx.execute( - [ - "python3", - "-c", - "import tensorflow as tf; import sys; " + - "sys.stdout.write(tf.sysconfig.get_lib())", - ], - quiet = True, - ) - if exec_result.return_code != 0: - fail("Could not locate tensorflow installation path.") - return exec_result.stdout.splitlines()[-1] - -def _eigen_archive_repo_impl(repo_ctx): - tf_include_path = _find_tf_include_path(repo_ctx) - repo_ctx.symlink(tf_include_path, "tf_includes") - repo_ctx.file( - "BUILD", - content = """ -cc_library( - name = "includes", - hdrs = glob(["tf_includes/Eigen/**/*.h", - "tf_includes/Eigen/**", - "tf_includes/unsupported/Eigen/**/*.h", - "tf_includes/unsupported/Eigen/**"]), - # https://groups.google.com/forum/#!topic/bazel-discuss/HyyuuqTxKok - includes = ["tf_includes"], - visibility = ["//visibility:public"], -) -""", - executable = False, - ) - -def _absl_includes_repo_impl(repo_ctx): - tf_include_path = _find_tf_include_path(repo_ctx) - repo_ctx.symlink( - tf_include_path + "/absl", - "absl", - ) - repo_ctx.file( - "BUILD", - content = """ -cc_library( - name = "includes", - hdrs = glob(["absl/**/*.h", - "absl/**/*.inc"]), - includes = ["absl"], - visibility = ["//visibility:public"], -) -""", - executable = False, - ) - -def _zlib_includes_repo_impl(repo_ctx): - tf_include_path = _find_tf_include_path(repo_ctx) - repo_ctx.symlink( - tf_include_path + "/external/zlib", - "zlib", - ) - repo_ctx.file( - "BUILD", - content = """ -cc_library( - name = "includes", - hdrs = glob(["zlib/**/*.h"]), - includes = ["zlib"], - visibility = ["//visibility:public"], -) -""", - executable = False, - ) - -def _protobuf_includes_repo_impl(repo_ctx): - tf_include_path = _find_tf_include_path(repo_ctx) - repo_ctx.symlink(tf_include_path, "tf_includes") - repo_ctx.file( - "BUILD", - content = """ -cc_library( - name = "includes", - hdrs = glob(["tf_includes/google/protobuf/*.h", - "tf_includes/google/protobuf/*.inc", - "tf_includes/google/protobuf/**/*.h", - "tf_includes/google/protobuf/**/*.inc"]), - includes = ["tf_includes"], - visibility = ["//visibility:public"], -) -""", - executable = False, - ) - -def _tensorflow_includes_repo_impl(repo_ctx): - tf_include_path = _find_tf_include_path(repo_ctx) - repo_ctx.symlink(tf_include_path, "tensorflow_includes") - repo_ctx.file( - "BUILD", - content = """ -cc_library( - name = "includes", - hdrs = glob(["tensorflow_includes/**/*.h", - "tensorflow_includes/third_party/eigen3/**"]), - includes = ["tensorflow_includes"], - deps = ["@absl_includes//:includes", - "@eigen_archive//:includes", - "@protobuf_archive//:includes", - "@zlib_includes//:includes",], - visibility = ["//visibility:public"], -) -""", - executable = False, - ) - -def _tensorflow_solib_repo_impl(repo_ctx): - tf_lib_path = _find_tf_lib_path(repo_ctx) - repo_ctx.symlink(tf_lib_path, "tensorflow_solib") - repo_ctx.file( - "BUILD", - content = """ -cc_library( - name = "framework_lib", - srcs = ["tensorflow_solib/libtensorflow_framework.so.2"], - visibility = ["//visibility:public"], -) -""", - ) - -def cc_tf_configure(): - """Autoconf pre-installed tensorflow repo.""" - make_eigen_repo = repository_rule(implementation = _eigen_archive_repo_impl) - make_eigen_repo(name = "eigen_archive") - make_absl_repo = repository_rule( - implementation = _absl_includes_repo_impl, - ) - make_absl_repo(name = "absl_includes") - make_zlib_repo = repository_rule( - implementation = _zlib_includes_repo_impl, - ) - make_zlib_repo(name = "zlib_includes") - make_protobuf_repo = repository_rule( - implementation = _protobuf_includes_repo_impl, - ) - make_protobuf_repo(name = "protobuf_archive") - make_tfinc_repo = repository_rule( - implementation = _tensorflow_includes_repo_impl, - ) - make_tfinc_repo(name = "tensorflow_includes") - make_tflib_repo = repository_rule( - implementation = _tensorflow_solib_repo_impl, - ) - make_tflib_repo(name = "tensorflow_solib") - -def lingvo_testonly_deps(): - if not native.existing_rule("com_google_googletest"): - http_archive( - name = "com_google_googletest", - build_file_content = """ -cc_library( - name = "gtest", - srcs = [ - "googletest/src/gtest-all.cc", - "googlemock/src/gmock-all.cc", - ], - copts = ["-D_GLIBCXX_USE_CXX11_ABI=0"], - hdrs = glob([ - "**/*.h", - "googletest/src/*.cc", - "googlemock/src/*.cc", - ]), - includes = [ - "googlemock", - "googletest", - "googletest/include", - "googlemock/include", - ], - linkopts = ["-pthread"], - visibility = ["//visibility:public"], -) - -cc_library( - name = "gtest_main", - copts = ["-D_GLIBCXX_USE_CXX11_ABI=0"], - srcs = ["googlemock/src/gmock_main.cc"], - linkopts = ["-pthread"], - visibility = ["//visibility:public"], - deps = [":gtest"], -) -""", - urls = [ - "https://github.com/google/googletest/archive/release-1.8.0.tar.gz", - ], - sha256 = "58a6f4277ca2bc8565222b3bbd58a177609e9c488e8a72649359ba51450db7d8", - strip_prefix = "googletest-release-1.8.0", - ) - -def lingvo_protoc_deps(): - http_archive( - name = "protobuf_protoc", - build_file_content = """ -filegroup( - name = "protoc_bin", - srcs = ["bin/protoc"], - visibility = ["//visibility:public"], -) -""", - urls = [ - "https://github.com/protocolbuffers/protobuf/releases/download/v3.8.0/protoc-3.8.0-linux-x86_64.zip", - ], - sha256 = "717903f32653f07cd895cfe89ca18ff4ca35f825afa7fe17bcb5cb13bf628be0", - ) - -def icu(): - third_party_http_archive( - name = "icu", - strip_prefix = "icu-release-64-2", - sha256 = "dfc62618aa4bd3ca14a3df548cd65fe393155edd213e49c39f3a30ccd618fc27", - urls = [ - "https://storage.googleapis.com/mirror.tensorflow.org/github.com/unicode-org/icu/archive/release-64-2.zip", - "https://github.com/unicode-org/icu/archive/release-64-2.zip", - ], - build_file = "//third_party/icu:BUILD.bazel", - system_build_file = "//third_party/icu:BUILD.system", - patch_file = "//third_party/icu:udata.patch", - ) diff --git a/lingvo/tasks/BUILD b/lingvo/tasks/BUILD deleted file mode 100644 index e98e1a23..00000000 --- a/lingvo/tasks/BUILD +++ /dev/null @@ -1,20 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "all_params", - srcs_version = "PY3", - # LINT.IfChange(task_dirs) - deps = [ - "//lingvo/tasks/asr/params", - "//lingvo/tasks/car/params", - "//lingvo/tasks/image/params", - "//lingvo/tasks/lm/params", - "//lingvo/tasks/mt/params", - "//lingvo/tasks/punctuator/params", - ], - # LINT.ThenChange(../model_imports.py:task_dirs) -) diff --git a/lingvo/tasks/__init__.py b/lingvo/tasks/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/asr/BUILD b/lingvo/tasks/asr/BUILD deleted file mode 100644 index 4f392698..00000000 --- a/lingvo/tasks/asr/BUILD +++ /dev/null @@ -1,286 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "frontend", - srcs = ["frontend.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:py_utils", - ], -) - -py_test( - name = "frontend_test", - srcs = ["frontend_test.py"], - data = [ - "//lingvo/tools/testdata:audio_data", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":frontend", - # Additional FFT kernels dependency. - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "input_generator", - srcs = ["input_generator.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:generic_input", - "//lingvo/core:py_utils", - ], -) - -py_test( - name = "input_generator_test", - srcs = ["input_generator_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":input_generator", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "encoder", - srcs = ["encoder.py", "encoder_v2.py", "blocks.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:layers", - # "//lingvo/core:conformer_layer", - "//lingvo/core:model_helper", - "//lingvo/core:plot", - "//lingvo/core:py_utils", - "//lingvo/core:rnn_cell", - "//lingvo/core:rnn_layers", - "//lingvo/core:spectrum_augmenter", - "//lingvo/core:summary_utils", - ], -) - -py_test( - name = "encoder_test", - srcs = ["encoder_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":encoder", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "fusion", - srcs = ["fusion.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:layers", - "//lingvo/core:py_utils", - "//lingvo/tasks/lm:layers", - ], -) - -py_library( - name = "decoder_utils", - srcs = ["decoder_utils.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:symbolic", - # Implicit six dependency. - ], -) - -py_test( - name = "decoder_utils_test", - srcs = ["decoder_utils_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":decoder", - ":decoder_utils", - "//lingvo:compat", - "//lingvo/core:rnn_cell", - "//lingvo/core:symbolic", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "contextualizer_base", - srcs = ["contextualizer_base.py"], - srcs_version = "PY3", - deps = [ - "//lingvo/core:base_layer", - ], -) - -py_library( - name = "decoder", - srcs = ["decoder.py"], - srcs_version = "PY3", - deps = [ - ":contextualizer_base", - ":decoder_utils", - ":fusion", - "//lingvo:compat", - "//lingvo/core:attention", - "//lingvo/core:base_decoder", - "//lingvo/core:cluster_factory", - "//lingvo/core:layers", - "//lingvo/core:plot", - "//lingvo/core:py_utils", - "//lingvo/core:recurrent", - "//lingvo/core:rnn_cell", - "//lingvo/core:summary_utils", - "//lingvo/core:symbolic", - # Implicit matplotlib dependency. - ], -) - -py_test( - name = "decoder_test", - size = "large", - srcs = ["decoder_test.py"], - python_version = "PY3", - shard_count = 10, - srcs_version = "PY3", - deps = [ - ":decoder", - # Implicit python proto dependency. - "//lingvo:compat", - "//lingvo/core:cluster_factory", - "//lingvo/core:layers", - "//lingvo/core:py_utils", - "//lingvo/core:symbolic", - "//lingvo/core:test_utils", - "//lingvo/core/ops:hyps_py_pb2", - # Implicit numpy dependency. - ], -) - -py_library( - name = "model_test_input_generator", - srcs = ["model_test_input_generator.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:py_utils", - ], -) - -py_library( - name = "model", - srcs = ["model.py"], - srcs_version = "PY3", - deps = [ - ":decoder", - ":decoder_utils", - ":encoder", - ":frontend", - "//lingvo:compat", - "//lingvo/core:base_model", - "//lingvo/core:metrics", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - "//lingvo/tools:audio_lib", - ], -) - -py_library( - name = "ctc_model", - srcs = ["ctc_model.py"], - srcs_version = "PY2AND3", - deps = [ - ":encoder", - ":frontend", - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:base_model", - "//lingvo/core:metrics", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - "//lingvo/tools:audio_lib", - # Implicit six dependency. - ], -) - -py_test( - name = "model_test", - srcs = ["model_test.py"], - data = ["//lingvo/tools/testdata:audio_data"], - python_version = "PY3", - shard_count = 15, - srcs_version = "PY3", - deps = [ - ":decoder", - ":input_generator", - ":model", - ":model_test_input_generator", - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:cluster_factory", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - "//lingvo/core:summary_utils", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_test( - name = "ctc_model_test", - srcs = ["ctc_model_test.py"], - data = ["//lingvo/tools/testdata:audio_data"], - python_version = "PY3", - shard_count = 1, - srcs_version = "PY3", - deps = [ - ":decoder", - ":input_generator", - ":ctc_model", - ":model_test_input_generator", - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:cluster_factory", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - "//lingvo/core:summary_utils", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -filegroup( - name = "wpm_files", - srcs = ["wpm_16k_librispeech.vocab"], -) diff --git a/lingvo/tasks/asr/__init__.py b/lingvo/tasks/asr/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/asr/align_input_generator.py b/lingvo/tasks/asr/align_input_generator.py deleted file mode 100644 index 83feb731..00000000 --- a/lingvo/tasks/asr/align_input_generator.py +++ /dev/null @@ -1,147 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Speech recognition input generator.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import generic_input -from lingvo.core import py_utils - -from tensorflow.python.ops import inplace_ops # pylint:disable=g-direct-tensorflow-import - - -class AsrInput(base_input_generator.BaseSequenceInputGenerator): - """Input generator for ASR.""" - - @classmethod - def Params(cls): - """Defaults params for AsrInput.""" - p = super().Params() - p.Define('frame_size', 40, 'The number of coefficients in each frame.') - p.Define('append_eos_frame', True, 'Append an all-zero frame.') - p.Define('static_vocabulary_table_path', '', 'Path to new-line delimited table of sample id keys') - p.source_max_length = 3000 - return p - - def _DataSourceFromFilePattern(self, file_pattern): - - def Proc(record): - """Parses a serialized tf.Example record.""" - # There we go! string, string, float32. I hope frames is allowed - # to be a waveform directly... - features = [ - ('uttid', tf.io.VarLenFeature(tf.int64)), - # Would like to change this to tf.int16 in the future, if that is possible (would have to read from - ('frames', tf.io.VarLenFeature(tf.float32)), - ] - example = tf.io.parse_single_example(record, dict(features)) - fval = {k: v.values for k, v in example.items()} - # Reshape the flattened vector into its original time-major - # representation. - fval['frames'] = tf.reshape( - fval['frames'], shape=[-1, self.params.frame_size]) - # Input duration determines the bucket. - bucket_key = tf.cast(tf.shape(fval['frames'])[0], tf.int32) - if self.params.append_eos_frame: - bucket_key += 1 - src_paddings = tf.zeros([tf.shape(fval['frames'])[0]], dtype=tf.float32) - return [ - fval['uttid'], fval['frames'], src_paddings - ], bucket_key - - return generic_input.GenericInput( - file_pattern=file_pattern, - processor=Proc, - dynamic_padding_dimensions=[0] * 3, - dynamic_padding_constants=[0] * 2 + [1], - **self.CommonInputOpArgs()) - - def _MaybePadSourceInputs(self, src_inputs, src_paddings): - p = self.params - if not p.append_eos_frame: - return src_inputs, src_paddings - - per_src_len = tf.reduce_sum(1 - src_paddings, 1) - per_src_len += 1 - max_src_len = tf.reduce_max(per_src_len) - input_shape = tf.shape(src_inputs) - input_len = tf.maximum(input_shape[1], tf.cast(max_src_len, tf.int32)) - pad_steps = input_len - input_shape[1] - src_inputs = tf.concat([ - src_inputs, - tf.zeros( - inplace_ops.inplace_update(input_shape, 1, pad_steps), - src_inputs.dtype) - ], 1) - src_paddings = 1 - tf.sequence_mask( - tf.reshape(per_src_len, [input_shape[0]]), tf.reshape(input_len, []), - src_paddings.dtype) - return src_inputs, src_paddings - - def __init__(self, params): - super().__init__(params) - p = self.params - - (utt_ids, src_frames, src_paddings), self._bucket_keys = self._BuildDataSource() - - self._sample_ids = utt_ids - - src_frames, src_paddings = self._MaybePadSourceInputs( - src_frames, src_paddings) - - # We expect src_inputs to be of shape - # [batch_size, num_frames, feature_dim, channels]. - src_frames = tf.expand_dims(src_frames, axis=-1) - - if p.pad_to_max_seq_length: - assert p.source_max_length - assert p.target_max_length - - if all(x == p.bucket_batch_limit[0] for x in p.bucket_batch_limit): - # Set the input batch size as an int rather than a tensor. - src_frames_shape = (self.InfeedBatchSize(), p.source_max_length, - p.frame_size, 1) - src_paddings_shape = (self.InfeedBatchSize(), p.source_max_length) - else: - tf.logging.warning( - 'Could not set static input shape since not all bucket batch sizes ' - 'are the same:', p.bucket_batch_limit) - src_frames_shape = None - src_paddings_shape = None - - src_frames = py_utils.PadBatchDimension(src_frames, self.InfeedBatchSize(), 0) - src_paddings = py_utils.PadBatchDimension(src_paddings, self.InfeedBatchSize(), - 1) - self._sample_ids = py_utils.PadBatchDimension(self._sample_ids, self.InfeedBatchSize(), - self._sample_ids.min) - - src_frames = py_utils.PadSequenceDimension( - src_frames, p.source_max_length, 0, shape=src_frames_shape) - src_paddings = py_utils.PadSequenceDimension( - src_paddings, p.source_max_length, 1, shape=src_paddings_shape) - self._sample_ids = tf.ensure_shape(self._sample_ids, self.InfeedBatchSize()) - - src = py_utils.NestedMap(src_inputs=src_frames, paddings=src_paddings) - - self._src = src - - def _InputBatch(self): - batch = py_utils.NestedMap() - batch.bucket_keys = self._bucket_keys - batch.src = self._src - # Bingo. TPUs don't support tf.string... If anyone there were some way to avoid enqueueing and dequeueing this in the first place... - batch.sample_ids = self._sample_ids - - return batch diff --git a/lingvo/tasks/asr/blocks.py b/lingvo/tasks/asr/blocks.py deleted file mode 100644 index 9b6e20b6..00000000 --- a/lingvo/tasks/asr/blocks.py +++ /dev/null @@ -1,315 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Encoders for the speech model.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import layers -from lingvo.core import model_helper -from lingvo.core import py_utils -from lingvo.core import rnn_cell -# from lingvo.core import conformer_layer - - -class LSTMBlock(base_layer.BaseLayer): - - @classmethod - def Params(cls): - """Configs for AsrEncoder.""" - p = super().Params() - p.Define('lstm_tpl', rnn_cell.LSTMCellSimple.Params(), - 'Configs template for the RNN layer.') - p.Define('lstm_cell_size', 256, 'LSTM cell size for the RNN layer.') - p.Define('input_feats', None, 'Number of features input to first LSTM.') - p.Define('lstm_type', 'fwd', 'fwd or bidi') - p.Define('num_lstm_layers', 3, 'Number of rnn layers to create') - p.Define('dropout', layers.DropoutLayer.Params(), - 'Apply dropout between LSTM') - p.Define('bidi_rnn_type', 'func', - 'Options: func. func: BidirectionalFRNN. ') - p.Define('unidi_rnn_type', 'func', - 'Options: func. func: UnidirectionalFRNN. ') - # Default config for the rnn layer. - p.lstm_tpl.params_init = py_utils.WeightInit.Uniform(0.1) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - params_rnn_layers = [] - lstm_out_size = p.lstm_cell_size - - assert p.lstm_type in {'bidi', 'fwd'}, 'Only fwd, bidi allowed' - if p.lstm_type == 'bidi': - lstm_out_size *= 2 - - output_dim = p.input_feats - for i in range(p.num_lstm_layers): - input_dim = output_dim - forward_p = p.lstm_tpl.Copy() - forward_p.name = 'fwd_rnn_L%d' % i - forward_p.num_input_nodes = input_dim - forward_p.num_output_nodes = p.lstm_cell_size - - if p.lstm_type == 'bidi': - backward_p = forward_p.Copy() - backward_p.name = 'bak_rnn_L%d' % i - rnn_p = self.CreateBidirectionalRNNParams(forward_p, backward_p) - rnn_p.name = 'brnn_L%d' % i - else: - rnn_p = self.CreateUnidirectionalRNNParams(forward_p) - rnn_p.name = 'rnn_L%d' % i - - params_rnn_layers.append(rnn_p) - output_dim = lstm_out_size - - if p.dropout: - self.CreateChildren('dropout', p.dropout) - - self.CreateChildren('rnn', params_rnn_layers) - - @property - def output_dim(self): - multiplier = 2 if self.params.lstm_type == 'bidi' else 1 - return self.params.lstm_cell_size * multiplier - - def CreateBidirectionalRNNParams(self, forward_p, backward_p): - return model_helper.CreateBidirectionalRNNParams(self.params, forward_p, - backward_p) - - def CreateUnidirectionalRNNParams(self, forward_p): - return model_helper.CreateUnidirectionalRNNParams(self.params, forward_p) - - def FProp(self, theta, inputs, paddings): - """ - inputs: (B, T, F, 1) - paddings: (B, T) - outputs: encoded, padding: (T, B, F), (T, B) - """ - - p = self.params - rnn_in = tf.transpose(tf.squeeze(inputs, axis=3), [1, 0, 2]) - rnn_padding = tf.expand_dims(tf.transpose(paddings), 2) - # rnn_in is of shape [time, batch, depth] - # rnn_padding is of shape [time, batch, 1] - - for i in range(p.num_lstm_layers): - rnn_out = self.rnn[i].FProp(theta.rnn[i], rnn_in, rnn_padding) - - if p.lstm_type == 'fwd': - rnn_out, _ = rnn_out - - if p.dropout: - rnn_out = self.dropout.FProp(theta.dropout, rnn_out) - - rnn_in = rnn_out - - encoded = rnn_out * (1.0 - rnn_padding) - padding = tf.squeeze(rnn_padding, [2]) - return encoded, padding - - -class ConvolutionalDownsampler(base_layer.BaseLayer): - """ Use convolution with striding to achieve stacking effect """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('cnn_tpl', layers.ConvLayer.Params(), - 'Configs template for the conv layer.') - p.Define('num_cnn_layers', None, 'Number of CNN layers') - p.Define('conv_filter_shapes', None, 'Filter shapes for each conv layer.') - p.Define('conv_filter_strides', None, - 'Filter strides for each conv layer (T, F).') - p.Define('input_shape', [None, None, 80, 1], - 'Shape of the input. This should a TensorShape with rank 4.') - - p.num_cnn_layers = 2 - p.conv_filter_shapes = [(5, 5, 1, 32), (5, 5, 32, 32)] - p.conv_filter_strides = [(1, 2), (3, 2)] # (T, F) - p.cnn_tpl.params_init = py_utils.WeightInit.TruncatedGaussian(0.1) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - assert p.num_cnn_layers == len(p.conv_filter_shapes) - assert p.num_cnn_layers == len(p.conv_filter_strides) - params_conv_layers = [] - for i in range(p.num_cnn_layers): - conv_p = p.cnn_tpl.Copy() - conv_p.name = 'conv_L%d' % i - conv_p.filter_shape = p.conv_filter_shapes[i] - conv_p.filter_stride = p.conv_filter_strides[i] - params_conv_layers.append(conv_p) - - self.CreateChildren('conv', params_conv_layers) - - assert p.input_shape is not None - conv_output_shape = p.input_shape - for i in range(p.num_cnn_layers): - conv_output_shape = self.conv[i].OutShape(conv_output_shape) - - assert len(conv_output_shape) == 4 # batch, height, width, channel. - feat_dim = conv_output_shape[-1] * conv_output_shape[-2] - self.conv_output_shape = (*conv_output_shape[:2], feat_dim, 1) - - @property - def output_dim(self): - return self.conv_output_shape[2] - - def FProp(self, theta, inputs, paddings): - # BTF1 -> BTF1 - - conv_out = inputs - out_padding = paddings - - for i, conv_layer in enumerate(self.conv): - conv_out, out_padding = conv_layer.FProp(theta.conv[i], conv_out, - out_padding) - - b, t, f1, f2 = py_utils.GetShape(conv_out) - conv_out = tf.reshape(conv_out, [b, t, f1 * f2, 1]) - return conv_out, out_padding - - -class InputStackingDownsampler(base_layer.BaseLayer): - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_shape', [None, None, 80, 1], - 'Shape of the input. This should a TensorShape with rank 4.') - p.Define('stacking', layers.StackingOverTime.Params(), 'Stacking params') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self.CreateChild('stacking', p.stacking.Copy()) - self.num_feats = p.input_shape[-2] * self.stacking.window_size - - @property - def output_dim(self): - return self.num_feats - - def FProp(self, theta, inputs, paddings): - # BTF1 -> BTF1 - inputs = tf.squeeze(inputs, axis=3) # BTF1 -> BTF - paddings = tf.expand_dims(paddings, axis=2) # BT -> BT1 - encoded, padding = self.stacking.FProp(inputs, paddings) - encoded = tf.expand_dims(encoded, axis=3) # BTF -> BTF1 - padding = tf.squeeze(padding, axis=2) # BT1 -> BT - return encoded, padding - - -# Lingvo's conformer models work with TF2.3 but TPUs have issues with TF2.3 -# class ConformerBlock(base_layer.BaseLayer): - -# @ classmethod -# def Params(cls): -# p = super().Params() - -# p.Define('input_feats', None, 'Required') -# p.Define('dropout_prob', 0., '') - -# p.Define('atten_num_heads', 4, '') -# p.Define('atten_local_context', 3, '') -# p.Define('kernel_size', 32, '') -# p.Define('fflayer_activation', 'SWISH', '') -# p.Define('layer_order', 'mhsa_before_conv', '') -# p.Define('num_conformer_blocks', 1, 'Number of conformer layers') - -# return p - -# def __init__(self, params): -# super().__init__(params) -# p = self.params -# assert p.input_feats is not None - -# conformer_tpl = conformer_layer.ConformerLayer.CommonParams( -# input_dim=p.input_feats, -# atten_num_heads=p.atten_num_heads, -# atten_local_context=p.atten_local_context, -# kernel_size=p.kernel_size, -# fflayer_hidden_dim=p.input_feats // 2) - -# for i in range(p.num_conformer_blocks): -# self.CreateChild(f'conformer_{i}', conformer_tpl.Copy()) - -# @property -# def output_dim(self): -# return self.params.input_feats - -# def FProp(self, theta, input, paddings): -# """ -# inputs: (B, T, F, 1) -# paddings: (B, T) -# outputs: encoded, padding: (T, B, F), (T, B) -# same as LSTM block -# """ -# p = self.params - -# input = tf.squeeze(input, 3) # BTF1 -> BTF - -# for i in range(p.num_conformer_blocks): -# conf_block = getattr(self, f'conformer_{i}') -# conf_theta = getattr(theta, f'conformer_{i}') -# encoded, padding = conf_block.FProp(conf_theta, input, paddings) -# input, paddings = encoded, padding - -# encoded = tf.transpose(encoded, [1, 0, 2]) -# padding = tf.transpose(padding) -# return encoded, padding - - -class VocabProjectionBlock(base_layer.BaseLayer): - - @classmethod - def Params(cls): - p = super().Params() - p.Define('input_dim', None, 'Required') - p.Define('vocab_size', None, 'Required') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - pp = layers.FCLayer.Params() - pp.activation = 'NONE' - pp.output_dim = min(p.vocab_size, 96) - pp.input_dim = p.input_dim - pp.params_init = py_utils.WeightInit.Uniform(0.1) - self.CreateChild('projection', pp.Copy()) - - remaining = p.vocab_size - 96 - if remaining > 0: - rp = layers.FCLayer.Params() - rp.activation = 'NONE' - rp.output_dim = p.vocab_size - 96 - rp.input_dim = p.input_dim - rp.params_init = py_utils.WeightInit.Uniform(0.01) - self.CreateChild('lowpri_projection', rp.Copy()) - - def FProp(self, theta, inputs, paddings): - encoded = self.projection.FProp(theta.projection, inputs) - - if hasattr(self, 'lowpri_projection'): - encoded2 = self.lowpri_projection.FProp(theta.lowpri_projection, inputs) - encoded = tf.concat([encoded, encoded2], axis=2) - - return encoded, paddings diff --git a/lingvo/tasks/asr/contextualizer_base.py b/lingvo/tasks/asr/contextualizer_base.py deleted file mode 100644 index 5c6ff7d4..00000000 --- a/lingvo/tasks/asr/contextualizer_base.py +++ /dev/null @@ -1,126 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""API for context injection into a speech decoder.""" - -from lingvo.core import base_layer - - -class ContextualizerBase(base_layer.BaseLayer): - """Base class for a contextualizer. - - Typical usage of a contextualizer is:: - - contextualizer.SetContextMap(context_map, theta) # Set context map. - contextualizer.InitAttention(...) # Initalize attn sources. - context = contextualizer.ZeroAttention(...) # Initalize attn state. - context = contextualizer.QueryAttention(...) # Call on each decoding step. - - `context` is a per-decoding-step context vector that augments the standard - LAS model with additional context. - - `context_map` can include data needed for initialization. - - After parameters of the contextualizer are set, these accessors can be used: - - - contextualizer.GetContextDim() - """ - - def SetContextMap(self, context_map, theta): - """Set the context map. - - Args: - context_map: A NestedMap object containing the context from which - attention vectors will be computed. - theta: NestedMap, parameters needed for embedding. - """ - raise NotImplementedError('SetContextMap') - - def InitAttention(self, theta, packed_src, misc_states): - """Initialized the contextualizer's attention. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - packed_src: A NestedMap object into which attention source vectors will be - placed. - misc_states: A NestedMap object into which attention states will be - placed. - """ - raise NotImplementedError('InitAttention') - - def ZeroAttention(self, theta, dec_bs, misc_states, audio_context, - packed_src): - """Creates the contextualizer 'zero' context vector. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - dec_bs: int32, the decoding batch size. - misc_states: A NestedMap object into which attention states will be - placed. - audio_context: Tensor of shape [dec_bs, aud_dim] representing the - audio-based context vector. - packed_src: A NestedMap object into which attention source vectors will be - placed. - - Returns: - A 'zero' context vector of shape [dec_bs, aud_dim + context_dim] - """ - raise NotImplementedError('ZeroAttention') - - def QueryAttention(self, theta, attn_query, misc_states, audio_context, - packed_src): - """Query the contextualizer's attention. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - attn_query: Tensor of shape [dec_bs, ...] representing the query vectors. - misc_states: A NestedMap object into which attention states will be - placed. - audio_context: Tensor of shape [dec_bs, aud_dim] representing the - audio-based context vector. - packed_src: A NestedMap object into which attention source vectors will be - placed. - - Returns: - A context vector of shape [dec_bs, aud_dim + context_dim] - """ - raise NotImplementedError('QueryAttention') - - def GetContextDim(self): - """Returns the context dimension.""" - raise NotImplementedError('GetContextDim') - - -class NullContextualizer(ContextualizerBase): - """An 'empty' or no-op contextualizer.""" - - def SetContextMap(self, context_map, theta): - pass - - def InitAttention(self, theta, packed_src): - pass - - def ZeroAttention(self, theta, dec_bs, misc_states, audio_context, - packed_src): - return audio_context - - def QueryAttention(self, theta, attn_query, misc_states, audio_context, - packed_src): - return audio_context - - def GetContextDim(self): - return 0 diff --git a/lingvo/tasks/asr/ctc_model.py b/lingvo/tasks/asr/ctc_model.py deleted file mode 100644 index ad655d39..00000000 --- a/lingvo/tasks/asr/ctc_model.py +++ /dev/null @@ -1,333 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""CTC model.""" - -from collections import defaultdict -import numpy as np -import os - -import lingvo.compat as tf -from lingvo.core import base_model -from lingvo.core import metrics -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.tasks.asr import blocks -from lingvo.tasks.asr import encoder_v2 -from lingvo.tasks.asr import decoder_utils -from lingvo.tasks.asr import input_generator - -# https://stackoverflow.com/a/2912455 -class keydefaultdict(defaultdict): - def __missing__(self, key): - if self.default_factory is None: - raise KeyError(key) - else: - ret = self[key] = self.default_factory(key) # pylint: disable=not-callable - return ret - -class CTCModel(base_model.BaseTask): - """ - CTC model without a language model. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - # Need to set frontend appropriately - 'frontend', None, - 'ASR frontend to extract features from input. Defaults to no frontend ' - 'which means that features are taken directly from the input.') - - p.Define('encoder_v2', encoder_v2.AsrEncoder.Params(), 'Encoder V2 Params') - - # Defaults based on graphemes / ascii_tokenizer.cc - p.Define('vocab_size', 76, - 'Vocabulary size, *including* the blank symbol.') - p.Define( - 'blank_index', 73, 'Index assigned to epsilon, aka blank for CTC. ' - 'This should never appear in the label sequence. Reconsider this.') - - p.Define('inference_compute_only_log_softmax', False, - 'At inference time, compute the output of log softmax, rather ' - 'than running any sort of CTC decoder.') - p.Define('log_softmax_output_directory', '', - 'Path to which to dump log_softmax values') - - tp = p.train - tp.lr_schedule = (schedule.PiecewiseConstantSchedule.Params().Set( - boundaries=[350000, 450000, 600000], values=[1.0, 0.1, 0.01, 0.001])) - - tp.vn_start_step = 20_000 - tp.vn_std = 0.075 - tp.l2_regularizer_weight = 1e-6 - tp.clip_gradient_norm_to_value = 1.0 - tp.grad_norm_to_clip_to_zero = 100.0 - tp.tpu_steps_per_loop = 100 - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - if not p.encoder_v2.name: - p.encoder_v2.name = 'enc' - self.CreateChild('encoder', p.encoder_v2) - - if p.frontend: - self.CreateChild('frontend', p.frontend) - - projection_p = blocks.VocabProjectionBlock.Params() - projection_p.vocab_size = p.vocab_size - projection_p.input_dim = self.encoder.output_dim - self.CreateChild('project_to_vocab_size', projection_p) - - if p.inference_compute_only_log_softmax: - assert p.log_softmax_output_directory, 'Must provide non-empty path to which to dump log softmax values' - # `ulimit -n` gives me 1024 on this machine. We may need to - # increase the number of open file descriptors allowed in this - # process - class WriterAndRecordsWritten: - records_written: int - file_handle: tf.io.TFRecordWriter - def __init__(self, write_path: str): - self.records_written = 0 - self.file_handle = tf.io.TFRecordWriter(write_path) - self._int64_audio_document_id_to_file_handle = keydefaultdict( - WriterAndRecordsWritten) - - def ComputePredictions(self, theta, input_batch): - return self._FrontendAndEncoderFProp(theta, input_batch.src) - - def DecodeWithTheta(self, theta, input_batch): - """Constructs the inference graph.""" - p = self.params - # from IPython import embed; embed() - with tf.name_scope('decode'), tf.name_scope(p.name): - with tf.name_scope('encoder'): - encoder_outputs = self._FrontendAndEncoderFProp(theta, input_batch.src) - if p.inference_compute_only_log_softmax: - global_step = tf.train.get_global_step() - increment_global_step = tf.assign(global_step, global_step + 1) - with tf.control_dependencies([increment_global_step]): - log_probabilities = tf.transpose(tf.nn.log_softmax(encoder_outputs.encoded, axis=2), - perm=(1, 0, 2)) - with tf.name_scope('decoder'): - decoder_outs = self._DecodeCTC(encoder_outputs) - # encoder_outputs's shape is [T,B,F] - return {'log_probabilities': log_probabilities, - 'log_probabilities_lengths': - py_utils.LengthsFromBitMask(encoder_outputs.padding, 0), - 'int64_uttid': input_batch.sample_ids, - 'int64_audio_document_id': input_batch.audio_document_ids, - 'num_utterances_in_audio_document': input_batch.num_utterances_in_audio_document, - 'transcripts': decoder_outs.transcripts, - } - with tf.name_scope('decoder'): - decoder_outs = self._DecodeCTC(encoder_outputs) - - decoder_metrics = py_utils.RunOnTpuHost(self._CalculateErrorRates, decoder_outs, input_batch) - return decoder_metrics - - def PostProcessDecodeOut(self, decode_out_dict, dec_metrics_dict): - p = self.params - if p.inference_compute_only_log_softmax: - mini_batch_size = decode_out_dict['log_probabilities'].shape[0] - dec_metrics_dict['num_samples_in_batch'].Update(mini_batch_size) - - for i in range(mini_batch_size): - int64_uttid = decode_out_dict['int64_uttid'][i] - int64_audio_document_id = decode_out_dict['int64_audio_document_id'][i] - if int64_uttid == input_generator.RawAsrInputIntegerUttIds.PAD_INDEX: - continue - length = decode_out_dict['log_probabilities_lengths'][i] - flat_log_probabilities = decode_out_dict['log_probabilities'][i, :length, :].flatten() - record_bytes = tf.train.Example( - features=tf.train.Features(feature={ - 'int64_uttid': tf.train.Feature( - int64_list=tf.train.Int64List(value=[int64_uttid])), - 'log_probabilities': tf.train.Feature( - float_list=tf.train.FloatList(value=flat_log_probabilities)), - 'transcripts': tf.train.Feature( - bytes_list=tf.train.BytesList(value=[decode_out_dict['transcripts'][i]])), - }) - ).SerializeToString() - output_path = os.path.join(p.log_softmax_output_directory, f'int64_audio_document_id={int64_audio_document_id}/shard.tfrecord') - pair = self._int64_audio_document_id_to_file_handle[output_path] - pair.file_handle.write(record_bytes) - pair.records_written += 1 - if pair.records_written == decode_out_dict['num_utterances_in_audio_document'][i]: - pair.file_handle.close() - del self._int64_audio_document_id_to_file_handle[output_path] - return - - gt_transcripts = decode_out_dict['target_transcripts'] - hyp_transcripts = decode_out_dict['decoded_transcripts'] - if not py_utils.use_tpu(): - utt_id = decode_out_dict['utt_id'] - else: - utt_id = [["TPU does not know utt_id, sorry"]] * len(gt_transcripts) - - for i in range(len(gt_transcripts)): - # tf.logging.info('utt_id : %s', utt_id[i][0]) - tf.logging.info('ref_str: %s', gt_transcripts[i]) - tf.logging.info('hyp_str: %s', hyp_transcripts[i]) - - total_word_err = np.sum(decode_out_dict['num_wrong_words']) - total_ref_words = np.sum(decode_out_dict['num_ref_words']) - total_char_err = np.sum(decode_out_dict['num_wrong_chars']) - total_ref_chars = np.sum(decode_out_dict['num_ref_chars']) - - dec_metrics_dict['num_samples_in_batch'].Update(len(gt_transcripts)) - dec_metrics_dict['wer'].Update(total_word_err / max(1., total_ref_words), - total_ref_words) - dec_metrics_dict['cer'].Update(total_char_err / max(1., total_ref_chars), - total_ref_chars) - tf.logging.info(' ]]] CER: %.3f ]]] WER: %.3f', - dec_metrics_dict['cer'].value, - dec_metrics_dict['wer'].value) - - def ComputeLoss(self, theta, predictions, input_batch): - output_batch = predictions - assert self.params.blank_index == 31 - ctc_loss = tf.nn.ctc_loss( - input_batch.tgt.labels, - output_batch.encoded, - py_utils.LengthsFromBitMask(input_batch.tgt.paddings, 1), - py_utils.LengthsFromBitMask(output_batch.padding, 0), - logits_time_major=True, - blank_index=self.params.blank_index) - - # ctc_loss.shape = (B) - total_loss = tf.reduce_mean(ctc_loss) - per_sequence_loss = {'loss': ctc_loss} - return dict(loss=(total_loss, 1.0)), per_sequence_loss - - def CreateDecoderMetrics(self): - base_metrics = { - 'wer': metrics.AverageMetric(), - 'cer': metrics.AverageMetric(), - 'num_samples_in_batch': metrics.AverageMetric() - } - return base_metrics - - @classmethod - def FPropMeta(cls, params, *args, **kwargs): - raise NotImplementedError('No FPropMeta available.') - - def _FrontendAndEncoderFProp(self, theta, input_batch_src): - p = self.params - - if p.frontend: - input_batch_src = self.frontend.FProp(theta.frontend, input_batch_src) - - encoder_out = self.encoder.FProp(theta.encoder, input_batch_src) - - encoded, padding = self.project_to_vocab_size(encoder_out.encoded, - encoder_out.padding) - outputs = py_utils.NestedMap(encoded=encoded, padding=padding) - return outputs - - def _DecodeCTC(self, output_batch): - tok_logits = output_batch.encoded # (T, B, F) - - # GALVEZ: Make beam_width a tunable parameter! - # (decoded,), _ = py_utils.RunOnTpuHost(tf.nn.ctc_beam_search_decoder, tok_logits, - # py_utils.LengthsFromBitMask( - # output_batch.padding, 0), - # beam_width=100) - # (decoded,), _ = tf.nn.ctc_beam_search_decoder(tok_logits, - # py_utils.LengthsFromBitMask( - # output_batch.padding, 0), - # beam_width=100) - return py_utils.RunOnTpuHost(cpu_tf_graph_decode, - tok_logits, output_batch.padding, - self.params.blank_index, self.input_generator) - - def _CalculateErrorRates(self, dec_outs_dict, input_batch): - # return {'stuff': dec_outs_dict.sparse_ids.values} - gt_seq_lens = py_utils.LengthsFromBitMask(input_batch.tgt.paddings, 1) - gt_transcripts = py_utils.RunOnTpuHost(self.input_generator.IdsToStrings, - input_batch.tgt.labels, - gt_seq_lens) - - # token error rate - char_dist = tf.edit_distance(tf.string_split(dec_outs_dict.transcripts, - sep=''), - tf.string_split(gt_transcripts, sep=''), - normalize=False) - - ref_chars = tf.strings.length(gt_transcripts) - num_wrong_chars = tf.reduce_sum(char_dist) - num_ref_chars = tf.cast(tf.reduce_sum(ref_chars), tf.float32) - cer = num_wrong_chars / num_ref_chars - - # word error rate - word_dist = decoder_utils.ComputeWer(dec_outs_dict.transcripts, - gt_transcripts) # (B, 2) - num_wrong_words = tf.reduce_sum(word_dist[:, 0]) - num_ref_words = tf.reduce_sum(word_dist[:, 1]) - wer = num_wrong_words / num_ref_words - ret_dict = { - 'target_ids': input_batch.tgt.ids, - 'target_labels': input_batch.tgt.labels, - 'target_weights': input_batch.tgt.weights, - 'target_paddings': input_batch.tgt.paddings, - 'target_transcripts': gt_transcripts, - 'decoded_transcripts': dec_outs_dict.transcripts, - 'wer': wer, - 'cer': cer, - 'num_wrong_words': num_wrong_words, - 'num_ref_words': num_ref_words, - 'num_wrong_chars': num_wrong_chars, - 'num_ref_chars': num_ref_chars - } - if not py_utils.use_tpu(): - ret_dict['utt_id'] = input_batch.sample_ids - - return ret_dict - -def cpu_tf_graph_decode(tok_logits, padding, blank_index, input_generator): - # (T, B, F) - # ctc_beam_search_decoder assumes blank_index=0 - assert blank_index == 31 - - # TODO: Consider making beam_width larger - (decoded,), _ = tf.nn.ctc_beam_search_decoder(tok_logits, - py_utils.LengthsFromBitMask(padding, 0), - beam_width=100) - # Could easily use blank_index here as well, right? - invalid = tf.constant(-1, tf.int64) - dense_dec = tf.sparse_to_dense(decoded.indices, - decoded.dense_shape, - decoded.values, - default_value=invalid) - - batch_segments = decoded.indices[:, 0] - times_in_each_batch = decoded.indices[:, 1] - - decoded_seq_lengths = tf.cast(tf.math.segment_max(times_in_each_batch, batch_segments) + 1, tf.int32) - # What happens if an empty sequence is output??? Then pad appropriately, tada! - decoded_seq_lengths = py_utils.PadBatchDimension(decoded_seq_lengths, tf.shape(tok_logits)[1], 0) - - hyp_str = py_utils.RunOnTpuHost(input_generator.IdsToStrings, - tf.cast(dense_dec, tf.int32), - decoded_seq_lengths) - - hyp_str = tf.strings.regex_replace(hyp_str, '()+', '') - hyp_str = tf.strings.regex_replace(hyp_str, '()+', '') - hyp_str = tf.strings.regex_replace(hyp_str, '()+', '') - return py_utils.NestedMap(sparse_ids=decoded, transcripts=hyp_str) diff --git a/lingvo/tasks/asr/ctc_model_test.py b/lingvo/tasks/asr/ctc_model_test.py deleted file mode 100644 index 78825f6b..00000000 --- a/lingvo/tasks/asr/ctc_model_test.py +++ /dev/null @@ -1,75 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for Asr Model.""" - -import lingvo.compat as tf -from lingvo.core import test_utils -from lingvo.tasks.asr import ctc_model -from lingvo.tasks.asr import model_test_input_generator as tig - - -class AsrCtcModelTest(test_utils.TestCase): - # bazel test //lingvo/tasks/asr:ctc_model_test --test_output=all - - def _testParams(self): - input_shape = [12, 16, 80, 1] # (B, T, F, 1) - p = ctc_model.CTCModel.Params() - p.vocab_size = 76 - p.blank_index = 73 - - # Initialize encoder params. - p.encoder = None - ep = p.encoder_v2 - ep.use_specaugment = True - - ep.stacking_subsampler = None - sub = ep.conv_subsampler - sub.input_shape = input_shape - - elp = ep.lstm_block - elp.input_feats = None # leave as none to infer automatically - elp.lstm_cell_size = 128 - elp.num_lstm_layers = 5 - elp.lstm_type = 'fwd' - elp.dropout.keep_prob = 0.8 - - # ep.lstm_block = None - # ecp = ep.conformer_block - # ecp.num_conformer_blocks = 5 - # ecp.name = 'conformer_layer' - - p.input = tig.TestInputGenerator.Params() - p.input.target_max_length = 5 - p.input.source_shape = input_shape - p.input.target_shape = [12, 5] - p.name = 'test_ctc_mdl' - return p - - def testFProp_conv_sub(self): - with self.session(use_gpu=False): - tf.random.set_seed(93820985) - p = self._testParams() - mdl = p.Instantiate() - - # FPropDefaultTheta -> FPropTower -> { ComputePredictions ; ComputeLoss; } - metrics, _ = mdl.FPropDefaultTheta() - self.evaluate(tf.global_variables_initializer()) - - ctc, _ = metrics['loss'] - test_utils.CompareToGoldenSingleFloat(self, 76.710762, ctc.eval()) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/asr/decoder.py b/lingvo/tasks/asr/decoder.py deleted file mode 100644 index 214fbc54..00000000 --- a/lingvo/tasks/asr/decoder.py +++ /dev/null @@ -1,1556 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Decoders for the speech model.""" - -import collections -import math -import lingvo.compat as tf -from lingvo.core import attention -from lingvo.core import base_decoder -from lingvo.core import cluster_factory -from lingvo.core import layers -from lingvo.core import plot -from lingvo.core import py_utils -from lingvo.core import recurrent -from lingvo.core import rnn_cell -from lingvo.core import summary_utils -from lingvo.core import symbolic -from lingvo.tasks.asr import contextualizer_base -from lingvo.tasks.asr import decoder_utils -from lingvo.tasks.asr import fusion -from matplotlib import font_manager - - -def _ToTensorArray(name, v, max_seq_length, clear_after_read=None): - """Create TensorArray from v, of size max_seq_length.""" - ta = tf.TensorArray( - v.dtype, max_seq_length, name=name, clear_after_read=clear_after_read) - ta = ta.unstack(v) - return ta - - -def _NewTensorArray(name, max_seq_length, dtype=None): - """Create empty TensorArray which can store max_seq_length elements.""" - return tf.TensorArray(dtype, max_seq_length, name=name) - - -class AsrDecoderBase(base_decoder.BaseBeamSearchDecoder): - """Base class for RNN-with-attention speech decoders. - - The decoder takes encoder_outputs, a NestedMap generated by the encoder, as - input. The NestedMap is expected to contain the following fields: - - - 'encoded': encoded features - - 'padding': padding for encoded features - - The decoder operates in a 'step-by-step' fashion. The model encapsulates all - information which should persist from one step to the next in the - DecoderStepState NestedMap, which provides a 'misc_states' NestedMap which can - store arbitrary information required by the specific decoder sub-class. - - A 'step' in training consists of the following sequence of steps which compute - the outputs from the decoder given the current input target (and the state of - the model after making the previous predictions): - - 1. Compute the input target at the current time step:: - - cur_target_info = self.TargetsToBeFedAtCurrentDecodeStep(...) - - 2. Update state and compute outputs by running SingleDecodeStep:: - - step_outs, new_state = self.SingleDecodeStep(...) - - 3. Update state based on the logits computed at this step:: - - new_state = self.PostStepDecoderStateUpdate(old_state, logits) - - 4. Display summaries based on the accumulated information across all steps:: - - self.AddAdditionalDecoderSummaries(seq_out_tas) - - Sub-classes can customize behavior by implementing the following functions, - which will modify the behavior of the decoder: - - For beam search decoder: - - _InitBeamSearchStateCallback - - _PreBeamSearchStepCallback - - _PostBeamSearchStepCallback - For EMBR training: - - ComputeHypsWithBeamSearch - - - MiscZeroState: NestedMap which represents the initial state for the - 'misc_states' in the DecoderStepState. The default implementation returns - an empty NestedMap. - - - SingleDecodeStep: This corresponds to the computation which happens in each - step of the model. The function should return the outputs of the decoder - as well as the updated state. - - - PostStepDecoderStateUpdate: A function which updates the DecoderStepState - after the output logits from the decoder have been computed. By default, - this returns the DecoderStepState unchanged. - - - TargetsToBeFedAtCurrentDecodeStep: Returns a TargetInfo namedtuple, which - represents information about the targets which should be input at the - current step, as well as the output label which should be predicted. - The default implementation uses the values in the batched 'targets' - provided by the InputGenerator. - - - AddAdditionalDecoderSummaries: A function which can be used to add any - decoder specific information as part of the summaries displayed during - training. By default this is a no-op. - - - CreateTargetInfoMisc: A function which can be used to store arbitrary - information as required by a sub-classes in the target info arrays used - to determine the current label at each step during training. By default, - this creates an empty NestedMap. - - A few other functions that control how the decoder initializes and computes - attention during the initial step, and during each step can also be - modified, if need be: - - - _GetAttenContextDim: The dimensionality of the attention context vector. - - _CreateAtten: Controls how the attention module is configured. Most - subclasses will not have to change this unless it changes how attention - works. - - BaseZeroState: Returns initial state of RNNs, and attention. - - _InitAttention: Initializes Tensors used by the attention module. - - _GetInitialSeqStateTensorArrays: Get intitial tensor arrays for - ComputePredictionsDynamic. - - _GetNewAttenProbs: Update atten probs for a timestep and return the - updated tensor array. - """ - # pylint: disable=invalid-name - # DecoderStepState encapsulates everything that needs to persist from one - # 'step' of next label prediction to the next. This interface is also used - # while performing inference in the model, so any changes here should be - # undertaken with care. Although the presence of the fields listed below is - # not enforced explicitly in the code, all sub-classes should use the same - # structure. - # - # rnn_states: List of NestedMaps, corresponding to states of all RNNs in the - # decoder. - # atten_context, atten_probs, atten_states: See attention.py for details. - # misc_states: NestedMap, which can contain anything the decoder needs to - # persist from one step to another. - # - # DecoderStepState = py_utils.NestedMap( - # rnn_states=..., - # atten_context=..., - # atten_probs=..., - # atten_states=..., - # misc_states=..., - # ) - - # TargetInfo encapsulates information about the input target sequence - # available during training. - # These are only used during training, so sub-classes are free to add any - # additional target specific information in the 'misc' field, which can be - # used to represent any model specific information. - # misc: NestedMap, that can contain any model specific target information. By - # default, this is an empty NestedMap. - TargetInfo = collections.namedtuple( - 'TargetInfo', ['id', 'label', 'weight', 'emb', 'padding', 'misc']) - - # SequenceOutTensorArrays encapsulates the various outputs generated as we - # step through the decoder. These are used to display statistics during - # training, and the information in these arrays can be used to modify the - # decoder steps, e.g., by using previously predicted outputs to modify targets - # fed at the next step in the ScheduledSampling decoder. - # These are only used during training, so modifications to these to add - # additional components are fine. In particular, the 'misc' field is a list of - # TensorArrays corresponding to the 'misc_states' in the DecoderStepState - # across all steps. - SequenceOutTensorArrays = collections.namedtuple( - 'SequenceOutTensorArrays', - ['rnn_outs', 'step_outs', 'atten_probs', 'logits', 'fusion', 'misc']) - # pylint: enable=invalid-name - - @classmethod - def Params(cls): - p = super().Params() - p.Define('dropout_prob', 0.0, 'Prob at which we do dropout.') - p.Define('emb', layers.EmbeddingLayer.Params(), 'Embedding layer params.') - p.Define('emb_dim', 0, 'dimension of the embedding layer.') - p.Define('label_smoothing', None, 'Label smoothing class.') - p.Define( - 'rnn_cell_tpl', rnn_cell.LSTMCellSimple.Params(), - 'RNNCell params template. ' - 'Can be a single param or ' - 'a list of rnn_layers params, one for each layer.') - p.Define('rnn_cell_dim', 0, 'size of the rnn cells.') - p.Define( - 'rnn_cell_hidden_dim', 0, 'internal size of the rnn cells. When ' - 'set to > 0 it enables a projection layer at the output of the ' - 'rnn cell (see call to SetRnnCellNodes).') - p.Define('attention', attention.AdditiveAttention.Params(), - 'Additive attention params.') - p.Define('softmax', layers.SimpleFullSoftmax.Params(), 'Softmax params.') - p.Define('softmax_uses_attention', True, - 'Controls whether attention is fed to the softmax or not.') - p.Define('source_dim', 0, 'Dimension of the source encodings.') - p.Define('atten_context_dim', 0, - 'Depth of the attention context vector output.') - p.Define( - 'attention_plot_font_properties', '', - 'Adds font properties for the given file if set. Required ' - 'for displaying east-Asian character sets on plot axes.') - p.Define('rnn_layers', 1, 'Number of rnn layers.') - p.Define( - 'residual_start', 0, - 'Start residual connections from this layer. For this and higher ' - 'layers, the layer output is the sum of the RNN cell output and ' - 'input; if the layer also normalizes its output, then the ' - 'normalization is done over this sum. Set to 0 to disable ' - 'residual connections.') - p.Define('fusion', fusion.NullFusion.Params(), 'Fusion class params.') - p.Define('parallel_iterations', 30, - 'Max number of iterations to run in parallel for while loop.') - p.Define( - 'per_token_avg_loss', True, - 'Use per-token average loss when set to True (default); when set ' - 'to False use sequence average loss (sum logP across tokens in an ' - 'output sequence) and average across all sequences in the batch.') - p.Define( - 'token_normalized_per_seq_loss', False, - 'Whether or not to normalize the per-sequence loss by the sequence ' - 'length.') - # Configs for scheduled sampling. - p.Define( - 'min_ground_truth_prob', 1.0, - 'The min probability of using the ground truth as the previous ' - 'prediction.') - p.Define('min_prob_step', 1e6, 'Step to reach min_ground_truth_prob.') - p.Define( - 'prob_decay_start_step', 1e4, - 'The step to starts linearly decrease the probability of sampling ' - 'ground truth.') - p.Define( - 'use_while_loop_based_unrolling', True, - 'Whether or not to use while loop based unrolling for training.' - ' If false, we use a functional while based unrolling.') - p.Define( - 'logit_types', {'logits': 1.0}, - 'A dict of logit_name -> loss_weight. logit_name must be a field in ' - 'the predictions NestedMap. loss_weight should add up to 1.0.') - p.Define( - 'use_unnormalized_logits_as_log_probs', True, - 'If true, decoder beam search may return unnormalized logits as ' - 'log_probs. Used for backwards-compatibility.') - p.Define( - 'contextualizer', contextualizer_base.NullContextualizer.Params(), - 'A contextualizer that can be used' - 'to inject context into the decoder. The default NullContextualizer ' - 'does not add parameters to the model nor changes the ' - 'computation.') - p.Define('focal_loss_alpha', None, 'The weighting factor alpha.') - p.Define('focal_loss_gamma', None, 'Tunable focusing parameter.') - p.Define('adapter_layer_tpl', layers.MultitaskAdapterLayer.Params(), - 'Params for domain/language adatper layer.') - p.Define( - 'adapter_task_id_field', None, - 'Setting this will enable the use of adapter layers. This is the name ' - 'of the field in the encoder_outputs to extract the tasks IDs for ' - 'adatper layers.') - - # Set some reasonable default values. - # Default config for the embedding layer. - vocab = 96 - p.emb_dim = 96 - p.emb.vocab_size = vocab - p.emb.max_num_shards = 1 - p.emb.params_init = py_utils.WeightInit.Uniform(1.0) - # Default config for the rnn layer. - p.rnn_cell_dim = 256 - p.rnn_cell_tpl.params_init = py_utils.WeightInit.Uniform(0.1) - # Default config for the attention model. - p.attention.hidden_dim = 128 - p.attention.params_init = py_utils.WeightInit.UniformSqrtDim(math.sqrt(3.0)) - # Default config for the softmax part. - p.softmax.num_classes = vocab - p.softmax.params_init = py_utils.WeightInit.Uniform(0.1) - # LM config, if used. - p.fusion.lm.vocab_size = vocab - # Other configs. - p.target_seq_len = 300 - p.source_dim = 512 - p.adapter_layer_tpl.data_format = 'TBC' - return p - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Updates params with the vocab size and wpm model. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model params updated with the vocab size and wpm model. - """ - p.emb.vocab_size = vocab_size - p.softmax.num_classes = vocab_size - p.fusion.lm = p.fusion.lm.cls.UpdateTargetVocabSize(p.fusion.lm, vocab_size, - wpm_model) - if p.label_smoothing: - p.label_smoothing.num_classes = vocab_size - return p - - def __init__(self, params): - params = params.Copy() - if params.min_ground_truth_prob < 1: - # Move embedding lookup onto worker. - params.emb.on_ps = False - super().__init__(params) - - p = self.params - assert not p.packed_input, ('Packed inputs are not yet supported for ' - 'AsrDecoderBase.') - - self._max_label_prob = 1 - p.min_ground_truth_prob - self._decay_interval = p.min_prob_step - p.prob_decay_start_step - if self._decay_interval <= 0: - raise ValueError('min_prob_step (%d) <= prob_decay_start_step (%d)' % - (p.min_prob_step, p.prob_decay_start_step)) - - if p.attention_plot_font_properties: - self._font_properties = font_manager.FontProperties( - fname=p.attention_plot_font_properties) - else: - self._font_properties = font_manager.FontProperties() - - name = p.name - self.CreateChild('contextualizer', p.contextualizer) - atten_context_dim = self._GetAttenContextDim() - assert symbolic.IsExpr(atten_context_dim) or atten_context_dim > 0 - - p.emb.dtype = p.dtype - p.emb.embedding_dim = p.emb_dim - self.CreateChild('emb', p.emb) - - params_rnn_cells = [] - params_adapter_layers = [] - feat_dim = p.emb_dim - for i in range(p.rnn_layers): - if isinstance(p.rnn_cell_tpl, (list, tuple)): - assert len(p.rnn_cell_tpl) == p.rnn_layers - rnn_cell_params = p.rnn_cell_tpl[i].Copy() - else: - rnn_cell_params = p.rnn_cell_tpl.Copy() - rnn_cell_params.dtype = p.dtype - rnn_cell_params.inputs_arity = 2 - decoder_utils.SetRnnCellNodes(p, rnn_cell_params) - rnn_cell_params.num_input_nodes = feat_dim + atten_context_dim - if i == 0: - rnn_cell_params.name = 'rnn_cell' - else: - rnn_cell_params.name = 'rnn_cell_%d' % i - feat_dim = rnn_cell_params.num_output_nodes - params_rnn_cells.append(rnn_cell_params) - if p.adapter_task_id_field is not None: - adapter_p = p.adapter_layer_tpl.Copy() - adapter_p.name = 'adapter_%d' % i - adapter_p.input_dim = feat_dim - params_adapter_layers.append(adapter_p) - self.CreateChildren('rnn_cell', params_rnn_cells) - self.CreateChildren('adapters', params_adapter_layers) - - p.softmax.dtype = p.dtype - p.softmax.input_dim = feat_dim - if p.softmax_uses_attention: - p.softmax.input_dim += atten_context_dim - self.CreateChild('softmax', p.softmax) - - if p.fusion: - p.fusion.base_model_logits_dim = p.softmax.input_dim - self.CreateChild('fusion', p.fusion) - - self._CreateAtten() - - if p.label_smoothing is not None: - p.label_smoothing.name = 'smoother' - if p.label_smoothing.num_classes == 0: - p.label_smoothing.num_classes = p.softmax.num_classes - elif p.label_smoothing.num_classes != p.softmax.num_classes: - raise ValueError('label_smoothing.num_classes ({}) does not match ' - 'softmax.num_classes ({})'.format( - p.label_smoothing.num_classes, - p.softmax.num_classes)) - self.CreateChild('smoother', p.label_smoothing) - - def _CreateAtten(self): - p = self.params - p.attention.dtype = p.dtype - p.attention.source_dim = p.attention.source_dim or p.source_dim - p.attention.query_dim = ( - p.attention.query_dim or self.rnn_cell[0].params.num_output_nodes) - self.CreateChild('atten', p.attention) - - def _GetAttenContextDim(self): - p = self.params - audio_context_dim = ( - p.atten_context_dim if p.atten_context_dim else p.source_dim) - additional_context_dim = self.contextualizer.GetContextDim() - return audio_context_dim + additional_context_dim - - def _ApplyDropout(self, theta, x_in, deterministic=False, extra_seed=None): - p = self.params - assert 0 <= p.dropout_prob and p.dropout_prob < 1.0 - if self.do_eval or p.dropout_prob == 0.0: - return x_in - - if deterministic: - seeds = py_utils.GenerateStepSeedPair(p, theta.global_step) - if extra_seed: - seeds += extra_seed - return py_utils.DeterministicDropout(x_in, 1.0 - p.dropout_prob, seeds) - else: - seed = p.random_seed - if seed and extra_seed: - seed += extra_seed - return tf.nn.dropout(x_in, rate=p.dropout_prob, seed=seed) - - def _InitAttention(self, theta, encoder_outputs): - """Intializes attention and returns a NestedMap with those values.""" - packed_src = self.atten.InitForSourcePacked( - theta.atten, - encoder_outputs.encoded, # src - encoder_outputs.encoded, # context - encoder_outputs.padding) - self.contextualizer.InitAttention(theta.contextualizer, packed_src) - return packed_src - - def _GetEncoderPaddings(self, encoder_outputs): - """Get Encoder Paddings from encoder_outputs.""" - if encoder_outputs and isinstance(encoder_outputs.padding, tf.Tensor): - return encoder_outputs.padding - else: - return None - - def BaseZeroState(self, - theta, - encoder_outputs, - bs, - misc_zero_states, - per_step_source_padding=None): - """Returns initial state of RNNs, and attention.""" - p = self.params - rnn_states = [] - for i in range(p.rnn_layers): - rnn_states.append(self.rnn_cell[i].zero_state(theta.rnn_cell[i], bs)) - - packed_src = self._InitAttention(theta, encoder_outputs) - zero_atten_state = self.atten.ZeroAttentionState( - tf.shape(encoder_outputs.padding)[0], bs) - (atten_context, atten_probs, atten_states) = ( - self.atten.ComputeContextVectorWithSource( - theta.atten, - packed_src, - py_utils.Zeros([bs, self.rnn_cell[0].params.num_output_nodes], - dtype=py_utils.FPropDtype(p)), - zero_atten_state, - per_step_source_padding=per_step_source_padding)) - atten_context = self.contextualizer.ZeroAttention( - theta.contextualizer, bs, misc_zero_states, atten_context, packed_src) - - return rnn_states, atten_context, atten_probs, atten_states, packed_src - - def AddAdditionalDecoderSummaries(self, encoder_outputs, targets, seq_out_tas, - softmax_input): - """Additional model-specific summaries which should be displayed.""" - pass - - def DecoderStepZeroState(self, theta, encoder_outputs, target_ids, bs): - misc_zero_states = self.MiscZeroState(theta, encoder_outputs, target_ids, - bs) - rnn_states, atten_context, atten_probs, atten_states, packed_src = ( - self.BaseZeroState(theta, encoder_outputs, bs, misc_zero_states)) - return py_utils.NestedMap( - rnn_states=rnn_states, - atten_context=atten_context, - atten_probs=atten_probs, - atten_states=atten_states, - fusion_states=self.fusion.zero_state(theta.fusion, bs), - misc_states=misc_zero_states), packed_src - - def _AddDecoderActivationsSummary(self, - encoder_outputs, - targets, - atten_probs, - rnn_outs, - softmax_input, - additional_atten_probs=None, - target_alignments=None): - """Adds summary about decoder activations. - - For each of the args, a TensorArray can also be a Tensor representing - the stacked array. - - Args: - encoder_outputs: a NestedMap computed by encoder. - targets: a NestedMap, usually input_batch.tgt. - atten_probs: a TensorArray of max_target_length elements, each of shape - [batch, max_source_length]. - rnn_outs: a list of TensorArray, one for each RNN layer. Each - TensorArray has max_target_length elements, each of shape [batch, - rnn_output_dim]. - softmax_input: a Tensor of shape [batch, max_target_length, vocab_size]. - additional_atten_probs: an optional list of (name, TensorArray) to display - along with atten_probs. - target_alignments: an optional Tensor of shape [batch, max_target_length] - where every value is an int32 in the range of [1, max_source_length], - representing number of source frames by which a target label should be - emitted. - - Returns: - A finalized figure. - """ - source_encs = encoder_outputs.encoded - source_paddings = encoder_outputs.padding - if not self.cluster.add_summary: - return - - def _ToTensor(t): - return t.stack() if isinstance(t, tf.TensorArray) else t - - atten_probs = _ToTensor(atten_probs) - rnn_outs = [_ToTensor(ta) for ta in rnn_outs] - if additional_atten_probs: - additional_atten_probs = [ - (name, _ToTensor(ta)) for name, ta in additional_atten_probs - ] - - num_cols = 2 + len(rnn_outs) - fig = plot.MatplotlibFigureSummary( - 'decoder_example', - figsize=(2.3 * (3 + num_cols - 1), 6), - max_outputs=1, - subplot_grid_shape=(2, num_cols), - gridspec_kwargs=dict( - width_ratios=[3] + [1] * (num_cols - 1), height_ratios=(4, 1))) - - # Attention needs a custom plot_func to allow for clean y-axis label for - # very long transcripts - def PlotAttention(fig, axes, transcript, atten_probs, title): - plot.AddImage(fig, axes, atten_probs, title=title) - axes.set_ylabel( - plot.ToUnicode(transcript) + '\nOutput token', - size='x-small', - wrap=True, - fontproperties=self._font_properties) - - index = 0 - if 'transcripts' not in targets: - return - transcript = targets.transcripts[:index + 1] - - srclen = tf.cast( - tf.round(tf.reduce_sum(1 - source_paddings[:, index])), tf.int32) - tgtlen = tf.cast( - tf.round(tf.reduce_sum(1 - targets.paddings[index, :])), tf.int32) - - def PlotAttentionForOneExample(atten_probs, - target_fig, - title, - alignments=None): - """Plots attention for one example.""" - tf.logging.info('Plotting attention for %s: %s %s', title, - atten_probs.shape, alignments) - atten_probs = atten_probs[:tgtlen, index, :srclen] - if alignments is not None: - # [tgtlen]. - alignment_positions = alignments[index, :tgtlen] - 1 - # [tgtlen, srclen]. - alignment_probs = tf.one_hot(alignment_positions, depth=srclen, axis=-1) - - # The summary image will use red bars to represent target label - # alignments and purple shades for attention probabilities. - atten_probs = 1 - tf.stack( - [ - atten_probs, - # Overlay atten_probs and alignment_probs on the green channel - # so that colors are visible on a white background. - tf.minimum(atten_probs + alignment_probs, 1.), - alignment_probs - ], - axis=-1) - probs = tf.expand_dims(atten_probs, 0) - target_fig.AddSubplot([transcript, probs], PlotAttention, title=title) - - PlotAttentionForOneExample(atten_probs, fig, title=u'atten_probs') - # rnn_outs and softmax_input have transposed shapes of [tgtlen, dim] - # compared to source_encs [dim, srclen]. - for i in range(len(rnn_outs)): - rnn_out = tf.expand_dims(rnn_outs[i][:tgtlen, index, :], 0) - fig.AddSubplot([rnn_out], title=u'rnn_outs/%d' % i) - fig.AddSubplot( - [softmax_input[:index + 1, :tgtlen, :]], title=u'softmax_input') - source_encs = tf.expand_dims( - tf.transpose(source_encs[:srclen, index, :]), 0) - fig.AddSubplot([source_encs], title=u'source_encs', xlabel=u'Encoder frame') - finalized_fig = fig.Finalize() - - if additional_atten_probs: - all_atten_probs = [('atten_probs', atten_probs)] + additional_atten_probs - num_atten_images = len(all_atten_probs) - atten_fig = plot.MatplotlibFigureSummary( - 'decoder_attention', figsize=(6, 3 * num_atten_images), max_outputs=1) - for key, probs in all_atten_probs: - PlotAttentionForOneExample( - probs, atten_fig, title=key, alignments=target_alignments) - atten_fig.Finalize() - return finalized_fig - - def _ComputeMetrics(self, - logits, - target_labels, - target_weights, - target_probs=None): - """Compute loss and misc metrics. - - Args: - logits: Tensor of shape [batch, time, num_classes]. - target_labels: Tensor of shape [batch, time]. - target_weights: Tensor of shape [batch, time]. - target_probs: Tensor of shape [batch, time, num_classes]. - Returns: - A (metrics, per_sequence_loss) pair. - """ - p = self.params - target_weights_sum = tf.reduce_sum(target_weights) - # add 0.000001 to avoid divide-by-zero. - target_weights_sum_eps = target_weights_sum + 0.000001 - correct_preds = tf.cast( - tf.equal(tf.argmax(logits, 2, output_type=tf.int32), target_labels), - py_utils.FPropDtype(p)) - correct_next_preds = tf.reduce_sum(correct_preds * target_weights) - accuracy = tf.identity( - correct_next_preds / target_weights_sum_eps, - name='fraction_of_correct_next_step_preds') - # Pad zeros so that we can stack them. - per_example_loss = py_utils.SoftmaxCrossEntropyFocalLoss( - logits=logits, - label_ids=target_labels, - label_probs=target_probs, - alpha=p.focal_loss_alpha, - gamma=p.focal_loss_gamma) - - per_sequence_loss = tf.reduce_sum(per_example_loss * target_weights, 1) - per_token_avg_loss = ( - tf.reduce_sum(per_sequence_loss) / target_weights_sum_eps) - if p.token_normalized_per_seq_loss: - per_seq_length = tf.reduce_sum(target_weights, 1) - # +0.001 to avoid possible divide by 0. - per_sequence_loss /= (per_seq_length + 0.001) - if p.per_token_avg_loss: - loss = per_token_avg_loss - loss_weight = target_weights_sum - else: # per-sequence average loss - loss = tf.reduce_mean(per_sequence_loss) - loss_weight = tf.shape(per_sequence_loss)[0] - metrics = { - 'loss': (loss, loss_weight), - # add log_pplx for compatibility with the mt/decoder.py - 'log_pplx': (per_token_avg_loss, target_weights_sum), - 'token_normed_prob': (tf.exp(-per_token_avg_loss), target_weights_sum), - } - metrics['fraction_of_correct_next_step_preds'] = (accuracy, - target_weights_sum) - return metrics, per_sequence_loss - - def InitDecoder(self, theta, encoder_outputs, dec_bs): - decoder_step_zero_state, packed_src = self.DecoderStepZeroState( - theta, encoder_outputs, - tf.ones([dec_bs, 1], dtype=tf.int32) * self.params.target_sos_id, - dec_bs) - - return (decoder_step_zero_state.rnn_states, - decoder_step_zero_state.atten_context, - decoder_step_zero_state.atten_probs, - decoder_step_zero_state.atten_states, - decoder_step_zero_state.fusion_states, - decoder_step_zero_state.misc_states, packed_src) - - def _InitBeamSearchStateCallback(self, theta, encoder_outputs, - num_hyps_per_beam): - raise NotImplementedError('_InitBeamSearchStateCallback') - - def _PreBeamSearchStepCallback(self, theta, encoder_outputs, step_ids, states, - num_hyps_per_beam): - raise NotImplementedError('_PreBeamSearchStepCallback') - - def _PostBeamSearchStepCallback(self, theta, encoder_outputs, new_step_ids, - states): - raise NotImplementedError('_PostBeamSearchStepCallback') - - def ComputeLoss(self, theta, predictions, targets): - """Computes loss metrics and per-sequence losses. - - Args: - theta: A NestedMap object containing weights' values of this - layer and its children layers. - predictions: A NestedMap containing logits (and possibly other fields). - targets: A dict of string to tensors representing the targets one is - trying to predict. Each tensor in targets is of shape [batch, time]. - - Returns: - (metrics, per_sequence_loss), where metrics is a dictionary containing - metrics for the xent loss and prediction accuracy. per_sequence is a - dictionary containing 'loss', a (-log(p)) vector of size [bs]. - """ - p = self.params - with tf.name_scope(p.name): - if 'probs' in targets: - target_probs = targets.probs - elif p.label_smoothing is not None: - target_probs = self.smoother.FProp(theta.smoother, targets.paddings, - targets.labels, targets.ids) - else: - target_probs = None - merged_metrics = {} - merged_per_sequence_loss = 0. - - def AddToMetric(acc, scale, metric): - assert len(acc) == 2 - assert len(metric) == 2 - return (acc[0] + scale * tf.cast(metric[0], py_utils.FPropDtype(p)), - acc[1] + scale * tf.cast(metric[1], py_utils.FPropDtype(p))) - - for logit_name, loss_weight in p.logit_types.items(): - metrics, per_sequence_loss = self._ComputeMetrics( - getattr(predictions, logit_name), targets.labels, targets.weights, - target_probs) - for k, v in metrics.items(): - tf.logging.info('Merging metric %s: %s', k, v) - merged_metrics[k + '/' + logit_name] = v - if k not in merged_metrics: - merged_metrics[k] = (tf.zeros( - shape=[], dtype=py_utils.FPropDtype(p)), - tf.zeros( - shape=[], dtype=py_utils.FPropDtype(p))) - merged_metrics[k] = AddToMetric(merged_metrics[k], loss_weight, v) - merged_per_sequence_loss += loss_weight * per_sequence_loss - return merged_metrics, {'loss': merged_per_sequence_loss} - - def CreateTargetInfoMisc(self, targets): - """Return a NestedMap corresponding to the 'misc' field in TargetInfo.""" - if 'fst_bias_probs' in targets: - return py_utils.NestedMap({ - 'fst_bias_probs': targets.fst_bias_probs, - }) - else: - return py_utils.NestedMap() - - def ComputePredictions(self, theta, encoder_outputs, targets): - """Computes logits. - - Args: - theta: A NestedMap object containing weights values of this layer and its - child layers. - encoder_outputs: a NestedMap computed by encoder. - targets: A dict of string to tensors representing the targets one is - trying to predict. Each tensor in targets is of shape [batch, time]. - - Returns: - A NestedMap object containing logit tensors as values, each of shape - [target_batch, max_target_length, vocab_size]. One of the keys must be - 'logits'. - """ - assert getattr(encoder_outputs, 'src_segment_id', None) is None - p = self.params - self.contextualizer.SetContextMap(targets, theta.contextualizer) - if 'weights' not in targets and 'paddings' in targets: - targets.weights = 1.0 - targets.paddings - if p.use_while_loop_based_unrolling: - predictions = self.ComputePredictionsDynamic(theta, encoder_outputs, - targets) - else: - predictions = self.ComputePredictionsFunctional(theta, encoder_outputs, - targets) - encoder_paddings = self._GetEncoderPaddings(encoder_outputs) - if isinstance(encoder_paddings, tf.Tensor): - # source_padding is of shape [time, batch]. Compute source_enc_len, which - # is used for computing attention loss. - predictions.source_enc_len = tf.reduce_sum(1 - encoder_paddings, axis=0) - if 'paddings' in targets: - source_batch = py_utils.GetShape(encoder_paddings)[1] - target_batch = py_utils.GetShape(targets.paddings)[0] - multiplier = target_batch // source_batch - source_len = py_utils.RepeatDim( - predictions.source_enc_len, multiplier, axis=0) - target_len = tf.reduce_sum(1 - targets.paddings, axis=1) - target_source_length_ratio = target_len / tf.maximum(source_len, 1.0) - summary_utils.scalar('avg_target_source_length_ratio', - tf.reduce_mean(target_source_length_ratio)) - return predictions - - def _GetInitialSeqStateTensorArrays(self, max_seq_length, - decoder_step_state_zero_fusion_flat, - decoder_step_state_zero_misc_flat): - """Get intitial tensor arrays for ComputePredictionsDynamic.""" - p = self.params - # TensorArrays for sequence outputs. - return AsrDecoder.SequenceOutTensorArrays( - rnn_outs=[ - _NewTensorArray( - name='rnn%d_outs' % i, - max_seq_length=max_seq_length, - dtype=py_utils.FPropDtype(p)) for i in range(p.rnn_layers) - ], - step_outs=_NewTensorArray( - name='step_outs', - max_seq_length=max_seq_length, - dtype=py_utils.FPropDtype(p)), - atten_probs=_NewTensorArray( - name='atten_probs', - max_seq_length=max_seq_length, - dtype=py_utils.FPropDtype(p)), - logits=_NewTensorArray( - name='logits', - max_seq_length=max_seq_length, - dtype=py_utils.FPropDtype(p)), - fusion=[ - _NewTensorArray( - name='fusion_states%d' % i, - max_seq_length=max_seq_length, - dtype=decoder_step_state_zero_fusion_flat[i].dtype) - for i in range(len(decoder_step_state_zero_fusion_flat)) - ], - misc=[ - _NewTensorArray( - name='misc_states%d' % i, - max_seq_length=max_seq_length, - dtype=decoder_step_state_zero_misc_flat[i].dtype) - for i in range(len(decoder_step_state_zero_misc_flat)) - ]) - - def _GetNewAttenProbs(self, seq_out_tas, time, decoder_step_state): - """Update atten probs for a timestep and return the updated tensor array.""" - return seq_out_tas.atten_probs.write(time, decoder_step_state.atten_probs) - - def _UpdateSequenceOutTensorArrays(self, decoder_step_state, time, step_outs, - seq_out_tas): - """Update SequenceOutTensorArrays at each time step.""" - new_rnn_outs = [] - assert len(seq_out_tas.rnn_outs) == len(decoder_step_state.rnn_states) - for i in range(len(seq_out_tas.rnn_outs)): - new_rnn_outs.append(seq_out_tas.rnn_outs[i].write( - time, decoder_step_state.rnn_states[i].m)) - new_logits_ta = seq_out_tas.logits.write(time, decoder_step_state.logits) - new_step_outs_ta = seq_out_tas.step_outs.write(time, step_outs) - new_atten_probs_ta = self._GetNewAttenProbs(seq_out_tas, time, - decoder_step_state) - new_seq_outs_fusion_states = [] - new_fusion_states_flat = decoder_step_state.fusion_states.Flatten() - for i in range(len(new_fusion_states_flat)): - new_seq_outs_fusion_states.append(seq_out_tas.fusion[i].write( - time, new_fusion_states_flat[i])) - new_seq_outs_misc_states = [] - new_misc_states_flat = decoder_step_state.misc_states.Flatten() - for i in range(len(new_misc_states_flat)): - new_seq_outs_misc_states.append(seq_out_tas.misc[i].write( - time, new_misc_states_flat[i])) - - return AsrDecoder.SequenceOutTensorArrays( - rnn_outs=new_rnn_outs, - step_outs=new_step_outs_ta, - atten_probs=new_atten_probs_ta, - logits=new_logits_ta, - fusion=new_seq_outs_fusion_states, - misc=new_seq_outs_misc_states) - - def _GetAttenProbsFromSequenceOutTensorArrays(self, atten_probs): - return tf.transpose(atten_probs.stack(), [1, 0, 2]) - - def _GetPredictionFromSequenceOutTensorArrays(self, seq_out_tas): - return py_utils.NestedMap( - # softmax_input is of shape [time, batch, dim] for compatibility. - softmax_input=seq_out_tas.step_outs.stack(), - # logits is of shape [batch, time, dim]. - logits=tf.transpose(seq_out_tas.logits.stack(), [1, 0, 2]), - attention=py_utils.NestedMap( - probs=self._GetAttenProbsFromSequenceOutTensorArrays( - seq_out_tas.atten_probs))) - - def _GetInitialTargetInfo(self, targets, max_seq_length, target_embs): - return AsrDecoderBase.TargetInfo( - id=_ToTensorArray( - 'target_ids_ta', - tf.transpose(targets.ids), - max_seq_length, - clear_after_read=False), - label=_ToTensorArray( - 'target_labels_ta', - tf.transpose(targets.labels), - max_seq_length, - clear_after_read=False), - weight=_ToTensorArray('target_weights_ta', - tf.transpose(targets.weights), max_seq_length), - emb=_ToTensorArray('target_embs_ta', tf.transpose( - target_embs, [1, 0, 2]), max_seq_length), - padding=_ToTensorArray( - 'target_paddings_ta', - tf.expand_dims(tf.transpose(targets.paddings), -1), max_seq_length), - misc=self.CreateTargetInfoMisc(targets), - ) - - def ComputePredictionsDynamic(self, theta, encoder_outputs, targets): - p = self.params - with tf.name_scope(p.name): - # Create TensorArrays corresponding to the targets to be used for - # training. - dec_bs = tf.shape(targets.ids)[0] - max_seq_length = tf.shape(targets.ids)[1] - - target_embs = self.emb.EmbLookup(theta.emb, tf.reshape(targets.ids, [-1])) - target_embs = tf.reshape(target_embs, [dec_bs, max_seq_length, p.emb_dim]) - target_embs = self._ApplyDropout(theta, target_embs) - target_info_tas = self._GetInitialTargetInfo(targets, max_seq_length, - target_embs) - - # Initialize all loop variables. - time = tf.constant(0, tf.int32) - # Decoder state. - decoder_step_state_zero, packed_src = self.DecoderStepZeroState( - theta, encoder_outputs, targets.ids, dec_bs) - decoder_step_state_zero_fusion_flat = ( - decoder_step_state_zero.fusion_states.Flatten()) - decoder_step_state_zero_misc_flat = ( - decoder_step_state_zero.misc_states.Flatten()) - - # TensorArrays for sequence outputs. - seq_out_tas = self._GetInitialSeqStateTensorArrays( - max_seq_length, decoder_step_state_zero_fusion_flat, - decoder_step_state_zero_misc_flat) - - def _LoopContinue(time, decoder_step_state, target_info_tas, seq_out_tas): - del decoder_step_state, target_info_tas, seq_out_tas - return time < max_seq_length - - def _LoopBody(time, old_decoder_step_state, target_info_tas, seq_out_tas): - """Computes decoder outputs and updates decoder_step_state.""" - cur_target_info = self.TargetsToBeFedAtCurrentDecodeStep( - time, theta, old_decoder_step_state, target_info_tas, seq_out_tas) - - step_outs, decoder_step_state = self.SingleDecodeStep( - theta, packed_src, cur_target_info, old_decoder_step_state) - - step_outs, decoder_step_state.fusion_states = self.fusion.FProp( - theta.fusion, old_decoder_step_state.fusion_states, step_outs, - cur_target_info.id, cur_target_info.padding) - - # Compute logits. - xent_loss = self.softmax.FProp( - theta.softmax, [step_outs], - class_weights=cur_target_info.weight, - class_ids=cur_target_info.label) - - decoder_step_state = self.PostStepDecoderStateUpdate( - decoder_step_state, xent_loss.logits) - - decoder_step_state.logits = self.fusion.ComputeLogitsWithLM( - decoder_step_state.fusion_states, decoder_step_state.logits) - - # Update SequenceOutTensorArrays. - new_seq_out_tas = self._UpdateSequenceOutTensorArrays( - decoder_step_state, time, step_outs, seq_out_tas) - del decoder_step_state.logits - return (time + 1, decoder_step_state, target_info_tas, new_seq_out_tas) - - loop_vars = time, decoder_step_state_zero, target_info_tas, seq_out_tas - # NOTE(skyewm): this could be more specific, but for now don't verify - # while_loop input/output shapes at all. - shape_invariants = tf.nest.map_structure(lambda t: tf.TensorShape(None), - loop_vars) - - (time, _, target_info_tas, seq_out_tas) = tf.while_loop( - _LoopContinue, - _LoopBody, - loop_vars=loop_vars, - shape_invariants=shape_invariants, - parallel_iterations=p.parallel_iterations, - swap_memory=False) - - softmax_input = seq_out_tas.step_outs.stack() - softmax_input = tf.transpose(softmax_input, [1, 0, 2]) - self._AddDecoderActivationsSummary(encoder_outputs, targets, - seq_out_tas.atten_probs, - seq_out_tas.rnn_outs, softmax_input) - self.AddAdditionalDecoderSummaries(encoder_outputs, targets, seq_out_tas, - softmax_input) - return self._GetPredictionFromSequenceOutTensorArrays(seq_out_tas) - - def ComputePredictionsFunctional(self, theta, encoder_outputs, targets): - p = self.params - # Currently, scheduled sampling is not supported. - assert p.min_ground_truth_prob == 1.0 - with tf.name_scope(p.name): - dec_bs = tf.shape(targets.ids)[0] - - # Decoder state. - state0, packed_src = self.DecoderStepZeroState(theta, encoder_outputs, - targets.ids, dec_bs) - - atten_context_dim = self._GetAttenContextDim() - rnn_output_dim = self.rnn_cell[-1].params.num_output_nodes - out_dim = rnn_output_dim + atten_context_dim - state0.step_outs = py_utils.Zeros([dec_bs, out_dim], - dtype=py_utils.FPropDtype(p)) - target_embs = self.emb.EmbLookup(theta.emb, targets.ids) - target_embs = self._ApplyDropout(theta, target_embs) - inputs = py_utils.NestedMap( - id=tf.transpose(targets.ids), - label=tf.transpose(targets.labels), - weight=tf.transpose(targets.weights), - emb=tf.transpose(target_embs, [1, 0, 2]), - padding=tf.expand_dims(tf.transpose(targets.paddings), -1), - misc=self.CreateTargetInfoMisc(targets), - ) - - # If the theta in the recurrent loop contains fusion related variables, - # it will allocate a large amount of memory even though it is not being - # used and exceed current TPU HBM limit. Thus remove fusion theta from - # the recurrent loop, and performs fusion outside the recurrent loop. - theta_no_fusion = theta.copy() - del theta_no_fusion.fusion - recurrent_theta = py_utils.NestedMap( - theta=theta_no_fusion, packed_src=packed_src) - state0_no_fusion = state0.copy() - del state0_no_fusion.fusion_states - - def RnnStep(recurrent_theta, state0, inputs): - """Computes one rnn step.""" - with tf.name_scope('single_decode_step'): - step_outs, state1 = self.SingleDecodeStep( - recurrent_theta.theta, - recurrent_theta.packed_src, - inputs, - state0, - use_deterministic_random=True) - state1.step_outs = step_outs - # TODO(syzhang, tsainath): Add SS into Functional Decoder, which - # requires computing softmax logits. - state1 = self.PostStepDecoderStateUpdate(state1, inputs.label) - return state1, py_utils.NestedMap() - - accumulated_states, _ = recurrent.Recurrent( - recurrent_theta, state0_no_fusion, inputs, RnnStep) - - if not p.softmax_uses_attention: - step_out, _ = tf.split( - accumulated_states.step_outs, [rnn_output_dim, atten_context_dim], - axis=-1) - else: - step_out = accumulated_states.step_outs - softmax_input, state0.fusion_states = self.fusion.FProp( - theta.fusion, state0.fusion_states, step_out, inputs.id, - inputs.padding, inputs.misc) - # TODO(syzhang): understand why we have to construct softmax outside the - # recurrent loop; otherwise, the BProp numbers don't match. - seq_logits = self._ComputeLogits(theta, softmax_input) - # TODO(syzhang): supports AddAdditionalDecoderSummaries(). - atten_states = accumulated_states.atten_states - if isinstance(atten_states, py_utils.NestedMap): - additional_atten_probs = sorted( - [(name, tensor) - for name, tensor in atten_states.FlattenItems() - if name.endswith('probs')]) - else: - additional_atten_probs = [] - rnn_outs = [ - cell.GetOutput(accumulated_states.rnn_states[i]) - for i, cell in enumerate(self.rnn_cell) - ] - self._AddDecoderActivationsSummary( - encoder_outputs, - targets, - accumulated_states.atten_probs, - rnn_outs, - softmax_input, - additional_atten_probs=additional_atten_probs, - target_alignments=getattr(targets, 'alignments', None)) - # seq_logits: [time, batch, num_classes]. - adjusted_logits = self.fusion.ComputeLogitsWithLM(state0.fusion_states, - seq_logits) - predictions = py_utils.NestedMap( - # Transpose to [batch, time, num_classes]. - logits_without_bias=tf.transpose(seq_logits, [1, 0, 2]), - logits=tf.transpose(adjusted_logits, [1, 0, 2]), - # softmax_input is of shape [time, batch, dim] for compatibility. - softmax_input=softmax_input) - attention_map = py_utils.NestedMap(probs=accumulated_states.atten_probs) - for k, v in additional_atten_probs: - attention_map[k] = v - # Transpose attention probs from [target_length, batch, source_length] to - # [batch, target_length, source_length]. - predictions.attention = attention_map.Transform( - lambda x: tf.transpose(x, [1, 0, 2])) - return predictions - - def _ComputeLogits(self, theta, softmax_input): - if isinstance(self.softmax, layers.ConvSoftmax): - return self.softmax.Logits(theta.softmax, softmax_input) - else: - # SoftmaxLayer.Logits() may not support 3-D inputs. So use FProp() with - # fake labels. - xent_loss = self.softmax.FProp( - theta.softmax, [softmax_input], - class_weights=tf.ones( - shape=tf.shape(softmax_input)[:-1], dtype=softmax_input.dtype), - class_ids=tf.ones(shape=tf.shape(softmax_input)[:-1], dtype=tf.int32)) - return xent_loss.logits - - def SingleDecodeStep(self, - theta, - packed_src, - cur_target_info, - decoder_step_state, - per_step_src_padding=None, - use_deterministic_random=False): - """Computes one 'step' of computation for the decoder. - - Must be implemented by sub-classes. Residual connections must also be taken - care of in sub-classes. - - Args: - theta: A NestedMap object containing weights' values of this - layer and its children layers. - packed_src: A NestedMap to represent the packed source tensors generated - by the attention model. - cur_target_info: TargetInfo namedtuple, which represents the targets - which represents information about the target at this step. It is up - to the various sub-classes to determine how to process the current - target. - decoder_step_state: DecoderStepState which encapsulates the state of the - decoder before computing outputs at the current step. - per_step_src_padding: Optional padding to be applied to the source_encs - which overrides the default padding in source_paddings. Used, for - example, by the Neural Transducer (NT) decoder. - use_deterministic_random: whether to use deterministic random numbers when - needed. Must be set to True if called from functional recurrent. - - Returns: - A tuple (step_out, new_decoder_state) which represent the outputs of the - decoder (usually logits), and the new decoder state after processing the - current step. - """ - # TODO(syzhang): unify the API to always pass in packed_src. - raise NotImplementedError('Must be implemented by sub-classes.') - - def MiscZeroState(self, theta, encoder_outputs, target_ids, bs): - """Returns initial state for other miscellaneous states, if any.""" - misc_zero_state = py_utils.NestedMap() - p = self.params - if self._max_label_prob > 0: - misc_zero_state.prev_predicted_ids = tf.reshape(target_ids[:, 0], [bs]) - step = tf.cast(theta.global_step, tf.float32) - sampling_p = (step - p.prob_decay_start_step) / self._decay_interval - groundtruth_p = 1 - (self._max_label_prob * sampling_p) - groundtruth_p = tf.maximum(groundtruth_p, p.min_ground_truth_prob) - groundtruth_p = tf.minimum(groundtruth_p, 1.0) - summary_utils.scalar('ground_truth_sampling_probability', groundtruth_p) - misc_zero_state.groundtruth_p = groundtruth_p - if p.adapter_task_id_field: - # encoder_outputs.encoded: [time, batch, dim] - source_bs = py_utils.GetShape(encoder_outputs.encoded, 2)[1] - # Only task_ids of shape [batch] is supported - task_ids = tf.reshape( - encoder_outputs.Get(p.adapter_task_id_field), [source_bs]) - multiplier = bs // source_bs - task_ids = tf.tile(task_ids, [multiplier]) - misc_zero_state.Set(p.adapter_task_id_field, task_ids) - return misc_zero_state - - def TargetsToBeFedAtCurrentDecodeStep(self, time, theta, decoder_step_state, - target_info_tas, seq_out_tas): - del seq_out_tas - - target_id = target_info_tas.id.read(time) - label = target_info_tas.label.read(time) - weight = tf.squeeze(target_info_tas.weight.read(time)) - emb = target_info_tas.emb.read(time) - padding = target_info_tas.padding.read(time) - misc = py_utils.NestedMap() - - # Use different id and embedding for scheduled sampling. - if self._max_label_prob > 0: - dec_bs = tf.shape(decoder_step_state.misc_states.prev_predicted_ids)[0] - pick_groundtruth = tf.less( - tf.random.uniform([dec_bs], seed=self.params.random_seed), - decoder_step_state.misc_states.groundtruth_p) - emb = tf.where( - pick_groundtruth, target_info_tas.emb.read(time), - self.emb.EmbLookup( - theta.emb, - tf.stop_gradient( - decoder_step_state.misc_states.prev_predicted_ids))) - target_id = tf.where(pick_groundtruth, target_info_tas.id.read(time), - decoder_step_state.misc_states.prev_predicted_ids) - return AsrDecoderBase.TargetInfo( - id=target_id, - label=label, - weight=weight, - emb=emb, - padding=padding, - misc=misc) - - def PostStepDecoderStateUpdate(self, decoder_step_state, logits=None): - """Update decoder states and logits after SingleDecodeStep. - - Args: - decoder_step_state: A NestedMap object which encapsulates decoder states. - logits: a tensor, predicted logits. - - Returns: - decoder_step_state. - - Raises: - ValueError: if scheduled sampling is used for functional unrolling or - if logits is None for while loop based unrolling. - """ - if not self.params.use_while_loop_based_unrolling: - if self.params.min_ground_truth_prob < 1.0: - raise ValueError('SS is not yet supported') - else: - if logits is None: - raise ValueError('logits cannot be None') - decoder_step_state.logits = logits - - if self._max_label_prob > 0: - bs = tf.shape(logits)[0] - # log_probs: [bs, num_classes] - log_probs = tf.nn.log_softmax(logits) - # log_prob_sample: [bs, 1] - log_prob_sample = tf.multinomial( - log_probs, 1, seed=self.params.random_seed) - # pred_ids: [bs] - pred_ids = tf.reshape(tf.cast(log_prob_sample, tf.int32), [bs]) - decoder_step_state.misc_states.prev_predicted_ids = pred_ids - return decoder_step_state - - -class AsrDecoder(AsrDecoderBase): - """Step-by-step decoder with LM fusion.""" - - @classmethod - def Params(cls): - p = super().Params() - return p - - def AddAdditionalDecoderSummaries(self, encoder_outputs, targets, seq_out_tas, - softmax_input): - """Add summaries not covered by the default activations summaries. - - Args: - encoder_outputs: a NestedMap computed by encoder. - targets: a NestedMap containing target info. - seq_out_tas: a SequenceOutTensorArrays. - softmax_input: a tensor of shape [batch, time, vocab_size]. - """ - if cluster_factory.Current().add_summary: - self.fusion.AddAdditionalDecoderSummaries( - encoder_outputs.encoded, encoder_outputs.padding, targets, - seq_out_tas, softmax_input) - - def _ComputeAttention(self, - theta, - rnn_out, - packed_src, - attention_state, - per_step_src_padding=None, - query_segment_id=None): - """Runs attention and computes context vector. - - Can be overridden by a child class if attention is computed differently. - - Args: - theta: A NestedMap object containing weights for the attention layers. - Expects a member named 'atten'. - rnn_out: A Tensor of shape [batch_size, query_dim]; output of the - first layer of decoder RNN, which is the query vector used for - attention. - packed_src: A NestedMap returned by self.atten.InitForSourcePacked. - attention_state: The attention state computed at the previous timestep. - Varies with the type of attention, but is usually a Tensor or a - NestedMap of Tensors of shape [batch_size, ]. - per_step_src_padding: Source sequence padding to apply at this step. - query_segment_id: a tensor of shape [batch_size]. - - Returns: - A tuple of 3 tensors: - - - The attention context vector: shaped [batch_size, context_dim]. - - The attention probability vector: shaped [batch_size, seq_len] - - The attention state: A Tensor or a NestedMap of Tensors of shape - [batch_size, ]. - """ - return self.atten.ComputeContextVectorWithSource( - theta.atten, - packed_src, - rnn_out, - attention_state=attention_state, - per_step_source_padding=per_step_src_padding, - query_segment_id=query_segment_id) - - def SingleDecodeStep(self, - theta, - packed_src, - cur_target_info, - decoder_step_state, - per_step_src_padding=None, - use_deterministic_random=False): - """Decode one step. - - Note that the implementation of attention here follows the model in - https://arxiv.org/pdf/1609.08144.pdf, detailed more in - https://arxiv.org/pdf/1703.08581.pdf. - - Args: - theta: A NestedMap object containing weights' values of this - layer and its children layers. - packed_src: A NestedMap to represent the packed source tensors generated - by the attention model. - cur_target_info: TargetInfo namedtuple, which represents the targets - which represents information about the target at this step. It is up - to the various sub-classes to determine how to process the current - target. - decoder_step_state: DecoderStepState which encapsulates the state of the - decoder before computing outputs at the current step. - per_step_src_padding: Optional padding to be applied to the source_encs - which overrides the default padding in source_paddings. Used, for - example, by the Neural Transducer (NT) decoder. - use_deterministic_random: whether to use deterministic random numbers when - needed. Must be set to True if called from functional recurrent. - - Returns: - A tuple (step_out, new_decoder_state) which represent the outputs of the - decoder (usually logits), and the new decoder state after processing the - current step. - """ - p = self.params - misc_states = decoder_step_state.misc_states - new_rnn_states = [] - new_rnn_states_0, _ = self.rnn_cell[0].FProp( - theta.rnn_cell[0], decoder_step_state.rnn_states[0], - py_utils.NestedMap( - act=[cur_target_info.emb, decoder_step_state.atten_context], - padding=cur_target_info.padding)) - new_rnn_states.append(new_rnn_states_0) - rnn_out = self.rnn_cell[0].GetOutput(new_rnn_states_0) - if p.adapter_task_id_field: - # rnn_out is [batch, dim], adapter layers requires [time, batch, dim] - rnn_out = self.adapters[0].FProp(theta.adapters[0], - tf.expand_dims(rnn_out, [0]), - misc_states.Get(p.adapter_task_id_field)) - rnn_out = tf.squeeze(rnn_out, [0]) - - (new_atten_context, new_atten_probs, - new_atten_states) = self._ComputeAttention( - theta, - rnn_out, - packed_src, - decoder_step_state.atten_states, - per_step_src_padding=per_step_src_padding) - # Here the attention context is being updated according to the - # contextualizer (the default contextualizer is a no-op). - new_atten_context = self.contextualizer.QueryAttention( - theta.contextualizer, rnn_out, misc_states, new_atten_context, - packed_src) - for i, cell in enumerate(self.rnn_cell[1:], 1): - new_rnn_states_i, _ = cell.FProp( - theta.rnn_cell[i], decoder_step_state.rnn_states[i], - py_utils.NestedMap( - act=[rnn_out, new_atten_context], - padding=cur_target_info.padding)) - new_rnn_states.append(new_rnn_states_i) - new_rnn_out = cell.GetOutput(new_rnn_states_i) - if p.adapter_task_id_field: - new_rnn_out = self.adapters[i].FProp( - theta.adapters[i], tf.expand_dims(new_rnn_out, [0]), - misc_states.Get(p.adapter_task_id_field)) - new_rnn_out = tf.squeeze(new_rnn_out, [0]) - new_rnn_out = self._ApplyDropout( - theta, - new_rnn_out, - deterministic=use_deterministic_random, - extra_seed=i * 1000) - if i + 1 >= self.params.residual_start > 0: - rnn_out += new_rnn_out - else: - rnn_out = new_rnn_out - - step_out = tf.concat([rnn_out, new_atten_context], 1) - - return step_out, py_utils.NestedMap( - rnn_states=new_rnn_states, - atten_context=new_atten_context, - atten_probs=new_atten_probs, - atten_states=new_atten_states, - misc_states=misc_states) - - def _GetNumHypsForBeamSearch(self, source_encs, num_hyps_per_beam): - """Returns number of hypothesis times batch_size. - - This function can be overridden by a child class if the total number of - hyps are to be computed in a different way, e.g., when the format of inputs - change. - Args: - source_encs: A Tensor of [time, batch, dim] with source encodings. - num_hyps_per_beam: Int, the number of hypothesis per example in the beam. - Returns: - A Tensor with value batch * num_hyps_per_beam. - """ - return tf.shape(source_encs)[1] * num_hyps_per_beam - - def _PostProcessAttenProbsForBeamSearch(self, atten_probs): - """Returns the attention probabilities after optional post processing. - - This is a noop for the base class. But this function can be overridden - by a child class, e.g., when the format of probabilities change. - Args: - atten_probs: A Tensor of [batch, source_len] dimension with atten probs. - Returns: - A Tensor with processed atten_probs. The same as input in this case. - """ - return atten_probs - - def _InitBeamSearchStateCallback(self, theta, encoder_outputs, - num_hyps_per_beam): - p = self.params - num_hyps = self._GetNumHypsForBeamSearch(encoder_outputs.encoded, - num_hyps_per_beam) - (rnn_states, atten_context, atten_probs, atten_states, - fusion_states, misc_states, packed_src) = self.InitDecoder( - theta, encoder_outputs, num_hyps) - # Throw away packed_src. We re-compute it in _PreBeamSearchStepCallback - # because we cannot pass 'packed_src' through 'states'. beam_search_helper - # assumes that all Tensors in 'states' have 'target_batch' as the first - # dimension. - del packed_src - atten_probs = self._PostProcessAttenProbsForBeamSearch(atten_probs) - all_atten_states = py_utils.NestedMap({ - 'atten_context': atten_context, - 'atten_probs': atten_probs, - 'atten_states': atten_states, - }) - - initial_results = py_utils.NestedMap({ - 'log_probs': - tf.nn.log_softmax( - tf.zeros([num_hyps, p.softmax.num_classes], - dtype=py_utils.FPropDtype(p))), - 'atten_probs': - atten_probs, - }) - other_states = py_utils.NestedMap({ - 'time_step': tf.constant(0), - 'rnn_states': rnn_states, - 'all_atten_states': all_atten_states, - 'fusion_states': fusion_states, - 'misc_states': misc_states, - }) - return initial_results, other_states - - def _PreBeamSearchStepCallback(self, theta, encoder_outputs, step_ids, states, - num_hyps_per_beam): - p = self.params - step_paddings = tf.zeros(tf.shape(step_ids), dtype=p.dtype) - embs = self.emb.EmbLookup(theta.emb, tf.reshape(step_ids, [-1])) - prev_rnn_states = states.rnn_states - prev_atten_states = states.all_atten_states.atten_states - prev_atten_context = states.all_atten_states.atten_context - prev_atten_probs = states.all_atten_states.atten_probs - prev_fusion_states = states.fusion_states - prev_misc_states = states.misc_states - - prev_decoder_step_state = py_utils.NestedMap( - rnn_states=prev_rnn_states, - atten_context=prev_atten_context, - atten_probs=prev_atten_probs, - atten_states=prev_atten_states, - misc_states=prev_misc_states) - # TODO(prabhavalkar): Must handle CreateMiscTargetInfo during beam search - # eval. - cur_target_info = AsrDecoderBase.TargetInfo( - id=tf.reshape(step_ids, [-1]), - label=None, - weight=None, - emb=embs, - padding=step_paddings, - misc=py_utils.NestedMap()) - - packed_src = self._InitAttention(theta, encoder_outputs) - step_out, new_decoder_step_state = self.SingleDecodeStep( - theta, - packed_src, - cur_target_info=cur_target_info, - decoder_step_state=prev_decoder_step_state) - (atten_context, atten_probs, rnn_states, atten_states, - misc_states) = (new_decoder_step_state.atten_context, - new_decoder_step_state.atten_probs, - new_decoder_step_state.rnn_states, - new_decoder_step_state.atten_states, - new_decoder_step_state.misc_states) - - if p.softmax_uses_attention: - # [batch, dims] - softmax_input = step_out - else: - # Strip the attention context from the last dimension of softmax_input. - # TODO(prabhavalkar): This currently assumes that the context is appended - # to the end, see tf.concat in - # AsrDecoderBase.ComputePredictionsFunctional().RnnStep(). Refactor the - # code so as to remove this assumption. - atten_context_dim = self._GetAttenContextDim() - rnn_output_dim = self.rnn_cell[-1].params.num_output_nodes - softmax_input, _ = tf.split( - step_out, [rnn_output_dim, atten_context_dim], axis=-1) - - softmax_input, fusion_states = self.fusion.FProp( - theta.fusion, prev_fusion_states, softmax_input, cur_target_info.id, - cur_target_info.padding) - - logits = self._ComputeLogits(theta, softmax_input) - logits = self.fusion.ComputeLogitsWithLM( - fusion_states, logits, is_eval=True) - if p.use_unnormalized_logits_as_log_probs: - log_probs = logits - else: - log_probs = tf.nn.log_softmax(logits) - - atten_probs = self._PostProcessAttenProbsForBeamSearch(atten_probs) - bs_results = py_utils.NestedMap({ - 'atten_probs': atten_probs, - 'log_probs': log_probs, - }) - all_atten_states = py_utils.NestedMap({ - 'atten_context': atten_context, - 'atten_probs': atten_probs, - 'atten_states': atten_states, - }) - new_states = py_utils.NestedMap({ - 'time_step': states.time_step + 1, - 'rnn_states': rnn_states, - 'all_atten_states': all_atten_states, - 'fusion_states': fusion_states, - 'misc_states': misc_states, - }) - return bs_results, new_states - - def _PostBeamSearchStepCallback(self, theta, encoder_outputs, new_step_ids, - states): - del encoder_outputs, new_step_ids - return states diff --git a/lingvo/tasks/asr/decoder_test.py b/lingvo/tasks/asr/decoder_test.py deleted file mode 100644 index 9460a238..00000000 --- a/lingvo/tasks/asr/decoder_test.py +++ /dev/null @@ -1,765 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for speech decoder.""" - -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import layers as lingvo_layers -from lingvo.core import py_utils -from lingvo.core import symbolic -from lingvo.core import test_utils -from lingvo.core.ops.hyps_pb2 import Hypothesis -from lingvo.tasks.asr import decoder -import numpy as np - -from google.protobuf import text_format - -FLAGS = tf.flags.FLAGS - - -class DecoderTest(test_utils.TestCase): - - def _DecoderParams(self, - vn_config, - num_classes=32, - num_rnn_layers=1): - """Create a small decoder for testing.""" - p = decoder.AsrDecoder.Params() - p.random_seed = 12345 - - p.name = 'decoder' - uniform_init = py_utils.WeightInit.Uniform(0.1, seed=12345) - - # Set up embedding params. - p.emb.vocab_size = num_classes - p.emb.max_num_shards = 1 - p.emb.params_init = uniform_init - - # Set up decoder RNN layers. - p.rnn_layers = num_rnn_layers - rnn_params = p.rnn_cell_tpl - rnn_params.params_init = uniform_init - - # Set up attention. - p.attention.hidden_dim = 16 - p.attention.params_init = uniform_init - - # Set up final softmax layer. - p.softmax.num_classes = num_classes - p.softmax.params_init = uniform_init - - # Set up variational noise params. - p.vn = vn_config - p.vn.scale = tf.constant(0.1) - - p.target_seq_len = 5 - p.source_dim = 8 - p.emb_dim = 2 - p.rnn_cell_dim = 4 - - return p - - def _getDecoderFPropMetrics(self, params): - """Creates decoder from params and computes metrics with random inputs.""" - dec = params.Instantiate() - src_seq_len = 5 - src_enc = tf.random.normal([src_seq_len, 2, 8], - seed=982774838, - dtype=py_utils.FPropDtype(params)) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=py_utils.FPropDtype(params)) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - # shape=[4, 5] - target_ids = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 15], [5, 6, 7, 8], - [10, 5, 2, 5]], - dtype=tf.int32)) - # shape=[4, 5] - target_labels = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 13], - [5, 7, 8, 10], [10, 5, 2, 4]], - dtype=tf.int32)) - # shape=[4, 5] - target_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], - [1, 1, 1, 0]], - dtype=py_utils.FPropDtype(params))) - target_transcripts = tf.constant(['abcd', 'bcde', 'klmp', 'fghi', 'kfcf']) - target_weights = 1.0 - target_paddings - # ids/labels/weights/paddings are all in [batch, time] shape. - targets = py_utils.NestedMap({ - 'ids': target_ids, - 'labels': target_labels, - 'weights': target_weights, - 'paddings': target_paddings, - 'transcripts': target_transcripts, - }) - decoder_outputs = dec.FPropDefaultTheta(encoder_outputs, targets) - return decoder_outputs.metrics, decoder_outputs.per_sequence['loss'] - - def _testDecoderFPropHelper(self, params): - metrics, per_sequence_loss = self._getDecoderFPropMetrics(params) - return metrics['loss'], per_sequence_loss - - def _testDecoderFPropFloatHelper(self, - func_inline=False, - num_decoder_layers=1, - target_seq_len=5, - residual_start=0): - """Computes decoder from params and computes loss with random inputs.""" - cluster = cluster_factory.ForTestingWorker(add_summary=True) - config = tf.config_pb2.ConfigProto( - graph_options=tf.GraphOptions( - optimizer_options=tf.OptimizerOptions( - do_function_inlining=func_inline))) - with cluster, self.session(use_gpu=False, config=config): - tf.random.set_seed(8372749040) - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._DecoderParams(vn_config) - p.rnn_layers = num_decoder_layers - p.residual_start = residual_start - p.target_seq_len = target_seq_len - dec = p.Instantiate() - src_seq_len = 5 - src_enc = tf.random.normal([src_seq_len, 2, 8], seed=9283748) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float32) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - target_ids = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 15], - [5, 6, 7, 8], [10, 5, 2, 5]], - dtype=tf.int32)) - target_labels = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 13], - [5, 7, 8, 10], [10, 5, 2, 4]], - dtype=tf.int32)) - target_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], - [1, 1, 1, 1]], - dtype=tf.float32)) - target_transcripts = tf.constant(['abcd', 'bcde', 'klmp', 'fghi', 'kfcf']) - target_weights = 1.0 - target_paddings - targets = py_utils.NestedMap({ - 'ids': target_ids, - 'labels': target_labels, - 'weights': target_weights, - 'paddings': target_paddings, - 'transcripts': target_transcripts, - }) - metrics = dec.FPropDefaultTheta(encoder_outputs, targets).metrics - loss = metrics['loss'][0] - correct_predicts = metrics['fraction_of_correct_next_step_preds'][0] - summaries = tf.summary.merge(tf.get_collection(tf.GraphKeys.SUMMARIES)) - - self.evaluate(tf.global_variables_initializer()) - loss_v, _ = self.evaluate([loss, correct_predicts]) - - summaries.eval() - - return loss_v - - # Actual tests follow. - - def testDecoderConstruction(self): - """Test that decoder can be constructed from params.""" - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - _ = decoder.AsrDecoder(p) - - def testDecoderFProp(self): - """Create decoder with default params, and verify that FProp runs.""" - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - - metrics, per_sequence_loss = self._getDecoderFPropMetrics(params=p) - self.assertIn('fraction_of_correct_next_step_preds', metrics) - self.evaluate(tf.global_variables_initializer()) - metrics_val, per_sequence_loss_val = self.evaluate( - [metrics, per_sequence_loss]) - tf.logging.info('metrics=%s, per_sequence_loss=%s', metrics_val, - per_sequence_loss_val) - - self.assertEqual(metrics_val['loss'], metrics_val['log_pplx']) - # Target batch size is 4. Therefore, we should expect 4 here. - self.assertEqual(per_sequence_loss_val.shape, (4,)) - - def testDecoderFPropWithMeanSeqLoss(self): - """Create and fprop a decoder with different dims per layer.""" - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - p.token_normalized_per_seq_loss = True - p.per_token_avg_loss = False - - metrics, per_sequence_loss = self._getDecoderFPropMetrics(params=p) - self.evaluate(tf.global_variables_initializer()) - metrics_val, per_sequence_loss_val = self.evaluate( - [metrics, per_sequence_loss]) - tf.logging.info('metrics=%s, per_sequence_loss=%s', metrics_val, - per_sequence_loss_val) - - self.assertNotEqual(metrics_val['loss'][0], metrics_val['log_pplx'][0]) - self.assertAllClose(metrics_val['loss'], (3.484608, 4.0)) - self.assertAllClose(metrics_val['log_pplx'], (3.496482, 15.0)) - # Target batch size is 4. Therefore, we should expect 4 here. - self.assertEqual(per_sequence_loss_val.shape, (4,)) - - def testDecoderFPropWithProjection(self): - """Create decoder with projection layers, and verify that FProp runs.""" - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - rnn_cell_tpl = p.rnn_cell_tpl - p.rnn_cell_tpl = [ - rnn_cell_tpl.Copy().Set( - num_output_nodes=i + 2, num_hidden_nodes=i + 5) - for i in range(p.rnn_layers) - ] - p.rnn_cell_dim = -1 - p.rnn_cell_hidden_dim = -1 - - loss, per_sequence_loss = self._testDecoderFPropHelper(params=p) - self.evaluate(tf.global_variables_initializer()) - loss_val, per_sequence_loss_val = self.evaluate([loss, per_sequence_loss]) - - print('loss = ', loss_val, 'per sequence loss = ', per_sequence_loss_val) - # Target batch size is 4. Therefore, we should expect 4 here. - self.assertEqual(per_sequence_loss_val.shape, (4,)) - - def testDecoderFPropWithPerLayerDims(self): - """Create and fprop a decoder with different dims per layer.""" - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - p.rnn_cell_hidden_dim = 6 - - loss, per_sequence_loss = self._testDecoderFPropHelper(params=p) - self.evaluate(tf.global_variables_initializer()) - loss_val, per_sequence_loss_val = self.evaluate([loss, per_sequence_loss]) - - print('loss = ', loss_val, 'per sequence loss = ', per_sequence_loss_val) - # Target batch size is 4. Therefore, we should expect 4 here. - self.assertEqual(per_sequence_loss_val.shape, (4,)) - - def testDecoderFPropDtype(self): - """Create decoder with different fprop_type, and verify that FProp runs.""" - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - p.fprop_dtype = tf.float64 - - loss, per_sequence_loss = self._testDecoderFPropHelper(params=p) - self.evaluate(tf.global_variables_initializer()) - loss_val, per_sequence_loss_val = self.evaluate([loss, per_sequence_loss]) - - print('loss = ', loss_val, 'per sequence loss = ', per_sequence_loss_val) - # Target batch size is 4. Therefore, we should expect 4 here. - self.assertEqual(per_sequence_loss_val.shape, (4,)) - - def testDecoderFPropDeterministicAttentionDropout(self): - """Verify that attention dropout is deterministic given fixed seeds.""" - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - p = self._DecoderParams( - py_utils.VariationalNoiseParams(None, True, False, seed=1792)) - - p.use_while_loop_based_unrolling = False - p.attention.atten_dropout_prob = 0.5 - p.attention.atten_dropout_deterministic = True - - loss, per_sequence_loss = self._testDecoderFPropHelper(params=p) - global_step = py_utils.GetGlobalStep() - self.evaluate(tf.global_variables_initializer()) - loss_val, per_sequence_loss_val, global_steps_val = self.evaluate( - [loss, per_sequence_loss, global_step]) - - print('loss = ', loss_val, 'per sequence loss = ', per_sequence_loss_val) - self.assertAllClose([3.587372, 15.0], loss_val) - self.assertAllClose([14.171288, 9.965696, 10.221684, 19.451914], - per_sequence_loss_val) - self.assertEqual(0, global_steps_val) - - # Run another step to test global_step and time_step are incremented - # correctly. - self.evaluate(tf.assign_add(global_step, 1)) - loss_val, per_sequence_loss_val, global_steps_val = self.evaluate( - [loss, per_sequence_loss, global_step]) - - print('loss = ', loss_val, 'per sequence loss = ', per_sequence_loss_val) - self.assertAllClose([3.626164, 15.0], loss_val) - self.assertAllClose([14.70993, 10.572938, 10.516836, 18.592758], - per_sequence_loss_val) - self.assertEqual(1, global_steps_val) - - def testLabelSmoothing(self): - """Verify that loss computation with label smoothing is as expected..""" - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - - p = self._DecoderParams(vn_config=py_utils.VariationalNoiseParams(None)) - p.label_smoothing = lingvo_layers.LocalizedLabelSmoother.Params() - p.label_smoothing.offsets = [-2, -1, 1, 2] - p.label_smoothing.weights = [0.015, 0.035, 0.035, 0.015] - - loss, _ = self._testDecoderFPropHelper(params=p) - self.evaluate(tf.global_variables_initializer()) - loss_val = self.evaluate(loss[0]) - - print('loss = ', loss_val) - test_utils.CompareToGoldenSingleFloat(self, 3.471763, loss_val) - - def testDecoderFPropFloatNoInline(self): - actual_value = self._testDecoderFPropFloatHelper(func_inline=False) - test_utils.CompareToGoldenSingleFloat(self, 3.458980, actual_value) - - def testDecoderFPropFloatNoInlinePadTargetsToLongerLength(self): - actual_value = self._testDecoderFPropFloatHelper( - func_inline=False, target_seq_len=10) - test_utils.CompareToGoldenSingleFloat(self, 3.458980, actual_value) - - def testDecoderFPropFloatInline(self): - actual_value = self._testDecoderFPropFloatHelper(func_inline=True) - test_utils.CompareToGoldenSingleFloat(self, 3.458980, actual_value) - - def testDecoderFPropFloatNoInline2Layers(self): - actual_value = self._testDecoderFPropFloatHelper( - func_inline=False, num_decoder_layers=2) - test_utils.CompareToGoldenSingleFloat(self, 3.457761, actual_value) - - def testDecoderFPropFloatInline2Layers(self): - actual_value = self._testDecoderFPropFloatHelper( - func_inline=True, num_decoder_layers=2) - test_utils.CompareToGoldenSingleFloat(self, 3.457761, actual_value) - - def testDecoderFPropFloat2LayersResidual(self): - actual_value = self._testDecoderFPropFloatHelper( - num_decoder_layers=2, residual_start=2) - test_utils.CompareToGoldenSingleFloat(self, 3.458294, actual_value) - - def testDecoderFPropDouble(self): - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - np.random.seed(827374) - - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams(None, False, False)) - p.dtype = tf.float64 - - dec = decoder.AsrDecoder(p) - src_seq_len = 5 - src_enc = tf.constant( - np.random.uniform(size=(src_seq_len, 2, 8)), tf.float64) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float64) - target_ids = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 15], - [5, 6, 7, 8], [10, 5, 2, 5]], - dtype=tf.int32)) - target_labels = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 13], - [5, 7, 8, 10], [10, 5, 2, 4]], - dtype=tf.int32)) - target_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], - [1, 1, 1, 1]], - dtype=tf.float64)) - target_transcripts = tf.constant(['abcd', 'bcde', 'klmp', 'fghi', 'kfcf']) - target_weights = 1.0 - target_paddings - targets = py_utils.NestedMap({ - 'ids': target_ids, - 'labels': target_labels, - 'weights': target_weights, - 'paddings': target_paddings, - 'transcripts': target_transcripts, - }) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - metrics = dec.FPropDefaultTheta(encoder_outputs, targets).metrics - loss = metrics['loss'][0] - - self.evaluate(tf.global_variables_initializer()) - - test_utils.CompareToGoldenSingleFloat(self, 3.467679, loss.eval()) - # Second run to make sure the function is determistic. - test_utils.CompareToGoldenSingleFloat(self, 3.467679, loss.eval()) - - def _testDecoderFPropGradientCheckerHelper(self, func_inline=False): - config = tf.config_pb2.ConfigProto( - graph_options=tf.GraphOptions( - optimizer_options=tf.OptimizerOptions( - do_function_inlining=func_inline))) - with self.session(use_gpu=False, config=config) as sess: - tf.random.set_seed(8372749040) - np.random.seed(274854) - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._DecoderParams(vn_config) - p.dtype = tf.float64 - - dec = p.Instantiate() - src_seq_len = 5 - src_enc = tf.constant( - np.random.uniform(size=(src_seq_len, 2, 8)), tf.float64) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float64) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - target_ids = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 15], - [5, 6, 7, 8], [10, 5, 2, 5]], - dtype=tf.int32)) - target_labels = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 13], - [5, 7, 8, 10], [10, 5, 2, 4]], - dtype=tf.int32)) - target_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], - [1, 1, 1, 1]], - dtype=tf.float64)) - target_transcripts = tf.constant(['abcd', 'bcde', 'klmp', 'fghi', 'kfcf']) - target_weights = 1.0 - target_paddings - - targets = py_utils.NestedMap({ - 'ids': target_ids, - 'labels': target_labels, - 'weights': target_weights, - 'paddings': target_paddings, - 'transcripts': target_transcripts, - }) - metrics = dec.FPropDefaultTheta(encoder_outputs, targets).metrics - loss = metrics['loss'][0] - all_vars = tf.trainable_variables() - grads = tf.gradients(loss, all_vars) - - def DenseGrad(var, grad): - if isinstance(grad, tf.Tensor): - return grad - elif isinstance(grad, tf.IndexedSlices): - return tf.math.unsorted_segment_sum(grad.values, grad.indices, - tf.shape(var)[0]) - - dense_grads = [DenseGrad(x, y) for (x, y) in zip(all_vars, grads)] - - self.evaluate(tf.global_variables_initializer()) - - test_utils.CompareToGoldenSingleFloat(self, 3.458078, loss.eval()) - # Second run to make sure the function is determistic. - test_utils.CompareToGoldenSingleFloat(self, 3.458078, loss.eval()) - - symbolic_grads = [x.eval() for x in dense_grads if x is not None] - numerical_grads = [] - for v in all_vars: - numerical_grads.append(test_utils.ComputeNumericGradient(sess, loss, v)) - - for x, y in zip(symbolic_grads, numerical_grads): - self.assertAllClose(x, y) - - def testDecoderFPropGradientCheckerNoInline(self): - self._testDecoderFPropGradientCheckerHelper(func_inline=False) - - def testDecoderFPropGradientCheckerInline(self): - self._testDecoderFPropGradientCheckerHelper(func_inline=True) - - def _testDecoderBeamSearchDecodeHelperWithOutput(self, - params, - src_seq_len=None, - src_enc_padding=None): - config = tf.config_pb2.ConfigProto( - graph_options=tf.GraphOptions( - optimizer_options=tf.OptimizerOptions(do_function_inlining=False))) - p = params - with self.session(use_gpu=False, config=config), self.SetEval(True): - tf.random.set_seed(837274904) - np.random.seed(837575) - p.beam_search.num_hyps_per_beam = 4 - p.dtype = tf.float32 - p.target_seq_len = 5 - - dec = p.Instantiate() - if src_seq_len is None: - src_seq_len = 5 - src_enc = tf.constant( - np.random.uniform(size=(src_seq_len, 2, 8)), tf.float32) - if src_enc_padding is None: - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=tf.float32) - - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding) - done_hyps = dec.BeamSearchDecode(encoder_outputs).done_hyps - self.evaluate(tf.global_variables_initializer()) - - softmax_wts = self.evaluate(dec.vars.softmax) - print('softmax wts = ', softmax_wts) - - done_hyps_serialized = self.evaluate([done_hyps])[0] - hyp = Hypothesis() - print('done hyps shape = ', done_hyps_serialized.shape) - for i in range(5): - for j in range(8): - print(i, j, len(done_hyps_serialized[i, j])) - hyp.ParseFromString(done_hyps_serialized[2, 5]) - print('hyp = ', hyp) - return hyp - - def _VerifyHypothesesMatch(self, hyp1, hyp2): - tf.logging.info('hyp1 = %s', hyp1) - tf.logging.info('hyp2 = %s', hyp2) - self.assertEqual(hyp1.beam_id, hyp2.beam_id) - self.assertEqual(list(hyp1.ids), list(hyp2.ids)) - self.assertAllClose(hyp1.scores, hyp2.scores) - self.assertEqual(len(hyp1.atten_vecs), len(hyp2.atten_vecs)) - for av1, av2 in zip(hyp1.atten_vecs, hyp2.atten_vecs): - self.assertAllClose(av1.prob, av2.prob) - - def testDecoderBeamSearchDecode(self): - np.random.seed(837575) - - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams(None, False, False), - num_classes=8) - p.beam_search.num_hyps_per_beam = 4 - p.dtype = tf.float32 - p.target_seq_len = 5 - - expected_str = """ - beam_id: 1 - ids: 0 - ids: 6 - ids: 2 - scores: -2.021608 - scores: -2.000098 - scores: -2.036338 - atten_vecs { - prob: 0.330158 - prob: 0.342596 - prob: 0.327246 - prob: 0.0 - prob: 0.0 - } - atten_vecs { - prob: 0.330158 - prob: 0.342597 - prob: 0.327245 - prob: 0.0 - prob: 0.0 - } - atten_vecs { - prob: 0.330158 - prob: 0.342597 - prob: 0.327245 - prob: 0.0 - prob: 0.0 - } - """ - expected_hyp = Hypothesis() - text_format.Parse(expected_str, expected_hyp) - - decoded_hyp = self._testDecoderBeamSearchDecodeHelperWithOutput(params=p) - self._VerifyHypothesesMatch(expected_hyp, decoded_hyp) - - def testDecoderSampleTargetSequences(self): - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams(None, False, False), - num_classes=8) - p.target_seq_len = 5 - p.random_seed = 1 - config = tf.config_pb2.ConfigProto( - graph_options=tf.GraphOptions( - optimizer_options=tf.OptimizerOptions(do_function_inlining=False))) - with self.session(use_gpu=False, config=config): - tf.random.set_seed(8372740) - np.random.seed(35315) - dec = p.Instantiate() - source_sequence_length = 5 - batch_size = 4 - source_encodings = tf.constant( - np.random.normal( - size=[source_sequence_length, batch_size, p.source_dim]), - dtype=tf.float32) - source_encoding_padding = tf.constant( - [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], [0.0, 1.0, 1.0, 1.0], - [0.0, 1.0, 1.0, 1.0], [0.0, 1.0, 1.0, 1.0]], - dtype=tf.float32) - encoder_outputs = py_utils.NestedMap( - encoded=source_encodings, padding=source_encoding_padding) - sampled_sequences = dec.SampleTargetSequences( - dec.theta, encoder_outputs, random_seed=tf.cast(123, tf.int32)) - self.assertAllEqual([batch_size, p.target_seq_len], - sampled_sequences.ids.shape) - self.evaluate(tf.global_variables_initializer()) - decoder_output = self.evaluate(sampled_sequences) - print('ids=%s' % np.array_repr(decoder_output.ids)) - lens = np.sum(1 - decoder_output.paddings, axis=1) - print('lens=%s' % lens) - # pyformat: disable - # pylint: disable=bad-whitespace,bad-continuation - expected_ids = [[6, 2, 2, 2, 2], - [0, 0, 7, 5, 1], - [6, 1, 5, 1, 5], - [6, 7, 7, 4, 4]] - # pylint: enable=bad-whitespace,bad-continuation - # pyformat: enable - expected_lens = [2, 5, 5, 5] - self.assertAllEqual(expected_lens, lens) - self.assertAllEqual(expected_ids, decoder_output.ids) - - # Sample again with the same random seed. - decoder_output2 = self.evaluate( - dec.SampleTargetSequences( - dec.theta, encoder_outputs, random_seed=tf.cast(123, tf.int32))) - # Get the same output. - self.assertAllEqual(decoder_output.ids, decoder_output2.ids) - self.assertAllEqual(decoder_output.paddings, decoder_output2.paddings) - - # Sample again with a different random seed. - decoder_output3 = self.evaluate( - dec.SampleTargetSequences( - dec.theta, encoder_outputs, random_seed=tf.cast(123456, - tf.int32))) - # Get different sequences. - self.assertNotAllClose(expected_ids, decoder_output3.ids) - - def testDecoderFPropWithSymbolicShape(self): - """Create decoder with default params, and verify that FProp runs.""" - with self.session(): - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - p.rnn_cell_dim = symbolic.Symbol('rnn_cell_dim') - - with symbolic.SymbolToValueMap(symbolic.STATIC_VALUES, - {p.rnn_cell_dim: 6}): - loss, per_sequence_loss = self._testDecoderFPropHelper(params=p) - self.evaluate(tf.global_variables_initializer()) - loss_val, per_sequence_loss_val = self.evaluate( - [loss, per_sequence_loss]) - - print('loss = ', loss_val, 'per sequence loss = ', per_sequence_loss_val) - # Target batch size is 4. Therefore, we should expect 4 here. - self.assertEqual(per_sequence_loss_val.shape, (4,)) - - def testUpdateTargetVocabSize(self): - p = self._DecoderParams( - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - p.label_smoothing = lingvo_layers.LocalizedLabelSmoother.Params() - p.label_smoothing.num_classes = p.softmax.num_classes - - vocab_size = 1024 - self.assertNotEqual(p.emb.vocab_size, vocab_size) - self.assertNotEqual(p.softmax.num_classes, vocab_size) - self.assertNotEqual(p.fusion.lm.vocab_size, vocab_size) - self.assertNotEqual(p.label_smoothing.num_classes, vocab_size) - p = p.cls.UpdateTargetVocabSize(p, vocab_size) - dec = p.Instantiate() - self.assertEqual(vocab_size, dec.params.emb.vocab_size) - self.assertEqual(vocab_size, dec.params.softmax.num_classes) - self.assertEqual(vocab_size, p.fusion.lm.vocab_size) - self.assertEqual(vocab_size, p.label_smoothing.num_classes) - - def testDecoderFPropWithAdapters(self): - """Create decoder with adapters, and verify that FProp runs.""" - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - - params = self._DecoderParams( - num_rnn_layers=2, - vn_config=py_utils.VariationalNoiseParams( - None, True, False, seed=12345)) - params.rnn_cell_dim = 3 - params.adapter_layer_tpl.Set( - bottleneck_dim=4, - num_tasks=16, - projection_params_init=py_utils.WeightInit.Gaussian(0.01)) - params.adapter_task_id_field = 'domain_ids' - - dec = params.Instantiate() - src_seq_len = 5 - src_enc = tf.random.normal([src_seq_len, 2, 8], - seed=982774838, - dtype=py_utils.FPropDtype(params)) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=py_utils.FPropDtype(params)) - domain_ids = tf.constant(np.random.randint(low=0, high=16, size=[2])) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding, domain_ids=domain_ids) - # shape=[4, 5] - target_ids = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 15], - [5, 6, 7, 8], [10, 5, 2, 5]], - dtype=tf.int32)) - # shape=[4, 5] - target_labels = tf.transpose( - tf.constant([[0, 1, 2, 3], [1, 2, 3, 4], [10, 11, 12, 13], - [5, 7, 8, 10], [10, 5, 2, 4]], - dtype=tf.int32)) - # shape=[4, 5] - target_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], - [1, 1, 1, 0]], - dtype=py_utils.FPropDtype(params))) - target_transcripts = tf.constant(['abcd', 'bcde', 'klmp', 'fghi', 'kfcf']) - target_weights = 1.0 - target_paddings - # ids/labels/weights/paddings are all in [batch, time] shape. - targets = py_utils.NestedMap({ - 'ids': target_ids, - 'labels': target_labels, - 'weights': target_weights, - 'paddings': target_paddings, - 'transcripts': target_transcripts, - }) - decoder_outputs = dec.FPropDefaultTheta(encoder_outputs, targets) - metrics = decoder_outputs.metrics - per_sequence_loss = decoder_outputs.per_sequence['loss'] - - self.assertIn('fraction_of_correct_next_step_preds', metrics) - self.evaluate(tf.global_variables_initializer()) - metrics_val, per_sequence_loss_val = self.evaluate( - [metrics, per_sequence_loss]) - tf.logging.info('metrics=%s, per_sequence_loss=%s', metrics_val, - per_sequence_loss_val) - - self.assertEqual(metrics_val['loss'], metrics_val['log_pplx']) - # Target batch size is 4. Therefore, we should expect 4 here. - self.assertEqual(per_sequence_loss_val.shape, (4,)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/asr/decoder_utils.py b/lingvo/tasks/asr/decoder_utils.py deleted file mode 100644 index ed9efa7a..00000000 --- a/lingvo/tasks/asr/decoder_utils.py +++ /dev/null @@ -1,168 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Common utilities for ASR decoders.""" - -import copy -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import symbolic -import six - - -def _IsSymbolOrPositive(dim): - return symbolic.IsSymbol(dim) or dim > 0 - - -def SetRnnCellNodes(decoder_params, rnn_cell_params): - if _IsSymbolOrPositive(decoder_params.rnn_cell_dim): - rnn_cell_params.num_output_nodes = decoder_params.rnn_cell_dim - if _IsSymbolOrPositive(decoder_params.rnn_cell_hidden_dim): - if not hasattr(rnn_cell_params, 'num_hidden_nodes'): - raise ValueError( - 'num_hidden_nodes not supported by the RNNCell: %s' % rnn_cell_params) - rnn_cell_params.num_hidden_nodes = decoder_params.rnn_cell_hidden_dim - - -def Tokenize(string): - """Returns a list containing non-empty tokens from the given string.""" - if not isinstance(string, str): - string = six.ensure_text(string, 'utf-8') - return string.split() - - -def ComputeWer(hyps, refs): - """Computes word errors in hypotheses relative to reference transcripts. - - Args: - hyps: Hypotheses, represented as string tensors of shape [N]. - refs: References, represented as string tensors of shape [N]. - - Returns: - An int64 tensor, word_errs, of size [N, 2] where word_errs[i, 0] corresponds - to the number of word errors in hyps[i] relative to refs[i]; word_errs[i, 1] - corresponds to the number of words in refs[i]. - """ - - def _NormalizeWhitespace(s): - return tf.strings.regex_replace(tf.strings.strip(s), r'\s+', ' ') - - hyps = _NormalizeWhitespace(hyps) - refs = _NormalizeWhitespace(refs) - - hyps = py_utils.HasRank(hyps, 1) - refs = py_utils.HasRank(refs, 1) - hyps = py_utils.HasShape(hyps, tf.shape(refs)) - - word_errors = tf.cast( - tf.edit_distance( - tf.string_split(hyps), tf.string_split(refs), normalize=False), - tf.int64) - - # Count number of spaces in reference, and increment by 1 to get total number - # of words. - ref_words = tf.cast( - tf.strings.length(tf.strings.regex_replace(refs, '[^ ]', '')) + 1, - tf.int64) - # Set number of words to 0 if the reference was empty. - ref_words = tf.where( - tf.equal(refs, ''), tf.zeros_like(ref_words, tf.int64), ref_words) - - return tf.concat( - [tf.expand_dims(word_errors, -1), - tf.expand_dims(ref_words, -1)], axis=1) - - -def EditDistance(ref_str, hyp_str): - """Computes Levenshtein edit distance between reference and hypotheses. - - Args: - ref_str: A string of the ref sentence. - hyp_str: A string of one actual hyp. - - Returns: - (ins, subs, del, total): - - - ins: number of insertions. - - subs: number of substitutions. - - del: number of deletions. - - total: total difference length. - """ - - class ErrorStats: - """Class to keep track of error counts.""" - - def __init__(self, ins, dels, subs, tot): - self.ins, self.dels, self.subs, self.total_cost = ins, dels, subs, tot - - def __repr__(self): - return 'ErrorStats(ins=%d, dels=%d, subs=%d, tot=%d)' % ( - self.ins, self.dels, self.subs, self.total_cost) - - # temp sequence to remember error type and stats. - e, cur_e = [], [] - lst_ref = Tokenize(ref_str) - for i in range(len(lst_ref) + 1): - e.append(ErrorStats(0, i, 0, i)) - cur_e.append(ErrorStats(0, 0, 0, 0)) - - lst_hyp = Tokenize(hyp_str) - for hyp_index in range(1, len(lst_hyp) + 1): - cur_e[0] = copy.copy(e[0]) - cur_e[0].ins += 1 - cur_e[0].total_cost += 1 - - for ref_index in range(1, len(lst_ref) + 1): - ins_err = e[ref_index].total_cost + 1 - del_err = cur_e[ref_index - 1].total_cost + 1 - sub_err = e[ref_index - 1].total_cost - if lst_hyp[hyp_index - 1] != lst_ref[ref_index - 1]: - sub_err += 1 - - if sub_err < ins_err and sub_err < del_err: - cur_e[ref_index] = copy.copy(e[ref_index - 1]) - if lst_hyp[hyp_index - 1] != lst_ref[ref_index - 1]: - cur_e[ref_index].subs += 1 - cur_e[ref_index].total_cost = sub_err - elif del_err < ins_err: - cur_e[ref_index] = copy.copy(cur_e[ref_index - 1]) - cur_e[ref_index].total_cost = del_err - cur_e[ref_index].dels += 1 - else: - cur_e[ref_index] = copy.copy(e[ref_index]) - cur_e[ref_index].total_cost = ins_err - cur_e[ref_index].ins += 1 - - for i in range(len(e)): - e[i] = copy.copy(cur_e[i]) - - return e[-1].ins, e[-1].subs, e[-1].dels, e[-1].total_cost - - -def EditDistanceInIds(ref_ids, hyp_ids): - ref_ids = ['%d' % x for x in ref_ids] - hyp_ids = ['%d' % x for x in hyp_ids] - ref_str = ' '.join(ref_ids) - hyp_str = ' '.join(hyp_ids) - return EditDistance(ref_str, hyp_str) - - -def FilterEpsilon(string): - """Filters out tokens from the given string.""" - return ' '.join(Tokenize(string.replace('', ' '))) - - -def FilterNoise(string): - """Filters out tokens from the given string.""" - return ' '.join(t for t in Tokenize(string) if t != '') diff --git a/lingvo/tasks/asr/decoder_utils_test.py b/lingvo/tasks/asr/decoder_utils_test.py deleted file mode 100644 index 5e81f764..00000000 --- a/lingvo/tasks/asr/decoder_utils_test.py +++ /dev/null @@ -1,212 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for decoder utility functions.""" - -import lingvo.compat as tf -from lingvo.core import rnn_cell -from lingvo.core import symbolic -from lingvo.core import test_utils -from lingvo.tasks.asr import decoder -from lingvo.tasks.asr import decoder_utils - -FLAGS = tf.flags.FLAGS - - -class DecoderUtilsSetRnnCellNodesTest(test_utils.TestCase): - - def testSetRnnCellNodes(self): - decoder_p = decoder.AsrDecoder.Params() - base_rnn_p = rnn_cell.LSTMCellSimple.Params().Set(num_output_nodes=4) - - # rnn_cell_dim > 0. - decoder_p.rnn_cell_dim = 8 - rnn_p = base_rnn_p.Copy() - decoder_utils.SetRnnCellNodes(decoder_p, rnn_p) - self.assertEqual(rnn_p.num_output_nodes, decoder_p.rnn_cell_dim) - - # rnn_cell_dim <= 0. - decoder_p.rnn_cell_dim = 0 - rnn_p = base_rnn_p.Copy() - decoder_utils.SetRnnCellNodes(decoder_p, rnn_p) - self.assertEqual(rnn_p.num_output_nodes, base_rnn_p.num_output_nodes) - - # rnn_cell_dim is a symbol. - decoder_p.rnn_cell_dim = symbolic.Symbol("rnn_cell_dim") - rnn_p = base_rnn_p.Copy() - decoder_utils.SetRnnCellNodes(decoder_p, rnn_p) - self.assertIs(rnn_p.num_output_nodes, decoder_p.rnn_cell_dim) - - # rnn_cell_hidden_dim > 0. - decoder_p.rnn_cell_hidden_dim = 16 - rnn_p = base_rnn_p.Copy() - decoder_utils.SetRnnCellNodes(decoder_p, rnn_p) - self.assertEqual(rnn_p.num_hidden_nodes, decoder_p.rnn_cell_hidden_dim) - - # rnn_cell_hidden_dim <= 0. - decoder_p.rnn_cell_hidden_dim = 0 - rnn_p = base_rnn_p.Copy() - decoder_utils.SetRnnCellNodes(decoder_p, rnn_p) - self.assertEqual(rnn_p.num_hidden_nodes, base_rnn_p.num_hidden_nodes) - - # rnn_cell_hidden_dim is a symbol. - decoder_p.rnn_cell_hidden_dim = symbolic.Symbol("rnn_cell_hidden_dim") - rnn_p = base_rnn_p.Copy() - decoder_utils.SetRnnCellNodes(decoder_p, rnn_p) - self.assertIs(rnn_p.num_hidden_nodes, decoder_p.rnn_cell_hidden_dim) - - -class DecoderUtilsTokenizeTest(test_utils.TestCase): - - def testTokenize(self): - s = "onetoken" - self.assertEqual(["onetoken"], decoder_utils.Tokenize(s)) - - s = "two tokens" - self.assertEqual(["two", "tokens"], decoder_utils.Tokenize(s)) - - s = " extra spaces are filtered " - self.assertEqual(["extra", "spaces", "are", "filtered"], - decoder_utils.Tokenize(s)) - - -class DecoderUtilsComputeWerTest(test_utils.TestCase): - - def testInvalidInputsExtraHyps(self): - with self.session(): - with self.assertRaises(Exception): - decoder_utils.ComputeWer(hyps=["one", "two"], refs=["one"]).eval() - - def testInvalidInputsExtraRefs(self): - with self.session(): - with self.assertRaises(Exception): - decoder_utils.ComputeWer(hyps=["one"], refs=["one", "two"]).eval() - - def testInvalidInputsWrongRank(self): - with self.session(): - with self.assertRaises(Exception): - decoder_utils.ComputeWer( - hyps=[["one"], ["two"]], refs=[["one"], ["two"]]).eval() - - def testBasic(self): - with self.session(): - self.assertAllEqual( - decoder_utils.ComputeWer(hyps=["one"], refs=["one"]).eval(), [[0, 1]]) - self.assertAllEqual( - decoder_utils.ComputeWer(hyps=["one two"], refs=["one two"]).eval(), - [[0, 2]]) - - def testMultiples(self): - with self.session(): - wer = decoder_utils.ComputeWer( - hyps=["one", "two pigs"], refs=["one", "three pink pigs"]) - self.assertAllEqual(wer.shape, [2, 2]) - self.assertAllEqual(wer.eval(), [[0, 1], [2, 3]]) - - def testConsecutiveWhiteSpace(self): - with self.session(): - wer = decoder_utils.ComputeWer( - hyps=["one two", "one two", "two pigs"], - refs=["one two", "one two ", "three pink pigs"]) - self.assertAllEqual(wer.shape, [3, 2]) - self.assertAllEqual(wer.eval(), [[0, 2], [0, 2], [2, 3]]) - - def testEmptyRefsAndHyps(self): - with self.session(): - wer = decoder_utils.ComputeWer( - hyps=["", "one two", ""], refs=["", "", "three four five"]) - self.assertAllEqual(wer.shape, [3, 2]) - self.assertAllEqual(wer.eval(), [[0, 0], [2, 0], [3, 3]]) - - def testDifferencesInCaseAreCountedAsErrors(self): - with self.session(): - wer = decoder_utils.ComputeWer( - hyps=["ONE two", "one two"], refs=["one two", "ONE two"]) - self.assertAllEqual(wer.shape, [2, 2]) - self.assertAllEqual(wer.eval(), [[1, 2], [1, 2]]) - - -class DecoderUtilsFilterTest(test_utils.TestCase): - - def testFilterEpsilon(self): - s = "no epsilon" - self.assertEqual(s, decoder_utils.FilterEpsilon(s)) - - s = "epsilon tokens areremoved" - self.assertEqual("epsilon tokens are removed", - decoder_utils.FilterEpsilon(s)) - - def testFilterNoise(self): - s = "no noise" - self.assertEqual(s, decoder_utils.FilterNoise(s)) - - s = " noise tokens are removed " - self.assertEqual("noise tokens are removed", decoder_utils.FilterNoise(s)) - - -class DecoderUtilsEditDistanceTest(test_utils.TestCase): - - def testEditDistance1(self): - ref = "a b c d e f g h" - hyp = "a b c d e f g h" - self.assertEqual((0, 0, 0, 0), decoder_utils.EditDistance(ref, hyp)) - - ref = "a b c d e f g h" - hyp = "a b d e f g h" - self.assertEqual((0, 0, 1, 1), decoder_utils.EditDistance(ref, hyp)) - - ref = "a b c d e f g h" - hyp = "a b c i d e f g h" - self.assertEqual((1, 0, 0, 1), decoder_utils.EditDistance(ref, hyp)) - - ref = "a b c d e f g h" - hyp = "a b c i e f g h" - self.assertEqual((0, 1, 0, 1), decoder_utils.EditDistance(ref, hyp)) - - ref = "a b c d e f g j h" - hyp = "a b c i d e f g h" - self.assertEqual((1, 0, 1, 2), decoder_utils.EditDistance(ref, hyp)) - - ref = "a b c d e f g j h" - hyp = "a b c i e f g h k" - self.assertEqual((1, 1, 1, 3), decoder_utils.EditDistance(ref, hyp)) - - ref = "" - hyp = "" - self.assertEqual((0, 0, 0, 0), decoder_utils.EditDistance(ref, hyp)) - ref = "" - hyp = "a b c" - self.assertEqual((3, 0, 0, 3), decoder_utils.EditDistance(ref, hyp)) - - ref = "a b c d" - hyp = "" - self.assertEqual((0, 0, 4, 4), decoder_utils.EditDistance(ref, hyp)) - - def testEditDistanceInIds(self): - ref = [0, 1, 2, 3, 9] - hyp = [0, 2, 3, 5, 6] - self.assertEqual((1, 1, 1, 3), decoder_utils.EditDistanceInIds(ref, hyp)) - - def testEditDistanceSkipsEmptyTokens(self): - ref = "a b c d e f g h" - hyp = "a b c d e f g h" - self.assertEqual((0, 0, 0, 0), decoder_utils.EditDistance(ref, hyp)) - - ref = "a b c d e f g h" - hyp = "a b c d e f g h" - self.assertEqual((0, 0, 0, 0), decoder_utils.EditDistance(ref, hyp)) - - -if __name__ == "__main__": - tf.test.main() diff --git a/lingvo/tasks/asr/encoder.py b/lingvo/tasks/asr/encoder.py deleted file mode 100644 index 7bf619b9..00000000 --- a/lingvo/tasks/asr/encoder.py +++ /dev/null @@ -1,464 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Encoders for the speech model.""" - -import collections -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import layers -from lingvo.core import model_helper -from lingvo.core import py_utils -from lingvo.core import rnn_cell -from lingvo.core import rnn_layers -from lingvo.core import spectrum_augmenter -from lingvo.core import summary_utils - -from tensorflow.python.ops import inplace_ops - -ConvLSTMBlock = collections.namedtuple('ConvLSTMBlock', ('rnn', 'cnn')) - - -class AsrEncoder(base_layer.BaseLayer): - """Speech encoder version 1.""" - - @classmethod - def Params(cls): - """Configs for AsrEncoder.""" - p = super().Params() - p.Define('specaugment_network', - spectrum_augmenter.SpectrumAugmenter.Params(), - 'Configs template for the augmentation network.') - p.Define('use_specaugment', False, 'Use specaugmentation or not.') - p.Define('lstm_tpl', rnn_cell.LSTMCellSimple.Params(), - 'Configs template for the RNN layer.') - p.Define('cnn_tpl', layers.ConvLayer.Params(), - 'Configs template for the conv layer.') - p.Define('proj_tpl', layers.ProjectionLayer.Params(), - 'Configs template for the projection layer.') - p.Define( - 'highway_skip', False, - 'If set, residual connections from different layers are gated. ' - 'Will only be used if residual_start is enabled.') - p.Define('highway_skip_tpl', layers.HighwaySkipLayer.Params(), - 'Configs template for the highway skip layer.') - p.Define('conv_lstm_tpl', rnn_cell.ConvLSTMCell.Params(), - 'Configs template for ConvLSTMCell.') - p.Define( - 'after_conv_lstm_cnn_tpl', layers.ConvLayer.Params(), - 'Configs template for the cnn layer immediately follow the' - ' convlstm layer.') - p.Define('conv_filter_shapes', None, 'Filter shapes for each conv layer.') - p.Define('conv_filter_strides', None, 'Filter strides for each conv layer.') - p.Define('input_shape', [None, None, None, None], - 'Shape of the input. This should a TensorShape with rank 4.') - p.Define('lstm_cell_size', 256, 'LSTM cell size for the RNN layer.') - p.Define('num_cnn_layers', 2, 'Number of conv layers to create.') - p.Define('num_conv_lstm_layers', 1, 'Number of conv lstm layers to create.') - p.Define('num_lstm_layers', 3, 'Number of rnn layers to create') - p.Define('project_lstm_output', True, - 'Include projection layer after each encoder LSTM layer.') - p.Define('pad_steps', 6, - 'Extra zero-padded timesteps to add to the input sequence. ') - p.Define( - 'residual_start', 0, 'Start residual connections from this lstm layer. ' - 'Disabled if 0 or greater than num_lstm_layers.') - p.Define('residual_stride', 1, - 'Number of lstm layers to skip per residual connection.') - p.Define('bidi_rnn_type', 'func', 'Options: func. ' - 'func: BidirectionalFRNN. ') - p.Define( - 'extra_per_layer_outputs', False, - 'Whether to output the encoding result from each encoder layer besides ' - 'the regular final output. The corresponding extra outputs are keyed ' - 'by "${layer_type}_${layer_index}" in the encoder output NestedMap, ' - 'where layer_type is one of: "conv", "conv_lstm" and "rnn".') - p.Define('stacking_layer_tpl', layers.StackingOverTime.Params(), - 'Configs template for the stacking layer over time.') - p.Define( - 'layer_index_before_stacking', -1, - 'The (0-based) index of the lstm layer after which the stacking layer ' - 'will be inserted. Negative value means no stacking layer will be ' - 'used.') - - # TODO(yonghui): Maybe move those configs to a separate file. - # Set some reasonable default values. - # - # NOTE(yonghui): The default config below assumes the following encoder - # architecture: - # - # cnn/batch-norm/relu -> - # cnn/batch-norm/relu -> - # bidirectional conv-lstm -> - # cnn/batch-norm/relu - # bidirectional lstm -> - # projection/batch-norm/relu -> - # bidirectional lstm -> - # projection/batch-norm/relu -> - # bidirectional lstm - # - # Default config for the rnn layer. - p.lstm_tpl.params_init = py_utils.WeightInit.Uniform(0.1) - - # Default config for the convolution layer. - # What is this 3 for? Delta and delta-delta? - p.input_shape = [None, None, 80, 3] - p.conv_filter_shapes = [(3, 3, 3, 32), (3, 3, 32, 32)] - p.conv_filter_strides = [(2, 2), (2, 2)] - p.cnn_tpl.params_init = py_utils.WeightInit.TruncatedGaussian(0.1) - # TODO(yonghui): Disable variational noise logic. - # NOTE(yonghui): Fortunately, variational noise logic is currently not - # implemented for ConvLayer yet (as of sep 22, 2016). - - # Default config for the projection layer. - p.proj_tpl.params_init = py_utils.WeightInit.TruncatedGaussian(0.1) - # TODO(yonghui): Disable variational noise logic. - # NOTE(yonghui): Fortunately, variational noise logic is currently not - # implemented for ProjectionLayer yet (as of sep 22, 2016). - - p.conv_lstm_tpl.filter_shape = [1, 3] # height (time), width (frequency) - p.conv_lstm_tpl.inputs_shape = [None, None, None, None] - p.conv_lstm_tpl.cell_shape = [None, None, None, None] - p.conv_lstm_tpl.params_init = py_utils.WeightInit.TruncatedGaussian(0.1) - p.after_conv_lstm_cnn_tpl.filter_shape = [3, 3, None, None] - p.after_conv_lstm_cnn_tpl.params_init = ( - py_utils.WeightInit.TruncatedGaussian(0.1)) - p.after_conv_lstm_cnn_tpl.filter_stride = [1, 1] - - # See https://arxiv.org/pdf/1610.03022.pdf, section 2.2. - p.proj_tpl.batch_norm = True - p.proj_tpl.activation = 'RELU' - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - # Use specAugment or not. - if p.use_specaugment: - self.CreateChild('specaugment', p.specaugment_network.Copy()) - # First create the conv layers. - - assert p.num_cnn_layers == len(p.conv_filter_shapes) - assert p.num_cnn_layers == len(p.conv_filter_strides) - params_conv_layers = [] - for i in range(p.num_cnn_layers): - conv_p = p.cnn_tpl.Copy() - conv_p.name = 'conv_L%d' % i - conv_p.filter_shape = p.conv_filter_shapes[i] - conv_p.filter_stride = p.conv_filter_strides[i] - params_conv_layers.append(conv_p) - self.CreateChildren('conv', params_conv_layers) - - conv_output_shape = p.input_shape - for i in range(p.num_cnn_layers): - conv_output_shape = self.conv[i].OutShape(conv_output_shape) - assert len(conv_output_shape) == 4 # batch, height, width, channel. - - params_conv_lstm_rnn = [] - params_conv_lstm_cnn = [] - for i in range(p.num_conv_lstm_layers): - # NOTE(yonghui): We assume that output from ConvLSTMBlock has the same - # shape as its input. - _, _, width, in_channel = conv_output_shape - f_conv_lstm_p = p.conv_lstm_tpl.Copy() - f_conv_lstm_p.name = 'f_conv_lstm_%d' % i - f_conv_lstm_p.inputs_shape = [None, 1, width, in_channel] - f_conv_lstm_p.cell_shape = [None, 1, width, in_channel] - b_conv_lstm_p = f_conv_lstm_p.Copy() - b_conv_lstm_p.name = 'b_conv_lstm_%d' % i - conv_lstm_rnn_p = self.CreateConvLstmLayerParams() - conv_lstm_rnn_p.name = 'conv_lstm_rnn' - conv_lstm_rnn_p.fwd = f_conv_lstm_p - conv_lstm_rnn_p.bak = b_conv_lstm_p - params_conv_lstm_rnn.append(conv_lstm_rnn_p) - cnn_p = p.after_conv_lstm_cnn_tpl.Copy() - cnn_p.name = 'conv_lstm_cnn_%d' % i - cnn_p.filter_shape[2] = 2 * in_channel - cnn_p.filter_shape[3] = in_channel - params_conv_lstm_cnn.append(cnn_p) - # TODO(yonghui): Refactor ConvLSTMBlock into a layer. - self.CreateChildren('conv_lstm_rnn', params_conv_lstm_rnn) - self.CreateChildren('conv_lstm_cnn', params_conv_lstm_cnn) - - (self._first_lstm_input_dim, - self._first_lstm_input_dim_pad) = self.FirstLstmLayerInputDimAndPadding( - conv_output_shape, pad_to_multiple=16) - - # Now create all the rnn layers and projection layers. - # TODO(yonghui): take care of device placement. - params_rnn_layers = [] - params_proj_layers = [] - params_highway_skip_layers = [] - output_dim = self._first_lstm_input_dim - for i in range(p.num_lstm_layers): - input_dim = output_dim - forward_p = p.lstm_tpl.Copy() - forward_p.name = 'fwd_rnn_L%d' % i - forward_p.num_input_nodes = input_dim - forward_p.num_output_nodes = p.lstm_cell_size - backward_p = forward_p.Copy() - backward_p.name = 'bak_rnn_L%d' % i - rnn_p = self.CreateBidirectionalRNNParams(forward_p, backward_p) - rnn_p.name = 'brnn_L%d' % i - params_rnn_layers.append(rnn_p) - output_dim = 2 * p.lstm_cell_size - - if p.project_lstm_output and (i < p.num_lstm_layers - 1): - proj_p = p.proj_tpl.Copy() - proj_p.input_dim = 2 * p.lstm_cell_size - proj_p.output_dim = 2 * p.lstm_cell_size - proj_p.name = 'proj_L%d' % i - params_proj_layers.append(proj_p) - - # add the skip layers - residual_index = i - p.residual_start + 1 - if p.residual_start > 0 and residual_index >= 0 and p.highway_skip: - highway_skip = p.highway_skip_tpl.Copy() - highway_skip.name = 'enc_hwskip_%d' % len(params_highway_skip_layers) - highway_skip.input_dim = 2 * p.lstm_cell_size - params_highway_skip_layers.append(highway_skip) - # Adds the stacking layer. - if p.layer_index_before_stacking == i: - stacking_layer = p.stacking_layer_tpl.Copy() - stacking_layer.name = 'stacking_%d' % i - self.CreateChild('stacking', stacking_layer) - stacking_window_len = ( - p.stacking_layer_tpl.left_context + 1 + - p.stacking_layer_tpl.right_context) - output_dim *= stacking_window_len - - self.CreateChildren('rnn', params_rnn_layers) - self.CreateChildren('proj', params_proj_layers) - self.CreateChildren('highway_skip', params_highway_skip_layers) - - @property - def _use_functional(self): - return True - - @property - def input_shape(self): - return self.params.input_shape - - def CreateBidirectionalRNNParams(self, forward_p, backward_p): - return model_helper.CreateBidirectionalRNNParams(self.params, forward_p, - backward_p) - - def CreateConvLstmLayerParams(self): - return rnn_layers.BidirectionalFRNN.Params() - - def FirstLstmLayerInputDimAndPadding(self, - conv_output_shape, - pad_to_multiple=16): - lstm_input_shape = conv_output_shape - # Makes sure the lstm input dims is multiple of 16 (alignment - # requirement from FRNN). - first_lstm_input_dim_unpadded = lstm_input_shape[2] * lstm_input_shape[3] - - if self._use_functional and (first_lstm_input_dim_unpadded % pad_to_multiple - != 0): - first_lstm_input_dim = int( - (first_lstm_input_dim_unpadded + pad_to_multiple - 1) / - pad_to_multiple) * pad_to_multiple - else: - first_lstm_input_dim = first_lstm_input_dim_unpadded - - first_lstm_input_dim_padding = ( - first_lstm_input_dim - first_lstm_input_dim_unpadded) - return first_lstm_input_dim, first_lstm_input_dim_padding - - @property - def supports_streaming(self): - return False - - def zero_state(self, theta, batch_size): - return py_utils.NestedMap() - - def FProp(self, theta, batch, state0=None): - """Encodes source as represented by 'inputs' and 'paddings'. - - Args: - theta: A NestedMap object containing weights' values of this - layer and its children layers. - batch: A NestedMap with fields: - - - src_inputs - The inputs tensor. It is expected to be of shape [batch, - time, feature_dim, channels]. - - paddings - The paddings tensor. It is expected to be of shape [batch, - time]. - state0: Recurrent input state. Not supported/ignored by this encoder. - - Returns: - A NestedMap containing - - - 'encoded': a feature tensor of shape [time, batch, depth] - - 'padding': a 0/1 tensor of shape [time, batch] - - 'state': the updated recurrent state - - '${layer_type}_${layer_index}': The per-layer encoder output. Each one - is a NestedMap containing 'encoded' and 'padding' similar to regular - final outputs, except that 'encoded' from conv or conv_lstm layers are - of shape [time, batch, depth, channels]. - """ - p = self.params - inputs, paddings = batch.src_inputs, batch.paddings - outputs = py_utils.NestedMap() - with tf.name_scope(p.name): - # Adding specAugmentation. - if p.use_specaugment and not self.do_eval: - inputs, paddings = self.specaugment.FProp(theta.specaugment, inputs, - paddings) - # Add a few extra padded timesteps at the end. This is for ensuring the - # correctness of the conv-layers at the edges. - if p.pad_steps > 0: - # inplace_update() is not supported by TPU for now. Since we have done - # padding on the input_generator, we may avoid this additional padding. - assert not py_utils.use_tpu() - inputs_pad = tf.zeros( - inplace_ops.inplace_update(tf.shape(inputs), 1, p.pad_steps), - inputs.dtype) - paddings_pad = tf.ones( - inplace_ops.inplace_update(tf.shape(paddings), 1, p.pad_steps), - paddings.dtype) - inputs = tf.concat([inputs, inputs_pad], 1, name='inputs') - paddings = tf.concat([paddings, paddings_pad], 1) - - plots = [ - summary_utils.PrepareSequenceForPlot( - tf.transpose(inputs, [0, 1, 3, 2]), paddings, 'inputs') - ] - - conv_out = inputs - out_padding = paddings - for i, conv_layer in enumerate(self.conv): - conv_out, out_padding = conv_layer.FProp(theta.conv[i], conv_out, - out_padding) - if p.extra_per_layer_outputs: - conv_out *= (1.0 - out_padding[:, :, tf.newaxis, tf.newaxis]) - outputs['conv_%d' % i] = py_utils.NestedMap( - encoded=tf.transpose(conv_out, [1, 0, 2, 3]), # to [t, b, d, c] - padding=tf.transpose(out_padding)) - plots.append( - summary_utils.PrepareSequenceForPlot( - tf.transpose(conv_out, [0, 1, 3, 2]), out_padding, - 'conv_%d_out' % i)) - - def TransposeFirstTwoDims(t): - first_dim = tf.shape(t)[0] - second_dim = tf.shape(t)[1] - t_new = tf.transpose( - tf.reshape(t, [first_dim, second_dim, -1]), [1, 0, 2]) - t_shape_new = tf.concat([[second_dim], [first_dim], tf.shape(t)[2:]], 0) - return tf.reshape(t_new, t_shape_new) - - # Now the conv-lstm part. - conv_lstm_out = conv_out - conv_lstm_out_padding = out_padding - for i, (rnn, cnn) in enumerate( - zip(self.conv_lstm_rnn, self.conv_lstm_cnn)): - conv_lstm_in = conv_lstm_out - # Move time dimension to be the first. - conv_lstm_in = TransposeFirstTwoDims(conv_lstm_in) - conv_lstm_in = tf.expand_dims(conv_lstm_in, 2) - conv_lstm_in_padding = tf.expand_dims( - tf.transpose(conv_lstm_out_padding), 2) - lstm_out = rnn.FProp(theta.conv_lstm_rnn[i], conv_lstm_in, - conv_lstm_in_padding) - # Move time dimension to be the second. - cnn_in = TransposeFirstTwoDims(lstm_out) - cnn_in = tf.squeeze(cnn_in, 2) - cnn_in_padding = conv_lstm_out_padding - cnn_out, cnn_out_padding = cnn.FProp(theta.conv_lstm_cnn[i], cnn_in, - cnn_in_padding) - conv_lstm_out, conv_lstm_out_padding = cnn_out, cnn_out_padding - if p.extra_per_layer_outputs: - conv_lstm_out *= ( - 1.0 - conv_lstm_out_padding[:, :, tf.newaxis, tf.newaxis]) - outputs['conv_lstm_%d' % i] = py_utils.NestedMap( - encoded=tf.transpose(conv_lstm_out, - [1, 0, 2, 3]), # to [t, b, d, c] - padding=tf.transpose(conv_lstm_out_padding)) - plots.append( - summary_utils.PrepareSequenceForPlot(conv_lstm_out, - conv_lstm_out_padding, - 'conv_lstm_%d_out' % i)) - - # Need to do a reshape before starting the rnn layers. - conv_lstm_out = py_utils.HasRank(conv_lstm_out, 4) - conv_lstm_out_shape = tf.shape(conv_lstm_out) - new_shape = tf.concat([conv_lstm_out_shape[:2], [-1]], 0) - conv_lstm_out = tf.reshape(conv_lstm_out, new_shape) - if self._first_lstm_input_dim_pad: - conv_lstm_out = tf.pad( - conv_lstm_out, - [[0, 0], [0, 0], [0, self._first_lstm_input_dim_pad]]) - - conv_lstm_out = py_utils.HasShape(conv_lstm_out, - [-1, -1, self._first_lstm_input_dim]) - - # Transpose to move the time dimension to be the first. - rnn_in = tf.transpose(conv_lstm_out, [1, 0, 2]) - rnn_padding = tf.expand_dims(tf.transpose(conv_lstm_out_padding), 2) - # rnn_in is of shape [time, batch, depth] - # rnn_padding is of shape [time, batch, 1] - - # Now the rnn layers. - num_skips = 0 - for i in range(p.num_lstm_layers): - rnn_out = self.rnn[i].FProp(theta.rnn[i], rnn_in, rnn_padding) - residual_index = i - p.residual_start + 1 - if p.residual_start > 0 and residual_index >= 0: - if residual_index % p.residual_stride == 0: - residual_in = rnn_in - if residual_index % p.residual_stride == p.residual_stride - 1: - # Highway skip connection. - if p.highway_skip: - rnn_out = self.highway_skip[num_skips].FProp( - theta.highway_skip[num_skips], residual_in, rnn_out) - num_skips += 1 - else: - # Residual skip connection. - rnn_out += py_utils.HasShape(residual_in, tf.shape(rnn_out)) - if p.project_lstm_output and (i < p.num_lstm_layers - 1): - # Projection layers. - rnn_out = self.proj[i].FProp(theta.proj[i], rnn_out, rnn_padding) - if i == p.num_lstm_layers - 1: - rnn_out *= (1.0 - rnn_padding) - if p.extra_per_layer_outputs: - rnn_out *= (1.0 - rnn_padding) - outputs['rnn_%d' % i] = py_utils.NestedMap( - encoded=rnn_out, padding=tf.squeeze(rnn_padding, [2])) - # Stacking layer connection. - if p.layer_index_before_stacking == i: - # Stacking layer expects input tensor shape as [batch, time, feature]. - # So transpose the tensors before and after the layer. - rnn_out, rnn_padding = self.stacking.FProp( - tf.transpose(rnn_out, [1, 0, 2]), - tf.transpose(rnn_padding, [1, 0, 2])) - rnn_out = tf.transpose(rnn_out, [1, 0, 2]) - rnn_padding = tf.transpose(rnn_padding, [1, 0, 2]) - - plots.append( - summary_utils.PrepareSequenceForPlot( - tf.transpose(rnn_out, [1, 0, 2]), - tf.transpose(rnn_padding, [1, 0, 2]), 'rnn_%d_out' % i)) - rnn_in = rnn_out - final_out = rnn_in - - summary_utils.PlotSequenceFeatures( - list(reversed(plots)), 'encoder_example', xlabel='Time') - - outputs['encoded'] = final_out - outputs['padding'] = tf.squeeze(rnn_padding, [2]) - outputs['state'] = py_utils.NestedMap() - return outputs diff --git a/lingvo/tasks/asr/encoder_test.py b/lingvo/tasks/asr/encoder_test.py deleted file mode 100644 index ebbd8355..00000000 --- a/lingvo/tasks/asr/encoder_test.py +++ /dev/null @@ -1,337 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for ASR encoder.""" - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.asr import encoder -import numpy as np - - -class EncoderTest(test_utils.TestCase): - - def _EncoderParams(self, vn_config): - p = encoder.AsrEncoder.Params() - p.name = 'encoder' - vn_config.scale = tf.constant(0.1) - params_init = py_utils.WeightInit.Uniform(0.05) - - rnn_params = p.lstm_tpl - rnn_params.vn = vn_config - rnn_params.params_init = params_init - - cnn_params = p.cnn_tpl - cnn_params.vn = vn_config - cnn_params.params_init = params_init - - proj_params = p.proj_tpl - proj_params.vn = vn_config - proj_params.params_init = params_init - - p.conv_filter_shapes = [[3, 3, 3, 6], [3, 3, 6, 6]] - p.conv_filter_strides = [[2, 2], [2, 2]] - p.input_shape = [None, None, 16, 3] - - p.conv_lstm_tpl.vn = vn_config - p.after_conv_lstm_cnn_tpl.vn = vn_config - - p.num_cnn_layers = 2 - p.num_lstm_layers = 2 - p.lstm_cell_size = 16 - p.num_conv_lstm_layers = 0 - - return p - - def _ForwardPass(self, p): - tf.random.set_seed(8372749040) - stt_enc = encoder.AsrEncoder(p) - batch = py_utils.NestedMap() - batch.src_inputs = tf.random.normal([2, 20, 16, 3], seed=92837472) - batch.paddings = tf.zeros([2, 20]) - return stt_enc.FPropDefaultTheta(batch) - - def testEncoderConstruction(self): - vn_config = py_utils.VariationalNoiseParams(None, True, False, seed=12345) - p = self._EncoderParams(vn_config) - _ = encoder.AsrEncoder(p) - - def testForwardPass(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out, 0) - self.evaluate(tf.global_variables_initializer()) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_enc_out = [ - [-2.63900943e-02, -4.88980189e-02, 1.78375337e-02, - -9.66763496e-03, -1.45432353e-02, 3.63842538e-03, - -4.93378285e-03, 9.87463910e-03, -1.98941268e-02, - -2.31636949e-02, -6.76718354e-03, -1.01988772e-02, - 4.81432397e-03, 9.02220048e-03, 1.31793215e-03, - -1.39696691e-02, -2.36637704e-02, -5.25583047e-04, - -3.79295787e-03, 1.09998491e-02, 8.54234211e-03, - -2.43989471e-02, -6.27756910e-03, -1.64192859e-02, - 1.54568311e-02, 3.69091239e-03, 1.27634332e-02, - 2.50437222e-02, 3.77510749e-02, 1.71656217e-02, - 1.94890760e-02, 4.31961473e-03], - [-1.61839426e-02, 1.27755934e-02, -1.96352396e-02, - 1.04363225e-02, 6.10197056e-03, -5.08408714e-03, - -9.20344493e-04, 2.55419128e-02, -3.58198807e-02, - -4.18110676e-02, 9.45025682e-03, -7.00431701e-04, - 2.31945589e-02, -6.53471798e-05, -1.94577798e-02, - -1.53421704e-02, -1.50274234e-02, 1.06492080e-03, - 8.32110923e-03, -1.38334394e-03, 2.02696323e-02, - 2.13975199e-02, 2.23143250e-02, -1.54133392e-02, - 1.83746461e-02, 8.25020485e-03, -1.64317098e-02, - 1.46762179e-02, 1.89543713e-03, -3.36170895e-03, - 3.14423591e-02, -2.64923554e-02 ]] - # pylint: enable=bad-whitespace - # pyformat: enable - enc_out_sum_val = enc_out_sum.eval() - print('enc_out_sum_val', np.array_repr(enc_out_sum_val)) - self.assertAllClose(expected_enc_out, enc_out_sum_val) - - def testForwardPassWithConvLSTM(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.num_conv_lstm_layers = 1 - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out) - self.evaluate(tf.global_variables_initializer()) - - enc_out_sum_val = enc_out_sum.eval() - print('expected enc_out_sum_val', enc_out_sum_val) - test_utils.CompareToGoldenSingleFloat( - self, -0.1091638132929802, enc_out_sum_val) - - def testForwardPassWithConvLSTM2Layers(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.num_conv_lstm_layers = 2 - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out) - self.evaluate(tf.global_variables_initializer()) - - enc_out_sum_val = enc_out_sum.eval() - print('expected enc_out_sum_val', enc_out_sum_val) - test_utils.CompareToGoldenSingleFloat( - self, 0.024680551141500473, enc_out_sum_val) - - def testForwardPassWithResidualStart1(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.residual_start = 1 - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out) - self.evaluate(tf.global_variables_initializer()) - - enc_out_sum_val = enc_out_sum.eval() - print('expected enc_out_sum_val', enc_out_sum_val) - test_utils.CompareToGoldenSingleFloat( - self, 119.61833190917969, enc_out_sum_val) - - def testForwardPassWithResidualStart2(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.residual_start = 2 - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out) - self.evaluate(tf.global_variables_initializer()) - - enc_out_sum_val = enc_out_sum.eval() - print('expected enc_out_sum_val', enc_out_sum_val) - test_utils.CompareToGoldenSingleFloat( - self, 13.103919982910156, enc_out_sum_val) - - def testForwardPassWithResidualStart1Interval2(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.residual_start = 1 - p.residual_stride = 2 - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out) - self.evaluate(tf.global_variables_initializer()) - - enc_out_sum_val = enc_out_sum.eval() - print('expected enc_out_sum_val', enc_out_sum_val) - test_utils.CompareToGoldenSingleFloat( - self, 92.58077239990234, enc_out_sum_val) - - def testForwardPassWithStackingAfterFinalLayer(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.stacking_layer_tpl.left_context = 1 - p.stacking_layer_tpl.right_context = 0 - p.stacking_layer_tpl.stride = 2 - p.layer_index_before_stacking = 1 - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out, 0) - self.evaluate(tf.global_variables_initializer()) - # pyformat: disable - # pylint: disable=bad-whitespace - expected_enc_out = [ - [-1.25796525e-02, -2.32883729e-02, 7.40477070e-03, -4.51436592e-03, - -5.84740378e-03, 2.30195466e-03, -3.08505213e-03, 4.05658083e-03, - -8.12252797e-03, -1.08030904e-02, -4.17955732e-03, -3.73707339e-03, - 6.97144482e-04, 2.79850606e-03, 8.33133236e-04, -5.75614115e-03, - -1.10648498e-02, -1.20132393e-03, -1.69872947e-03, 6.97519444e-03, - 2.46211258e-03, -1.28190573e-02, -8.66306946e-05, -6.09322963e-03, - 7.14540575e-03, -5.67986863e-05, 5.17684873e-03, 1.18097477e-02, - 1.74862407e-02, 9.13049746e-03, 7.31027778e-03, 4.83186450e-05, - -1.38104409e-02, -2.56096497e-02, 1.04327593e-02, -5.15327370e-03, - -8.69584084e-03, 1.33647269e-03, -1.84873224e-03, 5.81806153e-03, - -1.17716007e-02, -1.23606063e-02, -2.58761784e-03, -6.46180846e-03, - 4.11718246e-03, 6.22369815e-03, 4.84800315e-04, -8.21352564e-03, - -1.25989169e-02, 6.75740885e-04, -2.09423108e-03, 4.02465323e-03, - 6.08023722e-03, -1.15798926e-02, -6.19094400e-03, -1.03260633e-02, - 8.31142440e-03, 3.74771934e-03, 7.58658582e-03, 1.32339774e-02, - 2.02648211e-02, 8.03512800e-03, 1.21787926e-02, 4.27130330e-03], - [-5.94401825e-03, 4.23503201e-03, -7.39302021e-03, 3.84659087e-03, - 2.92047067e-03, -2.28955783e-03, 7.80778937e-05, 7.74920732e-03, - -1.29534695e-02, -1.44997425e-02, 3.00848205e-03, -1.33561785e-04, - 7.31927902e-03, -2.24683899e-03, -6.27679843e-03, -5.35295857e-03, - -5.39031485e-03, -4.90641687e-05, 4.03603073e-03, -1.08133641e-03, - 9.59445070e-03, 9.81783494e-03, 8.77558347e-03, -5.13678743e-03, - 7.19959754e-03, 3.93835502e-03, -6.01979066e-03, 6.13247836e-03, - 1.39782019e-03, 4.60287556e-04, 1.04263611e-02, -9.61792190e-03, - -1.02399308e-02, 8.54056142e-03, -1.22422148e-02, 6.58972748e-03, - 3.18149826e-03, -2.79453350e-03, -9.98417381e-04, 1.77927073e-02, - -2.28664111e-02, -2.73113251e-02, 6.44177478e-03, -5.66864444e-04, - 1.58752780e-02, 2.18148530e-03, -1.31809842e-02, -9.98921506e-03, - -9.63711366e-03, 1.11398206e-03, 4.28507291e-03, -3.02007422e-04, - 1.06751733e-02, 1.15796775e-02, 1.35387452e-02, -1.02765551e-02, - 1.11750513e-02, 4.31185029e-03, -1.04119312e-02, 8.54373723e-03, - 4.97616245e-04, -3.82199232e-03, 2.10159980e-02, -1.68744288e-02]] - # pylint: enable=bad-whitespace - # pyformat: enable - enc_out_sum_val = enc_out_sum.eval() - print('expected enc_out_sum_val', enc_out_sum_val) - self.assertAllClose(expected_enc_out, enc_out_sum_val) - - def testForwardPassWithStackingAfterMiddleLayer(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.stacking_layer_tpl.left_context = 1 - p.stacking_layer_tpl.right_context = 0 - p.stacking_layer_tpl.stride = 2 - p.layer_index_before_stacking = 0 - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out, 0) - - self.evaluate(tf.global_variables_initializer()) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_enc_out = [ - [0.00102275, -0.02697385, 0.01709868, -0.00939053, -0.01576837, - 0.0070826, -0.00626193, 0.01143604, -0.01742513, -0.00529445, - 0.00284249, -0.01362027, -0.00490865, 0.0216262, -0.01344598, - -0.00460993, -0.01329017, 0.01379208, -0.00850593, 0.0193335, - 0.01134925, -0.00131254, 0.00375953, -0.00588882, 0.01347932, - -0.00252493, 0.01274828, 0.01027388, 0.02657663, 0.02644286, - 0.0286899, -0.00833998], - [-0.01801126, 0.0115137, 0.01355767, 0.00113954, 0.00986663, - -0.0128988, 0.00794239, -0.00524312, 0.00246279, -0.00575782, - -0.00213567, -0.01528412, 0.00186096, 0.00253562, -0.00411006, - -0.00390748, -0.01001569, -0.00344393, -0.01211706, 0.00387725, - 0.02194905, 0.02578988, -0.00255773, 0.00690117, 0.00976908, - 0.01935913, 0.01131854, 0.0013859, -0.01567556, 0.01858256, - 0.02251371, -0.0185001]] - # pylint: enable=bad-whitespace - # pyformat: enable - enc_out_sum_val = enc_out_sum.eval() - print('enc_out_sum_val', np.array_repr(enc_out_sum_val)) - self.assertAllClose(expected_enc_out, enc_out_sum_val) - - def testForwardPassWithHighwaySkip(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.residual_start = 1 - p.residual_stride = 2 - p.highway_skip = True - enc_out = self._ForwardPass(p).encoded - enc_out_sum = tf.reduce_sum(enc_out) - self.evaluate(tf.global_variables_initializer()) - - enc_out_sum_val = enc_out_sum.eval() - print('expected enc_out_sum_val', enc_out_sum_val) - test_utils.CompareToGoldenSingleFloat( - self, 65.77313995361328, enc_out_sum_val) - - - def testForwardPassWithExtraPerLayerOutputs(self): - with self.session(use_gpu=False): - vn_config = py_utils.VariationalNoiseParams(None, False, False) - p = self._EncoderParams(vn_config) - p.num_conv_lstm_layers = 1 - p.extra_per_layer_outputs = True - enc_out = self._ForwardPass(p) - regular_encoded_sum = tf.reduce_sum(enc_out.encoded) - conv_0_encoded_sum = tf.reduce_sum(enc_out.conv_0.encoded) - conv_1_encoded_sum = tf.reduce_sum(enc_out.conv_1.encoded) - conv_lstm_0_encoded_sum = tf.reduce_sum(enc_out.conv_lstm_0.encoded) - rnn_0_encoded_sum = tf.reduce_sum(enc_out.rnn_0.encoded) - - self.evaluate(tf.global_variables_initializer()) - - # pyformat: disable - self.assertAllEqual(tf.shape(enc_out.conv_0.encoded).eval(), - [13, 2, 8, 6]) - self.assertAllEqual(tf.shape(enc_out.conv_0.padding).eval(), - [13, 2]) - self.assertAllEqual(tf.shape(enc_out.conv_1.encoded).eval(), - [7, 2, 4, 6]) - self.assertAllEqual(tf.shape(enc_out.conv_1.padding).eval(), - [7, 2]) - self.assertAllEqual(tf.shape(enc_out.conv_lstm_0.encoded).eval(), - [7, 2, 4, 6]) - self.assertAllEqual(tf.shape(enc_out.conv_lstm_0.padding).eval(), - [7, 2]) - self.assertAllEqual(tf.shape(enc_out.rnn_0.encoded).eval(), - [7, 2, 32]) - self.assertAllEqual(tf.shape(enc_out.rnn_0.padding).eval(), - [7, 2]) - self.assertAllEqual(tf.shape(enc_out.encoded).eval(), - [7, 2, 32]) - self.assertAllEqual(tf.shape(enc_out.padding).eval(), - [7, 2]) - # pyformat: enable - - test_utils.CompareToGoldenSingleFloat(self, 371.75390625, - conv_0_encoded_sum.eval()) - test_utils.CompareToGoldenSingleFloat(self, 92.5332946777, - conv_1_encoded_sum.eval()) - test_utils.CompareToGoldenSingleFloat(self, 80.975112915, - conv_lstm_0_encoded_sum.eval()) - test_utils.CompareToGoldenSingleFloat(self, 10.9648704529, - rnn_0_encoded_sum.eval()) - test_utils.CompareToGoldenSingleFloat(self, 0.0522322505713, - regular_encoded_sum.eval()) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/asr/encoder_v2.py b/lingvo/tasks/asr/encoder_v2.py deleted file mode 100644 index 1a5a21af..00000000 --- a/lingvo/tasks/asr/encoder_v2.py +++ /dev/null @@ -1,127 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Encoders for the speech model.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core import spectrum_augmenter -from lingvo.tasks.asr import blocks - - -class AsrEncoder(base_layer.BaseLayer): - """Speech encoder version 2.""" - - @classmethod - def Params(cls): - """Configs for AsrEncoder.""" - p = super().Params() - - # spec-augment - p.Define('specaugment_network', - spectrum_augmenter.SpectrumAugmenter.Params(), - 'Configs template for the augmentation network.') - p.Define('use_specaugment', False, 'Use specaugmentation or not.') - - # temporal downsampling, use one of the two - p.Define('conv_subsampler', blocks.ConvolutionalDownsampler.Params(), - 'Convolution subsampling layer params') - p.Define('stacking_subsampler', blocks.InputStackingDownsampler.Params(), - 'Stacking subsampling layer params') - p.Define('use_conv_subsampler', False, 'Enable p.conv_subsampler') - p.Define('use_stacking_subsampler', False, 'Enable p.stacking_subsampler') - - # actual encoding layers, use one of these - p.Define('lstm_block', blocks.LSTMBlock.Params(), 'LSTM layer params') - # p.Define('conformer_block', blocks.ConformerBlock.Params(), 'Conformer specs') - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - ##### Use specAugment or not #### - if p.use_specaugment: - self.CreateChild('specaugment', p.specaugment_network.Copy()) - - ##### handle sub-sampling #### - - assert not (p.use_conv_subsampler and p.use_stacking_subsampler), \ - 'Please use only one form of time subsampling' - - if p.use_conv_subsampler: - self.CreateChild('sub', p.conv_subsampler.Copy()) - else: - assert p.use_stacking_subsampler, 'Need one stacking module' - self.CreateChild('sub', p.stacking_subsampler.Copy()) - stack_out_feats = self.sub.output_dim - - ##### handle encoding ##### - if p.lstm_block is not None: - if p.lstm_block.input_feats is None: - p.lstm_block.input_feats = stack_out_feats - - assert p.lstm_block.input_feats == stack_out_feats - self.CreateChildren('enc', p.lstm_block.Copy()) - - @property - def output_dim(self): - return self.enc.output_dim - - @property - def _use_functional(self): - return True - - @property - def supports_streaming(self): - return False - - def zero_state(self, theta, batch_size): - return py_utils.NestedMap() - - def FProp(self, theta, batch, state0=None): - """Encodes source as represented by 'inputs' and 'paddings'. - Args: - theta: A NestedMap object containing weights' values of this - layer and its children layers. - batch: A NestedMap with fields: - - src_inputs - The inputs tensor. It is expected to be of shape [batch, - time, feature_dim, channels]. - - paddings - The paddings tensor. It is expected to be of shape [batch, - time]. - state0: Recurrent input state. Not supported/ignored by this encoder. - Returns: - A NestedMap containing - - 'encoded': a feature tensor of shape [time, batch, depth] - - 'padding': a 0/1 tensor of shape [time, batch] - - 'state': the updated recurrent state - """ - p = self.params - inputs, paddings = batch.src_inputs, batch.paddings - - with tf.name_scope(p.name): - - if p.use_specaugment and not self.do_eval: - inputs, paddings = self.specaugment.FProp(theta.specaugment, inputs, - paddings) - - inputs, paddings = self.sub.FProp(theta.sub, inputs, paddings) - - encoded, padding = self.enc.FProp(theta.enc, inputs, paddings) - - return py_utils.NestedMap(encoded=encoded, - padding=padding, - state=py_utils.NestedMap()) diff --git a/lingvo/tasks/asr/frontend.py b/lingvo/tasks/asr/frontend.py deleted file mode 100644 index 1184bb10..00000000 --- a/lingvo/tasks/asr/frontend.py +++ /dev/null @@ -1,480 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Layers to construct an ASR frontend.""" - -import collections -import math -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils - - -# AsrFrontendConfig which defines characteristics of the frontend that may -# be relevant to interfacing code which needs to reason about inputs and -# outputs. -# Fields: -# is_null: Whether this is the NullAsrFrontend. -# src_type: Interpretation of the src_inputs. Can be one of 'none' or 'pcm'. -# src_pcm_scale: If src_type is 'pcm', then this is the scale of each sample. -# If normalized, this should be 1.0. If unnormalized from int16, then it -# should be 32768.0. -# src_pcm_sample_rate: Sample rate of the expected src PCM frames. -# output_dim: Dimension of the output. Typically the number of mel bands -# or equiv. May be -1 for unknown. -# input_frame_ratio: Approximate ratio of the number of -# input_frames / output_frames. Intended to be multiplied by output frames -# (i.e. as part of bucket_bounds to arrive at input frames to the frontend). -AsrFrontendConfig = collections.namedtuple('AsrFrontendConfig', [ - 'is_null', - 'src_type', - 'src_pcm_scale', - 'src_pcm_sample_rate', - 'output_dim', - 'input_frame_ratio', -]) - - -def _NextPowerOfTwo(i): - return math.pow(2, math.ceil(math.log(i, 2))) - - -class BaseAsrFrontend(base_layer.BaseLayer): - """Base class for ASR frontends. - - An ASR frontend is responsible for performing feature extraction from the - input in the cases where features are not precomputed as part of the - dataset. In such cases, it would be typical for the input to consist of - waveform data in some form. - """ - - @property - def config(self): - """Returns the AsrFrontendConfig namedtuple for this instance.""" - return self.GetConfigFromParams(self.params) - - @staticmethod - def GetConfigFromParams(params): - """Returns an AsrFrontendConfig namedtuple with vital config settings.""" - raise NotImplementedError() - - def FProp(self, theta, input_batch): - """Generates ASR features for a batch. - - Shapes of the input_batch and output are dependent on the implementation - and should be paired with the model's input format and encoder expectations. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - input_batch: A NestedMap with fields: - - - 'src_inputs' - The inputs tensor, - compatible with model input. Expected to be of shape - [batch, time, ...]. - - 'paddings' - The paddings tensor. It is expected to be of shape - [batch, time]. - - Returns: - NestedMap of encoder inputs which can be passed directly to a - compatible encoder and contains: - - - 'src_inputs': inputs to the encoder, minimally of shape - [batch, time, ...]. - - 'paddings': a 0/1 tensor of shape [batch, time]. - """ - raise NotImplementedError() - - -class NullAsrFrontend(BaseAsrFrontend): - """ASR frontend that just returns its input as FProp output.""" - - @staticmethod - def GetConfigFromParams(params): - """Returns an AsrFrontendConfig namedtuple with vital config settings.""" - return AsrFrontendConfig( - is_null=True, - src_type='none', - src_pcm_sample_rate=-1, - src_pcm_scale=1.0, - output_dim=-1, - input_frame_ratio=1.0) - - def FProp(self, theta, input_batch): - return input_batch.DeepCopy() - - -class MelAsrFrontend(BaseAsrFrontend): - """An AsrFrontend that implements mel feature extraction from PCM frames. - - This is expressed in pure TensorFlow and without reference to external - resources. - - The frontend implements the following stages: - `Framer -> Window -> FFT -> FilterBank -> MeanStdDev -> SubSample` - - The FProp input to this layer can either have rank 3 or rank 4 shape: - [batch_size, timestep, packet_size, channel_count] - [batch_size, timestep * packet_size, channel_count] - - For compatibility with existing code, 2D [batch_size, timestep] mono shapes - are also supported. - - In the common case, the packet_size is 1. The 4D variant is accepted for - glueless interface to input generators that frame their input samples in - some way. The external framing choice does not influence the operation of - this instance, but it is accepted. - - TODO(laurenzo): Refactor call sites to uniformly use the 4D variant and - eliminate fallback logic in this class. - - Only 1 channel is currently supported. - TODO(laurenzo): Refactor this class to operate on multi-channel inputs. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'frontend' - p.Define('sample_rate', 16000.0, 'Sample rate in Hz') - p.Define('channel_count', 1, 'Number of channels.') - p.Define('frame_size_ms', 25.0, - 'Amount of data grabbed for each frame during analysis') - p.Define('frame_step_ms', 10.0, 'Number of ms to jump between frames') - p.Define('num_bins', 80, 'Number of bins in the mel-spectrogram output') - p.Define('lower_edge_hertz', 125.0, - 'The lowest frequency of the mel-spectrogram analsis') - p.Define('upper_edge_hertz', 7600.0, - 'The highest frequency of the mel-spectrogram analsis') - p.Define('preemph', 0.97, - 'The first-order filter coefficient used for preemphasis') - p.Define('noise_scale', 8.0, - 'The amount of noise (in 16-bit LSB units) to add') - p.Define('window_fn', 'HANNING', - 'Window function to apply (valid values are "HANNING", and None)') - p.Define( - 'pad_end', False, - 'Whether to pad the end of `signals` with zeros when the provided ' - 'frame length and step produces a frame that lies partially past ' - 'its end.') - p.Define( - 'per_bin_mean', None, - 'Per-bin (num_bins) means for normalizing the spectrograms. ' - 'Defaults to zeros.') - p.Define('per_bin_stddev', None, - 'Per-bin (num_bins) standard deviations. Defaults to ones.') - p.Define('stack_left_context', 0, 'Number of left context frames to stack.') - p.Define('stack_right_context', 0, - 'Number of right context frames to stack.') - p.Define('frame_stride', 1, 'The frame stride for sub-sampling.') - - return p - - @staticmethod - def GetConfigFromParams(params): - """Returns an AsrFrontendConfig namedtuple with vital config settings.""" - context_size = params.stack_left_context + params.stack_right_context + 1 - subsample_factor = params.num_bins * context_size - frame_step = round(params.sample_rate * params.frame_step_ms / 1000.0) - return AsrFrontendConfig( - is_null=False, - src_type='pcm', - src_pcm_scale=32768.0, - src_pcm_sample_rate=16000.0, - output_dim=subsample_factor, - input_frame_ratio=frame_step * subsample_factor) - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.frame_stride < 1: - raise ValueError('frame_stride must be positive.') - - assert p.channel_count == 1, 'Only 1 channel currently supported.' - # Make sure key params are in floating point. - p.sample_rate = float(p.sample_rate) - p.frame_step_ms = float(p.frame_step_ms) - p.frame_size_ms = float(p.frame_size_ms) - p.lower_edge_hertz = float(p.lower_edge_hertz) - p.upper_edge_hertz = float(p.upper_edge_hertz) - - self._frame_step = int(round(p.sample_rate * p.frame_step_ms / 1000.0)) - self._frame_size = (int(round(p.sample_rate * p.frame_size_ms / 1000.0)) + 1 - ) # +1 for the preemph - # Overdrive means double FFT size. - # Note: 2* because of overdrive - self._fft_size = 2 * int(max(512, _NextPowerOfTwo(self._frame_size))) - - self._CreateWindowFunction() - - # Mean/stddev. - if p.per_bin_mean is None: - p.per_bin_mean = [0.0] * p.num_bins - if p.per_bin_stddev is None: - p.per_bin_stddev = [1.0] * p.num_bins - assert len(p.per_bin_mean) == p.num_bins - assert len(p.per_bin_stddev) == p.num_bins - - def _CreateWindowFunction(self): - p = self.params - if p.window_fn is None: - self._window_fn = None - elif p.window_fn == 'HANNING': - - def _HanningWindow(frame_size, dtype): - return tf.signal.hann_window(frame_size, dtype=dtype) - - self._window_fn = _HanningWindow - else: - raise ValueError('Illegal value %r for window_fn param' % p.window_fn) - - @property - def window_frame_size(self): - return self._frame_size - - @property - def window_frame_step(self): - return self._frame_step - - def _RemoveChannelDim(self, pcm_audio_data): - if pcm_audio_data.shape.rank == 3: - pcm_audio_data = tf.squeeze(pcm_audio_data, 2) - assert pcm_audio_data.shape.rank == 2, ( - 'MelAsrFrontend only supports one channel') - return pcm_audio_data - - def _ReshapeToMono2D(self, pcm_audio_data, paddings): - """Reshapes a 3D or 4D input to 2D. - - Since the input to FProp can be 3D or 4D (see class comments), this will - collapse it back to a 2D, mono shape for internal processing. - - Args: - pcm_audio_data: 2D, 3D or 4D audio input. See class comments. Must have a - rank. - paddings: Original paddings shaped to the first two dims of - pcm_audio_data. - - Returns: - Tuple of 2D [batch_size, timestep] mono audio data, new paddings. - """ - shape = py_utils.GetShape(pcm_audio_data) - rank = len(shape) - if rank == 2: - return pcm_audio_data, paddings - elif rank == 3: - # [batch, time, channel] - with tf.control_dependencies([tf.assert_equal(shape[2], 1)]): - return tf.squeeze(pcm_audio_data, axis=2), paddings - elif rank == 4: - # [batch, time, packet, channel] - batch_size, orig_time, orig_packet_size, channel = shape - time = orig_time * orig_packet_size - with tf.control_dependencies([tf.assert_equal(channel, 1)]): - pcm_audio_data = tf.reshape(pcm_audio_data, (batch_size, time)) - # Transform paddings into the new time base with a padding per time - # step vs per packet by duplicating each packet. - paddings = tf.reshape( - tf.tile(tf.expand_dims(paddings, axis=2), [1, 1, orig_packet_size]), - (batch_size, time)) - return pcm_audio_data, paddings - else: - raise ValueError('Illegal pcm_audio_data shape') - - def FProp(self, theta, input_batch): - """Perform signal processing on a sequence of PCM data. - - NOTE: This implementation does not currently support paddings, and they - are accepted for compatibility with the super-class. - - TODO(laurenzo): Rework this to support paddings. - - Args: - theta: Layer theta. - input_batch: PCM input map: - - # int16 and float32 are both permissible - - 'src_inputs': int16 or float32 tensor of PCM audio data, scaled to - +/-32768 (versus [-1..1)!). See class comments for supported input - shapes. - - 'paddings': per frame 0/1 paddings. Shaped: [batch, frame]. - Returns: - NestedMap of encoder inputs which can be passed directly to a - compatible encoder and contains: - - - 'src_inputs': inputs to the encoder, minimally of shape - [batch, time, ...]. - - 'paddings': a 0/1 tensor of shape [batch, time]. - """ - - return self._FPropDefault(input_batch) - - def _FPropDefault(self, input_batch): - pcm_audio_data, pcm_audio_paddings = self._ReshapeToMono2D( - input_batch.src_inputs, input_batch.paddings) - - mel_spectrogram, mel_spectrogram_paddings = self._FPropChunk( - pcm_audio_data, pcm_audio_paddings) - - mel_spectrogram, mel_spectrogram_paddings = self._PadAndReshapeSpec( - mel_spectrogram, mel_spectrogram_paddings) - - return py_utils.NestedMap( - src_inputs=mel_spectrogram, paddings=mel_spectrogram_paddings) - - def _StackSignal(self, signal, stack_size, stride): - signal = tf.signal.frame( - signal=signal, - frame_length=stack_size, - frame_step=stride, - pad_end=False, - axis=1, - ) - signal = tf.reshape(signal, py_utils.GetShape(signal)[:2] + [-1]) - return signal - - def _PadAndReshapeSpec(self, mel_spectrogram, mel_spectrogram_paddings): - p = self.params - # Stack and sub-sample. - stack_size = 1 - if p.stack_left_context > 0: - # Since left context is leading, pad the left by duplicating the first - # frame. - stack_size += p.stack_left_context - mel_spectrogram = tf.concat( - [mel_spectrogram[:, 0:1, :]] * p.stack_left_context + - [mel_spectrogram], - axis=1) - mel_spectrogram_paddings = tf.concat( - [mel_spectrogram_paddings[:, 0:1]] * p.stack_left_context + - [mel_spectrogram_paddings], - axis=1) - - if p.stack_right_context > 0: - stack_size += p.stack_right_context - mel_spectrogram = tf.concat( - [mel_spectrogram] + - [mel_spectrogram[:, -1:, :]] * p.stack_right_context, - axis=1) - mel_spectrogram_paddings = tf.concat( - [mel_spectrogram_paddings] + - [mel_spectrogram_paddings[:, -1:]] * p.stack_right_context, - axis=1) - - if p.stack_left_context or p.stack_right_context: - mel_spectrogram = self._StackSignal(mel_spectrogram, stack_size, - p.frame_stride) - mel_spectrogram_paddings = self._StackSignal(mel_spectrogram_paddings, - stack_size, p.frame_stride) - # After stacking paddings, pad if any source frame was padded. - # Stacks into [batch_size, stacked_frame_dim, stack_size] like the - # spectrogram stacking above, and then reduces the stack_size dim - # to the max (effectively, making padding = 1.0 if any of the pre-stacked - # frames were 1.0). Final shape is [batch_size, stacked_frame_dim]. - mel_spectrogram_paddings = tf.reduce_max(mel_spectrogram_paddings, axis=2) - - # Add feature dim. Shape = [batch, time, features, 1] - mel_spectrogram = tf.expand_dims(mel_spectrogram, -1) - return mel_spectrogram, mel_spectrogram_paddings - - def _ApplyPreemphasis(self, framed_signal): - p = self.params - preemphasized = ( - framed_signal[:, :, 1:] - p.preemph * framed_signal[:, :, 0:-1]) - return preemphasized - - def _GetMelPadding(self, pcm_audio_paddings): - p = self.params - # shape: [batch, time, _frame_size] - framed_paddings = tf.signal.frame(pcm_audio_paddings, self._frame_size, - self._frame_step, p.pad_end) - # Pad spectrograms that have any padded frames. - mel_spectrogram_paddings = tf.reduce_max(framed_paddings, axis=2) - return mel_spectrogram_paddings - - def _FPropChunk(self, pcm_audio_chunk, pcm_audio_paddings): - p = self.params - pcm_audio_chunk = tf.cast(pcm_audio_chunk, tf.float32) - # shape: [batch, time, _frame_size] - framed_signal = tf.signal.frame(pcm_audio_chunk, self._frame_size, - self._frame_step, p.pad_end) - - # Pre-emphasis. - if p.preemph != 1.0: - preemphasized = self._ApplyPreemphasis(framed_signal) - else: - preemphasized = framed_signal[:-1] - - # Noise. - if p.noise_scale > 0.0: - noise_signal = tf.random.normal( - tf.shape(preemphasized), - stddev=p.noise_scale, - mean=0.0, - seed=p.random_seed) - else: - noise_signal = 0.0 - - # Apply window fn. - windowed_signal = preemphasized + noise_signal - if self._window_fn is not None: - window = self._window_fn(self._frame_size - 1, framed_signal.dtype) - windowed_signal *= window - - mel_spectrogram = self._MelSpectrogram(windowed_signal) - - output_floor = 1.0 - mel_spectrogram_log = tf.math.log( - tf.maximum(float(output_floor), mel_spectrogram)) - - # Mean and stddev. - mel_spectrogram_norm = ( - (mel_spectrogram_log - tf.convert_to_tensor(p.per_bin_mean)) / - tf.convert_to_tensor(p.per_bin_stddev)) - return mel_spectrogram_norm, self._GetMelPadding(pcm_audio_paddings) - - def _MelSpectrogram(self, signal): - """Computes the mel spectrogram from a waveform signal. - - Args: - signal: f32 Tensor, shaped [batch_size, num_samples] - - Returns: - f32 features Tensor, shaped [batch_size, num_frames, mel_channels] - """ - p = self.params - # FFT. - real_frequency_spectrogram = tf.signal.rfft(signal, [self._fft_size]) - magnitude_spectrogram = tf.abs(real_frequency_spectrogram) - - # Shape of magnitude_spectrogram is num_frames x (fft_size/2+1) - # Mel_weight is [num_spectrogram_bins, num_mel_bins] - mel_weight_matrix = tf.signal.linear_to_mel_weight_matrix( - num_mel_bins=p.num_bins, - num_spectrogram_bins=self._fft_size // 2 + 1, - sample_rate=p.sample_rate, - lower_edge_hertz=p.lower_edge_hertz, - upper_edge_hertz=p.upper_edge_hertz, - dtype=tf.float32) - # Weight matrix implemented in the magnitude domain. - batch_size, num_frames, fft_channels = py_utils.GetShape( - magnitude_spectrogram, 3) - mel_spectrogram = tf.matmul( - tf.reshape(magnitude_spectrogram, - [batch_size * num_frames, fft_channels]), mel_weight_matrix) - mel_spectrogram = tf.reshape(mel_spectrogram, - [batch_size, num_frames, p.num_bins]) - - return mel_spectrogram diff --git a/lingvo/tasks/asr/frontend_test.py b/lingvo/tasks/asr/frontend_test.py deleted file mode 100644 index 458f82a4..00000000 --- a/lingvo/tasks/asr/frontend_test.py +++ /dev/null @@ -1,480 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for asr frontend.""" - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tasks.asr import frontend -import numpy as np - - -class AsrFrontendTest(test_utils.TestCase): - - def _GetPcm(self): - """Gets sample wav file pcm samples. - - Returns: - (sample_rate, mono_audio) where mono_audio is of shape - [batch (=1), samples]. - """ - with open( - test_helper.test_src_dir_path('tools/testdata/gan_or_vae.wav'), - 'rb') as f: - wavdata = f.read() - result = tf.audio.decode_wav(wavdata) - # Remove the last dimension: channel is 1. - audio = py_utils.HasShape(result.audio, [75900, 1]) - audio = tf.squeeze(audio, axis=1) - # Returns audio as batch-major data with a single batch. - return result.sample_rate, tf.expand_dims(audio, axis=0) - - def _CreateFrontendParams(self): - p = frontend.MelAsrFrontend.Params() - p.sample_rate = 24000. - p.num_bins = 2 - p.noise_scale = 0. - self.params = p - - def testNullAsrFrontendConfig(self): - p = frontend.NullAsrFrontend.Params() - p.name = 'null' - fe = p.Instantiate() - config = fe.config - self.assertTrue(config.is_null) - self.assertEqual(config.src_type, 'none') - self.assertEqual(config.output_dim, -1) - self.assertEqual(config.input_frame_ratio, 1.0) - - def testMelFeaturesUnstackedConfig(self): - self._CreateFrontendParams() - p = self.params - fe = p.Instantiate() - config = fe.config - self.assertFalse(config.is_null) - self.assertEqual(config.src_type, 'pcm') - self.assertEqual(config.src_pcm_scale, 32768.0) - self.assertEqual(config.src_pcm_sample_rate, 16000.0) - self.assertEqual(config.output_dim, 2) - # Approx 34 output frames per second. - self.assertEqual(config.input_frame_ratio, 480.0) - - def testMelFeaturesLeftStackedConfig(self): - self._CreateFrontendParams() - p = self.params - p.stack_left_context = 2 - p.frame_stride = p.stack_left_context + 1 - fe = p.Instantiate() - config = fe.config - self.assertFalse(config.is_null) - self.assertEqual(config.src_type, 'pcm') - self.assertEqual(config.src_pcm_scale, 32768.0) - self.assertEqual(config.src_pcm_sample_rate, 16000.0) - self.assertEqual(config.output_dim, 6) - # Approx 12 output frames per second. - self.assertEqual(config.input_frame_ratio, 1440.0) - - def testMelFeaturesLeftRightStackedConfig(self): - self._CreateFrontendParams() - p = self.params - p.stack_right_context = 2 - p.stack_left_context = 2 - p.frame_stride = p.stack_right_context + p.stack_left_context + 1 - fe = p.Instantiate() - config = fe.config - # Approx 6 output frames per second. - self.assertEqual(config.input_frame_ratio, 2400.0) - - def testMelFeaturesUnstacked(self): - self._CreateFrontendParams() - p = self.params - mel_frontend = p.Instantiate() - sample_rate, pcm = self._GetPcm() - pcm *= 32768 - - # Convert to 4D [batch, time, packet, channels]. - sample_count = tf.shape(pcm)[1] - packet_size = 11 # A non-round number. - trimmed_pcm = pcm[:, 0:(sample_count // packet_size) * packet_size] - src_inputs = tf.reshape(trimmed_pcm, (1, -1, packet_size, 1)) - paddings = tf.zeros(tf.shape(src_inputs)[0:2]) - - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=src_inputs, paddings=paddings)) - log_mel = outputs.src_inputs - paddings = outputs.paddings - with self.session(): - pcm = self.evaluate(pcm) - tf.logging.info('pcm: ~ %s = %s', pcm.shape, pcm) - self.assertGreater(33000, np.amax(pcm)) - self.assertGreater(np.amax(pcm), 2.) - log_mel, paddings, sample_rate = self.evaluate( - [log_mel, paddings, sample_rate]) - self.assertEqual(sample_rate, p.sample_rate) - self.assertEqual(paddings.shape, log_mel.shape[0:2]) - self.assertAllEqual(paddings, np.zeros_like(paddings)) - # log_mel ~ [batch, time, feature_size, channel] - tf.logging.info('mel ~ %s', log_mel.shape) - self.assertEqual(log_mel.shape[2], 2) # 2 bins - # Squeeze the batch and channel dimensions out. - log_mel = np.squeeze(log_mel, axis=(0, 3)) - t = log_mel.shape[0] - mu = np.sum(log_mel, axis=0) / t - d = log_mel - mu - v = np.sum(d * d, axis=0) / (t - 1) - s = np.sqrt(v) - tf.logging.info('Found mean = %s', mu) - tf.logging.info('Found stddev = %s', s) - ref_unstacked_mean = [13.46184731, 13.30099297] - ref_unstacked_stddev = [1.3840059, 1.24434352] - self.assertAllClose(mu, ref_unstacked_mean, atol=1e-4) - self.assertAllClose(s, ref_unstacked_stddev, atol=1e-3) - - def testMelFeaturesLeftStacked(self): - self._CreateFrontendParams() - p = self.params - p.stack_left_context = 2 - p.frame_stride = p.stack_left_context + 1 - mel_frontend = p.Instantiate() - sample_rate, pcm = self._GetPcm() - pcm *= 32768 - - # Convert to 4D [batch, time, packet, channels]. - sample_count = tf.shape(pcm)[1] - packet_size = 11 # A non-round number. - trimmed_pcm = pcm[:, 0:(sample_count // packet_size) * packet_size] - src_inputs = tf.reshape(trimmed_pcm, (1, -1, packet_size, 1)) - paddings = tf.zeros(tf.shape(src_inputs)[0:2]) - - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=src_inputs, paddings=paddings)) - log_mel = outputs.src_inputs - paddings = outputs.paddings - with self.session(): - pcm = self.evaluate(pcm) - tf.logging.info('pcm: ~ %s = %s', pcm.shape, pcm) - self.assertGreater(33000, np.amax(pcm)) - self.assertGreater(np.amax(pcm), 2.) - log_mel, paddings, sample_rate = self.evaluate( - [log_mel, paddings, sample_rate]) - self.assertEqual(sample_rate, p.sample_rate) - self.assertEqual(paddings.shape, log_mel.shape[0:2]) - self.assertAllEqual(paddings, np.zeros_like(paddings)) - # log_mel ~ [batch, time, feature_size, channel] - tf.logging.info('mel ~ %s', log_mel.shape) - # Squeeze the batch and channel dimensions out. - log_mel = np.squeeze(log_mel, axis=(0, 3)) - t = log_mel.shape[0] - mu = np.sum(log_mel, axis=0) / t - d = log_mel - mu - v = np.sum(d * d, axis=0) / (t - 1) - s = np.sqrt(v) - tf.logging.info('Found mean = %s', mu) - tf.logging.info('Found stddev = %s', s) - ref_mean = (13.38236332, 13.2698698, 13.45229626, 13.26469517, - 13.46731281, 13.31649303) - ref_stddev = (1.52104115, 1.27433181, 1.41266346, 1.27072334, 1.41251481, - 1.28583682) - self.assertAllClose(mu, ref_mean, atol=1e-4) - self.assertAllClose(s, ref_stddev, atol=1e-3) - - def testMelFeaturesRightStacked(self): - self._CreateFrontendParams() - p = self.params - p.stack_right_context = 2 - p.frame_stride = p.stack_right_context + 1 - mel_frontend = p.Instantiate() - sample_rate, pcm = self._GetPcm() - pcm *= 32768 - - # Convert to 4D [batch, time, packet, channels]. - sample_count = tf.shape(pcm)[1] - packet_size = 11 # A non-round number. - trimmed_pcm = pcm[:, 0:(sample_count // packet_size) * packet_size] - src_inputs = tf.reshape(trimmed_pcm, (1, -1, packet_size, 1)) - paddings = tf.zeros(tf.shape(src_inputs)[0:2]) - - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=src_inputs, paddings=paddings)) - log_mel = outputs.src_inputs - paddings = outputs.paddings - with self.session(): - pcm = self.evaluate(pcm) - tf.logging.info('pcm: ~ %s = %s', pcm.shape, pcm) - self.assertGreater(33000, np.amax(pcm)) - self.assertGreater(np.amax(pcm), 2.) - log_mel, paddings, sample_rate = self.evaluate( - [log_mel, paddings, sample_rate]) - self.assertEqual(sample_rate, p.sample_rate) - self.assertEqual(paddings.shape, log_mel.shape[0:2]) - self.assertAllEqual(paddings, np.zeros_like(paddings)) - # log_mel ~ [batch, time, feature_size, channel] - tf.logging.info('mel ~ %s', log_mel.shape) - # Squeeze the batch and channel dimensions out. - log_mel = np.squeeze(log_mel, axis=(0, 3)) - t = log_mel.shape[0] - mu = np.sum(log_mel, axis=0) / t - d = log_mel - mu - v = np.sum(d * d, axis=0) / (t - 1) - s = np.sqrt(v) - tf.logging.info('Found mean = %s', mu) - tf.logging.info('Found stddev = %s', s) - ref_mean = (13.46731281, 13.31649303, 13.41263676, 13.28540039, - 13.48256969, 13.2802248) - ref_stddev = (1.41251481, 1.28583682, 1.43964291, 1.23710775, 1.32300735, - 1.23345602) - self.assertAllClose(mu, ref_mean, atol=1e-4) - self.assertAllClose(s, ref_stddev, atol=1e-3) - - def testMelFeaturesPaddedLeftStacked(self): - self._CreateFrontendParams() - p = self.params - p.stack_left_context = 2 - p.frame_stride = p.stack_left_context + 1 - mel_frontend = p.Instantiate() - sample_rate, pcm = self._GetPcm() - pcm *= 32768 - - # Convert to 4D [batch, time, packet, channels]. - sample_count = tf.shape(pcm)[1] - packet_size = 11 # A non-round number. - trimmed_pcm = pcm[:, 0:(sample_count // packet_size) * packet_size] - src_inputs = tf.reshape(trimmed_pcm, (1, -1, packet_size, 1)) - - # Create paddings such that the first 455 packets are unpadded. - paddings = tf.concat([ - tf.zeros([1, 455], dtype=tf.float32), - tf.ones([1, tf.shape(src_inputs)[1] - 455], dtype=tf.float32) - ], - axis=1) - # frame_step=240, frame_size=601, +1202 left padded frames - # 455 packets * 11 frames rounds = 5005 frames, rounds down to 21 mel - # frames. Divide by 3 for stacking = 7. - expected_unpadded = 7 - - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=src_inputs, paddings=paddings)) - log_mel = outputs.src_inputs - paddings = outputs.paddings - with self.session(): - pcm = self.evaluate(pcm) - tf.logging.info('pcm: ~ %s = %s', pcm.shape, pcm) - self.assertGreater(33000, np.amax(pcm)) - self.assertGreater(np.amax(pcm), 2.) - log_mel, paddings, sample_rate = self.evaluate( - [log_mel, paddings, sample_rate]) - self.assertEqual(sample_rate, p.sample_rate) - self.assertEqual(paddings.shape, log_mel.shape[0:2]) - self.assertAllEqual(paddings[:, 0:expected_unpadded], - np.zeros([1, expected_unpadded])) - self.assertAllEqual(paddings[:, expected_unpadded:], - np.ones([1, paddings.shape[1] - expected_unpadded])) - # log_mel ~ [batch, time, feature_size, channel] - tf.logging.info('mel ~ %s', log_mel.shape) - # Squeeze the batch and channel dimensions out. - log_mel = np.squeeze(log_mel, axis=(0, 3)) - t = log_mel.shape[0] - mu = np.sum(log_mel, axis=0) / t - d = log_mel - mu - v = np.sum(d * d, axis=0) / (t - 1) - s = np.sqrt(v) - tf.logging.info('Found mean = %s', mu) - tf.logging.info('Found stddev = %s', s) - ref_mean = (13.38236332, 13.2698698, 13.45229626, 13.26469517, - 13.46731281, 13.31649303) - ref_stddev = (1.52104115, 1.27433181, 1.41266346, 1.27072334, 1.41251481, - 1.28583682) - self.assertAllClose(mu, ref_mean, atol=1e-4) - self.assertAllClose(s, ref_stddev, atol=1e-3) - - def testMelFeaturesPaddedRightStacked(self): - self._CreateFrontendParams() - p = self.params - p.stack_right_context = 2 - p.frame_stride = p.stack_right_context + 1 - mel_frontend = p.Instantiate() - sample_rate, pcm = self._GetPcm() - pcm *= 32768 - - # Convert to 4D [batch, time, packet, channels]. - sample_count = tf.shape(pcm)[1] - packet_size = 11 # A non-round number. - trimmed_pcm = pcm[:, 0:(sample_count // packet_size) * packet_size] - src_inputs = tf.reshape(trimmed_pcm, (1, -1, packet_size, 1)) - - # Create paddings such that the first 455 packets are unpadded. - paddings = tf.concat([ - tf.zeros([1, 455], dtype=tf.float32), - tf.ones([1, tf.shape(src_inputs)[1] - 455], dtype=tf.float32) - ], - axis=1) - # frame_step=240, frame_size=600, +1200 right padded frames - # 455 packets * 11 frames rounds = 5005 frames, rounds down to 21 mel - # frames. Divide by 3 for stacking = 7. - # TODO(talremez): Make sure with this makes sense. - expected_unpadded = 6 - - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=src_inputs, paddings=paddings)) - log_mel = outputs.src_inputs - paddings = outputs.paddings - - with self.session(): - pcm = self.evaluate(pcm) - tf.logging.info('pcm: ~ %s = %s', pcm.shape, pcm) - self.assertGreater(33000, np.amax(pcm)) - self.assertGreater(np.amax(pcm), 2.) - log_mel, paddings, sample_rate = self.evaluate( - [log_mel, paddings, sample_rate]) - self.assertEqual(sample_rate, p.sample_rate) - self.assertEqual(paddings.shape, log_mel.shape[0:2]) - self.assertAllEqual(paddings[:, 0:expected_unpadded], - np.zeros([1, expected_unpadded])) - self.assertAllEqual(paddings[:, expected_unpadded:], - np.ones([1, paddings.shape[1] - expected_unpadded])) - - def testMelMeanVarNormalization(self): - self._CreateFrontendParams() - p = self.params - p.stack_left_context = 2 - p.frame_stride = p.stack_left_context + 1 - ref_mean = (13.38236332, 13.2698698, 13.45229626, 13.26469517, 13.46731281, - 13.31649303) - ref_stddev = (1.52104115, 1.27433181, 1.41266346, 1.27072334, 1.41251481, - 1.28583682) - p.per_bin_mean = ref_mean[:p.num_bins] - p.per_bin_stddev = ref_stddev[:p.num_bins] - mel_frontend = p.Instantiate() - _, pcm = self._GetPcm() - pcm *= 32768 - - # Convert to 4D [batch, time, packet, channels]. - sample_count = tf.shape(pcm)[1] - packet_size = 11 # A non-round number. - trimmed_pcm = pcm[:, 0:(sample_count // packet_size) * packet_size] - src_inputs = tf.reshape(trimmed_pcm, (1, -1, packet_size, 1)) - paddings = tf.zeros(tf.shape(src_inputs)[0:2]) - - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=src_inputs, paddings=paddings)) - log_mel = outputs.src_inputs - with self.session(): - log_mel = self.evaluate(log_mel) - # log_mel ~ [batch, time, feature_size, channel] - tf.logging.info('mel ~ %s', log_mel.shape) - # Squeeze the batch and channel dimensions out. - log_mel = np.squeeze(log_mel, axis=(0, 3)) - t = log_mel.shape[0] - mu = np.sum(log_mel, axis=0) / t - d = log_mel - mu - v = np.sum(d * d, axis=0) / (t - 1) - s = np.sqrt(v) - # Only take the base bin values: - mu = mu[:p.num_bins] - s = s[:p.num_bins] - self.assertAllClose(mu, np.zeros_like(mu), atol=1e-4) - self.assertAllClose(s, np.ones_like(s), atol=1e-3) - - def testMelFeaturesUnstacked2D(self): - # TODO(laurenzo): Remove this test once 2D inputs support removed. - self._CreateFrontendParams() - p = self.params - mel_frontend = p.Instantiate() - sample_rate, pcm = self._GetPcm() - pcm *= 32768 - - # Leave in 2D [batch, time]. - src_inputs = pcm - paddings = tf.zeros(tf.shape(src_inputs)[0:2]) - - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=src_inputs, paddings=paddings)) - log_mel = outputs.src_inputs - paddings = outputs.paddings - with self.session(): - pcm = self.evaluate(pcm) - tf.logging.info('pcm: ~ %s = %s', pcm.shape, pcm) - self.assertGreater(33000, np.amax(pcm)) - self.assertGreater(np.amax(pcm), 2.) - log_mel, paddings, sample_rate = self.evaluate( - [log_mel, paddings, sample_rate]) - self.assertEqual(sample_rate, p.sample_rate) - self.assertEqual(paddings.shape, log_mel.shape[0:2]) - self.assertAllEqual(paddings, np.zeros_like(paddings)) - # log_mel ~ [batch, time, feature_size, channel] - tf.logging.info('mel ~ %s', log_mel.shape) - self.assertEqual(log_mel.shape[2], 2) # 2 bins - # Squeeze the batch and channel dimensions out. - log_mel = np.squeeze(log_mel, axis=(0, 3)) - t = log_mel.shape[0] - mu = np.sum(log_mel, axis=0) / t - d = log_mel - mu - v = np.sum(d * d, axis=0) / (t - 1) - s = np.sqrt(v) - tf.logging.info('Found mean = %s', mu) - tf.logging.info('Found stddev = %s', s) - ref_unstacked_mean = [13.46184731, 13.30099297] - ref_unstacked_stddev = [1.3840059, 1.24434352] - self.assertAllClose(mu, ref_unstacked_mean, atol=1e-4) - self.assertAllClose(s, ref_unstacked_stddev, atol=1e-3) - - def testMelFeaturesUnstacked3D(self): - # TODO(laurenzo): Remove this test once 3D inputs support removed. - self._CreateFrontendParams() - p = self.params - mel_frontend = p.Instantiate() - sample_rate, pcm = self._GetPcm() - pcm *= 32768 - - # Leave in 3D [batch, time, 1]. - src_inputs = tf.expand_dims(pcm, axis=2) - paddings = tf.zeros(tf.shape(src_inputs)[0:2]) - - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=src_inputs, paddings=paddings)) - log_mel = outputs.src_inputs - paddings = outputs.paddings - with self.session(): - pcm = self.evaluate(pcm) - tf.logging.info('pcm: ~ %s = %s', pcm.shape, pcm) - self.assertGreater(33000, np.amax(pcm)) - self.assertGreater(np.amax(pcm), 2.) - log_mel, paddings, sample_rate = self.evaluate( - [log_mel, paddings, sample_rate]) - self.assertEqual(sample_rate, p.sample_rate) - self.assertEqual(paddings.shape, log_mel.shape[0:2]) - self.assertAllEqual(paddings, np.zeros_like(paddings)) - # log_mel ~ [batch, time, feature_size, channel] - tf.logging.info('mel ~ %s', log_mel.shape) - self.assertEqual(log_mel.shape[2], 2) # 2 bins - # Squeeze the batch and channel dimensions out. - log_mel = np.squeeze(log_mel, axis=(0, 3)) - t = log_mel.shape[0] - mu = np.sum(log_mel, axis=0) / t - d = log_mel - mu - v = np.sum(d * d, axis=0) / (t - 1) - s = np.sqrt(v) - tf.logging.info('Found mean = %s', mu) - tf.logging.info('Found stddev = %s', s) - ref_unstacked_mean = [13.46184731, 13.30099297] - ref_unstacked_stddev = [1.3840059, 1.24434352] - self.assertAllClose(mu, ref_unstacked_mean, atol=1e-4) - self.assertAllClose(s, ref_unstacked_stddev, atol=1e-3) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/asr/fusion.py b/lingvo/tasks/asr/fusion.py deleted file mode 100644 index 7c06857a..00000000 --- a/lingvo/tasks/asr/fusion.py +++ /dev/null @@ -1,177 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Utilities for fusing language models with the decoder output.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.tasks.lm import layers as lm_layers - - -class FusionBase(base_layer.BaseLayer): - """Base class for fusion with LMs.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('lm', lm_layers.NullLm.Params(), 'Language model params.') - p.Define( - 'base_model_logits_dim', None, - 'Dimension of base (i.e., the model being fused with the LM) model\'s ' - 'logits.') - return p - - def __init__(self, params): - """Initializes Fusion class.""" - try: - if isinstance(params.lm.emb, (layers.EmbeddingLayer)): - params.lm.emb.on_ps = False - except AttributeError: - pass - - super().__init__(params) - - p = self.params - self.CreateChild('lm', p.lm) - - def _CreateChildrenVariables(self): - # Backwards compatibility: manually call child.InstantiateVariables() - # outside of tf.variable_scope(p.name). - self.lm.InstantiateVariables() - super()._CreateChildrenVariables() - - def zero_state(self, theta, batch_size): - """Returns initial model state for fusion model.""" - state0 = py_utils.NestedMap() - state0.lm_states = self.lm.zero_state(theta.lm, batch_size) - return state0 - - def _FPropLm(self, theta, state0, ids, paddings, misc=None): - """LM FProp. - - Works for single step or entire seq. - - Args: - theta: A NestedMap object containing weights for the layer and its - children. - state0: A NestedMap of states (specific to the layer). - ids: Target ids, of shape [batch_size] for single step unrolling or - [batch_size, base_model_logits_dim] for the entire sequence. - paddings: Target paddings, of the same shape as 'ids'. - misc: NestedMap of miscellaneous items, which might be needed during - training. - - Returns: - (lm_output, state1): - - - lm_output: A NestedMap containing lm output. If 'ids' is 1-D, then - lm_output should have shape [batch_size, dim]; if it is 2-D then the - shape should be [seq_len, batch_size, dim]. - - state1: A NestedMap of updated states. - """ - state1 = state0.DeepCopy() - is_single_step = (ids.shape.ndims == 1) - if is_single_step: - seq_len = 1 - else: - seq_len = tf.shape(ids)[0] - - self._ModifyLmBeforeFProp(theta, state0, ids, paddings, misc) - - lm_output, state1.lm_states = self.lm.FProp( - theta.lm, tf.reshape(ids, [seq_len, -1]), - tf.reshape(paddings, [seq_len, -1]), state0.lm_states) - - if is_single_step: - # lm outputs have dimension [time, batch, dim]. Since this is only one - # step, remove time dimension. - lm_output = lm_output.Transform(lambda v: tf.squeeze(v, axis=0)) - - return lm_output, state1 - - def FProp(self, theta, state0, am_output, ids, paddings, misc=None): - """Real fusion logic happens here. - - Works for single step or for the entire sequence. - - Args: - theta: A NestedMap object containing weights for the layer and its - children. - state0: A NestedMap of states (specific to the layer). - am_output: The output from the speech model. 'am_output' can have shape - [batch_size, base_model_logits_dim] for a single step unrolling or - [seq_len, batch_size, base_model_logits_dim] for the entire sequence. - ids: Target ids, of shape [batch_size] for single step unrolling or - [batch_size, base_model_logits_dim] for the entire sequence. - paddings: Target paddings, of the same shape as 'ids'. - misc: NestedMap of miscellaneous items, which might be needed during - training. - - Returns: - (fused_output, state1): - - - fused_output: A tensor containing the fused result. If am_output is 2-D, - then the fused_output should have shape [batch_size, dim]; if - am_output is 3-D, then the shape should be [seq_len, batch_size, dim]. - - state1: a NestedMap of updated states (specific to the layer). - """ - del theta, state0, am_output, ids, paddings - raise NotImplementedError('Must be implemented by sub-classes.') - - def _ModifyLmBeforeFProp(self, theta, state0, ids, paddings, misc=None): - """Perform any LM modifications before LM FProp (no-op by default).""" - del theta, state0, ids, paddings, misc - - def ComputeLogitsWithLM(self, state, logits, is_eval=False): - """Compute resulting logits based on the fusion method. - - Args: - state: a NestedMap of states (specific to the layer). - logits: a tensor corresponds to AM logits. - is_eval: whether this is used in eval model (for example, beam search). - - Returns: - Resulting logits after fusion with the LM. - - Raises: - NotImplementedError: If method is not implemented. - """ - del state, logits, is_eval - raise NotImplementedError('Must be implemented by sub-classes.') - - def AddAdditionalDecoderSummaries(self, source_encs, source_paddings, targets, - seq_out_tas, softmax_input): - """Add any fusion related summaries (no-op by default). - - Args: - source_encs: A tensor of shape [time, batch_size, source_dim]. - source_paddings: A tensor of shape [time, batch_size]. - targets: A NestedMap containing target info. - seq_out_tas: A SequenceOutTensorArrays. - softmax_input: A tensor of shape [batch, time, vocab_size]. - """ - del source_encs, source_paddings, targets, seq_out_tas, softmax_input - - -class NullFusion(FusionBase): - """A trivial fusion layer which does nothing.""" - - def FProp(self, theta, state0, am_output, ids, paddings, misc=None): - del theta, ids, paddings - return am_output, state0 - - def ComputeLogitsWithLM(self, state, logits, is_eval=False): - return tf.nn.log_softmax(logits) if is_eval else logits diff --git a/lingvo/tasks/asr/input_generator.py b/lingvo/tasks/asr/input_generator.py deleted file mode 100644 index 6db0345b..00000000 --- a/lingvo/tasks/asr/input_generator.py +++ /dev/null @@ -1,357 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Speech recognition input generator.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import generic_input -from lingvo.core import py_utils - -from tensorflow.python.ops import inplace_ops # pylint:disable=g-direct-tensorflow-import - - -class AsrInput(base_input_generator.BaseSequenceInputGenerator): - """Input generator for ASR.""" - - @classmethod - def Params(cls): - """Defaults params for AsrInput.""" - p = super().Params() - p.Define('frame_size', 40, 'The number of coefficients in each frame.') - p.Define('append_eos_frame', True, 'Append an all-zero frame.') - p.source_max_length = 3000 - return p - - def _DataSourceFromFilePattern(self, file_pattern): - - def Proc(record): - """Parses a serialized tf.Example record.""" - # There we go! string, string, float32. I hope frames is allowed - # to be a waveform directly... - features = [ - ('uttid', tf.io.VarLenFeature(tf.string)), - ('transcript', tf.io.VarLenFeature(tf.string)), - ('frames', tf.io.VarLenFeature(tf.float32)), - ] - example = tf.io.parse_single_example(record, dict(features)) - fval = {k: v.values for k, v in example.items()} - # Reshape the flattened vector into its original time-major - # representation. - fval['frames'] = tf.reshape( - fval['frames'], shape=[-1, self.params.frame_size]) - # Input duration determines the bucket. - bucket_key = tf.cast(tf.shape(fval['frames'])[0], tf.int32) - if self.params.append_eos_frame: - bucket_key += 1 - tgt_ids, tgt_labels, tgt_paddings = self.StringsToIds(fval['transcript']) - src_paddings = tf.zeros([tf.shape(fval['frames'])[0]], dtype=tf.float32) - return [ - fval['uttid'], tgt_ids, tgt_labels, tgt_paddings, fval['frames'], - src_paddings - ], bucket_key - - return generic_input.GenericInput( - file_pattern=file_pattern, - processor=Proc, - dynamic_padding_dimensions=[0] * 6, - dynamic_padding_constants=[0] * 5 + [1], - **self.CommonInputOpArgs()) - - def _MaybePadSourceInputs(self, src_inputs, src_paddings): - p = self.params - if not p.append_eos_frame: - return src_inputs, src_paddings - - per_src_len = tf.reduce_sum(1 - src_paddings, 1) - per_src_len += 1 - max_src_len = tf.reduce_max(per_src_len) - input_shape = tf.shape(src_inputs) - input_len = tf.maximum(input_shape[1], tf.cast(max_src_len, tf.int32)) - pad_steps = input_len - input_shape[1] - src_inputs = tf.concat([ - src_inputs, - tf.zeros( - inplace_ops.inplace_update(input_shape, 1, pad_steps), - src_inputs.dtype) - ], 1) - src_paddings = 1 - tf.sequence_mask( - tf.reshape(per_src_len, [input_shape[0]]), tf.reshape(input_len, []), - src_paddings.dtype) - return src_inputs, src_paddings - - def __init__(self, params): - super().__init__(params) - p = self.params - - (utt_ids, tgt_ids, tgt_labels, tgt_paddings, src_frames, - src_paddings), self._bucket_keys = self._BuildDataSource() - - self._sample_ids = utt_ids - - src_frames, src_paddings = self._MaybePadSourceInputs( - src_frames, src_paddings) - - # We expect src_inputs to be of shape - # [batch_size, num_frames, feature_dim, channels]. - src_frames = tf.expand_dims(src_frames, axis=-1) - - # Convert target ids, labels, paddings, and weights from shape [batch_size, - # 1, num_frames] to [batch_size, num_frames] - tgt_ids = tf.squeeze(tgt_ids, axis=1) - tgt_labels = tf.squeeze(tgt_labels, axis=1) - tgt_paddings = tf.squeeze(tgt_paddings, axis=1) - - if p.pad_to_max_seq_length: - assert p.source_max_length - assert p.target_max_length - - if all(x == p.bucket_batch_limit[0] for x in p.bucket_batch_limit): - # Set the input batch size as an int rather than a tensor. - src_frames_shape = (self.InfeedBatchSize(), p.source_max_length, - p.frame_size, 1) - src_paddings_shape = (self.InfeedBatchSize(), p.source_max_length) - tgt_shape = (self.InfeedBatchSize(), p.target_max_length) - else: - tf.logging.warning( - 'Could not set static input shape since not all bucket batch sizes ' - 'are the same:', p.bucket_batch_limit) - src_frames_shape = None - src_paddings_shape = None - tgt_shape = None - - src_frames = py_utils.PadBatchDimension(src_frames, self.InfeedBatchSize(), 0) - src_paddings = py_utils.PadBatchDimension(src_paddings, self.InfeedBatchSize(), - 1) - tgt_ids = py_utils.PadBatchDimension(tgt_ids, self.InfeedBatchSize(), 0) - tgt_labels = py_utils.PadBatchDimension(tgt_labels, self.InfeedBatchSize(), 0) - tgt_paddings = py_utils.PadBatchDimension(tgt_paddings, self.InfeedBatchSize(), - 1) - - src_frames = py_utils.PadSequenceDimension( - src_frames, p.source_max_length, 0, shape=src_frames_shape) - src_paddings = py_utils.PadSequenceDimension( - src_paddings, p.source_max_length, 1, shape=src_paddings_shape) - tgt_ids = py_utils.PadSequenceDimension( - tgt_ids, p.target_max_length, 0, shape=tgt_shape) - tgt_labels = py_utils.PadSequenceDimension( - tgt_labels, p.target_max_length, 0, shape=tgt_shape) - tgt_paddings = py_utils.PadSequenceDimension( - tgt_paddings, p.target_max_length, 1, shape=tgt_shape) - - tgt = py_utils.NestedMap( - ids=tgt_ids, - labels=tgt_labels, - paddings=tgt_paddings, - weights=1.0 - tgt_paddings) - src = py_utils.NestedMap(src_inputs=src_frames, paddings=src_paddings) - - self._tgt = tgt - self._src = src - - def _InputBatch(self): - batch = py_utils.NestedMap() - batch.bucket_keys = self._bucket_keys - batch.src = self._src - batch.tgt = self._tgt - if not py_utils.use_tpu(): - batch.sample_ids = self._sample_ids - return batch - -class RawAsrInputIntegerUttIds(base_input_generator.BaseSequenceInputGenerator): - """Input generator for ASR. Assumes uttid is tf.int64""" - - PAD_INDEX = -1 - - @classmethod - def Params(cls): - """Defaults params for AsrInput.""" - p = super().Params() - p.Define('frame_size', 1, 'The number of coefficients in each frame.') - p.Define('append_eos_frame', True, 'Append an all-zero frame.') - # source_max_length is way too small by default when the length's time unit - # is in samples, given that we sample at 16kHz. - p.source_max_length = 3000 - return p - - def _DataSourceFromFilePattern(self, file_pattern): - - def Proc(record): - """Parses a serialized tf.Example record.""" - # There we go! string, string, float32. I hope frames is allowed - # to be a waveform directly... - features = [ - ('int64_uttid', tf.io.VarLenFeature(tf.int64)), - ('int64_audio_document_id', tf.io.VarLenFeature(tf.int64)), - ('num_utterances_in_audio_document', tf.io.VarLenFeature(tf.int64)), - ('transcript', tf.io.VarLenFeature(tf.string)), - ('frames', tf.io.FixedLenFeature((), tf.string)), - ] - example = tf.io.parse_single_example(record, dict(features)) - fval = {} - for k, v in example.items(): - if k == 'frames': - fval[k] = tf.cast(tf.io.decode_raw(v, tf.int16), tf.float32) - else: - assert isinstance(v, tf.SparseTensor) - fval[k] = v.values - # Reshape the flattened vector into its original time-major - # representation. - fval['frames'] = tf.reshape( - fval['frames'], shape=[-1, self.params.frame_size]) - # Input duration determines the bucket. - bucket_key = tf.cast(tf.shape(fval['frames'])[0], tf.int32) - if self.params.append_eos_frame: - bucket_key += 1 - tgt_ids, tgt_labels, tgt_paddings = self.StringsToIds(fval['transcript']) - src_paddings = tf.zeros([tf.shape(fval['frames'])[0]], dtype=tf.float32) - return [ - fval['int64_uttid'], fval['int64_audio_document_id'], - fval['num_utterances_in_audio_document'], tgt_ids, - tgt_labels, tgt_paddings, fval['frames'], - src_paddings - ], bucket_key - - return generic_input.GenericInput( - file_pattern=file_pattern, - processor=Proc, - dynamic_padding_dimensions=[0] * 8, - dynamic_padding_constants=[0] * 7 + [1], - **self.CommonInputOpArgs()) - - def _MaybePadSourceInputs(self, src_inputs, src_paddings): - p = self.params - if not p.append_eos_frame: - return src_inputs, src_paddings - - per_src_len = tf.reduce_sum(1 - src_paddings, 1) - per_src_len += 1 - max_src_len = tf.reduce_max(per_src_len) - input_shape = tf.shape(src_inputs) - input_len = tf.maximum(input_shape[1], tf.cast(max_src_len, tf.int32)) - pad_steps = input_len - input_shape[1] - src_inputs = tf.concat([ - src_inputs, - tf.zeros( - inplace_ops.inplace_update(input_shape, 1, pad_steps), - src_inputs.dtype) - ], 1) - src_paddings = 1 - tf.sequence_mask( - tf.reshape(per_src_len, [input_shape[0]]), tf.reshape(input_len, []), - src_paddings.dtype) - return src_inputs, src_paddings - - def __init__(self, params): - super().__init__(params) - p = self.params - - (utt_ids, audio_document_ids, num_utterances_in_audio_document, - tgt_ids, tgt_labels, tgt_paddings, src_frames, - src_paddings), self._bucket_keys = self._BuildDataSource() - - self._sample_ids = utt_ids - - src_frames, src_paddings = self._MaybePadSourceInputs( - src_frames, src_paddings) - - # We expect src_inputs to be of shape - # [batch_size, num_frames, feature_dim, channels]. - src_frames = tf.expand_dims(src_frames, axis=-1) - - # Convert target ids, labels, paddings, and weights from shape [batch_size, - # 1, num_frames] to [batch_size, num_frames] - tgt_ids = tf.squeeze(tgt_ids, axis=1) - tgt_labels = tf.squeeze(tgt_labels, axis=1) - tgt_paddings = tf.squeeze(tgt_paddings, axis=1) - - if p.pad_to_max_seq_length: - assert p.source_max_length - assert p.target_max_length - - if all(x == p.bucket_batch_limit[0] for x in p.bucket_batch_limit): - # Set the input batch size as an int rather than a tensor. - src_frames_shape = (self.InfeedBatchSize(), p.source_max_length, - p.frame_size, 1) - src_paddings_shape = (self.InfeedBatchSize(), p.source_max_length) - tgt_shape = (self.InfeedBatchSize(), p.target_max_length) - else: - tf.logging.warning( - 'Could not set static input shape since not all bucket batch sizes ' - 'are the same:', p.bucket_batch_limit) - src_frames_shape = None - src_paddings_shape = None - tgt_shape = None - - src_frames = py_utils.PadBatchDimension(src_frames, self.InfeedBatchSize(), 0) - src_paddings = py_utils.PadBatchDimension(src_paddings, self.InfeedBatchSize(), - 1) - tgt_ids = py_utils.PadBatchDimension(tgt_ids, self.InfeedBatchSize(), 0) - tgt_labels = py_utils.PadBatchDimension(tgt_labels, self.InfeedBatchSize(), 0) - tgt_paddings = py_utils.PadBatchDimension(tgt_paddings, self.InfeedBatchSize(), - 1) - self._sample_ids = py_utils.PadBatchDimension(self._sample_ids, self.InfeedBatchSize(), - type(self).PAD_INDEX) - # For reasons I don't understand, the shape of self._sample_ids after the above is - # [BatchSize, 1] rather than [BatchSize]. - self._sample_ids = tf.squeeze(self._sample_ids, axis=1) - self._sample_ids = tf.ensure_shape(self._sample_ids, self.InfeedBatchSize()) - - audio_document_ids = py_utils.PadBatchDimension(audio_document_ids, self.InfeedBatchSize(), - type(self).PAD_INDEX) - # For reasons I don't understand, the shape of audio_document_ids after the above is - # [BatchSize, 1] rather than [BatchSize]. - audio_document_ids = tf.squeeze(audio_document_ids, axis=1) - audio_document_ids = tf.ensure_shape(audio_document_ids, self.InfeedBatchSize()) - - num_utterances_in_audio_document = py_utils.PadBatchDimension(num_utterances_in_audio_document, self.InfeedBatchSize(), - type(self).PAD_INDEX) - # For reasons I don't understand, the shape of num_utterances_in_audio_document after the above is - # [BatchSize, 1] rather than [BatchSize]. - num_utterances_in_audio_document = tf.squeeze(num_utterances_in_audio_document, axis=1) - num_utterances_in_audio_document = tf.ensure_shape(num_utterances_in_audio_document, self.InfeedBatchSize()) - - - src_frames = py_utils.PadSequenceDimension( - src_frames, p.source_max_length, 0, shape=src_frames_shape) - src_paddings = py_utils.PadSequenceDimension( - src_paddings, p.source_max_length, 1, shape=src_paddings_shape) - tgt_ids = py_utils.PadSequenceDimension( - tgt_ids, p.target_max_length, 0, shape=tgt_shape) - tgt_labels = py_utils.PadSequenceDimension( - tgt_labels, p.target_max_length, 0, shape=tgt_shape) - tgt_paddings = py_utils.PadSequenceDimension( - tgt_paddings, p.target_max_length, 1, shape=tgt_shape) - - tgt = py_utils.NestedMap( - ids=tgt_ids, - labels=tgt_labels, - paddings=tgt_paddings, - weights=1.0 - tgt_paddings) - src = py_utils.NestedMap(src_inputs=src_frames, paddings=src_paddings) - - self._tgt = tgt - self._src = src - - self._audio_document_ids = audio_document_ids - self._num_utterances_in_audio_document = num_utterances_in_audio_document - - def _InputBatch(self): - batch = py_utils.NestedMap() - batch.bucket_keys = self._bucket_keys - batch.src = self._src - batch.tgt = self._tgt - batch.sample_ids = self._sample_ids - batch.audio_document_ids = self._audio_document_ids - batch.num_utterances_in_audio_document = self._num_utterances_in_audio_document - return batch diff --git a/lingvo/tasks/asr/input_generator_test.py b/lingvo/tasks/asr/input_generator_test.py deleted file mode 100644 index cf63baa5..00000000 --- a/lingvo/tasks/asr/input_generator_test.py +++ /dev/null @@ -1,176 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for asr.input_generator.""" - -import os -import lingvo.compat as tf -from lingvo.core import test_utils -from lingvo.tasks.asr import input_generator -import numpy as np - - -def _MakeBytesFeature(unicode_array): - value = [tf.compat.as_bytes(w) for w in unicode_array] - return tf.train.Feature(bytes_list=tf.train.BytesList(value=value)) - - -def _MakeInt64Feature(value): - return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) - - -def _MakeFloatFeature(value): - return tf.train.Feature(float_list=tf.train.FloatList(value=value)) - - -FRAME_SIZE = 40 - - -def _MakeFrames(utt_len): - return np.random.normal(size=FRAME_SIZE * utt_len) - - -def _MakeTfExample(uttid, frames, text): - flat_frames = frames.flatten() - feature = { - 'uttid': _MakeBytesFeature([uttid]), - 'transcript': _MakeBytesFeature([text]), - 'frames': _MakeFloatFeature(flat_frames) - } - return tf.train.Example(features=tf.train.Features(feature=feature)) - - -class InputTest(test_utils.TestCase): - - def _GenerateExamples(self, output_filepath): - example_def = [(b'utt1', (1234, b'HELLO WORLD')), - (b'utt2', (568, b'TIRED WITH ALL THESE')), - (b'utt3', (778, b'WOULD THAT IT WERE SO EASY'))] - self._example_def = dict(example_def) - tf_examples = [] - for xdef in example_def: - tf_examples.append( - _MakeTfExample(xdef[0], _MakeFrames(xdef[1][0]), xdef[1][1])) - with tf.python_io.TFRecordWriter(output_filepath) as outf: - for ex in tf_examples: - outf.write(ex.SerializeToString()) - - def _GenerateSetup(self, append_eos_frame, pad_to_max_seq_length=False): - tfrecords_filepath = os.path.join(tf.test.get_temp_dir(), - 'simple.tfrecords') - self._GenerateExamples(tfrecords_filepath) - p = input_generator.AsrInput.Params() - p.file_pattern = 'tfrecord:' + tfrecords_filepath - p.frame_size = FRAME_SIZE - p.target_max_length = 30 - p.bucket_upper_bound = [2560] - p.bucket_batch_limit = [3] - p.append_eos_frame = append_eos_frame - p.pad_to_max_seq_length = pad_to_max_seq_length - return p - - def _AssertAllOnes(self, np_data): - self.assertAllEqual(np_data, np.ones_like(np_data)) - - def _AssertAllZeros(self, np_data): - self.assertAllEqual(np_data, np.zeros_like(np_data)) - - def _AssertShapesAsExpected(self, shapes, expected): - for key, expected_shape in sorted(expected.items()): - self.assertAllEqual( - shapes.GetItem(key), expected_shape, msg='Shape of %s' % key) - - def _TestAsrInput(self, params): - p = params - with self.session(use_gpu=False): - inp = input_generator.AsrInput(p) - batch = inp.GetPreprocessedInputBatch() - vals = self.evaluate(batch) - shapes = vals.Transform(lambda x: x.shape) - shapes.VLog(0, 'shapes: ') - # sample_ids (3, 1) - # src.src_inputs (3, 1235, 40, 1) - # src.paddings (3, 1235) - # tgt.ids (3, 30) - # tgt.labels (3, 30) - # tgt.paddings (3, 30) - # tgt.weights (3, 30) - batch_size = p.bucket_batch_limit[0] - if p.pad_to_max_seq_length: - max_num_frames = p.source_max_length - else: - max_num_frames = np.amax( - [xdef[0] for xdef in self._example_def.values()]) - if p.append_eos_frame: - max_num_frames += 1 - - tgt_shape = [batch_size, p.target_max_length] - self._AssertShapesAsExpected( - shapes, { - 'sample_ids': [batch_size, 1], - 'src.src_inputs': [batch_size, max_num_frames, p.frame_size, 1], - 'src.paddings': [batch_size, max_num_frames], - 'tgt.ids': tgt_shape, - 'tgt.labels': tgt_shape, - 'tgt.paddings': tgt_shape, - 'tgt.weights': tgt_shape - }) - for b in range(batch_size): - ex = vals.Transform(lambda x: x[b]) - ref = self._example_def[ex.sample_ids[0]] - # Check source. - ref_num_frames = ref[0] - if p.append_eos_frame: - ref_num_frames += 1 - ref_num_padding_frames = max_num_frames - ref_num_frames - zero_frames = ex.src.src_inputs[max_num_frames - - ref_num_padding_frames:] - self._AssertAllZeros(zero_frames) - zero_paddings = ex.src.paddings[:ref_num_frames] - one_paddings = ex.src.paddings[ref_num_frames:] - self._AssertAllZeros(zero_paddings) - self._AssertAllOnes(one_paddings) - # Check target. Something like this: - # ids: [1 a b c 2 2 2] - # labels: [a b c 2 2 2 2] - # paddings: [0 0 0 1 1 1 1] - ref_num_graphemes = len(ref[1]) - ref_tgt_ids_padding = p.target_max_length - ref_num_graphemes - 1 - self.assertEqual(ex.tgt.ids[0], p.tokenizer.target_sos_id) - self.assertAllEqual(ex.tgt.ids[-ref_tgt_ids_padding:], - [2] * ref_tgt_ids_padding) - self.assertAllEqual(ex.tgt.labels[-ref_tgt_ids_padding - 1:], - [2] * (ref_tgt_ids_padding + 1)) - self.assertAllEqual(ex.tgt.ids[1:1 + ref_num_graphemes], - ex.tgt.labels[:ref_num_graphemes]) - self._AssertAllZeros(ex.tgt.paddings[:ref_num_graphemes + 1]) - self._AssertAllOnes(ex.tgt.paddings[ref_num_graphemes + 1:]) - self._AssertAllOnes(ex.tgt.weights[:ref_num_graphemes + 1]) - self._AssertAllZeros(ex.tgt.weights[ref_num_graphemes + 1:]) - - def testAsrInput(self): - p = self._GenerateSetup(append_eos_frame=True) - self._TestAsrInput(p) - - def testAsrInputWithoutEosFrame(self): - p = self._GenerateSetup(append_eos_frame=False) - self._TestAsrInput(p) - - def testAsrInputWithStaticShape(self): - p = self._GenerateSetup(append_eos_frame=False, pad_to_max_seq_length=True) - self._TestAsrInput(p) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/asr/mlperf_compat_frontend.py b/lingvo/tasks/asr/mlperf_compat_frontend.py deleted file mode 100644 index 48d70b2c..00000000 --- a/lingvo/tasks/asr/mlperf_compat_frontend.py +++ /dev/null @@ -1,148 +0,0 @@ -class MLPerfCompatFrontend(BaseAsrFrontend): - @classmethod - def Params(cls): - p = super(MelAsrFrontend, cls).Params() - p.name = 'frontend' - p.Define('sample_rate', 16000.0, 'Sample rate in Hz') - p.Define('channel_count', 1, 'Number of channels.') - p.Define('frame_size_ms', 25.0, - 'Amount of data grabbed for each frame during analysis') - p.Define('frame_step_ms', 10.0, 'Number of ms to jump between frames') - p.Define('num_bins', 80, 'Number of bins in the mel-spectrogram output') - p.Define('lower_edge_hertz', 125.0, - 'The lowest frequency of the mel-spectrogram analsis') - p.Define('upper_edge_hertz', 7600.0, - 'The highest frequency of the mel-spectrogram analsis') - p.Define('preemph', 0.97, - 'The first-order filter coefficient used for preemphasis') - p.Define('noise_scale', 8.0, - 'The amount of noise (in 16-bit LSB units) to add') - p.Define('window_fn', 'HANNING', - 'Window function to apply (valid values are "HANNING", and None)') - p.Define( - 'pad_end', False, - 'Whether to pad the end of `signals` with zeros when the provided ' - 'frame length and step produces a frame that lies partially past ' - 'its end.') - p.Define( - 'per_bin_mean', None, - 'Per-bin (num_bins) means for normalizing the spectrograms. ' - 'Defaults to zeros.') - p.Define('per_bin_stddev', None, - 'Per-bin (num_bins) standard deviations. Defaults to ones.') - p.Define('stack_left_context', 0, 'Number of left context frames to stack.') - p.Define('stack_right_context', 0, - 'Number of right context frames to stack.') - p.Define('frame_stride', 1, 'The frame stride for sub-sampling.') - - return p - - @staticmethod - def GetConfigFromParams(params): - """Returns an AsrFrontendConfig namedtuple with vital config settings.""" - context_size = params.stack_left_context + params.stack_right_context + 1 - subsample_factor = params.num_bins * context_size - frame_step = round(params.sample_rate * params.frame_step_ms / 1000.0) - return AsrFrontendConfig( - is_null=False, - src_type='pcm', - src_pcm_scale=32768.0, - src_pcm_sample_rate=16000.0, - output_dim=subsample_factor, - input_frame_ratio=frame_step * subsample_factor) - - @base_layer.initializer - def __init__(self, params): - super(MelAsrFrontend, self).__init__(params) - p = self.params - if p.frame_stride < 1: - raise ValueError('frame_stride must be positive.') - - assert p.channel_count == 1, 'Only 1 channel currently supported.' - # Make sure key params are in floating point. - p.sample_rate = float(p.sample_rate) - p.frame_step_ms = float(p.frame_step_ms) - p.frame_size_ms = float(p.frame_size_ms) - p.lower_edge_hertz = float(p.lower_edge_hertz) - p.upper_edge_hertz = float(p.upper_edge_hertz) - - self._frame_step = int(round(p.sample_rate * p.frame_step_ms / 1000.0)) - self._frame_size = (int(round(p.sample_rate * p.frame_size_ms / 1000.0)) + 1 - ) # +1 for the preemph - # Overdrive means double FFT size. - # Note: 2* because of overdrive - self._fft_size = 2 * int(max(512, _NextPowerOfTwo(self._frame_size))) - - self._CreateWindowFunction() - - # Mean/stddev. - if p.per_bin_mean is None: - p.per_bin_mean = [0.0] * p.num_bins - if p.per_bin_stddev is None: - p.per_bin_stddev = [1.0] * p.num_bins - assert len(p.per_bin_mean) == p.num_bins - assert len(p.per_bin_stddev) == p.num_bins - - def _CreateWindowFunction(self): - p = self.params - if p.window_fn is None: - self._window_fn = None - elif p.window_fn == 'HANNING': - - def _HanningWindow(frame_size, dtype): - return tf.signal.hann_window(frame_size, dtype=dtype) - - self._window_fn = _HanningWindow - else: - raise ValueError('Illegal value %r for window_fn param' % (p.window_fn,)) - - def FProp(self, theta, input_batch): - """Perform signal processing on a sequence of PCM data. - - NOTE: This implementation does not currently support paddings, and they - are accepted for compatibility with the super-class. - - TODO(laurenzo): Rework this to support paddings. - - Args: - theta: Layer theta. - input_batch: PCM input map: - - - 'src_inputs': int16 or float32 tensor of PCM audio data, scaled to - +/-32768 (versus [-1..1)!). See class comments for supported input - shapes. - - 'paddings': per frame 0/1 paddings. Shaped: [batch, frame]. - Returns: - NestedMap of encoder inputs which can be passed directly to a - compatible encoder and contains: - - - 'src_inputs': inputs to the encoder, minimally of shape - [batch, time, ...]. - - 'paddings': a 0/1 tensor of shape [batch, time]. - """ - - return self._FPropDefault(input_batch) - - def _FPropDefault(self, input_batch): - pcm_audio_data, pcm_audio_paddings = self._ReshapeToMono2D( - input_batch.src_inputs, input_batch.paddings) - - mel_spectrogram, mel_spectrogram_paddings = self._FPropChunk( - pcm_audio_data, pcm_audio_paddings) - - mel_spectrogram, mel_spectrogram_paddings = self._PadAndReshapeSpec( - mel_spectrogram, mel_spectrogram_paddings) - - return py_utils.NestedMap( - src_inputs=mel_spectrogram, paddings=mel_spectrogram_paddings) - - def _StackSignal(self, signal, stack_size, stride): - signal = tf.signal.frame( - signal=signal, - frame_length=stack_size, - frame_step=stride, - pad_end=False, - axis=1, - ) - signal = tf.reshape(signal, py_utils.GetShape(signal)[:2] + [-1]) - return signal diff --git a/lingvo/tasks/asr/model.py b/lingvo/tasks/asr/model.py deleted file mode 100644 index 976cd6a7..00000000 --- a/lingvo/tasks/asr/model.py +++ /dev/null @@ -1,496 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Speech model.""" - -import collections -import lingvo.compat as tf -from lingvo.core import base_model -from lingvo.core import metrics -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.tasks.asr import decoder -from lingvo.tasks.asr import decoder_utils -from lingvo.tasks.asr import encoder -from lingvo.tasks.asr import frontend as asr_frontend -from lingvo.tools import audio_lib - - -# hyps: [num_beams, num_hyps_per_beam] of serialized Hypothesis protos. -# ids: [num_beams * num_hyps_per_beam, max_target_length]. -# lens: [num_beams * num_hyps_per_beam]. -# scores: [num_beams, num_hyps_per_beam]. -# decoded: [num_beams, num_hyps_per_beam]. -DecoderTopK = collections.namedtuple( - 'topk', ['hyps', 'ids', 'lens', 'scores', 'decoded']) # pyformat: disable - - -class AsrModel(base_model.BaseTask): - """Speech model.""" - - @classmethod - def Params(cls): - p = super().Params() - p.encoder = encoder.AsrEncoder.Params() - p.decoder = decoder.AsrDecoder.Params() - p.Define( - 'frontend', None, - 'ASR frontend to extract features from input. Defaults to no frontend ' - 'which means that features are taken directly from the input.') - - p.Define( - 'include_auxiliary_metrics', True, - 'In addition to simple WER, also computes oracle WER, SACC, TER, etc. ' - 'Turning off this option will speed up the decoder job.') - - tp = p.train - tp.lr_schedule = ( - schedule.PiecewiseConstantSchedule.Params().Set( - boundaries=[350000, 500000, 600000], values=[1.0, 0.1, 0.01, - 0.001])) - tp.vn_start_step = 20000 - tp.vn_std = 0.075 - tp.l2_regularizer_weight = 1e-6 - tp.learning_rate = 0.001 - tp.clip_gradient_norm_to_value = 1.0 - tp.grad_norm_to_clip_to_zero = 100.0 - tp.tpu_steps_per_loop = 20 - - return p - - def __init__(self, params): - if not params.name: - raise ValueError('params.name not set.') - super().__init__(params) - p = self.params - - # Construct the model. - if p.encoder: - if not p.encoder.name: - p.encoder.name = 'enc' - self.CreateChild('encoder', p.encoder) - if p.decoder: - if not p.decoder.name: - p.decoder.name = 'dec' - self.CreateChild('decoder', p.decoder) - if p.frontend: - self.CreateChild('frontend', p.frontend) - - def _GetDecoderTargets(self, input_batch): - """Returns targets which will be forwarded to the decoder. - - Subclasses can override this method to change the target that is used by - the decoder. For example, a subclass could add additional targets that - can be forwared to the decoder. - - Args: - input_batch: a NestedMap which contains the targets. - - Returns: - a NestedMap corresponding to the target selected. - """ - return input_batch.tgt - - def _MakeDecoderTheta(self, theta, input_batch): - """Compute theta to be used by the decoder for computing metrics and loss. - - This method can be over-ridden by child classes to add values to theta that - is passed to the decoder. - - For example, to pass the one hot vector which indicates which data source - was selected a child class could over-ride this method as follows: - - def _MakeDecoderTheta(self, theta): - decoder_theta = super(MyModel, self)._MakeDecoderTheta(theta, input_batch) - decoder_theta.child_onehot = input_batch.source_selected - return decoder_theta - - Args: - theta: A `.NestedMap` object containing variable values used to compute - loss and metrics. - input_batch: NestedMap containing input data in the current batch. Unused - here. - - Returns: - A copy of the decoder theta. - """ - del input_batch # Unused - return theta.decoder.DeepCopy() - - def ComputePredictions(self, theta, input_batch): - input_batch_src = input_batch.src - encoder_outputs = self._FrontendAndEncoderFProp(theta, input_batch_src) - tgt = self._GetDecoderTargets(input_batch) - decoder_theta = self._MakeDecoderTheta(theta, input_batch) - return self.decoder.ComputePredictions(decoder_theta, encoder_outputs, tgt) - - def ComputeLoss(self, theta, predictions, input_batch): - tgt = self._GetDecoderTargets(input_batch) - decoder_theta = self._MakeDecoderTheta(theta, input_batch) - return self.decoder.ComputeLoss(decoder_theta, predictions, tgt) - - def _FrontendAndEncoderFProp(self, - theta, - input_batch_src, - initial_state=None): - """FProps through the frontend and encoder. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - input_batch_src: An input NestedMap as per `BaseAsrFrontend.FProp`. - initial_state: None or a NestedMap object containing the initial states. - - Returns: - A NestedMap as from `AsrEncoder.FProp`. - """ - p = self.params - if p.frontend: - input_batch_src = self.frontend.FProp(theta.frontend, input_batch_src) - if initial_state: - return self.encoder.FProp( - theta.encoder, input_batch_src, state0=initial_state) - else: - return self.encoder.FProp(theta.encoder, input_batch_src) - - def _GetTopK(self, decoder_outs, tag=''): - hyps = decoder_outs.topk_hyps - ids = tf.identity(decoder_outs.topk_ids, name='TopKLabelIds' + tag) - lens = tf.identity(decoder_outs.topk_lens, name='TopKLabelLengths' + tag) - scores = decoder_outs.topk_scores - decoded = decoder_outs.topk_decoded - - if ids is not None: - decoded = self.input_generator.IdsToStrings(ids, lens - 1) - decoded = tf.identity(decoded, name='top_k_decoded%s' % tag) - decoded = tf.reshape(decoded, tf.shape(hyps)) - if scores is not None and hyps is not None: - scores = tf.identity( - tf.reshape(scores, tf.shape(lens)), name='top_k_scores%s' % tag) - scores = tf.reshape(scores, tf.shape(hyps)) - return DecoderTopK(hyps, ids, lens, scores, decoded) - - def _ComputeNormalizedWER(self, hyps, refs): - # Filter out all '' tokens for norm_wer computation. - hyps_no_epsilon = tf.strings.regex_replace(hyps, '()+', ' ') - # norm_wer is size [num_transcripts * hyps_per_beam, 2] - norm_wer = decoder_utils.ComputeWer(hyps_no_epsilon, refs) - # Split into two tensors of size [num_transcripts * hyps_per_beam, 1] - norm_wer_errors, norm_wer_words = tf.split(norm_wer, [1, 1], 1) - shape = [-1, self.params.decoder.beam_search.num_hyps_per_beam] - norm_wer_errors = tf.reshape(norm_wer_errors, shape) - norm_wer_words = tf.reshape(norm_wer_words, shape) - - return norm_wer_errors, norm_wer_words - - def AddAdditionalDecoderMetricsToGraph(self, topk_hyps, filtered_hyps, - filtered_refs, input_batch, - decoder_outs): - """Returns a dict of metrics which should be computed from decoded hyps.""" - # The base class implementation returns an empty dictionary. Sub-classes can - # provide their own implementation. - return {} - - def DecodeWithTheta(self, theta, input_batch): - """Constructs the inference graph.""" - p = self.params - with tf.name_scope('decode'), tf.name_scope(p.name): - with tf.name_scope('encoder'): - encoder_outputs = self._FrontendAndEncoderFProp(theta, input_batch.src) - with tf.name_scope('beam_search'): - decoder_outs = self.decoder.BeamSearchDecodeWithTheta( - theta.decoder, encoder_outputs) - - if py_utils.use_tpu(): - # Decoder metric computation contains arbitrary execution - # that may not run on TPU. - decoder_metrics = py_utils.RunOnTpuHost(self._ComputeDecoderMetrics, - decoder_outs, input_batch) - else: - decoder_metrics = self._ComputeDecoderMetrics(decoder_outs, input_batch) - return decoder_metrics - - def _GetTargetForDecoderMetrics(self, input_batch): - """Returns targets which will be used to compute decoder metrics. - - Subclasses can override this method to change the target that is used when - calculating decoder metrics. - - Args: - input_batch: a NestedMap which contains the targets. - - Returns: - a NestedMap containing 'ids', 'labels', 'paddings', 'weights' - """ - return self._GetDecoderTargets(input_batch) - - def _ComputeDecoderMetrics(self, decoder_outs, input_batch): - """Computes metrics on output from decoder. - - Args: - decoder_outs: A `BeamSearchDecodeOutput`, a namedtuple containing the - decode results. - input_batch: A `NestedMap` of tensors representing the source, target, - and other components of the input batch. - - Returns: - A dict of Tensors containing decoder output and metrics. - """ - p = self.params - topk = self._GetTopK(decoder_outs) - tgt = self._GetTargetForDecoderMetrics(input_batch) - transcripts = self.input_generator.IdsToStrings( - tgt.labels, - tf.cast(tf.round(tf.reduce_sum(1.0 - tgt.paddings, 1) - 1.0), tf.int32)) - - # Filter out all isolated '' tokens. - noise_pattern = ' |^ | $|^$' - filtered_refs = tf.strings.regex_replace(transcripts, noise_pattern, ' ') - filtered_hyps = tf.strings.regex_replace(topk.decoded, noise_pattern, ' ') - # Compute translation quality scores for all hyps. - filtered_refs = tf.tile( - tf.reshape(filtered_refs, [-1, 1]), - [1, p.decoder.beam_search.num_hyps_per_beam]) - filtered_hyps = tf.reshape(filtered_hyps, [-1]) - filtered_refs = tf.reshape(filtered_refs, [-1]) - norm_wer_errors, norm_wer_words = self._ComputeNormalizedWER( - filtered_hyps, filtered_refs) - - ret_dict = { - 'target_ids': tgt.ids, - 'target_labels': tgt.labels, - 'target_weights': tgt.weights, - 'target_paddings': tgt.paddings, - 'transcripts': transcripts, - 'topk_decoded': topk.decoded, - 'topk_ids': topk.ids, - 'topk_lens': topk.lens, - 'topk_scores': topk.scores, - 'norm_wer_errors': norm_wer_errors, - 'norm_wer_words': norm_wer_words, - } - - if not py_utils.use_tpu(): - ret_dict['utt_id'] = input_batch.sample_ids - - ret_dict.update( - self.AddAdditionalDecoderMetricsToGraph(topk, filtered_hyps, - filtered_refs, input_batch, - decoder_outs)) - return ret_dict - - def CreateAdditionalDecoderMetrics(self): - """Returns a dictionary of additional metrics which should be computed.""" - # The base class implementation returns an empty dictionary. Sub-classes can - # provide their own implementation. - return {} - - def CreateDecoderMetrics(self): - base_metrics = { - 'num_samples_in_batch': metrics.AverageMetric(), - 'norm_wer': metrics.AverageMetric(), # Normalized word error rate. - 'corpus_bleu': metrics.CorpusBleuMetric(), - } - - if self.params.include_auxiliary_metrics: - base_metrics.update({ - 'wer': metrics.AverageMetric(), # Word error rate. - 'sacc': metrics.AverageMetric(), # Sentence accuracy. - 'ter': metrics.AverageMetric(), # Token error rate. - 'oracle_norm_wer': metrics.AverageMetric(), - }) - - # Add any additional metrics that should be computed. - base_metrics.update(self.CreateAdditionalDecoderMetrics()) - return base_metrics - - def UpdateAdditionalMetrics(self, dec_out_dict, dec_metrics_dict): - """Updates and returns a dictionary of metrics based on decoded hyps.""" - # Can be implemented in sub-classes to perform any model specific behavior. - # The default implementation just returns the metrics unchanged. - del dec_out_dict - return dec_metrics_dict - - # TODO(prabhavalkar): Add support to save out the decoded hypotheses. - def PostProcessDecodeOut(self, dec_out_dict, dec_metrics_dict): - p = self.params - assert 'topk_scores' in dec_out_dict, list(dec_out_dict.keys()) - topk_scores = dec_out_dict['topk_scores'] - topk_decoded = dec_out_dict['topk_decoded'] - transcripts = dec_out_dict['transcripts'] - if not py_utils.use_tpu(): - utt_id = dec_out_dict['utt_id'] - assert len(utt_id) == len(transcripts) - norm_wer_errors = dec_out_dict['norm_wer_errors'] - norm_wer_words = dec_out_dict['norm_wer_words'] - target_labels = dec_out_dict['target_labels'] - target_paddings = dec_out_dict['target_paddings'] - topk_ids = dec_out_dict['topk_ids'] - topk_lens = dec_out_dict['topk_lens'] - assert len(transcripts) == len(target_labels) - assert len(transcripts) == len(target_paddings) - assert len(transcripts) == len(topk_decoded) - assert (len(topk_ids) == p.decoder.beam_search.num_hyps_per_beam * - len(transcripts)) - assert len(norm_wer_errors) == len(transcripts) - assert len(norm_wer_words) == len(transcripts) - - dec_metrics_dict['num_samples_in_batch'].Update(len(transcripts)) - - def GetRefIds(ref_ids, ref_paddinds): - assert len(ref_ids) == len(ref_paddinds) - return_ids = [] - for i in range(len(ref_ids)): - if ref_paddinds[i] == 0: - return_ids.append(ref_ids[i]) - return return_ids - - total_norm_wer_errs = norm_wer_errors[:, 0].sum() - total_norm_wer_words = norm_wer_words[:, 0].sum() - - dec_metrics_dict['norm_wer'].Update( - total_norm_wer_errs / total_norm_wer_words, total_norm_wer_words) - - for ref_str, hyps in zip(transcripts, topk_decoded): - filtered_ref = decoder_utils.FilterNoise(ref_str) - filtered_ref = decoder_utils.FilterEpsilon(filtered_ref) - filtered_hyp = decoder_utils.FilterNoise(hyps[0]) - filtered_hyp = decoder_utils.FilterEpsilon(filtered_hyp) - dec_metrics_dict['corpus_bleu'].Update(filtered_ref, filtered_hyp) - - total_errs = 0 - total_oracle_errs = 0 - total_ref_words = 0 - total_token_errs = 0 - total_ref_tokens = 0 - total_accurate_sentences = 0 - key_value_pairs = [] - - if p.include_auxiliary_metrics: - for i in range(len(transcripts)): - ref_str = transcripts[i] - if not py_utils.use_tpu(): - tf.logging.info('utt_id: %s', utt_id[i]) - if self.cluster.add_summary: - tf.logging.info(' ref_str: %s', ref_str) - hyps = topk_decoded[i] - ref_ids = GetRefIds(target_labels[i], target_paddings[i]) - hyp_index = i * p.decoder.beam_search.num_hyps_per_beam - top_hyp_ids = topk_ids[hyp_index][:topk_lens[hyp_index]] - if self.cluster.add_summary: - tf.logging.info(' ref_ids: %s', ref_ids) - tf.logging.info(' top_hyp_ids: %s', top_hyp_ids) - total_ref_tokens += len(ref_ids) - _, _, _, token_errs = decoder_utils.EditDistanceInIds( - ref_ids, top_hyp_ids) - total_token_errs += token_errs - - assert p.decoder.beam_search.num_hyps_per_beam == len(hyps) - filtered_ref = decoder_utils.FilterNoise(ref_str) - filtered_ref = decoder_utils.FilterEpsilon(filtered_ref) - oracle_errs = norm_wer_errors[i][0] - for n, (score, hyp_str) in enumerate(zip(topk_scores[i], hyps)): - if self.cluster.add_summary: - tf.logging.info(' %f: %s', score, hyp_str) - filtered_hyp = decoder_utils.FilterNoise(hyp_str) - filtered_hyp = decoder_utils.FilterEpsilon(filtered_hyp) - ins, subs, dels, errs = decoder_utils.EditDistance( - filtered_ref, filtered_hyp) - # Note that these numbers are not consistent with what is used to - # compute normalized WER. In particular, these numbers will be - # inflated when the transcript contains punctuation. - tf.logging.info(' ins: %d, subs: %d, del: %d, total: %d', ins, subs, - dels, errs) - # Only aggregate scores of the top hypothesis. - if n == 0: - total_errs += errs - total_ref_words += len(decoder_utils.Tokenize(filtered_ref)) - if norm_wer_errors[i, n] == 0: - total_accurate_sentences += 1 - oracle_errs = min(oracle_errs, norm_wer_errors[i, n]) - total_oracle_errs += oracle_errs - - dec_metrics_dict['wer'].Update(total_errs / max(1., total_ref_words), - total_ref_words) - dec_metrics_dict['oracle_norm_wer'].Update( - total_oracle_errs / max(1., total_ref_words), total_ref_words) - dec_metrics_dict['sacc'].Update( - total_accurate_sentences / len(transcripts), len(transcripts)) - dec_metrics_dict['ter'].Update( - total_token_errs / max(1., total_ref_tokens), total_ref_tokens) - - # Update any additional metrics. - dec_metrics_dict = self.UpdateAdditionalMetrics(dec_out_dict, - dec_metrics_dict) - return key_value_pairs - - def Inference(self): - """Constructs inference subgraphs. - - Returns: - dict: A dictionary of the form ``{'subgraph_name': (fetches, feeds)}``. - Each of fetches and feeds is itself a dictionary which maps a string name - (which describes the tensor) to a corresponding tensor in the inference - graph which should be fed/fetched from. - """ - subgraphs = {} - with tf.name_scope('inference'): - subgraphs['default'] = self._InferenceSubgraph_Default() - return subgraphs - - def _InferenceSubgraph_Default(self): - """Constructs graph for offline inference. - - Returns: - (fetches, feeds) where both fetches and feeds are dictionaries. Each - dictionary consists of keys corresponding to tensor names, and values - corresponding to a tensor in the graph which should be input/read from. - """ - p = self.params - with tf.name_scope('default'): - # TODO(laurenzo): Once the migration to integrated frontends is complete, - # this model should be upgraded to use the MelAsrFrontend in its - # params vs relying on pre-computed feature generation and the inference - # special casing. - wav_bytes = tf.placeholder(dtype=tf.string, name='wav') - frontend = self.frontend if p.frontend else None - if not frontend: - # No custom frontend. Instantiate the default. - frontend_p = asr_frontend.MelAsrFrontend.Params() - frontend = frontend_p.Instantiate() - - # Decode the wave bytes and use the explicit frontend. - unused_sample_rate, audio = audio_lib.DecodeWav(wav_bytes) - audio *= 32768 - # Remove channel dimension, since we have a single channel. - audio = tf.squeeze(audio, axis=1) - # Add batch. - audio = tf.expand_dims(audio, axis=0) - input_batch_src = py_utils.NestedMap( - src_inputs=audio, paddings=tf.zeros_like(audio)) - input_batch_src = frontend.FPropDefaultTheta(input_batch_src) - - encoder_outputs = self.encoder.FPropDefaultTheta(input_batch_src) - decoder_outputs = self.decoder.BeamSearchDecode(encoder_outputs) - topk = self._GetTopK(decoder_outputs) - - feeds = {'wav': wav_bytes} - fetches = { - 'hypotheses': topk.decoded, - 'scores': topk.scores, - 'src_frames': input_batch_src.src_inputs, - 'encoder_frames': encoder_outputs.encoded - } - - return fetches, feeds diff --git a/lingvo/tasks/asr/model_test.py b/lingvo/tasks/asr/model_test.py deleted file mode 100644 index 378e1248..00000000 --- a/lingvo/tasks/asr/model_test.py +++ /dev/null @@ -1,430 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for Asr Model.""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import cluster_factory -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import summary_utils -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tasks.asr import decoder -from lingvo.tasks.asr import input_generator -from lingvo.tasks.asr import model -from lingvo.tasks.asr import model_test_input_generator as tig -import numpy as np - - -class DecoderForTest(decoder.AsrDecoder): - """Unit test class for AsrDecoder with functional.for based unrolling.""" - - @classmethod - def Params(cls): - p = super().Params() - p.use_while_loop_based_unrolling = False - return p - - -class AsrModelTest(test_utils.TestCase): - - def _testParams(self): - input_shape = [2, 16, 8, 3] - p = model.AsrModel.Params() - p.decoder.target_seq_len = 5 - p.encoder.input_shape = input_shape - p.input = tig.TestInputGenerator.Params() - p.input.target_max_length = 5 - p.input.source_shape = input_shape - p.input.target_shape = [2, 5] - p.name = 'test_mdl' - return p - - def testMakeDecoderTheta(self): - # Test that decoder theta returns a copy of theta.decoder without changes. - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(93820985) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - decoder_theta = mdl._MakeDecoderTheta(theta=mdl.theta, input_batch=None) - mdl.BProp() - self.assertEqual(decoder_theta, mdl.theta.decoder) - - def testFProp(self): - with self.session(use_gpu=False): - tf.random.set_seed(93820985) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - self.evaluate(tf.global_variables_initializer()) - test_utils.CompareToGoldenSingleFloat(self, 4.472597, mdl.loss.eval()) - - actual_var_names = [_.name for _ in tf.trainable_variables()] - print('all vars \n', '\n'.join(actual_var_names)) - expected_var_names = [ - 'test_mdl/enc/conv_L0/w/var:0', - 'test_mdl/enc/conv_L0/beta/var:0', - 'test_mdl/enc/conv_L0/gamma/var:0', - 'test_mdl/enc/conv_L1/w/var:0', - 'test_mdl/enc/conv_L1/beta/var:0', - 'test_mdl/enc/conv_L1/gamma/var:0', - 'test_mdl/enc/f_conv_lstm_0/wm/var:0', - 'test_mdl/enc/f_conv_lstm_0/b/var:0', - 'test_mdl/enc/b_conv_lstm_0/wm/var:0', - 'test_mdl/enc/b_conv_lstm_0/b/var:0', - 'test_mdl/enc/conv_lstm_cnn_0/w/var:0', - 'test_mdl/enc/conv_lstm_cnn_0/beta/var:0', - 'test_mdl/enc/conv_lstm_cnn_0/gamma/var:0', - 'test_mdl/enc/fwd_rnn_L0/wm/var:0', - 'test_mdl/enc/fwd_rnn_L0/b/var:0', - 'test_mdl/enc/bak_rnn_L0/wm/var:0', - 'test_mdl/enc/bak_rnn_L0/b/var:0', - 'test_mdl/enc/proj_L0/w/var:0', - 'test_mdl/enc/proj_L0/beta/var:0', - 'test_mdl/enc/proj_L0/gamma/var:0', - 'test_mdl/enc/fwd_rnn_L1/wm/var:0', - 'test_mdl/enc/fwd_rnn_L1/b/var:0', - 'test_mdl/enc/bak_rnn_L1/wm/var:0', - 'test_mdl/enc/bak_rnn_L1/b/var:0', - 'test_mdl/enc/proj_L1/w/var:0', - 'test_mdl/enc/proj_L1/beta/var:0', - 'test_mdl/enc/proj_L1/gamma/var:0', - 'test_mdl/enc/fwd_rnn_L2/wm/var:0', - 'test_mdl/enc/fwd_rnn_L2/b/var:0', - 'test_mdl/enc/bak_rnn_L2/wm/var:0', - 'test_mdl/enc/bak_rnn_L2/b/var:0', - 'test_mdl/dec/emb/var_0/var:0', - 'test_mdl/dec/rnn_cell/wm/var:0', - 'test_mdl/dec/rnn_cell/b/var:0', - 'test_mdl/dec/atten/source_var/var:0', - 'test_mdl/dec/atten/query_var/var:0', - 'test_mdl/dec/atten/hidden_var/var:0', - 'test_mdl/dec/softmax/weight_0/var:0', - 'test_mdl/dec/softmax/bias_0/var:0', - ] - self.assertCountEqual(expected_var_names, actual_var_names) - - def testDecode(self): - with self.session(use_gpu=False): - tf.random.set_seed(93820985) - p = self._testParams() - mdl = p.Instantiate() - input_batch = mdl.input_generator.GetPreprocessedInputBatch() - dec_out_dict = mdl.DecodeWithTheta(mdl.theta, input_batch) - self.evaluate(tf.global_variables_initializer()) - dec_out = self.evaluate(dec_out_dict) - print('dec_out', dec_out) - metrics_dict = mdl.CreateDecoderMetrics() - key_value_pairs = mdl.PostProcessDecodeOut(dec_out, metrics_dict) - - self.assertEqual(1.0, metrics_dict['wer'].value) - self.assertEqual(1.0, metrics_dict['norm_wer'].value) - self.assertEqual(1.0, metrics_dict['ter'].value) - self.assertEqual(0, len(key_value_pairs)) - - def testPostProcessDecodeOut(self): - p = self._testParams() - p.decoder.beam_search.num_hyps_per_beam = 2 - mdl = p.Instantiate() - fake_dec_out = { - 'utt_id': ['utt1', 'utt2'], - 'transcripts': ['a b c d', 'a'], - 'topk_decoded': [['a b c d', 'a b c d'], ['wrong', '']], - 'topk_scores': [[1.0, 0.9], [1.0, 0.9]], - 'topk_ids': [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]], - 'topk_lens': [2, 4, 4, 2], - 'target_labels': [[1, 2, 3, 4], [2, 3, 4, 5]], - 'target_paddings': [[0, 0, 0, 1], [0, 0, 0, 1]], - 'norm_wer_errors': [[0, 0], [1, 1]], - 'norm_wer_words': [[4, 4], [1, 1]], - } - fake_dec_out = {k: np.array(v) for k, v in fake_dec_out.items()} - metrics_dict = mdl.CreateDecoderMetrics() - key_value_pairs = mdl.PostProcessDecodeOut(fake_dec_out, metrics_dict) - - self.assertEqual(0 + 1, metrics_dict['wer'].total_value) - self.assertEqual(4 + 1, metrics_dict['wer'].total_weight) - self.assertEqual(0 + 1, metrics_dict['norm_wer'].total_value) - self.assertEqual(4 + 1, metrics_dict['norm_wer'].total_weight) - self.assertEqual(4, metrics_dict['ter'].total_value) - self.assertEqual(6, metrics_dict['ter'].total_weight) - self.assertEqual(2, metrics_dict['num_samples_in_batch'].total_value) - self.assertEqual(1.0, metrics_dict['num_samples_in_batch'].total_weight) - self.assertEqual((4 / 5 * 3 / 3 * 2 / 2 * 1 / 1)**(1 / 4), - metrics_dict['corpus_bleu'].value) - self.assertEqual((0 + 1) / 2, metrics_dict['sacc'].value) - self.assertEqual((0 + 1) / (4 + 1), metrics_dict['oracle_norm_wer'].value) - self.assertEqual(0, len(key_value_pairs)) - - def testPostProcessDecodeOutFiltersEpsilonTokensForWER(self): - p = self._testParams() - p.decoder.beam_search.num_hyps_per_beam = 1 - mdl = p.Instantiate() - fake_dec_out = { - 'utt_id': ['utt1', 'utt2'], - 'transcripts': ['a b c d', 'a b c'], - 'topk_decoded': [['a bc d'], ['a b']], - 'topk_scores': [[1.0], [1.0]], - 'topk_ids': [[1, 2, 3, 4], [2, 3, 4, 5]], - 'topk_lens': [3, 4], - 'target_labels': [[1, 2, 3, 4], [2, 3, 4, 5]], - 'target_paddings': [[0, 0, 0, 1], [0, 0, 1, 1]], - 'norm_wer_errors': [[0], [1]], - 'norm_wer_words': [[4], [3]], - } - fake_dec_out = {k: np.array(v) for k, v in fake_dec_out.items()} - metrics_dict = mdl.CreateDecoderMetrics() - kv_pairs = mdl.PostProcessDecodeOut(fake_dec_out, metrics_dict) - - self.assertEqual(0 + 1, metrics_dict['wer'].total_value) - self.assertEqual(7, metrics_dict['wer'].total_weight) - self.assertEqual(0 + 1, metrics_dict['norm_wer'].total_value) - self.assertEqual(7, metrics_dict['norm_wer'].total_weight) - self.assertEqual(0, len(kv_pairs)) - - def testPostProcessDecodeOutFiltersNoiseTokensForWER(self): - p = self._testParams() - p.decoder.beam_search.num_hyps_per_beam = 1 - mdl = p.Instantiate() - fake_dec_out = { - 'utt_id': ['utt1', 'utt2'], - 'transcripts': ['a b c d', 'a b c'], - 'topk_decoded': [['a b c d'], [' a b ']], - 'topk_scores': [[1.0], [1.0]], - 'topk_ids': [[1, 2, 3, 4], [2, 3, 4, 5]], - 'topk_lens': [3, 4], - 'target_labels': [[1, 2, 3, 4], [2, 3, 4, 5]], - 'target_paddings': [[0, 0, 0, 1], [0, 0, 1, 1]], - 'norm_wer_errors': [[0], [1]], - 'norm_wer_words': [[4], [3]], - } - fake_dec_out = {k: np.array(v) for k, v in fake_dec_out.items()} - metrics_dict = mdl.CreateDecoderMetrics() - kv_pairs = mdl.PostProcessDecodeOut(fake_dec_out, metrics_dict) - - self.assertEqual(0 + 1, metrics_dict['wer'].total_value) - self.assertEqual(7, metrics_dict['wer'].total_weight) - self.assertEqual(0 + 1, metrics_dict['norm_wer'].total_value) - self.assertEqual(7, metrics_dict['norm_wer'].total_weight) - self.assertEqual(0, len(kv_pairs)) - - def testPostProcessDecodeOutHandlesEmptyRef(self): - p = self._testParams() - p.decoder.beam_search.num_hyps_per_beam = 1 - mdl = p.Instantiate() - fake_dec_out = { - 'utt_id': ['utt1', 'utt2'], - 'transcripts': ['', 'a b c d'], - 'topk_decoded': [['a'], ['a b c d']], - 'topk_scores': [[1.0], [1.0]], - 'topk_ids': [[1, 2, 3, 4], [2, 3, 4, 5]], - 'topk_lens': [3, 4], - 'target_labels': [[1, 2, 3, 4], [2, 3, 4, 5]], - 'target_paddings': [[1, 1, 1, 1], [0, 0, 1, 1]], - 'norm_wer_errors': [[1], [0]], - 'norm_wer_words': [[0], [4]], - } - fake_dec_out = {k: np.array(v) for k, v in fake_dec_out.items()} - metrics_dict = mdl.CreateDecoderMetrics() - mdl.PostProcessDecodeOut(fake_dec_out, metrics_dict) - - self.assertEqual(1 + 0, metrics_dict['wer'].total_value) - self.assertEqual(0 + 4, metrics_dict['wer'].total_weight) - self.assertEqual(1 + 0, metrics_dict['norm_wer'].total_value) - self.assertEqual(0 + 4, metrics_dict['norm_wer'].total_weight) - - def testBProp(self): - with self.session(use_gpu=False): - tf.random.set_seed(93820985) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - mdl.BProp() - self.evaluate(tf.global_variables_initializer()) - test_utils.CompareToGoldenSingleFloat(self, 4.472597, mdl.loss.eval()) - mdl.train_op.run() - - def testBPropSmoothDecay(self): - with self.session(use_gpu=False): - tf.random.set_seed(93820985) - p = self._testParams() - p.train.lr_schedule = ( - schedule.ContinuousSchedule.Params().Set( - start_step=350000, half_life_steps=45000)) - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - mdl.BProp() - self.evaluate(tf.global_variables_initializer()) - test_utils.CompareToGoldenSingleFloat(self, 4.472597, mdl.loss.eval()) - mdl.train_op.run() - - def testAllLayerParams(self): - with self.session(use_gpu=False, graph=tf.Graph()): - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - lps = base_layer.RecursiveFindLayerParams(mdl.params) - l_names = sorted([p.cls.__name__ for p in lps]) - expected_layers = sorted([ - 'Adam', - 'AdditiveAttention', - 'AsciiTokenizer', - 'AsrDecoder', - 'AsrEncoder', - 'AsrModel', - 'BatchNormLayer', - 'BeamSearchHelper', - 'GreedySearchHelper', - 'TargetSequenceSampler', - 'ConvLSTMCell', - 'Conv2DLayer', - 'Conv2DLayer', - 'EmbeddingLayer', - 'HighwaySkipLayer', - 'LSTMCellSimple', - 'LSTMCellSimple', - 'LayerNorm', - 'NullContextualizer', - 'NullFusion', - 'NullLm', - 'Learner', - 'MultitaskAdapterLayer', - 'PiecewiseConstantSchedule', - 'ProjectionLayer', - 'SimpleFullSoftmax', - 'SpectrumAugmenter', - 'StackingOverTime', - 'TestInputGenerator', - ]) - self.assertEqual(expected_layers, l_names) - - def testParamValueSumSquared(self): - with self.session(use_gpu=False, graph=tf.Graph()): - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - all_vars = tf.trainable_variables() - py_utils.SumSquared(all_vars) - - def testCollectVarHistogram(self): - with self.session(use_gpu=False, graph=tf.Graph()): - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - var_grads = py_utils.ComputeGradients(mdl.loss, mdl.vars) - summary_utils.CollectVarHistogram(var_grads) - - def testGradientMult(self): - with self.session(use_gpu=False, graph=tf.Graph()): - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - var_grads = py_utils.ComputeGradients(mdl.loss, mdl.vars) - py_utils.ApplyGradMultiplier(var_grads, -1.1) - - def testLRDecay(self): - with self.session(use_gpu=False, graph=tf.Graph()): - p = self._testParams() - tp = p.train - tp.lr_schedule.boundaries = [300000, 400000, 500000] - tp.lr_schedule.values = [1.0, 0.1, 0.01, 0.001] - lrs = tp.lr_schedule.Instantiate() - steps = [299999, 300001, 399999, 400001, 499999, 500001] - fetches = [lrs.Value(_) for _ in steps] - values = self.evaluate(fetches) - self.assertAllClose([1.0, 0.1, 0.1, 0.01, 0.01, 0.001], values) - - def testBatchSplit(self): - - def Run(num_splits): - p = self._testParams() - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(93820981) - p.input.cur_iter_in_seed = False - p.input.bucket_batch_limit = [ - b * 2 / num_splits for b in p.input.bucket_batch_limit - ] - with cluster_factory.ForTestingWorker(gpus=num_splits, do_eval=True): - mdl = p.Instantiate() - metrics = mdl.FPropDefaultTheta()[0] - self.evaluate(tf.global_variables_initializer()) - return self.evaluate(metrics['loss']) - - res1, res2 = Run(1), Run(2) - self.assertAllClose(res1[0], res2[0]) - self.assertAllEqual(res1[1], res2[1]) - - def testInference(self): - - def _CreateModelParamsForTest(): - p = model.AsrModel.Params() - p.name = 'test_config' - - # Encoder params. - ep = p.encoder - ep.input_shape = [None, None, 80, 1] - ep.lstm_cell_size = 16 - ep.num_lstm_layers = 2 - ep.conv_filter_shapes = [(3, 3, 1, 32), (3, 3, 32, 32)] - ep.conv_filter_strides = [(2, 2), (2, 2)] - ep.num_conv_lstm_layers = 0 - # Initialize decoder params. - dp = p.decoder - dp.rnn_cell_dim = 16 - dp.rnn_layers = 2 - dp.source_dim = ep.lstm_cell_size * 2 - # Use functional while based unrolling. - dp.use_while_loop_based_unrolling = False - - p.input = input_generator.AsrInput.Params() - ip = p.input - ip.frame_size = 80 - ip.append_eos_frame = True - ip.pad_to_max_seq_length = False - return p - - with self.session( - use_gpu=False, graph=tf.Graph()) as sess, self.SetEval(True): - p = _CreateModelParamsForTest() - mdl = p.Instantiate() - subgraphs = mdl.Inference() - self.assertIn('default', subgraphs) - - fetches, feeds = subgraphs['default'] - self.assertIn('wav', feeds) - for name in ['hypotheses', 'scores', 'src_frames', 'encoder_frames']: - self.assertIn(name, fetches) - - with open( - test_helper.test_src_dir_path('tools/testdata/gan_or_vae.16k.wav'), - 'rb') as f: - wav = f.read() - self.evaluate(tf.global_variables_initializer()) - fetches = sess.run(fetches, {feeds['wav']: wav}) - - self.assertAllEqual((1, p.decoder.beam_search.num_hyps_per_beam), - fetches['hypotheses'].shape) - self.assertAllEqual((1, p.decoder.beam_search.num_hyps_per_beam), - fetches['scores'].shape) - self.assertAllEqual((1, 314, p.encoder.input_shape[2], 1), - fetches['src_frames'].shape) - self.assertAllEqual((80, 1, 2 * p.encoder.lstm_cell_size), - fetches['encoder_frames'].shape) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/asr/model_test_input_generator.py b/lingvo/tasks/asr/model_test_input_generator.py deleted file mode 100644 index 28baa243..00000000 --- a/lingvo/tasks/asr/model_test_input_generator.py +++ /dev/null @@ -1,268 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Simple input generator used for ASR model tests.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import py_utils - - -class TestInputGenerator(base_input_generator.BaseSequenceInputGenerator): - """A simple InputGenerator that delegate requests to another obj.""" - - @classmethod - def Params(cls): - p = super().Params() - p.random_seed = 20349582 - p.Define('feature_dims', 240, 'Feature dims') - p.Define('num_channels', 3, 'Data are preprocessed into these many ' - 'channels per timestep. E.g., feature_dims=240 is actually ' - '[80, 3], i.e., 3 channels, each with a 40-dim feature vector.') - p.Define('source_shape', [2, 10, 8, 3], 'source shape.') - p.Define('target_shape', [2, 5], 'targets shape.') - p.Define('fixed_target_labels', None, - 'If not None, use these as the targets instead of generating ' - 'random targets and set target padding to 0. Must have same ' - 'shape as target_shape.') - p.Define( - 'fixed_target_ids', None, - 'If not None, use these as the target_ids instead of generating ' - 'random targets. Must have same shape as target_shape.') - p.Define('cur_iter_in_seed', True, 'use current iter value in seed ' - 'computation.') - p.Define('integer_source_max', None, 'Generate integers as source values ' - 'with this value as an upper bound.') - p.Define( - 'float_source_max', None, 'Generate floats as source values ' - 'with this value as an upper bound.') - p.Define('for_mt', False, 'True if this is for mt models; ' - 'this affects some parts of batch generation') - p.Define('target_key', '', 'If non-empty, targets will be specified in ' - 'batch.additional_tgts[target_key] instead of batch.tgt.') - p.Define('target_key_target_shape', [2, 5], 'Shape of the targets stored ' - 'batch.additional_tgts[target_key].') - p.Define('set_tgt_and_additional_tgts', False, 'If true, ' - 'both batch.tgt and batch.additional_tgts[target_key] will ' - 'be set. target_key_target_shape must be specified.') - p.Define('target_language', 'ENGLISH', - 'The target language. Both language name (e.g. "ENGLISH") and ' - 'language code (e.g. "zh-CN") are acceptted.') - p.Define('align_label_with_frame', False, - 'Whether to generate label-frame alignments.') - p.Define( - 'bprop_filters', [], 'If set, simulates a multi source' - 'input and sets filters for each source i.e the first filter' - 'corresponds to the first source etc. The number of sources is set' - 'to the length of this param.') - p.Define( - 'number_sources', None, 'Integer which specifies the number of' - 'sources. Cannot be used along with bprop_filters.') - p.Define( - 'source_selected', None, 'Integer which specifies the index of the' - 'source selected. Corresponds to the data source that would be' - 'sampled by the input_generator when given multiple file_patterns.' - 'This has an effect only when number_sources is set and greater than 1.' - 'Can use either constant values or a tensor like' - 'tf.math.floormod(tf.train.get_or_create_global_step(), num_sources)') - p.Define('target_transcript', 'dummy_transcript', - 'Text to use for transcript.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._bprop_variable_filters = [''] - self._bprop_onehot = tf.constant([1], dtype=tf.float32) - if p.target_key and not p.target_key_target_shape: - raise ValueError('target_key_target_shape must be set when ' - 'target_key (%s) is not empty.' % p.target_key) - if (p.set_tgt_and_additional_tgts and - (p.target_key_target_shape[0] != p.target_shape[0])): - raise ValueError('The first dimension of target_key_target_shape (%d) ' - 'should match the first dimension of target_shape ' - '(%d) when both have to be set.' % - (p.target_key_target_shape[0], p.target_shape[0])) - self._cur_iter = 0 - if p.bprop_filters and p.number_sources: - raise ValueError( - 'Number of sources will be set to length of bprop_filters, the param' - 'number_sources should not be used when bprop_filters is set.') - number_sources = p.number_sources - if p.bprop_filters: - self._bprop_variable_filters = p.bprop_filters - number_sources = len(p.bprop_filters) - if number_sources and number_sources > 1: - self._bprop_onehot = tf.one_hot( - p.source_selected, number_sources, dtype=tf.float32) - - def _check_paddings(self, paddings): - with tf.name_scope('check_paddings'): - unpacked_paddings = tf.unstack(paddings) - - non_decr = [] - for t in unpacked_paddings: - non_d = tf.math.is_non_decreasing(t) - non_decr.append(non_d) - all_non_decr = tf.stack(non_decr) - - paddings = py_utils.with_dependencies([ - tf.assert_equal( - tf.reduce_any(tf.equal(paddings, 0.0)), - True, - message='must have at least one zero value.'), - tf.assert_equal(all_non_decr, True, message='must be non-decreasing') - ], paddings) - return paddings - - def GetBpropParams(self): - return self._bprop_params - - def GetBpropType(self): - """Get the current bprop type of the input generator batch.""" - return self._bprop_onehot - - def SampleIds(self): - p = self.params - if p.cur_iter_in_seed: - random_seed = p.random_seed * 2000 * self._cur_iter - else: - random_seed = p.random_seed * 2000 - return tf.as_string(tf.random.uniform(p.target_shape[:1], seed=random_seed)) - - def _Sources(self): - p = self.params - if p.cur_iter_in_seed: - self._cur_iter += 1 - - if p.integer_source_max: - inputs = tf.random.uniform( - p.source_shape, - maxval=p.integer_source_max, - dtype=tf.int32, - seed=p.random_seed + 1000 * self._cur_iter) - elif p.float_source_max: - inputs = tf.random.uniform( - p.source_shape, - maxval=p.float_source_max, - seed=p.random_seed + 1000 * self._cur_iter) - else: - inputs = tf.random.normal( - p.source_shape, seed=p.random_seed + 1000 * self._cur_iter) - - paddings = tf.cast( - tf.cumsum( - tf.random.uniform( - p.source_shape[:2], seed=p.random_seed + 1001 * self._cur_iter), - axis=1) > 0.5 * p.source_shape[1], tf.float32) - - paddings = self._check_paddings(paddings) - - return inputs, paddings - - def _Targets(self, target_shape): - p = self.params - if p.cur_iter_in_seed: - self._cur_iter += 1 - random_seed = p.random_seed * 2000 * self._cur_iter - - if p.fixed_target_ids is None: - tids = tf.cast( - tf.random.uniform(target_shape, seed=random_seed) * - p.tokenizer.vocab_size, tf.int32) - else: - tids = p.fixed_target_ids - assert tids.shape_as_list() == target_shape - - if p.fixed_target_labels is None: - tlabels = tf.cast( - tf.random.uniform(target_shape, seed=random_seed + 1) * - p.tokenizer.vocab_size, tf.int32) - tpaddings = tf.cast( - tf.cumsum( - tf.random.uniform( - target_shape[:2], seed=p.random_seed + 1001 * self._cur_iter), - axis=1) > 0.4 * target_shape[1], tf.float32) - tpaddings = self._check_paddings(tpaddings) - else: - tlabels = p.fixed_target_labels - assert tlabels.shape_as_list() == target_shape - tpaddings = tf.constant(0.0, shape=target_shape) - tweights = 1.0 - tpaddings - d = { - 'ids': tids, - 'labels': tlabels, - 'weights': tweights, - 'paddings': tpaddings - } - if not p.for_mt: - d['transcripts'] = tf.constant( - p.target_transcript, shape=[target_shape[0]]) - if p.align_label_with_frame: - source_len = p.source_shape[1] - d['alignments'] = tf.cast( - tf.random.uniform(target_shape, seed=p.random_seed) * source_len, - tf.int32) - return d - - def GlobalBatchSize(self): - p = self.params - return tf.constant(p.target_shape[0]) - - def _InputBatch(self): - p = self.params - ret = py_utils.NestedMap() - ret.src = py_utils.NestedMap() - input_name = 'ids' if p.for_mt else 'src_inputs' - ret.src[input_name], ret.src.paddings = self._Sources() - - # Set tgts only when needed: If target_key is specified, and both tgt and - # additional_tgts are not needed, we only set additional_tgts. This is - # useful when testing a model that solely uses additional_tgts instead - # of tgt. - if not p.target_key or p.set_tgt_and_additional_tgts: - ret.tgt = py_utils.NestedMap(self._Targets(p.target_shape)) - else: - ret.tgt = None - if p.target_key: - ret.additional_tgts = py_utils.NestedMap() - ret.additional_tgts[p.target_key] = py_utils.NestedMap( - self._Targets(p.target_key_target_shape)) - ret.sample_ids = self.SampleIds() - - # Cast floating point tensors to the fprop dtype (default: float32). - def _CastFloats(v): - if v is None: - return None - return tf.cast(v, py_utils.FPropDtype(p)) if v.dtype.is_floating else v - - ret.source_selected = tf.tile( - tf.expand_dims(self._bprop_onehot, 0), [p.source_shape[0], 1]) - return ret.Transform(_CastFloats) - - def _GetSourceInputsAndLabels(self, data_source): - p = self.params - src_inputs, src_paddings, labels = data_source - # The data are laid out in the channel-major order. In order to move channel - # to the last dimension, a tf.transpose of the data is needed. - src_inputs = tf.transpose( - tf.reshape( - src_inputs, - tf.concat([tf.shape(src_inputs)[:-1], [p.num_channels, -1]], 0)), - [0, 1, 3, 2]) - return src_inputs, src_paddings, labels - - def SetBpropType(self): - """Get the current bprop type of the input generator batch.""" - self._bprop_index = tf.one_hot(1, 2) diff --git a/lingvo/tasks/asr/params/BUILD b/lingvo/tasks/asr/params/BUILD deleted file mode 100644 index 9a9e51ed..00000000 --- a/lingvo/tasks/asr/params/BUILD +++ /dev/null @@ -1,38 +0,0 @@ -# Params for ASR models. - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -filegroup( - name = "param_files", - srcs = glob( - include = [ - "*.py", - ], - ), -) - -py_library( - name = "params", - srcs = [ - ":param_files", - ], - data = ["//lingvo/tasks/asr:wpm_files"], - srcs_version = "PY3", - tags = ["keep_dep"], - deps = [ - "//lingvo:model_registry", - "//lingvo/core:base_model_params", - "//lingvo/core:datasource", - "//lingvo/core:program_lib", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - "//lingvo/core:tokenizers", - "//lingvo/tasks/asr:input_generator", - "//lingvo/tasks/asr:model", - "//lingvo/tasks/asr:ctc_model", - ], -) diff --git a/lingvo/tasks/asr/params/README.md b/lingvo/tasks/asr/params/README.md deleted file mode 100644 index dcc7ab5b..00000000 --- a/lingvo/tasks/asr/params/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Setting up your machine for featurization / training / eval - -Set up conda environment and build lingvo using bazel on VM / local machine -```sh -# install ubuntu dependencies first -sudo apt install wget git screen neovim build-essential htop parallel sox libsox-fmt-mp3 - -# Download miniconda -wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -chmod +x Miniconda3-latest-Linux-x86_64.sh -./Miniconda3-latest-Linux-x86_64.sh - -# Create conda environment for lingvo, includes development tools (like yapf, pylint) as well as cuda -conda env create -n lingvo -f lingvo-copy/environment.yml -echo "conda activate lingvo" >> ~/.bashrc -bazel build -c opt //lingvo:trainer -``` -If you are running featurization you will need to create an extra volume when provisioning the VM. Then you will need to mount it. I have added the commands to `setup.sh` just in case, you can also find it in google cloud documentation. - -# Submit a training job on TPU -Make sure you are inside a google cloud cpu VM, and using the `lingvo` conda environment. -```sh -./submit.sh $device_name executor $class_model_name $fldr_name - -## Example usage: -./submit.sh ag-tpu1-1019 executor Grphm_DO_SpecAug_ConvStk_6x512Bidi_40batchsize 1127 -``` - -`device_name`: The `device_name` contains `-tpu`, script assumes you are training on a tpu. If a TPU by that name exists, the TPU is reused, if not, a new TPU is created with that name and the job is launched. `device_name` can also be `gpu` or `cpu`, and the job is launched locally in either of these cases. - -`executor`: Lingvo has a bunch of `runner` classes for managing the training loop. We use the `executor_tpu` for training on the tpu. Use `executor` when training on tpu, or `trainer` when running locally on cpu/gpu. Training on local machines is not fully tested, you may have to change batch size, data dir etc when training locally. -The tpu_executor runs a few iterations of training, then a few iterations of evaluation on the train and dev set, so you'll see 3 loss curves on tensorboard. -- train-train: training loss on the training dataset with augmentation (dropout, specuagment) -- eval-train: eval loss on the training dataset, without augmentation (should be better than training loss) -- eval-dev: eval loss on the dev dataset. I use this curve for picking which model to choose for evaluation. - -`class_model_name`: One of the fully specified model clases available in `lingvo/tasks/asr/params/peoplesspeech_ctc.py`. Use this class to specify all hyper-parameters. The class name in example above is our sota hyper-parameter spec for librispeech. You can also find the location of the training / dev / test data specified in this file. - -`fldr_name`: Google cloud storage suffix where to save the checkpoints and tensorboard events. *IMPORTANT*: Change the GS_BASE path in `tpu_submit` to your directory (right now it's pointing to `gs://the-peoples-speech-west-europe/PeoplesSpeech/ag_training`). If there are checkpoints of `$class_model_name` in `$fldr_name` directory, you will be restarting that job, and the folder will be reused for new checkpoints. - -# Eval a model on GPU/CPU: -We could not get the decoder / evaluation to run on TPU. Lingvo supports marking some functions to be only run on the tpu_host", but even this was not sufficient to make the decoding work, while the forward prop is running on the tpu. Therefore, I run the eval on my local gpu machine. You could also run on a cpu machine, but that is super slow. On a gtx1070 evaluation finishes in ~15 minutes. Make sure you set up the conda environment, just like above, and setup google cloud cli from https://cloud.google.com/sdk/docs/install - -Download the latest checkpoint, and tensorboard events of all the models in the experiment group to `$HOME_BASE` which is hardcoded in `download_models.sh`. -```sh -./download_models.sh ${fldr_name} - -## Example usage: -./download_models.sh 1127 -``` -Downloads from `gs://the-peoples-speech-west-europe/PeoplesSpeech/ag_training/1127` to `/home/anjali/data/PeoplesSpeech/models/1127`. Now you can evaluate the model on the dev set and see sample predictions, cer, wer using -```sh -./submit.sh gpu decode Grphm_DO_SpecAug_ConvStk_6x512Bidi_40batchsize 1127 -``` -Since I run locally on my home machine, I sometimes run into google cloud storage errors when evaling. So I have a copy of the devtest set locally - you may need this hack. You could also just get a small gpu vm for 1 hour and avoid all the model and dataset downloading. - -After one pass, the process will keep monitoring the checkpoint directory for new checkpoints. The decoder job dumps a lot of the decoded output to tensorboard, so you should not let the decoder continue past an epoch. - -# Featurizing a new dataset: -Make sure you have allocated a large disk on a large VM (>16 cores). See setup instructions above. -```sh -# Copy all the tarballs and csv files locally, takes a few hours -mkdir -p /mnt/disks/dataset/raw/v0.7/raw /mnt/disks/dataset/raw/v0.7/feats -gsutil -m rsync the-peoples-speech-west-europe/peoples-speech-v0.7 /mnt/disks/dataset/raw/v0.7/raw - -# featurize the dataset, usually takes ~24 hours -# ./featurize.sh $input_dir $output_dir -./featurize.sh /mnt/disks/dataset/raw/v0.7/raw /mnt/disks/dataset/raw/v0.7/feats - -# Upload the tfrecords back to the cloud -gsutil -m rsync /mnt/disks/dataset/raw/v0.7/feats gs://the-peoples-speech-west-europe/PeoplesSpeech/v0.7.1 -``` -`featurize.sh` calls `create_peoples_speech_asr_features.py` which first loads the csv file to read the audio filenames and transcripts (other metadata is thrown away) into a dictionary, keyed by the audio filepath. Then it makes a pass through the tarball, reading one audio file at a time, standardizing it (to 16Khz, single channel, 16 bit wav audio), calculates mel spectrograms (80 bins, 25ms window, 10ms step) and then saves it to tfrecord. - -The featurization outputs 1 tfrecord file each for dev and test set. Training dataset is split into 512 tfrecords, and we use 16 processes in parallel to do the featurization. Worker 0 outputs files 0..31, worker 1 outputs 32..63 and so on. I picked 512 files so that each tfrecord file is around 1GB, same as librispeech. Therefore, there are 512 output_shards, and 16 worker shards. diff --git a/lingvo/tasks/asr/params/inference_only.py b/lingvo/tasks/asr/params/inference_only.py deleted file mode 100644 index 2a23b869..00000000 --- a/lingvo/tasks/asr/params/inference_only.py +++ /dev/null @@ -1,94 +0,0 @@ -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.core import datasource -from lingvo.core import program -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import tokenizers -from lingvo.tasks.asr import input_generator -from lingvo.tasks.asr import ctc_model -from lingvo.tasks.asr import frontend as asr_frontend - -@model_registry.RegisterSingleTaskModel -class InferenceOnly(base_model_params.SingleTaskModelParams): - def RawInputParams(self): - """ - Reads in a raw waveform, where samples are float32 and in the range[-1,1] - """ - p = input_generator.AsrInput.Params() - - p.file_datasource = datasource.PrefixedDataSource.Params() - p.file_datasource.file_type = 'tfrecord' - p.file_datasource.file_pattern_prefix = 'gs://the-peoples-speech-west-europe/Librispeech' - - # We have only - p.frame_size = 1 - p.append_eos_frame = False - - p.pad_to_max_seq_length = True - p.file_random_seed = 0 - p.file_buffer_size = 10000 - # Set this to whatever - p.file_parallelism = 2 - - # Should be set very high... - p.source_max_length = 40_000 # 7412603 - 7389537 - p.bucket_upper_bound = [40_000] - - p.bucket_batch_limit = [1] - - return p - - def Inference(self): - p = self.RawInputParams() - p.file_datasource.file_pattern = '*.tfrecord' - # Keep it simple for now. This should be overridable - p.num_samples = 8 - return p - - def Task(self): - p = ctc_model.CTCModel.Params() - p.name = 'librispeech' - - # This is copied from audio_lib.py. Ideally these configurations - # would be split off into a function, but I want to minimize merge - # conflicts with lingvo for now. Of course, a merge conflict would - # indicate that parameters had changed, so my choice is clearly - # wrong, but oh well. - p.frontend = asr_frontend.MelAsrFrontend.Params() - pf = p.frontend - pf.sample_rate = 16000. - pf.frame_size_ms = 25. - pf.frame_step_ms = 10. - pf.num_bins = 80 - pf.lower_edge_hertz = 125. - pf.upper_edge_hertz = 7600. - pf.preemph = 0.97 - pf.noise_scale = 0. - pf.pad_end = False - - # No default encoder params in this class. - - tp = p.train - tp.learning_rate = 1e-4 - tp.lr_schedule = schedule.ContinuousSchedule.Params().Set( - start_step=25_000, half_life_steps=5_000, min=1e-6) - tp.scale_gradients = False - tp.l2_regularizer_weight = None - - # Setting p.eval.samples_per_summary to a large value ensures that dev, - # devother, test, testother are evaluated completely (since num_samples for - # each of these sets is less than 5000), while train summaries will be - # computed on 5000 examples. - p.eval.samples_per_summary = 2700 - p.eval.decoder_samples_per_summary = 2700 - - return p - - def ProgramSchedule(self): - return program.SimpleProgramScheduleForTask( - train_dataset_name='Train', - train_steps_per_loop=500, - eval_dataset_names=['Inference'], - eval_steps_per_loop=1, - decode_steps_per_loop=0) diff --git a/lingvo/tasks/asr/params/librispeech.py b/lingvo/tasks/asr/params/librispeech.py deleted file mode 100644 index 0e00e7eb..00000000 --- a/lingvo/tasks/asr/params/librispeech.py +++ /dev/null @@ -1,330 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Models for Librispeech dataset.""" - -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.core import datasource -from lingvo.core import program -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import tokenizers -from lingvo.tasks.asr import input_generator -from lingvo.tasks.asr import model - - -@model_registry.RegisterSingleTaskModel -class Librispeech960Base(base_model_params.SingleTaskModelParams): - """Base parameters for Librispeech 960 hour task.""" - - def _CommonInputParams(self, is_eval): - """Input generator params for Librispeech.""" - p = input_generator.AsrInput.Params() - - # Insert path to the base directory where the data are stored here. - # Generated using scripts in lingvo/tasks/asr/tools. - p.file_datasource = datasource.PrefixedDataSource.Params() - p.file_datasource.file_type = 'tfrecord' - p.file_datasource.file_pattern_prefix = '/tmp/librispeech' - - p.frame_size = 80 - p.append_eos_frame = True - - p.pad_to_max_seq_length = False - p.file_random_seed = 0 - p.file_buffer_size = 10000 - p.file_parallelism = 16 - - if is_eval: - p.source_max_length = 3600 - p.bucket_upper_bound = [639, 1062, 1275, 1377, 1449, 1506, 1563, 3600] - else: - p.source_max_length = 3000 - p.bucket_upper_bound = [639, 1062, 1275, 1377, 1449, 1506, 1563, 1710] - - p.bucket_batch_limit = [96, 48, 48, 48, 48, 48, 48, 48] - - return p - - def SetBucketSizes(self, params, bucket_upper_bound, bucket_batch_limit): - """Sets bucket sizes for batches in params.""" - params.bucket_upper_bound = bucket_upper_bound - params.bucket_batch_limit = bucket_batch_limit - return params - - def Train(self): - p = self._CommonInputParams(is_eval=False) - p.file_datasource.file_pattern = 'train/train.tfrecords-*' - p.num_samples = 281241 - return p - - def Dev(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/dev-clean.tfrecords-00000-of-00001') - p.num_samples = 2703 - return p - - def Devother(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/dev-other.tfrecords-00000-of-00001') - p.num_samples = 2864 - return p - - def Test(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/test-clean.tfrecords-00000-of-00001') - p.num_samples = 2620 - return p - - def Testother(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/test-other.tfrecords-00000-of-00001') - p.num_samples = 2939 - return p - - def Task(self): - p = model.AsrModel.Params() - p.name = 'librispeech' - - # Initialize encoder params. - ep = p.encoder - # Data consists 240 dimensional frames (80 x 3 frames), which we - # re-interpret as individual 80 dimensional frames. See also, - # LibrispeechCommonAsrInputParams. - ep.input_shape = [None, None, 80, 1] - ep.lstm_cell_size = 1024 - ep.num_lstm_layers = 4 - ep.conv_filter_shapes = [(3, 3, 1, 32), (3, 3, 32, 32)] - ep.conv_filter_strides = [(2, 2), (2, 2)] - ep.cnn_tpl.params_init = py_utils.WeightInit.Gaussian(0.001) - # Disable conv LSTM layers. - ep.num_conv_lstm_layers = 0 - - # Initialize decoder params. - dp = p.decoder - dp.rnn_cell_dim = 1024 - dp.rnn_layers = 2 - dp.source_dim = 2048 - # Use functional while based unrolling. - dp.use_while_loop_based_unrolling = False - - tp = p.train - tp.learning_rate = 2.5e-4 - tp.lr_schedule = schedule.ContinuousSchedule.Params().Set( - start_step=50000, half_life_steps=100000, min=0.01) - - # Setting p.eval.samples_per_summary to a large value ensures that dev, - # devother, test, testother are evaluated completely (since num_samples for - # each of these sets is less than 5000), while train summaries will be - # computed on 5000 examples. - p.eval.samples_per_summary = 5000 - p.eval.decoder_samples_per_summary = 0 - - # Use variational weight noise to prevent overfitting. - p.vn.global_vn = True - p.train.vn_std = 0.075 - p.train.vn_start_step = 20000 - - return p - - def ProgramSchedule(self): - return program.SimpleProgramScheduleForTask( - train_dataset_name='Train', - train_steps_per_loop=50, - eval_dataset_names=['Test'], - eval_steps_per_loop=5, - decode_steps_per_loop=0) - - -@model_registry.RegisterSingleTaskModel -class Librispeech960Grapheme(Librispeech960Base): - """Base params for Librispeech 960 hour experiments using grapheme models. - - With 8 workers using asynchronous gradient descent on 16 (8x2) GPUs, the model - achieves the following error rates after ~853.2K steps: - - ========= ===== - Dev 5.2% - DevOther 15.2% - Test 5.4% - TestOther 15.5% - ========= ===== - """ - - GRAPHEME_TARGET_SEQUENCE_LENGTH = 620 - GRAPHEME_VOCAB_SIZE = 76 - - def InitializeTokenizer(self, params): - """Initializes a grapheme tokenizer.""" - params.tokenizer = tokenizers.AsciiTokenizer.Params() - tokp = params.tokenizer - tokp.vocab_size = self.GRAPHEME_VOCAB_SIZE - tokp.append_eos = True - tokp.target_unk_id = 0 - tokp.target_sos_id = 1 - tokp.target_eos_id = 2 - - params.target_max_length = self.GRAPHEME_TARGET_SEQUENCE_LENGTH - return params - - def Train(self): - p = super().Train() - return self.InitializeTokenizer(params=p) - - def Dev(self): - p = super().Dev() - return self.InitializeTokenizer(params=p) - - def Devother(self): - p = super().Devother() - return self.InitializeTokenizer(params=p) - - def Test(self): - p = super().Test() - return self.InitializeTokenizer(params=p) - - def Testother(self): - p = super().Testother() - return self.InitializeTokenizer(params=p) - - def Task(self): - p = super().Task() - dp = p.decoder - dp.target_seq_len = self.GRAPHEME_TARGET_SEQUENCE_LENGTH - dp.emb_dim = self.GRAPHEME_VOCAB_SIZE - dp.emb.vocab_size = self.GRAPHEME_VOCAB_SIZE - dp.softmax.num_classes = self.GRAPHEME_VOCAB_SIZE - return p - - -@model_registry.RegisterSingleTaskModel -class Librispeech960GraphemeTpuV2(Librispeech960Grapheme): - """Librispeech 960 grapheme model for training on TPU V2.""" - - def _CommonInputParams(self, is_eval): - p = super()._CommonInputParams(is_eval) - - if py_utils.use_tpu(): - p.pad_to_max_seq_length = True - p.bucket_batch_limit = [48] * len(p.bucket_upper_bound) - p.source_max_length = p.bucket_upper_bound[-1] - - return p - - def Task(self): - p = super().Task() - - p.encoder.pad_steps = 0 - - return p - - -@model_registry.RegisterSingleTaskModel -class Librispeech960Wpm(Librispeech960Base): - """Base params for Librispeech 960 hour experiments using Word Piece Models. - - With 8 workers using asynchronous gradient descent on 16 (8x2) GPUs, the model - achieves the following error rates after ~632.6K steps: - - ========= ===== - Dev 4.3% - DevOther 13.0% - Test 4.5% - TestOther 13.2% - ========= ===== - """ - - # Set this to a WPM vocabulary file before training. By default, we use the - # pre-generated 16K word piece vocabulary checked in under 'tasks/asr/'. - WPM_SYMBOL_TABLE_FILEPATH = ( - 'lingvo/tasks/asr/wpm_16k_librispeech.vocab') - WPM_TARGET_SEQUENCE_LENGTH = 140 - WPM_VOCAB_SIZE = 16328 - - EMBEDDING_DIMENSION = 96 - NUM_TRAINING_WORKERS = 8 - - def InitializeTokenizer(self, params): - """Initializes a Word Piece Tokenizer.""" - params.tokenizer = tokenizers.WpmTokenizer.Params() - tokp = params.tokenizer - tokp.vocab_filepath = self.WPM_SYMBOL_TABLE_FILEPATH - tokp.vocab_size = self.WPM_VOCAB_SIZE - tokp.append_eos = True - tokp.target_unk_id = 0 - tokp.target_sos_id = 1 - tokp.target_eos_id = 2 - - params.target_max_length = self.WPM_TARGET_SEQUENCE_LENGTH - return params - - def Train(self): - p = super().Train() - return self.InitializeTokenizer(params=p) - - def Dev(self): - p = super().Dev() - return self.InitializeTokenizer(params=p) - - def Devother(self): - p = super().Devother() - return self.InitializeTokenizer(params=p) - - def Test(self): - p = super().Test() - return self.InitializeTokenizer(params=p) - - def Testother(self): - p = super().Testother() - return self.InitializeTokenizer(params=p) - - def Task(self): - p = super().Task() - dp = p.decoder - dp.target_seq_len = self.WPM_TARGET_SEQUENCE_LENGTH - dp.emb_dim = self.EMBEDDING_DIMENSION - dp.emb.vocab_size = self.WPM_VOCAB_SIZE - dp.emb.max_num_shards = self.NUM_TRAINING_WORKERS # One shard per worker. - dp.softmax.num_classes = self.WPM_VOCAB_SIZE - - return p - - -@model_registry.RegisterSingleTaskModel -class Librispeech960WpmTpuV2(Librispeech960Wpm): - """Librispeech 960 WPM model for training on TPU V2.""" - - def _CommonInputParams(self, is_eval): - p = super()._CommonInputParams(is_eval) - - if py_utils.use_tpu(): - p.pad_to_max_seq_length = True - p.bucket_batch_limit = [48] * len(p.bucket_upper_bound) - p.source_max_length = p.bucket_upper_bound[-1] - - return p - - def Task(self): - p = super().Task() - - p.encoder.pad_steps = 0 - p.decoder.emb.max_num_shards = 1 - - return p diff --git a/lingvo/tasks/asr/params/librispeech_ctc.py b/lingvo/tasks/asr/params/librispeech_ctc.py deleted file mode 100644 index d881e0bf..00000000 --- a/lingvo/tasks/asr/params/librispeech_ctc.py +++ /dev/null @@ -1,462 +0,0 @@ -from lingvo import model_registry -import lingvo.compat as tf -from lingvo.core import base_model_params -from lingvo.core import datasource -from lingvo.core import program -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import tokenizers -from lingvo.tasks.asr import input_generator -from lingvo.tasks.asr import ctc_model -from lingvo.tasks.asr import frontend as asr_frontend - -VOCAB_FILEPATH = "gs://the-peoples-speech-west-europe/Librispeech/tokens2.txt" - -tf.flags.DEFINE_integer( - 'ctc_inference_model_num_samples', 0, 'Total number of samples to run inference on. Should be equal to input.num_samples (this is checked)') -tf.flags.DEFINE_integer( - 'ctc_inference_model_batch_size', 0, - '') -tf.flags.DEFINE_integer( - 'ctc_inference_model_number_of_tpus', 8, - '') - - - -FLAGS = tf.flags.FLAGS - - -def intialize_vocab_file_tokenizer_params(): - pt = tokenizers.VocabFileTokenizer.Params() - pt.append_eos = False - - # TODO: Don't hard-code this path! How can I make it relative? - pt.token_vocab_filepath = VOCAB_FILEPATH - pt.tokens_delimiter = "" - pt.load_token_ids_from_vocab = False - with tf.io.gfile.GFile(VOCAB_FILEPATH, mode='r') as fh: - lines = fh.readlines() - for i, line in enumerate(lines): - if line == "\n": - pt.target_unk_id = i - elif line == "\n": - pt.target_sos_id = i - elif line == "\n": - pt.target_eos_id = i - pt.vocab_size = len(lines) - assert pt.vocab_size == 32 - assert pt.target_unk_id == 0 - assert pt.target_sos_id == 1, pt.target_sos_id - assert pt.target_eos_id == 2, pt.target_eos_id - - return pt - -@model_registry.RegisterSingleTaskModel -class Librispeech960Base(base_model_params.SingleTaskModelParams): - """Base parameters for Librispeech 960 hour task.""" - - def _CommonInputParams(self, is_eval): - """Input generator params for Librispeech.""" - p = input_generator.AsrInput.Params() - - # Insert path to the base directory where the data are stored here. - # Generated using scripts in lingvo/tasks/asr/tools. - p.file_datasource = datasource.PrefixedDataSource.Params() - p.file_datasource.file_type = 'tfrecord' - p.file_datasource.file_pattern_prefix = 'gs://the-peoples-speech-west-europe/Librispeech' - - p.frame_size = 80 - # Interesting. First I've heard of this. - p.append_eos_frame = False - - if tf.flags.FLAGS.tpu: - p.pad_to_max_seq_length = True - else: - p.pad_to_max_seq_length = False - p.file_random_seed = 0 - p.file_buffer_size = 10000 - p.file_parallelism = 16 - - if is_eval: - p.source_max_length = 3600 - p.bucket_upper_bound = [639, 1062, 1275, 1377, 1449, 1506, 1563, 3600] - else: - # So it looks like - p.source_max_length = 1710 - p.bucket_upper_bound = [639, 1062, 1275, 1377, 1449, 1506, 1563, 1710] - - p.bucket_batch_limit = [48] * 8 - - - p.tokenizer = intialize_vocab_file_tokenizer_params() - - return p - - def SetBucketSizes(self, params, bucket_upper_bound, bucket_batch_limit): - """Sets bucket sizes for batches in params.""" - params.bucket_upper_bound = bucket_upper_bound - params.bucket_batch_limit = bucket_batch_limit - return params - - def Train(self): - p = self._CommonInputParams(is_eval=False) - p.file_datasource.file_pattern = 'train/train.tfrecords-*' - p.num_samples = 281241 - return p - - def Dev(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/dev-clean.tfrecords-00000-of-00001') - p.num_samples = 2703 - p.target_max_length = 516 - # p.source_max_length = 3600 - # p.bucket_upper_bound = [3600] - # p.bucket_batch_limit = [1] - return p - - def Devother(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/dev-other.tfrecords-00000-of-00001') - p.num_samples = 2864 - return p - - def Test(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/test-clean.tfrecords-00000-of-00001') - p.num_samples = 2620 - return p - - def Testother(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/test-other.tfrecords-00000-of-00001') - p.num_samples = 2939 - return p - - def Task(self): - p = ctc_model.CTCModel.Params() - p.name = 'librispeech' - - with tf.io.gfile.GFile(VOCAB_FILEPATH, mode='r') as fh: - lines = fh.readlines() - assert lines[-1] == "\n" - p.vocab_size = len(lines) - p.blank_index = 31 - - assert p.vocab_size == 32 - assert p.blank_index == 31 - - ep = p.encoder_v2 - ep.use_stacking_subsampler = True - - # No default encoder params in this class. - - tp = p.train - tp.learning_rate = 1e-4 - tp.lr_schedule = schedule.ContinuousSchedule.Params().Set( - start_step=25_000, half_life_steps=5_000, min=1e-6) - tp.scale_gradients = False - tp.l2_regularizer_weight = None - - # A value of 0 will force each eval and dev task to iterate over - # the entire dataset. - p.eval.samples_per_summary = 0 - p.eval.decoder_samples_per_summary = 0 - - return p - - # From what I can tell, this is used only by ExecutorTpu. So perhaps - # I could change it such that it runs only on the Train set... Right? - def ProgramSchedule(self): - return program.SimpleProgramScheduleForTask( - train_dataset_name='Train', - train_steps_per_loop=100, - # I want to compute WER... - eval_dataset_names=['Dev'], - eval_steps_per_loop=1, - decode_steps_per_loop=1,) - - -# @model_registry.RegisterSingleTaskModel -# class TpuDecoderLibrispeech960Base(Librispeech960Base): - -# SAMPLES_PER_SECOND = 16_000 - -# def _InferenceInputParams(self): -# """ -# Reads in a raw waveform, where samples are float32 and in the range[-1,1] -# """ -# p = input_generator.RawAsrInputIntegerUttIds.Params() - -# p.file_datasource = datasource.PrefixedDataSource.Params() -# p.file_datasource.file_type = 'tfrecord' -# p.file_datasource.file_pattern_prefix = 'REPLACE_ME' -# p.file_datasource.file_pattern = '*.tfrecord' - -# p.frame_size = 1 -# p.append_eos_frame = False - -# p.pad_to_max_seq_length = True -# p.file_random_seed = 0 -# p.file_buffer_size = 1 -# # Set this to whatever -# p.file_parallelism = 1 - -# # 15 seconds is our maximum utterance size -# max_sample_length = 15 * type(self).SAMPLES_PER_SECOND -# p.source_max_length = max_sample_length -# p.bucket_upper_bound = [p.source_max_length] - -# p.bucket_batch_limit = [1] - -# return p - -# def Dev(self): -# return self._InferenceInputParams() - -# def Task(self): -# p = super().Task() -# # This is copied from audio_lib.py. Ideally these configurations -# # would be split off into a function, but I want to minimize merge -# # conflicts with lingvo for now. Of course, a merge conflict would -# # indicate that parameters had changed, so my choice is clearly -# # wrong, but oh well. -# p.frontend = asr_frontend.MelAsrFrontend.Params() -# pf = p.frontend -# pf.sample_rate = float(type(self).SAMPLES_PER_SECOND) -# pf.frame_size_ms = 25. -# pf.frame_step_ms = 10. -# pf.num_bins = 80 -# pf.lower_edge_hertz = 125. -# pf.upper_edge_hertz = 7600. -# pf.preemph = 0.97 -# pf.noise_scale = 0. -# pf.pad_end = False - -# p.inference_compute_only_log_softmax = True -# return p - -# def ProgramSchedule(self): -# # This value must be parameterized -# number_of_inputs = FLAGS.ctc_inference_model_num_samples -# batch_size = 8 -# import math -# decode_steps_per_loop = math.ceil(number_of_inputs / batch_size) -# return program.DecodeProgramSchedule( -# eval_dataset_names=['Dev'], -# decode_steps_per_loop=decode_steps_per_loop, # 1, -# # I may want to reevaluate this -# experimental_decoder=True) - - -@model_registry.RegisterSingleTaskModel -class Librispeech960Grapheme(Librispeech960Base): - - GRAPHEME_TARGET_SEQUENCE_LENGTH = 620 - GRAPHEME_VOCAB_SIZE = 76 - BLANK_IDX = 73 - - # This seems bad. Does this undo my VocabFileTokenizer??? - def InitializeTokenizer(self, params): - """Initializes a grapheme tokenizer.""" - params.tokenizer = tokenizers.AsciiTokenizer.Params() - tokp = params.tokenizer - tokp.vocab_size = self.GRAPHEME_VOCAB_SIZE - tokp.append_eos = False - tokp.target_unk_id = 0 - tokp.target_sos_id = 1 - tokp.target_eos_id = 2 - - params.target_max_length = self.GRAPHEME_TARGET_SEQUENCE_LENGTH - return params - - def Train(self): - p = super().Train() - return self.InitializeTokenizer(params=p) - - def Dev(self): - p = super().Dev() - return self.InitializeTokenizer(params=p) - - def Devother(self): - p = super().Devother() - return self.InitializeTokenizer(params=p) - - def Test(self): - p = super().Test() - return self.InitializeTokenizer(params=p) - - def Testother(self): - p = super().Testother() - return self.InitializeTokenizer(params=p) - - def Task(self): - p = super().Task() - p.vocab_size = self.GRAPHEME_VOCAB_SIZE - p.blank_index = self.BLANK_IDX - - return p - -@model_registry.RegisterSingleTaskModel -class Grphm_DO_SpecAug_StackingSubSampler(Librispeech960Base): - - def Task(self): - p = super().Task() - - ep = p.encoder_v2 - ep.use_specaugment = True - ep.use_conv_subsampler = False - ep.use_stacking_subsampler = True - - ep.stacking_subsampler.stacking.left_context = 1 - ep.stacking_subsampler.stacking.right_context = 1 - ep.stacking_subsampler.stacking.stride = ( - ep.stacking_subsampler.stacking.left_context + - 1 + - ep.stacking_subsampler.stacking.right_context - ) - - elp = p.encoder_v2.lstm_block - elp.dropout.keep_prob = 0.8 - elp.lstm_cell_size = 1024 - elp.num_lstm_layers = 6 - elp.lstm_type = 'fwd' - - return p - - -@model_registry.RegisterSingleTaskModel -class Grphm_DO_SpecAug_ConvStk_6x512Bidi(Librispeech960Grapheme): - - def Task(self): - p = super().Task() - - ep = p.encoder_v2 - ep.use_specaugment = True - ep.use_conv_subsampler = True - ep.use_stacking_subsampler = False - - elp = p.encoder_v2.lstm_block - elp.dropout.keep_prob = 0.8 - elp.lstm_cell_size = 512 - elp.num_lstm_layers = 6 - elp.lstm_type = 'bidi' - - ep.stacking_subsampler = None - ecp = ep.conv_subsampler - ecp.input_shape = [None, None, 80, 1] - return p - - -@model_registry.RegisterSingleTaskModel -class Grphm_DO_SpecAug_ConvStk_6x512Bidi_40batchsize(Librispeech960Grapheme): - - def Train(self): - p = super().Train() - # OOM with 48 - p.bucket_batch_limit = [40] * 8 - return p - - def Task(self): - p = super().Task() - - ep = p.encoder_v2 - ep.use_specaugment = True - ep.use_conv_subsampler = True - ep.use_stacking_subsampler = False - - elp = p.encoder_v2.lstm_block - elp.dropout.keep_prob = 0.8 - elp.lstm_cell_size = 512 - elp.num_lstm_layers = 6 - elp.lstm_type = 'bidi' - - ep.stacking_subsampler = None - ecp = ep.conv_subsampler - ecp.input_shape = [None, None, 80, 1] - return p - -@model_registry.RegisterSingleTaskModel -class TpuDecoderGrphm_DO_SpecAug_StackingSubSampler(Grphm_DO_SpecAug_StackingSubSampler): - - SAMPLES_PER_SECOND = 16_000 - - def _InferenceInputParams(self): - """ - Reads in a raw waveform, where samples are float32 and in the range[-1,1] - """ - p = input_generator.RawAsrInputIntegerUttIds.Params() - - p.file_datasource = datasource.PrefixedDataSource.Params() - p.file_datasource.file_type = 'tfrecord' - p.file_datasource.file_pattern_prefix = 'REPLACE_ME' - p.file_datasource.file_pattern = '*.tfrecord' - - p.frame_size = 1 - p.append_eos_frame = False - - p.pad_to_max_seq_length = True - p.file_random_seed = 0 - p.file_buffer_size = 1 - # Set this to whatever - p.file_parallelism = 1 - - # 15 seconds is our maximum utterance size - max_sample_length = 15 * type(self).SAMPLES_PER_SECOND - # TODOL: change to max_sample_length - p.source_max_length = max_sample_length - p.bucket_upper_bound = [p.source_max_length] - - p.bucket_batch_limit = [FLAGS.ctc_inference_model_batch_size] - - # assert p.num_samples == FLAGS.ctc_inference_model_num_samples - - p.tokenizer = intialize_vocab_file_tokenizer_params() - - return p - - def Dev(self): - return self._InferenceInputParams() - - def Task(self): - p = super().Task() - # This is copied from audio_lib.py. Ideally these configurations - # would be split off into a function, but I want to minimize merge - # conflicts with lingvo for now. Of course, a merge conflict would - # indicate that parameters had changed, so my choice is clearly - # wrong, but oh well. - p.frontend = asr_frontend.MelAsrFrontend.Params() - pf = p.frontend - pf.sample_rate = float(type(self).SAMPLES_PER_SECOND) - pf.frame_size_ms = 25. - pf.frame_step_ms = 10. - pf.num_bins = 80 - pf.lower_edge_hertz = 125. - pf.upper_edge_hertz = 7600. - pf.preemph = 0.97 - pf.noise_scale = 0. - pf.pad_end = False - - p.inference_compute_only_log_softmax = True - - # TODO: Consider doing this - # ep = p.encoder_v2 - # ep.use_specaugment = False - return p - - def ProgramSchedule(self): - # TODO: Generalize this - number_of_inputs = FLAGS.ctc_inference_model_num_samples - assert number_of_inputs != 0 - assert FLAGS.ctc_inference_model_batch_size != 0 - batch_size = FLAGS.ctc_inference_model_batch_size * FLAGS.ctc_inference_model_number_of_tpus - import math - decode_steps_per_loop = math.ceil(number_of_inputs / batch_size) - return program.DecodeProgramSchedule( - eval_dataset_names=['Dev'], - decode_steps_per_loop=decode_steps_per_loop, # 1, - # I may want to reevaluate this - experimental_decoder=False) diff --git a/lingvo/tasks/asr/params/params.py b/lingvo/tasks/asr/params/params.py deleted file mode 100644 index 3cad45e6..00000000 --- a/lingvo/tasks/asr/params/params.py +++ /dev/null @@ -1,22 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""ASR model parameters.""" - -# Import ModelParams to ensure that they are added to the global registry. -# LAS model -import lingvo.tasks.asr.params.librispeech # pylint: disable=unused-import -import lingvo.tasks.asr.params.librispeech_ctc # pylint: disable=unused-import -import lingvo.tasks.asr.params.peoplesspeech_ctc # pylint: disable=unused-import \ No newline at end of file diff --git a/lingvo/tasks/asr/params/peoplesspeech_ctc.py b/lingvo/tasks/asr/params/peoplesspeech_ctc.py deleted file mode 100644 index 5c301ce7..00000000 --- a/lingvo/tasks/asr/params/peoplesspeech_ctc.py +++ /dev/null @@ -1,192 +0,0 @@ -import os - -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.core import datasource -from lingvo.core import program -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import tokenizers -from lingvo.tasks.asr import input_generator -from lingvo.tasks.asr import ctc_model - -@model_registry.RegisterSingleTaskModel -class Peoplesspeech100Base(base_model_params.SingleTaskModelParams): - """Base parameters for Peoplesspeech 100k hour task.""" - - def _CommonInputParams(self, is_eval): - """Input generator params for Peoplesspeech.""" - p = input_generator.AsrInput.Params() - - # Insert path to the base directory where the data are stored here. - # Generated using scripts in lingvo/tasks/asr/tools. - p.file_datasource = datasource.PrefixedDataSource.Params() - p.file_datasource.file_type = 'tfrecord' - p.file_datasource.file_pattern_prefix = 'gs://the-peoples-speech-west-europe/PeoplesSpeech/v0.7.1/' - - p.frame_size = 80 - # Interesting. First I've heard of this. - p.append_eos_frame = False - - p.pad_to_max_seq_length = True - p.file_random_seed = 0 - p.file_buffer_size = 10000 - # N1 standard 2 has only 2 vCPUs, so we may want a larger machine. - # https://cloud.google.com/compute/docs/machine-types#n1_standard_machine_types - p.file_parallelism = 16 - - if is_eval: - p.source_max_length = 3600 - p.bucket_upper_bound = [639, 1062, 1275, 1377, 1449, 1506, 1563, 3600] - else: - # So it looks like - p.source_max_length = 1710 - p.bucket_upper_bound = [639, 1062, 1275, 1377, 1449, 1506, 1563, 1710] - - p.bucket_batch_limit = [48] * 8 - - return p - - def SetBucketSizes(self, params, bucket_upper_bound, bucket_batch_limit): - """Sets bucket sizes for batches in params.""" - params.bucket_upper_bound = bucket_upper_bound - params.bucket_batch_limit = bucket_batch_limit - return params - - def Train(self): - p = self._CommonInputParams(is_eval=False) - p.file_datasource.file_pattern = 'train/train.tfrecords-*' - p.num_samples = 2292260 - return p - - def Dev(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/dev.tfrecords-00000-of-00001') - p.num_samples = 3000 - return p - - def Test(self): - p = self._CommonInputParams(is_eval=True) - p.file_datasource.file_pattern = ( - 'devtest/test.tfrecords-00000-of-00001') - p.num_samples = 3000 - return p - - def Task(self): - p = ctc_model.CTCModel.Params() - p.name = 'peoplesspeech' - - # No default encoder params in this class. - - tp = p.train - tp.learning_rate = 1e-4 - tp.lr_schedule = schedule.ContinuousSchedule.Params().Set( - start_step=25_000, half_life_steps=5_000, min=1e-6) - tp.scale_gradients = False - tp.l2_regularizer_weight = None - - # Setting p.eval.samples_per_summary to a large value ensures that dev, - # devother, test, testother are evaluated completely (since num_samples for - # each of these sets is less than 5000), while train summaries will be - # computed on 5000 examples. - p.eval.samples_per_summary = 2700 - p.eval.decoder_samples_per_summary = 2700 - - return p - - def ProgramSchedule(self): - return program.SimpleProgramScheduleForTask( - train_dataset_name='Train', - train_steps_per_loop=500, - eval_dataset_names=['Dev', 'Train'], - eval_steps_per_loop=50, - decode_steps_per_loop=0) - - -@model_registry.RegisterSingleTaskModel -class Peoplesspeech100Grapheme(Peoplesspeech100Base): - - GRAPHEME_TARGET_SEQUENCE_LENGTH = 620 - GRAPHEME_VOCAB_SIZE = 76 - BLANK_IDX = 73 - - def InitializeTokenizer(self, params): - """Initializes a grapheme tokenizer.""" - params.tokenizer = tokenizers.AsciiTokenizer.Params() - tokp = params.tokenizer - tokp.vocab_size = self.GRAPHEME_VOCAB_SIZE - tokp.append_eos = False - tokp.target_unk_id = 0 - tokp.target_sos_id = 1 - tokp.target_eos_id = 2 - - params.target_max_length = self.GRAPHEME_TARGET_SEQUENCE_LENGTH - return params - - def Train(self): - p = super().Train() - return self.InitializeTokenizer(params=p) - - def Dev(self): - p = super().Dev() - return self.InitializeTokenizer(params=p) - - def Test(self): - p = super().Test() - return self.InitializeTokenizer(params=p) - - def Task(self): - p = super().Task() - p.vocab_size = self.GRAPHEME_VOCAB_SIZE - p.blank_index = self.BLANK_IDX - - return p - - -@model_registry.RegisterSingleTaskModel -class Grphm_DO_SpecAug_ConvStk_6x512Bidi(Peoplesspeech100Grapheme): - - def Task(self): - p = super().Task() - - ep = p.encoder_v2 - ep.use_specaugment = True - - elp = p.encoder_v2.lstm_block - elp.dropout.keep_prob = 0.8 - elp.lstm_cell_size = 512 - elp.num_lstm_layers = 6 - elp.lstm_type = 'bidi' - - ep.stacking_subsampler = None - ecp = ep.conv_subsampler - ecp.input_shape = [None, None, 80, 1] - return p - - -@model_registry.RegisterSingleTaskModel -class Grphm_DO_SpecAug_ConvStk_6x512Bidi_40batchsize(Peoplesspeech100Grapheme): - - def Train(self): - p = super().Train() - # OOM with 48 - p.bucket_batch_limit = [40] * 8 - return p - - def Task(self): - p = super().Task() - - ep = p.encoder_v2 - ep.use_specaugment = True - - elp = p.encoder_v2.lstm_block - elp.dropout.keep_prob = 0.8 - elp.lstm_cell_size = 512 - elp.num_lstm_layers = 6 - elp.lstm_type = 'bidi' - - ep.stacking_subsampler = None - ecp = ep.conv_subsampler - ecp.input_shape = [None, None, 80, 1] - return p diff --git a/lingvo/tasks/asr/rnnt_model.py b/lingvo/tasks/asr/rnnt_model.py deleted file mode 100644 index 0cbf96ca..00000000 --- a/lingvo/tasks/asr/rnnt_model.py +++ /dev/null @@ -1,63 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""RNN-T model.""" - -import collections -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import base_model -from lingvo.core import metrics -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.tasks.asr import decoder -from lingvo.tasks.asr import decoder_utils -from lingvo.tasks.asr import encoder -from lingvo.tasks.asr import frontend as asr_frontend -from lingvo.tools import audio_lib - -class RNNTModel(base_model.BaseTask): - - @classmethod - def Params(cls): - p = super().Params() - p.encoder = encoder.AsrEncoder.Params() - p.prediction = rnnt_prediction.Prediction.Params() - p.joint = rnnt_joint.RNNTJoint.Params() - p.Define( - 'frontend', None, - 'ASR frontend to extract features from input. Defaults to no frontend ' - 'which means that features are taken directly from the input.') - - p.Define('include_auxiliary_metrics', True, - 'In addition to simple WER, also computes oracle WER, SACC, TER, etc. ' - 'Turning off this option will speed up the decoder job.') - - tp = p.train - tp.lr_schedule = ( - schedule.PiecewiseConstantSchedule.Params().Set( - boundaries=[350_000, 500_000, 600_000], - values=[1.0, 0.1, 0.01, 0.001])) - tp.vn_start_step = 20_000 - tp.vn_std = 0.075 - tp.l2_regularizer_weight = 1e-6 - # Completely redundant with previous initial 1.0 value... which - # has higher precendence? - tp.learning_rate = 0.001 - tp.clip_gradient_norm_to_value = 1.0 - tp.grad_norm_to_clip_to_zero = 100.0 - # What does this mean? - tp.tpu_steps_per_loop = 20 - - diff --git a/lingvo/tasks/asr/rnnt_prediction.py b/lingvo/tasks/asr/rnnt_prediction.py deleted file mode 100644 index b7590ee7..00000000 --- a/lingvo/tasks/asr/rnnt_prediction.py +++ /dev/null @@ -1,155 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Prediction network for the RNNT model.""" -from lingvo.core import base_layer -from lingvo.core import layers - - -class Prediction(base_layer.BaseLayer): - - @classmethod - def Params(cls): - p = super().Params() - # This is input to an embedding layer - p.Define('input_shape', [None, None], 'Shape of the input. This should be a TensorShape with rank 2.') - p.Define('lstm_cell_size', 2048, 'LSTM cell size for the RNN layer.') - p.Define('num_lstm_layers', 8, 'Number of rnn layers to create.') - p.Define('project_lstm_output', True, - 'Include projection layer after each encoder LSTM layer.') - # How to use? - StackedFRNNLayerByLayer.Params() - p.Define('lstm_tpl', rnn_cell.LSTMCellSimple.Params(), - 'Configs template for the RNN layer.') - p.Define('proj_tpl', layers.ProjectionLayer.Params(), - 'Configs template for the projection layer.') - p.Define('stacking_layer_tpl', layers.StackingOverTime.Params(), - 'Configs template for the stacking layer over time.') - p.Define('embedding_tpl', layers.EmbeddingLayer.Params(), - 'Configs template for the embedding layer.') - p.Define( - 'layer_index_before_stacking', -1, - 'The (0-based) index of the lstm layer after which the stacking layer ' - 'will be inserted. Negative value means no stacking layer will be ' - 'used.') - - return p - - @base_layer.initializer - def __init__(self, params): - super().__init__(params) - p = self.params - name = p.name - - - (self._first_lstm_input_dim, - self._first_lstm_input_dim_pad) = self.FirstLstmLayerInputDimAndPadding( - p.embedding_tpl.embedding_dim, pad_to_multiple=16) - - - with tf.variable_scope(name): - params_rnn_layers = [] - params_proj_layers = [] - - embed_p = p.embedding_tpl.Copy() - embed_p.name = 'embed' - self.CreateChild('embed', embed_p) - - output_dim = self._first_lstm_input_dim - for i in range(p.num_lstm_layers): - input_dim = output_dim - lstm_p = p.lstm_tpl.Copy() - lstm_p.name = f"lstm_L{i}" - lstm_p.num_input_nodes = input_dim - lstm_p.num_output_nodes = p.lstm_cell_size - params_rnn_layers.append(lstm_p) - - if p.project_lstm_output and (i < p.num_lstm_layers - 1): - proj_p = p.proj_tpl.Copy() - proj_p.input_dim = p.lstm_cell_size - proj_p.output_dim = p.lstm_cell_size - proj_p.name = f'proj_L{i}' - params_proj_layers.append(proj_p) - - if p.layer_index_before_stacking == i: - stacking_layer = p.stacking_layer_tpl.Copy() - stacking_layer.name = f'stacking_L{i}' - self.CreateChild('stacking', stacking_layer) - stacking_window_len = ( - p.stacking_layer_tpl.left_context + 1 + - p.stacking_layer_tpl.right_context) - output_dim *= stacking_window_len - - self.CreateChildren('rnn', params_rnn_layers) - self.CreateChildren('proj', params_proj_layers) - - @property - def _use_functional(self): - return True - - @property - def input_shape(self): - return self.params.input_shape - - @property - def supports_streaming(self): - return True - - def FirstLstmLayerInputDimAndPadding(self, - lstm_input_shape, - pad_to_multiple=16): - # Makes sure the lstm input dims is multiple of 16 (alignment - # requirement from FRNN). - first_lstm_input_dim_unpadded = lstm_input_shape[2] - - if self._use_functional and (first_lstm_input_dim_unpadded % pad_to_multiple - != 0): - first_lstm_input_dim = int( - (first_lstm_input_dim_unpadded + pad_to_multiple - 1) / - pad_to_multiple) * pad_to_multiple - else: - first_lstm_input_dim = first_lstm_input_dim_unpadded - - first_lstm_input_dim_padding = ( - first_lstm_input_dim - first_lstm_input_dim_unpadded) - return first_lstm_input_dim, first_lstm_input_dim_padding - - def FProp(self, theta, batch, state0=None): - """Encodes source as represented by 'inputs' and 'paddings'. - - Args: - theta: A NestedMap object containing weights' values of this - layer and its children layers. - batch: A NestedMap with fields: - - - tokens - The inputs tensor. It is expected to be of shape [time, batch]. - - paddings - The paddings tensor. It is expected to be of shape [time, batch]. - state0: Recurrent input state. - - Returns: - A NestedMap containing - - - 'encoded': a feature tensor of shape [batch, time, depth] - - 'padding': a 0/1 tensor of shape [batch, time] - - 'state': the updated recurrent state - - '${layer_type}_${layer_index}': The per-layer encoder output. Each one - is a NestedMap containing 'encoded' and 'padding' similar to regular - final outputs, except that 'encoded' from conv or conv_lstm layers are - of shape [time, batch, depth, channels]. - """ - p = self.params - if len(batch) == 0: - - if state0 is None: - diff --git a/lingvo/tasks/asr/tools/BUILD b/lingvo/tasks/asr/tools/BUILD deleted file mode 100644 index 3abb1023..00000000 --- a/lingvo/tasks/asr/tools/BUILD +++ /dev/null @@ -1,43 +0,0 @@ -# tools for ASR task, including WER computation and breakdown analytics - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "simple_wer", - srcs = ["simple_wer.py"], - srcs_version = "PY3", -) - -py_test( - name = "simple_wer_test", - srcs = ["simple_wer_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":simple_wer", - "//lingvo:compat", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "simple_wer_v2", - srcs = ["simple_wer_v2.py"], - srcs_version = "PY3", -) - -py_test( - name = "simple_wer_v2_test", - srcs = ["simple_wer_v2_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":simple_wer_v2", - "//lingvo:compat", - "//lingvo/core:test_utils", - ], -) diff --git a/lingvo/tasks/asr/tools/__init__.py b/lingvo/tasks/asr/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/asr/tools/librispeech.01.download_train.sh b/lingvo/tasks/asr/tools/librispeech.01.download_train.sh deleted file mode 100755 index f1ff4805..00000000 --- a/lingvo/tasks/asr/tools/librispeech.01.download_train.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. librispeech_lib.sh - -mkdir -p "${ROOT}/raw" - -# This will take about half an hour on a good connection. -echo " -${SOURCE}/train-clean-100.tar.gz -${SOURCE}/train-clean-360.tar.gz -${SOURCE}/train-other-500.tar.gz" \ - | aria2c -x16 -s16 --dir="${ROOT}/raw" -i - diff --git a/lingvo/tasks/asr/tools/librispeech.02.download_devtest.sh b/lingvo/tasks/asr/tools/librispeech.02.download_devtest.sh deleted file mode 100755 index 83df5103..00000000 --- a/lingvo/tasks/asr/tools/librispeech.02.download_devtest.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. librispeech_lib.sh - -mkdir -p "${ROOT}/raw" - -echo " -${SOURCE}/dev-clean.tar.gz -${SOURCE}/dev-other.tar.gz -${SOURCE}/test-clean.tar.gz -${SOURCE}/test-other.tar.gz" \ - | aria2c -x16 -s16 --dir="${ROOT}/raw" -i - diff --git a/lingvo/tasks/asr/tools/librispeech.03.parameterize_train.sh b/lingvo/tasks/asr/tools/librispeech.03.parameterize_train.sh deleted file mode 100755 index 0fe7a1a9..00000000 --- a/lingvo/tasks/asr/tools/librispeech.03.parameterize_train.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. librispeech_lib.sh - -# To save space, we don't unpack to intermediate files. The first pass collects -# all transcription files from the tarball. The second pass unpacks the audio, -# decompresses it, and encodes MFCC frames in memory, then writes a tf.Example -# with the accompanying transcription from the first pass. - -# This takes about 10 minutes per set. -for subset in train-clean-100 train-clean-360 train-other-500; do - echo "=== First pass, collecting transcripts: ${subset}" - qsub_run \ - $(git rev-parse --show-toplevel)/bazel-bin/lingvo/tools/create_asr_features \ - --logtostderr \ - --input_tarball="${ROOT}/raw/${subset}.tar.gz" --dump_transcripts \ - --transcripts_filepath="${ROOT}/train/${subset}.txt" -done - -# We are allocating as follows: -# num utts num_shards subset -# 28539 10 train-clean-100 -# 104014 40 train-clean-360 -# 148688 50 train-other-500 -# 281241 100 Total -# -# We expect a total of, say, 100GB, so we want 100 shards to get into the 1GB -# range. We use 10 processors, each alloted a range of 10 output shards. - -# Second pass: Create tf.Examples. It takes about 90 minutes. - -rm -f FAILED -subset=train-clean-100 -echo "=== Second pass, parameterization: ${subset}" -for subshard in $(seq 0 9); do - set -x - # nice -n 20 python3 -m lingvo.tools.create_asr_features \ - qsub_run \ - $(git rev-parse --show-toplevel)/bazel-bin/lingvo/tools/create_asr_features \ - --logtostderr \ - --input_tarball="${ROOT}/raw/${subset}.tar.gz" --generate_tfrecords \ - --shard_id="${subshard}" --num_shards=10 --num_output_shards=100 \ - --output_range_begin="${subshard}" --output_range_end="$((subshard + 1))" \ - --output_template="${ROOT}/train/train.tfrecords-%5.5d-of-%5.5d" || touch FAILED & - set +x -done -wait -! [ -f FAILED ] - -subset=train-clean-360 -echo "=== Second pass, parameterization: ${subset}" -for subshard in $(seq 0 9); do - set -x - qsub_run \ - $(git rev-parse --show-toplevel)/bazel-bin/lingvo/tools/create_asr_features \ - --logtostderr \ - --input_tarball="${ROOT}/raw/${subset}.tar.gz" --generate_tfrecords \ - --shard_id="${subshard}" --num_shards=10 --num_output_shards=100 \ - --output_range_begin="$((10 + 4 * subshard))" \ - --output_range_end="$((10 + 4 * subshard + 4))" \ - --output_template="${ROOT}/train/train.tfrecords-%5.5d-of-%5.5d" || touch FAILED & - set +x -done -wait -! [ -f FAILED ] - -subset=train-other-500 -echo "=== Second pass, parameterization: ${subset}" -for subshard in $(seq 0 9); do - set -x - qsub_run \ - $(git rev-parse --show-toplevel)/bazel-bin/lingvo/tools/create_asr_features \ - --logtostderr \ - --input_tarball="${ROOT}/raw/${subset}.tar.gz" --generate_tfrecords \ - --shard_id="${subshard}" --num_shards=10 --num_output_shards=100 \ - --output_range_begin="$((50 + 5 * subshard))" \ - --output_range_end="$((50 + 5 * subshard + 5))" \ - --output_template="${ROOT}/train/train.tfrecords-%5.5d-of-%5.5d" || touch FAILED & - set +x -done -wait -! [ -f FAILED ] diff --git a/lingvo/tasks/asr/tools/librispeech.04.parameterize_devtest.sh b/lingvo/tasks/asr/tools/librispeech.04.parameterize_devtest.sh deleted file mode 100755 index 0a90ed61..00000000 --- a/lingvo/tasks/asr/tools/librispeech.04.parameterize_devtest.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. librispeech_lib.sh - -for subset in {dev,test}-{clean,other}; do - set -x - # python3 -m lingvo.tools.create_asr_features \ - # --run_under=$(which ipdb3) - bazel_ run -c opt //lingvo/tools:create_asr_features -- \ - --generate_tfrecords \ - --input_tarball="${ROOT}/raw/${subset}.tar.gz" \ - --shard_id=0 --num_shards=1 --num_output_shards=1 \ - --output_range_begin=0 --output_range_end=1 \ - --output_template="${ROOT}/devtest/${subset}.tfrecords-%5.5d-of-%5.5d" - set +x -done diff --git a/lingvo/tasks/asr/tools/librispeech.05.train_ctc.sh b/lingvo/tasks/asr/tools/librispeech.05.train_ctc.sh deleted file mode 100644 index 1d833f50..00000000 --- a/lingvo/tasks/asr/tools/librispeech.05.train_ctc.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -. ./librispeech_lib.sh - -set -euo pipefail - -cat < model_params.txt - -EOF - -bazel_ run //lingvo:trainer \ - --interactive \ - --mode=shell \ - --run_locally=cpu \ - --model= - --logdir=logs/ctc/ - --model_params_file_override=model_params.txt diff --git a/lingvo/tasks/asr/tools/librispeech_get_data.sh b/lingvo/tasks/asr/tools/librispeech_get_data.sh deleted file mode 100755 index 08bc6acd..00000000 --- a/lingvo/tasks/asr/tools/librispeech_get_data.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -./librispeech.01.download_train.sh -./librispeech.02.download_devtest.sh -./librispeech.03.parameterize_train.sh -./librispeech.04.parameterize_devtest.sh diff --git a/lingvo/tasks/asr/tools/librispeech_lib.sh b/lingvo/tasks/asr/tools/librispeech_lib.sh deleted file mode 100755 index 9c4ea92a..00000000 --- a/lingvo/tasks/asr/tools/librispeech_lib.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -# ROOT=/tmp/librispeech -ROOT=gs://the-peoples-speech-west-europe/Librispeech - -# From: -# http://www.openslr.org/12/ -SOURCE=http://www.openslr.org/resources/12 - -# If in China, use this mirror: -# http://cn-mirror.openslr.org/resources/12 - -# conda activate galvasr - -# My home directory in on an NFS mount, which bazel dislikes, so I need to do this. -bazel_() { - bazel --output_user_root=/home/ws15dgalvez/dgalvez-b02/.cache/bazel "$@" -} - -qsub_run() { - cat < qsub_script.sh -#!/bin/bash -#$ -cwd -#$ -j yes -#$ -l mem_free=5G,ram_free=5G,arch=*64* -#$ -v PATH - -. ./librispeech_lib.sh - -export CUDA_VISIBLE_DEVICES="" -source "$($CONDA_EXE info --base)/etc/profile.d/conda.sh" -conda activate galvasr - -$@ -EOF - -qsub qsub_script.sh -} - -# Never instantiate a cuda context. -export CUDA_VISIBLE_DEVICES="" diff --git a/lingvo/tasks/asr/tools/qsub_script.sh b/lingvo/tasks/asr/tools/qsub_script.sh deleted file mode 100644 index cf85f064..00000000 --- a/lingvo/tasks/asr/tools/qsub_script.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -#$ -cwd -#$ -j yes -#$ -l mem_free=5G,ram_free=5G,arch=*64* -#$ -v PATH - -. ./librispeech_lib.sh - -export CUDA_VISIBLE_DEVICES="" -source "/export/bdc01/home2/dgalvez/miniconda3/etc/profile.d/conda.sh" -conda activate galvasr - -/export/b02/ws15dgalvez/galvASR2/third_party/lingvo/bazel-bin/lingvo/tools/create_asr_features --logtostderr --input_tarball=gs://the-peoples-speech-west-europe/Librispeech/raw/train-other-500.tar.gz --generate_tfrecords --shard_id=3 --num_shards=10 --num_output_shards=100 --output_range_begin=65 --output_range_end=70 --output_template=gs://the-peoples-speech-west-europe/Librispeech/train/train.tfrecords-%5.5d-of-%5.5d diff --git a/lingvo/tasks/asr/tools/run_model.sh b/lingvo/tasks/asr/tools/run_model.sh deleted file mode 100755 index 3dacec4a..00000000 --- a/lingvo/tasks/asr/tools/run_model.sh +++ /dev/null @@ -1,14 +0,0 @@ -set -eu - -. librispeech_lib.sh - -bazel_ run -c opt //lingvo:trainer -- \ - --model=asr.librispeech_ctc.Librispeech960Base \ - --run_locally=cpu \ - --logtostderr \ - --logdir=my_logs/ - - - # --gcp_project=the-peoples-speech \ - # --tpu_zone= \ - # --tpu= diff --git a/lingvo/tasks/asr/tools/simple_wer.py b/lingvo/tasks/asr/tools/simple_wer.py deleted file mode 100644 index d6835c04..00000000 --- a/lingvo/tasks/asr/tools/simple_wer.py +++ /dev/null @@ -1,340 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Stand-alone script to evalute the word error rate (WER) for ASR tasks. - -THIS SCRIPT IS NO LONGER SUPPORTED. PLEASE USE simple_wer_v2.py INSTEAD. - -Tensorflow and Lingvo are not required to run this script. - -Example of Usage:: - - python simple_wer.py file_hypothesis file_reference - python simple_wer.py file_hypothesis file_reference diagnosis_html - -where `file_hypothesis` is the file name for hypothesis text and -`file_reference` is the file name for reference text. -`diagnosis_html` (optional) is the html filename to diagnose the errors. - -Or you can use this file as a library, and call either of the following: - - - ``ComputeWER(hyp, ref)`` compute WER for one pair of hypothesis/reference - - ``AverageWERs(hyps, refs)`` average WER for a list of hypotheses/references - -Note to evaluate the ASR, we consider the following pre-processing: - - - change transcripts to lower-case - - remove punctuation: ``" , . ! ? ( ) [ ]`` - - remove extra empty spaces -""" - -import re -import sys - - -def ComputeEditDistanceMatrix(hs, rs): - """Compute edit distance between two list of strings. - - Args: - hs: the list of words in the hypothesis sentence - rs: the list of words in the reference sentence - - Returns: - Edit distance matrix (in the format of list of lists), where the first - index is the reference and the second index is the hypothesis. - """ - dr, dh = len(rs) + 1, len(hs) + 1 - dists = [[]] * dr - - # Initialization. - for i in range(dr): - dists[i] = [0] * dh - for j in range(dh): - if i == 0: - dists[0][j] = j - elif j == 0: - dists[i][0] = i - - # Do dynamic programming. - for i in range(1, dr): - for j in range(1, dh): - if rs[i - 1] == hs[j - 1]: - dists[i][j] = dists[i - 1][j - 1] - else: - tmp0 = dists[i - 1][j - 1] + 1 - tmp1 = dists[i][j - 1] + 1 - tmp2 = dists[i - 1][j] + 1 - dists[i][j] = min(tmp0, tmp1, tmp2) - - return dists - - -def PreprocessTxtBeforeWER(txt): - """Preprocess text before WER caculation.""" - - # Lowercase, remove \t and new line. - txt = re.sub(r'[\t\n]', ' ', txt.lower()) - - # Remove punctuation before space. - txt = re.sub(r'[,.\?!]+ ', ' ', txt) - - # Remove punctuation before end. - txt = re.sub(r'[,.\?!]+$', ' ', txt) - - # Remove punctuation after space. - txt = re.sub(r' [,.\?!]+', ' ', txt) - - # Remove quotes, [, ], ( and ). - txt = re.sub(r'["\(\)\[\]]', '', txt) - - # Remove extra space. - txt = re.sub(' +', ' ', txt.strip()) - - return txt - - -def _GenerateAlignedHtml(hyp, ref, err_type): - """Generate a html element to highlight the difference between hyp and ref. - - Args: - hyp: Hypothesis string. - ref: Reference string. - err_type: one of 'none', 'sub', 'del', 'ins'. - - Returns: - a html string where disagreements are highlighted. - - hyp highlighted in green, and marked with - - ref highlighted in yellow - - """ - - highlighted_html = '' - if err_type == 'none': - highlighted_html += '%s ' % hyp - - elif err_type == 'sub': - highlighted_html += """ - %s - %s """ % (hyp, ref) - - elif err_type == 'del': - highlighted_html += """ - %s """ % ( - ref) - - elif err_type == 'ins': - highlighted_html += """ - %s """ % ( - hyp) - - else: - raise ValueError('unknown err_type ' + err_type) - - return highlighted_html - - -def GenerateSummaryFromErrs(nref, errs): - """Generate strings to summarize word errors. - - Args: - nref: integer of total words in references - errs: dict of three types of errors. e.g. {'sub':10, 'ins': 15, 'del': 3} - - Returns: - Two strings: - - string summarizing total error, total word, WER, - - string breaking down three errors: deleting, insertion, substitute - """ - - total_error = sum(errs.values()) - str_sum = 'total error = %d, total word = %d, wer = %.2f%%' % ( - total_error, nref, total_error * 100.0 / nref) - - str_details = 'Error breakdown: del = %.2f%%, ins=%.2f%%, sub=%.2f%%' % ( - errs['del'] * 100.0 / nref, errs['ins'] * 100.0 / nref, - errs['sub'] * 100.0 / nref) - - return str_sum, str_details - - -def ComputeWER(hyp, ref, diagnosis=False): - """Computes WER for ASR by ignoring diff of punctuation, space, captions. - - Args: - hyp: Hypothesis string. - ref: Reference string. - diagnosis (optional): whether to generate diagnosis str (in html format) - - Returns: - A tuple of 3 elements: - - - dict of three types of errors. e.g. ``{'sub':0, 'ins': 0, 'del': 0}`` - - num of reference words, integer - - aligned html string for diagnois (empty if diagnosis = False) - """ - - hyp = PreprocessTxtBeforeWER(hyp) - ref = PreprocessTxtBeforeWER(ref) - - # Compute edit distance. - hs = hyp.split() - rs = ref.split() - distmat = ComputeEditDistanceMatrix(hs, rs) - - # Back trace, to distinguish different errors: insert, deletion, substitution. - ih, ir = len(hs), len(rs) - errs = {'sub': 0, 'ins': 0, 'del': 0} - aligned_html = '' - while ih > 0 or ir > 0: - err_type = '' - - # Distinguish error type by back tracking - if ir == 0: - err_type = 'ins' - elif ih == 0: - err_type = 'del' - else: - if hs[ih - 1] == rs[ir - 1]: # correct - err_type = 'none' - elif distmat[ir][ih] == distmat[ir - 1][ih - 1] + 1: # substitute - err_type = 'sub' - elif distmat[ir][ih] == distmat[ir - 1][ih] + 1: # deletion - err_type = 'del' - elif distmat[ir][ih] == distmat[ir][ih - 1] + 1: # insert - err_type = 'ins' - else: - raise ValueError('fail to parse edit distance matrix') - - # Generate aligned_html - if diagnosis: - if ih == 0 or not hs: - tmph = ' ' - else: - tmph = hs[ih - 1] - if ir == 0 or not rs: - tmpr = ' ' - else: - tmpr = rs[ir - 1] - aligned_html = _GenerateAlignedHtml(tmph, tmpr, err_type) + aligned_html - - # If no error, go to previous ref and hyp. - if err_type == 'none': - ih, ir = ih - 1, ir - 1 - continue - - # Update error. - errs[err_type] += 1 - - # Adjust position of ref and hyp. - if err_type == 'del': - ir = ir - 1 - elif err_type == 'ins': - ih = ih - 1 - else: # err_type == 'sub' - ih, ir = ih - 1, ir - 1 - - assert distmat[-1][-1] == sum(errs.values()) - - # Num of words. For empty ref we set num = 1. - nref = max(len(rs), 1) - - return errs, nref, aligned_html - - -def AverageWERs(hyps, refs, verbose=True, diagnosis=False): - """Computes average WER from a list of references/hypotheses. - - Args: - hyps: list of hypothesis strings. - refs: list of reference strings. - verbose: optional (default True) - diagnosis (optional): whether to generate list of diagnosis html - - Returns: - A tuple of 3 elements: - - - dict of three types of errors. e.g. ``{'sub':0, 'ins': 0, 'del': 0}`` - - num of reference words, integer - - list of aligned html string for diagnosis (empty if diagnosis = False) - - """ - totalw = 0 - total_errs = {'sub': 0, 'ins': 0, 'del': 0} - aligned_html_list = [] - - for hyp, ref in zip(hyps, refs): - errs_i, nref_i, diag_str = ComputeWER(hyp, ref, diagnosis) - if diagnosis: - aligned_html_list += [diag_str] - - totalw += nref_i - total_errs['sub'] += errs_i['sub'] - total_errs['ins'] += errs_i['ins'] - total_errs['del'] += errs_i['del'] - - if verbose: - str_summary, str_details = GenerateSummaryFromErrs(totalw, total_errs) - print(str_summary) - print(str_details) - - return total_errs, totalw, aligned_html_list - - -def main(argv): - - hyp = open(argv[1], 'r').read() - ref = open(argv[2], 'r').read() - if len(argv) == 4: - diagnosis = True - fn_output = argv[3] - else: - diagnosis = False - fn_output = None - - errs, nref, aligned_html = ComputeWER(hyp, ref, diagnosis) - str_summary, str_details = GenerateSummaryFromErrs(nref, errs) - print(str_summary) - print(str_details) - - if fn_output: - with open(fn_output, 'wt') as fp: - fp.write('') - fp.write('

    ' % aligned_html) - fp.write('') - - -if __name__ == '__main__': - print('THIS SCRIPT IS NO LONGER SUPPORTED.' - 'PLEASE USE simple_wer_v2.py INSTEAD.') - - if len(sys.argv) < 3 or len(sys.argv) > 4: - print(""" -Example of Usage: - - python simple_wer.py file_hypothesis file_reference -or - python simple_wer.py file_hypothesis file_reference diagnosis_html - - where file_hypothesis is the file name for hypothesis text - file_reference is the file name for reference text. - diagnosis_html (optional) is the html filename to diagnose the errors. - -Or you can use this file as a library, and call either of the following - - ComputeWER(hyp, ref) to compute WER for one pair of hypothesis/reference - - AverageWERs(hyps, refs) to average WER for a list of hypotheses/references -""") - sys.exit(1) - - main(sys.argv) diff --git a/lingvo/tasks/asr/tools/simple_wer_test.py b/lingvo/tasks/asr/tools/simple_wer_test.py deleted file mode 100644 index 5edb605f..00000000 --- a/lingvo/tasks/asr/tools/simple_wer_test.py +++ /dev/null @@ -1,34 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for simple_wer.""" - -import lingvo.compat as tf -from lingvo.core import test_utils -from lingvo.tasks.asr.tools import simple_wer - - -class SimpleWerTest(test_utils.TestCase): - - def testWerIgnorePunctuation(self): - hyp = 'hello world. today is a good day, how are you' - ref = '(Hello world)! [Today] is a good day! How are you?' - errs, nw, _ = simple_wer.ComputeWER(hyp, ref) - self.assertEqual(sum(errs.values()), 0) - self.assertEqual(nw, 10) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/asr/tools/simple_wer_v2.py b/lingvo/tasks/asr/tools/simple_wer_v2.py deleted file mode 100644 index 369ef4ad..00000000 --- a/lingvo/tasks/asr/tools/simple_wer_v2.py +++ /dev/null @@ -1,437 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""The new version script to evalute the word error rate (WER) for ASR tasks. - -Tensorflow and Lingvo are not required to run this script. - -Example of Usage: - -a) `python simple_wer_v2.py file_hypothesis file_reference` -b) `python simple_wer_v2.py file_hypothesis file_reference file_keyphrases` - -where `file_hypothesis` is the filename for hypothesis text, -`file_reference` is the filename for reference text, and -`file_keyphrases` is the optional filename for important phrases -(one phrase per line). - -Note that the program will also generate a html to diagnose the errors, -and the html filename is `{$file_hypothesis}_diagnois.html`. - -Another way is to use this file as a stand-alone library, by calling class -SimpleWER with the following member functions: - -- AddHypRef(hyp, ref): Updates the evaluation for each (hyp,ref) pair. -- GetWER(): Computes word error rate (WER) for all the added hyp-ref pairs. -- GetSummaries(): Generates strings to summarize word and key phrase errors. -- GetKeyPhraseStats(): Measures stats for key phrases. - Stats include: - (1) Jaccard similarity: https://en.wikipedia.org/wiki/Jaccard_index. - (2) F1 score: https://en.wikipedia.org/wiki/Precision_and_recall. - -""" - -import re -import sys - - -def TxtPreprocess(txt): - """Preprocess text before WER caculation.""" - - # Lowercase, remove \t and new line. - txt = re.sub(r'[\t\n]', ' ', txt.lower()) - - # Remove punctuation before space. - txt = re.sub(r'[,.\?!]+ ', ' ', txt) - - # Remove punctuation before end. - txt = re.sub(r'[,.\?!]+$', ' ', txt) - - # Remove punctuation after space. - txt = re.sub(r' [,.\?!]+', ' ', txt) - - # Remove quotes, [, ], ( and ). - txt = re.sub(r'["\(\)\[\]]', '', txt) - - # Remove extra space. - txt = re.sub(' +', ' ', txt.strip()) - - return txt - - -def RemoveCommentTxtPreprocess(txt): - """Preprocess text and remove comments in the brancket, such as [comments].""" - - # Remove comments surrounded by box brackets: - txt = re.sub(r'\[\w+\]', '', txt) - - return TxtPreprocess(txt) - - -def HighlightAlignedHtml(hyp, ref, err_type): - """Generate a html element to highlight the difference between hyp and ref. - - Args: - hyp: Hypothesis string. - ref: Reference string. - err_type: one of 'none', 'sub', 'del', 'ins'. - - Returns: - a html string where disagreements are highlighted. - Note `hyp` is highlighted in green, and marked with - `ref` is highlighted in yellow. If you want html with nother styles, - consider to write your own function. - - Raises: - ValueError: if err_type is not among ['none', 'sub', 'del', 'ins']. - or if when err_type == 'none', hyp != ref - """ - - highlighted_html = '' - if err_type == 'none': - if hyp != ref: - raise ValueError('hyp (%s) does not match ref (%s) for none error' % - (hyp, ref)) - highlighted_html += '%s ' % hyp - - elif err_type == 'sub': - highlighted_html += """ - %s - %s """ % (hyp, ref) - - elif err_type == 'del': - highlighted_html += """ - %s """ % ( - ref) - - elif err_type == 'ins': - highlighted_html += """ - %s """ % ( - hyp) - - else: - raise ValueError('unknown err_type ' + err_type) - - return highlighted_html - - -def ComputeEditDistanceMatrix(hyp_words, ref_words): - """Compute edit distance between two list of strings. - - Args: - hyp_words: the list of words in the hypothesis sentence - ref_words: the list of words in the reference sentence - - Returns: - Edit distance matrix (in the format of list of lists), where the first - index is the reference and the second index is the hypothesis. - """ - reference_length_plus = len(ref_words) + 1 - hypothesis_length_plus = len(hyp_words) + 1 - edit_dist_mat = [[]] * reference_length_plus - - # Initialization. - for i in range(reference_length_plus): - edit_dist_mat[i] = [0] * hypothesis_length_plus - for j in range(hypothesis_length_plus): - if i == 0: - edit_dist_mat[0][j] = j - elif j == 0: - edit_dist_mat[i][0] = i - - # Do dynamic programming. - for i in range(1, reference_length_plus): - for j in range(1, hypothesis_length_plus): - if ref_words[i - 1] == hyp_words[j - 1]: - edit_dist_mat[i][j] = edit_dist_mat[i - 1][j - 1] - else: - tmp0 = edit_dist_mat[i - 1][j - 1] + 1 - tmp1 = edit_dist_mat[i][j - 1] + 1 - tmp2 = edit_dist_mat[i - 1][j] + 1 - edit_dist_mat[i][j] = min(tmp0, tmp1, tmp2) - - return edit_dist_mat - - -class SimpleWER: - """Compute word error rates after the alignment. - - Attributes: - key_phrases: list of important phrases. - aligned_htmls: list of diagnois htmls, each of which corresponding to a pair - of hypothesis and reference. - hyp_keyphrase_counts: dict. `hyp_keyphrase_counts[w]` counts how often a key - phrases `w` appear in the hypotheses. - ref_keyphrase_counts: dict. `ref_keyphrase_counts[w]` counts how often a key - phrases `w` appear in the references. - matched_keyphrase_counts: dict. `matched_keyphrase_counts[w]` counts how - often a key phrase `w` appear in the aligned transcripts when the - reference and hyp_keyphrase match. - wer_info: dict with four keys: 'sub' (substitution error), 'ins' (insersion - error), 'del' (deletion error), 'nw' (number of words). We can use - wer_info to compute word error rate (WER) as - (wer_info['sub']+wer_info['ins']+wer_info['del'])*100.0/wer_info['nw'] - """ - - def __init__(self, - key_phrases=None, - html_handler=HighlightAlignedHtml, - preprocess_handler=RemoveCommentTxtPreprocess): - """Initialize SimpleWER object. - - Args: - key_phrases: list of strings as important phrases. If key_phrases is - None, no key_phrases related metric will be computed. - html_handler: function to generate a string with html tags. - preprocess_handler: function to preprocess text before computing WER. - """ - self._preprocess_handler = preprocess_handler - self._html_handler = html_handler - self.key_phrases = key_phrases - self.aligned_htmls = [] - self.wer_info = {'sub': 0, 'ins': 0, 'del': 0, 'nw': 0} - if key_phrases: - # Pre-process key_phrase list - if self._preprocess_handler: - self.key_phrases = \ - [self._preprocess_handler(k) for k in self.key_phrases] - - # Init keyphrase_counts for every key phrase - self.ref_keyphrase_counts = {} - self.hyp_keyphrase_counts = {} - self.matched_keyphrase_counts = {} - for k in self.key_phrases: - self.ref_keyphrase_counts[k] = 0 - self.hyp_keyphrase_counts[k] = 0 - self.matched_keyphrase_counts[k] = 0 - else: - self.ref_keyphrase_counts = None - self.hyp_keyphrase_counts = None - self.matched_keyphrase_counts = None - - def AddHypRef(self, hypothesis, reference): - """Update WER when adding one pair of strings: (hypothesis, reference). - - Args: - hypothesis: Hypothesis string. - reference: Reference string. - - Raises: - ValueError: when the program fails to parse edit distance matrix. - """ - if self._preprocess_handler: - hypothesis = self._preprocess_handler(hypothesis) - reference = self._preprocess_handler(reference) - - # Compute edit distance. - hyp_words = hypothesis.split() - ref_words = reference.split() - distmat = ComputeEditDistanceMatrix(hyp_words, ref_words) - - # Back trace, to distinguish different erroref_words: ins, del, sub. - pos_hyp, pos_ref = len(hyp_words), len(ref_words) - wer_info = {'sub': 0, 'ins': 0, 'del': 0, 'nw': len(ref_words)} - aligned_html = '' - matched_ref = '' - while pos_hyp > 0 or pos_ref > 0: - err_type = '' - - # Distinguish error type by back tracking - if pos_ref == 0: - err_type = 'ins' - elif pos_hyp == 0: - err_type = 'del' - else: - if hyp_words[pos_hyp - 1] == ref_words[pos_ref - 1]: - err_type = 'none' # correct error - elif distmat[pos_ref][pos_hyp] == distmat[pos_ref - 1][pos_hyp - 1] + 1: - err_type = 'sub' # substitute error - elif distmat[pos_ref][pos_hyp] == distmat[pos_ref - 1][pos_hyp] + 1: - err_type = 'del' # deletion error - elif distmat[pos_ref][pos_hyp] == distmat[pos_ref][pos_hyp - 1] + 1: - err_type = 'ins' # insersion error - else: - raise ValueError('fail to parse edit distance matrix.') - - # Generate aligned_html - if self._html_handler: - if pos_hyp == 0 or not hyp_words: - tmph = ' ' - else: - tmph = hyp_words[pos_hyp - 1] - if pos_ref == 0 or not ref_words: - tmpr = ' ' - else: - tmpr = ref_words[pos_ref - 1] - aligned_html = self._html_handler(tmph, tmpr, err_type) + aligned_html - - # If no error, go to previous ref and hyp. - if err_type == 'none': - matched_ref = hyp_words[pos_hyp - 1] + ' ' + matched_ref - pos_hyp, pos_ref = pos_hyp - 1, pos_ref - 1 - continue - - # Update error. - wer_info[err_type] += 1 - - # Adjust position of ref and hyp. - if err_type == 'del': - pos_ref = pos_ref - 1 - elif err_type == 'ins': - pos_hyp = pos_hyp - 1 - else: # err_type == 'sub' - pos_hyp, pos_ref = pos_hyp - 1, pos_ref - 1 - - # Verify the computation of edit distance finishes - assert distmat[-1][-1] == wer_info['ins'] + \ - wer_info['del'] + wer_info['sub'] - - # Accumulate err_info before the next (hyp, ref). - for k in wer_info: - self.wer_info[k] += wer_info[k] - - # Collect aligned_htmls. - if self._html_handler: - self.aligned_htmls += [aligned_html] - - # Update key phrase info. - if self.key_phrases: - for w in self.key_phrases: - self.ref_keyphrase_counts[w] += reference.count(w) - self.hyp_keyphrase_counts[w] += hypothesis.count(w) - self.matched_keyphrase_counts[w] += matched_ref.count(w) - - def GetWER(self): - """Compute Word Error Rate (WER) to summarize word erroref_words. - - Note WER can be larger than 100.0, esp when there are many insertion errors. - - Returns: - WER as percentage number, usually between 0.0 to 100.0 - """ - nref = self.wer_info['nw'] - nref = max(1, nref) # non_zero value for division - total_error = self.wer_info['ins'] \ - + self.wer_info['del'] + self.wer_info['sub'] - return total_error * 100.0 / nref - - def GetKeyPhraseStats(self): - """Measure the Jaccard similarity of key phrases between hyps and refs. - - Returns: - jaccard_similarity: jaccard similarity, between 0.0 and 1.0 - F1_keyphrase: F1 score (=2/(1/prec + 1/recall)), between 0.0 and 1.0 - matched_keyphrases: num of matched key phrases. - ref_keyphrases: num of key phrases in the reference strings. - hyp_keyphrases: num of key phrases in the hypothesis strings. - """ - - matched_k = sum(self.matched_keyphrase_counts.values()) - ref_k = sum(self.ref_keyphrase_counts.values()) - hyp_k = sum(self.hyp_keyphrase_counts.values()) - joined_k = ref_k + hyp_k - matched_k - joined_k = max(1, joined_k) # non_zero value for division - jaccard_similarity = matched_k * 1.0 / joined_k - - f1_k = 2.0 * matched_k / max(ref_k + hyp_k, 1.0) - return (jaccard_similarity, f1_k, matched_k, ref_k, hyp_k) - - def GetSummaries(self): - """Generate strings to summarize word errors and key phrase errors. - - Returns: - str_sum: string summarizing total error, total word and WER. - str_details: string breaking down three error types: del, ins, sub. - str_str_keyphrases_info: string summarizing kerphrase information. - """ - nref = self.wer_info['nw'] - total_error = self.wer_info['ins'] \ - + self.wer_info['del'] + self.wer_info['sub'] - str_sum = 'total WER = %d, total word = %d, wer = %.2f%%' % ( - total_error, nref, self.GetWER()) - - str_details = 'Error breakdown: del = %.2f%%, ins=%.2f%%, sub=%.2f%%' % ( - self.wer_info['del'] * 100.0 / nref, self.wer_info['ins'] * 100.0 / - nref, self.wer_info['sub'] * 100.0 / nref) - - str_keyphrases_info = '' - if self.key_phrases: - jaccard_p, f1_p, matched_p, ref_p, hyp_p = self.GetKeyPhraseStats() - str_keyphrases_info = ('matched %d key phrases (%d in ref, %d in hyp), ' - 'jaccard similarity=%.2f, F1=%.2f') % \ - (matched_p, ref_p, hyp_p, jaccard_p, f1_p) - - return str_sum, str_details, str_keyphrases_info - - -def main(argv): - hypothesis = open(argv[1], 'r').read() - reference = open(argv[2], 'r').read() - - if len(argv) == 4: - phrase_lines = open(argv[3]).readlines() - keyphrases = [line.strip() for line in phrase_lines] - else: - keyphrases = None - - wer_obj = SimpleWER( - key_phrases=keyphrases, - html_handler=HighlightAlignedHtml, - preprocess_handler=RemoveCommentTxtPreprocess) - - wer_obj.AddHypRef(hypothesis, reference) - - str_summary, str_details, str_keyphrases_info = wer_obj.GetSummaries() - print(str_summary) - print(str_details) - print(str_keyphrases_info) - - try: - fn_output = argv[1] + '_diagnosis.html' - aligned_html = '
    '.join(wer_obj.aligned_htmls) - with open(fn_output, 'wt') as fp: - fp.write('') - fp.write('
    %s
    ' % aligned_html) - fp.write('') - except IOError: - print('failed to write diagnosis html') - - -if __name__ == '__main__': - if len(sys.argv) < 3 or len(sys.argv) > 4: - print(""" -Example of Usage: - - python simple_wer_v2.py file_hypothesis file_reference -or - python simple_wer_v2.py file_hypothesis file_reference file_keyphrases - - where file_hypothesis is the file name for hypothesis text - file_reference is the file name for reference text. - file_keyphrases (optional) is the filename of key phrases over which - you want to measure accuracy. - -Or you can use this file as a library, and call class SimpleWER - .AddHypRef(hyp, ref): add one pair of hypothesis/reference. You can call this - function multiple times. - .GetWER(): get the Word Error Rate (WER). - .GetKeyPhraseStats(): get stats for key phrases. The first value is Jaccard - Similarity of key phrases. - .GetSummaries(): generate strings to summarize word error and - key phrase errors. -""") - sys.exit(1) - - main(sys.argv) diff --git a/lingvo/tasks/asr/tools/simple_wer_v2_test.py b/lingvo/tasks/asr/tools/simple_wer_v2_test.py deleted file mode 100644 index cdb7bd05..00000000 --- a/lingvo/tasks/asr/tools/simple_wer_v2_test.py +++ /dev/null @@ -1,90 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for simple_wer_v2.""" - -import lingvo.compat as tf -from lingvo.core import test_utils -from lingvo.tasks.asr.tools import simple_wer_v2 as simple_wer - - -class SimpleWerTest(test_utils.TestCase): - - def testTxtPreprocess(self): - txt = 'abcd [ABC] (fg) ??? OK! ' - txt_preprocessed = simple_wer.TxtPreprocess(txt) - self.assertEqual(txt_preprocessed, 'abcd abc fg ok') - - def testRemoveCommentTxtPreprocess(self): - txt = 'abcd [ABC] (fg) ??? OK! ' - txt_preprocessed = simple_wer.RemoveCommentTxtPreprocess(txt) - self.assertEqual(txt_preprocessed, 'abcd fg ok') - - def testHighlightAlignedHtml(self): - hyp = 'thank you' - ref = 'thank you' - html = simple_wer.HighlightAlignedHtml(hyp, ref, 'none') - self.assertEqual(html.strip(), 'thank you') - - def testWerIgnoreCommentPunc(self): - ref = '(Hello world)! [pause] Today is a good day! How are you?' - hyp = 'hello world. today is a good day, how are you' - wer_obj = simple_wer.SimpleWER() - wer_obj.AddHypRef(hyp, ref) - err_info = wer_obj.wer_info - self.assertEqual(err_info['del'], 0) - self.assertEqual(err_info['sub'], 0) - self.assertEqual(err_info['ins'], 0) - self.assertEqual(err_info['nw'], 10) - - def testKeyPhraseCounts(self): - key_phrases = ['Google', 'Mars'] - wer_obj = simple_wer.SimpleWER(key_phrases=key_phrases) - - ref = 'Hey Google. I have a question about Mars, can I google it? ' - hyp = 'Hey Google! I have question about Mars, can I google it? ' - wer_obj.AddHypRef(hyp, ref) - self.assertEqual(sum(wer_obj.ref_keyphrase_counts.values()), 3) - self.assertEqual(sum(wer_obj.matched_keyphrase_counts.values()), 3) - - ref = 'Hey Google, could you tell me a story about Mars? ' - hyp = 'Hey Google, could you tell me a story about March? ' - wer_obj.AddHypRef(hyp, ref) - self.assertEqual(sum(wer_obj.ref_keyphrase_counts.values()), 5) - self.assertEqual(sum(wer_obj.matched_keyphrase_counts.values()), 4) - - def testKeyPhraseJaccardSimilarity(self): - key_phrases = ['Google', 'Mars and Earth'] - wer_obj = simple_wer.SimpleWER(key_phrases=key_phrases) - hyp = 'Hey Google, could you tell me a story about March and Earth?' - ref = 'Hey Google, could you tell me a story about Mars and Earth?' - wer_obj.AddHypRef(hyp, ref) - stats = wer_obj.GetKeyPhraseStats() - jaccard = stats[0] - self.assertEqual(jaccard, 0.5) - - def testKeyPhraseF1(self): - key_phrases = ['Google', 'Mars and Earth'] - wer_obj = simple_wer.SimpleWER(key_phrases=key_phrases) - hyp = 'Hey Google, could you tell me a story about March and Earth?' - ref = 'Hey Google, could you tell me a story about Mars and Earth?' - wer_obj.AddHypRef(hyp, ref) - stats = wer_obj.GetKeyPhraseStats() - f1 = stats[1] - self.assertAlmostEqual(f1, 0.66666666666666667, delta=0.01) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/asr/wpm_16k_librispeech.vocab b/lingvo/tasks/asr/wpm_16k_librispeech.vocab deleted file mode 100644 index b7b4fb2e..00000000 --- a/lingvo/tasks/asr/wpm_16k_librispeech.vocab +++ /dev/null @@ -1,16328 +0,0 @@ - - - - - -▁ -t -h -e -a -n -d -o -f -i -w -s -y -u -r -b -m -v -l -c -p -k -g -j -' -q -x -z -▅ -" -. -- -: -! -~ -` -; -# -$ -% -& -( -) -* -+ -, -/ -< -= -> -? -@ -[ -\ -] -^ -_ -{ -| -} -th -the -▁the -in -an -and -▁w -ing -▁h -ou -on -▁b -of -en -er -ed -▁and -▁of -or -▁f -▁wh -ch -re -▁s -▁a -▁m -▁th -is -▁c -▁in -▁p -▁t -▁to -ow -▁d -▁n -▁i -gh -▁sh -▁g -om -ar -at -▁that -▁for -▁no -as -▁was -ad -▁l -ll -ver -▁be -▁wi -▁with -▁it -you -▁you -ld -ould -ve -ther -▁his -▁had -▁he -es -▁her -ly -▁on -▁re -id -ion -im -▁him -ght -▁bu -ent -un -▁but -le -▁not -ight -it -ic -▁ha -▁have -ir -us -qu -al -ay -ac -▁is -▁this -▁an -▁r -▁e -▁ex -am -ain -ter -▁fr -▁from -ur -▁j -▁o -▁k -▁un -aid -▁said -▁she -▁ch -▁as -'s -▁whi -▁which -▁con -▁com -all -up -▁up -oun -▁ou -▁st -ble -ation -ak -▁kn -▁will -▁all -▁v -▁or -own -ough -▁pr -▁go -▁would -▁by -ce -ound -os -▁qu -ard -▁what -▁when -ess -ous -very -od -our -ind -▁who -ought -▁li -ish -end -▁so -ok -▁my -▁wer -▁were -▁we -▁do -▁could -▁ne -▁should -▁wor -ook -ct -ome -▁some -▁su -▁se -▁very -▁tr -▁tim -ag -ong -ul -il -ill -▁br -ack -▁know -▁now -el -sel -self -▁at -▁y -▁us -▁man -▁mis -▁lik -ust -▁out -out -▁me -▁mor -for -▁look -ive -▁if -▁other -bout -▁tw -▁one -ven -▁en -▁dis -▁de -ame -▁good -▁every -▁been -ence -▁over -▁they -▁gr -ect -▁sp -pp -ck -ut -est -ast -'t -op -ap -st -et -ant -ance -thing -able -der -▁under -▁their -▁about -▁any -▁gre -▁great -▁did -▁well -▁upon -way -ure -um -▁there -ff -ful -▁are -▁al -ol -ri -▁our -res -▁your -▁see -▁more -▁tak -▁never -▁old -▁befor -ace -▁hand -▁how -ity -itt -ittle -▁little -▁even -▁like -▁down -▁day -ave -ose -ost -ot -art -ist -ood -other -▁two -ather -aw -▁own -▁than -▁can -▁thought -ount -gain -▁again -ang -ig -▁just -▁ro -ove -fter -▁after -▁mister -ell -▁time -urn -▁such -ach -▁has -▁int -▁into -ward -▁only -▁long -▁himself -▁str -▁bo -▁back -▁pl -ink -▁think -▁bl -▁must -ick -▁ar -mp -▁exp -ally -ate -▁le -▁mad -▁mak -▁them -ment -▁before -▁per -ry -▁thr -▁through -▁cl -▁came -▁without -▁part -ious -irst -▁first -▁lif -▁say -▁made -one -▁way -on't -ull -uch -▁much -ress -▁turn -oll -▁wher -▁where -▁imp -▁may -▁nothing -ud -▁app -n't -ness -▁then -ab -are -ved -tain -▁comp -▁while -▁those -▁hous -▁am -▁miss -▁shall -▁don't -▁ob -▁place -ass -act -▁di -▁i' -▁cr -▁dr -ving -ge -age -oss -se -▁seem -ies -▁happ -igh -ie -ite -ide -ice -ep -▁rep -▁op -▁fe -▁room -▁let -▁pass -▁though -▁thing -▁thre -▁tell -▁still -▁pres -ber -▁come -ine -▁fl -ject -▁another -▁missus -aus -cc -ort -ock -▁most -▁ad -▁pre -▁mar -▁work -▁new -▁pro -▁night -▁sup -▁might -▁comm -▁something -ign -▁life -▁word -▁world -▁rem -▁diff -▁yet -▁get -air -ath -av -ail -au -aught -▁away -▁alway -▁house -ers -▁supp -ip -if -ond -▁end -▁ask -alk -▁make -▁count -▁love -▁last -▁gl -iz -▁off -ollow -ild -▁child -old -▁went -ause -▁acc -▁call -ction -ition -ear -▁year -▁appear -▁wom -▁enough -▁open -▁once -▁po -ople -eople -▁people -▁des -▁hun -▁wat -▁cour -▁cont -▁father -▁give -▁ent -ady -▁occ -ible -▁quite -▁ey -▁eyes -▁high -▁fri -▁friend -▁take -▁found -▁three -▁want -▁ind -ord -▁yes -ark -selve -▁woman -▁having -▁bec -▁because -▁face -▁tre -ire -▁right -▁home -ult -▁these -▁course -▁mother -▁saw -▁feel -▁sc -▁sm -▁follow -▁same -augh -ature -ise -ix -▁bre -▁lef -▁left -▁jo -▁heart -▁head -atter -▁against -▁sw -▁small -▁put -ince -ated -▁ever -▁arm -▁spe -▁speak -ue -gether -▁men -anc -▁ear -▁beg -▁among -▁each -▁white -ertain -▁certain -▁wr -▁present -▁kind -▁wind -▁mind -▁find -▁anything -▁many -leas -▁pleas -een -▁light -▁nor -▁eng -▁prop -ossible -▁country -ried -▁black -ning -▁morning -▁began -ian -ily -ty -oment -▁moment -▁kne -▁knew -▁few -turn -▁return -▁name -cl -less -▁ab -▁car -▁mean -▁brought -▁king -▁walk -▁talk -▁sub -▁always -cess -bl -lie -▁belie -▁sl -▁bet -▁better -▁betw -▁between -▁hal -▁half -pt -cept -▁dist -▁asked -▁order -▁gir -▁girl -ody -ary -▁cap -ither -▁here -▁door -▁oh -ffect -over -▁took -▁fell -▁char -▁near -▁suff -▁wish -▁god -▁leave -▁gave -▁youn -▁young -▁water -▁adv -▁hund -▁hundr -▁lady -▁whom -▁why -▁add -▁strang -em -man -ank -emp -orn -▁nex -▁att -▁themselve -▁myself -▁inter -▁matter -▁brother -▁believe -▁sec -▁disc -onder -▁wonder -ever -▁however -▁cons -▁conf -most -▁almost -▁live -ci -ied -iv -▁ev -▁herself -▁both -▁boy -uck -▁gu -▁hop -▁emp -▁remem -▁remember -▁hard -▁heard -▁second -lied -▁replied -▁poor -▁hor -▁poin -iver -▁seemed -▁underst -▁things -pect -amp -▁everything -ory -▁its -▁interest -ions -oke -▁del -▁done -▁hour -▁hum -ush -▁care -▁point -▁set -▁dark -▁war -▁el -▁rest -▁inst -▁far -▁wa -▁wait -eep -▁keep -▁looked -▁sure -▁dre -iness -▁captain -ully -fully -▁watch -▁remain -▁consid -▁side -aut -▁beaut -app -az -ular -▁four -▁opp -▁whose -ro -▁gen -▁gener -▁nature -land -les -dd -▁sudd -▁sudden -▁soon -▁stood -▁along -▁strong -ake -▁six -▁res -▁rec -▁sir -▁feet -▁next -▁known -▁quest -wer -swer -▁answer -▁years -osition -aster -ash -ason -▁reason -▁mon -▁became -▁become -▁draw -▁law -▁stre -▁fear -▁em -▁horse -▁above -▁eight -▁rather -▁ought -ief -oice -▁voice -▁necess -▁laugh -▁death -▁understand -fort -form -ently -▁got -aint -elt -▁felt -ase -ict -opp -▁i'll -▁i'm -'ll -▁master -▁subject -▁object -▁round -▁ve -▁wood -▁cried -▁therefor -▁going -▁letter -▁reach -▁play -ense -▁pers -▁person -▁perh -▁perhap -▁else -▁full -▁hundred -▁twent -▁dire -ical -▁ref -▁reg -▁read -▁wif -▁being -▁whole -ield -▁question -▁true -▁seen -▁remark -▁doct -▁doctor -▁dou -▁doub -oud -▁sou -▁thou -▁thous -▁thousand -▁express -▁contin -▁consider -▁stand -ower -▁power -▁window -▁continu -▁told -▁gold -▁adm -mm -▁form -▁thir -▁thus -▁taken -gg -hind -▁behind -▁chang -erv -▁expect -▁hands -▁perhaps -fect -▁perfect -▁since -▁prince -▁doubt -▁unt -▁until -▁sil -▁sat -▁serv -▁twenty -▁hold -▁wife -▁lo -▁show -▁fact -row -urp -▁lord -▁can't -▁sun -▁till -▁gone -▁alone -▁gi -▁given -▁called -▁book -▁land -ool -▁too -ph -▁ph -▁cre -▁ground -▁close -▁quick -▁col -▁street -▁plac -ready -▁already -▁sever -▁break -▁cond -body -▁body -ile -ia -ib -▁turned -▁temp -umber -▁childr -▁children -ott -▁together -led -▁rel -▁ready -▁prin -▁around -▁fre -▁less -▁cann -▁cannot -▁respect -▁either -▁lay -uth -ual -▁earth -▁does -▁princess -▁bur -▁start -▁dep -▁dear -arge -▁large -▁succ -▁vis -ister -▁prom -▁cle -▁clear -▁general -▁best -▁women -▁whe -▁whether -▁inde -▁indeed -▁compan -leep -▁deep -▁kep -▁step -▁prof -▁happen -'d -de -▁ide -▁really -▁rece -▁differ -▁human -▁dream -▁number -▁const -▁fire -af -▁mount -▁account -▁accord -lish -▁english -▁fall -▁wall -▁short -ial -▁i've -▁truth -▁fellow -og -ations -▁words -▁toward -▁soul -▁ext -ton -ret -▁secret -▁cur -▁imm -▁summ -ouch -ale -▁suppose -wh -▁therefore -▁unc -▁als -▁also -▁themselves -oney -▁money -▁mo -▁min -▁minut -'re -▁suddenly -▁joh -▁john -▁except -▁run -▁fam -▁family -▁carried -▁nat -▁natur -▁least -▁blood -▁looking -▁sk -▁charac -▁character -imp -▁simp -▁times -▁enter -▁didn't -▁mountain -▁trou -▁regard -▁success -▁five -▁gent -▁gentle -vent -lp -▁help -▁expl -▁act -▁neither -▁condition -▁diffic -▁difficult -▁direct -uct -uc -▁kept -▁sound -▁poss -▁possess -▁cross -▁pe -▁hope -▁effect -▁wild -▁gover -▁beauti -▁beautiful -▁fair -▁visit -▁govern -▁case -▁co -▁dress -▁excl -ances -▁contr -roach -ities -ubl -ublic -side -▁eighteen -▁sign -▁sleep -ately -ith -ich -▁rich -ived -▁received -▁disapp -▁disp -▁gentleman -▁won't -▁eas -▁ele -oot -▁foot -iet -▁quiet -gre -▁begin -ne -▁itself -ner -▁manner -utter -▁pret -▁pretty -▁art -enc -▁real -▁touch -▁several -▁jud -▁mil -▁daught -▁england -ens -ier -ience -ipp -▁mer -▁entire -▁spoke -ably -▁occas -▁comfort -▁har -▁par -▁bar -▁discover -▁ter -▁spir -▁spirit -▁hair -ific -ift -▁rock -▁es -▁kill -oken -eng -ength -▁different -▁evid -idd -ually -vel -▁yourself -▁state -quir -▁queen -▁bus -▁business -▁creature -▁direction -▁trouble -▁dead -▁def -▁lost -▁river -ub -hip -▁ship -jo -▁approach -ross -cross -▁dec -▁idea -▁blue -▁it's -▁pol -▁possible -▁road -oft -▁necessary -▁seven -ention -▁sent -▁soft -ek -ke -▁te -ore -▁surp -▁purp -▁surpr -▁purpose -▁rose -iew -▁view -▁cold -olut -atch -ates -▁command -▁intell -unt -▁sister -▁delight -▁neigh -▁need -▁soci -▁table -▁answered -ished -ison -▁prov -▁dev -▁equ -icular -▁particular -chool -▁terri -▁spr -▁week -▁lear -▁learn -▁smile -empt -▁attempt -▁mist -▁che -▁chief -▁sea -▁strange -▁decl -▁school -▁story -▁days -▁smil -day -▁attention -▁leg -ility -ative -▁court -▁lead -▁glad -▁hur -▁dur -▁during -osed -▁prison -▁pleasure -▁common -▁princ -▁princip -▁build -▁dang -▁prod -▁satis -▁satisf -ient -iff -uff -▁corn -▁ste -ude -ug -▁grand -▁carry -opped -ugg -▁list -▁listen -▁hon -▁feeling -itude -▁desc -▁desp -urch -▁church -▁cover -▁pap -▁eye -▁occasion -▁month -▁mus -▁exist -▁exc -▁import -▁sort -ting -▁coming -▁making -ling -▁bel -▁tear -▁big -▁living -▁lived -▁ang -▁sn -▁impossible -▁daughter -ans -▁trans -▁esc -▁escap -▁belong -▁bir -▁bird -▁hung -▁friends -aven -urther -▁further -▁cir -▁circ -▁circum -▁sugg -▁move -▁mag -outh -▁worth -▁enem -▁enjo -▁comple -resh -▁thirty -▁instant -▁mouth -▁ma -▁maj -ating -▁thank -though -▁although -▁bad -ised -ted -▁wide -▁guard -▁sur -▁wrong -▁sold -▁held -rib -▁arri -riage -▁free -▁servant -▁chr -▁christ -▁use -▁uncle -▁glass -aim -▁exclaim -▁utter -▁cause -▁son -▁gard -▁garden -▁ten -▁green -▁third -▁mark -▁accom -▁ver -▁ed -▁est -▁conver -cious -wn -▁town -▁clo -▁clothe -▁forg -▁slow -▁grow -▁neighb -▁whis -▁government -▁desire -▁phil -▁div -▁circumst -oc -▁conc -▁passed -▁dem -▁coll -▁tried -▁mod -▁explain -▁plain -▁drew -▁saf -▁silence -ah -▁struck -▁strength -'ve -▁public -▁chair -ffair -ledge -▁knowledge -▁obs -▁across -▁air -agg -▁suggest -br -band -usband -▁husband -par -▁prepar -▁accomp -ump -▁happy -▁happiness -▁meet -▁none -▁taking -▁z -▁drink -▁service -tern -▁sense -▁pie -▁inf -tim -▁sometim -▁sometimes -▁stud -▁hall -▁depart -▁fin -erve -▁inc -▁sight -▁bright -▁knight -▁getting -▁proper -▁peace -▁occup -ten -▁often -▁conduct -▁exper -▁beginning -▁profess -▁scar -▁scarce -▁hear -▁star -▁stay -▁stopped -▁quar -ford -▁result -ask -▁giving -▁ben -▁evening -▁heaven -▁suffer -ides -▁south -▁neck -▁length -▁loved -rup -▁cor -▁cut -▁lad -▁understood -▁city -▁running -ified -ifty -ung -▁conscious -▁pray -iously -▁certainly -ages -▁admir -▁pract -▁priv -▁prot -▁experience -ater -▁later -ents -▁er -▁danger -▁circumstances -▁returned -▁hast -▁past -▁cast -▁ho -▁demand -▁possib -▁grew -▁aw -▁trave -▁exact -▁exce -▁towards -▁piece -▁rob -itch -iss -ont -▁front -tle -attle -▁field -▁ill -ead -▁instead -head -▁dread -▁shad -▁shadow -▁whatever -▁chap -aith -▁faith -▁arrived -erved -▁observed -▁morrow -ender -ended -▁somewh -▁terrible -▁teach -oup -uthor -▁offic -icture -ney -▁stri -▁whisp -▁o' -▁sens -▁picture -▁broken -▁sevent -▁abs -ounc -avour -ments -▁disappear -aving -▁leaving -▁try -inner -▁dinner -▁easily -▁recog -▁mere -gree -▁cloud -▁fifty -▁prob -▁probably -▁societ -▁class -▁o'cl -▁grave -▁absolut -▁met -▁natural -▁hill -cul -lf -▁self -▁flo -▁flow -▁snow -▁others -▁squ -▁thy -▁thick -▁minutes -well -▁ac -▁accept -▁acqu -▁acquaint -igure -▁figure -▁ag -▁caught -▁fill -▁fine -itting -▁slight -ors -▁marg -▁margare -▁dog -ourney -▁journey -▁o'clock -▁chapter -mag -▁imag -mbl -te -▁complete -▁means -▁married -cially -▁bene -▁margaret -▁afterward -▁breath -▁beneath -▁low -▁crow -▁crowd -▁sitting -▁yell -▁win -▁winter -▁box -▁boat -▁esp -▁swe -▁sweet -oon -ized -ize -▁kiss -▁bank -▁opin -▁forget -▁bit -▁position -▁quarter -▁peter -▁laid -▁je -▁cry -raid -fraid -ract -▁sus -▁sy -▁symp -▁surprise -▁anim -▁animal -▁wit -▁hot -▁grace -▁mur -▁murder -▁occur -▁below -▁allow -▁chance -▁mill -uction -▁maid -▁ann -▁suffic -▁dri -▁infl -▁madame -▁vill -▁bott -▁sword -▁recogn -▁prec -asure -▁troubl -▁roll -▁excell -▁super -ffort -▁effort -▁eat -▁pat -▁opinion -ceived -▁indian -▁port -▁floor -▁moved -▁bring -▁sail -ron -▁fac -▁marriage -▁distance -▁concer -▁lip -▁according -▁miles -▁fool -▁fail -▁proce -▁obl -▁oblig -▁affect -ined -inct -lect -▁gather -▁fight -▁fright -▁fresh -▁caus -▁moon -▁shout -▁relig -▁silver -▁ass -▁vict -▁val -▁vir -▁presence -iately -▁dropped -▁bow -▁deter -▁determ -▁determined -▁ago -▁dut -▁strugg -▁stra -▁straight -▁continued -▁eag -▁reached -ov -▁arr -▁arrang -gr -▁describ -▁beauty -orrow -▁writt -itted -▁writ -▁brown -▁fast -▁write -▁refus -▁forward -▁makes -otion -olog -ifteen -teen -▁cal -▁camp -▁influ -bert -▁evil -▁conse -▁consequ -▁conv -▁concl -▁plan -▁address -▁threw -▁incre -▁attack -▁jack -ident -▁warm -men -▁gentlemen -irm -ired -ench -▁french -▁flowers -▁pleasant -▁twel -▁twelve -fortun -▁fish -▁honour -hood -▁fix -▁slowly -▁carriage -▁protect -▁satisfact -▁separ -▁produc -ically -▁finally -▁ret -▁requ -▁pull -▁pain -ike -▁longer -▁gaz -▁mention -ustom -▁ladies -▁heav -▁heavy -▁appearance -▁appeared -▁bill -▁born -▁bound -▁battle -▁cheek -▁nearly -asp -▁cent -▁arms -▁ran -▁prevent -▁afraid -▁becom -ope -▁tree -▁island -▁guess -▁board -ences -▁relie -▁trees -▁deal -▁early -ocket -▁pocket -▁impress -▁fort -▁fortun -int -under -▁commun -▁sit -bit -▁habit -▁ye -▁anne -▁anx -aken -▁written -▁slave -▁save -ches -ane -ote -▁wrote -ade -▁change -▁i'd -▁stream -▁contain -▁obtain -▁pay -▁declar -▁var -▁bear -▁train -▁inv -▁rev -▁stone -▁dest -▁yellow -▁enjoy -▁followed -▁sad -▁saint -alth -▁north -▁wound -▁believed -▁able -▁opport -▁opportun -▁food -▁enc -▁surprised -▁comes -ured -ivid -▁individ -▁individual -▁usual -▁whisper -▁quickly -▁jew -▁destro -▁doll -▁bell -iddle -ons -▁colon -▁pecul -▁wonderful -ination -▁conce -▁joy -▁intellig -▁intellect -▁hardly -▁silent -noon -▁afternoon -ann -▁tone -▁fanc -▁forgott -▁susp -▁stor -▁please -▁mem -▁member -▁liber -▁expression -▁famil -▁frequ -▁closed -▁outside -▁beside -▁repres -▁immed -▁immediately -eless -▁finished -▁bitter -▁music -▁yard -▁decid -▁trembl -▁author -▁send -▁middle -▁ser -iest -▁exclaimed -▁states -▁evidently -▁easy -▁safe -▁society -ey -arch -▁search -▁sing -▁single -▁simple -val -▁term -▁terr -▁you're -▁interrup -▁bed -▁happened -▁entered -▁det -▁extre -▁extrem -▁pur -▁pal -▁palace -▁various -ablish -▁establish -▁lab -▁bab -▁accomplish -▁press -▁tender -▁fun -▁throw -▁neg -▁bread -▁sal -ality -ution -▁broke -off -▁added -▁used -▁trust -▁influence -▁existence -▁espe -▁especially -▁shar -▁sharp -athe -▁ra -▁raised -▁promised -▁promise -ury -▁company -▁anxious -ism -▁priest -▁stair -▁seventeen -▁sufficient -▁line -▁courage -▁village -▁paper -ondon -▁london -yond -▁beyond -alley -▁ball -▁generally -inary -▁within -arrow -hold -▁spring -▁blow -▁blind -▁offer -oy -unk -▁assist -▁mine -▁vi -▁viol -ium -▁represent -▁colour -inc -ript -zz -▁narrow -▁valley -▁attract -▁satisfied -▁support -aterial -▁opened -red -ires -▁important -▁fifteen -▁couldn't -▁scarcely -▁compl -▁spoken -▁bottom -▁marry -▁mary -ited -▁ourselve -▁season -▁christian -▁sol -▁memory -▁elect -▁colonel -▁polit -velop -▁develop -▁pick -iar -▁peculiar -▁familiar -▁minute -ere -where -▁eager -▁worse -sembl -▁mass -▁grass -ute -▁darkness -avor -iment -▁crim -▁gall -ened -▁frightened -▁red -▁rush -▁ris -▁lie -▁hurried -ines -ames -▁calm -▁travell -oth -▁corner -▁firm -▁says -▁prev -▁collect -▁reflect -loy -▁employ -▁march -▁charm -▁distinct -▁situ -▁shook -▁gh -▁despair -▁office -▁walked -▁saying -▁perform -▁repe -▁repeated -▁gun -▁standing -▁ideas -▁claim -▁party -▁carefully -quired -▁conn -▁connect -▁proud -▁hours -ins -▁convers -▁conversation -▁judge -▁judg -▁affair -▁questions -▁furn -▁wick -▁bless -▁seems -eness -▁mess -▁confess -▁wanted -▁sorrow -▁spot -▁faint -▁favour -▁educ -▁knock -▁regular -ency -▁fash -▁fashion -▁property -▁farm -▁increas -ert -▁observ -▁slipp -▁constant -▁poet -▁golden -idden -▁entirely -▁stop -ham -amber -▁chamber -ament -▁main -▁earn -▁subst -▁proceed -▁nine -▁group -▁dollar -▁opportunity -▁rap -▁rapid -▁stick -▁lifted -▁inform -▁late -▁minister -while -ald -wise -▁simply -▁rid -▁duty -▁lower -▁loud -ission -usion -ius -olution -angu -▁langu -uit -▁fruit -▁ru -uce -▁intr -▁introd -▁instr -▁private -▁favor -▁serious -▁neighbour -▁angry -▁sigh -▁swift -▁supposed -▁receive -ra -▁extra -▁post -▁lips -▁stret -▁stretch -▁virt -▁aunt -▁handsome -▁die -ster -▁myster -▁suspect -▁motion -▁naturally -▁jane -▁jump -▁honor -▁honest -▁earnest -▁tears -▁instinct -▁oppos -▁opposite -sides -▁besides -▁advanc -▁sympath -uke -▁breakf -▁breakfast -▁somewhat -▁hat -ones -▁degree -▁latter -▁language -▁wise -▁ow -▁comb -iers -▁soldiers -▁soldier -▁forgotten -▁scen -▁president -▁harm -▁attend -▁piec -▁entertain -▁report -idence -ault -▁tall -▁path -▁pen -▁color -ony -▁confidence -▁cat -eth -▁police -come -uous -▁spite -▁wheel -▁unless -▁surr -▁surround -▁surf -▁surface -▁everybody -▁storm -amm -▁major -ounter -▁empty -▁ess -▁places -▁placed -▁game -▁merely -▁enemy -by -awn -▁magn -▁houses -light -▁spl -▁splend -▁suc -▁succe -▁roof -▁pale -▁sac -▁sick -▁burn -▁burst -▁appar -▁sacri -dom -▁kingdom -▁rom -oom -▁gloom -to -together -▁altogether -▁plant -▁absolute -ship -▁value -aph -▁excite -▁majest -▁sim -▁clim -ices -▁sand -▁oper -▁shap -ior -▁clean -▁concern -▁moving -▁filled -▁exam -▁exh -▁cheer -▁ce -▁social -cial -▁special -cient -▁ancient -▁spec -▁custom -▁mistress -gress -oop -oph -▁dar -▁brave -▁fortune -ric -▁suit -uel -▁cruel -▁measure -▁treasure -osoph -▁philosoph -let -tory -▁history -ilt -▁built -▁emb -▁delic -▁obey -▁key -▁scr -▁ceas -▁process -▁progress -▁hang -▁stepp -ries -▁miser -▁deliver -▁univer -▁phy -▁engag -▁begg -▁thrown -▁bold -▁occurred -▁army -ley -▁castle -▁fle -▁perfectly -▁advant -▁advantage -▁amer -▁americ -▁van -▁vol -▁nobody -▁shot -▁shut -uture -▁future -▁sky -▁west -▁flower -wood -iumph -▁summer -▁wouldn't -▁that's -▁exactly -▁seat -▁host -▁arch -▁sixteen -▁double -▁following -▁eld -▁escape -▁square -▁recover -▁accompan -▁climb -▁edge -▁catch -▁iron -▁wave -▁situation -▁gain -▁passion -▁yield -▁shore -▁extraord -▁murm -▁incl -▁drop -▁flash -▁liberty -▁temper -▁paid -▁frank -aft -▁extraordinary -▁sacrif -▁mid -▁ap -▁apart -▁tired -▁cab -▁cook -▁civ -▁civil -▁cup -▁cost -▁syst -▁system -▁american -▁wand -▁prove -▁lean -▁affection -▁arrival -▁constit -▁pros -dinary -▁breast -▁hurry -▁hurt -umb -umbl -▁song -▁examp -anch -▁gar -▁ge -▁geor -▁george -▁charge -▁lim -▁feature -▁clothes -▁force -▁seek -▁triumph -▁destroy -▁cott -▁difference -▁weak -akes -▁phys -▁hen -▁henry -▁den -▁superior -rew -read -▁spread -▁space -▁companion -▁companions -▁teeth -▁tea -▁spee -▁speech -ode -▁smoke -ven't -▁haven't -▁extreme -▁kitch -ax -olved -aved -oved -▁proved -▁sixty -▁doesn't -orry -▁sorry -▁wander -iety -▁rain -str -▁action -▁regret -▁pieces -▁und -▁seized -▁detail -▁tail -▁tong -▁tongue -ague -▁depend -▁resp -▁resolved -▁rub -▁proof -▁pop -▁pun -▁isn't -▁contrary -ables -arg -▁fat -▁clever -▁perm -▁curious -▁pref -▁material -▁meant -▁suspic -▁kitchen -use -▁waiting -▁pound -▁duke -▁nice -urb -▁disturb -▁accident -▁content -▁scene -▁imagination -▁imagine -▁egg -▁thor -▁thorough -▁you'll -▁bull -▁advent -▁fault -▁coat -▁treat -▁drawn -▁ordinary -▁luck -▁bro -▁broad -▁sides -▁serve -▁decided -▁eleven -▁twice -▁dick -▁simil -▁crown -▁grown -▁similar -arm -▁alarm -imm -▁threat -▁agree -olly -▁forth -▁fo -ob -▁lou -▁edward -▁somewhere -▁bush -▁gift -ork -▁york -▁appre -rel -▁tast -▁instance -▁flew -▁disappo -▁sav -▁sch -retch -▁wretch -▁labor -▁succeed -▁mir -▁stir -▁east -▁sunday -ues -▁excited -▁laughed -▁evidence -▁arg -▁splendid -▁accustom -▁protest -aff -▁occupied -▁there's -▁necessity -▁authority -▁mix -▁remained -▁sett -▁settled -▁former -▁fallen -▁contempt -chief -▁luc -▁lock -▁ghost -▁popular -▁merc -▁rough -ette -▁eliz -arily -▁cand -▁coup -▁cool -▁angel -▁adventure -▁usually -▁apparently -▁sprang -▁building -▁talking -▁magnific -▁movement -▁thinking -ending -▁started -▁turning -▁excuse -iction -▁glance -▁excitement -▁leap -▁meanwhile -itive -idge -uge -▁age -▁punish -▁rode -▁hors -▁horses -▁horr -ians -▁months -▁earl -ously -▁rang -▁events -erman -▁german -▁gate -▁native -▁detect -▁ven -▁desired -▁covered -inted -▁interrupted -▁paus -▁pack -▁paint -▁putting -▁perceived -▁sake -▁united -▁conceal -fford -▁witness -▁france -▁rejo -▁removed -▁record -▁shop -▁shoot -▁otherwise -▁shr -iod -▁period -▁anxiety -uss -▁discuss -▁trying -▁study -▁render -theless -▁nevertheless -▁fancy -▁baby -yp -▁typ -▁pride -▁pow -▁passage -▁spect -▁amount -▁ly -▁lying -▁thunder -▁guest -▁equal -▁equally -▁assum -▁lover -▁vain -dle -▁driven -key -▁strike -▁nin -▁ninet -▁nineteen -inite -igin -▁origin -▁d' -▁d'art -▁charles -▁mamm -▁reply -▁friendship -▁frequently -iently -▁precious -▁design -eign -ollect -▁recollect -urg -urope -▁europe -ery -acy -▁roy -▁royal -▁difficulty -ify -▁franc -▁glanc -▁milk -▁push -▁waters -▁stranger -▁d'artag -▁importance -▁forty -▁drove -▁intended -▁political -▁constitution -▁larg -ception -▁died -▁actually -▁joined -▁noble -▁smith -▁dry -▁hunting -▁thorn -▁stones -▁infinite -atic -terday -▁yesterday -▁excellent -▁asleep -olk -▁folk -▁cottage -▁required -▁intention -▁birth -▁moder -▁powers -▁maintain -▁prospect -▁troop -▁you've -▁physical -▁jul -▁danc -thod -▁method -▁loss -▁wealth -▁health -▁cow -▁advice -▁wear -▁keen -▁news -▁afterwards -▁aware -lex -ex -▁fox -▁contemp -▁farther -thern -onscious -▁unconscious -▁father's -▁virtue -▁wil -like -▁patient -▁huge -▁fier -▁fierce -▁purs -▁century -▁papa -▁papers -▁bun -▁buried -▁scre -▁check -▁example -▁changed -▁wine -▁devil -▁feather -▁weather -▁coff -▁cas -▁nur -▁faces -▁girls -▁encounter -▁unfortun -▁misfortun -▁sle -▁slept -▁satisfaction -▁possession -▁dreadful -▁silk -son -ants -raph -ograph -▁mistake -▁names -▁james -▁reck -▁rever -▁ut -▁enemies -ny -noc -gn -gnor -▁virg -erg -▁energ -sh -▁boys -▁we'll -▁entr -▁persu -▁project -▁alive -▁convinc -▁boil -▁learned -ided -▁killed -gan -▁urg -▁coast -▁immense -▁pleased -▁bod -▁bodies -oat -▁throat -▁drive -▁youth -ument -▁encour -▁vent -▁vast -▁southern -▁compar -sie -▁monsie -▁monsieur -▁elsie -▁taste -▁knif -itation -▁sufficiently -▁nation -igg -aces -▁takes -▁attach -▁paris -itary -▁military -▁lot -▁boot -▁la -▁lines -▁lose -▁concerning -▁burning -▁introduc -▁lake -▁presently -▁justice -▁fond -▁drunk -▁quietly -▁consist -▁utmost -▁speaking -▁expected -▁hidden -▁hide -▁hither -unch -▁travel -▁err -▁impr -▁stock -▁brain -▁grief -▁brief -▁ant -▁proport -▁ignor -irt -▁begun -▁pier -▁unh -▁unhapp -▁hath -▁emotion -▁elizab -▁elizabeth -▁wid -und -▁fit -arest -▁bob -be -▁seeing -▁prefer -▁religion -▁grad -▁gradually -▁gray -▁grant -▁race -▁prompt -father -▁grandfather -▁benef -▁dism -▁stup -▁drawing -▁visible -ocr -▁mach -▁man's -athed -aking -▁buck -▁tur -▁fur -▁fully -▁hole -▁terror -▁religious -asket -▁basket -▁task -aver -▁admitted -▁thin -▁skin -▁entrance -▁labour -▁observe -▁famous -▁cous -▁cousin -amond -▁diamond -oes -oses -osity -ancho -▁belief -iam -▁william -ik -iant -▁distant -▁distress -iosity -▁curiosity -ribut -agon -▁fixed -▁strik -ively -▁hyp -▁magic -▁communic -▁sell -▁officers -ares -ability -▁powder -▁discovered -uzz -▁doing -▁science -▁odd -▁hotel -▁america -▁message -att -▁capt -▁writing -▁foolish -▁anybody -▁declared -▁forest -▁russ -▁rail -▁descript -▁greatest -▁level -▁species -▁midst -▁ourselves -▁exhib -▁trib -▁provided -▁busy -▁showed -▁mort -▁intim -▁produce -▁ug -▁deck -▁lit -▁d'artagn -▁d'artagnan -▁unk -▁unkn -▁unknown -▁remind -▁patience -andon -▁abandon -▁parl -▁mart -▁mot -▁mysterious -gar -illiant -▁brilliant -▁relief -ibility -▁shame -▁desert -▁clos -▁amaz -▁murmured -▁education -▁organ -lection -▁reflection -▁dav -▁principle -▁sought -▁control -▁quarrel -▁personal -with -▁lamp -▁announc -▁allowed -▁consciousness -iving -▁forgive -▁motive -▁forgot -vest -▁books -▁choice -just -ugust -▁nurse -▁dwell -▁dust -▁dull -▁due -▁surely -▁club -rupt -▁card -▁shown -▁beast -▁argument -▁served -▁steps -▁moral -▁sacred -▁innoc -▁modern -▁perce -expect -▁lod -▁indiff -▁pot -▁pair -▁resist -istr -▁jun -▁beat -▁thrust -▁arth -▁arthur -▁mel -▁melancho -▁stuff -▁skill -▁nodd -▁fal -▁false -ital -▁capital -▁conqu -▁pet -▁noise -▁loc -▁indep -▁independ -▁notice -▁somebody -▁settle -▁everywhere -▁band -zen -▁dozen -▁relations -▁awful -▁appreci -▁shape -que -▁assured -▁inside -▁directly -in't -▁revolution -▁smok -ime -▁rule -▁cabin -▁tele -ining -▁shining -▁shock -▁hollow -▁thirteen -▁fourteen -▁evident -▁unto -io -▁dign -▁indign -▁dom -▁dim -▁dam -▁dare -▁bare -per -▁prosper -▁upper -▁perp -▁greater -▁unf -▁salt -▁information -▁seventy -▁bag -▁mistaken -▁smiled -▁smiling -ator -▁grat -▁spent -▁corres -how -▁request -▁nerv -▁dignity -▁passing -▁shoes -▁gently -▁grasp -▁corp -▁corre -▁correct -▁grey -▁greek -er's -▁dangerous -▁stupid -▁liter -▁hol -▁recall -▁glory -▁victory -▁couple -▁merch -▁sacrifice -▁practice -▁consci -oci -▁associ -▁consent -▁smo -▁smooth -▁conscience -▁neglect -▁unp -▁acquaintance -▁strain -▁majesty -iments -▁increase -▁recognized -▁intelligence -ves -▁wished -▁horror -▁pure -▁knees -▁indic -▁harry -▁hungry -▁delay -▁pursu -▁continue -lcome -▁welcome -▁cases -▁event -▁powerful -▁obliged -▁horri -▁horrible -▁genius -▁wol -▁possibly -▁cord -▁absence -▁fly -ival -▁rank -▁seated -▁seld -▁seldom -apped -ision -▁saved -▁smell -orth -worth -▁stories -rest -▁meeting -▁safet -▁ears -ouse -ole -vo -void -▁bridge -▁require -▁embr -▁rise -then -▁secure -ises -▁wis -▁wisdom -aged -▁principles -▁david -▁scream -▁nam -▁steam -▁loose -▁shoulders -ux -▁sex -▁singular -▁commission -▁dish -ows -▁parents -▁dece -ounce -▁permitted -▁fing -▁fingers -▁finger -▁condem -▁fever -▁match -▁flor -▁trem -▁depth -▁judgment -▁disappoint -▁arrest -▁fold -▁savage -▁dawn -▁indust -▁artic -▁worst -oices -▁voices -▁vague -▁conclusion -▁conclud -▁worship -▁beh -hab -▁ah -▁profound -▁awaken -▁advance -▁crack -▁laughing -▁stead -▁respons -▁poison -▁corresp -▁correspond -▁engage -▁wolf -▁robert -▁root -▁fourth -▁resembl -chang -▁exchang -▁exe -▁exec -▁exer -▁exerc -asc -ze -▁trade -ets -▁legs -▁risk -▁rising -▁arose -▁accompanied -▁moreover -▁horiz -▁blank -▁speed -▁etern -▁eff -▁innocent -▁hitherto -▁exercise -▁subm -▁oppress -▁instrument -▁substance -aughter -▁taught -▁thirst -▁interval -▁nervous -ensive -▁om -▁mat -▁reward -▁dressed -▁convent -itated -▁beloved -▁arrow -▁led -▁birds -isc -▁forc -▁trail -ulous -aul -▁paul -▁sole -▁solem -▁solemn -▁newsp -▁newspap -▁professor -▁neighbor -stair -▁walls -overy -▁discovery -▁hence -▁miserable -▁remarkable -▁considerable -habit -habitants -▁admit -▁retre -▁whispered -▁pursuit -▁retired -acious -▁mamma -▁sugar -▁sum -▁tom -▁fem -▁disappointed -▁merry -▁consequence -erence -▁officer -▁eg -▁disg -▁divine -▁divided -▁proportion -▁appo -▁appointed -▁size -▁holding -▁persist -▁privile -▁holy -▁instantly -uly -▁truly -ourn -▁pour -▁vig -▁hastily -▁dollars -▁delicate -▁supper -▁adam -▁learning -▁improve -▁constr -▁ir -▁sul -▁syl -▁sylv -▁sylvia -▁volum -▁confusion -▁erect -▁resum -▁appreh -▁emper -▁emperor -▁impression -▁env -▁relative -▁walking -ings -▁thoughts -▁type -▁arrange -▁track -▁lack -▁counc -▁council -▁deb -road -uments -▁virgin -▁feast -▁alice -▁calcul -▁roar -arr -▁embarr -▁whenever -ising -▁governor -▁buy -ancy -▁bas -▁intense -els -ssel -▁vessel -▁flat -▁higher -▁aloud -▁log -▁load -▁loving -▁aston -▁astonish -▁astonished -uished -ett -▁proposed -▁persons -▁letters -▁attitude -room -ges -enced -▁republic -▁reproach -▁interp -▁top -▁embarrass -▁wore -▁worn -night -▁midnight -▁flight -▁experi -▁negro -▁gratitude -▁execution -omp -▁wasn't -▁peas -ars -▁cloth -▁counten -▁countenance -den -▁sensible -▁useful -▁careful -▁faithful -▁explan -▁curtain -▁absolutely -▁includ -▁inclined -▁unexpect -▁dun -▁tor -order -▁precise -atherine -ned -▁reckon -▁coach -▁schem -▁dragg -abel -▁amuse -▁complex -▁throughout -▁considered -▁throne -▁consult -▁laughter -▁lives -▁knife -▁worthy -yr -▁ep -▁unpleas -field -▁particularly -▁oak -▁oce -▁ocean -▁plent -▁plenty -▁foreign -vereign -▁adver -▁enth -▁enthus -▁enthusi -▁animals -▁messeng -▁previous -▁prevail -▁addition -▁share -▁intellectual -▁engaged -ounsel -crib -▁rul -▁capable -ged -▁stern -▁fate -▁impat -▁philip -uish -ishop -▁bishop -▁supre -stone -oul -▁price -▁valu -▁engine -▁diver -▁weight -aily -▁daily -▁caused -▁offered -▁nearest -dy -▁somehow -cript -istic -ification -▁prepared -▁bottle -itiz -▁citiz -ization -▁caut -▁wings -▁alas -▁king's -▁cliff -▁stiff -▁cart -▁contract -▁hid -▁northern -▁scatter -▁liked -▁severe -▁female -▁freed -▁freedom -▁experienced -▁comfortable -lic -ler -iot -▁cave -▁named -▁adop -▁waist -▁mutter -▁profession -▁glow -▁forced -▁spark -▁rare -▁trad -▁consequently -▁mile -▁mult -▁cult -▁cultiv -▁med -▁mal -▁stairs -▁variety -▁services -▁rapidly -▁resolution -▁nose -kins -mother -▁grandmother -lexand -stand -▁station -▁flour -▁cho -▁choose -vey -▁reve -▁obv -▁obst -▁lux -▁lunch -▁max -▁sentiment -▁setting -ulation -▁wash -▁test -▁whir -▁treated -▁rubb -▁ease -▁pepp -ially -▁reveal -▁ideal -▁victim -▁horn -▁safety -▁completely -▁telegr -▁remembered -▁suspicion -▁descended -▁sympathy -ositive -▁explanation -▁tight -▁confirm -▁raise -▁proble -▁struggle -▁hind -▁softly -▁waited -▁barb -▁barbar -▁immedi -▁immediate -▁store -▁bore -▁convey -▁steady -▁arab -mond -▁official -▁trial -▁catherine -▁murmur -▁crime -▁touched -▁carp -osp -▁hosp -▁hospital -▁frag -▁indul -▁hom -omm -▁recomm -withstand -▁practical -▁awake -▁composed -▁instruction -▁lovely -▁purch -ners -▁burden -▁jeal -▁jealous -ckn -cknow -umn -▁bought -▁fought -▁temple -▁nearer -▁devot -etch -▁fetch -▁greet -▁household -▁behold -▁curr -ray -▁betray -▁ray -▁perplex -▁annoy -▁voy -onsense -▁nonsense -▁park -▁positive -▁consum -▁disposition -▁displ -▁display -▁ol -▁oliver -▁don -eld -▁beheld -ee -▁coffee -▁scot -▁unhappy -▁astonishment -ension -▁citizens -▁disting -▁distinguished -▁distinguish -▁distr -▁district -ina -ulf -▁anger -▁departure -▁circle -▁servants -ardon -▁pardon -▁pip -phere -osphere -▁fancied -▁mountains -▁comr -▁violent -▁darling -▁pity -▁gathered -abbit -ern -▁larger -▁exha -▁exhaust -▁illust -rop -▁dor -▁prisoner -▁physic -▁assistance -▁machine -▁meantime -▁widow -▁pan -▁we're -▁they're -▁leaves -▁duties -oseph -▁joseph -▁passeng -▁ox -▁meaning -▁original -pir -▁select -▁pierre -▁produced -▁univers -▁victor -▁suprem -ists -▁julia -▁whist -▁pra -▁praise -▁nay -▁releas -▁apolog -▁paused -▁realized -▁watched -▁watching -▁christm -▁christmas -▁opposition -▁million -▁disappeared -▁mood -▁flood -▁flung -▁growing -▁lies -▁column -▁vanished -▁hunt -▁problem -asm -▁enthusiasm -▁democr -▁affairs -▁appro -▁bal -▁bent -▁bay -▁magnificent -▁wicked -▁lone -▁ride -▁guide -▁abroad -▁abrupt -▁principal -▁thoroughly -▁span -incere -▁proc -▁electric -▁dance -iance -▁merchant -▁deli -▁deliber -▁eating -▁ec -▁cheerful -antic -▁committed -▁sour -▁source -▁memor -▁hunter -▁vision -amb -▁definite -▁roman -anches -▁branches -▁brush -▁ceased -work -▁specul -▁becomes -▁goes -▁occasions -allow -▁swallow -▁movements -▁whit -bles -▁leather -▁leaf -▁unfortunate -▁portion -▁lamb -na -▁trunk -onstr -ille -ville -illa -▁pill -▁marilla -▁polite -▁bloom -▁blush -▁flush -▁bride -▁aside -▁brit -▁british -▁maiden -▁remarked -udder -▁kindness -▁monster -acle -▁butter -▁alter -▁disgust -▁height -▁essent -▁amb -▁ambition -▁louis -▁awa -▁anyone -▁voyage -▁revol -▁proph -▁kitt -▁committ -▁committee -▁retain -view -▁interview -ev -▁nap -▁whil -▁whilst -▁colle -▁college -▁block -▁clock -▁blame -▁frame -▁product -▁chain -▁invited -▁drif -▁driving -▁cop -▁cell -achel -▁excess -▁strict -▁useless -▁lonely -▁pulled -▁wal -ral -▁countess -▁empire -▁sweep -▁swep -▁swept -▁weep -▁sheep -▁creep -▁ventured -▁venture -pe -▁egy -▁egypt -▁trick -▁countries -▁por -▁pause -▁pitch -▁libr -▁library -▁imagined -▁examined -▁drown -▁extended -▁extent -▁attended -vestig -▁continually -▁agreeable -fied -▁violence -▁absur -▁absurd -▁possibility -▁delightful -▁windows -▁necessarily -▁easter -▁thread -▁steadily -▁heavily -▁shaking -▁dearest -▁rope -▁incident -▁essential -▁counter -▁discour -▁mischief -▁fleet -pl -▁clasp -iced -▁noticed -▁ignorant -▁degre -▁agre -▁agreed -▁econ -▁econom -oard -board -▁thornton -▁shoulder -▁disposed -▁alike -▁punishment -▁grim -▁dy -▁consideration -▁bark -▁crept -▁crew -▁thither -▁centre -▁aff -▁robber -▁humble -ivity -▁trip -▁preach -▁spare -▁fare -▁farewell -▁purposes -eas -▁uneas -▁perpet -▁fairy -quis -▁row -▁domest -▁domestic -astic -▁active -mosphere -▁ice -▁respond -▁saddle -▁magistr -▁invent -agger -▁shudder -keep -▁spanish -▁admiration -acity -▁dying -▁conviction -▁deserve -▁desir -opping -▁reven -▁conven -iven -iveness -▁reign -aunt -▁volunt -▁steep -▁elder -▁connection -▁facult -▁advanced -▁stage -▁complain -▁amongst -▁gle -▁gleam -▁guil -▁guilty -▁perceive -▁clearly -▁surrender -▁silently -▁highest -▁manners -▁crush -▁crit -▁uncertain -reh -▁compreh -▁interpret -▁emph -her -▁parli -▁parliament -▁hans -▁quality -▁listened -▁explained -▁singing -▁experiment -▁multitude -▁compell -▁compass -▁ninety -▁desper -▁squire -▁warri -▁appoint -▁permission -▁scrap -▁prim -▁occasionally -▁laws -right -itable -▁circumstance -erchief -kerchief -▁handkerchief -▁straw -▁melancholy -▁destr -▁destruction -▁ugly -▁frequent -▁neighbourhood -▁refresh -▁rome -▁ros -osing -▁hastened -▁acquired -▁imper -▁impul -▁virginia -▁afford -▁sport -▁hale -▁haunt -erted -▁deserted -▁desires -othe -▁plat -▁platform -▁plans -▁kat -▁receiving -rain -▁chest -▁execut -▁hut -▁honey -▁repeat -▁confident -▁reading -▁tit -▁shone -▁everyone -▁sovereign -▁sank -▁sen -▁join -aced -▁succeeded -▁cloak -▁trif -▁weeks -▁likely -ingly -▁carrying -▁assert -▁vigor -▁absor -▁absorb -▁sin -▁careless -▁description -▁tin -▁tiny -▁tied -▁sang -▁secur -▁circl -▁rate -▁warning -▁limit -▁limb -▁reb -▁resolut -▁applied -▁stoop -ulated -▁hate -▁arrive -▁scient -▁scientific -▁tristr -▁tristram -▁sam -▁pear -▁stepped -▁id -▁solid -▁relation -▁feelings -ught -▁florence -▁napole -▁thom -▁thomas -▁instit -▁swear -▁carpet -acles -▁avoid -▁opening -▁ren -▁rabbit -▁sentence -ground -aked -a's -▁accordingly -▁constantly -▁characteristic -▁ident -▁anywhere -▁solitary -▁mercy -▁abund -▁narr -▁nich -▁managed -▁strip -▁hopeless -▁conquer -▁lieut -▁lieuten -▁lieutenant -▁forces -▁reduc -▁hopes -▁undert -pr -▁enterpr -▁terms -▁inquired -▁becoming -▁seriously -▁dismiss -ington -▁washington -▁mother's -▁threatened -▁sunsh -▁sunshine -▁irre -aries -▁gay -▁cattle -▁hamm -▁tramp -▁eagerly -▁formed -▁signific -▁knows -▁likewise -▁monkey -▁gown -▁misery -burg -▁pounds -▁families -▁entre -▁august -▁irr -▁irish -▁irres -▁spend -▁descend -▁literature -▁purple -▁ble -▁ken -▁greatly -▁sink -▁resign -▁demonstr -here -▁valuable -▁dw -▁dash -▁monarch -ew -▁renew -▁ende -▁endeavour -▁endeavor -esh -▁flesh -als -▁national -▁walter -▁glimp -▁giant -▁unpleasant -▁modest -▁mud -▁rust -▁prep -▁tal -▁unw -▁shouted -▁unus -▁unusual -▁spiritual -▁actual -▁mentioned -▁brand -▁branch -▁critic -uf -▁yonder -▁preserve -▁nerve -▁baron -▁degrees -aded -▁persuaded -▁exceed -▁talked -▁possessed -▁plung -▁ain't -atory -▁puzz -▁pil -▁attain -iform -▁uniform -▁failure -▁failed -▁assure -▁mich -▁mut -▁counsel -▁pink -▁appe -▁appeal -cknowled -▁ell -▁craft -▁demanded -cknowledge -▁publ -▁doubtless -oo -oe -oil -▁spoil -▁fro -port -▁tyr -▁tale -▁medic -▁flatter -▁politic -▁mystery -▁energy -▁pig -▁refuge -▁scorn -▁hills -▁fastened -▁jimm -▁activity -▁clothing -▁quiver -▁quant -▁quantity -▁eighty -▁gasp -udience -▁obsc -ifully -▁nav -ox -▁furnished -▁pretended -▁interf -▁protection -▁qual -▁qualities -▁que -▁queer -▁vex -▁title -▁difficulties -▁accus -utumn -▁startled -▁drank -▁dragon -▁regiment -▁emerg -▁candle -▁procl -▁sincere -▁representative -▁whither -▁scotland -▁market -▁raising -▁separate -▁separated -▁rad -▁ragg -▁central -▁blaz -▁gardens -com -omfort -▁outer -▁nichol -▁nicholas -▁invol -▁invitation -▁apartment -▁disappointment -▁daughters -▁robin -▁admin -▁administr -▁falling -▁idiot -ators -▁tort -▁nut -▁flame -▁flag -vell -▁retort -▁successful -▁ment -▁mental -▁current -fall -▁function -▁pipe -▁capacity -▁preserved -▁contempl -▁gaze -▁suffering -▁leading -▁disgu -▁survey -uchess -ifest -▁manifest -▁haste -▁finish -▁veil -▁disgr -▁disgrace -▁condemned -▁decision -▁civilization -▁cele -▁celebr -▁plainly -▁finding -▁tables -ilities -▁we've -unning -▁inquir -▁witch -▁trembling -▁retreat -▁craw -▁universal -▁jer -▁prud -▁pin -▁jim -▁billy -anny -▁richard -▁interesting -▁presum -▁permit -▁benefit -▁portra -athing -▁ignorance -▁earlier -▁union -illy -▁opinions -▁num -▁numer -▁numerous -▁nest -▁torn -▁lucy -▁population -lies -▁jewels -▁novel -▁virtues -▁rh -▁ash -▁desk -▁weakness -▁silly -ois -▁streets -▁ruin -▁patch -▁scratch -usk -kind -▁mankind -▁vivid -▁snatch -▁mont -▁monte -▁et -▁fulf -acc -▁wrapped -▁reput -▁repair -▁lodg -stairs -▁flutter -▁maxim -▁charming -▁shield -step -▁shade -▁local -▁dogs -▁impro -▁closely -▁stamp -▁consequences -▁telegraph -▁breathing -rell -▁brow -▁cunning -▁cav -▁congress -▁extremely -▁discip -▁ze -▁fee -▁feeble -▁ward -ture -▁mixture -▁jeff -▁intent -lly -▁wholly -▁shell -▁convict -lict -▁probable -▁caval -▁poem -▁pover -▁poverty -▁driver -▁shirt -▁ruth -▁conflict -▁maybe -upon -▁exceedingly -▁anxiously -▁glor -▁glorious -▁coward -▁im -▁image -▁palm -mith -orm -▁enorm -▁enormous -▁woods -▁wooden -▁frown -▁base -▁bond -▁unable -iable -▁prize -▁tremend -▁fortnight -▁tradition -▁articles -▁article -▁haw -▁peep -▁slender -▁proposition -▁declare -▁prepare -▁mirr -▁painful -▁relieved -▁supply -▁tower -▁manage -▁harsh -iday -▁holiday -▁desperate -en't -▁chris -▁observation -ehead -▁forehead -▁causes -▁reality -▁rachel -▁moonlight -▁commenced -▁involunt -▁katy -cy -▁stirred -▁indians -▁brook -▁uncomfort -▁jean -riek -▁shriek -▁arist -▁aristocr -▁twist -▁matters -▁reference -▁indifference -bury -coat -▁bones -▁refused -▁refuse -▁affirm -▁furnit -▁furniture -▁imperfect -▁thrill -▁cutting -▁marvell -▁marsh -▁thief -▁hy -▁hint -orthos -osom -▁bosom -▁examination -▁development -▁hoped -uk -▁monk -ecture -▁dwar -▁dwarf -reck -▁awoke -▁ech -▁madam -▁echo -▁ring -▁inj -▁happily -▁attribut -▁scarlet -▁accustomed -▁applic -▁await -▁pit -rived -▁hearing -▁knowing -▁joe -▁job -bly -▁doctr -▁instruments -▁instruct -▁construct -▁suffered -▁secured -▁pearl -riet -▁propriet -get -▁veget -▁generous -▁slightest -▁slightly -▁lev -▁revenge -▁stout -▁san -▁mighty -▁he's -▁what's -▁atmosphere -▁lat -more -▁rude -▁opposed -▁lightning -▁increased -▁suggested -▁stretched -▁arranged -▁liqu -▁natash -▁ing -▁cock -▁rage -▁sar -▁marvel -icip -▁anticip -▁tip -▁vic -▁shake -▁kindly -▁cease -▁ban -▁bath -▁cath -▁cathol -▁catholic -▁batter -▁cities -incol -incoln -▁lincoln -▁lest -▁wishes -▁proclaim -▁prisoners -▁ordered -▁transport -peare -akespeare -▁strangers -▁rout -▁railroad -▁reputation -▁louise -ountain -▁fountain -osen -▁chosen -osh -▁fairly -▁escaped -▁puzzled -ufact -▁overcome -avery -▁flying -▁hadn't -▁boast -▁contrast -▁marble -▁cries -▁rear -born -▁pictures -▁advantages -▁hatred -▁tun -▁supreme -▁gest -▁gesture -▁ham -▁pup -▁poetry -▁melt -▁dwelt -▁asham -▁ashamed -▁rushed -▁neighbors -voided -▁extern -▁buff -▁amid -▁warriors -uary -tic -athetic -▁devotion -▁sau -▁grie -▁smart -▁heads -▁orders -▁frozen -▁merit -▁spear -▁slavery -▁slip -▁june -▁reform -▁penc -▁wounded -▁mirror -▁rib -▁refer -▁border -▁disorder -▁displeas -▁begged -▁duchess -▁remote -ernard -▁bernard -▁ma' -▁pir -▁bedroom -▁wreck -▁informed -▁recognize -▁turkey -▁tax -▁keeping -ening -len -▁helen -▁wives -▁ard -▁tend -▁sooner -▁worry -▁spirits -▁complaint -▁suppress -ifth -▁fifth -▁illustr -▁rog -▁debt -▁sult -▁sof -▁inclination -▁determination -▁fatig -▁medicine -▁played -▁motionless -▁eternal -▁looks -▁rocks -▁readily -▁emily -▁sensation -▁sensitive -▁senses -ades -▁comrades -▁comrade -▁ma'am -aham -ibly -▁fatal -▁pupil -▁nodded -▁whence -▁od -▁sultan -▁mourn -▁mild -▁exquis -▁published -▁wrath -▁trap -▁timid -▁members -▁exquisite -▁encourage -▁dusk -▁clerg -bb -▁bree -▁breeze -▁operation -▁reluct -▁wagon -▁won -▁utterly -▁fields -▁brothers -ters -▁conditions -lter -▁shelter -▁emm -▁waves -▁picked -▁delighted -kin -▁apparent -▁fil -▁anti -▁doroth -▁penet -▁penetr -▁appet -▁appetite -▁ladder -antly -▁excellency -▁rif -ued -▁community -▁parts -▁spell -▁changes -▁absent -▁fighting -izing -▁listening -▁tones -▁tis -▁steel -▁attent -▁waved -▁wax -▁beaten -▁helpless -▁portrait -▁breathe -▁footstep -imney -▁chimney -▁domin -▁funny -▁staff -▁sauce -▁dutch -▁adopted -▁note -▁proceeded -bridge -▁detective -▁swell -▁swung -'n -jud -▁prejud -aud -▁expressed -▁shift -▁obser -▁features -xic -▁glanced -ology -ological -▁previously -▁bestow -▁stars -▁deeply -▁joyou -ipped -▁shiver -▁bushes -▁railway -▁troubles -liness -▁albert -▁telling -▁calling -▁pushed -▁venge -▁vengeance -▁related -▁incred -▁dancing -▁plate -▁mouse -house -▁troubled -wild -▁simplic -▁industry -▁ruined -▁leis -▁leisure -▁decide -▁treatment -▁crimson -▁johnson -▁hanging -▁bargain -▁effic -▁ridic -▁ridiculous -▁lodge -▁pool -zard -azz -▁ceter -cknowledged -▁simon -▁whisk -▁whip -▁kissed -▁tremendous -▁shakespeare -▁frog -▁steal -▁solitude -▁elsewhere -▁impulse -place -▁hook -▁accepted -▁conception -▁horizon -estern -▁western -▁altern -▁wake -▁approached -▁neat -▁ere -▁fund -▁created -▁schol -▁net -▁weary -▁lawy -▁satisfactory -▁peasant -istol -▁pistol -▁polly -▁porthos -▁gates -▁final -▁zeal -ovitch -▁consol -▁gallery -▁approaching -ambl -irty -▁dirty -▁unexpected -▁limited -▁exert -▁naked -▁romantic -▁penny -▁russian -▁edith -ered -▁fer -▁slipped -▁regarded -▁concerned -▁eggs -▁sore -▁flock -▁mock -agre -▁disagre -▁cheap -▁intimate -▁tomorrow -itor -▁holl -▁selfish -▁favorite -▁easier -bour -▁contrad -▁lift -▁thresh -▁threshold -▁imprison -▁physician -▁surprising -▁mutual -▁gloomy -inal -▁cardinal -▁criminal -▁lately -▁unfortunately -▁throng -▁romans -▁nail -▁remove -ule -chie -▁slaves -cel -▁precip -▁dian -▁diana -avern -▁compliment -▁range -▁grate -▁grateful -imate -▁depress -▁wretched -▁troops -▁creatures -▁furnish -▁propos -▁lawyer -erem -▁cerem -▁ceremony -▁agony -▁region -▁ult -▁suggestion -▁americans -▁restrain -▁bul -▁expense -▁oath -▁rum -▁alm -▁unl -▁neighborhood -▁childhood -▁advised -▁pressure -▁pressed -▁torture -▁ragged -▁rejoined -▁politeness -▁estate -▁chees -▁beautifully -▁spectacle -▁striking -▁exception -▁floating -▁anguish -▁province -▁henri -▁exposed -▁resumed -▁crossed -▁requires -▁tex -▁firmly -▁friendly -▁struggling -▁nations -▁holland -▁satisfy -▁perse -▁glimpse -▁interested -mber -▁brid -▁copp -▁bon -▁summon -▁legis -▁legisl -▁profit -▁boots -▁established -▁crying -iece -fric -▁completed -▁trace -▁roses -▁rival -▁jac -▁jacob -▁eaten -▁eastern -▁reserve -▁blew -▁page -▁undou -▁teleph -▁obvious -▁purchas -▁purchase -▁susan -elia -▁majority -▁gallop -▁millions -isions -▁provisions -▁series -ofty -▁rag -van -▁continent -▁cheeks -▁sarah -▁quoth -▁impatiently -▁impatience -▁impatient -▁convenient -▁fired -▁wire -▁emphas -▁independent -▁meat -▁fasc -▁creek -inction -▁distinction -▁section -▁shouldn't -whel -▁contents -▁contact -▁mortal -▁discussion -▁infant -eem -▁esteem -▁bade -▁exped -▁expedition -▁playing -ding -edding -▁wedding -▁working -▁asking -chan -▁mechan -▁cam -▁growth -▁oil -▁privilege -▁bol -action -▁convinced -▁devoted -▁ships -▁ins -▁insist -▁inspect -▁insult -▁clouds -▁brandon -▁barn -line -▁collar -▁kick -wick -▁obviously -▁lofty -▁lion -▁dart -▁abst -▁abstract -▁resc -▁rescue -used -▁confused -▁ca -▁humour -▁gazed -▁parties -▁dram -▁drag -▁ahead -▁jackson -▁wilson -▁engagement -▁bench -▁transf -▁jumped -▁pointed -▁stroke -▁sharply -▁pron -▁pronounc -ropri -▁appropri -pped -whelm -▁asc -▁ascertain -ained -▁dining -▁indiffer -▁indifferent -▁tempt -▁slain -▁terribly -▁der -▁derived -▁gro -▁groan -▁obtained -▁farmer -▁pierc -▁crist -▁cristo -▁rod -▁wherever -▁lap -▁chann -▁china -▁thine -▁examine -▁cetera -rid -▁corrid -pired -▁practically -▁thanks -▁intelligent -▁fires -▁properly -▁disagree -▁nurs -▁announced -▁anyhow -▁aren't -▁comparative -▁margu -ixote -▁quixote -▁gil -▁gilbert -▁surv -▁unm -mon -▁supplied -▁supposing -▁realize -▁garments -▁passengers -▁charged -▁indulg -▁cig -▁behav -▁waste -▁congr -▁conceived -▁deceived -▁carol -▁secretary -▁essence -▁ecc -▁eve -oisell -▁swimm -▁awk -▁awkward -▁gracious -engthen -ef -▁fatigue -▁elements -▁eleg -▁elev -▁election -▁element -▁preced -▁precaut -▁apple -▁ple -▁rifle -▁marked -▁uttered -▁employed -▁weap -▁weapons -▁universe -▁sne -▁sancho -▁oct -▁persuade -▁signal -▁roast -▁stole -▁sty -▁style -▁stuck -▁chuck -▁stopping -yn -▁preferred -▁deem -▁breathed -▁terrified -▁gained -▁ber -▁prayer -▁inhabitants -▁concluded -▁repent -▁isabel -▁yards -▁fortunate -▁fortunately -▁production -▁reject -▁innocence -▁temperature -▁stat -▁congrat -▁humanity -▁security -quet -ara -aly -arse -ural -▁external -▁assur -▁unwill -▁dismay -▁napoleon -▁commer -▁commercial -▁grove -▁barrel -▁backward -▁salut -▁comparison -▁instances -go -foot -▁literary -▁skirt -encies -uries -▁centuries -▁images -▁intend -▁pian -▁piano -▁caress -▁richmond -▁connected -vail -osure -▁depos -▁remembr -sem -sembly -▁despite -▁favourite -▁authorities -▁matthe -▁matthew -▁veh -▁vel -▁velve -▁forms -▁dared -▁genu -▁genuine -ulgar -▁pul -▁overwhelm -▁diss -▁endure -▁peasants -▁afflict -▁volume -▁transform -▁december -▁pole -▁presented -lin -▁wither -▁believing -▁misfortune -▁sprink -▁acquainted -▁advert -▁sisters -▁observing -▁distract -▁pony -quire -▁described -▁describe -▁mode -▁knee -▁pirates -▁fury -▁injury -▁chose -▁prose -▁recovered -▁specially -▁visitors -▁lifting -▁dropping -▁ingen -▁narrative -▁standard -▁interior -ember -ptember -▁september -▁wept -ption -▁division -▁contribut -▁you'd -▁undoub -▁undoubted -low -▁pillow -▁industri -▁serge -▁serp -▁muttered -▁marian -▁lin -▁linen -omen -enomen -▁phenomen -▁donkey -▁budd -▁tenderness -▁fitted -▁valj -▁grav -▁francis -▁hunger -▁ones -▁rat -▁arrangement -▁propose -▁peril -▁dan -▁friday -vember -▁violently -▁ways -▁uns -▁unre -▁captured -▁injured -▁announce -▁tomb -▁precisely -▁needed -▁eldest -▁income -▁restor -▁decor -▁ability -▁introduced -▁trump -▁kenned -▁pretend -▁bowed -▁immort -▁nancy -▁ashore -▁commerce -▁encl -▁timber -▁supplies -▁lament -▁frightful -▁hit -▁fulfill -▁summit -▁glove -▁vac -▁borrow -▁purse -▁clergy -▁deny -▁perpetual -ulate -▁uncomm -▁residence -▁providence -▁cream -▁tar -add -▁newspaper -▁newspapers -icken -▁raoul -▁mounted -umbled -ella -▁compelled -▁phot -▁photograph -▁cheese -▁bearing -▁belt -▁rolled -▁addressed -▁surrounded -▁feed -▁strok -▁jes -▁jesus -▁luxury -▁losing -▁lan -▁lantern -▁introduce -▁composition -▁louder -▁studied -▁waistcoat -▁leaning -▁plum -▁scheme -▁naught -cr -▁brick -▁sailors -▁sailor -▁spani -▁spaniard -▁clay -phew -▁nephew -alph -▁ralph -pher -pherd -▁alt -▁folly -urday -▁saturday -▁reckless -▁pace -uncel -▁launcel -▁laur -▁gallant -▁convention -chester -▁dreams -▁alexand -▁lyn -imony -▁launcelot -umbrell -▁umbrell -▁ur -▁experiences -▁phr -▁phrase -▁carri -▁manufact -▁understanding -▁sull -▁vulgar -▁unlike -▁extract -etic -▁aspect -▁hush -▁inspir -▁elb -▁returning -▁embraced -▁embrace -▁contradict -▁recommend -▁inspired -▁monstr -▁weapon -itter -▁glitter -olet -▁violet -▁duck -▁pluck -▁champ -apping -▁arguments -▁july -▁affected -▁preparations -▁reception -▁someone -▁deserved -▁desol -▁cautiously -▁elbow -▁kate -▁heat -▁hell -▁damp -▁defend -▁riding -▁encouraged -▁accur -▁efforts -▁score -▁resour -omach -▁stomach -▁treach -▁rules -▁guilt -▁induc -▁india -▁batt -▁fearful -▁embro -rog -isp -isk -▁brisk -▁musk -▁brut -▁brig -▁rig -▁constitut -wildered -▁safely -▁aboard -▁responsibility -▁scattered -▁musc -▁muscular -case -▁staircase -▁chase -chieve -▁wondered -▁prudence -ili -▁humili -▁punished -▁princes -▁angri -▁assembl -▁cryst -▁borne -▁objection -▁objects -▁disagreeable -▁flames -▁confined -▁denied -▁snake -look -moisell -▁concent -▁concentr -▁sighed -▁investig -▁sway -▁doorway -fore -▁glob -▁appears -ites -▁ages -oster -▁porter -▁porch -▁rebell -cast -▁faded -▁beard -ease -▁disease -▁sunlight -▁pilgr -▁awaited -▁amusement -▁seal -▁mademoisell -▁waver -▁resent -▁escort -ourse -▁indignation -▁citizen -▁doctrine -adow -▁tread -▁tad -ren -▁restless -becc -shire -aire -▁wants -▁bert -▁rebecc -▁revel -bur -burgh -▁institut -▁coarse -ighth -▁eighth -▁weigh -▁vest -▁faced -▁woman's -▁compet -▁worked -▁perceiving -▁celebrated -▁dig -▁bundle -▁resistance -▁tendency -▁sunk -▁highly -▁partly -▁morgan -▁cyr -▁discipline -▁bigg -▁telephone -▁prodig -▁artific -▁daylight -▁accompany -▁reserved -▁seize -▁sees -▁directed -▁exagger -▁ench -▁enchant -▁joke -▁leaned -▁julius -▁interfere -▁sentiments -▁gods -▁doors -▁chill -▁curiously -▁delicious -▁ruins -▁multip -▁refrain -▁indicated -▁locked -▁array -onday -▁sust -▁probability -▁glac -▁cos -▁cosette -▁mos -▁notwithstand -▁wheat -▁humor -▁swiftly -▁rigid -▁slid -▁introduction -▁musical -▁fog -▁fir -quirrel -▁wherefore -▁housekeep -▁enterprise -▁scenes -▁waving -bor -bacc -abb -▁patron -▁independence -▁shepherd -▁frost -sey -bacco -▁assurance -▁slaughter -▁balloon -oonful -fulness -▁anna -▁abruptly -oqu -▁eloqu -▁hideous -▁infer -▁inferior -▁embroid -win -inated -▁mask -▁mould -▁remains -▁moments -▁insect -▁johnny -▁referred -▁georg -▁decre -reland -▁tick -▁upstairs -omb -lars -▁danglars -▁pilgrim -▁horrid -▁specim -▁wilt -edge -▁destroyed -▁burg -verage -▁journ -▁institution -▁devour -▁struct -▁structure -▁stagger -▁communication -ka -▁grain -▁simplicity -atural -▁entreat -▁saving -▁points -▁bor -▁tempor -▁adjust -▁unjust -▁deprived -▁contrived -▁philosophy -worthy -inate -itate -▁adorn -▁dried -▁mission -▁britain -▁examining -▁chok -▁cher -▁marguer -▁marguerite -▁struggled -▁worm -▁rendered -▁patri -▁bitterly -▁steph -▁stephen -force -▁one's -▁pepper -▁knelt -▁breaking -▁velvet -▁slumber -▁hated -▁admire -nament -▁ornament -ilight -▁twilight -bled -▁twink -▁patiently -▁wet -▁jones -▁furious -onies -▁gazing -▁operations -▁pursued -▁cred -▁credit -▁classes -▁arrangements -▁harbour -▁cotton -▁naut -▁administration -▁channel -▁closer -▁kneel -▁moist -▁guests -ensible -▁assault -▁assign -asper -▁sensations -lor -▁stricken -aps -▁eagle -ique -▁fel -▁intentions -▁errand -▁error -▁administer -▁artist -▁heartily -▁pencil -▁tide -▁messenger -▁leader -▁retire -▁agitation -▁ali -▁villain -iscilla -▁whereupon -▁passions -▁disguise -▁seas -▁obscure -▁remonstr -alian -▁italian -▁speedily -▁conceive -ape -natural -▁spur -▁spain -▁invis -▁invisible -ceptible -▁throwing -habited -▁comment -▁puff -▁hoarse -▁parlour -▁torment -▁suck -▁incomp -▁incon -▁inconven -▁wolves -▁bloss -▁blossom -otted -▁tremble -▁testimony -ugh -▁stared -▁badly -▁coron -▁coal -agues -▁primitive -evitable -ribution -ribute -lyosh -lyosha -▁accomplished -itory -▁territory -umbling -▁arise -▁turtle -▁apost -▁apron -▁mc -ces -▁resources -▁male -▁moss -athedral -ovna -▁apprehension -▁machin -▁machinery -▁bidd -▁hero -▁floated -▁ribb -nes -azy -▁uneasy -▁dorothy -▁notwithstanding -▁respectable -▁emot -▁emotions -▁rejoiced -▁steward -▁upward -▁fred -▁freder -▁frederick -▁natasha -▁sofa -▁drum -▁launch -▁disch -ched -▁attached -itating -▁uncomfortable -ugged -ashed -▁kitty -▁statement -▁enjoyment -▁indisp -▁jolly -▁shadows -rov -▁alexandrov -▁pleaded -▁plead -▁attorn -▁attorney -▁generosity -▁norman -yard -▁entering -▁conject -aws -▁lav -▁cheerfully -▁regain -▁forever -▁department -ement -▁amazement -▁climbed -semble -▁editor -▁hay -▁wrink -▁wrought -▁breathless -▁treas -▁combat -▁combined -▁reader -ering -▁cler -▁clerk -▁writer -erable -▁eleph -▁stalk -▁harmony -▁bringing -uring -▁couch -▁contained -▁hatch -▁geograph -▁sem -▁corrupt -▁hood -▁brood -▁hugh -▁chiefly -▁distinctly -▁sadly -▁formerly -▁finest -▁procure -▁ashes -▁dimin -▁chin -▁emin -▁annie -▁irresist -atre -▁asserted -can -▁volcan -▁harr -ring -▁stirring -rav -▁extrav -▁extravag -▁risen -▁motor -▁trembled -ais -▁parad -▁paradise -▁advise -▁lands -▁landsc -▁landscape -▁obed -▁obedience -▁likeness -▁dragged -▁apples -smith -▁thumb -▁ridge -▁graham -▁seren -▁departed -sy -sych -▁psych -rey -▁inquiry -▁blown -▁pond -▁cannon -▁eyed -▁democratic -▁access -▁exchange -▁dale -▁sale -▁review -▁bust -▁worried -▁tub -▁slope -▁circular -▁cake -▁assent -▁slop -eg -▁dangers -▁prayers -▁stronger -▁delicacy -owl -▁fowl -vol -▁benevol -▁coffin -▁monday -▁magician -▁experiments -▁responsible -▁serpent -▁construction -▁valje -▁valjean -▁interrupt -▁downstairs -▁equality -▁prime -▁chall -▁earnestly -▁repose -▁exhausted -▁medium -▁medical -▁agent -▁slee -▁stroll -▁serving -▁jap -▁japan -▁visitor -▁scrup -▁threatening -▁despatch -▁carr -▁fanny -▁ceil -▁haught -▁hail -▁hare -▁hamp -▁nautil -▁discharg -▁discharge -▁agric -▁agricult -▁nautilus -▁reflected -▁painted -▁peak -▁fame -▁jerk -▁prosperity -rant -rous -▁durham -▁adventur -▁fellows -▁bind -▁bid -rig -▁showing -▁copy -▁mademoiselle -▁homes -▁bolt -▁forb -▁forbidden -▁mexic -▁meadow -▁candid -▁solic -▁solom -▁solomon -▁warned -▁planned -▁deliberately -▁droop -▁cru -▁god's -▁weeping -▁bonn -▁dil -▁soil -▁fogg -▁observations -▁details -▁rays -▁butterf -▁punct -▁defence -▁suitable -▁perfection -▁speck -▁defe -▁establishment -▁savages -▁deaf -▁forgetting -▁hypn -▁hypnot -▁interposed -▁ambass -▁balance -▁recognised -▁recognition -▁burnt -▁burned -▁rip -▁hypothe -▁hypocr -▁poured -▁insisted -▁vanity -▁charity -▁charl -▁charlott -aine -▁earliest -▁englishman -▁confound -▁priscilla -▁knocked -▁pine -▁ponder -▁crazy -▁dislike -▁plague -umult -▁tumult -▁bald -▁lessons -back -▁robb -▁joint -▁subtle -▁subs -▁subd -▁subdued -rence -▁cub -▁fisherman -▁leagues -▁promises -▁mail -sion -▁compassion -▁procession -▁complexion -▁improved -▁bone -▁smoking -justice -▁machines -▁reduced -▁succession -esc -▁condesc -▁crouch -▁crook -▁patriot -▁signs -▁shrink -phia -▁russia -▁polished -▁behaved -▁cruelty -apt -▁bapt -▁caution -▁superint -▁alight -course -▁intercourse -▁pred -▁prair -▁felic -▁regions -▁jury -▁lust -▁distrust -▁lane -▁revolver -▁liver -▁magistrate -▁knights -▁wip -▁elegant -▁educated -▁columb -▁dumb -▁dazz -▁ambassad -▁conclude -▁complic -▁arous -▁rous -▁torr -▁appr -▁approve -▁apply -▁appl -▁angrily -▁scrambl -▁sobb -▁persec -▁irreg -▁irregular -▁resolute -▁enthusiastic -▁gives -▁performance -▁audience -▁romance -▁remembrance -ram -▁marius -▁sergeant -▁bodily -▁vigorous -uster -▁metal -▁amused -▁goat -▁cousins -▁perman -▁artificial -▁let's -▁prosec -after -▁hull -▁prey -▁abandoned -▁helped -▁disturbed -▁draught -▁improvement -▁hedge -▁soup -▁merchants -▁halted -▁martin -pic -picuous -▁conspicuous -▁rav -▁copper -udding -▁knot -▁trot -▁traged -venue -▁sounds -▁damsel -▁sack -▁dignified -iency -▁induce -▁facts -▁hearts -▁destined -▁destin -▁patter -▁chatter -▁pant -▁assistant -▁pall -▁works -▁figures -▁radiant -▁belonged -▁guns -▁terrif -▁resort -▁sanct -▁rect -▁swarm -▁behave -▁mac -▁culture -▁graceful -iful -▁assail -▁anyway -▁recently -▁micha -▁michael -▁journal -▁pope -▁inex -ulating -▁squee -▁ratt -▁thence -▁memories -▁spectacles -▁thyself -cell -aker -▁promptly -▁contest -▁folks -▁observer -▁detain -▁deed -▁defect -▁woe -▁planet -▁navig -▁lemon -▁undertake -▁fortunes -tor -▁histor -▁involved -▁throb -▁altar -▁exhibited -▁exhibit -▁miracle -▁cavern -▁strengthen -▁wandered -▁wandering -▁ton -onc -▁lesson -▁prost -anqu -▁tranqu -▁tent -▁submit -▁abode -▁provision -▁provide -▁organization -▁wondering -▁sullen -▁pursue -▁germany -▁tales -▁scale -▁assumed -▁fundament -▁captive -▁glacier -▁jest -▁jen -▁jason -▁hiding -▁legislature -▁strongly -▁freely -▁moderate -▁suspended -▁colors -▁delivered -▁impart -wart -▁rooms -▁solution -▁employment -▁ecst -▁compared -umble -▁yo -▁suspected -abled -▁enabled -▁enl -▁quarters -▁generation -▁largest -▁strait -itud -itudes -▁plot -▁scare -ourning -▁mourning -wers -▁corners -▁weren't -▁pac -▁pacific -▁discl -cles -▁muscles -▁extensive -▁extend -▁perf -▁perfum -▁indef -▁indefinite -▁transl -▁suited -▁corpse -▁corpor -▁defeat -▁declined -ora -▁emma -▁literally -▁conclus -▁adj -▁relieve -▁mixed -▁cavalry -▁recent -▁tang -▁swamp -▁monstrous -▁winds -▁pile -▁limp -▁fragments -▁sher -▁sheriff -▁shaw -▁shor -▁shagg -▁shoe -▁bother -▁bible -▁prem -▁clut -▁tore -▁turk -ky -▁occupation -▁funer -▁funeral -▁concealed -dr -▁withdr -▁withdraw -raham -▁abraham -▁mosc -▁moscow -▁vital -ulliver -▁snapped -▁convince -▁convul -▁older -▁younger -▁potat -▁potatoes -▁faster -▁lanyard -▁insol -▁gw -▁herbert -▁rasc -sued -▁follows -▁resol -▁confession -▁lecture -▁reasonable -▁beggar -▁stayed -▁reconc -▁quot -▁quit -sitated -▁hesitated -▁agitated -▁interests -▁banks -▁shaggy -▁arriving -▁superintend -▁inflict -▁transpar -▁scott -▁magg -▁maggie -▁woke -▁impressions -▁overlook -▁criticism -▁unb -▁absorbed -▁speeches -▁unkind -▁situated -▁feared -▁printed -▁print -alt -▁shalt -ft -▁sympt -▁symptom -▁applause -▁treasury -▁undoubtedly -▁distribut -▁lordship -▁senator -▁undertaking -▁patsy -▁sleeping -▁staring -▁seeking -▁chicken -▁chances -aign -▁campaign -▁yourselve -▁stands -▁inquiries -▁apartments -olen -▁stolen -▁stable -▁stove -▁intelligible -▁incredible -▁adopt -▁button -▁cush -ird -▁bud -▁hawk -▁discourse -▁verse -berry -▁intens -ensions -▁combination -▁goodness -▁nursery -▁gratify -▁application -▁drake -▁he'll -▁sparg -▁granted -▁commanded -▁fled -▁levin -inations -▁eust -▁eustace -▁leaped -▁squirrel -▁sire -▁illum -▁persever -▁incap -▁envelop -▁cellar -▁slew -▁descent -▁unse -sequ -▁subsequ -▁minor -▁sketch -▁exclam -amed -▁sufferings -▁jealousy -▁holes -▁ju -▁parish -▁vault -▁remainder -▁battles -▁conj -▁owe -▁institutions -▁rou -▁affectionate -▁sunny -▁flee -▁fence -▁cigare -▁cigar -▁jar -▁jan -▁january -bruary -▁february -▁hencefor -▁venus -▁lily -igantic -▁gigantic -▁cordial -▁martyr -borough -▁recollection -▁kennedy -frica -▁contemptuous -▁virtuous -▁unreason -velope -▁dispute -▁bost -▁boston -▁bending -▁hostile -▁hound -▁letting -leness -▁landl -▁landlord -▁plants -▁carved -▁syn -▁hoof -▁antiqu -▁latin -▁proposal -▁gravely -eous -inous -▁goose -▁wink -▁enjoyed -▁occupy -acon -▁foul -▁advertise -▁tempted -▁script -▁archit -▁architecture -▁substit -▁smaller -▁veins -▁brass -▁surpass -▁parcel -▁guided -rage -▁center -rod -▁detach -▁beach -▁shill -▁sew -▁sympathetic -▁hunted -▁jewel -▁horseback -▁shaft -▁spargo -▁superst -▁crest -iter -▁teacher -▁fant -▁fantastic -leton -▁provok -▁cure -▁represented -▁ended -▁jail -ait -▁apr -▁april -▁skull -ikov -▁commander -▁tyranny -auvel -▁resolve -▁rolling -▁jimmy -▁jimmie -set -▁sunset -▁increasing -▁shores -▁recommended -ache -necess -▁unnecess -▁unacc -▁armed -alog -▁shooting -▁agnes -cules -▁embark -▁prow -▁capture -▁falsehood -▁calmly -▁gravity -inity -▁assass -▁blast -▁pinoc -▁pinocch -▁pinocchio -▁suspicious -itz -▁decay -▁chariot -idents -▁urged -▁prophet -oh -araoh -▁pharaoh -▁perception -▁warmth -▁elean -▁unnecessary -▁murderer -▁ger -▁gal -▁gang -ange -▁orange -▁gor -▁gorge -▁gulf -▁gas -geon -alty -▁betty -▁eleanor -▁liberal -▁tap -▁interm -▁danced -iding -▁civilized -▁raggedy -▁coloured -▁remaining -▁clergyman -▁plunged -▁prick -lfare -▁samuel -▁breton -▁breed -▁decent -▁depths -▁wail -▁likes -▁specific -▁visited -▁sons -thren -▁brethren -▁liquid -▁clover -▁economic -▁wag -▁inspector -▁crowded -▁closet -▁antagon -▁shortly -▁conjecture -▁mathe -▁uneasiness -▁vote -▁smote -▁irresistible -▁inexp -▁temptation -▁enlight -utom -▁surgeon -▁rent -▁exult -▁simult -▁bonnet -▁scholar -▁cherished -▁meal -▁worsh -▁sleeve -▁whoever -▁grin -▁valent -▁valentine -▁autumn -▁til -▁consumption -▁clothed -▁they've -▁relax -▁colored -▁fawn -▁intently -▁amiable -owed -▁barri -▁shrew -▁shrewd -▁shaken -▁appearances -▁scarec -▁scarecrow -▁venerable -drew -▁washed -▁francs -▁rud -▁exclus -about -▁ot -▁cathedral -atern -▁condemn -▁intern -▁corridor -▁simultane -▁picturesque -▁string -▁bowl -▁repl -▁replaced -▁imperial -▁inn -▁penn -▁lawn -▁provinc -▁amidst -▁ireland -▁transfer -ulations -▁generations -▁pave -▁pavement -▁appointment -▁spectators -▁link -equ -▁assume -▁roused -▁unusually -▁guardian -▁overhead -ucc -▁grind -▁partner -▁milton -▁sym -▁symb -▁symbol -▁crumb -▁crash -▁raft -▁collection -uffled -▁exercised -▁sounded -▁nigh -▁preparing -▁preparation -▁opportunities -▁swim -▁involuntarily -▁exercises -▁sparkling -▁permanent -▁scent -▁dew -▁retorted -anz -▁franz -▁perceval -▁students -▁explor -▁explos -▁mule -iting -▁exciting -▁tongues -▁substant -▁substances -▁hoop -▁lighted -▁pronounced -atically -▁niece -▁non -▁stores -▁travellers -iel -▁daniel -ih -izz -▁viz -izes -▁vot -▁reass -▁dost -ridge -▁issued -▁merri -▁avail -▁italy -▁stray -umm -▁conform -▁dismount -▁envelope -▁scold -▁incapable -▁esther -burn -▁recess -▁forgiven -▁norm -▁normal -▁legal -▁slay -▁circuit -▁drift -▁luxur -▁candles -oid -▁pilot -▁warrior -▁griev -▁grieved -▁eternity -▁polic -▁praised -▁entit -▁entitled -▁furiously -▁intimacy -▁reverence -▁groom -▁resemblance -▁temperament -▁exhibition -▁advoc -▁idol -▁uncommon -▁comprehend -▁certainty -▁actions -▁date -▁dense -▁habits -▁drinking -▁doubtful -▁significance -▁smiles -▁masses -▁ark -▁subjects -▁abb -nesday -▁fest -▁temporary -▁harbor -▁substantial -▁owing -▁psmith -▁misunder -▁plunder -▁plough -▁lucky -▁beasts -▁rebecca -▁juliet -anced -▁prairie -ieth -▁twentieth -▁stealth -▁wearing -▁reaching -▁changing -elphia -adelphia -▁philadelphia -▁digg -ishing -▁mathem -▁ceremonies -▁admission -▁prol -▁prolong -▁scotch -▁superf -▁scor -▁approved -▁shy -▁offended -▁marcell -▁hazard -joice -▁shower -rim -▁fitting -▁kid -▁forbid -▁harvest -ifications -▁pages -▁villages -▁emphasis -▁sustained -▁sustain -▁illness -▁enclosed -mouth -▁humm -▁ascend -▁ascended -▁henriett -▁revealed -▁sledge -▁games -▁critical -▁traver -▁jaws -▁november -▁temples -▁upright -▁thousands -▁tilney -▁quaint -▁hospit -▁hospitality -▁association -▁containing -▁wing -▁concert -▁needs -▁quarrell -▁controll -▁policy -▁happier -▁happiest -▁tempest -▁pirate -▁tig -▁gathering -▁stained -▁stated -▁stump -▁twas -▁crop -▁essentially -▁limbs -▁warrant -▁curse -▁thur -▁outward -▁nowhere -▁sphere -▁university -▁sailed -▁natured -▁jerry -▁estim -▁estr -▁estrall -▁stall -▁stim -▁aim -▁stool -▁wool -▁prostr -▁behal -▁behalf -▁juice -▁amelia -▁developed -▁envy -▁results -▁parson -▁watson -▁reasons -▁cough -▁din -▁shouting -oever -▁octo -▁october -ago -erson -▁dishon -▁descending -lcom -▁welcom -▁lazy -▁strictly -▁coachman -cription -uscript -▁cigaret -▁promising -▁subl -▁sublime -▁varied -▁hired -ola -▁abundance -▁monot -▁they'll -ouquet -zzie -▁flank -▁loudly -▁acquire -▁aladd -▁sources -▁duration -rom -▁destiny -▁procured -▁darcy -▁promin -oundrel -▁scoundrel -abella -▁bachel -▁compare -iscuit -▁rosy -▁chauvel -▁hasn't -▁edison -▁performed -▁clara -▁passionate -▁shrugged -▁obstacle -▁invention -▁sleeves -▁comprom -▁deeper -▁x -▁unworthy -▁restr -▁restraint -▁determine -▁impert -▁cap'n -▁happens -▁minds -▁degr -▁festival -▁costum -▁refined -▁ghast -bourne -▁marvellous -▁suspense -▁braz -▁magaz -▁dix -▁chron -▁wages -▁carriages -▁passages -▁fancies -▁scrut -▁cove -▁clung -▁cyril -▁embroidered -ieves -▁thieves -ogn -▁attempted -▁cares -▁eagerness -▁gregg -▁thoughtfully -▁quitted -▁submitted -▁peaceful -▁raw -▁andrew -▁screw -▁bury -▁creation -▁instinctively -▁corporal -▁distort -▁discont -▁wont -▁waking -▁dressing -▁salv -och -ech -lock -▁glances -▁rested -▁raven -▁gran -▁granite -▁followers -▁bend -upt -▁lights -▁shrill -▁nick -▁expedient -▁tie -▁brows -adows -▁rows -▁brute -▁bunch -uesday -▁analy -▁conseil -▁henriette -▁mustn't -▁needn't -▁sinister -▁register -▁positively -▁deceive -▁dishes -▁guards -▁pulling -gen -ena -▁screen -▁ellen -▁fern -▁efficiency -▁hyster -▁transm -▁irritated -▁obeyed -▁pho -▁phoen -▁phoenix -enz -▁dug -▁associated -cest -▁ancest -co -fold -neath -▁underneath -athan -▁meadows -flow -chor -▁anchor -▁seventh -▁righteous -▁apparat -▁apparatus -▁buzz -▁implor -▁conserv -▁fulfil -▁occurrence -▁flushed -▁bobby -▁carpent -▁torrent -▁desirable -▁desirous -▁glimm -▁drifted -▁despise -▁efficient -▁loaf -▁souls -▁heap -▁falter -▁bluff -▁jasper -▁compliments -▁fed -▁maz -▁francisc -▁attentively -▁haul -▁sulph -▁cupboard -▁restored -▁conspir -▁bulk -▁quart -▁superstition -▁assembly -▁accum -yster -▁oyster -▁blade -asured -▁withdrawn -▁offices -▁sober -mple -▁whirl -▁vessels -▁rarely -▁suffrage -▁pattern -▁student -▁rejoice -▁advancing -▁film -▁dict -▁attendants -▁garr -▁garrison -▁winchester -din -dain -▁disdain -▁measured -▁depended -▁exasper -▁ost -▁bough -▁davy -▁solemnly -▁mob -▁moan -▁almight -▁dreary -▁scripture -▁foam -▁team -nam -▁despised -▁tribe -▁teaching -▁sixth -▁unluck -▁glasses -ses -▁verses -▁caes -▁caesar -▁tribes -othing -▁belongs -▁belonging -acing -▁desolate -▁jok -▁bump -▁starting -▁vegetables -▁responded -▁madness -▁repeating -▁negative -▁negot -▁scrutin -eur -▁touching -joining -▁adjoining -▁lightly -▁constance -▁statue -▁plank -▁lively -▁strangely -▁whereas -▁legend -▁pomp -iqu -iences -▁defiance -▁defined -▁intensity -ulpt -▁sculpt -▁imitation -▁quantities -▁repul -▁dramatic -▁yours -▁blessed -▁julie -▁dilig -▁model -▁prior -▁accent -▁lizzie -▁thorpe -uz -▁jacket -▁acquaintances -▁ke -▁keith -▁kit -▁gladly -ugitive -▁elab -▁elabor -rehensible -▁awakened -▁folded -▁deposited -▁supposition -▁energetic -▁prover -▁proverb -▁perver -brew -▁assertion -▁boxes -▁consolation -▁ninth -▁loy -▁possibilities -▁traveling -▁admirable -▁ancestors -▁tonight -▁management -▁agreement -▁cents -▁cash -▁o'er -▁dreadfully -▁stub -▁stubborn -▁tulliver -▁pudding -▁ax -▁egyptian -▁ferv -▁wigg -▁doubled -▁restore -▁swore -gl -▁dougl -▁musket -▁policeman -▁flashed -▁trifle -▁trifling -uctive -▁dispatch -umerable -▁innumerable -▁lynde -▁manuscript -ondrous -▁merr -▁accomm -▁accommod -▁oxford -stead -▁including -▁sexual -▁harp -lets -▁embarrassment -▁beef -▁conquest -▁hercules -▁misfortunes -▁background -▁rand -▁scand -hand -forn -fornia -ifornia -▁california -lm -▁helm -▁fiery -▁agents -▁talents -▁talent -▁potent -▁county -▁justified -▁maidens -▁dame -▁oven -▁elbows -iour -▁behaviour -▁infantry -▁invar -▁harris -liest -▁prel -▁engineer -▁felicity -▁inher -▁inherit -▁globe -▁thee -▁crystal -▁courty -▁courtyard -chard -▁orchard -▁approval -gard -▁trait -▁scandal -▁jos -▁lace -▁professional -▁hoping -ories -▁idle -▁cigarette -inating -▁customary -▁prett -▁requis -▁daring -▁encouragement -▁entertainment -▁settlement -▁phenomena -▁rejoic -greg -▁congreg -▁faculties -▁faculty -▁kinds -▁imposed -▁expectation -▁frenz -▁hamil -▁hamilton -▁doom -ips -▁loyalty -▁expert -▁map -▁mul -▁numbers -▁islands -▁shows -▁correspondence -▁betrayed -▁behavior -▁phenomenon -▁eug -▁eugen -▁drunken -▁unseen -▁priests -▁aroused -retion -▁discretion -▁dipl -plaine -ynplaine -▁gwynplaine -▁diplom -▁tragedy -▁intervals -▁he'd -▁leaders -▁shrub -▁loun -▁whipped -▁stripped -▁striving -▁invari -▁invariably -▁reaction -▁blunt -▁dad -▁daddy -▁separation -▁dais -▁reminded -▁parall -▁parallel -roph -▁birthday -▁bottles -▁oppon -▁oppose -▁declaration -ational -▁rational -▁ador -▁cultivated -▁excur -▁umbrella -mph -▁attachment -▁aged -▁trout -▁scout -pping -▁vicinity -▁dirt -▁endless -▁assembled -▁apt -▁shaped -inter -▁marquis -▁estates -▁estimate -▁ultimate -▁gaily -▁float -▁artistic -▁calculated -▁nour -▁judges -▁japanes -▁japanese -▁aladdin -▁leon -▁leonora -▁spar -naments -▁abundant -▁personality -▁morality -▁personally -▁valiant -▁basin -▁axe -▁flick -▁flies -▁treason -▁feder -▁federal -▁effects -▁she's -▁cape -▁grunt -▁alyosha -▁chauvelin -▁chop -▁kar -lexey -▁dixon -ston -▁vehic -▁bub -▁radiance -▁fascinating -▁angle -▁transferred -▁pitiful -▁messages -▁languages -▁elm -▁eloquence -elly -▁cruelly -▁frenchman -▁vap -▁vice -▁vib -▁vibr -▁suspicions -▁owl -▁plates -▁detest -▁mike -lantic -▁skil -▁skilful -iless -▁jess -▁jam -▁bewildered -▁resentment -▁oars -▁travelling -fell -▁route -▁alert -▁arrested -▁considering -▁justify -▁directions -▁eyel -nel -▁respon -▁response -▁trous -▁trousers -▁tour -▁faintly -▁fugitive -▁coinc -▁disput -▁tuesday -▁betwix -▁lend -▁sprung -▁ming -▁mingled -▁writers -▁bullet -▁parlor -▁obstinate -▁obstr -▁withdrew -▁gree -▁greece -▁awe -▁awfully -ounded -▁facing -▁rice -▁prejudice -▁swimming -he -hov -▁proh -▁prohib -▁comfortably -▁considerably -bitious -▁bigger -▁wilder -▁wilderness -▁sickness -▁shatter -▁robe -borne -▁fert -▁examples -▁arbit -▁stif -▁nan -▁wheels -ma -▁surm -▁sie -▁siege -▁pav -▁intol -▁intolerable -▁intrig -▁moses -▁mast -▁glancing -▁vale -▁prudent -▁vizier -▁frock -▁appreciate -iate -▁release -▁estralla -maid -▁rhy -▁thankful -▁miracles -▁crust -▁career -▁harness -▁confron -▁fitz -▁contented -▁cared -▁guessed -▁attracted -▁attractive -▁drowned -▁supported -elled -▁mechanical -▁scal -▁vine -▁fashioned -▁scholars -▁startling -▁halt -▁fragrant -▁ungr -▁claws -▁rapture -▁discern -▁femin -▁feminine -ata -▁enlarg -▁foe -▁alarmed -▁jung -▁lung -▁feat -▁sweat -▁indignantly -▁indignant -▁basis -▁extremity -▁undertaken -cribed -▁thereupon -▁gn -mn -▁hymn -nie -master -▁healthy -▁chancell -▁defeated -▁defense -▁mansion -ging -omin -▁rein -▁especial -▁recovery -▁heavens -▁swing -▁metaph -▁monaster -▁plaster -▁disaster -boy -▁dant -▁sorts -▁serene -▁split -▁laura -▁identity -▁wondrous -▁upset -▁horrib -▁politics -▁fid -▁crisp -▁gorgeous -▁converse -▁eccles -▁studio -▁studies -▁jerus -▁jerusale -▁jerusalem -▁desperately -▁severely -▁incons -▁incense -▁rings -▁shillings -▁pockets -▁sacrificed -▁sacrifices -▁beating -▁cakes -▁dismal -▁arabella -▁shir -efort -▁villefort -▁summoned -▁telegram -▁calam -ignific -▁insignific -utor -fish -fe -▁strife -▁charlie -▁surrounding -▁architect -▁handle -▁freck -▁horace -▁dod -▁rubbed -rane -ranean -iterranean -rical -yl -▁steamer -▁financ -iled -▁vigil -▁patty -▁chancellor -ics -▁misunderstand -rection -urrection -▁consecr -▁treachery -▁renown -▁barric -▁barricade -▁joyous -▁feathers -▁mediterranean -▁rises -▁passes -▁league -▁feebly -▁surroundings -▁bidding -othed -▁syll -▁barr -dles -▁armies -bility -▁muse -▁museum -▁clasped -▁persuas -▁unwilling -▁housekeeper -▁barren -▁synony -▁practised -▁trim -▁haz -▁mas -▁boats -▁ceiling -crates -▁incur -ollen -▁challen -▁challenge -▁offence -▁seed -▁loaded -uthbert -▁comparatively -▁forbear -appear -▁reappear -▁charlotte -▁drifting -▁perish -amble -▁mah -▁marched -▁monument -▁fiercely -▁disob -utomob -▁dwelling -▁tightly -▁sending -▁shrank -▁shroud -▁yank -▁complicated -▁enable -▁favorable -inth -▁remarkably -▁incidents -aciously -▁nobility -▁blankets -▁illustri -▁highway -▁humbly -▁jungle -ases -▁phrases -anim -▁footsteps -▁contradiction -▁jelly -▁ambitious -▁extending -brow -▁eyebrow -▁dunb -▁dunbar -▁hob -icago -▁chicago -che -▁perch -▁cens -▁destructive -▁bite -▁sque -iated -▁anticipated -▁unconsciously -▁boldly -▁youngest -▁matt -▁liquor -▁tailor -▁scenery -an't -▁shan't -▁shed -lest -▁philosopher -▁biscuit -▁indicate -▁pencr -▁pencroft -▁chopped -▁rushing -sburg -▁petersburg -▁pump -▁plump -▁subord -▁challeng -▁dripping -▁impressed -▁indulgence -reak -break -▁originally -▁relate -▁mirth -▁henceforth -▁confine -▁dine -▁dares -▁acute -▁acad -▁academ -▁acid -▁acres -▁aid -▁alternative -▁wrest -▁matron -▁privileges -▁nervously -ustral -▁mabel -▁invite -▁inval -▁invalid -▁submission -▁remor -▁remorse -▁remed -▁remedy -▁induced -▁diamonds -▁triumphant -ulously -▁vacant -▁shawl -▁bliss -▁argue -▁excursion -▁discouraged -▁dex -▁dexter -▁harold -▁nerves -orpe -▁chines -▁chinese -▁wonderfully -▁rash -▁landlady -▁barrier -▁expressions -▁cautious -▁ingenious -poonful -▁tablespoonful -▁mistakes -▁marks -inder -▁drops -▁workmen -▁destination -seech -▁compact -▁eliza -▁prejudices -▁gifts -▁wiggily -▁wend -▁democracy -▁drain -▁drug -▁fain -▁unlock -▁eh -▁governess -▁ridicule -▁jacqu -▁needle -▁readiness -▁flora -ica -ilet -▁writes -▁pulse -itimate -▁legitimate -▁attacked -▁imaginary -▁pyr -▁retreated -▁animated -▁seam -▁sniff -▁preserv -▁welfare -▁internal -▁paces -▁languid -▁dissip -▁tavern -▁tank -▁petition -▁regarding -igr -▁mysteries -▁shine -▁traces -▁furnace -▁mathematic -▁aristocratic -▁tid -▁tidings -▁channels -▁heels -▁effective -▁carries -chieved -▁marth -▁martha -▁infirm -▁immortal -ki -▁gaunt -▁gambl -▁prominent -▁wig -▁commence -▁commit -▁u -▁ruff -▁interference -▁gross -thorpe -▁subsist -▁mate -▁slim -▁affections -▁percy -▁hann -▁bachelor -▁sins -▁factory -▁garment -▁substitute -ger -▁confed -▁confeder -▁tiger -▁crawled -oman -ronom -tronom -▁tribute -▁gratification -▁conscient -▁repress -▁influences -ichikov -▁revived -▁peop -▁rop -▁peered -▁peoples -▁padd -▁abbey -▁salvation -▁celia -▁illustrious -▁handed -▁instructions -▁tenderly -▁hurriedly -▁echoes -▁confid -▁formid -▁formidable -▁lid -▁winning -▁proceedings -▁transparent -ceptions -▁snuff -▁berth -▁eccent -▁eccentric -thenians -▁warn -▁senate -▁appropriate -▁otto -▁suffice -▁indisc -▁frantic -me -▁highness -▁strikes -▁sermon -▁bruce -▁faithfully -▁ling -▁lingered -auch -asmuch -▁frivol -▁ecclesi -▁sax -▁encourag -▁remarks -giving -▁roots -▁inner -▁gap -▁views -▁deject -ria -ripp -astroph -▁catastroph -rian -▁european -▁whale -▁abuse -▁edwin -▁edges -▁drap -▁blunder -▁differently -▁fig -▁ensued -mit -itious -▁grandeur -▁enclosure -▁buildings -enn -▁spoon -▁saloon -▁salute -▁aram -▁aramis -▁interven -▁empt -▁learnt -▁overt -▁significant -▁hurled -▁tunnel -▁travelled -▁traveller -▁exclamation -quest -▁trench -enched -▁yourselves -▁discontent -urity -▁obscurity -▁christianity -▁spin -▁spinning -▁paraly -▁confronted -▁converted -iggins -▁higgins -▁clutched -▁casual -▁relationship -▁signature -▁attendant -dess -▁goddess -▁skins -▁rost -▁rostov -▁gnaw -▁expecting -▁silvery -▁spotted -▁weariness -▁spray -▁essay -▁rugged -▁augustine -ivities -woman -▁lingering -▁fertile -▁expensive -▁simultaneously -▁vigorously -▁eminent -▁du -▁fairies -▁tech -▁techn -ching -▁whistle -rael -▁robins -insmore -▁dinsmore -▁childish -▁intending -▁rufford -▁crossing -ennox -▁ends -▁trumpet -ettle -▁kettle -▁uneasily -▁swallowed -▁acted -▁phant -▁phantom -▁nom -▁som -▁sanction -unction -▁damned -▁additional -▁clad -▁horizont -▁fears -▁sciences -▁obedient -regard -▁disregard -▁hack -▁supernatural -▁unnatural -▁carly -▁carlyle -▁fascinated -▁illuminated -▁disl -▁dislik -▁curl -▁adher -▁blessing -▁glittering -▁swee -▁expand -▁cats -▁acts -uded -▁murdered -▁collected -▁reluctantly -▁mechanically -▁mischie -▁insensible -▁provinces -▁fan -vid -interest -think -▁husband's -▁obstacles -▁obstin -▁measures -▁mare -▁uly -▁scrub -▁stain -▁voc -iation -▁loom -▁guine -ainty -▁dainty -▁vehement -▁decline -▁carb -arn -nut -picious -ware -▁cluster -▁ripe -▁ridden -▁rue -dyke -▁pointing -▁glowing -▁nun -▁pyram -▁pyramid -▁clam -cer -▁strings -▁stockings -▁prest -▁songs -▁thereby -its -atis -▁dissatis -▁bunny -▁jenny -▁miller -tel -▁dashed -▁bomb -ubert -▁cabinet -▁jehov -▁cass -▁stare -▁strode -▁splendour -▁occasional -▁superintendent -▁unfold -▁chem -▁chanced -▁expens -▁pledge -▁ivan -▁lovers -▁adventures -▁deer -acinth -▁hyacinth -▁stately -▁swam -▁improp -away -▁smother -▁avoided -▁closing -▁splash -▁dagger -▁feverish -▁scro -▁scroo -▁scrooge -▁urge -▁disappro -ifying -▁placid -▁placing -▁perspir -▁naples -▁girl's -▁deadly -▁neglected -▁deeds -▁disguised -▁rescued -▁yielded -▁ardent -▁forthwith -▁fireplace -▁commonplace -▁commonly -▁defarge -▁pasture -eslie -▁leslie -▁latest -▁cyn -▁twins -fresh -▁ascending -▁passionately -▁humph -ai -ji -▁marie -▁admired -▁admiral -▁mules -▁judged -▁apparition -▁thoughtful -▁chestnut -sitation -▁hesitation -▁stake -▁average -▁reef -▁acknowledged -▁sweetness -▁beck -▁damage -▁damn -▁adequ -▁secretly -▁torch -▁buffal -▁cards -▁brace -▁discomfort -▁fundamental -▁wrist -▁confirmed -▁ned -▁acting -icial -dled -▁foster -▁shirley -▁squeez -▁fisher -▁chapel -illery -▁artillery -▁tenth -▁circles -▁fuss -▁device -▁venice -seless -▁devote -▁troublesome -▁chat -▁bat -▁chick -▁nic -▁harmless -▁harmon -rance -▁girdle -▁begins -▁argued -▁whim -▁marianne -ester -▁bitterness -igger -version -▁appall -▁displayed -▁underground -▁devout -▁tobacco -▁lash -uity -▁ingenuity -gu -guard -▁emptied -▁twisted -▁released -▁indulged -▁indulge -▁elephant -bird -▁westward -▁downward -▁schoon -▁existed -▁shells -ased -▁deceased -▁recalled -▁crimes -ilian -▁volumes -▁wealthy -▁pad -▁falls -▁legg -▁decidedly -▁heavenly -▁pretending -▁hearth -▁jude -▁seizing -▁shutter -▁boiled -▁boiling -ading -▁pressing -▁painting -▁discre -▁beckon -▁equip -▁recip -▁equipped -▁douglas -icient -▁tyrant -▁overflow -▁ske -▁skeleton -▁smallest -▁lodged -▁briefly -▁mute -▁constitute -▁alexander -▁grinned -▁harriet -▁hamlet -▁hardship -▁ladyship -▁historical -▁malign -igneur -▁feign -▁woo -▁horrors -▁untr -▁trimm -▁ponies -▁torp -▁senior -▁distribution -▁abol -▁tranquill -▁tranquil -▁discreet -▁conference -▁eyelid -▁communicate -▁hostess -▁bart -▁impertin -▁controver -▁robbed -▁dependent -▁ghastly -▁sulphur -▁deform -▁divide -▁specimens -vas -▁canvas -▁lowest -▁babies -ipping -issipp -▁mississipp -▁mississippi -▁maintained -orous -▁contempor -▁twig -▁pillars -▁thanked -▁gasped -▁altered -aur -▁restaur -joy -thed -▁ethe -ithe -▁settling -▁scant -▁sect -▁tune -▁lodgings -▁gramm -▁reprod -▁muslin -▁ram -▁jews -▁kings -bs -▁methods -▁terrace -▁mity -▁mitya -▁wizard -▁attentive -▁wishing -▁evolution -▁russell -▁depends -▁resemble -▁polish -▁incomprehensible -▁boom -▁booth -itably -evitably -▁climate -ateau -▁chateau -▁awaiting -▁unaccount -▁breeches -▁diseas -▁towns -▁arbitr -rise -▁sunrise -▁wagg -▁ware -▁ecstas -▁carelessly -▁armour -ourt -▁composure -▁caroline -▁carolina -▁ultim -▁ultimately -▁addressing -▁comprehension -▁conducted -ifies -▁inquire -▁willing -▁lodging -▁perpetually -▁edition -▁receip -▁receiver -broken -▁glen -▁investigation -▁involuntary -▁wrinkled -onze -▁bronze -▁omn -▁cargo -▁rug -▁differences -▁verd -▁berries -▁bridle -▁create -▁mol -▁mascul -▁prelim -▁preliminary -▁included -▁ojo -▁pleasing -▁rubbing -▁crad -▁ruddy -▁gratified -▁secl -▁gentleness -▁requisite -▁thorndyke -aternal -▁endured -▁restrict -▁switch -▁cradle -▁calls -▁dazzling -▁continual -▁today -▁perplexity -▁strewn -▁streak -▁plentiful -▁mournful -▁drows -▁defended -▁reconciled -▁henrietta -▁loyal -▁rascal -▁verily -▁organized -▁quad -ada -▁fortress -▁mun -imble -▁worshipped -▁bang -angle -▁bi -▁willingly -▁thinks -▁peg -leg -▁volunte -▁murray -▁verdict -aves -▁tipto -▁communicated -▁banner -▁banquet -▁selection -▁cad -▁plaint -ino -▁peppino -▁influenced -▁tremulous -▁otter -unned -▁rack -▁races -▁bour -lay -▁perceptible -▁gait -occ -▁bucc -▁buccane -▁bug -▁alb -▁theory -icious -ks -lace -▁necklace -▁flint -uated -▁acknowledge -▁stamm -▁stammered -▁yoke -▁entrust -▁butterfly -▁butterflies -▁greeted -▁defiant -▁purely -▁rattle -▁wendy -▁hasty -▁hasten -▁brandy -▁beforehand -▁prosperous -ae -▁pang -▁sinking -▁parent -▁ascent -▁ribbons -▁garret -▁vehicle -▁accidents -▁rocky -▁pavl -▁splendor -▁dynam -stril -strong -▁africa -▁reinforce -emost -▁accused -▁bestowed -▁offering -▁superiority -▁spake -▁kent -▁kentuck -▁indispens -▁clement -upiter -occup -olph -▁erupt -▁suburb -▁bake -▁gab -▁gabri -▁gabriel -▁glare -▁youthful -▁vicar -▁pipes -▁characters -▁pickwick -▁swinging -▁jupiter -▁interrog -▁mice -ylum -▁rustling -▁magnetic -▁sadness -▁ate -▁presume -▁sod -▁trusted -▁tact -▁palaces -▁poly -▁wallace -▁imagin -▁crushed -▁prevented -▁hearted -▁jove -ives -▁attraction -▁bathed -▁boss -▁bacon -▁exclusively -▁painter -▁inconvenience -stretch -▁angels -▁envir -▁champion -▁renewed -▁summons -▁unhe -▁melted -▁phill -▁phillips -▁dispos -▁disposal -▁accordance -thodox -▁melody -▁hark -▁muddy -▁fidel -▁fidelity -▁teasp -▁undertook -▁stimul -▁motions -▁rebellion -▁chuckled -▁defin -▁definition -▁kin -▁peering -udge -▁impulses -▁jersey -▁believer -▁vary -▁tract -▁she'll -▁respectfully -▁wed -▁wednesday -▁traced -▁packed -coq -▁advis -▁hover -▁riches -▁strongest -ronsie -▁phronsie -▁encountered -ts -▁hud -▁bloody -▁lumber -▁jeffrey -▁femal -▁exertion -▁rend -▁overtook -▁whistling -▁lump -▁ign -▁physicians -▁felix -ez -▁costume -▁coolly -▁fishing -aming -▁convenience -▁mous -▁marvelous -▁atlantic -▁excused -▁magistrates -▁ambassador -▁lud -▁forgiveness -▁diminished -▁unequ -▁glided -otes -otesque -▁grotesque -▁marcella -▁pennsy -▁ribbon -▁moisture -▁sentences -▁disciples -▁struggles -▁turb -▁favourable -▁vegetable -▁fashionable -▁nois -▁noiseless -▁applaud -▁maximilian -▁lined -sue -▁issue -alogue -▁daresay -▁tray -▁trag -▁tragic -ager -▁pag -▁pagan -▁sag -cher -▁churchyard -▁churches -▁banished -▁cherry -▁annih -▁lu -▁bru -▁junior -▁announcement -▁turret -▁turf -▁expenses -▁notes -▁types -▁choosing -eon -▁luncheon -▁whistled -▁curtains -▁dreamed -lain -▁bye -▁fore -ye -▁owner -▁courteous -▁glar -▁gasc -▁republican -▁chew -▁tart -▁fuel -▁mason -▁loft -▁aloft -tal -▁total -▁shelf -▁outrage -▁coax -▁whiskers -▁lifeless -▁snap -▁pint -▁noon -▁wiser -▁doth -▁hurrying -▁dined -▁ditch -▁thrice -▁malice -▁lions -▁luke -▁lame -▁industrial -▁formal -▁brutal -▁blanket -▁flu -▁secre -▁secrecy -cliff -▁honourable -▁commod -▁wires -▁congratulate -▁masters -▁master's -▁senseless -▁germ -▁synonym -▁pains -elie -▁lakes -▁snakes -▁lark -▁imposing -enka -ushenka -▁grushenka -▁starved -▁unde -▁ruler -▁emphatically -▁wider -▁maria -▁eventually -▁proportions -▁recl -▁grip -▁recognise -▁nobl -bling -▁firing -▁loves -▁turns -▁bars -▁buddh -▁aristocracy -ander -seigneur -▁monseigneur -ieties -quisitive -▁enlist -▁franklin -▁frankly -▁vinegar -▁multiplied -▁trod -▁avenue -▁elaborate -▁twelf -▁flowing -▁philosophers -▁perched -▁excus -▁sed -▁ster -▁dismissed -▁reverse -▁extravagant -▁prayed -▁reported -▁explo -▁saviour -▁chival -▁lawyers -▁clar -▁clark -mark -▁checked -▁deemed -▁hereafter -▁cuthbert -▁cyl -▁insuff -▁lent -▁consented -▁greed -▁invest -▁rubber -▁attained -▁lick -▁sa -▁saunder -▁eric -athom -▁chronic -unge -▁plunge -▁southward -▁northward -▁homeward -▁rider -▁disturbance -▁pulp -▁exter -▁penalty -▁chichikov -▁flaming -▁clump -water -▁progr -▁contri -▁endowed -▁courtiers -▁knives -▁wo -▁outl -▁hyd -▁wagons -▁delicately -▁companionship -▁predict -▁sor -▁dresses -▁infern -▁dolly -▁sandy -▁unanim -▁gloves -▁bertram -▁labors -▁robes -▁visions -▁sworn -▁swollen -▁trained -▁almighty -ossack -▁cossack -▁coloss -▁chalk -▁confidential -▁sel -▁sequ -▁wipe -▁tuck -▁heroic -▁riot -▁inherited -▁habitual -loc -▁verloc -▁complac -▁stem -▁encamp -▁gild -▁matild -▁growled -▁curled -▁limits -▁ordin -▁ordinarily -▁disconc -lers -▁infancy -▁conclusions -▁barely -▁pushing -▁miriam -▁honorable -▁stag -▁genji -▁outline -▁proceeding -▁smoothed -▁pronounce -▁confer -▁notion -▁cushion -▁engross -▁decis -▁scamp -▁universally -▁parted -▁jehovah -▁gale -▁persian -▁roubles -▁roulet -▁regretted -▁prodigious -▁realm -▁publish -▁concentrated -▁babyl -▁babylon -▁demon -▁redou -▁stepmother -▁obstinacy -▁dantes -▁saul -▁sant -▁samp -craft -frey -▁surmount -▁tumbled -▁stumbled -▁peck -▁pict -▁dump -▁privately -ticoat -▁petticoat -boat -▁petty -▁mischiev -▁annihil -▁odour -▁financial -▁repentance -▁annoyance -▁holm -▁execute -▁bann -▁streams -▁constitu -▁aud -▁audible -▁ud -▁impud -▁giants -▁inheritance -▁nell -▁battery -▁framed -▁destit -▁destitute -▁allah -piece -▁horrified -▁practise -thm -▁rhythm -▁mechanism -power -▁overpower -▁chast -▁groups -don -uted -▁neut -▁neutral -▁echoed -▁assumption -abby -▁shabby -▁roads -▁alexandrovitch -▁twitch -ivial -▁trivial -▁obliv -▁farmers -▁zad -▁doctrines -▁mirac -▁traveled -▁protested -▁protestant -ovel -▁biggest -▁bridges -▁breach -▁achieve -▁landed -ession -▁session -▁sid -▁confessed -▁stooped -▁rainb -▁rainbow -▁climbing -▁preceding -▁calf -▁companies -▁decorated -▁excessive -▁massive -▁inspiration -▁nineteenth -▁odious -▁ingred -▁ingredient -▁curve -▁indispensable -▁twelfth -▁resource -▁planted -▁ripp -▁thomp -▁thompson -▁melod -▁imperative -▁hopkins -▁pegg -▁peer -▁roger -▁contemplation -▁fragrance -▁raymond -▁entertained -▁consisted -▁haughty -▁clapped -▁competition -arian -▁shorter -▁commend -▁merci -unci -ced -forced -▁clatter -▁earthqu -▁earthquake -▁wiped -▁fishermen -▁attire -▁bagg -▁amend -▁mend -▁selected -▁lur -▁lurk -▁executed -▁terrific -▁insufficient -▁hammer -▁fluid -▁keeper -▁epoch -▁inches -▁specimen -itten -▁wri -▁thicket -▁interpretation -▁monoton -▁monotonous -▁intoxic -▁israel -▁chor -▁chorus -uspicious -▁prevailed -▁discord -▁superficial -▁magazine -▁represents -▁strand -▁curate -▁searching -▁catching -▁theatre -▁spared -▁relating -▁relics -▁galley -▁compromise -athen -▁executive -▁bonds -▁killing -▁training -▁merciless -▁divor -▁divorce -▁grieve -▁wist -ombre -▁sombre -▁wre -▁wreath -▁fascination -▁disobey -iture -enditure -▁expenditure -▁demands -▁individuals -▁roughly -▁hudson -▁shared -▁irritation -▁painfully -▁monop -▁frail -▁lar -▁anton -▁decisive -▁coincidence -we -▁rans -▁successfully -▁unhappiness -eliness -▁os -▁osborne -▁fet -▁waverley -▁repor -▁reporter -▁softened -▁implied -▁punch -▁chairs -▁whirling -▁whirled -wich -▁abbe -▁abomin -▁throbb -▁paying -▁empress -▁colony -▁colonies -fl -▁amus -▁amusing -▁lennox -▁baggage -▁tranquillity -▁lob -▁holmes -▁shapes -▁weighed -fred -▁kindred -▁signed -▁cleared -▁welcomed -agraph -▁paragraph -▁cambridge -▁sternly -▁novelty -▁regiments -▁sparrow -▁impet -▁impetuous -▁illustration -▁motives -ats -atical -ashka -ukashka -▁considerations -achus -achusett -▁massachusett -▁viv -▁vag -▁eminence -▁fathom -▁conservative -▁subsequent -▁barbarous -aneous -▁carroll -▁inevitable -▁exists -sp -▁gosp -▁gospel -ingham -▁tortured -▁pictured -▁displeasure -▁lobster -▁recoil -▁mutton -▁fritz -vant -advant -▁disadvant -▁equival -▁forego -▁knit -▁blanch -▁honors -▁precautions -▁precaution -▁liable -▁testament -fare -▁vigour -cle -▁dup -▁proclam -▁durrance -▁tack -▁crab -▁searched -ula -▁bravely -▁namely -▁bum -▁bumble -▁deliberate -▁logical -combe -▁morn -net -▁sheet -▁lifet -▁lifetime -▁ledge -▁jewell -▁revolutionary -▁premises -▁burd -▁exterior -▁joining -▁academy -▁random -▁hinted -▁tink -▁sprinkle -▁females -▁tastes -▁oz -▁missionary -▁elevation -▁constructed -cloth -▁creator -▁gem -▁freight -▁fret -▁pert -try -olutions -▁nourish -▁teachers -▁inclinations -▁filling -▁ego -▁fi -▁fiend -▁perturb -▁possessions -▁humility -▁santa -▁stur -▁sturdy -iph -▁caliph -berries -▁imagining -▁miraculous -▁haunted -▁stamped -▁snatched -▁covering -▁penetrated -aylor -▁taylor -ushroom -otten -▁cripp -▁benefact -▁hundreds -▁currents -▁cliffs -▁reverend -▁existing -▁sailing -▁sails -▁chant -▁lil -▁meek -▁sleigh -▁husk -awning -▁tuf -▁ropes -▁sarc -▁whirlw -▁whirlwind -▁pleases -▁diame -▁cylinder -▁nostril -▁tribun -▁lucian -▁buffalo -▁guy -▁apprehended -▁apprehensions -▁pearls -▁remov -▁freckles -▁hart -▁doris -sis -▁analysis -▁hypothesis -▁cris -▁crisis -ris -▁fab -▁fabric -cent -▁patent -aware -▁raff -▁lilies -▁laden -▁natives -▁data -▁duel -▁enquir -▁ungrate -▁we'd -throp -▁beauties -long -▁majestic -▁respectful -▁merciful -▁puss -▁godfrey -▁coop -▁urgent -▁duly -▁wife's -▁refusal -▁constern -ball -▁contribute -▁attribute -▁restaurant -▁fry -▁voluntary -▁unex -ias -iary -▁starlight -▁oxen -▁dot -▁betwixt -▁illusion -▁exclusion -auchamp -▁speaker -▁cushions -▁knitting -▁creeping -ibilities -▁rubbish -irect -▁indirect -▁attentions -▁infall -▁insignificant -▁balc -▁balcony -theus -▁sweethe -▁sweetheart -▁anthe -▁anthea -▁consists -▁spy -▁divinity -▁lawrence -▁merriment -▁merrick -▁click -▁vanqu -▁vanquished -▁persecution -▁contriv -▁harrington -urance -▁recur -▁disappearance -▁artists -▁exchanged -omwell -▁cromwell -▁regularly -▁solely -▁litter -▁psychology -bishop -▁archbishop -oki -▁pok -ukoki -▁severity -▁picking -▁locks -swers -▁answers -▁milburgh -▁diameter -▁mammy -▁cyrus -uminous -▁luminous -▁blake -▁navy -mmy -ys -▁abys -▁metaphys -▁goods -▁symptoms -▁gets -▁junction -▁fist -▁unmist -▁unmistak -▁unchang -ama -▁charley -▁eastward -▁inward -▁headlong -▁undergo -▁plato -▁imprisonment -▁insolent -onathan -▁jonathan -▁nathan -▁latch -▁onward -▁scrib -▁scour -▁errors -▁preservation -▁rotten -▁ethel -▁sticks -utomobile -▁necessities -ocious -▁ferocious -▁nails -▁grows -▁advers -▁adversary -▁horsemen -▁deepest -▁insurrection -▁devices -▁poets -▁propriety -▁magnificence -▁olive -▁nobles -▁arrows -▁spaniards -▁helmet -▁packet -lessly -▁pretex -▁flowed -ossibility -▁impossibility -▁spending -▁ranks -▁vapour -▁witnesses -▁calamity -▁rapidity -▁ungrateful -▁toler -▁harder -▁charges -▁clum -▁clumsy -ounced -▁barbara -▁feud -▁cheval -▁chevalier -▁portions -▁utility -▁cha -▁lee -▁socrates -▁ravine -▁survived -▁ornaments -▁barber -▁text -▁curt -erville -uv -▁associations -uzov -utuzov -▁antiquity -erous -▁treacherous -▁immaterial -▁exile -▁manager -▁continuous -▁unique -▁stunned -▁economy -▁bleed -roll -▁identical -houn -▁calhoun -▁coun -▁nights -▁gidd -▁activities -▁sinc -▁sincerely -▁alien -▁frighten -▁eloquent -▁lun -▁buckingham -▁opera -▁operator -▁imprud -▁affl -▁affliction -▁fiction -▁fol -▁foli -▁foliage -▁fouquet -▁faire -▁fairest -▁annoyed -▁yawn -▁pont -▁pouring -▁wherein -▁nobleman -▁interruption -▁overtake -▁maker -▁sincer -▁sincerity -▁chickens -▁peb -▁pebbles -▁resting -▁annual -▁inviting -▁thwart -▁blows -▁blowing -▁brains -▁moust -▁moustache -wr -writing -▁dragging -▁praises -▁doc -▁document -▁documents -▁dipped -▁dizz -▁perished -▁engines -▁adding -▁prophe -▁prophecy -time -▁dolls -▁lang -▁lic -▁lukashka -▁delir -▁delusion -▁recourse -▁prostrate -▁gibb -▁despond -odor -yodor -▁fyodor -▁fyodorovitch -▁deput -olate -ocolate -▁betro -▁betrothed -▁morcer -▁morcerf -▁morris -▁politely -▁confided -▁uncover -erving -▁abide -▁stretches -▁javer -▁javert -ivable -ctic -▁beds -▁rivers -▁drama -▁dora -▁promote -▁hannah -▁grasped -tally -▁digging -▁mantle -▁dissatisfied -berg -▁arkady -▁arkadyev -▁ransom -▁montg -▁montgom -▁montgomery -▁correction -▁advances -▁injustice -▁naughty -▁enthusiast -anda -iana -▁sab -▁mercury -▁contains -▁transact -▁libert -▁muffled -▁liking -▁enjoying -▁greeting -▁eth -▁entreated -▁airship -▁neighbours -▁vices -▁blessings -raged -mitri -▁dmitri -▁hue -▁revenue -▁inexpress -▁inexplic -▁implic -▁filth -▁depression -▁conquered -▁darted -▁crooked -▁columbia -▁realised -itations -▁forests -▁grounds -▁convuls -▁tasted -▁ecclesiast -▁ecclesiastical -▁involve -▁feels -▁usurp -▁patric -▁baptism -▁circulation -▁circus -▁mow -castle -▁sorrowful -▁vass -▁injure -▁chaos -▁supple -▁exhort -heard -▁alph -▁alphab -▁alphabet -ustria -▁brings -▁spaces -▁spenc -▁ej -▁ejac -▁ejaculated -jah -▁bravery -▁stables -▁enchanted -ider -▁panic -▁purity -▁vegetation -▁revelation -▁zadig -▁elph -▁orph -▁soph -▁pretence -▁agriculture -cessantly -▁millionaire -▁tough -▁syd -▁sydney -▁lecoq -▁simplest -▁enraged -▁magnet -selfish -▁unselfish -▁unbroken -▁bog -aux -▁carn -▁available -▁quivering -▁treaty -▁strokes -▁shady -pire -▁slack -▁transgress -▁countless -▁counted -▁victorious -▁victoria -▁plains -▁lady's -▁exposure -▁ounce -▁alternately -▁wounds -▁parr -▁parrot -▁conventional -▁prettiest -▁vinc -▁vincent -ties -▁kutuzov -▁bount -▁console -▁inexper -▁luxuries -▁mound -hound -▁redeem -▁salary -▁flask -▁sobs -▁rot -▁sensibility -vory -▁gust -▁redoubled -▁missed -▁amazed -far -▁whar -▁preference -▁carav -zan -▁tarzan -▁pennsyl -▁pennsylvan -▁pennsylvania -▁imitate -▁concess -▁foundation -▁heavier -▁vaguely -hor -▁abhor -iah -▁proprietor -▁include -▁negroes -inda -▁trumpets -▁screamed -▁dreaded -▁sally -▁si -▁laure -▁manufacture -▁sculpture -▁wildly -▁bells -▁balls -rimony -▁matrimony -▁pigeon -▁francisco -▁remo -▁removing -onesome -▁lonesome -▁wanting -▁baked -irk -▁lever -▁leop -▁whiteness -▁seventeenth -▁diet -▁civility -▁superstitious -istinct -▁indistinct -▁colbert -▁stew -▁chocolate -soever -▁rese -▁perilous -▁purit -▁remn -▁diversion -▁clutching -▁confinement -▁slide -▁hospitable -▁humming -▁bags -▁precept -▁milan -▁glee -rie -▁elaps -▁pierced -▁warfare -▁vind -▁vow -▁vamp -▁pamp -▁chip -▁mall -▁mull -▁midd -▁sung -▁guar -▁guarant -▁lew -▁lewis -▁kl -▁fult -▁fulton -▁passive -▁persisted -▁brother's -▁fifteenth -▁compound -▁compens -▁compart -▁sandford -ingale -▁nightingale -▁cling -▁clinging -▁begging -▁laying -oneliness -▁loneliness -▁proudly -▁principally -▁impelled -▁embracing -▁memorable -▁tint -▁vexed -▁vexation -▁elderly -▁winterbourne -▁parker -▁brist -iston -▁conniston -▁glist -▁partial -▁infinitely -▁ounc -▁toby -oughby -▁landing -▁inspection -▁pork -▁curs -▁cursed -▁carbon -▁mushroom -▁simpson -istry -▁um -▁ticket -▁ast -▁rosamond -▁dimly -▁briskly -▁unlucky -gans -▁organs -▁repro -▁replies -▁seemingly -ifice -▁precipice -▁magnitude -▁angles -▁alfred -▁mars -▁boy's -▁glimpses -▁momentary -▁burton -▁fut -▁brightly -▁jenks -▁nought -ieu -▁adieu -ie's -▁adap -▁adapted -▁naval -▁situations -▁chemical -▁typical -▁arts -▁armor -▁trades -▁almah -▁cruc -▁appreciation -▁noisy -▁here's -anning -▁channing -usions -▁scared -▁martian -any -▁moham -▁mohamm -▁exal -▁exalted -▁moor -▁mole -▁moth -▁sealed -▁duncan -▁courtes -▁courtesy -▁unexpectedly -▁antip -▁miner -▁mexico -▁mexican -▁dispers -▁claw -o's -▁seats -▁enlightened -▁keys -▁dimensions -▁pretensions -▁producing -▁revolutions -▁urb -▁mortals -tery -bray -▁emer -▁emerald -▁proving -▁impass -ocity -▁gradual -▁precision -aday -▁nowaday -▁hebrew -quiet -▁sembl -▁ferdin -▁smashed -▁skirm -ometer -▁jug -▁eyebrows -▁agricultural -▁blink -▁opponent -▁mack -bourg -▁impost -▁disinterest -▁eames -icate -▁steadf -▁steadfast -▁despot -▁canoe -oor -ooroo -▁sanctuary -▁blaze -▁blazing -▁adel -ycle -▁sham -▁thirsty -inald -▁reginald -▁sixteenth -▁profitable -▁repet -▁repetition -ancourt -▁valancourt -▁baff -▁butler -▁lungs -▁swords -▁shrunk -▁distances -▁gravel -▁lime -▁territor -▁pitiless -▁popul -▁populace -▁denis -▁denial -▁martial -▁cordially -▁opponents -▁comte -▁squad -▁squadron -▁ruby -▁unpre -▁unaccustom -▁snapping -▁obstruct -gone -▁humphrey -▁loveliness -▁muzz -▁astronom -▁cel -▁celest -▁celestial -▁code -▁gretel -▁shades -osis -▁superfl -▁superfluous -▁wald -▁sprinkled -ardier -▁thenardier -▁headed -▁governed -▁physi -▁physically -▁ped -parable -▁pierce -▁environ -▁alexandrovna -▁breadth -▁carth -▁beth -areth -▁appare -▁corinth -▁lugg -▁luggage -▁courses -▁insolence -▁elphick -▁benevolence -▁eury -▁discrim -▁robinson -▁exhaustion -▁pupp -▁laurel -sitate -▁hesitate -olooroo -▁boolooroo -▁excellence -▁afforded -▁hurst -▁hurstwood -print -▁clue -▁twinkling -▁salmon -▁nodding -▁florid -▁explanations -▁badger -▁dome -▁statues -▁extinct -▁muscle -▁merrily -▁oppression -ustralia -rial -▁burial -▁bure -▁bureau -▁valet -wealth -▁poems -▁neatly -▁horns -▁deleg -▁keenly -▁mord -▁mordaunt -▁locality -▁crumbs -▁sports -▁trop -▁penetrate -▁penetrating -▁rifles -▁hopped -▁treasures -▁lounge -▁accuse -▁explosion -▁deplor -▁verge -▁mentally -▁cul -▁scottish -'n't -▁associates -▁routine -keeper -▁purchased -▁slopes -▁rattled -▁rattling -▁livid -▁ens -▁holiness -▁sterling -thouse -▁eighteenth -▁deserving -▁preserving -▁malady -utomatic -▁flourish -▁dreaming -▁frig -▁frances -▁nathaniel -▁rigg -▁rup -▁maud -▁mockery -▁slippery -▁bery -▁beryl -▁baz -azed -mates -▁gotten -▁pavlovna -▁stretching -▁sentimental -▁marshal -▁horribly -▁resolutely -thos -▁confidently -▁trapp -▁cabb -▁workman -▁appreciated -▁daybreak -▁void -▁voluntarily -▁temporarily -▁properties -▁attendance -▁trifles -▁guich -idious -▁pious -▁paws -▁poster -▁hog -▁technical -▁beseech -▁barrister -▁accurate -▁accuracy -▁accurately -▁fragment -▁elevated -▁gould -▁pupils -▁torrents -▁triumphantly -▁porridge -▁abilities -abilities -▁reports -▁inconsist -angled -▁fishes -escent -▁abyss -▁finds -▁regards -▁pinch -affold -▁scaffold -▁traff -▁traffic -▁utterance -▁overcoat -▁unreasonable -raved -lesome -▁jaw -▁ivanovna -▁crev -▁av -▁exerted -verted -▁futile -▁banners -▁factories -▁interchang -▁stealing -▁fidd -▁obligation -▁societies -escrib -▁indescrib -cribe -▁indescribable -▁witches -▁wits -▁hazel -▁obliter -▁rags -▁printing -▁merlin -▁insane -▁heroes -folk -wall -wig -▁kenwig -▁epis -▁episode -▁disclosed -question -▁unquestion -▁florent -▁soothing -vis -▁jarvis -▁rumour -▁malicious -▁warn't -▁roaring -▁burdens -▁solicitude -▁largely -▁based -▁basil -▁virgil -ropol -▁metropol -▁vrain -sieged -▁kenne -▁kenneth -▁troy -anna -▁tyrann -▁clown -e's -▁injuries -▁astray -▁coil -iles -▁prices -▁gill -▁inventor -▁invented -▁discussed -▁dungeon -▁identified -▁resignation -▁benevolent -▁superb -▁emergency -▁emerged -▁stepping -▁overheard -ios -▁semi -▁curved -▁staying -▁leads -▁tells -▁busily -ubb -▁ignomin -▁conspiracy -▁subsided -▁modesty -▁daisy -roar -▁uproar -▁extension -alm -▁rouletab -▁rouletabille -▁cultivation -▁reins -▁condens -▁texture -▁wilm -▁velocity -last -▁everlast -▁sobbed -▁sobbing -frican -an's -▁tube -▁tolerably -▁miserably -▁poles -▁antique -▁perpend -▁perpendicular -▁ferdinand -▁camel -▁mortim -▁excluded -▁starving -▁klaus -▁earnestness -▁clarence -held -▁ceaseless -rupted -▁bennet -▁chart -▁controversy -ipsy -▁gipsy -▁waken -▁strove -▁partners -▁partially -▁odor -▁sovereignty -▁wy -▁englishmen -▁masculine -▁tracks -▁wrigg -ustra -athustra -arathustra -▁zarathustra -▁width -restrial -▁ringing -▁rejoicing -▁cooking -▁electricity -▁particip -▁municip -▁confederate -▁burrow -▁overboard -▁spectator -▁maur -▁maurice -▁hagg -▁haggard -▁agg -▁michel -▁deference -▁ministers -▁exclusive -pool -▁liverpool -▁tops -▁inference -▁alongside -▁suppressed -▁tooth -▁pans -▁paw -▁cage -▁cutter -umberland -▁dispose -▁removal -ocation -▁pavlovitch -▁coin -▁grinning -▁bred -▁hermit -▁racing -▁crick -▁crude -rude -ertrude -▁gertrude -▁gord -▁na -▁commissioner -▁personage -▁accounts -apart -▁bonapart -▁grap -▁grapes -▁groc -ads -▁nuts -▁boh -▁bohem -▁conferred -▁snar -▁reluctance -▁reluctant -▁cant -▁reflections -▁license -▁stifled -▁berg -uniary -▁blamed -peak -▁pleasantly -▁davis -▁plight -▁gwend -▁gwendolen -ways -▁materials -▁divers -▁photop -▁photoplay -▁spots -▁claims -inster -▁associate -▁embarrassed -▁assisted -▁choked -▁lorry -▁rosal -▁endurance -▁schemes -▁child's -▁audacity -▁chooses -ulent -▁cogn -▁fruits -▁bills -▁expressive -▁mim -▁inconce -▁inconceivable -▁joyce -ridges -oux -▁compress -▁cec -▁alc -▁alp -▁fitzp -▁fitzpiers -▁hump -ha -▁paras -▁armstrong -▁cows -forward -▁trotted -▁arkadyevitch -▁refine -▁guarded -▁turkish -▁claimed -▁aggress -▁isabella -▁garland -▁morland -▁shah -▁providing -▁larry -▁presents -▁livery -▁disadvantage -▁belg -▁revolt -▁communities -▁communion -▁unpr -▁mumm -child -wright -▁loveliest -▁perchance -▁stress -▁undress -thest -arin -▁mazarin -▁mathematical -▁barrack -amel -▁interfered -▁bedside -▁invaded -▁degraded -▁dul -▁facility -▁vanish -▁aught -▁reassured -▁elastic -▁dispens -▁sill -▁knob -▁assigned -▁imaginative -▁owed -▁yach -▁yer -▁ominous -▁omitted -▁industrious -▁invas -▁invasion -▁environment -lessness -▁everlasting -thetic -▁susceptible -▁dinners -▁cater -▁caterp -▁caterpill -oir -▁sho -▁veter -▁drawer -▁molly -▁magnanim -▁deliverance -▁whereby -▁presby -▁subordinate -▁excessively -▁sioux -▁dand -▁commotion -▁patted -▁adorned -▁ferry -▁abject -▁cowboy -▁orphan -▁sciss -▁scissors -▁terrors -▁suggestions -incial -▁provincial -▁bathing -lorn -▁tangled -▁morsel -▁preoccup -▁phase -sip -ssip -▁gossip -yrinth -▁labyrinth -▁leopold -▁paste -▁plane -▁brun -▁programm -▁monastery -▁boff -▁boffin -▁relent -▁dissolved -▁gleaming -▁acquies -▁earthly -▁damon -▁retreating -▁sidney -▁elegance -▁international -▁mines -▁ministry -▁vicious -▁cornwall -▁replace -▁wholesome -olley -▁poll -▁perfume -▁deme -▁demean -▁demeanour -▁pathetic -▁unimp -▁dusty -▁slice -rice -fallen -leby -▁nickleby -▁unity -▁elmwood -▁prowess -▁colours -▁catastrophe -▁pew -▁berwin -▁warmly -alled -▁apert -▁aperture -▁cert -▁certific -▁gaw -▁gawaine -▁readers -▁denom -▁darkened -cile -▁advertisement -▁prescribed -▁restoration -▁luxurious -▁equipment -▁compartment -▁whatsoever -uart -▁stuart -uing -▁kissing -▁panting -ta -▁benches -▁shocked -▁ded -▁conveyed -▁blacksmith -▁footprint -▁monica -▁mountjoy -▁aspir -▁bleak -▁rebel -▁pel -▁pic -▁frivolous -▁entry -▁staggered -▁piled -▁piles -▁mayor -cuniary -▁pecuniary -▁leaping -▁sump -▁joyful -▁handful -▁eminently -▁lustre -▁shattered -▁protected -▁bears -icable -▁relatives -▁delirium -▁renounce -▁chopin -▁joyfully -▁adjac -▁adjacent -▁snail -▁mung -▁endeavoured -▁explore -▁tropical -▁enquired -▁deceit -▁mischievous -▁attempts -itors -ita -▁vit -▁dunde -▁sorcer -▁proclamation -▁posterity -▁commiss -norm -▁abnorm -▁fraud -sky -▁glistening -▁accost -▁bast -▁stillness -▁fantine -▁accidentally -▁rugg -▁jerome -▁pitt -urst -hurst -▁primary -▁waz -▁wazir -▁ecl -▁peach -▁babe -▁fleece -▁uncertainty -▁rights -▁suic -▁suicide -▁hubert -▁dazed -▁partic -▁particles -▁transmitted -onn -▁referring -▁expectations -aria -engaria -▁berengaria -▁canada -▁hoist -uber -▁squeeze -▁muzzle -▁whispering -icycle -▁baking -▁ba -▁bass -▁drill -▁footman -▁tents -▁resurrection -▁erected -▁elected -▁bicycle -▁heir -▁encir -▁encircl -▁burgund -▁kitten -▁baza -▁bazaar -▁wisest -▁vile -▁exaggerated -li -▁operate -uate -▁debate -ua -aitor -▁traitor -groom -▁bridegroom -▁trigger -▁truck -aska -▁cask -▁popp -▁roared -▁finer -▁bout -▁overwhelmed -▁karl -▁wield -▁swoon -▁arises -▁crawling -▁maxwell -▁keeps -▁nem -▁outstretch -▁gracefully -▁generously -▁judging -▁gyp -▁reel -▁nast -▁bleeding -▁intensely -▁dorothe -▁lords -▁groaned -▁robbers -▁improvements -▁adequate -▁birch -▁degen -▁clamber -▁selfishness -▁wickedness -▁cavalc -osc -▁transc -▁transcend -▁offend -▁aristot -rich -▁jewish -▁smugg -▁abominable -▁spont -▁spontaneous -▁indeb -▁indebted -▁magd -▁magdal -▁magdalen -▁conceptions -▁faults -▁balanced -▁presbyter -▁mattress -▁apology -ken -▁repay -▁doomed -burst -▁flashing -▁medicines -▁lizard -▁involves -▁strata -▁teaspoonful -▁picn -▁biograph -▁incom -▁kins -▁lads -▁candidate -▁persistent -▁arctic -▁arrog -▁switz -vy -▁strained -▁corpore -▁constrained -▁multitudes -vidrig -▁emphatic -▁vouch -▁envious -▁cit -▁ferocity -▁evidences -▁margin -▁baronet -▁lava -▁serpents -con -afe -▁dominion -▁planch -▁planchet -'er -▁ne'er -▁accumulated -fax -▁guarantee -ghai -▁glaciers -abouts -rogen -▁equivalent -▁ample -▁terry -▁thump -tered -▁noblest -▁capit -▁spraw -▁consternation -▁woven -▁aven -▁avenge -▁gau -▁eleventh -▁shutting -▁interrupting -▁nasty -▁stormy -▁humiliation -rift -▁comprehended -▁slugg -▁thickness -▁greatness -▁spoonful -▁importun -▁signifies -▁determining -gate -▁petrov -▁cob -itous -▁milit -▁tempting -▁righteousness -inguished -▁extinguished -▁letty -▁philosophical -▁schooner -▁predomin -on's -▁impl -▁springs -▁beatrice -▁romola -▁witnessed -▁chivalry -▁unsatis -dale -▁phine -▁phineas -▁costly -▁scept -ceptibly -▁sic -▁sicily -▁sage -▁saith -▁immortality -quiring -▁seeming -▁studying -iazz -▁piazz -uition -▁intuition -▁pleasures -▁poys -▁errant -▁harrison -down -▁herd -dained -▁chaise -▁misgiving -▁tearing -atrical -▁mukoki -▁folds -▁ecstasy -▁ty -▁jem -▁benj -▁benjam -beam -▁spacious -▁hire -▁submerg -▁ferg -▁fergus -▁submiss -▁arbitrary -▁tiss -▁tissue -▁secluded -▁slipping -▁slippers -▁varieties -▁steven -▁dwellings -▁foremost -▁melbury -▁shouts -▁travers -▁chains -ots -lo -▁sweeping -▁washing -▁chamberlain -▁lowered -▁questioned -▁confounded -▁elephants -▁orient -▁matilda -▁fulfilled -▁impressive -▁patr -▁retr -cients -▁queen's -▁devoid -▁fland -▁flanders -▁infernal -ruit -▁recruit -▁lyd -▁holds -oof -▁thereof -▁sparkled -▁paradox -▁bourge -▁bourgeois -▁kil -▁escaping -▁pigs -▁barred -▁cod -▁finn -▁darn -yce -▁bryce -▁produces -▁hawkins -▁envoy -▁frenzy -▁damasc -▁lain -▁hostility -▁unsucc -▁unsuccess -▁unspeak -▁presumption -▁councill -▁talb -▁talbot -▁arb -▁aris -▁arisen -▁retained -▁bullets -▁resolutions -▁shark -▁encroach -▁oppressive -▁oppressed -▁broom -▁brink -▁broth -▁evans -▁chased -▁apoll -▁ape -▁resume -▁aloof -▁sacr -▁sacrament -▁heav'n -▁tru -▁logic -▁charms -▁reflex -▁crater -▁mirrors -▁actors -▁beings -▁perv -▁bertucc -▁openly -▁monotony -▁bower -▁daughter's -y's -stood -▁misunderstood -▁russians -▁digest -▁spider -▁bewild -▁stooping -▁infam -▁beam -unition -munition -▁ammunition -▁columns -▁dishonest -▁dishonour -▁clamour -eries -▁galleries -▁energies -▁passenger -▁refreshment -▁spreading -▁helping -▁fanciful -▁diligence -▁highland -▁starve -▁solve -▁solved -▁tutor -thentic -▁feeding -▁rouse -▁hateful -▁bard -▁packard -lymp -▁olymp -▁emigr -▁skies -▁persuasion -▁bucket -▁titles -▁greyhound -▁grimly -▁depr -▁deprive -▁ramp -▁meditation -▁breez -▁firmness -▁brightness -hear -▁jumping -▁cork -▁humb -▁domain -▁rapt -▁delivery -▁usher -▁culpr -▁flourishing -▁reduce -▁dividing -▁dearly -udoff -ludoff -hludoff -khludoff -▁nekhludoff -▁pamph -▁abolition -▁whites -▁thanksgiving -▁outlines -▁outlaw -▁champag -▁champagne -▁depre -▁depict -▁treating -▁enchantment -▁hatter -▁hatteras -▁boughs -▁applies -▁messengers -▁perspiration -▁speculation -▁tebr -▁tebrick -phem -▁refusing -▁clearing -▁declaring -▁backwards -▁stealthily -▁subsequently -▁arc -▁arising -▁advertising -▁fatigued -▁garb -▁withered -▁sedley -itative -▁saxon -▁nib -ieces -▁successive -▁strive -ulture -▁gateway -▁marching -▁chaplain -▁gloomily -▁luckily -▁oriental -▁guid -▁guidance -▁overtaken -▁weaken -▁apprent -▁neighboring -▁curves -▁clapping -▁mortification -▁asylum -▁screech -izzard -▁sylvie -▁haired -▁chim -▁piercing -▁preceded -▁adams -▁ruled -▁rulers -▁nemo -▁undist -▁undisturb -ologist -▁afflicted -▁reckoned -▁detected -▁wan -▁famine -▁haze -etically -▁complained -▁remembering -ien -▁serf -▁sift -▁sall -▁engagements -▁jefferson -▁envied -agers -▁villagers -▁hag -▁pines -▁deliberation -▁formation -▁forming -▁plantation -▁entangled -▁injunction -▁distressed -▁spoiled -▁resigned -▁exemp -adian -▁canadian -▁flavor -▁sap -▁incor -▁regulations -▁voyages -▁stages -▁cottages -▁stature -▁seclusion -▁postp -▁records -▁crimin -▁criminals -oz -▁tri -▁bustle -▁clenched -▁shadowy -▁wester -akem -▁wakem -▁stragg -▁strat -▁occupations -▁therewith -▁handwriting -▁wilf -▁negl -▁neglig -▁pend -▁robbery -▁ohio -▁witchcraft -▁laud -▁dow -▁tries -▁aristotle -▁hipp -▁bows -▁og -▁consulted -▁slices -▁baffled -▁strap -▁schools -▁semblance -▁buoy -▁preval -uph -▁refinement -▁kneeling -▁genial -▁genie -▁yea -▁stony -thony -rat -▁ingrat -lestick -▁texas -▁perspect -▁visibly -▁propag -▁penit -ya -▁sonya -▁shovel -▁censure -▁anxieties -▁tapped -▁tatter -▁curls -▁expressing -▁monuments -▁monton -▁montoni -▁linton -▁shuddered -▁vitality -▁wegg -▁earning -▁planning -▁pa -▁paup -upp -▁draft -▁graces -▁votes -▁doctor's -▁coldly -▁gascoy -▁saluted -▁offensive -▁lavish -▁objections -ammed -ols -▁tools -annie -edges -▁marrying -▁enact -▁forefather -▁brim -▁explod -▁embod -▁jur -▁penrod -nikov -▁brill -▁preposter -▁wasted -▁huddled -▁resembled -▁renders -▁perseverance -▁writhing -▁attending -▁beggars -▁colleagues -▁harrow -▁furrow -▁leapt -ghthouse -▁lighthouse -▁anglo -▁carleton -▁receipt -▁rhyme -▁honestly -▁nik -▁ship's -▁iceland -▁contemplated -▁hunters -▁customs -▁commands -▁aggrav -stones -ste -▁barry -ufton -▁lufton -▁lance -▁settlers -quois -roquois -▁privileged -▁rampart -▁apiece -▁candlestick -▁forbidd -▁enemy's -▁pitcher -▁greedy -otting -▁mott -▁munch -▁foes -▁patches -▁wharf -▁croc -▁crocod -▁cramp -▁crut -▁divisions -▁comprehensive -▁rejected -▁nicely -▁demonstration -▁acceptance -▁balin -▁violin -▁lor -▁strolled -▁pitied -▁punctual -▁subter -▁subterranean -▁nether -▁teres -▁teresa -▁terrestrial -▁util -▁excuses -▁patriarch -▁tails -▁proposals -▁bridget -▁commanding -▁continues -▁vagab -▁vagabond -▁soap -▁kh -▁benjamin -thin -▁elin -▁elinor -▁nimble -▁awhile -▁hounds -▁donald -▁stitch -▁pillows -▁hearty -▁denz -▁denzil -▁abstain -▁wireless -▁dissol -▁allowance -onial -ential -▁consistent -yon -▁canyon -▁semyon -▁blur -▁injur -urable -▁ebb -▁survive -▁sandwich -▁morb -▁morbid -▁touches -▁sangu -▁retir -▁homer -▁unint -▁walks -▁loos -▁loosened -▁rosa -▁repast -▁dazzled -▁nett -▁enforced -▁thereafter -stein -agin -▁timidly -▁appeals -▁nostrils -oultry -▁vigilance -pole -▁supplic -▁rehear -▁countrymen -▁taxes -▁smash -▁bail -▁carthage -▁florentines -▁insens -▁watchful -▁mantel -▁admirably -▁admiring -▁untouch -▁targ -▁mournfully -▁imprisoned -▁earned -▁heroine -▁convert -▁bertuccio -▁han -▁rusty -▁scales -▁attributes -▁attributed -▁lydia -▁exalt -▁chil -▁chiltern -▁platter -▁plaus -cknowledg -agr -▁walnut -▁skiff -▁bailiff -▁courier -▁vines -▁spasm -▁thigh -▁pursuing -▁waiter -▁glenc -▁immeas -▁snob -▁snort -▁cavalier -▁esteemed -▁indication -▁commonwealth -▁rector -▁scope -escope -oscope -escence -▁impudence -elle -itchell -▁mitchell -▁rhe -▁rheum -▁interpre -▁suffolk -▁prolonged -▁coral -▁crawl -▁crawley -▁willoughby -▁reigned -▁responsibilities -▁kenn -▁peac -▁peacock -▁sarcastic -▁sarcasm -▁nora -▁cherish -▁plaintive -cibly -▁disconcerted -▁confederacy -▁reap -▁gloss -▁apparel -▁morton -▁venet -▁unintell -▁hurric -▁georgia -apse -verse -▁wolver -ouver -▁enforce -fellow -▁belinda -▁improper -▁graciously -leans -▁orleans -▁hurricane -▁inch -▁mature -▁premature -vidrigail -▁coals -▁alliance -▁brazen -▁ul -▁hinder -abbath -▁sabbath -▁potential -▁chaff -ees -▁flirt -▁continuing -▁evap -▁evapor -▁maturity -▁distemp -akov -▁perplexed -▁blushed -▁blushing -▁pite -▁gascoyne -▁illeg -▁truce -▁spruce -▁smoked -▁peculiarly -▁stack -▁smack -▁rak -▁riddle -▁arn -▁nowadays -▁obligations -▁oblige -iations -▁negotiations -▁guiche -▁silverbridge -elf -▁oneself -▁suite -▁faltered -oral -▁whal -quar -quarters -▁headquarters -▁mosqu -▁unaccountable -▁pinched -▁drained -▁tyrants -▁approb -▁approbation -▁approving -gged -▁corporeal -▁reasonably -▁disliked -▁depressed -sday -▁thursday -▁prosecutor -▁barbic -▁barbicane -▁pavil -▁congratulations -▁piety -▁amendment -▁descendants -▁figured -▁straightway -▁brigand -▁congregation -unciation -▁hoofs -sitating -▁hesitating -▁raz -▁runs -▁gliding -▁choir -▁lamps -▁discharged -iard -▁billiard -▁concur -▁concili -▁desperation -▁respects -▁prohibition -▁fourteenth -▁pell -ancouver -▁signify -▁signor -▁rendez -▁hens -▁manhood -▁boyhood -▁shilling -▁amazing -▁parting -▁saunders -▁separately -▁grop -▁widely -▁reed -▁guineas -▁migr -▁pod -▁juris -▁moderation -▁target -witch -▁secrets -▁undergone -▁neighbouring -▁client -▁claver -▁unequal -▁unmoved -▁discussing -▁tame -▁inhabited -▁propens -▁dejah -▁nourishment -▁concealment -▁seconds -▁shack -▁pulpit -ras -▁casting -▁chipm -▁chipmunk -▁chunk -▁flann -▁flannel -▁psychological -▁floors -▁anniver -▁farthest -chus -▁tab -▁lots -▁chasm -▁flavour -▁flown -▁forbade -▁memorial -▁coolness -▁deduction -▁kittens -▁unav -▁unavoid -ana -lift -▁torches -▁prince's -▁warlike -vidrigailov -▁borough -awares -mons -▁sermons -▁exertions -▁charco -▁resembling -unct -▁sumpt -▁sumptuous -▁dealt -▁bland -▁sized -▁shallow -▁bibl -▁wilfrid -▁demons -▁gurg -▁coroner -▁coronel -▁tel -▁telescope -▁tess -▁tons -eer -▁pretext -▁reflecting -▁causing -▁lull -▁repuls -▁deserves -▁trav -▁refract -drop -▁tickets -▁proclaimed -wreck -▁shipwreck -▁ours -▁legislation -▁navigation -yrcan -▁happening -ulsion -▁sluggish -▁thurston -iors -▁transformed -▁warden -▁wardro -▁wardrobe -▁mysteriously -inqu -▁relinqu -▁yacht -▁fib -▁fuzz -▁victims -▁unconc -▁uncomp -▁snug -▁worthless -▁senor -▁intrins -▁significantly -▁kennel -▁bait -▁prizes -▁moll -▁foll -▁fiddle -▁upwards -▁acquiring -▁inexplicable -▁accompaniment -urer -▁treasurer -idity -▁stupidity -▁minded -▁gardener -iresome -▁tiresome -▁reasoning -▁benny -▁unintellig -▁serenity -▁assuming -▁payment -▁whig -anthrop -▁philanthrop -ront -mont -▁nel -▁nelson -▁commencement -▁captiv -▁knocking -pence -elp -▁alighted -▁climax -▁fists -▁duc -▁fortified -▁constituents -▁couns -▁counsell -▁margaret's -▁devast -▁prohibited -▁totally -lar -▁recorded -▁baroness -▁corresponding -▁augustus -usive -▁answering -▁recomp -▁prophets -▁vainly -▁fortitude -▁stacy -▁supremacy -▁mostly -▁day's -'r -▁sewing -▁varying -▁imperturb -▁crocodile -orious -▁outlaws -▁outlined -▁bathe -▁forgetfulness -▁uttering -▁corral -▁qualified -▁enters -▁linley -▁embroidery -▁copies -mate -▁visiting -▁visits -▁insight -▁blight -▁cabbage -▁bored -ado -▁palp -▁sneered -▁appetites -▁ulys -▁ulysses -▁kicked -▁evinc -▁travilla -▁indications -▁intimately -▁assassin -▁cy -▁cycl -▁princesses -▁partition -▁intricate -▁arbaces -udged -▁panther -▁maloney -▁duplic -▁relay -▁tito -▁eaves -▁egyptians -▁hello -▁tricks -unuch -▁eunuch -▁saun -▁redd -▁reddish -▁grumbled -▁dulc -agged -▁crowned -▁shops -▁effectually -▁compose -▁complied -egiance -▁modified -▁collins -▁fetched -anned -▁expose -▁export -▁contemplate -▁rochester -oso -▁whiskey -rea -rep -▁paula -float -▁rushes -▁prophetic -▁runaway -▁beech -etery -emetery -▁cemetery -▁jesuit -▁moore -▁rogue -▁ceremonial -▁endeavored -▁outstretched -▁hunts -▁huntsman -vard -inting -▁genev -▁cloister -uthentic -▁grudge -▁bould -▁bouch -▁baker -▁antonia -▁antony -▁terrier -▁citoy -▁bristol -▁dub -▁kirk -▁pest -▁pension -not -▁sweeter -walk -▁anthony -▁apprehensive -▁symm -▁dedic -▁galah -▁galahad -▁inconvenient -▁sunbeam -noticed -▁winding -▁tumble -▁injurious -▁requiring -▁paths -etto -▁accidental -▁cere -▁cerebr -udg -▁workers -▁yawning -▁bits -▁cosm -▁accents -tes -▁ambassadors -▁pedd -▁hoopd -▁hoopdri -▁hoopdriver -▁improb -▁improving -▁oldest -▁sweetest -▁accompl -▁disquiet -▁disin -▁exclude -ervis -▁jervis -etr -enetr -▁impenetr -▁impenetrable -▁cricket -▁carpenter -ateg -▁resounded -▁abbot -▁banish -▁midday -▁unmistakable -▁comforted -▁traversed -▁hanged -cessant -▁fools -▁monks -▁sleepy -asphem -▁blasphem -▁grease -po -point -▁horizontal -ugn -▁repugn -▁hug -▁inscription -olated -▁memoir -▁respir -▁mullig -▁levell -▁shoem -▁accusation -▁reserv -▁moderately -arried -▁unmarried -▁enterprises -▁arouse -▁obtaining -▁adult -▁insects -▁constable -▁constancy -▁imb -▁meals -▁skirmish -▁admon -▁admitting -▁whart -▁gore -▁plausible -▁polar -▁rustle -▁caravan -▁puritan -▁vick -▁jake -▁kisses -▁hap -▁borrowed -▁delaware -▁brib -▁gables -▁langmore -▁wearied -▁habitually -▁savour -▁engraved -▁hain't -acit -▁tacit -▁tumbling -▁pike -▁jenkins -auc -▁claire -inburgh -▁edinburgh -▁perverse -▁quote -▁steed -▁rheumat -▁rheumatism -iliar -▁belle -▁charlest -▁translated -▁stairway -▁ambr -▁aye -▁fanatic -eseen -▁ga -▁gaiety -▁gum -▁gush -ona -▁guinea -▁frowned -▁unhappily -▁bruised -▁nile -▁junct -▁mousquet -▁encouraging -tical -▁decency -▁peel -▁yarn -▁incarn -▁scalp -ortion -▁mold -▁raid -ologists -▁implies -▁alley -▁massac -▁cooked -▁tinder -turned -▁taunt -▁capric -▁remedies -▁humbug -▁formula -▁electrical -▁avar -▁avarice -▁supplement -▁patriotism -▁towers -▁sara -rified -▁lustrous -▁nameless -▁they'd -▁she'd -▁caterpillar -some -▁bondage -ected -▁scrambled -▁regained -▁adventurous -▁stunt -▁lass -▁embass -▁standpoint -▁hitch -ssy -▁bessy -▁hardened -▁latitude -lium -▁opium -▁precinct -▁heathen -▁elapsed -▁ted -▁tedious -▁teddy -▁ody -dyakov -▁matches -▁reaches -▁devised -ilar -▁exhilar -▁inflamm -▁promotion -amor -amorph -▁metamorph -▁conqueror -iac -ancip -▁emancip -▁visual -▁shaving -▁liberties -▁attitudes -▁forlorn -▁possesses -▁copied -▁ecclesiastic -itiveness -▁easiest -▁wales -okes -ukes -▁dishe -ec -ecd -▁gallow -▁galloway -▁clair -▁laz -▁romarin -▁absurdity -▁kentucky -▁claus -▁flem -▁flex -▁historian -▁imperious -▁tumultuous -▁coins -▁hovel -onne -▁temperance -▁smoothing -▁wyn -▁yankee -▁invitations -vert -▁statements -▁bundles -olnikov -askolnikov -▁illustrated -▁fearfully -upport -▁unheard -▁temptations -▁dusky -hum -human -▁mohammed -▁sentin -▁sentinel -▁mary's -▁hydrogen -▁charging -▁charitable -▁harass -▁discoveries -abbed -▁discard -▁signals -▁doubted -▁inspire -xim -avia -▁hedges -▁greggory -▁baltim -▁profusion -▁admiralty -▁colonial -▁scanty -▁dat -▁threaten -▁peaks -▁rebels -▁tended -▁solemnity -▁crouched -egr -▁integr -eff -▁patting -▁parsonage -▁clash -urph -▁murph -eph -hire -▁tire -▁conscientious -▁magical -▁practicable -▁practices -▁practiced -▁unob -▁aristob -▁abound -▁fellowship -▁astonishing -iature -▁miniature -▁cracked -▁singularly -▁portug -▁dove -▁holly -▁acquis -▁acquisition -▁transition -▁ivory -enuous -▁strenuous -▁meddle -▁shrubber -▁affright -▁oblique -▁creed -▁paganel -▁degener -▁spun -▁cruise -rust -▁mistrust -▁mic -ico -icing -▁sacrificing -▁noticing -▁consisting -▁beware -▁canoes -▁rites -▁uncont -▁uncontroll -▁cecily -▁captivity -▁homage -ded -▁studded -▁pitched -▁assented -▁distributed -▁anticipation -▁tolerable -tty -▁misty -▁accomplishment -▁inserted -▁sledges -▁consumed -▁toil -▁tommy -▁entreaties -▁flound -▁swine -▁inscribed -▁noises -▁missing -▁fring -▁fluttering -▁fluttered -▁obstruction -▁attic -▁attired -▁expired -▁innocently -▁terminated -▁constitutional -▁mexicans -▁ounces -▁pail -▁wooz -▁malignant -▁candy -▁honesty -▁winterborne -▁symph -▁apprehend -eline -▁representatives -▁myst -▁mystic -▁frederic -▁briar -▁recount -▁recre -armed -▁innov -▁implac -▁exceeding -▁guiding -▁gordon -requ -lier -▁combinations -▁desolation -▁arches -▁growl -▁carter -▁brake -▁philipp -▁linger -▁nobler -▁conceit -▁conceivable -▁juncture -▁shines -bell -▁campbell -▁gull -▁shivered -▁cement -▁utop -▁daunt -▁nigger -▁enveloped -▁loses -▁martians -▁busied -▁baltimore -▁mowbray -▁debray -▁indies -▁package -▁uplift -▁obstinately -▁quarry -▁alma -▁peeped -▁piazza -▁phyll -▁speechless -▁regardless -▁garn -▁coco -▁owners -▁newly -▁hillside -vention -▁intervention -▁vindict -▁translation -▁gear -▁it'll -▁nod -▁boar -iquette -▁etiquette -▁assuredly -▁resided -▁groans -▁zealous -▁seaw -▁ranch -▁analog -▁located -arine -uating -uation -acies -▁contemporary -▁raced -▁raskolnikov -▁prosecution -▁irresistibly -▁scratched -▁connections -▁enlighten -▁plateau -▁hord -▁shaded -▁bere -▁shapeless -▁ogre -▁screaming -rescent -▁bray -▁hastening -▁conductor -▁continuance -▁amazon -▁exercising -loyd -▁lloyd -▁syllable -▁disheart -▁dock -▁recital -▁janet -▁jacques -illes -▁unbelie -▁latent -▁competent -▁correspondent -▁rebellious -▁ridd -▁provocation -▁cavalcant -▁luxuriant -▁diverted -▁pestil -▁wight -▁valor -▁deposit -▁deposits -▁affectionately -▁pooh -▁thrash -▁sidewalk -▁isolated -▁swayed -▁swaying -▁designed -▁imperceptible -dam -sterdam -▁ketch -▁sash -▁wisely -adok -▁kadok -okhov -▁laboratory -▁contrive -▁intrust -▁posture -▁cigarettes -forth -▁overth -▁overthrow -arth -▁garth -▁allison -▁emotional -▁fragile -▁compos -▁promont -▁yielding -journ -▁carl -illed -▁crystall -▁populous -▁windsor -▁indol -▁spong -▁vowed -▁casually -▁legged -▁castles -▁emerge -▁gestic -▁allowing -▁yale -▁infallible -▁file -▁reared -▁yelled -▁traddles -▁conceited -▁arranging -▁bully -▁choking -▁exclamations -voc -▁hampton -▁deception -▁scornfully -▁achievements -▁accomplishments -▁colonists -▁vehem -▁vehemence -▁research -▁diction -▁recompense -▁pretense -▁tense -▁laced -▁disgraced -▁l' -▁lind -▁frontier -▁falk -▁grizz -▁constituted -▁athenians -▁lounging -▁unear -▁committing -▁lunatic -▁idleness -▁sagacity -haw -▁meditated -aterially -lethorpe -▁oglethorpe -▁tendencies -▁independently -▁indec -▁apostles -▁apostle -▁earle -▁twinkle -▁twinkled -indle -▁garral -▁unimport -▁lena -▁contend -▁spencer -enhow -▁torpenhow -▁enliven -▁barbarians -▁pilgrimage -ighthood -▁sticking -inople -▁constantinople -▁ambush -▁digress -▁owen -▁politicians -▁familiarity -▁volley -▁transaction -▁repaired -▁mace -▁dialogue -▁diary -▁engaging -▁ambig -▁enig -▁governors -▁appearing -▁venom -▁preface -▁optim -▁rakit -▁controlled -▁peterkin -▁proff -▁proffered -▁sock -▁bah -ableness -▁shameful -▁improbable -offrey -▁geoffrey -▁syria -▁loath -▁loan -▁loop -▁contemplating -▁beaver -▁instructed -uced -▁distracted -▁sheltered -▁unfit -▁git -▁gul -▁gutter -▁weird -minster -▁westminster -▁exploits -iloqu -▁brazil -aub -▁stan -▁stanley -▁sting -▁waked -▁intercept -▁twin -▁cavalcanti -ini -▁brok -▁promot -▁pars -▁parsley -▁wingfold -▁pausing -▁exposition -▁dies -▁dip -▁sep -▁diseases -▁grammont -▁grammar -court -▁cooper -▁eternally -akeney -▁blakeney -ipper -▁oysters -▁ponderous -lves -▁vancouver -▁satin -▁anew -▁athos -▁volunteered -▁cleft -▁valued -▁morrel -strel -▁minstrel -▁impertinent -▁impertinence -opper -▁arduous -▁dances -▁realise -▁trough -▁dough -▁troll -▁misc -▁tenant -▁deuce -▁bung -▁brigitt -▁undone -▁undo -▁dubl -▁declares -vin -▁bin -▁bou -cessible -accessible -ecess -▁predecess -rac -▁syrac -▁thames -▁meditative -▁corpses -▁sexes -thesis -▁baba -▁rasp -▁cherries -▁complaints -brook -▁hats -▁freak -▁smelt -▁eagles -▁peddler -▁alexey -▁securing -▁chambers -▁picnic -▁oblivion -▁lapse -▁explaining -▁unarmed -rah -▁hurrah -▁requested -▁celebrate -▁logs -▁beams -oscop -▁silken -▁considers -theast -aston -ection -▁implacable -▁epist -▁nikol -▁nikolay -▁unfair -▁collapse -▁daint -▁dens -▁calculation -▁calculations -resses -▁diminish -▁geography -▁deceiving -▁chieftain -▁secondly -▁trinity -▁pussy -▁suggestive -▁capricious -▁spied -▁sinner -▁vein -▁hysterical -▁radical -▁cust -▁condemnation -▁plays -▁seeds -▁sorrows -▁wilmot -▁blot -▁svidrigailov -▁elders -thmetic -▁cerebral -▁brend -▁brenda -▁brack -▁integrity -▁witty -▁reconcili -▁ross -▁praying -▁wherewith -▁batteries -▁raises -▁multiplic -▁corruption -▁wright -▁wrung -▁strung -▁slung -▁stung -▁vo -▁rakitin -▁dublin -▁rim -▁brilli -▁brilliancy -▁ni -book -▁nurses -▁brushed -▁solicitor -▁christians -▁magp -▁magpie -▁dispersed -▁whispers -▁substituted -▁mounting -▁using -▁longing -▁longed -▁glaring -▁friend's -▁graz -▁swiss -evik -shevik -▁inhabit -▁exempl -▁dependence -▁verb -▁version -▁veranda -▁unearth -▁genus -▁euph -▁lac -▁thorac -▁loki -▁thoris -▁servad -▁servadac -▁servitude -▁salad -▁yolk -lan -▁monarchy -▁dominions -aternity -▁intrigue -▁scarf -▁josie -▁offers -lete -▁disgusted -▁darnay -▁moves -▁deign -▁cowardice -▁paced -▁rolls -▁calamities -▁jessica -encing -▁volupt -▁tempered -▁moment's -▁shares -▁squares -aches -▁adverse -main -▁delayed -▁winged -▁detained -▁horse's -▁cros -▁crosb -▁crosbie -▁stirrup -▁glencora -▁accl -▁terrify -▁provoked -▁switzer -▁switzerland -▁executioner -▁blossoms -▁affirmative -▁clem -▁exterm -mer -azad -razad -▁shahrazad -▁abnormal -▁regulated -▁kite -▁kay -ancing -▁problems -▁bidden -▁contracted -▁giles -▁indecision -▁richly -avors -▁proven -▁combatants -▁marqu -▁marquise -▁bands -▁greeks -▁cups -▁impuls -spread -▁virgilius -▁chatting -▁cowardly -▁rely -▁pruss -▁grade -▁quoted -▁octavia -▁eb -▁ebony -▁bony -▁suffoc -▁dive -grown -▁abused -▁dante -ele -▁steele -▁steer -▁sensitiveness -▁sensual -▁eyelids -▁flourished -▁bethought -▁battal -▁imitated -lant -▁indig -▁indo -▁indoors -ero -▁mortimer -▁persistently -▁hoard -▁shrieked -▁rick -▁perempt -▁thoracic -iciency -▁johnnie -▁nanny -▁barley -▁adapt -▁crowds -▁persuading -▁fading -▁knighthood -▁ties -▁clap -okesmith -▁recept -▁nieces -▁agency -▁scraps -▁incredulous -beard -▁cars -▁indulgent -▁pud -▁pantry -▁massachusetts -▁obscured -igured -▁rook -▁reinforcements -▁soliloqu -▁rustic -▁impending -▁grandchild -▁fray -▁perkins -▁recommendation -▁maze -▁mes -▁owes -▁elf -▁disf -▁shoved -▁pillar -▁pelop -▁geome -town -inn -▁vern -▁mulligan -▁magazines -uble -▁relish -▁speaks -▁sacram -▁vase -▁da -▁taciturn -▁zero -▁halting -▁devoured -▁dismounted -▁lasted -▁naz -▁ern -▁remonstrance -▁monkeys -▁stimulated -▁concerns -▁eruption -▁eclip -▁sheets -▁repulsive -▁manufactured -aneously -▁tremendously -▁stupend -▁stupendous -▁sherwin -▁sumter -▁doubts -▁debts -iser -▁palliser -▁martine -▁martineau -▁strangest -▁gilded -▁respected -▁uncon -▁uncanny -lyn -▁evelyn -▁shaved -▁particulars -▁wonders -▁ernest -▁bursting -▁gratefully -aging -tieth -▁thirtieth -▁porcel -▁concret -▁blues -▁edged -▁edifice -▁brooding -▁arnold -▁kang -▁kangar -▁kangaroo -▁tease -▁chilly -▁dean -▁respective -▁craving -▁ancients -▁anecd -▁paved -▁essex -▁splinter -▁vicom -▁vicomte -▁estimation -▁estimated -▁confirmation -▁contemporaries -▁males -▁descriptions -▁crescent -▁exempt -▁blond -enance -aintenance -▁extravagance -▁insensibility -▁permanently -▁squat -burton -▁warburton -▁dissipated -▁southw -▁brew -uptial -▁impartial -▁guides -▁tides -▁impudent -▁spright -▁dal -maker -▁contemptuously -▁chuckle -▁affront -▁profoundly -▁waterfall -▁nightfall -▁fearing -▁starv -▁starvation -▁vacation -▁compensation -▁secular -▁rawdon -▁loosen -▁giddy -ensively -▁unbounded -ills -cknowledgment -▁conjunction -▁protector -▁beckoned -▁lacking -▁tourn -▁cro -▁apollo -▁didst -▁industries -▁puck -▁redmain -▁cultivate -▁bewilder -▁bewilderment -▁frames -▁persecuted -▁extric -▁polona -▁disadvantages -▁heights -▁ducks -▁submarine -ulus -▁aristobulus -▁grated -▁richest -▁mainly -▁inflamed -▁custody -▁abundantly -▁ozma -▁sleek -▁organism -man's -▁heaped -▁skimm -▁baw -▁springing -▁straightforward -pton -▁immeasurable -▁failing -racticable -▁peculiarity -▁disfigured -▁corridors -▁oval -▁edna -enna -▁vienna -▁poultry -▁riders -▁peers -▁allusion -▁greg -▁gregory -▁burglar -shot -▁banker -▁mouths -▁adore -▁grigg -▁confide -▁diffused -▁denisov -▁consequent -ugging -▁stumbling -▁cylind -▁rendezv -hal -▁exceptional -▁exceptionally -▁grape -▁ivanovitch -▁pane -▁sane -▁paternal -▁conversations -▁safer -▁menace -▁fidget -▁alexandria -▁compliance -▁complais -▁hyrcan -▁sorely -▁indicating -▁indicates -▁raptur -▁unsuccessful -my -▁jock -▁mell -▁mellow -▁sow -▁repeatedly -▁ignore -▁sensibly -▁voluptuous -▁insin -▁goin -▁shin -▁ron -▁ronald -▁prosecute -▁sulk -▁poach -▁gravitation -pan -▁saucepan -▁gan -▁ganem -▁thong -▁embarked -▁bubble -▁amusements -▁makers -▁quadr -▁betsy -▁indifferently -▁ardently -▁edgar -▁sliced -▁slant -▁discourage -▁wanderings -▁milner -▁prompted -▁restrained -▁judgement -▁sue -▁muskrat -▁interpreter -▁fernand -▁cigars -▁displeased -▁consul -▁consort -▁reub -▁reuben -▁brighter -rians -▁strew -▁strengthened -▁stations -▁discovering -▁smoothly -▁blizzard -olic -▁adelaid -laid -oodoo -▁trotting -▁approxim -▁proxim -▁propped -▁parasol -▁dimm -▁shimm -▁blades -▁folliot -▁phoenic -▁pansy -aract -▁cataract -fact -▁unsatisfact -▁nero -▁pregn -▁sister's -▁governing -▁grasping -bler -▁crus -▁mosquit -▁unsatisfactory -▁athen -▁buttons -oons -▁diale -▁dialect -▁subsistence -▁fastidious -▁raged -▁gleamed -▁contemptible -▁louisa -▁plea -estra -chestra -viet -▁petrified -▁cair -▁irresolut -▁hammock -cock -▁mouthful -ene -▁yose -▁yosem -▁yosemite -moil -▁turmoil -▁topic -▁volcanic -▁volcano -▁schoolmaster -▁sacraments -▁maintenance -hall -▁metall -▁metallic -▁pavilion -▁dejection -▁carving -▁zepp -▁zeus -▁swoop -▁sloop -▁historic -▁histories -▁historians -▁modes -▁dizzy -▁transactions -▁administered -▁pallid -▁diverse -▁gallows -fellows -oeuv -owder -powder -▁gunpowder -'p -'pose -▁s'pose -▁site -▁tony -▁remnants -▁remnant -outhed -▁wharton -▁achievement -▁quilt -▁explic -▁categ -▁strateg -▁cordelia -▁chord -▁twain -▁orthodox -▁militia -▁postpon -▁newcom -▁clasping -▁torres -▁bruno -glass -▁mowgl -▁lobby -▁unexpl -▁valleys -▁constraint -▁taint -▁mink -▁unlo -▁unlim -▁unlimited -▁romay -▁romayne -▁ingenuous -▁stuffed -▁contributed -▁louisiana -▁representation -▁tumbl -▁charcoal -▁insurance -▁inevitably -▁popped -▁poign -▁tangle -uding -▁speculative -▁enchan -▁rendezvous -▁hauled -▁gestures -▁damascus -▁oliv -▁almond -▁adjourn -▁adjut -▁tut -utable -▁other's -ernel -impernel -▁describing -▁describes -ato -▁overh -▁overhang -▁porcelain -▁lawful -▁infect -▁primarily -▁wearily -▁vulture -isle -isance -▁alteration -▁disapproval -▁veteran -▁outburst -amiliar -▁unfamiliar -▁jet -▁prescription -▁presided -▁intrusion -▁imaginable -roy -▁gilroy -▁advocate -▁curd -▁smould -▁sooth -▁rosalind -ogue -▁compr -▁alac -▁alacr -▁alacrity -▁shrive -▁arguing -▁religions -▁mills -▁scow -▁scald -▁scull -▁scoff -▁andrea -▁revive -▁grandp -▁cashier -▁cable -▁revolving -▁hopeful -▁hypocris -▁goth -▁glimmering -▁decisions -▁colleges -▁lilac -▁droll -▁drought -▁nu -▁nuisance -▁wuz -▁functions -vonne -▁yvonne -▁nev -▁improv -▁yor -▁yorke -▁nursing -▁drooping -▁dundee -▁sometime -▁discons -▁disconsolate -▁discom -▁discomf -lane -ls -▁borls -▁borlsover -▁hindrance -▁clove -rove -▁clare -▁astr -▁chir -▁uninterest -▁unnoticed -▁unaware -▁unres -▁fried -▁unlocked -ustin -▁investigate -▁grumbling -▁bot -▁philamm -▁butcher -▁brightened -▁straightened -▁metals -▁installed -▁wench -▁rais -▁piteously -▁piteous -▁numb -▁appeas -▁acquitted -▁fruitless -▁fearless -▁jenning -▁jennie -▁minnie -▁acceptable -▁nuptial -▁majesty's -▁eugene -▁appealed -▁wyat -▁couplets -▁bracelets -▁doorstep -criptions -▁convulsions -▁fashions -▁appointments -▁requirements -▁contrivance -▁earthen -▁trunks -▁perspective -ierra -▁sierra -icorn -▁malicorn -▁maternal -▁declining -▁definitely -umbent -▁deport -▁depot -▁kara -▁beneficial -▁judicial -▁laund -▁elliot -ife -▁lashed -▁kennic -▁kennicott -cott -cass -▁poetic -▁epic -▁realization -▁location -▁reconstr -▁disinterested -▁professed -▁professors -▁people's -▁blister -▁dimp -▁whimp -▁curses -▁cured -▁curly -▁dancers -▁customers -▁selling -▁regularity -▁shameless -▁joam -▁joanna -iber -▁astound -▁bolted -▁shrug -▁stagn -oncile -▁reconcile -▁volition -▁animation -mare -▁nightmare -▁reeds -▁voiced -▁officials -▁sustenance -▁vicksburg -▁limitation -▁braid -▁awakening -▁infants -▁amory -▁conne -▁connex -▁abd -▁inflex -▁whoso -▁vanishing -▁mimi -▁trodden -▁irritable -▁irrational -▁irritating -▁parch -▁parchment -▁fum -▁dissolution -▁blindness -▁unbear -▁glug -▁illustrate -▁consulting -▁consultation -▁impulsively -▁rupert -▁sorrowfully -▁shrine -issy -▁variations -▁whereof -▁boundary -odora -▁profane -▁colossal -▁teas -▁meetings -▁skirts -▁pistols -▁incline -▁vivacity -▁camels -▁camer -▁huck -▁clamor -▁intoxicated -▁zadok -▁bolshevik -▁extraordin -▁extraordinarily -▁communicating -▁ugliness -▁ambros -▁dess -▁dessert -▁wailing -▁retiring -▁rye -▁rames -▁rambl -▁rudder -▁rinald -▁doubtfully -▁gypsy -▁bedstead -▁gib -▁foref -▁enlarge -▁lect -▁heaviest -▁renaud -▁infid -▁victual -▁speedy -▁cheerfulness -▁carelessness -▁dubious -▁unite -▁distorted -▁penniless -▁veritable -▁vertical -▁bates -▁grimaud -storm -tingham -▁seamen -▁citoyen -ampton -astrous -▁disastrous -▁gond -▁scanned -utomatically -▁proprietors -▁spilet -▁spilett -▁crucif -▁crucifix -▁indiscretion -▁constell -▁armand -interrupted -▁margery -▁quivered -▁vestig -▁carrie -▁entertaining -▁muff -ustr -▁distast -▁entail -▁robust -▁mused -▁successor -▁smelling -▁sixpence -▁vas -shaw -▁avow -uder -▁intruder -▁gentry -▁berry -▁scruples -▁needles -▁conclusive -▁perceptions -▁leopard -▁alarming -▁forefing -▁forefinger -▁dealing -▁avon -▁avonle -▁incomplete -▁grandson -▁rascals -▁penitent -▁folding -▁foreb -▁forebod -▁luther -ugment -▁carfax -▁carcass -dust -▁sawdust -erby -▁bounderby -▁football -itial -feit -▁dotted -▁pedest -▁men's -▁socialist -robor -▁corrobor -▁inquiring -▁recognizing -▁apothe -▁frere -▁survival -▁consecrated -▁exasperated -▁fathers -▁proofs -▁boister -▁brad -▁identify -▁pouch -▁abolished -▁dinah -▁crouching -▁recovering -came -▁woollen -rogr -▁maryland -amour -▁cabbages -▁degrad -▁hannib -valanch -▁leisurely -▁condescend -▁seals -▁crystals -▁shuff -▁shaven -▁popularity -▁yon -▁retic -▁sallie -▁benefactor -▁surpl -▁wars -▁procuring -isation -▁micr -▁glittered -▁enduring -▁rogers -▁gerald -▁swed -▁sweden -▁swagger -▁flickering -pring -spring -▁offspring -▁cartridges -▁creaking -▁permitting -irtier -▁shiny -▁sly -▁plut -▁glut -▁tribut -tering -▁bowing -▁grinding -▁snowy -▁mov -▁rebu -▁rebuke -▁desiring -▁herds -▁grapp -▁gren -▁grub -▁griff -▁reproduce -▁clavering -▁congratulated -▁dangling -▁harmonious -▁patriotic -▁preposterous -▁werner -haust -▁inexhaust -keepers -▁drawers -▁maids -▁collars -▁uses -▁bib -▁bibbs -▁balk -▁omnib -▁omnip -▁subdue -▁beauchamp -▁unsh -▁unin -▁headache -▁basement -▁detachment -▁inventions -▁imperson -▁impre -▁impot -▁impracticable -▁resear -▁researches -▁offense -▁bravest -▁canst -tically -athetically -▁discred -▁shred -▁voted -▁surveyed -affect -▁myles -▁dishonor -▁posts -allows -▁swallows -▁products -owerberry -▁virtually -▁thrush -▁bobb -kirt -▁ink -▁grandchildren -▁concentration -▁egot -▁pianof -▁pianofort -▁befallen -▁tipped -▁gripped -▁courageous -▁accommodation -▁walcott -▁yearning -▁doubly -▁tactics -▁smothered -▁scores -▁compel -▁countenances -▁shak -requently -▁gladness -▁shifted -▁hindered -▁remin -▁reminisc -▁needless -▁gaining -▁ruling -▁forcing -▁wrapp -▁indecent -▁darrow -anter -▁pleasanter -▁emble -▁emblem -▁pebble -▁nit -▁youngster -▁noiselessly -▁titan -▁cran -▁resembles -▁despotism -▁solace -▁penance -▁grating -▁von -▁charleston -▁surg -▁hoot -▁representing -lokhov -▁emperor's -▁cannib -▁frankness -archy -▁projectile -▁unimportant -▁hydra -cox -▁wilcox -▁dorm -▁balancing -▁bac -▁bacchus -▁poplar -▁phyllis -▁goldsmith -▁incomparable -▁oblong -▁gour -coming -▁legislative -▁provoke -▁invinc -▁invincible -erting -▁asserting -▁reverie -▁robart -▁saucy -▁glacial -▁nitrogen -▁negotiation -▁rosalie -osa -osph -▁phosph -▁degradation -▁stark -▁crock -▁phosphor -▁collector -▁overwhelming -▁counting -▁arabian -▁freem -▁surviv -rimonial -▁scrape -▁troph -▁suffers -▁quadrup -▁scientists -▁baile -▁haines -dry -▁sundry -▁jewelry -▁charmed -▁destroying -▁shivering -▁crushing -▁contentment -▁consign -▁enlarged -rupulous -▁syrup -▁marj -▁marjor -▁inherent -iating -▁ll -▁randolph -▁wren -▁burne -▁cupful -▁madman -▁nursed -▁profuse -▁knotted -▁advisable -▁ludov -▁overcame -▁unspeakable -▁hampshire -▁spade -adequ -adequate -▁tufts -▁poisoned -▁founded -▁critically -▁dose -▁impose -▁imply -▁imple -▁implements -▁sweetly -▁rightly -▁invariable -ifiable -▁persecut -a'n't -▁sha'n't -path -▁seasons -▁bricks -▁gulp -▁publicly -▁barefoot -▁objected -▁portugues -▁ford -▁occurs -▁commons -▁mattie -▁encomp -▁defending -▁recollections -▁humiliating -▁softer -▁dissent -▁disclosure -▁uppermost -▁hatchet -▁galloped -▁enm -▁enmity -▁chirp -▁influential -▁foc -▁smitten -vir -▁sagacious -▁scientist -▁boundless -▁professions -▁sowerberry -▁overthr -▁overthrown -owness -▁adored -ctor -▁invested -▁convincing -▁merchand -▁pollen -▁wintry -▁scruple -▁scrupulous -▁fabulous -▁solar -▁shod -▁dedicated -▁crowns -▁athens -▁settlements -▁epithe -▁triang -▁clang -▁thickly -▁denounced -▁uncouth -▁jane's -▁grandpap -▁neelie -▁relates -rithmetic -▁intrep -airy -▁dairy -chair -▁armchair -▁unintelligible -▁goats -▁dandy -▁amy -sed -ssed -▁tossed -▁roofs -▁shutters -▁hailed -▁thirteenth -▁conveniently -▁antagonist -comb -▁expans -▁exposing -▁interfering -lihood -retches -▁wretches -▁circling -▁packing -▁abashed -▁splashed -ree -▁lashes -▁constitutes -▁farmhouse -▁odys -▁odysse -▁odysseus -otic -▁cot -▁customer -▁quer -▁queried -pper -ker -▁traveler -▁patrol -▁dickens -note -▁footnote -▁anecdote -▁anecdotes -▁hardy -▁travelers -▁twould -▁succour -▁lucie -▁silas -▁collision -▁collaps -▁liar -▁siber -▁golf -▁sideways -▁convictions -▁wiping -▁storms -▁gilt -▁ging -▁ginger -▁clause -▁toilet -▁ardour -▁everyday -▁festivities -▁materially -▁gigg -▁excav -▁striped -▁overlooked -▁lair -▁leonard -▁profile -▁philist -▁philistines -▁slammed -▁plunging -▁shifting -▁belgium -▁illusions -▁marchion -▁marchioness -▁glowed -▁geneva -▁wyld -▁unanimous -▁infamous -▁galile -ygen -▁oxygen -in's -▁altitude -▁encompass -▁bitten -▁designs -▁crane -▁grievous -▁succeeding -▁josiana -▁trampled -▁handled -▁musicians -grow -▁exeter -▁accepting -herent -▁molten -▁fireside -wald -▁oswald -▁herald -▁wheeled -▁graduate -▁pointer -▁crusoe -▁ingredients -ileas -▁phileas -▁muttering -▁joking -▁jokes -▁congen -▁privacy -▁wines -▁hier -▁animosity -▁mocking -▁battered -▁freez -▁jerked -▁ruffled -▁rural -▁vasili -lit -▁dwind -▁sherwood -▁ruffian -▁bramble -▁scramble -▁sanguine -▁teap -▁teapot -▁metropolis -▁royalty -▁humpty -imentary -▁cynth -▁cynthia -▁gluck -▁currants -▁currency -▁avonlea -▁perry -▁programme -▁objective -▁murphy -▁prettier -respect -▁disrespect -uspect -▁unsuspect -▁mutually -▁knitted -▁returns -▁ardor -zevir -▁adherents -aments -erned -▁discerned -▁unconcerned -▁aspirations -▁crump -▁snub -▁samson -▁thomson -erd -imerd -▁shimerd -▁naom -▁naomi -▁koodoo -▁rafter -▁georgina -▁bounded -▁flickered -▁deacon -▁brav -▁mong -▁natty -▁dumpty -▁flattery -▁flattered -▁errone -▁coats -insula -▁peninsula -▁bonaparte -▁clearer -▁cheque -▁gravy -▁append -▁napkin -▁disapprove -inois -▁illinois -▁prisoner's -▁inquiringly -▁fringe -▁judicious -▁stride -▁corps -▁tus -▁tuske -▁tuskege -▁tuscan -▁discomfiture -▁greas -▁gret -▁grettel -▁complet -▁completion -▁combine -peck -▁blended -▁bankrupt -▁occupants -▁imprudent -▁mutil -▁disincl -▁gawain -▁crevice -▁grabbed -▁promontory -▁selina -▁frustr -▁weaker -col -▁malcol -▁jol -▁jars -▁malchus -▁amsterdam -▁calmness -equality -▁infinity -▁bridal -▁token -▁bohemia -▁peggot -▁peggotty -▁ridges -▁whims -▁traditions -▁replen -▁satan -▁belly -▁furthermore -▁soothe -▁undisturbed -▁trenches -▁d'ye -shop -shadow -owski -▁senators -▁complacency -▁physiogn -▁betook -▁rameses -▁huss -aterina -▁katerina -▁inexpressible -▁flatten -▁comed -▁comedy -▁poyser -▁cheery -▁cheering -▁peeping -▁travels -▁darwin -▁myth -growth -▁particle -▁mingle -esley -ulity -▁incredulity -▁oats -▁hir -▁hild -▁surfaces -▁pins -▁achieved -▁encumber -▁concurrence -▁untrue -▁untruth -▁icy -▁strides -▁knave -▁knuck -▁knoll -▁diplomacy -▁iceberg -▁locust -▁locom -▁termed -roc -▁buccaneers -▁breeding -▁lynx -▁implored -▁adjusted -▁yorkshire -▁tam -editary -▁hilt -▁nevada -▁heath -▁heathcliff -▁varn -▁variation -▁wilmington -▁wicker -▁expan -▁expanse -▁triangle -▁lining -▁tucked -▁peculiarities -▁horseman -▁chairman -▁raging -▁benefic -▁sentinels -▁dived -▁diseng -▁langdon -▁refreshed -▁mechanics -lled -▁contribution -▁contag -▁contention -agu -▁stork -▁stored -▁entrusted -▁lookout -▁gaston -▁afore -▁afresh -▁blocks -▁speculations -▁whimsical -▁blazed -▁squeezed -▁bounds -▁blindly -▁nar -▁quench -▁wrench -▁mingling -▁randal -aled -▁rewarded -▁shelves -▁cloudy -▁watches -stock -▁marine -thorn -▁drenched -▁corrupted -▁transient -▁aimed -▁vividly -▁steamboat -cular -▁guise -▁enquire -▁enri -▁enriched -▁abhorrence -▁prospects -▁fits -▁morals -▁pelopon -▁peloponnes -▁besieged -▁bearings -▁impregn -▁swelling -▁swelled -▁heaps -▁knaps -▁knapsack -▁anglic -▁despatched -ivoc -▁obsequ -tles -▁turtles -▁pursuers -▁riv -▁sparks -▁sympathetically -▁comments -zed -▁concentrate -▁wrenched -▁casket -umbr -▁precipitous -▁caprice -▁seaman -▁involving -▁potato -▁antipater -▁antipath -▁locksley -▁peev -▁peevish -▁microscop -▁dinars -bec -▁coqu -▁unquestionably -▁wares -▁rats -▁omnibus -▁plucked -▁ascertained -▁marsden -▁daoud -▁occasioned -▁owned -▁quartz -auff -auffeur -▁resisted -▁comprehending -▁whizz -▁blackness -▁boisterous -strained -▁unanimously -▁dejected -▁elim -▁elzevir -▁precipices -rose -▁scrupulously -▁rents -inished -▁unfinished -▁habitation -▁predic -▁predicament -▁footh -▁wroth -▁unavail -▁keel -▁avowed -▁shopping -▁shakes -▁precipitate -▁assurances -▁fasten -▁wistful -▁wistfully -▁accomplice -▁intrigues -▁bewitch -▁silenced -▁vampire -orily -▁satisfactorily -▁greedily -▁tremor -▁simpler -▁explicit -▁uncl -▁massacre -▁appertain -▁disgraceful -▁gig -▁recipient -brand -▁divert -meg -▁nutmeg -▁scroll -▁reiter -▁justly -▁contrivances -▁relatively -▁realizing -▁uncovered -▁rhod -▁expost -▁geron -▁geronim -▁geronimo -heim -▁sut -▁suzan -▁exit -▁lurking -▁mouthed -▁dictionary -▁groove -▁unaffect -▁drowsy -▁distribute -▁cope -▁core -▁spher -ipher -▁madden -▁launched -▁piqu -▁pose -▁respectability -▁carpets -▁assistants -▁protestants -▁harvard -▁sliding -▁bubbles -ofore -etofore -▁transcendent -▁insupport -▁institute -▁creditors -▁dorry -▁unsc -▁unscrupulous -▁osc -▁osmond -ienne -bon -▁irrevoc -▁observance -▁schoolboy -▁jub -endarm -▁gendarm -aug -▁cleanliness -▁deficient -▁reformation -▁skipper -▁paddle -▁unprof -▁martyrdom -▁inexhaustible -▁instincts -▁instinctive -▁processes -▁exuber -▁memorand -▁chum -▁chagr -▁hurons -▁bash -▁crumbling -▁alcoh -▁acorn -za -▁alg -▁embodied -▁stip -une -▁shun -▁shedding -▁blooming -▁tet -▁tete -▁tending -▁debauch -▁ceases -▁reversed -▁enables -▁aggre -▁aggregate -reast -▁abreast -▁inconsistent -▁exploit -▁spit -▁sph -▁sphin -▁sphinx -▁appalling -▁fling -▁filthy -▁monopo -▁monopoly -▁excitedly -▁schoolroom -▁woozy -▁patchwork -▁purest -▁snowdrop -▁individually -▁pinn -▁pinned -▁whereabouts -▁decorations -▁fade -romising -from -▁marvelled -▁conveniences -▁diminution -▁bee -▁scornful -▁scrutiny -▁incumbent -▁planets -▁raddle -▁conversion -▁sneer -▁nothin -▁gride -▁pensive -▁defensive -▁defer -▁defy -▁defied -▁cellars -▁missionaries -▁prone -▁zone -▁connexion -lem -▁enigm -▁meekly -▁equator -▁comply -▁shoemaker -▁derive -▁extort -▁barbarian -▁deafening -▁unearthly -▁practising -▁hilar -▁hive -▁adventurer -▁adventurers -▁superiors -▁loosed -▁securely -▁pimpernel -▁inwardly -▁sickly -▁correctly -▁recesses -▁friction -▁adjustment -▁persh -▁assim -▁assimil -▁vows -▁divined -▁glimmer -▁trimmed -▁rivalry -▁gallantry -▁agricola -▁oakes -▁redoub -▁captain's -▁gems -▁proceeds -▁fails -▁flocks -▁mists -▁gruff -▁saus -▁miranda -▁frogs -ilib -▁equilib -▁equilibri -▁equilibrium -ils -▁breaks -ante -▁kenwigs -▁agreeably -▁lavender -▁brimm -▁fras -▁fraser -▁retribution -▁amiss -▁mand -akers -▁jov -▁jovial -▁increases -▁preferable -▁gobl -▁fragoso -▁sentry -▁prettily -▁buying -▁pacing -▁ludic -▁ludicrous -▁psychic -▁urging -▁omnipot -▁walt -▁waltz -▁novels -▁mug -▁tension -▁percent -▁accessible -▁fixing -▁disturbing -▁affecting -▁drowning -▁bayon -▁binding -▁lanc -▁maple -ilghai -▁nilghai -▁devils -▁expended -▁enjoined -▁heroism -▁adjutant -▁shrugging -▁streaked -▁hereditary -▁lag -▁lagoon -▁spout -▁furt -▁clod -▁patronage -▁stiffly -▁derision -▁demure -▁demol -▁demoral -oles -▁includes -▁instig -▁ensuing -▁deity -▁contending -▁geographical -ton's -▁mik -▁greasy -lands -▁flashes -▁conversed -▁fainted -▁postpone -▁respecting -▁meditating -abble -▁absently -▁fervently -▁bathsh -▁subt -▁sample -▁steered -eb -▁bathsheb -▁seduced -▁vernon -▁joll -▁perforce -▁poised -▁advertised -▁skinner -▁hopelessly -▁rudely -▁modestly -▁pleading -▁clutch -thood -▁reek -▁gothic -▁footmen -▁bunder -▁bundercombe -▁melbourne -▁dissolve -▁graveyard -▁pamel -▁blouse -▁grouse -▁resident -▁mama -▁southwest -▁favors -▁vibration -▁bask -▁unr -▁vestib -▁vestibule -▁ballast -sham -▁feversham -▁gresham -▁worms -▁spears -▁proves -▁sherry -▁crag -▁crashed -mphithe -▁attempting -fit -▁outfit -iesthood -▁priesthood -▁authors -▁authorized -▁possum -▁deprec -▁depreci -civ -▁scur -iner -▁paler -▁gout -▁traps -▁ghosts -▁unprepar -▁corrected -▁concrete -▁unatt -▁pratt -▁rides -▁grandma -▁roadside -lliver -▁dye -▁cane -venture -adventure -▁unfavor -▁separating -▁athenian -▁blithe -▁thrilling -▁complacently -▁scorched -▁nazareth -▁estra -▁calend -▁calendar -▁suspend -▁suspension -▁thrilled -▁assailed -▁tom's -▁misle -▁prevailing -▁introducing -▁suzanne -▁morocc -▁pion -▁pioneer -▁poetical -▁appell -▁temporal -▁outrageous -▁preparatory -arken -▁dexterity -▁surgery -▁threats -▁woes -▁devise -▁treatise -▁cyp -▁spectre -▁reliable -▁erroneous -asuring -▁wisc -▁wiscons -▁wisconsin -▁wisp -▁therein -▁raisins -▁sord -▁sordid -▁sissy -▁warren -▁metaphysical -▁hypocrisy -▁astronomy -▁burgundy -▁servile -▁deportment -▁scarborough -ogne -lov -▁sown -▁darker -▁memorandum -▁boldness -▁tips -▁ruggles -▁strut -▁scut -▁plush -▁presty -faction -▁dissatisfaction -▁recreation -▁implicit -▁wilful -▁sprawling -▁carson -▁powerless -▁tho -▁thinner -▁thaw -▁praising -▁assemblage -belie -▁disbelie -▁greeb -▁veiled -▁curry -▁pricked -▁vapor -▁razor -▁rubies -▁doun -▁dounia -▁hullo -▁dissim -▁sah -▁hardships -▁livelihood -▁condescended -mann -▁paltry -elope -▁ful -ionys -▁dionys -▁dapp -▁conway -▁pathway -▁diligently -augur -▁finely -▁detestable -▁ribs -▁jord -▁jordan -▁frant -▁frantically -▁hobbled -▁vehicles -▁raffles -▁projected -▁tartar -▁dashing -▁jailer -▁catalogue -▁workmanship -▁confederation -▁smear -▁fulfilment -▁ryn -▁spat -▁spokes -▁sped -▁mit -▁surprises -▁afar -▁gaul -rocious -▁mythology -▁diplomatic -▁preaching -▁wrinkles -▁knightley -▁prejudiced -abul -abulary -▁vocabulary -onnected -▁grenville -thell -▁othell -▁ceasing -▁reckoning -imetheus -▁epimetheus -▁politician -▁decomp -▁marion -▁marionette -chamber -▁eclipse -▁provident -▁seating -▁chry -▁uncompromising -▁tankerville -▁stowed -▁stella -▁polonaise -▁hopping -▁curling -▁skir -▁individuality -▁denmark -▁assailants -▁tenants -▁louv -olf -▁insure -▁quack -▁confront -▁symmet -▁anita -▁finite -▁prefers -▁immov -▁merits -▁saints -inent -▁imminent -rent -▁fervent -▁screened -▁unwillingly -▁hetty -braska -▁nebraska -▁moraine -▁maslov -▁cheered -▁announcing -▁municipal -aboo -▁diving -▁preoccupied -▁shuttle -▁attacks -▁crippled -▁mitig -▁pallor -▁implore -▁cray -▁overjoy -▁offerings -▁mortar -▁plumage -▁reappeared -▁juan -▁ordinance -▁reassuring -▁capturing -esee -▁adela -daughter -▁abiding -▁bamb -▁bamboo -▁amazingly -▁venturing -▁undertone -▁undertaker -▁tarling -loe -▁chloe -issure -▁fissure -▁wrapping -▁chinam -▁mineral -▁inflicted -▁committees -▁kr -▁caps -▁disappearing -▁throbbing -▁poland -▁resid -▁reside -▁soda -▁swain -▁swans -▁beauteous -▁loaves -▁norah -▁tilted -▁frowning -▁reclining -▁melting -▁intrinsic -▁ethere -▁stimulating -▁tissues -▁inconveniences -▁astounded -▁verm -▁similarly -▁ester -▁esterbrook -▁opposing -cturn -▁carrier -bald -▁cald -▁trudged -borg -separable -▁omen -▁omit -▁exultation -▁supporting -▁depraved -▁micaw -▁micawber -oxys -▁paroxys -▁sophia -▁scented -▁editorial -▁geological -afty -▁crafty -▁homesick -▁ravages -people -▁florina -▁hintock -▁skirted -▁rejoinder -▁handling -▁humorous -ese -▁geese -▁dipping -▁whipping -▁sulky -▁laf -▁lafitt -▁impair -▁sloping -ety -▁scy -▁scythe -▁harlow -▁subscription -▁unbearable -▁delirious -▁attainment -▁ignored -▁loathsome -▁lighter -▁telford -▁ank -▁ankle -▁fork -▁puzzle -▁ankles -▁brus -▁brag -▁merchandise -▁surmise -▁despoil -▁ornamented -▁stiffened -▁bunches -▁role -▁rogues -▁weakened -▁indirectly -▁fortifications -▁dent -▁embank -▁woodpeck -▁roaming -▁beaming -▁admiringly -▁florida -▁hunch -▁lengthen -▁irrit -▁fruitful -▁impotent -utation -▁decoration -▁ingratitude -engthe -engthening -▁favoured -▁reced -▁turbulent -▁midwin -▁hyrcanus -▁gourville -▁behaving -▁ting -▁tinge diff --git a/lingvo/tasks/car/BUILD b/lingvo/tasks/car/BUILD deleted file mode 100644 index df8874a3..00000000 --- a/lingvo/tasks/car/BUILD +++ /dev/null @@ -1,562 +0,0 @@ -# Car related tasks. - -package(default_visibility = [ - "//visibility:public", -]) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "car_layers", - srcs = ["car_layers.py"], - srcs_version = "PY3", - deps = [ - ":car_lib", - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:py_utils", - ], -) - -py_library( - name = "detection_3d_lib", - srcs = ["detection_3d_lib.py"], - srcs_version = "PY3", - deps = [ - ":geometry", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/tasks/car/ops", - # Implicit numpy dependency. - ], -) - -py_library( - name = "detection_decoder", - srcs = ["detection_decoder.py"], - srcs_version = "PY3", - deps = [ - ":detection_3d_lib", - "//lingvo:compat", - "//lingvo/core:py_utils", - ], -) - -py_test( - name = "detection_decoder_test", - srcs = ["detection_decoder_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":detection_decoder", - "//lingvo:compat", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "detection_3d_metrics", - srcs = ["detection_3d_metrics.py"], - srcs_version = "PY3", - deps = [ - ":summary", - ":transform_util", - # Implicit PIL dependency. - "//lingvo:compat", - "//lingvo/core:metrics", - "//lingvo/core:plot", - "//lingvo/core:py_utils", - # Implicit matplotlib dependency. - # Implicit numpy dependency. - # Implicit Tensorboard dependency. - ], -) - -py_test( - name = "detection_3d_metrics_test", - srcs = ["detection_3d_metrics_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":detection_3d_metrics", - ":transform_util", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_test( - name = "detection_3d_lib_test", - srcs = ["detection_3d_lib_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":detection_3d_lib", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_test( - name = "car_layers_test", - srcs = ["car_layers_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":car_layers", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "ap_metric", - srcs = [ - "ap_metric.py", - ], - srcs_version = "PY3", - deps = [ - ":breakdown_metric", - "//lingvo:compat", - "//lingvo/core:hyperparams", - "//lingvo/core:metrics", - "//lingvo/core:py_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "calibration_processing", - srcs = [ - "calibration_processing.py", - ], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:plot", - # Implicit numpy dependency. - ], -) - -py_test( - name = "calibration_processing_test", - srcs = ["calibration_processing_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":calibration_processing", - ":kitti_metadata", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "breakdown_metric", - srcs = [ - "breakdown_metric.py", - ], - srcs_version = "PY3", - deps = [ - ":calibration_processing", - "//lingvo:compat", - "//lingvo/core:hyperparams", - "//lingvo/core:plot", - # Implicit numpy dependency. - ], -) - -py_test( - name = "breakdown_metric_test", - srcs = ["breakdown_metric_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":breakdown_metric", - ":kitti_ap_metric", - ":kitti_metadata", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "builder_lib", - srcs = ["builder_lib.py"], - srcs_version = "PY3", - deps = [ - ":car_layers", - "//lingvo:compat", - "//lingvo/core:builder_layers", - "//lingvo/core:layers", - "//lingvo/core:py_utils", - # Implicit numpy dependency. - ], -) - -py_test( - name = "builder_lib_test", - srcs = ["builder_lib_test.py"], - python_version = "PY3", - shard_count = 2, - srcs_version = "PY3", - deps = [ - ":builder_lib", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "evaluation_metadata", - srcs = [ - "evaluation_metadata.py", - ], - srcs_version = "PY3", - deps = [ - # Implicit numpy dependency. - ], -) - -py_library( - name = "car_lib", - srcs = ["car_lib.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:py_utils", - ], -) - -py_library( - name = "geometry", - srcs = ["geometry.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:py_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "input_extractor", - srcs = ["input_extractor.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:py_utils", - "//lingvo/tasks/car:base_extractor", - ], -) - -py_test( - name = "input_extractor_test", - srcs = ["input_extractor_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":input_extractor", - ":input_preprocessors", - "//lingvo:compat", - "//lingvo/core:hyperparams", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "base_decoder", - srcs = ["base_decoder.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:py_utils", - "//lingvo/core/ops:record_py_pb2", - ], -) - -py_library( - name = "base_extractor", - srcs = ["base_extractor.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:datasource", - "//lingvo/core:generic_input", - "//lingvo/core:hyperparams", - "//lingvo/core:py_utils", - ], -) - -py_library( - name = "kitti_ap_metric", - srcs = [ - "kitti_ap_metric.py", - ], - srcs_version = "PY3", - deps = [ - ":ap_metric", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/tasks/car/ops", - # Implicit numpy dependency. - ], -) - -py_library( - name = "kitti_decoder", - srcs = ["kitti_decoder.py"], - srcs_version = "PY3", - deps = [ - ":base_decoder", - ":detection_3d_lib", - ":detection_3d_metrics", - ":geometry", - ":kitti_ap_metric", - ":kitti_metadata", - ":transform_util", - "//lingvo:compat", - "//lingvo/core:metrics", - "//lingvo/core:py_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "kitti_metadata", - srcs = [ - "kitti_metadata.py", - ], - srcs_version = "PY3", - deps = [":evaluation_metadata"], -) - -py_library( - name = "lr_util", - srcs = ["lr_util.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:cluster_factory", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - ], -) - -py_test( - name = "lr_util_test", - size = "small", - srcs = ["lr_util_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":kitti_input_generator", - ":lr_util", - ":pillars", - "//lingvo:compat", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "pillars", - srcs = ["pillars.py"], - srcs_version = "PY3", - deps = [ - ":builder_lib", - ":detection_3d_lib", - ":geometry", - ":point_detector", - # Implicit enum dependency. - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:layers", - "//lingvo/core:optimizer", - "//lingvo/core:py_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "point_detector", - srcs = ["point_detector.py"], - srcs_version = "PY3", - deps = [ - ":detection_decoder", - ":kitti_decoder", - "//lingvo:compat", - "//lingvo/core:base_model", - "//lingvo/core:py_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "pointnet", - srcs = ["pointnet.py"], - srcs_version = "PY3", - deps = [ - ":builder_lib", - "//lingvo:compat", - "//lingvo/core:builder_layers", - "//lingvo/core:py_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "starnet", - srcs = ["starnet.py"], - srcs_version = "PY3", - deps = [ - ":builder_lib", - ":detection_3d_lib", - ":geometry", - ":point_detector", - # Implicit enum dependency. - "//lingvo:compat", - "//lingvo/core:py_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "summary", - srcs = ["summary.py"], - srcs_version = "PY3", - deps = [ - ":transform_util", - # Implicit PIL dependency. - "//lingvo:compat", - "//lingvo/core:plot", - # Implicit matplotlib dependency. - # Implicit numpy dependency. - ], -) - -py_test( - name = "pointnet_test", - srcs = ["pointnet_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":pointnet", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - ], -) - -py_test( - name = "summary_test", - srcs = ["summary_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":summary", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "transform_util", - srcs = [ - "transform_util.py", - ], - srcs_version = "PY3", - deps = [ - # Implicit numpy dependency. - ], -) - -py_test( - name = "transform_util_test", - size = "small", - srcs = [ - "transform_util_test.py", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":transform_util", - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_test( - name = "car_lib_test", - srcs = ["car_lib_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":car_lib", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_test( - name = "geometry_test", - srcs = ["geometry_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":geometry", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "input_preprocessors", - srcs = ["input_preprocessors.py"], - srcs_version = "PY3", - deps = [ - ":car_lib", - ":detection_3d_lib", - ":geometry", - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:py_utils", - "//lingvo/tasks/car/ops", - # Implicit numpy dependency. - ], -) - -py_library( - name = "kitti_input_generator", - srcs = [ - "kitti_input_generator.py", - ], - srcs_version = "PY3", - deps = [ - ":geometry", - ":input_extractor", - ":input_preprocessors", - ":kitti_metadata", - "//lingvo:compat", - "//lingvo/core:datasource", - "//lingvo/core:hyperparams", - "//lingvo/core:py_utils", - "//lingvo/core/ops", - ], -) diff --git a/lingvo/tasks/car/README.md b/lingvo/tasks/car/README.md deleted file mode 100644 index ec83ec1a..00000000 --- a/lingvo/tasks/car/README.md +++ /dev/null @@ -1,158 +0,0 @@ -# Lingvo 3D pointcloud object detection - -This directory contains libraries and models for training 3D classification and -detection datasets using primarily pointcloud inputs. - -The repo contains the code needed to reproduce the results at -https://arxiv.org/abs/1908.11069. - -## Guide to reproduce StarNet on GCP - -The guide below is meant to illustrate how to train a StarNet model on GCP using -CloudTPUs for training and GPUs for evaluation, using GKE. - -### Setup - -First, you will need to setup a GCP account. - -Some useful resources: - -* https://cloud.google.com/kubernetes-engine/docs/tutorials/hello-app -* https://cloud.google.com/tpu/docs/kubernetes-engine-setup -* https://cloud.google.com/kubernetes-engine/docs/troubleshooting - -You may also consider reading the -[TensorFlow Minigo Cloud TPU](https://github.com/tensorflow/minigo/tree/master/cluster) -script repository for managing GCP resources. - -#### Cloud TPU cluster setup - -__IMPORTANT: These commands will result in VMs and other GCP resources being -created and will result in charges to your GCP account! Proceed with care!__ - -To properly create the CloudTPU cluster, one should follow the TPU documentation -above. An example might be: - - gcloud container clusters create tpu_v3_cluster --cluster-version=1.13 --scopes=cloud-platform,gke-default --enable-ip-alias --enable-tpu --zone=europe-west4-a - -where we create a TPU-compatible cluster in a zone that contains V3 TPU pods. We -only need to specify the size of the TPU pod we want when we create the job. - -#### GPU cluster setup - -__IMPORTANT: These commands will result in VMs and other GCP resources being -created and will result in charges to your GCP account! Proceed with care!__ - -First, create the cluster. You may need to get -[GPU quota](https://cloud.google.com/compute/quotas#gpus) in the zone you want -for the GPU type you want. - -For example, here we create a P100 cluster of size 1 using high-memory CPU -instances (which are useful for evaluating / decoding): - - gcloud container clusters create p100-europe-west4-a-nh16 --accelerator type=nvidia-tesla-p100,count=1 --num-nodes=1 --zone europe-west4-a --scopes=cloud-platform,gke-default --enable-ip-alias --cluster-version=1.13 --machine-type=n1-highmem-16 - -To be able to launch GPU jobs on this cluster, one must then install the -drivers. - - kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/nvidia-driver-installer/cos/daemonset-preloaded.yaml - -To check that the GPUs have been fully initialized with these drivers, you can -run: - - kubectl get no -w -o yaml | grep -E 'hostname:|nvidia.com/gpu' - -#### Docker setup - -It is required to use Docker to be able to push Docker images for GKE to load. - -One should set up docker following instructions online. - -We have provided a lingvo-compatible dockerfile that comes with the lingvo pip -package pre-installed. One should build the docker with the GPU nvidia-docker -base for running on GPUs. For example, from the current repo: - - docker build --tag tensorflow:lingvo_lib_gpu --build-arg base_image=nvidia/cuda:10.0-cudnn7-runtime-ubuntu18.04 - < lingvo/docker/lib.dockerfile - -This will be the base docker image you will use to run lingvo jobs on GKE. - -#### Data prep - -To train our existing models without modification, one must first upload -TF.Example versions of the datasets to GCS. - -We have provided scripts to process the raw data in the tools/ and waymo/tools -subdirectories. For example, one can run tools/kitti_exporter.py to create the -TFRecord TFExample files from the raw KITTI data; one can then upload the -results to GCS under a bucket you own. - -## Launching the training job on a 4x4 Cloud TPU V3. - -We have provided a python script that launches / manages GKE jobs at -lingvo/tools/gke_launch.py. Let's say you want to reproduce the -model used for training the StarNet Pedestrian/Cyclist model on KITTI named -StarNetPedCycModel0704 in params/kitti.py. - -To launch training on TPU, decoding for the validation set on GPU, and a -TensorBoard to monitor it all, one can run: - - # Name of the registered model in params/kitti.py to run. - export MODEL=params.kitti.StarNetPedCycModel0704 - - # Environment variable containing path to input dataset. - export DATA_ENV="KITTI_DIR=gs://my-bucket/kitti" - - # For waymo, one can use the version generated via generate_waymo_tf.py - # graciously hosted by Waymo; you must have registered for access to - # the Waymo dataset to be able to access the following bucket. - # export DATA_ENV="WAYMO_DIR=gs://waymo_open_dataset_v_1_0_0_tf_example_lingvo/v.1.2.0/" - - export SPLIT=dev # Use 'minidev' for smaller Waymo validation set. - - # Base docker image name to use for packaging the - # code to run. - export DOCKER_IMAGE=gcr.io/my-project/lingvo-code - - # Location of where the code to be packaged into - # the docker file is located. - export CODE_DIR=/path/to/base/of/lingvo_repo - - # Log directory to write checkpoints to. - export LOGDIR=gs://my-bucket/logs/starnet.kitti.v0 - - # Name (or filter) of the TPU cluster you created above. - export TPU_CLUSTER_NAME=tpu_v3_cluster - - # Specify using a 4x4 Cloud TPU v3. - export TPU_TYPE=v3-32 - - # Name (or filter) of the GPU cluster you created above. - export GPU_CLUSTER_NAME=p100 - - # Specify using a p100 GPU. - export GPU_TYPE=p100 - - # The prefix name of the jobs to launch on GKE. - export EXP_NAME=kitti.starnet.pedcyc.v0 - - python3 lingvo/tools/gke_launch.py \ - --model=$MODEL \ - --base_image=tensorflow:lingvo_lib_gpu \ - --image=$DOCKER_IMAGE \ - --logdir=$LOGDIR \ - --tpu_type=$TPU_TYPE \ - --trainer_cell=$TPU_CLUSTER_NAME \ - --decoder_cell=$GPU_CLUSTER_NAME \ - --decoder_gpus=1 \ - --gpu_type=$GPU_TYPE \ - --decoder=$SPLIT \ - --extra_envs=$DATA_ENV \ - --name=$EXP_NAME \ - --build=$CODE_DIR/lingvo/tasks/car \ - reload all - -As the model trains, the TPU and GPU jobs will output events to the log -directory, which the TensorBoard job will visualize. - -To bring down the jobs, you can run the above command with "down" instead of -"reload". diff --git a/lingvo/tasks/car/__init__.py b/lingvo/tasks/car/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/car/ap_metric.py b/lingvo/tasks/car/ap_metric.py deleted file mode 100644 index 1ef2242d..00000000 --- a/lingvo/tasks/car/ap_metric.py +++ /dev/null @@ -1,556 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Average precision metric interface.""" - -import functools -from lingvo import compat as tf -from lingvo.core import hyperparams -from lingvo.core import py_utils -from lingvo.core.metrics import BaseMetric -from lingvo.tasks.car import breakdown_metric -import numpy as np - - -# TODO(shlens): Consider making this internal data structure a list of NestedMap -# to simplify the implementation. -class Boxes3D: - """A container for a list of 7-DOF 3D boxes. - - Each 3D box is represented by the 7-tuple [x, y, z, dx, dy, dz, phi], - where x, y, z is the center of the box, dx, dy, dz represent the width, - length, and height of the box, and phi is the rotation of the box. - """ - - def __init__(self): - self._capacity = 0 - self._size = 0 - self._buf = py_utils.NestedMap( - imgids=np.empty([0]), - scores=np.empty([0]), - boxes=np.empty([0, 7]), - difficulties=np.empty([0]), - distances=np.empty([0]), - num_points=np.empty([0]), - rotations=np.empty([0]), - heights_in_pixels=np.empty([0]), - speeds=np.empty([0, 2])) - - def Add(self, img_id, score, box, difficulty, distance, num_points, rotation, - height_in_pixels, speed): - """Adds a bbox. - - Args: - img_id: A unique image identifier. - score: The confidence score. - box: [1 x 7] numpy array. - difficulty: The difficulty of a box. On KITTI this is one of [0, 1, 2, 3]. - distance: The binned distance of a box. - num_points: Number of laser points in box. - rotation: The binned rotation of a box. - height_in_pixels: The height of the 2D bbox of this object in the camera - image. - speed: A [1 x 2] numpy array with speed of object in world frame. - """ - if self._size >= self._capacity: - # resize - if self._capacity: - # Increase the capacity exponentially. - self._capacity += self._capacity // 4 - else: - self._capacity = 100 - self._buf = self._buf.Transform(self._Resize) - self._buf.imgids[self._size] = img_id - self._buf.scores[self._size] = score - self._buf.boxes[self._size] = box - self._buf.difficulties[self._size] = difficulty - self._buf.distances[self._size] = distance - self._buf.num_points[self._size] = num_points - self._buf.rotations[self._size] = rotation - self._buf.heights_in_pixels[self._size] = height_in_pixels - self._buf.speeds[self._size] = speed - self._size += 1 - - def _Resize(self, arr): - n = self._capacity - ret = np.empty([n] + list(arr.shape)[1:], dtype=arr.dtype) - ret[:arr.shape[0]] = arr - return ret - - @property - def imgids(self): - return self._buf.imgids[:self._size] - - @property - def scores(self): - return self._buf.scores[:self._size] - - @property - def boxes(self): - return self._buf.boxes[:self._size] - - @property - def difficulties(self): - return self._buf.difficulties[:self._size] - - @property - def distances(self): - return self._buf.distances[:self._size] - - @property - def num_points(self): - return self._buf.num_points[:self._size] - - @property - def rotations(self): - return self._buf.rotations[:self._size] - - @property - def heights_in_pixels(self): - return self._buf.heights_in_pixels[:self._size] - - @property - def speeds(self): - return self._buf.speeds[:self._size] - - -class APMetrics(BaseMetric): - """Measure an assortment of precision-recall metrics on a dataset.""" - - @classmethod - def Params(cls, metadata): - """Params builder for APMetrics.""" - p = hyperparams.InstantiableParams(cls) - p.Define( - 'metadata', metadata, - 'Instance of class obeying EvaluationMetadata interface consisting of ' - 'parameters specifying the details of the evaluation.') - p.Define( - 'breakdown_metrics', [], - 'List of extra breakdown metrics when computing AP. Valid values ' - 'include: ["num_points", "distance", "rotation"]. See ' - 'breakdown_metric.py:ByName for the full list. ByDifficulty is ' - 'always used.') - p.Define( - 'metric_weights', None, - 'For metrics that have multiple breakdown metrics, ' - 'a user may want the value() function to be a function ' - 'of the various breakdowns. If provided, this specifies ' - 'the weights assigned to each class\'s metric contribution. ' - 'metric_weights should be a dictionary mapping every difficulty ' - 'level to a numpy vector of weights whose values corresponds ' - 'to the order of metadata.EvalClassIndices() weighting.') - p.Define( - 'box_type', '3d', 'Specifies what kind of box evaluation will ' - 'be performed (only supported by waymo metric). ' - 'One of ["2d", "3d"]: 3d means to do 3D AP calculation, and 2d ' - 'means to do a top down Birds-Eye-View AP calculation.') - return p - - def __init__(self, params): - """Initialize the metrics.""" - self.params = params.Copy() - self._is_eval_complete = False - self._groundtruth = {} # keyed by class id. - self._prediction = {} # keyed by class id. - self._str_to_imgid = {} - self._iou_thresholds = self.params.metadata.IoUThresholds() - - self.metadata = self.params.metadata - assert self.params.box_type in ['2d', '3d'] - - # We must always include ByDifficulty. - metrics_params = breakdown_metric.ByDifficulty.Params().Set( - metadata=self.metadata) - self._breakdown_metrics = { - 'difficulty': breakdown_metric.ByDifficulty(metrics_params) - } - - for breakdown_metric_name in self.params.breakdown_metrics: - self._breakdown_metrics[breakdown_metric_name] = ( - breakdown_metric.ByName(breakdown_metric_name)(metrics_params)) - - def _GetImageId(self, str_id): - if str_id in self._str_to_imgid: - return self._str_to_imgid[str_id] - else: - imgid = len(self._str_to_imgid) - self._str_to_imgid[str_id] = imgid - return imgid - - def _AddGroundtruth(self, box): - """Record a ground truth box.""" - (str_imgid, classid, score, box, difficulty, distance, num_points, rot, - speed) = box - imgid = self._GetImageId(str_imgid) - assert classid > 0 and classid < self.metadata.NumClasses(), ( - '{} vs. {}'.format(classid, self.metadata.NumClasses())) - - boxes = self._groundtruth.get(classid) - if boxes is None: - boxes = Boxes3D() - self._groundtruth[classid] = boxes - boxes.Add(imgid, score, box, difficulty, distance, num_points, rot, -1, - speed) - # Invalidate the evaluation. - self._is_eval_complete = False - - def _LoadBoundingBoxes(self, - box_type, - class_id, - distance=None, - num_points=None, - rotation=None): - """Load a specified set of bounding boxes. - - If no boxes are found, return None. - - Note that we do *not* specify 'difficulty' as an option due to how the KITTI - evaluates bounding boxes across difficulty levels. - - Args: - box_type: string. Either 'groundtruth' or 'prediction' - class_id: int32 specifying the class - distance: int32 specifying a binned Euclidean distance of the ground truth - bounding box. If None is specified, all distances are selected. - num_points: int32 specifying a binned number of laser points within the - ground truth bounding box. If None is specified, all boxes are selected. - rotation: int32 specifying a binned rotation within the ground truth - bounding box. If None is specified, all boxes are selected. - - Returns: - Boxes3D containing bounding boxes or None if no bounding boxes available. - """ - assert box_type in ['groundtruth', 'prediction'] - if box_type == 'groundtruth': - boxes_by_class = self._groundtruth - else: - boxes_by_class = self._prediction - - if class_id not in boxes_by_class: - return None - boxes = boxes_by_class[class_id] - - if boxes is not None and distance is not None: - # Filter bounding boxes based a binned (integer) distance. - filtered_boxes = None - for i, s, b, d, dist, n, r, h, v in zip(boxes.imgids, boxes.scores, - boxes.boxes, boxes.difficulties, - boxes.distances, boxes.num_points, - boxes.rotations, - boxes.heights_in_pixels, - boxes.speeds): - if dist == distance: - if filtered_boxes is None: - filtered_boxes = Boxes3D() - filtered_boxes.Add(i, s, b, d, dist, n, r, h, v) - boxes = filtered_boxes - - if boxes is not None and num_points is not None: - # Filter bounding boxes based a binned (integer) number of points. - filtered_boxes = None - for i, s, b, d, dist, n, r, h, v in zip(boxes.imgids, boxes.scores, - boxes.boxes, boxes.difficulties, - boxes.distances, boxes.num_points, - boxes.rotations, - boxes.heights_in_pixels, - boxes.speeds): - if n == num_points: - if filtered_boxes is None: - filtered_boxes = Boxes3D() - filtered_boxes.Add(i, s, b, d, dist, n, r, h, v) - boxes = filtered_boxes - - if boxes is not None and rotation is not None: - # Filter bounding boxes based a binned (integer) rotation. - filtered_boxes = None - for i, s, b, d, dist, n, r, h, v in zip(boxes.imgids, boxes.scores, - boxes.boxes, boxes.difficulties, - boxes.distances, boxes.num_points, - boxes.rotations, - boxes.heights_in_pixels, - boxes.speeds): - if r == rotation: - if filtered_boxes is None: - filtered_boxes = Boxes3D() - filtered_boxes.Add(i, s, b, d, dist, n, r, h, v) - boxes = filtered_boxes - - return boxes - - def _GetData(self, - classid, - difficulty=None, - distance=None, - num_points=None, - rotation=None): - """Returns groundtruth and prediction for the classid in a NestedMap. - - Args: - classid: int32 specifying the class - difficulty: String in [easy, moderate, hard]. If None specified, all - difficulty levels are permitted. - distance: int32 specifying a binned Euclidean distance of the ground truth - bounding box. If None is specified, all distances are selected. - num_points: int32 specifying a binned number of laser points within the - ground truth bounding box. If None is specified, all boxes are selected. - rotation: int32 specifying a binned rotation within the ground truth - bounding box. If None is specified, all boxes are selected. - - Returns: - NestedMap containing iou_threshold, groundtruth and predictions for - specified, classid, difficulty level and binned distance. If no bboxes - are found with these parameters, returns None. - """ - raise NotImplementedError('_GetData must be implemented') - - def _BuildMetric(self, feed_data, classid): - raise NotImplementedError('_BuildMetric must be implemented') - - def _ComputeFinalMetrics(self, - classids=None, - difficulty=None, - distance=None, - num_points=None, - rotation=None): - """Compute precision-recall curves as well as average precision. - - Args: - classids: A list of N int32. - difficulty: String in [easy, moderate, hard]. If None specified, all - difficulty levels are permitted. - distance: int32 specifying a binned Euclidean distance of the ground truth - bounding box. If None is specified, all distances are selected. - num_points: int32 specifying a binned number of laser points within the - ground truth bounding box. If None is specified, all boxes are selected. - rotation: int32 specifying a binned rotation within the ground truth - bounding box. If None is specified, all boxes are selected. - - Returns: - dict. Each entry in the dict is a list of C (number of classes) dicts - containing mapping from metric names to individual results. Individual - entries may be the following items. - - scalars: A list of C (number of classes) dicts mapping metric - names to scalar values. - - curves: A list of C dicts mapping metrics names to np.float32 - arrays of shape [NumberOfPrecisionRecallPoints()+1, 2]. In the last - dimension, 0 indexes precision and 1 indexes recall. - - calibrations: A list of C dicts mapping metrics names to np.float32 - arrays of shape [number of predictions, 2]. The first column is the - predicted probabilty and the second column is 0 or 1 indicating that the - prediction matched a ground truth item. - """ - raise NotImplementedError('_ComputeFinalMetric must be implemented') - - def Update(self, str_id, result): - """Update this metric with a newly evaluated image. - - Args: - str_id: A string. Unique identifier of an image. - result: A NestedMap with these fields: - groundtruth_labels - [N]. Groundtruth boxes' labels. - - groundtruth_bboxes - [N, 7]. Groundtruth boxes coordinates. - - groundtruth_difficulties - [N]. Groundtruth boxes difficulties. - - groundtruth_num_points - [N]. Number of laser points in bounding - boxes. - - groundtruth_speed - [N, 2] Speed in (vx, vy) of the ground truth - object. - - detection_scores - [C, M] - For each class (C classes) we have (up to) - M predicted boxes. - - detection_boxes - [C, M, 7] - [i, j, :] are coordinates for i-th - classes j-th predicted box. - - detection_heights_in_pixels - [C, M]. [i, j] is the height of i-th - classes j-th boxes 2D image coordinate (camera view). - - """ - n = result.groundtruth_labels.shape[0] - assert result.groundtruth_bboxes.shape == (n, 7) - - if 'groundtruth_speed' not in result: - result.groundtruth_speed = np.zeros((n, 2), dtype=np.float32) - - groundtruth_result = py_utils.NestedMap( - bboxes=result.groundtruth_bboxes, - num_points=result.groundtruth_num_points, - difficulties=result.groundtruth_difficulties, - labels=result.groundtruth_labels) - for m in self._breakdown_metrics.values(): - m.AccumulateHistogram(groundtruth_result) - m.AccumulateCumulative(groundtruth_result) - - # TODO(vrv): The Boxes3D structure currently expects a fixed - # number of arguments corresponding to every possible - # breakdown metric, even if not requested or present, requiring - # dummy values in the latter case. We should figure - # out how to avoid requiring these dummy values by making - # the Boxes3D object take a dynamic set of attributes. - num_points = [0] * n - rotations = [0] * n - distances = [0] * n - if 'num_points' in self._breakdown_metrics: - num_points = self._breakdown_metrics['num_points'].Discretize( - result.groundtruth_num_points) - if 'rotation' in self._breakdown_metrics: - rotations = self._breakdown_metrics['rotation'].Discretize( - result.groundtruth_bboxes) - if 'distance' in self._breakdown_metrics: - distances = self._breakdown_metrics['distance'].Discretize( - result.groundtruth_bboxes) - - for label, bbox, difficulty, distance, num_points, rotations, speed in zip( - result.groundtruth_labels, result.groundtruth_bboxes, - result.groundtruth_difficulties, distances, num_points, rotations, - result.groundtruth_speed): - self._AddGroundtruth((str_id, label, 1., bbox, difficulty, distance, - num_points, rotations, speed)) - - c = result.detection_scores.shape[0] - assert c == self.metadata.NumClasses(), '%s vs. %s' % ( - c, self.metadata.NumClasses()) - - str_imgid = self._GetImageId(str_id) - - # Iterate first by class. - for class_id in range(1, c): - assert class_id > 0 and class_id < self.metadata.NumClasses(), ( - '{} vs. {}'.format(class_id, self.metadata.NumClasses())) - - # Get or create the box list for the class. This is done for speed -- we - # do it once per class to avoid dictionary lookups to fetch the field for - # every box. - boxes_for_class = self._prediction.get(class_id) - if boxes_for_class is None: - boxes_for_class = Boxes3D() - self._prediction[class_id] = boxes_for_class - - # Get boxes and scores for this class. - bboxes = result.detection_boxes[class_id, :] - scores = result.detection_scores[class_id, :] - heights_in_pixels = result.detection_heights_in_pixels[class_id, :] - # Select bboxes where scores > 0. - non_zero_bboxes = bboxes[scores > 0] - non_zero_scores = scores[scores > 0] - non_zero_heights_in_pixels = heights_in_pixels[scores > 0] - - rotations = [0] * len(non_zero_bboxes) - distances = [0] * len(non_zero_bboxes) - if 'distance' in self._breakdown_metrics: - # Compute all distances for non-zero-bboxes in one shot. - distances = self._breakdown_metrics['distance'].Discretize( - non_zero_bboxes) - if 'rotation' in self._breakdown_metrics: - rotations = self._breakdown_metrics['rotation'].Discretize( - non_zero_bboxes) - - # Add each box to the list. - # - # NOTE: The length of this loop can be large (e.g., for an early - # checkpoint), so any code inside of this for loop should be - # double-checked for efficiency. - dummy_speed = np.zeros((1, 2), dtype=np.float32) - for box_id in range(non_zero_bboxes.shape[0]): - boxes_for_class.Add( - img_id=str_imgid, - score=non_zero_scores[box_id], - box=non_zero_bboxes[box_id], - difficulty=0, - distance=distances[box_id], - num_points=0, - rotation=rotations[box_id], - height_in_pixels=non_zero_heights_in_pixels[box_id], - speed=dummy_speed) - - def _EvaluateIfNecessary(self): - """Evaluate all precision recall metrics.""" - if self._is_eval_complete: - return - compute_metrics_fn = functools.partial( - self._ComputeFinalMetrics, classids=self.metadata.EvalClassIndices()) - for metric_class in self._breakdown_metrics.values(): - metric_class.ComputeMetrics(compute_metrics_fn) - self._is_eval_complete = True - - @property - def value(self): - if self.params.metric_weights is None: - # Choose a backwards compatible default. The default assumes: - # - # if KITTI, metric_weights is all zeros except for car/moderate. - # - # if not KITTI, metric_weights are all equal. - metric_weights = {} - keys = list(self.metadata.DifficultyLevels().keys()) - for difficulty in keys: - if 'moderate' in keys and len(keys) == 3: - # The KITTI case. - if difficulty == 'moderate': - metric_weights[difficulty] = np.array([1., 0., 0.]) - else: - metric_weights[difficulty] = np.array([0., 0., 0.]) - else: - # Every difficulty and every class is weighted equally. - metric_weights[difficulty] = np.array( - [1.] * len(self.metadata.EvalClassIndices())) - else: - metric_weights = self.params.metric_weights - - # Compute weighted average of AP scores across all classes and difficulties. - num_sum = 0.0 - denom_sum = 0.0 - for difficulty, value in self._AveragePrecisionByDifficulty().items(): - metric_weight = metric_weights[difficulty] - denom = np.sum(metric_weight) - if denom == 0.0: - continue - num_sum += np.sum(np.nan_to_num(value) * metric_weight) - denom_sum += denom - - # Ensure at least some weights are specified - assert denom_sum > 0., 'All AP metric weights were 0!' - return num_sum / denom_sum - - def _AveragePrecisionByDifficulty(self): - """Special case to identify mAP versus difficulty.""" - self._EvaluateIfNecessary() - return self._breakdown_metrics['difficulty']._average_precisions # pylint:disable=protected-access - - def Summary(self, name): - self._EvaluateIfNecessary() - - ret = tf.Summary() - - # Put '.value' first (so it shows up in logs / summaries, etc). - ret.value.add(tag='{}/weighted_mAP'.format(name), simple_value=self.value) - - average_precision_by_difficulty = self._AveragePrecisionByDifficulty() - for i, j in enumerate(self.metadata.EvalClassIndices()): - classname = self.metadata.ClassNames()[j] - for difficulty in self.metadata.DifficultyLevels(): - tag_str = '{}/{}/AP_{}'.format(name, classname, difficulty) - ap_value = average_precision_by_difficulty[difficulty][i] - ret.value.add(tag=tag_str, simple_value=ap_value) - - for metric_class in self._breakdown_metrics.values(): - image_summaries = metric_class.GenerateSummaries(name) - for image_summary in image_summaries: - ret.value.extend(image_summary.value) - return ret diff --git a/lingvo/tasks/car/base_decoder.py b/lingvo/tasks/car/base_decoder.py deleted file mode 100644 index ab0ea8f3..00000000 --- a/lingvo/tasks/car/base_decoder.py +++ /dev/null @@ -1,89 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Base models for point-cloud based detection.""" - -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.core.ops import record_pb2 - - -class BaseDecoder(base_layer.BaseLayer): - """A decoder to use for decoding a detector model.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'summarize_boxes_on_image', False, - 'If true, enable the summary metric that projects bounding boxes ' - 'to the camera image to view predictions from camera view.') - - p.Define('ap_metric', None, 'Configuration of AP metric for decoding.') - - p.Define( - 'laser_sampling_rate', 0.05, - 'Rate at which real laser outputs are added to decoder output. ' - 'Because the laser outputs are large, we only want to output ' - 'the lasers on a small number of the batches.') - return p - - def _SampleLaserForVisualization(self, points_xyz, points_padding): - """Samples laser points based on configured laser_sampling_rate. - - Args: - points_xyz: [batch, num_points, 3] float Tensor. - points_padding: [batch, num_points] float Tensor. - - Returns: - .NestedMap: - - - points_xyz: 0.0 or points_xyz float Tensor passthrough. - - points_padding: 0.0 or points_padding float Tensor passthrough. - - points_sampled: scalar bool Tensor if points were sampled. - If false, points_xyz and points_padding are scalar 0s to - reduce the amount of data transferred. - """ - p = self.params - rand = tf.random.uniform(shape=(), minval=0, maxval=1, dtype=tf.float32) - sample_laser = rand < p.laser_sampling_rate - points_xyz, points_padding = tf.cond( - sample_laser, lambda: (points_xyz, points_padding), lambda: (0.0, 0.0)) - return py_utils.NestedMap({ - 'points_xyz': points_xyz, - 'points_padding': points_padding, - 'points_sampled': sample_laser - }) - - def SaveTensors(self, tensor_map): - """Returns a serialized representation of the contents of `tensor_map`. - - Args: - tensor_map: A NestedMap of string keys to numpy arrays. - - Returns: - A serialized record_pb2.Record() of the contents of 'tensor_map'. - """ - - def AddKeyVals(**kwargs): - record = record_pb2.Record() - for k, v in kwargs.items(): - record.fields[k].CopyFrom(tf.make_tensor_proto(v)) - return record - - records = AddKeyVals(**tensor_map) - serialized = records.SerializeToString() - return serialized diff --git a/lingvo/tasks/car/base_extractor.py b/lingvo/tasks/car/base_extractor.py deleted file mode 100644 index ac07f73b..00000000 --- a/lingvo/tasks/car/base_extractor.py +++ /dev/null @@ -1,308 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Base extractor interface.""" - -from lingvo import compat as tf -from lingvo.core import base_input_generator -from lingvo.core import datasource -from lingvo.core import generic_input -from lingvo.core import hyperparams -from lingvo.core import py_utils - -# Items exceeding this value will be dropped and not sent to the trainer. -BUCKET_UPPER_BOUND = 9999 - - -def _ParseSequenceExample(record, feature_map, context_map): - """Parse a SequenceExample, adding the context features to the features.""" - context, features = tf.io.parse_single_sequence_example( - serialized=record, - context_features=context_map, - sequence_features=feature_map) - # Add all keys from context to features. Keys must not overlap. - common_keys = set(context.keys()) & set(features.keys()) - if common_keys: - raise ValueError( - 'Keys {} are present in context and features.'.format(common_keys)) - features.update(context) - return features - - -def _TextInput(record, feature_map): - # record is a Tensor containing a string line. - if feature_map: - raise ValueError('For PlainText datasets, FeatureMap() must be empty.') - return {'line': record} - - -# Supported raw record types and the corresponding parsing functions. -_PARSING_FUNCTIONS = { - 'EXAMPLE': tf.io.parse_single_example, - 'SEQUENCE_EXAMPLE': _ParseSequenceExample, - 'TEXT': _TextInput, -} - - -class _BaseExtractor(base_input_generator.BaseInputGeneratorFromFiles): - """The base extractor for all V06+-derived Minecraft datasets. - - Subclasses should define and pass in a custom dictionary of extractors to - select which fields from V06+ datasets to output from an input - generator. - - Preprocessors are applied to all the extracted outputs jointly, in the - specified sequence. - """ - - @classmethod - def Params(cls, extractors): - """Defaults params. - - Args: - extractors: An hyperparams.Params of extractor names to Extractors. A few - extractor types are *required*: - 'labels': A LabelExtractor.Params(). - - Returns: - A base_layer Params object. - """ - p = super().Params() - p.Define('extractors', extractors, - 'A hyperparams.Params() of FieldsExtractors.') - p.Define('preprocessors', hyperparams.Params(), - 'A Params() of Preprocessors.') - p.Define( - 'preprocessors_order', [], - 'A list corresponding to flattened keys in preprocessors ' - 'Params(). This specifies the execution order of the ' - 'preprocessors.') - p.Define('record_type', 'EXAMPLE', - 'Raw record format, default to tf.Example.') - - p.batch_size = 64 - p.use_per_host_infeed = True - p.file_random_seed = 0 - - p.file_datasource = datasource.SimpleDataSource.Params() - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - # Instantiate every extractor as a child layer. - self._extractors = py_utils.NestedMap() - for (name, eparam) in p.extractors.IterParams(): - name = name.replace('.', '_') - self.CreateChild(name, eparam) - self._extractors[name] = self.children[name] - - # Instantiate preprocessors based on their ordering. - flattened_processors = dict(p.preprocessors.IterParams()) - - # Validate that all keys in preprocessors_order appear are valid. - if not set(p.preprocessors_order).issubset( - list(flattened_processors.keys())): - raise ValueError( - 'preprocessor_order specifies keys which were not found in ' - 'preprocessors. preprocessors_order={} preprocessors keys={}'.format( - p.preprocessors_order, list(flattened_processors.keys()))) - - preprocessors = [flattened_processors[key] for key in p.preprocessors_order] - self.CreateChildren('preprocessors', preprocessors) - - dtypes = self.DType() - shapes = self.Shape() - if not dtypes.IsCompatible(shapes): - raise ValueError('{} vs. {}'.format(dtypes.DebugString(), - shapes.DebugString())) - dtypes.Pack(list(zip(dtypes.Flatten(), - shapes.Flatten()))).VLog(0, 'InpGen: ') - - def FeatureMap(self): - """Get a mapping from feature names to feature tensors.""" - feature_map = {} - self._extractors.Transform(lambda e: feature_map.update(e.FeatureMap())) - return feature_map - - def ContextMap(self): - """Get a mapping from context names to context tensors.""" - context_map = {} - self._extractors.Transform(lambda e: context_map.update(e.ContextMap())) - return context_map - - def Shape(self): - shapes = self._extractors.Transform(lambda x: x.Shape()) - for preprocessor in self.preprocessors: - shapes = preprocessor.TransformShapes(shapes) - return shapes - - def DType(self): - dtypes = self._extractors.Transform(lambda x: x.DType()) - for preprocessor in self.preprocessors: - dtypes = preprocessor.TransformDTypes(dtypes) - return dtypes - - @property - def class_names(self): - raise NotImplementedError('Return a list of class names strings.') - - def _DataSourceFromFilePattern(self, file_pattern): - - def Proc(record): - """Parses a serialized tf.Example record.""" - bucket, outputs = self.ExtractUsingExtractors(record) - return outputs.Flatten(), bucket - - # Ensure buckets above BUCKET_UPPER_BOUND are dropped. - args = self.CommonInputOpArgs() - args['bucket_upper_bound'] = [BUCKET_UPPER_BOUND - 1] - return generic_input.GenericInput( - processor=Proc, file_pattern=file_pattern, **args) - - def ProcessFeatures(self, features): - """Process extracted features. - - Args: - features: A dict of extracted Tensors from the records. - - Returns: - A tuple of tensors: - - - bucket_id: A scalar int Tensor. - - extracted: a NestedMap of Tensors extracted. - """ - def ExtractAndFilter(e): - with tf.name_scope(e.params.name): - with tf.name_scope('extract'): - extracted = e.Extract(features) - with tf.name_scope('filter'): - bucket = e.Filter(extracted) - return bucket, extracted - - bucket_extracted = self._extractors.Transform(ExtractAndFilter) - buckets = bucket_extracted.Transform(lambda x: x[0]) - extracted = bucket_extracted.Transform(lambda x: x[1]) - - # Return the maximum bucket id so that any extractor can decide whether - # to filter the entire example. - max_bucket = tf.reduce_max(buckets.Flatten()) - - def NullLike(): - """A function to return the same Tensor signature as Preprocess. - - This is necessary for the tf.cond() to avoid executing the preprocessor - for examples that are going to be dropped because it exceeds the bucket - limit; tf.cond() requires that the output of both branches yields the same - structure. - - Returns: - A structure with the same Tensor dtype as the output of - Preprocess. - """ - shapes = self.Shape() - rets = [] - for dtype, shape in zip(self.DType().Flatten(), shapes.Flatten()): - if shape.is_fully_defined(): - rets += [tf.zeros(dtype=dtype, shape=shape)] - else: - rets += [tf.zeros(dtype=dtype, shape=[])] # Our best guess. - return shapes.Pack(rets) - - def Preprocess(extracted): - for key, preprocessor in zip(self.params.preprocessors_order, - self.preprocessors): - with tf.name_scope(key), tf.name_scope(preprocessor.params.name): - extracted = preprocessor.TransformFeatures(extracted) - return extracted - - # If the extractor wants to filter the example, don't run the preprocessor. - # - # Preprocessors can then assume that only examples that pass filtering will - # be executed. - # - # Note that the NullLike branch may return tensors with shapes different - # from self.Shape(). - final_output = tf.cond( - tf.less(max_bucket, BUCKET_UPPER_BOUND), lambda: Preprocess(extracted), - NullLike) - - return max_bucket, final_output - - def ExtractUsingExtractors(self, record): - """Extracts Tensors from a tf.Example record using self.extractors. - - Args: - record: A tf.Example input to pass to tf.io.parse_single_example. - - Returns: - A tuple of tensors: - - - bucket_id: A scalar int Tensor. - - extracted: a NestedMap of Tensors extracted. - """ - if self.params.record_type not in _PARSING_FUNCTIONS: - raise ValueError('Invalid record_type: {}'.format( - self.params.record_type)) - - parsing_fn = _PARSING_FUNCTIONS[self.params.record_type] - if self.params.record_type == 'SEQUENCE_EXAMPLE': - features = parsing_fn(record, self.FeatureMap(), self.ContextMap()) - else: - features = parsing_fn(record, self.FeatureMap()) - - return self.ProcessFeatures(features) - - def _InputBatch(self): - batched_outputs, bucket_keys = self._BuildDataSource() - ret = self._NestedMapFromBatchedOutputs(batched_outputs) - ret.bucket_keys = bucket_keys - return ret - - def _NestedMapFromBatchedOutputs(self, outputs): - """Create a NestedMap from a tuple of outputs from generic_input_op.""" - batch_size = self.InfeedBatchSize() - shapes = self.Shape() - shapes.VLog(0, 'input extractor shape: ') - flatten_shapes = shapes.Flatten() - dtypes = self.DType() - flatten_dtypes = dtypes.FlattenItems() - assert len(flatten_shapes) == len(outputs), '{} vs. {}'.format( - len(flatten_shapes), len(outputs)) - assert len(flatten_dtypes) == len(outputs), '{} vs. {}'.format( - len(flatten_dtypes), len(outputs)) - - rets = [] - for (output, (name, dtype), shape) in zip(outputs, flatten_dtypes, - flatten_shapes): - assert dtype == output.dtype, '{}: {} vs. {}'.format( - name, dtype, output.dtype) - # Pad every output to make shapes fixed according to the corresponding - # declared shape, since the shapes of outputs are lost through - # generic_input_op. - try: - shape.assert_is_fully_defined() - except ValueError as e: - raise ValueError('Invalid shape for %s: %s' % (name, e)) - padded = py_utils.PadOrTrimTo(output, [batch_size] + shape.as_list()) - rets += [padded] - - rets = shapes.Pack(rets) - if py_utils.use_tpu(): - # Drops tf.string tensors, which is not supported on TPUs. - rets = rets.Filter(lambda x: x.dtype != tf.string) - return rets diff --git a/lingvo/tasks/car/breakdown_metric.py b/lingvo/tasks/car/breakdown_metric.py deleted file mode 100644 index 60666b29..00000000 --- a/lingvo/tasks/car/breakdown_metric.py +++ /dev/null @@ -1,778 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Library for calculating precision recall conditioned on a variate. - -ByDistance: Calculate precision recall based on distance in world coordinates. -ByRotation: Calculate precision recall based on rotation in world coordinates. -ByNumPoints: Calculate maximum recall based on number of points in bounding box. -ByDifficulty: Calculate precision recall based on difficulty. -""" - -from lingvo import compat as tf -from lingvo.core import hyperparams -from lingvo.core import plot -from lingvo.tasks.car import calibration_processing as calibration -import numpy as np - - -def _FindRecallAtGivenPrecision(precision_recall, precision_level): - """Find the recall at given precision level. - - Args: - precision_recall: np.array of shape [n, m, 2] where n is the number of - classes, m is then number of values in the curve, 2 indexes between - precision [0] and recall [1]. np.float32. - precision_level: float32. Selected precision level between (0, 1). - Typically, this may be 0.95. - - Returns: - recall: np.array of shape [n] consisting of recall for all classes - where the values are 0.0 if a given precision is never achieved. - """ - # The method for computing precision-recall inserts precision = 0.0 - # when a particular recall value has not been achieved. The maximum - # recall value is therefore the highest recall value when the associated - # precision > 0. - assert len(precision_recall.shape) == 3, 'Invalid precision recall curve.' - assert precision_recall.shape[-1] == 2, 'Invalid precision recall curve.' - assert precision_level > 0.0, 'Precision must be greater then 0.' - assert precision_level < 1.0, 'Precision must be less then 1.' - num_classes = precision_recall.shape[0] - - recall = np.zeros(shape=(num_classes), dtype=np.float32) - for i in range(num_classes): - precisions = precision_recall[i, :, 0] - recalls = precision_recall[i, :, 1] - indices_at_precision_level = np.flatnonzero(precisions >= precision_level) - if indices_at_precision_level.size > 0: - recall[i] = np.max(recalls[indices_at_precision_level]) - return recall - - -def _FindMaximumRecall(precision_recall): - """Find the maximum recall in all precision recall curves. - - Args: - precision_recall: np.array of shape [n, m, 2] where n is the number of - classes, m is then number of values in the curve, 2 indexes between - precision [0] and recall [1]. np.float32. - - Returns: - max_recall: np.array of shape [n] consisting of max recall for all classes - where the values are 0.0 if objects are found. - """ - # The method for computing precision-recall inserts precision = 0.0 - # when a particular recall value has not been achieved. The maximum - # recall value is therefore the highest recall value when the associated - # precision > 0. - assert len(precision_recall.shape) == 3, 'Invalid precision recall curve.' - assert precision_recall.shape[-1] == 2, 'Invalid precision recall curve.' - num_classes = precision_recall.shape[0] - - max_recall = np.zeros(shape=(num_classes), dtype=np.float32) - - valid_precisions = precision_recall[:, :, 0] > 0.0 - for i in range(num_classes): - valid_precisions_indices = valid_precisions[i, :] - if np.any(valid_precisions_indices): - max_recall[i] = np.max(precision_recall[i, valid_precisions_indices, 1]) - return max_recall - - -class BreakdownMetric: - """Base class for calculating precision recall conditioned on a variate.""" - - @classmethod - def Params(cls): - p = hyperparams.Params() - p.Define( - 'metadata', None, - 'Class obeying EvaluationMetadata interface consisting of ' - 'parameters specifying the details of the evaluation.') - return p - - def __init__(self, p): - self.params = p - assert p.metadata is not None - self._histogram = np.zeros( - shape=(self.NumBinsOfHistogram(), p.metadata.NumClasses()), - dtype=np.int32) - self._values = np.zeros( - shape=(self.NumBinsOfHistogram(), 1), dtype=np.float32) - self._cumulative_distribution = {} - for l in range(p.metadata.NumClasses()): - self._cumulative_distribution[l] = [] - self._average_precisions = {} - self._precision_recall = {} - self._calibration = {} - self._classnames = p.metadata.ClassNames() - self._classids = p.metadata.EvalClassIndices() - - # We only want to calculate calibration for the lowest difficulty setting - # which will include *all* data. Including all data is due to the way - # _GetData() works in the kitti_ap_metric. - difficulties = p.metadata.DifficultyLevels() - lowest_difficulty_str = min(difficulties, key=lambda k: difficulties[k]) - self._calibration[lowest_difficulty_str] = ( - calibration.CalibrationCalculator(p.metadata)) - - def NumBinsOfHistogram(self): - """Returns int32 of number of bins in histogram.""" - return NotImplementedError() - - def ComputeMetrics(self, compute_metrics_fn): - """Compute precision-recall analysis conditioned on particular metric. - - Args: - compute_metrics_fn: Function that that calculates precision-recall metrics - and accepts named arguments for conditioning. Typically, this would be - APMetrics._ComputeFinalMetrics(). - - Returns: - nothing - """ - del compute_metrics_fn - return NotImplementedError() - - def GenerateSummaries(self, name): - """Generate list of image summaries plotting precision-recall analysis. - - Args: - name: string providing scope - - Returns: - list of image summaries - """ - del name - return NotImplementedError() - - def Discretize(self, values): - """Discretize statistics into integer values. - - Args: - values: 1-D np.array of variate to be discretized. - - Returns: - 1-D np.array of int32 ranging within [0, cls.NumOfBinsOfHistogram()] - """ - del values - return NotImplementedError() - - def _AccumulateHistogram(self, statistics=None, labels=None): - """Accumulate histogram of binned statistic by label. - - Args: - statistics: int32 np.array of shape [K, 1] of binned statistic - labels: int32 np.array of shape [K, 1] of labels - - Returns: - nothing - """ - assert np.issubdtype(statistics.dtype, np.integer) - if not statistics.size: - return - p = self.params - assert np.max(statistics) < self._histogram.shape[0], ( - 'Histogram shape too small %d vs %d' % - (np.max(statistics), self._histogram.shape[0])) - for l in range(p.metadata.NumClasses()): - indices = np.where(labels == l)[0] - for s in statistics[indices]: - self._histogram[s, l] += 1 - - def _AccumulateCumulative(self, statistics=None, labels=None): - """Accumulate cumulative of real-valued statistic by label. - - Args: - statistics: float32 np.array of shape [K, 1] of statistic - labels: int32 np.array of shape [K, 1] of labels - - Returns: - nothing - """ - p = self.params - for l in range(p.metadata.NumClasses()): - indices = np.where(labels == l)[0] - if indices.size: - self._cumulative_distribution[l].extend(statistics[indices].tolist()) - - def AccumulateCumulative(self, result): - """Accumulate cumulative of real-valued statistic by label. - - Args: - result: A NestedMap with these fields: - .labels: [N]. Groundtruth boxes' labels. - .bboxes: [N, 7]. Groundtruth boxes coordinates. - .difficulties: [N]. Groundtruth boxes difficulties. - .num_points: [N]. Number of laser points in bounding boxes. - - Returns: - nothing - """ - pass - - -def ByName(breakdown_metric_name): - """Return a BreakdownMetric class by name.""" - breakdown_mapping = { - 'distance': ByDistance, - 'num_points': ByNumPoints, - 'rotation': ByRotation, - 'difficulty': ByDifficulty - } - if breakdown_metric_name not in breakdown_mapping: - raise ValueError('Invalid breakdown name: %s, valid names are %s' % - (breakdown_metric_name, list(breakdown_mapping.keys()))) - return breakdown_mapping[breakdown_metric_name] - - -class ByDistance(BreakdownMetric): - """Calculate average precision as function of distance.""" - - def NumBinsOfHistogram(self): - p = self.params - return int( - np.rint(p.metadata.MaximumDistance() / p.metadata.DistanceBinWidth())) - - @classmethod - def _CalculateEuclideanDistanceFromOrigin(cls, bboxes): - """Calculate the Euclidean distance from the origin for each bounding box. - - Note that the LabelsExtractor originally returns groundtruth_bboxes of - shape [N, 7] where N is the number of bounding boxes. The last axis is - ordered [x, y, z, w, h, d, phi]. Hence, the Euclidean distance to the origin - is the L2 norm of the first 3 entries. - - Args: - bboxes: [N, 7] np.float of N bounding boxes. See details above. - - Returns: - np.array [N] of Euclidean distances. - """ - positions = bboxes[:, :3] - # Note that we employ axis=1 to compute the norm over [x, y, z] for each - # of N bounding boxes. - return np.linalg.norm(positions, axis=1, keepdims=False) - - def Discretize(self, bboxes): - p = self.params - distances = self._CalculateEuclideanDistanceFromOrigin(bboxes) - distances_binned = np.digitize( - distances, - np.arange(0.0, p.metadata.MaximumDistance(), - p.metadata.DistanceBinWidth())) - # index == 0 corresponds to distances outside less than 0.0. Since this is - # not possible, we discard this possibility and make the output 0 indexed to - # match the behavior of np.histogram(). - assert np.all(distances_binned > 0.0), 'Euclidean distance is negative.' - distances_binned -= 1 - return distances_binned - - def AccumulateHistogram(self, result): - distances = self.Discretize(result.bboxes) - self._AccumulateHistogram(statistics=distances, labels=result.labels) - - def ComputeMetrics(self, compute_metrics_fn): - tf.logging.info('Calculating by distance: start') - p = self.params - for d in range(self.NumBinsOfHistogram()): - value_at_histogram = ( - d * p.metadata.DistanceBinWidth() + - p.metadata.DistanceBinWidth() / 2.0) - self._values[d] = value_at_histogram - - metrics = compute_metrics_fn(distance=d) - curves = metrics['curves'] - self._precision_recall[d] = np.array([c['pr'] for c in curves]) - assert len(self._values) == len(list(self._precision_recall.keys())) - tf.logging.info('Calculating by distance: finished') - - def GenerateSummaries(self, name): - """Generate an image summary for AP versus distance by class.""" - p = self.params - - legend_names = [] - for j in p.metadata.EvalClassIndices(): - legend_names.append(p.metadata.ClassNames()[j]) - - num_distances = self._values.shape[0] - ys = np.zeros( - shape=(num_distances, len(p.metadata.EvalClassIndices())), - dtype=np.float32) - - for dist in self._precision_recall: - ys[dist, :] = _FindMaximumRecall(self._precision_recall[dist]) - - def _Setter(fig, axes): - """Configure the plot for mAP versus distance.""" - axes.grid(b=True) - fontsize = 14 - for i, j in enumerate(p.metadata.EvalClassIndices()): - for d, x in enumerate(self._values): - h = self._histogram[d][j] - y = min(ys[d][i] + 0.03, 1.0) - if h > 0: - text_label = '{} {}s'.format(h, legend_names[i].lower()[:3]) - axes.text(x, y, text_label, fontdict={'fontsize': fontsize - 2}) - - axes.set_xlabel('distance (world coordinates)', fontsize=fontsize) - axes.set_xticks( - np.arange( - 0.0, - p.metadata.MaximumDistance() + p.metadata.DistanceBinWidth(), - p.metadata.DistanceBinWidth())) - axes.set_ylabel('maximum recall', fontsize=fontsize) - axes.set_ylim([-0.01, 1.05]) - axes.set_yticks(np.arange(0.0, 1.05, 0.1)) - axes.legend([name.lower() for name in legend_names], numpoints=1, loc=3) - fig.tight_layout() - - tag_str = '{}/recall_by_distance'.format(name) - image_summary = plot.Curve( - name=tag_str, - figsize=(10, 8), - xs=self._values, - ys=ys, - setter=_Setter, - marker='.', - markersize=14, - linestyle='-', - linewidth=2, - alpha=0.5) - return [image_summary] - - -class ByNumPoints(BreakdownMetric): - """Calculate average precision as function of the number of points.""" - - def NumBinsOfHistogram(self): - return self.params.metadata.NumberOfPointsBins() - - def _LogSpacedBinEdgesofPoints(self): - p = self.params - return np.logspace( - np.log10(1.0), np.log10(p.metadata.MaximumNumberOfPoints()), - p.metadata.NumberOfPointsBins() + 1) - - def Discretize(self, num_points): - num_points_binned = np.digitize(num_points, - self._LogSpacedBinEdgesofPoints()) - # index == 0 corresponds to boxes with 0 points. Because we plot everything - # logarithmically, this is a pain in the buttocks. For simplicity, we merely - # accumulate the boxes with 0 points into the first bin. - num_points_binned[num_points_binned == 0] = 1 - num_bins = len(self._LogSpacedBinEdgesofPoints()) - # index == len(self._LogSpacedBinEdgesofPoints()) corresponds to - # points with to points outside of the range of the last edge. We map - # these points back to the final bucket for simplicity. - num_points_binned[num_points_binned == num_bins] -= 1 - # There is an inconsistency between how np.digitize() and np.histogram() - # index their bins and this is due to the fact that index == 0 is reserved - # for examples less than the minimum bin edge. - num_points_binned -= 1 - return num_points_binned - - def AccumulateHistogram(self, result): - num_points = self.Discretize(result.num_points) - self._AccumulateHistogram(statistics=num_points, labels=result.labels) - - def AccumulateCumulative(self, result): - self._AccumulateCumulative( - statistics=result.num_points, labels=result.labels) - - def ComputeMetrics(self, compute_metrics_fn): - tf.logging.info('Calculating by number of points: start') - # Note that we skip the last edge as the number of edges is one greater - # then the number of bins. - self._values = self._LogSpacedBinEdgesofPoints()[:-1] - for n, _ in enumerate(self._values): - metrics = compute_metrics_fn(num_points=n) - curves = metrics['curves'] - self._precision_recall[n] = np.array([c['pr'] for c in curves]) - assert len(self._values) == len(list(self._precision_recall.keys())) - tf.logging.info('Calculating by number of points: finished') - - def GenerateSummaries(self, name): - """Generate an image summary for max recall by number of points by class.""" - image_summaries = self._GenerateCumulativeSummaries(name) - p = self.params - - legend_names = [] - for j in p.metadata.EvalClassIndices(): - legend_names.append(p.metadata.ClassNames()[j]) - - num_points_bins = self._values.shape[0] - ys = np.zeros( - shape=(num_points_bins, len(p.metadata.EvalClassIndices())), - dtype=np.float32) - - for num_points in self._precision_recall: - ys[num_points, :] = _FindMaximumRecall(self._precision_recall[num_points]) - - def _Setter(fig, axes): - """Configure the plot for max recall versus number of points.""" - axes.grid(b=True) - fontsize = 14 - for i, j in enumerate(p.metadata.EvalClassIndices()): - for n, x in enumerate(self._values): - h = self._histogram[n][j] - y = min(ys[n][i] + 0.03, 1.0) - if h > 0: - text_label = '{} {}s'.format(h, legend_names[i].lower()[:3]) - axes.text(x, y, text_label, fontdict={'fontsize': fontsize - 2}) - - axes.set_xlabel('number of points', fontsize=fontsize) - axes.set_xticks(self._values) - axes.set_ylabel('maximum recall', fontsize=fontsize) - axes.set_ylim([-0.01, 1.05]) - axes.set_xlim([(1.0 / 1.3) * self._values[0], 1.3 * self._values[-1]]) - axes.set_yticks(np.arange(0.0, 1.05, 0.1)) - axes.set_xscale('log') - axes.legend([name.lower() for name in legend_names], - numpoints=1, - loc='upper left') - fig.tight_layout() - - tag_str = '{}/recall_by_num_points'.format(name) - image_summary = plot.Curve( - name=tag_str, - figsize=(10, 8), - xs=self._values, - ys=ys, - setter=_Setter, - marker='.', - markersize=14, - linestyle='-', - linewidth=2, - alpha=0.5) - image_summaries.append(image_summary) - return image_summaries - - def _GenerateCumulativeSummaries(self, name): - """Generate an image summary for CDF of a variate.""" - xs = [] - ys = [] - num_zeros = [] - legend_names = [] - min_value = 5.0 - p = self.params - - for i, j in enumerate(p.metadata.EvalClassIndices()): - legend_names.append(p.metadata.ClassNames()[j]) - if len(self._cumulative_distribution[j]) > min_value: - self._cumulative_distribution[j].sort() - x = np.array(self._cumulative_distribution[j]) - nonzeros = np.flatnonzero(x) - cdf = np.arange(x.size).astype(np.float) / x.size - xs.append(x) - ys.append(cdf) - num_zeros.append(x.size - nonzeros.size) - else: - xs.append(None) - ys.append(None) - num_zeros.append(None) - - image_summaries = [] - for i, j in enumerate(p.metadata.EvalClassIndices()): - classname = p.metadata.ClassNames()[j] - - def _Setter(fig, axes): - """Configure the plot for CDF of the variate.""" - axes.grid(b=False) - fontsize = 14 - - axes.set_ylim([0, 1.05]) - axes.set_xlim([1.0, 11500]) - axes.set_ylabel('cumulative distribution', fontsize=fontsize) - axes.set_xlabel('number of points', fontsize=fontsize) - axes.set_xscale('log') - legend_text = '{} {}s ({} contain zero points)'.format( - xs[i].size, - p.metadata.ClassNames()[j].lower(), num_zeros[i]) - axes.legend({legend_text}, loc='upper left') - fig.tight_layout() - - if xs[i] is not None: - tag_str = '{}/{}/cdf_of_num_points'.format(name, classname) - image_summary = plot.Curve( - name=tag_str, - figsize=(10, 8), - xs=xs[i], - ys=ys[i], - setter=_Setter, - marker='', - linestyle='-', - linewidth=2, - alpha=0.5) - image_summaries.append(image_summary) - return image_summaries - - -class ByRotation(BreakdownMetric): - """Calculate average precision as function of rotation.""" - - def NumBinsOfHistogram(self): - return self.params.metadata.NumberOfRotationBins() - - def _CalculateRotation(self, bboxes): - """Calculate rotation angle mod between (0, 2 * pi) for each box. - - Args: - bboxes: [N, 7] np.float of N bounding boxes. See details above. - - Returns: - np.array [N] of rotation angles in radians. - """ - if not bboxes.size: - return np.empty_like(bboxes) - p = self.params - # Although groundtruth is constrained to be in [-pi, pi], predictions are - # unbounded. We map all predictions to their equivalent value in [-pi, pi]. - rotations = np.copy(bboxes[:, -1]) - rotations += np.pi - rotations = np.mod(rotations, 2.0 * np.pi) - rotations -= np.pi - # Now we remove ambiguity in 180 degree rotations as measured by our IOU - # calculations by mapping everything to [0, pi] range. - rotations = np.where(rotations > 0.0, rotations, rotations + np.pi) - # Floating numerical issues can surface occasionally particularly within - # subsequent binning. The clipping makes these operations reliable. - epsilon = 1e-5 - rotations = np.clip(rotations, epsilon, - p.metadata.MaximumRotation() - epsilon) - return rotations - - def Discretize(self, bboxes): - rotations = self._CalculateRotation(bboxes) - p = self.params - bin_width = ( - p.metadata.MaximumRotation() / float(self.NumBinsOfHistogram())) - # TODO(shlens): Consider merging the entries with -1 and 0 bin index - # because rotation is circular. - rotations_binned = np.digitize( - rotations, np.arange(0.0, p.metadata.MaximumRotation(), bin_width)) - # index == 0 corresponds to distances outside less than 0.0. Since this is - # not possible, we discard this possibility and make the output 0 indexed to - # match the behavior of np.histogram(). - assert np.all(rotations_binned > 0.0), ('Rotation is negative: %s' % - rotations_binned) - rotations_binned -= 1 - return rotations_binned - - def AccumulateHistogram(self, result): - rotations = self.Discretize(result.bboxes) - self._AccumulateHistogram(statistics=rotations, labels=result.labels) - - def ComputeMetrics(self, compute_metrics_fn): - tf.logging.info('Calculating by rotation: start') - p = self.params - self._values = np.zeros( - shape=(self.NumBinsOfHistogram(), 1), dtype=np.float32) - bin_width = ( - p.metadata.MaximumRotation() / float(self.NumBinsOfHistogram())) - for r in range(self.NumBinsOfHistogram()): - # Calculate the center of the histogram bin. - value_at_histogram = r * bin_width + bin_width / 2.0 - self._values[r] = value_at_histogram - metrics = compute_metrics_fn(rotation=r) - curves = metrics['curves'] - self._precision_recall[r] = np.array([c['pr'] for c in curves]) - assert len(self._values) == len(list(self._precision_recall.keys())) - tf.logging.info('Calculating by rotation: finished') - - def GenerateSummaries(self, name): - """Generate an image summary for AP versus rotation by class.""" - p = self.params - legend_names = [] - for j in p.metadata.EvalClassIndices(): - legend_names.append(p.metadata.ClassNames()[j]) - - num_rotations = self._values.shape[0] - rotation_in_degrees = self._values * 180.0 / np.pi - ys = np.zeros( - shape=(num_rotations, len(p.metadata.EvalClassIndices())), - dtype=np.float32) - - for rotation in self._precision_recall: - ys[rotation, :] = _FindMaximumRecall(self._precision_recall[rotation]) - - def _Setter(fig, axes): - """Configure the plot for max recall versus distance.""" - axes.grid(b=True) - fontsize = 14 - for i, j in enumerate(p.metadata.EvalClassIndices()): - for r, x in enumerate(rotation_in_degrees): - h = self._histogram[r][j] - y = min(ys[r][i] + 0.03, 1.0) - text_label = '{} {}s'.format(h, legend_names[i].lower()[:3]) - axes.text(x, y, text_label, fontdict={'fontsize': fontsize - 2}) - - axes.set_xlabel('rotation (degrees)', fontsize=fontsize) - bin_width = ( - p.metadata.MaximumRotation() / float(self.NumBinsOfHistogram())) - axes.set_xticks( - np.arange(0.0, - p.metadata.MaximumRotation() + bin_width, bin_width) * - 180.0 / np.pi) - axes.set_ylabel('maximum recall', fontsize=fontsize) - axes.set_ylim([-0.01, 1.05]) - axes.set_yticks(np.arange(0.0, 1.05, 0.1)) - axes.set_xlim([0.0, 180.0]) - axes.legend([name.lower() for name in legend_names], - numpoints=1, - loc='upper right') - fig.tight_layout() - - tag_str = '{}/recall_by_rotation'.format(name) - image_summary = plot.Curve( - name=tag_str, - figsize=(10, 8), - xs=rotation_in_degrees, - ys=ys, - setter=_Setter, - marker='.', - markersize=14, - linestyle='-', - linewidth=2, - alpha=0.5) - return [image_summary] - - -class ByDifficulty(BreakdownMetric): - """Calculate average precision as function of difficulty.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('ap_key', 'ap', 'Metrics key for the AP value.') - p.Define('pr_key', 'pr', 'Metrics key for the PR value.') - return p - - def NumBinsOfHistogram(self): - return len(self.params.metadata.DifficultyLevels()) + 1 - - def Discretize(self, difficulties): - return difficulties.astype(np.int32) - - def AccumulateHistogram(self, result): - difficulties = self.Discretize(result.difficulties) - self._AccumulateHistogram(statistics=difficulties, labels=result.labels) - - def ComputeMetrics(self, compute_metrics_fn): - p = self.params - tf.logging.info('Calculating by difficulty: start') - for difficulty in self.params.metadata.DifficultyLevels(): - metrics = compute_metrics_fn(difficulty=difficulty) - scalars = metrics['scalars'] - curves = metrics['curves'] - self._average_precisions[difficulty] = [s[p.ap_key] for s in scalars] - self._precision_recall[difficulty] = np.array( - [c[p.pr_key] for c in curves]) - - # Only KITTI metrics contains calibration data, - # And we only compute calibration for the lowest difficulty level - # to include all data. - if difficulty in self._calibration: - self._calibration[difficulty].Calculate(metrics) - - tf.logging.info('Calculating by difficulty: finished') - - def GenerateSummaries(self, name): - """Generate an image summary for PR by difficulty and for calibration. - - Args: - name: str, name of summary. - - Returns: - list of summaries - """ - - legend = {} - p = self.params - for class_id in p.metadata.EvalClassIndices(): - legend[class_id] = [] - for difficulty, i in p.metadata.DifficultyLevels().items(): - num_objects = self._histogram[i][class_id] - legend[class_id].append('%s (%d)' % (difficulty, num_objects)) - - summaries = [] - for i, j in enumerate(p.metadata.EvalClassIndices()): - - def _PRSetter(fig, axes): - """Configure the plot for precision recall.""" - ticks = np.arange(0, 1.05, 0.1) - axes.grid(b=False) - axes.set_xlabel('Recall') - axes.set_xticks(ticks) - axes.set_ylabel('Precision') - axes.set_yticks(ticks) - # pylint: disable=undefined-loop-variable - axes.legend(legend[j], numpoints=1) # pylint: disable=cell-var-from-loop - # pylint: enable=undefined-loop-variable - fig.tight_layout() - - classname = p.metadata.ClassNames()[j] - # Generate Precision-Recall curves. - rs = [] - ps = [] - for difficulty in p.metadata.DifficultyLevels(): - ps += [self._precision_recall[difficulty][i][:, 0]] - rs += [self._precision_recall[difficulty][i][:, 1]] - tag_str = '{}/{}/PR'.format(name, classname) - image_summary = plot.Curve( - name=tag_str, - figsize=(10, 8), - xs=rs[0], - ys=np.array(ps).T, - setter=_PRSetter, - marker='.', - markersize=14, - linestyle='-', - linewidth=2, - alpha=0.5) - summaries.append(image_summary) - - for difficulty, c in self._calibration.items(): - # Note that we only generate a calibration for a single difficulty level. - calibration_summaries = c.Summary(name) - for calibration_summary in calibration_summaries: - summaries.append(calibration_summary) - - # Generate scalar summaries for the various recalls for each difficulty. - for difficulty in p.metadata.DifficultyLevels(): - max_recall = _FindMaximumRecall(self._precision_recall[difficulty]) - for i, j in enumerate(p.metadata.EvalClassIndices()): - classname = p.metadata.ClassNames()[j] - summary = tf.Summary(value=[ - tf.Summary.Value( - tag='{}/{}/max_recall_{}'.format(name, classname, difficulty), - simple_value=max_recall[i]) - ]) - summaries.append(summary) - - for precision_level in p.metadata.RecallAtPrecision(): - recall_at_precision = _FindRecallAtGivenPrecision( - self._precision_recall[difficulty], precision_level) - for i, j in enumerate(p.metadata.EvalClassIndices()): - classname = p.metadata.ClassNames()[j] - summary = tf.Summary(value=[ - tf.Summary.Value( - tag='{}/{}/recall_{}_{}'.format( - name, classname, int(precision_level * 100), difficulty), - simple_value=recall_at_precision[i]) - ]) - summaries.append(summary) - return summaries diff --git a/lingvo/tasks/car/breakdown_metric_test.py b/lingvo/tasks/car/breakdown_metric_test.py deleted file mode 100644 index 2f95815c..00000000 --- a/lingvo/tasks/car/breakdown_metric_test.py +++ /dev/null @@ -1,418 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for breakdown_metric.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car import breakdown_metric -from lingvo.tasks.car import kitti_ap_metric -from lingvo.tasks.car import kitti_metadata -import numpy as np - -FLAGS = tf.flags.FLAGS - - -class BreakdownMetricTest(test_utils.TestCase): - - def _GenerateRandomBBoxes(self, num_bboxes): - xyz = np.random.uniform(low=-1.0, high=1.0, size=(num_bboxes, 3)) - dimension = np.random.uniform(low=-1, high=1.0, size=(num_bboxes, 3)) - rotation = np.random.uniform(low=-np.pi, high=np.pi, size=(num_bboxes, 1)) - bboxes = np.concatenate([xyz, dimension, rotation], axis=-1) - return bboxes - - def _GenerateBBoxesAtDistanceAndRotation(self, num_boxes, distance, rotation): - bboxes = np.zeros(shape=(num_boxes, 7)) - bboxes[:, -1] = rotation - bboxes[:, 0] = distance - return bboxes - - def _GenerateMetricsWithTestData(self, num_classes): - metadata = kitti_metadata.KITTIMetadata() - num_bins_of_distance = int( - np.rint(metadata.MaximumDistance() / metadata.DistanceBinWidth())) - num_bins_of_rotation = metadata.NumberOfRotationBins() - num_bins_of_points = metadata.NumberOfPointsBins() - - # Generate ground truth bounding boxes with prescribed labels, distances, - # rotations and number of points. - expected_objects_at_distance = np.random.randint( - low=0, high=8, size=(num_classes, num_bins_of_distance), dtype=np.int32) - expected_objects_at_rotation = np.zeros( - shape=(num_classes, num_bins_of_rotation), dtype=np.int32) - # Note that we need preserve the same number of objects for each label. - expected_objects_at_points = np.zeros( - shape=(num_classes, num_bins_of_points), dtype=np.int32) - prob = 1.0 / float(num_bins_of_points) - for c in range(num_classes): - num_objects_for_class = np.sum(expected_objects_at_distance[c, :]) - expected_objects_at_points[c, :] = np.random.multinomial( - num_objects_for_class, pvals=num_bins_of_points * [prob]) - # Zero out the number of boxes in the background class. - expected_objects_at_distance[0, :] = 0 - expected_objects_at_points[0, :] = 0 - expected_objects_at_rotation[0, :] = 0 - - bboxes = [] - labels = [] - num_points = [] - bin_width = ( - metadata.MaximumRotation() / float(metadata.NumberOfRotationBins())) - # Note that we always skip 'Background' class 0. - for label in range(1, num_classes): - for distance_index in range(num_bins_of_distance): - distance = ( - distance_index * metadata.DistanceBinWidth() + - metadata.DistanceBinWidth() / 2.0) - num_box = expected_objects_at_distance[label, distance_index] - - if num_box > 0: - rotation_index = np.random.randint(num_bins_of_rotation) - expected_objects_at_rotation[label, rotation_index] += num_box - rotation = rotation_index * bin_width + bin_width / 2.0 - - bboxes.append( - self._GenerateBBoxesAtDistanceAndRotation(num_box, distance, - rotation)) - labels.append(label * np.ones(shape=[num_box], dtype=np.int32)) - - point_bin_edges = np.logspace( - np.log10(1.0), np.log10(metadata.MaximumNumberOfPoints()), - metadata.NumberOfPointsBins() + 1) - for point_index in range(num_bins_of_points): - num_box = expected_objects_at_points[label, point_index] - for _ in range(num_box): - points = (point_bin_edges[point_index] + - point_bin_edges[point_index + 1]) / 2.0 - num_points.append([points]) - - bboxes = np.concatenate(bboxes) - labels = np.concatenate(labels) - num_points = np.concatenate(num_points) - - # Generate dummy predictions as placeholders for the API. - num_predictions = 9 - prediction_scores = np.random.uniform(size=[num_classes, num_predictions]) - prediction_bboxes = self._GenerateRandomBBoxes( - num_predictions * num_classes).reshape( - (num_classes, num_predictions, 7)) - - # Update the metrics. - metric_names = ['rotation', 'num_points', 'distance'] - ap_params = kitti_ap_metric.KITTIAPMetrics.Params(metadata).Set( - breakdown_metrics=metric_names) - metrics = ap_params.Instantiate() - metrics.Update( - 'dummy_image1', - py_utils.NestedMap( - groundtruth_labels=labels, - groundtruth_bboxes=bboxes, - groundtruth_difficulties=np.ones(shape=(bboxes.shape[0])), - groundtruth_num_points=num_points, - detection_scores=prediction_scores, - detection_boxes=prediction_bboxes, - detection_heights_in_pixels=np.ones( - shape=prediction_bboxes.shape[0:2]) * 100)) - - return py_utils.NestedMap( - metrics=metrics, - expected_objects_at_distance=expected_objects_at_distance, - expected_objects_at_points=expected_objects_at_points, - expected_objects_at_rotation=expected_objects_at_rotation) - - def testLoadBoundingBoxes(self): - # Test if all of the groundtruth data loads correctly for each label - # when no distance is specified. - metadata = kitti_metadata.KITTIMetadata() - num_classes = len(metadata.ClassNames()) - test_data = self._GenerateMetricsWithTestData(num_classes) - - expected_num_objects = np.sum( - test_data.expected_objects_at_distance, axis=1) - - # Note that we always skip 'Background' class 0. - for label in range(1, num_classes): - data = test_data.metrics._LoadBoundingBoxes( - 'groundtruth', label, distance=None) - - if expected_num_objects[label] == 0: - self.assertIsNone(data) - else: - self.assertEqual(expected_num_objects[label], len(data.boxes)) - self.assertEqual(expected_num_objects[label], len(data.imgids)) - self.assertEqual(expected_num_objects[label], len(data.scores)) - self.assertEqual(expected_num_objects[label], len(data.difficulties)) - - self.assertAllEqual( - np.ones(shape=[expected_num_objects[label]]), data.scores) - self.assertAllEqual( - np.zeros(shape=[expected_num_objects[label]]), data.imgids) - - def testLoadBoundingBoxesDifficulty(self): - metadata = kitti_metadata.KITTIMetadata() - num_classes = len(metadata.ClassNames()) - test_data = self._GenerateMetricsWithTestData(num_classes) - - expected_num_objects = np.sum( - test_data.expected_objects_at_distance, axis=1) - - difficulty_metric = test_data.metrics._breakdown_metrics['difficulty'] - - # Test if difficulties are properly accumulated. - for d in metadata.DifficultyLevels().values(): - if d == 1: - self.assertAllEqual(expected_num_objects, - difficulty_metric._histogram[d, :]) - else: - self.assertAllEqual( - np.zeros_like(expected_num_objects), - difficulty_metric._histogram[d, :]) - - def testLoadBoundingBoxesDistance(self): - # Test if all of the groundtruth data loads correctly for each label - # when distance is specified. - metadata = kitti_metadata.KITTIMetadata() - num_classes = len(metadata.ClassNames()) - test_data = self._GenerateMetricsWithTestData(num_classes) - num_bins_of_distance = int( - np.rint(metadata.MaximumDistance() / metadata.DistanceBinWidth())) - - distance_metric = test_data.metrics._breakdown_metrics['distance'] - - # Test if all of the groundtruth data loads correctly for each label - # when no distance is specified. - self.assertAllEqual(test_data.expected_objects_at_distance, - np.transpose(distance_metric._histogram)) - - # Note that we always skip 'Background' class 0. - for label in range(1, num_classes): - for distance in range(num_bins_of_distance): - data = test_data.metrics._LoadBoundingBoxes( - 'groundtruth', label, distance=distance) - - if test_data.expected_objects_at_distance[label, distance] == 0: - self.assertIsNone(data) - else: - self.assertEqual( - test_data.expected_objects_at_distance[label, distance], - len(data.boxes)) - self.assertEqual( - test_data.expected_objects_at_distance[label, distance], - len(data.imgids)) - self.assertEqual( - test_data.expected_objects_at_distance[label, distance], - len(data.scores)) - self.assertEqual( - test_data.expected_objects_at_distance[label, distance], - len(data.difficulties)) - - self.assertAllEqual( - np.ones(shape=[ - test_data.expected_objects_at_distance[label, distance] - ]), data.scores) - self.assertAllEqual( - np.zeros(shape=[ - test_data.expected_objects_at_distance[label, distance] - ]), data.imgids) - - def testLoadBoundingBoxesNumPoints(self): - # Test if all of the groundtruth data loads correctly for each label - # when number of points is specified. - metadata = kitti_metadata.KITTIMetadata() - num_classes = len(metadata.ClassNames()) - test_data = self._GenerateMetricsWithTestData(num_classes) - num_bins_of_points = metadata.NumberOfPointsBins() - - num_points_metric = test_data.metrics._breakdown_metrics['num_points'] - - self.assertAllEqual(test_data.expected_objects_at_points, - np.transpose(num_points_metric._histogram)) - - # Note that we always skip 'Background' class 0. - for label in range(1, num_classes): - for num_points in range(num_bins_of_points): - data = test_data.metrics._LoadBoundingBoxes( - 'groundtruth', label, num_points=num_points) - - if test_data.expected_objects_at_points[label, num_points] == 0: - self.assertIsNone(data) - else: - # Skip the first bin because it is a special case. - if num_points == 0: - continue - self.assertEqual( - test_data.expected_objects_at_points[label, num_points], - len(data.boxes)) - self.assertEqual( - test_data.expected_objects_at_points[label, num_points], - len(data.imgids)) - self.assertEqual( - test_data.expected_objects_at_points[label, num_points], - len(data.scores)) - self.assertEqual( - test_data.expected_objects_at_points[label, num_points], - len(data.difficulties)) - - self.assertAllEqual( - np.ones(shape=[ - test_data.expected_objects_at_points[label, num_points] - ]), data.scores) - self.assertAllEqual( - np.zeros(shape=[ - test_data.expected_objects_at_points[label, num_points] - ]), data.imgids) - - def testLoadBoundingBoxesRotation(self): - # Test if all of the groundtruth data loads correctly for each label - # when rotation is specified. - metadata = kitti_metadata.KITTIMetadata() - num_classes = len(metadata.ClassNames()) - test_data = self._GenerateMetricsWithTestData(num_classes) - num_bins_of_rotation = metadata.NumberOfRotationBins() - - rotation_metric = test_data.metrics._breakdown_metrics['rotation'] - - # Test if all of the groundtruth data loads correctly for each label - # when no distance is specified. - self.assertAllEqual(test_data.expected_objects_at_rotation, - np.transpose(rotation_metric._histogram)) - - # Note that we always skip 'Background' class 0. - for label in range(1, num_classes): - for rotation in range(num_bins_of_rotation): - data = test_data.metrics._LoadBoundingBoxes( - 'groundtruth', label, rotation=rotation) - - if test_data.expected_objects_at_rotation[label, rotation] == 0: - self.assertIsNone(data) - else: - self.assertEqual( - test_data.expected_objects_at_rotation[label, rotation], - len(data.boxes)) - self.assertEqual( - test_data.expected_objects_at_rotation[label, rotation], - len(data.imgids)) - self.assertEqual( - test_data.expected_objects_at_rotation[label, rotation], - len(data.scores)) - self.assertEqual( - test_data.expected_objects_at_rotation[label, rotation], - len(data.difficulties)) - - self.assertAllEqual( - np.ones(shape=[ - test_data.expected_objects_at_rotation[label, rotation] - ]), data.scores) - self.assertAllEqual( - np.zeros(shape=[ - test_data.expected_objects_at_rotation[label, rotation] - ]), data.imgids) - - def testAccumulateHistogram(self): - metadata = kitti_metadata.KITTIMetadata() - num_per_class = np.arange(metadata.NumClasses()) + 1 - statistics = [ - 1 * np.ones(shape=(np.sum(num_per_class)), dtype=np.int32), - 2 * np.ones(shape=(np.sum(2 * num_per_class)), dtype=np.int32) - ] - statistics = np.concatenate(statistics) - - labels = [] - for i, n in enumerate(num_per_class): - labels.extend([i] * n) - for i, n in enumerate(num_per_class): - labels.extend([i] * 2 * n) - labels = np.array(labels) - assert len(statistics) == len(labels) - - metrics_params = breakdown_metric.BreakdownMetric.Params().Set( - metadata=metadata) - test_breakdown_metric = breakdown_metric.ByDifficulty(metrics_params) - test_breakdown_metric._AccumulateHistogram( - statistics=statistics, labels=labels) - - for class_index, n in enumerate(num_per_class): - self.assertEqual(n, test_breakdown_metric._histogram[1, class_index]) - self.assertEqual(2 * n, test_breakdown_metric._histogram[2, class_index]) - - def testByName(self): - metric_class = breakdown_metric.ByName('difficulty') - self.assertEqual(metric_class, breakdown_metric.ByDifficulty) - with self.assertRaises(ValueError): - breakdown_metric.ByName('undefined') - - def testFindMaximumRecall(self): - # The shape of the precision_recall_curves is [n, m, 2] where n is the - # number of classes, m is then number of values in the curve, 2 indexes - # between precision [0] and recall [1]. - car = np.transpose( - np.array( - [[0.9, 0.7, 0.5, 0.1, 0.0, 0.0], [0.0, 0.2, 0.5, 0.9, 1.0, 1.0]], - dtype=np.float32)) - ped = np.transpose( - np.array( - [[0.9, 0.7, 0.5, 0.0, 0.0, 0.0], [0.0, 0.2, 0.5, 0.9, 1.0, 1.0]], - dtype=np.float32)) - cyc = np.transpose( - np.array( - [[0.9, 0.7, 0.0, 0.0, 0.0, 0.0], [0.0, 0.2, 0.5, 0.9, 1.0, 1.0]], - dtype=np.float32)) - foo = np.transpose( - np.array( - [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.2, 0.5, 0.9, 1.0, 1.0]], - dtype=np.float32)) - precision_recall_curves = np.stack([car, ped, cyc, foo]) - max_recall = breakdown_metric._FindMaximumRecall(precision_recall_curves) - self.assertAllEqual([4], max_recall.shape) - self.assertNear(0.9, max_recall[0], 1e-7) - self.assertNear(0.5, max_recall[1], 1e-7) - self.assertNear(0.2, max_recall[2], 1e-7) - self.assertNear(0.0, max_recall[3], 1e-7) - - def testFindRecallAtGivenPrecision(self): - # The shape of the precision_recall_curves is [n, m, 2] where n is the - # number of classes, m is then number of values in the curve, 2 indexes - # between precision [0] and recall [1]. - car = np.transpose( - np.array( - [[0.9, 0.7, 0.5, 0.1, 0.0, 0.0], [0.0, 0.2, 0.5, 0.9, 1.0, 1.0]], - dtype=np.float32)) - ped = np.transpose( - np.array( - [[0.9, 0.7, 0.5, 0.0, 0.0, 0.0], [0.0, 0.2, 0.5, 0.9, 1.0, 1.0]], - dtype=np.float32)) - cyc = np.transpose( - np.array( - [[0.9, 0.7, 0.0, 0.0, 0.0, 0.0], [0.0, 0.2, 0.5, 0.9, 1.0, 1.0]], - dtype=np.float32)) - foo = np.transpose( - np.array( - [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.2, 0.5, 0.9, 1.0, 1.0]], - dtype=np.float32)) - precision_recall_curves = np.stack([car, ped, cyc, foo]) - precision_level = 0.5 - recall = breakdown_metric._FindRecallAtGivenPrecision( - precision_recall_curves, precision_level) - self.assertAllEqual([4], recall.shape) - self.assertNear(0.5, recall[0], 1e-7) - self.assertNear(0.5, recall[1], 1e-7) - self.assertNear(0.2, recall[2], 1e-7) - self.assertNear(0.0, recall[3], 1e-7) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/builder_lib.py b/lingvo/tasks/car/builder_lib.py deleted file mode 100644 index 05b6ce44..00000000 --- a/lingvo/tasks/car/builder_lib.py +++ /dev/null @@ -1,1151 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""A collection of helper functions to build lingvo layer params.""" - -import functools -import math - -from lingvo import compat as tf -from lingvo.core import builder_layers -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.tasks.car import car_layers - -import numpy as np - -# Keys for NestedMap for points: points, features, and padding. -POINTS_KEY = 'points' -FEATURES_KEY = 'features' -PADDING_KEY = 'padding' - -# Keys for NestedMap for range images: xyz, features, and mask. -XYZ_KEY = 'xyz' -MASK_KEY = 'mask' - - -################################################################################ -# Initializations -################################################################################ -def TruncatedGaussianInit(filter_shape): - factor = 2.0 - trunc_stddev = math.sqrt(1.3 * factor / filter_shape[-1]) - return py_utils.WeightInit.TruncatedGaussian(scale=trunc_stddev) - - -def KaimingUniformFanInRelu(shape): - del shape - return py_utils.WeightInit.KaimingUniformFanInRelu() - - -################################################################################ -# Lingvo Layer Builders -################################################################################ -# pyformat: disable -class ModelBuilderBase: - """Model builder with commonly used layers.""" - - def __init__(self): - self.conv_init_method = TruncatedGaussianInit - self.linear_params_init = None - self.bn_params_init = None - self.activation_fn = tf.nn.relu - self.fc_bn_after_linear = False - - def _Rep(self, name, repeat, *subs): - """Helper to construct a sequential layer repeated several time.""" - return builder_layers.SequentialLayer.Params().Set( - name=name, repeat=repeat, sub=list(subs)) - - def _Seq(self, name, *subs): - return builder_layers.SequentialLayer.Params().Set( - name=name, repeat=1, sub=list(subs)) - - def _Branch(self, name, body, fetches): - return builder_layers.BranchLayer.Params().Set( - name=name, body=body, fetches=fetches) - - def _BN(self, name, dims): - bn_params = layers.BatchNormLayer.Params().Set( - name=name, - dim=dims, - decay=0.99, - # TODO(b/148537111): consider setting this to True. - add_stats_to_moving_average_variables=False) - if self.bn_params_init: - bn_params = bn_params.Set(params_init=self.bn_params_init) - return bn_params - - def _Linear(self, name, idims, odims, params_init=None): - linear_params = builder_layers.LinearLayer.Params().Set( - name=name, input_dims=idims, output_dims=odims) - - params_init = params_init or self.linear_params_init - if params_init is not None: - linear_params = linear_params.Set(params_init=params_init) - - return linear_params - - def _Bias(self, name, dims, params_init=None): - bias_params = builder_layers.BiasLayer.Params().Set(name=name, dims=dims) - if params_init is not None: - bias_params = bias_params.Set(params_init=params_init) - return bias_params - - def _Activation(self, name, activation_fn): - return builder_layers.MapLayer.Params().Set(name=name, fn=activation_fn) - - def _Relu(self, name): - return self._Activation(name, activation_fn=tf.nn.relu) - - def _Sigmoid(self, name): - return self._Activation(name, activation_fn=tf.nn.sigmoid) - - def _FC(self, name, idims, odims, use_bn=True, activation_fn=None): - """Fully connected layer, with optional batch norm.""" - activation_fn = activation_fn or self.activation_fn - if isinstance(idims, (list, tuple)): - idims = idims[0] - fc_layers = [self._Linear('linear', idims, odims)] - if use_bn and self.fc_bn_after_linear: - # Note that bn should use odims, since after linear. - fc_layers = fc_layers + [self._BN('bn', odims)] - elif use_bn and (not self.fc_bn_after_linear): - # Note that bn should use idims, since before linear. - fc_layers = [self._BN('bn', idims)] + fc_layers - else: - # Add bias since no batch norm that folds in bias. - fc_layers = fc_layers + [self._Bias('bias', odims)] - fc_layers += [self._Activation('activation', activation_fn=activation_fn)] - - return self._Seq(name, *fc_layers) - - def _MLP(self, name, dims, use_bn=True, activation_fn=None): - l = [] - for n, (i, o) in enumerate(zip(dims[:-1], dims[1:])): - l += [self._FC('l%03d' % n, i, o, use_bn=use_bn, - activation_fn=activation_fn)] - return self._Seq(name, *l) - - def _Map(self, name, fn, **kwargs): - return builder_layers.MapLayer.Params().Set(name=name, fn=fn, **kwargs) - - def _Max(self, name): - return self._Map(name=name, fn=tf.reduce_max, kwargs={'axis': -2}) - - def _Reshape(self, name, shape): - return builder_layers.MapLayer.Params().Set( - name=name, fn=tf.reshape, kwargs={'shape': shape}) - - def _Matmul(self, name, *subs): - def ParFn(*xs): - result = xs[0] - for v in xs[1:]: - result = tf.matmul(result, v) - return result - return self._Par(name, ParFn, *subs) - - def _GLU(self, name, idims, odims): - - def Gate(x): - u, v = tf.split(x, 2, axis=-1) - return u * tf.sigmoid(v) - - return self._Seq( - name, - self._Linear('linear', idims, odims * 2), - self._Bias('bias', odims * 2), - builder_layers.MapLayer.Params().Set(name='gate', fn=Gate)) - - def _Dropout(self, name, keep_prob): - return layers.DropoutLayer.Params().Set(name=name, keep_prob=keep_prob) - - def _Fetch(self, name): - return layers.FetchLayer.Params().Set(name=name) - - def _FirstN(self, name, n): - """Return the first n args.""" - return builder_layers.FirstNLayer.Params().Set(name=name, n=n) - - def _ArgIdx(self, name, index): - return builder_layers.ArgIndexLayer.Params().Set(name=name, idx=index) - - def _Join(self, name, *subs): - r"""Juxtapose outputs from \*subs in a single output tuple.""" - - def Join(xs): - arg_lists = [list(x) for x in xs] - result = arg_lists[0] - for v in arg_lists[1:]: - result = result + v - return tuple(result) - - return builder_layers.ParallelLayer.Params().Set( - name=name, sub=list(subs), merge=Join) - - def _Par(self, name, fn, *subs): - """Helper to construct a parallel layer which merge branches.""" - - def _Merge(xs): - rets = [] - for ys in zip(*xs): - rets.append(fn(*ys)) - return tuple(rets) - - # TODO(zhifengc): fill merge_meta to compute flops and output shape, etc. - return builder_layers.ParallelLayer.Params().Set( - name=name, sub=list(subs), merge=_Merge) - - def _Concat(self, name, *subs): - r"""Concatenate outputs from \*subs along the last dimensions.""" - return self._Par(name, lambda *xs: tf.concat(xs, axis=-1), *subs) - - def _BroadcastConcat(self, name, *subs): - r"""Concatenate outputs from \*subs, broadcasting to match leading shape.""" - - def _Merge(*xs): - """Broadcast all dimensions except the last, and concat on last dim.""" - - # Stack all shapes and take max on each dimension to get leading shape. - leading_shape = tf.stack([tf.shape(x)[:-1] for x in xs]) - leading_shape = tf.reduce_max(leading_shape, axis=0) - # Broadcast each x. - broadcast_xs = [] - for x in xs: - broadcast_shape = tf.concat([leading_shape, tf.shape(x)[-1:]], axis=0) - broadcast_xs.append(tf.broadcast_to(x, broadcast_shape)) - - # Concat on last dimension. - concat_xs = tf.concat(broadcast_xs, axis=-1) - - return concat_xs - - return self._Par(name, _Merge, *subs) - - def _ApplyFnMulti(self, name, fn, *subs): - """A common use case where every branch produces a single output tensor.""" - return self._Par(name, fn, *subs) - - def _ApplyInParallelAndMerge(self, name, merge_fn, *subs): - """Applies the subs in parallel to the given input and merges their outputs. - - Args: - name: String layer name. - merge_fn: Function to merge the outputs of `subs`, which will be a - flattened list of subs outputs. For example, if there were 3 subs with - outputs (out1,), (out21, out22), (out31, out32), the output will be - [out1, out21, out22, out31, out32]. - *subs: Modules to be applied in parallel to the input - - Returns: - Params for this layer. - """ - - return self._Seq( - name, - self._Join('parallel_subs', *subs), - self._ApplyFn('merge', merge_fn)) - - def _ApplyFn(self, name, fn): - """Apply fn over the input tuple.""" - return builder_layers.ParallelLayer.Params().Set( - name=name, sub=[self._Seq('id')], merge=lambda xs: (fn(*(xs[0])),)) - - def _MakeInputFeatureFromPoints(self, name): - """Transforms points to features using the points with constant padding. - - Layer input/output shapes: [N x P x 3] -> ([N x P x 3], [N x P x 4]) - - Args: - name: String layer name. - - Returns: - Params for this layer. - """ - - def PadOne(inp): - inp = py_utils.HasShape(inp, [-1, -1, 3]) - return tf.pad(inp, [[0, 0], [0, 0], [0, 1]], constant_values=1.0) - - return self._Join(name, self._Seq('id'), self._ApplyFn('pad', fn=PadOne)) - - def _Squeeze(self, name, axis=None): - def _SqueezeFn(x): - return tf.squeeze(x, axis=axis) - return self._ApplyFn(name, fn=_SqueezeFn) - - def _ConvPlain(self, name, filter_shape, filter_stride=(1, 1), - padding='SAME', conv_init_method=None): - conv_init_method = conv_init_method or self.conv_init_method(filter_shape) - return layers.Conv2DLayerNoPadding.Params().Set( - name=name, - filter_shape=filter_shape, - filter_stride=filter_stride, - padding=padding, - params_init=conv_init_method) - - def _DeconvPlain(self, name, filter_shape, filter_stride=(1, 1)): - return layers.DeconvLayer.Params().Set( - name=name, - filter_shape=filter_shape, - filter_stride=filter_stride, - params_init=self.conv_init_method(filter_shape)) - - def _Conv(self, name, filter_shape, stride=(1, 1), padding='SAME', - use_bn=True): - """Helper to construct a conv/normalize/actvation layer.""" - # TODO(zhifengc): Revisit whether BatchNormLayer should apply gamma when the - # following activation is a relu. - if isinstance(stride, tuple): - filter_stride = stride - elif isinstance(stride, int): - filter_stride = (stride, stride) - else: - raise ValueError( - 'Input stride not a tuple or int. Is a {}'.format(type(stride))) - norm = self._BN('bn', filter_shape[3]) if use_bn else self._Identity(name) - return self._Seq( - name, - self._ConvPlain('conv', filter_shape, filter_stride, padding), - norm, - self._Relu('relu')) - - def _Identity(self, name): - """Apply identity transformation.""" - return layers.IdentityLayer.Params().Set(name=name) - - def _Shortcut(self, name, idims, odims, stride): - """Apply ResNet shortcut transformation.""" - if idims != odims or stride != 1: - return self._ConvPlain(name, (1, 1, idims, odims), stride) - else: - return self._Identity(name) - - def _ResidualLayer(self, name, filter_size, stride): - """ResNet basic layer, as in https://arxiv.org/pdf/1512.03385.pdf Fig. 2. - - Args: - name: string layer name. - filter_size: tuple of integers (filter_height, filter_width, idims, odims) - , which represent filter height, filter width, input channel size, - output channel size. - stride: integer of tuple of integers to apply to all dimensions when using - 2D convolution. This will be applied to the shortcut layer and the - first convolution layer. - - Returns: - Params for a residual layer. - """ - filter_height, filter_width, idims, odims = filter_size - repeated_filter_size = (filter_height, filter_width, odims, odims) - return self._Seq( - name, - self._Add( - 'add', - self._Shortcut('shortcut', idims, odims, stride), - self._Seq( - 'residual', - self._Conv('conv_a', filter_size, stride), - self._ConvPlain('conv_b', repeated_filter_size), - self._BN('residual_bn', odims))), - self._Relu('relu_add')) - - def _ResidualBlock(self, name, filter_size, stride, repeats): - """ResNet block that downsamples at the beginning. - - Args: - name: string block name. - filter_size: tuple of integers (filter_height, filter_width, idims, odims) - , which represent filter height, filter width, input channel size, - output channel size. - stride: integer of tuple of integers to apply to all dimensions when using - 2D convolution. It is applied only to the first downsampling layer for - efficiency. - repeats: integer number of residual layers to stack. - - Returns: - Params for a residual block. - """ - filter_height, filter_width, _, odims = filter_size - repeated_filter_size = (filter_height, filter_width, odims, odims) - return self._Seq( - name, - self._ResidualLayer('res3x3_0', filter_size, stride), - self._Rep( - 'rep', - repeats, - self._ResidualLayer('res3x3_1', repeated_filter_size, 1)), - self._Fetch('final')) - - ########################################## - # Self-attention stack. - # - # NOTE: layers_with_attention has TransformerLayers. We do not use that - # directly because those layers assumes input tensors are in [time, batch, - # dims] so that it's convenient for left-to-right decoding use case. Point - # cloud modeling is unlikely need that. - # - # We also restrict the attention type to the simplest dot-attention for now. - ########################################## - def _SelfAttenStack(self, name, depth, dims, hdims, heads, keep_prob): - """A self-attentional stack. - - Args: - name: string layer name. - depth: int. The number of transformer layers. - dims: int. The dimension of each transformer layer. - hdims: int. The hidden dimension of each transformer layer. - Typically, hdims >= dims. - heads: int. The number of attention heads. - keep_prob: Dropout keep probability. - - Returns: - Params for this layer. - """ - return self._Rep( - name, depth, self._Atten('atten', dims, hdims, heads, keep_prob)) - - def _Atten(self, name, dims, hdims, heads, keep_prob=1.): - """Transformer self-attention layer.""" - return self._Seq( - name, - self._AttenSelf('self', dims, hdims, heads, keep_prob), - self._AttenFF('ff', dims, hdims, keep_prob)) - - def _LN(self, name, dims): - """Layer norm.""" - return layers.LayerNorm.Params().Set(name=name, input_dim=dims) - - def _Project(self, name, idims, odims): - """Project layer. Simply X*W+b.""" - return self._Seq( - name, - self._Linear('linear', idims, odims), - self._Bias('bias', odims)) - - def _Add(self, name, lhs, rhs): - """Add two branches. out = lhs(in) + rhs(in).""" - return self._ApplyFnMulti(name, lambda x, y: x + y, lhs, rhs) - - def _Multiply(self, name, lhs, rhs): - """Multiply two branches. out = lhs(in) * rhs(in).""" - return self._ApplyFnMulti(name, lambda x, y: x * y, lhs, rhs) - - def _AttenFF(self, name, dims, hdims, keep_prob=1.): - """Transformer feed-forward layer.""" - return self._Seq( - name, - self._Add( - 'residual', - self._Seq('id'), - self._Seq( - 'ff', - self._LN('ln', dims), - self._FC('fc', dims, hdims, use_bn=False), - self._Project('proj', hdims, dims), - self._Dropout('dropout', keep_prob)))) - - def _AttenSelf(self, name, dims, hdims, heads, keep_prob=1.): - """Dot-attention, multiple heads, self-attention.""" - assert hdims % heads == 0, 'hdims={} heads={}'.format(hdims, heads) - - def _Atten(query, key, val): - """Returns weighted val based on dot-attention between query and key.""" - b, n, _ = py_utils.GetShape(query) - - # Query. - query = py_utils.HasShape(query, [b, n, hdims]) - query = tf.reshape(query, [b, n, heads, hdims // heads]) - query = tf.transpose(query, [0, 2, 1, 3]) - - # Key. - key = py_utils.HasShape(key, [b, n, hdims]) - key = tf.reshape(key, [b, n, heads, hdims // heads]) - key = tf.transpose(key, [0, 2, 1, 3]) - - # query:[b, heads, n, hdims // heads] - # key: [b, heads, n, hdims // heads]^T - dotp = tf.matmul(query, key, transpose_b=True) - probs = tf.nn.softmax(dotp) - probs = py_utils.HasShape(probs, [b, heads, n, n]) - - # value (aka. context) - val = py_utils.HasShape(val, [b, n, hdims]) - val = tf.reshape(val, [b, n, heads, hdims // heads]) - val = tf.transpose(val, [0, 2, 1, 3]) - val = py_utils.HasShape(val, [b, heads, n, hdims // heads]) - - # Weighted average of value (context). [b, heads, n, hdims // heads] - out = tf.matmul(probs, val) - out = tf.transpose(out, [0, 2, 1, 3]) - out = tf.reshape(out, [b, n, hdims]) - return out - - return self._Seq( - name, - self._Add( - 'residual', - self._Seq('id'), - self._Seq( - 'lapd', - self._LN('ln', dims), - self._ApplyFnMulti( - 'atten', - _Atten, - self._Project('query', dims, hdims), - self._Project('key', dims, hdims), - self._Project('value', dims, hdims)), - self._Project('post', hdims, dims), - self._Dropout('dropout', keep_prob)))) - - ########################################## - # Location and Padding Aware Layers and Helpers - # - # These layers expect as input a NestedMap containing the following keys: - # - # points: tensor with shape [..., 3] containing xyz coordinates - # features: tensor with shape [..., C] containing features for each point - # padding: tensor with same leading shape containing 0/1 with - # 1 representing a padded point, and 0 representing a real point. - # - ########################################## - class _Decorators: - """Internal decorators for builder functions.""" - - @classmethod - def ExpectsNestedMapTensor(cls, expected_keys=()): - """Adds a validation layer before the layer produced by builder_fn. - - Args: - expected_keys: A string, or an iterable of strings that are expected to - be in the input NestedMap. - - Returns: - A decorator function that can be applied on builder functions. - """ - - if isinstance(expected_keys, str): - expected_keys = [expected_keys] - expected_keys = set(expected_keys) - - def _ValidateFn(inp): - """Validate that inp contains the expected keys.""" - if not isinstance(inp, py_utils.NestedMap): - raise ValueError('Input not a `NestedMap`. Is a {}'.format(type(inp))) - input_keys = set(inp.keys()) - if not expected_keys.issubset(input_keys): - missing_keys = expected_keys - input_keys - raise ValueError('Input is missing key(s): {}'.format( - ', '.join(missing_keys))) - return inp - - def _Decorator(builder_fn): - def _DecoratedFn(self, *args, **kwargs): - p = builder_fn(self, *args, **kwargs) - # pylint: disable=protected-access - return self._Seq('validated_%s' % p.name, - self._ApplyFn('validate', fn=_ValidateFn), p) - # pylint: enable=protected-access - return _DecoratedFn - - return _Decorator - - @classmethod - def ExpectsNestedMapPointsTensor(cls, builder_fn): - """Adds a validation layer before the layer produced by builder_fn.""" - return cls.ExpectsNestedMapTensor( - expected_keys=(POINTS_KEY, FEATURES_KEY, PADDING_KEY))(builder_fn) - - @classmethod - def ExpectsNestedMapRangeImage(cls, builder_fn): - """Adds a validation layer before the layer produced by builder_fn.""" - return cls.ExpectsNestedMapTensor( - expected_keys=(XYZ_KEY, FEATURES_KEY, MASK_KEY))(builder_fn) - - @_Decorators.ExpectsNestedMapTensor() - def _GetValue(self, name, key, default=None): - """Expects a NestedMap as input and produces the value for the given key.""" - - def GetValueFn(inp): - if not isinstance(inp, py_utils.NestedMap): - raise ValueError('At layer with name={}. Unable to retrieve key {}, ' - 'input not a `NestedMap`. Is a {}'.format( - name, key, type(inp))) - return inp.get(key, default) - - return self._Map(name, fn=GetValueFn) - - @_Decorators.ExpectsNestedMapTensor() - def _ParMap(self, name, key_to_sub): - """Perform parallel layers and create a NestedMap from the outputs. - - Parallel branches on an input `NestedMap`. Each branch should expect the - same `NestedMap` as input; each branch's output will be mapped to the - specified key in key_to_sub. - - Args: - name: String layer name. - key_to_sub: Dictionary mapping keys to sub params. Each sub should expect - a NestedMap input. - - Returns: - Params for this layer. - """ - sorted_keys = sorted(key_to_sub.keys()) - sorted_subs = [key_to_sub[k] for k in sorted_keys] - - def _MakeNestedMap(*vals): - return py_utils.NestedMap(dict(zip(sorted_keys, vals))) - - return self._ApplyFnMulti(name, _MakeNestedMap, *sorted_subs) - - @_Decorators.ExpectsNestedMapTensor() - def _SeqToKey(self, name, key, *subs): - """Apply sequence to only update the value at a specific key. - - Note: This is the more general version of SeqOnKey. SeqOnKey automatically - extracts the key value before running subs, while SeqToKey passes the entire - input through. - - This function expects a NestedMap as an input, and applies the provided - subs on the entire NestedMap, but only updates the value at the specified - key. The resulting value (tensor or NestedMap) from these subs is placed as - the new value for the specified key in the original NestedMap. The rest of - the key, value pairs are returned untouched. - - Args: - name: string layer name. - key: string key to update after running sequence on. - *subs: A list of sub layer Params that should expect the input NestedMap. - The output of the subs will be placed back to the specified key. - Returns: - Params for this layer. - """ - - def _Merge(outputs): - """Merges the outputs from ParallelLayer. - - Args: - outputs: A tuple of two elements: (a) the original input NestedMap and - (b) the outputs from applying the sub layers in subs. - - Returns: - A new NestedMap map with the specified key's value replaced with the - result from subs. - """ - - input_map = outputs[0][0] - seq_result = outputs[1][0] - new_map = input_map.DeepCopy() - new_map[key] = seq_result - - return (new_map,) - - return builder_layers.ParallelLayer.Params().Set( - name=name, sub=[ - self._Seq('id'), - self._Seq('key_seq', - *subs) - ], merge=_Merge) - - def _SeqOnKey(self, name, key, *subs): - """Sequential layers operating on a specific key only.""" - return self._SeqToKey(name, key, self._GetValue('get_{}'.format(key), key), - *subs) - - @_Decorators.ExpectsNestedMapTensor(FEATURES_KEY) - def _SeqOnFeatures(self, name, *subs): - """Sequential layers operating on the features only.""" - return self._SeqOnKey(name, FEATURES_KEY, *subs) - - @_Decorators.ExpectsNestedMapPointsTensor - def _PaddedMax(self, name): - """Padding aware max pooling layer, emits a single tensor.""" - def _PaddedMaxFn(inp): - """Apply padded max using reduce_max with paddings replaced by neginf.""" - # Replace all padded features with -inf. - neginf_padding = tf.where( - inp.padding > 0, -np.inf * inp.padding, inp.padding) - features = inp.features + neginf_padding[..., tf.newaxis] - features = tf.reduce_max(features, axis=-2) - - # Replace features of all padded points by zeros. If a batch of points are - # all padded, then reduce_min over the padding will be 1. We set the - # features to be zero, so that we don't get any downstream issue with - # NaNs. Note that inf * 0 = NaN. - all_padded = tf.cast(tf.reduce_min(inp.padding, axis=-1), tf.bool) - all_padded = tf.broadcast_to(all_padded[..., tf.newaxis], - py_utils.GetShape(features)) - features = tf.where(all_padded, tf.zeros_like(features), features) - return py_utils.CheckNumerics(features) - - return self._Map(name=name, fn=_PaddedMaxFn) - - @_Decorators.ExpectsNestedMapPointsTensor - def _PaddedMean(self, name): - """Padding aware mean pooling layer, emits a single tensor.""" - - def _PaddedMeanFn(inp): - """Apply padded mean using reduce_sum and dividing by # real points.""" - # Replace all padded features with 0 by masking the padded features out. - mask = 1 - inp.padding - features = inp.features * mask[..., tf.newaxis] - features = tf.reduce_sum(features, axis=-2) - num_real_points = tf.reduce_sum(mask, axis=-1, keepdims=True) - # Prevent the divisor of our padded mean from ever being 0, so that - # the gradient flowing back through this op doesn't give us NaNs. - num_real_points = tf.maximum(num_real_points, 1) - features = features / num_real_points - - # Replace features of all padded points by zeros. If a batch of points are - # all padded, then num_real_points will be zero. We set the features to be - # zero, so that we don't get any downstream issue with NaNs. - # Note that inf * 0 = NaN. - all_padded = tf.equal(num_real_points, 0.) - all_padded = tf.broadcast_to(all_padded, py_utils.GetShape(features)) - features = tf.where(all_padded, tf.zeros_like(features), features) - return py_utils.CheckNumerics(features) - - return self._Map(name=name, fn=_PaddedMeanFn) - - @_Decorators.ExpectsNestedMapPointsTensor - def _PaddedSum(self, name): - """Padding aware sum pooling layer, emits a single tensor.""" - - def _PaddedSumFn(inp): - # Replace all padded features with 0 by masking the padded features out. - mask = 1 - inp.padding - features = inp.features * mask[..., tf.newaxis] - features = tf.reduce_sum(features, axis=-2) - return features - - return self._Map(name=name, fn=_PaddedSumFn) - - @_Decorators.ExpectsNestedMapTensor(FEATURES_KEY) - def _FeaturesFC(self, name, idims, odims, use_bn=True, activation_fn=None): - """Applies a FC layer to `features` key, emits a `NestedMap`.""" - activation_fn = activation_fn or self.activation_fn - bn_join_layer = self._Join( - 'join_features_padding', - self._GetValue('get_features', FEATURES_KEY), - self._Seq('expand_padding', - self._GetValue('get_padding', 'padding'), - self._ApplyFn('expand', - fn=lambda t: t[..., tf.newaxis]))) - if use_bn and self.fc_bn_after_linear: - # Note that bn should use odims, since after linear. - seq_p = [ - self._SeqToKey('linear_seq', - FEATURES_KEY, - self._GetValue('get_features', FEATURES_KEY), - self._Linear('linear', idims, odims)), - bn_join_layer, - self._BN('bn', odims), - ] - elif use_bn and (not self.fc_bn_after_linear): - # Note that bn should use idims, since before linear. - seq_p = [ - bn_join_layer, - self._BN('bn', idims), - self._Linear('linear', idims, odims), - ] - else: - # Add bias since no batch norm that folds in bias. - seq_p = [ - self._GetValue('get_features', FEATURES_KEY), - self._Linear('linear', idims, odims), - self._Bias('bias', odims), - ] - seq_p += [self._Activation('activation', activation_fn=activation_fn)] - return self._SeqToKey(name, FEATURES_KEY, *seq_p) - - @_Decorators.ExpectsNestedMapTensor(FEATURES_KEY) - def _FeaturesMLP(self, name, dims, use_bn=True, activation_fn=None): - l = [] - for n, (i, o) in enumerate(zip(dims[:-1], dims[1:])): - l += [self._FeaturesFC('l%03d' % n, i, o, use_bn=use_bn, - activation_fn=activation_fn)] - return self._Seq(name, *l) - - def _CondFC(self, name, idims, adims, odims, use_bn=True, activation_fn=None): - """Conditional FC layer to use with GIN as a combiner. - - ([..., P, idims], [..., 1, adims]) -> [..., P, odims] - - This layer expects an input tuple (features, aggregate), where features - contains per-point features, and aggregate is a global feature (e.g., - computed using max-pooling over all the points). The aggregate tensor - is used to compute a linear transformation that is used in this FC layer. - Each example in the batch has a different linear transformation. This layer - is similar to the T-Net transformation in PointNet. - - Args: - name: String name for this layer. - idims: Dimension for last axis of features tensor. - adims: Dimension for last axis of aggregate tensor. - odims: Number of output dimensions. - use_bn: Whether to enable batch norm. - activation_fn: Optional override for the activation function. If None, - defaults to the activation fn that the builder is initialized with. - - Returns: - Params for a layer. - """ - activation_fn = activation_fn or self.activation_fn - - def _ReshapeTransform(inp): - """Reshape the transformation tensor to [..., idims, odims].""" - base_shape = py_utils.GetShape(inp)[:-1] - out_shape = list(base_shape) + [idims, odims] - return tf.reshape(inp, out_shape) - - transform_net = self._Matmul( - 'cond_transform', - self._Seq( - 'prep_features', - self._ArgIdx('arg0', index=[0]), - self._BN('bn', idims) if use_bn else self._Seq('id')), - self._Seq( - 'compute_linear_transform', - self._ArgIdx('arg1', index=[1]), - self._Squeeze('squeeze', axis=-2), - # TODO(jngiam): Consider other configurations for FC dims here. - self._FC('fc0', adims, adims * 2, use_bn=use_bn, - activation_fn=activation_fn), - # We use identity for the output since we want the transformation - # matrix to have negative values too. - self._FC('fc1', adims * 2, idims * odims, use_bn=use_bn, - activation_fn=tf.identity), - self._ApplyFn('reshape', fn=_ReshapeTransform))) - - return self._Seq( - name, - transform_net, - self._Seq('id') if use_bn else self._Bias('bias', odims), - self._Activation('activation', activation_fn=activation_fn)) - - def _GINCondFC(self, name, lhs, rhs, idims, adims, odims, use_bn=True, - activation_fn=None): - return self._Seq( - name, - self._Join('join', lhs, rhs), - self._CondFC('cond_fc', idims, adims, odims, use_bn=use_bn, - activation_fn=activation_fn)) - - @_Decorators.ExpectsNestedMapPointsTensor - def _GIN(self, - name, - mlp_dims, - aggregate_sub, - readout_sub, - combine_method='add', - eps=0., - use_bn=True): - """Graph Isomorphism Network (GIN) [1]. - - This implements the GIN network. The input goes through multiple - GINIntermediateLayers which performs the update equation (eqn 4.1 in paper). - - The final network output is the concatenation of all the intermediate - layer outputs (eqn 4.2 in paper), with readout aggregation being mean or - max. - - output = concat([readout(f0), readout(f1),..., readout(fN)]), - where f1 = intermediate_gin(g0), etc. - - This is akin to a DenseNet structure, where all the intermediate layers - are short-circuited to the end. - - Using sum corresponds to the proposed method by [1], which enables learning - graphs with repeated nodes. 'mean' and 'max' may confuse graphs with - repeated nodes but could better capture distribution statistics or distinct - elements. - - Note that eps=0 performs well in their results (Section 7). - - [1] How Powerful are Graph Neural Networks? - Keyulu Xu, Weihua Hu, Jure Leskovec, Stefanie Jegelka. ICLR 2019. - https://arxiv.org/abs/1810.00826 - - Args: - name: String layer name. - mlp_dims: List of list of ints representing the intermediate feature MLPs. - The first dim should match the dim of the input features. Each output - dim of the MLP should match the next. - aggregate_sub: Params for a layer that has padding-aware aggregation - (e.g., PaddedMean, PaddedMax, PaddedSum). This is used by the - intermediate layers. - readout_sub: Params for a layer that has padding-aware aggregation - (e.g., PaddedMean, PaddedMax, PaddedSum). This is used to aggregate - the feature vectors for representing the entire graph. - combine_method: Either 'add', 'concat', or 'cond_fc'. This is used by the - intermediate layers. - eps: float scale parameter, see _GINIntermediateLayer for details. - use_bn: Whether to enable batch norm in intermediate FC layers. - - Returns: - Params for this layer. This layer expects a `NestedMap` with points, - features, and padding. - """ - if combine_method not in ['add', 'concat', 'cond_fc']: - raise ValueError('Unexpected combine method: {}'.format(combine_method)) - - # Validate mlp_dims; every adjacent MLP should have a matching number of - # channels. - for idx, (mlp_i, mlp_o) in enumerate(zip(mlp_dims[:-1], mlp_dims[1:])): - prev_layer_dims = mlp_i[-1] - next_layer_dims = mlp_o[0] - # When concatenating, we expect the next layer to have double the input - # channels. - if combine_method == 'concat': - prev_layer_dims *= 2 - if prev_layer_dims != next_layer_dims: - raise ValueError( - 'mlp_dims do not match ({} != {}) at layer {} ' - 'with dims: {} and {}'.format( - idx, prev_layer_dims, next_layer_dims, mlp_i, mlp_o)) - - # Build the network recursively in a way that concats all the - # intermediate outputs. - def _Build(depth): - if depth == len(mlp_dims): - return readout_sub - else: - return self._Concat( - 'gin_concat', - readout_sub, - self._Seq( - 'seq', - self._GINIntermediateLayer( - 'gin_intermediate', - mlp_dims[depth], - aggregate_sub, - combine_method, - eps, - use_bn), - _Build(depth + 1))) - - p = _Build(depth=0) - - return self._Seq(name, p) - - @_Decorators.ExpectsNestedMapPointsTensor - def _GINIntermediateLayer( - self, - name, - dims, - aggregate_sub, - combine_method='add', - eps=0., - use_bn=True): - """Implements an intermediate GIN layer. - - This layer takes in a `NestedMap` and produces a `NestedMap` that contains - points, features, and padding. - - Given features f_i for each point i, compute new features f'_i: - - f'_i = MLP((1 + eps) * f_i + aggregate(f_j)) where j are neighbors of i - - Note: This layer aggregates over the entire point cloud, not just the - neighbors. In practice, aggregation over the neighbors can be done by having - pre-grouping the points before invoking this layer. - - Args: - name: String layer name. - dims: List of int dims for the MLP layer. - aggregate_sub: Params for a layer that has padding-aware aggregation - (e.g., PaddedMean, PaddedMax, PaddedSum). - combine_method: Either 'add', 'concat', or 'cond_fc'. Add follows the - paper suggestion of adding the aggregated feature to each point. Concat - broadcasts the aggregated features and concats it to each point. Cond_FC - computes an example-dependent linear transformation; this is similar to - T-Net. - eps: float scale parameter, see equation above for details. - use_bn: Whether to enable batch norm in FC layers. - - Returns: - Params for this layer. - """ - combine_method_to_fn = { - 'add': self._Add, - 'concat': self._BroadcastConcat, - 'cond_fc': functools.partial( - self._GINCondFC, - idims=dims[0], odims=dims[0], adims=dims[0], - use_bn=use_bn) - } - if combine_method not in combine_method_to_fn: - raise ValueError('Unexpected combine method: {}'.format(combine_method)) - combine_fn = combine_method_to_fn[combine_method] - return self._Seq( - name, - self._SeqToKey( - 'map_features', - FEATURES_KEY, - combine_fn( - 'combine', - self._Seq( - 'left', - self._GetValue('get_features', FEATURES_KEY), - self._ApplyFn('eps_scale', fn=lambda t: (1. + eps) * t)), - self._Seq( - 'right', - aggregate_sub, - self._ApplyFn('expand_dims', - fn=lambda t: tf.expand_dims(t, axis=-2))))), - self._FeaturesMLP('mlp', dims, use_bn=use_bn)) - - @_Decorators.ExpectsNestedMapPointsTensor - def _ConcatPointsToFeatures(self, name): - return self._SeqToKey( - name, - FEATURES_KEY, - self._Concat( - 'concat', - self._GetValue('get_points', 'points'), - self._GetValue('get_features', FEATURES_KEY))) - - @_Decorators.ExpectsNestedMapPointsTensor - def _SetAbstraction(self, - name, - feature_extraction_sub, - num_samples, - group_size, - ball_radius, - sample_neighbors_uniformly=True): - """Set abstraction layer that samples, groups points, and featurizes groups. - - This is based on PointNet++ concept of set abstractions. - - This layer samples `num_samples` points using Farthest Point Sampling - algorithm. For each sampled point, it forms a group of size `group_size` - consisting the points within the distance of `ball_radius` from the sampled - point. - - It then applies the feature_extraction_sub to the extracted groups, note - that the tensors passed to the feature_extraction_sub will have leading - shapes corresponding to [batch_size, num_query_points, num_points_per_group, - ...]. - - A simple feature_extraction_sub that does a MLP followed by max pooling - on the grouped points could be composed using FeaturesMLP and PaddedMax. - - This layer expects a NestedMap of points, features, and padding; it produces - a corresponding NestedMap with updated points, features, and padding. - - Args: - name: name for the layer. - feature_extraction_sub: Params for a layer that takes in a NestedMap of - points, features, and padding. These tensors correspond to grouped - points. Note that the leading shape of these tensors are [batch_size, - num_query_points, num_points_per_group, ...]. This layer should produce - a single tensor representing the features computed by summarizing the - grouped points. - num_samples: Number of points to be sampled. - group_size: Number neighbours for each sampled point. - ball_radius: The distance around each sampled point to obtain neighbours. - sample_neighbors_uniformly: Whether to sample neighbors uniformly within - the ball radius. - - Returns: - Params for Set Abstraction layer based on PointNet++. - """ - return self._Seq( - name, - car_layers.SamplingAndGroupingLayer.Params().Set( - name='sample_group', - num_samples=num_samples, - ball_radius=ball_radius, - group_size=group_size, - sample_neighbors_uniformly=sample_neighbors_uniformly), - # Output of sampling and grouping is a NestedMap of grouped_points and - # query_points. `query_points` is a nestedmap consists of the sampled - # points and padding. `grouped_points` consists of the groups around the - # sampled points, corresponding features and padding. - self._ParMap( - 'pmap', - dict( - points=self._Seq('seq_points', - self._GetValue('q', 'query_points'), - self._GetValue('p', 'points')), - features=self._Seq( - 'seq_features', - self._GetValue('q', 'grouped_points'), - feature_extraction_sub), - padding=self._Seq('seq_padding', - self._GetValue('q', 'query_points'), - self._GetValue('p', 'padding')), - ))) # pyformat: disable - - @_Decorators.ExpectsNestedMapPointsTensor - def _PointConvParametricConv(self, - name, - mlp_dims, - num_in_channels, - num_out_channels): - """Parametric convolution based on PointConv. - - Note that this layer assumes that the features have already been weighted - by density. This layer follows Figure 5. in [1] but without the initial - inverse density scale weighting part. When working with range images, - one can use RIScaleFeaturesByDensity to scale the features beforehand. - - [1] PointConv: Deep Convolutional Networks on 3D Point Clouds. CVPR 2019. - Wu, Wenxuan and Qi, Zhongang and Fuxin, Li. - - Args: - name: string name for this layer. - mlp_dims: dims for mlp applied to the points. the first dimension of - mlp_dims must be 3. - num_in_channels: integer number of input channels (features) - num_out_channels: integer number of output channels (features). - - Returns: - Params for a parametric conv layer. - """ - if mlp_dims[0] != 3: - raise ValueError( - 'First dimension of mlp_dims must be 3. mlp_dims={}'.format(mlp_dims)) - - def _CombineLastTwoDims(x): - shape = py_utils.GetShape(x) - return tf.reshape(x, shape[:-2] + [np.prod(shape[-2:])]) - - return self._Seq( - name, - self._Par( - 'transpose_matmul', - lambda x, y: tf.matmul(x, y, transpose_a=True), - # Features [..., points, num_in_channels]. - self._GetValue('get_features', 'features'), - self._Seq( - 'transform_points', - # Map points into features to use FeaturesMLP which is padding - # batchnorm aware. - self._SeqToKey( - 'points_as_features', 'features', - self._GetValue('get_points', 'points')), - self._FeaturesMLP('points_mlp', mlp_dims), - # Output of this should be [..., points, mlp_dims[-1]]. - self._GetValue('get_transformed_points', 'features'))), - # Post transform_matmul, should be [..., num_in_channels, mlp_dims[-1]]. - # Note: The paper's use of conv is equivalent to reshaping so that the - # last two dims are combined together. - self._ApplyFn('reshape', fn=_CombineLastTwoDims), - # TODO(jngiam): Consider handling batch norm carefully here, not all - # center points have valid values. - self._FC('fc', num_in_channels * mlp_dims[-1], num_out_channels)) -# pyformat: enable diff --git a/lingvo/tasks/car/builder_lib_test.py b/lingvo/tasks/car/builder_lib_test.py deleted file mode 100644 index 48fb6f85..00000000 --- a/lingvo/tasks/car/builder_lib_test.py +++ /dev/null @@ -1,466 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for builder_lib.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car import builder_lib -import numpy as np - - -class BuilderLibTest(test_utils.TestCase): - - def _TestFProp(self, p, in_shape, expected_out_shape): - g = tf.Graph() - with g.as_default(): - l = p.Instantiate() - x = tf.random.normal(shape=in_shape) - y = l.FPropDefaultTheta(x) - if isinstance(y, (list, tuple)): - self.assertEqual(len(y), 1) - y = y[0] - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - val = self.evaluate(y) - self.assertEqual(val.shape, expected_out_shape) - - def testLN(self): - self._TestFProp(builder_lib.ModelBuilderBase()._LN('ln', 128), - (4, 100, 128), (4, 100, 128)) - - def testProject(self): - self._TestFProp(builder_lib.ModelBuilderBase()._Project('p', 64, 128), - (4, 100, 64), (4, 100, 128)) - - def testAdd(self): - b = builder_lib.ModelBuilderBase() - p = b._Add('p', b._Seq('l'), b._Seq('r')) - self._TestFProp(p, (4, 100, 64), (4, 100, 64)) - - def testAttenFF(self): - b = builder_lib.ModelBuilderBase() - p = b._AttenFF('p', 128, 256, 0.8) - self._TestFProp(p, (4, 100, 128), (4, 100, 128)) - - def testAttenSelf(self): - b = builder_lib.ModelBuilderBase() - p = b._AttenSelf('p', 128, 256, 4, 0.8) - self._TestFProp(p, (4, 100, 128), (4, 100, 128)) - - def testAtten(self): - b = builder_lib.ModelBuilderBase() - p = b._Atten('p', 128, 256, 4, 0.8) - self._TestFProp(p, (4, 100, 128), (4, 100, 128)) - - def testSelfAttenStack(self): - b = builder_lib.ModelBuilderBase() - p = b._SelfAttenStack('p', 6, 128, 256, 4, 0.8) - self._TestFProp(p, (4, 100, 128), (4, 100, 128)) - - def testGetValue(self): - b = builder_lib.ModelBuilderBase() - p = b._GetValue('p', 'test_key') - l = p.Instantiate() - x = py_utils.NestedMap({ - 'test_key': tf.constant(2.0), - 'ignore_key': tf.constant(1.0) - }) - y = l.FPropDefaultTheta(x) - with self.session(): - actual_y = self.evaluate(y) - self.assertEqual(actual_y, 2.0) - - def testGetValueDefault(self): - b = builder_lib.ModelBuilderBase() - p = b._GetValue('p', 'not_a_key', default=tf.constant(3.0)) - l = p.Instantiate() - x = py_utils.NestedMap({ - 'test_key': tf.constant(2.0), - 'ignore_key': tf.constant(1.0) - }) - y = l.FPropDefaultTheta(x) - with self.session(): - actual_y = self.evaluate(y) - self.assertEqual(actual_y, 3.0) - - def testSeqToKeyFeaturesOnly(self): - b = builder_lib.ModelBuilderBase() - p = b._SeqToKey('p', 'features', b._GetValue('get_features', 'features'), - b._Sigmoid('sigmoid')) - l = p.Instantiate() - x = py_utils.NestedMap({ - 'points': tf.constant(3.0), - 'features': tf.constant(0.0), - 'padding': tf.constant(1.0), - }) - y = l.FPropDefaultTheta(x) - with self.session(): - actual_y = self.evaluate(y) - self.assertDictEqual( - actual_y, { - 'points': np.asarray([3.0]), - 'features': np.asarray([0.5]), - 'padding': np.asarray([1.0]), - }) - - def testSeqToKeyFeaturesRaisesIfNotNestedMap(self): - b = builder_lib.ModelBuilderBase() - p = b._SeqToKey('p', 'features', b._GetValue('get_features', 'features'), - b._Sigmoid('sigmoid')) - l = p.Instantiate() - x = tf.constant(1.0) - with self.assertRaisesRegex(ValueError, - r'Input not a `NestedMap`. Is a .*'): - l.FPropDefaultTheta(x) - - def testSeqToKey(self): - b = builder_lib.ModelBuilderBase() - # Update features with sigmoid(padding). - p = b._SeqToKey('p', 'features', b._GetValue('get_padding', 'padding'), - b._Sigmoid('sigmoid')) - l = p.Instantiate() - x = py_utils.NestedMap({ - 'points': tf.constant(3.0), - 'features': tf.constant(5.0), - 'padding': tf.constant(0.0), - 'extra_key': tf.constant(1.0), - }) - y = l.FPropDefaultTheta(x) - with self.session(): - actual_y = self.evaluate(y) - self.assertDictEqual( - actual_y, { - 'points': np.asarray([3.0]), - 'features': np.asarray([0.5]), - 'padding': np.asarray([0.0]), - 'extra_key': np.asarray([1.0]), - }) - - def _getNestedMapTestData(self): - np_x = py_utils.NestedMap({ - 'points': - np.random.rand(3, 5, 3), - 'features': - np.random.rand(3, 5, 5), - 'padding': - np.asarray([[0, 0, 1, 1, 1], [0, 1, 1, 1, 1], [1, 1, 1, 1, 1]]), - }) - x = py_utils.NestedMap({ - 'points': tf.constant(np_x.points, dtype=tf.float32), - 'features': tf.constant(np_x.features, dtype=tf.float32), - 'padding': tf.constant(np_x.padding, dtype=tf.float32), - }) - return np_x, x - - def testPaddedMax(self): - b = builder_lib.ModelBuilderBase() - p = b._PaddedMax('p') - l = p.Instantiate() - - np_x, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - expected_y = np.stack([ - # First example should take max over the first two points. - np.amax(np_x.features[0, :2, :], axis=0), - # Second example should take max over only the first points. - np_x.features[1, 0, :], - # Third example should be all zeros, since all points are padded. - np.zeros_like(np_x.features[2, 0, :]), - ], axis=0) # pyformat: disable - with self.session(): - actual_y = self.evaluate(y) - self.assertAllClose(actual_y, expected_y) - - def testPaddedMean(self): - b = builder_lib.ModelBuilderBase() - p = b._PaddedMean('p') - l = p.Instantiate() - - np_x, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - expected_y = np.stack([ - # First example should take mean over the first two points. - np.mean(np_x.features[0, :2, :], axis=0), - # Second example should take mean over only the first points. - np_x.features[1, 0, :], - # Third example should be all zeros, since all points are padded. - np.zeros_like(np_x.features[2, 0, :]), - ], axis=0) # pyformat: disable - with self.session(): - actual_y = self.evaluate(y) - self.assertAllClose(actual_y, expected_y) - - def testPaddedMeanGrad(self): - b = builder_lib.ModelBuilderBase() - p = b._Seq('seq', b._FeaturesFC('fc', 5, 10), b._PaddedMean('p')) - l = p.Instantiate() - - _, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - loss = tf.reduce_sum(y) - - all_vars = tf.trainable_variables() - grads = tf.gradients(loss, all_vars) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - np_grads = self.evaluate(grads) - for np_grad in np_grads: - self.assertTrue(np.all(np.isfinite(np_grad))) - - def testPaddedSum(self): - b = builder_lib.ModelBuilderBase() - p = b._PaddedSum('p') - l = p.Instantiate() - - np_x, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - expected_y = np.stack([ - # First example should take sum over the first two points. - np.sum(np_x.features[0, :2, :], axis=0), - # Second example should take sum over only the first points. - np_x.features[1, 0, :], - # Third example should be all zeros, since all points are padded. - np.zeros_like(np_x.features[2, 0, :]), - ], axis=0) # pyformat: disable - with self.session(): - actual_y = self.evaluate(y) - self.assertAllClose(actual_y, expected_y) - - def testFeaturesFC(self): - b = builder_lib.ModelBuilderBase() - p = b._FeaturesFC('p', idims=5, odims=6, use_bn=True) - l = p.Instantiate() - - np_x, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - # Points and padding should be equal, and features should be transformed. - self.assertAllClose(actual_y.points, np_x.points) - self.assertAllEqual(actual_y.padding, np_x.padding) - self.assertAllEqual(actual_y.features.shape, (3, 5, 6)) - self.assertTrue(np.all(np.isfinite(actual_y.features))) - - def testFeaturesFCNoBatchNorm(self): - b = builder_lib.ModelBuilderBase() - p = b._FeaturesFC('p', idims=5, odims=6, use_bn=False) - l = p.Instantiate() - - np_x, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - # Points and padding should be equal, and features should be transformed. - self.assertAllClose(actual_y.points, np_x.points) - self.assertAllEqual(actual_y.padding, np_x.padding) - self.assertAllEqual(actual_y.features.shape, (3, 5, 6)) - self.assertTrue(np.all(np.isfinite(actual_y.features))) - - def testFeaturesFCBNAfterLinear(self): - b = builder_lib.ModelBuilderBase() - b.fc_bn_after_linear = True - p = b._FeaturesFC('p', idims=5, odims=6, use_bn=True) - l = p.Instantiate() - - np_x, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - # Points and padding should be equal, and features should be transformed. - self.assertAllClose(actual_y.points, np_x.points) - self.assertAllEqual(actual_y.padding, np_x.padding) - self.assertAllEqual(actual_y.features.shape, (3, 5, 6)) - self.assertTrue(np.all(np.isfinite(actual_y.features))) - - def testCondFC(self): - b = builder_lib.ModelBuilderBase() - p = b._CondFC('p', idims=10, adims=8, odims=12) - l = p.Instantiate() - y = l.FPropDefaultTheta( - tf.random.uniform((3, 4, 5, 10)), tf.random.uniform((3, 4, 1, 8))) - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - self.assertAllEqual(actual_y.shape, (3, 4, 5, 12)) - self.assertTrue(np.all(np.isfinite(actual_y))) - - def testGINWithCondFC(self): - b = builder_lib.ModelBuilderBase() - p = b._GIN( - 'p', [[5, 2, 6], [6, 8, 7]], - b._PaddedMax('p_max'), - b._PaddedMax('p_max'), - combine_method='cond_fc') - l = p.Instantiate() - - _, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - # We expect the output to have all the features concatenated together. - self.assertAllEqual(actual_y.shape, (3, 5 + 6 + 7)) - self.assertTrue(np.all(np.isfinite(actual_y))) - - def testGINIntermediateLayer(self): - b = builder_lib.ModelBuilderBase() - p = b._GINIntermediateLayer('p', [5, 3, 6], b._PaddedSum('sum')) - l = p.Instantiate() - - np_x, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - # Points and padding should be equal, and features should be transformed. - self.assertAllClose(actual_y.points, np_x.points) - self.assertAllEqual(actual_y.padding, np_x.padding) - self.assertAllEqual(actual_y.features.shape, (3, 5, 6)) - self.assertTrue(np.all(np.isfinite(actual_y.features))) - - def testGINLayer(self): - b = builder_lib.ModelBuilderBase() - p = b._GIN('p', [[5, 2, 6], [6, 8, 7]], b._PaddedMax('p_max'), - b._PaddedMax('p_max')) - l = p.Instantiate() - - _, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - # We expect the output to have all the features concatenated together. - self.assertAllEqual(actual_y.shape, (3, 5 + 6 + 7)) - self.assertTrue(np.all(np.isfinite(actual_y))) - - def testGINLayerRaisesUnexpectedCombiner(self): - b = builder_lib.ModelBuilderBase() - with self.assertRaisesRegex(ValueError, 'Unexpected combine method: .*'): - b._GIN( - 'p', [[5, 2, 6], [6, 8, 7]], - b._PaddedMax('p_max'), - b._PaddedMax('p_max'), - combine_method='unknown') - - def testGINLayerRaisesDimsMismatch(self): - b = builder_lib.ModelBuilderBase() - with self.assertRaisesRegex(ValueError, 'mlp_dims do not match .*'): - b._GIN( - 'p', [[5, 2, 6], [6, 8, 7]], - b._PaddedMax('p_max'), - b._PaddedMax('p_max'), - combine_method='concat') - - def testBroadcastConcat(self): - b = builder_lib.ModelBuilderBase() - p = b._BroadcastConcat('p', b._ArgIdx('arg0', [0]), b._ArgIdx('arg1', [1])) - l = p.Instantiate() - - # 2 x 1 x 3 - x1 = np.asarray([[[1, 2, 3]], [[4, 5, 6]]]) - self.assertEqual(x1.shape, (2, 1, 3)) - # 1 x 4 x 2 - x2 = np.asarray([[[7, 8], [9, 10], [11, 12], [13, 14]]]) - self.assertEqual(x2.shape, (1, 4, 2)) - - y = l.FPropDefaultTheta(tf.constant(x1), tf.constant(x2)) - - expected_y = np.concatenate([ - np.broadcast_to(x1, [2, 4, 3]), - np.broadcast_to(x2, [2, 4, 2]), - ], axis=-1) # pyformat: disable - with self.session(): - actual_y = self.evaluate(y) - # x1 will be broadcasted to [2, 4, 3] - # x2 will be broadcasted to [2, 4, 2] - # Concatenation on axis=-1 should result in a tensor of shape [2, 4, 5] - self.assertEqual(actual_y.shape, (2, 4, 5)) - self.assertAllEqual(actual_y, expected_y) - - def testGINLayerWithConcat(self): - b = builder_lib.ModelBuilderBase() - p = b._GIN( - # The first dim in each MLP should be double the last output as we are - # using concat combiner. - 'p', - [[10, 2, 6], [12, 8, 7]], - b._PaddedMax('p_max'), - b._PaddedMax('p_max'), - combine_method='concat') - l = p.Instantiate() - - _, x = self._getNestedMapTestData() - y = l.FPropDefaultTheta(x) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - # We expect the output to have all the features concatenated together. - self.assertAllEqual(actual_y.shape, (3, 5 + 6 + 7)) - self.assertTrue(np.all(np.isfinite(actual_y))) - - def testResidualBlock(self): - b = builder_lib.ModelBuilderBase() - p_square_stride = b._ResidualBlock('residual_block', (3, 3, 4, 8), (2, 2), - 2) - self._TestFProp(p_square_stride, (4, 100, 100, 4), (4, 50, 50, 8)) - p_nonsquare_stride = b._ResidualBlock('residual_block', (3, 3, 4, 8), - (2, 1), 2) - self._TestFProp(p_nonsquare_stride, (4, 100, 100, 4), (4, 50, 100, 8)) - - def testPointConvParametricConvShapes(self): - batch_size, num_groups, points_per_group, num_in_channels = 4, 5, 6, 7 - num_out_channels = 8 - b = builder_lib.ModelBuilderBase() - p = b._PointConvParametricConv('test', [3, 4, 9], num_in_channels, - num_out_channels) - l = p.Instantiate() - x = py_utils.NestedMap( - points=tf.random.uniform((batch_size, num_groups, points_per_group, 3), - dtype=tf.float32), - features=tf.random.uniform( - (batch_size, num_groups, points_per_group, num_in_channels), - dtype=tf.float32), - padding=tf.cast( - tf.random.uniform((batch_size, num_groups, points_per_group), - minval=0, - maxval=2, - dtype=tf.int32), tf.float32)) - y = l.FPropDefaultTheta(x) - - with self.session(): - self.evaluate(tf.global_variables_initializer()) - actual_y = self.evaluate(y) - self.assertAllEqual(actual_y.shape, - (batch_size, num_groups, num_out_channels)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/calibration_processing.py b/lingvo/tasks/car/calibration_processing.py deleted file mode 100644 index 5060096c..00000000 --- a/lingvo/tasks/car/calibration_processing.py +++ /dev/null @@ -1,228 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Library for calculating calibration on a prediction.""" - -from lingvo import compat as tf -from lingvo.core import plot -import numpy as np - - -def ExpectedCalibrationError(confidence, - empirical_accuracy, - num_examples, - min_confidence=None): - """Calculate the expected calibration error. - - Args: - confidence: 1-D np.array of float32 binned confidence scores with one number - per bin - empirical_accuracy: 1-D np.array of float32 binned empirical accuracies with - one number per bin - num_examples: 1-D np.array of int for the number of examples within a bin. - min_confidence: float32 of minimum confidence score to use in the - calculation. If None, no filtering is applied. - - Returns: - float32 of expected calibration error - """ - assert confidence.shape[0] == empirical_accuracy.shape[0] - assert empirical_accuracy.shape[0] == num_examples.shape[0] - - ece = np.abs(empirical_accuracy - confidence) * num_examples - - if min_confidence: - bin_indices = np.where(confidence > min_confidence) - ece = ece[bin_indices] - num_examples = num_examples[bin_indices] - - ece = np.sum(ece) - total_num_examples = np.sum(num_examples) - if total_num_examples != 0: - ece /= total_num_examples - else: - ece = 0.0 - return ece - - -def CalibrationCurve(scores, hits, num_bins): - """Compute data for calibration reliability diagrams. - - Args: - scores: 1-D np.array of float32 confidence scores - hits: 1-D np.array of int32 (either 0 or 1) indicating whether predicted - label matches the ground truth label - num_bins: int for the number of calibration bins - - Returns: - A tuple containing: - - mean_predicted_accuracies: np.array of mean predicted accuracy for each - bin - - mean_empirical_accuracies: np.array of mean empirical accuracy for each - bin - - num_examples: np.array of the number of examples in each bin - """ - mean_predicted_accuracies = [] - mean_empirical_accuracies = [] - num_examples = [] - - # Bin the hits and scores based on the scores. - edges = np.linspace(0.0, 1.0, num_bins + 1) - bin_indices = np.digitize(scores, edges, right=True) - # Put examples with score equal to 0 in bin 1 because we will skip bin 0. - bin_indices = np.where(scores == 0.0, 1, bin_indices) - - for j in range(num_bins + 1): - if j == 0: - continue - indices = np.where(bin_indices == j)[0] - # pylint: disable=g-explicit-length-test - if len(indices) > 0: - mean_predicted_accuracy = np.mean(scores[indices]) - mean_empirical_accuracy = np.mean(hits[indices]) - num_example = len(indices) - else: - mean_predicted_accuracy = (edges[j - 1] + edges[j]) / 2.0 - mean_empirical_accuracy = 0.0 - num_example = 0 - # pylint: enable=g-explicit-length-test - - mean_predicted_accuracies.append(mean_predicted_accuracy) - mean_empirical_accuracies.append(mean_empirical_accuracy) - num_examples.append(num_example) - - mean_predicted_accuracies = np.array(mean_predicted_accuracies) - mean_empirical_accuracies = np.array(mean_empirical_accuracies) - num_examples = np.array(num_examples) - return mean_predicted_accuracies, mean_empirical_accuracies, num_examples - - -class CalibrationCalculator: - """Base class for calculating calibration on a prediction.""" - - def __init__(self, metadata): - self._metadata = metadata - self._num_calibration_bins = self._metadata.NumberOfCalibrationBins() - self._calibration_by_class = None - self._classnames = self._metadata.ClassNames() - self._classids = self._metadata.EvalClassIndices() - - def Calculate(self, metrics): - """Calculate metrics for calibration. - - Args: - metrics: A dict. Each entry in the dict is a list of C (number of classes) - dicts containing mapping from metric names to individual results. - Individual entries may be the following items: - - scalars: A list of C (number of classes) dicts mapping metric names to - scalar values. - - curves: A list of C dicts mapping metrics names to np.float32 arrays of - shape [NumberOfPrecisionRecallPoints()+1, 2]. In the last dimension, 0 - indexes precision and 1 indexes recall. - - calibrations: A list of C dicts mapping metrics names to np.float32 - arrays of shape [number of predictions, 2]. The first column is the - predicted probabilty and the second column is 0 or 1 indicating that the - prediction matched a ground truth item. - - Returns: - nothing - """ - if 'calibrations' not in metrics: - tf.logging.info( - 'CalibrationProcessing invoked but no metrics available ' - 'for calculating calibration.') - return - - self._calibration_by_class = {} - for i, c in enumerate(metrics['calibrations']): - classid = self._classids[i] - classname = self._classnames[classid] - - if np.all(np.isnan(c['calibrations'])) or c['calibrations'].size == 0: - tf.logging.info( - 'Skipping %s for calibration calculation because no ' - 'output provided.' % classname) - continue - tf.logging.info('Calculating calibration for %s: %d items.' % - (classname, len(c['calibrations']))) - - # Ensure that all counts are greater then zero and less then or equal - # to 1.0 to guarantee that all scores are counted. - scores_and_hits = np.clip(c['calibrations'], 1e-10, 1.0) - scores = scores_and_hits[:, 0] - hits = scores_and_hits[:, 1] - curve_data = CalibrationCurve(scores, hits, self._num_calibration_bins) - self._calibration_by_class[classname] = np.array(curve_data[0:3]) - tf.logging.info('Finished calculating calibration for %s.' % - classname) - - def Summary(self, name): - """Generate tf summaries for calibration. - - Args: - name: str, name of summary. - - Returns: - list of tf.Summary - """ - summaries = [] - for class_id in self._metadata.EvalClassIndices(): - classname = self._metadata.ClassNames()[class_id] - tag_str = '{}/{}/calibration'.format(name, classname) - - if classname not in self._calibration_by_class: - continue - - # Extract the data. - mean_predicted_accuracy = self._calibration_by_class[classname][0, :] - mean_empirical_accuracy = self._calibration_by_class[classname][1, :] - num_examples_per_bin = self._calibration_by_class[classname][-1, :] - total_examples = np.sum(num_examples_per_bin) - legend = ['%s (%d)' % (classname, total_examples)] - - def _CalibrationSetter(fig, axes): - """Configure the plot for calibration.""" - ticks = np.arange(0, 1.05, 0.1) - axes.grid(b=False) - axes.set_xlabel('Predicted accuracy') - axes.set_xticks(ticks) - axes.set_ylabel('Empirical accuracy') - axes.set_yticks(ticks) - axes.legend(legend, numpoints=1) # pylint: disable=cell-var-from-loop - fig.tight_layout() - - calibration_curve_summary = plot.Curve( - name=tag_str, - figsize=(10, 8), - xs=mean_predicted_accuracy, - ys=mean_empirical_accuracy, - setter=_CalibrationSetter, - marker='.', - markersize=14, - linestyle='-', - linewidth=2, - alpha=0.5) - - ece = ExpectedCalibrationError(mean_predicted_accuracy, - mean_empirical_accuracy, - num_examples_per_bin) - - ece_summary = tf.Summary(value=[ - tf.Summary.Value( - tag='{}/{}/calibration_ece'.format(name, classname), - simple_value=ece) - ]) - summaries.extend([calibration_curve_summary, ece_summary]) - return summaries diff --git a/lingvo/tasks/car/calibration_processing_test.py b/lingvo/tasks/car/calibration_processing_test.py deleted file mode 100644 index cfe8ebce..00000000 --- a/lingvo/tasks/car/calibration_processing_test.py +++ /dev/null @@ -1,138 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -"""Tests for calibration_processing.""" - -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import calibration_processing -from lingvo.tasks.car import kitti_metadata - -import numpy as np - - -class CalibrationProcessingTest(test_utils.TestCase): - - def testExpectedCalibrationError(self): - confidence = np.array([0.05, 0.15, 0.2, 0.3]) - accuracies = np.array([0.25, 0.15, 0.2, 0.3]) - num_examples = np.array([1000, 10, 10, 10]) - ece = calibration_processing.ExpectedCalibrationError( - confidence, accuracies, num_examples, min_confidence=0.1) - self.assertNear(ece, 0.0, 1e-4) - ece = calibration_processing.ExpectedCalibrationError( - confidence, accuracies, num_examples) - self.assertNear(ece, 0.1942, 1e-4) - - def testCalibrationCurveEqualScoresAndHits(self): - # Test calibration curve data when scores are equal to hits. - # This is an edge case where scores are either 0 or 1. - num_bins = 2 - scores = np.array([0, 1, 0]) - hits = scores - mean_predicted_accuracies, mean_empirical_accuracies, num_examples = \ - calibration_processing.CalibrationCurve(scores, hits, num_bins) - expected_mean_predicted_accuracies = np.array([0.0, 1.0]) - self.assertAllEqual(expected_mean_predicted_accuracies, - mean_predicted_accuracies) - expected_mean_empirical_accuracies = np.array([0, 1]) - self.assertAllEqual(expected_mean_empirical_accuracies, - mean_empirical_accuracies) - expected_num_examples = np.zeros(shape=num_bins) - expected_num_examples[0] = 2 - expected_num_examples[num_bins - 1] = 1 - self.assertAllEqual(expected_num_examples, num_examples) - ece = calibration_processing.ExpectedCalibrationError( - mean_predicted_accuracies, mean_empirical_accuracies, num_examples) - self.assertEqual(ece, 0.0) - - def testCalibrationCurvePerfectCalibration(self): - # Test calibration curve data when empirical accuracy corresponds to mean - # predicted accuracy. - num_bins = 2 - scores = np.array([0.25, 0.25, 0.25, 0.25, 0.75, 0.75, 0.75, 0.75]) - hits = np.array([1, 0, 0, 0, 1, 1, 1, 0]) - mean_predicted_accuracies, mean_empirical_accuracies, num_examples = \ - calibration_processing.CalibrationCurve(scores, hits, num_bins) - expected_mean_predicted_accuracies = np.array([0.25, 0.75]) - self.assertAllEqual(expected_mean_predicted_accuracies, - mean_predicted_accuracies) - expected_mean_empirical_accuracies = np.array([0.25, 0.75]) - self.assertAllEqual(expected_mean_empirical_accuracies, - mean_empirical_accuracies) - expected_num_examples = np.zeros(shape=num_bins) - expected_num_examples[0] = 4 - expected_num_examples[num_bins - 1] = 4 - self.assertAllEqual(expected_num_examples, num_examples) - ece = calibration_processing.ExpectedCalibrationError( - mean_predicted_accuracies, mean_empirical_accuracies, num_examples) - self.assertEqual(ece, 0.0) - - def testAllDataInOneBin(self): - # Test calibration curve data when all data is in one bin. - num_bins = 2 - scores = np.array([1, 1, 1]) - hits = np.array([0, 0, 0]) - mean_predicted_accuracies, mean_empirical_accuracies, num_examples = \ - calibration_processing.CalibrationCurve(scores, hits, num_bins) - expected_mean_predicted_accuracies = np.array([0.25, 1.0]) - self.assertAllEqual(expected_mean_predicted_accuracies, - mean_predicted_accuracies) - expected_mean_empirical_accuracies = np.array([0, 0]) - self.assertAllEqual(expected_mean_empirical_accuracies, - mean_empirical_accuracies) - expected_num_examples = np.zeros(shape=num_bins) - expected_num_examples[0] = 0 - expected_num_examples[num_bins - 1] = 3 - self.assertAllEqual(expected_num_examples, num_examples) - ece = calibration_processing.ExpectedCalibrationError( - mean_predicted_accuracies, mean_empirical_accuracies, num_examples) - self.assertEqual(ece, 1.0) - - def testEmptyBins(self): - # Test calibration curve data when there are no examples. - num_bins = 2 - scores = np.array([]) - hits = np.array([]) - mean_predicted_accuracies, mean_empirical_accuracies, num_examples = \ - calibration_processing.CalibrationCurve(scores, hits, num_bins) - expected_mean_predicted_accuracies = np.array([0.25, 0.75]) - self.assertAllEqual(expected_mean_predicted_accuracies, - mean_predicted_accuracies) - expected_mean_empirical_accuracies = np.array([0, 0]) - self.assertAllEqual(expected_mean_empirical_accuracies, - mean_empirical_accuracies) - expected_num_examples = np.zeros(shape=num_bins) - self.assertAllEqual(expected_num_examples, num_examples) - ece = calibration_processing.ExpectedCalibrationError( - mean_predicted_accuracies, mean_empirical_accuracies, num_examples) - self.assertEqual(ece, 0.0) - - def testCalibrationCalculator(self): - # End to end test for the calibration calculator. - metadata = kitti_metadata.KITTIMetadata() - calculator = calibration_processing.CalibrationCalculator(metadata) - scores_and_hits = np.array([[0.3, 1], [0.5, 1], [0.7, 1]]) - metrics = {} - metrics['calibrations'] = [{'calibrations': scores_and_hits}] - calculator.Calculate(metrics) - summaries = calculator.Summary('Test') - self.assertEqual(len(summaries), 2) - ece_summary = summaries[1] - self.assertEqual(0.5, ece_summary.value[0].simple_value) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/car_layers.py b/lingvo/tasks/car/car_layers.py deleted file mode 100644 index fa41f624..00000000 --- a/lingvo/tasks/car/car_layers.py +++ /dev/null @@ -1,110 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Common layers for car models. - -These are usually sub-classes of base_layer.BaseLayer used by builder_lib. -""" - -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.tasks.car import car_lib - - -class SamplingAndGroupingLayer(base_layer.BaseLayer): - """Sampling and Grouping layer (based on PointNet++).""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'num_samples', 0, - 'Number of points to be sampled. Each sampled point will be ' - 'returned with its corresponding group of neighbors.') - p.Define('ball_radius', 0, - 'The distance around each sampled point to obtain neighbours.') - p.Define('group_size', 0, 'Number neighbours for each sampled point.') - p.Define( - 'sample_neighbors_uniformly', True, - 'Whether to sample neighbors uniformly within the ball radius. ' - 'If False, this will pick the nearest neighbors by distance.') - return p - - def FProp(self, theta, input_data): - """Apply projection to inputs. - - Args: - theta: A NestedMap object containing weights' values of this layer and its - children layers. - input_data: A NestedMap object containing 'points', 'features', 'padding' - Tensors, all of type tf.float32. - 'points': Shape [N, P1, 3] - 'features': Shape [N, P1, F] - 'padding': Shape [N, P1] where 0 indicates real, 1 indicates padded. - - Returns: - A NestedMap consisting of the following two NestedMaps, - grouped_points: consists of the grouped points, features and padding. - query_points: consists of the sampled points and padding. - """ - - p = self.params - features = input_data.features - n, p1, c = py_utils.GetShape(features) - points = py_utils.HasShape(input_data.points, [n, p1, 3]) - padding = py_utils.HasShape(input_data.padding, [n, p1]) - - # Sampling - sampled_idx, _ = car_lib.FarthestPointSampler( - points, padding, num_sampled_points=p.num_samples) - query_points = car_lib.MatmulGather(points, tf.expand_dims(sampled_idx, -1)) - query_points = tf.squeeze(query_points, -2) - - # Grouping - grouped_idx, grouped_padding = car_lib.NeighborhoodIndices( - points, - query_points, - p.group_size, - points_padding=padding, - max_distance=p.ball_radius, - sample_neighbors_uniformly=p.sample_neighbors_uniformly) - grouped_points = car_lib.MatmulGather(points, grouped_idx) - # Normalize the grouped points based on the location of the query point. - grouped_points -= tf.expand_dims(query_points, -2) - grouped_features = car_lib.MatmulGather(features, grouped_idx) - - # Get the padding for the query points. - query_padding = tf.array_ops.batch_gather(padding, sampled_idx) - - # Verify the shapes of output tensors. - query_points = py_utils.HasShape(query_points, [n, p.num_samples, 3]) - query_padding = py_utils.HasShape(query_padding, [n, p.num_samples]) - grouped_features = py_utils.HasShape(grouped_features, - [n, p.num_samples, p.group_size, c]) - grouped_padding = py_utils.HasShape(grouped_padding, - [n, p.num_samples, p.group_size]) - - output_grouped_points = py_utils.NestedMap( - points=grouped_points, - features=grouped_features, - padding=grouped_padding) - output_query = py_utils.NestedMap( - points=query_points, padding=query_padding) - output_map = py_utils.NestedMap({ - 'grouped_points': output_grouped_points, - 'query_points': output_query - }) - return output_map diff --git a/lingvo/tasks/car/car_layers_test.py b/lingvo/tasks/car/car_layers_test.py deleted file mode 100644 index 0a065031..00000000 --- a/lingvo/tasks/car/car_layers_test.py +++ /dev/null @@ -1,82 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for car_layers.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car import car_layers - - -class CarLayersTest(test_utils.TestCase): - - def _testNestedOutShape(self, p, input_shape, expected_shape): - batch_size, num_points, _ = input_shape - g = tf.Graph() - with g.as_default(): - net = p.Instantiate() - input_data = py_utils.NestedMap( - points=tf.random.uniform(input_shape[:-1] + (3,)), - features=tf.random.uniform(input_shape), - padding=tf.zeros((batch_size, num_points), dtype=tf.float32), - label=tf.random.uniform((batch_size,), - minval=0, - maxval=16, - dtype=tf.int32)) - result = net.FPropDefaultTheta(input_data) - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - np_result = self.evaluate(result) - grouped_points_result = np_result.grouped_points - self.assertEqual(grouped_points_result.features.shape, - expected_shape.grouped_points.features) - self.assertEqual(grouped_points_result.points.shape, - expected_shape.grouped_points.points) - self.assertEqual(grouped_points_result.padding.shape, - expected_shape.grouped_points.padding) - - query_points_result = np_result.query_points - self.assertEqual(query_points_result.points.shape, - expected_shape.query_points.points) - self.assertEqual(query_points_result.padding.shape, - expected_shape.query_points.padding) - - def testSamplingAndGrouping(self): - for num_points in [1024, 256]: - for input_dims in [3, 6, 9]: - for group_size in [32, 64]: - p = car_layers.SamplingAndGroupingLayer.Params().Set( - name='SampleGroupTest', - num_samples=256, - ball_radius=0.2, - group_size=group_size, - sample_neighbors_uniformly=True) - grouped_points_shape = py_utils.NestedMap( - features=(8, 256, group_size, input_dims), - points=(8, 256, group_size, 3), - padding=(8, 256, group_size)) - query_points_shape = py_utils.NestedMap( - points=(8, 256, 3), padding=(8, 256)) - expected_shape = py_utils.NestedMap({ - 'grouped_points': grouped_points_shape, - 'query_points': query_points_shape - }) - self._testNestedOutShape(p, (8, num_points, input_dims), - expected_shape) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/car_lib.py b/lingvo/tasks/car/car_lib.py deleted file mode 100644 index 49d289b6..00000000 --- a/lingvo/tasks/car/car_lib.py +++ /dev/null @@ -1,596 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Library of functions on tensors for car layers, builders, and models.""" - - -# pylint:enable=g-direct-tensorflow-import -import lingvo.compat as tf -from lingvo.core import py_utils - - -def SquaredDistanceMatrix(pa, pb, mem_optimized=False): - """Compute pair-wise squared distances. - - Expanded version (faster but potentially numerically unstable): - distance = pa^2 - 2*pa*pb + pb^2 - - Non-expanded version (slow but numerically stable): - distance = (pa - pb)^2 - - Args: - pa: tensor of shape [N, P1, dims] - pb: tensor of shape [N, P2, dims] - mem_optimized: Whether to use the memory-optimized expanded formulation. - Defaults to False. If enabled, the expanded version is used that - may have numerical issues. - - Returns: - tensor of shape [N, P1, P2] - """ - - def _ExpandedSquaredDistanceMatrix(pa, pb): - squared_pa = tf.reduce_sum(tf.square(pa), axis=2, keepdims=True) - squared_pb = tf.transpose( - tf.reduce_sum(tf.square(pb), axis=2, keepdims=True), perm=[0, 2, 1]) - # We have observed that entries may < 0. when using the expanded version. - # The max operation guards that from being possible. - return tf.maximum( - squared_pa - 2 * tf.matmul(pa, pb, transpose_b=True) + squared_pb, 0.0) - - def _NonExpandedSquaredDistanceMatrix(pa, pb): - diff = tf.expand_dims(pa, axis=2) - tf.expand_dims(pb, axis=1) - squared_diff = tf.square(diff) - squared_dis = tf.reduce_sum(squared_diff, axis=3) - return squared_dis - - if mem_optimized: - return _ExpandedSquaredDistanceMatrix(pa, pb) - else: - return _NonExpandedSquaredDistanceMatrix(pa, pb) - - -def NeighborSquaredDistanceMatrix(points, neighbor_points): - """Compute the squared distance matrix between points and their neighbors. - - Args: - points: A float tf.Tensor of shape [N, P1, 3] with point positions. - neighbor_points: A float tf.Tensor fo shape [N, P1, K, 3] with neighbor - positions. - - Returns: - Squared distance matrix between points and their K nearest neighbors - as a float tf.Tensor of shape [N, P1, K]. - """ - points = py_utils.HasShape(points, [-1, -1, 3]) - n, p1 = py_utils.GetShape(points, 2) - neighbor_points = py_utils.HasShape(neighbor_points, [n, p1, -1, 3]) - _, _, k = py_utils.GetShape(neighbor_points, 3) - - sq_diff = tf.square(neighbor_points - tf.reshape(points, [n, p1, 1, 3])) - sq_dist = tf.reduce_sum(sq_diff, axis=3) - return py_utils.HasShape(sq_dist, [n, p1, k]) - - -def KnnIndices(points, query_points, k, valid_num=None, max_distance=None): - """k-nearest neighbors of query_points in points. - - The caller should ensure that points[i, :valid_num[i], :] are the non-padding - points. - - Padding is returned alongside indices. Non-padded points are guaranteed to - be unique (non-repeated) points from original non-padded points. - - Padded points arise due to either a lack of points (k exceeds valid_num) - or points are too far away (exceeds max distance). - - TODO(weihan,jngiam): For backwards compatibility with PointCNN, if there are - fewer than k points to select (possibly because of valid_num), the points - selected will first consist of those in the non-padded points, and - then those from the padded points. This assumes that the padded points are - duplications of the original points. PointCNN should be updated to respect - padding. - - The auxiliary input 'valid_num' marks the number of non-padding points in each - sample. This is needed because we randomly duplicated points to make the input - fix-sized, we want search for k-NN in non-padding points first otherwise the - result may degenerate to be k-duplications of the query point itself. - - Args: - points: tensor of shape [N, P1, dims]. - query_points: tensor of shape [N, P2, dims] - k: Integer. - valid_num: tensor of shape [N,] - max_distance: float representing the maximum distance that each neighbor can - be. If there are no points within the distance, then the closest point is - returned (regardless of distance). If this is set to None, then - max_distance is not used. - - Returns: - A pair of tensors: - - - indices: tensor of shape [N, P2, k]. - - padding: tensor of shape [N, P2 ,k] where 1 represents a padded point, and - 0 represents an unpadded (real) point. - - """ - p1 = tf.shape(points)[1] - padding = None - if valid_num is not None: - padding = tf.greater_equal(tf.range(p1), tf.expand_dims( - valid_num, -1)) # [N, P1], False/True padding - return NeighborhoodIndices(points, query_points, k, padding, max_distance) - - -def NeighborhoodIndices(points, - query_points, - k, - points_padding=None, - max_distance=None, - sample_neighbors_uniformly=False): - """Get indices to k-neighbors of query_points in points. - - Padding is returned along-side indices. Non-padded points are guaranteed to - be unique (non-repeated) points from original non-padded points. - - Padded points arise due to either a lack of points (k exceeds the number - of original non-padded points) or points are too far away (exceeds max - distance). - - Note: Padded point indices may refer to padded points from the original, or - may be duplicates of the closest point. - - TODO(weihan,jngiam): PointCNN implementation makes an assumption that padded - points are repeated points from the original points. This behavior is - maintained here, but we should update PointCNN to respect indices paddings. - - Args: - points: tensor of shape [N, P1, dims]. - query_points: tensor of shape [N, P2, dims] - k: Integer. - points_padding: optional tensor of shape [N, P1] containing True/1.0 iff the - point is a padded point. if None, then all points are considered real - points. - max_distance: float representing the maximum distance that each neighbor can - be. If there are no points within the distance, then the closest point is - returned (regardless of distance). If this is set to None, then no - filtering by distance is performed. - sample_neighbors_uniformly: boolean specifying whether to sample neighbors - uniformly if they are within max distance. - - Returns: - A pair of tensors: - - - indices: tensor of shape [N, P2, k]. - - padding: tensor of shape [N, P2, k] where 1 represents a padded point, and - 0 represents an unpadded (real) point. - - """ - n, p1 = py_utils.GetShape(points, 2) - query_points = py_utils.HasShape(query_points, [n, -1, -1]) - _, p2 = py_utils.GetShape(query_points, 2) - - # Compute pair-wise squared distances. - # Note that dist_mat contains the squared distance (without sqrt). Thus, when - # using max_distance, we will need to square max_distance to make sure it's - # in the same units. - dist_mat = SquaredDistanceMatrix(query_points, points) - dist_mat = py_utils.HasShape(dist_mat, [n, p2, p1]) - - # Add a large scalar to the distances for padded points. - # dist_mat[i, j, k] will be: - # if k < valid_num[i]: distance between points[i, k] and query_points[i, j] - # otherwise: a large scalar added to dist_mat[i, j, k] - if points_padding is not None: - points_padding = tf.cast(tf.expand_dims(points_padding, 1), tf.float32) - points_padding = py_utils.HasShape(points_padding, [n, 1, p1]) - large_scalar = tf.reduce_max(dist_mat) + 1 - dist_mat += points_padding * large_scalar - - # To perform sampling neighbors uniformly efficiently, we set all neighbors - # that are within the distance threshold to have distances be drawn uniformly - # at random. Using top_k with this enables selecting a random set quickly - # without replacement. - if sample_neighbors_uniformly: - if max_distance is not None: - mask_by_distance = tf.less_equal(dist_mat, max_distance**2) - dist_mat = tf.where( - mask_by_distance, - tf.square(max_distance) * tf.random.uniform(tf.shape(dist_mat)), - dist_mat) - else: - raise ValueError('Uniform sampling requires specifying max_distance.') - - top_k_dist, indices = tf.nn.top_k(-dist_mat, k=k, sorted=True) # N x P2 x K - - # Set padding using top_k_dist; padded points will have distance exceeding - # the large_scalar. - if points_padding is not None: - paddings = tf.greater_equal(-top_k_dist, large_scalar) - else: - paddings = tf.zeros_like(top_k_dist, dtype=tf.bool) - - # Filter by max_distances by setting all indices that exceed the max_distance - # to the closest point. - if max_distance is not None: - # Mask is true for points that are further than max_distance. - mask_by_distance = tf.greater(-top_k_dist, tf.square(max_distance)) - closest_idx = tf.tile(indices[:, :, :1], [1, 1, k]) - indices = tf.where(mask_by_distance, closest_idx, indices) - paddings |= mask_by_distance - - indices = tf.reshape(indices, [n, p2, k]) - paddings = tf.cast(paddings, tf.float32) - - return indices, paddings - - -def MatmulGather(source, indices): - """Drop in replacement for tf.gather_nd() optimized for speed on TPU. - - TODO(weihan): tf.gather_nd() is supposed to be implemented in the same way - on TPU. Investigate why it's much slower. - - Args: - source: tensor of shape [N, P1, C] - indices: tensor of shape [N, P2, K] - - Returns: - tensor of shape [N, P2, K, C] - """ - source = py_utils.HasRank(source, 3) - n, p1, c = py_utils.GetShape(source) - indices = py_utils.HasShape(indices, [n, -1, -1]) - _, p2, k = py_utils.GetShape(indices) - - onehot = tf.one_hot(indices, depth=p1) # N x P2 x K x P1 - reshaped = tf.reshape(onehot, [n, -1, p1]) # N x (P2 x K) x P1 - target = tf.matmul(reshaped, source) # N x (P2 x K) x C - return tf.reshape(target, [n, p2, k, c]) - - -def FarthestPointSampler(points, - padding, - num_sampled_points, - precomputed_squared_distance=None, - num_seeded_points=0, - random_seed=None): - """Samples num_sampled_points from points using farthest point sampling. - - Algorithm: - 1. Start by selecting a random point and adding to a selected set. - 2. For all remaining points, find the furthest point from those selected. - 3. Add furthest point to selected. - 4. Repeat 2-3 until num_sampled_points are selected. - - More details at https://en.wikipedia.org/wiki/Farthest-first_traversal - - This output of this function can be used with tf.array_ops.batch_gather to - extract the desired points, for example: - tf.array_ops.batch_gather(points, sampled_idx) - - Args: - points: floating point tf.Tensor of shape [N, P1, dims] - padding: A floating point tf.Tensor of shape [N, P1] with 0 if the point is - real, and 1 otherwise. - num_sampled_points: integer number of points to sample. - precomputed_squared_distance: optional tf.Tensor of shape [N, P1, P1] of - distances between each point. if None, distances will be computed on the - fly. - num_seeded_points: If num_seeded_points > 0, then the first - num_seeded_points in points are considered to be seeded in the FPS - sampling. Note that we assume that these points are *not* padded, and do - not check padding when seeding them. - random_seed: optional integer random seed to use with all the random ops. - - Returns: - A tuple of tf.Tensors (sampled_idx, closest_idx) of types - (tf.int32, tf.int32). - - sampled_idx is of shape [N, num_sampled_points] representing the indices - selected using the sampler. This will have range of [0, P1]. - - closest_idx is of shape [N, P1] representing the indices of the closest - sampled points for each input point. closest_idx is used in PCNN as part of - the pooling operation: each point is assigned to the closest sampled point - and a max is taken over them. This will have a range of [0, P2] with the - index of the closest sampled point that remains. - """ - points = py_utils.HasRank(points, 3) - batch_size, num_points, dims = py_utils.GetShape(points, 3) - - points = py_utils.with_dependencies( - [py_utils.assert_greater_equal(num_points, num_sampled_points)], points) - - # Add a tiny bit of noise to the distance matrix or points so all - # points are unique. This will also ensure true repeated points - # like padded points are only selected after all valid points are selected. - if precomputed_squared_distance is not None: - precomputed_squared_distance = py_utils.HasShape( - precomputed_squared_distance, [batch_size, num_points, num_points]) - precomputed_squared_distance += tf.random.uniform( - (batch_size, num_points, 1), - minval=1e-6, - maxval=1e-5, - dtype=tf.float32, - seed=random_seed) - else: - points += tf.random.uniform((batch_size, num_points, dims), - minval=1e-6, - maxval=1e-5, - dtype=tf.float32, - seed=random_seed) - - # TensorArray to store the sampled indices in the loop. - sampled_idx = tf.TensorArray(tf.int32, num_sampled_points) - - # Initialize distance_to_selected to inf for all points. - distance_to_selected = float('inf') * tf.ones((batch_size, num_points)) - - # For tracking the index to the closest selected point. - closest_idx = tf.zeros((batch_size, num_points), dtype=tf.int32) - - # Current loop index counter. - curr_idx = tf.constant(0, dtype=tf.int32) - - # Get number of valid points (1 is padded, so num_points - num_padded). - num_valid_points = tf.cast( - tf.cast(num_points, dtype=tf.float32) - tf.reduce_sum(padding, axis=1), - dtype=tf.int32) - - def _BodyFn(curr_idx, distance_to_selected, sampled_idx, closest_idx): - """Loop body for farthest point sampler.""" - - def _GetRandomRealPoint(): - """Select the first point. - - For the first point, we want any random real (non padded) point, so we - create a random values per point, and then set all padded ones to - some large value (more than the maxval). We then take the min per batch - element to get the first points. - - Returns: - Tensor containing the index of a random point selected for each example - in the batch. - """ - random_values = tf.random.uniform((batch_size, num_points), - minval=0, - maxval=1, - dtype=tf.float32, - seed=random_seed) - random_values = tf.where( - tf.equal(padding, 0.0), random_values, padding * 10) - return tf.argmin(random_values, axis=1, output_type=tf.int32) - - def _GetFurthestPoint(): - """Get point that is furthest from those already selected. - - We also bias the sampling towards real points by setting the distance - to padded points negative until we are out of real points. - - Returns: - Tensor containing the index of the next farthest point selected for each - example in the batch. - """ - # Set padded points distance to negative so they aren't selected. - padding_masked_distance_to_selected = tf.where( - tf.equal(padding, 0.0), distance_to_selected, -1.0 * tf.ones( - (batch_size, num_points), dtype=tf.float32)) - # But only do this when we still have valid points left. - padding_masked_distance_to_selected = tf.where( - tf.less(curr_idx, num_valid_points), - padding_masked_distance_to_selected, distance_to_selected) - return tf.argmax( - padding_masked_distance_to_selected, axis=-1, output_type=tf.int32) - - def _GetSeededPoint(): - """Select a seeded point. - - Seeded points are assumed to be at the beginning of the original points. - - Returns: - Tensor containing the index of the next seeded point to select for each - example in the batch. - """ - return tf.ones((batch_size,), dtype=tf.int32) * curr_idx - - # Select indices for this loop iteration. - def _Seeded(): - return tf.cond( - tf.less(curr_idx, num_seeded_points), _GetSeededPoint, - _GetFurthestPoint) - - def _Real(): - return tf.cond( - tf.equal(curr_idx, 0), _GetRandomRealPoint, _GetFurthestPoint) - - new_selected = tf.cond(tf.greater(num_seeded_points, 0), _Seeded, _Real) - sampled_idx = sampled_idx.write(curr_idx, new_selected) - - # Extract the distance to the latest point selected to update - # distance_to_selected. - new_selected_gather_idx = tf.stack([tf.range(batch_size), new_selected], - axis=1) - if precomputed_squared_distance is not None: - new_distance = tf.gather_nd(precomputed_squared_distance, - new_selected_gather_idx) - else: - new_points = tf.reshape( - tf.gather_nd(points, new_selected_gather_idx), [batch_size, 1, dims]) - new_distance = tf.reshape( - SquaredDistanceMatrix(points, new_points), [batch_size, num_points]) - - is_newly_closest = tf.less(new_distance, distance_to_selected) - distance_to_selected = tf.minimum(distance_to_selected, new_distance) - - # Track the index to the closest selected point. - new_selected_tiled = tf.tile([[curr_idx]], [batch_size, num_points]) - closest_idx = tf.cond( - tf.equal(curr_idx, 0), - # At the first loop iteration, the init points are the closest. - lambda: new_selected_tiled, - # Otherwise, update with the new points based on the distances. - lambda: tf.where(is_newly_closest, new_selected_tiled, closest_idx)) - return curr_idx + 1, distance_to_selected, sampled_idx, closest_idx - - _, _, sampled_idx, closest_idx = tf.while_loop( - lambda curr_idx, *args: tf.less(curr_idx, num_sampled_points), - _BodyFn, - loop_vars=(curr_idx, distance_to_selected, sampled_idx, closest_idx), - back_prop=False, - maximum_iterations=num_sampled_points) - - sampled_idx = sampled_idx.stack() # num_sampled_points x n - sampled_idx = tf.transpose(sampled_idx, [1, 0]) - - if isinstance(batch_size, int) and isinstance(num_sampled_points, int): - sampled_idx.set_shape((batch_size, num_sampled_points)) - - return sampled_idx, closest_idx - - -# TODO(bencaine): This was moved so that we can make this more generic in the -# future and provide min/avg/max pooling with one function. -def MaxPool3D(points, point_features, pooling_idx, closest_idx): - """Apply max pooling to a point cloud with computed sampling indices. - - sampled_idx and closest_idx are the outputs of a sampler such as - FurthestPointSampler. - - The pooling operation results in a point cloud with fewer points, where the - pooled points are specified by pooling_idx. Each element of pooling_idx - contains an integer in the range [0, P1) containing the index of the point in - points/points_features. - - Max pooling is performed by assigning each point to its closest pooled point, - and then taking a max over the features of points assigned. We assume that - this mapping is provided by closest_idx, where each element should contain - an integer in the range [0, P2) containing the index of the pooled point that - each point is assigned to. - - Note: This logic for pooling assumes that there will be at least - one value > 0 per sampled region for each feature, otherwise it will return 0. - Additionally, it does a reduce over a masked version of the features, so - mean and min would not work without a change in the logic. - - Args: - points: a floating point tf.Tensor with shape [N, P1, 3] - point_features: a floating point tf.Tensor with shape [N, P1, C] - pooling_idx: A tf.int32 tf.Tensor of shape [N, P2] with the index of which - points we want to keep. Each value should be in the range [0, P1]. - closest_idx: A tf.int32 tf.Tensor of shape [N, P1] representing which - sampled point is closest to each original point. Each value should be in - the range of [0, P2]. - - Returns: - A tuple of tf.Tensors (pooled_points, pooled_features). - - pooled_points has shape [N, P2, 3] representing the locations of each - selected point. P2 corresponds to num_pooled_points. - - pooled_features has shape [N, P2, C] representing the pooled features at - each point. - """ - batch_size, num_points = py_utils.GetShape(points, 2) - point_features = py_utils.HasShape(point_features, - [batch_size, num_points, -1]) - pooling_idx = py_utils.HasShape(pooling_idx, [batch_size, -1]) - _, num_output_points = py_utils.GetShape(pooling_idx) - _, _, feature_dims = py_utils.GetShape(point_features, 3) - - # Gather new point locations. - pooled_points = tf.array_ops.batch_gather(points, pooling_idx) - - mask = tf.one_hot(closest_idx, num_output_points) # [N, P1, P2] - mask = tf.transpose(mask, [2, 0, 1]) # [P2, N, P1] - - def _PartialPoolFeaturesFn(partial_mask): - partial_mask = tf.tile( - tf.reshape(partial_mask, [batch_size, num_points, 1]), - [1, 1, feature_dims]) - # Note: This method of pooling assumes there will be a value > 0 - # And will only work with max under this condition. - return tf.reduce_max(partial_mask * point_features, axis=1) - - # Performing a map_fn over the pooled points is more memory efficient. - pooled_point_features = tf.map_fn(_PartialPoolFeaturesFn, mask) # [P2, N, P1] - pooled_point_features = tf.transpose(pooled_point_features, [1, 0, 2]) - - return pooled_points, pooled_point_features - - -def SegmentPool3D(points, - point_features, - pooling_idx, - closest_idx, - pooling_method='max'): - """Performs {min/max/average} pooling over a pointcloud given indices. - - This should be functionally identical when using max to the above - MaxPool3D function, except it turns out to be much more memory efficient - on a TPU, and supports min/max/mean. - - Args: - points: A float tf.Tensor of shape [N, P1, 3] with point locations. - point_features: A float tf.Tensor of shape [N, P1, C] with point features. - pooling_idx: A tf.int32 tf.Tensor of shape [N, P2] with the index of which - points we want to keep. Each value should be in the range [0, P1]. - closest_idx: A tf.int32 tf.Tensor of shape [N, P1] representing which - sampled point is closest to each original point. Each value should be in - the range of [0, P2]. - pooling_method: A string for which pooling function to use. Should be one of - {'min', 'max', 'mean'}. - - Returns: - pooled_points: A float tf.Tensor of shape [N, P2, 3] with the pooled - point locations. - pooled_features: A float tf.Tensor of shape [N, P2, C] with the pooled - features. - Raises: - ValueError: If pooling_method is not one of {min/max/mean}. - """ - segment_pooling_functions = { - 'min': tf.math.unsorted_segment_min, - 'max': tf.math.unsorted_segment_max, - 'mean': tf.math.unsorted_segment_mean - } - - if pooling_method not in segment_pooling_functions: - raise ValueError('`pooling_method` must be one of {}.'.format( - list(segment_pooling_functions.keys()))) - segment_fn = segment_pooling_functions[pooling_method] - - points = py_utils.HasShape(points, [-1, -1, 3]) - n, p1 = py_utils.GetShape(points, 2) - point_features = py_utils.HasShape(point_features, [n, p1, -1]) - _, _, c = py_utils.GetShape(point_features) - pooling_idx = py_utils.HasShape(pooling_idx, [n, -1]) - _, p2 = py_utils.GetShape(pooling_idx) - closest_idx = py_utils.HasShape(closest_idx, [n, p1]) - - # Subselect our output points - pooled_points = tf.array_ops.batch_gather(points, pooling_idx) - - # Loop over batch dimension of our features/indices, as unsorted_segment_X - # does not currently support a batch dimension. - def _LoopFn(args): - example_features, example_closest_idx = args - return segment_fn(example_features, example_closest_idx, num_segments=p2) - - pooled_features = tf.map_fn( - fn=_LoopFn, elems=(point_features, closest_idx), dtype=tf.float32) - - return (py_utils.HasShape(pooled_points, [n, p2, 3]), - py_utils.HasShape(pooled_features, [n, p2, c])) diff --git a/lingvo/tasks/car/car_lib_test.py b/lingvo/tasks/car/car_lib_test.py deleted file mode 100644 index be823c0e..00000000 --- a/lingvo/tasks/car/car_lib_test.py +++ /dev/null @@ -1,466 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for car_lib.""" - -import lingvo.compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import car_lib -import numpy as np - - -class CarLibTest(test_utils.TestCase): - - def np_sq_dis(self, pa, pb): - sq_dis = np.zeros((pa.shape[0], pa.shape[1], pb.shape[1])) - for n in range(pa.shape[0]): - for i in range(pa.shape[1]): - for j in range(pb.shape[1]): - sq_dis[n, i, j] = np.sum(np.square(pa[n, i] - pb[n, j])) - return sq_dis - - def _np_sq_dis_neighbors(self, points, neighbor_idx): - """Run the np square distance function and gather based on neighbor.""" - n, p1, k = neighbor_idx.shape - sq_dist_all = self.np_sq_dis(points, points) - sq_dist_neighbors = np.zeros((n, p1, k), dtype=np.float32) - for b_i in range(n): - for p_i in range(p1): - for k_i in range(k): - sq_dist_neighbors[b_i, p_i, k_i] = sq_dist_all[ - b_i, p_i, neighbor_idx[b_i, p_i, k_i]] - return sq_dist_neighbors - - def testSquaredDistance(self): - g = tf.Graph() - with g.as_default(): - pa_tensor = tf.placeholder(tf.float32, shape=[None, None, 4]) - pb_tensor = tf.placeholder(tf.float32, shape=[None, None, 4]) - sq_dis_tensor = car_lib.SquaredDistanceMatrix( - pa_tensor, pb_tensor, mem_optimized=False) - sq_dis_tensor_mem_optimized = car_lib.SquaredDistanceMatrix( - pa_tensor, pb_tensor, mem_optimized=True) - - num_trials = 3 - for _ in range(num_trials): - rand_pa = np.random.randn(10, 5, 4) - rand_pb = np.random.randn(10, 6, 4) - np_sq_dis = self.np_sq_dis(rand_pa, rand_pb) - - with self.session(graph=g) as sess: - sq_dis, sq_dis_mem_optimized = sess.run( - [sq_dis_tensor, sq_dis_tensor_mem_optimized], - feed_dict={ - pa_tensor: rand_pa, - pb_tensor: rand_pb, - }) - self.assertAllClose(sq_dis, np_sq_dis) - self.assertAllClose(sq_dis_mem_optimized, np_sq_dis) - - def testNeighborSquaredDistance(self): - n, p1, k = 2, 10, 3 - points = tf.random.uniform((n, p1, 3)) - neighbor_idx = tf.random.uniform((n, p1, k), - minval=0, - maxval=p1, - dtype=tf.int32) - neighbor_points = car_lib.MatmulGather(points, neighbor_idx) - - sq_dist_result = car_lib.NeighborSquaredDistanceMatrix( - points, neighbor_points) - - with self.session(): - [np_points, np_neighbor_idx, np_sq_dist_result - ] = self.evaluate([points, neighbor_idx, sq_dist_result]) - np_sq_dist_expected = self._np_sq_dis_neighbors(np_points, - np_neighbor_idx) - self.assertAllClose(np_sq_dist_result, np_sq_dist_expected) - - def testKnnIndicesMaxDistance(self): - points = tf.constant([[[1, 1, 1], [2, 2, 2], [4, 4, 4], [5, 5, 5]]], - dtype=tf.float32) - query_points = tf.constant([[ - [1, 1, 1], - [4, 4, 4], - ]], dtype=tf.float32) - valid_num = tf.constant([4], dtype=tf.int32) - - # Max distance so that one neighbor can be selected. - max_distance = 2.0 - expected_1nn = np.array([[[0], [2]]], dtype=np.int32) - expected_1nn_padding = np.array([[[0], [0]]], dtype=np.float32) - expected_2nn = np.array([[[0, 1], [2, 3]]], dtype=np.int32) - expected_2nn_padding = np.array([[[0, 0], [0, 0]]], dtype=np.float32) - expected_3nn = np.array([[[0, 1, 0], [2, 3, 2]]], dtype=np.int32) - expected_3nn_padding = np.array([[[0, 0, 1], [0, 0, 1]]], dtype=np.float32) - with self.session(): - output_1nn, output_1nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 1, valid_num, max_distance)) - self.assertAllEqual(output_1nn, expected_1nn) - self.assertAllEqual(output_1nn_padding, expected_1nn_padding) - - output_2nn, output_2nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 2, valid_num, max_distance)) - self.assertAllEqual(output_2nn, expected_2nn) - self.assertAllEqual(output_2nn_padding, expected_2nn_padding) - - output_3nn, output_3nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 3, valid_num, max_distance)) - self.assertAllEqual(output_3nn, expected_3nn) - self.assertAllEqual(output_3nn_padding, expected_3nn_padding) - - # Max distance so that only itself can be selected. - max_distance = 0.1 - expected_1nn = np.array([[[0], [2]]], dtype=np.int32) - expected_1nn_padding = np.array([[[0], [0]]], dtype=np.float32) - expected_2nn = np.array([[[0, 0], [2, 2]]], dtype=np.int32) - expected_2nn_padding = np.array([[[0, 1], [0, 1]]], dtype=np.float32) - expected_3nn = np.array([[[0, 0, 0], [2, 2, 2]]], dtype=np.int32) - expected_3nn_padding = np.array([[[0, 1, 1], [0, 1, 1]]], dtype=np.float32) - with self.session(): - output_1nn, output_1nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 1, valid_num, max_distance)) - self.assertAllEqual(output_1nn, expected_1nn) - self.assertAllEqual(output_1nn_padding, expected_1nn_padding) - - output_2nn, output_2nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 2, valid_num, max_distance)) - self.assertAllEqual(output_2nn, expected_2nn) - self.assertAllEqual(output_2nn_padding, expected_2nn_padding) - - output_3nn, output_3nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 3, valid_num, max_distance)) - self.assertAllEqual(output_3nn, expected_3nn) - self.assertAllEqual(output_3nn_padding, expected_3nn_padding) - - def testKnnIndices(self): - points = tf.constant([[[1, 1], [2, 2], [4, 4], [5, 5]]], dtype=tf.float32) - query_points = tf.constant([[ - [1, 1], - [4, 4], - ]], dtype=tf.float32) - - # Case 1: when all points are valid. - valid_num = tf.constant([4], dtype=tf.int32) - expected_1nn = np.array([[[0], [2]]], dtype=np.int32) - expected_2nn = np.array([[[0, 1], [2, 3]]], dtype=np.int32) - expected_3nn = np.array([[[0, 1, 2], [2, 3, 1]]], dtype=np.int32) - with self.session(): - output_1nn, _ = self.evaluate( - car_lib.KnnIndices(points, query_points, 1, valid_num)) - self.assertAllEqual(output_1nn, expected_1nn) - - output_2nn, _ = self.evaluate( - car_lib.KnnIndices(points, query_points, 2, valid_num)) - self.assertAllEqual(output_2nn, expected_2nn) - - output_3nn, _ = self.evaluate( - car_lib.KnnIndices(points, query_points, 3, valid_num)) - self.assertAllEqual(output_3nn, expected_3nn) - - # Case 2: not all points are valid. - valid_num = tf.constant([2], dtype=tf.int32) - expected_1nn = np.array([[[0], [1]]], dtype=np.int32) - expected_1nn_padding = np.array([[[0], [0]]], dtype=np.float32) - expected_2nn = np.array([[[0, 1], [1, 0]]], dtype=np.int32) - expected_2nn_padding = np.array([[[0, 0], [0, 0]]], dtype=np.float32) - expected_3nn = np.array([[[0, 1, 2], [1, 0, 2]]], dtype=np.int32) - expected_3nn_padding = np.array([[[0, 0, 1], [0, 0, 1]]], dtype=np.float32) - - with self.session(): - output_1nn, output_1nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 1, valid_num)) - self.assertAllEqual(output_1nn, expected_1nn) - self.assertAllEqual(output_1nn_padding, expected_1nn_padding) - - output_2nn, output_2nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 2, valid_num)) - self.assertAllEqual(output_2nn, expected_2nn) - self.assertAllEqual(output_2nn_padding, expected_2nn_padding) - - output_3nn, output_3nn_padding = self.evaluate( - car_lib.KnnIndices(points, query_points, 3, valid_num)) - self.assertAllEqual(output_3nn, expected_3nn) - self.assertAllEqual(output_3nn_padding, expected_3nn_padding) - - # Case 3: explicit padding. - padding = tf.constant([[1, 0, 1, 0]]) - expected_1nn = np.array([[[1], [3]]], dtype=np.int32) - expected_1nn_padding = np.array([[[0], [0]]], dtype=np.float32) - expected_2nn = np.array([[[1, 3], [3, 1]]], dtype=np.int32) - expected_2nn_padding = np.array([[[0, 0], [0, 0]]], dtype=np.float32) - expected_3nn = np.array([[[1, 3, 0], [3, 1, 2]]], dtype=np.int32) - expected_3nn_padding = np.array([[[0, 0, 1], [0, 0, 1]]], dtype=np.float32) - with self.session(): - output_1nn, output_1nn_padding = self.evaluate( - car_lib.NeighborhoodIndices(points, query_points, 1, padding)) - self.assertAllEqual(output_1nn, expected_1nn) - self.assertAllEqual(output_1nn_padding, expected_1nn_padding) - - output_2nn, output_2nn_padding = self.evaluate( - car_lib.NeighborhoodIndices(points, query_points, 2, padding)) - self.assertAllEqual(output_2nn, expected_2nn) - self.assertAllEqual(output_2nn_padding, expected_2nn_padding) - - output_3nn, output_3nn_padding = self.evaluate( - car_lib.NeighborhoodIndices(points, query_points, 3, padding)) - self.assertAllEqual(output_3nn, expected_3nn) - self.assertAllEqual(output_3nn_padding, expected_3nn_padding) - - def testNeighborhoodIndicesWithUniformSampling(self): - points = tf.constant([[[1, 1], [2, 2], [4, 4], [5, 5]]], dtype=tf.float32) - query_points = tf.constant([[ - [2, 2], - [5, 5], - ]], dtype=tf.float32) - - padding = tf.constant([[1, 0, 1, 0]]) - - # With max_distance=1.1, only the nearest point will be returned (and - # repeated). - expected_3nn = np.array([[[1, 1, 1], [3, 3, 3]]], dtype=np.int32) - expected_paddings = np.array([[[0, 1, 1], [0, 1, 1]]], dtype=np.float32) - with self.session(): - output_3nn, paddings = self.evaluate( - car_lib.NeighborhoodIndices( - points, - query_points, - 3, - padding, - max_distance=1.1, - sample_neighbors_uniformly=True)) - self.assertAllEqual(output_3nn, expected_3nn) - self.assertAllEqual(paddings, expected_paddings) - - def testNeighborhoodIndicesWithUniformSamplingRaisesIfNoMaxDistance(self): - points = tf.constant([[[1, 1], [2, 2], [4, 4], [5, 5]]], dtype=tf.float32) - query_points = tf.constant([[ - [1, 1], - [4, 4], - ]], dtype=tf.float32) - padding = tf.constant([[1, 0, 1, 0]]) - with self.assertRaisesRegex( - ValueError, r'.*Uniform sampling requires specifying max_distance.*'): - car_lib.NeighborhoodIndices( - points, query_points, 1, padding, sample_neighbors_uniformly=True) - - def testFarthestPointSamplerOnePoint(self): - points = tf.constant([ - [[1, 1, 1, 1]], - [[2, 2, 2, 2]], - ], dtype=tf.float32) - padding = tf.zeros((2, 1), dtype=tf.float32) - selected_idx, _ = car_lib.FarthestPointSampler(points, padding, 1) - with self.session(): - selected_idx = self.evaluate(selected_idx) - self.assertAllEqual(selected_idx, [[0], [0]]) - - def testFarthestPointSamplerInsufficientPoints(self): - points = tf.constant([ - [[0, 1, 1]], - [[2, 2, 2]], - ], dtype=tf.float32) - padding = tf.zeros((2, 1), dtype=tf.float32) - with self.session(): - with self.assertRaisesRegex(tf.errors.InvalidArgumentError, - r'.*Condition x >= y did not hold.*'): - sampled_idx, closest_idx = car_lib.FarthestPointSampler( - points, padding, 2) - self.evaluate((sampled_idx, closest_idx)) - - def testFarthestPointSamplerSelectMinMax(self): - points = tf.constant([ - [[0, 1, 1], [1, 1, 1], [2, 1, 1], [3, 1, 1], [4, 1, 1], [5, 1, 1]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - [[0, 2, 3], [1, 2, 3], [2, 2, 3], [3, 2, 3], [4, 2, 3], [5, 2, 3]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - ], dtype=tf.float32) # pyformat: disable - padding = tf.zeros((4, 6), dtype=tf.float32) - selected_idx, closest_idx = car_lib.FarthestPointSampler(points, padding, 2) - with self.session(): - selected_idx, closest_idx = self.evaluate([selected_idx, closest_idx]) - for batch_idx in range(4): - self.assertIn( - selected_idx[batch_idx, 1], [0, 5], - msg=('The second selected point must be one of the end ' - 'points corresponding to index 0 or 5.')) - - # Closest indices should either be 0 or 1 since we picked 2 points. - self.assertTrue( - np.all((closest_idx >= 0) & (closest_idx < 2)), - msg='Closest index must be among selected indices.') - - def testFarthestPointSamplerSeeded(self): - points = tf.constant([ - [[0, 1, 1], [1, 1, 1], [2, 1, 1], [3, 1, 1], [4, 1, 1], [5, 1, 1]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - [[0, 2, 3], [1, 2, 3], [2, 2, 3], [3, 2, 3], [4, 2, 3], [5, 2, 3]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - ], dtype=tf.float32) # pyformat: disable - padding = tf.zeros((4, 6), dtype=tf.float32) - selected_idx, closest_idx = car_lib.FarthestPointSampler( - points, padding, 3, num_seeded_points=2) - with self.session(): - selected_idx, closest_idx = self.evaluate([selected_idx, closest_idx]) - # First two selected points are seeded. - self.assertTrue(np.all(selected_idx[:, 0] == 0)) - self.assertTrue(np.all(selected_idx[:, 1] == 1)) - # Third point is the last point since it's farthest. - self.assertTrue(np.all(selected_idx[:, 2] == 5)) - # Closest indices should either be 0, 1 or 2 since we picked 3 points. - self.assertTrue( - np.all((closest_idx >= 0) & (closest_idx < 3)), - msg='Closest index must be among selected indices.') - - def testFarthestPointSamplerAllPoints(self): - points = tf.constant([ - [[0, 1, 1], [1, 1, 1], [2, 1, 1], [3, 1, 1], [4, 1, 1], [5, 1, 1]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - [[0, 2, 3], [1, 2, 3], [2, 2, 3], [3, 2, 3], [4, 2, 3], [5, 2, 3]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - ], dtype=tf.float32) # pyformat: disable - padding = tf.zeros((4, 6), dtype=tf.float32) - sampled_idx, closest_idx = car_lib.FarthestPointSampler(points, padding, 6) - with self.session(): - sampled_idx, closest_idx = self.evaluate([sampled_idx, closest_idx]) - for batch_n in range(4): - self.assertSetEqual( - set(sampled_idx[batch_n, :]), - set(np.arange(6)), - msg='All points should be selected.') - for point_idx in range(6): - # For each selected point in sampled_idx, we verify that the - # closest_idx assigned to that point matches itself. This is done by - # finding the location of the point in sampled_idx (which is shuffled - # during sampling). The value of the point in closest_idx should match - # the index assigned to the point in sampled_idx. - expected_closest_idx = None - # location refers to the index where the point appears in sampled_idx. - # This should be what closest_idx refers to. - for location, sample_idx in enumerate(sampled_idx[batch_n, :]): - if point_idx == sample_idx: - expected_closest_idx = location - break - - self.assertIsNotNone(expected_closest_idx, - 'Point not found in sampled_idx result.') - self.assertEqual( - closest_idx[batch_n][point_idx], - expected_closest_idx, - msg='Closest index should be the point itself.') - - def testFarthestPointSamplerGatherPoints(self): - points = tf.constant([ - [[0, 1, 1], [1, 1, 1], [2, 1, 1], [3, 1, 1], [4, 1, 1], [5, 1, 1]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - [[0, 2, 3], [1, 2, 3], [2, 2, 3], [3, 2, 3], [4, 2, 3], [5, 2, 3]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - ], dtype=tf.float32) # pyformat: disable - padding = tf.zeros((4, 6), dtype=tf.float32) - n = 4 - num_points = 3 - selected_idx, _ = car_lib.FarthestPointSampler(points, padding, num_points) - gather_indices = tf.stack([ - tf.tile(tf.expand_dims(tf.range(n), 1), [1, num_points]), selected_idx - ], - axis=2) - sampled_points = tf.gather_nd(points, gather_indices) - with self.session(): - sampled_points = self.evaluate(sampled_points) - self.assertEqual(sampled_points.shape, (n, num_points, 3)) - - def testFarthestPointSamplerPadding(self): - points = tf.constant([ - [[0, 1, 1], [1, 1, 1], [2, 1, 1], [3, 1, 1], [4, 1, 1], [5, 1, 1]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - [[0, 2, 3], [1, 2, 3], [2, 2, 3], [3, 2, 3], [4, 2, 3], [5, 2, 3]], - [[0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], [4, 2, 1], [5, 2, 1]], - ], - dtype=tf.float32) - - padding = tf.constant([[0, 0, 0, 0, 1, 1], [0, 0, 1, 1, 0, 0], - [1, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1]], - dtype=tf.float32) - - np_expected_selected_idx = np.array( - [[0, 1, 2, 3], [0, 1, 4, 5], [2, 3, 4, 5], [1, 2, 3, 4]], - dtype=np.int32) - - num_points = 4 - selected_idx, _ = car_lib.FarthestPointSampler(points, padding, num_points) - - with self.session(): - np_selected_idx = self.evaluate(selected_idx) - np_selected_idx.sort(axis=1) - self.assertAllEqual(np_selected_idx, np_expected_selected_idx) - - def _testPooling3D(self, pooling_fn): - num_points_in = 100 - num_points_out = 10 - batch_size = 8 - num_features = 32 - points = tf.random.uniform( - shape=(batch_size, num_points_in, 3), - minval=-1, - maxval=1, - dtype=tf.float32) - - # Note: This max pooling impl is incorrect if the feature range is negative. - features = tf.random.uniform( - shape=(batch_size, num_points_in, num_features), - minval=0, - maxval=1, - dtype=tf.float32) - padding = tf.zeros((batch_size, num_points_in), dtype=tf.float32) - - pooling_idx, closest_idx = car_lib.FarthestPointSampler( - points, padding, num_points_out) - - pooled_points, pooled_features = pooling_fn( - points=points, - point_features=features, - pooling_idx=pooling_idx, - closest_idx=closest_idx) - - with self.session(): - [ - np_points, np_features, np_pooling_idx, np_closest_idx, - np_pooled_points, np_pooled_features - ] = self.evaluate([ - points, features, pooling_idx, closest_idx, pooled_points, - pooled_features - ]) - - for batch_n in range(batch_size): - # Grab the selected pooling points from our sampler to compare to - # the output of our pooling. - expected_pooled_pts = np_points[batch_n, np_pooling_idx[batch_n, :], :] - self.assertAllClose(expected_pooled_pts, np_pooled_points[batch_n, ...]) - - np_batch_features = np_features[batch_n, ...] - for idx in range(num_points_out): - in_group = np_closest_idx[batch_n, :] == idx - expected_max = np.max(np_batch_features[in_group], axis=0) - self.assertAllClose(expected_max, np_pooled_features[batch_n, idx]) - - def testMaxPool3D(self): - return self._testPooling3D(car_lib.MaxPool3D) - - def testSegmentPool3D(self): - return self._testPooling3D(car_lib.SegmentPool3D) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/detection_3d_lib.py b/lingvo/tasks/car/detection_3d_lib.py deleted file mode 100644 index 1838b698..00000000 --- a/lingvo/tasks/car/detection_3d_lib.py +++ /dev/null @@ -1,863 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Library of useful for functions for working with 3D object detection.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.tasks.car import geometry -from lingvo.tasks.car import ops -import numpy as np - - -class Utils3D: - """Helper routines for 3D detection problems. - - One common method to do 3D anchor box assignment is to match anchors to - ground-truth bboxes. First, we generate proposal anchors at given priors - (center locations, dimension prior, offset prior) to tile the input space. - After tiling the input space, each anchor can be assigned to a ground-truth - bbox by measuring IOU and taking a threshold. Note that a ground-truth bbox - may be assigned to multiple anchors - this is expected, and is managed at - inference time by non-max suppression. - - Note: This implementation is designed to be used at input generation, and - does not support a batch dimension. - - The following functions in this utility class helps with that: - - - CreateDenseCoordinates: Makes it easy to create a dense grid of - coordinates that would usually correspond to center locations. - - - MakeAnchorBoxes: Given a list of center coordinates, dimension priors, and - offset priors, this function creates actual anchor bbox parameters at each - coordinate. More than one box can be at each center. - - - IOUAxisAlignedBoxes: This function computes the IOU between two lists of - boxes. - - - AssignAnchors: This function assigns each anchor a ground-truth bbox. Note - that one ground-truth bbox can be assigned to multiple anchors. The model - is expected to regress the residuals each anchor and it's corresponding - ground-truth bbox parameters. - """ - - def ScaledHuberLoss(self, labels, predictions, weights=1.0, delta=1.0): - r"""Scaled Huber (SmoothL1) Loss. - - This function wraps tf.losses.huber_loss to rescale it by 1 / delta, and - uses Reduction.NONE. - - This scaling results in the following formulation instead:: - - (1/d) * 0.5 * x^2 if \|x\| <= d - \|x\| - 0.5 * d if \|x\| > d - - where x is labels - predictions. - - Hence, delta changes where the quadratic bowl is, but does not change the - overall shape of the loss outside of delta. - - Args: - labels: The ground truth output tensor, same dimensions as 'predictions'. - predictions: The predicted outputs. - weights: Optional Tensor whose rank is either 0, or the same rank as - labels, and must be broadcastable to labels (i.e., all dimensions must - be either 1, or the same as the corresponding losses dimension). - delta: float, the point where the huber loss function changes from a - quadratic to linear. - - Returns: - Weighted loss float Tensor. This has the same shape as labels. - """ - return (1. / delta) * tf.losses.huber_loss( - labels, - predictions, - weights=weights, - delta=delta, - reduction=tf.losses.Reduction.NONE) - - def CornerLoss(self, gt_bboxes, predicted_bboxes, symmetric=True): - """Corner regularization loss. - - This function computes the corner loss, an alternative regression loss - for box residuals. This was used in the Frustum-PointNets paper [1]. - - We compute the predicted bboxes (all 8 corners) and compute a SmoothedL1 - loss between the corners of the predicted boxes and ground truth. Hence, - this loss can help encourage the model to maximize the IoU of the - predictions. - - [1] Frustum PointNets for 3D Object Detection from RGB-D Data - https://arxiv.org/pdf/1711.08488.pdf - - Args: - gt_bboxes: tf.float32 of shape [..., 7] which contains (x, y, z, dx, dy, - dz, phi), corresponding to ground truth bbox parameters. - predicted_bboxes: tf.float32 of same shape as gt_bboxes containing - predicted bbox parameters. - symmetric: boolean. If True, computes the minimum of the corner loss - with respect to both the gt box and the gt box rotated 180 degrees. - - Returns: - tf.float32 Tensor of shape [...] where each entry contains the corner loss - for the corresponding bbox. - """ - bbox_shape = py_utils.GetShape(gt_bboxes) - batch_size = bbox_shape[0] - - gt_bboxes = tf.reshape(gt_bboxes, [batch_size, -1, 7]) - predicted_bboxes = tf.reshape(predicted_bboxes, [batch_size, -1, 7]) - - gt_corners = geometry.BBoxCorners(gt_bboxes) - predicted_corners = geometry.BBoxCorners(predicted_bboxes) - huber_loss = self.ScaledHuberLoss(gt_corners, predicted_corners) - huber_loss = tf.reduce_sum(huber_loss, axis=[-2, -1]) - - if symmetric: - # Compute the loss assuming the ground truth is flipped 180, and - # take the minimum of the two losses. - rot = tf.constant([[[0., 0., 0., 0., 0., 0., np.pi]]], dtype=tf.float32) - rotated_gt_bboxes = gt_bboxes + rot - rotated_gt_corners = geometry.BBoxCorners(rotated_gt_bboxes) - rotated_huber_loss = self.ScaledHuberLoss( - labels=rotated_gt_corners, predictions=predicted_corners) - rotated_huber_loss = tf.reduce_sum(rotated_huber_loss, axis=[-2, -1]) - huber_loss = tf.minimum(huber_loss, rotated_huber_loss) - - huber_loss = tf.reshape(huber_loss, bbox_shape[:-1]) - return huber_loss - - def CreateDenseCoordinates(self, ranges): - """Create a matrix of coordinate locations corresponding to a dense grid. - - Example: To create (x, y) coordinates corresponding over a 10x10 grid with - step sizes 1, call ``CreateDenseCoordinates([(1, 10, 10), (1, 10, 10)])``. - - Args: - ranges: A list of 3-tuples, each tuple is expected to contain (min, max, - num_steps). Each list element corresponds to one dimesion. Each tuple - will be passed into np.linspace to create the values for a single - dimension. - - Returns: - tf.float32 tensor of shape [total_points, len(ranges)], where - total_points = product of all num_steps. - - """ - total_points = int(np.prod([r_steps for _, _, r_steps in ranges])) - cycle_steps = total_points - stack_coordinates = [] - - for r_start, r_stop, r_steps in ranges: - values = tf.linspace( - tf.cast(r_start, tf.float32), tf.cast(r_stop, tf.float32), - tf.cast(r_steps, tf.int32)) - cycle_steps //= r_steps - gather_idx = (tf.range(total_points) // cycle_steps) % r_steps - stack_coordinates.append(tf.gather(values, gather_idx)) - - return tf.stack(stack_coordinates, axis=1) - - def MakeAnchorBoxes(self, - anchor_centers, - anchor_box_dimensions, - anchor_box_offsets, - anchor_box_rotations=None): - """Create anchor boxes from centers, dimensions, offsets. - - Args: - anchor_centers: [A, dims] tensor. Center locations to generate boxes at. - anchor_box_dimensions: [B, dims] tensor corresponding to dimensions of - each box. The inner-most dimension of this tensor must match - anchor_centers. - anchor_box_offsets: [B, dims] tensor corresponding to offsets of each box. - anchor_box_rotations: [B] tensor corresponding to rotation of each box. If - None, rotation will be set to 0. - - Returns: - A [num_anchors_center, num_boxes_per_center, 2 * dims + 1] tensor. Usually - dims=3 for 3D, where ``[..., :dims]`` corresponds to location, - ``[..., dims:2*dims]`` corresponds to dimensions, and ``[..., -1]`` - corresponds to rotation. - """ - num_centers, dims = py_utils.GetShape(anchor_centers) - num_box_per_center = py_utils.GetShape(anchor_box_dimensions, 1)[0] - - # Offset the centers by the box offsets - anchor_centers = tf.reshape(anchor_centers, [num_centers, 1, dims]) - anchor_box_offsets = tf.reshape(anchor_box_offsets, - [1, num_box_per_center, dims]) - anchor_box_centers = anchor_centers + anchor_box_offsets - anchor_box_centers = py_utils.HasShape( - anchor_box_centers, [num_centers, num_box_per_center, dims]) - - # Concat the dimensions and rotation parameters - anchor_box_dimensions = tf.tile( - tf.expand_dims(anchor_box_dimensions, 0), [num_centers, 1, 1]) - if anchor_box_rotations is None: - anchor_box_rotations = tf.zeros([num_centers, num_box_per_center, 1]) - else: - anchor_box_rotations = tf.tile( - tf.reshape(anchor_box_rotations, [1, num_box_per_center, 1]), - [num_centers, 1, 1]) - - anchor_bboxes = tf.concat( - [anchor_box_centers, anchor_box_dimensions, anchor_box_rotations], - axis=-1) - - return anchor_bboxes - - def IOU2DRotatedBoxes(self, bboxes_u, bboxes_v): - """Computes IoU between every pair of bboxes with headings. - - This function ignores the z dimension, which is not usually considered - during anchor assignment. - - Args: - bboxes_u: tf.float32. [U, dims]. [..., :7] are (x, y, z, dx, dy, dz, r). - bboxes_v: tf.float32. [V, dims]. [..., :7] are (x, y, z, dx, dy, dz, r). - - Returns: - tf.float32 tensor with shape [U, V], where [i, j] is IoU between - i-th bbox of bboxes_u and j-th bbox of bboxes_v. - """ - - def _IgnoreZCoordinate(bboxes): - """Set z center to 0, and z dimension to 1.""" - num_bboxes = py_utils.GetShape(bboxes, 1)[0] - return tf.stack([ - bboxes[:, 0], bboxes[:, 1], tf.zeros((num_bboxes,)), - bboxes[:, 3], bboxes[:, 4], tf.ones((num_bboxes,)), - bboxes[:, 6] - ], axis=1) # pyformat: disable - - bboxes_u = _IgnoreZCoordinate(bboxes_u) - bboxes_v = _IgnoreZCoordinate(bboxes_v) - return ops.pairwise_iou3d(bboxes_u, bboxes_v) - - def AssignAnchors(self, - anchor_bboxes, - gt_bboxes, - gt_bboxes_labels, - gt_bboxes_mask, - foreground_assignment_threshold=0.5, - background_assignment_threshold=0.35, - background_class_id=0, - force_match=True, - similarity_fn=None): - """Assigns anchors to bboxes using a similarity function (SSD-based). - - Each anchor box is assigned to the top matching ground truth box. - Ground truth boxes can be assigned to multiple anchor boxes. - - Assignments can result in 3 outcomes: - - - Positive assignment (if score >= foreground_assignment_threshold): - assigned_gt_labels will reflect the assigned box label and - assigned_cls_mask will be set to 1.0 - - Background assignment (if score <= background_assignment_threshold): - assigned_gt_labels will be background_class_id and assigned_cls_mask - will be set to 1.0 - - Ignore assignment (otherwise): - assigned_gt_labels will be background_class_id and assigned_cls_mask - will be set to 0.0 - - The detection loss function would usually: - - - Use assigned_cls_mask for weighting the classification loss. The mask - is set such that the loss applies to foreground and background - assignments only - ignored anchors will be set to 0. - - Use assigned_reg_mask for weighting the regression loss. The mask is set - such that the loss applies to foreground assignments only. - - The thresholds (foreground_assignment_threshold and - background_assignment_threshold) should be tuned per dataset. - - TODO(jngiam): Consider having a separate threshold for regression boxes; a - separate threshold is used in PointRCNN. - - Args: - anchor_bboxes: tf.float32. [A, 7], where [..., :] corresponds to box - parameters (x, y, z, dx, dy, dz, r). - gt_bboxes: tf.float32. [G, 7], where [..., :] corresponds to ground truth - box parameters (x, y, z, dx, dy, dz, r). - gt_bboxes_labels: tensor with shape [G]. Ground truth labels for each - bounding box. - gt_bboxes_mask: tensor with shape [G]. Mask for ground truth boxes, 1 iff - the gt_bbox is a real bbox. - foreground_assignment_threshold: Similarity score threshold for assigning - foreground bounding boxes; scores need to be >= - foreground_assignment_threshold to be assigned to foreground. - background_assignment_threshold: Similarity score threshold for assigning - background bounding boxes; scores need to be <= - background_assignment_threshold to be assigned to background. - background_class_id: class id to be assigned to anchors_gt_class if no - anchor boxes match. - force_match: Boolean specifying if force matching is enabled. If - force matching is enabled, then matched anchors which are also the - highest scoring with a ground-truth box are considered foreground - matches as long as their similarity score > 0. - similarity_fn: Function that computes the a similarity score (e.g., IOU) - between pairs of bounding boxes. This function should take in two - tensors corresponding to anchor and ground-truth bboxes, and return a - matrix [A, G] with the similarity score between each pair of bboxes. The - score must be non-negative, with greater scores representing more - similar. The fore/background_assignment_thresholds will be applied to - this score to determine if the an anchor is foreground, background or - ignored. If set to None, the function will default to IOU2DRotatedBoxes. - - Returns: - NestedMap with the following keys - - - assigned_gt_idx: shape [A] index corresponding to the index of the - assigned ground truth box. Anchors not assigned to a ground truth box - will have the index set to -1. - - assigned_gt_bbox: shape [A, 7] bbox parameters assigned to each anchor. - - assigned_gt_similarity_score: shape [A] (iou) score between the anchor - and the gt bbox. - - assigned_gt_labels: shape [A] label assigned to bbox. - - assigned_cls_mask: shape [A] mask for classification loss per anchor. - This should be 1.0 if the anchor has a foreground or background - assignment; otherwise, it will be assigned to 0.0. - - assigned_reg_mask: shape [A] mask for regression loss per anchor. - This should be 1.0 if the anchor has a foreground assignment; - otherwise, it will be assigned to 0.0. - Note: background anchors do not have regression targets. - """ - if similarity_fn is None: - similarity_fn = self.IOU2DRotatedBoxes - - # Shape validation. - anchor_bboxes = py_utils.HasShape(anchor_bboxes, [-1, 7]) - num_anchor_bboxes, _ = py_utils.GetShape(anchor_bboxes, 2) - gt_bboxes = py_utils.HasShape(gt_bboxes, [-1, 7]) - num_gt_bboxes, _ = py_utils.GetShape(gt_bboxes, 2) - - # Compute similarity score and reduce max by anchors and by ground-truth. - similarity_score = similarity_fn(anchor_bboxes, gt_bboxes) - similarity_score = py_utils.HasShape(similarity_score, - [num_anchor_bboxes, num_gt_bboxes]) - - # Reduce over ground-truth boxes, so we have the max score per anchor. - anchor_max_score = tf.reduce_max(similarity_score, axis=1) - anchor_max_idx = tf.argmax(similarity_score, axis=1) - - if force_match: - # Reduce over anchors, so we have the max score per ground truth box. - gt_max_score = tf.reduce_max(similarity_score, axis=0, keepdims=True) - - # Force matches occur when the top matching gt bbox for an anchor is the - # top matching anchor for the gt bbox. When force matching, we match - # these boxes as long as their similarity score exceeds 0. - force_matches = ( - tf.equal(similarity_score, gt_max_score) - & tf.equal(similarity_score, anchor_max_score[..., tf.newaxis]) - & tf.greater(similarity_score, 0.) - & tf.cast(gt_bboxes_mask[tf.newaxis, ...], tf.bool)) - force_match_indicator = tf.reduce_any(force_matches, axis=1) - force_match_idx = tf.argmax(tf.cast(force_matches, tf.int32), axis=1) - - # In assigning foreground/background anchors later, force_match_indicator - # is used to determine which anchors are force foreground, and the index - # assigned will be taken from anchor_max_idx. - - # Force matchers must also be the max scoring gt bbox per anchor. - # We overwrite anchor_max_idx to ensure that the right match is done. - anchor_max_idx = tf.where(force_match_indicator, force_match_idx, - anchor_max_idx) - - # Ensure that max score boxes are not padded boxes by setting score to 0 - # for boxes that are padded. - gathered_mask = tf.array_ops.batch_gather(gt_bboxes_mask, anchor_max_idx) - anchor_max_score = tf.where( - tf.equal(gathered_mask, 1), anchor_max_score, - tf.zeros_like(anchor_max_score)) - - # Boolean tensors corresponding to whether an anchor is background or - # foreground based on thresholding. - background_anchors = tf.less_equal(anchor_max_score, - background_assignment_threshold) - foreground_anchors = tf.greater_equal(anchor_max_score, - foreground_assignment_threshold) - if force_match: - # Background anchors are below threshold and not force matches. - background_anchors &= ~force_match_indicator - # Foreground anchors are above thresholds or force matches. - foreground_anchors |= force_match_indicator - - # Add dummy background bbox to gt_boxes to facilitate batch gather. - dummy_bbox = tf.constant([[0, 0, 0, 1, 1, 1, 0]], dtype=tf.float32) - - # Since we are concatenating the dummy bbox, the index corresponds to the - # number of boxes. - dummy_bbox_idx = py_utils.GetShape(gt_bboxes, 1)[0] - dummy_bbox_idx = tf.cast(dummy_bbox_idx, tf.int64) - - gt_bboxes = tf.concat([gt_bboxes, dummy_bbox], axis=0) - gt_bboxes_labels = tf.concat([gt_bboxes_labels, [background_class_id]], - axis=0) - - # Gather indices so that all foreground boxes are gathered from gt_bboxes, - # while all background and ignore boxes gather the dummy_bbox. - anchor_gather_idx = tf.where(foreground_anchors, anchor_max_idx, - tf.ones_like(anchor_max_idx) * dummy_bbox_idx) - - # Gather the bboxes and weights. - assigned_gt_bbox = tf.array_ops.batch_gather(gt_bboxes, anchor_gather_idx) - assigned_gt_labels = tf.array_ops.batch_gather(gt_bboxes_labels, - anchor_gather_idx) - - # Set masks for classification and regression losses. - assigned_cls_mask = tf.cast(background_anchors | foreground_anchors, - tf.float32) - assigned_reg_mask = tf.cast(foreground_anchors, tf.float32) - - # Set assigned_gt_idx such that dummy boxes have idx = -1. - assigned_gt_idx = tf.where( - tf.equal(anchor_gather_idx, dummy_bbox_idx), - tf.ones_like(anchor_gather_idx) * -1, anchor_gather_idx) - assigned_gt_idx = tf.cast(assigned_gt_idx, tf.int32) - - return py_utils.NestedMap( - assigned_gt_idx=assigned_gt_idx, - assigned_gt_bbox=assigned_gt_bbox, - assigned_gt_similarity_score=anchor_max_score, - assigned_gt_labels=assigned_gt_labels, - assigned_cls_mask=assigned_cls_mask, - assigned_reg_mask=assigned_reg_mask) - - def LocalizationResiduals(self, anchor_bboxes, assigned_gt_bboxes): - """Computes the anchor residuals for every bbox. - - For a given bbox, compute residuals in the following way: - - Let ``anchor_bbox = (x_a, y_a, z_a, dx_a, dy_a, dz_a, phi_a)`` - and ``assigned_gt_bbox = (x_gt, y_gt, z_gt, dx_gt, dy_gt, dz_gt, phi_gt)`` - - Define ``diagonal_xy = sqrt(dx_a^2 + dy_a^2)`` - - Then the corresponding residuals are given by:: - - x_residual = (x_gt - x_a) / (diagonal_xy) - y_residual = (y_gt - y_a) / (diagonal_xy) - z_residual = (z_gt - z_a) / (dz_a) - - dx_residual = log(dx_gt / dx_a) - dy_residual = log(dy_gt / dy_a) - dz_residual = log(dz_gt / dz_a) - - phi_residual = phi_gt - phi_a - - The normalization for x and y residuals by the diagonal was first - proposed by [1]. Intuitively, this reflects that objects can usually - move freely in the x-y plane, including diagonally. On the other hand, - moving in the z-axis (up and down) can be considered orthogonal to x-y. - - For phi_residual, one way to frame the loss is with - SmoothL1(sine(phi_residual - phi_predicted)). - The use of sine to wrap the phi residual was proposed by [2]. This - stems from the observation that bboxes at phi and phi + pi are the same - bbox, fully overlapping in 3D space, except that the direction is - different. Note that the use of sine makes this residual invariant to - direction when a symmetric loss like SmoothL1 is used. In - ResidualsToBBoxes, we ensure that the phi predicted is between [0, pi). - - The Huber (SmoothL1) loss can then be applied to the delta between these - target residuals and the model predicted residuals. - - [1] VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection - https://arxiv.org/abs/1711.06396 - - [2] SECOND: Sparsely Embedded Convolutional Detection - https://pdfs.semanticscholar.org/5125/a16039cabc6320c908a4764f32596e018ad3.pdf - - Args: - anchor_bboxes: tf.float32. where [..., :7] contains (x, y, z, dx, dy, dz, - phi), corresponding to each anchor bbox parameters. - assigned_gt_bboxes: tf.float32 of the same shape as anchor_bboxes - containing the corresponding assigned ground-truth bboxes. - - Returns: - A tf.float32 tensor of the same shape as anchor_bboxes with target - residuals for every corresponding bbox. - """ - anchor_bboxes_shape = py_utils.GetShape(anchor_bboxes) - anchor_bboxes = py_utils.with_dependencies( - [py_utils.assert_equal(anchor_bboxes_shape[-1], 7)], anchor_bboxes) - assigned_gt_bboxes = py_utils.HasShape(assigned_gt_bboxes, - anchor_bboxes_shape) - - x_a, y_a, z_a, dx_a, dy_a, dz_a, phi_a = tf.unstack( - anchor_bboxes, num=7, axis=-1) - x_gt, y_gt, z_gt, dx_gt, dy_gt, dz_gt, phi_gt = tf.unstack( - assigned_gt_bboxes, num=7, axis=-1) - - diagonal_xy = tf.sqrt(tf.square(dx_a) + tf.square(dy_a)) - - # The anchor dimensions is usually a hard-coded param given to the input - # generator and should not be 0. We use CheckNumerics to ensure that is the - # case. - x_residual = py_utils.CheckNumerics((x_gt - x_a) / diagonal_xy) - y_residual = py_utils.CheckNumerics((y_gt - y_a) / diagonal_xy) - z_residual = py_utils.CheckNumerics((z_gt - z_a) / dz_a) - - dx_residual = py_utils.CheckNumerics(tf.math.log(dx_gt / dx_a)) - dy_residual = py_utils.CheckNumerics(tf.math.log(dy_gt / dy_a)) - dz_residual = py_utils.CheckNumerics(tf.math.log(dz_gt / dz_a)) - - phi_residual = phi_gt - phi_a - - return tf.stack([ - x_residual, y_residual, z_residual, - dx_residual, dy_residual, dz_residual, - phi_residual, - ], axis=-1) # pyformat: disable - - def ResidualsToBBoxes(self, - anchor_bboxes, - residuals, - min_angle_rad=-np.pi, - max_angle_rad=np.pi): - r"""Converts anchor_boxes and residuals to predicted bboxes. - - This converts predicted residuals into bboxes using the following formulae:: - - x_predicted = x_a + x_residual * diagonal_xy - y_predicted = y_a + y_residual * diagonal_xy - z_predicted = z_a + z_residual * dz_a - - dx_predicted = dx_a * exp(dx_residual) - dy_predicted = dy_a * exp(dy_residual) - dz_predicted = dz_a * exp(dz_residual) - - # Adding the residual, and bounding it between - # [min_angle_rad, max_angle_rad] - phi_predicted = NormalizeAngleRad(phi_a + phi_residual, - min_angle_rad, max_angle_rad) - - These equations follow from those in LocalizationResiduals, where we solve - for the \*_gt variables. - - Args: - anchor_bboxes: tf.float32. where [..., :7] contains (x, y, z, dx, dy, dz, - phi), corresponding to each anchor bbox parameters. - residuals: tf.float32 of the same shape as anchor_bboxes containing - predicted residuals at each anchor location. - min_angle_rad: Scalar with the minimum angle allowed (before wrapping) - in radians. - max_angle_rad: Scalar with the maximum angle allowed (before wrapping) - in radians. This value usually should be pi. - - Returns: - A tf.float32 tensor of the same shape as anchor_bboxes with predicted - bboxes. - """ - anchor_bboxes_shape = py_utils.GetShape(anchor_bboxes) - anchor_bboxes = py_utils.with_dependencies( - [py_utils.assert_equal(anchor_bboxes_shape[-1], 7)], anchor_bboxes) - residuals = py_utils.HasShape(residuals, anchor_bboxes_shape) - - x_a, y_a, z_a, dx_a, dy_a, dz_a, phi_a = tf.unstack( - anchor_bboxes, num=7, axis=-1) - (x_residual, y_residual, z_residual, dx_residual, dy_residual, dz_residual, - phi_residual) = tf.unstack( - residuals, num=7, axis=-1) - - diagonal_xy = tf.sqrt(tf.square(dx_a) + tf.square(dy_a)) - - x_predicted = x_a + x_residual * diagonal_xy - y_predicted = y_a + y_residual * diagonal_xy - z_predicted = z_a + z_residual * dz_a - - dx_predicted = dx_a * tf.exp(dx_residual) - dy_predicted = dy_a * tf.exp(dy_residual) - dz_predicted = dz_a * tf.exp(dz_residual) - - # We bound the angle between [min_angle_rad, max_angle_rad], which should - # be passed in depending on the heading handling in the calling model. - # If the model uses a sine(delta_phi) transformation in the loss, then it - # cannot distinguish direction and a [0, np.pi] - # [min_angle_rad, max_angle_rad] should be used. - # If there is a heading encoding that is directional, most likely you - # should use a [-np.pi, np.pi] [min_angle_rad, max_angle_rad]. - phi_predicted = phi_a + phi_residual - phi_predicted = geometry.WrapAngleRad(phi_predicted, min_angle_rad, - max_angle_rad) - - return tf.stack([ - x_predicted, y_predicted, z_predicted, - dx_predicted, dy_predicted, dz_predicted, - phi_predicted, - ], axis=-1) # pyformat: disable - - def NMSIndices(self, - bboxes, - scores, - max_output_size, - nms_iou_threshold=0.3, - score_threshold=0.01): - """Apply NMS to a series of 3d bounding boxes in 7-DOF format. - - Args: - bboxes: A [num_boxes, 7] floating point Tensor of bounding boxes in [x, y, - z, dx, dy, dz, phi] format. - scores: A [num_boxes] floating point Tensor containing box - scores. - max_output_size: Maximum number of boxes to predict per input. - nms_iou_threshold: IoU threshold to use when determining whether two boxes - overlap for purposes of suppression. - score_threshold: The score threshold passed to NMS that allows NMS to - quickly ignore irrelevant boxes. - - Returns: - The NMS indices and the mask of the padded indices. - """ - bboxes = py_utils.HasShape(bboxes, [-1, 7]) - - # Extract x, y, w, h, then convert to extrema. - # - # Note that we drop the rotation angle because we don't have an NMS - # operation that takes rotation into account. - bboxes_2d = tf.stack( - [bboxes[:, 0], bboxes[:, 1], bboxes[:, 3], bboxes[:, 4]], axis=-1) - bboxes_extrema = geometry.XYWHToBBoxes(bboxes_2d) - - # Compute NMS with padding; we use the padded version so this function can - # be used in a map_fn. This function returns the scalar number of boxes - # for each example. - # - # We use an IoU threshold of 0.3 since our anchor boxes have rotations - # that make the default IoU threshold of 0.5 possibly too high. - nms_index_padded, num_valid = tf.image.non_max_suppression_padded( - bboxes_extrema, - scores, - iou_threshold=nms_iou_threshold, - max_output_size=max_output_size, - score_threshold=score_threshold, - pad_to_max_output_size=True) - - # Return the mask of valid indices instead of just a scalar number. - mask = tf.concat( - [tf.ones([num_valid]), - tf.zeros([max_output_size - num_valid])], axis=0) - - nms_index_padded = tf.where(mask > 0, nms_index_padded, - tf.zeros_like(nms_index_padded)) - return nms_index_padded, mask - - def BatchedNMSIndices(self, - bboxes, - scores, - nms_iou_threshold=0.3, - score_threshold=0.01, - max_num_boxes=None): - """Batched version of NMSIndices. - - Args: - bboxes: A [batch_size, num_boxes, 7] floating point Tensor of bounding - boxes in [x, y, z, dx, dy, dz, phi] format. - scores: A [batch_size, num_boxes, num_classes] floating point Tensor - containing box scores. - nms_iou_threshold: IoU threshold to use when determining whether two boxes - overlap for purposes of suppression. - score_threshold: The score threshold passed to NMS that allows NMS to - quickly ignore irrelevant boxes. - max_num_boxes: The maximum number of boxes per example to emit. If None, - this value is set to num_boxes from the shape of bboxes. - - Returns: - The NMS indices and the mask of the padded indices for each example - in the batch. - """ - batch_size, num_boxes = py_utils.GetShape(bboxes, 2) - - if max_num_boxes is not None: - max_output_size = max_num_boxes - else: - max_output_size = num_boxes - - output_shape = [batch_size, max_output_size] - - def NMSBody(args): - bbox, score = args - return self.NMSIndices(bbox, score, max_output_size, nms_iou_threshold, - score_threshold) - - nms_indices, valid_mask = tf.map_fn( - fn=NMSBody, - elems=(bboxes, scores), - dtype=(tf.int32, tf.float32), - back_prop=False) - - nms_indices = py_utils.PadOrTrimTo(nms_indices, output_shape) - return nms_indices, valid_mask - - def BatchedOrientedNMSIndices(self, bboxes, scores, nms_iou_threshold, - score_threshold, max_boxes_per_class): - """Runs batched version of a Per-Class 3D (7-DOF) Non Max Suppression. - - All outputs have shape [batch_size, num_classes, max_boxes_per_class]. - - Args: - bboxes: A [batch_size, num_boxes, 7] floating point Tensor of bounding - boxes in [x, y, z, dx, dy, dz, phi] format. - scores: A [batch_size, num_boxes, num_classes] floating point Tensor - containing box scores. - nms_iou_threshold: Either a float or a list of floats of len num_classes - with the IoU threshold to use when determining whether two boxes overlap - for purposes of suppression. - score_threshold: Either a float or a list of floats of len num_classes - with the score threshold that allows NMS to quickly ignore boxes. - max_boxes_per_class: An integer scalar with the maximum number of boxes - per example to emit per class. - - Returns: - A tuple of 3 tensors: - - - bbox_indices: An int32 Tensor with the indices of the chosen boxes. - Values are in sort order until the class_idx switches. - - bbox_scores: A float32 Tensor with the score for each box. - - valid_mask: A float32 Tensor with 1/0 values indicating the validity of - each box. 1 indicates valid, and 0 invalid. - """ - bboxes = py_utils.HasShape(bboxes, [-1, -1, 7]) - batch_size, num_boxes = py_utils.GetShape(bboxes, 2) - scores = py_utils.HasShape(scores, [batch_size, num_boxes, -1]) - _, _, num_classes = py_utils.GetShape(scores) - - # Force the thresholds to be tensors of len num_classes - nms_iou_threshold = tf.broadcast_to( - tf.convert_to_tensor(nms_iou_threshold), [num_classes]) - score_threshold = tf.broadcast_to( - tf.convert_to_tensor(score_threshold), [num_classes]) - - def NMSBody(args): - per_sample_bboxes, per_sample_scores = args - indices, scores, mask = ops.non_max_suppression_3d( - per_sample_bboxes, - per_sample_scores, - nms_iou_threshold=nms_iou_threshold, - score_threshold=score_threshold, - max_boxes_per_class=max_boxes_per_class) - return indices, scores, mask - - bbox_indices, bbox_scores, valid_mask = tf.map_fn( - fn=NMSBody, - elems=(bboxes, scores), - dtype=(tf.int32, tf.float32, tf.float32), - back_prop=False) - - output_shape = [batch_size, num_classes, max_boxes_per_class] - bbox_indices = py_utils.PadOrTrimTo(bbox_indices, output_shape) - bbox_scores = py_utils.PadOrTrimTo(bbox_scores, output_shape) - valid_mask = py_utils.PadOrTrimTo(valid_mask, output_shape) - return bbox_indices, bbox_scores, valid_mask - - def CornersToImagePlane(self, corners, velo_to_image_plane): - """Project 3d box corners to the image plane. - - Args: - corners: A [batch, num_boxes, 8, 3] floating point tensor containing the 8 - corners points for each 3d bounding box. - velo_to_image_plane: A [batch, 3, 4] batch set of projection matrices from - velo xyz to image plane xy. After multiplication, you need to divide by - last coordinate to recover 2D pixel locations. - - Returns: - A [batch, num_boxes, 8, 2] floating point Tensor containing the 3D - bounding box corners projected to the image plane. - """ - batch_size, num_boxes, _, _ = py_utils.GetShape(corners, 4) - - def CornersToPlaneBody(args): - """Body of function to convert each bounding box to the image plane.""" - (corners, velo_to_image_plane) = args - # corners[i] is [num_boxes, 8, 3]: flatten the points in this batch and do - # the conversion in one call. - bbox_corners = tf.reshape(corners, [-1, 3]) - image_plane_corners = geometry.PointsToImagePlane(bbox_corners, - velo_to_image_plane) - image_plane_corners = tf.reshape(image_plane_corners, [-1, 8, 2]) - return image_plane_corners - - corners_in_image_plane = tf.map_fn( - fn=CornersToPlaneBody, - elems=(corners, velo_to_image_plane), - dtype=tf.float32, - back_prop=False) - - corners_in_image_plane = py_utils.HasShape(corners_in_image_plane, - [batch_size, num_boxes, 8, 2]) - return corners_in_image_plane - - -def RandomPadOrTrimTo(tensor_list, num_points_out, seed=None): - """Pads or Trims a list of Tensors on the major dimension. - - Slices if there are more points, or pads if not enough. - - In this implementation: - Padded points are random duplications of real points. - Sliced points are a random subset of the real points. - - Args: - tensor_list: A list of tf.Tensor objects to pad or trim along first dim. All - tensors are expected to have the same first dimension. - num_points_out: An int for the requested number of points to trim/pad to. - seed: Random seed to use for random generators. - - Returns: - A tuple of output_tensors and a padding indicator. - - - output_tensors: A list of padded or trimmed versions of our tensor_list - input tensors, all with the same first dimension. - - padding: A tf.float32 tf.Tensor of shape [num_points_out] with 0 if the - point is real, 1 if it is padded. - """ - actual_num = tf.shape(tensor_list[0])[0] - point_idx = tf.range(num_points_out, dtype=tf.int32) - padding_tensor = tf.where(point_idx < actual_num, - tf.zeros([num_points_out], dtype=tf.float32), - tf.ones([num_points_out], dtype=tf.float32)) - - def _Slicing(): - # Choose a random set of indices. - indices = tf.range(actual_num) - indices = tf.random.shuffle(indices, seed=seed)[:num_points_out] - return [tf.gather(t, indices, axis=0) for t in tensor_list] - - def _Padding(): - indices = tf.random.uniform([num_points_out - actual_num], - minval=0, - maxval=actual_num, - dtype=tf.int32, - seed=seed) - padded = [] - for t in tensor_list: - padded.append(tf.concat([t, tf.gather(t, indices, axis=0)], axis=0)) - return padded - - def _PadZeros(): - padded = [] - for t in tensor_list: - shape = tf.concat([[num_points_out], tf.shape(t)[1:]], axis=0) - padded.append(tf.zeros(shape=shape, dtype=t.dtype)) - return padded - - data = tf.cond( - actual_num > num_points_out, - _Slicing, lambda: tf.cond(tf.equal(actual_num, 0), _PadZeros, _Padding)) - return (data, padding_tensor) diff --git a/lingvo/tasks/car/detection_3d_lib_test.py b/lingvo/tasks/car/detection_3d_lib_test.py deleted file mode 100644 index b5c6dbe5..00000000 --- a/lingvo/tasks/car/detection_3d_lib_test.py +++ /dev/null @@ -1,528 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for detection_3d_lib.""" - -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import detection_3d_lib -import numpy as np - - -class Utils3DTest(test_utils.TestCase): - - def testScaledHuberLoss(self): - utils_3d = detection_3d_lib.Utils3D() - labels = tf.constant([1, 2, 3], dtype=tf.float32) - # Predictions are less than delta, exactly at delta, and more than delta, - # respectively. - predictions = tf.constant([1.4, 1.2, 4.0], dtype=tf.float32) - delta = 0.8 - expected_loss = [ - 1. / delta * 0.5 * (0.4)**2, - 0.5 * delta, - 1.0 - 0.5 * delta, - ] - loss = utils_3d.ScaledHuberLoss(labels, predictions, delta=delta) - with self.session(): - actual_loss = self.evaluate(loss) - self.assertAllClose(actual_loss, expected_loss) - - def testCornerLoss(self): - utils_3d = detection_3d_lib.Utils3D() - gt_bboxes = tf.constant([[[[0., 0., 0., 1., 1., 1., 0.], - [0., 0., 0., 1., 1., 1., 0.], - [0., 0., 0., 1., 1., 1., 0.], - [0., 0., 0., 1., 1., 1., 0.], - [0., 0., 0., 1., 1., 1., 0.]]]]) - predicted_bboxes = tf.constant([[[ - [0., 0., 0., 1., 1., 1., 0.], # Same as GT - [0., 0., 0., 1., 1., 1., np.pi], # Opposite heading - [0., 0., 0., 1., 1., 1., np.pi / 2.], # 90-deg rotation - [1., 1., 1., 1., 1., 1., 0], # Different center - [0., 0., 0., 2., 2., 2., 0], # Different size - ]]]) - loss = utils_3d.CornerLoss(gt_bboxes, predicted_bboxes) - with self.session(): - actual_loss = self.evaluate(loss) - self.assertEqual(actual_loss.shape, (1, 1, 5)) - - def testCornerLossAsym(self): - utils_3d = detection_3d_lib.Utils3D() - gt_bboxes = tf.constant([[[[0., 0., 0., 1., 1., 1., 0.], - [0., 0., 0., 1., 1., 1., 0.]]]]) - predicted_bboxes = tf.constant([[[ - [0., 0., 0., 1., 1., 1., 0.], # Same as GT - [0., 0., 0., 1., 1., 1., np.pi], # Opposite heading - ]]]) - expected_loss = [[[ - 0., - 8, - ]]] - loss = utils_3d.CornerLoss(gt_bboxes, predicted_bboxes, symmetric=False) - with self.session(): - actual_loss = self.evaluate(loss) - print(actual_loss) - self.assertAllClose(actual_loss, expected_loss) - - def testCreateDenseCoordinates(self): - utils_3d = detection_3d_lib.Utils3D() - one_dim = utils_3d.CreateDenseCoordinates([(0.5, 1.5, 3)]) - with self.session(): - actual_one_dim = self.evaluate(one_dim) - self.assertAllEqual(actual_one_dim, [[0.5], [1.0], [1.5]]) - - two_by_two = utils_3d.CreateDenseCoordinates([(0, 1, 2), (1, 2, 2)]) - with self.session(): - actual_two_by_two = self.evaluate(two_by_two) - self.assertAllEqual(actual_two_by_two, [[0, 1], [0, 2], [1, 1], [1, 2]]) - - three_dims = utils_3d.CreateDenseCoordinates([(0, 1, 5), (1, 2, 5), - (0, 10, 5)]) - self.assertAllEqual(three_dims.shape, [5 * 5 * 5, 3]) - - def testMakeAnchorBoxesWithoutRotation(self): - utils_3d = detection_3d_lib.Utils3D() - anchor_bboxes = utils_3d.MakeAnchorBoxes( - anchor_centers=tf.constant([[0, 0, 0], [1, 1, 1]], dtype=tf.float32), - anchor_box_dimensions=tf.constant([[1, 2, 3], [3, 4, 5]], - dtype=tf.float32), - anchor_box_offsets=tf.constant([[0, 0, 0], [1, 1, 1]], - dtype=tf.float32), - anchor_box_rotations=None) - with self.session(): - actual_anchor_bboxes = self.evaluate(anchor_bboxes) - self.assertAllEqual(actual_anchor_bboxes, - [[[0, 0, 0, 1, 2, 3, 0], [1, 1, 1, 3, 4, 5, 0]], - [[1, 1, 1, 1, 2, 3, 0], [2, 2, 2, 3, 4, 5, 0]]]) - - def testMakeAnchorBoxesWithRotation(self): - utils_3d = detection_3d_lib.Utils3D() - anchor_bboxes = utils_3d.MakeAnchorBoxes( - anchor_centers=tf.constant([[0, 0, 0], [1, 1, 1]], dtype=tf.float32), - anchor_box_dimensions=tf.constant([[1, 2, 3], [3, 4, 5]], - dtype=tf.float32), - anchor_box_offsets=tf.constant([[0, 0, 0], [1, 1, 1]], - dtype=tf.float32), - anchor_box_rotations=tf.constant([0, 0.5])) - with self.session(): - actual_anchor_bboxes = self.evaluate(anchor_bboxes) - self.assertAllEqual(actual_anchor_bboxes, - [[[0, 0, 0, 1, 2, 3, 0], [1, 1, 1, 3, 4, 5, 0.5]], - [[1, 1, 1, 1, 2, 3, 0], [2, 2, 2, 3, 4, 5, 0.5]]]) - - def testAssignAnchors(self): - utils_3d = detection_3d_lib.Utils3D() - anchor_bboxes = tf.constant( - [ - [0, 1, 1, 2, 2, 2, 0], # Ignored - [-1, 1, 1, 2, 2, 2, 0], # Background - [0.9, 1, 1, 2, 2, 2, 0], # Foreground - [5, 5, 5, 1, 1, 2, 0], # Force matched to foreground - ], - dtype=tf.float32) - - # Second gt box should be forced match, third one should be ignored. - gt_bboxes = tf.constant([[1, 1, 1, 2, 2, 2, 0], [5, 5, 5, 2, 2, 2, 0], - [10, 10, 10, 2, 2, 2, 0]], - dtype=tf.float32) - gt_bboxes_labels = tf.constant([1, 2, 3]) - gt_bboxes_mask = tf.constant([1, 1, 1]) - - assigned_anchors = utils_3d.AssignAnchors( - anchor_bboxes, - gt_bboxes, - gt_bboxes_labels, - gt_bboxes_mask, - foreground_assignment_threshold=0.5, - background_assignment_threshold=0.25) - with self.session(): - actual_assigned_anchors, gt_bboxes = self.evaluate( - (assigned_anchors, gt_bboxes)) - - self.assertAllEqual(actual_assigned_anchors.assigned_gt_idx, - [-1, -1, 0, 1]) - self.assertAllEqual(actual_assigned_anchors.assigned_gt_labels, - [0, 0, 1, 2]) - self.assertAllEqual(actual_assigned_anchors.assigned_gt_bbox, [ - [0, 0, 0, 1, 1, 1, 0], - [0, 0, 0, 1, 1, 1, 0], - [1, 1, 1, 2, 2, 2, 0], - [5, 5, 5, 2, 2, 2, 0], - ]) - - self.assertAllEqual(actual_assigned_anchors.assigned_cls_mask, - [0, 1, 1, 1]) - self.assertAllEqual(actual_assigned_anchors.assigned_reg_mask, - [0, 0, 1, 1]) - - self.assertAllEqual( - actual_assigned_anchors.assigned_gt_similarity_score.shape, [4]) - - def testAssignAnchorsWithoutForceMatch(self): - utils_3d = detection_3d_lib.Utils3D() - anchor_bboxes = tf.constant( - [ - [0, 1, 1, 2, 2, 2, 0], # Ignored - [-1, 1, 1, 2, 2, 2, 0], # Background - [0.9, 1, 1, 2, 2, 2, 0], # Foreground - [5, 5, 5, 1, 1, 2, 0], # Background, since no force match - ], - dtype=tf.float32) - - # Second gt box should be forced match, third one should be ignored. - gt_bboxes = tf.constant([[1, 1, 1, 2, 2, 2, 0], [5, 5, 5, 2, 2, 2, 0], - [10, 10, 10, 2, 2, 2, 0]], - dtype=tf.float32) - gt_bboxes_labels = tf.constant([1, 2, 3]) - gt_bboxes_mask = tf.constant([1, 1, 1]) - - assigned_anchors = utils_3d.AssignAnchors( - anchor_bboxes, - gt_bboxes, - gt_bboxes_labels, - gt_bboxes_mask, - foreground_assignment_threshold=0.5, - background_assignment_threshold=0.25, - force_match=False) - with self.session(): - actual_assigned_anchors, gt_bboxes = self.evaluate( - (assigned_anchors, gt_bboxes)) - - self.assertAllEqual(actual_assigned_anchors.assigned_gt_idx, - [-1, -1, 0, -1]) - self.assertAllEqual(actual_assigned_anchors.assigned_gt_labels, - [0, 0, 1, 0]) - self.assertAllEqual(actual_assigned_anchors.assigned_gt_bbox, [ - [0, 0, 0, 1, 1, 1, 0], - [0, 0, 0, 1, 1, 1, 0], - [1, 1, 1, 2, 2, 2, 0], - [0, 0, 0, 1, 1, 1, 0], - ]) - - self.assertAllEqual(actual_assigned_anchors.assigned_cls_mask, - [0, 1, 1, 1]) - self.assertAllEqual(actual_assigned_anchors.assigned_reg_mask, - [0, 0, 1, 0]) - - self.assertAllEqual( - actual_assigned_anchors.assigned_gt_similarity_score.shape, [4]) - - def testAssignAnchorsWithPadding(self): - utils_3d = detection_3d_lib.Utils3D() - anchor_bboxes = tf.constant([[0, 0, 0, 1, 2, 3, 0], [1, 1, 1, 3, 4, 5, 0.5], - [1, 1, 1, 1, 2, 3, 0], [2, 2, 2, 3, 4, 5, - 0.5]]) - gt_bboxes = anchor_bboxes + 0.05 - gt_bboxes_labels = tf.constant([1, 2, 3, 4]) - gt_bboxes_mask = tf.constant([1, 1, 0, 0]) - - assigned_anchors = utils_3d.AssignAnchors(anchor_bboxes, gt_bboxes, - gt_bboxes_labels, gt_bboxes_mask) - with self.session(): - actual_assigned_anchors, gt_bboxes = self.evaluate( - (assigned_anchors, gt_bboxes)) - - # Last two boxes are padded, thus not assigned. - self.assertAllEqual(actual_assigned_anchors.assigned_gt_idx, - [0, 1, -1, -1]) - self.assertAllEqual(actual_assigned_anchors.assigned_gt_labels, - [1, 2, 0, 0]) - self.assertAllEqual(actual_assigned_anchors.assigned_gt_bbox[0:2, :], - gt_bboxes[0:2, :]) - - # 2nd and 3rd should match dummy bbox. - self.assertAllEqual(actual_assigned_anchors.assigned_gt_bbox[2, :], - [0, 0, 0, 1, 1, 1, 0]) - self.assertAllEqual(actual_assigned_anchors.assigned_gt_bbox[3, :], - [0, 0, 0, 1, 1, 1, 0]) - - # First two are foreground, last two are background. - self.assertAllEqual(actual_assigned_anchors.assigned_cls_mask, - [1, 1, 1, 1]) - self.assertAllEqual(actual_assigned_anchors.assigned_reg_mask, - [1, 1, 0, 0]) - - self.assertAllEqual( - actual_assigned_anchors.assigned_gt_similarity_score.shape, [4]) - - def testLocalizationResiduals(self): - utils_3d = detection_3d_lib.Utils3D() - - anchor_bboxes = tf.constant([[1, 2, 3, 4, 3, 6, 0]], dtype=tf.float32) - gt_bboxes = tf.constant([[2, 22, 303, 4, 9, 12, 0.5]], dtype=tf.float32) - - # diagonal_xy = 5 [since sqrt(3^2 + 4^2) = 5] - expected_residuals = np.asarray([[ - 1. / 5, - 20. / 5, - 300. / 6, - 0., - np.log(9. / 3.), - np.log(12. / 6.), - 0.5, - ]]) - residuals = utils_3d.LocalizationResiduals(anchor_bboxes, gt_bboxes) - - with self.session(): - actual_residuals = self.evaluate(residuals) - self.assertAllClose(actual_residuals, expected_residuals) - - def testResidualsToBBoxes(self): - utils_3d = detection_3d_lib.Utils3D() - - anchor_bboxes = tf.constant([[1, 2, 3, 4, 3, 6, 0]], dtype=tf.float32) - expected_predicted_bboxes = np.asarray([[2, 22, 303, 4, 9, 12, 0.5]]) - - residuals = tf.constant([[ - 1. / 5, 20. / 5, 300. / 6, 0., - np.log(9. / 3.), - np.log(12. / 6.), - 0.5, - ]], dtype=tf.float32) # pyformat: disable - predicted_bboxes = utils_3d.ResidualsToBBoxes(anchor_bboxes, residuals) - - with self.session(): - actual_predicted_bboxes = self.evaluate(predicted_bboxes) - self.assertAllClose(actual_predicted_bboxes, expected_predicted_bboxes) - - def testResidualsToBBoxesNegPiToPi(self): - utils_3d = detection_3d_lib.Utils3D() - - anchor_bboxes = tf.constant( - [[1, 2, 3, 4, 3, 6, 0.2], [1, 2, 3, 4, 3, 6, -0.2]], dtype=tf.float32) - expected_predicted_bboxes = np.asarray( - [[2, 22, 303, 4, 9, 12, -np.pi + 0.2], - [2, 22, 303, 4, 9, 12, np.pi - 0.2]]) - - residuals = tf.constant([ - [1. / 5, 20. / 5, 300. / 6, 0., - np.log(9. / 3.), np.log(12. / 6.), np.pi], - [1. / 5, 20. / 5, 300. / 6, 0., - np.log(9. / 3.), np.log(12. / 6.), -np.pi] - ], dtype=tf.float32) # pyformat: disable - predicted_bboxes = utils_3d.ResidualsToBBoxes( - anchor_bboxes, residuals, min_angle_rad=-np.pi, max_angle_rad=np.pi) - - with self.session(): - actual_predicted_bboxes = self.evaluate(predicted_bboxes) - self.assertAllClose(actual_predicted_bboxes, expected_predicted_bboxes) - - def testZeroResiduals(self): - utils_3d = detection_3d_lib.Utils3D() - - anchor_bboxes = tf.constant([[1, 2, 3, 4, 3, 6, 0]], dtype=tf.float32) - expected_predicted_bboxes = np.asarray([[1, 2, 3, 4, 3, 6, 0]]) - - residuals = tf.zeros((1, 7)) - predicted_bboxes = utils_3d.ResidualsToBBoxes(anchor_bboxes, residuals) - - with self.session(): - actual_predicted_bboxes = self.evaluate(predicted_bboxes) - self.assertAllClose(actual_predicted_bboxes, expected_predicted_bboxes) - - def testResidualsToBBoxPhiFloorMod(self): - utils_3d = detection_3d_lib.Utils3D() - - anchor_bboxes = tf.constant([[1, 2, 3, 4, 3, 6, np.pi]], dtype=tf.float32) - - # We expected the returned phi value to be floormod w.r.t. pi. - expected_predicted_bboxes = np.asarray([[1, 2, 3, 4, 3, 6, 1.]]) - - residuals = tf.constant([[0, 0, 0, 0, 0, 0, 1.0]], dtype=tf.float32) - predicted_bboxes = utils_3d.ResidualsToBBoxes( - anchor_bboxes, residuals, min_angle_rad=0.0) - - with self.session(): - actual_predicted_bboxes = self.evaluate(predicted_bboxes) - self.assertAllClose(actual_predicted_bboxes, expected_predicted_bboxes) - - def testNMSIndices(self): - utils_3d = detection_3d_lib.Utils3D() - - # Create three anchor boxes, two largely overlapping and one - # not overlapping with either. - # - # Set a batch size of 1 and use the Batched version to test - # both functions. - anchor_bboxes = tf.constant( - [[[1, 2, 3, 4, 3, 6, 0.], [1, 2, 2, 4, 3, 6, 0.], - [10, 20, 30, 4, 3, 6, 0.]]], - dtype=tf.float32) - - # Treat them all as high scores. - scores = tf.constant([[0.7, 0.8, 0.6]]) - - with self.session(): - nms_indices, valid_mask = utils_3d.BatchedNMSIndices( - anchor_bboxes, scores) - indices, mask = self.evaluate([nms_indices, valid_mask]) - # One box is filtered out. - self.assertEqual(2, np.sum(mask)) - # The two boxes that remain are the second one (because of its higher - # score) and the last one (which overlaps with nothing). - self.assertAllEqual([[1, 2, 0]], indices) - - # Flip the scores; expect the first box to be chosen instead. - # Change the last box's threshold to be 0.0, so that the - # default setting for the score threshold filters it out too. - scores_2 = tf.constant([[0.8, 0.7, 0.0]]) - nms_indices, valid_mask = utils_3d.BatchedNMSIndices( - anchor_bboxes, scores_2) - indices, mask = self.evaluate([nms_indices, valid_mask]) - self.assertEqual(1, np.sum(mask)) - self.assertAllEqual([[0, 0, 0]], indices) - - def testOrientedNMSIndices(self): - utils_3d = detection_3d_lib.Utils3D() - - # Assignments and IoU scores calculated offline. - bboxes_data = tf.constant( - [[ - [10.35, 8.429, -1.003, 3.7, 1.64, 1.49, 1.582], - [10.35, 8.429, -1.003, 3.7, 1.64, 1.49, 0.0], # box 0 rotated - [11.5, 8.429, -1.003, 3.7, 1.64, 1.49, 1.0], # Rotated to overlap - [13.01, 8.149, -0.953, 4.02, 1.55, 1.52, 1.592], - [13.51, 8.39, -1.0, 4.02, 1.55, 1.52, 1.592], # Slight translation - [13.51, 8.39, -1.0, 1.0, 1.0, 1.52, 1.592], # Smaller box - [13.51, 8.39, -1.0, 1.0, 1.0, 1.52, 1.9], # Smaller box - ]], - dtype=tf.float32) - - # Notes on the data: - # Lets say we have 3 classes and a thresh of 0.1 - # Keep box [0, 3] for class 0 - # Keep box [6] only for class 1 - # Keep box [2] for class 2 - scores_data = tf.constant([[ - [0.9, 0.1, 0.0], - [0.89, 0.1, 0.01], - [0.5, 0.01, 0.49], - [0.8, 0.1, 0.1], - [0.79, 0.11, 0.2], - [0.2, 0.8, 0.1], - [0.1, 0.9, 0.0], - ]], - dtype=tf.float32) - - with self.session(): - outputs = utils_3d.BatchedOrientedNMSIndices( - bboxes_data, - scores_data, - nms_iou_threshold=0.1, - score_threshold=0.3, - max_boxes_per_class=5) - indices, scores, valid_mask = self.evaluate(outputs) - - class_masks = [ - valid_mask[0, cls_idx, :].astype(np.bool) for cls_idx in range(3) - ] - # Check the correct number of valid results per class - self.assertEqual(class_masks[0].sum(), 2) - self.assertEqual(class_masks[1].sum(), 1) - self.assertEqual(class_masks[2].sum(), 1) - - # Check the results for each class - self.assertAllEqual(indices[0, 0, class_masks[0]], [0, 3]) - self.assertAllClose(scores[0, 0, class_masks[0]], [0.9, 0.8]) - - self.assertAllEqual(indices[0, 1, class_masks[1]], [6]) - self.assertAllClose(scores[0, 1, class_masks[1]], [0.9]) - - self.assertAllEqual(indices[0, 2, class_masks[2]], [2]) - self.assertAllClose(scores[0, 2, class_masks[2]], [0.49]) - - # Use a list of score thresholds instead - outputs = utils_3d.BatchedOrientedNMSIndices( - bboxes_data, - scores_data, - nms_iou_threshold=[0.1, 0.1, 0.1], - score_threshold=[0.899, 0.5, 0.3], - max_boxes_per_class=5) - indices, scores, valid_mask = self.evaluate(outputs) - - class_masks = [ - valid_mask[0, cls_idx, :].astype(np.bool) for cls_idx in range(3) - ] - # Check the correct number of valid results per class - self.assertEqual(class_masks[0].sum(), 1) - self.assertEqual(class_masks[1].sum(), 1) - self.assertEqual(class_masks[2].sum(), 1) - - # Check the results for each class - self.assertAllEqual(indices[0, 0, class_masks[0]], [0]) - self.assertAllClose(scores[0, 0, class_masks[0]], [0.9]) - - self.assertAllEqual(indices[0, 1, class_masks[1]], [6]) - self.assertAllClose(scores[0, 1, class_masks[1]], [0.9]) - - self.assertAllEqual(indices[0, 2, class_masks[2]], [2]) - self.assertAllClose(scores[0, 2, class_masks[2]], [0.49]) - - def testRandomPadOrTrimToTrim(self): - points = tf.constant([[1., 2., 3.], [4., 5., 6.], [7., 8., 9.], - [10., 11., 12.]]) - features = tf.constant([[100.], [200.], [300.], [400.]]) - - points, features = detection_3d_lib.RandomPadOrTrimTo([points, features], - 2, - seed=123)[0] - with self.session(): - points_np, features_np = self.evaluate([points, features]) - # Slicing choose a random 2 points. - self.assertAllClose([[1., 2., 3.], [10., 11., 12.]], points_np) - self.assertAllClose([[100.], [400.]], features_np) - - def testRandomPadOrTrimToPad(self): - points = tf.constant([[1., 2., 3.], [4., 5., 6.], [7., 8., 9.], - [10., 11., 12.]]) - features = tf.constant([[100.], [200.], [300.], [400.]]) - - points, features = detection_3d_lib.RandomPadOrTrimTo([points, features], - 10, - seed=123)[0] - with self.session(): - points_np, features_np = self.evaluate([points, features]) - # Padding repeats a random set of points. - self.assertAllClose([[1., 2., 3.], [1., 2., 3.], [10., 11., 12.], - [7., 8., 9.], [7., 8., 9.], [4., 5., 6.]], - points_np[4:]) - self.assertAllClose([[100.], [100.], [400.], [300.], [300.], [200.]], - features_np[4:]) - - def testRandomPadOrTrimToEmpty(self): - points = tf.constant([[1., 2., 3.]]) - features = tf.constant([[100.]]) - points, features = detection_3d_lib.RandomPadOrTrimTo( - [points[0:0], features[0:0]], 10, seed=123)[0] - with self.session(): - points_np, features_np = self.evaluate([points, features]) - self.assertAllClose(points_np, np.zeros(shape=(10, 3))) - self.assertAllClose(features_np, np.zeros(shape=(10, 1))) - - def testCornersToImagePlane(self): - utils_3d = detection_3d_lib.Utils3D() - batch = 4 - num_boxes = 50 - - corners = tf.random.uniform([batch, num_boxes, 8, 3]) - velo_to_image_plane = tf.random.uniform([batch, 3, 4]) - corners_to_image_plane = utils_3d.CornersToImagePlane( - corners, velo_to_image_plane) - self.assertEqual([batch, num_boxes, 8, 2], corners_to_image_plane.shape) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/detection_3d_metrics.py b/lingvo/tasks/car/detection_3d_metrics.py deleted file mode 100644 index b5b9d9d8..00000000 --- a/lingvo/tasks/car/detection_3d_metrics.py +++ /dev/null @@ -1,546 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Metrics for 3D detection problems.""" - -from lingvo import compat as tf -from lingvo.core import metrics -from lingvo.core import plot -from lingvo.core import py_utils -from lingvo.tasks.car import summary -from lingvo.tasks.car import transform_util -import matplotlib.colors as matplotlib_colors -import matplotlib.patches as matplotlib_patches -import matplotlib.patheffects as path_effects -import numpy as np -import PIL.Image as Image -import PIL.ImageDraw as ImageDraw -import PIL.ImageFont as ImageFont -from tensorboard.plugins.mesh import summary as mesh_summary - - -class TopDownVisualizationMetric(metrics.BaseMetric): - """Top-down detection visualization, expecting 3D laser points and 2D bboxes. - - Updates to this metric is expected to be `.NestedMap` containing: - - visualization_labels: [N, B1] int tensor containing visualization labels. - - predicted_bboxes: [N, B1, 5] float tensor containing predicted 2D bboxes - each with (x, y, dx, dy, phi). - - visualization_weights: [N, B1] float tensor containing weights for each - prediction. predictions with 0 weight will not be drawn. - - points_xyz: [N, P, 3] float tensor containing points (x, y, z) coordinates. - - points_padding: [N, P] tensor containing 1 if the point is a padded point. - - gt_bboxes_2d: [N, B2, 5] float tensor containing ground-truth 2D bboxes. - - gt_bboxes_2d_weights: [N, B2] float tensor containing weights for each - ground-truth. predictions with 0 weight will not be drawn. The - ground-truth mask can be used here. - - labels: [N, B2] int tensor containing ground-truth labels. - - difficulties: [N, B2]: int tensor containing the difficulty levels of - each groundtruth box. - - Default parameters visualize the area around the car, with the car centered in - the image, over a 32m x 48m range. - - Ground-truth boxes will be drawn with color=cyan (see DrawBBoxesOnImages for - details). Predicted boxes will be drawn with a color from the PIL color list, - with a different color per class. - """ - - def __init__(self, - top_down_transform, - class_id_to_name=None, - image_height=1536, - image_width=1024, - figsize=None, - ground_removal_threshold=-1.35, - sampler_num_samples=8): - """Initialize TopDownVisualizationMetric. - - Args: - top_down_transform: transform_util.Transform object that specifies - how to transform a coordinate in the world coordinate to the top - down projection. See documentation for - transform_util.MakeCarToImageTransform for more details on - configuration. - class_id_to_name: Dictionary mapping from class id to name. - image_height: int image height. - image_width: int image width. - figsize: (w, h) float tuple. This is the size of the rendered figure in - inches. A dpi=100 is used in plot.Image; note that the axes and title - will take up space in the final rendering. If None, this will default to - (image_width / 100 * 1.5, image_height / 100 * 1.5). - ground_removal_threshold: Floating point value used to color ground points - differently. Defaults to -1.35 which happens to work well for KITTI. - sampler_num_samples: Number of batches to keep for visualizing. - """ - self._class_id_to_name = class_id_to_name or {} - self._image_width = image_width - self._image_height = image_height - - figsize = figsize or (image_width / 100. * 1.5, image_height / 100. * 1.5) - self._figsize = figsize - - self._ground_removal_threshold = ground_removal_threshold - self._sampler = py_utils.UniformSampler(num_samples=sampler_num_samples) - self._top_down_transform = top_down_transform - self._summary = None - - def Update(self, decoded_outputs): - """Add top down visualization to summaries. - - Args: - decoded_outputs: A `.NestedMap` containing the following fields: - visualization_labels, predicted_bboxes, visualization_weights, - points_xyz, points_padding, gt_bboxes_2d, gt_bboxes_2d_weights, and - labels. - """ - self._sampler.Add(decoded_outputs) - # Invalidate cache. - self._summary = None - - def _XYWHToExtrema(self, bboxes): - """Convert from x, y, dx, dy to extrema ymin, xmin, ymax, xmax.""" - mtrix = np.array([ - # x y dx dy - [0.0, 1.0, 0.0, -.5], # ymin - [1.0, 0.0, -.5, 0.0], # xmin - [0.0, 1.0, 0.0, 0.5], # ymax - [1.0, 0.0, 0.5, 0.0], # xmax - ]).T - bboxes = bboxes.copy() - bboxes[..., :4] = np.matmul(bboxes[..., :4], mtrix) - return bboxes - - def _DrawLasers(self, images, points_xyz, points_padding, transform): - """Draw laser points.""" - for batch_idx in range(images.shape[0]): - for points_idx in range(points_xyz.shape[1]): - if points_padding[batch_idx, points_idx] == 0: - x, y, z = points_xyz[batch_idx, points_idx, :3] - tx, ty, _ = transform_util.TransformPoint(transform, x, y, z) - if tx < 0 or ty < 0 or tx >= images.shape[2] or ty >= images.shape[1]: - continue - - # Drop ground points from visualization. - if z < self._ground_removal_threshold: - # Brown out the color for ground points. - color = (64, 48, 48) - else: - color = (255, 255, 255) - - images[batch_idx, int(ty), int(tx), :] = color - - def Summary(self, name): - self._EvaluateIfNecessary(name) - return self._summary - - def _EvaluateIfNecessary(self, name): - """Create a top down image summary, if not already created.""" - if self._summary is not None: - return - - tf.logging.info('Generating top down summary.') - ret = tf.Summary() - - transform = self._top_down_transform - - for batch_idx, batch_sample in enumerate(self._sampler.samples): - batch_size = batch_sample.labels.shape[0] - visualization_labels = batch_sample.visualization_labels - predicted_bboxes = batch_sample.predicted_bboxes - visualization_weights = batch_sample.visualization_weights - points_xyz = batch_sample.points_xyz - points_padding = batch_sample.points_padding - gt_bboxes_2d = batch_sample.gt_bboxes_2d - gt_bboxes_2d_weights = batch_sample.gt_bboxes_2d_weights - labels = batch_sample.labels - difficulties = batch_sample.difficulties - source_ids = batch_sample.source_ids - - # Create base images for entire batch that we will update. - images = np.zeros([batch_size, self._image_height, self._image_width, 3], - dtype=np.uint8) - - # Draw lasers first, so that bboxes can be on top. - self._DrawLasers(images, points_xyz, points_padding, transform) - - # Draw ground-truth bboxes. - gt_bboxes_2d = np.where( - np.expand_dims(gt_bboxes_2d_weights > 0, -1), gt_bboxes_2d, - np.zeros_like(gt_bboxes_2d)) - transformed_gt_bboxes_2d = summary.TransformBBoxesToTopDown( - gt_bboxes_2d, transform) - - summary.DrawBBoxesOnImages( - images, - transformed_gt_bboxes_2d, - gt_bboxes_2d_weights, - labels, - self._class_id_to_name, - groundtruth=True) - - # Draw predicted bboxes. - predicted_bboxes = np.where( - np.expand_dims(visualization_weights > 0, -1), predicted_bboxes, - np.zeros_like(predicted_bboxes)) - transformed_predicted_bboxes = summary.TransformBBoxesToTopDown( - predicted_bboxes, transform) - - summary.DrawBBoxesOnImages( - images, - transformed_predicted_bboxes, - visualization_weights, - visualization_labels, - self._class_id_to_name, - groundtruth=False) - - # Draw the difficulties on the image. - self.DrawDifficulty(images, transformed_gt_bboxes_2d, - gt_bboxes_2d_weights, difficulties) - - for idx in range(batch_size): - source_id = source_ids[idx] - - def AnnotateImage(fig, axes, source_id=source_id): - """Add source_id to image.""" - del fig - # Draw in top middle of image. - text = axes.text( - 500, - 15, - source_id, - fontsize=16, - color='blue', - fontweight='bold', - horizontalalignment='center') - text.set_path_effects([ - path_effects.Stroke(linewidth=3, foreground='lightblue'), - path_effects.Normal() - ]) - - image_summary = plot.Image( - name='{}/{}/{}'.format(name, batch_idx, idx), - aspect='equal', - figsize=self._figsize, - image=images[idx, ...], - setter=AnnotateImage) - ret.value.extend(image_summary.value) - - tf.logging.info('Done generating top down summary.') - self._summary = ret - - def DrawDifficulty(self, images, gt_bboxes, gt_box_weights, difficulties): - """Draw the difficulty values on each ground truth box.""" - batch_size = np.shape(images)[0] - try: - font = ImageFont.truetype('arial.ttf', size=20) - except IOError: - font = ImageFont.load_default() - - for batch_id in range(batch_size): - image = images[batch_id, :, :, :] - original_image = image - image = Image.fromarray(np.uint8(original_image)).convert('RGB') - draw = ImageDraw.Draw(image) - difficulty_vector = difficulties[batch_id] - box_data = gt_bboxes[batch_id] - - for box_id in range(box_data.shape[0]): - box_weight = gt_box_weights[batch_id, box_id] - if box_weight == 0: - continue - center_x = box_data[box_id, 0] - center_y = box_data[box_id, 1] - difficulty_value = str(difficulty_vector[box_id]) - - # Draw a rectangle background slightly larger than the text. - text_width, text_height = font.getsize(difficulty_value) - draw.rectangle( - [(center_x - text_width / 1.8, center_y - text_height / 1.8), - (center_x + text_width / 1.8, center_y + text_height / 1.8)], - fill='darkcyan') - - # Center the text in the rectangle - draw.text((center_x - text_width / 2, center_y - text_height / 2), - str(difficulty_value), - fill='lightcyan', - font=font) - np.copyto(original_image, np.array(image)) - - -class WorldViewer(metrics.BaseMetric): - """World Viewer for 3d point cloud scenes.""" - # Defines the maximum hue range for point cloud colorization by distance. - _MAX_HUE = 0.65 - - # Distance from car after which we consider all points equally far. - _MAX_DISTANCE_METERS = 40. - - def __init__(self, sampler_num_samples=8): - """Init.""" - self._sampler = py_utils.UniformSampler(num_samples=sampler_num_samples) - self._summary = None - - def Update(self, decoded_outputs): - """Add point cloud mesh data to be summarized. - - Args: - decoded_outputs: A `.NestedMap` containing the following fields: - visualization_labels, predicted_bboxes, visualization_weights, - points_xyz, points_padding, gt_bboxes_2d, gt_bboxes_2d_weights, and - labels. - """ - self._sampler.Add(decoded_outputs) - # Invalidate cache. - self._summary = None - - def Summary(self, name): - self._EvaluateIfNecessary(name) - return self._summary - - def _EvaluateIfNecessary(self, name): - """Create a mesh summary, if not already created.""" - if self._summary is not None: - return - - summ = None - tf.logging.info('Generating mesh summary.') - for i, batch_sample in enumerate(self._sampler.samples): - points_xyz = batch_sample.points_xyz[i:i + 1] - points_padding = batch_sample.points_padding[i:i + 1] - points_mask = (1. - points_padding).astype(bool) - # Apply mask and expand to include a batch dimension. - points_xyz = points_xyz[points_mask][np.newaxis, ...] - - # Compute colors based off distance from car. - distance = np.sqrt(points_xyz[0, :, 0]**2 + points_xyz[0, :, 1]**2 + - points_xyz[0, :, 2]**2) - # Normalize by some max distance beyond which we don't distinguish - # distance. - max_distance = np.ones_like(distance) * WorldViewer._MAX_DISTANCE_METERS - distance = np.minimum(max_distance, distance) - scale = (max_distance - distance) / max_distance - - # Convert to RGB. - hue = np.minimum(WorldViewer._MAX_HUE, scale)[..., np.newaxis] - # Invert hue so red is closer. - hue = WorldViewer._MAX_HUE - hue - s, v = np.ones_like(hue), np.ones_like(hue) - hsv = np.hstack([hue, s, v]) - rgb = matplotlib_colors.hsv_to_rgb(hsv) - colors = np.minimum(255., rgb * 255.).astype(np.uint8) - colors = colors[np.newaxis, ...] - summ = mesh_summary.pb( - '{}/point_cloud/{}'.format(name, i), - vertices=points_xyz, - colors=colors, - faces=None) - # At the moment, only one scene summary is supported; writing - # more makes the TensorBoard mesh visualizer hang. - break - - if summ: - self._summary = summ - - -class CameraVisualization(metrics.BaseMetric): - """Camera detection visualization. - - Visualizes a camera image and predicted bounding boxes on top - of the image. - - Updates to this metric is expected to be `.NestedMap` containing: - - camera_images: [N, W, H, 3] float tensor containing camera image data. - - bbox_corners: [N, B1, 8, 2] float tensor containing bounding box corners. - For each batch (N), for each box B, there are 8 corners, each with - an X and Y value. - - bbox_scores: [N, B1] float tensor containing predicted box scores. - - """ - - def __init__(self, - figsize=(15, 15), - bbox_score_threshold=0.01, - sampler_num_samples=8, - draw_3d_boxes=True): - """Initialize CameraVisualization. - - Args: - figsize: (w, h) float tuple. This is the size of the rendered figure in - inches. A dpi=100 is used in plot.Image; note that the axes and title - will take up space in the final rendering. If None, this will default to - (image_width / 100 * 1.5, image_height / 100 * 1.5). - bbox_score_threshold: The threshold over which bboxes will be drawn on the - image. - sampler_num_samples: Number of batches to keep for visualizing. - draw_3d_boxes: Whether to draw 2d or 3d bounding boxes. 3d bounding - boxes depict the 8 corners of the bounding box, whereas the 2d - bounding boxes depict the extrema x and y dimensions of the boxes - on the image plane. - """ - self._figsize = figsize - self._bbox_score_threshold = bbox_score_threshold, - self._sampler = py_utils.UniformSampler(num_samples=sampler_num_samples) - self._draw_3d_boxes = draw_3d_boxes - self._summary = None - - def Update(self, decoded_outputs): - self._sampler.Add(decoded_outputs) - # Invalidate cache. - self._summary = None - - def Summary(self, name): - self._EvaluateIfNecessary(name) - return self._summary - - def _EvaluateIfNecessary(self, name): - """Create a camera image summary if not already created.""" - if self._summary is not None: - return - - ret = tf.Summary() - - for sample_idx, sample in enumerate(self._sampler.samples): - batch_size = sample.camera_images.shape[0] - - for batch_idx in range(batch_size): - image = sample.camera_images[batch_idx] - - # [num bboxes, 8, 2]. - bbox_corners = sample.bbox_corners[batch_idx] - - # [num_bboxes] - bbox_scores = sample.bbox_scores[batch_idx] - - def Draw3DBoxes(fig, - axes, - bbox_corners=bbox_corners, - bbox_scores=bbox_scores): - """Draw 3d bounding boxes.""" - del fig - for bbox_id in range(bbox_corners.shape[0]): - # Skip visualizing low-scoring boxes. - bbox_score = bbox_scores[bbox_id] - if bbox_score < self._bbox_score_threshold: - continue - bbox_data = bbox_corners[bbox_id] - - # Draw the score of each box. - # - # Turn score into an integer for better display. - center_x = np.mean(bbox_data[:, 0]) - center_y = np.mean(bbox_data[:, 1]) - bbox_score = int(bbox_score * 100) - text = axes.text( - center_x, - center_y, - bbox_score, - fontsize=12, - color='red', - fontweight='bold') - text.set_bbox(dict(facecolor='yellow', alpha=0.4)) - - # The BBoxToCorners function produces the points - # in a deterministic order, which we use to draw - # the faces of the polygon. - # - # The first 4 points are the "top" of the bounding box. - # The second 4 points are the "bottom" of the bounding box. - # - # We then draw the last 4 connecting points by choosing - # two of the connecting faces in the right order. - face_points = [] - face_points += [[ - bbox_data[0, :], bbox_data[1, :], bbox_data[2, :], - bbox_data[3, :] - ]] - face_points += [[ - bbox_data[4, :], bbox_data[5, :], bbox_data[6, :], - bbox_data[7, :] - ]] - face_points += [[ - bbox_data[1, :], bbox_data[2, :], bbox_data[6, :], - bbox_data[5, :] - ]] - face_points += [[ - bbox_data[0, :], bbox_data[3, :], bbox_data[7, :], - bbox_data[4, :] - ]] - for face in face_points: - # Each face is a list of 4 x,y points - face_xy = np.array(face) - axes.add_patch( - matplotlib_patches.Polygon( - face_xy, closed=True, edgecolor='red', facecolor='none')) - - def Draw2DBoxes(fig, - axes, - bbox_corners=bbox_corners, - bbox_scores=bbox_scores): - """Draw 2d boxes on the figure.""" - del fig - # Extract the 2D extrema of each bbox and the max score - for bbox_id in range(bbox_corners.shape[0]): - # Skip visualizing low-scoring boxes. - bbox_score = bbox_scores[bbox_id] - if bbox_score < self._bbox_score_threshold: - continue - bbox_data = bbox_corners[bbox_id] - - ymin = np.min(bbox_data[:, 1]) - xmin = np.min(bbox_data[:, 0]) - ymax = np.max(bbox_data[:, 1]) - xmax = np.max(bbox_data[:, 0]) - height = ymax - ymin - width = xmax - xmin - # Turn score into an integer for better display. - bbox_score = int(bbox_score * 100) - text = axes.text( - xmin, - ymin, - bbox_score, - fontsize=12, - color='red', - fontweight='bold') - text.set_bbox(dict(facecolor='yellow', alpha=0.4)) - axes.add_patch( - matplotlib_patches.Rectangle((xmin, ymin), - width, - height, - edgecolor='red', - facecolor='none')) - - # For each image, draw the boxes on that image. - draw_fn = Draw3DBoxes if self._draw_3d_boxes else Draw2DBoxes - image_summary = plot.Image( - name='{}/{}/{}'.format(name, sample_idx, batch_idx), - aspect='equal', - figsize=self._figsize, - image=image, - setter=draw_fn) - ret.value.extend(image_summary.value) - self._summary = ret diff --git a/lingvo/tasks/car/detection_3d_metrics_test.py b/lingvo/tasks/car/detection_3d_metrics_test.py deleted file mode 100644 index 8a43445f..00000000 --- a/lingvo/tasks/car/detection_3d_metrics_test.py +++ /dev/null @@ -1,109 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for detection_3d_metrics.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car import detection_3d_metrics -from lingvo.tasks.car import transform_util -import numpy as np - - -class Detection3dMetricsTest(test_utils.TestCase): - - def testTopDownVisualizationMetric(self): - top_down_transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=32., - image_ref_x=512., - image_ref_y=1408., - flip_axes=True) - metric = detection_3d_metrics.TopDownVisualizationMetric(top_down_transform) - - batch_size = 4 - num_preds = 10 - num_gt = 12 - num_points = 128 - - visualization_labels = np.random.randint(0, 2, (batch_size, num_preds)) - predicted_bboxes = np.random.rand(batch_size, num_preds, 5) - visualization_weights = np.abs(np.random.rand(batch_size, num_preds)) - - labels = np.random.randint(0, 2, (batch_size, num_gt)) - gt_bboxes_2d = np.random.rand(batch_size, num_gt, 5) - gt_bboxes_2d_weights = np.abs(np.random.rand(batch_size, num_gt)) - difficulties = np.random.randint(0, 3, (batch_size, num_gt)) - - points_xyz = np.random.rand(batch_size, num_points, 3) - points_padding = np.random.randint(0, 2, (batch_size, num_points)) - source_ids = np.full([batch_size], '012346') - - metric.Update( - py_utils.NestedMap({ - 'visualization_labels': visualization_labels, - 'predicted_bboxes': predicted_bboxes, - 'visualization_weights': visualization_weights, - 'labels': labels, - 'gt_bboxes_2d': gt_bboxes_2d, - 'gt_bboxes_2d_weights': gt_bboxes_2d_weights, - 'points_xyz': points_xyz, - 'points_padding': points_padding, - 'difficulties': difficulties, - 'source_ids': source_ids, - })) - - _ = metric.Summary('test') - - def testCameraVisualization(self): - metric = detection_3d_metrics.CameraVisualization() - - batch_size = 4 - num_preds = 10 - - images = np.random.rand(batch_size, 512, 1024, 3) - bbox_corners = np.random.rand(batch_size, num_preds, 8, 2) - bbox_scores = np.random.rand(batch_size, num_preds) - - metric.Update( - py_utils.NestedMap({ - 'camera_images': images, - 'bbox_corners': bbox_corners, - 'bbox_scores': bbox_scores - })) - - # Test that the metric runs. - _ = metric.Summary('test') - - def testMesh(self): - metric = detection_3d_metrics.WorldViewer() - - batch_size = 4 - num_points = 128 - points_xyz = np.random.rand(batch_size, num_points, 3) * 40. - points_padding = np.random.randint(0, 2, (batch_size, num_points)) - - metric.Update( - py_utils.NestedMap({ - 'points_xyz': points_xyz, - 'points_padding': points_padding, - })) - - # Test that the metric runs. - _ = metric.Summary('test') - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/detection_decoder.py b/lingvo/tasks/car/detection_decoder.py deleted file mode 100644 index 41b26d23..00000000 --- a/lingvo/tasks/car/detection_decoder.py +++ /dev/null @@ -1,220 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Functions to help with decoding detector model outputs.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.tasks.car import detection_3d_lib - - -def DecodeWithNMS(predicted_bboxes, - classification_scores, - nms_iou_threshold, - score_threshold, - max_boxes_per_class=None, - use_oriented_per_class_nms=False): - """Perform NMS on predicted bounding boxes / associated logits. - - Args: - predicted_bboxes: [batch_size, num_boxes, 7] float Tensor containing - predicted bounding box coordinates. - classification_scores: [batch_size, num_boxes, num_classes] float Tensor - containing predicted classification scores for each box. - nms_iou_threshold: IoU threshold to use when determining whether two boxes - overlap for purposes of suppression. Either a float or a list of len - num_classes. - score_threshold: The score threshold passed to NMS that allows NMS to - quickly ignore irrelevant boxes. Either a float or a list of len - num_classes. - max_boxes_per_class: The maximum number of boxes per example to emit. - If None, this value is set to num_boxes from the shape of - predicted_bboxes. - use_oriented_per_class_nms: Whether to use the oriented per class NMS - or treat everything as one class and having no orientation. - - Returns: - bbox_indices: Indices of the boxes selected after NMS. Tensor of shape - [batch_size, num_classes, max_boxes_per_class] if per class NMS is used. - If single class NMS, this will be of shape [batch_size, - max_boxes_per_class]. - predicted_bboxes: Filtered bboxes after NMS of shape - [batch_size, num_classes, max_boxes_per_class, 7]. - bbox_scores: A float32 Tensor with the score for each box of shape - [batch_size, num_classes, max_boxes_per_class]. - valid_mask: A float32 Tensor with 1/0 values indicating the validity of - each box. 1 indicates valid, and 0 invalid. Tensor of shape - [batch_size, num_classes, max_boxes_per_class]. - """ - if use_oriented_per_class_nms: - nms_fn = _MultiClassOrientedDecodeWithNMS - else: - nms_fn = _SingleClassDecodeWithNMS - - return nms_fn( - predicted_bboxes=predicted_bboxes, - classification_scores=classification_scores, - nms_iou_threshold=nms_iou_threshold, - score_threshold=score_threshold, - max_boxes_per_class=max_boxes_per_class) - - -def _MultiClassOrientedDecodeWithNMS(predicted_bboxes, - classification_scores, - nms_iou_threshold, - score_threshold, - max_boxes_per_class=None): - """Perform Oriented Per Class NMS on predicted bounding boxes / logits. - - Args: - predicted_bboxes: [batch_size, num_boxes, 7] float Tensor containing - predicted bounding box coordinates. - classification_scores: [batch_size, num_boxes, num_classes] float Tensor - containing predicted classification scores for each box. - nms_iou_threshold: IoU threshold to use when determining whether two boxes - overlap for purposes of suppression. Either a float or a list of len - num_classes. - score_threshold: The score threshold passed to NMS that allows NMS to - quickly ignore irrelevant boxes. Either a float or a list of len - num_classes. It is strongly recommended that the score for non-active - classes (like background) be set to 1 so they are discarded. - max_boxes_per_class: The maximum number of boxes per example to emit. If - None, this value is set to num_boxes from the shape of predicted_bboxes. - - Returns: - bbox_indices: Indices of the boxes selected after NMS. Tensor of shape - [batch_size, num_classes, max_boxes_per_class]. - predicted_bboxes: Filtered bboxes after NMS of shape - [batch_size, num_classes, max_boxes_per_class, 7]. - bbox_scores: A float32 Tensor with the score for each box of shape - [batch_size, num_classes, max_boxes_per_class]. - valid_mask: A float32 Tensor with 1/0 values indicating the validity of - each box. 1 indicates valid, and 0 invalid. Tensor of shape - [batch_size, num_classes, max_boxes_per_class]. - """ - utils_3d = detection_3d_lib.Utils3D() - predicted_bboxes = py_utils.HasShape(predicted_bboxes, [-1, -1, 7]) - batch_size, num_predicted_boxes, _ = py_utils.GetShape(predicted_bboxes) - classification_scores = py_utils.HasShape( - classification_scores, [batch_size, num_predicted_boxes, -1]) - _, _, num_classes = py_utils.GetShape(classification_scores) - - if max_boxes_per_class is None: - max_boxes_per_class = num_predicted_boxes - - # Compute NMS for every sample in the batch. - bbox_indices, bbox_scores, valid_mask = utils_3d.BatchedOrientedNMSIndices( - predicted_bboxes, - classification_scores, - nms_iou_threshold=nms_iou_threshold, - score_threshold=score_threshold, - max_boxes_per_class=max_boxes_per_class) - - # TODO(bencaine): Consider optimizing away the tf.tile or make upstream - # changes to make predicted boxes include a class dimension. - # Get the original box for each index selected by NMS. - predicted_bboxes = tf.tile(predicted_bboxes[:, tf.newaxis, :, :], - [1, num_classes, 1, 1]) - predicted_bboxes = tf.array_ops.batch_gather(predicted_bboxes, bbox_indices) - return bbox_indices, predicted_bboxes, bbox_scores, valid_mask - - -def _SingleClassDecodeWithNMS(predicted_bboxes, - classification_scores, - nms_iou_threshold, - score_threshold, - max_boxes_per_class=None): - """Perform NMS on predicted bounding boxes / associated logits. - - Args: - predicted_bboxes: [batch_size, num_boxes, 7] float Tensor containing - predicted bounding box coordinates. - classification_scores: [batch_size, num_boxes, num_classes] float Tensor - containing predicted classification scores for each box. - nms_iou_threshold: IoU threshold to use when determining whether two boxes - overlap for purposes of suppression. - score_threshold: The score threshold passed to NMS that allows NMS to - quickly ignore irrelevant boxes. - max_boxes_per_class: The maximum number of boxes per example to emit. If - None, this value is set to num_boxes from the shape of predicted_bboxes. - - Returns: - nms_indices: Indices of the boxes selected after NMS. Tensor of shape - [batch_size, num_classes, max_boxes_per_class]. - predicted_bboxes: Filtered bboxes after NMS of shape - [batch_size, num_classes, max_boxes_per_class, 7]. - bbox_scores: A float32 Tensor with the score for each box of shape - [batch_size, num_classes, max_boxes_per_class]. - valid_mask: A float32 Tensor with 1/0 values indicating the validity of - each box. 1 indicates valid, and 0 invalid. Tensor of shape - [batch_size, num_classes, max_boxes_per_class]. - """ - utils_3d = detection_3d_lib.Utils3D() - predicted_bboxes = py_utils.HasShape(predicted_bboxes, [-1, -1, 7]) - batch_size, num_predicted_boxes, _ = py_utils.GetShape(predicted_bboxes) - classification_scores = py_utils.HasShape( - classification_scores, [batch_size, num_predicted_boxes, -1]) - _, _, num_classes = py_utils.GetShape(classification_scores) - - if not isinstance(nms_iou_threshold, float): - raise ValueError('Single class NMS only supports a scalar ' - '`nms_iou_threshold`.') - if not isinstance(score_threshold, float): - raise ValueError('Single class NMS only supports a scalar ' - '`score_threshold`.') - - if max_boxes_per_class is None: - max_boxes_per_class = num_predicted_boxes - - # TODO(jngiam): Change to be per-class bboxes, and hence, per-class NMS, and - # per-class thresholding. - # [batch, num_predicted_boxes] - nms_scores = tf.reduce_max(classification_scores, axis=-1) - - # Compute the most likely label by computing the highest class score from - # the output of the sigmoid. - likely_labels = tf.argmax(classification_scores, axis=-1) - - # When background is the most likely class for the box, mask out the scores - # of that box from NMS scoring so the background boxes don't dominate the - # NMS. - nms_scores *= tf.cast(likely_labels > 0, tf.float32) - - # Compute NMS for every sample in the batch. - nms_indices, valid_mask = utils_3d.BatchedNMSIndices( - predicted_bboxes, - nms_scores, - nms_iou_threshold=nms_iou_threshold, - score_threshold=score_threshold, - max_num_boxes=max_boxes_per_class) - - # Reorder the box data and logits according to NMS scoring. - predicted_bboxes = tf.array_ops.batch_gather(predicted_bboxes, nms_indices) - classification_scores = tf.array_ops.batch_gather(classification_scores, - nms_indices) - - # Now reformat the output of NMS to match the format of the - # MultiClassOrientedDecodeWithNMS, which outputs a per class NMS result. - # This takes the leading shape of - # [batch_size, num_classes, max_boxes_per_class] for all outputs, which - # means since this NMS is not class specific we need to tile the outputs - # num_classes times or reorder the data such that its [batch, num_classes]. - predicted_bboxes = tf.tile(predicted_bboxes[:, tf.newaxis, :, :], - [1, num_classes, 1, 1]) - classification_scores = tf.transpose(classification_scores, (0, 2, 1)) - classification_scores = py_utils.HasShape( - classification_scores, [batch_size, num_classes, max_boxes_per_class]) - valid_mask = tf.tile(valid_mask[:, tf.newaxis, :], [1, num_classes, 1]) - return nms_indices, predicted_bboxes, classification_scores, valid_mask diff --git a/lingvo/tasks/car/detection_decoder_test.py b/lingvo/tasks/car/detection_decoder_test.py deleted file mode 100644 index 9599045c..00000000 --- a/lingvo/tasks/car/detection_decoder_test.py +++ /dev/null @@ -1,120 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for detection_decoder.""" - -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import detection_decoder - - -class DetectionDecoderTest(test_utils.TestCase): - """Tests for detection_decoder library.""" - - def testDecoderWithOrientedPerClassNMS(self): - batch_size = 4 - num_preds = 8 - num_classes = 10 - - # An example of setting the score threshold high and IOU threshold low - # for classes we don't care about - score_threshold = [1.0] * num_classes - score_threshold[1] = 0.05 - - nms_iou_threshold = [0.0] * num_classes - nms_iou_threshold[1] = 0.5 - - with tf.Graph().as_default(): - tf.random.set_seed(12345) - predicted_bboxes = tf.random.normal([batch_size, num_preds, 7]) - classification_scores = tf.random.uniform( - [batch_size, num_preds, num_classes], minval=0, maxval=1) - - idxs, bboxes, bbox_scores, valid_mask = detection_decoder.DecodeWithNMS( - predicted_bboxes, - classification_scores, - nms_iou_threshold=nms_iou_threshold, - score_threshold=score_threshold, - use_oriented_per_class_nms=True) - - with self.session(): - outputs = self.evaluate([ - predicted_bboxes, classification_scores, idxs, bboxes, bbox_scores, - valid_mask - ]) - (input_bboxes, input_scores, output_idxs, output_bboxes, output_scores, - mask) = outputs - - self.assertEqual((batch_size, num_preds, 7), input_bboxes.shape) - self.assertEqual((batch_size, num_classes, num_preds), - output_idxs.shape) - self.assertEqual((batch_size, num_classes, num_preds, 7), - output_bboxes.shape) - self.assertEqual((batch_size, num_preds, num_classes), - input_scores.shape) - self.assertEqual((batch_size, num_classes, num_preds), - output_scores.shape) - self.assertEqual((batch_size, num_classes, num_preds), mask.shape) - - # Assert that NMS did some kind of filtering for each class - for cls_idx in range(num_classes): - self.assertEqual( - mask[:, cls_idx, :].sum(), - (input_scores[:, :, cls_idx] > score_threshold[cls_idx]).sum()) - self.assertEqual( - mask[:, cls_idx, :].sum(), - (output_scores[:, cls_idx, :] > score_threshold[cls_idx]).sum()) - - def testDecoderSingleClassNMS(self): - batch_size = 4 - num_preds = 8 - num_classes = 10 - - score_threshold = 0.05 - nms_iou_threshold = 0.5 - with tf.Graph().as_default(): - tf.random.set_seed(12345) - predicted_bboxes = tf.random.normal([batch_size, num_preds, 7]) - classification_scores = tf.random.uniform( - [batch_size, num_preds, num_classes], minval=0, maxval=1) - - idxs, bboxes, bbox_scores, valid_mask = detection_decoder.DecodeWithNMS( - predicted_bboxes, - classification_scores, - nms_iou_threshold=nms_iou_threshold, - score_threshold=score_threshold, - use_oriented_per_class_nms=False) - - with self.session(): - outputs = self.evaluate([ - predicted_bboxes, classification_scores, idxs, bboxes, bbox_scores, - valid_mask - ]) - (input_bboxes, input_scores, output_idxs, output_bboxes, output_scores, - mask) = outputs - - self.assertEqual((batch_size, num_preds, 7), input_bboxes.shape) - self.assertEqual((batch_size, num_preds), output_idxs.shape) - self.assertEqual((batch_size, num_classes, num_preds, 7), - output_bboxes.shape) - self.assertEqual((batch_size, num_preds, num_classes), - input_scores.shape) - self.assertEqual((batch_size, num_classes, num_preds), - output_scores.shape) - self.assertEqual((batch_size, num_classes, num_preds), mask.shape) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/evaluation_metadata.py b/lingvo/tasks/car/evaluation_metadata.py deleted file mode 100644 index e1b09b1b..00000000 --- a/lingvo/tasks/car/evaluation_metadata.py +++ /dev/null @@ -1,102 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Base class specifying metadata used in evaluation.""" - -import numpy as np - - -class EvaluationMetadata: - """Interface for defining metadata of dataset.""" - - def __init__(self, name): - self.name = name - - def ClassNames(self): - """Returns a list of human-interpretable strings.""" - raise NotImplementedError() - - def LabelMap(self): - """Return a label map of int -> str for each class.""" - return dict(zip(range(self.NumClasses()), self.ClassNames())) - - def NumClasses(self): - """Total number of classes for the dataset.""" - return len(self.ClassNames()) - - def DifficultyLevels(self): - """Dictionary of difficulty level strings to int32 indices.""" - raise NotImplementedError() - - def EvalClassIndices(self): - """List of int32 indices for the classes that should be evaled.""" - eval_classes = sorted(self.IoUThresholds().keys()) - return [self.ClassNames().index(name) for name in eval_classes] - - def IoUThresholds(self): - """Dictionary of IoU thresholds for every evaluated class. - - The keys of the dictionary are used to compute EvalClassIndices(). - """ - raise NotImplementedError() - - def IgnoreClassIndices(self): - """List of int32 indices for the classes that should be ignored. - - A detection that matches with a groundtruth bbox of any neighbor class will - not be considered as false positive in eval. - """ - raise NotImplementedError() - - def NumberOfPrecisionRecallPoints(self): - """Number of points on the precision-recall curve.""" - raise NotImplementedError() - - def MaximumDistance(self): - """Maximum empirically observed Euclidean distance in world coordinates.""" - raise NotImplementedError() - - def DistanceBinWidth(self): - """The width of each bin for Euclidean distance in world coordinates.""" - raise NotImplementedError() - - def MaximumNumberOfPoints(self): - """Maximum empirically observed number of points in bounding box.""" - raise NotImplementedError() - - def NumberOfPointsBins(self): - """Number of logarithmically space bins for examining points.""" - raise NotImplementedError() - - def MaximumRotation(self): - """Maximum rotation angle in world coordinates.""" - return np.pi - - def NumberOfRotationBins(self): - """Number of linear spaced rotations to bin.""" - raise NotImplementedError() - - def MinHeight2D(self): - """Minimum height of detections to be evaluated. - - Returns: - A dictionary of difficulty level strings to ints that are height - thresholds for each level. - """ - return NotImplementedError() - - def RecallAtPrecision(self): - """Report the recall at a given precision level..""" - return [0.50, 0.95] diff --git a/lingvo/tasks/car/geometry.py b/lingvo/tasks/car/geometry.py deleted file mode 100644 index 7f88683a..00000000 --- a/lingvo/tasks/car/geometry.py +++ /dev/null @@ -1,645 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Routines related to geometric operations on bboxes. - -Bboxes and coordinates are always in the last dimension of a tensor. - -2D BBoxes are represented by (ymin, xmin, ymax, xmax). - -2D BBoxes can also be represented by (centroid x, centroid y, width, height). - -2D coordinates are represented by (x, y). - -3D coordinates are represented by (x, y, z). -""" - -import lingvo.compat as tf -from lingvo.core import py_utils -import numpy as np - - -def _BroadcastMatmul(x, y): - """Broadcast y and matmul with x. - - Args: - x: A tensor of shape [..., b]. - y: A matrix of shape [b, c]. - - Returns: - Tensor: ``z[..., c]``, where ``z[i..., :] = matmul(x[i..., :], y)`` - """ - y = py_utils.HasRank(y, 2) - x_reshaped = tf.reshape(x, [-1, tf.shape(x)[-1]]) - result = tf.matmul(x_reshaped, y) - return tf.reshape(result, - tf.concat( - [tf.shape(x)[:-1], tf.shape(y)[-1:]], axis=0)) - - -def _MakeRotationMatrix(yaw, roll, pitch): - """Create a 3x3 rotation matrix from yaw, roll, pitch (angles in radians). - - Note: Yaw -> Z, Roll -> X, Pitch -> Y. - - Args: - yaw: float tensor representing a yaw angle in radians. - roll: float tensor representing a roll angle in radians. - pitch: float tensor representing a pitch angle in radians. - - Returns: - A [3, 3] tensor corresponding to a rotation matrix. - """ - - # pyformat: disable - def _UnitX(angle): - return tf.reshape([1., 0., 0., - 0., tf.cos(angle), -tf.sin(angle), - 0., tf.sin(angle), tf.cos(angle)], - shape=[3, 3]) - - def _UnitY(angle): - return tf.reshape([tf.cos(angle), 0., tf.sin(angle), - 0., 1., 0., - -tf.sin(angle), 0., tf.cos(angle)], - shape=[3, 3]) - - def _UnitZ(angle): - return tf.reshape([tf.cos(angle), -tf.sin(angle), 0., - tf.sin(angle), tf.cos(angle), 0., - 0., 0., 1.], - shape=[3, 3]) - # pyformat: enable - return tf.matmul(tf.matmul(_UnitZ(yaw), _UnitX(roll)), _UnitY(pitch)) - - -def CoordinateTransform(points, pose): - """Translate 'points' to coordinates according to 'pose' vector. - - pose should contain 6 floating point values: - translate_x, translate_y, translate_z: The translation to apply. - yaw, roll, pitch: The rotation angles in radians. - - Args: - points: Float shape [..., 3]: Points to transform to new coordinates. - pose: Float shape [6]: [translate_x, translate_y, translate_z, yaw, roll, - pitch]. The pose in the frame that 'points' comes from, and the defintion - of the rotation and translation angles to apply to points. - - Returns: - 'points' transformed to the coordinates defined by 'pose'. - """ - translate_x = pose[0] - translate_y = pose[1] - translate_z = pose[2] - - # Translate the points so the origin is the pose's center. - translation = tf.reshape([translate_x, translate_y, translate_z], shape=[3]) - translated_points = points + translation - - # Compose the rotations along the three axes. - # - # Note: Yaw->Z, Roll->X, Pitch->Y. - yaw, roll, pitch = pose[3], pose[4], pose[5] - rotation_matrix = _MakeRotationMatrix(yaw, roll, pitch) - - # Finally, rotate the points about the pose's origin according to the - # rotation matrix. - rotated_points = _BroadcastMatmul(translated_points, rotation_matrix) - return rotated_points - - -def TransformPoints(points, transforms): - """Apply 4x4 transforms to a set of points. - - Args: - points: A [..., num_points, 3] tensor of xyz point locations. - transforms: A [..., 4, 4] tensor with the same leading shape as points. - - Returns: - A tensor with the same shape as points, transformed respectively. - """ - # Create homogeneous coordinates for points. - points = tf.concat([points, tf.ones_like(points[..., :1])], axis=-1) - - # Apply transformations, and divide by last axis to project back to 3D-space. - # Transpose the transforms since the transformation is usually expected to - # be applied such that new_points = T * current_point. - points = tf.matmul(points, transforms, transpose_b=True) - points = points[..., :3] / points[..., 3:] - - return points - - -def WrapAngleRad(angles_rad, min_val=-np.pi, max_val=np.pi): - """Wrap the value of `angles_rad` to the range [min_val, max_val].""" - max_min_diff = max_val - min_val - return min_val + tf.math.floormod(angles_rad + max_val, max_min_diff) - - -def TransformBBoxes3D(bboxes_3d, transforms): - """Apply 4x4 transforms to 7 DOF bboxes (change center and rotation). - - Args: - bboxes_3d: A [..., num_boxes, 7] tensor representing 3D bboxes. - transforms: A [..., 4, 4] tensor with the same leading shape as bboxes_3d. - These transforms are expected to only affect translation and rotation, - while not scaling the data. This ensures that the bboxes have the same - dimensions post transformation. - - Returns: - A tensor with the same shape as bboxes_3d, with transforms applied to each - bbox3d. - """ - center_xyz = bboxes_3d[..., :3] - dimensions = bboxes_3d[..., 3:6] - rot = bboxes_3d[..., 6:] - - # Transform center and rotation, assuming that dimensions are not changed. - center_xyz = TransformPoints(center_xyz, transforms) - rot += tf.atan2(transforms[..., 1:2, 0:1], transforms[..., 0:1, 0:1]) - rot = WrapAngleRad(rot) - - return tf.concat([ - center_xyz, - dimensions, - rot, - ], axis=-1) # pyformat: disable - - -def XYWHToBBoxes(xywh): - """Converts xywh to bboxes.""" - mtrx = tf.constant( - np.array([ - # x y h w - [0.0, 1.0, 0.0, -.5], # ymin - [1.0, 0.0, -.5, 0.0], # xmin - [0.0, 1.0, 0.0, 0.5], # ymax - [1.0, 0.0, 0.5, 0.0], # xmax - ]).T, - dtype=xywh.dtype) - return _BroadcastMatmul(xywh, mtrx) - - -def PointsToImagePlane(points, velo_to_image_plane): - """Converts 3D points to the image plane. - - Args: - points: A [N, 3] Floating point tensor containing xyz points. Points are - assumed to be in velo coordinates. - velo_to_image_plane: A [3, 4] matrix from velo xyz to image plane xy. After - multiplication, you need to divide by last coordinate to recover 2D pixel - locations. - - Returns: - A [N, 2] Floating point tensor containing points in the image plane. - """ - points = py_utils.HasRank(points, 2) - num_points = tf.shape(points)[0] - points = py_utils.HasShape(points, [num_points, 3]) - velo_to_image_plane = py_utils.HasShape(velo_to_image_plane, [3, 4]) - - # Add homogenous coordinates to points. - points = tf.concat([points, tf.ones((num_points, 1))], axis=-1) - - # Perform projection and divide by last coordinate to recover 2D pixel - # locations. - points_image = tf.matmul(points, velo_to_image_plane, transpose_b=True) - points_image = points_image[:, :2] / points_image[:, 2:3] - - points_image = py_utils.HasShape(points_image, [num_points, 2]) - return points_image - - -def BBoxesToXYWH(bboxes): - """Converts bboxes to xywh.""" - mtrx = tf.constant( - np.array([ - # ymin xmin ymax xmax - [0.0, 0.5, 0.0, 0.5], # x centroid - [0.5, 0.0, 0.5, 0.0], # y centroid - [0.0, -1., 0.0, 1.0], # width - [-1., 0.0, 1.0, 0.0], # height - ]).T, - dtype=bboxes.dtype) - return _BroadcastMatmul(bboxes, mtrx) - - -def BBoxesCentroid(bboxes): - """Returns the centroids of bboxes.""" - mtrx = tf.constant( - np.array([ - # ymin xmin ymax xmax - [0.0, 0.5, 0.0, 0.5], # x centroid - [0.5, 0.0, 0.5, 0.0], # y centroid - ]).T, - dtype=bboxes.dtype) - return _BroadcastMatmul(bboxes, mtrx) - - -def ReorderIndicesByPhi(anchor, bboxes): - """Sort bboxes based their angles relative to the anchor point. - - Args: - anchor: A vector of (x0, y0). - bboxes: A matrix of shape [N, 4]. - - Returns: - A permutation of tf.range(n) which can be used to reshuffle bboxes to the - sorted order. (e.g., tf.gather(bboxes, indices)). - """ - - def _True(inputs): - """True branch when num of inputs.bboxes is non-zero.""" - n = tf.shape(inputs.bboxes)[0] - centroid = BBoxesCentroid(inputs.bboxes) - - # Computed dot products between centroid and the anchor point. - dot = tf.squeeze( - tf.matmul(centroid, tf.expand_dims(inputs.anchor, 1)), axis=1) - - # Normalize dot to get the cosine of the angles. - norm = tf.norm(inputs.anchor) * tf.norm(centroid, axis=1) - cosine = tf.where( - tf.greater(norm, 0), dot / norm, tf.zeros([n], norm.dtype)) - - # Disambiguates the angle anchor--O--point is positive or negative by the - # sign of cross products between angle and points. tf.linalg.cross takes - # 3-vector (x, y, z), so we set z to 0. tf.linalg.cross does not support - # broadcasting, so we tile anchor to shape [n, 3]. - cross = tf.linalg.cross( - tf.tile( - tf.pad(tf.expand_dims(inputs.anchor, 0), [[0, 0], [0, 1]]), [n, 1]), - tf.pad(centroid, [[0, 0], [0, 1]])) - - # If the sign is positive, the points lie on the clockwise side of - # O-->anchor. Hence, -1 - cosine moves the cosine values to [-2, 0]. If the - # sign is negative, the points lie on the counter-clockwise side of - # O-->anchor. 1 + cosine moves the cosine values to [0, 2]. - # - # The car dataset shows that the points are scanned in the counter-clockwise - # fashion. Therefore, top-k orders the points in the same order in which - # bboxes appears in the spin. - score = tf.where(tf.greater(cross, 0)[:, 2], -1 - cosine, 1 + cosine) - - _, indices = tf.nn.top_k(score, n, sorted=True) - return indices - - def _False(_): - return tf.zeros([0], dtype=tf.int32) - - n = tf.shape(bboxes)[0] - return py_utils.If( - tf.greater(n, 0), py_utils.NestedMap(anchor=anchor, bboxes=bboxes), _True, - _False) - - -def _SmoothL1Norm(a): - """Smoothed L1 norm.""" - # F&F paper formula (3). - # http://openaccess.thecvf.com/content_cvpr_2018/papers/Luo_Fast_and_Furious_CVPR_2018_paper.pdf - return tf.where(tf.abs(a) < 1, 0.5 * tf.square(a), tf.abs(a) - 0.5) - - -def DistanceBetweenCentroidsAndBBoxesFastAndFurious(centroids, bboxes, masks): - """Computes the distance between centroids and bboxes. - - The distance/loss is loosely following the 'Fast and Furious' paper by Luo et - al., CVPR'18. This is just one way of calculating the distances. We will - probably develop other ways. - - Args: - centroids: [..., 4]. x/y/w/h for bboxes. - bboxes: [..., 4]. ymin/xmin/ymax/xmax for bboxes. - masks: [...]. masks[i] == 1 means i-th entry (centroids[i] and bboxes[i]) - should be considered in the distance/loss calculation. - - Returns: - A [...] tensor. i-th value is the distance measure of centroids[i] and - bboxes[i]. - """ - x, y, w, h = tf.unstack(centroids, axis=-1, num=4) - # "gt" suffix means 'ground truth'. - x_gt, y_gt, w_gt, h_gt = tf.unstack(BBoxesToXYWH(bboxes), axis=-1, num=4) - - def Pos(x): - return tf.maximum(tf.constant(1e-8, x.dtype), x) - - # The following terms are zeros when masks[i] is 0. - l_x = py_utils.CheckNumerics(masks * (x - x_gt) / Pos(w_gt)) - l_y = py_utils.CheckNumerics(masks * (y - y_gt) / Pos(h_gt)) - s_w = py_utils.CheckNumerics(masks * tf.math.log(Pos(w) / Pos(w_gt))) - s_h = py_utils.CheckNumerics(masks * tf.math.log(Pos(h) / Pos(h_gt))) - return (_SmoothL1Norm(l_x) + _SmoothL1Norm(l_y) + _SmoothL1Norm(s_w) + - _SmoothL1Norm(s_h)) - - -def DistanceBetweenCentroids(u, v, masks): - """Computes the distance between centroids. - - Args: - u: [..., 4]. x/y/w/h for bboxes. - v: [..., 4]. x/y/w/h for bboxes. - masks: [...]. masks[i] == 1 means i-th entry (u[i] and v[i]) should be - considered in the distance/loss calculation. - - Returns: - A [...] tensor. i-th value is the distance measure of u[i] and v[i]. - """ - return masks * tf.reduce_sum(_SmoothL1Norm(u - v), axis=-1) - - -# TODO(zhifengc/drpng): Consider other possible loss formuation: -# E.g., -# (L1(u[x], v[x]) + L1(u[w], v[w]))*(L1(u[y], v[y]) + L1(u[h], v[h])) - - -def _IsOnLeftHandSideOrOn(point, v1, v2): - """Checks if a point lays on a vector direction, or is to the left. - - Args: - point: a tensor of shape [..., 2] of points to check. - v1: a float tensor of shape [..., 2] of vertices. - v2: a tensor of shape and type as v1. The second vertices. - - Returns: - A tensor of booleans indicating whether each point is on the left - of, or exactly on, the direction indicated by the vertices. - """ - v1 = py_utils.HasShape(v1, tf.shape(v2)) - # Prepare for broadcast: All point operations are on the right, - # and all v1/v2 operations are on the left. This is faster than left/right - # under the assumption that we have more points than vertices. - point_x = point[..., tf.newaxis, :, 0] - point_y = point[..., tf.newaxis, :, 1] - v1_x = v1[..., 0, tf.newaxis] - v2_x = v2[..., 0, tf.newaxis] - v1_y = v1[..., 1, tf.newaxis] - v2_y = v2[..., 1, tf.newaxis] - d1 = (point_y - v1_y) * (v2_x - v1_x) - d2 = (point_x - v1_x) * (v2_y - v1_y) - return d1 >= d2 - - -def _IsCounterClockwiseDirection(v1, v2, v3): - """Checks if the path from v1 to v3 via v2 is counter-clockwise. - - When v1 is equal to v2, or v2 equals v3, return true, by fiat. Tis will - work when the v's are padded vectors. - - Args: - v1: a float Tensor of shape [..., 2], indicating the starting point. - v2: a Tensor of same type and shape as v1, indicating the via point. - v3: a Tensor of same type and shape as v1, indicating the ending point. - - Returns: - True for all directions such that v1 to v3 via v2 is a counter clockwise - direction. - """ - # Check if it's on the left hand side, strictly, and without broadcasting. - v1 = py_utils.HasShape(v1, tf.shape(v2)) - v1 = py_utils.HasShape(v1, tf.shape(v3)) - v1_x, v1_y = v1[..., 0], v1[..., 1] - v2_x, v2_y = v2[..., 0], v2[..., 1] - v3_x, v3_y = v3[..., 0], v3[..., 1] - d1 = (v3_y - v1_y) * (v2_x - v1_x) - d2 = (v3_x - v1_x) * (v2_y - v1_y) - return d1 >= d2 - - -def IsWithinBBox(points, bbox): - """Checks if points are within a 2-d bbox. - - The function returns true if points are strictly inside the box. It also - returns true when the points are exactly on the box edges. - - Args: - points: a float Tensor of shape [..., 2] of points to be tested. The last - coordinates are (x, y). - bbox: a float Tensor of shape [..., 4, 2] of bboxes. The last coordinates - are the four corners of the bbox and (x, y). The corners are assumed to be - given in counter-clockwise order. - - Returns: - Tensor: If ``pshape = tf.shape(points)[:-1]`` and - ``bshape = tf.shape(bbox)[:-2]``, returns a boolean tensor of shape - ``tf.concat(pshape, bshape)``, where each element is true if the point is - inside to the corresponding box. If a point falls exactly on an edge of the - bbox, it is also true. - """ - bshape = py_utils.GetShape(bbox)[:-2] - pshape = py_utils.GetShape(points)[:-1] - bbox = py_utils.HasShape(bbox, tf.concat([bshape, [4, 2]], axis=0)) - points = py_utils.HasShape(points, tf.concat([pshape, [2]], axis=0)) - # Enumerate all 4 edges: - v1, v2, v3, v4 = (bbox[..., 0, :], bbox[..., 1, :], bbox[..., 2, :], - bbox[..., 3, :]) - v1v2v3_check = tf.reduce_all(_IsCounterClockwiseDirection(v1, v2, v3)) - v2v3v4_check = tf.reduce_all(_IsCounterClockwiseDirection(v2, v3, v4)) - v4v1v2_check = tf.reduce_all(_IsCounterClockwiseDirection(v4, v1, v2)) - v3v4v1_check = tf.reduce_all(_IsCounterClockwiseDirection(v3, v4, v1)) - with tf.control_dependencies([ - py_utils.Assert(v1v2v3_check, [v1, v2, v3]), - py_utils.Assert(v2v3v4_check, [v3, v3, v4]), - py_utils.Assert(v4v1v2_check, [v4, v1, v2]), - py_utils.Assert(v3v4v1_check, [v3, v4, v1]) - ]): - is_inside = tf.math.logical_and( - tf.math.logical_and( - _IsOnLeftHandSideOrOn(points, v1, v2), - _IsOnLeftHandSideOrOn(points, v2, v3)), - tf.math.logical_and( - _IsOnLeftHandSideOrOn(points, v3, v4), - _IsOnLeftHandSideOrOn(points, v4, v1))) - # Swap the last two dimensions. - is_inside = tf.einsum('...ij->...ji', tf.cast(is_inside, tf.int32)) - return tf.cast(is_inside, tf.bool) - - -def BBoxCorners2D(bboxes): - """Extract the corner points from a 5-DOF bbox representation. - - Args: - bboxes: A [..., 5] floating point bounding box representation ([x, y, dx, - dy, phi]). - - Returns: - A [..., 4, 2] floating point Tensor containing - the corner (x, y) points for every bounding box. - """ - corners = tf.constant([ - [0.5, 0.5], - [-0.5, 0.5], - [-0.5, -0.5], - [0.5, -0.5], - ]) - - leading_shape = py_utils.GetShape(bboxes)[:-1] - - # Extract location, dimension, and rotation. - location = bboxes[..., :2] - dimensions = bboxes[..., 2:4] - phi_world = bboxes[..., 4] - - # Convert rotation_phis into rotation matrices along unit z. - cos = tf.cos(phi_world) - sin = tf.sin(phi_world) - rotations_world = tf.reshape( - tf.stack([cos, -sin, sin, cos], axis=-1), leading_shape + [2, 2]) - - # Create axis-aligned corners from length/width/height. - corners = tf.einsum('...i,ji->...ji', dimensions, corners) - - # Rotate the corners coordinates to the rotated world frame. - corners = tf.einsum('...ij,...kj->...ki', rotations_world, corners) - - # Translate corners to the world location. - corners = corners + tf.reshape(location, leading_shape + [1, 2]) - return corners - - -def BBoxCorners(bboxes): - """Extract the corner points from a 7-DOF bbox representation. - - Args: - bboxes: A [batch, num_boxes, 7] floating point bounding box representation - ([x, y, z, dx, dy, dz, phi]). - - Returns: - A [batch, num_boxes, 8, 3] floating point Tensor containing - the corner (x, y, z) points for every bounding box. - """ - # Code adapted from vale/soapbox codebase. - # - # Corners in normalized box frame (unit cube centered at origin). - # - # Dimensions is [length, width, height]. - corners = tf.constant([ - [0.5, 0.5, 0.5], # top - [-0.5, 0.5, 0.5], # top - [-0.5, -0.5, 0.5], # top - [0.5, -0.5, 0.5], # top - [0.5, 0.5, -0.5], # bottom - [-0.5, 0.5, -0.5], # bottom - [-0.5, -0.5, -0.5], # bottom - [0.5, -0.5, -0.5], # bottom - ]) - - batch, nb, _ = py_utils.GetShape(bboxes, 3) - - # Extract location, dimension, and rotation. - location = bboxes[:, :, :3] - dimensions = bboxes[:, :, 3:6] - phi_world = bboxes[:, :, 6] - - # Convert rotation_phis into rotation matrices along unit z. - cos = tf.cos(phi_world) - sin = tf.sin(phi_world) - zero = tf.zeros_like(cos) - one = tf.ones_like(cos) - rotations_world = tf.reshape( - tf.stack([cos, -sin, zero, sin, cos, zero, zero, zero, one], axis=2), - [batch, nb, 3, 3]) - - # Create axis-aligned corners from length/width/height. - corners = tf.einsum('bni,ji->bnji', dimensions, corners) - - # Rotate the corners coordinates to the rotated world frame. - corners = tf.einsum('bnij,bnkj->bnki', rotations_world, corners) - - # Translate corners to the world location. - corners = corners + tf.reshape(location, (batch, nb, 1, 3)) - return corners - - -def IsWithinBBox3D(points_3d, bboxes_3d): - """Checks if points are within a 3-d bbox. - - Args: - points_3d: [num_points, 3] float32 Tensor specifying points in 3-d space as - [x, y, z] coordinates. - bboxes_3d: [num_bboxes, 7] float32 Tensor specifying a 3-d bboxes specified - as [x, y, z, dx, dy, dz, phi] where x, y and z is the center of the box. - - Returns: - boolean Tensor of shape [num_points, num_bboxes] indicating whether the - points belong within each box. - """ - points_3d = py_utils.HasRank(points_3d, 2) - points_3d = py_utils.HasShape(points_3d, [-1, 3]) - num_points, _ = py_utils.GetShape(points_3d, 2) - - bboxes_3d = py_utils.HasRank(bboxes_3d, 2) - bboxes_3d = py_utils.HasShape(bboxes_3d, [-1, 7]) - num_bboxes, _ = py_utils.GetShape(bboxes_3d, 2) - - # Compute the 3-D corners of the bounding boxes. - bboxes_3d_b = tf.expand_dims(bboxes_3d, 0) - bbox_corners = BBoxCorners(bboxes_3d_b) - bbox_corners = py_utils.HasShape(bbox_corners, [1, -1, 8, 3]) - # First four points are the top of the bounding box. - # Counter-clockwise arrangement of points specifying 2-d Euclidean box. - # (x0, y1) <--- (x1, y1) - # ^ - # | - # | - # (x0, y0) ---> (x1, y0) - bboxes_2d_corners = bbox_corners[0, :, 0:4, 0:2] - bboxes_2d_corners = py_utils.HasShape(bboxes_2d_corners, [-1, 4, 2]) - # Determine if points lie within 2-D (x, y) plane for all bounding boxes. - points_2d = points_3d[:, :2] - is_inside_2d = IsWithinBBox(points_2d, bboxes_2d_corners) - is_inside_2d = py_utils.HasShape(is_inside_2d, [num_points, num_bboxes]) - - # Determine if points lie with the z-dimension for all bounding boxes. - [_, _, z, _, _, dz, _] = tf.split(bboxes_3d, 7, axis=-1) - - def _ComputeLimits(center, width): - left = center - width / 2.0 - right = center + width / 2.0 - return left, right - - z0, z1 = _ComputeLimits(z, dz) - z_points = tf.expand_dims(points_3d[:, 2], -1) - - is_inside_z = tf.math.logical_and( - tf.less_equal(z_points, z1[tf.newaxis, :, 0]), - tf.greater_equal(z_points, z0[tf.newaxis, :, 0])) - is_inside_z = py_utils.HasShape(is_inside_z, [num_points, num_bboxes]) - - return tf.math.logical_and(is_inside_z, is_inside_2d) - - -def SphericalCoordinatesTransform(points_xyz): - """Converts points from xyz coordinates to spherical coordinates. - - https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions - for definitions of the transformations. - - Args: - points_xyz: A floating point tensor with shape [..., 3], where the inner 3 - dimensions correspond to xyz coordinates. - - Returns: - A floating point tensor with the same shape [..., 3], where the inner - dimensions correspond to (dist, theta, phi), where phi corresponds to - azimuth/yaw (rotation around z), and theta corresponds to pitch/inclination - (rotation around y). - """ - dist = tf.sqrt(tf.reduce_sum(tf.square(points_xyz), axis=-1)) - theta = tf.acos(points_xyz[..., 2] / tf.maximum(dist, 1e-7)) - # Note: tf.atan2 takes in (y, x). - phi = tf.atan2(points_xyz[..., 1], points_xyz[..., 0]) - return tf.stack([dist, theta, phi], axis=-1) diff --git a/lingvo/tasks/car/geometry_test.py b/lingvo/tasks/car/geometry_test.py deleted file mode 100644 index e84b8744..00000000 --- a/lingvo/tasks/car/geometry_test.py +++ /dev/null @@ -1,458 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for geometry.""" - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car import geometry -import numpy as np - - -class GeometryTest(test_utils.TestCase): - - def testXYWHBBoxesCentroid(self): - with self.session(): - xywh = np.tile( - np.array([[[10, 20, 8, 6], [0, 0, 0, 0], [-10, -20, 2.4, 3.6]]]), - (3, 2, 1)) - bboxes = np.tile( - np.array([[[17, 6, 23, 14], [0, 0, 0, 0], [-21.8, -11.2, -18.2, - -8.8]]]), (3, 2, 1)) - centroid = np.tile(np.array([[[10, 20], [0, 0], [-10, -20]]]), (3, 2, 1)) - print('shapes = ', xywh.shape, bboxes.shape, centroid.shape) - self.assertAllClose(geometry.XYWHToBBoxes(xywh).eval(), bboxes) - self.assertAllClose(geometry.BBoxesToXYWH(bboxes).eval(), xywh) - self.assertAllClose(geometry.BBoxesCentroid(bboxes).eval(), centroid) - - def testPointsToImage(self): - # From a KITTI example. - velo_to_image_plane = tf.constant( - [[6.09695409e+02, -7.21421597e+02, -1.25125855e+00, -1.23041806e+02], - [1.80384202e+02, 7.64479802e+00, -7.19651474e+02, -1.01016688e+02], - [9.99945389e-01, 1.24365378e-04, 1.04513030e-02, -2.69386912e-01]]) - - points = tf.constant([[1.25120001e+01, -5.09700012e+00, -7.26999998e-01], - [1.27309999e+01, -5.21099997e+00, 1.85000002e-01], - [-100, -20, -30]]) - - with self.session(): - points_image = geometry.PointsToImagePlane(points, velo_to_image_plane) - result = points_image.eval() - # First two points map to the image plane, the last one falls outside. - expected = [[914.15222168, 215.81137085], [914.61260986, 162.28927612], - [463.57763672, -32.7820015]] - self.assertAllClose(expected, result) - - def testReorderIndicesByPhi(self): - # Picks the anchor point somewhere on the x-y plane. - phi0 = 2 * np.pi / 3 - anchor = np.array([np.cos(phi0), np.sin(phi0)]) - - # Generates points clock-wise relative to the anchor. - pnts = [] - n, delta = 16, 0.01 - for i in range(n): - phi = phi0 + 1e-6 + 2 * np.pi / n * i - x, y = (1 + i) * np.cos(phi), (1 + i) * np.sin(phi) - pnts += [[y - delta, x - delta, y + delta, x + delta]] - pnts = np.array(pnts) - labels = np.arange(n) - - # Randomly permutate the points. - perm = np.random.permutation(n) - pnts = pnts[perm] - labels = labels[perm] - - # Uses ReorderIndicesByPhi to figure out how to reshuffle the points to - # recover their original order. - with self.session(): - indices = geometry.ReorderIndicesByPhi(anchor, pnts).eval() - - print('indices = ', indices) - print('labels = ', labels[indices]) - self.assertAllEqual(labels[indices], np.flip(np.arange(n), 0)) - - def testReorderIndicesByPhiEmpty(self): - with self.session(): - indices = geometry.ReorderIndicesByPhi( - tf.constant([1., 0.], tf.float32), - tf.constant(np.zeros([0, 4]), tf.float32)).eval() - - self.assertEqual(indices.shape, (0,)) - - def testDistanceBetweenCentroidsAndBBoxesFastAndFurious(self): - # pyformat: disabled - predicted = np.array([ - [1., 1., 2., 2.], # Perfect. - [2., 2., 2., 2.], # w/h are perfect. - [1., 1., 1., 1.], # centroids are perfect. - [0., 0., 0., 0.], # /0 if not padded properly. - ]) - groundtruth = np.array([ - [0., 0., 2., 2.], - [0., 0., 2., 2.], - [0., 0., 2., 2.], - [0., 0., 0., 0.], - ]) - masks = np.array([ - 1., - 1., - 1., - 0., - ]) - # pyformat: enabled - with self.session(): - distance = geometry.DistanceBetweenCentroidsAndBBoxesFastAndFurious( - predicted, groundtruth, masks) - self.assertAllClose( - distance.eval(), - [0.0, 1.0 / 4, np.square(np.log(1. / 2)), 0.0]) - - def testDistanceBetweenCentroids(self): - # pyformat: disabled - u = np.array([ - [1., 1., 2., 2.], # Perfect. - [2., 2., 2., 2.], # w/h are perfect. - [1., 1., 1., 1.], # centroids are perfect. - [0., 1., 2., 3.], # does not matter. - ]) - v = np.array([ - [1., 1., 2., 2.], # Perfect. - [1., 1., 2., 2.], # w/h are perfect. - [1., 1., 2., 2.], - [1., 2., 3., 4.], - ]) - masks = np.array([ - 1., - 1., - 1., - 0., - ]) - # pyformat: enabled - with self.session(): - distance = geometry.DistanceBetweenCentroids(u, v, masks) - self.assertAllClose(distance.eval(), [0.0, 1.0, 1.0, 0.0]) - - def testCoordinateTransform(self): - # This is a validated test case from a colab on a real scene. - # - # A single point [1, 1, 3]. - point = tf.constant([[[5736.94580078, 1264.85168457, 45.0271225]]], - dtype=tf.float32) - # Replicate the point to test broadcasting behavior. - replicated_points = tf.tile(point, [2, 4, 1]) - - # Pose of the car (x, y, z, yaw, roll, pitch). - # - # We negate the translations so that the coordinates are translated - # such that the car is at the origin. - pose = tf.constant([ - -5728.77148438, -1264.42236328, -45.06399918, -3.10496902, 0.03288471, - 0.00115049 - ], - dtype=tf.float32) - - transformed_points = geometry.CoordinateTransform(replicated_points, pose) - with self.session(): - result = transformed_points.eval() - - # We expect the point to be translated close to the car, and then rotated - # mostly around the x-axis. - expected = np.tile([[[-8.18451203, -0.13086951, -0.04200766]]], [2, 4, 1]) - - self.assertAllClose(expected, result) - - def _MakeTransformTestRotationMatrices(self, batch_size): - # Make a batch of 4x4 transformation matrices that only has rotation around - # the z-axis (world rotation). - rot_matrices = [] - for _ in range(batch_size): - rot_matrix = geometry._MakeRotationMatrix(tf.random.uniform([]), 0., 0.) - # Embed rotation matrix into a 4 x 4 matrix - rot_matrix = tf.pad(rot_matrix, [[0, 1], [0, 1]]) + tf.linalg.tensor_diag( - [0, 0, 0, 1.]) - rot_matrices.append(rot_matrix) - transforms = tf.stack(rot_matrices, axis=0) - return transforms - - def _MakeTransformTestTranslationMatrices(self, batch_size): - # Make a batch of 4x4 transformation matrices that translate in all - # directions. - translation_matrices = [] - for _ in range(batch_size): - translation_matrix = tf.random.uniform([3, 1]) - translation_matrix = tf.pad(translation_matrix, [[0, 1], [3, 0]]) - translation_matrix += tf.linalg.tensor_diag([1., 1., 1., 1.]) - translation_matrices.append(translation_matrix) - transforms = tf.stack(translation_matrices, axis=0) - return transforms - - def testTransformPointsRotation(self): - batch_size, num_points = 10, 8 - points = tf.random.uniform((batch_size, num_points, 3)) - transforms = self._MakeTransformTestRotationMatrices(batch_size) - points_transformed = geometry.TransformPoints(points, transforms) - with self.session(): - actual_points, actual_points_transformed = self.evaluate( - (points, points_transformed)) - # Points are the same on the z-axis (no rotation). - self.assertAllClose(actual_points[:, :, 2], actual_points_transformed[:, :, - 2]) - # Points are transformed, and different. - self.assertNotAllClose(actual_points, actual_points_transformed) - - def testTransformPointsTranslation(self): - batch_size, num_points = 10, 8 - points = tf.random.uniform((batch_size, num_points, 3)) - transforms = self._MakeTransformTestTranslationMatrices(batch_size) - points_transformed = geometry.TransformPoints(points, transforms) - with self.session(): - actual_points, actual_points_transformed, actual_transforms = self.evaluate( - (points, points_transformed, transforms)) - # Points are transformed, and different. - self.assertNotAllClose(actual_points, actual_points_transformed) - # Manually transform points and check that they are as expected. - actual_translation = actual_transforms[:, :3, 3] - self.assertAllClose(actual_points + actual_translation[:, np.newaxis, :], - actual_points_transformed) - - def testWrapAngleRad(self): - angles = tf.random.uniform([100], - minval=-100., - maxval=100., - dtype=tf.float32) - wrapped_angles = geometry.WrapAngleRad(angles) - with self.session(): - actual_angles, actual_wrapped_angles = self.evaluate( - (angles, wrapped_angles)) - - # The sine values of the angles should remain the same after wrapping. - self.assertAllClose( - np.sin(actual_angles), np.sin(actual_wrapped_angles), atol=1e-5) - - # Check ranges match the wrapped expectations. - self.assertTrue(np.all(actual_wrapped_angles >= -np.pi)) - self.assertTrue(np.all(actual_wrapped_angles <= np.pi)) - - def testTransformBBoxes3D(self): - batch_size, num_boxes = 10, 20 - bboxes_3d = tf.random.uniform((batch_size, num_boxes, 7)) - transforms = self._MakeTransformTestTranslationMatrices(batch_size) - bboxes_3d_transformed = geometry.TransformBBoxes3D(bboxes_3d, transforms) - with self.session(): - actual_bboxes_3d, actual_bboxes_3d_transformed = self.evaluate( - (bboxes_3d, bboxes_3d_transformed)) - - self.assertAllEqual(actual_bboxes_3d.shape, - actual_bboxes_3d_transformed.shape) - - # Dimensions (slice 3:6) should remain unchanged. - self.assertAllClose(actual_bboxes_3d[..., 3:6], - actual_bboxes_3d_transformed[..., 3:6]) - - # Rotation should remain unchanged. - self.assertAllClose(actual_bboxes_3d[..., 6], - actual_bboxes_3d_transformed[..., 6]) - - # Center xyz should be different. - self.assertNotAllClose(actual_bboxes_3d[..., :3], - actual_bboxes_3d_transformed[..., :3]) - - def testTransformBBoxes3DConsistentWithPoints(self): - num_boxes, num_points = 20, 100 - points = tf.random.uniform((num_points, 3)) - bboxes_3d = tf.random.uniform((num_boxes, 7)) - in_bboxes = geometry.IsWithinBBox3D(points, bboxes_3d) - transforms = self._MakeTransformTestTranslationMatrices(1)[0] - points_transformed = geometry.TransformPoints(points, transforms) - bboxes_3d_transformed = geometry.TransformBBoxes3D(bboxes_3d, transforms) - in_bboxes_transformed = geometry.IsWithinBBox3D(points_transformed, - bboxes_3d_transformed) - with self.session(): - actual_in_bboxes, actual_in_bboxes_transformed = self.evaluate( - (in_bboxes, in_bboxes_transformed)) - self.assertAllEqual(actual_in_bboxes, actual_in_bboxes_transformed) - - def testIsOnLeftHandSideOrOn(self): - v1 = tf.constant([[0., 0.]], dtype=tf.float32) - v2 = tf.constant([[1., 0.]], dtype=tf.float32) - p = tf.constant([[.5, .5], [-1., -3], [-1., 1.]], dtype=tf.float32) - with self.session(): - actual = self.evaluate(geometry._IsOnLeftHandSideOrOn(p, v1, v2)) - self.assertAllEqual([[True, False, True]], actual) - actual = self.evaluate(geometry._IsOnLeftHandSideOrOn(v1, v1, v2)) - self.assertAllEqual([[True]], actual) - actual = self.evaluate(geometry._IsOnLeftHandSideOrOn(v2, v1, v2)) - self.assertAllEqual([[True]], actual) - - def testIsWithinBBox3D(self): - num_points, num_bboxes = 19, 4 - # rotate the first box by pi / 2 so dim_x and dim_y are swapped. - # The last box is a cube rotated by 45 degrees. - bboxes = tf.constant([[1.0, 2.0, 3.0, 6.0, 0.4, 6.0, np.pi / 2], - [4.0, 5.0, 6.0, 7.0, 0.8, 7.0, 0.0], - [0.4, 0.3, 0.2, 0.1, 0.1, 0.2, 0.0], - [-10., -10., -10., 3., 3., 3., np.pi / 4]], - dtype=tf.float32) - points = tf.constant( - [ - [1.0, 2.0, 3.0], # box 0 (centroid) - [0.8, 2.0, 3.0], # box 0 (below x) - [1.1, 2.0, 3.0], # box 0 (above x) - [1.3, 2.0, 3.0], # box 0 (too far x) - [0.7, 2.0, 3.0], # box 0 (too far x) - [4.0, 5.0, 6.0], # box 1 (centroid) - [4.0, 4.6, 6.0], # box 1 (below y) - [4.0, 5.4, 6.0], # box 1 (above y) - [4.0, 4.5, 6.0], # box 1 (too far y) - [4.0, 5.5, 6.0], # box 1 (too far y) - [0.4, 0.3, 0.2], # box 2 (centroid) - [0.4, 0.3, 0.1], # box 2 (below z) - [0.4, 0.3, 0.3], # box 2 (above z) - [0.4, 0.3, 0.0], # box 2 (too far z) - [0.4, 0.3, 0.4], # box 2 (too far z) - [5.0, 7.0, 8.0], # none - [1.0, 5.0, 3.6], # box0, box1 - [-11.6, -10., -10.], # box3 (rotated corner point). - [-11.4, -11.4, -10.], # not in box3, would be if not rotated. - ], - dtype=tf.float32) - expected_is_inside = np.array([ - [True, False, False, False], - [True, False, False, False], - [True, False, False, False], - [False, False, False, False], - [False, False, False, False], - [False, True, False, False], - [False, True, False, False], - [False, True, False, False], - [False, False, False, False], - [False, False, False, False], - [False, False, True, False], - [False, False, True, False], - [False, False, True, False], - [False, False, False, False], - [False, False, False, False], - [False, False, False, False], - [True, True, False, False], - [False, False, False, True], - [False, False, False, False], - ]) - assert points.shape[0] == num_points - assert bboxes.shape[0] == num_bboxes - assert expected_is_inside.shape[0] == num_points - assert expected_is_inside.shape[1] == num_bboxes - - with self.session(): - is_inside = self.evaluate(geometry.IsWithinBBox3D(points, bboxes)) - self.assertAllEqual([num_points, num_bboxes], is_inside.shape) - self.assertAllEqual(expected_is_inside, is_inside) - - def testIsWithinBBox(self): - bbox = tf.constant([[[0., 0.], [1., 0.], [1., 1.], [0., 1.]]], - dtype=tf.float32) - points = tf.constant( - [[-.5, -.5], [.5, -.5], [1.5, -.5], [1.5, .5], [1.5, 1.5], [.5, 1.5], - [-.5, 1.5], [-.5, .5], [1., 1.], [.5, .5]], - dtype=tf.float32) - with self.session(): - is_inside = self.evaluate(geometry.IsWithinBBox(points, bbox)) - expected = [[False]] * 8 + [[True]] * 2 - self.assertAllEqual(expected, is_inside) - - def testIsWithinRotatedBBox(self): - bbox = tf.constant([[[.2, 0.], [1., .2], [.8, 1.], [0., .8]]], - dtype=tf.float32) - points = tf.constant([[0., 0.], [1., 0], [1., 1.], [0., 1.], [.5, .5]], - dtype=tf.float32) - with self.session(): - is_inside = self.evaluate(geometry.IsWithinBBox(points, bbox)) - expected = [[False]] * 4 + [[True]] - self.assertAllEqual(expected, is_inside) - - def testIsCounterClockwiseDirection(self): - points = tf.constant( - [[[0., 0.], [0., 0.], [0., 1.]], [[0., 0.], [0., 1.], [1., 1.]], - [[0., .8], [.2, 0.], [1., .2]], [[.2, .0], [1., .2], [.0, 1.]], - [[1., 1.], [1., 0], [0., 0.]]], - dtype=tf.float32) - # points ~ [num points, v1/v2/v3, x/y]. - points = py_utils.HasShape(points, [-1, 3, 2]) - expected = [True, False, True, True, False] - with self.session(): - dircheck = self.evaluate( - geometry._IsCounterClockwiseDirection(points[:, 0, :], - points[:, 1, :], points[:, - 2, :])) - self.assertAllEqual(expected, dircheck) - - def testBBoxCorners(self): - # Create four bounding boxes, two identical in each batch. - # - # This tests both that the batching and number of box dimensions are handled - # properly. - bboxes = tf.constant([[[1, 2, 3, 4, 3, 6, 0.], [1, 2, 3, 4, 3, 6, 0.]], - [[1, 2, 3, 4, 3, 6, np.pi / 2.], - [1, 2, 3, 4, 3, 6, np.pi / 2.]]]) - corners = geometry.BBoxCorners(bboxes) - with self.session(): - corners_np = self.evaluate(corners) - self.assertEqual((2, 2, 8, 3), corners_np.shape) - - # Extrema of first two boxes are ([-1, 3], [0.5, 3.5], [0, 6]) - for i in [0, 1]: - self.assertAllClose(-1, np.min(corners_np[0, i, :, 0])) - self.assertAllClose(3, np.max(corners_np[0, i, :, 0])) - self.assertAllClose(0.5, np.min(corners_np[0, i, :, 1])) - self.assertAllClose(3.5, np.max(corners_np[0, i, :, 1])) - self.assertAllClose(0, np.min(corners_np[0, i, :, 2])) - self.assertAllClose(6, np.max(corners_np[0, i, :, 2])) - - # Extrema of second two boxes is ([-0.5, 2.5], [0, 4], [0, 6]) - # because it's the first box rotated by 90 degrees. - for i in [0, 1]: - self.assertAllClose(-0.5, np.min(corners_np[1, i, :, 0])) - self.assertAllClose(2.5, np.max(corners_np[1, i, :, 0])) - self.assertAllClose(0, np.min(corners_np[1, i, :, 1])) - self.assertAllClose(4, np.max(corners_np[1, i, :, 1])) - self.assertAllClose(0, np.min(corners_np[1, i, :, 2])) - self.assertAllClose(6, np.max(corners_np[1, i, :, 2])) - - def testSphericalCoordinatesTransform(self): - np_xyz = np.random.randn(5, 6, 3) - points_xyz = tf.constant(np_xyz, dtype=tf.float32) - spherical_coordinates = geometry.SphericalCoordinatesTransform(points_xyz) - - with self.session(): - actual_spherical_coordinates = self.evaluate(spherical_coordinates) - - # Convert coordinates back to xyz to verify. - dist = actual_spherical_coordinates[..., 0] - theta = actual_spherical_coordinates[..., 1] - phi = actual_spherical_coordinates[..., 2] - - x = dist * np.sin(theta) * np.cos(phi) - y = dist * np.sin(theta) * np.sin(phi) - z = dist * np.cos(theta) - - self.assertAllClose(x, np_xyz[..., 0]) - self.assertAllClose(y, np_xyz[..., 1]) - self.assertAllClose(z, np_xyz[..., 2]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/input_extractor.py b/lingvo/tasks/car/input_extractor.py deleted file mode 100644 index 20737825..00000000 --- a/lingvo/tasks/car/input_extractor.py +++ /dev/null @@ -1,188 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Input extractors. - -Input extractors are an API for parsing and processing a set of fields from -serialized records. -""" - -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils - -from lingvo.tasks.car import base_extractor - -BaseExtractor = base_extractor._BaseExtractor # pylint:disable=protected-access -BUCKET_UPPER_BOUND = base_extractor.BUCKET_UPPER_BOUND - - -################################################################################ -# Extractors for car data. -################################################################################ -class FieldsExtractor(base_layer.BaseLayer): - """An API for parsing and processing a set of fields from serialized records. - - Input generators often need to parse several fields from a serialized record. - This involves two stages: specifying the name and type of the fields to - extract from serialized records (tf.Example or tf.SequenceExample), and then - processing the raw output into a form to be consumed by higher-level callers. - - This class attempts to modularize this processing within the Minecraft input - generators, so that users can easily create input generator pipelines that mix - and match the composition of different fields from the same dataset. - - A descendant of this class will implement three functions: - - 1) FeatureMap(): returning a dictionary of field names to field types, e.g., - 'images' to tf.io.VarLenFeature(tf.string). For PlainTextIterator - datasets, FeatureMap() should be empty. - - 2) _Extract(features): Given a 'features' dictionary containing the result - from calling tf.io.parse_example or tf.parse_sequence_example on all - extractors' features, produce a NestedMap of Tensors. - - NOTE: The return of the overall pipeline is a NestedMap of batched - Tensors. However, the names and associations of the fields of each - extractor are lost on the boundary of the map fn. At the moment, one - must implement _Extract() such that the names of the fields returned in - the NestedMap matches self.Shape()'s keys; this is checked during the - parent's Extract() call. - - 3) Shape(): A NestedMap mapping names of outputs to their static shape, - without the batch dimension. In _InputBatch, this shape will be used to - ensure that every output has a statically known shape. - - The caller of Extractors calls each extractor's FeatureMap() to populate the - schema passed to tf.io.parse_example() or tf.parse_sequence_example(). The - resulting dicationary of Tensors is then passed to each extractor's _Extract() - function (via FieldsExtractor.Extract()) to return each extractor's output. - - It is the responsibility of the caller to maintain orders of outputs, since - NestedMaps do not have any inherent ordering during iteration. - """ - - @classmethod - def Params(cls): - """Defaults params.""" - p = super().Params() - p.name = cls.__name__ - return p - - def __init__(self, params): - super().__init__(params) - self.SetVariableFree() - - def FeatureMap(self): - """Return a dictionary from tf.Example feature names to Features.""" - raise NotImplementedError() - - def ContextMap(self): - """Return a dict mapping tf.SequenceExample context names to Features.""" - return {} - - def Extract(self, features): - """Given 'feature' (Sparse)Tensors, output Tensors for consumption. - - NOTE: Implementation provided by subclasses's _Extract() method. - - Args: - features: A dictionary of (Sparse)Tensors which includes tensors from all - extractors. - - Returns: - A NestedMap of output Tensors. - """ - outputs = self._Extract(features) - shapes = self.Shape() - assert outputs.IsCompatible(shapes), '{} vs. {}'.format( - outputs.DebugString(), shapes.DebugString()) - return outputs - - def Filter(self, outputs): - """Return the bucket based on the result of Extract(). - - This function should return 1 if the example should pass through without - being dropped, and BUCKET_UPPER_BOUND if the example should be dropped. - - Args: - outputs: The NestedMap returned by this extractor's _Extract() function. - This is useful to implement filtering based on the values of the - extracted example. - - Returns: - A scalar bucket id. - """ - del outputs - return 1 - - def Shape(self): - """Return a NestedMap of un-batched fully-specified tf.TensorShapes.""" - raise NotImplementedError() - - def DType(self): - """Return a NestedMap mapping names to tf.DType.""" - raise NotImplementedError() - - def _Extract(self, features): - """The subclass-defined implementation of Extract(). - - Args: - features: A dictionary of (Sparse)Tensors which includes tensors from all - extractors. - - Returns: - A NestedMap of output Tensors whose key names match self.Shape()'s keys. - """ - raise NotImplementedError() - - -class LaserExtractor(FieldsExtractor): - """Interface for extracting laser data. - - Must produce: - points_xyz: [max_num_points, 3] - XYZ coordinates of laser points. - - points_feature: [max_num_points, num_features] - Features for each point in - points_xyz. - - points_padding: [max_num_points]: Padding for points. 0 means the - corresponding point is the original, and 1 means there is no point - (xyz or feature) present. Only present if max_num_points is not - None. - - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('max_num_points', None, 'The number of points per spin.') - p.Define('num_features', 1, 'Number of features per laser point.') - return p - - def Shape(self): - p = self.params - ret = py_utils.NestedMap( - points_xyz=tf.TensorShape([p.max_num_points, 3]), - points_feature=tf.TensorShape([p.max_num_points, p.num_features])) - if p.max_num_points is not None: - ret.points_padding = tf.TensorShape([p.max_num_points]) - return ret - - def DType(self): - ret = py_utils.NestedMap(points_xyz=tf.float32, points_feature=tf.float32) - if self.params.max_num_points is not None: - ret.points_padding = tf.float32 - return ret diff --git a/lingvo/tasks/car/input_extractor_test.py b/lingvo/tasks/car/input_extractor_test.py deleted file mode 100644 index 9f5934c8..00000000 --- a/lingvo/tasks/car/input_extractor_test.py +++ /dev/null @@ -1,45 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for input_extractor.""" - -from lingvo import compat as tf -from lingvo.core import hyperparams -from lingvo.core import test_utils -from lingvo.tasks.car import input_extractor -from lingvo.tasks.car import input_preprocessors - - -class InputExtractorTest(test_utils.TestCase): - - def testBaseExtractorRaisesErrorWithMissingPreprocessorKeys(self): - extractors = hyperparams.Params() - preprocessors = hyperparams.Params() - preprocessors.Define( - 'count_points', - input_preprocessors.CountNumberOfPointsInBoxes3D.Params(), '') - preprocessors.Define('viz_copy', - input_preprocessors.CreateDecoderCopy.Params(), '') - p = input_extractor.BaseExtractor.Params(extractors).Set( - preprocessors=preprocessors, - preprocessors_order=['count_points', 'missing_key', 'viz_copy']) - with self.assertRaisesRegex( - ValueError, - r'preprocessor_order specifies keys which were not found .*'): - p.Instantiate() - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/input_preprocessors.py b/lingvo/tasks/car/input_preprocessors.py deleted file mode 100644 index 1faeac57..00000000 --- a/lingvo/tasks/car/input_preprocessors.py +++ /dev/null @@ -1,3450 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Input preprocessors.""" - -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import py_utils -from lingvo.tasks.car import car_lib -from lingvo.tasks.car import detection_3d_lib -from lingvo.tasks.car import geometry -from lingvo.tasks.car import ops -import numpy as np -# pylint:disable=g-direct-tensorflow-import -from tensorflow.python.ops import inplace_ops -# pylint:enable=g-direct-tensorflow-import - - -def _ConsistentShuffle(tensors, seed): - """Shuffle multiple tensors with the same shuffle order.""" - shuffled_idx = tf.range(tf.shape(tensors[0])[0]) - shuffled_idx = tf.random.shuffle(shuffled_idx, seed=seed) - return tuple([tf.gather(t, shuffled_idx) for t in tensors]) - - -def _GetApplyPointMaskFn(points_mask): - """Returns a function that applies a mask to one of our points tensors.""" - - def _ApplyPointMaskFn(points_tensor): - """Applies a mask to the points tensor.""" - if points_tensor is None: - return points_tensor - return tf.boolean_mask(points_tensor, points_mask) - - return _ApplyPointMaskFn - - -def _Dense(sparse): - return tf.sparse_to_dense( - sparse_indices=sparse.indices, - output_shape=sparse.dense_shape, - sparse_values=sparse.values, - default_value=0) - - -class Preprocessor(base_layer.BaseLayer): - """Base class for input preprocessor. - - Input preprocessors expect the combined output of all extractors and performs - a transformation on them. Input preprocessors can add/edit/remove fields - from the NestedMap of features. - - Note: Features correspond to that for one example (no batch dimension). - - Sub-classes need to implement the following three functions: - - 1) TransformFeatures(features): Given a NestedMap of features representing the - output of all the extractors, apply a transformation on the features. - - 2) TransformShapes(shapes): Given a corresponding NestedMap of shapes, - produce a NestedMap of shapes that corresponds to the transformation of the - features after TransformFeatures. - - 3) TransformDTypes(dtypes): Given a corresponding NestedMap of dtypes, - produce a NestedMap of dtypes that corresponds to the transformation of the - features after TransformFeatures. - - The preprocessor is expected to explicitly pass through untouched fields. - For example, a preprocessor that does data augmentation should modify the - features NestedMap on the fields it cares about augmenting, and then return - the features NestedMap. - """ - - @classmethod - def Params(cls): - """Default params.""" - p = super().Params() - p.name = cls.__name__ - return p - - def FProp(self, theta, features): - """Performs TransformFeatures.""" - del theta # unused - return self.TransformFeatures(features) - - def TransformFeatures(self, features): - """Transforms the features for one example. - - Args: - features: A `NestedMap` of tensors. - - Returns: - A `NestedMap` of tensors corresponding. - """ - raise NotImplementedError() - - def TransformShapes(self, shapes): - """Sets correct shapes corresponding to TransformFeatures. - - Args: - shapes: A `NestedMap` of TensorShapes, corresponding to the - pre-transformed features. - - Returns: - A `NestedMap` of TensorShapes corresponding to the transformed features. - """ - raise NotImplementedError() - - def TransformDTypes(self, dtypes): - """Sets correct dtypes corresponding to TransformFeatures. - - Args: - dtypes: A `NestedMap` of DTypes, corresponding to the pre-transformed - features. - - Returns: - A `NestedMap` of DTypes corresponding to the transformed features. - """ - raise NotImplementedError() - - -class EntryPreprocessor(Preprocessor): - """A Preprocessor that transforms a NestedMap sub-structure. - - Some preprocessors want to apply a function to any NestedMap whose key matches - a specific prefix. An EntryPreprocessor provides an interface for specifying - the function transformation for a NestedMap of inputs, adding, modifying, or - deleting the entries in that NestedMap. - - For example, if an input contains a nested structure such as: - - lasers.front.xyz - .features - - lasers.side.xyz - .features - - and one wants to apply a transform that modifies the .xyz features - on both structures, one can define an EntryPreprocessor that implements: - - UpdateEntry(entry): - UpdateEntryShape(shapes): - UpdateEntryDType(dtypes): - - and set self.params.prefixes = ['lasers.front', 'lasers.side'] - where the prefixes refer to a fully-qualified NestedMap sub-structure. - - The arguments to these functions will contain just the NestedMap structure - whose key prefix can be found in self.params.prefixes. One can then modify - these structures as desired. - - Example: - def UpdateEntry(self, entry): - # entry is a NestedMap. - assert 'xyz' in entry - entry.xyz = self._ApplyFn(entry.xyz) - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('prefixes', ['pseudo_ri'], 'List of keys to apply to.') - return p - - def _ApplyToMatchingStructure(self, nested_map, fn): - """Apply fn to any NestedMap sub-structure whose prefix is in p.prefixes.""" - p = self.params - # Don't mutate the original. - nested_map = nested_map.DeepCopy() - updated_entries = [] - for prefix in p.prefixes: - entry = nested_map.GetItem(prefix) - if not isinstance(entry, py_utils.NestedMap): - raise TypeError('Prefix key {} selected a {}, not a NestedMap!'.format( - prefix, type(entry))) - fn(entry) - updated_entries.append(entry) - return nested_map, updated_entries - - def UpdateEntry(self, entry): - """Update the Tensors in a NestedMap entry. - - Args: - entry: A NestedMap of Tensors. - """ - raise NotImplementedError() - - def UpdateEntryShape(self, shapes): - """Update the shapes in a NestedMap entry. - - Args: - shapes: A NestedMap of TensorShapes. - """ - raise NotImplementedError() - - def UpdateEntryDType(self, dtypes): - """Transform the dtypes in a NestedMap entry. - - Args: - dtypes: A NestedMap of dtypes. - """ - raise NotImplementedError() - - def TransformFeatures(self, features): - features, _ = self._ApplyToMatchingStructure(features, self.UpdateEntry) - return features - - def TransformShapes(self, shapes): - shapes, _ = self._ApplyToMatchingStructure(shapes, self.UpdateEntryShape) - return shapes - - def TransformDTypes(self, dtypes): - dtypes, _ = self._ApplyToMatchingStructure(dtypes, self.UpdateEntryDType) - return dtypes - - -class CreateDecoderCopy(Preprocessor): - """Creates references to current lasers, images, and labels. - - This is useful if the data is further transformed. - - If desired, the keys that are copied can be customized by overriding the - default keys param. - - This preprocessor expects features to optionally contain the following keys: - - lasers - a NestedMap of tensors - - images - a NestedMap of tensors - - labels - a NestedMap of tensors - - Adds the following features (if the features existed): - - decoder_copy.lasers - a copy of the lasers NestedMap - - decoder_copy.images - a copy of the images NestedMap - - decoder_copy.labels - a copy of the labels NestedMap - - The processor also by default pads the laser features; this can be disabled - by setting the pad_lasers param to None. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('keys', ['lasers', 'labels', 'images'], - 'Keys to look for and copy if exists.') - p.Define('parent_key', 'decoder_copy', 'The key to nest the copies under.') - p.Define('pad_lasers', PadLaserFeatures.Params(), - 'Params for a layer that pads the laser features.') - p.name = 'create_decoder_copy' - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.pad_lasers is not None: - self.CreateChild('pad_lasers', p.pad_lasers) - - def _DeepCopyIfExists(self, keys, nested_map, parent_key): - """Deep copy a specific key to a parent key if it exists.""" - for key in keys: - if key in nested_map: - if parent_key not in nested_map: - nested_map[parent_key] = py_utils.NestedMap() - nested_map[parent_key][key] = nested_map[key].DeepCopy() - return nested_map - - def TransformFeatures(self, features): - p = self.params - features = self._DeepCopyIfExists(p.keys, features, p.parent_key) - if p.pad_lasers is not None: - features[p.parent_key] = self.pad_lasers.TransformFeatures( - features[p.parent_key]) - return features - - def TransformShapes(self, shapes): - p = self.params - shapes = self._DeepCopyIfExists(p.keys, shapes, p.parent_key) - if p.pad_lasers is not None: - shapes[p.parent_key] = self.pad_lasers.TransformShapes( - shapes[p.parent_key]) - return shapes - - def TransformDTypes(self, dtypes): - p = self.params - dtypes = self._DeepCopyIfExists(p.keys, dtypes, p.parent_key) - if p.pad_lasers is not None: - dtypes[p.parent_key] = self.pad_lasers.TransformDTypes( - dtypes[p.parent_key]) - return dtypes - - -class FilterByKey(Preprocessor): - """Filters features to keep only specified keys. - - This keeps only feature entries that are specified. This allows us to reduce - the number of fields returned. For example, during training, one may not - need the actual laser points if training with a pillars based model that - has a preprocessor that already maps the points to grid. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'keep_key_prefixes', [''], 'Prefixes of keys to keep. If this ' - 'contains the empty string, then it will keep all the keys.') - return p - - def _FilterFn(self, key, entry): - """Filter a nested map.""" - del entry # unused - p = self.params - for prefix in p.keep_key_prefixes: - if key.startswith(prefix): - return True - return False - - def TransformFeatures(self, features): - return features.FilterKeyVal(self._FilterFn) - - def TransformShapes(self, shapes): - return shapes.FilterKeyVal(self._FilterFn) - - def TransformDTypes(self, dtypes): - return dtypes.FilterKeyVal(self._FilterFn) - - -class FilterGroundTruthByNumPoints(Preprocessor): - """Removes ground truth boxes with less than params.min_num_points points. - - This preprocessor expects features to contain the following keys:: - labels.labels of shape [..., L] - labels.bboxes_3d of shape [..., L, 7] - labels.bboxes_3d_mask of shape [..., L] - labels.unfiltered_bboxes_3d_mask of shape [..., L] - labels.bboxes_3d_num_points of shape [..., L]. - - Modifies the bounding box data to turn off ground truth objects that don't - meet the params.min_num_points point filter: - - labels.labels: Boxes with less than params.min_num_points have their label - set to params.background_id (defaults to 0). - - labels.bboxes_3d_mask: Boxes with less than params.min_num_points are set - to 0. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'min_num_points', 1, 'The minimum number of points allowed before ' - 'the associated ground truth box is turned off. Defaults to 1.') - p.Define( - 'background_id', 0, 'The ID of the background class we set ' - 'filtered boxes to. Defaults to 0.') - return p - - def TransformFeatures(self, features): - p = self.params - bbox_is_valid = tf.greater_equal(features.labels.bboxes_3d_num_points, - p.min_num_points) - features.labels.labels = tf.where( - bbox_is_valid, features.labels.labels, - p.background_id * tf.ones_like(features.labels.labels)) - features.labels.bboxes_3d_mask *= tf.cast(bbox_is_valid, tf.float32) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class FilterGroundTruthByDifficulty(Preprocessor): - """Removes groundtruth boxes based on detection difficulty. - - This preprocessor expects features to contain the following keys:: - labels.single_frame_detection_difficulties of shape [..., L] - labels.labels of shape [..., L] - labels.bboxes_3d_mask of shape [..., L] - labels.unfiltered_bboxes_3d_mask of shape [..., L] - - The preprocessor masks out the bboxes_3d_mask / labels based on whether - single_frame_detection_difficulties is greater than p.difficulty_threshold. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'background_id', 0, 'The ID of the background class we set ' - 'filtered boxes to. Defaults to 0.') - p.Define( - 'difficulty_threshold', 1, - 'Filter groundtruth bounding boxes whose detection difficulty is ' - 'greater than `difficulty_threshold`') - return p - - def TransformFeatures(self, features): - p = self.params - bbox_is_valid = tf.less_equal( - features.labels.single_frame_detection_difficulties, - p.difficulty_threshold) - features.labels.labels = tf.where( - bbox_is_valid, features.labels.labels, - p.background_id * tf.ones_like(features.labels.labels)) - features.labels.bboxes_3d_mask *= tf.cast(bbox_is_valid, tf.float32) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class CountNumberOfPointsInBoxes3D(Preprocessor): - """Computes bboxes_3d_num_points. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - labels.bboxes_3d of shape [L, 7] - - labels.bboxes_3d_mask of shape [L] - - and optionally points_padding of shape [P] corresponding to the padding. - if points_padding is None, then all points are considered valid. - - Adds the following features: - labels.bboxes_3d_num_points: [L] - integer tensor containing the number of - laser points for each corresponding bbox. - """ - - def TransformFeatures(self, features): - points_xyz = features.lasers.points_xyz - if 'points_padding' in features.lasers: - points_mask = 1 - features.lasers.points_padding - points_xyz = tf.boolean_mask(points_xyz, points_mask) - - points_in_bboxes_mask = geometry.IsWithinBBox3D(points_xyz, - features.labels.bboxes_3d) - bboxes_3d_num_points = tf.reduce_sum( - tf.cast(points_in_bboxes_mask, tf.int32), axis=0, keepdims=False) - bboxes_3d_num_points *= tf.cast(features.labels.bboxes_3d_mask, tf.int32) - - features.labels.bboxes_3d_num_points = bboxes_3d_num_points - return features - - def TransformShapes(self, shapes): - num_bboxes = shapes.labels.bboxes_3d[0] - shapes.labels.bboxes_3d_num_points = tf.TensorShape([num_bboxes]) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.labels.bboxes_3d_num_points = tf.int32 - return dtypes - - -class AddPerPointLabels(Preprocessor): - """Computes the class and bbox id of each point. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - labels.bboxes_3d of shape [L, 7] - - labels.labels of shape [L] - - This makes an assumption that each point is only in 1 box, which should - almost always true in 3D. In cases where this is not true, the largest - label integer and largest bbox_id will be assigned. - - NOTE: Be very careful that this is performed after any modifications - to the semantic labels of each point in the pointcloud. Examples of this - would be operators like GroundTruthAugmentation, or DropBoxesOutOfRange. - - Adds the following features: - lasers.points_label: [P] - integer tensor containing the class id of each - point. - lasers.points_bbox_id: [P] - integer tensor containing box id of each - point from 0 to num_bboxes, where an id of num_bboxes indicates a - background point. - lasers.points_bbox_3d: [P, 7] - float tensor containing bounding box of - each point. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'per_dimension_adjustment', None, - 'A list of len 3 of floats with the amount (in meters) to add to ' - 'each dimension of the box before using it to select points. ' - 'If enabled, this is designed to protect against overly tight box ' - 'annotations that appear in KITTI.') - return p - - def TransformFeatures(self, features): - p = self.params - points_xyz = features.lasers.points_xyz - bboxes_3d = features.labels.bboxes_3d - num_points, _ = py_utils.GetShape(points_xyz) - num_bboxes, _ = py_utils.GetShape(bboxes_3d) - - if p.per_dimension_adjustment: - if len(p.per_dimension_adjustment) != 3: - raise ValueError( - 'param `per_dimension_adjustment` expected to be len 3.') - dims_adjustment = tf.constant([0, 0, 0] + p.per_dimension_adjustment + - [0]) - bboxes_3d = bboxes_3d + dims_adjustment - - # Find which points are in each box and what class each box is. - points_in_bboxes_mask = geometry.IsWithinBBox3D(points_xyz, bboxes_3d) - points_in_bboxes_mask = tf.cast(points_in_bboxes_mask, tf.int32) - points_in_bboxes_mask = py_utils.HasShape(points_in_bboxes_mask, - [num_points, num_bboxes]) - - # points_in_bboxes_mask is a [num_points, num_bboxes] 0/1 tensor - # indicating whether that point is in a given box. - # Each point should only be in one box, so after broadcasting the label - # across the binary mask, we do a reduce_max to get the max label id - # for each point. Since each point only belongs to one box, it will be - # the only non-zero (background) label in that box. - # Note: We assume background to be class_id == 0 - points_label = tf.reduce_max( - points_in_bboxes_mask * features.labels.labels, axis=1) - points_bbox_id = tf.argmax( - points_in_bboxes_mask, axis=1, output_type=tf.int32) - # If the class is background, make its id == num_bboxes - points_bbox_id = tf.where(points_label > 0, points_bbox_id, - tf.broadcast_to(num_bboxes, [num_points])) - - # For each point, get the bbox_3d data. - dummy_bbox = tf.constant([[0, 0, 0, 0, 0, 0, 0]], dtype=tf.float32) - bboxes_3d = tf.concat([bboxes_3d, dummy_bbox], axis=0) - points_bbox_3d = tf.gather(bboxes_3d, points_bbox_id) - - points_label = tf.reshape(points_label, [num_points]) - points_bbox_id = tf.reshape(points_bbox_id, [num_points]) - features.lasers.points_label = points_label - features.lasers.points_bbox_id = points_bbox_id - features.lasers.points_bbox_3d = points_bbox_3d - return features - - def TransformShapes(self, shapes): - num_points = shapes.lasers.points_xyz[0] - shapes.lasers.points_label = tf.TensorShape([num_points]) - shapes.lasers.points_bbox_id = tf.TensorShape([num_points]) - shapes.lasers.points_bbox_3d = tf.TensorShape([num_points, 7]) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.lasers.points_label = tf.int32 - dtypes.lasers.points_bbox_id = tf.int32 - dtypes.lasers.points_bbox_3d = tf.float32 - return dtypes - - -class PointsToGrid(Preprocessor): - """Bins points to a 3D-grid using custom op: ops.point_to_grid. - - Expects features to have keys: - - lasers.points_xyz of shape [P, 3] - - and optionally points_padding of shape [P] corresponding to the padding. - if points_padding is None, then all points are considered valid. - - If normalizing the labels is enabled, then also expects: - - labels.weights - - labels.bboxes_td - - labels.bboxes_td_mask - - labels.bboxes_3d_mask - - Let: - gx, gy, gz = p.grid_size - F = 3 + num_laser_features - - Adds the following features: - grid_centers: [gx, gy, gz, 3]: For each grid cell, the (x,y,z) - floating point coordinate of its center. - grid_num_points: [gx, gy, gz]: The number of points in each grid - cell (integer). - laser_grid: [gx, gy, gz, num_points_per_cell, F] - A 5D floating - point Tensor containing the laser data placed into a fixed grid. - - Modifies the bboxes in labels to also be within the grid range x/y by default. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_points_per_cell', 100, - 'The maximum number of points per cell.') - p.Define('grid_size', (40, 40, 1), 'Grid size along x,y,z axis.') - - # The max range of x and y is [-80, 80]. - p.Define('grid_range_x', (-80, 80), 'The X-axis Range covered by the grid') - p.Define('grid_range_y', (-80, 80), 'The Y-axis Range covered by the grid') - p.Define('grid_range_z', (-2, 4), 'The Z-axis Range covered by the grid') - - p.Define('normalize_td_labels', True, - 'Whether to clip the labels to the grid limits.') - return p - - def _NormalizeLabels(self, ymin, xmin, ymax, xmax, x_range, y_range): - """Normalizes the bboxes within a given range.""" - assert x_range, 'Must specify x_range if clipping.' - assert y_range, 'Must specify y_range if clipping.' - assert len(x_range) == 2, 'x_range %s must be 2 elements.' % x_range - assert len(y_range) == 2, 'y_range %s must be 2 elements.' % y_range - - x_range_min = x_range[0] - x_range_len = x_range[1] - x_range[0] - y_range_min = y_range[0] - y_range_len = y_range[1] - y_range[0] - - xmin = tf.cast(xmin - x_range_min, tf.float32) / tf.cast( - x_range_len, tf.float32) - xmax = tf.cast(xmax - x_range_min, tf.float32) / tf.cast( - x_range_len, tf.float32) - ymin = tf.cast(ymin - y_range_min, tf.float32) / tf.cast( - y_range_len, tf.float32) - ymax = tf.cast(ymax - y_range_min, tf.float32) / tf.cast( - y_range_len, tf.float32) - - return ymin, xmin, ymax, xmax - - def TransformFeatures(self, features): - p = self.params - - points_xyz = features.lasers.points_xyz - points_feature = features.lasers.points_feature - if ('points_padding' in features.lasers and - features.lasers.points_padding is not None): - points_mask = 1 - features.lasers.points_padding - points_xyz = tf.boolean_mask(points_xyz, points_mask) - points_feature = tf.boolean_mask(points_feature, points_mask) - - points_full = tf.concat([points_xyz, points_feature], axis=-1) - points_grid_full, grid_centers, num_points = ops.point_to_grid( - points_full, p.num_points_per_cell, p.grid_size[0], p.grid_size[1], - p.grid_size[2], p.grid_range_x, p.grid_range_y, p.grid_range_z) - - features.laser_grid = points_grid_full - features.grid_centers = grid_centers - features.grid_num_points = num_points - - if p.normalize_td_labels: - # Normalize bboxes_td w.r.t grid range. - obb = features.labels - x_range = p.grid_range_x - y_range = p.grid_range_y - ymin, xmin, ymax, xmax = tf.unstack(obb.bboxes_td[..., :4], axis=-1) - ymin, xmin, ymax, xmax = self._NormalizeLabels( - ymin, xmin, ymax, xmax, x_range=x_range, y_range=y_range) - obb.bboxes_td = tf.concat( - [tf.stack([ymin, xmin, ymax, xmax], axis=-1), obb.bboxes_td[..., 4:]], - axis=-1) - - return features - - def TransformShapes(self, shapes): - p = self.params - shapes.grid_centers = tf.TensorShape(list(p.grid_size) + [3]) - shapes.grid_num_points = tf.TensorShape(list(p.grid_size)) - shapes.laser_grid = tf.TensorShape( - list(p.grid_size) + - [p.num_points_per_cell, 3 + shapes.lasers.points_feature[-1]]) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.grid_centers = tf.float32 - dtypes.grid_num_points = tf.int32 - dtypes.laser_grid = tf.float32 - return dtypes - - -class _PointPillarGridSettings: - """Settings for PointPillars model defined in paper. - - https://arxiv.org/abs/1812.05784 - """ - # Chooses grid sizes that are a multiple of 16 to support point pillars - # model requirements. These also happen to match the values - # in the PointPillars paper (voxel width of 0.16m in x, y) - GRID_X = 432 - GRID_Y = 496 - GRID_Z = 1 - - # These fields are set in the subclasses. - GRID_X_RANGE = None - GRID_Y_RANGE = None - GRID_Z_RANGE = None - - @classmethod - def UpdateGridParams(cls, grid_params): - """Apply PointPillars settings to grid_params.""" - grid_params.grid_size = (cls.GRID_X, cls.GRID_Y, cls.GRID_Z) - grid_params.grid_range_x = cls.GRID_X_RANGE - grid_params.grid_range_y = cls.GRID_Y_RANGE - grid_params.grid_range_z = cls.GRID_Z_RANGE - - @classmethod - def UpdateAnchorGridParams(cls, anchor_params, output_stride=2): - """Apply PointPillars settings to anchor_params.""" - # Set anchor settings to match grid settings. - # Grid size for anchors is half the resolution. - anchor_params.grid_size = (cls.GRID_X // output_stride, - cls.GRID_Y // output_stride, cls.GRID_Z) - anchor_params.grid_range_x = cls.GRID_X_RANGE - anchor_params.grid_range_y = cls.GRID_Y_RANGE - # Grid along z axis should be pinned to 0. - anchor_params.grid_range_z = (0, 0) - - -def MakeGridSettings(grid_x_range, grid_y_range, grid_z_range, grid_x, grid_y, - grid_z): - """Returns configured class for PointPillar grid settings.""" - - class GridSettings(_PointPillarGridSettings): - GRID_X_RANGE = grid_x_range - GRID_Y_RANGE = grid_y_range - GRID_Z_RANGE = grid_z_range - GRID_X = grid_x - GRID_Y = grid_y - GRID_Z = grid_z - - return GridSettings - - -PointPillarGridCarSettings = MakeGridSettings( - grid_x_range=(0, 69.12), - grid_y_range=(-39.68, 39.68), - grid_z_range=(-3, 1), - grid_x=432, - grid_y=496, - grid_z=1) - -PointPillarGridPedCycSettings = MakeGridSettings( - grid_x_range=(0, 47.36), - grid_y_range=(-19.84, 19.84), - grid_z_range=(-2.5, 0.5), - grid_x=432, - grid_y=496, - grid_z=1) - - -class GridToPillars(Preprocessor): - """Create pillars from a grid of points. - - Expects features to have keys: - grid_centers: [gx, gy, gz, 3] - - grid_num_points: [gx, gy, gz] - - laser_grid: [gx, gy, gz, num_points_per_cell, F] - - Adds the following features: - point_count: [num_pillars]. The number of points in the pillar. - - point_locations: [num_pillars, 3]. The grid location of each pillar. - - pillar_points: [num_pillars, num_points_per_cell, F]. Points of each - pillar. - - Drops the following features by default: - laser_grid - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_points_per_cell', 100, - 'The maximum number of points per cell.') - p.Define('num_pillars', 12000, 'The maximum number of pillars to produce.') - p.Define('drop_laser_grid', True, 'Whether to drop the laser_grid feature.') - # The density based sampler is more expensive. - p.Define('use_density_sampler', False, - 'Use a density based sampler during pillar selection.') - return p - - def _GumbelTransform(self, probs): - """Adds gumbel noise to log probabilities for multinomial sampling. - - This enables fast sampling from a multinomial distribution without - replacement. See https://arxiv.org/abs/1611.01144 for details. - A colab that demonstrates this in practice is here: - http://colab/drive/1iuMt2n_r7dKPQG9T0UVMuK3fkbBayKjd - - Args: - probs: A 1-D float tensor containing probabilities, summing to 1. - - Returns: - A 1-D float tensor of the same size of probs, with gumbel noise added to - log probabilities. Taking the top k elements from this provides a - multinomial sample without replacement. - """ - p = self.params - log_prob = tf.math.log(probs) - probs_shape = tf.shape(probs) - uniform_samples = tf.random.uniform( - shape=probs_shape, - dtype=probs.dtype, - seed=p.random_seed, - name='uniform_samples') - gumbel_noise = -tf.math.log(-tf.math.log(uniform_samples)) - return gumbel_noise + log_prob - - def _DensitySample(self, num_points): - p = self.params - - # Flatten to [nx * ny * nz] for convenience during sampling. - num_grid_points = np.prod(p.grid_size) - flattened_num_points = tf.reshape(num_points, [num_grid_points]) - - # Normalize flattened_num_points to sum to 1. - flattened_num_points = tf.cast(flattened_num_points, tf.float32) - flattened_num_points /= tf.reduce_sum(flattened_num_points) - - # TODO(jngiam): Consider generalizing this to enable other methods of - # sampling: e.g., use largest deviation in z-axis. The gumbel transform - # can still be applied regardless. - - # Add gumbel noise for multinomial sampling. - sampling_logits = self._GumbelTransform(flattened_num_points) - _, locations = tf.nn.top_k( - sampling_logits, k=min(p.num_pillars, num_grid_points)) - - # Unravel coordinates back to grid locations. - locations = tf.unravel_index(locations, p.grid_size) - - # Unravel index will return a 3 x num_locations tensor, this needs to be - # transposed so that we have it as num_locations x 3. - locations = py_utils.HasShape(locations, [3, -1]) - locations = tf.transpose(locations) - - return locations - - def TransformFeatures(self, features): - p = self.params - - num_points = features.grid_num_points - if p.use_density_sampler: - locations = self._DensitySample(num_points) - else: - # Select non-empty cells uniformly at random. - locations = tf.random.shuffle(tf.cast(tf.where(num_points > 0), tf.int32)) - - num_features = py_utils.GetShape(features.laser_grid)[-1] - - # [nx, ny, nz, np, 4] (x, y, z, f) - points = features.laser_grid - # [K, np, 4] (x, y, z, f) - points = tf.gather_nd(points, locations) - # [nx, ny, nz, 1, 3] (cx, cy, cz) - centers = features.grid_centers[..., tf.newaxis, :] - # [K, 1, 3] (cx, cy, cz) - centers = tf.gather_nd(centers, locations) - # NOTE: If there are fewer pillars than p.num_pillars, the following - # padding creates many 'fake' pillars at grid cell (0, 0, 0) with - # an all-zero pillar. Hopefully, the model can learn to ignore these. - # - # pillar_points[i, :, :] is the pillar located at pillar_locations[i, :3], - # and pillar_points[i, :, :] == points_grid_full[pillar_locations[i, :3]]. - # for 0 <= i < pillar_count; - # pillar_locations[i, :3] are zero-ed, for i >= pillar_count. - features.pillar_count = tf.shape(locations)[0] - features.pillar_locations = py_utils.PadOrTrimTo(locations, - [p.num_pillars, 3]) - features.pillar_points = py_utils.PadOrTrimTo( - points, [p.num_pillars, p.num_points_per_cell, num_features]) - features.pillar_centers = py_utils.PadOrTrimTo(centers, - [p.num_pillars, 1, 3]) - - if p.drop_laser_grid: - del features['laser_grid'] - - return features - - def TransformShapes(self, shapes): - p = self.params - num_features = shapes.laser_grid[-1] - shapes.pillar_count = tf.TensorShape([]) - shapes.pillar_locations = tf.TensorShape([p.num_pillars, 3]) - shapes.pillar_points = tf.TensorShape( - [p.num_pillars, p.num_points_per_cell, num_features]) - shapes.pillar_centers = tf.TensorShape([p.num_pillars, 1, 3]) - if p.drop_laser_grid: - del shapes['laser_grid'] - return shapes - - def TransformDTypes(self, dtypes): - p = self.params - dtypes.pillar_count = tf.int32 - dtypes.pillar_locations = tf.int32 - dtypes.pillar_points = tf.float32 - dtypes.pillar_centers = tf.float32 - if p.drop_laser_grid: - del dtypes['laser_grid'] - return dtypes - - -class GridAnchorCenters(Preprocessor): - """Create anchor centers on a grid. - - Anchors are placed in the middle of each grid cell. For example, on a 2D grid - range (0 -> 10, 0 -> 10) with a 10 x 5 grid size, the anchors will be placed - at [(0.5, 1), (0.5, 3), ... , (9.5, 7), (9.5, 9)]. - - Adds the following features: - anchor_centers: [num_locations, 3] - Floating point output containing the - center (x, y, z) locations for tiling anchor boxes. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'grid_size', (20, 20, 1), 'Grid size along x,y,z axis. This will ' - 'be used to generate the anchor center locations. Note that this ' - 'would likely be different from the grid_* parameters in ' - 'LaserGridExtractor: the grid extractor may choose to extract ' - 'points more densely. Instead, this should correspond to the ' - 'model\'s prediction layer: the predicted anchor box residuals ' - 'should match this grid.') - p.Define('grid_range_x', (-25, 25), 'The x-axis range covered by the grid.') - p.Define('grid_range_y', (-25, 25), 'The y-axis range covered by the grid.') - p.Define('grid_range_z', (0, 0), 'The z-axis range covered by the grid.') - return p - - def TransformFeatures(self, features): - p = self.params - utils_3d = detection_3d_lib.Utils3D() - - # Compute the grid cell size and adjust the range sent to dense coordinates - # by half a cell size so as to ensure that the anchors are placed in the - # center of each grid cell. - grid_size_x, grid_size_y, grid_size_z = p.grid_size - grid_cell_sizes = [ - float(p.grid_range_x[1] - p.grid_range_x[0]) / grid_size_x, - float(p.grid_range_y[1] - p.grid_range_y[0]) / grid_size_y, - float(p.grid_range_z[1] - p.grid_range_z[0]) / grid_size_z, - ] - half_size_x, half_size_y, half_size_z = np.asarray(grid_cell_sizes) / 2.0 - - grid_shape = list(p.grid_size) + [3] - anchor_centers = utils_3d.CreateDenseCoordinates([ - [ - p.grid_range_x[0] + half_size_x, - p.grid_range_x[1] - half_size_x, - grid_size_x - ], - [ - p.grid_range_y[0] + half_size_y, - p.grid_range_y[1] - half_size_y, - grid_size_y - ], - [ - p.grid_range_z[0] + half_size_z, - p.grid_range_z[1] - half_size_z, - grid_size_z - ], - ]) # pyformat: disable - features.anchor_centers = tf.reshape(anchor_centers, grid_shape) - - return features - - def TransformShapes(self, shapes): - p = self.params - shapes.anchor_centers = tf.TensorShape(list(p.grid_size) + [3]) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.anchor_centers = tf.float32 - return dtypes - - -class SparseCenterSelector(Preprocessor): - """Select centers for anchors and cells. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - and optionally points_padding of shape [P] corresponding to the padding. - if points_padding is None, then all points are considered valid. - - If lasers.num_seeded_points of shape [] is provided, it indicates that the - first num_seeded_points of lasers.points_xyz should be used as seeds for - farthest point sampling (e.g., always chosen). Currently the concept - of seeding is not implemented for anything but farthest point sampling. - - Adds the following features: - anchor_centers: [num_cell_centers, 3] - Floating point output containing the - center (x, y, z) locations for tiling anchor boxes. - cell_center_xyz: [num_cell_centers, 3] - Floating point output containing - the center (x, y, z) locations for each cell to featurize. - """ - - _SAMPLING_METHODS = ['farthest_point', 'random_uniform'] - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_cell_centers', 256, 'Number of centers.') - p.Define( - 'features_preparation_layers', [], - 'A list of Params for layers to run on the features before ' - 'performing farthest point sampling. For example, one may wish to ' - 'drop points out of frustum for KITTI before selecting centers. ' - 'Note that these layers will not mutate the original features, ' - 'instead, a copy will be made.') - p.Define( - 'sampling_method', 'farthest_point', - 'Which sampling method to use. One of {}'.format(cls._SAMPLING_METHODS)) - p.Define( - 'fix_z_to_zero', True, 'Whether to fix z to 0 when retrieving the ' - 'center xyz coordinates.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - if p.sampling_method not in self._SAMPLING_METHODS: - raise ValueError('Param `sampling_method` must be one of {}.'.format( - self._SAMPLING_METHODS)) - if p.features_preparation_layers is not None: - self.CreateChildren('features_preparation_layers', - p.features_preparation_layers) - - def _FarthestPointSampleCenters(self, points_xyz, num_seeded_points): - """Samples centers with Farthest Point Sampling. - - Args: - points_xyz: An unpadded tf.float32 Tensor of shape [P, 3] with per point - (x, y, z) locations. We expect any padded points to be removed before - this function is called. - num_seeded_points: integer indicating how many of the first - num_seeded_points points in points_xyz should be considered - as seeds for FPS (always chosen). - - Returns: - A tf.float32 Tensor of shape [p.num_cell_centers, 3] with selected centers - to use as anchors. - """ - p = self.params - num_points = tf.shape(points_xyz)[0] - points_padding = tf.zeros((num_points,), dtype=tf.float32) - padded_num_points = tf.maximum(num_points, p.num_cell_centers) - - # Pad both the points and padding if for some reason the input pointcloud - # has less points than p.num_cell_centers. - points_xy = py_utils.PadOrTrimTo(points_xyz[:, :2], [padded_num_points, 2]) - points_padding = py_utils.PadOrTrimTo( - points_padding, [padded_num_points], pad_val=1.0) - - sampled_idx, _ = car_lib.FarthestPointSampler( - points_xy[tf.newaxis, ...], - points_padding[tf.newaxis, ...], - p.num_cell_centers, - num_seeded_points=num_seeded_points, - random_seed=p.random_seed) - sampled_idx = sampled_idx[0, :] - - # Gather centers. - if p.fix_z_to_zero: - centers = tf.concat([ - tf.gather(points_xy, sampled_idx), - tf.zeros((p.num_cell_centers, 1)), - ], axis=-1) # pyformat: disable - else: - centers = tf.gather(points_xyz, sampled_idx) - - return centers - - def _RandomUniformSampleCenters(self, points_xyz): - """Samples centers with Random Uniform Sampling. - - Args: - points_xyz: An unpadded tf.float32 Tensor of shape [P, 3] with per point - (x, y, z) locations. We expect any padded points to be removed before - this function is called. - - Returns: - A tf.float32 Tensor of shape [p.num_cell_centers, 3] with selected centers - to use as anchors. - """ - p = self.params - # We want the center Z value to be 0 so just exclude it - centers_xy = tf.random.shuffle(points_xyz[:, :2], seed=p.random_seed) - selected_centers_xy = py_utils.PadOrTrimTo(centers_xy, - [p.num_cell_centers, 2]) - return tf.concat([selected_centers_xy, - tf.zeros((p.num_cell_centers, 1))], - axis=-1) - - def _SampleCenters(self, points_xyz, num_seeded_points): - p = self.params - if p.sampling_method == 'farthest_point': - return self._FarthestPointSampleCenters(points_xyz, num_seeded_points) - elif p.sampling_method == 'random_uniform': - if num_seeded_points > 0: - raise NotImplementedError( - 'Random sampling with seeded points not yet implemented.') - return self._RandomUniformSampleCenters(points_xyz) - else: - raise ValueError('Param `sampling_method` must be one of {}.'.format( - self._SAMPLING_METHODS)) - - def TransformFeatures(self, features): - p = self.params - - prepared_features = features.DeepCopy() - for prep_layer in self.features_preparation_layers: - prepared_features = prep_layer.FPropDefaultTheta(prepared_features) - - num_seeded_points = prepared_features.lasers.get('num_seeded_points', 0) - points_data = prepared_features.lasers - - points_xyz = points_data.points_xyz - if 'points_padding' in points_data: - points_padding = points_data.points_padding - points_mask = 1 - points_padding - points_xyz = tf.boolean_mask(points_xyz, points_mask) - - centers = self._SampleCenters(points_xyz, num_seeded_points) - centers = py_utils.HasShape(centers, [p.num_cell_centers, 3]) - - features.anchor_centers = centers - features.cell_center_xyz = centers - - return features - - def TransformShapes(self, shapes): - p = self.params - shapes.anchor_centers = tf.TensorShape([p.num_cell_centers, 3]) - shapes.cell_center_xyz = tf.TensorShape([p.num_cell_centers, 3]) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.anchor_centers = tf.float32 - dtypes.cell_center_xyz = tf.float32 - return dtypes - - -class SparseCellGatherFeatures(Preprocessor): - """Select local features for each cell. - - This preprocessor expects features to contain: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, F] - - cell_center_xyz of shape [C, 3] - - and optionally points_padding of shape [P] corresponding to the padding. - if points_padding is None, then all points are considered valid. - - Adds the following features: - cell_points_xyz: [num_centers, num_points_per_cell, 3] - Floating point - output containing the (x, y, z) locations for each point for a given - center. - cell_feature: [num_centers, num_points_per_cell, F] - Floating point output - containing the features for each point for a given center. - cell_points_padding: [num_centers, num_points_per_cell] - 0/1 padding - for the points in each cell. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_points_per_cell', 128, 'The number of points per cell.') - p.Define('max_distance', 3.0, 'Max distance of point to cell center.') - p.Define( - 'sample_neighbors_uniformly', False, - 'Whether to sample the neighbor points for every cell center ' - 'uniformly at random. If False, this will default to selecting by ' - 'distance.') - return p - - def TransformFeatures(self, features): - p = self.params - - num_centers = py_utils.GetShape(features.cell_center_xyz, 1)[0] - num_features = py_utils.GetShape(features.lasers.points_feature)[-1] - - points_xyz = features.lasers.points_xyz - points_feature = features.lasers.points_feature - if 'points_padding' in features.lasers: - points_mask = 1 - features.lasers.points_padding - points_xyz = tf.boolean_mask(points_xyz, points_mask) - points_feature = tf.boolean_mask(points_feature, points_mask) - - # Note: points_xyz and points_feature must be unpadded as we pass - # padding=None to neighborhood indices. Ensuring that it is unpadded - # helps improve performance. - - # Get nearby points using kNN. - sample_indices, sample_indices_padding = car_lib.NeighborhoodIndices( - tf.expand_dims(points_xyz, 0), - tf.expand_dims(features.cell_center_xyz, 0), - p.num_points_per_cell, - points_padding=None, - max_distance=p.max_distance, - sample_neighbors_uniformly=p.sample_neighbors_uniformly) - - # Take first example since NeighboorhoodIndices expects batch dimension. - sample_indices = sample_indices[0, :, :] - sample_indices_padding = sample_indices_padding[0, :, :] - - sample_indices = py_utils.HasShape(sample_indices, - [num_centers, p.num_points_per_cell]) - - cell_points_xyz = tf.gather(points_xyz, sample_indices) - cell_points_xyz = py_utils.HasShape(cell_points_xyz, - [num_centers, p.num_points_per_cell, 3]) - - cell_feature = tf.gather(points_feature, sample_indices) - cell_feature = py_utils.HasShape( - cell_feature, [num_centers, p.num_points_per_cell, num_features]) - - cell_points_padding = py_utils.HasShape( - sample_indices_padding, [num_centers, p.num_points_per_cell]) - - features.update({ - 'cell_points_xyz': cell_points_xyz, - 'cell_feature': cell_feature, - 'cell_points_padding': cell_points_padding, - }) - return features - - def TransformShapes(self, shapes): - p = self.params - num_centers = shapes.cell_center_xyz[0] - base_shape = [num_centers, p.num_points_per_cell] - num_features = shapes.lasers.points_feature[-1] - shapes.cell_points_xyz = tf.TensorShape(base_shape + [3]) - shapes.cell_feature = tf.TensorShape(base_shape + [num_features]) - shapes.cell_points_padding = tf.TensorShape(base_shape) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.cell_points_xyz = tf.float32 - dtypes.cell_feature = tf.float32 - dtypes.cell_points_padding = tf.float32 - return dtypes - - -class SparseCellCentersTopK(Preprocessor): - """Given selected centers and gathered points/features, apply a filter. - - This preprocessor expects features to contain `cell_center_xyz` and all - entries in params.features_to_modify, and that the leading dimension should - all be the same (num_cell_centers from SparseCenterSelector). - - We then modify all values in features that are specified in - params.features_to_modify by sorting them with the specified sort function - (specified by params.sort_by) operating on features.cell_center_xyz, and then - taking the top K (specified by params.num_cell_centers) along the first - dimension. - """ - - _REGISTERED_SORT_FUNCTIONS = ['distance'] - - @classmethod - def Params(cls): - p = super().Params() - p.Define('num_cell_centers', 512, 'The number of centers after filtering.') - p.Define( - 'sort_by', 'distance', 'A string specifying which sort function ' - 'to use. Currently we just support `distance`.') - p.Define('features_to_modify', [ - 'cell_center_xyz', 'anchor_centers', 'cell_points_xyz', 'cell_feature', - 'cell_points_padding' - ], 'A list of keys from the features dict to modify.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.sort_by not in self._REGISTERED_SORT_FUNCTIONS: - raise ValueError('{} not supported. We only support {}.'.format( - p.sort_by, self._REGISTERED_SORT_FUNCTIONS)) - if len(p.features_to_modify) < 1: - raise ValueError('Need to modify at least one feature.') - - def _SortByDistance(self, features): - dist = tf.linalg.norm(features.cell_center_xyz, axis=-1) - return tf.argsort(dist, axis=-1, direction='ASCENDING') - - def _Sort(self, features): - p = self.params - if p.sort_by == 'distance': - return self._SortByDistance(features) - else: - raise ValueError('Unsupported sort function: {}.'.format(p.sort_by)) - - def TransformFeatures(self, features): - p = self.params - sort_indices = self._Sort(features) - sort_indices_top_k = sort_indices[:p.num_cell_centers, ...] - - # Gather each of the relevant items - for key in p.features_to_modify: - shape = py_utils.GetShape(features[key]) - output_shape = [p.num_cell_centers] + shape[1:] - features[key] = py_utils.PadOrTrimTo( - tf.gather(features[key], sort_indices_top_k), output_shape) - return features - - def TransformShapes(self, shapes): - p = self.params - for key in p.features_to_modify: - shapes[key] = tf.TensorShape([p.num_cell_centers] + shapes[key][1:]) - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class TileAnchorBBoxes(Preprocessor): - """Creates anchor_bboxes given anchor_centers. - - This preprocessor expects features to contain the following keys: - - anchor_centers of shape [...base shape..., 3] - - Adds the following features: - anchor_bboxes: base_shape + [7] - Floating point anchor box - output containing the anchor boxes and the 7 floating point - values for each box that define the box (x, y, z, dx, dy, dz, phi). - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('anchor_box_dimensions', [], - 'List of anchor box sizes per center.') - p.Define('anchor_box_offsets', [], 'List of anchor box offsets per center.') - p.Define('anchor_box_rotations', [], - 'List of anchor box rotations per center.') - return p - - def TransformFeatures(self, features): - p = self.params - utils_3d = detection_3d_lib.Utils3D() - - assert p.anchor_box_dimensions - assert p.anchor_box_offsets - assert p.anchor_box_rotations - - base_shape = py_utils.GetShape(features.anchor_centers)[:-1] - num_box_per_center = len(p.anchor_box_dimensions) - - anchor_centers = tf.reshape(features.anchor_centers, [-1, 3]) - anchor_bboxes = utils_3d.MakeAnchorBoxes( - anchor_centers, tf.identity(p.anchor_box_dimensions), - tf.identity(p.anchor_box_offsets), tf.identity(p.anchor_box_rotations)) - features.anchor_bboxes = tf.reshape(anchor_bboxes, - base_shape + [num_box_per_center, 7]) - - return features - - def TransformShapes(self, shapes): - p = self.params - base_shape = shapes.anchor_centers[:-1] - num_box_per_center = len(p.anchor_box_dimensions) - shapes.anchor_bboxes = base_shape.concatenate([num_box_per_center, 7]) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.anchor_bboxes = tf.float32 - return dtypes - - -class _AnchorBoxSettings: - """Helper class to parameterize and update anchor box settings.""" - # Implementations should fill out the following class members. - DIMENSION_PRIORS = [] - ROTATIONS = [] - CENTER_X_OFFSETS = [] - CENTER_Y_OFFSETS = [] - CENTER_Z_OFFSETS = [] - - @classmethod - def NumAnchors(cls): - return np.prod([ - len(cls.DIMENSION_PRIORS), - len(cls.ROTATIONS), - len(cls.CENTER_X_OFFSETS), - len(cls.CENTER_Y_OFFSETS), - len(cls.CENTER_Z_OFFSETS) - ]) - - @classmethod - def GenerateAnchorSettings(cls): - """Generate anchor settings. - - Returns: - A `NestedMap` containing three lists of the same length: - - anchor_box_dimensions - - anchor_box_rotations - - anchor_box_offsets - - These can be used with the TileAnchorBBoxes preprocessor. - """ - anchor_box_dimensions = [] - anchor_box_rotations = [] - anchor_box_offsets = [] - - # The following is equivalent to a formulation of itertools.product, but - # is explicitly listed for readability. - - # *Please note*: The ordering is important for ModelV2, which makes - # assumptions that the offset dimensions come first. - for cx in cls.CENTER_X_OFFSETS: - for cy in cls.CENTER_Y_OFFSETS: - for cz in cls.CENTER_Z_OFFSETS: - for rot in cls.ROTATIONS: - for dims in cls.DIMENSION_PRIORS: - anchor_box_dimensions += [dims] - anchor_box_rotations += [rot] - anchor_box_offsets += [(cx, cy, cz)] - - # Check one of the lists has entries. - assert anchor_box_dimensions - - return py_utils.NestedMap( - anchor_box_dimensions=anchor_box_dimensions, - anchor_box_rotations=anchor_box_rotations, - anchor_box_offsets=anchor_box_offsets) - - @classmethod - def Update(cls, params): - """Updates anchor box settings from input configuration lists. - - Given dimensions priors, rotations, and offsets, computes the cartesian - product of the settings. - - Args: - params: The KITTIAnchorExtractorBase.Params() object to update. - - Returns: - Params updated with the anchor settings. - - In total there are N combinations, where each (anchor_box_dimensions[i], - anchor_box_rotations[i], anchor_box_offsets[i]) for i in range(N) is an - option. - """ - p = params - settings = cls.GenerateAnchorSettings() - p.anchor_box_dimensions = settings.anchor_box_dimensions - p.anchor_box_rotations = settings.anchor_box_rotations - p.anchor_box_offsets = settings.anchor_box_offsets - return p - - -def MakeAnchorBoxSettings(dimension_priors, rotations, center_x_offsets, - center_y_offsets, center_z_offsets): - """Returns a configured class for setting anchor box settings.""" - - class CustomAnchorBoxSettings(_AnchorBoxSettings): - DIMENSION_PRIORS = dimension_priors - ROTATIONS = rotations - CENTER_X_OFFSETS = center_x_offsets - CENTER_Y_OFFSETS = center_y_offsets - CENTER_Z_OFFSETS = center_z_offsets - - return CustomAnchorBoxSettings - - -class SparseCarV1AnchorBoxSettings(_AnchorBoxSettings): - """Anchor box settings for training on Cars for Sparse models.""" - # Borrowed from PointPillar dimension prior for cars. - DIMENSION_PRIORS = [(1.6, 3.9, 1.56)] - - # 4 Rotations with axis aligned and both diagonals. - ROTATIONS = [0, np.pi / 2, np.pi / 4, 3 * np.pi / 4] - - # 25 offsets per anchor box with fixed z offset at -1. - CENTER_X_OFFSETS = np.linspace(-1.5, 1.5, 5) - CENTER_Y_OFFSETS = np.linspace(-1.5, 1.5, 5) - CENTER_Z_OFFSETS = [-1.] - - -class PointPillarAnchorBoxSettingsCar(_AnchorBoxSettings): - DIMENSION_PRIORS = [(1.6, 3.9, 1.56)] - ROTATIONS = [0, np.pi / 2] - # Fixed offset for every anchor box, based on a reading of the paper / code - # 0 offsets for x and y, and -1 for z. - CENTER_X_OFFSETS = [0.] - CENTER_Y_OFFSETS = [0.] - CENTER_Z_OFFSETS = [-1.] - - -class PointPillarAnchorBoxSettingsPed(PointPillarAnchorBoxSettingsCar): - DIMENSION_PRIORS = [(0.6, 0.8, 1.73)] - CENTER_Z_OFFSETS = [-0.6] - - -class PointPillarAnchorBoxSettingsCyc(PointPillarAnchorBoxSettingsCar): - DIMENSION_PRIORS = [(0.6, 1.76, 1.73)] - CENTER_Z_OFFSETS = [-0.6] - - -class PointPillarAnchorBoxSettingsPedCyc(PointPillarAnchorBoxSettingsCar): - DIMENSION_PRIORS = [(0.6, 0.8, 1.7), (0.6, 1.76, 1.73)] - CENTER_Z_OFFSETS = [-0.6] - - -class AnchorAssignment(Preprocessor): - """Perform anchor assignment on the features. - - This preprocessor expects features to contain the following keys: - - anchor_bboxes of shape [...base shape..., 7] - - labels.bboxes_3d - - labels.labels - - labels.bboxes_3d_mask - - Adds the following features: - - anchor_localization_residuals: base_shape + [7] floating point tensor of - residuals. The model is expected to regress against these residuals as - targets. The residuals can be converted back into bboxes using - detection_3d_lib.Utils3D.ResidualsToBBoxes. - assigned_gt_idx: base_shape - The corresponding index of the ground - truth bounding box for each anchor box in anchor_bboxes, anchors not - assigned will have idx be set to -1. - assigned_gt_bbox: base_shape + [7] - The corresponding ground - truth bounding box for each anchor box in anchor_bboxes. - assigned_gt_labels: base_shape - The assigned groundtruth label - for each anchor box. - assigned_gt_similarity_score: base_shape - The similarity score - for each assigned anchor box. - assigned_cls_mask: base_shape mask for classification loss per anchor. - This should be 1.0 if the anchor has a foreground or background - assignment; otherwise, it will be assigned to 0.0. - assigned_reg_mask: base_shape mask for regression loss per anchor. - This should be 1.0 if the anchor has a foreground assignment; - otherwise, it will be assigned to 0.0. - Note: background anchors do not have regression targets. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'foreground_assignment_threshold', 0.5, - 'Score (usually IOU) threshold for assigning a box as foreground.') - p.Define( - 'background_assignment_threshold', 0.35, - 'Score (usually IOU) threshold for assigning a box as background.') - return p - - def TransformFeatures(self, features): - p = self.params - utils_3d = detection_3d_lib.Utils3D() - - # anchor_bboxes will be returned with shape [#centers, #boxes_per_center, 7] - # flatten boxes here for matching. - base_shape = py_utils.GetShape(features.anchor_bboxes)[:-1] - anchor_bboxes = tf.reshape(features.anchor_bboxes, [-1, 7]) - - assigned_anchors = utils_3d.AssignAnchors( - anchor_bboxes, - features.labels.bboxes_3d, - features.labels.labels, - features.labels.bboxes_3d_mask, - foreground_assignment_threshold=p.foreground_assignment_threshold, - background_assignment_threshold=p.background_assignment_threshold) - - # Add new features. - features.assigned_gt_idx = tf.reshape(assigned_anchors.assigned_gt_idx, - base_shape) - features.assigned_gt_bbox = tf.reshape(assigned_anchors.assigned_gt_bbox, - base_shape + [7]) - features.assigned_gt_labels = tf.reshape( - assigned_anchors.assigned_gt_labels, base_shape) - features.assigned_gt_similarity_score = tf.reshape( - assigned_anchors.assigned_gt_similarity_score, base_shape) - features.assigned_cls_mask = tf.reshape(assigned_anchors.assigned_cls_mask, - base_shape) - features.assigned_reg_mask = tf.reshape(assigned_anchors.assigned_reg_mask, - base_shape) - - # Compute residuals. - features.anchor_localization_residuals = utils_3d.LocalizationResiduals( - features.anchor_bboxes, features.assigned_gt_bbox) - - return features - - def TransformShapes(self, shapes): - base_shape = shapes.anchor_bboxes[:-1] - box_shape = base_shape.concatenate([7]) - - shapes.anchor_localization_residuals = box_shape - shapes.assigned_gt_idx = base_shape - shapes.assigned_gt_bbox = box_shape - shapes.assigned_gt_labels = base_shape - shapes.assigned_gt_similarity_score = base_shape - shapes.assigned_cls_mask = base_shape - shapes.assigned_reg_mask = base_shape - return shapes - - def TransformDTypes(self, dtypes): - dtypes.anchor_localization_residuals = tf.float32 - dtypes.assigned_gt_idx = tf.int32 - dtypes.assigned_gt_bbox = tf.float32 - dtypes.assigned_gt_labels = tf.int32 - dtypes.assigned_gt_similarity_score = tf.float32 - dtypes.assigned_cls_mask = tf.float32 - dtypes.assigned_reg_mask = tf.float32 - return dtypes - - -class DropLaserPointsOutOfRange(Preprocessor): - """Drops laser points that are out of pre-defined x/y/z ranges. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, F] - - and optionally points_padding of shape [P] corresponding to the padding. - if points_padding is None, then all points are considered valid. - - Modifies the following features: - Removes or sets padding to 1 for all points outside a given range. Modifies - all items in the lasers subdictionary like lasers.points_xyz, - lasers.points_feature, lasers.points_padding, and optionally - lasers.points_label, lasers.points_bbox_id. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('keep_x_range', (-np.inf, np.inf), - 'Only points that have x coordinates within this range are kept.') - p.Define('keep_y_range', (-np.inf, np.inf), - 'Only points that have y coordinates within this range are kept.') - p.Define( - 'keep_z_range', (-np.inf, np.inf), - 'Only points that have z coordinates within this range are kept. ' - 'Approximate ground-removal can be performed by specifying a ' - 'lower-bound on the z-range.') - return p - - def TransformFeatures(self, features): - p = self.params - - points_xyz = features.lasers.points_xyz - if 'points_padding' in features.lasers: - points_mask = tf.cast(1 - features.lasers.points_padding, tf.bool) - else: - # All points are real, we keep points unpadded by applying boolean_mask - # on points_mask later. - points_mask = tf.ones_like(points_xyz[:, 0], dtype=tf.bool) - - min_x, max_x = p.keep_x_range - min_y, max_y = p.keep_y_range - min_z, max_z = p.keep_z_range - - # Short-circuit if all ranges are set to -inf, inf. - if (np.all(np.isneginf([min_x, min_y, min_z])) and - np.all(np.isposinf([max_x, max_y, max_z]))): - return features - - if min_x != -np.inf: - points_mask &= points_xyz[:, 0] >= min_x - if min_y != -np.inf: - points_mask &= points_xyz[:, 1] >= min_y - if min_z != -np.inf: - points_mask &= points_xyz[:, 2] >= min_z - - if max_x != np.inf: - points_mask &= points_xyz[:, 0] <= max_x - if max_y != np.inf: - points_mask &= points_xyz[:, 1] <= max_y - if max_z != np.inf: - points_mask &= points_xyz[:, 2] <= max_z - - if 'points_padding' in features.lasers: - # Suffices to just update the padding. - features.lasers.points_padding = 1. - tf.cast(points_mask, tf.float32) - else: - features.lasers = features.lasers.Transform( - _GetApplyPointMaskFn(points_mask)) - - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class KITTIDropPointsOutOfFrustum(Preprocessor): - """Drops laser points that are outside of the camera frustum. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, F] - - images.velo_to_image_plane of shape [3, 4] - - images.width of shape [1] - - images.height of shape [1] - - and optionally points_padding of shape [P] corresponding to the padding. - if points_padding is None, then all points are considered valid. - - Modifies the following features: - lasers.points_xyz, lasers.points_feature, lasers.points_padding, and - optionally lasers.points_label, lasers.points_bbox_id so that - points outside the frustum have padding set to 1 or are removed. - """ - - def TransformFeatures(self, features): - # Drop points behind the car (behind x-axis = 0). - images = features.images - front_indices = features.lasers.points_xyz[:, 0] >= 0 - - if 'points_padding' not in features.lasers: - # Keep tensors unpadded and small using boolean_mask. - features.lasers.points_xyz = tf.boolean_mask(features.lasers.points_xyz, - front_indices) - features.lasers.points_feature = tf.boolean_mask( - features.lasers.points_feature, front_indices) - - # Drop those points outside the image plane. - points_image = geometry.PointsToImagePlane(features.lasers.points_xyz, - images.velo_to_image_plane) - in_image_plane = ( - (points_image[:, 0] >= 0) & - (points_image[:, 0] <= tf.cast(images.width, tf.float32)) & - (points_image[:, 1] >= 0) & - (points_image[:, 1] <= tf.cast(images.height, tf.float32))) - - if 'points_padding' in features.lasers: - # Update padding to only include front indices and in image plane. - points_mask = tf.cast(1 - features.lasers.points_padding, tf.bool) - points_mask &= front_indices - points_mask &= in_image_plane - features.lasers.points_padding = 1. - tf.cast(points_mask, tf.float32) - else: - features.lasers = features.lasers.Transform( - _GetApplyPointMaskFn(in_image_plane)) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class RandomWorldRotationAboutZAxis(Preprocessor): - """Rotates the world randomly as a form of data augmentation. - - Rotations are performed around the *z-axis*. This assumes that the car is - always level. In general, we'd like to instead rotate the car on the spot, - this would then make sense for cases where the car is on a slope. - - When there are leading dimensions, this will rotate the boxes with the same - transformation across all the frames. This is useful when the input is a - sequence of frames from the same run segment. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [..., 3] - - labels.bboxes_3d of shape [..., 7] - - Modifies the following features: - lasers.points_xyz, labels.bboxes_3d with the same rotation applied to both. - - Adds the following features: - world_rot_z which contains the rotation applied to the example. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'max_rotation', None, - 'The rotation amount will be randomly picked from ' - '[-max_rotation, max_rotation).') - p.Define( - 'include_world_rot_z', True, - 'Whether to include the applied rotation as an additional tensor. ' - 'It can be helpful to disable this when using the preprocessor in a ' - 'way that expects the structure of the features to be the same ' - '(e.g., as a branch in tf.cond).') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.max_rotation is None: - raise ValueError('max_rotation needs to be specified, instead of None.') - - def TransformFeatures(self, features): - p = self.params - rot = tf.random.uniform((), - minval=-p.max_rotation, - maxval=p.max_rotation, - seed=p.random_seed) - - # Rotating about the z-axis is equal to experiencing yaw. - pose = [0., 0., 0., rot, 0., 0.] - - # Rotate points. - features.lasers.points_xyz = geometry.CoordinateTransform( - features.lasers.points_xyz, pose) - - # Rotate bboxes, note that heading has a special case. - bboxes_xyz = features.labels.bboxes_3d[..., :3] - bboxes_dims = features.labels.bboxes_3d[..., 3:6] - bboxes_rot = features.labels.bboxes_3d[..., 6:] - - bboxes_xyz = geometry.CoordinateTransform(bboxes_xyz, pose) - - # The heading correction should subtract rot from the bboxes rotations. - bboxes_rot = geometry.WrapAngleRad(bboxes_rot - rot) - - features.labels.bboxes_3d = tf.concat([bboxes_xyz, bboxes_dims, bboxes_rot], - axis=-1) - if p.include_world_rot_z: - features.world_rot_z = rot - return features - - def TransformShapes(self, shapes): - if self.params.include_world_rot_z: - shapes.world_rot_z = tf.TensorShape([]) - return shapes - - def TransformDTypes(self, dtypes): - if self.params.include_world_rot_z: - dtypes.world_rot_z = tf.float32 - return dtypes - - -class DropPointsOutOfFrustum(Preprocessor): - """Drops points outside of pre-defined theta / phi ranges. - - Note that the ranges for keep_phi_range can be negative, this is because the - phi values wrap around 2*pi. Thus, a valid range that filters the 90 deg - frontal field of view of the car can be specified as [-pi/4, pi/4]. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 7] - - lasers.points_feature of shape [P] - - Modifies the following features: - - lasers.points_xyz removing any points out of frustum. - - lasers.points_feature removing any points out of frustum. - - Note: We expect a downstream processor that filters out boxes with few points - to drop the corresponding bboxes. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('keep_theta_range', (0., np.pi), - 'Only points that have theta coordinates within this range.') - p.Define('keep_phi_range', (0., 2. * np.pi), - 'Only points that have phi coordinates within this range.') - return p - - def TransformFeatures(self, features): - p = self.params - - if 'points_padding' in features.lasers: - raise ValueError('DropPointsOutOfFrustum preprocessor does not support ' - 'padded lasers.') - - points_xyz = features.lasers.points_xyz - points_feature = features.lasers.points_feature - - min_theta, max_theta = p.keep_theta_range - if (min_theta < 0. or min_theta > np.pi or max_theta < 0. or - max_theta > np.pi): - raise ValueError('Valid values for theta are between 0 and pi, ' - 'keep_theta_range={}'.format(p.keep_theta_range)) - - if min_theta > max_theta: - raise ValueError('min_theta must be <= max_theta, ' - 'keep_theta_range={}'.format(p.keep_theta_range)) - - min_phi, max_phi = p.keep_phi_range - if (min_phi < -2. * np.pi or min_phi > 2. * np.pi or - max_phi < -2. * np.pi or max_phi > 2. * np.pi): - raise ValueError('Valid values for phi are between -2*pi and 2*pi,' - 'keep_phi_range={}'.format(p.keep_phi_range)) - - if min_phi > max_phi: - raise ValueError('min_phi must be <= max_phi, ' - 'keep_phi_range={}'.format(p.keep_phi_range)) - - _, theta, phi = tf.unstack( - geometry.SphericalCoordinatesTransform(points_xyz), axis=-1) - - # phi is returned in range [-pi, pi], we shift the values which are between - # [-pi, 0] to be [pi, 2pi] instead to make the logic below easier to follow. - # Hence, all phi values after this will be [0, 2pi]. - phi = tf.where(phi >= 0., phi, 2. * np.pi + phi) - - # Theta does not have circular boundary conditions, a simple check suffices. - points_mask = (theta >= min_theta) & (theta <= max_theta) - - if min_phi < 0. and max_phi < 0.: - # Both are less than zero, we just just add 2pi and will use the regular - # check. - min_phi += 2. * np.pi - max_phi += 2. * np.pi - - if min_phi < 0.: - # The minimum threshold is below 0, so we split into checking between - # (0 to min_phi) and (0 to max_phi). Note that min_phi is negative, but - # phi is always positive, so we take 2*pi + min_phi to get the range of - # appropriate values. - points_mask &= (phi >= (2. * np.pi + min_phi)) | (phi <= max_phi) - else: - # Both must be greater than 0 if we get to this condition. - assert min_phi >= 0. - assert max_phi >= 0. - points_mask &= (phi >= min_phi) & (phi <= max_phi) - - features.lasers.points_xyz = tf.boolean_mask(points_xyz, points_mask) - features.lasers.points_feature = tf.boolean_mask(points_feature, - points_mask) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class DropBoxesOutOfRange(Preprocessor): - """Drops boxes outside of pre-defined x/y/z ranges (boundaries inclusive). - - This preprocessor expects features to contain the following keys: - - labels.bboxes_3d of shape [N, 7] - - labels.bboxes_3d_mask of shape [N] - - Modifies the following features: - - labels.bboxes_3d_mask to mask out any additional boxes. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('keep_x_range', (-np.inf, np.inf), - 'Only boxes that have x coordinates within this range are kept.') - p.Define('keep_y_range', (-np.inf, np.inf), - 'Only boxes that have y coordinates within this range are kept.') - p.Define('keep_z_range', (-np.inf, np.inf), - 'Only boxes that have z coordinates within this range are kept.') - return p - - def TransformFeatures(self, features): - p = self.params - - min_x, max_x = p.keep_x_range - min_y, max_y = p.keep_y_range - min_z, max_z = p.keep_z_range - - # Short-circuit if all ranges are set to -inf, inf. - if (np.all(np.isneginf([min_x, min_y, min_z])) and - np.all(np.isposinf([max_x, max_y, max_z]))): - return features - - # For each bounding box, compute whether any of its extrema - # fall outside of the range. - bboxes_3d_corners = geometry.BBoxCorners( - features.labels.bboxes_3d[tf.newaxis, ...])[0] - bboxes_3d_corners = py_utils.HasShape(bboxes_3d_corners, [-1, 8, 3]) - - min_bbox_x = tf.reduce_min(bboxes_3d_corners[:, :, 0], axis=-1) - max_bbox_x = tf.reduce_max(bboxes_3d_corners[:, :, 0], axis=-1) - - min_bbox_y = tf.reduce_min(bboxes_3d_corners[:, :, 1], axis=-1) - max_bbox_y = tf.reduce_max(bboxes_3d_corners[:, :, 1], axis=-1) - - min_bbox_z = tf.reduce_min(bboxes_3d_corners[:, :, 2], axis=-1) - max_bbox_z = tf.reduce_max(bboxes_3d_corners[:, :, 2], axis=-1) - - mask = ( - tf.math.logical_and(min_bbox_x >= min_x, max_bbox_x <= max_x) - & tf.math.logical_and(min_bbox_y >= min_y, max_bbox_y <= max_y) - & tf.math.logical_and(min_bbox_z >= min_z, max_bbox_z <= max_z)) - - max_num_boxes = py_utils.GetShape(features.labels.bboxes_3d_mask) - mask = py_utils.HasShape(mask, max_num_boxes) - - features.labels.bboxes_3d_mask *= tf.cast(mask, tf.float32) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class PadLaserFeatures(Preprocessor): - """Pads laser features so that the dimensions are fixed. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, F] - - and optionally points_padding of shape [P] corresponding to the padding. - if points_padding is None, then all points are considered valid. - - Modifies the following features: - lasers.points_xyz and lasers.points_feature to add padding. - Optionally also modifies lasers.points_label and lasers.points_bbox_id - if they exist to add padding. - Modifies/adds the following features: - labels.points_padding of shape [P] representing the padding. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('max_num_points', 128500, - 'Max number of points to pad the points to.') - return p - - def TransformFeatures(self, features): - p = self.params - - if 'points_padding' in features.lasers: - points_mask = 1 - features.lasers.points_padding - points_mask = tf.cast(points_mask, tf.bool) - features.lasers = features.lasers.Transform( - _GetApplyPointMaskFn(points_mask)) - - npoints = tf.shape(features.lasers.points_xyz)[0] - features.lasers.points_padding = tf.ones([npoints]) - - shuffled_idx = tf.range(npoints) - shuffled_idx = tf.random.shuffle(shuffled_idx, seed=p.random_seed) - - def _PadOrTrimFn(points_tensor): - # Shuffle before trimming so we have a random sampling - points_tensor = tf.gather(points_tensor, shuffled_idx) - return py_utils.PadOrTrimTo(points_tensor, [p.max_num_points] + - points_tensor.shape[1:].as_list()) - - features.lasers = features.lasers.Transform(_PadOrTrimFn) - features.lasers.points_padding = 1.0 - features.lasers.points_padding - return features - - def TransformShapes(self, shapes): - p = self.params - - def _TransformShape(points_shape): - return tf.TensorShape([p.max_num_points] + points_shape[1:].as_list()) - - shapes.lasers = shapes.lasers.Transform(_TransformShape) - shapes.lasers.points_padding = tf.TensorShape([p.max_num_points]) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.lasers.points_padding = tf.float32 - return dtypes - - -class WorldScaling(Preprocessor): - """Scale the world randomly as a form of data augmentation. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - labels.bboxes_3d of shape [L, 7] - - Modifies the following features: - lasers.points_xyz, labels.bboxes_3d with the same scaling applied to both. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('scaling', None, 'The scaling range.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.scaling is None: - raise ValueError('scaling needs to be specified, instead of None.') - if len(p.scaling) != 2: - raise ValueError('scaling needs to be a list of two elements.') - - def TransformFeatures(self, features): - p = self.params - scaling = tf.random.uniform((), - minval=p.scaling[0], - maxval=p.scaling[1], - seed=p.random_seed, - dtype=features.lasers.points_xyz.dtype) - - # Scale points [num_points, 3]. - features.lasers.points_xyz *= scaling - - # Scaling bboxes (location and dimensions). - bboxes_xyz = features.labels.bboxes_3d[..., :3] * scaling - bboxes_dims = features.labels.bboxes_3d[..., 3:6] * scaling - bboxes_rot = features.labels.bboxes_3d[..., 6:] - - features.labels.bboxes_3d = tf.concat([bboxes_xyz, bboxes_dims, bboxes_rot], - axis=-1) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class RandomDropLaserPoints(Preprocessor): - """Randomly dropout laser points and the corresponding features. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, K] - - - Modifies the following features: - lasers.points_xyz, lasers.points_feature. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('keep_prob', 0.95, 'Probability for keeping points.') - return p - - def TransformFeatures(self, features): - p = self.params - if 'points_padding' in features.lasers: - points_mask = 1 - features.lasers.points_padding - points_xyz = tf.boolean_mask(features.lasers.points_xyz, points_mask) - points_feature = tf.boolean_mask(features.lasers.points_feature, - points_mask) - else: - points_xyz = features.lasers.points_xyz - points_feature = features.lasers.points_feature - - num_points, _ = py_utils.GetShape(features.lasers.points_xyz) - - pts_keep_sample_prob = tf.random.uniform([num_points], - minval=0, - maxval=1, - seed=p.random_seed) - pts_keep_mask = pts_keep_sample_prob < p.keep_prob - - points_xyz = tf.boolean_mask(points_xyz, pts_keep_mask) - points_feature = tf.boolean_mask(points_feature, pts_keep_mask) - - if 'points_padding' in features.lasers: - features.lasers.points_xyz = py_utils.PadOrTrimTo( - points_xyz, tf.shape(features.lasers.points_xyz)) - features.lasers.points_feature = py_utils.PadOrTrimTo( - points_feature, tf.shape(features.lasers.points_feature)) - total_points = tf.shape(points_xyz)[0] - features.lasers.points_padding = 1.0 - py_utils.PadOrTrimTo( - tf.ones([total_points]), tf.shape(features.lasers.points_padding)) - else: - features.lasers.points_xyz = points_xyz - features.lasers.points_feature = points_feature - - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class RandomFlipY(Preprocessor): - """Flip the world along axis Y as a form of data augmentation. - - When there are leading dimensions, this will flip the boxes with the same - transformation across all the frames. This is useful when the input is a - sequence of frames from the same run segment. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [..., 3] - - labels.bboxes_3d of shape [..., 7] - - Modifies the following features: - lasers.points_xyz, labels.bboxes_3d with the same flipping applied to both. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('flip_probability', 0.5, 'Probability of flipping.') - return p - - def TransformFeatures(self, features): - p = self.params - threshold = 1. - p.flip_probability - choice = tf.random.uniform( - (), minval=0.0, maxval=1.0, seed=p.random_seed) >= threshold - - # Flip points - points_xyz = features.lasers.points_xyz - points_y = tf.where(choice, -points_xyz[..., 1:2], points_xyz[..., 1:2]) - features.lasers.points_xyz = tf.concat( - [points_xyz[..., 0:1], points_y, points_xyz[..., 2:3]], axis=-1) - - # Flip boxes - bboxes_xyz = features.labels.bboxes_3d[..., :3] - bboxes_y = tf.where(choice, -bboxes_xyz[..., 1:2], bboxes_xyz[..., 1:2]) - bboxes_xyz = tf.concat( - [bboxes_xyz[..., 0:1], bboxes_y, bboxes_xyz[..., 2:3]], axis=-1) - # Compensate rotation. - bboxes_dims = features.labels.bboxes_3d[..., 3:6] - bboxes_rot = features.labels.bboxes_3d[..., 6:] - bboxes_rot = tf.where(choice, geometry.WrapAngleRad(-bboxes_rot), - bboxes_rot) - features.labels.bboxes_3d = tf.concat([bboxes_xyz, bboxes_dims, bboxes_rot], - axis=-1) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class GlobalTranslateNoise(Preprocessor): - """Add global translation noise of xyz coordinates to points and boxes. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - labels.bboxes_3d of shape [L, 7] - - Modifies the following features: - lasers.points_xyz, labels.bboxes_3d with the same - random translation noise applied to both. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('noise_std', [0.2, 0.2, 0.2], - 'Standard deviation of translation noise per axis.') - return p - - def TransformFeatures(self, features): - p = self.params - # Use three different seeds but the same base seed so - # that the values are different. - base_seed = p.random_seed - x_seed = base_seed - y_seed = None if base_seed is None else base_seed + 1 - z_seed = None if base_seed is None else base_seed + 2 - random_translate_x = tf.random.normal((), - mean=0.0, - stddev=p.noise_std[0], - seed=x_seed) - random_translate_y = tf.random.normal((), - mean=0.0, - stddev=p.noise_std[1], - seed=y_seed) - random_translate_z = tf.random.normal((), - mean=0.0, - stddev=p.noise_std[2], - seed=z_seed) - - pose = tf.stack([ - random_translate_x, random_translate_y, random_translate_z, 0.0, 0.0, - 0.0 - ], - axis=0) - - # Translate points. - points_xyz = features.lasers.points_xyz - features.lasers.points_xyz = geometry.CoordinateTransform(points_xyz, pose) - - # Translate boxes - bboxes_xyz = features.labels.bboxes_3d[..., :3] - bboxes_xyz = geometry.CoordinateTransform(bboxes_xyz, pose) - features.labels.bboxes_3d = tf.concat( - [bboxes_xyz, features.labels.bboxes_3d[..., 3:]], axis=-1) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class RandomBBoxTransform(Preprocessor): - """Randomly transform bounding boxes and the points inside them. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, K] - - lasers.points_padding of shape [P] - - labels.bboxes_3d of shape [L, 7] - - labels.bboxes_3d_mask of shape [L] - - Modifies the following features: - lasers.points_{xyz,feature,padding}, labels.bboxes_3d with the - transformed bounding boxes and points. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'max_rotation', None, - 'The rotation amount will be randomly picked from ' - '[-max_rotation, max_rotation).') - # At the moment we don't use this because it can cause boxes to collide with - # each other. We need to compute box intersections when deciding whether to - # apply the translation jitter. Theoretically we should also do this for - # rotation. - p.Define('noise_std', [0.0, 0.0, 0.0], - 'Standard deviation of translation noise per axis.') - p.Define( - 'max_scaling', None, - 'When max_scaling is not none, delta parameters s_x, s_y, s_z are ' - 'drawn from [-max_scaling[i], max_scaling[i]] where i is in [0, 3].') - p.Define( - 'max_shearing', None, - 'When max_shearing is not none, shearing parameters sh_x^y, sh_x^z, ' - 'sh_y^x, sh_y^z, sh_z^x, sh_z^y are drawn from ' - '[-max_shearing[i], max_shearing[i]], where i is in [0, 5].') - p.Define( - 'max_num_points_per_bbox', 16384, - 'The maximum number of points that fall within a bounding box. ' - 'Bounding boxes with more points than this value will ' - 'have some points droppped.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.max_rotation is None: - raise ValueError('max_rotation needs to be specified, instead of None.') - if p.max_scaling is not None: - if len(p.max_scaling) != 3: - raise ValueError('max_scaling needs to be specified as either None or ' - 'list of 3 floating point numbers, instead of {}.' - ''.format(p.max_scaling)) - if p.max_shearing is not None: - if len(p.max_shearing) != 6: - raise ValueError('max_shearing needs to be specified as either None or ' - 'list of 6 floating point numbers, instead of {}.' - ''.format(p.max_shearing)) - - def _Foreground(self, features, points_xyz, points_feature, real_bboxes_3d, - points_in_bbox_mask, rotation, translate_pose, transform_fn): - """Extract and transform foreground points and features.""" - out_bbox_xyz, out_bbox_feature, out_bbox_mask = self._ForLoopBuffers( - features) - - # Only iterate over the actual number of boxes in the scene. - actual_num_bboxes = tf.reduce_sum( - tf.cast(features.labels.bboxes_3d_mask, tf.int32)) - - ret = py_utils.ForLoop( - body=transform_fn, - start=0, - limit=actual_num_bboxes, - delta=1, - loop_state=py_utils.NestedMap( - points_xyz=points_xyz, - points_feature=points_feature, - bboxes_3d=real_bboxes_3d, - points_in_bbox_mask=points_in_bbox_mask, - rotation=rotation, - translate_pose=translate_pose, - out_bbox_points=out_bbox_xyz, - out_bbox_feature=out_bbox_feature, - out_bbox_mask=out_bbox_mask)) - - # Gather all of the transformed points and features - out_bbox_xyz = tf.reshape(ret.out_bbox_points, [-1, 3]) - num_features = features.lasers.points_feature.shape[-1] - out_bbox_feature = tf.reshape(ret.out_bbox_feature, [-1, num_features]) - out_bbox_mask = tf.cast(tf.reshape(ret.out_bbox_mask, [-1]), tf.bool) - fg_xyz = tf.boolean_mask(out_bbox_xyz, out_bbox_mask) - fg_feature = tf.boolean_mask(out_bbox_feature, out_bbox_mask) - return fg_xyz, fg_feature - - def _Background(self, points_xyz, points_feature, points_in_bbox_mask): - # If a point is in any bounding box, it is a foreground point. - foreground_points_mask = tf.reduce_any(points_in_bbox_mask, axis=-1) - # All others are background. We rotate all of the foreground points to - # final_points_* and keep the background points unchanged - background_points_mask = tf.math.logical_not(foreground_points_mask) - background_points_xyz = tf.boolean_mask(points_xyz, background_points_mask) - background_points_feature = tf.boolean_mask(points_feature, - background_points_mask) - return background_points_xyz, background_points_feature - - def _ForLoopBuffers(self, features): - """Create and return the buffers for the for loop.""" - p = self.params - bboxes_3d = features.labels.bboxes_3d - - # Compute the shapes and create the buffers for the For loop. - max_num_bboxes = tf.shape(bboxes_3d)[0] - per_box_shape = [max_num_bboxes, p.max_num_points_per_bbox, 3] - out_bbox_points = inplace_ops.empty( - per_box_shape, dtype=tf.float32, init=True) - - num_features = features.lasers.points_feature.shape[-1] - bbox_feature_shape = [ - max_num_bboxes, p.max_num_points_per_bbox, num_features - ] - out_bbox_feature = inplace_ops.empty( - bbox_feature_shape, dtype=tf.float32, init=True) - - per_box_mask_shape = [max_num_bboxes, p.max_num_points_per_bbox] - out_bbox_mask = inplace_ops.empty( - per_box_mask_shape, dtype=tf.float32, init=True) - - return out_bbox_points, out_bbox_feature, out_bbox_mask - - def TransformFeatures(self, features): - p = self.params - - num_features = features.lasers.points_feature.shape[-1] - - def Transform(i, state): - """Transform the points in bounding box `i`.""" - state.points_xyz = tf.reshape(state.points_xyz, [-1, 3]) - bbox_mask = tf.reshape(state.points_in_bbox_mask[:, i], [-1]) - - # Fetch only the points in the bounding box. - points_xyz_masked = tf.boolean_mask(state.points_xyz, bbox_mask) - points_feature_masked = tf.boolean_mask(state.points_feature, bbox_mask) - - num_points = tf.shape(points_xyz_masked)[0] - - # TODO(vrv): Fold the following into a single transformation - # matrix. - # - # Translate the box to the origin, then rotate the desired - # rotation angle. - translation_vec = state.bboxes_3d[i, 0:3] - rotation_vec = [state.rotation[i], 0., 0.] - pose = tf.concat([-translation_vec, rotation_vec], axis=0) - points_xyz_adj = geometry.CoordinateTransform(points_xyz_masked, pose) - if p.max_scaling is not None or p.max_shearing is not None: - # Translate the points in the bounding box by moving dz/2 so that the - # bottom of the bounding box is at Z = 0 when any of the two - # (max_scaling or max_shearing) is not None - translation_scale_or_shear = tf.stack( - [0., 0., state.bboxes_3d[i, 5] / 2], axis=0) - pose1 = tf.concat([translation_scale_or_shear, [0., 0., 0.]], axis=0) - points_xyz_adj = geometry.CoordinateTransform(points_xyz_adj, pose1) - else: - translation_scale_or_shear = tf.stack([0., 0., 0.], axis=0) - - if p.max_scaling is not None: - # Perform scaling to the point cloud - # Scaling matrix - # [[s_x+1 0 0] - # [ 0 s_y+1 0] - # [ 0 0 s_z+1]] - sx = tf.random.uniform([], - minval=-p.max_scaling[0], - maxval=p.max_scaling[0], - seed=p.random_seed) - sy = tf.random.uniform([], - minval=-p.max_scaling[1], - maxval=p.max_scaling[1], - seed=p.random_seed) - sz = tf.random.uniform([], - minval=-p.max_scaling[2], - maxval=p.max_scaling[2], - seed=p.random_seed) - scaling_matrix = tf.stack( - [[sx + 1., 0., 0.], [0., sy + 1., 0.], [0., 0., sz + 1.]], axis=0) - - points_xyz_adj = tf.einsum('ij,kj->ki', scaling_matrix, points_xyz_adj) - - if p.max_shearing is not None: - # Perform shearing to the point cloud - # Shearing matrix - # [[1 sh_x^y sh_x^z] - # [sh_y^x 1 sh_y^z] - # [sh_z^x sh_z^y 1 ]] - sxy = tf.random.uniform([], - minval=-p.max_shearing[0], - maxval=p.max_shearing[0], - seed=p.random_seed) - sxz = tf.random.uniform([], - minval=-p.max_shearing[1], - maxval=p.max_shearing[1], - seed=p.random_seed) - syx = tf.random.uniform([], - minval=-p.max_shearing[2], - maxval=p.max_shearing[2], - seed=p.random_seed) - syz = tf.random.uniform([], - minval=-p.max_shearing[3], - maxval=p.max_shearing[3], - seed=p.random_seed) - szx = tf.random.uniform([], - minval=-p.max_shearing[4], - maxval=p.max_shearing[4], - seed=p.random_seed) - szy = tf.random.uniform([], - minval=-p.max_shearing[5], - maxval=p.max_shearing[5], - seed=p.random_seed) - shearing_matrix = tf.stack( - [[1., sxy, sxz], [syx, 1., syz], [szx, szy, 1.]], axis=0) - points_xyz_adj = tf.einsum('ij,kj->ki', shearing_matrix, points_xyz_adj) - - # Translate the points back, adding noise if needed. - translation_with_noise = ( - translation_vec - translation_scale_or_shear + - state.translate_pose[i]) - pose2 = tf.concat([translation_with_noise, [0., 0., 0.]], axis=0) - final_points_xyz = geometry.CoordinateTransform(points_xyz_adj, pose2) - - # final_points_xyz is an [M, 3] Tensor where M is the number of points in - # the box. - points_mask = tf.ones([num_points], dtype=tf.float32) - - final_points_xyz = py_utils.PadOrTrimTo(final_points_xyz, - [p.max_num_points_per_bbox, 3]) - final_points_feature = py_utils.PadOrTrimTo( - points_feature_masked, [p.max_num_points_per_bbox, num_features]) - points_mask = py_utils.PadOrTrimTo(points_mask, - [p.max_num_points_per_bbox]) - state.out_bbox_points = inplace_ops.alias_inplace_update( - state.out_bbox_points, [i], tf.expand_dims(final_points_xyz, 0)) - state.out_bbox_feature = inplace_ops.alias_inplace_update( - state.out_bbox_feature, [i], tf.expand_dims(final_points_feature, 0)) - state.out_bbox_mask = inplace_ops.alias_inplace_update( - state.out_bbox_mask, [i], tf.expand_dims(points_mask, 0)) - - return state - - # Get the points and features that reside in boxes. - if 'points_padding' in features.lasers: - points_mask = 1 - features.lasers.points_padding - points_xyz = tf.boolean_mask(features.lasers.points_xyz, points_mask) - points_feature = tf.boolean_mask(features.lasers.points_feature, - points_mask) - else: - points_xyz = features.lasers.points_xyz - points_feature = features.lasers.points_feature - - # Fetch real bounding boxes and compute point mask. - real_bboxes_3d = tf.boolean_mask(features.labels.bboxes_3d, - features.labels.bboxes_3d_mask) - points_in_bbox_mask = geometry.IsWithinBBox3D(points_xyz, real_bboxes_3d) - - # Choose a random rotation for every real box. - num_boxes = tf.shape(real_bboxes_3d)[0] - rotation = tf.random.uniform([num_boxes], - minval=-p.max_rotation, - maxval=p.max_rotation, - seed=p.random_seed) - - base_seed = p.random_seed - x_seed = base_seed - y_seed = None if base_seed is None else base_seed + 1 - z_seed = None if base_seed is None else base_seed + 2 - random_translate_x = tf.random.normal([num_boxes], - mean=0.0, - stddev=p.noise_std[0], - seed=x_seed) - random_translate_y = tf.random.normal([num_boxes], - mean=0.0, - stddev=p.noise_std[1], - seed=y_seed) - random_translate_z = tf.random.normal([num_boxes], - mean=0.0, - stddev=p.noise_std[2], - seed=z_seed) - - translate_pose = tf.stack( - [random_translate_x, random_translate_y, random_translate_z], axis=1) - - fg_xyz, fg_feature = self._Foreground(features, points_xyz, points_feature, - real_bboxes_3d, points_in_bbox_mask, - rotation, translate_pose, Transform) - - # Concatenate them with the background points and features. - bg_xyz, bg_feature = self._Background(points_xyz, points_feature, - points_in_bbox_mask) - all_points = tf.concat([bg_xyz, fg_xyz], axis=0) - all_features = tf.concat([bg_feature, fg_feature], axis=0) - - # Shuffle the points/features randomly. - all_points, all_features = _ConsistentShuffle((all_points, all_features), - p.random_seed) - - # Padding should technically be unnecessary: the number of points before and - # after should be the same, but in practice we sometimes seem to drop a few - # points, and so we pad to make the shape fixed. - # - # TODO(vrv): Identify the source of this problem and then assert a shape - # matching check. - if 'points_padding' in features.lasers: - features.lasers.points_xyz = py_utils.PadOrTrimTo( - all_points, tf.shape(features.lasers.points_xyz)) - features.lasers.points_feature = py_utils.PadOrTrimTo( - all_features, tf.shape(features.lasers.points_feature)) - total_points = tf.shape(all_points)[0] - features.lasers.points_padding = 1.0 - py_utils.PadOrTrimTo( - tf.ones([total_points]), tf.shape(features.lasers.points_padding)) - else: - features.lasers.points_xyz = all_points - features.lasers.points_feature = all_features - - # Translate noise. - bboxes_xyz = real_bboxes_3d[..., :3] - bboxes_xyz += translate_pose[..., :3] - - bboxes_dim = real_bboxes_3d[..., 3:6] - # Rotate bboxes by their corresponding rotation. - bboxes_rot = real_bboxes_3d[..., 6:] - bboxes_rot -= rotation[:, tf.newaxis] - features.labels.bboxes_3d = py_utils.PadOrTrimTo( - tf.concat([bboxes_xyz, bboxes_dim, bboxes_rot], axis=-1), - tf.shape(features.labels.bboxes_3d)) - features.labels.bboxes_3d_mask = py_utils.PadOrTrimTo( - tf.ones(tf.shape(real_bboxes_3d)[0]), - tf.shape(features.labels.bboxes_3d_mask)) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class GroundTruthAugmentor(Preprocessor): - """Augment bounding box labels and points from a database. - - This preprocessor expects features to contain the following keys: - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, K] - - lasers.points_padding of shape [P] - - labels.bboxes_3d of shape [L, 7] - - labels.bboxes_3d_mask of shape [L] - - labels.labels of shape [L] - - Modifies the above features so that additional objects from - a groundtruth database are added. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'groundtruth_database', None, - 'If not None, loads groundtruths from this database and adds ' - 'them to the current scene. Groundtruth database is expected ' - 'to be a TFRecord of KITTI or Waymo crops.') - p.Define( - 'num_db_objects', None, - 'Number of objects in the database. Because we use TFRecord ' - 'we cannot easily query the number of objects efficiencly.') - p.Define('max_num_points_per_bbox', 2048, - 'Maximum number of points in each bbox to augment with.') - p.Define( - 'filter_min_points', 0, - 'Minimum number of points each database object must have ' - 'to be included in an example.') - p.Define( - 'filter_max_points', None, - 'Maximum number of points each database object must have ' - 'to be included in an example.') - p.Define( - 'difficulty_sampling_probability', None, - 'Probability for sampling ground truth example whose difficulty ' - 'equals {0, 1, 2, 3, ...}. Example: [1.0, 1.0, 1.0, 1.0] for ' - 'uniform sampling 4 different difficulties. Default value is ' - 'None = uniform sampling for all difficulties.') - p.Define( - 'class_sampling_probability', None, - 'Probability for sampling ground truth example based on its class index' - ' Example: For KITTI classes are [Background, Car, Van, Truck, ' - 'Pedestrian, Person_sitting, Cyclist, Tram, Misc, DontCare], using ' - 'probability vector [0., 1.0, 1.0, 0., 0., 0., 0.,0., 0., 0.], we ' - 'uniformly sampling Car and Van. Default value is None: Uses ' - 'label_filter flag and does not sample based on class.') - p.Define('filter_min_difficulty', 0, - 'Filter ground truth boxes whose difficulty is < this value.') - p.Define('max_augmented_bboxes', 15, - 'Maximum number of augmented bounding boxes per scene.') - p.Define( - 'label_filter', [], - 'A list where if specified, only examples of these label integers will ' - 'be included in an example.') - p.Define( - 'batch_mode', False, 'Bool value to control whether the whole' - 'groundtruth database is loaded or partially loaded to save memory' - 'usage. Setting to False loads the whole ground truth database into ' - 'memory. Otherwise, only a fraction of the data will be loaded into ' - 'the memory.') - return p - - def _ReadDB(self, file_patterns): - """Read the groundtruth database and return as a NestedMap of Tensors.""" - p = self.params - - def Process(record): - """Process a groundtruth record.""" - feature_map = { - 'num_points': tf.io.FixedLenFeature((), tf.int64, 0), - 'points': tf.io.VarLenFeature(dtype=tf.float32), - 'points_feature': tf.io.VarLenFeature(dtype=tf.float32), - 'bbox_3d': tf.io.VarLenFeature(dtype=tf.float32), - 'label': tf.io.FixedLenFeature((), tf.int64, 0), - 'difficulty': tf.io.FixedLenFeature((), tf.int64, 0), - 'text': tf.io.VarLenFeature(dtype=tf.string), - } - - example_data = tf.io.parse_single_example(record, feature_map) - num_points = example_data['num_points'] - - points = tf.reshape(_Dense(example_data['points']), [num_points, 3]) - features = tf.reshape( - _Dense(example_data['points_feature']), [num_points, 1]) - points_mask = tf.ones(num_points, dtype=tf.bool) - - # TODO(vrv): Use random selection instead of first N points. - points = py_utils.PadOrTrimTo(points, [p.max_num_points_per_bbox, 3]) - features = py_utils.PadOrTrimTo(features, [p.max_num_points_per_bbox, 1]) - points_mask = py_utils.PadOrTrimTo(points_mask, - [p.max_num_points_per_bbox]) - - bboxes_3d = tf.reshape(_Dense(example_data['bbox_3d']), [7]) - label = tf.cast(example_data['label'], tf.int32) - difficulty = tf.cast(example_data['difficulty'], tf.int32) - return (points, features, points_mask, bboxes_3d, label, difficulty) - - if p.batch_mode: - # Prepare dataset for ground truth bounding boxes. Randomly shuffle the - # file patterns. - file_count = len(tf.io.gfile.glob(file_patterns)) - dataset = tf.stateless_list_files(file_patterns) - dataset = dataset.apply(tf.stateless_cache_dataset()) - dataset = dataset.apply( - tf.stateless_shuffle_dataset( - buffer_size=file_count, reshuffle_each_iteration=True)) - dataset = dataset.interleave( - tf.data.TFRecordDataset, cycle_length=10, num_parallel_calls=10) - dataset = dataset.repeat() - # Only prefetch a few objects from the database to reduce memory - # consumption. - dataset = dataset.map(Process, num_parallel_calls=10) - # We need more bboxes than max_augmented_bboxes in a batch, because some - # of the boxes are filtered out. - dataset = dataset.batch(p.max_augmented_bboxes * 10) - dataset = dataset.apply(tf.stateless_cache_dataset()).prefetch( - p.max_augmented_bboxes * 30) - else: - # Prepare dataset for ground truth bounding boxes. - dataset = tf.stateless_list_files(file_patterns) - dataset = dataset.interleave( - tf.data.TFRecordDataset, cycle_length=10, num_parallel_calls=10) - # Read the entire dataset into memory. - dataset = dataset.take(p.num_db_objects) - dataset = dataset.map(Process, num_parallel_calls=10) - # We batch the output of the dataset into a very large Tensor, then cache - # it in memory. - dataset = dataset.batch(p.num_db_objects) - dataset = dataset.apply(tf.stateless_cache_dataset()).repeat() - - iterator = dataset.make_one_shot_iterator() - input_batch = iterator.get_next() - - (db_points_xyz, db_points_feature, db_points_mask, db_bboxes, db_labels, - db_difficulties) = input_batch - return py_utils.NestedMap( - points_xyz=db_points_xyz, - points_feature=db_points_feature, - points_mask=db_points_mask, - bboxes_3d=db_bboxes, - labels=db_labels, - difficulties=db_difficulties) - - def _CreateExampleFilter(self, db): - """Construct db example filter. - - Args: - db: NestedMap of the following Tensors: points_mask - [N, P] - The points - mask for every object in the database, where N is the number of objects - and P is the maximum number of points per object. labels - [N] - int32 - Label for each object in the database. difficulties - [N] - int32 - Difficulty for each label in the database. - - Returns: - A [N] boolean Tensor for each object in the database, True if - that corresponding object passes the filter. - """ - p = self.params - db_points_mask = db.points_mask - db_label = db.labels - db_difficulty = db.difficulties - - num_objects_in_database = tf.shape(db_points_mask)[0] - - # Filter number of objects. - points_per_object = tf.reduce_sum(tf.cast(db_points_mask, tf.int32), axis=1) - example_filter = points_per_object >= p.filter_min_points - if p.filter_max_points: - example_filter = tf.math.logical_and( - example_filter, points_per_object <= p.filter_max_points) - - if p.difficulty_sampling_probability is not None: - # Sample db based on difficulity of each example. - sampling_prob = p.difficulty_sampling_probability - db_difficulty_probability = tf.zeros_like(db_difficulty, dtype=tf.float32) - for difficulty_idx, difficulty_prob in enumerate(sampling_prob): - db_difficulty_probability += ( - tf.cast(tf.equal(db_difficulty, difficulty_idx), tf.float32) * - difficulty_prob) - - sampled_filter = tf.random.uniform( - tf.shape(example_filter), - minval=0, - maxval=1, - dtype=tf.float32, - seed=p.random_seed) - sampled_filter = sampled_filter < db_difficulty_probability - example_filter &= sampled_filter - else: - # Filter out db examples below min difficulty - example_filter = tf.math.logical_and( - example_filter, db_difficulty >= p.filter_min_difficulty) - - example_filter = tf.reshape(example_filter, [num_objects_in_database]) - db_label = tf.reshape(db_label, [num_objects_in_database]) - if p.class_sampling_probability is not None: - # Sample example based on its class probability. - sampling_prob = p.class_sampling_probability - db_class_probability = tf.zeros_like(db_label, dtype=tf.float32) - - for class_idx, class_prob in enumerate(sampling_prob): - db_class_probability += ( - tf.cast(tf.equal(db_label, class_idx), tf.float32) * class_prob) - - sampled_filter = tf.random.uniform( - tf.shape(example_filter), - minval=0, - maxval=1, - dtype=tf.float32, - seed=p.random_seed) - sampled_filter = sampled_filter < db_class_probability - example_filter &= sampled_filter - elif p.label_filter: - # Filter based on labels. - # Create a label filter where all is false - valid_labels = tf.constant(p.label_filter) - label_mask = tf.reduce_any( - tf.equal(db_label[..., tf.newaxis], valid_labels), axis=1) - example_filter = tf.math.logical_and(example_filter, label_mask) - return example_filter - - # TODO(vrv): Create an overlap filter that also ensures that boxes don't - # overlap with groundtruth points, so that the scenes are more plausible. - def _FilterIndices(self, gt_bboxes_3d, db_bboxes, db_idx): - """Identify database boxes that don't overlap with other boxes.""" - # We accomplish overlap filtering by first computing the pairwise 3D IoU of - # all boxes (concatenated) as a way of computing pairwise box overlaps. - num_gt_bboxes = tf.shape(gt_bboxes_3d)[0] - filtered_bboxes = tf.gather(db_bboxes, db_idx) - all_bboxes = tf.concat([gt_bboxes_3d, filtered_bboxes], axis=0) - pairwise_overlap = ops.pairwise_iou3d(all_bboxes, all_bboxes) - - # We now have an M x M matrix with 1s on the diagonal and non-zero entries - # whenever a box collides with another. - # - # To increase the number of boxes selected, we filter the upper triangular - # entries so that the boxes are chosen greedily: boxes with smaller indices - # will be selected before later boxes, because earlier boxes will not appear - # to collide with later boxes, but later boxes may collide with earlier - # ones. - pairwise_overlap = tf.linalg.band_part(pairwise_overlap, -1, 0) - - # We compute the sum of the IoU overlaps for all database boxes. - db_overlap_sums = tf.reduce_sum(pairwise_overlap[num_gt_bboxes:], axis=1) - - # Those boxes that don't overlap with any other boxes will only have - # a 1.0 IoU with itself. - non_overlapping_boxes = tf.reshape(db_overlap_sums <= 1., [-1]) - - # Filter to select only those object ids that pass this filter. - db_idx = tf.boolean_mask(db_idx, non_overlapping_boxes) - return db_idx - - def TransformFeatures(self, features): - p = self.params - - tf.logging.info('Loading groundtruth database at %s' % - (p.groundtruth_database)) - db = p.groundtruth_database.Instantiate().BuildDataSource(self._ReadDB).data - - original_features_shape = tf.shape(features.lasers.points_feature) - - # Compute the number of bboxes to augment. - num_bboxes_in_scene = tf.reduce_sum( - tf.cast(features.labels.bboxes_3d_mask, tf.int32)) - max_bboxes = tf.shape(features.labels.bboxes_3d_mask)[0] - num_augmented_bboxes = tf.minimum(max_bboxes - num_bboxes_in_scene, - p.max_augmented_bboxes) - - # Compute an object index over all objects in the database. - num_objects_in_database = tf.shape(db.points_xyz)[0] - db_idx = tf.range(num_objects_in_database) - - # Find those indices whose examples pass the filters, and select only those - # indices. - example_filter = self._CreateExampleFilter(db) - db_idx = tf.boolean_mask(db_idx, example_filter) - - # At this point, we might still have a large number of object candidates, - # from which we only need a sample. - # To reduce the amount of computation, we randomly subsample to slightly - # more than we want to augment. - db_idx = tf.random.shuffle( - db_idx, seed=p.random_seed)[0:num_augmented_bboxes * 5] - - # After filtering, further filter out the db boxes that would occlude with - # other boxes (including other database boxes). - # - # Gather the filtered ground truth bounding boxes according to the mask, so - # we can compute overlaps below. - gt_bboxes_3d_mask = tf.cast(features.labels.bboxes_3d_mask, tf.bool) - gt_bboxes_3d = tf.boolean_mask(features.labels.bboxes_3d, gt_bboxes_3d_mask) - gt_bboxes_3d = py_utils.HasShape(gt_bboxes_3d, [num_bboxes_in_scene, 7]) - db_idx = self._FilterIndices(gt_bboxes_3d, db.bboxes_3d, db_idx) - - # From the filtered object ids, select only as many boxes as we need. - shuffled_idx = db_idx[0:num_augmented_bboxes] - num_augmented_bboxes = tf.shape(shuffled_idx)[0] - - # Gather based off the indices. - sampled_points_xyz = tf.gather(db.points_xyz, shuffled_idx) - sampled_points_feature = tf.gather(db.points_feature, shuffled_idx) - sampled_mask = tf.reshape( - tf.gather(db.points_mask, shuffled_idx), - [num_augmented_bboxes, p.max_num_points_per_bbox]) - sampled_bboxes = tf.gather(db.bboxes_3d, shuffled_idx) - sampled_labels = tf.gather(db.labels, shuffled_idx) - - # Mask points/features. - sampled_points_xyz = tf.boolean_mask(sampled_points_xyz, sampled_mask) - sampled_points_feature = tf.boolean_mask(sampled_points_feature, - sampled_mask) - - # Flatten before concatenation with ground truths. - sampled_points_xyz = tf.reshape(sampled_points_xyz, [-1, 3]) - sampled_points_feature = tf.reshape(sampled_points_feature, - [-1, original_features_shape[-1]]) - sampled_bboxes = tf.reshape(sampled_bboxes, [-1, 7]) - - # Concatenate the samples with the ground truths. - if 'points_padding' in features.lasers: - points_mask = tf.cast(1. - features.lasers.points_padding, tf.bool) - # Densify the original points. - dense_points_xyz = tf.boolean_mask(features.lasers.points_xyz, - points_mask) - dense_points_feature = tf.boolean_mask(features.lasers.points_feature, - points_mask) - - # Concatenate the dense original points with our new sampled oints. - points_xyz = tf.concat([dense_points_xyz, sampled_points_xyz], axis=0) - points_feature = tf.concat([dense_points_feature, sampled_points_feature], - axis=0) - original_points_shape = tf.shape(features.lasers.points_xyz) - features.lasers.points_xyz = py_utils.PadOrTrimTo(points_xyz, - original_points_shape) - features.lasers.points_feature = py_utils.PadOrTrimTo( - points_feature, original_features_shape) - # Compute the modified mask / padding. - final_points_mask = py_utils.PadOrTrimTo( - tf.ones(tf.shape(points_xyz)[0]), - tf.shape(features.lasers.points_padding)) - features.lasers.points_padding = 1. - final_points_mask - else: - points_xyz = tf.concat([features.lasers.points_xyz, sampled_points_xyz], - axis=0) - points_feature = tf.concat( - [features.lasers.points_feature, sampled_points_feature], axis=0) - features.lasers.points_xyz = points_xyz - features.lasers.points_feature = points_feature - - # Reconstruct a new, dense, bboxes_3d vector that includes the filtered - # groundtruth bounding boxes followed by the database augmented boxes. - bboxes_3d = tf.concat([gt_bboxes_3d, sampled_bboxes], axis=0) - bboxes_3d = py_utils.PadOrTrimTo(bboxes_3d, [max_bboxes, 7]) - features.labels.bboxes_3d = bboxes_3d - bboxes_3d_mask = tf.ones( - num_bboxes_in_scene + num_augmented_bboxes, dtype=tf.float32) - features.labels.bboxes_3d_mask = py_utils.PadOrTrimTo( - bboxes_3d_mask, [max_bboxes]) - - gt_labels = tf.boolean_mask(features.labels.labels, gt_bboxes_3d_mask) - gt_labels = py_utils.HasShape(gt_labels, [num_bboxes_in_scene]) - - labels = tf.concat([gt_labels, sampled_labels], axis=0) - features.labels.labels = py_utils.PadOrTrimTo(labels, [max_bboxes]) - - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class FrustumDropout(Preprocessor): - """Randomly drops out points in a frustum. - - All points are first converted to spherical coordinates, and then a point - is randomly selected. All points in the frustum around that point within - a given phi, theta angle width and distance to the original greater than - a given value are dropped with probability = 1 - keep_prob. - - Here, we can specify whether the dropped frustum is the union or intersection - of the phi and theta angle filters. - - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, K] - - Optionally points_padding of shape [P] corresponding to the padding. - if points_padding is None, then all points are considered valid. - - Modifies the following features: - lasers.points_xyz, lasers.points_feature, lasers.points_padding with points - randomly dropped out. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('theta_width', 0.03, 'Theta angle width for dropping points.') - p.Define('phi_width', 0.0, 'Phi angle width for dropping points.') - p.Define( - 'distance', 0.0, 'Drop points that have larger distance to the' - 'origin than the value given here.') - p.Define( - 'keep_prob', 0.0, 'keep_prob: 1. = drop no points in the Frustum,' - '0 = drop all points, between 0 and 1 = down sample the points.') - p.Define( - 'drop_type', 'union', 'Drop either the union or intersection of ' - 'phi width and theta width.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.phi_width < 0: - raise ValueError('phi_width must be >= 0, phi_width={}'.format( - p.phi_width)) - if p.theta_width < 0: - raise ValueError('theta_width must be >= 0, theta_width={}'.format( - p.theta_width)) - if p.distance < 0: - raise ValueError('distance must be >= 0, distance={}'.format(p.distance)) - if p.keep_prob < 0 or p.keep_prob > 1: - raise ValueError('keep_prob must be >= 0 and <=1, keep_prob={}'.format( - p.keep_prob)) - if p.drop_type not in ['union', 'intersection']: - raise ValueError('drop_type must be union or intersection ,' - 'drop_type={}'.format(p.drop_type)) - - def TransformFeatures(self, features): - p = self.params - points_xyz = features.lasers.points_xyz - points_feature = features.lasers.points_feature - if 'points_padding' in features.lasers: - points_padding = features.lasers.points_padding - else: - points_padding = None - - if points_padding is not None: - points_mask = tf.cast(1 - points_padding, tf.bool) - num_total_points = py_utils.GetShape(points_mask)[0] - real_points_idx = tf.boolean_mask( - tf.range(0, num_total_points, dtype=tf.int32), points_mask) - num_points = py_utils.GetShape(real_points_idx)[0] - else: - points_mask = tf.ones_like(points_xyz[:, 0], dtype=tf.bool) - num_total_points = py_utils.GetShape(points_mask)[0] - num_points = py_utils.GetShape(points_xyz)[0] - - r, theta, phi = tf.unstack( - geometry.SphericalCoordinatesTransform(points_xyz), axis=-1) - - def _PickRandomPoint(): - point_idx = tf.random.uniform((), - minval=0, - maxval=num_points, - dtype=tf.int32) - if points_padding is not None: - point_idx = real_points_idx[point_idx] - return point_idx - - # Pick a point at random and drop all points that are near that point in the - # frustum for distance larger than r; repeat this for both theta and phi. - if p.theta_width > 0: - theta_half_width = p.theta_width / 2. - point_idx = _PickRandomPoint() - # Points within theta width and further than distance will be dropped. - theta_drop_filter = ((theta < (theta[point_idx] + theta_half_width)) & - (theta > (theta[point_idx] - theta_half_width)) & - (r > p.distance)) - else: - theta_drop_filter = tf.zeros_like(points_mask, dtype=tf.bool) - - if p.phi_width > 0: - phi_half_width = p.phi_width / 2. - point_idx = _PickRandomPoint() - # Points within phi width and further than distance will be dropped. - phi_drop_filter = ((phi < (phi[point_idx] + phi_half_width)) & - (phi > - (phi[point_idx] - phi_half_width)) & (r > p.distance)) - else: - phi_drop_filter = tf.zeros_like(points_mask, dtype=tf.bool) - - # Create drop_filter by combining filters. This contains a filter for the - # points to be removed. One can use the intersection method to limit the - # dropped points be within both phi and theta ranges. - if p.drop_type == 'union': - drop_filter = theta_drop_filter | phi_drop_filter - elif p.drop_type == 'intersection': - drop_filter = theta_drop_filter & phi_drop_filter - - if p.keep_prob == 0: - # Drop all points in drop_filter. - down_sampling_filter = drop_filter - else: - # Randomly drop points in drop_filter based on keep_prob. - sampling_drop_filter = tf.random.uniform([num_total_points], - minval=0, - maxval=1, - dtype=tf.float32) - # Points greater than the threshold (keep_prob) will be dropped. - sampling_drop_filter = sampling_drop_filter > p.keep_prob - - # Instead of dropping all points in the frustum, we drop out points - # that are in the selected frustum (drop_filter). - down_sampling_filter = drop_filter & sampling_drop_filter - - points_mask &= ~down_sampling_filter - - if points_padding is not None: - features.lasers.points_padding = 1 - tf.cast(points_mask, tf.float32) - else: - features.lasers.points_xyz = tf.boolean_mask(points_xyz, points_mask) - features.lasers.points_feature = tf.boolean_mask(points_feature, - points_mask) - - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class RepeatPreprocessor(Preprocessor): - """Repeat a preprocessor multiple times. - - This preprocessor takes a preprocessor as a subprocessor and apply the - subprocessor to features multiple times (repeat_count). - - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('repeat_count', 1, 'Number of times the subprocessor is applied to' - ' features.') - p.Define('subprocessor', None, 'One of the input preprocessors.') - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.subprocessor is None: - raise ValueError('No subprocessor was specified for RepeatPreprocessor.') - if p.repeat_count < 0 or not isinstance(p.repeat_count, int): - raise ValueError( - 'repeat_count must be >= 0 and int, repeat_count={}'.format( - p.repeat_count)) - - self.CreateChild('subprocessor', p.subprocessor) - - def TransformFeatures(self, features): - p = self.params - for _ in range(p.repeat_count): - features = self.subprocessor.FPropDefaultTheta(features) - - return features - - def TransformShapes(self, shapes): - p = self.params - for _ in range(p.repeat_count): - shapes = self.subprocessor.TransformShapes(shapes) - - return shapes - - def TransformDTypes(self, dtypes): - p = self.params - for _ in range(p.repeat_count): - dtypes = self.subprocessor.TransformDTypes(dtypes) - - return dtypes - - -class RandomApplyPreprocessor(Preprocessor): - """Randomly apply a preprocessor with certain probability. - - This preprocessor takes a preprocessor as a subprocessor and apply the - subprocessor to features with certain probability. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('prob', 1.0, 'The probability the subprocessor being executed.') - p.Define('subprocessor', None, 'Params for an input preprocessor.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.subprocessor is None: - raise ValueError('No subprocessor was specified for RepeatPreprocessor.') - if p.prob < 0 or p.prob > 1 or not isinstance(p.prob, float): - raise ValueError( - 'prob must be >= 0 and <=1 and float type, prob={}'.format(p.prob)) - - self.CreateChild('subprocessor', p.subprocessor) - - def TransformFeatures(self, features): - p = self.params - choice = tf.random.uniform( - (), minval=0.0, maxval=1.0, seed=p.random_seed) <= p.prob - # Features is passed downstream and may be modified, we make deep copies - # here to use with tf.cond to avoid having tf.cond access updated - # versions. Note that we need one copy for each branch in case the branches - # further modify features. - features_0, features_1 = features.DeepCopy(), features.DeepCopy() - features = tf.cond(choice, - lambda: self.subprocessor.TransformFeatures(features_0), - lambda: features_1) - return features - - def TransformShapes(self, shapes): - shapes_transformed = self.subprocessor.TransformShapes(shapes) - - if not shapes.IsCompatible(shapes_transformed): - raise ValueError( - 'NestedMap structures are different between shapes and transformed' - 'shapes. Original shapes: {}. Transformed shapes: {}'.format( - shapes, shapes_transformed)) - - def IsCompatibleWith(a, b): - return a.is_compatible_with(b) - - if not all( - py_utils.Flatten( - py_utils.Transform(IsCompatibleWith, shapes, shapes_transformed))): - raise ValueError( - 'Shapes after transformation - {} are different from original ' - 'shapes - {}.'.format(shapes_transformed, shapes)) - - return shapes - - def TransformDTypes(self, dtypes): - transformed_dtypes = self.subprocessor.TransformDTypes(dtypes) - if transformed_dtypes != dtypes: - raise ValueError( - 'DTypes after transformation of preprocessor - {} should be ' - 'the same as {}, but get {}.'.format(self.params.subprocessor, dtypes, - transformed_dtypes)) - return dtypes - - -class SparseSampler(Preprocessor): - """Fused SparseCenterSelector and SparseCellGatherFeatures. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, F] - - Adds the following features: - anchor_centers - [num_centers, 3] - Floating point output containing the - center (x, y, z) locations for tiling anchor boxes. - - cell_center_xyz - [num_centers, 3] - Floating point output containing - the center (x, y, z) locations for each cell to featurize. - - cell_center_padding - [num_centers] - 0/1 padding for each center. - - cell_points_xyz - [num_centers, num_neighbors, 3] - Floating point - output containing the (x, y, z) locations for each point for a given - center. - - cell_feature - [num_centers, num_neighbors, F] - Floating point output - containing the features for each point for a given center. - - cell_points_padding - [num_centers, num_neighbors] - 0/1 padding - for the points in each cell. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('center_selector', 'farthest', 'Method to sample centers. ' - 'Valid options - uniform, farthest.') - p.Define('neighbor_sampler', 'uniform', 'Method to select neighbors. ' - 'Valid options - uniform, closest.') - p.Define('num_centers', 16, 'The number of centers to sample.') - p.Define( - 'features_preparation_layers', [], - 'A list of Params for layers to run on the features before ' - 'performing farthest point sampling. For example, one may wish to ' - 'drop points out of frustum for KITTI before selecting centers. ' - 'Note that these layers will not mutate the original features, ' - 'instead, a copy will be made.') - p.Define( - 'keep_z_range', (-np.inf, np.inf), - 'Only points that have z coordinates within this range are kept. ' - 'Approximate ground-removal can be performed by specifying a ' - 'lower-bound on the z-range.') - p.Define('num_neighbors', 64, 'Sample these many points within the ' - 'neighorhood.') - p.Define( - 'max_distance', 1.0, 'Points with L2 distances from a center ' - 'larger than this threshold are not considered to be in the ' - 'neighborhood.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.features_preparation_layers: - self.CreateChildren('features_preparation_layers', - p.features_preparation_layers) - - def TransformFeatures(self, features): - p = self.params - n, m = p.num_centers, p.num_neighbors - - prepared_features = features.DeepCopy() - if p.features_preparation_layers: - for prep_layer in self.features_preparation_layers: - prepared_features = prep_layer.FPropDefaultTheta(prepared_features) - - points_data = prepared_features.lasers - points = py_utils.HasShape(points_data.points_xyz, [-1, 3]) - - if 'points_padding' in points_data: - points_mask = 1 - points_data.points_padding - points = tf.boolean_mask(points, points_mask) - - # If num_points < num_centers, pad points to have at least num_centers - # points. - num_points = tf.shape(points)[0] - required_num_points = tf.maximum(num_points, p.num_centers) - zeros = tf.zeros([required_num_points - num_points, 3]) - points = tf.concat([points, zeros], axis=0) - - num_seeded_points = points_data.get('num_seeded_points', 0) - - neighbor_algorithm = 'auto' - # Based on benchmarks, the hash solution works better when the number of - # centers is >= 16 and there are at least 10k points per point cloud. - if p.num_centers >= 16: - neighbor_algorithm = 'hash' - - centers, center_paddings, indices, indices_paddings = ops.sample_points( - points=tf.expand_dims(points, 0), - points_padding=tf.zeros([1, required_num_points], tf.float32), - num_seeded_points=num_seeded_points, - center_selector=p.center_selector, - neighbor_sampler=p.neighbor_sampler, - neighbor_algorithm=neighbor_algorithm, - num_centers=p.num_centers, - center_z_min=p.keep_z_range[0], - center_z_max=p.keep_z_range[1], - num_neighbors=p.num_neighbors, - max_distance=p.max_distance, - random_seed=p.random_seed if p.random_seed else -1) - centers = py_utils.HasShape(centers, [1, n])[0, :] - center_paddings = py_utils.HasShape(center_paddings, [1, n])[0, :] - indices = py_utils.HasShape(indices, [1, n, m])[0, :] - indices_paddings = py_utils.HasShape(indices_paddings, [1, n, m])[0, :] - features.cell_center_padding = center_paddings - features.cell_center_xyz = py_utils.HasShape( - tf.gather(points, centers), [n, 3]) - features.anchor_centers = features.cell_center_xyz - features.cell_points_xyz = py_utils.HasShape( - tf.gather(points, indices), [n, m, 3]) - features.cell_feature = tf.gather(points_data.points_feature, indices) - features.cell_points_padding = indices_paddings - return features - - def TransformShapes(self, shapes): - p = self.params - n, m, f = p.num_centers, p.num_neighbors, shapes.lasers.points_feature[-1] - shapes.anchor_centers = tf.TensorShape([n, 3]) - shapes.cell_center_padding = tf.TensorShape([n]) - shapes.cell_center_xyz = tf.TensorShape([n, 3]) - shapes.cell_points_xyz = tf.TensorShape([n, m, 3]) - shapes.cell_feature = tf.TensorShape([n, m, f]) - shapes.cell_points_padding = tf.TensorShape([n, m]) - return shapes - - def TransformDTypes(self, dtypes): - dtypes.anchor_centers = tf.float32 - dtypes.cell_center_padding = tf.float32 - dtypes.cell_center_xyz = tf.float32 - dtypes.cell_points_xyz = tf.float32 - dtypes.cell_feature = tf.float32 - dtypes.cell_points_padding = tf.float32 - return dtypes diff --git a/lingvo/tasks/car/kitti_ap_metric.py b/lingvo/tasks/car/kitti_ap_metric.py deleted file mode 100644 index 8df5d500..00000000 --- a/lingvo/tasks/car/kitti_ap_metric.py +++ /dev/null @@ -1,255 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Average Precision metric class for KITTI.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.tasks.car import ap_metric -from lingvo.tasks.car import ops -import numpy as np - - -class KITTIAPMetrics(ap_metric.APMetrics): - """The KITTI implementation of AP metric.""" - - def _GetData(self, - classid, - difficulty=None, - distance=None, - num_points=None, - rotation=None): - """Returns groundtruth and prediction for the classid in a NestedMap. - - Args: - classid: int32 specifying the class - difficulty: String in [easy, moderate, hard]. If None specified, all - difficulty levels are permitted. - distance: int32 specifying a binned Euclidean distance of the ground truth - bounding box. If None is specified, all distances are selected. - num_points: int32 specifying a binned number of laser points within the - ground truth bounding box. If None is specified, all boxes are selected. - rotation: int32 specifying a binned rotation within the ground truth - bounding box. If None is specified, all boxes are selected. - - Returns: - NestedMap containing iou_threshold, groundtruth and predictions for - specified, classid, difficulty level and binned distance. If no bboxes - are found with these parameters, returns None. - """ - assert classid > 0 and classid < self.metadata.NumClasses() - g = self._LoadBoundingBoxes( - 'groundtruth', - classid, - distance=distance, - num_points=num_points, - rotation=rotation) - # Note that we do not specify num_points for predictions because only - # groundtruth boxes contain points. - p = self._LoadBoundingBoxes( - 'prediction', classid, distance, num_points=None, rotation=rotation) - if g is None or p is None: - return None - - # KITTI ignores detections that are too small. - min_height = 0 - if difficulty: - min_height = self.metadata.MinHeight2D()[difficulty] - pd_ignore = p.heights_in_pixels < min_height - # We first fetch the ground truth boxes for the specified class id - # into gt_boxes. - gt_boxes = g.boxes - gt_imgids = g.imgids - # Ignore bboxes that are more difficult than this levels by setting their - # gt_ignore to 1 (IgnoreOneMatch). - if difficulty: - gt_ignore = g.difficulties < self.metadata.DifficultyLevels()[difficulty] - gt_ignore = gt_ignore.astype(np.int32) - else: - gt_ignore = np.zeros_like(g.difficulties, dtype=np.int32) - - # Extract the bounding boxes from the groundtruth for 'similar' - # categories. Add these to the list of ground truth bounding boxes - # with an ignore setting of 1 (IgnoreOneMatch). - if classid in self.metadata.IgnoreClassIndices(): - for class_id_to_ignore in self.metadata.IgnoreClassIndices()[classid]: - g_ignore = self._LoadBoundingBoxes( - 'groundtruth', class_id=class_id_to_ignore) - - if g_ignore is not None: - n_ignore_boxes = g_ignore.boxes.shape[0] - gt_imgids = np.concatenate([gt_imgids, g_ignore.imgids]) - gt_boxes = np.concatenate([gt_boxes, g_ignore.boxes]) - gt_ignore = np.concatenate([gt_ignore, np.ones(n_ignore_boxes)]) - - # Extract the DontCare bounding boxes from the data, and add - # these to the list of bounding boxes to evaluate with an ignore - # setting of 2 (IgnoreAllMatches). Only relevant to KITTI. - if 'DontCare' in self.metadata.ClassNames(): - g_ignore = self._LoadBoundingBoxes( - 'groundtruth', class_id=self.metadata.ClassNames().index('DontCare')) - if g_ignore is not None: - n_ignore_boxes = g_ignore.boxes.shape[0] - gt_imgids = np.concatenate([gt_imgids, g_ignore.imgids]) - gt_boxes = np.concatenate([gt_boxes, g_ignore.boxes]) - gt_ignore = np.concatenate([gt_ignore, 2 * np.ones(n_ignore_boxes)]) - - iou_threshold = self._iou_thresholds[self.metadata.ClassNames()[classid]] - return py_utils.NestedMap( - iou_threshold=iou_threshold, - gt=py_utils.NestedMap(imgid=gt_imgids, bbox=gt_boxes, ignore=gt_ignore), - pd=py_utils.NestedMap( - imgid=p.imgids, bbox=p.boxes, score=p.scores, ignore=pd_ignore)) - - def _BuildMetric(self, feed_data, classid): - """Construct tensors and the feed_dict for KITTI metric op. - - Args: - feed_data: a NestedMap returned by _GetData() - classid: integer. Unused in this implementation. - - Returns: - A tuple of 3 dicts: - - - scalar_metrics: a dict mapping all the metric names to fetch tensors. - - curves: a dict mapping all the curve names to fetch tensors. - - feed_dict: a dict mapping the tensors in feed_tensors to feed values. - """ - if feed_data is None: - dummy_scalar = tf.constant(np.nan) - dummy_calibration = tf.constant(np.nan) - dummy_curve = tf.zeros([self.metadata.NumberOfPrecisionRecallPoints(), 2], - tf.float32) - scalar_metrics = {'ap': dummy_scalar} - curve_metrics = {'pr': dummy_curve} - calibration_metrics = {'calibrations': dummy_calibration} - - return py_utils.NestedMap( - feed_dict={}, - scalar_metrics=scalar_metrics, - curve_metrics=curve_metrics, - calibration_metrics=calibration_metrics) - - feed_dict = {} - - f_iou = tf.placeholder(tf.float32) - feed_dict[f_iou] = feed_data.iou_threshold - - f_gt_bbox = tf.placeholder(tf.float32) - feed_dict[f_gt_bbox] = feed_data.gt.bbox - - f_gt_imgid = tf.placeholder(tf.int32) - feed_dict[f_gt_imgid] = feed_data.gt.imgid - - f_gt_ignore = tf.placeholder(tf.int32) - feed_dict[f_gt_ignore] = feed_data.gt.ignore - - f_pd_bbox = tf.placeholder(tf.float32) - feed_dict[f_pd_bbox] = feed_data.pd.bbox - - f_pd_imgid = tf.placeholder(tf.int32) - feed_dict[f_pd_imgid] = feed_data.pd.imgid - - f_pd_ignore = tf.placeholder(tf.int32) - feed_dict[f_pd_ignore] = feed_data.pd.ignore - - f_pd_score = tf.placeholder(tf.float32) - feed_dict[f_pd_score] = feed_data.pd.score - - # TODO(shlens): The third returned argument contain statistics for measuring - # the calibration error. Use it. - ap, pr, calibration = ops.average_precision3d( - iou_threshold=f_iou, - groundtruth_bbox=f_gt_bbox, - groundtruth_imageid=f_gt_imgid, - groundtruth_ignore=f_gt_ignore, - prediction_bbox=f_pd_bbox, - prediction_imageid=f_pd_imgid, - prediction_ignore=f_pd_ignore, - prediction_score=f_pd_score, - num_recall_points=self.metadata.NumberOfPrecisionRecallPoints()) - - scalar_metrics = {'ap': ap} - curve_metrics = {'pr': pr} - calibration_metrics = {'calibrations': calibration} - return py_utils.NestedMap( - feed_dict=feed_dict, - scalar_metrics=scalar_metrics, - curve_metrics=curve_metrics, - calibration_metrics=calibration_metrics) - - def _ComputeFinalMetrics(self, - classids=None, - difficulty=None, - distance=None, - num_points=None, - rotation=None): - """Compute precision-recall curves as well as average precision. - - Args: - classids: A list of N int32. - difficulty: String in [easy, moderate, hard]. If None specified, all - difficulty levels are permitted. - distance: int32 specifying a binned Euclidean distance of the ground truth - bounding box. If None is specified, all distances are selected. - num_points: int32 specifying a binned number of laser points within the - ground truth bounding box. If None is specified, all boxes are selected. - rotation: int32 specifying a binned rotation within the ground truth - bounding box. If None is specified, all boxes are selected. - - Returns: - dict. Each entry in the dict is a list of C (number of classes) dicts - containing mapping from metric names to individual results. Individual - entries may be the following items. - - scalars: A list of C (number of classes) dicts mapping metric - names to scalar values. - - curves: A list of C dicts mapping metrics names to np.float32 - arrays of shape [NumberOfPrecisionRecallPoints()+1, 2]. In the last - dimension, 0 indexes precision and 1 indexes recall. - - calibrations: A list of C dicts mapping metrics names to np.float32 - arrays of shape [number of predictions, 2]. The first column is the - predicted probabilty and the second column is 0 or 1 indicating that the - prediction matched a ground truth item. - """ - tf.logging.info('Computing final KITTI metrics.') - assert classids is not None, 'classids must be supplied.' - feed_dict = {} - g = tf.Graph() - scalar_fetches = [] - curve_fetches = [] - calibration_fetches = [] - with g.as_default(): - for classid in classids: - data = self._GetData( - classid, - difficulty=difficulty, - distance=distance, - num_points=num_points, - rotation=rotation) - metrics = self._BuildMetric(data, classid) - scalar_fetches += [metrics.scalar_metrics] - curve_fetches += [metrics.curve_metrics] - calibration_fetches += [metrics.calibration_metrics] - feed_dict.update(metrics.feed_dict) - - with tf.Session(graph=g) as sess: - results = sess.run([scalar_fetches, curve_fetches, calibration_fetches], - feed_dict=feed_dict) - tf.logging.info('Finished computing final KITTI metrics.') - return { - 'scalars': results[0], - 'curves': results[1], - 'calibrations': results[2] - } diff --git a/lingvo/tasks/car/kitti_decoder.py b/lingvo/tasks/car/kitti_decoder.py deleted file mode 100644 index 01caf1ae..00000000 --- a/lingvo/tasks/car/kitti_decoder.py +++ /dev/null @@ -1,389 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Base models for point-cloud based detection.""" - -from lingvo import compat as tf -from lingvo.core import metrics -from lingvo.core import py_utils -from lingvo.tasks.car import base_decoder -from lingvo.tasks.car import detection_3d_lib -from lingvo.tasks.car import detection_3d_metrics -from lingvo.tasks.car import geometry -from lingvo.tasks.car import kitti_ap_metric -from lingvo.tasks.car import kitti_metadata -from lingvo.tasks.car import transform_util -import numpy as np - - -class KITTIDecoder(base_decoder.BaseDecoder): - """A decoder to use for decoding a detector model on KITTI. - - This class implements the basic Decoder metrics for KITTI to provide - visualizations and AP calculations. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'filter_predictions_outside_frustum', False, - 'If true, predictions whose bounding box center is outside of the ' - 'image frustum are dropped.') - p.Define( - 'truncation_threshold', 0.0, - 'Specifies how much of a bounding box can be truncated ' - 'by the edge of the image frustum and still be kept. A value of 0.0 ' - 'means that we only drop predictions whose 2d bounding box ' - 'falls entirely outside the image frustum. A value of 1.0 means ' - 'we drop predictions where *any* portion of the bounding box falls ' - 'outside the frustum.') - p.ap_metric = kitti_ap_metric.KITTIAPMetrics.Params( - kitti_metadata.KITTIMetadata()) - return p - - def CreateDecoderMetrics(self): - """Decoder metrics for KITTI.""" - p = self.params - - kitti_metric_p = p.ap_metric.Copy().Set(cls=kitti_ap_metric.KITTIAPMetrics) - apm = kitti_metric_p.Instantiate() - class_names = apm.metadata.ClassNames() - - # Convert the list of class names to a dictionary mapping class_id -> name. - class_id_to_name = dict(enumerate(class_names)) - - top_down_transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=32., - image_ref_x=512., - image_ref_y=1408., - flip_axes=True) - - decoder_metrics = py_utils.NestedMap({ - 'top_down_visualization': - (detection_3d_metrics.TopDownVisualizationMetric( - top_down_transform, - image_height=1536, - image_width=1024, - class_id_to_name=class_id_to_name)), - 'num_samples_in_batch': metrics.AverageMetric(), - 'kitti_AP_v2': apm, - }) - - decoder_metrics.mesh = detection_3d_metrics.WorldViewer() - - if p.summarize_boxes_on_image: - decoder_metrics.camera_visualization = ( - detection_3d_metrics.CameraVisualization( - bbox_score_threshold=p.visualization_classification_threshold)) - - return decoder_metrics - - def _CreateFrustumMask(self, bbox_corners_image, bbox2d_corners_image_clipped, - image_height, image_width): - """Creates a box mask for boxes whose projections fall outside of image.""" - p = self.params - batch_size, num_boxes = py_utils.GetShape(bbox_corners_image, 2) - if not p.filter_predictions_outside_frustum: - return tf.ones(shape=(batch_size, num_boxes), dtype=tf.float32) - - def _MinMax(bbox_corners): - """Computes the min and max over corners.""" - bbox_min = tf.reduce_min(bbox_corners, axis=-1) - bbox_max = tf.reduce_max(bbox_corners, axis=-1) - bbox_min = py_utils.HasShape(bbox_min, [batch_size, num_boxes]) - bbox_max = py_utils.HasShape(bbox_max, [batch_size, num_boxes]) - return bbox_min, bbox_max - - bbox_min_x, bbox_max_x = _MinMax(bbox_corners_image[:, :, :, 0]) - bbox_min_y, bbox_max_y = _MinMax(bbox_corners_image[:, :, :, 1]) - - # Compute the fraction of the clipped 2d image projection and the - # full 2d image projection. We simply need to divide the area - # of each cropped box by the area of the full box to get the - # overlap fraction. - original_area = (bbox_max_x - bbox_min_x) * (bbox_max_y - bbox_min_y) - bbox_clipped_x_min = bbox2d_corners_image_clipped[..., 0] - bbox_clipped_y_min = bbox2d_corners_image_clipped[..., 1] - bbox_clipped_x_max = bbox2d_corners_image_clipped[..., 2] - bbox_clipped_y_max = bbox2d_corners_image_clipped[..., 3] - clipped_area = (bbox_clipped_x_max - bbox_clipped_x_min) * ( - bbox_clipped_y_max - bbox_clipped_y_min) - fraction = clipped_area / original_area - - frustum_mask = (fraction > p.truncation_threshold) - frustum_mask = py_utils.HasShape(frustum_mask, [batch_size, num_boxes]) - frustum_mask = tf.cast(frustum_mask, tf.float32) - return frustum_mask - - def _BBox2DImage(self, bbox_corners_image, input_images): - """Compute [xmin, ymin, xmax, ymax] 2D bounding boxes from corners.""" - # Clip the boundaries of the bounding box to the image width/height. - bci_x = bbox_corners_image[..., 0:1] - image_width = tf.broadcast_to( - input_images.width[..., tf.newaxis, tf.newaxis], tf.shape(bci_x)) - bci_x = tf.clip_by_value(bci_x, 0.0, tf.cast(image_width, tf.float32)) - - bci_y = bbox_corners_image[..., 1:2] - image_height = tf.broadcast_to( - input_images.height[..., tf.newaxis, tf.newaxis], tf.shape(bci_y)) - bci_y = tf.clip_by_value(bci_y, 0.0, tf.cast(image_height, tf.float32)) - - bbox_corners_image_clipped = tf.concat([bci_x, bci_y], axis=-1) - - # Compute the [xmin, ymin, xmax, ymax] bounding boxes from [batch, - # num_boxes, 8, 2] extrema. - min_vals = tf.math.reduce_min(bbox_corners_image_clipped, axis=2) - max_vals = tf.math.reduce_max(bbox_corners_image_clipped, axis=2) - bbox2d_corners_image = tf.concat([min_vals, max_vals], axis=2) - return bbox2d_corners_image - - def ProcessOutputs(self, input_batch, model_outputs): - """Produce additional decoder outputs for KITTI. - - Args: - input_batch: A .NestedMap of the inputs to the model. - model_outputs: A .NestedMap of the outputs of the model, including:: - - per_class_predicted_bboxes: [batch, num_classes, num_boxes, 7] float - Tensor with per class 3D (7 DOF) bounding boxes. - - per_class_predicted_bbox_scores: [batch, num_classes, num_boxes] float - Tensor with per class, per box scores. - - per_class_valid_mask: [batch, num_classes, num_boxes] masking Tensor - indicating which boxes were still kept after NMS for each class. - - Returns: - A NestedMap of additional decoder outputs needed for - PostProcessDecodeOut. - """ - p = self.params - per_class_predicted_bboxes = model_outputs.per_class_predicted_bboxes - batch_size, num_classes, num_boxes, _ = py_utils.GetShape( - per_class_predicted_bboxes) - flattened_num_boxes = num_classes * num_boxes - - input_labels = input_batch.decoder_copy.labels - input_lasers = input_batch.decoder_copy.lasers - input_images = input_batch.decoder_copy.images - - with tf.device('/cpu:0'): - # Convert the predicted bounding box points to their corners - # and then project them to the image plane. - # - # This output can be used to: - # - # A) Visualize bounding boxes (2d or 3d) on the camera image. - # - # B) Compute the height of the predicted boxes to filter 'too small' boxes - # as is done in the KITTI eval. - predicted_bboxes = tf.reshape(per_class_predicted_bboxes, - [batch_size, flattened_num_boxes, 7]) - bbox_corners = geometry.BBoxCorners(predicted_bboxes) - bbox_corners = py_utils.HasShape(bbox_corners, - [batch_size, flattened_num_boxes, 8, 3]) - utils_3d = detection_3d_lib.Utils3D() - bbox_corners_image = utils_3d.CornersToImagePlane( - bbox_corners, input_images.velo_to_image_plane) - bbox_corners_image = py_utils.HasShape( - bbox_corners_image, [batch_size, flattened_num_boxes, 8, 2]) - - # Clip the bounding box corners so they remain within - # the image coordinates. - bbox2d_corners_image_clipped = self._BBox2DImage(bbox_corners_image, - input_images) - bbox2d_corners_image_clipped = py_utils.HasShape( - bbox2d_corners_image_clipped, [batch_size, flattened_num_boxes, 4]) - - # Compute the frustum mask to filter out bounding boxes that - # are 'outside the frustum'. - frustum_mask = self._CreateFrustumMask(bbox_corners_image, - bbox2d_corners_image_clipped, - input_images.height, - input_images.width) - - # Reshape all of these back to [batch_size, num_classes, num_boxes, ...] - bbox_corners_image = tf.reshape( - bbox_corners_image, [batch_size, num_classes, num_boxes, 8, 2]) - - bbox2d_corners_image_clipped = tf.reshape( - bbox2d_corners_image_clipped, [batch_size, num_classes, num_boxes, 4]) - frustum_mask = tf.reshape(frustum_mask, - [batch_size, num_classes, num_boxes]) - - ret = py_utils.NestedMap({ - # For mAP eval - 'source_ids': input_labels.source_id, - 'difficulties': input_labels.difficulties, - 'num_points_in_bboxes': input_batch.labels.bboxes_3d_num_points, - # For exporting. - 'velo_to_image_plane': input_images.velo_to_image_plane, - 'velo_to_camera': input_images.velo_to_camera, - # Predictions. - 'bbox_corners_image': bbox_corners_image, - 'bbox2d_corners_image': bbox2d_corners_image_clipped, - 'frustum_mask': frustum_mask, - # Ground truth. - 'bboxes_3d': input_labels.bboxes_3d, - 'bboxes_3d_mask': input_labels.bboxes_3d_mask, - 'unfiltered_bboxes_3d_mask': input_labels.unfiltered_bboxes_3d_mask, - 'labels': input_labels.labels, - }) - - laser_sample = self._SampleLaserForVisualization( - input_lasers.points_xyz, input_lasers.points_padding) - ret.update(laser_sample) - - if p.summarize_boxes_on_image: - ret.camera_images = input_images.image - return ret - - def PostProcessDecodeOut(self, dec_out_dict, dec_metrics_dict): - """Post-processes the decoder outputs.""" - p = self.params - # Update num_samples_in_batch. - batch_size, num_classes, num_boxes, _ = ( - dec_out_dict.per_class_predicted_bboxes.shape) - dec_metrics_dict.num_samples_in_batch.Update(batch_size) - - # Apply frustum mask to predicted box outputs. - masked_pred_bbox_scores = ( - dec_out_dict.per_class_predicted_bbox_scores * - dec_out_dict.frustum_mask) - visualization_weights = ( - dec_out_dict.visualization_weights * dec_out_dict.frustum_mask) - - # TODO(bencaine): Add class base colors to camera image projection. - if p.summarize_boxes_on_image: - # Update the camera visualization. - flattened_bbox_corners = np.reshape(dec_out_dict.bbox_corners_image, - [batch_size, -1, 8, 2]) - flattened_visualization_weights = np.reshape(visualization_weights, - [batch_size, -1]) - dec_metrics_dict.camera_visualization.Update( - py_utils.NestedMap({ - 'camera_images': dec_out_dict.camera_images, - # TODO(vrv): Use 2D output instead of 3D output. - 'bbox_corners': flattened_bbox_corners, - 'bbox_scores': flattened_visualization_weights, - })) - - # Update decoder output by removing z-coordinate, thus reshaping the bboxes - # to [batch, num_bboxes, 5] to be compatible with - # TopDownVisualizationMetric. - - # Indices corresponding to the 2D bbox parameters (x, y, dx, dy, phi). - bbox_2d_idx = np.asarray([1, 1, 0, 1, 1, 0, 1], dtype=np.bool) - bboxes_2d = dec_out_dict.bboxes_3d[..., bbox_2d_idx] - predicted_bboxes = dec_out_dict.per_class_predicted_bboxes[..., bbox_2d_idx] - - if dec_out_dict.points_sampled: - tf.logging.info('Updating sample for top down visualization') - dec_metrics_dict.mesh.Update( - py_utils.NestedMap({ - 'points_xyz': dec_out_dict.points_xyz, - 'points_padding': dec_out_dict.points_padding, - })) - - # Flatten our predictions/scores to match the API of the visualization - # The last dimension of flattened_bboxes is 5 due to the mask - # above using bbox_2d_idx. - flattened_bboxes = np.reshape(predicted_bboxes, - [batch_size, num_classes * num_boxes, 5]) - flattened_visualization_weights = np.reshape( - visualization_weights, [batch_size, num_classes * num_boxes]) - # Create a label id mask for now to maintain compatibility. - # TODO(bencaine): Refactor visualizations to reflect new structure. - flattened_visualization_labels = np.tile( - np.arange(0, num_classes)[np.newaxis, :, np.newaxis], - [batch_size, 1, num_boxes]) - flattened_visualization_labels = np.reshape( - flattened_visualization_labels, [batch_size, num_classes * num_boxes]) - - dec_metrics_dict.top_down_visualization.Update( - py_utils.NestedMap({ - 'visualization_labels': flattened_visualization_labels, - 'predicted_bboxes': flattened_bboxes, - 'visualization_weights': flattened_visualization_weights, - 'points_xyz': dec_out_dict.points_xyz, - 'points_padding': dec_out_dict.points_padding, - 'gt_bboxes_2d': bboxes_2d, - 'gt_bboxes_2d_weights': dec_out_dict.bboxes_3d_mask, - 'labels': dec_out_dict.labels, - 'difficulties': dec_out_dict.difficulties, - 'source_ids': dec_out_dict.source_ids, - })) - - # Update KITTI AP metrics. - for batch_idx in range(batch_size): - # Use class scores since it's masked - pred_bboxes = dec_out_dict.per_class_predicted_bboxes[batch_idx] - pred_bbox_scores = masked_pred_bbox_scores[batch_idx] - - # Compute height in 2D perspective view, that will be used for filtering - # outputs downstream. - pred_bbox2d_corners_image = dec_out_dict.bbox2d_corners_image[batch_idx] - # We assume y2 >= y1, so pred_heights_image will always be >= 0. - pred_heights_image = ( - pred_bbox2d_corners_image[:, :, 3] - - pred_bbox2d_corners_image[:, :, 1]) - - gt_mask = dec_out_dict.unfiltered_bboxes_3d_mask[batch_idx].astype(bool) - gt_labels = dec_out_dict.labels[batch_idx][gt_mask] - gt_bboxes = dec_out_dict.bboxes_3d[batch_idx][gt_mask] - gt_difficulties = dec_out_dict.difficulties[batch_idx][gt_mask] - gt_num_points = dec_out_dict.num_points_in_bboxes[batch_idx][gt_mask] - - for metric_class in [dec_metrics_dict.kitti_AP_v2]: - metric_class.Update( - dec_out_dict.source_ids[batch_idx], - py_utils.NestedMap( - groundtruth_labels=gt_labels, - groundtruth_bboxes=gt_bboxes, - groundtruth_difficulties=gt_difficulties, - groundtruth_num_points=gt_num_points, - detection_scores=pred_bbox_scores, - detection_boxes=pred_bboxes, - detection_heights_in_pixels=pred_heights_image, - )) - - # Returned values are saved in model_dir/decode. We can offline convert - # them into KITTI's format. - # velo_to_image_plane and velo_to_camera are needed for converting 3D bboxes - # back to the camera coordinate as required by KITTI's format. - # - output_to_save = [] - for i in range(batch_size): - gt_save_mask = dec_out_dict.unfiltered_bboxes_3d_mask[i].astype(bool) - pd_save_mask = dec_out_dict.per_class_valid_mask[i] > 0 - # Create a class id matrix we can then mask out - # since when you use the boolean mask in numpy it collapses the matrix - # from [num_classes, num_bboxes,...] to just [num_valid_bbboxes]. - class_ids = np.tile(np.arange(num_classes)[:, np.newaxis], [1, num_boxes]) - - saved_results = py_utils.NestedMap( - img_id=dec_out_dict.source_ids[i], - bboxes=dec_out_dict.per_class_predicted_bboxes[i][pd_save_mask], - scores=masked_pred_bbox_scores[i][pd_save_mask], - class_ids=class_ids[pd_save_mask], - gt_labels=dec_out_dict.labels[i][gt_save_mask], - gt_bboxes=dec_out_dict.bboxes_3d[i][gt_save_mask], - gt_difficulties=dec_out_dict.difficulties[i][gt_save_mask], - velo_to_image_plane=dec_out_dict.velo_to_image_plane[i], - velo_to_camera=dec_out_dict.velo_to_camera[i], - bboxes_2d=dec_out_dict.bbox2d_corners_image[i][pd_save_mask], - ) - - serialized = self.SaveTensors(saved_results) - output_to_save += [(dec_out_dict.source_ids[i], serialized)] - return output_to_save diff --git a/lingvo/tasks/car/kitti_input_generator.py b/lingvo/tasks/car/kitti_input_generator.py deleted file mode 100644 index ca8ca136..00000000 --- a/lingvo/tasks/car/kitti_input_generator.py +++ /dev/null @@ -1,649 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Input generator for KITTI data.""" - -from lingvo import compat as tf -from lingvo.core import datasource -from lingvo.core import hyperparams -from lingvo.core import ops -from lingvo.core import py_utils -from lingvo.tasks.car import geometry -from lingvo.tasks.car import input_extractor -from lingvo.tasks.car import input_preprocessors -from lingvo.tasks.car import kitti_metadata - - -def _Dense(sparse, default_value=0): - return tf.sparse_to_dense( - sparse_indices=sparse.indices, - output_shape=sparse.dense_shape, - sparse_values=sparse.values, - default_value=default_value) - - -def _NestedMapToParams(nmap): - p = hyperparams.Params() - for k, v in nmap.FlattenItems(): - p.Define(k, v, '') - return p - - -def ComputeKITTIDifficulties(box_image_height, occlusion, truncation): - """Compute difficulties from box height, occlusion, and truncation.""" - # Easy: No occlusion, max truncation 15% - easy_level = tf.cast((box_image_height >= 40.) & (occlusion <= 0.) - & (truncation <= 0.15), tf.int32) * 3 - # Moderate: max occlusion: partly occluded, max truncation 30% - moderate_level = tf.cast((occlusion <= 1.) & (truncation <= 0.3) - & (box_image_height >= 25.), tf.int32) * 2 - # Hard: Difficult to see, max truncation 50% - hard_level = tf.cast((occlusion <= 2.) & (truncation <= 0.5) - & (box_image_height >= 25.), tf.int32) * 1 - - # Occlusion = 3 and higher truncation is "super hard", and - # will map to 0 (ignored). - difficulties = tf.maximum(tf.maximum(hard_level, moderate_level), easy_level) - - return difficulties - - -class KITTILaserExtractor(input_extractor.LaserExtractor): - """Base extractor for the laser points from a KITTI tf.Example.""" - - @classmethod - def Params(cls): - p = super().Params().Set(max_num_points=None, num_features=1) - return p - - def FeatureMap(self): - feature_map = { - 'pointcloud/xyz': tf.io.VarLenFeature(dtype=tf.float32), - 'pointcloud/reflectance': tf.io.VarLenFeature(dtype=tf.float32), - } - return feature_map - - def _Extract(self, features): - p = self.params - points_xyz = tf.reshape(_Dense(features['pointcloud/xyz']), [-1, 3]) - points_feature = tf.reshape( - _Dense(features['pointcloud/reflectance']), [-1, p.num_features]) - - if p.max_num_points is not None: - npoints = tf.shape(points_xyz)[0] - points_xyz = py_utils.PadOrTrimTo(points_xyz, [p.max_num_points, 3]) - points_feature = py_utils.PadOrTrimTo(points_feature, - [p.max_num_points, p.num_features]) - points_padding = 1.0 - py_utils.PadOrTrimTo( - tf.ones([npoints]), [p.max_num_points]) - - ret = py_utils.NestedMap( - points_xyz=points_xyz, points_feature=points_feature) - if p.max_num_points is not None: - ret.points_padding = points_padding - return ret - - -class KITTIImageExtractor(input_extractor.FieldsExtractor): - """Extracts the image information (left camera) from a KITTI tf.Example. - - Produces: - image: [512, 1382, 3] - Floating point Tensor containing image data. Note - that image may not be produced if decode_image is set to False. During - training, we may not want to decode the images. - - width: [1] - integer scalar width of the original image. - - height: [1] - integer scalar width of the original image. - - velo_to_image_plane: [3, 4] - transformation matrix from velo xyz to image - plane xy. After multiplication, you need to divide by last coordinate to - recover 2D pixel locations. - - velo_to_camera: [4, 4] - transformation matrix from velo xyz to camera xyz. - - camera_to_velo: [4, 4] - transformation matrix from camera xyz to velo xyz. - """ - _KITTI_MAX_HEIGHT = 512 - _KITTI_MAX_WIDTH = 1382 - - @classmethod - def Params(cls): - p = super().Params() - p.Define('decode_image', True, 'Whether to decode and produce image.') - return p - - def FeatureMap(self): - p = self.params - feature_map = { - 'image/format': - tf.io.FixedLenFeature((), tf.string, default_value='png'), - 'image/height': - tf.io.FixedLenFeature((), tf.int64, default_value=1), - 'image/width': - tf.io.FixedLenFeature((), tf.int64, default_value=1), - 'image/source_id': - tf.io.FixedLenFeature((), tf.string, default_value=''), - # The camera calibration matrices can be used later with width/height - # to perform out of camera frustum point dropping. - 'transform/velo_to_image_plane': - tf.io.FixedLenFeature(shape=(3, 4), dtype=tf.float32), - 'transform/velo_to_camera': - tf.io.FixedLenFeature(shape=(4, 4), dtype=tf.float32), - 'transform/camera_to_velo': - tf.io.FixedLenFeature(shape=(4, 4), dtype=tf.float32), - } - if p.decode_image: - feature_map['image/encoded'] = tf.io.FixedLenFeature((), - tf.string, - default_value='') - return feature_map - - def _Extract(self, features): - p = self.params - - if p.decode_image: - raw = features['image/encoded'] - image = tf.image.decode_png(raw, channels=3) - image = tf.image.convert_image_dtype(image, tf.float32) - # Padding instead of rescaling to preserve the pixel coordinates. - image = py_utils.PadOrTrimTo( - image, [self._KITTI_MAX_HEIGHT, self._KITTI_MAX_WIDTH, 3]) - - width = tf.reshape(features['image/width'], [1]) - height = tf.reshape(features['image/height'], [1]) - - velo_to_image_plane = features['transform/velo_to_image_plane'] - velo_to_camera = features['transform/velo_to_camera'] - camera_to_velo = features['transform/camera_to_velo'] - - extracted_features = py_utils.NestedMap( - width=width, - height=height, - velo_to_image_plane=velo_to_image_plane, - velo_to_camera=velo_to_camera, - camera_to_velo=camera_to_velo) - - if p.decode_image: - extracted_features.image = image - - return extracted_features - - def Shape(self): - p = self.params - shape = py_utils.NestedMap( - width=tf.TensorShape([1]), - height=tf.TensorShape([1]), - velo_to_image_plane=tf.TensorShape([3, 4]), - velo_to_camera=tf.TensorShape([4, 4]), - camera_to_velo=tf.TensorShape([4, 4])) - if p.decode_image: - shape.image = tf.TensorShape( - [self._KITTI_MAX_HEIGHT, self._KITTI_MAX_WIDTH, 3]) - return shape - - def DType(self): - p = self.params - dtype = py_utils.NestedMap( - width=tf.int64, - height=tf.int64, - velo_to_image_plane=tf.float32, - velo_to_camera=tf.float32, - camera_to_velo=tf.float32) - if p.decode_image: - dtype.image = tf.float32 - return dtype - - -# Various coordinate systems in the outputs: -# - bboxes: 2D "image" coordinate. -# - bboxes_3d[3:6] (locations): 3D "world" coordinate. -# - bboxes_3d[:3] (dimension) -# - points_xyz: 3D "world" coordinate. -# -# To convert from: -# "camera" to "world": use extrinsics/R and extrinsics/t. -# "camera" to "image": use intrinsics/K - - -class KITTILabelExtractor(input_extractor.FieldsExtractor): - """Extracts the object labels from a KITTI tf.Example. - - Emits: - bboxes_count: Scalar number of 2D bounding boxes in the example. - - bboxes: [p.max_num_objects, 4] - 2D bounding box data in [ymin, xmin, ymax, - xmax] format. - - bboxes_padding: [p.max_num_objects] - Padding for bboxes. - - bboxes_3d: [p.max_num_objects, 7] - 3D bounding box data in [x, y, z, dx, - dy, dz, phi] format. x, y, z are the object center; dx, dy, dz are the - dimensions of the box, and phi is the rotation angle around the z-axis. - 3D bboxes are defined in the velodyne coordinate frame. - - bboxes_3d_mask: [p.max_num_objects] - Mask for bboxes (mask is the inversion - of padding). - - bboxes3d_proj_to_image_plane: [p.max_num_objects, 8, 2] - For each - bounding box, the 8 corners of the bounding box in projected image - coordinates (x, y). - - bboxes_td: [p.max_num_objects, 4] - The 3D bounding box data in top down - projected coordinates (ymin, xmin, ymax, xmax). This currently ignores - rotation. - - bboxes_td_mask: [p.max_num_objects]: Mask for bboxes_td. - - bboxes_3d_num_points: [p.max_num_objects]: Number of points in each box. - - labels: [p.max_num_objects] - Integer label for each bounding box object - corresponding to the index in KITTI_CLASS_NAMES. - - texts: [p.max_num_objects] - The class name for each label in labels. - - source_id: Scalar string. The unique identifier for each example. - - See ComputeKITTIDifficulties for more info of the following:: - - box_image_height: [p.max_num_objects] - The height of the box in pixels - of each box in the projected image plane. - - occlusion: [p.max_num_objects] - The occlusion level of each bounding box. - - truncation: [p.max_num_objects] - The truncation level of each bounding box. - - difficulties: [p.max_num_objects] - The computed difficulty based on the - above three factors. - """ - KITTI_CLASS_NAMES = kitti_metadata.KITTIMetadata().ClassNames() - - # Sub-classes for filtering labels when training class specific models. - SUBCLASS_DICT = { - 'human': [4, 5], - 'cyclist': [6], - 'motor': [1, 2, 3, 7], - 'pedestrian': [4], - } - - @classmethod - def Params(cls): - p = super().Params() - p.Define('max_num_objects', 50, 'The number of objects per example.') - p.Define('filter_labels', None, 'If not None, specifies a list of label ' - 'indices to keep.') - return p - - def FeatureMap(self): - return { - 'image/source_id': - tf.io.FixedLenFeature((), tf.string, ''), - 'object/image/bbox/xmin': - tf.io.VarLenFeature(tf.float32), - 'object/image/bbox/xmax': - tf.io.VarLenFeature(tf.float32), - 'object/image/bbox/ymin': - tf.io.VarLenFeature(tf.float32), - 'object/image/bbox/ymax': - tf.io.VarLenFeature(tf.float32), - 'object/label': - tf.io.VarLenFeature(tf.string), - 'object/has_3d_info': - tf.io.VarLenFeature(dtype=tf.int64), - 'object/occlusion': - tf.io.VarLenFeature(dtype=tf.int64), - 'object/truncation': - tf.io.VarLenFeature(dtype=tf.float32), - 'object/velo/bbox/xyz': - tf.io.VarLenFeature(dtype=tf.float32), - 'object/velo/bbox/dim_xyz': - tf.io.VarLenFeature(dtype=tf.float32), - 'object/velo/bbox/phi': - tf.io.VarLenFeature(dtype=tf.float32), - 'transform/velo_to_image_plane': - tf.io.FixedLenFeature(shape=(3, 4), dtype=tf.float32), - } - - def _Extract(self, features): - p = self.params - - source_id = py_utils.HasShape(features['image/source_id'], []) - xmin = _Dense(features['object/image/bbox/xmin']) - xmax = _Dense(features['object/image/bbox/xmax']) - ymin = _Dense(features['object/image/bbox/ymin']) - ymax = _Dense(features['object/image/bbox/ymax']) - - # 2d bounding box in image coordinates. - bboxes = tf.stack([ymin, xmin, ymax, xmax], axis=1) - bboxes_count = tf.shape(bboxes)[0] - bboxes = py_utils.PadOrTrimTo(bboxes, [p.max_num_objects, 4]) - - bboxes_padding = 1.0 - py_utils.PadOrTrimTo( - tf.ones([bboxes_count]), [p.max_num_objects]) - - dim_xyz = tf.reshape(_Dense(features['object/velo/bbox/dim_xyz']), [-1, 3]) - loc_xyz = tf.reshape(_Dense(features['object/velo/bbox/xyz']), [-1, 3]) - phi = tf.reshape(_Dense(features['object/velo/bbox/phi']), [-1, 1]) - # bboxes_3d is in [x, y, z, dx, dy, dz, phi]. - bboxes_3d = tf.concat([loc_xyz, dim_xyz, phi], axis=1) - - cx, cy, _, dx, dy, _, _ = tf.unstack(bboxes_3d, num=7, axis=-1) - bboxes_td = tf.stack([ - cy - dy / 2, - cx - dx / 2, - cy + dy / 2, - cx + dx / 2, - ], axis=-1) # pyformat: disable - bboxes_td = py_utils.PadOrTrimTo(bboxes_td, [p.max_num_objects, 4]) - - has_3d_info = tf.cast(_Dense(features['object/has_3d_info']), tf.float32) - bboxes_3d_mask = py_utils.PadOrTrimTo(has_3d_info, [p.max_num_objects]) - bboxes_td_mask = bboxes_3d_mask - - # Fill in difficulties from bounding box height, truncation and occlusion. - bb_height = ymax - ymin - box_image_height = py_utils.PadOrTrimTo(bb_height, [p.max_num_objects]) - box_image_height *= bboxes_3d_mask - - # 0 to 3 indicating occlusion level. 0 means fully visible, 1 means partly, - occlusion = tf.reshape(_Dense(features['object/occlusion']), [-1]) - occlusion = tf.cast(occlusion, tf.float32) - occlusion = py_utils.PadOrTrimTo(occlusion, [p.max_num_objects]) - occlusion *= bboxes_3d_mask - - # Truncation: 0 -> not truncated, 1.0 -> truncated - truncation = tf.reshape(_Dense(features['object/truncation']), [-1]) - truncation = py_utils.PadOrTrimTo(truncation, [p.max_num_objects]) - truncation *= bboxes_3d_mask - - difficulties = ComputeKITTIDifficulties(box_image_height, occlusion, - truncation) - difficulties = py_utils.PadOrTrimTo(difficulties, [p.max_num_objects]) - - # Make a batch axis to call BBoxCorners, and take the first result back. - bbox3d_corners = geometry.BBoxCorners(bboxes_3d[tf.newaxis, ...])[0] - - # Project the 3D bbox to the image plane. - velo_to_image_plane = features['transform/velo_to_image_plane'] - bboxes3d_proj_to_image_plane = geometry.PointsToImagePlane( - tf.reshape(bbox3d_corners, [-1, 3]), velo_to_image_plane) - - # Output is [num_objects, 8 corners per object, (x, y)]. - bboxes3d_proj_to_image_plane = tf.reshape(bboxes3d_proj_to_image_plane, - [-1, 8, 2]) - bboxes3d_proj_to_image_plane = py_utils.PadOrTrimTo( - bboxes3d_proj_to_image_plane, [p.max_num_objects, 8, 2]) - - texts = features['object/label'].values - labels = ops.static_map_string_int(x=texts, keys=self.KITTI_CLASS_NAMES) - - labels = py_utils.PadOrTrimTo(labels, [p.max_num_objects]) - texts = py_utils.PadOrTrimTo(texts, [p.max_num_objects]) - - # Filter labels by setting bboxes_padding, bboxes_3d_mask, and - # bboxes_td_mask appropriately. - if p.filter_labels is not None: - valid_labels = tf.constant([p.filter_labels]) - bbox_mask = tf.reduce_any( - tf.equal(tf.expand_dims(labels, 1), valid_labels), axis=1) - bbox_mask = tf.cast(bbox_mask, tf.float32) - bboxes_padding = 1 - bbox_mask * (1 - bboxes_padding) - filtered_bboxes_3d_mask = bboxes_3d_mask * bbox_mask - bboxes_td_mask *= bbox_mask - else: - filtered_bboxes_3d_mask = bboxes_3d_mask - - # Placeholder for counting the number of laser points that reside within - # each 3-d bounding box. This must be filled in outside of this function - # based on the loaded 3-d laser points. - bboxes_3d_num_points = tf.zeros([p.max_num_objects], dtype=tf.int32) - bboxes_3d_num_points = py_utils.PadOrTrimTo(bboxes_3d_num_points, - [p.max_num_objects]) - - # Pad bboxes_3d. - bboxes_3d = py_utils.PadOrTrimTo(bboxes_3d, [p.max_num_objects, 7]) - - return py_utils.NestedMap( - source_id=source_id, - bboxes_count=bboxes_count, - bboxes=bboxes, - bboxes_padding=bboxes_padding, - bboxes_3d=bboxes_3d, - bboxes_3d_mask=filtered_bboxes_3d_mask, - unfiltered_bboxes_3d_mask=bboxes_3d_mask, - bboxes3d_proj_to_image_plane=bboxes3d_proj_to_image_plane, - bboxes_td=bboxes_td, - bboxes_td_mask=bboxes_td_mask, - bboxes_3d_num_points=bboxes_3d_num_points, - labels=labels, - texts=texts, - box_image_height=box_image_height, - occlusion=occlusion, - truncation=truncation, - difficulties=difficulties) - - def Shape(self): - p = self.params - return py_utils.NestedMap( - source_id=tf.TensorShape([]), - bboxes_count=tf.TensorShape([]), - bboxes=tf.TensorShape([p.max_num_objects, 4]), - bboxes_padding=tf.TensorShape([p.max_num_objects]), - bboxes_3d=tf.TensorShape([p.max_num_objects, 7]), - bboxes_3d_mask=tf.TensorShape([p.max_num_objects]), - unfiltered_bboxes_3d_mask=tf.TensorShape([p.max_num_objects]), - bboxes3d_proj_to_image_plane=tf.TensorShape([p.max_num_objects, 8, 2]), - bboxes_td=tf.TensorShape([p.max_num_objects, 4]), - bboxes_td_mask=tf.TensorShape([p.max_num_objects]), - bboxes_3d_num_points=tf.TensorShape([p.max_num_objects]), - labels=tf.TensorShape([p.max_num_objects]), - texts=tf.TensorShape([p.max_num_objects]), - box_image_height=tf.TensorShape([p.max_num_objects]), - occlusion=tf.TensorShape([p.max_num_objects]), - truncation=tf.TensorShape([p.max_num_objects]), - difficulties=tf.TensorShape([p.max_num_objects])) - - def DType(self): - return py_utils.NestedMap( - source_id=tf.string, - bboxes_count=tf.int32, - bboxes=tf.float32, - bboxes_padding=tf.float32, - bboxes_3d=tf.float32, - bboxes_3d_mask=tf.float32, - unfiltered_bboxes_3d_mask=tf.float32, - bboxes3d_proj_to_image_plane=tf.float32, - bboxes_td=tf.float32, - bboxes_td_mask=tf.float32, - bboxes_3d_num_points=tf.int32, - labels=tf.int32, - texts=tf.string, - box_image_height=tf.float32, - occlusion=tf.float32, - truncation=tf.float32, - difficulties=tf.int32) - - -class KITTIBase(input_extractor.BaseExtractor): - """KITTI dataset base parameters.""" - - @classmethod - def Params(cls, *args, **kwargs): - p = super().Params(*args, **kwargs) - - # Subclasses should set the following in file_datasource: - # - file_pattern_prefix: path to data directory (may be overridden at - # runtime) - # - file_pattern: file pattern of records relative to the - # data directory - p.file_datasource = datasource.PrefixedDataSource.Params() - p.file_datasource.file_type = 'tfrecord' - - return p - - @property - def class_names(self): - return KITTILabelExtractor.KITTI_CLASS_NAMES - - -class KITTILaser(KITTIBase): - """KITTI object detection dataset. - - This class emits KITTI images, labels, and the raw laser - representation of the data. See KITTIGrid and KITTISparse - for alternative laser representations. - - Input batch contains outputs from: - - KITTIImageExtractor - - KITTILabelExtractor - - KITTILaserExtractor - """ - - @classmethod - def Params(cls): - """Defaults params.""" - extractors = hyperparams.Params() - extractors.Define('labels', KITTILabelExtractor.Params(), '') - extractors.Define('lasers', KITTILaserExtractor.Params(), '') - extractors.Define('images', KITTIImageExtractor.Params(), '') - preprocessors = py_utils.NestedMap( - count_points=input_preprocessors.CountNumberOfPointsInBoxes3D.Params(), - viz_copy=input_preprocessors.CreateDecoderCopy.Params(), - pad_lasers=input_preprocessors.PadLaserFeatures.Params().Set( - max_num_points=128500)) - - p = super().Params(extractors).Set( - preprocessors=_NestedMapToParams(preprocessors), - preprocessors_order=['viz_copy', 'count_points', 'pad_lasers']) - return p - - @property - def class_names(self): - return KITTILabelExtractor.KITTI_CLASS_NAMES - - -class KITTISparseLaser(KITTIBase): - """KITTI object detection dataset for sparse detection models. - - This class emits KITTI images, labels, and the sparse laser - representation of the data. See KITTIGrid and KITTISparse - for alternative laser representations. - - Input batch contains outputs from: - - KITTILabelExtractor - - KITTILaserExtractor - - Transformed with: - - Metadata annotation: - - CountNumberOfPointsInBoxes3D - - Visualization: - - CreateDecoderCopy - - Sparse gather of points for featurization: - - SparseCenterSelector - - SparseCellGatherFeatures - - Anchor creation for classification regression targets: - - TileAnchorBBoxes - - AnchorAssignment - """ - - @classmethod - def Params(cls): - """Defaults params.""" - extractors = hyperparams.Params() - extractors.Define('labels', KITTILabelExtractor.Params(), '') - extractors.Define('lasers', KITTILaserExtractor.Params(), '') - extractors.Define('images', KITTIImageExtractor.Params(), '') - preprocessors = py_utils.NestedMap( - count_points=input_preprocessors.CountNumberOfPointsInBoxes3D.Params(), - viz_copy=input_preprocessors.CreateDecoderCopy.Params(), - keep_xyz_range=input_preprocessors.DropLaserPointsOutOfRange.Params(), - select_centers=input_preprocessors.SparseCenterSelector.Params(), - gather_features=input_preprocessors.SparseCellGatherFeatures.Params(), - tile_anchors=input_preprocessors.TileAnchorBBoxes.Params(), - assign_anchors=input_preprocessors.AnchorAssignment.Params(), - pad_lasers=input_preprocessors.PadLaserFeatures.Params().Set(), - ) - - p = super().Params(extractors).Set( - preprocessors=_NestedMapToParams(preprocessors), - preprocessors_order=[ - 'viz_copy', - 'keep_xyz_range', - 'count_points', - 'select_centers', - 'gather_features', - 'tile_anchors', - 'assign_anchors', - 'pad_lasers', - ], - ) - return p - - -class KITTIGrid(KITTIBase): - """KITTI object detection dataset. - - This class emits KITTI images, labels, and the fixed grid laser - representation of the data. - - Input batch contains outputs from: - - KITTILabelExtractor - - KITTILaserExtractor - - Transformed with: - - Metadata annotation: - - CountNumberOfPointsInBoxes3D - - Visualization: - - CreateDecoderCopy - - Points to Pillars - - PointsToGrid - - GridToPillars - - Anchor creation for classification regression targets: - - GridAnchorCenters - - TileAnchorBBoxes - - AnchorAssignment - """ - - @classmethod - def Params(cls): - """Defaults params.""" - extractors = hyperparams.Params() - extractors.Define('labels', KITTILabelExtractor.Params(), '') - extractors.Define('lasers', KITTILaserExtractor.Params(), '') - preprocessors = py_utils.NestedMap( - count_points=input_preprocessors.CountNumberOfPointsInBoxes3D.Params(), - viz_copy=input_preprocessors.CreateDecoderCopy.Params(), - points_to_grid=input_preprocessors.PointsToGrid.Params(), - grid_to_pillars=input_preprocessors.GridToPillars.Params(), - grid_anchor_centers=input_preprocessors.GridAnchorCenters.Params(), - tile_anchors=input_preprocessors.TileAnchorBBoxes.Params(), - assign_anchors=input_preprocessors.AnchorAssignment.Params(), - pad_lasers=input_preprocessors.PadLaserFeatures.Params().Set( - max_num_points=128500), - ) - p = super().Params(extractors).Set( - preprocessors=_NestedMapToParams(preprocessors), - preprocessors_order=[ - 'viz_copy', - 'count_points', - 'points_to_grid', - 'grid_to_pillars', - 'grid_anchor_centers', - 'tile_anchors', - 'assign_anchors', - 'pad_lasers', - ], - ) - return p diff --git a/lingvo/tasks/car/kitti_metadata.py b/lingvo/tasks/car/kitti_metadata.py deleted file mode 100644 index 9c2d0553..00000000 --- a/lingvo/tasks/car/kitti_metadata.py +++ /dev/null @@ -1,86 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Metadata for KITTI dataset employed in evaluation.""" - -from lingvo.tasks.car import evaluation_metadata - - -class KITTIMetadata(evaluation_metadata.EvaluationMetadata): - """Metadata describing the KITTI dataset used for evaluation.""" - - def __init__(self): - super().__init__('kitti') - - def ClassNames(self): - """Returns a list of human-interpretable strings.""" - return [ - 'Background', 'Car', 'Van', 'Truck', 'Pedestrian', 'Person_sitting', - 'Cyclist', 'Tram', 'Misc', 'DontCare' - ] - - def DifficultyLevels(self): - """Dictionary of difficulty level strings to int32 indices.""" - return {'hard': 1, 'moderate': 2, 'easy': 3} - - def IoUThresholds(self): - return { - 'Car': 0.7, - 'Pedestrian': 0.5, - 'Cyclist': 0.5, - } - - def IgnoreClassIndices(self): - """Dictionary of int32 indices for the classes that should be ignored.""" - # A detection that matches with a groundtruth bbox of any neighbor class - # will not be considered as false positive in eval. - return { - self.ClassNames().index('Car'): [self.ClassNames().index('Van')], - self.ClassNames().index('Pedestrian'): [ - self.ClassNames().index('Person_sitting') - ], - } - - def NumberOfPrecisionRecallPoints(self): - """Number of points on the precision-recall curve.""" - return 41 - - def MaximumDistance(self): - """Maximum empirically observed Euclidean distance in world coordinates.""" - return 80.0 - - def DistanceBinWidth(self): - """The width of each bin for Euclidean distance in world coordinates.""" - return 10.0 - - def MaximumNumberOfPoints(self): - """Maximum empirically observed number of points in bounding box.""" - return 3000.0 - - def NumberOfPointsBins(self): - """Number of logarithmically space bins for examining points.""" - return 20 - - def NumberOfRotationBins(self): - """Number of linear spaced rotations to bin.""" - return 10 - - def NumberOfCalibrationBins(self): - """Number of linear spaced calibration bins.""" - return 15 - - def MinHeight2D(self): - """Minimum height of detections to be evaluated.""" - return {'hard': 25, 'moderate': 25, 'easy': 40} diff --git a/lingvo/tasks/car/lr_util.py b/lingvo/tasks/car/lr_util.py deleted file mode 100644 index 4ca10426..00000000 --- a/lingvo/tasks/car/lr_util.py +++ /dev/null @@ -1,105 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Learning rate schedule utility functions.""" - -import math - -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import py_utils -from lingvo.core import schedule - - -def _GetTrainingStatistics(train_input_p): - """Get training statistics, including total batch size and steps per epoch.""" - cluster = cluster_factory.Current() - # E.g., this is 1 for a single GPU, 8 for a 2x2 TPU, 32 for a 4x4 TPU, - # or 0 if no training job is launched. - total_num_cores = cluster.total_worker_devices - total_batch_size = max(train_input_p.batch_size * total_num_cores, 1) - steps_per_epoch = float(train_input_p.num_samples) / total_batch_size - return py_utils.NestedMap( - total_num_cores=total_num_cores, - total_batch_size=total_batch_size, - steps_per_epoch=steps_per_epoch) - - -def SetExponentialLR(train_p, - train_input_p, - exp_start_epoch, - total_epoch, - warmup_epoch=0, - limit_epoch=None, - multiplier_min=0.01, - warmup_init=0.): - """Sets a linear rampup and exponential decay LR schedule on train_p. - - This is a wrapper around LinearRampupExponentialDecayScaledByNumSplitSchedule - that sets the steps using epochs and the training statistics. - - Args: - train_p: train parameters. - train_input_p: The training set input parameters. - exp_start_epoch: The start epoch of exponential annealing. - total_epoch: Total number of epoch to train. - warmup_epoch: Epoch for the warm up ramp to end at. Note that the learning - rate will be fixed between the end of the warmup phase and the beginning - of the exponential annealing phase. - limit_epoch: Epoch to end exponential annealing. If None, this will be set - to 0.95 * total_epoch, that is, the last 5% of training time will be at - the minimum learning rate. - multiplier_min: The multiplier minimum at the end of exponential decay. - warmup_init: Initial value for the warmup phase. Note that warm up can be - disabled by either setting warmup_init to 1 or setting warmup_epoch to 0. - """ - - # Determine steps based on the training statistics, since the number of steps - # depends on the number of examples per step. - train_stats = _GetTrainingStatistics(train_input_p) - warmup_steps = warmup_epoch * train_stats.steps_per_epoch - exp_start_steps = exp_start_epoch * train_stats.steps_per_epoch - total_steps = total_epoch * train_stats.steps_per_epoch - if limit_epoch is None: - limit_epoch = 0.95 * total_epoch - limit_steps = limit_epoch * train_stats.steps_per_epoch - - tf.logging.info('#cores = %d batch size = %d steps/epoch = %d', - train_stats.total_num_cores, - train_stats.total_batch_size, - train_stats.steps_per_epoch) - tf.logging.info( - 'warmup_steps = %d exp_start_steps = %d total_steps = %d ' - 'limit_steps = %d', warmup_steps, exp_start_steps, total_steps, - limit_steps) - - assert 0. <= warmup_steps <= exp_start_steps <= limit_steps <= total_steps - - # Ensure that warmup is disabled by also setting warmup_init to 1 if - # warmup_epoch is set to 0. - if warmup_epoch == 0.: - warmup_init = 1. - - train_p.max_steps = math.ceil(total_epoch * train_stats.steps_per_epoch) - train_p.lr_schedule = ( - schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params()) - train_p.lr_schedule.Set( - warmup=warmup_steps, - decay_start=exp_start_steps, - decay_end=limit_steps, - min=multiplier_min, - warmup_init=warmup_init, - # Set num_splits to 1 so that no further scaling is done. - num_splits=1) diff --git a/lingvo/tasks/car/lr_util_test.py b/lingvo/tasks/car/lr_util_test.py deleted file mode 100644 index c3299715..00000000 --- a/lingvo/tasks/car/lr_util_test.py +++ /dev/null @@ -1,72 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lr_util.""" - -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import kitti_input_generator -from lingvo.tasks.car import lr_util -from lingvo.tasks.car import pillars - - -class LrUtilTest(test_utils.TestCase): - - def _testInput(self): - p = kitti_input_generator.KITTIGrid.Params() - p.batch_size = 8 - p.num_samples = 128 - return p - - def _testParams(self): - p = pillars.ModelV1.Params() - p.name = 'test' - p.input = self._testInput() - return p - - def testExponentialWithLinearRamp(self): - p = self._testParams() - lr_util.SetExponentialLR( - p.train, - p.input, - warmup_epoch=1, - exp_start_epoch=2, - total_epoch=10, - warmup_init=0.) - schedule_layer = p.train.lr_schedule.Instantiate() - with self.session(): - # Linear ramp up. - self.assertLess(self.evaluate(schedule_layer.Value(8)), 1.) - # Peak learning rate. - self.assertEqual(self.evaluate(schedule_layer.Value(16)), 1.) - # Still at peak learning rate. - self.assertEqual(self.evaluate(schedule_layer.Value(24)), 1.) - # Exponential ramp down. - self.assertLess(self.evaluate(schedule_layer.Value(48)), 1.) - - def testExponentialWithoutLinearRamp(self): - p = self._testParams() - lr_util.SetExponentialLR( - p.train, p.input, exp_start_epoch=0, total_epoch=10) - schedule_layer = p.train.lr_schedule.Instantiate() - with self.session(): - # Peak learning rate at 0. - self.assertEqual(self.evaluate(schedule_layer.Value(0)), 1.) - # Exponential ramp down within first epoch. - self.assertLess(self.evaluate(schedule_layer.Value(4)), 1.) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/ops/BUILD b/lingvo/tasks/car/ops/BUILD deleted file mode 100644 index f41500c8..00000000 --- a/lingvo/tasks/car/ops/BUILD +++ /dev/null @@ -1,181 +0,0 @@ -load( - "//lingvo:lingvo.bzl", - "custom_kernel_library", - "gen_op_cclib", - "gen_op_pylib", - "lingvo_cc_library", - "lingvo_cc_test", -) - -package(default_visibility = [ - "//visibility:public", -]) - -licenses(["notice"]) # Apache 2.0 - -lingvo_cc_library( - name = "box_util", - srcs = ["box_util.cc"], - hdrs = ["box_util.h"], -) - -lingvo_cc_library( - name = "image_metrics", - srcs = ["image_metrics.cc"], - hdrs = ["image_metrics.h"], -) - -# Op definitions to be used from python. -gen_op_cclib( - name = "car_ops", - srcs = ["car_ops.cc"], -) - -gen_op_pylib( - name = "py_car_ops", - srcs = ["__init__.py"], - cc_lib_name = "car_ops", - kernel_deps = [ - ":op_kernels", - ], - py_deps = [ - "//lingvo:compat", - ], -) - -py_library( - name = "ops", - srcs = ["__init__.py"], - srcs_version = "PY3", - deps = [ - ":py_car_ops", # buildcleaner: keep - "//lingvo:compat", - ], -) - -# Op kernel deps. -cc_library( - name = "op_kernels", - deps = [ - ":average_precision_3d_op", - ":nms_3d_op", - ":pairwise_iou_op", - ":point_grid_op", - ":sampling_ops", - ], -) - -custom_kernel_library( - name = "sampling_ops", - srcs = ["sampling_ops.cc"], - op_def_lib = [":car_ops"], - deps = [ - ":ps_utils", - ], -) - -py_test( - name = "sampling_ops_test", - srcs = ["sampling_ops_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -lingvo_cc_library( - name = "ps_utils", - srcs = ["ps_utils.cc"], - hdrs = ["ps_utils.h"], - deps = [ - # Implicit absl.synchronization dependency. - ], -) - -custom_kernel_library( - name = "pairwise_iou_op", - srcs = ["pairwise_iou_op.cc"], - op_def_lib = [":car_ops"], - deps = [ - ":box_util", - ], -) - -custom_kernel_library( - name = "point_grid_op", - srcs = ["point_grid_op.cc"], - op_def_lib = [":car_ops"], -) - -custom_kernel_library( - name = "average_precision_3d_op", - srcs = ["average_precision_3d_op.cc"], - op_def_lib = [":car_ops"], - deps = [ - ":box_util", - ":image_metrics", - ], -) - -py_test( - name = "average_precision_3d_op_test", - srcs = ["average_precision_3d_op_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_test( - name = "point_grid_op_test", - srcs = ["point_grid_op_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - # Implicit tensorflow dependency. - ], -) - -custom_kernel_library( - name = "nms_3d_op", - srcs = ["nms_3d_op.cc"], - op_def_lib = [":car_ops"], - deps = [ - ":box_util", - ], -) - -py_test( - name = "nms_3d_op_test", - srcs = ["nms_3d_op_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":ops", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -lingvo_cc_test( - name = "ps_utils_test", - srcs = ["ps_utils_test.cc"], - deps = [ - ":ps_utils", - ], -) diff --git a/lingvo/tasks/car/ops/__init__.py b/lingvo/tasks/car/ops/__init__.py deleted file mode 100644 index 5d0650da..00000000 --- a/lingvo/tasks/car/ops/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Car Operations.""" - -from lingvo import compat as tf - -# Try static linking: -try: - from lingvo.tasks.car.ops import gen_car_ops # pylint: disable=g-import-not-at-top -except ImportError: - gen_car_ops = tf.load_op_library( - tf.resource_loader.get_path_to_datafile('car_ops.so')) - -# Set gen_car_ops function module so sphinx generates documentation. -for v in gen_car_ops.__dict__.values(): - try: - v.__module__ = 'lingvo.tasks.car.ops' - except: # pylint: disable=bare-except - pass - -pairwise_iou3d = gen_car_ops.pairwise_iou3d -point_to_grid = gen_car_ops.point_to_grid -non_max_suppression_3d = gen_car_ops.non_max_suppression3d -average_precision3d = gen_car_ops.average_precision3d -sample_points = gen_car_ops.sample_points diff --git a/lingvo/tasks/car/ops/average_precision_3d_op.cc b/lingvo/tasks/car/ops/average_precision_3d_op.cc deleted file mode 100644 index 389ff61b..00000000 --- a/lingvo/tasks/car/ops/average_precision_3d_op.cc +++ /dev/null @@ -1,219 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include - -#include "lingvo/tasks/car/ops/box_util.h" -#include "lingvo/tasks/car/ops/image_metrics.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/lib/core/status.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -enum class APAlgorithm { - kVOC = 0, - kKITTI = 1, -}; - -class AP3DOp final : public OpKernel { - public: - explicit AP3DOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, ctx->GetAttr("num_recall_points", &num_recall_points_)); - string ap_algorithm_name; - OP_REQUIRES_OK(ctx, ctx->GetAttr("algorithm", &ap_algorithm_name)); - if (ap_algorithm_name == "KITTI") { - ap_algorithm_ = APAlgorithm::kKITTI; - } else if (ap_algorithm_name == "VOC") { - ap_algorithm_ = APAlgorithm::kVOC; - } else { - OP_REQUIRES( - ctx, false, - errors::InvalidArgument("algorithm must be one of \"KITTI\", \"VOC\"," - "but got ", - ap_algorithm_name)); - } - - OP_REQUIRES( - ctx, num_recall_points_ > 0, - errors::InvalidArgument("num_recall_points must be positive but get ", - num_recall_points_)); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor* gt_bbox = nullptr; - OP_REQUIRES_OK(ctx, ctx->input("groundtruth_bbox", >_bbox)); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(gt_bbox->shape()), - errors::InvalidArgument("bbox must be a matrix, but get ", - gt_bbox->shape().DebugString())); - OP_REQUIRES(ctx, gt_bbox->dim_size(1) == 7, - errors::InvalidArgument("bbox must be [:, 7], but get ", - gt_bbox->shape().DebugString())); - const int n = gt_bbox->dim_size(0); - - const Tensor* gt_imageid = nullptr; - OP_REQUIRES_OK(ctx, ctx->input("groundtruth_imageid", >_imageid)); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(gt_imageid->shape()), - errors::InvalidArgument("imageid must be a vector, but get ", - gt_imageid->shape().DebugString())); - OP_REQUIRES(ctx, gt_imageid->dim_size(0) == n, - errors::InvalidArgument("imageid shape mismatch, get ", - gt_imageid->shape().DebugString())); - const Tensor* gt_ignore = nullptr; - OP_REQUIRES_OK(ctx, ctx->input("groundtruth_ignore", >_ignore)); - OP_REQUIRES(ctx, gt_imageid->shape().IsSameSize(gt_ignore->shape()), - errors::InvalidArgument("gt_ignore shape mismatch: ", - gt_ignore->shape().DebugString())); - - std::vector> groundtruth; - std::vector groundtruth_boxes = - box::ParseBoxesFromTensor(*gt_bbox); - groundtruth.reserve(n); - // Ignore difficulty as it's currently used by VOC only. - for (int i = 0; i < n; ++i) { - image::Detection g; - g.difficult = false; - g.imgid = gt_imageid->flat()(i); - g.score = 1.0; - g.box = groundtruth_boxes[i]; - g.ignore = static_cast(gt_ignore->flat()(i)); - groundtruth.push_back(g); - } - - const Tensor* pd_bbox = nullptr; - OP_REQUIRES_OK(ctx, ctx->input("prediction_bbox", &pd_bbox)); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(pd_bbox->shape()), - errors::InvalidArgument("bbox must be a matrix, but get ", - pd_bbox->shape().DebugString())); - OP_REQUIRES(ctx, pd_bbox->dim_size(1) == 7, - errors::InvalidArgument("bbox must be [:, 7], but get ", - pd_bbox->shape().DebugString())); - const int m = pd_bbox->dim_size(0); - - const Tensor* pd_imageid = nullptr; - OP_REQUIRES_OK(ctx, ctx->input("prediction_imageid", &pd_imageid)); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(pd_imageid->shape()), - errors::InvalidArgument("imageid must be a vector, but get ", - pd_imageid->shape().DebugString())); - OP_REQUIRES(ctx, pd_imageid->dim_size(0) == m, - errors::InvalidArgument("imageid shape mismatch, get ", - pd_imageid->shape().DebugString())); - - const Tensor* pd_score = nullptr; - OP_REQUIRES_OK(ctx, ctx->input("prediction_score", &pd_score)); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(pd_score->shape()), - errors::InvalidArgument("score must be a vector, but get ", - pd_score->shape().DebugString())); - OP_REQUIRES(ctx, pd_score->dim_size(0) == m, - errors::InvalidArgument("score shape mismatch, get ", - pd_score->shape().DebugString())); - - const Tensor* pd_ignore = nullptr; - OP_REQUIRES_OK(ctx, ctx->input("prediction_ignore", &pd_ignore)); - OP_REQUIRES(ctx, pd_imageid->shape().IsSameSize(pd_ignore->shape()), - errors::InvalidArgument("pt_ignore shape mismatch: ", - pd_ignore->shape().DebugString())); - - const Tensor* iou_threshold = nullptr; - OP_REQUIRES_OK(ctx, ctx->input("iou_threshold", &iou_threshold)); - OP_REQUIRES( - ctx, TensorShapeUtils::IsScalar(iou_threshold->shape()), - errors::InvalidArgument("iou_threshold must be a scalar, but get ", - iou_threshold->shape().DebugString())); - - std::vector> prediction; - std::vector prediction_boxes = - box::ParseBoxesFromTensor(*pd_bbox); - - prediction.reserve(m); - for (int i = 0; i < m; ++i) { - image::Detection p; - p.difficult = false; - p.imgid = pd_imageid->flat()(i); - p.score = pd_score->flat()(i); - p.box = prediction_boxes[i]; - p.ignore = static_cast(pd_ignore->flat()(i)); - prediction.push_back(p); - } - - // Prediction score and binary indicator whether the prediction is a - // positive hit for each prediction for the specified IOU threshold. - Tensor out_score_and_hit(DT_FLOAT, {m, 2}); - auto t_out_score_and_hit = out_score_and_hit.matrix(); - std::vector is_hit; - is_hit.reserve(m); - std::vector score; - score.reserve(m); - - Tensor out_ap(DT_FLOAT, {}); - Tensor out_pr(DT_FLOAT, {num_recall_points_, 2}); - auto t_out_pr = out_pr.matrix(); - image::AveragePrecision::Options opts; - opts.iou_threshold = iou_threshold->scalar()(); - opts.num_recall_points = num_recall_points_; - std::vector pr; - if (ap_algorithm_ == APAlgorithm::kKITTI) { - out_ap.scalar()() = - image::AveragePrecision(opts).FromBoxesKITTI( - groundtruth, prediction, &pr, &is_hit, &score); - } else { - out_ap.scalar()() = - image::AveragePrecision(opts).FromBoxes( - groundtruth, prediction, &pr); - // TODO(shlens): Potentially implement this statistic for the VOC analysis - // as well. In the mean time, we just insert dummy values. - for (int i = 0; i < prediction.size(); ++i) { - score.push_back(-1.0); - is_hit.push_back(-1.0); - } - } - // Save out the detection score and the is_hit binary indicator. - CHECK_EQ(prediction.size(), is_hit.size()); - for (int i = 0; i < prediction.size(); ++i) { - t_out_score_and_hit(i, 0) = static_cast(score[i]); - t_out_score_and_hit(i, 1) = static_cast(is_hit[i]); - } - - if (pr.size() < num_recall_points_) { - LOG(WARNING) << "PR array size smaller than expected, expect: " - << num_recall_points_ << ", got " << pr.size(); - } - for (int i = 0; i < pr.size() && i < num_recall_points_; ++i) { - t_out_pr(i, 0) = pr[i].p; - t_out_pr(i, 1) = pr[i].r; - } - for (int i = pr.size(); i < num_recall_points_; ++i) { - t_out_pr(i, 0) = 0; - t_out_pr(i, 1) = i / static_cast(num_recall_points_ - 1); - } - ctx->set_output(0, out_ap); - ctx->set_output(1, out_pr); - ctx->set_output(2, out_score_and_hit); - } - - private: - int num_recall_points_ = -1; - APAlgorithm ap_algorithm_; -}; - -REGISTER_KERNEL_BUILDER(Name("AveragePrecision3D").Device(DEVICE_CPU), AP3DOp); - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/average_precision_3d_op_test.py b/lingvo/tasks/car/ops/average_precision_3d_op_test.py deleted file mode 100644 index 3320536a..00000000 --- a/lingvo/tasks/car/ops/average_precision_3d_op_test.py +++ /dev/null @@ -1,131 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.tasks.car.ops.car_metrics_ops.""" - -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import ops -import numpy as np - - -class ImageMetricsOpsTest(test_utils.TestCase): - - def _GenerateRandomBBoxes(self, num_images, num_bboxes): - xyz = np.random.uniform(low=-1.0, high=1.0, size=(num_bboxes, 3)) - dimension = np.random.uniform(low=0.1, high=1.0, size=(num_bboxes, 3)) - rotation = np.random.uniform(low=-np.pi, high=np.pi, size=(num_bboxes, 1)) - bboxes = np.concatenate([xyz, dimension, rotation], axis=-1) - imageid = np.random.randint(0, num_images, size=[num_bboxes]) - scores = np.random.uniform(size=[num_bboxes]) - return bboxes, imageid, scores - - def _GetAP(self, gt_bbox, gt_imgid, pd_bbox, pd_imgid, pd_score, algorithm): - g = tf.Graph() - with g.as_default(): - iou, pr, score_and_hit = ops.average_precision3d( - iou_threshold=0.5, - groundtruth_bbox=gt_bbox, - groundtruth_imageid=gt_imgid, - groundtruth_ignore=tf.zeros_like(gt_imgid, dtype=tf.int32), - prediction_bbox=pd_bbox, - prediction_imageid=pd_imgid, - prediction_score=pd_score, - prediction_ignore=tf.zeros_like(pd_imgid, dtype=tf.int32), - num_recall_points=41, - algorithm=algorithm) - with self.session(graph=g): - val = self.evaluate([iou, pr, score_and_hit]) - return val - - def testAPKITTI(self): - k, n, m = 10, 100, 20 - gt_bbox, gt_imgid, _ = self._GenerateRandomBBoxes(k, n) - pd_bbox, pd_imgid, pd_score = self._GenerateRandomBBoxes(k, m) - # IoU between two set of random boxes; - iou, _, score_and_hit = self._GetAP( - gt_bbox, gt_imgid, pd_bbox, pd_imgid, pd_score, algorithm='KITTI') - self.assertAllEqual(score_and_hit.shape, (m, 2)) - self.assertTrue(0 <= iou and iou <= 1.0) - - # Make the predictions be a duplicate of the ground truth to emulate - # perfect detection. - iou, _, score_and_hit = self._GetAP( - gt_bbox, gt_imgid, gt_bbox, gt_imgid, np.ones(n), algorithm='KITTI') - self.assertAllEqual(score_and_hit.shape, (n, 2)) - self.assertAllEqual(score_and_hit[:, 1], np.ones(n)) - self.assertEqual(1, iou) - - # Ditto as above but make the detection scores unique so that one can test - # that the scores are correctly returned. - iou, _, score_and_hit = self._GetAP( - gt_bbox, - gt_imgid, - gt_bbox, - gt_imgid, - np.linspace(0, 1, n), - algorithm='KITTI') - self.assertAllEqual(score_and_hit.shape, (n, 2)) - self.assertAllClose(score_and_hit[:, 0], np.linspace(0, 1, n)) - self.assertAllEqual(score_and_hit[:, 1], np.ones(n)) - self.assertEqual(1, iou) - - # IoU of empty detection - iou, _, score_and_hit = self._GetAP( - gt_bbox, gt_imgid, pd_bbox, pd_imgid + n, pd_score, algorithm='KITTI') - self.assertAllEqual(score_and_hit.shape, (m, 2)) - self.assertAllEqual(score_and_hit[:, 1], np.zeros(m)) - self.assertEqual(0, iou) - - def testAPVOC(self): - k, n, m = 10, 100, 20 - gt_bbox, gt_imgid, _ = self._GenerateRandomBBoxes(k, n) - pd_bbox, pd_imgid, pd_score = self._GenerateRandomBBoxes(k, m) - # IoU between two set of random boxes; - iou, _, _ = self._GetAP( - gt_bbox, gt_imgid, pd_bbox, pd_imgid, pd_score, algorithm='VOC') - self.assertTrue(0 <= iou and iou <= 1.0) - # IoU of perfect detection - iou, _, score_and_hit = self._GetAP( - gt_bbox, gt_imgid, gt_bbox, gt_imgid, np.ones(n), algorithm='VOC') - # Just check that dummy values are returned. - self.assertAllEqual(score_and_hit.shape, (n, 2)) - self.assertAllEqual(score_and_hit, -1.0 * np.ones(shape=(n, 2))) - - self.assertEqual(1, iou) - # IoU of empty detection - iou, _, _ = self._GetAP( - gt_bbox, gt_imgid, pd_bbox, pd_imgid + n, pd_score, algorithm='VOC') - self.assertEqual(0, iou) - - def testAllZeroValue(self): - k, n, m = 10, 100, 20 - gt_bbox, gt_imgid, _ = self._GenerateRandomBBoxes(k, n) - pd_bbox, pd_imgid, pd_score = self._GenerateRandomBBoxes(k, m) - # IoU between two set of random boxes; - iou, pr, _ = self._GetAP( - gt_bbox * 0, - gt_imgid * 0, - pd_bbox * 0, - pd_imgid * 0, - pd_score * 0, - algorithm='KITTI') - self.assertEqual(0, iou) - self.assertAllEqual(pr.shape, (41, 2)) - self.assertAllEqual(np.zeros(41), pr[:, 0]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/ops/box_util.cc b/lingvo/tasks/car/ops/box_util.cc deleted file mode 100644 index ddd6d29b..00000000 --- a/lingvo/tasks/car/ops/box_util.cc +++ /dev/null @@ -1,327 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/tasks/car/ops/box_util.h" - -#include -#include - -namespace tensorflow { -namespace lingvo { -namespace box { - -const double kEPS = 1e-8; - -// Min,max box dimensions (length, width, height). Boxes with dimensions that -// exceed these values will have box intersections of 0. -constexpr double kMinBoxDim = 1e-3; -constexpr double kMaxBoxDim = 1e6; - -// A line with the representation a*x + b*y + c = 0. -struct Line { - double a = 0; - double b = 0; - double c = 0; - - Line(const Vertex& v1, const Vertex& v2) - : a(v2.y - v1.y), b(v1.x - v2.x), c(v2.x * v1.y - v2.y * v1.x) {} - - // Computes the line value for a vertex v as a * v.x + b * v.y + c - double LineValue(const Vertex& v) const { return a * v.x + b * v.y + c; } - - // Computes the intersection point with the other line. - Vertex IntersectionPoint(const Line& other) const { - const double w = a * other.b - b * other.a; - CHECK_GT(std::fabs(w), kEPS) << "No intersection between the two lines."; - return Vertex((b * other.c - c * other.b) / w, - (c * other.a - a * other.c) / w); - } -}; - -// Computes the coordinates of its four vertices given a 2D rotated box, -std::vector ComputeBoxVertices(const double cx, const double cy, - const double w, const double h, - const double heading) { - const double dxcos = (w / 2.) * std::cos(heading); - const double dxsin = (w / 2.) * std::sin(heading); - const double dycos = (h / 2.) * std::cos(heading); - const double dysin = (h / 2.) * std::sin(heading); - return {Vertex(cx - dxcos + dysin, cy - dxsin - dycos), - Vertex(cx + dxcos + dysin, cy + dxsin - dycos), - Vertex(cx + dxcos - dysin, cy + dxsin + dycos), - Vertex(cx - dxcos - dysin, cy - dxsin + dycos)}; -} - -// Computes the intersection points between two rotated boxes, by following: -// -// 1. Initiazlizes the current intersection points with the vertices of one box, -// and the other box is taken as the cutting box; -// -// 2. For each cutting line in the cutting box (four cutting lines in total): -// For each point in the current intersection points: -// If the point is inside of the cutting line: -// Adds it to the new intersection points; -// if current point and its next point are in the opposite side of the -// cutting line: -// Computes the line of current points and its next point as tmp_line; -// Computes the intersection point between the cutting line and -// tmp_line; -// Adds the intersection point to the new intersection points; -// After checking each cutting line, sets current intersection points as -// new intersection points; -// -// 3. Returns the final intersection points. -std::vector ComputeIntersectionPoints( - const std::vector& rbox_1, const std::vector& rbox_2) { - std::vector intersection = rbox_1; - const int vertices_len = rbox_2.size(); - for (int i = 0; i < rbox_2.size(); ++i) { - const int len = intersection.size(); - if (len <= 2) { - break; - } - const Vertex& p = rbox_2[i]; - const Vertex& q = rbox_2[(i + 1) % vertices_len]; - Line cutting_line(p, q); - // Computes line value. - std::vector line_values; - line_values.reserve(len); - for (int j = 0; j < len; ++j) { - line_values.push_back(cutting_line.LineValue(intersection[j])); - } - // Updates current intersection points. - std::vector new_intersection; - for (int j = 0; j < len; ++j) { - const double s_val = line_values[j]; - const Vertex& s = intersection[j]; - // Adds the current vertex. - if (s_val <= 0 || std::fabs(s_val) <= kEPS) { - new_intersection.push_back(s); - } - const double t_val = line_values[(j + 1) % len]; - // Skips the checking of intersection point if the next vertex is on the - // line. - if (std::fabs(t_val) <= kEPS) { - continue; - } - // Adds the intersection point. - if ((s_val > 0 && t_val < 0) || (s_val < 0 && t_val > 0)) { - Line s_t_line(s, intersection[(j + 1) % len]); - new_intersection.push_back(cutting_line.IntersectionPoint(s_t_line)); - } - } - intersection = new_intersection; - } - return intersection; -} - -// Computes the area of a convex polygon, -double ComputePolygonArea(const std::vector& convex_polygon) { - const int len = convex_polygon.size(); - if (len <= 2) { - return 0; - } - double area = 0; - for (int i = 0; i < len; ++i) { - const Vertex& p = convex_polygon[i]; - const Vertex& q = convex_polygon[(i + 1) % len]; - area += p.x * q.y - p.y * q.x; - } - return std::fabs(0.5 * area); -} - -RotatedBox2D::RotatedBox2D(const double cx, const double cy, const double w, - const double h, const double heading) - : cx_(cx), cy_(cy), w_(w), h_(h), heading_(heading) { - // Compute loose bounds on dimensions of box that doesn't require computing - // full intersection. We can do this by trying to compute the largest circle - // swept by rotating the box around its center. The radius of that circle - // is the length of the ray from the center to the box corner. The upper - // bound for this value is the length of the longer dimension divided by two - // and then multiplied by root(2) (worst-case being a square box); we choose - // 1.5 as slightly higher than root(2), and then use these extrema to do - // simple extrema box checks without having to compute the true cos/sin value. - double max_dim = std::max(w_, h_) / 2. * 1.5; - loose_min_x_ = cx_ - max_dim; - loose_max_x_ = cx_ + max_dim; - loose_min_y_ = cy_ - max_dim; - loose_max_y_ = cy_ + max_dim; - - extreme_box_dim_ = (w_ <= kMinBoxDim || h_ <= kMinBoxDim); - extreme_box_dim_ |= (w_ >= kMaxBoxDim || h_ >= kMaxBoxDim); -} - -double RotatedBox2D::Area() const { - if (area_ < 0) { - const double area = ComputePolygonArea(box_vertices()); - area_ = std::fabs(area) <= kEPS ? 0 : area; - } - return area_; -} - -const std::vector& RotatedBox2D::box_vertices() const { - if (box_vertices_.empty()) { - box_vertices_ = ComputeBoxVertices(cx_, cy_, w_, h_, heading_); - } - - return box_vertices_; -} - -bool RotatedBox2D::NonZeroAndValid() const { return !extreme_box_dim_; } - -bool RotatedBox2D::MaybeIntersects(const RotatedBox2D& other) const { - // If the box dimensions of either box are too small / large, - // assume they are not well-formed boxes (otherwise we are - // subject to issues due to catastrophic cancellation). - if (extreme_box_dim_ || other.extreme_box_dim_) { - return false; - } - - // Check whether the loose extrema overlap -- if not, then there is - // no chance that the two boxes overlap even when computing the true, - // more expensive overlap. - if ((loose_min_x_ > other.loose_max_x_) || - (loose_max_x_ < other.loose_min_x_) || - (loose_min_y_ > other.loose_max_y_) || - (loose_max_y_ < other.loose_min_y_)) { - return false; - } - - return true; -} - -double RotatedBox2D::Intersection(const RotatedBox2D& other) const { - // Do a fast intersection check - if the boxes are not near each other - // then we can return early. If they are close enough to maybe overlap, - // we do the full check. - if (!MaybeIntersects(other)) { - return 0.0; - } - - // Computes the intersection polygon. - const std::vector intersection_polygon = - ComputeIntersectionPoints(box_vertices(), other.box_vertices()); - // Computes the intersection area. - const double intersection_area = ComputePolygonArea(intersection_polygon); - - return std::fabs(intersection_area) <= kEPS ? 0 : intersection_area; -} - -double RotatedBox2D::IoU(const RotatedBox2D& other) const { - // Computes the intersection area. - const double intersection_area = Intersection(other); - if (intersection_area == 0) { - return 0; - } - // Computes the union area. - const double union_area = Area() + other.Area() - intersection_area; - if (std::fabs(union_area) <= kEPS) { - return 0; - } - return intersection_area / union_area; -} - -std::vector ParseBoxesFromTensor(const Tensor& boxes_tensor) { - int num_boxes = boxes_tensor.dim_size(0); - - const auto t_boxes_tensor = boxes_tensor.matrix(); - - std::vector bboxes3d; - bboxes3d.reserve(num_boxes); - for (int i = 0; i < num_boxes; ++i) { - const double center_x = t_boxes_tensor(i, 0); - const double center_y = t_boxes_tensor(i, 1); - const double center_z = t_boxes_tensor(i, 2); - const double dimension_x = t_boxes_tensor(i, 3); - const double dimension_y = t_boxes_tensor(i, 4); - const double dimension_z = t_boxes_tensor(i, 5); - const double heading = t_boxes_tensor(i, 6); - const double z_min = center_z - dimension_z / 2; - const double z_max = center_z + dimension_z / 2; - RotatedBox2D box2d(center_x, center_y, dimension_x, dimension_y, heading); - if (dimension_x <= 0 || dimension_y <= 0) { - bboxes3d.emplace_back(RotatedBox2D(), z_min, z_max); - } else { - bboxes3d.emplace_back(box2d, z_min, z_max); - } - } - return bboxes3d; -} - -bool Upright3DBox::NonZeroAndValid() const { - // If min is larger than max, the upright box is invalid. - // - // If the min and max are equal, the height of the box is 0. and thus the box - // is zero. - if (z_min - z_max >= 0.) { - return false; - } - - return rbox.NonZeroAndValid(); -} - -double Upright3DBox::IoU(const Upright3DBox& other) const { - // Check that both boxes are non-zero and valid. Otherwise, - // return 0. - if (!NonZeroAndValid() || !other.NonZeroAndValid()) { - return 0; - } - - // Quickly check whether z's overlap; if they don't, we can return 0. - const double z_inter = - std::max(.0, std::min(z_max, other.z_max) - std::max(z_min, other.z_min)); - if (z_inter == 0) { - return 0; - } - - const double base_inter = rbox.Intersection(other.rbox); - if (base_inter == 0) { - return 0; - } - - const double volume_1 = rbox.Area() * (z_max - z_min); - const double volume_2 = other.rbox.Area() * (other.z_max - other.z_min); - const double volume_inter = base_inter * z_inter; - const double volume_union = volume_1 + volume_2 - volume_inter; - return volume_inter > 0 ? volume_inter / volume_union : 0; -} - -double Upright3DBox::Overlap(const Upright3DBox& other) const { - // Check that both boxes are non-zero and valid. Otherwise, - // return 0. - if (!NonZeroAndValid() || !other.NonZeroAndValid()) { - return 0; - } - - const double z_inter = - std::max(.0, std::min(z_max, other.z_max) - std::max(z_min, other.z_min)); - if (z_inter == 0) { - return 0; - } - - const double base_inter = rbox.Intersection(other.rbox); - if (base_inter == 0) { - return 0; - } - - const double volume_1 = rbox.Area() * (z_max - z_min); - const double volume_inter = base_inter * z_inter; - // Normalizes intersection of volume by the volume of this box. - return volume_inter > 0 ? volume_inter / volume_1 : 0; -} - -} // namespace box -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/box_util.h b/lingvo/tasks/car/ops/box_util.h deleted file mode 100644 index 51273efb..00000000 --- a/lingvo/tasks/car/ops/box_util.h +++ /dev/null @@ -1,143 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_BOX_UTIL_H_ -#define THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_BOX_UTIL_H_ - -#include -#include - -#include "tensorflow/core/framework/tensor.h" - -namespace tensorflow { -namespace lingvo { -namespace box { - -struct Vertex; - -// A rotated 2D bounding box represented as (cx, cy, w, h, r). cx, cy are the -// box center coordinates; w, h are the box width and height; heading is the -// rotation angle in radian relative to the 'positive x' direction. -class RotatedBox2D { - public: - // Creates an empty rotated 2D box. - RotatedBox2D() : RotatedBox2D(0, 0, 0, 0, 0) {} - - RotatedBox2D(const double cx, const double cy, const double w, const double h, - const double heading); - - // Returns the area of the box. - double Area() const; - - // Returns the intersection area between this box and the given box. - double Intersection(const RotatedBox2D& other) const; - - // Returns the IoU between this box and the given box. - double IoU(const RotatedBox2D& other) const; - - // Returns true if the box is valid (width and height are not extremely - // large or small). - bool NonZeroAndValid() const; - - private: - // Computes / caches box_vertices_ calculation. - const std::vector& box_vertices() const; - - // Returns true if this box and 'other' might intersect. - // - // If this returns false, the two boxes definitely do not intersect. If this - // returns true, it is still possible that the two boxes do not intersect, and - // the more expensive intersection code will be called. - bool MaybeIntersects(const RotatedBox2D& other) const; - - double cx_ = 0; - double cy_ = 0; - double w_ = 0; - double h_ = 0; - double heading_ = 0; - - // Loose boundaries for fast intersection test. - double loose_min_x_ = -1; - double loose_max_x_ = -1; - double loose_min_y_ = -1; - double loose_max_y_ = -1; - - // True if the dimensions of the box are very small or very large in any - // dimension. - bool extreme_box_dim_ = false; - - // The following fields are computed on demand. They are logically - // const. - - // Cached area. Access via Area() public API. - mutable double area_ = -1; - - // Stores the vertices of the box. Access via box_vertices(). - mutable std::vector box_vertices_; -}; - -// A 3D box of 7-DOFs: only allows rotation around the z-axis. -struct Upright3DBox { - RotatedBox2D rbox = RotatedBox2D(); - double z_min = 0; - double z_max = 0; - - // Creates an empty rotated 3D box. - Upright3DBox() = default; - - // Creates a 3D box from the raw input data with size 7. The data format is - // (center_x, center_y, center_z, dimension_x, dimension_y, dimension_z, - // heading) - Upright3DBox(const std::vector& raw) - : rbox(raw[0], raw[1], raw[3], raw[4], raw[6]), - z_min(raw[2] - raw[5] / 2.0), - z_max(raw[2] + raw[5] / 2.0) {} - - Upright3DBox(const RotatedBox2D& rb, const double z_min, const double z_max) - : rbox(rb), z_min(z_min), z_max(z_max) {} - - // Computes intersection over union (of the volume). - double IoU(const Upright3DBox& other) const; - - // Computes overlap: intersection of this box and the given box normalized - // over the volume of this box. - double Overlap(const Upright3DBox& other) const; - - // Returns true if the box is valid (width and height are not extremely - // large or small, and zmin < zmax). - bool NonZeroAndValid() const; -}; - -// Converts a [N, 7] tensor to a vector of N Upright3DBox objects. -std::vector ParseBoxesFromTensor(const Tensor& boxes_tensor); - -// A vertex with (x, y) coordinate. -// -// This is an internal implementation detail of RotatedBox2D. -struct Vertex { - double x = 0; - double y = 0; - - // Creates an empty Vertex. - Vertex() = default; - - Vertex(const double x, const double y) : x(x), y(y) {} -}; - -} // namespace box -} // namespace lingvo -} // namespace tensorflow - -#endif // THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_BOX_UTIL_H_ diff --git a/lingvo/tasks/car/ops/car_ops.cc b/lingvo/tasks/car/ops/car_ops.cc deleted file mode 100644 index e9e04cc0..00000000 --- a/lingvo/tasks/car/ops/car_ops.cc +++ /dev/null @@ -1,258 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "tensorflow/core/framework/op.h" -#include "tensorflow/core/framework/shape_inference.h" - -namespace tensorflow { -namespace { - -REGISTER_OP("PairwiseIou3D") - .Input("boxes_a: float") - .Input("boxes_b: float") - .Output("iou: float") - .SetShapeFn([](tensorflow::shape_inference::InferenceContext* c) { - c->set_output( - 0, c->MakeShape({c->Dim(c->input(0), 0), c->Dim(c->input(1), 0)})); - return tensorflow::Status::OK(); - }) - .Doc(R"doc( -Calculate pairwise IoUs between two set of 3D bboxes. Every bbox is represented -as [center_x, center_y, center_z, dim_x, dim_y, dim_z, heading]. -boxes_a: A tensor of shape [num_boxes_a, 7] -boxes_b: A tensor of shape [num_boxes_b, 7] -)doc"); - -REGISTER_OP("PointToGrid") - .Input("points: float") - .Output("output_points: float") - .Output("grid_centers: float") - .Output("num_points: int32") - .Attr("num_points_per_cell: int") - .Attr("x_intervals: int") - .Attr("y_intervals: int") - .Attr("z_intervals: int") - .Attr("x_range: list(float)") - .Attr("y_range: list(float)") - .Attr("z_range: list(float)") - .SetShapeFn([](shape_inference::InferenceContext* c) { - auto feature_dim = c->Dim(c->input(0), 1); - int gx, gy, gz, n; - TF_RETURN_IF_ERROR(c->GetAttr("x_intervals", &gx)); - TF_RETURN_IF_ERROR(c->GetAttr("y_intervals", &gy)); - TF_RETURN_IF_ERROR(c->GetAttr("z_intervals", &gz)); - TF_RETURN_IF_ERROR(c->GetAttr("num_points_per_cell", &n)); - c->set_output(0, c->MakeShape({gx, gy, gz, n, feature_dim})); - c->set_output(1, c->MakeShape({gx, gy, gz, 3})); - c->set_output(2, c->MakeShape({gx, gy, gz})); - return ::tensorflow::Status::OK(); - }) - .Doc(R"doc( -Re-organize input points into equally spaced grids. Points in each grid cell are -shuffled. When not enough available points, the center of each cell with all 0 -on feature dimensions will be used as padding. - -The number of points in each grid cell. I.e., -output_points[i, j, k, num_points[i, j, k]\:, \:] are padded points. - -points: [n, d]. d >= 3 and the first 3 dimensions are treated as x,y,z. -output_points: - [x_intervals, y_intervals, z_intervals, num_per_grid, d]. -grid_centers: - [x_intervals, y_intervals, z_intervals, 3]. Grid cell centers. -num_points: - [x_intervals, y_intervals, z_intervals]. -num_points_per_cell: int. Number of points to keep in each cell. -x_intervals: int. Number of cells along x-axis. -y_intervals: int. Number of cells along y-axis. -z_intervals: int. Number of cells along z-axis. -x_range: tuple of two scalars\: (xmin, xmax). Spatial span of the grid. -y_range: tuple of two scalars\: (ymin, ymax). Spatial span of the grid. -z_range: tuple of two scalars\: (zmin, zmax). Spatial span of the grid. -)doc"); - -REGISTER_OP("NonMaxSuppression3D") - .Input("bboxes: float") - .Input("scores: float") - .Input("nms_iou_threshold: float") - .Input("score_threshold: float") - .Output("bbox_indices: int32") - .Output("bbox_scores: float") - .Output("valid_mask: float") - .Attr("max_boxes_per_class: int") - .SetShapeFn([](tensorflow::shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle bboxes_shape; - shape_inference::ShapeHandle scores_shape; - shape_inference::ShapeHandle iou_threshold_shape; - shape_inference::ShapeHandle score_threshold_shape; - TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 2, &bboxes_shape)); - TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 2, &scores_shape)); - TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 1, &iou_threshold_shape)); - TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 1, &score_threshold_shape)); - - int max_boxes_per_class; - TF_RETURN_IF_ERROR( - c->GetAttr("max_boxes_per_class", &max_boxes_per_class)); - auto num_classes = c->Dim(c->input(1), 1); - - shape_inference::ShapeHandle output_shape = - c->MakeShape({num_classes, max_boxes_per_class}); - c->set_output(0, output_shape); - c->set_output(1, output_shape); - c->set_output(2, output_shape); - return tensorflow::Status::OK(); - }) - .Doc(R"doc( -Greedily selects the top subset of 3D (7 DOF format) bounding boxes per class. - -This implementation is rotation and class aware, and for each class takes the -best boxes that are above our score_threshold and also don't overlap more than -our nms_iou_threshold with any better scoring boxes. - -bboxes: A tf.float32 Tensor of shape [num_bboxes, 7] where the box is of - format [center_x, center_y, center_z, dim_x, dim_y, dim_z, heading]. -scores: A tf.float32 Tensor of shape [num_bboxes, num_classes] with a score - per box for each class. -nms_iou_threshold: A tf.float32 Tensor of shape [num_classes] specifying the - max overlap between two boxes we allow before saying these boxes overlap, - and suppressing one of them. -score_threshold: A tf.float32 Tensor of shape [num_classes] specifying the - minimum class score (per class) a box can have before it is removed. -max_boxes_per_class: An integer specifying how many (at most) boxes to - return for each class. -bbox_indices: - [num_classes, max_boxes_per_class] with the indices of selected boxes - for each class. -bbox_scores: - [num_classes, max_boxes_per_class] with the score of selected boxes - for each class. -valid_mask: - [num_classes, max_boxes_per_class] with a 1 for a valid box and a 0 - for invalid boxes for each class. -)doc"); - -REGISTER_OP("AveragePrecision3D") - .Input("iou_threshold: float") - .Input("groundtruth_bbox: float") - .Input("groundtruth_imageid: int32") - .Input("groundtruth_ignore: int32") - .Input("prediction_bbox: float") - .Input("prediction_imageid: int32") - .Input("prediction_ignore: int32") - .Input("prediction_score: float") - .Output("average_precision: float") - .Output("precision_recall: float") - .Output("score_and_hit: float") - .Attr("num_recall_points: int >= 1 = 1") - .Attr("algorithm: string = \"KITTI\"") - .Doc(R"doc( -Computes average precision for 3D bounding boxes. - -The output PR is sorted by recall in descending order. When there isn't enough -data for num_recall_points + 1 sample points, this tensor will be zero-padded. - -iou_threshold: IoU threshold. -groundtruth_bbox: [N, 7]. N ground truth bounding boxes. -groundtruth_imageid: [N]. N image ids for ground truth bounding boxes. -groundtruth_ignore: [N]. Valid values are 0 - Don't ignore; 1 - Ignore the - first match; 2 - Ignore all matches. -prediction_bbox: [M, 7]. M predicted bounding boxes. -prediction_imageid: [M]. M image ids for the predicted bounding boxes. -prediction_score: [M]. M scores for each predicted bounding box. -prediction_ignore: [N]. The ignore types for predictions. Currently only used by - the KITTI AP. Valid values are 0 - Don't ignore; 1 - Ignore the first match. -average_precision: - A scalar. The AP metric. -precision_recall: - [num_recall_points, 2]. List of PR points. -score_and_hit: - [M, 2] Prediction score and corresponding binary indication whether a - prediction detected a ground truth item. -algorithm: string. One of ["KITTI", "VOC"]. See this paper "Supervised - learning and evaluation of KITTI's cars detector with DPM", Section III.A for - the differences between KITTI AP and VOC AP. -)doc"); - -// TODO(vrv): Convert some of the attrs to inputs. -REGISTER_OP("SamplePoints") - .Input("points: float") - .Input("points_padding: float") - .Input("num_seeded_points: int32") - .Output("center: int32") - .Output("center_padding: float") - .Output("indices: int32") - .Output("indices_padding: float") - .Attr("center_selector: string") - .Attr("neighbor_sampler: string") - .Attr("neighbor_algorithm: string = \"auto\"") - .Attr("num_centers: int") - .Attr("center_z_min: float") - .Attr("center_z_max: float") - .Attr("num_neighbors: int") - .Attr("max_distance: float") - .Attr("random_seed: int = -1") - .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { - shape_inference::ShapeHandle points; - TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 3, &points)); - shape_inference::DimensionHandle batch_size = c->Dim(points, 0); - - int num_centers, num_neighbors; - TF_RETURN_IF_ERROR(c->GetAttr("num_centers", &num_centers)); - TF_RETURN_IF_ERROR(c->GetAttr("num_neighbors", &num_neighbors)); - c->set_output(0, c->MakeShape({batch_size, num_centers})); - c->set_output(1, c->MakeShape({batch_size, num_centers})); - c->set_output(2, c->MakeShape({batch_size, num_centers, num_neighbors})); - c->set_output(3, c->MakeShape({batch_size, num_centers, num_neighbors})); - return ::tensorflow::Status::OK(); - }) - .Doc(R"doc( -Sample points among 'points'. - -points: [B, N, K]. B is the batch size; N is the number of points; K is the - number of dimensions of each point. -points_padding: [B, N]. 0/1 padding of points. If points_padding[b, i] is 0., - points[b, i, :] are valid point coordinates. Otherwise, point[b, i, :] are - all zeros. -num_seeded_points: If num_seeded_points > 0, then the first - num_seeded_points in points are considered to be seeded in the FPS - sampling. Note that we assume that these points are *not* padded, and do - not check padding when seeding them. -center: - [B, M]. the indices of selected centers. -center_padding: - [B, M]. If center_padding[b, i] is 0., center[b, i], - indices[b, i, :] and indices_padding[b, i, :] are valid sampled center. - Otherwise, center_padding[b, i] is 1.0, and center[b, i] and indices[b, i, :] - are all zeros while indices_padding[b, i, :] are all 1.0. -indices: - [B, M, P]. the indices of selected points. -indices_padding: - [B, M, P]. 0/1 padding of indices. -center_selector: Valid options - 'farthest', 'uniform'. -neighbor_sampler: Valid options - 'uniform', 'closest'. -neighbor_algorithm: Valid options - 'auto', 'hash'. -num_centers: The number of centers to sample for each batch example (M). -center_z_min: Points with z less than center_z_min are not considered for - center selection. -center_z_max: Points with z greater than center_z_max are not considered - for center selection. -num_neighbors: Sample these many points within the neighborhood (P). -max_distance: Points with L2 distances from a center larger than this - threshold are not considered to be in the neighborhood. -random_seed: The random seed. -)doc"); - -} // namespace -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/image_metrics.cc b/lingvo/tasks/car/ops/image_metrics.cc deleted file mode 100644 index d1ecfd54..00000000 --- a/lingvo/tasks/car/ops/image_metrics.cc +++ /dev/null @@ -1,156 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include "lingvo/tasks/car/ops/image_metrics.h" - -#include -#include - -namespace tensorflow { -namespace lingvo { -namespace image { - -float Box2D::Length(const Box2D::Interval& a) { - return std::max(0.f, a.max - a.min); -} - -float Box2D::Intersection(const Box2D::Interval& a, const Box2D::Interval& b) { - Interval c; - c.min = std::max(a.min, b.min); - c.max = std::min(a.max, b.max); - return Length(c); -} - -float Box2D::Area() const { return Length(x) * Length(y); } - -float Box2D::Intersection(const Box2D& other) const { - return Intersection(x, other.x) * Intersection(y, other.y); -} - -float Box2D::Union(const Box2D& other) const { - return Area() + other.Area() - Intersection(other); -} - -float Box2D::IoU(const Box2D& other) const { - const float total = Union(other); - if (total > 0) { - return Intersection(other) / total; - } else { - return 0.0; - } -} - -float Box2D::Overlap(const Box2D& other) const { - const float intersection = Intersection(other); - return intersection > 0 ? intersection / Area() : 0.0; -} - -namespace KITTI { -bool IsBetterMatch(const MatchResult& match1, const MatchResult& match2, - MatchingCriterion criterion) { - // Always true if match2 is "unmatched". - if (match2.matched_idx == kUnMatched) { - return true; - } - if (criterion == kBestScore) { - return match1.detection_score > match2.detection_score; - } else if (criterion == kBestIoU) { - if (match1.pd_ignore_type == kDontIgnore) { - if (match2.pd_ignore_type != kDontIgnore || - match1.matched_overlap > match2.matched_overlap) { - return true; - } - } - } - return false; -} - -float ComputePrecision( - const std::unordered_map>& pd_assignments, - const float score_threshold) { - int total = 0; - int tp = 0; - int self_ignore = 0, match_ignore = 0, unmatched = 0; - for (const auto& per_image_assignment : pd_assignments) { - for (const auto& match : per_image_assignment.second) { - if (match.detection_score >= score_threshold && - match.gt_ignore_type == kDontIgnore && - match.pd_ignore_type == kDontIgnore) { - total++; - if (match.matched_idx != kUnMatched) { - tp++; - } - } - // Only for debugging. - if (match.pd_ignore_type != kDontIgnore) { - self_ignore++; - } - if (match.gt_ignore_type != kDontIgnore) { - match_ignore++; - } - if (match.matched_idx == kUnMatched) { - unmatched++; - } - } - } - - if (total == 0) { - return 1; - } else { - return static_cast(tp) / total; - } -} - -std::vector FindThresholds( - const std::unordered_map>& gt_assignment, - const int num_recall_points) { - std::vector thresholds; - std::vector all_matched_scores; - int total_gt = 0; - for (const auto& per_img_assignmeht : gt_assignment) { - for (const auto& match : per_img_assignmeht.second) { - if (match.gt_ignore_type == kDontIgnore) { - if (match.matched_idx != kUnMatched && - match.pd_ignore_type == kDontIgnore) { - all_matched_scores.push_back(match.detection_score); - } - total_gt++; - } - } - } - if (total_gt == 0) { - // There's no groundtruth to evaluate. We return an empty vector. - return thresholds; - } - std::sort(all_matched_scores.begin(), all_matched_scores.end(), - std::greater()); - for (int i = 1; i <= all_matched_scores.size(); ++i) { - const float left_recall = static_cast(i) / total_gt; - const float right_recall = static_cast(i + 1) / total_gt; - const float target_recall = - thresholds.size() / static_cast(num_recall_points - 1); - if (right_recall - target_recall >= target_recall - left_recall || - i == all_matched_scores.size()) { - thresholds.push_back(all_matched_scores[i - 1]); - } - } - return thresholds; -} - -} // namespace KITTI - -} // namespace image -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/image_metrics.h b/lingvo/tasks/car/ops/image_metrics.h deleted file mode 100644 index ea12ef6d..00000000 --- a/lingvo/tasks/car/ops/image_metrics.h +++ /dev/null @@ -1,468 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#ifndef THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_IMAGE_METRICS_H_ -#define THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_IMAGE_METRICS_H_ - -#include -#include -#include - -#include "tensorflow/core/framework/tensor.h" - -namespace tensorflow { -namespace lingvo { -namespace image { - -struct Box2D { - struct Interval { - float min = 0; - float max = 0; - }; - Interval x; - Interval y; - static float Length(const Interval& a); - static float Intersection(const Interval& a, const Interval& b); - float Area() const; - float Intersection(const Box2D& other) const; - float Union(const Box2D& other) const; - // Intersection of this box and the given box normalized over the union of - // this box and the given box. - float IoU(const Box2D& other) const; - // Intersection of this box and the given box normalized over the area of - // this box. - float Overlap(const Box2D& other) const; -}; - -// If the value is: -// - kDontIgnore: The object is included in this evaluation. -// - kIgnoreOneMatch: the first matched prediction bbox will be ignored. This -// is useful when this groundtruth object is not intended to be evaluated. -// - kIgnoreAllMatches: all matched prediction bbox will be ignored. Typically -// it is used to mark an area that has not been labeled. -enum IgnoreType { - kDontIgnore = 0, - kIgnoreOneMatch = 1, - kIgnoreAllMatches = 2, -}; - -template -struct Detection { - public: - bool difficult = false; - int64_t imgid = 0; - float score = 0; - BoxType box; - IgnoreType ignore = IgnoreType::kDontIgnore; -}; - -// Precision and recall. -struct PR { - float p = 0; - float r = 0; - PR(const float p_, const float r_) : p(p_), r(r_) {} -}; - -template -class AveragePrecision { - public: - // iou_threshold: A predicted box matches a ground truth box if and only if - // IoU between these two are larger than this iou_threshold. Default: 0.5. - // num_recall_points: AP is computed as the average of maximum precision at (1 - // + num_recall_points) recall levels. E.g., if num_recall_points is 10, - // recall levels are 0., 0.1, 0.2, ..., 0.9, 1.0. Default: 100 - // normalized_coordinate: If true, bounding box's coordinates are within [0, - // 1), and the interval is left-close and right-open. Hence, the length of an - // interval is simply interval.max - interval.min. Otherwise, coordinates are - // in the image pixel space and the interval is close on both ends. I.e., the - // length of an interval is interval.max - interval.min + 1. - // NOTE: voc_eval.py used by Fast-RCNN sets normalized_coordinate=false. - struct Options { - float iou_threshold = 0.5; - int num_recall_points = 100; - }; - AveragePrecision() : AveragePrecision(Options()) {} - explicit AveragePrecision(const Options& opts) : opts_(opts) {} - - // Given a sequence of precision-recall points ordered by the recall in - // non-increasing order, returns the average of maximum precisions at - // different recall values (0.0, 0.1, 0.2, ..., 0.9, 1.0). - // The p-r pairs at these fixed recall points will be written to pr_out, if - // it is not null_ptr. - float FromPRCurve(const std::vector& pr, - std::vector* pr_out = nullptr); - - // An axis aligned bounding box for an image with id 'imageid'. Score - // indicates its confidence. - // - // 'difficult' is a special bit specific to Pascal VOC dataset and tasks using - // the data. If 'difficult' is true, by convention, the box is often ignored - // during the AP calculation. I.e., if a predicted box matches a 'difficult' - // ground box, this predicted box is ignored as if the model does not make - // such a prediction. - - // Given the set of ground truth boxes and a set of predicted boxes, returns - // the average of the maximum precisions at different recall values. - float FromBoxes(const std::vector>& groundtruth, - const std::vector>& prediction, - std::vector* pr_out = nullptr); - - // The KITTI variant of AP calculation. - // Comparing with VOC/COCO AP, there are 3 main differences. - // - KITTI AP has two matching passes. The 1st pass matches every gt bbox - // with the pd bbox of highest score while having an IoU larger than the - // threshold. The 2nd pass matches every gt with the pd bbox of largest - // IoU. In VOC/COCO AP there's only one pass that does the same as KITTI's - // 1st pass. - // - // - The operating points on PR curve are decided by the result of the 1st - // pass whereas the actuall PR values are computed using 2nd pass. - // - // - Special "ignore" bit can be set on groundtruth or prediction bboxes. - // - On groundtruth, "ignore" means the bbox is of a neighbor class, or - // of a "difficuty" level that is not intended to be evaluated. - // - On prediction, "ignore" means the bbox, when projected to the - // camera image space, has its height smaller than a threshold. - float FromBoxesKITTI(const std::vector>& groundtruth, - const std::vector>& prediction, - std::vector* pr_out = nullptr, - std::vector* is_hit = nullptr, - std::vector* score = nullptr); - - private: - Options opts_; -}; - -template -float AveragePrecision::FromPRCurve(const std::vector& pr, - std::vector* pr_out) { - // Because pr[...] are ordered by recall, iterate backward to compute max - // precision. p(r) = max_{r' >= r} p(r') for r in 0.0, 0.1, 0.2, ..., 0.9, - // 1.0. Then, take the average of (num_recal_points) quantities. - float p = 0; - float sum = 0; - int r_level = opts_.num_recall_points; - for (int i = pr.size() - 1; i >= 0; --i) { - const PR& item = pr[i]; - if (i > 0) { - CHECK_GE(item.r, pr[i - 1].r); // Ordered. - } - // Because r takes values opts_.num_recall_points, opts_.num_recall_points - - // 1, ..., 0, the following condition is checking whether item.r crosses r / - // opts_.num_recall_points. I.e., 1.0, 0.90, ..., 0.01, 0.0. We don't use - // float to represent r because 0.01 is not representable precisely. - while (item.r * opts_.num_recall_points < r_level) { - const float recall = - static_cast(r_level) / opts_.num_recall_points; - CHECK_GE(r_level, 0); - sum += p; - r_level -= 1; - if (pr_out != nullptr) { - pr_out->emplace_back(p, recall); - } - } - p = std::max(p, item.p); - } - for (; r_level >= 0; --r_level) { - const float recall = static_cast(r_level) / opts_.num_recall_points; - sum += p; - if (pr_out != nullptr) { - pr_out->emplace_back(p, recall); - } - } - return sum / (1 + opts_.num_recall_points); -} - -template -float AveragePrecision::FromBoxes( - const std::vector>& groundtruth, - const std::vector>& prediction, - std::vector* pr_out) { - // Index ground truth boxes based on imageid. - std::unordered_map>> gt; - int num_gt = 0; - for (auto& box : groundtruth) { - gt[box.imgid].push_back(box); - if (!box.difficult && box.ignore == kDontIgnore) { - ++num_gt; - } - } - - if (num_gt == 0) { - return NAN; - } - - // Sort all predicted boxes by their scores in a non-ascending order. - std::vector> pd = prediction; - std::sort(pd.begin(), pd.end(), - [](const Detection& a, const Detection& b) { - return a.score > b.score; - }); - - // Computes p-r for every prediction. - std::vector pr; - int correct = 0; - int num_pd = 0; - for (int i = 0; i < pd.size(); ++i) { - const Detection& b = pd[i]; - auto* g = >[b.imgid]; - auto best = g->end(); - float best_iou = -INFINITY; - for (auto it = g->begin(); it != g->end(); ++it) { - const auto iou = b.box.IoU(it->box); - if (iou > best_iou) { - best = it; - best_iou = iou; - } - } - if ((best != g->end()) && (best_iou >= opts_.iou_threshold)) { - if (best->difficult) { - continue; - } - switch (best->ignore) { - case kDontIgnore: { - ++correct; - ++num_pd; - g->erase(best); - pr.push_back({static_cast(correct) / num_pd, - static_cast(correct) / num_gt}); - break; - } - case kIgnoreOneMatch: { - g->erase(best); - break; - } - case kIgnoreAllMatches: { - break; - } - } - } else { - ++num_pd; - pr.push_back({static_cast(correct) / num_pd, - static_cast(correct) / num_gt}); - } - } - return FromPRCurve(pr, pr_out); -} - -namespace KITTI { - -enum MatchingCriterion { - // Match a groundtruth to the prediction of highest score. - kBestScore = 0, - // Match a groundtruth to the prediction of largest IoU. - kBestIoU = 1, -}; - -const int kUnMatched = -1; -struct MatchResult { - int matched_idx = kUnMatched; - // When MatchingMode is kBestIoU and matched_idx is not kUnMatched, - // this field will contain the IoU between the prediction and the groundtruth. - float matched_overlap = 0; - // When MatchingMode is kBestIoU and matched_idx is not kUnMatched, - // this field will contain the confidence score of the prediction. - float detection_score = 0; - IgnoreType gt_ignore_type = IgnoreType::kDontIgnore; - IgnoreType pd_ignore_type = IgnoreType::kDontIgnore; -}; - -// Whether match1 is a better match than match2. -bool IsBetterMatch(const MatchResult& match1, const MatchResult& match2, - MatchingCriterion criterion); - -// Compute precision from detection assignments given a score threshold. -// Input arguments: -// pd_assignments: the 2nd pass matching results on all predictions. -// score_threshold: only the predictions with score above this threshold will -// be evaluated. -float ComputePrecision( - const std::unordered_map>& pd_assignments, - const float score_threshold); - -// Match groundtruth with predictions from one scene. -template -void MatchOneScene(const std::vector>& groundtruth, - const std::vector>& prediction, - const MatchingCriterion criterion, const float iou_threshold, - const float score_threshold, - std::vector* gt_assignment, - std::vector* pd_assignment) { - gt_assignment->clear(); - pd_assignment->clear(); - gt_assignment->resize(groundtruth.size()); - pd_assignment->resize(prediction.size()); - for (int j_pd = 0; j_pd < prediction.size(); ++j_pd) { - // Transfer over detection scores and ignore type from 'prediction' - pd_assignment->at(j_pd).detection_score = prediction[j_pd].score; - pd_assignment->at(j_pd).pd_ignore_type = prediction[j_pd].ignore; - } - for (int i_gt = 0; i_gt < groundtruth.size(); ++i_gt) { - if (groundtruth[i_gt].ignore == IgnoreType::kIgnoreAllMatches) { - continue; - } - MatchResult best_matched_pd; - best_matched_pd.gt_ignore_type = groundtruth[i_gt].ignore; - for (int j_pd = 0; j_pd < prediction.size(); ++j_pd) { - // For this groundtruth, find the best unassigned prediction that matches. - if (pd_assignment->at(j_pd).matched_idx != kUnMatched || - prediction[j_pd].score < score_threshold) { - // Skip predictions that have already been matched, or predictions below - // the score threshold. - continue; - } - const float iou = groundtruth[i_gt].box.IoU(prediction[j_pd].box); - MatchResult curr; - curr.matched_idx = j_pd; - curr.matched_overlap = iou; - curr.detection_score = prediction[j_pd].score; - curr.gt_ignore_type = groundtruth[i_gt].ignore; - curr.pd_ignore_type = prediction[j_pd].ignore; - if (iou > iou_threshold && - IsBetterMatch(curr, best_matched_pd, criterion)) { - best_matched_pd = curr; - } - } - MatchResult best_matched_gt = best_matched_pd; - best_matched_gt.matched_idx = i_gt; - - gt_assignment->at(i_gt) = best_matched_pd; - if (best_matched_pd.matched_idx != kUnMatched) { - pd_assignment->at(best_matched_pd.matched_idx) = best_matched_gt; - } - } -} - -// Input arguments: -// groundtruth: image_id -> list of bboxes mapping. -// prediction: image_id -> list of bboxes mapping. The key set must be the -// same as the one in groundtruth. -// criterion: should be kBestScore in 1st pass matching and kBestIoU in the -// 2nd pass. -// iou_threshold: bboxes can be matched if ther IoU is greater than this. -// score_thresold: predictions with score lower than this are ignored. -// gt_assignments: the matching results for every groundtruth. -// pd_assignments: the matching results for every prediction. -template -void MatchAll( - const std::unordered_map>>& - groundtruth, - const std::unordered_map>>& - prediction, - const MatchingCriterion criterion, const float iou_threshold, - const float score_threshold, - std::unordered_map>* gt_assignments, - std::unordered_map>* pd_assignments) { - CHECK_EQ(groundtruth.size(), prediction.size()); - std::vector all_img_keys; - for (const auto& gt_iter : groundtruth) { - const int64_t img_id = gt_iter.first; - const auto pd_iter = prediction.find(img_id); - CHECK(pd_iter != prediction.end()) - << "Groundtruth and prediction must have the same key set"; - const auto& gt = gt_iter.second; - const auto& pd = pd_iter->second; - MatchOneScene(gt, pd, criterion, iou_threshold, score_threshold, - &((*gt_assignments)[img_id]), &((*pd_assignments)[img_id])); - } -} - -std::vector FindThresholds( - const std::unordered_map>& gt_assignment, - const int num_recall_points); - -} // namespace KITTI - -// TODO(shlens): Consider removing score as a returned value as this is in -// theory already available in prediction. Another option is to combine as a -// std::pair with is_hit. -template -float AveragePrecision::FromBoxesKITTI( - const std::vector>& groundtruth, - const std::vector>& prediction, std::vector* pr_out, - std::vector* is_hit, std::vector* score) { - std::unordered_map>> gt_bins; - std::unordered_map>> pd_bins; - // Ensure that gt_bins and pd_bins have the same key sets. - for (const auto& box : groundtruth) { - gt_bins[box.imgid].push_back(box); - if (pd_bins.find(box.imgid) == pd_bins.end()) { - pd_bins[box.imgid] = std::vector>(); - } - } - std::vector index_to_image_id; - index_to_image_id.reserve(prediction.size()); - std::vector index_to_prediction_index; - index_to_prediction_index.reserve(prediction.size()); - for (int i = 0; i < prediction.size(); ++i) { - auto& box = prediction[i]; - pd_bins[box.imgid].push_back(box); - index_to_image_id.push_back(box.imgid); - index_to_prediction_index.push_back(pd_bins[box.imgid].size() - 1); - if (gt_bins.find(box.imgid) == gt_bins.end()) { - gt_bins[box.imgid] = std::vector>(); - } - } - // Pass 1 matching: find overlapping detection of best score. - std::unordered_map> gt_assignments; - std::unordered_map> pd_assignments; - KITTI::MatchAll(gt_bins, pd_bins, KITTI::kBestScore, opts_.iou_threshold, 0, - >_assignments, &pd_assignments); - for (int i = 0; i < prediction.size(); ++i) { - const int image_id = index_to_image_id[i]; - const int prediction_index = index_to_prediction_index[i]; - const float detection_score = - pd_assignments[image_id][prediction_index].detection_score; - float correct = 0.0; - if (pd_assignments[image_id][prediction_index].matched_idx != - KITTI::kUnMatched) { - correct = 1.0; - } - if (score != nullptr) { - score->push_back(detection_score); - } - if (is_hit != nullptr) { - is_hit->push_back(correct); - } - } - - const auto thresholds = - KITTI::FindThresholds(gt_assignments, opts_.num_recall_points); - pr_out->clear(); - float ap = 0; - for (int i = 0; i < thresholds.size(); ++i) { - // Pass 2 matching: find detection above the score threshold with largest - // overlap. - KITTI::MatchAll(gt_bins, pd_bins, KITTI::kBestIoU, opts_.iou_threshold, - thresholds[i], >_assignments, &pd_assignments); - const float precision = - KITTI::ComputePrecision(pd_assignments, thresholds[i]); - const float recall = static_cast(i) / (opts_.num_recall_points - 1); - - pr_out->push_back({precision, recall}); - ap += precision; - } - - for (int i = thresholds.size() - 2; i >= 0; --i) { - pr_out->at(i).p = std::max(pr_out->at(i + 1).p, pr_out->at(i).p); - } - return ap / opts_.num_recall_points; -} - -} // namespace image -} // namespace lingvo -} // namespace tensorflow - -#endif // THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_IMAGE_METRICS_H_ diff --git a/lingvo/tasks/car/ops/nms_3d_op.cc b/lingvo/tasks/car/ops/nms_3d_op.cc deleted file mode 100644 index cc10e636..00000000 --- a/lingvo/tasks/car/ops/nms_3d_op.cc +++ /dev/null @@ -1,196 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include -#include -#include - -#include "lingvo/tasks/car/ops/box_util.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/core/errors.h" -#include "tensorflow/core/platform/logging.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -struct PairHashFn { - size_t operator()(const std::pair& pair) const { - return std::hash{}((static_cast(pair.first) << 32) | - static_cast(pair.second)); - } -}; - -class NonMaxSuppression3DOp : public OpKernel { - public: - explicit NonMaxSuppression3DOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, - ctx->GetAttr("max_boxes_per_class", &max_boxes_per_class_)); - } - - void CheckShapes(OpKernelContext* ctx) { - const Tensor& bboxes_3d = ctx->input(0); - const Tensor& class_scores = ctx->input(1); - const Tensor& nms_iou_threshold = ctx->input(2); - const Tensor& score_threshold = ctx->input(3); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(bboxes_3d.shape()), - errors::InvalidArgument("In[0] must be a matrix, but get ", - bboxes_3d.shape().DebugString())); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(class_scores.shape()), - errors::InvalidArgument("In[1] must be a matrix, but get ", - class_scores.shape().DebugString())); - OP_REQUIRES( - ctx, TensorShapeUtils::IsVector(nms_iou_threshold.shape()), - errors::InvalidArgument("In[2] must be a vector, but get ", - nms_iou_threshold.shape().DebugString())); - OP_REQUIRES(ctx, TensorShapeUtils::IsVector(score_threshold.shape()), - errors::InvalidArgument("In[3] must be a vector, but get ", - score_threshold.shape().DebugString())); - OP_REQUIRES(ctx, bboxes_3d.dim_size(1) == 7, - errors::InvalidArgument("bboxes must be of shape [-1, 7]. Is: ", - bboxes_3d.shape().DebugString())); - const int num_classes = class_scores.dim_size(1); - OP_REQUIRES(ctx, nms_iou_threshold.dim_size(0) == num_classes, - errors::InvalidArgument( - "nms_iou_threshold must be of shape [num_classes]. Is: ", - nms_iou_threshold.shape().DebugString())); - OP_REQUIRES(ctx, score_threshold.dim_size(0) == num_classes, - errors::InvalidArgument( - "score_threshold must be of shape [num_classes]. Is: ", - nms_iou_threshold.shape().DebugString())); - } - - void Compute(OpKernelContext* ctx) override { - CheckShapes(ctx); - const Tensor& bboxes_3d = ctx->input(0); - const Tensor& class_scores = ctx->input(1); - const Tensor& nms_iou_threshold = ctx->input(2); - const Tensor& score_threshold = ctx->input(3); - - const int num_bboxes = bboxes_3d.dim_size(0); - const int num_classes = class_scores.dim_size(1); - - // Parse data into usable forms - std::vector boxes = box::ParseBoxesFromTensor(bboxes_3d); - const auto& t_class_scores = class_scores.matrix(); - const auto& t_nms_iou_threshold = nms_iou_threshold.vec(); - const auto& t_score_threshold = score_threshold.vec(); - - struct Candidate { - int box_idx; - float score; - }; - - auto score_cmp = [](const Candidate& box1, const Candidate& box2) { - return box1.score < box2.score; - }; - - // Allocate outputs - Tensor* bbox_indices = nullptr; - Tensor* bbox_scores = nullptr; - Tensor* valid_mask = nullptr; - - auto output_shape = TensorShape({num_classes, max_boxes_per_class_}); - OP_REQUIRES_OK( - ctx, ctx->allocate_output("bbox_indices", output_shape, &bbox_indices)); - OP_REQUIRES_OK( - ctx, ctx->allocate_output("bbox_scores", output_shape, &bbox_scores)); - OP_REQUIRES_OK( - ctx, ctx->allocate_output("valid_mask", output_shape, &valid_mask)); - auto t_bbox_indices = bbox_indices->matrix(); - auto t_bbox_scores = bbox_scores->matrix(); - auto t_valid_mask = valid_mask->matrix(); - t_bbox_indices.setZero(); - t_bbox_scores.setZero(); - t_valid_mask.setZero(); - - // Create cache for IoU calculations - // Keys are pairs of box ids where the first id is always less than the - // second id due to symmetry in the calculation. - std::unordered_map, float, PairHashFn> iou_cache; - auto get_iou = [&iou_cache, &boxes](int32 idx1, int32 idx2) { - std::pair key{std::min(idx1, idx2), std::max(idx1, idx2)}; - auto it = iou_cache.find(key); - if (it != iou_cache.end()) { - return it->second; - } - - float iou = boxes[idx1].IoU(boxes[idx2]); - iou_cache.emplace(key, iou); - return iou; - }; - - for (int cls_idx = 0; cls_idx < num_classes; ++cls_idx) { - // Use priority queue to sort candidates above the score threshold - std::priority_queue, decltype(score_cmp)> - candidate_priority_queue(score_cmp); - for (int box_idx = 0; box_idx < num_bboxes; ++box_idx) { - if (t_class_scores(box_idx, cls_idx) >= t_score_threshold(cls_idx)) { - candidate_priority_queue.emplace( - Candidate({box_idx, t_class_scores(box_idx, cls_idx)})); - } - } - - std::vector selected; - Candidate next_candidate; - while ((selected.size() < max_boxes_per_class_) && - (!candidate_priority_queue.empty())) { - next_candidate = candidate_priority_queue.top(); - candidate_priority_queue.pop(); - - // Idea taken from tensorflow/core/kernels/non_max_suppression_op.cc - // Overlapping boxes are likely to have similar scores, - // therefore we iterate through the previously selected boxes backwards - // in order to see if `next_candidate` should be suppressed. - bool should_select = true; - for (int selected_idx = static_cast(selected.size()) - 1; - selected_idx >= 0; --selected_idx) { - if (get_iou(next_candidate.box_idx, selected[selected_idx].box_idx) > - t_nms_iou_threshold(cls_idx)) { - should_select = false; - break; - } - } - - if (should_select) { - selected.push_back(next_candidate); - } - } - - // For each class, copy results into output tensors. - // We can just use size since we protect against ever selecting more - // than max_boxes_per_class_ per class. - for (int insert_idx = 0; insert_idx < selected.size(); insert_idx++) { - const auto& to_insert = selected[insert_idx]; - t_bbox_indices(cls_idx, insert_idx) = to_insert.box_idx; - t_bbox_scores(cls_idx, insert_idx) = to_insert.score; - t_valid_mask(cls_idx, insert_idx) = 1.0; - } - } - } - - private: - // These are attributes and logically const. - int max_boxes_per_class_; -}; - -REGISTER_KERNEL_BUILDER(Name("NonMaxSuppression3D").Device(DEVICE_CPU), - NonMaxSuppression3DOp); - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/nms_3d_op_test.py b/lingvo/tasks/car/ops/nms_3d_op_test.py deleted file mode 100644 index 1b4f07e0..00000000 --- a/lingvo/tasks/car/ops/nms_3d_op_test.py +++ /dev/null @@ -1,204 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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 time -import unittest -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import ops -import numpy as np - - -class Nms3dOpTest(test_utils.TestCase): - - def _GetData(self): - # Assignments and IoU scores derived from externally calculated test cases - # created with shapely. - bboxes = tf.constant( - [ - [10.35, 8.429, -1.003, 3.7, 1.64, 1.49, 1.582], - [10.35, 8.429, -1.003, 3.7, 1.64, 1.49, 0.0 - ], # Box 0 rotated ~90 deg - [11.5, 8.429, -1.003, 3.7, 1.64, 1.49, 1.0], # Rotated to overlap - [13.01, 8.149, -0.953, 4.02, 1.55, 1.52, 1.592], - [13.51, 8.39, -1.0, 4.02, 1.55, 1.52, 1.592], # Slight translation - [13.51, 8.39, -1.0, 1.0, 1.0, 1.52, 1.592], # Smaller box - [13.51, 8.39, -1.0, 1.0, 1.0, 1.52, 1.9], # Smaller box - ], - dtype=tf.float32) - - scores = tf.constant([ - [0.9, 0.09, 0.0], - [0.88, 0.109, 0.011], - [0.5, 0.01, 0.49], - [0.8, 0.1, 0.1], - [0.79, 0.12, 0.19], - [0.2, 0.79, 0.11], - [0.1, 0.9, 0.0], - ], - dtype=tf.float32) - return bboxes, scores - - def _TestNMSOp(self, bboxes_3d, class_scores, nms_iou_threshold, - score_threshold, max_boxes_per_class, expected_indices): - with self.session(): - bbox_indices, bbox_scores, valid_mask = ops.non_max_suppression_3d( - bboxes_3d, - class_scores, - nms_iou_threshold=nms_iou_threshold, - score_threshold=score_threshold, - max_boxes_per_class=max_boxes_per_class) - bbox_idx, scores, mask = self.evaluate( - [bbox_indices, bbox_scores, valid_mask]) - - num_classes = len(expected_indices) - expected_shape = (num_classes, max_boxes_per_class) - self.assertEqual(bbox_idx.shape, expected_shape) - self.assertEqual(scores.shape, expected_shape) - self.assertEqual(mask.shape, expected_shape) - - total_expected_valid_boxes = sum([len(exp) for exp in expected_indices]) - self.assertEqual(mask.sum(), total_expected_valid_boxes) - - for cls_idx in range(num_classes): - cls_mask = mask[cls_idx, :].astype(np.bool) - self.assertEqual(cls_mask.sum(), len(expected_indices[cls_idx])) - self.assertAllEqual(bbox_idx[cls_idx, cls_mask], - expected_indices[cls_idx]) - - def testMultiClassNMS(self): - bboxes_3d, class_scores = self._GetData() - expected_indices = [[0, 3], [6], [2]] - self._TestNMSOp( - bboxes_3d, - class_scores, - nms_iou_threshold=[0.1, 0.1, 0.1], - score_threshold=[0.3, 0.3, 0.3], - max_boxes_per_class=5, - expected_indices=expected_indices) - - def testLowerScoreThreshold(self): - bboxes_3d, class_scores = self._GetData() - # Lower threshold means more boxes are included. - expected_indices = [[0, 3], [6, 1], [2, 4]] - self._TestNMSOp( - bboxes_3d, - class_scores, - nms_iou_threshold=[0.1, 0.1, 0.1], - score_threshold=[0.01, 0.01, 0.01], - max_boxes_per_class=5, - expected_indices=expected_indices) - - def testHighIoUThreshold(self): - bboxes_3d, class_scores = self._GetData() - expected_indices = [[0, 1, 3, 4, 2, 5, 6], [6, 5, 4, 1, 3, 0, 2], - [2, 4, 5, 3, 1]] - # Increase IoU Threshold and max number of boxes so - # all non-zero score boxes are returned. - self._TestNMSOp( - bboxes_3d, - class_scores, - nms_iou_threshold=[0.999, 0.999, 0.999], - score_threshold=[0.01, 0.01, 0.01], - max_boxes_per_class=10, - expected_indices=expected_indices) - - def testOneClassVsMultiClass(self): - # Check running on all 3 classes versus each independently. - bboxes_3d, class_scores = self._GetData() - num_classes = 3 - max_boxes_per_class = 5 - with self.session(): - bbox_indices, bbox_scores, valid_mask = ops.non_max_suppression_3d( - bboxes_3d, - class_scores, - nms_iou_threshold=[0.1, 0.1, 0.1], - score_threshold=[0.3, 0.3, 0.3], - max_boxes_per_class=max_boxes_per_class) - multiclass_indices, multiclass_scores, multiclass_valid_mask = self.evaluate( - [bbox_indices, bbox_scores, valid_mask]) - self.assertEqual(multiclass_indices.shape, - (num_classes, max_boxes_per_class)) - self.assertEqual(multiclass_scores.shape, - (num_classes, max_boxes_per_class)) - self.assertEqual(multiclass_valid_mask.shape, - (num_classes, max_boxes_per_class)) - - # For each class, get results for just that class and compare. - for cls_idx in range(num_classes): - bbox_idx, bbox_scores, valid_mask = ops.non_max_suppression_3d( - bboxes_3d, - class_scores[:, cls_idx:cls_idx + 1], - nms_iou_threshold=[0.1], - score_threshold=[0.3], - max_boxes_per_class=max_boxes_per_class) - per_class_indices, per_class_scores, per_class_valid_mask = self.evaluate( - [bbox_idx, bbox_scores, valid_mask]) - - self.assertEqual(per_class_indices.shape, (1, max_boxes_per_class)) - self.assertEqual(per_class_scores.shape, (1, max_boxes_per_class)) - self.assertEqual(per_class_valid_mask.shape, (1, max_boxes_per_class)) - - per_class_mask = per_class_valid_mask[0, :].astype(np.bool) - multiclass_mask = multiclass_valid_mask[cls_idx, :].astype(np.bool) - self.assertAllEqual(per_class_indices[0, per_class_mask], - multiclass_indices[cls_idx, multiclass_mask]) - self.assertAllEqual(per_class_scores[0, per_class_mask], - multiclass_scores[cls_idx, multiclass_mask]) - - @unittest.skip('Speed benchmark') - def testSpeed(self): - num_bboxes_list = [500, 1000, 10000] - num_classes_list = [3, 10, 25] - - for num_bboxes in num_bboxes_list: - for num_classes in num_classes_list: - bboxes_3d = tf.random.uniform((num_bboxes, 7), - minval=0.1, - maxval=2, - dtype=tf.float32) - # Make half zero so we can see behavior with very low values that - # will get filtered out quickly. - class_scores = tf.concat([ - tf.random.uniform((num_bboxes // 2, num_classes), - minval=0, - maxval=1, - dtype=tf.float32), - tf.zeros((num_bboxes // 2, num_classes), dtype=tf.float32) - ], - axis=0) - - with self.session(): - outputs = ops.non_max_suppression_3d( - bboxes_3d, - class_scores, - max_boxes_per_class=1000, - nms_iou_threshold=[0.1] * num_classes, - score_threshold=[0.3] * num_classes) - - timings = [] - for _ in range(10): - start = time.time() - _ = self.evaluate(outputs) - end = time.time() - timings.append(end - start) - avg = sum(timings) / len(timings) - print('[{},{},{},{},{}]'.format(num_bboxes, num_classes, min(timings), - avg, max(timings))) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/ops/pairwise_iou_op.cc b/lingvo/tasks/car/ops/pairwise_iou_op.cc deleted file mode 100644 index b9d781c7..00000000 --- a/lingvo/tasks/car/ops/pairwise_iou_op.cc +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include - -#include "lingvo/tasks/car/ops/box_util.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/core/errors.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -class PairwiseIoUOp : public OpKernel { - public: - explicit PairwiseIoUOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} - - void Compute(OpKernelContext* ctx) override { - const Tensor& a = ctx->input(0); - const Tensor& b = ctx->input(1); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(a.shape()), - errors::InvalidArgument("In[0] must be a matrix, but get ", - a.shape().DebugString())); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(b.shape()), - errors::InvalidArgument("In[0] must be a matrix, but get ", - b.shape().DebugString())); - OP_REQUIRES(ctx, 7 == a.dim_size(1), - errors::InvalidArgument("Matrix size-incompatible: In[0]: ", - a.shape().DebugString())); - OP_REQUIRES(ctx, 7 == b.dim_size(1), - errors::InvalidArgument("Matrix size-incompatible: In[1]: ", - b.shape().DebugString())); - - const int n_a = a.dim_size(0); - const int n_b = b.dim_size(0); - - Tensor* iou_a_b = nullptr; - OP_REQUIRES_OK( - ctx, ctx->allocate_output("iou", TensorShape({n_a, n_b}), &iou_a_b)); - - auto t_iou_a_b = iou_a_b->matrix(); - - std::vector box_a = box::ParseBoxesFromTensor(a); - std::vector box_b = box::ParseBoxesFromTensor(b); - for (int i_a = 0; i_a < n_a; ++i_a) { - for (int i_b = 0; i_b < n_b; ++i_b) { - t_iou_a_b(i_a, i_b) = box_a[i_a].IoU(box_b[i_b]); - } - } - } -}; - -REGISTER_KERNEL_BUILDER(Name("PairwiseIou3D").Device(DEVICE_CPU), - PairwiseIoUOp); - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/point_grid_op.cc b/lingvo/tasks/car/ops/point_grid_op.cc deleted file mode 100644 index 740eba30..00000000 --- a/lingvo/tasks/car/ops/point_grid_op.cc +++ /dev/null @@ -1,192 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include -#include -#include -#include -#include -#include - -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/core/errors.h" - -namespace tensorflow { -namespace lingvo { -namespace { - -const unsigned int kOpRandomSeed = 16; - -int FindBucket(const float val, const float min_val, - const float interval_size) { - return static_cast(std::floor((val - min_val) / interval_size)); -} - -class PointToGridOp : public OpKernel { - public: - explicit PointToGridOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - OP_REQUIRES_OK(ctx, - ctx->GetAttr("num_points_per_cell", &num_points_per_cell_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("x_intervals", &x_intervals_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("y_intervals", &y_intervals_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("z_intervals", &z_intervals_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("x_range", &x_range_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("y_range", &y_range_)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("z_range", &z_range_)); - - OP_REQUIRES(ctx, x_intervals_ > 0 && y_intervals_ > 0 && z_intervals_ > 0, - errors::InvalidArgument("intervals must be positive.")); - OP_REQUIRES( - ctx, - x_range_.size() == 2 && y_range_.size() == 2 && z_range_.size() == 2, - errors::InvalidArgument("intervals must be tuple or list of two.")); - OP_REQUIRES( - ctx, - x_range_[0] < x_range_[1] && y_range_[0] < y_range_[1] && - z_range_[0] < z_range_[1], - errors::InvalidArgument( - "intervals must have lower bounds smaller than upper bounds.")); - } - - void Compute(OpKernelContext* ctx) override { - const Tensor& input_points = ctx->input(0); - OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(input_points.shape()), - errors::InvalidArgument("points must be a matrix, but get ", - input_points.shape().DebugString())); - OP_REQUIRES(ctx, input_points.dim_size(1) >= 3, - errors::InvalidArgument( - "points must have at least 3 on 2nd dimension.")); - - const float xmin_ = x_range_[0]; - const float xmax_ = x_range_[1]; - const float ymin_ = y_range_[0]; - const float ymax_ = y_range_[1]; - const float zmin_ = z_range_[0]; - const float zmax_ = z_range_[1]; - const float x_interval_size = (xmax_ - xmin_) / x_intervals_; - const float y_interval_size = (ymax_ - ymin_) / y_intervals_; - const float z_interval_size = (zmax_ - zmin_) / z_intervals_; - - // Create a bucket structure where the first dimension is the bucket id and - // the second vector contains the indices of the points that fall into the - // bucket. - // - // We linearize the indexing so that each bucket coordinate maps to a single - // offset. - std::vector> buckets_vec; - - // The number of buckets is the product of all the intervals. - buckets_vec.resize(x_intervals_ * y_intervals_ * z_intervals_); - - const auto& t_input_points = input_points.matrix(); - const int n_input_points = input_points.dim_size(0); - const int n_input_features = input_points.dim_size(1); - - std::vector visit_order(n_input_points); - std::iota(visit_order.begin(), visit_order.end(), 0); - std::shuffle(visit_order.begin(), visit_order.end(), - std::default_random_engine(kOpRandomSeed)); - - for (const int i : visit_order) { - int bucket_x = FindBucket(t_input_points(i, 0), xmin_, x_interval_size); - int bucket_y = FindBucket(t_input_points(i, 1), ymin_, y_interval_size); - int bucket_z = FindBucket(t_input_points(i, 2), zmin_, z_interval_size); - if (bucket_x >= 0 && bucket_x < x_intervals_ && bucket_y >= 0 && - bucket_y < y_intervals_ && bucket_z >= 0 && bucket_z < z_intervals_) { - // Compute the linearized bucket offset. - auto bucket_id = BucketId(bucket_x, bucket_y, bucket_z); - buckets_vec[bucket_id].push_back(i); - } - } - - Tensor* output_points = nullptr; - OP_REQUIRES_OK(ctx, - ctx->allocate_output( - "output_points", - TensorShape({x_intervals_, y_intervals_, z_intervals_, - num_points_per_cell_, n_input_features}), - &output_points)); - Tensor* grid_centers = nullptr; - OP_REQUIRES_OK( - ctx, ctx->allocate_output( - "grid_centers", - TensorShape({x_intervals_, y_intervals_, z_intervals_, 3}), - &grid_centers)); - Tensor* num_points = nullptr; - OP_REQUIRES_OK(ctx, - ctx->allocate_output( - "num_points", - TensorShape({x_intervals_, y_intervals_, z_intervals_}), - &num_points)); - - auto t_output_points = output_points->tensor(); - auto t_grid_centers = grid_centers->tensor(); - auto t_num_points = num_points->tensor(); - - // Padded points will be set to 0. Users can compute the mean by taking - // the sum and dividing by effective number of points. - t_output_points.setConstant(0); - - for (int bx = 0; bx < x_intervals_; ++bx) { - for (int by = 0; by < y_intervals_; ++by) { - for (int bz = 0; bz < z_intervals_; ++bz) { - const float cell_x_center = xmin_ + (bx + 0.5) * x_interval_size; - const float cell_y_center = ymin_ + (by + 0.5) * y_interval_size; - const float cell_z_center = zmin_ + (bz + 0.5) * z_interval_size; - t_grid_centers(bx, by, bz, 0) = cell_x_center; - t_grid_centers(bx, by, bz, 1) = cell_y_center; - t_grid_centers(bx, by, bz, 2) = cell_z_center; - - // Compute the bucket and the number of points that mapped to that - // bucket. - auto bucket_id = BucketId(bx, by, bz); - const std::vector& bucket = buckets_vec[bucket_id]; - const int effective_num = - std::min(num_points_per_cell_, static_cast(bucket.size())); - t_num_points(bx, by, bz) = effective_num; - - // Add the points in bucket to the output. - for (int i = 0; i < effective_num; ++i) { - t_output_points.chip<0>(bx).chip<0>(by).chip<0>(bz).chip<0>(i) = - t_input_points.chip<0>(bucket[i]); - } - } - } - } - } - - private: - // Linearizes the bucket index. - int BucketId(int bucket_x, int bucket_y, int bucket_z) { - return (bucket_z + (bucket_y * z_intervals_) + - (bucket_x * y_intervals_ * z_intervals_)); - } - - int num_points_per_cell_; - int x_intervals_; - int y_intervals_; - int z_intervals_; - std::vector x_range_; - std::vector y_range_; - std::vector z_range_; -}; - -REGISTER_KERNEL_BUILDER(Name("PointToGrid").Device(DEVICE_CPU), PointToGridOp); - -} // namespace -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/point_grid_op_test.py b/lingvo/tasks/car/ops/point_grid_op_test.py deleted file mode 100644 index a77bb61b..00000000 --- a/lingvo/tasks/car/ops/point_grid_op_test.py +++ /dev/null @@ -1,133 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.tasks.car.ops.point_grid_op.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car import ops -import numpy as np - - -class PointGridOpTest(test_utils.TestCase): - - def _testGrid(self, points, num_points_per_cell, grid_size, grid_range): - points = np.array(points) # p x (3 + d) - with self.session(use_gpu=False): - out_points, grid_centers, num_points = ops.point_to_grid( - points, - num_points_per_cell=num_points_per_cell, - x_intervals=grid_size[0], - y_intervals=grid_size[1], - z_intervals=grid_size[2], - x_range=grid_range[0], - y_range=grid_range[1], - z_range=grid_range[2]) - # Test shape - out_points = py_utils.HasShape( - out_points, - list(grid_size) + [num_points_per_cell, -1]) - grid_centers = py_utils.HasShape(grid_centers, list(grid_size) + [3]) - num_points = py_utils.HasShape(num_points, grid_size) - out_points, grid_centers, num_points = self.evaluate( - [out_points, grid_centers, num_points]) - - # Test points are in the right cell. - boundaries = [] - for axis in range(3): - boundaries += [ - np.linspace( - start=grid_range[axis][0], - stop=grid_range[axis][1], - num=grid_size[axis] + 1, - ) - ] - boundaries = np.stack(np.meshgrid(*boundaries, indexing='ij'), axis=-1) - - # Cast num_points to the points_mask, indicating whether a point - # within a pillar is a real point or not. - points_index = np.arange(num_points_per_cell, dtype=num_points.dtype) - points_index = np.reshape(points_index, [1, 1, 1, num_points_per_cell]) - real_points_mask = np.less(points_index, num_points[..., np.newaxis]) - real_points_mask = np.tile( - np.expand_dims(real_points_mask, axis=-1), [1, 1, 1, 1, 3]) - - # Check whether real points are within the right boundaries. - valid_bound = np.logical_and( - out_points[..., :3] >= boundaries[:-1, :-1, :-1, np.newaxis, ...], - out_points[..., :3] < boundaries[1:, 1:, 1:, np.newaxis, ...]) - padded_points_mask = 1 - real_points_mask - # Check if points are either in valid bounds or padded. - valid = np.logical_or(valid_bound, padded_points_mask) - self.assertTrue(np.all(valid)) - - # Check that points are either zero (padded) or real. - valid = np.logical_or(out_points[..., :3] == 0, real_points_mask) - self.assertTrue(np.all(valid)) - - counts = np.zeros(grid_size) - - def idx(v, l, u, k): - if v < l or v >= u: - return None - return int((v - l) / (u - l) * k) - - for (x, y, z) in points: - ix = idx(x, grid_range[0][0], grid_range[0][1], grid_size[0]) - iy = idx(y, grid_range[1][0], grid_range[1][1], grid_size[1]) - iz = idx(z, grid_range[2][0], grid_range[2][1], grid_size[2]) - if all(i is not None for i in (ix, iy, iz)): - counts[ix, iy, iz] += 1 - counts = np.minimum(counts, num_points_per_cell) - self.assertAllEqual(counts, num_points) - print(np.min(counts), np.max(counts)) - - def testSimpleGrid(self): - points = [[0, 0, 0], [1, 1, 1], [2, 2, 2]] - self._testGrid(points, 1, [2, 2, 2], [[0, 2.1], [0, 2.1], [0, 2.1]]) - - def testRandomPoints(self): - np.random.seed(7483) - points = np.random.random((100, 3)) - grid_size = (5, 4, 3) - grid_range = ((0, 0.3), (-1, 1.4), (0, 1.0)) - num_points_per_cell = 3 - self._testGrid(points, num_points_per_cell, grid_size, grid_range) - - -class PointGridOpBenchmark(tf.test.Benchmark): - - def benchmarkSimpleGrid(self): - points = np.random.uniform(0.0, 1.0, (200000, 3)) - num_points_per_cell = 100 - grid_size = [200, 200, 1] - grid_range = [[0., 1.], [0., 1], [0, 1]] - sess = tf.Session() - out_points, _, _ = ops.point_to_grid( - points, - num_points_per_cell=num_points_per_cell, - x_intervals=grid_size[0], - y_intervals=grid_size[1], - z_intervals=grid_size[2], - x_range=grid_range[0], - y_range=grid_range[1], - z_range=grid_range[2]) - - print(self.run_op_benchmark(sess, out_points, min_iters=1000)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/ops/ps_utils.cc b/lingvo/tasks/car/ops/ps_utils.cc deleted file mode 100644 index 26a8e64b..00000000 --- a/lingvo/tasks/car/ops/ps_utils.cc +++ /dev/null @@ -1,501 +0,0 @@ -/* Copyright 2019 The TensorFlow 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. -==============================================================================*/ -#include "lingvo/tasks/car/ops/ps_utils.h" - -#include -#include -#include -#include -#include - -#include "absl/synchronization/mutex.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/framework/tensor_shape.h" -#include "tensorflow/core/lib/strings/stringprintf.h" -#include "tensorflow/core/platform/macros.h" - -namespace tensorflow { -namespace lingvo { -namespace car { - -namespace { - -int BucketId(int bucket_x, int bucket_y, int bucket_z, int y_intervals, - int z_intervals) { - return (bucket_z + (bucket_y * z_intervals) + - (bucket_x * y_intervals * z_intervals)); -} - -int FindBucket(const float val, const float min_val, - const float interval_size) { - return static_cast(std::floor((val - min_val) / interval_size)); -} - -// Given a sequence of identifiers (Add), returns k elements uniformly sampled -// from the sequence. -class UniformSampler { - public: - explicit UniformSampler(int k, uint64 seed) : k_(k), rnd_(seed) { - CHECK_GT(k, 0); - ids_.reserve(k_); - } - - void Add(int id, float unused_score) { - ++num_; - if (ids_.size() < k_) { - ids_.push_back(Item{id}); - } else { - const int64 rnd = rnd_() % num_; - // Replace w/ prob k_ / num_ following the reservoir sampling algorithm R. - if (rnd < k_) { - ids_[rnd].id = id; - } - } - } - - struct Item { - int id; - }; - - const std::vector& Get() const { return ids_; } - - void Reset() { - num_ = 0; - ids_.clear(); - } - - private: - const int32 k_; - std::mt19937 rnd_; - int32 num_ = 0; - std::vector ids_; -}; - -// Given a sequence of identifiers with scores (Add), returns k elements with -// the smallest scores. -class TopKSampler { - public: - explicit TopKSampler(int k, uint64 unused_seed) : k_(k) { CHECK_GT(k, 0); } - - void Add(int id, float score) { - if (selected_ && score > items_[k_ - 1].score) { - return; - } - items_.push_back(Item{id, score}); - if (items_.size() > 2 * k_) { - Shrink(); - } - } - - struct Item { - int id; - float score; - }; - - const std::vector& Get() { - if (items_.size() > k_) { - Shrink(); - } - return items_; - } - - void Reset() { - items_.clear(); - selected_ = false; - } - - private: - const int k_ = -1; - bool selected_ = false; - std::vector items_; - struct Cmp { - bool operator()(const Item& a, const Item& b) const { - return a.score < b.score; - } - }; - Cmp cmp_; - - void Shrink() { - std::nth_element(items_.begin(), items_.begin() + k_ - 1, items_.end(), - cmp_); - items_.resize(k_); - selected_ = true; - } -}; - -// Uniform center selection among n points (w/o replacement). -class UniformSelector { - public: - UniformSelector(const std::vector& candidates, uint64 seed) { - for (int i = 0; i < candidates.size(); ++i) { - if (candidates[i]) { - ids_.push_back(i); - } - } - std::shuffle(ids_.begin(), ids_.end(), std::mt19937(seed)); - } - - int Get() { - if (ids_.empty()) { - return -1; - } - int id = ids_.back(); - ids_.resize(ids_.size() - 1); - return id; - } - - void Update(int j, float score) {} // No op. - - private: - std::vector ids_; -}; - -// Select the point farthest from selected centers so far. -class FarthestSelector { - public: - FarthestSelector(std::vector candidates, uint64 seed) - : num_(candidates.size()), - candidates_(std::move(candidates)), - min_dist_sq_(DT_FLOAT, {num_}), - min_dist_sq_t_(min_dist_sq_.vec()), - farthest_(DT_INT32, {}), - farthest_t_(farthest_.scalar()) { - CHECK_LT(kLargeDistA, kLargeDistB); - // Initialize min_dist_sq_ so that the 1st selected point is random. - std::mt19937 gen(seed); - std::uniform_real_distribution dis(kLargeDistA, kLargeDistB); - for (int i = 0; i < num_; ++i) { - if (candidates_[i]) { - ++num_valid_; - min_dist_sq_t_(i) = dis(gen); - } else { - min_dist_sq_t_(i) = -1.0; - } - } - } - - int Get() { - if (num_sampled_ >= num_valid_) { - return -1; - } - farthest_t_ = min_dist_sq_t_.argmax().template cast().eval(); - ++num_sampled_; - return farthest_t_(); - } - - // Updates j-th distance squared to all chosen centers. - void Update(int j, float dist_sq) { - DCHECK_LT(dist_sq, kLargeDistA); - if (candidates_[j] && (dist_sq < min_dist_sq_t_(j))) { - min_dist_sq_t_(j) = dist_sq; - } - } - - private: - const float kLargeDistA = 1e30; - const float kLargeDistB = kLargeDistA * 1.1; - - const int num_; // # of points. - int num_valid_ = 0; // # of points candidates_[i] is True. - int num_sampled_ = 0; // # of Get() has been called. - std::vector candidates_; // i-th can be selected iff candidates_[i]. - Tensor min_dist_sq_; - decltype(min_dist_sq_.vec()) min_dist_sq_t_; - Tensor farthest_; - decltype(farthest_.scalar()) farthest_t_; -}; - -class RNG { - public: - RNG() : rng_(std::random_device("/dev/urandom")()) {} - - uint64 Get() { - absl::MutexLock l(&mu_); - return rng_(); - } - - private: - absl::Mutex mu_; - std::mt19937_64 rng_; -}; - -} // namespace - -uint64 PSUtils::Seed() const { - if (opts_.random_seed != -1) { - return opts_.random_seed; - } - static RNG* rng = new RNG(); - return rng->Get(); -} - -template -T Square(T x) { - return x * x; -} - -template -PSUtils::Result PSUtils::DoSampling(const Tensor& points, - const Tensor& points_padding, - const int32 num_seeded_points) const { - // Points must be of rank 3, and padding must be a matrix. - DCHECK_EQ(points.dims(), 3); - DCHECK_EQ(points_padding.dims(), 2); - // 3D points. - DCHECK_EQ(points.dim_size(2), 3); - DCHECK_EQ(points.dim_size(0), points_padding.dim_size(0)); - DCHECK_EQ(points.dim_size(1), points_padding.dim_size(1)); - - auto points_t = points.tensor(); - auto points_padding_t = points_padding.matrix(); - const int64 batch_size = points.dim_size(0); - const int64 num_points = points.dim_size(1); - - Result result; - result.center = Tensor(DT_INT32, {batch_size, opts_.num_centers}); - result.center_padding = Tensor(DT_FLOAT, {batch_size, opts_.num_centers}); - result.indices = - Tensor(DT_INT32, {batch_size, opts_.num_centers, opts_.num_neighbors}); - result.indices_padding = - Tensor(DT_FLOAT, {batch_size, opts_.num_centers, opts_.num_neighbors}); - - auto center_t = result.center.matrix(); - center_t.setConstant(0); - auto center_padding_t = result.center_padding.matrix(); - auto indices_t = result.indices.tensor(); - indices_t.setConstant(0); - auto padding_t = result.indices_padding.tensor(); - padding_t.setConstant(1.0); - - // Max distance squared as the threshold. - const float threshold = Square(opts_.max_dist); - - // The idea behind the hash lookup is to only do neighbor / distance checks - // for plausibly close neighbors, rather than looking at all points for each - // center. We do this by gridifying the points, and for each center only - // looking at points in nearby grid cells. This a cheap version of a more - // sophisticated algorithm like using a KDTree or RangeTree. - const bool use_hash_lookup = - (opts_.neighbor_search_algorithm == PSUtils::Options::N_HASH); - - for (int cur_batch = 0; cur_batch < batch_size; ++cur_batch) { - std::vector candidates(num_points); - - float xmin = std::numeric_limits::max(); - float ymin = std::numeric_limits::max(); - float zmin = std::numeric_limits::max(); - float xmax = std::numeric_limits::lowest(); - float ymax = std::numeric_limits::lowest(); - float zmax = std::numeric_limits::lowest(); - - for (int i = 0; i < num_points; ++i) { - // The first num_seeded_points are not candidates of the selector, because - // they are always selected. - candidates[i] = - (i >= num_seeded_points && points_padding_t(cur_batch, i) == 0.0) && - (opts_.center_z_min <= points_t(cur_batch, i, 2)) && - (points_t(cur_batch, i, 2) <= opts_.center_z_max); - - // Find min / max points for computing grid buckets. - if (use_hash_lookup && points_padding_t(cur_batch, i) == 0.0) { - xmin = std::min(points_t(cur_batch, i, 0), xmin); - xmax = std::max(points_t(cur_batch, i, 0), xmax); - ymin = std::min(points_t(cur_batch, i, 1), ymin); - ymax = std::max(points_t(cur_batch, i, 1), ymax); - zmin = std::min(points_t(cur_batch, i, 2), zmin); - zmax = std::max(points_t(cur_batch, i, 2), zmax); - } - } - - // Stores a mapping of bucket_id -> list of point indices. The buckets are - // the voxelized breakdown of the 3D space and points fall into these - // voxels. The length of the cube is the max_distance. - std::vector> buckets_vec; - std::vector> buckets_values; - - int x_intervals = 0; - int y_intervals = 0; - int z_intervals = 0; - - if (use_hash_lookup) { - // Adjust boundaries to avoid edge conditions. We use max_dist as a - // conservative estimate. - xmin -= opts_.max_dist; - ymin -= opts_.max_dist; - zmin -= opts_.max_dist; - xmax += opts_.max_dist; - ymax += opts_.max_dist; - zmax += opts_.max_dist; - - x_intervals = std::ceil((xmax - xmin) / opts_.max_dist); - y_intervals = std::ceil((ymax - ymin) / opts_.max_dist); - z_intervals = std::ceil((zmax - zmin) / opts_.max_dist); - - // The number of buckets is the product of all the intervals. - buckets_vec.resize(x_intervals * y_intervals * z_intervals); - for (int i = 0; i < num_points; ++i) { - // Compute which bucket each valid point falls into. - // - // A valid is a non-padded, non-seeded point. - if (points_padding_t(cur_batch, i) == 0.0 && i >= num_seeded_points) { - int bucket_x = - FindBucket(points_t(cur_batch, i, 0), xmin, opts_.max_dist); - int bucket_y = - FindBucket(points_t(cur_batch, i, 1), ymin, opts_.max_dist); - int bucket_z = - FindBucket(points_t(cur_batch, i, 2), zmin, opts_.max_dist); - if (bucket_x >= 0 && bucket_x < x_intervals && bucket_y >= 0 && - bucket_y < y_intervals && bucket_z >= 0 && - bucket_z < z_intervals) { - // Compute the linearized bucket offset. - auto bucket_id = BucketId(bucket_x, bucket_y, bucket_z, y_intervals, - z_intervals); - buckets_vec[bucket_id].push_back(i); - } - } - } - } - - Selector selector(candidates, Seed()); - Sampler sampler(opts_.num_neighbors, Seed()); - - for (int i = 0; i < opts_.num_centers; ++i) { - // Pick a point as i-th center. - int k; - if (i < num_seeded_points) { - k = i; - } else { - // Pick a point as i-th center. - k = selector.Get(); - } - - if (k < 0) { - center_padding_t(cur_batch, i) = 1.0; - continue; - } - center_padding_t(cur_batch, i) = 0.0; - center_t(cur_batch, i) = k; - - // Goes through all *non-seeded* points. If j-th point is within a radius - // of center, adds it to the sampler. - sampler.Reset(); - - std::vector neighbor_idx; - if (use_hash_lookup) { - // For each center, compute the bucket it is in. - int bucket_x = - FindBucket(points_t(cur_batch, k, 0), xmin, opts_.max_dist); - int bucket_y = - FindBucket(points_t(cur_batch, k, 1), ymin, opts_.max_dist); - int bucket_z = - FindBucket(points_t(cur_batch, k, 2), zmin, opts_.max_dist); - - // Iterate over 3x3x3 buckets centered at [bucket_x, bucket_y, bucket_z] - // - // Extract the neighborhood indices from there. - for (int bx = bucket_x - 1; bx <= bucket_x + 1; ++bx) { - if (bx < 0 || bx >= x_intervals) continue; - for (int by = bucket_y - 1; by <= bucket_y + 1; ++by) { - if (by < 0 || by >= y_intervals) continue; - for (int bz = bucket_z - 1; bz <= bucket_z + 1; ++bz) { - if (bz < 0 || bz >= z_intervals) continue; - auto bucket_id = BucketId(bx, by, bz, y_intervals, z_intervals); - auto bucket_indices = buckets_vec[bucket_id]; - neighbor_idx.insert(neighbor_idx.end(), bucket_indices.begin(), - bucket_indices.end()); - } - } - } - - } else { - neighbor_idx.reserve(num_points); - for (int j = num_seeded_points; j < num_points; ++j) { - if (points_padding_t(cur_batch, j) == 0.0) { - neighbor_idx.push_back(j); - } - } - } - - // Iterate over all neighbor indices. - for (int j : neighbor_idx) { - auto ss_xy = - Square(points_t(cur_batch, k, 0) - points_t(cur_batch, j, 0)) + - Square(points_t(cur_batch, k, 1) - points_t(cur_batch, j, 1)); - auto z = points_t(cur_batch, j, 2); - auto ss_xyz = ss_xy + Square(points_t(cur_batch, k, 2) - z); - if (ss_xyz <= threshold) { - sampler.Add(j, ss_xyz); - } - selector.Update(j, ss_xy); - } - auto ids = sampler.Get(); - CHECK_LE(0, ids.size()); - CHECK_LE(ids.size(), opts_.num_neighbors); - for (int j = 0; j < ids.size(); ++j) { - indices_t(cur_batch, i, j) = ids[j].id; - padding_t(cur_batch, i, j) = 0.0f; - } - } - } - - return result; -} - -string PSUtils::Options::DebugString() const { - // clang-format off - return strings::Printf( - "cmethod/#centers/zmin/zmax/nmethod/#neighbors/maxdist/seed " - "%s/%d/%.3f/%.3f/%s/%d/%.3f/%d", - cmethod == C_UNIFORM ? "uniform" : "farthest", - num_centers, - center_z_min, - center_z_max, - nmethod == N_UNIFORM ? "uniform" : "closest", num_neighbors, - max_dist, - random_seed); - // clang-format on -} - -PSUtils::Result PSUtils::Sample(const Tensor& points, - const Tensor& points_padding, - const int32 num_seeded_points) const { - if (opts_.cmethod == Options::C_UNIFORM && - opts_.nmethod == Options::N_UNIFORM) { - CHECK_EQ(num_seeded_points, 0) - << "Seeding only supported for Farthest Point Sampling"; - return DoSampling(points, points_padding, - num_seeded_points); - } - if (opts_.cmethod == Options::C_UNIFORM && - opts_.nmethod == Options::N_CLOSEST) { - CHECK_EQ(num_seeded_points, 0) - << "Seeding only supported for Farthest Point Sampling"; - return DoSampling(points, points_padding, - num_seeded_points); - } - if (opts_.cmethod == Options::C_FARTHEST && - opts_.nmethod == Options::N_UNIFORM) { - return DoSampling(points, points_padding, - num_seeded_points); - } - CHECK_EQ(opts_.cmethod, Options::C_FARTHEST); - CHECK_EQ(opts_.nmethod, Options::N_CLOSEST); - return DoSampling(points, points_padding, - num_seeded_points); -} - -} // namespace car -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/ps_utils.h b/lingvo/tasks/car/ops/ps_utils.h deleted file mode 100644 index e3ed6a8a..00000000 --- a/lingvo/tasks/car/ops/ps_utils.h +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright 2019 The TensorFlow 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. -==============================================================================*/ -#ifndef THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_PS_UTILS_H_ -#define THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_PS_UTILS_H_ - -#include - -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/platform/types.h" - -namespace tensorflow { -namespace lingvo { -namespace car { - -// Point cloud sampling utilities. -class PSUtils { - public: - struct Options { - // Center selection method. - enum CenterSelectionMethod { C_UNIFORM, C_FARTHEST }; - CenterSelectionMethod cmethod = C_FARTHEST; - - // The number of centers to sample. - int num_centers = 128; - - // Points with z outside of the following range are not considered for - // center selection. - float center_z_min = std::numeric_limits::lowest(); - float center_z_max = std::numeric_limits::max(); - - // Center selection method. - enum NeighborSelectionMethod { N_UNIFORM, N_CLOSEST }; - NeighborSelectionMethod nmethod = N_UNIFORM; - - // For each center, sample this many points within the neighorhood. - int num_neighbors = 1024; - - // Points with L2 distances in 3D from a center larger than this threshold - // is not considered to be in the neighborhood. - float max_dist = std::numeric_limits::max(); - - // The random seed. - int random_seed = -1; - - enum NeighborSearchAlgorithm { N_AUTO, N_HASH }; - NeighborSearchAlgorithm neighbor_search_algorithm = N_AUTO; - - string DebugString() const; - }; - - explicit PSUtils(const Options& opts) : opts_(opts) {} - - // Samples centers within 'points' (3D) and creates neighborhood for each - // center. - // - // If num_seeded_points is > 0, then the first num_seeded_points are used as - // seeds for the center selection component of sampling, but are ignored for - // neighborhood selection. - struct Result { - // [num_centers]. Indices of the center points. - Tensor center; - - // [num_centers]. 0/1 paddings indicating that 0 means i-th center is a real - // sampled center point, while 1 means otherwise. - Tensor center_padding; - - // [num_centers, num_neighbors]. Indices of neighbors. - Tensor indices; - - // indices[i, j] is a real point if and only if indices_padding[i, j] is - // 0. Otherwise, indices_padding[i, j] is 1.0. - Tensor indices_padding; - }; - Result Sample(const Tensor& points, const Tensor& points_padding, - const int32 num_seeded_points) const; - - private: - const Options opts_; - - uint64 Seed() const; - - template - Result DoSampling(const Tensor& points, const Tensor& points_padding, - const int32 num_seeded_points) const; -}; - -} // namespace car -} // namespace lingvo -} // namespace tensorflow - -#endif // THIRD_PARTY_PY_LINGVO_TASKS_CAR_OPS_PS_UTILS_H_ diff --git a/lingvo/tasks/car/ops/ps_utils_test.cc b/lingvo/tasks/car/ops/ps_utils_test.cc deleted file mode 100644 index c05f1509..00000000 --- a/lingvo/tasks/car/ops/ps_utils_test.cc +++ /dev/null @@ -1,371 +0,0 @@ -/* Copyright 2019 The TensorFlow 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. -==============================================================================*/ -#include "lingvo/tasks/car/ops/ps_utils.h" - -#include -#include - -#include -#include "tensorflow/core/lib/strings/stringprintf.h" -#include "tensorflow/core/platform/test_benchmark.h" - -namespace tensorflow { -namespace lingvo { -namespace car { - -struct Point { - float x; - float y; -}; - -// Generates batch_size sets of points, the k-th example in the batch has n-k -// centers (i-th center is on (i, i)) and m points near each center. -void GeneratePoints(int batch_size, int n, int m, Tensor* points, - Tensor* points_padding) { - std::mt19937 rng(39183); - *points = Tensor(DT_FLOAT, {batch_size, n * m, 3}); - *points_padding = Tensor(DT_FLOAT, {batch_size, n * m}); - auto points_t = points->tensor(); - auto points_padding_t = points_padding->matrix(); - points_padding_t.setConstant(0.0); - - for (int cur_batch = 0; cur_batch < batch_size; ++cur_batch) { - std::vector points; - for (int i = 0; i < n - cur_batch; ++i) { - for (int j = 0; j < m; ++j) { - float v = i + j / 1000.0; - points.push_back(Point{v, v}); - } - } - std::shuffle(points.begin(), points.end(), rng); - for (int i = 0; i < (n - cur_batch) * m; ++i) { - points_t(cur_batch, i, 0) = points[i].x; - points_t(cur_batch, i, 1) = points[i].y; - points_t(cur_batch, i, 2) = 0; - } - for (int i = (n - cur_batch) * m; i < n * m; ++i) { - points_padding_t(cur_batch, i) = 1.0; - } - } -} - -void Log(const Tensor& points, const PSUtils::Result& result) { - const int batch_size = result.center.dim_size(0); - const int n = result.center.dim_size(1); - CHECK_EQ(result.indices.dim_size(0), batch_size); - CHECK_EQ(result.indices.dim_size(1), n); - - const int m = result.indices.dim_size(2); - CHECK_EQ(result.indices_padding.dim_size(1), n); - CHECK_EQ(result.indices_padding.dim_size(2), m); - - auto points_t = points.tensor(); - auto center_t = result.center.matrix(); - auto center_padding_t = result.center_padding.matrix(); - auto indices_t = result.indices.tensor(); - auto indices_padding_t = result.indices_padding.tensor(); - for (int cur_batch = 0; cur_batch < batch_size; ++cur_batch) { - fprintf(stdout, "batch id %d\n", cur_batch); - for (int i = 0; i < n; ++i) { - CHECK_EQ(0.0, center_padding_t(cur_batch, i)); - fprintf(stdout, - "(%5.3f %5.3f): ", points_t(cur_batch, center_t(cur_batch, i), 0), - points_t(cur_batch, center_t(cur_batch, i), 1)); - for (int j = 0; j < m; ++j) { - fprintf(stdout, "(%5.3f %5.3f)/%1.0f, ", - points_t(cur_batch, indices_t(cur_batch, i, j), 0), - points_t(cur_batch, indices_t(cur_batch, i, j), 1), - indices_padding_t(cur_batch, i, j)); - } - fprintf(stdout, "\n"); - } - } -} - -std::vector GetCenters(const Tensor& points, - const PSUtils::Result& result) { - const int batch_size = result.center.dim_size(0); - const int n = result.center.dim_size(1); - CHECK_EQ(result.indices.dim_size(0), batch_size); - CHECK_EQ(result.indices.dim_size(1), n); - - const int m = result.indices.dim_size(2); - CHECK_EQ(result.indices_padding.dim_size(1), n); - CHECK_EQ(result.indices_padding.dim_size(2), m); - - auto points_t = points.tensor(); - auto center_t = result.center.matrix(); - auto center_padding_t = result.center_padding.matrix(); - auto indices_t = result.indices.tensor(); - std::vector centers; - for (int cur_batch = 0; cur_batch < batch_size; ++cur_batch) { - for (int i = 0; i < n; ++i) { - CHECK_EQ(0.0, center_padding_t(cur_batch, i)); - const int center = - static_cast(points_t(cur_batch, center_t(cur_batch, i), 0)); - for (int j = 0; j < m; ++j) { - CHECK_EQ(points_t(cur_batch, indices_t(cur_batch, i, j), 0), - points_t(cur_batch, indices_t(cur_batch, i, j), 1)); - CHECK_EQ(center, static_cast(points_t( - cur_batch, indices_t(cur_batch, i, j), 0))); - } - centers.push_back(center); - } - } - return centers; -} - -#if defined(PLATFORM_GOOGLE) -TEST(PSUtilsTest, Uniform_Uniform) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_UNIFORM; - opts.nmethod = PSUtils::Options::N_UNIFORM; - opts.num_centers = 8; - opts.num_neighbors = 16; - opts.max_dist = 1.0; - opts.random_seed = 12345; - PSUtils fu(opts); - Tensor points; - Tensor points_padding; - GeneratePoints(3, 8, 100, &points, &points_padding); - auto ret = fu.Sample(points, points_padding, 0); - Log(points, ret); - // Generated points will correspond to clusters like: - // (0, 0), (0, 0.001), (0, 0.002), ... - // (1, 1), (1, 1.001), (1, 1.002), ... - // ... - // (7, 7), (7, 7.001), (7, 7.002), ... - // - // GetCenters returns the first coordinate of each point, and hence, we - // expect them to repeat. - // Some clusters are sampled more than once. - EXPECT_EQ(GetCenters(points, ret), - std::vector({1, 0, 2, 4, 0, 3, 1, 3, // 1st example. - 1, 2, 5, 3, 5, 2, 0, 0, // 2nd example. - 1, 2, 5, 2, 5, 4, 5, 4})); // 3rd example. -} - -TEST(PSUtilsTest, Uniform_Closest) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_UNIFORM; - opts.nmethod = PSUtils::Options::N_CLOSEST; - opts.num_centers = 8; - opts.num_neighbors = 16; - opts.max_dist = 1.0; - opts.random_seed = 12345; - PSUtils fu(opts); - Tensor points; - Tensor points_padding; - GeneratePoints(3, 8, 100, &points, &points_padding); - auto ret = fu.Sample(points, points_padding, 0); - Log(points, ret); - // Some clusters are sampled more than once. - EXPECT_EQ(GetCenters(points, ret), - std::vector({1, 0, 2, 4, 0, 3, 1, 3, // 1st example. - 1, 2, 5, 3, 5, 2, 0, 0, // 2nd example. - 1, 2, 5, 2, 5, 4, 5, 4})); // 3rd example. -} - -TEST(PSUtilsTest, Farthest_Uniform) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_FARTHEST; - opts.nmethod = PSUtils::Options::N_UNIFORM; - opts.num_centers = 8; - opts.num_neighbors = 16; - opts.max_dist = 1.0; - opts.random_seed = 12345; - PSUtils fu(opts); - Tensor points; - Tensor points_padding; - GeneratePoints(3, 8, 100, &points, &points_padding); - auto ret = fu.Sample(points, points_padding, 0); - Log(points, ret); - // Generated points will correspond to clusters like: - // (0, 0), (0, 0.001), (0, 0.002), ... - // (1, 1), (1, 1.001), (1, 1.002), ... - // ... - // (7, 7), (7, 7.001), (7, 7.002), ... - // - // GetCenters returns the first coordinate of each point. With farthest point - // sampling, for an example without any paddings, we expect the samples to - // have all different first coordinates, and all 8 clusters should be covered. - EXPECT_EQ( - GetCenters(points, ret), - std::vector( - {3, 7, 0, 5, 1, 6, 4, 2, // 1st example. - 6, 0, 3, 1, 4, 5, 2, 0, // 2nd example, last one is a duplicate. - 3, 0, 5, 1, 4, 2, 0, 1})); // 3rd example, last two are duplicates. -} - -TEST(PSUtilsTest, Farthest_Uniform_Hash) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_FARTHEST; - opts.nmethod = PSUtils::Options::N_UNIFORM; - opts.neighbor_search_algorithm = PSUtils::Options::N_HASH; - opts.num_centers = 8; - opts.num_neighbors = 16; - opts.max_dist = 1.0; - opts.random_seed = 12345; - PSUtils fu(opts); - Tensor points; - Tensor points_padding; - GeneratePoints(3, 8, 100, &points, &points_padding); - auto ret = fu.Sample(points, points_padding, 0); - Log(points, ret); - // Like above, but notice that the order is different since the algorithm is - // changed and uniform provides no ordering guarantees. - EXPECT_EQ( - GetCenters(points, ret), - std::vector( - {3, 6, 1, 4, 7, 0, 2, 5, // 1st example. - 6, 4, 0, 2, 3, 1, 5, 4, // 2nd example, last one is a duplicate. - 3, 5, 1, 0, 4, 2, 0, 3})); // 3rd example, last two are duplicates. -} - -TEST(PSUtilsTest, Farthest_Closest) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_FARTHEST; - opts.nmethod = PSUtils::Options::N_CLOSEST; - opts.num_centers = 8; - opts.num_neighbors = 16; - opts.max_dist = 10.0; - opts.random_seed = 12345; - PSUtils fu(opts); - Tensor points; - Tensor points_padding; - GeneratePoints(3, 8, 100, &points, &points_padding); - auto ret = fu.Sample(points, points_padding, 0); - Log(points, ret); - // All 8 clusters are covered. - EXPECT_EQ( - GetCenters(points, ret), - std::vector( - {3, 7, 0, 5, 1, 6, 4, 2, // 1st example. - 6, 0, 3, 1, 4, 5, 2, 0, // 2nd example, last one is a duplicate. - 3, 0, 5, 1, 4, 2, 0, 1})); // 3rd example, last two are duplicates. -} - -TEST(PSUtilsTest, Farthest_Closest_Hash) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_FARTHEST; - opts.nmethod = PSUtils::Options::N_CLOSEST; - opts.neighbor_search_algorithm = PSUtils::Options::N_HASH; - opts.num_centers = 8; - opts.num_neighbors = 16; - opts.max_dist = 10.0; - opts.random_seed = 12345; - PSUtils fu(opts); - Tensor points; - Tensor points_padding; - GeneratePoints(3, 8, 100, &points, &points_padding); - auto ret = fu.Sample(points, points_padding, 0); - Log(points, ret); - // All 8 clusters are covered. - EXPECT_EQ( - GetCenters(points, ret), - std::vector( - {3, 7, 0, 5, 1, 6, 4, 2, // 1st example. - 6, 0, 3, 1, 4, 5, 2, 0, // 2nd example, last one is a duplicate. - 3, 0, 5, 1, 4, 2, 0, 1})); // 3rd example, last two are duplicates. -} - -TEST(PSUtilsTest, TestSeeded) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_FARTHEST; - opts.nmethod = PSUtils::Options::N_CLOSEST; - opts.num_centers = 4; - opts.num_neighbors = 2; - opts.max_dist = 10.0; - opts.random_seed = 12345; - PSUtils fu(opts); - - // Six points along a line: (0, 1, 2, 3, 4, 5). - Tensor points(DT_FLOAT, {1, 6, 3}); - Tensor points_padding(DT_FLOAT, {1, 6}); - auto points_t = points.tensor(); - auto points_padding_t = points_padding.matrix(); - points_padding_t.setConstant(0.0); - for (int i = 0; i < 6; ++i) { - points_t(0, i, 0) = i; - points_t(0, i, 1) = 0; - points_t(0, i, 2) = 0; - } - - // Choose the first two points as the seed (0, and 1). - auto ret = fu.Sample(points, points_padding, 2); - Log(points, ret); - - // The first two are always chosen, and then the next one is 5, since it is - // the farthest from 0 and 1. The remaining is the middle point between 1 and - // 5. - auto center_t = ret.center.matrix(); - EXPECT_EQ(points_t(0, center_t(0, 0), 0), 0.); - EXPECT_EQ(points_t(0, center_t(0, 1), 0), 1.); - EXPECT_EQ(points_t(0, center_t(0, 2), 0), 5.); - EXPECT_EQ(points_t(0, center_t(0, 3), 0), 3.); - - // Seeded points are not neighbors; the closest neighbor of the seeded - // point is 2., not 1. - auto indices_t = ret.indices.tensor(); - EXPECT_EQ(points_t(0, indices_t(0, 0, 0), 0), 2.); -} - -void BenchmarkFarthestPoint(int iters, int num_centers, int num_neighbors, - PSUtils::Options opts) { - testing::StopTiming(); - testing::SetLabel(strings::Printf("#Centers=%4d #Neighbors=%4d", num_centers, - num_neighbors)); - PSUtils fu(opts); - Tensor points; - Tensor points_padding; - GeneratePoints(1, 1000, 100, &points, &points_padding); - testing::StartTiming(); - for (int i = 0; i < iters; ++i) { - auto ret = fu.Sample(points, points_padding, 0); - } -} - -void BM_Farthest(int iters, int num_centers, int num_neighbors) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_FARTHEST; - opts.nmethod = PSUtils::Options::N_UNIFORM; - opts.num_centers = num_centers; - opts.num_neighbors = num_neighbors; - opts.max_dist = 1.0; - opts.random_seed = -1; - BenchmarkFarthestPoint(iters, num_centers, num_neighbors, opts); -} - -BENCHMARK(BM_Farthest)->RangePair(1, 1024, 1, 1024); - -void BM_FarthestHash(int iters, int num_centers, int num_neighbors) { - PSUtils::Options opts; - opts.cmethod = PSUtils::Options::C_FARTHEST; - opts.nmethod = PSUtils::Options::N_UNIFORM; - opts.neighbor_search_algorithm = PSUtils::Options::N_HASH; - opts.num_centers = num_centers; - opts.num_neighbors = num_neighbors; - opts.max_dist = 1.0; - opts.random_seed = -1; - BenchmarkFarthestPoint(iters, num_centers, num_neighbors, opts); -} - -BENCHMARK(BM_FarthestHash)->RangePair(1, 1024, 1, 1024); - -#endif - -} // namespace car -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/sampling_ops.cc b/lingvo/tasks/car/ops/sampling_ops.cc deleted file mode 100644 index d53a032a..00000000 --- a/lingvo/tasks/car/ops/sampling_ops.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright 2019 The TensorFlow 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. -==============================================================================*/ -#include "lingvo/tasks/car/ops/ps_utils.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/lib/core/errors.h" - -namespace tensorflow { -namespace lingvo { -namespace car { - -class SamplePointsOp : public OpKernel { - public: - explicit SamplePointsOp(OpKernelConstruction* ctx) : OpKernel(ctx) { - string method; - OP_REQUIRES_OK(ctx, ctx->GetAttr("center_selector", &method)); - OP_REQUIRES(ctx, method == "uniform" || method == "farthest", - errors::InvalidArgument(method, " is not valid.")); - if (method == "uniform") { - opts_.cmethod = PSUtils::Options::C_UNIFORM; - } else { - CHECK_EQ(method, "farthest"); - opts_.cmethod = PSUtils::Options::C_FARTHEST; - } - OP_REQUIRES_OK(ctx, ctx->GetAttr("neighbor_sampler", &method)); - OP_REQUIRES(ctx, method == "uniform" || method == "closest", - errors::InvalidArgument(method, " is not valid.")); - if (method == "uniform") { - opts_.nmethod = PSUtils::Options::N_UNIFORM; - } else { - CHECK_EQ(method, "closest"); - opts_.nmethod = PSUtils::Options::N_CLOSEST; - } - - OP_REQUIRES_OK(ctx, ctx->GetAttr("neighbor_algorithm", &method)); - OP_REQUIRES( - ctx, method == "auto" || method == "hash", - errors::InvalidArgument(method, " is not a valid neighbor algorithm.")); - if (method == "hash") { - opts_.neighbor_search_algorithm = PSUtils::Options::N_HASH; - } - - LOG(INFO) << "Sampling options: " << opts_.DebugString(); - OP_REQUIRES_OK(ctx, ctx->GetAttr("num_centers", &opts_.num_centers)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("center_z_min", &opts_.center_z_min)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("center_z_max", &opts_.center_z_max)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("num_neighbors", &opts_.num_neighbors)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("max_distance", &opts_.max_dist)); - OP_REQUIRES_OK(ctx, ctx->GetAttr("random_seed", &opts_.random_seed)); - } - - void Compute(OpKernelContext* ctx) override { - PSUtils ps(opts_); - auto ret = ps.Sample(ctx->input(0), ctx->input(1), - ctx->input(2).scalar()()); - ctx->set_output(0, ret.center); - ctx->set_output(1, ret.center_padding); - ctx->set_output(2, ret.indices); - ctx->set_output(3, ret.indices_padding); - } - - private: - PSUtils::Options opts_; -}; - -REGISTER_KERNEL_BUILDER(Name("SamplePoints").Device(DEVICE_CPU), - SamplePointsOp); - -} // namespace car -} // namespace lingvo -} // namespace tensorflow diff --git a/lingvo/tasks/car/ops/sampling_ops_test.py b/lingvo/tasks/car/ops/sampling_ops_test.py deleted file mode 100644 index e66161f4..00000000 --- a/lingvo/tasks/car/ops/sampling_ops_test.py +++ /dev/null @@ -1,160 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for lingvo.tasks.car.ops.sampling_ops.""" - -from absl.testing import parameterized -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import ops - -import numpy as np - - -class SamplingOpsTest(parameterized.TestCase, test_utils.TestCase): - - @parameterized.named_parameters([ - ('uniform_uniform', 'uniform', 'uniform', 'auto'), - ('uniform_closest', 'uniform', 'closest', 'auto'), - ('farthest_uniform', 'farthest', 'uniform', 'auto'), - ('farthest_closest', 'farthest', 'closest', 'auto'), - ('farthest_closest_hash', 'farthest', 'closest', 'hash'), - ]) - def testBasic(self, cmethod, nmethod, nalgo): - b, n, m, k = 3, 10000, 128, 128 - g = tf.Graph() - with g.as_default(): - points = tf.random.uniform(shape=(b, n, 3)) - points_padding = tf.zeros(shape=(b, n)) - center, center_padding, indices, indices_padding = ops.sample_points( - points=points, - points_padding=points_padding, - num_seeded_points=0, - center_selector=cmethod, - neighbor_sampler=nmethod, - neighbor_algorithm=nalgo, - num_centers=m, - center_z_min=-np.inf, - center_z_max=np.inf, - num_neighbors=k, - max_distance=0.25) - - # Ensure shapes are known at graph construction. - self.assertListEqual(center.shape.as_list(), [b, m]) - self.assertListEqual(center_padding.shape.as_list(), [b, m]) - self.assertListEqual(indices.shape.as_list(), [b, m, k]) - self.assertListEqual(indices_padding.shape.as_list(), [b, m, k]) - - with self.session(graph=g): - cp, c, i, p = self.evaluate( - [center_padding, center, indices, indices_padding]) - - # Very basic validity checking. - self.assertEqual(cp.shape, (b, m)) - self.assertTrue(np.all(cp == 0.0)) - self.assertEqual(c.shape, (b, m)) - self.assertTrue(np.all(np.logical_and(0 <= c, c < n))) - self.assertEqual(i.shape, (b, m, k)) - self.assertTrue(np.all(np.logical_and(0 <= i, i < n))) - self.assertEqual(p.shape, (b, m, k)) - self.assertTrue(np.all(np.logical_or(0. == p, 1. == p))) - - @parameterized.named_parameters([ - ('uniform_uniform', 'uniform', 'uniform', 'auto'), - ('uniform_closest', 'uniform', 'closest', 'auto'), - ('farthest_uniform', 'farthest', 'uniform', 'auto'), - ('farthest_closest', 'farthest', 'closest', 'auto'), - ('farthest_closest_hash', 'farthest', 'closest', 'hash'), - ]) - def testZFilter(self, cmethod, nmethod, nalgo): - b, n, m, k = 1, 10000, 128, 128 - g = tf.Graph() - with g.as_default(): - points = tf.random.uniform(shape=(b, n, 3)) - points_padding = tf.zeros(shape=(b, n)) - center, center_padding, indices, indices_padding = ops.sample_points( - points=points, - points_padding=points_padding, - num_seeded_points=0, - center_selector=cmethod, - neighbor_sampler=nmethod, - num_centers=m, - center_z_min=0.25, - center_z_max=0.75, - num_neighbors=k, - max_distance=0.25) - - # Ensure shapes are known at graph construction. - self.assertListEqual(center.shape.as_list(), [b, m]) - self.assertListEqual(center_padding.shape.as_list(), [b, m]) - self.assertListEqual(indices.shape.as_list(), [b, m, k]) - self.assertListEqual(indices_padding.shape.as_list(), [b, m, k]) - - with self.session(graph=g): - c1, p1 = self.evaluate([center, points]) - c2, p2 = self.evaluate([center, points]) - - # With extremely high probability, sampling centers twice should be - # different. - self.assertGreater(np.setdiff1d(c1, c2).size, 0) - - # Centers should be filtered by z range. - self.assertTrue((0.25 <= p1[0, c1[0], 2]).all()) - self.assertTrue((p1[0, c1[0], 2] <= 0.75).all()) - self.assertTrue((0.25 <= p2[0, c2[0], 2]).all()) - self.assertTrue((p2[0, c2[0], 2] <= 0.75).all()) - - @parameterized.named_parameters([ - ('uniform_uniform', 'uniform', 'uniform'), - ('uniform_closest', 'uniform', 'closest'), - ('farthest_uniform', 'farthest', 'uniform'), - ('farthest_closest', 'farthest', 'closest'), - ]) - def testSampleFewerCentersThanPoints(self, cmethod, nmethod): - b, n, m, k = 1, 100, 128, 8 - g = tf.Graph() - with g.as_default(): - points = tf.random.uniform(shape=(b, n, 3)) - points_padding = tf.zeros(shape=(b, n)) - center, center_padding, indices, indices_padding = ops.sample_points( - points=points, - points_padding=points_padding, - num_seeded_points=0, - center_selector=cmethod, - neighbor_sampler=nmethod, - num_centers=m, - center_z_min=-np.inf, - center_z_max=np.inf, - num_neighbors=k, - max_distance=0.25) - - # Ensure shapes are known at graph construction. - self.assertListEqual(center.shape.as_list(), [b, m]) - self.assertListEqual(center_padding.shape.as_list(), [b, m]) - self.assertListEqual(indices.shape.as_list(), [b, m, k]) - self.assertListEqual(indices_padding.shape.as_list(), [b, m, k]) - - with self.session(graph=g): - p, c = self.evaluate([center_padding, center]) - - self.assertAllEqual(p[0, :n], np.zeros([n])) - self.assertAllEqual(p[0, n:], np.ones([m - n])) - tf.logging.info('c[:n]=%s', c[0, :n]) - self.assertAllEqual(np.sort(c[0, :n]), np.arange(n)) - self.assertAllEqual(c[0, n:], np.zeros([m - n])) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/params/BUILD b/lingvo/tasks/car/params/BUILD deleted file mode 100644 index c62c60c0..00000000 --- a/lingvo/tasks/car/params/BUILD +++ /dev/null @@ -1,57 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "kitti", - srcs = ["kitti.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo:model_registry", - "//lingvo/core:base_model_params", - "//lingvo/core:cluster_factory", - "//lingvo/core:datasource", - "//lingvo/core:optimizer", - "//lingvo/core:py_utils", - "//lingvo/tasks/car:input_preprocessors", - "//lingvo/tasks/car:kitti_input_generator", - "//lingvo/tasks/car:lr_util", - "//lingvo/tasks/car:starnet", - # Implicit numpy dependency. - ], -) - -py_library( - name = "waymo", - srcs = ["waymo.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:model_registry", - "//lingvo/core:base_model_params", - "//lingvo/core:cluster_factory", - "//lingvo/core:hyperparams", - "//lingvo/core:optimizer", - "//lingvo/core:py_utils", - "//lingvo/tasks/car:input_preprocessors", - "//lingvo/tasks/car:lr_util", - "//lingvo/tasks/car:starnet", - "//lingvo/tasks/car/waymo:waymo_decoder", - "//lingvo/tasks/car/waymo:waymo_metadata", - "//lingvo/tasks/car/waymo:waymo_open_input_generator", - # Implicit numpy dependency. - ], -) - -py_library( - name = "params", - srcs = ["params.py"], - srcs_version = "PY3", - tags = ["keep_dep"], - deps = [ - ":kitti", - ":waymo", - ], -) diff --git a/lingvo/tasks/car/params/kitti.py b/lingvo/tasks/car/params/kitti.py deleted file mode 100644 index 0d229760..00000000 --- a/lingvo/tasks/car/params/kitti.py +++ /dev/null @@ -1,628 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Train models on KITTI data.""" - -import os - -from lingvo import compat as tf -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.core import cluster_factory -from lingvo.core import datasource -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.tasks.car import input_preprocessors -from lingvo.tasks.car import kitti_input_generator -from lingvo.tasks.car import lr_util -from lingvo.tasks.car import starnet -import numpy as np - - -# Set $KITTI_DIR to the base path of where all the KITTI files can be found. -# -# E.g., 'gs://your-bucket/kitti/3d' -_KITTI_BASE = os.environ.get('KITTI_DIR', 'FILL-ME-IN') - - -# Specifications for the different dataset splits. -def KITTITrainSpec(params): - p = params.Copy() - p.file_datasource.file_pattern = ( - 'kitti_object_3dop_train.tfrecord-*-of-00100') - p.num_samples = 3712 - return p - - -def KITTIValSpec(params): - p = params.Copy() - p.file_datasource.file_pattern = ('kitti_object_3dop_val.tfrecord-*-of-00100') - p.num_samples = 3769 - return p - - -def KITTITestSpec(params): - p = params.Copy() - p.file_datasource.file_pattern = ('kitti_object_test.tfrecord-*-of-00100') - p.num_samples = 7518 - return p - - -class KITTITrain(kitti_input_generator.KITTILaser): - """KITTI train set with raw laser data.""" - - @classmethod - def Params(cls): - """Defaults params.""" - p = super().Params() - return KITTITrainSpec(p) - - -class KITTIValidation(kitti_input_generator.KITTILaser): - """KITTI validation set with raw laser data.""" - - @classmethod - def Params(cls): - """Defaults params.""" - p = super().Params() - return KITTIValSpec(p) - - -class KITTITest(kitti_input_generator.KITTILaser): - """KITTI test set with raw laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return KITTITestSpec(p) - - -class KITTIGridTrain(kitti_input_generator.KITTIGrid): - """KITTI train set with grid laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return KITTITrainSpec(p) - - -class KITTIGridValidation(kitti_input_generator.KITTIGrid): - """KITTI validation set with grid laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return KITTIValSpec(p) - - -class KITTIGridTest(kitti_input_generator.KITTIGrid): - """KITTI validation set with grid laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return KITTITestSpec(p) - - -class KITTISparseLaserTrain(kitti_input_generator.KITTISparseLaser): - """KITTI train set with sparse laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return KITTITrainSpec(p) - - -class KITTISparseLaserValidation(kitti_input_generator.KITTISparseLaser): - """KITTI validation set with sparse laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return KITTIValSpec(p) - - -class KITTISparseLaserTest(kitti_input_generator.KITTISparseLaser): - """KITTI test set with sparse laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return KITTITestSpec(p) - - -def _MaybeRemove(values, key): - """Remove the entry 'key' from 'values' if present.""" - if key in values: - values.remove(key) - - -def AddLaserAndCamera(params): - """Adds laser and camera extractors.""" - cluster = cluster_factory.Current() - job = cluster.job - if job != 'decoder': - return params - - extractor_params = list(dict(params.extractors.IterParams()).values()) - extractor_classes = [p.cls for p in extractor_params] - - # Add images if not present. - if kitti_input_generator.KITTIImageExtractor not in extractor_classes: - params.extractors.Define('images', - kitti_input_generator.KITTIImageExtractor.Params(), - '') - - # Add raw lasers if not present. - if kitti_input_generator.KITTILaserExtractor not in extractor_classes: - labels = None - for p in extractor_params: - if p.cls == kitti_input_generator.KITTILabelExtractor: - labels = p - if labels is None: - labels = kitti_input_generator.KITTILabelExtractor.Params() - params.extractors.Define( - 'lasers', kitti_input_generator.KITTILaserExtractor.Params(labels), '') - - return params - - -################################################################################ -# StarNet -################################################################################ -@model_registry.RegisterSingleTaskModel -class StarNetCarsBase(base_model_params.SingleTaskModelParams): - """StarNet model for cars.""" - RUN_LOCALLY = False - NUM_ANCHOR_BBOX_OFFSETS = 25 - NUM_ANCHOR_BBOX_ROTATIONS = 4 - NUM_ANCHOR_BBOX_DIMENSIONS = 1 - FOREGROUND_ASSIGNMENT_THRESHOLD = 0.6 - BACKGROUND_ASSIGNMENT_THRESHOLD = 0.45 - INCLUDED_CLASSES = ['Car'] - - class AnchorBoxSettings(input_preprocessors.SparseCarV1AnchorBoxSettings): - ROTATIONS = [0, np.pi / 2, 3. * np.pi / 4, np.pi / 4] - - def _configure_input(self, p): - """Base function managing the delegation of job specific input configs.""" - self._configure_generic_input(p) - cluster = cluster_factory.Current() - job = cluster.job - if job.startswith('trainer'): - self._configure_trainer_input(p) - elif job.startswith('decoder'): - self._configure_decoder_input(p) - elif job.startswith('evaler'): - self._configure_evaler_input(p) - else: - tf.logging.info('There are no input configuration changes to for ' - 'job {}.'.format(job)) - if self.RUN_LOCALLY: - p.num_batcher_threads = 1 - p.file_buffer_size = 1 - p.file_parallelism = 1 - - def _configure_generic_input(self, p): - """Update input_config `p` for all jobs.""" - p.file_datasource.file_pattern_prefix = _KITTI_BASE - - # Perform frustum dropping before ground removal (keep_xyz_range). - p.preprocessors.Define( - 'remove_out_of_frustum', - (input_preprocessors.KITTIDropPointsOutOfFrustum.Params()), '') - p.preprocessors_order.insert( - p.preprocessors_order.index('keep_xyz_range'), 'remove_out_of_frustum') - - # Approximate ground removal. - p.preprocessors.keep_xyz_range.keep_z_range = (-1.35, np.inf) - - # Max num points can be smaller since we have dropped points out of frustum. - p.preprocessors.pad_lasers.max_num_points = 32768 - - # TODO(jngiam): Analyze if these settings are optimal. - p.preprocessors.select_centers.num_cell_centers = 256 - p.preprocessors.gather_features.num_points_per_cell = 512 - p.preprocessors.gather_features.sample_neighbors_uniformly = True - p.preprocessors.gather_features.max_distance = 3.0 - - p.preprocessors.assign_anchors.foreground_assignment_threshold = ( - self.FOREGROUND_ASSIGNMENT_THRESHOLD) - p.preprocessors.assign_anchors.background_assignment_threshold = ( - self.BACKGROUND_ASSIGNMENT_THRESHOLD) - - # Apply car anchor box settings. - tile_anchors_p = p.preprocessors.tile_anchors - self.AnchorBoxSettings.Update(p.preprocessors.tile_anchors) - num_anchor_configs = ( - self.NUM_ANCHOR_BBOX_OFFSETS * self.NUM_ANCHOR_BBOX_ROTATIONS * - self.NUM_ANCHOR_BBOX_DIMENSIONS) - - assert len(tile_anchors_p.anchor_box_dimensions) == num_anchor_configs - assert len(tile_anchors_p.anchor_box_rotations) == num_anchor_configs - assert len(tile_anchors_p.anchor_box_offsets) == num_anchor_configs - - # Filter label extractor for anchors and visualization. - if 'labels' in p.extractors: - filtered_labels = [ - kitti_input_generator.KITTILabelExtractor.KITTI_CLASS_NAMES.index( - class_name) for class_name in self.INCLUDED_CLASSES - ] - p.extractors.labels.filter_labels = filtered_labels - p = AddLaserAndCamera(p) - - def _configure_trainer_input(self, p): - """Update input_config `p` for jobs running training.""" - # TODO(bencaine): Change the default in input_generator to be False - # and only set this true in _configure_decoder_input - p.extractors.images.decode_image = False - _MaybeRemove(p.preprocessors_order, 'count_points') - _MaybeRemove(p.preprocessors_order, 'viz_copy') - p.preprocessors.Define( - 'rot_box', (input_preprocessors.RandomBBoxTransform.Params().Set( - max_rotation=np.pi / 20.)), '') - p.preprocessors.Define('random_flip', - input_preprocessors.RandomFlipY.Params(), '') - p.preprocessors.Define( - 'global_rot', - (input_preprocessors.RandomWorldRotationAboutZAxis.Params().Set( - max_rotation=np.pi / 4.)), '') - p.preprocessors.Define( - 'world_scaling', - (input_preprocessors.WorldScaling.Params().Set(scaling=[0.95, 1.05])), - '') - - # Do per object transforms, then random flip, then global rotation, then - # global scaling. - preprocessor_order = [ - 'rot_box', 'random_flip', 'global_rot', 'world_scaling' - ] - insert_index = p.preprocessors_order.index('select_centers') - p.preprocessors_order = ( - p.preprocessors_order[:insert_index] + preprocessor_order + - p.preprocessors_order[insert_index:]) - - # Add ground truth augmenter to before all preprocessors. - allowed_label_ids = [ - kitti_input_generator.KITTILabelExtractor.KITTI_CLASS_NAMES.index( - class_name) for class_name in self.INCLUDED_CLASSES - ] - groundtruth_db = datasource.PrefixedDataSource.Params() - groundtruth_db.file_pattern_prefix = _KITTI_BASE - groundtruth_db.file_pattern = ('kitti_train_object_cls.tfrecord-*-of-00100') - - p.preprocessors.Define( - 'bbox_aug', (input_preprocessors.GroundTruthAugmentor.Params().Set( - groundtruth_database=groundtruth_db, - num_db_objects=19700, - filter_min_points=5, - max_augmented_bboxes=15, - label_filter=allowed_label_ids, - )), '') - p.preprocessors_order = ['bbox_aug'] + p.preprocessors_order - - p.preprocessors.Define('frustum_dropout', - (input_preprocessors.FrustumDropout.Params().Set( - theta_width=0.03, phi_width=0.0)), '') - p.preprocessors_order.insert( - p.preprocessors_order.index('gather_features'), 'frustum_dropout') - - p.batch_size = 2 - p.file_parallelism = 64 - p.num_batcher_threads = 64 - - def _configure_decoder_input(self, p): - """Update input_config `p` for jobs running decoding.""" - p.batch_size = 4 - p.file_parallelism = 8 - p.num_batcher_threads = 8 - p.file_buffer_size = 500 - - def _configure_evaler_input(self, p): - """Update input_config `p` for jobs running evaluation.""" - # TODO(bencaine): Change the default in input_generator to be False - # and only set this true in _configure_decoder_input - p.extractors.images.decode_image = False - _MaybeRemove(p.preprocessors_order, 'count_points') - _MaybeRemove(p.preprocessors_order, 'viz_copy') - p.batch_size = 4 - p.file_parallelism = 8 - p.num_batcher_threads = 8 - p.file_buffer_size = 500 - - def Train(self): - p = KITTISparseLaserTrain.Params() - self._configure_input(p) - return p - - def Test(self): - p = KITTISparseLaserTest.Params() - self._configure_input(p) - return p - - def Dev(self): - p = KITTISparseLaserValidation.Params() - self._configure_input(p) - return p - - def Task(self): - num_classes = len( - kitti_input_generator.KITTILabelExtractor.KITTI_CLASS_NAMES) - p = starnet.ModelV2.Params( - num_classes, - num_anchor_bboxes_offsets=self.NUM_ANCHOR_BBOX_OFFSETS, - num_anchor_bboxes_rotations=self.NUM_ANCHOR_BBOX_ROTATIONS, - num_anchor_bboxes_dimensions=self.NUM_ANCHOR_BBOX_DIMENSIONS) - - p.name = 'sparse_detector' - - tp = p.train - tp.optimizer = optimizer.Adam.Params() - tp.clip_gradient_norm_to_value = 5 - - ep = p.eval - # Evaluate the whole dataset. - ep.samples_per_summary = 0 - - # To be tuned. - p.train.l2_regularizer_weight = 1e-4 - - # Adapted from V1 tuning. - tp.ema_decay = 0.99 - # TODO(b/148537111): consider setting this to True. - tp.ema_decay_moving_vars = False - tp.learning_rate = 0.001 - lr_util.SetExponentialLR( - train_p=tp, - train_input_p=self.Train(), - exp_start_epoch=150, - total_epoch=650) - - p.dimension_loss_weight = .3 - p.location_loss_weight = 3. - p.loss_weight_classification = 1. - p.loss_weight_localization = 3. - p.rotation_loss_weight = 0.3 - - return p - - -@model_registry.RegisterSingleTaskModel -class StarNetCarModel0701(StarNetCarsBase): - """StarNet Car model trained on KITTI.""" - - class AnchorBoxSettings(input_preprocessors.SparseCarV1AnchorBoxSettings): - CENTER_X_OFFSETS = np.linspace(-1.294, 1.294, 5) - CENTER_Y_OFFSETS = np.linspace(-1.294, 1.294, 5) - - def _configure_generic_input(self, p): - super()._configure_generic_input(p) - # For selecting centers, drop points out of frustum and do approximate - # ground removal. - p.preprocessors.select_centers.features_preparation_layers = [ - input_preprocessors.KITTIDropPointsOutOfFrustum.Params(), - input_preprocessors.DropLaserPointsOutOfRange.Params().Set( - keep_z_range=(-1., np.inf)), - ] - - # Remove frustum dropping from original preprocessors. - p.preprocessors_order.remove('remove_out_of_frustum') - - # Keep all points in front of the car for featurizing, do not remove ground. - p.preprocessors.keep_xyz_range.keep_x_range = (0., np.inf) - p.preprocessors.keep_xyz_range.keep_y_range = (-40., 40.) - p.preprocessors.keep_xyz_range.keep_z_range = (-np.inf, np.inf) - p.preprocessors.pad_lasers.max_num_points = 72000 - - p.preprocessors.select_centers.sampling_method = 'farthest_point' - - p.preprocessors.select_centers.num_cell_centers = 768 - - p.preprocessors.gather_features.max_distance = 3.75 - - p.preprocessors.assign_anchors.foreground_assignment_threshold = 0.567087 - # Disable ignore class, by setting background threshold > foreground. - p.preprocessors.assign_anchors.background_assignment_threshold = 1.0 - - p.preprocessors.select_centers.features_preparation_layers = [ - input_preprocessors.KITTIDropPointsOutOfFrustum.Params(), - input_preprocessors.DropLaserPointsOutOfRange.Params().Set( - keep_z_range=(-1.4, np.inf)), - ] - - def _configure_trainer_input(self, p): - super()._configure_trainer_input(p) - - p.preprocessors.Define( - 'global_loc_noise', - (input_preprocessors.GlobalTranslateNoise.Params().Set( - noise_std=[0., 0., 0.35])), '') - p.preprocessors_order.insert( - p.preprocessors_order.index('world_scaling') + 1, 'global_loc_noise') - - def Task(self): - p = super().Task() - - # Builder configuration. - builder = starnet.Builder() - builder.linear_params_init = py_utils.WeightInit.KaimingUniformFanInRelu() - gin_layer_sizes = [32, 256, 512, 256, 256, 128] - num_laser_features = 1 - gin_layers = [ - # Each layer should expect as input - 2 * dims of the last layer's - # output. We assume a middle layer that's the size of 2 * dim_out. - [dim_in * 2, dim_out * 2, dim_out] - for (dim_in, dim_out) in zip(gin_layer_sizes[:-1], gin_layer_sizes[1:]) - ] - p.cell_feature_dims = sum(gin_layer_sizes) - p.cell_featurizer = builder.GINFeaturizerV2( - name='feat', - fc_dims=gin_layer_sizes[0], - mlp_dims=gin_layers, - num_laser_features=num_laser_features, - fc_use_bn=False) - p.anchor_projected_feature_dims = 512 - - # Loss and training params - p.train.learning_rate = 0.001 / 2. # Divide by batch size. - p.focal_loss_alpha = 0.2 - p.focal_loss_gamma = 3.0 - class_name_to_idx = kitti_input_generator.KITTILabelExtractor.KITTI_CLASS_NAMES - num_classes = len(class_name_to_idx) - p.per_class_loss_weight = [0.] * num_classes - p.per_class_loss_weight[class_name_to_idx.index('Car')] = 1. - - # Decoding / NMS params. - p.use_oriented_per_class_nms = True - p.max_nms_boxes = 512 - p.nms_iou_threshold = [0.0] * num_classes - p.nms_iou_threshold[class_name_to_idx.index('Car')] = 0.0831011 - p.nms_score_threshold = [1.0] * num_classes - p.nms_score_threshold[class_name_to_idx.index('Car')] = 0.321310 - p.output_decoder.truncation_threshold = 0.65 - p.output_decoder.filter_predictions_outside_frustum = True - return p - - -@model_registry.RegisterSingleTaskModel -class StarNetPedCycModel0704(StarNetCarsBase): - """StarNet Ped/Cyc model trained on KITTI.""" - - INCLUDED_CLASSES = ['Pedestrian', 'Cyclist'] - - FOREGROUND_ASSIGNMENT_THRESHOLD = 0.48 - # Any value > FOREGROUND is equivalent. - BACKGROUND_ASSIGNMENT_THRESHOLD = 0.80 - - NUM_ANCHOR_BBOX_OFFSETS = 9 - NUM_ANCHOR_BBOX_ROTATIONS = 4 - NUM_ANCHOR_BBOX_DIMENSIONS = 3 - - class AnchorBoxSettings(input_preprocessors.SparseCarV1AnchorBoxSettings): - # PointPillars priors for pedestrian/cyclists. - DIMENSION_PRIORS = [(0.6, 0.8, 1.7), (0.6, 0.6, 1.2), (0.6, 1.76, 1.73)] - ROTATIONS = [0, np.pi / 2, 3. * np.pi / 4, np.pi / 4] - CENTER_X_OFFSETS = np.linspace(-0.31, 0.31, 3) - CENTER_Y_OFFSETS = np.linspace(-0.31, 0.31, 3) - CENTER_Z_OFFSETS = [-0.6] - - def _configure_generic_input(self, p): - super()._configure_generic_input(p) - # For selecting centers, drop points out of frustum and do approximate - # ground removal. - p.preprocessors.select_centers.features_preparation_layers = [ - input_preprocessors.KITTIDropPointsOutOfFrustum.Params(), - input_preprocessors.DropLaserPointsOutOfRange.Params().Set( - keep_z_range=(-1., np.inf)), - ] - - # Remove frustum dropping from original preprocessors. - p.preprocessors_order.remove('remove_out_of_frustum') - - # Keep all points in front of the car for featurizing, do not remove ground. - p.preprocessors.keep_xyz_range.keep_x_range = (0., 48.0) - p.preprocessors.keep_xyz_range.keep_y_range = (-20., 20.) - p.preprocessors.keep_xyz_range.keep_z_range = (-np.inf, np.inf) - p.preprocessors.pad_lasers.max_num_points = 72000 - p.preprocessors.select_centers.sampling_method = 'farthest_point' - p.preprocessors.select_centers.num_cell_centers = 512 - p.preprocessors.select_centers.features_preparation_layers = [ - input_preprocessors.KITTIDropPointsOutOfFrustum.Params(), - input_preprocessors.DropLaserPointsOutOfRange.Params().Set( - keep_z_range=(-1.4, np.inf)), - ] - - p.preprocessors.gather_features.max_distance = 2.55 - - def _configure_trainer_input(self, p): - super()._configure_trainer_input(p) - - allowed_label_ids = [ - kitti_input_generator.KITTILabelExtractor.KITTI_CLASS_NAMES.index( - class_name) for class_name in self.INCLUDED_CLASSES - ] - p.preprocessors.bbox_aug.Set( - num_db_objects=19700, - filter_min_difficulty=2, - filter_min_points=7, - max_augmented_bboxes=2, - max_num_points_per_bbox=1558, - label_filter=allowed_label_ids, - ) - p.batch_size = 2 - - def _configure_decoder_input(self, p): - """Update input_config `p` for jobs running decoding.""" - super()._configure_decoder_input(p) - p.batch_size = 4 - - def _configure_evaler_input(self, p): - """Update input_config `p` for jobs running evaluation.""" - super()._configure_evaler_input(p) - p.batch_size = 4 - - def Task(self): - p = super().Task() - p.train.learning_rate = 7e-4 - - builder = starnet.Builder() - builder.linear_params_init = py_utils.WeightInit.KaimingUniformFanInRelu() - gin_layer_sizes = [32, 256, 512, 256, 256, 128] - num_laser_features = 1 - gin_layers = [ - # Each layer should expect as input - 2 * dims of the last layer's - # output. We assume a middle layer that's the size of 2 * dim_out. - [dim_in * 2, dim_out * 2, dim_out] - for (dim_in, dim_out) in zip(gin_layer_sizes[:-1], gin_layer_sizes[1:]) - ] - p.cell_feature_dims = sum(gin_layer_sizes) - # Disable BN on first layer - p.cell_featurizer = builder.GINFeaturizerV2( - 'feat', - gin_layer_sizes[0], - gin_layers, - num_laser_features, - fc_use_bn=False) - p.anchor_projected_feature_dims = 512 - - class_name_to_idx = kitti_input_generator.KITTILabelExtractor.KITTI_CLASS_NAMES - num_classes = len(class_name_to_idx) - p.per_class_loss_weight = [0.] * num_classes - p.per_class_loss_weight[class_name_to_idx.index('Pedestrian')] = 3.5 - p.per_class_loss_weight[class_name_to_idx.index('Cyclist')] = 3.25 - - p.focal_loss_alpha = 0.9 - p.focal_loss_gamma = 1.25 - - p.use_oriented_per_class_nms = True - p.max_nms_boxes = 1024 - p.nms_iou_threshold = [0.0] * num_classes - p.nms_iou_threshold[class_name_to_idx.index('Cyclist')] = 0.49 - p.nms_iou_threshold[class_name_to_idx.index('Pedestrian')] = 0.32 - - p.nms_score_threshold = [1.0] * num_classes - p.nms_score_threshold[class_name_to_idx.index('Cyclist')] = 0.11 - p.nms_score_threshold[class_name_to_idx.index('Pedestrian')] = 0.23 - - p.output_decoder.filter_predictions_outside_frustum = True - p.output_decoder.truncation_threshold = 0.65 - # Equally weight pedestrian and cyclist moderate classes. - p.output_decoder.ap_metric.metric_weights = { - 'easy': np.array([0.0, 0.0, 0.0]), - 'moderate': np.array([0.0, 1.0, 1.0]), - 'hard': np.array([0.0, 0.0, 0.0]) - } - - return p diff --git a/lingvo/tasks/car/params/params.py b/lingvo/tasks/car/params/params.py deleted file mode 100644 index 661d802a..00000000 --- a/lingvo/tasks/car/params/params.py +++ /dev/null @@ -1,22 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Car models hyper-parameters.""" - -# Import ModelParams to ensure that they are added to the global registry. -# pylint: disable=unused-import -import lingvo.tasks.car.params.kitti -import lingvo.tasks.car.params.waymo -# pylint: enable=unused-import diff --git a/lingvo/tasks/car/params/waymo.py b/lingvo/tasks/car/params/waymo.py deleted file mode 100644 index d14143f2..00000000 --- a/lingvo/tasks/car/params/waymo.py +++ /dev/null @@ -1,473 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Train models on the waymo open dataset.""" - -import os - -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.core import cluster_factory -from lingvo.core import hyperparams -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.tasks.car import input_preprocessors -from lingvo.tasks.car import lr_util -from lingvo.tasks.car import starnet -from lingvo.tasks.car.waymo import waymo_decoder -from lingvo.tasks.car.waymo import waymo_metadata -from lingvo.tasks.car.waymo import waymo_open_input_generator -import numpy as np - -# Set $WAYMO_DIR to the base path of where all the WAYMO files can be found. -_WAYMO_BASE = os.environ.get('WAYMO_DIR', 'FILL-ME-IN') - - -def WaymoTrainSpec(params): - """Training set.""" - p = params.Copy() - p.file_datasource.file_pattern = 'train.tfr-*-of-01000' - p.num_samples = 158361 - return p - - -def WaymoMiniTrainSpec(params): - """Training shards used for decoding.""" - p = params.Copy() - p.file_datasource.file_pattern = 'train.tfr-000[0-2]?-of-01000' - p.num_samples = 4773 - return p - - -def WaymoValSpec(params): - """Validation set. - - Validation contains no overlapping run segments with training. - - Args: - params: A Waymo open dataset input params. - - Returns: - An updated Waymo open dataset params with the validation spec. - """ - p = params.Copy() - p.file_datasource.file_pattern = 'valid.tfr-*-of-01000' - p.num_samples = 40077 - return p - - -def WaymoMinivalSpec(params): - """Miniature validation set (10% of full validation set).""" - p = params.Copy() - p.file_datasource.file_pattern = 'valid.tfr-000??-of-01000' - p.num_samples = 4109 - return p - - -def WaymoTestSpec(params): - # For now, using validation set, since test set is not available. - return WaymoValSpec(params) - - -class WaymoSparseLaserTrain(waymo_open_input_generator.WaymoSparseLaser): - """Waymo train set with sparse laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return WaymoTrainSpec(p) - - -class WaymoSparseLaserValidation(waymo_open_input_generator.WaymoSparseLaser): - """Waymo validation set with sparse laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return WaymoValSpec(p) - - -class WaymoSparseLaserMinival(waymo_open_input_generator.WaymoSparseLaser): - """Waymo mini validation set with sparse laser data.""" - - @classmethod - def Params(cls): - p = super().Params() - return WaymoMinivalSpec(p) - - -class WaymoSparseLaserTest(waymo_open_input_generator.WaymoSparseLaser): - """Waymo test set with sparse laser data. Has no groundtruth labels.""" - - @classmethod - def Params(cls): - p = super().Params() - return WaymoTestSpec(p) - - -def _FilterKeepLabels(params, label_names): - """Keep only label names in 'label_names' from input.""" - metadata = waymo_metadata.WaymoMetadata() - filtered_labels = [ - metadata.ClassNames().index(label_name) for label_name in label_names - ] - params.extractors.labels.filter_labels = filtered_labels - - -@model_registry.RegisterSingleTaskModel -class StarNetBase(base_model_params.SingleTaskModelParams): - """StarNet model for running on Waymo. - - This the base model, please refer to specialized vehicles, pedestrians, etc. - models below to get an appropriate model to train. - """ - RUN_LOCALLY = False - NUM_ANCHOR_BBOX_OFFSETS = 25 - NUM_ANCHOR_BBOX_ROTATIONS = 4 - NUM_ANCHOR_BBOX_DIMENSIONS = 1 - - # Ground truth filtering - GT_MIN_NUM_POINTS = 5 - - # Architecture Params - NUM_GIN_LAYERS = 5 - GIN_HIDDEN_DIMS = 64 - NUM_POINTS_PER_CELL = 512 - - class AnchorBoxSettings(input_preprocessors.SparseCarV1AnchorBoxSettings): - ROTATIONS = [0, np.pi / 2, 3. * np.pi / 4, np.pi / 4] - - def _configure_input(self, p, split): - p.file_datasource.file_pattern_prefix = _WAYMO_BASE - - job_type = cluster_factory.Current().job - - max_num_points = int(64 * 2650 * 1.5) - p.preprocessors = hyperparams.Params() - p.preprocessors.Define('filter_nlz_points', - waymo_open_input_generator.FilterNLZPoints.Params(), - '') - # TODO(bencaine): Change this to filter based on difficulty instead - p.preprocessors.Define( - 'filter_groundtruth', - input_preprocessors.FilterGroundTruthByNumPoints.Params(), '') - p.preprocessors.Define('viz_copy', - input_preprocessors.CreateDecoderCopy.Params(), '') - p.preprocessors.Define('select_centers', - input_preprocessors.SparseCenterSelector.Params(), - '') - p.preprocessors.Define( - 'gather_features', - input_preprocessors.SparseCellGatherFeatures.Params(), '') - p.preprocessors.Define('tile_anchors', - input_preprocessors.TileAnchorBBoxes.Params(), '') - p.preprocessors.Define('assign_anchors', - input_preprocessors.AnchorAssignment.Params(), '') - p.preprocessors.Define( - 'pad_lasers', - input_preprocessors.PadLaserFeatures.Params().Set( - max_num_points=max_num_points), '') - - p.preprocessors.viz_copy.pad_lasers.max_num_points = max_num_points - p.preprocessors.filter_groundtruth.min_num_points = self.GT_MIN_NUM_POINTS - - p.preprocessors.select_centers.num_cell_centers = 1024 - p.preprocessors.gather_features.num_points_per_cell = self.NUM_POINTS_PER_CELL - p.preprocessors.gather_features.sample_neighbors_uniformly = True - p.preprocessors.gather_features.max_distance = 2.75 - - p.preprocessors.assign_anchors.foreground_assignment_threshold = 0.6 - p.preprocessors.assign_anchors.background_assignment_threshold = 0.45 - - p.preprocessors_order = [ - 'filter_nlz_points', - 'filter_groundtruth', - 'viz_copy', - 'select_centers', - 'gather_features', - 'tile_anchors', - 'assign_anchors', - 'pad_lasers', - ] - - # Apply car anchor box settings. - tile_anchors_p = p.preprocessors.tile_anchors - self.AnchorBoxSettings.Update(p.preprocessors.tile_anchors) - num_anchor_configs = self.AnchorBoxSettings.NumAnchors() - - assert len(tile_anchors_p.anchor_box_dimensions) == num_anchor_configs - assert len(tile_anchors_p.anchor_box_rotations) == num_anchor_configs - assert len(tile_anchors_p.anchor_box_offsets) == num_anchor_configs - - # If this is not the decoder job (e.g., this is trainer), turn off - # image decoding, do not count points, and do not make visualization copies. - if job_type != 'decoder': - p.preprocessors_order.remove('viz_copy') - # Do not need laser points during training for current V2 model. This - # reduces amount of data sent over during training. - p.preprocessors.pad_lasers.max_num_points = 0 - - p.file_buffer_size = 32 - p.file_parallelism = 8 - p.num_batcher_threads = 8 - if self.RUN_LOCALLY: - p.num_batcher_threads = 1 - p.file_buffer_size = 1 - p.file_parallelism = 1 - - if job_type.startswith('trainer'): - p.batch_size = 2 - else: - p.batch_size = 4 - p.file_buffer_size = 64 - p.file_parallelism = 16 - p.num_batcher_threads = 16 - return p - - def Train(self): - p = waymo_open_input_generator.WaymoSparseLaser.Params() - p = WaymoTrainSpec(p) - p = self._configure_input(p, 'Train') - return p - - def Minitrain(self): - p = self.Train() - p = WaymoMiniTrainSpec(p) - return p - - def Test(self): - p = waymo_open_input_generator.WaymoSparseLaser.Params() - p = WaymoTestSpec(p) - p = self._configure_input(p, 'Test') - return p - - def Dev(self): - p = waymo_open_input_generator.WaymoSparseLaser.Params() - p = WaymoValSpec(p) - p = self._configure_input(p, 'Dev') - return p - - def Minidev(self): - p = self.Dev() - p = WaymoMinivalSpec(p) - return p - - def Task(self): - metadata = waymo_metadata.WaymoMetadata() - num_classes = len(metadata.ClassNames()) - p = starnet.ModelV2.Params( - num_classes, - num_anchor_bboxes_offsets=self.NUM_ANCHOR_BBOX_OFFSETS, - num_anchor_bboxes_rotations=self.NUM_ANCHOR_BBOX_ROTATIONS, - num_anchor_bboxes_dimensions=self.NUM_ANCHOR_BBOX_DIMENSIONS, - num_laser_features=3) - - # Update the Point Cloud Featurizer architecture - starnet_builder = starnet.Builder() - starnet_builder.linear_params_init = ( - py_utils.WeightInit.KaimingUniformFanInRelu()) - - gin_layers = [ - [self.GIN_HIDDEN_DIMS*2, self.GIN_HIDDEN_DIMS*4, self.GIN_HIDDEN_DIMS] - ] * self.NUM_GIN_LAYERS # pyformat: disable - - p.cell_featurizer = starnet_builder.GINFeaturizerV2( - 'feat', - num_laser_features=3, - fc_dims=self.GIN_HIDDEN_DIMS, - mlp_dims=gin_layers, - fc_use_bn=False) - p.cell_feature_dims = self.GIN_HIDDEN_DIMS * (self.NUM_GIN_LAYERS + 1) - - p.output_decoder = waymo_decoder.WaymoOpenDatasetDecoder.Params() - p.max_nms_boxes = 512 - p.use_oriented_per_class_nms = True - - # Note: Sub-classes need to set nms_iou_threshold and nms_score_threshold - # appropriately. - p.nms_iou_threshold = [0.0] * num_classes - - # TODO(jngiam): 1.1 for untrained classes is needed to avoid an issue - # with boxutils error. - p.nms_score_threshold = [1.1] * num_classes - - p.name = 'starnet' - tp = p.train - tp.optimizer = optimizer.Adam.Params() - tp.clip_gradient_norm_to_value = 5 - - ep = p.eval - - # Train set uses a smaller decoding set, so we can - # safely eval over the entire input. - ep.samples_per_summary = 0 - - # To be tuned. - p.train.l2_regularizer_weight = 1e-8 - - cluster = cluster_factory.Current() - train_cluster_p = cluster.params.Copy() - train_cluster_p.job = 'trainer_client' - train_cluster_p.mode = 'sync' - - # When running a decoding only job, there are no trainer workers, so we set - # worker replicas to 1 as a dummy value. - if train_cluster_p.worker.replicas <= 0: - train_cluster_p.worker.replicas = 1 - - # Set learning rate and schedule. - with cluster_factory.Cluster(train_cluster_p): - train_input_p = self.Train() - - # Adapted from V1 tuning. - tp.ema_decay = 0.99 - # TODO(b/148537111): consider setting this to True. - tp.ema_decay_moving_vars = False - tp.learning_rate = 0.001 - lr_util.SetExponentialLR( - train_p=tp, - train_input_p=train_input_p, - exp_start_epoch=5, - total_epoch=75) - - p.dimension_loss_weight = .3 - p.location_loss_weight = 3. - p.loss_weight_classification = 1. - p.loss_weight_localization = 3. - p.rotation_loss_weight = 0.3 - - return p - - -@model_registry.RegisterSingleTaskModel -class StarNetVehicle(StarNetBase): - """StarNet model for cars, running on Waymo.""" - GT_MIN_NUM_POINTS = 5 - - class AnchorBoxSettings(input_preprocessors.SparseCarV1AnchorBoxSettings): - DIMENSION_PRIORS = [(4.725808, 2.079292, 1.768998)] - ROTATIONS = [0, np.pi / 2, 3. * np.pi / 4, np.pi / 4] - CENTER_X_OFFSETS = np.linspace(-1.294, 1.294, 5) - CENTER_Y_OFFSETS = np.linspace(-1.294, 1.294, 5) - CENTER_Z_OFFSETS = [0.819622] - - def _configure_input(self, p, split): - p = super()._configure_input(p, split) - - # Select points in approx z range, set using 10 and 90 percentile from - # train data. - p.preprocessors.select_centers.features_preparation_layers = [ - input_preprocessors.DropLaserPointsOutOfRange.Params().Set( - keep_z_range=(0.197431, 2.046459)), - ] - - _FilterKeepLabels(p, ['Vehicle']) - return p - - def Task(self): - p = super().Task() - p.train.l2_regularizer_weight = 3e-5 - - class_names = waymo_metadata.WaymoMetadata().ClassNames() - num_classes = len(class_names) - - p.per_class_loss_weight = [0.] * num_classes - p.per_class_loss_weight[class_names.index('Vehicle')] = 1.0 - - # See WaymoMetadata.EvalClassIndices for correspondences for metric_weights. - p.output_decoder.ap_metric.metric_weights = { - 'default': np.array([1.0, 0.0, 0.0]), - } - p.nms_iou_threshold[class_names.index('Vehicle')] = 0.03 - p.nms_score_threshold[class_names.index('Vehicle')] = 0.31 - return p - - -@model_registry.RegisterSingleTaskModel -class StarNetPed(StarNetBase): - """StarNet model for pedestrians, running on Waymo.""" - NUM_ANCHOR_BBOX_ROTATIONS = 2 - GT_MIN_NUM_POINTS = 5 - - class AnchorBoxSettings(input_preprocessors.SparseCarV1AnchorBoxSettings): - DIMENSION_PRIORS = [(0.901340, 0.857218, 1.712443)] - ROTATIONS = [0, np.pi / 2] - CENTER_X_OFFSETS = np.linspace(-0.6, 0.6, 5) - CENTER_Y_OFFSETS = np.linspace(-0.6, 0.6, 5) - CENTER_Z_OFFSETS = [0.819622] - - def _configure_input(self, p, split): - p = super()._configure_input(p, split) - - # Select points in approx z range, set using 10 and 90 percentile from - # train data. - p.preprocessors.select_centers.features_preparation_layers = [ - input_preprocessors.DropLaserPointsOutOfRange.Params().Set( - keep_z_range=(0.09522381, 1.720825)), - ] - - _FilterKeepLabels(p, ['Pedestrian']) - return p - - def Task(self): - p = super().Task() - p.train.l2_regularizer_weight = 3e-6 - - class_names = waymo_metadata.WaymoMetadata().ClassNames() - num_classes = len(class_names) - - p.per_class_loss_weight = [0.] * num_classes - p.per_class_loss_weight[class_names.index('Pedestrian')] = 1.0 - - # See WaymoMetadata.EvalClassIndices for correspondences for metric_weights. - p.output_decoder.ap_metric.metric_weights = { - 'default': np.array([0.0, 1.0, 0.0]), - } - p.nms_iou_threshold[class_names.index('Pedestrian')] = 0.46 - p.nms_score_threshold[class_names.index('Pedestrian')] = 0.01 - - return p - - -@model_registry.RegisterSingleTaskModel -class StarNetPedFused(StarNetPed): - """StarNet Pedestrian model with SparseSampler fused input preprocessor.""" - - def _configure_input(self, p, split): - p = super()._configure_input(p, split) - - job_type = cluster_factory.Current().job - - if job_type.startswith('trainer') and not self.RUN_LOCALLY: - p.file_buffer_size = 48 - p.file_parallelism = 48 - p.num_batcher_threads = 48 - - # Fuses select_centers and gather_features into one sampler. - p.preprocessors.Define( - 'sampler', - input_preprocessors.SparseSampler.Params().Set( - center_selector='farthest', - neighbor_sampler='uniform', - num_centers=p.preprocessors.select_centers.num_cell_centers, - keep_z_range=(0.09522381, 1.720825), - num_neighbors=p.preprocessors.gather_features.num_points_per_cell, - max_distance=2.75), '') - p.preprocessors.Delete('select_centers') - p.preprocessors.Delete('gather_features') - p.preprocessors_order.remove('select_centers') - p.preprocessors_order[p.preprocessors_order.index( - 'gather_features')] = 'sampler' - return p diff --git a/lingvo/tasks/car/pillars.py b/lingvo/tasks/car/pillars.py deleted file mode 100644 index e737f8e2..00000000 --- a/lingvo/tasks/car/pillars.py +++ /dev/null @@ -1,665 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -r"""PointPillars implementation. - -[1] PointPillars. https://arxiv.org/abs/1812.05784 -""" - -import enum -import functools -from lingvo import compat as tf -from lingvo.core import base_layer -from lingvo.core import layers -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.tasks.car import builder_lib -from lingvo.tasks.car import detection_3d_lib -from lingvo.tasks.car import geometry -from lingvo.tasks.car import point_detector -import numpy as np - - -def SparseToDense(grid_shape, locations, feats): - """Converts a sparse representation back to the dense grid. - - Args: - grid_shape: (nx, ny, nz). The shape of the grid. - locations: [b, p, 3]. Locations of the pillars. - feats: [b, p, fdims]. Extracted features for pillars. - - Returns: - grid_feats of shape [b, nx, ny, nz * fdims]. - """ - nx, ny, nz = grid_shape - b, p, _ = py_utils.GetShape(locations, 3) - feats = py_utils.HasShape(feats, [b, p, -1]) - _, _, fdims = py_utils.GetShape(feats, 3) - indices = tf.concat( - [tf.tile(tf.range(b)[:, tf.newaxis, tf.newaxis], [1, p, 1]), locations], - axis=2) - grid = tf.scatter_nd(indices, feats, [b, nx, ny, nz, fdims]) - return tf.reshape(grid, [b, nx, ny, nz * fdims]) - - -class PointsToGridFeaturizer(base_layer.BaseLayer): - """Layer for processing points to grid outputs.""" - - @classmethod - def Params(cls, num_laser_features, num_output_features=64): - p = super().Params() - p.Define('num_laser_features', num_laser_features, - 'The number of (non-xyz) laser features of the input.') - - builder = Builder() - total_num_laser_features = 9 + num_laser_features - p.Define( - 'featurizer', - builder.Featurizer('feat', - [total_num_laser_features, num_output_features]), - 'Point cloud feature extractor.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self.CreateChild('featurizer', p.featurizer) - - def FProp(self, theta, input_batch): - # pyformat: disable - """Compute features for the pillars and convert them back to a dense grid. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - input_batch: A `.NestedMap` object containing input tensors. Following - keys are required: - - - grid_num_points: Integer tensor with shape [batch size, nx, ny, nz], - where nx, ny, nz corresponds to the grid sizes (i.e., number of voxels - in each axis dimension). - - pillar_points: Float tensor with shape [batch size, num_pillars, - num_points_per_pillar, 3 + num_laser_features] - - pillar_centers: Float tensor with shape [batch size, num_pillars, - num_points_per_pillar, 3] - - pillar_locations: Float tensor with shape [batch size, num_pillars, 3] - - Returns: - The dense features with shape [b, nx, ny, nz * fdims]. - """ - # pyformat: enable - p = self.params - bs, nx, ny, nz = py_utils.GetShape(input_batch.grid_num_points, 4) - # Process points to concatenate a set of fixed features (e.g., - # add means, centers, normalize points to means). - num_features = 3 + p.num_laser_features - pillar_points = py_utils.HasShape(input_batch.pillar_points, - [bs, -1, -1, num_features]) - _, npillars, npoints, _ = py_utils.GetShape(pillar_points, 4) - pillar_xyz = pillar_points[..., :3] - - # Compute number of points per pillar and prepare for broadcasting. - pillar_num_points = tf.gather_nd( - input_batch.grid_num_points, input_batch.pillar_locations, batch_dims=1) - pillar_num_points = pillar_num_points[..., tf.newaxis, tf.newaxis] - - # Compute mean by computing sum and dividing by number of points. Clip the - # denominator by 1.0 to gracefully handle empty pillars. - pillar_sum = tf.reduce_sum(pillar_xyz, axis=2, keepdims=True) - pillar_means = pillar_sum / tf.maximum( - tf.cast(pillar_num_points, tf.float32), 1.0) - - pillar_feats = pillar_points[..., 3:] - pillar_centers = py_utils.HasShape(input_batch.pillar_centers, - [bs, -1, 1, 3]) - pillar_concat = tf.concat( - axis=3, - values=[ - pillar_xyz - pillar_means, pillar_feats, - tf.tile(pillar_means, [1, 1, npoints, 1]), - tf.tile(pillar_centers, [1, 1, npoints, 1]) - ]) - # Featurize pillars. - pillar_features = self.featurizer.FProp(theta.featurizer, pillar_concat) - - # Convert back to the dense grid. - pillar_locations = py_utils.HasShape(input_batch.pillar_locations, - [bs, npillars, 3]) - dense_features = SparseToDense( - grid_shape=(nx, ny, nz), - locations=pillar_locations, - feats=pillar_features) - return dense_features - - -# pyformat: disable -class Builder(builder_lib.ModelBuilderBase): - """Builder for the Pillars model.""" - - def __init__(self): - super().__init__() - self.conv_init_method = builder_lib.KaimingUniformFanInRelu - self.linear_params_init = py_utils.WeightInit.KaimingUniformFanInRelu() - self.bn_params_init = py_utils.WeightInit.UniformPositive() - - def Featurizer(self, name, dims): - return self._Seq( - name, - self._MLP('mlp', dims), - self._Max('max')) - - def _Deconv(self, name, filter_shape, stride): - return layers.DeconvLayer.Params().Set( - name=name, - filter_shape=filter_shape, - filter_stride=(stride, stride)) - - def _Block(self, name, stride, repeats, idims, odims): - """[1]. Sec 2.2.""" - return self._Seq( - name, - self._Conv('c3x3', (3, 3, idims, odims), stride), - self._Rep( - 'rep', - repeats, - self._Conv('c3x3', (3, 3, odims, odims))), - self._Fetch('final')) - - def _TopDown(self, name, strides=(2, 2, 2), channel_multiplier=1): - """[1]. Sec 2.2.""" - if len(strides) != 3: - raise ValueError('`strides` expected to be list/tuple of len 3.') - - return self._Seq( - name, - self._Block('b0', strides[0], 3, channel_multiplier * 64, - channel_multiplier * 64), - self._Block('b1', strides[1], 5, channel_multiplier * 64, - channel_multiplier * 128), - self._Block('b2', strides[2], 5, channel_multiplier * 128, - channel_multiplier * 256)) - - def _Upsample(self, name, stride, idims, odims): - """[1]. Sec 2.2.""" - # Match the kernel size to the stride in order to ensure that the output - # activation map has no holes and to minimize any checkerboard artifacts. - # TODO(shlens): Consider replacing this in the future with a bilinear - # interpolation followed by a 3x3 convolution. - kernel = stride - return self._Seq( - name, - self._Deconv('deconv', (kernel, kernel, odims, idims), stride), - self._BN('bn', odims), - self._Relu('relu')) - - def Contract(self, down_strides=(2, 2, 2), channel_multiplier=1): - """Contracting part of [1] Sec 2.2.""" - return self._Branch( - 'branch', - self._TopDown('topdown', strides=down_strides, - channel_multiplier=channel_multiplier), - ['b1.final', 'b0.final']) - - def Expand(self, odims, channel_multiplier=1): - """Expanding part of [1] Sec 2.2.""" - # Note that the resulting output will be 3*odims - return self._Concat( - 'concat', - self._Seq( - 'b2', - self._ArgIdx('idx', [0]), - self._Upsample('ups', 4, channel_multiplier * 256, odims)), - self._Seq( - 'b1', - self._ArgIdx('idx', [1]), - self._Upsample('ups', 2, channel_multiplier * 128, odims)), - self._Seq( - 'b0', - self._ArgIdx('idx', [2]), - self._Upsample('ups', 1, channel_multiplier * 64, odims))) - - def Backbone(self, odims, down_strides=(2, 2, 2), channel_multiplier=1): - """[1]. Sec 2.2.""" - # We assume (H, W) are multiple of 8. So that we can concat - # multiple-scale feature maps together after upsample. - return self._Seq( - 'backbone', - self.Contract(down_strides, channel_multiplier=channel_multiplier), - self.Expand(odims, channel_multiplier=channel_multiplier)) - - def Detector(self, name, idims, odims, conv_init_method=None, - bias_params_init=None): - # Implemented according to VoxelNet - # https://arxiv.org/pdf/1711.06396.pdf - # May add more Conv2D layers before predictor for better performance. - return self._Seq( - name, - self._ConvPlain('predict', (3, 3, idims, odims), - conv_init_method=conv_init_method), - self._Bias('predict_bias', odims, bias_params_init)) - -# pyformat: enable - - -class LossNormType(enum.Enum): - NO_NORM = 0 - NORM_BY_NUM_POSITIVES = 1 - - -class ModelV1(point_detector.PointDetectorBase): - """PointPillars model. - - Base class implements common Decoder functions, though they can be - overridden if desired. - """ - - NUM_OUTPUT_CHANNELS = 128 - - @classmethod - def Params(cls, - grid_size_z=1, - num_anchors=2, - num_classes=1, - num_laser_features=1): - p = super().Params(num_classes=num_classes) - p.Define('grid_size_z', grid_size_z, 'The grid size along the z-axis.') - p.Define('num_anchors', num_anchors, 'The number of anchor boxes.') - p.Define('num_laser_features', num_laser_features, - 'The number of (non-xyz) laser features of the input.') - p.Define('input_featurizer', - PointsToGridFeaturizer.Params(num_laser_features), - 'Point cloud feature extractor.') - - builder = Builder() - p.Define('backbone', builder.Backbone(cls.NUM_OUTPUT_CHANNELS), - 'Dense features pyramid.') - # Backbone() concatenates 3 different scales of features. - idims = 3 * cls.NUM_OUTPUT_CHANNELS - # 7: predicted (dx, dy, dz, dw, dl, dh, dt). - class_odims = grid_size_z * num_anchors * num_classes - reg_odims = grid_size_z * num_anchors * 7 - rot_odims = grid_size_z * num_anchors * 2 - # Although theoretically a single conv layer can generate both the - # regression and classification logits, we try to implement the paper - # faithfully, which uses two different layers. - p.Define('class_detector', builder.Detector('class', idims, class_odims), - 'Dense features to class logits.') - p.Define('regression_detector', builder.Detector('reg', idims, reg_odims), - 'Dense features to regression logits.') - p.Define('direction_classifier', builder.Detector('dir', idims, rot_odims), - 'Dense features to rotation direction classifier.') - # We disable the direction classifier by default since it has - # weird discontinous optimization objectives around the threshold - # and it doesn't improve mAP. - p.Define( - 'direction_classifier_weight', 0.0, - 'If > 0, adds a direction classifier to the model and adds ' - 'to the total loss with this weight.') - p.Define( - 'direction_aware_rot_loss', False, 'If True, changes the heading loss ' - 'from sin(theta_delta) to WrapAngleRad(theta_delta), which makes the ' - 'model produce headings between [-pi to pi].') - - p.Define( - 'squash_rotation_predictions', False, - 'Apply tanh squashing to rotation predictions to ensure outputs ' - 'are between (-pi, pi).') - p.Define('focal_loss_alpha', 0.25, 'The alpha parameter in focal loss ' - '(see paper eq. 4).') - p.Define('focal_loss_gamma', 2.0, 'The gamma parameter in focal loss ' - '(see paper eq. 4).') - p.Define( - 'localization_loss_weight', 2.0, - 'Localization loss weight factor between localization and ' - 'class loss contributions.') - p.Define( - 'classification_loss_weight', 1.0, - 'Classification loss weight factor between localization and ' - 'class loss contributions.') - p.Define( - 'location_loss_weight', 1.0, - 'Weight multiplier for contribution of location loss ' - 'to full localization/regression loss') - p.Define( - 'dimension_loss_weight', 1.0, - 'Weight multiplier for contribution of dimension loss ' - 'to full localization/regression loss') - p.Define( - 'rotation_loss_weight', 1.0, - 'Weight multiplier for contribution of rotation loss ' - 'to full localization/regression loss') - - p.Define('loss_norm_type', LossNormType.NORM_BY_NUM_POSITIVES, - 'Normalization function for class and regularization weights.') - - p.Define('oracle_location', False, - 'If true, the model predicts the ground truth for location.') - p.Define('oracle_dimension', False, - 'If true, the model predicts the ground truth for dimension.') - p.Define('oracle_rotation', False, - 'If true, the model predicts the ground truth for rotation.') - - tp = p.train - tp.learning_rate = 0.001 - tp.optimizer = optimizer.Momentum.Params().Set(alpha=0.9) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._utils = detection_3d_lib.Utils3D() - - self.CreateChild('input_featurizer', p.input_featurizer) - self.CreateChild('backbone', p.backbone) - self.CreateChild('class_detector', p.class_detector) - self.CreateChild('regression_detector', p.regression_detector) - if p.direction_classifier_weight > 0.0: - self.CreateChild('direction_classifier', p.direction_classifier) - - def ComputePredictions(self, theta, input_batch): - """Computes predictions for `input_batch`. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - input_batch: A `.NestedMap` object containing input tensors to this tower. - - Returns: - A `.NestedMap` contains - logits - [b, nx, ny, nz, na, 7 + num_classes] - """ - p = self.params - input_batch.Transform(lambda x: (x.shape, x.shape.num_elements())).VLog( - 0, 'input_batch shapes: ') - - # Make pillars representation from input_batch. - dense_features = self.input_featurizer.FProp(theta.input_featurizer, - input_batch) - - # Backbone - tf.logging.vlog(1, 'dense_features.shape = %s', dense_features.shape) - act = self.backbone.FProp(theta.backbone, dense_features) - tf.logging.vlog(1, 'act.shape = %s', act.shape) - - # Convert the output of the backbone into class logits and regression - # residuals using two different layers. - class_detection = self.class_detector.FProp(theta.class_detector, act) - reg_detection = self.regression_detector.FProp(theta.regression_detector, - act) - bs, nx, ny, _ = py_utils.GetShape(class_detection, 4) - predicted_classification_logits = tf.reshape( - class_detection, - [bs, nx, ny, p.grid_size_z, p.num_anchors, p.num_classes]) - predicted_residuals = tf.reshape( - reg_detection, [bs, nx, ny, p.grid_size_z, p.num_anchors, 7]) - - if p.squash_rotation_predictions: - predicted_rotations = predicted_residuals[..., 6:] - predicted_rotations = np.pi * tf.tanh(predicted_rotations) - predicted_residuals = tf.concat( - [predicted_residuals[..., :6], predicted_rotations], axis=-1) - - if p.oracle_location or p.oracle_dimension or p.oracle_rotation: - gt_residuals = py_utils.HasShape( - input_batch.anchor_localization_residuals, - [bs, nx, ny, p.grid_size_z, p.num_anchors, 7]) - - # Replace the predicted components with the ground truth if needed. - if p.oracle_location: - location = gt_residuals[..., 0:3] - else: - location = predicted_residuals[..., 0:3] - - if p.oracle_dimension: - dimension = gt_residuals[..., 3:6] - else: - dimension = predicted_residuals[..., 3:6] - - if p.oracle_rotation: - rotation = gt_residuals[..., 6:] - else: - rotation = predicted_residuals[..., 6:] - predicted_residuals = tf.concat([location, dimension, rotation], axis=-1) - - ret = py_utils.NestedMap({ - 'residuals': predicted_residuals, - 'classification_logits': predicted_classification_logits, - }) - - if p.direction_classifier_weight > 0.0: - predicted_dir = self.direction_classifier.FProp( - theta.direction_classifier, act) - predicted_dir = tf.reshape(predicted_dir, - [bs, nx, ny, p.grid_size_z, p.num_anchors, 2]) - ret.predicted_dir = predicted_dir - - return ret - - def _ComputeClassificationLoss(self, predictions, input_batch, class_weights): - """Compute classification loss for the given predictions. - - Args: - predictions: The output of `ComputePredictions`, contains: logits - [b, - nx, ny, nz, na, 7 + num_classes]. na is the number of anchor - boxes per cell. [..., :7] are (dx, dy, dz, dw, dl, dh, dt). - input_batch: The input batch from which we accesses the groundtruth. - class_weights: Per-class weights to use in loss computation. - - Returns: - Classification loss. - - """ - p = self.params - predicted_class_logits = py_utils.HasShape( - predictions.classification_logits, - [-1, -1, -1, -1, p.num_anchors, p.num_classes]) - bs, nx, ny, nz, na, _ = py_utils.GetShape(predicted_class_logits, 6) - assigned_gt_labels = py_utils.HasShape(input_batch.assigned_gt_labels, - [bs, nx, ny, nz, na]) - class_loss = py_utils.SigmoidCrossEntropyFocalLoss( - logits=predicted_class_logits, - labels=tf.one_hot(assigned_gt_labels, p.num_classes), - alpha=p.focal_loss_alpha, - gamma=p.focal_loss_gamma) - class_loss *= class_weights[..., tf.newaxis] - class_loss_sum = tf.reduce_sum(class_loss) - return class_loss_sum - - def ComputeLoss(self, theta, predictions, input_batch): - """Computes loss and other metrics for the given predictions. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - predictions: The output of `ComputePredictions`, contains: logits - [b, - nx, ny, nz, na, 7 + num_classes]. na is the number of anchor - boxes per cell. [..., :7] are (dx, dy, dz, dw, dl, dh, dt). - input_batch: The input batch from which we accesses the groundtruth. - - Returns: - Two dicts defined as BaseTask.ComputeLoss. - """ - p = self.params - predicted_residuals = py_utils.HasShape(predictions.residuals, - [-1, -1, -1, -1, p.num_anchors, 7]) - predicted_class_logits = py_utils.HasShape( - predictions.classification_logits, - [-1, -1, -1, -1, p.num_anchors, p.num_classes]) - bs, nx, ny, nz, na, _ = py_utils.GetShape(predicted_class_logits, 6) - - # Compute class and regression weights. - class_weights = input_batch.assigned_cls_mask - class_weights = py_utils.HasShape(class_weights, [bs, nx, ny, nz, na]) - reg_weights = input_batch.assigned_reg_mask - reg_weights = py_utils.HasShape(reg_weights, [bs, nx, ny, nz, na]) - reg_weights = tf.expand_dims(reg_weights, -1) - - if p.loss_norm_type == LossNormType.NORM_BY_NUM_POSITIVES: - # Compute number of positive anchors per example. - foreground_mask = py_utils.HasShape(input_batch.assigned_reg_mask, - [bs, nx, ny, nz, na]) - # Sum to get the number of foreground anchors for each example. - loss_normalization = tf.reduce_sum(foreground_mask, axis=[1, 2, 3, 4]) - loss_normalization = tf.maximum(loss_normalization, - tf.ones_like(loss_normalization)) - # Reshape for broadcasting. - loss_normalization = tf.reshape(loss_normalization, [bs, 1, 1, 1, 1, 1]) - - class_weights /= loss_normalization - reg_weights /= loss_normalization - - # Classification loss. - class_loss_sum = self._ComputeClassificationLoss(predictions, input_batch, - class_weights) - - # Regression loss. - anchor_localization_residuals = py_utils.HasShape( - input_batch.anchor_localization_residuals, [bs, nx, ny, nz, na, 7]) - - # Location and dimensions loss. - reg_loc_and_dims_loss = self._utils.ScaledHuberLoss( - predictions=py_utils.HasShape(predicted_residuals[..., :6], - [bs, nx, ny, nz, na, 6]), - labels=anchor_localization_residuals[..., :6], - delta=1 / (3.**2)) - - # Rotation loss is computed on a transform on rot_delta. For a direction - # aware loss, we simply wrap the angles to -pi to pi; for a loss that is - # symmetric to direction (i.e., rotating by pi), we use a sin transform. - rot_delta_transform = tf.sin - if p.direction_aware_rot_loss: - rot_delta_transform = functools.partial( - geometry.WrapAngleRad, min_val=-np.pi, max_val=np.pi) - - rot_delta = ( - predicted_residuals[..., 6:] - anchor_localization_residuals[..., 6:]) - reg_rot_loss = self._utils.ScaledHuberLoss( - predictions=rot_delta_transform(rot_delta), - labels=tf.zeros_like(rot_delta), - delta=1 / (3.**2)) - - # Direction loss - if p.direction_classifier_weight > 0.0: - # The target rotations are in the assigned_gt_bbox tensor, - # which already has assigned a gt bounding box to every anchor. - rot_target = input_batch.assigned_gt_bbox[..., 6] - # If rotation is > 0, the class is 1, else it is 0. - rot_dir = tf.cast(rot_target > 0., tf.int32) - - # Compute one-hot labels as a target. - rot_dir_onehot = tf.one_hot(rot_dir, 2) - - # Manually handle loss reduction. - dir_loss = tf.losses.softmax_cross_entropy( - onehot_labels=rot_dir_onehot, - logits=predictions.predicted_dir, - weights=tf.squeeze(reg_weights, axis=-1), - reduction=tf.losses.Reduction.NONE) - # Reduce across all dimensions (we'll divide by the batch size below). - dir_loss_sum = tf.reduce_sum(dir_loss) - else: - dir_loss_sum = 0.0 - - # Compute loss contribution from location and dimension separately. - reg_loc_loss = reg_loc_and_dims_loss[..., :3] * reg_weights - reg_loc_loss_sum = tf.reduce_sum(reg_loc_loss) - - reg_dim_loss = reg_loc_and_dims_loss[..., 3:6] * reg_weights - reg_dim_loss_sum = tf.reduce_sum(reg_dim_loss) - - # Compute rotation loss contribution. - reg_rot_loss *= reg_weights - reg_rot_loss_sum = tf.reduce_sum(reg_rot_loss) - - # Num. predictions. - # TODO(zhifengc): Consider other normalization factors. E.g., # of bboxes. - preds = tf.cast(bs, class_loss_sum.dtype) - - # Normalize all of the components by batch size. - reg_loc_loss = reg_loc_loss_sum / preds - reg_dim_loss = reg_dim_loss_sum / preds - reg_rot_loss = reg_rot_loss_sum / preds - class_loss = class_loss_sum / preds - dir_loss = dir_loss_sum / preds - - # Compute total localization regression loss. - reg_loss = ( - p.location_loss_weight * reg_loc_loss + - p.dimension_loss_weight * reg_dim_loss + - p.rotation_loss_weight * reg_rot_loss) - - # Apply weights to normalized class losses. - loss = ( - class_loss * p.classification_loss_weight + - reg_loss * p.localization_loss_weight + - dir_loss * p.direction_classifier_weight) - - metrics_dict = { - 'loss': (loss, preds), - 'loss/class': (class_loss, preds), - 'loss/reg': (reg_loss, preds), - 'loss/reg/rot': (reg_rot_loss, preds), - 'loss/reg/loc': (reg_loc_loss, preds), - 'loss/reg/dim': (reg_dim_loss, preds), - 'loss/dir': (dir_loss, preds), - } - - # Calculate dimension errors - min_angle_rad = -np.pi if p.direction_aware_rot_loss else 0 - gt_bboxes = self._utils_3d.ResidualsToBBoxes( - input_batch.anchor_bboxes, - anchor_localization_residuals, - min_angle_rad=min_angle_rad, - max_angle_rad=np.pi) - predicted_bboxes = self._utils_3d.ResidualsToBBoxes( - input_batch.anchor_bboxes, - predicted_residuals, - min_angle_rad=min_angle_rad, - max_angle_rad=np.pi) - dimension_errors_dict = self._BBoxDimensionErrors(gt_bboxes, - predicted_bboxes, - reg_weights) - metrics_dict.update(dimension_errors_dict) - - per_example_dict = { - 'residuals': predicted_residuals, - 'classification_logits': predicted_class_logits, - } - - return metrics_dict, per_example_dict - - def _BBoxesAndLogits(self, input_batch, predictions): - """Decode an input batch, computing predicted bboxes from residuals.""" - p = self.params - - # Decode residuals. - min_angle_rad = -np.pi if p.direction_aware_rot_loss else 0 - predicted_bboxes = self._utils.ResidualsToBBoxes( - input_batch.anchor_bboxes, - predictions.residuals, - min_angle_rad=min_angle_rad, - max_angle_rad=np.pi) - - # predicted_bboxes is a [batch, nx, ny, nz, na, 7] Tensor. - batch_size, nx, ny, nz, na, _ = py_utils.GetShape(predicted_bboxes, 6) - num_boxes = nx * ny * nz * na - - # Reshape to [batch_size, num_boxes, 7] - predicted_bboxes = tf.reshape(predicted_bboxes, [batch_size, num_boxes, 7]) - - classification_logits = tf.reshape(predictions.classification_logits, - [batch_size, num_boxes, -1]) - - return py_utils.NestedMap({ - 'predicted_bboxes': predicted_bboxes, - 'classification_logits': classification_logits - }) diff --git a/lingvo/tasks/car/point_detector.py b/lingvo/tasks/car/point_detector.py deleted file mode 100644 index dbfbf6e3..00000000 --- a/lingvo/tasks/car/point_detector.py +++ /dev/null @@ -1,343 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Base models for point-cloud based detection.""" - -from lingvo import compat as tf -from lingvo.core import base_model -from lingvo.core import py_utils -from lingvo.tasks.car import detection_3d_lib -from lingvo.tasks.car import detection_decoder - -from lingvo.tasks.car import kitti_decoder -import numpy as np - - -class PointDetectorBase(base_model.BaseTask): - """Base class for implementing point-based detectors. - - Subclasses should implement _BBoxesAndLogits() to compute the bounding box and - scores given an input batch, and specify an appropriate decoder - implementation. - """ - - @classmethod - def Params(cls, num_classes): - p = super().Params() - # We choose a high number of boxes per example by default to bound overall - # runtime but not so low that we end up missing real boxes from complicated - # scenes. - p.Define('num_classes', num_classes, - 'The number of classes, including the background class.') - p.Define( - 'max_nms_boxes', 1024, - 'Maximum number of boxes per example to emit from non-max-suppression.') - p.Define( - 'nms_iou_threshold', 0.3, - 'NMS IoU threshold for suppressing overlapping boxes. ' - 'Can either be a float or a list of len num_classes.') - p.Define( - 'nms_score_threshold', 0.01, 'NMS threshold for scores. ' - 'Can either be a float or a list of len num_classes. ' - 'It is recommended that this be 1 for all non-active classes ' - 'like background.') - p.Define( - 'visualization_classification_threshold', 0.25, - 'Classification score threshold for determining if a prediction ' - 'is positive for the purposes of visualizations.') - p.Define('output_decoder', kitti_decoder.KITTIDecoder.Params(), - 'Implementation of decoder.') - p.Define( - 'use_oriented_per_class_nms', False, - 'Whether to use oriented per class nms or single class non-oriented.') - p.Define( - 'inference_batch_size', None, - 'If specified, hardcodes the inference batch size to this value. ' - 'Useful mostly for computing the FLOPS of a model so that the shape is ' - 'fully defined.') - p.Define( - 'decode_include_residuals', False, - 'If True, includes the residuals and ground truth anchors in the ' - 'decoder output dictionary. This can be helpful for downstream ' - 'analysis.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._utils_3d = detection_3d_lib.Utils3D() - self.CreateChild('output_decoder', p.output_decoder) - - def CreateDecoderMetrics(self): - """Create decoder metrics.""" - return self.output_decoder.CreateDecoderMetrics() - - def _BBoxesAndLogits(self, input_batch, predictions): - """Fetch and return the bounding boxes and logits from an input. - - Args: - input_batch: The input batch from which to produce boxes and logits. - predictions: The output dictionary of ComputePredictions. - - Returns: - A .NestedMap containing - - - predicted_bboxes: A [batch_size, num_boxes, 7] floating point Tensor. - - classification_logits: A [batch_size, num_boxes, num_classes] floating - point Tensor. - """ - raise NotImplementedError('_BoxesAndLogits method not implemented.') - - def _Placeholders(self): - """Return a NestedMap of placeholders to fill in for inference. - - Runs the configured input pipeline to generate the expected shapes and types - of the inputs. - - Returns: - A NestedMap of placeholders matching the input structure of - the inference model. - """ - p = self.params - with tf.Graph().as_default(): - inputs = self.params.input.Instantiate() - - # Turn those inputs into placeholders. - placeholders = [] - for input_shape, dtype in zip(inputs.Shape().Flatten(), - inputs.DType().Flatten()): - batched_input_shape = [p.inference_batch_size] + input_shape.as_list() - placeholders.append(tf.placeholder(dtype, batched_input_shape)) - - result = inputs.DType().Pack(placeholders) - return result - - def _BBoxDimensionErrors(self, - gt_bboxes, - pred_bboxes, - regression_weights, - epsilon=1e-6): - """Calculates the errors per bounding box dimension for assigned anchors. - - Args: - gt_bboxes: float Tensor of shape [..., 7] with the ground truth bounding - box for each anchor. - pred_bboxes: float Tensor of shape [..., 7] with the predicted bounding - box for each anchor. - regression_weights: float Tensor with 0/1 weights indicating whether the - anchor had a positive assignment with same base shape as `gt_bboxes` and - `pred_bboxes` excluding the last dimension. - epsilon: A float epsilon for the denominiator of our MaskedAverage. - - Returns: - A metrics dict with mean bounding box errors for all positive assigned - anchor locations. - """ - if py_utils.GetShape(gt_bboxes)[-1] != 7: - raise ValueError('`gt_bboxes` last dimension should be 7.') - if py_utils.GetShape(pred_bboxes)[-1] != 7: - raise ValueError('`pred_bboxes` last dimension should be 7.') - batch_size = py_utils.GetShape(pred_bboxes)[0] - # Get the leading dims for later (the -1 is to exclude the last dim). - leading_dims = len(py_utils.GetShape(pred_bboxes)) - 1 - - sum_regression_weights = tf.reduce_sum(regression_weights) + epsilon - - def _MaskedAverage(value, axis=None): - return (tf.reduce_sum(value * regression_weights, axis=axis) / - sum_regression_weights) - - center_error = tf.linalg.norm( - gt_bboxes[..., :3] - pred_bboxes[..., :3], axis=-1, keepdims=True) - mean_center_error = _MaskedAverage(center_error) - - # Dimension error as shape [3] so we can get separate height, width, length - mean_dimension_error = _MaskedAverage( - gt_bboxes[..., 3:6] - pred_bboxes[..., 3:6], - axis=list(range(leading_dims))) - - # Angular error in degrees - mean_angular_error_rad = _MaskedAverage(gt_bboxes[..., 6:] - - pred_bboxes[..., 6:]) - mean_angular_error_deg = mean_angular_error_rad * (180 / np.pi) - - return py_utils.NestedMap({ - 'error/center_distance': (mean_center_error, batch_size), - 'error/length': (mean_dimension_error[0], batch_size), - 'error/width': (mean_dimension_error[1], batch_size), - 'error/height': (mean_dimension_error[2], batch_size), - 'error/rotation_deg': (mean_angular_error_deg, batch_size), - }) - - def Inference(self): - """Builds the inference graph. - - Default subgraph should return: - - predicted_bboxes: A [batch_size, num_boxes, 7] float Tensor. - - classification_scores: A [batch_size, num_boxes, num_classes] float - Tensor. - - Returns: - A dictionary whose values are a tuple of fetches and feeds. - """ - p = self.params - subgraphs = {} - with tf.name_scope('inference'): - input_placeholders = self._Placeholders() - predictions = self.ComputePredictions(self.theta, input_placeholders) - bboxes_and_logits = self._BBoxesAndLogits(input_placeholders, predictions) - predicted_bboxes = bboxes_and_logits.predicted_bboxes - classification_logits = bboxes_and_logits.classification_logits - classification_scores = tf.sigmoid(classification_logits) - - _, per_cls_bboxes, per_cls_bbox_scores, per_cls_valid_mask = ( - detection_decoder.DecodeWithNMS( - predicted_bboxes, - classification_scores, - nms_iou_threshold=p.nms_iou_threshold, - score_threshold=p.nms_score_threshold, - max_boxes_per_class=p.max_nms_boxes, - use_oriented_per_class_nms=p.use_oriented_per_class_nms)) - per_cls_bbox_scores *= per_cls_valid_mask - - # TODO(vrv): Fix the inference graph for KITTI, since we need - # to apply frustum clipping. This requires customizing the - # inference placeholders for each model. - fetches = { - 'per_class_predicted_bboxes': per_cls_bboxes, - 'per_class_predicted_bbox_scores': per_cls_bbox_scores, - 'per_class_valid_mask': per_cls_valid_mask - } - subgraphs['default'] = fetches, dict(input_placeholders.FlattenItems()) - return subgraphs - - # TODO(bencaine): Reduce code duplication between Inference/Decode. - def Decode(self, input_batch): - """Decode an input batch, computing predicted bboxes from residuals.""" - p = self.params - - predictions = self.ComputePredictions(self.theta, input_batch) - bboxes_and_logits = self._BBoxesAndLogits(input_batch, predictions) - predicted_bboxes = bboxes_and_logits.predicted_bboxes - batch_size, num_bboxes, _ = py_utils.GetShape(predicted_bboxes, 3) - classification_logits = bboxes_and_logits.classification_logits - classification_logits = py_utils.HasShape( - classification_logits, [batch_size, num_bboxes, p.num_classes]) - - classification_scores = tf.sigmoid(classification_logits) - - _, per_example_dict = self.ComputeLoss(self.theta, predictions, input_batch) - if 'score_scaler' in per_example_dict: - classification_scores *= per_example_dict['score_scaler'] - - with tf.device('/cpu:0'): - # Decode the predicted bboxes, performing NMS. - per_cls_idxs, per_cls_bboxes, per_cls_bbox_scores, per_cls_valid_mask = ( - detection_decoder.DecodeWithNMS( - predicted_bboxes, - classification_scores, - nms_iou_threshold=p.nms_iou_threshold, - score_threshold=p.nms_score_threshold, - max_boxes_per_class=p.max_nms_boxes, - use_oriented_per_class_nms=p.use_oriented_per_class_nms)) - - # per_cls_valid_mask is [batch, num_classes, num_boxes] Tensor that - # indicates which boxes were selected by NMS. Each example will have a - # different number of chosen bboxes, so the mask is present to allow us - # to keep the boxes as a batched dense Tensor. - # - # We mask the scores by the per_cls_valid_mask so that none of these boxes - # will be interpreted as valid. - per_cls_bbox_scores *= per_cls_valid_mask - visualization_weights = py_utils.HasShape( - per_cls_bbox_scores, [batch_size, p.num_classes, p.max_nms_boxes]) - - # For top down visualization, filter boxes whose scores are not above the - # visualization threshold. - visualization_weights = tf.where( - tf.greater_equal(visualization_weights, - p.visualization_classification_threshold), - visualization_weights, tf.zeros_like(visualization_weights)) - - model_outputs = py_utils.NestedMap() - model_outputs.per_class_predicted_bboxes = per_cls_bboxes - model_outputs.per_class_predicted_bbox_scores = per_cls_bbox_scores - model_outputs.per_class_valid_mask = per_cls_valid_mask - - decoder_outputs = py_utils.NestedMap({ - 'per_class_predicted_bboxes': per_cls_bboxes, - 'per_class_predicted_bbox_scores': per_cls_bbox_scores, - 'per_class_valid_mask': per_cls_valid_mask, - 'visualization_weights': visualization_weights, - }) - - if p.decode_include_residuals: - # Including the residuals in the decoder output makes it possible to save - # the outputs for further analysis. Note that we ensure that the outputs - # match the per-class NMS output format of [batch, num_classes, ...]. - def _ReshapeGather(tensor): - """Reshapes tensor and then gathers using the nms indices.""" - tensor = tf.gather( - tf.reshape(tensor, [batch_size, num_bboxes, -1]), - per_cls_idxs, - batch_dims=1) - if not p.use_oriented_per_class_nms: - # Tile so that the data fits the expected per class shape of - # [batch_size, num_classes, ...]. When *not* using oriented NMS, the - # num_classes dimension will be missing since the indices will not - # have it. - tensor = tf.tile(tensor[:, tf.newaxis, :, :], - [1, p.num_classes, 1, 1]) - return tensor - - decoder_outputs.update({ - 'per_class_gt_residuals': - _ReshapeGather(input_batch.anchor_localization_residuals), - 'per_class_gt_labels': - _ReshapeGather(input_batch.assigned_gt_labels), - 'per_class_residuals': - _ReshapeGather(predictions.residuals), - 'per_class_logits': - _ReshapeGather(predictions.classification_logits), - 'per_class_anchor_boxes': - _ReshapeGather(input_batch.anchor_bboxes), - }) - - decoder_outputs.update( - self.output_decoder.ProcessOutputs(input_batch, model_outputs)) - - # Produce global step as an output (which is the step - # of the checkpoint being decoded.) - decoder_outputs.global_step = py_utils.GetGlobalStep() - - return decoder_outputs - - def PostProcessDecodeOut(self, dec_out_dict, dec_metrics_dict): - return self.output_decoder.PostProcessDecodeOut(dec_out_dict, - dec_metrics_dict) - - def DecodeFinalize(self, decode_finalize_args): - decode_out_path = decode_finalize_args.decode_out_path - decode_out = decode_finalize_args.decode_out - - if not decode_out: - return - - # Write out a tf record file for all values in decode_out. - with tf.io.TFRecordWriter(decode_out_path) as f: - for _, v in decode_out: - f.write(v) diff --git a/lingvo/tasks/car/pointnet.py b/lingvo/tasks/car/pointnet.py deleted file mode 100644 index fe505b74..00000000 --- a/lingvo/tasks/car/pointnet.py +++ /dev/null @@ -1,236 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""PointNet architecture. - -PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation - https://arxiv.org/abs/1612.00593 -""" - -from lingvo import compat as tf -from lingvo.core import builder_layers -from lingvo.core import py_utils -from lingvo.tasks.car import builder_lib -import numpy as np - - -class PointNet(builder_lib.ModelBuilderBase): - """Builder for PointNet layers.""" - - def _ConcatWithBranch(self, name, tile, *subs): - """Concat x with subs(x).""" - - def Merge(xs): - act_self = xs[0][0] - act_branch = xs[1][0] - if tile: - num_pts = tf.shape(act_self)[1] - act_branch = tf.tile(tf.expand_dims(act_branch, 1), [1, num_pts, 1]) - return (tf.concat([act_self, act_branch], axis=-1),) - - return builder_layers.ParallelLayer.Params().Set( - name=name, - sub=[self._Seq('id'), self._Seq('branch', *subs)], - merge=Merge) - - def _ConcatWithOnehot(self, name): - """Make an onehot from shape category id and concat with point features. - - See original Pointnet paper for justification. - - Args: - name: Name of the layer. - - Returns: - Params for a layer that updates the NestedMap features value with - the input features + a one hot encoding of the category id. - """ - - def _ConcatOnehotFn(input_data): - """Concat the input features with a onehot version of the label ids.""" - features = input_data.features - label = input_data.label - num_pts = tf.shape(features)[1] - label_one_hot = tf.one_hot(tf.cast(label, tf.int32), depth=16) - label_one_hot = tf.tile(tf.expand_dims(label_one_hot, 1), [1, num_pts, 1]) - input_data.features = tf.concat([features, label_one_hot], axis=-1) - return input_data - - return self._ApplyFn(name, fn=_ConcatOnehotFn) - - def _TNet(self, name, idims): - return self._Matmul( - name, - self._Seq('iden'), - self._Seq( - 'tran', - self._MLP('mlp0', [idims, 64, 128, 1024]), - self._Max('max'), - self._MLP('mlp1', [1024, 512, 256]), - # This layer is initialized as an identity transformation. - self._Linear('linear', 256, idims * idims) - .Set(params_init=py_utils.WeightInit.Constant(scale=0)), - self._Bias('bias', idims * idims).Set( - params_init=py_utils.WeightInit.Constant( - scale=np.eye(idims).reshape([-1]).tolist())), - self._Reshape('reshape', [-1, idims, idims]), - self._Fetch('transform_matrix') - )) - - def Classifier(self, - name='pointnet', - keep_prob=0.7, - input_dims=3, - feature_dims=256): - """PointNet architecture for classification.""" - # pyformat: disable - p = self._Seq( - name, - self._SeqOnFeatures( - 'point_features', - self._TNet('inp_trans', input_dims), - self._Fetch('inp_transformed'), - self._MLP('feat', [input_dims, 64, 64]), - self._TNet('feat_trans', 64), - self._MLP('mlp1', [64, 64, 128, 1024]), - self._Fetch('pre_maxpool')), - self._PaddedMax('max'), - self._Seq( - 'cls_features', - self._Fetch('feature'), - self._FC('fc0', 1024, 512), - self._Dropout('dropout', keep_prob), - self._FC('fc1', 512, feature_dims))) - # pyformat: enable - - p.Define('output_dim', feature_dims, 'Final output dimension.') - return p - - def Segmentation(self, name='pointnet_segmentation'): - """PointNet archetecture for segmentation.""" - # dropout is NOT used in the vanilla setting from the paper. - # pyformat: disable - main_tower = self._Seq( - 'main_tower', - self._GetValue('get_features', builder_lib.FEATURES_KEY), - self._TNet('inp_trans', 3), - self._MLP('feat', [3, 64, 64]), - self._TNet('feat_trans', 64), - self._ConcatWithBranch( - 'concat1', - True, - self._MLP('mlp1', [64, 64, 128, 1024]), - self._Max('max'), - self._Fetch('feature'))) - segmentation_tower = self._Seq( - name, - main_tower, - self._Fetch('point_feature'), - self._MLP('final_mlp', [1088, 512, 256, 128])) - # pyformat: enable - p = segmentation_tower - p.Define('output_dim', 128, 'Final output dimension.') - return p - - def SegmentationShapeNet(self, name='pointnet_shapenet', keep_prob=0.8): - """The modified network for part segmentation in ShapeNet.""" - main_tower = self._SeqOnFeatures( - 'main_tower_seq', self._TNet('inp_trans', 3), self._FC('fc1', 3, 64), - self._ConcatWithBranch( - 'concat1', False, self._FC('fc2', 64, 128), - self._ConcatWithBranch( - 'concat2', False, self._FC('fc3', 128, 128), - self._ConcatWithBranch( - 'concat3', False, self._TNet('fea_trans', 128), - self._ConcatWithBranch( - 'concat4', False, self._FC('fc4', 128, 512), - self._ConcatWithBranch('concat5', True, - self._FC('fc5', 512, 2048), - self._Max('max'))))))) - p = self._Seq(name, main_tower, self._ConcatWithOnehot('concat_onehot'), - self._GetValue('get_features', builder_lib.FEATURES_KEY), - self._FC('final_fc1', 3024, 256), - self._Dropout('dropout1', keep_prob), - self._FC('final_fc2', 256, 256), - self._Dropout('dropout2', keep_prob), - self._FC('final_fc3', 256, 128)) - # pyformat: enabled - p.Define('output_dim', 128, 'Final output dimension.') - return p - - -class PointNetPP(builder_lib.ModelBuilderBase): - """Builder for PointNet++ Model.""" - - def _SetAbstractionWithMLPMax(self, - name, - mlp_dims, - num_samples, - group_size, - ball_radius, - sample_neighbors_uniformly=True): - feature_extraction_sub = self._Seq( - 'mlpmax', - self._ConcatPointsToFeatures('concat_feat_points'), - self._FeaturesMLP('sa_MLP', mlp_dims), - self._PaddedMax('sa_max_pool')) # pyformat: disable - return self._SetAbstraction( - name=name, - feature_extraction_sub=feature_extraction_sub, - num_samples=num_samples, - group_size=group_size, - ball_radius=ball_radius, - sample_neighbors_uniformly=sample_neighbors_uniformly) - - def _ModelNet40Featurizer(self, input_dims): - return self._Seq( - 'sa_featurizer', - self._SetAbstractionWithMLPMax( - name='sa0', - num_samples=512, - ball_radius=0.2, - group_size=32, - sample_neighbors_uniformly=True, - mlp_dims=[input_dims + 3, 64, 64, 128]), - self._SetAbstractionWithMLPMax( - name='sa1', - num_samples=128, - ball_radius=0.4, - group_size=32, - sample_neighbors_uniformly=True, - mlp_dims=[128 + 3, 128, 128, 256]), - self._ConcatPointsToFeatures('concat_feat_points'), - self._FeaturesMLP('sa_MLP', [256 + 3, 256, 512, 1024]), - self._PaddedMax('sa_max_pool')) - - def Classifier(self, - name='pointnetpp', - input_dims=3, - feature_dims=256, - keep_prob=0.8, - num_points=1024): - """PointNet++ architecture for modelnet40 classification.""" - - featurizer_sub = self._ModelNet40Featurizer(input_dims) - p = self._Seq( - name, - featurizer_sub, - self._FC('fc0', 1024, 512), - self._Dropout('dropout0', keep_prob), - self._FC('fc1', 512, feature_dims), - self._Dropout('dropout1', keep_prob)) # pyformat: disable - p.Define('output_dim', feature_dims, 'Final output dimension.') - - return p diff --git a/lingvo/tasks/car/pointnet_test.py b/lingvo/tasks/car/pointnet_test.py deleted file mode 100644 index 82ce01e1..00000000 --- a/lingvo/tasks/car/pointnet_test.py +++ /dev/null @@ -1,75 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for pointnet.""" - -from absl.testing import parameterized -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car import pointnet - - -class PointNetTest(test_utils.TestCase, parameterized.TestCase): - - def _testOutShape(self, p, input_shape, expected_shape): - batch_size, num_points, _ = input_shape - g = tf.Graph() - with g.as_default(): - net = p.Instantiate() - input_data = py_utils.NestedMap( - points=tf.random.uniform((batch_size, num_points, 3)), - features=tf.random.uniform(input_shape), - padding=tf.zeros((batch_size, num_points), dtype=tf.float32), - label=tf.random.uniform((batch_size,), - minval=0, - maxval=16, - dtype=tf.int32)) - result = net.FPropDefaultTheta(input_data) - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - np_result = self.evaluate(result) - self.assertEqual(np_result.shape, expected_shape) - - @parameterized.parameters((128, 3), (128, 9), (256, 3)) - def testPointNetClassifier(self, feature_dims, input_dims): - p = pointnet.PointNet().Classifier( - input_dims=input_dims, feature_dims=feature_dims) - # Network should produce a global feature of feature_dims. - self.assertEqual(p.output_dim, feature_dims) - self._testOutShape(p, (8, 128, input_dims), (8, feature_dims)) - - def testPointNetSegmentation(self): - p = pointnet.PointNet().Segmentation() - # Network takes batch_size=8 input and produce 128-dim pointwise feature. - self.assertEqual(p.output_dim, 128) - self._testOutShape(p, (8, 100, 3), (8, 100, 128)) - - def testPointNetSegmentationShapeNet(self): - p = pointnet.PointNet().SegmentationShapeNet() - self.assertEqual(p.output_dim, 128) - self._testOutShape(p, (8, 2000, 3), (8, 2000, 128)) - - @parameterized.parameters((128, 3), (128, 9), (256, 3)) - def testPointNetPPClassifier(self, feature_dims, input_dims): - p = pointnet.PointNetPP().Classifier( - input_dims=input_dims, feature_dims=feature_dims) - # Network should produce a global feature of feature_dims. - self.assertEqual(p.output_dim, feature_dims) - self._testOutShape(p, (8, 1024, input_dims), (8, feature_dims)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/starnet.py b/lingvo/tasks/car/starnet.py deleted file mode 100644 index 062090ad..00000000 --- a/lingvo/tasks/car/starnet.py +++ /dev/null @@ -1,909 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""StarNet: A sparse, targeted detection network. - -Point clouds and anchors are sampled from the raw points, at centers determined -by a sampler. Each center can have multiple anchors bboxes. Each center's -point cloud is featurized and then used to regress and classify the associated -anchors bboxes. - -The featurizer could be range from a simple MLPMax network, to more complex -point-based models such as PointNet++, PointCNN, PCNN, etc. - -In V1, anchor_bboxes shared the same featurization around each cell -(featurized_cell), but they had different regression/classification networks -for each offset/dimension/rotation that produced predictions. - -In V2, we bring the featurization closer to each anchor_bbox, by featurizing -at each anchor_bbox (offset) location instead. Note that anchor_bboxes at the -same offset, but with different rotation/dimension priors will have the same -featurization. -""" - -import enum -import functools -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.tasks.car import builder_lib -from lingvo.tasks.car import detection_3d_lib -from lingvo.tasks.car import geometry -from lingvo.tasks.car import point_detector -import numpy as np - - -class Builder(builder_lib.ModelBuilderBase): - """Builder for StarNet Model.""" - - def MLPMaxFeaturizer(self, dims): - """MLP followed by Max Featurizer.""" - return self._Seq('feat', self._GetValue('get_value', 'features'), - self._MLP('mlp', dims), self._Max('max')) - - def PaddedMLPMaxFeaturizer(self, idims, dims, use_bn=True): - """MLP followed by Max on padded points.""" - - # TODO(bencaine): Add simple builder test - # Avoid batch norm in the first layer since our FC layers do BN-FC-Relu - # So the first op on the raw data would be BN, which is weird for absolute - # xyz values. - return self._Seq('feat', - self._FeaturesFC('input_fc', idims, dims[0], use_bn=False), - self._FeaturesMLP('mlp', dims, use_bn=use_bn), - self._PaddedMax('max')) - - def FC(self, name, idims, odims, use_bn=True, activation_fn=tf.nn.relu): - """Fully-connected layer.""" - return self._FC(name, idims, odims, use_bn, activation_fn) - - def Linear(self, name, idims, odims, params_init=None): - """Linear layer for predicting residuals and classification logits.""" - return self._Linear(name, idims, odims, params_init) - - def Bias(self, name, dims, params_init=None): - """Bias layer with optional initialization.""" - return self._Bias(name, dims, params_init) - - def LinearWithBias(self, - name, - idims, - odims, - linear_params_init=None, - bias_params_init=None): - """Linear with bias layer with optional initialization.""" - return self._Seq(name, - self.Linear('linear', idims, odims, linear_params_init), - self.Bias('bias', odims, bias_params_init)) - - def Atten(self, - name, - depth, - dims, - hdims, - heads, - odims, - keep_prob=1.0, - linear_params_init=None, - bias_params_init=None): - """Stacked self-attention followed with a projection.""" - return self._Seq( - name, - self._SelfAttenStack('attens', depth, dims, hdims, heads, keep_prob), - self.LinearWithBias('proj', dims, odims, linear_params_init, - bias_params_init)) - - def GINFeaturizer(self, name, fc_dims, mlp_dims, num_laser_features=1): - """GIN-based Featurizer.""" - total_input_features = 3 + num_laser_features - return self._Seq( - name, - # Dropping the cell center xyz prevents the model from over-fitting - # to the absolute coordinates. - self._SeqToKey( - 'drop_cell_center_xyz', 'features', - self._GetValue('get_features', 'features'), - self._ApplyFn('drop_cell_center_xyz', fn=lambda t: t[..., 3:])), - self._FeaturesFC('fc0', total_input_features, fc_dims), - self._GIN( - 'gin', - mlp_dims, - aggregate_sub=self._PaddedMax('p_max'), - readout_sub=self._PaddedMean('p_mean'), - combine_method='concat')) # pyformat: disable - - def GINFeaturizerV2(self, - name, - fc_dims, - mlp_dims, - num_laser_features=1, - fc_use_bn=True): - """GIN-based Featurizer for Model V2.""" - total_input_features = 3 + num_laser_features - return self._Seq( - name, - self._FeaturesFC( - 'fc0', total_input_features, fc_dims, use_bn=fc_use_bn), - self._GIN( - 'gin', - mlp_dims, - aggregate_sub=self._PaddedMax('p_max'), - readout_sub=self._PaddedMean('p_mean'), - combine_method='concat')) # pyformat: disable - - def ZerosCellFeaturizer(self, name, dims): - """Produces features with 0 values, used for disabling cell featurizer.""" - - def _ZerosFeature(nested_data): - # Points has shape [batch_size, num_anchors, points_per_anchor, 3]. - # The featurizer should produce features for each anchor. - shape = py_utils.GetShape(nested_data.points) - return tf.zeros(shape[:-2] + [dims]) - - return self._ApplyFn(name, fn=_ZerosFeature) - - -class LossNormType(enum.Enum): - NO_NORM = 0 - NORM_BY_NUM_POS_PER_CENTER = 1 - - -class ModelBase(point_detector.PointDetectorBase): - """StarNet Detection Model. - - This model expects that input_batch contains: - cell_center_xyz: [N, C, 3] - cell_points_xyz: [N, C, P, 3] - cell_feature: [N, C, P, 1] - anchor_bboxes: [N, C, B, 7] - anchor_localization_residuals: [N, C, B, 7] - assigned_gt_labels: [N, C, B] - assigned_cls_mask: [N, C, B] - - where: - N - batch size - C - num centers - P - num points per center - B - num anchor bboxes per center - - The centers for the anchor_bboxes should match those of cell_center_xyz. - Specifically, num_anchor_bboxes_per_center should match that of the - corresponding input generator. - - Base class implements common Decoder functions, though they can be - overridden if desired. - - Sub-classes are expected to implement ComputePredictions. - """ - - @classmethod - def Params(cls, - num_classes, - num_anchor_bboxes_per_center, - num_laser_features=1): - p = super().Params(num_classes=num_classes) - p.Define( - 'num_anchor_bboxes_per_center', num_anchor_bboxes_per_center, - 'The number of anchor bboxes per center. This should match that ' - 'of the corresponding input generator.') - - p.Define('focal_loss_alpha', 0.25, - 'Alpha parameter for focal loss for classification.') - p.Define('focal_loss_gamma', 2.0, - 'Gamma parameter for focal loss for classification.') - - p.Define('huber_loss_delta', 1. / (3.**2), - 'delta threshold for scaled huber loss.') - p.Define('loss_weight_localization', 2.0, - 'Weighting factor for localization loss.') - p.Define('loss_weight_classification', 1.0, - 'Weighting factor for classification loss.') - - p.Define('loss_norm_type', None, - 'Normalization type for class and regularization weights.') - - p.Define( - 'squash_rotation_predictions', False, - 'Apply tanh squashing to rotation predictions to ensure outputs ' - 'are between (-pi, pi).') - p.Define( - 'direction_aware_rot_loss', False, 'If True, changes the heading loss ' - 'from sin(theta_delta) to WrapAngleRad(theta_delta), which makes the ' - 'model produce headings between [-pi to pi].') - - p.Define( - 'location_loss_weight', 1.0, - 'Weight multiplier for contribution of location loss ' - 'to full localization/regression loss') - p.Define( - 'dimension_loss_weight', 1.0, - 'Weight multiplier for contribution of dimension loss ' - 'to full localization/regression loss') - p.Define( - 'rotation_loss_weight', 1.0, - 'Weight multiplier for contribution of rotation loss ' - 'to full localization/regression loss') - p.Define( - 'corner_loss_weight', 0.0, - 'Weight multiplier for contribution of corner loss ' - 'to full localization/regression loss') - p.Define( - 'per_class_loss_weight', [0.] + [1.] * (num_classes - 1), - 'A list with a float value per class with a multiple to multiply ' - 'the classification losses of that classes anchors by. Note that ' - 'the background class is always class 0, and should be assigned a ' - 'weight of 0.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self._utils = detection_3d_lib.Utils3D() - - if len(p.per_class_loss_weight) != p.num_classes: - raise ValueError('`Need `per_class_loss_weight` to be of len equal ' - 'to the number of classes.') - if p.per_class_loss_weight[0] != 0.0: - raise ValueError('Background class should be assigned 0 weight. ' - 'per_class_loss_weight={}'.format( - str(p.per_class_loss_weight))) - - def ComputeLoss(self, theta, predictions, input_batch): - """Compute loss for the sparse detector model v1. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - predictions: A `.NestedMap` object containing residuals and - classification_logits. - input_batch: A `.NestedMap` expected to contain cell_center_xyz, - cell_points_xyz, cell_feature, anchor_bboxes, - anchor_localization_residuals, assigned_gt_labels, and - assigned_cls_mask. See class doc string for details. - - Returns: - Two dicts: - - - A dict containing str keys and (metric, weight) pairs as values, where - one of the keys is expected to be 'loss'. - - A dict containing arbitrary tensors describing something about each - training example, where the first dimension of each tensor is the batch - index. - """ - p = self.params - - batch_size, num_centers = py_utils.GetShape(input_batch.cell_center_xyz, 2) - - # Assert shapes of inputs. - anchor_bboxes = py_utils.HasShape( - input_batch.anchor_bboxes, - [batch_size, num_centers, p.num_anchor_bboxes_per_center, 7]) - anchor_localization_residuals = py_utils.HasShape( - input_batch.anchor_localization_residuals, - [batch_size, num_centers, p.num_anchor_bboxes_per_center, 7]) - predicted_residuals = py_utils.HasShape( - predictions.residuals, - [batch_size, num_centers, p.num_anchor_bboxes_per_center, 7]) - - assigned_gt_labels = py_utils.HasShape( - input_batch.assigned_gt_labels, - [batch_size, num_centers, p.num_anchor_bboxes_per_center]) - predicted_classification_logits = py_utils.HasShape( - predictions.classification_logits, [ - batch_size, num_centers, p.num_anchor_bboxes_per_center, - p.num_classes - ]) - - # assigned_cls_mask is for weighting the classification loss. - # Ignored targets will have their mask = 0; this happens when their IOU is - # not high enough to be a foreground object and not low enough to be - # background. - class_weights = py_utils.HasShape( - input_batch.assigned_cls_mask, - [batch_size, num_centers, p.num_anchor_bboxes_per_center]) - class_weights = tf.reshape( - class_weights, - [batch_size, num_centers, p.num_anchor_bboxes_per_center, 1]) - - # Broadcast per class loss weights. For each anchor, there are num_classes - # prediction heads, we weight the outputs of these heads by the per class - # loss weights. - per_class_loss_weight = tf.constant([[[p.per_class_loss_weight]]], - dtype=tf.float32) - per_class_loss_weight = py_utils.HasShape(per_class_loss_weight, - [1, 1, 1, p.num_classes]) - class_weights *= per_class_loss_weight - class_weights = py_utils.HasShape(class_weights, [ - batch_size, num_centers, p.num_anchor_bboxes_per_center, p.num_classes - ]) - - # We use assigned_reg_mask for masking the regression loss. - # Only foreground objects will have assigned_reg_mask = 1. - reg_weights = py_utils.HasShape( - input_batch.assigned_reg_mask, - [batch_size, num_centers, p.num_anchor_bboxes_per_center]) - reg_weights = tf.reshape( - reg_weights, - [batch_size, num_centers, p.num_anchor_bboxes_per_center, 1]) - - if p.loss_norm_type == LossNormType.NORM_BY_NUM_POS_PER_CENTER: - # Compute number of positive anchors per example. - foreground_mask = py_utils.HasShape( - input_batch.assigned_reg_mask, - [batch_size, num_centers, p.num_anchor_bboxes_per_center]) - - # Sum to get the number of foreground anchors for each example. - loss_normalization = tf.reduce_sum(foreground_mask, axis=2) - loss_normalization = tf.maximum(loss_normalization, - tf.ones_like(loss_normalization)) - - # Reshape for broadcasting. - loss_normalization = tf.reshape(loss_normalization, - [batch_size, num_centers, 1, 1]) - - # Normalize so that the loss is independent of # centers. - loss_normalization *= num_centers - class_weights /= loss_normalization - reg_weights /= loss_normalization - - classification_loss = py_utils.SigmoidCrossEntropyFocalLoss( - logits=predicted_classification_logits, - labels=tf.one_hot(assigned_gt_labels, p.num_classes), - alpha=p.focal_loss_alpha, - gamma=p.focal_loss_gamma) - - # Apply mask. - classification_loss *= class_weights - - # TODO(jngiam): Consider normalizing by num_foreground_anchors for each - # example instead. This would match the 1/N_positive normalization in - # point pillars. - - # Reduce sum over centers, boxes and classes. - classification_loss = tf.reduce_sum(classification_loss, axis=[1, 2, 3]) - - # Reduce mean over batch. - classification_loss = tf.reduce_mean(classification_loss) - - # Localization regression loss with Huber loss (SmoothL1). - regression_loc_and_dims_loss = self._utils_3d.ScaledHuberLoss( - labels=anchor_localization_residuals[..., :6], - predictions=predicted_residuals[..., :6], - delta=p.huber_loss_delta) - - # Rotation loss is computed on a transform on rotation_delta. For a - # direction aware loss, we simply wrap the angles to -pi to pi; for a loss - # that is symmetric to direction (i.e., rotating by pi), we use a sin - # transform. - rotation_delta_transform = tf.sin - if p.direction_aware_rot_loss: - rotation_delta_transform = functools.partial( - geometry.WrapAngleRad, min_val=-np.pi, max_val=np.pi) - rotation_delta = ( - predicted_residuals[..., 6:] - anchor_localization_residuals[..., 6:]) - regression_rotation_loss = self._utils_3d.ScaledHuberLoss( - labels=tf.zeros_like(rotation_delta), - predictions=rotation_delta_transform(rotation_delta), - delta=p.huber_loss_delta) - - reg_loc_loss = regression_loc_and_dims_loss[..., :3] - reg_dim_loss = regression_loc_and_dims_loss[..., 3:6] - - gt_bboxes = self._utils_3d.ResidualsToBBoxes( - anchor_bboxes, - anchor_localization_residuals, - min_angle_rad=-np.pi, - max_angle_rad=np.pi) - predicted_bboxes = self._utils_3d.ResidualsToBBoxes( - anchor_bboxes, - predicted_residuals, - min_angle_rad=-np.pi, - max_angle_rad=np.pi) - - # Apply mask to individual losses. - # - # And then reduce sum over centers, boxes, residuals, and batch - # and divide by the batch_size. - regression_rotation_loss *= reg_weights - reg_rot_loss = tf.reduce_sum(regression_rotation_loss) / batch_size - - reg_loc_loss *= reg_weights - reg_loc_loss = tf.reduce_sum(reg_loc_loss) / batch_size - - reg_dim_loss *= reg_weights - reg_dim_loss = tf.reduce_sum(reg_dim_loss) / batch_size - - # Do not create corner loss graph if weight is 0.0 - # TODO(bcyang): Remove condition after fixing corner loss NaN issue - if p.corner_loss_weight != 0.0: - reg_corner_loss = self._utils_3d.CornerLoss( - gt_bboxes=gt_bboxes, predicted_bboxes=predicted_bboxes) - reg_corner_loss = tf.expand_dims(reg_corner_loss, axis=-1) - - reg_corner_loss *= reg_weights - reg_corner_loss = tf.reduce_sum(reg_corner_loss) / batch_size - else: - reg_corner_loss = 0.0 - - # Sum components of regression loss. - regression_loss = ( - p.location_loss_weight * reg_loc_loss + - p.dimension_loss_weight * reg_dim_loss + - p.rotation_loss_weight * reg_rot_loss + - p.corner_loss_weight * reg_corner_loss) - - # Compute total loss. - total_loss = ( - p.loss_weight_localization * regression_loss + - p.loss_weight_classification * classification_loss) - - metrics_dict = py_utils.NestedMap({ - 'loss': (total_loss, batch_size), - 'loss/regression': (regression_loss, batch_size), - 'loss/regression/loc': (reg_loc_loss, batch_size), - 'loss/regression/dim': (reg_dim_loss, batch_size), - 'loss/regression/rot': (reg_rot_loss, batch_size), - 'loss/regression/corner': (reg_corner_loss, batch_size), - 'loss/classification': (classification_loss, batch_size), - }) - - # Calculate dimension errors - dimension_errors_dict = self._BBoxDimensionErrors(gt_bboxes, - predicted_bboxes, - reg_weights) - metrics_dict.update(dimension_errors_dict) - - per_example_dict = py_utils.NestedMap({ - 'residuals': predicted_residuals, - 'classification_logits': predicted_classification_logits, - 'predicted_bboxes': predicted_bboxes, - 'gt_bboxes': gt_bboxes, - 'reg_weights': reg_weights, - }) - - return metrics_dict, per_example_dict - - def ComputePredictions(self, theta, input_batch): - """Computes predictions for `input_batch`. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - input_batch: A `.NestedMap` expected to contain cell_center_xyz, - cell_points_xyz, cell_feature, anchor_bboxes, - anchor_localization_residuals, assigned_gt_labels, and - assigned_cls_mask. See class doc string for details. - - Returns: - A `.NestedMap` object containing residuals and classification_logits. - """ - raise NotImplementedError('Abstract method: %s' % type(self)) - - def _BBoxesAndLogits(self, input_batch, predictions): - """Decode an input batch, computing predicted bboxes from residuals.""" - batch_size, num_centers, num_predictions_per_center = py_utils.GetShape( - predictions.residuals, 3) - - # Decode residuals. - predicted_bboxes = self._utils_3d.ResidualsToBBoxes( - input_batch.anchor_bboxes, - predictions.residuals, - min_angle_rad=-np.pi, - max_angle_rad=np.pi) - - # Reshape to [batch_size, num_centers * num_predictions_per_center, ...] - num_predicted_boxes = num_centers * num_predictions_per_center - predicted_bboxes = tf.reshape(predicted_bboxes, - [batch_size, num_predicted_boxes, -1]) - - classification_logits = tf.reshape(predictions.classification_logits, - [batch_size, num_predicted_boxes, -1]) - - return py_utils.NestedMap({ - 'predicted_bboxes': predicted_bboxes, - 'classification_logits': classification_logits, - }) - - -class ModelV1(ModelBase): - """StarNet Model V1. - - In this model, each center is first featurized into a single feature vector, - which is then used to predict the residuals for all the bboxes at that center. - Concretely, each featurized center needs to make B * (7 + num_classes) - predictions. - - Effectively, anchor_bboxes shared the same featurization around each cell - (featurized_cell), but have different regression/classification networks - for each offset/dimension/rotation that produced predictions. - """ - - @classmethod - def Params(cls, - num_classes, - num_anchor_bboxes_per_center, - num_laser_features=1): - - p = super().Params( - num_classes=num_classes, - num_anchor_bboxes_per_center=num_anchor_bboxes_per_center, - num_laser_features=num_laser_features) - - builder = Builder() - final_feature_dims = 1024 - # The first 7 here corresponds to the concatenation of the following - # features: - # x, y, z of the center. - # x, y, z of each point relative to the center. - # features of the point. - num_features = 6 + num_laser_features - p.Define( - 'cell_featurizer', - builder.MLPMaxFeaturizer([num_features, 32, 256, final_feature_dims]), - 'Point cloud feature extractor.') - - p.Define( - 'localization_regressor', - builder.Linear('localization', final_feature_dims, - num_anchor_bboxes_per_center * 7), - 'Localization residual regressor.') - p.Define( - 'classifier', - builder.Linear('classification', final_feature_dims, - num_anchor_bboxes_per_center * num_classes), - 'Classification layer (producing logits).') - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - self.CreateChild('cell_featurizer', p.cell_featurizer) - self.CreateChild('localization_regressor', p.localization_regressor) - self.CreateChild('classifier', p.classifier) - - def ComputePredictions(self, theta, input_batch): - """Computes predictions for `input_batch`. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - input_batch: A `.NestedMap` expected to contain cell_center_xyz, - cell_points_xyz, cell_feature, anchor_bboxes, - anchor_localization_residuals, assigned_gt_labels, and - assigned_cls_mask. See class doc string for details. - - Returns: - A `.NestedMap` object containing residuals and classification_logits. - """ - p = self.params - input_batch.Transform(lambda x: (x.shape, x.shape.num_elements())).VLog( - 1, 'input_batch shapes: ') - - cell_feature = py_utils.HasRank(input_batch.cell_feature, 4) - batch_size, num_centers, num_points_per_cell = py_utils.GetShape( - cell_feature, 3) - - cell_points_xyz = py_utils.HasShape( - input_batch.cell_points_xyz, - [batch_size, num_centers, num_points_per_cell, 3]) - cell_center_xyz = py_utils.HasShape(input_batch.cell_center_xyz, - [batch_size, num_centers, 3]) - - cell_points_padding = py_utils.HasShape( - input_batch.cell_points_padding, - [batch_size, num_centers, num_points_per_cell]) - - # TODO(jngiam): Make concat_feature computation a layer or configureable. - cell_center_xyz = tf.reshape(cell_center_xyz, - [batch_size, num_centers, 1, 3]) - centered_cell_points_xyz = cell_points_xyz - cell_center_xyz - concat_feature = tf.concat([ - tf.tile(cell_center_xyz, [1, 1, num_points_per_cell, 1]), - centered_cell_points_xyz, cell_feature - ], axis=-1) # pyformat: disable - - # Featurize point clouds at each center. - point_input = py_utils.NestedMap({ - 'points': centered_cell_points_xyz, - 'features': concat_feature, - 'padding': cell_points_padding, - }) - featurized_cell = self.cell_featurizer.FProp(theta.cell_featurizer, - point_input) - featurized_cell = py_utils.HasShape(featurized_cell, - [batch_size, num_centers, -1]) - - # Predict localization residuals. - predicted_residuals = self.localization_regressor.FProp( - theta.localization_regressor, featurized_cell) - predicted_residuals = tf.reshape( - predicted_residuals, - [batch_size, num_centers, p.num_anchor_bboxes_per_center, 7]) - - if p.squash_rotation_predictions: - predicted_rotations = predicted_residuals[..., 6:] - predicted_rotations = np.pi * tf.tanh(predicted_rotations) - predicted_residuals = tf.concat( - [predicted_residuals[..., :6], predicted_rotations], axis=-1) - - # Predict object classification at each bbox. - predicted_classification_logits = self.classifier.FProp( - theta.classifier, featurized_cell) - predicted_classification_logits = tf.reshape( - predicted_classification_logits, [ - batch_size, num_centers, p.num_anchor_bboxes_per_center, - p.num_classes - ]) - - return py_utils.NestedMap({ - 'residuals': predicted_residuals, - 'classification_logits': predicted_classification_logits, - }) - - -class ModelV2(ModelBase): - """StarNet Model V2. - - This model is similar to V1 except that featurizations are computed at the - location of each anchor_bbox instead of each cell center. - - In V2, we don't share the featurization among anchor_bboxes in the same cell. - Instead, the model featurizes at each anchor_bbox (offset) location. - Note that anchor_bboxes at the same offset, but with different - rotation/dimension priors will have the same featurization. - - Note: This model makes assumptions about the ordering of anchor_bboxes, and - assumes that offsets correspond to the 'outer dimensions'; see - input_generator._AnchorBoxSettings.GenerateAnchorSettings for details. - - Note that the different rotation/dimension settings will have their own - classification/regression heads - and these are now shared across all offsets. - - In summary, we want the featurizer to be location (xyz coordinate) specific, - and at each location, we want to have rotation/dimension specific regressors. - This makes it more natural to leverage featurizers that produce features - at these anchor-offset specific locations. For example, one could apply a - PCNN/PointConv model on the entire point cloud (instead of each cell) and - produce features at given anchor locations. - """ - - @classmethod - def Params(cls, - num_classes, - num_anchor_bboxes_offsets, - num_anchor_bboxes_rotations, - num_anchor_bboxes_dimensions, - num_laser_features=1): - num_anchor_bboxes_per_center = ( - num_anchor_bboxes_offsets * num_anchor_bboxes_rotations * - num_anchor_bboxes_dimensions) - p = super().Params(num_classes, num_anchor_bboxes_per_center, - num_laser_features) - - # Good defaults from V1 tuning. - p.loss_norm_type = LossNormType.NORM_BY_NUM_POS_PER_CENTER - - # V2 specifics below. - p.Define( - 'num_anchor_bboxes_offsets', num_anchor_bboxes_offsets, - 'The number of anchor bboxes offsets per center. ' - 'This should match that of the corresponding input generator.') - p.Define( - 'num_anchor_bboxes_rotations', num_anchor_bboxes_rotations, - 'The number of anchor bboxes rotations per center. ' - 'This should match that of the corresponding input generator.') - p.Define( - 'num_anchor_bboxes_dimensions', num_anchor_bboxes_dimensions, - 'The number of anchor bboxes dimensions per center. ' - 'This should match that of the corresponding input generator.') - - builder = Builder() - builder.linear_params_init = py_utils.WeightInit.KaimingUniformFanInRelu() - - # In Model V2, the inputs to the cell featurizer only include the relative - # coordinates of each point concatenated with the point's features. - # GINFeaturizerV2 takes that into account and expects input_dims=4. - num_gin_layers = 5 - gin_hidden_dims = 128 - gin_layers = [ - [gin_hidden_dims * 2, gin_hidden_dims * 4, gin_hidden_dims] - ] * num_gin_layers # pyformat: disable - - # Models can specify different cell featurizers as long as cell_feature_dims - # is also set correctly. - p.Define( - 'cell_feature_dims', gin_hidden_dims * (num_gin_layers + 1), - 'Dimensions of the features produced by the cell featurizer. ' - 'This should match the output of the cell_featurizer.') - p.Define( - 'cell_featurizer', - builder.GINFeaturizerV2( - 'feat', - gin_hidden_dims, - gin_layers, - num_laser_features=num_laser_features), - 'Point cloud local cell feature extractor.') - - # The cell_feature_projector layer projects the featurized cells to each - # offset location. This is mainly to keep compatibility with V1. Note that - # this may be removed once we find a better global featurizer. - # A FC layer will automatically be created at init that projects the - # cell features to each anchor location with this dims. - p.Define( - 'anchor_projected_feature_dims', 128, 'Dimensions of projected ' - 'features from cell featurizer. A FC layer will be used to ' - 'project the cell features.') - - p.Define('oracle_location', False, - 'If true, the model predicts the ground truth for location.') - p.Define('oracle_dimension', False, - 'If true, the model predicts the ground truth for dimension.') - p.Define('oracle_rotation', False, - 'If true, the model predicts the ground truth for rotation.') - p.Define( - 'oracle_classification', False, - 'If true, the model predicts the ground truth for classification.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - builder = Builder() - builder.linear_params_init = py_utils.WeightInit.KaimingUniformFanInRelu() - - num_anchors_per_offset = ( - p.num_anchor_bboxes_rotations * p.num_anchor_bboxes_dimensions) - - # Create params for fixed sub layers. - cell_feature_projector_p = builder.FC( - 'cell_feature_projector', p.cell_feature_dims, - p.num_anchor_bboxes_offsets * p.anchor_projected_feature_dims) - - localization_regressor_p = builder.Linear('localization_regressor', - p.anchor_projected_feature_dims, - num_anchors_per_offset * 7, - py_utils.WeightInit.Constant(0.)) - - classifier_p = builder.LinearWithBias( - 'classifier', - p.anchor_projected_feature_dims, - num_anchors_per_offset * p.num_classes, - bias_params_init=py_utils.WeightInit.Constant(-4.595)) - - self.CreateChild('cell_featurizer', p.cell_featurizer) - self.CreateChild('cell_feature_projector', cell_feature_projector_p) - self.CreateChild('localization_regressor', localization_regressor_p) - self.CreateChild('classifier', classifier_p) - - def _CellFeaturizer(self, theta, input_batch): - """Featurizes each center location.""" - # Validate Shapes - cell_feature = py_utils.HasRank(input_batch.cell_feature, 4) - batch_size, num_centers, num_points_per_cell = py_utils.GetShape( - cell_feature, 3) - - cell_points_xyz = py_utils.HasShape( - input_batch.cell_points_xyz, - [batch_size, num_centers, num_points_per_cell, 3]) - cell_center_xyz = py_utils.HasShape(input_batch.cell_center_xyz, - [batch_size, num_centers, 3]) - - cell_points_padding = py_utils.HasShape( - input_batch.cell_points_padding, - [batch_size, num_centers, num_points_per_cell]) - - # Center each cell - cell_center_xyz = tf.reshape(cell_center_xyz, - [batch_size, num_centers, 1, 3]) - centered_cell_points_xyz = cell_points_xyz - cell_center_xyz - concat_feature = tf.concat([ - centered_cell_points_xyz, cell_feature - ], axis=-1) # pyformat: disable - - # Featurize point clouds at each center. - point_input = py_utils.NestedMap({ - 'points': centered_cell_points_xyz, - 'features': concat_feature, - 'padding': cell_points_padding, - }) - featurized_cell = self.cell_featurizer.FProp(theta.cell_featurizer, - point_input) - featurized_cell = py_utils.HasShape(featurized_cell, - [batch_size, num_centers, -1]) - return featurized_cell - - def ComputePredictions(self, theta, input_batch): - """Computes predictions for `input_batch`. - - Args: - theta: A `.NestedMap` object containing variable values of this task. - input_batch: A `.NestedMap` expected to contain lasers.points_xyz, - lasers.points_feature, lasers.points_padding, cell_center_xyz, - cell_points_xyz, cell_feature, anchor_bboxes, - anchor_localization_residuals, assigned_gt_labels, and - assigned_cls_mask. See class doc string for details. - - Returns: - A `.NestedMap` object containing residuals and classification_logits. - """ - p = self.params - input_batch.Transform(lambda x: (x.shape, x.shape.num_elements())).VLog( - 1, 'input_batch shapes: ') - cell_feature = py_utils.HasRank(input_batch.cell_feature, 4) - batch_size, num_centers = py_utils.GetShape(cell_feature, 2) - - featurized_cell = self._CellFeaturizer(theta, input_batch) - - # Project each featurized_cell features to each bbox per center. - featurized_anchors = self.cell_feature_projector.FProp( - theta.cell_feature_projector, featurized_cell) - - # Reshape output so that we have features per offset. - featurized_anchors = tf.reshape( - featurized_anchors, - [batch_size, num_centers, p.num_anchor_bboxes_offsets, -1]) - - # Predict localization residuals. - predicted_residuals = self.localization_regressor.FProp( - theta.localization_regressor, featurized_anchors) - predicted_residuals = tf.reshape( - predicted_residuals, - [batch_size, num_centers, p.num_anchor_bboxes_per_center, 7]) - - if any([p.oracle_location, p.oracle_dimension, p.oracle_rotation]): - gt_residuals = py_utils.HasShape( - input_batch.anchor_localization_residuals, - [batch_size, num_centers, p.num_anchor_bboxes_per_center, 7]) - residuals = [] - if p.oracle_location: - residuals.append(gt_residuals[..., 0:3]) - else: - residuals.append(predicted_residuals[..., 0:3]) - - if p.oracle_dimension: - residuals.append(gt_residuals[..., 3:6]) - else: - residuals.append(predicted_residuals[..., 3:6]) - - if p.oracle_rotation: - residuals.append(gt_residuals[..., 6:]) - else: - residuals.append(predicted_residuals[..., 6:]) - predicted_residuals = tf.concat(residuals, axis=-1) - - if p.squash_rotation_predictions: - predicted_rotations = predicted_residuals[..., 6:] - predicted_rotations = np.pi * tf.tanh(predicted_rotations) - predicted_residuals = tf.concat( - [predicted_residuals[..., :6], predicted_rotations], axis=-1) - - # Predict object classification at each bbox. - predicted_classification_logits = self.classifier.FProp( - theta.classifier, featurized_anchors) - predicted_classification_logits = tf.reshape( - predicted_classification_logits, [ - batch_size, num_centers, p.num_anchor_bboxes_per_center, - p.num_classes - ]) - - if p.oracle_classification: - assigned_gt_labels = py_utils.HasShape( - input_batch.assigned_gt_labels, - [batch_size, num_centers, p.num_anchor_bboxes_per_center]) - predicted_classification_logits = tf.one_hot(assigned_gt_labels, - p.num_classes) - - return py_utils.NestedMap({ - 'residuals': predicted_residuals, - 'classification_logits': predicted_classification_logits, - }) diff --git a/lingvo/tasks/car/summary.py b/lingvo/tasks/car/summary.py deleted file mode 100644 index 9ce333cb..00000000 --- a/lingvo/tasks/car/summary.py +++ /dev/null @@ -1,594 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Functions to help with plotting summaries. - -These functions try to take care in ensuring that transformations on points and -headings are consistent. For example, when points are transformed from the -standard x/y plane (+x going right, +y going up) to an alternate formulation -such as 'flipped axes', the heading must also be updated to account for the -change in axis directions. -""" - -import collections -import math - -from lingvo import compat as tf -from lingvo.core import plot -from lingvo.tasks.car import transform_util -import matplotlib.patheffects as path_effects -import numpy as np - -import PIL.Image as Image -import PIL.ImageColor as ImageColor -import PIL.ImageDraw as ImageDraw -import PIL.ImageFont as ImageFont - - -# Source code for keys can be found at: -# https://github.com/python-pillow/Pillow/blob/080bfd3ee1412b401d520fe26c51e2f5515e3a65/src/PIL/ImageColor.py#L163 -def _PILColorList(): - pil_color_list = sorted(ImageColor.colormap.keys()) - pil_color_list.remove('black') - return pil_color_list - - -PIL_COLOR_LIST = _PILColorList() - - -def ExtractRunIds(run_segments): - """Extract the RunIds from the run_segments feature field. - - Args: - run_segments: a string Tensor of shape [batch, 1] containing a text proto. - - See `SummaryTest.testExtractRunIds` for an example. - - Returns: - A string Tensor of shape [batch], containing the extracted run id. - """ - run_segments = tf.convert_to_tensor(run_segments)[:, 0] - return tf.strings.regex_replace(run_segments, - r'[^:]+: "(.+)"\n[^:]+: (\d+)(.|\n)*', - r'\1_\2') - - -def CameraImageSummary(frontal_images, run_segment_strings, figsize=(6, 4)): - """Write frontal_images as tf.Summaries. - - Args: - frontal_images: Float tensor of frontal camera images: Shape: [batch, - height, width, depth]. Expected aspect ratio of 3:2 for visualization. - run_segment_strings: Tensor of strings: Shape: [batch, 1]. The associated - RunSegment proto for the batch. - figsize: Tuple indicating size of camera image. Default is (6, 4) - indicating a 3:2 aspect ratio for visualization. - """ - # Parse the run segment strings to extract the run segment info. - run_segment_ids = ExtractRunIds(run_segment_strings) - - def DrawCameraImage(fig, axes, frontal_image, run_segment_id): - """Draw camera image for image summary.""" - plot.AddImage( - fig=fig, - axes=axes, - data=frontal_image / 256., - show_colorbar=False, - suppress_xticks=True, - suppress_yticks=True) - txt = axes.text( - x=0.5, - y=0.01, - s=run_segment_id, - color='blue', - fontsize=14, - transform=axes.transAxes, - horizontalalignment='center') - txt.set_path_effects([ - path_effects.Stroke(linewidth=3, foreground='lightblue'), - path_effects.Normal() - ]) - - with plot.MatplotlibFigureSummary( - 'examples', figsize=figsize, max_outputs=10) as fig: - # Plot raw frontal image samples for each example. - fig.AddSubplot([frontal_images, run_segment_ids], DrawCameraImage) - - -def _CarToImageTransform(): - return transform_util.MakeCarToImageTransform( - pixels_per_meter=10., image_ref_x=250, image_ref_y=750, flip_axes=True) - - -def DrawTopDown(lasers): - """Draw the laser points in the top down car view.""" - # For every laser point, convert the point to a top down image. - - # Lasers is a [r, b, s*6] tensor where b is the batch size. - # Transpose back to batch major - lasers = np.transpose(lasers, [1, 0, 2]) - # Reshape data to get all points in the spin. - lasers = np.reshape(lasers, [np.shape(lasers)[0], -1]) - - car_to_image_transform = _CarToImageTransform() - - # Create an empty image of the appropriate size. - batch_size = min(8, np.shape(lasers)[0]) - images = np.zeros(shape=(batch_size, 1000, 500, 3), dtype=np.uint8) - - # TODO(vrv): Slice the lasers into [b, x, y, z] matrix - # and then do a batch_matmul on all points at once. - max_npoints = np.shape(lasers)[1] // 6 - for b in range(batch_size): - for i in range(max_npoints): - index = i * 6 - x = lasers[b][index] - y = lasers[b][index + 1] - z = lasers[b][index + 2] - - # TODO(vrv): Use lasers_padding to filter out invalid points. - # For now, just assume that all zeros means that the point - # shouldn't be drawn. - if x == 0 and y == 0 and z == 0: - continue - - tx, ty, _ = transform_util.TransformPoint(car_to_image_transform, x, y, z) - # Point outside image. - if tx < 0 or ty < 0 or tx >= images.shape[2] or ty >= images.shape[1]: - continue - - # Fill in that point in the image - images[b, int(ty), int(tx), :] = (255, 255, 255) - - return images - - -def MakeRectangle(l, w, theta, offset=(0, 0)): - """Make rotated rectangle.""" - c, s = math.cos(theta), math.sin(theta) - coordinates = [(l / 2.0, w / 2.0), (l / 2.0, -w / 2.0), (-l / 2.0, -w / 2.0), - (-l / 2.0, w / 2.0)] - return [(c * x - s * y + offset[0], s * x + c * y + offset[1]) - for (x, y) in coordinates] - - -def DrawHeadingTriangle(draw, x, y, heading, color, scale=25): - """Draw a triangle indicating `heading` at `x`, `y` with `color`.""" - ch = scale * math.cos(heading) - sh = scale * math.sin(heading) - lead_point = (x - ch, y - sh) - anchor_1 = (x - sh / 3, y + ch / 3) - anchor_2 = (x + sh / 3, y - ch / 3) - draw.line([anchor_1, lead_point, anchor_2], fill=color, width=4) - - -def DrawCircle(draw, x, y, fill, outline, circle_size=5): - """Draw a circle at `x`, `y`.""" - draw.ellipse( - (x - circle_size, y - circle_size, x + circle_size, y + circle_size), - fill=fill, - outline=outline) - - -# TODO(vrv): Support multiple display strings. -def DrawBoundingBoxOnImage(image, - box, - display_str, - color='red', - thickness=4, - text_loc='BOTTOM'): - """Draw bounding box on the input image.""" - original_image = image - image = Image.fromarray(np.uint8(original_image)).convert('RGB') - draw = ImageDraw.Draw(image) - - center_x, center_y, width, height, heading = box - box2d = transform_util.Box2D(center_x, center_y, width, height, heading) - corners = list(box2d.corners.reshape(-1, 2)) - points = [tuple(c) for c in corners] - points += [points[0]] - draw.line(points, fill=color, width=thickness) - - # Draw heading. - max_dim = max(width, height) / 2. - end_heading_point = (center_x + max_dim * math.cos(heading), - center_y + max_dim * math.sin(heading)) - start_heading_point = ((end_heading_point[0] - center_x) / 2 + center_x, - (end_heading_point[1] - center_y) / 2 + center_y) - - heading_points = [start_heading_point, end_heading_point] - draw.line(heading_points, fill=color, width=thickness) - - # Compute extremes so we can anchor the labels to them. - xs = [x[0] for x in points] - ys = [x[1] for x in points] - left = np.min(xs) - bottom = np.min(ys) - top = np.max(ys) - - try: - font = ImageFont.truetype('arial.ttf', 24) - except IOError: - font = ImageFont.load_default() - - text_width, text_height = font.getsize(display_str) - margin = np.ceil(0.05 * text_height) - if text_loc == 'TOP': - text_bottom = top - else: - text_bottom = bottom + text_height - - draw.rectangle([(left, text_bottom - text_height - 2 * margin), - (left + text_width, text_bottom)], - fill=color) - draw.text((left + margin, text_bottom - text_height - margin), - display_str, - fill='black', - font=font) - np.copyto(original_image, np.array(image)) - - -def VisualizeBoxes(image, - boxes, - classes, - scores, - class_id_to_name, - min_score_thresh=.25, - line_thickness=4, - groundtruth_box_visualization_color='black', - skip_scores=False, - skip_labels=False, - text_loc='TOP'): - """Visualize boxes on top down image.""" - box_to_display_str_map = collections.defaultdict(str) - box_to_color_map = collections.defaultdict(str) - num_boxes = boxes.shape[0] - for i in range(num_boxes): - if scores is not None and scores[i] < min_score_thresh: - continue - box = tuple(boxes[i].tolist()) - display_str = '' - if not skip_labels: - if classes[i] in class_id_to_name: - class_name = class_id_to_name[classes[i]] - display_str = str(class_name) - else: - display_str = 'N/A' - if not skip_scores: - if not display_str: - display_str = '{}%'.format(int(100 * scores[i])) - else: - display_str = '{}: {}%'.format(display_str, int(100 * scores[i])) - box_to_display_str_map[box] = display_str - if scores is None: - box_to_color_map[box] = groundtruth_box_visualization_color - else: - box_to_color_map[box] = PIL_COLOR_LIST[classes[i] % len(PIL_COLOR_LIST)] - - # Draw all boxes onto image. - for box, color in box_to_color_map.items(): - DrawBoundingBoxOnImage( - image, - box, - color=color, - thickness=line_thickness, - display_str=box_to_display_str_map[box], - text_loc=text_loc) - return image - - -def TransformBBoxesToTopDown(bboxes, car_to_image_transform=None): - """Convert bounding boxes from car coordinates to top down pixel coordinates. - - Args: - bboxes: A (batch, nbboxes, 4 or 5) np.float32 tensor containing bounding box - xywhh in car coordinates (smooth adjusted by car pose). - car_to_image_transform: An optional Transform object. If None, this will be - created using _CarToImageTransform. - - Returns: - np.array of shape (batch, nbboxes) containing the bounding boxes in top down - image space. - """ - if car_to_image_transform is None: - car_to_image_transform = _CarToImageTransform() - - batch_size = np.shape(bboxes)[0] - nbboxes = np.shape(bboxes)[1] - transformed_boxes = np.zeros_like(bboxes) - - for batch_id in range(batch_size): - for box_id in range(nbboxes): - # TODO(vrv): When we predict heading, we should assert - # that the length of bbox_data is 5. - bbox_data = np.squeeze(bboxes[batch_id, box_id, :]) - - x, y, width, height = (bbox_data[0], bbox_data[1], bbox_data[2], - bbox_data[3]) - - if len(bbox_data) == 5: - heading = bbox_data[4] - else: - heading = 0.0 - - # Skip boxes that cannot be visualized. - if width <= 0 or height <= 0: - continue - if any([np.isnan(c) or np.isinf(c) for c in bbox_data]): - continue - - # Bounding boxes are in car coordinates (smooth adjusted by car pose). - # Transform from car coordinates to new coordinates. - bbox_car = transform_util.Box2D(x, y, width, height, heading) - bbox_transformed = bbox_car.Apply(car_to_image_transform) - bbox_values = bbox_transformed.AsNumpy() - if len(bbox_data) == 4: - bbox_values = bbox_values[:-1] - transformed_boxes[batch_id, box_id, :] = bbox_values - - return transformed_boxes - - -def DrawBBoxesOnImages(images, bboxes, box_weights, labels, class_id_to_name, - groundtruth): - """Draw ground truth boxes on top down image. - - Args: - images: A 4D uint8 array (batch, height, width, depth) of images to draw on - top of. - bboxes: A (batch, nbboxes, 4 or 5) np.float32 tensor containing bounding box - xywhh to draw specified in top down pixel values. - box_weights: A (batch, nbboxes) float matrix indicating the predicted score - of the box. If the score is 0.0, no box is drawn. - labels: A (batch, nbboxes) integer matrix indicating the true or predicted - label indices. - class_id_to_name: Dictionary mapping from class id to name. - groundtruth: Boolean indicating whether bounding boxes are ground truth. - - Returns: - 'images' with the bboxes drawn on top. - """ - # Assert 4d shape. - assert len(np.shape(images)) == 4 - - if np.shape(images)[3] == 1: - # Convert from grayscale to RGB. - images = np.tile(images, (1, 1, 1, 3)) - - # Assert channel dimension is 3 dimensional. - assert np.shape(images)[3] == 3 - - batch_size = np.shape(images)[0] - nbboxes = np.shape(bboxes)[1] - - for batch_id in range(batch_size): - image = images[batch_id, :, :, :] - # Draw a box for each box and label if weights is 1. - - transformed_boxes = [] - box_scores = [] - label_ids = [] - - for box_id in range(nbboxes): - box_weight = box_weights[batch_id, box_id] - # If there is no box to draw, continue. - if box_weight == 0.0: - continue - - # TODO(vrv): When we predict heading, we should assert - # that the length of bbox_data is 5. - bbox_data = np.squeeze(bboxes[batch_id, box_id, :]) - if len(bbox_data) == 5: - x, y, width, length, heading = bbox_data - else: - x, y, width, length = bbox_data - heading = 0.0 - - # Check whether we can draw the box. - bbox = transform_util.Box2D(x, y, width, length, heading) - ymin, xmin, ymax, xmax = bbox.Extrema() - - if ymin == 0 and xmin == 0 and ymax == 0 and xmax == 0: - continue - - # TODO(vrv): Support drawing boxes on the edge of the - # image. - if (xmin < 0 or ymin < 0 or xmax >= image.shape[1] or - ymax >= image.shape[0]): - continue - - # We can draw a box on the image, so fill in the score, the boxes, - # and the label. - transformed_boxes.append([x, y, width, length, heading]) - box_scores.append(box_weight) - label_ids.append(labels[batch_id, box_id]) - - if transformed_boxes: - transformed_boxes = np.stack(transformed_boxes, axis=0) - scores = None if groundtruth else np.array(box_scores) - text_loc = 'TOP' if groundtruth else 'BOTTOM' - VisualizeBoxes( - image=image, - boxes=transformed_boxes, - classes=label_ids, - scores=scores, - class_id_to_name=class_id_to_name, - groundtruth_box_visualization_color='cyan', - skip_scores=groundtruth, - skip_labels=False, - text_loc=text_loc) - return images - - -def DrawTrajectory(image, bboxes, masks, labels, is_groundtruth): - """Draw the trajectory of bounding boxes on 'image'. - - Args: - image: The uint8 image array to draw on. Assumes [1000, 500, 3] input with - RGB value ranges. - bboxes: A [num_steps, num_objects, 5] float array containing the bounding - box information over a sequence of steps. bboxes are expected to be in - car coordinates. - masks: A [num_steps, num_objects] integer array indicating whether the - corresponding bbox entry in bboxes is present (1 = present). - labels: A [num_steps, num_objects] integer label indicating which class is - being predicted. Used for colorizing based on labels. - is_groundtruth: True if the scene is the groundtruth vs. the predicted. - - Returns: - The updated image array. - """ - image = Image.fromarray(np.uint8(image)).convert('RGB') - draw = ImageDraw.Draw(image) - - try: - font = ImageFont.truetype('arial.ttf', 20) - except IOError: - font = ImageFont.load_default() - - pixels_per_meter = 10. - image_ref_x = 250. - image_ref_y = 750. - car_to_image_transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=pixels_per_meter, - image_ref_x=image_ref_x, - image_ref_y=image_ref_y, - flip_axes=True) - - # Iterate over each object and produce the series of visualized trajectories - # over time. - for object_idx in range(bboxes.shape[1]): - # Annotate the box with the class type - label = labels[0, object_idx] - - # Choose a label_consistent color. - color = PIL_COLOR_LIST[label % len(PIL_COLOR_LIST)] - # Make predictions white. - if not is_groundtruth: - color = 'white' - - # Convert string color name to RGB so we can manipulate it. - color_rgb = ImageColor.getrgb(color) - - # For each sample, extract the data, transform to image coordinates, and - # store in centroids. - centroids = [] - for time in range(bboxes.shape[0]): - if masks[time, object_idx] == 0: - continue - - center_x, center_y, width, height, heading = bboxes[time, object_idx, :] - - # Compute the new heading. - heading = transform_util.TransformHeading(car_to_image_transform, heading) - - # Transform from car to image coords. - x, y, _ = transform_util.TransformPoint(car_to_image_transform, center_x, - center_y, 0.0) - - # Hack to scale from meters to pixels. - width *= pixels_per_meter - height *= pixels_per_meter - - # Collect the centroids of all of the points. - centroids.append((x, y, heading)) - - # Draw the groundtruth bounding box at the first timestep. - if is_groundtruth and time == 0: - # Draw a rectangle - rect = MakeRectangle(height, width, heading, offset=(x, y)) - rect += [rect[0]] - draw.line(rect, fill=color_rgb, width=4) - - delta = 20 - - # Annotate the box with the object index - draw.text((x + delta, y + delta), - str(object_idx), - fill='white', - font=font) - - # Draw a callout - draw.line([(x, y), (x + delta, y + delta)], fill='white', width=1) - - # Extract the point pairs from centroids and draw a line through them. - point_pairs = [] - for (x, y, heading) in centroids: - point_pairs.append((x, y)) - if point_pairs: - draw.line(point_pairs, width=4, fill=color_rgb) - - # Draw the centroids. - triangle_color_rgb = color_rgb - for i, (x, y, heading) in enumerate(centroids): - if i == 0: - # Draw the heading for the first timestep. - scale = 25 if is_groundtruth else 15 - DrawHeadingTriangle(draw, x, y, heading, triangle_color_rgb, scale) - else: - # Draw a circle for the centroids of other timesteps. - outline_color = color_rgb - circle_size = 5 if is_groundtruth else 3 - DrawCircle( - draw, - x, - y, - fill=triangle_color_rgb, - outline=outline_color, - circle_size=circle_size) - - # Desaturate the color with every timestep. - increment = 45 # Allow this to be modified? - triangle_color_rgb = (triangle_color_rgb[0] - increment, - triangle_color_rgb[1] - increment, - triangle_color_rgb[2] - increment) - - return np.array(image) - - -def GetTrajectoryComparison(gt_bboxes, gt_masks, gt_labels, pred_bboxes, - pred_masks, pred_labels): - """Draw a trajectory comparison of groundtruth and predicted. - - Args: - gt_bboxes: A [batch_size, num_steps, num_objects, 5] float array containing - the bounding box information over a sequence of steps. - gt_masks: A [batch_size, num_steps, num_objects] integer array indicating - whether the corresponding bbox entry in bboxes is present (1 = present). - gt_labels: A [batch_size, num_steps, num_objects] integer label indicating - which class is being predicted. Used for colorizing based on labels. - pred_bboxes: A [batch_size, num_steps, num_objects, 5] float array - containing the bounding box information over a sequence of steps. - pred_masks: A [batch_size, num_steps, num_objects] integer array indicating - whether the corresponding bbox entry in bboxes is present (1 = present). - pred_labels: A [batch_size, num_steps, num_objects] integer label indicating - which class is being predicted. Used for colorizing based on labels. - - Returns: - images: A np.uint8 images array that can be displayed of size - [batch_size, 1000, 500, 3]. - """ - batch_size = gt_bboxes.shape[0] - images = np.zeros([batch_size, 1000, 500, 3], dtype=np.uint8) - for b in range(batch_size): - images[b] = DrawTrajectory( - images[b], gt_bboxes[b], gt_masks[b], gt_labels[b], is_groundtruth=True) - images[b] = DrawTrajectory( - images[b], - pred_bboxes[b], - pred_masks[b], - pred_labels[b], - is_groundtruth=False) - return images diff --git a/lingvo/tasks/car/summary_test.py b/lingvo/tasks/car/summary_test.py deleted file mode 100644 index 98a5021e..00000000 --- a/lingvo/tasks/car/summary_test.py +++ /dev/null @@ -1,112 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for summary.""" - -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.car import summary -import numpy as np - - -class SummaryTest(test_utils.TestCase): - """Tests for Summary utilities.""" - - def testDrawBBoxes(self): - bs = 5 - nbboxes = 2 - class_id_to_name = {0: 'foo', 1: 'bar'} - - loc_weights = np.ones(shape=(bs, nbboxes)) - labels = np.ones(shape=(bs, nbboxes)) - cbboxes = np.zeros(shape=(bs, nbboxes, 4)) - # Ensures ymin/xmin/ymax/ymin. - cbboxes[:, :, :2] = 100. * np.random.normal(size=(bs, nbboxes, 2)) - cbboxes[:, :, 2:4] = cbboxes[:, :, :2] + 10. * np.random.uniform( - size=(bs, nbboxes, 2)) - - images = np.zeros(shape=(bs, 100, 100, 1), dtype=np.uint8) - summary.DrawBBoxesOnImages(images, cbboxes, loc_weights, labels, - class_id_to_name, True) - - def testTransformBBoxesToTopDown(self): - bs = 5 - nbboxes = 2 - cbboxes = np.zeros(shape=(bs, nbboxes, 4)) - # Ensures ymin/xmin/ymax/ymin. - cbboxes[:, :, :2] = 100. * np.random.normal(size=(bs, nbboxes, 2)) - cbboxes[:, :, 2:4] = cbboxes[:, :, :2] + 10. * np.random.uniform( - size=(bs, nbboxes, 2)) - - image_bboxes = summary.TransformBBoxesToTopDown(cbboxes) - self.assertAllEqual(cbboxes.shape, image_bboxes.shape) - - def testExtractRunIds(self): - - def PythonExtractRunIds(run_segments): - """Extract the RunIds from the run_segments feature field.""" - num_segments = run_segments.shape[0] - run_ids = [] - for i in range(num_segments): - # Run segment is a serialized RunSegmentProto. We don't have access to - # it, but it's pretty simple so just manually parse the string for now - # to get the run id. - # - # One can add a duplicate proto to make parsing more explicit. - run_segment = run_segments[i][0] - run_id = run_segment.split('\n')[0].split('"')[1] - start_time = str(int(float(run_segment.split('\n')[1].split(' ')[1]))) - run_ids.append(run_id + '_' + start_time) - run_ids = [tf.compat.as_bytes(r) for r in run_ids] - return np.stack(run_ids) - - example_run_segments = [ - [ - 'run: "20170903_161642_C00844"\nstart_offset:' - ' 6876.0795650482178\nend_offset: 6896.0795650482178\nstart_ts:' - ' 1504462278.6018586\nend_ts: 1504462298.6018586\n' - ], - [ - 'run: "20001029_0123458_C00844"\nstart_offset:' - ' 66666.0795650482178\nend_offset: 77777.0795650482178\nstart_ts:' - ' 1504462278.6018586\nend_ts: 1504462298.6018586\n' - ], - ] - expected_ids = PythonExtractRunIds(np.array(example_run_segments)) - with self.session(): - run_ids = self.evaluate( - summary.ExtractRunIds(tf.constant(example_run_segments))) - self.assertAllEqual(expected_ids, run_ids) - - def testTrajectory(self): - # Test that the trajectory code can execute. A test that validates the - # output is challenging - bs = 2 - steps = 5 - nbboxes = 8 - gt_bboxes = np.zeros(shape=(bs, steps, nbboxes, 5)) - gt_masks = np.ones(shape=(bs, steps, nbboxes)) - - pred_bboxes = np.zeros(shape=(bs, steps, nbboxes, 5)) - pred_masks = np.ones(shape=(bs, steps, nbboxes)) - labels = np.ones(shape=(bs, steps, nbboxes)).astype(np.int32) - - image = summary.GetTrajectoryComparison(gt_bboxes, gt_masks, labels, - pred_bboxes, pred_masks, labels) - self.assertEqual((bs, 1000, 500, 3), image.shape) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/testdata/BUILD b/lingvo/tasks/car/testdata/BUILD deleted file mode 100644 index 613f4152..00000000 --- a/lingvo/tasks/car/testdata/BUILD +++ /dev/null @@ -1,24 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) # Apache 2.0 - -filegroup( - name = "kitti_raw", - data = glob([ - "kitti_raw_*", - ]), -) - -filegroup( - name = "waymo_tutorial_frames", - data = glob([ - "waymo_*", - ]), -) - -filegroup( - name = "waymo_ap_testdata", - data = glob([ - "waymo_decode_sparse_dev*", - ]), -) diff --git a/lingvo/tasks/car/testdata/kitti_raw_calib_testdata.txt b/lingvo/tasks/car/testdata/kitti_raw_calib_testdata.txt deleted file mode 100644 index 2b8496d5..00000000 --- a/lingvo/tasks/car/testdata/kitti_raw_calib_testdata.txt +++ /dev/null @@ -1,7 +0,0 @@ -P0: 7.215377000000e+02 0.000000000000e+00 6.095593000000e+02 0.000000000000e+00 0.000000000000e+00 7.215377000000e+02 1.728540000000e+02 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 1.000000000000e+00 0.000000000000e+00 -P1: 7.215377000000e+02 0.000000000000e+00 6.095593000000e+02 -3.875744000000e+02 0.000000000000e+00 7.215377000000e+02 1.728540000000e+02 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 1.000000000000e+00 0.000000000000e+00 -P2: 7.215377000000e+02 0.000000000000e+00 6.095593000000e+02 4.485728000000e+01 0.000000000000e+00 7.215377000000e+02 1.728540000000e+02 2.163791000000e-01 0.000000000000e+00 0.000000000000e+00 1.000000000000e+00 2.745884000000e-03 -P3: 7.215377000000e+02 0.000000000000e+00 6.095593000000e+02 -3.395242000000e+02 0.000000000000e+00 7.215377000000e+02 1.728540000000e+02 2.199936000000e+00 0.000000000000e+00 0.000000000000e+00 1.000000000000e+00 2.729905000000e-03 -R0_rect: 9.999239000000e-01 9.837760000000e-03 -7.445048000000e-03 -9.869795000000e-03 9.999421000000e-01 -4.278459000000e-03 7.402527000000e-03 4.351614000000e-03 9.999631000000e-01 -Tr_velo_to_cam: 7.533745000000e-03 -9.999714000000e-01 -6.166020000000e-04 -4.069766000000e-03 1.480249000000e-02 7.280733000000e-04 -9.998902000000e-01 -7.631618000000e-02 9.998621000000e-01 7.523790000000e-03 1.480755000000e-02 -2.717806000000e-01 -Tr_imu_to_velo: 9.999976000000e-01 7.553071000000e-04 -2.035826000000e-03 -8.086759000000e-01 -7.854027000000e-04 9.998898000000e-01 -1.482298000000e-02 3.195559000000e-01 2.024406000000e-03 1.482454000000e-02 9.998881000000e-01 -7.997231000000e-01 diff --git a/lingvo/tasks/car/testdata/kitti_raw_label_testdata.txt b/lingvo/tasks/car/testdata/kitti_raw_label_testdata.txt deleted file mode 100644 index 766f76ec..00000000 --- a/lingvo/tasks/car/testdata/kitti_raw_label_testdata.txt +++ /dev/null @@ -1,7 +0,0 @@ -Truck 0.00 0 -1.57 599.41 156.40 629.75 189.25 2.85 2.63 12.34 0.47 1.49 69.44 -1.56 -Car 0.00 0 1.85 387.63 181.54 423.81 203.12 1.67 1.87 3.69 -16.53 2.39 58.49 1.57 -Cyclist 0.00 3 -1.65 676.60 163.95 688.98 193.93 1.86 0.60 2.02 4.59 1.32 45.84 -1.55 -DontCare -1 -1 -10 503.89 169.71 590.61 190.13 -1 -1 -1 -1000 -1000 -1000 -10 -DontCare -1 -1 -10 511.35 174.96 527.81 187.45 -1 -1 -1 -1000 -1000 -1000 -10 -DontCare -1 -1 -10 532.37 176.35 542.68 185.27 -1 -1 -1 -1000 -1000 -1000 -10 -DontCare -1 -1 -10 559.62 175.83 575.40 183.15 -1 -1 -1 -1000 -1000 -1000 -10 diff --git a/lingvo/tasks/car/tools/BUILD b/lingvo/tasks/car/tools/BUILD deleted file mode 100644 index 16d33d62..00000000 --- a/lingvo/tasks/car/tools/BUILD +++ /dev/null @@ -1,109 +0,0 @@ -package(default_visibility = [ - "//visibility:public", -]) - -licenses(["notice"]) # Apache 2.0 - -py_binary( - name = "kitti_exporter", - srcs = ["kitti_exporter.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":kitti_data", - # Implicit network file system dependency. - # Implicit PIL dependency. - # Implicit absl.app dependency. - # Implicit absl.flags dependency. - # Implicit absl.logging dependency. - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_binary( - name = "create_kitti_crop_dataset", - srcs = [ - "create_kitti_crop_dataset.py", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - # Implicit absl.app dependency. - # Implicit absl.flags dependency. - # Implicit apache_beam dependency. - "//lingvo:compat", - "//lingvo:model_registry", - "//lingvo/core:cluster_factory", - "//lingvo/core:py_utils", - "//lingvo/tasks/car:geometry", - "//lingvo/tasks/car:input_extractor", - "//lingvo/tasks/car/params", - "//lingvo/tools:beam_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "kitti_data", - srcs = ["kitti_data.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_test( - name = "kitti_data_test", - srcs = ["kitti_data_test.py"], - data = [ - "//lingvo/tasks/car/testdata:kitti_raw", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":kitti_data", - "//lingvo:compat", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - "//lingvo/tasks/car:geometry", - # Implicit numpy dependency. - ], -) - -py_library( - name = "export_kitti_detection_lib", - srcs = ["export_kitti_detection.py"], - srcs_version = "PY3", - deps = [ - ":kitti_data", - # Implicit absl.flags dependency. - "//lingvo:compat", - "//lingvo/core/ops:record_py_pb2", - "//lingvo/tasks/car:kitti_metadata", - # Implicit numpy dependency. - ], -) - -py_binary( - name = "export_kitti_detection", - srcs = ["export_kitti_detection.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [":export_kitti_detection_lib"], -) - -py_binary( - name = "compare_params", - srcs = ["compare_params_main.py"], - main = "compare_params_main.py", - python_version = "PY3", - srcs_version = "PY3", - deps = [ - # Implicit absl.flags dependency. - "//lingvo:compat", - "//lingvo/tasks/car/params:kitti", - "//lingvo/tools:compare_params_lib", - ], -) diff --git a/lingvo/tasks/car/tools/__init__.py b/lingvo/tasks/car/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/car/tools/compare_params_main.py b/lingvo/tasks/car/tools/compare_params_main.py deleted file mode 100644 index f61e3a9f..00000000 --- a/lingvo/tasks/car/tools/compare_params_main.py +++ /dev/null @@ -1,47 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tool for comparing two models / hyperparams.""" - -from absl import flags -from lingvo import compat as tf -from lingvo.tasks.car.params import kitti # pylint:disable=unused-import -from lingvo.tools import compare_params - -FLAGS = flags.FLAGS -flags.DEFINE_string("model1", None, - "Registered name or path to params.txt of model 1.") -flags.DEFINE_string("model2", None, - "Registered name or path to params.txt of model 2.") - - -def main(argv): - if len(argv) > 1: - raise tf.app.UsageError("Too many command-line arguments.") - - cfg1_text = compare_params.get_model_params_as_text(FLAGS.model1) - cfg2_text = compare_params.get_model_params_as_text(FLAGS.model2) - - cfg1_not_cfg2, cfg2_not_cfg1, cfg1_and_cfg2_diff = ( - compare_params.hyperparams_text_diff(cfg1_text, cfg2_text)) - - compare_params.print_hyperparams_text_diff(FLAGS.model1, FLAGS.model2, - cfg1_not_cfg2, cfg2_not_cfg1, - cfg1_and_cfg2_diff) - return 0 - - -if __name__ == "__main__": - tf.app.run(main) diff --git a/lingvo/tasks/car/tools/create_kitti_crop_dataset.py b/lingvo/tasks/car/tools/create_kitti_crop_dataset.py deleted file mode 100644 index 8f8e1d6c..00000000 --- a/lingvo/tasks/car/tools/create_kitti_crop_dataset.py +++ /dev/null @@ -1,272 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -r"""Creates KITTI Classification dataset out of the KITTI Object detection data. - -Produces TFRecords with the following format: --------------------------------------------------------------------------------- - -Values: - num_points: Int64 scalar with the number of points in that box. - - points: Float list of 3D locations (X, Y, Z) per point. - - points_feature: Float list of per-point feature (reflectance scalar). - - bbox_3d: Float list of the 7 box coordinates. bboxes_3d[0:3] is the absolute - center location in meters. bboxes_3d[3:6] is the bbox dimensions in meters, - and bboxes_3d[6] is the rotation in radians. - - label: An Int64 scalar with the class label id. - - text: A bytelist with the string name of the class. - - difficulty: An Int64 scalar with the difficulty level of the object. - - occlusion: An Int64 scalar with the occlusion level of the object. - - scene_id: An Int64 scalar with the original scene id the object is from. - - bbox_id: An Int64 scalar with which number bounding box the object is from. - - -Note: All scalars are wrapped in a list of len 1. --------------------------------------------------------------------------------- - -IMPORTANT: Depending on your model, you may or may not want to enable -preprocessors to export for data augmentation. If unsure, do *not* run with -preprocessors as that might add additional data augmentation steps. - -To run: - -bazel run -c opt \ - //lingvo/tasks/car/tools:create_kitti_crop_dataset \ - --model_name=car.kitti.PillarsModelV1 \ - --norun_preprocessors \ - --input_file_pattern=/path/to/kitti/train_pattern \ - --output_file_pattern=/path/to/output/gt_objects@100 -""" - -from absl import app -from absl import flags - -import apache_beam as beam - -from lingvo import compat as tf -from lingvo import model_registry -from lingvo.core import cluster_factory -from lingvo.core import py_utils -from lingvo.tasks.car import geometry -from lingvo.tasks.car import input_extractor -from lingvo.tasks.car.params import kitti # pylint: disable=unused-import -from lingvo.tools import beam_utils - -import numpy as np - - -flags.DEFINE_string('input_file_pattern', None, 'Where to get the data.') -flags.DEFINE_string('output_file_pattern', None, - 'Output directory + prefix where to write files.') -flags.DEFINE_string( - 'model_name', 'car.kitti.PillarsModelV1', - 'Name of registered model whose input pipeline ' - 'will be used to mine groundtruth objects from.') -flags.DEFINE_enum('split', 'Train', ['Train', 'Dev'], - 'Split of data to generate GT objects from') -flags.DEFINE_bool( - 'run_preprocessors', False, - 'Whether to run the preprocessors when extracting data. ' - 'Leave this as False if you are generating data for the ' - 'purposes of analysis. If you are generating data for ground ' - 'truth bbox augmentation, enable this.') - -FLAGS = flags.FLAGS - - -def _GetFilteredBoundingBoxData(kitti_data): - """Given a single batch element of data, process it for writing. - - Args: - kitti_data: A NestedMap of KITTI input generator returned data with a batch - size of 1. - - Returns: - A NestedMap of all the output data we need to write per bounding box - cropped pointclouds. - """ - points = kitti_data.lasers.points_xyz - points_feature = kitti_data.lasers.points_feature - bboxes_3d = kitti_data.labels.bboxes_3d - bboxes_3d_mask = kitti_data.labels.bboxes_3d_mask - bboxes_3d = tf.boolean_mask(bboxes_3d, bboxes_3d_mask) - - if 'points_padding' in kitti_data.lasers: - points_validity_mask = tf.cast(kitti_data.lasers.points_padding - 1, - tf.bool) - points = tf.boolean_mask(points, points_validity_mask) - points_feature = tf.boolean_mask(points_feature, points_validity_mask) - - points_in_bboxes_mask = geometry.IsWithinBBox3D(points, bboxes_3d) - - output_map = py_utils.NestedMap() - # Points and features contain the whole pointcloud, which we will use - # per box boolean masks later in _ToTFExampleProto to subselect data per box. - output_map.points = points - output_map.points_feature = points_feature - output_map.points_in_bboxes_mask = points_in_bboxes_mask - - output_map.source_id = kitti_data.labels.source_id - - # Add additional data - output_keys = [ - 'bboxes_3d', - 'labels', - 'texts', - 'occlusion', - 'difficulties', - 'truncation', - ] - for key in output_keys: - output_map[key] = tf.boolean_mask(kitti_data.labels[key], - kitti_data.labels.bboxes_3d_mask) - return output_map - - -class _ProcessShard(beam.DoFn): - """Process a given shard.""" - - def __init__(self, model_name, split, run_preprocessors): - self._model_name = model_name - self._split = split - self._run_preprocessors = run_preprocessors - self._sess = None - - # Create a cluster configuration assuming evaluation; the input pipelines - # need to know the cluster job type to set up the outputs correctly. - cluster = cluster_factory.Current() - cluster.params.job = 'evaler' - cluster.params.mode = 'sync' - cluster.params.task = 0 - cluster.params.evaler.replicas = 1 - self._cluster = cluster_factory.Cluster(cluster.params) - - def _create_graph(self): - if self._sess is not None: - return - - with self._cluster: - cfg = model_registry.GetParams(self._model_name, self._split) - cfg.input.batch_size = 1 - # Turn off label filtering so the database contains - # all objects. - cfg.input.extractors.labels.filter_labels = None - - # Disable preprocessors if they are not required. - if not self._run_preprocessors: - cfg.input.preprocessors_order = [] - - graph = tf.Graph() - with graph.as_default(): - inp = cfg.input.Instantiate() - self._elem = tf.placeholder(tf.string) - bucket, batch = inp.ExtractUsingExtractors(self._elem) - self._filtered_data = _GetFilteredBoundingBoxData(batch) - self._bucket = bucket - self._sess = tf.Session(graph=graph) - - def _ToTFExampleProto(self, filtered_data, bbox_idx): - num_boxes = filtered_data.bboxes_3d.shape[0] - if bbox_idx >= num_boxes: - raise ValueError('`bbox_id` should be < num_boxes') - bbox_mask = filtered_data.points_in_bboxes_mask[:, bbox_idx] - - num_points, _ = py_utils.GetShape(filtered_data.points[bbox_mask], 2) - - example = tf.train.Example() - feature = example.features.feature - feature['num_points'].int64_list.value[:] = [num_points] - feature['points'].float_list.value[:] = ( - filtered_data.points[bbox_mask].ravel().tolist()) - feature['points_feature'].float_list.value[:] = ( - filtered_data.points_feature[bbox_mask].ravel().tolist()) - # Note: bbox_3d, label, text, difficulty are singular, which is inconsistent - # with the original KITTI data, but that is because each example - # has only one bounding box, one label, one text, one difficulty. - feature['bbox_3d'].float_list.value[:] = ( - filtered_data.bboxes_3d[bbox_idx, :].ravel().tolist()) - feature['label'].int64_list.value[:] = [ - filtered_data.labels[bbox_idx].astype(np.int64) - ] - feature['text'].bytes_list.value[:] = [filtered_data.texts[bbox_idx]] - feature['difficulty'].int64_list.value[:] = [ - filtered_data.difficulties[bbox_idx].astype(np.int64) - ] - feature['occlusion'].int64_list.value[:] = [ - filtered_data.occlusion[bbox_idx].astype(np.int64) - ] - feature['scene_id'].int64_list.value[:] = [int(filtered_data.source_id)] - feature['bbox_id'].int64_list.value[:] = [int(bbox_idx)] - return example - - def process(self, value): - self._create_graph() - elem_str = value.SerializeToString() - - b, bucket = self._sess.run([self._filtered_data, self._bucket], - feed_dict={self._elem: elem_str}) - if bucket > input_extractor.BUCKET_UPPER_BOUND: - return - b = py_utils.NestedMap(b) - - # Flatten the batch. - flatten = b.FlattenItems() - if not flatten: - return - - num_boxes = b.bboxes_3d.shape[0] - - # For each box, get the pointcloud and write it as an example. - for bbox_id in range(num_boxes): - tf_example = self._ToTFExampleProto(b, bbox_id) - yield tf_example - - -def main(_): - beam_utils.BeamInit() - - if not FLAGS.output_file_pattern: - raise ValueError('Must provide an output_file_pattern') - - reader = beam.io.ReadFromTFRecord( - FLAGS.input_file_pattern, coder=beam.coders.ProtoCoder(tf.train.Example)) - - model_name = FLAGS.model_name - split = FLAGS.split - run_preprocessors = FLAGS.run_preprocessors - - with beam_utils.GetPipelineRoot() as root: - _ = ( - root - | 'Read' >> reader - | 'ToTFExample' >> beam.ParDo( - _ProcessShard(model_name, split, run_preprocessors)) - | 'Reshuffle' >> beam.Reshuffle() - | 'Write' >> beam.io.WriteToTFRecord( - FLAGS.output_file_pattern, - coder=beam.coders.ProtoCoder(tf.train.Example))) - - -if __name__ == '__main__': - app.run(main) diff --git a/lingvo/tasks/car/tools/export_kitti_detection.py b/lingvo/tasks/car/tools/export_kitti_detection.py deleted file mode 100644 index 84607497..00000000 --- a/lingvo/tasks/car/tools/export_kitti_detection.py +++ /dev/null @@ -1,232 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -r"""Read saved Decoder's outputs and convert to KITTI text format. - -First, obtain a KITTI camera calibration file. - -To export all detections from a single model: - -python export_kitti_detection.py \ ---decoder_path=/path/to/decoder_out_000103000 \ ---calib_file=/tmp/kitti_test_calibs.npz \ ---output_dir=/tmp/my-kitti-export-directory \ ---logtostderr - ---- OR --- - -Export combined detections selected from multiple models: - -python export_kitti_detection.py \ ---car_decoder_path=/path/to/car_decoder_out \ ---ped_decoder_path=/path/to/ped_decoder_out \ ---cyc_decoder_path=/path/to/cyc_decoder_out \ ---calib_file=/tmp/kitti_test_calibs.npz \ ---output_dir=/tmp/my-kitti-export-directory \ ---logtostderr -""" - -from absl import flags -from lingvo import compat as tf -from lingvo.core.ops import record_pb2 -from lingvo.tasks.car import kitti_metadata -from lingvo.tasks.car.tools import kitti_data -import numpy as np - - -FLAGS = flags.FLAGS -flags.DEFINE_string( - "decoder_path", None, "Paths to decoder file containing output " - "of decoder for everything. Either supply this argument or individual " - "decoder paths for cars, pedestrians and cyclists.") -flags.DEFINE_string( - "car_decoder_path", None, - "Paths to decoder file containing output of decoder for cars." - "Either supply plus cyclists and pedestrians or supply one " - "decoder for all labels.") -flags.DEFINE_string( - "ped_decoder_path", None, - "Paths to decoder file containing output of decoder for " - "pedestrians. Either supply plus cyclists and cars or " - "supply one decoder for all labels.") -flags.DEFINE_string( - "cyc_decoder_path", None, - "Paths to decoder file containing output of decoder for cyclist. " - "Either supply plus cars and pedestrians or supply one " - "decoder for all labels.") -flags.DEFINE_string( - "calib_file", None, - "Path to a npz file that contains all calibration matrices.") -flags.DEFINE_string("output_dir", None, "Place to write detections.") -flags.DEFINE_float("score_threshold", 0, "Ignore detections with lower score.") - - -def LoadCalibData(fname): - """Load and parse calibration data from NPZ file.""" - # If this throws an error, make sure the npz file was generated from - # the same version of python as this binary. - npz = np.load(fname) - scene_to_calib = {} - for idx, scene_id in enumerate(npz["scene_id"]): - tf.logging.info("Processing %s", scene_id) - raw_calib = {} - raw_calib["P0"] = npz["P0"][idx] - raw_calib["P1"] = npz["P1"][idx] - raw_calib["P2"] = npz["P2"][idx] - raw_calib["P3"] = npz["P3"][idx] - raw_calib["R0_rect"] = npz["R0_rect"][idx] - raw_calib["Tr_velo_to_cam"] = npz["Tr_velo_to_cam"][idx] - raw_calib["Tr_imu_to_velo"] = npz["Tr_imu_to_velo"][idx] - - calib = kitti_data.ParseCalibrationDict(raw_calib) - scene_to_calib[scene_id] = calib - return scene_to_calib - - -def ExtractNpContent(np_dict, calib): - """Parse saved np arrays and convert 3D bboxes to camera0 coordinates. - - Args: - np_dict: a dict of numpy arrays. - calib: a parsed calibration dictionary. - - Returns: - A tuple of 6 ndarrays: - - - location_camera: [N, 3]. [x, y, z] in camera0 coordinate. - - dimension_camera: [N, 3]. The [height, width, length] of objects. - - phi_camera: [N]. Rotation around y-axis in camera0 coodinate. - - bboxes_2d: [N, 4]. The corresponding 2D bboxes in the image coordinate. - - scores: [N]. Confidence scores for each box for the assigned class. - - class_ids: [N]. The class id assigned to each box. - """ - bboxes = np_dict["bboxes"] - scores = np_dict["scores"] - class_ids = np_dict["class_ids"] - bboxes_2d = np_dict["bboxes_2d"] - - # Transform from velodyne coordinates to camera coordinates. - velo_to_cam_transform = kitti_data.VeloToCameraTransformation(calib) - location_cam = np.zeros((len(bboxes), 3)) - dimension_cam = np.zeros((len(bboxes), 3)) - rotation_cam = np.zeros((len(bboxes), 1)) - for idx, bbox in enumerate(bboxes): - location_cam[idx, :], dimension_cam[idx, :], rotation_cam[idx, :] = ( - kitti_data.BBox3DToKITTIObject(bbox, velo_to_cam_transform)) - - return location_cam, dimension_cam, rotation_cam, bboxes_2d, scores, class_ids - - -_INCLUDED_KITTI_CLASS_NAMES = ["Car", "Pedestrian", "Cyclist"] - - -def ExportKITTIDetection(out_dir, source_id, location_cam, dimension_cam, - rotation_cam, bboxes_2d, scores, class_name, is_first): - """Write detections to a text file in KITTI format.""" - tf.logging.info("Exporting %s for %s" % (class_name, source_id)) - fname = out_dir + "/" + source_id + ".txt" - with tf.io.gfile.GFile(fname, "a") as fid: - # Ensure we always create a file even when there's no detection. - # TODO(shlens): Test whether this is actually necessary on the KITTI - # eval server. - if is_first: - fid.write("") - for location, dimension, ry, bbox_2d, score in zip( - location_cam, dimension_cam, rotation_cam, bboxes_2d, scores): - if score < FLAGS.score_threshold: - continue - # class_name, truncated(ignore), alpha(ignore), bbox2D x 4 - part1 = [class_name, -1, -1, -10] + list(bbox_2d) - # dimesion x 3, location x 3, rotation_y x 1, score x 1 - fill = tuple(part1 + list(dimension) + list(location) + [ry] + [score]) - kitti_format_string = ("%s %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf " - "%lf %lf %lf %lf") - kitti_line = kitti_format_string % fill - fid.write(kitti_line + "\n") - - -def main(argv): - if len(argv) > 1: - raise tf.app.UsageError("Too many command-line arguments.") - - if FLAGS.decoder_path: - assert not FLAGS.car_decoder_path and not FLAGS.ped_decoder_path \ - and not FLAGS.cyc_decoder_path, ("Either provide decoder_path or " - "individual decoders but not both.") - else: - assert FLAGS.car_decoder_path and FLAGS.ped_decoder_path and \ - FLAGS.cyc_decoder_path, ("No decoder_path specified. Please supply all " - "individual decoder_paths for labels.") - is_single_decoder_file = FLAGS.decoder_path is not None - - if is_single_decoder_file: - list_of_decoder_paths = [FLAGS.decoder_path] - else: - # Note the correspondence between _INCLUDED_KITTI_CLASS_NAMES ordering and - # this list. - list_of_decoder_paths = [ - FLAGS.car_decoder_path, FLAGS.ped_decoder_path, FLAGS.cyc_decoder_path - ] - - # A list of dictionaries mapping img ids to a dictionary of numpy tensors. - table_data = [] - - img_ids = [] - for table_path in list_of_decoder_paths: - img_id_dict = {} - for serialized in tf.io.tf_record_iterator(table_path): - record = record_pb2.Record() - record.ParseFromString(serialized) - img_id = str(tf.make_ndarray(record.fields["img_id"])) - img_ids.append(img_id) - np_dict = {k: tf.make_ndarray(v) for k, v in record.fields.items()} - img_id_dict[img_id] = np_dict - table_data.append(img_id_dict) - img_ids = list(set(img_ids)) - - if not tf.io.gfile.exists(FLAGS.output_dir): - tf.io.gfile.mkdir(FLAGS.output_dir) - - all_kitti_class_names = kitti_metadata.KITTIMetadata().ClassNames() - calib_data = LoadCalibData(tf.io.gfile.GFile(FLAGS.calib_file, "rb")) - count = 0 - for img_id in img_ids: - # Ignore padded samples where the img_ids are empty. - if not img_id: - continue - for table_index, img_id_dict in enumerate(table_data): - if img_id in img_id_dict: - np_dict = img_id_dict[img_id] - - (location_cam, dimension_cam, rotation_cam, bboxes_2d, scores, - class_ids) = ExtractNpContent(np_dict, calib_data[img_id + ".txt"]) - if is_single_decoder_file: - valid_labels = _INCLUDED_KITTI_CLASS_NAMES - else: - valid_labels = [_INCLUDED_KITTI_CLASS_NAMES[table_index]] - is_first = table_index == 0 - for class_name in valid_labels: - class_mask = (class_ids == all_kitti_class_names.index(class_name)) - ExportKITTIDetection(FLAGS.output_dir, img_id, - location_cam[class_mask], - dimension_cam[class_mask], - rotation_cam[class_mask], bboxes_2d[class_mask], - scores[class_mask], class_name, is_first) - count += 1 - tf.logging.info("Total example exported: %d", count) - - -if __name__ == "__main__": - tf.app.run(main) diff --git a/lingvo/tasks/car/tools/kitti_data.py b/lingvo/tasks/car/tools/kitti_data.py deleted file mode 100644 index 40c2d444..00000000 --- a/lingvo/tasks/car/tools/kitti_data.py +++ /dev/null @@ -1,364 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Library for parsing KITTI raw data.""" - -from lingvo import compat as tf -import numpy as np - - -def LoadVeloBinFile(filepath): - """Reads and parse raw KITTI velodyne binary file. - - Args: - filepath: Path to a raw KITTI velodyne binary file. - - Returns: - A dictionary with keys xyz and reflectance containing numpy arrays. - """ - with tf.io.gfile.GFile(filepath, 'rb') as f: - scan = np.frombuffer(f.read(), dtype=np.float32).reshape((-1, 4)) - xyz = scan[:, :3] - reflectance = scan[:, 3:] - return { - 'xyz': xyz, - 'reflectance': reflectance, - } - - -def LoadLabelFile(filepath): - """Reads and parse raw KITTI label file. - - The ordering of the arrays for bbox, dimensions, and location follows the - order in the table below. We refer to the length (dx), width (dy), height (dz) - for clarity. - - Each line in the label contains (per KITTI documentation): - - +--------+------------+------------------------------------------------------+ - | Values | Name | Description | - +========+============+======================================================+ - | 1 | type | Describes the type of object: 'Car', 'Van', 'Truck', | - | | | 'Pedestrian', 'Person_sitting', 'Cyclist', 'Tram', | - | | | 'Misc' or 'DontCare' | - +--------+------------+------------------------------------------------------+ - | 1 | truncated | Float from 0 (non-truncated) to 1 (truncated), where | - | | | truncated refers to the object leaving image | - | | | boundaries. | - +--------+------------+------------------------------------------------------+ - | 1 | occluded | Integer (0,1,2,3) indicating occlusion state: | - | | | 0 = fully visible, 1 = partly occluded | - | | | 2 = largely occluded, 3 = unknown | - +--------+------------+------------------------------------------------------+ - | 1 | alpha | Observation angle of object, ranging [-pi..pi] | - +--------+------------+------------------------------------------------------+ - | 4 | bbox | 2D bounding box of object in the image (0-based | - | | | index): left, top, right, bottom pixel coordinates. | - +--------+------------+------------------------------------------------------+ - | 3 | dimensions | 3D object dimensions: height, width, length (meters) | - +--------+------------+------------------------------------------------------+ - | 3 | location | 3D object location x,y,z in camera coordinates | - | | | (in meters) | - +--------+------------+------------------------------------------------------+ - | 1 | rotation_y | Rotation ry around Y-axis in camera coordinates | - | | | [-pi..pi] | - +--------+------------+------------------------------------------------------+ - | 1 | score | Only for results: Float, indicating confidence in | - | | | detection, needed for p/r curves, higher is better. | - +--------+------------+------------------------------------------------------+ - - Args: - filepath: Path to a raw KITTI label file. - - Returns: - A list of dictionary with keys corresponding to the name column above. type, - truncated, occluded, alpha, bbox, dimensions, location, rotation_y, score. - Note that the order of the floats in bbox, dimensions, and location - correspond to that in the doc-string above. - """ - objects = [] - with tf.io.gfile.GFile(filepath, 'r') as f: - for line in f: - line = line.strip() - if not line: # Skip empty lines - continue - line_splits = line.split(' ') - if len(line_splits) not in [15, 16]: - raise ValueError( - 'Found {} tokens in Line: "{}". Expects only 15/16 token'.format( - len(line_splits), line)) - - # If score does not exist, we append a -1 to indicate so. - if len(line_splits) == 15: - line_splits.append(-1.) - - (obj_type, truncated, occluded, alpha, - bbox_left, bbox_top, bbox_right, bbox_bottom, - height, width, length, - cam_x, cam_y, cam_z, - rotation_y, score) = line_splits # pyformat: disable - - obj = { - 'type': obj_type, - 'truncated': float(truncated), - 'occluded': int(occluded), - 'alpha': float(alpha), - 'bbox': [ - float(x) for x in [bbox_left, bbox_top, bbox_right, bbox_bottom] - ], - 'dimensions': [float(x) for x in [height, width, length]], - 'location': [float(x) for x in [cam_x, cam_y, cam_z]], - 'rotation_y': float(rotation_y), - 'score': float(score), - } - _ValidateLabeledObject(obj) - objects.append(obj) - - return objects - - -def _ValidateLabeledObject(obj): - """Validate that obj has expected values.""" - - if obj['type'] not in [ - 'Car', 'Van', 'Truck', 'Pedestrian', 'Person_sitting', 'Cyclist', 'Tram', - 'Misc', 'DontCare' - ]: - raise ValueError('Invalid type: %s' % obj['type']) - - if not ((obj['truncated'] == -1.0) or - (obj['truncated'] >= 0.0 and obj['truncated'] <= 1.0)): - raise ValueError('Invalid truncated value: %s' % obj['truncated']) - - if obj['occluded'] not in [-1, 0, 1, 2, 3]: - raise ValueError('Invalid occluded value: %s' % obj['occluded']) - - if not (obj['alpha'] == -10. or - (obj['alpha'] >= -np.pi and obj['alpha'] <= np.pi)): - raise ValueError('Invalid alpha value: %s' % obj['alpha']) - - if not (obj['rotation_y'] == -10. or - (obj['rotation_y'] >= -np.pi and obj['rotation_y'] <= np.pi)): - raise ValueError('Invalid rotation_y value: %s' % obj['rotation_y']) - - return obj - - -def ParseCalibrationDict(raw_calib): - """Parse transformation matrices in a raw KITTI calibration dictionary. - - Per the KITTI documentation: - - All matrices are stored row-major, i.e., the first values correspond - to the first row. R0_rect contains a 3x3 matrix which you need to extend to - a 4x4 matrix by adding a 1 as the bottom-right element and 0's elsewhere. - Tr_xxx is a 3x4 matrix (R|t), which you need to extend to a 4x4 matrix - in the same way. - - IMPORTANT: The coordinates in the camera coordinate system can be projected in - the image by using the 3x4 projection matrix in the calib folder, where for - the left color camera for which the images are provided, P2 must be used. - - Args: - raw_calib: A dictionary of raw KITTI calibration values with keys P0, P1, - P2, P3, R0_rect, Tr_imu_to_velo, and Tr_velo_to_cam containing flattened - matrices of appropriate size. - - Returns: - A dictionary with keys P0, P1, P2, P3, R0_rect, Tr_imu_to_velo, - and Tr_velo_to_cam containing reshaped and extended matrices. - """ - # The projection matrices are 3x4 matrices. - calib = {} - calib['P0'] = raw_calib['P0'].reshape([3, 4]) - calib['P1'] = raw_calib['P1'].reshape([3, 4]) - calib['P2'] = raw_calib['P2'].reshape([3, 4]) - calib['P3'] = raw_calib['P3'].reshape([3, 4]) - - # R0_rect contains a 3x3 matrix which you need to extend to a 4x4 matrix by - # adding a 1 as the bottom-right element and 0's elsewhere. - extended_r0_rect = np.eye(4) - extended_r0_rect[:3, :3] = raw_calib['R0_rect'].reshape([3, 3]) - calib['R0_rect'] = extended_r0_rect - - # Tr_xxx is a 3x4 matrix (R|t), which you need to extend to a 4x4 matrix - # in the same way! - extended_tr_imu_to_velo = np.eye(4) - extended_tr_imu_to_velo[:3, :4] = raw_calib['Tr_imu_to_velo'].reshape([3, 4]) - calib['Tr_imu_to_velo'] = extended_tr_imu_to_velo - - extended_tr_velo_to_cam = np.eye(4) - extended_tr_velo_to_cam[:3, :4] = raw_calib['Tr_velo_to_cam'].reshape([3, 4]) - calib['Tr_velo_to_cam'] = extended_tr_velo_to_cam - - return calib - - -def LoadCalibrationFile(filepath): - """Read and parse a raw KITTI calibration file. - - Args: - filepath: Path to a raw KITTI calibration file. - - Returns: - A dictionary with keys P0, P1, P2, P3, R0_rect, Tr_imu_to_velo, - and Tr_velo_to_cam containing reshaped and extended transformation - matrices. - """ - raw_calib = {} - with tf.io.gfile.GFile(filepath, 'r') as f: - for line in f: - line = line.strip() - if not line: # Skip empty lines - continue - key, value = line.split(':', 1) - raw_calib[key] = np.array([float(x) for x in value.split()]) - return ParseCalibrationDict(raw_calib) - - -def VeloToImagePlaneTransformation(calib): - """Compute the transformation matrix from velo xyz to image plane xy. - - Per the KITTI documentation, to project a point from Velodyne coordinates into - the left color image, you can use this formula: - - x = P2 * R0_rect * Tr_velo_to_cam * y - - After applying the transformation, you will need to divide the by the last - coordinate to recover the 2D pixel locations. - - Args: - calib: A calibration dictionary returned by LoadCalibrationFile. - - Returns: - A numpy 3x4 transformation matrix. - """ - return np.dot(calib['P2'], np.dot(calib['R0_rect'], calib['Tr_velo_to_cam'])) - - -def VeloToCameraTransformation(calib): - """Compute the transformation matrix from velo xyz to camera xyz. - - Per the KITTI documentation, to project a point from Velodyne coordinates into - the left color image, you can use this formula: - - x = P2 * R0_rect * Tr_velo_to_cam * y - - NOTE: The above formula further projects the xyz point to the image plane - using P2, which we do not apply in this function since we are working with - xyz (3D coordinates). - - Args: - calib: A calibration dictionary returned by LoadCalibrationFile. - - Returns: - A numpy 4x4 transformation matrix. - """ - return np.dot(calib['R0_rect'], calib['Tr_velo_to_cam']) - - -def CameraToVeloTransformation(calib): - """Compute the transformation matrix from camera to velo. - - This is the inverse transformation of CameraToVeloTransformation. - - Args: - calib: A calibartion dictionary returned by LoadCalibrationFile. - - Returns: - A numpy 4x4 transformation matrix. - """ - return np.linalg.pinv(VeloToCameraTransformation(calib)) - - -def AnnotateKITTIObjectsWithBBox3D(objects, calib): - """Add our canonical bboxes 3d format to KITTI objects. - - The annotated bboxes 3d are in the velodyne coordinate frame. - - Args: - objects: A list of KITTI objects returned by LoadLabelFile. - calib: A calibartion dictionary returned by LoadCalibrationFile. - - Returns: - The original list of KITTI objects, where each object has new keys - 'has_3d_info' indicating if the object has valid 3D bounding box data, and - 'bbox3d' which corresponds to our canonical bboxes 3d format. - """ - - # All objects will share the same transformation matrix, which we compute - # once here. - transformation_matrix = CameraToVeloTransformation(calib) - for obj in objects: - obj['bbox3d'] = _KITTIObjectToBBox3D(obj, transformation_matrix) - obj['has_3d_info'] = _KITTIObjectHas3DInfo(obj) - return objects - - -def _KITTIObjectHas3DInfo(obj): - """Check whether KITTI object has valid 3D bounding box information.""" - height, width, length = obj['dimensions'] - # KITTI raw data has -1 for all 3 dimensions when no 3D box info is present. - return not (width == -1 or length == -1 or height == -1) - - -def _KITTIObjectToBBox3D(obj, cam_to_velo_transform): - """Convert one object given the transformation matrix.""" - height, width, length = obj['dimensions'] - - # Avoid transforming objects with invalid boxes. - if not _KITTIObjectHas3DInfo(obj): - return [-1000, -1000, -1000, -1, -1, -1, -10] - - velo_xyz = np.dot(cam_to_velo_transform, np.asarray(obj['location'] + [1.])) - x, y, z = velo_xyz.tolist()[:3] - - # In the raw data, the z coordinate is at the bottom of the object, we need - # to reposition z so that it's at the center of the object. - z += height / 2. - - # Our velodyne bbox rotation goes the other direction and is rotated by - # -np.pi/2. See http://www.cvlibs.net/datasets/kitti/setup.php. - rot = -obj['rotation_y'] - rot -= np.pi / 2. - bbox3d = [x, y, z] + [length, width, height] + [rot] - - return bbox3d - - -def BBox3DToKITTIObject(bbox3d, velo_to_cam_transform): - """Convert one bbox3d into KITTI's location, dimension, and rotation_y.""" - x, y, z, length, width, height, rot = bbox3d - - # Avoid transforming objects with invalid boxes. See _KITTIObjectHas3DInfo. - if width == -1 or length == -1 or height == -1: - return [-1000, -1000, -1000], [-1, -1, -1], -10 - - # Convert our velodyne bbox rotation back to camera. Reverse the direction and - # rotate by np.pi/2. See http://www.cvlibs.net/datasets/kitti/setup.php. - rotation_y = rot + np.pi / 2. - rotation_y = -rotation_y - rotation_y = np.mod(rotation_y, 2 * np.pi) - rotation_y = np.where(rotation_y >= np.pi, rotation_y - 2 * np.pi, rotation_y) - - # Reposition z so that it is at the bottom of the object. - if height > 0: - z -= height / 2. - - camera_xyz = np.dot(velo_to_cam_transform, np.asarray([x, y, z, 1.])) - location = camera_xyz.tolist()[:3] - dimensions = height, width, length - - return location, dimensions, rotation_y diff --git a/lingvo/tasks/car/tools/kitti_data_test.py b/lingvo/tasks/car/tools/kitti_data_test.py deleted file mode 100644 index c5f4ff86..00000000 --- a/lingvo/tasks/car/tools/kitti_data_test.py +++ /dev/null @@ -1,139 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for kitti_data.""" - -from lingvo import compat as tf -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tasks.car import geometry -from lingvo.tasks.car.tools import kitti_data -import numpy as np - - -class KittiDataTest(test_utils.TestCase): - - def setUp(self): - super().setUp() - self._label_file = test_helper.test_src_dir_path( - 'tasks/car/testdata/kitti_raw_label_testdata.txt') - self._calib_file = test_helper.test_src_dir_path( - 'tasks/car/testdata/kitti_raw_calib_testdata.txt') - - def testLoadLabelFile(self): - objects = kitti_data.LoadLabelFile(self._label_file) - self.assertEqual(len(objects), 7) - self.assertEqual(objects[1]['type'], 'Car') - self.assertEqual(objects[1]['truncated'], 0.0) - self.assertEqual(objects[1]['occluded'], 0) - self.assertEqual(objects[1]['alpha'], 1.85) - self.assertAllClose(objects[1]['bbox'], [387.63, 181.54, 423.81, 203.12]) - self.assertAllClose(objects[1]['dimensions'], [1.67, 1.87, 3.69]) - self.assertAllClose(objects[1]['location'], [-16.53, 2.39, 58.49]) - self.assertEqual(objects[1]['rotation_y'], 1.57) - self.assertEqual(objects[1]['score'], -1.) - - def testLoadCalibrationFile(self): - calib = kitti_data.LoadCalibrationFile(self._calib_file) - - self.assertAllEqual(calib['P0'].shape, [3, 4]) - self.assertAllEqual(calib['P1'].shape, [3, 4]) - self.assertAllEqual(calib['P2'].shape, [3, 4]) - self.assertAllEqual(calib['P3'].shape, [3, 4]) - - self.assertAllEqual(calib['R0_rect'].shape, [4, 4]) - self.assertAllEqual(calib['R0_rect'][3, :], [0., 0., 0., 1.]) - self.assertAllEqual(calib['R0_rect'][:, 3], [0., 0., 0., 1.]) - - self.assertAllEqual(calib['Tr_imu_to_velo'].shape, [4, 4]) - self.assertAllEqual(calib['Tr_imu_to_velo'][3, :], [0., 0., 0., 1.]) - self.assertAllEqual(calib['Tr_velo_to_cam'].shape, [4, 4]) - self.assertAllEqual(calib['Tr_velo_to_cam'][3, :], [0., 0., 0., 1.]) - - def testVeloToCamAndCamToVeloAreInverses(self): - calib = kitti_data.LoadCalibrationFile(self._calib_file) - velo_to_cam = kitti_data.VeloToCameraTransformation(calib) - cam_to_velo = kitti_data.CameraToVeloTransformation(calib) - self.assertAllClose(cam_to_velo.dot(velo_to_cam), np.eye(4)) - - def testAnnotateKITTIObjectsWithBBox3D(self): - objects = kitti_data.LoadLabelFile(self._label_file) - calib = kitti_data.LoadCalibrationFile(self._calib_file) - objects = kitti_data.AnnotateKITTIObjectsWithBBox3D(objects, calib) - for obj in objects: - self.assertEqual(len(obj['bbox3d']), 7) - - # atol=0.01 corresponds to a 1cm tolerance. - self.assertAllClose( - objects[0]['bbox3d'][:3], [69.72, -0.45, 0.58], atol=0.01) - self.assertAllClose(objects[0]['bbox3d'][3:], - [12.34, 2.63, 2.85, -0.01079633]) - self.assertAllEqual(objects[0]['has_3d_info'], True) - - # no 3D data - self.assertAllEqual(objects[3]['has_3d_info'], False) - - def testKITTIObjToBBoxAndInverse(self): - objects = kitti_data.LoadLabelFile(self._label_file) - calib = kitti_data.LoadCalibrationFile(self._calib_file) - for obj in objects: - bbox3d = kitti_data._KITTIObjectToBBox3D( - obj, kitti_data.CameraToVeloTransformation(calib)) - location, dimensions, rotation_y = kitti_data.BBox3DToKITTIObject( - bbox3d, kitti_data.VeloToCameraTransformation(calib)) - self.assertAllClose(obj['location'], location) - self.assertAllClose(obj['dimensions'], dimensions) - self.assertAllClose(obj['rotation_y'], rotation_y) - - def testVeloToImagePlaneTransformation(self): - objects = kitti_data.LoadLabelFile(self._label_file) - calib = kitti_data.LoadCalibrationFile(self._calib_file) - - # Only apply to object 0. - obj = objects[0] - bbox3d = kitti_data._KITTIObjectToBBox3D( - obj, kitti_data.CameraToVeloTransformation(calib)) - - # Convert to corners in our canonical space. - corners = geometry.BBoxCorners(tf.constant([[bbox3d]], dtype=tf.float32)) - with self.session(): - corners_np = self.evaluate(corners) - corners_np = corners_np.reshape([8, 3]) - - # Add homogenous coordinates. - corners_np = np.concatenate([corners_np, np.ones((8, 1))], axis=-1) - - # Apply the velo to image plane transformation. - velo_to_img = kitti_data.VeloToImagePlaneTransformation(calib) - corners_np = np.dot(corners_np, velo_to_img.T) - - # Divide by the last coordinate to recover pixel locations. - corners_np[:, 0] /= corners_np[:, 2] - corners_np[:, 1] /= corners_np[:, 2] - - # Obtain 2D bbox. - min_x = np.min(corners_np[:, 0]) - max_x = np.max(corners_np[:, 0]) - min_y = np.min(corners_np[:, 1]) - max_y = np.max(corners_np[:, 1]) - bbox = [min_x, min_y, max_x, max_y] # left, top, right, bottom. - - # This should correspond to the GT bbox in obj['bbox']. - # We use atol=0.1 here since they should close to the nearest pixel. - self.assertAllClose(bbox, obj['bbox'], atol=0.1) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/tools/kitti_exporter.py b/lingvo/tasks/car/tools/kitti_exporter.py deleted file mode 100644 index 62c0249b..00000000 --- a/lingvo/tasks/car/tools/kitti_exporter.py +++ /dev/null @@ -1,265 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Create TFRecords files from KITTI raw data. - -Parses KITTI raw data with different splits, indicated with split files. -A split file is a text file that specifies frame names to be included in the -split, with one name per line. Splits with 'test' in the filename use testing -data, while other splits use training data. This program expects KITTI raw data -in the following directory structure: - - kitti_object/ - training/ # Contains KITTI raw train data - label2/ - velodyne/ - calib/ - image_2/ - testing/ # Contains KITTI raw test data - velodyne/ - calib/ - image_2/ - splits/ # Contains split files identifying frame names in the split. - split_name.txt - -Outputs examples in TFRecords files correspond to KITTI frames with the -following format: - - # frame information - image/source_id: unique frame name e.g '000000', '000010' - - # 2D image data - image/encoded: PNG encoded string - image/height: image height - image/width: image width - image/format: 'PNG' - - # 3D velodyne pointcloud data (variable P points per frame) - pointcloud/xyz = point positions (P x 3 tensor). - pointcloud/reflectance: point reflectances (P x 1 tensor). - - # Object level data (variable N objects per frame) - object/image/bbox/xmin: min X pixel location in raw image (N x 1 tensor). - - object/image/bbox/xmax: max X pixel location in raw image (N x 1 tensor). - - object/image/bbox/ymin: min Y pixel location in raw image (N x 1 tensor). - - object/image/bbox/ymax: max Y pixel location in raw image (N x 1 tensor). - - object/label: one of {'Car', 'Pedestrian', 'Cyclist'} identifying object - class (N x 1 tensor). - - object/has_3d_info: 1 if object has valid 3D info else 0 (N x 1 tensor). - - object/occlusion: int in {0, 1, 2, 3} of occlusion state (N x 1 tensor). - - object/truncation: float in 0 (non-truncated) to 1 (truncated) (N x 1 tensor). - - object/velo/bbox/xyz: 3D bbox locations in velo frame (N x 3 tensor). - - object/velo/bbox/dim_xyz: length (dx), width (dy), height (dz) indicating - object dimensions (N x 3 tensor). - - object/velo/bbox/phi: bbox rotation in velo frame (N x 1 tensor). - - # Transformation matrices - transform/velo_to_image_plane: 3x4 matrix from velo xyz to image plane xy. - After multiplication, you need to divide by last coordinate to recover 2D - pixel locations. - - transform/velo_to_camera: 4x4 matrix from velo xyz to camera xyz. - - transform/camera_to_velo 4x4 matrix from camera xyz to velo xyz. -""" - -import contextlib -import io -import os - -from absl import app -from absl import flags -from absl import logging - -from lingvo import compat as tf -from lingvo.tasks.car.tools import kitti_data -import numpy as np -from PIL import Image - -FLAGS = flags.FLAGS - -flags.DEFINE_string('kitti_object_dir', None, - 'Path to a kitti object directory.') -flags.DEFINE_string('split', None, - 'Name of the split file to be used for parsing.') -flags.DEFINE_string('tfrecord_path', None, 'Output tfrecord path.') -flags.DEFINE_integer( - 'num_shards', 1, 'Number of output shards (between 1 and 99999). Files' - 'named {tfrecord_path}-{shard_num}-of-{total_shards}.') - - -def _ReadObjectDataset(root_dir, frame_names): - """Reads and parses KITTI dataset files into a list of TFExample protos.""" - examples = [] - - total_frames = len(frame_names) - for frame_index, frame_name in enumerate(frame_names): - image_file_path = os.path.join(root_dir, 'image_2', frame_name + '.png') - calib_file_path = os.path.join(root_dir, 'calib', frame_name + '.txt') - velo_file_path = os.path.join(root_dir, 'velodyne', frame_name + '.bin') - label_file_path = os.path.join(root_dir, 'label_2', frame_name + '.txt') - - example = tf.train.Example() - feature = example.features.feature - - # frame information - feature['image/source_id'].bytes_list.value[:] = [frame_name] - - # 2D image data - encoded_image = tf.io.gfile.GFile(image_file_path).read() - feature['image/encoded'].bytes_list.value[:] = [encoded_image] - image = np.array(Image.open(io.BytesIO(encoded_image))) - assert image.ndim == 3 - assert image.shape[2] == 3 - image_width = image.shape[1] - image_height = image.shape[0] - feature['image/width'].int64_list.value[:] = [image_width] - feature['image/height'].int64_list.value[:] = [image_height] - feature['image/format'].bytes_list.value[:] = ['PNG'] - - # 3D velodyne point data - velo_dict = kitti_data.LoadVeloBinFile(velo_file_path) - point_list = velo_dict['xyz'].ravel().tolist() - feature['pointcloud/xyz'].float_list.value[:] = point_list - reflectance_list = velo_dict['reflectance'].ravel().tolist() - feature['pointcloud/reflectance'].float_list.value[:] = reflectance_list - - # Object data - calib_dict = kitti_data.LoadCalibrationFile(calib_file_path) - if tf.io.gfile.exists(label_file_path): - # Load object labels for training data - object_dicts = kitti_data.LoadLabelFile(label_file_path) - object_dicts = kitti_data.AnnotateKITTIObjectsWithBBox3D( - object_dicts, calib_dict) - else: - # No object labels for test data - object_dicts = {} - - num_objects = len(object_dicts) - xmins = [None] * num_objects - xmaxs = [None] * num_objects - ymins = [None] * num_objects - ymaxs = [None] * num_objects - labels = [None] * num_objects - has_3d_infos = [None] * num_objects - - # 3D info - occlusions = [None] * num_objects - truncations = [None] * num_objects - xyzs = [None] * num_objects - dim_xyzs = [None] * num_objects - phis = [None] * num_objects - - for object_index, object_dict in enumerate(object_dicts): - xmins[object_index] = object_dict['bbox'][0] - xmaxs[object_index] = object_dict['bbox'][2] - ymins[object_index] = object_dict['bbox'][1] - ymaxs[object_index] = object_dict['bbox'][3] - labels[object_index] = object_dict['type'] - has_3d_infos[object_index] = 1 if object_dict['has_3d_info'] else 0 - occlusions[object_index] = object_dict['occluded'] - truncations[object_index] = object_dict['truncated'] - xyzs[object_index] = object_dict['bbox3d'][:3] - dim_xyzs[object_index] = object_dict['bbox3d'][3:6] - phis[object_index] = object_dict['bbox3d'][6] - - feature['object/image/bbox/xmin'].float_list.value[:] = xmins - feature['object/image/bbox/xmax'].float_list.value[:] = xmaxs - feature['object/image/bbox/ymin'].float_list.value[:] = ymins - feature['object/image/bbox/ymax'].float_list.value[:] = ymaxs - feature['object/label'].bytes_list.value[:] = labels - feature['object/has_3d_info'].int64_list.value[:] = has_3d_infos - feature['object/occlusion'].int64_list.value[:] = occlusions - feature['object/truncation'].float_list.value[:] = truncations - xyzs = np.array(xyzs).ravel().tolist() - feature['object/velo/bbox/xyz'].float_list.value[:] = xyzs - dim_xyzs = np.array(dim_xyzs).ravel().tolist() - feature['object/velo/bbox/dim_xyz'].float_list.value[:] = dim_xyzs - feature['object/velo/bbox/phi'].float_list.value[:] = phis - - # Transformation matrices - velo_to_image_plane = kitti_data.VeloToImagePlaneTransformation(calib_dict) - feature['transform/velo_to_image_plane'].float_list.value[:] = ( - velo_to_image_plane.ravel().tolist()) - velo_to_camera = kitti_data.VeloToCameraTransformation(calib_dict) - feature['transform/velo_to_camera'].float_list.value[:] = ( - velo_to_camera.ravel().tolist()) - cam_to_velo = kitti_data.CameraToVeloTransformation(calib_dict) - feature['transform/camera_to_velo'].float_list.value[:] = ( - cam_to_velo.ravel().tolist()) - - examples.append(example) - if frame_index % 100 == 0: - logging.info('Processed frame %d of %d.', frame_index, total_frames) - - return examples - - -def _ExportObjectDatasetToTFRecord(root_dir, split_file, tfrecord_path, - num_shards): - """Exports KITTI dataset files to TFRecord files.""" - if num_shards <= 0: - raise ValueError('TFRecord dataset must have at least one shard.') - - logging.info('Reading frame names from split_file %s.', split_file) - frame_names = [line.rstrip('\n') for line in tf.io.gfile.GFile(split_file)] - logging.info('Reading object dataset with %d frames.', len(frame_names)) - dataset = _ReadObjectDataset(root_dir, frame_names) - logging.info('Saving object dataset at %s with %d shards.', tfrecord_path, - num_shards) - - tf_record_output_filenames = [ - '{}-{:05d}-of-{:05d}'.format(tfrecord_path, index, num_shards) - for index in range(num_shards) - ] - - with contextlib.ExitStack() as exit_stack: - tf_record_writers = [ - exit_stack.enter_context(tf.io.TFRecordWriter(filename)) - for filename in tf_record_output_filenames - ] - total_examples = len(dataset) - for example_index, example in enumerate(dataset): - output_shard_index = example_index % num_shards - serialized_example = example.SerializeToString() - tf_record_writers[output_shard_index].write(serialized_example) - if example_index % 100 == 0: - logging.info('Wrote frame %d of %d.', example_index, total_examples) - - -def main(unused_argv): - split_type = 'testing' if 'test' in FLAGS.split else 'training' - root_dir = os.path.join(FLAGS.kitti_object_dir, split_type) - split_file = os.path.join(FLAGS.kitti_object_dir, 'splits', - '{}.txt'.format(FLAGS.split)) - _ExportObjectDatasetToTFRecord(root_dir, split_file, FLAGS.tfrecord_path, - FLAGS.num_shards) - - -if __name__ == '__main__': - flags.mark_flag_as_required('kitti_object_dir') - flags.mark_flag_as_required('split') - flags.mark_flag_as_required('tfrecord_path') - app.run(main) diff --git a/lingvo/tasks/car/transform_util.py b/lingvo/tasks/car/transform_util.py deleted file mode 100644 index 8d45abe3..00000000 --- a/lingvo/tasks/car/transform_util.py +++ /dev/null @@ -1,188 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Utilities for performing 3D transformations on points.""" - -import copy - -import numpy as np - - -class Box2D: - """A representation of a 2D rotated bounding box. - - Box2D is based on conventions for 3D coordinate systems where y-x are - switched. - """ - - def __init__(self, x, y, width, length, angle): - """Initialize a Box2D. - - Args: - x: center x coordinate of bounding box. - y: center y coordinate of bounding box. - width: Width of box in the x-dimension. - length: Length of box in the y-dimension. - angle: Angle in radians wrt to the direction of the longer axis. - """ - self._center = np.array([x, y]) - self._width = width - self._length = length - self._angle = angle - self._angle_v = np.array([np.cos(angle), np.sin(angle)]) - - offset = self._angle_v * (length / 2.) - self._start = self._center - offset - self._end = self._center + offset - # Compute the four corners of the rotated box. - self._corners = self._ComputeCorners() - - @property - def corners(self): - """Returns a [4, 2] numpy matrix containing the four corner points.""" - return self._corners - - def _ComputeCorners(self): - """Compute the four corners of the bounding box.""" - if self._length > 0: - perp_unit = np.array([-self._angle_v[1], self._angle_v[0]]) - else: - perp_unit = np.array([0., 0.]) - w2 = perp_unit * (self._width / 2.) - - corner_1 = np.array([self._start[0] + w2[0], self._start[1] + w2[1]]) - corner_2 = np.array([self._end[0] + w2[0], self._end[1] + w2[1]]) - corner_3 = np.array([self._end[0] - w2[0], self._end[1] - w2[1]]) - corner_4 = np.array([self._start[0] - w2[0], self._start[1] - w2[1]]) - return np.array([corner_1, corner_2, corner_3, corner_4]) - - def Extrema(self): - """Returns the extrema of the bounding box.""" - ymin = np.min(self._corners[:, 1]) - xmin = np.min(self._corners[:, 0]) - ymax = np.max(self._corners[:, 1]) - xmax = np.max(self._corners[:, 0]) - return ymin, xmin, ymax, xmax - - def Apply(self, transform): - """Apply `transform` to the current box and return a new box.""" - # Transform corner points using the transform matrix. - new_corners = [] - - # TODO(vrv): vectorize - for corner in self._corners: - # Extend to z, w. - corner = np.concatenate([corner, np.array([0., 1.])], axis=0) - # Apply the transform. - corner_adjusted = np.matmul(transform, corner) - new_corners.append(corner_adjusted[0:2]) - - new_corners = np.stack(new_corners, axis=0) - - # Compute the new center. - ymin = np.min(new_corners[:, 1]) - xmin = np.min(new_corners[:, 0]) - ymax = np.max(new_corners[:, 1]) - xmax = np.max(new_corners[:, 0]) - center_x = xmin + (xmax - xmin) / 2. - center_y = ymin + (ymax - ymin) / 2. - - # Compute the new width and length. - scale_transform = CopyTransform(transform)[0:2, 0:2] - w_l = np.array([self._width, self._length]) - new_wl = np.abs(np.matmul(scale_transform, w_l)) - new_width = new_wl[0] - new_length = new_wl[1] - - # Compute the transformed heading. - transformed_heading = TransformHeading(transform, self._angle) - return Box2D(center_x, center_y, new_width, new_length, transformed_heading) - - def AsNumpy(self): - """Return the 5DOF (xywhh) representation as a numpy array.""" - return np.array([ - self._center[0], self._center[1], self._width, self._length, self._angle - ]) - - -def TransformHeading(transform, heading): - """Compute 'heading' given transform. - - The heading provided as input is assumed to be in the original coordinate - space. When the coordinate space undergoes a transformation (e.g., with - CarToImageTransform), the heading in the new coordinate space must be - recomputed. - - We compute this by deriving the formula for the angle of transformed unit - vector defined by 'heading'. - - Args: - transform: 4x4 numpy matrix used to convert from car to image coordinates. - heading: Floating point scalar heading. - - Returns: - Heading in the transformed coordinate system. - """ - x1, y1 = np.cos(heading), np.sin(heading) - - # Transform the unit ray. - unit_ray = np.array([x1, y1, 0.0, 1.0]) - transform_no_shift = CopyTransform(transform) - transform_no_shift[0, 3] = 0 - transform_no_shift[1, 3] = 0 - transformed_ray = np.matmul(transform_no_shift, unit_ray) - x2, y2 = transformed_ray[0:2] - - # Use arctan2 to compute the new rotation angle; note that arctan2 takes 'y' - # and then 'x'. - new_heading = np.arctan2(y2, x2) - return new_heading - - -def TransformPoint(transform, x, y, z): - """Transform an x, y, z point given the 4x4 `transform`.""" - result = np.matmul(transform, np.array([x, y, z, 1.])) - return result[0], result[1], result[2] - - -def CopyTransform(transform): - """Return a copy of `transform`.""" - return copy.copy(transform) - - -def MakeCarToImageTransform(pixels_per_meter, image_ref_x, image_ref_y, - flip_axes): - """Creates a 4x4 numpy matrix for car to top down image coordinates. - - Args: - pixels_per_meter: Number of pixels that represent a meter in top down view. - image_ref_x: Number of pixels to shift the car in the x direction. - image_ref_y: Number of pixels to shift the car in the y direction. - flip_axes: Boolean indicating whether the x/y axes should be flipped[ during - the transform. - - Returns: - A 4x4 matrix transform. - """ - ppm1 = 0. if flip_axes else pixels_per_meter - ppm2 = -pixels_per_meter if flip_axes else 0. - # pyformat: disable - car_to_image_transform = np.array([ - [ppm1, ppm2, 0., image_ref_x], - [ppm2, ppm1, 0., image_ref_y], - [0., 0., 1., 0.], - [0., 0., 0., 1.]]) - # pyformat: enable - return car_to_image_transform diff --git a/lingvo/tasks/car/transform_util_test.py b/lingvo/tasks/car/transform_util_test.py deleted file mode 100644 index f2053719..00000000 --- a/lingvo/tasks/car/transform_util_test.py +++ /dev/null @@ -1,135 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for transform_util.""" - -import lingvo.compat as tf -from lingvo.tasks.car import transform_util -import numpy as np - - -class TransformUtilTest(tf.test.TestCase): - """Tests for methods in transform_util.""" - - def testMakeCarToImageTransformFlipAxesTrue(self): - transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=10.0, image_ref_x=250, image_ref_y=750, flip_axes=True) - # pyformat: disable - self.assertAllClose( - np.matrix([[0., -10., 0., 250.,], - [-10., 0., 0., 750.,], - [0., 0., 1., 0.,], - [0., 0., 0., 1.,]]), - transform) - # pyformat: enable - - def testMakeCarToImageTransformFlipAxesFalse(self): - transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=10.0, - image_ref_x=250, - image_ref_y=750, - flip_axes=False) - # pyformat: disable - self.assertAllClose( - np.matrix([[10., 0., 0., 250.,], - [0., 10., 0., 750.,], - [0., 0., 1., 0.,], - [0., 0., 0., 1.,]]), - transform) - # pyformat: enable - - def testTransformPoint(self): - transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=10.0, - image_ref_x=250, - image_ref_y=750, - flip_axes=False) - tx, ty, tz = transform_util.TransformPoint(transform, 0.0, 1.0, 0.0) - # X gets translated. - self.assertEqual(250., tx) - # Y gets translated and scaled by pixels_per_meter. - self.assertEqual(760., ty) - self.assertEqual(0., tz) - - def testCopyTransform(self): - # Same transform as above. - transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=10.0, - image_ref_x=250, - image_ref_y=750, - flip_axes=False) - # Test that copying the transform yields the same result. - copy_transform = transform_util.CopyTransform(transform) - tx, ty, tz = transform_util.TransformPoint(copy_transform, 0.0, 1.0, 0.0) - self.assertEqual(250., tx) - self.assertEqual(760., ty) - self.assertEqual(0., tz) - - def testBox2DCornerAxisAligned(self): - # Center at 1., 1., width of 1. and length of 2. - # - # The heading of 0. indicates the heading of the 'long' - # side of the box, so it is longer in the positive y direction. - box = transform_util.Box2D(1.0, 1.0, 2., 1., 0.) - self.assertAllClose(box.corners, - [[0.5, 2.], [1.5, 2.], [1.5, 0.], [0.5, 0.]]) - - def testBox2DCornerRotated(self): - # Like above but rotated 90 degrees; the order is important. - box = transform_util.Box2D(1.0, 1.0, 2., 1., np.pi / 2.) - self.assertAllClose(box.corners, - [[0., 0.5], [0., 1.5], [2., 1.5], [2., 0.5]]) - - def testBox2DTransform(self): - # Take the box from above and apply a car-image transform. - box = transform_util.Box2D(1.0, 1.0, 2., 1., 0.) - transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=10.0, image_ref_x=250, image_ref_y=750, flip_axes=True) - new_box = box.Apply(transform) - - # The center flips across the x=y axis to -1, -1. After the scaling and - # translation, the box should be centered (240, 740). Because the box flips - # across the axis, the width and length get flipped from 2, 1 to [10, 20]. - # - # The flip axes should cause the heading to go from 0. to -pi/2. - self.assertAllClose([240., 740., 10., 20., -np.pi / 2.], new_box.AsNumpy()) - - # Check ymin/xmin/ymax/xmax: the rectangle is now longer in the y-dimension - # than the x-dimension. - self.assertAllClose((730., 235., 750., 245.), new_box.Extrema()) - - def testTransformHeading(self): - transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=1.0, image_ref_x=123, image_ref_y=455, flip_axes=True) - - # Ray (0, 1): 90 degrees becomes -180 degrees. - self.assertAllClose(-np.pi, - transform_util.TransformHeading(transform, np.pi / 2.)) - - # Ray (1, 0): 0 degrees becomes -90 - self.assertAllClose(-np.pi / 2., - transform_util.TransformHeading(transform, 0.)) - - # (-1, 0) becomes (0, 1) or np.pi / 2. - self.assertAllClose(np.pi / 2., - transform_util.TransformHeading(transform, np.pi)) - - # (0, -1) becomes (1, 0) or 0 - self.assertAllClose(0., - transform_util.TransformHeading(transform, 1.5 * np.pi)) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/waymo/BUILD b/lingvo/tasks/car/waymo/BUILD deleted file mode 100644 index 8ce1d690..00000000 --- a/lingvo/tasks/car/waymo/BUILD +++ /dev/null @@ -1,121 +0,0 @@ -# Car related tasks. - -package(default_visibility = [ - "//visibility:public", -]) - -licenses(["notice"]) # Apache 2.0 - -py_test( - name = "waymo_ap_metric_test", - srcs = ["waymo_ap_metric_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":waymo_ap_metric", - ":waymo_metadata", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - # Implicit Waymo Open Dataset label dependency. - # Implicit Waymo Open Dataset metrics_proto dependency. - ], -) - -py_test( - name = "waymo_open_input_generator_test", - srcs = ["waymo_open_input_generator_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":waymo_open_input_generator", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "waymo_ap_metric", - srcs = [ - "waymo_ap_metric.py", - ], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/tasks/car:ap_metric", - # Implicit numpy dependency. - # Implicit Waymo Open Dataset label dependency. - # Implicit Waymo Open Dataset detection metrics dependency. - # Implicit Waymo Open Dataset py_metrics dependency. - # Implicit Waymo Open Dataset config_util_py dependency. - # Implicit Waymo Open Dataset breakdown_proto dependency. - # Implicit Waymo Open Dataset metrics_proto dependency. - ], -) - -py_library( - name = "waymo_decoder", - srcs = ["waymo_decoder.py"], - srcs_version = "PY3", - deps = [ - ":waymo_ap_metric", - ":waymo_metadata", - "//lingvo:compat", - "//lingvo/core:metrics", - "//lingvo/core:py_utils", - "//lingvo/tasks/car:base_decoder", - "//lingvo/tasks/car:detection_3d_metrics", - "//lingvo/tasks/car:transform_util", - # Implicit numpy dependency. - ], -) - -py_library( - name = "waymo_metadata", - srcs = [ - "waymo_metadata.py", - ], - srcs_version = "PY3", - deps = [ - "//lingvo/tasks/car:evaluation_metadata", - ], -) - -py_library( - name = "waymo_open_input_generator", - srcs = ["waymo_open_input_generator.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:datasource", - "//lingvo/core:hyperparams", - "//lingvo/core:py_utils", - "//lingvo/tasks/car:input_extractor", - "//lingvo/tasks/car:input_preprocessors", - # Implicit numpy dependency. - ], -) - -py_library( - name = "export_to_submission_format_lib", - srcs = ["export_to_submission_format.py"], - srcs_version = "PY3", - deps = [ - # Implicit absl.flags dependency. - "//lingvo:compat", - "//lingvo/core/ops:record_py_pb2", - # Implicit Waymo Open Dataset metrics_proto dependency. - ], -) - -py_binary( - name = "export_to_submission_format", - srcs = ["export_to_submission_format.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [":export_to_submission_format_lib"], -) diff --git a/lingvo/tasks/car/waymo/__init__.py b/lingvo/tasks/car/waymo/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/car/waymo/export_to_submission_format.py b/lingvo/tasks/car/waymo/export_to_submission_format.py deleted file mode 100644 index 77069572..00000000 --- a/lingvo/tasks/car/waymo/export_to_submission_format.py +++ /dev/null @@ -1,138 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -r"""Read Waymo Decoder's outputs and convert to submission format. - -Example: - -python export_to_submission_format.py \ - --decoder_path=/tmp/decoder_out_000060600 \ - --output_dir=/tmp/test_decoder_output - -preds.bin and gts.bin will be found in /tmp/test_decoder_output/ dir. -""" - -import os - -from absl import flags -from lingvo import compat as tf -from lingvo.core.ops import record_pb2 -from waymo_open_dataset.protos import metrics_pb2 - -FLAGS = flags.FLAGS -flags.DEFINE_string("decoder_path", None, - "Path to the decoder output tf.Record file.") -flags.DEFINE_string("output_dir", None, "Place to write detections.") -flags.DEFINE_float("score_threshold", 0, "Ignore detections with lower score.") - - -def convert_detections(table_path): - """Convert detections in `table_path` to metric format. - - Args: - table_path: Path to TFRecord file of decoder outputs. - - Returns: - (preds, gts): metric_pb2.Objects() of predictions and groundtruths. - """ - img_ids = [] - img_id_dict = {} - for serialized in tf.io.tf_record_iterator(table_path): - record = record_pb2.Record() - record.ParseFromString(serialized) - img_id = str(tf.make_ndarray(record.fields["frame_id"])) - img_ids.append(img_id) - np_dict = {k: tf.make_ndarray(v) for k, v in record.fields.items()} - img_id_dict[img_id] = np_dict - - preds = metrics_pb2.Objects() - gts = metrics_pb2.Objects() - for img_id in img_ids: - # Extract the underlying context string and timestamp - # from the image id. - # - # TODO(vrv): Consider embedding these values into the decoder output - # individually. - context_name = img_id[2:img_id.rindex("_")] - timestamp = int(img_id[img_id.rindex("_") + 1:-1]) - - np_dict = img_id_dict[img_id] - pred_bboxes = np_dict["bboxes"] # [max boxes, 7] - pred_scores = np_dict["scores"] # [max_boxes] - gt_bboxes = np_dict["gt_bboxes"] # [num_gt_boxes, 7] - gt_labels = np_dict["gt_labels"] # [num_gt_boxes] - class_ids = np_dict["class_ids"] # [max_boxes] - - def _add_box(label, box_vec): - label.box.center_x = box_vec[0] - label.box.center_y = box_vec[1] - label.box.center_z = box_vec[2] - label.box.length = box_vec[3] - label.box.width = box_vec[4] - label.box.height = box_vec[5] - label.box.heading = box_vec[6] - - num_gts = gt_bboxes.shape[0] - for gt_idx in range(num_gts): - gt_object = metrics_pb2.Object() - gt_object.context_name = context_name - gt_object.frame_timestamp_micros = timestamp - label = gt_object.object - _add_box(label, gt_bboxes[gt_idx]) - label.type = gt_labels[gt_idx] - # We should fill in the difficulty level once we want to measure the - # breakdown by LEVEL. - label.detection_difficulty_level = 0 - gts.objects.append(gt_object) - - num_pds = pred_bboxes.shape[0] - for pd_idx in range(num_pds): - score = pred_scores[pd_idx] - if score < FLAGS.score_threshold: - continue - pd_object = metrics_pb2.Object() - pd_object.context_name = context_name - pd_object.frame_timestamp_micros = timestamp - pd_object.score = score - label = pd_object.object - _add_box(label, pred_bboxes[pd_idx]) - label.type = class_ids[pd_idx] - preds.objects.append(pd_object) - - return preds, gts - - -def main(argv): - if len(argv) > 1: - raise tf.app.UsageError("Too many command-line arguments.") - - preds, gts = convert_detections(FLAGS.decoder_path) - - if not tf.io.gfile.exists(FLAGS.output_dir): - tf.io.gfile.mkdir(FLAGS.output_dir) - - # Write the predictions and gts into individual files. - # - # The outputs can then be passed to the official metrics implementations and - # server. - with tf.io.gfile.GFile(os.path.join(FLAGS.output_dir, "preds.bin"), "w") as f: - f.write(preds.SerializeToString()) - - with tf.io.gfile.GFile(os.path.join(FLAGS.output_dir, "gts.bin"), "w") as f: - f.write(gts.SerializeToString()) - - -if __name__ == "__main__": - tf.app.run(main) diff --git a/lingvo/tasks/car/waymo/tools/BUILD b/lingvo/tasks/car/waymo/tools/BUILD deleted file mode 100644 index 47011c43..00000000 --- a/lingvo/tasks/car/waymo/tools/BUILD +++ /dev/null @@ -1,40 +0,0 @@ -package(default_visibility = [ - "//visibility:public", -]) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "waymo_proto_to_tfe", - srcs = [ - "waymo_proto_to_tfe.py", - ], - srcs_version = "PY3", - deps = [ - # Implicit apache_beam dependency. - "//lingvo:compat", - "//lingvo/tasks/car:geometry", - # Implicit numpy dependency. - # Implicit Waymo Open Dataset proto dependency. - # Implicit Waymo Open Dataset range_image_utils dependency. - # Implicit Waymo Open Dataset transform_utils dependency. - ], -) - -py_binary( - name = "generate_waymo_tf", - srcs = [ - "generate_waymo_tf.py", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":waymo_proto_to_tfe", - # Implicit absl.app dependency. - # Implicit absl.flags dependency. - # Implicit apache_beam dependency. - "//lingvo:compat", - "//lingvo/tools:beam_utils", - # Implicit Waymo Open Dataset proto dependency. - ], -) diff --git a/lingvo/tasks/car/waymo/tools/__init__.py b/lingvo/tasks/car/waymo/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/car/waymo/tools/generate_waymo_tf.py b/lingvo/tasks/car/waymo/tools/generate_waymo_tf.py deleted file mode 100644 index fdc31261..00000000 --- a/lingvo/tasks/car/waymo/tools/generate_waymo_tf.py +++ /dev/null @@ -1,77 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -r"""Tool to convert Waymo Open Dataset to tf.Examples. - -An example of running on Cloud DataFlow: (not verified working yet). - -export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json - -path/to/generate_waymo_tf.py \ - --input_file_pattern=gs://path/to/waymo/inputs \ - --output_filebase=gs://$BUCKET/waymo/training/output@1000 -- \ - --project=$PROJECT \ - --temp_location=$TEMP_DIR \ - --runner=DataflowRunner -""" - -from absl import app -from absl import flags - -import apache_beam as beam -from lingvo import compat as tf -from lingvo.tasks.car.waymo.tools import waymo_proto_to_tfe -from lingvo.tools import beam_utils -from waymo_open_dataset import dataset_pb2 - -tf.enable_eager_execution() - -flags.DEFINE_string('input_file_pattern', None, 'Path to read input') -flags.DEFINE_string('output_filebase', None, 'Path to write output') - -FLAGS = flags.FLAGS - - -def main(argv): - beam_utils.BeamInit() - - assert FLAGS.input_file_pattern - assert FLAGS.output_filebase - - # Construct pipeline options from argv. - options = beam.options.pipeline_options.PipelineOptions(argv[1:]) - - reader = beam_utils.GetReader( - 'tfrecord', - FLAGS.input_file_pattern, - value_coder=beam.coders.ProtoCoder(dataset_pb2.Frame)) - - writer = beam_utils.GetWriter( - 'tfrecord', - file_pattern=FLAGS.output_filebase, - value_coder=beam.coders.ProtoCoder(tf.train.Example)) - - emitter_fn = beam_utils.GetEmitterFn('tfrecord') - with beam_utils.GetPipelineRoot(options=options) as root: - _ = ( - root - | 'Read' >> reader - | 'ConvertToTFExample' >> beam.ParDo( - waymo_proto_to_tfe.WaymoOpenDatasetConverter(emitter_fn)) - | 'Write' >> writer) - - -if __name__ == '__main__': - app.run(main) diff --git a/lingvo/tasks/car/waymo/tools/waymo_proto_to_tfe.py b/lingvo/tasks/car/waymo/tools/waymo_proto_to_tfe.py deleted file mode 100644 index d7f51d84..00000000 --- a/lingvo/tasks/car/waymo/tools/waymo_proto_to_tfe.py +++ /dev/null @@ -1,592 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -r"""Library to convert Waymo Open Dataset to tf.Examples. - -Generates a tf.Example proto for every dataset_pb2.Frame containing -the following keys and their values: - -* Frame-level metadata - -run_segment: string - The identifier of the driving sequence in the dataset. - -run_start_offset: int64 - The start offset within the run_segment sequence. - -time_of_day: string - Categorical description of time of day, e.g., "Day". - -location: string - Categorical description of geographical location, e.g., -"location_sf". - -weather: string - Categorical description of weather of scene, e.g., "sunny". - -pose: float: 4x4 transformation matrix for converting from "world" coordinates -to SDC coordinates. - -* Lasers - -There are 5 LIDAR sensors: "TOP", "SIDE_LEFT", "SIDE_RIGHT", "FRONT", "REAR". -Each LIDAR currently provides two returns, "ri1" and "ri2" for the first and -second returns of each shot. - -For every $LASER and $RI, we embed the raw range image: - -$LASER_$RI: float - flattened range image data of shape [H, W, C] from the -original proto. - -$LASER_$RI_shape: int64 - shape of the range image. - -For every lidar $LASER, we extract the calibrations: - -$LASER_beam_inclinations: float - List of beam angle inclinations for TOP -LIDAR (non-uniform). - -$LASER_beam_inclination_min: float - Minimum beam inclination for uniform -LIDARs. - -$LASER_beam_inclination_max: float - Maximum beam inclination for uniform -LIDARs. - -$LASER_extrinsics: float - 4x4 transformation matrix for converting from -SDC coordinates to LIDAR coordinates. - -The TOP LIDAR currently has a per-pixel range image pose to accommodate for -rolling shutter effects when projecting to 3D cartesian coordinates. We -embed this range image pose as TOP_pose. - -To allow for easier use, we also project all $LASERs to a stacked 3D cartesian -coordinate point cloud as: - -laser_$LASER_$RI - float: An [N, 6] matrix where there are N total points, -the first three dimensions are the x, y, z caresian coordinates, and the last -three dimensions are the intensity, elongation, and "is_in_no_label_zone" -bit for each point. - -* Camera images - -There are 5 cameras in the dataset: "FRONT", "FRONT_LEFT", "FRONT_RIGHT", -"SIDE_LEFT", and "SIDE_RIGHT". - -For each $CAM, we store: - -image_$CAM: string - Scalar Png format camera image. - -image_$CAM_shape: int64 - [3] - Vector containing the shape of the camera -image as [height, width, channels]. - -image_$CAM_pose: float - [4, 4] Matrix transformation for converting from -world coordinates to camera center. - -image_$CAM_pose: float - Scalar timestamp offset of when image was taken. - -image_$CAM_shutter: float - Scalar shutter value. - -image_$CAM_velocity: float - [6] Vector describing velocity of camera for -rolling shutter adjustment. See original proto for details. - -image_%CAM_camera_trigger_time: Scalar float for when camera was triggered. - -image_$CAM_camera_readout_done_time: Scalar float for when camera image finished -reading out data. - -camera_$CAM_extrinsics: float - 4x4 pose transformation for converting from -camera center coordinates to 2d projected view. - -camera_$CAM_intrinsics: float - [9] intrinsics transformation for converting -from camera center coordinates to 2d projected view. - -camera_$CAM_width: int64 - Scalar width of image. - -camera_$CAM_height: int64 - Scalar height of image. - -camera_$CAM_rolling_shutter_direction: int64 - Scalar value indicating the -direction of the rolling shutter adjustment. - -* Labels - -For each frame, we store the following label information for the M bounding -boxes in the frame. - -labels: int64 - [M] - The integer label class for every 3D bounding box -corresponding to the enumeration defined in the proto. - -label_ids: string - [M] - The unique label string identifying each labeled -object. This can be used for associating the same object across frames of the -same run segment. - -bboxes_3d: float - A flattened [M, 7] matrix where there are M boxes in the -frame, and each box is defined by a 7-DOF format - [center_x ,center_y, -center_z, length, width, height, heading]. - -label_metadata: floating point - A flattened [M, 4] matrix where there are -M boxes in the frame, and each md entry is the [speed_x, speed_y, accel_x, -accel_y] of the object. - -bboxes_3d_num_points: int64 - [M] - The number of points that fall into each -3D bounding box: can be used for computing the difficulty of each bounding -box. - -detection_difficulties: int64 - DO NOT USE FOR EVALUATION. Indicates whether the -labelers have determined that the object is of LEVEL_2 difficulty. -Should be used jointly with num_points above to set the difficulty level, -which we save in `single_frame_detection_difficulties`. Because it does not -include information about the number of points in its calculation, -it is an incomplete definition of difficulty and will not correspond to the -leaderboard if used to calculate metrics. - -single_frame_detection_difficulties: int64 - Indicates the difficulty level as -either LEVEL_1 (1), or LEVEL_2 (2), or IGNORE (999). We first ignore all 3D -labels without any LiDAR points. Next, we assign LEVEL_2 to examples where -either the labeler annotates as hard or if the example has <= 5 LiDAR points. -Finally, the rest of the examples are assigned to LEVEL_1. - -tracking_difficulties: int64 - Indicates whether the labelers have determined -that the tracked object is of LEVEL_2 difficulty. - -nlz_proto_strs: string - Vector of NoLabelZone polygon protos. Currently -unused. -""" - -import zlib - -import apache_beam as beam -from lingvo import compat as tf -import numpy as np -from waymo_open_dataset import dataset_pb2 -from waymo_open_dataset.utils import range_image_utils -from waymo_open_dataset.utils import transform_utils - - -class WaymoOpenDatasetConverter(beam.DoFn): - """Converts WaymoOpenDataset into tf.Examples. See file docstring.""" - - def __init__(self, emitter_fn): - self._emitter_fn = emitter_fn - - def process(self, item): - """Convert 'item' into tf.Example format.""" - output = tf.train.Example() - feature = output.features.feature - - # Convert run segment - run_segment = item.context.name - run_start_offset = item.timestamp_micros - key = run_segment + '_' + str(run_start_offset) - feature['run_segment'].bytes_list.value[:] = [ - tf.compat.as_bytes(run_segment) - ] - feature['run_start_offset'].int64_list.value[:] = [run_start_offset] - - # Extract metadata about frame. - feature['time_of_day'].bytes_list.value[:] = [ - tf.compat.as_bytes(item.context.stats.time_of_day) - ] - feature['location'].bytes_list.value[:] = [ - tf.compat.as_bytes(item.context.stats.location) - ] - feature['weather'].bytes_list.value[:] = [ - tf.compat.as_bytes(item.context.stats.weather) - ] - - # Convert pose: a 4x4 transformation matrix. - feature['pose'].float_list.value[:] = list(item.pose.transform) - self.frame_pose = tf.convert_to_tensor( - np.reshape(np.array(item.pose.transform), [4, 4]), dtype=tf.float32) - - # Extract laser names. - laser_names = [] - for laser in item.lasers: - laser_name = laser.name - real_name = dataset_pb2.LaserName.Name.Name(laser_name) - laser_names += [real_name] - - # Extract laser data (range images) and the calibrations. - self.extract_lasers(feature, item.lasers) - - self.extract_laser_calibrations(feature, item.context.laser_calibrations) - - range_image_pose = self._get_range_image_pose(item.lasers) - feature['TOP_pose'].float_list.value[:] = range_image_pose.numpy().reshape( - [-1]) - - # From the range images, also turn them into 3D point clouds. - self.add_point_cloud(feature, laser_names, range_image_pose) - merged_pointcloud_xyz = self._get_merged_pointcloud(feature, laser_names) - - self.add_labels(feature, item.laser_labels, merged_pointcloud_xyz) - self.add_no_label_zones(feature, item.no_label_zones) - - camera_calibrations_dict = ({ - camera_calibration.name: camera_calibration - for camera_calibration in item.context.camera_calibrations - }) - # Extract camera image data and the calibrations. - self.extract_camera_images(feature, item.images, camera_calibrations_dict) - self.extract_camera_calibrations(feature, - list(camera_calibrations_dict.values())) - - return self._emitter_fn(key, output) - - def _get_range_image_pose(self, lasers): - """Fetches the per-pixel pose information for the range image.""" - range_image_gbr_pose = None - for laser in lasers: - if laser.name != dataset_pb2.LaserName.TOP: - continue - pose_str = zlib.decompress(laser.ri_return1.range_image_pose_compressed) - # Deserialize from MatrixFloat serialization. - range_image_gbr_pose = dataset_pb2.MatrixFloat() - range_image_gbr_pose.ParseFromString(pose_str) - - assert range_image_gbr_pose is not None - shape = list(range_image_gbr_pose.shape.dims) - range_image_gbr_pose_tensor = np.array( - range_image_gbr_pose.data).reshape(shape) - range_image_gbr_pose_tensor_rotation = transform_utils.get_rotation_matrix( - range_image_gbr_pose_tensor[..., 0], - range_image_gbr_pose_tensor[..., 1], - range_image_gbr_pose_tensor[..., 2]) - range_image_gbr_pose_tensor_translation = range_image_gbr_pose_tensor[..., - 3:] - range_image_gbr_pose_tensor = transform_utils.get_transform( - range_image_gbr_pose_tensor_rotation, - range_image_gbr_pose_tensor_translation) - - assert range_image_gbr_pose_tensor.shape == (64, 2650, 4, 4) - return range_image_gbr_pose_tensor - - def _parse_range_image(self, range_image): - """Parse range_image proto and convert to MatrixFloat form.""" - if range_image.range_image_compressed: - ri_str = zlib.decompress(range_image.range_image_compressed) - # Deserialize from MatrixFloat serialization. - ri = dataset_pb2.MatrixFloat() - ri.ParseFromString(ri_str) - else: - ri = range_image.range_image - return ri - - def extract_camera_images(self, feature, camera_images, - camera_calibrations_dict): - """Extract the images into the tf.Example feature map. - - Args: - feature: A tf.Example feature map. - camera_images: A repeated car.open_dataset.CameraImage proto. - camera_calibrations_dict: A dictionary maps camera name to - car.open_dataset.CameraCalibration proto. - """ - for camera_image in camera_images: - camera_name = camera_image.name - camera_calibration = camera_calibrations_dict[camera_name] - real_name = dataset_pb2.CameraName.Name.Name(camera_name) - feature['image_%s' % real_name].bytes_list.value[:] = [ - tf.compat.as_bytes(camera_image.image) - ] - feature['image_%s_shape' % real_name].int64_list.value[:] = ([ - camera_calibration.height, camera_calibration.width, 3 - ]) - feature['image_%s_pose' % real_name].float_list.value[:] = ( - list(camera_image.pose.transform)) - velocity = camera_image.velocity - feature['image_%s_velocity' % real_name].float_list.value[:] = ([ - velocity.v_x, velocity.v_y, velocity.v_z, velocity.w_x, velocity.w_y, - velocity.w_z - ]) - feature['image_%s_pose_timestamp' % - real_name].float_list.value[:] = ([camera_image.pose_timestamp]) - feature['image_%s_shutter' % - real_name].float_list.value[:] = ([camera_image.shutter]) - feature['image_%s_camera_trigger_time' % - real_name].float_list.value[:] = ([ - camera_image.camera_trigger_time - ]) - feature['image_%s_camera_readout_done_time' % - real_name].float_list.value[:] = ([ - camera_image.camera_readout_done_time - ]) - - def extract_camera_calibrations(self, feature, camera_calibrations): - """Extract the camera calibrations into the tf.Example feature map. - - Args: - feature: A tf.Example feature map. - camera_calibrations: A CameraCalibration proto from the Waymo Dataset. - """ - for camera_calibration in camera_calibrations: - camera_name = camera_calibration.name - real_name = dataset_pb2.CameraName.Name.Name(camera_name) - - feature['camera_%s_extrinsics' % real_name].float_list.value[:] = list( - camera_calibration.extrinsic.transform) - feature['camera_%s_intrinsics' % real_name].float_list.value[:] = list( - camera_calibration.intrinsic) - feature['camera_%s_width' % - real_name].int64_list.value[:] = [camera_calibration.width] - feature['camera_%s_height' % - real_name].int64_list.value[:] = [camera_calibration.height] - feature['camera_%s_rolling_shutter_direction' % - real_name].int64_list.value[:] = [ - camera_calibration.rolling_shutter_direction - ] - - def extract_lasers(self, feature, lasers): - """Extract the lasers from range_images into the tf.Example feature map. - - Args: - feature: A tf.Example feature map. - lasers: A repeated car.open_dataset.Laser proto. - """ - for laser in lasers: - ri1 = self._parse_range_image(laser.ri_return1) - ri2 = self._parse_range_image(laser.ri_return2) - - # Add the range image data (flattened) and their original shape - # to the output feature map. - laser_name = laser.name - real_name = dataset_pb2.LaserName.Name.Name(laser_name) - feature['%s_ri1' % real_name].float_list.value[:] = ri1.data - feature['%s_ri1_shape' % real_name].int64_list.value[:] = ri1.shape.dims - feature['%s_ri2' % real_name].float_list.value[:] = ri2.data - feature['%s_ri2_shape' % real_name].int64_list.value[:] = ri2.shape.dims - - def extract_laser_calibrations(self, feature, laser_calibrations): - """Extract the laser calibrations into the tf.Example feature map. - - Args: - feature: A tf.Example feature map. - laser_calibrations: A LaserCalibrations proto from the Waymo Dataset. - """ - for laser_calibration in laser_calibrations: - laser_name = laser_calibration.name - real_name = dataset_pb2.LaserName.Name.Name(laser_name) - feature['%s_beam_inclinations' % real_name].float_list.value[:] = ( - laser_calibration.beam_inclinations) - feature['%s_beam_inclination_min' % real_name].float_list.value[:] = ([ - laser_calibration.beam_inclination_min - ]) - feature['%s_beam_inclination_max' % real_name].float_list.value[:] = ([ - laser_calibration.beam_inclination_max - ]) - feature['%s_extrinsics' % real_name].float_list.value[:] = list( - laser_calibration.extrinsic.transform) - - def add_point_cloud(self, feature, laser_names, range_image_pose): - """Convert the range images in `feature` to 3D point clouds. - - Adds the point cloud data to the tf.Example feature map. - - Args: - feature: A tf.Example feature map. - laser_names: A list of laser names (e.g., 'TOP', 'REAR', 'SIDE_LEFT'). - range_image_pose: A range image pose Tensor for the GBR. - """ - for laser_name in laser_names: - beam_inclinations = np.array(feature['%s_beam_inclinations' % - laser_name].float_list.value[:]) - # beam_inclinations will be populated if there is a non-uniform - # beam configuration (e.g., for the TOP lasers). Others that have - # uniform beam inclinations are only parameterized by the min and max. - # We use these min and max if the beam_inclinations are not present, - # and turn them into a uniform inclinations array. - if beam_inclinations.size == 0: - beam_inclination_min = feature['%s_beam_inclination_min' % - laser_name].float_list.value[:] - beam_inclination_max = feature['%s_beam_inclination_max' % - laser_name].float_list.value[:] - - laser_ri_name = '%s_ri1' % laser_name - range_image_shape = feature[laser_ri_name + - '_shape'].int64_list.value[:] - height = tf.cast(range_image_shape[0], tf.float32) - - beam_inclinations = tf.constant( - [beam_inclination_min[0], beam_inclination_max[0]]) - beam_inclinations = range_image_utils.compute_inclination( - beam_inclinations, height) - - beam_extrinsics = np.array( - feature['%s_extrinsics' % laser_name].float_list.value[:]).reshape( - 4, 4) - - for ri_type in ['ri1', 'ri2']: - laser_ri_name = '%s_%s' % (laser_name, ri_type) - # For each of the 4 features of the lasers: - range_image = np.array(feature[laser_ri_name].float_list.value[:]) - range_image_shape = feature[laser_ri_name + - '_shape'].int64_list.value[:] - range_image = range_image.reshape(range_image_shape) - # Compute mask. At the moment, invalid values in the range image - # representation are indicated via a -1. entry. Callers are expected - # to create this mask when passing into the conversion function below. - range_image_mask = range_image[..., 0] >= 0 - - # Get the 'range' feature from the range images. - range_image_range = range_image[..., 0] - - # Call utility to convert point cloud to cartesian coordinates. - # - # API expects a batch dimension for all inputs. - batched_pixel_pose = None - batched_frame_pose = None - # At the moment, only the GBR has per-pixel pose. - if laser_name == 'TOP': - batched_pixel_pose = range_image_pose[tf.newaxis, ...] - batched_frame_pose = self.frame_pose[tf.newaxis, ...] - - batched_range_image_range = tf.convert_to_tensor( - range_image_range[np.newaxis, ...], dtype=tf.float32) - batched_extrinsics = tf.convert_to_tensor( - beam_extrinsics[np.newaxis, ...], dtype=tf.float32) - batched_inclinations = tf.convert_to_tensor( - beam_inclinations[np.newaxis, ...], dtype=tf.float32) - - batched_inclinations = tf.reverse(batched_inclinations, axis=[-1]) - - range_image_cartesian = ( - range_image_utils.extract_point_cloud_from_range_image( - batched_range_image_range, - batched_extrinsics, - batched_inclinations, - pixel_pose=batched_pixel_pose, - frame_pose=batched_frame_pose)) - - points_xyz = tf.gather_nd(range_image_cartesian[0], - tf.where(range_image_mask)) - - # Fetch the features corresponding to each xyz coordinate and - # concatentate them together. - points_features = tf.cast( - tf.gather_nd(range_image[..., 1:], tf.where(range_image_mask)), - tf.float32) - points_data = tf.concat([points_xyz, points_features], axis=-1) - - # Add laser feature to output. - # - # Skip embedding shape since we assume that all points have six features - # and so we can reconstruct the number of points. - points_list = list(points_data.numpy().reshape([-1])) - feature['laser_%s' % laser_ri_name].float_list.value[:] = points_list - - def _get_merged_pointcloud(self, feature, laser_names): - """From the feature list, get a merged pointcloud of all laser sources.""" - points_list = [] - for laser_name in laser_names: - for ri_type in ['ri1', 'ri2']: - laser_ri_name = '%s_%s' % (laser_name, ri_type) - points_xyz = np.array(feature['laser_%s' % - laser_ri_name].float_list.value[:]) - points_list.append(np.reshape(points_xyz, (-1, 3))) - return np.concatenate(points_list, axis=0) - - def _single_frame_detection_difficulty(self, human_difficulty, num_points): - """Create the `single_frame_detection_difficulty` field. - - When labeling, humans have the option to label a particular frame's bbox - as difficult, which overrides the normal number of points based - definition. Additionally, boxes with 0 points are ignored by the metric - code. - - Args: - human_difficulty: What the human raters labeled the difficulty as. This is - from the detection_difficulty_level field, and will be either 0 (default - value, which is UKNOWN in the proto enum) or 2 (LEVEL_2 difficulty). - num_points: The number of points in the bbox. - - Returns: - single_frame_detection_difficulty: The single frame detection difficulty - per the Waymo Open Dataset paper's definition. - """ - if num_points <= 0: - return 999 - - if human_difficulty: - return human_difficulty - - if num_points <= 5: - return 2 - else: - return 1 - - def add_labels(self, feature, labels, points_xyz): - """Add 3d bounding box labels into the output feature map. - - Args: - feature: A tf.Example feature map. - labels: A repeated car.open_dataset.Label proto. - points_xyz: A numpy array of shape [-1, 3] with the pointcloud. This is - used to calculate the number of points in each 3D bounding box. - """ - label_classes = [] - label_ids = [] - detection_difficulty_levels = [] - tracking_difficulty_levels = [] - bboxes_3d_num_points = [] - single_frame_detection_difficulty_levels = [] - bboxes = [] - label_md = [] - - for label in labels: - box = label.box - bbox_3d = [ - box.center_x, box.center_y, box.center_z, box.length, box.width, - box.height, box.heading - ] - md = [ - label.metadata.speed_x, label.metadata.speed_y, - label.metadata.accel_x, label.metadata.accel_y - ] - label_md += md - bboxes += bbox_3d - label_classes += [label.type] - label_ids += [tf.compat.as_bytes(label.id)] - detection_difficulty_levels += [label.detection_difficulty_level] - tracking_difficulty_levels += [label.tracking_difficulty_level] - bboxes_3d_num_points += [label.num_lidar_points_in_box] - - # Compute the single frame difficulty level per object. - human_labeler_difficulty = label.detection_difficulty_level - num_points = bboxes_3d_num_points[-1] - single_frame_detection_difficulty = ( - self._single_frame_detection_difficulty(human_labeler_difficulty, - num_points)) - single_frame_detection_difficulty_levels += [ - single_frame_detection_difficulty - ] - - bboxes = np.array(bboxes).reshape(-1) - label_md = np.array(label_md).reshape(-1) - feature['labels'].int64_list.value[:] = label_classes - feature['label_ids'].bytes_list.value[:] = label_ids - feature['detection_difficulties'].int64_list.value[:] = ( - detection_difficulty_levels) - feature['single_frame_detection_difficulties'].int64_list.value[:] = ( - single_frame_detection_difficulty_levels) - feature['tracking_difficulties'].int64_list.value[:] = ( - tracking_difficulty_levels) - feature['bboxes_3d'].float_list.value[:] = list(bboxes) - feature['label_metadata'].float_list.value[:] = list(label_md) - feature['bboxes_3d_num_points'].int64_list.value[:] = (bboxes_3d_num_points) - - def add_no_label_zones(self, feature, no_label_zones): - """Add no label zones into the output feature map. - - Args: - feature: A tf.Example feature map. - no_label_zones: A repeated car.open_dataset.Polygon2dProto proto. - """ - nlz_proto_strs = [] - for nlz in no_label_zones: - nlz_proto_strs += [tf.compat.as_bytes(nlz.SerializeToString())] - feature['no_label_zones'].bytes_list.value[:] = nlz_proto_strs diff --git a/lingvo/tasks/car/waymo/waymo_ap_metric.py b/lingvo/tasks/car/waymo/waymo_ap_metric.py deleted file mode 100644 index 2ac804d3..00000000 --- a/lingvo/tasks/car/waymo/waymo_ap_metric.py +++ /dev/null @@ -1,456 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Average Precision metric class for Waymo open dataset. - -The Waymo library provides a metrics breakdown API for a set of breakdowns -implemented in their library. This wrapper uses our basic abstraction for -building AP metrics but only allows breakdowns that are supported in the -Waymo breakdown API. Should you want other breakdowns, consider using the -standard AP metrics implementation with our custom breakdowns. -""" - -from lingvo import compat as tf -from lingvo.core import plot -from lingvo.core import py_utils -from lingvo.tasks.car import ap_metric -from lingvo.tasks.car import breakdown_metric - -import numpy as np -from waymo_open_dataset import label_pb2 -from waymo_open_dataset.metrics.ops import py_metrics_ops -from waymo_open_dataset.metrics.python import config_util_py as config_util -from waymo_open_dataset.protos import breakdown_pb2 -from waymo_open_dataset.protos import metrics_pb2 - - -def _BuildWaymoMetricConfig(metadata, box_type, waymo_breakdown_metrics): - """Build the Config proto for Waymo's metric op.""" - config = metrics_pb2.Config() - # config.num_desired_score_cutoffs = metadata.NumberOfPrecisionRecallPoints() - num_pr_points = metadata.NumberOfPrecisionRecallPoints() - config.score_cutoffs.extend( - [i * 1.0 / (num_pr_points - 1) for i in range(num_pr_points)]) - config.matcher_type = metrics_pb2.MatcherProto.Type.TYPE_HUNGARIAN - if box_type == '2d': - config.box_type = label_pb2.Label.Box.Type.TYPE_2D - else: - config.box_type = label_pb2.Label.Box.Type.TYPE_3D - # Default values - config.iou_thresholds[:] = [0.7, 0.7, 0.7, 0.7, 0.7] - for class_name, threshold in metadata.IoUThresholds().items(): - cls_idx = metadata.ClassNames().index(class_name) - config.iou_thresholds[cls_idx] = threshold - # Run on all the data for 2 difficulty levels - config.breakdown_generator_ids.append(breakdown_pb2.Breakdown.ONE_SHARD) - difficulty = metrics_pb2.Difficulty() - difficulty.levels.append(label_pb2.Label.DifficultyLevel.Value('LEVEL_1')) - difficulty.levels.append(label_pb2.Label.DifficultyLevel.Value('LEVEL_2')) - config.difficulties.append(difficulty) - - # Add extra breakdown metrics. - for breakdown_value in waymo_breakdown_metrics: - breakdown_id = breakdown_pb2.Breakdown.GeneratorId.Value(breakdown_value) - config.breakdown_generator_ids.append(breakdown_id) - difficulty = metrics_pb2.Difficulty() - difficulty.levels.append(label_pb2.Label.DifficultyLevel.Value('LEVEL_1')) - difficulty.levels.append(label_pb2.Label.DifficultyLevel.Value('LEVEL_2')) - config.difficulties.append(difficulty) - return config - - -class WaymoAPMetrics(ap_metric.APMetrics): - """The Waymo Open Dataset implementation of AP metric.""" - - @classmethod - def Params(cls, metadata): - """Params builder for APMetrics.""" - p = super().Params(metadata) - p.Define( - 'waymo_breakdown_metrics', [], - 'List of extra waymo breakdown metrics when computing AP. These ' - 'should match the names of the proto entries in metrics.proto, such ' - 'as `RANGE` or `OBJECT_TYPE`.') - return p - - def __init__(self, params): - super().__init__(params) - self._waymo_metric_config = _BuildWaymoMetricConfig( - self.metadata, self.params.box_type, - self.params.waymo_breakdown_metrics) - # Compute only waymo breakdown metrics. - breakdown_names = config_util.get_breakdown_names_from_config( - self._waymo_metric_config) - waymo_params = WaymoBreakdownMetric.Params().Set( - metadata=self.metadata, breakdown_list=breakdown_names) - self._breakdown_metrics['waymo'] = WaymoBreakdownMetric(waymo_params) - - # Remove the base metric. - del self._breakdown_metrics['difficulty'] - - def _GetData(self, - classid, - difficulty=None, - distance=None, - num_points=None, - rotation=None): - """Returns groundtruth and prediction for the classid in a NestedMap. - - Args: - classid: int32 specifying the class - difficulty: Not used. - distance: int32 specifying a binned Euclidean distance of the ground truth - bounding box. If None is specified, all distances are selected. - num_points: int32 specifying a binned number of laser points within the - ground truth bounding box. If None is specified, all boxes are selected. - rotation: int32 specifying a binned rotation within the ground truth - bounding box. If None is specified, all boxes are selected. - - Returns: - NestedMap containing iou_threshold, groundtruth and predictions for - specified, classid, difficulty level and binned distance. If no bboxes - are found with these parameters, returns None. - """ - del difficulty - assert classid > 0 and classid < self.metadata.NumClasses() - - g = self._LoadBoundingBoxes( - 'groundtruth', - classid, - distance=distance, - num_points=num_points, - rotation=rotation) - # Note that we do not specify num_points for predictions because only - # groundtruth boxes contain points. - p = self._LoadBoundingBoxes( - 'prediction', classid, distance, num_points=None, rotation=rotation) - if g is None or p is None: - return None - - gt_boxes = g.boxes - gt_imgids = g.imgids - gt_speeds = g.speeds - iou_threshold = self._iou_thresholds[self.metadata.ClassNames()[classid]] - - return py_utils.NestedMap( - iou_threshold=iou_threshold, - gt=py_utils.NestedMap( - imgid=gt_imgids, - bbox=gt_boxes, - speed=gt_speeds, - difficulty=g.difficulties), - pd=py_utils.NestedMap(imgid=p.imgids, bbox=p.boxes, score=p.scores)) - - def _BuildMetric(self, feed_data, classid): - """Construct tensors and the feed_dict for Waymo metric op. - - Args: - feed_data: a NestedMap returned by _GetData(). - classid: integer. - - Returns: - A tuple of 3 dicts: - - - scalar_metrics: a dict mapping all the metric names to fetch tensors. - - curves: a dict mapping all the curve names to fetch tensors. - - feed_dict: a dict mapping the tensors in feed_tensors to feed values. - """ - breakdown_names = config_util.get_breakdown_names_from_config( - self._waymo_metric_config) - if feed_data is None: - dummy_scalar = tf.constant(np.nan) - dummy_curve = tf.zeros([self.metadata.NumberOfPrecisionRecallPoints(), 2], - tf.float32) - scalar_metrics = {'ap': dummy_scalar, 'ap_ha_weighted': dummy_scalar} - curve_metrics = {'pr': dummy_curve, 'pr_ha_weighted': dummy_curve} - - for i, metric in enumerate(breakdown_names): - scalar_metrics['ap_%s' % metric] = dummy_scalar - scalar_metrics['ap_ha_weighted_%s' % metric] = dummy_scalar - curve_metrics['pr_%s' % metric] = dummy_curve - curve_metrics['pr_ha_weighted_%s' % metric] = dummy_curve - - return py_utils.NestedMap( - feed_dict={}, - scalar_metrics=scalar_metrics, - curve_metrics=curve_metrics) - - feed_dict = {} - - f_gt_bbox = tf.placeholder(tf.float32) - feed_dict[f_gt_bbox] = feed_data.gt.bbox - - f_gt_imgid = tf.placeholder(tf.int32) - feed_dict[f_gt_imgid] = feed_data.gt.imgid - - f_gt_speed = tf.placeholder(tf.float32) - feed_dict[f_gt_speed] = feed_data.gt.speed - - f_gt_difficulty = tf.placeholder(tf.uint8) - feed_dict[f_gt_difficulty] = feed_data.gt.difficulty - - f_pd_bbox = tf.placeholder(tf.float32) - feed_dict[f_pd_bbox] = feed_data.pd.bbox - - f_pd_imgid = tf.placeholder(tf.int32) - feed_dict[f_pd_imgid] = feed_data.pd.imgid - - f_pd_score = tf.placeholder(tf.float32) - feed_dict[f_pd_score] = feed_data.pd.score - - num_gt_bboxes = feed_data.gt.imgid.shape[0] - num_pd_bboxes = feed_data.pd.imgid.shape[0] - gt_class_ids = tf.constant(classid, dtype=tf.uint8, shape=[num_gt_bboxes]) - pd_class_ids = tf.constant(classid, dtype=tf.uint8, shape=[num_pd_bboxes]) - ap, ap_ha, pr, pr_ha, _ = py_metrics_ops.detection_metrics( - prediction_bbox=f_pd_bbox, - prediction_type=pd_class_ids, - prediction_score=f_pd_score, - prediction_frame_id=tf.cast(f_pd_imgid, tf.int64), - prediction_overlap_nlz=tf.zeros_like(f_pd_imgid, dtype=tf.bool), - ground_truth_bbox=f_gt_bbox, - ground_truth_type=gt_class_ids, - ground_truth_frame_id=tf.cast(f_gt_imgid, tf.int64), - ground_truth_difficulty=f_gt_difficulty, - ground_truth_speed=f_gt_speed, - config=self._waymo_metric_config.SerializeToString()) - - # All tensors returned by Waymo's metric op have a leading dimension - # B=number of breakdowns. At this moment we always use B=1 to make - # it compatible to the python code. - scalar_metrics = {'ap': ap[0], 'ap_ha_weighted': ap_ha[0]} - curve_metrics = {'pr': pr[0], 'pr_ha_weighted': pr_ha[0]} - - for i, metric in enumerate(breakdown_names): - # There is a scalar / curve for every breakdown. - scalar_metrics['ap_%s' % metric] = ap[i] - scalar_metrics['ap_ha_weighted_%s' % metric] = ap_ha[i] - curve_metrics['pr_%s' % metric] = pr[i] - curve_metrics['pr_ha_weighted_%s' % metric] = pr_ha[i] - return py_utils.NestedMap( - feed_dict=feed_dict, - scalar_metrics=scalar_metrics, - curve_metrics=curve_metrics) - - def _ComputeFinalMetrics(self, - classids=None, - difficulty=None, - distance=None, - num_points=None, - rotation=None): - """Compute precision-recall curves as well as average precision. - - Args: - classids: A list of N int32. - difficulty: Not used. - distance: int32 specifying a binned Euclidean distance of the ground truth - bounding box. If None is specified, all distances are selected. - num_points: int32 specifying a binned number of laser points within the - ground truth bounding box. If None is specified, all boxes are selected. - rotation: int32 specifying a binned rotation within the ground truth - bounding box. If None is specified, all boxes are selected. - - Returns: - dict. Each entry in the dict is a list of C (number of classes) dicts - containing mapping from metric names to individual results. Individual - entries may be the following items. - - scalars: A list of C (number of classes) dicts mapping metric - names to scalar values. - - curves: A list of C dicts mapping metrics names to np.float32 - arrays of shape [NumberOfPrecisionRecallPoints()+1, 2]. In the last - dimension, 0 indexes precision and 1 indexes recall. - """ - del difficulty - tf.logging.info('Computing final Waymo metrics.') - assert classids is not None, 'classids must be supplied.' - feed_dict = {} - g = tf.Graph() - scalar_fetches = [] - curve_fetches = [] - with g.as_default(): - for classid in classids: - data = self._GetData( - classid, - distance=distance, - num_points=num_points, - rotation=rotation) - metrics = self._BuildMetric(data, classid) - scalar_fetches += [metrics.scalar_metrics] - curve_fetches += [metrics.curve_metrics] - feed_dict.update(metrics.feed_dict) - - with tf.Session(graph=g) as sess: - results = sess.run([scalar_fetches, curve_fetches], feed_dict=feed_dict) - tf.logging.info('Finished computing final Waymo metrics.') - return {'scalars': results[0], 'curves': results[1]} - - @property - def value(self): - """Returns weighted mAP over all eval classes.""" - self._EvaluateIfNecessary() - ap = self._breakdown_metrics['waymo']._average_precisions # pylint:disable=protected-access - breakdown_names = config_util.get_breakdown_names_from_config( - self._waymo_metric_config) - - num_sum = 0.0 - denom_sum = 0.0 - # Compute the average AP over all eval classes. The first breakdown - # is the overall mAP. - for class_index in range(len(self.metadata.EvalClassIndices())): - num_sum += np.nan_to_num(ap[breakdown_names[0]][class_index]) - denom_sum += 1. - return num_sum / denom_sum - - def Summary(self, name): - """Implements custom Summary for Waymo metrics.""" - self._EvaluateIfNecessary() - - ret = tf.Summary() - # Put '.value' first (so it shows up in logs / summaries, etc). - ret.value.add(tag='{}/weighted_mAP'.format(name), simple_value=self.value) - - ap = self._breakdown_metrics['waymo']._average_precisions # pylint:disable=protected-access - aph = self._breakdown_metrics['waymo']._average_precision_headings # pylint:disable=protected-access - breakdown_names = config_util.get_breakdown_names_from_config( - self._waymo_metric_config) - - for i, class_index in enumerate(self.metadata.EvalClassIndices()): - classname = self.metadata.ClassNames()[class_index] - for breakdown_name in breakdown_names: - # 'ONE_SHARD' breakdowns are the overall metrics (not sliced up) - # So we should make that the defualt metric. - if 'ONE_SHARD' in breakdown_name: - # For the overall mAP, include the class name - # and set the breakdown_str which will have the level - prefix = '{}/{}'.format(name, classname) - postfix = breakdown_name.replace('ONE_SHARD_', '') - breakdown_str = postfix if postfix else 'UNKNOWN' - # Otherwise check that the class we are looking at is in the breakdown. - elif classname.lower() in breakdown_name.lower(): - prefix = '{}_extra'.format(name) - breakdown_str = breakdown_name - else: - continue - - tag_str = '{}/AP_{}'.format(prefix, breakdown_str) - ap_value = ap[breakdown_name][i] - ret.value.add(tag=tag_str, simple_value=ap_value) - tag_str = '{}/APH_{}'.format(prefix, breakdown_str) - aph_value = aph[breakdown_name][i] - ret.value.add(tag=tag_str, simple_value=aph_value) - - image_summaries = self._breakdown_metrics['waymo'].GenerateSummaries(name) - for image_summary in image_summaries: - ret.value.extend(image_summary.value) - - return ret - - -class WaymoBreakdownMetric(breakdown_metric.BreakdownMetric): - """Calculate average precision as function of difficulty.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'breakdown_list', [], - 'A list of breakdown names corresponding to the breakdown ' - 'metrics computed from the Waymo breakdown generator config.') - return p - - def __init__(self, p): - super().__init__(p) - self._average_precision_headings = {} - self._precision_recall_headings = {} - - def ComputeMetrics(self, compute_metrics_fn): - p = self.params - tf.logging.info('Calculating waymo AP breakdowns: start') - metrics = compute_metrics_fn() - scalars = metrics['scalars'] - curves = metrics['curves'] - - for breakdown_str in p.breakdown_list: - self._average_precisions[breakdown_str] = [ - s['ap_%s' % breakdown_str] for s in scalars - ] - self._average_precision_headings[breakdown_str] = [ - s['ap_ha_weighted_%s' % breakdown_str] for s in scalars - ] - self._precision_recall[breakdown_str] = np.array( - [c['pr_%s' % breakdown_str] for c in curves]) - self._precision_recall_headings[breakdown_str] = np.array( - [c['pr_ha_weighted_%s' % breakdown_str] for c in curves]) - tf.logging.info('Calculating waymo AP breakdowns: finished') - - def GenerateSummaries(self, name): - """Generate an image summary for precision recall by difficulty.""" - p = self.params - - image_summaries = [] - for i, class_index in enumerate(p.metadata.EvalClassIndices()): - - def _Setter(fig, axes): - """Configure the plot for precision recall.""" - ticks = np.arange(0, 1.05, 0.1) - axes.grid(b=False) - axes.set_xlabel('Recall') - axes.set_xticks(ticks) - axes.set_ylabel('Precision') - axes.set_yticks(ticks) - # TODO(vrv): Add legend indicating number of objects in breakdown. - fig.tight_layout() - - classname = p.metadata.ClassNames()[class_index] - for breakdown_name in p.breakdown_list: - # 'ONE_SHARD' breakdowns are the overall metrics (not sliced up) - # So we should never skip this. - if 'ONE_SHARD' in breakdown_name: - breakdown_str = breakdown_name.replace('ONE_SHARD_', '') - tag_str = '{}/{}/{}/PR'.format(name, classname, breakdown_str) - # Otherwise check that the class we are looking at is in the breakdown. - elif classname.lower() in breakdown_name.lower(): - tag_str = '{}/{}/{}/PR'.format(name, classname, breakdown_name) - else: - continue - - ps = [self._precision_recall[breakdown_name][i][:, 0]] - rs = [self._precision_recall[breakdown_name][i][:, 1]] - image_summary = plot.Curve( - name=tag_str, - figsize=(10, 8), - xs=rs[0], - ys=np.array(ps).T, - setter=_Setter, - marker='.', - markersize=14, - linestyle='-', - linewidth=2, - alpha=0.5) - image_summaries.append(image_summary) - return image_summaries - - # Fill in dummy implementations which are largely - # unused. The current implementation does not provide breakdown - # image summaries that do bucketing; we assume that the waymo breakdown - # implementations will break things down as necessary. - def AccumulateHistogram(self, result): - pass - - def AccumulateCumulative(self, result): - pass - - def NumBinsOfHistogram(self): - return 1 diff --git a/lingvo/tasks/car/waymo/waymo_ap_metric_test.py b/lingvo/tasks/car/waymo/waymo_ap_metric_test.py deleted file mode 100644 index 2f6db482..00000000 --- a/lingvo/tasks/car/waymo/waymo_ap_metric_test.py +++ /dev/null @@ -1,130 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for waymo_ap_metric.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car.waymo import waymo_ap_metric -from lingvo.tasks.car.waymo import waymo_metadata -import numpy as np -from waymo_open_dataset import label_pb2 - -FLAGS = tf.flags.FLAGS - - -class APTest(test_utils.TestCase): - - def testWaymoAPConfig(self): - metadata = waymo_metadata.WaymoMetadata() - # Use 2D metric. - config = waymo_ap_metric._BuildWaymoMetricConfig(metadata, '2d', []) - vehicle_idx = label_pb2.Label.Type.Value('TYPE_VEHICLE') - ped_idx = label_pb2.Label.Type.Value('TYPE_PEDESTRIAN') - cyc_idx = label_pb2.Label.Type.Value('TYPE_CYCLIST') - - thresholds_meta = metadata.IoUThresholds() - self.assertNear(config.iou_thresholds[vehicle_idx], - thresholds_meta['Vehicle'], 1e-6) - self.assertNear(config.iou_thresholds[ped_idx], - thresholds_meta['Pedestrian'], 1e-6) - self.assertNear(config.iou_thresholds[cyc_idx], thresholds_meta['Cyclist'], - 1e-6) - - def testPerfectBox(self): - metadata = waymo_metadata.WaymoMetadata() - params = waymo_ap_metric.WaymoAPMetrics.Params(metadata) - m = params.Instantiate() - # Make one update with a perfect box. - update_dict = py_utils.NestedMap( - groundtruth_labels=np.array([1]), - groundtruth_bboxes=np.ones(shape=(1, 7)), - groundtruth_difficulties=np.zeros(shape=(1)), - groundtruth_num_points=None, - detection_scores=np.ones(shape=(5, 1)), - detection_boxes=np.ones(shape=(5, 1, 7)), - detection_heights_in_pixels=np.ones(shape=(5, 1))) - - m.Update('1234', update_dict) - - waymo_ap = m.value - self.assertAllClose(waymo_ap, 1. / 3.) - - # Write a summary. - summary = m.Summary('foo') - # Check that both AP and APH are in the tags. - tags = [v.tag for v in summary.value] - self.assertIn('foo/Pedestrian/AP_LEVEL_1', tags) - self.assertIn('foo/Pedestrian/APH_LEVEL_1', tags) - self.assertIn('foo/Pedestrian/AP_LEVEL_2', tags) - self.assertIn('foo/Pedestrian/APH_LEVEL_2', tags) - - def testWaymoBreakdowns(self): - metadata = waymo_metadata.WaymoMetadata() - params = waymo_ap_metric.WaymoAPMetrics.Params(metadata) - params.waymo_breakdown_metrics = ['RANGE', 'VELOCITY'] - - m = params.Instantiate() - # Make one update with a perfect box. - update_dict = py_utils.NestedMap( - groundtruth_labels=np.array([1]), - groundtruth_bboxes=np.ones(shape=(1, 7)), - groundtruth_difficulties=np.zeros(shape=(1)), - groundtruth_num_points=None, - groundtruth_speed=np.zeros(shape=(1, 2)), - detection_scores=np.ones(shape=(5, 1)), - detection_boxes=np.ones(shape=(5, 1, 7)), - detection_heights_in_pixels=np.ones(shape=(5, 1))) - - m.Update('1234', update_dict) - - # Write a summary. - summary = m.Summary('foo') - # Check that the summary value for default ap and - # a waymo breakdown version by range is the same. - for v in summary.value: - if v.tag == 'foo/Vehicle/AP_LEVEL_1': - default_val = v.simple_value - elif v.tag == 'foo/Vehicle/APH_LEVEL_1': - aph_default_val = v.simple_value - elif v.tag == 'foo_extra/AP_RANGE_TYPE_VEHICLE_[0, 30)_LEVEL_1': - ap_bd_val_l1 = v.simple_value - elif v.tag == 'foo_extra/AP_RANGE_TYPE_VEHICLE_[0, 30)_LEVEL_2': - ap_bd_val_l2 = v.simple_value - elif v.tag == 'foo_extra/APH_RANGE_TYPE_VEHICLE_[0, 30)_LEVEL_1': - aph_bd_val_l1 = v.simple_value - elif v.tag == 'foo_extra/APH_RANGE_TYPE_VEHICLE_[0, 30)_LEVEL_2': - aph_bd_val_l2 = v.simple_value - elif v.tag == 'foo_extra/AP_VELOCITY_TYPE_VEHICLE_STATIONARY_LEVEL_1': - vbd_val_l1 = v.simple_value - elif v.tag == 'foo_extra/AP_VELOCITY_TYPE_VEHICLE_STATIONARY_LEVEL_2': - vbd_val_l2 = v.simple_value - - self.assertEqual(ap_bd_val_l1, default_val) - self.assertEqual(ap_bd_val_l2, default_val) - self.assertEqual(aph_bd_val_l1, aph_default_val) - self.assertEqual(aph_bd_val_l2, aph_default_val) - self.assertEqual(vbd_val_l1, default_val) - self.assertEqual(vbd_val_l2, default_val) - - # Check that eval classes not evaluated are not present. - tags = [v.tag for v in summary.value] - self.assertNotIn('foo_extra/APH_RANGE_TYPE_SIGN_[0, 30)_LEVEL_1', tags) - self.assertNotIn('foo_extra/APH_RANGE_TYPE_SIGN_[0, 30)_LEVEL_2', tags) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/car/waymo/waymo_decoder.py b/lingvo/tasks/car/waymo/waymo_decoder.py deleted file mode 100644 index b80496a7..00000000 --- a/lingvo/tasks/car/waymo/waymo_decoder.py +++ /dev/null @@ -1,294 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Base models for point-cloud based detection.""" - -from lingvo import compat as tf -from lingvo.core import metrics -from lingvo.core import py_utils -from lingvo.tasks.car import base_decoder -from lingvo.tasks.car import detection_3d_metrics -from lingvo.tasks.car import transform_util -from lingvo.tasks.car.waymo import waymo_ap_metric -from lingvo.tasks.car.waymo import waymo_metadata -import numpy as np - - -class WaymoOpenDatasetDecoder(base_decoder.BaseDecoder): - """A decoder to use for decoding a detector model on Waymo.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'draw_visualizations', False, 'Boolean for whether to draw ' - 'visualizations. This is independent of laser_sampling_rate.') - p.ap_metric = waymo_ap_metric.WaymoAPMetrics.Params( - waymo_metadata.WaymoMetadata()) - p.Define( - 'extra_ap_metrics', {}, - 'Dictionary of extra AP metrics to run in the decoder. The key' - 'is the name of the metric and the value is a sub-class of ' - 'APMetric') - p.Define( - 'save_residuals', False, - 'If True, this expects the residuals and ground-truth to be available ' - 'in the decoder output dictionary, and it will save it to the decoder ' - 'output file. See decode_include_residuals in PointDetectorBase ' - 'for details.') - return p - - def CreateDecoderMetrics(self): - """Decoder metrics for WaymoOpenDataset.""" - p = self.params - - waymo_metric_p = p.ap_metric.Copy().Set(cls=waymo_ap_metric.WaymoAPMetrics) - waymo_metrics = waymo_metric_p.Instantiate() - class_names = waymo_metrics.metadata.ClassNames() - - # TODO(bencaine,vrv): There's some code smell with this ap_metrics params - # usage. We create local copies of the params to then instantiate them. - # Failing to do this risks users editing the params after construction of - # the object, making each object method call have the potential for side - # effects. - # Create a new dictionary with copies of the params converted to objects - # so we can then add these to the decoder metrics. - extra_ap_metrics = {} - for k, metric_p in p.extra_ap_metrics.items(): - extra_ap_metrics[k] = metric_p.Instantiate() - - waymo_metric_bev_p = waymo_metric_p.Copy() - waymo_metric_bev_p.box_type = '2d' - waymo_metrics_bev = waymo_metric_bev_p.Instantiate() - # Convert the list of class names to a dictionary mapping class_id -> name. - class_id_to_name = dict(enumerate(class_names)) - - # TODO(vrv): This uses the same top down transform as for KITTI; - # re-visit these settings since detections can happen all around - # the car. - top_down_transform = transform_util.MakeCarToImageTransform( - pixels_per_meter=32., - image_ref_x=512., - image_ref_y=1408., - flip_axes=True) - decoder_metrics = py_utils.NestedMap({ - 'top_down_visualization': - (detection_3d_metrics.TopDownVisualizationMetric( - top_down_transform, - image_height=1536, - image_width=1024, - class_id_to_name=class_id_to_name)), - 'num_samples_in_batch': metrics.AverageMetric(), - 'waymo_metrics': waymo_metrics, - 'waymo_metrics_bev': waymo_metrics_bev, - }) - self._update_metrics_class_keys = ['waymo_metrics_bev', 'waymo_metrics'] - for k, metric in extra_ap_metrics.items(): - decoder_metrics[k] = metric - self._update_metrics_class_keys.append(k) - - decoder_metrics.mesh = detection_3d_metrics.WorldViewer() - return decoder_metrics - - def ProcessOutputs(self, input_batch, model_outputs): - """Produce additional decoder outputs for WaymoOpenDataset. - - Args: - input_batch: A .NestedMap of the inputs to the model. - model_outputs: A .NestedMap of the outputs of the model, including:: - - per_class_predicted_bboxes: [batch, num_classes, num_boxes, 7] float - Tensor with per class 3D (7 DOF) bounding boxes. - - per_class_predicted_bbox_scores: [batch, num_classes, num_boxes] float - Tensor with per class, per box scores. - - per_class_valid_mask: [batch, num_classes, num_boxes] masking Tensor - indicating which boxes were still kept after NMS for each class. - - Returns: - A NestedMap of additional decoder outputs needed for - PostProcessDecodeOut. - """ - del model_outputs - p = self.params - input_labels = input_batch.labels - input_metadata = input_batch.metadata - source_ids = tf.strings.join([ - input_metadata.run_segment, - tf.as_string(input_metadata.run_start_offset) - ], - separator='_') - ret = py_utils.NestedMap({ - 'num_points_in_bboxes': input_batch.labels.bboxes_3d_num_points, - # Ground truth. - 'bboxes_3d': input_labels.bboxes_3d, - 'bboxes_3d_mask': input_labels.bboxes_3d_mask, - 'labels': input_labels.labels, - 'label_ids': input_labels.label_ids, - 'speed': input_labels.speed, - 'acceleration': input_labels.acceleration, - # Fill the following in. - 'source_ids': source_ids, - 'difficulties': input_labels.single_frame_detection_difficulties, - 'unfiltered_bboxes_3d_mask': input_labels.unfiltered_bboxes_3d_mask, - 'run_segment': input_metadata.run_segment, - 'run_start_offset': input_metadata.run_start_offset, - 'pose': input_metadata.pose, - }) - if p.draw_visualizations: - laser_sample = self._SampleLaserForVisualization( - input_batch.lasers.points_xyz, input_batch.lasers.points_padding) - ret.update(laser_sample) - return ret - - def PostProcessDecodeOut(self, dec_out_dict, dec_metrics_dict): - """Post-processes the decoder outputs.""" - p = self.params - # Update num_samples_in_batch. - batch_size, num_classes, num_boxes, _ = ( - dec_out_dict.per_class_predicted_bboxes.shape) - dec_metrics_dict.num_samples_in_batch.Update(batch_size) - - # Update decoder output by removing z-coordinate, thus reshaping the bboxes - # to [batch, num_bboxes, 5] to be compatible with - # TopDownVisualizationMetric. - - # Indices corresponding to the 2D bbox parameters (x, y, dx, dy, phi). - bbox_2d_idx = np.asarray([1, 1, 0, 1, 1, 0, 1], dtype=np.bool) - bboxes_2d = dec_out_dict.bboxes_3d[..., bbox_2d_idx] - predicted_bboxes = dec_out_dict.per_class_predicted_bboxes[..., bbox_2d_idx] - - if p.draw_visualizations and dec_out_dict.points_sampled: - tf.logging.info('Updating sample for top down visualization') - dec_metrics_dict.mesh.Update( - py_utils.NestedMap({ - 'points_xyz': dec_out_dict.points_xyz, - 'points_padding': dec_out_dict.points_padding, - })) - - # Flatten our predictions/scores to match the API of the visualization - # The last dimension of flattened_bboxes is 5 due to the mask - # above using bbox_2d_idx. - flattened_bboxes = np.reshape(predicted_bboxes, - [batch_size, num_classes * num_boxes, 5]) - flattened_visualization_weights = np.reshape( - dec_out_dict.visualization_weights, - [batch_size, num_classes * num_boxes]) - - # Create a label id mask for now to maintain compatibility. - # TODO(bencaine): Refactor visualizations to reflect new structure. - flattened_visualization_labels = np.tile( - np.arange(0, num_classes)[np.newaxis, :, np.newaxis], - [batch_size, 1, num_boxes]) - flattened_visualization_labels = np.reshape( - flattened_visualization_labels, [batch_size, num_classes * num_boxes]) - dec_metrics_dict.top_down_visualization.Update( - py_utils.NestedMap({ - 'visualization_labels': flattened_visualization_labels, - 'predicted_bboxes': flattened_bboxes, - 'visualization_weights': flattened_visualization_weights, - 'points_xyz': dec_out_dict.points_xyz, - 'points_padding': dec_out_dict.points_padding, - 'gt_bboxes_2d': bboxes_2d, - 'gt_bboxes_2d_weights': dec_out_dict.bboxes_3d_mask, - 'labels': dec_out_dict.labels, - 'difficulties': dec_out_dict.difficulties, - 'source_ids': dec_out_dict.source_ids, - })) - - # Update AP metrics. - - # Skip zeroth step decoding. - if dec_out_dict.global_step == 0: - return None - - # TODO(bencaine/vrv): Refactor to unify Waymo code and KITTI - - # Returned values are saved in model_dir/decode_* directories. - output_to_save = [] - - for batch_idx in range(batch_size): - pred_bboxes = dec_out_dict.per_class_predicted_bboxes[batch_idx] - pred_bbox_scores = dec_out_dict.per_class_predicted_bbox_scores[batch_idx] - - # The current API expects a 'height' matrix to be passed for filtering - # detections based on height. This is a KITTI-ism that we need to remove, - # but for now we just give a height of 1. The MinHeight metadata function - # for non-KITTI datasets should have a threshold lower than this value. - heights = np.ones((num_classes, num_boxes)).astype(np.float32) - - gt_mask = dec_out_dict.bboxes_3d_mask[batch_idx].astype(bool) - gt_labels = dec_out_dict.labels[batch_idx][gt_mask] - gt_bboxes = dec_out_dict.bboxes_3d[batch_idx][gt_mask] - gt_difficulties = dec_out_dict.difficulties[batch_idx][gt_mask] - gt_num_points = dec_out_dict.num_points_in_bboxes[batch_idx][gt_mask] - # Note that this is not used in the KITTI evaluation. - gt_speed = dec_out_dict.speed[batch_idx][gt_mask] - - # TODO(shlens): Update me - for metric_key in self._update_metrics_class_keys: - metric_cls = dec_metrics_dict[metric_key] - metric_cls.Update( - dec_out_dict.source_ids[batch_idx], - py_utils.NestedMap( - groundtruth_labels=gt_labels, - groundtruth_bboxes=gt_bboxes, - groundtruth_difficulties=gt_difficulties, - groundtruth_num_points=gt_num_points, - groundtruth_speed=gt_speed, - detection_scores=pred_bbox_scores, - detection_boxes=pred_bboxes, - detection_heights_in_pixels=heights, - )) - - # We still want to save all ground truth (even if it was filtered - # in some way) so we use the unfiltered_bboxes_3d_mask here. - gt_save_mask = dec_out_dict.unfiltered_bboxes_3d_mask[batch_idx].astype( - bool) - pd_save_mask = dec_out_dict.per_class_valid_mask[batch_idx] > 0 - class_ids = np.tile(np.arange(num_classes)[:, np.newaxis], [1, num_boxes]) - - saved_results = py_utils.NestedMap( - pose=dec_out_dict.pose[batch_idx], - frame_id=dec_out_dict.source_ids[batch_idx], - bboxes=pred_bboxes[pd_save_mask], - scores=pred_bbox_scores[pd_save_mask], - gt_labels=dec_out_dict.labels[batch_idx][gt_save_mask], - gt_label_ids=dec_out_dict.label_ids[batch_idx][gt_save_mask], - gt_speed=dec_out_dict.speed[batch_idx][gt_save_mask], - gt_acceleration=dec_out_dict.acceleration[batch_idx][gt_save_mask], - class_ids=class_ids[pd_save_mask], - gt_bboxes=dec_out_dict.bboxes_3d[batch_idx][gt_save_mask], - gt_difficulties=dec_out_dict.difficulties[batch_idx][gt_save_mask], - ) - - if p.save_residuals: - # The leading shapes of these tensors should match bboxes and scores. - # These are the underlying tensors that can are used to compute score - # and bboxes. - saved_results.update({ - 'bboxes_gt_residuals': - dec_out_dict.per_class_gt_residuals[batch_idx][pd_save_mask], - 'bboxes_gt_labels': - dec_out_dict.per_class_gt_labels[batch_idx][pd_save_mask], - 'bboxes_residuals': - dec_out_dict.per_class_residuals[batch_idx][pd_save_mask], - 'bboxes_logits': - dec_out_dict.per_class_logits[batch_idx][pd_save_mask], - 'bboxes_anchor_boxes': - dec_out_dict.per_class_anchor_boxes[batch_idx][pd_save_mask], - }) - - serialized = self.SaveTensors(saved_results) - output_to_save += [(dec_out_dict.source_ids[batch_idx], serialized)] - return output_to_save diff --git a/lingvo/tasks/car/waymo/waymo_metadata.py b/lingvo/tasks/car/waymo/waymo_metadata.py deleted file mode 100644 index 3c626df8..00000000 --- a/lingvo/tasks/car/waymo/waymo_metadata.py +++ /dev/null @@ -1,92 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Metadata for Waymo dataset employed in evaluation.""" - -from lingvo.tasks.car import evaluation_metadata - - -class WaymoMetadata(evaluation_metadata.EvaluationMetadata): - """Metadata describing the Waymo dataset used for evaluation. - - Some of these entries need to be revisited and set correctly for - the final version of the Waymo dataset (e.g., MaximumDistance) - based off an analysis of the dataset. - """ - - def __init__(self): - super().__init__('waymo') - - def ClassNames(self): - """Returns a list of human-interpretable strings.""" - return ['Unknown', 'Vehicle', 'Pedestrian', 'Sign', 'Cyclist'] - - def DifficultyLevels(self): - """Dictionary of difficulty level strings to int32 indices.""" - return {'UNKNOWN': 0, 'LEVEL_1': 1, 'LEVEL_2': 2} - - def IoUThresholds(self): - return { - 'Vehicle': 0.7, - 'Pedestrian': 0.5, - 'Cyclist': 0.5, - } - - def EvalClassIndices(self): - """List of int32 indices for the classes that should be evaled.""" - return [ - self.ClassNames().index(name) - for name in ['Vehicle', 'Pedestrian', 'Cyclist'] - ] - - def IgnoreClassIndices(self): - """Dictionary of int32 indices for the classes that should be ignored.""" - # A detection that matches with a groundtruth bbox of any neighbor class - # will not be considered as false positive in eval. - return {} - - def NumberOfPrecisionRecallPoints(self): - """Number of points on the precision-recall curve.""" - return 101 - - def MaximumDistance(self): - """Maximum empirically observed Euclidean distance in world coordinates.""" - # TODO(vrv): Compute the actual value. - return 80.0 - - def DistanceBinWidth(self): - """The width of each bin for Euclidean distance in world coordinates.""" - return 5.0 - - def MaximumNumberOfPoints(self): - """Maximum empirically observed number of points in bounding box.""" - # TODO(vrv): Compute the actual value. - return 30000.0 - - def NumberOfPointsBins(self): - """Number of logarithmically space bins for examining points.""" - return 20 - - def NumberOfRotationBins(self): - """Number of linear spaced rotations to bin.""" - return 10 - - def NumberOfCalibrationBins(self): - """Number of linear spaced calibration bins.""" - return 15 - - def MinHeight2D(self): - """Minimum height of detections to be evaluated.""" - return {level: 0 for level in self.DifficultyLevels()} diff --git a/lingvo/tasks/car/waymo/waymo_open_input_generator.py b/lingvo/tasks/car/waymo/waymo_open_input_generator.py deleted file mode 100644 index 8bfe9405..00000000 --- a/lingvo/tasks/car/waymo/waymo_open_input_generator.py +++ /dev/null @@ -1,1000 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Input generator for waymo open dataset (WaymoOD).""" - -from lingvo import compat as tf -from lingvo.core import datasource -from lingvo.core import hyperparams -from lingvo.core import py_utils -from lingvo.tasks.car import input_extractor -from lingvo.tasks.car import input_preprocessors - -import numpy as np - - -def _Dense(sparse, default_value=0): - return tf.sparse_to_dense( - sparse_indices=sparse.indices, - output_shape=sparse.dense_shape, - sparse_values=sparse.values, - default_value=default_value) - - -def _NestedMapToParams(nmap): - p = hyperparams.Params() - for k, v in nmap.FlattenItems(): - p.Define(k, v, '') - return p - - -class WaymoFrameMetadataExtractor(input_extractor.FieldsExtractor): - """Extracts per frame metadata from a WaymoOD tf.Example. - - Emits: - pose: [4, 4] - A float Tensor with the 4x4 transformation matrix for - converting from "world" coordinates SDC coordinates. - - run_segment: string scalar - The run segment identifier. - - run_start_offset: int64 scalar - Offset of this scene from the start of the - run segment (in microseconds). - - time_of_day: string scalar - Categorical description of time of day, - e.g., "Day". - - location: string scalar - Categorical description of geographical location, - e.g., "location_sf". - - weather: string scalar - Categorical description of weather of scene, - e.g., "sunny". - """ - - # Valid options for metadata that we can use for validation - # Filters that aren't in this list will still be allowed, but these will - # be checked for extra safety. - VALIDATED_FILTER_OPTIONS = py_utils.NestedMap( - time_of_day=['Day', 'Dawn/Dusk', 'Night'], - # Generated test data uses 'rain', so we keep the word 'rain' for - # backwards compatibility. Eventually we should converge on using 'rainy'. - weather=['rain', 'rainy', 'sunny', 'unknown'], - location=[ - 'location_sf', 'location_phx', 'location_kir', 'location_other' - ]) - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'equality_filters', None, 'A list of tuples(str, list) ' - 'where each first value is a metadata key (e.g. `weather`) ' - 'and the second value is a list of valid values to filter for. ' - 'Each filter will check whether the value of a given example ' - 'for that metadata key matches one of the allowed filter values. ' - 'Then the result of each filter (each tuple) will be AND-ed ' - 'together. Example usage would be: ' - '[("location", ["location_sf"]), ("weather", ["sunny"])] ' - 'Which would only allow through examples that are in SF ' - 'AND have sunny weather. ') - return p - - def _ValidateFilterValues(self): - """Check the filter against several blessed values.""" - p = self.params - for filter_key, filter_values in p.equality_filters: - # Type check - if (not isinstance(filter_key, str) or - not isinstance(filter_values, list)): - raise ValueError('Each element in `equality_filters` must be a ' - 'tuple of (str, list).') - # If it's not one of the "blessed" validated options, just let it through - if filter_key not in self.VALIDATED_FILTER_OPTIONS: - continue - - # If we do know its valid options, check each value against this list - valid_options = self.VALIDATED_FILTER_OPTIONS[filter_key] - for filter_value in filter_values: - if filter_value not in valid_options: - raise ValueError( - 'Filter {} value: {} not in valid options: {}'.format( - filter_key, filter_value, valid_options)) - - def __init__(self, params): - super().__init__(params) - p = self.params - - if p.equality_filters: - if not isinstance(p.equality_filters, list): - raise ValueError('`equality_filters` param must be a list.') - if not all([isinstance(val, tuple) for val in p.equality_filters]): - raise ValueError('Every item in `equality_filters` must be a tuple.') - self._ValidateFilterValues() - - def FeatureMap(self): - """Return a dictionary from tf.Example feature names to Features.""" - feature_map = {} - feature_map['pose'] = tf.io.VarLenFeature(dtype=tf.float32) - feature_map['run_segment'] = tf.io.FixedLenFeature((), tf.string, '') - feature_map['run_start_offset'] = tf.io.FixedLenFeature((), tf.int64, 0) - feature_map['time_of_day'] = tf.io.FixedLenFeature((), tf.string, '') - feature_map['location'] = tf.io.FixedLenFeature((), tf.string, '') - feature_map['weather'] = tf.io.FixedLenFeature((), tf.string, '') - return feature_map - - def _Extract(self, features): - """Extract data into Tensor format.""" - vehicle_pose = tf.reshape(_Dense(features['pose']), [4, 4]) - run_segment = features['run_segment'] - run_start_offset = features['run_start_offset'] - time_of_day = features['time_of_day'] - location = features['location'] - weather = features['weather'] - return py_utils.NestedMap( - pose=vehicle_pose, - run_segment=run_segment, - run_start_offset=run_start_offset, - time_of_day=time_of_day, - location=location, - weather=weather) - - def Shape(self): - """The expected shape of each field.""" - return py_utils.NestedMap( - pose=tf.TensorShape([4, 4]), - run_segment=tf.TensorShape([]), - run_start_offset=tf.TensorShape([]), - time_of_day=tf.TensorShape([]), - location=tf.TensorShape([]), - weather=tf.TensorShape([])) - - def DType(self): - """The Dtype of each field.""" - return py_utils.NestedMap( - pose=tf.float32, - run_segment=tf.string, - run_start_offset=tf.int64, - time_of_day=tf.string, - location=tf.string, - weather=tf.string) - - def Filter(self, outputs): - """Optionally filters the data based on context info.""" - p = self.params - if p.equality_filters is None: - return 1 - - allowed_example = tf.convert_to_tensor(True) - for filter_key, filter_values in p.equality_filters: - if filter_key not in outputs: - raise ValueError( - 'Filter key `{}` not found in extracted data.'.format(filter_key)) - has_allowed_data = tf.reduce_any( - tf.equal(outputs[filter_key], filter_values)) - allowed_example = tf.math.logical_and(allowed_example, has_allowed_data) - - not_allowed_example = 1 - tf.cast(allowed_example, tf.int32) - return 1 + (not_allowed_example * input_extractor.BUCKET_UPPER_BOUND) - - -class WaymoImageExtractor(input_extractor.FieldsExtractor): - """Extracts the camera image data from a WaymoOD tf.Example. - - The cameras are [FRONT, FRONT_LEFT, FRONT_RIGHT, SIDE_LEFT, SIDE_RIGHT]. - - Emits dictionary, where each camera is a key (camera name) and the value is - a NestedMap containing: - - image: [height, width, 3] - Images from the corresponding cameras. - - intrinsics: [9] - Instrinsics of the camera. - - extrinsics: [4, 4] - Extrinsics of the camera - - pose: [4, 4] - Pose of the camera when the corresponding image is taken. - - velocity: [6] - Velocity of the camera when the corresponding image is - taken. The first three numbers (vx, vy, vz) are velocities in world frame, - in m/s. The last three numbers (roll, pitch, yaw) are the rotation rates - in vehicle frame, in rad/s. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('camera_names', - ['FRONT', 'FRONT_LEFT', 'FRONT_RIGHT', 'SIDE_LEFT', 'SIDE_RIGHT'], - 'The names of the cameras from which images will be extracted.') - p.Define('image_shape', [1280, 1920, 3], - 'The shape that images are cropped to.') - return p - - def FeatureMap(self): - """Return a dictionary from tf.Example feature names to Features.""" - p = self.params - features = {} - features['pose'] = tf.io.VarLenFeature(dtype=tf.float32) - - for camera_name in p.camera_names: - features['image_%s' % camera_name] = tf.io.VarLenFeature(dtype=tf.string) - features['image_%s_shape' % camera_name] = ( - tf.io.VarLenFeature(dtype=tf.int64)) - features['camera_%s_intrinsics' % - camera_name] = tf.io.VarLenFeature(dtype=tf.float32) - features['camera_%s_extrinsics' % - camera_name] = tf.io.VarLenFeature(dtype=tf.float32) - - features['camera_%s_rolling_shutter_direction' % - camera_name] = tf.io.FixedLenFeature( - dtype=tf.int64, shape=()) - features['image_%s_pose' % - camera_name] = tf.io.VarLenFeature(dtype=tf.float32) - features['image_%s_velocity' % - camera_name] = tf.io.VarLenFeature(dtype=tf.float32) - - for feat in [ - 'pose_timestamp', 'shutter', 'camera_trigger_time', - 'camera_readout_done_time' - ]: - features['image_%s_%s' % (camera_name, feat)] = tf.io.FixedLenFeature( - dtype=tf.float32, shape=()) - return features - - def _Extract(self, features): - """Returns the image Tensor.""" - outputs = py_utils.NestedMap() - p = self.params - for camera_name in p.camera_names: - image_shape = tf.reshape( - _Dense(features['image_%s_shape' % camera_name]), [-1]) - image = tf.io.decode_png( - tf.strings.reduce_join( - _Dense(features['image_%s' % camera_name], default_value=''))) - image = tf.reshape(image, image_shape) - image = py_utils.PadOrTrimTo(image, p.image_shape) - intrinsics = tf.reshape( - _Dense(features['camera_%s_intrinsics' % camera_name]), [9]) - extrinsics = tf.reshape( - _Dense(features['camera_%s_extrinsics' % camera_name]), [4, 4]) - pose = tf.reshape(_Dense(features['image_%s_pose' % camera_name]), [4, 4]) - velocity = tf.reshape( - _Dense(features['image_%s_velocity' % camera_name]), [6]) - - outputs[camera_name] = py_utils.NestedMap() - outputs[camera_name]['image'] = image - outputs[camera_name]['intrinsics'] = intrinsics - outputs[camera_name]['extrinsics'] = extrinsics - outputs[camera_name]['pose'] = pose - outputs[camera_name]['velocity'] = velocity - outputs[camera_name]['rolling_shutter_direction'] = features[ - 'camera_%s_rolling_shutter_direction' % camera_name] - - for feat in [ - 'shutter', 'camera_trigger_time', 'camera_readout_done_time', - 'pose_timestamp' - ]: - outputs[camera_name][feat] = features['image_%s_%s' % - (camera_name, feat)] - - return outputs - - def Shape(self): - """Shape of images.""" - p = self.params - shapes = py_utils.NestedMap() - for camera_name in p.camera_names: - shapes[camera_name] = py_utils.NestedMap() - shapes[camera_name]['image'] = tf.TensorShape(p.image_shape) - # 1d Array of [f_u, f_v, c_u, c_v, k{1, 2}, p{1, 2}, k{3}]. - # Note that this intrinsic corresponds to the images after scaling. - # Camera model: pinhole camera. - # Lens distortion: - # Radial distortion coefficients: k1, k2, k3. - # Tangential distortion coefficients: p1, p2. - # k_{1, 2, 3}, p_{1, 2} follows the same definition as OpenCV. - shapes[camera_name]['intrinsics'] = tf.TensorShape([9]) - shapes[camera_name]['extrinsics'] = tf.TensorShape([4, 4]) - shapes[camera_name]['pose'] = tf.TensorShape([4, 4]) - shapes[camera_name]['velocity'] = tf.TensorShape([6]) - for feat in [ - 'pose_timestamp', 'shutter', 'camera_trigger_time', - 'camera_readout_done_time' - ]: - shapes[camera_name][feat] = tf.TensorShape([]) - shapes[camera_name]['rolling_shutter_direction'] = tf.TensorShape([]) - - return shapes - - def DType(self): - """Dtypes of images.""" - p = self.params - dtypes = py_utils.NestedMap() - for camera_name in p.camera_names: - dtypes[camera_name] = py_utils.NestedMap() - dtypes[camera_name]['image'] = tf.uint8 - dtypes[camera_name]['intrinsics'] = tf.float32 - dtypes[camera_name]['extrinsics'] = tf.float32 - dtypes[camera_name]['pose'] = tf.float32 - dtypes[camera_name]['velocity'] = tf.float32 - for feat in [ - 'pose_timestamp', 'shutter', 'camera_trigger_time', - 'camera_readout_done_time' - ]: - dtypes[camera_name][feat] = tf.float32 - dtypes[camera_name]['rolling_shutter_direction'] = tf.int64 - return dtypes - - -class WaymoLaserExtractor(input_extractor.LaserExtractor): - """Extracts the raw laser data from a WaymoOD tf.Example.""" - - @classmethod - def Params(cls): - p = super().Params().Set(max_num_points=None, num_features=3) - p.Define('lidar_names', ['TOP', 'SIDE_LEFT', 'SIDE_RIGHT', 'FRONT', 'REAR'], - 'The names of the lidars from which lasers will be extracted.') - p.Define( - 'lidar_returns', ['ri1', 'ri2'], 'Which return from the LiDAR to ' - 'extract when we merge the point cloud.') - return p - - def FeatureMap(self): - """Return a dictionary from tf.Example feature names to Features.""" - p = self.params - features = {} - for lidar in p.lidar_names: - for ri in p.lidar_returns: - features['laser_%s_%s' % - (lidar, ri)] = tf.io.VarLenFeature(dtype=tf.float32) - return features - - def _Extract(self, features): - """Returns the laser Tensor.""" - p = self.params - all_xyzs = [] - all_laser_features = [] - - for lidar in p.lidar_names: - for ri in p.lidar_returns: - feature_name = 'laser_%s_%s' % (lidar, ri) - laser_data = tf.reshape( - _Dense(features[feature_name]), [-1, 3 + p.num_features]) - points_xyz = laser_data[..., 0:3] - points_feature = laser_data[..., 3:] - - all_xyzs += [points_xyz] - all_laser_features += [points_feature] - - # Stack all of the points along the major dimension - points_xyz = tf.concat(all_xyzs, axis=0) - points_feature = tf.concat(all_laser_features, axis=0) - - if p.max_num_points is not None: - npoints = tf.shape(points_xyz)[0] - points_xyz = py_utils.PadOrTrimTo(points_xyz, [p.max_num_points, 3]) - points_feature = py_utils.PadOrTrimTo(points_feature, - [p.max_num_points, p.num_features]) - points_padding = 1.0 - py_utils.PadOrTrimTo( - tf.ones([npoints]), [p.max_num_points]) - - ret = py_utils.NestedMap( - points_xyz=points_xyz, points_feature=points_feature) - if p.max_num_points is not None: - ret.points_padding = points_padding - return ret - - -class WaymoLabelExtractor(input_extractor.FieldsExtractor): - """Extracts the bounding box and label info from a WaymoOD tf.Example. - - Emits: - labels: [p.max_num_objects] - Integer label for each bounding box object - corresponding to the index in car.open_dataset.Label.Type (shifted by 1 to - have 0 represent the background class). - - label_ids: [p.max_num_objects] - String unique identifier for each labeled - object on a per run_segment basis. This can be used for associating - objects across frames (over time). - - detection_difficulties: [p.max_num_objects] - DO NOT USE FOR EVALUATION. - The per-box difficulty level for detection task as defined in - car.open_dataset.Label.DifficultyLevel. This is the human raters - difficulty level, which does NOT include information about the number - of points per box. Therefore, it is an incomplete definition of difficulty - and will not correspond to the leaderboard if used to calculate metrics. - - single_frame_detection_difficulties: [p.max_num_objects] - The per-box - difficulty level derived via both detection_difficulties (labeler defined) - and metric defined (number of points in box). - - tracking_difficulties: [p.max_num_objects] - The per-box difficulty level - for tracking task as defined in car.open_dataset.Label.DifficultyLevel. - - bboxes_3d: [p.max_num_objects, 7] - 3D bounding box data in [x, y, z, l, w, - h, heading] format. x, y, z are the object center in world coordinates; - l, w, h are the dimensions of the box, and heading is the rotation angle - around the z-axis. See car.open_dataset.Label.Box for definitions. - - bboxes_3d_mask: [p.max_num_objects] - Mask for all the above tensors (mask - is the inversion of padding). - - bboxes_3d_num_points: [p.max_num_objects] - Integer for each box indicating - how many points are in that ground truth box. - - unfiltered_bboxes_3d_mask: [p.max_num_objects] - The mask before filtering - out bboxes whose labels are not in p.filter_labels. - - speed: [p.max_num_objects, 2] - The object speed in x, y. - - acceleration: [p.max_num_objects, 2] - The object acceleration in x, y. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('max_num_objects', 512, - 'Each frame may contain up to these many bbox.') - p.Define( - 'filter_labels', [], 'Specifies a list of label ' - 'indices to keep. If empty, no filtering is done.') - return p - - def FeatureMap(self): - """Return a dictionary from tf.Example feature names to Features.""" - feature_map = {} - feature_map['labels'] = tf.io.VarLenFeature(dtype=tf.int64) - feature_map['label_ids'] = tf.io.VarLenFeature(dtype=tf.string) - feature_map['detection_difficulties'] = tf.io.VarLenFeature(dtype=tf.int64) - feature_map['single_frame_detection_difficulties'] = tf.io.VarLenFeature( - dtype=tf.int64) - feature_map['tracking_difficulties'] = tf.io.VarLenFeature(dtype=tf.int64) - feature_map['bboxes_3d'] = tf.io.VarLenFeature(dtype=tf.float32) - feature_map['bboxes_3d_num_points'] = tf.io.VarLenFeature(dtype=tf.int64) - feature_map['label_metadata'] = tf.io.VarLenFeature(dtype=tf.float32) - return feature_map - - def _Extract(self, features): - p = self.params - # Label values match the proto enum car.open_dataset.Label.Type. The value - # range is [1..4] for non-background labels. - labels = tf.cast(_Dense(features['labels']), tf.int32) - labels = py_utils.PadOrTrimTo(labels, [p.max_num_objects]) - label_ids = tf.reshape(_Dense(features['label_ids'], ''), [-1]) - label_ids = py_utils.PadOrTrimTo(label_ids, [p.max_num_objects], '') - bboxes_3d = tf.reshape(_Dense(features['bboxes_3d']), [-1, 7]) - bboxes_3d_mask = tf.ones([tf.shape(bboxes_3d)[0]]) - bboxes_3d_num_points = tf.cast( - _Dense(features['bboxes_3d_num_points']), tf.int32) - bboxes_3d = py_utils.PadOrTrimTo(bboxes_3d, [p.max_num_objects, 7]) - bboxes_3d_mask = py_utils.PadOrTrimTo(bboxes_3d_mask, [p.max_num_objects]) - bboxes_3d_num_points = py_utils.PadOrTrimTo(bboxes_3d_num_points, - [p.max_num_objects]) - label_metadata = tf.reshape(_Dense(features['label_metadata']), [-1, 4]) - label_metadata = py_utils.PadOrTrimTo(label_metadata, - [p.max_num_objects, 4]) - - detection_difficulties = py_utils.PadOrTrimTo( - tf.cast(_Dense(features['detection_difficulties']), tf.int32), - [p.max_num_objects]) - single_frame_detection_difficulties = py_utils.PadOrTrimTo( - tf.cast( - _Dense(features['single_frame_detection_difficulties']), tf.int32), - [p.max_num_objects]) - tracking_difficulties = py_utils.PadOrTrimTo( - tf.cast(_Dense(features['tracking_difficulties']), tf.int32), - [p.max_num_objects]) - unfiltered_bboxes_3d_mask = bboxes_3d_mask - - if p.filter_labels: - valid_labels = tf.constant([p.filter_labels]) - bbox_mask = tf.reduce_any( - tf.equal(tf.expand_dims(labels, 1), valid_labels), axis=1) - bboxes_3d_mask *= tf.cast(bbox_mask, tf.float32) - - outputs = { - 'labels': - labels, - 'label_ids': - label_ids, - 'detection_difficulties': - detection_difficulties, - 'single_frame_detection_difficulties': - single_frame_detection_difficulties, - 'tracking_difficulties': - tracking_difficulties, - 'bboxes_3d': - bboxes_3d, - 'bboxes_3d_mask': - bboxes_3d_mask, - 'bboxes_3d_num_points': - bboxes_3d_num_points, - 'unfiltered_bboxes_3d_mask': - unfiltered_bboxes_3d_mask, - 'speed': - label_metadata[:, :2], - 'acceleration': - label_metadata[:, 2:], - } - - return py_utils.NestedMap(outputs) - - def Shape(self): - """Shape of BBoxes.""" - p = self.params - shapes = { - 'labels': - tf.TensorShape([p.max_num_objects]), - 'label_ids': - tf.TensorShape([p.max_num_objects]), - 'detection_difficulties': - tf.TensorShape([p.max_num_objects]), - 'single_frame_detection_difficulties': - tf.TensorShape([p.max_num_objects]), - 'tracking_difficulties': - tf.TensorShape([p.max_num_objects]), - 'bboxes_3d': - tf.TensorShape([p.max_num_objects, 7]), - 'bboxes_3d_mask': - tf.TensorShape([p.max_num_objects]), - 'bboxes_3d_num_points': - tf.TensorShape([p.max_num_objects]), - 'unfiltered_bboxes_3d_mask': - tf.TensorShape([p.max_num_objects]), - 'speed': - tf.TensorShape([p.max_num_objects, 2]), - 'acceleration': - tf.TensorShape([p.max_num_objects, 2]) - } - return py_utils.NestedMap(shapes) - - def DType(self): - """Dtypes of BBoxes.""" - dtypes = py_utils.NestedMap() - dtypes.labels = tf.int32 - dtypes.label_ids = tf.string - dtypes.detection_difficulties = tf.int32 - dtypes.single_frame_detection_difficulties = tf.int32 - dtypes.tracking_difficulties = tf.int32 - dtypes.bboxes_3d = tf.float32 - dtypes.bboxes_3d_mask = tf.float32 - dtypes.bboxes_3d_num_points = tf.int32 - dtypes.unfiltered_bboxes_3d_mask = tf.float32 - dtypes.speed = tf.float32 - dtypes.acceleration = tf.float32 - return dtypes - - -class RangeImageExtractor(input_extractor.FieldsExtractor): - """Extracts the range images from a Waymo OD tf.Example. - - The outputs contain the following: - - Let ri_shape = [H, W] of the corresponding range image. - - - For every short range laser (params.cbr_laser_names): - For every return (params.returns): - $LASERNAME_RETURN: - .xyz - tf.float32 of ri_shape + [3] - - .features - tf.float32 of ri_shape + [4] - - .mask - tf.float32 of ri_shape indicating whether the laser - xyz and feature at each coordinate is real or padded. A coordinate - has a real point iff the mask is set to 1. - - $LASERNAME_beam_inclinations: tf.float32 [2] listing the - min and max beam inclinations. - - $LASERNAME_extrinsics: tf.float32 [4, 4] extrinsics matrix. - - - For every longer range laser (params.gbr_laser_names): - For every return (params.returns): - .xyz: tf.float32 of ri_shape + [3] - .features: tf.float32 of ri_shape + [4] - .mask: tf.float32 of ri_shape - - $LASERNAME_beam_inclinations: tf.float32 [64] listing the - non-uniform beam inclinations for the longer range laser. - - $LASERNAME_extrinsics: tf.float32 [4, 4] extrinsics matrix - - $LASERNAME_pose: tf.float32 of ri_shape + [4, 4], which is the - per-pixel pose. - - On laser returns: - ri1 and ri2 are the first and second returns of the sensors. - - On laser sensors: - If there are 5 total sensors, there will be 5 * len(returns) - outputs. - - The last dimension of range image is 4, indicating the following - features: - - range: (if entry is -1, it means there is no laser value there). - - intensity - - elongation - - 1. if laser point entry in 'no label zone', 0. otherwise. - - The xyz range image output is a [H, W, 3] Tensor indicating the cartesian - coordinates corresponding to each range image pixel in the range image. One - should use the mask computed from the 'range' channel of the range image to - select only the points that exist. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'cbr_laser_names', ['SIDE_LEFT', 'FRONT', 'REAR', 'SIDE_RIGHT'], - 'The names of the CBR sensors from which range images ' - 'will be extracted.') - p.Define('cbr_ri_shape', [200, 600, 4], 'Shape of each CBR range image.') - p.Define( - 'gbr_laser_names', ['TOP'], - 'The names of the GBR sensors from which range images ' - 'will be extracted.') - p.Define( - 'returns', ['ri1', 'ri2'], - 'The names of the laser returns to export. E.g., ri1 is ' - 'the first return, ri2 is the second return.') - p.Define('gbr_ri_shape', [64, 2650, 4], 'Shape of each GBR range image.') - return p - - def FeatureMap(self): - """Return a dictionary from tf.Example feature names to Features.""" - p = self.params - feature_map = {} - for laser in p.cbr_laser_names + p.gbr_laser_names: - feature_map['%s_beam_inclinations' % laser] = ( - tf.io.VarLenFeature(dtype=tf.float32)) - feature_map['%s_beam_inclination_min' % laser] = ( - tf.io.VarLenFeature(dtype=tf.float32)) - feature_map['%s_beam_inclination_max' % laser] = ( - tf.io.VarLenFeature(dtype=tf.float32)) - feature_map['%s_extrinsics' % - laser] = tf.io.VarLenFeature(dtype=tf.float32) - if laser in p.gbr_laser_names: - feature_map['%s_pose' % laser] = tf.io.VarLenFeature(dtype=tf.float32) - - for returns in p.returns: - feature_map['%s_%s' % - (laser, returns)] = tf.io.VarLenFeature(dtype=tf.float32) - feature_map['%s_%s_shape' % - (laser, returns)] = tf.io.VarLenFeature(dtype=tf.int64) - feature_map['pose'] = tf.io.VarLenFeature(dtype=tf.float32) - return feature_map - - def _Extract(self, features): - p = self.params - ri_outputs = {} - outputs = {} - frame_pose = tf.reshape(_Dense(features['pose']), [4, 4]) - for laser in p.cbr_laser_names + p.gbr_laser_names: - # Extract range images. - for returns in p.returns: - ri_shape = tf.reshape( - _Dense(features['%s_%s_shape' % (laser, returns)]), [-1]) - range_image = tf.reshape( - _Dense(features['%s_%s' % (laser, returns)]), ri_shape) - - shape_to_check = ( - p.cbr_ri_shape if laser in p.cbr_laser_names else p.gbr_ri_shape) - range_image = py_utils.HasShape(range_image, shape_to_check) - - ri_outputs['%s_%s' % (laser, returns)] = range_image - - # Extract beam inclinations and extrinsics - outputs['%s_extrinsics' % laser] = tf.reshape( - _Dense(features['%s_extrinsics' % laser]), [4, 4]) - - # CBRs have uniform inclination - for laser in p.cbr_laser_names: - beam_inclination_min = tf.reshape( - _Dense(features['%s_beam_inclination_min' % laser]), []) - beam_inclination_max = tf.reshape( - _Dense(features['%s_beam_inclination_max' % laser]), []) - outputs['%s_beam_inclinations' % laser] = tf.stack( - [beam_inclination_min, beam_inclination_max], axis=0) - - # GBRs have non-uniform inclinations defined by 64 floats. - for laser in p.gbr_laser_names: - outputs['%s_beam_inclinations' % laser] = tf.reshape( - _Dense(features['%s_beam_inclinations' % laser]), [64]) - - # Embed xyz onto each range image pixel. - for laser in p.cbr_laser_names + p.gbr_laser_names: - extrinsics = outputs['%s_extrinsics' % laser] - inclinations = outputs['%s_beam_inclinations' % laser] - if laser in p.cbr_laser_names: - ri_shape = p.cbr_ri_shape - - # Convert from 2-tuple range inclination to the full range - # via linear interpolation. - # - # CBR lasers currently are always uniform inclinations specified by a - # length 2 vector. - height = ri_shape[0] - min_inclination = inclinations[0] - max_inclination = inclinations[1] - diff = max_inclination - min_inclination - ratio = (.5 + tf.cast(tf.range(0, height), tf.float32)) / tf.cast( - height, tf.float32) - # interpolate from min to max inclination. - inclinations = (ratio * diff) + min_inclination - else: - ri_shape = p.gbr_ri_shape - - pixel_pose = None - if laser in p.gbr_laser_names: - pixel_pose = tf.reshape( - _Dense(features['%s_pose' % laser]), - shape=p.gbr_ri_shape[0:2] + [4, 4]) - outputs['%s_pose' % laser] = pixel_pose - - for returns in p.returns: - range_image = ri_outputs['%s_%s' % (laser, returns)] - range_image = tf.reshape(range_image, ri_shape) - range_image_mask = range_image[..., 0] >= 0 - ri_xyz = tf.cast( - self._XYZFromRangeImage(range_image, range_image_mask, extrinsics, - inclinations, pixel_pose, frame_pose), - tf.float32) - - # Produce the NestedMap of xyz, features, mask. - ri_result = py_utils.NestedMap({ - 'xyz': ri_xyz, - 'features': range_image, - 'mask': tf.cast(range_image_mask, tf.float32), - }) - - outputs['%s_%s' % (laser, returns)] = ri_result - - return py_utils.NestedMap(outputs) - - def _XYZFromRangeImage(self, - lidar_image, - lidar_image_mask, - extrinsics, - inclinations, - pixel_pose=None, - frame_pose=None): - """Extract the cartesian coordinates from the range image. - - Args: - lidar_image: [H, W, C] range image Tensor. - lidar_image_mask: [H, W] boolean indicating which 2d coordinates in the - lidar image are present. - extrinsics: [4, 4] float matrix representing transformation matrix to - world coordinates. - inclinations: [V] beam inclinations vector. - pixel_pose: [64, 2650, 4, 4] tensor representing per pixel pose of GBR. - frame_pose: [4, 4] matrix representing vehicle to world transformation. - - Returns: - [H, W, 3] range image cartesian coordinates. - """ - height, width, channels = py_utils.GetShape(lidar_image, 3) - - conversion_dtype = tf.float32 - lidar_image = tf.cast(lidar_image, conversion_dtype) - extrinsics = tf.cast(extrinsics, conversion_dtype) - inclinations = tf.cast(inclinations, conversion_dtype) - inclinations = tf.reverse(inclinations, axis=[-1]) - - az_correction = py_utils.HasShape( - tf.atan2(extrinsics[1, 0], extrinsics[0, 0]), []) - ratios = (tf.cast(tf.range(width, 0, -1), dtype=conversion_dtype) - - .5) / tf.cast(width, conversion_dtype) - ratios = py_utils.HasShape(ratios, [width]) - - azimuth = (ratios * 2. - 1.) * np.pi - az_correction[..., tf.newaxis] - azimuth = py_utils.HasShape(azimuth, [width]) - - lidar_image_mask = lidar_image_mask[..., tf.newaxis] - lidar_image_mask = tf.tile(lidar_image_mask, [1, 1, channels]) - lidar_image = tf.where(lidar_image_mask, lidar_image, - tf.zeros_like(lidar_image)) - lidar_image_range = lidar_image[..., 0] - - azimuth = py_utils.HasShape(azimuth[tf.newaxis, ...], [1, width]) - inclinations = py_utils.HasShape(inclinations[..., tf.newaxis], [height, 1]) - - cos_azimuth = tf.cos(azimuth) - sin_azimuth = tf.sin(azimuth) - cos_incl = tf.cos(inclinations) - sin_incl = tf.sin(inclinations) - - x = cos_azimuth * cos_incl * lidar_image_range - y = sin_azimuth * cos_incl * lidar_image_range - z = sin_incl * lidar_image_range - - lidar_image_points = tf.stack([x, y, z], -1) - lidar_image_points = py_utils.HasShape(lidar_image_points, - [height, width, 3]) - rotation = extrinsics[0:3, 0:3] - translation = extrinsics[0:3, 3][tf.newaxis, ...] - - # Transform the image points in cartesian coordinates to - # the world coordinate system using the extrinsics matrix. - # - # We first flatten the points, apply rotation, then - # reshape to restore the original input and then apply - # translation. - lidar_image_points = tf.matmul( - tf.reshape(lidar_image_points, [-1, 3]), rotation, transpose_b=True) - lidar_image_points = tf.reshape(lidar_image_points, [height, width, 3]) - lidar_image_points += translation - - lidar_image_points = py_utils.HasShape(lidar_image_points, - [height, width, 3]) - # GBR uses per pixel pose. - if pixel_pose is not None: - pixel_pose_rotation = pixel_pose[..., 0:3, 0:3] - pixel_pose_translation = pixel_pose[..., 0:3, 3] - lidar_image_points = tf.einsum( - 'hwij,hwj->hwi', pixel_pose_rotation, - lidar_image_points) + pixel_pose_translation - if frame_pose is None: - raise ValueError('frame_pose must be set when pixel_pose is set.') - # To vehicle frame corresponding to the given frame_pose - # [4, 4] - world_to_vehicle = tf.linalg.inv(frame_pose) - world_to_vehicle_rotation = world_to_vehicle[0:3, 0:3] - world_to_vehicle_translation = world_to_vehicle[0:3, 3] - # [H, W, 3] - lidar_image_points = tf.einsum( - 'ij,hwj->hwi', world_to_vehicle_rotation, - lidar_image_points) + world_to_vehicle_translation[tf.newaxis, - tf.newaxis, :] - - return lidar_image_points - - def Shape(self): - """Shape of BBoxes.""" - p = self.params - shapes = {} - for laser in p.cbr_laser_names: - cbr_shape = p.cbr_ri_shape[:-1] - for returns in p.returns: - shape_dict = py_utils.NestedMap({ - 'xyz': tf.TensorShape(cbr_shape + [3]), - 'features': tf.TensorShape(cbr_shape + [4]), - 'mask': tf.TensorShape(cbr_shape), - }) - shapes['%s_%s' % (laser, returns)] = shape_dict - - shapes['%s_extrinsics' % laser] = tf.TensorShape([4, 4]) - shapes['%s_beam_inclinations' % laser] = tf.TensorShape([2]) - for laser in p.gbr_laser_names: - gbr_shape = p.gbr_ri_shape[:-1] - for returns in p.returns: - shape_dict = py_utils.NestedMap({ - 'xyz': tf.TensorShape(gbr_shape + [3]), - 'features': tf.TensorShape(gbr_shape + [4]), - 'mask': tf.TensorShape(gbr_shape), - }) - shapes['%s_%s' % (laser, returns)] = shape_dict - shapes['%s_extrinsics' % laser] = tf.TensorShape([4, 4]) - shapes['%s_beam_inclinations' % laser] = tf.TensorShape([64]) - shapes['%s_pose' % laser] = tf.TensorShape(gbr_shape + [4, 4]) - - return py_utils.NestedMap(shapes) - - def DType(self): - """Dtypes of BBoxes.""" - p = self.params - dtypes = {} - for laser in p.cbr_laser_names + p.gbr_laser_names: - for returns in p.returns: - dtype_dict = py_utils.NestedMap({ - 'xyz': tf.float32, - 'features': tf.float32, - 'mask': tf.float32, - }) - dtypes['%s_%s' % (laser, returns)] = dtype_dict - dtypes['%s_extrinsics' % laser] = tf.float32 - dtypes['%s_beam_inclinations' % laser] = tf.float32 - for laser in p.gbr_laser_names: - dtypes['%s_pose' % laser] = tf.float32 - return py_utils.NestedMap(dtypes) - - -class FilterNLZPoints(input_preprocessors.Preprocessor): - """Filters points that are in no-label-zones. - - This preprocessor expects features to contain the following keys: - - lasers.points_xyz of shape [P, 3] - - lasers.points_feature of shape [P, F] - - Modifies the following features: - - lasers.points_xyz of shape [P2, 3] - - lasers.points_feature of shape [P2, F] - - where P - P2 are the number of points dropped because the corresponding - point was in a no-label-zone. - """ - - @classmethod - def Params(cls): - p = super().Params() - return p - - def TransformFeatures(self, features): - # We assume that the lasers are not padded, and all points are real. - if ('points_padding' in features.lasers and - features.lasers.points_padding is not None): - raise ValueError('FilterNLZPoints preprocessor does not support ' - 'padded lasers.') - - # The 3rd feature in the laser is 1.0 for points in a no-label-zone - # and -1. for normal points. - is_not_nlz = tf.not_equal(features.lasers.points_feature[:, 2], 1.0) - features.lasers.points_xyz = tf.boolean_mask(features.lasers.points_xyz, - is_not_nlz) - features.lasers.points_feature = tf.boolean_mask( - features.lasers.points_feature, is_not_nlz) - return features - - def TransformShapes(self, shapes): - return shapes - - def TransformDTypes(self, dtypes): - return dtypes - - -class WaymoSparseLaser(input_extractor.BaseExtractor): - """Sparse laser input extractor for Waymo dataset.""" - - @classmethod - def Params(cls): - """Defaults params.""" - extractors = hyperparams.Params() - extractors.Define('lasers', WaymoLaserExtractor.Params(), '') - extractors.Define('labels', WaymoLabelExtractor.Params(), '') - extractors.Define('metadata', WaymoFrameMetadataExtractor.Params(), '') - - preprocessors = py_utils.NestedMap( - count_points=input_preprocessors.CountNumberOfPointsInBoxes3D.Params(), - viz_copy=input_preprocessors.CreateDecoderCopy.Params(), - keep_xyz_range=input_preprocessors.DropLaserPointsOutOfRange.Params(), - filter_nlz_points=FilterNLZPoints.Params(), - select_centers=input_preprocessors.SparseCenterSelector.Params(), - gather_features=input_preprocessors.SparseCellGatherFeatures.Params(), - tile_anchors=input_preprocessors.TileAnchorBBoxes.Params(), - assign_anchors=input_preprocessors.AnchorAssignment.Params(), - pad_lasers=input_preprocessors.PadLaserFeatures.Params().Set(), - ) - p = super().Params(extractors).Set( - preprocessors=_NestedMapToParams(preprocessors), - preprocessors_order=[ - 'viz_copy', - 'keep_xyz_range', - 'filter_nlz_points', - 'count_points', - 'select_centers', - 'gather_features', - 'tile_anchors', - 'assign_anchors', - 'pad_lasers', - ], - ) - - p.file_datasource = datasource.PrefixedDataSource.Params() - p.file_datasource.file_type = 'tfrecord' - - return p diff --git a/lingvo/tasks/car/waymo/waymo_open_input_generator_test.py b/lingvo/tasks/car/waymo/waymo_open_input_generator_test.py deleted file mode 100644 index 64101dc0..00000000 --- a/lingvo/tasks/car/waymo/waymo_open_input_generator_test.py +++ /dev/null @@ -1,53 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for WaymoOpenInputGenerator.""" - -from lingvo import compat as tf -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.car.waymo import waymo_open_input_generator -import numpy as np - -FLAGS = tf.flags.FLAGS - - -class WaymoOpenInputGeneratorTest(test_utils.TestCase): - - def testFilterNLZ(self): - num_points = 32 - num_features = 3 - points_feature = np.random.rand(num_points, num_features).astype(np.float32) - # Mark one point as being in nlz. - points_feature[-1, 2] = 1. - lasers_np = py_utils.NestedMap( - points_xyz=np.random.rand(num_points, 3).astype(np.float32), - points_feature=points_feature) - features_np = py_utils.NestedMap(lasers=lasers_np) - features = features_np.Transform(tf.constant) - - preprocessor_p = waymo_open_input_generator.FilterNLZPoints.Params() - processor = preprocessor_p.Instantiate() - - features = processor.TransformFeatures(features) - with self.session(): - actual_features = self.evaluate(features) - # one point dropped because it was in nlz - self.assertEqual((num_points - 1, 3), - actual_features.lasers.points_xyz.shape) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/image/BUILD b/lingvo/tasks/image/BUILD deleted file mode 100644 index 684d52d3..00000000 --- a/lingvo/tasks/image/BUILD +++ /dev/null @@ -1,64 +0,0 @@ -# Image related tasks. -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "classifier", - srcs = ["classifier.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_model", - "//lingvo/core:layers", - "//lingvo/core:metrics", - "//lingvo/core:plot", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - # Implicit numpy dependency. - ], -) - -py_test( - name = "classifier_test", - srcs = ["classifier_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":classifier", - ":input_generator", - "//lingvo:compat", - "//lingvo:model_registry", - "//lingvo/core:cluster_factory", - "//lingvo/core:layers", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - "//lingvo/tasks/image/params:mnist", - # Implicit numpy dependency. - ], -) - -py_library( - name = "input_generator", - srcs = ["input_generator.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_input_generator", - ], -) - -py_test( - name = "input_generator_test", - srcs = ["input_generator_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":input_generator", - "//lingvo:compat", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) diff --git a/lingvo/tasks/image/__init__.py b/lingvo/tasks/image/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/image/classifier.py b/lingvo/tasks/image/classifier.py deleted file mode 100644 index c8e82176..00000000 --- a/lingvo/tasks/image/classifier.py +++ /dev/null @@ -1,335 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Image classification models.""" - -import lingvo.compat as tf -from lingvo.core import base_model -from lingvo.core import layers -from lingvo.core import metrics -from lingvo.core import plot -from lingvo.core import py_utils -from lingvo.core import schedule -import numpy as np - - -def TopKAccuracy(k, logits, labels, weights): - """Compute top-k accuracy. - - Args: - k: An int scalar. Top-k. - logits: A [N, C] float tensor. - labels: A [N] int vector. - weights: A [N] float vector. - - Returns: - A float scalar. The accuracy at precision k. - """ - logits = py_utils.HasRank(logits, 2) - n, _ = tf.unstack(tf.shape(logits), 2) - labels = py_utils.HasShape(labels, [n]) - weights = py_utils.HasShape(weights, [n]) - correct = tf.nn.in_top_k(targets=labels, predictions=logits, k=k) - return tf.reduce_sum(tf.cast(correct, weights.dtype) * weights) / tf.maximum( - 1e-8, tf.reduce_sum(weights)) - - -class BaseClassifier(base_model.BaseTask): - """Base class for image classifier.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('softmax', layers.SimpleFullSoftmax.Params(), 'Softmax layer.') - p.Define('add_image_summary', True, 'If True, adds image summary in ' - 'evaler/decoder jobs.') - return p - - def _AddSummary(self, batch, prediction): - """Adds image summaries for the batch.""" - p = self.params - if not self.do_eval or not p.add_image_summary: - # Image summaries only works in evaler/decoder. - return - - def Draw(fig, axes, img, label, pred): - plot.AddImage( - fig=fig, - axes=axes, - data=img[:, :, 0] / 256., - show_colorbar=False, - suppress_xticks=True, - suppress_yticks=True) - axes.text( - x=0.5, - y=0, - s=u'%d vs. %d' % (label, pred), - transform=axes.transAxes, - horizontalalignment='center') - - with plot.MatplotlibFigureSummary( - 'examples', figsize=(1, 1), max_outputs=10) as fig: - fig.AddSubplot([batch.raw, batch.label, prediction], Draw) - - def _Accuracy(self, k, logits, labels, weights): - """Compute top-k accuracy. - - Args: - k: An int scalar. Top-k. - logits: A [N, C] float tensor. - labels: A [N] int vector. - weights: A [N] float vector. - - Returns: - A float scalar. The accuracy at precision k. - """ - return TopKAccuracy(k, logits, labels, weights) - - -class ModelV1(BaseClassifier): - """CNNs with maxpooling followed by a softmax.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'filter_shapes', [(0, 0, 0, 0)], - 'Conv filter shapes. Must be a list of sequences of 4. ' - 'Elements are in order of height, width, in_channel, out_channel') - p.Define( - 'window_shapes', [(0, 0)], - 'Max pooling window shapes. Must be a list of sequences of 2. ' - 'Elements are in order of height, width.') - p.Define('batch_norm', False, 'Apply BN or not after the conv.') - p.Define('dropout_prob', 0.0, - 'Probability of the dropout applied after pooling.') - - tp = p.train - tp.learning_rate = 1e-4 # Adam base LR. - tp.lr_schedule = ( - schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params() - .Set(warmup=100, decay_start=100000, decay_end=1000000, min=0.1)) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - - assert len(p.filter_shapes) == len(p.window_shapes) - - # A few conv + max pooling layers. - shape = [None] + list(p.input.data_shape) - conv_params = [] - pooling_params = [] - for i, (kernel, window) in enumerate(zip(p.filter_shapes, p.window_shapes)): - conv_params.append(layers.ConvLayer.Params().Set( - name='conv%d' % i, - filter_shape=kernel, - filter_stride=(1, 1), - batch_norm=p.batch_norm)) - pooling_params.append(layers.PoolingLayer.Params().Set( - name='pool%d' % i, window_shape=window, window_stride=window)) - self.CreateChildren('conv', conv_params) - self.CreateChildren('pool', pooling_params) - - # Logs expected activation shapes. - for i in range(len(self.conv)): - tf.logging.info('shape %d %s', i, shape) - shape = self.conv[i].OutShape(shape) - tf.logging.info('shape %d %s', i, shape) - shape = self.pool[i].OutShape(shape) - tf.logging.info('shape %s', shape) - - # FC layer to project down to p.softmax.input_dim. - self.CreateChild( - 'fc', - layers.FCLayer.Params().Set( - name='fc', - input_dim=np.prod(shape[1:]), - output_dim=p.softmax.input_dim)) - self.CreateChild('softmax', p.softmax) - - def FPropTower(self, theta, input_batch): - p = self.params - batch = tf.shape(input_batch.data)[0] - height, width, depth = p.input.data_shape - act = tf.reshape(input_batch.data, [batch, height, width, depth]) - for i in range(len(self.conv)): - # Conv, BN (optional) - act, _ = self.conv[i].FProp(theta.conv[i], act) - # MaxPool - act, _ = self.pool[i].FProp(theta.pool[i], act) - # Dropout (optional) - if p.dropout_prob > 0.0 and not self.do_eval: - act = tf.nn.dropout(act, rate=p.dropout_prob, seed=p.random_seed) - # FC - act = self.fc.FProp(theta.fc, tf.reshape(act, [batch, -1])) - - # Softmax - labels = tf.cast(input_batch.label, tf.int64) - xent = self.softmax.FProp( - theta=theta.softmax, - inputs=act, - class_weights=input_batch.weight, - class_ids=labels) - - self._AddSummary(input_batch, xent.per_example_argmax) - - rets = { - 'loss': (xent.avg_xent, batch), - 'log_pplx': (xent.avg_xent, batch), - 'num_preds': (batch, 1), - } - if self.do_eval: - acc1 = self._Accuracy(1, xent.logits, labels, input_batch.weight) - acc5 = self._Accuracy(5, xent.logits, labels, input_batch.weight) - rets.update( - accuracy=(acc1, batch), - acc5=(acc5, batch), - error=(1. - acc1, batch), - error5=(1. - acc5, batch)) - return rets, {'loss': xent.per_example_xent} - - def Decode(self, input_batch): - with tf.name_scope('decode'): - return self.FPropDefaultTheta(input_batch)[0] - - def CreateDecoderMetrics(self): - return { - 'num_samples_in_batch': metrics.AverageMetric(), - } - - def PostProcessDecodeOut(self, dec_out_dict, dec_metrics_dict): - dec_metrics_dict['num_samples_in_batch'].Update( - dec_out_dict['num_samples_in_batch'][0]) - - -class ModelV2(BaseClassifier): - """CNNs followed by a softmax.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('extract', None, 'Param for the layer to extract image features.') - p.Define('label_smoothing', 0., 'Smooth the labels towards 1/num_classes.') - p.Define('compute_accuracy_for_training', False, - 'Whether to compute accuracy for training.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.name - - self.CreateChild('extract', p.extract) - self.CreateChild('softmax', p.softmax) - - def ComputePredictions(self, theta, input_batch): - # Forward through layers. - act = self.extract.FProp(theta.extract, input_batch.data) - # Avg pool - act = tf.reduce_mean(act, axis=[1, 2]) - logits = self.softmax.Logits(theta.softmax, act) - return py_utils.NestedMap(act=act, logits=logits) - - def ComputeLoss(self, theta, predictions, input_batch): - p = self.params - batch = tf.shape(input_batch.data)[0] - act = predictions.act - with tf.ops.colocate_with(act): - tf.logging.info("{}'s device: {}".format(act, act.device)) - # Softmax - labels = tf.cast(input_batch.label, tf.int64) - onehot_labels = tf.one_hot(labels, p.softmax.num_classes) - if p.label_smoothing > 0: - smooth_positives = 1.0 - p.label_smoothing - smooth_negatives = p.label_smoothing / p.softmax.num_classes - onehot_labels = onehot_labels * smooth_positives + smooth_negatives - - xent = self.softmax.FProp( - theta=theta.softmax, - inputs=act, - class_weights=input_batch.weight, - class_probabilities=onehot_labels) - - self._AddSummary(input_batch, xent.per_example_argmax) - - rets = { - 'loss': (xent.avg_xent, batch), - 'log_pplx': (xent.avg_xent, batch), - 'num_preds': (batch, 1), - } - if self.do_eval or p.compute_accuracy_for_training: - acc1 = self._Accuracy(1, xent.logits, labels, input_batch.weight) - acc5 = self._Accuracy(5, xent.logits, labels, input_batch.weight) - rets.update( - accuracy=(acc1, batch), - acc5=(acc5, batch), - error=(1. - acc1, batch), - error5=(1. - acc5, batch)) - return rets, {'loss': xent.per_example_xent} - - def Inference(self): - """Constructs inference subgraphs. - - Returns: - dict: A dictionary of the form ``{'subgraph_name': (fetches, feeds)}``. - Each of fetches and feeds is itself a dictionary which maps a string name - (which describes the tensor) to a corresponding tensor in the inference - graph which should be fed/fetched from. - """ - subgraphs = {} - with tf.name_scope('inference'): - subgraphs['default'] = self._InferenceSubgraph_Default() - return subgraphs - - def _InferenceSubgraph_Default(self): - """Constructs graph for single-image inference. - - Returns: - (fetches, feeds) where both fetches and feeds are dictionaries. Each - dictionary consists of keys corresponding to tensor names, and values - corresponding to a tensor in the graph which should be input/read from. - """ - p = self.params - with tf.name_scope('default'): - normalized_image = tf.placeholder( - dtype=p.dtype, shape=p.input.data_shape, name='normalized_image') - inputs = py_utils.NestedMap(data=normalized_image[tf.newaxis, ...]) - logits = tf.reshape( - self.ComputePredictions(self.theta, inputs).logits, - [p.softmax.num_classes], - name='logits') - feeds = { - 'normalized_image': normalized_image, - } - fetches = { - 'logits': logits, - 'probs': tf.nn.softmax(logits, name='probs'), - 'prediction': tf.argmax(logits, name='prediction'), - } - return fetches, feeds - - def DecodeWithTheta(self, theta, input_batch): - """Constructs the decode graph for decoding with theta.""" - predictions = self.ComputePredictions(theta, input_batch) - labels = tf.cast(input_batch.label, tf.int64) - ret = py_utils.NestedMap() - ret.correct_top1 = tf.nn.in_top_k( - targets=labels, predictions=predictions.logits, k=1) - ret.correct_top5 = tf.nn.in_top_k( - targets=labels, predictions=predictions.logits, k=5) - return ret diff --git a/lingvo/tasks/image/classifier_test.py b/lingvo/tasks/image/classifier_test.py deleted file mode 100644 index b857bd6b..00000000 --- a/lingvo/tasks/image/classifier_test.py +++ /dev/null @@ -1,126 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for classifier.""" - -from lingvo import model_registry -import lingvo.compat as tf -from lingvo.core import cluster_factory -from lingvo.core import layers -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core.test_utils import CompareToGoldenSingleFloat -from lingvo.tasks.image import classifier -from lingvo.tasks.image import input_generator -from lingvo.tasks.image.params import mnist -import numpy as np - - -@model_registry.RegisterSingleTaskModel -class MnistV2(mnist.Base): - """A test MNIST model for classifier.ModelV2.""" - - @classmethod - def Task(cls): - p = classifier.ModelV2.Params() - p.name = 'testv2' - p.extract = layers.Conv2DLayerNoPadding.Params().Set( - filter_shape=(5, 5, 1, 50), filter_stride=(2, 2)) - p.label_smoothing = 0.1 - p.softmax.input_dim = 50 - p.softmax.num_classes = 10 - p.train.learning_rate = 0.1 - return p - - -class ClassifierTest(test_utils.TestCase): - - def setUp(self): - self.data_path = input_generator.FakeMnistData( - self.get_temp_dir(), train_size=0) - - def _runOneStep(self, model): - f_loss = self.evaluate(model.GetTask().loss) - self.evaluate(model.GetTask().train_op) - return f_loss - - def testMnistLeNet5(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(1618) - p = model_registry.GetParams('image.mnist.LeNet5', 'Test') - p.random_seed = 73234288 - p.input.ckpt = self.data_path - p.task.params_init = py_utils.WeightInit.Uniform(0.1, seed=73234288) - with cluster_factory.ForTestingWorker(mode='sync', job='trainer_client'): - model = p.Instantiate() - model.ConstructFPropBPropGraph() - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - CompareToGoldenSingleFloat(self, 2.302583, self._runOneStep(model)) - CompareToGoldenSingleFloat(self, 2.302405, self._runOneStep(model)) - - def testMnistV2(self): - g = tf.Graph() - with g.as_default(): - tf.random.set_seed(1618) - p = model_registry.GetParams('test.MnistV2', 'Test') - p.random_seed = 73234288 - p.input.ckpt = self.data_path - p.task.params_init = py_utils.WeightInit.Uniform(0.1, seed=73234288) - with cluster_factory.ForTestingWorker(mode='sync', job='trainer_client'): - model = p.Instantiate() - model.ConstructFPropBPropGraph() - with self.session(graph=g): - self.evaluate(tf.global_variables_initializer()) - CompareToGoldenSingleFloat(self, 2.302583, self._runOneStep(model)) - CompareToGoldenSingleFloat(self, 2.142516, self._runOneStep(model)) - - def testInference(self): - with self.session() as sess: - tf.random.set_seed(1618) - p = model_registry.GetParams('test.MnistV2', 'Test') - p.random_seed = 73234288 - p.input.ckpt = self.data_path - p.task.params_init = py_utils.WeightInit.Uniform(0.1, seed=73234288) - model = p.Instantiate() - subgraphs = model.GetTask().Inference() - self.assertCountEqual(['default'], list(subgraphs.keys())) - fetches, feeds = subgraphs['default'] - self.assertCountEqual(['normalized_image'], list(feeds.keys())) - self.assertCountEqual(['logits', 'probs', 'prediction'], - list(fetches.keys())) - self.evaluate(tf.global_variables_initializer()) - fetch_results = sess.run( - fetches, {feeds['normalized_image']: np.zeros(p.input.data_shape)}) - self.assertAllEqual([p.task.softmax.num_classes], - fetch_results['logits'].shape) - self.assertAllEqual([p.task.softmax.num_classes], - fetch_results['probs'].shape) - self.assertAllEqual([], fetch_results['prediction'].shape) - - def testDecodeRuns(self): - g = tf.Graph() - with g.as_default(): - p = model_registry.GetParams('test.MnistV2', 'Test') - model = p.Instantiate() - task = model.GetTask() - input_batch = task.GetInputBatch()[0] - result = task.Decode(input_batch) - self.assertIn('correct_top1', result) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/image/input_generator.py b/lingvo/tasks/image/input_generator.py deleted file mode 100644 index 21601fd9..00000000 --- a/lingvo/tasks/image/input_generator.py +++ /dev/null @@ -1,87 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Input generator for image data.""" - -import os -import lingvo.compat as tf -from lingvo.core import base_input_generator - -from tensorflow.python.ops import io_ops - - -class _MnistInputBase(base_input_generator.BaseTinyDatasetInput): - """Base input params for MNIST.""" - - @classmethod - def Params(cls): - """Defaults params.""" - p = super().Params() - p.data_dtype = tf.uint8 - p.data_shape = (28, 28, 1) - p.label_dtype = tf.uint8 - return p - - def _Preprocess(self, raw): - data = tf.stack([ - tf.image.per_image_standardization(img) for img in tf.unstack(raw) - ]) - data.set_shape(raw.shape) - return data - - -class MnistTrainInput(_MnistInputBase): - """MNist training set.""" - - @classmethod - def Params(cls): - """Defaults params.""" - p = super().Params() - p.data = 'x_train' - p.label = 'y_train' - p.num_samples = 60000 - p.batch_size = 256 - p.repeat = True - return p - - -class MnistTestInput(_MnistInputBase): - """MNist test set.""" - - @classmethod - def Params(cls): - """Defaults params.""" - p = super().Params() - p.data = 'x_test' - p.label = 'y_test' - p.num_samples = 10000 - p.batch_size = 256 - p.repeat = False - return p - - -def FakeMnistData(tmpdir, train_size=60000, test_size=10000): - """Fake Mnist data for unit tests.""" - data_path = os.path.join(tmpdir, 'ckpt') - with tf.Graph().as_default(): - with tf.Session() as sess: - x_train = tf.ones((train_size, 28, 28, 1), dtype=tf.uint8) - y_train = tf.ones((train_size), dtype=tf.uint8) - x_test = tf.ones((test_size, 28, 28, 1), dtype=tf.uint8) - y_test = tf.ones((test_size), dtype=tf.uint8) - sess.run( - io_ops.save_v2(data_path, ['x_train', 'y_train', 'x_test', 'y_test'], - [''] * 4, [x_train, y_train, x_test, y_test])) - return data_path diff --git a/lingvo/tasks/image/input_generator_test.py b/lingvo/tasks/image/input_generator_test.py deleted file mode 100644 index 5979d4a8..00000000 --- a/lingvo/tasks/image/input_generator_test.py +++ /dev/null @@ -1,99 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for input_generator.""" - -import lingvo.compat as tf -from lingvo.core import test_utils -from lingvo.tasks.image import input_generator -import numpy as np - - -class InputGeneratorTest(test_utils.TestCase): - - def setUp(self): - self.data_path = input_generator.FakeMnistData(self.get_temp_dir()) - - def _trainInput(self): - p = input_generator.MnistTrainInput.Params() - p.ckpt = self.data_path - p.batch_size = 100 - return p - - def _testInput(self): - p = input_generator.MnistTestInput.Params() - p.ckpt = self.data_path - return p - - def testMnistTrain(self): - p = self._trainInput() - with self.session(): - inp = p.Instantiate() - inp_batch = inp.GetPreprocessedInputBatch() - for _ in range(10): - batch = self.evaluate(inp_batch) - self.assertEqual(batch.data.shape, (100, 28, 28, 1)) - self.assertEqual(batch.data.dtype, np.float32) - self.assertEqual(batch.label.shape, (100,)) - self.assertEqual(batch.label.dtype, np.float32) - - def testMnistTest(self): - p = self._testInput() - with self.session(): - inp = p.Instantiate() - inp_batch = inp.GetPreprocessedInputBatch() - ids = [] - for _ in range(39): - batch = self.evaluate(inp_batch) - self.assertEqual(batch.data.shape, (256, 28, 28, 1)) - self.assertEqual(batch.data.dtype, np.float32) - self.assertEqual(batch.label.shape, (256,)) - self.assertEqual(batch.label.dtype, np.float32) - ids += batch.sample_ids.tolist() - batch = self.evaluate(inp_batch) - self.assertEqual(batch.data.shape, (256, 28, 28, 1)) - self.assertEqual(batch.data.dtype, np.float32) - self.assertEqual(batch.label.shape, (256,)) - self.assertEqual(batch.label.dtype, np.float32) - ids += batch.sample_ids.tolist() - self.assertEqual(list(range(p.num_samples)), - sorted(ids)) # Exactly 1 epoch. - - # repeat=False. We should see OutOfRange error. - with self.assertRaises(tf.errors.OutOfRangeError): - _ = self.evaluate(inp_batch) - - def _GetIds(self, p, sample_ids): - """Goes through one epoch of inp and returns the sample ids.""" - iters = int((p.num_samples + p.batch_size - 1) / p.batch_size) - ids = [] - for _ in range(iters): - ids += self.evaluate(sample_ids).tolist() - return ids - - def testMnistTrainRandomness(self): - p = self._trainInput() - with self.session(): - inp = p.Instantiate() - batch = inp.GetPreprocessedInputBatch() - epoch0 = self._GetIds(p, batch.sample_ids) - epoch1 = self._GetIds(p, batch.sample_ids) - self.assertEqual(list(range(p.num_samples)), sorted(epoch0)) - self.assertEqual(list(range(p.num_samples)), sorted(epoch1)) - self.assertNotEqual(epoch0, epoch1) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/image/params/BUILD b/lingvo/tasks/image/params/BUILD deleted file mode 100644 index 807b56c5..00000000 --- a/lingvo/tasks/image/params/BUILD +++ /dev/null @@ -1,28 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "mnist", - srcs = ["mnist.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:model_registry", - "//lingvo/core:base_model_params", - "//lingvo/core:program_lib", - "//lingvo/tasks/image:classifier", - "//lingvo/tasks/image:input_generator", - ], -) - -py_library( - name = "params", - srcs = ["params.py"], - srcs_version = "PY3", - tags = ["keep_dep"], - deps = [ - ":mnist", - ], -) diff --git a/lingvo/tasks/image/params/mnist.py b/lingvo/tasks/image/params/mnist.py deleted file mode 100644 index 273d821f..00000000 --- a/lingvo/tasks/image/params/mnist.py +++ /dev/null @@ -1,66 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Train models on MNIST data.""" - -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.tasks.image import classifier -from lingvo.tasks.image import input_generator - - -class Base(base_model_params.SingleTaskModelParams): - """Input params for MNIST.""" - - @property - def path(self): - # Generated using lingvo/tools:keras2ckpt. - return '/tmp/mnist/mnist' - - def Train(self): - p = input_generator.MnistTrainInput.Params() - p.ckpt = self.path - return p - - def Test(self): - p = input_generator.MnistTestInput.Params() - p.ckpt = self.path - return p - - def Dev(self): - return self.Test() - - -@model_registry.RegisterSingleTaskModel -class LeNet5(Base): - """LeNet params for MNIST classification.""" - - BN = False - DROP = 0.2 - - def Task(self): - p = classifier.ModelV1.Params() - p.name = 'lenet5' - # Overall architecture: - # conv, maxpool, conv, maxpool, fc, softmax. - p.filter_shapes = [(5, 5, 1, 20), (5, 5, 20, 50)] - p.window_shapes = [(2, 2), (2, 2)] - p.batch_norm = self.BN - p.dropout_prob = self.DROP - p.softmax.input_dim = 300 - p.softmax.num_classes = 10 - p.train.save_interval_seconds = 10 # More frequent checkpoints. - p.eval.samples_per_summary = 0 # Eval the whole set. - return p diff --git a/lingvo/tasks/image/params/params.py b/lingvo/tasks/image/params/params.py deleted file mode 100644 index 907e9ff9..00000000 --- a/lingvo/tasks/image/params/params.py +++ /dev/null @@ -1,21 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Image models hyper-parameters.""" - -# Import ModelParams to ensure that they are added to the global registry. -# pylint: disable=unused-import -import lingvo.tasks.image.params.mnist -# pylint: enable=unused-import diff --git a/lingvo/tasks/lm/BUILD b/lingvo/tasks/lm/BUILD deleted file mode 100644 index 6572e026..00000000 --- a/lingvo/tasks/lm/BUILD +++ /dev/null @@ -1,104 +0,0 @@ -load( - "//lingvo:lingvo.bzl", - "lingvo_cuda_py_test", -) - -# Language modelling related tasks. -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "input_generator", - srcs = ["input_generator.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:generic_input", - "//lingvo/core:py_utils", - "//lingvo/core:tokenizers", - ], -) - -py_test( - name = "input_generator_test", - srcs = ["input_generator_test.py"], - data = [ - "//lingvo/tasks/lm/testdata:lm1b_100", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":input_generator", - "//lingvo:compat", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "layers", - srcs = ["layers.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:batch_major_attention", - "//lingvo/core:layers", - "//lingvo/core:layers_with_attention", - "//lingvo/core:layers_with_gpipe", - "//lingvo/core:py_utils", - "//lingvo/core:rnn_cell", - "//lingvo/core:rnn_layers", - ], -) - -lingvo_cuda_py_test( - name = "layers_test", - srcs = ["layers_test.py"], - python_version = "PY3", - shard_count = 10, - tags = ["noasan"], - deps = [ - ":layers", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "model", - srcs = ["model.py"], - srcs_version = "PY3", - deps = [ - ":layers", - "//lingvo:compat", - "//lingvo/core:base_model", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - ], -) - -py_test( - name = "model_test", - srcs = ["model_test.py"], - data = [ - "//lingvo/tasks/lm/testdata:lm1b_100", - "//lingvo/tasks/lm/testdata:small_word_vocab", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":input_generator", - ":model", - "//lingvo:compat", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - "//lingvo/core:tokenizers", - ], -) diff --git a/lingvo/tasks/lm/__init__.py b/lingvo/tasks/lm/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/lm/input_generator.py b/lingvo/tasks/lm/input_generator.py deleted file mode 100644 index 1adf1cc2..00000000 --- a/lingvo/tasks/lm/input_generator.py +++ /dev/null @@ -1,92 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Language model input generator.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import generic_input -from lingvo.core import py_utils -from lingvo.core import tokenizers - - -class LmInput(base_input_generator.BaseSequenceInputGenerator): - """Reads tokenized plain text input such as from lm1b.""" - - @classmethod - def Params(cls): - """Defaults params for `LmInput`.""" - p = super().Params() - p.Define('fixed_input_shape', False, 'Fixed input shape or not.') - p.tokenizer = tokenizers.AsciiTokenizer.Params() - return p - - def __init__(self, params): - params.pad_to_max_seq_length = True - super().__init__(params) - p = self.params - p.fixed_input_shape = p.fixed_input_shape or py_utils.use_tpu() - - (text, self._word_count), self._bucket_keys = self._BuildDataSource() - self._ids, self._labels, self._paddings = self.StringsToIds(text) - tf.summary.histogram('examples/sequence_length', - tf.reduce_sum(1.0 - self._paddings, axis=1)) - self._weights = 1.0 - self._paddings - if p.fixed_input_shape: - if py_utils.use_tpu(): - # When flush_every_n is on, at end of each epoch, our input - # generator can generate a batch smaller than - # bucket_batch_limit - assert not p.flush_every_n, 'flush_every_n is not allowed on TPU.' - assert min(self.infeed_bucket_batch_limit) == max( - self.infeed_bucket_batch_limit) - bs = min(self.infeed_bucket_batch_limit) - else: - bs = max(self.infeed_bucket_batch_limit) - - def SetShape(x): - x.set_shape([bs, p.target_max_length]) - - SetShape(self._ids) - SetShape(self._labels) - SetShape(self._paddings) - SetShape(self._weights) - self._word_count.set_shape([bs]) - - def InfeedBatchSize(self): - """Override BaseSequenceInputGenerator.""" - return tf.shape(self._ids)[0] - - def _DataSourceFromFilePattern(self, file_pattern): - - def ReadInput(line): - word_count = tf.size(tf.strings.split([line])) - strlen = tf.size(tf.strings.split([line], '')) - return [line, word_count], strlen - - return generic_input.GenericInput( - file_pattern=file_pattern, - processor=ReadInput, - **self.CommonInputOpArgs()) - - def _InputBatch(self): - ret = py_utils.NestedMap() - ret.bucket_key = self._bucket_keys - ret.ids = self._ids - ret.labels = self._labels - ret.paddings = self._paddings - ret.weights = self._weights - ret.word_count = self._word_count - return ret diff --git a/lingvo/tasks/lm/input_generator_test.py b/lingvo/tasks/lm/input_generator_test.py deleted file mode 100644 index 1bc722d4..00000000 --- a/lingvo/tasks/lm/input_generator_test.py +++ /dev/null @@ -1,58 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for lm.input_generator.""" - -import lingvo.compat as tf -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tasks.lm import input_generator - - -class InputGeneratorTest(test_utils.TestCase): - - def _InputParams(self): - p = input_generator.LmInput.Params() - p.file_pattern = "text:" + test_helper.test_src_dir_path( - "tasks/lm/testdata/lm1b_100.txt") - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.file_buffer_size = 1 - p.bucket_upper_bound = [20] - p.bucket_batch_limit = [2] - p.target_max_length = 20 - return p - - def testLmInputGen(self): - p = self._InputParams() - - with self.session(use_gpu=False): - inp = p.Instantiate() - inp_batch = self.evaluate(inp.GetPreprocessedInputBatch()) - print(inp_batch) - # pyformat: disable - # pylint: disable=line-too-long - expected_ids = [ - [1, 13, 24, 3, 23, 5, 13, 8, 3, 24, 12, 5, 24, 3, 24, 27, 19, 3, 19, 10], - [1, 24, 12, 9, 3, 17, 5, 14, 19, 22, 13, 24, 29, 3, 27, 13, 16, 16, 3, 6], - ] - # pylint: enable=line-too-long - # pyformat: enable - self.assertEqual(expected_ids, inp_batch.ids.tolist()) - self.assertEqual([[1.0] * 20, [1.0] * 20], inp_batch.weights.tolist()) - - -if __name__ == "__main__": - tf.test.main() diff --git a/lingvo/tasks/lm/layers.py b/lingvo/tasks/lm/layers.py deleted file mode 100644 index 6cb4204e..00000000 --- a/lingvo/tasks/lm/layers.py +++ /dev/null @@ -1,1322 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Common layers for language models.""" - -import math -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import batch_major_attention -from lingvo.core import layers -from lingvo.core import layers_with_attention -from lingvo.core import layers_with_gpipe -from lingvo.core import py_utils -from lingvo.core import rnn_cell -from lingvo.core import rnn_layers - - -class BaseLanguageModel(base_layer.BaseLayer): - """Abstract base class for a language model layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('vocab_size', 0, 'Number of vocabulary tokens.') - return p - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Sets the params with the given vocab size and wpm model. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model params updated with the vocab size and wpm model. - """ - p.vocab_size = vocab_size - return p - - def zero_state(self, theta, batch_size): - raise NotImplementedError('Abstract method') - - def FProp(self, theta, inputs, paddings, state0, *args, **kwargs): - """Computes xent loss given the language model inputs. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: a tensor of shape [time, batch] or [time, batch, dims]. - paddings: a 0/1 tensor of shape [time, batch]. - state0: A `.NestedMap` containing the initial recurrent state. - *args: optional extra arguments. - **kwargs: optional extra keyword arguments. - - Returns: - (xent_output, state1). `xent_output` is a `.NestedMap` as defined by - `SoftmaxLayer`'s return value and `state1` is the next recurrent state. - """ - raise NotImplementedError('Abstract method') - - def Logits(self, theta, inputs, paddings, *args, **kwargs): - """FProp and returns the logits for the whole sequence.""" - xent_output, _ = self.FProp( - theta, - inputs, - paddings, - state0=self.zero_state(theta, - tf.shape(inputs)[1]), - *args, - **kwargs) - return xent_output.logits - - @classmethod - def StepOutputDimension(cls, params): - """Returns dimensions of `Step()`'s output dimension. - - Args: - params: Params for this layer. - - Returns: - A `.NestedMap` with fields - logits: a python int. - The vocab size. - last_hidden: a python int. - The last hidden layer's dimension. - """ - raise NotImplementedError('Abstract method') - - def Step(self, theta, inputs, paddings, state0, *args, **kwargs): - """FProp one step. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: a tensor of shape [batch] or [batch, dims]. - paddings: a 0/1 tensor of shape [batch]. - state0: A `.NestedMap` containing the initial recurrent state. - *args: optional extra arguments. - **kwargs: optional extra keyword arguments. - - Returns: - A tuple (output, state1). - output: A `.NestedMap` with fields. - logits: - [batch, vocab_size]. - log_probs: - [batch, vocab_size]. - last_hidden: - [batch, dims]. - state1: - The new recurrent state. - """ - - def ExpandTime(x): - return tf.expand_dims(x, axis=0) - - xent_output, state1 = self.FProp( - theta=theta, - inputs=ExpandTime(inputs), - paddings=ExpandTime(paddings), - state0=state0, - *args, - **kwargs) - - output = py_utils.NestedMap() - output.log_probs = tf.squeeze(xent_output.log_probs, axis=0) - output.probs = tf.squeeze(xent_output.probs, axis=0) - output.last_hidden = tf.squeeze(xent_output.last_hidden, axis=0) - if 'logits' in xent_output: - # FstLm doesn't return logits. - output.logits = tf.squeeze(xent_output.logits, axis=0) - return output, state1 - - def GetFeedDict(self): - """Returns an optional feed dict with str keys and Tensor values.""" - return {} - - def CombineStates(self, state0, state1, switch_cond): - """Combines states based on a switch conditional. - - Args: - state0: a NestedMap of states to use for batch elements where switch_cond - is true. - state1: a NestedMap of states to use for batch elements where switch_cond - is false. - switch_cond: bool tensor of shape [batch] on which to switch. - - Returns: - a NestedMap of states. - """ - raise NotImplementedError('Abstract method') - - -class NullLm(BaseLanguageModel): - """A trivial language model does nothing really.""" - - def zero_state(self, theta, batch_size): - return py_utils.NestedMap( - m=tf.zeros([batch_size, 0], dtype=self.params.dtype)) - - def FProp(self, theta, inputs, paddings, state0, *args, **kwargs): - p = self.params - time = tf.shape(inputs)[0] - batch = tf.shape(inputs)[1] - logits = tf.zeros([time, batch, p.vocab_size], dtype=p.dtype) - return py_utils.NestedMap( - logits=logits, - probs=tf.nn.softmax(logits), - log_probs=tf.nn.log_softmax(logits), - last_hidden=tf.zeros([time, batch, 0], dtype=p.dtype)), state0 - - def Logits(self, theta, inputs, paddings, *args, **kwargs): - """FProp and returns the logits for the whole sequence.""" - p = self.params - del theta, paddings - time, batch = tf.unstack(tf.shape(inputs)[:2]) - return tf.zeros([time, batch, p.vocab_size], dtype=p.dtype) - - @classmethod - def StepOutputDimension(cls, params): - """Returns dimensions of `Step()`'s output dimension.""" - return py_utils.NestedMap(logits=params.vocab_size, last_hidden=0) - - def Step(self, theta, inputs, paddings, state0, *args, **kwargs): - """FProp one step.""" - p = self.params - batch = tf.shape(inputs)[0] - logits = tf.zeros([batch, p.vocab_size], dtype=p.dtype) - return py_utils.NestedMap( - logits=logits, - log_probs=tf.nn.log_softmax(logits), - probs=tf.nn.softmax(logits), - last_hidden=tf.zeros([batch, 0], dtype=p.dtype)), state0 - - def CombineStates(self, state0, state1, switch_cond): - """Combines states based on a switch conditional. - - Args: - state0: a NestedMap of states to use for batch elements where switch_cond - is true. - state1: a NestedMap of states to use for batch elements where switch_cond - is false. - switch_cond: bool tensor of shape [batch] on which to switch. - - Returns: - a NestedMap of states. - """ - return state0 - - -def _RnnOutputSize(rnns): - cell = rnns.cell_tpl[-1] - return cell.num_output_nodes - - -def ComputeXentOutput(softmax_layer, - softmax_theta, - activations, - labels, - num_samples=1): - """Compute Softmax CrossEntropy output.""" - seqlen, batch, _ = tf.unstack(tf.shape(activations), num=3) - if labels is None: - # We can only compute the logits here. - logits = softmax_layer.Logits( - theta=softmax_theta, - inputs=tf.reshape(activations, [seqlen * batch * num_samples, -1])) - xent_output = py_utils.NestedMap( - logits=tf.reshape(logits, [seqlen, batch, -1])) - elif 'class_ids' in labels: - # labels.class_ids: [len, batch] - if num_samples > 1: - class_ids = tf.tile(labels.class_ids, [1, num_samples]) - class_weights = tf.tile(labels.class_weights, [1, num_samples]) - else: - class_ids = labels.class_ids - class_weights = labels.class_weights - xent_output = softmax_layer.FProp( - theta=softmax_theta, - inputs=activations, - class_weights=class_weights, - class_ids=class_ids) - else: - assert 'class_probabilities' in labels - if num_samples > 1: - class_probabilities = tf.tile(labels.class_probabilities, - [1, num_samples]) - class_weights = tf.tile(labels.class_weights, [1, num_samples]) - else: - class_probabilities = labels.class_probabilities - class_weights = labels.class_weights - xent_output = softmax_layer.FProp( - theta=softmax_theta, - inputs=activations, - class_weights=class_weights, - class_probabilities=class_probabilities) - return xent_output - - -class RnnLmNoEmbedding(BaseLanguageModel): - """Stacked RNN based language model layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('rnns', rnn_layers.StackedFRNNLayerByLayer.Params(), - 'The stacked-RNNs layer params.') - p.Define('softmax', layers.SimpleFullSoftmax.Params(), - 'The softmax layer params.') - p.Define( - 'direct_features_dim', 0, - 'If > 0, then the number of dimensions of direct features ' - 'that bypass the RNN and are provided directly to the softmax ' - 'input.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if not isinstance(p.rnns.cell_tpl, (list, tuple)): - p.rnns.cell_tpl = [p.rnns.cell_tpl] - p.rnns.allow_implicit_capture = p.allow_implicit_capture - - cell_output_size = _RnnOutputSize(p.rnns) - output_layer_size = cell_output_size + p.direct_features_dim - - if output_layer_size != p.softmax.input_dim: - raise ValueError( - 'Output layer size %d does not match softmax input size %d! ' - 'cell_output_size: %d direct_features_dim: %d ' % - (output_layer_size, p.softmax.input_dim, cell_output_size, - p.direct_features_dim)) - if p.softmax.num_classes != p.vocab_size: - raise ValueError( - 'softmax num of classess %d does not match vocabulary size %d!' % - (p.softmax.num_classes, p.vocab_size)) - - self.CreateChild('rnns', p.rnns) - self.CreateChild('softmax', p.softmax) - - def zero_state(self, theta, batch_size): - return self.rnns.zero_state(theta.rnns, batch_size) - - @classmethod - def StepOutputDimension(cls, params): - return py_utils.NestedMap( - logits=params.vocab_size, last_hidden=params.softmax.input_dim) - - def Step(self, - theta, - inputs, - paddings, - state0, - direct_features=None, - *args, - **kwargs): - """FProp one step. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: a tensor of shape [batch] or [batch, dims]. - paddings: a 0/1 tensor of shape [batch]. - state0: A `.NestedMap` containing the initial recurrent state. - direct_features: If not None, a tensor of [batch, direct_feature_dims] - that is concatenated to the output of the last RNN layer. - *args: optional extra arguments. - **kwargs: optional extra keyword arguments. - - Returns: - A tuple (output, state1). - output: A `.NestedMap` with fields. - logits: - [batch, vocab_size]. - last_hidden: - [batch, dims]. - state1: - The new recurrent state. - """ - - def ExpandTime(x): - return tf.expand_dims(x, axis=0) - - if direct_features is not None: - direct_features = py_utils.HasRank(direct_features, 2) - direct_features = ExpandTime(direct_features) - - xent_output, state1 = self.FProp( - theta=theta, - inputs=ExpandTime(inputs), - paddings=ExpandTime(paddings), - state0=state0, - direct_features=direct_features, - *args, - **kwargs) - - output = py_utils.NestedMap() - output.logits = tf.squeeze(xent_output.logits, axis=0) - output.probs = tf.squeeze(xent_output.probs, axis=0) - output.log_probs = tf.squeeze(xent_output.log_probs, axis=0) - output.last_hidden = tf.squeeze(xent_output.last_hidden, axis=0) - return output, state1 - - def FProp(self, - theta, - inputs, - paddings, - state0, - labels=None, - direct_features=None): - """Computes xent loss given the language model input activations. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: input activation. A tensor of shape [time, batch, dims]. - paddings: a 0/1 tensor of shape [time, batch]. - state0: A `.NestedMap` containing the initial recurrent state. - labels: If not None, a `.NestedMap` containing the following fields. - - - class_weights, a tensor with shape [time, batch] containing the - weights for each target word. - - class_ids, a tensor with shape [time, batch] of int32 dtype containing - the target class labels. - - class_probabilities, a tensor with shape [time, batch, vocab_size] of - float values indicating class-membership probabilities. - direct_features: - If not None, a tensor of [time, batch, direct_feature_dims] that is - concatenated to the output of the last RNN layer. - - Returns: - If `labels` is not None, returns (xent_output, state1), where - `xent_output` is a `.NestedMap` as defined by `SoftmaxLayer`'s return - value and `state1` is the next recurrent state. Otherwise, - `xent_output` contains the softmax logits, probabilities (.probs) and - log-probabilities (.log_probs). - """ - inputs = py_utils.HasRank(inputs, 3) - seqlen, batch, _ = tf.unstack(tf.shape(inputs), num=3) - paddings = py_utils.HasShape(paddings, [seqlen, batch]) - assert state0 is not None - activation, state1 = self.rnns.FProp(theta.rnns, inputs, - tf.expand_dims(paddings, 2), state0) - - if direct_features is not None: - direct_features = py_utils.HasRank(direct_features, 3) - activation = tf.concat([activation, direct_features], axis=2) - - if labels is None: - # We can only compute the logits here. - logits = self.softmax.Logits( - theta=theta.softmax, - inputs=tf.reshape(activation, [seqlen * batch, -1])) - xent_output = py_utils.NestedMap( - logits=tf.reshape(logits, [seqlen, batch, -1])) - xent_output.probs = tf.nn.softmax(xent_output.logits) - xent_output.log_probs = tf.nn.log_softmax(xent_output.logits) - elif 'class_ids' in labels: - xent_output = self.softmax.FProp( - theta=theta.softmax, - inputs=activation, - class_weights=labels.class_weights, - class_ids=labels.class_ids) - else: - assert 'class_probabilities' in labels - xent_output = self.softmax.FProp( - theta=theta.softmax, - inputs=activation, - class_weights=labels.class_weights, - class_probabilities=labels.class_probabilities) - xent_output.last_hidden = activation - return xent_output, state1 - - def CombineStates(self, state0, state1, switch_cond): - """Combines states based on a switch conditional. - - Args: - state0: a NestedMap of states to use for batch elements where switch_cond - is true. - state1: a NestedMap of states to use for batch elements where switch_cond - is false. - switch_cond: bool tensor of shape [batch] on which to switch. - - Returns: - a NestedMap of states. - """ - updated_rnn_states = [] - for i in range(self.params.rnns.num_layers): - updated_rnn_states.append( - py_utils.NestedMap({ - 'c': tf.where(switch_cond, state0.rnn[i].c, state1.rnn[i].c), - 'm': tf.where(switch_cond, state0.rnn[i].m, state1.rnn[i].m) - })) - combined_state = py_utils.NestedMap({'rnn': updated_rnn_states}) - return combined_state - - -class RnnLm(RnnLmNoEmbedding): - """Stacked RNN based language model layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('emb', layers.EmbeddingLayer.Params(), - 'The embedding layer params.') - p.Define('embedding_dropout_keep_prob', 1.0, 'Embedding dropout keep prob.') - p.Define('embedding_dropout_seed', None, 'Embedding dropout seed.') - p.emb.max_num_shards = 1 - return p - - # TODO(zhifengc): Consider merge Params() and CommonParams(). - @classmethod - def CommonParams(cls, - vocab_size, - emb_dim=1024, - num_layers=2, - rnn_dims=2048, - rnn_hidden_dims=0, - residual_start=1, - layer_norm=False, - softmax_max_alloc=None): - """A LM model parameterized by vocab size, etc. - - Args: - vocab_size: Vocab size. - emb_dim: Embedding dimension. - num_layers: The number of rnn layers. - rnn_dims: Each RNN layer has this many output nodes. - rnn_hidden_dims: If > 0, each RNN layer has this many hidden nodes. - residual_start: index of the first layer with a residual connection; - higher index layers also have residuals. - layer_norm: If true, use LayerNormalizedLSTMCellSimple otherwise use - LSTMCellSimple. - softmax_max_alloc: If set to a positive integer the soft-max - computation is chunked into allocations of at most - `softmax_max_alloc`; when left to its default value of None no - chunking is done. - - Returns: - A `RnnLm` parameter object. - """ - p = cls.Params() - p.vocab_size = vocab_size - - init_scale = 1.0 / math.sqrt(rnn_dims) - - # Embedding. - p.emb.vocab_size = vocab_size - p.emb.embedding_dim = emb_dim - p.emb.scale_sqrt_depth = True - p.emb.params_init = py_utils.WeightInit.Uniform(init_scale) - - # RNNs - p.rnns.num_layers = num_layers - # Which layer starts to have the residual connection. - p.rnns.skip_start = residual_start - lstm_cell_tpl = rnn_cell.LayerNormalizedLSTMCellSimple.Params( - ) if layer_norm else rnn_cell.LSTMCellSimple.Params() - lstm_cell_tpl.Set( - num_input_nodes=rnn_dims, - num_output_nodes=rnn_dims, - num_hidden_nodes=rnn_hidden_dims) - if num_layers > 1: - p.rnns.cell_tpl = [lstm_cell_tpl.Copy(), lstm_cell_tpl.Copy()] - else: - p.rnns.cell_tpl = [lstm_cell_tpl.Copy()] - p.rnns.cell_tpl[0].num_input_nodes = emb_dim - - # Softmax - p.softmax.input_dim = rnn_dims - p.softmax.num_classes = vocab_size - p.softmax.params_init = py_utils.WeightInit.Uniform(init_scale) - if softmax_max_alloc: - # If the vocab is very large, computes the softmax chunk-by-chunk. - p.softmax.chunk_size = max(1, int(softmax_max_alloc / vocab_size)) - - return p - - def __init__(self, params, verify_sizes=True): - super().__init__(params) - p = self.params - - if verify_sizes: - assert p.emb.vocab_size == p.vocab_size, ('{} vs. {}'.format( - p.emb.vocab_size, p.vocab_size)) - assert p.emb.embedding_dim == p.rnns.cell_tpl[0].num_input_nodes, ( - '{} vs. {}'.format(p.emb.embedding_dim, - p.rnns.cell_tpl[0].num_input_nodes)) - - self.CreateChild('emb', p.emb) - - def FProp(self, - theta, - inputs, - paddings, - state0, - labels=None, - direct_features=None): - """Computes xent loss given the language model input activations. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: input ids. An int32 tensor of shape [time, batch]. - paddings: a 0/1 tensor of shape [time, batch]. - state0: A `.NestedMap` containing the initial recurrent state. - labels: If not None, a `.NestedMap` containing the following fields: - - - class_weights, a tensor with shape [time, batch] containing the - weights for each target word. - - class_ids, a tensor with shape [time, batch] of int32 dtype containing - the target class labels. - - class_probabilities, a tensor with shape [time, batch, vocab_size] of - float values indicating class-membership probabilities. - direct_features: - If not None, a tensor of [time, batch, direct_feature_dims] that is - concatenated to the output of the last RNN layer. - - Returns: - If `labels` is not None, returns (xent_output, state1), where - `xent_output` is a `.NestedMap` as defined by `SoftmaxLayer`'s return - value and `state1` is the next recurrent state. Otherwise, - `xent_output` only contains the softmax logits. - """ - ids = py_utils.HasRank(inputs, 2) - paddings = py_utils.HasShape(paddings, tf.shape(ids)) - assert state0 - activation = self.emb.EmbLookup(theta.emb, ids) - # Dropout on embeddings is only applied in training. - p = self.params - if p.embedding_dropout_keep_prob < 1.0 and not self.do_eval: - activation = tf.nn.dropout( - activation, - rate=1 - p.embedding_dropout_keep_prob, - seed=p.embedding_dropout_seed) - return super().FProp(theta, activation, paddings, state0, labels, - direct_features) - - -class ConditionalRnnLm(RnnLmNoEmbedding): - """RnnLm where looked up embedding is concatenated with a condition vector.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('condition_dim', 128, 'The size of the condition vector.') - p.Define('emb', layers.EmbeddingLayer.Params(), - 'The embedding layer params.') - p.Define( - 'embedding_dropout_keep_prob', 1.0, 'Embedding dropout keep prob.' - 'Dropout is applied after concatenating with condition vector.') - p.Define('embedding_dropout_seed', None, 'Embedding dropout seed.') - p.emb.max_num_shards = 1 - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - assert p.emb.vocab_size == p.vocab_size, ('{} vs. {}'.format( - p.emb.vocab_size, p.vocab_size)) - assert (p.emb.embedding_dim + p.condition_dim == - p.rnns.cell_tpl[0].num_input_nodes), ('{} vs. {}'.format( - p.emb.embedding_dim, p.rnns.cell_tpl[0].num_input_nodes)) - - self.CreateChild('emb', p.emb) - - def FProp(self, - theta, - inputs, - paddings, - state0, - condition, - labels=None, - direct_features=None): - """Computes xent loss given the language model input and condition. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: input ids. An int32 tensor of shape [time, batch]. - paddings: a 0/1 tensor of shape [time, batch]. - state0: A `.NestedMap` containing the initial recurrent state. - condition: input condition. A tensor of shape [batch, condition_dim]. - labels: If not None, a `.NestedMap` containing the following fields: - - class_weights, a tensor with shape [time, batch] containing the weights - for each target word. - class_ids, a tensor with shape [time, batch] of - int32 dtype containing the target class labels. - class_probabilities, a - tensor with shape [time, batch, vocab_size] of float values indicating - class-membership probabilities. - direct_features: If not None, a tensor of [time, batch, - direct_feature_dims] that is concatenated to the output of the last RNN - layer. - - Returns: - If `labels` is not None, returns (xent_output, state1), where - `xent_output` is a `.NestedMap` as defined by `SoftmaxLayer`'s return - value and `state1` is the next recurrent state. Otherwise, - `xent_output` only contains the softmax logits. - """ - p = self.params - # `condition` should have shape (batch_size, dim) - condition = py_utils.HasShape(condition, - [tf.shape(paddings)[1], p.condition_dim]) - # Expand the time dimension -> (time, batch_size, dim) - condition = tf.tile( - tf.expand_dims(condition, 0), [tf.shape(inputs)[0], 1, 1]) - - ids = py_utils.HasRank(inputs, 2) - paddings = py_utils.HasShape(paddings, tf.shape(ids)) - activation = self.emb.EmbLookup(theta.emb, ids) - activation = tf.concat([activation, tf.cast(condition, p.dtype)], -1) - # Dropout on embeddings is only applied in training. - if p.embedding_dropout_keep_prob < 1.0 and not self.do_eval: - activation = tf.nn.dropout( - activation, - rate=1 - p.embedding_dropout_keep_prob, - seed=p.embedding_dropout_seed) - return super().FProp(theta, activation, paddings, state0, labels, - direct_features) - - -class MoeLm(BaseLanguageModel): - """Mixture of experts language modeling class.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define( - 'emb', - layers.EmbeddingLayer.Params().Set(max_num_shards=1), - 'The embedding layer params.') - p.Define('shared_emb', True, 'If true, uses a single embedding') - p.Define( - 'add_postgating_rnn', True, 'If true, add an RNNLM post gating. ' - 'If false, add only a softmax on top.') - p.Define('rnns', rnn_layers.StackedFRNNLayerByLayer.Params(), - 'The stacked-RNNs layer params.') - p.Define('number_of_experts', 7, 'Number of experts.') - p.Define('merge', RnnLmNoEmbedding.Params(), - 'The LM to use for the merged LM') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if not isinstance(p.rnns.cell_tpl, (list, tuple)): - p.rnns.cell_tpl = [p.rnns.cell_tpl] - - assert p.emb.vocab_size == p.vocab_size, ('{} vs. {}'.format( - p.emb.vocab_size, p.vocab_size)) - assert p.emb.embedding_dim == p.rnns.cell_tpl[0].num_input_nodes, ( - '{} vs. {}'.format(p.emb.embedding_dim, - p.rnns.cell_tpl[0].num_input_nodes)) - if p.add_postgating_rnn: - assert p.merge.vocab_size == p.vocab_size, ('{} vs. {}'.format( - p.merge.vocab_size, p.vocab_size)) - - # Embeddings - if p.shared_emb: - self.CreateChild('emb', p.emb) - else: - # 0-th embedding is for the domain predictor. - self.CreateChildren('emb', [ - p.emb.Copy().Set(name='emb_%d' % i) - for i in range(1 + p.number_of_experts) - ]) - - # Rnns - # 0-th rnns is for the domain predictor. - self.CreateChildren('rnns', - [p.rnns.Copy() for i in range(1 + p.number_of_experts)]) - - # Softmax - rnn_output_size = _RnnOutputSize(p.rnns) - sm_params = layers.SimpleFullSoftmax.Params() - sm_params.name = 'domain_predictor_softmax' - sm_params.input_dim = rnn_output_size - sm_params.num_classes = p.number_of_experts - self.CreateChild('domain_predictor_softmax', sm_params) - - # Merge - if p.add_postgating_rnn: - self.CreateChild('merge', p.merge) - else: - output_sm_params = layers.SimpleFullSoftmax.Params() - output_sm_params.name = 'output_softmax' - output_sm_params.input_dim = rnn_output_size - output_sm_params.num_classes = p.vocab_size - self.CreateChild('output_softmax', output_sm_params) - - def zero_state(self, theta, batch_size): - p = self.params - state0 = py_utils.NestedMap(rnns=[ - x.zero_state(x_theta, batch_size) - for x, x_theta in zip(self.rnns, theta.rnns) - ]) - if p.add_postgating_rnn: - state0.merge = self.merge.zero_state(theta.merge, batch_size) - return state0 - - def FProp(self, theta, inputs, paddings, state0, labels=None): - """Forward compute.""" - p = self.params - - ids = py_utils.HasRank(inputs, 2) - paddings = py_utils.HasShape(paddings, tf.shape(ids)) - seqlen, batch = tf.unstack(tf.shape(inputs), num=2) - assert state0 - - paddings_3d = tf.expand_dims(paddings, axis=2) - - # RNNs - if p.shared_emb: - emb_act = [self.emb.EmbLookup(theta.emb, inputs) - ] * (1 + p.number_of_experts) - else: - emb_act = [ - self.emb[i].EmbLookup(theta.emb[i], inputs) - for i in range(1 + p.number_of_experts) - ] - state1 = py_utils.NestedMap(rnns=[]) - rnns_act = [] - for i, act in enumerate(emb_act): - act, state = self.rnns[i].FProp(theta.rnns[i], act, paddings_3d, - state0.rnns[i]) - act = py_utils.HasRank(act, 3) - rnns_act += [act] - state1.rnns += [state] - - # [time, batch, experts, dims]. - expert_stacked = tf.stack(rnns_act[1:], axis=2) - - # Compute gating softmax. The 0-th rnns is used as the expert - # predictor. Because SoftmaxLayer.Logits takes a matrix as input, - # we reshape rnns_act[0], the domain predictor activation, to a - # matrix here. - act = tf.reshape(rnns_act[0], [seqlen * batch, -1]) - logits = self.domain_predictor_softmax.Logits( - theta.domain_predictor_softmax, act) - # [time, batch, experts] - gating = tf.reshape(tf.nn.softmax(logits), [seqlen, batch, -1]) - - # Mix the experts. - # [time, batch, dims] - combined = tf.squeeze( - tf.matmul( - # [time, batch, 1, experts] - tf.expand_dims(gating, axis=2), - # [time, batch, experts, dims] - expert_stacked), - axis=2) - - if p.add_postgating_rnn: - # Note that this layer includes 1 or more RNN layers followed - # by a softmax. - xent_loss, state1.merge = self.merge.FProp(theta.merge, combined, - paddings, state0.merge, labels) - else: - xent_loss = self.output_softmax.FProp( - theta=theta.output_softmax, - inputs=combined, - class_weights=labels.class_weights, - class_ids=labels.class_ids) - - # return xent_loss, state1 - return xent_loss, state1 - - -class TransformerLmNoEmbedding(BaseLanguageModel): - """Transformer language model.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('position_emb', layers.PositionalEmbeddingLayer.Params(), - 'Position embedding layer params.') - p.Define( - 'model_dim', 512, 'Model dimension that applies to embedding ' - 'layers and all Transformer layers.') - p.Define('num_trans_layers', 6, 'Number of Transformer layers.') - p.Define('trans_tpl', layers_with_attention.TransformerLayer.Params(), - 'Transformer Layer params.') - p.Define('input_dropout_prob', 0.0, 'Prob at which we do input dropout.') - p.Define( - 'residual_dropout_prob', 0.0, 'Dropout prob to the output of ' - 'each sub-layer before it is added to the sub-layer input.') - p.Define( - 'atten_dropout_prob', 0.0, 'Dropout prob to the attention ' - 'weights in each Transformer attention sub-layer.') - p.Define( - 'relu_dropout_prob', 0.0, 'Dropout prob to the inner layer ' - 'output (ReLU activation) in each Transformer feed-forward ' - 'sub-layer.') - p.Define('softmax', layers.SimpleFullSoftmax.Params(), - 'The softmax layer params.') - - # Default config for the transformer layers. - p.trans_tpl.has_aux_atten = False - p.trans_tpl.mask_self_atten = True - p.trans_tpl.tr_atten_tpl.num_attention_heads = 8 - p.trans_tpl.tr_atten_tpl.atten_tpl.enable_ctx_pre_proj = True - p.trans_tpl.tr_atten_tpl.atten_tpl.enable_ctx_post_proj = True - p.trans_tpl.tr_fflayer_tpl.hidden_dim = 2048 - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - p.trans_tpl.tr_atten_tpl.residual_dropout_prob = p.residual_dropout_prob - p.trans_tpl.tr_atten_tpl.atten_dropout_prob = p.atten_dropout_prob - p.trans_tpl.tr_fflayer_tpl.residual_dropout_prob = p.residual_dropout_prob - p.trans_tpl.tr_fflayer_tpl.relu_dropout_prob = p.relu_dropout_prob - - p.position_emb.embedding_dim = p.model_dim - self.CreateChild('position_emb', p.position_emb) - - dropout_tpl = layers.DropoutLayer.Params().Set( - keep_prob=(1.0 - p.input_dropout_prob)) - self.CreateChild('input_dropout', dropout_tpl) - - params_trans_layers = [] - for i in range(p.num_trans_layers): - params = p.trans_tpl.Copy() - params.source_dim = p.model_dim - params.name = 'layer_%d' % i - params_trans_layers.append(params) - self.CreateChildren('trans', params_trans_layers) - - p.softmax.input_dim = p.model_dim - p.softmax.num_classes = p.vocab_size - self.CreateChild('softmax', p.softmax) - - def zero_state(self, theta, batch_size): - p = self.params - state0 = py_utils.NestedMap() - for layer in range(p.num_trans_layers): - state0['layer_%d' % layer] = py_utils.NestedMap({ - 'key': tf.zeros([0, batch_size, p.model_dim]), - 'value': tf.zeros([0, batch_size, p.model_dim]), - }) - return state0 - - @classmethod - def StepOutputDimension(cls, params): - return py_utils.NestedMap( - logits=params.vocab_size, last_hidden=params.softmax.input_dim) - - def Step(self, theta, inputs, paddings, state0, *args, **kwargs): - """FProp one step. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: a tensor of shape [batch, model_dim]. - paddings: a 0/1 tensor of shape [batch]. Unused here. - state0: A `.NestedMap` containing the prefix states up to step t-1. - *args: optional extra arguments. - **kwargs: optional extra keyword arguments. - - Returns: - A tuple (output, state1). - output: A `.NestedMap` with fields. - logits: - [batch, vocab_size]. - last_hidden: - [batch, model_dims]. - state1: - The updated prefix states including step t. - """ - - prefix_len, _ = py_utils.GetShape(state0['layer_0'].key, 2) - # [1, model_dim] - posit_embs = self.position_emb.FProp(theta.position_emb, - prefix_len + 1)[-1:, :] - # [batch, model_dim] - input_embs = inputs + posit_embs - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - - # Make a copy of the input. - state1 = state0.Pack(state0.Flatten()) - - layer_in = input_embs - for i, (layer, layer_theta) in enumerate(zip(self.trans, theta.trans)): - layer_prefix_states = state0['layer_%i' % i] - # [batch, model_dim] - layer_out, _, updated_prefix_states = layer.ExtendStep( - layer_theta, layer_in, layer_prefix_states) - state1['layer_%i' % i] = updated_prefix_states - layer_in = layer_out - - # [batch, vocab_size] - logits = self.softmax.Logits(theta=theta.softmax, inputs=layer_out) - - output = py_utils.NestedMap(logits=logits, last_hidden=layer_out) - return output, state1 - - def FProp(self, theta, inputs, paddings, state0=None, labels=None): - """Computes xent loss given the language model input activations. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: Input activation. A tensor of shape [time, batch, model_dim]. - paddings: A 0/1 tensor of shape [time, batch]. - state0: Not used for Transformer. - labels: If not None, a `.NestedMap` containing the following fields: - - - class_weights, a tensor with shape [time, batch] containing the - weights for each target word. - - class_ids, a tensor with shape [time, batch] of int32 dtype containing - the target class labels. - - class_probabilities, a tensor with shape [time, batch, vocab_size] of - float values indicating class-membership probabilities. - - Returns: - If `labels` is not None, returns (xent_output, None), where - `xent_output` is a `.NestedMap` as defined by `SoftmaxLayer`'s return - value. Otherwise, `xent_output` only contains the softmax logits. - """ - p = self.params - inputs = py_utils.HasRank(inputs, 3) - seqlen, batch, _ = tf.unstack(tf.shape(inputs), num=3) - inputs = py_utils.HasShape(inputs, [seqlen, batch, p.model_dim]) - paddings = py_utils.HasShape(paddings, [seqlen, batch]) - - # [time, 1, model_dim] - posit_embs = tf.expand_dims( - self.position_emb.FProp(theta.position_emb, seqlen), 1) - # [time, batch, model_dim] - input_embs = inputs + posit_embs - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - - layer_in = input_embs - for layer, layer_theta in zip(self.trans, theta.trans): - # [time, batch, model_dim] - layer_out, _ = layer.FProp(layer_theta, layer_in, paddings) - layer_in = layer_out - - if labels is None: - # We can only compute the logits here. - logits = self.softmax.Logits( - theta=theta.softmax, - inputs=tf.reshape(layer_out, [seqlen * batch, -1])) - xent_output = py_utils.NestedMap( - logits=tf.reshape(logits, [seqlen, batch, -1])) - elif 'class_ids' in labels: - xent_output = self.softmax.FProp( - theta=theta.softmax, - inputs=layer_out, - class_weights=labels.class_weights, - class_ids=labels.class_ids) - else: - assert 'class_probabilities' in labels - xent_output = self.softmax.FProp( - theta=theta.softmax, - inputs=layer_out, - class_weights=labels.class_weights, - class_probabilities=labels.class_probabilities) - xent_output.last_hidden = layer_out - return xent_output, None - - -class TransformerLm(TransformerLmNoEmbedding): - """Stacked RNN based language model layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('emb', layers.EmbeddingLayer.Params(), - 'The embedding layer params.') - p.emb.max_num_shards = 1 - return p - - @classmethod - def CommonParams(cls, - model_dim, - hidden_dim, - num_heads, - num_layers, - learning_rate, - warmup_steps, - vocab_size, - input_dropout_prob=0.0, - residual_dropout_prob=0.1, - atten_dropout_prob=0.0, - relu_dropout_prob=0.0, - softmax_max_alloc=None): - """Common setup for Transformer language models. - - Args: - model_dim: model dimension. - hidden_dim: hidden dimension of feed-forward inner layer. - num_heads: number of attention heads. - num_layers: number of layers in the transformer LM. - learning_rate: learning rate. - warmup_steps: warmup steps for TransformerSchedule. - vocab_size: vocab size. - input_dropout_prob: dropout prob to the sums of the token embeddings and - the position embeddings. - residual_dropout_prob: dropout prob to the output of each sub-layer before - it is added to the sub-layer input. - atten_dropout_prob: dropout prob to the attention weights in each - Transformer attention sub-layer. - relu_dropout_prob: dropout prob to the inner layer output (ReLU - activation) in each Transformer feed-forward sub-layer. - softmax_max_alloc: If set to a positive integer the soft-max - computation is chunked into allocations of at most - softmax_max_alloc; when left to its default value of None no - chunking is done. - - Returns: - A Params object containing the parameters that set up a Transformer LM. - """ - p = cls.Params() - p.name = 'transformerlm' - - p.model_dim = model_dim - p.vocab_size = vocab_size - p.num_trans_layers = num_layers - p.input_dropout_prob = input_dropout_prob - p.residual_dropout_prob = residual_dropout_prob - p.atten_dropout_prob = atten_dropout_prob - p.relu_dropout_prob = relu_dropout_prob - - default_params_init = py_utils.WeightInit.Xavier(1.0) - emb_params_init = py_utils.WeightInit.Gaussian(1.0 / math.sqrt(p.model_dim)) - p.emb.Set( - vocab_size=vocab_size, - embedding_dim=p.model_dim, - max_num_shards=16, - params_init=emb_params_init, - scale_sqrt_depth=True) - - p.position_emb.Set(embedding_dim=p.model_dim, trainable_scaling=False) - - p.trans_tpl.has_aux_atten = False - p.trans_tpl.mask_self_atten = True - - p.trans_tpl.tr_atten_tpl.Set( - num_attention_heads=num_heads, params_init=default_params_init) - - p.trans_tpl.tr_atten_tpl.atten_tpl.Set( - enable_ctx_pre_proj=True, enable_ctx_post_proj=True) - - p.trans_tpl.tr_fflayer_tpl.Set( - hidden_dim=hidden_dim, params_init=default_params_init) - - p.softmax.Set( - num_classes=vocab_size, num_shards=16, params_init=emb_params_init) - - if softmax_max_alloc: - # If the vocab is very large, computes the softmax chunk-by-chunk. - p.softmax.chunk_size = max(1, int(softmax_max_alloc / vocab_size)) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - assert p.emb.vocab_size == p.vocab_size, ('{} vs. {}'.format( - p.emb.vocab_size, p.vocab_size)) - assert p.emb.embedding_dim == p.position_emb.embedding_dim, ( - '{} vs. {}'.format(p.emb.embedding_dim, p.position_emb.embedding_dim)) - assert p.emb.embedding_dim == p.model_dim, ('{} vs. {}'.format( - p.emb.embedding_dim, p.model_dim)) - - self.CreateChild('emb', p.emb) - - def FProp(self, theta, inputs, paddings, state0=None, labels=None): - """Computes xent loss given the language model input activations. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - inputs: Input ids. An int32 tensor of shape [time, batch]. - paddings: A 0/1 tensor of shape [time, batch]. - state0: Not used for Transformer. - labels: If not None, a `.NestedMap` containing the following fields: - - - class_weights, a tensor with shape [time, batch] containing the - weights for each target word. - - class_ids, a tensor with shape [time, batch] of int32 dtype containing - the target class labels. - - class_probabilities, a tensor with shape [time, batch, vocab_size] of - float values indicating class-membership probabilities. - - Returns: - If `labels` is not None, returns (xent_output, state1), where - `xent_output` is a `.NestedMap` as defined by `SoftmaxLayer`'s return - value and `state1` is the next recurrent state. Otherwise, - `xent_output` only contains the softmax logits. - """ - ids = py_utils.HasRank(inputs, 2) - paddings = py_utils.HasShape(paddings, tf.shape(ids)) - activation = self.emb.EmbLookup(theta.emb, ids) - return super().FProp(theta, activation, paddings, labels=labels) - - -class GPipeTransformerLm(BaseLanguageModel): - """GPipe Transformer based language model layer.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('stack', layers_with_gpipe.GPipeTransformerStack.Params(), - 'GPipeTransformerStack Layer params.') - return p - - @classmethod - def CommonParams(cls, - vocab_size, - model_dim, - attention_hidden_dim=None, - hidden_dim=1024, - num_heads=8, - num_layers=6, - splits=1, - num_micro_batches=1, - micro_batch_size=None, - num_shards=16, - input_dropout_prob=0.0, - residual_dropout_prob=0.1, - atten_dropout_prob=0.0, - relu_dropout_prob=0.0, - softmax_max_alloc=None): - """Common setup for Transformer language models. - - Args: - vocab_size: vocab size. - model_dim: model dimension. - attention_hidden_dim: hidden dim for the attention layer. - hidden_dim: hidden dimension of feed-forward inner layer. - num_heads: number of attention heads. - num_layers: number of layers in the transformer LM. - splits: list or number of partitions for GPipe. - num_micro_batches: number of micro batches for GPipe. - micro_batch_size: size of a micro batch. - num_shards: num_shards for softmax. Assert vocab_size % num_shards == 0 - input_dropout_prob: dropout prob to the sums of the token embeddings and - the position embeddings. - residual_dropout_prob: dropout prob to the output of each sub-layer before - it is added to the sub-layer input. - atten_dropout_prob: dropout prob to the attention weights in each - Transformer attention sub-layer. - relu_dropout_prob: dropout prob to the inner layer output (ReLU - activation) in each Transformer feed-forward sub-layer. - softmax_max_alloc: If set to a positive integer the soft-max computation - is chunked into allocations of at most softmax_max_alloc; when left to - its default value of None no chunking is done. - - Returns: - A Params object containing the parameters that set up a Transformer LM. - """ - p = cls.Params() - p.name = 'transformerlm' - p.vocab_size = vocab_size - p.stack.splits = splits - p.stack.model_dim = model_dim - p.stack.num_micro_batches = num_micro_batches - p.stack.micro_batch_size = micro_batch_size - p.stack.num_encoder_layers = num_layers - p.stack.batch_dim = 0 - emb_params_init = py_utils.WeightInit.Gaussian(1.0 / math.sqrt(model_dim)) - p.stack.emb_tpl.ret_task_ids = True - p.stack.emb_tpl.token_emb.Set( - use_matmul=False, - use_3d_weight_tensor=False, - vocab_size=vocab_size, - embedding_dim=model_dim, - params_init=emb_params_init) - p.stack.emb_tpl.position_emb.Set( - embedding_dim=model_dim, trainable_scaling=False) - p.stack.emb_tpl.input_dropout_prob = input_dropout_prob - - trans_tpl = batch_major_attention.GPipeTransformerLayer.Params() - trans_tpl.has_aux_atten = False - trans_tpl.mask_self_atten = True - trans_tpl.input_dim = model_dim - trans_tpl.output_dim = model_dim - trans_tpl.tr_atten_tpl.input_dim = model_dim - trans_tpl.tr_atten_tpl.hidden_dim = attention_hidden_dim or model_dim - trans_tpl.tr_atten_tpl.is_masked = True - trans_tpl.tr_atten_tpl.num_heads = num_heads - trans_tpl.tr_atten_tpl.atten_tpl.use_bias = False - trans_tpl.tr_fflayer_tpl.hidden_dim = hidden_dim - p.stack.encoder_tpl = trans_tpl - - p.stack.softmax_tpl.Set( - num_classes=vocab_size, input_dim=model_dim, num_shards=num_shards) - if softmax_max_alloc: - # If the vocab is very large, computes the softmax chunk-by-chunk. - p.stack.softmax_tpl.chunk_size = max(1, - int(softmax_max_alloc / vocab_size)) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - p.stack.name = p.name - p.stack.state_dtype = p.dtype - if p.fprop_dtype: - p.stack.state_dtype = p.fprop_dtype - self.CreateChild('stack', p.stack) - - def zero_state(self, theta, batch_size): - return py_utils.NestedMap() - - def FProp(self, theta, inputs, paddings, state0=None, labels=None): - """Computes xent loss given the language model input activations. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - inputs: Input ids. An int32 tensor of shape [batch, time]. - paddings: A 0/1 tensor of shape [batch, time]. - state0: Not used for Transformer. - labels: If not None, a `.NestedMap` containing the following fields: - - class_weights, a tensor with shape [batch, time] containing the weights - for each target word. - class_ids, a tensor with shape [batch, time] of - int32 dtype containing the target class labels. - class_probabilities, a - tensor with shape [batch, time, vocab_size] of float values indicating - class-membership probabilities. - - Returns: - If `labels` is not None, returns (xent_output, state1), where - `xent_output` is a `.NestedMap` as defined by `SoftmaxLayer`'s return - value and `state1` is the next recurrent state. Otherwise, - `xent_output` only contains the softmax logits. - """ - ids = py_utils.HasRank(inputs, 2) - paddings = py_utils.HasShape(paddings, tf.shape(ids)) - per_example_xent, logits = self.stack.FProp(theta.stack, ids, paddings, - None, None, None, None, - labels.class_ids, - labels.class_weights) - per_example_argmax = py_utils.ArgMax(logits) - total_xent = tf.reduce_sum(per_example_xent * labels.class_weights) - total_weights = tf.reduce_sum(labels.class_weights) - xent_output = py_utils.NestedMap( - total_weight=total_weights, - per_example_xent=per_example_xent, - logits=logits, - per_example_argmax=per_example_argmax, - avg_xent=total_xent / total_weights, - total_xent=total_xent) - return xent_output, {} diff --git a/lingvo/tasks/lm/layers_test.py b/lingvo/tasks/lm/layers_test.py deleted file mode 100644 index 64f784e5..00000000 --- a/lingvo/tasks/lm/layers_test.py +++ /dev/null @@ -1,972 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for layers.""" - -import lingvo.compat as tf -from lingvo.core import batch_major_attention -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.lm import layers as lm_layers -import numpy as np - -FLAGS = tf.flags.FLAGS - - -class RnnLmNoEmbeddingTest(test_utils.TestCase): - - def _testParams(self, dims, vocab): - p = lm_layers.RnnLmNoEmbedding.Params() - p.name = 'rnnlm' - p.vocab_size = vocab - p.rnns.cell_tpl.num_output_nodes = dims - p.rnns.cell_tpl.num_input_nodes = dims - p.softmax.input_dim = dims - p.softmax.num_classes = vocab - return p - - def testBasic(self): - time, batch, dims, vocab = 5, 3, 6, 8 - p = self._testParams(dims, vocab) - - with self.session(use_gpu=True): - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.normal(size=[time, batch, dims]) - inputs = tf.constant(inputs, tf.float32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant( - np.random.randint(vocab, size=(time, batch)), tf.int32) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 2.1042602, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testProjection(self): - time, batch, dims, proj, vocab = 5, 3, 6, 4, 8 - p = self._testParams(dims, vocab) - p.rnns.cell_tpl.num_output_nodes = proj - p.rnns.cell_tpl.num_hidden_nodes = dims - p.softmax.input_dim = proj - - with self.session(use_gpu=True): - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.normal(size=[time, batch, dims]) - inputs = tf.constant(inputs, tf.float32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant( - np.random.randint(vocab, size=(time, batch)), tf.int32) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 2.1322777, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testBasicGrad(self): - time, batch, dims, vocab = 5, 3, 6, 8 - p = self._testParams(dims, vocab) - p.dtype = tf.float64 - - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.normal(size=[time, batch, dims]) - inputs = tf.constant(inputs, tf.float64) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float64) - targets = tf.constant( - np.random.randint(vocab, size=(time, batch)), tf.int32) - - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - lm_vars = lm.vars.Flatten() - # Now add the backward graph. - grads = tf.gradients(xent_output.avg_xent, lm_vars) - - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(len(lm_vars), len(grads)) - for x, grad_x in zip(lm_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_output.avg_xent, x, delta=1e-6) - self.assertAllClose(grad_symbolic, grad_numeric, atol=0.005) - - def testDirectFeatures(self,): - time, batch, dims, vocab = 5, 3, 6, 8 - p = self._testParams(dims, vocab) - direct_features_dim = 4 - p.direct_features_dim = direct_features_dim - p.softmax.input_dim = dims + direct_features_dim - - with self.session(use_gpu=True): - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.normal(size=[time, batch, dims]) - inputs = tf.constant(inputs, tf.float32) - - direct_features = np.random.normal( - size=[time, batch, direct_features_dim]) - direct_features = tf.constant(direct_features, tf.float32) - - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant( - np.random.randint(vocab, size=(time, batch)), tf.int32) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets), - direct_features=direct_features) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 2.3522419929504395, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testCombineStates(self): - time, batch, dims, vocab = 5, 3, 6, 8 - p = self._testParams(dims, vocab) - - with self.session(use_gpu=True): - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.normal(size=[time, batch, dims]) - inputs = tf.constant(inputs, tf.float32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant( - np.random.randint(vocab, size=(time, batch)), tf.int32) - self.evaluate(tf.global_variables_initializer()) - - state0 = lm.zero_state(lm.theta, batch) - _, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=state0, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - switch = tf.constant([True, True, False], dtype=tf.bool) - combined_state = lm.CombineStates(state0, state1, switch) - state0_val, state1_val, combined_state_val = self.evaluate( - [state0, state1, combined_state]) - - print('state1_val', state1_val) - print('combined_state_val', combined_state_val) - combined_m_state = combined_state_val.rnn[0].m - combined_c_state = combined_state_val.rnn[0].c - self.assertAllEqual(combined_m_state[0], state0_val.rnn[0].m[0]) - self.assertAllEqual(combined_c_state[0], state0_val.rnn[0].c[0]) - self.assertAllEqual(combined_m_state[1], state0_val.rnn[0].m[1]) - self.assertAllEqual(combined_c_state[1], state0_val.rnn[0].c[1]) - self.assertAllEqual(combined_m_state[2], state1_val.rnn[0].m[2]) - self.assertAllEqual(combined_c_state[2], state1_val.rnn[0].c[2]) - - -class RnnLmTest(test_utils.TestCase): - - def testBasic(self): - time, batch, dims, vocab = 5, 3, 6, 8 - - p = lm_layers.RnnLm.Params() - p.name = 'rnnlm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - p.rnns.cell_tpl.num_output_nodes = dims - p.rnns.cell_tpl.num_input_nodes = dims - p.softmax.input_dim = dims - p.softmax.num_classes = vocab - - with self.session(use_gpu=True): - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant(targets, tf.int32) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 2.0853612, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testDropout(self): - seed = 12345 - tf.random.set_seed(seed) - np.random.seed(seed) - - time, batch, dims, vocab = 5, 3, 6, 8 - - p = lm_layers.RnnLm.Params() - p.name = 'rnnlm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - p.embedding_dropout_keep_prob = 0.8 - p.embedding_dropout_seed = seed - p.rnns.cell_tpl.num_output_nodes = dims - p.rnns.cell_tpl.num_input_nodes = dims - p.softmax.input_dim = dims - p.softmax.num_classes = vocab - - with self.session(use_gpu=True): - lm = p.Instantiate() - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant(targets, tf.int32) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 2.084798, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testBasicGrad(self): - time, batch, dims, vocab = 5, 3, 6, 8 - - p = lm_layers.RnnLm.Params() - p.dtype = tf.float64 - p.name = 'rnnlm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - p.rnns.cell_tpl.num_output_nodes = dims - p.rnns.cell_tpl.num_input_nodes = dims - p.softmax.input_dim = dims - p.softmax.num_classes = vocab - - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float64) - targets = tf.constant(targets, tf.int32) - self.evaluate(tf.global_variables_initializer()) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - lm_vars = lm.vars.Flatten() - # Now add the backward graph. - grads = tf.gradients(xent_output.avg_xent, lm_vars) - - for i, x in enumerate(grads): - if isinstance(x, tf.IndexedSlices): - grads[i] = tf.math.unsorted_segment_sum(x.values, x.indices, - x.dense_shape[0]) - - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(len(lm_vars), len(grads)) - for x, grad_x in zip(lm_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_output.avg_xent, x, delta=1e-6) - self.assertAllClose(grad_symbolic, grad_numeric, atol=0.005) - - -class ConditionalRnnLmTest(test_utils.TestCase): - - def testBasic(self): - time, batch, dims, vocab, condition_dim = 5, 3, 6, 8, 7 - - p = lm_layers.ConditionalRnnLm.Params() - p.name = 'conditionalrnnlm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - model_dim = dims + condition_dim - p.rnns.cell_tpl.num_output_nodes = model_dim - p.rnns.cell_tpl.num_input_nodes = model_dim - p.softmax.input_dim = model_dim - p.softmax.num_classes = vocab - p.condition_dim = condition_dim - - with self.session(use_gpu=True): - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant(targets, tf.int32) - condition = tf.constant(np.ones([batch, condition_dim]), tf.float64) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - condition=condition, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 2.10713076, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testDropout(self): - seed = 12345 - tf.random.set_seed(seed) - np.random.seed(seed) - - time, batch, dims, vocab, condition_dim = 5, 3, 6, 8, 7 - - p = lm_layers.ConditionalRnnLm.Params() - p.name = 'conditionalrnnlm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - p.embedding_dropout_keep_prob = 0.8 - p.embedding_dropout_seed = seed - model_dim = dims + condition_dim - p.rnns.cell_tpl.num_output_nodes = model_dim - p.rnns.cell_tpl.num_input_nodes = model_dim - p.softmax.input_dim = model_dim - p.softmax.num_classes = vocab - p.condition_dim = condition_dim - - with self.session(use_gpu=True): - lm = p.Instantiate() - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant(targets, tf.int32) - condition = tf.constant(np.ones([batch, condition_dim]), tf.float64) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - condition=condition, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 2.17278885, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testBasicGrad(self): - time, batch, dims, vocab, condition_dim = 5, 3, 6, 8, 7 - - p = lm_layers.ConditionalRnnLm.Params() - p.name = 'conditionalrnnlm' - p.dtype = tf.float64 - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - model_dim = dims + condition_dim - p.rnns.cell_tpl.num_output_nodes = model_dim - p.rnns.cell_tpl.num_input_nodes = model_dim - p.softmax.input_dim = model_dim - p.softmax.num_classes = vocab - p.condition_dim = condition_dim - - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float64) - targets = tf.constant(targets, tf.int32) - condition = tf.constant(np.ones([batch, condition_dim]), tf.float64) - self.evaluate(tf.global_variables_initializer()) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - condition=condition, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - lm_vars = lm.vars.Flatten() - # Now add the backward graph. - grads = tf.gradients(xent_output.avg_xent, lm_vars) - - for i, x in enumerate(grads): - if isinstance(x, tf.IndexedSlices): - grads[i] = tf.math.unsorted_segment_sum(x.values, x.indices, - x.dense_shape[0]) - - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(len(lm_vars), len(grads)) - for x, grad_x in zip(lm_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_output.avg_xent, x, delta=1e-6) - self.assertAllClose(grad_symbolic, grad_numeric, atol=0.005) - - -class MoeLmTest(test_utils.TestCase): - - def _MoeLmParams(self, vocab, shared_emb, add_postgating_rnn=True): - p = lm_layers.MoeLm.Params() - p.name = 'moelm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = 17 - p.shared_emb = shared_emb - p.add_postgating_rnn = add_postgating_rnn - p.rnns.cell_tpl.num_input_nodes = 17 - p.rnns.cell_tpl.num_output_nodes = 23 - p.number_of_experts = 4 - p.merge.vocab_size = vocab - p.merge.rnns.cell_tpl.num_input_nodes = 23 - p.merge.rnns.cell_tpl.num_output_nodes = 32 - p.merge.softmax.input_dim = 32 - p.merge.softmax.num_classes = vocab - return p - - def _GetData(self, vocab, time, batch): - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant(targets, tf.int32) - labels = py_utils.NestedMap(class_weights=1 - paddings, class_ids=targets) - return inputs, paddings, labels - - def testFPropNoSharedEmb(self): - vocab, time, batch = 7, 13, 3 - p = self._MoeLmParams(vocab, False) - - with self.session(graph=tf.Graph()): - np.random.seed(54321) - tf.random.set_seed(123456) - lm = p.Instantiate() - inputs, paddings, labels = self._GetData(vocab, time, batch) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=labels) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 1.9460623, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testFPropSharedEmb(self): - vocab, time, batch = 7, 13, 3 - p = self._MoeLmParams(vocab, False) - - with self.session(graph=tf.Graph()): - np.random.seed(54321) - tf.random.set_seed(123456) - lm = p.Instantiate() - inputs, paddings, labels = self._GetData(vocab, time, batch) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=labels) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 1.9460623, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testFPropNoPostGatingRNN(self): - vocab, time, batch = 7, 13, 3 - p = self._MoeLmParams(vocab, False, False) - - with self.session(graph=tf.Graph()): - np.random.seed(54321) - tf.random.set_seed(123456) - lm = p.Instantiate() - inputs, paddings, labels = self._GetData(vocab, time, batch) - self.evaluate(tf.global_variables_initializer()) - xent_output, state1 = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - state0=lm.zero_state(lm.theta, batch), - labels=labels) - - xent_output_val, state1_val = self.evaluate([xent_output, state1]) - - print('xent_output_val', xent_output_val) - print('state1', state1_val) - test_utils.CompareToGoldenSingleFloat(self, 1.9443978, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testBProp(self): - vocab, time, batch = 7, 4, 3 - p = self._MoeLmParams(vocab, True) - p.dtype = tf.float64 - - with self.session(graph=tf.Graph()) as sess: - np.random.seed(54321) - tf.random.set_seed(123456) - lm = p.Instantiate() - inputs, paddings, labels = self._GetData(vocab, time, batch) - self.evaluate(tf.global_variables_initializer()) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=tf.cast(paddings, p.dtype), - state0=lm.zero_state(lm.theta, batch), - labels=labels) - - lm_vars = lm.vars.Flatten() - # Now add the backward graph. - grads = tf.gradients(xent_output.avg_xent, lm_vars) - - for i, x in enumerate(grads): - if isinstance(x, tf.IndexedSlices): - grads[i] = tf.math.unsorted_segment_sum(x.values, x.indices, - x.dense_shape[0]) - - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(len(lm_vars), len(grads)) - step = 11 # Speed up the test. - for x, grad_x in zip(lm_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_output.avg_xent, x, step=step, delta=1e-6) - self.assertAllClose( - grad_symbolic.reshape([-1])[::step], - grad_numeric.reshape([-1])[::step]) - - -class TransformerLmNoEmbeddingTest(test_utils.TestCase): - - def _testParams(self, dtype=tf.float32): - model_dim, hidden_dim, vocab_size = 4, 6, 8 - p = lm_layers.TransformerLmNoEmbedding.Params() - p.name = 'xformerlm' - p.random_seed = 93820986 - p.dtype = dtype - p.vocab_size = vocab_size - p.model_dim = model_dim - p.num_trans_layers = 3 - p.position_emb.embedding_dim = model_dim - p.trans_tpl.source_dim = model_dim - p.trans_tpl.tr_atten_tpl.num_attention_heads = 2 - p.trans_tpl.tr_fflayer_tpl.hidden_dim = hidden_dim - p.softmax.input_dim = model_dim - p.softmax.num_classes = vocab_size - return p - - def _testInputs(self, dtype=tf.float32, last_padding=1.0): - time, batch, model_dim, vocab_size = 5, 3, 4, 8 - np.random.seed(12345) - inputs = np.random.normal(size=[time, batch, model_dim]) - inputs = tf.constant(inputs, dtype) - paddings = np.zeros([time, batch]) - paddings[-1] = last_padding - paddings = tf.constant(paddings, dtype) - targets = tf.constant( - np.random.randint(vocab_size, size=(time, batch)), tf.int32) - return inputs, paddings, targets - - def testBasic(self): - p = self._testParams(dtype=tf.float32) - with self.session(use_gpu=True): - lm = p.Instantiate() - inputs, paddings, targets = self._testInputs(dtype=tf.float32) - self.evaluate(tf.global_variables_initializer()) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - xent_output_val = self.evaluate(xent_output) - print('xformer xent_output_val.avg_xent', xent_output_val.avg_xent) - test_utils.CompareToGoldenSingleFloat(self, 3.018613, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testBasicGrad(self): - p = self._testParams(dtype=tf.float64) - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - lm = p.Instantiate() - inputs, paddings, targets = self._testInputs(dtype=tf.float64) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - lm_vars = lm.vars.Flatten() - # Now add the backward graph. - grads = tf.gradients(xent_output.avg_xent, lm_vars) - - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(len(lm_vars), len(grads)) - for x, grad_x in zip(lm_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_output.avg_xent, x, delta=1e-6) - self.assertAllClose(grad_symbolic, grad_numeric, atol=0.005) - - def testStep(self): - p = self._testParams(dtype=tf.float32) - with self.session(use_gpu=True): - lm = p.Instantiate() - inputs, paddings, _ = self._testInputs(dtype=tf.float32, last_padding=0.0) - - self.evaluate(tf.global_variables_initializer()) - xent_output, _ = lm.FPropDefaultTheta(inputs=inputs, paddings=paddings) - logits1 = xent_output.logits - - time, batch = 5, 3 - prefix_states = lm.zero_state(lm.theta, batch) - logits2 = [] - for i in range(time): - l_i_out, prefix_states = lm.Step(lm.theta, inputs[i, :, :], - paddings[i, :], prefix_states) - logits2.append(l_i_out.logits) - logits2 = tf.stack(logits2) - - self.evaluate(tf.global_variables_initializer()) - logits1_v, logits2_v = self.evaluate([logits1, logits2]) - print('xformer logits1_v', logits1_v) - print('xformer logits2_v', logits2_v) - self.assertAllClose(logits1_v, logits2_v) - - -class TransformerLmTest(test_utils.TestCase): - - def testBasic(self): - time, batch, dims, hidden_dim, vocab = 5, 3, 6, 4, 8 - - p = lm_layers.TransformerLm.Params() - p.name = 'transformerlm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - p.model_dim = dims - p.num_trans_layers = 3 - p.position_emb.embedding_dim = dims - p.trans_tpl.source_dim = dims - p.trans_tpl.tr_atten_tpl.num_attention_heads = 2 - p.trans_tpl.tr_fflayer_tpl.hidden_dim = hidden_dim - p.softmax.input_dim = dims - p.softmax.num_classes = vocab - - with self.session(use_gpu=True): - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant(targets, tf.int32) - self.evaluate(tf.global_variables_initializer()) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - xent_output_val = self.evaluate(xent_output) - - print('xent_output_val', xent_output_val) - test_utils.CompareToGoldenSingleFloat(self, 3.0489848, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testDropout(self): - seed = 12345 - tf.random.set_seed(seed) - np.random.seed(seed) - - time, batch, dims, hidden_dim, vocab = 5, 3, 6, 4, 8 - - p = lm_layers.TransformerLm.Params() - p.name = 'transformerlm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - p.model_dim = dims - p.num_trans_layers = 3 - p.trans_tpl.source_dim = dims - p.trans_tpl.tr_atten_tpl.num_attention_heads = 2 - p.trans_tpl.tr_fflayer_tpl.hidden_dim = hidden_dim - p.softmax.input_dim = dims - p.softmax.num_classes = vocab - - with self.session(use_gpu=True): - lm = p.Instantiate() - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float32) - targets = tf.constant(targets, tf.int32) - self.evaluate(tf.global_variables_initializer()) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - xent_output_val = self.evaluate(xent_output) - - print('xent_output_val', xent_output_val) - test_utils.CompareToGoldenSingleFloat(self, 3.038596, xent_output_val.avg_xent) # pyformat: disable pylint: disable=line-too-long - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testBasicGrad(self): - time, batch, dims, hidden_dim, vocab = 5, 3, 6, 4, 8 - - p = lm_layers.TransformerLm.Params() - p.dtype = tf.float64 - p.name = 'transformerlm' - p.vocab_size = vocab - p.emb.vocab_size = vocab - p.emb.embedding_dim = dims - p.model_dim = dims - p.num_trans_layers = 1 - p.trans_tpl.source_dim = dims - p.trans_tpl.tr_atten_tpl.num_attention_heads = 2 - p.trans_tpl.tr_fflayer_tpl.hidden_dim = hidden_dim - p.softmax.input_dim = dims - p.softmax.num_classes = vocab - - with self.session(use_gpu=False, graph=tf.Graph()) as sess: - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float64) - targets = tf.constant(targets, tf.int32) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - - lm_vars = lm.vars.Flatten() - grads = tf.gradients(xent_output.avg_xent, lm_vars) - - for i, x in enumerate(grads): - if isinstance(x, tf.IndexedSlices): - grads[i] = tf.math.unsorted_segment_sum(x.values, x.indices, - x.dense_shape[0]) - - self.evaluate(tf.global_variables_initializer()) - self.assertEqual(len(lm_vars), len(grads)) - for x, grad_x in zip(lm_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_output.avg_xent, x, delta=1e-6) - self.assertAllClose(grad_symbolic, grad_numeric, atol=0.005) - - -class GPipeTransformerLmTest(test_utils.TestCase): - - def _testParams(self, batch, dims, hidden_dim, vocab): - p = lm_layers.GPipeTransformerLm.Params() - p.name = 'transformerlm' - p.vocab_size = vocab - p.stack.num_encoder_layers = 2 - p.stack.emb_tpl.ret_task_ids = True - p.stack.emb_tpl.token_emb.vocab_size = vocab - p.stack.emb_tpl.token_emb.embedding_dim = dims - p.stack.emb_tpl.position_emb.embedding_dim = dims - p.stack.model_dim = dims - p.stack.splits = 2 - p.stack.softmax_tpl.input_dim = dims - p.stack.softmax_tpl.num_classes = vocab - p.stack.batch_dim = 0 - trans_tpl = batch_major_attention.GPipeTransformerLayer.Params() - trans_tpl.has_aux_atten = False - trans_tpl.mask_self_atten = True - trans_tpl.input_dim = dims - trans_tpl.output_dim = dims - trans_tpl.tr_atten_tpl.input_dim = dims - trans_tpl.tr_atten_tpl.hidden_dim = dims - trans_tpl.tr_atten_tpl.is_masked = True - trans_tpl.tr_atten_tpl.num_heads = 2 - trans_tpl.tr_fflayer_tpl.hidden_dim = hidden_dim - p.stack.encoder_tpl = trans_tpl - p.random_seed = 12345 - return p - - def _SetupGraph(self, p, time, batch, vocab, return_grad=False): - lm = p.Instantiate() - np.random.seed(12345) - inputs = np.random.randint(vocab, size=[time, batch]) - targets = np.zeros([time, batch]) - targets[:-1] = inputs[1:] - inputs = tf.constant(inputs, tf.int32) - paddings = np.zeros([time, batch]) - paddings[-1] = 1.0 - paddings = tf.constant(paddings, tf.float64 if return_grad else tf.float32) - targets = tf.constant(targets, tf.int32) - xent_output, _ = lm.FPropDefaultTheta( - inputs=inputs, - paddings=paddings, - labels=py_utils.NestedMap( - class_weights=1 - paddings, class_ids=targets)) - if not return_grad: - return xent_output - - lm_vars = lm.vars.Flatten() - grads = tf.gradients(xent_output.avg_xent, lm_vars) - for i, x in enumerate(grads): - if isinstance(x, tf.IndexedSlices): - grads[i] = tf.math.unsorted_segment_sum(x.values, x.indices, - x.dense_shape[0]) - self.assertEqual(len(lm_vars), len(grads)) - return xent_output, lm_vars, grads - - def testBasic(self): - time, batch, dims, hidden_dim, vocab = 5, 3, 6, 4, 8 - p = self._testParams(batch, dims, hidden_dim, vocab) - xent_output = self._SetupGraph(p, time, batch, vocab) - assert p.stack.encoder_tpl.tr_atten_tpl.is_masked - with self.session(): - self.evaluate(tf.global_variables_initializer()) - xent_output_val = self.evaluate(xent_output) - - print('xent_output_val', xent_output_val) - test_utils.CompareToGoldenSingleFloat(self, 5.12745, - xent_output_val.avg_xent) - self.assertAllEqual(xent_output_val.per_example_argmax, - np.argmax(xent_output_val.logits, axis=-1)) - - def testBasicGrad(self): - time, batch, dims, hidden_dim, vocab = 5, 3, 6, 4, 8 - p = self._testParams(batch, dims, hidden_dim, vocab) - p.dtype = tf.float64 - xent_output, lm_vars, grads = self._SetupGraph( - p, time, batch, vocab, return_grad=True) - with self.session() as sess: - self.evaluate(tf.global_variables_initializer()) - for x, grad_x in zip(lm_vars, grads): - grad_symbolic = self.evaluate(grad_x) - grad_numeric = test_utils.ComputeNumericGradient( - sess, xent_output.avg_xent, x, delta=1e-6) - self.assertAllClose(grad_symbolic, grad_numeric, atol=0.05) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/lm/model.py b/lingvo/tasks/lm/model.py deleted file mode 100644 index d0d86217..00000000 --- a/lingvo/tasks/lm/model.py +++ /dev/null @@ -1,288 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""LM models.""" - -import lingvo.compat as tf -from lingvo.core import base_model -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.tasks.lm import layers - - -class LanguageModel(base_model.BaseTask): - """Language model.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('lm', layers.RnnLm.Params(), 'LM layer.') - - tp = p.train - tp.Define( - 'max_lstm_gradient_norm', 0.0, - 'Clip gradient for vars in lstm layers by setting this value to ' - 'something > 0.') - tp.Define( - 'sum_loss_across_tokens_in_batch', False, - 'Sum the logP across predicted tokens in batch when set to True; ' - 'average across predicted tokens in batch o/w (default).') - - tp.lr_schedule = schedule.PiecewiseConstantSchedule.Params().Set( - boundaries=[350000, 500000, 600000], values=[1.0, 0.1, 0.01, 0.001]) - tp.vn_start_step = 20000 - tp.vn_std = 0.0 - tp.learning_rate = 0.001 - tp.l2_regularizer_weight = 1e-6 - tp.clip_gradient_norm_to_value = 1.0 - tp.grad_norm_to_clip_to_zero = 100.0 - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - assert p.lm.vocab_size == p.input.tokenizer.vocab_size, ( - 'lm.vocab_size does not match input.tokenizer.vocab_size: %d vs %d' % - (p.lm.vocab_size, p.input.tokenizer.vocab_size)) - - # Construct the model. - self.CreateChild('lm', p.lm) - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Updates the params with the input vocab_size and WPM model. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model params updated with the vocab size and wpm model. - """ - p.lm = p.lm.cls.UpdateTargetVocabSize(p.lm, vocab_size, wpm_model) - return p - - def _TrimIfPossibleThenTranspose(self, ids, paddings, labels, weights): - data = (ids, paddings, labels, weights) - if not py_utils.use_tpu(): - max_seq_len = tf.cast( - tf.reduce_max(tf.reduce_sum(1.0 - paddings, 1)), tf.int32) - data = (x[:, :max_seq_len] for x in data) - return (tf.transpose(x) for x in data) - - def FPropTower(self, theta, input_batch): - p = self.params - ids, paddings, labels_ids, weights = self._TrimIfPossibleThenTranspose( - input_batch.ids, input_batch.paddings, input_batch.labels, - input_batch.weights) - - batch_size = tf.shape(ids)[1] - state0 = self.lm.zero_state(theta.lm, batch_size) - labels = py_utils.NestedMap(class_ids=labels_ids, class_weights=weights) - xent_output, _ = self.lm.FProp(theta.lm, ids, paddings, state0, labels) - - # +1 to account for the end of sequence symbol. - num_words = tf.cast( - tf.reduce_sum(input_batch.word_count + tf.constant(1, dtype=tf.int32)), - tf.float32) - predicted_labels = tf.cast(xent_output.per_example_argmax, labels_ids.dtype) - - num_preds = xent_output.total_weight - mean_acc = tf.reduce_sum( - tf.cast(tf.equal(labels_ids, predicted_labels), tf.float32) * - weights) / ( - num_preds + 1e-4) - - loss = xent_output.avg_xent - if p.train.sum_loss_across_tokens_in_batch: - loss = xent_output.total_xent - return { - 'loss': (loss, num_preds), - 'fraction_of_correct_next_step_preds': (mean_acc, num_preds), - 'log_pplx': (xent_output.avg_xent, num_preds), - 'log_pplx_per_word': (xent_output.total_xent / num_words, num_words), - 'num_predictions': (num_preds, 1), - 'num_words': (num_words, 1) - }, {} - - def AdjustGradients(self, var_grad): - """Clip LSTM gradients. - - Args: - var_grad: a `.NestedMap` of (variable, gradient). You can view - `var_grad` as an ordered list of (key, (var, grad)) tuples. Every - key of `var_grad` exists in `vmap`. Every variable in `vmap` that - contributes to loss must exist in `var_grad`. Every var of `var_grad` - must exist in `vmap`. `grad` is the corresponding gradient computed - for `var`. `grad` is guaranteed to be not None. - - Returns: - adjusted version of `var_grad` that has clipped the LSTM gradients - if `self.params.max_lstm_gradient_norm` is set. - """ - - p = self.params - if p.train.max_lstm_gradient_norm: - lstm_var_grad = var_grad.lm.rnns - lstm_vars = lstm_var_grad.Transform(lambda x: x[0]).Flatten() - lstm_grads = lstm_var_grad.Transform(lambda x: x[1]).Flatten() - clipped_lstm_grads, _ = tf.clip_by_global_norm( - lstm_grads, p.train.max_lstm_gradient_norm) - var_grad.lm.rnns = var_grad.lm.rnns.Pack( - list(zip(lstm_vars, clipped_lstm_grads))) - - return var_grad - - def Inference(self): - """Constructs the inference subgraphs. - - Returns: - dict: ``{'subgraph_name': (fetches, feeds)}`` - """ - subgraphs = {} - with tf.name_scope('inference'): - subgraphs['default'] = self._InferenceSubgraph_Default() - return subgraphs - - def _InferenceSubgraph_Default(self): - """Default inference subgraph. - - Returns: - (fetches, feeds): - - - fetches: A dictionary of fetches, containing: - - - log_pplx_per_token: A matrix of shape [batch, time]. [i, j] - is i-th input text's j-th token's log prob. - - paddings: A matrix of shape [batch, time]. The padding mask. - - log_pplx_per_sample: A vector of shape [batch]. [i] - is i-th input text's log prob. - - num_oovs_per_sample: A vector of shape [batch] counting the total - number of out-of-vocabulary tokens in each input. - - tokens_from_labels: A vector of shape [batch] returning the predicted - tokens as a sequence after mapping them back to strings from ids using - the vocabulary. - - ids: A matrix of shape [batch, time]. [i, j] - is i-th input text's j-th token's id. - - - feeds: A dictionary of feeds, containing: - - - text: A placeholder for a vector of strings. - """ - text = tf.placeholder(tf.string, shape=[None]) - # [batch, time] - ids, labels, paddings = self.input_generator.StringsToIds(text) - lengths = tf.reduce_sum(tf.cast(1 - paddings, tf.int32), axis=1) - tokens_from_labels = self.input_generator.IdsToStrings(labels, lengths) - oovs = tf.equal(labels, self.input_generator.tokenizer.unk_id) - num_oovs_per_sample = tf.cast( - tf.round( - tf.reduce_sum(tf.cast(oovs, tf.float32) * (1 - paddings), axis=1)), - tf.int32) - # [time, batch] - ids, paddings, labels, weights = self._TrimIfPossibleThenTranspose( - ids, paddings, labels, 1.0 - paddings) - batch_size = tf.shape(ids)[1] - xent_output, _ = self.lm.FPropDefaultTheta( - inputs=ids, - paddings=paddings, - state0=self.lm.zero_state(self.theta.lm, batch_size), - labels=py_utils.NestedMap(class_ids=labels, class_weights=weights)) - - per_example_xent = py_utils.HasShape(xent_output.per_example_xent, - tf.shape(ids)) - log_pplx_per_sample = tf.reduce_sum( - per_example_xent * (1 - paddings), axis=0) - fetches = { - 'log_pplx_per_token': # [batch, time] - tf.transpose(per_example_xent), - 'paddings': # [batch, time] - tf.transpose(paddings), - 'lengths': # [batch] - lengths, - 'log_pplx_per_sample': # [batch] - log_pplx_per_sample, - 'num_oovs_per_sample': # [batch], int32 - num_oovs_per_sample, - 'tokens_from_labels': # [batch], string - tokens_from_labels, - 'ids': # [batch, time], int32 - ids - } - feeds = {'text': text} - return fetches, feeds - - -class FixedShapeInputLanguageModel(LanguageModel): - - def _TrimIfPossibleThenTranspose(self, ids, paddings, labels, weights): - data = (ids, paddings, labels, weights) - if not py_utils.use_tpu() and self.do_eval: - max_seq_len = tf.cast( - tf.reduce_max(tf.reduce_sum(1.0 - paddings, 1)), tf.int32) - data = (x[:, :max_seq_len] for x in data) - return (tf.transpose(x) for x in data) - - -class BatchMajorLanguageModel(LanguageModel): - """Batch major implementation of the language model.""" - - def _TrimIfPossible(self, ids, paddings, labels, weights): - data = (ids, paddings, labels, weights) - if not py_utils.use_tpu(): - max_seq_len = tf.cast( - tf.reduce_max(tf.reduce_sum(1.0 - paddings, 1)), tf.int32) - data = (x[:, :max_seq_len] for x in data) - return data - - def FPropTower(self, theta, input_batch): - p = self.params - tf.logging.info('input_batch=%r', input_batch) - ids, paddings, labels_ids, weights = self._TrimIfPossible( - input_batch.ids, input_batch.paddings, input_batch.labels, - input_batch.weights) - fprop_dtype = py_utils.FPropDtype(p) - paddings = tf.cast(paddings, fprop_dtype) - weights = tf.cast(weights, fprop_dtype) - tf.logging.info('inputs={}'.format((ids, paddings, labels_ids, weights))) - - batch_size = tf.shape(ids)[0] - state0 = self.lm.zero_state(theta.lm, batch_size) - labels = py_utils.NestedMap(class_ids=labels_ids, class_weights=weights) - xent_output, _ = self.lm.FProp(theta.lm, ids, paddings, state0, labels) - - # +1 to account for the end of sequence symbol. - num_words = tf.cast( - tf.reduce_sum(input_batch.word_count + tf.constant(1, dtype=tf.int32)), - fprop_dtype) - predicted_labels = tf.cast(xent_output.per_example_argmax, labels_ids.dtype) - - num_preds = xent_output.total_weight - mean_acc = tf.reduce_sum( - tf.cast(tf.equal(labels_ids, predicted_labels), fprop_dtype) * - weights) / tf.math.maximum(num_preds, 1) - loss = xent_output.avg_xent - if p.train.sum_loss_across_tokens_in_batch: - loss = xent_output.total_xent - return { - 'loss': (loss, num_preds), - 'fraction_of_correct_next_step_preds': (mean_acc, num_preds), - 'log_pplx': (xent_output.avg_xent, num_preds), - 'log_pplx_per_word': (xent_output.total_xent / num_words, num_words), - 'num_predictions': (num_preds, 1), - 'num_words': (num_words, 1) - }, {} diff --git a/lingvo/tasks/lm/model_test.py b/lingvo/tasks/lm/model_test.py deleted file mode 100644 index f4c6cd76..00000000 --- a/lingvo/tasks/lm/model_test.py +++ /dev/null @@ -1,170 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for lm.model.""" - -import lingvo.compat as tf -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.core import tokenizers -from lingvo.tasks.lm import input_generator -from lingvo.tasks.lm import model - - -class ModelTest(test_utils.TestCase): - - def _InputParams(self, for_training): - p = input_generator.LmInput.Params() - p.file_pattern = 'text:' + test_helper.test_src_dir_path( - 'tasks/lm/testdata/lm1b_100.txt') - if for_training: - p.file_buffer_size = 10000 - p.bucket_upper_bound = [50] - p.bucket_batch_limit = [64] - p.target_max_length = 1000 - else: - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.file_buffer_size = 1 - p.bucket_upper_bound = [20] - p.bucket_batch_limit = [2] - p.target_max_length = 20 - p.tokenizer.vocab_size = 64 - return p - - def _Params(self): - p = model.LanguageModel.Params() - p.name = 'lm_test' - vocab, dims = 64, 64 - p.lm.vocab_size = vocab - p.lm.emb.vocab_size = vocab - p.lm.emb.embedding_dim = dims - p.lm.rnns.num_layers = 4 - p.lm.rnns.cell_tpl.num_output_nodes = dims - p.lm.rnns.cell_tpl.num_input_nodes = dims - p.lm.softmax.input_dim = dims - p.lm.softmax.num_classes = vocab - return p - - def testLmFprop(self): - tf.random.set_seed(93820986) - p = self._Params() - p.input = self._InputParams(for_training=False) - - with self.session(use_gpu=False): - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.eval_metrics['loss'][0] - logp = mdl.eval_metrics['log_pplx'][0] - logp_per_word = mdl.eval_metrics['log_pplx_per_word'][0] - accuracy = mdl.eval_metrics['fraction_of_correct_next_step_preds'][0] - self.evaluate(tf.global_variables_initializer()) - - loss, logp, logp_per_word, accuracy = self.evaluate( - [loss, logp, logp_per_word, accuracy]) - test_utils.CompareToGoldenSingleFloat(self, 4.160992, loss) - test_utils.CompareToGoldenSingleFloat(self, 4.160992, logp) - test_utils.CompareToGoldenSingleFloat(self, 5.944274, logp_per_word) - test_utils.CompareToGoldenSingleFloat(self, 0.000000, accuracy) - - def testLmTrain(self): - p = self._Params() - p.input = self._InputParams(for_training=True) - tp = p.train - tp.learning_rate = 3e-3 - - with self.session(): - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - mdl.BProp() - loss = mdl.eval_metrics['loss'][0] - self.evaluate(tf.global_variables_initializer()) - - # Run some steps and we expect the loss goes down. - loss_val, _ = self.evaluate([loss, mdl.train_op]) - self.assertGreater(loss_val, 4.0) - for i in range(10): - loss_val, _ = self.evaluate([loss, mdl.train_op]) - tf.logging.info('%d loss = %f', i, loss_val) - self.assertLess(loss_val, 3.8) - - def testLmInference(self): - tf.random.set_seed(93820986) - p = self._Params() - p.input = self._InputParams(for_training=False) - tf.logging.info('Params: %s', p.ToText()) - - with self.session(use_gpu=False) as sess: - mdl = p.Instantiate() - subgraphs = mdl.Inference() - self.assertIn('default', subgraphs) - fetches, feeds = subgraphs['default'] - self.evaluate(tf.global_variables_initializer()) - vals = sess.run( - fetches=fetches, - feed_dict={feeds['text']: ['pray for world peace', 'happy birthday']}) - print('actual vals = ', vals) - self.assertEqual(vals['log_pplx_per_sample'].shape, (2,)) - self.assertEqual(vals['log_pplx_per_token'].shape, (2, 20)) - self.assertEqual(vals['paddings'].shape, (2, 20)) - - def testLmInferenceWordLevel(self): - tf.random.set_seed(93820986) - p = self._Params() - p.input = self._InputParams(for_training=False) - p.input.tokenizer = tokenizers.VocabFileTokenizer.Params() - p.input.tokenizer.vocab_size = 64 - p.input.target_max_length = 5 - p.input.pad_to_max_seq_length = True - # target_{sos,eos,unk}_id must be consistent with token_vocab_filepath. - p.input.tokenizer.target_sos_id = 1 - p.input.tokenizer.target_eos_id = 2 - p.input.tokenizer.target_unk_id = 3 - p.input.tokenizer.token_vocab_filepath = test_helper.test_src_dir_path( - 'tasks/lm/testdata/small_word_vocab.txt') - tf.logging.info('Params: %s', p.ToText()) - - with self.session(use_gpu=False) as sess: - mdl = p.Instantiate() - subgraphs = mdl.Inference() - self.assertIn('default', subgraphs) - fetches, feeds = subgraphs['default'] - self.evaluate(tf.global_variables_initializer()) - vals = sess.run( - fetches=fetches, - feed_dict={ - feeds['text']: [ - 'pray for more peace', 'happy about', 'one flambergastic will' - ] - }) - print('actual vals = ', vals) - self.assertEqual(vals['log_pplx_per_sample'].shape, (3,)) - self.assertEqual(vals['log_pplx_per_token'].shape, (3, 5)) - self.assertEqual(vals['paddings'].shape, (3, 5)) - expected_tokens_from_labels = [ - b' for more
    ', b' about
    ', - b'one will
    ' - ] - self.assertListEqual(vals['tokens_from_labels'].tolist(), - expected_tokens_from_labels) - expected_num_oovs_per_sample = [2, 1, 1] - self.assertListEqual(vals['num_oovs_per_sample'].tolist(), - expected_num_oovs_per_sample) - expected_ids = [[1, 1, 1], [3, 3, 41], [21, 8, 3], [35, 2, 61], [3, 2, 2]] - self.assertListEqual(vals['ids'].tolist(), expected_ids) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/lm/params/BUILD b/lingvo/tasks/lm/params/BUILD deleted file mode 100644 index b3d44db3..00000000 --- a/lingvo/tasks/lm/params/BUILD +++ /dev/null @@ -1,33 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "one_billion_wds", - srcs = ["one_billion_wds.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:model_registry", - "//lingvo/core:base_model_params", - "//lingvo/core:layers", - "//lingvo/core:optimizer", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - "//lingvo/core:tokenizers", - "//lingvo/tasks/lm:input_generator", - "//lingvo/tasks/lm:layers", - "//lingvo/tasks/lm:model", - ], -) - -py_library( - name = "params", - srcs = ["params.py"], - srcs_version = "PY3", - tags = ["keep_dep"], - deps = [ - ":one_billion_wds", - ], -) diff --git a/lingvo/tasks/lm/params/one_billion_wds.py b/lingvo/tasks/lm/params/one_billion_wds.py deleted file mode 100644 index 28928011..00000000 --- a/lingvo/tasks/lm/params/one_billion_wds.py +++ /dev/null @@ -1,259 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Train word-level LMs on 1 Billion Words benchmark data.""" - -import os - -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.core import layers -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import tokenizers -from lingvo.tasks.lm import input_generator as lm_inp -from lingvo.tasks.lm import layers as lm_layers -from lingvo.tasks.lm import model - - -class WordLevelOneBwdsBase(base_model_params.SingleTaskModelParams): - """Params for training a word-level LM on One Billion Wds text corpus. - - Tries to match https://github.com/rafaljozefowicz/lm. - """ - - # Generated using lingvo/tasks/lm/tools:download_lm1b. - CORPUS_DIR = '/tmp/lm1b/1-billion-word-language-modeling-benchmark-r13output/' - - # BIG-LSTM model size: embedding/projection dim = 1024; LSTM state dim = 8192 - EMBEDDING_DIM = 1024 - MAX_TOKENS = 1024 - NUM_EMBEDDING_SHARDS = 8 - NUM_SAMPLED = 8192 - NUM_SOFTMAX_SHARDS = 8 - RNN_STATE_DIM = 8192 - VOCAB_SIZE = 793472 # includes - WORD_VOCAB = os.path.join(CORPUS_DIR, 'vocab.txt') - - def Train(self): - p = lm_inp.LmInput.Params() - p.bucket_upper_bound = [10, 20, 30, 40, 50, 100, 256, 512, 1024] - p.bucket_batch_limit = [1024, 512, 256, 256, 128, 128, 64, 32, 16] - p.file_buffer_size = 10000000 - p.file_parallelism = 10 - p.file_pattern = 'text:' + os.path.join( - self.CORPUS_DIR, 'training-monolingual.tokenized.shuffled', 'news.en*') - p.name = '1bwds_train_set' - p.tokenizer = tokenizers.VocabFileTokenizer.Params() - p.num_batcher_threads = 16 - p.target_max_length = self.MAX_TOKENS - p.tokenizer.target_sos_id = 1 - p.tokenizer.target_eos_id = 2 - p.tokenizer.target_unk_id = 3 - p.tokenizer.token_vocab_filepath = self.WORD_VOCAB - p.tokenizer.vocab_size = self.VOCAB_SIZE - return p - - def Dev(self): - p = self.Train() - # Use small batches for eval. - p.bucket_upper_bound = [10, 20, 30, 40, 50, 100, 256, 512, 1024] - p.bucket_batch_limit = [128, 64, 32, 32, 16, 16, 4, 2, 1] - p.file_buffer_size = 1 - p.file_parallelism = 1 - p.file_pattern = 'text:' + os.path.join( - self.CORPUS_DIR, 'heldout-monolingual.tokenized.shuffled', - 'news.en.heldout-00001*') - p.name = '1bwds_dev_set' - p.num_batcher_threads = 1 - p.num_samples = 6206 # Number of sentences to evaluate on. - return p - - def Test(self): - p = self.Dev() - p.file_pattern = 'text:' + os.path.join( - self.CORPUS_DIR, 'heldout-monolingual.tokenized.shuffled', - 'news.en.heldout-00000*') - p.name = '1bwds_test_set' - p.num_samples = 6075 # Number of sentences to evaluate on. - return p - - def Task(self): - p = model.LanguageModel.Params() - p.name = '1bwds_word_level_lm' - p.eval.samples_per_summary = 10000 - - p.lm = lm_layers.RnnLm.CommonParams( - vocab_size=self.VOCAB_SIZE, - emb_dim=self.EMBEDDING_DIM, - num_layers=2, - residual_start=3, # disable residuals - rnn_dims=self.EMBEDDING_DIM, - rnn_hidden_dims=self.RNN_STATE_DIM) - - # Input embedding needs to be sharded. - p.lm.emb.max_num_shards = self.NUM_EMBEDDING_SHARDS - p.lm.embedding_dropout_keep_prob = 0.75 - # Match the initialization in github code. - p.lm.emb.params_init = py_utils.WeightInit.UniformUnitScaling( - 1.0 * self.NUM_EMBEDDING_SHARDS) - - # We also want dropout after each of the RNN layers. - p.lm.rnns.dropout.keep_prob = 0.75 - - # Adjusts training params. - tp = p.train - tp.sum_loss_across_tokens_in_batch = True - # Disable any so called "clipping" (gradient scaling really). - tp.clip_gradient_norm_to_value = 0.0 - tp.grad_norm_to_clip_to_zero = 0.0 - # Do clip the LSTM gradients. - tp.max_lstm_gradient_norm = 16 - # Straight Adagrad; very sensitive to initial accumulator value, the default - # 0.1 value is far from adequate. - # TODO(ciprianchelba): tune accumulator value, learning rate, clipping - # threshold. - tp.learning_rate = 0.2 - tp.lr_schedule = ( - schedule.PiecewiseConstantSchedule.Params().Set( - boundaries=[], values=[1.0])) - tp.l2_regularizer_weight = None # No regularization. - tp.optimizer = optimizer.Adagrad.Params() - return p - - -@model_registry.RegisterSingleTaskModel -class WordLevelOneBwdsSimpleSampledSoftmax(WordLevelOneBwdsBase): - """Use sampled soft-max in training.""" - - def Task(self): - p = super().Task() - num_input_dim = p.lm.softmax.input_dim - p.lm.softmax = layers.SimpleFullSoftmax.Params() - p.lm.softmax.input_dim = num_input_dim - p.lm.softmax.num_classes = self.VOCAB_SIZE - p.lm.softmax.num_sampled = self.NUM_SAMPLED - p.lm.softmax.num_shards = self.NUM_SOFTMAX_SHARDS - # Match the initialization in github code. - p.lm.softmax.params_init = py_utils.WeightInit.UniformUnitScaling( - 1.0 * self.NUM_SOFTMAX_SHARDS) - assert p.lm.softmax.num_classes % p.lm.softmax.num_shards == 0 - return p - - -@model_registry.RegisterSingleTaskModel -class WordLevelOneBwdsSimpleSampledSoftmaxTiny( - WordLevelOneBwdsSimpleSampledSoftmax): - """Tiny model size for local, debugging runs of the above.""" - - EMBEDDING_DIM = 7 - MAX_TOKENS = 1024 - NUM_EMBEDDING_SHARDS = 1 - NUM_SAMPLED = 8 - NUM_SOFTMAX_SHARDS = 8 - RNN_STATE_DIM = 32 - - -# Example large transformer model using GPIPE. -# Instructions: -# trainer --run_locally=gpu --mode=sync \ -# --model=lm.one_billion_wds.OneBWdsGPipeTransformerWPM \ -# --logdir=/tmp/lm/log --logtostderr --worker_split_size=4 --worker_gpus=4 -# Relative throughput on multiple V100s, each with 16GB ram. -# GPUs throughput -# 1 1 -# 2 0.93 -# 4 0.85 -# 8 0.775 -@model_registry.RegisterSingleTaskModel -class OneBWdsGPipeTransformerWPM(WordLevelOneBwdsBase): - """LM using gpipe transformer.""" - VOCAB_SIZE = 32000 - EMBEDDING_DIM = 2048 - BATCH_SIZE = 32 - MAX_TOKENS = 1024 # The max sequence length in one example. - - # GPIPE related params. - GPUS = 4 - # A list of ending index for each split/partition in ascending order. - # For example SPLITS = [8, 16, 24, 32] defined a 32 layer model with 4 splits, - # each of which contains 8 layers. - # The number belows runs on 16GB-V100s. Your mileage may vary. - SPLITS = [8 * (i + 1) for i in range(GPUS)] - LAYERS = SPLITS[-1] - # Set NUM_MICRO_BATCHES >= len(SPLITS) * 4 to minimize gpipe bubble. - NUM_MICRO_BATCHES = 32 - - def Train(self): - p = super().Train() - # Replace it with your own wordpiece tokenizer. - p.tokenizer = tokenizers.AsciiTokenizer.Params() - p.target_max_length = self.MAX_TOKENS - p.tokenizer.target_sos_id = 1 - p.tokenizer.target_eos_id = 2 - p.tokenizer.target_unk_id = 0 - p.tokenizer.vocab_size = self.VOCAB_SIZE - p.bucket_upper_bound = [self.MAX_TOKENS] - p.bucket_batch_limit = [self.BATCH_SIZE] - p.fixed_input_shape = True - return p - - def Dev(self): - p = self.Train() - p.file_pattern = 'text:' + os.path.join( - self.CORPUS_DIR, 'heldout-monolingual.tokenized.shuffled', - 'news.en.heldout-00001*') - p.name = '1bwds_dev_set' - p.num_batcher_threads = 1 - p.num_samples = 6206 # Number of sentences to evaluate on. - return p - - def Test(self): - p = self.Dev() - p.file_pattern = 'text:' + os.path.join( - self.CORPUS_DIR, 'heldout-monolingual.tokenized.shuffled', - 'news.en.heldout-00000*') - p.name = '1bwds_test_set' - p.num_samples = 6075 # Number of sentences to evaluate on. - return p - - def Task(self): - """Language model on 1bw dataset using gpipe transformer.""" - p = model.BatchMajorLanguageModel.Params() - p.eval.samples_per_summary = 0 - p.name = '1bwds_wpm_level_lm' - p.lm = lm_layers.GPipeTransformerLm.CommonParams( - model_dim=self.EMBEDDING_DIM, - vocab_size=self.VOCAB_SIZE, - hidden_dim=self.EMBEDDING_DIM * 4, - num_layers=self.LAYERS, - splits=self.SPLITS, - num_micro_batches=self.NUM_MICRO_BATCHES, - micro_batch_size=self.BATCH_SIZE // self.NUM_MICRO_BATCHES, - num_heads=16, - softmax_max_alloc=128 * (2**20), - atten_dropout_prob=0.1, - residual_dropout_prob=0.1) - - p.train.Set( - learning_rate=0.5, - optimizer=optimizer.Adam.ParamsA(), - clip_gradient_norm_to_value=0.0, - grad_norm_to_clip_to_zero=0.0, - lr_schedule=schedule.TransformerSchedule.Params().Set( - warmup_steps=40000, worker_replicas=1, - model_dim=self.EMBEDDING_DIM)) - return p diff --git a/lingvo/tasks/lm/params/params.py b/lingvo/tasks/lm/params/params.py deleted file mode 100644 index 46ffb9ff..00000000 --- a/lingvo/tasks/lm/params/params.py +++ /dev/null @@ -1,21 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Language models hyper-parameters.""" - -# Import ModelParams to ensure that they are added to the global registry. -# pylint: disable=unused-import -import lingvo.tasks.lm.params.one_billion_wds -# pylint: enable=unused-import diff --git a/lingvo/tasks/lm/testdata/BUILD b/lingvo/tasks/lm/testdata/BUILD deleted file mode 100644 index 5bcb9ff3..00000000 --- a/lingvo/tasks/lm/testdata/BUILD +++ /dev/null @@ -1,16 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) # Apache 2.0 - -# First 100 training sentences from the 1 Billion Word Language Model Benchmark. -# http://www.statmt.org/lm-benchmark -filegroup( - name = "lm1b_100", - data = ["lm1b_100.txt"], -) - -# Small word-level vocab file (taken from a list of stopwords). -filegroup( - name = "small_word_vocab", - data = ["small_word_vocab.txt"], -) diff --git a/lingvo/tasks/lm/testdata/lm1b_100.txt b/lingvo/tasks/lm/testdata/lm1b_100.txt deleted file mode 100644 index 0197314b..00000000 --- a/lingvo/tasks/lm/testdata/lm1b_100.txt +++ /dev/null @@ -1,100 +0,0 @@ -The U.S. Centers for Disease Control and Prevention initially advised school systems to close if outbreaks occurred , then reversed itself , saying the apparent mildness of the virus meant most schools and day care centers should stay open , even if they had confirmed cases of swine flu . -When Ms. Winfrey invited Suzanne Somers to share her controversial views about bio-identical hormone treatment on her syndicated show in 2009 , it won Ms. Winfrey a rare dollop of unflattering press , including a Newsweek cover story titled " Crazy Talk : Oprah , Wacky Cures & You . " -Elk calling -- a skill that hunters perfected long ago to lure game with the promise of a little romance -- is now its own sport . -Don 't ! -Fish , ranked 98th in the world , fired 22 aces en route to a 6-3 , 6-7 ( 5 / 7 ) , 7-6 ( 7 / 4 ) win over seventh-seeded Argentinian David Nalbandian . -Why does everything have to become such a big issue ? -AMMAN ( Reuters ) - King Abdullah of Jordan will meet U.S. President Barack Obama in Washington on April 21 to lobby on behalf of Arab states for a stronger U.S. role in Middle East peacemaking , palace officials said on Sunday . -To help keep traffic flowing the Congestion Charge will remain in operation through-out the strike and TfL will be suspending road works on major London roads wherever possible . -If no candidate wins an absolute majority , there will be a runoff between the top two contenders , most likely in mid-October . -Authorities previously served search warrants at Murray 's Las Vegas home and his businesses in Las Vegas and Houston . -Brent North Sea crude for November delivery rose 84 cents to 68.88 dollars a barrel . -That seems to have been their model up til now . -Gordon will join Luol Deng on the GB team ; their respective NBA teams , the Detroit Pistons and the Chicago Bulls , play tonight . -Nikam maintains the attacks were masterminded by the Muslim militant group Lashkar-e-Taiba . -Last year , Williams was unseeded , ranked 81st and coming off one of her worst losses on tour -- in a Tier 4 event at Hobart -- yet she beat six seeded players en route to the title at Melbourne Park . -It said that two officers involved in the case had been disciplined . -" There is more intelligence now being gathered , " the official said , adding that such efforts would continue for some time . -The majority will be of the standard 6X6 configuration for carrying personnel . -" Consequently , necessary actions may not be taken to reduce the risks to children of sexual exploitation and drug or alcohol misuse , " the report said . • Almost two-thirds of inspected schools were good or outstanding , but the number of underperforming secondaries remained " stubborn and persistent . " -What a World Cup . -But , there have also been many cases of individuals and small groups of people protesting , as in the case of Rongye Adak , a nomad who called for the return of the Dalai Lama and for the freedom of Tibet during the Lithang Horse Racing Festival , in eastern Tibet . -James Duncan , head of transportation at Bournemouth Borough Council , said : " Our legal team is reviewing the entitlement of taxis to drop and pick up passengers at bus stops , only for as long as is absolutely necessary to fulfil that function and for no other reason . -To Mo concerning the food log you kept -- Dr. Buchholz recommends the same thing . -The CBO estimates that only 23 percent of that would be spent in 2009 and 2010 . -Even so , Democrats slammed Bush as out of touch . -An information campaign will be launched later to raise awareness of employment rights and how to enforce them . -At the gallery the concept is less vague , as Ms. Piper cites specific instances of racial violence , political assassinations and the devastation of Hurricane Katrina . -There have been some exceptions -- such as Medicare in 1965 . -The government guidance will be reviewed early next year after a period of public comment . -It wasn 't the most seaworthy of prizes . -LOUIS - A former Anheuser-Busch executive who was the company 's top-ranking woman has sued the brewer claiming it encourages a " frat party " atmosphere and pays women less in salary and bonuses than men . -What does it say about Pelosi that she lost 95 dem votes when she has all the power . -Skea , a 3-month-old Taipei store that has nothing to do with Ikea , sells custom-made , small-scale buildings as well as cars , boats , baseball mitts or just about anything else that can be made from paper , cardboard and light wood . -A dozen athletes with connections to Lake Placid competed in Vancouver . -They owe $ 10,500 on their car . -" We need them to move on and accept change , because their problems are a distraction from our goal of building a more integrated Asean , " said Ong Keng Yong , the association 's secretary general . -The internet is sort-of-40 this year . -His approach was inquisitive , a meeting of artful hesitation with fluid technique . -Katrin 's father , Dr Edmund Radmacher , inherited a chemistry company that he transformed into a flourishing concern . -On that basis Mr Perry is not a man fit for higher office . -Grand ladies in exquisitely tailored houndstooth check may have had husbands who bought them their rarefied Dior clothing , but it is only too clear who was wearing the trousers . -The two men reportedly lived alone in the shelter , a two-bedroom flat that is run by a private social care company , financed partly by the city authorities . -A lot rode on the opening lead . -Why you support the FAKE war on terror I understand , you are just following orders . -The motive for the attack on the pope remains unclear but it has not been linked to Islamic issues . -A post-mortem examination is due to be carried out in the next few days , a spokesman said . -HBOS is the lead bank in a syndicate that includes Lloyds TSB , Royal Bank of Scotland , HSBC and National Australia Bank and granted New Star the loan last year . -You do , however , still have to choose between those technologies on most larger screens . -There are currently some 5 million homeowners that are 90 days or more past due on their mortgages , according to Fannie Mae chief economist Doug Duncan . -There was an extended paralysis of racing in the major horse racing states of New South Wales and Queensland , including the loss of popular traditional racing carnivals . -They wanted things to be monumental , so they could be seen from a distance , " explains Laurence Mouillefarine , associate curator of the show , picking out some big , showy brooches to illustrate the point . -More than 45,000 women are diagnosed with breast cancer each year in the UK , and more than 12,000 die from the disease . -That agency 's grant panels do not use culturally specific criteria when awarding money . -In the five months since he started the company , he has proven his hunch : People are willing to pay for New York City tap water . -They also maintain that because Google 's system for identifying and displaying ads is more lucrative than Yahoo 's approach , the deal will generate additional revenue for Yahoo that will it make it a more formidable competitor to both Google and Microsoft . -Forecasts called for plunging temperatures and afternoon rain storms in the early Southern Hemisphere autumn . -Kloppers said BHP would continue talking to its customers about the merger . -Sailing is a passion . -I would argue that the media caters just to that , if not simple minded , to those who are not disciplined enough to see out the truth , THEIR OWN truth . -Android is being developed , " assures Mobiclip 's vice-president of marketing Denis Pagnac . -LONDON ( Reuters ) - Want to know your chance of having a baby through in-vitro fertilization ? -NEW YORK ( Reuters ) - Toll Brothers ( TOL.N : Quote , Profile , Research ) said on Thursday that it expected to report a 36 percent drop in quarterly home-building revenue , while net orders for new homes fell more steeply than in prior quarters , reflecting last month 's deepening decline in the U.S. housing market . -The six , who call themselves " Ztohoven , " claim that the aim of the project - called Media Reality - was to show how reality can be manipulated by the media . -Stupak had pledged to oppose the health care package unless given greater assurance that it would not allow federal funding of elective abortions . -Sheik Salman al-Feraiji , al-Sadr 's chief representative in Sadr City , issued a statement with demands to quell the discontent , including the release of Sadrist detainees , an end to military operations against them and al-Maliki 's resignation . -Cowles declined to comment to ABC News for this story . -In fairness , the Scots finished the half more adeptly than they 'd started and McFadden was unlucky not to win a penalty when he was obstructed in the box . -Results from dozens of clinical studies will be released at the gathering opening Friday and running through June 2 . -There , 1 per cent of the population owns 49 per cent of the land ; here , 0.3 per cent owns 69 per cent . -" One wall is enough , " he said . -" Next question , " said Fisher with a smile . -He seems genuinely affected and I almost suggest we meet again , informally as friends , but retrieve the last scraps of my dignity . -Prosecutors said Plotkin ran the schemes , enlisting David Pajcin , a former Goldman Sachs Group Inc. analyst who has pleaded guilty to charges in the case and is cooperating with the government . -In anticipation of prolonged outages , Rockford city officials coordinated assistance to provide access to warming centers for residents . -From TPP-- that 's how i see it. it 's a resource . -In the nearby village of Tepalcatepec , concrete homes built with dollars sent home by migrants stand next to tin-roof shacks . -They do not listen , or even know how to listen , they are operating on " eternal truths " that are not open to debate . -Most vacationers visit America 's national parks during the summer months--when the roads are crowded , restaurants are packed and prices are at their peak . -Good , there you go ! -" In World War II , we knew who the enemy was and we fought with them . -To some this may seem puzzling , given that dishonour is spread fairly evenly between both of the main parties . -3 ( UPI ) -- An Arizona elementary school principal placed on leave for writing a letter jokingly calling children " stupid " has apologized to parents , officials said . -So embrace the idea of big . -Reclusive North Korean leader Kim Jong Il isn 't always easy to explain . -Items were stolen during the incident . -Santiago tried to stop the fight after the 11th round , but Cotto went back out to take even more punishment before a final flurry along the ropes prompted referee Kenny Bayless to end it . -Knecht and Benner presented the fossil last month in Krakow , Poland , during the Second International Congress on Ichnology . -John McMahon , manager of the Torquay Tigers Football Club , said the pictures of Klan members were posted on the Internet by a volunteer , a retired member of the club , without the knowledge of officials , The Age reported Thursday . -The sub-Billy Liar / Elliot one , where the gruff father is disgusted by his son 's airy-fairy wholemeal ambitions , is particularly good . -Meanwhile , many Algerians say they have not felt the effects of the country 's new wealth . -A secret review of Fletcher 's death concluded two years ago that there was enough evidence to charge two Libyans , The Daily Telegraph reported earlier this month . -Earlier , state television said the blast happened in central Iran , without giving details . -Coach K and North Carolina 's Roy Williams are busy arguing over injury reports , so we know they didn 't hear Capel on Tobacco Road . -Lance , Inc. has scheduled a conference call and presentation with investors at 9 : 00 am eastern time on Friday , July 24 , 2009 to discuss financial results . To participate in the conference call , the dial-in number is ( 800 ) 789-3681 for U.S. callers or ( 702 ) 696-4943 for international callers . A continuous telephone replay of the call will be available beginning at 12 : 00 pm on July 24th and running through July 31st at midnight . The replay telephone number is ( 800 ) 642-1687 for U.S. callers or ( 706 ) 645-9291 for international callers . The replay access code is 17972828 . Investors may also access a web-based replay of the conference call at Lance 's website http : / / ir.lance.com. -We all have an interest in the success of these institutions . -Translated , that means debt for each household grew by $ 582 in the last six months , compared with $ 418 in the previous six . -It turns out that woman have sex for all of these reasons and more , and that their choices are not arbitrary ; there may be evolutionary explanations at work . -For months , the panel had been investigating the Democratic senator 's appointment and seating . -Because most blacks today enjoy improved education and quality of life , " dealing with the African-American community is the same as dealing with any other community , " said Dick Harpootlian , a white Obama supporter and former chairman of the South Carolina Democratic Party . -Ditto her bum-grazing cut-offs . diff --git a/lingvo/tasks/lm/testdata/small_word_vocab.txt b/lingvo/tasks/lm/testdata/small_word_vocab.txt deleted file mode 100644 index bfef33f6..00000000 --- a/lingvo/tasks/lm/testdata/small_word_vocab.txt +++ /dev/null @@ -1,64 +0,0 @@ - 0 - 1 - 2 - 3 -'s 4 -'t 5 -, 6 -. 7 -about 8 -am 9 -an 10 -and 11 -are 12 -as 13 -at 14 -be 15 -been 16 -but 17 -by 18 -did 19 -do 20 -for 21 -had 22 -has 23 -have 24 -he 25 -her 26 -him 27 -how 28 -if 29 -in 30 -is 31 -it 32 -less 33 -me 34 -more 35 -my 36 -no 37 -not 38 -of 39 -on 40 -one 41 -or 42 -our 43 -out 44 -she 45 -so 46 -that 47 -the 48 -then 49 -they 50 -this 51 -to 52 -too 53 -was 54 -we 55 -were 56 -what 57 -when 58 -who 59 -why 60 -will 61 -with 62 -you 63 diff --git a/lingvo/tasks/lm/tools/BUILD b/lingvo/tasks/lm/tools/BUILD deleted file mode 100644 index c6396780..00000000 --- a/lingvo/tasks/lm/tools/BUILD +++ /dev/null @@ -1,15 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_binary( - name = "download_lm1b", - srcs = ["download_lm1b.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - ], -) diff --git a/lingvo/tasks/lm/tools/__init__.py b/lingvo/tasks/lm/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/lm/tools/download_lm1b.py b/lingvo/tasks/lm/tools/download_lm1b.py deleted file mode 100644 index 47f2c812..00000000 --- a/lingvo/tasks/lm/tools/download_lm1b.py +++ /dev/null @@ -1,60 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Downloads and processes lm1b dataset (http://www.statmt.org/lm-benchmark).""" - -import glob -import os -import lingvo.compat as tf - -tf.flags.DEFINE_string("outdir", "/tmp/lm1b", "The output directory.") -tf.flags.DEFINE_integer( - "count_cutoff", 3, "Ignore tokens that appear fewer than " - "this amount of times when creating the vocab file.") - -FLAGS = tf.flags.FLAGS - - -def main(_): - basename = "1-billion-word-language-modeling-benchmark-r13output" - fname = basename + ".tar.gz" - url = "http://www.statmt.org/lm-benchmark/" + fname - sha256hash = "01ba60381110baf7f189dfd2b8374de371e8c9a340835793f190bdae9e90a34e" - - tf.keras.utils.get_file( - fname, url, file_hash=sha256hash, cache_subdir=FLAGS.outdir, extract=True) - - tf.logging.info("Generating vocab file. This may take a few minutes.") - vocab = {} - for fname in glob.glob( - os.path.join(FLAGS.outdir, basename, - "training-monolingual.tokenized.shuffled", "news.en*")): - with open(fname) as f: - for line in f: - for w in line.split(): - vocab[w] = vocab.get(w, 0) + 1 - - with open(os.path.join(FLAGS.outdir, basename, "vocab.txt"), "w") as f: - f.write("\t0\n\t1\n\t2\n\t3\n") - id = 4 - for k, v in sorted(list(vocab.items()), key=lambda kv: (-kv[1], kv[0])): - if v < FLAGS.count_cutoff: - break - f.write("%s\t%d\n" % (k, id)) - id += 1 - - -if __name__ == "__main__": - tf.app.run(main) diff --git a/lingvo/tasks/mt/BUILD b/lingvo/tasks/mt/BUILD deleted file mode 100644 index 46ebd35c..00000000 --- a/lingvo/tasks/mt/BUILD +++ /dev/null @@ -1,284 +0,0 @@ -load( - "//lingvo:lingvo.bzl", - "lingvo_proto_cc", - "lingvo_proto_py", -) - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "layers", - srcs = ["layers.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:layers", - "//lingvo/core:layers_with_attention", - ], -) - -py_test( - name = "layers_test", - srcs = ["layers_test.py"], - python_version = "PY3", - shard_count = 2, - srcs_version = "PY3", - deps = [":layers_test_lib"], -) - -py_library( - name = "layers_test_lib", - testonly = 1, - srcs = ["layers_test.py"], - srcs_version = "PY3", - deps = [ - ":layers", - "//lingvo:compat", - "//lingvo/core:layers_with_attention", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "encoder", - srcs = ["encoder.py"], - srcs_version = "PY3", - deps = [ - ":layers", - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:batch_major_attention", - "//lingvo/core:layers", - "//lingvo/core:model_helper", - "//lingvo/core:py_utils", - "//lingvo/core:rnn_cell", - "//lingvo/core:summary_utils", - ], -) - -py_test( - name = "encoder_test", - srcs = ["encoder_test.py"], - python_version = "PY3", - shard_count = 12, - srcs_version = "PY3", - deps = [ - ":encoder", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:self_attention_layer", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "decoder", - srcs = ["decoder.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:attention", - "//lingvo/core:base_decoder", - "//lingvo/core:batch_major_attention", - "//lingvo/core:layers", - "//lingvo/core:layers_with_attention", - "//lingvo/core:model_helper", - "//lingvo/core:plot", - "//lingvo/core:py_utils", - "//lingvo/core:quant_utils", - "//lingvo/core:rnn_cell", - "//lingvo/core:rnn_layers", - "//lingvo/core:summary_utils", - ], -) - -py_test( - name = "decoder_test", - srcs = ["decoder_test.py"], - python_version = "PY3", - shard_count = 20, - srcs_version = "PY3", - deps = [":decoder_test_lib"], -) - -py_library( - name = "decoder_test_lib", - testonly = 1, - srcs = ["decoder_test.py"], - srcs_version = "PY3", - deps = [ - ":decoder", - # Implicit absl.testing.parameterized dependency. - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:input_generator_helper", - "//lingvo/core:layers", - "//lingvo/core:layers_with_attention", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - "//lingvo/core/ops:hyps_py_pb2", - # Implicit numpy dependency. - ], -) - -py_library( - name = "data_augmenter", - srcs = ["data_augmenter.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:py_utils", - "//lingvo/core/ops", - # Implicit numpy dependency. - ], -) - -py_test( - name = "data_augmenter_test", - srcs = ["data_augmenter_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":data_augmenter", - "//lingvo:compat", - "//lingvo/core:base_layer", - "//lingvo/core:py_utils", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -py_library( - name = "input_generator", - srcs = ["input_generator.py"], - srcs_version = "PY3", - deps = [ - ":text_input_py_pb2", - "//lingvo:compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:generic_input", - "//lingvo/core:py_utils", - "//lingvo/core:summary_utils", - "//lingvo/core:tokenizers", - "//lingvo/core/ops", - ], -) - -py_test( - name = "input_generator_test", - srcs = ["input_generator_test.py"], - data = [ - ":wpm_ende", - "//lingvo/tasks/mt/testdata:input_test_data", - "//lingvo/tasks/mt/testdata:mlperf_tfexample", - "//lingvo/tasks/mt/testdata:wmt14_ende_tfexample", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":data_augmenter", - ":input_generator", - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - "//lingvo/core:tokenizers", - # Implicit numpy dependency. - ], -) - -py_library( - name = "model", - srcs = ["model.py"], - srcs_version = "PY3", - deps = [ - ":decoder", - ":encoder", - "//lingvo:compat", - "//lingvo/core:base_model", - "//lingvo/core:insertion", - "//lingvo/core:metrics", - "//lingvo/core:py_utils", - ], -) - -py_test( - name = "model_test", - size = "large", - srcs = ["model_test.py"], - python_version = "PY3", - shard_count = 20, - srcs_version = "PY3", - deps = [":model_test_lib"], -) - -py_library( - name = "model_test_lib", - testonly = 1, - srcs = ["model_test.py"], - data = [ - "//lingvo/tasks/mt/testdata:wmt14_ende_tfexample", - ], - srcs_version = "PY3", - deps = [ - ":decoder", - ":encoder", - ":input_generator", - ":model", - "//lingvo:compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:base_layer", - "//lingvo/core:cluster_factory", - "//lingvo/core:optimizer", - "//lingvo/core:py_utils", - "//lingvo/core:schedule", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) - -filegroup( - name = "wpm_ende", - srcs = glob(include = [ - "wpm-ende*.voc", - ]), -) - -py_library( - name = "base_config", - srcs = ["base_config.py"], - srcs_version = "PY3", - deps = [ - ":decoder", - ":encoder", - ":input_generator", - "//lingvo/core:attention", - "//lingvo/core:layers", - "//lingvo/core:optimizer", - "//lingvo/core:py_utils", - "//lingvo/core:rnn_cell", - "//lingvo/core:rnn_layers", - "//lingvo/core:schedule", - ], -) - -lingvo_proto_cc( - name = "text_input_proto", - src = "text_input.proto", -) - -lingvo_proto_py( - name = "text_input_py_pb2", - src = "text_input.proto", - deps = [":text_input_proto"], -) diff --git a/lingvo/tasks/mt/README.md b/lingvo/tasks/mt/README.md deleted file mode 100644 index 0619d678..00000000 --- a/lingvo/tasks/mt/README.md +++ /dev/null @@ -1,322 +0,0 @@ -# Local machine translation quick start - -To build a local translation model, we provide the tools to run a WMT16 -Multimodal EN-DE experiment. This dataset consists of translated image captions -(we are ignoring the associated images). The vocabulary is limited and sentences -are short, meaning you can build a working system quickly, using only CPUs. - -## Download and prepare the data - -We provide scripts to download the data. These scripts require the repository to -be git cloned and assumes the lingvo pip package is installed. - -```shell -cd lingvo/tasks/mt/tools -./wmtm16_get_data.sh -``` - -The master script above calls scripts for individual steps. If any step fails, -it can be re-run by hand. Similar steps are described in individual detail under -the Full Machine translation task below. The entire process should complete in -fewer than 10 minutes. - -In total, `250MB` of free space are required. The location is configured in -`lingvo/tasks/mt/tools/wmtm16_lib.sh`, defaulting to -`/tmp/wmtm16`. The final output is in -`/tmp/wmtm16/wpm`, stored in `tfrecord` format, which is described -in detail under the Full Machine translation task. - -To install aria2c, run: - -```shell -sudo apt-get install aria2 -``` - -## Training a toy system - -MT training is most naturally run on a cluster with many parallel processes, but -for this small task, we can run locally, taking advantage of multiple CPUs on a -reasonable development machine. - -```shell -mkdir /tmp/wmtm16/log - -python3 -m lingvo.trainer \ - --run_locally=cpu --mode=sync --saver_max_to_keep=3 \ - --logdir=/tmp/wmtm16/log \ - --job=controller,trainer_client \ - --model=mt.wmtm16_en_de.WmtCaptionEnDeTransformer \ - --logtostderr >& /tmp/wmtm16/log/train.log -``` - -This will begin training a model, providing statistics on training perplexity -(log_pplx) as training runs. Typically, you will also want to monitor your -progress on a held-out set. Background the above process or open a new terminal -to start two other processes to monitor our progress, one to track the -perplexity assigned to reference translations: - -```shell -python3 -m lingvo.trainer \ - --run_locally=cpu --mode=sync --saver_max_to_keep=3 \ - --logdir=/tmp/wmtm16/log \ - --job=evaler_Dev \ - --model=mt.wmtm16_en_de.WmtCaptionEnDeTransformer \ - --logtostderr >& /tmp/wmtm16/log/eval_dev.log -``` - -And one to report the accuracy of the decoder's output when compared to those -reference translations: - -```shell -python3 -m lingvo.trainer \ - --run_locally=cpu --mode=sync --saver_max_to_keep=3 \ - --logdir=/tmp/wmtm16/log \ - --job=decoder_Dev \ - --model=mt.wmtm16_en_de.WmtCaptionEnDeTransformer \ - --logtostderr >& /tmp/wmtm16/log/decode_dev.log -``` - -Note that we give the same log directory for both of these processes. The -monitors use the trainer's checkpoints, saved in the log directory, to load -models and compute scores. - -Example translation outputs appear in -`/tmp/wmtm16/log/decode_dev.log`. To track development BLEU and -perplexity, run tensorboard on `/tmp/wmtm16/log`. Training should -achieve well over 30 BLEU in fewer than 10,000 steps. - -# Full Machine translation task - -We provide the tools to run the WMT14 EN-DE setup. The system is described in -[The Best of Both Worlds: Combining Recent Advances in Neural Machine -Translation](http://aclweb.org/anthology/P18-1008) - -## Downloading the data - -We provide scripts to download the data. These scripts require the repository to -be git cloned and assumes the lingvo pip package is installed. - -```shell -cd lingvo/tasks/mt/tools -./wmt14_get_data.sh -``` - -The master script above calls scripts for individual steps. If any step fails, -it can be re-run by hand. - -In total, `13GB` of free space are required. The location is configured in -`lingvo/tasks/mt/tools/wmt14_lib.sh`, defaulting to -`/tmp/wmt14`. - -The final output is in `/tmp/wmt14/wpm`. It comes in the form of -`tf.Example` protos in `tfrecord` format. The examples contain the following -features: - -* `source_id`: An `int64` tensor of length `S`, with wordpiece IDs, always - terminated with `2`, the ID for `
    `. -* `source_padding`: A `float` tensor of zeros, of length `S`. -* `source_word`: A `string` tensor of wordpieces, always ending in `
    `, but - not starting with ``. -* `target_id`: An `int64` tensor of length `T` with wordpiece IDs, always - starting with `1`, the ID for ``, but not terminated with ``. -* `target_padding`: A `float` tensor of zeros, of length `T`. -* `target_word`: A `string` tensor of wordpiece, always starting with `` - but not ending in ``. -* `target_label`: An `int64` tensor of length `T`, with wordpiece IDs, like - `target_id`, but, instead of starting with ``, is terminated with ``. -* `target_weight`: A `float` tensor of ones, of length `T`. -* `natural_order`: An `int64` which is always `1`. - -Above, `S` is the length of the source (EN), in word pieces, including the -trailing ``. Similarly, `T` is the length of the target (DE), in word -pieces, including either the beginning ``, or the trailing ``. - -Both source and target share the same wordpiece inventory. - -### Downloading the Moses scripts - -The first step, `wmt14.01.download_moses_scripts.sh`, obtains a copy of the -moses decoder from github. In particular, the tokenizer, cleaning, and SGM -conversion scripts will be used. - -### Downloading the training data - -On a typical 100Mbit connection, the second step, `wmt14.02.download_train.sh`, -will take roughly half an hour to complete. It will download the following into -the `${ROOT}/raw` directory: - -* `training-parallel-europarl-v7.tgz`: `628M` -* `training-parallel-commoncrawl.tgz`: `876M` -* `training-parallel-nc-v9.tgz`: `77M` - -### Downloading the dev set - -The step `wmt14.03.download_devtest.sh` will result in `${ROOT}/raw`: - -* `dev.tgz`: `17M` -* `test-filtered.tgz`: `3.2M` - -### Unpacking the data - -The steps `wmt14.04.unpack_train.sh` and `wmt14.05.unpack_devtest.sh` unpack the -train data into `${ROOT}/unpacked`. Note that we will add SGM-converted test -files into the `${ROOT}/unpacked` directory later. - -### Tokenizing the data - -The steps `wmt14.06.tokenize_train.sh` and `wmt14.07.tokenize_devtest.sh` -tokenize the training data, into `${ROOT}/tokenized/{train,dev,test}`. - -For test, an additional step of converting the SGM is done prior to -tokenization. For train data, a cleaning step is done after tokenization. The -following files are going to be used as input to the next stage: - -* dev/newstest2013.de: `391K`, 3000 lines -* dev/newstest2013.en: `342K`, 3000 lines -* test/newstest2014.de: `369K`, 2737 lines -* test/newstest2014.en: `337K`, 2737 lines -* train/commoncrawl.clean.de: `329M`, `2.4M` lines -* train/commoncrawl.clean.en: `305M`, `2.4M` lines -* train/europarl-v7.clean.de: `315M`, `1.9M` lines -* train/europarl-v7.clean.en: `276M`, `1.9M` lines -* train/news-commentary-v9.clean.de: `33M`, `2.4M` lines -* train/news-commentary-v9.clean.en: `28M`, `2.4M` lines - -### Wordpiece model encoding - -The next steps, `wmt14.08.wpm_encode_train.sh` and -`wmt14.09.wpm_encode_devtest.sh`, encode the traiing data with the wordpiece -model, as well as transcoding to the final format, to wit `tf.Example` in -`tfrecord` format. - -Conversion of the training data will take about one hour on a relatively recent -machine. - -## Setting up your cluster - -**NOTE: This section is outdated and may not work as documented.** - -We provide a script running a docker fleet as an debugging example -(`lingvo/docker/run_distributed.py`). Please take a look at the -script to see the cluster configuration. - -### Shared filesystem - -You will need a cluster with a distributed filesystem, such as `HDFS`, `NFS`, -`sshfs`, or Google [filestore](https://cloud.google.com/filestore/). The -filesystem will be used to store the checkpoints and the trainer binary. - -The shared filesystem must mount the data and the log directory, possibly in two -different directories. - -### Cluster configuration - -For each cluster node, you will need to designate a role and a port number. It -is assumed that nodes in the cluster may occasionally crash or be replaced at -any time. However, when they come back, they must remain under the same -hostname, answering to the same port. - -We will train in synchronous mode. For training, you will need the following -nodes: - -* `trainer_client`: (singleton) controls the distributed workers. -* `controller`: (singleton) saves the checkpoints. -* `worker`: (8 replicas) runs the actual training. - -To continuous evaluation, you will need to add: - -* `decoder_`: runs the beam search on the dev or eval set. -* `evaler_`: runs the BLEU scoring. - -#### Cluster spec - -For training, processes need to be aware of each other. This is done by -specifying a cluster spec string. Let us assume the following cluster spec: - -```python -{ - "worker": [ - "wk0.example.com:43222", - "wk1.example.com:43222", - "wk2.example.com:43222", - ], - "controller": ["cont.example.com:43214",], - "trainer_client": ["tc.example.com:24601"], -} -``` - -The `trainer_client` is running on a machine under hostname `tc.example.com`, -with an available port `24601`, and each worker is on a machine with hostname -`wk0.example.com`, `wk1.example.com`, and `wk2.example.com` respectively. There -is no naming convention required for the hostnames. The cluster spec is -specified as follows: - -``` -cluster_spec := ( '@', )* -role_spec := (, '=', , (',' )* ) -machine_spec := (, ':', ) -``` - -In the example above, the role spec for the `worker` role would be: - -``` -worker=wk0.example.com:43222,wk1.example.com:43222,wk2.example.com:43222 -``` - -and the final cluster spec would be: - -``` -worker=wk0.example.com:43222,wk1.example.com:43222,wk2.example.com:43222@\ -controler=cont.example.com:43214@\ -trainer_client=tc.example.com:24601 -``` - -For decoding, each process has its own cluster spec, for instance, -`decoder_test=dec.example.com:3984`. - -### Uploading the binary - -**NOTE: This section is outdated and needs to be updated to use the pip package -instead.** - -There is a single binary which can be obtained as follows: - -```shell -bazel build -c opt lingvo:trainer.par -``` - -You may then copy the binary to the shared filesystem. Henceforth, the trainer -will be invoked using `trainer.par --...` from the unpacked directory. The -`trainer.par` binary is a self-contained executable in a zip format. - -All cluster nodes must have tensorflow installed, with the same version as the -one that was used to build lingvo. - -### Uploading the data - -You will need to upload the data files to the shared filesystem. The location -must match the one specified in -`lingvo/tasks/mt/params/wmt14_en_de.py` as `DATADIR`. - -## Running the model - -You are now ready to run the model. - -For each of the roles (`trainer_client`, `controller`, `decoder_dev`, etc), you -must run the trainer with the proper cluster spec: - -```shell -trainer.par --cluster_spec= \ - --model=mt.wmt14_en_de.WmtEnDeTransformerSmall \ - --job= --task= --mode=sync --logtostderr \ - --logdir= -``` - -as described above. - -## Examining results - -To examine the results, it is best to run a tensorboard process on the log -directory. - - diff --git a/lingvo/tasks/mt/__init__.py b/lingvo/tasks/mt/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/mt/base_config.py b/lingvo/tasks/mt/base_config.py deleted file mode 100644 index 4cbcdeaf..00000000 --- a/lingvo/tasks/mt/base_config.py +++ /dev/null @@ -1,509 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Several functions to initialize typical values of dataset parameters.""" - -import math - -from lingvo.core import attention -from lingvo.core import layers -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.core import rnn_cell -from lingvo.core import rnn_layers -from lingvo.core import schedule -from lingvo.tasks.mt import decoder -from lingvo.tasks.mt import encoder -from lingvo.tasks.mt import input_generator - - -def InitTrainDatasetParams(vocab_size=None, params=None): - """Initializes typical values for train datasets. - - Args: - vocab_size: the number of tokens in your vocabulary. The default is None - because this parameter is often not used. - params: initial Params value, e.g. `NmtInput.Params()`. - - Returns: - a `Params` object. - """ - if params is None: - params = input_generator.NmtInput.Params() - params.is_nmt_example = True - - params.file_random_seed = 0 - - # How many threads to run in parallel. - params.file_parallelism = 16 - - # Note, for training, we prefer to use big file_buffer_size (as long as all - # fits in RAM), to more thoroughly randomize the training examples. when the - # file_buffer_size too small, we run the risk of sequentially going over the - # example as they are stored in the sstable which may not be random (e.g. - # maybe alphabetically ordered). - params.file_buffer_size = 10000000 - - if vocab_size is not None: - params.tokenizer.vocab_size = vocab_size - - # The bucket upper bound is determined based on an exponentially growing - # scheme, with _GenerateBuckets(10, 100) resulting buckets starting from - # minimum bucket size of 10 to maximum bucket size of 137. - # For word and sub-word level NMT, we train on sequences up to maximum - # bucket size and discard the examples that are longer than 137. - params.bucket_upper_bound = [10, 14, 19, 26, 36, 50, 70, 98, 137] - - # The bucket batch limit determines how many examples are there in each - # batch during training. We reduce the batch size for the buckets that - # have higher upper bound (batches that consist of longer sequences eg. - # 98, 137) in order to prevent out of memory issues. - # Note that this hyperparameter varies widely based on the model and language. - # larger models may warrant smaller batches in order to fit in memory, for - # example; and ideographical languages like Chinese may benefit from more - # buckets. - params.bucket_batch_limit = [128] * 8 + [64] - return params - - -def InitTestDatasetParams(vocab_size=None, params=None): - """Initializes typical values for test and dev datasets. - - Args: - vocab_size: the number of tokens in your vocabulary. - params: initial Params value, e.g. `NmtInput.Params()`. - - Returns: - a `Params` object. - """ - - if params is None: - params = input_generator.NmtInput.Params() - - params.file_random_seed = 27182818 - - # How many threads to run in parallel. - params.file_parallelism = 1 - - # In order to make exactly one pass over the dev/test sets, we set buffer - # size to 1. Greater numbers may cause inaccurate dev/test scores. - params.file_buffer_size = 1 - - if vocab_size is not None: - params.tokenizer.vocab_size = vocab_size - - # The largest bucket upper bound must be larger than the longest sequence - # length in dev/test set. Since we discard sequences longer than the - # max(bucket_upper_bound) we may end up having scores based on only shorter - # sequences only if we mistakenly set this to be too small. - params.bucket_upper_bound = [10, 14, 19, 26, 36, 50, 70, 98, 137, 200] - params.bucket_batch_limit = [128] * 8 + [64] + [32] - return params - - -def InitTransformerTestBuckets(params): - params.bucket_upper_bound = [10, 14, 19, 26, 36, 50, 70, 98, 137, 200] - params.bucket_batch_limit = [16] * 10 - return params - - -def InitTransformerTrainBuckets(params): - params.bucket_upper_bound = [8, 12, 16, 24, 32, 48, 64, 96] - params.bucket_batch_limit = [512, 341, 256, 170, 128, 85, 64, 42] - return params - - -def SetupTransformerParams(p, - name, - vocab_size, - model_dim, - hidden_dim, - num_heads, - num_layers, - learning_rate, - warmup_steps, - residual_dropout_prob=0.1, - input_dropout_prob=0.0, - atten_dropout_prob=0.0, - relu_dropout_prob=0.0, - label_smoothing_uncertainty=0.1, - is_transparent=False, - activation='RELU', - add_unnormalized_residuals=False, - atten_hidden_dim=0, - num_encoder_layers=None, - num_decoder_layers=None): - """Common model setup for different transformer models. - - Args: - p: The initial params object to modify. - name: An identifier for an instance of a transformer model. - vocab_size: an integer representing the size of the vocabulary, probably - 16000 or 32000. - model_dim: dimension of the transformer block (column) - hidden_dim: dimension of Feed-Forward neural network in each layer - num_heads: number of attention heads to use for the transformer - num_layers: number of layers in the transformer - learning_rate: learning rate for Adam. For the base model, we use 1.0; for - the big model, 3.0 - warmup_steps: warmup steps for `TransformerSchedule`. For the - base model, we use 4000; for the big model, 40000 - residual_dropout_prob: dropout prob to the output of each sub-layer before - it is added to the sub-layer input - input_dropout_prob: dropout prob to the sums of the token embeddings and the - position embeddings - atten_dropout_prob: dropout prob to the attention weights in each - Transformer attention sub-layer - relu_dropout_prob: dropout prob to the inner layer output (ReLU activation) - in each Transformer feed-forward sub-layer - label_smoothing_uncertainty: if this value is 0, no label smoothing will be - applied - is_transparent: If set, decoder layers attend to weighted combinations of - encoder layers. - activation: Non-linearity for feed-forward layers. - add_unnormalized_residuals: If set, uses un-normalized residuals in - TransformerAttentionLayer - atten_hidden_dim: Explicitly set attention hidden dim. - num_encoder_layers: to set a different number of layers for the encoder. - num_decoder_layers: to set a different number of layers for the decoder. - - Returns: - A Params object containing the parameters that specify a transformer model - (Vaswani 2017) - - """ - p.name = name - - # Transformer encoder and decoder setup - num_encoder_layers = num_encoder_layers or num_layers - num_decoder_layers = num_decoder_layers or num_layers - p.encoder = SetupTransformerEncoder( - model_dim, vocab_size, num_encoder_layers, num_heads, hidden_dim, - residual_dropout_prob, input_dropout_prob, atten_dropout_prob, - relu_dropout_prob, is_transparent, activation, add_unnormalized_residuals, - atten_hidden_dim) - p.decoder = SetupTransformerDecoder( - model_dim, vocab_size, num_decoder_layers, num_heads, hidden_dim, - residual_dropout_prob, input_dropout_prob, atten_dropout_prob, - relu_dropout_prob, label_smoothing_uncertainty, is_transparent, - activation, add_unnormalized_residuals, atten_hidden_dim) - - p.train.Set( - learning_rate=learning_rate, - optimizer=optimizer.Adam.ParamsB(), - clip_gradient_norm_to_value=0.0, - grad_norm_to_clip_to_zero=0.0, - lr_schedule=schedule.TransformerSchedule.Params().Set( - warmup_steps=warmup_steps, worker_replicas=1, model_dim=model_dim)) - - p.eval.samples_per_summary = 12000 - return p - - -def SetupTransformerDecoder(model_dim, - vocab_size, - num_layers, - num_heads, - hidden_dim, - residual_dropout_prob=0.1, - input_dropout_prob=0.0, - atten_dropout_prob=0.0, - relu_dropout_prob=0.0, - label_smoothing_uncertainty=0.1, - is_transparent=False, - activation='RELU', - add_unnormalized_residuals=False, - atten_hidden_dim=0): - """Common setup for transformer model decoder.""" - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - default_params_init = py_utils.WeightInit.Xavier(1.0) - emb_params_init = py_utils.WeightInit.Gaussian(1.0 / math.sqrt(model_dim)) - - # Decoder - decoder_params = decoder.TransformerDecoder.Params() - decoder_params.source_dim = model_dim - decoder_params.model_dim = model_dim - decoder_params.num_trans_layers = num_layers - decoder_params.input_dropout_prob = input_dropout_prob - - decoder_params.token_emb.Set( - vocab_size=vocab_size, - embedding_dim=model_dim, - max_num_shards=16, - params_init=emb_params_init, - vn=disable_vn, - scale_sqrt_depth=True) - - decoder_params.position_emb.Set( - embedding_dim=model_dim, trainable_scaling=False, vn=disable_vn) - - decoder_params.trans_tpl.source_dim = model_dim - decoder_params.trans_tpl.tr_atten_tpl.Set( - source_dim=model_dim, - num_attention_heads=num_heads, - residual_dropout_prob=residual_dropout_prob, - atten_dropout_prob=atten_dropout_prob, - params_init=default_params_init, - add_unnormalized_input=add_unnormalized_residuals, - atten_hidden_dim=atten_hidden_dim, - vn=disable_vn) - - decoder_params.trans_tpl.tr_atten_tpl.atten_tpl.Set( - enable_ctx_pre_proj=True, - enable_ctx_post_proj=True, - context_dim=model_dim, - vn=disable_vn) - - decoder_params.trans_tpl.tr_fflayer_tpl.Set( - input_dim=model_dim, - hidden_dim=hidden_dim, - residual_dropout_prob=residual_dropout_prob, - relu_dropout_prob=relu_dropout_prob, - params_init=default_params_init, - vn=disable_vn, - activation=activation) - - decoder_params.softmax.Set( - num_classes=vocab_size, - vn=disable_vn, - params_init=emb_params_init, - num_shards=16) - - decoder_params.per_word_avg_loss = True - decoder_params.label_smoothing = layers.UniformLabelSmoother.Params() - decoder_params.label_smoothing.num_classes = vocab_size - decoder_params.label_smoothing.uncertainty = label_smoothing_uncertainty - - if is_transparent: - decoder_params.is_transparent = True - - return decoder_params - - -def SetupTransformerEncoder(model_dim, - vocab_size, - num_layers, - num_heads, - hidden_dim, - residual_dropout_prob=0.1, - input_dropout_prob=0.0, - atten_dropout_prob=0.0, - relu_dropout_prob=0.0, - is_transparent=False, - activation='RELU', - add_unnormalized_residuals=False, - atten_hidden_dim=0): - """Common setup for transformer model encoder. - - Args: - model_dim: specifies dimension of transformer layers, token embeddings, - and positional embeddings as well context vectors (attention values). - vocab_size: for token embeddings. - num_layers: number of transformer layers. - num_heads: number of attention heads. - hidden_dim: in transformer feedforward layer. - residual_dropout_prob: used in transformer feedforward and attention layer. - input_dropout_prob: input dropout. - atten_dropout_prob: used in attention layer. - relu_dropout_prob: used in transformer feedforward layer. - is_transparent: if set, outputs a merger of embeddings and layer outputs. - activation: Non-linearity for feed-forward layers. - add_unnormalized_residuals: If set, uses un-normalized residuals in - TransformerAttentionLayer - atten_hidden_dim: Explicitly set attention hidden dim. - - Returns: - Encoder params. - """ - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - default_params_init = py_utils.WeightInit.Xavier(1.0) - emb_params_init = py_utils.WeightInit.Gaussian(1.0 / math.sqrt(model_dim)) - - # Encoder - encoder_params = encoder.TransformerEncoder.Params() - - encoder_params.token_emb.Set( - embedding_dim=model_dim, - max_num_shards=16, - params_init=emb_params_init, - vocab_size=vocab_size, - vn=disable_vn, - scale_sqrt_depth=True) - - encoder_params.position_emb.Set( - embedding_dim=model_dim, trainable_scaling=False, vn=disable_vn) - - # Encoder TransformerStack params - encoder_params.model_dim = model_dim - encoder_params.transformer_stack.model_dim = model_dim - encoder_params.transformer_stack.num_transformer_layers = num_layers - encoder_params.input_dropout_prob = input_dropout_prob - - encoder_params.transformer_stack.transformer_tpl.tr_atten_tpl.Set( - num_attention_heads=num_heads, - residual_dropout_prob=residual_dropout_prob, - atten_dropout_prob=atten_dropout_prob, - params_init=default_params_init, - add_unnormalized_input=add_unnormalized_residuals, - atten_hidden_dim=atten_hidden_dim, - vn=disable_vn) - - encoder_params.transformer_stack.transformer_tpl.tr_atten_tpl.atten_tpl.Set( - num_attention_heads=num_heads, - enable_ctx_pre_proj=True, - enable_ctx_post_proj=True, - context_dim=model_dim, - vn=disable_vn) - - encoder_params.transformer_stack.transformer_tpl.tr_fflayer_tpl.Set( - hidden_dim=hidden_dim, - residual_dropout_prob=residual_dropout_prob, - relu_dropout_prob=relu_dropout_prob, - params_init=default_params_init, - vn=disable_vn, - activation=activation) - - if is_transparent: - encoder_params.transformer_stack.is_transparent = True - - return encoder_params - - -def SetupRNMTParams(p, - name, - vocab_size, - embedding_dim, - hidden_dim, - num_heads, - num_encoder_layers, - num_decoder_layers, - learning_rate, - lr_warmup_steps, - lr_decay_start, - lr_decay_end, - lr_min, - atten_dropout_prob, - residual_dropout_prob, - ls_uncertainty, - l2_regularizer_weight, - is_transparent=False, - num_hyps_per_beam=16, - adam_beta1=0.9, - adam_beta2=0.999, - adam_epsilon=8e-07): - """Creates RNMT+ params common to all datasets. - - Args: - p: The initial params object to modify. - name: A descriptive name for your model. - vocab_size: size of the vocabulary. Probably 32000 or 16000. - embedding_dim: Dimension of token embeddings. - hidden_dim: LSTM cell size. - num_heads: number of attention heads. - num_encoder_layers: Number of layers in the encoder. - num_decoder_layers: Number of layers in the decoder. - learning_rate: Optimizer learning rate. - lr_warmup_steps: Warm-up steps for the optimizer. - lr_decay_start: Learning rate exponential decay starting step. - lr_decay_end: Learning rate exponential decay end step. - lr_min: Minimum learning rate (ratio with initial learning rate). - atten_dropout_prob: Dropout for the attention. - residual_dropout_prob: Dropout for residual layers. - ls_uncertainty: Label smoothing uncertainty. - l2_regularizer_weight: Weight for l2 regularization on parameters. - is_transparent: If set, decoder attends to weighted combination of encoder - layers. - num_hyps_per_beam: Number of hyps to keep per source sequence. - adam_beta1: Beta-1 parameter of Adam optimizer. - adam_beta2: Beta-2 parameter of Adam optimizer. - adam_epsilon: Epsilon parameter of Adam optimizer. - - Returns: - a Params() object specifying the RNMT+ Parameters. - """ - - # TODO(orhanf): add transparent connections. - del is_transparent - - p.name = name - - default_params_init = py_utils.WeightInit.Uniform(0.04) - rnn_cell_tpl = rnn_cell.LayerNormalizedLSTMCellSimple.Params().Set( - num_output_nodes=hidden_dim, - output_nonlinearity=False, - params_init=default_params_init) - - # RNMT+ encoder setup. - p.encoder = encoder.MTEncoderBiRNN.Params().Set( - num_lstm_layers=num_encoder_layers, - lstm_cell_size=hidden_dim, - encoder_out_dim=hidden_dim, - lstm_tpl=rnn_cell_tpl.Copy(), - dropout_prob=residual_dropout_prob) - p.encoder.emb.embedding_dim = embedding_dim - p.encoder.emb.vocab_size = vocab_size - - # RNMT+ decoder setup. - p.decoder = decoder.MTDecoderV1.Params().Set( - rnn_layers=num_decoder_layers, - rnn_cell_tpl=rnn_cell_tpl.Copy(), - atten_rnn_cell_tpl=rnn_cell_tpl.Copy(), - dropout_prob=residual_dropout_prob, - attention=attention.MultiHeadedAttention.Params().Set( - source_dim=hidden_dim, - hidden_dim=hidden_dim, - query_dim=hidden_dim, - context_dim=hidden_dim, - num_attention_heads=num_heads, - inner_atten_params=attention.AdditiveAttention.Params(), - use_source_vec_as_attention_value=True, - enable_ctx_pre_proj=False, - enable_query_proj=True, - atten_dropout_prob=atten_dropout_prob, - atten_dropout_deterministic=True), - atten_rnn_cls=rnn_layers.FRNNWithAttention, - feed_attention_context_vec_to_softmax=True, - label_smoothing=layers.UniformLabelSmoother.Params().Set( - num_classes=vocab_size, uncertainty=ls_uncertainty)) - p.decoder.emb.vocab_size = vocab_size - p.decoder.emb.embedding_dim = embedding_dim - p.decoder.softmax.num_classes = vocab_size - p.decoder.source_dim = hidden_dim - - # Inference related. - p.decoder.beam_search.num_hyps_per_beam = num_hyps_per_beam - - # Optimization setup. - learning_rate_schedule = ( - schedule.LinearRampupExponentialDecayScaledByNumSplitSchedule.Params() - .Set( - warmup=lr_warmup_steps, - decay_start=lr_decay_start, - decay_end=lr_decay_end, - min=lr_min)) - p.train.Set( - l2_regularizer_weight=l2_regularizer_weight, - grad_norm_tracker=layers.GradNormTracker.Params().Set( - name='gradient_norm_tracker'), - learning_rate=learning_rate, - lr_schedule=learning_rate_schedule, - grad_norm_to_clip_to_zero=100000.0, - optimizer=optimizer.Adam.Params().Set( - beta1=adam_beta1, beta2=adam_beta2, epsilon=adam_epsilon), - ) - - # Evaluation related - p.eval.samples_per_summary = 12000 - return p diff --git a/lingvo/tasks/mt/data_augmenter.py b/lingvo/tasks/mt/data_augmenter.py deleted file mode 100644 index 29a69d69..00000000 --- a/lingvo/tasks/mt/data_augmenter.py +++ /dev/null @@ -1,106 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Classes which modifies inputs for pretraining. - -Currently support: MASS -""" - -from lingvo.core import base_layer -from lingvo.core import ops -from lingvo.core import py_utils -import numpy as np - - -class MASS(base_layer.BaseLayer): - """Prepare data for MASS pretraining. - - This class is a wrapper for the MASS op. - """ - - @classmethod - def Params(cls): - # Params for the MASS op api. - p = super().Params() - p.Define('mask_id', 3, 'Id for the mask token.') - p.Define('mask_ratio', 0.5, 'Mask fraction.') - p.Define('mask_minlen', 0, 'Minimum number of tokens to mask.') - p.Define( - 'span_len', 100000, - 'Split total mask_len into segments of this size and randomly' - ' distribute them across the src sentence for segmented masking.') - p.Define( - 'random_start_prob', 0.6, - 'Probability that placement of masked segments will be entirely ' - 'random. Remaining cases are split evenly between masking at the ' - 'beginning and at the end.') - # keep_prob/rand_prob/mask_prob must sum to 1. - p.Define( - 'keep_prob', 0.1, - 'Probability that a token designated for masking will be ' - 'unchanged.') - p.Define( - 'rand_prob', 0.1, - 'Probability that a token designated for masking will be replaced ' - 'with a random token.') - p.Define( - 'mask_prob', 0.8, - 'Probability that a token designated for masking will be replaced ' - 'with mask_id.') - p.Define( - 'mask_target', True, - 'If true, the target is masked with the inverse of the source ' - 'mask.') - p.Define('vocab_size', 0, - 'Used when selecting a random token to replaced a masked token.') - p.Define( - 'first_unreserved_id', 4, - 'Tokens greater than or equal to this may be selected at random ' - 'to replace a masked token.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if not np.isclose([p.keep_prob + p.rand_prob + p.mask_prob], [1]): - raise ValueError('keep_prob, rand_prob, mask_prob must sum to 1') - if p.vocab_size == 0: - raise ValueError('vocab_size parameter must be set explicitly') - - def Mask(self, seq_ids, weights, actual_seq_len): - p = self.params - (src_ids, tgt_ids, tgt_labels, tgt_weights) = ops.mass( - seq_ids, - weights, - actual_seq_len, - mask_id=p.mask_id, - mask_ratio=p.mask_ratio, - mask_minlen=p.mask_minlen, - span_len=p.span_len, - random_start_prob=p.random_start_prob, - keep_prob=p.keep_prob, - rand_prob=p.rand_prob, - mask_prob=p.mask_prob, - mask_target=p.mask_target, - vocab_size=p.vocab_size, - first_unreserved_id=p.first_unreserved_id) - - mass_out = py_utils.NestedMap() - mass_out.src = py_utils.NestedMap() - mass_out.src.ids = src_ids - mass_out.tgt = py_utils.NestedMap() - mass_out.tgt.ids = tgt_ids - mass_out.tgt.labels = tgt_labels - mass_out.tgt.weights = tgt_weights - return mass_out diff --git a/lingvo/tasks/mt/data_augmenter_test.py b/lingvo/tasks/mt/data_augmenter_test.py deleted file mode 100644 index 8a1ce80b..00000000 --- a/lingvo/tasks/mt/data_augmenter_test.py +++ /dev/null @@ -1,65 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for data_augmenter.""" - -from lingvo import compat as tf -from lingvo.core import test_utils -from lingvo.tasks.mt import data_augmenter -import numpy as np - -FLAGS = tf.flags.FLAGS - - -class MassLayerTest(test_utils.TestCase): - - def _MassParams(self): - p = data_augmenter.MASS.Params() - p.mask_id = 3 - p.mask_ratio = 0.5 - p.mask_minlen = 0 - p.span_len = 3 - p.random_start_prob = 0.8 - p.keep_prob = 0 - p.rand_prob = 0 - p.mask_prob = 1 - p.mask_target = True - p.vocab_size = 64 - p.first_unreserved_id = 4 - p.name = "mass_layer" - return p - - def testMassLayer(self): - with self.session(use_gpu=False) as sess: - batch_size = 3 - seq_len = 10 - p = self._MassParams() - mass_layer = data_augmenter.MASS(p) - seq_ids = tf.fill([batch_size, seq_len], 4) - weights = tf.ones([batch_size, seq_len]) - actual_seq_len = tf.fill([batch_size], 10) - mass_out = mass_layer.Mask(seq_ids, weights, actual_seq_len) - (src_ids, tgt_ids, tgt_labels, tgt_weights) = sess.run([ - mass_out.src.ids, mass_out.tgt.ids, mass_out.tgt.labels, - mass_out.tgt.weights - ]) - self.assertAllEqual(np.sum(src_ids == 3, axis=1), [5, 5, 5]) - self.assertAllEqual(np.sum(tgt_ids == 3, axis=1), [5, 5, 5]) - self.assertAllEqual(tgt_labels, - 4 * np.ones([batch_size, seq_len], dtype=np.int32)) - self.assertAllEqual(np.sum(tgt_weights, axis=1), [5., 5., 5.]) - - -if __name__ == "__main__": - tf.test.main() diff --git a/lingvo/tasks/mt/decoder.py b/lingvo/tasks/mt/decoder.py deleted file mode 100644 index dbb2c1e7..00000000 --- a/lingvo/tasks/mt/decoder.py +++ /dev/null @@ -1,2708 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Machine translation decoder. -""" - -import math -import lingvo.compat as tf -from lingvo.core import attention -from lingvo.core import base_decoder -from lingvo.core import batch_major_attention -from lingvo.core import layers -from lingvo.core import layers_with_attention -from lingvo.core import model_helper -from lingvo.core import plot -from lingvo.core import py_utils -from lingvo.core import quant_utils -from lingvo.core import rnn_cell -from lingvo.core import rnn_layers -from lingvo.core import summary_utils - - -class MTBaseDecoder(base_decoder.BaseBeamSearchDecoder): - """Base class for Lingvo MT decoders.""" - - @classmethod - def Params(cls): - p = super().Params() - p.Define('label_smoothing', None, 'Label smoothing class.') - p.Define('softmax', layers.SimpleFullSoftmax.Params(), 'Softmax params.') - p.Define( - 'per_word_avg_loss', False, 'Compute loss averaged per word. If False ' - 'loss is computed averaged per sequence.') - p.Define('unidi_rnn_type', 'func', 'Options: func, native_cudnn. ' - 'func: FRNN, native_cudnn: CuDNNLSTM.') - p.Define('feed_attention_context_vec_to_softmax', False, - 'Whether to concatenate attention context vector to rnn output' - ' before softmax.') - p.Define('per_example_tensors', False, 'Return per example tensors') - - # Default config for the softmax part. - p.softmax.num_classes = 32000 # 32k - p.softmax.num_shards = 8 - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.label_smoothing is not None: - p.label_smoothing.name = 'smoother' - p.label_smoothing.num_classes = p.softmax.num_classes - self.CreateChild('smoother', p.label_smoothing) - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Sets the params with the given vocab size and wpm model. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model params updated with the vocab size and wpm model. - """ - p.softmax.num_classes = vocab_size - return p - - def _ComputeXentLoss(self, - theta, - softmax_input, - target_labels, - target_weights, - target_paddings, - target_segment_ids=None, - time_axis=0): - """Computes cross-entropy loss given the softmax input, labels and weights. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - softmax_input: A tensor of shape [time, batch, p.softmax.input_dim]. - target_labels: A matrix of tf.int32. [time, batch]. - target_weights: A matrix of params.dtype. [time, batch]. - target_paddings: A matrix of params.dtype. [time, batch]. - target_segment_ids: A matrix of params.dtype. [time, batch]. - time_axis: If 0, the inputs are time-major: [time, batch, ...]; if 1, the - inputs are batch-major: [batch, time, ...]. - - Returns: - The cross entropy loss. - """ - p = self.params - softmax_input = tf.reshape(softmax_input, [-1, p.softmax.input_dim]) - - if p.label_smoothing is None: - xent_loss = self.softmax.FProp( - theta.softmax, [softmax_input], - class_weights=tf.reshape(target_weights, [-1, 1]), - class_ids=tf.reshape(target_labels, [-1, 1])) - else: - # [time, batch, num_classes] - if time_axis == 0: - target_probs = tf.transpose( - self.smoother.FProp( - theta.smoother, - tf.transpose(target_paddings), - tf.transpose(target_labels), - target_ids=None), [1, 0, 2]) - else: - target_probs = self.smoother.FProp( - theta.smoother, target_paddings, target_labels, target_ids=None) - xent_loss = self.softmax.FProp( - theta.softmax, [softmax_input], - class_weights=tf.reshape(target_weights, [-1, 1]), - class_probabilities=tf.reshape(target_probs, - [-1, p.softmax.num_classes])) - return xent_loss - - def _ComputeSoftmaxMetrics(self, - xent_loss, - target_labels, - target_weights, - target_segment_ids=None, - time_axis=0): - """Computes cross-entropy metrics given the cross-entropy loss. - - Args: - xent_loss: The output of `_ComputeXentLoss`. - target_labels: A matrix of tf.int32. [time, batch]. - target_weights: A matrix of params.dtype. [time, batch]. - target_segment_ids: A matrix of params.dtype. [time, batch]. - time_axis: If 0, the inputs are time-major: [time, batch, ...]; if 1, the - inputs are batch-major: [batch, time, ...]. - - Returns: - A tuple (metrics, per_example_tensors). - metrics: - A dictionary containing metrics for the xent loss and prediction - accuracy. - per_example_tensors: - A dictionary of per-example tensors. - """ - p = self.params - if p.per_word_avg_loss: - final_loss = tf.identity(xent_loss.avg_xent, name='loss') - loss_weight = tf.identity(xent_loss.total_weight, name='num_predictions') - else: - # NOTE: Per-sequence loss is the sum of each example's loss. The - # final loss for a training batch is the mean loss of sequences in - # the batch. - # [time, batch] - per_example_loss = tf.reshape(xent_loss.per_example_xent, - py_utils.GetShape(target_weights)) - per_sequence_loss = tf.reduce_sum( - per_example_loss * target_weights, axis=time_axis) - if p.packed_input: - assert target_segment_ids is not None, ( - 'Need target segment ids for ' - 'normalizing loss when training with packed inputs.') - num_samples = tf.cast( - tf.reduce_sum( - tf.reduce_max(target_segment_ids, 0) - - tf.reduce_min(target_segment_ids, 0) + 1), - dtype=per_sequence_loss.dtype) - final_loss = tf.reduce_sum(per_sequence_loss) / num_samples - else: - final_loss = tf.reduce_mean(per_sequence_loss) - loss_weight = py_utils.GetShape(per_sequence_loss)[0] - - metrics = { - 'loss': (final_loss, loss_weight), - 'log_pplx': (xent_loss.avg_xent, xent_loss.total_weight), - } - - per_example_tensors = {} - if p.per_example_tensors: - per_example_tensors['per_example_loss'] = tf.reshape( - xent_loss.per_example_xent, py_utils.GetShape(target_weights)) - per_example_tensors['per_sequence_loss'] = tf.reduce_sum( - per_example_tensors['per_example_loss'] * target_weights, - axis=time_axis) - per_example_tensors['loss'] = per_example_tensors['per_sequence_loss'] - per_example_tensors['logits'] = tf.reshape( - xent_loss.logits, - tf.concat([py_utils.GetShape(target_weights), [-1]], 0)) - per_example_tensors['log_probs'] = tf.reshape( - xent_loss.log_probs, - tf.concat([py_utils.GetShape(target_weights), [-1]], 0)) - - # NOTE: tf.argmax is not implemented for the JF backend, see b/36093673 - # Skip the fraction_of_correct_next_step_preds during training. - if self.do_eval: - logits = xent_loss.logits - correct_preds = tf.cast( - tf.equal( - tf.cast(tf.reshape(tf.argmax(logits, 1), [-1]), tf.int32), - tf.reshape(target_labels, [-1])), p.dtype) - correct_next_preds = tf.reduce_sum( - correct_preds * tf.reshape(tf.cast(target_weights, p.dtype), [-1])) - num_preds = tf.reduce_sum(tf.cast(target_weights, p.dtype)) - accuracy = tf.identity( - correct_next_preds / num_preds, - name='fraction_of_correct_next_step_preds') - metrics['fraction_of_correct_next_step_preds'] = (accuracy, num_preds) - return metrics, per_example_tensors - - def _FPropSoftmax(self, - theta, - softmax_input, - target_labels, - target_weights, - target_paddings, - target_segment_ids=None, - time_axis=0): - """Computes cross-entropy loss given the softmax input, labels and weights. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - softmax_input: A tensor of shape [time, batch, p.softmax.input_dim]. - target_labels: A matrix of tf.int32. [time, batch]. - target_weights: A matrix of params.dtype. [time, batch]. - target_paddings: A matrix of params.dtype. [time, batch]. - target_segment_ids: A matrix of params.dtype. [time, batch]. - time_axis: If 0, the inputs are time-major: [time, batch, ...]; if 1, the - inputs are batch-major: [batch, time, ...]. - - Returns: - A tuple (metrics, per_example_tensors). - metrics: - A dictionary containing metrics for the xent loss and prediction - accuracy. - per_example_tensors: - A dictionary of per-example tensors. - """ - xent_loss = self._ComputeXentLoss(theta, softmax_input, target_labels, - target_weights, target_paddings) - return self._ComputeSoftmaxMetrics(xent_loss, target_labels, target_weights, - target_segment_ids, time_axis=time_axis) - - def ComputeLoss(self, theta, predictions, targets): - """Populates a metrics dictionary based on the output of ComputePredictions. - - Args: - theta: Nested map describing decoder model parameters. - predictions: NestedMap describing the decoding process, requiring: - .softmax_input: Tensor of shape [time, batch, params.softmax.input_dim]. - targets: NestedMap describing the target sequences. - - Returns: - Two dicts. - - - A map from metric name (a python string) to a tuple (value, weight). - Both value and weight are scalar Tensors. - - A map from name to arbitrary tensors, where the first dimension must - be the batch index. - """ - segment_id = None - if self.params.packed_input: - segment_id = tf.transpose(targets.segment_ids) - if isinstance(predictions, py_utils.NestedMap): - predictions = predictions.softmax_input - return self._FPropSoftmax(theta, predictions, tf.transpose(targets.labels), - tf.transpose(targets.weights), - tf.transpose(targets.paddings), segment_id) - - def _TruncateTargetSequence(self, targets): - """Truncate padded time steps from all sequences.""" - # The following tensors are all in the [batch, time] shape. - # Let's make a copy of targets. - targets = targets.Pack(targets.Flatten()) - target_ids = targets.ids - target_labels = targets.labels - target_weights = targets.weights - target_paddings = targets.paddings - max_seq_length = tf.cast( - tf.round(tf.reduce_max(tf.reduce_sum(1.0 - target_paddings, 1))), - tf.int32) - summary_utils.scalar('max_seq_length', max_seq_length) - # Assert to make sure after max_seq_length, all are padded steps for all - # sequences. - target_paddings = py_utils.with_dependencies([ - py_utils.assert_equal( - tf.constant(True, tf.bool), - tf.reduce_all(target_paddings[:, max_seq_length:] > 0.5)) - ], target_paddings) - target_ids = py_utils.with_dependencies([ - py_utils.AssertIdShape( - py_utils.GetShape(target_ids), py_utils.GetShape(target_labels), - py_utils.GetShape(target_paddings), - py_utils.GetShape(target_weights)) - ], target_ids) - targets.ids = target_ids[:, :max_seq_length] - targets.labels = target_labels[:, :max_seq_length] - targets.weights = target_weights[:, :max_seq_length] - targets.paddings = target_paddings[:, :max_seq_length] - return targets - - def _AddAttenProbsSummary(self, source_paddings, targets, atten_probs): - """Add summary of attention probs. - - Args: - source_paddings: source padding, of shape [src_len, src_batch]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [tgt_batch, tgt_len]. - atten_probs: a list of attention probs, each element is of shape [tgt_len, - tgt_batch, src_len]. - """ - if not self.cluster.add_summary: - return - - self._AddAttenProbsImageSummary(source_paddings, targets, atten_probs) - self._AddAttenProbsHistogramSummary(atten_probs) - - def _AddAttenProbsHistogramSummary(self, atten_probs): - """Add histogram summary of attention probs. - - Args: - atten_probs: a list of attention probs, each element is of shape [tgt_len, - tgt_batch, src_len]. - """ - for i, probs in enumerate(atten_probs): - # a prefix from the context will be used, which looks like - # fprop/wmt14_en_de_transformer/tower_0_0/dec/ - summary_utils.histogram('atten{}'.format(i + 1), probs) - - def _AddAttenProbsImageSummary(self, source_paddings, targets, atten_probs): - """Add image summary of attention probs. - - Args: - source_paddings: source padding, of shape [src_len, src_batch]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [tgt_batch, tgt_len]. - atten_probs: a list of attention probs, each element is of shape [tgt_len, - tgt_batch, src_len]. - """ - def PlotAttention(fig, axes, cur_atten_probs, title, set_x_label): - plot.AddImage(fig, axes, cur_atten_probs, title=title) - axes.set_ylabel(plot.ToUnicode('Output sequence index'), wrap=True) - if set_x_label: - axes.set_xlabel(plot.ToUnicode('Input sequence index'), wrap=True) - - index = 0 - srclen = tf.cast( - tf.round(tf.reduce_sum(1 - source_paddings[:, index])), tf.int32) - tgtlen = tf.cast( - tf.round(tf.reduce_sum(1 - targets.paddings[index, :])), tf.int32) - - num_rows = len(atten_probs) - with plot.MatplotlibFigureSummary( - 'decoder_example', - figsize=(6, 3 * num_rows), - max_outputs=1, - subplot_grid_shape=(num_rows, 1)) as fig: - for i, probs in enumerate(atten_probs): - # Extract first entry in batch of attention prob matrices - # [tgt_len, src_len] - probs = probs[:, index, :] - probs = tf.expand_dims(probs[:tgtlen, :srclen], 0) - fig.AddSubplot([probs], - PlotAttention, - title='atten_probs_%d' % i, - set_x_label=(i == len(atten_probs) - 1)) - - def _ExpandToNumHyps(self, source_enc_len, num_hyps_per_beam): - """Repeat each value according to num hyps. - - Args: - source_enc_len: source encoder length; int [batch]. - num_hyps_per_beam: number of hypotheses - - Returns: - New version of source_enc_len; int [batch * num_hyps_per_beam]. - Target_batch is (num_hyps_per_beam * batch). - Example: src_enc_len = [3, 2, 1] and num_hyps_per_beam = 2 - --> [3, 2, 1, 3, 2, 1] - """ - x = tf.tile(input=source_enc_len, multiples=[num_hyps_per_beam]) - return x - - -class MTDecoderV1(MTBaseDecoder, quant_utils.QuantizableLayer): - """MT decoder v1.""" - - @classmethod - def Params(cls): - p = super().Params() - # Shared embedding. - p.Define('emb', layers.EmbeddingLayer.Params(), 'Embedding layer params.') - p.Define('source_dim', 1024, 'Dimension of the source encoding.') - p.Define('attention', attention.AdditiveAttention.Params(), - 'Additive attention params.') - p.Define('atten_rnn_cell_tpl', rnn_cell.LSTMCellSimple.Params(), - 'Attention RNNCell params template.') - p.Define('rnn_cell_tpl', rnn_cell.LSTMCellSimple.Params(), - 'RNNCell params template.') - p.Define('rnn_cell_dim', 1024, 'size of the rnn cells.') - p.Define('rnn_layers', 8, 'Number of rnn layers.') - p.Define('residual_start', 2, 'Start residual connections from this layer.') - p.Define('atten_rnn_cls', rnn_layers.FRNNWithAttention, - 'Which atten rnn cls to use.') - p.Define('use_prev_atten_ctx', False, - 'If True, all decoder layers use previous attention context as ' - 'input. Otherwise, only first decoder layer uses previous ' - 'attention context and the rest of the layers use current ' - 'attention context.') - p.Define('dropout_prob', 0.0, 'Prob at which we do dropout.') - # Default value was mildly tuned. Could be further tuned in the future. - p.Define('qlogsoftmax_range_min', -10.0, 'Quantization of the output of ' - 'log softmax.') - p.Define( - 'use_zero_atten_state', False, 'To use zero attention state ' - 'instead of computing attention with zero query vector.') - - p.Define('cc_schedule', None, 'Clipping cap schedule.') - p.Define( - 'init_step_ids', False, - 'Initializes beam search with first target id instead of .' - 'Use this when decoding starts with target_lang id intead of ' - 'token at time step 0. Make sure the training data has ' - 'target_lang id as the first token in target sequence.') - - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - default_params_init = py_utils.WeightInit.Uniform(0.04) - - # Default config for the embedding. - p.emb.vn = disable_vn - p.emb.vocab_size = 32000 - p.emb.embedding_dim = 1024 - p.emb.max_num_shards = 16 - p.emb.params_init = default_params_init - - # Default config for the attention model. - p.attention.vn = disable_vn - p.attention.hidden_dim = 1024 - p.attention.params_init = None # Filled in after dims are known. - # Default config for the attention rnn cell. - p.atten_rnn_cell_tpl.vn = disable_vn - p.atten_rnn_cell_tpl.params_init = default_params_init - # Default config for the rnn cell. - p.rnn_cell_tpl.vn = disable_vn - p.rnn_cell_tpl.params_init = default_params_init - # Default config for the softmax part. - p.softmax.vn = disable_vn - p.softmax.num_classes = 32000 # 32k - p.softmax.num_shards = 16 - p.softmax.params_init = default_params_init - - # Default config for beam search. - p.target_seq_len = 300 - p.beam_search.length_normalization = 0.2 - p.beam_search.coverage_penalty = 0.2 - - return p - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Updates the params with the input vocab_size and WPM model. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model params updated with the vocab size and wpm model. - """ - p = super().UpdateTargetVocabSize(p, vocab_size) - p.emb.vocab_size = vocab_size - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.emb.vocab_size == p.softmax.num_classes - - if p.cc_schedule is None: - self.cc_schedule = None - else: - self.CreateChild('cc_schedule', p.cc_schedule) - - self.CreateChild('emb', p.emb) - - p.attention.dtype = p.dtype - p.attention.source_dim = p.source_dim - p.attention.query_dim = p.rnn_cell_dim - p.attention.packed_input = p.packed_input - if p.attention.params_init is None: - p.attention.params_init = py_utils.WeightInit.Gaussian( - 1. / math.sqrt(p.attention.source_dim + p.attention.query_dim), - seed=p.random_seed) - atten_params = p.attention.Copy() - - params = p.atten_rnn_cell_tpl.Copy() - params.name = 'atten_rnn' - params.dtype = p.dtype - params.reset_cell_state = p.packed_input - params.num_input_nodes = p.emb.embedding_dim + p.attention.source_dim - params.num_output_nodes = p.rnn_cell_dim - atten_rnn_cell = params.Copy() - - params = p.atten_rnn_cls.Params() - params.name = 'frnn_with_atten' - params.dtype = p.dtype - params.cell = atten_rnn_cell - params.attention = atten_params - params.output_prev_atten_ctx = p.use_prev_atten_ctx - params.packed_input = p.packed_input - params.use_zero_atten_state = p.use_zero_atten_state - params.atten_context_dim = p.attention.source_dim - self.CreateChild('frnn_with_atten', params) - - # TODO(zhifengc): Avoid this? - self._atten = self.frnn_with_atten.attention - - rnn_layers_params = [] - for i in range(1, p.rnn_layers): - params = p.rnn_cell_tpl.Copy() - params.name = 'rnn%d' % i - params.dtype = p.dtype - params.num_input_nodes = p.rnn_cell_dim + p.attention.source_dim - params.num_output_nodes = p.rnn_cell_dim - params.reset_cell_state = p.packed_input - rnn_cell_p = params - - params = model_helper.CreateUnidirectionalRNNParams( - self.params, rnn_cell_p) - params.name = 'frnn%d' % i - params.packed_input = p.packed_input - rnn_layers_params.append(params) - - self.CreateChildren('frnn', rnn_layers_params) - - p.softmax.dtype = p.dtype - if p.feed_attention_context_vec_to_softmax: - p.softmax.input_dim = p.rnn_cell_dim + p.attention.source_dim - else: - p.softmax.input_dim = p.rnn_cell_dim - self.CreateChild('softmax', p.softmax) - - def _CreateChildrenVariables(self): - with tf.variable_scope(self.params.name): - if py_utils.use_tpu(): - emb_device = self.cluster.WorkerDeviceInModelSplit(0) - else: - emb_device = '' - with tf.device(emb_device): - self.emb.InstantiateVariables() - self.frnn_with_atten.InstantiateVariables() - for frnn in self.frnn: - frnn.InstantiateVariables() - super()._CreateChildrenVariables() - - def ApplyDropout(self, x_in): - p = self.params - assert 0 <= p.dropout_prob and p.dropout_prob < 1.0 - if self.do_eval or p.dropout_prob == 0.0: - return x_in - else: - return tf.nn.dropout(x_in, rate=p.dropout_prob) - - def ApplyClipping(self, theta, x): - if self.cc_schedule: - return self.cc_schedule.ApplyClipping(theta.cc_schedule, x) - else: - return x - - @py_utils.NameScopeDecorator('MTDecoderV1/ComputePredictions') - def ComputePredictions(self, theta, encoder_outputs, targets): - """Decodes `targets` given encoded source. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: a NestedMap computed by encoder. Expected to contain: - encoded - source encoding, of shape [time, batch, depth]. - padding - source encoding's padding, of shape [time, batch]. - segment_id - (optional) source segment id, of shape [time, batch]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [batch, time]. - - Returns: - A `.NestedMap` containing information about the decoding process. At a - minimum, this should contain: - softmax_input: Tensor of shape [time, batch, params.softmax.input_dim]. - attention: `.NestedMap` of attention distributions of shape [batch, - time, source_len]. - source_enc_len: Lengths of source sentences. Tensor of shape [batch]. - """ - p = self.params - source_paddings = encoder_outputs.padding - time, batch = py_utils.GetShape(source_paddings, 2) - source_encs = py_utils.HasShape(encoder_outputs.encoded, - [time, batch, p.source_dim]) - with tf.name_scope(p.name): - target_ids = tf.transpose(targets.ids) - target_paddings = py_utils.HasRank(targets.paddings, 2) - target_paddings = tf.expand_dims(tf.transpose(target_paddings), 2) - if p.packed_input: - target_segment_id = tf.expand_dims(tf.transpose(targets.segment_ids), 2) - else: - target_segment_id = tf.zeros_like(target_paddings) - - if py_utils.use_tpu(): - emb_device = self.cluster.WorkerDeviceInModelSplit(0) - else: - emb_device = '' - with tf.device(emb_device): - inputs = self.emb.EmbLookup(theta.emb, target_ids) - inputs = self.ApplyClipping(theta, inputs) - summary_utils.histogram('input_emb', inputs) - inputs = self.ApplyDropout(inputs) - self._emb_out = inputs - - # Layer 0 intertwines with attention. - (accumulated_states, _, - side_info) = self.frnn_with_atten.AccumulateStates( - theta.frnn_with_atten, - source_encs, - source_paddings, - inputs, - target_paddings, - src_segment_id=getattr(encoder_outputs, 'segment_id', None), - segment_id=target_segment_id) - - (atten_ctxs, xs, atten_probs) = self.frnn_with_atten.PostProcessStates( - accumulated_states, side_info) - - self._AddAttenProbsSummary(source_paddings, targets, [atten_probs]) - - atten_ctxs = self.ApplyClipping(theta, atten_ctxs) - summary_utils.histogram('atten_ctxs', atten_ctxs) - - for i, (layer, layer_theta) in enumerate(zip(self.frnn, theta.frnn)): - # Forward through Layer-(i + 1) because Layer-0 handled before. - ys, _ = layer.FProp( - layer_theta, - tf.concat([xs, atten_ctxs], 2), - target_paddings, - segment_id=target_segment_id) - ys = self.ApplyDropout(ys) - if 1 + i >= p.residual_start: - xs += ys # Residual skip - xs = self.ApplyClipping(theta, xs) - else: - xs = ys - summary_utils.histogram('layer_out_%s' % i, xs) - - if p.feed_attention_context_vec_to_softmax: - xs = tf.concat([xs, atten_ctxs], 2) - - # Get intermediate attention information - atten_states = accumulated_states.atten_state - if isinstance(atten_states, py_utils.NestedMap): - additional_atten_probs = sorted( - [(name, tensor) - for name, tensor in atten_states.FlattenItems() - if name.endswith('probs')]) - else: - additional_atten_probs = [] - attention_map = py_utils.NestedMap(probs=accumulated_states.atten_probs) - attention_map.update(additional_atten_probs) - - # Transpose attention probs from [target_length, batch, source_length] - # to [batch, target_length, source_length] - def _TransposeAttentions(x): - return tf.transpose(x, [1, 0, 2]) - - attention_map = attention_map.Transform(_TransposeAttentions) - if isinstance(source_paddings, tf.Tensor): - source_enc_len = tf.reduce_sum(1 - source_paddings, axis=0) - - return py_utils.NestedMap( - softmax_input=xs, - attention=attention_map, - source_enc_len=source_enc_len) - - def AddExtraDecodingInfo(self, encoder_outputs, targets): - """Adds extra decoding information to encoded_outputs. - - Args: - encoder_outputs: a NestedMap computed by encoder. - targets: a NestedMap containing target input fields. - - Returns: - encoder_ouputs with extra information used for decoding. - """ - p = self.params - if p.init_step_ids: - encoder_outputs['init_step_ids'] = targets.ids[:, 0] - return encoder_outputs - - @py_utils.NameScopeDecorator('MTDecoderV1/InitDecoder') - def _InitDecoder(self, theta, encoder_outputs, num_hyps): - """Returns initial decoder states. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: a NestedMap computed by encoder. - num_hyps: Scalar Tensor of type int, Number of hypothesis maintained in - beam search, equal to beam_size * num_hyps_per_beam. - - Returns: - Tuple of initial model states. Also inserts 'packed_src' to - 'encoder_outputs'. - """ - p = self.params - source_paddings = encoder_outputs.padding - time, batch = py_utils.GetShape(source_paddings, 2) - source_encs = py_utils.HasShape(encoder_outputs.encoded, - [time, batch, p.source_dim]) - rnn_states = [ - self.frnn_with_atten.cell.zero_state(theta.frnn_with_atten.cell, - num_hyps) - ] - for layer, layer_theta in zip(self.frnn, theta.frnn): - rnn_states.append(layer.rnn_cell.zero_state(layer_theta, num_hyps)) - - if p.use_zero_atten_state: - encoder_outputs.packed_src = self._atten.InitForSourcePacked( - theta.frnn_with_atten.atten, source_encs, source_encs, - source_paddings) - s_seq_len = tf.shape(source_encs)[0] - context_dim = tf.shape(source_encs)[2] - atten_context = tf.zeros([num_hyps, context_dim], dtype=source_encs.dtype) - atten_states = self._atten.ZeroAttentionState(s_seq_len, num_hyps) - atten_probs = tf.zeros([num_hyps, s_seq_len], dtype=source_encs.dtype) - else: - encoder_outputs.packed_src = self._atten.InitForSourcePacked( - theta.frnn_with_atten.atten, source_encs, source_encs, - source_paddings) - - src_seq_len = tf.shape(source_encs)[0] - zero_atten_state = self._atten.ZeroAttentionState(src_seq_len, num_hyps) - - (atten_context, atten_probs, - atten_states) = self._atten.ComputeContextVectorWithSource( - theta.frnn_with_atten.atten, - encoder_outputs.packed_src, - tf.zeros([num_hyps, p.rnn_cell_dim], dtype=py_utils.FPropDtype(p)), - attention_state=zero_atten_state) - - assert atten_states is not None - return rnn_states, atten_context, atten_probs, atten_states - - @py_utils.NameScopeDecorator('MTDecoderV1/DecodeStep') - def _DecodeStep(self, theta, encoder_outputs, embs, step_paddings, - prev_atten_context, rnn_states, prev_atten_states): - """Decode one step.""" - p = self.params - new_rnn_states = [] - new_rnn_states_0, _ = self.frnn_with_atten.cell.FProp( - theta.frnn_with_atten.cell, rnn_states[0], - py_utils.NestedMap( - act=[tf.concat([embs, prev_atten_context], 1)], - padding=step_paddings, - reset_mask=tf.ones_like(step_paddings))) - new_rnn_states.append(new_rnn_states_0) - rnn_out = self.frnn_with_atten.cell.GetOutput(new_rnn_states_0) - cur_atten_context, atten_probs, atten_states = ( - self._atten.ComputeContextVectorWithSource( - theta.frnn_with_atten.atten, - encoder_outputs.packed_src, - rnn_out, - attention_state=prev_atten_states)) - assert atten_states is not None - - if p.use_prev_atten_ctx: - atten_context = prev_atten_context - else: - atten_context = cur_atten_context - - for i, (layer, layer_theta) in enumerate(zip(self.frnn, theta.frnn)): - new_rnn_states_i, _ = layer.rnn_cell.FProp( - layer_theta.cell, rnn_states[1 + i], - py_utils.NestedMap( - act=[tf.concat([rnn_out, atten_context], 1)], - padding=step_paddings, - reset_mask=tf.ones_like(step_paddings))) - new_rnn_states.append(new_rnn_states_i) - new_rnn_out = layer.rnn_cell.GetOutput(new_rnn_states_i) - if 1 + i >= p.residual_start: - rnn_out += new_rnn_out - rnn_out = self.ApplyClipping(theta, rnn_out) - else: - rnn_out = new_rnn_out - # Concatenating atten_context vec to rnn output before softmax might help - if p.feed_attention_context_vec_to_softmax: - step_out = tf.concat([rnn_out, atten_context], 1) - else: - step_out = rnn_out - return (cur_atten_context, atten_probs, new_rnn_states, step_out, - atten_states) - - def _GetAttentionInitState(self): - """Gets the attention initialization state. - - It is valid to call this after `_DecoderInit()`. Inference subclasses use - this to split computation across subgraph boundaries. - - Returns: - `.NestedMap` of attention source states. - """ - return self._atten.GetInitializationSourceState() - - def _SetAttentionInitState(self, new_init_state): - """Sets the attention initialization state. - - Args: - new_init_state: `.NestedMap` compatible with that returned from - `_GetAttentionSourceState`. - """ - self._atten.SetInitializationSourceState(new_init_state) - - def _InitBeamSearchStateCallback(self, theta, encoder_outputs, - num_hyps_per_beam): - """Returns initial beams search states. - - Args: - theta: a NestedMap of parameters. - encoder_outputs: a NestedMap computed by encoder. - num_hyps_per_beam: An int, number hyps to keep for source sentence. - - Returns: - A tuple (initial_results, states). - initial_results: a `.NestedMap` of initial results. - atten_probs: - The initial attention probs, of shape [tgt_batch, src_len]. - states: a `.NestedMap` of initial model states. - rnn_states: - Initial state of the RNN. - atten_context: - Initial attention context vector. - atten_states: - Initial attention state. - """ - p = self.params - num_beams = py_utils.GetShape(encoder_outputs.padding)[1] - num_hyps = num_beams * num_hyps_per_beam - rnn_states, init_atten_context, atten_probs, atten_states = ( - self._InitDecoder(theta, encoder_outputs, num_hyps)) - - initial_results = py_utils.NestedMap( - log_probs=tf.zeros([num_hyps, p.softmax.num_classes], - dtype=py_utils.FPropDtype(p)), - atten_probs=atten_probs) - - if p.init_step_ids and hasattr(encoder_outputs, 'init_step_ids'): - initial_results['step_ids'] = tf.expand_dims( - self._ExpandToNumHyps(encoder_outputs.init_step_ids, - num_hyps_per_beam), 1) - - return initial_results, py_utils.NestedMap({ - 'time_step': tf.constant(0), - 'rnn_states': rnn_states, - 'atten_context': init_atten_context, - 'atten_probs': atten_probs, - 'atten_states': atten_states, - }) - - @py_utils.NameScopeDecorator('MTDecoderV1/PreBeamSearchStepCallback') - def _PreBeamSearchStepCallback(self, theta, encoder_outputs, step_ids, states, - num_hyps_per_beam): - """Returns logits for sampling ids and the next model states. - - Args: - theta: a NestedMap of parameters. - encoder_outputs: a NestedMap computed by encoder. - step_ids: A tensor of shape [tgt_batch, 1]. - states: A `.NestedMap` of tensors representing states that the clients - would like to keep track of for each of the active hyps. - num_hyps_per_beam: Beam size. - Returns: - A tuple (results, out_states). - results: A `.NestedMap` of beam search results. - atten_probs: - The updated attention probs, of shape [tgt_batch, src_len]. - log_probs: - Log prob for each of the tokens in the target vocab. This is of shape - [tgt_batch, vocab_size]. - out_states: A `.NestedMap`. The updated states. - rnn_states: - Last state of the RNN. - atten_context: - Updated attention context vector. - atten_states: - Updates attention states. - """ - p = self.params - - prev_rnn_states = states['rnn_states'] - prev_atten_context = states['atten_context'] - prev_atten_probs = states['atten_probs'] - prev_atten_states = states['atten_states'] - step_paddings = tf.zeros(py_utils.GetShape(step_ids), dtype=p.dtype) - embs = self.emb.EmbLookup(theta.emb, tf.reshape(step_ids, [-1])) - embs = self.ApplyClipping(theta, embs) - atten_context, atten_probs, rnn_states, step_out, atten_states = ( - self._DecodeStep(theta, encoder_outputs, embs, step_paddings, - prev_atten_context, prev_rnn_states, - prev_atten_states)) - atten_probs = tf.reshape(atten_probs, tf.shape(prev_atten_probs)) - - logits = self.softmax.Logits(theta.softmax, [step_out]) - log_probs = self.fns.qlogsoftmax( - logits, qmin=p.qlogsoftmax_range_min, qmax=0.0) - - if p.use_prev_atten_ctx: - cur_atten_probs = prev_atten_probs - else: - cur_atten_probs = atten_probs - - bs_results = py_utils.NestedMap({ - 'atten_probs': cur_atten_probs, # the probs exposed to beam search - 'log_probs': log_probs, - }) - new_states = py_utils.NestedMap({ - 'time_step': states.time_step + 1, - 'rnn_states': rnn_states, - 'atten_context': atten_context, - 'atten_probs': atten_probs, # the updated attention probs - 'atten_states': atten_states, - }) - - return bs_results, new_states - - def _PostBeamSearchStepCallback(self, theta, encoder_outputs, new_step_ids, - states): - # There is nothing to do here. - return states - - -class TransformerDecoder(MTBaseDecoder): - """Transformer decoder. - - Implements the decoder of Transformer model: - https://arxiv.org/abs/1706.03762. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('token_emb', layers.EmbeddingLayer.Params(), - 'Token embedding layer params.') - p.Define('position_emb', layers.PositionalEmbeddingLayer.Params(), - 'Position embedding layer params.') - p.Define('source_dim', 1024, 'Dimension of encoder outputs.') - p.Define('model_dim', 1024, 'Model dimension that applies to embedding ' - 'layers and all Transformer layers.') - p.Define('num_trans_layers', 6, 'Number of Transformer layers.') - p.Define( - 'trans_tpl', layers_with_attention.TransformerLayer.Params(), - 'Transformer layer params. ' - ' Can be a list. num_trans_layers should be divisible by ' - 'len(trans_tpl).') - p.Define('input_dropout_prob', 0.0, 'Prob at which we do input dropout.') - p.Define( - 'is_transparent', False, 'If set, expects a tensor of shape ' - '[time, batch, source_dim, num_trans_layers] as source encodings.') - p.Define( - 'add_multiheaded_attention_scalar_summary', False, - 'If set, will include scalar summaries for multi-headed attention' - ' to visualize the sparsity statistics of attention weights.') - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params') - p.Define( - 'ln_output', False, 'If True, layer normalization is applied to the ' - 'final output of the decoder.') - - # TODO(miachen): Extend this to more general logic of adding multiple - # embedding fields. - p.Define('task_emb', None, 'Task embedding layer params.') - p.Define( - 'init_step_ids', False, - 'Initializes beam search with first target id instead of .' - 'Use this when decoder has target language token intead of ' - 'token at time step 0.' - 'Make sure the training is done in similar manner.') - # MASS pretraining related (https://github.com/microsoft/MASS) - p.Define( - 'use_lang_dependent_atten', False, 'If True, attention between ' - 'encoder and decoder is language dependent.') - - p.Define('zero_token_embs_first_time_step', False, - 'If True, the first time step uses zeros as the post-emb lookup.') - - # Default config for the token embedding. - p.token_emb.vocab_size = 32000 - p.token_emb.embedding_dim = p.model_dim - p.token_emb.max_num_shards = 16 - p.token_emb.params_init = py_utils.WeightInit.Gaussian( - 1.0 / math.sqrt(p.token_emb.embedding_dim)) - p.token_emb.scale_sqrt_depth = True - - # Default config for the position embedding. - p.position_emb.embedding_dim = p.model_dim - - # Default config for the transformer layers. - p.trans_tpl.source_dim = p.model_dim - p.trans_tpl.tr_atten_tpl.source_dim = p.model_dim - p.trans_tpl.tr_atten_tpl.num_attention_heads = 8 - p.trans_tpl.tr_fflayer_tpl.input_dim = p.model_dim - p.trans_tpl.tr_fflayer_tpl.hidden_dim = 2048 - - # Default config for beam search. - p.target_seq_len = 300 - p.beam_search.length_normalization = 0.5 - p.beam_search.coverage_penalty = 0.0 - p.beam_search.batch_major_state = False - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - if p.softmax.cls == layers.SharedSoftmaxLayer: - self._token_emb_vocab_size = p.softmax.num_classes - self._token_emb_dim = p.model_dim - self._share_sm_emb = True - else: - self._token_emb_vocab_size = p.token_emb.vocab_size - self._token_emb_dim = p.token_emb.embedding_dim - self._share_sm_emb = False - - assert self._token_emb_vocab_size == p.softmax.num_classes - assert self._token_emb_dim == p.position_emb.embedding_dim - if p.model_dim != self._token_emb_dim: - tf.logging.warning( - 'token_emb.embedding_dim != model_dim (%s vs. %s), ' - 'creating a projection!') - proj_p = layers.ProjectionLayer.Params().Copy() - proj_p.name = 'emb_proj' - proj_p.input_dim = p.token_emb.embedding_dim - proj_p.output_dim = p.model_dim - self.CreateChild('emb_proj', proj_p) - - if p.use_lang_dependent_atten and p.task_emb: - p.trans_tpl.num_aux_atten_post_proj = p.task_emb.vocab_size - - if not self._share_sm_emb: - self.CreateChild('token_emb', p.token_emb) - self.CreateChild('position_emb', p.position_emb) - if p.task_emb: - assert p.task_emb.embedding_dim == self._token_emb_dim - self.CreateChild('task_emb', p.task_emb) - - dropout_tpl = layers.DropoutLayer.Params() - dropout_tpl.keep_prob = (1.0 - p.input_dropout_prob) - self.CreateChild('input_dropout', dropout_tpl) - - params_trans_layers = [] - denom = 1 - if isinstance(p.trans_tpl, list): - denom = len(p.trans_tpl) - assert p.num_trans_layers % denom == 0 - for i in range(p.num_trans_layers // denom): - if isinstance(p.trans_tpl, list): - for q in p.trans_tpl: - params = q.Copy() - params_trans_layers.append(params) - else: - params = p.trans_tpl.Copy() - params_trans_layers.append(params) - - for i, params in enumerate(params_trans_layers): - params.name = 'trans_layer_%d' % i - params.packed_input = p.packed_input - params.has_aux_atten = True - params.mask_self_atten = True - - # Initialize decoder output layer norm - if p.ln_output: - params = p.ln_tpl.Copy() - params.name = 'dec_out_ln' - params.input_dim = p.model_dim - self.CreateChild('layer_norm_out', params) - - self.CreateChildren('trans', params_trans_layers) - - p.softmax.input_dim = p.model_dim - self.CreateChild('softmax', p.softmax) - - def _CreateChildrenVariables(self): - if self._share_sm_emb: - # Taking shared emb/softmax layer out of the decoder variable scope so - # that it can also be shared by encoder if needed. - with tf.variable_scope('shared_emb', reuse=tf.AUTO_REUSE): - self.softmax.InstantiateVariables() - super()._CreateChildrenVariables() - - def _RemoveEOSProbs(self, p, probs, source_enc_len): - """Remove the attention probs on EOS symbol and renormalize. - - Args: - p: decoder params. - probs: attention probs matrix; float [batch, target_len, source_len]. - source_enc_len: source encoder length; int [batch]. - - Returns: - probs with value on last actual token (EOS token) replaced by 0 and - renormalized so that final dim (src_len) sums to 1 again; float - [batch, target_len, source_len]. - """ - batch = py_utils.GetShape(probs)[0] - source_enc_len = py_utils.HasShape(source_enc_len, [batch]) - - # Set -1 values - target_len = py_utils.GetShape(probs)[1] - replacements = tf.ones([py_utils.GetShape(probs)[0], target_len], - dtype=py_utils.FPropDtype(p)) * (-1) - - index_0 = tf.reshape(tf.range(batch), shape=[batch, 1, 1]) - index_0 *= tf.ones(shape=[batch, target_len, 1], dtype=tf.int32) - - index_1 = tf.ones(shape=[batch, 1], dtype=tf.int32) - index_1 *= tf.expand_dims(tf.range(target_len), 0) - index_1 = tf.expand_dims(index_1, -1) - - index_2 = tf.reshape(source_enc_len, shape=[batch, 1, 1]) - 1 # Note the -1 - index_2 = tf.cast(index_2, tf.int32) - index_2 *= tf.ones(shape=[batch, target_len, 1], dtype=tf.int32) - - index = tf.concat([index_0, index_1, index_2], axis=2) - - # Original update matrix contained -1 values. Change all to 1 except for - # those positions coming from scatter which will be 0. - updates = tf.scatter_nd( - index, updates=replacements, shape=py_utils.GetShape(probs)) - updates += 1 - res = probs * updates - - # Normalize to that probs sum to 1. - # Add eps to sum to deal with case where all probs except last one are 0. - # In this case then, attention probs will not sum to 1 but this seems still - # better then evenly distributing attention probs in this case. - s = tf.reduce_sum(res, axis=2, keepdims=True) - epsilon = tf.constant(value=1e-6, dtype=py_utils.FPropDtype(p)) - s += epsilon - res /= s - return res - - def _ZeroOutFirstTimeStep(self, token_embs, batch, target_time): - """Zeroes out the first time step. - - Args: - token_embs: [batch, time, model_dim] embeding lookups - batch: Batch size scalar - target_time: Target sequence length scalar. - - Returns: - modified token_embs with the first time step zeroed out. - """ - p = self.params - - zero_out_index = tf.expand_dims(tf.constant([0]), axis=1) - # [[[0]]] - zero_out_index = tf.expand_dims(zero_out_index, axis=1) - - # [[0]...[target_time-1]] - time_steps = tf.expand_dims(tf.range(target_time), axis=1) - condition = tf.equal(zero_out_index, time_steps) - mask = tf.logical_not(tf.tile(condition, [batch, 1, p.model_dim])) - mask = tf.cast(mask, dtype=tf.float32) - return token_embs * mask - - def _FProp(self, theta, encoder_outputs, targets): - """Decodes `targets` given encoded source. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: a NestedMap computed by encoder. Expected to contain: - encoded - source encoding. When `p.is_transparent` is False, it is a - tensor of shape [time, batch, depth]. When `p.is_transparent` is True, - it is a tensor of shape [time, batch, depth, num_trans_layers] if - `self.do_eval` is True, and a list of `num_trans_layers` tensors of - shape [time, batch, depth] if `self.do_eval` is False. padding - source - encoding's padding, of shape [time, batch]. segment_id - source segment - id, of shape [time, batch]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [batch, time]. - - Returns: - A `.NestedMap` containing output of last decoder layer and attention probs - - - softmax_input: Tensor of shape [time, batch, params.softmax.input_dim]. - - attention: `.NestedMap` of attention distributions of shape - [batch, target_length, source_length]. - """ - p = self.params - source_encs = encoder_outputs.encoded - source_paddings = encoder_outputs.padding - src_segment_id = getattr(encoder_outputs, 'segment_id', None) - time, batch = py_utils.GetShape(source_paddings, 2) - if p.is_transparent: - if self.do_eval: - source_encs = py_utils.HasShape( - source_encs, [time, batch, p.source_dim, p.num_trans_layers]) - source_encs = tf.unstack(source_encs, axis=3) - else: - assert isinstance(source_encs, list) - assert len(source_encs) == p.num_trans_layers - for i in range(p.num_trans_layers): - source_encs[i] = py_utils.HasShape(source_encs[i], - [time, batch, p.source_dim]) - else: - source_encs = py_utils.HasShape(source_encs, [time, batch, p.source_dim]) - source_encs = [source_encs] * p.num_trans_layers - with tf.name_scope(p.name): - # [batch, time] - target_ids = targets.ids - # [time, batch] - target_paddings = tf.transpose(targets.paddings) - target_segment_pos = None - target_segment_id = None - if p.packed_input: - target_segment_id = tf.transpose(targets.segment_ids) - target_segment_pos = targets.segment_pos - assert src_segment_id is not None, ('Need to provide src_segment_id ' - 'for packed input.') - - # Embedding layer - # [batch, time, model_dim] - if not self._share_sm_emb: - token_embs = self.token_emb.EmbLookup(theta.token_emb, target_ids) - else: - token_embs = self.softmax.EmbLookup(theta.softmax, target_ids) - - target_batch = py_utils.GetShape(target_ids)[0] - target_time = py_utils.GetShape(target_ids)[1] - - if p.zero_token_embs_first_time_step: - # For models that do not use an explicit start-of-sequence token - # with associated embedding, but instead use zeros. - token_embs = self._ZeroOutFirstTimeStep(token_embs, target_batch, - target_time) - - # [1, time, model_dim] - if p.packed_input: - posit_embs = self.position_emb.FPropWithPosition( - theta.position_emb, target_segment_pos) - else: - posit_embs = tf.expand_dims( - self.position_emb.FProp(theta.position_emb, target_time), 0) - - # [time, batch, model_dim] - input_embs = token_embs + posit_embs - - atten_idx = None - if p.task_emb: - if p.use_lang_dependent_atten: - atten_idx = targets.task_ids - # Works for both packed and unpacked inputs. - atten_idx = tf.reshape(tf.transpose(atten_idx), [-1]) - input_embs += self.task_emb.EmbLookup(theta.task_emb, targets.task_ids) - - if p.model_dim != self._token_emb_dim: - input_embs = self.emb_proj.FProp(theta.emb_proj, input_embs) - - input_embs = tf.transpose(input_embs, [1, 0, 2]) - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - - if not p.packed_input: - src_enc_len = tf.reduce_sum(1 - source_paddings, axis=0) - num_hyps_per_beam = tf.div( - py_utils.GetShape(target_paddings)[1], - py_utils.GetShape(source_paddings)[1]) - src_enc_len = self._ExpandToNumHyps(src_enc_len, num_hyps_per_beam) - - layer_in = input_embs - per_layer_attn_probs = [] - for i, (layer, layer_theta) in enumerate(zip(self.trans, theta.trans)): - extra_kwargs = dict() - if isinstance(layer, layers_with_attention.TransformerWithContextLayer): - # If the encoder contains encodings for the context and the - # transformer layer in the decoder is able to attend to it, we pass - # them to the transformer layer. - extra_kwargs['tertiary_vecs'] = encoder_outputs.context_encoded - extra_kwargs['tertiary_paddings'] = encoder_outputs.context_padding - # [time, batch, model_dim] - layer_out, probs = layer.FProp( - layer_theta, - layer_in, - target_paddings, - source_encs[i], - source_paddings, - source_segment_id=target_segment_id, - aux_segment_id=src_segment_id, - atten_idx=atten_idx, - **extra_kwargs) - layer_in = layer_out - pl_probs = tf.transpose(probs, [1, 0, 2]) - if p.packed_input: - # For packed inputs we are currently not removing the EOS token. - per_layer_attn_probs.append(pl_probs) - else: - # Remove attention weight on last (EOS) token and re-normalize - # so that last dimension sums to 1. See b/129097156. - # Original probs shape: [trg time, batch, src time] - norma_atten_probs_3d = self._RemoveEOSProbs(p, pl_probs, src_enc_len) - per_layer_attn_probs.append(norma_atten_probs_3d) - - if p.ln_output: - layer_out = self.layer_norm_out.FProp(theta.layer_norm_out, layer_out) - - # per_layer_attn_probs shape: [batch, trg time, src time] - self._AddAttenProbsSummary(source_paddings, targets, per_layer_attn_probs) - - # Aggregate per-layer attention probs. - aggregated_atten_probs = ( - tf.math.add_n(per_layer_attn_probs) / len(per_layer_attn_probs)) - - attention_map = py_utils.NestedMap(probs=aggregated_atten_probs) - return py_utils.NestedMap( - softmax_input=layer_out, attention=attention_map) - - def AddExtraDecodingInfo(self, encoder_outputs, targets): - """Adds extra decoding information to encoded_outputs. - - Args: - encoder_outputs: a NestedMap computed by encoder. - targets: a NestedMap containing target input fields. - - Returns: - encoder_ouputs with extra information used for decoding. - """ - p = self.params - if p.task_emb: - encoder_outputs['target_task_ids'] = targets.task_ids[:, 0] - if p.init_step_ids: - encoder_outputs['init_step_ids'] = targets.ids[:, 0] - return encoder_outputs - - def ExtendStep(self, theta, encoder_outputs, new_ids, t, prefix_states): - """Extend prefix as represented by `prefix_states` by one more step. - - This function is expected to be called during fast decoding of Transformer - models. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: a NestedMap computed by encoder, containing: - - - encoded: source encoding, of shape [time, batch, depth]. Can be [time, - bs, depth, num_trans_layers] if is_transparent is set. - - padding: source encoding's padding, of shape [time, batch]. - new_ids: new input ids, of shape [batch]. - t: a scalar, the current time step, 0-based. - prefix_states: a `.NestedMap` representing the prefix that has already - been decoded. - - Returns: - A tuple (last_decoder_out, prefix_states, atten_probs), where - last_decoder_out is the output of the last decoder layer of - shape [batch, model_dim], `prefix_states` is the update prefix states, - and atten_probs contains attention in shape [batch, src_len] for the - given target position. - """ - p = self.params - source_paddings = encoder_outputs.padding - time, batch = py_utils.GetShape(source_paddings, 2) - if p.is_transparent: - source_encs = py_utils.HasShape( - encoder_outputs.encoded, - [time, batch, p.source_dim, p.num_trans_layers]) - source_encs = tf.unstack(source_encs, axis=3) - else: - source_encs = py_utils.HasShape(encoder_outputs.encoded, - [time, batch, p.source_dim]) - source_encs = [source_encs] * p.num_trans_layers - with tf.name_scope(p.name): - # Embedding layer - # [batch, time, model_dim] - if not self._share_sm_emb: - token_embs = self.token_emb.EmbLookup(theta.token_emb, new_ids) - else: - token_embs = self.softmax.EmbLookup(theta.softmax, new_ids) - - if p.zero_token_embs_first_time_step: - # For models that do not use an explicit start-of-sequence token - # with associated embedding, but instead use zeros. - zeros = tf.zeros_like(token_embs) - token_embs = tf.cond(tf.equal(t, 0), lambda: zeros, lambda: token_embs) - - # [time, model_dim] - posit_embs = tf.slice( - self.position_emb.FProp(theta.position_emb, p.target_seq_len), [t, 0], - [1, p.model_dim]) - input_embs = token_embs + posit_embs - - # Infer num_hyps_per_beam: new_ids has orig_batch_size * num_hyps_per_beam - # source_paddings has orig_batch_size. - num_hyps_per_beam = tf.div( - py_utils.GetShape(new_ids)[0], - py_utils.GetShape(source_paddings)[1]) - - atten_idx = None - if p.task_emb: - task_ids = self._ExpandToNumHyps(encoder_outputs.target_task_ids, - num_hyps_per_beam) - if p.use_lang_dependent_atten: - atten_idx = task_ids - input_embs += self.task_emb.EmbLookup(theta.task_emb, task_ids) - - if p.model_dim != self._token_emb_dim: - input_embs = self.emb_proj.FProp(theta.emb_proj, input_embs) - - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - # Make a copy of the input. - out_prefix_states = prefix_states.Pack(prefix_states.Flatten()) - - layer_in = input_embs - - # Infer true source encoder length from the padding. - src_enc_len = tf.reduce_sum(1 - source_paddings, axis=0) - - # Need to expand src_enc_len to reflect multiple hypotheses. - src_enc_len = self._ExpandToNumHyps(src_enc_len, num_hyps_per_beam) - - atten_probs = [] - for i, (layer, layer_theta) in enumerate(zip(self.trans, theta.trans)): - extra_kwargs = dict() - if isinstance(layer, layers_with_attention.TransformerWithContextLayer): - # If the encoder contains encodings for the context and the - # transformer layer in the decoder is able to attend to it, we pass - # them to the transformer layer. - extra_kwargs['tertiary_vecs'] = encoder_outputs.context_encoded - extra_kwargs['tertiary_paddings'] = encoder_outputs.context_padding - # [time, batch, model_dim] - layer_prefix_states = prefix_states['layer_%i' % i] - layer_out, probs, updated_prefix_states = layer.ExtendStep( - layer_theta, - layer_in, - layer_prefix_states, - source_encs[i], - source_paddings, - t=t if p.beam_search.name == 'tpu_beam_search' else None, - atten_idx=atten_idx, - **extra_kwargs) - out_prefix_states['layer_%i' % i] = updated_prefix_states - layer_in = layer_out - # Enforce shape: [batch, src_len] - probs = tf.squeeze(probs, [0]) - # Remove attention weight on last (EOS) token and re-normalize - # so that last dimension sums to 1. See b/129097156. - probs_3d = tf.expand_dims(probs, axis=1) - probs_3d = self._RemoveEOSProbs(p, probs_3d, src_enc_len) - probs = tf.squeeze(probs_3d, axis=1) - - atten_probs.append(probs) - - if p.ln_output: - layer_out = self.layer_norm_out.FProp(theta.layer_norm_out, layer_out) - - # Aggregate per-layer attention probs. - aggregated_atten_probs = tf.math.add_n(atten_probs) / len(atten_probs) - return layer_out, out_prefix_states, aggregated_atten_probs - - def ComputePredictions(self, theta, encoder_outputs, targets): - """Decodes `targets` given encoded source. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: a NestedMap computed by encoder. Expected to contain: - - encoded - source encoding, of shape [time, batch, depth]. Can be [time, - batch, depth, num_layers] if is_transparent is set. - - padding - source encoding's padding, of shape [time, batch]. - segment_id - source segment id, of shape [time, batch]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [batch, time]. - - Returns: - A `.NestedMap` containing output of last decoder layer and attention probs - - - softmax_input: Tensor of shape [time, batch, params.softmax.input_dim]. - - attention: `.NestedMap` of attention distributions of shape - [batch, time, source_len]. - """ - return self._FProp(theta, encoder_outputs, targets) - - def SampleSequenceDecode(self, encoder_outputs): - """Decode via sampling from softmax at each step. - - Args: - encoder_outputs: the outputs of the encoder. - - Returns: - BeamSearchDecodeOutput, same as what BeamSearchDecode returns. - """ - p = self.params - non_tpu = p.beam_search.name != 'tpu_beam_search' - - def InitCallback(theta, encoder_outputs, num_hyps_per_beam=1): - """Wrapper for _InitBeamSearchStateCallback for sequence sampler. - - The main change is to ensure state tensors have fixed shapes. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer - and its children layers. - encoder_outputs: a NestedMap computed by encoder. - num_hyps_per_beam: An int, number hyps to keep for source sentence. - - Returns: - A NestedMap of - - - initial_results: a `.NestedMap` of initial results. - - states: a `.NestedMap` of initial model states. - """ - init_results, states = self._InitBeamSearchStateCallback( - theta, encoder_outputs, num_hyps_per_beam) - if non_tpu: - prefix_states = states['prefix_states'] - for layer in range(p.num_trans_layers): - key = prefix_states['layer_%d' % layer]['key'] - value = prefix_states['layer_%d' % layer]['value'] - key_shapes = py_utils.GetShape(key) - bs = key_shapes[1] - atten_dim = key_shapes[2] - zeros = tf.zeros([p.target_seq_len, bs, atten_dim], - dtype=py_utils.FPropDtype(p)) - prefix_states['layer_%d' % layer]['key'] = tf.concat([key, zeros], 0) - prefix_states['layer_%d' % layer]['value'] = tf.concat([value, zeros], - 0) - return init_results, states - - def PreBeamSearchCallback(theta, - encoder_outputs, - step_ids, - states, - num_hyps_per_beam=1): - """Wrapper for _PreBeamSearchStepCallback for sequence sampler. - - The main change is to ensure state tensors have fixed shapes. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer - and its children layers. - encoder_outputs: a NestedMap computed by encoder. - step_ids: A tensor of shape [tgt_batch, 1]. - states: A `.NestedMap` of tensors representing states that the clients - would like to keep track of for each of the active hyps. - num_hyps_per_beam: Beam size. - - Returns: - A NestedMap of - - - results: A `.NestedMap` of beam search results. - - out_states: A `.NestedMap`. The updated states. - """ - - if non_tpu: - # Strip off paddings. - prefix_states = states['prefix_states'] - target_time = states.time_step - for layer in range(p.num_trans_layers): - key = prefix_states['layer_%d' % layer]['key'] - val = prefix_states['layer_%d' % layer]['value'] - prefix_states['layer_%d' % layer]['key'] = tf.slice( - key, [0, 0, 0], [target_time, -1, -1]) - prefix_states['layer_%d' % layer]['value'] = tf.slice( - val, [0, 0, 0], [target_time, -1, -1]) - - bs_results, new_states = self._PreBeamSearchStepCallback( - theta, encoder_outputs, step_ids, states, num_hyps_per_beam) - - if non_tpu: - # Add back paddings (to maintain paddings shape). - bs = tf.shape(new_states.prefix_states['layer_0']['key'])[1] - dim = tf.shape(new_states.prefix_states['layer_0']['key'])[2] - pad = tf.zeros([p.target_seq_len - new_states.time_step, bs, dim], - dtype=py_utils.FPropDtype(p)) - for layer in range(p.num_trans_layers): - key = new_states.prefix_states['layer_%d' % layer]['key'] - val = new_states.prefix_states['layer_%d' % layer]['value'] - new_states.prefix_states['layer_%d' % layer]['key'] = tf.concat( - [key, pad], axis=0) - new_states.prefix_states['layer_%d' % layer]['value'] = tf.concat( - [val, pad], axis=0) - - return bs_results, new_states - - random_seed = tf.random.uniform( - shape=[], maxval=(2**31 - 1), dtype=tf.int32, seed=p.random_seed) - sample = self.target_sequence_sampler.Sample( - self.theta, encoder_outputs, random_seed, InitCallback, - PreBeamSearchCallback, self._PostBeamSearchStepCallback) - bs = tf.shape(sample.ids)[0] - # Only need to make sure topk_hyps has the right shape - # [bs, num_hyps_per_beam], where num_hyps_per_beam=1 for sampling. - # TODO(yuancao): Support sampling multiple sequences and remove - # num_hyps_per_beam constraint. - assert self.params.beam_search.num_hyps_per_beam == 1 - sample.topk_hyps = tf.zeros([bs, 1], dtype=tf.string) - sample.topk_ids = sample.ids - weights = 1 - sample.paddings - sample.topk_lens = tf.cast(tf.reduce_sum(weights, axis=1), dtype=tf.int32) - sample.topk_scores = tf.reduce_sum( - tf.math.log(tf.reduce_max(tf.nn.softmax(sample.logits), axis=2)) * - weights, - axis=1) - return sample - - def _InitBeamSearchStateCallback(self, theta, encoder_outputs, - num_hyps_per_beam): - """Returns initial beams search states. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: a NestedMap computed by encoder. - num_hyps_per_beam: An int, number hyps to keep for source sentence. - Returns: - A tuple (initial_results, states). - initial_results: a `.NestedMap` of initial results. - atten_probs: - The initial attention probs, of shape [tgt_batch, src_len]. - states: a `.NestedMap` of initial model states. - source_encs: - A tensor of shape [src_batch, src_len, source_dim]. - source_paddings: - A tensor of shape [src_batch, src_len]. - target_ids: - Initial empty list of decoded ids. [num_hyps, 0]. - - """ - p = self.params - - source_encs = encoder_outputs.encoded - num_hyps = py_utils.GetShape(source_encs)[1] * num_hyps_per_beam - source_len = py_utils.GetShape(source_encs)[0] - - # Dummy attention probs - atten_probs = tf.ones([num_hyps, source_len]) / tf.cast( - source_len, tf.float32) - initial_results = py_utils.NestedMap( - log_probs=tf.zeros([num_hyps, p.softmax.num_classes], - dtype=py_utils.FPropDtype(p)), - atten_probs=atten_probs) - - if p.init_step_ids: - initial_results['step_ids'] = tf.expand_dims( - self._ExpandToNumHyps(encoder_outputs.init_step_ids, - num_hyps_per_beam), 1) - - batch_size = num_hyps - if isinstance(p.trans_tpl, list): - atten_hidden_dim = p.trans_tpl[0].tr_atten_tpl.atten_hidden_dim - assert [tpl.tr_atten_tpl.atten_hidden_dim for tpl in p.trans_tpl - ].count(atten_hidden_dim) == len( - p.trans_tpl), 'atten_hidden_dim must match' - else: - atten_hidden_dim = p.trans_tpl.tr_atten_tpl.atten_hidden_dim - - if not atten_hidden_dim: - atten_hidden_dim = p.model_dim - - if p.beam_search.name == 'tpu_beam_search': - seq_len = p.target_seq_len - else: - seq_len = 0 - - prefix_states = py_utils.NestedMap() - for layer in range(p.num_trans_layers): - prefix_states['layer_%d' % layer] = py_utils.NestedMap({ - 'key': - tf.zeros([seq_len, batch_size, atten_hidden_dim], - dtype=py_utils.FPropDtype(p)), - 'value': - tf.zeros([seq_len, batch_size, atten_hidden_dim], - dtype=py_utils.FPropDtype(p)), - }) - - return initial_results, py_utils.NestedMap({ - 'prefix_states': prefix_states, - 'time_step': tf.constant(0) - }) - - def _PreBeamSearchStepCallback(self, theta, encoder_outputs, step_ids, states, - num_hyps_per_beam): - """Returns logits for sampling ids and the next model states. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: a NestedMap computed by encoder. - step_ids: A tensor of shape [tgt_batch, 1]. - states: A `.NestedMap` of tensors representing states that the clients - would like to keep track of for each of the active hyps. - num_hyps_per_beam: Beam size. - Returns: - A tuple (results, out_states). - results: A `.NestedMap` of beam search results. - atten_probs: - The updated attention probs, of shape [tgt_batch, src_len]. - log_probs: - Log prob for each of the tokens in the target vocab. This is of - shape [tgt_batch, vocab_size]. - out_states: A `.NestedMap`. The updated states. - source_encs: - A tensor of shape [src_batch, src_len, source_dim]. - source_paddings: - A tensor of shape [src_batch, src_len]. - target_ids: - Updated list of decoded ids. [num_hyps, Num of decoded ids]. - - """ - p = self.params - - target_time = states.time_step - prefix_states = states.prefix_states - - new_states = states.Pack(states.Flatten()) - - layer_out, updated_prefix_states, atten_probs = self.ExtendStep( - theta, encoder_outputs, tf.squeeze(step_ids, 1), target_time, - prefix_states) - - new_states.prefix_states = updated_prefix_states - new_states.time_step = target_time + 1 - - softmax_input = tf.reshape(layer_out, [-1, p.softmax.input_dim]) - logits = self.softmax.Logits(theta.softmax, [softmax_input]) - - num_hyps = py_utils.GetShape(step_ids)[0] - # [time * batch, num_classes] -> [time, batch, num_classes] - logits = tf.reshape(logits, (-1, num_hyps, p.softmax.num_classes)) - # [time, batch, num_classes] -> [batch, time, num_classes] - logits = tf.transpose(logits, (1, 0, 2)) - - # Only return logits for the last ids - log_probs = tf.nn.log_softmax(tf.squeeze(logits, axis=1)) - - bs_results = py_utils.NestedMap({ - 'atten_probs': atten_probs, - 'log_probs': log_probs, - }) - - return bs_results, new_states - - def _PostBeamSearchStepCallback(self, theta, encoder_outputs, new_step_ids, - states): - # There is nothing to do here. - return states - - def _AddAttenProbsScalarSummary(self, source_paddings, targets, atten_probs): - """Add scalar summary of multi-headed transformer attention probs. - - This summary is primarily used to show statistics of the multi-headed - attention that reveals potential sparsity related properties. The - multi-headed attention probability tensors are exposed by - `MultiHeadedAttention.ComputeContextVectorWithSource` with the name - `multi_headed_atten_prob`. The following statistics are summarized: - - - 1_v_2: margin of the largest value vs. the 2nd largest - - 1_v_3: similar, but vs the 3rd largest - - mean: mean of the attention probs. NOTE: the sequences in a mini-batch - are not always of the same length. The attention probability for the - padded time index in target sequences are removed. However, the padding - for the source sequences are left unchanged. As a result, the atten - probs vectors will have some extra zero entries, so the mean calculated - here will be smaller than the true mean. - - source_padding_ratio: as explained above, the source paddings are not - handled when computing the mean. This summary show the average ratio - of time-steps that are padded values in the source sequences, to give - a reference of roughly how much the mean summarized above should be - adjusted. - - 1_v_mean: margin of the largest value vs the mean value. - - sum: the sum of the attention prob vectors. Should always be 1, for sanity - check only. - - The quantity above are computed for each sequence in the mini-batch, each - valid (target) sequence index, and each attention head, and then the - average value is reported to the tensorboard as a scalar summary. - - Args: - source_paddings: source padding, of shape [src_len, src_batch]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [tgt_batch, tgt_len]. - atten_probs: a list of attention probs, each element is of shape [tgt_len, - tgt_batch, src_len]. - """ - default_graph = tf.get_default_graph() - # looks like fprop/wmt14_en_de_transformer/tower_0_0/dec - name_scope = default_graph.get_name_scope() - # NOTE: shapes - # source_paddings: [src_len, src_batch] - # targets.paddings: [tgt_batch, tgt_len]. - source_time = tf.shape(source_paddings)[0] - source_batch = tf.shape(source_paddings)[1] - target_time = tf.shape(targets.paddings)[1] - target_batch = tf.shape(targets.paddings)[0] - num_heads = self.trans[0].self_atten.params.num_attention_heads - with tf.control_dependencies([tf.assert_equal(source_batch, target_batch)]): - target_batch = tf.identity(target_batch) - - source_padding_ratio = tf.cast( - tf.reduce_sum(source_paddings, axis=0), tf.float32) - source_padding_ratio /= tf.cast(tf.shape(source_paddings)[0], tf.float32) - summary_utils.scalar('source_padding_ratio', - tf.reduce_mean(source_padding_ratio)) - - for i in range(len(atten_probs)): - suffix = '_{}'.format(i) if i > 0 else '' - # Tensor exported from MultiHeadedAttention.ComputeContextVectorWithSource - # shape [target_time * batch_size, num_heads, source_time] - try: - mha_probs = default_graph.get_tensor_by_name( - name_scope + ('/aux_atten{}/MultiHeadedAttention/' - 'ComputeContextVectorWithSource/' - 'multi_headed_atten_prob:0').format(suffix)) - except KeyError: - # no such tensor found, stop here - return - - mha_probs = tf.reshape( - mha_probs, (target_time, target_batch, num_heads, source_time)) - - # remove time padding from target_time - # (tgt_t, batch, n_heads, src_t) => (n_valid, n_heads, src_t) - # explicit reshape is used here to give masks static ndims, otherwise - # tf.boolean_mask will fail - masks = tf.reshape( - tf.equal(targets.paddings, 0), (target_time, target_batch)) - mha_probs = tf.boolean_mask(mha_probs, masks) - - # note we did not remove invalid entries according to source_paddings, - # because the result will no longer be a rectangular tensor, just - # remember when interpreting some statistics like mean, there are some - # padded zero entries due to non-uniform sequence lengths - - # (n_valid, n_heads, src_t) => (n_valid*n_heads, src_t) - mha_probs = tf.reshape(mha_probs, (-1, tf.shape(mha_probs)[-1])) - - probs_top3, _ = tf.math.top_k(mha_probs, k=3) - probs_mean = tf.math.reduce_mean(mha_probs, axis=1) - probs_sum = tf.math.reduce_sum(mha_probs, axis=1) # sanity check - - margins_12 = tf.reduce_mean(probs_top3[:, 0] - probs_top3[:, 1]) - margins_13 = tf.reduce_mean(probs_top3[:, 0] - probs_top3[:, 2]) - margins_1m = tf.reduce_mean(probs_top3[:, 0] - probs_mean) - summary_utils.scalar('1_v_2/atten{}'.format(i), margins_12) - summary_utils.scalar('1_v_3/atten{}'.format(i), margins_13) - summary_utils.scalar('1_v_mean/atten{}'.format(i), margins_1m) - summary_utils.scalar('mean/atten{}'.format(i), tf.reduce_mean(probs_mean)) - summary_utils.scalar('sum/atten{}'.format(i), tf.reduce_mean(probs_sum)) - - def _AddAttenProbsSummary(self, source_paddings, targets, atten_probs): - """Add summary of attention probs. - - Args: - source_paddings: source padding, of shape [src_len, src_batch]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [tgt_batch, tgt_len]. - atten_probs: a list of attention probs, each element is of shape [tgt_len, - tgt_batch, src_len]. - """ - super()._AddAttenProbsSummary(source_paddings, targets, atten_probs) - if self.cluster.add_summary and self.params.add_multiheaded_attention_scalar_summary: - self._AddAttenProbsScalarSummary(source_paddings, targets, atten_probs) - - -class InsertionDecoder(base_decoder.BaseBeamSearchDecoder): - """Basic Insertion decoder for MT (or any symbol based sequence). - - References: - KERMIT: https://arxiv.org/pdf/1906.01604.pdf - Insertion Transformer: https://arxiv.org/pdf/1902.03249.pdf - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('token_emb', layers.EmbeddingLayer.Params(), - 'Token embedding layer params.') - p.Define('position_emb', layers.PositionalEmbeddingLayer.Params(), - 'Position embedding layer params.') - p.Define( - 'model_dim', 1024, 'Model dimension that applies to embedding ' - 'layers and all Transformer layers.') - p.Define('num_trans_layers', 6, 'Number of Transformer layers.') - p.Define('trans_tpl', layers_with_attention.TransformerLayer.Params(), - 'Transformer layer params.') - p.Define('softmax', layers.SimpleFullSoftmax.Params(), 'Softmax params.') - p.Define('input_dropout_prob', 0.0, 'Prob at which we do input dropout.') - - # Default config for the token embeddings. - p.token_emb.vocab_size = 32000 * 2 - p.token_emb.embedding_dim = p.model_dim - p.token_emb.max_num_shards = 16 - p.token_emb.params_init = py_utils.WeightInit.Gaussian( - 1.0 / math.sqrt(p.token_emb.embedding_dim)) - p.token_emb.scale_sqrt_depth = True - - # Default config for the position embeddings. - p.position_emb.embedding_dim = p.model_dim - - # Default config for the transformer layers. - p.trans_tpl.source_dim = p.model_dim - p.trans_tpl.tr_atten_tpl.source_dim = p.model_dim - p.trans_tpl.tr_atten_tpl.num_attention_heads = 8 - p.trans_tpl.tr_fflayer_tpl.input_dim = p.model_dim - p.trans_tpl.tr_fflayer_tpl.hidden_dim = 4096 - - # Default config for the softmax. - p.softmax.num_classes = 32000 - p.softmax.num_shards = 8 - - p.target_seq_len = 300 - - return p - - @classmethod - def UpdateTargetVocabSize(cls, p, vocab_size, wpm_model=None): - """Sets the vocab size in the params. - - Args: - p: model params. - vocab_size: size of the vocabulary. - wpm_model: file name prefix pointing to a wordpiece model. - - Returns: - Model params updated with the vocab size and wpm model. - """ - p.softmax.num_classes = vocab_size - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.token_emb.vocab_size % p.softmax.num_classes == 0 - assert p.token_emb.embedding_dim == p.position_emb.embedding_dim - assert p.token_emb.embedding_dim == p.model_dim - - self.CreateChild('token_emb', p.token_emb) - self.CreateChild('position_emb', p.position_emb) - - dropout_tpl = layers.DropoutLayer.Params() - dropout_tpl.keep_prob = (1.0 - p.input_dropout_prob) - self.CreateChild('input_dropout', dropout_tpl) - - params_trans_layers = [] - for i in range(p.num_trans_layers): - params = p.trans_tpl.Copy() - params.name = 'trans_layer_%d' % i - params.packed_input = p.packed_input - params.has_aux_atten = False - params.mask_self_atten = True - params_trans_layers.append(params) - self.CreateChildren('trans', params_trans_layers) - - p.softmax.input_dim = p.model_dim - self.CreateChild('softmax', p.softmax) - - def ComputePredictions(self, theta, encoder_outputs, targets): - """Compute 1-step of the insertion iteration. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: This should be None. - targets: A `.NestedMap`. - - ids: The target ids of shape [batch_size, time_dim]. - - paddings: The target paddings of shape [batch_size, time_dim]. - - Returns: - A `.NestedMap`. - - outputs: The contextualized output vectors of shape - [batch_size, time_dim, model_dim]. - """ - p = self.params - - # TODO(williamchan): Enable cross-attention. - assert encoder_outputs is None - - with tf.name_scope(p.name): - # [batch, time] - target_ids = targets.ids - # [time, batch] - target_paddings = tf.transpose(targets.paddings) - - # Embedding layer - # [batch, time, model_dim] - token_embs = self.token_emb.EmbLookup(theta.token_emb, target_ids) - target_time = py_utils.GetShape(target_ids)[1] - - # [1, time, model_dim] - posit_embs = tf.expand_dims( - self.position_emb.FProp(theta.position_emb, target_time), 0) - - # [time, batch, model_dim] - input_embs = token_embs + posit_embs - - input_embs = tf.transpose(input_embs, [1, 0, 2]) - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - - layer_in = input_embs - for layer, layer_theta in zip(self.trans, theta.trans): - # [time, batch, model_dim] - layer_out, _ = layer.FProp(layer_theta, layer_in, target_paddings) - layer_in = layer_out - - return py_utils.NestedMap(outputs=layer_out) - - def ComputeLoss(self, theta, predictions, targets): - # pyformat: disable - """Returns the insertion loss. - - Args: - theta: A `.NestedMap` object capturing decoder model parameters. - predictions: A `.NestedMap` describing the decoding process, requiring - .outputs: Tensor of shape [time, batch, params.softmax.input_dim]. - targets: A `.NestedMap`. - - - target_indices: A Tensor capturing the relevant insertion tokens to - tf.gather_nd the log-probs. - - - target_weights: A Tensor capturing the relevant insertion tokens' - weights. - - Returns: - Two dicts. - - A map from metric name (a python string) to a tuple (value, weight). - Both value and weight are scalar Tensors. - - A map from name to arbitrary tensors, where the first dimension must - be the batch index. - """ - # pyformat: enable - p = self.params - - batch_size = py_utils.GetShape(predictions.outputs)[0] - - state = tf.reshape(predictions.outputs, [-1, p.softmax.input_dim]) - logits = self.softmax.Logits(theta.softmax, state) - logits = tf.reshape( - logits, - tf.concat([ - py_utils.GetShape(predictions.outputs)[:-1], - [p.softmax.num_classes] - ], 0)) - log_probs = tf.nn.log_softmax(logits) - - # `target_indices` are in the form [batch, time, vocab], where as `logits` - # are in the form [time, batch, vocab]. We need to swap the columns. - target_indices = tf.concat([ - predictions.tgt.target_indices[:, 1:2], - predictions.tgt.target_indices[:, 0:1], - predictions.tgt.target_indices[:, 2:3], - ], 1) - - loss = tf.reduce_sum( - tf.gather_nd(log_probs, target_indices) * - predictions.tgt.target_weights) - loss_weight = tf.cast(batch_size, tf.float32) - - return ({ - 'loss': (loss, loss_weight) - }, { - 'log_probs': log_probs, - 'logits': logits - }) - - -class TransformerBatchMajorDecoder(MTBaseDecoder): - """Transformer decoder with batch major implementation. - - Implements the decoder of Transformer model: - https://arxiv.org/abs/1706.03762. - """ - - @classmethod - def Params(cls): - p = super().Params() - p.Define('token_emb', layers.EmbeddingLayer.Params(), - 'Token embedding layer params.') - p.Define('shared_emb', None, 'Embedding shared with softmax.') - p.Define('position_emb', layers.PositionalEmbeddingLayer.Params(), - 'Position embedding layer params.') - p.Define('source_dim', 1024, 'Dimension of encoder outputs.') - p.Define( - 'model_dim', 1024, 'Model dimension that applies to embedding ' - 'layers and all Transformer layers.') - p.Define('num_trans_layers', 6, 'Number of Transformer layers.') - p.Define('trans_decoder_tpl', - batch_major_attention.TransformerDecoderLayer.Params(), - 'Transformer layer params.') - p.Define('input_dropout_prob', 0.0, 'Prob at which we do input dropout.') - p.Define('input_dropout_tpl', layers.DropoutLayer.Params(), - 'Input dropout layer params.') - p.Define('final_layer_norm', False, - 'Whether or not to apply layer norm after transformer stack.') - p.Define('use_fused_layernorm', False, 'Whether to use fused layernorm.') - p.Define('use_fast_softmax', False, - 'Whether or not to use a faster softmax with label smoothing.') - p.Define( - 'input_data_format', 'TBC', 'The data format of input features: ' - 'TBC for [time, batch, feature_dim], ' - 'BTC for [batch, time, feature_dim].') - p.Define( - 'prediction_data_format', 'TBC', - 'The data format of predictions and per-example losses: ' - 'TBC for [time, batch, ...], ' - 'BTC for [batch, time, ...].') - - # Default config for the token embedding. - p.token_emb.vocab_size = 32000 - p.token_emb.embedding_dim = p.model_dim - p.token_emb.max_num_shards = 16 - p.token_emb.params_init = py_utils.WeightInit.Gaussian( - 1.0 / math.sqrt(p.token_emb.embedding_dim)) - p.token_emb.scale_sqrt_depth = True - - # Default config for the position embedding. - p.position_emb.embedding_dim = p.model_dim - - # Default config for the transformer decoder layers. - p.trans_decoder_tpl.input_dim = p.model_dim - p.trans_decoder_tpl.tr_atten_tpl.input_dim = p.model_dim - p.trans_decoder_tpl.tr_atten_tpl.num_heads = 8 - p.trans_decoder_tpl.tr_fflayer_tpl.input_dim = p.model_dim - p.trans_decoder_tpl.tr_fflayer_tpl.hidden_dim = 2048 - - # Default config for beam search. - p.target_seq_len = 300 - p.beam_search.length_normalization = 0.5 - p.beam_search.coverage_penalty = 0.0 - p.beam_search.batch_major_state = False - p.beam_search.batch_major_compute = True - p.beam_search.short_seq_limit = 40 - - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - if p.shared_emb: - self.CreateChild('softmax', p.shared_emb) - - if not p.shared_emb: - self.CreateChild('token_emb', p.token_emb) - self.CreateChild('position_emb', p.position_emb) - - dropout_tpl = p.input_dropout_tpl.Copy() - dropout_tpl.keep_prob = (1.0 - p.input_dropout_prob) - self.CreateChild('input_dropout', dropout_tpl) - - params_trans_layers = [] - for i in range(p.num_trans_layers): - params = p.trans_decoder_tpl.Copy() - params.name = 'decoder_trans_layer_%d' % i - params_trans_layers.append(params) - self.CreateChildren('decoder_trans', params_trans_layers) - - p.softmax.input_dim = p.model_dim - if not p.shared_emb: - self.CreateChild('softmax', p.softmax) - - if p.final_layer_norm: - layer_norm_p = layers.LayerNorm.Params().Set( - name='final_ln', - input_dim=p.model_dim, - use_fused_layernorm=p.use_fused_layernorm, - fprop_dtype=p.input_dropout_tpl.fprop_dtype) - self.CreateChild('final_ln', layer_norm_p) - - def _CreateChildrenVariables(self): - if self.params.shared_emb: - with tf.variable_scope('shared_emb', reuse=tf.AUTO_REUSE): - self.softmax.InstantiateVariables() - super()._CreateChildrenVariables() - - def _MaybeTransposeEncoderOutputs(self, encoder_outputs, target_data_format): - p = self.params - if p.input_data_format == target_data_format: - return encoder_outputs - transposed = py_utils.NestedMap( - encoded=tf.transpose(encoder_outputs.encoded, [1, 0, 2]), - padding=tf.transpose(encoder_outputs.padding)) - if getattr(encoder_outputs, 'segment_id', None) is None: - transposed.segment_id = None - else: - transposed.segment_id = tf.transpose(encoder_outputs.segment_id) - return transposed - - def _MaybeTransposeTargets(self, targets): - p = self.params - if p.prediction_data_format == 'BTC': - return targets - transposed = py_utils.NestedMap() - for k, v in targets.items(): - if v is not None: - with tf.name_scope('transpose_%s' % k): - v = tf.transpose(py_utils.HasShape(v, [-1, -1])) - transposed[k] = v - return transposed - - def _FProp(self, theta, encoder_outputs, targets): - """Decodes `targets` given encoded source. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: A '.NestedMap' object computed by encoder. * encoded - - Source encoding of shape [source_time, source_batch, dim] or - [source_batch, source_time, dim], depending on p.input_data_format. * - paddings - Source encoding's padding of shape [source_time, - source_batch] or [source_batch, source_time]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [batch, target_time]. - - Returns: - softmax_input: Tensor of shape [target_time, batch, dim]. - """ - p = self.params - # [batch, source_time, dim] - encoder_out_bm = self._MaybeTransposeEncoderOutputs(encoder_outputs, 'BTC') - aux_vec = encoder_out_bm.encoded - aux_paddings = encoder_out_bm.padding - aux_segment_id = getattr(encoder_out_bm, 'segment_id', None) - - with tf.name_scope(p.name): - # [batch, target_time] - target_ids = targets.ids - target_paddings = targets.paddings - target_time = py_utils.GetShape(target_ids)[1] - target_segment_pos = None - target_segment_id = None - if p.packed_input: - target_segment_id = targets.segment_ids - target_segment_pos = targets.segment_pos - assert aux_segment_id is not None, ('Need to provide aux_segment_id ' - 'for packed input.') - - # Embedding layer - # [batch, target_time, dim] - if not p.shared_emb: - token_embs = self.token_emb.EmbLookup(theta.token_emb, target_ids) - else: - token_embs = self.softmax.EmbLookup(theta.softmax, target_ids) - # [1, target_time, dim] - if p.packed_input: - posit_embs = self.position_emb.FPropWithPosition( - theta.position_emb, target_segment_pos) - else: - posit_embs = tf.expand_dims( - self.position_emb.FProp(theta.position_emb, target_time), 0) - # [batch, target_time, dim] - input_embs = token_embs + posit_embs - - if p.input_dropout_tpl.fprop_dtype: - input_embs = tf.cast(input_embs, p.input_dropout_tpl.fprop_dtype) - target_paddings = tf.cast(target_paddings, - p.input_dropout_tpl.fprop_dtype) - - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - layer_in = input_embs - # Explicitly set the input shape of Transformer layers, to avoid - # unknown shape error occurred to tf.einsum on nonTPU devices. - batch, _, dim = py_utils.GetShape(aux_vec, 3) - layer_in = tf.reshape(layer_in, [batch, target_time, dim]) - if p.packed_input: - segment_mask = batch_major_attention.SegmentMask( - target_segment_id, target_segment_id, dtype=layer_in.dtype) - causal_padding = tf.expand_dims( - tf.tile( - tf.expand_dims( - batch_major_attention.CausalPadding( - target_time, dtype=layer_in.dtype), 0), [batch, 1, 1]), - 1) - causal_mask = causal_padding * segment_mask.dtype.max * tf.constant( - -0.7, dtype=segment_mask.dtype) - segment_mask += causal_mask - aux_segment_mask = batch_major_attention.SegmentMask( - target_segment_id, aux_segment_id, dtype=layer_in.dtype) - for layer, layer_theta in zip(self.decoder_trans, theta.decoder_trans): - # [batch, target_time, dim] - layer_out, _ = layer.FProp( - layer_theta, - layer_in, - target_paddings, - aux_vec, - aux_paddings, - segment_mask=segment_mask if p.packed_input else None, - aux_segment_mask=aux_segment_mask if p.packed_input else None) - layer_in = layer_out - - if p.final_layer_norm: - layer_out = self.final_ln.FProp(theta.final_ln, layer_out) - if p.prediction_data_format == 'TBC': - # Transpose the softmax_input to match the input requirement of - # ComputePredictions. - layer_out = tf.transpose(layer_out, [1, 0, 2]) - return layer_out - - def ExtendStep(self, - theta, - encoder_outputs, - new_ids, - time_step, - prefix_states, - use_short_seq_opt=False): - """Extend prefix as represented by `prefix_states` by one more step. - - This function is expected to be called during fast decoding of Transformer - models. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: A '.NestedMap' object computed by encoder. * encoded - - Source encoding of shape [source_time, source_batch, dim] or - [source_batch, source_time, dim], depending on p.input_data_format. * - paddings - Source encoding's padding of shape [source_time, - source_batch] or [source_batch, source_time]. - new_ids: New input ids, of shape [target_batch, 1]. - time_step: A scalar, the current decode step, 0-based. - prefix_states: A `.NestedMap` representing the previous decoded states. - key - [target_time, target_batch, num_heads, dim_per_head]. value - - [target_time, target_batch, num_heads, dim_per_head]. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - last_decoder_out: The last decoder layer of shape [target_batch, dim]. - updated_prefix_states: A `.NestedMap` representing the updated states. - key - [target_time, target_batch, num_heads, dim_per_head]. - value - [target_time, target_batch, num_heads, dim_per_head]. - """ - p = self.params - encoder_out_bm = self._MaybeTransposeEncoderOutputs(encoder_outputs, 'BTC') - # [source_batch, source_time, dim] - aux_vec = encoder_out_bm.encoded - # [source_batch, source_time] - aux_paddings = encoder_out_bm.padding - - with tf.name_scope(p.name): - # Embedding layer - # [target_batch, 1, dim] - if not p.shared_emb: - token_embs = self.token_emb.EmbLookup(theta.token_emb, new_ids) - else: - token_embs = self.softmax.EmbLookup(theta.softmax, new_ids) - # [1, 1, dim] - if isinstance(time_step, tf.Tensor): - time_step_t = tf.reshape(time_step, [1, 1]) - elif isinstance(time_step, int): - time_step_t = tf.constant([[time_step]], dtype=tf.int32) - else: - raise ValueError('Unexpected input type `%s` for `time_step`.' % - type(time_step)) - posit_embs = self.position_emb.FPropWithPosition(theta.position_emb, - time_step_t) - # [target_batch, 1, dim] - input_embs = token_embs + posit_embs - - if p.input_dropout_tpl.fprop_dtype: - input_embs = tf.cast(input_embs, p.input_dropout_tpl.fprop_dtype) - - # Make a copy of the input. - updated_prefix_states = prefix_states.DeepCopy() - - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - layer_in = input_embs - for i, (layer, layer_theta) in enumerate( - zip(self.decoder_trans, theta.decoder_trans)): - # [target_batch, 1, dim] - layer_out, updated_states = layer.ExtendStep( - layer_theta, layer_in, aux_vec, aux_paddings, - prefix_states['layer_%i' % i], time_step, use_short_seq_opt) - updated_prefix_states['layer_%i' % i] = updated_states - layer_in = layer_out - - # [target_batch, dim] - last_decoder_out = tf.squeeze(layer_out, 1) - if p.final_layer_norm: - last_decoder_out = self.final_ln.FProp(theta.final_ln, last_decoder_out) - return last_decoder_out, updated_prefix_states - - def ComputePredictions(self, theta, encoder_outputs, targets): - """Decodes `targets` given encoded source. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: A '.NestedMap' object computed by encoder. * encoded - - Source encoding of shape [source_time, source_batch, dim] or - [source_batch, source_time, dim], depending on p.input_data_format. * - paddings - Source encoding's padding of shape [source_time, - source_batch] or [source_batch, source_time]. - targets: A dict of string to tensors representing the targets one try to - predict. Each tensor in targets is of shape [batch, target_time]. - - Returns: - Output of the last decoder layer, of shape [target_time, batch, dim]. - """ - return self._FProp(theta, encoder_outputs, targets) - - def _FPropFastSoftmax(self, - theta, - softmax_input, - target_labels, - target_weights, - time_axis=0): - """Computes cross-entropy loss with label smoothing. - - As compared to the _FPropSoftmax, this version is faster by removing the - data formatting overheads and bias of the linear projection. A normalizing - factor is also added to the xentropy result be better model quality. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - softmax_input: A tensor of shape [time, batch, p.softmax.input_dim]. - target_labels: A matrix of tf.int32. [time, batch]. - target_weights: A matrix of params.dtype. [time, batch]. - time_axis: If 0, the inputs are time-major: [time, batch, ...]; if 1, the - inputs are batch-major: [batch, time, ...]. - - Returns: - A tuple (metrics, per_example_tensors). - metrics: - A dictionary containing metrics for the xent loss and prediction - accuracy. - per_example_tensors: - A dictionary of per-example tensors. - """ - p = self.params - assert p.label_smoothing is not None - assert p.per_word_avg_loss - - softmax_input = tf.reshape(softmax_input, [-1, p.softmax.input_dim]) - - logits = self.softmax.SimpleLogits(theta.softmax, softmax_input) - logits = tf.cast(logits, tf.float32) - - high_confidence = 1.0 - p.label_smoothing.uncertainty - low_confidence = p.label_smoothing.uncertainty / tf.cast( - p.label_smoothing.num_classes - 1, tf.float32) - normalizing = -( - high_confidence * tf.math.log(high_confidence) + - tf.cast(p.softmax.num_classes - 1, tf.float32) * low_confidence * - tf.math.log(low_confidence + 1e-20)) - - target_labels = tf.reshape(target_labels, [-1]) - soft_targets = tf.one_hot( - tf.cast(target_labels, tf.int32), - depth=p.softmax.num_classes, - on_value=high_confidence, - off_value=low_confidence) - - xentropy = tf.nn.softmax_cross_entropy_with_logits( - logits=logits, labels=soft_targets) - xent = xentropy - normalizing - - target_weights_shape = py_utils.GetShape(target_weights) - orig_target_weights = target_weights - target_weights = tf.cast(tf.reshape(target_weights, [-1]), xent.dtype) - total_xent = tf.reduce_sum(xent * target_weights) - total_weights = tf.reduce_sum(target_weights) - - final_loss = total_xent / total_weights - loss_weight = total_weights - - metrics = { - 'loss': (final_loss, loss_weight), - 'log_pplx': (final_loss, loss_weight), - } - - per_example_tensors = {} - if p.per_example_tensors: - per_example_tensors['per_example_loss'] = tf.reshape( - xent, target_weights_shape) - per_example_tensors['per_sequence_loss'] = tf.reduce_sum( - per_example_tensors['per_example_loss'] * orig_target_weights, - axis=time_axis) - per_example_tensors['loss'] = per_example_tensors['per_sequence_loss'] - per_example_tensors['logits'] = tf.reshape( - logits, tf.concat([target_weights_shape, [-1]], 0)) - per_example_tensors['log_probs'] = tf.reshape( - tf.nn.log_softmax(logits), tf.concat([target_weights_shape, [-1]], 0)) - - # NOTE: tf.argmax is not implemented for the JF backend, see b/36093673 - # Skip the fraction_of_correct_next_step_preds during training. - if self.do_eval: - correct_preds = tf.cast( - tf.equal( - tf.cast(tf.reshape(tf.argmax(logits, 1), [-1]), tf.int32), - tf.reshape(target_labels, [-1])), p.dtype) - correct_next_preds = tf.reduce_sum( - correct_preds * tf.reshape(tf.cast(target_weights, p.dtype), [-1])) - num_preds = tf.reduce_sum(tf.cast(target_weights, p.dtype)) - accuracy = tf.identity( - correct_next_preds / num_preds, - name='fraction_of_correct_next_step_preds') - metrics['fraction_of_correct_next_step_preds'] = (accuracy, num_preds) - return metrics, per_example_tensors - - def ComputeLoss(self, theta, predictions, targets): - """Populates a metrics dictionary based on the output of ComputePredictions. - - Args: - theta: Nested map describing decoder model parameters. - predictions: NestedMap describing the decoding process, requiring: - .softmax_input: Tensor of shape [time, batch, params.softmax.input_dim]. - targets: NestedMap describing the target sequences. - - Returns: - Two dicts. - - - A map from metric name (a python string) to a tuple (value, weight). - Both value and weight are scalar Tensors. - - A map from name to arbitrary tensors, where the first dimension must - be the batch index. - """ - p = self.params - targets = self._MaybeTransposeTargets(targets) - if isinstance(predictions, py_utils.NestedMap): - predictions = predictions.softmax_input - time_axis = {'TBC': 0, 'BTC': 1}.get(p.prediction_data_format) - if p.use_fast_softmax: - return self._FPropFastSoftmax( - theta, - predictions, - targets.labels, - targets.weights, - time_axis=time_axis) - else: - return self._FPropSoftmax( - theta, - predictions, - targets.labels, - targets.weights, - targets.paddings, - targets.get('segment_ids', None), - time_axis=time_axis) - - def _InitBeamSearchStateCallback(self, theta, encoder_outputs, - num_hyps_per_beam): - """Returns initial beams search states. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: A '.NestedMap' object computed by encoder. * encoded - - Source encoding of shape [source_time, source_batch, dim] or - [source_batch, source_time, dim], depending on p.input_data_format. * - paddings - Source encoding's padding of shape [source_time, - source_batch] or [source_batch, source_time]. - num_hyps_per_beam: An int, number hyps to keep for source sentence. - - Returns: - initial_results: A `.NestedMap` of initial beam search results. - log_probs - Log prob for each of the tokens in the target vocab, - of shape [target_batch, vocab_size]. - atten_probs - The updated attention probs, of shape - [target_batch, source_time]. - states: A `.NestedMap` of initial model states. - prefix_states - A `.NestedMap` representing the empty decoded states. - key - [target_time, target_batch, num_heads, dim_per_head]. - value - [target_time, target_batch, num_heads, dim_per_head]. - time_step - A scalar, the initial decode step (0). - """ - p = self.params - - # [source_batch, source_time, dim] - encoder_out_bm = self._MaybeTransposeEncoderOutputs(encoder_outputs, 'BTC') - aux_vec = encoder_out_bm.encoded - target_batch = py_utils.GetShape(aux_vec)[0] * num_hyps_per_beam - source_time = py_utils.GetShape(aux_vec)[1] - target_time = p.target_seq_len - - log_probs = tf.zeros([target_batch, p.softmax.num_classes], - dtype=py_utils.FPropDtype(p)) - # Dummy attention probs - atten_probs = ( - tf.ones([target_batch, source_time], dtype=py_utils.FPropDtype(p)) / - tf.cast(source_time, py_utils.FPropDtype(p))) - initial_results = py_utils.NestedMap( - log_probs=log_probs, atten_probs=atten_probs) - - prefix_states = py_utils.NestedMap() - for layer in range(p.num_trans_layers): - prefix_states['layer_%d' % layer] = self.decoder_trans[layer].InitStates( - theta.decoder_trans[layer], target_batch, target_time) - - return initial_results, py_utils.NestedMap({ - 'prefix_states': prefix_states, - 'time_step': tf.constant(0) - }) - - def _PreBeamSearchStepCallback(self, - theta, - encoder_outputs, - new_ids, - states, - num_hyps_per_beam, - use_short_seq_opt=False): - """Returns logits for sampling ids and the next model states. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - encoder_outputs: A '.NestedMap' object computed by encoder. * encoded - - Source encoding of shape [source_time, source_batch, dim] or - [source_batch, source_time, dim], depending on p.input_data_format. * - paddings - Source encoding's padding of shape [source_time, - source_batch] or [source_batch, source_time]. - new_ids: A tensor of shape [target_batch, 1]. - states: A `.NestedMap` of tensors representing states that the clients - would like to keep track of for each of the active hyps. prefix_states - - A `.NestedMap` representing the previous decoded states. key - - [target_time, target_batch, num_heads, dim_per_head]. value - - [target_time, target_batch, num_heads, dim_per_head]. time_step - A - scalar, the current decode step, 0-based. - num_hyps_per_beam: A scalar, beam size. - use_short_seq_opt: A bool, whether using short sequence optimization. - - Returns: - bs_results: A `.NestedMap` of beam search results. - log_probs - Log prob for each of the tokens in the target vocab, - of shape [target_batch, vocab_size]. - atten_probs - The updated attention probs, of shape - [target_batch, source_time]. - new_states: A `.NestedMap` object. The updated states. - prefix_states - A `.NestedMap` representing the updated decoded states. - key - [target_time, target_batch, num_heads, dim_per_head]. - value - [target_time, target_batch, num_heads, dim_per_head]. - time_step - A scalar, the current decode step, 0-based. - """ - p = self.params - # [source_batch, source_time, dim] - encoder_out_bm = self._MaybeTransposeEncoderOutputs(encoder_outputs, 'BTC') - - target_batch = py_utils.GetShape(new_ids)[0] - source_batch = target_batch // num_hyps_per_beam - - new_states = states.Pack(states.Flatten()) - time_step = states.time_step - prefix_states = states.prefix_states - - # The inputs are ordered as num_hyps_per_beam by num_beams, - # which needs to be transposed for the layer computation. - # [num_hyps_per_beam, source_batch, 1] - new_ids = tf.reshape(new_ids, [num_hyps_per_beam, source_batch, 1]) - # [source_batch, num_hyps_per_beam, 1] - new_ids = tf.transpose(new_ids, [1, 0, 2]) - # [source_batch * num_hyps_per_beam, 1] - new_ids = tf.reshape(new_ids, [-1, 1]) - - softmax_input, updated_prefix_states = self.ExtendStep( - theta, encoder_outputs, new_ids, time_step, prefix_states, - use_short_seq_opt) - - # Transpose the outputs as num_beams by num_hyps_per_beam to match the - # beam search requirement. - # [source_batch, num_hyps_per_beam, dim] - softmax_input = tf.reshape(softmax_input, - [source_batch, num_hyps_per_beam, -1]) - # [num_hyps_per_beam, source_batch, dim] - softmax_input = tf.transpose(softmax_input, [1, 0, 2]) - # [num_hyps_per_beam * source_batch, dim] - softmax_input = tf.reshape(softmax_input, [target_batch, -1]) - - # [target_batch, vocab_size] - logits = self.softmax.Logits(theta.softmax, [softmax_input]) - - # Only return logits for the last ids - log_probs = tf.nn.log_softmax(logits) - - # Dummy attention probs - source_time = py_utils.GetShape(encoder_out_bm.padding)[1] - atten_probs = ( - tf.ones([target_batch, source_time], dtype=py_utils.FPropDtype(p)) / - tf.cast(source_time, py_utils.FPropDtype(p))) - - bs_results = py_utils.NestedMap({ - 'log_probs': log_probs, - 'atten_probs': atten_probs, - }) - - new_states.prefix_states = updated_prefix_states - new_states.time_step = time_step + 1 - - return bs_results, new_states - - def _PostBeamSearchStepCallback(self, theta, encoder_outputs, new_step_ids, - states): - # There is nothing to do here. - return states diff --git a/lingvo/tasks/mt/decoder_test.py b/lingvo/tasks/mt/decoder_test.py deleted file mode 100644 index 3b239df6..00000000 --- a/lingvo/tasks/mt/decoder_test.py +++ /dev/null @@ -1,1374 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for mt.decoder.""" - -from absl.testing import parameterized -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import input_generator_helper as ig_helper -from lingvo.core import layers -from lingvo.core import layers_with_attention -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.core.ops.hyps_pb2 import Hypothesis -from lingvo.core.test_utils import CompareToGoldenSingleFloat -from lingvo.tasks.mt import decoder -import numpy as np - -FLAGS = tf.flags.FLAGS - -_NUMPY_RANDOM_SEED = 9885784 -_TF_RANDOM_SEED = 8372749040 - - -class DecoderTestCaseBase(test_utils.TestCase): - - def _Inputs(self, dtype=tf.float32, init_step_ids=False): - np.random.seed(_NUMPY_RANDOM_SEED) - src_seq_len = 5 - # batch = 2 - src_enc = tf.constant( - np.random.normal(size=[src_seq_len, 2, 4]), dtype=dtype) - src_enc_padding = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]], - dtype=dtype) - # batch = 4, time = 3. - target_ids = tf.transpose( - tf.constant([[0, 1, 2, 3], [0, 5, 6, 7], [0, 10, 11, 12]], - dtype=tf.int32)) - target_labels = tf.transpose( - tf.constant([[1, 2, 3, 4], [5, 6, 7, 8], [10, 11, 12, 13]], - dtype=tf.int32)) - target_paddings = tf.transpose( - tf.constant([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0]], dtype=dtype)) - target_weights = 1.0 - target_paddings - targets = py_utils.NestedMap({ - 'ids': target_ids, - 'labels': target_labels, - 'weights': target_weights, - 'paddings': target_paddings - }) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_enc_padding, segment_id=None) - if init_step_ids: - tgt_prefix = tf.constant(np.random.randint(4, size=[2]), dtype=tf.int32) - encoder_outputs['init_step_ids'] = tgt_prefix - return encoder_outputs, targets - - def _DecoderParams( - self, - per_word_avg_loss=False, - dtype=tf.float32, - fprop_dtype=None, - decoder_cls=decoder.MTDecoderV1, - ): - p = decoder_cls.Params() - p.name = 'decoder' - p.source_dim = 4 - p.emb.vocab_size = 16 - p.emb.embedding_dim = 4 - p.emb.max_num_shards = 1 - p.rnn_cell_dim = 4 - p.rnn_layers = 3 - p.attention.hidden_dim = 2 - p.softmax.num_classes = 16 - p.softmax.num_shards = 1 - p.per_word_avg_loss = per_word_avg_loss - p.dtype = dtype - p.target_seq_len = 5 - p.random_seed = 12345 - p.emb.params_init = py_utils.WeightInit.Uniform(0.04, 12345) - p.atten_rnn_cell_tpl.params_init = py_utils.WeightInit.Uniform(0.04, 12345) - p.rnn_cell_tpl.params_init = py_utils.WeightInit.Uniform(0.04, 12345) - p.softmax.params_init = py_utils.WeightInit.Uniform(0.04, 123) - - for lp in base_layer.RecursiveFindLayerParams(p): - lp.dtype = dtype - - if fprop_dtype: - py_utils.UpdateFpropDtype(p, fprop_dtype) - - return p - - def _DecoderFPropHelper(self, - decoder_cls, - dtype, - fprop_dtype, - feed_att_context_to_softmax, - expected_loss, - per_example_tensors=False): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams( - dtype=dtype, fprop_dtype=fprop_dtype, decoder_cls=decoder_cls) - p.per_example_tensors = per_example_tensors - - p.feed_attention_context_vec_to_softmax = feed_att_context_to_softmax - dec = p.Instantiate() - encoder_outputs, targets = self._Inputs(dtype=fprop_dtype) - fprop_out = dec.FPropDefaultTheta(encoder_outputs, targets) - loss = fprop_out.metrics['loss'][0] - - self.evaluate(tf.global_variables_initializer()) - actual_loss = loss.eval() - print('actual loss = ', actual_loss) - CompareToGoldenSingleFloat(self, expected_loss, actual_loss) - if per_example_tensors: - per_example = fprop_out.per_sequence - self.assertIn('loss', per_example) - self.assertAllEqual(per_example['loss'].shape.as_list(), [4]) - - def _DecoderGradientCheckerHelper(self, - decoder_cls, - feed_att_context_to_softmax=False): - with self.session(use_gpu=True, graph=tf.Graph()) as sess: - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=tf.float64, decoder_cls=decoder_cls) - p.feed_attention_context_vec_to_softmax = feed_att_context_to_softmax - dec = p.Instantiate() - encoder_outputs, targets = self._Inputs(dtype=tf.float64) - loss, _ = dec.FPropDefaultTheta(encoder_outputs, targets).metrics['loss'] - all_vars = tf.trainable_variables() - grads = tf.gradients(loss, all_vars) - print('num of vars ', len(all_vars)) - - def DenseGrad(var, grad): - if isinstance(grad, tf.Tensor): - return grad - elif isinstance(grad, tf.IndexedSlices): - return tf.math.unsorted_segment_sum(grad.values, grad.indices, - tf.shape(var)[0]) - - grads = [DenseGrad(x, y) for x, y in zip(all_vars, grads)] - - self.evaluate(tf.global_variables_initializer()) - symbolic_grads = [gd.eval() for gd in grads] - numerical_grads = [] - for v in all_vars: - numerical_grads.append( - test_utils.ComputeNumericGradient(sess, loss, v, delta=1e-5)) - - rets = {} - for v, x, y in zip(all_vars, symbolic_grads, numerical_grads): - print('symbolic_grads, numerical_grads :', v.name) - print(x) - print(y) - self.assertAllClose(x, y) - rets[v.name] = x - - return rets - - def _DecoderPerWordAvgLossFPropHelper(self, - decoder_cls, - feed_att_context_to_softmax=False): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(True, decoder_cls=decoder_cls) - p.feed_attention_context_vec_to_softmax = feed_att_context_to_softmax - dec = p.Instantiate() - encoder_outputs, targets = self._Inputs() - loss, _ = dec.FPropDefaultTheta(encoder_outputs, targets).metrics['loss'] - self.evaluate(tf.global_variables_initializer()) - actual_loss = loss.eval() - print('actual loss = ', actual_loss) - if p.feed_attention_context_vec_to_softmax: - CompareToGoldenSingleFloat(self, 2.768977, actual_loss) - else: - CompareToGoldenSingleFloat(self, 2.772613, actual_loss) - - -class DecoderTest(DecoderTestCaseBase, parameterized.TestCase): - - def testDecoderConstruction(self): - p = self._DecoderParams() - _ = decoder.MTDecoderV1(p) - - def testDecoderFPropFunctional(self): - self._DecoderFPropHelper(decoder.MTDecoderV1, tf.float64, tf.float64, False, - 7.624605) - - def testDecoderFPropFunctionalFloat64Dtype(self): - self._DecoderFPropHelper(decoder.MTDecoderV1, tf.float32, tf.float64, False, - 7.624684) - - def testDecoderFPropFunctionalFloat64FpropDtype(self): - self._DecoderFPropHelper(decoder.MTDecoderV1, tf.float64, tf.float32, False, - 7.624604) - - def testDecoderFPropFunctionalFeedingAttContext(self): - self._DecoderFPropHelper(decoder.MTDecoderV1, tf.float64, tf.float64, True, - 7.640674) - - def testDecoderFPropPerExampleTensors(self): - self._DecoderFPropHelper( - decoder.MTDecoderV1, - tf.float64, - tf.float64, - False, - 7.624605, - per_example_tensors=True) - - def testDecoderBPropFunctional(self): - self._DecoderGradientCheckerHelper(decoder.MTDecoderV1) - - def testDecoderBPropFunctionalFeedingAttContext(self): - self._DecoderGradientCheckerHelper( - decoder.MTDecoderV1, feed_att_context_to_softmax=True) - - def testDecoderPerWordAvgLossFPropFunctional(self): - self._DecoderPerWordAvgLossFPropHelper(decoder.MTDecoderV1) - - def testDecoderPerWordAvgLossFPropFunctionalFeedingAttContext(self): - self._DecoderPerWordAvgLossFPropHelper( - decoder.MTDecoderV1, feed_att_context_to_softmax=True) - - def testBeamSearchDecode(self, dtype=tf.float32): - with self.session(use_gpu=True), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - src_batch = 2 - p = self._DecoderParams(dtype=dtype) - src_time = p.target_seq_len - p.beam_search.num_hyps_per_beam = 2 - p.rnn_cell_dim = 32 - dec = decoder.MTDecoderV1(p) - encoder_outputs, _ = self._Inputs(dtype=dtype) - decode = dec.BeamSearchDecode(encoder_outputs) - # topk_decoded is None in MT decoder, set it to a fake tensor to pass - # self.evaluate(decode). - decode = decode._replace(topk_decoded=tf.constant(0, tf.float32)) - - self.evaluate(tf.global_variables_initializer()) - actual_decode = self.evaluate(decode) - - self.assertTupleEqual( - (src_time, src_batch * p.beam_search.num_hyps_per_beam), - actual_decode.done_hyps.shape) - self.assertTupleEqual( - (src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_hyps.shape) - self.assertTupleEqual( - (src_batch * p.beam_search.num_hyps_per_beam, src_time), - actual_decode.topk_ids.shape) - self.assertTupleEqual( - (src_batch * p.beam_search.num_hyps_per_beam,), - actual_decode.topk_lens.shape) - self.assertTupleEqual( - (src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_scores.shape) - - expected_topk_ids = [[2, 0, 0, 0, 0], [13, 2, 0, 0, 0], [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0]] - - expected_topk_lens = [1, 2, 0, 0] - expected_topk_scores = [[-3.783162, -5.767723], [0., 0.]] - - self.assertAllEqual(expected_topk_ids, actual_decode.topk_ids) - self.assertAllEqual(expected_topk_lens, actual_decode.topk_lens) - self.assertAllClose(expected_topk_scores, actual_decode.topk_scores) - - def testBeamSearchDecodeTgtPrefix(self, dtype=tf.float32): - with self.session(use_gpu=True), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - src_batch = 2 - p = self._DecoderParams(dtype=dtype) - p.init_step_ids = True # initializes beam search with predefined ids. - p.beam_search.num_hyps_per_beam = 2 - p.rnn_cell_dim = 32 - dec = decoder.MTDecoderV1(p) - encoder_outputs, _ = self._Inputs(dtype=dtype, init_step_ids=True) - decode = dec.BeamSearchDecode(encoder_outputs) - # topk_decoded is None in MT decoder, set it to a fake tensor to pass - # self.evaluate(decode). - decode = decode._replace(topk_decoded=tf.constant(0, tf.float32)) - self.evaluate(tf.global_variables_initializer()) - actual_decode = self.evaluate(decode) - - num_hyps = src_batch * p.beam_search.num_hyps_per_beam - self.assertTupleEqual((p.target_seq_len, num_hyps), - actual_decode.done_hyps.shape) - self.assertTupleEqual((src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_hyps.shape) - self.assertTupleEqual((num_hyps, p.target_seq_len), - actual_decode.topk_ids.shape) - self.assertTupleEqual((num_hyps,), actual_decode.topk_lens.shape) - self.assertTupleEqual((src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_scores.shape) - - expected_topk_ids = [[2, 0, 0, 0, 0], [13, 2, 0, 0, 0], [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0]] - expected_topk_lens = [1, 2, 0, 0] - expected_topk_scores = [[-3.783162, -5.767723], [0., 0.]] - self.assertAllEqual(expected_topk_ids, actual_decode.topk_ids) - self.assertAllEqual(expected_topk_lens, actual_decode.topk_lens) - self.assertAllClose(expected_topk_scores, actual_decode.topk_scores) - - @parameterized.named_parameters( - ('Bias0ConsistentFalse', 0., False), - ('Bias0ConsistentTrue', 0., True), - ('Bias1ConsistentFalse', 1., False), - ('Bias1ConsistentTrue', 1., True), - ) - def testBeamSearchDecodeBiased(self, bias, bias_only_if_consistent): - dtype = tf.float32 - with self.session(use_gpu=True), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - src_batch = 2 - p = self._DecoderParams(dtype=dtype) - p.bias_only_if_consistent = bias_only_if_consistent - p.target_seq_len = 6 - p.beam_search.num_hyps_per_beam = 2 - p.rnn_cell_dim = 32 - dec = p.Instantiate() - encoder_outputs, _ = self._Inputs(dtype=dtype) - encoder_outputs['targets'] = py_utils.NestedMap( - labels=tf.constant([[1, 3, 0, 0], [3, 4, 5, 2]]), - paddings=tf.constant([[0, 0, 1, 1], [0, 0, 0, 0]], dtype=dtype)) - encoder_outputs['targets']['weights'] = tf.fill( - tf.shape(encoder_outputs.targets.labels), bias) - decode = dec.BeamSearchDecodeBiased(encoder_outputs) - - # topk_decoded is None in MT decoder, set it to a fake tensor to pass - # self.evaluate(decode). - decode = decode._replace(topk_decoded=tf.constant(0, tf.float32)) - - self.evaluate(tf.global_variables_initializer()) - actual_decode = self.evaluate(decode) - - num_hyps = src_batch * p.beam_search.num_hyps_per_beam - self.assertTupleEqual((p.target_seq_len, num_hyps), - actual_decode.done_hyps.shape) - self.assertTupleEqual((src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_hyps.shape) - self.assertTupleEqual((num_hyps, p.target_seq_len), - actual_decode.topk_ids.shape) - self.assertTupleEqual((num_hyps,), actual_decode.topk_lens.shape) - self.assertTupleEqual((src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_scores.shape) - - if bias == 0: - expected_topk_ids = [[2, 0, 0, 0, 0, 0], [13, 2, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]] - expected_topk_lens = [1, 2, 0, 0] - expected_topk_scores = [[-3.783162, -5.767723], [0., 0.]] - elif bias == 1 and bias_only_if_consistent: - expected_topk_ids = [[1, 3, 2, 0, 0, 0], [1, 3, 13, 2, 0, 0], - [3, 4, 5, 2, 0, 0], [0, 0, 0, 0, 0, 0]] - expected_topk_lens = [3, 4, 4, 0] - expected_topk_scores = [[-3.073836, -5.474799], [-0.415888, 0.]] - elif bias == 1 and (not bias_only_if_consistent): - expected_topk_ids = [[1, 3, 2, 0, 0, 0], [1, 3, 13, 2, 0, 0], - [3, 4, 5, 2, 0, 0], [3, 4, 0, 2, 0, 0]] - expected_topk_lens = [3, 4, 4, 4] - expected_topk_scores = [[-3.073836, -5.474799], [-0.415888, -23.295631]] - - self.assertAllEqual(expected_topk_ids, actual_decode.topk_ids) - self.assertAllEqual(expected_topk_lens, actual_decode.topk_lens) - self.assertAllClose(expected_topk_scores, actual_decode.topk_scores) - - def testBeamSearchDecodeUseZeroAttenState(self, dtype=tf.float32): - with self.session(use_gpu=True), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - src_batch = 2 - p = self._DecoderParams(dtype=dtype) - src_time = p.target_seq_len - p.beam_search.num_hyps_per_beam = 2 - p.use_zero_atten_state = True - p.rnn_cell_dim = 32 - dec = decoder.MTDecoderV1(p) - encoder_outputs, _ = self._Inputs(dtype=dtype) - decode = dec.BeamSearchDecode(encoder_outputs) - # topk_decoded is None in MT decoder, set it to a fake tensor to pass - # self.evaluate(decode). - decode = decode._replace(topk_decoded=tf.constant(0, tf.float32)) - - self.evaluate(tf.global_variables_initializer()) - actual_decode = self.evaluate(decode) - - self.assertTupleEqual( - (src_time, src_batch * p.beam_search.num_hyps_per_beam), - actual_decode.done_hyps.shape) - self.assertTupleEqual((src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_hyps.shape) - self.assertTupleEqual( - (src_batch * p.beam_search.num_hyps_per_beam, src_time), - actual_decode.topk_ids.shape) - self.assertTupleEqual((src_batch * p.beam_search.num_hyps_per_beam,), - actual_decode.topk_lens.shape) - self.assertTupleEqual((src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_scores.shape) - - expected_topk_ids = [[2, 0, 0, 0, 0], [13, 2, 0, 0, 0], [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0]] - - expected_topk_lens = [1, 2, 0, 0] - expected_topk_scores = [[-3.783176, -5.767704], [0., 0.]] - - self.assertAllEqual(expected_topk_ids, actual_decode.topk_ids) - self.assertAllEqual(expected_topk_lens, actual_decode.topk_lens) - self.assertAllClose(expected_topk_scores, actual_decode.topk_scores) - - def testBeamSearchDecodeFeedingAttContext(self, dtype=tf.float32): - with self.session(use_gpu=True), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - src_batch = 2 - p = self._DecoderParams(dtype=dtype) - src_time = p.target_seq_len - p.beam_search.num_hyps_per_beam = 2 - p.rnn_cell_dim = 32 - p.feed_attention_context_vec_to_softmax = True - dec = decoder.MTDecoderV1(p) - encoder_outputs, _ = self._Inputs(dtype=dtype) - decode = dec.BeamSearchDecode(encoder_outputs) - # topk_decoded is None in MT decoder, set it to a fake tensor to pass - # self.evaluate(decode). - decode = decode._replace(topk_decoded=tf.constant(0, tf.float32)) - self.evaluate(tf.global_variables_initializer()) - actual_decode_feeding_att_context = self.evaluate(decode) - - self.assertTupleEqual( - (src_time, src_batch * p.beam_search.num_hyps_per_beam), - actual_decode_feeding_att_context.done_hyps.shape) - self.assertTupleEqual( - (src_batch, p.beam_search.num_hyps_per_beam), - actual_decode_feeding_att_context.topk_hyps.shape) - self.assertTupleEqual( - (src_batch * p.beam_search.num_hyps_per_beam, src_time), - actual_decode_feeding_att_context.topk_ids.shape) - self.assertTupleEqual( - (src_batch * p.beam_search.num_hyps_per_beam,), - actual_decode_feeding_att_context.topk_lens.shape) - self.assertTupleEqual( - (src_batch, p.beam_search.num_hyps_per_beam), - actual_decode_feeding_att_context.topk_scores.shape) - - expected_topk_ids = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [2, 0, 0, 0, 0], - [8, 2, 0, 0, 0]] - - expected_topk_lens = [0, 0, 1, 2] - expected_topk_scores = [[0., 0.], [-3.292501, -5.533068]] - - self.assertAllEqual(expected_topk_ids, - actual_decode_feeding_att_context.topk_ids) - self.assertAllEqual(expected_topk_lens, - actual_decode_feeding_att_context.topk_lens) - self.assertAllClose(expected_topk_scores, - actual_decode_feeding_att_context.topk_scores) - - def testSampleTargetSequences(self, dtype=tf.float32): - with self.session(use_gpu=True), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - src_batch = 2 - p = self._DecoderParams(dtype=dtype) - if p.cls != decoder.MTDecoderV1: - tf.logging.info('Skipping testSampleTargetSequences for %s', p.cls) - return - p.rnn_cell_dim = 32 - dec = p.Instantiate() - encoder_outputs, _ = self._Inputs(dtype=dtype) - sample = dec.SampleTargetSequences( - dec.theta, - encoder_outputs, - random_seed=tf.constant(1, dtype=tf.int32)) - self.evaluate(tf.global_variables_initializer()) - actual_sample = self.evaluate(sample) - - self.assertTupleEqual((src_batch, p.target_seq_len), - actual_sample.ids.shape) - self.assertTupleEqual((src_batch, p.target_seq_len), - actual_sample.paddings.shape) - - expected_ids = [[0, 12, 12, 13, 5], [12, 10, 15, 1, 2]] - self.assertAllEqual(expected_ids, actual_sample.ids) - - -class TransformerDecoderTestCaseBase(test_utils.TestCase): - - def _DecoderParams(self, - per_word_avg_loss=False, - is_transparent=False, - dtype=tf.float32, - fprop_dtype=None, - use_task_emb=False, - init_step_ids=False): - p = decoder.TransformerDecoder.Params() - p.name = 'decoder' - p.source_dim = 4 - p.model_dim = 4 - p.num_trans_layers = 6 - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - p.token_emb.vn = disable_vn - p.token_emb.vocab_size = 20 - p.token_emb.embedding_dim = 4 - p.token_emb.max_num_shards = 1 - p.token_emb.params_init = py_utils.WeightInit.GaussianSqrtDim(seed=12345) - p.position_emb.embedding_dim = 4 - if use_task_emb: - p.task_emb = p.token_emb.Copy() - p.task_emb.vocab_size = 4 - p.trans_tpl.vn = disable_vn - p.init_step_ids = init_step_ids - p.trans_tpl.source_dim = 4 - p.trans_tpl.tr_atten_tpl.source_dim = 4 - p.trans_tpl.tr_atten_tpl.num_attention_heads = 2 - p.trans_tpl.tr_fflayer_tpl.input_dim = 4 - p.trans_tpl.tr_fflayer_tpl.hidden_dim = 8 - p.label_smoothing = layers.LocalizedLabelSmoother.Params() - p.label_smoothing.offsets = [-2, -1, 1, 2] - p.label_smoothing.weights = [0.015, 0.035, 0.035, 0.015] - p.softmax.vn = disable_vn - p.softmax.num_classes = 20 - p.softmax.num_shards = 1 - p.per_word_avg_loss = per_word_avg_loss - p.random_seed = 1234 - p.dtype = dtype - p.target_seq_len = 5 - p.is_transparent = is_transparent - - for lp in base_layer.RecursiveFindLayerParams(p): - lp.dtype = dtype - - py_utils.UpdateFpropDtype(p, fprop_dtype) - - return p - - def _Inputs(self, dtype=tf.float32, has_task_ids=False, init_step_ids=False): - np.random.seed(_NUMPY_RANDOM_SEED) - src_time = 5 - src_batch = 4 - num_hyps = 2 - emb_dims = 4 - src_enc = tf.constant( - np.random.normal(size=[src_time, src_batch, emb_dims]), dtype=dtype) - src_paddings = tf.zeros([src_time, src_batch], dtype=dtype) - tgt_time = 5 - tgt_batch = src_batch * num_hyps - self.tgt_batch = tgt_batch - - tgt_ids = tf.constant( - np.random.randint(20, size=[tgt_batch, tgt_time]), dtype=tf.int32) - tgt_labels = tf.constant( - np.random.randint(20, size=[tgt_batch, tgt_time]), dtype=tf.int32) - tgt_paddings = tf.zeros([tgt_batch, tgt_time], dtype=dtype) - tgt_weights = 1.0 - tgt_paddings - tgts = py_utils.NestedMap({ - 'ids': tgt_ids, - 'labels': tgt_labels, - 'weights': tgt_weights, - 'paddings': tgt_paddings - }) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_paddings, segment_id=None) - - if has_task_ids: - task_ids = tf.constant( - np.random.randint(4, size=[src_batch]), dtype=tf.int32) - tgts['task_ids'] = tf.tile( - tf.expand_dims(tf.tile(task_ids, [num_hyps]), 1), [1, tgt_time]) - encoder_outputs['target_task_ids'] = task_ids - if init_step_ids: - tgt_prefix = tf.constant( - np.random.randint(4, size=[src_batch]), dtype=tf.int32) - encoder_outputs['init_step_ids'] = tgt_prefix - return (encoder_outputs, tgts, num_hyps) - - def _InputsForAttentionTest(self, dtype=tf.float32, has_task_ids=False): - np.random.seed(_NUMPY_RANDOM_SEED) - src_time = 5 - src_batch = 2 - num_hyps = 2 - emb_dims = 4 - src_enc = tf.constant( - np.random.normal(size=[src_time, src_batch, emb_dims]), dtype=dtype) - src_paddings = tf.constant( - [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [0.0, 1.0]], - dtype=dtype) - tgt_time = 5 - tgt_batch = src_batch * num_hyps - self.tgt_batch = tgt_batch - - tgt_ids = tf.constant( - np.random.randint(20, size=[tgt_batch, tgt_time]), dtype=tf.int32) - tgt_labels = tf.constant( - np.random.randint(20, size=[tgt_batch, tgt_time]), dtype=tf.int32) - tgt_paddings = tf.zeros([tgt_batch, tgt_time], dtype=dtype) - tgt_weights = 1.0 - tgt_paddings - tgts = py_utils.NestedMap({ - 'ids': tgt_ids, - 'labels': tgt_labels, - 'weights': tgt_weights, - 'paddings': tgt_paddings - }) - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=src_paddings, segment_id=None) - - if has_task_ids: - task_ids = tf.constant( - np.random.randint(4, size=[src_batch]), dtype=tf.int32) - tgts['task_ids'] = tf.tile( - tf.expand_dims(tf.tile(task_ids, [num_hyps]), 1), [1, tgt_time]) - encoder_outputs['target_task_ids'] = task_ids - - return (encoder_outputs, tgts, num_hyps) - - -class TransformerDecoderTest(TransformerDecoderTestCaseBase): - - def testDecoderConstruction(self): - p = self._DecoderParams() - dec = decoder.TransformerDecoder(p) - self.assertIsInstance(dec, p.cls) - - def testDecoderWithNgramMaskConstruction(self): - p = self._DecoderParams() - # Turn on N-gram masking in the TransformerLayer. - # Before doing so though copy the self-attention params to avoid - # the auxilliary attention being masked as well. - p.trans_tpl.tr_aux_atten_tpl = p.trans_tpl.tr_atten_tpl.Copy() - p.trans_tpl.tr_atten_tpl.is_masked = True - p.trans_tpl.tr_atten_tpl.mask_ngram_order = 3 - p.trans_tpl.tr_atten_tpl.mask_type = 'ngram' - dec = decoder.TransformerDecoder(p) - self.assertIsInstance(dec, p.cls) - - def testDecoderConstructionWithTplList(self): - p = self._DecoderParams() - p.trans_tpl = [p.trans_tpl.Copy(), p.trans_tpl.Copy()] - dec = decoder.TransformerDecoder(p) - self.assertIsInstance(dec, p.cls) - - def testTransparentDecoderConstruction(self): - p = self._DecoderParams(is_transparent=True) - dec = decoder.TransformerDecoder(p) - self.assertIsInstance(dec, p.cls) - - def _testPackedInputs(self, dtype=tf.float32): - p = self._DecoderParams() - np.random.seed(_NUMPY_RANDOM_SEED) - src_time = 5 - batch = 2 - emb_dims = 4 - tgt_time = 5 - src_enc = tf.constant( - np.random.normal(size=[src_time, batch, p.source_dim]), dtype=dtype) - paddings = tf.zeros([src_time, batch], dtype=dtype) - tgt_ids = tf.constant( - np.random.randint(20, size=[batch, tgt_time]), dtype=tf.int32) - tgt_labels = tf.constant( - np.random.randint(20, size=[batch, tgt_time]), dtype=tf.int32) - tgt_paddings = tf.zeros([batch, tgt_time], dtype=dtype) - tgt_weights = 1.0 - tgt_paddings - tgts = py_utils.NestedMap({ - 'ids': tgt_ids, - 'labels': tgt_labels, - 'weights': tgt_weights, - 'paddings': tgt_paddings - }) - - src_enc_packed = tf.transpose(src_enc, [1, 0, 2]) - src_enc_packed = tf.reshape(src_enc_packed, [-1, 1, emb_dims]) - src_enc_padding_packed = tf.reshape(paddings, [-1, 1]) - target_packed = py_utils.NestedMap({ - 'ids': tf.reshape(tgts.ids, [1, -1]), - 'labels': tf.reshape(tgts.labels, [1, -1]), - 'weights': tf.reshape(tgts.weights, [1, -1]), - 'paddings': tf.reshape(tgts.paddings, [1, -1]) - }) - src_segment_id = tf.transpose( - tf.constant( - np.asarray([[0, 0, 0, 0, 0, 1, 1, 1, 1, 1]]), dtype=tf.float32)) - target_packed.segment_ids = tf.constant( - np.asarray([[0, 0, 0, 0, 0, 1, 1, 1, 1, 1]]), dtype=tf.float32) - target_packed.segment_pos = tf.constant( - np.asarray([[0, 1, 2, 3, 4, 0, 1, 2, 3, 4]])) - return (src_enc, paddings, tgts, src_enc_packed, src_enc_padding_packed, - src_segment_id, target_packed) - - def _testTransparentInputs(self, - num_layers=6, - dtype=tf.float32, - is_eval_mode=False): - src_time = 5 - src_batch = 4 - emb_dims = 4 - encoder_outputs, tgts, num_hyps = self._Inputs(dtype) - src_enc = tf.constant( - np.random.normal(size=[src_time, src_batch, emb_dims, num_layers]), - dtype=dtype) - if not is_eval_mode: - src_enc = tf.unstack(src_enc, axis=3) - encoder_outputs.encoded = src_enc - return (encoder_outputs, tgts, num_hyps) - - def testDecoderFPropWithPacking(self, dtype=tf.float32): - with self.session(use_gpu=True): - with tf.variable_scope('transformer_test', reuse=tf.AUTO_REUSE): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(per_word_avg_loss=True, dtype=dtype) - # Localized label smoother messes up the loss with packing. - p.label_smoothing = None - dec = decoder.TransformerDecoder(p) - p_packed = p.Copy() - p_packed.packed_input = True - dec_packed = decoder.TransformerDecoder(p_packed) - - (src_enc, paddings, tgts, src_enc_packed, src_enc_padding_packed, - src_segment_id, target_packed) = self._testPackedInputs() - encoder_outputs = py_utils.NestedMap( - encoded=src_enc, padding=paddings, segment_id=None) - loss, _ = dec.FProp(dec.theta, encoder_outputs, tgts).metrics['loss'] - encoder_outputs_packed = py_utils.NestedMap( - encoded=src_enc_packed, - padding=src_enc_padding_packed, - segment_id=src_segment_id) - loss_packed, _ = dec_packed.FProp(dec_packed.theta, - encoder_outputs_packed, - target_packed).metrics['loss'] - self.evaluate(tf.global_variables_initializer()) - actual_loss, packed_loss = self.evaluate([loss, loss_packed]) - self.assertAlmostEqual( - np.float32(packed_loss), np.float32(actual_loss), delta=1e-4) - - def testTransparentDecoderFProp(self, dtype=tf.float32): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(is_transparent=True, dtype=dtype) - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, _ = self._testTransparentInputs(dtype=dtype) - loss, _ = dec.FPropDefaultTheta(encoder_outputs, targets).metrics['loss'] - self.evaluate(tf.global_variables_initializer()) - actual_loss = loss.eval() - print('actual loss = ', actual_loss) - CompareToGoldenSingleFloat(self, 19.725393, actual_loss) - - def test_ExpandToNumHyps(self, dtype=tf.float32): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(is_transparent=True, dtype=dtype) - dec = decoder.TransformerDecoder(p) - - src_enc_len = tf.constant([3, 2, 1]) - num_hyps = 2 - expected = tf.constant([3, 2, 1, 3, 2, 1]) - expanded = dec._ExpandToNumHyps(src_enc_len, num_hyps) - expanded_v, expected_v = self.evaluate([expanded, expected]) - self.assertAllEqual(expanded_v, expected_v) - - def test_RemoveEOSProbs(self, dtype=tf.float32): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(is_transparent=True, dtype=dtype) - dec = decoder.TransformerDecoder(p) - - src_enc_len = tf.constant([5, 3, 5]) - - # [batch, target_len, source_len] - probs = tf.constant([[[0.2, 0.2, 0.2, 0.2, 0.2]], - [[0.2, 0.3, 0.5, 0.0, 0.0]], - [[0.0, 0.0, 0.0, 0.0, 1.0]]]) - new_probs = dec._RemoveEOSProbs(p, probs, src_enc_len) - new_probs_v = self.evaluate([new_probs]) - - expected_probs = tf.constant([[[0.25, 0.25, 0.25, 0.25, 0.0]], - [[0.4, 0.6, 0.0, 0.0, 0.0]], - [[0.0, 0.0, 0.0, 0.0, 0.0]]]) - - new_probs_v, expected_probs_v = self.evaluate([new_probs, expected_probs]) - - self.assertAllClose(expected_probs_v, new_probs_v) - - def testDecoderFPropWithTaskEmb(self, dtype=tf.float32): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype, use_task_emb=True) - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, _ = self._Inputs(dtype=dtype, has_task_ids=True) - loss, _ = dec.FPropDefaultTheta(encoder_outputs, targets).metrics['loss'] - self.evaluate(tf.global_variables_initializer()) - actual_loss = loss.eval() - CompareToGoldenSingleFloat(self, 18.374338, actual_loss) - - def testDecoderFPropWithLangDepAtten(self, dtype=tf.float32): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype, use_task_emb=True) - # 4 tasks, 2 languages. - p.use_lang_dependent_atten = True - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, _ = self._Inputs(dtype=dtype, has_task_ids=True) - loss, _ = dec.FPropDefaultTheta(encoder_outputs, targets).metrics['loss'] - self.evaluate(tf.global_variables_initializer()) - actual_loss = loss.eval() - CompareToGoldenSingleFloat(self, 16.200066, actual_loss) - - def testDecoderFPropWithContext(self, dtype=tf.float32): - with self.session(use_gpu=True): - with tf.variable_scope('transformer_test', reuse=tf.AUTO_REUSE): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(per_word_avg_loss=True, dtype=dtype) - p.trans_tpl = layers_with_attention.TransformerWithContextLayer.Params() - p.trans_tpl.source_dim = 4 - p.trans_tpl.tr_atten_tpl.source_dim = 4 - p.trans_tpl.tr_atten_tpl.num_attention_heads = 2 - p.trans_tpl.tr_fflayer_tpl.input_dim = 4 - p.trans_tpl.tr_fflayer_tpl.hidden_dim = 8 - dec = p.Instantiate() - - enc_outputs, targets, _ = self._Inputs() - encoder_outputs = py_utils.NestedMap( - encoded=enc_outputs.encoded, - padding=enc_outputs.padding, - context_encoded=enc_outputs.encoded, - context_padding=enc_outputs.padding) - loss, _ = dec.FProp(dec.theta, encoder_outputs, targets).metrics['loss'] - self.evaluate(tf.global_variables_initializer()) - CompareToGoldenSingleFloat(self, 3.816574, loss.eval()) - - def testDecoderFPropWithZeroFirstStep(self, dtype=tf.float32): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype) - p.zero_token_embs_first_time_step = True - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, _ = self._Inputs(dtype=dtype, has_task_ids=True) - loss, _ = dec.FPropDefaultTheta(encoder_outputs, targets).metrics['loss'] - self.evaluate(tf.global_variables_initializer()) - actual_loss = loss.eval() - CompareToGoldenSingleFloat(self, 21.425932, actual_loss) - - def _testExtendStep(self, sess, dec, encoder_outputs, tgts, num_hyps): - p = self._DecoderParams() - - # Infer true source encoder length from the padding. - src_enc_len = tf.reduce_sum(1 - encoder_outputs.padding, axis=0) - src_enc_len = dec._ExpandToNumHyps(src_enc_len, num_hyps) - - # Run Fprop - fprop_out = dec._FProp(dec.theta, encoder_outputs, tgts) - l_out1 = fprop_out.softmax_input - attention_map_fprop = fprop_out.attention - - # run ExtendStep - prefix_states = py_utils.NestedMap() - for i in range(6): - layer_i_states = py_utils.NestedMap() - # The first dim is for the decode step (sequence length). - # Here's 0 as placeholder - layer_i_states.key = tf.zeros([0, self.tgt_batch, p.model_dim]) - layer_i_states.value = tf.zeros([0, self.tgt_batch, p.model_dim]) - prefix_states['layer_%i' % i] = layer_i_states - - l_out2 = [] - per_step_atten_probs = [] - for i in range(5): - l_i_out, prefix_states, atten_probs = dec.ExtendStep( - dec.theta, encoder_outputs, tgts.ids[:, i], i, prefix_states) - l_out2.append(l_i_out) - per_step_atten_probs.append(atten_probs) - l_out2 = tf.stack(l_out2) - bs_atten_probs = tf.stack(per_step_atten_probs) - - attention_map_bs = py_utils.NestedMap(probs=bs_atten_probs) - - def _TransposeAttentions(x): - return tf.transpose(x, [1, 0, 2]) - - attention_map_bs = attention_map_bs.Transform(_TransposeAttentions) - - self.evaluate(tf.global_variables_initializer()) - - l_out1_v, l_out2_v, attention_map_fprop_v, attention_map_bs_v, src_enc_len_v = self.evaluate( - [l_out1, l_out2, attention_map_fprop, attention_map_bs, src_enc_len]) - - # Ensure that FProp and BeamSearch output are the same. - self.assertAllClose(l_out1_v, l_out2_v, rtol=1e-05, atol=1e-05) - - # Ensure that FProp and BeamSearch attention matrix is the same. - self.assertAllClose(attention_map_fprop_v.probs, attention_map_bs_v.probs) - - print('attention map', attention_map_fprop_v.probs) - - # End-to-end test attention probs -- ensure EOS symbol and positions - # behind EOS have 0 probability. - for i in range(0, len(src_enc_len_v)): - pos = int(src_enc_len_v[i]) - 1 - self.assertEqual( - np.count_nonzero(attention_map_fprop_v.probs[i][:, pos:]), 0) - - def testDecoderExtendStep(self, dtype=tf.float32): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype) - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, num_hyps = ( - self._InputsForAttentionTest(dtype=dtype)) - - self._testExtendStep(sess, dec, encoder_outputs, targets, num_hyps) - - def testDecoderWithNgramMaskExtendStep(self, dtype=tf.float32): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype) - # Turn on N-gram masking in the TransformerLayer. - # Before doing so though copy the self-attention params to avoid - # the auxilliary attention being masked as well. - p.trans_tpl.tr_aux_atten_tpl = p.trans_tpl.tr_atten_tpl.Copy() - p.trans_tpl.tr_atten_tpl.is_masked = True - p.trans_tpl.tr_atten_tpl.mask_ngram_order = 3 - p.trans_tpl.tr_atten_tpl.mask_type = 'ngram' - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, num_hyps = ( - self._InputsForAttentionTest(dtype=dtype)) - - self._testExtendStep(sess, dec, encoder_outputs, targets, num_hyps) - - def testDecoderExtendStepWithTaskEmb(self, dtype=tf.float32): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype, use_task_emb=True) - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, num_hyps = ( - self._InputsForAttentionTest(dtype=dtype, has_task_ids=True)) - - self._testExtendStep(sess, dec, encoder_outputs, targets, num_hyps) - - def testDecoderExtendStepZeroFirstTimeStep(self, dtype=tf.float32): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype) - p.zero_token_embs_first_time_step = True - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, num_hyps = ( - self._InputsForAttentionTest(dtype=dtype)) - self._testExtendStep(sess, dec, encoder_outputs, targets, num_hyps) - - def testTransparentDecoderExtendStep(self, dtype=tf.float32): - with self.session(use_gpu=True) as sess, self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(is_transparent=True, dtype=dtype) - dec = decoder.TransformerDecoder(p) - encoder_outputs, targets, num_hyps = self._testTransparentInputs( - dtype=dtype, is_eval_mode=True) - self._testExtendStep(sess, dec, encoder_outputs, targets, num_hyps) - - def testDecoderExtendStepWithContext(self, dtype=tf.float32): - with self.session(use_gpu=True) as sess: - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype) - p.trans_tpl = layers_with_attention.TransformerWithContextLayer.Params() - p.trans_tpl.source_dim = 4 - p.trans_tpl.tr_atten_tpl.source_dim = 4 - p.trans_tpl.tr_atten_tpl.num_attention_heads = 2 - p.trans_tpl.tr_fflayer_tpl.input_dim = 4 - p.trans_tpl.tr_fflayer_tpl.hidden_dim = 8 - dec = p.Instantiate() - enc_outputs, targets, num_hyps = ( - self._InputsForAttentionTest(dtype=dtype, has_task_ids=True)) - encoder_outputs = py_utils.NestedMap( - encoded=enc_outputs.encoded, - padding=enc_outputs.padding, - context_encoded=enc_outputs.encoded, - context_padding=enc_outputs.padding) - self._testExtendStep(sess, dec, encoder_outputs, targets, num_hyps) - - def testDecoderFPropSplitBatch(self, dtype=tf.float32): - with self.session(use_gpu=True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._DecoderParams(dtype=dtype) - dec = decoder.TransformerDecoder(p) - - encoder_outputs, targets, _ = self._Inputs(dtype=dtype) - src_enc1, src_enc2 = tf.split(encoder_outputs.encoded, 2, 1) - src_paddings1, src_paddings2 = tf.split(encoder_outputs.padding, 2, 1) - - # source idx <-> target idx: - # 0 <-> (0, 4), 1 <-> (1, 5), 2 <-> (2, 6), 3 <-> (3, 7) - tgts = ig_helper.SplitDictOfTensors(targets, 4) - targets1 = py_utils.NestedMap({ - 'ids': tf.concat([tgts[0]['ids'], tgts[2]['ids']], 0), - 'labels': tf.concat([tgts[0]['labels'], tgts[2]['labels']], 0), - 'weights': tf.concat([tgts[0]['weights'], tgts[2]['weights']], 0), - 'paddings': tf.concat([tgts[0]['paddings'], tgts[2]['paddings']], 0) - }) - targets2 = py_utils.NestedMap({ - 'ids': tf.concat([tgts[1]['ids'], tgts[3]['ids']], 0), - 'labels': tf.concat([tgts[1]['labels'], tgts[3]['labels']], 0), - 'weights': tf.concat([tgts[1]['weights'], tgts[3]['weights']], 0), - 'paddings': tf.concat([tgts[1]['paddings'], tgts[3]['paddings']], 0) - }) - - loss, _ = dec.FPropDefaultTheta(encoder_outputs, targets).metrics['loss'] - encoder_outputs1 = py_utils.NestedMap( - encoded=src_enc1, padding=src_paddings1, segment_id=None) - loss1, _ = dec.FPropDefaultTheta(encoder_outputs1, - targets1).metrics['loss'] - encoder_outputs2 = py_utils.NestedMap( - encoded=src_enc2, padding=src_paddings2, segment_id=None) - loss2, _ = dec.FPropDefaultTheta(encoder_outputs2, - targets2).metrics['loss'] - - self.evaluate(tf.global_variables_initializer()) - actual_loss, actual_loss1, actual_loss2 = self.evaluate( - [loss, loss1, loss2]) - print('actual loss = ', actual_loss) - print('actual loss1 = ', actual_loss1) - print('actual loss2 = ', actual_loss2) - self.assertAlmostEqual( - actual_loss, np.mean([actual_loss1, actual_loss2]), delta=0.0001) - - def _testBeamSearch(self, - expected_values, - dtype=tf.float32, - init_step_ids=False, - has_task_ids=False): - tf.random.set_seed(_TF_RANDOM_SEED) - src_batch = 4 - src_time = 5 - p = self._DecoderParams(dtype=dtype, init_step_ids=init_step_ids) - p.beam_search.num_hyps_per_beam = 2 - p.beam_search.coverage_penalty = 0.0 - p.beam_search.length_normalization = 0 - dec = decoder.TransformerDecoder(p) - encoder_outputs, _, _ = self._Inputs( - dtype=dtype, has_task_ids=has_task_ids, init_step_ids=init_step_ids) - decode = dec.BeamSearchDecode(encoder_outputs) - # topk_decoded is None in MT decoder, set it to a fake tensor to pass - # self.evaluate(decode). - decode = decode._replace(topk_decoded=tf.constant(0, tf.float32)) - - with self.session(use_gpu=True): - self.evaluate(tf.global_variables_initializer()) - actual_decode = self.evaluate(decode) - - self.assertTupleEqual( - (src_time, src_batch * p.beam_search.num_hyps_per_beam), - actual_decode.done_hyps.shape) - self.assertTupleEqual( - (src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_hyps.shape) - self.assertTupleEqual( - (src_batch * p.beam_search.num_hyps_per_beam, src_time), - actual_decode.topk_ids.shape) - self.assertTupleEqual( - (src_batch * p.beam_search.num_hyps_per_beam,), - actual_decode.topk_lens.shape) - self.assertTupleEqual( - (src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_scores.shape) - - # Assert expected IDs etc - self.assertAllEqual(expected_values['topk_ids'], actual_decode.topk_ids) - self.assertAllEqual(expected_values['topk_lens'], actual_decode.topk_lens) - self.assertAllClose(expected_values['topk_scores'], - actual_decode.topk_scores) - - # Assert expected attention probs. - hypstr = actual_decode.topk_hyps.flatten()[1] - hyp = Hypothesis() - hyp.ParseFromString(hypstr) - print('HYP:', hyp) - - atten_vec_0 = list(np.expand_dims(np.array(hyp.atten_vecs[0].prob), 0)[0]) - atten_vec_1 = list(np.expand_dims(np.array(hyp.atten_vecs[1].prob), 0)[0]) - - self.assertAllClose(atten_vec_0, expected_values['atten_vec_0']) - self.assertAllClose(atten_vec_1, expected_values['atten_vec_1']) - - # Test normalized scores of hypotheses. - CompareToGoldenSingleFloat(self, expected_values['normalized_score'], - hyp.normalized_score) - - def testBeamSearchDecode(self, dtype=tf.float32): - expected_values = {} - expected_values['topk_ids'] = [[5, 2, 0, 0, 0], [17, 2, 0, 0, 0], - [5, 2, 0, 0, 0], [17, 3, 2, 0, 0], - [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] - expected_values['topk_lens'] = [2, 2, 2, 3, 0, 0, 0, 0] - expected_values['topk_scores'] = [[-3.821746, -3.980103], - [-3.817123, -4.522634], [0., 0.], - [0., 0.]] - - expected_values['atten_vec_0'] = [ - 0.532658, 0.140424, 0.122954, 0.203961, 0. - ] - expected_values['atten_vec_1'] = [ - 0.067983, 0.532731, 0.284223, 0.115062, 0. - ] - expected_values['normalized_score'] = -3.980103 - - self._testBeamSearch( - expected_values=expected_values, - dtype=dtype, - init_step_ids=False, - has_task_ids=False) - - def testBeamSearchDecodeTgtPrefix(self, dtype=tf.float32): - expected_values = {} - expected_values['topk_ids'] = [[5, 2, 0, 0, 0], [1, 2, 0, 0, 0], - [5, 2, 0, 0, 0], [5, 3, 2, 0, 0], - [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] - expected_values['topk_lens'] = [2, 2, 2, 3, 0, 0, 0, 0] - expected_values['topk_scores'] = [[-3.837574, -4.1681], - [-3.821537, -5.069403], [0., 0.], - [0., 0.]] - - expected_values['atten_vec_0'] = [0.58009, 0.129324, 0.109171, 0.181414, 0.] - expected_values['atten_vec_1'] = [ - 0.072272, 0.474015, 0.330433, 0.123279, 0. - ] - expected_values['normalized_score'] = -4.1681 - - self._testBeamSearch( - expected_values=expected_values, - dtype=dtype, - init_step_ids=True, - has_task_ids=False) - - def _testSampleSequence(self, - expected_values, - dtype=tf.float32, - init_step_ids=False, - has_task_ids=False): - tf.random.set_seed(_TF_RANDOM_SEED) - src_batch = 4 - src_time = 5 - p = self._DecoderParams(dtype=dtype, init_step_ids=init_step_ids) - p.beam_search.num_hyps_per_beam = 1 - p.beam_search.coverage_penalty = 0.0 - p.beam_search.length_normalization = 0 - dec = p.Instantiate() - encoder_outputs, _, _ = self._Inputs( - dtype=dtype, has_task_ids=has_task_ids, init_step_ids=init_step_ids) - decode = dec.SampleSequenceDecode(encoder_outputs) - - with self.session(use_gpu=True): - self.evaluate(tf.global_variables_initializer()) - actual_decode = self.evaluate(decode) - - self.assertTupleEqual((src_batch, p.beam_search.num_hyps_per_beam), - actual_decode.topk_hyps.shape) - self.assertTupleEqual( - (src_batch * p.beam_search.num_hyps_per_beam, src_time), - actual_decode.topk_ids.shape) - self.assertTupleEqual((src_batch * p.beam_search.num_hyps_per_beam,), - actual_decode.topk_lens.shape) - - self.assertAllEqual(expected_values['topk_ids'], actual_decode.topk_ids) - self.assertAllEqual(expected_values['topk_lens'], actual_decode.topk_lens) - self.assertAllClose(expected_values['topk_scores'], - actual_decode.topk_scores) - - def testSampleSequenceDecode(self, dtype=tf.float32): - expected_values = {} - expected_values['topk_ids'] = [[4, 3, 12, 7, 11], [17, 7, 2, 2, 2], - [17, 2, 2, 2, 2], [17, 1, 1, 16, 4]] - expected_values['topk_lens'] = [5, 3, 2, 5] - expected_values['topk_scores'] = [ - -6.985453, -3.714368, -2.899912, -8.281981 - ] - self._testSampleSequence( - expected_values=expected_values, - dtype=dtype, - init_step_ids=True, - has_task_ids=False) - - -class InsertionDecoderTest(TransformerDecoderTestCaseBase): - - def testDecoderConstruction(self): - p = decoder.InsertionDecoder.Params() - p.name = 'insertion_decoder' - dec = p.Instantiate() - self.assertIsInstance(dec, decoder.InsertionDecoder) - - -class TransformerBatchMajorDecoderTest(test_utils.TestCase, - parameterized.TestCase): - """Test Transformer decoder.""" - - def _ConstructTransformerBatchMajorDecoder(self, - dtype=tf.float32, - packed_input=False, - **kwargs): - p = decoder.TransformerBatchMajorDecoder.Params() - p.name = 'decoder' - p.packed_input = packed_input - p.source_dim = 4 - p.model_dim = 4 - p.num_trans_layers = 6 - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - p.token_emb.vn = disable_vn - p.token_emb.vocab_size = 20 - p.token_emb.embedding_dim = 4 - p.token_emb.max_num_shards = 1 - p.token_emb.params_init = py_utils.WeightInit.GaussianSqrtDim() - p.position_emb.embedding_dim = 4 - p.trans_decoder_tpl.vn = disable_vn - p.trans_decoder_tpl.input_dim = 4 - p.trans_decoder_tpl.tr_atten_tpl.input_dim = 4 - p.trans_decoder_tpl.tr_atten_tpl.num_heads = 2 - p.trans_decoder_tpl.tr_fflayer_tpl.input_dim = 4 - p.trans_decoder_tpl.tr_fflayer_tpl.hidden_dim = 8 - p.trans_decoder_tpl.packed_input = packed_input - p.softmax.vn = disable_vn - p.softmax.num_classes = 20 - p.softmax.num_shards = 1 - p.per_word_avg_loss = False - p.random_seed = 12345 - p.target_seq_len = 5 - p.beam_search.num_hyps_per_beam = 2 - p.beam_search.coverage_penalty = 0.0 - p.beam_search.length_normalization = 0 - p.dtype = dtype - for k, v in kwargs.items(): - setattr(p, k, v) - for lp in base_layer.RecursiveFindLayerParams(p): - lp.dtype = dtype - return decoder.TransformerBatchMajorDecoder(p) - - def _Inputs(self, dtype=tf.float32, packed_input=False): - np.random.seed(9885784) - source_time = 5 - batch = 4 - dim = 4 - encoded = tf.constant( - np.random.normal(size=[source_time, batch, dim]), dtype=dtype) - padding = tf.zeros([source_time, batch], dtype=dtype) - target_time = 5 - target_ids = tf.constant( - np.random.randint(20, size=[batch, target_time]), dtype=tf.int32) - target_labels = tf.constant( - np.random.randint(20, size=[batch, target_time]), dtype=tf.int32) - target_paddings = tf.zeros([batch, target_time], dtype=dtype) - target_weights = 1.0 - target_paddings - targets = py_utils.NestedMap({ - 'ids': target_ids, - 'labels': target_labels, - 'weights': target_weights, - 'paddings': target_paddings - }) - encoder_outputs = py_utils.NestedMap(encoded=encoded, padding=padding) - - if packed_input: - encoder_outputs.segment_id = tf.tile( - tf.constant(np.asarray([[1, 1, 2, 2, 2]]), dtype=tf.float32), - [batch, 1]) - encoder_outputs.segment_id = tf.transpose(encoder_outputs.segment_id) - targets.segment_pos = tf.tile( - tf.constant(np.asarray([[0, 1, 0, 1, 2]]), dtype=tf.float32), - [batch, 1]) - targets.segment_ids = tf.tile( - tf.constant(np.asarray([[1, 1, 2, 2, 2]]), dtype=tf.float32), - [batch, 1]) - return encoder_outputs, targets - - def testDecoderConstruction(self): - _ = self._ConstructTransformerBatchMajorDecoder() - - def testDecoderConstructionPackedInput(self): - self._ConstructTransformerBatchMajorDecoder(packed_input=True) - - @parameterized.named_parameters(('TBC', 'TBC'), ('BTC', 'BTC')) - def testDecoderFProp(self, prediction_data_format): - with self.session(use_gpu=True) as sess: - dec = self._ConstructTransformerBatchMajorDecoder( - prediction_data_format=prediction_data_format, - per_example_tensors=True) - encoder_outputs, targets = self._Inputs() - dec_out = dec.FPropDefaultTheta(encoder_outputs, targets) - dec_out = tf.nest.map_structure(tf.convert_to_tensor, dec_out) - tf.global_variables_initializer().run() - actual_dec_out = sess.run(dec_out) - print('actual decoder output =', actual_dec_out) - self.assertAllClose(27.047781, actual_dec_out.metrics['loss'][0]) - - def testDecoderFPropPackedInput(self): - with self.session(use_gpu=True) as sess: - dec = self._ConstructTransformerBatchMajorDecoder(packed_input=True) - encoder_outputs, targets = self._Inputs(packed_input=True) - loss, _ = dec.FPropDefaultTheta(encoder_outputs, targets).metrics['loss'] - tf.global_variables_initializer().run() - actual_loss = sess.run(loss) - print('actual loss = ', actual_loss) - self.assertAllClose(15.041874, actual_loss) - - def testDecoderExtendStep(self): - with self.session(use_gpu=True) as sess: - dec = self._ConstructTransformerBatchMajorDecoder() - encoder_outputs, targets = self._Inputs() - - layer_out1 = dec._FProp(dec.theta, encoder_outputs, targets) - - prefix_states = py_utils.NestedMap() - for i in range(6): - layer_i_states = py_utils.NestedMap() - layer_i_states.key = tf.zeros([5, 4, 2, 2]) - layer_i_states.value = tf.zeros([5, 4, 2, 2]) - prefix_states['layer_%i' % i] = layer_i_states - - layer_out2 = [] - for i in range(5): - layer_i_out, prefix_states = dec.ExtendStep( - dec.theta, encoder_outputs, tf.expand_dims(targets.ids[:, i], 1), i, - prefix_states) - layer_out2.append(layer_i_out) - layer_out2 = tf.stack(layer_out2) - - tf.global_variables_initializer().run() - actual_layer_out1, actual_layer_out2 = sess.run([layer_out1, layer_out2]) - self.assertAllClose(actual_layer_out1, actual_layer_out2) - - def testBeamSearchDecode(self): - with self.session(use_gpu=True) as sess: - dec = self._ConstructTransformerBatchMajorDecoder() - encoder_outputs, _ = self._Inputs() - decode = dec.BeamSearchDecode(encoder_outputs) - # topk_decoded is None in MT decoder, set it to a fake tensor to pass - # sess.run(decode). - decode = decode._replace(topk_decoded=tf.constant(0, tf.float32)) - - tf.global_variables_initializer().run() - actual_decode = sess.run(decode) - - source_batch = 4 - source_time = 5 - num_hyps_per_beam = 2 - self.assertTupleEqual((source_time, source_batch * num_hyps_per_beam), - actual_decode.done_hyps.shape) - self.assertTupleEqual((source_batch, num_hyps_per_beam), - actual_decode.topk_hyps.shape) - self.assertTupleEqual((source_batch * num_hyps_per_beam, source_time), - actual_decode.topk_ids.shape) - self.assertTupleEqual((source_batch * num_hyps_per_beam,), - actual_decode.topk_lens.shape) - self.assertTupleEqual((source_batch, num_hyps_per_beam), - actual_decode.topk_scores.shape) - - expected_topk_ids = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [8, 16, 2, 0, 0], - [8, 11, 7, 2, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], - [8, 8, 8, 2, 0], [8, 11, 7, 2, 0]] - expected_topk_lens = [0, 0, 3, 4, 0, 0, 4, 4] - expected_topk_scores = [[0., 0.], [-3.154168, -3.2543223], [0., 0.], - [-4.8691816, -4.9210157]] - tf.logging.info(['testBeamSearchDecode actual_decode', actual_decode]) - - self.assertAllEqual(expected_topk_ids, actual_decode.topk_ids) - self.assertAllEqual(expected_topk_lens, actual_decode.topk_lens) - self.assertAllClose(expected_topk_scores, actual_decode.topk_scores) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/mt/encoder.py b/lingvo/tasks/mt/encoder.py deleted file mode 100644 index 1730164c..00000000 --- a/lingvo/tasks/mt/encoder.py +++ /dev/null @@ -1,924 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Encoders for the machine translation model. -""" - -import math -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import batch_major_attention -from lingvo.core import layers -from lingvo.core import model_helper -from lingvo.core import py_utils -from lingvo.core import rnn_cell -from lingvo.core import summary_utils -from lingvo.tasks.mt import layers as mt_layers - -tf.flags.DEFINE_bool('transformer_encoder_truncates_inputs', False, - 'Whether TransformerEncoder truncates inputs to max len.') - - -class MTEncoderV1(base_layer.BaseLayer): - """Machine translation encoder version 1.""" - - @classmethod - def Params(cls): - """Configs for `MTEncoderV1`.""" - p = super().Params() - p.Define('emb', layers.EmbeddingLayer.Params(), 'Embedding layer params.') - p.Define('lstm_tpl', - rnn_cell.LSTMCellSimple.Params(), - 'Configs template for the RNN layer.') - p.Define('lstm_tpl_uni', None, - 'Override configs template for the unidirectional RNN layers.') - p.Define('lstm_tpl_bidi', None, - 'Override configs template for the bidirectional RNN layer.') - p.Define('lstm_cell_size', 1024, 'LSTM cell size for the RNN layer.') - p.Define('num_lstm_layers', 8, 'Number of rnn layers to create') - p.Define('dropout_prob', 0.0, 'Prob at which we do dropout.') - p.Define('unidi_rnn_type', 'func', 'Options: func. ' 'func: FRNN.') - p.Define('bidi_rnn_type', 'func', 'Options: func. ' - 'func: BidirectionalFRNN. ') - p.Define('cc_schedule', None, 'Clipping cap schedule.') - p.Define( - 'packed_input', False, 'If True, encoder and all layers support ' - 'multiple examples in a single sequence.') - - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - default_params_init = py_utils.WeightInit.Uniform(0.04) - - # Default config for the embedding. - p.emb.vn = disable_vn - p.emb.vocab_size = 32000 - p.emb.embedding_dim = 1024 - p.emb.max_num_shards = 16 - p.emb.params_init = default_params_init - - for tpl in [p.lstm_tpl, p.lstm_tpl_uni, p.lstm_tpl_bidi]: - if tpl is not None: - tpl.vn = disable_vn - tpl.params_init = default_params_init - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert not p.packed_input, ('Packed inputs are not yet supported for ' - 'MTEncoderV1.') - - if p.cc_schedule is not None: - self.CreateChild('cc_schedule', p.cc_schedule) - - self.CreateChild('emb', p.emb) - - rnn_layers_params = [] - - # L0 is a bi-directional lstm. - - # L0's forward lstm cell - if p.lstm_tpl_bidi is None: - params = p.lstm_tpl.Copy() - else: - params = p.lstm_tpl_bidi.Copy() - params.name = 'L0_rnn_fwd' - params.num_input_nodes = p.emb.embedding_dim - params.num_output_nodes = p.lstm_cell_size - forward_lstm = params - - # L0's backward lstm cell - params = params.Copy() - params.name = 'L0_rnn_bak' - backward_lstm = params - - # L0 layer. - params = model_helper.CreateBidirectionalRNNParams(self.params, - forward_lstm, - backward_lstm) - params.name = 'L0' - rnn_layers_params.append(params) - - # The latter layers are all uni-directional lstm. - input_size = 2 * p.lstm_cell_size - for i in range(1, p.num_lstm_layers): - # Forward lstm cell. - if p.lstm_tpl_uni is None: - cell = p.lstm_tpl.Copy() - else: - cell = p.lstm_tpl_uni.Copy() - cell.name = 'L%d_rnn' % i - cell.num_input_nodes = input_size - cell.num_output_nodes = p.lstm_cell_size - # Forward lstm layer. - params = model_helper.CreateUnidirectionalRNNParams(self.params, cell) - params.name = 'L%d' % i - rnn_layers_params.append(params) - input_size = p.lstm_cell_size - - self.CreateChildren('rnn', rnn_layers_params) - - dropout_p = layers.DropoutLayer.Params().Set( - name='dropout_layer', - keep_prob=1.0 - p.dropout_prob, - random_seed=p.random_seed + 84828474 if p.random_seed else None) - self.CreateChild('dropout', dropout_p) - - def ApplyClipping(self, theta, x): - p = self.params - if not p.cc_schedule: - return x - cap = tf.cast(self.cc_schedule.GetState(theta.cc_schedule), x.dtype) - return tf.clip_by_value(x, -cap, cap) - - def FProp(self, theta, input_batch): - """Encodes source as represented by `inputs` and `paddings`. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - input_batch: A `.NestedMap` with fields: - - ids: The inputs tensor. It is expected to be of shape [batch, time]. - - paddings: The paddings tensor. Expected shape [batch, time]. - - Returns: - A NestedMap containing: - - - encoded: The encoded features, a tensor of shape [time, batch, depth] - - padding: of shape [time, batch] - - segment_id: [time, batch] if packed inputs are supported by the model - (and all layers), or None otherwise. - """ - p = self.params - src_segment_id = None - with tf.name_scope(p.name): - # Now the rnn layers. - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(input_batch.ids), [-1, -1]), - py_utils.assert_shape_match( - tf.shape(input_batch.ids), tf.shape(input_batch.paddings)) - ], tf.transpose(input_batch.ids)) - paddings = tf.expand_dims(tf.transpose(input_batch.paddings), 2) - xs = self.emb.EmbLookup(theta.emb, inputs) - xs = self.ApplyClipping(theta, xs) - self._emb_out = xs - ps = paddings - # When cc_schedule is specified, make sure lstm_tpl is QuantizedLSTMCell - # with the same cc_schedule so that the RNN layer output is within - # clipping range. - xs = self.rnn[0].FProp(theta.rnn[0], xs, ps) - xs = self.dropout.FProp(theta.dropout, xs) - for i in range(1, p.num_lstm_layers): - layer = self.rnn[i] - ys, _ = layer.FProp(theta.rnn[i], xs, ps) - ys = self.dropout.FProp(theta.dropout, ys) - if hasattr(layer.params, 'cell'): - layer_params = layer.params.cell - else: - layer_params = layer.params - if layer_params.num_input_nodes == layer_params.num_output_nodes: - xs += ys # Residual skip - xs = self.ApplyClipping(theta, xs) - else: - # When cc_schedule is specified, make sure lstm_tpl is - # QuantizedLSTMCell with the same cc_schedule so that the RNN layer - # output is within clipping range. - xs = ys - return py_utils.NestedMap( - encoded=xs, padding=tf.squeeze(ps, [2]), segment_id=src_segment_id) - - -class MTEncoderUniRNN(base_layer.BaseLayer): - """MT encoder that consists of a stack of uni-directional RNN layers.""" - - @classmethod - def Params(cls): - """Configs for `MTEncoderUniRNN`.""" - p = super().Params() - p.Define('emb', layers.EmbeddingLayer.Params(), 'Embedding layer params.') - p.Define('lstm_tpl', rnn_cell.LSTMCellSimple.Params(), - 'Configs template for the RNN layer.') - p.Define('lstm_cell_size', 512, 'LSTM cell size for the RNN layer.') - p.Define('num_lstm_layers', 8, 'Number of rnn layers to create') - p.Define('dropout_prob', 0.0, 'Prob at which we do dropout.') - p.Define('residual_start', 2, - 'Layer at which we start residual connections.') - p.Define('unidi_rnn_type', 'func', 'Options: func. ' 'func: FRNN.') - p.Define('cc_schedule', None, 'Clipping cap schedule.') - - p.Define('is_transparent', False, - 'If set, outputs a merger of layer outputs.') - p.Define( - 'transparent_merger_tpl', - layers.WeightedSumLayer.Params().Set(add_weight_summaries=True), - 'Merger op for layer outputs.') - p.Define( - 'packed_input', False, 'If True, encoder and all layers support ' - 'multiple examples in a single sequence.') - - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - default_params_init = py_utils.WeightInit.Uniform(0.04) - - # Default config for the embedding. - p.emb.vn = disable_vn - p.emb.vocab_size = 32000 - p.emb.embedding_dim = 1024 - p.emb.max_num_shards = 16 - p.emb.params_init = default_params_init - - p.lstm_tpl.vn = disable_vn - p.lstm_tpl.params_init = default_params_init - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert not p.packed_input, ('Packed inputs are not yet supported for ' - 'MTEncoderUniRNN.') - - if p.cc_schedule is None: - self.cc_schedule = None - else: - self.CreateChild('cc_schedule', p.cc_schedule) - - self.CreateChild('emb', p.emb) - - rnn_layers_params = [] - - num_input_nodes = p.emb.embedding_dim - for i in range(p.num_lstm_layers): - cell = p.lstm_tpl.Copy() - cell.name = 'L%d_rnn' % i - cell.num_input_nodes = num_input_nodes - cell.num_output_nodes = p.lstm_cell_size - params = model_helper.CreateUnidirectionalRNNParams(self.params, cell) - params.name = 'L%d' % i - rnn_layers_params.append(params) - num_input_nodes = cell.num_output_nodes - - self.CreateChildren('rnn', rnn_layers_params) - - dropout_p = layers.DropoutLayer.Params().Set( - name='dropout_layer', - keep_prob=1.0 - p.dropout_prob, - random_seed=p.random_seed + 827366448 if p.random_seed else None) - self.CreateChild('dropout', dropout_p) - - if p.is_transparent: - transparent_params = p.transparent_merger_tpl.Copy() - transparent_params.name = 'transparent' - transparent_params.num_sources = p.num_lstm_layers - self.CreateChild('transparent_merger', transparent_params) - - def ApplyClipping(self, theta, x): - if self.cc_schedule: - return self.cc_schedule.ApplyClipping(theta.cc_schedule, x) - else: - return x - - def zero_state(self, theta, batch_size): - return py_utils.NestedMap(rnn=[ - self.rnn[i].zero_state(theta.rnn[i], batch_size) - for i in range(len(self.rnn)) - ]) - - def FProp(self, theta, input_batch, state0=None): - p = self.params - src_segment_id = None - with tf.name_scope(p.name): - # Reshape to [t, b] - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(input_batch.ids), [-1, -1]), - py_utils.assert_shape_match( - tf.shape(input_batch.ids), tf.shape(input_batch.paddings)) - ], tf.transpose(input_batch.ids)) - paddings = tf.expand_dims(tf.transpose(input_batch.paddings), 2) - - # Setup streaming states. - if not state0: - state0 = self.zero_state(theta, tf.shape(inputs)[1]) - state1 = py_utils.NestedMap(rnn=[None] * p.num_lstm_layers) - - xs = self.emb.EmbLookup(theta.emb, inputs) - xs = self.ApplyClipping(theta, xs) - summary_utils.histogram('input_emb', xs) - xs = self.dropout.FProp(theta.dropout, xs) - ps = paddings - # Now the rnn layers. - outputs_list = [] - for i in range(0, p.num_lstm_layers): - layer = self.rnn[i] - ys, state1.rnn[i] = layer.FProp( - theta.rnn[i], xs, ps, state0=state0.rnn[i]) - ys = self.dropout.FProp(theta.dropout, ys) - if i >= p.residual_start: - xs += ys # Residual skip - xs = self.ApplyClipping(theta, xs) - else: - xs = ys - outputs_list.append(xs) - summary_utils.histogram('layer_out_%s' % i, xs) - - if p.is_transparent: - xs = self.transparent_merger.FProp(theta.transparent_merger, - outputs_list) - - return py_utils.NestedMap( - encoded=xs, - padding=tf.squeeze(ps, [2]), - segment_id=src_segment_id, - state=state1) - - -class MTEncoderBiRNN(base_layer.BaseLayer): - """MT encoder that consists of a stack of bi-directional RNN layers.""" - - @classmethod - def Params(cls): - """Configs for `MTEncoderBiRNN`.""" - p = super().Params() - p.Define('emb', layers.EmbeddingLayer.Params(), 'Embedding layer params.') - p.Define('lstm_tpl', - rnn_cell.LSTMCellSimple.Params(), - 'Configs template for the RNN layer.') - p.Define('proj_tpl', layers.ProjectionLayer.Params(), - 'Configs template for the projection layer.') - p.Define('lstm_cell_size', 512, 'LSTM cell size for the RNN layer.') - p.Define('num_lstm_layers', 8, 'Number of rnn layers to create') - p.Define('dropout_prob', 0.0, 'Prob at which we do dropout.') - p.Define('residual_start', 2, - 'Layer at which we start residual connections.') - p.Define('encoder_out_dim', 1024, 'Depth of the encoder output.') - p.Define('bidi_rnn_type', 'func', 'Options: func. ' - 'func: BidirectionalFRNN. ') - p.Define('cc_schedule', None, 'Clipping cap schedule.') - - p.Define('is_transparent', False, - 'If set, outputs a merger of layer outputs.') - p.Define( - 'transparent_merger_tpl', - layers.WeightedSumLayer.Params().Set(add_weight_summaries=True), - 'Merger op for layer outputs.') - p.Define( - 'packed_input', False, 'If True, encoder and all layers support ' - 'multiple examples in a single sequence.') - - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - default_params_init = py_utils.WeightInit.Uniform(0.04) - - # Default config for the embedding. - p.emb.vn = disable_vn - p.emb.vocab_size = 32000 - p.emb.embedding_dim = 1024 - p.emb.max_num_shards = 16 - p.emb.params_init = default_params_init - - p.lstm_tpl.vn = disable_vn - p.lstm_tpl.params_init = default_params_init - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - if p.cc_schedule is None: - self.cc_schedule = None - else: - self.CreateChild('cc_schedule', p.cc_schedule) - - self.CreateChild('emb', p.emb) - - rnn_layers_params = [] - - for i in range(p.num_lstm_layers): - params = p.lstm_tpl.Copy() - params.name = 'L%d_rnn_fwd' % i - if i == 0: - params.num_input_nodes = p.emb.embedding_dim - else: - params.num_input_nodes = 2 * p.lstm_cell_size - params.num_output_nodes = p.lstm_cell_size - params.reset_cell_state = p.packed_input - forward_lstm = params - - params = params.Copy() - params.name = 'L%d_rnn_bak' % i - params.reset_cell_state = p.packed_input - backward_lstm = params - - params = model_helper.CreateBidirectionalRNNParams( - self.params, forward_lstm, backward_lstm) - params.packed_input = p.packed_input - params.name = 'L%d' % i - rnn_layers_params.append(params) - - self.CreateChildren('rnn', rnn_layers_params) - - if p.lstm_cell_size * 2 != p.encoder_out_dim: - # Project the encoder output to the desired dim. - proj_p = p.proj_tpl.Copy().Set( - name='proj', - batch_norm=False, - input_dim=p.lstm_cell_size * 2, - output_dim=p.encoder_out_dim) - if p.cc_schedule is not None: - proj_p.has_bias = False - proj_p.activation = 'TANH' - else: - proj_p.has_bias = True - proj_p.activation = 'NONE' - self.CreateChild('final_proj', proj_p) - - dropout_p = layers.DropoutLayer.Params().Set( - name='dropout_layer', - keep_prob=1.0 - p.dropout_prob, - random_seed=p.random_seed + 827366448 if p.random_seed else None) - self.CreateChild('dropout', dropout_p) - - if p.is_transparent: - transparent_params = p.transparent_merger_tpl.Copy() - transparent_params.name = 'transparent' - transparent_params.num_sources = p.num_lstm_layers - self.CreateChild('transparent_merger', transparent_params) - - def ApplyClipping(self, theta, x): - if self.cc_schedule: - return self.cc_schedule.ApplyClipping(theta.cc_schedule, x) - else: - return x - - def FProp(self, theta, input_batch): - p = self.params - with tf.name_scope(p.name): - inputs = py_utils.with_dependencies([ - py_utils.assert_shape_match(tf.shape(input_batch.ids), [-1, -1]), - py_utils.assert_shape_match( - tf.shape(input_batch.ids), tf.shape(input_batch.paddings)) - ], tf.transpose(input_batch.ids)) - paddings = tf.expand_dims(tf.transpose(input_batch.paddings), 2) - if p.packed_input: - src_segment_id = tf.expand_dims( - tf.transpose(input_batch.segment_ids), 2) - else: - src_segment_id = None - xs = self.emb.EmbLookup(theta.emb, inputs) - xs = self.ApplyClipping(theta, xs) - summary_utils.histogram('input_emb', xs) - xs = self.dropout.FProp(theta.dropout, xs) - ps = paddings - # Now the rnn layers. - outputs_list = [] - for i in range(0, p.num_lstm_layers): - layer = self.rnn[i] - ys = layer.FProp(theta.rnn[i], xs, ps, segment_id=src_segment_id) - ys = self.dropout.FProp(theta.dropout, ys) - if i >= p.residual_start: - xs += ys # Residual skip - xs = self.ApplyClipping(theta, xs) - else: - xs = ys - outputs_list.append(xs) - summary_utils.histogram('layer_out_%s' % i, xs) - - if p.is_transparent: - xs = self.transparent_merger.FProp(theta.transparent_merger, - outputs_list) - - if p.lstm_cell_size * 2 != p.encoder_out_dim: - # Project to the right depth. - xs = self.final_proj.FProp(theta.final_proj, xs, ps) - summary_utils.histogram('final_proj_out', xs) - - if src_segment_id is not None: - src_segment_id = tf.squeeze(src_segment_id, [2]) - - return py_utils.NestedMap( - encoded=xs, padding=tf.squeeze(ps, [2]), segment_id=src_segment_id) - - -class TransformerEncoder(base_layer.BaseLayer): - """Transformer stack with sinusoidal positional embeddings and attention. - - Implements the encoder of 'Attention is All You Need': - https://arxiv.org/abs/1706.03762. - """ - - @classmethod - def Params(cls): - """Configs for `TransformerEncoder`.""" - p = super().Params() - - # Embedding related - p.Define('token_emb', - layers.EmbeddingLayer.Params().Set( - vocab_size=32000, - embedding_dim=1024, - max_num_shards=16, - params_init=py_utils.WeightInit.Gaussian( - 1.0 / math.sqrt(1024)), - scale_sqrt_depth=True), 'Embedding layer params.') - - p.Define('shared_emb', None, 'Embedding shared with Decoder.') - - # Positional embedding related - p.Define( - 'position_emb', - layers.PositionalEmbeddingLayer.Params().Set(embedding_dim=1024), - 'Positional Embedding layer params.') - - # TODO(miachen): Extend this to more general logic of adding multiple - # embedding fields. - # Task embedding related - p.Define('task_emb', None, 'Task embedding layer params.') - - p.Define('model_dim', 1024, 'Characteristic depth (dimension).') - p.Define('input_dropout_prob', 0.0, 'Prob at which we do input dropout.') - - p.Define('transformer_stack', mt_layers.TransformerStack.Params(), - 'TransformerStack layer params.') - p.Define( - 'packed_input', False, 'If True, encoder and all layers support ' - 'multiple examples in a single sequence.') - - # MASS pretraining related (https://github.com/microsoft/MASS). - p.Define( - 'apply_source_mask', False, 'If True, apply source mask ' - '(corresponding to those masked words) to encoder states.') - p.Define( - 'source_mask_id', 0, 'Id for masked words in source inputs. ' - 'Only needed when p.apply_source_mask is True.') - - p.transformer_stack.num_transformer_layers = 6 - p.transformer_stack.transformer_tpl.tr_atten_tpl.num_attention_heads = 8 - p.transformer_stack.transformer_tpl.tr_fflayer_tpl.hidden_dim = 8192 - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - if p.shared_emb: - # Naming this 'softmax' to match the name of the same component in the - # decoder. Variable names need to be the same in order to be reused. - self.CreateChild('softmax', p.shared_emb) - - assert p.token_emb.embedding_dim == p.position_emb.embedding_dim - p.transformer_stack.Set(model_dim=p.model_dim, packed_input=p.packed_input) - if p.model_dim != p.token_emb.embedding_dim: - tf.logging.warning('token_emb.embedding_dim != model_dim (%s vs. %s), ' - 'creating a projection!') - proj_p = layers.ProjectionLayer.Params().Copy() - proj_p.name = 'emb_proj' - proj_p.input_dim = p.token_emb.embedding_dim - proj_p.output_dim = p.model_dim - proj_p.batch_norm = True - self.CreateChild('emb_proj', proj_p) - - # Token embeddings - if not p.shared_emb: - p.token_emb.dtype = p.dtype - self.CreateChild('token_emb', p.token_emb) - - # Positional embeddings - self.CreateChild('position_emb', p.position_emb) - - # Task embeddings. - if p.task_emb: - assert p.task_emb.embedding_dim == p.token_emb.embedding_dim - self.CreateChild('task_emb', p.task_emb) - - dropout_tpl = layers.DropoutLayer.Params() - dropout_tpl.keep_prob = (1.0 - p.input_dropout_prob) - self.CreateChild('input_dropout', dropout_tpl) - - p.transformer_stack.name = p.name - self.CreateChild('transformer_stack', p.transformer_stack) - - def _CreateChildrenVariables(self): - if self.params.shared_emb: - with tf.variable_scope('shared_emb', reuse=tf.AUTO_REUSE): - self.softmax.InstantiateVariables() - self.transformer_stack.InstantiateVariables() - super()._CreateChildrenVariables() - - def FProp(self, theta, input_batch): - """Embeds source ids and transforms with TransformerStack. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - input_batch: A `.NestedMap` with fields: - - - ids: The inputs tensor. It is expected to be of shape [batch, time]. - - paddings: The paddings tensor. Expected shape [batch, time]. - - task_ids: If p.task_emb is provided, must contain per-token task - ids of shape [batch, time]. - - Returns: - A NestedMap containing - - - encoded: The encoded features, either a tensor of shape - [time, batch, depth], or a list of tensors if is_transparent is set in - transformer_stack. - - padding: of shape [time, batch] - - segment_id: [time, batch] if packed inputs are supported by the model - (and all layers), or None otherwise. - - embedded_inputs: [time, batch, depth] embedded inputs tokens without - positional encodings. - """ - - p = self.params - with tf.name_scope(p.name): - src_segment_id = None - src_segment_pos = None - input_ids = py_utils.with_dependencies([ - py_utils.assert_shape_match( - tf.shape(input_batch.ids), tf.shape(input_batch.paddings)), - py_utils.assert_equal(tf.rank(input_batch.ids), 2) - ], input_batch.ids) - - if (not py_utils.use_tpu() and - tf.flags.FLAGS.transformer_encoder_truncates_inputs): - max_seq_length = tf.cast( - tf.reduce_max(tf.reduce_sum(1.0 - input_batch.paddings, 1)), - tf.int32) - paddings = py_utils.with_dependencies([ - py_utils.assert_equal( - tf.constant(True, tf.bool), - tf.reduce_all(input_batch.paddings[:, max_seq_length:] > 0.5)) - ], input_batch.paddings) - input_ids = input_ids[:, :max_seq_length] - paddings = paddings[:, :max_seq_length] - if p.packed_input: - src_segment_id = input_batch.segment_ids[:, :max_seq_length] - src_segment_pos = input_batch.segment_pos[:, :max_seq_length] - else: - paddings = input_batch.paddings - if p.packed_input: - src_segment_id = input_batch.segment_ids - src_segment_pos = input_batch.segment_pos - - max_time = tf.shape(input_ids)[1] - - # Input token embeddings + positional embeddings - if not p.shared_emb: - input_embs = self.token_emb.EmbLookup(theta.token_emb, - tf.reshape(input_ids, [-1])) - else: - input_embs = self.softmax.EmbLookup(theta.softmax, - tf.reshape(input_ids, [-1])) - - input_embs = tf.reshape(input_embs, - [-1, max_time, p.token_emb.embedding_dim]) - # [time, batch, dim] - orig_input_embs = tf.transpose(input_embs, [1, 0, 2]) - - if p.packed_input: - position_embs = self.position_emb.FPropWithPosition( - theta.position_emb, src_segment_pos) - else: - position_embs = self.position_emb.FProp(theta.position_emb, max_time) - position_embs = tf.reshape(position_embs, - [1, max_time, p.token_emb.embedding_dim]) - input_embs += position_embs - if p.task_emb: - input_embs += self.task_emb.EmbLookup(theta.task_emb, - input_batch.task_ids) - - if p.model_dim != p.token_emb.embedding_dim: - input_embs = self.emb_proj.FProp(theta.emb_proj, input_embs) - - paddings = tf.cast(tf.transpose(paddings), py_utils.FPropDtype(p)) - if p.packed_input: - src_segment_id = tf.transpose(src_segment_id) - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - - # [time, batch, dim] - transformer_input = tf.transpose(input_embs, [1, 0, 2]) - - if not self.do_eval and p.apply_source_mask: - # Augment padding for masked source word positions. - dtype = paddings.dtype - source_mask = tf.where( - tf.equal(input_ids, p.source_mask_id), - tf.ones_like(input_ids, dtype=dtype), - tf.zeros_like(input_ids, dtype=dtype)) - # Make sure padding is between 0 and 1. - paddings = tf.clip_by_value(paddings + tf.transpose(source_mask), 0.0, - 1.0) - - encoded, padding, segment_id = self.transformer_stack.FProp( - theta.transformer_stack, transformer_input, paddings, src_segment_id) - return py_utils.NestedMap( - encoded=encoded, - padding=padding, - segment_id=segment_id, - embedded_inputs=orig_input_embs) - - def FPropFullSequence(self, theta, ids, paddings): - return self.FProp(theta, py_utils.NestedMap(ids=ids, - paddings=paddings))['encoded'] - - -class TransformerBatchMajorEncoder(base_layer.BaseLayer): - """Transformer encoder with batch major implementation. - - This encoder first applies dropout to the input embeddings, - then returns encoded output produced by p.transformer_stack using - self_attention_layer builder. - - Example definition for a stack of 6 transformer layers: - - builder_params = self_attention_layer.Builder.Params().Set( - model_dim=model_dim, - ff_hidden_dim=ff_hidden_dim, - num_heads=num_heads, - selfatten_add_unnormalized_input=False, - selfatten_enable_value_proj=True) - - p.transformer_stack = builder_params.Instantiate().TransformerStack( - 'transformer_stack', 6) - - Implements the encoder of 'Attention is All You Need': - https://arxiv.org/abs/1706.03762. - """ - - @classmethod - def Params(cls): - p = super().Params() - - # Default config for the token embedding. - p.Define( - 'token_emb', - layers.EmbeddingLayer.Params().Set( - vocab_size=32000, - embedding_dim=1024, - max_num_shards=16, - params_init=py_utils.WeightInit.Gaussian(1.0 / math.sqrt(1024)), - scale_sqrt_depth=True), 'Embedding layer params.') - - p.Define('shared_emb', None, 'Embedding shared with Decoder.') - - # Default config for the position embedding. - p.Define('position_emb', - layers.PositionalEmbeddingLayer.Params().Set(embedding_dim=1024), - 'Positional Embedding layer params.') - - p.Define('model_dim', 1024, 'Characteristic depth (dimension).') - p.Define('input_dropout_prob', 0.0, 'Prob at which we do input dropout.') - p.Define('input_dropout_tpl', layers.DropoutLayer.Params(), - 'Input dropout layer params.') - p.Define('transformer_stack', None, 'TransformerStack layer params.') - p.Define( - 'packed_input', False, 'If True, encoder and all layers support ' - 'multiple examples in a single sequence.') - p.Define('final_layer_norm', False, - 'Whether or not to apply the final layer normalization.') - p.Define('use_fused_layernorm', False, 'Whether to use fused layernorm.') - p.Define( - 'output_data_format', 'TBC', 'The data format of output features: ' - 'TBC for [time, batch, feature_dim], ' - 'BTC for [batch, time, feature_dim].') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - assert p.output_data_format in ('TBC', 'BTC') - - if p.shared_emb: - self.CreateChild('softmax', p.shared_emb) - - p.token_emb.dtype = p.dtype - if not p.shared_emb: - self.CreateChild('token_emb', p.token_emb) - self.CreateChild('position_emb', p.position_emb) - - dropout_tpl = p.input_dropout_tpl.Copy() - dropout_tpl.keep_prob = (1.0 - p.input_dropout_prob) - self.CreateChild('input_dropout', dropout_tpl) - - if p.transformer_stack: - self.CreateChild('transformer_stack', p.transformer_stack) - - if p.final_layer_norm: - layer_norm_p = layers.LayerNorm.Params().Set( - name='final_ln', - input_dim=p.model_dim, - use_fused_layernorm=p.use_fused_layernorm, - fprop_dtype=p.input_dropout_tpl.fprop_dtype) - self.CreateChild('final_ln', layer_norm_p) - - def _CreateChildrenVariables(self): - if self.params.shared_emb: - with tf.variable_scope('shared_emb', reuse=tf.AUTO_REUSE): - self.softmax.InstantiateVariables() - super()._CreateChildrenVariables() - - def FProp(self, theta, input_batch): - """Embeds source ids and transforms with TransformerStack. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - input_batch: A `.NestedMap` object containing: ids - The inputs tensor of - shape [batch, time]. paddings - The ids' paddings of shape [batch, - time]. - - Returns: - A '.NestedMap' object containing: - encoded - The encoded features of shape [time, batch, dim] or [batch, - time, dim], depending p.output_data_format. - padding - The encoded features' padding of shape [time, batch] or - [batch, time]. - segment_id - The segmentation of packed inputs of shape [time, batch] or - [batch, time] if it is supported by the model, or None otherwise. - embedded_inputs - The embedded inputs tokens without positional - encodings of shape [time, batch, dim] or [batch, time, dim]. - """ - - p = self.params - with tf.name_scope(p.name): - # [batch, time] - input_ids = input_batch.ids - # [batch, time] - paddings = input_batch.paddings - - # [batch, time] - segment_ids = input_batch.segment_ids if p.packed_input else None - - batch = py_utils.GetShape(input_ids)[0] - time = py_utils.GetShape(input_ids)[1] - - # Embedding layer. - # [batch, time, dim] - if not p.shared_emb: - input_embs = self.token_emb.EmbLookup(theta.token_emb, input_ids) - else: - input_embs = self.softmax.EmbLookup(theta.softmax, input_ids) - orig_input_embs = input_embs - - # [1, time, dim] - if p.packed_input: - positions = input_batch.segment_pos - position_embs = tf.expand_dims( - self.position_emb.FPropWithPosition(theta.position_emb, positions), - 0) - else: - position_embs = tf.expand_dims( - self.position_emb.FProp(theta.position_emb, time), 0) - - # [batch, time, dim] - input_embs += position_embs - - if p.input_dropout_tpl.fprop_dtype: - input_embs = tf.cast(input_embs, p.input_dropout_tpl.fprop_dtype) - paddings = tf.cast(paddings, p.input_dropout_tpl.fprop_dtype) - - input_embs = self.input_dropout.FProp(theta.input_dropout, input_embs) - # [batch, time, dim] - transformer_input = input_embs - # Explicitly set the input shape of Transformer layers, to avoid - # unknown shape error occurred to tf.einsum on nonTPU devices. - transformer_input = tf.reshape(transformer_input, - [batch, time, p.model_dim]) - - # Compute self-attention segment mask once. - if p.packed_input: - segment_mask = batch_major_attention.SegmentMask( - segment_ids, segment_ids, dtype=transformer_input.dtype) - else: - segment_mask = tf.zeros([batch, 1, time, time]) - - encoded, padding = self.transformer_stack.FProp(theta.transformer_stack, - transformer_input, - paddings, segment_mask) - - if p.final_layer_norm: - encoded = self.final_ln.FProp(theta.final_ln, encoded) - - seq_lengths = tf.cast(tf.reduce_sum(1. - padding, axis=1), tf.int32) - - if p.output_data_format == 'TBC': - encoded = tf.transpose(encoded, [1, 0, 2]) # [time, batch, dim] - padding = tf.transpose(padding) # [time, batch] - segment_ids = tf.transpose(segment_ids) if p.packed_input else None - orig_input_embs = tf.transpose(orig_input_embs, [1, 0, 2]) - - return py_utils.NestedMap( - encoded=encoded, - padding=padding, - seq_lengths=seq_lengths, # used by beam_search_helper. - segment_id=segment_ids, - embedded_inputs=orig_input_embs) diff --git a/lingvo/tasks/mt/encoder_test.py b/lingvo/tasks/mt/encoder_test.py deleted file mode 100644 index 4081d017..00000000 --- a/lingvo/tasks/mt/encoder_test.py +++ /dev/null @@ -1,570 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Tests for mt.encoder.""" - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import self_attention_layer -from lingvo.core import test_utils -from lingvo.tasks.mt import encoder -import numpy as np - - -class EncoderTest(test_utils.TestCase): - - def _EncoderParams(self): - p = encoder.MTEncoderV1.Params() - p.name = 'encoder' - p.emb.vocab_size = 16 - p.emb.embedding_dim = 4 - p.emb.max_num_shards = 1 - p.lstm_cell_size = 4 - p.num_lstm_layers = 3 - p.random_seed = 837464 - return p - - def testEncoderConstruction(self): - p = self._EncoderParams() - _ = encoder.MTEncoderV1(p) - - def testForwardPass(self): - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - p = self._EncoderParams() - mt_enc = encoder.MTEncoderV1(p) - batch = py_utils.NestedMap() - batch.ids = tf.transpose(tf.reshape(tf.range(0, 8, 1), [4, 2])) - batch.paddings = tf.zeros([2, 4]) - enc_out = mt_enc.FPropDefaultTheta(batch).encoded - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out = enc_out.eval() - tf.logging.info('testForwardPass actual_enc_out %r' % actual_enc_out) - expected_enc_out = [ - [[-2.5584161e-06, -5.6742726e-07, -8.1548797e-06, 2.6712776e-06], - [1.1781749e-06, -4.7786052e-08, 4.2439538e-06, -3.3840388e-06]], - [[-2.6852279e-06, 2.0878532e-07, -1.0491179e-05, 5.9619756e-06], - [2.0423495e-06, 3.1651740e-07, 5.7234793e-06, -3.8120934e-06]], - [[3.0904158e-07, -1.2983286e-06, -1.2469604e-05, 6.6027828e-06], - [-3.8620223e-07, 3.8890593e-07, 1.9976458e-06, 1.0078909e-06]], - [[1.0130438e-07, -1.1145677e-06, -1.2745468e-05, 8.0924037e-06], - [-1.3496270e-06, -3.2355717e-06, -3.0266469e-06, -3.9747570e-06]] - ] - self.assertAllClose(expected_enc_out, actual_enc_out) - - def _UniEncoderParams(self): - p = encoder.MTEncoderUniRNN.Params() - p.name = 'encoder' - p.emb.vocab_size = 16 - p.emb.embedding_dim = 2 - p.emb.max_num_shards = 1 - p.lstm_cell_size = 2 - p.num_lstm_layers = 3 - p.random_seed = 837464 - return p - - def _BiEncoderParams(self): - p = encoder.MTEncoderBiRNN.Params() - p.name = 'encoder' - p.emb.vocab_size = 16 - p.emb.embedding_dim = 4 - p.emb.max_num_shards = 1 - p.lstm_cell_size = 4 - p.num_lstm_layers = 3 - p.encoder_out_dim = 2 - p.random_seed = 837464 - return p - - def testBiEncoderForwardPassWithInputPacking(self): - with self.session(use_gpu=False): - with tf.variable_scope('bienc_test', reuse=tf.AUTO_REUSE): - bs = 3 - sl = 3 - tf.random.set_seed(8372749040) - p = self._BiEncoderParams() - mt_enc = encoder.MTEncoderBiRNN(p) - packed_params = p.Copy() - packed_params.packed_input = True - mt_enc_packed = encoder.MTEncoderBiRNN(packed_params) - - batch = py_utils.NestedMap() - batch.ids = tf.constant( - np.random.randint(low=0, high=15, size=[bs, sl], dtype=np.int32)) - batch.paddings = tf.zeros([bs, sl]) - - packed_batch = py_utils.NestedMap() - packed_batch.ids = tf.reshape(batch.ids, [1, -1]) - packed_batch.paddings = tf.reshape(batch.paddings, [1, -1]) - packed_batch.segment_ids = tf.constant( - [[0, 0, 0, 1, 1, 1, 2, 2, 2]], dtype=tf.float32) - packed_batch.segment_pos = tf.constant( - [[0, 1, 2, 0, 1, 2, 0, 1, 2]], dtype=tf.int32) - enc_out = mt_enc.FPropDefaultTheta(batch).encoded - enc_out = tf.transpose(enc_out, [1, 0, 2]) - - packed_enc_out = mt_enc_packed.FPropDefaultTheta(packed_batch) - packed_enc_out = tf.reshape(packed_enc_out.encoded, tf.shape(enc_out)) - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out, actual_packed_enc_out = self.evaluate( - [enc_out, packed_enc_out]) - self.assertAllClose(actual_packed_enc_out, actual_enc_out) - - def testTransparentEncoderConstruction(self): - p = self._BiEncoderParams() - p.is_transparent = True - _ = encoder.MTEncoderBiRNN(p) - - def testUniEncoderForwardPass(self): - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - p = self._UniEncoderParams() - mt_enc = encoder.MTEncoderUniRNN(p) - batch = py_utils.NestedMap() - batch.ids = tf.transpose(tf.reshape(tf.range(0, 8, 1), [4, 2])) - batch.paddings = tf.zeros([2, 4]) - enc_out = mt_enc.FPropDefaultTheta(batch).encoded - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out = enc_out.eval() - tf.logging.info('testUniEncoderForwardPass actual_enc_out %r' % - actual_enc_out) - expected_enc_out = [[[-4.3304257e-07, 5.4100457e-07], - [-4.0170832e-07, -2.6441572e-07]], - [[-1.7024040e-07, -1.8555815e-07], - [-6.4563977e-07, -3.7835261e-07]], - [[-2.4001852e-07, 5.1114228e-07], - [-3.4349023e-07, -1.0049351e-06]], - [[1.8068013e-07, -6.8982729e-08], - [3.3005003e-07, -8.8834116e-07]]] - self.assertAllClose(expected_enc_out, actual_enc_out) - - def testBiEncoderForwardPass(self): - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - p = self._BiEncoderParams() - mt_enc = encoder.MTEncoderBiRNN(p) - batch = py_utils.NestedMap() - batch.ids = tf.transpose(tf.reshape(tf.range(0, 8, 1), [4, 2])) - batch.paddings = tf.zeros([2, 4]) - enc_out = mt_enc.FPropDefaultTheta(batch).encoded - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out = enc_out.eval() - tf.logging.info('testBiEncoderForwardPass actual_enc_out %r' % - actual_enc_out) - expected_enc_out = [[[-2.47998378e-06, 7.36457878e-06], - [7.89248020e-07, -2.67464316e-06]], - [[-2.98803275e-06, 8.20233890e-06], - [1.00139073e-06, -2.24554151e-06]], - [[-5.06675951e-06, 1.15983785e-05], - [-4.58391014e-07, -2.99553108e-07]], - [[-4.34937465e-06, 8.58816838e-06], - [-1.74859031e-06, 3.99598093e-06]]] - self.assertAllClose(expected_enc_out, actual_enc_out) - - def testBiEncoderForwardPassWithDropout(self): - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - p = self._BiEncoderParams() - p.dropout_prob = 0.5 - mt_enc = encoder.MTEncoderBiRNN(p) - batch = py_utils.NestedMap() - batch.ids = tf.transpose(tf.reshape(tf.range(0, 8, 1), [4, 2])) - batch.paddings = tf.zeros([2, 4]) - enc_out = mt_enc.FPropDefaultTheta(batch).encoded - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out = enc_out.eval() - print('bi_enc_actual_enc_out_with_dropout', np.array_repr(actual_enc_out)) - expected_enc_out = [[[-1.8358192e-05, 1.2103478e-05], - [2.9347059e-06, -3.0652325e-06]], - [[-8.1282624e-06, 4.5443494e-06], - [3.0826509e-06, -5.2950490e-06]], - [[-4.6669629e-07, 2.4246765e-05], - [-1.5221613e-06, -1.9654153e-06]], - [[-1.1511075e-05, 1.9061190e-05], - [-5.7250163e-06, 9.2785704e-06]]] - self.assertAllClose(expected_enc_out, actual_enc_out) - - def testBiEncoderForwardPassWithTransparent(self): - with self.session(use_gpu=False): - tf.random.set_seed(8372749040) - p = self._BiEncoderParams() - p.is_transparent = True - mt_enc = encoder.MTEncoderBiRNN(p) - batch = py_utils.NestedMap() - batch.ids = tf.transpose(tf.reshape(tf.range(0, 8, 1), [4, 2])) - batch.paddings = tf.zeros([2, 4]) - enc_out = mt_enc.FPropDefaultTheta(batch).encoded - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out = enc_out.eval() - tf.logging.info( - 'testBiEncoderForwardPassWithTransparent actual_enc_out %r' % - actual_enc_out) - expected_enc_out = [[[1.53976856e-04, -1.66475205e-04], - [-1.02031634e-04, 1.39693424e-04]], - [[1.62726530e-04, -2.22654475e-04], - [-4.89080339e-05, 1.10912690e-04]], - [[1.28586107e-04, -1.62333992e-04], - [7.22907062e-05, -9.17545694e-05]], - [[9.02724860e-05, -1.71898617e-04], - [-9.77059244e-06, 7.55862275e-05]]] - self.assertAllClose(expected_enc_out, actual_enc_out) - - -class TransformerEncoderTest(test_utils.TestCase): - - def _EncoderParams(self): - p = encoder.TransformerEncoder.Params() - p.name = 'transformer_encoder' - p.token_emb.params_init = py_utils.WeightInit.GaussianSqrtDim() - p.token_emb.vocab_size = 64 - p.token_emb.embedding_dim = 16 - p.token_emb.max_num_shards = 1 - p.token_emb.vn = py_utils.VariationalNoiseParams(1.0, False, False) - p.model_dim = 16 - p.position_emb.embedding_dim = 16 - ts = p.transformer_stack - ts.num_transformer_layers = 6 - ts.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - ts.transformer_tpl.tr_fflayer_tpl.hidden_dim = 5 - return p - - def testEncoderConstruction(self): - p = self._EncoderParams() - _ = encoder.TransformerEncoder(p) - - def testTransparentEncoderConstruction(self): - p = self._EncoderParams() - p.transformer_stack.is_transparent = True - p.transformer_stack.num_transparent_outputs = 2 - _ = encoder.TransformerEncoder(p) - - def testForwardPass(self): - with self.session(use_gpu=False): - bs = 2 - sl = 21 - tf.random.set_seed(8372749040) - p = self._EncoderParams() - mt_enc = encoder.TransformerEncoder(p) - batch = py_utils.NestedMap() - batch.ids = tf.constant( - np.random.randint(low=0, high=63, size=[bs, sl], dtype=np.int32)) - batch.paddings = tf.zeros([bs, sl]) - out = mt_enc.FPropDefaultTheta(batch) - enc_out_sum = tf.reduce_sum(out.encoded, 0) - emb_out_sum = tf.reduce_sum(out.embedded_inputs, 0) - enc_padding = out.padding - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out, actual_enc_out_sum, actual_emb_out_sum, \ - actual_padding = self.evaluate( - [out.encoded, enc_out_sum, emb_out_sum, enc_padding]) - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_enc_out = [ - [ 49.45291519, -31.5743885 , 39.43684387, -47.67513275, - 35.39754105, 14.41970444, 29.58752823, -43.06747055, - 24.09403419, -7.62717247, 18.48112106, 20.42408371, - 5.1519866 , -19.66542244, 29.81095314, 56.90407944], - [ 55.26333618, -30.39743614, 29.68314743, -37.61392975, - 43.02292252, 13.88345146, 15.73033905, -24.68696213, - 24.70776558, -29.18026161, 15.41469955, 27.77672577, - -5.36326742, -22.78984642, 22.15843391, 22.7237072 ]] - expected_emb_out_sum = [ - [ 3.11785889, 1.33086884, -1.96904886, -4.81911993, 1.25389254, - 1.52582073, 0.79906291, 4.07078457, -1.20546532, -2.97308111, - 0.22460097, 2.99702668, -2.29453254, 6.06631422, 1.68836212, - 5.35728741], - [ 1.41723049, -1.39409399, -1.49569404, -0.24654561, 1.09658146, - 4.51638842, 2.72023368, -0.45651400, 3.46091199, -0.43925080, - 1.02091551, 3.89704037, 1.87841535, -0.27947778, -0.91630745, - 1.34230828]] - # pylint: enable=bad-whitespace - # pyformat: enable - self.assertAllEqual(actual_enc_out.shape, [sl, bs, p.model_dim]) - self.assertAllEqual(actual_padding.shape, [sl, bs]) - self.assertAllClose( - expected_enc_out, actual_enc_out_sum, rtol=1e-05, atol=1e-05) - self.assertAllClose( - expected_emb_out_sum, actual_emb_out_sum, rtol=1e-05, atol=1e-05) - - def testForwardPassWithTaskEmb(self): - with self.session(use_gpu=False): - bs = 2 - sl = 21 - tf.random.set_seed(8372749040) - p = self._EncoderParams() - p.task_emb = p.token_emb.Copy() - p.task_emb.vocab_size = 4 - mt_enc = encoder.TransformerEncoder(p) - batch = py_utils.NestedMap() - batch.ids = tf.constant( - np.random.randint(low=0, high=63, size=[bs, sl], dtype=np.int32)) - batch.task_ids = tf.constant( - np.random.randint(low=0, high=3, size=[bs, sl], dtype=np.int32)) - batch.paddings = tf.zeros([bs, sl]) - - enc_out = mt_enc.FPropDefaultTheta(batch) - enc_out_sum = tf.reduce_sum(enc_out.encoded, 0) - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out = enc_out_sum.eval() - - # pyformat: disable - # pylint: disable=bad-whitespace - expected_enc_out = [ - [ 1.2796677, -31.786997, -0.4054339, -32.61311 , - 42.41403, 11.020338, 54.115948, -61.322887, - 39.593548, 15.315696, -20.373957, 1.8548622, - -17.743631, 3.140956, 30.730812, 41.4348], - [ -1.0373995, -31.306532, -2.6323462, -32.078648, - 45.80049, 16.409424, 55.00114, -63.102333, - 40.4261, 14.198621, -23.027012, 1.0839912, - -20.739473, 0.7242553, 32.49956, 41.592197]] - # pylint: enable=bad-whitespace - # pyformat: enable - self.assertAllClose( - expected_enc_out, actual_enc_out, rtol=1e-05, atol=1e-05) - - def testForwardPassWithSourceMask(self): - with self.session(use_gpu=False): - bs = 2 - sl = 21 - tf.random.set_seed(8372749040) - p = self._EncoderParams() - p.task_emb = p.token_emb.Copy() - p.task_emb.vocab_size = 4 - # 4 tasks, 2 languages. - p.apply_source_mask = True - mt_enc = encoder.TransformerEncoder(p) - batch = py_utils.NestedMap() - batch.ids = tf.constant( - np.random.randint(low=0, high=63, size=[bs, sl], dtype=np.int32)) - batch.task_ids = tf.constant( - np.random.randint(low=0, high=3, size=[bs, sl], dtype=np.int32)) - batch.paddings = tf.zeros([bs, sl]) - - enc_out = mt_enc.FPropDefaultTheta(batch) - enc_out_sum = tf.reduce_sum(enc_out.encoded, 0) - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out = enc_out_sum.eval() - # pyformat: disable - # pylint: disable=bad-whitespace - print(actual_enc_out) - - expected_enc_out = [ - [1.2796695, -31.786999, -0.4054371, -32.61311, 42.414032, 11.020337, - 54.11595, -61.322884, 39.59355, 15.315693, -20.373957, 1.8548615, - -17.743631, 3.1409538, 30.730812, 41.4348], - [-1.0374013, -31.306532, -2.6323478, -32.078648, 45.800484, 16.40942, - 55.001144, -63.10233, 40.4261, 14.19862, -23.027012, 1.0839913, - -20.739471, 0.7242559, 32.499565, 41.592197]] - # pylint: enable=bad-whitespace - # pyformat: enable - self.assertAllClose( - expected_enc_out, actual_enc_out, rtol=1e-05, atol=1e-05) - - def testForwardPassWithInputPacking(self): - with self.session(use_gpu=False): - with tf.variable_scope('transformer_test', reuse=tf.AUTO_REUSE): - bs = 3 - sl = 3 - tf.random.set_seed(8372749040) - p = self._EncoderParams() - mt_enc = encoder.TransformerEncoder(p) - packed_params = p.Copy() - packed_params.packed_input = True - mt_enc_packed = encoder.TransformerEncoder(packed_params) - - batch = py_utils.NestedMap() - batch.ids = tf.constant( - np.random.randint(low=0, high=63, size=[bs, sl], dtype=np.int32)) - batch.paddings = tf.zeros([bs, sl]) - - packed_batch = py_utils.NestedMap() - packed_batch.ids = tf.reshape(batch.ids, [1, -1]) - packed_batch.paddings = tf.reshape(batch.paddings, [1, -1]) - packed_batch.segment_ids = tf.constant( - [[0, 0, 0, 1, 1, 1, 2, 2, 2]], dtype=tf.float32) - packed_batch.segment_pos = tf.constant( - [[0, 1, 2, 0, 1, 2, 0, 1, 2]], dtype=tf.int32) - enc_out = mt_enc.FPropDefaultTheta(batch).encoded - enc_out = tf.transpose(enc_out, [1, 0, 2]) - - packed_enc_out = mt_enc_packed.FPropDefaultTheta(packed_batch) - packed_enc_out = tf.reshape(packed_enc_out.encoded, tf.shape(enc_out)) - - enc_out = tf.reduce_sum(enc_out, axis=0) - packed_enc_out = tf.reduce_sum(packed_enc_out, axis=0) - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out, actual_packed_enc_out = self.evaluate( - [enc_out, packed_enc_out]) - - self.assertAllClose(actual_packed_enc_out, actual_enc_out) - - def testForwardPassSplitBatch(self): - with self.session(use_gpu=False): - bs = 8 - sl = 20 - tf.random.set_seed(8372749040) - p = self._EncoderParams() - p.random_seed = 1234 - mt_enc = encoder.TransformerEncoder(p) - - batch = py_utils.NestedMap() - batch.ids = tf.constant( - np.random.randint(low=0, high=63, size=[bs, sl], dtype=np.int32)) - batch.paddings = tf.zeros([bs, sl]) - out = mt_enc.FPropDefaultTheta(batch) - enc_out = out.encoded - emb_out = out.embedded_inputs - - inputs1, inputs2 = tf.split(batch.ids, 2, 0) - paddings1, paddings2 = tf.split(batch.paddings, 2, 0) - - batch.ids = inputs1 - batch.paddings = paddings1 - out1 = mt_enc.FPropDefaultTheta(batch) - enc_out1 = out1.encoded - emb_out1 = out1.embedded_inputs - - batch.ids = inputs2 - batch.paddings = paddings2 - out2 = mt_enc.FPropDefaultTheta(batch) - enc_out2 = out2.encoded - emb_out2 = out2.embedded_inputs - - self.evaluate(tf.global_variables_initializer()) - actual_enc_out, actual_enc_out1, actual_enc_out2, \ - actual_emb_out, actual_emb_out1, actual_emb_out2 = self.evaluate( - [enc_out, enc_out1, enc_out2, emb_out, emb_out1, emb_out2]) - self.assertAllClose(actual_enc_out, - np.concatenate([actual_enc_out1, actual_enc_out2], 1)) - self.assertAllClose(actual_emb_out, - np.concatenate([actual_emb_out1, actual_emb_out2], 1)) - - def testEncoderVars(self): - p = self._EncoderParams() - mt_enc = encoder.TransformerEncoder(p) - enc_vars = mt_enc.vars - flatten_vars = enc_vars.Flatten() - self.assertEqual(len(flatten_vars), 91) - - -class TransformerBatchMajorEncoderTest(test_utils.TestCase): - - def _TestBuilder(self, model_dim, ff_hidden_dim, num_heads, packed_input): - return self_attention_layer.Builder.Params().Set( - model_dim=model_dim, - ff_hidden_dim=ff_hidden_dim, - num_heads=num_heads, - selfatten_add_unnormalized_input=False, - selfatten_enable_value_proj=False, - packed_input=packed_input).Instantiate() - - def _EncoderParams(self, packed_input=False): - p = encoder.TransformerBatchMajorEncoder.Params() - p.name = 'transformer_encoder' - p.packed_input = packed_input - p.token_emb.params_init = py_utils.WeightInit.GaussianSqrtDim() - p.token_emb.vocab_size = 64 - p.token_emb.embedding_dim = 16 - p.token_emb.max_num_shards = 1 - p.token_emb.vn = py_utils.VariationalNoiseParams(1.0, False, False) - p.model_dim = 16 - p.position_emb.embedding_dim = 16 - stack = self._TestBuilder(16, 5, 2, packed_input).TransformerStack( - 'transformer_stack', 6) - p.transformer_stack = ( - self_attention_layer.StackedTransformerEncoderLayers.Cast(stack)) - p.params_init = py_utils.WeightInit.Xavier(scale=1.0, seed=0) - return p - - def testEncoderConstruction(self): - p = self._EncoderParams() - p.Instantiate() - - def testForwardPass(self): - with self.session(use_gpu=False) as sess: - bs = 2 - sl = 21 - d = 16 - tf.random.set_seed(8372749040) - p = self._EncoderParams() - mt_enc = p.Instantiate() - batch = py_utils.NestedMap() - batch.ids = tf.constant( - np.random.randint(low=0, high=63, size=[bs, sl], dtype=np.int32)) - batch.paddings = tf.zeros([bs, sl]) - out = mt_enc.FPropDefaultTheta(batch) - enc_out_sum = tf.reduce_sum(out.encoded) - - tf.global_variables_initializer().run() - actual_enc_out, actual_enc_out_sum = sess.run([out.encoded, enc_out_sum]) - - self.assertAllEqual([sl, bs, d], actual_enc_out.shape) - self.assertAllClose(306.010132, actual_enc_out_sum) - - def testForwardPassPackedInput(self): - with self.session(use_gpu=False) as sess: - bs = 2 - sl = 21 - d = 16 - tf.random.set_seed(8372749040) - p = self._EncoderParams(packed_input=True) - - mt_enc = p.Instantiate() - batch = py_utils.NestedMap() - batch.ids = tf.constant( - np.random.randint(low=0, high=63, size=[bs, sl], dtype=np.int32)) - - # Pack these into a single batch - packed_bs = 1 - packed_sl = 2 * sl - batch.ids = tf.reshape(batch.ids, [packed_bs, packed_sl]) - - batch.paddings = tf.zeros([packed_bs, packed_sl]) - batch.segment_pos = [ - list(range(sl)) + list(range(sl)), - ] - batch.segment_ids = [ - [0 for i in range(sl)] + [1 for i in range(sl)], - ] - - out = mt_enc.FPropDefaultTheta(batch) - enc_out_sum = tf.reduce_sum(out.encoded) - - tf.global_variables_initializer().run() - actual_enc_out, actual_enc_out_sum = sess.run([out.encoded, enc_out_sum]) - - self.assertAllEqual([packed_sl, packed_bs, d], actual_enc_out.shape) - self.assertAllClose(306.010132, actual_enc_out_sum) - - def testEncoderVars(self): - p = self._EncoderParams() - mt_enc = p.Instantiate() - enc_vars = mt_enc.vars - flatten_vars = enc_vars.Flatten() - self.assertLen(flatten_vars, 91) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/mt/input_generator.py b/lingvo/tasks/mt/input_generator.py deleted file mode 100644 index dfcf8915..00000000 --- a/lingvo/tasks/mt/input_generator.py +++ /dev/null @@ -1,1056 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Machine translation input generator.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import generic_input -from lingvo.core import hyperparams -from lingvo.core import ops -from lingvo.core import py_utils -from lingvo.core import summary_utils -from lingvo.core import tokenizers -from lingvo.tasks.mt import text_input_pb2 - -from google.protobuf import descriptor_pb2 - - -class NmtInput(base_input_generator.BaseSequenceInputGenerator): - """Generator for NMT.""" - - @classmethod - def Params(cls): - """Defaults params for `NmtInput`.""" - p = super().Params() - p.Define( - 'natural_order_model', True, - 'Whether the model consuming the input is a natural order model. Input ' - 'is generated in natural order if True. Input is generated in reversed ' - 'order if False. The value should be consistent with the underlying ' - 'model. Set to True if training or using a natural order model, ' - 'otherwise set to False.') - p.tokenizer = tokenizers.VocabFileTokenizer.Params() - p.source_max_length = 300 - return p - - def _DataSourceFromFilePattern(self, file_pattern): - - def Proc(record): - """Parses a serialized tf.Example record.""" - outputs = [ - ('source_id', tf.io.VarLenFeature(tf.int64)), - ('source_padding', tf.io.VarLenFeature(tf.float32)), - ('target_id', tf.io.VarLenFeature(tf.int64)), - ('target_padding', tf.io.VarLenFeature(tf.float32)), - ('target_label', tf.io.VarLenFeature(tf.int64)), - ('target_weight', tf.io.VarLenFeature(tf.float32)), - ] - features = tf.io.parse_single_example(record, dict(outputs)) - for k, v in features.items(): - features[k] = v.values - bucket_key = tf.cast( - tf.maximum( - tf.reduce_sum(1.0 - features['source_padding']), - tf.reduce_sum(1.0 - features['target_padding'])), tf.int32) - return [features[k] for k, _ in outputs], bucket_key - - return generic_input.GenericInput( - file_pattern=file_pattern, - processor=Proc, - dynamic_padding_dimensions=[0] * 6, - dynamic_padding_constants=[0, 1, 0, 1, 0, 0], - **self.CommonInputOpArgs()) - - def __init__(self, params): - super().__init__(params) - p = self.params - - self.natural_order_model = p.natural_order_model - - (self._src_ids, self._src_paddings, self._tgt_ids, self._tgt_paddings, - self._tgt_labels, - self._tgt_weights), self._bucket_keys = self._BuildDataSource() - - if p.pad_to_max_seq_length: - self._PadSequences() - - def _PadSequences(self): - p = self.params - assert p.source_max_length - - if min(self.infeed_bucket_batch_limit) == max( - self.infeed_bucket_batch_limit): - source_shape = [min(self.infeed_bucket_batch_limit), p.source_max_length] - target_shape = [min(self.infeed_bucket_batch_limit), p.target_max_length] - else: - source_shape = None - target_shape = None - self._src_ids = py_utils.PadSequenceDimension(self._src_ids, - p.source_max_length, 0, - source_shape) - self._src_paddings = py_utils.PadSequenceDimension(self._src_paddings, - p.source_max_length, 1, - source_shape) - self._tgt_ids = py_utils.PadSequenceDimension(self._tgt_ids, - p.target_max_length, 0, - target_shape) - self._tgt_paddings = py_utils.PadSequenceDimension(self._tgt_paddings, - p.target_max_length, 1, - target_shape) - self._tgt_labels = py_utils.PadSequenceDimension(self._tgt_labels, - p.target_max_length, 0, - target_shape) - self._tgt_weights = py_utils.PadSequenceDimension(self._tgt_weights, - p.target_max_length, 0, - target_shape) - - def InfeedBatchSize(self): - """Override BaseSequenceInputGenerator.""" - return tf.shape(self._src_ids)[0] - - def _InputBatch(self): - ret = py_utils.NestedMap() - - ret.bucket_keys = self._bucket_keys - - ret.src = py_utils.NestedMap() - ret.src.ids = tf.cast(self._src_ids, dtype=tf.int32) - ret.src.paddings = self._src_paddings - - ret.tgt = py_utils.NestedMap() - ret.tgt.ids = self._tgt_ids - ret.tgt.labels = tf.cast(self._tgt_labels, dtype=tf.int32) - ret.tgt.weights = self._tgt_weights - ret.tgt.paddings = self._tgt_paddings - - if (self.params.fprop_dtype is None or - self.params.dtype == self.params.fprop_dtype): - return ret - - def _Cast(v): - if not v.dtype.is_floating: - return v - return tf.cast(v, self.params.fprop_dtype) - - return ret.Transform(_Cast) - - -class MlPerfInput(base_input_generator.BaseSequenceInputGenerator): - """Generator for MLPerf TFRecords.""" - - @classmethod - def Params(cls): - """Default params for `MlPerfInput`.""" - p = super().Params() - - p.Define('natural_order_model', True, '') - p.Define( - 'sos_id', 0, 'Start of sentence id' - 'Note in the MLPerf encoding, this is actually , however we can ' - 'make use of it since we never actually use .') - - p.Define( - 'packed_input', False, - 'If True, then we also consume {inputs,targets}_{position,segementation}' - ) - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - self.natural_order_model = p.natural_order_model - - ( - self._src_ids, - self._src_paddings, - self._tgt_ids, - self._tgt_paddings, - self._tgt_labels, - self._tgt_weights, - self._src_seg_pos, - self._src_seg_ids, - self._tgt_seg_pos, - self._tgt_seg_ids, - ), self._bucket_keys = self._BuildDataSource() - - if p.pad_to_max_seq_length: - assert p.source_max_length - - if min(self.infeed_bucket_batch_limit) == max( - self.infeed_bucket_batch_limit): - source_shape = [ - min(self.infeed_bucket_batch_limit), p.source_max_length - ] - target_shape = [ - min(self.infeed_bucket_batch_limit), p.target_max_length - ] - else: - source_shape = None - target_shape = None - self._src_ids = py_utils.PadSequenceDimension(self._src_ids, - p.source_max_length, 0, - source_shape) - self._src_paddings = py_utils.PadSequenceDimension( - self._src_paddings, p.source_max_length, 1, source_shape) - self._tgt_ids = py_utils.PadSequenceDimension(self._tgt_ids, - p.target_max_length, 0, - target_shape) - self._tgt_paddings = py_utils.PadSequenceDimension( - self._tgt_paddings, p.target_max_length, 1, target_shape) - self._tgt_labels = py_utils.PadSequenceDimension(self._tgt_labels, - p.target_max_length, 0, - target_shape) - self._tgt_weights = py_utils.PadSequenceDimension(self._tgt_weights, - p.target_max_length, 0, - target_shape) - - self._src_seg_ids = py_utils.PadSequenceDimension(self._src_seg_ids, - p.source_max_length, 0, - source_shape) - self._src_seg_pos = py_utils.PadSequenceDimension(self._src_seg_pos, - p.source_max_length, 0, - source_shape) - self._tgt_seg_ids = py_utils.PadSequenceDimension(self._tgt_seg_ids, - p.target_max_length, 0, - target_shape) - self._tgt_seg_pos = py_utils.PadSequenceDimension(self._tgt_seg_pos, - p.target_max_length, 0, - target_shape) - - def InfeedBatchSize(self): - """Override BaseSequenceInputGenerator.""" - return tf.shape(self._src_ids)[0] - - def _DataSourceFromFilePattern(self, file_pattern): - p = self._params - - def _DerivePaddingsAndIds(src_ids, tgt_labels): - """tgt_ids is tgt_labels shifted right by one, with a SOS ID prepended.""" - tgt_ids = tf.concat([[p.sos_id], tgt_labels[:-1]], axis=0) - src_paddings = tf.zeros(tf.shape(src_ids), dtype=tf.float32) - tgt_paddings = tf.zeros(tf.shape(tgt_ids), dtype=tf.float32) - tgt_weights = tf.ones(tf.shape(tgt_ids), dtype=tf.float32) - - bucket_key = tf.cast( - tf.maximum( - tf.reduce_sum(1.0 - src_paddings), - tf.reduce_sum(1.0 - tgt_paddings)), tf.int32) - - return src_paddings, tgt_ids, tgt_paddings, tgt_weights, bucket_key - - def _ProcPacked(record): - """TFExample -> Tensors for PackedInput.""" - outputs = [ - ('inputs', tf.io.VarLenFeature(tf.int64)), - ('targets', tf.io.VarLenFeature(tf.int64)), - ('inputs_segmentation', tf.io.VarLenFeature(tf.int64)), - ('inputs_position', tf.io.VarLenFeature(tf.int64)), - ('targets_segmentation', tf.io.VarLenFeature(tf.int64)), - ('targets_position', tf.io.VarLenFeature(tf.int64)), - ] - - features = tf.io.parse_single_example(record, dict(outputs)) - for k, v in features.items(): - features[k] = v.values - - src_ids = features['inputs'] - tgt_labels = features['targets'] - - src_pos = features['inputs_position'] - src_seg = features['inputs_segmentation'] - - tgt_pos = features['targets_position'] - tgt_seg = features['targets_segmentation'] - - src_paddings, tgt_ids, tgt_paddings, tgt_weights, bucket_key = _DerivePaddingsAndIds( - src_ids, tgt_labels) - return [ - src_ids, - src_paddings, - tgt_ids, - tgt_paddings, - tgt_labels, - tgt_weights, - src_pos, - src_seg, - tgt_pos, - tgt_seg, - ], bucket_key - - def _Proc(record): - """Parses a serialized tf.Example record.""" - outputs = [ - ('inputs', tf.io.VarLenFeature(tf.int64)), - ('targets', tf.io.VarLenFeature(tf.int64)), - ] - features = tf.io.parse_single_example(record, dict(outputs)) - for k, v in features.items(): - features[k] = v.values - - src_ids = features['inputs'] - tgt_labels = features['targets'] - - # Derive trivial segmentation for unpacked input. - src_paddings, tgt_ids, tgt_paddings, tgt_weights, bucket_key = _DerivePaddingsAndIds( - src_ids, tgt_labels) - - src_len = tf.shape(src_ids)[0] - tgt_len = tf.shape(tgt_ids)[0] - src_pos = tf.range(src_len, dtype=tf.int32) - src_seg = tf.zeros_like(src_paddings) - tgt_pos = tf.range(tgt_len, dtype=tf.int32) - tgt_seg = tf.zeros_like(tgt_paddings) - - return [ - src_ids, - src_paddings, - tgt_ids, - tgt_paddings, - tgt_labels, - tgt_weights, - src_pos, - src_seg, - tgt_pos, - tgt_seg, - ], bucket_key - - if not p.packed_input: - processor_fn = _Proc - else: - processor_fn = _ProcPacked - - return generic_input.GenericInput( - file_pattern=file_pattern, - processor=processor_fn, - dynamic_padding_dimensions=[0] * 10, - dynamic_padding_constants=[0, 1, 0, 1, 0, 0, 0, 0, 0, 0], - **self.CommonInputOpArgs()) - - def _InputBatch(self): - ret = py_utils.NestedMap() - ret.bucket_keys = self._bucket_keys - - ret.src = py_utils.NestedMap() - ret.src.ids = tf.cast(self._src_ids, dtype=tf.int32) - ret.src.paddings = self._src_paddings - - ret.tgt = py_utils.NestedMap() - ret.tgt.ids = self._tgt_ids - ret.tgt.labels = tf.cast(self._tgt_labels, dtype=tf.int32) - ret.tgt.weights = self._tgt_weights - ret.tgt.paddings = self._tgt_paddings - - ret.src.segment_pos = self._src_seg_pos - ret.src.segment_ids = self._src_seg_ids - - ret.tgt.segment_pos = self._tgt_seg_pos - ret.tgt.segment_ids = self._tgt_seg_ids - - if (self.params.fprop_dtype is None or - self.params.dtype == self.params.fprop_dtype): - return ret - - def _Cast(v): - if not v.dtype.is_floating: - return v - return tf.cast(v, self.params.fprop_dtype) - - return ret.Transform(_Cast) - - -def _GetSegmentPos(weights): - """Returns a segment_pos tensor from the given weights tensor.""" - maxlen = tf.shape(weights)[1] - ret = tf.cast(tf.range(maxlen), dtype=tf.float32) - return tf.cast(weights * ret, dtype=tf.int32) - - -def _GetDescriptorSetForTextInput(): - """Returns a string for tf.io.decode_proto's descriptor_source.""" - file_descriptor_set = descriptor_pb2.FileDescriptorSet() - text_input_pb2.DESCRIPTOR.CopyToProto(file_descriptor_set.file.add()) - return b'bytes://' + file_descriptor_set.SerializeToString() - - -class TextPackedInput(base_input_generator.BaseSequenceInputGenerator): - """Generator for packed text input.""" - - @classmethod - def Params(cls): - r"""Defaults params for TextInput. - - Returns: - A Params object for TextPackedInput. - - Notes about usage: - - * Input files contain UTF8 encoded texts. p.input_file_type controls - what format to extract these texts from. The default is 'tsv', in which - case p.file_pattern should be prefixed by file type 'text:', and - every line in the input file should have text columns separated by - a tab '\t'. Otherwise p.input_file_type can be Sentence or SentencePair - protos. - - For tsv input files, in the default case, the file should contain 2 - columns, for the source and the target sentence. Special cases: - - - When quality scores are present (see p.quality_score_filter_fn below), - it should contain 3 columns, the last being a quality score. - - When MASS is enabled, it should contain a single column. - - * p.tokenizer or p.tokenizer_dict is used to perform string to id - conversions. If key `src` or `tgt` is present in p.tokenizer_dict, - it will be used for generating the ids for src or tgt, respectively. - Otherwise the default tokenizer will be used. - - * p.packing_factor depends on the training data and max lengths used. - - If this value is too small, we generate packed batches that contain - too many padding that could have been used to pack more examples. - If this value is too large, we use more memory and randomly discard - examples that could not fit. - - One can look at the num_samples_in_batch graph to determine if its - value is too small. For example, with an effective scaled batch size of - 1024, suppose we set p.packing_factor=3.0, and observe that - num_samples_in_batch is saturated at 3072(=1024x3), this means 3.0 is - likely too small. If we instead observe that num_samples_in_batch - fluctuates around 2500, this means 3.0 is larger than needed. - - We believe that there can be a slight bias against longer sequences - when packing is enabled. The remedy is either use larger effective - batch size, or use a larger-than-optimal packing factor when effective - batch size is smaller. For example, p.packing_factor = 8 seems to work - reasonably well in practice. - - * p.source_max_length and p.target_max_length control both the shape of - the generated input batch (how long each row is) and the filtering - (max allowed lengths for source and targt, respectively). - - p.bucket_upper_bound also conrols the filtering of examples. Inputs - with either source or target sequence lengths exceeding it will be - filtered out. - - It's not meaningful to set p.bucket_upper_bound higher than both - p.source_max_length and p.target_max_length. - - When packing is enabled, however, a smaller p.bucket_upper_bound means - that individual sequences have a smaller max length, but the packed - batch may have a larger total length. - - * p.file_pattern_task_ids, p.task_to_{src,tgt}_lang_map are all used - to manipulate batch.{src,tgt}.task_ids. - - For each eaxmple, its task is obtained from the source id, which is - the index of the example's origin file in p.file_pattern. The task - id populated in the input batch is determined by: - p.task_to_{src,tgt}_lang_map[ p.file_pattern_task_ids[souce_id] ], - for src and tgt, respectively, where if a list is empty it falls - back to an identity map. - - In the future we may define a separate lang_ids field to the input - batch to disambiguate. - - * p.quality_score_filter_fn can be used when a column of quality score - is present in the input .tsv file. The quality score must be the last - column. This filter function returns True to filter, e.g. use - p.quality_score_filter_fn = lambda x: x <= 0.3 for scores where higher - means better. - - p.quality_score_filter_fn typically should only contain a simple - comparison (<, >, <=, or >=), as it relies on tf.Tensor's overloading - of __le__() etc. to work. For example: lambda x: ( 0.3 < x and x < 0.9) - won't work. But tf.math.logical_and(0.3 < x, x < 0.9) is okay. - - Also note that 'p.quality_score_filter_fn = lambda _: False' is - equivalent with 'p.quality_score_filter_fn = None', in which case - no quality score column is needed (or evaluated). - - * Consider enabling multithreading for the trainer job (in the Train() - method). For example: p.num_batcher_threads = 128. - """ - p = super().Params() - - p.Define('file_pattern_task_ids', [], - 'task_id corresponding to list of file_patterns.') - p.Define('task_to_src_lang_map', [], 'Map of task id to src language id.') - p.Define('task_to_tgt_lang_map', [], 'Map of task id to tgt language id.') - - p.Define( - 'packing_factor', None, - 'A multiplicative factor for packing. This is the ratio between ' - 'pre-packing batch size and after-packing batch size. If None, ' - 'packing is disabled; otherwise the packing factor should be a ' - 'float with a value greater than 1.') - - p.Define( - 'quality_score_filter_fn', None, - 'A user defined boolean function on a float (quality score). ' - 'When present, the input .tsv file has an additional column ' - 'of floats representing a quality score, and each line is ' - 'filtered out when this function returns True on that score.') - - p.Define( - 'input_file_type', 'tsv', 'The type of input file contents.' - ' Must be one of ["tsv", "sentence_proto"], for tab-separated' - ' values, or Sentence/SentencePair protos, respectively.') - p.Define( - 'single_column_input', False, 'Indicates input is single-column rather' - ' than double-column. When input_file_type is sentence_proto, this' - ' means Sentence proto rather than SentencePair proto.') - - p.Define('natural_order_model', True, 'Only True is supported now.') - p.Define('target_language', '', 'Language on target side.') - p.Define('mass_layer', None, 'If not None, use the specified layer to do ' - 'MASS masking.') - p.Define( - 'mass_task_ids', None, 'List of task IDs for MASS. If None and ' - 'single_column_input=True, apply MASS to all tasks, otherwise ' - 'only apply to the specified tasks.') - # Back translation - p.Define('bt_task_ids', [], 'List of task ids for back-translation.') - # Denoising (https://arxiv.org/pdf/1711.00043) - p.Define('denoise', hyperparams.Params(), 'Params for denosing tasks.') - p.denoise.Define('task_ids', [], 'List of task IDs for denoising.') - p.denoise.Define('noise_sent_prob', 1, - 'Probability of noising an input sentence.') - p.denoise.Define( - 'shuffle_tok_range', 3, 'Range of noise for shuffling tokens, following' - ' https://arxiv.org/pdf/1711.00043. Note that shuffle_tok_range of 3 ' - 'implies tokens may be permuted at most 3 position.') - p.denoise.Define('drop_tok_prob', 0.1, 'Probability of dropping tokens.') - p.denoise.Define('blank_tok_prob', 0.1, 'Probability of blanking tokens.') - p.denoise.Define('blank_id', 3, 'ID of blank token.') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - if not p.natural_order_model: - raise ValueError('Only p.natural_order_model=True is supported now.') - self.natural_order_model = p.natural_order_model - - if p.packing_factor: - # Packing is enabled. We override p.bucket_batch_limit with the - # pre-packing batch size. - if p.packing_factor <= 1.0: - raise ValueError('p.packing_factor must be > 1.0: ', p.packing_factor) - if len(p.bucket_upper_bound) != 1 or len(p.bucket_batch_limit) != 1: - raise ValueError( - 'when packing is enabled, p.bucket_upper_bound ' - 'and p.bucket_batch_limits must be arrays of length ' - '1:', p.bucket_upper_bound, p.bucket_batch_limit) - self._packed_batch_size = p.bucket_batch_limit[0] - p.bucket_batch_limit[0] = int(p.bucket_batch_limit[0] * p.packing_factor) - else: - self._packed_batch_size = None - - # Ensure that the max lengths are not None, as tokenizer.StringsToIds() - # might use it to pad the encoded ids tensor. - if p.target_max_length is None: - p.target_max_length = p.bucket_upper_bound[-1] - if p.source_max_length is None: - p.source_max_length = p.target_max_length - - if p.input_file_type not in ['tsv', 'sentence_proto']: - raise ValueError('p.input_file_type must be one of ["tsv",' - ' "sentence_proto"], got {}'.format( - params.input_file_type)) - if p.quality_score_filter_fn: - if not isinstance(p.quality_score_filter_fn(0.0), bool) and not ( - isinstance(p.quality_score_filter_fn(0.0), tf.Tensor) and - p.quality_score_filter_fn(0.0).dtype == tf.bool): - raise ValueError( - 'p.quality_score_filter_fn must return a bool on a float input, ' - 'e.g. p.quality_score_filter_fn = lambda x: x <= 0.3.') - if p.input_file_type != 'tsv': - raise ValueError( - 'p.quality_score_filter_fn requires p.input_file_type == "tsv".') - - self._src_tokenizer_key = ('src' if 'src' in self.tokenizer_dict else - base_input_generator.DEFAULT_TOKENIZER_KEY) - self._src_tokenizer = self.tokenizer_dict[self._src_tokenizer_key] - self._tgt_tokenizer_key = ('tgt' if 'tgt' in self.tokenizer_dict else - base_input_generator.DEFAULT_TOKENIZER_KEY) - self._tgt_tokenizer = self.tokenizer_dict[self._tgt_tokenizer_key] - - # TODO(alisonlui): Support single-column Sentence proto input. - if p.single_column_input and p.input_file_type == 'sentence_proto': - raise NotImplementedError( - 'Single column Sentence proto input not yet supported.') - if p.mass_layer is not None and not p.single_column_input: - raise ValueError('Must be single_column_input if mass layer is provided.') - if p.mass_layer is not None: - # Creat the MASS layer (wrapper for the MASS op). - self.CreateChild('mass_layer', p.mass_layer) - - # A `.NestedMap` of input tensors for the current input batch. - # We memoize it here to avoid accidentally calling self._BuildDataSource() - # more than once. - self._batch = self._DataSourceToInputBatch() - - def _GetBucketKey(self, features, filtered): - """Returns a the bucket key for a given input.""" - # The token ids are not truncated if and only if it ends with padding - # or the last id is EOS. - src_fits = tf.math.logical_or( - tf.math.equal(features.src.ids_indicator[-1], 0), - tf.math.equal(features.src.ids[-1], self._src_tokenizer.eos_id)) - tgt_fits = tf.math.logical_or( - tf.math.equal(features.tgt.ids_indicator[-1], 0), - tf.math.equal(features.tgt.labels[-1], self._tgt_tokenizer.eos_id)) - - # We return the max of sourcec or target sequence length if and only if both - # src and tgt fit. Otherwise we return a key of -1 to filter out this input. - def _MaxLen(): - src_len = tf.cast( - tf.math.reduce_sum(features.src.ids_indicator), dtype=tf.int32) - tgt_len = tf.cast( - tf.math.reduce_sum(features.tgt.ids_indicator), dtype=tf.int32) - return tf.math.maximum(src_len, tgt_len) - - filtered = tf.math.logical_or( - filtered, tf.math.logical_not(tf.math.logical_and(src_fits, tgt_fits))) - return tf.cond(filtered, lambda: -1, _MaxLen) - - def _GetTaskIds(self, source_id): - """Look up the correct task_id from the source_id tensor.""" - if self.params.file_pattern_task_ids: - file_task_ids = tf.constant( - self.params.file_pattern_task_ids, dtype=tf.int32) - return tf.gather(file_task_ids, source_id) - return source_id - - def _GetLangIds(self, source_id): - """Look up the correct lang_id from the source_id tensor.""" - task_id = self._GetTaskIds(source_id) - src_lang_id = task_id - tgt_lang_id = task_id - if self.params.task_to_src_lang_map: - src_langs = tf.constant(self.params.task_to_src_lang_map, dtype=tf.int32) - src_lang_id = tf.gather(src_langs, task_id) - if self.params.task_to_tgt_lang_map: - tgt_langs = tf.constant(self.params.task_to_tgt_lang_map, dtype=tf.int32) - tgt_lang_id = tf.gather(tgt_langs, task_id) - return src_lang_id, tgt_lang_id - - def _ProcessSingleInput(self, source_id, src, tgt): - """Performs strings-to-ids on the given input pair via p.tokenizer_dict.""" - _, src_labels, src_paddings = self.StringsToIds( - tf.reshape(src, [1]), is_source=True, key=self._src_tokenizer_key) - tgt_ids, tgt_labels, tgt_paddings = self.StringsToIds( - tf.reshape(tgt, [1]), is_source=False, key=self._tgt_tokenizer_key) - # Mask positions to 0 where padding is 1 for consistency. We do this because - # tokenizer implementation may use EOS token to pad. - src_labels = py_utils.ApplyPadding(src_paddings, src_labels) - tgt_ids = py_utils.ApplyPadding(tgt_paddings, tgt_ids) - tgt_labels = py_utils.ApplyPadding(tgt_paddings, tgt_labels) - - features = py_utils.NestedMap() - features.src = py_utils.NestedMap() - features.src.ids = src_labels - # ids_indicator is 1 if and only if the output from tokenizer has a - # non-padded id. Unlike weights, it will not mutate and can be used for - # determining actual sequence length, for example. - features.src.ids_indicator = 1 - src_paddings - features.src.weights = 1 - src_paddings - features.src.paddings = src_paddings - features.tgt = py_utils.NestedMap() - features.tgt.ids = tgt_ids - features.tgt.labels = tgt_labels - features.tgt.ids_indicator = 1 - tgt_paddings - features.tgt.weights = 1 - tgt_paddings - features.tgt.paddings = tgt_paddings - - src_task_id, tgt_task_id = self._GetLangIds(source_id) - # task_ids are padded with zeros. - features.src.task_ids = tf.cast( - features.src.ids_indicator, dtype=tf.int32) * src_task_id - features.src.source_ids = tf.cast( - features.src.ids_indicator, dtype=tf.int32) * source_id - features.tgt.task_ids = tf.cast( - features.tgt.ids_indicator, dtype=tf.int32) * tgt_task_id - features.tgt.source_ids = tf.cast( - features.tgt.ids_indicator, dtype=tf.int32) * source_id - - if not py_utils.use_tpu(): - features.src.strs = src - features.tgt.strs = tgt - return features.Transform(tf.squeeze) - - def _ProcessMASSInput(self, source_id, src): - """Perform MASS input processing.""" - if self.do_eval or self.mass_layer is None: - # At eval time, we copy src to tgt - return self._ProcessSingleInput(source_id, src, src) - - _, labels, paddings = self.StringsToIds( - tf.reshape(src, [1]), is_source=True, key=self._src_tokenizer_key) - weights = 1 - paddings - actual_seq_len = tf.cast(tf.reduce_sum(weights, 1), tf.int32) - src_lang_ids, tgt_lang_ids = self._GetLangIds(source_id) - - mass_out = self.mass_layer.Mask(labels, weights, actual_seq_len) - - features = py_utils.NestedMap() - features.src = py_utils.NestedMap() - features.src.ids = mass_out.src.ids - features.src.paddings = paddings - features.src.weights = weights - features.src.task_ids = tf.cast( - features.src.weights, dtype=tf.int32) * src_lang_ids - features.src.source_ids = tf.cast( - features.src.weights, dtype=tf.int32) * source_id - features.src.ids_indicator = weights - features.tgt = py_utils.NestedMap() - features.tgt.ids = mass_out.tgt.ids - features.tgt.labels = mass_out.tgt.labels - features.tgt.paddings = paddings - features.tgt.weights = mass_out.tgt.weights - features.tgt.task_ids = tf.cast(weights, dtype=tf.int32) * tgt_lang_ids - features.tgt.source_ids = tf.cast(weights, dtype=tf.int32) * source_id - features.tgt.ids_indicator = weights - - if not py_utils.use_tpu(): - features.src.strs = src - features.tgt.strs = src - return features.Transform(tf.squeeze) - - def _ReadRecordTsv(self, record): - """Reads a single input record from a tab-separated values file.""" - # Assuming UTF8 text input separated by tabs. - sentences = tf.strings.split([record], sep='\t', result_type='RaggedTensor') - # If the row_lengths are not enough (e.g. row has only 1 column), - # record_batcher throws away this record but it does not crash the program - # per lingvo/core/ops/record_batcher.cc. - # This means that it's okay if the file contains more columns than needed. - src = sentences[0, 0] - tgt = sentences[0, 1] - # We manually filter the record if either source or target sentence is an - # empty string. - filtered = tf.math.logical_or( - tf.math.equal(tf.strings.length(src), 0), - tf.math.equal(tf.strings.length(tgt), 0)) - if not self.params.quality_score_filter_fn: - return src, tgt, filtered - filtered = tf.math.logical_or( - filtered, - self.params.quality_score_filter_fn( - tf.strings.to_number(sentences[0, 2]))) - return src, tgt, filtered - - def _ReadRecordTsvSingleColumn(self, record): - """Reads an input record, taking first column of one or more TSV columns.""" - # Assuming UTF8 text input which may have 1 or more tab-separated columns - sentences = tf.strings.split([record], sep='\t', result_type='RaggedTensor') - src = sentences[0, 0] - filtered = tf.math.equal(tf.strings.length(src), 0) - return src, filtered - - def _ReadRecordSentencePairProto(self, record): - """Reads the input record as a binary SentencePair proto.""" - # We defer handling the `lang` field in the proto until TextPackedInput - # figures out how to handle lang_ids. For now `lang` fields are ignored. - _, sentence_protos = tf.io.decode_proto( - bytes=record, - message_type='tensorflow.lingvo.SentencePair', - field_names=['src_sentence', 'tgt_sentence'], - output_types=[tf.string, tf.string], - descriptor_source=_GetDescriptorSetForTextInput()) - sentence_protos = tf.squeeze(sentence_protos) - _, sentences = tf.io.decode_proto( - bytes=sentence_protos, - message_type='tensorflow.lingvo.Sentence', - field_names=['sentence'], - output_types=[tf.string], - descriptor_source=_GetDescriptorSetForTextInput()) - sentences = tf.squeeze(sentences) - return sentences[0], sentences[1] - - def _DataSourceFromFilePattern(self, file_pattern, input_source_weights=None): - - def Processor(source_id, record): - """Parses a record, which is a line of text.""" - - task_id = self._GetTaskIds(source_id) - - if self.params.input_file_type == 'tsv': - - def _ApplyMass(task_id): - mass_task_ids = tf.constant(self.params.mass_task_ids, dtype=tf.int32) - return tf.reduce_any(tf.equal(task_id, mass_task_ids)) - - def _MASSInput(): - src, filtered = self._ReadRecordTsvSingleColumn(record) - return self._ProcessMASSInput(source_id, src), filtered - - def _SingleInput(): - src, tgt, filtered = self._ReadRecordTsv(record) - return self._ProcessSingleInput(source_id, src, tgt), filtered - - if self.params.single_column_input: - # For monolingual input, MASS is applied by default. - # If mass_task_ids is specified, only apply MASS to specified tasks. - if self.params.mass_task_ids is not None: - cond = _ApplyMass(task_id) - features, filtered = tf.cond(cond, _MASSInput, _SingleInput) - else: - features, filtered = _MASSInput() - else: - features, filtered = _SingleInput() - - else: - src, tgt = self._ReadRecordSentencePairProto(record) - filtered = tf.constant(False, dtype=tf.bool) - features = self._ProcessSingleInput(source_id, src, tgt) - - return features, self._GetBucketKey(features, filtered) - - return generic_input.GenericInput( - processor=Processor, - file_pattern=file_pattern, - input_source_weights=input_source_weights, - **self.CommonInputOpArgs()) - - def _Pack(self, batch): - """Packs a given batch. - - Note that this may change the batch size. - - This function packs the input batch and adds .segment_ids and .segment_pos - fields to its `src` and `tgt` fields. - - Args: - batch: a `.NestedMap` of input tensors to be packed. It is modified in - place. - """ - src_actual_seq_len = tf.math.reduce_sum( - tf.cast(batch.src.ids_indicator, tf.int32), axis=1) - tgt_actual_seq_len = tf.math.reduce_sum( - tf.cast(batch.tgt.ids_indicator, tf.int32), axis=1) - summary_utils.histogram('source_seq_lengths', src_actual_seq_len) - summary_utils.histogram('target_seq_lengths', tgt_actual_seq_len) - - if not self.params.packing_factor: - # Supply segment_ids and segment_pos with no packing. - batch.src.segment_ids = batch.src.ids_indicator - batch.src.segment_pos = _GetSegmentPos(batch.src.ids_indicator) - batch.tgt.segment_ids = batch.tgt.ids_indicator - batch.tgt.segment_pos = _GetSegmentPos(batch.tgt.ids_indicator) - return - - (src_segment_ids, src_segment_pos, src_indices_in_input, tgt_segment_ids, - tgt_segment_pos, tgt_indices_in_input) = ops.pack_sequences( - src_actual_seq_len, tgt_actual_seq_len, self._ScaledBatchSize(), - self.params.source_max_length, self.params.target_max_length) - - uniq_src_indices_in_input = tf.unique( - tf.reshape(src_indices_in_input, [-1])).y - uniq_tgt_indices_in_input = tf.unique( - tf.reshape(tgt_indices_in_input, [-1])).y - summary_utils.histogram( - 'packed_source_seq_lengths', - tf.gather(src_actual_seq_len, uniq_src_indices_in_input, axis=0)) - summary_utils.histogram( - 'packed_target_seq_lengths', - tf.gather(tgt_actual_seq_len, uniq_tgt_indices_in_input, axis=0)) - - # We deferred adding .paddings and use its complement .ids_indicator - # exclusively so that we can apply the packing with padding set to 0 for all - # fields. - def ApplyPackingToSource(x): - if x.dtype == tf.string: - return ops.apply_packing(x, '\t', src_segment_ids, src_indices_in_input) - return ops.apply_packing(x, 0, src_segment_ids, src_indices_in_input) - - src_paddings = ops.apply_packing(batch.src.paddings, 1, src_segment_ids, - src_indices_in_input) - batch.src = batch.src.Transform(ApplyPackingToSource) - batch.src.paddings = src_paddings - batch.src.segment_ids = tf.cast(src_segment_ids, tf.float32) - batch.src.segment_pos = src_segment_pos - - def ApplyPackingToTarget(x): - if x.dtype == tf.string: - return ops.apply_packing(x, '\t', tgt_segment_ids, tgt_indices_in_input) - return ops.apply_packing(x, 0, tgt_segment_ids, tgt_indices_in_input) - - tgt_paddings = ops.apply_packing(batch.tgt.paddings, 1, tgt_segment_ids, - tgt_indices_in_input) - batch.tgt = batch.tgt.Transform(ApplyPackingToTarget) - batch.tgt.paddings = tgt_paddings - batch.tgt.segment_ids = tf.cast(tgt_segment_ids, tf.float32) - batch.tgt.segment_pos = tgt_segment_pos - - def _ScaledBatchSize(self): - # Adjust (post-packing) batch size according to the cluster spec. - # See the impl of BaseSequenceInputGenerator.infeed_bucket_batch_limit() - cluster = self.cluster - batch_size = (self._packed_batch_size or self.params.bucket_batch_limit[0]) - scaled_batch_size = batch_size * cluster.num_splits_per_client - if self.params.use_per_host_infeed and cluster.num_tpu_hosts > 0: - scaled_batch_size = scaled_batch_size // cluster.num_tpu_hosts - return scaled_batch_size - - # TODO(yujieq): create a separate wrapper layer for the noise-adding process - def _AddNoise(self, batch): - """Adding noise the src (see https://arxiv.org/pdf/1711.00043). - - This function implement 3 types of noise (hyparams defined in - self.params.denoise): - 1) slightly shuffle the sentence following p.shuffle_tok_range - 2) randomly drop tokens with probability p.drop_tok_prob - 3) randomly mask tokens with probability p.blank_tok_prob - The noises are added to the input with probability p.noise_sent_prob. - - Args: - batch: a `.NestedMap` of the input batch. - """ - - def IsSpecialExample(task_ids, special_task_ids): - """A utility function indicates whether inputs belong to specific tasks. - - Args: - task_ids: Task ids for the input batch. Tensor of shape [batch]. - special_task_ids: A list of specified task ids. - - Returns: - A tensor indicating whether each sample in the batch belong to the - specified task. Return a tensor of size [batch]. - """ - batch_size = py_utils.GetShape(task_ids)[0] - return tf.reduce_any( - tf.equal( - tf.expand_dims(task_ids, -1), - tf.cast( - tf.broadcast_to( - special_task_ids, - [batch_size, len(special_task_ids)]), tf.int32)), -1) - - p = self.params.denoise - batch_size = tf.shape(batch.src.ids)[0] - source_max_len = tf.shape(batch.src.ids)[1] - - # Shuffle tokens according to p.shuffle_tok_range - noise = tf.random.uniform([batch_size, source_max_len], 0, - p.shuffle_tok_range + 1) - - # Don't shuffle eos or padding - shuffle_tok_range = tf.fill([batch_size, source_max_len], - float(p.shuffle_tok_range)) - shifted_paddings = tf.pad( - batch.src.paddings[:, 1:], [[0, 0], [0, 1]], constant_values=1) - noise = tf.where(tf.equal(shifted_paddings, 0), noise, shuffle_tok_range) - indices = tf.broadcast_to( - tf.range(source_max_len, dtype=tf.int32), [batch_size, source_max_len]) - noisy_indices = tf.cast(indices, dtype=tf.float32) + noise - permutations = tf.argsort(noisy_indices) - stacked = tf.stack([batch.src.ids, permutations], axis=1) - denoise_src_ids = tf.stack( - tf.map_fn(lambda x: tf.gather(x[0], x[1]), stacked), axis=0) - - # Select tokens to drop with probability=p.drop_tok_prob - random_drop_tok = tf.random.uniform([batch_size, source_max_len]) - # Don't drop eos token - is_keep_tok = tf.math.logical_or( - tf.greater(random_drop_tok, p.drop_tok_prob), - tf.equal(denoise_src_ids, self._src_tokenizer.eos_id)) - denoise_src_ids = tf.ragged.boolean_mask(denoise_src_ids, - is_keep_tok).to_tensor( - default_value=0, - shape=tf.shape(batch.src.ids)) - denoise_src_paddings = tf.ragged.boolean_mask( - batch.src.paddings, is_keep_tok).to_tensor( - default_value=1, shape=tf.shape(batch.src.ids)) - - # Select tokens to blank with probability=p.blank_tok_prob - # Don't blank eos token - random_blank_tok = tf.random.uniform([batch_size, source_max_len]) - shifted_paddings = tf.pad( - denoise_src_paddings[:, 1:], [[0, 0], [0, 1]], constant_values=1) - is_blank_tok = tf.math.logical_and( - tf.less(random_blank_tok, p.blank_tok_prob), - tf.equal(shifted_paddings, 0)) - blank_id = tf.fill([batch_size, source_max_len], p.blank_id) - denoise_src_ids = tf.where(is_blank_tok, blank_id, denoise_src_ids) - - # Select denoising task examples with probability=p.denoise_sent_prob - random_uniform_sent = tf.random.uniform([batch_size]) - is_denoise_sent = tf.math.logical_and( - tf.less(random_uniform_sent, p.noise_sent_prob), - IsSpecialExample( - self._GetTaskIds(batch.src.source_ids[:, 0]), p.task_ids)) - batch.src.ids = tf.where(is_denoise_sent, denoise_src_ids, batch.src.ids) - batch.src.paddings = tf.where(is_denoise_sent, denoise_src_paddings, - batch.src.paddings) - batch.src.ids_indicator = 1 - batch.src.paddings - batch.src.weights = batch.src.ids_indicator - - def _DataSourceToInputBatch(self): - """The current input batch as a `.NestedMap` of input tensors.""" - ret, _ = self._BuildDataSource() - - p = self.params - if p.denoise.task_ids and p.denoise.noise_sent_prob > 0: - self._AddNoise(ret) - - self._Pack(ret) - if 'weights' not in ret.src or 'weights' not in ret.tgt: - ret.src.weights = ret.src.ids_indicator - ret.tgt.weights = ret.tgt.ids_indicator - if 'paddings' not in ret.src or 'paddings' not in ret.tgt: - ret.src.paddings = 1 - ret.src.weights - ret.tgt.paddings = 1 - ret.tgt.weights - del ret.src.ids_indicator - del ret.tgt.ids_indicator - - if self.params.pad_to_max_seq_length: - assert self.params.source_max_length - - def _EnsureSrcShape(x): - if x.dtype == tf.string: - return tf.ensure_shape(x, [self._ScaledBatchSize()]) - return tf.ensure_shape( - x, [self._ScaledBatchSize(), self.params.source_max_length]) - - def _EnsureTgtShape(x): - if x.dtype == tf.string: - return tf.ensure_shape(x, [self._ScaledBatchSize()]) - return tf.ensure_shape( - x, [self._ScaledBatchSize(), self.params.target_max_length]) - - ret.src = ret.src.Transform(_EnsureSrcShape) - ret.tgt = ret.tgt.Transform(_EnsureTgtShape) - - summary_utils.histogram('source_token_ids', ret.src.ids) - summary_utils.histogram('target_token_ids', ret.tgt.ids) - - # Casts floating point tensors to fprop_dtype before returning. - return ret.Transform(self.Cast) - - def _InputBatch(self): - """The current input batch. - - Returns: - A `.NestedMap` of input tensors. - """ - return self._batch - - def GlobalBatchSize(self): - """Returns the total number of examples in the current batch.""" - # The number of examples is indicated by the segment_ids of the target. - num_segments = tf.math.reduce_max(self._batch.tgt.segment_ids, axis=1) - return tf.reduce_sum(tf.cast(num_segments, dtype=tf.int32)) diff --git a/lingvo/tasks/mt/input_generator_test.py b/lingvo/tasks/mt/input_generator_test.py deleted file mode 100644 index a354fc64..00000000 --- a/lingvo/tasks/mt/input_generator_test.py +++ /dev/null @@ -1,452 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for input generator.""" - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.core import tokenizers -from lingvo.tasks.mt import input_generator -import numpy as np - - -class InputTest(test_utils.TestCase): - - def _CreateMlPerfInputParams(self): - p = input_generator.MlPerfInput.Params() - input_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/translate_ende_wmt32k-train-00511-of-00512') - p.file_pattern = 'tfrecord:' + input_file - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.bucket_upper_bound = [20, 40] - p.bucket_batch_limit = [4, 8] - return p - - def _CreateMlPerfPackedInputParams(self): - p = input_generator.MlPerfInput.Params() - input_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/translate_ende_mlperf.packed.tfrecord') - p.file_pattern = 'tfrecord:' + input_file - p.packed_input = True - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.bucket_upper_bound = [20, 240] - p.bucket_batch_limit = [4, 4] - return p - - def _CreateNmtInputParams(self): - p = input_generator.NmtInput.Params() - input_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/wmt14_ende_wpm_32k_test.tfrecord') - p.file_pattern = 'tfrecord:' + input_file - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.bucket_upper_bound = [20, 40] - p.bucket_batch_limit = [4, 8] - return p - - def testBasic(self): - p = self._CreateNmtInputParams() - with self.session(use_gpu=False): - inp = input_generator.NmtInput(p) - # Runs a few steps. - for _ in range(10): - self.evaluate(inp.GetPreprocessedInputBatch()) - - def testMlPerfPackedInput(self): - p = self._CreateMlPerfPackedInputParams() - with self.session(use_gpu=False): - inp = input_generator.MlPerfInput(p) - for _ in range(1): - fetched = py_utils.NestedMap( - self.evaluate(inp.GetPreprocessedInputBatch())) - tf.logging.info(fetched.src.ids.shape) - tf.logging.info(fetched.src.segment_ids.shape) - tf.logging.info(fetched.src.segment_pos.shape) - tf.logging.info(fetched.tgt.segment_ids.shape) - tf.logging.info(fetched.tgt.segment_pos.shape) - - def checkPadShape(self, x, pad, batch_size, actual_max, pad_length): - # Check the shape: (batch, maxlen) - self.assertEqual(x.shape, (batch_size, pad_length)) - # Check the padding. - self.assertAllEqual(x[:, actual_max:], - np.full((batch_size, (pad_length - actual_max)), pad)) - - def testMlPerfPackedInputPadToMax(self): - p = self._CreateMlPerfPackedInputParams() - p.source_max_length = 300 - p.target_max_length = 300 - p.pad_to_max_seq_length = True - with self.session(use_gpu=False): - inp = input_generator.MlPerfInput(p) - for _ in range(1): - fetched = py_utils.NestedMap( - self.evaluate(inp.GetPreprocessedInputBatch())) - - self.checkPadShape( - fetched.src.ids, pad=0, batch_size=4, actual_max=240, pad_length=300) - - self.checkPadShape( - fetched.tgt.ids, pad=0, batch_size=4, actual_max=240, pad_length=300) - - self.checkPadShape( - fetched.tgt.segment_ids, - pad=0, - batch_size=4, - actual_max=240, - pad_length=300) - - self.checkPadShape( - fetched.tgt.segment_pos, - pad=0, - batch_size=4, - actual_max=240, - pad_length=300) - - def testMlPerf(self): - p = self._CreateMlPerfInputParams() - with self.session(use_gpu=False): - inp = input_generator.MlPerfInput(p) - # Runs a few steps. - for _ in range(10): - fetched = py_utils.NestedMap( - self.evaluate(inp.GetPreprocessedInputBatch())) - tf.logging.info(fetched) - - def testMlPerfPadToMax(self): - p = self._CreateMlPerfInputParams() - p.bucket_upper_bound = [20] - p.bucket_batch_limit = [4] - p.source_max_length = 30 - p.target_max_length = 30 - p.pad_to_max_seq_length = True - - with self.session(use_gpu=False): - inp = input_generator.MlPerfInput(p) - # Runs a few steps. - for _ in range(10): - fetched = py_utils.NestedMap( - self.evaluate(inp.GetPreprocessedInputBatch())) - - def Check(x, pad): - # Check the shape: (batch, maxlen) - self.assertEqual(x.shape, (4, 30)) - # Check the padding. - self.assertAllEqual(x[:, 20:], np.full((4, 10), pad)) - Check(fetched.src.ids, 0) - Check(fetched.src.paddings, 1) - Check(fetched.tgt.ids, 0) - Check(fetched.tgt.labels, 0) - Check(fetched.tgt.weights, 0) - Check(fetched.tgt.paddings, 1) - - def testPadToMax(self): - p = self._CreateNmtInputParams() - p.bucket_upper_bound = [20] - p.bucket_batch_limit = [4] - p.source_max_length = 30 - p.target_max_length = 30 - p.pad_to_max_seq_length = True - with self.session(use_gpu=False): - inp = input_generator.NmtInput(p) - fetched = py_utils.NestedMap( - self.evaluate(inp.GetPreprocessedInputBatch())) - - def Check(x, pad): - # Check the shape: (batch, maxlen) - self.assertEqual(x.shape, (4, 30)) - # Check the padding. - self.assertAllEqual(x[:, 20:], np.full((4, 10), pad)) - - Check(fetched.src.ids, 0) - Check(fetched.src.paddings, 1) - Check(fetched.tgt.ids, 0) - Check(fetched.tgt.labels, 0) - Check(fetched.tgt.weights, 0) - Check(fetched.tgt.paddings, 1) - - def testSplitSources(self): - p = self._CreateNmtInputParams() - num_splits = 2 - expected_ids_split_1 = [ - [ - 228, 58, 854, 11, 392, 45, 77, 67, 1346, 30, 25, 10, 2283, 933, 14, - 3, 872, 4677, 5, 2 - ], - [ - 328, 22, 463, 571, 134, 10, 3815, 6311, 8, 2203, 3, 654, 2724, 1064, - 5, 2, 0, 0, 0, 0 - ], - ] - - expected_ids_split_2 = [ - [ - 16, 599, 11, 8, 113, 3, 145, 558, 489, 4373, 36, 55, 8988, 5, 2, 0, - 0, 0, 0, 0 - ], - [ - 16, 343, 95, 296, 4550, 4786, 1798, 23019, 8, 10296, 3, 107, 6428, - 1812, 5, 2, 0, 0, 0, 0 - ], - ] - - with self.session(use_gpu=False): - inp = input_generator.NmtInput(p) - splits = inp.SplitInputBatch(num_splits) - split_ids = self.evaluate([splits[0].src.ids, splits[1].src.ids]) - self.assertAllEqual(expected_ids_split_1, split_ids[0]) - self.assertAllEqual(expected_ids_split_2, split_ids[1]) - - def testSplitTargets(self): - p = self._CreateNmtInputParams() - num_splits = 2 - - with self.session(use_gpu=False): - inp = input_generator.NmtInput(p) - fetched = self.evaluate(inp.SplitInputBatch(num_splits)) - - expected_ids_split_1 = [ - [ - 1, 400, 5548, 12, 583, 43, 61, 179, 1265, 22, 27, 7193, 16, 5, 782, - 14077, 6734, 4, 0 - ], - [ - 1, 1639, 32, 1522, 93, 38, 6812, 2624, 9, 2440, 3, 39, 11, 2364, - 24238, 9, 317, 4, 0 - ], - ] - - expected_ids_split_2 = [ - [ - 1, 53, 17787, 12, 3, 5, 1554, 871, 9, 1398, 3, 2784, 18, 25579, 942, - 29828, 5998, 77, 4 - ], - [ - 1, 67, 4141, 11483, 2008, 6, 483, 46, 23, 14852, 3, 39, 5, 9732, - 495, 3176, 21523, 4, 0 - ], - ] - - self.assertAllEqual(expected_ids_split_1, fetched[0].tgt.ids) - self.assertAllEqual(expected_ids_split_2, fetched[1].tgt.ids) - - def testTextPackedInputProto(self): - p = input_generator.TextPackedInput.Params() - p.flush_every_n = 0 - p.require_sequential_order = True - p.repeat_count = 1 - p.file_pattern = 'tfrecord:' + test_helper.test_src_dir_path( - 'tasks/mt/testdata/en_fr.tfrecord') - p.pad_to_max_seq_length = True - p.tokenizer = tokenizers.AsciiTokenizer.Params() - p.input_file_type = 'sentence_proto' - p.source_max_length = 22 - p.target_max_length = 24 - p.bucket_batch_limit = [2] - with self.session() as sess: - inp = p.Instantiate() - batch_tensor = inp.GetPreprocessedInputBatch() - for k, x in batch_tensor.FlattenItems(): - self.assertTrue(x.shape.is_fully_defined(), k) - batch, num_examples = sess.run([batch_tensor, inp.GlobalBatchSize()]) - self.assertEqual(num_examples, 2) - self.assertEqual(len(batch.src), 8) - self.assertAllEqual(batch.src.strs, - [b'I love paragliding!', b'vol biv paragliding']) - self.assertAllEqual(batch.tgt.strs, - [b"J'adore le parapente!", b'vol biv parapente']) - self.assertAllEqual( - batch.src.ids, - np.array([ - [ - 13, 3, 16, 19, 26, 9, 3, 20, 5, 22, 5, 11, 16, 13, 8, 13, 18, - 11, 35, 2, 0, 0 - ], - [ - 26, 19, 16, 3, 6, 13, 26, 3, 20, 5, 22, 5, 11, 16, 13, 8, 13, - 18, 11, 2, 0, 0 - ], - ])) - self.assertAllEqual( - batch.tgt.ids, - np.array([ - [ - 1, 14, 32, 5, 8, 19, 22, 9, 3, 16, 9, 3, 20, 5, 22, 5, 20, 9, - 18, 24, 9, 35, 0, 0 - ], - [ - 1, 26, 19, 16, 3, 6, 13, 26, 3, 20, 5, 22, 5, 20, 9, 18, 24, 9, - 0, 0, 0, 0, 0, 0 - ], - ])) - self.assertAllEqual( - batch.tgt.labels, - np.array([ - [ - 14, 32, 5, 8, 19, 22, 9, 3, 16, 9, 3, 20, 5, 22, 5, 20, 9, 18, - 24, 9, 35, 2, 0, 0 - ], - [ - 26, 19, 16, 3, 6, 13, 26, 3, 20, 5, 22, 5, 20, 9, 18, 24, 9, 2, - 0, 0, 0, 0, 0, 0 - ], - ])) - - def testTextPackedInputNoPerHostInfeed(self): - # We need to move the call to _DataSourceToInputBatch() to in _InputBatch() - # to place it per host. This in turn requires us to stop override - # GlobalBatchSize() but add a metric instead. - # For now, use_per_host_infeed is not supported. - p = input_generator.TextPackedInput.Params() - p.file_pattern = 'text:' + test_helper.test_src_dir_path( - 'tasks/mt/testdata/en_de.text') - p.use_per_host_infeed = True - p.file_random_seed = 0 - self.assertRaisesRegex( - ValueError, - 'This input generator does not support p.use_per_host_infeed', - p.Instantiate) - - def testTextPackedInputTextWpm(self): - p = input_generator.TextPackedInput.Params() - p.flush_every_n = 0 - p.require_sequential_order = True - p.repeat_count = 1 - p.file_pattern = 'text:' + test_helper.test_src_dir_path( - 'tasks/mt/testdata/en_de.text') - p.tokenizer = tokenizers.WpmTokenizer.Params().Set( - vocab_filepath=test_helper.test_src_dir_path( - 'tasks/mt/wpm-ende-2k.voc'), - vocab_size=2000) - p.source_max_length = 12 - p.target_max_length = 15 - p.bucket_batch_limit = [2] - with self.session() as sess: - inp = p.Instantiate() - batch_tensor = inp.GetPreprocessedInputBatch() - batch, num_examples = sess.run([batch_tensor, inp.GlobalBatchSize()]) - self.assertEqual(num_examples, 2) - print(batch) - self.assertAllEqual( - batch.src.ids, - np.array([[109, 251, 98, 595, 1009, 245, 326, 129, 4, 2, 0, 0], - [115, 276, 18, 66, 2, 0, 0, 0, 0, 0, 0, 0]])) - self.assertAllEqual( - batch.tgt.ids, - np.array([[ - 1, 197, 446, 458, 419, 284, 323, 1411, 571, 456, 409, 13, 4, 0, 0 - ], [1, 115, 281, 18, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])) - self.assertAllEqual( - batch.tgt.labels, - np.array([[ - 197, 446, 458, 419, 284, 323, 1411, 571, 456, 409, 13, 4, 2, 0, 0 - ], [115, 281, 18, 66, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])) - - def testTextPackedInputTextPacking(self): - p = input_generator.TextPackedInput.Params() - p.flush_every_n = 0 - p.require_sequential_order = True - p.file_pattern = 'text:' + test_helper.test_src_dir_path( - 'tasks/mt/testdata/en_de.text') - p.tokenizer = tokenizers.WpmTokenizer.Params().Set( - vocab_filepath=test_helper.test_src_dir_path( - 'tasks/mt/wpm-ende-2k.voc'), - vocab_size=2000) - # We repeat the 2-line file twice for a batch of 2, each packing both lines. - p.repeat_count = 2 - p.source_max_length = 16 - p.target_max_length = 20 - p.bucket_batch_limit = [2] - p.packing_factor = 2 - with self.session() as sess: - inp = p.Instantiate() - batch_tensor = inp.GetPreprocessedInputBatch() - batch, num_examples = sess.run([batch_tensor, inp.GlobalBatchSize()]) - self.assertEqual(num_examples, 4) - self.assertAllEqual( - batch.src.ids, - np.array([ - [ - 109, 251, 98, 595, 1009, 245, 326, 129, 4, 2, 115, 276, 18, 66, - 2, 0 - ], - [ - 115, 276, 18, 66, 2, 109, 251, 98, 595, 1009, 245, 326, 129, 4, - 2, 0 - ], - ])) - self.assertAllEqual( - batch.src.segment_ids, - np.array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0], - [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0]], - dtype=np.float32)) - self.assertAllEqual( - batch.src.segment_pos, - np.array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 0], - [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]])) - self.assertAllEqual( - batch.src.strs, - np.array([ - b'Too much has changed.\tHello!', b'Hello!\tToo much has changed.' - ])) - - self.assertAllEqual( - batch.tgt.ids, - np.array([ - [ - 1, 197, 446, 458, 419, 284, 323, 1411, 571, 456, 409, 13, 4, 1, - 115, 281, 18, 66, 0, 0 - ], - [ - 1, 115, 281, 18, 66, 1, 197, 446, 458, 419, 284, 323, 1411, 571, - 456, 409, 13, 4, 0, 0 - ], - ])) - self.assertAllEqual( - batch.tgt.labels, - np.array([ - [ - 197, 446, 458, 419, 284, 323, 1411, 571, 456, 409, 13, 4, 2, - 115, 281, 18, 66, 2, 0, 0 - ], - [ - 115, 281, 18, 66, 2, 197, 446, 458, 419, 284, 323, 1411, 571, - 456, 409, 13, 4, 2, 0, 0 - ], - ])) - self.assertAllEqual( - batch.tgt.segment_ids, - np.array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 0], - [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0]], - dtype=np.float32)) - self.assertAllEqual( - batch.tgt.segment_pos, - np.array( - [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, 0, 0], - [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0]])) - self.assertAllEqual( - batch.tgt.strs, - np.array([ - b'Daf\xc3\xbcr hat sich zu viel ver\xc3\xa4ndert.\tHallo!', - b'Hallo!\tDaf\xc3\xbcr hat sich zu viel ver\xc3\xa4ndert.' - ])) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/mt/layers.py b/lingvo/tasks/mt/layers.py deleted file mode 100644 index 2b25fcb1..00000000 --- a/lingvo/tasks/mt/layers.py +++ /dev/null @@ -1,190 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -"""Lingvo MT layers. -""" - -import lingvo.compat as tf -from lingvo.core import base_layer -from lingvo.core import layers -from lingvo.core import layers_with_attention - - -class TransformerStack(base_layer.BaseLayer): - """Stacked self- multi-head attention and fully connected layers. - - With optional layer normalization applied to the final output. - - See 'Attention Is All You Need' https://arxiv.org/abs/1706.03762 - for details. - """ - - @classmethod - def Params(cls): - """Configs for TransformerStack.""" - p = super().Params() - - # Transformer related - p.Define('model_dim', 1024, 'Characteristic depth (dimension).') - p.Define('num_transformer_layers', 6, 'Number of transformer layers.') - p.Define( - 'transformer_tpl', layers_with_attention.TransformerLayer.Params(), - 'TransformerLayer params tpl. Can be a list of params. ' - 'num_transformer_layers should be divisible by len(transformer_tpl).') - - p.Define('ln_tpl', layers.LayerNorm.Params(), 'Layer norm default params') - p.Define('ln_output', False, - 'If set, layer normalization is applied to the final output' - ' of the encoder transformer stack.') - - p.Define('is_transparent', False, - 'If set, outputs a merger of embeddings and layer outputs.') - p.Define('num_transparent_outputs', 6, 'Number of transparent outputs.') - p.Define( - 'transparent_merger_tpl', - layers.WeightedSumLayer.Params().Set(add_weight_summaries=True), - 'Merger op for layer outputs.') - p.Define('packed_input', False, - 'If True, assumes multiple training samples per input.') - p.Define('has_aux_attention', False, - 'Allows encoder layers to attend auxiliary inputs.') - p.Define('mask_self_atten', False, 'If True, use masked self-attention.') - p.transformer_tpl.tr_atten_tpl.num_attention_heads = 8 - p.transformer_tpl.tr_fflayer_tpl.hidden_dim = 8192 - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - # Add transformer layers. - transformer_layer_params = [] - denom = 1 - if isinstance(p.transformer_tpl, list): - denom = len(p.transformer_tpl) - assert p.num_transformer_layers % len(p.transformer_tpl) == 0 - for i in range(p.num_transformer_layers // denom): - if isinstance(p.transformer_tpl, list): - for q in p.transformer_tpl: - params = q.Copy() - transformer_layer_params.append(params) - else: - params = p.transformer_tpl.Copy() - transformer_layer_params.append(params) - - for i, params in enumerate(transformer_layer_params): - params.name = 'trans_%d' % i - params.source_dim = p.model_dim - params.packed_input = p.packed_input - params.has_aux_atten = p.has_aux_attention - params.mask_self_atten = p.mask_self_atten - - self.CreateChildren('trans', transformer_layer_params) - - # Initialize TransformerStack output layer norm - if p.ln_output: - params = p.ln_tpl.Copy() - # Keeping historic 'enc_out_ln' name for checkpoint compatibility. - params.name = 'enc_out_ln' - params.input_dim = p.model_dim - self.CreateChild('layer_norm_out', params) - - if p.is_transparent: - transparent_params = [] - if not p.num_transparent_outputs: - raise ValueError('num_transparent_outputs should be greater than 0.') - for i in range(p.num_transparent_outputs): - transparent_param = p.transparent_merger_tpl.Copy() - transparent_param.name = 'transparent_%d' % i - transparent_param.num_sources = 1 + len(transformer_layer_params) - transparent_params.append(transparent_param) - self.CreateChildren('transparent_merger', transparent_params) - - def FProp(self, - theta, - transformer_input, - paddings, - src_segment_id=None, - aux_vecs=None, - aux_paddings=None, - aux_segment_id=None): - """Transforms source sequence of Tensors with Transformers layers. - - Args: - theta: A `.NestedMap` object containing weights' values of this - layer and its children layers. - transformer_input: A sequence of input Tensors of [time, batch, dim] - shape. - paddings: A sequence of 0s and 1s indicating input paddings of - [time, batch] shape. - src_segment_id: A sequence of ints indicating segment ids of - [time, batch] shape. - aux_vecs: A sequence of input Tensors of [aux_time, batch, dim] shape, as - context for the cross-attention layer. - aux_paddings: A sequence of 0s and 1s indicating input paddings of - [aux_time, batch] shape. - aux_segment_id: A sequence of ints indicating segment ids of - [aux_time, batch] shape. - - Returns: - (outputs, out_paddings, segment_ids) tuple. `outputs` is of the shape - [time, batch, depth], and `out_paddings` has shape [time, batch]. If - is_transparent is True, can return a list of num_transformer_layers - tensors of shape [time, batch, depth] if `self.do_eval` is False, and a - [time, batch, depth, num_transparent_outputs] tensor if `self.do_eval` is - True. If packed_input is True, also returns segment_id, otherwise returns - None. - """ - p = self.params - if p.packed_input: - assert src_segment_id is not None, ('Need to specify src_segment_id if ' - 'packed input is supported.') - outputs_list = [transformer_input] - with tf.name_scope(p.name): - for i, transformer_l in enumerate(self.trans): - - # For encoder, keys, values and queries are the same - transformer_output, _ = transformer_l.FProp( - theta.trans[i], - transformer_input, - paddings, - aux_vecs=aux_vecs, - aux_paddings=aux_paddings, - source_segment_id=src_segment_id, - aux_segment_id=aux_segment_id) - transformer_input = transformer_output - outputs_list.append(transformer_output) - - if p.ln_output: - transformer_output = self.layer_norm_out.FProp(theta.layer_norm_out, - transformer_output) - - # When is_transparent is set, it outputs a list of tensors during - # training and the stacked tensors otherwise. This dual behavior is meant - # to avoid excessive memory usage during training (which was prohibiting - # training on TPUs), and simplify the beam search interface. - if p.is_transparent: - if p.num_transparent_outputs == 1: - transformer_output = self.transparent_merger[0].FProp( - theta.transparent_merger[0], outputs_list) - else: - transformer_output = [] - for i in range(p.num_transparent_outputs): - merged_outputs = self.transparent_merger[i].FProp( - theta.transparent_merger[i], outputs_list) - transformer_output.append(merged_outputs) - if self.do_eval: - transformer_output = tf.stack(transformer_output, 3) - - return transformer_output, paddings, src_segment_id diff --git a/lingvo/tasks/mt/layers_test.py b/lingvo/tasks/mt/layers_test.py deleted file mode 100644 index dc14a28a..00000000 --- a/lingvo/tasks/mt/layers_test.py +++ /dev/null @@ -1,363 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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 -"""Tests for mt.layers.""" - -import lingvo.compat as tf -from lingvo.core import layers_with_attention -from lingvo.core import py_utils -from lingvo.core import test_utils -from lingvo.tasks.mt import layers as mt_layers -import numpy as np - - -NUMPY_RANDOM_SEED = 505837249 - - -class LayersTest(test_utils.TestCase): - - def _TransformerParams(self, layer=mt_layers.TransformerStack): - model_dim = 2 - params = layer.Params() - params.name = 'transformer' - params.model_dim = model_dim - params.num_transformer_layers = 1 - # Note: hidden_dim % num_attention_heads == 0 - params.transformer_tpl.tr_atten_tpl.num_attention_heads = 1 - params.transformer_tpl.tr_fflayer_tpl.hidden_dim = model_dim - params.random_seed = 0 - return params - - def _ContextualTransformerParams(self, - layer=mt_layers.TransformerStack): - params = self._TransformerParams(layer) - params.has_aux_attention = True - return params - - def _MaskedTransformerParams(self, - layer=mt_layers.TransformerStack): - params = self._TransformerParams(layer) - params.mask_self_atten = True - return params - - def _TransformerStackFProp(self, dtype, fprop_dtype, layer): - # time = 2, - batch = 3 - tf.flags.FLAGS.tpu_compatible = True - with self.session(use_gpu=False): - params = self._TransformerParams(layer=layer) - params.dtype = dtype - params.fprop_dtype = fprop_dtype - xformer = layer(params) - - input_arr = np.array( - [ - [[0, 1]] * batch, - [[1, -1]] * batch, - ], dtype=int) - paddings_arr = np.array([[0] * batch, [0] * batch], dtype=int) - inputs = tf.constant(input_arr.tolist(), dtype=fprop_dtype) - paddings = tf.constant(paddings_arr.tolist(), dtype=fprop_dtype) - - output, _, _ = xformer.FProp(xformer.theta, inputs, paddings) - - self.evaluate(tf.global_variables_initializer()) - output = self.evaluate(output) - - self.assertAllCloseAccordingToType( - [[[2.761079, -3.756719]] * batch, [[-2.623049, 3.3679538]] * batch], - output) - - def testTransformerStackFPropFp32Fp32(self): - self._TransformerStackFProp(tf.float32, tf.float32, - mt_layers.TransformerStack) - - def testTransformerStackAlternateLayers(self): - batch = 3 - tf.flags.FLAGS.tpu_compatible = True - with self.session(use_gpu=False): - model_dim = 2 - num_transformer_layers = 2 - transformer_tpl = layers_with_attention.TransformerLayer.Params() - transformer_tpl.tr_atten_tpl.num_attention_heads = 1 - transformer_tpl.tr_fflayer_tpl.hidden_dim = 2 - - params = mt_layers.TransformerStack.Params().Set( - name='transformer', - model_dim=model_dim, - num_transformer_layers=num_transformer_layers, - transformer_tpl=[ - transformer_tpl.Copy() for _ in range(num_transformer_layers) - ], - random_seed=123456) - - xformer = mt_layers.TransformerStack(params) - input_arr = np.array([ - [[0, 1]] * batch, - [[1, -1]] * batch, - ], dtype=int) - paddings_arr = np.array([[0] * batch, [0] * batch], dtype=int) - inputs = tf.constant( - input_arr.tolist(), dtype=py_utils.FPropDtype(params)) - paddings = tf.constant( - paddings_arr.tolist(), dtype=py_utils.FPropDtype(params)) - output, _, _ = xformer.FProp(xformer.theta, inputs, paddings) - - self.evaluate(tf.global_variables_initializer()) - output = self.evaluate(output) - print(repr(output)) - self.assertAllCloseAccordingToType( - np.array([[[-0.940543, 1.479253]] * batch, - [[-0.413938, -2.550903]] * batch]), output) - - def testTransformerStackFPropWithPackedInputs(self): - # batch = 2. time = 2, depth = 2 - with self.session(use_gpu=True): - with tf.variable_scope('packing_test', reuse=tf.AUTO_REUSE): - params = self._TransformerParams() - xformer = mt_layers.TransformerStack(params) - packed_params = params.Copy() - packed_params.packed_input = True - xformer_packed = mt_layers.TransformerStack(packed_params) - - input_arr = np.array([[[0, 1], [1, -1]], [[1, 2], [-2, -1]]], dtype=int) - paddings_arr = np.array([[0, 0], [0, 0]], dtype=int) - seg_id_arr = np.array([[0, 1, 0, 1]], dtype=int) - - inputs = tf.constant(input_arr.tolist(), dtype=tf.float32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - inputs_packed = tf.reshape(inputs, [-1, 1, 2]) - paddings_packed = tf.reshape(paddings, [-1, 1]) - seg_id = tf.transpose( - tf.constant(seg_id_arr.tolist(), dtype=tf.float32)) - - output, _, _ = xformer.FProp(xformer.theta, inputs, paddings, seg_id) - - output_packed, _, _ = xformer_packed.FProp( - xformer_packed.theta, inputs_packed, paddings_packed, seg_id) - output_packed = tf.reshape(output_packed, tf.shape(output)) - - self.evaluate(tf.global_variables_initializer()) - output, output_packed = self.evaluate([output, output_packed]) - - self.assertAllClose(output_packed, output) - - def testTransparentTransformerStackTrainFProp(self): - # time = 2, batch = 1 - with self.session(use_gpu=True): - params = self._TransformerParams() - params.is_transparent = True - params.num_transparent_outputs = 2 - - xformer = mt_layers.TransformerStack(params) - - input_arr = np.array([[[0, 1]], [[1, -1]]], dtype=int) - paddings_arr = np.array([[0], [0]], dtype=int) - - inputs = tf.constant(input_arr.tolist(), dtype=tf.float32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - - self.evaluate(tf.global_variables_initializer()) - outputs, _, _ = xformer.FPropDefaultTheta(inputs, paddings) - out_1, out_2 = self.evaluate(outputs) - self.assertAllClose([[[1.38054, -1.37836]], [[-0.811525, 1.183977]]], - out_1) - self.assertAllClose([[[1.38054, -1.37836]], [[-0.811525, 1.183977]]], - out_2) - - def testTransparentTransformerStackEvalFProp(self): - # time = 2, batch = 1 - with self.session(use_gpu=True), self.SetEval(True): - params = self._TransformerParams() - params.is_transparent = True - params.num_transparent_outputs = 2 - - xformer = mt_layers.TransformerStack(params) - - input_arr = np.array([[[0, 1]], [[1, -1]]], dtype=int) - paddings_arr = np.array([[0], [0]], dtype=int) - - inputs = tf.constant(input_arr.tolist(), dtype=tf.float32) - paddings = tf.constant(paddings_arr.tolist(), dtype=tf.float32) - - self.evaluate(tf.global_variables_initializer()) - outputs, _, _ = xformer.FPropDefaultTheta(inputs, paddings) - out = self.evaluate(outputs) - self.assertAllClose([[[1.38054, -1.37836]], [[-0.811525, 1.183977]]], - out[:, :, :, 0]) - self.assertAllClose([[[1.38054, -1.37836]], [[-0.811525, 1.183977]]], - out[:, :, :, 1]) - - def _TransformerSingleSourceInputs(self, depth=3, dtype=tf.float32): - np.random.seed(NUMPY_RANDOM_SEED) - source_vecs = tf.stack( - [tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(5)]) - source_padding = tf.transpose( - tf.constant([[0, 0, 1, 1, 0], [1, 0, 0, 0, 1]], dtype=dtype)) - aux_source_vecs = tf.stack( - [tf.constant(np.random.rand(2, depth), dtype=dtype) for _ in range(7)]) - aux_source_paddings = tf.transpose( - tf.constant( - [[0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1]], dtype=dtype)) - return source_vecs, source_padding, aux_source_vecs, aux_source_paddings - - def _TransformerMultiSourceInputs(self, depth=3, dtype=tf.float32): - np.random.seed(NUMPY_RANDOM_SEED) - src_names = ['en1', 'en2', 'de'] - slens = [11, 10, 9] - sbatch = 3 - tlen = 5 - source_vecs = tf.constant( - np.random.uniform(size=(tlen, sbatch*2, depth)), dtype) - source_padding = tf.constant(np.zeros([tlen, sbatch*2, 1]), dtype) - aux_source_vecs = py_utils.NestedMap() - aux_source_paddings = py_utils.NestedMap() - for slen, sname in zip(slens, src_names): - aux_source_vecs[sname] = tf.constant( - np.random.uniform(size=[slen, sbatch, depth]), dtype) - aux_source_paddings[sname] = tf.constant(np.zeros([slen, sbatch]), dtype) - return (source_vecs, source_padding, aux_source_vecs, aux_source_paddings) - - def _ExpectedSingleSourceResults(self): - # pylint: disable=bad-whitespace - # pyformat: disable - expected_ctx = [ - [[-1.2441386 , 0.79554689, 0.92997617, -0.11617106], - [-0.35435563, -1.3054322 , 1.22436929, 0.78819978]], - [[ 0.25911736, 1.59861779, -0.79328805, -0.69870573], - [-0.07478544, -0.97613871, -0.28578228, 1.68864977]], - [[ 1.70767629, 0.47948313, -1.04493523, -0.77614671], - [-0.7304405 , -0.97698098, 1.06615043, 0.99392742]], - [[-0.8379035 , -0.4325709 , 1.64835262, -0.01260686], - [ 1.9789927 , -0.0406196 , -0.70901859, -0.87701303]], - [[-0.31843656, -0.88714617, -0.10433094, 1.6746937 ], - [-0.75752175, 0.063187 , -0.50349152, 1.54967213]]] - expected_probs = [ - [[ 0.2506268 , 0., 0.2491456 , 0., 0.24775073, 0., 0.25247693], - [ 0., 0.33213311, 0., 0.3369872 , 0., 0.33087969, 0. ]], - [[ 0.2478824 , 0., 0.24961777, 0., 0.24944726, 0., 0.25305259], - [ 0., 0.32697931, 0., 0.33510387, 0., 0.33791685, 0. ]], - [[ 0.24868786, 0., 0.2501137 , 0., 0.25135297, 0., 0.24984553], - [ 0., 0.33173615, 0. , 0.3365739, 0., 0.33168989, 0. ]], - [[ 0.25286067, 0., 0.24919504, 0., 0.24831679, 0., 0.24962756], - [ 0., 0.33384994, 0., 0.33144677, 0., 0.33470333, 0. ]], - [[ 0.24830991, 0., 0.25117707, 0., 0.25028574, 0., 0.25022724], - [ 0., 0.32832602, 0., 0.33358034, 0., 0.33809367, 0. ]]] - # pyformat: enable - # pylint: enable=bad-whitespace - return expected_ctx, expected_probs - - def testTransformerAttentionLayerReference(self): - depth = 4 - p = layers_with_attention.TransformerAttentionLayer.Params() - p.name = 'transformer_atten' - p.source_dim = depth - p.is_masked = False - p.num_attention_heads = 2 - p.atten_tpl.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - transformer_atten_ref = layers_with_attention.TransformerAttentionLayer(p) - - (query_vec, _, aux_vecs, - aux_paddings) = self._TransformerSingleSourceInputs(depth) - - ctx_ref, probs_ref = transformer_atten_ref.FPropDefaultTheta( - query_vec, aux_paddings, aux_vecs) - - expected_ctx, expected_probs = self._ExpectedSingleSourceResults() - with self.session(use_gpu=True): - self.evaluate(tf.global_variables_initializer()) - actual_ctx_ref, actual_probs_ref = self.evaluate([ctx_ref, probs_ref]) - tf.logging.info(np.array_repr(actual_ctx_ref)) - tf.logging.info(np.array_repr(actual_probs_ref)) - self.assertAllClose(expected_ctx, actual_ctx_ref) - self.assertAllClose(expected_probs, actual_probs_ref) - - def testContextualTransformerStackFProp(self): - # time = 2, - batch = 3 - dtype = tf.float32 - fprop_dtype = tf.float32 - layer = mt_layers.TransformerStack - tf.flags.FLAGS.tpu_compatible = True - with self.session(use_gpu=False): - params = self._ContextualTransformerParams(layer=layer) - params.dtype = dtype - params.fprop_dtype = fprop_dtype - xformer = layer(params) - - input_arr = np.array([ - [[0, 1]] * batch, - [[1, -1]] * batch, - ], dtype=int) - - context_arr = np.array([ - [[0, 1]] * batch, - [[1, -1]] * batch, - [[-1, 1]] * batch, - ], - dtype=int) - paddings_arr = np.array([[0] * batch, [0] * batch], dtype=int) - context_paddings_arr = np.array([[0] * batch, [0] * batch, [0] * batch], - dtype=int) - inputs = tf.constant(input_arr.tolist(), dtype=fprop_dtype) - paddings = tf.constant(paddings_arr.tolist(), dtype=fprop_dtype) - context = tf.constant(context_arr.tolist(), dtype=fprop_dtype) - context_paddings = tf.constant( - context_paddings_arr.tolist(), dtype=fprop_dtype) - - output, _, _ = xformer.FProp( - xformer.theta, - inputs, - paddings, - aux_vecs=context, - aux_paddings=context_paddings) - - self.evaluate(tf.global_variables_initializer()) - output = self.evaluate(output) - self.assertAllCloseAccordingToType( - [[[-2.622666, 3.367474]] * batch, [[2.719156, -3.707336]] * batch], - output) - - def testMaskedTransformerStackFProp(self): - batch = 3 - dtype = tf.float32 - fprop_dtype = tf.float32 - layer = mt_layers.TransformerStack - tf.flags.FLAGS.tpu_compatible = True - with self.session(use_gpu=False): - params = self._MaskedTransformerParams(layer=layer) - params.dtype = dtype - params.fprop_dtype = fprop_dtype - xformer = layer(params) - - input_arr = np.array([ - [[0, 1]] * batch, - [[1, -1]] * batch, - ], dtype=int) - - paddings_arr = np.array([[0] * batch, [0] * batch], dtype=int) - - inputs = tf.constant(input_arr.tolist(), dtype=fprop_dtype) - paddings = tf.constant(paddings_arr.tolist(), dtype=fprop_dtype) - - output, _, _ = xformer.FProp(xformer.theta, inputs, paddings) - - self.evaluate(tf.global_variables_initializer()) - output = self.evaluate(output) - self.assertAllClose( - [[[2.761103, -3.756748]] * batch, [[-2.623049, 3.367954]] * batch], - output) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/mt/model.py b/lingvo/tasks/mt/model.py deleted file mode 100644 index 45fb1237..00000000 --- a/lingvo/tasks/mt/model.py +++ /dev/null @@ -1,386 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""MT models.""" - -import lingvo.compat as tf -from lingvo.core import base_model -from lingvo.core import insertion -from lingvo.core import metrics -from lingvo.core import py_utils -from lingvo.tasks.mt import decoder -from lingvo.tasks.mt import encoder - - -class MTBaseModel(base_model.BaseTask): - """Base Class for NMT models.""" - - def _EncoderDevice(self): - """Returns the device to run the encoder computation.""" - if py_utils.use_tpu(): - return tf.device(self.cluster.WorkerDeviceInModelSplit(0)) - else: - return tf.device('') - - def _DecoderDevice(self): - """Returns the device to run the decoder computation.""" - if py_utils.use_tpu(): - return tf.device(self.cluster.WorkerDeviceInModelSplit(1)) - else: - return tf.device('') - - def __init__(self, params): - super().__init__(params) - p = self.params - if p.encoder: - self.CreateChild('enc', p.encoder) - self.CreateChild('dec', p.decoder) - - def _CreateChildrenVariables(self): - p = self.params - with tf.variable_scope(p.name): - with self._EncoderDevice(): - if p.encoder: - self.enc.InstantiateVariables() - with self._DecoderDevice(): - self.dec.InstantiateVariables() - super()._CreateChildrenVariables() - - def ComputePredictions(self, theta, batch): - p = self.params - - with self._EncoderDevice(): - encoder_outputs = ( - self.enc.FProp(theta.enc, batch.src) if p.encoder else None) - with self._DecoderDevice(): - predictions = self.dec.ComputePredictions(theta.dec, encoder_outputs, - batch.tgt) - if isinstance(predictions, py_utils.NestedMap): - # Pass through encoder output as well for possible use as a FProp output - # for various meta-MT modeling approaches, such as MT quality estimation - # classification. - predictions['encoder_outputs'] = encoder_outputs - return predictions - - def ComputeLoss(self, theta, predictions, input_batch): - with self._DecoderDevice(): - return self.dec.ComputeLoss(theta.dec, predictions, input_batch.tgt) - - def _GetTokenizerKeyToUse(self, key): - """Returns a tokenizer key to use for the provided `key`.""" - if key in self.input_generator.tokenizer_dict: - return key - return None - - def _BeamSearchDecode(self, input_batch): - p = self.params - with tf.name_scope('fprop'), tf.name_scope(p.name): - encoder_outputs = self.enc.FPropDefaultTheta(input_batch.src) - encoder_outputs = self.dec.AddExtraDecodingInfo(encoder_outputs, - input_batch.tgt) - decoder_outs = self.dec.BeamSearchDecode(encoder_outputs) - - topk_hyps = decoder_outs.topk_hyps - topk_ids = decoder_outs.topk_ids - topk_lens = decoder_outs.topk_lens - topk_scores = decoder_outs.topk_scores - - slen = tf.cast( - tf.round(tf.reduce_sum(1 - input_batch.src.paddings, 1) - 1), - tf.int32) - srcs = self.input_generator.IdsToStrings( - input_batch.src.ids, slen, self._GetTokenizerKeyToUse('src')) - topk_decoded = self.input_generator.IdsToStrings( - topk_ids, topk_lens - 1, self._GetTokenizerKeyToUse('tgt')) - topk_decoded = tf.reshape(topk_decoded, tf.shape(topk_hyps)) - topk_scores = tf.reshape(topk_scores, tf.shape(topk_hyps)) - - refs = self.input_generator.IdsToStrings( - input_batch.tgt.labels, - tf.cast( - tf.round(tf.reduce_sum(1.0 - input_batch.tgt.paddings, 1) - 1.0), - tf.int32), self._GetTokenizerKeyToUse('tgt')) - - ret_dict = { - 'target_ids': input_batch.tgt.ids, - 'target_labels': input_batch.tgt.labels, - 'target_weights': input_batch.tgt.weights, - 'target_paddings': input_batch.tgt.paddings, - 'sources': srcs, - 'targets': refs, - 'topk_decoded': topk_decoded, - 'topk_lens': topk_lens, - 'topk_scores': topk_scores, - } - return ret_dict - - def _PostProcessBeamSearchDecodeOut(self, dec_out_dict, dec_metrics_dict): - """Post processes the output from `_BeamSearchDecode`.""" - p = self.params - topk_scores = dec_out_dict['topk_scores'] - topk_decoded = dec_out_dict['topk_decoded'] - targets = dec_out_dict['targets'] - sources = dec_out_dict['sources'] - unsegment = dec_metrics_dict['corpus_bleu'].unsegmenter - - num_samples = len(targets) - assert num_samples == len(topk_decoded), ( - '%s vs %s' % (num_samples, len(topk_decoded))) - assert num_samples == len(sources) - dec_metrics_dict['num_samples_in_batch'].Update(num_samples) - - key_value_pairs = [] - for i in range(num_samples): - src, tgt = sources[i], targets[i] - src_unseg, tgt_unseg = unsegment(src), unsegment(tgt) - tf.logging.info('source: %s', src_unseg) - tf.logging.info('target: %s', tgt_unseg) - hyps = topk_decoded[i] - assert p.decoder.beam_search.num_hyps_per_beam == len(hyps) - info_str = u'src: {} tgt: {} '.format(src_unseg, tgt_unseg) - for n, (score, hyp_str) in enumerate(zip(topk_scores[i], hyps)): - hyp_str_unseg = unsegment(hyp_str) - tf.logging.info(' %f: %s', score, hyp_str_unseg) - info_str += u' hyp{n}: {hyp} score{n}: {score}'.format( - n=n, hyp=hyp_str_unseg, score=score) - # Only aggregate scores of the top hypothesis. - if n == 0: - dec_metrics_dict['corpus_bleu'].Update(tgt, hyp_str) - key_value_pairs.append((src_unseg, info_str)) - return key_value_pairs - - def CreateDecoderMetrics(self): - decoder_metrics = { - 'num_samples_in_batch': metrics.AverageMetric(), - 'corpus_bleu': metrics.CorpusBleuMetric(separator_type='wpm'), - } - return decoder_metrics - - def Decode(self, input_batch): - """Constructs the decoding graph.""" - return self._BeamSearchDecode(input_batch) - - def PostProcessDecodeOut(self, dec_out, dec_metrics): - return self._PostProcessBeamSearchDecodeOut(dec_out, dec_metrics) - - -class TransformerModel(MTBaseModel): - """Transformer Model. - - Implements Attention is All You Need: - https://arxiv.org/abs/1706.03762 - """ - - @classmethod - def Params(cls): - p = super().Params() - p.encoder = encoder.TransformerEncoder.Params() - p.decoder = decoder.TransformerDecoder.Params() - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - assert p.encoder.model_dim == p.decoder.source_dim - - -class RNMTModel(MTBaseModel): - """RNMT+ Model. - - Implements RNMT Variants in The Best of Both Worlds paper: - https://aclweb.org/anthology/P18-1008 - """ - - @classmethod - def Params(cls): - p = super().Params() - p.encoder = encoder.MTEncoderBiRNN.Params() - p.decoder = decoder.MTDecoderV1.Params() - return p - - -class InsertionModel(MTBaseModel): - """Insertion-based model. - - References: - KERMIT: https://arxiv.org/pdf/1906.01604.pdf - Insertion Transformer: https://arxiv.org/pdf/1902.03249.pdf - """ - - @classmethod - def Params(cls): - p = super().Params() - p.decoder = decoder.InsertionDecoder.Params() - p.Define('insertion', insertion.SymbolInsertionLayer.Params(), - 'Insertion specifications (i.e., rollin and oracle policy).') - return p - - def __init__(self, params): - super().__init__(params) - p = self.params - - self.CreateChild('insertion', p.insertion) - - def _SampleCanvasAndTargets(self, x, x_paddings): - """Sample a canvas and its corresponding targets. - - Args: - x: A Tensor representing the canvas. - x_paddings: A Tensor representing the canvas paddings. - - Returns: - A `NestedMap` capturing the new sampled canvas and its targets. - """ - p = self.params - - # TODO(williamchan): Consider grabbing `eos_id` from `x` instead of `p`. - eos_id = p.decoder.target_eos_id - - # Sample a canvas (and it's corresponding targets). - return self.insertion.FProp(None, x, x_paddings, eos_id, True) - - def _CreateCanvasAndTargets(self, batch): - # pyformat: disable - """Create the canvas and targets. - - Args: - batch: A `.NestedMap`. - - - src: A `.NestedMap`. - - ids: The source ids, ends in . - - paddings: The source paddings. - - - tgt: A `.NestedMap`. - - ids: The target ids, ends in . - - paddings: The target paddings. - - Returns: - A `NestedMap`. - - canvas: The canvas (based off of the `rollin_policy`) of shape - [batch_size, c_dim]. - - canvas_paddings: The paddings of `canvas_indices`. - - target_indices: The target indices (i.e., use these indices to - tf.gather_nd the log-probs). Optional, only during training. - - target_weights: The target weights. Optional, only during training. - """ - # pyformat: enable - p = self.params - - if not self.do_eval: - # Sample our src and tgt canvas. - src_descriptor = self._SampleCanvasAndTargets(batch.src.ids, - batch.src.paddings) - tgt_descriptor = self._SampleCanvasAndTargets(batch.tgt.ids, - batch.tgt.paddings) - - # Offset the src ids (to unshare embeddings between src/tgt). Note, we - # only offset the canvas ids, but we do not offset the vocab ids. This - # will result in unshared embeddings, but shared softmax. This is due to - # GPU/TPU memory limitations, empirically it is known that unsharing - # everything results in better performance. - vocab_size = p.decoder.softmax.num_classes - src_descriptor.canvas = tf.where( - tf.equal(src_descriptor.canvas_paddings, 0), - src_descriptor.canvas + vocab_size, src_descriptor.canvas) - - # Offset the tgt indices (need shift according to src length). - batch_size = py_utils.GetShape(batch.src.ids)[0] - # `target_batch` is a [num_targets, batch_size] tensor where each row - # identifies which batch the target belongs to. Note the observation that, - # tf.reduce_sum(target_batch, 1) == 1 \forall rows. - target_batch = tf.cast( - tf.equal( - tf.expand_dims(tf.range(batch_size), 0), - tf.expand_dims(tgt_descriptor.target_indices[:, 0], 1)), tf.int32) - src_lens = tf.cast( - tf.reduce_sum(1 - src_descriptor.canvas_paddings, 1), tf.int32) - # `tgt_offset` is shape [num_targets] where each entry corresponds to the - # offset needed for that target (due to the source length). - tgt_offset = tf.matmul(target_batch, tf.expand_dims(src_lens, 1)) - # We shift the tgt slot without touching the batch or vocab. - tgt_descriptor.target_indices += tf.concat( - [tf.zeros_like(tgt_offset), tgt_offset, - tf.zeros_like(tgt_offset)], 1) - - # The canvas is simply the sequence-level concat of the src and tgt. - canvas, canvas_paddings = insertion.SequenceConcat( - src_descriptor.canvas, src_descriptor.canvas_paddings, - tgt_descriptor.canvas, tgt_descriptor.canvas_paddings) - target_indices = tf.concat( - [src_descriptor.target_indices, tgt_descriptor.target_indices], 0) - target_weights = tf.concat( - [src_descriptor.target_weights, tgt_descriptor.target_weights], 0) - - return py_utils.NestedMap( - canvas=canvas, - canvas_paddings=canvas_paddings, - target_indices=target_indices, - target_weights=target_weights) - - def ComputePredictions(self, theta, batch): - # pyformat: disable - """Compute the model predictions. - - Args: - theta: A `.NestedMap` object containing weights' values of this layer and - its children layers. - batch: A `.NestedMap`. - - - src: A `.NestedMap`. - - ids: The source ids, ends in . - - paddings: The source paddings. - - - tgt: A `.NestedMap`. - - ids: The target ids, ends in . - - paddings: The target paddings. - - Returns: - A `.NestedMap`. - - outputs: The contextualized output vectors of shape - [batch_size, time_dim, model_dim]. - - tgt: A `.NestedMap` (optional, only during training). - - ids: The canvas ids. - - paddings: The canvas paddings. - - target_indices: The target indices. - - target_weights: The target weights. - """ - # pyformat: enable - p = self.params - - # TODO(williamchan): Currently, we only support KERMIT mode (i.e., no - # encoder, unified architecture). - assert not p.encoder - - # Sometimes src and tgt have different types. We reconcile here and use - # int32. - batch.src.ids = tf.cast(batch.src.ids, tf.int32) - batch.tgt.ids = tf.cast(batch.tgt.ids, tf.int32) - - canvas_and_targets = self._CreateCanvasAndTargets(batch) - batch = py_utils.NestedMap( - tgt=py_utils.NestedMap( - ids=canvas_and_targets.canvas, - paddings=canvas_and_targets.canvas_paddings)) - - predictions = super().ComputePredictions(theta, batch) - - if not self.do_eval: - predictions.tgt = py_utils.NestedMap( - ids=canvas_and_targets.canvas, - paddings=canvas_and_targets.canvas_paddings, - target_indices=canvas_and_targets.target_indices, - target_weights=canvas_and_targets.target_weights) - - return predictions diff --git a/lingvo/tasks/mt/model_test.py b/lingvo/tasks/mt/model_test.py deleted file mode 100644 index e19146c4..00000000 --- a/lingvo/tasks/mt/model_test.py +++ /dev/null @@ -1,704 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for MT Models.""" - -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_layer -from lingvo.core import cluster_factory -from lingvo.core import optimizer -from lingvo.core import py_utils -from lingvo.core import schedule -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tasks.mt import decoder -from lingvo.tasks.mt import encoder -from lingvo.tasks.mt import input_generator -from lingvo.tasks.mt import model -import numpy as np - - -FLAGS = tf.flags.FLAGS - -_TF_RANDOM_SEED = 93820986 - - -class TestInputGenerator(base_input_generator.BaseSequenceInputGenerator): - - @classmethod - def Params(cls): - p = super().Params() - p.Define('split', True, '') - return p - - def __init__(self, params): - super().__init__(params) - self._step = 0 - - def InfeedBatchSize(self): - if self.params.split: - return 10 / 2 - - return 10 - - def _InputBatch(self): - np.random.seed(1) - bs, sl = 10, 7 - src_ids = tf.constant( - np.random.randint(low=0, high=8192 - 1, size=[bs, sl], dtype=np.int32)) - tgt_ids = tf.constant( - np.random.randint(low=0, high=8192 - 1, size=[bs, sl], dtype=np.int32)) - tgt_labels = tf.constant( - np.random.randint(low=0, high=8192 - 1, size=[bs, sl], dtype=np.int32)) - tgt_weights = tf.constant(np.ones(shape=[bs, sl], dtype=np.float32)) - - src_paddings = tf.zeros([bs, sl]) - tgt_paddings = tf.zeros([bs, sl]) - - ret = py_utils.NestedMap() - ret.src = py_utils.NestedMap() - ret.tgt = py_utils.NestedMap() - - if self.params.split: - src_ids = tf.split(src_ids, 2, 0) - src_paddings = tf.split(src_paddings, 2, 0) - tgt_ids = tf.split(tgt_ids, 2, 0) - tgt_labels = tf.split(tgt_labels, 2, 0) - tgt_paddings = tf.split(tgt_paddings, 2, 0) - tgt_weights = tf.split(tgt_weights, 2, 0) - - ret.src.ids = tf.cond( - tf.equal(tf.math.floormod(py_utils.GetGlobalStep(), 2), 0), - lambda: src_ids[0], lambda: src_ids[1]) - ret.src.paddings = tf.cond( - tf.equal(tf.math.floormod(py_utils.GetGlobalStep(), 2), 0), - lambda: src_paddings[0], lambda: src_paddings[1]) - ret.tgt.ids = tf.cond( - tf.equal(tf.math.floormod(py_utils.GetGlobalStep(), 2), 0), - lambda: tgt_ids[0], lambda: tgt_ids[1]) - ret.tgt.labels = tf.cond( - tf.equal(tf.math.floormod(py_utils.GetGlobalStep(), 2), 0), - lambda: tgt_labels[0], lambda: tgt_labels[1]) - ret.tgt.paddings = tf.cond( - tf.equal(tf.math.floormod(py_utils.GetGlobalStep(), 2), 0), - lambda: tgt_paddings[0], lambda: tgt_paddings[1]) - ret.tgt.weights = tf.cond( - tf.equal(tf.math.floormod(py_utils.GetGlobalStep(), 2), 0), - lambda: tgt_weights[0], lambda: tgt_weights[1]) - else: - ret.src.ids = src_ids - ret.src.paddings = src_paddings - ret.tgt.ids = tgt_ids - ret.tgt.labels = tgt_labels - ret.tgt.paddings = tgt_paddings - ret.tgt.weights = tgt_weights - - return ret - - -class TransformerModelTest(test_utils.TestCase): - - def _InputParams(self): - p = input_generator.NmtInput.Params() - input_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/wmt14_ende_wpm_32k_test.tfrecord') - vocab_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/wmt14_ende_wpm_32k_test.vocab') - p.file_pattern = 'tfrecord:' + input_file - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.bucket_upper_bound = [40] - p.bucket_batch_limit = [8] - p.source_max_length = 200 - p.target_max_length = 200 - - p.tokenizer.token_vocab_filepath = vocab_file - p.tokenizer.vocab_size = 32000 - return p - - def _EncoderParams(self): - p = encoder.TransformerEncoder.Params() - p.name = 'encoder' - p.random_seed = 1234 - p.model_dim = 4 - p.token_emb.embedding_dim = 4 - p.token_emb.max_num_shards = 1 - p.token_emb.params_init = py_utils.WeightInit.GaussianSqrtDim( - seed=p.random_seed) - p.position_emb.embedding_dim = 4 - p.transformer_stack.transformer_tpl.tr_atten_tpl.num_attention_heads = 2 - p.transformer_stack.transformer_tpl.tr_fflayer_tpl.hidden_dim = 5 - return p - - def _DecoderParams(self): - p = decoder.TransformerDecoder.Params() - p.name = 'decoder' - p.random_seed = 1234 - p.source_dim = 4 - p.model_dim = 4 - p.token_emb.embedding_dim = 4 - p.token_emb.max_num_shards = 1 - p.token_emb.params_init = py_utils.WeightInit.GaussianSqrtDim( - seed=p.random_seed) - p.position_emb.embedding_dim = 4 - p.trans_tpl.source_dim = 4 - p.trans_tpl.tr_atten_tpl.source_dim = 4 - p.trans_tpl.tr_atten_tpl.num_attention_heads = 2 - p.trans_tpl.tr_fflayer_tpl.input_dim = 4 - p.trans_tpl.tr_fflayer_tpl.hidden_dim = 8 - p.softmax.num_shards = 1 - p.target_seq_len = 5 - return p - - def _testParams(self): - p = model.TransformerModel.Params() - p.name = 'test_mdl' - p.input = self._InputParams() - p.encoder = self._EncoderParams() - p.decoder = self._DecoderParams() - p.train.learning_rate = 2e-4 - return p - - def testConstruction(self): - with self.session(): - p = self._testParams() - mdl = p.Instantiate() - print('vars = ', mdl.vars) - flatten_vars = mdl.vars.Flatten() - print('vars flattened = ', flatten_vars) - self.assertEqual(len(flatten_vars), 238) - - # Should match tf.trainable_variables(). - self.assertEqual(len(tf.trainable_variables()), len(flatten_vars)) - - def testFProp(self, dtype=tf.float32, fprop_dtype=tf.float32): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - p.dtype = dtype - if fprop_dtype: - p.fprop_dtype = fprop_dtype - p.input.dtype = fprop_dtype - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(5): - vals += [self.evaluate((loss, logp))] - - print('actual vals = %s' % np.array_repr(np.array(vals))) - self.assertAllClose(vals, [[233.57518, 10.381119], [236.10052, 10.378047], - [217.99896, 10.380901], [217.94647, 10.378406], - [159.5997, 10.380468]]) - - def testFPropEvalMode(self): - with self.session(), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(5): - vals += [self.evaluate((loss, logp))] - print('actual vals = ', vals) - self.assertAllClose(vals, [ - [233.57518, 10.381119], - [236.10052, 10.378047], - [217.99896, 10.380901], - [217.94647, 10.378406], - [159.5997, 10.380468], - ]) - - def testBProp(self): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - mdl.BProp() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(5): - vals += [self.evaluate((loss, logp, mdl.train_op))[:2]] - print('BProp actual vals = ', vals) - expected_vals = [ - [233.57518, 10.381119], - [236.05138, 10.375884], - [217.9087, 10.376605], - [217.77725, 10.370345], - [159.43497, 10.369753], - ] - self.assertAllClose(vals, expected_vals) - - def testBPropWithAccumComparison(self): - - def _SetDefaults(p): - p.random_seed = 12345 - p.decoder.input_dropout_prob = 0.0 - mp = p.encoder.transformer_stack.transparent_merger_tpl - mp.weighted_merger_dropout_prob = 0.0 - disable_vn = py_utils.VariationalNoiseParams(1.0, False, False) - for lp in base_layer.RecursiveFindLayerParams(p): - # TODO(lepikhin): lp.dtype = dtype - lp.params_init = py_utils.WeightInit.Gaussian(0.1, 12345) - lp.vn = disable_vn - - tp = p.train - assert tp.l2_regularizer_weight is None - tp.clip_gradient_norm_to_value = False - tp.grad_norm_to_clip_to_zero = False - tp.optimizer = optimizer.SGD.Params() - tp.learning_rate = 1e-2 - tp.lr_schedule = schedule.ContinuousSchedule.Params() - for l in p.ToText().split('\n'): - print(l) - return p - - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - p.input = TestInputGenerator.Params() - p.input.split = True - p = _SetDefaults(p) - p.train.optimizer = optimizer.Accumulator.Params().Set( - accum_steps=2, optimizer_tpl=p.train.optimizer) - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - mdl.BProp() - - self.evaluate(tf.global_variables_initializer()) - - for _ in range(2): - self.evaluate(mdl.train_op) - - expected = self.evaluate(mdl.dec.softmax.vars['weight_0']) - - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - p.input = TestInputGenerator.Params() - p.input.split = False - p = _SetDefaults(p) - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - mdl.BProp() - - self.evaluate(tf.global_variables_initializer()) - - self.evaluate(mdl.train_op) - - actual = self.evaluate(mdl.dec.softmax.vars['weight_0']) - - self.assertAllClose(expected, actual, rtol=1e-2, atol=1e-2) - - def testBatchSplit(self): - - def Run(num_splits): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(93820981) - p = self._testParams() - p.input.bucket_batch_limit = [ - b * 2 / num_splits for b in p.input.bucket_batch_limit - ] - with cluster_factory.ForTestingWorker(gpus=num_splits): - mdl = p.Instantiate() - metrics = mdl.FPropDefaultTheta()[0] - self.evaluate(tf.global_variables_initializer()) - return self.evaluate(metrics['loss']) - - res1, res2 = Run(1), Run(2) - self.assertAllClose(res1[0], res2[0]) - self.assertAllEqual(res1[1], res2[1]) - - def testBatchSizeInInputGenerator(self): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - with cluster_factory.ForTestingWorker( - mode='sync', job='trainer_client', gpus=5): - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.loss - self.evaluate(tf.global_variables_initializer()) - _ = self.evaluate(loss) - self.assertEqual(mdl.input_generator.infeed_bucket_batch_limit, [40]) - - def testDecode(self): - with self.session(use_gpu=False): - tf.random.set_seed(93820985) - p = self._testParams() - mdl = p.Instantiate() - input_batch = mdl.input_generator.GetPreprocessedInputBatch() - dec_out_dict = mdl.Decode(input_batch) - self.evaluate(tf.global_variables_initializer()) - dec_out = self.evaluate(dec_out_dict) - metrics_dict = mdl.CreateDecoderMetrics() - key_value_pairs = mdl.PostProcessDecodeOut(dec_out, metrics_dict) - self.assertNear(0.0, metrics_dict['corpus_bleu'].value, 1.0e-5) - self.assertLen(key_value_pairs, 8) - for k, v in key_value_pairs: - self.assertIn(k, v) - - -class RNMTModelTest(test_utils.TestCase): - - def _InputParams(self): - p = input_generator.NmtInput.Params() - input_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/wmt14_ende_wpm_32k_test.tfrecord') - vocab_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/wmt14_ende_wpm_32k_test.vocab') - p.file_pattern = 'tfrecord:' + input_file - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.bucket_upper_bound = [40] - p.bucket_batch_limit = [8] - p.source_max_length = 200 - p.target_max_length = 200 - - p.tokenizer.token_vocab_filepath = vocab_file - p.tokenizer.vocab_size = 32000 - return p - - def _EncoderParams(self): - p = encoder.MTEncoderBiRNN.Params() - p.name = 'encoder' - p.emb.vocab_size = 32000 - p.emb.embedding_dim = 4 - p.emb.max_num_shards = 1 - p.lstm_cell_size = 4 - p.num_lstm_layers = 3 - p.encoder_out_dim = 4 - return p - - def _DecoderParams(self): - p = decoder.MTDecoderV1.Params() - p.name = 'decoder' - p.source_dim = 4 - p.emb.vocab_size = 32000 - p.emb.embedding_dim = 4 - p.emb.max_num_shards = 1 - p.rnn_cell_dim = 4 - p.rnn_layers = 3 - p.attention.hidden_dim = 2 - p.softmax.num_classes = 32000 - p.softmax.num_shards = 1 - return p - - def _testParams(self): - p = model.RNMTModel.Params() - p.name = 'test_mdl' - p.input = self._InputParams() - p.encoder = self._EncoderParams() - p.decoder = self._DecoderParams() - p.train.learning_rate = 1.0 - return p - - def testConstruction(self): - with self.session(): - p = self._testParams() - mdl = p.Instantiate() - flatten_vars = mdl.vars.Flatten() - # encoder/embedding: 1 - # encoder/lstms: 2 * (3 (forward) + 3 (backward)) - # encoder/proj: 2 - # decoder/embedding: 1 - # decoder/atten: 3 - # decoder/lstms: 2 * 3 - # decoder/softmax: 2 - self.assertEqual(len(flatten_vars), 1 + 12 + 2 + 1 + 3 + 6 + 2) - - # Should match tf.trainable_variables(). - self.assertEqual(len(tf.trainable_variables()), len(flatten_vars)) - - def testFProp(self): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(5): - vals += [self.evaluate((loss, logp))] - self.assertAllClose(vals, [ - [233.403564, 10.373495], - [235.996948, 10.373494], - [217.843338, 10.373493], - [217.843338, 10.373491], - [159.492432, 10.373494], - ]) - - def testFPropEvalMode(self): - with self.session(), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(5): - vals += [self.evaluate((loss, logp))] - self.assertAllClose(vals, [ - [233.403564, 10.373495], - [235.996948, 10.373494], - [217.843338, 10.373493], - [217.843338, 10.373491], - [159.492432, 10.373494], - ]) - - def testBProp(self): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - mdl.BProp() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(5): - vals += [self.evaluate((loss, logp, mdl.train_op))[:2]] - expected_vals = [ - [233.403564, 10.373495], - [219.442184, 9.645809], - [181.665314, 8.650729], - [185.266647, 8.822222], - [157.343857, 10.233747], - ] - self.assertAllClose(vals, expected_vals, atol=1e-3) - - def testDecode(self): - with self.session(use_gpu=False), self.SetEval(True): - tf.random.set_seed(93820985) - p = self._testParams() - mdl = p.Instantiate() - input_batch = mdl.input_generator.GetPreprocessedInputBatch() - dec_out_dict = mdl.Decode(input_batch) - self.evaluate(tf.global_variables_initializer()) - dec_out = self.evaluate(dec_out_dict) - metrics_dict = mdl.CreateDecoderMetrics() - key_value_pairs = mdl.PostProcessDecodeOut(dec_out, metrics_dict) - self.assertNear(0.0, metrics_dict['corpus_bleu'].value, 1.0e-5) - self.assertLen(key_value_pairs, 8) - for k, v in key_value_pairs: - self.assertIn(k, v) - - def testBatchSplit(self): - - def Run(num_splits): - with self.session(use_gpu=False, graph=tf.Graph()): - tf.random.set_seed(93820981) - p = self._testParams() - p.input.bucket_batch_limit = [ - b * 2 / num_splits for b in p.input.bucket_batch_limit - ] - with cluster_factory.ForTestingWorker(gpus=num_splits): - mdl = p.Instantiate() - metrics = mdl.FPropDefaultTheta()[0] - self.evaluate(tf.global_variables_initializer()) - return self.evaluate(metrics['loss']) - - res1, res2 = Run(1), Run(2) - self.assertAllClose(res1[0], res2[0]) - self.assertAllEqual(res1[1], res2[1]) - - def testBatchSizeInInputGenerator(self): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - cluster_params = cluster_factory.Cluster.Params() - cluster_params.mode = 'sync' - cluster_params.job = 'trainer_client' - cluster_params.worker.name = '/job:localhost' - cluster_params.worker.gpus_per_replica = 5 - cluster_params.input.name = '/job:localhost' - cluster_params.input.replicas = 1 - cluster_params.input.gpus_per_replica = 0 - with cluster_params.Instantiate(): - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.loss - self.evaluate(tf.global_variables_initializer()) - _ = self.evaluate(loss) - self.assertEqual(mdl.input_generator.infeed_bucket_batch_limit, [40]) - - -class InsertionModelTest(test_utils.TestCase): - - def _InputParams(self): - p = input_generator.NmtInput.Params() - input_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/wmt14_ende_wpm_32k_test.tfrecord') - vocab_file = test_helper.test_src_dir_path( - 'tasks/mt/testdata/wmt14_ende_wpm_32k_test.vocab') - p.file_pattern = 'tfrecord:' + input_file - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.bucket_upper_bound = [40] - p.bucket_batch_limit = [8] - p.source_max_length = 200 - p.target_max_length = 200 - - p.tokenizer.token_vocab_filepath = vocab_file - p.tokenizer.vocab_size = 32000 - return p - - def _DecoderParams(self): - p = decoder.InsertionDecoder.Params() - p.name = 'decoder' - return p - - def _testParams(self): - p = model.InsertionModel.Params() - p.name = 'insertion' - p.input = self._InputParams() - p.decoder = self._DecoderParams() - p.random_seed = 12345 - return p - - def testSampleCanvasAndTargets(self): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - - x = np.asarray([[10, 11, 12, 13, 14, 15, 2], [10, 11, 12, 13, 14, 15, 2], - [2, 0, 0, 0, 0, 0, 0], [10, 11, 12, 13, 14, 2, 0]], - np.int32) - x_paddings = np.asarray([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 1]], - np.float32) - - p = self._testParams() - mdl = p.Instantiate() - - descriptor = mdl._SampleCanvasAndTargets( - tf.convert_to_tensor(x), tf.convert_to_tensor(x_paddings)) - - canvas, canvas_paddings, target_indices, target_weights = self.evaluate([ - descriptor.canvas, descriptor.canvas_paddings, - descriptor.target_indices, descriptor.target_weights - ]) - - canvas_gold = np.asarray([[13, 15, 2, 0, 0], [10, 11, 14, 2, 0], - [2, 0, 0, 0, 0], [10, 11, 13, 14, 2]], np.int32) - canvas_paddings_gold = np.asarray( - [[0., 0., 0., 1., 1.], [0., 0., 0., 0., 1.], [0., 1., 1., 1., 1.], - [0., 0., 0., 0., 0.]], np.float32) - target_indices_gold = np.asarray( - [[0, 0, 10], [0, 0, 11], [0, 0, 12], [0, 0, 2], [0, 1, 14], [0, 1, 2], - [0, 2, 2], [1, 0, 2], [1, 1, 2], [1, 2, 12], [1, 2, 13], [1, 2, 2], - [1, 3, 15], [1, 3, 2], [2, 0, 2], [3, 0, 2], [3, 1, 2], [3, 2, 12], - [3, 2, 2], [3, 3, 2], [3, 4, 2]], np.int32) - target_weights_gold = np.asarray([1, 1, 1, 0, 1, 0, 1] + - [1, 1, 1, 1, 0, 1, 0] + [1] + - [1, 1, 1, 0, 1, 1], np.float32) - target_weights_gold = np.reshape(target_weights_gold, - [target_weights_gold.shape[0], 1]) - - self.assertAllEqual(canvas, canvas_gold) - self.assertAllEqual(canvas_paddings, canvas_paddings_gold) - self.assertAllEqual(target_indices, target_indices_gold) - self.assertAllEqual(target_weights, target_weights_gold) - - def testCreateCanvasAndTargets(self): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - batch = py_utils.NestedMap( - src=py_utils.NestedMap( - ids=tf.convert_to_tensor( - np.asarray([ - [10, 11, 12, 14, 2, 0], - [20, 21, 22, 24, 25, 2], - ], np.int32)), - paddings=tf.convert_to_tensor( - np.asarray([[0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0]], - np.float32))), - tgt=py_utils.NestedMap( - ids=tf.convert_to_tensor( - np.asarray([[100, 101, 102, 104, 2, 0], - [200, 201, 202, 204, 205, 2]], np.int32)), - paddings=tf.convert_to_tensor( - np.asarray([[0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0]], - np.float32)))) - - p = self._testParams() - mdl = p.Instantiate() - - descriptor = mdl._CreateCanvasAndTargets(batch) - - canvas, canvas_paddings, target_indices, target_weights = self.evaluate([ - descriptor.canvas, descriptor.canvas_paddings, - descriptor.target_indices, descriptor.target_weights - ]) - - canvas_gold = np.asarray([ - [32014, 32002, 104, 2, 0, 0, 0, 0], - [32020, 32021, 32022, 32002, 200, 201, 202, 2], - ], np.int32) - canvas_paddings_gold = np.asarray( - [[0., 0., 0., 0., 1., 1., 1., 1.], [0., 0., 0., 0., 0., 0., 0., 0.]], - np.float32) - target_indices_gold = np.asarray( - [[0, 0, 10], [0, 0, 11], [0, 0, 12], [0, 0, 2], [0, 1, 2], [1, 0, 2], - [1, 1, 2], [1, 2, 2], [1, 3, 24], [1, 3, 25], [1, 3, 2], [0, 2, 100], - [0, 2, 101], [0, 2, 102], [0, 2, 2], [0, 3, 2], [1, 4, 2], [1, 5, 2], - [1, 6, 2], [1, 7, 204], [1, 7, 205], [1, 7, 2]], np.int32) - target_weights_gold = np.asarray([1, 1, 1, 0, 1] + [1, 1, 1, 1, 1, 0] + - [1, 1, 1, 0, 1] + [1, 1, 1, 1, 1, 0], - np.float32) - target_weights_gold = np.reshape(target_weights_gold, - [target_weights_gold.shape[0], 1]) - - self.assertAllEqual(canvas, canvas_gold) - self.assertAllEqual(canvas_paddings, canvas_paddings_gold) - self.assertAllEqual(target_indices, target_indices_gold) - self.assertAllEqual(target_weights, target_weights_gold) - - def testConstruction(self): - with self.session(): - p = self._testParams() - mdl = p.Instantiate() - flatten_vars = mdl.vars.Flatten() - self.assertEqual(len(flatten_vars), 122) - self.assertEqual(len(tf.trainable_variables()), len(flatten_vars)) - - def testFPropGraph(self): - """Test the construction of the fprop graph, then fprop the graph.""" - with self.session(): - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - - self.evaluate(tf.global_variables_initializer()) - self.evaluate(mdl.loss) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/mt/params/BUILD b/lingvo/tasks/mt/params/BUILD deleted file mode 100644 index cc88e3bd..00000000 --- a/lingvo/tasks/mt/params/BUILD +++ /dev/null @@ -1,30 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -filegroup( - name = "param_files", - srcs = glob( - include = [ - "*.py", - ], - ), -) - -py_library( - name = "params", - srcs = [ - ":param_files", - ], - srcs_version = "PY3", - tags = ["keep_dep"], - deps = [ - "//lingvo:model_registry", - "//lingvo/core:base_model_params", - "//lingvo/tasks/mt:base_config", - "//lingvo/tasks/mt:input_generator", - "//lingvo/tasks/mt:model", - ], -) diff --git a/lingvo/tasks/mt/params/params.py b/lingvo/tasks/mt/params/params.py deleted file mode 100644 index 1d0b522f..00000000 --- a/lingvo/tasks/mt/params/params.py +++ /dev/null @@ -1,22 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Machine translation model hyper-parameters.""" - -# Import ModelParams to ensure that they are added to the global registry. -# pylint: disable=unused-import -import lingvo.tasks.mt.params.wmt14_en_de -import lingvo.tasks.mt.params.wmtm16_en_de -# pylint: enable=unused-import diff --git a/lingvo/tasks/mt/params/wmt14_en_de.py b/lingvo/tasks/mt/params/wmt14_en_de.py deleted file mode 100644 index 10dfa5e4..00000000 --- a/lingvo/tasks/mt/params/wmt14_en_de.py +++ /dev/null @@ -1,202 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Train NMT Models on WMT'14 English-German machine translation task.""" - -import os - -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.tasks.mt import base_config -from lingvo.tasks.mt import input_generator -from lingvo.tasks.mt import model - - -@model_registry.RegisterSingleTaskModel -class WmtEnDeTransformerBase(base_model_params.SingleTaskModelParams): - """Params for WMT'14 En->De.""" - - DATADIR = '/tmp/wmt14/wpm/' - VOCAB_SIZE = 32000 - - def _CommonInputParams(self, is_eval): - """Input generator params for WMT'14 En->De.""" - p = input_generator.NmtInput.Params() - - p.tokenizer.vocab_size = self.VOCAB_SIZE - - if is_eval: - p.file_random_seed = 27182818 - p.file_parallelism = 1 - p.file_buffer_size = 1 - p.bucket_upper_bound = [10, 14, 19, 26, 36, 50, 70, 98, 137, 200] - p.bucket_batch_limit = [16] * 8 + [4] * 2 - else: - p.file_random_seed = 0 - p.file_parallelism = 16 - p.file_buffer_size = 10000000 - p.bucket_upper_bound = ([8, 10, 12, 14, 16, 20, 24, 28] + - [32, 40, 48, 56, 64, 80, 96]) - p.bucket_batch_limit = ([512, 409, 341, 292, 256, 204, 170, 146] + - [128, 102, 85, 73, 64, 51, 42]) - - return p - - def Train(self): - p = self._CommonInputParams(is_eval=False) - - p.file_pattern = 'tfrecord:' + os.path.join(self.DATADIR, - 'train.tfrecords-*') - p.tokenizer.token_vocab_filepath = os.path.join(self.DATADIR, - 'wpm-ende.voc') - p.num_samples = 4492447 - return p - - def Dev(self): - p = self._CommonInputParams(is_eval=True) - p.file_pattern = 'tfrecord:' + os.path.join(self.DATADIR, 'dev.tfrecords') - p.tokenizer.token_vocab_filepath = os.path.join(self.DATADIR, - 'wpm-ende.voc') - p.num_samples = 3000 - return p - - def Test(self): - p = self._CommonInputParams(is_eval=True) - p.file_pattern = 'tfrecord:' + os.path.join(self.DATADIR, 'test.tfrecords') - p.tokenizer.token_vocab_filepath = os.path.join(self.DATADIR, - 'wpm-ende.voc') - p.num_samples = 2737 - return p - - def Task(self): - p = base_config.SetupTransformerParams( - model.TransformerModel.Params(), - name='wmt14_en_de_transformer_base', - vocab_size=self.VOCAB_SIZE, - model_dim=512, - hidden_dim=2048, - num_heads=8, - num_layers=6, - residual_dropout_prob=0.1, - input_dropout_prob=0.1, - learning_rate=3.0, - warmup_steps=40000) - p.eval.samples_per_summary = 7500 - return p - - -@model_registry.RegisterSingleTaskModel -class WmtEnDeTransformerSmall(WmtEnDeTransformerBase): - """Small Transformer Params for WMT'14 En->De.""" - - def Task(self): - p = base_config.SetupTransformerParams( - model.TransformerModel.Params(), - name='wmt14_en_de_transformer_small', - vocab_size=self.VOCAB_SIZE, - model_dim=64, - hidden_dim=128, - num_heads=2, - num_layers=2, - residual_dropout_prob=0.1, - input_dropout_prob=0.1, - learning_rate=3.0, - warmup_steps=40000) - p.eval.samples_per_summary = 7500 - return p - - -@model_registry.RegisterSingleTaskModel -class WmtEnDeTransformerSmallCloudTpu(WmtEnDeTransformerSmall): - """Small Transformer Params for WMT'14 En->De on TPU.""" - - def _CommonInputParams(self, is_eval): - p = super()._CommonInputParams(is_eval) - - p.pad_to_max_seq_length = True - p.source_max_length = p.bucket_upper_bound[-1] - p.bucket_batch_limit = [64] * len(p.bucket_upper_bound) - return p - - def Task(self): - p = super().Task() - - p.decoder.token_emb.max_num_shards = 1 - p.encoder.token_emb.max_num_shards = 1 - return p - - -@model_registry.RegisterSingleTaskModel -class WmtEnDeRNMT(WmtEnDeTransformerBase): - """Params for WMT'14 En->De in sync training.""" - - def _CommonInputParams(self, is_eval): - p = super()._CommonInputParams(is_eval) - - if is_eval: - p.bucket_upper_bound = [10, 14, 19, 26, 36, 50, 70, 98, 200] - p.bucket_batch_limit = [128] * 8 + [32] - else: - p.bucket_upper_bound = [10, 14, 19, 26, 36, 50, 70, 98] - p.bucket_batch_limit = [128] * 7 + [64] - - return p - - def Task(self): - p = base_config.SetupRNMTParams( - model.RNMTModel.Params(), - name='wmt14_en_de_rnmtplus_base', - vocab_size=self.VOCAB_SIZE, - embedding_dim=1024, - hidden_dim=1024, - num_heads=4, - num_encoder_layers=6, - num_decoder_layers=8, - learning_rate=1e-4, - l2_regularizer_weight=1e-5, - lr_warmup_steps=500, - lr_decay_start=400000, - lr_decay_end=1200000, - lr_min=0.5, - ls_uncertainty=0.1, - atten_dropout_prob=0.3, - residual_dropout_prob=0.3, - adam_beta2=0.98, - adam_epsilon=1e-6, - ) - p.eval.samples_per_summary = 7500 - return p - - -@model_registry.RegisterSingleTaskModel -class WmtEnDeRNMTCloudTpu(WmtEnDeRNMT): - """Params for WMT'14 En->De in sync training on TPU.""" - - def _CommonInputParams(self, is_eval): - p = super()._CommonInputParams(is_eval) - - p.pad_to_max_seq_length = True - p.source_max_length = p.bucket_upper_bound[-1] - p.bucket_batch_limit = [16] * len(p.bucket_upper_bound) - - return p - - def Task(self): - p = super().Task() - - p.encoder.emb.max_num_shards = 1 - p.decoder.emb.max_num_shards = 1 - - return p diff --git a/lingvo/tasks/mt/params/wmtm16_en_de.py b/lingvo/tasks/mt/params/wmtm16_en_de.py deleted file mode 100644 index 28fe3aa1..00000000 --- a/lingvo/tasks/mt/params/wmtm16_en_de.py +++ /dev/null @@ -1,114 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Train NMT Models on WMT'16 MMT English-German machine translation task.""" - -import os - -from lingvo import model_registry -from lingvo.core import base_model_params -from lingvo.tasks.mt import base_config -from lingvo.tasks.mt import input_generator -from lingvo.tasks.mt import model - - -@model_registry.RegisterSingleTaskModel -class WmtCaptionEnDeTransformer(base_model_params.SingleTaskModelParams): - """Params for WMT'16 En->De Captions (ignoring the images).""" - - # Generated using scripts in lingvo/mt/tools. - DATADIR = '/tmp/wmtm16/wpm/' - VOCAB_SIZE = 2000 - VOCAB_FILE = 'wpm-ende-2k.voc' - - def _CommonInputParams(self, is_eval): - """Input generator params for WMT'16 En->De.""" - p = input_generator.NmtInput.Params() - if is_eval: - p.file_random_seed = 27182818 - p.file_parallelism = 1 - p.file_buffer_size = 1 - p.bucket_upper_bound = [10, 14, 19, 26, 36, 50, 70, 98, 137, 200] - p.bucket_batch_limit = [16] * 8 + [4] * 2 - else: - p.file_random_seed = 0 - p.file_parallelism = 1 - p.file_buffer_size = 29000 - p.bucket_upper_bound = [14, 17, 20, 24, 29, 35, 45, 75] - p.bucket_batch_limit = [292, 240, 204, 170, 141, 117, 91, 54] - - p.tokenizer.vocab_size = self.VOCAB_SIZE - p.tokenizer.token_vocab_filepath = os.path.join(self.DATADIR, - self.VOCAB_FILE) - - return p - - def Train(self): - p = self._CommonInputParams(is_eval=False) - p.file_pattern = 'tfrecord:' + os.path.join(self.DATADIR, 'train.tfrecords') - p.num_samples = 29000 - return p - - def Dev(self): - p = input_generator.NmtInput.Params() - p.file_pattern = 'tfrecord:' + os.path.join(self.DATADIR, 'val.tfrecords') - p.num_samples = 1014 - return p - - def Test(self): - p = input_generator.NmtInput.Params() - p.file_pattern = 'tfrecord:' + os.path.join(self.DATADIR, 'test.tfrecords') - p.num_samples = 1000 - return p - - def Task(self): - p = base_config.SetupTransformerParams( - model.TransformerModel.Params(), - name='wmt14_en_de_transformer_base', - vocab_size=self.VOCAB_SIZE, - model_dim=256, - hidden_dim=512, - num_heads=2, - num_layers=2, - residual_dropout_prob=0.2, - input_dropout_prob=0.2, - learning_rate=1.0, - warmup_steps=1000) - p.eval.samples_per_summary = 7500 - p.train.save_interval_seconds = 60 - p.train.max_steps = 12000 - return p - - -@model_registry.RegisterSingleTaskModel -class WmtCaptionEnDeTransformerCloudTpu(WmtCaptionEnDeTransformer): - """Params for WMT'16 En->De Captions (ignoring the images) on TPU.""" - - def _CommonInputParams(self, is_eval): - p = super()._CommonInputParams(is_eval) - - p.pad_to_max_seq_length = True - p.source_max_length = p.bucket_upper_bound[-1] - p.bucket_batch_limit = [16] * len(p.bucket_batch_limit) - - return p - - def Task(self): - p = super().Task() - - p.encoder.token_emb.max_num_shards = 1 - p.decoder.token_emb.max_num_shards = 1 - - return p diff --git a/lingvo/tasks/mt/testdata/BUILD b/lingvo/tasks/mt/testdata/BUILD deleted file mode 100644 index 0fc88ccb..00000000 --- a/lingvo/tasks/mt/testdata/BUILD +++ /dev/null @@ -1,29 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) # Apache 2.0 - -# First 100 sentences of the news-test 2013 from WMT'14 development set. -# http://www.statmt.org/wmt14/dev.tgz -filegroup( - name = "wmt14_ende_tfexample", - data = [ - "wmt14_ende_wpm_32k_test.tfrecord", - "wmt14_ende_wpm_32k_test.vocab", - ], -) - -filegroup( - name = "mlperf_tfexample", - data = [ - "translate_ende_mlperf.packed.tfrecord", - "translate_ende_wmt32k-train-00511-of-00512", - ], -) - -filegroup( - name = "input_test_data", - data = [ - "en_de.text", - "en_fr.tfrecord", - ], -) diff --git a/lingvo/tasks/mt/testdata/en_de.text b/lingvo/tasks/mt/testdata/en_de.text deleted file mode 100644 index 1eecbd63..00000000 --- a/lingvo/tasks/mt/testdata/en_de.text +++ /dev/null @@ -1,2 +0,0 @@ -Too much has changed. Dafür hat sich zu viel verändert. -Hello! Hallo! diff --git a/lingvo/tasks/mt/testdata/en_fr.tfrecord b/lingvo/tasks/mt/testdata/en_fr.tfrecord deleted file mode 100644 index 3422000a48e67beca2f96ab03015fb553465b1ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140 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

    {o48zwAahWVXJ-T&QR1?QLXta2Q#*)M7q=; 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

    X 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

    G_>4A!`x)}JR3R0#y(xzCNhW8VY}C>pTh~^ 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%0AK_PDC>KEw;3X?s7Y-+APolr{1eX`vx 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 z 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�b*96Pvk#|*$gPi*z)mv?O2?0-_oGo=1d~1PjFKE;#8XA|S$ebK=5P?b8W^*H zjC!Y*KSK4~IQ)fyMdm<1B0Ea09zGD~x*h}Ena>IqW*TQ&k&f@kck7#?lw*w{m(bVS zlGIYT?k%jLZ0XP{gJ297ZS}%H??Vsj5OaB&xDa+(b9?kL4ezMQ`Su}1mPp??2vg}7 zl^_?HgPGFte;wHb+;JAiv9e5G*gQ6B8F+X@8b@}K3QCM}UCxi(-h z?nCoS58)OqI`6z!zeaW*6$mpg2UVmuuu`+?ep~pxn4U??t-monna0oC0*f2X;o8^B zN&T+d_K>z)g~z_WN3)fy&b}HAO#l?ZH({|KrSzqEFDez~(P^}4&myYtIiAHlO!;`3 zd&Q!eq^CZa*VW}YqExcCm)yWqRvGD}nt~i1XTY_CCro zT`Asev77ARviXBN;hxcFpm@MJ&ah!kE}N+z3OM~ydpHd2z6=N5l&xmHDLs937Qi&k zJLhbl<#vb?A2AZA8aE8#(%5Hw)_gMc>m*VGu9oLIe!m6EN+IZLyHoGQ+=d=Udqtuh zYXF2mBnjcWibm=jhU;}`~8-8>QSr z@v+oiSRtuS19_(4YIjFMcr@KV`t1x<3D3X4bXh~!ab!D<%C;W8 znf~eW&kR{J`c679kB@@n^1$WU5cL_8E0TsmD81~5mlD$1*`LbmaGW_a;_5LNgR10W z=_|XGZ~F^psAUjM7jhRY(kS8_aTN;q^>p!bxY7?38Xuu^eNhH~`4z_dMxSZPdNTjK z-F24ClpK$SgS;88$My(al?a~pO1|;AK7{b@sstXZV39$-V#r?j+Y=^TMgRB4E>fdw zBkwMT{Gm_2z8(pc2BktisXzG|{T-+y|D0d(@7 zaz*U2o<8+0>6?uA@PpQW@)e&YPuML+=f4nY?-EGBub;3Yo5C^vBIT8WcU zOaMrYa(>uT1V=gJ4f9M4p5j7nv(Lba4ZtMJ?=!@yQ%npv8I?Q*Cc9fDFWA=#Wi!5J zCnQRf-A{T7S_i=kRl6#=amzR2t**bXM_*MB?Ku1z+-(QGHg#!06Bo;(NiGIx`pdP7 z6Uz?MXzv;1_RuVhoTOYWFG*t@#dz$jD1mu&F>^`cYMJp4EA#ydtwmUpPS?djyi&j8 zd#6M{?EYd7^8cd_ra2ahvc>SF-jeAlP)n^JDjdotTPDJ$uhaIxa*3hf*2a0VTj1kN z9y?L+U@+q&)hiN_0|@*+78Ls(6C{}Z#FozF=bfj&$HM?Iyo0BN-Mn5kvk5w)@Jg@e z>}3wiSo3S$(1oN14C#$>QGpdfZw|d;bMfWjw@CI9SVV~*NK6mUMYUZDkMWID#iA6e zgKp%NG5S1#Mc2vK`i;VuAo|#sT@eTsBJQ-6>zaK)r&uMzi$9j5;BGCoMUM_!2_=J> zP>#&Q-c2>VT>YCjV0rHZN8GJmIyAzK2&l11)pw`%E!YfgV>34GvgU&34r>k3vB5OK z>RYZwS(`QuVQ*-5+kB#KfzT}~Y8KsUxy{<_qPZ=q=3wWvAFr%*cJYZ2`H?jO%ucy$JW*<(WUlF9Uf2 ze+_TqDbxl`D|U)Q0(a4H2H7o5nt`Vf``UiShgL$6ZGk7)e3#zw#x*b*={pmT00WAq z93)rjAVeWH|J#Cdrjw#mAN4_W(63gSuxPM|_Pw^C&xY}NLFytGm>*e+1 zXC#9s6mnq_OptK$Q#9;l?uYm_aO08=ITia`tPS9>q*P=ScFY~j%L@s zm=sTWro*LPlhD%mtUm}zMzY?)E9l2Ihmv7<1n8Mp&;b$@17+ku*5r6}#t4E(`mUDZ zhf$Ee9}+MoqwoCM;pxtiVttpg-)7N47}K?qJHiCPWqm=?BV;QJH>7vLz<4P8?IES_ za(9II!Q`p8_^9_yv3DVny)+W}&Cw5`KjS_-Ftfv`L(~pn7o)ZIT1q7;+Ta*u8)3P# z3oF|NqDnhbL32X-fL!L&<3DrfCgxFzl@LY(h7nx!!GU{+j=@!9*tt6D9+zf;slDEj zZ@`qVV(a@n@QTqm6)lD5*4Qo}^DeG*bUmH!r{l9gya9Gx=KW^Nj z&8(+osRbj_QBmVV?I~(G-83I&+A6i?Q1x}~TuFeoU`8CjR@Viq5kp$Oxq3~OLw0{y6RQGpLwY2;=n9);PMlpeeWzvi2P67p!`WQ!dx)%Zu?*rq% z4Jy`v1BM51e@JTR!(Sa3g1(>4B*Cm-iB;t64X%pYw;{0XYkduFyps?D41dOCJQ=q(3FLvC)1W)!FkP51nMD8{JnYX->z&LKYQTnf-ei~- z4=-k+Xu`ce+bNzHXGlp^f2lWrco`&T!2KW9zcJv!@wEyb z4Z(~W4;RE<;$G?ux=H|(&j5T2_md3yJGGhL)w0A%e?(++LCzG`pP z4s9Hxc2?%MHy>9wQ_2k#A4vtEo@SJ}->etwV3R}soPl&HCw{<+=%8Gvo^`L|MyvH> z;w@LsV)9Y9X0;h!9`r0#E8Wd(KT>0X1o0S8ZZSuP_kN z#r#CHP+VqY7}O!&W>`kQ$IGF#803cNyHRQ{$Bl`4Rbu^VYPaeEr{LH@=;09wL5vz5 z=v&kY4GsIM!XTfOt=xm^$Xtj8uFU$g6>wL+4TV#w5Xue2@E)n+gBc(f^s&Nr($_P5 zzzth#cn}pYwIf}#3}mtMr*H|I;NBP!T`0zt#&FyN7LU(p(wSf_I{FT#D&VTQqsEQ9 z+_mJ}QEaM#d8Koz*f4Hk*laoH@xE$q3JOIBL|b5#UDO&h@Q(WVqBo2q;oKw1Fu`_m znw#qy3`#&pT3A1pai!AriXey~+ZD?=k+3rE)AU)m)6rg8^Jv?+%iI3l+iq3EivQ9) zSL<`M0<~Lg>h7w$MYSIVDU{a3ZR1YYO$FbM+O-^|M(AEs)Jg|+h-Vr}qITN%8oJlq z{J1FQuhNFAdg`?gd+{X-Ov$ck`(6!7%og!bLvXJ9)9Y?&B$8Ttr~}o@>Y}wbg}1(q zYhK}W&4?mKeseP|45O5&4G*Gx%qopnOY~&5+*J;Q#KvG_iuN$=~6U&+@Ro~Iu6 zw! zc`SQtd|(BH$XB{l27`m}Uxj?hsQ=)dpWcH}e}7{=4oG~_TYDA1)lx`j zj*)c>nJ4(+-%!%r#_z4|dfd$V8*o?mc-<{+qJ1;TYdJy7nhJ5XfKXng9jfB({7N14 zuW?hirhhHzmwN3r1Pq(C(y&HiCH&P+ur!^k8);PQi)eGbZTT^3AKLg=taj-+N-6Ux zKBr+Yt^%z+rtgJUs_W)jcoBvD-=a9OctQsNN1&!eKXKT)*WXK;xco+UcjI z=X&+AeKW?kI?iKtsiS9^}x3w0ACHzU>{uUJdxFX-5kt>F${BgukO$ zJOOifb`(aA>zB6!uKtvA0c?}lTye3*}9tUjB0c?PIbFA=A}88$P#Bn_R7w z)Q?38c&^L_nY+LD>yVS2vCMSfIo{Sp70=XW?S)c_7-Xtbp+Q^el5;xMR^ra z9q+Z5b?t@*c;hCh|J#iEO(pGnpbf;Y`nq?>PYn#eXx(?W_y_X0JUq5RahjvuwDw1+)@EfzwrsEk8_J(7I_hcP(0bE56t`@s8c!yv1XA8vFdGoN$c8SO5FuztC}C|`V@c?F{9 z99o2;SII_9`|-;Bt9yFJ%kv;)_9;E$=Sg52Q2onW&J z$e+BD4>SE4lWUPP4r@)6_9>5$(_}92`}uM=A^z-M7(eI<3TD0O0!Yb(atdd%MelD( z(F)izt=RSUmnM^HJ7n9;_|evf?4r8ur2Abz1c5!x^E+bhtdw6a5FB9d^FvHHFl;^s zN>?$~I5Z7AGUV=7s1xCcI+mo8J91SftpPb!Qq+W2@;iHMl96$fPGG{V)uU!sF%`}} zb177MVpg#flGCY7+$DbppuG40nP##F1L zH@oeWIFj2*4W;80=p5qqNMC$ZzOJ{a2kdke<h7sK|um?A6a+kp2g2I+AEVzO860q30wbfjJ^rSNqxp$!AvuQ922g_z+1 zki(E0gy?s4wOmOVs-7yY;bdzp-*vrY7)yn7Wg^q)v5F^l&n?i5+*d+lz<>v6WgHGG z-Z6ZJDK%eOU33=FPp5^thY?SQ+kXgnh5AS@(8~weUCA0t(Mtx?hy&r={7i~_i1h2b zM0??J4z0-#TGltS%P_CzRa8q_oiXRo%*bx9$j>Q~6`y$s9SQ*>gm;*18JvPi#%`l> zG>P6%Ssu4wKiWh;#BGnXa}z^eM=8+}CyZL+f`+w_@)_+xCr`~^~;`&^kzBOn3Fy?(EM3;L-uq#m`Jt}gKSSBX71yu zHxwH{^~}9r#N7kw<|sI|2emf4zwOfnQ{AchGuR(5k;A(z?}c_7Pd~;rY#h+VX*uyy zuC|%bJmQ`+rh+9z8GXu6b6&sQlyb+O*nZwIMA%h{TlX7 zQ_q8b5gg8ha1`5FBL@Fy_)Us)cRF$5B7ISzHQ_p@eG?uDbA;k)c=GF_-ret6pGWrg zk(W_8gns8)3MU!DoiGrGS=?r)(q8Pr(V{41T92nTJXQ6$zKa)@eTC0-ID|vfrQL6{ zw5lJ8y9TG>7qnV``tg49&Ft>7l$)?tanBm{Dvo5QJe@Mdk|KN+JS%}c-5GKh_Op+T zPeXFQUl6}Atwn@o!Ekd?ERgNcH-=hbFwmExBi|9JM43T9){*TTEvtD2XJ+FPExp(x zZM8AONNTI*CZgH?LVL()bDtLUQ&&aHRZ;C4q$<`+CirMI0vt{$ zLn%I(%Bz8F@c}P#c|x>Vm!}v6uz%P9(s_P0>=LDXujIEb29w2x{*b^ude>s<-zndZ z5B@F%Z5H;^{&04@8JWrNV^5%KVHtRRnAdR;+;doOLcIT2Jk}zJH52}lL~3}KHO5Ot z{;*7Z8B_1}JM6Nr>oSm@CS7hC_V-4`CD%dFB7vblz;M13=Oh>vN5x8pHYa1zA#ZR9P2>k85VJET zLi&y;MDoP&afdp?1BGDYZd4~trp?K#L~wWZHe<< zzrPBpiEN1l9;4}p&}S)9Ye8(T5$C9VYD29gu~E(aIx_cWUW-ln&tE@nv#Vp*HalxJ zszX#|KA@Qtdr`_o6kkia?w}`;?!Kfe1^^7BN}-q%qch-Aq+KrC?DBWP0X?>7F2zNe ziY*_};`3mr?=709ua{=Ei%gq$K|Whp6m}`3<9t zMD!EGgN(44$f0wp4gvQh9OHD$$~)&%8KYj)6eN0Nj^J(%KvTbHk%$EB91PWeB;8VBg@BtyIo84 zG2oF0Au$%>Pgk&t8xJv7_jNRKzV+8`<~&>mE)&0B6^HT5P^A?DLPn3bHQ=n*yr}=c z>e@s#Hv(0ti~2BBZ`QlJOOKwt-fT#_dTZ&_8dP5w#NJvjkd2$U&EtLBjjdRbmTLQ% zl3LGZ-qVhfE{>#vYqBMEuB75fkF7_tt?%5y+H5A?%-vhCD{uXS=;>=hTE>^GcO7Y8 zuO`cN0mB-}t6NjBDs7W!gSCoJr@M<_5~UT9UV*X(Fi3j){{1&d(hhWJUl(miqlqip zA?g?;lm&Nd?Y4A{7}LHFBlObzIBMIXjbi)W`Zc}Y@}so=9?f+fUd7(hE)|K8hM}XK z2Nm#`#APel4@N8PYnW9_gcO?3O+Eg4=|*0Nu6;nHX!Pr?tXdEAor9S;4_H4uU+=)2 z)jml2uNY^&m;KHbQXmEjXaeE*vR2*0qEx!8ZYuE(VO}PR3$^w zSC9DBMo!-BlM`T)zZ$lYXdB<%FmvBiAx;SMs27T~ANJqL)h)!YuWguL4~fVx4-oeZBtnJ-Kst}aIa!(ZsHFbE-21uMl(REXH0 z5nEOZ(rO~QV84On(kL0{y3SrB9OKBvE=0fUT|!?cFZmANv+quQsIYs4M=;FA@V%SM z&*xIwOYb#Y z0k1Hh0;XU2TIejCTocn^IYpNUxjuh*WG%b=&QnJ*?L+DBgT124eNYajUl-EX2u7|5 z{jU1gYs@y*ZaJ6Y+@X9H-ayXkW4f6rnqE0aF8b_(v*;Eng?qT~Kj_-W?_|TB`^Ira z4PU4bT;Mg09NQ6~#NQ;IR741H+t&#V?Jpndx zr8YQ_W>X7pqm8%J!?ZKmMrb25X)z}OQ-&@}Z-{y6q z?iQb;bw%^@G=2chzp78t{2A0sYM-Q`*kJF`{AsY%u8)JA8o!TB#;2$WZMIO#Sc;FL z4w5j_JWt|v6O1$C;qDq394s0c7zsZaMY%^M!}el0&Y<8$66GG$%g1@+3K)DGPz1b- zTE~yJN3YPcaJb)pAj&P>U>QBXh}hp&Joq(G^qoK4SK_ewo|0uWu|*%7F?+kD%bs#E z9Wxn&)AwORG}3@LY8ORTSh*WxzEEVyPrjVr#a#XWW*h##Y=)0JYwZzqf?fkkA9`zOf| zEIL9z<+kR5JXIltUfHHN$AnoTON zQnc>;ucSCll%}cBc1WOlrCN*5Eh;UDmF^bd2+`Qu_?K&bvKpwq*bHbcX{F%}+PKDE zJc=G0J#Uv$$|V%)>v`Wb1-*&|8cd(lATHvr*yGzPxz{R-jn=JRvBJ zIVD!XE;7$92{|5ixw(~g6ni;M?fDWSYISaVm`?p}(Hcmz=WwP>ok6oDtVgWB2Oa+K>)`W`B< zN!V9iF^_Ai1-(uGJj!?upgOxmevo#O-moJs|JPGuS*&yVh%ZI)A`)S7f>qQ~oa*R-o?#}xxIL!u0ho6! z%IH7H*1MbHkTV0HNV~cO_(G`+U(@SeMnn;BP0rc%MJ0 z5VF;4NFyJC@&RCc82!cCzE?2zWfpp?1JJvn7xPzNsLu zwYpC#x<)$QBaROuVJS^^ep>J)DeQOfox}m;X6Hicl|NW4vbWjE+$T}KaARm0Pl{)+ zGut@?5AEn7bWr9;pEut@JlPC|IN=uu@uq?GlveWysxyyZb3hZ_7Ll4ie&Zoh{g$O$ z4Ah?S?dgLSMD6U>U{=w(w~!M?tK_J02ftQ8(tOH#NK?R_7UcFiUR_O9m zw>6Bh&-iuL7Gr2+)&^n>Y!SU92a)Yv&Qa_Hp z%o?4FhVvJ}prMmNaU#;IJ=V*=e=uZ798B=tiQ(Mb&Hs66S229Nkl@%}AeeLcUBlF-|UKei3L_QBj+%JYuX?-W8Y{rM68+LXC`$~faxx1$z$ z5*6?$E2J&F`(2f-KfJVi)nRQY-1h>KTm;4F zeKs2>M-GzT`)Six-rrun1<0I3x)v_HMF%Q9E<>h}tNkf^7Q9Wj`;ak(RP5-4yAV5C zIMgmMbM6-<%KxOsXa_+7)H!O?z!7ENWtvm4EHK>bN&`z@@(?ETmlXWA=Hu5UO1Ylm z*HW9t=k^MOT&t5NrPskmptYFJY7oaNj&o0ZV2w4mr9?P_o$POs&z;DD$QzhgB7W>8 zITu=ntEwOUf&5`A#BkS5lmnKZ7x zjIf1JNo|}aUMxN(@};(khutu@NcAFRL@-dmC)Ll$t>kM(hi0N*mTbF%^H>hgB4J9b zY%?mY>*C?LX#4i2M{-56u*vldLYKgYI}LV#b@Jy-Xvh4;u)A*{^g6N|Rdk0sP2^5m zewqEfNw25QW0KYbGf`C9{a7$u8DwfmABlj$-b~F4IBI_43HTeTo^T7(6oDZA*JN-W zMEMH%n%AGfS{<=kvj`18s#g8AsyK#^7&&Tm%X}RFqj}$88h(im=Jo)Tn0uq5J+!-B zecDaVTT|^ft!onWc0z>Prglw8Bbv19Mb$dB`<>sgnQ5#II`s9 z03uBs<>x98!}~8GeWJ8@aGg^CA=_MU6@#tx`S0?;7vN8m!T?-LUI5(7z;F~FTo_vS zpWz0--**k~y0OPPds_Sc!9y_*n(4>-D%0LOZ2>&Gz#+3tMM}q=D@X z42gfbd#o?qC%b=0#K0){KD*By2gH`)%Erz)=S$hp&NkKR>6)lKwuX zepAriQHr8~{*jOnWfhn2W$6$KZI1Ev{vkt;1N~>hW@#t*Oa%Bd;V?>X# zonV5YFaps)yODbCDTEti29HxGmC||&Y1~K{GtBCzk}m&yYZNf{`wqiktwa)|J8Ilh z8wtT15S2Dv1KK@LyF2PoqP+F^&F?-EuVw3l$(RhisLzMX{(qWnw}0L3@2PIT-7qyeH7L?(b4qA;OHW%9_c{(UoJ@^giR$E>Fe_NZk{l@C8 zZNB~gps}s5qSnTrP$T-;lrodz4^UeM`q~cXFLZy5M=$df^6Y0nrqZyRPygQ42jPUA z-DuFeItps`p&*#>Pkwl~9T-%RFAR5)?qQ$k2fM`$SZQ&{Aba*0kL+Xv=9`|h>YWIuL%vQj_SF+Hz~(hoZCMSVj`R>mV!Bd zV6YB#2;yn{o~lw_hCv__=@3jQnCtw3JO_oo8CYI?53YQj)VF}53qRa3 z#*~ikSk>psa0hRoUrk==AUe!{AYW98hnVQzD?EWM8BjXG!m7yT`VY_7!k3#T&yp<{ zEAqNA0=!p;C__;wxdI%0sQYOy{yfOmk5%*#Rm{p78?U&%6|woFI^(zl_?E-%KK!k9 z;;b?)zwt>>76N~?9XFjeem2b}O^AUOkfD)QU~_B;bx)&~UDax8MU^uXf1?>O&J$%3 z;ZKuX=1~nC4u*^c!TOk_B^u?cq2}U>QjA0TEE$Tp15i1ZWH@l8QLspV+VkZYBuRun z<*+=wjy|Q<4mvz`ifp+)t3HVfeP1T}(5E{F3}3GA8cBqN8xbRWhMS7-3|PdF>>fMK z$l@OIVX0q=)|gU|IVDU>~&k3_`|knNEO9TKqv#s*o`*TY(5US<<2C(SE)M_%8j(#-W`hj*9j2SL^> zy~mvm$*^cNC>3Wo4wPZ^k`q5wjztBdc#Z5pCKQ_%cPUhxqRb{fR#5ls2m(4;ap4@G zQzM;l6Sl4qpcNL6W<=)!gPC+HERx2`J@q<_nv5jlzrH!Q{^8Vb%WvAIv%k)Uwq576 z57P>51r$=lY84ANZnfsKj;@kA&u>-MHZ!}M+|Es2oZ>@>{ z-&~Dby<&^1=KD578edoaZ`13xF0}EwZ8f$06pb~tPtsg$^>Hw&WV27+UP`%#;*%*4 zx_RUN?QXkR?x#qW_etLf*VbSx#6HT!1wcvz@p77$2PYf~*TS@&oNZVbd_K3rNAL&6 zFraEgsMD>H71PMy?(3xaBQTH1KLNd@i$%BfPAr9IefzINnvs!twHfBh`0?I=*@q|uBg{i z$prJqhUHt4uk8Qp*%FMx;?7^B9kJ61+iic0_|rbo&EI_bO(SK$qZ>hvV4P|di?-y(5r0U^r8qQ-ZZ;NXW`-VF@bS;=b&^f*XSir;T{KJ) zS8ale_H;Ey2E$Xk;8~P)ze2D;Nt3qg*X_{(3JQ<_OJo9ock|umsd3-{VBSW1omhQ@|F3 zhV@`K;H0#;m>gw2M);iNJ$Sty|EL2oInUdsO-t+KgVB*ML3jM&*qI~pszuy{lb@<)s%b8ur zAVabVEn@1-w=)j`)qbII=xb2)R^Z)7;6y55WZW= zD06-*_c_0QF<~SxssR&RY)OR!3o)_uQV6bv`?pbzrWZb%Oc=3mT*>S@@XA7Ci()!& zpi?JY%_?+;e>&%|9R<-w{R!r{st;qf1cEz~Tb^Wz^?5@gT_>F&xyOICYEJ2AtEVZ0 zJF@piJST@X;~EEe^V1i(MSsFv{q*!oc(@N9$}l?F)NYjur!Ua{{n4B;jqg99!v0Tw zIcDYBNU>)uPj|#<-GRKp^ymeW0`okl>bXRET`}m|V2Ll!uMwdJ7~d3M5UJ2LxL`5< z7{{x-;n+f zTmv+XR$3C9rf@VG>89&_@U$^;_jl_hv2Q%n8xK&qTeTd;r#EY}cTvh66rVs@z2Ze@ zkxO-8mW#Y$JJ|V%eJ{eCC_TDln#GL|x0j)6ed`!eBARx3!%Nh$dc*2CSZ%=7*_QWY zHvNM3``6u;?E!QQkRdm}V*333Y)ZbQ0OOsPA$dqMTrWLs6>d7 zi0OtIPYpfNyI`m}mmUOFKl(c047MFELx*i$0~{_)I|!GKz=lI;zm)`V=d(>V8>A5y zJy4}0%v?Wb{WNhRI=mDYu=bY=7*(P?6V^_b9{~DMry9$&USsO(DBBVGaNhryx%U8z z>gfK*|7UL5-Mfvplm%&0#ICV-?A@rbL`|Z;rkY+~V@#+5qJRj9hzN>^NKtGkiUjs4PSdUma#DnS`WOHa{HAS=>XD5El*z^B}kRNHpPrL}T zAN}fTx_PWQz2OtVjn3P&1iQw@TH~!?BfVg+hz?z@n+y9VX6jxJg~B9o(W$$mtKHpH zE-y`Yv3E(gUjBcV(zyIPm}-YhXvl8P61XUJG35P0=?k29s{?%VHPP`!+@X3+3N=YK zJvec(n!;?WI^6P?`=9<6O$NC6pf)`5XQ~D?8c(7`Qvz4=(uMeVV#J=>m}v0!ExD$1Ng zL8fraq?hc$5z}~#wyFhgra>sGm+YC~4k^DLK7s(o55PENV#FWQ>p0KrS&ZU3#?qS+ zumc$HNMI_)QcxjT2V%$BZ%s1UZN)I|<}x%;-)Z6^>Tz`sLb)mTPWAajW<|If6NO%G zmxC`MKLE^*pnUM_b-z><*Iy;IgL{qPisZ;q8^qXQlrn_EDB1~d+~l;@L}{pQ-?CNY z(K@u|q+r|hO5!Q6z7I157Yz@OJK1}K&(1@1k0HgB$WKy+N1JqJov4g|Yl3k)obI!^+hn0Xp(7=MbUNA? z;R`C^_&&J#+gq-)QL&=vk3CmlvM_es;}jUj*7c~+Eb#s8Ls0Vs!k8JtV=sZ}Tcy8$ z{2uLE3idUfIbu`>eNRoCfv-8JeC`92-=cI|NLP!Cr&-}=@+i;rF6nuA94B@F+RVL$o9IMxcXtWR;?8g5h1K=~NBhs_yPD>kKe2WY}?8j=mHrdgu- z5wD|9Dw$sMzl~zOc2iB|ehomhD%T_~qlpwSky0j5+yUymWYy??cHv!1AA-KWq->SE zbQ(EvUroSe*V2biUAeNi^T+ zIo`;H)iPUW23PMsLctGSZ#HrJT2UI`O;f(BVDx`GRS2eor3XozEn{}IhtSj zTQ!-1sXQ{9&u(>Y@3k{t?JVbs4b!_Z(QJMWJ!SZGypTc0}m^kK1b6Kto!Uh}Mc;)w6!D ze{*tD5w}~${WA@01J;k~73y4r+RAJ6KvP$9lcZZVn zbOznshKIhsWGj69^__L_CHZuLZkFEVwIFy+^4a@f>Hxo;MoCQl%YIpq^Dz?+EIKS5 z*3zvSV6?FmrsA#il4UvX4JWLV0LMF$%6aHk&qt@+YAix2F1<;*mJ!^*fEafL9(ca$ z6evj&VxWtAVrQ+vA-yln?^D3z0sVuhHyYrpn*VU>?l$e0sGXM`ET%?_Fr*&crHvNU zWwoUJ3wqZE2Hxsq(uAOqeb;6*vTn_(rhL3rx&^~lJvOpxKJ`B`QET#*8j@{)d_%70 zZ|`VMt8r;0sD`BJ-{!-E1C-R{*rk$O9Rb8eU2iR}^nuiiV3sbBaA!xb;@<*{5lL=B z9N}IrWJt2L`AM34VoTfwayQE*>r#PvX~4vjV^Ugpf{o=1H@B6i))+ z%}B@Jk#P)anKdms1U%=F@m*(qE56<*gx)tTKzQdHa(HlI_gl;Raof9l&6jYc)GHnA zZOIY}l1Zx;EH?}H_03*UZF5!crWvv9*X^fo#ew>Yvm+!|cR@uZtg(G;EPkvf=D&A0 zneeT+#v8;B?aFq+M#QYH0Pzb-oRbR@@o(}M6pGaICF4EkIgOvr%fVOM{dr)S)r5`q zD8b(kdM8<(c>$$x+a&xVV8tAe=shPoJmy|%4i5LE`dF1Vh-Dbu`|Y$4`ivnPX!(W@ z+~@}vhU)B`d!@^n>A5~IJ%M;XM(CQsueS5}v4=Kp=x$TLdv=-p4Y#iHy1G# zKi>Za5pEHCma|mRt?mtU7Jsy4LKdBdQif?{;OoxiHW7SGljqv_RVlKfXqa)a=VnSLre3ne{L1?@G_x17*9=)VJ--dsEmOsGZuZ8`r!4^?7En{VGaAfDpkb866Ks>l)Y_0g>TyW^A-hv+r0tegh;k+hx&a*KfRzl&XCK)kYG7IslT z;hUYlpyZqvDboD_5AS><-8`obe1X!xDIs*f!-zl8NJ*LI^esIY`ocROcPX1+G{)4%M%Uvek zz7x)MPqn%A;kFs;X>plC?usheL5N~s*B!$FUWM?`;4aSaN?$-vsiOicK{y4fUUI;t z0(NWyoGt$*hExM@u1{!SP%X&~EU7WkK^D}=aMY)ovLAKnmMp2+Gx~S8J}MW1@&a(@ zTge}r+C`}jY3+Njw%i{(2-DWOS~K_DwzDuVxZyj4W?4gb>y@-duKO_*{6nmmu#1wZI5cEM<$z)hf#fr3FF=KlM zvp>ATm=Awwxe+>?7VP?kb{#CDACX;Jf&yW(ym!Ei^VYeOP*E|8F5(1zk|?5C+BE(qZOe%;bVCPgswt+1)Ev8@&JV7=V*e-~ zrA_KJ`>JKulA~yNZpl$vJU8+cRjNb$MLkbxpsHgijkIR0r0K)F53S=S*YBE-!*aB_ z3>o)S4X3#d%2MEpHFKKwM!pWwv8BGVomv{RHD^oSJnk>d=#NqL=t%L3!xrB9d$#gscJ5?i7PS%hGWNB_wLQ$xWH^#T z+u^+mp(o3SByFC1M6a&0uN+ZgC(c|e@df8u$@9=5`VnHKJ)(BvLftxH1(#$RKAlgvem30eZtT7RXF3(z zkt+uKnXLwg8C8Lz?h;>-!$)}-jW9!I>pDlpwIutQD+XH)&d)o#a64N#N5U4(h$idi zqrc3+kwsSGeHQ6rh3)D=Ym6H?SAFMf*20-iR|iZZyLxSha|2oN7X7QhrD^%&)utmK zqT*ox@IzG0=^Z{qRiy(TBAQn@(xrZ9{r&OAT+UIBroOGu@MWO94qU04owZ|JqUDy# zJ7xa;nzf>}wyJB&YfV>C>i#0AScBI31ySNw`xgdXSL-2ILO#_FmXs*VgrZaX;F-li zpdG9-^u3ONxinWt6xTcDE?BvmY`X%))Q76Pf#64>19St7$U17X3z#0!sY!y{XzF^V zr-hp@#y~Hhw0qLWqC};3`l9pmPfz|>`V4JntuRz?HGeHg7Ke(R;n`r{<8YlRn(NfF zzxLOg0G)E4in6w%Nz(<`-`Am27CHg3wcQvlix!Vc2ft@n0FA-bG+CtmJFSeL&~`hTbJgWE(&|ziLU^b(ep0Q` z)*Ehz_mx%!TkI(B`qP_+;x>%vX2bV{{^D<4DSn zL{FQZV}zK_0dZ(a5)5M3es)D8zAE|PK>xq2ox7$6g7a|CJ22us+v66mqTuweSHI%j zW|dp|je8HxW^FGmJq}mV?oX@c5~QKEnj7eJ0{&L zubRc2q+em!;6(UFe3-c@DYwI2@E#K=GAHajH%Y^*bNq+`&oL+R^Y=4OHuv!tJXQ9 z{aob#g{;p@y=yu*eq`={df)UuArsLIw)|&rB8(0|6``>E;MerT$CCU$HzwYa`hQP0 zF27Ik%6K^w79aUO8-+o?Y9ZhzC-DDXyE_K7r>{6eaXh>ck4i29bOsfio8ppyKuQ=^ zq-n8o5gM2J9DyfiVOuP$iG=^0M=6o>OGsW+ZL@XUT1(v+gY7loe-uL4pzV+X6#|1B zB*T`BL1aQ#(_D(sueX7hnUHp-1dhD}?}BjQHu;YW2<<68t|K@E8MX$+_Ol(#goTtm z*!Zcvz{RRS(fbf!Nv#w_!aQn9ZV=xq@Mh_kT1K0sQJjc=h|1F(CPpEsBa=L!tc;_p zK=^3Vy?BVA>d0t_f;!G)Osz5O4s$^uE%(zEF7z<6*v-729!4U^o1zpeP@NRZnJ66y za`Hyz-WCX|y3>s@<_`qqskn^x_HDOK+e(OpY{{OyPp27xa*r8RQGeR7wbq?wo zXNZ_D5-KZmB@PIkXZOnfb*K7r-d53oEgEt>Pjw6ds{s!c)(DAYE)hTc4#AQn7e)sC zI$~CNKYzwDT|ScU%NoR&_J@KIBkqC@0!gE>T=EtO>iaQ4gy>xJzybFr1?!!s>Pu$0 z68_zfM0KsOdIw&?Nh(JEMP%nVJKw>KYGZ=5Ll(aI07L zv(?)xDSHXGDqw5bXbr2PVo2Wg7%uyO1CNz; zAqHME`L&t~ofw2b>l(AZ6+x~+Ino6xTBQu2 zp;bo_XijmKT!IXUWsUypU~>y^A%5_D+A_>eOoXf7luXNX0TUa&+cmISFx?M7;RJ10 zKbj#J8|d>>0?B#N{?TVqy0ZHbdipy`eh=xH{RzR$qiEbOx%U=c(ekLFpNG(ULqXqN znLhvM(x?^wsNulCR;`6sH$aJ!yDRM-wlplSdZ)Iwa@)#lqS@ALB-W~d=7i=?vKEhZ z9?P6Z;}zizQNBbs4bmxEoSVR!#c!yL6cqcg&h6hPOwXz?6bgixynf9!`buz1UM5N+QM zRwr0t7FF{TlgH^+{_Lo2kPlCFZuCpFW9jDts+1US{K1Q!xryEdXQ?65@58|UA;@1r z94C*Re0`;Tp+X+t`{@kwK=@QAGYxREUirj}CiV_+guc-)1wAREjdJ5*^C#J|6Z-N( za+XjT5YMpEeS$sM?Z6n2Juw{R1A&Pk-!Hyf2Im>}U?fiN)3kIWvu3p5?J>C6BRL;t z3$w|S>HP4dM#GTh>E`Pa0Q1gL2WSIdxKjw^x_Js!fFS3a4c6l7eK&6kHFgkce_{3b zLESWz)j)n17tJ=BXr0x9(&DkHS3UPqg8)mlPSxy1O-X7upax>Y-lHb*_jaHrwOZ~# zO!F;BC8%m^`#C!3=dUl;@79;DJRi>$$T?p%2Il|V&Y|&!*cRyF;w4M}v+UE)KL6s& zuaSjT()*3V6eI#3y?T3^V!V{C84HOgsN&(KT{8;$B za}frN{rV;JdOX~F+_ByU8OVD&1=3ova(3wtNe#A-ag(S;Jr&I z6FIj5!qG&WNhS8uf+@(-I%OlW%^K-TGKxMn^dWa&;Io1a7!jrWunf%lc1krx&$fTe z_1eOHQ9DP}a>S08Y(NS98&z6h(x}|oYw@ksHpc~M@F7zz^rkHC|0vzSG&LpE@X&WA zwZ%Spe)_N6RoWUbO&`~q{?6Z~*14(42fuT&)do{LLh()RDiM@&JH>@kuI9PUh%U5c zBpW~{brSC)`<=i>FLL4RD50X3)ky-G0eeO-dq4=HOEHo=KVC5ot%O`R47AYCX4Z}I z7=)a=Yb@VA?Y3N=-W35!SIz2Y^JjR!r%NO+wnFhDcVtuRqCo$u@xZ4{uvU0KKE&o; z8wpxbmya>d_>_4zP~pH1e8G6^K$kV0|DGb=81n$NFSFK5FKF9XW`B>7&9g;OLc?I8 z_5Nd^pOy>v_QCVtWeBk{Ay`-&3v*mvBG_d)+6UUkbcLc^X^Gr zPc!1K7+;+qY*;rKIGnj|}#q-y}&q8`DJxV?=Ox#ke^k)Coct zlqSLn;lNeKk&083M(+o^+IS3nc!_xSKC{Gb>qI8MJKoRsW+E6VE3V^(@`QoYOwpi7SzlnjY z`6^@Ij1{(Qzh(C^SV+Szzn@a>rMOt?mOi;?8ZvkbcVZ2Eyfpx17JgFQ9oF&tsXr!bQ`|*MKpgBlXK22{Ys2Vwurv*tEj5vRCA|gTwPFry-=&pxW>MNsDAJ!-~P_Ye=szfKC0yhXnf{!nt*$pAj%}dC6WZ$6F6Yj zqh@KIKbU1^!pSLRGq%na+s@n5J8MBeH{*lUG_HNaHR-b!`xGzVhLe^`T&2MQwJ)X*~kJ-4H zCVehbJe>7n#dXTY@&w83{&3MP5g9KITE&R74Y`0tszNIV9PWCg#|Xj>6Ws?V67-cQXoBihmT4@^ZfAY3vjv!gH0}Seby6$ z?5QM7KfwAZ8td#BJ#ly*IVaPVW^IEl$1tFry)-@v4$*lc(vp3irYG_+eeU1TLCo3_ z$Q^`d_oKQQl^}i2{nk<_twuuS4Hr41C-i|7$RtO+0lC-EO^_yMeFg|phgMMWLRNZK zAb1FlxTD?`=EE73CTVLK)r@;99`mGYB6>pPDidgS8}p)*sF=-WgikDkreJ&ti(q0SHJLe2HK++u9CYi8%v7l5WDzMwlxCwXBqG!P7i<~LE-EG6$ zPHphz@R_|IP73@r8L}Y&!bf|5k$nff$Y{J_9oM^Wp(`zBoe4W8jY@Cbod^Q}_|vAdRw;za}n&&<5k5$6!{g zucD_X1N0%xfj=|)oQ9*_vUSlMY8$Z;M;W01q634z zi4nc;P+9aXihT8?9Q32fuqOl+B{H1^lFV-Y9NiOOD{%z3?qx>YCL6^DMzxf4c`c`y zJucj-GoXM`a(Qo2bAiE;-}Il49CbU0&rH}#yNLMPie=aYhEMzW1EihiwUj}9c3uR1 z$>W~*W!RSjJ??^Jnck`93th2}Zo1HWTw$!Q=qT4ylAFx3rp@&A`t&g~L&81o*>aU% zCi@LeE$4oda7Q~`AnfU>Tq+oEQ!L}$VEyM0EKib^6ug+t6fqp%1e$w&yBn^H`Bjc< z{GE|Kn#}7q<}sh8X3lCRHBQ=&rBqY9g><#UbGxM)uwkyN=C^8cbF0-{$W&`w#SB_o zlxogVwvp8mv7-Y;RYg>PTX&_+*rIByH6b=8w=8hE@+(tka^?|xZ8!IbjYOFYg@>J&>p7;N9oo-7H2@|pfHmSeIp=A651mg4{A^~ zyS`$fv8=Xp8~Pr=H5L6={0VR{UBd;hhpctL+&c>1qx~m4N-As~I^pf|R_D!ws(YOr zwYoPBM8GB#TGYJfV|HogjZO!cB#T>}`Tq0&I&0&1o>cOKv(_D+=?4wFIPt`m+Rl*x zyCaTZwT4?g1k2#a7E|jMkGR$OdK`WWejqHTsy{fZ?5ljk@&&b zyXMaR;6#Y40bk?J5%AWnnQ3)trnKSxYi8d5#BWgjYf(NkdXI?YPl%g!YG&=v7?@yY3LV7LqsfH@|6WE`EcR139$_G>wz3 z@!-&m#)ChowQJUdL)3&@{cn@)@WOvDTQdvgbGVrr%w}uHT-^|;>shmRaNji=K`c`) zP6dlurEeNJcamt zkW5f!aVO`U>0jXmhMsG1ZtNWGa2c#5FBnHq)$7Ycf$>l;t+13-KX9|@&@BFZn`{nU zN&dE~>!+W8`SrK|{-3?L?HAqugk4}!uNpiO@TQOpk@0ef);`cG5bw5{)10R{2Zvc* zraD?(P$bPc^}krV1uWVx(ag-WS8Z%j?wSS_@+nZJ1D=u8{?~drG+PXf53P?W4M&pN zL|Z43hQo%sgz5m&k-xm*uzE9j)z7c?BkPH+zg0zP{7f~e)?fav*6hv3M%@@Rr*&i` zT9x6ln zBU?-*(@Kcjcx9R}9@*e!lEYe;O+Af*=xH8Zcnhj4$Kb$2=ps4eK-g+u*s~E22oYK( zP{P#1SkZCFkh|2&E?ZdM7a@y^C$2h$*a-S3T-O#zpK}(swS^W57Si+f z6%4yH7*TILqY#3^hQ1;yz)vGXdlhp=kwUph{uDNf`YMdsQL}_CvvMFcTC1rYm!jEBR)nWb5$`;^?%hc z(MFOwcG?z0ZNAOaJvC#f%^y^(lzZAlcwI@9rlbV30H?= zNFa%lD@_N5pO*6Tbt~4%b@uBJ?v?1dTyhL)tX{q(LK--RoYp3X29_5=q@l#$>~Lql z&L~su`}zYv*eWTQCccB#+S%*jtD2tj4I3a~zEtv6few#c%DI~#wn2q-;v+bu2)0N2 z&8dgFtrer=*Gjzy4j_Ix;f8L?FJ=<%oEKQ{?#lX#^CfBU%ubT+1A9h2Q=p)-CzlZg zii2c5(9P(Y6Y;3M z0{vUZ**2L?TObX1^i^9|EwNEg+)lPi+uTBe+Ob-yQrwnC-L5aC^rpDA)UW>VNPAS( zfJf9NRzSy*A__U!UK1Ka7VXn2qTCI%%LQ@dv9_?BxErwaLkcm3d<_V_Q9z2>+g*hI z@(2SoMsio|u+^2`77U@!V4R>uRl0Lz2c>IH*$=J(50ITtv^K&M9qqCdI89v|AS$AS zx3!BXHVbpF*=Dg>mK0oINfgj;^loTd+$47?KFra=8#;)aiPClp!sdd%n50OrNOV(( zJMzX(PsPs+;}8ln7co3}LB42>(U9a2SnL5~9kkp=v2=m0fOcoEL6#9QdM>#_3B0Ac zbRq~zXX)F)JBIRz80D$o2}L!Lc3Tq zcZ7sazXtL5Krm)(eRqn*^v)@W{Z#*fQBuKaCGNZb=7n25bL<$kkzY1Rsb`%HRCS!P zeOhz&sp+YKt=?Cs+E(o&s%hR~p|1)Ev&TK$(G182?DSKWzAjk)zr+7Wt&PZK|r5Z4G!w$~1=o;7S@#XXlABkDw+v z4Ax|WY~W9 z2*(~0?IA;)cA;hbDpUHvRuD*obu;2&+}VD&`)oz&W zRLYl_`xN<0)*3kXs|zx*lxI*ntUy%WRp|569T()1` zUn-PH9U0**5;gXTekS9{Sj36tk52#^f9~(%LXDMXtOFu_YGCTKGU($KrG+5WTCXsz z=|T$39y9VZF?l}Qm(Dd1hW9u~8&9F18d$ttEWaHF?RiRxVd`_JF!0ERk@xe!rFTgmWJIqQJVPc#GTwlzJDkF7 zNnJ9+{Na7LHYj<;#;JugSv_>0HL|MOYEJS<-nq(Gc-s|0fPMAE)#$TX|1{wOU6chS zpOKdtX5cN1St^j<+S6 z6O4|<$nTgX196&=pA&WGWt5_zF=y5pH?Z;E8BZ`c4dEf3))hxRwJ* z-E84w_#;Bc=I?^|ncl>m9-Y9F{-gIwM;=gSFDk?U^rUQRrYrOf$${t8PNRY|Pzujx zyq-qE8w2LWFU-s7c~B%+oCkMh)ySR6OG$wdg5o&-Y_)}n6l|i7eC%TIv>$r?o)E8L z*T5y%Pd%k^z|mXv6vfBY4qq^3yZFoqUNjT_#~r}C56^5*58h{e6=gZ-zm`c$ZjmfM zIBZYuf^BI}3YI!9_+x$jzFU^Kd>Q8@$Fp*S`puD2zCjdc66=G&J^uaeS^C*T=TV2n z_6BrSryMDUb2tT7rzw`cqdf7m3uW&nrMHdo?@t_0L*PyP5pLPq(~rmTTLVJN=fZhC zU0*mohrF-N?@YUB`YlyG(qrso!3T4FF=$K}YU_^@`c)A)2-c8Y=FZ&$hCTT&8Cpso z>dm_)ycY2;PR{GWh4upJwNiv-I6gOGj+MPn1tgCQF><3Orck0ADCJ7*O2>^IvV4z_ z2q(^Y!`&kXNG=>z#??M0D*YVcz6oS?-b0fUcJzsB@E{mYYYBIiK*=y&`W_o3B&9vB z+yFnrA#_3rr2Q(O(@N1TqQY8m>8Hy-p(4Wl4J6N955jZ^64sjYocm(psD09Vcm&=H zkG|a}H7)r#_y#aO{5QLpTtwq0KFnJY><~>9g@~kK;|@^*`e1j#iv==LWjkb}iS()X zsR6;=O?p^zt?MpEn93INNA7o!!)xZ_nsaKUJ0xYgSQkZ5J@3d)%My=1A_Ef0a69$)#UXgrlGT zm)SR=ez`iym*^pz`-TtF@=+f3Y|OTJxG1jZsT?hFmQ^UpB{JGFV-rLq0JCE!Ft)#^ zc77{(&ocm+{V2}Z!)~&0U?vQ3c?EAj`zl>%T=A_NI4j5Z*b4)>i-MH_lFQDB6}09o zO06FV*A}4VzHN2{_$Wx&s%o447=!cdGI@R^IE)~<-e{c|@>lZAHRuL<&&D`pV_hii z)x<-HMvLP=uj(4wC4BOsAkbf8ciID(N1{L&_U7AzQ!RD7>StMdKuH&n~8M0>4vA-E2o% zlL_C(Xf??>Cp8*d)$(q|5Lyvb_zD^`jMUl8s4K0Gh9Y+jJQ+KUt-*~9Qs_c&RtNB&--??h5$`0!5ZZyl;1Q$u&(h5R=@;9sfI{8Kyd#Np1>u#u?(H^xq_ zvW%7TP^6^Ffn&6Ak){=r(0c7u3lr|bF|ChG;pPY0}pLpI^-APG_7E-1P= zi^;S}FMcOnu@@Xa539uzy<7j?C3cK~gJW&DH;T=#;L0$UpR9B?_QK!~;`*x2V#Jo4 zGVG--T4A2bfhBAAL#h)&asGZ-BHp#4c*X$%22#p<31QVDBxmuF@&m0h^6- zbQPHfeKCeb8q9DooGxa?9$n&kloX%<;$kLBB8)}{e)MFr@#uky{jlsVTyY|sNo|p6 z1SqU;VKX8eppQ*!<*91nb#y6rv|Rh)oYDWa!;SG#6;&SID&)VQk-NAphQ5#^y~RE- z>Wse5bWI-K!gT4!GJ!M3Ww!R05r4>%Eida;qkniM8M9x|(;7o4@m^|Ng-WHYfoGwWsSBOh&)Z`G8A(KQwv@QMbZDkFj<}N zN;^;Vr`Ku;@}YpXUpMYKx8Zl0{o`OjXo@G)m`AYP6<*WvoM>Oa7`wb#GW*JB;5Lqe zD_`66ZW$eBy}E5i<%QJYvF6R~(Sp9#c&0xOzN^7v>RlRaUA8qvgBJd8keh7iypC7x zt>Y3X(LpKyT^MtH*^VfNR#`uz6xkDW0*sA$Y~~-(2PE-3Tu{nV!-G zZ9A>x*L@+Tw-hC@Uq|XGPAn{m$Hlhn11{X$W8#s6P&!e>MjVH~QYB>SES-7f zJtFn2T}U7|P{$2DgtNFE3t#R5?vcpTJQmIG&;Dr~!3^8dGipfu4VX3lRSqPLKdSS4 zUCXybKR)RmAbCpssZ0fmmy>j6`BP+SnlMsN2aQzf3WY*6b&%v$!^2T0J3Sc9f0RV* zA;$orn)`2Pm;B>iGbglRWuIWQ-_eG41-h3J%1GjD_T&_P5yyEe6;IeJ?2VtE+iywc znepi96Y{^VLE%6M_39=HYf0f=Nbiy@*YVdShsIha$_JiDK?r)=XgwFqSVu?dk@2^c z*3L{t7CM}Y^)4zDWs0O<=98`D3%#13@4tt}{fe}2BM=OY^Vf8sP%YcPF+tm|((&o5 zPS&V??rK7aZ#{o&DVpYOBb`HaS}WU6)soe~I-0EdmS%t*uc4u&da^odYg%hcvv5Ln zA{8{kXxQM2i1Gq@pp#n9vQSDpLD0r@COa^0^c>kn=|w3S`vLtx=%@Fl7=*XLPtQ{* zssblUT7Y?|`F?}4DgVE=+lK{0pdlS06fECwl4vql`ldU^tWsx4ZCJ5vIbUEM^?9+z5}f_7Nq^q@f-GVKnsaDKEpd57Md-1D|9~W0uSd6&%k5*`aTa ziyd@#uLI-zBaXtX+e@$Sg)nAVz!-7zh%?>y&G~D~MqIcatB%-gae)teY-Yp`#zniK zvhz6NMfFIc6MEV`9i8!Bm)e_)o^{;S6h@ZK`>cz ze9}W*-2bO?Q5PV^1IcB`aN zr}8T@Rqyshk7>5&=_Du6->glN808Lu#QR_YiONB`$#fB+5VH$ zJhQE{U7S(fJvUD3Ph~HUS*-VZ!oxK1?N-+_eAg}Ntb%mCzRdDoCfpQmKq05~@y=x# z5P1n?Nx$I!x>3{q`Y%A~v^Skw%~(x@|K&`^N#A-hBZc={ZMDOy7Wdy_DjloSu~Ig+ zZI){@vwfb|x;n<1Y}kF@66GtxJtsjU^N9R-H5j~NFmN&MZI>pD6>V9;eBeQ1A-E=|ZJE+%+|?RnK88PCt1 z7X9oGdCE%@MAwzMjPM3=)Y9I@(Z>-Qpqz_A8@a>p1tDf(sNLLcAdT%SpbNOUv0!mT zz+ReATYIeNa$nY6;jAUPYeC<`s99_xkTGK&e%~f1t6AL#jL-oiYv60<$UN~H{XpQP ztDC<}_yK*vTDy?i^*)R6i`W)^I*W-dORq1!q2$8So_k1k{Nez1084M#oMGj5X4497~*m= z;xPr1dQ5w>9*g^P9$JWj-0=HEQA)htgj1bzKk^u7&JVbub}ZFJONzk)MJDz=MJ6Z_ zD$+UKFNz}97xph3Aia_fTgUbF(Mu>o2YG?fu`b1vYnXIkfFX_O43JzjVU4_Q|z zUk$=kaTsju@&Zwf&g+pe?kg4>d0TE>Vsu(ZKpzub{%ODT0h>t?>2|<5<@fznC=m)g zH-Yey^#L8h2N*sjRbYHh`})EK2bH_}g*l_e(XI_}l5>Uc1#~qV917td^1To~MH|Sq zop!DTCTBqvIrtRz0fHq|v!}bR0aDVb#&A1>{q)s;Epr!*x^2jM=?LzDn^xJ^GUF)$ zmN=m4z1%2i+1HF9TkoVK?qk-mAGXkGyF@F>2|Bw8lI6LRHxxpNNcSin)41*GBobak ziE>o;|L5#^TJr@p2X$+kc8j%9Ky4Q9=%)6pN6%m)~;NN>7g-sbc0*JUUfjj)6#Cv80pdMSv1 zJg9<)FWWmU>N?!hmfUrOqEeU~kCxk`m`&X?f}rL!nE6YRE&UDG>7sl8h)A6ya2I_0 z-~9DB*z6EWcFi=u(|93~9vsSk*BZ#eK*h^S9`d%k-ad&@P=fhCLma^4TqoyQJW*lv{0J*1{bQU&uMm#@F-n~D9D>I^_^4#XWN%0Cq}eq>y}VqA>E-PTZZx?rX)?Q8CY#Up`X?R$ven^C z#SPT*5QTcGCIjDpS-jgJdGX9yvp<+Kci#LD7c5-#(Z?O_Z}W1wx~}67)ZgD_KRfT? z;^qGY#=F6sr9;|^Y0dl6+yzX9-QAHg9aP1(*A(a3cq($Bl&=Z*g18gyO;9c(Ir^W3 zEg-e$NU{(kK$k7grBWcuydntg!a@l~rgMSS#&<5j@DV2YVC>${W%4JxUEwy|vD1D| z$19GoT6DQ6OYwm97YKr=(*xq|5IVL>R6+~Nk`eA!+k^15btVBbW!e|?P;iq$Ug13z zy&|MLiTrK%B+B#~N9GE?{{#WyuFgM&I*y}q(H^T5L>LPG_4fNNI_zb^C9y7o zfup(0AHn8d%9F%p5X2I8Vfui0V)b)QOq|KfPp1?C>i$n*(k$W6GK2Yy@OR{!lZ>Cc^&%EJfVL|5S7LT#1Y6?V^i zRoKiK=^p)9^5}QfB9=5PV)h_$w{wKT5pQkWwfv8P7jPG$L z98b$FVPK}%4lDG4><5RpNC0kB=4bMiO{^qfzeiJt&qVKpMIj;w6 zb>ML%$~SafU3huv|c=iUfNG5d5Q! zVOf9hph)LKuVCCgZ>XTjLFl4&Z+INWt)m0q>rI!TZ(#~-8dXLvQ=%^DN>27$kk02} zK?VI8q|&(E??h{Tnhf=a3zT$6uWdlz0MP|8z3((!nkyS}A_SfthS}^+kcaMp*&&Pf znk67UFMFGavgvb7!uY$vU`o3Sa>nkU_7PWsP_&!Xrnk{vva+$UBK9T9i2~41h77F& zsWW|ZW;s*|=Ew>P;lcP=nQ&<$7#}CyvD*Mm&yb*7c$vmjO`aO<+6U(a)vu@qvbp)jL0?#e$A zrS?;2cF}5XsLUmeI$|dHPPIE$SbZrz`j%Q@hH5px<4)d@;_WcGwzE6J*qW8mz|~rl zTV3tAtxaSZ8z`j@#jT?vVRa?+LkTpAr4Mc3bwzq) z7D_XA*@#O&CEVN(O8^(GlX(8zZN#(tN7X=nI!#V#NnW@B?o0=$m1n&m$joOw9CL!{ zh_pEO!*QXaq)@P$8!NGN*1s-5tGT0eh$JomCS93A^a>W9VJc@`I|m-N$>yQsaEH}% zUYa3As|^HQjAhC@6U%e6#Xi1pFbpcBi5KAJ@)0_Rej@$P>*%?Ta1ayDXQ^uY*xvkfA-XiB+@;v%T;4mpydrQu+G!Ay91Oych&B;s1*{e)hJ z(T~Y15-0YyUi}o#%4xSP=8o7%ceF!pc51ko414z3M-X*jX9Z=NpP+yS^>}HxoZ{^%gj;Vi37ukJc_S z-m(yGnF(Gfx054i+PrUQ`!KUR$ zWufqTkrPo|kAb*VTexrP`{?rf2(p2?N9{v&oN0P4%ITrut&Yf&7pS> zgMN7guvx0-&Q_-p~pF9~gXN1%#1zbLP zG@hkBm)yFIXr6!!fiI-xPd=b`Rl!Rlpm!)P_+o>5DH5*LP>$4>E11!lPNQmB1fp=i zP>%iKmEtkbXLU!Z*3k)y4*JoF43~XMKVY4yW0EBFb!7Pkj>qKwezM;6%N)B`%c>}N z>p&Rc&PWiMWSZ%2L2d2-KA5w{P+d)a=VnfPn&{0#8g#6b&AVK;Q1IKdRSy0A)SeMl zOHCsK(U_r$vuLDujO8>h^$yIY30K)IjY~hKy6)Bth~kdO|1q>>)6taF!(XbSbr5>7 zSqwJK?X+{z_}dkE3-czGPc`0olPF~(h3AbOqot>;%YHp(PK@>rzhr0c4ASZu+GG7` zcaW!q!ZRBpMbxm&U^sgMv|<<+TDXgfG4Vo3h9l@)+^H@vN+>@uL@Sb(pE-p4`#piX z=pdBo?g4lHe#f~lWDbn>`jWW{XhvBh@nJ>fhagz@<~9f|26KWj&1AwL;RVZDuYoLT zlrP+x`4H-XFdP}}nVb9Gl?h)765-_B%6UZkx`zv;y8#jZ36HaeVF?)B+q94;%uOL| zsW1Q;T7k$(AYV^Bg&yyPgDAZgisua*bPLxnymvr&kE!w2&T`T62y!kNjJ?5P zt(G<)>N1O2URQ37{+b@=x@KI1)E*eqAXqn$PNeYC{2Zm-qDzup2?BU+Pkp0 z_q|__3WOU=Vd}Gy+EL>Cdmvi47q$OMcEYM|k^et&?*SIo@%<0~&)j>r+^sAuW$8!} zd&L@ik2SF-F^P#ujK=hDifJ?zP>NCn5k#?}pn@o(*sy{kSU^$ih=`z~sGy?nxeE)s z%PwHz?|FTahiP~2+}Shd%$zyrbLON>iZ-*3t%3K2bc*v<{!O_6hgS+=^j1E<&Q0_ENRfLthI$& zw%!<{tl>DZf>MQ1>@w=WfTOfW;tV`*N>^9Iu6Lhe=yc+d|J^0`SwDIBY=lMMjq&TS zde)r|)5!|qh=&q!BrK$_u#a}Z7)!S5@2iul=egmk54!k_&-K`VVbw8^6m@NjPLIhO~`p$1Zr5} z6mI$`29Bft+V{^ZGY@`R?YcynkH$2ra;f+w|E!33a7UqcV!u5LsIWUL630*-U)cJ- ze}0m>RVj^I{j;J?yoJ1D-UR)w(Mm{hV&28Jcf#S$IykB-nU-d;jGqdM-}XA?S`yLI ziPcBpaS8-Wd6!+we!3qEB{QCzFGy$ZR6$6am=Z*w;f~l*d@tH+;J%X}=73F4%pJ2f z8^j1fYEPa!oy6E|s_}g!2EuMNRkuvVttC=TyGCLi z74y_(cD*qFt*`X|u{Wd`kgcii`X;EZ0bA5Ei_>BhT1-v%RMDC?x_8QkpD4^P7+R&j^Xt9^0e9~Uf z$h;Hr$z#}pAp~f3 ze`^*Y<#)Jh!o9I;kQJ%s0iyuXE9Sql%yj2t;J8nN<%6q!TP)e?13OSK|B@p9{OAn)hG1c^+1gLuM|F1r z<+zgUrOb~qj${U0w zcoyjJQb4EiO*;P&MfMBMypu8Ym?^g8bm0%6?dMP03n%|tLR;h9?WGP{n08&lm`a`r zEQ}A2eQsm}tsy(ZOHQlQ@aoeoZCH7&P8eRI&8$bw0(+CcoN-LkJ%vWS+zgF3$o$r{ zadv54EB$j&Gfy>BRX?t`Hx zqhJ>`9wWz`sy;5EW(IBdf%b0KWUa+#}p zSpvq{bioIHE^N1lkg*OCG`7BNBl9464}8V0ld`uA$lP2|odPyXSLW(2DcxM11y|FurX`#qu;Efpdg|8b=D2sX zSklm-A)$^|#^yH)*leOYPS_)41?{*f6FetYx>{xgc>B-%!}z* zS6@V_>$juSI>C7I`>q7xD*kP7^lpO>zvQbckBbEbtZ9g1X&XFS-^2I$n#OospyiTm zk9(*-d_eJEKZjgKFbv_suF`_nR5%V18_Lfy+hNiCFFILBa+rl^r;laB4z35OY0ziB z&KCFZj7-k40&cKa=PwVGI6jwmnYT(3PCmp>tN*ec0LReT;dBw?M}rjOllt@(>@R@3 zJIrYzqur*F!!H6@LkZ!Xmoh^Pq+k&xB>C(`^A>-=N)X`MThDb-er-9q(V zGMBl=Q@rCyIu<=l$QqC)@P0G1sCd6(#nMIgS=O}!*9 z!Cfkdmr5OyBXRpWRfNDMo(gMZiocZtHOL`#M$)!!vPM0=Dtp+y$PgpjSlC5+82Pr=Ta zcdQ5o@5v|at)}g-g~RimQ6`+AiBpzPTnqKQ4|!}Z)AqB zYZc1IwfWr%IuK65=6T}LL82i3bt;29e6Q*e2qP<|JQJcQnmIa~4s*Rc%lfIqQ^8?e z>Q5c&$5R>s1cRbxj7sm5P=rusvyN_pJiL30p%Al?Nx*Zh`U?`e$8!qy7-QQV%}UlG z)fO{awA?B}Hblo7nl=1amy!)rvp}1ib?8__#ae%L=vhDW#fYQt+tdS5DchQsuu)>p z9kMCoYu>P2_bA%P_*yU_Z1| znAh2SPl^#A5mO^n@FAU8RU%!OiEE1&n0dHNR?a6LQT3o@*)p& zf!X(674yVA44%zSzYVq4JqWwzck@&BMKdwzbCHGk(9jJtBmBXBFI}oqKbPlxnhOMR znVhq0CsIFT=GS4C*^BmsO{BP?1!7d6D35W9vYBCBZV7H!63gMW*2KN!I4~U%W=dHh z4erbh0AZ!bjFI@TqI|kFSFn9Pd+<2Ca@m7`i!(;D$L(A&n=T|B8{XvP^JV)hGchK! z*DA>Eu!xY+R-*K-$3z$hm8}~~$IE>+(-XA(mUh}Q%8jf2G-e0!gV2iwbm4W@dX6d!G##^edvwLq{EB)hq zo!L8wY_EZz(%eyUzrrp*pf@AblmCLeqp(D5M^~VG<+ag*-v@K4G)F!i>$EM3|56ZgGtGg3}J*LY1aDMPnuElyPN|rT_j**1W|hJu3VOb@bj5{bJKyGpBt>W_Dib6o*Wo1 ziV1<;_tEO#^ZoV1P7qnPOx#eE`2-?_l@3@&#<9=RAgQf*^|w}{N5>$k=Y8oz$qs}? zs>GevW7$T*_y;*~b@s-Gh+g4^sQ1=2s*6G%Td^5r=Q}oZSoe-4vPyp5Br*F?xq1WQ zDxDd*doHDU_NHhHKO+G|<<^x|u>A>abPG#^0e3#UunqmbHAB5{8UsHSRb$ixt}9M6 zaTxZvU!Yfu7xlcQT>TK)v!-d?8^xMzvOPItfphPp0w78{*5T?1? zh@3kAu(${gtc6dJ-)?3}Bf<9xrzM=@d}XItC%&eWMk;B;FBPGV?V?c7$A))Z&d;?H z1h?nR*RAUpOrLN;g)HSmKCLEryoY3>|H4=v5YQz>U8VsOSvL3JB2oXP*yKSQ(H%-k8DBd(^IxiO5u!%dWCX8!*%7Sy9$ z)KoVDUNl!%@9X>)ni%aus{+@P;bX54Q6c5j{$EiC7$Of%Sul(bKg^5l7ECL2D)0VaIvE++72^hkM5Ad5d9E1FmUl0)$dQv- zujpHp=RwGs?QHd1@hU{M$dEpqd7&7HRsR@p+BKWu-YQj23ttE2rt=Rz0P{NAl)%2T z5A{1son*x;f4ocGdvq;v#PuDy4!q8R`B?UtYDFzzs0H@GI7K9yA3T#KL#WKw>qp6^HhHw1 zM;Eeuyu%<@V2hoQF1Cs7H6YkhK;JfhIDjesfx;k7&t#!8RGOxK8Sc7Wh$>U`YH z@pM0US_=}fXvcuXotWFXuyr65Ct@Un0(#$AUY01CLYrtrb7{J1)bKeiqHnlBXi54R z^c5S8*vjM7j*e=`1w;KBDyx52GgWv`-CV8FI!y)Z*SImPC93BHZAgOQrpG7~I*H{7 zGa33MF*igB^!gj%z%M9+Pcf`a=x}S>EJzaF;#<%K_D{dCsPQQ7$Ha6Xw5JbAX4Z7K zI5+m5WtJL2Fr%b%Clh0u4W zR!w;&;?G;h>$7?E4bi9sS~%|>e9zr{VeFeK;3ziFT)Vk_Ti~+fB*(j(eO0lC8?^tOd<=T~CnDWG_vT8kh1(3PyxO^enpM zpJ(r!Lw9h+w2OS=Lp1)N#!^GBJ%E^dQ1vUrMxBOx2hjCe6xS-d(+XoA1}ypStn`k10aa?x5Yjlfoskv zu7fB;pgi?O%4nv#ebkIlTk+lZZuhCoLCx9z0Wr#DV+osA1BIOvz3_~ae_^q4>Xp&?KUVguq>~_;B4+VWgjXtMVsU?;F%|p4{?*)F z&K35=N#tj_ZcO0E{*#pW67+}j9L4y2lGZ*EUzqKB)3pup$Y76-UpKn&!;8P6CUA>c znY5Cx3>F7YPQ45ga=RmMTH#@^-*FOdjb>cgeaejkUYlYFmhF;!9=X5^BTpP|w%^x{ zQE;)tvD_)2=bBB@nfsmuee6?V1lO@tX@F5-LiI!x84sqQu0(n@7MwH5QmX}t|PLaH^bLj+=7P1eZDV^ zn0@r3HaUoXf$i$nHEL3&B+42WcNfXwD-b$kf459qKE3A@3?_r|)kSEzaNU+S6qoK@ z`W$?6LsTRbs+Y+G0qry9`8*s^F>Qj?80+impbZF9+$V+z=_g#5W0#B;z2};m41eaZX1zP3rK@%ThvWDPW z&kAwdL-qB9tM;W`H3a?pGyX4f(d76jlvN11gTa=L;8YyIdjXRG z$qQus*l`%kFQ0tyowL{@hs)da?5$brZ%7NqF#i8@H*em7(I<>n)VsZNx4;GukDB%V zb{OElW_vmhwx0K4OC#*BCFZABpX2{=(a-f_-nrWsm29Q-jS#O5&%bu5v2>&UM!SVr zqMJke8vO#jyk1u4lxov#rS>(mp@uqTT#zP%g!BMZ<-p$6rz4#owmzfv`)dDsdOr!5 z)jioY9_Q*(dPXkVCIOh%u?o=3Xc-3>P3jv8aCwQ=zc&U=HZ&Ewru;3;MvD{D#O&t{ zri3**5&9J^O^k$tV`;c#4yRPhDK?lAZ<8+|?dEKC$&+zsQqf&0p7Zt~QlxuncM*ok zTYyLCA%1j^_PpPg&%%Hb_Horv1z*Ajs(tk5G2O@(Xn()MT52(Rq1|&JJ>Q$@af0sX z!}xFHd}ssh&O#`k%vpH=obOR&&gKdr4vHJJYyY+VZcCoxHN;6@5=-t;mfq%SR7wm) zn#(R1EbNUBU$!4N`lqLV_i{VkOZ;pG6Qxp!63HDNychS5DdsL#-mGyrO7cCO~!8HIV{I3Yo zW!>fkVqj#PHEYo-(@Ki8)AgK9w@5c9 zmFeZ1&+0HWy{0;K&A|+6KW7NhS`a|OiAe_xuWGY)DPkK(3g}(j&mIihXvuyTewn2y z24u~q;#@lMB)*04>C*num1;umR7|{OUvz3D(>u20o6Ztn>ZWBa?tkj(^h%EoI~_@f zo;7y-m6Em=^c@R7XXknXy-(ydu*Q6aONLOJLf8M<&oO}5Y+~)sxCjz$SDFr@Hv&hY zlfy*_>AW3wkTlk0R20VC#+P)MvH?;^q&U`b=xr+2v0Or@E<^)Q^sgwhI%0yjLiEW1 z@E*pm0i+6{hCaJ>=vhv*h|~7!&uF1ad%0;7=0VkcvIx5FS_~wTH}Rr5`Tl^70KH64 z;%>8B0u=;mqWtBO(1d%c&vUT5Do|4;A79B)rSO;n$L&A6LzM+xN0S&5hYVAb|G@Kt~X7_XGFW^pq5xprBuljyOnw{^6YB3Vo4kTmn*1}QeulS6A>eN6}0~;xf9t!Mmp`@0*vbgAt@G$YujWv9qp9c5l3?sya>4pX9e4+ z?aaB!81U-`f2Yjb{Qfe?{eET7&8I*-hh1L{_}ax7F5UWu@^&Ev4GUzl)K0him3Pnv znLF1uQQ2r#d~5iIRvWFP4vT9nZ%i3wf`*zHDAO;}&ud?)sbzPI4eGHN5a_Q#PqUCx zc~k6bvaT-)6rl{C+*a;Go6v!XHQ;zqc-)zvE|9F{`w)y^XqrSlC)T!V0`wZud4n~3!3;M)~2FNifk>i~Xor&>#l7jJ=K)42uUHKG9B zk`ItavVM!86Dq3X_98*$3y;%s4yLc`T1t>4KV>d|-u6*D3pxdEDaauDFmuP++mztn z+vfOJkOtBCkS)sZ!x+ZU&tB6tFAWM@dFbx!Rp#4D+ zzS*LXzd~+7FX@7u6X)RGS3sc=fC&sId z*VF@k7-(Ri$kY(Y?x6jZj-A4-IjBhC8Vt@=>Z%T1QifVZU;eg_Ej-%=f;0O;I5|nluk=9VHeNcLsg#$KBM;&ILV5ZhUFcUZkp2jxhFBE7FT37- z1LQx0JemyM(_N_UFQ=y`MQ>2?)p5y8KMw*9Ra!8#Pc7|Fd{|DSC)r5xBSbuO>rLXB zTr(G5-3@#bPO+!PoGk1pc?p@oPk{WvyWf=unImeXW8frVq>R2o@gMN`>4>zAulTvX zeX3A!o=>uYzYZLW#6E1mkNHARKABhd|8l|Ut_U-fFI*)&J6>7{CeJ(}ANb)S{x;J} zkF{=So+BU#@sVhMmcPs9$3hw%n*fUuaikjWqLO>r^!r~eoroYzl04qFKtS5 zky`vn0yxrl+vd^yZGwJ|D<$MCc@c}JNYj1viVfV3`7MU=(%Clr+l=lOqe-rDPo|$T zyyws=HL(3?*p_J}-lpr9#sk98T4>z#8a7(ezqW0*52KYvE3`2|jWlXn{I(rxG&}Qu zODx*YH6a!40)1j>$ZOGu@uUID;fl0~rasB0Xl*k$P%6YNiK7mZSI%ilbeft^#)^V= z!5nc&Hzs}sED>fpD0<-zu16k~;{)p^qm=?0EiB~aQogG&AjeE`9&?z=an83oH)f2b zSQ$Yda9ob|38llwW4sNjDgzvf?T^}cgQ#p)jOq`wWO|@zHC3S!ZRm6Lhi&r@;I>e; zZt`a5I38i|Q?rvKTS>=a^y0b%%w@QXfk%VeJUt7{IO~(b3e|YCb0f}*xNAuFTodJS zyJ9%`&0}_kS+R9YpPR$$ws701-a+RO)_a4QrE=9D$&X-}^Bsnabt>zAdXOl#L&ujE zqMw&Y6v3!+Qt$Wosyj$ zKj1ymF6XP){nC2}GQ0aFIw1$#AQMHR#l{*)_${JK$#1wVFH*$X{g}cl!m0;Oyy(9| zo$YbYVZDcPa=$Ml4sL6fL!vJzF8wbP+2v>~ULjPm7m*ES(1^x8vD^;LTOPKX^ApK8 zJwDC(te}nq@>d>8JytKY+tJ@uvWI1F#j;(SI1Qz>>9g5%1F^O;VVsbS)EJ@fhT^7# z&Sz`2D7}(W-KW@llqVhC;dKIBmY)zlvi9lB>@rUd163wo`p;!#8SOF$yIt*CeHaaS zx`(yTI06g%BaIf7!jV;Q^!GyIHDo7uf2Gq&nRJn9(_UU8zK3<u-d z7)SDmk7=4Ne%a3Ms!+C*zJ3t)UItm&7%8#CNp>z8eZ2@y^J)=6M(5EwLT`BX*kJC7 zs}fm~{jFVi@ZefM;YUp0E+cl@FXU)a@1bU2qbA~JAHXBx$4f_KeSBG9y1xQ;Q?j5U zNc;JHZUDOUy1f6((NFe6=0Njn@Snz1Cgq5;*+C&}+?k8;0KM*pZDgPSC>@#wdts6f zT%Ux@{XZLsHKiH4A3oBbGt(rB9))iO{0;y@DiHKTzH#=cqy;KkAFx#8WkQ$Dl{lRVyM+xDcgz-V( z9Bv)K7be@7#5u%~*Hh#&+SQEhbQf)X?!Wq` z8)ZY9iZ}R{x6t$rrbE-z;IwFFXM@&udL^Y=LE&a&HLNBhtuWAV+5GBNa7RFDdLrJ^ zF@a(OO~(4!kaq6Ti-5VMjGVa#LpXJ>=TqvV*K=O1iTJRbJm;;;f!uyxLaRW!p4X_@ z<8#I7lY-DS9pN{)tAa>JVm7CML>GYhYCG0ENH3Qw3e{s%a!Db+r(Ap# zm?gB>tf8xJqB-91jH&K8VXa8ado0KzIUS}CpIRk+zi*&RS~W0SWQL&gAlk-=Y84m8 z6s&>hHIVd6F7AU4Vpt~k+=2dWcYRfDc?H8fRYF^Aokp-Fk3CGCADBx>ITX06J9<6F z5AR6S17eH$VNbzcgou`G=$r!|W^oZDHntO;u0n>*P4!Nyl-esN?eN0ps3}fP$Hmfy zPrt;@BnP`sN9Km&w8<@mX89Uh`^L)aP_>({ZJlz+DA4!JkLy_48@$9|Qi{eNu1+c_j_u{_N}HvU(C-D*}=GkCA>hMZyIA5kbDeZsA%rNeOD{u)Y2b(Au%ROE;;~w^!YX$I48CB_p8Cgg zz?s3DLm(;|o=>U3abWm0tXd&f-0nu&4ZlUDUx%QtZc6`f%KpH9+S+li+DgQ1#YS+R zeU8pFee!X&>nYQS6tG9_R0bX0@f_)4CA!epIeMNyj~iv4O|oneUF{fTa?LWxNX6&=?Nn2%<=>G zC%un%dbx`S~MioFmVRWn3^b{^`6nUPtvk7 z(zHb9kA@SmCR))|DFzOeOU@17$qdXNB1aI52s8RuBy%FLqdCgv(Glc}(oFcYjTKTj zLo`v&xp?VEB>B{1u<={qKks7-PV;|_AboJRd1c~$pldr?Z4sNi6z30_A`PaVcg^d@ zV2E>$ngvMBxUZE5(A+QRvmm?-qkt}DB!{j-p@_ehe{^aQ(mGdnNhmzZm~@G^wvc2F z^q%`dDp=obgWW+m26dyjV?xeBpP&kOBtXL+B={|CJ|5N6VFi5uKbxid;W7^iNOcTK z;LJ9-W_iPcaNQ33p-6oC9STYqjGC4u6;zv>!~hX(x?`uQeT z-HuK=O>{CX#w^f{HPH0grX>xlrM`+?X2g(4v!u18Q!A6kg%?+&FTh%JNN$i|deY0V zn9?iNms)zZGg?`V$a-(KZTZ{xzHWY6v}>DAQA3GZ%r{I2m(ZIM-K^GB=ud-@yw-GR zlA3Ax@9U@;a%_MdHsTM|@}u=F^c(8uwHBamm2S>3qNTw$>cY2jT>>qfa) zgN1|E+MGTPQ4@2>5}4CIyf+?fhO;-m-M7GHKfTUX>Js@OBJxTv;65i5Evj^%ST7in z{~n`=v`P?LibtC5rx!8x?QhNKgH~(~-eNTMVhjGccmwX-h3d{L7lT=-G$I%5&ve;+ z6*&B2ydlFE*Y$J*Zz~ zE5gFr4<@e!vVconHaSBGA}^N;_xM3#GPfN?KxEj=&o#ZmV2ESan~+5|skfHg>1~K# zCp>drVHAFaU@opvusE%54@N4y>@hg9(-U$^w3@!qk@WPViq}9KkQZz+%G4dYMBNeh&c_mjYWa^|*#_56!%k28jMq#{b(K!NPDbBTQL9obsblY^ z8`jCyK(qQH_F*H=@NJ^HNiaH?mO?Z_j}nKyHn*y_GU<>(`O4a~eO(R?bfKKH(AtKO zyMDOE+V%*96{Kw&D@4%=q)zs1s9uBCFZA(Mu)8NB{yz|!9-yCD@DI@0gVAuM!(;H3 zwQZAz=XLeFJA`zK03^vN(hM2-LjAI?ZQ6cv$AgZtM+@(2D?Etwz@Aq#_3b&0Y^up( z1WH3| zq8WPOu==6r@wiToVZVLr`bBfP6nY{BOVVvG>&HNT1IGoe-MV_Cr2X4!ryG5+%q}t!rA+uq{)|LH&CDyc}=c8V()RMD>KQj}Rfi(K8B!i-W*~gUu^ThSL-5 zL(B>Nifv<^5H7Y8kaO9VBb1JmEGfy=x|h zhI8S~OZwaApW_%w^w8}4A~`X^ms`*|J*YEXsamFIxO3l2HoFHu|1{HW=UUi_{`tvW z&!+=ZKQL}xjKZT%1z88%d!nTn9Rg`{$k0AE7IYbP$eb)OnKEGMr)|+>>i`*5PInHO z)^AE437AzVS|LD>{W5CJF^)$@F&HY3?Re7of*Z)_2y@J6c7%<8|J&ZM34QC_ zJ~mg|TwT2vQ1eZU*Xq^iWwm*W!78o7x3D+VCY7dXW5mOi8zdmz!lvY-U7|}o+BrQ! zGC(+*B7Fi<=NqYqWe;iX4R=y1v?1F@#kQL{+WoLER16u|``8dS9Uc}3Wd1G-)KL4H zx8L;MP8oUi$JR$E6gRS?(Bt$s+^%aiz2|qb;Q$QC zW*u?_R!b3p2f5(LDw&m&w`wVA;0oQ=cti1^4Uo|8nLOJICWx(&`>D+8xZi`3r%2Lj z3?RK^2GcX$8?ujJ{N*ih<`-th=y7)jjXv#g6|K#z7myDIdLD&UX2jv~D(HbsD>o1N zBZBg=A&Wu75|yuO;Lz{+-LeCLSy0EGd2Ah1tlkdjesI*XN7W_Blj4AUd0%ajlv*nn z@|IE?Hv*GR;Z+5y7_r0ldk>(s9H%?+2KNG`rNdzX?qb(VMk9Nppj6_+q*{zUB(h$` z=h)yLX;KViBg94|^l$iN4I?b>h`tzI!|d{yGMD_H)hc=>1 z;>!*ch{LH^+lvdNJulP%8n#go5_kCC`Fi!Zn?v-w>=_wnM`{?A%9E&S0R!~>+kZ{h zfPs?cmW9!Ep-uV*)7mSbsr{pd;`RPET>u=!M1z;?c1o2*v74zOXRVa+qB#EH_owcG z8hM6f_DNI^7AAlqLqHV%qZq{3^s9oDgTT9i0oTO&VxoA)J7pzqf68jH6H%mZ0+&Eu zT)QBmc)A^`w}RPbEAE**+p2~56YZ#if=NXZamvv1JHf(|J=a~eke4DNCuWYusNd!A zh`k#Ri}-(uM@Ak7L|Yx;>K>N`@xL)S5iFm26q1#A=5{<=R?KiVP7BG~V^)Lx!Q6EYZoF|Ms zYnuOFgg|AM*L@vc`VIMOX&2QKy5f6rD4Da7_duE#BYUSwlBCY^xq>+NfzsD-FpU9CaVn z%{A&j43`@&HMd|lHttMUQK~44ji6Rheztw*Z6hbd-8b|-?pPrr{W`zmU0e%7Z-(5Q zE_g2S5%OM75_s{^=isD>zhSx{hLLTUUf@EWEQGT%{(7>H;o^fX|7sZ{I8H~|QgCP7 z(cAEHzM$%NNBX9T>AY2}^`CNjAwPibmqC>rgvs;HP=f8Bl|n>0INMoqxKazh4o4Mu zzA(aS%OA{g5-Jf^{T^fK>r9?d0y_u?aA?LJN8#Ezbyg$vI_3e-&qA zg9NwM0=;IcdTU%U46(Fss_4D_&#i&87Hg*ZQ(c0E@fQGR%g?p|4_5S*9guMaOe*2> zN_Z%)VAIN>@ArkYwO^0B{D)tMT1%29pmRoHo6RO_3T_ILqG8O&eqX)gvMvet^yt;9 z=bCL~tkPI{!-#Ym=wvvxK$C*HJ!aElPG}i)t<|jVed2|4f~tP=rYEQBvrWZSUCDb}cb!~VA1x!*V|+K(Tf(Loum*?)RWw3B8@GaOI_l*Rvx-;{hyfQL3=o`fw3;l-goV={2}r^psr{bz zSOytHbd@np8ky6hY35+nN=cS8)Ze{0YEwP~U-S{8wpMp~D zKb+&5pYi5vwY7ik+G{Y2wcUUI-ShwMDPyK`>@Fj=YZ`Jl${Ct2b^Vg2OJAo%YvmhE z>a6@G88%&OubD)tHd833H!!6geRsgZQFx4rc?bh~?0y1!9aL!e{I8xR!pAbcEUC@cXnP>K zXMf6Ue<;3sL=2qxMERSK514253JF48wRP`onR3-FShoo5Po03>lTtb+^1RS1`@)=? z&J?7YX4#Bm_aT_+9S46CG7;Z$ly=2W5!J5**=|n=Xft*}|C=^6|WpCaJ_kpB|`+xw5YJg1mjw*zq5UPcs{9_))3U@vCt z`|bEpCQAHaxK|}G4;bsk%qrkM^kM{rvZS5U)&V5mF_2`-@EfL+6TB>3&4cbRq;YCZxF~5x3uJ zOp8Huv|UQ?9Z_&=7Ne{YJ?1N(%0zqmiPlg716|9ULaEI^LKwzpnYXBiCA&oE4cDnl zbI`~P(QeQ14ob0|WibQ{9Lf5=gynshWrH~l*1c#jzfOzJjg6vqj1FYj8dK5kA!$mt z{PSR6+Lv&F?Oppn>ATdOtetQc@Y<_)uI!X2Z2IOkFAib1{@4}VhT*)eEJv~r#Ta*z>2nU^sI{=B z{g;5pn}C4`_8LJPE2aug#9&yw4?``g&m*dJq4H`Je2qqj`ye6{7I%LoT1SRmMnqgQAs&-( z$l1V+q+$tl{-NqJd}>z$8HoCgS{6qf@hiT`IgpoAUxF=1qiPaOoiKhzP`xCt4L$G^ zyF`Z2fej}$UrOagv9Cyn&hb>R$5a6#o^^W2Q~xllzVlK|H955s7UK>3M%X=rbigQT zu$m{Vr4eFHNZ%hFL>CDD_f3{SSSpB^kb9|_)Eqf`yNEl^8Zlagc zehIOo}S!A zR;?+~R}mNq^RC^Mc3SJ2-$=1BIOxs>+G`ha?p_ABo3BBIp$uw_?-~r(XzwL-(gwDo zt-9gU+ka^9Hw^AF)P0~eNprh{ z@>kIQMIhX;jicwx$E$+aPHU$-9h0f@-%FPNWW`3~)x7B84;$YJ@4_kXM|;uV7LkWP zARET|0E{a2*XZ`~oeS}{VZCA|ySaOEFz}W_ftWpNt5^yw>!^Cl;LlM;5$67-f?ipUgfVmn32ujq1w1NkY4x_1|n zWQ1iO{%}D)li*;-iN|7%Une{z9+OSKic44Z)!q(X@Cv1{+NNqq0p48? zxr?~{g8mD=N!$mb#C9)HSGyWsuSPc?9E1C!KM`q?$Hj(cs|JL1vlL_5s>5(as;qbg zs|q2Ncds)yKbB~8wsXRq(}Hz;!hO1OTQQg)ledqW!d?E|{9=eVi`@`#+xlwOvta_&FV!JZb*3q$>d|29v#p@k zR+4B#vqoBNNNEwRki&j%;^CxO%i0THi=`Oj9Y&iQtT0%pV`-$%^t7MQPU{ZMc#Wr4LSx4z+Ht(cnnCR#JZ?$i>N36u&Q zV&kdaiJdO+p746DC3l=i-k~;Ej7k4qI9}ZICP^i=p%q1TczaJTwI^r8d$hWgG2#p{+C9zJJPm^N6AG zP%c#UxDGk{AZ;avUNvLAJES4tkXbGz=84J#ir*GZi#`geV>~CBSJ)TLQd?i6zSJy4 z75I%yBzpy|^B@L(QuL4e>xyL|!-=(W)DREZC*n=R!!=LDzLp$;JCv9Bsd<4gSwhCL zPfo(a%YadcE?AWF4_!jM+B=iTnWXRAkd4yUtug5qMp#OLvYh#&zL z3rv@~ZnGup_d-$-pjoK*`vId6X}VK}^#w{8Ao3^Y5wziy{i8K-aY5QWASSm+D9Gt7 zDBv#|A}kgyCT(w1CMA#~?ku!9OAmGIR5Ps$eFbDCuIA6#BUB9?1E$fozP-s%I*aD} z3IyADOI??_;yJf-(un;adElD)5Q=}@fWKzBJt8Mgkycm)G5ruLWvf)K?8ibv;Dd_0it^~hI|uAIvsy@)btE02nV6}F||wGc|i zzfa!>{tXb^A_eUQkL}tPSnLO@OeLSb2%E^WQRotfR@AwC`4PmmkgRc>>0YV<9&A_o?$mqk|1VK89cBY`bp>H5xedJol4d_2%^Z|8Fo1S;+}Xezc1hb zC5Ih15r(kGseHQV5O?}cdVk$*llmyuzX&a?*dY1a(SYi23Y*Kfo?o}v@(nq2Yn$tE zaJEE?Ghd=q7bx~T6$Xf96*{qe8CX7ddu?;oHqK^6I+%~Q67ARkPYWP?X0mL>F_3Qj zal;w7E4ejFRfGmi{_Xb6d^C&^7uY;r3d3lK;b@;iZ*(v z9Xx^8wSI6p8{-P`g2j1lw)d9#foyBrjyXKh_A^irfJ(83(hXrC&G_YzfQyJ-Dml<5N})Q}=D~Po&q=f!yWYL@0*BO0-%wr@x{6NP zy%59}0-1i*_V7*!7T^0$Vs=I%N^kf47BJ`AY=CahdTm}z&hpAt=P$#4GC{?jvX0mc zl|+=*>8|@Sx`V15PloFLT`sBc3r>If`~(btbR0sgofD^9_pWai2=}Vt^`Ijw(9~Rn zK_fb#de|on(uXg<3RQMI(GgqGi)s`i$jbjK!m<(fwSLC8n|iF%h{nXViKPv<0ao|= zpF0>lWT-B9f>w_#dTnVq`3_2jvG0kIH+zV5ZY__8gz@E zQN0x7ZqX&z9TfTvy&QjogL=rcM2~MwzaJ%AeZQzqH%nQ}=V=Su{!|mXl#b;-MVIP(zK(`ZP0?#+5kfs*6gDMr)7x>ttE@oQeCHp8K)xs>nf#~c z!LpH#dmf!nH`1~1hx(LkO24LXqU!0>6d#s6D1{<*jRtDT49`YO4PkJI) zn8s9r1YgVB^j+Z=+^sF&3=IDT>NFF~(gbK-_P}Hvm~LS>GqwMI_4g(utwlM_?skmr zVg@zR;#=%WJI<$0w{InkLUVG?a9xWst)&L76=JP#rzP$V|CsiPf4vs|yHUIUVi&!o zKpZ+#ukWg-9fr!ii49OokG52=13TAJW9@qFv`$p6`ii$;X;4oiE&b*O>YM+0wSLlO zlUgTG!%1xwaf1p?&ud7dK0_n+#=GKJN)=79k!WgEM7ELx#W3c|Mp$I-6Nd*pb~W|$ zAM9fXmlc24PRWbI_&BN^)KQ+xrXeM^_uPmm5)y0^$vxXp^u#y{6?2YD;*j4~c!Kg# z4BQjVW+SShW)FzAOh@DMnJf=|eT&V`w@X2Xh43#=4T>iB+HFUpB$mtUDgY*cuQa<; z3!(%IaiEy9Sw9Mu{Baccvrot)h@DE>Ipj>+p|H9ktr$EWPsts3 zzk+oFrP(v;0uPV7Vai^Ai5KFA;ZZMNlR2c_Xs1IYhIZ@!@cV$@rmZ~)<}V#xMQfGH z><YMlW!k0s=kyRj z1EC`@VmEIuA)<>)NV%^ z+uDByh_kgt%@gq(UT+vC;KouTq>NPEpa^cTy7`Y6=JkqNT?W#`jMkuI5?>45igkvR8cf%5~mzULvGt^)d1 z5ne|}*{6vWc!kX5 z*oUM8LgDo5!`=60*u>CXj=NZ$?edZtL%|Y6bLVvNsfu* zOie%AiOW8BT!vz8GDJ>vkoy3=3x~heg7m{yH&v1pq3I1+U4(=DQ*KoYOC{Iv4yP*q zK#q02V4g)UO<^OY?8Qby<{YBRChT!^1l~uP&^O!fAFP$K@4fykxpvhw}L{jtO1;P_YL9%`>6L25Tpmn9f zC?8<ncwvc**Y#BUN1~pHU4#)xev6{f*Woc;cm%SoI3M0D zQ69gSK$CHphI|j_=VyEyi+cDcRM^n+lPEgaKY-90u4tKpvaMTNRch9FA z+voIyV~D&g zT(0m#HPRjkYvX!L8CM3U7lU}u6CmOc{%#V4YrgkA=74D!xxWs~Zwzq=1opm}i|;=% zQ~W;Qv9aFZd=Txj_YHAbO6^<@>ptI#(P5OFL#*#V40(x>L&uBRtzBOHaJCe9W|F`Jr7npK5%^P z1@=>y30FI=dG8f_7|-v$lgz}}-bJgIoF0}-#<(JYXvc%Su9~}SO@dp|5cMs#jqP!x zx92)2?p8U!j-9E@j)nrZ(fwY_Qy42iwVMR^!prZknrD29ybYmtn_O^f_*zS=L(exy zzqx+CaoMSiRW+73rqEolG1HjRu_825j92j%li&L*x?WU%_R+9wY^03!Gjb#Q5L6Yw zmNmDeHOp7?S8o|>WgAztfz_%h(yZ0y{~B7!nlk+r&E>V0v4Nz{f{rxA^?iuSld!cd za($z-u2rFx)YSvMc|~jE9vJS~aH$4g5_ijb4cJx2Sifd^nxhl0PD3M2{u-o`MtW(U z;Wtkzt-x?KL@_PV43d!!*E^-F3q|*vKoe8?SG< zLc-49+j*Bo`(=KBK+emMBO%;rF~wD#BR|FS9ys9{d8EhvO&~sK=6+u;!kYCv;MK+t70C5|cu$rUNnPB4)8GK_+V@ii%b4F6lR5fcM+Gzc0s0Tx3jp(l0wJh2oYHl>VDb;JjzT}K^d65!+ z4U6GQ4$KvV|6U3YIt%kDbrvs|<(n=-qNOksXE7N5=L24Tkc>|3`TrA4+Dm2f4$QrY z5hNF}>w)WOr7!GX3O{xzz=(P!>?Trbxe=|$cS!|HobS?zmlTdixhz?SPcWqOsv^i0 zE_UaRC5#1zeimTJN&yB^l*d!cC8R?tKVUze)aXjk^i%(qiZAetY1$Jo3AF- z`|-fU@G#OPzJ`O{x=XJG6J9bkMar2gr&p4L8audiG zc*}i#{4vv%ONgx+gomY@Paw>a3y;ag8c7p;)47;z!b=6Tjh6V(XMd$2+lOTFAAf{_ z`u;!KzB?ewqxt_g`#ksD-E)-=M^{7;6*Yn8Y*_)7~`mCXpsZ5i5#_ zC}06aQ4yu7D1x8@iWEB_V#Nv;u>9sZIJoj0V)B0dLw09(pLVk|yR$Q&2__fHp*jRU zWN^=n?#kbMgg|4J)Te<|#qU!N%srqivATd?uaqH|lqQ zlq!>A)2MB^aFQOJa;(bi1CrmKmH7)7yD+Q$7-jV zd@l9;;TeSrk5k_`UQ*n7D-K5a9)g;K;8cld(dBl@g4+;3leUil12ljD(B!sTuoh8+3{&;_N!x9)e4> zm1g|61N?tDun*nf6~_ii&reJHbGy`6f+w5-$heHxc3EfF)PxkusmV`feYR!s3+ByZ9rD z>!XaZB|*L1%nn$JUQT1^g;E@LHW}~+cTMQvg?NuJ zPcpy001(eF%t73MQ=fg!4FUfz$xp?#wqAo?ReP<$AH~(ifHs<1R}=rH6YFDS+o+kL zy88isHH1`M_0v+*(LK>&ydv8TEc8y{ayk@SPSLA7H$N&--1kH(T0jVn0q)#|V3r*cbH>1zm&(lymoW zRbX}o;ugc%ICzs=Rh@ySy@uYaMvNe>)Vr3RbuY8L+I_|f%47d66!De+kqf#deL!32fz;|g;5=wUgn!pxaygr z5TZC1nYevqcbMGkC&D%IQpyYkWvQg;JFGU}F$ynJDZNY|>gVuozL^M7ZMQDOFZ0b6 zPU!sWpmcrD6un>W)hwguL)+wi^4eEvV?Y~Ct>UQ@=iH^&lf7l+^g%Pnbj414YkK0T zMGUpKpd+qyDuyzvEsCBz`@eBO)qB~0SZ(j8Y3Zl)S#&FHeG=U^{STC{wd6lQsnRKS zKUF6_u?XTOI3~?o+mGD+ZlgdN5sx1Iu8U!aL0kq&51 z@L2w)Yd|IlR*xa&=&HQIM7nCYV%ZZQ>!zRqUHZs`1V}v|kSJexa1JsGAl~`9sf4>P z6S^L*iW5d=iOA)>uzMD{C)*VV@!yb(Vnvyfl#dFSv0p(}d(QS5i}v+uYJb=B?j7!( zfaA^<8$_;OtdOtmpfZm#BOZ!_A1{8FRGfioD+E{BIg^QS&t@)meOr1PB7H~$A>46~d#WjK$+-^VX;g#F>wgb8}a1WFx>q{^X(H@Cwuu%Pk=mV15x5lX?6y8P~5ZC4H z&RN~6Cy&bQTeSy{i&ik?;sPl8Ex7m6T3FkMRE;9{Tq795(HBzR*9zX*> zp(h&jsv0S$OV+KmnfykttLA!50d2gEyhPn?Ku4EOy1jI3&1(W)V)KPs+Ax23i@|0U zrCLs5On3rDDBT5!x ze`lzi@fYqr3vwblOJl#aPR~Vz-qB4NV!;jBHj2B#e%RVscmz^S8{yu3hR!AVwqz4U zUl-q-?@K?Nn|KJ7^VZG@(#)}D^m9CgO89OG?c9BhDQ8Y8LtR?t5lhu$l7chv3}Vlq zg4_?`@_O$c@#A)gxtA3wcPsp+?ihdhZ84(3+bZxye@ubLA1ySe!{x;73P!n~ly#J? zX2Q8+`M7RjtjNDdV=>5_Gsg~5-*G&Bi4w}LzO$0%z8!tbe4xCDQQ|t|Ec#;DuSciu zNN;~C`j@A45; z9A=*3F|t2V;o|v){&KM8hcyfl*K5+N8U^t~RyF9_0zsqOqFdu%n(Fk?sc7$-jW*~b z(L@>9Kxn47p6)iHrdrgCx}d98_|r``D-=CVqFd!LUupXj@NFM7w3bbSx}djXXsWiL z5b+e6v1O!E&cnO~2v?IAq>k~rp)k$1BN>p>CyI*lC61NvC*qt~38>E`8N;og+Y$D@2<(!Uw)l#o#rl%cGDtnXZCzoNSv~yj`U0vCI z*4#BtH6SYU5jgGmCnMRR%ox6lC3oeLK)CSVhu7iIq#bWPvGw2r$@*4cJotAz^D%iP zIxgl4f~d%A;5%*P!r|7JP}@{fQP*M1uWFi0T$d_Iw;KxS+mb?)`7vhr_|r0mCt3fv z4kEk#71t%2R8f;h`IyFav0=k-*6nlUuzh#D6?_O5mqB0d-oBgg8=WV`4%2+QoHpN1 zb@KUBu;-WfZ&2AuGP+zP(na7=_wA9(;E1X=Gy$z_k~4yJf;9O8fe+25gnAMnsx+H0b`gKG}~aa zzpH9>J*@UBUIn+NlO=s7`=OmU5F=zU|0&yhem!K)$oj&vN+I>QBs^&|JMWJV(Zjcz z3bR>mT}V14yhr*Vx?v0+=&C#;sFP| zuZY$-ciPg;$A<`#WW*ZR!_O?;4+Vgrz8*u9FzLj1Z&b;i3+}-^S`AQj(Lz;39dUjB zRdDA+PVu&PHiC^bZ!Ytgxiji9C0*0OJ!~07xfCE=3ks?YLhN2LMNY0h#`Dv%Pm2^j zaGQCt0Wq}qP?_Dfy56CZskP`sP5R&!3Ne0H`dk>c9E!t1YI@?o33yPLxD1lq5_f~C zWV>h{Y3OiX5xWyN1GfY>&F8NljHk1X-wwcI$xEJR2QIaJ@ms7HSwGj<;=>mIEVxX)|5UWCXVMXH zMCk3!bb`?92)qO`3? z9UNcP-sIZfX0J9IwMvQSh?s;JaTRnph=T zMV8wuWsMU>_0sA-3x^5mMCxD3a3z@sjno_V=Y_%o2)EArsQ$O7 z(pB%Tq@N2E_h=HWkX>MMD6;~{6pC&OgYjPXgxELq*E@3Jhe?4%xwGoLiPA)HgRl<#NZ-@?y#TS^{{_qA<0NnT;qjNj^P8QQS6gfwgkxSz9LQ+Nw zMP~bJLH;HkFI1j{Lp$>t}#8nx`*16yq(>+FhQeTjWlqHy?C)I zG*eVhe;ZWW&^203JbI>;#IIlssckA5?M59Xqf=?oG0lZ@lYhK?t64t}9_C}gUPsh1 zBWw1@psEA*zQGqTBxt?(D~hcjfxl$<5wt1V@BDAeI-g0m@-JqP5w2^pFX&8~_V#>L zzgM;EM1ytf?x~NZpcRE^G;~mVD`@jCL3&@oAX?4*9AZe-<`BN}zvUDo<8^sPyT+Di z?eg*{RSt!yqIKf!JvWSXTsCFNQ4nN6@8itrVU1MDVDfn422qk-3UN&#$1T^&@63uu zJLC~CUG^qMK`=h_Bg>rv)2fl=q>>xpzEHKf94`ElKkSA~5oziZ3yLH0Ff!Y%(xKuQ z2$uMO7ov@!c8mk>E= z(`JYy7WOsf(h!AWJLZ)Px``H6>nCWFoxSC)y{q8wW6GDfYg)7bTNb_G5FAEK(~Rwq8VjfB`sL_ORxPlJ`;Pf-N9Q9` zW*0M`b*Sew>mnYfGL4jc^*nra4-MYct9rKG-gc|DiZ=*7H3;2r<60OT-(sqap;XZn z;@=$;?1THCrpQ(oLVPUjQLKW+mWccDOyI!%^5Q(peQ-|{Cty}}beMT-#2p~#DXW=J zlu5`V9B>LzmT?soyq z!aM@H;Zjq=-EEQlUcb9*0mFQ<$x>=z$Fxl#M8+0{zp-I4Ab=$IkVEV7cFG)+N_2ob zgGmp#aTsn@PP#9Xn*@Kjn#tMWs@@~6^2$!O877(5O9r4N;;FF*`(>B(YG->!wuQc2paP+ zkp4&BzsLnhA?u!??@Z0~cp=f|-VIW-G@(_wDDPuN7Q`Hr4y}ca5{`Sb4&DhI(X9G^ z8#2I~&62aXT911xi0UF?&ygGkb7`*d9w@KMf;*m3PNJ(;edrh}JqMOg+fQx7AcK<* zXJnWEDn<)Zo^>fCic`Cs{8+HC!|Lt=CYR0oJcUZ6MU!Hv%^jYKuUg;1?P87KnMKdr zcvu<-OtgxKR(RipQ|MykHiQmvUTbsf0PMsRUlgqQ{^ZQ+iLy6>ObXsS={Zugb;285 zH*Tmy`Da%?t(3Ivfj!40L@7V#m>Ls4_x zLt%7AC=||)h=ICq+4l+7 z(l4)~6)G7$Mwj+nABd*2RSu8tf@6pX!$^Z!?k%i4EFEMKMO{$%eo!oon>koZc1Z^$ z*TXnz)dDh(6J11-496L!;EhMax1$B4N}6e!Pg^_rQV0w`_BNbBh^8%bQe1LBXc(Mg zc5l`x+gY|xB(uhPMbh!1P`)4J7euOIB%kDr3lXf@0%vMaEZ5%SWH{%`VPO8!2sopY z#7Kh?GJoRYrlVA2Lz*Twe=9$=MTV%u*7^Iyh-Z1vYu2eR&vqKl-jcKH=$2*CGFr8S zW*5=Eo{uobZmBHo2%M{e*iepSOVK8k*oXRoN)Oc4FOPztgRlmjZT?Z zh%1Y+<7>z!L3S2`s`f=yKvJlhRR zMhpsJ9&~IRLbCjz%nxJ{7_xT|WG_;oW3PP@#WKmn9i8f?HuekXxIYRkBR)!)bk!qH z;QLGVC1kx6n=RggwNtNn#Y`<$k{j#c^rRwae(`E$(zKnz6R}V(^`AgWJl)8>p*{nL zU|7x0pVkoK!)yQdWrDxls;_nmmpeWDT|iP&;hsmvAqa6v!2|AU+q;+W=efQV@QxuP zs~l4%R)&IQ2(x$@T=P&Yl|Apl)qy<=JSs68R9V0tc%^;V$Y!3OIdvIW%YW>Z-P`EE z*QB9wo=v>AwS`25smvD;9<}!GNcxJfoppYFX-IvA230p3Q=@#56zoMXqWCoo@DD#n zJKtN=&G1e2C~dqr-}`D73Tz>1J>EV-REG$gNmiMplQkV?RgP((CApYtE9H9?8~nD# zvz-tlx@yOiyXIK*CEF>lg#t{_G2dg08oJcZvDy z(5U^^v$uFv%)2txg*zZ0|66Sj!VKsgsdBw%@xFN;X0cPvX8pT{?1X4LqGFw%K+FaZ zEX14(v&-MgbX-V?HCxrQ(~2+8<$%-dy$@lT>;$Yu2liyM{p2JX9wdc!q-rkNL7(rl zL|7V!DCD6*5LE`1y$nDV$&$W-*)HEzIa@x}g+1CNG0Uqnt49i_NpOuxTdwt8|8G&{;$^ixEdr zS8<=RJ^>zxa$VQG)2&#%u&>&0)IgnTrjusg8Ribr*xOqdv3z;v3`~Fj@Ph3f)}DSw z`|vbGNqUSs*{pp7EakV^dMW={pS~&MjnRhYIObM$Wvr($`aju+8M#&~xwWJlsQGVF zck8l=R?R4j7&I8;89vT1M&soaEn?eDgS?~1DlJv^n3gR&gHmBkbPAPXa}o0(V(m{=94Xh3cLD^!ffiS z-%GedI@OVTOW^VvknbHIKcN}9TC!)JWb2>%fn0(d`^0sCILHDry_7InKdtk+(LWm< znJx*L$WUSC6{Ynu+q2@UWT%2??@d5Sc$aHT098y#Bb0oy}gk#~?TRrk3@D^-1 zkNH|77mdx0mWjyUfuCx)@@%_b+pVn@>o)rtX>2ANX;mN0vFK*2iINVaRQ?p}LtPR3 zQ5XyDhoDXQ7AEvwAj6f3X5x6b4<+=$EU5M-KPP>?<2^eh-lcueT985J<`B{!0jsOW z&b{s?TL00@AjFfRFI)!~;Rm4@iqZt4l|Nrda||B(3kla^@e$~UdqP(4wu2JW_v9x{ z55q+pX1kdK-Nfa=4t{7^z=-Q3+3>MRxQ44E&@{bMHRN4!&jUhy!ehWZ77`{09$O#8 zL!a?aVIM{4O!_0wCxVM0j`kMQ3!vaX5d`xhO~Ra151|;{f}8H(hX`gE^->HYxqJ#Ull+2*o1bS(a*k1Wk14sFo(AM6E8`m{>4^c-4V=^J8U%gsgf!-epb z$(B7(?k0CbAD_1uy`hh4f@k;qcC#y>ssLPF8~)Rh$ZZu3gtCTUOgMA&R$;6qX?cr! z;+Nm{#Uq4u{qbV7L~%H!T0yZ(sjYC3>Xth#WZ=_WIAu+K+HM-GDiK;pR&RrlvpDGw zk?`4;rD4!5Z2>&r4uVtG+rG^b{&5u2!{N|xM<^S~^Y8waWM6}r42UU%fLLf6*;%q` zcD&@)p9>w%3<+Hg8BD!it9qj`q8O_1^XYuxsAZ256816r=yN*My@=M z=Qrf}U}<3rxHO<=4hN@Yuwd7~sTdJLM#!(>G|<)+^xE{QZMyYn_0=lsg52mW_4NB` z)p^jhe^C|-WXGvc`J2df_w5l^k&&HB0*n*Ple{NCWqn44$1;TsIRc7I^IP!TnxQKK z;Vg^IB!rw6&_O1efrl}b!d+^hJl1TnP_%$*w9GdZ(vL91vYHeBd)bQk552Bt)mu=$ z57f*eQYHb8YvI_;&A?&z

    AzYwAaC#4lu88ML{~9*`a{Qa|F-6J>I2GmQSIokN4OU(`WuZ@VPKk zzFY?cIjjv$326j+)S%jUU!)IVT*e~fp*Zn!-hABS7E zZ7l}b{xgg37=4Vf=zKvDI74`iX8oKP}LIAbUxP@Z<8m|v( z)L??%a$B7mhD^yF?-$^tHnmKi)B@a2;Oleb5 zQ;G+9*62dL=z}o_G>-~eIh|S2!Yokxnbm*PhP1|SYv`P$PH#Lj=_IrOhcT;*F|4FC z0u8gM*K3p!s_s)!R6rIS5kBl&Qw)kzGw+U(p>P%U%dh8oUAI5pyJ8d+b!; z^OON&Os;WTrDQm_$~pDdBCp%OO8QSQd1#G_L8R<5K15AISz;)Awbc@#bGdxyK{)TV zq!oh53%hpmXOjBLc131NzjAowhk=J50=?cb+LlcHX;d(H<+rm(L+dpb zhs7>eWG-~P9WLyt(wk;=YoMUl4-9=C7p|y%j+x7~pC0}CB6-=8=Hi$VHC zk~C5tEvs(#hDFS(somqnh(jS=76^A;3cA+TpEvtdJ6TB8sdSdz`-?S`TtXO-NsFn zrj&BKXV)}M>lKt@8HKSw?vl?#Nb4Agq{MqI#L^qmDBGRJF}A{E!%9)Vy|yh84Y>^B z0Nv%b zQIKuay^w68L{U&^l=fA3TjQm}EE`%-u*zl0_vw`AIzebF+g!XjrNfYFY8&aBj(a5E z@qL1wptufuNf~LX)=dr!@me~JQC8W8lTrG)UG+cvsRthP^FCHpEI5Ii(zlaYAZ+3UrEmYg@-zBMexYaxm@VTaAK+}G&OtC1&Z#;!YPznqZ z9?CE1ysnN_f=r5-{I!1(oS(E3O-CMlYO*j0$j&r)l8LCOT(E1tkVyI`Gk>k$2NqQ} zDD^6HU9bi$JT9z<;$#T+AY7;jBLXIvR7eoD@T7oy=I~h1|A;`jte-@IL;o(77#FD6 z<{GT?KKi4@#t1$RL@0~;uMuHF$J>;7GPhSyGF)Q5=PU>UDc9>ICTb0|BZBzx_OlN*XK(2lsQLXOQ{=a=Zt*n_a`OrM!E&e%wseVpgA$ z@0He?vCTM^=uS>0TE7%<@FyZ#o{1+6e=ZC6~&s@PMwnDLMS82IxOFX=p`pglU*+Y@3)KL&4>#SU^NXb zs#~L(^+G9vX#F`_wp%C;bVF3EgwCSWej5cCQ_8!xuQZiAZR|@#&#ljS+8?r5CXQq5 zrT8kL5DO@ii-#!5G_V^#clVHuu6J8m23xH(UReq3w~NJ+=&EZOs;$@i5m`0R%H~dq zoj2NwN74QQCWR(P33KZs(c+J{O+uJ3`n2(7XCYlg;!OA#;BDrrzE479|A+Do)9UGH z{LSmLa?s7b3Zg&fx3Ks0F4sK0qsU5!VShjkjdM_gO+(B5IyYaX`BH<{8EVb`x0u)s zYguQNsuT3=GR=_;tYa0~@Osv>ew!z$t?mzSIM-og>ffwC-P~szE>nGZGk*;a3SZLv zC>Shl8XoqL+U~oZQXoEi28C7}4ZQ2dAxK{Xy>78V!%3d4{E{!Yu?xwz4ES`rqwi0L z#+=}|W7JLIwZ~v_oUA7%Tk#t7P>^ATpz7vkj|8r&e&peT%j+%sRU*^HazA$AiG+Z3i+oov>x5Nz}DA(@??1SLBg> zr?6QRS0b*ChDBeLFMtLH7sQ+&anSxpWk(VdGq;71A@cq9YAje}bwW|o+M0Lc|a2M4UgS>EfpkOLKs+-`NDnolc=BXcAyipnsZ~79b&Mo944AB z`e?ss@g%ZN$oPI5^ozrw!OZPHYTtmx*t5hYlD07mni@;E5TX3YOL&G3Ns?avZiipP z))^UIT$0Zof8bVFgvfdG?*#H&9JRiC!2F`mQ)y&?A08{afr+TG43*%b>7v(cp{|K+ z3)Kx58!pj%+th%>`Z~t1&8>!QIphh9WUtHFn5J9eVoDKCu_4q=ld7E{zSo_RRydue zNv6*m)<{}Cp$73;wwf?_6^y%&cbu!^pAMTSmX-_iT{p{?6Z)w{xZxjV<<8@R1ZxF7 zj4wNiea%m;1v(JVigyKJ(i0HDoaKb{n=?>p<*yhm%VJl~^_?Ynu@Yr0XO6(=-nQ35 z;bUGTa#XP56XDe_Yx6;HBO7Y%$cAOGXZ%4j()O@8)>L%jt6k7KQ!HK@2sf?leE~r{ z>GLR1n;PIjL9W$d>s%1~Q@301 z`&cHTANMAF!H!<^4Ui`hVGbZ(EqcOOHkWexj3S$x+|*=8n7Ges&X+dEW2;Gq_3wNR{I8P-|1 zl{SSxaO<@Vc?yb39%jP8KmFU1ifVWZYWZ5&><(mbi_s`(+HbT|>c?$8>HpQvyMC}# zqoC(+GXtY4YG!j*D{6L>K6Inj9xe52bp2?tWorBPHA-=XV)LnPOV7d9c^iSb4Of4* zI`~`4N(}fWmZaKE-|*9s0U2Jd`%%GSvkb1DL$s9H1wyQz{?Wn_0?*AxXtEjsS6b)) z`nB1M2g(Yc{8j!E&>r<7sjdNmO%#RM6aLUU`yxEu0C!Kry-A2eouS*FK(9bHx z`Dp81lMk3k3fitUy-u#eO4p1KuxY9hB{O;W7GBY=B@vMG$B|o5_v?8Sp8b6G6r7)V z$8_UzP8LP!>v+*3=TE^~o5PpIK19OCQHb$QrLr(!Xm2 zW2$FgjfUObd)sKgd6di#>c6n*lTes_jiBXA)el*HWl4jQZ-IclmJSz9u&3+f@H(h+ z!o8&9v!8}l3r25n$%RBPzcQuN`TT2ma1hQQ{xUC8)6sRPn1VJJX;97@AUBA+nkj9j zsGf^9TcO#)#xsNAT82xMXMq8MeiI%xZ1+L7ytu?TlXp;5tO&yYoTQ>ex2f!6 zcIaBuo!8;@cdZV7W}cb_wZ<#)2>!;TP|>{J;91;9ZfsQ~vXbICFE7U*`0ZWkc% zuYoIVKdV3&pB?T+L#t@X3!^C)=8y|LYW@lt_;eri>yQOo=e`l&f+}EIJM8lCH+(%MjcC#WMhQM;Uu}SeWGfsObz<)%!>L;M zIBqFq7C-^Kbrd|1rv;PSZs{NM->)tG(BZmYQtC?NaTP`8VYKmp)VU-%TWeLp^^>AGyZ!MCdM4bh zF!jX~NWQpG=qGSWW<}4XWR!i4C0S%9;^(|w7+j*tEJw@y1YUDqihYAXbV_&<(TQ(9 z!U*8}bSIHeV`+a1VM|0uoU_rOHwmtzabulDkFBr)HslKTwotTheneP0s(mBo_MJTMzYhdZ=b{zowbY zPh*W&3tJE0_njHo?*YvNyREzn;>X4*P~n9dPOnSA%X<^qBkSikZ2rl)!@pwdWV{AR zC4!Hn6pJV}jQVF_`ygwvv^WNSxIr;=F}8e*Xl}_=3PSwxvCB{-UNyZ!x@+#*9O$?5 z$D(CGFmg_JvXa3|a(mkl-tB=8!b*=v7v@@lbUQiA{}z=@xFGtE0Bc7&MqULAuv@$R zuEK-Tat2XmuE4-kFz2O&p%=J|j84J6cu2zNL+*PO;?{hSo#6gtA4R$^v^WSw;=YJn zvks-+)#xj-z@2}+KyW&TO4M8vgw)ATfe7mREf3JhfWb#C5KSG>wtM@9gZ3sPT6>|m zug@KP!M5L>LIJ+`l!Y{h$~D=F9T){trrU4=*xiRmoN?7k^lB6P8XM6O_84R}9d?-U zz&nd&!k6YGn4xzuFIi!0ve=T2LkZjCZXvCm4Tb9x_!@F5jRp{3;s)nKIKx$}WG}1L@ zh^BZ$V_MW_RP&+Xufb`dehh8ShE4ypP@f9T%mj=C-FWN@rW64b>r18dJYZs#PCPHP zy@U{M9r-{C)h2jZ%^x1=(q}0-_Y;#ee^ON#Fssl3(_@`&_PEkqC}#r?1F>QgsJY82 z&uw2H!f?w<;1r~sg|E+Df}7~5aE;!83v?MAiiSeQYK3?ZTOce`oY@O#z11y2vPiFj6>GeX30W#F8i|`sOl#?FbqQ9#zn)aZqWlsue7f6x8v&BmHa50rIB9I z1ICeF8=Wt7^?X9kx4zJ(AO-|ogCw|R^(thZKsq^B0mJ;D+NcnjSET$lPTaAm?Ktgj(Yvau6n8TyTpJ%gD+HCLgS~_dZ5x9RAgl-9z zl}2~P7*~?-yGyP^ml~qj@r_{Zz+Jt_Hiw0f>ySt9_-3^~^!>C;0Xl4B*)sCrdsH9r zJ(|?WFaG4X_n(??Wr|gjx*GH>H`^gt5(2g8C!Xpm_xo=MB2MpS z%5h~cB_%eiz9%(`o%S)Y(0f*|a=F`d{)L`&7*1|j?ht4c$I^U-h@U9P>$*dbPIfrs zM~C%T-OeK?3)0N3vGd>odOvG{>y1gr{qS(CsFKzY;;i;^VNj!3=nzlwFLTiCZD+!5 zJcbyECjq_qwcjQ~>OVRR7GqEkkjJEfTf=klHxTfT-Ngqt?B>@$e-oa(^6Q|KMY|ew zoi|_n&&%y-DL-kQ4!YO{^`3y|RodP~%Z zYDrx^pxWI^e?1lrj8&)ba7qzEu|ZVzS{QPom2|^Gs9gy@!BVo?QW}lZOMNO|I|Z9d zp%qSMFOT^#5m0E z>=(aaN_+zP`BRwK%Ly;RdYSPd;a;@Y&7v{%urLa4TU|q|PFkv*vY|&&{S}9@Y<#Vo z1%OUy4<>JS&tgpv(Jv$57492Dj-qu-4W*o>XL_H=fCG3_;=><#@M z6Fl3a0dX**TS_hgB1IM%^Su{%YQ6nxD$67c4hZl$hlCA}P|i&{X3Cdk76u;hcwVc6 zT;l|d=_o5_8XmHBY`Gn3+uuUCC4R}km|lq;zP)At$4JyR(nZF?GjnDA%AfEv^L-u0 z^#8t1ARf(^H(#pM-?dz&nHu_v^wAnko134xtiJ&PscP*uojm!Jf&HnTti3O3=MC&l zO=PVtNiD0s9W|!a_N8WaAdMnD+Yyq&|Jq^|*Knz}BI2LfRmuokIwB(sc#oHr*IN8c zi-W0+cWVzmuFSjsI<2)TYGn;vNi@zQI=2u5eHb7cP>g8SW+R#yT_C0KqcG^qC1P~S zLMVMOGzElZ2x3Z|gy>cx{y)>pM*xK(!nofb{%18=BqPFH=VQeX>}k2M<5ny5z!0wW zjObX^DbpynyXfpgh_%Gq`=)lk+pZsE@BLW3eX?+8D}vT>rNU^zR~YS_(D6NA3csp{ zjrYD$2(nEOZX}x5uLkJnpYZOZPWz~F2fdB65mf&!JQm^2J%JBc=Y!~N(v^{Lv$w$C z`1f(PmrTikqg~2wfukiIBfX1a2fpzE4D8N_xuF+`F#P*{v2cSI_Dx@Y0+=$T7f&Wk z0pjL%4yErBY)@s$L($0=aqOK6x>0~=dOd&llJ2FqlGeH6Tvi+mBYs_MNf>F6Ng?um zj1Vx64iaLlVLm9%_{XNBWxJ%^BW&(?86!a4v(|iM_`?=_%`Cc-L3zj;IMr#JVDaRA zP{#6+jd>qn;|Ip1Z8)Ltkf&(7_#8zrAK?0^c3RfJAZho`!PYb<1+D=dLgUVDxI}Zl z8mecAZlzhEmP3}WSL>r%z?w|0rChU2PrXo!;WaTft&|#LYbn{t>@*WUp~McPty+B4t%QDGe1Qapdg zXY$7!ItHt)l3Hh4;97p32KO$K8=}p$A(g|&O zMtwGPFt>L&0)F1161a8rYs-~Ay7HJy>miRQ_Mm2A@NqQpzxH{pfk~fK=7ZC&m8wxg5RIt%R zMGzD#Hmq2|hGIi0_TM}QhgY5hH1Fr^{%4%+AivPC={)Cp-EH>3ypQ-skvI z)AJEtI!f}4-0mptSi(xDgh?felfOQna36QftIL3k>bkXS4pRMApu^nM^aZNlN;TT+ z6JO5d=ttw|z^=sQ04;qCC?sT`#ptc8%5lk8NbueOk1d#I&LW&+H&{IwzWwdNbGl)I zw+{j^g-+Yq3`r+rdk3m8YmLhk$rN8M6BCSktf0<9a9HUol2k9+OdWGav1st=pLibB90F4GGBd|;@S&oV|3>RD3 znof1%=nYn`4oCaE#!18Ep3%+S!{Eg+7UamAr^ zMD6AcFf9E~0z3&*)98$tOo(|- zE;<#4z-CNG1B&Uq2oSy5Few^Lk1#`Mi{Ee?u#(a&qxb}Bp(}sqx0|wlx0$6ZiYwMN zR7wlNuX6c_@SA#FRa_o7m0zNC4|s|;@a5;wS1puU05`o-NnMiI5twAU6-HE#qiUG! z?`%;d5N-%`J^W!g@n(0!({8c06Dim$dH7|86=J6W20kwuy~)c1kLjMN@GK%L16=F1LJt!5om~bcneCVeIT7Tig@TA7I91r?~?*P(hC4J!Hw` z*jBjeaGLXwt#*x{&XDfkl+ddLf4FzO?|=NX6NZq#2b+4liRt4G)8ViL=1s?!bQa`} zQy8t?eK{$BlKz)nbFX2T1k3}MOC_OIMQc{J)HC`#xTV2vcrl8Nj{uLo-Q~+WZCnXU zZvlfJGw(h7*4sTUkZ-DY!ATa;_IwPyRp)o;zp zndx8gF)DPIFx3mzf~E@i$|lay#tfv96GlBtGwIQA1Zm)T4JlQ{amAs{)%`SSo$bg6 zzdSrYA(~pkSCJNl)FEa$Mney07Q$oCL-VQbZ?1~2Z@VQiqPqWjY~W&uH2J|85w zAO`hZ&eOXm;hu$**#c1}2db88ia68^hY-(jWe$|Iq3_KC&KHl*c|69Z%N6g?T#M>vnVz&@pXK=Ky#53I+xf5bfy4?_WbX z01w9WQxG0>l&H(Y1<&d-4L8AdW-ApuHx+AmbW*ZtzF`K|J-^@puIXmkXt-*e5 zQZd<%M7AJ9kkQ$=(W>Eoq^mV-I%W#udyyg3WINIW{3}~YlL^rR45z?stt?;i5HesE<0Ns#Y;1 zD&ir_q0iJp((r$-S>bMa4YQG)o%E*g>V)Ry<0caG?OocvVPZY}YB!=8l0v|(AK#6oa=ukJP*Dzc!& z;zBWyg~Nw9@D)}z_220jc}NJ;uKo0NXrl1-_`)w%)Nj%^j&7R6FI95!FZkoDm&xLaUGsx2K|c3p;?-#=w{(GvFN9LOZg9i$Ej+*qw zr{2c1YG_}{0XQV51zhCh6eJC#*Cl|<0GAG!TO*x(`(PH(`>prUJ)QOyzhpYOqL zJINf%_2oCZ1n}hBh0sCBkV5EkDMzoQCGFC<4%?`7dqpN?cLr&4@r)uait)K7-`)Sw zC(5*`ag&u~(BL1B(GS6A9z+F_`&%H}ZFMMg4z^PHIY!yn#=;+~M#l#csQ#b&||#jizZCi7+}PSbyZ;p z7q=$U(TwR7G!r5^QzBDj{+OwqDl@a91Dl@`p~ZL>jO)5-(Er6_-pfsDE~Sa0_z3DU zS%UiGG#FMgy2z1qZDZ}xCliD3qFOfg9$e%r-SR%J^w|7KtAv)LF(&{!mzCGCxNH*I zcJD}=V{aVroGy}Mx>vk8xExToqqwQ{(yrJ}9faSoHFdk6HEEp)*Y zBq^#})LfZ&0u}Kd1q)qp?~Gz>4EUYw&$)~qrwel)ZOZI+4yxS-{YqDZY%AKJ92<0t zRh_nx-&JX>mpNcEgjX>$YwEtWi7PA-o+1L$spcAK()rVBkCe^AYZA!}0@yq(%&*_F z%eOpvm(NuRVwcX`WCf*JN?~rd)8p;58rl@9Mm9K2MW0hjpQ-N^!#d}u6e*8_aaXQF zRvX-jYeLa#2=~PLHdkLL+zQXsCndx}O#-=l*FdS*NK+v6In7(tqm4Z&8xSEowH;ko z&`w*r9c?EsSnh#<)8O3k$0x2nMX+!}^v+PVOQU+CS}BVB>UD`9{^WBjkkg=Gir zNrKXjMTB0N07n+!hK+%e)qyta@Ywx)SjUAln%Z|EHRJsp13z8b5<>vq;$7_aa%U;U z)^{X{OW->DuLHFhTPW7&qA^1;cIMzly4^<58fnr}epBsrM_zF(1L*6g12D5|neI}e zTa+oVVIS8k=r$$O-^RBoy;kELs=ltjLFt$O7ZF>UJu_yd9MjopbW=B-9z%%UbTm>F zpDF#xk?>i1TlsoQgE>)FQ7Hk$Ms`$&sl8GNzuxm3fuX3LQmP{RSs;B@c7FUNs*d4Urojsivvg0|M$T5j-5fYVVyVQh-r}(W_u1Pr@vEqhZOL42=5cm!&sGQC!CPvp2(qaPY<8<1Y--qbBM(kYW7DT(6S^-%Pqa}8hrhP z&7V{TLz}uMl=c;JA#LfsZ~8aURKuXVT+9DZtF%LtHZp(f0yx!M{%qWn3xh)tNWQ zJZ`XfV>;h6+PVQx}ZC{@G`;V@>@Z;>qJWT^LZnx$#HbPJ>6m8 zEs8zPCC!DQVfHgO?1dVeHh%OSSKJRU50r_Gt&Z6l5xh~gZu#m1^~?J94q|XIm|!n0 z1{Z^g*;d!t`r_HMlU|m{m)UG}t6P6K{j%`NV#Yf!>hd#VY-3cyhA5<0WO#X%1qUd> zM8~3QhbRhFvAs9Y9XirsHfV2(#`s4R^agftCOGV}Vb9oW={AGX29CUqrZIFOHIE74 zThg_X>IE=1oLjxEon*I@_Jsp-?wC^7s&xl$7Y9qx251L8 zIKoj#q)exb;pmf;1XZJgA&>ScHzeEH4$?x7&f#2hq}%~Z>C%5F_>Wp~+v_AXC=Q{~SBE+6@thZfN58ag%i%g_HN95y{B1Ib?R0@s_rJi94gna%dWGC# z9@J54m8VoPsB6-4_?V@WRR`_RDklU@iqJMG8KPPweutjZ9aGc`N5-M@xtO@DlEuje zJ%Iz3F|dk6p_=yvNs`_n#8vSjk(;Hv@fLsFV#y+=2rUt2d&&J~rpLiQ?J?|8!XJseoR!T_#tas| z7Bs0zAFop}?zc{Va@9kzbz7G8!S zpC4KBk0uvThwTGFAdbJdtqvAR;;58bs~jw!7$sKe^E(!92qle79g$+h>C z((+j#NeKbh^MJAa>2R4qb>yvIJIwyH(-x^7-7189T-*kY)%Y-SPR7=%pAj;c4W={-DfdiKK{hx`o?_@OIJ6 zo*|cSL7@7~a!CKZfGV@l66>qFa_O|V2C7=|o!XEV^mNQ-8q9#3AlN$6`!{`&Z2R>e zAf$;NYBY>+PbhkR)GcQ-+fcK>rbmc6h~D<3Uwp;gsUH1xb|k$%hMQBDzHZ8I>o@e& zj9t}yxl%CtF%dZZiRK0HPhXGO8T|?LPL)RL0l^cjdC2Cl%)wvjm}zDZG3V8ND*Do5 z;F4h-<;USFb*UhCC!Ca=ZadeC!`vb%1Bl>`)xUP!puvZ2xIivhLNMCu16l}$et`bq z6q5sY*n~vbDwRkRt?y2_gWJ$GThyS_Tn*j(koP9Rmb?bk1*9HLtvEhlC2d77cmv&< z-cYTwlxW?gYFlyhM=2$zT3XG;6Bt@uAE2ifQkSgk=w~vCR^Z%AEj*H@ss2Ofn`!2+*)$JoR>vlg8fk8%Nt6>e zhA${OM7f%7-8PGT86%lvXQFU@#%8{3j^*hwsMjwVvbKhr;EUOy=m?-Rz7*zTxJ>xX zAIyd=%n3P`D1VqR>3do2I5ZZ%Q3C0YVL=AOEr#@^=ya4xMomBmq_lQNpwQ`@MF>q2 zcs`!{h_FXm?e3q*r&9;{`4}$QC8>`*lklrOye%(yqa08xv&SG)crQKt0y#O9cxjEK zUO-(Zd(t-Gjaih2U)V*%KP`d;MwR%XTy`KDmv1h)%xw1hX!uFu(dXP>@@ele#usdvFb8P%K3v6YZxqN*7z=d4ft=SN~4X2Ad%K&D72NJLeUB#~SlBK^D z=5y4V@R`IT4fY%tuIu>#bH*3CMQcoo&8~~mrZLbweQI0G5h=o7l{g!SP^2)bdVHtk z`n`8X=UJQiRVqGCOq1(NG|zy4Y`pn4#t@wJe5IO;Cob|e2vR!bOoQrm;-tyV8M+Z1 zlO_XzUgg#JoAIApyxs&6{4bgtAtb$~Mrg{2Y7Fif8Wh9)LUXH0M>s~SM|?g^q5q31 zFf)eU=bJ4ye_o92of>9FBH=gb@%|)AgL&Z+DO3m_gqlG+KRX7N1q>O!g622lsc}Xa zV^6%CpVL(i`5q~K&~t}@67I28C=eLb`^ZMJU#wdjZ z&tYv>_SnWGDHgi7t|){ZA0@ck@28Kpcf3qalIxz{Kj%T)Z46mQvfPHnay57pdx%h& z48q@5&>r^-Vdk>eJy)=86J^_&pPZMnPx!XCm}4InbzM0NP(hCFvdcq3wsah^P9c^F zDw5wXgLX`Wyg;}`&5~rfIgp=^lOc06;HfeNB-;4hXqi&nZydpZ(4WrT1Og3Ot!^r}JRG1BS6)mjRUu5;Yei zZzWig%~tYi)GOX*hF@uqLC4Np&-!6bt8Dk3s8z&dAT0)xNtkr2OT~~cwO$9!JAmkM zh(=T|&BHR*nl$J?bO%W>{`bL$AAS7Er=NY^yhk*GWz=L)ZKzn0OLdJSrKnkH7eXw0Wc)cbm#lL@-%YRoyhRl+Gw&;jj}nLu1oEwUt5+d%4+ ztGWC!nhqU9yChHb4;Jl$WH;Mmw$NXlPLGlNdYdn$*(uwdAF#{FCJba1sL&ovcF?5V zC$|T8kfvGmA5Yl)An3mf(;Z5tKkYN;JS-ZFc3H|in9a=H3PZ^SDJi1K5>B!ck3r{{ z-YzwM81^)RtNs>K#vj5#uPTG#t()BcP4pl=U8P%UgwCN$er zl3CrMwBZk|A9?nrb+BHV2yskfsBvAVRn$GNX+N8$M`#K|znwq*l5P)Cn*9`CNR7z# zjF1LF?LK&Jo7UTp?W#yi2df<(rCDe?zkdZ-?P9r@$Kdc{<`w=LQ`_@uN9p4a&%qYJ zm`it3;MlNr2Vn_cpwjkO3)&$TDCvXQuv@{=Wjo>Pl`Jdt2m&oiIc*`}oP^q*_1RX^ zI-e7QwN~8(^tIm3O16&Lq`WWD4l7-Q$q?$_MLQw|cSorecd6sdEV7j6miiMW2*MA5 zT~a%IqZZFqT&Fv2S^DHOD>`Zj`67e_kGmj$Pq|sL5H1$LxLY6F6;c({nh6B`vLf0P zZ-Q1*dYwPUe8Kx5uS~bDio1d2&Dm_Q{UQfn43U-!hq|)!IxmnUwyv>Vf}fDm{gyYo zze`d-eA;5_2~2iE&16g!1odH-c2NX@A`#Ou#l!A?sj#JMK9SPfJKXDkkqe(u=5=^d zEKMiwhp>~F6#*}8do9ZYp;OoTK09q=rs-jDvjWH!9@FwlmyJ@CJc3yJu0L>lAQ%I` z2%VnV_^cU;ktrNPAHm!BGW;Adf%P$HYJh)eEU>Edi9-MSQ6NmcHoc+BX>^B*i*C9cEpDoiZ&(%4caT;rl#y*$@V@%zuJ(Y;uc* zKVfv?%Oy~uj>c}V7T!Ca|V_z`zUVI=#uGQS{5em^$=HV<^a zH8KeJXf=0t6h8L3CjJ48ct5fdY_Z@TCC~Ld<2vz42Udm{i+hLN#h{bx4?ylT+jHZd zoQ9$+;27Ww+VV&H>0`1i;7A9hBcv5LZjX2uey`((_T3H-`>s#C z)N!fh6+VXD>(oAv2)u9TJQs{T!LYLHG8=uyh%eQ^)$F@f+=s`X4#~O)XTDBzu3ZZ? zU%f5wwx|8cA8d3C4e>`Drz_o4$$}-o3|UXf*8li~J>XAlkoU2_3zZvATYp=wXrt>P ze;`R6h?T9q9P}TC=~r~DP)NSg<&XTOQ|r(3zxPu9AAecNSHFxja*Jqg5dJEe197ko z&AZ2Q7D?{Xot8q(?GtDmP`UtijH$9m6(@?xEmBykkP-2N5}2?Lq)S%Au>;@|);^uK zy9}A{ozO_?Y}hvt-*+wAtyR}rZbh>Ql=QAz)jMdR%kOM8d8)wQ4i6a8y9}iJl@9x@ z%l}w~u?u_AIOLgV*ubrnhETo79`dfC2XyE?_WY2#CkmmpFnGAodA1s3CGMXvclae+ zJ9-aFY0p7=eu$@_WA52edXDQ)ANnj4qm!>z>ko?3q>zL2hJ>z^*3icR9kEd)?f7E~ z^_b)*L5h_RJ&(MnTt#m3!ZjM5UkFd+YpR66A{)sj&oNnCfiFS#wsx0MmZj4=UisAG zkmWAuw9>wm&~v(n4R%OSK5`Wzfjt+*!~noS5tnnJ=Q(|w(GxXH^TqZhoi@~vW9waG z8SXG^HNxrv!S_oN#7i~bUQwr6@5B5)PwE&L@JrX>~$hu`&w_+?yhmfF=>}`v5JC9{v zs1=Po3hTc~mqwCn6!GG1PNV7AQm*O%%zJxj8_S!|(9>u#9?s(}FHzGfz-UX1WoC(l&8Nb zx3b3(vU%c#*)dZ-+IL-ucwJ4b1vFLI2ddi|d`ZEXYCEJs5+5^#Op_r&3;(T*;pTR2-bv!!n_efhspNO zES|jDWuqTtBhxDW^X<7ab}4cFM$p3g&0=T_Nz~y zjhCAuS^f3~f)y!<(6Ax3iq)E;;D&YsQpO5e_?n(Pb!*vp@i!~wR~?Tt(^c%+%nYg+ zSPW)lDA*K=LHo@A8&XkKzvrsfT<^tKs`%Y{%X=%O$)GTa%PQvgrG?PmuL#@^@P7~N z!dh2eg*8`U@Xao~^?g$JPI|S>j7<^FQ71 zlIAR59{>p>SA3Jud4+AcHe+=k6QmD53s);IQ_It&Z8?EER5s zT__!{vDxo7ka%hM#g?<-WQ%CUzC&Pn&H^P#!>OvY9GG$$+*a7O3I6oS8;&#fN$pwd zC4b$x1hzj{bzXp{Y5B2^pjGvRr4ktjU$@28bAyxCa zz*s4A+__qJ5<)%jVre_oM#*)rTWH>(Mba+hzRG1oj`Kv|14No2rT1xewdM}9Ic|d@ zJwk?5ec>QU<%2Pq#`;B2^DCyf9qorP8+2U2Wk+jC6yNeN8?TU@{e@KdLBaqbSokMz zp-_B|?%#l@UHKJgb8AB$YjO_*)7h627JuE+zwj=?wM)P9i}#dkX9QW0pco*xG=D@? z!zxI?bVPCc;Zhakx(Evx^HRO1tY7vAY5EqAo|X4~;<{poYi&qbYh3f$C`076mRxap z-cn!}i>a}bcdGD>cB@+AjIi_$HrnRlkU+fAO}0LzvIy_?1TNZIRSO0#!t}HdW=fcn zebRt7*;bi83rS1GVYb?%a~9+GVXRCN;Cad`Ru%w)LaF0f$x(o(hvCfti8|bR8%BT- zR=n{%)Gc^i?yoMf8~xP{iLkLo{Wkrwu}(@)if;y*K8k)>JE{r5D2Rizivfmy<@Gg6 zjq<{}Mq@5mU)N`bSQcY`qA(-wXH?Z^hIM5FmMF#mU&8cQp*|zT^6p(NzF1r@1gL)) zafV;0;unaW|8$X!^=G${zj?hK?WER9z2i27xu8n4vTT8xWf)Ss4M1$Vyi=C9jtd1465>yreBDzu4RvLq{`>%z{W$ z!&f)IV19mdE&B6hG$W!_-MlpXzMKyIlF8KQ%}aezNQ2m5LbaOGpzGK&tv?h+Oxpc!+St(((11yB5N=z@L9HBQy#MIi15)s8D};$$<|2%7W1?^!jhFo|BsVUjuSJ)se9Z^{ zz0h(+C(ErANun3_(8jGqm*`bAo=~G|;Z7xN|4bV+J05-0b}6@bjL}|x*RT&UWz9xL zfTHJFBAFIXM@wZ9bgDz!^ueHCG*uw1gggJmgXOJYgv7kg!E-2WCL zRr0A0={Ce~5JUr4tLYZI37k(bDfXoGvTwT! z$vI}&Yi-3a%*Z3lfg{_Z`G9vczR$piIJNJVhk(cYTu54g=}}k`z+Go}{P+{?@H{Pq zyf?X&)U9$;#vtklEbzcrKy5_PWiX0C#YD1e^aR>iSFtud7-mXqkPEAbrjqb?Nk096 zxhr`H7_D(G`ilj^;2#y6;RVC4x(#R0QZ&zPuDZMyzYywptL+!N<5C7Zd3c zvf7U8M8P8FY$Qw=bWoYpKk`%8b5^cL@56)&GFVZ`aGTK@P6!)0SaF@4!rO3e&_0`v zQm4Xc>vfTsr7-8tU+e-2!xh%0)jmD9w6tc7%5h!u&??|Fu>H_)th4otp`nI2d=lj0C<@pRHj z?XV>tifsajU{+9QA$2%|5C2Q8h_qcvO9!?4H6GqV--bIxu@M63B&5;*RCzu3qUYk3 zEKcVW9P}rPg2MmS@6~&wVkhx6O8$WvjbsUEP6K~JguOPk{pdKR=&wE-tuKqP&jzNc zU(jcpVr3InY658Bt*;^P)|mw8(X*I>x{2m`V*+t3ai}&xr`aa64JIK88pYIWe9eP@ z9ZfW`--;512~dPW5F|n*I*>k%7?j0`L>y*9Df)m0s=-J@1x&qtA`!|Rs;V$O_U@0~$U&oDq3C|o2iIW+_4hfOCyrU6P_AfS z?0A-Y&{>`!=d08n{I-*jfr_sc(XiOt{q}j-ya$qUfhpJlLdfOE<6=1CwJ!?h=R-V6 zr&1a3Q>;yf)n`|%qglr^)ZOM(!~6&c{rqc*MDaRxiC(vHQ z<_d}gstdjD%PrTq8l%QQ5x;O~kkb9HP`Z;2Q# z{8w)f)}~0W=7FrNMeq!+re+9IWmdF=NF{cfElG$v_?q*1SO&SvjMh#OhmNid;9 z(ZH!{%%_qwAvjBe)U#+$^)u*zFeDBghbsLu6ufcw$tC;Rl{BgZ~r;ySVP<$SR!4%8hD8cK|cBM08eX{;I z0^4o*z$owt>LXtJQRZ;)SM7h}D9XaL0j++~Abp(k<+- zho{lQqDswJ}bD)|_Wmw(Di>cs2^7DUbf^gp> zr;@>ql>SBh+F@9MZNzsjoR{t$iW|-nb9xWms&6J)>(I>7Z&M9s$k_~7N6*dq)uZ)> zqK?k>eoaXpA^ane4OzG1MfrrO-EK3{jJ)={b#HFJ#_>d2ML=SEx>ydw78|VZ+U;2Z z&h*?)8MnZ}*R2pUDcpVA&+diQU%0-biE0j8`kmx@FbGWvF21KhdT%UUlL3e2EIqHE z!Rl7$+ezc+0YlHWx&{e~h02!kvJk0oFivpEZ2ekRV?Kq{t-yp)(#3->_FuCT z;(RWLLxd$R<1v#l#)|8OZIEqy9rt>@UO8z{=wgd0R*%tBj*$hdHcaS)RbYEta*Yq6 zN*w|yJ?vEJZq;>Mwx{HCDL)ia&i`dqlZVlVu6dDKiNqHkD%i|{k`KdBR2c0$)@ObF z;d#BkZ8V>(;&WcAG`3J0Ob3%f6?xc3m9B!X!zoF&;*c_&a_Jy-+I<#HvEwYuyd+yYO5^Bb z+rWQJ$$QV=xr=B13ziO*nQ$Z&gM1#eYw2*qY7IZQaymvL^kzfaQYQ+O?Q)| zI&Zh$xgBb0E(6s8=a{Y))N&9Kc9fBV&ge=UNF>pz(3QULAYF1Em@;yY>QF>(SRJ?i z0t1Pjptw|SA4_~5v^?|i>F+S}#uHD;qHpPWHrFBD4U<~3+vJkZm&U^~9Q@7?Odf?k z0;`q#D)D-(uW}z8^C`C&{G;@UW(lK8catt^!=`eMSs!k)=PH>2^Hd#wtnT^L#xQ} zXr4vY@y%esP4HARyi*5m4(}AhX<(vF7xdVxSW%Sa8UlF~JxxW@3ec1Tf2|RbRFuc* z@n$paX24=rFPOB}Fp!n`zUEu>0w9=2LP^7n0hDeYbi1$VZxbZ)|2K+~7!8=f4FCT6 zHPHa&b=Y96ZSe|)4=M6E?1~F@f99upQhz>w}J?B>icj?9wQNo zF<#-+C{{Kr6fQb>Tb`W{t>oSPw!qwz5c$sDKuBK>Z&OgES=D6`y_!DK-7y=2z721G zc|5}|>?q&b&wo3_cyF=zZ(W?FL@4OD^S|t!p=(z`B9Gai*gip;%NZEP+=F;cYb$#$ z-#jqgdhK_Zh)Z4_gMqjnABQyO*)i}odgN^zb37FMZDX{W4TfZ&0EXX6ci&CvGl}2E7I|Lih1g5i z;BAiL=I+~n;0n{NPNUTH9s(p^iauZJ~Q85ra>Co7naFL4OAMzgj ztXM$%VvN(_{0^#V`HPgtZg}`+zQi)74Vracq_)EmFFb5x_J}UYf1nlfyInHb%TDq{G2~Xj;)ow~P-)pm!efR^hez$$d zuotw3k8F3F6g#8N;DA(UBW?P03ua^j6x-e^`n}p!2qrqO7#$ElUD+Qs^&_kwbo3h* z%;i`*TfQGS^jIXgSG>99TP0bKlD~Ci_iERm8xUNE`hNVJ|H@W$@C%3asJ}ng`?>2e z$4oS>7HqRC-VDu$R_j}T#0x#1B|;nPUA^{XLCEj68r{2=M8hthV|T%O)7aRSr7h>= z!bQvrT{SGR6lAB9L00kRTpX9Jf1hk}s>iI}0iQ9YUmpDwgL)hsB`;}l>oTlyvXp6) zV{APRt@iCjd(%U(N#dOE7|V=&_$KnCu$5Rc?+^ z7{QIII$JO^U_Ca2I!p6N=34~8%}lEpSPZ6T_sldtrl?bHIyA<|Vi1*aHhCX9II6_o4_7rTem8Q6bMnR8K4ixvw(bF|-0xE^%g94eUA_Sy<8vQDCPfzK zfJ8giLfA~C+np@%)4FzeCx!&@%b~Kpt7q5ca1AbfREq1o+fbiT*V#I6Zn|B-UjB+g zh^S#gkU>P_Pmolb_*3dj4F=LieKbKL4#D4BKCjC!x_3si5Il2?B40sj<cVO`YVErZ6CjWyQLy=gOD#Q%)yw-v!BV+o?@iQQErdt!`%GUiEyd8px~FR2aj;^w zj5|SVF)K3b6A@N@{7)w=+)s2Ksv8t%JWdJlUd%bLVDQD@I_#!Z)Ine#VMdv}}i^SgC7-sO& zja6R0c4H;|57CwRLh{lOaU0QO5k8$-zn5K7YL}9Y zZegKwU7y+1i$QDep?>k~Q@GwqgKOUHNDGY6?Dlf~6=Xse>)T7A)eWaTk#PM>TlO^N zK-R#j7JVW*&9pqd5#*NpC0i-@Z(R z@qm(TqOEVVy1xVDN2sE2USP<1b%$Zsw$z_Ti_h*~%5PL*BJhU0_-91(nBe6+N+FY< zG8Slyc69tf1))7JCxe|ly`w% z=Kh44Qi8?~#jUiUeYW&E`@{0JLQnMSPQ z^yWu&f*SRQYnT%Kg6Mp_1nN&rUAcaG6g*lhG}y0aQJQoLV}9QKC%Esh7c8dhaK1)W zt%Ns;b@G@D&!g;$c2|2x)0T6(%Kv=kpy6vOM z;}BFHSoO<>6}0so=StUzuioB@O3+>QT2Sk(uU_;jhqLzbo7M|(y&v+>g876WhGzff zb}tqxJ@PJrhL7%1jL|vz6uz-lu=P&=ON&(ja}z;}6WWqY9>#0t7s9nAu*yF1cf7uj zQMfl<0B&>qjkRVy1K}E`gUWd5h{5?{A2xJeezbUdPf%AjoW@yA; zLGx8cHnci}7p{pAXn}u)v!rdb3Y9Vx8rBOu5%V{r5%REyDk21*({e=&-feO&)1_S3 zu?LCSC|U*>1{H?ndMhv-@1GH5OUUm_|AYk~S4@s-Mk z?6c{bD1&_kpy)Ow0Qqft{BHxLLDgY0HLz#}%yvQhDiRliH$2%Bpw!)Cwu3ZR)Q*(UeJl~{BL_W67%_jPNOgj{E7}K z1u#qQp1TfK{eIAjwcZl}HI$sKg2PnlC6F(3j=TqQ{u*_0teg*bxm=7g@SfiuT3cp* zMJBB!XWOx|$U?9^U|-io7SBW=ON@n6zVMuv`L^3%g4qXUIc2cp2n2r3Uhv5WS*a88 zS^@mYHqQ$oV?^590d{=sB9L$FxVp{GCE&K_x2mUb=I1T$sXrfX7wVYcQVBQZ(cF2S zOgaefb~!)d#twXhz{1DhvJ~dx=4ZZ|*&inm_f7YpmL;nTU{jBUUGbUmw#9fkhzWx) zxZu}o$L04bHpy>l&|bOxtq5k(^oeqRIA+@b@E{CocVesEOr@a!{ zHiFzV4U1sB0tGjMYM!{OMAXN+MQxTHm~AleTQIyd?D47{UlrdtHu+d z#k1iiO1|nA;MAclyvp$V#~I?{F>cwqqmP=c9u824_bXP|LYkKCnkn zj}m)n+I*&9ebZm`!H}~ai=vT?hoe=&Z;>SK;{4*1}l1CI+W|- zfCsIZ;YnW~_nB+W(K!ApikzQ#p=*st_;=`@MB_UJPRw^cI#f!Bx|R{$clUfoBj`57 zzdO%~~kFBvL6X|&Ncim%6Mem1F!gzLqFpy9AK&~%iYx*ImsGH z%T9YK6{n(AyZN)fR9gz~b^8T`+qG02C6qsTvLBd`eld9Ap?ll&9{U{Cg>Ky96)>*c zy`|y?$Dsq@i9w6GFgnjOidmG2#98FDs?C#cm1CVkzAm1?u!*aQlv{iBStPsE z7G)iQ#-qJ)J=WT9>~T$fi@heSfnr%0zj-t4u&A1mGLdP2Zzl%C-SbC&kKh6Kufw%3 z-O`HTO@Cj~Y6I0ne1LrU71-xAwcEvOpv;Hm-!q{<%D<|%UNeAsGNG2O+$|)C>9KY4 zyp=GAY#PmP$cDIB*f&Z3tly!%5VaJxEW;%=w4qem{V{~eV_MFg>tH?j{H4y zYJS}c6{pIkNvYRta%Z#N)*M z+uIa$XNCGK=~k(X8-?JnWrwb76_ob;?hU2b)9i&U05x%(#Wh z@Y1whCz;BZxUEZse~uMHl9l5e+MZuybyPxfEZ}|3Gbo zhk5dS7)0r9QXnZCcG?AlCXkWV``-z8tL=H#^1O=h-Zm8-zMI|$nH#e z&^Go3-28`8JrIJc$-1;(!L{l0U=J|vxrOMklv@O$R0*8hgUU3tPdR9D=PaD%Cz8=^ zWXJ!plU3T}$d5}@DKGGjdSxLzwDeeq-(SEc^jSX(`fakkGsTTeo#$RoC9M*2;@n~) z`UQ3#RWcuHJ@WA0;xPS~-QXbKAqng}Y?^iCR7Uo^O+|~X{fC@n@uiK!x3H)_4+ppGb+vqw#G{0KoTcI`@T zt(|Lc7tECAG=C)={xp^2b`DKn49-hs8B)7iYCyOwMVSHHH^YG3tI_PowYT9~JH;Ny0z%K{&>iCDFAoP@z+k%lO5Lq2{R!kR26NzyfxSSY3rbGhXovC+h1Bv1`8_P$xS-MFunaSw>2(~#E^&stb zu(Nsk=@@SON{Qs2maZlHAebcrjNaq0hJ-mFDWWyHsBEMoNAbPJ4Ag|RiW?~!GFUUb zQ~>JwIC#y`5k8Z$ff4@vx@f{M83&tVbhljWpGBLoo~?<)-M8dJY;4?^eJ9ZtO&ND?HrBPyXf`oRfp4U&hYs?m&nmz z`ADnyS%BxBdlR4jCBLH%7`PO3VR@~a17}V`t3T6xfdr3|XP$#=!;`z5>6_mpXh8W! z(8i6;P67MYx0o>Ikc3Yuf;UwaDX_;At)Ef!tc3`9fmKh(RO^q1KeYTRr_&8;0Y?Ae zuP8D5;0nwsa#!=5z5>>ZZ10RoXLfzslY$T-GsfWDw%Oh>bB);e&bh-F@)-qV|UyMPL zFlv<$4LFS7D?4D-_SCcpxkncibe;KDhF+FRvyzSM0H2jvgN+9bQC~uadYTq0G z?|vMOZ(<>l3SI+D0Q!RS`GKTtknFcRl>drs@4w&9*y*-g0_qL!`JI4l!0#KoL1Uovd{(ybpGg>Ykg) z9OnYOb8oRidXnl-Co9y13!sQV!o+G2KJ7H=uumclROKingNW3ul*r^Rx)+K%2#BAm$=y)o{Vg}MVeF$+p> zOut**PI9}G6@8QPO9#o3(P1P~aZ0h1_613y^9NgRBeDK{13ZH^WpC|iB za_xj1zn50RxCh=AzjkmsjM~Q*A1+gJ*N%X)G9BpO-cvsCwsY8L5x`0YV+t@U41spK z&s7|(+pb8rtoq%%q;N$%pRVGgMJV}lt%f7Yap0qseBcZdT+}2Ex1h&}rhE#yy8HFk2q-yjy4_Hdq zu@;DQgxyY&Qc?_2w&bF%G^Q~ zZ{Q2~Qc}{!2UB)9=F@c(f)Ag(Sw5wpt=%y&$Fd12f3DjFwz+ISKCbus zb528~bBS=9#}}^0U~;l&4paGrff_z<4`5r!FBLz^t-se|?O6*)+>(nikyVvX!LKMG z-DYrvA~|_yDe#3qMt+77VBgNT4kd-X1PqBK~io1 zYw!FgZ<7XxPQX#gge$P7w`{S`j)Cs!Jf^M>`NH3St8lyRrA!V~IwMCix|rL}RKkO}e5Y2-r}t zpopkgK?MY*C`wTj5d}d7#eyhSK*jf2>K)u6ro5l`uiJTMc6WB}nR(`U=6Sx)k5kJS zlKkyx{_Bk+?9KW53kISRkFBFWKW*gZHt5(21;=;Do$gb#FXQTz3! zn`pa!aTaZFvT<|Pdt!ybyxbo))ynbDW)3ajH~bSVTqUJ+GpV8*tveb{{odW8-lCx{ z$yTuEhtfFS0rIe>BK9PE|Xv>^Z>WgXs>}gJZ-K&CN zojBhY+@$)`3McKIDB?p$l}*F!EKGr@q)(b7V{5j3CIXG|3cv0Zh2UNZt@GK4JHYrm z$#0&6iU|y!a`eMgcRqNGAMtVQTDWVx-XAKcwrOXf^_ss@y1K6bRAG-}dd}~z{28(n znEM~^G~ge%HVlx5=7Z4+`ztj{FPB5`RKg2S+PVWO<1q*)ShsXUux^~;*>8T}Yg=IbgTWqi46(yUM^UiW zI&vdO$}P54z>d-0pSCgcmB0R#zpf+kaE6U@7Yu)b3fTcsKj)W_ZY^@(g;3*ms{{v# zlh473kJmdn!|cP9FQPHfCcV!So%18Fb$*7~KQ&@0Y9yd zhQS+?F!FlF(|)O`aM|+GFdf-8R$k5<&_0qWI&M(ToBg7TUsw5QtaulQs@sQhvVGkP zbiIGlk*x>$vO6%0aQNU|U~m;qQP=*cfKBPpWcqk(J%oFFTZz|v__ zaTZ`Se7K0Tq2h>{IB3oh*@1~j7dC89oK`M$QdT~E`|9?{Iwsk4Yx|#PE6nWsn|+*&f|R`JVto?2I=~Fr}{+?yRR1q zZ}Q-Nk`VY;$h&vRo__-(HiIlp+NYL|=_x<+(ho;vwUB@5O!mSJa2=gFe!DJx$SSkn z0irX$4}F`vs-WQ240+UVBD(gzH1yn8Kue`E+3Sm6h$ z+zmLYJlI?O2okKhJtu+n9u_d#8$aXGcL}_%=^NOoG{{6AOhD^cEO|f(nsyHU#-!m0@bHRe44IT7`3X{));zF_luL zP?(F*!!iW!uK}iPzzw*!8Jz5E7B7J$*{Ey!)j@ACa+M{o25*L>6Uvu18^dqZ4Vsgq z&@V)9l|n3}E{VjA)fLR$g#}aVhE;6LB*m^FpH>mi{tys_~(ddXvYP zFvSgBQgAWT&k2GvD-hUB#23woOoGUjh*_B~OX`$<2_=uv!OD*|-NohU&%uvoExQCv zfHHs*@qXptH~40kHiv%{x^ea?yzP1ej{N9tYrj!EoJJabN@8NRIilB)bhA_Av8Q!UiwFv$c4Vg?`szV8AGn23(+mios#xt z<)3xe7&B76;AJ}$JUEXxgQ%@W%03JmwTkpQ(>QYZUlV>Enu8f9=yT>Z@{- zlXixPSnstTk$CyNQ5?QDCWpXCLEpE+gH zp{>ej++X8T_)L6YFh1hO$e!6`%iMT8CL5W)_K1S3!3c>w?$#c&VGG`7O*W4!h;kX> z-HGa~Q5ADSqWcEZ-}@W&KAt|~lbN$-e>!LGXP4_<>gmj{2KlQJic+s#_IQje#Y zRrGOgb(OshHl{+`f$r?e>yR|B$eJ|mR%bU@#0vMHh2?r>)5-s4$aazgZ6aW3!e?`6 zKM(LiOa0}+*q0T$XU9EsaHJnjM2K~hq2)$rty8IOi|4y5NSw%~LR@a>O1VqL4}&lk zW513(NSFNYTZgm9U?Y*mi=)IM;U!OKydf01wABU2r!HVAqQjMJKorJX5Q}qw=Q|G} zORiTTzO@ycKd?LjB@?PEGrt^wAAS@}H_yNnE$G&>pTkDFn3yJHI(`4!mzO{^U=qCnvN;FQCmC}$Azo^$<)+6zw4k5OJ~rqhE@duDr^`}@J|2V{^8d!Q zMU%(Zn6cx=PnbCAqsddIPJ1_VH)S%-J5Dw8)Ze;EHLS4F1O9$Gz6M*!e}2_SrfMx} z6SggZOeM7cymuzvjn-vNaR!Lc$GI^|45fXiFxP5N8E_)vI-z?WYi-VCW1 zU@H*&@JTT?0>u1*#ZtS&)Dcwd6$J+Ct#(aHpSTwWG?}_YSV%;%e5wn04os$Ezq=|~ z^C7v)3V4~xsc_W6Xkigs^f^jflMX-HOZHrcm76gFFUc9gO^&{V<@@2d7(vQL*!yO{ z=(gUXt?dc#q_of318vW6zbSUljOy;)`FH-BGkI$LF+y$dRkQcoZ@vo0Bf&Dtla$Z& zH93_DPcLJnqDkD3J23Ta%=it2-2Y(%ndJ0akglzw*znf4ss%Y+KPJb_O)Rg7Bo`JN zU3S11my1@t0+i2*n7Z5W&V3kGjB~LU_Dqzq5&CyZ>Dd-R)6xvcx$ePZXGKz%b*{i> zKft9R%^-vnJ%a09cNz@%YQnW**rI2!cXe7W=7$ZHQZ&L4!(MVAvx2+WxAaN?eo&@Z(x@fNa8L)p+ymU6TmzA0Bo${_`4_Yg5J72TWONZlIZ} z#rIs*^`m#6zWqMv-++eqz7J|>-*Cm(>f8J?Ehjm3bFbMYr_rLJW%k#S8!ZA_G*XRf zYCW-$zS>-|URTwiq;mXG75#G$r3|C^om8*whF|!sflIxlSGsLE0eLcv!wbfSbYyfcH9EN z#@Qr}G3ArfP_$fNs$?PHGC1fYFsQCY^9lN6$(V9@Bp2c}B!S9;4|7~lM|&=>6wRwq zaI5d*Z*|)}H`@Lsrhgc|?@#6xZb-x6nZ#*K*hmz(9Z(4MG<5!JHNh$R(&*)^a#22S zR&@&=#zE_-{eP3wqC9vQA06(X`abgL@p6N^o_plbr-;mK#n9y zRN59b$({Pwrni}=z{xB>i?jJ8Dbw{3i}-y?7a19W?u4#zgVMiAdt%+cZ~)hcYv1ig z--L6k3H40*PcNF|Voi1RoZS=_i2LmHcBdz`qIH(jV<-NRocGb>eg8L-o7M@yZcg=k z{-z5`gPPG?ohHp*V|$H6O%}W68>-j-msp66nX9>S^XsLCTFs2unA$8qYGSBrS_4nyhZ&RCt$;iWJf|)Zzutp#>vAZ#j9+D%ycvvK+NJow6DR5B$z#`!**t7j8O)Y$ zFp^{*hwNNv9i0Y30z!15x8Iu&Z)D1^ytaXnG&v9s2Se+7N(ycgOHYynm>N1i1I{JE z{EE*99Q-SDa@l?$9s=A(@F7T}o;s6~{g5}A)L;h`i{PBaVB0Jcl4UZ+>9B?0VK8$0 z9z_NoR5Z~o{%Q0PnrPG)6>VONHKxn=X=Y=WccLh zkC-Eh*9Kf35hy90{ddFYqc#_Tq;!s!W|<`%MVtvPrE`HVTokxQT(t^L!VXdIyXfLE z(n5Cir$?CfhpZKlBrE7bAbnDj;3JEtl1xgRl84DE-%?j);?p`J`VJ?*`LWQ6D%=>T z^*X!t(M8#VUc)()T_Tt7&~`dmV0912BUWa=41K8M;pnC7!dyd?3-8w9L%C$>bL+hW zz38r4g}{stA8O%!47jZpf}boRTo$37WjeF}In2NR-7OcwJeU!62naqkK*tg55-@!h zI#<-!Y&-sUT2wz-zq-e4g$C9yy+Far*;R>e_X3&e3+bHsc8rB0+i_0@wj>I zy55sbg=aJ!)ioTQwYZ_ZdN^N;Y@Y9YxLO0X0)n{Jl>a^H-<%x7It>E+{{cy(K&&A) z-599LznU$8uwT>+4p7H2?-cOW4c;+24b}hYpfoca8U{suJnGO&`4AOtaXp1cehBTc z1rE-Cy`;@uinI?Pd7PAg!R68fk&T`cDW+fg31h87*gb7#5yAO3cJLcU9;G;W7ceE! z7AWteNd3NYE8-9yoim;CDN7+ey`v%hwCIwRk3N$(XB;^ewsiUGb-1%RZ_h@}Z z|Mkd(aXb6fX@811+SgjLG6TFv*sZ0042pn2vXOs5liA_oU}E;`<(sfS5~9CxK~G?b zXrq0SNV3INlGMJ$luS2VN9TXG6^LQcdDzlrzI<&MhUOTCv^!(ydj_&TCEe3G{b|`I z*VaKAo*ph#+;{~!3tF$?$%IaqEDZe_@5!P4Wc$8Y)ol$d^k;6gUia0}f%~nb3o)x; zh-6z8Ja&hhy&@-#Cd;cJtDUpa-Lnvn`Aa4I1BspYpgh%Ivdo9lC! zYW%jFWUpwuc$6yyQ5*RpsQEgCn-1%UfocAONb)jRhNhH&Dv86z@lM+(7A}L6JmA zEa=0e67(?iiX+|FwE#{)waA1R$_i(xtIGYsff-Lh$J^h_#Dl*<;pX^K|_LF#BU=n>7|`l7XvC!3A|)*q~vZ%3iuaV60caIX7(WgGKDt>~UD zZ8w*Hq{F|^geKJtWS{!KU$-gML{+8LU)Nutp<4A??P$J*60TrdV(WNG*HCy2C)6yZ>zG4> zlvWuPl}k+nq&hyD21YYXwJ=6?gV5wxeN;4zf-0%WI0%ILvEV&b1ac)&z9jrJ;>8Dc z*=;s19C4TzBtb+57?tw+>*y34Nu0qQ#9YQ`?{GE^FnyKCgY<~N%%v->PtL(;?}{Z` zob>(BUfau4a!hs)N*%Mimq|(D!VRsY3iL)eIxNZtCE>Df&_ytXONJ7ILX!$lXoJb1 zb}a*+6A*x|wL6WZFaDB0HCSt~U50LoRgi0HDGH*Fjj(^gmFk|t*}c)`h71#9pT?-~ z{63zA;NAHYLy9S9yT@Jm>xN3j8~vTMD;#N6iZJ>YWoT9{A>oQoi}^%3;w_LYZC!su|{c{`jG!Dr#QD?GQCUi%8a<#eALGTz?lA4}ECjXeT+?uNwSjG%*?kFF2368M5Gy+z2O&#vXY%1oQ3V-S4XKmg#~C47 zMQC^R9&}}*QOp_CFO@K!1wr4Q+Xc_3~X^u9hU13rHu z-$;um@9oVdq6+(qZVbh`u7~sU{bhQx+!av3oEDp-*7ri+&&-+fM^S+`mhjmZKSN3E z6h^y>$cc#$SWaMwV2G7i`5A4JV#3^PAHhsxhHt_iIuE5fezV2tjbx( zK=TcADad&ZTp0g=wTjgZ5~4(JQ+j1^QU7Oi*JY50M|8WpkiI|Xu?b+XCL5of(4)8j zqIj~5tToTq&1HMrY!*2mfdpU^@b1Sv3O!h(GnMP28!kDn6nB|g)2VYPlv#|9o$v<4 z-|R*K`C*r0mb|uDZ@8sX)dw}%B+jjaLto(piQ5gg$K(FYuSr|DthqxB>QNUWq3hzJu*;D%yy5s4ISBloz0 z#pF7w@poibbv-D|7=hc6WHJ5n$4WB9DhU%9V?L~oX@OuYD<*Ej9@5vpXKt+o{@k(M zSUyKC7(<(a*R%kE;NNp!)5c-i=o)?Jv^UmXqum8hIEK*}BwLqwLxc4BPe>|qtnsBL zxu%V#D(^I2*9*ZGUGUK=0xE)1?xFZFs#n>Fqe~^>Gh!C|=R?=Woqf1Wr?{E3B?$&+ zw}RIlgmL4O{UP%*EHzwaz4rmswj}Nq5Y(?V$=(M>LipybPvol>kvBccO_F|Xd$R(; zily~WD4$dD)o8bo3%kF#cQ)K?{6mW%kOV(E*M5I!-1Q{?EzOB*^XlJJRxhX*`?m4?^3 zuUxxrR>m^~J(}Y6R+&i7_V46RnQcX377YRfiO@yt8CIWfZTN!uHEOaXg|F{)(Kd8- z63$3X5SYBZr6Lh+$%nwrqQjW4UOA)=_oVYXmPOK1JgBamL{>Izc!1#1xJ(sxav+HHv1F;=R&&RWoWe6aF zabLGSaqmOoqTn-@zCSoZhr3{gH}+=_Qd4wZb?<{(=CNL0f+EQsp;ySFv6v59On;x65*>O4{hE68v9j)A%8c%XJ;9f3a!<0j^!6uzw9oOcnB?@yhkCa@|T+ z`>S!`qA2Z4S6xk`)k(dmqQz^xY^2ZE7n|sA97=3N7!THk0;w4NT+ zeSCAj>La1Kq;XJGiS;8=d!aH8nlDvHN8@4ncR@Y3;*Mi;IIJ?=phby(0F+X zRbW23)g3C+5FL`rp+ccwWO*zdA!)HMJ#q7ZUP=`T(X{YxJHK4lt7^8tzBFJt^v~^x z3UXfms_zMJ?`N{!l#IJA55+B8=u+?fFyc83_x|nvNd4oO>ggipQ!H{L*KWd%(a+Yx z6Pf5}JG4LCB>dGZ2wfVy41JdUDe5BS-Hx32L;4(cVA%S&bMz(s+w%BdPjT3RAiyp} z#e(3?jMfBhC8UuMhvSrVjkVpQ5{Txc(PP*5@~}h5dSZ!>w?E+zyYEBQW+>4+#cUOk zG?Ht&SGOAa!v2CcFbOj*6vEQq(uo;{P3@CKkOXv8zR~6)OsZtU+hF4!4_iOe$MZxX zWn7cP^16Ahx+thw1T_ciG-}?cSm&Cnh1}pyY1M3tW-IadQd_+?y08MO*YWEcMEfdA z=|!RWsZ}N4{T^5Rqv$0}(b+~DBTsK2RR)}{0LJ7@9KR7*ITHhR?nlAwXFwloi^?am z^ANnt5pyOANM|Kug~$?{IO-rZRP2e#n^PHf@hNoVNZ}S2(d$R;|BRKr8Bbu*2vMjphE;q)+B^MMy#=XtoOgJ|sPV5*S2n@dWfIB*7P_UVL z3KvNNwTE#$HaxobN!V|R@LuGen5+7VNf0tf-VcIDXzo~!09}~v*!v-*UV-ga_)Zc} zKbHI)#+t7G{roLp))E1si^NF;q_tQJA^-#5re#I+rw-ziG%~BK&xjLOYi0PPF){y# z&;IXFThw`?&ajT|yIL!nPKuT`ZE3su=$+1$=-zAoGfcvr&*q9 zqN<8B?br2!4zWraKtk&hu;dcIDIZG?(q#I)tSpE;1?BP#jJnvjI`LoiNE$mo3{tx%nzv?>5$%0 zh(KM#i!izE$-mle;Pt1SwUunwlk9GHf99A#xCV*vluZ>KL>>D<8ek3PK&0Ak?p^f~ zgf8)CJ_>Y&w!X@1vyT2!IZG$KqcmyQZcj+!_)A7RY>VeTo3{dd>6%kikpclYnE_bxR`fciNr$+7n%T?^!F-2e)-68+gOBl1?`bgCxUap25{`j%7p0 z=uM>IF~K84{}6g{fK}wn@sQaxcQv?dgO!sukp=x~`m}7+9Rq2a#6EuwEFDwgPR_uE zFL0<9DuU)-ehneIIX`daByRCwwBs2pj3Q~aZbKep)Qty*Zp z)7lA5Ia)JC6Gt~o)6U{TO)Z-X5sOU(yeCm&!VTxY6^+~M+S6=hy>pSRT}#{5tavSN z-LjVTt#~{cuRAASK`9Yt)ty4ywoON$9if+M=(TS~f;XJrq1v4Q*|;k+7= zAhV$+)$xH=^mWvSasG3eOa18OwuF1j(APF&La@tdTuMablt(54v?BtmT8K^GGNN;K zEGp4Hx~1TVX_pe=GUO@5!kn+hu{>vI;G_EnaVe@`=sk{3;{wO9bXgQUG+xbcL&=F% z=!^CZy+;4OOzze>p`9ve-9Xs!?EtKVFxOjS(YXcsu9PyM^yz}=AEFLKp$ z({&8wt7rEijGx2!TKb$}KsOPcD|4AASp17we7BI#O!eLLVH5V86fO4nUAp@W#JddW zN)tIEtm=wPABlJ4&X%0}4NvT85o!{3A`eK)gNqnh}fCS!DS$84ID&Svf zc518tn`;lB8kbQC)d z(6DGXI|4$b+J|Z@XdHlsMY&-FYOXYmg9@$DkZ2#3+5t*e+1H$+lyMYhIlW=X47fDr zLf0FLw9bc>uc;`b!(m`|pGZs0*CF0xgi{;zyE-^FJsEVIPeSS$&^d=r+D-v7Yp?OY*O(jj-`nx6}Qz!uJQzmzi0^ zH^8h;WlVVnWOh6p1PhATEg=b9OIND6}JAlS;yz54y+8@(gYL#~X! zV^Z9vfH*TQ^l-n)u0y-VeUi_OpF8}@=!{QK=pCPt&!JO&LpzfmPc~y8zdpYkf>%Mp z6vLXA)OS(P{I|$ff*m$E(faqg$A?{p{yj-Bg)>ecuQMcDWN8vU!tw132&D^RFi{rn zw)#pjc8t+iB>r9y3L0IRqVoHu9t}+^*C;Yn-i+yGp*zuwtN8crL$HY}@bc z1=u)nrza*>9bLK{&RfRq6=1UXQVqefb+cQ;+MrHSDXCmGIy}eKuGvZz(cR8oV@nN0 z4W!0N=`=M_7-HVEda?YBD9Z?6N&=Yy7gJy}spH<;XTjn=;Z(Ud>dzy;Z^qyNmQLoY zIhhgI7}@f^AD|LFcU32Y61KR_%T}=r5?j@A>J{#ZUIo2ZEY4g7sJO0}zYAXEp-KLr z*s%--eO^NCU<9^B5DWRpiP**uoLiGvX3+MH;Q0APvgqt@%pl~SD(=-Rk#MuwW@_S^ z<3Q&#WnKH64hCii^YX_*H5URva~%ih=2A8;J&;#@dF0hWHqrUm%k@mY3ZLqlE*BN) zi&v8B=l5yo$~l`VZlZaru4CQ92!gm(bjs_BIMG7DZ9rdc50i*cbWg8?|6NqS&;AzR z7)M;}8(!br{6=?UI>2C?A=ldt4(Tr!-RNHZnstbl)oJHfCvP$}ur5i1mlW{%68;O# zM&Yr_5BX6VSKG!!b*ZCPs$Yot9h$_v4^d)*bFaU(Uu}q9Q;De^HTuK%1K3bUk8wvl zNKaUXK#g9Sw;q~8*8ZW3sM=Kd{~2@OJ^pqz9{8t)dL9xk_!D%4Zhw$gM$r5oI_x5x ziU2pu{2@mMFy!VCrd;7}mgqsYyTC5%00(z7H_J_HVaG){Jm6ZFKo{6IE^sY`U?fZa z5WK+FZkAC$JT*(}c&&?{!^&AVhg@+wJfOffz+vk}aJ9VRgpIi))7eo4u=Qe!|_e|HhBAxs2^OhN7x6SSy!AkT!fr1Yi3>TQcc-q zoza(x9FX2Otz%l>M;>GqR=8R28y7JSKLuY}V6zkZ1e5~+-^uDPb z*E$>-kZ*H50%PhgLTp6!SNSK~uE-mLRZM_GunR;E$ac!;du+hfFVQz= znD@3A zGw(IZd_Z>XlAy z8e))1lan;zn7Eln^;P^zs26h1p!|#fqfqQd2{zL8kh?%nIP#5{Sn&{;-h1a~ABQ$K z<6xCeWF!QO$e_Y@qVRb5$nzG43b_ouHRss}9$)98+*nV)f*B799{f(x*|Pz>WH%v6 zCVR~rWOJP9tHrNr@sT3`2PwzGXc?=AmV zEC=~dgZQhUE{LGzf}cmmS5FiIdGwrRfK;< z;z^={zQL42s$n;Dj0O2Od|yR%InUkU5T7*?y5H_Wgv)j-WOvXN z<`-Q*nJ5+!u-aj&lh;7Fj_79JxA!yZ96aeL>av4p={0CVZ?zNuGgP1t5lsA|_f0?< zl!^T5QqYyfu?BRO@jleQZ%aCQ0_#TI??flGzrhlEJHsNv2zw(Cs(t^TZin5pUhS>A zRTSy%aGCc1RJ@)eoSyDMQr+o?pJxI1@FbQ6}BV!%oStcB$9 zpL=yB6ZHBPNvfaU*I}nI?sxt*TX*w9NKM436yB*cHf^9mbJV6Z3iS8X7*&+VnoHrF zuc`Uu6)4Mrf8L@!jFuUqfvka4Kku3#0lcHJwrb6E%~UOzD}tMYW*b7?ur%JuXew)A z7!~HIyKKq>%}_M3hbl=Mt2Abl7MT~+nMFXsu1}q|j8bB5Q5Oo6PGsP*a>*LF-8)0t z&3X%&cZf2@1CW(=-6qT$WnD^L+Xvy_`cEUJRKM%a;6CF}C(I8~g+Ak9;K%M7j|s;t z{a7NMR~`9=!E)>NAP!?8wLbth2+NHAWt($|9BsMFvRgj*e@6z-x-9BuVt>N$OqZIV zFZ6nUihGYpV|)@O%a!bB$Os7nvji+yO2!EpYNtI}bmaG?lFK%C=h{DDVwjyW#Aqi^ zhs?{tlm2Ju2=LHSdQE?TKS4q^;ZI8n%5Y8MJDu&bJs$4rC+L~rDY}Vepu#@-5HP3F z7#ap+gQ#ReUh!#zrQS0Mzks6N&6DWcSk@{Y5Qu2d%`C{g@M#Qu$6cJbsc75^m}zJ8G}!* zzF+?;_U;^cjw>h1=bL9Hci2@cpDEwPtN0!NO;%?u?A&5<9kaj3Z!@z%Q@gN|`|M@f4;wC$IZ&q$2vnwAtV53;sfDXQ^bdNslWjaLVDlmEp0up7b48k`x7} zWYRBwYm&Mk9RJfd2C?v(3<6)*j7h2c=QQyKIjy5fbqoApX8EJ{o5A zJ4=S2Plpu5OE^kKNp^|HqO+`IaT&7@Dji4#CvhjIDj~lBGU+4UP-tRysoP!3cnig2gUrm41ZuYU^Ph&9Ng1ycjSq--o+;vN{^*p;9Zt2Zf*7>3v zx^hIWLbr}vVlw2A)Zpbo5M5^j5qioj|IfgIj4z{`69fG%=o+@(&FBRU<;sVr&CRt= z`S54+8?i9wmdTMRg2x7K%nHdpJ=Wdu1#sdAqv;i0*{vVs@Kg14iQdSGr6PkuauR*F zd>~&>$is*3Gd7rYc^T<@0^LIw|6J_@VvbO)SDpYPre_{e7<-y*&?CXDo~t3fSZw~3 z&S3T)MZRkFVCp&3=k4h`P&9EP!OTC05yl}H-i&u~ETh;g5vDEh#77FP))^n-#j?2R z8O&K)x3lapapgQ%gZyOps2x5hqu22yu<%@x%Dyy0M9K)RtOVh2C)1^YaKc=V-Ym{W zP+IZ+u6n28ny7nK`zK!`BYJ9alB{D!O`C;x(ptREHfi`C3obU*w6JQUHNVI*f|}TL zXZa8CR^GwgZ+l1i^q}`X4zD zRlDJ|&Fcy%WwX=azK*8>X2CiB-Hu;I*li!{dKAzP%&VPGG*n#&W;HoACGvw;iEztt ze<6e&gY~#g^rJSo688-?MY|1c?@{a8JhR%YyRMDNR&i-x^u&AtXO=;0!{r{tl{|($ z1J<>XmXF<=00}P8`Z26PPqF*v>)KGT-5>V3fcGeT9x-_!zt^9+eI7`_Cpttrp%Qf@w~kMTj8z_iGMS5xec~*?LWoDJO@zWk@L~_^yc;pd;^YC6 zbGn%r@J@RsK+He{)Y^vQh{uuVbm?l&#(7cSI6z{to_Q)ggq!LbKHrEqUu-pl$IN8T zo`Fz(b@sN%>hKUV=WtJ@e!}i+>zBLDeP=TkHT;_@Wd#P-oso-9PaFE)>3FFtTg|=* zUnTG_cVcMDW{7cvTO3j_H)9P0Bjnt=84QQ#s~^2S2Iv7D2!5aHOkK2*u#uy z5b<%DD7o7;DbB0t{w3zM8oEJ_TP(j(htJXCnsXAACxDM`Mx51GHCo2sRcyS>tE}tQ zmrWM;#yMfM$ic?se;7NZfz7p>2-TFXK7CtqueLUt=$dG?S>>BV5msaMb?K@CiQ4E; zSwJPyY*cJ|w%Bn)cI1@Bkpvs5H@3Z+b@_ynrgdAlp0 zL~)X1BcG5vdQUr-@b257e=?LRM6Rpg{%6k>@`((!<7z#Vc* zch_5ZoCJo~I?Y+sN9ICa8~GF7@KmRA%si{iUhKLJ{HNU?iRQN= zYK=R&-gS%|U&TmPQO<2Ul^7bH?VL2MXn_p9sN!BAJXmhKxp|~S`S>>Mwaaa{@Y$TJ z2qwZ{RXVD>z2c%_FPvz%DHcxJE!g^2umqm7b^g4}p&f05R*{w z+1@g^90Bs-yi2mkK>PBs%yjbGp?oyHk>+eURB*vd?fP&)m8oz~M_jOd(hR?knNO(EfJ2^&*HOu)ib(&i(K4r%x#P^{0@xSabtA z`Sz^*AaPilf!PJ~Y3Y-%9fP}90Hf1kSCp!`NHZqjcSsL7v>Q=ZGOs*C*u~D(wG;aQ`vaW`leJbvgYMxE15QY5J1}CHpqD&=x z3TYL_UAJcGO~zy=Ro4B0d_PnIxwsW9KTDFu!H(^~q?ndbgv)w}09B#$%Luzo8Y3w~ zk=QE5^!SiRB*YnXX#_`mfZ;<|I0O2n$$nsk`>F`{*=3&d>L#mb=VKwJA5pPVWY?Sh z7?i9lKpY4z5}VM0jtOXzdHG!y6>8wK6LxeO7LkJ{%wuU~!ymeR( zFXG^>$qjUp*>5Em_^Xuc5%1OuLieIO2j9k|)XK`!ceYRGldDgg-`9-L_^+=ywAD;C z(n4;mskX40U41N06KU{|pV0IHm^e$bUR(modl<`6zgtkd&?YD}2?cG+V4ZNN`_&{Q z-iP*n5(8VS>Y0Dn>q+xKXdVVFN^#xy*Z;oHM{92T-b2y&2Gyadx#q8_NZ1TYnM&~~ z)E>^?B**G^eh(KiU`g13CHp6Ly*T6|n3b~WBbimi7sX9#CYbFtvI$-dj(d%2TBX@^ zy)7%OX;j??>r+Tg3+D(}k~}ha+!D9*P~9qnEoCtx;0__r@u2MhrXU?3$|&Bxf;;F$ z)lYN@#gia6_i(FzSd!Rr$@8T>&ks0Q1t+>cZg+RMd)MK?f93W~@c>Ejh&$4~;Rum% z5>~t6uKNHY>Wxq5rPhxpMcd5U{us*J_a|z%<|M;xL&Z}?+iT9Sd`6?Us66+!S@bq| zWsXO8lQuv<%oKXx9g4bcV1?MCJ0n~R!1D^64}qoY*TXhDjQl-75@GyvIbU7((l8f} zmIH$gGN_&iZ21H%YcdD+N3c!HSa{+1<~KZQA$(2y+Gm{_gy$riJb_8Gm55Y6KL3&G zE29t+;t3pf5uieQRgl+1{_<&u`utYgr~{D2^jiUD@uD{jE}9sx)6^%aZUJbzW;A2Z z)7ZGFjj$0W7Up$EIjs3!El2E)cJ4x2llQ(~^#%mls8@ykD5VdDZa6A@sfBY7*w%7x zBpk5qnr1UTgRR7*R@9P^0ku4SClg3C4zbF!n(%lN4&TVoZirQ}suZf2T@?DiJi!dd z0tbrs8rASQP7iWdyxj0sB)I5VdF$0M_esM9bi=J??I?z$Z;*$IjG@9~*!s3M-g>vq zgm#Xvf%W*U@Yg=HkAl_seBsDtj}Sxi=oI8~FO3@>vW)t~Z4-LbY()*fnw^WsBA>^y zFO78(DMR*>u&IU!*&*9%iSH)#=Iy|FSzxSD0mPYb^j6DUJe#R~Xvr@&N6kelpM|bw zh{1J)Eim#TK8ijoK;Tit1@qo(x~*2OACKq}%LmK(H5$0~4JZ-M@MSXxcB=n+3qsEw zs*mzol&7NB1LbeMpanN+Xre-@xkUekdNzJbl&=Y2L0VMVo5q;&3Am9=XO8Pfz|TlTOwoxi;1mDeZ^z}{0V_HK9%B3iy_38`wpzn% zvC})GFZh7Rz@T$F?fT#^M|-=&Y;48t|3zoWwU6m@ZSe>Y;pj3c5$z-PLuaj4I}~Z0 zBYLP=r4I7yH1H~7tlcGEoy03erllVc@#)!FB2I`T$2${7XHuURp>}kbK4C@0-s@L7t0WY-Omp zZGQYid^X~;80ApXi>?G6smjGqc#)!r^=R*gns6^ebWSVllIZk$h?D6E4gWNaXp9-AeIy{`k5QRdv-BL_zS1u+)tyQ@bTyyhNv$>(yEey!Q3o#flL0e zuY5+e;FD`%p+`!jB6=kH5L&2h{^$M5CnNuG~>i9TTE zWkH{%u`4TVxxQ92yeuA@;$AHZ42t_t(YazQ@H1nC{a} zLDuzwSjt^aALaFm`wz-OFc@F*#>APTceZ-50oZtbmfo!MsQ)0grV1mv_ZnExS@`N9 zD`c+-zE!q{U~GI}?1ja4>S&}G$E)cKbq<^(Hag7ny(ek|I}Mx1iR)vNZq_s+jeLw zXb~38kTuB;bupk?YAgjb$bbe?Q^Q0fO1(=!^#zX^(CcMIFs0l?@f#>#`B?taTaYX! z8H*~oP9<_%zGTut`I9y~Ke#|Y4%!pvdk~MSpf??+AeEwEJ!zsY>2#fu2s2f_ zg_8=`ulge@%1u`3^-B?B*Y7zAtrGGdUto@mnOE%ed_GAupQjMS$n`9ImXO9!@GLc1 zHP_=S=~gh@XfMg%4X5Tu1;Z8k3Qp%}D7LW+l0Mf()EVreG}UngneLRas8h*OTW?a< zMkMi)$6*>Uq2(jV5qM;(`d7bu_=~;~UNHKtbOEm4t45Ia8H?uj2(W!Y zI;~4W_spP~`9_jQDPVMT>D*j+4rjP{ikRS;dtMBFYA!(+X~_!vYd8wL(+2qUlrT`c z;_*y^q&kkmo}KxgUn$1yl_NwuZ7P%?+h@`SJW+ zouYPgQ6mD>mrhNKy|xw_=^BYOz8c!9G}1K^H5%14w>0`EI)xe!HL!)YD=qAz+4ilS zVYJSjIzCbRZD14CD-A584fBJ~P>~Y}lro;;W2t}v%f9s-*xyi6vT*y)rw1C|Z*#o6 z>`2F3JMg^H#M=7FSdTf9O8aW6^~$HvHprcbw>!hy3lK^@8C%F*q9By%_tw5G#GY6F?udbMTY;~%_i3M)0M14WvlcSZ(#>Ph4gc4-#w4Cw!?~}(yw_hLV)o<=$_26q z*oyAV+!6w9y4L)R4|1w-!?R`A7jR(8)VDKmQLj$)#?-VuHpJPYwq>T%lSP{l=&HI+ z!_#iQfq>4Vqoql^z#|*#o?12Ij4V<0p_TTvFE{rr0;aB6kWfZ zsl%lQ^^MiukB#)Be!Oce()jvJr_8Z6{fr)5^DHgRs=uY$r!E^k>K=~R{pYhV$nLYI zFDd_7XRoRJ`3OetI;7g7jmKI|RE@e-QOhh*J+FIL6D{?c$eL)HH4g2EjM|=|fv!g4 zLmSM2n<(W53Sp_-zYx*x=IP^EReq+A&h)GVww9f(wy|Pbm7R~y+-Fl@j10X>Oedpb zR0V?9iPGBV0jcNZf%UxHz1%PkWFB)67wR;eKn@kS&tP5mMw}cvGxG^@a>1g*a#cna z8mh6e*9Ek*HL$TzuaP~*7l(q^f<%=ER@zs;RgHz4y!CV!p-~g&Re3A62=Ax)otaMY z3(LsnPlwmBuh!mWI#o08O68+bez5;7yrLd+#=DspUtjn-0u?7P9I@n9t#BXIahVNY zfI(Od|D8Xj^rd)j>O*JP#}jW#K(kukGvAQpwc#!*Ov?GU-+z)sMkuMqhw-B`xqpjdVvsw zAFvQP>*>)cQjU)8ameto=s9KZdXAn|{0g|={T74z>3%6ednjE@xDbO?A;4xDV>mGN zyc0_wGQ{YJSd$0HCYk8=_6xp!BRMH{qX%~Jp!BMP(09=6p%~CB81E|L%B43p!wSQ# z^TnA%KiOn_W)SufW__*YUEo05eLzp2MQQ1V%dR z`cmS}gEtwAk98H5P$yTx>95@N$s5d@iK)8Y+W1dD`>EWGY+C&0D0u#ahS@)KWh)s2 z+3laXobflsj z{x`!kPZ~FU5$;$2?eK(upmo-{3aHI~Z5RR#>gszme@D~C)&FT)4{e)|chR+NPMXp+ zZEu^SHM5hcty13tYXk^Q9pWnaSh5SXod=GVXBlOT{ApL=0LL~Nw%AnbsrgliRmC5W zBz0PDMt0JmeT$PPJ@_S{j65(Bz z9qwBaLq$ZFGO1MYY{V5zA!Yvr0j{M_EVjFm@KRuo%Gd6O;ra)xN%v0IyQH;C(S6XW z_!_KbqH%@9j$boFV2irXv3(9#+YFa%{$E?+CO3S=+I|7VG^c%1I%GN?%LhD7pI(Df zy?5}DpI&*w+3`0OK1&4H?r}4EyorL*(&ImmexMVg#3Uc(#*&CGOe(7MiO(R{9gI5s z{=(>-SpS4Kj#(HylQ``ajp>m-POy(=_Qemjj21N3U~L*}U67L6?)iNS?_y`I^-cM? zSxR$^-Kg#ze6G&qBGG@A=A-d)^Da=0x8~DM3QgwSTg7!`8L3A zJ}IPELn?gh34<`>CaGv!7znZY&N)CIfXA32ouwBO*&D@$kF!RJ9!uG0$ARo-30KS- z?4v3Pcbtzz$9eSV43m08u@~sj3G5+4EMza}!ahv?uppj2&LO+O-v2P5f=xj}cfFTkDA32P0^=Q9s zEkMW|BCj669C6S+805F)OF%odx3Me;Ezw(8au9nf#16QBMCh<9Lf;ROpFO{tTw$?$ zJLvqt?FNBr)%n(T5DU*L?7M>(Fhg!bpr~U)GA1pfqdxVUVKHQn=@cDq|Dq{6rS4#| zoI4>EyWm0+MbSbzZG7k!xQH8+|Im1hPALNi4IVOd*zge})o{VoSwWMbL2!tvN%$@} zhHGozh^|&tE2wS-v{p39rYWR`)Odh>3zP-G=e_TOrm4NwMNF+i^ZKQ>I5k?D8ZSla zWi|7GTA|*8pwV_*)YHu$uJ&Uh`^W0{+(*(Rw)?mwj=VIVG?q-C^z^g2C#gv}DA|TT znw9$llhm3>J9v-ry$`niNcTxM>A5)uv&_mRCYJWAOqAC6sBH|Ip{M;s_(QrPMcyRI zZX+VOB9@9I;l8kO69kP~Dk&06H#?pld+RdbY21hD{Krmq{i=GDw9AF{{t*3P&AgnV zW6I;a+|rvk#oKNJ^?7;X_hY57W(n!>+=YlFkJDjAuUDebk8-^M9g_|l^QGT}ek93R z#K?=JhBdPlVJ8mBoNW6YI1TAZ!mQ0V=L6Uvon+btu1{X}tHOqi-hcAV+$;^Je z9tjJFNkTtz1)?1n%f`jc%LR`M{@@L7>y&4kF@N|ClvedgdtPC~WPcyIj8kag*BEt#u*K zj>4;a@I>eVvMCfQHe$xOeKwacZB6&sX-|{jrIlH)wbqdvQ8JM=qi$S<(HJJ`Pdcq{ zf9l&F4@L~LD>?&@V&NVy-S@@X8K)fX@u$|oII?{?gp=QX4fcgx6oRpIHhCtobm}lB z;eRO${b(V9-4^-=wa*70?+5SuFd$A^xF7D_1Zi+I_OMcC_=D46f2U8;eh4+$RtPVL z7F(`iF=f1SG3;7w%li*0+l0qVd%Im2Zg!qdY8?ckUw2L4#0dFbzl8I2%J`L16YFzt z;3LB-e=scg#Ha}BKKe^cw%%z^XOZFV=U_zmX?64(M_97QDp!8T2Q%0x4!XiRKiIw= zs@iS&xjYE-=O}v;lW5*7mhNt1htDO}{_MhC)BW3x5QPSU(XljGNAI~09uqJpPbK5W zrLP8`gTQzQ(RDp@O}~VH8sV&UzH6&!X6kF3@-3mO3Ambq+S2fM3j(rtll34Ul(~R` zh4qt1Bd}gWh-pjOwWN*eMyGC{UhA^ec>C1!aZM>&BXIbY4F)x5O8JKHl_X!k%icjO z0_vWzV^6eoc?fe>+wMClB#e1IDk>jdABAXfA9^3Pe}X$$or4|BkP>=^IeUy2(W&cT zej<-HQTIQiKO}LY^6fBekJ8g3NKC8_z+oxjD(HRmbE^QmoIx(*hpl$h`xf)*SZw$O zBcRTsf%E`G3TCBP<>sINFLU1=7v=H%|C)WCd+xdC?l^imIto~V*n8};#@G_0DJGUA z#>5zniRqyThy@T)il`{k6cj}e0SgL<*suaBf}$XzqA2$Ao9E!*DnjykeP2KSkY{Ii zpRzYQJ3Bk`o`9tgYP*-dZ*anpH;_pWiq@G`f%n+BUt*~Q1n7H)P(VjJ3pPh%5O%7q zUD7?+@7#2sDyTKj8p*!|TX+bBm{Dhz!9*jgI)+_8>}4njn;IJKv(4}W(M53y^v7Ey zaEC>=twgs(c<0OJ)xMAy01pTXoeKLOul^h}(!1ksL>LxESe=*duTBqKv zoTB=~kS4tavD8%68adR4HrgVq4MM70!qo4VHZIq{KMFm?dCIt8T_7+@5874^x9(;d zRE?s{P>Z1AxnpCiw^m<;KDrk4sLqCJL_T0cCxhGq`Ay)iYx03wx#^czztH%NHGjtz ze?=9gROM1zB}IygR@%a)uCrT>!&}VSq1#(T$s1}Pt-qi?3d+)3MxzB^2Y#Sb20{H& z=a95|RB*srF)({o^xN%}Jf7lWs5Qjt*&2`?ZI>~_ql-r`7jgA|FyHn5Jp)8e3=<6d zCSMl7Y~2sny6;6CMC;?C4_pVn?EiTHlWOrW9SSgMA%+F_+4Ih-5r+oW!M!Q9B;_TT z9v^?*F8EtCNN@hD&Sm|GbFiW>1;g%IksY+M$&Pt5E63zA$4&VBf~)(TGYK#%@qvAx zS&n*u(9fSe$^hdSX(@U(=34Hs?nlD=I#tV%LeMX$qwgz3m;X4zpj1Q-QNz-i9oB!% zQFMusA}mVZ+sw3%=S;#T<2|T&`szQgcVND(Bv#J(4tFW}ITr4;H$H~G`)kNB%mvMb z7_Oub8zTc0+3$npRfTQOu^UO4wq5&ET?kN zVODa8x=62l4d;fuc>w0`Pb{Z4*jaMVyI$|?-Boa{%N0-ZWF+rg8(jfMy6&*_?r4cn zk)a9T`h-al_&dwFGr!m?Zc0IM{|8g*BXAJbv$rNFXnQ~JIUg5Z?xhZ`OgW>~=D(^r zM=Q%5>aNsD)k)NXB+=PQjmU*G>u3Iy+?V3Ksi!?c!hjB~g(AV- z=>iW-D{$?*ExpbHJG#K&M<2@MAe|s!Pe_kZ34)jCbZqNmDCAoQiVaN~Sx}T~O+T2+ zrVHrwB6x^EbUbXyLD>@}i}u?<(C9dHH#vs!g^#Hw9u1EM!%y&BF_bM8H82^(_xCjw zWy+C62a0b452!6Yi%FBE$=GHW89gaezD2SH(j67;kwq$kO zhe#t$O3@g(jEufU(XR!i;DxCfM^8Fvvx5>TPD4Z4#;0yEZ&yUx(M_z`IETLa(I}YA zuCQQtkzlw#PI&n9iNU0vHKaH00YRiqGfUbwv817N3l(W&cM7&pMPiXZIc?B*FBP7= z4YwvVQg;tID?qMX{$1?2V@yNBhPhiu?B(*LT)GZM-3s!}cp{=_`=ItqYp|~ta<58o zUrYV;)afBNxOZ^T%{KNhBF6}wU=kk>Qrt$5dc?9}0}fVWSb4VD-S(NDV9-4{0(d-w zdhJJZsjfXo!YPt{irS!#+$m411Tc~Aez~;KAW5%vYHc7a>!FauM zzfWP2`~ZWsUZd&#Umb0?IPPx`&(YlLO_(@o@{|uhn)>my>7RW1+2=F9_;Ti~*>f;$ z``mBmefRzR9~S($aM4eTfBt33ufP5Nht6l~Bx(t!W-EKs=CkFFT$Uc~TsZ@D>l#YU zqOE^5G!eC;rjpiJZc$hJ*N2vEeewTgd$q4t7W91m+sQ4CkAS;i) z47VsNVz$@*QYZ|vzDQpP1M`M^AYv;R^D-mJbTvcjyA5o7F1HVHRx&x+O;IxA!CW-h z0G{sqd*Ghm`-k;Ja$?M(GRW8tn1;G&JM43=a@gtzCq3be;WScZ*D*f<%p%Xj*t|H1 z;?aTo#Oy@G0SB`FnP9X4%S=334%qNK5GVFpWVtXWjI7Og%gU#qO zZ)RHk$tuBpnD=tnUNbV?t?|{C!Gl6r=?zIWaB`7=px2JOe2y%@VoEcf4xE(I57REHumH@;euQ}$-KeHsGW?^p$r z$~Um149=Z{(peOIV7#DQ6Zm?)uFYnvb6U16H*{Go+SsFw%^PEv+ty2eYd+vw=dSY^ z$kAf)P`BJuqqbEhD5rW`{=R+wYqw}wBDAof<94VBi!GFV6U9YReNzTli3^RyxAPFA zLc;&qy$ov30hz;Lz}|lGGt019WFt3fBUNTfxJO-#^DvYSf$*QxuHs~|_@$!+$)9Lnza0rNP= zRSdfADBj=r1*$>y^3}5ZkBgvo)MlxKJ2$e7+TK;M;}qduEwys3TwFuu z%wj6h)@^+Tui3q=kKTG+33+UvBmBiBgcNu!5N>p3H@eDluE8O|_|45^r4bKuKny?e zuo4wf^c|r;@_M`{nL^i7LLT}eb|1v2!TZ7$)+~*#uuo`go;>>o36`>ZWO3j}c^KRh zV78n)eMcJ&xQ+RMMGuz)212sQljt-byjdPT_>k1lsKe=jjC3D!Mpy;(wZ&>To^{6I z031RWp91i{33b$6I8HOqMle=eEP8m`;91+6Xh?B>VBAA=sUz);D*q9hE<1n3`#)ppS^evU>j{($+5J*UB8RsSaCf?V#R6E@c3H;-0q}8RIjrl{)RC;0EDQbm*NExx=R`( zGt8k%d&Xm{JldO!v=Z6ycmTdIns@Lz4`RKp?K>75)6K0_Kl0_v^R zOHI}(wfn1`0k*P(zGk}V>Qt4|zuph2Ee&ns9(DPz!WON&)Wa^Cc zlFd(3Owjnp?VdQ^bTIyl*BsBtREEtHGFX1BzJ_a~hRM0w?NC66c0AVOyc0S`;Hj(D z!M{>Us!WO|yoBb+^M>t9YuczyX7!b~jon+-Tz4a#RBfUPg*()2e@><3yD4rbg{z_# zn@3CfM+!t&aFyOb6MEfNus}rVb=8;xg$>>d%iPeSyi!X>D2HNss89_mLr7!t4bus0pp10quAu#=Qf zDTXG>2Q+?$LK)ycTk%#q(t&?5QZWvTM-U+XEOnz(=zGHO-%2F?(i}d0X_wlMIcXrP z{Nm9hHm?+x;+HQEgcR8)lO9phE5a}X`qds7a#)l)#$zg*I_5e5snM#5ab-cTI9He4S|wfbTU{)-MilE7 z&`nhBwZ4I(BD2*l)e;^k3Sqj;92j?^h_Y9i5^7?gt#nl2^>ooiR;Q_#{nU)zx!A0ua6QQCD1W&AAa+?r_*&^HkDo!#4uk!hhahm-!f5G6r*QV_g6_EkewO*Qi!mP8^zFfljMy^b~OWSkj5)Xh{XDZ$!mLw`11Eb zGrWfGH>&V}P2KlE=YfoPan*Yl7~@m~;~aC$urpd4-FDGosCpC#v)IYk%u^97=LH)+ zff-nA#)xZ7$_M+{4f_4#(h*54ap8J$i+@0q%rW;(?Gnel#2uI-%;+NN{C>wxk`;Zb zoleTOn_|FtGBw7*_19-I*I)O05Z3q_jmJ>6qs}qnj1JN82ICb&C09RCKrgj@#E3{$+nnZ+pmzGMcn9h7WqxvnHJGGjw3k>lhgKJlFPlcOBq;Vt;=)xj+ zWy)?5=5%KgAW7)HFxa6A^UZGZ<+XvWSMh(+Wt)KUvBmR13KGT}ei9%xp?&A3?FXiY zjj0F3Qw$y}Gz!8;+b&rP{@s_l@UO(scbV+b{83yZlUBCZWK-M6wCt8A$)GD8g&~q+ zqu2Hm{h`WPxdSAA);@ z9OqLxLq>mbMaXR21?CL>yr;=|;NcasJJX9rUKV>Hr#*)97LzXi_9AHpeOqvyq}eLi zvt2hsD;y)~4Tk>)ybVSidA}!=hLWZ1C6UN|Vd{N2kA5Lt;}YjgXUF< z>o`Wf4e~1BZs<8iRzZW+!3NqSb&7hanWNN9O)WHPp{mhWP^*OsLFKY>(r|a152LRW zc^N^RFw{7YB4?4umWM~D4f8n-hCTKoiu*fJR1dnu@RTbf4Kw>}@jW=eZt)^)?Fl$L z7*AVl&v(8iVK84ExdyKYn?vV2+()c^Nc#C8axK;)j20b4Mo9Nb(nJ=RyM)C=e!L2n zNx4hCY4m5E6(eTP|NIJn!zE9K7F>hKSpUhG^tpqfH`ENo zbb2Gs(vN`lmp$g~J;7sq3^DaGjqhZ!%HaS@FJZg&!%*#0gpQtjJq-3wVE6X8fHK~8 z$$5IKCE38(dBBou62v&8mjsz2ggtXzEk}!{uJ)*vn$%&v7g|b>idL+y)hJ5;HNQ$L#z1`p^r(7T zgP9s@9Z<*6aM3DiUnnIps^wsDBS9 zZ}YIxAD47^PGM@;#mAY=UPOXo>i7pJrubAP2ce8&OwUW6Q)E>hY)%KsPQlp@4LMw~ z#SzN+DEnx1&r6+LxCo(yTQVOJcEz8M>Fu8m4!guwrR1`iVVOTZ@SIWLGBB`o_z44F@I-0}zBOW^oqT5+ z!dvbTEEBBW0C-e}FKU|0%v)kywi$?I8~NZ5Znd-9Nya%2^OIIuWPkVas6sKVE`)NY zGe3Ld!Ps$;pA4HVLGZY(mXb`eL{>&k$KvH6K#_(Q86P0sDe|)um!Uzht>g881&S9E ztesjwc9>(%+fH05bEuQAU9h13>xZ_Re`S2gLHx9MIP3+x`&RqGI>{S9FsMb9_OiXD zL#5v3( z^Hpy{mo_x*tS78YN91hl+=2f5>+gR&5SPdcykRN$fG_x=SC>ELx6^nlKFCuUa=nR? zM^aok6)$4wtCUb$*h@%94a&79!9j2q!Poco@piu_yxvoUfZ3s6%t|t>7O=G-&9oFn z!<8RWoUgOxn3ndiJN<$PpWQ{xZnfCRaPKgF{YpUeOFw2rsLUvV3V;LR+=0hf(Un7R z@fy&3dn}?~aVrQNU~qE;E8R|AaZYxg?Nb7SUJKakMgibYOcd{d8zfZaRulRab3lAe z;=b@9O)s^|FdG~_H?Z?wA+e*;`_a$Hv)5~}CjxWQakEmKV|py-?m_``A{nYDF7mfy zkN4UMGLdYlxYp>@)5xo_WefIt?qytRxOi6hMmgaAoYmF)d zMx&(KJT<0Iu+iYzi~s0C)2CMFdM5^~ajOP_KO=`)!hYrpKv(YB;s1VnnQ3Ytv zxItaEsrQGhRghE%(~`GB6hfboP9cJkNzzyu=f{<-f@6zBjrwwPsdY?%jDR{*(9Ffw%aF3ADO!C{Bv-~sNBwpaAuTjwX^HM z9RhVhB;j&9d1Iio7o-@+g#$uIFY@`Z?q`AI=xKz07_5jTm1LcO#66J4I^XahuBP5S z7ad*$5pH$t?F2aQ?z}A)^KznNNW+aJ1C=l1mVF$=$;0am-shB~AWM56C;4NUt7(<| z4yOR4$+W8cb-z*Zc4dF&33HlS)bP#-Y>Gp3dS)1IU6L1&$5xTXW?M($496wx+>{Sq z_3gOtIJ}vVHdQbaFB)dK_XwQgf%?2N9)tWLpl@57{^{dfef0nTIGJ8B_x<9e#zlSU z{V(=%+hknpz0}kthI&c4|J6-xd8hq0J)sfGO6%5I1YO&lR?AVHcWWEsu^K1n`1v&i zOJh;Dims2UrLC)IxS{rJp?LLec%0d`u~1p)W%ciFmdS5 z+k^>SK*^6%Tn_a-244Rj39&uyV2siVA9BawK1qy&OmwBIp3?pfY_d51434p~wT7ET zR^p%z7#DGfY?4}bc0E35ZKr$@DL1%6sH4z*-3#z-KcfuxXTUR5;cs}7YH783zUX8p z%Z@46K@e>u=l1oq!L_7Fa?A^t3@&u4JVI`kz6SQY-$V?76gT%Rk*!G{g|>+iE4Z;~2&bV}U`=6k4h zM&||M0Qn0vUmqe5Il0q)+`c*beg4uhz!PHWZ>l$7`UA6S@T9ws+-7x=o0<6nPyZ!3 z3)V06`#fg>HcFOS4?AqcHT4y{u7sD4n`RbGinPpGAloGM`~2*%;LrAc@`6f4$OLZw zhJ}yZH|&P?%LI})OdRT#iKtUIf>Y1=&*vU?3o$(F@Y#A-x&ukFLBDTP`k!L%lbtc1C7|^lb89WCBf5awek$*@c z#=K?``O%*;NgV3qX7W4~Ld0uHsS_X7lG$lrJ+QH!#P9PfF35UUhe}TgUJ9yUbay|I z)hp4^6ye6rFo9L&PFc!KI!eGMF}{<6Ar~Xn>6`_x+5G{~?TSk?XX4FHyxrEY3?h0l!|5FAamRSF!oY$)!c+@{InO^WiW)ki(9vSGis;C5H{cj zWYs`>A??YsD+R)XXh3E^3;sE!v1pOJi-GdRA8IhEK5)(ttP~^ze*pJy~ zk|PK_h6dRYenS}g?pvg;rdxv`@~5;9<}{&JO1PbZ3G;B*8R$W7;9Ou3 z{_6)Z7Y&;RV-_55(@ep~=$9J=RqtYe-v|6Q^Qq`LwVLyU;5x9s?*Yn=Bd?C$_h-Ra zQft5k&fHQ2D<)U5-cuq?=b#Bw9J8Q%SvQ=~kKUhPeDrU7=R*7PotUut2JCKk3h{@; zn6N@}X*(1-Kad_}C28aoXSL1hfn?uWK2s!4?t?|(wxXvvkrZ3itmQ4H$>Npp9P!BU z1+I>+oUx_zA2Z`~qc9rgt2Vpfj)*m9zse=OU_EFe(9m0McThrfcN4LA)JoHaN zKA7!Df)k7EN$(mDxINZhyfX+gd?6cM-NdV>#P%ZG>IGuA9o_2M zk@zf!tr@U80K7yS4CbXETv=rIS08SIs0nh@W5i4``{xB)isLQ1@BDDW^B&g9nLjUP z_{U4hj&5nWe8BG^5^rHC@3?$=eiAtE@_ff&z5U7EPu_h#c%DV-oUiNWqwNIT7LdgK@^uPYvX21 zC$p#$^$W!2-(KTN?dMe#bo|DuKP!K&wn@@1=-f6n5mgS+G%j@=995}^%*&L#nBp!_ z@m5|7$?@+VT?5Ck4*3;eeinoOH+0JCmU#y53A4H#@AN>ptzCMb z%+CbLAI5ijGR2=e`0HiaHEYX(if61}jCmID>_~BY%lXH{U{mi{Ai+OvD}bzzU7yY> zTnqNmQ#M*B%zHDTW+PmiiFT`~ZD6T*tNBMJtm)<3J4Zw^Fqh4nPj3uH=ZL;%hAp4> zdKKhG!Of4%d;9DMgOa~a3mE<`1yQ^U)audujcOMxZ(r`e`1Njx;N;FSh{*7&3eKH#8L9bA6)l#WN>z8^*WmDYhX;yG+nzi|5l-!5n zymYhopFj4x!ZDZInN$yED_zXH(pPNKKRZCIZFh=~Ip6%nn;5RKVHDX`*;mt(?}9}w zlYs|}whEGAI1vVMlcG=pm`klkOz^>%m zBAWHz2wIa+uN<#5_e^7^}-+ZNbY^n&hnNg@&MQE^3L~)|-Y^-Thh- z#iKF_tTbmq6LO4pg*N5wUNh>35gm6>u|Dgs)loB3>XEa#aS2x@k;WtrS_kCHNKhTO zPRHGW$?ZtYspL=8H+tDX+6A)_#BIvsyPOkN3OX^72( zo|_wmMjciP?gB;$81V(eaH$CW8V#iZ0;5Sb=S?tM_$@J?Ej&P$4;CboA_LY!@dKbU z&5Q=T0C&+LOHri7PM}{2ju{0A{4kLu@4RTCcur%lcfME;qU*g!8$E%3zA)>e18KL> z{siKpY#5~oS6ppiMC^vfP*VQg2)%fO^5OWNXzcjHMtWi3F`9g}4LYnCmQD@$`JSi) z^Iep~7U9;RI1oJsxT8bf7HS`wp$GHMZ1iGwR)DD;y>;|;^iVrb3_~H05+>7lNS{pe zzIFc@C{fQZtuy|v6>Z<%m_YnF8{ z18P+DHK7$H^<|@}qq-jL7FLaMl^U{i7 z4b6u^L6m$Ig-+w+OjEcUrU;R{#Y`L^K|H(ZPUv=TNhC;m@^xV$#QZ_3ol>YHpEAiA zF!rX%*cY{|dbuLoXOBpjTV}P1_CEmU44!lNw_f3!!5w9f6 zVVmd~cCmUq9n+jpa9z5avdV%n+l1VD;r?|%T*c?dJ6Cqu`>TOGg1izK=h%?v#tt2e zEaxbm=8NXf>(Yy4KEg#2_<>>9r55xolF4%8MoQHEY23(DfeMS+;&jWYnE&B`=xzXVHszp%OJou>QRy1kXzU4+bYjqN} z4Jddx(6j>A5cw^_-6Z3V!CEod20La6lUz;iF+OgXVfiRIB)~LS=$?Hn9X8n7Wm&T2 zALN;A%EnRsbLkc+zY5=uc#{QNWa>unU*2p6gEGNM3AwTbPK%`eTj4#?fk_2$ie5#n zFGfrQ(cz2eNFFAow^OI+T_nkM((IFFHhD2nftvO&zDy~fE+p;=#FKKd=t@7*?)&va za}has9V6QbNv=lUXHm<|%IaY1U34geC+`_1!y$7Kc0d)r+)cQ8))YQeAe&p!{*!$U z55F&FNSRYY41C!Yo3MvhLLDo*$5gx7`5Ag4Uh)Mag5Sgh+s1cy^qrAj3=dql&tjK$ zIX^Du7tx2G@l9|Im1bGq71mo^5vRck#>jAK25fi$-zG#rBr$wkg#O_ql`**q#1E5} zVK4-Lw`{Ioml)BGSuvDi%>D<51DZdvX1PggR@J^+)u`D)qIJ#nx6)tJ(qRQxt7m_u zoQEwhyIKo}jzvO~q;s9nB&se7+Sl5v15$J^5L#63jLJHZOUVyY+(86Y?RYr>&FNX6 z%s-a`X%<)tBHA=i;>97X;T&f^%pg&WmAb^H(@+KxZjQqOf=1KXTLX(G?qkskho_oGe9? zf4#Uyu+iA~?jxv=hCzK09yRNeoR4qchiqXVj0azu1XU=KEw>Rlmv9O~2bP}&v&}C7 zSE9)WjIuuYA*%rPObv{LZ0Wh*uKq!|$(;{4Md!f5iS~a@9VOn_rD8b=$o$=|MbO=3x4``@wW?pF!m&J4CQ*E zWBF}`d}&3KR>O~I+)JaV+w7>jQ4_W4LMLbV@``*3mBOWF9Tz zTezp8T8(4$vkG6MlRK13bF=YD@Y8Je}fFseHlT&*||KPg?Dnm`3A7 zoxF^icv*aJ59IRUykHq_=Vh8GMMbkB0L;Y!hKa0ON~eO+N2FxrMv!@ul3`cl;d(8~ z(YUX6)`D};X-M1(YY=VVu66milv)s^n$}F7cc|Nwp=bQS`SmN63w%+{F zEqE4yXwIh3<~VOW23yvFf!P`_aytf&+JvL|zMLy1@kX)-L!t^H@-D1`-iHr@1Nj4` z_0^veCoG@azSL~la;S6JD!$4%M+vug;(S)4`YLOrm3y*F8Mdz8z$1PCB0-JWbf4e1PXRB_Dt zXiCj{2Vvx$cTRnU4=pvzAl*YhpRm0I)^MnvyRPYoLD%O~K(J*(%_huUb9c4^i^a@? ziOK=lTpu zR9RjpgE#M-RTx|4QiI2_h*30JwnOE7*OCypx6nmX2UvM~3*L0ns#a=^)CyXbU!Ao| zqVg}RDgf1qoM*G0@G+5N8pHb}Um#3?ddhL)MWJYgjUV~meW7pfN;i{AB=n`auU1T zpCAw&GpDm1wj1<9NNp4PILVjGg2b5A*qiX6WhEG=(lEW#(W>^^h>kb92}T_<=glC| zTs2{M;1hD(uq(QrMSW9^QTDy)UFksB?bIH!%q1MI*TF-xJK|`|dJ)_*JWB|@lk6eh zWW>T(22tPO&Si&R z?4XoP2}|Msfq0xj(s&@-wz7N*s=XsIDr)4Vq>C-PFlT_WK| zJ0(Kj9GKE=4eh@01WUZ=OL4Gew$y9XQL|ekMEOFiRaPfWg;`tS>X+RDzyo%07o0E( z`N%f7>BKi0{^qB`We|dXBjJ$BiVh-XdYJ2(KSVdgMR*7*OE`u}a`;HB0>eXs*J&`N z`S#)O6?y~d(92hZNql-qqv4=vw>9G9w+-kw&+B9Yxo~ zTQDaNQ-I!xsha__g{|+q@dkt%pM&g49Qe;3B-@n_Om$cC$|O^GjX^B?|7?atZwFsreMF8e)E=~ z;#K%dd9VNBt;W4S{(~k+a*g|P=)0xU46U4O+KXgsDOyWs+X*_>R^6nQR^0!IA-T05 z-8Pea%Oj%uQ~x_vW3V{%Y-v(?Pf&8i%*&(tzCMSZYgGcX_$h^8+4-jj3nWMW_O{@| zlsCMFS07bu0P8)2ZaRH1(rNoP5J&Y1?_4aAt)elrpM(n?bYos=I!I-TS?z6f$jV^T z1$g4#ed2V73MxAe?49{0mA+8m9x`{r%cb!Z3(FzR?t0j z;l^=T+vA*Jk>rlyo^!BLEMck#JRX!fZdDyfCLf=e3xNY%2jWF&WQN*bCH3I?J4@Srh*jjQ#A@bA;9197nYr#{J>eR?tIs zeJ3K$vMrJn1Vh2Nv8#am5yKu7Og@V$#$_-z^)H8FbWrqj!n;rq%sd)BYUI^nC;Q&y zeBgjU?(-Z3=)f#;mrNlp*iH>3UF^Xx<6)fwM;x`I@K6}B9WsAPn_cG` zz~s{b%=SLbO_n_a^TA7<7DGULK1Nwz4$qxDunKzvXXq6aBR>8bf(@-790e=pRFBs^iHo== zcWc5!H1FJ8`1JF%&)XONoQQYf@nv*xUT%W?I&fur_3Z=@1rna*tV9!KmrKPfIWX0I4&oZT#SJ9r-LrxOc*Jom9teu=MBvs zX&_Xa-4rxTVLx$GJA!$I8fJ2^;c-`yV)h0bvc6KxtDa}9D2lev!7^q%-#$mQu7ic5 z&f50%KS#Y`HDJQ@m;Hp>=fp8sg|Yp64uS<<`Pfhp9SCeK^;^A5oLnIR^6J5`%X7rO zL7zr`_ABb{F~_K-)G_L)q@nI`Jm9q7Xo81C7RfAI%T+{c+jSE&a=Abe4+8D zZ&pG8-7w`b|NM~KP&)0Rw8wf-Oneq6KC5fu_c)#juf%olvN+{;{85<=XV7Rdj-Tt~ zdoN#L=;*padG2@_)_V!k=zB@zW{gC9un2B)%w&%!hio_t~^N zoOi(uo0E2BcxW6<<#>WamdnhGZ{Wn-v3oy{;4mkw;+7#FNjyyCy)$R0zI!$rm+)A@ zz#!+8rKl9_0j}I;!_b3#atgWfUikbB$RLlpuOu7I_l>&*PWkjIC>$dRCYf{(Mq;ulpmb2*~FQhGQHG8R)hH0 zRSqfNjs7o6jwH4yuV+<=?eEU}q543!W{@Q%>me#_r%!{chUNp<}# zZ>eq(h2DVM(!Ay?A*|FEM4qvm7R1JyA<}Kk1~*(SBe*l9sWOFj3}CIfZ7TlY*`H;DH69 zS0CnaB%hfVy-Pt{JRIyz;v@(mnI#dP5)n3P*3=YoTwp>M2{Nz33<_fYd=ddBhe<89 zQpnOf%zis8OXjTan)HgARo#g_BzhvXOCc4)?!7Et*k_5VEHL_a7iOWb3S?yd)j(G7 zfW3T9hAxcMOZF1F@99ow4vh7Ma&w_?Etvm?5exf93jAE_&0`0J;}@}L4SPs-8`z!n z0izv8=n&pKep-y%He_OLhES!bUaQ6%Y16eSEpo4WTia{Jc;YHQLf1`HX{y)w`aF1{ z&c$9iuQgV69Y&P5E?xh5-O{s4sV+b|u2t8hw_j_V!(1TaQeQN#^RvgwY3`|v3(|)o z^91CNfP0`hR2pUNMPENfbxgIc_2~yZU3;umDEhXBdd62Dxp}O)LY1{WhmvPg+yUyi zC@d75LPNl8`3E5Z;4@YteneKrLzu1c$JIl!ejuKRtSotU96}MG-BG;!gZlX>BH`A; zl6Qy1y?YUzymFJkbm>Egv6OS!eI%w;J$9Xg)u!k3@uYLTnTt3j9-Na-!PpH(XU@RY zkrLOWF84>oOh35`%3lK}WGkDs(-X#H_TMBpX(Zv^I7A!X8ZX#5XvJ67<2(BHTGPoB zZXSSQ!%eXuc-Z%vC%jkq6v6jSUxNeV6z9J5HM0%${>9>~B*?aL33R_gUBVRe54ii5 zWpw>_8C)YvyZw|$RS)lvjGjIh8=U5+S z0^MKO#~R_Ko3|kA7zpm;QhUihet@{B5N`Q_xb(*W3^|jXvUCd?wA-63zYK*V#dwoM zhIuU5i6))}DZ_4!4FAS$wfx1ecWI#c+gks+J#Oy(j7{ljZ-4jN^AGL_xx4dWh z2B;qBvkZ<=vO7PXnO|tJ=B(1xHPSHjeh;QDAUZH&75;DnQUvKR%$Ks zONrc*py5M9c<9;Ps|1O+wS)fl`fK>}p%!cPR_d+L<2#qvyw)ofS=(C#L7juzBTB5W zk=1nvtkjJwv`?K4*_C(PM!Sd*^sh{mY?7+tE?;0E@%^gr*ROD<5HRRv&7P)4) zbf$=(0@OAtf$hGU$Vx~P|5IKhfkoc8t@tXA2{l@o2=5wXPYuq6FR_b)^>+)dnBq0h z`nxf)({0$%UC49(oj<1>fJ%nmHoF0q&-S{*UK+tU=wy~@;C_-lH5|fv9Y8j{|0$C_ zb%>Jh*;7Qx?mK3M!!BRwDm`ORN(s{}*P1gAez`3i-piNSFJh|NF9-fv=@2WflvRFf zmkisfYG>r}mAd?mlv2E9u4uVdJuJAzwk=h*RA-~FQXgFpnxIPaNIg1$a@uPwWN$C( zU)XflXf1I~gqGI2f4SP}c0|%#?3x!j^HIrH$2F<~0}cK9aQO>S1;k?ihd#XTb{ z&BRSDrsntL6+7z)1WBnk#bh1#Y0|se#_S3-lJ8Nk1XD*wo`E_`dxlHvdAJfDxM9kU z!_FPwbe?z0=rSykRRzJ5?qRt8hQRT0_$C+-^Tb|$aCqrQo_}l+_c@2^Ry>>|Pcggc zCE~D(R7pccE37b+=>Y)~DR#-*2HBS4C4Bcp1Zh*;V2neFqmRYvkL=NF8FQF&)rM#v zi=|H!!tDWuJ3{UHhIHJNiOzUt#*fw^`rAQNnmexIQ0HFidS&cC&P$1JJQ&<{io%$N z4EVfrI^!)y|Gl}F1is(nz)vivyv68R#9P3%)IjAd)-m&2C73k&X%yeCO*XoBX!g@A zH(JN_khR5{Rzt3`*$3z{krxy00?8uuo58|Uf~uip07Gx4Enc67Ldn5S=SLU~2&3rF zQbFXM27>;ZeEcoB0mz+!P~mPwmpbH-P*SWwKj!PEMsWE(6WDe`>P?G{ zkSVR+2!s*nla;_`^BieY=fuztJGZlX=!ls)D%_>_5yWf1OlY;_obxR{YX1iRBqMs& z2eO@=?);Q1{@La@?k#@#*(Q4KM57LqFgW$3cqvVMnO$W4Mu_6*C63;R!($uyF5hne z#<60qO;H(5uM>(qWLrUW(a`F~GAJ>(qaPXx_xOTz6}Q|}czJ$*`m@aj!pcG(tt$}R zy960-k&v(6@q--m0q#MD6N8camrOTsF>lPeNndcV=hhfS1LUMWWJzZx*4`Qz|&-8`+MzO3rB5&^OiT>I!cxXkVolRLX% zBztx4b#(fZdY*u=8z8td*JA_*Nj!j{Jy3KRPMTm%7~JlaIh9Bsy|9P(zH{XsI570R!O^hm{iUvNilKcuNOy4T@po@HTgSSVIcv8ba!R%?FTJ>Nu@F^nw7Buwnqh1U@U5QRsmF8x}0e55tq+ISSpA7+hc zK)wENw`yJQ7FxAX)tI?OEhHHgCq6nvr-Ia$(BhVL1+`ioibI=7T55w1f|e%HOi)#- zT6e7zM|Hu8ekso7Yhh^xBm&tXtt5A;0-IfsO`oGZK8%oL_8EMGdNTB}KRSz4jo!J~ zBO2NtYL{DyOP{JhkZ|kwgYSESRq^oNn(@Txm?-WD923sp@aKwKAc=s3`@y_%Mz~@) zEI<8a{QP&lLZy$o+B+K2c1ny!zL>IOF?V&cEPb@u?K8lOfx)g%v(WE7^AyNxdyq5a zhHytcm`lzK^A(bTjbcESu^sGXC;DK*%@u#uNlAqH#=Q{O<$ZD|7-TOk6!VAdtcNj=F|O@LgjJ`FBkpC0Pz- z3z*uY5S{}(=xbuPoDA5$2MkakAhqZyn%&)MP0bo50!Br-u9H@K6RTYy;Xm=e%7n(( zR|*A5REPNa|Da4j3=!R$u~IImG46jWw28;H8tdS39i(P1*ScyQtE;x+&)l`tEu~Co zyO_Z|XDa(Z6@kh7oBr3^wWfayZCa?(BQ)p!ameR-;|6@_JAUoBG8q@5W4x4GthPU) z#qnzJ&;1|V^grL?tvBHIG%HUDlsuNg6gww+d}Oeu{i<*Kzgl>8!A68M^;-przX+c$ zWUq*9N<>*qH9goi{BLiG8tIzxmPN}*Fw{F5BHYq-La*HdXCdERl+ZC#*x{;tJ!}?| zyOz&H;e_2UB45!76#a}5p0Xo!o}F+12gU@ORZ60Y;BhYjdcq%{=74s>3NHK%qu2>{kM(90#sxhP36$E~+k`Xi(iV_3h#kcLVr$ksq|5~~h zz4jj0LFu4m(cta*!}9^kbU&KP4K3f_>+C#aol885ZC z?2u?U?uIRz>{e4YEVQXNW-9~cC?)eunzuDtgZxHoc zKGH|#*R^WIQ(KJ(Rg6AmhpE6mqz}y^mni9zyU_n2{Ud72&lVf3p7kYTL?5N<-r>jc zoe73rq&Gen2}G~|%S5!lFTUXanZDGLyLqd(UrO^*lUR8xTdF}=H`$LG z?NlxH=1(JZ>PyPG{@>OKJ-xM7gtilOEEsJgX)P+6g#Xh8Rc)Z!J{o#@v~5H*TWb!8 zTA4!_-6mtxPD-9gaS2q6@YMmZpWh$|T?TrNzF}KxY4&uXJS zBKsRiGXGfyyI5C64L^t1kOFS=C_(2NAU+m|39>AWD+iGKh)y!y_((hQsNG~D@wP`> zN{GO;R1o_WabD+`6A7i&V9&Qw3d8A~#~WBBP)I`*g(wge($LlPhMeKf7h>dpl|Ug6 z*AOwL`s~Drd3kuw7;|eOu7B#d#DS*j^3dNgI|vj4F__uD55soWkhukPs#)Bk5P0n% z_WY2Dl`qnsh!2>B--2SXLNH!a2u2ACMi-@EG~>nMtA#3p5l1E0vR|WE^!)J3!0KnHd|x;rmdCoVm+Zxa zOKTBR+9}J0IY-|R2Csn^HYL5w+wC#N>|lONVV#?KH5?u!@!AIU(}G8)3d`)zQb-{3d@Ht`1@TIPW-r$d!4(6KG#KCI1 zW;<-V4rg4V!8Z593MZD19EaFyGz=ynhs z|NTum*q4JmWTu}}L?~(;m^j-^e8lDP*!EsjWF#!lLa^49IR)-lIqOI5_ol*DK)|Mp zB&#c$(UZv$m=FS?j^d+9V6_tNnABKwe7XlC<71&39>juVS3S(ZzkKGxld=lg9xBzO zItCKvtn3;xZE*E-AU8^Z6i+8B*(ye1S=PM~R8D3s4s08tCC2;@$0p5Osdc5sn#~1O zCT+a_zuf0F#T8DQ{>i7GeLmw09m3^pX8SgG(bGW>SsnV8JJGy*$)@B7C|tzRsgK5a zd>+j^PtHRf#a57Y0!-&Y*di%?|1eBRu&;u`As0Iyp8u3)KP@dsBj@mk>mh6mz0>3r znH4BHb_YDW7S15-@IpD?>55!f=FC1Vg}iUB3r@_u+UpEG{B;WWjy*2Lpzz0{vLUR0 zZ|eYA0#v!njt@<&g;zH)p;<8kEG`d+qHM_XhI1Wp*CUYmx)J>U=xyL%ih`S_ik58S ztCHc4H{26`ok1?A!JU4!gd{Bm*(#33+g5%-FsXMB(QyZ6b{$>FDLfsk(O$#R;q(38 z6Y}B3fx{BQq3jOS*W$#A0P9dL4%U)q@Zh(nQ5dmw5f;`sl6Ml_gi6V9&fp`1?hT?~ z!hM5g`9Oy6?H8R6TiW~2THBvJ<%C`&6(rD&4-j`eZW{0_bHZ3euN>mx3zn>6sB?`K z4ca|(+Cib^8|~}`RzkY6^A4)q#ApB}r#vv?#-u7JnMPReVsO}kR}_RB9g)teE>gax$`hBH2eft{jVN-?(gQ1ckEFCCLV@A7s zmBIn&Rzst&?>0JcM5$=%=t+Si_$K}7TOJ^&F|9!dm_KWm^otRV#w;>yROcfC;jSMJ zpq-xBK0OV$zgmF+$b^miq+~v(FhTH6N=eDpynY1ge^ zJnqqa-1)S z`zO(&L!xNZRPzN1aA%-<2K|8AMfMp_x+K{NY`SCv9+~nRQft0=J-F}PKsI?3?5t^4^-B+3mvkqs422!&V zkk(a;9>p5Y#2lBeqM|B817n$lSlIbfl3kNongkf+ z!c}MB{kkW(1Q6d4C=Ow}2X@3o_8`dBKBnhN} zh!PEhvCSIhyDxE=^O#Y#Ar(};xPBnXBqv~dXMXPg{rLUQ&$llP0t`Iv$(^-dYa&^0 z$Cw_JL`(ld0f5eOy46>u5NKIj)#CEExbieHwf2Q>=2lQOIb09=mXh(cxLbvwm(jJD zoF^E4l=CpXanZkgG+9V@$8w_b)T6&G--;QHBU!b3fHG!M9Y}d@;1b^XrP6F9pMLYrE#&9Q$QiV`l5XIjh@pK>3`RuQE>Bd!_Ybo6K7%FEwWRc1q>ysdTE*_5t zZ-*dQWprF}L$z`q88Pu0(4ixu55jfv^G}X^xSQ!7iJ@p73dTon0@30ys11kX4t(cX z;=|LTdznYQbGR3Zx>%Yli3QP9iastGe8*_5MJ7tXHg#>WP?6lt8_!YFx|ZHO%f6I} zXntwmF&ea;31dSDpWvfF7RzGEM9VDkbM7RgGmy`qKAFlMchHP#;&ZOP&lsw5ws8W8 z&W`Xx|2(|as(Y29%MM$Sw2p~{5WY}QH>hVo=q#~Em*Wl;EEm<+9=hwt!X;3Pks#~F zM(XNjQ;88!F-*3VoWMiCh)lXGeN85M26d&P^zPV`v2?48pzh_*k6}y~V^63MJM2@G zN%__M4~Fv4zRA&|gL<3i<~e%2-}F_|m&fDT8Om>B-`>V*F0KvM*IF%n4O};AR&z_t zto1TocGohiYr^9U?)7@PnI^OC+^nus7LqX?R>@5_iMI@RvI})w6^v1!l7hV{5N0NRt~5>g zaGfti_=4nRHHbHS{hMRh14v&A#bXl2U;m!LXaOcc?ZiA_Xm|}U8=!i4=4W2^10#{& zttR$EZghR~8QH`dm2sKWUUCodNF>Gb;U;OHKDW9GUW>tRZjSxgtzdu0El@yCj2yBH z#s7TTI~a|Z#xL+ zS}qGg%%4BDj;j3R$e@SqGsk_L;#%T<{@fAQkR0)won%8Td!}D)0xp9||rBs_KZUber^=ko7BJA0dQQf;DUW&*9?J|m;@<>cl%q}_!EIJTheU-`sf`R`eK5ha^e$Z z4ZU*%h*F%h$cH`!kSZ5`c+%J(muot_vB_d%sBrl*w|jTMIQ#vZxPX)syI7wzwelnV zB1x)TkOu1PGwWb+Cqkb$_U1gSFVGj5Wf(&cZO3;oh~P=LuL>M5J_I&gbVk(o;LsQNc}E!x8noA9 zU=6*MxnYtrLxymtt{EZ`U%$S}U${bS#=Bc(+y=O`Cc7E8HjvRW^R6##0}oeM(P7`} zgnk~*o0B!y>S?yMCM%&yQY~2P%hSc+MN8l|Rceln^%tAzGL;shju`I$eAj>PiLMs{ zzU}Y1go~8(7$D7O>J_MJ0B`6Gcl8%oS1Wi%^(Nr|zbqOXqOCQCdeu1E{X>9BC_Xh? zb$>mh)%DtP1_~7_8~un^(pa%BVEoPhTc3f;llyZfY>%qcR_cZ|n~SeIclsWkp^6YE zjntxLq4S}wuoh(~2UG#s_Zahnekvwkt)#q{!LDfmHu>#S4)ZZF;^{c{2yHs;DcRFG z))}KDeT*u@D7b2w&EVM4A?wi~Qrd zbo&^)BeHZk;d;!tXmTJILdW>h1ATrkRjJ$bwp@oc;(71v+y!N38G)n*R1oVlD?tz zUngI^En6np)=U2;G5-U=hX{?(Rvy`SJ~e2a{5QM$o6QF{7=UsZp;n{%c|lawgnLTz zNY9xogpb<4Vm89{)gYvAgYjnQBIsx0`kdlD2l^sXxq$xY z2Xy!62!>FUmRMe01}o9&>Z2dF64@)83l5oRatzzS2f_i(cOY!WYqVQ=3hq|_8M>4C zmaerHt(Zr;c%#SDN7eA@O!h3?C2L*k<)Mwt3me42M!fVqIzo;&sC!KXz5?k-EAhUw zb5~I`r9{UzMHe;?t85_ae(8HgVopbY^Uw^VNX}H3^UC$_H|#-3$AF+3PyntOz6VcYdvK`!f}!7r6ZFTM7_kyugVLdJ+`tM*HN{WE zc#sh;>=e4zR_I~9j))2;45CwI!plT(kggODQV-xeb~{H{J}q$HD)Rvk>slVaUi}^+ zoe~5BAE7V^J-=qGx;Zbqe!u?B_3204AvwR6oA1{e>)C&d0!EI~F)ijTPPF>gder9- zZ>U&1U;7zhi<%@g%1r~=&0Rh_@Fv%K4U08GpCOj^m-8DrTI-C;K-}Lt{-q;0Hxri) zi^k1`VZAy3BNz5I&RgRwwPvbih?#3oPm|d(FgJRqL2o9Pp(V`3Q>q;lnr^H`TUfDw z?{>8}A>=;n7F;({RT`5kMv@1nSu%v_A73BCF@Z`q7W>K?3R&?oO8nRkH|}^>6_?u2 zl6!1|+T?;Wz>MEZil?l(2*l!jBXPXhx@G93E{=B{R@qB@*-HGA%h6z177cc<@Lp@3 z&@?jzPAT1PR>JeU;Ec%Es%7>lE(Am6DmXW3x!oP{3`!z!D)!3_m&iH#rKkq2pAuttJ zelsnt{jqVTIU{ni80U|1&KFsn z%tR-?d=Z8Ucw*Q4Iox;+c6ZFlC3fR56n{R!*O&&lFYF)hT5kCSo8R0Hx99uQpT|++ zBAHtik+9-3u#tpNMI-V*xox{V7e%DzBTe zDNLRogHF~Q-nBLJvC=H}wsVe-O#)Z&*E9nf_d*RPZ0xY{iPOjZS~!`h`Ib&|odh3o zrCCP9vCnXc&RL8@b6L&a9Ym=DC~i4b@c=Q#1vJ0+qNDrLPsw6;VN8!YVpa!TCeNf8 zVexU-r$A${Mf#(dGZ??(hRp##ctz(a>xsA!v|A6&>7b-bEz9Uqxs&Bdd%;h;jchLp z>Hp1A;X zcT5yTk`SvBRzOd^`~5xP)b;P^jF;EDpe+)8vYlWTsbtH@(GKRv+DW%ttsg3oCd^j9 z9Jl2q^KUQV1GnK75l_57T1#Kc1+Q#2n^SO*^oyq01%V{`yAmcILe0xZF4}~8+EDY? z{el(~xPhCo!y1TO4zC8qQdtmBb$bs*fDp*1c;NwG94hEbZ$VS3@Q$Q&*WK(LOX(Kz zN^S#*vksg{r*zJ>A8A z?P%`2oD0%J$ge^561eA0kKk>j0<($`4~n3F^9N#ndw%A?;H z|9(p4FQX4e!SngWS>QjpT5=2?$Ty#bFxt~ebeX|5LVdwRAWWO^?!Vhx&c69Zx zv1M-$wyht4aI5Y9!6_DGXrRLKHCt*cFSWfSG@jyzip<|HN>8a2(hWTY^qOA;3zCe; z0hYyPM0n;)I#ev6tG|3AyeZK1M1guvgqjy#6BLR>=|+6bJo=^Ec}nC7OM&bWn$vMZ z`*qz!1uATp%7xNuBJhMJApM4o6ADu#piXfV^JWOGCp=WCbmm;BP#(CUqH- zC-mfNix~Q!n+P5Tle!Yg+X?Hs^xxDPpf$^ahApe5ZdxkS7c{5!`mNVcFK*pxZ!ePm z4LZ?o+7iv^p^Fx?^b?A9u0bl%EoeeBh?mmf{j!@<;r&bkRnPFE!BouxRp>f)Nf3(H zU>AAYKrP;vaR%NO>u|new>QR_At!o3=oXL#OXCD}nhf^BOP@jU~LWxTrmk*ojeTo>?BVH9GMhvy*6AyCVpG z>wS{Mx_^bixD&;ur!deUcLxr*Us9|jCrO+u38fda%jBo6DvU8gcvI#l*6<=yaRGQ0 zn3@Bg_JdoW?xpL8BI)2YBr+46cbLsGns%ett!|8*|L?pI4RRhEpFoD1N%l+(3xJTR zj#vMb?p#%6)2LALQE#Q#b~~eA7nj8lxRi~F}OzwqH7=wDSA!}LL*X>t<6AlX>aL}xe4^!29WJ83D&G4y2#^m! zg@M#d;Uy*aH`?1jv@cynf44#6jH!JX`AoXfEJnpf$uXCztJ8ClBH39Kf~Gor9df}r z*jN-}wR6;6D5H3D9-Ez`D#5yX++7GZHs`atsRKKF>WTp;=!n7ttPCVXybXLHvW zA2bS@w_i@!E9Y^E*gWe{A*+#9S(ulK55r)Z|l zcz2p#s>x2KNow;Lt>>254b`Gfpb6*E<|>WP|0~;{zWNEHS?CMHLgwG@VDON?(1mW3 z|D~Pd-@n*tnKar|+Jr{hl5C_M{)sEr+G;LSs(gyerM}*Z_9x?h<3dM-4>{rk7ZqV} zZx#4-Fpuj%xU#-g!vaX5Lx-1{aE{alf@EB51_T%VEop?8s zM;glJVe+@1fM{{7I*+t;APUNT&KUOM(G@nbWifcEOIeFBd7xo3o7 z(7*9ZnT+ud)}iOAgj+QtZGPOe#gZOzY^Tcac8*3Po?r;}hnRFg;K19Idb}94ynJSG z5e_qGE)yboKDK;Xu`dil2IWoS{Grr+_Vdqj`;A}p7$n21x+igeAduZ%yY@GZB3@#A z$K~@krXaFcDi4l*?Z2ZUaxfyXc)xS(D;V(R6Q2kunCbr;dJ-iJe&6oj^+!&)bG3@Q zBjZK(=4xb3v|inX)O3f@*+W`Up8TDj-mW1>Q%?!2bR2$d<3PFW|QZH*b{$Kp7 zwK=@qW1BcaT_rt?KzH3Yq;%v^{?=RnEun@i{^B?5Y4&#Cu3P&pW`eJ(XExMX4U^Eq zL*xALD2C0X@;)c{Ff+Zszr=>-g3NE&QB$k(uZCc-xvKX_pkU+>zb*u!CH8&dW&g{@ zamQd6^}xFO8Vt+#820~{+$Yp7>M;aMGGycbUHA@T-3Uu;LWs6o0rC57T+s`rcmfH$C!41-UKf_Q=FF0;-Tu>0sryXJ!J|{cxA) zl~6;G`yJ27C0msnozx6#CF!v72{@;?Cj2B>ya65LpL!G1+g4N&9vKkAEn~Jh$^TY- z*=ErTFQH~Nt=YLxr%uRSl17PTicG6%fb4fij@g}I*H zyT2JT9^oY-MD`|yT(R{o83MyqL)1@<(bh?lWxkMzxA;ZKnxDUSY;>9(x4UDQGpZ>n z^@!k_Wv#^l3@LRr8ZSL8?=QE0i-vlt>dE504Rx|PyuH=vgUg8O9zihFtu-L?u-tOkE)^2;@{bh(im17<- zp_?u_p6e#iAw;}@<%tlXRA&a1z;5zv&X(y>EJ)It^=u?>pQXE*{1YjC z8({zjZpB_zGosxgzn_KQf-;C7f=}wGRN_UR97h3!6LrV1k9b%~fNks_QU3S43 z$EVC+<|62ByL2jjaDX?w9Cya%)kBPj*#kubOCXJuErF{O zoNxKTx`(iTRI$ft#~RApX$jPf+V2I0;`u&5$o9=X{$;ss#4s}7?!rCT_iGIm)UBvJ z*;4=~{J<=n@^4=}SW4D>Sv?Eu_(A%dc3VD6{5>6f?33Pk+{rGSs+<)v zZ1#M4=%IwU#H z52yV4Ms0*V)WHR*n0sFodaQyFNx~>UOg_B_>D2Xyux23>PWkqFD$J0CcL}w@E5)G+ zeSG@>h7cKeyz_Zd^U-;-TCPb|_r{GHqwY4l*wyd=U6!H4W(>ehHopM-C;HGbS4ChJ zeLd>t>850I?pou%e4ePz67CeSc}?ON4JGr0+_#R771u$&TlivrDG1lE2jh9?FjTU1 z+t&d^*iF7kXjOh1WDfL64?^#Ub52O57kgxSlDRJ;@WfjPH$Ja&O0pnRszvfs%qF37 zqIJmUvk@<09~!dTAWa@w0&8UPMt6<6sb8jumkV7Le6*TZCc;x_!IjW|tsOZDg7+M{ zl*1AcOFxwe5S+Sj7E8Y-1fd}5>kfTsKepm?vXnyb#QkvH;czz}m+ebni(|al_9+I5 z76z;Uq4dV*cj-&V0zJ-y%I3z*_O`IHJJ$zWc zlZSKAUVgOi@a{Id3PFBDls8>bz_F!b)YUz)%Id74n&vdrH79g(YW?QBrsLF2T#B5( zb!bLbi-~Fx;ocZh!RoOzYHBag^oP~j_u4>^Ew^s5)NrBU5-n2!ok|?yvTLNot&}Q; z!Z?WG44smPdJ*fj9C}}x<7I2{g8AD4F+!wwXGn?b8IMF*J;{92AHq{(te#OL3g~S# zx^FzCZaW?*NBrc$5$_N35eVpz6WI7@Gsyg;Z?KS><3 zyB%7{c?ZFRx#uuwcZyjOS`0dz1-}AFbg;Uh95Hd`Q}bv2-%Ka3`_Z}G1vf`!$c4v@ zub8l#InfhF`b?a>7F>yO9J7xLFqSQk$2eXGQ3T810`to{u4e>y1$_0WT<-~J~pl`%?i-@{A=`o3MB5am>F`@_U z1l5kl^cCxh9--(lWmDsjx(fYCx`zoDJ4!>|iT_tPmC21)V9>>vM0AZ7UXkpz+W#0q zPY~tg6@tY_3D_&X;KdB0@nds_J?$GNtl*;EQf6JTUM=V_Wi0RW4% zcKzm$x^jSas_``5!jsNr8i($m&9z)lK=lZ?vbS0CI%`E&wyj&^Emvr{)UZCyaJ#oH z;rtCLcQmtMkP?~~wD#+_V(HiXH*Z^xHZABr8>R;xa%f0!2&CMg&558?YbY+5I`IsH z>wO?Q-i7sY*ka+?mE`JrPEfyExX(QNpR?X8jQ+5mRyu| zno#ipI)LZSycG^FDxmi;bqwP4T-d)BwiUwc%3rPe6gpp>VPSG*CnSHtn64UNcamOz zm&e2yzR4H9|D>h{tW?4-lp~nuu z;KJ=qj4mVE9{@0M&2gp0fI z<1naU!JkL>flB82d(ujyuZ%uZ-p~Cx}a=`!U=$;`YoF)^;|Q1Zeg1X$_&0wH#w}SC z2Vbo5Bw`zD34MA>k5dUX#9-y9;+c0Hw0hr$SHuaZn z4o~KPY!a6NW+`Y%dBR9seF3WfZRUw_SU=!3!-NeIS5qfxmt?We5!iYY3aNe2-Z9s# zenK__gI`_TcafM_*mvR{*{7%?FF~w*Fxpr+`hQ3qBO!)nO^$pMsPwfbJM+QMBe4)x zZ9t)O@jS`)e8@t`yj6nFfOmM+e>b67`2QebV^?h>-L}Y*)rOe zt}cd6qnU)Ef+LIJjeAlb8QINx79sR0(Y&bdhhB5h?sBOa|CS%7_H!t=EK^Kc{h1&lZ_`iehp-Sl$YW? zwB7vHyJwy;e?0eI4@WI+&x{XVi+AyckM`_Yf6gA`QF4hsuSTW8VACTu8OA+!BzDp>>DFj^>YM7kPDQMw|wol1iEyU z@>K^-!7~%HZ$0?Et5EJm@3IH3c^&XrM_(17nK>Gnt6v--;@^vthi+scB3pMMDlq7i zl4x@D00{pjkY49;C^>~_3B)kA?hn~OgcSP}Q?6A#(Nmv&C|34ET+ApFg)z-{B(lbk zw|5o;<0QRB3*6VDoKs@vx}5RtTk+#$IV;3R(GFJWswn3kvclN@r!cUX5W@`<#YWWy zrdzH9TBQ9x(fT<~IJUC_`8SHY1#l?}VLX)X))BS=Rv38gNINBck+P1Z z*m%1)gFK%xeC(+& zX*lrk4|*M-TF&HQ6!dSq8j3Nn?Y4DZ)b%ubpx(dQGtu<9`OLIBBXt#Y=H=}thD*~D zFLzKXMEi}QQrr*AdUGqykE-b6(7|I z^Lriif*xF5rRCij)ngJlrTr&Wy(35j`i8zSikvhl8-~?!Ha<_{wphMMHg2Egb7*LE z?PJ_!=v7=`J}~PkjKr{_UH-tkpN-=?VO~c|g(+>;2)FKioM2Des{87AW5bBQKH?D%c5MQA0Yd6E26U zA*Zpw>$b^m~_tFn1tui?`Afdzk_Hi;3^;lkDwa1=l7oq+ca3HH# z{$}AXhM8(BQ!!M}P`>v0&2SyVCH03|oeDY$eY&bf-yMx5!=&9)2DY!?wp~Y66>*zn z{2h$`rGElcM}a$RVCC!2ek*>UU0E+r(4X0qJLpa8vJ8|E8}&eUh^mM`@!gZ4Q)rpl zwA^60{>!z2mlrVksI%BVck>_yL36plf#NrDNP-*!Q}ybeKk!mK*pJ^845gu9lx<8- zlSh)99)~{9K?$(WdEqOPPrg4VJS-(9$&&(?!itf&Ant5}oCN`k;r^djI(E1|>+;k` z-<=f)v@_Zzn>t~}8A1ipR>R%GU2nX5!&aQTh#dVT>iw6$zwX(6lfwTlFjfs&ku+X| z1khok9WZdwC!K0~;xI}U0|pS@%JR`#^u5t27QImM1J5cidBLbc_tH{GW6rFA{7n#P zjqUwx_o9)0=4wc#GpM?;bf}R3+P`Fy_|zkKHQy|tT|2B+dmS#Og6wG^Xhs!#4n`fJ zD5+H~9}4HmaIbk>sjN!eVH2P|=9&mT)M*ep{_!p`%?>IW1GBd!-M6jZtv=vzvxrws zj2iw8+pA0yZDkKhdHNZ)2Y`j3QL1|qmaz8-Lu|^@+HIOpq z8=%7`y@x|=th8*{#u{Igs%s04y?X|`6bxQtVHq?^w9GV=t;-J;vhRI4F<{0T2CH43 zx~R)kC&J^yFJ_Ec`lZ87`>PY8kHHPw(?%(7-ROEkUG3PtoHP*aYI#Gpntp#TrWVmk zR}bqzR^-pFQj$<2k9s%INHg$arBf_%!rP!OixA-Jvh*q?x+0--gngTiK2ZPY)na-% zJd)0_-fWIxQ|6*`n^?Y^R)42bao5OP%6rLqvxrrOW6$Y($;g<_vn>D|G*8oKsW4p1 z#~GHMLg*JipO*RA(b?2_S)lDY2jDPRQFC!x!y%je#M%}y&29$Yrmjv#%tDoN+&edk_|Kzj&J)v#6dUpyvnYxsx+wH5aM)r(Du? ze*~RFObbOkQMFQB2O_9n0p6yj1}hmXuvF;_0e+Z{K>NW;vzK;3C&7HHa*ZOACaeBD z-hIv2wLcfz4*ZW|BPBTSUgD?9Tu}H5QV7Pp_EnZz8b7=aR4xqNatAi{G2ViF?bF*6 zjT5?Mm4P!By{9P4=zjiVB;osUdVtF~Y6m>G&+1$23pMupW?dHF zh=q&}%;g{M3!G|1wYIkV#BBK%AlGRU$au;^$Ra9U)i)U-lx)^vI$kAMBYkea+A(nos(me4RGsg8|Fu(gmV}f9x`0clOTh8b(Oib6D4I-$I5I^(K8+Q=%-%R+I?- zNqm(S)_p4rR?nhrYgUuRt3PS*{HS|k)Jgqyo6Z)NjNwonJg9er&ZVUu8fzg)RRcD1 zgE3TFo3>W3mGZx5>evFMv~tO;A*8mFhn{-+Nu+tML87TI(4m&*39dv-n{b6vU81-= z>M(r`?W`{g%(p3PRLB|kPOcE71ws7R&zu z_IJz0>)J(d#+xJ+z_DFW?h9sYvB)&>4KS>@U=_Sf0T=!v_YgMf6Z_?Jd-=U%aIb@z zHbcBv1m_iG*d$*FWUO|X|C)-fQgQcT=Vf3c%uem&8KjFzs?##mf%ZYL7)G3Fg`M17 z0g6784x!aBZlvTH&d?2}4v2+$$78V9Sx5+5)}#!7Cz zfra=cb;*_eaLb{NO|Ag98kO61`k>_<=)Wicd`F7c8C``7Rsk81>bz(8#m8VTNm1^X z2CalqKF8rI^UM#J3gnVk?a=HE&OL#WX|=rB7tRK{$vIh?FGUZC;90Cq$%&s(%4iA#fA> z*aY4)c37hq#^uSe5W#lN%7WRV2$w-s7enas}@8L37$jvaTxRJ`K}k; z2Gu~)j8twVhZqDsj(QwOV^Aj)q*b_BjM5N*T$3a2;79t{?>6sJj>`M1=8 z9^S9Ny8c{kPv6#RG?f1}J-S{FuVLOgfAw(a`mE+Gxuu-e*fn3?oJ(tRT2z?p2O}1V z{^};EcXIelor!6ZFg#CMB`r;mo-+M&rkNANe_Na_ENqxC?ugbVxQ9}uQm7C)E&9BZ zX^wF~$ILl96i)yRk3KJ8Zc=eX6{iU3=#>U<&|nIKBfCsfr#hYMe1+kylXeXv9Y6mw zgj!1%53Bv@p?TGOKI-9PxCT|L;C-X?C1CgQ;b1i3xV?xlA=GK>*Il!O#Nw+0<~h`M z%P_l!=Im7}{7f3z?WdeD}bqp-26|84X0b=f}XHY3jUcr}2@{$ugbXJP3Ac zUc>vDiLhAYk32fy_p4JjJ^<7Ec8_5NzDIJK%C3RZxss&_IUv}67Ix0POCt~(YqD4x zPr?{pw_V^tI{ta-lkg$FXbSy(6s>Z6sEin~ig)kb3Kr-Dfv(Q{uv9yV(H^v+=wX>- zeDNjhafukr*T^9csEEO6`C*?8&x8kOs%nQw)T11A%7e$gMLVubkMY|d6Ls#j@p%bw zVc{!07afxaiQ`GayDRVl_H%!;fQ}D`X8q^RpbZ&_$e(AE@y#h=EPFoyy_8oH@%kx@ z)#7T%%LMNqaVt!_1_QWPzniY5bB+Frg1v!%h_uku+MbDVL@X@zR#%s33(FcYxGl8} z*D+jz?=n>Avp)Vn_xi1W&i@TP#U;r2_}6+hD>p!O6}SS!Din2zUe?gS3A9%FTRb*h z`1)79;aqJ#V0h)f9Vk)@*7OD@TPs`Z@h>^O>9*HR=_qBij$|4ts}H8rq*0Tkwo35r zL6KS@5xwN0vv0fwUFfZnYV*iVLH1=tX9MThK3w z!jSf>$qAcyUs!>rFkVo`veRBVpYFrPw6E!GQneW!t51`~qLH1#*@bwUxQXS$%;U(p z56;iqh@#@k;k_q?+q)$yo)NE+d?tAepB#ZxW>sBEes+G9#0MpBfV(^4VsDFHOBv#LRhA@A+X*tsb+T63epBaz zg9AqQegancw%>D6)DMRxpK$Tr$Pzm8BR^X*X4wn`Nb;j4Xcu<}9x9B&aBu5VL;mPg zCQ|hXc>>&p{-v^VM?}!%IPbl%fhwU^BTPld#bgEXTn?Ew89xpn$E_#11g?TufiL)w zw8=&ndrOL@FhsnZ_>d&pE!ylcUDzQS$gts-;-#)(rBJm99bNzu2k%5|9rxqhIED@+>1k;2bC%h24}r8lyurK89ODhNo-^zLG~ZGaU?`wL(k_uO zPeAB+hitOOq}+&9((@iDg&XBWAw57TvBkBngA&;56gfnB16p+rr32X#@OuaYM4LMK ziM$lTEEcW0cuS*Nk&lr0*qYDVLde8BVmgm2K{_lJm$@6S2GI@kd7gIYWo8^CQIw`a znvLxXFwY|NN6+3d-?kluMN`I*d%|w?N3U_2Agv(~=BCNF#xZebf&wK^UcW19;ROuJ zBG(kc1acie>Gwy{0@5YK5-*`%5WyOg=nmwCsql%zja@*ThJL$uST`R54|V89I*d-M z-$A3o>?65dat`q^o6*@_P~8Ep$dD;h>%v0QpSWqdHFD|JwgzphRlk|&tIUNqst)`q zl^=zm?K#Kb#e$q52=$N+mEK~rM_-Xdi5$4i4IafTz|)_60-9GO`xxN)IeJptxwxm+=Vbd2oZlI;UsN z%_g4kW*>NUSN40TK6T?|U;;TRl0T+WCF?9jU0*3IAC2-dC&CyK77iQX6p43Kc$p&5 zx)U94Q6$f>7QGgrL0lyz^w@#YC%slKh*$vv-bi_L8U&gVI&CHFY?rG_gi30sOrRO3 z-(i@aD_>p?*VABbHyTg+t}nq;z8ORTV+Ke|iolb5wH!OKN&1!Pd2=^ZuLW~y!HjtO zZi(L>KL(El-IG7sYqL)v%p8+Pr9Q*(we$(7g))IWkf38k5=F8^BFl^Ie9|!fdFjZ$ zm#N*(C}eMiE3OqTWWh%BLCYfGC;p3pEC08clYC@LT_&S88b~Q&*xflxjhq zXIh+2quUXy?dl#UWP7F8Ws{;#`ipy@o zNk2FUp(k-sY^5rsSFN4^!he%{S0LiEu&*|_^d>8*k-Oj&8;ew&N{xNSaM6My*(WAZ zu1lt|)2_$D5;Fv&2_ACZd4Pn=k%u3L=s}88thlnv{plO~|M!%!5!odjpNyWZTR5V-CSwfEpOgbjLVEJnsvegN#Q+7$T0 zt9GX1)3#x=Hn?CH_Pb!EXqOCqF*@|vHYtR;bOdsEIGL-?dvAkt{t<*{=f#s{-&}EB zMMnesv#D5jdFOlY)1>;HG8YiL>T-=+kP>6XaZaG4>})}P;;s@syIkMc3^YVmr91@+r_|Bie9 z4{e0F#rjN=AA_nKxce>a3jgFDDvk+`S4vF8S{Yp3L_LCnCw^Tu8o%crs57n!5yYfaYwk}>!}mc z>7?UGqyG2Ag(kc4vOEJW8Q(@L)scw3pJTyJyCHbgu~<}Wzm_7BT){Tcujo2jN?NfPPi%ez8_=CV#5RfVI>P7|t$-JwuLsv1;HR6rS^K zuMqAKp7f@c47}nXUQQ*PgLJtCvZymc+%BHFqZuw2Ym@Z^>|)*7bdp0|dLNIj0knl% zfg(xFJFW5lm)~4*81Ax2kVB~fdPm=aO@is-C@P8ms)$+g?UrzaNL6^_F_2qo$Q)d= zT6GAm?FoInD`%oN$3QhXv!fB9zEJzv56T#({@eM z&0lD!hE6`Wy76e@N2wN5+#*VSQ04z8XXQr_nf^5pJ|*D8CG4kh&DI@a@z${O9X#Y- z-v#a|B7~h-FSx+eK;?3@xOLhe0bM)4>@Ix`>t)KJcQ?X>n-d;5nbVb)8yR6fq`^9l zeN9~kX(e89cAhhHEudK>-_3bW9hHH94;x1l;}4&HpT}aXiz4AEsu;+7LuY2H@ZfN+ zlgND?o~-|)SWxUj7STl6@5fid1%Yr|$x4^7CoaP)bl1U9R%{KVQER}5Mz5hI;Jy;f z>C#}hH&sR^DEs*>q@TMBRzosESZMO`{(*Ln1=8mw3m=~(Q=ZO zvqHhVn_^L^yc}#734D1J3p{}j4JZBr^JCP#U-%V_;Qgv4CMoq>Z{C%Y(Ojautw$B@ zUswBCv)Sp(Gtrybcr?*ltdpse&?sBfzoOz;^CCPOT_KZ9O^U5^U3+7B2J{Y< z9Q{_t@4?#%8snq>^$cLprZ^=RZ$>)SjFD_JS9N%1xv}H3e&vUrBX&?uk0U=Jfa@T8m6Ds~)7EUfs3{JxM#I(>i2N?b^xD1y$@`^>mZ+jMp> z85S%>%a|1N4pk%0DaUa?rBKDzOFY5^MAH7xbIP^+aV`o^FmH1%(I(=iG|Z>9x6+1Z zZKBol+?;4TBu%_fK>6ovsmz2eCwVms*3!MaVsitY%L-l0oDruTm(9Y3* z)^OaWD4!bksKW^z4YV|<9nxQ)^KIPDUlCOe;hvFol*dWxB;=Zy?8?$`iD+)5#(BJ>=+ zPmut&m=l9CzYX8xE#rqmMcfCAglwe}U39CATs_z&)E#jU4xg@+3Fx2r?V5C1M(7J2 zk4Smx=LAa-^elkt8!V+DR$fV6VLgh(^iK!vM0D=E2*q|;q`boR`!Qu0#+6v-F}`2@ z7XILeumQE)ORr`={JFb< zfjT(7=j@A59!w9!Z7divP$}J|d~^Z^rb@NE01sxa!srusd%VAU0E74=Y36KkIxKtl z6emeGZ!b<%Y$If+1#9x~IGD9-czK5$rRi>CIgTmwi~7KPTk+s4N_7<AJw3NOGUtJoz0m1qtKJJAH@Kw$>@W(w3cR_Ww-A2)4F#5Sf90v6z1?bMT(e?K5 zCvddqF*r76)^{%)^JU`O!#nwjC0t#<;|@5!h=u`HTiDDhFk4s!Ig|y5?a>PL**9oB zVw=-Ds1z=b6xU67jZr>{dmka|b0vQa@s_1<_f&UqJ#u&T-1Ovv>KtH*LH$e%Rp15+ z3}kCDMXefIkIgLz3>F$JXlmQntRi>2@fxv!s4f!j0@+TdQAK5t%8_)TJmfz@(Xk_N z-Wa*%RYcupf?&(M!FZNghprG8MMrbswZ%a%5@z9W{9RGLNjxku5nb~*3JK*#$KEc-v5rJw=x$Z5TVyI+6CJFj6jt$@NASZL{5KiP2V4O%%uQ>U7{uiL#o|2c12v zmoF63e5W4PUa2s58Dd!S)3YEm2Lncs3++}RXvRV7cnV2qv?uK3MtGzNGtfXPW+JV%XtBI$VbgT;GrXgdcAbW2 z4kP{OHAvDdG=n>dfz6}kU4k#w+8`Qt06kAlH-(0@fek^Ot-UKW8X9W$hx8$d+eX%J zr3=ffXzr+tmjRnog%44A5$?5NHdFuOH_T&G;{<~REjQHDOe;(4u{mFAa1T)vS<|XI zX`O_AAyN%{|2MAf>c~bPjTj~nEy8GgY(^LjWPlGXQG!;P0itNjIRddZ^$S`W3Ng4o z>I?KufjZCdJk*smPJzx-v-x1O5ka&*y0u50Nx_6>ji~>&Wop!QVzlI~a7u;d39At) z@erwHirvuO=?omS#B(is3$D;yX)gpLW7ZGxrzLT+O)itT^+PNXTH@476xXjzzC?eX zszfu^&%@ab*=TXwQQiCq6i-33tUXNBR|O8!KZ7g_vVxAcB(sKGH6|VH+3*hH2WX5i z2*XXRMXmN?tD2keU(=&oLB4GVc>Zg@-7VZ#uG%3qA$ZZf;@mGEghIhIb(0`}IK&WE zY(EC8^VJ?LB1wG)IO3c6^LD3drc6Q*iK53X>NBWyMukQco%6*OT0H;?mU2~OdLf2K z+9dlVlH%W3BR6{3BhU%)0!HrGgVtvy)UHKf^oP2?VVgQH9mQPm*w)Ts#z;3GQpNiv z!XCVKMDtyvBn645`L~kwrZ+lkG$liJL8ryp0P%w65uvFvJ5Xzm(x>M3e`6`d7&J*q|iT;eOWzdLeH|%!1hpy=P5Mz_G z99Sj7TMJ#!*yn>NXcIVNmPifqgb_@noon3K8e1jbXy|bWHJW!@z}rm4|N0rko1;RT zjEJU`s3~3p2`V-Z)!gar%epZl8UtYmy`n#_eC)!7D7fYNHr8rTJq50!)s0mv*04rj`%aIZy?X0_IJDuXSMOtW zj*xAt-KI)SJgN1!X_CvWGvHI-){UgKji!}5Uc=js&i2xVgq|HpUzYYh-TJd$@Xlbk zZxdMxaZtVun3aRiZ-!9?V=g5i&kQzWe@>Zu#5H5czzgqrcU`UAJ~`KiY^DzNlO0
    pm*qJ!^1V@VLt3XkOISD0VIP(Q_jWG%?) zvLOYY%6pvk18@5Zy$pmrwe*LCX*QcV@v6Q(=*Gr`v)Re6=^#2pu1-t*m@A&S^2^d` z$^B5JMd(r#L9bTtL10j8&>nJY9MDQxYs|s#ZMJ?_orsr`yHr8v z3W5g$8;0<=N8Q30h%%5=$c-D0soe@iJ*JAoz{t7G2TU06F{#-K3 zibpG}B$|?3@36@W0@nev9Wbx<#-1+^p|1!rHoDL)VFS3_*$T(mS-0FE2=myUF97Mo zACn-)Ja*vypVnh4-|=S%0-tu6gjlA9D_~9E1Hk$~5_u2@$3J*wiFy0ZOk$;SgdIh#Cw3kU82W-H1+1t!z^dwQSQ z7Ir%JDQ+#&%*%TocLlO2N|Z4fK?v6Wv~d$$;4{%GOr@~NgsuH!VwQYegm6rOCnsg& zo+N??ek%DehHR(8UPJW6T$#ati7(hbAQw9vbRpxD$NQ2!3@e}&Jepp<9d^ss&s^T2 z!+?}oXD0iH!?`J$#4{Yq2W;TUyMOF>1kp<}7oJ&QfXD%5l;3w{T`~J_5w%J7f+og( zRQ6X7=V$)7tzk7#zH?qOpQ7NsnlxT5Q9dJl4Y_MF zp?lmf)?BvzAcTSrlp4}UX#ELWB>wCd7>W@vcxqj!{XUv>%oU00)kY%vjW}9wrhL2T znfxA*O^$lm{P!YED{&6=ZK}j#^8ggPhiN)N<_R{>B?29(`T_koau+i<5AIH1Mqh!e zZhfJkm-5ZND#&y&$8bU#d_$b-a&y|qTi3{r=PWo=_z@F2uVF<@|!jv2*ob@+3 zrYq@8Hcpa@C$=>HPe8)KG#pGG%?Y=r_)t+iQQV_<(OGQWQVqX{t%f7|e!mrTA0#`@ z)J5B)T0$=zO+$(*HLVUIrGuymmUZ)kgsl7<6G;Q2czSV`s{!^%u~@Hk$+?)**h}1v%?cwvAbi=SG%0?{L`#p zRI2O)2_}bd%orMO%yC8dKRLf|N=}D_++kV!v2wyGG(-VDyx75?Y`eH>Q|CEAd@dFn+xSCf|V?cuufv%`zai zlS|xyyy`%Vp3viR;JV52_d2AVKybS0eIAY^eAjcv+xNr%g=q(&CLbP@LQOD~mLk@i z!77ZjtCDVWlAJSs2AiwE>N29qAUquFM!d(^j$nVB}JM2|@L zx;Mk8-{?Z3plhvR4fEWhL-d6G27DnpLV9;0oeM_Up3j39Gw<$!HS1ydm+unWyjtjb zN8!jHN`z8ID%`T+5=Y+idr8%Z6~mH;A{z>IUNkqf~=K0 z8i|cRxQ+`oKG1oqcEF}`F%UN)VoB=$6CxlR{jXIqb2a>R*wAG*zk&LB-Ena3c-zW* z;MrjC>u2zY^F0b%vcYc^bO>Z;N83bF?$GbqFY;6G|G{6_4D0YnXfq;FZU22=AC#>( zT`?Q8m`K?yql2s$v3>?y|GH-?KsOFpCGJ|QUpe-O1)s`D5Cf|83Ya{lq-jhi!|KbNLY4l%YvSaR&xd zl?i!zIgtBZL3fL=WC6CGLT*w$%Gwx)s<+nlFug<5(`nkR&O62+&4o(bN1v@5sn4)a= zr8gUqFS9)1Hg(>{=*o`{$$4Of61qkaBgWddxCp8Oo&I*o7nPd7U{)(`T5oj&T-FX) zp?Gi~Z0i?A8baV7A1-N3t41T2?vnc5Z7b!i6t(Igr;7d1Xy2UBbgjA)@=d5*lYJ=V z3JMqUmLL5*W)D@~uO&y@E%b51B$k1&Mqm5_*b)!e1Rak+qSGMy*ir?ns4x)GX9h(K zwmSk3I3J9rK22=u(VBC_T!P?`a%v$OS@LMMR)0?AFIXfO)7iA>)QDsehl^c_h%RUC zWOs3OYw6C2R*+=T=!3l2qQp24h@a_;A7oryK9KIGXqo5qt0Hl&{^-G5A&|U8^WHspGtx-#M6sMe3mYUoQjp(7MAlJ=}2^#Ng5%;>=?c4Y7Dg4AnI5R zmvN8;TraVY4ADdHVfhM$-YNNh%tnfD`maXAw2;iYwRj8htL3Iq_-bD0kizw3ox-=N zKurvDT%#by3w2p6k-Rm)rP`5+ivL?W9ciU%C93U0TCdZ3X-BKJ%2kImYV{g#8!yE5 zSDrlrX7y-L?^7(U{KY5B`DpH?j`lj*f%Ks7hQq^oCuz~bijRr+A~|=|n(MJJ?>uA}h+hAPTlxFd zA!Ky1LVQ-nxh?2<<`&F9(0d# zY=fd?xbunLB~a|-4E0`PEXC0M_J}!PsVBSE=e)9GjC4XCXV_s+05Ezod}QWB5Mll< zk3ktdNCeyQ#PKcs=pPN|+G{;U6a1EcRh$tt6EkLQDxJ zo7+|^rfqkM>r&YHYG2bph&FbeS%)93X;n~Vm2CMlw2ju zT@x6p{cW_?Y(eD9bs$HnrkC0(`S0Ggkl`B3YAw=QlcXuORTQn_yGc3=SA1@>1@In9 zi4YPysT?!*FE1|fo7g#lB9dh$iTmrtRH-C37dGw$6N12pE-tG|U|2F3AK=0VO2bcs zNqQ$t-k9By7xF^wC*k+u1a4#b)x*Q~0-~l?#DVmlA6Vu5crhBov!%wxG$wOeJ=|wG zL^*kgoP0-Cc97ouzI1Rk2_o2a5?t@ie7Iz+dFJe-f;Z3DBjPM1i;M)11=u2V~Xvy#STg4M|cu)i#- zy^oXmyIr3@AAEoKWvNsCtiu(MEq-|dhDmLM|2nncypmwtx$km7pdC^)&qTUsIhe%% zPr7Gn(e#x=uUhRJ=OJXK@9!vy`r0I7nA7To+2U8nplsM4+cux7)cW`d?=+9~77PVi&8RAW?zVYAYYh#?&_&R_d{r-LgKOGmmA_R( zz|PY?yHxC=_50f(>Kct^boI~Iyl-_t{?^B~*$6{jo6IJWlroIs*HN#&H+#bkKQPQX zhjxBImM6fTOf-coNTJiHW$~cIf3HU$wdh}JPOPuWc1p9sdD$9BLJ-~$?n)~y*6oBW zhCD>8f^&!q3H^3C+BHCW8vS?^O_!rtMM#b%**%5WnuXE#&4*7aRIH&KN(>Z{!}H_d z&PqU0`FIH_gFK7zF16GA4+B#!5+(*F zLWiXGF}h8I?Ou4NfQ-L{wj?2%2ptgXuJKErKZcBR7!7hT0nU)kve4NVI#pnt>trl? zCy|^%Fmd#=`>6~z83nL{Ips+IGiTuVaxf#(aCm3>rLObP)iIolqtk5FQxQ>9{S|@ zP*+IhNCcxePbt=jbHp}t#1^OM{ZN4m{k^2;G7%Z^oc58C5i{s?DWUI$!V4bV;R=Vk zE%sgfV<)`Kr%#PB#YfvELeKRe+nNhZkW<6}pGBAit`mmU4oIQWmqR$lVvFfye)wg* z`z)#Jh6@ql@$BcP$ly7MadjVhdej+K>Tf!E(E6@?xrn>S-V&qhjOWlw*!}gvMJW_P zaB>gmczCG(;4(?i6g)ot=x4O5Wbt}uVi1;BKM@9Zui`pku3&@=`)WUe!tFsgzmI;N zC4Ou?*yhn6b_K?9VQ`@w0=^FGIE(9Mcu*!fbPfl+ARSq^+Wc3)&fNd2`|opSmv{Mi zJt4~TZ*5w%Fpw5f!zM#_iLyg$Wp5en+G>U>4f{&NY^0me+@RF*y2-rMboEb`$QlEqb(PyNS~>=&j@-%!>5MZHhOXttt?o|u zxHKOwQMew z&VE?DT+-{1^wo?6gAUIW=fnt?vWe|i@xL@oaAAyLG>FYzLC9%tWP-ukL?K{!>q!9> zY?#CDAUO8`vO=K7xNBlh=H~ms=(}Ss$w)*z{A6)!L~nC-L38kDVN)T03_zx2@GjR(u_nLx6-Z(0UY+?_uAv5b4vd&{qkBfoxb z{Ks3pF!4Y#9#ZTJ1k(d&(IRaZK8raz{s{(oHuZ;#j_*PzP~f;_p~ruXu4zY@B;!X4DDT&PhWjyd0b|$`{i6q^=N_0sk;tn3}9n=xGodPD*sp zb&v&j6dgsHWKD`r6b{{0wUqEDnpOeXlroFr_fwWBqJb8Ol|uWQ?G--|1uI2#E3*LS zz^nv=h8eJzR4iD#MAVY~Z~o^Yt`E_vHZTo>(VLwUKatQ6&Aj#jv(166z6&Gh06o$A z01(yjKK}TYPqJX8td^L%C!j+wI(47ZA5lR9k6}$LQM{fn>KUMi@Gbt}Z!BtHcLd{z zUvl}qZ6mIJz!iTpf>vYpL#WhuL5cawFEhtxPmFWO>b?fO&AGf7i90=T$4a^2?%E5Z04^O?~qcjD=^ z7GsAD>aoO*IC`?F`X|4(y2Q9+NGVo%^*ejkSW$xxHz+-~j%UtVOHN8cjY;RM)tE@B zT6QWKE?$9^WpK7r;VzW)wgF;{fwC>?ofeDRA(uA@D6KG1;Qyk=zpuFR(H>72zJ^GA zP6A_O9Fqm1wo%z&a=V9gypcC-nVrJaY=i@n+dcAoV4&A37<+L$Fc*WMR`IUEEmP7t z%-Dw&?l|v6qjN69 zKA&7DBK))>DpZLXBU2Z77NeL&TnV3Kc(o69iuXd-e7j^Aj-H+>k}-bnidkfebvA@5 zZ*GJlC&KfW>vr0m@Kmpdr4S@%4_CBkVCm41b6-1`n3t zj(P`E(`(&Y7#BZ!(+U{7DBj?h$;<9O(@&#-5x~kcc6;TZybk=8woF_}HQG^-9N;V@yJ>E#$u z;5NlaPoIRrj5VKLT5{4<-)5@_gM8|`BlruW-{=+kK8p}VL?Kq3C%4Z+xbg7iBoG3s zfqAcJ3hmcnF9tk`0%4PsaC7h$541)ELQL=DBS|sf+g{oZPtCz#(dV$W(<=R$!!ROb#L8VT{DAPnGvxw#w>9ZB@a4i!gAB>&ftcaapK`b4R%LwZIOhT$u?bt#UpQ?hM;lp+sRzB>YTF$v~|txnM%N_|6poa zZ?qI^A+>o9)aJHdd#7(hRXgEq3VPDsNnLgRm5w7N)$Dv*_4ALEOtZ$C`bN91(FKB_ zL)oYli|X&QcjY;|8w~L852}vi(-nMZ(}Z13DKVwn3To0?TOuSOtKwvpB5}yC0ULqN zun`>?bCMcVWqnmD1TP7>$Sl*vq6|p29%vCrK0P4zgX6Fd<=^!h2r~1QJf5)v0t~x* ziS<)Ke3~o4+-4m60#m2?IC<@a-kdC-`09xgDaAz+`{VJ>uH^nK!(>lHrn|-k5QNtH zZwas1Nni1RD)s?$j0z-=EiZ<`neHc-?SlDYF$pVOU?4*8mvqd%P6*fW zvGIX1!6tgi8E~B$v(})uD`sVT=nJnh;njK|2_KrKfFA3H-*|w?uG1vd+Uq@`V;f)2 z?%7(fOQtd}Kq;4pE2)!f;P{t>{nQUCX$+c_`Qak-I=M)_XCY)BI>jV{I~DF!l?4dN zb{7)ix!~%K{U*Sck8l6Ml^@4UOLcZb&8e&kgwoCE8YLP8om!-oni@qKg!*1}khN$K zoi%k9G=R|v<-C`U1Cx88ybXM1D|1=RkkD)*wtlHGP1R}F=O15{NU6=6E`MSlq;kfR;HNN`empzN+4|(HLk}?PrBV^Vh zA;;%{$9z-_Y;)}IT-EWE$+0>4^X82!qOPnJLS$Y)2KMoc6VzVWy!|euGeXV^Gg*>r z$=)PuFTyt4JGrpu5}5Svnq=&C0w!BG&gZVnWLZSHD<0x|&qK9Y3{2=@xZJbc;pC#Q z?tAw^*&BqhErU4G%PQDxx#$ufG}0toX?{vVcx>c71~+PQVw?ZH(-6EhNFK7lAY;*F zmj^g^{qrEw672-iVCVbR>whJ@?Y*Bb*q8K;KUInHv30_1FQc|ZN%p`X2=bc|<7lYaH&ijqa;J9AsXfA33XjD<TY`xV`{TkmV@wWg>T}{fTmi3}EWKq2 z|HNNdjjda=j;Zsx24^=B)OcK{fvgP%Z&s{@*Xv1Ixx5*%j^W+4oTaFyyl%OnR-$&> z%ppo`S*=k~ji7e>4_)20_13~kzM;aBc$*t{8Wef5-_j0z)jxA24RbQEYK^Ou_zImMK+h~f(=4;i(Y9)2653gPA&u!#KZm{~jv$IPLpz+8bHb^>hZClkUze32eO>uC6ekjN=oPPUIHcW`rI zOkOAto&ctoD|Hy6aRc~r?p;F?ZywKHo{ohp^HJP9{CuW7@w;XEB*UGg|3Q`o0tt2_ z?~~ig-w|DD6GR39DU-diyKP2-kHOU%IB0XfH#(shCUPVo0e*Myz9Sv(cO$FKmVZX> zbii%{JV9gQ&m(Rl`G}W@ab@jiI%!=#1$X!scp}5ACz~1uWEhI zt;sK%6rCnc0p_WDqubrzZm42?Egfhf3A-=tad#_4UpEzQsVG*CFPDZVbEGLMb;> zd|ZP}-1ZhSaXZSyAbo$)sIEFD`V+k`pmG!J9$RQDh(vb;iFq`)(6-mk4>}0)utanP zUi9#?u9Jux|J*CBmxxaTAY}x$$ok<_-;c?ON3aZb42!iS-frZM_3tZl;bH{HtBqAs zF?aNCJbXDbox~Vp2zVsgGb_=Eq$GFO0QPvO2;TIM>a_Wr|B=I=lH8@+{P9HW20+D?z)Rd z4`v;><7)JxRXj6_k1;UTF#zqAbizmmQhOa2oOuHUUrx-JCkVR>P(}H%K{KVUhU{;W zT{Ck&qF`lI-?)$l-M=BG*J*?h=AUS>I9>xXnyppBPAht6%PNx@C5GHW^8K z>9ayEv)KRY(R3km%WNynxlWox=Rx_`^C`I5I}mP}VYa)_B@p`MUh)(__BD`gc10OWsrIh zoikox{20rv7@n2@2*i#ZC(ZRSJTJoDI^i2~;>Q$PUyd*{w%SBm9Rdtjk30m8MTm)% zFp>gnHB+AZtLGHVrIq_ul}bpW4co{ez-b2c=nI1Q8Gv6%kMrP*JcV z7C=-46;V-&qGG{9Q52El|6E{UD`1lM%jZ3J=1jTw4m0P>oH^%vJe?P=>HnWYf^adh zAF&B-DEn(2!n%p?iC2+k)|ci+Vxad*G(TBCJ+2(jHz{<}m#mizjEC*g z!37mCAy@hsy=<;p-&g@#dyO{#<%}y?)jNBrCsS*_8yi~Iu?H3wk^9DaIY=X=&{0YYYA=Ft(E=_Q!ER45J z+$ZEpg}ZtpH0}xEs&QM4ELSH$Da!#zmo1Zj;Xw zhZ=$Sm7Vxx?^GXfcZGdpNC>qNmR}i}!y5jQB=8peZhZ6wL=S$x8R9>3dD##D%-(dv z6Nq+3Fv?_bMVkVQ5!JVdm^_@5H$Dk;B!9<5RcdvV+%YS8|ZxQBgq=)N)NQd z9qYIF+(*Q?nw)q$1091K4VQ&kWWY5`$x)MZHUny$c36_Pdya^>4Y%3-1MA-@{!t*_ z^1<~X#*+PK!0tiMHTzINZU#R(T2Ac4C5(7k#$_;Fg}@SiJMl&QzZXaz4fE`6+B;~{ z{>4vvn*0*?y}`KRAz<13ZexWaF(LkZ(p5Ic0qz5N8R)CslkH!zS5WnnxbkPtHJqe+((| z%bKn@>k{U!73_I8#O8W$RQsJWzf*>xuq0WceW)W`w+0$TE|sLpNM1=+&5A~M=WN@C zFYk7dc=laCVI4 zoQMY{)41|}27GX@icW7pl|(1+=crx$cT}m}^1oZCwVe`CDJholAww0e-)kv(5QVUt zr&)xUtQYQJ$vVqwsIk5SAB&+$W;4>mZ=(KHTOD_hHXs4}#`F zH4?WBU@sLx4DztP{jyoTqz6n>ZUn^`SgG2#LdhFwRyKXT|1uy=ag{%{Rl-It-4 zQE*OpU4P6?*y0Tz7N(&UKVlqi?Sa>W(sG83`>;lWH(}u%KLpL+Pc`9a9*6>gsK}I# zcqg-~@E$oJmK~&jI3bFGrh;2?>EZ_uS}`}8eb&Hv|S`TLJl$$5+D=n^|a`8*PX4{*?PUy z&tY6<8|zbevbwsgesfk3oEx?M=V4`IA0X2i8%NFclVe;I{p~F4^})y|u`SBRhN3gj z+P=rrVAiQW(fg+wh6O>{*i%5t#^w?-rflq8!dFQ{O}o;wFIp*xW3Ug!8=)@xZ3UfO zZ{#lYT!`M=e4kk90cmR_QfkyjZKtdIbW>NCZ7h8n;ZiSG(+74ZDN-g3DuXXa(-<9I z;58r#k-7G*hPrRVyEXPdG@Iz33593d;W#1&hcQAF6ju#4(UyzR#TK36U#tXC@jH`@ zq_2#h(*4f|g5_2Vfh(%LJaNNS-zA84UN1&#Rv%Z07)egR46|qm<1%-G{~LN{+f1w8 z-W&*1O2Eu@!82p3!G+pHqj5qiNv4|m=M|-zh6hhUUJ2Y|-2%N+6Q@&^a3yrEUgv6c zwl_5`7>~v9xA8(c#Q+tRI%0&X59${U(MqM#kbczYZr?>m7 z`rW#7nxC$XqV&b>Cfon!c1qD`y-lmWmy++MxCA`mApw59Ad(A6ZeT)w?u~6pGH}K1SGx0nH zr_6gO_qh?hB}-lKQ6~JeTKM0`E$px5x%SwVg4bQN!a_x+h4HmiD_-O-jQat0JV&g; z`}9*`3e9(my|%Ex*Xs>A=)b`BFlf#HmhUw^VB4Lay;rn|dn#`7NzPNo{XK&NH9LY9 zQQ1Db(o(90g!g~iscI&X+A6it+EUt3W$^H&Qh{@Clk7D_UQM_v@&}^h-yBQ^)QN<( zHUvY#S=}5|;sePxXPBdKvL71_Xyi70=wr?*b_}!R5O<}E$d8eF(ro5Y zNXPB{YQ%&s{K$ZQ$+00);mLk$0r94+PVRy1cOL+I=00?{cu0h$C1_eFdM-*qRYMaJ zRP`2^wYOW)A)sr-gmyJxuPiF+bLu9fJM#8

    VnZPI6Z#KQ>3#$7I|d5VmhUn+fSA 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

    4(3&qkqh!tA7gi_g@k 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≠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 z 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 z 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 diff --git a/lingvo/tasks/mt/testdata/wmt14_ende_wpm_32k_test.tfrecord b/lingvo/tasks/mt/testdata/wmt14_ende_wpm_32k_test.tfrecord deleted file mode 100644 index 4e72fd127e18110bd8547dc92f508a5ae46486ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100951 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 0.00010073416224276751 - 0 - 0.034041494367619232 -▁, 0.044370583669798951 -▁. 0.034193368449234512 -▁the 0.024996024997377464 -▁in 0.013820257295280546 -▁of 0.013096835231150048 -▁and 0.011855031910074663 -▁die 0.011740110104447195 -▁der 0.011334449579483175 -▁to 0.011119680102548058 -▁und 0.010981523899890322 -▁a 0.0072767267451170738 -▁is 0.0058704945092683571 -▁that 0.0044993657990812239 -▁zu 0.0044366939207065685 -▁for 0.0043121192137855853 -▁von 0.0040675083304576049 -▁den 0.00404126592572441 -▁ist 0.0034980023078314011 -▁für 0.003348984697007059 -▁on 0.0032371163601561119 -▁mit 0.0030027758910379895 -▁an 0.0029572542026773617 -▁( 0.0029510714966683284 -▁with 0.0029246434592179508 -▁) 0.0029235523934516504 -▁auf 0.0029215483177906345 -▁das 0.0028834633033857247 -▁The 0.0028719464980747804 -▁be 0.0028172227307337958 -▁are 0.0027216521887669549 -▁des 0.0026750621651768251 -▁I 0.0025450510715383408 -▁eine 0.0024546555151158259 -▁as 0.0023835430307435437 -▁this 0.002364900202146452 -▁: 0.0022545396575697667 -▁Sie 0.0022526795419751376 -▁im 0.0022435039819543785 -▁nicht 0.0021727665514392615 -▁" 0.002111117547220512 -▁we 0.0021092271242434853 -▁it 0.0021047454200714957 -▁- 0.0020964071014894603 -▁werden 0.0020879437649550356 -▁ein 0.0020739114469051217 -▁by 0.0020056478564781785 -▁have 0.0019806063817724737 -▁dass 0.0019659262434238191 -▁dem 0.00195816755353013 -▁not 0.0019549511825732247 -▁sich 0.0019108387874940678 -▁Die 0.0018981665132292293 -▁you 0.0018804480597952532 -▁wir 0.0018604792832182834 -▁es 0.001792825628862084 -▁will 0.0017900865991779349 -▁which 0.0017094765388469229 -▁auch 0.0016807148329519592 -▁was 0.0016336133722838762 -▁from 0.0016308061509893333 -s 0.0016028778981102931 -▁sind 0.0015479344022469286 -▁at 0.0015133082178581023 -▁or 0.0014936879761787004 -▁wird 0.0014589557159514837 -▁has 0.0014394491269560715 -▁In 0.0013971248674383508 -es 0.0013324034523290826 -▁über 0.001292912933065502 -▁als 0.0012920643263583797 -▁um 0.0012841237921703075 -▁can 0.0012529867451799613 -▁ich 0.0012514372802549033 -▁all 0.001250524270360187 -▁oder 0.0012454780911910495 -▁also 0.0011776880536140625 -▁our 0.0011752520977538858 -▁' 0.0011725736828345312 -▁haben 0.0011563819637888152 -▁European 0.0011486535812775236 -ing 0.0011432967514388146 -▁/ 0.0011248622860957041 -▁so 0.0011227597114418968 -ed 0.0011172134604632053 -▁aus 0.0011037645595244378 -- 0.001093854045480546 -▁hat 0.0010883911398034468 -▁einer 0.0010788973522675169 -▁! 0.0010655924113951362 -▁sie 0.0010651567427731763 -▁? 0.0010530944045790817 -▁wie 0.0010497833230521854 -en 0.0010272308421258522 -▁zur 0.0010010528405802516 -er 0.00098061429957612464 -▁einen 0.0009742270187359101 -▁s 0.00096388083607005842 -▁Das 0.0009567586012067112 -▁bei 0.00095570541966840772 -▁Union 0.000946503340688051 -▁your 0.00092571247636355665 -▁'s 0.00091399109622143085 -▁We 0.00090434198335071522 -▁Ich 0.00088891931413332878 -▁können 0.00087744418147318046 -▁but 0.00085214130559429959 -▁its 0.00084567446787529226 -▁nach 0.00082966838154675923 -▁– 0.00082007609501803835 -▁am 0.00081728402741469426 -▁would 0.00081509053061369532 -▁more 0.00080059602498222374 -▁their 0.00079561046057788072 -e 0.0007949436981651418 -▁einem 0.00079203797787780813 -▁uns 0.00077510372796336024 -▁been 0.00077380808736587892 -▁’ 0.0007671025790104935 -▁durch 0.00076420064714595947 -▁Wir 0.00076088956561906292 -▁vor 0.00075604417285830708 -▁zum 0.00075543044836476325 -▁This 0.000752498209117832 -▁It 0.00074820971450862512 -i 0.00074789527541625387 -o 0.00074594802609723241 -▁one 0.00074219369910277647 -▁diese 0.000739950952805382 -▁nur 0.0007375718232872 -a 0.00072052013226596293 -▁should 0.00070793499172551639 -▁kann 0.00070666587008763268 -▁Mr 0.00070654842898084345 -▁; 0.00069951332784188821 -▁Der 0.00069749788691247287 -▁dieser 0.000661568485080566 -▁Commission 0.0006507904222155538 -▁EU 0.00065002894923282351 -▁very 0.00064103902192924752 -▁Es 0.00063728090651199193 -▁must 0.00063708769694920966 -▁... 0.00063106031627496208 -▁A 0.00062999955789105931 -▁daß 0.0006262300772054048 -▁er 0.00062021406179955615 -▁Kommission 0.00061681963497106727 -▁there 0.00061680826970266834 -▁they 0.00061316001854660269 -▁noch 0.00061047781520444844 -y 0.000608780601790204 -▁new 0.00060785243820428913 -▁do 0.000599506542776654 -▁wurde 0.00059842305385595328 -▁other 0.00059783205989920752 -▁Hotel 0.00059718423960046692 -▁“ 0.00059128187687860784 -▁only 0.00058804656380770435 -▁aber 0.00057848079623858107 -▁like 0.00057809816553581615 -▁Herr 0.00057794662862383 -▁about 0.00057130552345603875 -▁time 0.00056780123236635993 -▁B 0.0005651569132522022 -▁wenn 0.00056440680553787093 -▁sehr 0.00054936676702324941 -▁S 0.0005473172302886373 -▁sein 0.000545858687510771 -▁müssen 0.00054376747812536267 -▁President 0.00054175961404154661 -▁de 0.00053396682834266082 -▁man 0.00053339098807711363 -▁P 0.00052664759549373168 -▁war 0.00051676360040943761 -▁these 0.00051601349269510635 -▁D 0.00051332750093015256 -in 0.00050956559709009733 -▁who 0.00050898217997895074 -▁up 0.00050648939777677923 -▁us 0.00050185236827000423 -▁L 0.000491991103722508 -▁diesem 0.00049076365473542057 -k 0.00049029767873106321 -▁if 0.00048509238580434031 -▁no 0.00048442562339160144 -▁T 0.00048395964738724413 -▁mehr 0.00048258823833376982 -▁C 0.00047809895731618129 -t 0.0004762426301443514 -▁out 0.00047573119306639828 -el 0.0004725943789882858 -▁alle 0.00046638136559685523 -▁Europäischen 0.00046511224395897152 -▁any 0.0004648356890945969 -▁M 0.00046275963340038713 -an 0.00046086163357776113 -▁% 0.00045811123862521321 -▁Europe 0.0004545463327707399 -▁K 0.00045376591767401145 -▁H 0.00045362195760762462 -▁Parliament 0.000451523171376617 -▁people 0.00044890537122205693 -▁into 0.00044856441317008819 -em 0.00044719300411661387 -▁were 0.00044709071670102327 -▁R 0.00044435926386247361 -on 0.00044034353569484162 -▁bis 0.000439150182512951 -ers 0.00043886605080297706 -▁unter 0.00043607398319963297 -▁möchte 0.00043362666207105726 -al 0.00043361529680265828 -▁F 0.00043222115721238606 -▁such 0.00043083080604491348 -et 0.0004271257285468531 -te 0.00042528076664342219 -▁what 0.00042159084283656037 -▁G 0.00042023458747428468 -▁States 0.00041885181315241138 -ic 0.00041759784520572634 -u 0.00041715838816096658 -ung 0.00041364273180288883 -▁report 0.00041314265999333464 -▁eines 0.00041152879188068259 -▁Member 0.0004082139219309864 -is 0.0004068879739511079 -ar 0.00040560748704482525 -▁my 0.00040498997412848184 -▁need 0.00040465659292211243 -▁countries 0.00040439140332613674 -▁than 0.00040345945131742213 -ischen 0.00040323214594944297 -▁vom 0.000401311415590019 -n 0.00039935658942539815 -or 0.00039776924027234365 -▁when 0.00039770483708474958 -b 0.00039470440622742454 -▁his 0.00039465515673102908 -▁well 0.0003938671647887013 -▁ihre 0.00039180247436289051 -ten 0.00038935894165711445 -▁Präsident 0.00038934757638871552 -▁work 0.00038863156447958115 -▁use 0.00038858610340598528 -▁most 0.00038749882606248497 -ig 0.00038653656667137317 -▁some 0.00038640018345058563 -▁them 0.00038526744503348945 -▁Council 0.00038520304184589538 -▁first 0.00038483177641152939 -▁[ 0.00038426351299158148 -▁] 0.000383395964170461 -▁sowie 0.00038317244722528148 -z 0.00037908095060165648 -▁now 0.00037827022812253079 -▁dieses 0.00037799746168095576 -▁make 0.00037741025614700959 -l 0.00037339831640217729 -▁gibt 0.00037294749408901861 -▁many 0.0003703713665852547 -▁hier 0.00037015921490847416 -▁important 0.0003695151830325332 -st 0.00036950002934133457 -▁2 0.00036651854059800785 -▁hotel 0.00036645413741041372 -. 0.00036620410150563665 -▁between 0.00036582147080287174 -r 0.00036152539934806549 -h 0.00036074119582853734 -▁W 0.00035615341581815785 -▁because 0.00035514569535345018 -▁zwischen 0.00035507371532025677 -m 0.00035470244988589084 -at 0.00035463804669829671 -▁N 0.00035365684519318664 -▁he 0.00035290673747885538 -▁take 0.0003528196037544634 -▁ 0.00035048593530987728 -▁un 0.00034889479773402308 -as 0.00034812953632849325 -de 0.00034769765612933283 -▁over 0.00034760294555934152 -▁ab 0.000347019528448195 -ische 0.00034593603952749426 -▁muss 0.00034415548081165748 -▁Parlament 0.00034415169238885779 -▁V 0.00034309851085055431 -d 0.00034082545717076268 -▁Bericht 0.00034081030347956405 -▁Ein 0.00034033296220680782 -▁p 0.0003383895013105859 -▁unsere 0.00033720751339709427 -▁1 0.00033614675501319146 -▁keine 0.00033532845568846648 -f 0.00033465790485292791 -c 0.00033442681106214911 -▁l 0.00033382824025980396 -ie 0.00033376004864941024 -▁„ 0.00033306676727707377 -▁just 0.00033155897500281197 -▁m 0.00033095661577766717 -▁d 0.00033049063977330987 -▁Ihnen 0.00032975568575017723 -os 0.00032974810890457794 -▁two 0.00032824410505311578 -▁b 0.00032818349028832135 -▁t 0.00032799406914833871 -▁information 0.00032786147435035087 -p 0.00032475117923183588 -▁support 0.00032380028510912306 -▁made 0.00032242129921004946 -▁years 0.00032187197790409981 -ation 0.000321553750388929 -▁had 0.00032003459284626819 -▁If 0.00031970121163989873 -ia 0.000318988988153564 -▁anderen 0.00031761000225449039 -ungs 0.00031675760712456853 -▁way 0.00031406025009121576 -▁Europa 0.00031304116435810917 -▁Zeit 0.00031275324422533552 -it 0.00031272672526573797 -▁Im 0.00031232515244897479 -▁those 0.00031115452980388209 -▁An 0.0003107605338327182 -▁Mitgliedstaaten 0.00031019227041277023 -▁world 0.00030959369961042514 -ungen 0.00030657432663910183 -ly 0.00030557797144279314 -▁where 0.000304259600308514 -▁3 0.00030364208739217061 -▁Wenn 0.00030276317330265115 -▁policy 0.0003001605268392897 -us 0.00029900505788539562 -igen 0.00029797839530668968 -▁immer 0.00029762607198632196 -▁dann 0.0002961485870944574 -▁system 0.00029494386864416781 -▁” 0.00029362549750988864 -▁being 0.00029199268728323827 -ern 0.00029102663946932683 -ieren 0.00029088646782573965 -iert 0.00029058718242456713 -den 0.00028888996901032263 -▁Er 0.00028796938227000705 -▁may 0.00028673814486011986 -▁diesen 0.00028641234049934974 -▁damit 0.00028520383362626051 -ige 0.00028451055225392404 -▁good 0.0002834384286016223 -w 0.00028319975796524415 -▁gegen 0.00028274135880648619 -▁wurden 0.00028190790579056259 -▁da 0.00028182077206617055 -▁even 0.00028167681199978378 -▁dies 0.000280888820057456 -g 0.00027835057678168862 -▁5 0.000277877023931732 -▁E 0.00027772169859694628 -▁Frau 0.00027716480044539729 -▁right 0.00027523649324037405 -▁Diese 0.00027388023787809835 -ate 0.00027381204626770458 -▁As 0.00027187237379428235 -ent 0.00027110332396595283 -▁area 0.00027040246574801708 -▁10 0.00026824306475221497 -▁part 0.00026770132029186466 -▁under 0.0002668224062023452 -▁For 0.00026663298506236256 -▁unserer 0.00026662161979396358 -▁order 0.00026452662198575561 -▁Ihre 0.00026443948826136357 -▁r 0.000264053069135799 -▁year 0.00026114734884846534 -▁Menschen 0.00026094656244008373 -▁habe 0.00026071168022650523 -le 0.00026065485388451044 -▁f 0.00026026843475894588 -ur 0.00025865456664629378 -▁sondern 0.00025842347285551497 -▁here 0.00025795749685115767 -▁4 0.00025772261463757922 -▁You 0.00025768473040958269 -▁seine 0.00025733998393481425 -▁jedoch 0.00025683233527966083 -▁market 0.00025633605189290628 -▁Europäische 0.00025486993226944064 -▁through 0.00025458201213666705 -▁place 0.00025317650727799584 -▁c 0.00025292647137321878 -um 0.00025271431969643823 -lich 0.00025267643546844171 -ist 0.00025222182473248338 -▁possible 0.00025163461919853721 -v 0.00025060416819703163 -▁same 0.00025013819219267433 -▁after 0.00024962296669192157 -▁& 0.00024953583296752953 -▁Welt 0.00024771360160089653 -▁neue 0.00024737264354892779 -▁Mit 0.00024724762559653928 -▁see 0.00024627021251422886 -▁heute 0.00024577392912747431 -▁own 0.00024378879558045627 -▁h 0.00024374333450686043 -▁There 0.000243409953300491 -▁Jahr 0.00024340237645489169 -▁political 0.0002428719972629403 -▁could 0.00024123918703628994 -▁want 0.0002393563409048625 -▁then 0.0002392123808384757 -▁within 0.00023903053654409238 -▁development 0.00023898507547049654 -liche 0.00023884111540410974 -▁Rat 0.000237496225310233 -▁Z 0.00023705676826547327 -ens 0.00023604904780076563 -sch 0.00023532545904603194 -lichen 0.0002352572674356382 -▁bereits 0.00023518528740244478 -▁rights 0.00023449579445290798 -▁international 0.00023401087633455244 -▁economic 0.00023374189831577708 -ter 0.00023311302013103471 -▁much 0.00023236291241670348 -▁both 0.00023191966694914411 -▁against 0.00023182874480195243 -▁mich 0.00023179464899675554 -▁Ab 0.00023129457718720138 -re 0.00023113925185241563 -▁used 0.00023113546342961596 -est 0.000230851331719642 -▁find 0.00023070358323045557 -ge 0.00023062781477446251 -il 0.00023023003038049895 -▁therefore 0.00022975647753054236 -▁how 0.00022897606243381388 -C 0.00022876012233423367 -▁Auf 0.00022872602652903681 -▁sollte 0.00022809335992149478 -▁ihrer 0.00022790393878151214 -▁public 0.00022761601864873854 -▁J 0.00022750236596474896 -▁Aus 0.0002274190206631566 -▁neuen 0.00022719171529517743 -der 0.00022707048576558854 -▁ohne 0.00022706290891998923 -▁europäischen 0.00022614611060247327 -▁Jahren 0.00022454360775822014 -▁n 0.0002244337434970302 -ik 0.00022375182739309269 -end 0.00022365332840030173 -▁does 0.00022338435038152637 -S 0.00022296383545076492 -▁andere 0.00022216447824003818 -ra 0.0002219220191808604 -ol 0.00022161136851128887 -im 0.00022131587153291596 -▁country 0.00022111508512453435 -▁zwei 0.00022075518495856734 -id 0.00022022480576661596 -▁ver 0.00021982323294985275 -▁ganz 0.00021969063815186491 -ier 0.00021882308933074442 -isch 0.00021805782792521457 -ien 0.00021801994369721804 -▁without 0.0002179517520868243 -▁still 0.00021741000762647394 -▁Entwicklung 0.00021617498179378713 -▁best 0.00021603102172740033 -▁number 0.00021578098582262324 -▁Sch 0.00021507633918188782 -▁great 0.00021483009169991039 -A 0.00021459899790913158 -ies 0.000214485345225142 -▁sollten 0.00021447776837954269 -▁finden 0.0002138110059668038 -▁me 0.00021370871855121317 -▁denen 0.00021362916167242047 -▁Frage 0.00021358748902162427 -▁different 0.00021328441519765206 -▁fact 0.00021275782442850032 -▁services 0.0002124547506045281 -▁wieder 0.00021221986839094963 -▁point 0.0002114280880258222 -▁social 0.00021109470681945273 -able 0.00021083709406907635 -j 0.00021065146135189336 -▁last 0.00021062873081509546 -▁free 0.00020841629190009823 -▁available 0.00020823065918291523 -ad 0.00020818898653211906 -▁st 0.00020784424005735065 -ant 0.00020618491087110274 -ir 0.00020607883503271247 -ch 0.00020606368134151384 -▁bietet 0.00020584774124193363 -▁view 0.00020555982110916004 -▁Vor 0.00020396110668770656 -ine 0.00020374516658812635 -ot 0.0002031958452821767 -▁say 0.00020283594511620969 -▁room 0.00020262379343942912 -▁So 0.00020247604495024266 -▁w 0.00020241164176264858 -T 0.00020219570166306837 -▁per 0.00020141149814354022 -▁Land 0.00020133572968754717 -▁selbst 0.00020131678757354891 -ian 0.00020101750217237633 -▁dazu 0.00020101371374957669 -▁Ver 0.00020079398522719684 -▁20 0.00020068790938880654 -▁la 0.00020055910301361836 -▁k 0.00020009312700926106 -▁liegt 0.00020006660804966351 -ab 0.00019964988154170168 -▁able 0.00019940363405972426 -▁get 0.00019939605721412494 -▁end 0.00019866110319099231 -▁St 0.00019848304731940862 -▁machen 0.00019843758624581281 -▁allen 0.00019835045252142078 -▁know 0.0001980284365834503 -▁viele 0.0001977405164506767 -▁already 0.00019761928692108781 -▁her 0.00019750563423709822 -▁future 0.00019730105940591697 -▁believe 0.00019680477601916244 -▁weil 0.00019664566226157702 -▁Unternehmen 0.0001966077780335805 -▁before 0.00019638804951120064 -am 0.00019536896377809405 -▁That 0.00019410741898580967 -▁O 0.00019338004180827635 -M 0.00019304666060190688 -▁today 0.00019291406580391904 -▁All 0.00019265645305354266 -▁course 0.00019254658879235272 -▁denn 0.00019229276446477598 -▁human 0.0001922586686595791 -▁too 0.00019191771060761035 -▁jetzt 0.00019174344315882634 -2 0.00019141763879805619 -ion 0.00019115623762488014 -ut 0.00019111456497408397 -▁process 0.00019085695222370759 -▁products 0.00019037961095095133 -▁service 0.00018986817387299821 -ere 0.00018911427773586731 -▁seiner 0.00018817853730435306 -▁darauf 0.0001877655992191909 -▁Arbeit 0.00018727310425523604 -▁think 0.00018717081683964541 -▁national 0.00018704958731005652 -▁cannot 0.00018685258932447458 -▁Lage 0.00018639040174291694 -▁data 0.00018575773513537493 -▁geht 0.00018545844973420235 -▁ob 0.00018531070124501591 -B 0.00018504551164904022 -▁schon 0.00018475380309346696 -▁But 0.00018459847775868118 -▁Maßnahmen 0.00018402263749313396 -▁Informationen 0.00018392792692314264 -▁set 0.00018392035007754333 -P 0.000183378605617193 -▁offers 0.000182548941024069 -▁case 0.00018220040612650096 -om 0.00018203371552331625 -ul 0.00018191248599372736 -▁dafür 0.00018085930445542388 -é 0.00018037817475986798 -▁einige 0.00018035165580027041 -▁Bereich 0.00017975687342072492 -▁2009 0.00017958639439474055 -un 0.00017946137644235202 -▁question 0.00017935530060396172 -▁6 0.0001790522267799895 -▁Commissioner 0.00017886659406280651 -▁Um 0.00017859382762123151 -▁city 0.00017841956017244749 -▁high 0.00017840819490404854 -▁15 0.000178370310676052 -▁Internet 0.00017790054624889506 -▁Nach 0.00017780962410170339 -ated 0.00017713528484336521 -▁Dies 0.0001763169855186402 -D 0.00017616544860665409 -▁en 0.00017590783585627769 -▁g 0.00017581312528628637 -▁Herrn 0.00017575629894429158 -▁level 0.00017542291773792214 -▁small 0.00017521076606114158 -▁during 0.00017500240280716068 -▁30 0.00017499103753876172 -1 0.00017485844274077388 -ro 0.0001748091932443784 -▁ihr 0.00017450990784320582 -▁bin 0.00017418410348243567 -▁wo 0.00017391133704086067 -ations 0.00017383935700766728 -▁ihren 0.00017319911355452596 -▁mir 0.00017317259459492839 -▁allem 0.00017309682613893534 -▁viel 0.00017277102177816518 -▁v 0.00017245279426299435 -ver 0.00017211562463382527 -▁e 0.00017195272245344018 -▁said 0.00017178224342745581 -▁financial 0.00017155872648227629 -▁debate 0.00017131247900029886 -▁Jahre 0.00017130490215469958 -▁real 0.00017083513772754263 -▁issue 0.00017081619561354436 -▁means 0.00017047144913877595 -▁every 0.00017030097011279158 -I 0.00017026308588479505 -▁At 0.0001702517206163961 -▁day 0.00017016837531480373 -▁Für 0.0001700433573624152 -▁example 0.00016994106994682457 -▁quality 0.00016984635937683325 -▁| 0.00016972512984724436 -▁proposal 0.00016948645921086622 -▁Stadt 0.00016946751709686795 -▁meine 0.00016914550115889747 -▁business 0.000169096251662502 -▁clear 0.00016908109797130337 -▁access 0.00016869467884573879 -▁including 0.00016861133354414645 -▁long 0.0001684938924373572 -▁rooms 0.00016765665099863393 -▁Eine 0.00016756194042864261 -▁On 0.00016756194042864261 -▁Zimmer 0.00016745207616745267 -F 0.00016736494244306066 -▁since 0.00016733084663786381 -iv 0.00016721340553107455 -▁Our 0.00016653148942713706 -▁However 0.00016641404832034781 -▁help 0.0001661753776839697 -▁unseren 0.00016585715016879886 -E 0.0001657169785252117 -▁situation 0.00016567530587441553 -3 0.000165637421646419 -▁wollen 0.00016540253943284053 -▁Teil 0.0001652358488296558 -▁offer 0.00016505779295807212 -x 0.00016399703457416936 -▁Art 0.00016385307450778253 -op 0.00016363334598540268 -▁each 0.00016304235202865684 -▁würde 0.00016294006461306621 -iger 0.00016187551780636378 -ive 0.00016165957770678357 -ischer 0.00016153834817719467 -▁particular 0.00016149288710359886 -▁back 0.00016143984918440371 -▁gut 0.00016105721848163877 -▁Daten 0.00015956457989857559 -▁Wie 0.00015910239231701795 -and 0.0001590493543978228 -▁Rahmen 0.00015883720272104226 -▁Und 0.00015852655205147073 -▁Parlaments 0.000158446995172678 -▁etwas 0.00015837501513948462 -▁während 0.00015837501513948462 -▁Committee 0.00015816286346270406 -▁Ziel 0.00015804163393311516 -▁möglich 0.00015780296329673705 -▁nun 0.00015754535054636067 -ag 0.00015741654417117246 -▁three 0.00015739002521157489 -▁common 0.00015692404920721761 -▁next 0.000156810396523228 -▁form 0.00015680660810042836 -▁sagen 0.00015679524283202941 -▁problem 0.00015663234065164434 -▁far 0.0001563709394784683 -ings 0.00015615499937888809 -▁areas 0.00015613226884209015 -ors 0.00015590875189691066 -▁doch 0.00015581025290411967 -▁He 0.00015557158226774156 -▁measures 0.00015515106733698009 -▁seit 0.00015498437673379538 -▁8 0.00015479495559381272 -▁12 0.00015473812925181793 -▁few 0.00015460174603103044 -▁give 0.00015449945861543981 -▁7 0.000153809965665903 -ell 0.00015357887187512418 -▁And 0.00015343112338593774 -▁18 0.00015329852858794989 -▁energy 0.0001531128958707669 -ap 0.00015310153060236795 -ierte 0.0001527757262415978 -▁provide 0.00015266965040320753 -age 0.00015247644084042522 -▁� 0.00015224534704964642 -▁Länder 0.000151999099567669 -▁insbesondere 0.00015183998581008357 -ale 0.00015176042893129085 -ions 0.00015150660460371411 -▁citizens 0.00015149145091251551 -▁taken 0.00015135885611452767 -▁To 0.00015127172239013563 -▁erhalten 0.00015115049286054674 -G 0.00015079816954017904 -ure 0.00015070345897018773 -▁agreement 0.00015051782625300473 -ip 0.00015043069252861272 -O 0.00015020338716063356 -▁weiter 0.00014998365863825371 -▁China 0.00014968437323708113 -L 0.00014961239320388772 -▁Al 0.00014941539521830577 -▁put 0.00014924870461512107 -▁again 0.00014854026955158598 -▁waren 0.00014838115579400056 -▁large 0.00014806671670162939 -ity 0.00014778637341445508 -▁special 0.00014724084053130507 -gen 0.00014714612996131375 -▁ensure 0.00014713097627011516 --S 0.00014710824573331723 -▁further 0.00014670667291655405 -4 0.00014668394237975612 -▁Un 0.00014647936754857489 -▁beim 0.00014606264104061306 -▁Bürger 0.0001459641420478221 -▁go 0.00014593762308822453 -▁Grund 0.00014584291251823321 -ug 0.00014583912409543357 -▁come 0.0001454830123522662 -ow 0.00014539587862787416 -▁re 0.00014533905228587937 -▁soll 0.00014473290463793494 -▁du 0.00014455484876635125 -▁Fall 0.00014454727192075194 -▁ersten 0.00014430860128437383 -▁problems 0.00014397900850080404 -▁company 0.00014388050950801308 -▁modern 0.00014382747158881793 -▁Kunden 0.00014369108836803041 -▁einfach 0.00014366456940843287 -▁ins 0.00014360395464363841 -▁Zusammenarbeit 0.00014334255347046236 -eln 0.00014333497662486307 -ete 0.00014331982293366445 -▁tun 0.00014289551958010334 -▁shall 0.00014286142377490646 -▁lassen 0.00014283111639250924 -▁necessary 0.00014281975112411029 -▁position 0.00014268715632612244 -▁Unterstützung 0.00014266442578932451 -ise 0.00014234998669695334 -▁change 0.00014231967931455612 -R 0.00014206585498697938 -▁Group 0.00014197872126258738 -▁security 0.00014197872126258738 -bar 0.0001418650685785978 -7 0.00014182718435060127 -▁given 0.00014141045784263944 -▁geben 0.00014135363150064465 -iz 0.00014089523234188668 -▁pro 0.00014084977126829084 -▁why 0.00014078157965789707 -▁Recht 0.00014067171539670716 -▁however 0.00014051260163912174 -▁Vorschlag 0.00014042925633752938 -od 0.00014026256573434464 -▁down 0.00013943290114122069 -ierten 0.00013937228637642626 -▁einmal 0.00013920938419604119 -▁vote 0.00013910709678045056 -▁besteht 0.00013899723251926062 -▁located 0.00013898586725086167 -▁while 0.00013893282933166652 -▁Problem 0.00013878508084248008 -▁hand 0.000138709312386487 -▁cooperation 0.00013861460181649568 -▁2010 0.000138535044937703 -▁2008 0.00013839487329411583 -ak 0.00013837214275731793 -▁stehen 0.00013825849007332834 -▁sch 0.00013817893319453562 -▁stay 0.00013817893319453562 -5 0.00013780766776016966 -▁Bei 0.00013779630249177071 -▁group 0.00013770538034457903 -▁main 0.00013761066977458771 -ang 0.00013719773168942555 -▁wichtig 0.00013717878957542729 -▁Sicherheit 0.00013706892531423735 -▁With 0.00013694390736184882 -▁drei 0.00013678100518146376 -sten 0.00013674690937626687 -▁always 0.00013648171978029118 -▁System 0.00013640973974709777 -▁US 0.00013628472179470923 -▁needs 0.00013625062598951235 -ierung 0.00013619758807031723 -▁Ländern 0.00013607635854072833 -▁steht 0.0001357657078711568 -▁life 0.00013575813102555749 -▁They 0.00013572024679756097 -▁Namen 0.00013560280569077174 -▁sehen 0.00013556492146277522 -▁law 0.00013536034663159396 -▁hope 0.00013523911710200506 -▁Fragen 0.0001350269654252245 -▁seinen 0.00013486027482203979 -▁Euro 0.0001346102389172627 -▁better 0.00013353811526496095 -▁result 0.00013296985184501305 -▁wäre 0.00013271223909463667 -▁legal 0.00013255691375985089 -▁United 0.00013251902953185436 -▁Ch 0.00013246220318985957 -▁50 0.00013232203154627241 -▁dabei 0.00013218564832548493 -▁protection 0.00013215155252028804 -ste 0.00013206063037309637 -▁Was 0.0001319545545347061 -▁full 0.00013195076611190645 -▁House 0.00013189393976991166 -8 0.00013183711342791687 -▁present 0.00013140523322875645 -▁action 0.00013137492584635923 -enden 0.00013127642685356825 -▁Kollegen 0.00013116656259237833 -▁zusammen 0.00013090516141920229 -▁using 0.00013088243088240435 -▁letzten 0.00012994290202809048 -▁What 0.00012987092199489707 -▁open 0.00012966255874091617 -ue 0.00012960952082172102 -▁100 0.00012951102182893006 -▁USA 0.00012944661864133596 -▁worden 0.00012929887015214949 -▁did 0.00012923446696455539 -▁Am 0.0001290336805561738 -tr 0.00012889729733538632 -▁ge 0.00012886320153018943 -N 0.0001288253173021929 -ert 0.00012849572451862311 -▁around 0.00012842753290822937 -▁Community 0.00012841237921703075 -▁based 0.00012839343710303248 -ck 0.00012825326545944533 -ates 0.00012764332938870122 -ac 0.00012751452301351305 --B 0.00012734783241032831 -ism 0.00012709021965995193 -▁große 0.0001270447585863561 -▁Verfügung 0.00012695383643916442 -▁großen 0.00012695383643916442 -▁Ihr 0.0001269007985199693 -io 0.00012669622368878804 -▁welche 0.00012652953308560331 -▁working 0.00012647270674360852 -▁current 0.00012637041932801789 -▁deren 0.00012610144130924256 -▁Sp 0.00012580215590806998 -oc 0.00012574911798887483 -▁power 0.00012565819584168318 -▁Seite 0.00012564304215048456 -▁Bedeutung 0.00012560894634528768 -6 0.00012533239148091303 -▁terms 0.00012529071883011686 -▁production 0.00012502552923414115 -▁muß 0.00012486641547655575 -els 0.00012463153326297728 -licher 0.00012461259114897902 -▁aller 0.00012460880272617935 -▁become 0.00012455576480698423 -▁These 0.00012453682269298596 -▁centre 0.00012452545742458698 -▁whether 0.00012450651531058872 -▁Mrs 0.00012436255524420192 -▁particularly 0.0001243322478618047 -▁Aber 0.00012392309819944219 -▁Markt 0.00012386248343464776 -▁Möglichkeit 0.00012386248343464776 -▁zurück 0.00012360487068427138 -▁together 0.00012337377689349254 -W 0.00012335483477949428 -H 0.00012333968108829566 -ating 0.00012315783679391233 -▁* 0.00012301008830472587 -ke 0.00012286612823833907 -▁When 0.00012283582085594185 -▁another 0.00012283582085594185 -▁local 0.00012267670709835643 -▁Auch 0.00012254790072316823 -▁Als 0.00012254032387756895 -▁km 0.00012206298260481269 -▁trade 0.0001218129467000356 -th 0.00012169171717044671 -ess 0.00012162731398285261 -▁water 0.00012153260341286129 -▁second 0.00012124089485728803 -K 0.0001211310305960981 -ets 0.00012083174519492552 -▁children 0.00012080901465812762 -ill 0.00012040365341856476 -▁Politik 0.000120248328083779 -eten 0.00011994146583700712 -▁Mittel 0.00011974825627422483 -U 0.00011974446785142517 -▁issues 0.00011966491097263248 -ts 0.00011958914251663941 -▁bieten 0.000119430028759054 -▁2000 0.00011937320241705921 -▁Unter 0.00011934289503466198 -▁German 0.00011924060761907135 -▁davon 0.00011920651181387448 -▁Thema 0.0001191648391630783 -▁2007 0.00011849807675033942 -ard 0.0001184109430259474 -▁Kommissar 0.00011831623245595608 -▁conditions 0.00011814954185277136 -▁darüber 0.00011814196500717205 -ud 0.0001179942165179856 -▁daher 0.00011797906282678699 -ay 0.00011782373749200122 -▁alles 0.00011751687524522934 -▁stellen 0.00011747520259443317 -▁kein 0.00011731608883684775 -▁Weg 0.00011727820460885122 -▁certain 0.00011717591719326059 -▁gegenüber 0.00011702816870407414 -he 0.00011694103497968212 -▁Über 0.00011686905494648872 -▁Probleme 0.00011686147810088941 -▁systems 0.00011685011283249046 -ber 0.00011667205696090677 -▁Kinder 0.0001166455380013092 -▁Frauen 0.0001165735579681158 -▁eigenen 0.00011635382944573593 -▁experience 0.00011624396518454601 -▁page 0.00011617198515135261 -▁Beispiel 0.00011612652407775677 -▁various 0.0001160280250849658 -▁global 0.00011598256401136997 -▁Weise 0.00011589543028697795 -▁role 0.00011582723867658421 -▁16 0.0001157552586433908 -▁health 0.00011574389337499184 -▁sowohl 0.00011572495126099358 -▁11 0.00011563402911380192 -▁women 0.00011543703112821997 -▁befindet 0.00011541051216862239 -▁whole 0.00011541051216862239 -▁25 0.00011539914690022344 -▁weitere 0.00011534232055822865 --M 0.0001152665521022356 -▁Regierung 0.00011524003314263802 -▁companies 0.00011520214891464149 -▁kommen 0.00011519457206904218 -▁industry 0.00011511501519024947 -ok 0.00011498620881506128 -▁Man 0.0001149369593186658 -▁Le 0.00011476648029268142 -▁respect 0.00011468313499108907 --T 0.00011458463599829809 -▁Bar 0.00011458463599829809 -▁sector 0.00011451265596510468 -▁politischen 0.00011450886754230504 -▁Ihrer 0.00011447477173710815 -▁basis 0.00011447477173710815 -▁Zukunft 0.00011434975378471962 -▁Schutz 0.00011434217693912031 -▁New 0.0001143270232479217 -▁9 0.00011430429271112378 -▁könnte 0.00011425125479192865 -▁erreichen 0.00011422094740953142 -▁project 0.00011411487157114114 -▁sp 0.00011409592945714288 -▁unser 0.00011403531469234843 -▁wissen 0.00011402394942394948 -▁Tr 0.00011400879573275086 -▁state 0.00011399364204155225 -▁Ende 0.00011396333465915504 -▁especially 0.00011395196939075607 -▁Hier 0.00011389135462596162 -▁car 0.00011380422090156962 -▁really 0.00011376254825077343 -▁account 0.00011368677979478038 -▁Service 0.00011364131872118454 -▁natürlich 0.00011359206922478906 -▁No 0.00011358070395639009 -▁control 0.00011356933868799114 -her 0.00011350493550039704 -be 0.00011347462811799982 -▁meiner 0.00011341780177600503 -▁research 0.00011338370597080816 -▁wirklich 0.00011325489959561996 -ne 0.00011323974590442135 -▁matter 0.00011319807325362517 -▁besonders 0.00011306547845563732 -▁budget 0.00011298213315404496 -▁hatte 0.00011297834473124531 -▁unterstützen 0.00011285711520165642 -9 0.00011281544255086024 -▁close 0.00011277755832286371 -▁staff 0.0001124896381900901 -▁+ 0.00011186454842814739 -ä 0.00011175847258975712 -ov 0.0001114705524569835 -▁EUR 0.00011142887980618733 -▁done 0.00011133795765899565 -▁government 0.00011123945866620468 -▁ihnen 0.00011113717125061406 -▁hin 0.0001111258059822151 -▁verschiedenen 0.00011099321118422725 -▁entfernt 0.0001108681932318387 -▁Staaten 0.00011083030900384217 -▁Tag 0.00011074317527945017 -▁z 0.00011030750665749009 -▁Haus 0.00011008777813511024 -aus 0.00011000443283351787 -ical 0.00010999306756511892 -V 0.000109970337028321 -▁private 0.00010995139491432274 -▁create 0.00010992866437752482 -▁Seiten 0.00010981501169353524 -▁innerhalb 0.00010980364642513627 -▁La 0.00010978849273393766 -▁location 0.00010972030112354392 -▁look 0.00010963695582195155 -▁_ 0.00010958012947995676 -▁transport 0.0001093604009575769 -▁Mar 0.00010932251672958037 -▁vielen 0.00010920128719999148 -▁region 0.00010911036505279981 -▁dort 0.00010891715549001752 -og 0.00010864438904844252 -ics 0.00010861029324324565 -▁site 0.00010851179425045468 -▁direkt 0.0001084625447540592 -eg 0.00010816325935288662 -▁Programm 0.00010813674039328905 -▁Germany 0.00010803445297769843 -▁major 0.00010801172244090051 -▁Re 0.00010797762663570364 -▁X 0.00010792837713930815 -▁Rolle 0.00010768970650293002 -ment 0.00010766697596613211 -art 0.00010761393804693696 -▁pr 0.00010736390214215988 -ein 0.00010729571053176614 -se 0.00010727297999496822 --K 0.00010712144308298211 -▁single 0.000106969906170996 -▁Umwelt 0.00010687898402380432 -▁U 0.00010660242915942968 -▁zwar 0.00010656833335423279 -ben 0.00010653044912623627 -▁old 0.00010646983436144182 -▁specific 0.0001064395269790446 -ans 0.000106424373287846 -▁million 0.00010641679644224669 -▁internationalen 0.00010641300801944703 -▁Br 0.00010636375852305155 -all 0.00010624252899346266 -▁möchten 0.00010606826154467863 -▁something 0.00010600764677988418 -ib 0.00010584095617669947 -land 0.00010572351506991023 -▁itself 0.00010553409392992759 -▁several 0.00010544317178273592 -▁start 0.00010519692430075849 -▁personal 0.00010516661691836127 -▁off 0.0001050681179255703 -▁rapporteur 0.00010494688839598141 -▁Ihren 0.0001049393115503821 -ins 0.00010472715987360154 -▁general 0.00010470821775960328 -▁Alle 0.00010467033353160675 -▁Uhr 0.00010442787447242898 -▁less 0.00010414374276245501 -▁subject 0.00010407933957486092 -▁continue 0.00010393916793127377 --s 0.00010393537950847411 -up 0.00010392780266287481 -ator 0.00010386339947528071 -▁making 0.00010374595836849148 -▁2006 0.00010354896038290953 -▁Richtlinie 0.00010348834561811508 -▁programme 0.00010343151927612029 -ah 0.0001033746929341255 -▁excellent 0.0001033746929341255 -▁resolution 0.00010329513605533279 -▁third 0.00010325346340453661 -▁wish 0.00010298448538576126 -▁gentlemen 0.00010268141156178904 -▁often 0.00010243516407981162 -▁least 0.00010240106827461473 -▁Ar 0.00010231014612742307 -elle 0.00010230256928182376 -heit 0.00010221543555743175 -ass 0.00010217755132943522 -▁away 0.00010198434176665293 -▁le 0.0001019540343842557 -▁name 0.00010175703639867377 -▁14 0.00010170021005667898 -▁going 0.00010151457733949598 -▁Hotels 0.00010138198254150814 -▁Zu 0.0001013365214679123 -▁following 0.00010127211828031821 -▁management 0.00010120392666992446 -▁Form 0.00010116225401912827 -▁ch 0.00010093873707394877 -▁erste 0.00010093494865114911 -▁regard 0.00010084023808115779 -▁europäische 0.0001007909885847623 -▁Software 0.00010068491274637203 -ial 0.00010064324009557585 -▁individual 0.00010054852952558453 -▁directive 0.00010053716425718557 -▁Wirtschaft 0.00010045381895559321 -ina 0.00010041972315039634 -▁17 0.00010038941576799911 -▁rules 0.00010027197466120987 -ea 0.00010018484093681786 -ner 0.00010013559144042237 -▁crisis 0.00010011664932642411 -▁World 0.00010005224613883002 -▁short 9.9984054528436261e-05 -if 9.9961323991638342e-05 -.com 9.9904497649643551e-05 -▁Artikel 9.9749172314857787e-05 -ER 9.9722653355260226e-05 -▁software 9.95483859064762e-05 -▁Y 9.94915595644814e-05 --F 9.9461252182084177e-05 -▁environment 9.9430944799686961e-05 -ons 9.929456157889946e-05 -▁ask 9.9245312082503966e-05 -ce 9.92263699685057e-05 -▁hinaus 9.92112162773071e-05 -▁online 9.9169543626510912e-05 -▁food 9.9146813089712993e-05 -▁money 9.9124082552915074e-05 -▁führen 9.910514043891681e-05 -▁found 9.9071044633719938e-05 -▁Meinung 9.9040737251322722e-05 -▁Da 9.89536035269307e-05 -▁forward 9.8930872990132782e-05 -▁welcome 9.8923296144533485e-05 -als 9.8915719298934174e-05 -▁once 9.8885411916536958e-05 -▁price 9.8707356044953274e-05 -▁product 9.8620222320561254e-05 -▁towards 9.86088570521623e-05 -▁Fl 9.8593703360963693e-05 -ile 9.8563395978566463e-05 -ach 9.8548242287367855e-05 -iges 9.8442166448977584e-05 -sp 9.8252745308994934e-05 -▁13 9.8245168463395637e-05 -▁little 9.8230014772197029e-05 -▁weit 9.8104996819808481e-05 -ende 9.8101208397008825e-05 -▁range 9.79989209814182e-05 -▁period 9.7775404036238688e-05 -▁thank 9.7438234407069585e-05 -▁things 9.7370042796675842e-05 -▁until 9.7123795314698415e-05 -la 9.7085911086701888e-05 -au 9.704802685870536e-05 -▁family 9.7013931053508475e-05 -▁Leben 9.6930585751916123e-05 -▁ent 9.6858605718722711e-05 -▁institutions 9.6858605718722711e-05 -▁Zusammenhang 9.6775260417130359e-05 -▁Situation 9.6767683571531048e-05 -ub 9.6763895148731393e-05 -▁o 9.6676761424339386e-05 -▁unserem 9.6646454041942156e-05 -▁home 9.6612358236745284e-05 -▁beiden 9.645703290195952e-05 -ous 9.6430513942361945e-05 -▁Madam 9.64267255195623e-05 -ort 9.6271400184776539e-05 -▁Be 9.6188054883184174e-05 -▁customers 9.61615359235866e-05 -▁kommt 9.6085767467593545e-05 -Y 9.5782693643621327e-05 -▁days 9.5733444147225833e-05 -▁Personen 9.5661464114032434e-05 -▁air 9.5468254551250143e-05 -ite 9.54000629408564e-05 -ze 9.5051528043288343e-05 -▁sicher 9.5028797506490424e-05 -ler 9.4918933245300484e-05 -qu 9.4865895326105349e-05 -▁aid 9.484316478930743e-05 -▁design 9.4839376366507788e-05 -▁Treaty 9.46688973405234e-05 -▁fast 9.4631013112526875e-05 -▁progress 9.4456745663742847e-05 -ger 9.44453803953439e-05 -▁increase 9.4426438281345631e-05 -▁him 9.4335516134153968e-05 -▁approach 9.4301420328957083e-05 -▁Dieses 9.4278689792159164e-05 -ost 9.4244593986962292e-05 -pl 9.4225651872964028e-05 -▁opportunity 9.4206709758965765e-05 -ement 9.4191556067767156e-05 -▁24 9.41006339205755e-05 -hr 9.4036230732981392e-05 -ob 9.3941520162990074e-05 -á 9.3850598015798411e-05 -ary 9.383544432459979e-05 -▁call 9.3702849526611944e-05 -▁growth 9.3630869493418545e-05 -ast 9.3623292647819235e-05 -X 9.3592985265422018e-05 -one 9.3558889460225146e-05 -▁'t 9.3532370500627572e-05 -▁Rechts 9.34982746954307e-05 -är 9.34338715078366e-05 -▁® 9.34338715078366e-05 -▁rather 9.3426294662237288e-05 -▁00 9.3395987279840071e-05 -▁Hilfe 9.33618914746432e-05 -▁International 9.333158409224597e-05 -▁y 9.3214142985456732e-05 -▁quite 9.31762587574602e-05 -▁activities 9.3100490301467151e-05 -▁San 9.30398755366727e-05 -▁agree 9.2998202885876535e-05 -ide 9.296410708067965e-05 -▁stellt 9.296410708067965e-05 -▁Mal 9.2945164966681386e-05 -▁play 9.2930011275482778e-05 -uch 9.2926222852683123e-05 -ance 9.2748166981099439e-05 -▁Members 9.2717859598702223e-05 -▁Region 9.2672398525106385e-05 -▁ja 9.2445093157127222e-05 -▁Lösung 9.2399632083531384e-05 -▁etwa 9.2361747855534856e-05 -▁positive 9.2327652050337985e-05 -▁Präsidentin 9.2304921513540066e-05 -▁bringen 9.2229153057547011e-05 -▁list 9.2024578226365767e-05 -▁2005 9.1914713965175827e-05 -are 9.1895771851177563e-05 -▁jeder 9.1725292825193191e-05 --P 9.1710139133994583e-05 -▁provided 9.1679831751597366e-05 -▁weniger 9.1649524369200137e-05 -ick 9.1630582255201873e-05 -▁bzw. 9.1300989471632081e-05 -▁resources 9.1229009438438682e-05 -▁allow 9.1210067324440418e-05 -▁fully 9.1179759942043188e-05 -chen 9.1145664136846316e-05 -▁40 9.1054741989654654e-05 -▁might 9.1016857761658126e-05 -▁daran 9.1013069338858471e-05 -▁line 9.0994127224860207e-05 -▁gilt 9.095624299686368e-05 -▁Deshalb 9.0941089305665072e-05 -▁i 9.0899416654868889e-05 -ec 9.0853955581273051e-05 -▁Bank 9.0744091320083125e-05 -▁Durch 9.0664534441290415e-05 -▁bitte 9.05698238712991e-05 -▁del 9.0478901724107421e-05 -▁j 9.0331153234920968e-05 -▁indem 9.0255384778927913e-05 -fl 9.0228865819330339e-05 -▁value 9.0001560451351175e-05 -ied 8.9880330921762283e-05 -▁gesagt 8.9880330921762283e-05 -▁favour 8.986896565336333e-05 -▁El 8.9857600384964377e-05 -▁decision 8.9796985620169931e-05 -zen 8.9641660285384167e-05 -▁yet 8.9592410788988673e-05 -▁Berlin 8.95583149837918e-05 -ys 8.9482546527798747e-05 -▁Kosten 8.9478758104999091e-05 -▁sea 8.9372682266608821e-05 -▁include 8.93347980386123e-05 -▁September 8.9262818005418881e-05 -▁After 8.9247664314220273e-05 -▁town 8.9190837972225482e-05 -▁interest 8.9092338979434508e-05 -▁technology 8.9016570523441454e-05 -▁State 8.8982474718244582e-05 -▁nämlich 8.891807153065048e-05 -▁left 8.8857456765856034e-05 -▁ladies 8.8838514651857771e-05 -▁sogar 8.8724861967868189e-05 -▁fest 8.8721073545068534e-05 -its 8.862257455227756e-05 -00 8.8588478747080688e-05 -▁se 8.8580901901481377e-05 -▁field 8.8478614485890762e-05 -▁having 8.8425576566695613e-05 -▁reason 8.8421788143895971e-05 -▁contact 8.8402846029897708e-05 -▁Produkte 8.8383903915899444e-05 -▁ideal 8.836496180190118e-05 -aten 8.8327077573904653e-05 -▁Millionen 8.8171752239118889e-05 -▁Europas 8.80580995551293e-05 -▁proposals 8.8020215327132766e-05 -▁text 8.7963388985137976e-05 -▁amendments 8.7808063650352211e-05 -igkeit 8.7607277241970622e-05 -▁solution 8.7588335127972359e-05 -▁al 8.7482259289582074e-05 -▁times 8.74557403299845e-05 -▁nehmen 8.7429221370386939e-05 -▁among 8.7425432947587284e-05 -▁Gemeinschaft 8.7398913987989709e-05 -▁Bild 8.7364818182792837e-05 -▁members 8.735724133719354e-05 -▁di 8.7315568686397357e-05 -▁easy 8.7281472881200472e-05 -igung 8.7273896035601175e-05 -ised 8.7152666506012282e-05 -▁Punkt 8.7141301237613329e-05 -▁2004 8.7137512814813674e-05 -▁minutes 8.7095840164017491e-05 -▁et 8.703143697642339e-05 -EN 8.7004918016825829e-05 -▁safety 8.69746106344286e-05 -▁framework 8.6883688487236936e-05 -▁show 8.6815496876843193e-05 -igt 8.6690478924454645e-05 -▁Rates 8.6671536810456381e-05 -▁costs 8.6648806273658462e-05 -ve 8.6573037817665408e-05 -our 8.6516211475670617e-05 -▁economy 8.6345732449686244e-05 -▁above 8.6197983960499777e-05 -men 8.6144946041304642e-05 -ö 8.6133580772905689e-05 -▁ebenfalls 8.5936582787323742e-05 -▁Web 8.5807776412135552e-05 -▁politische 8.5701700573745268e-05 -▁ihn 8.5694123728145957e-05 -▁adopted 8.56107784265536e-05 -▁Unsere 8.560699000375395e-05 -▁content 8.5535009970560551e-05 -▁Dieser 8.552743312496124e-05 -▁taking 8.5512279433762632e-05 -▁One 8.5413780440971658e-05 -▁Deutschland 8.5391049904173739e-05 -br 8.5281185642983813e-05 -▁enjoy 8.5197840341391448e-05 -▁gemacht 8.5197840341391448e-05 -iss 8.5194051918591792e-05 -▁makes 8.5175109804593529e-05 -▁schnell 8.5053880275004636e-05 -▁addition 8.4807632793027209e-05 -▁called 8.4788690679028946e-05 -▁seinem 8.4750806451032418e-05 -isches 8.4686403263438317e-05 -▁ihm 8.4572750579448735e-05 -▁UN 8.4557596888250127e-05 -zeit 8.4515924237453944e-05 -▁behalf 8.4478040009457417e-05 -▁Preis 8.4398483130664707e-05 -ett 8.43833294394661e-05 -▁darf 8.4315137829072355e-05 -▁website 8.4307560983473044e-05 -▁gehen 8.4296195715074092e-05 -▁opinion 8.4220427259081024e-05 -▁Gr 8.4209061990682071e-05 --L 8.4163600917086233e-05 -▁Aussprache 8.4102986152291787e-05 -▁Geld 8.4102986152291787e-05 -▁Bezug 8.4046159810297e-05 -▁dessen 8.4008275582300469e-05 -▁Ort 8.3932507126307414e-05 -▁Sh 8.39173534351088e-05 -▁others 8.3818854442317832e-05 -▁unterstützt 8.3799912328319569e-05 -ore 8.3792335482720272e-05 -▁fl 8.3754451254723731e-05 -▁legislation 8.3652163839133115e-05 -▁provides 8.3405916357155675e-05 -▁genau 8.3402127934356033e-05 -▁difficult 8.33566668607602e-05 -▁she 8.3318782632763668e-05 -▁klar 8.3314994209964012e-05 -▁bar 8.32998405187654e-05 -▁gute 8.3250591022369911e-05 -▁application 8.3243014176770613e-05 -▁past 8.3133149915580687e-05 -▁results 8.3129361492781032e-05 -▁key 8.3110419378782768e-05 -▁building 8.2992978271993531e-05 -▁Fraktion 8.2943728775598037e-05 -▁standard 8.2795980286411584e-05 -elt 8.2792191863611929e-05 -▁points 8.2758096058415057e-05 -▁projects 8.2735365521617138e-05 -ium 8.2731577098817482e-05 -▁darin 8.2485329616840055e-05 -▁2003 8.2428503274845264e-05 -▁attention 8.2409561160847e-05 -▁beautiful 8.2360311664451507e-05 -▁Haupt 8.2288331631258108e-05 -▁due 8.2269389517259845e-05 -▁Rechte 8.224287055766227e-05 -▁erst 8.2167102101669216e-05 -liches 8.1954950424888661e-05 -▁Kultur 8.18791819688956e-05 -ich 8.1826144049700471e-05 -▁concerned 8.1814778781301518e-05 -▁gerade 8.1795836667303254e-05 -ismus 8.1731433479709153e-05 -▁kind 8.1704914520111578e-05 -▁glaube 8.1602627104520949e-05 -▁dar 8.1587473413322341e-05 -▁responsibility 8.1553377608125469e-05 -▁person 8.1488974420531368e-05 --D 8.1477609152132415e-05 -▁sh 8.14700323065331e-05 -▁policies 8.14473017697352e-05 -▁simply 8.1435936501336232e-05 -▁con 8.1432148078536577e-05 -▁Qualität 8.139426385054005e-05 -▁dürfen 8.1337437508545259e-05 -me 8.1250303784153252e-05 -▁öffentlichen 8.1238938515754285e-05 -▁nichts 8.1019209993374433e-05 -▁Website 8.0969960496978939e-05 -▁einigen 8.0939653114581723e-05 -▁TV 8.09244994233831e-05 -▁questions 8.09017688865852e-05 -▁risk 8.0651732981808113e-05 -▁groups 8.0466100264625119e-05 -▁heart 8.0458523419025822e-05 -▁importance 8.0409273922630328e-05 -▁Restaurant 8.0333505466637274e-05 -▁jeden 8.0197122245849773e-05 -▁schaffen 8.0121353789856719e-05 -▁brauchen 8.0094834830259144e-05 -▁light 8.001906637426609e-05 -▁Spiel 8.0011489528666793e-05 -▁effective 8.0000124260267826e-05 -▁known 7.9984970569069218e-05 -▁step 7.9916778958675461e-05 -kt 7.9867529462279981e-05 -ain 7.985995261668067e-05 -▁Abstimmung 7.9856164193881028e-05 -▁Hand 7.9852375771081373e-05 -▁Finanz 7.9806914697485535e-05 -▁sollen 7.9776607315088318e-05 -▁Dank 7.9549301947109141e-05 -▁ihrem 7.9530359833110878e-05 -▁society 7.9473533491116087e-05 -▁keinen 7.943564926311956e-05 -▁natural 7.9424283994720607e-05 -▁nationalen 7.9212132317940052e-05 -ica 7.9162882821544558e-05 -▁complete 7.9117421747948734e-05 -▁environmental 7.9075749097152551e-05 -▁version 7.9056806983154288e-05 -▁Park 7.9041653291955679e-05 -▁meisten 7.9026499600757058e-05 -▁greater 7.90075574867588e-05 -▁besser 7.8981038527161233e-05 -ü 7.896209641316297e-05 -▁top 7.893936587636505e-05 -▁handelt 7.88711742659713e-05 -▁strategy 7.8848443729173388e-05 -ust 7.8840866883574077e-05 --C 7.8795405809978239e-05 -pr 7.872342577678484e-05 -ellen 7.8602196247195947e-05 -▁2001 7.8363525610817831e-05 -▁standards 7.8337006651220257e-05 -ster 7.8242296081228939e-05 -av 7.8223353967230675e-05 -▁Wasser 7.79884717536522e-05 -▁weiterhin 7.7943010680056363e-05 -▁Gesellschaft 7.77876853452706e-05 --A 7.7742224271674774e-05 -▁along 7.7689186352479625e-05 -▁event 7.766645581568172e-05 -▁principle 7.763614843328449e-05 -ade 7.7590687359688652e-05 -▁central 7.7530072594894219e-05 -▁betrifft 7.7465669407300117e-05 -▁Wahl 7.742778517930359e-05 -les 7.7333074609312272e-05 -▁deal 7.7314132495314008e-05 -▁tr 7.72989788041154e-05 -▁National 7.7261094576118873e-05 -ges 7.7143653469329636e-05 -fen 7.7136076623730325e-05 -▁required 7.708303870453519e-05 -▁Ebene 7.7075461858935879e-05 -▁words 7.706030816773727e-05 -▁19 7.6931501792549081e-05 -▁views 7.6848156490956715e-05 -▁user 7.68405796453574e-05 -▁four 7.6700408001770248e-05 -▁bekannt 7.668525431057164e-05 -▁serious 7.6651158505374768e-05 -▁Produkt 7.65602363581831e-05 -▁Se 7.64314299829949e-05 -man 7.6401122600597686e-05 -▁bedeutet 7.6223066729014e-05 -▁breakfast 7.6105625622224766e-05 -▁Windows 7.6041222434630664e-05 -▁macht 7.6037434011831022e-05 -▁training 7.5976819247036576e-05 -▁€ 7.5976819247036576e-05 -▁total 7.5935146596240393e-05 -▁deutlich 7.589347394544421e-05 -▁deshalb 7.5870743408646291e-05 -▁Energie 7.5866954985846636e-05 -▁besten 7.583664760344942e-05 -▁clearly 7.5825282335050467e-05 -▁meeting 7.578739810705394e-05 -▁0 7.575709072465671e-05 -▁Projekt 7.5722994919459838e-05 -▁sei 7.5673745423064344e-05 -▁pay 7.5556304316275107e-05 -▁solche 7.551842008827858e-05 -ri 7.5499477974280316e-05 -▁000 7.5480535860282053e-05 -▁Jahres 7.5450228477884836e-05 --R 7.5442651632285526e-05 -▁authorities 7.538961371309039e-05 -▁via 7.5351729485093863e-05 -▁inter 7.52683841835015e-05 -▁recent 7.5245653646703579e-05 -▁near 7.5082751466318517e-05 -▁voll 7.5078963043518862e-05 -▁essential 7.50600209295206e-05 -▁peace 7.4942579822731361e-05 -▁Vertrag 7.4900907171935178e-05 -▁cultural 7.4881965057936915e-05 -▁November 7.4832715561541435e-05 -▁aufgrund 7.4760735528348036e-05 -az 7.47228513003515e-05 -▁technical 7.4684967072354968e-05 -▁Zugang 7.4624352307560535e-05 -und 7.459783334796296e-05 -▁würden 7.459025650236365e-05 -▁young 7.454100700596817e-05 -▁achieve 7.4533430160368859e-05 -▁pages 7.45296417375692e-05 -ness 7.45220648919699e-05 -auf 7.4351585865985533e-05 -▁bed 7.43061247923897e-05 -▁wichtige 7.42341447591963e-05 -▁Arbeits 7.4226567913596986e-05 -▁Von 7.4215202645198033e-05 -▁IT 7.4203837376799066e-05 -ene 7.4147011034804276e-05 -eh 7.4090184692809485e-05 -ou 7.4067454156011566e-05 -▁address 7.4010627814016775e-05 -▁feel 7.4003050968417478e-05 -▁bring 7.3881821438828585e-05 -kl 7.3790899291636922e-05 -ph 7.3771957177638659e-05 -▁internal 7.3734072949642118e-05 -▁findet 7.3677246607647327e-05 -▁directly 7.366966976204803e-05 -▁side 7.3662092916448719e-05 -▁facilities 7.3635573956851158e-05 -▁visit 7.3525709695661218e-05 -▁longer 7.3514344427262266e-05 -▁Menschenrechte 7.3408268588871981e-05 -▁Natur 7.3400691743272684e-05 -▁fundamental 7.3385538052074062e-05 -▁solutions 7.335144224687719e-05 -í 7.3336288555678582e-05 -▁success 7.3328711710079271e-05 -▁enough 7.32946159048824e-05 -▁statt 7.32870390592831e-05 -▁months 7.3211270603290034e-05 -▁Richtung 7.3199905334891082e-05 -▁nächsten 7.3199905334891082e-05 -▁Ansicht 7.2923350470516425e-05 -▁el 7.2866524128521634e-05 -▁proposed 7.2855158860122681e-05 -▁nature 7.2851370437323026e-05 -▁Gebiet 7.2805909363727188e-05 -▁2002 7.2802120940927546e-05 -▁responsible 7.2786967249728924e-05 -ität 7.2771813558530316e-05 -ice 7.2737717753333444e-05 -▁employment 7.26922566797376e-05 -▁April 7.2684679834138309e-05 -▁guests 7.2650584028941423e-05 -▁currently 7.2620276646544207e-05 -isierung 7.2597546109746288e-05 -▁nutzen 7.2574815572948369e-05 -▁add 7.245358604335949e-05 -▁history 7.2438432352160868e-05 -of 7.242327866096226e-05 -▁Umsetzung 7.2366452318967469e-05 -▁Nähe 7.2358875473368158e-05 -▁requirements 7.2226280675380313e-05 -▁art 7.2101262722991779e-05 -▁Herren 7.1968667925003933e-05 -▁interests 7.1942148965406359e-05 -▁‘ 7.1904264737409832e-05 -▁reform 7.1824707858617122e-05 -ss 7.1779246785021284e-05 -▁allerdings 7.1733785711425459e-05 -▁events 7.1669382523831357e-05 -▁web 7.16655941010317e-05 -ose 7.1597402490637958e-05 -▁Please 7.1548152994242465e-05 -ures 7.1510268766245938e-05 --c 7.144586557865185e-05 -isten 7.14420771558522e-05 -▁Demokratie 7.1381462391057748e-05 -▁hoch 7.1279174975467119e-05 -▁cost 7.1192041251075112e-05 -ft 7.1116272795082057e-05 -▁regions 7.11124843722824e-05 -ative 7.10480811846883e-05 -▁involved 7.1044292761888645e-05 -▁let 7.1044292761888645e-05 -▁From 7.1021562225090726e-05 -▁Verantwortung 7.0972312728695246e-05 -▁themselves 7.09116979639008e-05 -ically 7.0851083199106353e-05 -▁Text 7.08397179307074e-05 -ful 7.0820775816709137e-05 -▁network 7.0805622125510529e-05 -▁live 7.0798045279911218e-05 -▁22 7.075258420631538e-05 -▁beispielsweise 7.0680604173121981e-05 -▁needed 7.0661662059123717e-05 -▁parties 7.06389315223258e-05 -▁Haushalts 7.0616200985527879e-05 -Z 7.0604835717128926e-05 -▁direct 7.0582105180331007e-05 -▁hoffe 7.0555586220733433e-05 -▁never 7.0494971455939e-05 -▁extremely 7.0449510382343162e-05 -▁sure 7.03585882351515e-05 -▁competition 7.0316915584355317e-05 -▁voted 7.0275242933559134e-05 -▁please 7.0263877665160181e-05 -ders 7.0210839745965032e-05 -▁Bedingungen 7.0210839745965032e-05 -▁station 7.0180532363567816e-05 -▁gleich 7.0142648135571289e-05 -▁Con 7.0082033370776842e-05 -▁democracy 7.0066879679578234e-05 -▁music 6.9949438572789e-05 -▁night 6.9949438572789e-05 -▁gemeinsamen 6.9926708035991078e-05 -▁notwendig 6.991155434479247e-05 -▁language 6.9896400653593862e-05 -▁efforts 6.9854728002797679e-05 -▁vier 6.9854728002797679e-05 -▁force 6.9835785888799416e-05 -▁freedom 6.9706979513611212e-05 -▁implementation 6.96918258224126e-05 -uss 6.9608480520820239e-05 -▁23 6.96046920980206e-05 -ish 6.9574384715623367e-05 -▁gehört 6.9498616259630312e-05 -▁etc 6.945694360883413e-05 -▁Ziele 6.9449366763234832e-05 -▁ago 6.9426636226436913e-05 -▁wohl 6.9381175152841075e-05 -▁kleinen 6.9373598307241764e-05 -▁Vorschläge 6.9339502502044893e-05 -▁upon 6.9320560388046629e-05 -▁century 6.9290253005649413e-05 -▁60 6.92826761600501e-05 -▁Interesse 6.92675224688515e-05 -▁GmbH 6.9222061395255656e-05 -▁Grundlage 6.9218272972456e-05 -ew 6.92106961268567e-05 -ized 6.9199330858457737e-05 -▁vielleicht 6.9176600321659831e-05 -▁read 6.9153869784861912e-05 -▁changes 6.9112197134065729e-05 -ken 6.9093255020067466e-05 -▁verwendet 6.9070524483269547e-05 -▁plan 6.896066022207962e-05 -ind 6.8930352839682391e-05 -fer 6.8926564416882749e-05 -▁Anwendung 6.8907622302884485e-05 -▁Ihrem 6.8828065424091775e-05 -▁$ 6.8816700155692809e-05 -ats 6.8805334887293856e-05 -▁1. 6.8790181196095248e-05 -schaft 6.8672740089306011e-05 -▁team 6.8642432706908781e-05 -bereich 6.8608336901711909e-05 -▁developed 6.8562875828116071e-05 -▁run 6.8540145291318152e-05 -▁beach 6.853635686851851e-05 -▁Russia 6.84567999897258e-05 -▁comfortable 6.8411338916129962e-05 -▁keep 6.8358300996934827e-05 -▁regional 6.83204167689383e-05 -▁sprechen 6.8316628346138644e-05 -▁sozialen 6.8282532540941772e-05 -▁Daher 6.8237071467345934e-05 -▁lot 6.81840335481508e-05 -▁Inter 6.8130995628955663e-05 -▁= 6.8100688246558434e-05 -▁Verfahren 6.80476503273633e-05 -ionen 6.7945362911772669e-05 -▁regulation 6.7941574488973014e-05 -▁De 6.7892324992577534e-05 -▁erreicht 6.7877171301378926e-05 -ally 6.7850652341781351e-05 -▁Geschichte 6.7820344959384135e-05 -▁Lebens 6.779382599978656e-05 -▁By 6.7767307040188986e-05 -▁bl 6.7755941771790033e-05 -▁situated 6.7676384892997323e-05 -▁Reihe 6.76536543561994e-05 -▁restaurant 6.76536543561994e-05 -▁.. 6.7642289087800451e-05 -▁My 6.7623346973802188e-05 -▁though 6.760819328260358e-05 -▁strong 6.7585462745805661e-05 -▁Pr 6.7562732209007742e-05 -ino 6.7555155363408431e-05 -▁either 6.7505905867012951e-05 -▁Minister 6.7471810061816079e-05 -▁Kunst 6.7456656370617457e-05 -gr 6.744907952501816e-05 -▁Ex 6.7433925833819552e-05 -▁comes 6.7407406874221977e-05 -sk 6.7358157377826484e-05 -▁werde 6.7327849995429267e-05 -▁cases 6.7255869962235868e-05 -▁negotiations 6.7252081539436213e-05 -▁halten 6.724071627103726e-05 -▁sc 6.7217985734239341e-05 -ean 6.7146005701045929e-05 -▁Möglichkeiten 6.7146005701045929e-05 -▁effect 6.7142217278246287e-05 -▁France 6.70853909362515e-05 -▁weltweit 6.70853909362515e-05 -▁markets 6.7009622480258428e-05 -▁Sport 6.68353550314744e-05 -▁AG 6.6831566608674758e-05 -▁bleiben 6.6831566608674758e-05 -▁West 6.6823989763075447e-05 -ix 6.6774740266679967e-05 -▁May 6.6752009729882048e-05 -▁everything 6.6717913924685176e-05 -▁bereit 6.6687606542287947e-05 -▁Information 6.6589107549496973e-05 -▁Bl 6.6573953858298365e-05 -▁foreign 6.6517127516303574e-05 -▁developing 6.6448935905909817e-05 -▁dis 6.6448935905909817e-05 -bl 6.6433782214711209e-05 -isation 6.6429993791911553e-05 -▁land 6.6414840100712945e-05 -▁Debatte 6.6403474832313992e-05 -lichkeit 6.6301187416723363e-05 -per 6.6286033725524755e-05 -ana 6.6259514765927181e-05 -▁final 6.6225418960730309e-05 -▁request 6.6198900001132735e-05 -▁true 6.6126919967939335e-05 -▁Damen 6.6111766276740727e-05 -▁unique 6.6054939934745936e-05 -ling 6.6047363089146626e-05 -▁arbeiten 6.5986748324352179e-05 -▁created 6.5960229364754618e-05 -ON 6.5952652519155308e-05 -politik 6.58882493315612e-05 -▁How 6.5876884063162253e-05 -▁additional 6.5804904029968854e-05 -mer 6.5759442956373016e-05 -▁hard 6.5702616614378225e-05 -▁rest 6.5660943963582043e-05 -▁house 6.5604117621587252e-05 -▁consider 6.55927523531883e-05 -▁verschiedene 6.55435028567928e-05 -▁Paris 6.5516983897195231e-05 -▁fr 6.5516983897195231e-05 -▁later 6.5475311246399062e-05 -▁prices 6.54639459780001e-05 -▁improve 6.5426061750003568e-05 -▁könnten 6.5418484904404271e-05 -▁states 6.5312409066013987e-05 -▁almost 6.5308620643214331e-05 -▁French 6.52707364152178e-05 -▁according 6.5266947992418149e-05 -▁Berichterstatter 6.5259371146818851e-05 -▁capital 6.5259371146818851e-05 -▁En 6.525179430121954e-05 -▁{ 6.5221486918822324e-05 -▁Presidency 6.5217698496022669e-05 -▁meet 6.5187391113625439e-05 -▁Blick 6.51836026908258e-05 -▁living 6.5122987926031351e-05 -▁education 6.51191995032317e-05 -co 6.5104045812033087e-05 -▁post 6.5028277356040033e-05 -▁neu 6.4929778363249059e-05 -▁road 6.4876740444053924e-05 -▁oft 6.4838856216057383e-05 -▁share 6.4797183565261214e-05 -▁verfügt 6.4728991954867457e-05 -▁file 6.4694896149670585e-05 --G 6.4691107726870929e-05 -berg 6.4596397156879611e-05 -▁Hinblick 6.4577455042881348e-05 -▁included 6.45660897744824e-05 -ters 6.4539570814884821e-05 -▁Einsatz 6.45168402780869e-05 -▁gemeinsame 6.4505475009687949e-05 -▁climate 6.448274447289003e-05 -▁democratic 6.4467590781691422e-05 -▁turn 6.4467590781691422e-05 -alen 6.4395610748498009e-05 -▁Minuten 6.4369091788900448e-05 -▁understand 6.4369091788900448e-05 -▁actually 6.4365303366100793e-05 -▁thus 6.4338784406503218e-05 -▁1999 6.4334995983703563e-05 -▁Du 6.4331207560903921e-05 -▁ermöglicht 6.4270592796109475e-05 -att 6.4255439104910866e-05 -▁21 6.4247862259311556e-05 -▁co 6.4191035917316765e-05 -▁Rück 6.4156940112119893e-05 -▁Platz 6.4028133736931689e-05 -▁idea 6.4016768468532737e-05 -▁www. 6.3997826354534473e-05 -▁Doch 6.3994037931734818e-05 -▁wenig 6.39637305493376e-05 -▁lange 6.3922057898541419e-05 -itz 6.3895538938943844e-05 -ij 6.3891750516144189e-05 -▁accept 6.3884173670544891e-05 -ising 6.3869019979346283e-05 -▁Design 6.3853866288147662e-05 -▁Institutionen 6.3815982060151134e-05 -▁stand 6.3815982060151134e-05 -▁soon 6.3789463100553573e-05 -▁program 6.3781886254954263e-05 -▁Auswirkungen 6.377052098655531e-05 -▁tax 6.374021360415808e-05 -▁Wert 6.3687175684962944e-05 -ize 6.3675810416563978e-05 -▁seen 6.3645503034166762e-05 -ES 6.3637926188567451e-05 -▁Druck 6.3637926188567451e-05 -▁receive 6.3573523000973363e-05 -ren 6.3471235585382734e-05 -▁reach 6.345229347138447e-05 -hen 6.3429562934586551e-05 -▁zeigt 6.3338640787394889e-05 -▁Strategie 6.3278026022600442e-05 -▁hatten 6.3247718640203226e-05 -ha 6.323635337180426e-05 -▁Musik 6.323635337180426e-05 -▁Th 6.323635337180426e-05 -▁care 6.3202257566607388e-05 -ization 6.3122700687814678e-05 -▁Israel 6.3118912265015023e-05 -▁führt 6.2978740621427866e-05 -▁type 6.2971163775828569e-05 -▁» 6.2956010084629961e-05 -▁Dienstleistungen 6.2918125856633434e-05 -▁appropriate 6.2846145823440021e-05 -▁erforderlich 6.281962686384246e-05 -▁sub 6.2793107904244886e-05 -fe 6.2785531058645575e-05 -▁Ad 6.2702185757053223e-05 -▁Tatsache 6.2702185757053223e-05 -▁funds 6.2668089951856338e-05 -ka 6.2637782569459121e-05 -we 6.2630205723859811e-05 -▁que 6.2630205723859811e-05 -▁original 6.2626417301060169e-05 -▁held 6.2611263609861547e-05 -anz 6.26074751870619e-05 -▁outside 6.25998983414626e-05 -▁clean 6.2592321495863283e-05 -▁par 6.257337938186502e-05 -▁wirtschaftlichen 6.2516553039870229e-05 -▁Ergebnis 6.2490034080272668e-05 -▁lead 6.239532351028135e-05 -ann 6.23915350874817e-05 -▁gesamten 6.2274093980692457e-05 -▁Ausdruck 6.223620975269593e-05 -▁Verbraucher 6.223620975269593e-05 -▁designed 6.2228632907096619e-05 -▁procedure 6.2183171833500781e-05 -▁moment 6.2164229719502518e-05 -▁thing 6.212634549150599e-05 -▁walk 6.2084672840709821e-05 -▁Schritt 6.2001327539117456e-05 --f 6.198996227071849e-05 -▁Regionen 6.1986173847918848e-05 --W 6.1963443311120929e-05 -▁American 6.1880098009528564e-05 -▁rate 6.1880098009528564e-05 -▁ebenso 6.1846002204331692e-05 -erm 6.1777810593937935e-05 -▁einzelnen 6.1747503211540718e-05 -▁features 6.1736137943141765e-05 -▁Landes 6.170204213794488e-05 -▁knowledge 6.170204213794488e-05 -▁Partner 6.1675523178347319e-05 -▁danken 6.1618696836352528e-05 -▁War 6.1614908413552873e-05 -▁entwickelt 6.1565658917157379e-05 -▁darum 6.1535351534760163e-05 -▁famous 6.1527774689160852e-05 -rat 6.152398626636121e-05 -▁term 6.1482313615565028e-05 -▁week 6.1478525192765372e-05 -▁Kolleginnen 6.1463371501566764e-05 -▁Entscheidung 6.1455794655967453e-05 -▁big 6.14444293875685e-05 -▁links 6.1376237777174743e-05 -ks 6.1364872508775791e-05 -▁workers 6.1353507240376824e-05 -▁} 6.1326988280779263e-05 -▁majority 6.1266373515984817e-05 -▁200 6.12587966703855e-05 -▁Is 6.1224700865188634e-05 -▁Ma 6.1186816637192107e-05 -▁weiß 6.1186816637192107e-05 -▁impact 6.11792397915928e-05 -▁Version 6.1092106067200789e-05 -tern 6.1084529221601478e-05 -▁Sk 6.1080740798801823e-05 -▁early 6.1073163953202526e-05 -▁professional 6.1039068148005654e-05 -▁Seit 6.0986030228810512e-05 -▁looking 6.0986030228810512e-05 -ign 6.09784533832112e-05 -▁Erfolg 6.0967088114812241e-05 -▁customer 6.0963299692012593e-05 -werk 6.0959511269212937e-05 -▁five 6.091026177281745e-05 -▁Außerdem 6.0861012276421964e-05 -▁Kon 6.0807974357226828e-05 -▁communication 6.0781455397629261e-05 -▁Zum 6.0777666974829605e-05 -▁denke 6.0747359592432382e-05 -▁… 6.0747359592432382e-05 -▁throughout 6.0743571169632733e-05 -to 6.0735994324033422e-05 -ists 6.0717052210035159e-05 -▁amount 6.07056869416362e-05 -▁Antwort 6.0641283754042104e-05 -▁space 6.0629918485643145e-05 -▁Mitglieder 6.0550361606850435e-05 -▁Wort 6.0504900533254604e-05 -▁fair 6.0421555231662245e-05 -▁guarantee 6.0410189963263286e-05 -▁August 6.0398824694864326e-05 -▁hours 6.0379882580866063e-05 -▁takes 6.03609404668678e-05 -IS 6.0353363621268495e-05 -eren 6.0323056238871272e-05 -▁South 6.0292748856474049e-05 -▁across 6.0288960433674393e-05 -▁internationale 6.0258653051277177e-05 -▁wide 6.0251076205677866e-05 -▁Änderungsanträge 6.02321340916796e-05 -▁doing 6.020940355488169e-05 -▁Nicht 6.0171519326885163e-05 -▁Abgeordneten 6.0133635098888629e-05 -▁jedem 6.0046501374496615e-05 -▁works 6.0035136106097662e-05 -▁certainly 5.9997251878101135e-05 -▁date 5.997073291850356e-05 -▁Verhandlungen 5.9944213958905993e-05 -▁parts 5.990254130810981e-05 -IN 5.9898752885310161e-05 -ink 5.9868445502912938e-05 -▁Ge 5.9860868657313634e-05 -▁context 5.9845714966115019e-05 -IT 5.9822984429317107e-05 -▁Bereichen 5.9788888624120228e-05 -▁gemeinsam 5.9785100201320573e-05 -▁Industrie 5.9747215973324046e-05 -▁users 5.97434275505244e-05 -▁traditional 5.9697966476928559e-05 -▁konnte 5.9690389631329255e-05 -ages 5.9660082248932032e-05 -▁Fr 5.962598644373516e-05 -▁Änderungsantrag 5.9588102215738633e-05 -icht 5.9584313792938977e-05 -▁befinden 5.9584313792938977e-05 -▁send 5.9565371678940714e-05 -AN 5.9561583256141065e-05 -ran 5.9561583256141065e-05 -▁hätte 5.9523699028144531e-05 -▁gest 5.94782379545487e-05 -▁guest 5.946687268614974e-05 -▁Not 5.9357008424959814e-05 -▁Zentrum 5.933806631096155e-05 -▁perfect 5.9303970505764672e-05 -▁gleichzeitig 5.9285028391766408e-05 -AL 5.9273663123367449e-05 -▁Unser 5.9266086277768145e-05 -▁sl 5.9213048358573e-05 -▁check 5.9209259935773354e-05 -▁investment 5.9197894667374394e-05 -▁setzen 5.9186529398975435e-05 -▁ever 5.9088030406184461e-05 -▁book 5.9034992486989326e-05 -dern 5.9012261950191407e-05 -▁weiteren 5.89971082589928e-05 -▁Vereinigten 5.8985742990593839e-05 -▁Red 5.8906186111801129e-05 -▁although 5.8902397689001481e-05 -▁Kraft 5.889482084340217e-05 -▁act 5.889482084340217e-05 -uf 5.8891032420602521e-05 -▁relations 5.8879667152203562e-05 -▁took 5.8879667152203562e-05 -▁z.B. 5.8837994501407379e-05 -▁concerns 5.8815263964609467e-05 -▁27 5.8811475541809811e-05 -▁pool 5.8807687119010156e-05 -▁Beziehungen 5.8690246012220919e-05 -▁existing 5.86599386298237e-05 -▁ar 5.861826597902752e-05 -▁Turkey 5.8508401717837587e-05 -▁Liste 5.8493248026638978e-05 -▁neben 5.8485671181039674e-05 -form 5.8451575375842796e-05 -▁Raum 5.8417479570645924e-05 -▁Bevölkerung 5.837959534264939e-05 -▁Ent 5.837959534264939e-05 -stand 5.8375806919849741e-05 -▁Gesundheit 5.8375806919849741e-05 -▁Or 5.8356864805851478e-05 -▁Online 5.831898057785495e-05 -▁> 5.8311403732255646e-05 -▁connection 5.826215423586016e-05 -▁remain 5.8247000544661545e-05 -▁Japan 5.8231846853462937e-05 -▁Programme 5.8231846853462937e-05 -▁heißt 5.8110617323874044e-05 -▁ha 5.8084098364276476e-05 -▁Abkommen 5.8076521518677172e-05 -och 5.8050002559079605e-05 -е 5.8046214136279949e-05 -▁programmes 5.8012118331083071e-05 -▁Familien 5.8008329908283422e-05 -ants 5.7993176217084807e-05 -▁English 5.7989387794285158e-05 -▁try 5.7890888801494185e-05 -▁jede 5.7845427727898353e-05 -▁significant 5.7818908768300779e-05 -▁low 5.7796178231502867e-05 -▁je 5.7788601385903556e-05 -ots 5.7773447694704948e-05 -▁mentioned 5.7769659271905292e-05 -▁card 5.77128329299105e-05 -▁America 5.7701467661511542e-05 -▁helfen 5.7674948701913974e-05 -▁apply 5.7614333937119528e-05 -▁Interessen 5.7610545514319879e-05 -gl 5.759918024592092e-05 -▁model 5.7591603400321616e-05 -▁Let 5.7493104407530642e-05 -▁rund 5.7485527561931338e-05 -▁leading 5.7474162293532379e-05 -▁kleine 5.7466585447933074e-05 -▁writing 5.7409759105938284e-05 -▁tra 5.7398393837539324e-05 -▁Aufgabe 5.7383240146340709e-05 -▁Beitrag 5.73605096095428e-05 -▁concerning 5.7349144341143837e-05 -▁Government 5.7330202227145574e-05 -▁Court 5.7315048535946959e-05 -▁completely 5.7273375885150783e-05 -▁Sicherheits 5.7269587462351127e-05 -▁don 5.7269587462351127e-05 -▁friendly 5.7250645348352864e-05 -▁verbunden 5.724306850275356e-05 -▁reasons 5.7197607429157722e-05 -▁Person 5.7159723201161194e-05 -▁nothing 5.7118050550365019e-05 -▁related 5.7091531590767444e-05 -▁half 5.7068801053969532e-05 -▁Handels 5.7053647362770917e-05 -▁aware 5.6985455752377167e-05 -▁Türkei 5.6974090483978214e-05 -▁community 5.6974090483978214e-05 -▁Selbst 5.6970302061178558e-05 --m 5.693620625598168e-05 -▁decisions 5.6913475719183768e-05 -▁includes 5.6902110450784808e-05 -bare 5.68869567595862e-05 -▁Best 5.687559149118724e-05 -über 5.6856649377188977e-05 -▁fünf 5.6852860954389321e-05 -▁Do 5.6841495685990362e-05 -.000 5.6826341994791754e-05 -▁Forschung 5.6818765149192449e-05 -10 5.6814976726392794e-05 -▁Themen 5.6799823035194186e-05 -▁ad 5.679603461239453e-05 -▁gestellt 5.6754361961598348e-05 -▁internet 5.6716477733601821e-05 -▁member 5.6652074546007726e-05 -▁London 5.6636920854809111e-05 -▁wichtigen 5.6636920854809111e-05 -▁enthalten 5.6633132432009462e-05 -▁game 5.6617978740810847e-05 -▁basic 5.6606613472411888e-05 -▁front 5.658009451281432e-05 -▁meinen 5.658009451281432e-05 -▁Kontrolle 5.6545998707617448e-05 -vers 5.6538421862018137e-05 -▁heraus 5.6523268170819529e-05 -▁material 5.6447499714826475e-05 -▁Förderung 5.6405827064030292e-05 -▁unseres 5.638309652723238e-05 -▁committee 5.6314904916838629e-05 -▁population 5.6242924883645224e-05 -▁military 5.6220194346847304e-05 -▁holiday 5.6216405924047656e-05 -ely 5.6170944850451818e-05 -▁Geschäfts 5.6170944850451818e-05 --in 5.6167156427652169e-05 -▁Par 5.6152002736453554e-05 -▁80 5.6102753240058067e-05 -▁allows 5.6061080589261892e-05 -▁media 5.6038350052463972e-05 -▁culture 5.6000465824467445e-05 -▁practice 5.599667740166779e-05 -▁develop 5.5981523710469182e-05 -AR 5.5962581596470918e-05 -enn 5.5939851059673e-05 -▁Ausschuss 5.5909543677275776e-05 -▁extra 5.5864082603679945e-05 -▁test 5.5852717335280985e-05 -▁Since 5.5784525724887235e-05 -▁liegen 5.5754218342490012e-05 --H 5.5742853074091059e-05 -▁gab 5.5742853074091059e-05 -amm 5.57390646512914e-05 -▁mean 5.57314878056921e-05 -▁Verbindung 5.5678449886496957e-05 -AT 5.5651930926899389e-05 -ual 5.5651930926899389e-05 -▁agreements 5.5648142504099734e-05 -▁gestimmt 5.5621623544502166e-05 -▁mind 5.5610258276103207e-05 -▁jedes 5.5606469853303558e-05 -baren 5.5591316162104943e-05 -▁gegeben 5.5564797202507375e-05 -▁travel 5.5534489820110152e-05 -oll 5.5500394014913281e-05 -▁choice 5.5420837136120571e-05 -▁Kr 5.5405683444921956e-05 -▁Reform 5.5371587639725084e-05 -▁bleibt 5.5356433948526469e-05 -▁bekommen 5.528445391533307e-05 -agen 5.5231415996137928e-05 -oss 5.5197320190941056e-05 -alt 5.5182166499742441e-05 -innen 5.5170801231343488e-05 -▁body 5.5144282271745914e-05 -eller 5.5072302238552515e-05 -gel 5.50571485473539e-05 -▁objectives 5.4958649554562933e-05 -eter 5.4954861131763278e-05 -▁received 5.4954861131763278e-05 -ars 5.4905611635367791e-05 -ep 5.4867727407371264e-05 -▁jobs 5.4829843179374736e-05 -ó 5.4826054756575081e-05 -▁values 5.4826054756575081e-05 -▁wegen 5.4822266333775432e-05 -▁ex 5.4761651568980986e-05 -tra 5.4697248381386884e-05 -▁partners 5.468967153578758e-05 -▁Tra 5.4666940998989668e-05 -▁package 5.4651787307791053e-05 -▁simple 5.4640422039392094e-05 -▁Entschließung 5.461011465699487e-05 -AS 5.4606326234195222e-05 -▁Lisbon 5.4579807274597654e-05 -▁established 5.4443424053810153e-05 -▁Strand 5.44396356310105e-05 -▁files 5.4386597711815363e-05 -▁center 5.4379020866216052e-05 -▁richtig 5.4363867175017444e-05 -▁performance 5.4341136638219524e-05 -ationen 5.4329771369820565e-05 -▁euro 5.4284310296224734e-05 -▁civil 5.4261579759426815e-05 -▁Damit 5.4235060799829247e-05 -iven 5.42312723770296e-05 -lin 5.4227483954229943e-05 -ines 5.4204753417432024e-05 -▁equipment 5.418581130343376e-05 -▁applications 5.4140350229837929e-05 -▁computer 5.4132773384238625e-05 -▁Bad 5.4113831270240361e-05 -▁entire 5.4091100733442442e-05 -ond 5.4060793351045219e-05 -▁Verordnung 5.4015332277449388e-05 -▁gerne 5.4003967009050428e-05 -30 5.3992601740651469e-05 -▁Prozess 5.397744804945286e-05 -▁pl 5.39660827810539e-05 -ente 5.3947140667055637e-05 -ong 5.3939563821456333e-05 -▁aim 5.3916833284658414e-05 -▁Nutzung 5.3901679593459806e-05 -urs 5.3878949056661887e-05 -▁regarding 5.3867583788262927e-05 -▁Behörden 5.3852430097064319e-05 -▁red 5.3784238486670569e-05 -▁move 5.3769084795471954e-05 -▁Meer 5.3757719527072994e-05 -che 5.3708470030677514e-05 -ette 5.3704681607877859e-05 -▁concern 5.3700893185078204e-05 -▁Bitte 5.368195107107994e-05 -▁wichtigsten 5.3663008957081676e-05 -▁Here 5.3655432111482372e-05 -▁Mitarbeiter 5.3628913151884805e-05 -▁High 5.36213363062855e-05 -▁Ausschusses 5.3579663655489318e-05 -ese 5.35645099642907e-05 -▁Iran 5.35569331186914e-05 -▁Port 5.3522837313494527e-05 -▁stop 5.3515260467895216e-05 -▁agricultural 5.3496318353896953e-05 -▁sound 5.3492529931097304e-05 -▁quickly 5.3469799394299385e-05 -▁keyword 5.3450857280301121e-05 -▁mehrere 5.3435703589102513e-05 -▁Krise 5.34205498979039e-05 -▁below 5.3416761475104249e-05 -▁objective 5.3375088824308067e-05 -▁Car 5.3359935133109452e-05 -▁Kl 5.33561467103098e-05 -▁Auswahl 5.3322050905112925e-05 -▁answer 5.32841666771164e-05 -▁image 5.3280378254316749e-05 -▁demand 5.3242494026320222e-05 -▁prevent 5.3223551912321958e-05 -▁Str 5.3189456107125079e-05 -▁reports 5.317809083872612e-05 -recht 5.316672557032716e-05 -▁fight 5.316672557032716e-05 -▁Den 5.31477834563289e-05 -▁gr 5.3094745537133761e-05 -▁Wirtschafts 5.3072015000335842e-05 -▁Standard 5.3018977081140707e-05 -▁Post 5.3003823389942092e-05 -fr 5.2958362316346261e-05 -▁hinsichtlich 5.2954573893546605e-05 -▁Gruppe 5.2931843356748693e-05 -▁pass 5.2928054933949037e-05 -los 5.2924266511149382e-05 -▁built 5.2916689665550078e-05 -port 5.2844709632356679e-05 -▁ganze 5.2825767518358415e-05 -▁met 5.2795460135961192e-05 -ute 5.2772729599163273e-05 -▁goods 5.2757575907964665e-05 -▁ganzen 5.2723480102767786e-05 -▁City 5.2689384297570915e-05 -▁face 5.2670442183572651e-05 -▁Ne 5.266286533797334e-05 -▁Em 5.2617404264377509e-05 -▁potential 5.26022505731789e-05 -▁enthält 5.2598462150379245e-05 -▁wobei 5.2583308459180637e-05 -▁500 5.2541635808384454e-05 -▁East 5.2515116848786887e-05 -platz 5.2511328425987231e-05 -▁lack 5.2499963157588272e-05 --d 5.2477232620790359e-05 -oy 5.2473444197990704e-05 -▁besch 5.2431771547194521e-05 -▁benefit 5.2420406278795562e-05 -▁Erweiterung 5.2363579936800771e-05 -istischen 5.2333272554403548e-05 -▁industrial 5.2333272554403548e-05 -ives 5.23294841316039e-05 -uer 5.2310542017605636e-05 -▁return 5.2302965172006332e-05 -▁contains 5.2299176749206676e-05 -▁ermöglichen 5.2291599903607372e-05 -▁Verbesserung 5.2257504098410493e-05 -▁double 5.223856198441223e-05 -▁function 5.2204466179215358e-05 -ech 5.215900510561952e-05 -▁Gäste 5.2121120877622993e-05 -oren 5.2113544032023689e-05 -▁« 5.2105967186424385e-05 -▁search 5.2094601918025425e-05 -▁achieved 5.2075659804027161e-05 -▁derzeit 5.2071871381227506e-05 -▁sense 5.2068082958427857e-05 -▁Anfang 5.2037775576030634e-05 -▁Macht 5.2022621884832026e-05 -▁seems 5.1996102925234452e-05 -▁Kampf 5.1977160811236188e-05 --E 5.1969583965636884e-05 -▁Handel 5.1965795542837228e-05 -ante 5.1943065006039316e-05 -▁party 5.1912757623642093e-05 -uc 5.18559312816473e-05 -▁men 5.18559312816473e-05 -▁Gewalt 5.1840777590448687e-05 -ür 5.1821835476450424e-05 -▁indeed 5.1806681785251815e-05 -▁Barcelona 5.1783951248453896e-05 -▁complex 5.1776374402854592e-05 -▁focus 5.1761220711655977e-05 -▁recently 5.1738490174858065e-05 -▁zeigen 5.173470175205841e-05 -▁Personal 5.17195480608598e-05 -▁Affairs 5.1700605946861538e-05 -▁II 5.1685452255662923e-05 -▁initiative 5.1685452255662923e-05 -▁treatment 5.1681663832863274e-05 -▁hinter 5.1666510141664659e-05 -IC 5.1617260645269173e-05 -▁See 5.1609683799669868e-05 -▁nice 5.1507396384079246e-05 -▁Gelegenheit 5.1499819538479935e-05 -▁Insel 5.1473300578882368e-05 -▁job 5.1461935310483408e-05 -▁Team 5.1424051082486881e-05 -fall 5.1420262659687232e-05 -ole 5.1416474236887577e-05 -▁Finally 5.1405108968488617e-05 -50 5.1382378431690705e-05 -▁gehören 5.1329340512495563e-05 -▁Angebot 5.1325552089695908e-05 --1 5.1302821552897995e-05 -▁highly 5.1280091016100076e-05 -▁Bo 5.1272514170500772e-05 -▁June 5.11740151777098e-05 -▁beginning 5.1166438332110495e-05 -▁successful 5.1086881453317785e-05 -amp 5.1064150916519866e-05 -▁Schaffung 5.1048997225321257e-05 -▁Central 5.1041420379721953e-05 -▁note 5.1033843534122649e-05 -▁floor 5.1007324574525075e-05 -▁völlig 5.0988382460526811e-05 -▁Now 5.0984594037727163e-05 -▁Management 5.094292138693098e-05 -▁equipped 5.0848210816939662e-05 -▁Zahl 5.0840633971340351e-05 -▁Gen 5.0833057125741047e-05 -▁spielen 5.08292687029414e-05 -▁Darüber 5.0817903434542439e-05 -▁dialogue 5.078380762934556e-05 -ano 5.0780019206545911e-05 -▁levels 5.0742134978549384e-05 -▁lang 5.0738346555749729e-05 -▁presented 5.0734558132950073e-05 -▁br 5.0708039173352506e-05 -▁Ergebnisse 5.0704250750552857e-05 -▁gar 5.0704250750552857e-05 -▁gl 5.0647424408558059e-05 -▁everyone 5.0643635985758411e-05 -▁Some 5.0628482294559796e-05 -▁huge 5.0601963334962228e-05 -▁Verwendung 5.0492099073772295e-05 -ele 5.0480733805373342e-05 -▁verbessern 5.0431484308977856e-05 -▁governments 5.0420119040578896e-05 -▁fort 5.0393600080981328e-05 -ums 5.0367081121383754e-05 -▁integration 5.035950427578445e-05 -▁bestehen 5.0332985316186882e-05 -▁Business 5.0317831624988267e-05 -▁increased 5.0272370551392436e-05 -▁recht 5.0257216860193828e-05 -▁lässt 5.0253428437394172e-05 -out 5.0223121054996949e-05 -ord 5.02193326321973e-05 -▁Kontakt 5.0185236827000422e-05 -▁Wein 5.0166294713002158e-05 -▁active 5.0143564176204239e-05 -iens 5.0120833639406327e-05 -que 5.0109468371007367e-05 -▁bus 5.0109468371007367e-05 -▁später 5.0086737834209448e-05 -▁guten 5.0045065183413273e-05 -▁Q 5.0029911492214658e-05 -▁consumers 4.9965508304620563e-05 -▁comp 4.9961719881820907e-05 -,000 4.9920047231024725e-05 -ulation 4.9920047231024725e-05 -▁Zeitpunkt 4.991247038542542e-05 -▁higher 4.991247038542542e-05 -▁carried 4.9867009311829589e-05 -▁Her 4.9840490352232021e-05 -▁verwenden 4.9832913506632711e-05 -▁com 4.98101829698348e-05 -▁Woche 4.9806394547035143e-05 -▁stage 4.9795029278636183e-05 -weise 4.9749568205040352e-05 -▁Russland 4.971547239984348e-05 -▁min 4.9700318708644865e-05 -▁eigene 4.9696530285845217e-05 -▁reviews 4.9685165017446257e-05 -▁General 4.96813765946466e-05 -▁non 4.9677588171846953e-05 -cken 4.9643492366650075e-05 -▁series 4.9613184984252851e-05 -▁Wer 4.9582877601855628e-05 -▁Tage 4.9575300756256324e-05 -▁agreed 4.956772391065702e-05 -▁verfügen 4.9552570219458405e-05 -▁whose 4.95449933738591e-05 --p 4.9522262837061189e-05 -▁großer 4.9518474414261533e-05 -▁Notwendigkeit 4.948816703186431e-05 -▁mein 4.9423763844270215e-05 -▁amendment 4.941997542147056e-05 -▁office 4.9401033307472296e-05 -▁press 4.9397244884672641e-05 -▁cl 4.9382091193474033e-05 -bau 4.932905327427889e-05 -▁Dialog 4.9313899583080282e-05 -▁soziale 4.9310111160280627e-05 -▁direction 4.9306322737480978e-05 -▁principles 4.9306322737480978e-05 -▁choose 4.9302534314681323e-05 -▁leicht 4.9245707972686532e-05 -▁asked 4.9238131127087228e-05 -▁Linux 4.9222977435888613e-05 -▁mal 4.9219189013088964e-05 -ax 4.913205528869695e-05 -err 4.9113113174698687e-05 -uen 4.9113113174698687e-05 -▁First 4.9097959483500072e-05 -▁similar 4.9097959483500072e-05 -▁Lösungen 4.9079017369501808e-05 -▁Transport 4.9044921564304936e-05 -▁PC 4.9010825759108058e-05 -▁— 4.9007037336308409e-05 -ition 4.8919903611916395e-05 -vor 4.8882019383919868e-05 -▁meinem 4.8882019383919868e-05 -▁perhaps 4.8874442538320557e-05 -iere 4.8855500424322293e-05 -▁commitment 4.8851712001522645e-05 -▁Therefore 4.883655831032403e-05 -▁Reise 4.87986740823275e-05 -▁shows 4.8749424585932016e-05 -▁zunächst 4.870775193513584e-05 -ase 4.8681232975538265e-05 -ents 4.866229086154e-05 -▁gesamte 4.8658502438740353e-05 -▁rates 4.8635771901942434e-05 -▁Tat 4.8620618210743826e-05 -▁shown 4.8620618210743826e-05 -ight 4.8597887673945907e-05 -а 4.8578945559947643e-05 -▁dr 4.8567580291548684e-05 -▁fördern 4.8567580291548684e-05 -ments 4.8476658144357014e-05 -▁Änderungen 4.8476658144357014e-05 -▁added 4.8472869721557366e-05 -▁hotels 4.8461504453158406e-05 -▁Nord 4.8442562339160143e-05 -▁Debian 4.8434985493560839e-05 -▁matters 4.8404678111163615e-05 -▁thanks 4.840088968836396e-05 -▁Sozial 4.8385735997165352e-05 -▁angenommen 4.8381947574365696e-05 -▁innovative 4.8336486500769865e-05 -▁nuclear 4.8313755963971946e-05 -▁Juni 4.8302390695572986e-05 -ct 4.8275871735975419e-05 -ious 4.8245564353578196e-05 -sen 4.8234199085179236e-05 -▁Mitglied 4.8234199085179236e-05 -▁gleichen 4.8188738011583405e-05 -ker 4.8158430629186182e-05 -▁tatsächlich 4.8131911669588614e-05 -▁Frankreich 4.8116757978390006e-05 -▁aspects 4.8097815864391742e-05 -ank 4.8075085327593823e-05 -▁Of 4.8075085327593823e-05 -ät 4.8067508481994519e-05 -▁Mai 4.8067508481994519e-05 -essen 4.803341267679764e-05 -▁protect 4.8025835831198336e-05 -▁sorgen 4.8014470562799377e-05 -▁latest 4.8003105294400417e-05 -▁Article 4.7991740026001458e-05 -▁considered 4.7991740026001458e-05 -min 4.796522106640389e-05 -▁Berg 4.7923548415607714e-05 -▁joint 4.7919759992808059e-05 -▁70 4.7915971570008403e-05 -▁EN 4.7915971570008403e-05 -▁Buch 4.7893241033210491e-05 -▁Während 4.7870510496412572e-05 -▁promote 4.7847779959614653e-05 -str 4.78364146912157e-05 -▁vers 4.7825049422816741e-05 -yn 4.7802318886018822e-05 -▁Herzen 4.7775799926421254e-05 -▁came 4.7753069389623335e-05 -▁Initiative 4.774928096682368e-05 -▁à 4.7726550430025767e-05 -▁enable 4.7707608316027504e-05 -▁previous 4.7707608316027504e-05 -▁Qu 4.7703819893227848e-05 -alten 4.7669724088030976e-05 -▁effects 4.7658358819632017e-05 -itt 4.7624263014435138e-05 -▁schriftlich 4.7624263014435138e-05 -▁Ant 4.760910932323653e-05 -way 4.7590167209238267e-05 -▁opportunities 4.7582590363638962e-05 -ek 4.75636482496407e-05 -▁behind 4.7559859826841043e-05 -ven 4.7556071404041388e-05 -▁geführt 4.7521975598844516e-05 -▁300 4.7487879793647638e-05 -▁Regierungen 4.7487879793647638e-05 -▁Erfahrung 4.7480302948048334e-05 -▁popular 4.746136083405007e-05 -ark 4.7423476606053543e-05 -▁stark 4.7419688183253894e-05 -▁possibility 4.7404534492055279e-05 -▁Spain 4.7396957646455975e-05 -ht 4.7362861841259096e-05 -▁Pl 4.7359073418459448e-05 -▁Fach 4.7347708150060488e-05 -ame 4.7340131304461184e-05 -▁commercial 4.7324977613262569e-05 -▁speak 4.7321189190462921e-05 -▁summer 4.7298458653665e-05 -▁entwickeln 4.72908818080657e-05 -▁external 4.7271939694067434e-05 -▁Dabei 4.726436284846813e-05 -▁accommodation 4.726436284846813e-05 -▁easily 4.7241632311670211e-05 -▁ne 4.7234055466070907e-05 -▁Sc 4.7230267043271251e-05 -▁Open 4.7199959660874028e-05 -▁äußerst 4.71620754328775e-05 -assen 4.71544985872782e-05 -▁setting 4.7150710164478541e-05 -.1 4.7143133318879237e-05 -▁atmosphere 4.7139344896079582e-05 -▁materials 4.7120402782081318e-05 -▁bisher 4.7112825936482014e-05 -enen 4.7074941708485487e-05 -raum 4.7059788017286879e-05 -▁oil 4.7044634326088264e-05 -kon 4.7040845903288615e-05 -ock 4.7029480634889656e-05 -▁follow 4.7029480634889656e-05 -и 4.7010538520891392e-05 -▁reached 4.7006750098091737e-05 -▁offen 4.7002961675292088e-05 -▁providing 4.695750060169625e-05 -▁verstehen 4.695750060169625e-05 -▁Einführung 4.6949923756096946e-05 -▁ten 4.6915827950900074e-05 -▁Organisation 4.6908251105300763e-05 -▁exchange 4.6874155300103891e-05 -▁kurz 4.6874155300103891e-05 -istische 4.6859001608905276e-05 -▁video 4.6859001608905276e-05 -lagen 4.6851424763305972e-05 -▁response 4.6840059494907013e-05 -▁York 4.6824905803708405e-05 -▁prepared 4.68173289581091e-05 -▁Trans 4.6787021575711877e-05 -▁Italy 4.6768079461713614e-05 -kommen 4.6764291038913958e-05 -▁reduce 4.674913734771535e-05 -▁Nur 4.6737772079316391e-05 -iver 4.6730195233717086e-05 -▁Flug 4.6730195233717086e-05 -ear 4.6703676274119512e-05 -▁sustainable 4.6684734160121248e-05 -▁Amsterdam 4.6643061509325072e-05 -▁relevant 4.6624119395326809e-05 -zug 4.6593812012929586e-05 -▁creation 4.6586235167330282e-05 -▁head 4.6578658321730971e-05 -▁hohen 4.6548350939333748e-05 -▁neuer 4.6529408825335484e-05 -▁Standpunkt 4.6514255134136876e-05 -▁lediglich 4.6499101442938268e-05 -▁Schw 4.6483947751739653e-05 -▁Position 4.6468794060541045e-05 -▁tool 4.6468794060541045e-05 -▁North 4.6465005637741389e-05 -eth 4.645364036934243e-05 -▁Anzahl 4.6446063523743126e-05 -▁somit 4.6446063523743126e-05 -▁pressure 4.644227510094347e-05 -▁Republic 4.6430909832544517e-05 -▁Umgebung 4.6430909832544517e-05 -▁bevor 4.6400602450147294e-05 -nen 4.6377871913349375e-05 -pt 4.6377871913349375e-05 -▁decided 4.6366506644950416e-05 -.00 4.6298315034556665e-05 -geb 4.6286949766157706e-05 -▁gewährleisten 4.62717960749591e-05 -▁Film 4.6252853960960834e-05 -ja 4.6199816041765692e-05 -▁em 4.6196027618966043e-05 -▁digital 4.6192239196166388e-05 -▁Bau 4.6184662350567084e-05 -▁Schl 4.6158143390969516e-05 -▁Familie 4.6127836008572293e-05 -OR 4.6116470740173333e-05 -▁port 4.6101317048974725e-05 -▁size 4.6078586512176806e-05 -▁submitted 4.60710096665775e-05 -▁coming 4.6044490706979927e-05 -▁capacity 4.6033125438580975e-05 -▁gebracht 4.60066064789834e-05 -▁closed 4.5983875942185488e-05 -▁St. 4.5976299096586177e-05 -▁non- 4.5961145405387569e-05 -act 4.5949780136988609e-05 -▁style 4.5930838022990346e-05 -▁gives 4.5900530640593123e-05 -▁Bus 4.5889165372194163e-05 -▁wirtschaftliche 4.587780010379521e-05 --b 4.58702232581959e-05 -▁Ro 4.5862646412596595e-05 -▁details 4.5862646412596595e-05 -▁Pro 4.5832339030199372e-05 -▁places 4.5824762184600068e-05 -▁Sprache 4.5820973761800413e-05 -▁British 4.580960849340146e-05 -▁Fehler 4.5764147419805622e-05 -igten 4.5756570574206318e-05 -▁26 4.574141688300771e-05 -akt 4.5737628460208054e-05 -▁increasing 4.57338400374084e-05 -▁Convention 4.573005161460875e-05 --t 4.5688378963812568e-05 -▁res 4.5677013695413608e-05 -▁remains 4.5673225272613959e-05 -▁FR 4.5661860004215e-05 -▁WTO 4.5658071581415344e-05 -▁Neben 4.5654283158615696e-05 -▁leben 4.5639129467417081e-05 -▁ausgestattet 4.5635341044617432e-05 -uren 4.5631552621817777e-05 -▁concept 4.5608822085019858e-05 -▁x 4.5586091548221945e-05 -▁sites 4.558230312542229e-05 -asse 4.5567149434223682e-05 -▁actions 4.5544418897425763e-05 -▁rec 4.5544418897425763e-05 -▁obwohl 4.55254767834275e-05 -▁supply 4.5510323092228891e-05 -ities 4.5498957823829931e-05 -ierungs 4.5487592555430972e-05 -ire 4.5472438864232357e-05 -mann 4.5464862018633053e-05 -haus 4.5430766213436181e-05 -▁train 4.5423189367836877e-05 -▁Also 4.5381516717040694e-05 -▁March 4.5351209334643471e-05 -dr 4.5343632489044167e-05 -▁Africa 4.5343632489044167e-05 -▁Cl 4.5336055643444863e-05 -▁December 4.5324690375045904e-05 -▁anderer 4.5324690375045904e-05 -▁property 4.5317113529446593e-05 -▁enlargement 4.5309536683847289e-05 -▁priority 4.5298171415448329e-05 -▁est 4.5264075610251457e-05 -▁Kommissarin 4.5245133496253194e-05 -▁island 4.5214826113855971e-05 -▁bestimmte 4.5207249268256666e-05 -▁cause 4.5184518731458747e-05 -▁document 4.5184518731458747e-05 -▁credit 4.5165576617460484e-05 -▁Integration 4.514663450346222e-05 -ces 4.5142846080662565e-05 -▁inform 4.5142846080662565e-05 -▁quiet 4.5142846080662565e-05 -arm 4.5131480812263612e-05 -▁table 4.5101173429866389e-05 -OS 4.5089808161467429e-05 -▁Erachtens 4.5089808161467429e-05 -mittel 4.5086019738667774e-05 -▁partner 4.5086019738667774e-05 -ues 4.5082231315868125e-05 -ux 4.5082231315868125e-05 -▁Ukraine 4.5082231315868125e-05 -▁balance 4.507844289306847e-05 -▁leave 4.5055712356270551e-05 -▁Bekämpfung 4.5014039705474375e-05 -▁Kosovo 4.5014039705474375e-05 -▁Dinge 4.4991309168676456e-05 -▁Freiheit 4.4983732323077152e-05 -▁meines 4.4972367054678192e-05 -▁billion 4.4945848095080625e-05 -▁Vertrags 4.4942059672280969e-05 -ucht 4.4934482826681665e-05 -▁Produktion 4.49155407126834e-05 -▁zahlreiche 4.49155407126834e-05 -ible 4.4889021753085834e-05 -▁consumer 4.4881444907486523e-05 -▁six 4.4877656484686874e-05 -▁zweite 4.4877656484686874e-05 -▁Museum 4.4854925947888955e-05 --N 4.4805676451493468e-05 -▁35 4.4794311183094509e-05 -▁CD 4.4767792223496941e-05 -lage 4.4764003800697293e-05 -iel 4.4760215377897637e-05 -burg 4.472233114990111e-05 -▁ange 4.4714754304301806e-05 -▁Dr 4.4699600613103191e-05 -▁limited 4.4692023767503887e-05 -stellen 4.4680658499104927e-05 -▁funding 4.4661716385106664e-05 -ma 4.4657927962307015e-05 -▁1998 4.4654139539507359e-05 -▁Landwirtschaft 4.4650351116707704e-05 -ios 4.4646562693908055e-05 -▁Lissabon 4.4646562693908055e-05 -▁former 4.4638985848308745e-05 -con 4.4627620579909792e-05 -▁bestimmten 4.4623832157110136e-05 -▁einschließlich 4.4582159506313954e-05 -ome 4.4555640546716386e-05 -ash 4.4551852123916731e-05 -▁Western 4.4548063701117082e-05 -▁90 4.4510179473120555e-05 -▁2. 4.449502578192194e-05 -▁assistance 4.4487448936322636e-05 -▁age 4.4457141553925413e-05 -inn 4.4423045748728541e-05 -▁sign 4.4407892057529926e-05 -▁sk 4.4400315211930622e-05 -▁Wachstum 4.4392738366331318e-05 -▁Aktivitäten 4.4381373097932358e-05 -▁Fortschritte 4.4373796252333054e-05 -red 4.4332123601536871e-05 -erg 4.4316969910338263e-05 -schen 4.429802779634e-05 -over 4.4294239373540344e-05 -▁supported 4.4290450950740689e-05 -▁movement 4.4263931991143121e-05 -ack 4.4248778299944513e-05 -▁ground 4.4241201454345209e-05 -▁tragen 4.4237413031545553e-05 -igte 4.42336246087459e-05 -▁scheint 4.4195740380749371e-05 -▁independent 4.41692214211518e-05 -ements 4.4135125615954924e-05 -ry 4.4104818233557708e-05 -na 4.4089664542359093e-05 -rad 4.4089664542359093e-05 -▁Club 4.4055568737162221e-05 -▁Zur 4.4055568737162221e-05 -▁forms 4.4036626623163958e-05 -bs 4.4029049777564647e-05 -iden 4.4029049777564647e-05 -▁Co 4.4025261354765e-05 -mal 4.4013896086366039e-05 -▁nie 4.3987377126768471e-05 -ca 4.3972223435569856e-05 -▁Dezember 4.3960858167170896e-05 -▁bet 4.3949492898771944e-05 -wert 4.3934339207573329e-05 -▁Mon 4.393055078477368e-05 -▁charge 4.3904031825176106e-05 -ient 4.38964549795768e-05 -▁außerdem 4.38964549795768e-05 -▁erster 4.3892666556777146e-05 -▁brought 4.3866147597179578e-05 -ves 4.386235917437993e-05 -sche 4.3854782328780619e-05 -▁restaurants 4.3828263369183051e-05 -ieb 4.3801744409585483e-05 -▁sagte 4.3794167563986179e-05 -▁versuchen 4.3794167563986179e-05 -▁regulations 4.3786590718386869e-05 -▁allein 4.3744918067590693e-05 -▁Preise 4.3733552799191733e-05 -▁speed 4.3725975953592429e-05 -EL 4.370324541679451e-05 -▁Hoch 4.3695668571195206e-05 -▁pleased 4.3684303302796246e-05 -▁Funktion 4.3665361188797983e-05 -▁Italian 4.3653995920399023e-05 -▁Gruppen 4.3650207497599368e-05 -CH 4.3642630652000064e-05 -▁mobile 4.3612323269602841e-05 -▁Mehrheit 4.3600958001203888e-05 -▁January 4.3585804310005273e-05 -▁variety 4.355549692760805e-05 -▁creating 4.3536554813609786e-05 -▁Dem 4.3517612699611523e-05 -▁solchen 4.3506247431212563e-05 -▁Medien 4.3502459008412914e-05 -▁Jahrhundert 4.3498670585613259e-05 -▁airport 4.34873053172143e-05 -ows 4.3472151626015691e-05 -▁gew 4.3472151626015691e-05 -▁Konferenz 4.3464574780416387e-05 -▁dadurch 4.3464574780416387e-05 -▁Stelle 4.3460786357616732e-05 -▁Gefahr 4.3456997934817076e-05 -▁rule 4.3456997934817076e-05 -▁sur 4.3456997934817076e-05 -ont 4.3434267398019164e-05 -▁zweiten 4.3419113706820549e-05 -▁insgesamt 4.3403960015621941e-05 -kr 4.3396383170022637e-05 -▁gemäß 4.3392594747222981e-05 -▁Stunden 4.3377441056024373e-05 -▁hohe 4.3377441056024373e-05 -▁Gu 4.3366075787625414e-05 -▁sofort 4.3343345250827495e-05 -▁Bestimmungen 4.3324403136829231e-05 -▁Benutzer 4.3316826291229927e-05 -▁construction 4.3316826291229927e-05 -▁nor 4.3286518908832704e-05 -▁Party 4.3252423103635832e-05 -▁Bel 4.3244846258036521e-05 -▁apartment 4.3233480989637568e-05 -▁28 4.3210750452839649e-05 -unden 4.3176654647642771e-05 -▁safe 4.3172866224843122e-05 -▁contract 4.3169077802043467e-05 -▁link 4.31501356880452e-05 -▁Erklärung 4.3138770419646244e-05 -▁discussion 4.3134981996846595e-05 -▁qu 4.310846303724902e-05 -▁alten 4.3104674614449372e-05 -▁log 4.3097097768850068e-05 -▁purpose 4.3089520923250757e-05 -▁October 4.3085732500451108e-05 -ingen 4.3081944077651453e-05 -programm 4.3070578809252493e-05 -▁message 4.3070578809252493e-05 -▁einzige 4.3025117735656662e-05 -▁largest 4.30137524672577e-05 -▁instead 4.3002387198858743e-05 -▁Star 4.2979656662060831e-05 -▁DE 4.2945560856863952e-05 -és 4.2937984011264648e-05 -enz 4.2915253474466729e-05 -▁aufgenommen 4.2907676628867425e-05 -▁Frühstück 4.2900099783268121e-05 -amb 4.2896311360468465e-05 -führung 4.2892522937668817e-05 -ET 4.28773692464702e-05 -ory 4.2847061864072979e-05 -enten 4.284327344127333e-05 -▁geb 4.2839485018473674e-05 -lo 4.2828119750074715e-05 -▁erneut 4.2816754481675762e-05 -▁ca. 4.2797812367677492e-05 -▁produced 4.2797812367677492e-05 -▁code 4.2782658676478884e-05 -▁Aufmerksamkeit 4.277508183087958e-05 -▁became 4.2767504985280275e-05 -▁PL 4.2759928139680965e-05 -ited 4.275235129408166e-05 -▁vergangenen 4.2748562871282012e-05 -▁B. 4.2737197602883052e-05 -▁conference 4.2718255488884789e-05 -▁avoid 4.26690059924893e-05 -▁stability 4.2574295422497984e-05 -▁Booking.com 4.2570506999698328e-05 -ini 4.2555353308499713e-05 -▁interesting 4.2543988040100761e-05 -▁Basis 4.2521257503302842e-05 -▁school 4.2521257503302842e-05 -▁study 4.2506103812104234e-05 -▁Staats 4.2502315389304578e-05 -unternehmen 4.2464431161308051e-05 -▁Server 4.2449277470109436e-05 -▁Ste 4.2445489047309787e-05 -orn 4.2441700624510132e-05 -▁express 4.2411393242112909e-05 -▁Entscheidungen 4.2362143745717422e-05 -▁ways 4.2354566900118118e-05 -▁click 4.2350778477318469e-05 -▁Ha 4.233941320891951e-05 -▁Flughafen 4.2335624786119854e-05 -▁steps 4.2331836363320206e-05 -▁Steuer 4.2316682672121591e-05 -▁expect 4.2312894249321942e-05 -▁film 4.2309105826522287e-05 -о 4.2301528980922982e-05 --Z 4.2271221598525759e-05 -▁item 4.2267433175726104e-05 -eit 4.2263644752926455e-05 -▁gesch 4.22598563301268e-05 -esten 4.224849106172784e-05 -▁März 4.224849106172784e-05 -▁erfolgreich 4.2233337370529232e-05 -▁sent 4.2221972102130273e-05 -▁Today 4.2214395256530968e-05 -US 4.2203029988132009e-05 -▁effort 4.2203029988132009e-05 -▁genießen 4.2203029988132009e-05 -ote 4.2191664719733049e-05 -▁Reg 4.2191664719733049e-05 -▁started 4.2187876296933394e-05 -arbeit 4.2176511028534441e-05 -▁Entwicklungs 4.216893418293513e-05 -▁rich 4.2153780491736522e-05 -▁ändern 4.21234731093393e-05 -▁Punkte 4.2096954149741731e-05 -▁schwer 4.2081800458543116e-05 -▁Golf 4.2078012035743468e-05 -▁Oktober 4.2078012035743468e-05 -gest 4.2074223612943812e-05 -▁los 4.2059069921745204e-05 -▁allowed 4.2036339384947285e-05 -▁regards 4.1990878311351454e-05 -▁origin 4.1941628814955967e-05 -▁task 4.1934051969356663e-05 -▁Wettbewerb 4.1862071936163257e-05 -▁Urlaub 4.18582835133636e-05 -▁produce 4.1854495090563953e-05 -▁source 4.1854495090563953e-05 -▁fishing 4.1839341399365338e-05 -▁Anforderungen 4.182418770816673e-05 -AM 4.181282243976777e-05 -▁vertreten 4.181282243976777e-05 -▁activity 4.1793880325769507e-05 -▁procedures 4.17787266345709e-05 -schutz 4.1774938211771243e-05 -.2 4.1767361366171939e-05 -▁Küche 4.1763572943372284e-05 -ald 4.1748419252173676e-05 -▁Real 4.17219002925761e-05 -▁Unternehmens 4.1699169755778189e-05 -▁ges 4.1699169755778189e-05 -ff 4.1695381332978533e-05 -▁tell 4.1691592910178885e-05 -ged 4.1687804487379229e-05 -▁Gästebewertungen 4.166507395058131e-05 -▁status 4.1646131836583047e-05 -▁download 4.16423434137834e-05 -▁Services 4.1630978145384438e-05 -▁compromise 4.1627189722584783e-05 -ators 4.1623401299785134e-05 -▁operation 4.1615824454185823e-05 -▁Wochen 4.1600670762987215e-05 -aut 4.1566574957790344e-05 -▁helpful 4.1558998112191033e-05 -isiert 4.1551421266591729e-05 -▁Sicht 4.152869072979381e-05 -▁Berichts 4.15135370385952e-05 -▁Italien 4.1509748615795546e-05 -▁correct 4.15059601929959e-05 -▁Air 4.1498383347396593e-05 -▁review 4.1487018078997634e-05 -▁legislative 4.1483229656197978e-05 -isieren 4.147944123339833e-05 -▁Rights 4.1464287542199715e-05 -▁Mac 4.1449133851001106e-05 -▁Juli 4.1426403314203187e-05 -▁contribution 4.1415038045804228e-05 -▁Villa 4.1411249623004579e-05 -▁Bilder 4.1392307509006316e-05 -▁Öffentlichkeit 4.1380942240607356e-05 -▁geschlossen 4.13771538178077e-05 -▁Beschäftigung 4.1369576972208397e-05 -▁schließlich 4.1362000126609092e-05 -▁word 4.1316539053013254e-05 -▁größten 4.1312750630213606e-05 -af 4.1297596939014991e-05 -pe 4.1297596939014991e-05 -verfahren 4.1297596939014991e-05 -▁Vorschriften 4.1293808516215342e-05 -▁Spanish 4.1263501133818119e-05 -▁park 4.122940532862124e-05 -▁Ben 4.1221828483021936e-05 -▁leider 4.1210463214622977e-05 -▁Staat 4.1191521100624713e-05 -▁erwarten 4.1142271604229226e-05 -▁gen 4.1142271604229226e-05 -ively 4.1134694758629922e-05 -▁damage 4.1130906335830267e-05 -▁village 4.1119541067431314e-05 -▁Bereiche 4.1111964221832e-05 -▁written 4.11043873762327e-05 -▁exp 4.1096810530633395e-05 -▁Luft 4.109302210783374e-05 -isen 4.1077868416635132e-05 -▁Transparenz 4.1077868416635132e-05 -▁Grenzen 4.1066503148236172e-05 -▁eigentlich 4.1062714725436523e-05 -▁distance 4.1058926302636868e-05 -led 4.1043772611438253e-05 -▁class 4.1039984188638604e-05 -▁Fund 4.1028618920239645e-05 -stein 4.1009676806241381e-05 -ime 4.1002099960642077e-05 -ane 4.0990734692243118e-05 -▁provisions 4.0971792578244854e-05 -▁eingesetzt 4.096421573264555e-05 -▁konnten 4.0949062041446935e-05 -▁instruments 4.0945273618647286e-05 -▁During 4.0933908350248327e-05 -par 4.0930119927448671e-05 -▁Development 4.0926331504649023e-05 -son 4.0918754659049712e-05 -ären 4.0892235699452144e-05 -▁cr 4.0839197780257009e-05 -ör 4.0835409357457353e-05 -ari 4.0808890397859786e-05 -▁relation 4.0793736706661171e-05 -▁Höhe 4.0786159861061867e-05 -▁sun 4.0774794592662907e-05 -▁giving 4.0755852478664643e-05 -▁ES 4.0729333519067076e-05 -▁option 4.0729333519067076e-05 -▁Ber 4.072554509626742e-05 -▁leisten 4.072554509626742e-05 -▁plant 4.0714179827868461e-05 -▁got 4.0710391405068812e-05 -▁historical 4.0699026136669853e-05 -▁Site 4.06952377138702e-05 -▁Anfrage 4.0680084022671589e-05 -▁labour 4.0680084022671589e-05 -▁statement 4.0668718754272629e-05 -▁wenige 4.065735348587367e-05 -bo 4.0634622949075758e-05 -▁documents 4.06308345262761e-05 -▁Ansatz 4.0627046103476447e-05 -▁July 4.0627046103476447e-05 -▁Portugal 4.0611892412277839e-05 -▁poor 4.0611892412277839e-05 -▁ourselves 4.0604315566678534e-05 -▁Arbeitnehmer 4.0600527143878879e-05 -▁Folgen 4.0600527143878879e-05 -▁criteria 4.0592950298279575e-05 -▁Min 4.0547489224683744e-05 -▁draw 4.0479297614289993e-05 -▁Präsidenten 4.046035550029173e-05 -▁build 4.0418682849495547e-05 -erb 4.0411106003896243e-05 -▁Rad 4.0373221775899716e-05 -▁base 4.036943335310006e-05 -▁geworden 4.0346702816302141e-05 -barkeit 4.0335337547903189e-05 -ational 4.0323972279504229e-05 -▁Bildung 4.0323972279504229e-05 -uk 4.0312607011105269e-05 -▁genommen 4.0312607011105269e-05 -12 4.030124174270631e-05 -▁conflict 4.030124174270631e-05 -▁efficient 4.0293664897107006e-05 -▁Groß 4.02860880515077e-05 -▁durchgeführt 4.02860880515077e-05 -▁Diskussion 4.0278511205908391e-05 -▁Sa 4.0278511205908391e-05 -▁Such 4.0278511205908391e-05 -▁Computer 4.0244415400711519e-05 -▁Many 4.0240626977911864e-05 -▁Pre 4.0229261709512911e-05 -▁violence 4.0229261709512911e-05 -▁Meine 4.0210319595514641e-05 -▁benefits 4.0210319595514641e-05 -▁types 4.0195165904316032e-05 -▁Sommer 4.0180012213117424e-05 -▁draft 4.0180012213117424e-05 -▁structure 4.0172435367518113e-05 -▁news 4.0157281676319505e-05 -▁benötigen 4.015349325351985e-05 -▁wichtiger 4.015349325351985e-05 -▁Systems 4.0115609025523322e-05 -▁fun 4.0043628992329923e-05 -anten 4.002468687833166e-05 -▁ind 4.002468687833166e-05 -▁Investitionen 4.0017110032732349e-05 -▁require 4.0009533187133045e-05 -▁Januar 3.9990591073134781e-05 -▁suchen 3.995649526793791e-05 -▁wine 3.99489184223386e-05 -▁Nationen 3.994512999953895e-05 -▁ant 3.9937553153939646e-05 -kosten 3.9903457348742767e-05 -▁aktuellen 3.98769383891452e-05 -▁1997 3.9861784697946585e-05 -ex 3.9846631006747977e-05 -▁highest 3.9827688892749713e-05 -ging 3.9816323624350753e-05 -▁Präsidentschaft 3.9808746778751449e-05 -▁immediately 3.9793593087552834e-05 -▁Firma 3.9778439396354226e-05 -▁useful 3.9767074127955267e-05 --2 3.9759497282355963e-05 --l 3.97405551683577e-05 -▁kannst 3.9721613054359435e-05 -▁star 3.971782463155978e-05 -▁Although 3.9714036208760131e-05 -▁gel 3.9710247785960476e-05 -▁lower 3.9698882517561516e-05 -geben 3.9691305671962212e-05 -▁Fre 3.9676151980763604e-05 -▁Your 3.9668575135164293e-05 -▁distribution 3.9619325638768806e-05 -▁gas 3.9615537215969158e-05 -▁nahe 3.9596595101970894e-05 -J 3.9581441410772279e-05 -eil 3.9554922451174711e-05 -▁University 3.9528403491577144e-05 -ierter 3.9524615068777488e-05 -▁europäischer 3.952082664597784e-05 -ult 3.950946137757888e-05 -▁Sinne 3.9490519263580617e-05 -▁kennen 3.9479153995181657e-05 -▁elements 3.9464000303983042e-05 -▁transfer 3.9464000303983042e-05 -▁transparency 3.9464000303983042e-05 -ias 3.9445058189984778e-05 -ai 3.942990449878617e-05 -▁integrated 3.9426116075986515e-05 -▁friends 3.9422327653186866e-05 -▁finally 3.9407173961988251e-05 -▁ihres 3.9407173961988251e-05 -▁method 3.9407173961988251e-05 -▁Gründen 3.94033855391886e-05 -ada 3.9399597116388947e-05 -▁verb 3.9399597116388947e-05 -▁alternative 3.9392020270789643e-05 -etz 3.9354136042793116e-05 -inen 3.935034761999346e-05 -▁gefunden 3.935034761999346e-05 -ites 3.9342770774394156e-05 -industrie 3.9327617083195548e-05 -▁Sal 3.9320040237596237e-05 -▁normal 3.9301098123597974e-05 -▁section 3.9278367586800061e-05 -ology 3.92670023184011e-05 -▁employees 3.92670023184011e-05 -▁weder 3.9263213895601446e-05 -erk 3.9236694936003879e-05 -▁parking 3.9225329667604919e-05 -▁Can 3.9191233862408047e-05 -▁late 3.9187445439608392e-05 -ability 3.9179868594009088e-05 -▁Plan 3.9179868594009088e-05 -▁Aufenthalt 3.9168503325610128e-05 -▁rel 3.9168503325610128e-05 -ath 3.9157138057211169e-05 -▁agenda 3.9157138057211169e-05 -▁angesichts 3.9141984366012561e-05 -▁child 3.9138195943212905e-05 -hof 3.9111676983615338e-05 -▁mountain 3.9111676983615338e-05 -▁Stand 3.9104100138016033e-05 -▁Projekte 3.9100311715216378e-05 -▁Centre 3.9096523292416723e-05 -▁equal 3.9070004332819155e-05 -OM 3.901696641362402e-05 -▁round 3.9013177990824364e-05 -acht 3.9009389568024709e-05 -▁drive 3.9001812722425405e-05 -▁numerous 3.8982870608427141e-05 -▁bezüglich 3.8979082185627492e-05 -▁Auffassung 3.8975293762827837e-05 -▁Furthermore 3.8960140071629222e-05 -iers 3.893740953483131e-05 -▁challenges 3.892604426643235e-05 -▁Sache 3.8922255843632695e-05 -▁applied 3.8914678998033391e-05 -▁halte 3.8914678998033391e-05 -è 3.8884371615636167e-05 -▁reading 3.88578526560386e-05 -▁29 3.8854064233238944e-05 -halt 3.8827545273641377e-05 -ella 3.8823756850841728e-05 -▁daily 3.8812391582442768e-05 -▁Gra 3.8801026314043809e-05 -▁Heute 3.8801026314043809e-05 -hip 3.8789661045644849e-05 -▁Fahr 3.8789661045644849e-05 -ians 3.8785872622845194e-05 -▁tools 3.877829577724589e-05 -itions 3.8763142086047282e-05 -OL 3.8747988394848667e-05 -▁Sol 3.8740411549249363e-05 -amen 3.8736623126449714e-05 -▁reality 3.8713892589651795e-05 -▁bathroom 3.8676008361655268e-05 -▁elections 3.8672219938855612e-05 -▁poverty 3.8660854670456653e-05 -▁EC 3.86494894020577e-05 -▁begrüße 3.86494894020577e-05 -▁payment 3.86494894020577e-05 -va 3.8638124133658741e-05 -▁Fuß 3.8619182019660477e-05 -▁Cr 3.8611605174061166e-05 -ara 3.8607816751261517e-05 -▁processes 3.8596451482862558e-05 -▁Beginn 3.8566144100465335e-05 -▁apartments 3.8566144100465335e-05 -zer 3.8562355677665686e-05 -▁offering 3.8562355677665686e-05 -▁# 3.8558567254866031e-05 -ular 3.8547201986467071e-05 -▁wider 3.8547201986467071e-05 -▁Test 3.8543413563667416e-05 -ön 3.8528259872468808e-05 -möglichkeiten 3.8516894604069848e-05 -▁Free 3.8516894604069848e-05 -hin 3.85131061812702e-05 -▁sieht 3.8509317758470544e-05 -▁eher 3.8505529335670888e-05 -▁death 3.8463856684874713e-05 -▁illegal 3.84562798392754e-05 --An 3.8437337725277138e-05 -▁solidarity 3.8429760879677834e-05 -▁consequences 3.842218403407853e-05 -▁­ 3.841081876567957e-05 -▁led 3.8403241920080266e-05 -▁treffen 3.8403241920080266e-05 -▁bringt 3.8380511383282347e-05 -iker 3.835399242368478e-05 -▁Regeln 3.834262715528582e-05 --In 3.8331261886886861e-05 -▁enter 3.8319896618487908e-05 -▁saying 3.83123197728886e-05 -teil 3.8304742927289293e-05 -▁Forschungs 3.8300954504489644e-05 -▁beste 3.8289589236090685e-05 -▁Linie 3.827443554489207e-05 -▁talk 3.8266858699292766e-05 -▁bes 3.8259281853693462e-05 -▁gra 3.8255493430893806e-05 -▁analysis 3.82479165852945e-05 -▁Ba 3.8236551316895542e-05 -▁diejenigen 3.8232762894095894e-05 -ba 3.819487866609936e-05 -▁Per 3.819487866609936e-05 -▁ziehen 3.8168359706501792e-05 -▁Gesch 3.8156994438102833e-05 -▁Schlusselwortern 3.8156994438102833e-05 -▁Tagesordnung 3.8156994438102833e-05 -▁gelten 3.814562916970388e-05 -▁beyond 3.8141840746904224e-05 -▁green 3.8138052324104569e-05 -▁implement 3.8092591250508738e-05 -bt 3.8088802827709082e-05 -▁entsprechenden 3.8085014404909434e-05 -▁öffentliche 3.8085014404909434e-05 -▁beitragen 3.806607229091117e-05 -▁instrument 3.8058495445311866e-05 -▁paid 3.8039553331313596e-05 -▁End 3.8035764908513947e-05 -ott 3.8009245948916379e-05 -▁official 3.8001669103317068e-05 -istic 3.799788068051742e-05 -▁entsprechend 3.799788068051742e-05 -▁colleagues 3.7982726989318805e-05 -▁happy 3.7967573298120197e-05 -▁offered 3.7967573298120197e-05 -bel 3.7952419606921582e-05 -▁board 3.7948631184121933e-05 -ierungen 3.7925900647324014e-05 -▁goes 3.7914535378925054e-05 -▁Kar 3.790695853332575e-05 -press 3.7899381687726446e-05 -▁practical 3.7876651150928527e-05 -▁Aufgaben 3.7865285882529568e-05 -weg 3.7846343768531304e-05 -esch 3.7834978500132351e-05 -▁Besuch 3.7819824808933736e-05 -ably 3.7816036386134088e-05 -▁Human 3.7804671117735128e-05 -▁exist 3.7793305849336169e-05 -▁thought 3.7793305849336169e-05 -▁Bemühungen 3.777057531253825e-05 -▁persons 3.777057531253825e-05 -▁@ 3.77667868897386e-05 -▁requires 3.7751633198539986e-05 -▁Fest 3.7725114238942418e-05 -zu 3.7717537393343114e-05 -▁Verkehr 3.7717537393343114e-05 -ika 3.7706172124944155e-05 -▁vergessen 3.7698595279344851e-05 -res 3.7668287896947627e-05 -▁Fällen 3.7664499474147972e-05 -▁31 3.7660711051348323e-05 -▁Nr. 3.7634192091750749e-05 -iet 3.76304036689511e-05 -▁ideas 3.76304036689511e-05 -▁Mediterranean 3.7626615246151445e-05 -▁bit 3.7611461554952837e-05 -▁Dr. 3.7596307863754222e-05 -▁Monaten 3.7596307863754222e-05 -▁carry 3.7588731018154918e-05 -▁minimum 3.7554635212958046e-05 -▁warm 3.7543269944559086e-05 -▁Restaurants 3.7535693098959775e-05 -▁van 3.7535693098959775e-05 -▁tour 3.7512962562161863e-05 -▁Auto 3.74940204481636e-05 -▁cut 3.7490232025363944e-05 -▁Änderung 3.748265517976464e-05 -ying 3.7475078334165336e-05 -ungs- 3.7452347797367417e-05 -less 3.7444770951768113e-05 -▁Microsoft 3.7425828837769849e-05 -▁Even 3.7418251992170545e-05 -int 3.7410675146571234e-05 -▁zehn 3.7410675146571234e-05 -▁Wohn 3.7372790918574707e-05 -▁Public 3.7353848804576443e-05 -▁verhindern 3.7353848804576443e-05 -vent 3.7331118267778531e-05 -ider 3.732354142217922e-05 -ün 3.732354142217922e-05 -lauf 3.7297022462581652e-05 -nisse 3.7274291925783733e-05 -cl 3.7262926657384781e-05 -EC 3.7259138234585125e-05 -▁Bürgern 3.7251561388985821e-05 --up 3.7243984543386517e-05 -▁getan 3.72288308521879e-05 -▁forces 3.7217465583788943e-05 -▁Ressourcen 3.7209888738189639e-05 -▁historic 3.7209888738189639e-05 -▁shopping 3.720610031538999e-05 -▁behandelt 3.7198523469790679e-05 -▁erfolgt 3.7190946624191375e-05 -▁advantage 3.7134120282196584e-05 -▁congratulate 3.7103812899799361e-05 -▁refer 3.7096236054200057e-05 -igungen 3.70848707858011e-05 -▁Instrument 3.7081082363001442e-05 -▁modified 3.7077293940201793e-05 -▁Austria 3.7065928671802834e-05 -arten 3.7062140249003178e-05 -▁Bundes 3.7062140249003178e-05 -▁Wunsch 3.7062140249003178e-05 -▁tomorrow 3.7062140249003178e-05 -▁32 3.705835182620353e-05 -▁organisations 3.7043198135004915e-05 -▁Grand 3.7028044443806307e-05 -▁Produktions 3.7028044443806307e-05 -▁Zusammen 3.7028044443806307e-05 -▁hätten 3.7005313907008387e-05 -▁clients 3.6986371793010124e-05 -▁waste 3.6982583370210475e-05 -ius 3.697879494741082e-05 -▁Security 3.6971218101811516e-05 -▁Tagen 3.6971218101811516e-05 -▁doubt 3.6971218101811516e-05 -hn 3.6963641256212212e-05 -▁sales 3.6963641256212212e-05 -▁weeks 3.6963641256212212e-05 -▁genannten 3.6937122296614637e-05 -▁crucial 3.6914391759816725e-05 -▁Des 3.6895449645818461e-05 --St 3.6891661223018806e-05 -▁starting 3.688787280021915e-05 -ail 3.68765075318202e-05 -▁implemented 3.6861353840621583e-05 -▁nimmt 3.6849988572222623e-05 -▁45 3.6834834881024015e-05 -▁Gl 3.6834834881024015e-05 -▁Video 3.6804527498626792e-05 -▁server 3.6785585384628528e-05 -▁morgen 3.6766643270630265e-05 -gesch 3.6736335888233041e-05 -▁Start 3.6728759042633737e-05 -▁z. 3.6709816928635474e-05 -ko 3.6706028505835818e-05 -▁anderem 3.6698451660236514e-05 -plan 3.6694663237436859e-05 -yl 3.6675721123438595e-05 -▁Republik 3.6664355855039636e-05 -▁reference 3.6656779009440332e-05 -ect 3.6652990586640683e-05 -▁published 3.6652990586640683e-05 -ischem 3.6603741090245196e-05 -▁paar 3.6603741090245196e-05 -len 3.6588587399046581e-05 -▁operating 3.6584798976246933e-05 -IL 3.6562068439449014e-05 -weisen 3.6554491593849709e-05 -▁maximum 3.6546914748250405e-05 -▁fre 3.653176105705179e-05 -▁besondere 3.6520395788652831e-05 -▁häufig 3.6516607365853182e-05 -▁Augen 3.6505242097454223e-05 -▁hold 3.6505242097454223e-05 -▁lines 3.6505242097454223e-05 -▁str 3.6501453674654567e-05 -▁comprehensive 3.6482511560656304e-05 -▁sometimes 3.6478723137856655e-05 -▁Enterprise 3.6459781023858391e-05 -▁growing 3.6444627332659776e-05 -▁learn 3.6440838909860121e-05 -▁morning 3.6437050487060472e-05 -▁Inhalt 3.6421896795861857e-05 -▁ready 3.6421896795861857e-05 -▁wer 3.6421896795861857e-05 -▁Natürlich 3.6418108373062209e-05 -▁Atmosphäre 3.639537783626429e-05 -üt 3.6391589413464641e-05 -▁versucht 3.638401256786533e-05 -▁monitoring 3.6380224145065682e-05 -▁Behandlung 3.63461283398688e-05 -▁E-Mail 3.6330974648670195e-05 -▁machine 3.6327186225870539e-05 -▁Amendment 3.6323397803070891e-05 -▁Room 3.6312032534671931e-05 -▁efficiency 3.6312032534671931e-05 -▁Bürgerinnen 3.6300667266272972e-05 -▁lives 3.6289301997874012e-05 -▁erklärt 3.6251417769877485e-05 -etten 3.6247629347077829e-05 -ps 3.6243840924278181e-05 -wer 3.6232475655879221e-05 -▁Werte 3.6228687233079566e-05 -▁Gegen 3.6183226159483735e-05 -▁Green 3.6183226159483735e-05 -▁options 3.6171860891084775e-05 -▁Erfahrungen 3.6156707199886167e-05 -▁sechs 3.6156707199886167e-05 -▁reduction 3.6141553508687552e-05 -▁street 3.6141553508687552e-05 -▁mon 3.61377650858879e-05 -gend 3.61301882402886e-05 -▁Garten 3.61301882402886e-05 -▁Directive 3.6126399817488944e-05 -▁Werk 3.611882297188964e-05 -get 3.6111246126290329e-05 -▁Folge 3.610745770349068e-05 -▁select 3.6103669280691025e-05 -▁join 3.6092304012292065e-05 -▁DVD 3.6080938743893112e-05 -▁caused 3.6077150321093457e-05 -maßnahmen 3.60733618982938e-05 -▁fordern 3.6020323979098666e-05 -▁While 3.6016535556299011e-05 -▁sports 3.6008958710699707e-05 -▁hervor 3.60013818651004e-05 -▁Struktur 3.5990016596701443e-05 -uten 3.5986228173901788e-05 -▁relating 3.5982439751102139e-05 -▁Pol 3.5971074482703179e-05 -▁Tour 3.5971074482703179e-05 -▁überhaupt 3.595970921430422e-05 -▁ber 3.5952132368704916e-05 -▁Me 3.594834394590526e-05 -▁anything 3.5940767100305956e-05 -20 3.59369786775063e-05 -▁Mehr 3.5910459717908733e-05 -▁email 3.5910459717908733e-05 -EM 3.5887729181110821e-05 -▁She 3.5883940758311165e-05 -▁warum 3.5868787067112557e-05 -▁begin 3.5815749147917415e-05 -▁probably 3.5800595456718807e-05 -pf 3.5796807033919151e-05 -▁garden 3.5774076497121232e-05 -▁prior 3.5758922805922624e-05 -▁Lassen 3.5747557537523665e-05 -use 3.5717250155126442e-05 --V 3.5702096463927833e-05 -▁establish 3.5702096463927833e-05 -▁swimming 3.5679365927129914e-05 -▁Center 3.5675577504330259e-05 -▁Neu 3.5675577504330259e-05 -▁Spanien 3.567178908153061e-05 -▁players 3.567178908153061e-05 -▁zahlreichen 3.5652846967532347e-05 --based 3.5630116430734428e-05 -▁languages 3.5626328007934779e-05 -▁1996 3.5622539585135124e-05 -▁accordance 3.5618751162335468e-05 -▁vital 3.5614962739535819e-05 -▁border 3.5611174316736164e-05 -ional 3.5599809048337205e-05 -▁berücksichtigt 3.5588443779938245e-05 -▁Airport 3.5584655357138596e-05 -▁technologies 3.5580866934338941e-05 -issen 3.5546771129142069e-05 -▁worldwide 3.5542982706342414e-05 -▁Denn 3.553540586074311e-05 -▁richtige 3.5497521632746582e-05 -▁gewesen 3.5478579518748319e-05 -▁farmers 3.5417964753953873e-05 -ild 3.5414176331154217e-05 -▁target 3.5395234217155953e-05 -erv 3.5391445794356305e-05 -▁Gebäude 3.5391445794356305e-05 -▁tourist 3.5383868948757e-05 -▁welches 3.5380080525957345e-05 -▁größte 3.5372503680358041e-05 -▁stimmen 3.5372503680358041e-05 -▁terrorism 3.5364926834758737e-05 -▁lo 3.5353561566359778e-05 -▁stress 3.5349773143560122e-05 -▁Google 3.5338407875161163e-05 -▁Praxis 3.5334619452361514e-05 -▁Vergangenheit 3.5330831029561859e-05 -▁erfüllen 3.5311888915563595e-05 -▁Gemeinschafts 3.5304312069964291e-05 -▁Terrorismus 3.5296735224364987e-05 -▁comfort 3.5281581533166372e-05 -▁Conference 3.5266427841967764e-05 -▁mention 3.5258850996368453e-05 -▁Thank 3.5232332036770885e-05 -erst 3.5209601499972973e-05 -▁geschaffen 3.5205813077173317e-05 -▁mass 3.5202024654373662e-05 -▁´ 3.5202024654373662e-05 -▁Einzel 3.51906593859747e-05 -▁interested 3.51830825403754e-05 -endes 3.517929411757575e-05 -▁challenge 3.516792884917679e-05 -ards 3.5141409889579222e-05 -▁extent 3.5114890929981648e-05 -▁jeweiligen 3.5111102507182e-05 -ta 3.5103525661582688e-05 -ata 3.509973723878304e-05 -▁methods 3.5095948815983384e-05 -▁Comp 3.5084583547584425e-05 -▁Aspekte 3.5077006701985121e-05 -sh 3.5069429856385817e-05 -▁ones 3.5065641433586161e-05 -▁Swiss 3.50466993195879e-05 -▁south 3.50466993195879e-05 -▁Viele 3.5042910896788249e-05 -▁elegant 3.5039122473988594e-05 -▁voting 3.502018035999033e-05 -▁UK 3.5012603514391026e-05 -▁secure 3.500881509159137e-05 -▁Gesamt 3.4963354017995539e-05 -▁wählen 3.4963354017995539e-05 -▁stets 3.4948200326796931e-05 -▁zumindest 3.4948200326796931e-05 -▁historischen 3.494062348119762e-05 -▁schützen 3.494062348119762e-05 -▁Zweitens 3.4925469789999012e-05 -oth 3.4906527676000748e-05 -voll 3.4906527676000748e-05 -▁pre 3.4902739253201093e-05 -▁verpflichtet 3.4902739253201093e-05 -halten 3.4891373984802133e-05 -▁ger 3.4887585562002485e-05 -▁Finanzierung 3.4883797139202829e-05 -▁Kern 3.4880008716403181e-05 -▁scientific 3.4880008716403181e-05 -▁neues 3.4876220293603525e-05 -▁° 3.4868643448004221e-05 -▁games 3.486106660240491e-05 -▁adopt 3.4826970797208038e-05 -▁Vergleich 3.4823182374408383e-05 -▁bald 3.4815605528809079e-05 -▁hear 3.481181710600943e-05 -feld 3.4808028683209775e-05 -80 3.4762567609613943e-05 -▁release 3.4762567609613943e-05 -▁Technologie 3.4754990764014633e-05 -▁Chinese 3.4751202341214984e-05 -▁chance 3.474362549561568e-05 -▁außerhalb 3.4739837072816024e-05 -ands 3.4724683381617416e-05 -▁Winter 3.4724683381617416e-05 -▁wanted 3.4713318113218457e-05 -▁Ph 3.4705741267619153e-05 -▁hot 3.47019528448195e-05 -▁Schritte 3.4679222308021578e-05 -▁acc 3.4667857039622619e-05 -▁seines 3.4667857039622619e-05 -▁selection 3.4667857039622619e-05 -▁les 3.466406861682297e-05 -▁modernen 3.466406861682297e-05 -▁sectors 3.4656491771223666e-05 -▁walking 3.4656491771223666e-05 -iner 3.465270334842401e-05 -▁John 3.465270334842401e-05 -ID 3.4633761234425747e-05 -▁Süd 3.4629972811626091e-05 -net 3.4622395966026787e-05 -ico 3.4614819120427483e-05 -▁fine 3.458451173803026e-05 -▁Firmen 3.4580723315230605e-05 -▁maintain 3.4580723315230605e-05 -▁Opfer 3.4569358046831652e-05 -▁images 3.4546627510033733e-05 -ierende 3.4531473818835125e-05 -ierenden 3.4531473818835125e-05 -nahme 3.4512531704836854e-05 -▁ma 3.4508743282037206e-05 -ender 3.4497378013638246e-05 -AC 3.4470859054040678e-05 -ima 3.4444340094443111e-05 -š 3.4444340094443111e-05 -▁businesses 3.4444340094443111e-05 -▁disc 3.4432974826044151e-05 -▁Vertrauen 3.4395090598047624e-05 --w 3.4387513752448313e-05 -▁verantwortlich 3.4387513752448313e-05 -▁despite 3.4383725329648664e-05 -▁sport 3.4360994792850745e-05 -▁vollständig 3.4349629524451786e-05 -▁Verb 3.4334475833253178e-05 -▁Ob 3.4330687410453522e-05 -atz 3.4323110564854218e-05 -▁Mo 3.4323110564854218e-05 -za 3.4319322142054563e-05 -▁Spieler 3.4311745296455259e-05 -▁visitors 3.430795687365561e-05 -▁Berichterstatterin 3.4304168450855955e-05 -sicht 3.4292803182456995e-05 -▁Dann 3.4281437914058036e-05 -▁150 3.4273861068458731e-05 -▁lies 3.4270072645659076e-05 -▁rep 3.4270072645659076e-05 -▁long-term 3.4247342108861164e-05 -▁lost 3.4247342108861164e-05 -▁Rechtsvorschriften 3.4232188417662549e-05 -▁kam 3.4224611572063245e-05 -▁Angelegenheit 3.4198092612465677e-05 -▁genug 3.4186727344066717e-05 -▁bank 3.4182938921267062e-05 -ola 3.4179150498467413e-05 -▁Krieg 3.4179150498467413e-05 -eal 3.414884311607019e-05 -▁Frieden 3.4145054693270535e-05 -▁Binnenmarkt 3.4133689424871582e-05 -▁influence 3.4126112579272271e-05 -▁powers 3.4126112579272271e-05 -nis 3.4110958888073663e-05 -▁Ireland 3.4110958888073663e-05 -▁Mess 3.4107170465274008e-05 -▁Nun 3.4099593619674704e-05 -▁Dazu 3.40920167740754e-05 -▁heutigen 3.4084439928476095e-05 -▁Mor 3.408065150567644e-05 -▁separate 3.4061709391678176e-05 -▁persönlichen 3.4057920968878521e-05 -▁Dokument 3.4046555700479568e-05 -▁Gold 3.4016248318082345e-05 -40 3.4004883049683385e-05 -▁absolutely 3.3993517781284426e-05 -▁raised 3.3993517781284426e-05 -▁imp 3.3989729358484771e-05 -▁trying 3.3959421976087554e-05 -▁unten 3.3951845130488243e-05 -ms 3.393290301648998e-05 -▁Ober 3.393290301648998e-05 -ado 3.3929114593690331e-05 -▁Herausforderungen 3.3925326170890676e-05 -▁Schwierigkeiten 3.3917749325291371e-05 -▁accepted 3.3902595634092757e-05 -▁Allerdings 3.3895018788493452e-05 -▁technischen 3.3895018788493452e-05 -▁bel 3.3891230365693804e-05 -▁Just 3.3876076674495189e-05 -▁trotz 3.387228825169554e-05 -▁einge 3.3868499828895885e-05 -▁folgt 3.3868499828895885e-05 -▁Parteien 3.3860922983296581e-05 -▁erlaubt 3.3838192446498662e-05 -äre 3.3830615600899358e-05 -wirtschaft 3.3811673486901094e-05 -stellung 3.3807885064101438e-05 -▁ausschließlich 3.379273137290283e-05 -je 3.3747270299306992e-05 -▁Sonder 3.3720751339709425e-05 -▁stärker 3.371317449411012e-05 -▁deutschen 3.3701809225711161e-05 -▁Regel 3.36828671117129e-05 -▁bessere 3.36828671117129e-05 -▁erwartet 3.3679078688913242e-05 -▁Falls 3.365634815211533e-05 -atory 3.3652559729315674e-05 -ichte 3.3652559729315674e-05 -▁Rom 3.364498288371637e-05 -▁Vertreter 3.3629829192517762e-05 -▁usw. 3.3629829192517762e-05 -rea 3.36184639241188e-05 -▁e-mail 3.3614675501319147e-05 -▁frei 3.36108870785195e-05 -▁Moreover 3.3595733387320883e-05 -▁™ 3.3595733387320883e-05 -▁More 3.3591944964521228e-05 -▁Einige 3.3588156541721579e-05 -▁Hinsicht 3.3584368118921924e-05 -ift 3.3580579696122275e-05 -▁demands 3.3573002850522964e-05 -▁² 3.3569214427723316e-05 -▁strategic 3.3561637582124012e-05 -omm 3.3557849159324356e-05 -▁erinnern 3.3557849159324356e-05 -yr 3.3550272313725052e-05 -▁leaders 3.3508599662928869e-05 -▁Secondly 3.349723439452991e-05 -▁Prozent 3.348586912613095e-05 -fin 3.34820807033313e-05 -▁fall 3.3478292280531646e-05 -▁gelegen 3.3463138589333038e-05 -▁Angelegenheiten 3.3459350166533383e-05 -▁fields 3.3447984898134423e-05 -.3 3.3425254361336511e-05 -unt 3.3410100670137896e-05 -▁Res 3.3402523824538592e-05 -▁changed 3.3402523824538592e-05 -▁Hel 3.3394946978939288e-05 -RE 3.3376004864941024e-05 -▁Ihres 3.3376004864941024e-05 -▁borders 3.3376004864941024e-05 -▁Rede 3.3353274328143105e-05 -▁Mag 3.33456974825438e-05 -▁Licht 3.3341909059744146e-05 -▁Only 3.3341909059744146e-05 -▁overall 3.3334332214144841e-05 -▁Nacht 3.3322966945745882e-05 -▁Modell 3.3319178522946233e-05 -▁innovation 3.3315390100146578e-05 -▁path 3.3307813254547274e-05 -ru 3.330023640894797e-05 -▁namely 3.3296447986148314e-05 -cy 3.3292659563348659e-05 -▁Annahme 3.328887114054901e-05 -▁Russian 3.3281294294949706e-05 -▁IN 3.3266140603751091e-05 -ED 3.3239621644153523e-05 -▁Last 3.3232044798554219e-05 -ination 3.3224467952954908e-05 -ified 3.3201737416156996e-05 -▁eben 3.3201737416156996e-05 -ziehen 3.3197948993357341e-05 -▁Einrichtung 3.3197948993357341e-05 -▁wrong 3.3190372147758037e-05 -ales 3.3179006879359077e-05 -▁groß 3.3179006879359077e-05 -▁annual 3.3175218456559422e-05 -▁IP 3.3137334228562894e-05 -ST 3.312975738296359e-05 -ogen 3.3118392114564631e-05 -м 3.3107026846165678e-05 -▁represent 3.3107026846165678e-05 -▁übernehmen 3.3088084732167408e-05 -haft 3.3084296309367759e-05 -▁folgenden 3.3084296309367759e-05 -▁Ist 3.3065354195369495e-05 -▁erfahren 3.3065354195369495e-05 -0 3.306156577256984e-05 -AD 3.3046412081371232e-05 -▁sort 3.3046412081371232e-05 -▁falls 3.3016104698974009e-05 -markt 3.3004739430575049e-05 -▁calls 3.3000951007775394e-05 -verkehr 3.2989585739376441e-05 -▁Gas 3.2989585739376441e-05 -▁privaten 3.2989585739376441e-05 -▁Besch 3.2985797316576786e-05 -ato 3.298200889377713e-05 -UR 3.2974432048177826e-05 -▁negative 3.2974432048177826e-05 -▁cover 3.2966855202578522e-05 -▁yourself 3.2963066779778867e-05 -▁representatives 3.2955489934179562e-05 -gew 3.29441246657806e-05 -iegen 3.29365478201813e-05 -▁Med 3.2928970974581995e-05 -▁Mont 3.2894875169385116e-05 -▁allgemeinen 3.2868356209787548e-05 -▁player 3.28645677869879e-05 -▁President-in-Office 3.2856990941388589e-05 -▁Solidarität 3.285320251858894e-05 -▁etc. 3.2826683558991366e-05 -▁bef 3.2815318290592413e-05 -▁gesehen 3.2815318290592413e-05 -▁worked 3.2811529867792758e-05 -zeiten 3.28077414449931e-05 -▁sw 3.2788799330994839e-05 -▁photo 3.2781222485395535e-05 -▁Arbeitsplätze 3.277364563979623e-05 -▁menu 3.2766068794196926e-05 -▁bitten 3.2750915102998311e-05 -▁begrüßen 3.27357614117997e-05 -▁beginnen 3.2724396143400744e-05 -anc 3.2720607720601088e-05 -fahren 3.2720607720601088e-05 -▁initiatives 3.2720607720601088e-05 -▁Verkehrs 3.2709242452202136e-05 -▁automatisch 3.2701665606602825e-05 -▁Its 3.2694088761003521e-05 -ton 3.2690300338203872e-05 -▁evening 3.2682723492604561e-05 -unter 3.26637813786063e-05 -▁Afghanistan 3.2648627687407689e-05 -imm 3.2644839264608034e-05 -gehen 3.2641050841808385e-05 -▁processing 3.2641050841808385e-05 -▁expressed 3.262589715060977e-05 -▁setzt 3.262589715060977e-05 -▁Obwohl 3.2606955036611507e-05 -▁zudem 3.2606955036611507e-05 -▁client 3.2584224499813588e-05 -än 3.2580436077013939e-05 -▁entscheiden 3.2580436077013939e-05 -▁Sea 3.2576647654214283e-05 -▁intended 3.2569070808614979e-05 -▁Volk 3.2565282385815324e-05 -▁fresh 3.2553917117416371e-05 -▁fund 3.2553917117416371e-05 -iß 3.2550128694616716e-05 -▁His 3.2550128694616716e-05 -▁beide 3.2531186580618452e-05 -▁NATO 3.2508456043820533e-05 -▁banks 3.2504667621020884e-05 -▁mainly 3.2493302352621925e-05 -▁relationship 3.2493302352621925e-05 -▁flexible 3.2481937084222965e-05 -ware 3.247814866142331e-05 -▁Milliarden 3.247814866142331e-05 -▁bad 3.247814866142331e-05 -▁heard 3.247814866142331e-05 -▁limit 3.247814866142331e-05 -▁darstellt 3.24554181246254e-05 -▁th 3.2432687587827479e-05 -ises 3.2425110742228175e-05 -▁accession 3.241753389662887e-05 -▁endlich 3.2406168628229911e-05 -▁plans 3.2406168628229911e-05 -ence 3.2398591782630607e-05 -▁deine 3.2391014937031296e-05 -▁ability 3.2383438091431992e-05 -▁traffic 3.2383438091431992e-05 -▁talking 3.2379649668632343e-05 -▁committed 3.2375861245832688e-05 -▁ban 3.2372072823033032e-05 -▁cities 3.2368284400233384e-05 -▁Iraq 3.2364495977433728e-05 -cr 3.2341765440635809e-05 -▁month 3.2337977017836161e-05 -▁Armut 3.2330400172236857e-05 -▁vorgeschlagen 3.23266117494372e-05 -▁Wann 3.2322823326637546e-05 -▁letzte 3.2315246481038242e-05 -reich 3.2300092789839633e-05 -▁verl 3.2288727521440674e-05 -▁proper 3.228115067584137e-05 -schaften 3.2265996984642755e-05 -▁goal 3.2265996984642755e-05 -igungs 3.2262208561843106e-05 -▁Gast 3.2220535911046924e-05 -▁eindeutig 3.21902285286497e-05 -▁IS 3.2182651683050396e-05 -aden 3.2167497991851781e-05 -▁running 3.2159921146252477e-05 -▁Hause 3.2152344300653173e-05 -ification 3.2129613763855254e-05 -AP 3.2125825341055605e-05 -▁character 3.2125825341055605e-05 -▁People 3.21182484954563e-05 -▁placed 3.2114460072656646e-05 -▁Erstens 3.2110671649856991e-05 -▁element 3.2106883227057342e-05 -ystem 3.2095517958658382e-05 -▁love 3.2091729535858727e-05 -▁terrace 3.2087941113059078e-05 -▁alone 3.2068998999060815e-05 -▁brand 3.2068998999060815e-05 -▁appear 3.2046268462262896e-05 -▁Einfluss 3.2019749502665328e-05 -▁suggest 3.2015961079865672e-05 -fragen 3.2012172657066017e-05 -▁dringend 3.1985653697468449e-05 -▁versch 3.1985653697468449e-05 -▁Sil 3.1970500006269841e-05 -▁entgegen 3.1947769469471922e-05 -▁discuss 3.1943981046672273e-05 -▁Leute 3.1936404201072963e-05 -▁Pass 3.1921250509874354e-05 -.0 3.191367366427505e-05 -▁Christian 3.1909885241475395e-05 -▁Verfassung 3.1887154704677476e-05 -▁Roma 3.1883366281877827e-05 -▁families 3.1883366281877827e-05 -▁trust 3.1872001013478868e-05 -pro 3.1860635745079908e-05 -achen 3.1856847322280259e-05 -▁improved 3.1856847322280259e-05 -▁planning 3.1837905208281989e-05 -▁folgen 3.1818963094283725e-05 -ule 3.1792444134686158e-05 -▁kaum 3.1735617792691367e-05 -▁Pal 3.1724252524292407e-05 -▁benutzt 3.1720464101492759e-05 -▁opening 3.1720464101492759e-05 -▁Falle 3.1705310410294144e-05 -iten 3.1701521987494495e-05 -▁foot 3.168636829629588e-05 -▁students 3.1682579873496232e-05 -ones 3.1678791450696576e-05 -▁black 3.1675003027896921e-05 -hal 3.1659849336698313e-05 -ira 3.1656060913898657e-05 -▁comments 3.1656060913898657e-05 -▁außer 3.1652272491099008e-05 -▁Anstrengungen 3.1648484068299353e-05 -▁Ja 3.1648484068299353e-05 -▁Ausschuß 3.1637118799900393e-05 -▁buy 3.1633330377100745e-05 -▁cohesion 3.1633330377100745e-05 -▁God 3.1629541954301089e-05 -▁approved 3.1614388263102481e-05 -ona 3.160681141750317e-05 -▁author 3.1603022994703522e-05 -▁Val 3.1599234571903866e-05 -▁structural 3.1591657726304562e-05 -▁genannt 3.1584080880705258e-05 -▁größere 3.15802924579056e-05 -▁reforms 3.154998507550838e-05 -atic 3.1542408229909075e-05 -go 3.1542408229909075e-05 -▁Waren 3.1534831384309771e-05 -ds 3.1527254538710467e-05 -▁currency 3.1527254538710467e-05 -▁automatically 3.1515889270311508e-05 -▁affected 3.1504524001912548e-05 -▁suitable 3.1493158733513589e-05 -gebiet 3.147800504231498e-05 -▁Costa 3.147800504231498e-05 -▁administrative 3.147800504231498e-05 -▁Tätigkeit 3.1474216619515325e-05 -▁ausge 3.1474216619515325e-05 -▁increasingly 3.147042819671567e-05 -▁med 3.147042819671567e-05 -ake 3.1466639773916021e-05 -▁justice 3.1466639773916021e-05 -▁feature 3.1462851351116366e-05 -▁Verpflichtungen 3.1459062928316717e-05 -▁Sitzung 3.1451486082717406e-05 -▁genutzt 3.1451486082717406e-05 -▁decide 3.1440120814318453e-05 -▁zusätzliche 3.1421178700320183e-05 -▁Zustimmung 3.1406025009121575e-05 -▁unbedingt 3.1406025009121575e-05 -ama 3.1402236586321919e-05 -▁Netz 3.1394659740722615e-05 -▁mod 3.1387082895123311e-05 -angen 3.1383294472323656e-05 -itäts 3.1383294472323656e-05 -▁bo 3.1383294472323656e-05 -▁Kauf 3.1375717626724352e-05 -▁oben 3.1368140781125047e-05 -itäten 3.1352987089926439e-05 -▁moderne 3.1349198667126784e-05 -▁export 3.1337833398727824e-05 -zentrum 3.1326468130328865e-05 -▁Qualitäts 3.1326468130328865e-05 -▁bilden 3.1303737593530953e-05 -▁conclusion 3.1303737593530953e-05 -▁emissions 3.1292372325131993e-05 -▁Organisationen 3.1277218633933378e-05 -▁transparent 3.1273430211133729e-05 -bild 3.1269641788334074e-05 -▁entspricht 3.1254488097135466e-05 -▁regime 3.1246911251536155e-05 -▁eingehen 3.1239334405936851e-05 -▁circumstances 3.1224180714738243e-05 -▁usually 3.1216603869138939e-05 -▁reduced 3.1212815446339283e-05 -▁collection 3.1209027023539628e-05 -▁host 3.1186296486741715e-05 -▁Bed 3.11711427955431e-05 -с 3.1159777527144141e-05 -▁delivery 3.1159777527144141e-05 -▁trans 3.1159777527144141e-05 -▁phone 3.1137046990346229e-05 -▁überzeugt 3.1137046990346229e-05 -bed 3.111431645354831e-05 -▁kon 3.10991627623497e-05 -for 3.1076432225551782e-05 -ley 3.1072643802752127e-05 -old 3.1061278534353174e-05 -punkt 3.1061278534353174e-05 -▁Zug 3.1053701688753863e-05 -▁Idee 3.1027182729156296e-05 -▁Verwaltung 3.1027182729156296e-05 -▁began 3.1023394306356647e-05 -▁supports 3.1019605883556992e-05 -▁confidence 3.1015817460757336e-05 -▁vorgelegt 3.1012029037957688e-05 -IA 3.1004452192358377e-05 -▁Tor 3.0996875346759073e-05 -▁Nations 3.0989298501159768e-05 -▁fish 3.0985510078360113e-05 -▁gesetzt 3.0985510078360113e-05 -▁laws 3.0981721655560464e-05 -▁Kompromiss 3.0966567964361849e-05 -▁Tier 3.0966567964361849e-05 -▁administration 3.0966567964361849e-05 -▁extensive 3.0943837427563937e-05 -ree 3.0932472159164978e-05 -▁erkennen 3.0924895313565674e-05 -▁Öl 3.0924895313565674e-05 --h 3.0921106890766018e-05 -▁selbstverständlich 3.0917318467966363e-05 -▁whom 3.0917318467966363e-05 -▁priorities 3.0902164776767755e-05 -▁Holz 3.089458793116845e-05 -▁contribute 3.0890799508368795e-05 -ache 3.0875645817170187e-05 -▁Anerkennung 3.0868068971570883e-05 -hol 3.0864280548771227e-05 -omp 3.0856703703171923e-05 -ides 3.0849126857572619e-05 -▁Social 3.0849126857572619e-05 -▁plus 3.0849126857572619e-05 -▁Abschluss 3.0841550011973308e-05 -heiten 3.083776158917366e-05 -▁aktiv 3.0818819475175396e-05 -▁powerful 3.0811242629576085e-05 -▁unemployment 3.0807454206776436e-05 -isk 3.0803665783976781e-05 -▁mag 3.0792300515577822e-05 -▁Wahlen 3.0784723669978517e-05 -system 3.0780935247178869e-05 -ekt 3.0765781555980254e-05 -▁Boden 3.074683944198199e-05 -uß 3.0739262596382686e-05 -▁says 3.0739262596382686e-05 -▁Cor 3.0724108905184071e-05 -▁mindestens 3.0724108905184071e-05 --O 3.0705166791185808e-05 -▁cross 3.0701378368386159e-05 -▁infrastructure 3.067864783158824e-05 -▁north 3.0640763603591713e-05 -air 3.0629398335192753e-05 -▁wonderful 3.0625609912393104e-05 -schl 3.0614244643994145e-05 -▁rise 3.059909095279553e-05 -▁Spa 3.0591514107196226e-05 -▁remember 3.0591514107196226e-05 -▁unterschiedlichen 3.0591514107196226e-05 -▁white 3.058772568439657e-05 -ays 3.0583937261596922e-05 -prozess 3.0580148838797266e-05 -▁ensuring 3.0557418301999354e-05 -▁wesentlich 3.0557418301999354e-05 -▁Don 3.05536298791997e-05 -▁Vers 3.0549841456400043e-05 -▁else 3.0549841456400043e-05 -▁Indeed 3.053847618800109e-05 -▁berücksichtigen 3.053847618800109e-05 -▁rein 3.0534687765201435e-05 -▁comment 3.053089934240178e-05 -▁electronic 3.0515745651203171e-05 -▁Com 3.0511957228403519e-05 -▁Agenda 3.0508168805603864e-05 -▁Mann 3.0504380382804212e-05 -▁assist 3.0496803537204908e-05 -▁encourage 3.04892266916056e-05 -une 3.0485438268805948e-05 -▁scope 3.0485438268805948e-05 -▁verfügbar 3.0481649846006296e-05 -▁inv 3.0477861423206644e-05 -kraft 3.0455130886408725e-05 -fern 3.0451342463609073e-05 -▁Czech 3.0447554040809421e-05 -▁Körper 3.043240034961081e-05 -▁Kriterien 3.0413458235612546e-05 -▁getroffen 3.0402092967213586e-05 -▁seem 3.039072769881463e-05 -▁victims 3.0386939276014975e-05 -daten 3.0383150853215323e-05 -▁Vereinbarung 3.0383150853215323e-05 -▁Most 3.0367997162016711e-05 -▁gezeigt 3.0367997162016711e-05 -izing 3.0337689779619491e-05 -▁Osten 3.0337689779619491e-05 -▁kl 3.0333901356819836e-05 -▁danke 3.0326324511220532e-05 -▁Gar 3.032253608842088e-05 -▁broad 3.0299805551622961e-05 -▁Each 3.0288440283224005e-05 -▁mehreren 3.0269498169225741e-05 -icher 3.0258132900826782e-05 -▁bewusst 3.0258132900826782e-05 -▁ged 3.025434447802713e-05 -▁participation 3.0224037095629906e-05 -▁played 3.0212671827230947e-05 -▁fellow 3.0205094981631643e-05 -▁crime 3.0197518136032339e-05 -▁specifically 3.0193729713232683e-05 -ym 3.0178576022034072e-05 -▁manner 3.0178576022034072e-05 -▁Frankfurt 3.0159633908035808e-05 -▁agriculture 3.0152057062436504e-05 -▁larger 3.0148268639636852e-05 -▁1995 3.0140691794037545e-05 -Q 3.0121749680039281e-05 -▁Hintergrund 3.0095230720441713e-05 -▁tabled 3.0091442297642058e-05 -ordnung 3.0083865452042754e-05 -▁introduced 3.0083865452042754e-05 -▁Durchführung 3.0072500183643794e-05 -▁Lebensmittel 3.0072500183643794e-05 -▁entsprechende 3.0072500183643794e-05 -▁Anspruch 3.0068711760844142e-05 -▁getting 3.0061134915244838e-05 -▁Inn 3.0053558069645531e-05 -à 3.0049769646845879e-05 -▁aktuelle 3.0042192801246574e-05 -▁ass 3.0042192801246574e-05 -ined 3.0038404378446919e-05 -▁sources 3.0038404378446919e-05 -▁extrem 3.0034615955647267e-05 -▁reg 3.0030827532847615e-05 -vel 3.0027039110047963e-05 -erei 3.0011885418849351e-05 -▁demokratischen 3.0011885418849351e-05 -▁detailed 3.0004308573250044e-05 -▁Vorteile 3.0000520150450392e-05 -▁nachdem 3.0000520150450392e-05 -▁Besucher 2.999673172765074e-05 -▁Download 2.9989154882051436e-05 -▁effectively 2.9977789613652476e-05 -▁hinweisen 2.9966424345253517e-05 -▁Stabilität 2.995505907685456e-05 -000 2.9951270654054905e-05 -ga 2.9947482231255253e-05 -▁animals 2.9947482231255253e-05 -▁bi 2.9947482231255253e-05 -▁import 2.99436938084556e-05 -▁competitive 2.9928540117256989e-05 -▁functions 2.9928540117256989e-05 -▁nationale 2.9924751694457337e-05 -▁mar 2.9920963271657685e-05 -own 2.991717484885803e-05 -iste 2.9913386426058378e-05 -▁served 2.9894444312060114e-05 -ips 2.9875502198061851e-05 -▁record 2.9867925352462546e-05 -▁mm 2.9848983238464279e-05 -▁Mitteilung 2.9845194815664627e-05 -▁glauben 2.9845194815664627e-05 -▁India 2.9833829547265671e-05 -▁pleasant 2.9830041124466016e-05 -▁geeignet 2.9826252701666364e-05 -▁installation 2.9822464278866712e-05 -▁globalen 2.9811099010467752e-05 -q 2.98073105876681e-05 -▁purposes 2.9799733742068796e-05 -▁ship 2.9799733742068796e-05 -▁referred 2.9769426359671573e-05 -▁Sprachen 2.9758061091272613e-05 -▁Part 2.9754272668472961e-05 -cker 2.9750484245673309e-05 -▁rural 2.9750484245673309e-05 -▁aimed 2.973911897727435e-05 -▁erwähnt 2.9735330554474698e-05 -▁gesprochen 2.9735330554474698e-05 -▁rechts 2.9712600017676782e-05 -▁Inhalte 2.9701234749277823e-05 -▁400 2.9693657903678519e-05 -▁Asia 2.9693657903678519e-05 -▁critical 2.9693657903678519e-05 -▁sitting 2.9678504212479907e-05 -▁exactly 2.9663350521281295e-05 -zt 2.9655773675681988e-05 -uck 2.9640619984483376e-05 -▁reception 2.9640619984483376e-05 -ife 2.9636831561683724e-05 -▁serve 2.962925471608442e-05 -▁partnership 2.9625466293284768e-05 -▁eingerichtet 2.9621677870485113e-05 -▁protected 2.9621677870485113e-05 -▁motion 2.9595158910887545e-05 -▁regular 2.9595158910887545e-05 -▁Eur 2.9587582065288238e-05 -▁kitchen 2.9583793642488586e-05 -▁Homepage 2.9580005219688934e-05 -ologie 2.9576216796889281e-05 -▁http 2.9568639951289974e-05 -▁benutzen 2.9557274682891018e-05 -▁valid 2.9557274682891018e-05 -IM 2.9553486260091362e-05 -▁Abend 2.9545909414492058e-05 -▁besonderen 2.9545909414492058e-05 -▁euch 2.95345441460931e-05 -cc 2.9530755723293447e-05 -ances 2.9519390454894487e-05 -▁wahrscheinlich 2.9515602032094835e-05 -▁Österreich 2.9511813609295183e-05 -▁Telefon 2.9504236763695879e-05 -▁Ger 2.9500448340896224e-05 -▁Zeichen 2.9496659918096572e-05 -▁camera 2.9496659918096572e-05 -▁Ag 2.949287149529692e-05 -▁Komfort 2.9485294649697615e-05 -ique 2.9473929381298656e-05 -▁Reformen 2.9466352535699348e-05 -board 2.9458775690100044e-05 -▁risks 2.9447410421701085e-05 -▁Dienst 2.9443621998901433e-05 -▁Ausgaben 2.9439833576101781e-05 -▁becoming 2.9432256730502473e-05 -▁Tradition 2.9428468307702821e-05 -▁akzeptieren 2.9424679884903169e-05 -▁Ost 2.9409526193704558e-05 -▁Ind 2.9398160925305598e-05 -▁2020 2.9394372502505946e-05 -▁institutional 2.9386795656906642e-05 -▁sicherzustellen 2.938300723410699e-05 -▁Irak 2.9379218811307334e-05 -rechte 2.9375430388507682e-05 -▁Why 2.9375430388507682e-05 -▁display 2.9375430388507682e-05 -ichten 2.9367853542908378e-05 -▁Power 2.9367853542908378e-05 -uz 2.9360276697309071e-05 -▁Karte 2.9360276697309071e-05 -▁Informations 2.9341334583310807e-05 -ike 2.9333757737711503e-05 -▁discussed 2.9322392469312544e-05 -▁wants 2.9322392469312544e-05 -▁Partei 2.931481562371324e-05 -▁represents 2.9311027200913584e-05 -bes 2.930345035531428e-05 -▁spacious 2.9295873509714976e-05 -▁precisely 2.9276931395716712e-05 -ished 2.9273142972917057e-05 -▁February 2.9273142972917057e-05 -▁police 2.9273142972917057e-05 -▁Waffen 2.92617777045181e-05 --Ver 2.9257989281718445e-05 -RA 2.9257989281718445e-05 -▁Schließlich 2.9250412436119141e-05 -▁siehe 2.9246624013319489e-05 -olle 2.9231470322120878e-05 -▁Kn 2.9231470322120878e-05 -▁developments 2.9231470322120878e-05 -▁Sitz 2.9208739785322962e-05 -▁Then 2.9208739785322962e-05 -▁erzielt 2.9208739785322962e-05 -utz 2.919358609412435e-05 -▁Middle 2.919358609412435e-05 -▁stated 2.919358609412435e-05 -▁Zunächst 2.9189797671324695e-05 -▁discussions 2.9186009248525043e-05 -▁Code 2.9182220825725391e-05 -▁unver 2.9182220825725391e-05 -▁Agreement 2.9167067134526779e-05 -▁Datei 2.9159490288927475e-05 -▁Zudem 2.9155701866127823e-05 -▁Haushalt 2.9151913443328168e-05 -▁bestehenden 2.9148125020528516e-05 -▁likely 2.9148125020528516e-05 -▁Board 2.9144336597728864e-05 -IP 2.9140548174929212e-05 -▁spend 2.9110240792531989e-05 -stadt 2.9106452369732336e-05 -verb 2.9102663946932681e-05 -▁Cal 2.9087510255734073e-05 -▁freuen 2.9087510255734073e-05 -▁managed 2.9087510255734073e-05 -TA 2.9076144987335113e-05 -.5 2.9049626027737542e-05 -▁Friedens 2.9049626027737542e-05 -▁Protokoll 2.904583760493789e-05 -▁Rec 2.904583760493789e-05 -▁programs 2.904583760493789e-05 -▁möglicherweise 2.9030683913739279e-05 -ork 2.9023107068139975e-05 -reg 2.9023107068139975e-05 -▁otherwise 2.9015530222540667e-05 -▁Sektor 2.9007953376941363e-05 -gruppe 2.9004164954141711e-05 -▁Verwaltungs 2.9000376531342059e-05 -▁3. 2.8996588108542403e-05 -▁budgetary 2.8996588108542403e-05 -▁Stil 2.8981434417343792e-05 -ING 2.8970069148944836e-05 -▁mark 2.8966280726145184e-05 -▁thousands 2.8966280726145184e-05 -stoff 2.8962492303345528e-05 -▁metres 2.8958703880545876e-05 -▁Schlüssel 2.8951127034946572e-05 -ring 2.8928396498148653e-05 -▁Wissen 2.8924608075349e-05 -▁inside 2.8920819652549349e-05 -eau 2.8909454384150389e-05 -▁won 2.8898089115751433e-05 -▁selected 2.8890512270152126e-05 -ours 2.8882935424552822e-05 -rit 2.8882935424552822e-05 -▁loss 2.887914700175317e-05 -val 2.8875358578953514e-05 -▁eng 2.886778173335421e-05 -▁Switzerland 2.88526280421556e-05 -▁geändert 2.88526280421556e-05 -▁Chance 2.8848839619355947e-05 -▁Nutzen 2.8841262773756639e-05 -hand 2.8833685928157335e-05 -▁shower 2.8833685928157335e-05 -este 2.8829897505357683e-05 -▁Policy 2.8814743814159071e-05 -▁festgelegt 2.8814743814159071e-05 -▁hands 2.8803378545760112e-05 -▁Mark 2.8795801700160808e-05 -▁Wettbewerbsfähigkeit 2.8795801700160808e-05 -▁bestimmt 2.8792013277361156e-05 -▁expected 2.8792013277361156e-05 -ron 2.87882248545615e-05 -▁covered 2.87882248545615e-05 -▁measure 2.87882248545615e-05 -▁tradition 2.8780648008962196e-05 -▁Unterkategorien 2.8776859586162544e-05 -▁winter 2.8776859586162544e-05 -▁season 2.8773071163362892e-05 -▁Research 2.8769282740563237e-05 -yst 2.8765494317763585e-05 -genden 2.8750340626564973e-05 -▁Rest 2.8750340626564973e-05 -arbeiten 2.8738975358166017e-05 -zimmer 2.8735186935366362e-05 -▁hoffen 2.873139851256671e-05 -▁entry 2.8727610089767057e-05 -▁Haltung 2.8716244821368098e-05 -▁color 2.8712456398568446e-05 -▁prüfen 2.8712456398568446e-05 -▁actual 2.8708667975768794e-05 -▁anders 2.8708667975768794e-05 -▁umgesetzt 2.8708667975768794e-05 -▁zuletzt 2.8704879552969142e-05 -werte 2.8701091130169486e-05 -▁medical 2.8701091130169486e-05 -▁sub-categories 2.8701091130169486e-05 -▁Fort 2.8697302707369834e-05 -▁assessment 2.8682149016171223e-05 -▁attack 2.8682149016171223e-05 -▁Straßen 2.8674572170571919e-05 -▁harmon 2.8674572170571919e-05 -60 2.8670783747772267e-05 -aum 2.8670783747772267e-05 -▁Antrag 2.8655630056573655e-05 -▁notwendigen 2.8636687942575391e-05 -aren 2.8625322674176432e-05 -▁closely 2.8617745828577128e-05 -▁Rooms 2.8606380560178168e-05 -▁Straße 2.8602592137378516e-05 -▁debt 2.8602592137378516e-05 -▁gerecht 2.8602592137378516e-05 -▁erfüllt 2.8595015291779209e-05 -igkeiten 2.8591226868979557e-05 -▁schw 2.8587438446179905e-05 -▁listed 2.8583650023380253e-05 -Ã 2.8579861600580597e-05 -▁earlier 2.8579861600580597e-05 -▁anderes 2.8572284754981293e-05 -▁freien 2.855334264098303e-05 -▁followed 2.8549554218183377e-05 -▁happen 2.8549554218183377e-05 -▁paper 2.8549554218183377e-05 -▁vorher 2.8545765795383722e-05 -▁dienen 2.8534400526984766e-05 -▁votes 2.8519246835786154e-05 -ipp 2.8511669990186847e-05 -ump 2.8507881567387195e-05 -▁guidelines 2.8507881567387195e-05 -▁Funktionen 2.8500304721787891e-05 -▁introduction 2.8492727876188583e-05 -▁finanziellen 2.8481362607789627e-05 -eut 2.847378576219032e-05 -ief 2.8466208916591016e-05 -▁worth 2.8454843648192056e-05 -▁zufrieden 2.84434783797931e-05 -▁Click 2.8439689956993444e-05 -▁jederzeit 2.843211311139414e-05 -▁Kenntnis 2.8424536265794833e-05 -▁Col 2.8401805728996917e-05 -▁ergreifen 2.8386652037798306e-05 -▁01 2.8382863614998654e-05 -▁Monate 2.8371498346599694e-05 -▁Partnerschaft 2.8348767809801778e-05 -▁Erst 2.8344979387002126e-05 -▁Technologien 2.8344979387002126e-05 -▁jene 2.8341190964202474e-05 -ting 2.8333614118603167e-05 -▁Privat 2.8329825695803515e-05 -▁Kommunikation 2.8326037273003863e-05 -▁Schweiz 2.8318460427404555e-05 -▁beziehen 2.8318460427404555e-05 -▁Dateien 2.8314672004604903e-05 -▁enterprises 2.8314672004604903e-05 -ains 2.83070951590056e-05 -▁Suche 2.8299518313406292e-05 -ices 2.829572989060664e-05 -▁Gew 2.829572989060664e-05 -hren 2.825405723981046e-05 -▁Ausbildung 2.8250268817010805e-05 -▁fahren 2.8250268817010805e-05 -buch 2.82426919714115e-05 -▁lösen 2.82426919714115e-05 -▁strengthen 2.8238903548611849e-05 -▁kostenlos 2.8235115125812197e-05 -▁wünschen 2.8231326703012541e-05 -▁1, 2.8219961434613585e-05 -▁braucht 2.8208596166214626e-05 -▁Gesetz 2.8201019320615322e-05 -▁opened 2.8201019320615322e-05 -▁save 2.8201019320615322e-05 -enk 2.818586562941671e-05 -urt 2.818586562941671e-05 -▁Produkten 2.8182077206617055e-05 -▁buildings 2.8178288783817403e-05 -▁vermeiden 2.8174500361017751e-05 -arr 2.8159346669819139e-05 -▁ISO 2.8151769824219835e-05 -▁verbessert 2.8151769824219835e-05 -▁discover 2.8136616133021223e-05 -gruppen 2.8132827710221571e-05 -▁Office 2.8129039287421916e-05 -▁gleiche 2.8106308750624e-05 -▁Act 2.8102520327824348e-05 -▁Aufnahme 2.8098731905024696e-05 -▁Außen 2.8094943482225041e-05 -▁improving 2.8087366636625737e-05 -iken 2.8083578213826085e-05 -▁Klima 2.8083578213826085e-05 -hl 2.8076001368226777e-05 -▁Konzept 2.8076001368226777e-05 -rag 2.8072212945427125e-05 -ser 2.8057059254228513e-05 -▁firm 2.8057059254228513e-05 -▁op 2.8049482408629209e-05 -▁schwierig 2.8045693985829557e-05 -▁-- 2.8034328717430598e-05 -▁spirit 2.8034328717430598e-05 -▁Apartments 2.8030540294630946e-05 -▁Vielfalt 2.8019175026231986e-05 -▁Kindern 2.8004021335033375e-05 -▁networks 2.8000232912233723e-05 -▁ed 2.7992656066634419e-05 -▁speech 2.7985079221035111e-05 -ager 2.7981290798235459e-05 -ests 2.7981290798235459e-05 -▁trip 2.7981290798235459e-05 -ony 2.7977502375435807e-05 -▁Zweck 2.7977502375435807e-05 -▁super 2.79699255298365e-05 -▁Arten 2.7958560261437543e-05 -▁linked 2.793961814743928e-05 -▁track 2.7932041301839972e-05 -▁beds 2.7924464456240668e-05 -gang 2.7920676033441013e-05 -▁darunter 2.7920676033441013e-05 -▁sa 2.7920676033441013e-05 -▁Einrichtungen 2.7916887610641361e-05 -▁Meter 2.7913099187841709e-05 -▁exclusive 2.7913099187841709e-05 -▁map 2.7909310765042057e-05 -▁sufficient 2.7909310765042057e-05 -ication 2.7905522342242405e-05 -▁lesen 2.7905522342242405e-05 -▁operations 2.7901733919442749e-05 -▁supporting 2.7901733919442749e-05 -.de 2.7897945496643097e-05 -ki 2.7897945496643097e-05 -▁telephone 2.7886580228244138e-05 -▁uses 2.7882791805444486e-05 -▁understanding 2.7879003382644833e-05 -atur 2.7875214959845181e-05 -▁Kapital 2.7875214959845181e-05 -▁Greece 2.786006126864657e-05 -▁Wettbewerbs 2.784869600024761e-05 -▁considerable 2.7833542309049e-05 -▁immigration 2.7833542309049e-05 -▁Bush 2.7822177040650043e-05 -▁Innovation 2.7822177040650043e-05 -▁Off 2.7818388617850391e-05 -▁degree 2.7807023349451431e-05 -▁so-called 2.7803234926651779e-05 -aff 2.779186965825282e-05 -▁marketing 2.779186965825282e-05 -▁Standards 2.7769139121454904e-05 -orm 2.7765350698655248e-05 -yp 2.7761562275855596e-05 -anlagen 2.775019700745664e-05 -▁Top 2.775019700745664e-05 -▁fallen 2.775019700745664e-05 -ita 2.7735043316258029e-05 -welt 2.7727466470658721e-05 -▁jeweils 2.7723678047859069e-05 -▁Economic 2.7716101202259765e-05 -▁touch 2.7716101202259765e-05 -▁sal 2.7708524356660458e-05 -▁sicherlich 2.7708524356660458e-05 -▁Moment 2.7704735933860806e-05 -▁Betrieb 2.7700947511061154e-05 -▁Mus 2.76971590882615e-05 -▁criminal 2.7693370665461846e-05 -▁länger 2.7689582242662194e-05 -▁Sinn 2.767064012866393e-05 -▁entschieden 2.767064012866393e-05 -ahn 2.7663063283064626e-05 -IG 2.7621390632268444e-05 -▁mis 2.7621390632268444e-05 -▁verw 2.7621390632268444e-05 -▁ständig 2.7606236941069832e-05 -▁provision 2.760244851827018e-05 -▁went 2.7598660095470528e-05 -eat 2.7579717981472264e-05 -ights 2.7560775867474e-05 -č 2.7560775867474e-05 -asch 2.7553199021874693e-05 -т 2.7549410599075041e-05 -▁angezeigt 2.7545622176275389e-05 -▁entweder 2.7530468485076778e-05 -kte 2.7522891639477474e-05 -▁components 2.7522891639477474e-05 -▁cuisine 2.7507737948278862e-05 -▁gern 2.7503949525479207e-05 -▁Go 2.7496372679879902e-05 -▁Kur 2.74887958342806e-05 -▁alte 2.74887958342806e-05 -▁founded 2.7485007411480943e-05 -▁Tages 2.7469853720282331e-05 -▁detail 2.7469853720282331e-05 -▁relax 2.7462276874683027e-05 -▁e.g. 2.7458488451883375e-05 -▁Wege 2.7454700029083723e-05 -▁komplett 2.743575791508546e-05 -▁Stellen 2.7431969492285804e-05 -▁door 2.7428181069486152e-05 -▁gelangen 2.7420604223886848e-05 -ison 2.7416815801087193e-05 -▁properly 2.7413027378287541e-05 -▁speziell 2.7413027378287541e-05 -▁'ll 2.7409238955487888e-05 -▁Menge 2.7397873687088929e-05 -▁Every 2.7394085264289277e-05 -▁playing 2.7386508418689973e-05 -▁presence 2.7378931573090665e-05 -▁übernachten 2.7378931573090665e-05 -▁purchase 2.7371354727491361e-05 -▁erzielen 2.7363777881892054e-05 -▁Mass 2.7359989459092402e-05 -▁che 2.7359989459092402e-05 -▁tourism 2.7359989459092402e-05 -▁Mir 2.735620103629275e-05 -▁stock 2.7352412613493098e-05 -▁experts 2.7348624190693442e-05 -▁gave 2.7341047345094138e-05 -▁animal 2.7337258922294486e-05 -tw 2.7329682076695179e-05 -▁36 2.7318316808296222e-05 -▁Grundsatz 2.7310739962696915e-05 -▁Stimme 2.7310739962696915e-05 -ös 2.7303163117097611e-05 -▁items 2.7295586271498303e-05 -▁Februar 2.7291797848698651e-05 -▁teilweise 2.7291797848698651e-05 -▁pres 2.7265278889101084e-05 -allen 2.7250125197902472e-05 -▁Belarus 2.724633677510282e-05 -▁ging 2.7242548352303165e-05 -mar 2.7238759929503513e-05 -▁zeit 2.7238759929503513e-05 -cks 2.7231183083904209e-05 -▁typical 2.7227394661104556e-05 -ith 2.72236062383049e-05 -satz 2.72236062383049e-05 -▁vorgesehen 2.72236062383049e-05 -▁Street 2.7219817815505249e-05 -▁Abschließend 2.7216029392705597e-05 -bew 2.7204664124306637e-05 -schw 2.7204664124306637e-05 -▁su 2.7200875701506985e-05 -▁wahr 2.7197087278707333e-05 -uit 2.7193298855907681e-05 -▁entstehen 2.7181933587508722e-05 -▁Mod 2.7159203050710806e-05 -line 2.7144049359512195e-05 -▁duty 2.7140260936712539e-05 -▁erhöhen 2.7140260936712539e-05 -▁Guest 2.7136472513912887e-05 -tens 2.7132684091113235e-05 -▁remind 2.7125107245513931e-05 -▁Brussels 2.7113741977114971e-05 -▁S. 2.7113741977114971e-05 -▁entirely 2.7113741977114971e-05 -▁fear 2.7109953554315319e-05 -gende 2.7102376708716012e-05 -ox 2.7102376708716012e-05 -▁dank 2.709858828591636e-05 -▁generally 2.7094799863116708e-05 -▁Forderung 2.7091011440317056e-05 -▁Station 2.7079646171918096e-05 -▁pol 2.7072069326318792e-05 -▁coffee 2.7053127212320525e-05 -▁resort 2.7053127212320525e-05 -▁Status 2.7045550366721221e-05 -70 2.7037973521121917e-05 -hm 2.7037973521121917e-05 -▁ausdrücklich 2.7026608252722957e-05 -▁coordination 2.7022819829923305e-05 -▁fisheries 2.7022819829923305e-05 -▁Arab 2.7007666138724694e-05 -▁besuchen 2.7007666138724694e-05 -▁competitiveness 2.7007666138724694e-05 -ction 2.6992512447526082e-05 -▁na 2.698872402472643e-05 -ull 2.6984935601926778e-05 -aw 2.6981147179127123e-05 -,00 2.6977358756327471e-05 -▁plants 2.6977358756327471e-05 -▁presidency 2.6977358756327471e-05 -▁Menschenrechts 2.6958416642329207e-05 -▁Health 2.6950839796729903e-05 -▁technische 2.6947051373930248e-05 -▁Vereinten 2.6943262951130596e-05 -▁Because 2.6939474528330943e-05 -▁une 2.6935686105531291e-05 -ev 2.6928109259931984e-05 -▁gekommen 2.6928109259931984e-05 -▁welcher 2.6928109259931984e-05 -▁bas 2.6924320837132332e-05 -▁Jetzt 2.6912955568733372e-05 -leg 2.6901590300334416e-05 -▁Arbeiten 2.6901590300334416e-05 -▁po 2.6901590300334416e-05 -▁vorhanden 2.6901590300334416e-05 -▁Wo 2.6897801877534764e-05 -▁PHP 2.6894013454735109e-05 -▁u 2.6894013454735109e-05 -▁peaceful 2.6890225031935457e-05 -д 2.6875071340736845e-05 -▁erstellt 2.6875071340736845e-05 -handel 2.6871282917937193e-05 -ester 2.6867494495137541e-05 -▁Poland 2.6867494495137541e-05 -frage 2.6859917649538234e-05 -ektor 2.6856129226738582e-05 -▁stimme 2.6856129226738582e-05 -äch 2.6844763958339622e-05 -ände 2.6837187112740318e-05 -▁Material 2.6837187112740318e-05 -▁Pakistan 2.6837187112740318e-05 -▁difficulties 2.6837187112740318e-05 -▁permanent 2.6837187112740318e-05 -oph 2.6833398689940666e-05 -▁authority 2.6833398689940666e-05 -▁sagt 2.6833398689940666e-05 -▁wären 2.6833398689940666e-05 -▁dealing 2.6829610267141014e-05 -▁bear 2.6818244998742054e-05 -▁kommenden 2.6818244998742054e-05 -▁Black 2.6803091307543443e-05 -▁install 2.6799302884743791e-05 -▁relate 2.6791726039144483e-05 -▁Mitte 2.6780360770745527e-05 -▁funktioniert 2.6780360770745527e-05 -▁schl 2.6780360770745527e-05 -▁recommend 2.6765207079546916e-05 -▁Name 2.6750053388348304e-05 -ahr 2.6746264965548652e-05 -best 2.6746264965548652e-05 -▁wollte 2.6742476542749e-05 -▁Gott 2.6738688119949344e-05 -▁Kurz 2.6734899697149692e-05 -ß 2.6727322851550388e-05 -▁shops 2.6723534428750736e-05 -▁Leistungen 2.6715957583151429e-05 -ften 2.6708380737552125e-05 -▁Entwurf 2.6700803891952817e-05 -▁betonen 2.6700803891952817e-05 -▁summit 2.6700803891952817e-05 -ange 2.6693227046353513e-05 -▁beschlossen 2.6693227046353513e-05 -ille 2.6681861777954554e-05 -▁organisation 2.666291966395629e-05 -▁ph 2.6628823858759415e-05 -▁erhält 2.6625035435959763e-05 -▁Banken 2.6621247013160111e-05 -▁facing 2.6613670167560803e-05 -stoffe 2.6609881744761151e-05 -▁Unfortunately 2.6609881744761151e-05 -▁format 2.6602304899161847e-05 -▁informieren 2.6602304899161847e-05 -▁Berichte 2.6598516476362192e-05 -omb 2.659472805356254e-05 -ush 2.659472805356254e-05 -orr 2.6590939630762888e-05 -▁son 2.6587151207963236e-05 -ativen 2.658336278516358e-05 -heim 2.6579574362363928e-05 -▁Leider 2.6575785939564276e-05 -▁combat 2.6571997516764624e-05 -▁hour 2.6556843825566012e-05 -▁Veränderungen 2.6549266979966705e-05 -▁Great 2.65416901343674e-05 -▁district 2.6537901711567749e-05 -▁handeln 2.6534113288768097e-05 -90 2.6522748020369137e-05 -zeichen 2.6515171174769833e-05 -▁2011 2.6511382751970178e-05 -uche 2.6507594329170526e-05 -▁Instrumente 2.6500017483571222e-05 -▁lernen 2.6488652215172262e-05 -etr 2.6477286946773303e-05 -▁vehicles 2.6469710101174e-05 -▁machines 2.6465921678374346e-05 -onen 2.6439402718776775e-05 -▁Teile 2.6439402718776775e-05 -▁average 2.6435614295977123e-05 -▁Warum 2.6431825873177471e-05 -▁Abgeordnete 2.64053069135799e-05 -▁campaign 2.64053069135799e-05 -▁speaking 2.64053069135799e-05 -▁Tel 2.6401518490780248e-05 -▁meetings 2.6390153222381289e-05 -legen 2.6374999531182677e-05 -▁las 2.6367422685583373e-05 -▁Webseite 2.6363634262783721e-05 -▁applies 2.6363634262783721e-05 -▁fragen 2.6363634262783721e-05 -▁fixed 2.6359845839984069e-05 -▁forget 2.6356057417184413e-05 -itor 2.6352268994384761e-05 -ode 2.6348480571585109e-05 -ining 2.633711530318615e-05 -ty 2.6314384766388234e-05 -▁Initiativen 2.6314384766388234e-05 -▁court 2.6314384766388234e-05 -▁thinking 2.6299231075189623e-05 -▁EADS 2.6276500538391707e-05 -▁Engagement 2.6276500538391707e-05 -▁Vielen 2.6272712115592055e-05 -▁darstellen 2.6272712115592055e-05 -▁dagegen 2.6249981578794136e-05 -▁sm 2.6246193155994484e-05 -iums 2.623861631039518e-05 -no 2.623861631039518e-05 -▁institution 2.6219674196396913e-05 -▁Maschinen 2.6212097350797609e-05 -▁Zeitraum 2.6208308927997957e-05 -iar 2.6204520505198305e-05 -▁Systeme 2.6200732082398649e-05 -▁Geschäft 2.6185578391200041e-05 -▁Miss 2.6170424700001429e-05 -▁consideration 2.6170424700001429e-05 -schlag 2.6166636277201774e-05 -▁Casino 2.6166636277201774e-05 -▁kn 2.6162847854402122e-05 -▁Herz 2.6155271008802818e-05 -▁Bern 2.6143905740403858e-05 -▁becomes 2.6143905740403858e-05 -▁income 2.6136328894804554e-05 -ior 2.6128752049205247e-05 -uring 2.6124963626405595e-05 -▁numbers 2.6124963626405595e-05 -▁stabil 2.6121175203605943e-05 -▁Installation 2.6113598358006635e-05 -▁Zeiten 2.6109809935206983e-05 -▁.... 2.6102233089607679e-05 -▁aspect 2.6090867821208719e-05 -▁durchaus 2.6083290975609415e-05 -▁wenigen 2.6083290975609415e-05 -▁Once 2.607950255280976e-05 -▁einz 2.6075714130010108e-05 -▁gefordert 2.6075714130010108e-05 -dem 2.6071925707210456e-05 -▁born 2.6064348861611152e-05 -▁object 2.6060560438811496e-05 -▁unternehmen 2.6045406747612885e-05 -▁Gipfel 2.6034041479213929e-05 -▁deep 2.6022676210814969e-05 -▁erleben 2.6022676210814969e-05 -▁Ra 2.6011310942416013e-05 -▁opposition 2.6007522519616358e-05 -ags 2.5984791982818442e-05 -▁Währung 2.5984791982818442e-05 -▁dev 2.5984791982818442e-05 -▁def 2.5973426714419482e-05 -▁picture 2.5973426714419482e-05 -▁veröffentlicht 2.5958273023220871e-05 -ced 2.5950696177621567e-05 -▁Madrid 2.5950696177621567e-05 -▁stärken 2.5950696177621567e-05 -▁Angaben 2.5939330909222607e-05 -▁Cas 2.5939330909222607e-05 -▁konkrete 2.5935542486422955e-05 -▁absolut 2.5920388795224344e-05 -▁flexibility 2.5920388795224344e-05 -▁Absatz 2.591281194962504e-05 -▁told 2.5897658258426428e-05 -lex 2.5893869835626776e-05 -RO 2.5890081412827124e-05 -▁Fonds 2.5878716144428164e-05 -▁gener 2.5874927721628512e-05 -▁Har 2.5871139298828857e-05 -▁mer 2.5871139298828857e-05 -▁Kritik 2.5863562453229553e-05 -▁Sub 2.5863562453229553e-05 -▁practices 2.5863562453229553e-05 -▁screen 2.5863562453229553e-05 -▁advanced 2.5852197184830593e-05 -IR 2.5848408762030941e-05 -▁erklären 2.5848408762030941e-05 -▁database 2.583325507083233e-05 -▁dec 2.5814312956834066e-05 -▁Breakfast 2.5780217151637191e-05 -▁Bra 2.5765063460438579e-05 -▁Mio. 2.5757486614839275e-05 -▁Mitgliedstaat 2.5757486614839275e-05 -▁Netzwerk 2.5753698192039623e-05 -▁gewährleistet 2.5753698192039623e-05 -bedingungen 2.5749909769239968e-05 -▁domestic 2.5749909769239968e-05 -▁aims 2.5746121346440316e-05 -stra 2.5738544500841012e-05 -▁Mult 2.5738544500841012e-05 -▁attempt 2.573475607804136e-05 -rechts 2.5727179232442052e-05 -▁signal 2.5727179232442052e-05 -▁Hauses 2.5719602386842748e-05 -▁PT 2.5719602386842748e-05 -▁According 2.5715813964043093e-05 -▁Glück 2.5715813964043093e-05 -▁ST 2.5715813964043093e-05 -tes 2.5700660272844484e-05 -▁benötigt 2.5700660272844484e-05 -▁freue 2.5693083427245177e-05 -▁condition 2.5685506581645873e-05 -▁rev 2.5685506581645873e-05 -▁middle 2.5681718158846221e-05 -▁klare 2.5677929736046565e-05 -igem 2.5670352890447261e-05 -▁trägt 2.5666564467647609e-05 -spr 2.5662776044847954e-05 -▁Anteil 2.5662776044847954e-05 -▁Global 2.5658987622048302e-05 -▁PDF 2.5658987622048302e-05 -iting 2.565519919924865e-05 -▁bodies 2.565519919924865e-05 -▁defined 2.5647622353649346e-05 -▁myself 2.5647622353649346e-05 -▁easier 2.5640045508050038e-05 -▁Private 2.5632468662450734e-05 -ister 2.5621103394051774e-05 -▁Klicken 2.5617314971252122e-05 -▁liebe 2.5602161280053511e-05 -▁hält 2.5598372857253859e-05 -.4 2.5594584434454207e-05 -▁politics 2.5594584434454207e-05 -▁Herausforderung 2.55870075888549e-05 -▁lokalen 2.55870075888549e-05 -▁propose 2.5571853897656288e-05 -ily 2.5549123360858372e-05 -▁mid 2.5549123360858372e-05 -▁Worte 2.5530181246860108e-05 -▁Santa 2.5526392824060456e-05 -▁himself 2.55226044012608e-05 -▁signed 2.5518815978461149e-05 -▁respond 2.5507450710062189e-05 -ichen 2.5503662287262537e-05 -aler 2.5492297018863581e-05 -see 2.5488508596063926e-05 --st 2.5484720173264274e-05 -▁Sim 2.5469566482065662e-05 -▁klicken 2.5469566482065662e-05 -▁Prioritäten 2.546577805926601e-05 -▁strength 2.546577805926601e-05 -▁Brüssel 2.5461989636466358e-05 -▁ancient 2.5454412790867051e-05 -▁angesprochen 2.5454412790867051e-05 -▁Gebieten 2.54506243680674e-05 -▁Hafen 2.54506243680674e-05 -▁erfolgen 2.5446835945267747e-05 -isierten 2.5443047522468095e-05 -▁parliamentary 2.5443047522468095e-05 -▁erscheint 2.5439259099668442e-05 -fre 2.5431682254069135e-05 -▁initial 2.5431682254069135e-05 -▁Schul 2.5420316985670175e-05 -▁Wieder 2.5420316985670175e-05 -▁financing 2.5420316985670175e-05 -▁experienced 2.5416528562870523e-05 -▁informed 2.5416528562870523e-05 -▁teil 2.5416528562870523e-05 -aster 2.5412740140070871e-05 -ünd 2.5412740140070871e-05 -▁translation 2.5412740140070871e-05 -▁3D 2.539758644887226e-05 -▁Versuch 2.5393798026072608e-05 -▁stable 2.5390009603272956e-05 -spiel 2.5378644334873996e-05 -▁Regelung 2.5371067489274692e-05 -▁seriously 2.5371067489274692e-05 -▁Zwischen 2.5367279066475037e-05 -я 2.5352125375276429e-05 -▁Herstellung 2.5348336952476773e-05 -▁Any 2.5329394838478509e-05 -▁Association 2.5325606415678857e-05 -▁establishment 2.5318029570079553e-05 -▁failure 2.5318029570079553e-05 -ological 2.5306664301680594e-05 -cher 2.5295299033281634e-05 -▁möglichst 2.5295299033281634e-05 -▁inf 2.5291510610481982e-05 -dienst 2.5276356919283371e-05 -▁Dienste 2.5272568496483719e-05 -▁Paul 2.5272568496483719e-05 -▁optim 2.5272568496483719e-05 -eder 2.5257414805285107e-05 -▁promoting 2.5253626382485455e-05 -▁estate 2.5242261114086496e-05 -▁Forum 2.5238472691286843e-05 -▁dangerous 2.5238472691286843e-05 -▁Sand 2.5234684268487191e-05 -▁dennoch 2.5227107422887884e-05 -▁konzentrieren 2.5227107422887884e-05 -▁voice 2.5227107422887884e-05 -▁Titel 2.5215742154488928e-05 -▁Note 2.5211953731689272e-05 -▁anyone 2.5211953731689272e-05 -▁Ö 2.5200588463290316e-05 -amt 2.5189223194891357e-05 -pen 2.5185434772091705e-05 -▁Stellungnahme 2.5181646349292053e-05 -▁Camp 2.5174069503692745e-05 -▁Zweifel 2.5170281080893093e-05 -▁finanzielle 2.5170281080893093e-05 -▁erstellen 2.5158915812494134e-05 -▁nächste 2.5151338966894829e-05 -▁Dennoch 2.5143762121295525e-05 -ée 2.5136185275696218e-05 -▁Voll 2.5136185275696218e-05 -fähigkeit 2.5128608430096914e-05 -▁Klein 2.5124820007297258e-05 -lose 2.5121031584497606e-05 -▁Jahrhunderts 2.5113454738898302e-05 -▁dir 2.510966631609865e-05 -▁Rot 2.5102089470499343e-05 -▁Regional 2.5098301047699691e-05 -ection 2.5094512624900039e-05 -▁Nachfolgend 2.5090724202100387e-05 -annt 2.507178208810212e-05 -▁Home 2.507178208810212e-05 -▁Völker 2.507178208810212e-05 -▁Achtung 2.5064205242502816e-05 -▁connected 2.5064205242502816e-05 -▁lie 2.5064205242502816e-05 -▁differences 2.5060416819703163e-05 -▁threat 2.5060416819703163e-05 -ativ 2.5056628396903511e-05 -▁Einhaltung 2.5056628396903511e-05 -▁positions 2.5056628396903511e-05 -▁bequem 2.5052839974103856e-05 -▁models 2.5052839974103856e-05 -isse 2.5045263128504552e-05 -▁Forderungen 2.50414747057049e-05 -▁Aufbau 2.5037686282905244e-05 -urn 2.503010943730594e-05 -gt 2.5018744168906981e-05 -▁studies 2.5014955746107329e-05 -у 2.5011167323307677e-05 -▁Ed 2.5007378900508025e-05 -falls 2.5003590477708369e-05 -▁difference 2.5003590477708369e-05 -oten 2.4999802054908717e-05 -▁Hall 2.4996013632109065e-05 -▁break 2.4996013632109065e-05 -▁combination 2.4996013632109065e-05 -ved 2.4988436786509761e-05 -▁Einigung 2.4988436786509761e-05 -▁Entschließungsantrag 2.4988436786509761e-05 -▁unterschiedliche 2.497328309531115e-05 -▁gegründet 2.4969494672511497e-05 -▁defence 2.496191782691219e-05 -abel 2.4954340981312886e-05 -▁Terrasse 2.4954340981312886e-05 -▁Will 2.4942975712913926e-05 -▁täglich 2.4939187290114274e-05 -▁Bet 2.4935398867314622e-05 -▁persönlich 2.4935398867314622e-05 -▁beaches 2.4927822021715315e-05 -▁box 2.4920245176116011e-05 -▁Mel 2.4916456753316355e-05 -▁appears 2.4916456753316355e-05 -▁booking 2.4901303062117747e-05 -▁Kingdom 2.4897514639318092e-05 -▁verfolgen 2.4897514639318092e-05 -▁Schengen 2.4889937793718788e-05 -▁structures 2.4889937793718788e-05 -▁associated 2.4882360948119484e-05 -▁Saint 2.4878572525319828e-05 -▁Aspekt 2.4867207256920872e-05 -▁Unterkunft 2.4867207256920872e-05 -▁humanitarian 2.4867207256920872e-05 -▁übertragen 2.4867207256920872e-05 -▁carefully 2.4863418834121217e-05 -▁Formen 2.4855841988521912e-05 -▁verd 2.4855841988521912e-05 -▁Kirche 2.485205356572226e-05 -▁amerikanischen 2.4844476720122953e-05 -▁happened 2.4844476720122953e-05 -▁reservation 2.48406882973233e-05 -▁33 2.4829323028924341e-05 -▁Hamburg 2.4829323028924341e-05 -▁rem 2.4829323028924341e-05 -▁Analyse 2.4825534606124689e-05 -▁ski 2.4821746183325037e-05 -▁article 2.4814169337725733e-05 -rech 2.4799015646527122e-05 -▁ref 2.4791438800927814e-05 -▁implementing 2.4787650378128162e-05 -▁Jugend 2.478386195532851e-05 -▁courses 2.47687082641299e-05 -▁Vis 2.4764919841330246e-05 -▁Irish 2.4761131418530594e-05 -▁Over 2.4761131418530594e-05 -▁exhibition 2.4761131418530594e-05 -▁houses 2.4757342995730939e-05 -▁Paket 2.4753554572931287e-05 -▁legen 2.4749766150131635e-05 -▁Gesundheits 2.4745977727331983e-05 -▁geleistet 2.4738400881732675e-05 -▁spielt 2.4738400881732675e-05 -ifying 2.4734612458933023e-05 -urg 2.4734612458933023e-05 -▁instance 2.4734612458933023e-05 -▁Firstly 2.4730824036133371e-05 -▁betrachtet 2.4730824036133371e-05 -kten 2.4727035613333719e-05 -▁umfasst 2.4727035613333719e-05 -▁nennen 2.4711881922135108e-05 -▁Texte 2.4700516653736148e-05 -▁verlassen 2.4696728230936496e-05 -▁Großbritannien 2.4689151385337189e-05 -▁prefer 2.4689151385337189e-05 -▁u. 2.4689151385337189e-05 -11 2.4685362962537537e-05 -inter 2.4685362962537537e-05 -▁75 2.4685362962537537e-05 -▁Fischerei 2.4685362962537537e-05 -vollen 2.4681574539737885e-05 -▁statements 2.467399769413858e-05 -▁unabhängig 2.467399769413858e-05 -▁una 2.4662632425739621e-05 -▁1994 2.4655055580140313e-05 -▁Aussicht 2.4655055580140313e-05 -▁attractive 2.4655055580140313e-05 -▁impossible 2.4651267157340661e-05 -▁names 2.463611346614205e-05 -▁Main 2.4628536620542746e-05 -▁diskutieren 2.4624748197743094e-05 -▁schönen 2.4624748197743094e-05 -▁shared 2.4624748197743094e-05 -▁Sehenswürdigkeiten 2.460580608374483e-05 -▁concrete 2.4594440815345871e-05 -▁agencies 2.4586863969746563e-05 -richt 2.4583075546946911e-05 -▁denken 2.4571710278547955e-05 -▁leader 2.4571710278547955e-05 -▁spent 2.4571710278547955e-05 -▁immediate 2.4556556587349343e-05 -anda 2.4545191318950384e-05 -▁Gut 2.4545191318950384e-05 -▁architecture 2.4545191318950384e-05 -▁reducing 2.4545191318950384e-05 -▁Resort 2.453761447335108e-05 -▁pictures 2.453761447335108e-05 -▁Wellness 2.4533826050551424e-05 -box 2.4530037627751772e-05 -▁48 2.452624920495212e-05 -länder 2.4522460782152468e-05 -▁Due 2.4514883936553161e-05 -▁Trade 2.4503518668154205e-05 -▁producers 2.4499730245354549e-05 -▁Gemeinsamen 2.4492153399755245e-05 -▁announced 2.4492153399755245e-05 -▁alt 2.4476999708556633e-05 -▁coast 2.4469422862957329e-05 -▁arrival 2.4465634440157677e-05 -▁Dorf 2.445805759455837e-05 -▁rent 2.445805759455837e-05 -▁Ideen 2.444669232615941e-05 -▁reasonable 2.444669232615941e-05 -▁Lesung 2.4439115480560106e-05 -▁Ser 2.4439115480560106e-05 -▁watch 2.4439115480560106e-05 -ehen 2.4423961789361495e-05 -▁adoption 2.4416384943762191e-05 -zy 2.4412596520962535e-05 -▁Mindest 2.4412596520962535e-05 -▁positiv 2.4408808098162883e-05 -▁garantiert 2.4405019675363231e-05 -▁Digital 2.4386077561364967e-05 -▁Links 2.4378500715765663e-05 -▁net 2.4378500715765663e-05 -▁ben 2.4374712292966008e-05 -▁contain 2.4370923870166356e-05 -▁truly 2.4370923870166356e-05 -▁counter 2.4367135447366704e-05 -▁hören 2.4367135447366704e-05 -▁Are 2.4363347024567052e-05 -▁opt 2.4363347024567052e-05 -▁Support 2.4355770178967744e-05 -▁evidence 2.4351981756168092e-05 -uff 2.434819333336844e-05 -▁Hersteller 2.434819333336844e-05 -▁television 2.4344404910568788e-05 -ci 2.4340616487769133e-05 -ding 2.4340616487769133e-05 -▁Ruhe 2.4340616487769133e-05 -ih 2.4336828064969481e-05 -ead 2.4333039642169829e-05 -▁Anwendungen 2.4333039642169829e-05 -▁guarantees 2.4333039642169829e-05 -▁background 2.4329251219370177e-05 -▁calling 2.4325462796570521e-05 -▁feststellen 2.4325462796570521e-05 -iff 2.4321674373770869e-05 -▁Umfang 2.4321674373770869e-05 -▁unto 2.4314097528171565e-05 -app 2.4306520682572258e-05 -▁Richtlinien 2.4306520682572258e-05 -▁availability 2.4306520682572258e-05 -▁arbeitet 2.4302732259772606e-05 -▁charged 2.4302732259772606e-05 -▁released 2.4302732259772606e-05 -▁erforderlichen 2.4298943836972953e-05 -▁advance 2.4287578568573994e-05 -▁described 2.4287578568573994e-05 -▁ev 2.428000172297469e-05 -▁gef 2.428000172297469e-05 -mäßig 2.4264848031776078e-05 -▁Sehr 2.4261059608976426e-05 -▁Gal 2.4253482763377119e-05 -▁Sorge 2.4242117494978163e-05 -▁genuine 2.4242117494978163e-05 -▁Gleichzeitig 2.4238329072178507e-05 -▁ahead 2.4230752226579203e-05 -▁Ak 2.42231753809799e-05 -▁imm 2.4215598535380592e-05 -erne 2.421181011258094e-05 -▁Charakter 2.421181011258094e-05 -▁recognition 2.4208021689781287e-05 -▁Vielzahl 2.4204233266981632e-05 -▁Volks 2.4204233266981632e-05 -ament 2.420044484418198e-05 -▁Stra 2.420044484418198e-05 -▁England 2.4196656421382328e-05 -▁überprüfen 2.4177714307384064e-05 -▁Far 2.4166349038985105e-05 -▁Wald 2.4162560616185453e-05 -elling 2.4154983770586149e-05 -▁sol 2.4154983770586149e-05 -bahn 2.4151195347786493e-05 -▁cards 2.4151195347786493e-05 -▁Justice 2.4147406924986841e-05 -▁Ass 2.4143618502187189e-05 -▁obtain 2.4139830079387537e-05 -▁improvement 2.4136041656587885e-05 -▁formal 2.4128464810988578e-05 -▁relaxing 2.4128464810988578e-05 -verl 2.4120887965389273e-05 -▁EG 2.4120887965389273e-05 -ving 2.4117099542589618e-05 -▁fin 2.4117099542589618e-05 -▁wishes 2.4113311119789966e-05 -▁ausreichend 2.4109522696990314e-05 -▁possibilities 2.4105734274190662e-05 -eas 2.410194585139101e-05 -▁Polen 2.4098157428591354e-05 -▁stattfinden 2.40867921601924e-05 -▁Other 2.4079215314593091e-05 -▁urban 2.4079215314593091e-05 -▁Prime 2.4075426891793439e-05 -▁membership 2.4075426891793439e-05 -▁richtigen 2.4060273200594827e-05 -▁Stärkung 2.4056484777795175e-05 -▁nearby 2.4056484777795175e-05 -▁Rechnung 2.4041331086596564e-05 -▁glass 2.4041331086596564e-05 -▁monetary 2.4037542663796912e-05 -▁einiger 2.4026177395397952e-05 -▁discrimination 2.4018600549798648e-05 -▁miss 2.4007235281399688e-05 -▁unacceptable 2.4007235281399688e-05 -▁Roman 2.3999658435800384e-05 -▁Patienten 2.3980716321802121e-05 -▁Ferien 2.3973139476202813e-05 -ently 2.3969351053403161e-05 -▁territory 2.3969351053403161e-05 -▁Arbeitslosigkeit 2.3965562630603509e-05 -▁kulturellen 2.3950408939404898e-05 -zahl 2.3946620516605246e-05 -▁Seine 2.3946620516605246e-05 -▁Live 2.3939043671005938e-05 -▁ret 2.3939043671005938e-05 -preis 2.3935255248206286e-05 -▁Landschaft 2.3935255248206286e-05 -▁Tourismus 2.3935255248206286e-05 -ool 2.3931466825406634e-05 -▁hingewiesen 2.3931466825406634e-05 -▁tasks 2.3931466825406634e-05 -▁consumption 2.3920101557007674e-05 -▁continued 2.3916313134208022e-05 -▁unless 2.3908736288608715e-05 -▁Entwicklungen 2.3901159443009411e-05 -▁Ins 2.3897371020209759e-05 -▁tätig 2.3897371020209759e-05 --Sch 2.3893582597410107e-05 -▁zuvor 2.3889794174610451e-05 -▁übrigen 2.38860057518108e-05 -▁Fisch 2.3882217329011147e-05 -▁Pack 2.387464048341184e-05 -by 2.3870852060612188e-05 -ishing 2.3870852060612188e-05 -▁cur 2.3870852060612188e-05 -alk 2.3863275215012884e-05 -▁CO2 2.3863275215012884e-05 -▁USB 2.3863275215012884e-05 -▁skills 2.3863275215012884e-05 -▁seek 2.3855698369413576e-05 -▁wood 2.3855698369413576e-05 -AG 2.3851909946613924e-05 -▁Bedarf 2.3851909946613924e-05 -▁miteinander 2.3851909946613924e-05 -hilfe 2.384433310101462e-05 -▁Künstler 2.384433310101462e-05 -lands 2.3840544678214968e-05 -▁Poker 2.3840544678214968e-05 -▁cars 2.3832967832615661e-05 -▁Barroso 2.3829179409816008e-05 -sta 2.3825390987016356e-05 -▁trading 2.38216025642167e-05 -▁Risiko 2.3817814141417049e-05 -off 2.3814025718617397e-05 -▁Afrika 2.3814025718617397e-05 -▁continues 2.3810237295817745e-05 -eld 2.3798872027418785e-05 -▁Stock 2.3798872027418785e-05 --con 2.3795083604619133e-05 -▁Link 2.3795083604619133e-05 -▁figures 2.3795083604619133e-05 -LE 2.3791295181819481e-05 -▁Foundation 2.3787506759019826e-05 -▁det 2.3787506759019826e-05 -ügen 2.3779929913420522e-05 -▁packages 2.377614149062087e-05 -▁unfortunately 2.377614149062087e-05 -▁erw 2.3772353067821218e-05 -▁bezeichnet 2.3768564645021562e-05 -▁sup 2.3768564645021562e-05 -▁African 2.3760987799422258e-05 -▁combined 2.3757199376622606e-05 -▁devices 2.3753410953822954e-05 -▁hom 2.37496225310233e-05 -▁participate 2.37496225310233e-05 -acy 2.3742045685423995e-05 -vo 2.3738257262624342e-05 -▁introduce 2.3738257262624342e-05 -werke 2.3734468839824687e-05 -▁greatest 2.3726891994225383e-05 -▁Bas 2.3723103571425731e-05 -ugen 2.3715526725826423e-05 -▁profitieren 2.3715526725826423e-05 -▁Beschäftigungs 2.3707949880227119e-05 -▁completed 2.3704161457427467e-05 -▁generation 2.3704161457427467e-05 -▁Michael 2.3700373034627812e-05 -▁strongly 2.3700373034627812e-05 -ora 2.3685219343429204e-05 -▁impl 2.3681430920629548e-05 -▁Leistung 2.3673854075030244e-05 -schein 2.3670065652230592e-05 -▁desire 2.3666277229430937e-05 -▁Fol 2.3662488806631285e-05 -▁Ap 2.3647335115432673e-05 -▁Dollar 2.3647335115432673e-05 -ately 2.3635969847033717e-05 -▁beauty 2.3635969847033717e-05 -▁19. 2.3632181424234065e-05 -▁aufge 2.3628393001434409e-05 -,5 2.3620816155835105e-05 -▁raise 2.3620816155835105e-05 -hotel 2.3617027733035453e-05 -▁surrounding 2.3609450887436146e-05 -▁allowing 2.3605662464636494e-05 -▁• 2.3605662464636494e-05 -▁Back 2.3601874041836842e-05 -▁Festival 2.359808561903719e-05 -igh 2.3590508773437882e-05 -▁uner 2.3590508773437882e-05 -▁weiterer 2.3590508773437882e-05 -▁enables 2.3582931927838578e-05 -ble 2.3579143505038923e-05 -▁zahlen 2.3575355082239271e-05 -▁Deutsche 2.3571566659439619e-05 -sel 2.3567778236639967e-05 -▁explain 2.3563989813840315e-05 -▁perform 2.3541259277042395e-05 -▁Media 2.3537470854242743e-05 -▁eingeführt 2.3537470854242743e-05 -▁stat 2.3529894008643439e-05 -▁command 2.351852874024448e-05 -▁verloren 2.351852874024448e-05 -achten 2.3510951894645176e-05 -▁Irland 2.350716347184552e-05 -▁aufmerksam 2.350716347184552e-05 -▁Pf 2.3499586626246216e-05 -▁Europeans 2.3495798203446564e-05 -▁Bedürfnisse 2.3488221357847257e-05 -▁SP 2.3488221357847257e-05 -▁conduct 2.3488221357847257e-05 -▁Teilnehmer 2.3480644512247953e-05 -▁attractions 2.3480644512247953e-05 -▁gewinnen 2.3480644512247953e-05 -▁symbol 2.34768560894483e-05 -▁wor 2.34768560894483e-05 -▁religious 2.3461702398249689e-05 -▁window 2.3461702398249689e-05 -ense 2.3457913975450037e-05 -▁shop 2.3450337129850729e-05 -▁Konflikt 2.3446548707051077e-05 -▁wh 2.3446548707051077e-05 -ländern 2.343897186145177e-05 -▁Lo 2.343897186145177e-05 -▁Turkish 2.343897186145177e-05 -▁Very 2.3435183438652118e-05 -▁mission 2.3412452901854202e-05 -▁registered 2.3412452901854202e-05 -▁river 2.3412452901854202e-05 -esh 2.340866447905455e-05 -▁spread 2.340866447905455e-05 -▁Altstadt 2.3397299210655591e-05 -▁relativ 2.3389722365056287e-05 --J 2.3382145519456979e-05 -▁absolute 2.3374568673857675e-05 -▁verm 2.3374568673857675e-05 -ores 2.3366991828258368e-05 -▁Vienna 2.3366991828258368e-05 -▁Wissenschaft 2.3366991828258368e-05 -▁economies 2.3366991828258368e-05 -å 2.3363203405458716e-05 -▁Ol 2.3363203405458716e-05 -▁Innen 2.3359414982659063e-05 -▁Justiz 2.3359414982659063e-05 -met 2.3351838137059756e-05 -▁Prot 2.3332896023061492e-05 -▁schön 2.3332896023061492e-05 -▁Fe 2.332910760026184e-05 -▁Problemen 2.332910760026184e-05 -abe 2.3313953909063229e-05 -▁Beschreibung 2.3306377063463925e-05 -▁beginnt 2.3306377063463925e-05 -▁Überwachung 2.3306377063463925e-05 -▁Fotos 2.3302588640664273e-05 -hnen 2.3298800217864617e-05 -gleich 2.3295011795064965e-05 -▁überall 2.3295011795064965e-05 -▁insurance 2.3291223372265313e-05 -▁meters 2.3291223372265313e-05 -▁zentrale 2.3291223372265313e-05 -▁Di 2.3287434949465661e-05 -▁Foto 2.3283646526666006e-05 -pflicht 2.327228125826705e-05 -▁Democrats 2.327228125826705e-05 -▁entitled 2.327228125826705e-05 -▁160 2.3268492835467397e-05 -▁closer 2.3268492835467397e-05 -▁gem 2.3268492835467397e-05 -atten 2.326091598986809e-05 -▁surface 2.326091598986809e-05 -▁geschehen 2.3257127567068438e-05 -▁Tod 2.3253339144268786e-05 -omen 2.3245762298669478e-05 -uct 2.3241973875869826e-05 -stelle 2.3238185453070174e-05 -▁Mer 2.3238185453070174e-05 -▁button 2.3238185453070174e-05 -▁danach 2.3234397030270522e-05 -ches 2.3226820184671215e-05 -▁Angesichts 2.3226820184671215e-05 -▁Geschäftsordnung 2.3226820184671215e-05 -▁einsetzen 2.3226820184671215e-05 -▁Technik 2.3223031761871563e-05 -ables 2.3215454916272259e-05 -▁Beach 2.3211666493472603e-05 -▁dedicated 2.3207878070672951e-05 -▁unterstütze 2.3207878070672951e-05 -ulated 2.3200301225073647e-05 -▁Drogen 2.3177570688275728e-05 -ida 2.3173782265476076e-05 -▁34 2.3169993842676424e-05 -▁Data 2.3169993842676424e-05 -▁geschützt 2.3169993842676424e-05 -▁Beitritt 2.3162416997077116e-05 -ala 2.3154840151477812e-05 -▁regionalen 2.315105172867816e-05 -▁san 2.3147263305878508e-05 -erung 2.31396864602792e-05 -▁il 2.31396864602792e-05 -▁manage 2.3124532769080589e-05 -▁betroffen 2.3113167500681633e-05 -▁pour 2.3113167500681633e-05 -iles 2.3109379077881978e-05 -mel 2.3109379077881978e-05 -в 2.3105590655082326e-05 -▁fruit 2.3105590655082326e-05 -▁sämtliche 2.3079071695484758e-05 -▁sonst 2.3075283272685103e-05 -▁keinerlei 2.307149484988545e-05 -▁vorliegenden 2.307149484988545e-05 -▁Griechenland 2.3060129581486494e-05 -▁Rome 2.3056341158686839e-05 -▁bestätigt 2.3056341158686839e-05 -▁commun 2.3052552735887187e-05 -▁Jeder 2.3048764313087535e-05 -▁Bal 2.3044975890287883e-05 -▁Lake 2.3041187467488231e-05 -▁sale 2.3041187467488231e-05 -▁37 2.3037399044688575e-05 -▁besitzt 2.3037399044688575e-05 -▁ernst 2.3033610621888923e-05 -▁Tal 2.3026033776289619e-05 -▁engine 2.3026033776289619e-05 -▁gegenwärtig 2.3026033776289619e-05 -ätze 2.3022245353489964e-05 -fol 2.3018456930690312e-05 -▁Geb 2.3010880085091008e-05 -zogen 2.30033032394917e-05 -▁Meiner 2.30033032394917e-05 -▁square 2.30033032394917e-05 -▁Eigen 2.2988149548293089e-05 -▁Fortschritt 2.2984361125493437e-05 -▁Priorität 2.2984361125493437e-05 -▁Dis 2.2980572702693784e-05 -▁Kal 2.2980572702693784e-05 -utes 2.2976784279894132e-05 -volle 2.297299585709448e-05 -▁Federal 2.2969207434294825e-05 -▁union 2.2969207434294825e-05 -▁Europäer 2.2961630588695521e-05 -▁Kind 2.2961630588695521e-05 -▁enormous 2.2957842165895869e-05 -▁näher 2.2957842165895869e-05 -▁Anlagen 2.2954053743096213e-05 -karte 2.2946476897496909e-05 -osen 2.2946476897496909e-05 -▁learning 2.2946476897496909e-05 -▁exercise 2.2931323206298298e-05 -▁Wind 2.2927534783498646e-05 -▁Another 2.2923746360698994e-05 -▁vollkommen 2.2916169515099686e-05 -▁großes 2.2912381092300034e-05 -▁spezielle 2.2908592669500382e-05 -▁compared 2.290480424670073e-05 -▁Elemente 2.2901015823901075e-05 -▁einfache 2.2901015823901075e-05 -▁verfolgt 2.2901015823901075e-05 -▁politischer 2.2893438978301771e-05 -▁individuals 2.2885862132702466e-05 -▁perspective 2.2885862132702466e-05 -▁railway 2.2885862132702466e-05 -▁installed 2.2878285287103159e-05 -▁session 2.2878285287103159e-05 -▁reliable 2.2874496864303507e-05 -▁totally 2.2874496864303507e-05 -▁Bis 2.2870708441503855e-05 -▁Dom 2.28669200187042e-05 -stellungen 2.2863131595904547e-05 -▁Prinzip 2.2859343173104895e-05 -▁wind 2.2859343173104895e-05 -ids 2.2855554750305243e-05 -▁asylum 2.2855554750305243e-05 -▁bath 2.2855554750305243e-05 -▁buffet 2.2851766327505591e-05 -▁beteiligt 2.2840401059106632e-05 -▁französischen 2.2832824213507328e-05 -▁luxury 2.2829035790707672e-05 -▁vision 2.282524736790802e-05 -▁bill 2.2821458945108368e-05 -▁Verpflichtung 2.2817670522308716e-05 -▁gestalten 2.2813882099509061e-05 -▁diss 2.2802516831110105e-05 -▁gross 2.2802516831110105e-05 -▁Bewertung 2.2798728408310452e-05 -▁Book 2.2798728408310452e-05 -▁offensichtlich 2.2791151562711145e-05 -▁cons 2.2787363139911493e-05 -iness 2.2775997871512533e-05 -scha 2.2775997871512533e-05 -stell 2.2764632603113577e-05 -▁advertising 2.2764632603113577e-05 -fert 2.2760844180313922e-05 -itter 2.2760844180313922e-05 -wagen 2.2760844180313922e-05 -▁größeren 2.2760844180313922e-05 -▁Cap 2.275705575751427e-05 -▁empfehlen 2.2741902066315658e-05 -▁angeboten 2.2734325220716354e-05 -▁kept 2.2730536797916702e-05 -▁meist 2.2722959952317395e-05 -cia 2.2719171529517743e-05 -▁holidays 2.2715383106718091e-05 -▁storage 2.2715383106718091e-05 -iving 2.2707806261118783e-05 -án 2.2707806261118783e-05 -▁Il 2.2704017838319131e-05 -▁extended 2.2700229415519479e-05 -▁ag 2.2696440992719827e-05 -▁Austrian 2.2692652569920171e-05 -▁gutes 2.2688864147120519e-05 -▁Nevertheless 2.2685075724320867e-05 -▁forced 2.2677498878721563e-05 -aging 2.2673710455921908e-05 -▁1990 2.2669922033122256e-05 -▁Good 2.2669922033122256e-05 -▁wonder 2.2666133610322604e-05 -▁stands 2.2658556764723296e-05 -▁Ir 2.264719149632434e-05 -▁früher 2.2635826227925381e-05 -▁platform 2.2635826227925381e-05 -▁spending 2.2635826227925381e-05 -▁claims 2.2628249382326077e-05 -äm 2.2624460959526425e-05 -▁corporate 2.2620672536726769e-05 -cing 2.2616884113927117e-05 -ater 2.2613095691127465e-05 -▁zusätzlich 2.2613095691127465e-05 -▁Schaden 2.2609307268327813e-05 -▁gu 2.2609307268327813e-05 -▁biggest 2.2605518845528158e-05 -▁Indien 2.2601730422728505e-05 -▁sides 2.2601730422728505e-05 -▁Globalisierung 2.25941535771292e-05 -▁Av 2.2590365154329549e-05 -▁Tisch 2.2586576731529894e-05 -▁device 2.2582788308730242e-05 -▁convinced 2.2575211463130938e-05 -▁AR 2.2571423040331282e-05 -▁Auftrag 2.2571423040331282e-05 -▁Regelungen 2.2571423040331282e-05 -▁warten 2.256763461753163e-05 -any 2.2563846194731978e-05 -▁Foreign 2.2563846194731978e-05 -▁golf 2.2563846194731978e-05 -▁Liefer 2.2560057771932326e-05 -▁story 2.2560057771932326e-05 -ello 2.2548692503533367e-05 -▁Mein 2.2544904080733715e-05 -▁gain 2.2544904080733715e-05 -▁urgent 2.2541115657934063e-05 -▁science 2.2537327235134411e-05 -▁memory 2.2533538812334755e-05 -▁Christ 2.2529750389535103e-05 -▁Worten 2.2525961966735451e-05 -▁failed 2.25221735439358e-05 -▁voraus 2.25221735439358e-05 -▁Botschaft 2.2514596698336492e-05 -▁Grenz 2.2510808275536839e-05 -▁garantieren 2.2510808275536839e-05 -▁AC 2.2488077738738924e-05 -▁Infrastruktur 2.2484289315939268e-05 -▁Les 2.2484289315939268e-05 -▁highlight 2.2480500893139616e-05 -▁Market 2.2476712470339964e-05 -▁ergeben 2.2472924047540312e-05 -▁mountains 2.2472924047540312e-05 -▁Netherlands 2.2465347201941005e-05 -▁Kle 2.2461558779141353e-05 -▁granted 2.2461558779141353e-05 -ieß 2.24577703563417e-05 -▁Day 2.2450193510742393e-05 -öl 2.2446405087942741e-05 -▁umfassende 2.2442616665143089e-05 -▁gegenwärtigen 2.2438828242343437e-05 -▁quick 2.2438828242343437e-05 -ua 2.2435039819543785e-05 -▁Islam 2.2435039819543785e-05 -▁Pap 2.2435039819543785e-05 -▁Karten 2.243125139674413e-05 -eam 2.2427462973944478e-05 -▁Maße 2.2427462973944478e-05 -i- 2.2408520859946214e-05 -▁self 2.2404732437146562e-05 -▁Universität 2.240094401434691e-05 -▁folgende 2.2397155591547254e-05 -▁para 2.2397155591547254e-05 -▁phys 2.2397155591547254e-05 -▁core 2.238957874594795e-05 -lichem 2.2385790323148298e-05 -üsse 2.2385790323148298e-05 -▁38 2.2385790323148298e-05 -▁app 2.2385790323148298e-05 -▁internationaler 2.2378213477548991e-05 -▁passed 2.2378213477548991e-05 -▁Direkt 2.2366848209150035e-05 -▁Eastern 2.2366848209150035e-05 -▁Eindruck 2.2366848209150035e-05 -▁Jo 2.2363059786350379e-05 -ops 2.2344117672352116e-05 -▁moving 2.2340329249552464e-05 -ura 2.233275240395316e-05 -▁beinhaltet 2.233275240395316e-05 -▁advice 2.23213871355542e-05 -▁bra 2.23213871355542e-05 -▁verbundenen 2.23213871355542e-05 -▁conclusions 2.2313810289954896e-05 -▁Begriff 2.231002186715524e-05 -▁Front 2.231002186715524e-05 -▁Alt 2.2306233444355588e-05 -▁controls 2.2306233444355588e-05 -▁Van 2.2302445021555936e-05 -com 2.2291079753156977e-05 -fälle 2.2291079753156977e-05 -oms 2.2291079753156977e-05 -▁Sonnen 2.2283502907557673e-05 -▁Vol 2.2283502907557673e-05 -▁Company 2.2279714484758021e-05 -▁Latin 2.2275926061958365e-05 -isierte 2.2272137639158713e-05 -s- 2.2272137639158713e-05 -▁Meeres 2.2272137639158713e-05 -km 2.2268349216359061e-05 -▁Größe 2.2264560793559409e-05 -▁establishing 2.2264560793559409e-05 -▁weight 2.2264560793559409e-05 -ored 2.2260772370759757e-05 -▁solcher 2.2260772370759757e-05 -▁unit 2.2260772370759757e-05 -▁tief 2.225319552516045e-05 -▁Gebrauch 2.2245618679561146e-05 -▁finance 2.2245618679561146e-05 -Innen 2.2238041833961838e-05 -org 2.2238041833961838e-05 -owing 2.2234253411162186e-05 -istr 2.2215311297163922e-05 -wir 2.2215311297163922e-05 -otel 2.221152287436427e-05 -▁Monat 2.2207734451564618e-05 -▁abgeschlossen 2.2207734451564618e-05 -▁acht 2.2200157605965311e-05 -▁west 2.2200157605965311e-05 -▁Mil 2.2196369183165659e-05 -▁fiscal 2.2196369183165659e-05 -▁saw 2.2196369183165659e-05 -, 2.2192580760366007e-05 -▁deliver 2.2192580760366007e-05 -▁mobil 2.2188792337566351e-05 -▁unable 2.2188792337566351e-05 -nt 2.2181215491967047e-05 -▁guide 2.2173638646367743e-05 -▁re- 2.2173638646367743e-05 -atives 2.2166061800768436e-05 -wasser 2.2162273377968784e-05 -▁disease 2.2162273377968784e-05 -▁Control 2.2158484955169132e-05 -▁Nor 2.2154696532369476e-05 -alter 2.2150908109569824e-05 -▁4. 2.2147119686770172e-05 -▁Nutzer 2.2147119686770172e-05 -▁fire 2.2147119686770172e-05 -▁EL 2.214333126397052e-05 -▁Greek 2.214333126397052e-05 -▁perfectly 2.214333126397052e-05 -lem 2.2139542841170868e-05 -▁Ur 2.2139542841170868e-05 -▁contrary 2.2139542841170868e-05 -aps 2.2135754418371213e-05 -▁church 2.213196599557156e-05 -▁scale 2.2124389149972256e-05 -ham 2.2116812304372949e-05 -▁Bur 2.2113023881573297e-05 -▁Regulation 2.2105447035973993e-05 -ils 2.2097870190374685e-05 -uchten 2.2097870190374685e-05 -▁Märkte 2.2097870190374685e-05 -▁Studio 2.2097870190374685e-05 -▁factors 2.2097870190374685e-05 -ieg 2.2090293344775381e-05 -ude 2.2090293344775381e-05 -▁ro 2.2086504921975729e-05 -▁tried 2.2086504921975729e-05 -▁Beteiligung 2.207513965357677e-05 -▁Gründe 2.207513965357677e-05 -oo 2.2067562807977462e-05 -▁Lizenz 2.2059985962378158e-05 -▁wesentlichen 2.2041043848379894e-05 -▁'re 2.2037255425580242e-05 -▁drawn 2.2037255425580242e-05 -ific 2.2033467002780587e-05 -▁bew 2.2029678579980935e-05 -н 2.2018313311581979e-05 -▁chosen 2.2010736465982671e-05 -▁interface 2.2010736465982671e-05 -▁Constitution 2.2006948043183019e-05 -▁Erklärungen 2.2006948043183019e-05 -▁artists 2.2006948043183019e-05 -hem 2.2003159620383367e-05 -atoren 2.1999371197583715e-05 -▁Palace 2.199558277478406e-05 -ured 2.1991794351984408e-05 -gabe 2.1988005929184756e-05 -▁entsprechen 2.1988005929184756e-05 -▁aufzu 2.1984217506385104e-05 -▁maintenance 2.1984217506385104e-05 -▁Special 2.1980429083585448e-05 --System 2.1972852237986144e-05 -▁Sw 2.1969063815186492e-05 -▁definition 2.196527539238684e-05 -▁98 2.1961486969587185e-05 -ify 2.1957698546787533e-05 -▁te 2.1953910123987881e-05 -▁weapons 2.1953910123987881e-05 -▁compl 2.1938756432789269e-05 -▁fe 2.1938756432789269e-05 -▁laid 2.1938756432789269e-05 -▁mo 2.1938756432789269e-05 -▁rightly 2.1934968009989617e-05 -▁route 2.1934968009989617e-05 -ount 2.1931179587189965e-05 -▁250 2.1927391164390309e-05 -▁Grunde 2.1919814318791005e-05 -fahrt 2.1916025895991353e-05 -▁Kurs 2.1916025895991353e-05 -▁Martin 2.1916025895991353e-05 -▁neither 2.19122374731917e-05 -▁win 2.19122374731917e-05 -▁Thus 2.1908449050392046e-05 -▁erhöht 2.1908449050392046e-05 -▁collect 2.1904660627592394e-05 -▁Bahnhof 2.1900872204792742e-05 -▁blog 2.189708378199309e-05 -ula 2.1893295359193434e-05 -▁honourable 2.1893295359193434e-05 -▁Institute 2.188571851359413e-05 -▁aut 2.188571851359413e-05 -ams 2.1874353245195171e-05 -▁Unabhängigkeit 2.1870564822395519e-05 -▁expression 2.1870564822395519e-05 -▁geöffnet 2.1866776399595867e-05 -▁Yet 2.1862987976796215e-05 -staaten 2.1859199553996559e-05 -eable 2.1855411131196907e-05 -teile 2.1851622708397255e-05 -▁Militär 2.1847834285597603e-05 -▁commitments 2.1844045862797951e-05 -▁Städte 2.1840257439998295e-05 -▁psych 2.1840257439998295e-05 -▁spa 2.1836469017198643e-05 -änder 2.1832680594398991e-05 -▁Maß 2.1832680594398991e-05 -▁photos 2.1821315326000032e-05 -iqu 2.181752690320038e-05 -TS 2.1809950057601076e-05 -ros 2.180616163480142e-05 -ç 2.1798584789202116e-05 -▁Po 2.1798584789202116e-05 -▁candidate 2.1798584789202116e-05 -▁outstanding 2.1798584789202116e-05 -▁Fern 2.1783431098003505e-05 -auer 2.1779642675203853e-05 -▁Marken 2.1779642675203853e-05 -▁update 2.1779642675203853e-05 -bst 2.17758542524042e-05 -▁Erde 2.17758542524042e-05 -▁outcome 2.17758542524042e-05 -▁rock 2.1772065829604545e-05 -▁betrachten 2.1768277406804893e-05 -▁agency 2.1756912138405937e-05 -▁intervention 2.1745546870006977e-05 -▁Verl 2.1741758447207325e-05 -▁Ru 2.173797002440767e-05 -▁speziellen 2.173797002440767e-05 -▁Gegensatz 2.1734181601608018e-05 -▁meaning 2.1726604756008714e-05 -isierungs 2.1722816333209062e-05 -▁Band 2.1715239487609754e-05 -▁Tür 2.1715239487609754e-05 -▁Beim 2.170766264201045e-05 -▁ambitious 2.170766264201045e-05 -▁register 2.170766264201045e-05 -▁dient 2.1703874219210798e-05 -▁high- 2.1703874219210798e-05 -izes 2.1688720528012187e-05 -▁Ko 2.1688720528012187e-05 -▁claim 2.1688720528012187e-05 -▁achieving 2.1684932105212531e-05 -▁ap 2.1684932105212531e-05 -▁patent 2.1684932105212531e-05 --r 2.1681143682412879e-05 -▁Net 2.1681143682412879e-05 -▁flight 2.1673566836813575e-05 -▁sensitive 2.1673566836813575e-05 -ee 2.1669778414013923e-05 -▁Mas 2.1662201568414615e-05 -▁obviously 2.1654624722815311e-05 -unk 2.1650836300015656e-05 -SP 2.1631894186017392e-05 -▁stra 2.1631894186017392e-05 -▁constantly 2.1616740494818784e-05 -▁ECU 2.1612952072019129e-05 -▁Kopf 2.1601586803620173e-05 -▁Studien 2.1601586803620173e-05 -äger 2.1597798380820517e-05 -▁Flexibilität 2.1597798380820517e-05 -▁langen 2.1594009958020865e-05 -▁Strom 2.1590221535221213e-05 -▁approximately 2.1586433112421561e-05 -▁Pool 2.1567490998423297e-05 -▁zentral 2.1563702575623642e-05 -ports 2.155991415282399e-05 -▁aircraft 2.1556125730024338e-05 -▁block 2.1552337307224686e-05 -▁gelungen 2.1552337307224686e-05 -illa 2.1548548884425034e-05 -▁Verständnis 2.1548548884425034e-05 -▁sell 2.1544760461625378e-05 -▁Sam 2.1537183616026074e-05 -▁Hinweis 2.1529606770426767e-05 -▁Kontroll 2.1529606770426767e-05 -▁rail 2.1529606770426767e-05 -▁5. 2.1522029924827463e-05 -▁colleague 2.1522029924827463e-05 -▁promotion 2.1522029924827463e-05 -▁starke 2.1518241502027811e-05 -ected 2.1514453079228159e-05 -ival 2.1514453079228159e-05 -▁vehicle 2.1510664656428503e-05 -▁Datenschutz 2.1506876233628851e-05 -▁schöne 2.1506876233628851e-05 -▁Ereignisse 2.1499299388029547e-05 -gu 2.1495510965229895e-05 -▁obligations 2.1495510965229895e-05 -▁Betriebs 2.149172254243024e-05 -▁surrounded 2.149172254243024e-05 -setzung 2.1484145696830936e-05 -▁Aktionen 2.1453838314433712e-05 -▁paragraph 2.1453838314433712e-05 -▁category 2.145004989163406e-05 -ball 2.1446261468834408e-05 -▁d. 2.1446261468834408e-05 -cht 2.1442473046034753e-05 -ering 2.1442473046034753e-05 -▁britischen 2.1442473046034753e-05 -▁sauber 2.14386846232351e-05 -▁42 2.1434896200435449e-05 -anlage 2.1431107777635797e-05 -OT 2.1427319354836145e-05 -▁informiert 2.1427319354836145e-05 -▁Weitere 2.1423530932036489e-05 -▁limits 2.1423530932036489e-05 -▁someone 2.1419742509236837e-05 -▁election 2.1415954086437185e-05 -leben 2.1408377240837881e-05 -▁continuing 2.1404588818038226e-05 -bildung 2.1400800395238574e-05 -zeug 2.1400800395238574e-05 -▁Bay 2.1400800395238574e-05 -▁daraus 2.1400800395238574e-05 -▁Nachdem 2.1397011972438922e-05 -▁Peter 2.1397011972438922e-05 -▁Geist 2.139322354963927e-05 -▁Jean 2.1389435126839614e-05 -▁organised 2.1389435126839614e-05 -▁yesterday 2.1385646704039962e-05 -haltung 2.1374281435641006e-05 -▁Verein 2.13667045900417e-05 -▁Fin 2.1359127744442394e-05 -▁Immobilien 2.1359127744442394e-05 -▁Gefühl 2.1355339321642739e-05 -▁Gleich 2.1355339321642739e-05 -▁Verkauf 2.1351550898843087e-05 -▁payments 2.1351550898843087e-05 -iation 2.1347762476043435e-05 -▁allgemein 2.1343974053243783e-05 -▁delay 2.1343974053243783e-05 -▁Farben 2.1340185630444131e-05 -▁Süden 2.1340185630444131e-05 -▁Zahlen 2.1336397207644475e-05 -▁successfully 2.1336397207644475e-05 -▁accessible 2.1328820362045171e-05 -▁situations 2.1328820362045171e-05 -▁1992 2.1325031939245519e-05 -▁Under 2.1325031939245519e-05 -▁elected 2.1325031939245519e-05 -▁rapid 2.1321243516445864e-05 -▁guter 2.1317455093646212e-05 -▁é 2.1317455093646212e-05 -▁Lang 2.131366667084656e-05 -▁Wander 2.131366667084656e-05 -ér 2.1309878248046908e-05 -▁contents 2.13023014024476e-05 -gesellschaft 2.1294724556848296e-05 -ipe 2.1294724556848296e-05 -ufen 2.1294724556848296e-05 -▁manchmal 2.1294724556848296e-05 -ulate 2.1290936134048644e-05 -▁Sl 2.1287147711248992e-05 -▁title 2.1287147711248992e-05 -▁Mad 2.1279570865649684e-05 -▁Bill 2.1275782442850032e-05 -▁Feld 2.1275782442850032e-05 -▁custom 2.127199402005038e-05 -▁Schiff 2.1264417174451073e-05 -▁club 2.1264417174451073e-05 -▁equality 2.1264417174451073e-05 -▁Asyl 2.1256840328851769e-05 -▁minute 2.1256840328851769e-05 -▁THE 2.1253051906052117e-05 -.org 2.1249263483252461e-05 -▁radio 2.1249263483252461e-05 -▁goals 2.1245475060452809e-05 -▁20. 2.1237898214853505e-05 -▁192 2.123410979205385e-05 -▁hauptsächlich 2.123410979205385e-05 -▁Empfehlungen 2.1230321369254198e-05 -▁reject 2.1230321369254198e-05 -▁satellite 2.1230321369254198e-05 -▁Te 2.1226532946454546e-05 -▁Wall 2.1226532946454546e-05 -▁Realität 2.1222744523654894e-05 -▁Located 2.1218956100855242e-05 -▁race 2.1218956100855242e-05 -ace 2.1215167678055586e-05 -ades 2.1215167678055586e-05 -▁solid 2.1215167678055586e-05 -▁Ä 2.1200013986856978e-05 -losen 2.1196225564057323e-05 -▁Get 2.119243714125767e-05 -▁Sur 2.119243714125767e-05 -▁rot 2.119243714125767e-05 -▁physical 2.1184860295658366e-05 -oor 2.1181071872858711e-05 -▁Ausland 2.1181071872858711e-05 -▁organization 2.1177283450059059e-05 -era 2.1173495027259407e-05 -▁Niveau 2.1173495027259407e-05 -▁wealth 2.1173495027259407e-05 --3 2.1169706604459755e-05 -▁Anlage 2.1169706604459755e-05 -▁asking 2.1169706604459755e-05 -programme 2.1158341336060795e-05 -▁Mensch 2.1158341336060795e-05 -▁Generation 2.1154552913261143e-05 -▁Haut 2.1154552913261143e-05 -▁gro 2.1154552913261143e-05 -▁Blog 2.1146976067661836e-05 -▁Sun 2.1146976067661836e-05 -ails 2.1143187644862184e-05 -▁reflect 2.1143187644862184e-05 -▁Wirkung 2.113561079926288e-05 -▁EP 2.1131822376463228e-05 -▁Hoffnung 2.1131822376463228e-05 -▁balanced 2.1131822376463228e-05 -▁Agency 2.1128033953663572e-05 -▁Marketing 2.1128033953663572e-05 -▁Agentur 2.112424553086392e-05 -oin 2.1116668685264616e-05 -▁massive 2.1116668685264616e-05 -▁store 2.1116668685264616e-05 -druck 2.1112880262464961e-05 -▁expenditure 2.1093938148466697e-05 -ela 2.1090149725667045e-05 -▁beachten 2.1090149725667045e-05 -▁Mitteln 2.1086361302867393e-05 -▁monitor 2.1086361302867393e-05 -▁Fraktionen 2.1082572880067741e-05 -▁sens 2.1078784457268089e-05 -entr 2.1074996034468433e-05 -▁Vorteil 2.1074996034468433e-05 -▁independence 2.1074996034468433e-05 -▁recommendations 2.1074996034468433e-05 -hafte 2.1071207611668781e-05 -▁Los 2.1071207611668781e-05 -▁ideale 2.1071207611668781e-05 -▁Beschluss 2.105605392047017e-05 -inder 2.1052265497670518e-05 -▁length 2.1052265497670518e-05 -äl 2.1044688652071214e-05 -▁Gedanken 2.1044688652071214e-05 -▁por 2.1044688652071214e-05 -▁allgemeine 2.1040900229271558e-05 -▁tut 2.1040900229271558e-05 -▁laut 2.1037111806471906e-05 -▁herzlich 2.1033323383672254e-05 -fully 2.1029534960872602e-05 -▁rasch 2.1025746538072947e-05 -elles 2.1021958115273295e-05 -▁lake 2.1018169692473643e-05 -▁medium-sized 2.1014381269673991e-05 -▁Lord 2.1010592846874338e-05 -▁Sprach 2.1010592846874338e-05 -▁furnished 2.0999227578475379e-05 -▁ill 2.0980285464477115e-05 -▁gehalten 2.0976497041677463e-05 -schrift 2.0965131773278504e-05 -bringen 2.0961343350478852e-05 -▁mittels 2.09575549276792e-05 -▁seven 2.09575549276792e-05 -▁write 2.09575549276792e-05 -▁geschieht 2.0953766504879544e-05 -angebot 2.0949978082079892e-05 -▁einverstanden 2.0949978082079892e-05 -▁Dimension 2.094618965928024e-05 -▁motor 2.094618965928024e-05 -▁Grad 2.0938612813680933e-05 -▁extend 2.0934824390881281e-05 -24 2.0919670699682669e-05 -▁changing 2.0919670699682669e-05 -▁i.e. 2.0919670699682669e-05 -▁treten 2.0919670699682669e-05 -▁festgestellt 2.0915882276883017e-05 -▁dé 2.0908305431283713e-05 -▁profit 2.0904517008484058e-05 -▁sobald 2.0896940162884753e-05 -abkommen 2.08931517400851e-05 -▁consists 2.08931517400851e-05 -haften 2.0889363317285449e-05 -▁targets 2.0889363317285449e-05 -▁Zoll 2.0885574894485794e-05 -▁merely 2.0881786471686142e-05 -▁Environment 2.087799804888649e-05 -▁Vom 2.087799804888649e-05 -▁Beihilfen 2.0874209626086838e-05 -▁Ke 2.0874209626086838e-05 -▁niemand 2.0870421203287186e-05 -▁Pa 2.086663278048753e-05 -▁Treffen 2.0862844357687878e-05 -ova 2.0859055934888226e-05 -▁Gran 2.0859055934888226e-05 -▁Voraussetzungen 2.0859055934888226e-05 -▁medium 2.0859055934888226e-05 -atch 2.0851479089288919e-05 -ollen 2.0847690666489267e-05 -▁Zugriff 2.0847690666489267e-05 -▁Old 2.0843902243689615e-05 -▁Programms 2.0843902243689615e-05 -▁landscape 2.0843902243689615e-05 -akte 2.0840113820889963e-05 -▁kaufen 2.0828748552491003e-05 -▁Report 2.0824960129691351e-05 -▁Hauptstadt 2.0817383284092044e-05 -▁Those 2.0817383284092044e-05 -▁möglichen 2.0817383284092044e-05 -▁species 2.0813594861292392e-05 -▁participants 2.0806018015693087e-05 -▁schneller 2.0806018015693087e-05 -▁damals 2.0802229592893435e-05 -▁acts 2.079844117009378e-05 -▁einzigen 2.0794652747294128e-05 -▁jüngsten 2.0794652747294128e-05 -▁Las 2.0790864324494476e-05 -▁Risiken 2.0790864324494476e-05 -▁bars 2.0787075901694824e-05 -▁contemporary 2.0783287478895172e-05 -▁smaller 2.0783287478895172e-05 -ossen 2.0779499056095516e-05 -▁Manager 2.0779499056095516e-05 -▁Square 2.0779499056095516e-05 -▁Summit 2.0779499056095516e-05 -▁Gegenteil 2.0775710633295864e-05 -▁obvious 2.0775710633295864e-05 -▁unternommen 2.0775710633295864e-05 -▁waiting 2.0775710633295864e-05 -fte 2.0771922210496212e-05 -▁communities 2.0771922210496212e-05 -▁spricht 2.0771922210496212e-05 -▁Objekt 2.0760556942097253e-05 -▁slow 2.07567685192976e-05 -▁Motor 2.0752980096497949e-05 -▁Enjoy 2.0749191673698297e-05 -▁wall 2.0749191673698297e-05 -ickt 2.0745403250898641e-05 -▁Palestinian 2.0745403250898641e-05 -▁print 2.0745403250898641e-05 -▁putting 2.0741614828098989e-05 -▁erfordert 2.0737826405299337e-05 -▁dimension 2.073024955970003e-05 -▁mail 2.073024955970003e-05 -▁jungen 2.0726461136900378e-05 -▁selten 2.0718884291301073e-05 -licherweise 2.0715095868501421e-05 -▁Ep 2.0715095868501421e-05 -▁mechanism 2.0707519022902114e-05 -▁colour 2.0692365331703502e-05 -▁prevention 2.0692365331703502e-05 -▁vorgeschlagenen 2.0692365331703502e-05 -▁Teilen 2.068857690890385e-05 -ends 2.0684788486104198e-05 -vert 2.0681000063304546e-05 -▁Gebiete 2.0677211640504891e-05 -▁Both 2.0673423217705239e-05 -▁Spezial 2.0662057949306283e-05 -15 2.0654481103706975e-05 -▁CS 2.0646904258107671e-05 -▁Ratspräsident 2.0646904258107671e-05 -▁abge 2.0646904258107671e-05 -▁sold 2.0646904258107671e-05 -elf 2.0639327412508364e-05 -ania 2.0635538989708712e-05 -straße 2.0635538989708712e-05 -▁Ze 2.0635538989708712e-05 -grund 2.06203852985101e-05 -ills 2.06203852985101e-05 -▁Arbeitsmarkt 2.0616596875710448e-05 -▁Royal 2.0616596875710448e-05 -▁schlecht 2.0616596875710448e-05 -iertes 2.0612808452910796e-05 -▁disposal 2.0612808452910796e-05 -▁regelmäßig 2.0612808452910796e-05 -▁contained 2.060902003011114e-05 -▁leads 2.0605231607311488e-05 -▁Bi 2.0601443184511836e-05 -▁guaranteed 2.0601443184511836e-05 -agn 2.0597654761712184e-05 -lie 2.0597654761712184e-05 -▁Phase 2.0597654761712184e-05 -▁persönliche 2.0590077916112877e-05 -▁Energy 2.0586289493313225e-05 -▁Männer 2.0574924224914269e-05 -▁sozial 2.0567347379314961e-05 -▁Via 2.0563558956515309e-05 -▁fraud 2.0559770533715657e-05 -▁Grundrechte 2.055219368811635e-05 -▁Sup 2.055219368811635e-05 -▁nationaler 2.055219368811635e-05 -▁übernommen 2.055219368811635e-05 -▁factor 2.0548405265316698e-05 -▁sage 2.0544616842517046e-05 -▁Rules 2.0540828419717393e-05 -eng 2.0533251574118086e-05 -agt 2.0529463151318434e-05 -▁Kat 2.0529463151318434e-05 -▁bringing 2.0529463151318434e-05 -zone 2.0525674728518782e-05 -▁Regierungs 2.0525674728518782e-05 -TE 2.0521886305719126e-05 -▁Politiker 2.0521886305719126e-05 -▁pointed 2.0518097882919474e-05 -▁regul 2.0518097882919474e-05 -▁Island 2.0514309460119822e-05 -▁Su 2.0514309460119822e-05 -▁grounds 2.0514309460119822e-05 -▁planned 2.0514309460119822e-05 -▁liberal 2.051052103732017e-05 -▁Mitgliedern 2.0499155768921211e-05 -▁proceed 2.0499155768921211e-05 -▁blue 2.0495367346121559e-05 -pp 2.0487790500522255e-05 -▁Bestandteil 2.0480213654922947e-05 -▁zus 2.0480213654922947e-05 -▁verstärkt 2.0468848386523988e-05 -hor 2.0465059963724336e-05 -▁Thanks 2.0465059963724336e-05 -▁convenient 2.0465059963724336e-05 -▁teilen 2.0465059963724336e-05 -ione 2.0461271540924684e-05 -fonds 2.0457483118125032e-05 -▁JavaScript 2.0457483118125032e-05 -▁registration 2.044232942692642e-05 -▁acceptable 2.0434752581327113e-05 -hö 2.043096415852746e-05 -▁substantial 2.0427175735727808e-05 -▁800 2.0423387312928156e-05 -▁nachhaltige 2.0423387312928156e-05 -▁password 2.0423387312928156e-05 -▁nearly 2.0415810467328849e-05 -▁dynamic 2.0412022044529197e-05 -oon 2.0408233621729545e-05 -▁inc 2.0408233621729545e-05 -▁prop 2.0408233621729545e-05 -▁Sweden 2.0404445198929893e-05 -▁Typ 2.0400656776130237e-05 -▁Option 2.0396868353330585e-05 -▁ID 2.0389291507731281e-05 -▁band 2.0385503084931629e-05 -ery 2.0377926239332322e-05 -▁resulting 2.037413781653267e-05 -▁Schluss 2.0366560970933366e-05 -▁anbieten 2.036277254813371e-05 -▁operate 2.036277254813371e-05 -▁III 2.0358984125334058e-05 -▁causes 2.0355195702534406e-05 -lungs 2.0351407279734754e-05 -uchen 2.0351407279734754e-05 -▁RO 2.0351407279734754e-05 -▁farm 2.0351407279734754e-05 -bank 2.0343830434135447e-05 -lav 2.0343830434135447e-05 -▁Welche 2.0343830434135447e-05 -management 2.0340042011335794e-05 -▁examples 2.0340042011335794e-05 -änden 2.033246516573649e-05 -▁Klimaanlage 2.033246516573649e-05 -▁brings 2.033246516573649e-05 -iem 2.0328676742936835e-05 -▁manufacturing 2.0324888320137183e-05 -▁zugleich 2.0321099897337531e-05 -inger 2.0317311474537879e-05 -▁Card 2.0317311474537879e-05 -▁Alter 2.0313523051738223e-05 -▁Del 2.0313523051738223e-05 -▁wodurch 2.0313523051738223e-05 -gn 2.0309734628938571e-05 -▁Königreich 2.0305946206138919e-05 -▁pleasure 2.0305946206138919e-05 -▁Pflicht 2.0298369360539615e-05 -alls 2.0290792514940308e-05 -pass 2.0290792514940308e-05 -▁seeking 2.0290792514940308e-05 -führer 2.0279427246541352e-05 -▁responsibilities 2.0279427246541352e-05 --de 2.0275638823741696e-05 -▁Beg 2.0275638823741696e-05 -▁fuel 2.0275638823741696e-05 -gere 2.0271850400942044e-05 -äng 2.0271850400942044e-05 -▁Touristen 2.0271850400942044e-05 -▁conclude 2.0271850400942044e-05 -▁Dort 2.0268061978142392e-05 -▁cheap 2.026427355534274e-05 -▁Teilnahme 2.0256696709743433e-05 -▁Sant 2.025290828694378e-05 -▁letter 2.025290828694378e-05 -ION 2.0249119864144128e-05 -▁betroffenen 2.0249119864144128e-05 -abl 2.0241543018544821e-05 -▁höher 2.0241543018544821e-05 -▁kleiner 2.0241543018544821e-05 -▁praktisch 2.0233966172945517e-05 -▁klein 2.0226389327346209e-05 -eite 2.0222600904546557e-05 -iler 2.0222600904546557e-05 -▁vorschlagen 2.0222600904546557e-05 -▁testing 2.0215024058947253e-05 -▁multiple 2.02112356361476e-05 -▁strengthening 2.0199870367748642e-05 -ausschuss 2.019608194494899e-05 -▁capable 2.0192293522149334e-05 -ope 2.0188505099349682e-05 -р 2.0188505099349682e-05 -▁Experten 2.0188505099349682e-05 -▁Effizienz 2.0180928253750378e-05 -▁identity 2.0180928253750378e-05 -▁Nov 2.0173351408151071e-05 -▁Chamber 2.0169562985351419e-05 -▁Lin 2.0169562985351419e-05 -▁argument 2.0169562985351419e-05 -▁Pat 2.0158197716952462e-05 -▁Prüfung 2.0158197716952462e-05 -▁Maria 2.0154409294152807e-05 -produktion 2.0150620871353155e-05 -arch 2.0146832448553503e-05 -▁beschäftigt 2.0143044025753851e-05 -▁zählen 2.0143044025753851e-05 -64 2.0139255602954195e-05 -fs 2.0139255602954195e-05 -▁Ebenso 2.0135467180154543e-05 -▁Korea 2.0135467180154543e-05 -▁recovery 2.0135467180154543e-05 -▁taste 2.0135467180154543e-05 -▁Alliance 2.0131678757354891e-05 -▁Liberalisierung 2.0131678757354891e-05 -▁zone 2.0124101911755587e-05 -▁Leit 2.011652506615628e-05 -▁gewählt 2.011652506615628e-05 -▁spaces 2.011652506615628e-05 -zul 2.0108948220556976e-05 -▁association 2.010515979775732e-05 -▁traditionellen 2.010515979775732e-05 -▁Where 2.0101371374957668e-05 -ky 2.0097582952158016e-05 -▁governance 2.0097582952158016e-05 -▁Fil 2.0090006106558712e-05 -▁decades 2.0090006106558712e-05 -▁Apartment 2.0086217683759057e-05 -▁Details 2.0082429260959405e-05 -▁Dutch 2.0082429260959405e-05 -▁Messe 2.0078640838159753e-05 -▁zunehmend 2.0078640838159753e-05 -▁secret 2.00748524153601e-05 -▁Schwerpunkt 2.0071063992560448e-05 -▁optimal 2.0067275569760793e-05 -▁steel 2.0067275569760793e-05 -▁Hälfte 2.0063487146961141e-05 -▁Heil 2.0059698724161489e-05 -ho 2.0052121878562181e-05 -▁auszu 2.0052121878562181e-05 -▁weg 2.0052121878562181e-05 -▁Internationalen 2.0044545032962877e-05 -▁River 2.0036968187363573e-05 -▁h. 2.0036968187363573e-05 -▁Bew 2.0025602918964614e-05 -▁sichern 2.0025602918964614e-05 -reichen 2.0021814496164962e-05 -▁21. 2.0021814496164962e-05 -▁School 2.0021814496164962e-05 -▁deutsche 2.0018026073365306e-05 -▁120 2.0014237650565654e-05 -▁Führung 2.0014237650565654e-05 -staat 2.0002872382166698e-05 -▁Tro 1.9995295536567391e-05 -▁forest 1.9995295536567391e-05 -▁southern 1.9995295536567391e-05 -▁adequate 1.9991507113767739e-05 -▁copy 1.9991507113767739e-05 -▁Erhöhung 1.9987718690968087e-05 -▁Original 1.9987718690968087e-05 -▁int 1.9987718690968087e-05 -▁sets 1.9987718690968087e-05 -po 1.9983930268168435e-05 -hrt 1.9976353422569127e-05 -▁AB 1.9976353422569127e-05 -▁master 1.9976353422569127e-05 -mon 1.9964988154170168e-05 -▁Liebe 1.9964988154170168e-05 -ahl 1.9957411308570863e-05 -▁besseren 1.9957411308570863e-05 -▁hergestellt 1.9957411308570863e-05 -▁Morgen 1.9942257617372252e-05 -▁Charter 1.99384691945726e-05 -▁primary 1.99384691945726e-05 -zuf 1.9934680771772948e-05 -▁driving 1.9930892348973292e-05 -▁previously 1.9930892348973292e-05 -ded 1.9923315503373988e-05 -▁Rotary 1.9923315503373988e-05 -▁notice 1.9923315503373988e-05 -▁Two 1.9911950234975029e-05 -▁Inc 1.9908161812175377e-05 -▁books 1.9908161812175377e-05 -▁wichtigste 1.9900584966576073e-05 -▁Trotz 1.9893008120976765e-05 -▁consensus 1.9893008120976765e-05 -▁patients 1.9893008120976765e-05 -▁diskutiert 1.9885431275377461e-05 -▁versions 1.9885431275377461e-05 -▁andererseits 1.9881642852577809e-05 --4 1.9877854429778154e-05 -▁Zimmern 1.9877854429778154e-05 -iment 1.9866489161379197e-05 -▁Java 1.9866489161379197e-05 -▁Norden 1.9866489161379197e-05 -king 1.9862700738579545e-05 -annten 1.9851335470180586e-05 -aufen 1.9843758624581278e-05 -ury 1.9843758624581278e-05 -▁truth 1.9839970201781626e-05 -▁vis 1.9839970201781626e-05 -itten 1.9836181778981974e-05 -▁discussing 1.9836181778981974e-05 -▁primarily 1.9836181778981974e-05 -gar 1.9821028087783363e-05 -park 1.9817239664983711e-05 -▁600 1.9817239664983711e-05 -lock 1.9809662819384403e-05 -▁Nice 1.9809662819384403e-05 -▁sieben 1.9809662819384403e-05 -▁Überprüfung 1.9809662819384403e-05 -▁Koordinierung 1.98020859737851e-05 -▁requested 1.98020859737851e-05 -▁Stein 1.9798297550985447e-05 -▁White 1.9794509128185795e-05 -▁industries 1.9794509128185795e-05 -▁addressed 1.979072070538614e-05 -▁av 1.9783143859786836e-05 -▁perfekt 1.9783143859786836e-05 -icks 1.9779355436987183e-05 -▁Romania 1.9779355436987183e-05 -▁Aufgrund 1.9775567014187531e-05 -▁remove 1.9775567014187531e-05 -▁190 1.9767990168588224e-05 -inal 1.9764201745788572e-05 -▁Eltern 1.9764201745788572e-05 -▁serv 1.9764201745788572e-05 -▁weiteres 1.9764201745788572e-05 -ktion 1.976041332298892e-05 -▁Absicht 1.976041332298892e-05 -▁Financial 1.9756624900189264e-05 -▁fällt 1.9756624900189264e-05 -▁Flash 1.9752836477389612e-05 -▁Laufe 1.974904805458996e-05 -tel 1.9737682786191e-05 -▁expensive 1.9737682786191e-05 -▁invest 1.9737682786191e-05 -▁Methode 1.9726317517792045e-05 -wand 1.9722529094992389e-05 -▁Ausnahme 1.9718740672192737e-05 -▁affect 1.9714952249393085e-05 -▁kosten 1.9714952249393085e-05 -▁cap 1.9711163826593433e-05 -esse 1.9707375403793781e-05 -▁Stadtzentrum 1.9707375403793781e-05 -▁1993 1.9703586980994126e-05 -▁Ton 1.9703586980994126e-05 -▁begann 1.9703586980994126e-05 -▁attacks 1.9692221712595169e-05 -▁Veranstaltungen 1.9688433289795514e-05 -▁beg 1.9688433289795514e-05 -▁liability 1.9684644866995862e-05 -▁comm 1.968085644419621e-05 -▁kürzlich 1.9677068021396558e-05 -iche 1.9673279598596906e-05 -▁kinds 1.9673279598596906e-05 -▁talks 1.9673279598596906e-05 -▁thereby 1.96657027529976e-05 -▁parallel 1.9661914330197946e-05 -elten 1.9658125907398294e-05 -▁schools 1.9658125907398294e-05 -▁Einkaufs 1.9654337484598642e-05 -▁nations 1.9654337484598642e-05 -aire 1.9650549061798987e-05 -▁U.S. 1.9650549061798987e-05 -▁Je 1.9646760638999335e-05 -▁Gästen 1.9642972216199683e-05 -anden 1.9639183793400031e-05 -ural 1.9635395370600375e-05 -▁Inseln 1.9635395370600375e-05 -▁Obama 1.9635395370600375e-05 -▁audio 1.9635395370600375e-05 -▁cont 1.9635395370600375e-05 -▁representative 1.9635395370600375e-05 -bericht 1.9631606947800723e-05 -iti 1.9631606947800723e-05 -mm 1.9631606947800723e-05 -▁Ess 1.9631606947800723e-05 -▁willkommen 1.9631606947800723e-05 -▁eight 1.9624030102201419e-05 -▁seitens 1.9624030102201419e-05 -entwicklung 1.9620241679401767e-05 -▁appeal 1.9620241679401767e-05 -▁NI 1.9616453256602112e-05 -▁phase 1.9616453256602112e-05 -▁Methoden 1.961266483380246e-05 -▁globale 1.961266483380246e-05 -let 1.96012995654035e-05 -▁finde 1.96012995654035e-05 -▁Pflanzen 1.9593722719804196e-05 -▁Law 1.9589934297004544e-05 -ower 1.9582357451405236e-05 -▁lag 1.9578569028605584e-05 -▁USS 1.9574780605805932e-05 -▁Diskriminierung 1.957099218300628e-05 -legt 1.9567203760206628e-05 -▁jemand 1.9567203760206628e-05 -▁HD 1.9559626914607321e-05 -▁orders 1.9555838491807669e-05 -▁einzelne 1.9544473223408709e-05 -▁junge 1.9533107955009753e-05 -▁apart 1.9521742686610794e-05 -▁reagieren 1.9521742686610794e-05 -▁Tar 1.9510377418211834e-05 -▁demokratische 1.9510377418211834e-05 -icken 1.9506588995412182e-05 -▁55 1.9499012149812878e-05 -geht 1.949143530421357e-05 -ound 1.949143530421357e-05 -▁Formular 1.9487646881413918e-05 -▁Mutter 1.9487646881413918e-05 -▁Wel 1.9487646881413918e-05 -▁akzeptiert 1.9487646881413918e-05 -ł 1.9483858458614266e-05 -▁Find 1.9483858458614266e-05 -▁turned 1.9483858458614266e-05 -▁default 1.9480070035814611e-05 -▁Patent 1.9476281613014959e-05 -▁Revolution 1.9461127921816347e-05 -▁opposite 1.9457339499016695e-05 -▁Angriff 1.9453551076217043e-05 -л 1.9449762653417391e-05 -ube 1.9442185807818084e-05 -▁evaluation 1.9442185807818084e-05 -ames 1.943460896221878e-05 -▁Bon 1.9430820539419128e-05 -▁Express 1.9430820539419128e-05 -▁nation 1.9430820539419128e-05 -▁sinnvoll 1.9427032116619472e-05 -ya 1.942324369381982e-05 -▁Anliegen 1.9419455271020168e-05 -▁extreme 1.9419455271020168e-05 -▁moral 1.9419455271020168e-05 -▁Arch 1.9411878425420864e-05 -ervice 1.9408090002621209e-05 -utions 1.9404301579821557e-05 -▁alter 1.9404301579821557e-05 -verkehrs 1.9396724734222252e-05 -▁Institution 1.9396724734222252e-05 -▁Gel 1.9385359465823293e-05 -▁named 1.9373994197424333e-05 -iest 1.9370205774624681e-05 -öt 1.9370205774624681e-05 -▁prepare 1.9366417351825029e-05 -ilt 1.9362628929025377e-05 -▁delivered 1.9362628929025377e-05 -cept 1.9358840506225725e-05 -uld 1.9358840506225725e-05 -▁Ta 1.9358840506225725e-05 -▁Vorbereitung 1.9358840506225725e-05 --free 1.935505208342607e-05 -▁11. 1.9347475237826766e-05 -▁Willen 1.9347475237826766e-05 -▁corner 1.9347475237826766e-05 -part 1.9343686815027114e-05 -ential 1.9339898392227458e-05 -▁Wegen 1.9339898392227458e-05 -▁anerkannt 1.9336109969427806e-05 -▁Tiere 1.9332321546628154e-05 -▁determined 1.9332321546628154e-05 -▁ländlichen 1.9332321546628154e-05 -under 1.932474470102885e-05 -▁Spe 1.932474470102885e-05 -▁reply 1.932474470102885e-05 -▁Fenster 1.9320956278229195e-05 -▁dealt 1.9320956278229195e-05 -ächt 1.9313379432629891e-05 -▁Versch 1.9309591009830238e-05 -▁echte 1.9305802587030583e-05 -▁Doppel 1.9298225741431279e-05 -setzen 1.9294437318631627e-05 -▁Wien 1.9294437318631627e-05 -▁Gesetzgebung 1.9286860473032319e-05 -▁Theater 1.9283072050232667e-05 -▁behandeln 1.9279283627433015e-05 -▁rad 1.9279283627433015e-05 -üssen 1.9275495204633363e-05 -▁angeht 1.9275495204633363e-05 -▁leg 1.9271706781833708e-05 -▁mutual 1.9271706781833708e-05 -▁Trek 1.9267918359034056e-05 -▁waters 1.9267918359034056e-05 -▁freie 1.9264129936234404e-05 -bens 1.9260341513434752e-05 -▁Zustand 1.9260341513434752e-05 -esen 1.92565530906351e-05 -▁Fischer 1.92565530906351e-05 -▁Mos 1.92565530906351e-05 -▁Prodi 1.92565530906351e-05 -▁betont 1.92565530906351e-05 -zi 1.924518782223614e-05 -▁dritten 1.9241399399436488e-05 -▁noise 1.9241399399436488e-05 -▁Anpassung 1.9233822553837181e-05 -▁Ordnung 1.9233822553837181e-05 -▁nochmals 1.9233822553837181e-05 -▁PR 1.9226245708237877e-05 -▁except 1.9226245708237877e-05 -▁snow 1.9226245708237877e-05 -EP 1.9222457285438224e-05 -ayer 1.9222457285438224e-05 -ía 1.9222457285438224e-05 -▁Freizeit 1.9222457285438224e-05 -▁grand 1.9222457285438224e-05 -▁Fähigkeit 1.9218668862638569e-05 -▁ren 1.9218668862638569e-05 -bert 1.9214880439838917e-05 -ways 1.9207303594239613e-05 -▁positiven 1.9207303594239613e-05 -▁manufacturers 1.9203515171439961e-05 -▁Despite 1.9192149903041e-05 -▁amongst 1.9192149903041e-05 -▁diversity 1.9188361480241349e-05 -▁roll 1.9188361480241349e-05 -bon 1.9180784634642042e-05 -▁berühmten 1.9180784634642042e-05 -▁Jan 1.9173207789042738e-05 -▁WC 1.9173207789042738e-05 -▁hall 1.9173207789042738e-05 -▁Stunde 1.916563094344343e-05 -▁umgeben 1.916563094344343e-05 -amer 1.9158054097844126e-05 -nissen 1.9158054097844126e-05 -25 1.9154265675044474e-05 -▁Church 1.9154265675044474e-05 -▁News 1.9154265675044474e-05 -▁onto 1.9154265675044474e-05 -▁Minutes 1.9150477252244822e-05 -▁sicherstellen 1.9150477252244822e-05 -▁well- 1.9150477252244822e-05 -▁Architektur 1.9146688829445167e-05 -▁qualified 1.9146688829445167e-05 -SE 1.9142900406645515e-05 -▁bewegen 1.9139111983845863e-05 -▁führenden 1.9135323561046211e-05 -▁Verhältnis 1.9131535138246555e-05 -▁Ca 1.9127746715446903e-05 -▁anti 1.9127746715446903e-05 -▁begonnen 1.9127746715446903e-05 -eration 1.9123958292647251e-05 -▁User 1.9116381447047947e-05 -▁skin 1.909743933304968e-05 --Kon 1.9086074064650724e-05 -▁Out 1.9086074064650724e-05 -▁Klimawandel 1.9082285641851072e-05 -▁Export 1.9078497219051416e-05 -▁Sein 1.9074708796251764e-05 -qualität 1.9070920373452112e-05 -▁diverse 1.9070920373452112e-05 -▁Dar 1.906713195065246e-05 -▁Wohl 1.906713195065246e-05 -▁urge 1.906713195065246e-05 -▁accounts 1.9063343527852808e-05 -▁leadership 1.9059555105053153e-05 -▁charming 1.9051978259453849e-05 -▁193 1.9048189836654197e-05 -▁Reaktion 1.9040612991054889e-05 -▁investments 1.9040612991054889e-05 -ibility 1.9036824568255237e-05 -▁Unterschiede 1.9036824568255237e-05 -ubl 1.9033036145455585e-05 -▁Freunde 1.9029247722655933e-05 -▁feeling 1.9029247722655933e-05 -bereiche 1.9025459299856278e-05 -raft 1.9025459299856278e-05 -▁father 1.9025459299856278e-05 -station 1.9017882454256973e-05 -▁Umständen 1.9017882454256973e-05 -▁Fälle 1.9010305608657666e-05 -▁Minderheiten 1.9010305608657666e-05 -▁Englisch 1.9006517185858014e-05 -▁Wachstums 1.9002728763058362e-05 -▁Pet 1.899894034025871e-05 -scher 1.8995151917459058e-05 -uts 1.8991363494659402e-05 -▁39 1.8991363494659402e-05 -▁AD 1.8991363494659402e-05 -house 1.8979998226260446e-05 -99 1.8976209803460791e-05 -▁Verfügbarkeit 1.8968632957861487e-05 -PS 1.8957267689462527e-05 -▁verlieren 1.8957267689462527e-05 -eb 1.8953479266662875e-05 -strategie 1.8938325575464264e-05 -▁\ 1.8938325575464264e-05 -▁Einklang 1.8934537152664612e-05 -▁Munich 1.8934537152664612e-05 -▁Time 1.8934537152664612e-05 -barer 1.8930748729864959e-05 -▁technological 1.8930748729864959e-05 -▁Master 1.8926960307065307e-05 -▁fand 1.8926960307065307e-05 -▁confirm 1.8923171884265652e-05 -▁Well 1.8919383461466e-05 -▁consultation 1.8919383461466e-05 -▁leiden 1.8919383461466e-05 -▁marked 1.8919383461466e-05 -▁moved 1.8919383461466e-05 -▁mechanisms 1.8915595038666348e-05 -iell 1.8911806615866696e-05 -▁Before 1.8911806615866696e-05 -ando 1.8892864501868432e-05 -▁Stück 1.8892864501868432e-05 -▁Super 1.8885287656269125e-05 -▁Take 1.8881499233469473e-05 -▁cm 1.8881499233469473e-05 -▁effizient 1.8881499233469473e-05 -▁1000 1.8877710810669821e-05 -chten 1.8873922387870169e-05 -halts 1.8873922387870169e-05 -▁burden 1.8873922387870169e-05 -▁sex 1.8873922387870169e-05 -▁pension 1.8870133965070513e-05 -▁Adresse 1.8858768696671557e-05 -▁runs 1.885119185107225e-05 -iant 1.8847403428272598e-05 -▁recommended 1.8847403428272598e-05 -▁Sound 1.8843615005472946e-05 -▁fordert 1.8843615005472946e-05 -▁interpret 1.8839826582673293e-05 -▁vielmehr 1.8839826582673293e-05 -▁heavy 1.8836038159873638e-05 -▁starken 1.8836038159873638e-05 -▁überzeugen 1.8836038159873638e-05 -zus 1.8832249737073986e-05 -igsten 1.8828461314274334e-05 -ú 1.8828461314274334e-05 -▁König 1.8828461314274334e-05 -ê 1.8817096045875374e-05 -fest 1.8813307623075722e-05 -▁Dadurch 1.880951920027607e-05 -▁bedarf 1.880951920027607e-05 -▁Schloss 1.8801942354676763e-05 -▁Freude 1.8798153931877111e-05 -▁Log 1.8790577086277807e-05 -▁ehemaligen 1.8790577086277807e-05 -▁zustimmen 1.8790577086277807e-05 -arian 1.8786788663478155e-05 -▁München 1.87830002406785e-05 -▁Zentral 1.87830002406785e-05 -▁wait 1.87830002406785e-05 -▁COM 1.8779211817878847e-05 -reis 1.8775423395079195e-05 -▁Kredit 1.8775423395079195e-05 -▁illustr 1.8775423395079195e-05 -▁Diskussionen 1.8771634972279543e-05 -▁mode 1.8771634972279543e-05 -▁Grenze 1.8767846549479888e-05 -▁knows 1.8767846549479888e-05 -▁improvements 1.8764058126680236e-05 -▁Hans 1.8760269703880584e-05 -▁attitude 1.8760269703880584e-05 -▁expl 1.8756481281080932e-05 -▁fail 1.8756481281080932e-05 -wort 1.8752692858281279e-05 -reiten 1.8748904435481624e-05 -▁Kollege 1.8748904435481624e-05 -▁Fra 1.874132758988232e-05 -▁switch 1.874132758988232e-05 -▁helping 1.8737539167082668e-05 -▁significantly 1.8737539167082668e-05 -aker 1.8733750744283016e-05 -▁Kombination 1.8733750744283016e-05 -EA 1.872996232148336e-05 -▁derzeitigen 1.872996232148336e-05 -▁privacy 1.872996232148336e-05 -ody 1.8726173898683708e-05 -ident 1.8722385475884056e-05 -▁Bedenken 1.8722385475884056e-05 -rei 1.8718597053084404e-05 -▁experiences 1.8718597053084404e-05 -▁friend 1.8718597053084404e-05 -▁Handeln 1.8714808630284749e-05 -anischen 1.8707231784685445e-05 -lt 1.8707231784685445e-05 -▁Play 1.8707231784685445e-05 -▁thr 1.8703443361885793e-05 -▁vorgenommen 1.8699654939086141e-05 -▁ergriffen 1.8695866516286485e-05 -OP 1.8692078093486833e-05 -▁Unterschied 1.8692078093486833e-05 -▁error 1.8692078093486833e-05 -▁nötig 1.8692078093486833e-05 -▁Kunde 1.8688289670687181e-05 -▁react 1.8688289670687181e-05 -iziert 1.8676924402288222e-05 -▁Verhalten 1.8676924402288222e-05 -▁43 1.867313597948857e-05 -iding 1.8665559133889266e-05 -▁befassen 1.8654193865490306e-05 -wa 1.8646617019891002e-05 -▁Angebote 1.8646617019891002e-05 -▁Berücksichtigung 1.8642828597091346e-05 -▁blood 1.8642828597091346e-05 -▁output 1.8642828597091346e-05 -▁politisch 1.8642828597091346e-05 -▁wiederum 1.8642828597091346e-05 -▁requests 1.8635251751492042e-05 -▁compliance 1.863146332869239e-05 -▁expertise 1.863146332869239e-05 -▁prot 1.8623886483093083e-05 -▁bildet 1.8620098060293431e-05 -▁gewisse 1.8620098060293431e-05 -▁cold 1.8616309637493779e-05 -▁shape 1.8616309637493779e-05 -▁competence 1.8612521214694127e-05 -▁regret 1.8612521214694127e-05 -▁Set 1.8608732791894471e-05 -▁bedrooms 1.8604944369094819e-05 -▁seien 1.8604944369094819e-05 -14 1.8597367523495515e-05 -▁presentation 1.8597367523495515e-05 -heits 1.859357910069586e-05 -ict 1.859357910069586e-05 -▁Gesetze 1.8586002255096556e-05 -▁keiner 1.8586002255096556e-05 -▁Menü 1.8578425409497252e-05 -▁exc 1.8578425409497252e-05 -▁feed 1.8578425409497252e-05 -▁streets 1.8578425409497252e-05 -▁läuft 1.8567060141098292e-05 -▁existence 1.856327171829864e-05 -▁höchste 1.856327171829864e-05 -▁speakers 1.856327171829864e-05 -▁Zahlungs 1.8559483295498985e-05 -▁Pan 1.855190644989968e-05 -▁treated 1.855190644989968e-05 -ital 1.8548118027100028e-05 -▁Eigenschaften 1.8540541181500721e-05 -▁Use 1.8540541181500721e-05 -▁lock 1.8540541181500721e-05 -▁95 1.8536752758701069e-05 -▁Vielleicht 1.8536752758701069e-05 -▁Prof 1.8532964335901417e-05 -▁Stat 1.8532964335901417e-05 -▁connect 1.8532964335901417e-05 -▁flat 1.8532964335901417e-05 -stoffen 1.8525387490302113e-05 -▁binding 1.8521599067502457e-05 -▁schließen 1.8517810644702805e-05 -▁Skype 1.8514022221903153e-05 -▁welchen 1.8506445376303846e-05 -ße 1.8502656953504194e-05 -ст 1.8502656953504194e-05 -angs 1.849508010790489e-05 -gegen 1.849508010790489e-05 -▁David 1.849508010790489e-05 -▁Antworten 1.8491291685105238e-05 -▁piece 1.8491291685105238e-05 -▁NL 1.8487503262305582e-05 -▁destination 1.8487503262305582e-05 -pression 1.848371483950593e-05 -▁websites 1.848371483950593e-05 -▁cal 1.8472349571106971e-05 -▁furniture 1.8468561148307319e-05 -LA 1.8464772725507667e-05 -▁Hy 1.8464772725507667e-05 -▁grow 1.8464772725507667e-05 -▁Op 1.8460984302708014e-05 -▁expectations 1.8460984302708014e-05 -▁konkreten 1.8457195879908362e-05 -jahr 1.8449619034309055e-05 -▁Fahrzeug 1.8449619034309055e-05 -▁Voraussetzung 1.8449619034309055e-05 -▁consistent 1.8449619034309055e-05 -▁launched 1.8449619034309055e-05 -▁Amerika 1.8445830611509403e-05 -▁drugs 1.8445830611509403e-05 -▁abhängig 1.8434465343110443e-05 -ward 1.8426888497511139e-05 -▁felt 1.8426888497511139e-05 -ienst 1.8423100074711487e-05 -▁sanctions 1.8419311651911832e-05 -▁Cont 1.841552322911218e-05 -▁Comm 1.8411734806312528e-05 -▁wild 1.8404157960713224e-05 -zial 1.8400369537913568e-05 -▁Radio 1.8400369537913568e-05 -▁eye 1.8400369537913568e-05 -▁betreffen 1.8396581115113916e-05 -▁dishes 1.8385215846714957e-05 -▁equ 1.8381427423915305e-05 -▁sand 1.8381427423915305e-05 -hy 1.8377639001115653e-05 -▁units 1.8377639001115653e-05 -una 1.8373850578316e-05 -book 1.8370062155516348e-05 -▁lose 1.8370062155516348e-05 -▁recognise 1.8370062155516348e-05 -▁Sel 1.8366273732716693e-05 -▁Airbus 1.8362485309917041e-05 -arkt 1.8358696887117389e-05 -einander 1.8343543195918777e-05 -▁browser 1.8335966350319473e-05 -▁Dokumente 1.8328389504720166e-05 -▁p.m. 1.8328389504720166e-05 -entwurf 1.8324601081920514e-05 -▁basiert 1.8324601081920514e-05 -▁compensation 1.8324601081920514e-05 -▁occur 1.8324601081920514e-05 -▁Allgemeinen 1.8320812659120862e-05 -▁Gewinn 1.8320812659120862e-05 -▁adapt 1.8320812659120862e-05 -▁obligation 1.8320812659120862e-05 -▁ser 1.8320812659120862e-05 -altung 1.831702423632121e-05 -▁conf 1.831702423632121e-05 -▁intention 1.831702423632121e-05 -isc 1.8313235813521554e-05 -UN 1.8309447390721902e-05 -▁Untersuchung 1.8309447390721902e-05 -▁genauso 1.8309447390721902e-05 -▁angemessen 1.830565896792225e-05 -preise 1.8301870545122598e-05 -▁Schäden 1.8301870545122598e-05 -karten 1.8294293699523291e-05 -▁Hinter 1.8286716853923987e-05 -▁Wirtschafts- 1.8286716853923987e-05 -oe 1.8282928431124334e-05 -▁entlang 1.8282928431124334e-05 -annte 1.8275351585525027e-05 -ati 1.8275351585525027e-05 -front 1.8275351585525027e-05 -▁Technology 1.8275351585525027e-05 -▁Lauf 1.8271563162725375e-05 -äd 1.8267774739925723e-05 -▁Plaza 1.8267774739925723e-05 -▁18. 1.8248832625927459e-05 -▁ersch 1.8248832625927459e-05 -▁fantastic 1.8248832625927459e-05 -werten 1.8245044203127804e-05 -▁relatively 1.8245044203127804e-05 -▁Kan 1.8241255780328152e-05 -▁rejected 1.8241255780328152e-05 -▁soft 1.8241255780328152e-05 -▁fit 1.82374673575285e-05 -▁represented 1.82374673575285e-05 -öst 1.8233678934728848e-05 -▁wirksam 1.8229890511929196e-05 -▁bedroom 1.822610208912954e-05 -▁constant 1.8218525243530236e-05 -▁extension 1.8218525243530236e-05 -▁Grundsätze 1.8214736820730584e-05 -▁Strukturfonds 1.8214736820730584e-05 -▁emphasis 1.8214736820730584e-05 -reiben 1.8210948397930929e-05 -ücken 1.8207159975131277e-05 -▁older 1.8207159975131277e-05 -organisation 1.8203371552331625e-05 -▁deiner 1.8203371552331625e-05 -▁fo 1.8203371552331625e-05 -▁gestern 1.8203371552331625e-05 -▁Leistungs 1.8199583129531973e-05 -▁vert 1.8199583129531973e-05 -▁6. 1.8192006283932665e-05 -▁Westen 1.8192006283932665e-05 -ungsverfahren 1.8184429438333361e-05 -▁exists 1.8184429438333361e-05 --out 1.8180641015533709e-05 -ssystem 1.8180641015533709e-05 -▁Datenbank 1.8180641015533709e-05 -▁Kooperation 1.8180641015533709e-05 -▁Kor 1.8176852592734054e-05 -▁Welcome 1.8176852592734054e-05 -▁Signal 1.81730641699344e-05 -▁Zusatz 1.81730641699344e-05 -▁helps 1.81730641699344e-05 -ausschusses 1.8169275747134749e-05 -iana 1.8169275747134749e-05 --Pl 1.8165487324335097e-05 -▁pack 1.8165487324335097e-05 -▁trotzdem 1.815791047873579e-05 -UM 1.8154122055936138e-05 -▁verz 1.8154122055936138e-05 -ray 1.8150333633136486e-05 -▁Dec 1.8146545210336834e-05 -▁proud 1.8146545210336834e-05 -▁Hin 1.8142756787537182e-05 -▁volle 1.8142756787537182e-05 -äumen 1.8138968364737526e-05 -elled 1.8131391519138222e-05 -▁65 1.8131391519138222e-05 -▁appreciate 1.8131391519138222e-05 -▁trees 1.812760309633857e-05 -▁articles 1.8123814673538915e-05 -13 1.8120026250739263e-05 -änd 1.8116237827939611e-05 -▁stations 1.8116237827939611e-05 -atische 1.8108660982340307e-05 -▁Element 1.8108660982340307e-05 -▁bezieht 1.8104872559540651e-05 -▁comply 1.8104872559540651e-05 -▁reicht 1.8101084136741e-05 -raf 1.8097295713941347e-05 -▁Dublin 1.8097295713941347e-05 -wn 1.8093507291141695e-05 -▁Acc 1.808971886834204e-05 -▁unmittelbar 1.808971886834204e-05 -▁Definition 1.8085930445542388e-05 -▁keeping 1.8085930445542388e-05 -▁wichtiges 1.8085930445542388e-05 -lichkeiten 1.8082142022742735e-05 -▁resolve 1.8082142022742735e-05 -▁senden 1.8082142022742735e-05 -▁Paper 1.8078353599943083e-05 -▁Tom 1.8078353599943083e-05 -▁King 1.8074565177143431e-05 -▁MySQL 1.8074565177143431e-05 -▁substances 1.8074565177143431e-05 -▁97 1.8070776754343776e-05 -▁Action 1.8063199908744472e-05 -▁Schulden 1.8063199908744472e-05 -▁sprach 1.8063199908744472e-05 -▁arms 1.805941148594482e-05 -mut 1.8044257794746208e-05 -zz 1.8044257794746208e-05 -▁objects 1.8044257794746208e-05 -lag 1.8040469371946556e-05 -oft 1.8040469371946556e-05 -▁44 1.8040469371946556e-05 -▁verlangen 1.8040469371946556e-05 -▁operators 1.8029104103547597e-05 -▁ziemlich 1.8029104103547597e-05 -▁Bes 1.8017738835148637e-05 -▁dinner 1.8017738835148637e-05 -ograph 1.8013950412348985e-05 -▁Aut 1.8013950412348985e-05 -▁minim 1.8006373566749681e-05 -▁hoher 1.8002585143950026e-05 -▁Ges 1.7991219875551069e-05 -▁engineering 1.7991219875551069e-05 -▁entered 1.7991219875551069e-05 -▁umfassenden 1.7987431452751417e-05 -immer 1.7983643029951762e-05 -▁Bef 1.7983643029951762e-05 -▁liefern 1.7983643029951762e-05 -oto 1.7976066184352458e-05 -▁Beifall 1.7976066184352458e-05 -▁award 1.7972277761552806e-05 -▁taxes 1.7972277761552806e-05 -▁emphasise 1.79647009159535e-05 -produkt 1.7960912493153846e-05 -▁vast 1.7960912493153846e-05 -▁Spiele 1.7957124070354194e-05 -▁erhältlich 1.7953335647554542e-05 -▁Austausch 1.7949547224754887e-05 -▁Player 1.7949547224754887e-05 -▁Sanktionen 1.7949547224754887e-05 -▁tests 1.7949547224754887e-05 -▁Est 1.7945758801955235e-05 -▁advantages 1.7945758801955235e-05 -▁wenden 1.7945758801955235e-05 -▁Dach 1.7938181956355931e-05 -cer 1.7934393533556279e-05 -▁Mand 1.7934393533556279e-05 -▁regionale 1.7926816687956971e-05 -etzen 1.7923028265157319e-05 -▁führte 1.7919239842357667e-05 -▁Weiter 1.791166299675836e-05 -▁temperature 1.791166299675836e-05 -▁Leitlinien 1.7907874573958708e-05 -▁whatever 1.7907874573958708e-05 -▁refugees 1.7904086151159056e-05 -▁reichen 1.7900297728359403e-05 -▁Küste 1.7892720882760096e-05 -▁Verbrechen 1.7892720882760096e-05 -▁verändert 1.7888932459960444e-05 -▁arm 1.7885144037160792e-05 -▁Bewegung 1.7881355614361136e-05 -flug 1.7877567191561484e-05 -▁approval 1.7877567191561484e-05 -▁fill 1.7873778768761832e-05 -▁Tur 1.786999034596218e-05 -▁Wahrheit 1.7854836654763569e-05 -▁bekannten 1.7851048231963917e-05 -▁Aktion 1.7847259809164261e-05 -▁centres 1.7847259809164261e-05 -leistungen 1.7843471386364609e-05 -▁Safety 1.7843471386364609e-05 -▁Work 1.7835894540765305e-05 -▁Krisen 1.7832106117965653e-05 -▁willing 1.7832106117965653e-05 -olf 1.7828317695165998e-05 -▁Ministers 1.7828317695165998e-05 -▁Rule 1.7824529272366346e-05 -▁sofern 1.7820740849566694e-05 -ais 1.7816952426767042e-05 -▁USD 1.7813164003967389e-05 -▁erweitert 1.7813164003967389e-05 -▁whilst 1.7813164003967389e-05 -▁geschrieben 1.7809375581167734e-05 -erein 1.7805587158368082e-05 -▁delegation 1.7805587158368082e-05 -▁har 1.7805587158368082e-05 --Fraktion 1.780179873556843e-05 -▁historische 1.780179873556843e-05 -▁Maßnahme 1.7798010312768778e-05 -inden 1.7794221889969122e-05 -▁applicable 1.7794221889969122e-05 -▁affairs 1.779043346716947e-05 -▁latter 1.7786645044369818e-05 -▁texts 1.7786645044369818e-05 -▁höhere 1.7782856621570166e-05 -▁Organ 1.7779068198770514e-05 -▁Gener 1.7775279775970859e-05 -▁cross-border 1.7767702930371555e-05 -▁happens 1.7767702930371555e-05 -19 1.7763914507571903e-05 -▁falsch 1.7763914507571903e-05 -▁GNU 1.7760126084772247e-05 -▁Landwirte 1.7760126084772247e-05 -▁Sen 1.7760126084772247e-05 -trag 1.7756337661972595e-05 -uh 1.7752549239172943e-05 -▁entertainment 1.7748760816373291e-05 -uses 1.7744972393573639e-05 -linien 1.7741183970773984e-05 -▁Monte 1.7741183970773984e-05 -▁arrangements 1.7741183970773984e-05 -▁helped 1.7741183970773984e-05 -▁naturally 1.7741183970773984e-05 -cent 1.7737395547974332e-05 -ancy 1.7726030279575376e-05 -▁Umweltschutz 1.7726030279575376e-05 -▁match 1.7726030279575376e-05 -▁personally 1.7726030279575376e-05 -75 1.772224185677572e-05 -inos 1.772224185677572e-05 -▁involving 1.772224185677572e-05 -▁italienischen 1.7718453433976068e-05 -▁util 1.7718453433976068e-05 -stehen 1.7707088165577109e-05 -▁politicians 1.7707088165577109e-05 -jo 1.7703299742777456e-05 -opp 1.7703299742777456e-05 -ously 1.7703299742777456e-05 -▁SO 1.7703299742777456e-05 -politischen 1.7699511319977804e-05 -▁defend 1.7695722897178152e-05 -▁respective 1.7695722897178152e-05 -▁finding 1.7688146051578845e-05 -▁Strategien 1.7684357628779193e-05 -stellt 1.7680569205979541e-05 -▁spring 1.7680569205979541e-05 -▁Akt 1.7676780783179889e-05 -▁Lag 1.7676780783179889e-05 -▁beträgt 1.7676780783179889e-05 -▁hair 1.7676780783179889e-05 -uter 1.7672992360380233e-05 -▁disaster 1.7672992360380233e-05 -▁dining 1.7665415514780929e-05 -▁eröffnet 1.7665415514780929e-05 -▁Show 1.7661627091981277e-05 -▁ern 1.7661627091981277e-05 -▁si 1.7657838669181625e-05 -▁valuable 1.7657838669181625e-05 -▁Verträge 1.765405024638197e-05 -▁danger 1.765405024638197e-05 -▁tours 1.765405024638197e-05 -itation 1.7650261823582318e-05 -▁Beruf 1.7650261823582318e-05 -▁ans 1.7650261823582318e-05 -▁Essen 1.7642684977983014e-05 -▁outdoor 1.7642684977983014e-05 -arte 1.7638896555183358e-05 -uelle 1.7638896555183358e-05 -▁verk 1.7638896555183358e-05 -▁Ferner 1.7635108132383706e-05 -▁Schule 1.7635108132383706e-05 -▁ann 1.7635108132383706e-05 -▁pat 1.7635108132383706e-05 -▁intend 1.7631319709584054e-05 -▁Dat 1.7627531286784402e-05 -▁exclusively 1.7627531286784402e-05 -▁peoples 1.7627531286784402e-05 -▁AN 1.762374286398475e-05 -utt 1.7619954441185095e-05 -▁Mut 1.7619954441185095e-05 -▁Strategy 1.7619954441185095e-05 -▁covers 1.7619954441185095e-05 -▁Besitz 1.7616166018385443e-05 -▁recognised 1.7608589172786138e-05 -▁museum 1.7604800749986486e-05 -onds 1.7601012327186831e-05 -▁49 1.7601012327186831e-05 -▁Agrar 1.7601012327186831e-05 -▁pain 1.7601012327186831e-05 -▁suffering 1.7601012327186831e-05 -da 1.7593435481587527e-05 -▁teilnehmen 1.7593435481587527e-05 -tend 1.7585858635988219e-05 -▁1991 1.7585858635988219e-05 -▁stone 1.7585858635988219e-05 -oun 1.7582070213188567e-05 -▁od 1.7582070213188567e-05 -punkte 1.7574493367589263e-05 -▁hostels 1.7570704944789611e-05 -▁journey 1.7566916521989956e-05 -istisch 1.7559339676390652e-05 -▁zusätzlichen 1.7559339676390652e-05 -agon 1.7555551253591e-05 -▁Swedish 1.7555551253591e-05 -▁Empfehlung 1.7551762830791344e-05 -olo 1.754418598519204e-05 -▁spot 1.754418598519204e-05 -▁1989 1.7540397562392388e-05 -▁diseases 1.7540397562392388e-05 -▁leisure 1.7540397562392388e-05 -▁self- 1.7540397562392388e-05 -▁Studenten 1.7532820716793081e-05 -▁protest 1.7532820716793081e-05 -▁charm 1.7525243871193777e-05 -▁shift 1.7525243871193777e-05 -behörden 1.7521455448394124e-05 -ook 1.7521455448394124e-05 -▁Führungs 1.7521455448394124e-05 -▁Suite 1.7521455448394124e-05 -▁entdecken 1.7521455448394124e-05 -usion 1.7517667025594472e-05 -▁weltweiten 1.7517667025594472e-05 -position 1.7513878602794817e-05 -▁künftig 1.7513878602794817e-05 -wechsel 1.7510090179995165e-05 -▁AL 1.7510090179995165e-05 -unde 1.7506301757195513e-05 -aste 1.7498724911596205e-05 -▁exceptional 1.7498724911596205e-05 -fällen 1.7494936488796553e-05 -▁creative 1.7487359643197249e-05 -oli 1.7483571220397597e-05 -▁Kenn 1.7483571220397597e-05 -▁solve 1.7483571220397597e-05 -▁erscheinen 1.747599437479829e-05 -▁mobility 1.747599437479829e-05 -▁Freund 1.7472205951998638e-05 -▁Materialien 1.746462910639933e-05 -▁Update 1.746462910639933e-05 -▁gefördert 1.7460840683599678e-05 -▁99 1.7453263838000374e-05 -▁Schweden 1.7449475415200722e-05 -▁opposed 1.7449475415200722e-05 --year 1.7438110146801763e-05 -urm 1.7438110146801763e-05 -▁Abs 1.7438110146801763e-05 -▁Badezimmer 1.7438110146801763e-05 -▁Delegation 1.7438110146801763e-05 -▁weather 1.7438110146801763e-05 -eta 1.7430533301202455e-05 -▁OF 1.7430533301202455e-05 -18 1.7415379610003847e-05 -▁kurzem 1.7415379610003847e-05 -▁gering 1.7411591187204191e-05 -▁var 1.7411591187204191e-05 -▁anti- 1.7407802764404539e-05 -äs 1.7400225918805235e-05 -▁fulfil 1.7400225918805235e-05 -eless 1.7396437496005583e-05 -reifen 1.7396437496005583e-05 -▁zugänglich 1.7396437496005583e-05 -▁confirmed 1.7392649073205928e-05 -▁intellectual 1.7385072227606624e-05 -▁Music 1.7381283804806972e-05 -▁Ambiente 1.7377495382007316e-05 -ón 1.7373706959207664e-05 -▁Az 1.7369918536408012e-05 -▁Erwartungen 1.7369918536408012e-05 -▁cas 1.7369918536408012e-05 -plätze 1.736613011360836e-05 -reise 1.7362341690808708e-05 -weit 1.7362341690808708e-05 -▁familiar 1.7362341690808708e-05 -▁Britain 1.7358553268009053e-05 -▁Kann 1.7358553268009053e-05 -▁renewable 1.73547648452094e-05 -netz 1.7350976422409749e-05 -î 1.7350976422409749e-05 -▁schönsten 1.7350976422409749e-05 -▁Access 1.7347187999610097e-05 -▁interessiert 1.7347187999610097e-05 -▁grundlegenden 1.7343399576810441e-05 -▁discovered 1.7339611154010789e-05 -▁looks 1.7339611154010789e-05 -▁Mittelpunkt 1.7335822731211137e-05 -▁voller 1.7335822731211137e-05 -ema 1.7332034308411485e-05 -ito 1.7332034308411485e-05 -▁Ohne 1.7332034308411485e-05 -aur 1.7320669040012525e-05 -äss 1.7316880617212873e-05 -▁Anfragen 1.7316880617212873e-05 -▁schwierigen 1.7316880617212873e-05 -olog 1.7313092194413221e-05 -▁Umgang 1.7313092194413221e-05 -▁Nachfrage 1.7309303771613569e-05 -▁besitzen 1.7309303771613569e-05 -▁öffnen 1.7309303771613569e-05 -▁laufen 1.7305515348813914e-05 -▁sn 1.7305515348813914e-05 -▁Eisenbahn 1.7301726926014262e-05 -▁hilft 1.7301726926014262e-05 -ieben 1.729793850321461e-05 -▁Lehr 1.729793850321461e-05 -▁eing 1.7294150080414958e-05 -▁jährlich 1.7294150080414958e-05 -▁wesentliche 1.7294150080414958e-05 -▁eigenes 1.7290361657615302e-05 -▁Studie 1.7282784812015998e-05 -esp 1.7275207966416694e-05 -▁dans 1.7275207966416694e-05 -▁obtained 1.7267631120817387e-05 -▁Kategorie 1.7256265852418427e-05 -▁Spr 1.7244900584019471e-05 -▁offices 1.7244900584019471e-05 -▁sum 1.7244900584019471e-05 -▁entstanden 1.7241112161219819e-05 -ric 1.7237323738420164e-05 -ž 1.7237323738420164e-05 -▁repeat 1.7237323738420164e-05 -▁Robert 1.7233535315620511e-05 --20 1.7229746892820859e-05 -ysteme 1.7225958470021207e-05 -▁amb 1.7225958470021207e-05 -▁faced 1.7225958470021207e-05 -grad 1.7222170047221555e-05 -▁combating 1.7222170047221555e-05 -▁Ausgangspunkt 1.72183816244219e-05 -▁Beweis 1.72183816244219e-05 -▁bedeuten 1.7210804778822596e-05 -▁listen 1.7210804778822596e-05 -▁Rock 1.7207016356022944e-05 -▁GDP 1.7203227933223288e-05 -▁directives 1.7203227933223288e-05 -▁passengers 1.7203227933223288e-05 -▁Director 1.7199439510423636e-05 -▁hervorragende 1.7199439510423636e-05 -▁Papier 1.7195651087623984e-05 -▁außen 1.7191862664824332e-05 -▁zuerst 1.7191862664824332e-05 -In 1.718807424202468e-05 -▁zweifellos 1.7184285819225025e-05 -▁Wirklichkeit 1.7180497396425373e-05 -▁largely 1.7180497396425373e-05 -gem 1.7176708973625721e-05 -▁residence 1.7172920550826069e-05 -mod 1.7169132128026413e-05 -▁7. 1.7169132128026413e-05 -▁automatic 1.7169132128026413e-05 --5 1.7161555282427109e-05 --n 1.7161555282427109e-05 -▁determine 1.7161555282427109e-05 -▁Mart 1.7157766859627457e-05 -▁prec 1.7157766859627457e-05 -▁IM 1.715019001402815e-05 -▁niemals 1.715019001402815e-05 -▁millions 1.7146401591228498e-05 -▁verf 1.7146401591228498e-05 -ani 1.7142613168428845e-05 -itive 1.7142613168428845e-05 -esser 1.7138824745629193e-05 -▁AT 1.7138824745629193e-05 -▁ausgezeichnete 1.7138824745629193e-05 -▁constructive 1.7138824745629193e-05 -▁Blut 1.7131247900029886e-05 -▁temporary 1.7131247900029886e-05 -▁08 1.7127459477230234e-05 -▁Oper 1.7127459477230234e-05 -▁Rand 1.7127459477230234e-05 -▁launch 1.7127459477230234e-05 -▁serves 1.7127459477230234e-05 -ohn 1.7123671054430582e-05 -▁fix 1.7123671054430582e-05 -▁impressive 1.7123671054430582e-05 -▁Nachrichten 1.711988263163093e-05 -▁inzwischen 1.711988263163093e-05 -▁weshalb 1.7116094208831274e-05 -к 1.7112305786031622e-05 -▁figure 1.710851736323197e-05 -urch 1.7100940517632666e-05 -▁Beratung 1.7097152094833011e-05 -▁protecting 1.7097152094833011e-05 -▁flow 1.7093363672033359e-05 -▁involvement 1.7093363672033359e-05 -▁interessant 1.7089575249233707e-05 -▁natürlichen 1.7089575249233707e-05 -MS 1.7085786826434055e-05 -gung 1.7085786826434055e-05 -▁occasion 1.70819984036344e-05 -▁Son 1.7074421558035095e-05 -erin 1.7070633135235443e-05 -▁Garden 1.7070633135235443e-05 -▁Agrarpolitik 1.7066844712435791e-05 -▁neuesten 1.7059267866836484e-05 -ually 1.7055479444036832e-05 --0 1.7051691021237179e-05 -gan 1.7051691021237179e-05 -▁Burg 1.7051691021237179e-05 -▁Kandidaten 1.7044114175637872e-05 -▁au 1.7044114175637872e-05 -▁drink 1.7021383638839956e-05 -▁OS 1.7017595216040304e-05 -▁lovely 1.7017595216040304e-05 -▁schreiben 1.7017595216040304e-05 -▁Entwicklungsländern 1.7013806793240652e-05 -▁removed 1.7010018370440997e-05 -▁couple 1.7006229947641345e-05 -▁eyes 1.7006229947641345e-05 -▁increases 1.7006229947641345e-05 -▁2.0 1.7002441524841693e-05 -▁installiert 1.7002441524841693e-05 -vis 1.6998653102042041e-05 -▁Reich 1.6998653102042041e-05 -▁в 1.6998653102042041e-05 -▁96 1.6994864679242385e-05 -▁George 1.6994864679242385e-05 -▁Second 1.6994864679242385e-05 -▁Hol 1.6991076256442733e-05 -▁leaving 1.6991076256442733e-05 -frist 1.6987287833643081e-05 -▁chain 1.6987287833643081e-05 -reisen 1.6983499410843429e-05 -▁Luxembourg 1.6979710988043777e-05 -▁healthy 1.6979710988043777e-05 -▁hinzu 1.6979710988043777e-05 -▁Instead 1.6975922565244122e-05 -▁Ladies 1.6975922565244122e-05 -▁treaty 1.6975922565244122e-05 -▁07 1.697213414244447e-05 -▁amounts 1.697213414244447e-05 -▁Beispiele 1.6968345719644818e-05 -▁newly 1.6968345719644818e-05 -gestellt 1.6964557296845166e-05 -▁Sauna 1.6964557296845166e-05 -xt 1.696076887404551e-05 -▁Presse 1.696076887404551e-05 -▁equally 1.696076887404551e-05 -▁officials 1.696076887404551e-05 -hi 1.6956980451245858e-05 -▁SV 1.6956980451245858e-05 -▁Sign 1.6956980451245858e-05 -▁ruhigen 1.6956980451245858e-05 -▁Alternative 1.6953192028446206e-05 -▁Standort 1.6953192028446206e-05 -▁Verbesserungen 1.6953192028446206e-05 -asen 1.6949403605646554e-05 -kultur 1.6949403605646554e-05 -▁Farb 1.6949403605646554e-05 -▁Vorsitz 1.6949403605646554e-05 -▁happening 1.6949403605646554e-05 -▁reden 1.6949403605646554e-05 -▁Eis 1.6934249914447942e-05 -änge 1.693046149164829e-05 -▁Men 1.6922884646048983e-05 -▁ausgezeichneten 1.6922884646048983e-05 -▁bemüht 1.6919096223249331e-05 -▁Bauern 1.6915307800449679e-05 -▁Stahl 1.6911519377650027e-05 -▁vieler 1.6907730954850371e-05 -iber 1.6900154109251067e-05 -▁seat 1.6896365686451415e-05 -▁Vision 1.6892577263651763e-05 -▁offenen 1.6888788840852108e-05 -▁reserve 1.6888788840852108e-05 -▁Bahn 1.6885000418052456e-05 -▁firstly 1.6885000418052456e-05 -besch 1.6881211995252804e-05 -▁17. 1.6881211995252804e-05 -▁Box 1.6881211995252804e-05 -▁president 1.6881211995252804e-05 -▁laden 1.6877423572453152e-05 -▁strategies 1.6877423572453152e-05 -▁hit 1.6869846726853844e-05 -▁mult 1.6866058304054192e-05 -▁gold 1.6854693035655233e-05 -▁09 1.685090461285558e-05 -legung 1.6847116190055928e-05 -▁einfachen 1.6843327767256276e-05 -▁intelligent 1.6843327767256276e-05 -▁impression 1.6839539344456621e-05 -▁insist 1.6835750921656969e-05 -▁ball 1.6828174076057665e-05 -▁criticism 1.6828174076057665e-05 -weisung 1.6824385653258013e-05 -▁Glas 1.6820597230458357e-05 -▁organizations 1.6816808807658705e-05 -iter 1.6813020384859053e-05 -pack 1.6813020384859053e-05 -▁Ka 1.6813020384859053e-05 -▁communications 1.6813020384859053e-05 -▁entscheidend 1.68092319620594e-05 -▁contributions 1.6805443539259749e-05 -▁handling 1.679407827086079e-05 -▁AS 1.6786501425261482e-05 -▁catch 1.6786501425261482e-05 -▁mandate 1.6786501425261482e-05 -▁ursprünglichen 1.6786501425261482e-05 -ungssystem 1.678271300246183e-05 -▁fühlen 1.6778924579662178e-05 -▁kurze 1.6778924579662178e-05 -wesen 1.6771347734062874e-05 -▁15. 1.6771347734062874e-05 -▁boat 1.6767559311263219e-05 -▁Croatia 1.6763770888463566e-05 -▁preparation 1.6763770888463566e-05 -cle 1.6759982465663914e-05 -▁Town 1.6759982465663914e-05 -ido 1.6756194042864262e-05 -▁universal 1.6756194042864262e-05 -▁balcony 1.6752405620064607e-05 -▁classic 1.6752405620064607e-05 -▁a. 1.6748617197264955e-05 -▁angegeben 1.6748617197264955e-05 -▁glad 1.6748617197264955e-05 -▁Normen 1.6744828774465303e-05 -▁Einstellung 1.6737251928866e-05 -▁Nein 1.6737251928866e-05 -▁discharge 1.6729675083266691e-05 -▁verändern 1.6729675083266691e-05 -▁remaining 1.6725886660467039e-05 -▁root 1.6725886660467039e-05 -16 1.6722098237667387e-05 -▁einerseits 1.671452139206808e-05 -▁gelegt 1.671452139206808e-05 -▁organisiert 1.671452139206808e-05 -▁volume 1.671452139206808e-05 -▁awareness 1.6710732969268428e-05 -▁license 1.6710732969268428e-05 -▁Nieder 1.6706944546468776e-05 -▁Überzeugung 1.6706944546468776e-05 -rab 1.6703156123669124e-05 -▁Dauer 1.6699367700869468e-05 -▁ac 1.6699367700869468e-05 -▁cell 1.6699367700869468e-05 -▁grundlegende 1.6691790855270164e-05 -▁menschlichen 1.6691790855270164e-05 -▁sorgt 1.6691790855270164e-05 -▁spanischen 1.6691790855270164e-05 -й 1.6688002432470512e-05 -▁12. 1.6688002432470512e-05 -▁requirement 1.6688002432470512e-05 -orte 1.668421400967086e-05 -▁charges 1.6680425586871205e-05 -etter 1.6676637164071553e-05 -ny 1.6676637164071553e-05 -ernen 1.66728487412719e-05 -▁Their 1.6669060318472248e-05 -▁früh 1.6665271895672593e-05 -ims 1.6661483472872941e-05 -schluss 1.6661483472872941e-05 -▁Ratsvorsitz 1.6661483472872941e-05 -ENT 1.6653906627273637e-05 -▁Jedes 1.6653906627273637e-05 -▁Kommunikations 1.6653906627273637e-05 -32 1.6650118204473985e-05 -laufen 1.6650118204473985e-05 -▁collective 1.6650118204473985e-05 -▁betreffenden 1.6646329781674329e-05 -▁prov 1.6646329781674329e-05 -▁präsentiert 1.6646329781674329e-05 -▁restrictions 1.6646329781674329e-05 -▁doesn 1.6642541358874677e-05 -▁Berufs 1.6638752936075025e-05 -▁Luc 1.6638752936075025e-05 -wards 1.6631176090475718e-05 -▁fighting 1.6631176090475718e-05 -sicherheit 1.6627387667676066e-05 -schuld 1.6623599244876414e-05 -▁abroad 1.6619810822076762e-05 -▁Brand 1.6612233976477454e-05 -▁earth 1.6612233976477454e-05 -▁Stellung 1.660465713087815e-05 -▁kulturelle 1.660465713087815e-05 -▁liberalisation 1.660465713087815e-05 -▁funktionieren 1.6597080285278846e-05 -hes 1.6593291862479191e-05 -▁camp 1.6593291862479191e-05 -▁musste 1.6593291862479191e-05 -▁KMU 1.6589503439679539e-05 -▁surroundings 1.6585715016879887e-05 -▁10. 1.6581926594080234e-05 -▁Japanese 1.6578138171280579e-05 -▁Press 1.6578138171280579e-05 -ifizierung 1.6574349748480927e-05 -▁issued 1.6570561325681275e-05 -▁stimmt 1.6570561325681275e-05 -▁suite 1.6570561325681275e-05 -▁Bucht 1.6566772902881623e-05 -▁Hof 1.6566772902881623e-05 -▁creates 1.6566772902881623e-05 -▁debates 1.6566772902881623e-05 -▁depends 1.6566772902881623e-05 -▁Rem 1.6562984480081971e-05 -▁Hostelsclub 1.6559196057282315e-05 -▁dark 1.6559196057282315e-05 -▁richten 1.6559196057282315e-05 -▁Angst 1.6555407634482663e-05 -▁ausgesprochen 1.6551619211683011e-05 -▁prove 1.6551619211683011e-05 -▁vous 1.6551619211683011e-05 -▁höchsten 1.6547830788883359e-05 -▁rapporteurs 1.6547830788883359e-05 -▁Mc 1.6544042366083704e-05 -▁operational 1.6544042366083704e-05 -▁starten 1.6544042366083704e-05 -▁achten 1.65364655204844e-05 -▁UNO 1.6532677097684748e-05 -▁Mobil 1.6528888674885096e-05 -▁Veranstaltung 1.6528888674885096e-05 -▁Et 1.652510025208544e-05 -▁Küsten 1.652510025208544e-05 -▁abgelehnt 1.652510025208544e-05 -▁breit 1.652510025208544e-05 -mitteln 1.6521311829285788e-05 -oid 1.6521311829285788e-05 -▁Termin 1.6521311829285788e-05 -▁abzu 1.6521311829285788e-05 -▁cru 1.6521311829285788e-05 -▁lift 1.6517523406486136e-05 -▁Canada 1.6513734983686484e-05 -▁trifft 1.6513734983686484e-05 -▁u.a. 1.6513734983686484e-05 -▁Bulgaria 1.6509946560886829e-05 -inge 1.6506158138087177e-05 -▁percent 1.6502369715287525e-05 -iser 1.6498581292487873e-05 -▁aufzunehmen 1.6498581292487873e-05 -▁beraten 1.6498581292487873e-05 -▁characteristics 1.6494792869688221e-05 -▁domin 1.6494792869688221e-05 -atter 1.6491004446888565e-05 -▁opinions 1.6491004446888565e-05 -▁Dusche 1.6487216024088913e-05 -▁renovated 1.6479639178489609e-05 -rate 1.64720623328903e-05 -▁album 1.64720623328903e-05 -▁lea 1.64720623328903e-05 -ystems 1.6468273910090649e-05 -▁Heimat 1.6460697064491345e-05 -▁returned 1.6460697064491345e-05 -oni 1.645690864169169e-05 -▁Executive 1.645690864169169e-05 -▁wa 1.645690864169169e-05 -itting 1.6453120218892038e-05 -uel 1.6453120218892038e-05 -▁gezogen 1.6453120218892038e-05 -oot 1.6449331796092386e-05 -▁Nahen 1.6449331796092386e-05 -▁Ausstattung 1.6445543373292734e-05 -▁dich 1.6441754950493082e-05 -▁resolutions 1.6441754950493082e-05 -atte 1.6437966527693426e-05 -▁Steuern 1.6437966527693426e-05 -▁View 1.6437966527693426e-05 -▁contracts 1.6437966527693426e-05 -▁sauna 1.6437966527693426e-05 -▁soweit 1.6437966527693426e-05 -▁Wild 1.6434178104893774e-05 -▁maintained 1.6434178104893774e-05 -▁Gentoo 1.6430389682094122e-05 -▁Geschmack 1.6430389682094122e-05 -én 1.642660125929447e-05 -▁decorated 1.642660125929447e-05 -▁größer 1.6422812836494815e-05 -iced 1.6419024413695163e-05 -minister 1.6419024413695163e-05 -▁Armen 1.6419024413695163e-05 -▁übernimmt 1.6419024413695163e-05 -aal 1.6415235990895511e-05 -▁gratulieren 1.6415235990895511e-05 -▁Marina 1.6411447568095859e-05 -▁riesigen 1.6411447568095859e-05 -▁vol 1.6411447568095859e-05 -ired 1.6407659145296207e-05 -▁identify 1.6407659145296207e-05 -ero 1.64000822996969e-05 -▁dritte 1.64000822996969e-05 -▁messages 1.64000822996969e-05 -▁Budget 1.6392505454097595e-05 -▁sexual 1.6392505454097595e-05 -▁dream 1.6388717031297943e-05 -▁strict 1.6388717031297943e-05 -▁ungefähr 1.6384928608498288e-05 -ued 1.6381140185698635e-05 -▁faith 1.6381140185698635e-05 -▁Lie 1.6377351762898983e-05 -▁walls 1.6373563340099331e-05 -▁Kap 1.6369774917299676e-05 -▁aktuell 1.6365986494500024e-05 -▁gets 1.6365986494500024e-05 -ander 1.6362198071700372e-05 -▁Strukturen 1.6362198071700372e-05 -▁brief 1.6362198071700372e-05 -▁Year 1.635840964890072e-05 -▁empf 1.635840964890072e-05 -▁umzusetzen 1.635840964890072e-05 -▁Verfahrens 1.6354621226101068e-05 -▁Wechsel 1.6354621226101068e-05 -▁fee 1.6354621226101068e-05 -▁Ul 1.634704438050176e-05 -▁Washington 1.634704438050176e-05 -iegel 1.6339467534902456e-05 -▁AM 1.63356791121028e-05 -▁youth 1.63356791121028e-05 -▁Harmonisierung 1.6331890689303149e-05 -▁effektiv 1.6331890689303149e-05 -▁hardware 1.6320525420904193e-05 -▁Hohen 1.6316736998104537e-05 -▁erlauben 1.6309160152505233e-05 -▁revolution 1.6309160152505233e-05 -▁verschiedener 1.6309160152505233e-05 -▁WLAN 1.6305371729705581e-05 -ielle 1.6301583306905929e-05 -▁Tem 1.6301583306905929e-05 -▁precise 1.6297794884106274e-05 -can 1.6294006461306621e-05 -▁traditions 1.6294006461306621e-05 -▁Tagung 1.6290218038506969e-05 -▁artist 1.6290218038506969e-05 -▁künftigen 1.6290218038506969e-05 -▁BSE 1.6286429615707317e-05 -▁cast 1.6286429615707317e-05 -amin 1.6282641192907662e-05 -▁gewonnen 1.6282641192907662e-05 -freiheit 1.627885277010801e-05 -führ 1.6275064347308358e-05 -mark 1.6275064347308358e-05 -▁cable 1.6275064347308358e-05 -▁energ 1.6271275924508706e-05 -▁verursacht 1.6267487501709054e-05 -asc 1.62636990789094e-05 -ove 1.6259910656109746e-05 -ras 1.6256122233310094e-05 -▁Thomas 1.6256122233310094e-05 -▁Bestellung 1.6252333810510442e-05 -▁Feuer 1.6252333810510442e-05 -▁durchzuführen 1.6252333810510442e-05 -▁zählt 1.6252333810510442e-05 -▁fashion 1.6248545387710787e-05 -eiten 1.6244756964911135e-05 -▁gender 1.6244756964911135e-05 -▁owner 1.6244756964911135e-05 -▁anstatt 1.6240968542111483e-05 -▁bekämpfen 1.6240968542111483e-05 -▁buchen 1.6240968542111483e-05 -reit 1.6229603273712523e-05 -▁Punkten 1.6222026428113219e-05 -ood 1.6218238005313567e-05 -uellen 1.621066115971426e-05 -▁A3 1.621066115971426e-05 -▁komp 1.621066115971426e-05 -▁occup 1.621066115971426e-05 -▁MEPs 1.6206872736914608e-05 -▁iron 1.6206872736914608e-05 -UT 1.6199295891315303e-05 -rom 1.6199295891315303e-05 -▁reich 1.6199295891315303e-05 -rahmen 1.6195507468515648e-05 -▁Übereinstimmung 1.6195507468515648e-05 -▁Zw 1.6191719045715996e-05 -ruf 1.6187930622916344e-05 -▁Kol 1.6187930622916344e-05 -erien 1.6184142200116692e-05 -liste 1.6184142200116692e-05 -▁Farbe 1.618035377731704e-05 -EF 1.616898850891808e-05 -▁involve 1.616898850891808e-05 -iesen 1.6165200086118428e-05 -▁reserves 1.6165200086118428e-05 -äh 1.6161411663318773e-05 -▁fordere 1.6161411663318773e-05 -▁verlangt 1.6161411663318773e-05 -▁zwe 1.6161411663318773e-05 -seite 1.6157623240519121e-05 -▁gelöst 1.6157623240519121e-05 -▁copyright 1.6153834817719469e-05 -▁meat 1.6146257972120165e-05 -▁parliament 1.6146257972120165e-05 -▁Life 1.6142469549320509e-05 -▁Vater 1.6142469549320509e-05 -iction 1.6138681126520857e-05 -▁MS 1.6138681126520857e-05 -▁Rumänien 1.6138681126520857e-05 -▁Professional 1.6134892703721205e-05 -▁beschränkt 1.6134892703721205e-05 -▁coll 1.6134892703721205e-05 -▁conflicts 1.6134892703721205e-05 -▁mix 1.6134892703721205e-05 -▁regularly 1.6134892703721205e-05 -kor 1.6131104280921553e-05 -▁Abschnitt 1.6127315858121898e-05 -▁hum 1.6127315858121898e-05 -▁Balkan 1.6123527435322246e-05 -▁schafft 1.6123527435322246e-05 -▁Polish 1.6119739012522594e-05 -▁east 1.6119739012522594e-05 -▁Procedure 1.6115950589722942e-05 -▁reported 1.6115950589722942e-05 -▁Host 1.6112162166923289e-05 -▁Reserv 1.6108373744123634e-05 -SS 1.6104585321323982e-05 -abr 1.6104585321323982e-05 -▁transportation 1.6104585321323982e-05 -▁64 1.610079689852433e-05 -▁Ros 1.610079689852433e-05 -▁Geräte 1.6097008475724678e-05 -▁Noch 1.6097008475724678e-05 -▁lautet 1.6097008475724678e-05 -▁visa 1.6097008475724678e-05 -▁Boot 1.6085643207325718e-05 -▁metal 1.6085643207325718e-05 -ental 1.6081854784526066e-05 -▁¤ 1.6081854784526066e-05 -▁Krankheiten 1.6078066361726414e-05 -▁Mittelmeer 1.6078066361726414e-05 -▁Änderungsanträgen 1.6070489516127107e-05 -▁mus 1.6066701093327455e-05 -▁mut 1.6066701093327455e-05 -ñ 1.6062912670527803e-05 -▁Gerichtshof 1.6062912670527803e-05 -▁Maastricht 1.6062912670527803e-05 -▁Verhandlungs 1.6059124247728151e-05 -▁battle 1.6059124247728151e-05 -▁facilitate 1.6059124247728151e-05 -▁mögliche 1.6059124247728151e-05 -▁ur 1.6059124247728151e-05 -IB 1.6055335824928495e-05 -ney 1.6055335824928495e-05 -sc 1.6055335824928495e-05 -▁Gegend 1.6055335824928495e-05 -ziele 1.6051547402128843e-05 -las 1.6047758979329191e-05 -▁Ausführungen 1.6047758979329191e-05 -▁gone 1.6047758979329191e-05 -▁auftreten 1.6043970556529539e-05 -▁schnelle 1.6043970556529539e-05 -äg 1.6040182133729884e-05 -▁Legal 1.6040182133729884e-05 -▁Übersetzung 1.6040182133729884e-05 -▁recommendation 1.6036393710930232e-05 -▁Award 1.603260528813058e-05 -▁Zuständigkeit 1.603260528813058e-05 -▁angesehen 1.603260528813058e-05 -▁drawing 1.6025028442531276e-05 -▁parents 1.6025028442531276e-05 -▁IC 1.602124001973162e-05 -▁cor 1.602124001973162e-05 -aries 1.6017451596931968e-05 -oring 1.6017451596931968e-05 -▁annehmen 1.6017451596931968e-05 -code 1.6009874751332664e-05 -har 1.6009874751332664e-05 -▁Win 1.6009874751332664e-05 -▁individuell 1.6009874751332664e-05 -▁properties 1.6009874751332664e-05 -arischen 1.6002297905733356e-05 -leit 1.6002297905733356e-05 -▁Tatsächlich 1.5998509482933704e-05 -itze 1.5994721060134052e-05 -▁depend 1.5994721060134052e-05 -▁intensive 1.5994721060134052e-05 -unds 1.59909326373344e-05 -▁Deutsch 1.59909326373344e-05 -▁northern 1.59909326373344e-05 -▁sem 1.5987144214534745e-05 -▁hundreds 1.5983355791735093e-05 -▁customs 1.5979567368935441e-05 -▁submit 1.5979567368935441e-05 -▁Cons 1.5975778946135789e-05 -▁topic 1.5971990523336137e-05 -ared 1.5968202100536481e-05 -arf 1.5968202100536481e-05 -▁breite 1.5968202100536481e-05 -▁conducted 1.5968202100536481e-05 -bad 1.5964413677736829e-05 -▁Festlegung 1.5964413677736829e-05 -ritt 1.5960625254937177e-05 -86 1.5956836832137525e-05 -odes 1.5956836832137525e-05 -spar 1.5956836832137525e-05 -▁Gestaltung 1.5956836832137525e-05 -▁dates 1.5956836832137525e-05 -▁wissenschaftlichen 1.5956836832137525e-05 -▁Anti 1.595304840933787e-05 -▁avec 1.595304840933787e-05 -▁pred 1.595304840933787e-05 --Reg 1.5949259986538218e-05 -uber 1.5949259986538218e-05 -▁Vorschlägen 1.5949259986538218e-05 -▁Congress 1.5945471563738566e-05 -▁Garantie 1.5945471563738566e-05 -▁Problems 1.5945471563738566e-05 -▁Regen 1.5945471563738566e-05 -▁zentralen 1.5945471563738566e-05 -▁sehe 1.5941683140938914e-05 -▁somewhat 1.5941683140938914e-05 -▁Earth 1.5937894718139262e-05 -side 1.5934106295339606e-05 -vorschriften 1.5934106295339606e-05 -▁Lu 1.5930317872539954e-05 -▁PRO 1.5930317872539954e-05 -▁erheblich 1.5930317872539954e-05 -▁mitten 1.5926529449740302e-05 -standards 1.592274102694065e-05 -ungsver 1.5915164181341343e-05 -▁Block 1.5915164181341343e-05 -▁aufgefordert 1.5915164181341343e-05 -igend 1.591137575854169e-05 -rats 1.591137575854169e-05 -▁ausgeschlossen 1.591137575854169e-05 -▁ausgezeichnet 1.591137575854169e-05 -▁fac 1.5903798912942386e-05 -▁bezug 1.5900010490142731e-05 -▁worse 1.5900010490142731e-05 -lass 1.5896222067343079e-05 -▁Aktien 1.5892433644543427e-05 -version 1.5888645221743775e-05 -▁Beitritts 1.5888645221743775e-05 -ucher 1.5884856798944123e-05 -▁Fa 1.5884856798944123e-05 -▁Kilometer 1.5884856798944123e-05 -▁einig 1.5884856798944123e-05 -▁Drittländern 1.5877279953344815e-05 -▁ste 1.5877279953344815e-05 -▁Fax 1.5869703107745511e-05 -▁Reis 1.5869703107745511e-05 -▁Stud 1.5869703107745511e-05 -▁terrorist 1.5869703107745511e-05 -▁vacation 1.5869703107745511e-05 -oma 1.5865914684945856e-05 -▁displayed 1.5865914684945856e-05 -▁hervorheben 1.5865914684945856e-05 -▁kurzen 1.5862126262146204e-05 -▁Ideal 1.5858337839346552e-05 -▁DA 1.58545494165469e-05 -▁Miet 1.58545494165469e-05 --Programm 1.5846972570947592e-05 -fach 1.5846972570947592e-05 -üss 1.5846972570947592e-05 -▁beglückwünschen 1.5846972570947592e-05 -▁pros 1.5846972570947592e-05 -produkte 1.584318414814794e-05 -▁depending 1.584318414814794e-05 -räume 1.5835607302548636e-05 -▁Einheit 1.5835607302548636e-05 -▁define 1.5835607302548636e-05 -▁Disk 1.5831818879748981e-05 -▁faster 1.5831818879748981e-05 -▁parliaments 1.5831818879748981e-05 -back 1.5828030456949329e-05 -▁performed 1.5828030456949329e-05 -▁career 1.5824242034149676e-05 -▁interior 1.5824242034149676e-05 -▁holding 1.5820453611350024e-05 -▁regulatory 1.5820453611350024e-05 -ban 1.5816665188550372e-05 -lei 1.5812876765750717e-05 -▁Sach 1.5809088342951065e-05 -▁zugunsten 1.5809088342951065e-05 -▁updated 1.5805299920151413e-05 -▁ir 1.5801511497351761e-05 -▁joined 1.5797723074552105e-05 -pool 1.5793934651752453e-05 -▁decision-making 1.5793934651752453e-05 -ätzen 1.57901462289528e-05 -▁Food 1.57901462289528e-05 -uell 1.5786357806153149e-05 -zeugen 1.5782569383353497e-05 -▁expert 1.5782569383353497e-05 -▁Cyprus 1.5778780960553842e-05 -erc 1.577499253775419e-05 -ivity 1.577499253775419e-05 -ragen 1.577499253775419e-05 -stück 1.577499253775419e-05 -▁Ganz 1.577499253775419e-05 -St 1.5771204114954538e-05 -▁Fähigkeiten 1.5771204114954538e-05 -▁nachhaltigen 1.5771204114954538e-05 -▁Ref 1.5767415692154886e-05 -▁vo 1.5767415692154886e-05 -▁towns 1.5763627269355234e-05 -▁frequently 1.5759838846555578e-05 -▁dead 1.5756050423755926e-05 -▁seeing 1.5756050423755926e-05 -▁zufolge 1.5756050423755926e-05 -IV 1.5752262000956274e-05 -▁Check 1.5752262000956274e-05 -▁Entwicklungsländer 1.5752262000956274e-05 -▁Wird 1.5752262000956274e-05 -▁connections 1.5752262000956274e-05 -▁Sonne 1.5748473578156622e-05 --200 1.5740896732557315e-05 --Ar 1.5740896732557315e-05 -lungen 1.5740896732557315e-05 -▁Umfeld 1.5740896732557315e-05 -▁settings 1.5740896732557315e-05 -▁Hostels 1.573331988695801e-05 -▁answers 1.573331988695801e-05 -▁deals 1.573331988695801e-05 -▁durchführen 1.573331988695801e-05 -▁stylish 1.573331988695801e-05 -ingly 1.5729531464158358e-05 -▁Fair 1.5729531464158358e-05 -▁Hilfs 1.5729531464158358e-05 -▁driver 1.5729531464158358e-05 -▁früheren 1.5729531464158358e-05 -▁verbringen 1.5729531464158358e-05 -chte 1.5725743041358703e-05 -▁Def 1.5725743041358703e-05 -▁Max 1.5725743041358703e-05 -▁Regime 1.5725743041358703e-05 -▁Modern 1.5721954618559051e-05 -▁sodass 1.57181661957594e-05 -▁minority 1.5714377772959747e-05 -ress 1.5710589350160091e-05 -▁emergency 1.5706800927360439e-05 -nehmer 1.5703012504560787e-05 -▁16. 1.5703012504560787e-05 -▁SMEs 1.5703012504560787e-05 -▁zuständigen 1.5703012504560787e-05 -nungs 1.5699224081761135e-05 -▁Detail 1.5699224081761135e-05 -rach 1.5695435658961483e-05 -▁Bro 1.5691647236161828e-05 -order 1.5687858813362176e-05 -▁Alles 1.5687858813362176e-05 -▁Ratspräsidentschaft 1.5687858813362176e-05 -▁islands 1.5687858813362176e-05 -▁mostly 1.5687858813362176e-05 -atischen 1.5684070390562524e-05 -▁Ausstellung 1.5684070390562524e-05 -fra 1.5680281967762872e-05 -upp 1.5680281967762872e-05 -uted 1.567649354496322e-05 -▁ought 1.567649354496322e-05 -finden 1.5672705122163564e-05 -teilen 1.5672705122163564e-05 -Г 1.5672705122163564e-05 -▁02 1.5672705122163564e-05 -▁Road 1.5672705122163564e-05 -▁Gleichgewicht 1.5661339853764608e-05 -з 1.5657551430964953e-05 -▁verabschiedet 1.5657551430964953e-05 -▁Kontext 1.5649974585365649e-05 -asting 1.5646186162565997e-05 -▁Protection 1.5646186162565997e-05 -▁Taiwan 1.5646186162565997e-05 -▁mother 1.5646186162565997e-05 -OW 1.5642397739766344e-05 -▁follows 1.5642397739766344e-05 -▁bestätigen 1.5634820894167037e-05 -▁cash 1.5634820894167037e-05 -▁ultimately 1.5634820894167037e-05 -ektion 1.5631032471367385e-05 -▁Mail 1.5631032471367385e-05 -▁continent 1.5631032471367385e-05 -▁noted 1.5631032471367385e-05 -▁techniques 1.5631032471367385e-05 -▁Ident 1.5627244048567733e-05 -▁Zentralbank 1.5627244048567733e-05 -▁label 1.5604513511769814e-05 -▁proof 1.5604513511769814e-05 -▁Websites 1.5600725088970162e-05 -▁windows 1.5600725088970162e-05 -▁bottom 1.559693666617051e-05 -▁demanding 1.559693666617051e-05 -▁div 1.5593148243370858e-05 -▁Speicher 1.5589359820571202e-05 -geber 1.558557139777155e-05 -▁Monetary 1.558557139777155e-05 -▁d.h. 1.558557139777155e-05 -nahmen 1.5581782974971898e-05 -ena 1.5574206129372594e-05 -umb 1.5574206129372594e-05 -▁Arbeitsplätzen 1.5574206129372594e-05 -▁Erk 1.5574206129372594e-05 -▁Pin 1.5574206129372594e-05 -▁ra 1.5574206129372594e-05 -▁Rund 1.5570417706572939e-05 -17 1.5566629283773287e-05 -▁Nachricht 1.5566629283773287e-05 -▁hostel 1.5566629283773287e-05 -▁ethnic 1.5562840860973635e-05 -jährigen 1.5559052438173983e-05 -▁notwendige 1.5559052438173983e-05 -▁organized 1.5559052438173983e-05 -▁Ziffer 1.5555264015374331e-05 -▁visual 1.5555264015374331e-05 -olit 1.5547687169775023e-05 -▁drug 1.5543898746975371e-05 -▁grateful 1.5543898746975371e-05 -▁46 1.5540110324175719e-05 -▁Read 1.5540110324175719e-05 -▁Übereinkommen 1.5540110324175719e-05 -▁ll 1.5532533478576411e-05 -acher 1.5528745055776759e-05 -▁8. 1.5528745055776759e-05 -▁Wass 1.5528745055776759e-05 -▁electricity 1.5528745055776759e-05 -▁meant 1.5528745055776759e-05 -itzen 1.5524956632977107e-05 -▁inner 1.5524956632977107e-05 -aged 1.5521168210177455e-05 -▁Rel 1.5521168210177455e-05 -▁Drittens 1.55173797873778e-05 -▁Gewicht 1.5513591364578148e-05 -▁Ausführung 1.5509802941778496e-05 -▁Plenum 1.5509802941778496e-05 -▁Arbeitsplatz 1.5506014518978844e-05 -▁MB 1.5506014518978844e-05 -▁interessante 1.5498437673379536e-05 -▁invite 1.5498437673379536e-05 -▁Hot 1.5494649250579884e-05 -▁Sony 1.5494649250579884e-05 -haben 1.548707240498058e-05 -hne 1.548707240498058e-05 -▁Prä 1.548707240498058e-05 -▁castle 1.548707240498058e-05 -▁Ersatz 1.5483283982180925e-05 -▁öffentlich 1.5483283982180925e-05 -▁Deutschen 1.5479495559381273e-05 -amil 1.5475707136581621e-05 -▁Schlafzimmer 1.5475707136581621e-05 -▁divided 1.5475707136581621e-05 -▁Tool 1.5471918713781969e-05 -iano 1.5468130290982317e-05 -▁bezahlen 1.5468130290982317e-05 -▁einzigartige 1.5464341868182661e-05 -▁Redner 1.5460553445383009e-05 -▁produziert 1.5460553445383009e-05 -▁quarter 1.5460553445383009e-05 -inner 1.5445399754184398e-05 -uction 1.5445399754184398e-05 -▁delighted 1.5445399754184398e-05 -▁hängt 1.5445399754184398e-05 -▁library 1.5445399754184398e-05 -▁suites 1.5445399754184398e-05 -▁derartige 1.5441611331384745e-05 -▁mat 1.5441611331384745e-05 -weis 1.5437822908585093e-05 -▁Förder 1.5437822908585093e-05 -aking 1.5426457640186134e-05 -▁intensiv 1.541888079458683e-05 -▁PS 1.5415092371787174e-05 -▁Training 1.540751552618787e-05 -▁relaxation 1.540751552618787e-05 -ý 1.5399938680588566e-05 -▁Eing 1.5399938680588566e-05 -▁James 1.5399938680588566e-05 -▁serviert 1.5399938680588566e-05 -â 1.5396150257788911e-05 -▁statistics 1.5396150257788911e-05 -▁trend 1.5396150257788911e-05 -ever 1.5392361834989259e-05 -▁Anbieter 1.5388573412189607e-05 -▁Buchung 1.5388573412189607e-05 -▁gewissen 1.5388573412189607e-05 -▁Mat 1.5373419720990995e-05 -▁arrive 1.5373419720990995e-05 -▁contrast 1.5369631298191343e-05 -▁Whether 1.5365842875391691e-05 -▁contacts 1.5362054452592036e-05 -▁visible 1.5362054452592036e-05 -▁Verbot 1.5358266029792384e-05 -▁wines 1.5358266029792384e-05 -▁Erholung 1.5354477606992731e-05 -ature 1.5350689184193079e-05 -▁EM 1.5346900761393427e-05 -▁producing 1.5346900761393427e-05 -▁180 1.5343112338593772e-05 -▁Regierungskonferenz 1.5343112338593772e-05 -▁verhindert 1.5343112338593772e-05 -▁expansion 1.533932391579412e-05 -▁succeed 1.533932391579412e-05 -▁ticket 1.533932391579412e-05 -bit 1.5335535492994468e-05 -▁Mexico 1.5335535492994468e-05 -▁dre 1.5331747070194816e-05 -▁widely 1.5331747070194816e-05 -nach 1.5324170224595508e-05 -▁Budapest 1.5324170224595508e-05 -▁Schuld 1.5324170224595508e-05 -▁poll 1.5324170224595508e-05 -ality 1.5320381801795856e-05 -maschine 1.5320381801795856e-05 -▁SC 1.5320381801795856e-05 -▁Partnern 1.5316593378996204e-05 -▁Techn 1.5316593378996204e-05 -▁attend 1.5316593378996204e-05 -▁geprüft 1.5316593378996204e-05 -▁SA 1.5312804956196552e-05 -▁courage 1.5312804956196552e-05 -emb 1.5309016533396897e-05 -gut 1.5309016533396897e-05 -▁Prov 1.5309016533396897e-05 -▁channels 1.5309016533396897e-05 -▁entwickelten 1.5309016533396897e-05 -▁investors 1.5309016533396897e-05 -▁heritage 1.5305228110597245e-05 -▁locations 1.5305228110597245e-05 -fahr 1.5301439687797593e-05 -▁migration 1.5301439687797593e-05 -▁welfare 1.5301439687797593e-05 -▁Videos 1.5297651264997941e-05 -▁tourists 1.5297651264997941e-05 -▁verleihen 1.5293862842198285e-05 -schafts 1.5290074419398633e-05 -▁bedeut 1.5290074419398633e-05 -▁verstanden 1.5290074419398633e-05 -blick 1.5286285996598981e-05 -▁Prinzipien 1.5286285996598981e-05 -▁Yes 1.5282497573799329e-05 -▁entrance 1.5282497573799329e-05 -qual 1.5278709150999677e-05 -▁Europol 1.5278709150999677e-05 -▁stars 1.5278709150999677e-05 -mo 1.5274920728200022e-05 -uls 1.5274920728200022e-05 -west 1.5274920728200022e-05 -▁Planung 1.5274920728200022e-05 -▁Qual 1.5274920728200022e-05 -▁Verringerung 1.527113230540037e-05 -▁Zwei 1.5263555459801065e-05 -▁valley 1.5263555459801065e-05 -▁Kreuz 1.5259767037001413e-05 -▁erreichbar 1.5259767037001413e-05 -▁ausgesetzt 1.525597861420176e-05 -▁divers 1.5248401768602454e-05 -▁starts 1.5248401768602454e-05 -▁konkret 1.52446133458028e-05 -▁pollution 1.52446133458028e-05 -▁Adobe 1.5237036500203494e-05 -▁Ausgabe 1.5233248077403842e-05 -▁aufnehmen 1.522945965460419e-05 -▁weak 1.522945965460419e-05 -▁Prague 1.5225671231804537e-05 -▁pieces 1.5225671231804537e-05 -ación 1.5221882809004885e-05 -▁heat 1.5221882809004885e-05 -▁Pop 1.5218094386205231e-05 -▁index 1.5218094386205231e-05 -▁planet 1.5218094386205231e-05 -key 1.5206729117806273e-05 -wald 1.5206729117806273e-05 -▁exciting 1.5206729117806273e-05 -▁involves 1.5206729117806273e-05 -▁tea 1.5206729117806273e-05 -wohn 1.5202940695006621e-05 -▁Besonders 1.5202940695006621e-05 -▁inhabitants 1.5202940695006621e-05 -▁unmöglich 1.5202940695006621e-05 -▁Ankunft 1.5199152272206967e-05 -▁stored 1.5199152272206967e-05 -ni 1.5195363849407315e-05 -▁Funds 1.5195363849407315e-05 -▁Geschäfte 1.5195363849407315e-05 -▁abgestimmt 1.5195363849407315e-05 -▁careful 1.5195363849407315e-05 -zwe 1.5191575426607661e-05 -▁direkten 1.5191575426607661e-05 -▁'ve 1.5187787003808009e-05 -▁cool 1.5187787003808009e-05 -ario 1.5180210158208704e-05 -ikan 1.5180210158208704e-05 -▁declaration 1.5180210158208704e-05 -▁Lab 1.517642173540905e-05 -▁Nahrungsmittel 1.517642173540905e-05 -iro 1.5172633312609398e-05 -▁bestimmen 1.5172633312609398e-05 -▁entsteht 1.5172633312609398e-05 -▁flights 1.5172633312609398e-05 -▁organ 1.5172633312609398e-05 -▁passiert 1.5168844889809746e-05 -▁presents 1.5168844889809746e-05 -▁afternoon 1.5165056467010092e-05 -▁subsidies 1.5165056467010092e-05 -▁visiting 1.5153691198611134e-05 -▁welchem 1.5153691198611134e-05 -▁Verteidigung 1.5146114353011828e-05 -▁cosy 1.5146114353011828e-05 -▁pers 1.5146114353011828e-05 -▁explore 1.5142325930212176e-05 -gef 1.5138537507412523e-05 -▁IV 1.5138537507412523e-05 -▁drivers 1.5138537507412523e-05 -▁suffer 1.5134749084612871e-05 -altet 1.5130960661813217e-05 -▁Portuguese 1.5127172239013565e-05 -▁Shop 1.5127172239013565e-05 -▁input 1.5127172239013565e-05 -▁remote 1.5127172239013565e-05 -▁showing 1.5127172239013565e-05 -▁Char 1.5123383816213911e-05 -▁Framework 1.5119595393414259e-05 -lösung 1.5115806970614605e-05 -▁Faktoren 1.5115806970614605e-05 -▁Kamera 1.5115806970614605e-05 -▁gardens 1.5112018547814953e-05 -▁oral 1.5112018547814953e-05 -▁Führer 1.5108230125015301e-05 -▁Haftung 1.5108230125015301e-05 -▁Bemerkungen 1.5104441702215647e-05 -▁Bildungs 1.5100653279415995e-05 -▁definiert 1.5100653279415995e-05 -▁virtual 1.5100653279415995e-05 -▁micro 1.5096864856616342e-05 -▁03 1.509307643381669e-05 -▁Audio 1.5089288011017036e-05 -▁Beiträge 1.5089288011017036e-05 -▁Up 1.5089288011017036e-05 -▁bilateral 1.5089288011017036e-05 -▁proved 1.5089288011017036e-05 -izer 1.5085499588217384e-05 -▁indicated 1.5085499588217384e-05 -gesetz 1.5081711165417732e-05 -▁bound 1.5081711165417732e-05 -▁Brazil 1.5077922742618078e-05 -▁Gerichte 1.5077922742618078e-05 -▁Straf 1.5074134319818426e-05 -▁kämpfen 1.5074134319818426e-05 -▁legt 1.5074134319818426e-05 -▁wollten 1.5074134319818426e-05 -fähig 1.5070345897018772e-05 -▁Copenhagen 1.5070345897018772e-05 -▁Wünsche 1.5070345897018772e-05 -▁cutting 1.5062769051419466e-05 -▁visited 1.5062769051419466e-05 -ican 1.5058980628619814e-05 -▁sorgfältig 1.5058980628619814e-05 -▁understood 1.5058980628619814e-05 -▁Format 1.5055192205820161e-05 -▁Schweizer 1.5055192205820161e-05 -▁collaboration 1.5055192205820161e-05 -▁mil 1.5055192205820161e-05 -▁vollständige 1.5055192205820161e-05 -ückt 1.5051403783020509e-05 -▁Blue 1.5051403783020509e-05 -▁focused 1.5051403783020509e-05 -iellen 1.5047615360220857e-05 -▁Freiheiten 1.5047615360220857e-05 -▁maintaining 1.5047615360220857e-05 -▁Zwe 1.5043826937421203e-05 -▁Beziehung 1.5040038514621551e-05 -▁wann 1.5040038514621551e-05 -▁westlichen 1.5040038514621551e-05 -▁Baltic 1.5036250091821897e-05 -▁preis 1.5032461669022245e-05 -ems 1.5028673246222591e-05 -▁Vietnam 1.5028673246222591e-05 -▁echten 1.5028673246222591e-05 -ч 1.5024884823422939e-05 -▁Einbeziehung 1.5024884823422939e-05 -▁Neue 1.5024884823422939e-05 -▁Fleisch 1.5021096400623287e-05 -▁demonstrated 1.5021096400623287e-05 -▁prim 1.5021096400623287e-05 -▁Deswegen 1.5017307977823633e-05 -▁Konsens 1.5017307977823633e-05 -▁ep 1.5017307977823633e-05 -▁fitness 1.5017307977823633e-05 -▁geboren 1.5013519555023981e-05 -▁Friday 1.5009731132224328e-05 -▁accident 1.5009731132224328e-05 -▁dur 1.5005942709424676e-05 -▁provider 1.5005942709424676e-05 -lern 1.5002154286625022e-05 -▁Verw 1.5002154286625022e-05 -▁demonstrate 1.5002154286625022e-05 -▁owners 1.5002154286625022e-05 -▁ausgeführt 1.499836586382537e-05 -▁staatlichen 1.499836586382537e-05 -ey 1.4994577441025718e-05 -systeme 1.4994577441025718e-05 -▁heating 1.4994577441025718e-05 -▁Fernseh 1.4990789018226064e-05 -▁Jede 1.4990789018226064e-05 -▁begrüßt 1.4990789018226064e-05 -▁Ball 1.4987000595426412e-05 -▁Verbindungen 1.4987000595426412e-05 -häuser 1.4983212172626758e-05 -▁Pact 1.4983212172626758e-05 -▁erleichtern 1.4983212172626758e-05 -▁inflation 1.4983212172626758e-05 -ande 1.4979423749827106e-05 -▁Hostel 1.4979423749827106e-05 -▁Nos 1.4975635327027453e-05 -ähr 1.49718469042278e-05 -▁Games 1.4968058481428147e-05 -▁Question 1.4968058481428147e-05 -▁Vorschlags 1.4968058481428147e-05 -▁signs 1.4968058481428147e-05 -abil 1.4964270058628495e-05 -▁carbon 1.4964270058628495e-05 -▁kostet 1.4964270058628495e-05 -▁geltenden 1.4960481635828843e-05 -▁hinzufügen 1.4960481635828843e-05 -▁transition 1.4960481635828843e-05 -▁lounge 1.4956693213029189e-05 -▁beschrieben 1.4952904790229537e-05 -mas 1.4949116367429883e-05 -▁Kompetenz 1.4945327944630231e-05 -halb 1.4941539521830577e-05 -wo 1.4941539521830577e-05 -▁13. 1.4941539521830577e-05 -▁Asien 1.4941539521830577e-05 -▁höchst 1.4941539521830577e-05 -▁nah 1.4941539521830577e-05 -bek 1.4937751099030925e-05 -ösen 1.4937751099030925e-05 -▁Werbung 1.4937751099030925e-05 -▁department 1.4937751099030925e-05 -atis 1.4933962676231273e-05 -linie 1.4933962676231273e-05 -pa 1.4933962676231273e-05 -werks 1.4922597407832314e-05 -▁chair 1.4922597407832314e-05 -▁spoken 1.4922597407832314e-05 -▁höheren 1.4915020562233008e-05 -▁Polizei 1.4911232139433356e-05 -▁Golden 1.4907443716633702e-05 -▁unw 1.4907443716633702e-05 -▁Orte 1.490365529383405e-05 -▁Fun 1.4899866871034398e-05 -▁gezwungen 1.4899866871034398e-05 -▁finished 1.4896078448234744e-05 -▁Zypern 1.4892290025435092e-05 -▁Sorgen 1.4888501602635439e-05 -kop 1.4880924757036133e-05 -▁straight 1.4880924757036133e-05 -alle 1.4877136334236481e-05 -anner 1.4877136334236481e-05 -ava 1.4877136334236481e-05 -▁Ungarn 1.4877136334236481e-05 -▁gespeichert 1.4877136334236481e-05 -▁Gemeinde 1.4873347911436829e-05 -▁Balkon 1.4869559488637175e-05 -fallen 1.4865771065837523e-05 -▁Projekten 1.4865771065837523e-05 -▁privat 1.4865771065837523e-05 -ologischen 1.4861982643037869e-05 -ätz 1.4861982643037869e-05 -▁Gründung 1.4861982643037869e-05 -▁Ware 1.4861982643037869e-05 -▁accompanied 1.4861982643037869e-05 -▁raw 1.4858194220238217e-05 -▁Canon 1.4854405797438563e-05 -▁Che 1.4854405797438563e-05 -▁Dass 1.4854405797438563e-05 -▁pet 1.4854405797438563e-05 -▁solange 1.4854405797438563e-05 -▁theme 1.4854405797438563e-05 -▁circ 1.4850617374638911e-05 -▁2, 1.4846828951839259e-05 -▁Automat 1.4843040529039606e-05 -▁Image 1.4843040529039606e-05 -▁printing 1.4843040529039606e-05 -asser 1.4839252106239953e-05 -▁Putin 1.4839252106239953e-05 -ikt 1.48354636834403e-05 -▁facility 1.48354636834403e-05 -▁fra 1.48354636834403e-05 -ringen 1.4831675260640648e-05 -▁Edition 1.4827886837840994e-05 -▁bele 1.4827886837840994e-05 -▁spät 1.4827886837840994e-05 -ector 1.4820309992241688e-05 -ierend 1.4820309992241688e-05 -▁BIP 1.4820309992241688e-05 -▁gebaut 1.4820309992241688e-05 -ancing 1.4816521569442036e-05 -▁bur 1.4816521569442036e-05 -▁chinesischen 1.4816521569442036e-05 -▁ak 1.4812733146642384e-05 -▁high-quality 1.4812733146642384e-05 -▁nahezu 1.4812733146642384e-05 -osten 1.480894472384273e-05 -▁categories 1.480894472384273e-05 -▁verehrte 1.480894472384273e-05 -med 1.4801367878243425e-05 -elte 1.4797579455443773e-05 -uft 1.4797579455443773e-05 -ausschuß 1.4793791032644119e-05 -set 1.4793791032644119e-05 -▁Vereinbarungen 1.4793791032644119e-05 -▁sad 1.4793791032644119e-05 -ießen 1.4790002609844467e-05 -▁gek 1.4790002609844467e-05 -kehr 1.4786214187044815e-05 -▁char 1.4786214187044815e-05 -▁Betracht 1.4778637341445509e-05 -▁spectacular 1.4778637341445509e-05 -▁Symbol 1.4774848918645855e-05 -put 1.4771060495846203e-05 -▁Project 1.4771060495846203e-05 -▁Uns 1.4771060495846203e-05 -▁settlement 1.4771060495846203e-05 -▁verringern 1.4771060495846203e-05 -▁Rein 1.4767272073046549e-05 -▁2012 1.4763483650246897e-05 -▁Kyoto 1.4763483650246897e-05 -keit 1.4759695227447244e-05 -▁Webseiten 1.4759695227447244e-05 -▁Energ 1.4755906804647592e-05 -▁controlled 1.4755906804647592e-05 -▁9. 1.475211838184794e-05 -▁CO 1.475211838184794e-05 -▁Karl 1.475211838184794e-05 -▁exceed 1.475211838184794e-05 -ave 1.4748329959048286e-05 -▁angemessene 1.4748329959048286e-05 -▁Univers 1.4744541536248634e-05 -▁ferner 1.4744541536248634e-05 -▁ongoing 1.4744541536248634e-05 -▁plenty 1.4744541536248634e-05 -▁rational 1.4744541536248634e-05 -▁Dir 1.474075311344898e-05 -onic 1.4736964690649328e-05 -▁Perhaps 1.4736964690649328e-05 -▁conc 1.4736964690649328e-05 -▁fertig 1.4736964690649328e-05 -▁vorlegen 1.4736964690649328e-05 -ois 1.4733176267849674e-05 -▁Autor 1.4733176267849674e-05 -▁Sar 1.4733176267849674e-05 -▁bemühen 1.4733176267849674e-05 -▁documentation 1.4733176267849674e-05 -▁Anw 1.472559942225037e-05 -▁spr 1.472559942225037e-05 -▁Einwohner 1.4721810999450716e-05 -▁Interess 1.4721810999450716e-05 -▁heutige 1.4721810999450716e-05 -▁labor 1.4721810999450716e-05 -▁exception 1.4718022576651064e-05 -iol 1.4714234153851411e-05 -▁era 1.4714234153851411e-05 -wel 1.4710445731051759e-05 -▁Lö 1.4710445731051759e-05 -ford 1.4702868885452453e-05 -▁enc 1.4702868885452453e-05 -▁safeguard 1.4702868885452453e-05 -▁Fluss 1.4699080462652799e-05 -▁boot 1.4695292039853147e-05 -▁erinnert 1.4695292039853147e-05 -▁personnel 1.4695292039853147e-05 -▁effectiveness 1.4691503617053495e-05 -▁Mobilität 1.4687715194253841e-05 -yer 1.4683926771454189e-05 -halte 1.4680138348654535e-05 -▁Gesellschaften 1.4680138348654535e-05 -▁recon 1.4680138348654535e-05 -▁Fel 1.4676349925854883e-05 -vention 1.467256150305523e-05 -▁definitely 1.467256150305523e-05 -spartner 1.4668773080255578e-05 -otten 1.4661196234656272e-05 -▁Bars 1.465740781185662e-05 -▁ke 1.465740781185662e-05 --und 1.4653619389056966e-05 -▁Network 1.4653619389056966e-05 -▁concluded 1.4653619389056966e-05 -▁integriert 1.4653619389056966e-05 -▁ve 1.4653619389056966e-05 -PE 1.4649830966257314e-05 -▁citizen 1.4649830966257314e-05 -▁committees 1.4638465697858356e-05 -tritt 1.4634677275058702e-05 -▁m2 1.4634677275058702e-05 -▁tickets 1.4634677275058702e-05 -▁Jun 1.463088885225905e-05 -▁syn 1.463088885225905e-05 -▁Argument 1.4627100429459397e-05 -▁Hö 1.4627100429459397e-05 -▁47 1.4623312006659745e-05 -▁Page 1.4623312006659745e-05 -setz 1.4619523583860091e-05 -▁Aktiv 1.4615735161060439e-05 -▁Außenpolitik 1.4615735161060439e-05 -▁Provinz 1.4615735161060439e-05 -▁awarded 1.4615735161060439e-05 -▁educational 1.4615735161060439e-05 -ibe 1.4611946738260785e-05 -▁Veröffentlichung 1.4611946738260785e-05 -▁belong 1.4611946738260785e-05 -▁Viertel 1.4608158315461133e-05 -▁replaced 1.4608158315461133e-05 -▁resistance 1.4604369892661481e-05 -▁Michel 1.4600581469861827e-05 -▁Abst 1.4593004624262521e-05 -▁Ac 1.4589216201462869e-05 -▁Anschluss 1.4589216201462869e-05 -▁umwelt 1.4589216201462869e-05 -▁übrigens 1.4589216201462869e-05 -ending 1.4585427778663216e-05 -▁Roll 1.4585427778663216e-05 -▁Verlust 1.4585427778663216e-05 -aria 1.4581639355863564e-05 -ll 1.4581639355863564e-05 -▁Mill 1.4581639355863564e-05 -▁foundation 1.4581639355863564e-05 -▁Gerät 1.4577850933063912e-05 -▁Gespräche 1.4574062510264258e-05 -▁firms 1.4574062510264258e-05 -▁jegliche 1.4574062510264258e-05 -▁spezifischen 1.4574062510264258e-05 --Pr 1.4570274087464606e-05 -ilit 1.4570274087464606e-05 -▁Amendments 1.4570274087464606e-05 -▁Identität 1.4570274087464606e-05 -▁passenger 1.4570274087464606e-05 -▁possibly 1.4570274087464606e-05 -▁Israeli 1.45626972418653e-05 -▁Chancen 1.4558908819065646e-05 -▁Sollten 1.4558908819065646e-05 -▁behaviour 1.4558908819065646e-05 -▁organis 1.4558908819065646e-05 -▁termin 1.4551331973466341e-05 -▁minorities 1.4547543550666688e-05 -▁bestehende 1.4543755127867036e-05 -▁06 1.4539966705067383e-05 -▁count 1.4532389859468077e-05 -▁occasions 1.4532389859468077e-05 -▁phrase 1.4528601436668425e-05 -▁sugar 1.4528601436668425e-05 -▁Browser 1.4524813013868771e-05 -osa 1.4521024591069119e-05 -▁Pla 1.4521024591069119e-05 -▁Vorgehen 1.4521024591069119e-05 -▁verdient 1.4521024591069119e-05 -ography 1.4517236168269467e-05 -▁700 1.4517236168269467e-05 -▁Northern 1.4513447745469813e-05 -▁corruption 1.4513447745469813e-05 -▁recorded 1.4513447745469813e-05 -will 1.4509659322670161e-05 -▁anzu 1.4509659322670161e-05 -▁geograph 1.4509659322670161e-05 -anne 1.4505870899870508e-05 -asten 1.4505870899870508e-05 --8 1.4502082477070855e-05 -▁crimes 1.4502082477070855e-05 -▁boost 1.4498294054271202e-05 -▁popul 1.4498294054271202e-05 -▁speaker 1.4498294054271202e-05 -▁forum 1.449450563147155e-05 -▁profile 1.449450563147155e-05 -▁rapidly 1.449450563147155e-05 -▁Applause 1.4490717208671896e-05 -▁Women 1.4486928785872244e-05 -▁prem 1.4483140363072592e-05 -▁configuration 1.4479351940272938e-05 -▁Description 1.4475563517473286e-05 -▁usual 1.4471775094673632e-05 -▁verg 1.4471775094673632e-05 -▁arch 1.446798667187398e-05 -▁load 1.446798667187398e-05 -▁sil 1.446798667187398e-05 -▁Belgium 1.4464198249074327e-05 -▁trafficking 1.4464198249074327e-05 -instrument 1.4460409826274675e-05 -▁Ther 1.4460409826274675e-05 -▁Viel 1.4460409826274675e-05 -wege 1.4456621403475022e-05 -▁Alexander 1.4456621403475022e-05 -▁manufacturer 1.4456621403475022e-05 -sb 1.4452832980675369e-05 -istan 1.4449044557875717e-05 -▁Cast 1.4449044557875717e-05 -▁Tibet 1.4445256135076063e-05 -▁letztlich 1.4445256135076063e-05 -▁tackle 1.4445256135076063e-05 -▁85 1.4441467712276411e-05 -▁cycle 1.4433890866677105e-05 -▁grundsätzlich 1.4433890866677105e-05 -etic 1.44263140210778e-05 -▁compatible 1.44263140210778e-05 -▁Indian 1.4422525598278147e-05 -▁behalten 1.4422525598278147e-05 -licht 1.4418737175478494e-05 -▁einfacher 1.4414948752678842e-05 -▁ident 1.4414948752678842e-05 -▁pre- 1.4414948752678842e-05 -▁gallery 1.4411160329879188e-05 -▁liber 1.4411160329879188e-05 -▁territorial 1.4411160329879188e-05 -▁verurteilt 1.4411160329879188e-05 -▁1980 1.4407371907079536e-05 -▁Cam 1.4407371907079536e-05 -▁Sollte 1.4407371907079536e-05 -▁competent 1.4403583484279882e-05 -▁exit 1.4403583484279882e-05 -▁neun 1.439979506148023e-05 -▁woman 1.439979506148023e-05 -kräfte 1.4396006638680578e-05 -▁yes 1.4396006638680578e-05 -▁Soviet 1.4392218215880924e-05 -95 1.4388429793081272e-05 -▁assume 1.4388429793081272e-05 -▁Mach 1.4384641370281618e-05 -▁Email 1.4380852947481966e-05 -▁Insbesondere 1.4380852947481966e-05 -▁Pers 1.4380852947481966e-05 -▁berechtigt 1.4380852947481966e-05 -▁herr 1.4380852947481966e-05 -▁proven 1.4380852947481966e-05 -▁immigrants 1.4377064524682313e-05 -▁Desk 1.4373276101882661e-05 -▁Rezeption 1.4373276101882661e-05 -▁desired 1.4373276101882661e-05 -▁assets 1.4365699256283355e-05 --Projekt 1.4361910833483703e-05 -▁erf 1.4361910833483703e-05 -urlaub 1.4354333987884397e-05 -▁Selbstverständlich 1.4354333987884397e-05 -akes 1.4350545565084743e-05 -▁Besorgnis 1.4350545565084743e-05 -▁honour 1.4350545565084743e-05 -beziehungen 1.4346757142285091e-05 -▁Bord 1.4346757142285091e-05 -▁Dienstleistungs 1.4346757142285091e-05 -▁Oder 1.4346757142285091e-05 -▁Wi-Fi 1.4346757142285091e-05 -stands 1.4342968719485437e-05 -▁Respekt 1.4342968719485437e-05 -▁Werkzeug 1.4342968719485437e-05 -▁overcome 1.4342968719485437e-05 -▁respected 1.4342968719485437e-05 -TO 1.4339180296685785e-05 -last 1.4339180296685785e-05 -üb 1.4339180296685785e-05 -▁HTML 1.4335391873886133e-05 -▁gewährt 1.4335391873886133e-05 -▁routes 1.433160345108648e-05 -▁0, 1.4327815028286828e-05 -▁Büro 1.4327815028286828e-05 -▁Ann 1.4324026605487174e-05 -▁Arm 1.4324026605487174e-05 -▁constitution 1.4324026605487174e-05 -▁Mangel 1.4320238182687522e-05 -▁containing 1.4320238182687522e-05 -▁nights 1.4320238182687522e-05 -▁verbinden 1.4320238182687522e-05 -fläche 1.4316449759887868e-05 -▁Autobahn 1.4316449759887868e-05 -▁nunmehr 1.4316449759887868e-05 -▁Us 1.4312661337088216e-05 -ы 1.4308872914288564e-05 -▁Optionen 1.4308872914288564e-05 -▁Untersuchungen 1.4308872914288564e-05 -▁scheme 1.4308872914288564e-05 -▁Gefahren 1.430508449148891e-05 -▁Phil 1.430508449148891e-05 -▁TO 1.430508449148891e-05 -▁Versand 1.430508449148891e-05 -▁letzter 1.430508449148891e-05 -wahl 1.4301296068689258e-05 -▁52 1.4301296068689258e-05 -▁Apart 1.4301296068689258e-05 -▁Einwanderung 1.4301296068689258e-05 -ckt 1.4297507645889604e-05 -▁einheitlichen 1.4297507645889604e-05 -▁films 1.4297507645889604e-05 -ständ 1.4293719223089952e-05 -▁ersetzt 1.4293719223089952e-05 -▁Preisen 1.4289930800290299e-05 -▁Verbreitung 1.4286142377490647e-05 -▁organisieren 1.4286142377490647e-05 -▁pla 1.4286142377490647e-05 -unch 1.4282353954690995e-05 -▁BMW 1.4282353954690995e-05 -▁Salzburg 1.4282353954690995e-05 -▁Single 1.4282353954690995e-05 -▁rechtlichen 1.4282353954690995e-05 -achs 1.4278565531891341e-05 -organisationen 1.4278565531891341e-05 -▁Given 1.4278565531891341e-05 -porte 1.4274777109091689e-05 -scht 1.4274777109091689e-05 -▁Marktes 1.4270988686292035e-05 -▁alcohol 1.4270988686292035e-05 -▁correctly 1.4270988686292035e-05 -▁Rob 1.4267200263492383e-05 -▁beschäftigen 1.4267200263492383e-05 -▁installieren 1.4267200263492383e-05 -uv 1.4263411840692729e-05 -▁replace 1.4263411840692729e-05 -▁Lager 1.4259623417893077e-05 -▁credibility 1.4259623417893077e-05 -▁entschlossen 1.4259623417893077e-05 -iot 1.4255834995093423e-05 -▁Komponenten 1.4255834995093423e-05 -▁enjoyed 1.4255834995093423e-05 -▁partly 1.4255834995093423e-05 -eds 1.4252046572293771e-05 -rochen 1.4252046572293771e-05 -▁Pen 1.4252046572293771e-05 -▁Tit 1.4252046572293771e-05 -▁Var 1.4248258149494119e-05 -ju 1.4244469726694466e-05 -▁Kapitel 1.4244469726694466e-05 -▁facts 1.4244469726694466e-05 -▁sit 1.4244469726694466e-05 -ı 1.4240681303894814e-05 -▁Energien 1.4240681303894814e-05 -▁concert 1.4240681303894814e-05 -▁elect 1.4240681303894814e-05 -▁Fehl 1.423689288109516e-05 -▁Zielen 1.423689288109516e-05 -▁imposed 1.423689288109516e-05 -▁pract 1.423689288109516e-05 -▁gespielt 1.4233104458295508e-05 -▁individuelle 1.4233104458295508e-05 -▁sending 1.4233104458295508e-05 -aktion 1.4229316035495854e-05 -▁constitutional 1.4229316035495854e-05 -genommen 1.4225527612696202e-05 -irs 1.4225527612696202e-05 -▁direkte 1.4225527612696202e-05 -zentren 1.422173918989655e-05 -▁Nachbar 1.422173918989655e-05 -▁att 1.422173918989655e-05 -▁centuries 1.422173918989655e-05 -▁gestaltet 1.422173918989655e-05 -▁luxurious 1.422173918989655e-05 -ück 1.4217950767096896e-05 -▁corresponding 1.4217950767096896e-05 -bekämpfung 1.4214162344297244e-05 -▁Zwecke 1.4214162344297244e-05 -▁erfolgreiche 1.4214162344297244e-05 -▁manager 1.4214162344297244e-05 -eto 1.421037392149759e-05 -problem 1.421037392149759e-05 -▁transactions 1.421037392149759e-05 -▁vorhandenen 1.421037392149759e-05 -▁vorstellen 1.4206585498697938e-05 -▁Malta 1.4202797075898285e-05 -▁arrived 1.4202797075898285e-05 -vertrag 1.4199008653098633e-05 -▁Bu 1.4199008653098633e-05 -▁subsidiarity 1.4199008653098633e-05 -▁appointed 1.4195220230298979e-05 -hips 1.4191431807499327e-05 -▁lobby 1.4191431807499327e-05 -▁pop 1.4191431807499327e-05 -ín 1.4187643384699675e-05 -▁§ 1.4187643384699675e-05 -▁Hungary 1.4183854961900021e-05 -▁SAP 1.4183854961900021e-05 -▁conditioning 1.4183854961900021e-05 -▁ice 1.4183854961900021e-05 -▁weekend 1.4183854961900021e-05 -▁Tre 1.4180066539100369e-05 -▁professionals 1.4172489693501063e-05 -▁Puerto 1.4168701270701409e-05 -▁stronger 1.4168701270701409e-05 -▁14. 1.4164912847901757e-05 -▁Alpen 1.4164912847901757e-05 -▁SD 1.4164912847901757e-05 -▁emerging 1.4164912847901757e-05 -но 1.4161124425102105e-05 -dert 1.4157336002302452e-05 -▁CF 1.4157336002302452e-05 -▁Egypt 1.4157336002302452e-05 -.D 1.41535475795028e-05 -▁Syn 1.41535475795028e-05 -▁marquis 1.41535475795028e-05 -zeichnet 1.4149759156703146e-05 -▁Vel 1.414218231110384e-05 -▁entscheidende 1.414218231110384e-05 -▁individuellen 1.414218231110384e-05 -▁unfair 1.414218231110384e-05 -anzen 1.4138393888304188e-05 -▁Ski 1.4138393888304188e-05 -▁Sche 1.4134605465504534e-05 -▁VAT 1.4134605465504534e-05 -▁Within 1.4134605465504534e-05 -▁aud 1.4134605465504534e-05 -▁maritime 1.4134605465504534e-05 -▁minister 1.4134605465504534e-05 -▁adults 1.4130817042704882e-05 -▁uniform 1.4130817042704882e-05 -play 1.4123240197105576e-05 -▁distributed 1.4123240197105576e-05 -▁inklusive 1.4123240197105576e-05 -▁Gaza 1.4119451774305924e-05 -▁Politiken 1.4119451774305924e-05 -UNG 1.4115663351506271e-05 -ility 1.4115663351506271e-05 -▁Fitness 1.4115663351506271e-05 -gegangen 1.4111874928706619e-05 -▁Kräfte 1.4111874928706619e-05 -▁Lim 1.4111874928706619e-05 -▁jenen 1.4111874928706619e-05 -itors 1.4108086505906965e-05 -▁Ban 1.4108086505906965e-05 -▁Following 1.4108086505906965e-05 -▁functional 1.4108086505906965e-05 -asst 1.4104298083107313e-05 -▁2013 1.4104298083107313e-05 -▁beruht 1.4104298083107313e-05 -▁lots 1.4104298083107313e-05 -▁Kirk 1.4100509660307661e-05 -▁Met 1.4100509660307661e-05 -▁Tip 1.4100509660307661e-05 -▁außerordentlich 1.4100509660307661e-05 -▁satisfied 1.4100509660307661e-05 -▁fuer 1.4096721237508007e-05 -▁verst 1.4092932814708355e-05 -▁Event 1.4089144391908701e-05 -stre 1.4085355969109049e-05 -▁Serie 1.4085355969109049e-05 -eral 1.4077779123509743e-05 -▁Vorstellung 1.4077779123509743e-05 -▁entsp 1.4077779123509743e-05 -▁Beh 1.4073990700710091e-05 -▁dürfte 1.4073990700710091e-05 -▁fir 1.4073990700710091e-05 -▁ze 1.4073990700710091e-05 -▁declared 1.4070202277910438e-05 -▁functioning 1.4070202277910438e-05 -ни 1.4066413855110786e-05 -▁ursprünglich 1.4066413855110786e-05 -▁britische 1.4062625432311132e-05 -▁destruction 1.4062625432311132e-05 -anst 1.405883700951148e-05 -▁adopting 1.405883700951148e-05 -▁Staff 1.4055048586711826e-05 -frei 1.4051260163912174e-05 -ommen 1.4051260163912174e-05 -▁Bemerkung 1.4051260163912174e-05 -▁unterschiedlich 1.4051260163912174e-05 -▁Freunden 1.404747174111252e-05 -▁Client 1.4043683318312868e-05 -beh 1.4039894895513216e-05 -▁Spaß 1.4039894895513216e-05 -▁Ho 1.4036106472713563e-05 -▁judicial 1.4036106472713563e-05 -▁unverzüglich 1.403231804991391e-05 -imal 1.4028529627114257e-05 -▁Kab 1.4028529627114257e-05 -▁Structural 1.4028529627114257e-05 -▁195 1.4024741204314605e-05 -▁Institut 1.4024741204314605e-05 -▁integr 1.4020952781514951e-05 -▁magnificent 1.4017164358715299e-05 -uro 1.4013375935915647e-05 -▁slightly 1.4009587513115993e-05 -▁Pra 1.4005799090316341e-05 -▁Wil 1.4005799090316341e-05 -▁col 1.4005799090316341e-05 -pos 1.4002010667516687e-05 -▁Water 1.4002010667516687e-05 -▁aussprechen 1.4002010667516687e-05 -▁died 1.3994433821917382e-05 -▁globalisation 1.3994433821917382e-05 -▁rental 1.399064539911773e-05 -▁· 1.399064539911773e-05 -etting 1.3986856976318076e-05 -▁Städten 1.3986856976318076e-05 -▁reduzieren 1.3986856976318076e-05 -▁ruhig 1.3986856976318076e-05 -▁shares 1.3986856976318076e-05 -▁simpl 1.3979280130718772e-05 -zusch 1.3975491707919118e-05 -▁Kom 1.3975491707919118e-05 -▁attract 1.3975491707919118e-05 -▁nu 1.3975491707919118e-05 -zig 1.3971703285119466e-05 -▁stehenden 1.3971703285119466e-05 -▁bright 1.3967914862319812e-05 -▁prakt 1.3967914862319812e-05 -▁fehlt 1.396412643952016e-05 -ziel 1.3960338016720506e-05 -▁Werke 1.3960338016720506e-05 -▁zusammenarbeiten 1.3960338016720506e-05 -echnik 1.3956549593920854e-05 -▁Dre 1.3956549593920854e-05 -▁Kons 1.3956549593920854e-05 -▁courts 1.3952761171121202e-05 -▁inst 1.3952761171121202e-05 -▁schlägt 1.3948972748321549e-05 -▁histor 1.3945184325521897e-05 -einrichtungen 1.3941395902722243e-05 -emen 1.3941395902722243e-05 -▁verstärken 1.3941395902722243e-05 -▁suppliers 1.3937607479922591e-05 -▁Ausmaß 1.3933819057122937e-05 -▁Sav 1.3933819057122937e-05 -▁grant 1.3933819057122937e-05 -▁angegebenen 1.3930030634323285e-05 -▁Hal 1.3926242211523633e-05 -▁Mitarbeitern 1.3926242211523633e-05 -▁eastern 1.3926242211523633e-05 -▁Betrag 1.3922453788723979e-05 -ield 1.3918665365924327e-05 -versorgung 1.3918665365924327e-05 -mus 1.3914876943124673e-05 -rans 1.3914876943124673e-05 -▁Auge 1.3914876943124673e-05 -▁Schönheit 1.3914876943124673e-05 -▁chemical 1.3914876943124673e-05 -▁extraordinary 1.3914876943124673e-05 -▁ließ 1.3914876943124673e-05 -▁milk 1.3914876943124673e-05 -▁eingereicht 1.3911088520325021e-05 -▁miles 1.3911088520325021e-05 -▁verkaufen 1.3911088520325021e-05 -▁Erzeugnisse 1.3907300097525368e-05 -▁kamen 1.3903511674725716e-05 -▁klaren 1.3892146406326758e-05 -ae 1.3888357983527104e-05 -ь 1.3888357983527104e-05 -▁Verlauf 1.3888357983527104e-05 -▁Kir 1.3884569560727452e-05 -▁drop 1.3884569560727452e-05 -leistung 1.3876992715128146e-05 -▁sing 1.3876992715128146e-05 -sam 1.3873204292328492e-05 -▁Kopenhagen 1.3873204292328492e-05 -vi 1.386941586952884e-05 -▁Who 1.3861839023929535e-05 -35 1.3858050601129883e-05 -▁addresses 1.3858050601129883e-05 -▁lists 1.3858050601129883e-05 -▁considering 1.3854262178330229e-05 -▁disp 1.3854262178330229e-05 -▁taxation 1.3854262178330229e-05 -▁Hamas 1.3850473755530577e-05 -▁plays 1.3850473755530577e-05 -olution 1.3846685332730923e-05 -▁Sunday 1.3842896909931271e-05 -▁Wohlstand 1.3842896909931271e-05 -▁domain 1.3842896909931271e-05 -DA 1.3839108487131617e-05 -▁treat 1.3839108487131617e-05 -65 1.3835320064331965e-05 -▁Aktions 1.3835320064331965e-05 -▁consent 1.3835320064331965e-05 -▁identified 1.3835320064331965e-05 -▁Mobile 1.3827743218732659e-05 -▁TH 1.3827743218732659e-05 -▁encouraging 1.3827743218732659e-05 -▁proposes 1.3827743218732659e-05 -eilen 1.3823954795933007e-05 -▁Abkommens 1.3816377950333702e-05 -▁cent 1.3816377950333702e-05 -rieben 1.3812589527534048e-05 -▁Gesetzes 1.3812589527534048e-05 -▁preparing 1.3812589527534048e-05 -prozesses 1.3808801104734396e-05 -▁Balkans 1.3808801104734396e-05 -▁formed 1.3805012681934744e-05 -▁russischen 1.3805012681934744e-05 -▁Gericht 1.3797435836335438e-05 -▁hundred 1.3797435836335438e-05 -▁wiederholen 1.3797435836335438e-05 -light 1.3793647413535784e-05 -▁Stimmen 1.3793647413535784e-05 -bach 1.3789858990736132e-05 -▁Bea 1.3786070567936478e-05 -▁Sektoren 1.3786070567936478e-05 -▁honest 1.3786070567936478e-05 -▁plenary 1.3786070567936478e-05 -▁pal 1.3782282145136826e-05 --to 1.3778493722337173e-05 -▁substance 1.3778493722337173e-05 -osion 1.3774705299537521e-05 -▁providers 1.3774705299537521e-05 -geräte 1.3767128453938215e-05 -▁Aqu 1.3763340031138563e-05 -▁Wetter 1.3763340031138563e-05 -lau 1.3759551608338909e-05 -standing 1.3759551608338909e-05 -regeln 1.3755763185539257e-05 -▁Newsletter 1.3755763185539257e-05 -▁Charles 1.3751974762739603e-05 -▁mini 1.3751974762739603e-05 -▁abs 1.3748186339939951e-05 -▁ergänzen 1.3748186339939951e-05 -▁resolved 1.3748186339939951e-05 -▁suit 1.3748186339939951e-05 -▁ensures 1.37443979171403e-05 -schließen 1.3736821071540993e-05 -wendung 1.3736821071540993e-05 -▁Parkplatz 1.3736821071540993e-05 -▁didn 1.3736821071540993e-05 -▁erhielt 1.3736821071540993e-05 -ede 1.373303264874134e-05 -▁Without 1.373303264874134e-05 -▁sensible 1.373303264874134e-05 -fort 1.3729244225941688e-05 -ially 1.3729244225941688e-05 -▁Ga 1.3729244225941688e-05 -▁jener 1.3729244225941688e-05 -▁vern 1.3729244225941688e-05 -▁äußern 1.3725455803142034e-05 -immt 1.371787895754273e-05 -▁Verd 1.3714090534743076e-05 -▁erfolgreichen 1.3710302111943424e-05 -▁Thirdly 1.3702725266344118e-05 -kurs 1.3698936843544464e-05 -paket 1.3698936843544464e-05 -▁Männern 1.3695148420744812e-05 -▁everyday 1.3695148420744812e-05 -▁vorbereitet 1.3695148420744812e-05 -▁Investment 1.3691359997945159e-05 -▁Programmen 1.3691359997945159e-05 -▁hol 1.3687571575145507e-05 -▁pur 1.3687571575145507e-05 -▁roads 1.3687571575145507e-05 -ikanische 1.3683783152345855e-05 -▁Charta 1.3683783152345855e-05 -▁Exchange 1.3683783152345855e-05 -▁Tochter 1.3683783152345855e-05 -▁erhebliche 1.3683783152345855e-05 -▁Kontrollen 1.3679994729546201e-05 -▁vermitteln 1.3679994729546201e-05 -▁army 1.3676206306746549e-05 -informationen 1.3672417883946895e-05 -▁Massen 1.3672417883946895e-05 -▁artistic 1.3672417883946895e-05 -yt 1.3668629461147243e-05 -▁Schulen 1.3668629461147243e-05 -▁U-Bahn 1.3668629461147243e-05 -▁drinks 1.3668629461147243e-05 -atik 1.3664841038347589e-05 -▁Bereitstellung 1.3664841038347589e-05 -▁actively 1.3664841038347589e-05 -▁western 1.3664841038347589e-05 -ört 1.3661052615547937e-05 -IX 1.3657264192748285e-05 -▁Güter 1.3657264192748285e-05 -▁Schlussfolgerungen 1.3653475769948631e-05 -▁component 1.3653475769948631e-05 -▁Schn 1.3645898924349326e-05 -▁bestimmter 1.3645898924349326e-05 -icherung 1.3642110501549674e-05 -▁Lieferung 1.3642110501549674e-05 -▁Milch 1.3642110501549674e-05 -▁Satz 1.3642110501549674e-05 -▁Zusätzlich 1.3642110501549674e-05 -▁Fundamental 1.363832207875002e-05 -▁vieles 1.363832207875002e-05 -kes 1.3634533655950368e-05 -nd 1.3634533655950368e-05 -▁pe 1.3634533655950368e-05 -▁updates 1.3634533655950368e-05 -mit 1.3626956810351062e-05 -▁Bek 1.3626956810351062e-05 -hre 1.362316838755141e-05 -itionen 1.362316838755141e-05 -ires 1.3619379964751756e-05 -hood 1.3615591541952104e-05 -▁с 1.3615591541952104e-05 -PC 1.3611803119152451e-05 -bene 1.3611803119152451e-05 -icted 1.3611803119152451e-05 -ра 1.3611803119152451e-05 -▁Traum 1.3611803119152451e-05 -▁Valley 1.3611803119152451e-05 -▁eventually 1.3611803119152451e-05 -▁thousand 1.3611803119152451e-05 -▁Gegenstand 1.3608014696352798e-05 -▁empfangen 1.3608014696352798e-05 -▁inclusion 1.3608014696352798e-05 -▁nahm 1.3608014696352798e-05 -▁Cup 1.3604226273553145e-05 -▁assure 1.3604226273553145e-05 -▁deposit 1.3600437850753493e-05 -führt 1.3596649427953841e-05 -▁Assembly 1.3596649427953841e-05 -ivität 1.3592861005154187e-05 -▁adjust 1.3592861005154187e-05 -▁enhance 1.3592861005154187e-05 -dienste 1.3589072582354535e-05 -▁birth 1.3589072582354535e-05 -▁division 1.3589072582354535e-05 -▁strategischen 1.3589072582354535e-05 -▁Add 1.3585284159554881e-05 -gesellschaften 1.3577707313955575e-05 -▁Apple 1.3577707313955575e-05 -▁Chinas 1.3577707313955575e-05 -▁Einstellungen 1.3577707313955575e-05 -▁beigetragen 1.3577707313955575e-05 -eak 1.357013046835627e-05 -other 1.357013046835627e-05 -▁voran 1.3566342045556618e-05 -▁Krankheit 1.3558765199957312e-05 -▁topics 1.3558765199957312e-05 -sprache 1.355497677715766e-05 -ßen 1.355497677715766e-05 -▁proportion 1.3547399931558354e-05 -▁traditionelle 1.3547399931558354e-05 -▁Verbraucherschutz 1.3536034663159396e-05 -▁Genehmigung 1.3532246240359742e-05 -ologische 1.352845781756009e-05 -▁Ram 1.352845781756009e-05 -▁concepts 1.352845781756009e-05 -formen 1.3524669394760437e-05 -ès 1.3524669394760437e-05 -▁Sudan 1.3524669394760437e-05 -▁upper 1.3524669394760437e-05 -law 1.3520880971960785e-05 -werken 1.3517092549161131e-05 -▁Content 1.3517092549161131e-05 -▁musical 1.3517092549161131e-05 -betrieb 1.3513304126361479e-05 -nern 1.3513304126361479e-05 -▁Sens 1.3513304126361479e-05 -▁arr 1.3513304126361479e-05 -▁euros 1.3513304126361479e-05 -stud 1.3509515703561827e-05 -maschinen 1.3505727280762173e-05 -▁eingehalten 1.3505727280762173e-05 -▁decisive 1.3501938857962521e-05 -▁elsewhere 1.3501938857962521e-05 -▁Kabel 1.3498150435162867e-05 -▁Vide 1.3498150435162867e-05 -▁amerikanische 1.3498150435162867e-05 -▁EF 1.3494362012363215e-05 -▁bek 1.3494362012363215e-05 -▁university 1.3494362012363215e-05 -igent 1.3490573589563561e-05 -ques 1.3490573589563561e-05 -▁Next 1.3490573589563561e-05 -▁duties 1.3490573589563561e-05 -kn 1.3486785166763909e-05 -α 1.3486785166763909e-05 -▁CH 1.3486785166763909e-05 -▁Import 1.3486785166763909e-05 -▁großem 1.3486785166763909e-05 -▁toler 1.3486785166763909e-05 -▁TR 1.3482996743964256e-05 -▁kleines 1.3482996743964256e-05 -▁Thursday 1.3479208321164604e-05 -▁Vill 1.3479208321164604e-05 -▁supplies 1.3479208321164604e-05 -▁Rechtsgrundlage 1.3475419898364952e-05 -▁auseinander 1.3475419898364952e-05 -▁bea 1.3475419898364952e-05 -▁spoke 1.3475419898364952e-05 -▁wunderschönen 1.3475419898364952e-05 -deut 1.3467843052765646e-05 -urz 1.3467843052765646e-05 -б 1.3467843052765646e-05 -п 1.3467843052765646e-05 -▁langsam 1.3467843052765646e-05 -▁lokale 1.3467843052765646e-05 -▁ACP 1.346026620716634e-05 -rank 1.3456477784366686e-05 -ifies 1.3452689361567034e-05 -▁Kong 1.3452689361567034e-05 -▁anwesend 1.3452689361567034e-05 --minute 1.3448900938767382e-05 -each 1.3448900938767382e-05 -enter 1.3448900938767382e-05 -ugh 1.3448900938767382e-05 -▁Bedrohung 1.3445112515967728e-05 -▁Professor 1.3445112515967728e-05 -▁Schne 1.3445112515967728e-05 -▁beteiligen 1.3445112515967728e-05 -▁Gest 1.3441324093168076e-05 -▁Inf 1.3441324093168076e-05 -▁deficit 1.3441324093168076e-05 -▁gewiss 1.3437535670368423e-05 -▁pick 1.3437535670368423e-05 -▁Mini 1.3433747247568771e-05 -▁om 1.3433747247568771e-05 -▁vulnerable 1.3433747247568771e-05 -▁Kongress 1.3429958824769117e-05 -▁hart 1.3429958824769117e-05 -▁Klim 1.3422381979169811e-05 -▁erzeugt 1.3422381979169811e-05 -▁proposing 1.3422381979169811e-05 -▁subjects 1.3418593556370159e-05 -endo 1.3411016710770853e-05 -▁Gem 1.3411016710770853e-05 -▁Zus 1.3411016710770853e-05 -▁applying 1.3411016710770853e-05 -oche 1.3407228287971201e-05 -▁Atom 1.3407228287971201e-05 -gemeinschaft 1.3403439865171547e-05 -lung 1.3403439865171547e-05 -▁Pay 1.3403439865171547e-05 -▁absence 1.3403439865171547e-05 -▁aktivieren 1.3403439865171547e-05 -▁handle 1.3403439865171547e-05 -▁Among 1.3399651442371895e-05 -▁Weiß 1.3399651442371895e-05 -MA 1.3395863019572242e-05 -omin 1.3395863019572242e-05 -ories 1.3395863019572242e-05 -▁lessons 1.3395863019572242e-05 -▁solely 1.3395863019572242e-05 -▁stolz 1.3395863019572242e-05 -▁einzigartigen 1.339207459677259e-05 -airs 1.3388286173972938e-05 -reiche 1.3388286173972938e-05 -▁bislang 1.3388286173972938e-05 -apt 1.3384497751173284e-05 -▁Extrem 1.3384497751173284e-05 -▁packaging 1.3384497751173284e-05 -▁theory 1.3384497751173284e-05 -▁Flugzeug 1.3380709328373632e-05 -▁beobachten 1.3376920905573978e-05 -▁countryside 1.3376920905573978e-05 -▁nevertheless 1.3376920905573978e-05 -acc 1.3373132482774326e-05 -▁Fußball 1.3373132482774326e-05 -▁generated 1.3373132482774326e-05 -▁lit 1.3373132482774326e-05 -▁shock 1.3373132482774326e-05 -▁voluntary 1.3373132482774326e-05 -ski 1.336555563717502e-05 -.net 1.3361767214375368e-05 -affen 1.3361767214375368e-05 -ennung 1.3361767214375368e-05 -▁Federation 1.3361767214375368e-05 -▁elektronischen 1.3361767214375368e-05 -▁Amt 1.3354190368776062e-05 -▁fourth 1.3354190368776062e-05 -gebiete 1.3350401945976409e-05 -▁carrying 1.3350401945976409e-05 -▁Erstellung 1.3346613523176757e-05 -▁Pil 1.3346613523176757e-05 -▁Ren 1.3346613523176757e-05 -▁Like 1.3342825100377103e-05 -▁alarm 1.3342825100377103e-05 -▁Gerichts 1.3339036677577451e-05 -iern 1.3335248254777797e-05 -▁shipping 1.3335248254777797e-05 -▁tobacco 1.3335248254777797e-05 -aya 1.3331459831978145e-05 -elli 1.3331459831978145e-05 -▁suggestions 1.3331459831978145e-05 -▁appeared 1.3327671409178493e-05 -▁info 1.3327671409178493e-05 -astic 1.3323882986378839e-05 -astr 1.3323882986378839e-05 -▁Kaffee 1.3323882986378839e-05 -▁disk 1.3320094563579187e-05 -geschäft 1.3316306140779533e-05 -▁Sicherung 1.3316306140779533e-05 -▁legitimate 1.3316306140779533e-05 -▁Baum 1.3312517717979881e-05 -▁Castle 1.3312517717979881e-05 -▁attach 1.3312517717979881e-05 -▁entscheidender 1.3312517717979881e-05 -▁Holiday 1.3308729295180228e-05 -▁pure 1.3308729295180228e-05 -▁villages 1.3308729295180228e-05 -oz 1.3304940872380576e-05 -▁Glaubwürdigkeit 1.3304940872380576e-05 -▁OR 1.3304940872380576e-05 -▁komme 1.3304940872380576e-05 -▁Os 1.3301152449580924e-05 -▁description 1.3301152449580924e-05 -gegeben 1.329736402678127e-05 -▁betreffend 1.329736402678127e-05 -▁departure 1.329736402678127e-05 -▁excessive 1.329736402678127e-05 -▁cop 1.3293575603981618e-05 -▁54 1.3289787181181964e-05 -▁Monday 1.3289787181181964e-05 -▁Sind 1.3289787181181964e-05 -▁Freitag 1.3285998758382312e-05 -▁Travel 1.3285998758382312e-05 -▁liefert 1.3282210335582658e-05 -IF 1.3278421912783006e-05 -dorf 1.3278421912783006e-05 -racht 1.3278421912783006e-05 -▁ausgerichtet 1.3278421912783006e-05 -▁impose 1.3278421912783006e-05 -▁wrote 1.3274633489983352e-05 -▁Halb 1.32708450671837e-05 -▁verkauft 1.32708450671837e-05 -▁Besides 1.3267056644384048e-05 -▁greatly 1.3267056644384048e-05 -▁Hat 1.3263268221584395e-05 -▁wert 1.3263268221584395e-05 -▁Turn 1.3259479798784743e-05 -▁channel 1.3259479798784743e-05 -ě 1.3255691375985089e-05 -▁gearbeitet 1.3251902953185437e-05 -▁liked 1.3251902953185437e-05 -garten 1.3248114530385783e-05 -▁Bulgarien 1.3248114530385783e-05 -▁director 1.3248114530385783e-05 -ueller 1.3244326107586131e-05 -▁della 1.3244326107586131e-05 -▁französische 1.3244326107586131e-05 -▁co- 1.3240537684786479e-05 -▁verbindet 1.3240537684786479e-05 -▁Mission 1.3236749261986825e-05 -▁Wohnung 1.3236749261986825e-05 -▁genannte 1.3236749261986825e-05 -▁XP 1.3232960839187173e-05 -▁fa 1.3232960839187173e-05 -▁Sohn 1.3229172416387519e-05 -▁finish 1.3229172416387519e-05 --related 1.3225383993587867e-05 -▁Mr. 1.3225383993587867e-05 -▁philosophy 1.3225383993587867e-05 -▁tro 1.3225383993587867e-05 -▁Geschwindigkeit 1.3221595570788214e-05 -▁MP3 1.3221595570788214e-05 -▁Schon 1.3221595570788214e-05 -▁abuse 1.3221595570788214e-05 -▁unterb 1.3221595570788214e-05 -prof 1.3217807147988562e-05 -▁Hungarian 1.3214018725188908e-05 -▁deutsch 1.3214018725188908e-05 -▁ships 1.3214018725188908e-05 -▁Darum 1.3210230302389256e-05 -▁offiziellen 1.3210230302389256e-05 -▁repair 1.3210230302389256e-05 -quis 1.3206441879589604e-05 -▁Ansichten 1.3206441879589604e-05 -▁Common 1.3206441879589604e-05 -▁Lam 1.320265345678995e-05 -▁Montag 1.320265345678995e-05 -▁desk 1.320265345678995e-05 -▁schicken 1.320265345678995e-05 -rog 1.3198865033990298e-05 -▁Staates 1.3198865033990298e-05 -▁Konsequenzen 1.3195076611190644e-05 -▁tw 1.3195076611190644e-05 -idad 1.3191288188390992e-05 -▁solches 1.3191288188390992e-05 -▁gained 1.3183711342791686e-05 -▁Geh 1.3176134497192381e-05 -▁Tri 1.3172346074392729e-05 -▁anbelangt 1.3172346074392729e-05 -äft 1.3168557651593075e-05 -▁Gang 1.3168557651593075e-05 -▁Kroatien 1.3168557651593075e-05 -han 1.3164769228793423e-05 -oke 1.3164769228793423e-05 -▁Date 1.3164769228793423e-05 -▁bereitgestellt 1.3164769228793423e-05 -▁Quality 1.3160980805993769e-05 -▁supplier 1.3160980805993769e-05 -▁teams 1.3160980805993769e-05 -cess 1.3153403960394465e-05 -▁exports 1.3153403960394465e-05 -▁zuständig 1.3153403960394465e-05 -▁decor 1.3149615537594811e-05 -▁ou 1.3149615537594811e-05 -▁song 1.3149615537594811e-05 -▁Hom 1.3145827114795159e-05 -▁Jul 1.3145827114795159e-05 -ird 1.3142038691995506e-05 -▁Fahrt 1.3142038691995506e-05 -▁Coll 1.3138250269195853e-05 -▁Anlass 1.31344618463962e-05 -▁Nutz 1.31344618463962e-05 -▁housing 1.31344618463962e-05 --I 1.3130673423596548e-05 -▁Übergangs 1.3130673423596548e-05 -▁wiederholt 1.3126885000796894e-05 -omat 1.3123096577997242e-05 -▁AIDS 1.3123096577997242e-05 -▁Guests 1.3123096577997242e-05 -▁Sterne 1.3123096577997242e-05 -▁erl 1.3123096577997242e-05 -icate 1.311930815519759e-05 -▁delicious 1.311930815519759e-05 -▁iPhone 1.311930815519759e-05 -schule 1.3115519732397936e-05 -▁interpretation 1.3115519732397936e-05 -▁sozialer 1.3115519732397936e-05 -▁wirtschaft 1.3115519732397936e-05 -▁Überlegungen 1.3115519732397936e-05 -▁gere 1.310794288679863e-05 -▁reservations 1.310794288679863e-05 -▁Agriculture 1.3104154463998978e-05 -▁Having 1.3104154463998978e-05 -▁trial 1.3104154463998978e-05 -▁Mur 1.3100366041199325e-05 -▁Head 1.3096577618399673e-05 -▁sounds 1.3096577618399673e-05 -▁banking 1.309278919560002e-05 -▁beantworten 1.309278919560002e-05 -▁comparison 1.3089000772800367e-05 -▁difficulty 1.3089000772800367e-05 -ning 1.3085212350000715e-05 -▁Gewährleistung 1.3085212350000715e-05 -▁Still 1.3081423927201061e-05 -▁einst 1.3081423927201061e-05 -AV 1.3077635504401409e-05 -Ü 1.3077635504401409e-05 -▁receiving 1.3077635504401409e-05 -▁roof 1.3077635504401409e-05 -▁weist 1.3073847081601755e-05 -▁Beschlüsse 1.3070058658802103e-05 -cies 1.3066270236002449e-05 -▁stammen 1.3066270236002449e-05 -▁Anstieg 1.3062481813202797e-05 -▁Opt 1.3062481813202797e-05 -▁Zivilgesellschaft 1.3062481813202797e-05 -▁doors 1.3062481813202797e-05 -▁sleep 1.3062481813202797e-05 -ume 1.3058693390403145e-05 -▁regarded 1.3058693390403145e-05 -▁ähnlich 1.3058693390403145e-05 -richtung 1.3054904967603492e-05 -jährige 1.305111654480384e-05 -ängen 1.305111654480384e-05 -fluss 1.3047328122004186e-05 -▁Chr 1.3047328122004186e-05 -▁Lux 1.3047328122004186e-05 -▁genügend 1.3047328122004186e-05 -hör 1.3043539699204534e-05 -▁innen 1.3043539699204534e-05 -▁Herbst 1.303975127640488e-05 -▁Kollegin 1.303975127640488e-05 -▁specified 1.303975127640488e-05 -▁Rio 1.3035962853605228e-05 -geh 1.3032174430805576e-05 -nummer 1.3032174430805576e-05 -umm 1.3032174430805576e-05 -▁module 1.3032174430805576e-05 -ista 1.302459758520627e-05 -aming 1.3020809162406616e-05 -ifik 1.3020809162406616e-05 -▁Mitgliedschaft 1.3020809162406616e-05 -▁invited 1.3020809162406616e-05 -maß 1.3017020739606964e-05 -sektor 1.3017020739606964e-05 -üh 1.3017020739606964e-05 -▁Eigentum 1.3017020739606964e-05 -▁Schüler 1.3017020739606964e-05 -▁harm 1.3017020739606964e-05 -li 1.3013232316807311e-05 -▁MA 1.3013232316807311e-05 -▁Mrd. 1.3013232316807311e-05 -▁welt 1.3013232316807311e-05 -orf 1.3009443894007659e-05 -▁Jung 1.3009443894007659e-05 -▁Strasbourg 1.3009443894007659e-05 -▁pursue 1.3009443894007659e-05 -▁satisfaction 1.3009443894007659e-05 -▁Direct 1.3005655471208007e-05 -▁inmitten 1.3005655471208007e-05 -▁Hong 1.3001867048408353e-05 -▁rain 1.3001867048408353e-05 -▁corr 1.29980786256087e-05 -▁crew 1.29980786256087e-05 -▁disabled 1.29980786256087e-05 -▁überprüft 1.29980786256087e-05 -▁Potenzial 1.2994290202809047e-05 -▁periods 1.2994290202809047e-05 -▁suggested 1.2994290202809047e-05 -room 1.2990501780009395e-05 -▁abgesch 1.2990501780009395e-05 -▁entspannen 1.2990501780009395e-05 -▁fol 1.2990501780009395e-05 -ui 1.2986713357209741e-05 -▁bu 1.2986713357209741e-05 -▁filter 1.2982924934410089e-05 -▁mittleren 1.2982924934410089e-05 -pol 1.2979136511610435e-05 -▁Budgets 1.2979136511610435e-05 -▁Mov 1.2979136511610435e-05 -▁Positionen 1.2979136511610435e-05 -▁Wissens 1.2979136511610435e-05 -▁colours 1.2975348088810783e-05 -▁grav 1.2975348088810783e-05 -andt 1.2971559666011131e-05 -▁herrscht 1.2971559666011131e-05 -▁manual 1.2971559666011131e-05 -▁Quellen 1.2967771243211478e-05 -▁begrenzt 1.2967771243211478e-05 -▁ernsthaft 1.2967771243211478e-05 -▁letztendlich 1.2967771243211478e-05 -▁wireless 1.2967771243211478e-05 -▁attempts 1.2960194397612172e-05 -▁val 1.295640597481252e-05 -▁wohnen 1.295640597481252e-05 -enkt 1.2952617552012866e-05 -struktur 1.2952617552012866e-05 -▁Finanzierungs 1.2952617552012866e-05 -▁cy 1.2952617552012866e-05 -▁standing 1.2952617552012866e-05 -ô 1.2945040706413562e-05 -▁Eng 1.2945040706413562e-05 -▁edge 1.2945040706413562e-05 -▁forests 1.2945040706413562e-05 -▁overview 1.2945040706413562e-05 -▁Übrigen 1.2945040706413562e-05 -log 1.2941252283613908e-05 -kom 1.2937463860814256e-05 -▁Tennis 1.2937463860814256e-05 -▁mussten 1.2937463860814256e-05 -▁revision 1.2933675438014602e-05 -▁weitgehend 1.2933675438014602e-05 -▁well-known 1.2933675438014602e-05 -wend 1.292988701521495e-05 -▁hinweg 1.2926098592415297e-05 -▁stream 1.2926098592415297e-05 --time 1.2922310169615645e-05 -asts 1.2922310169615645e-05 -erry 1.2922310169615645e-05 -isl 1.2922310169615645e-05 -▁savings 1.2922310169615645e-05 -▁Laut 1.2914733324016339e-05 -▁eingesch 1.2914733324016339e-05 -▁Atlantic 1.2910944901216687e-05 -▁Wählen 1.2910944901216687e-05 -do 1.2907156478417033e-05 -reib 1.2907156478417033e-05 -▁resource 1.2907156478417033e-05 -▁verteidigen 1.2907156478417033e-05 -auschen 1.2903368055617381e-05 -material 1.2903368055617381e-05 -Ä 1.2903368055617381e-05 -▁machte 1.2903368055617381e-05 -▁terror 1.2903368055617381e-05 -▁oper 1.2899579632817727e-05 -▁tennis 1.2899579632817727e-05 -▁Gerechtigkeit 1.2895791210018075e-05 -▁erstmals 1.2895791210018075e-05 -▁script 1.2895791210018075e-05 -ücht 1.2892002787218421e-05 -▁entwickelte 1.2892002787218421e-05 -uting 1.2888214364418769e-05 -▁Hardware 1.2888214364418769e-05 -▁optional 1.2884425941619117e-05 -gänge 1.2880637518819464e-05 -ultur 1.2880637518819464e-05 -▁41 1.2880637518819464e-05 -▁A5-0 1.2880637518819464e-05 -▁Playa 1.2880637518819464e-05 -▁Unternehmer 1.2880637518819464e-05 -▁Erfüllung 1.2873060673220158e-05 -▁Klaus 1.2873060673220158e-05 -▁erlebt 1.2873060673220158e-05 --U 1.2869272250420506e-05 -wahlen 1.2869272250420506e-05 -▁realistic 1.2869272250420506e-05 -▁scene 1.2869272250420506e-05 -▁breiten 1.28616954048212e-05 -▁patient 1.28616954048212e-05 -niveau 1.2857906982021546e-05 -▁fan 1.2854118559221894e-05 -▁gehe 1.2854118559221894e-05 -gebracht 1.2850330136422242e-05 -▁Kulturen 1.2850330136422242e-05 -▁welcoming 1.2850330136422242e-05 -immen 1.2846541713622588e-05 -▁Währungs 1.2846541713622588e-05 -rt 1.2842753290822936e-05 -▁Luxus 1.2842753290822936e-05 -▁betreiben 1.2842753290822936e-05 -▁bridge 1.2842753290822936e-05 -▁passion 1.2842753290822936e-05 -▁Stoffe 1.2838964868023283e-05 -uster 1.2835176445223631e-05 -▁Mak 1.2835176445223631e-05 -▁aktive 1.2835176445223631e-05 -▁Übergang 1.2835176445223631e-05 -irt 1.2831388022423977e-05 -▁Risik 1.2831388022423977e-05 -▁Meister 1.2827599599624325e-05 -▁Pres 1.2827599599624325e-05 -eland 1.2823811176824673e-05 -ience 1.2823811176824673e-05 -▁Eingang 1.2823811176824673e-05 -▁acting 1.2823811176824673e-05 -▁reaction 1.2823811176824673e-05 -▁korrekt 1.2816234331225367e-05 -▁reduziert 1.2816234331225367e-05 -▁Gerade 1.2812445908425713e-05 -▁Situated 1.2808657485626061e-05 -▁Space 1.2808657485626061e-05 -▁arguments 1.2808657485626061e-05 -▁examine 1.2808657485626061e-05 -▁Brasilien 1.2804869062826407e-05 -▁Captain 1.2804869062826407e-05 -▁permitted 1.2804869062826407e-05 -▁Class 1.2801080640026755e-05 -▁federal 1.2801080640026755e-05 -▁rising 1.2801080640026755e-05 -erver 1.279350379442745e-05 -▁allzu 1.279350379442745e-05 -▁confirmation 1.279350379442745e-05 -▁ports 1.279350379442745e-05 -schnitt 1.2785926948828144e-05 -▁Wider 1.2785926948828144e-05 -▁enabling 1.2785926948828144e-05 -steuer 1.2782138526028492e-05 -▁Plus 1.2782138526028492e-05 -▁destroyed 1.2782138526028492e-05 -▁Gehminuten 1.2778350103228838e-05 -▁Passwort 1.2778350103228838e-05 -▁investieren 1.2778350103228838e-05 -etzt 1.2770773257629532e-05 -▁football 1.2770773257629532e-05 -▁wirtschaftlicher 1.2770773257629532e-05 -time 1.276698483482988e-05 -cel 1.2763196412030228e-05 -▁Verletzung 1.2763196412030228e-05 -▁retain 1.2763196412030228e-05 -▁IF 1.2759407989230574e-05 -geschichte 1.2755619566430922e-05 -▁electric 1.2751831143631269e-05 -leitung 1.2748042720831617e-05 -▁Klasse 1.2748042720831617e-05 -behörde 1.2744254298031963e-05 -freie 1.2744254298031963e-05 -▁festival 1.2744254298031963e-05 -▁Reduzierung 1.2740465875232311e-05 -▁betrieben 1.2740465875232311e-05 -▁representation 1.2740465875232311e-05 -▁Aussch 1.2732889029633005e-05 -▁leaves 1.2732889029633005e-05 -▁narrow 1.2732889029633005e-05 -▁religion 1.2732889029633005e-05 -▁© 1.2732889029633005e-05 -piel 1.2729100606833353e-05 -▁1.5 1.2729100606833353e-05 -äum 1.27253121840337e-05 -▁Rea 1.27253121840337e-05 -▁angepasst 1.27253121840337e-05 -▁unforgettable 1.27253121840337e-05 -▁construct 1.2721523761234047e-05 -200 1.2717735338434394e-05 -bedarf 1.2717735338434394e-05 -▁stellte 1.2717735338434394e-05 -inken 1.2713946915634741e-05 -ubs 1.2713946915634741e-05 -uge 1.2713946915634741e-05 -▁könnt 1.2713946915634741e-05 -▁optimale 1.2713946915634741e-05 -eile 1.2710158492835088e-05 -tt 1.2710158492835088e-05 --Er 1.2706370070035436e-05 -▁Korruption 1.2706370070035436e-05 -▁cooperate 1.2706370070035436e-05 -AB 1.2702581647235784e-05 -▁constitute 1.2702581647235784e-05 -▁farming 1.2702581647235784e-05 -▁hierbei 1.2702581647235784e-05 -▁tritt 1.2702581647235784e-05 -▁refers 1.269879322443613e-05 -▁tend 1.269879322443613e-05 -▁Georg 1.2695004801636478e-05 -▁horse 1.2695004801636478e-05 -68 1.2691216378836824e-05 -▁Based 1.2691216378836824e-05 -▁Belgien 1.2691216378836824e-05 -▁bekommt 1.2691216378836824e-05 -car 1.2687427956037172e-05 -▁Family 1.2683639533237518e-05 -▁Liber 1.2683639533237518e-05 -▁Lobby 1.2683639533237518e-05 -▁Vorhaben 1.2683639533237518e-05 -▁obliged 1.2683639533237518e-05 -▁politically 1.2683639533237518e-05 -kontrolle 1.2679851110437866e-05 -▁Lounge 1.2679851110437866e-05 -▁Erd 1.2676062687638214e-05 -▁Investitions 1.2676062687638214e-05 -▁Entlastung 1.2672274264838561e-05 -▁Kontakte 1.2672274264838561e-05 -▁unterscheiden 1.2672274264838561e-05 -▁п 1.2672274264838561e-05 -▁directory 1.2668485842038908e-05 -▁rely 1.2668485842038908e-05 -▁Place 1.2664697419239255e-05 -▁Dist 1.2660908996439603e-05 -▁legitim 1.2660908996439603e-05 -▁Einkommen 1.2657120573639949e-05 -▁gelingt 1.2657120573639949e-05 -▁Islands 1.2653332150840297e-05 -▁führende 1.2653332150840297e-05 -▁Full 1.2649543728040645e-05 -arn 1.2645755305240991e-05 -east 1.2645755305240991e-05 -▁Donnerstag 1.2645755305240991e-05 -▁erwähnen 1.2645755305240991e-05 -▁verschl 1.2645755305240991e-05 -ält 1.2641966882441339e-05 -▁Saison 1.2641966882441339e-05 -▁Stockholm 1.2641966882441339e-05 -üg 1.2638178459641685e-05 -▁Katastrophe 1.2638178459641685e-05 -▁blind 1.2638178459641685e-05 -▁dynam 1.2638178459641685e-05 -▁publication 1.2638178459641685e-05 -uste 1.2634390036842033e-05 -▁Metro 1.2634390036842033e-05 -▁Trotzdem 1.2634390036842033e-05 -▁freundlich 1.2634390036842033e-05 -▁instructions 1.2634390036842033e-05 -▁paying 1.2634390036842033e-05 -ences 1.263060161404238e-05 -▁Kin 1.263060161404238e-05 -▁MP 1.263060161404238e-05 -▁eth 1.263060161404238e-05 -▁versichern 1.263060161404238e-05 -▁Finland 1.2626813191242728e-05 -▁prosperity 1.2626813191242728e-05 -▁ye 1.2623024768443074e-05 -▁zukünftigen 1.2623024768443074e-05 -▁skiing 1.2619236345643422e-05 -▁dry 1.2611659500044116e-05 -▁prof 1.2611659500044116e-05 -anning 1.260408265444481e-05 -▁Grünen 1.260408265444481e-05 -▁Metall 1.260408265444481e-05 -▁retail 1.260408265444481e-05 -▁welcomed 1.260408265444481e-05 -orten 1.2600294231645158e-05 -▁Praktiken 1.2600294231645158e-05 -▁Dafür 1.2596505808845504e-05 -▁Tourism 1.2596505808845504e-05 -▁continuous 1.25889289632462e-05 -▁Konto 1.2585140540446547e-05 -▁renowned 1.2585140540446547e-05 -▁Hor 1.2581352117646895e-05 -▁Liberal 1.2581352117646895e-05 -▁ersetzen 1.2581352117646895e-05 -▁einzusetzen 1.2577563694847241e-05 -por 1.2573775272047589e-05 -▁cleaning 1.2573775272047589e-05 -▁Mö 1.2569986849247935e-05 -▁SE 1.2569986849247935e-05 -▁imports 1.2569986849247935e-05 -▁rare 1.2569986849247935e-05 -▁significance 1.2569986849247935e-05 -pläne 1.2566198426448283e-05 -▁Schiffe 1.2566198426448283e-05 -▁entdeckt 1.2566198426448283e-05 -▁sought 1.2566198426448283e-05 --on 1.2562410003648629e-05 -ESS 1.2562410003648629e-05 -achte 1.2562410003648629e-05 -▁53 1.2562410003648629e-05 -▁killed 1.2562410003648629e-05 -▁Mot 1.2554833158049325e-05 -▁characters 1.2554833158049325e-05 -▁ded 1.2551044735249671e-05 -▁Marke 1.2547256312450019e-05 -▁Prag 1.2547256312450019e-05 -▁einbezogen 1.2547256312450019e-05 -▁trips 1.2547256312450019e-05 -jähr 1.2543467889650366e-05 -▁Refer 1.2539679466850714e-05 -▁Facilities 1.253589104405106e-05 -▁gestattet 1.253589104405106e-05 -▁lunch 1.253589104405106e-05 -anger 1.2532102621251408e-05 -emon 1.2532102621251408e-05 -▁Umsatz 1.2532102621251408e-05 -▁erk 1.2532102621251408e-05 -▁kontaktieren 1.2532102621251408e-05 -▁rank 1.2532102621251408e-05 -▁verteilt 1.2532102621251408e-05 -▁Through 1.2528314198451756e-05 -▁id 1.2528314198451756e-05 -▁societies 1.2528314198451756e-05 -▁umfassend 1.2528314198451756e-05 -ided 1.2524525775652102e-05 -uben 1.2524525775652102e-05 -▁Erlebnis 1.2524525775652102e-05 -▁harmonisation 1.2524525775652102e-05 -▁herunter 1.2524525775652102e-05 -▁legally 1.2524525775652102e-05 -▁manche 1.2524525775652102e-05 -cles 1.252073735285245e-05 -ibly 1.252073735285245e-05 -▁LT 1.252073735285245e-05 -▁beendet 1.252073735285245e-05 -▁dependent 1.252073735285245e-05 -▁freedoms 1.252073735285245e-05 -▁institutionellen 1.252073735285245e-05 -aßt 1.2516948930052796e-05 -estr 1.2516948930052796e-05 -▁CAP 1.2516948930052796e-05 -▁Wand 1.2516948930052796e-05 -▁verfügbaren 1.2516948930052796e-05 -eckt 1.2513160507253144e-05 -▁Schle 1.2513160507253144e-05 -rates 1.250937208445349e-05 -▁radical 1.250937208445349e-05 -uppen 1.2505583661653838e-05 -▁04 1.2505583661653838e-05 -▁Vo 1.2505583661653838e-05 -▁anschließend 1.2505583661653838e-05 -▁loc 1.2505583661653838e-05 -▁Pir 1.2501795238854185e-05 -▁Bio 1.2498006816054533e-05 -▁Erm 1.2498006816054533e-05 -▁Schrift 1.2498006816054533e-05 -▁behaupten 1.2498006816054533e-05 -▁justified 1.2494218393254881e-05 -▁deswegen 1.2490429970455227e-05 -▁getragen 1.2490429970455227e-05 -▁withdraw 1.2490429970455227e-05 -emin 1.2482853124855921e-05 -endung 1.2482853124855921e-05 -▁Game 1.2482853124855921e-05 -▁videos 1.2482853124855921e-05 -GB 1.2479064702056269e-05 -techn 1.2475276279256615e-05 -▁Prozesse 1.2475276279256615e-05 -▁bedeutende 1.2475276279256615e-05 -arge 1.2471487856456963e-05 -▁referendum 1.2471487856456963e-05 -▁specialist 1.2471487856456963e-05 -▁Berliner 1.2467699433657311e-05 -▁ansprechen 1.2467699433657311e-05 -▁none 1.2467699433657311e-05 -uy 1.2463911010857657e-05 -▁illegalen 1.2463911010857657e-05 -▁plug 1.2460122588058005e-05 -ianischen 1.2456334165258352e-05 -▁Eurozone 1.2456334165258352e-05 -▁ord 1.2456334165258352e-05 -ко 1.24525457424587e-05 -▁Verarbeitung 1.24525457424587e-05 -▁tables 1.24525457424587e-05 -▁unterstreichen 1.24525457424587e-05 -▁EZB 1.2448757319659046e-05 -▁Frist 1.2448757319659046e-05 -▁Plattform 1.2448757319659046e-05 -▁vorzulegen 1.2441180474059742e-05 -▁SS 1.2437392051260088e-05 -▁wissenschaftliche 1.2437392051260088e-05 -▁interview 1.2433603628460436e-05 -▁offene 1.2433603628460436e-05 -▁Tätigkeiten 1.2429815205660782e-05 -▁größeres 1.2429815205660782e-05 -▁negotiating 1.2429815205660782e-05 -▁fans 1.242602678286113e-05 -izen 1.2422238360061476e-05 -▁Venice 1.2422238360061476e-05 -▁Genießen 1.2418449937261824e-05 -▁emp 1.2418449937261824e-05 -▁welcomes 1.2418449937261824e-05 -programms 1.2414661514462171e-05 -▁mitteilen 1.2414661514462171e-05 -▁supervision 1.2414661514462171e-05 -ро 1.2410873091662519e-05 -▁konzentriert 1.2410873091662519e-05 -▁langfristige 1.2410873091662519e-05 -▁relative 1.2410873091662519e-05 -inz 1.2407084668862867e-05 -istischer 1.2407084668862867e-05 -nes 1.2407084668862867e-05 -▁bere 1.2407084668862867e-05 -▁homes 1.2407084668862867e-05 -▁looked 1.2407084668862867e-05 -schau 1.2403296246063213e-05 -technik 1.2403296246063213e-05 -▁clubs 1.2403296246063213e-05 -▁univers 1.2403296246063213e-05 -▁UNESCO 1.2399507823263561e-05 -▁Zürich 1.2395719400463907e-05 -▁steigen 1.2395719400463907e-05 -▁viol 1.2395719400463907e-05 -▁vorgesehenen 1.2395719400463907e-05 -kur 1.2391930977664255e-05 -ulations 1.2391930977664255e-05 -▁Sachen 1.2391930977664255e-05 -anter 1.2388142554864601e-05 -▁Education 1.2388142554864601e-05 -▁Deb 1.2384354132064949e-05 -▁surely 1.2384354132064949e-05 -▁Auditors 1.2380565709265297e-05 -▁Ebenen 1.2380565709265297e-05 -▁Stuttgart 1.2376777286465643e-05 -▁Flex 1.2372988863665991e-05 -▁complicated 1.2372988863665991e-05 -▁contributed 1.2372988863665991e-05 -▁hast 1.2372988863665991e-05 -▁herum 1.2372988863665991e-05 -TER 1.2369200440866338e-05 -iance 1.2369200440866338e-05 -▁hospitality 1.2369200440866338e-05 -oper 1.2365412018066686e-05 -▁forthcoming 1.2365412018066686e-05 -▁wellness 1.2365412018066686e-05 -xy 1.2361623595267032e-05 -▁Leg 1.2361623595267032e-05 -▁enterprise 1.2361623595267032e-05 -▁suffered 1.2361623595267032e-05 -▁Seg 1.235783517246738e-05 -ilen 1.2354046749667726e-05 -jekt 1.2354046749667726e-05 -▁Ausflüge 1.2354046749667726e-05 -▁Existenz 1.2354046749667726e-05 -▁appearance 1.2354046749667726e-05 -▁konfrontiert 1.2354046749667726e-05 -▁künftige 1.2354046749667726e-05 -rechen 1.2350258326868074e-05 -▁Rückkehr 1.2350258326868074e-05 -▁cra 1.2350258326868074e-05 -▁kr 1.2350258326868074e-05 -▁sharing 1.2350258326868074e-05 -bus 1.2346469904068422e-05 -modell 1.2346469904068422e-05 -▁intern 1.2346469904068422e-05 -▁spezifische 1.2346469904068422e-05 -äst 1.2342681481268768e-05 -▁Abfall 1.2342681481268768e-05 -▁Entscheidungs 1.2342681481268768e-05 -▁eat 1.2342681481268768e-05 -▁Betten 1.2338893058469116e-05 -▁Gottes 1.2338893058469116e-05 -▁supposed 1.2338893058469116e-05 -trägen 1.2335104635669462e-05 -▁194 1.2335104635669462e-05 -▁dog 1.2335104635669462e-05 -▁everybody 1.2335104635669462e-05 -▁teile 1.2335104635669462e-05 -ugt 1.233131621286981e-05 -▁Situationen 1.233131621286981e-05 -▁preventing 1.233131621286981e-05 -erson 1.2327527790070157e-05 -richtlinie 1.2327527790070157e-05 -ärm 1.2327527790070157e-05 -▁1st 1.2327527790070157e-05 -▁manip 1.2327527790070157e-05 -▁remained 1.2327527790070157e-05 -▁techn 1.2327527790070157e-05 -ochen 1.2323739367270505e-05 -▁Tabak 1.2323739367270505e-05 -▁allocated 1.2323739367270505e-05 -▁Willkommen 1.2319950944470853e-05 -▁altern 1.2319950944470853e-05 -▁expand 1.2319950944470853e-05 -▁geboten 1.2319950944470853e-05 -▁studio 1.2319950944470853e-05 -▁Freizügigkeit 1.2316162521671199e-05 -▁Ruf 1.2316162521671199e-05 -TR 1.2308585676071893e-05 -▁Corporate 1.2308585676071893e-05 -▁amenities 1.2308585676071893e-05 -anische 1.2304797253272241e-05 -▁Weiteren 1.2304797253272241e-05 -▁überein 1.2304797253272241e-05 -mitt 1.2301008830472587e-05 -oman 1.2301008830472587e-05 -▁bisherigen 1.2301008830472587e-05 -▁intr 1.2297220407672935e-05 -▁abstimmen 1.2293431984873282e-05 -▁modules 1.2293431984873282e-05 -▁geraten 1.2289643562073629e-05 -▁originally 1.2285855139273977e-05 -▁parameters 1.2285855139273977e-05 -hören 1.2282066716474324e-05 -▁Kranken 1.2282066716474324e-05 -▁enabled 1.2282066716474324e-05 -▁Übernachtung 1.2282066716474324e-05 -▁è 1.2282066716474324e-05 -bereichen 1.2278278293674672e-05 -▁hardly 1.2278278293674672e-05 -▁A. 1.2274489870875018e-05 -▁Poly 1.2274489870875018e-05 -▁emb 1.2274489870875018e-05 -laden 1.2270701448075366e-05 -▁ordinary 1.2270701448075366e-05 -▁pa 1.2270701448075366e-05 -▁proceedings 1.2266913025275712e-05 -▁Fahrzeuge 1.226312460247606e-05 -ausch 1.2259336179676408e-05 -ø 1.2259336179676408e-05 -▁Ablehnung 1.2259336179676408e-05 -▁unterzeichnet 1.2259336179676408e-05 -mat 1.2255547756876754e-05 -▁Thailand 1.2255547756876754e-05 -uropa 1.2251759334077102e-05 -▁Sky 1.2251759334077102e-05 -▁dom 1.2251759334077102e-05 -▁museums 1.2251759334077102e-05 -▁spe 1.2251759334077102e-05 -used 1.2247970911277449e-05 -▁NGOs 1.2247970911277449e-05 -▁typ 1.2247970911277449e-05 -stant 1.2244182488477796e-05 -▁Does 1.2244182488477796e-05 -trans 1.2240394065678143e-05 -▁Richt 1.2240394065678143e-05 -▁Socialist 1.2240394065678143e-05 -▁romantic 1.2236605642878491e-05 -nehmen 1.2232817220078839e-05 -▁Frühstücksbuffet 1.2232817220078839e-05 -▁calm 1.2232817220078839e-05 -▁directed 1.2232817220078839e-05 -▁geprägt 1.2232817220078839e-05 -▁herunterladen 1.2232817220078839e-05 -▁superior 1.2229028797279185e-05 -ells 1.2225240374479533e-05 -▁beings 1.2225240374479533e-05 -▁compact 1.2225240374479533e-05 -▁hervorgehoben 1.2225240374479533e-05 -▁lay 1.2225240374479533e-05 -▁leistungs 1.2225240374479533e-05 -bruch 1.2221451951679879e-05 -▁Widerspruch 1.2221451951679879e-05 -hält 1.2217663528880227e-05 -▁SK 1.2217663528880227e-05 -▁lieber 1.2217663528880227e-05 -▁realise 1.2213875106080573e-05 -iefer 1.2210086683280921e-05 -▁Perspektive 1.2210086683280921e-05 -▁firmly 1.2210086683280921e-05 -reibung 1.2206298260481268e-05 -rix 1.2206298260481268e-05 -▁AND 1.2206298260481268e-05 -▁cells 1.2206298260481268e-05 -▁cuts 1.2206298260481268e-05 -▁Australia 1.2202509837681616e-05 -▁Det 1.2202509837681616e-05 -▁Medizin 1.2202509837681616e-05 -halle 1.2198721414881963e-05 -▁Stability 1.2198721414881963e-05 -▁multi- 1.2198721414881963e-05 -partner 1.219493299208231e-05 -х 1.219493299208231e-05 -▁Widerstand 1.219493299208231e-05 -▁movie 1.219493299208231e-05 -▁unterliegen 1.219493299208231e-05 -züge 1.2191144569282658e-05 -▁habit 1.2191144569282658e-05 -▁Opposition 1.2187356146483004e-05 -▁vorgeschlagene 1.2187356146483004e-05 -▁AV 1.2183567723683352e-05 -▁EX 1.2183567723683352e-05 -▁Customer 1.2179779300883698e-05 -▁Vista 1.2179779300883698e-05 -spro 1.2175990878084046e-05 -▁programming 1.2175990878084046e-05 -▁indicate 1.2172202455284394e-05 -folge 1.2164625609685088e-05 -▁Joseph 1.2164625609685088e-05 -▁Kommun 1.2164625609685088e-05 -▁Saturday 1.2164625609685088e-05 -▁Piazza 1.2160837186885435e-05 -▁Reisen 1.2160837186885435e-05 -▁Should 1.2157048764085783e-05 -▁Small 1.2157048764085783e-05 -▁errors 1.2157048764085783e-05 -IK 1.2153260341286129e-05 -zess 1.2153260341286129e-05 -▁Americans 1.2153260341286129e-05 -▁Vin 1.2153260341286129e-05 -▁Wirtschaftswachstum 1.2153260341286129e-05 -▁Tim 1.2149471918486477e-05 -▁entstand 1.2145683495686823e-05 -▁rat 1.2145683495686823e-05 --sh 1.2141895072887171e-05 -▁Planeten 1.2141895072887171e-05 -▁motiv 1.2138106650087519e-05 -▁movements 1.2138106650087519e-05 -ruck 1.2134318227287865e-05 -äus 1.2134318227287865e-05 -▁Bett 1.2134318227287865e-05 -▁Mallorca 1.2134318227287865e-05 -▁Urteil 1.2134318227287865e-05 -EG 1.2130529804488213e-05 -ef 1.2130529804488213e-05 -▁Haushaltsplan 1.2130529804488213e-05 -▁barriers 1.2130529804488213e-05 -▁nord 1.2130529804488213e-05 -▁Big 1.2126741381688559e-05 -▁Anwender 1.2122952958888907e-05 -▁Kas 1.2122952958888907e-05 -▁menschliche 1.2122952958888907e-05 -▁ergibt 1.2119164536089254e-05 -▁Suiten 1.2115376113289602e-05 -▁hochwertige 1.2115376113289602e-05 -▁missing 1.2115376113289602e-05 -▁transform 1.2115376113289602e-05 -leiter 1.211158769048995e-05 -zent 1.211158769048995e-05 -ühren 1.211158769048995e-05 -▁deb 1.211158769048995e-05 -▁Serbia 1.2107799267690296e-05 -▁fees 1.2107799267690296e-05 -▁spons 1.2107799267690296e-05 -▁gefallen 1.2104010844890644e-05 -▁recognized 1.2104010844890644e-05 -lösungen 1.210022242209099e-05 -▁behoben 1.210022242209099e-05 -CE 1.2096433999291338e-05 -orial 1.2096433999291338e-05 -richten 1.2096433999291338e-05 -water 1.2096433999291338e-05 -▁rap 1.2096433999291338e-05 -▁anschließen 1.2092645576491684e-05 -▁tree 1.2092645576491684e-05 -stände 1.2088857153692032e-05 -▁Asian 1.2088857153692032e-05 -▁Hard 1.2088857153692032e-05 -▁Brenn 1.208506873089238e-05 -▁angenehm 1.208506873089238e-05 -handels 1.2081280308092726e-05 -lers 1.2081280308092726e-05 -▁68 1.2081280308092726e-05 -▁Pension 1.2081280308092726e-05 -▁explained 1.2081280308092726e-05 -verwaltung 1.2073703462493421e-05 -▁productivity 1.2073703462493421e-05 -▁sight 1.2073703462493421e-05 -▁anhand 1.2069915039693769e-05 -▁anzuzeigen 1.2069915039693769e-05 -read 1.2066126616894115e-05 -▁classes 1.2066126616894115e-05 -▁dist 1.2066126616894115e-05 -▁finanziert 1.2066126616894115e-05 -▁introducing 1.2066126616894115e-05 -CC 1.2062338194094463e-05 -ichts 1.2062338194094463e-05 -▁dialog 1.2062338194094463e-05 -▁internen 1.2062338194094463e-05 -▁remarks 1.2062338194094463e-05 -macht 1.2058549771294809e-05 -▁Christmas 1.2058549771294809e-05 -▁Wor 1.2058549771294809e-05 -▁zukünftige 1.2058549771294809e-05 -▁Hauptbahnhof 1.2054761348495157e-05 -▁ausgehen 1.2054761348495157e-05 -▁Geneva 1.2050972925695505e-05 -▁rechten 1.2050972925695505e-05 -▁saved 1.2050972925695505e-05 -utzen 1.2047184502895851e-05 -▁Barr 1.2047184502895851e-05 -▁Konvent 1.2047184502895851e-05 -▁amazing 1.2047184502895851e-05 -▁whereas 1.2047184502895851e-05 -▁Pe 1.20433960800962e-05 -▁Miß 1.2039607657296545e-05 -▁Nachhaltigkeit 1.2039607657296545e-05 -▁eingegangen 1.2039607657296545e-05 -▁hearing 1.2039607657296545e-05 -▁sustainability 1.2039607657296545e-05 -zahlung 1.2035819234496893e-05 -▁Abendessen 1.2035819234496893e-05 -▁showed 1.2035819234496893e-05 -angel 1.203203081169724e-05 -imp 1.203203081169724e-05 -▁Culture 1.203203081169724e-05 -▁programm 1.203203081169724e-05 -schläge 1.2028242388897588e-05 -ypen 1.2028242388897588e-05 -zahlen 1.2028242388897588e-05 -▁enhanced 1.2028242388897588e-05 -▁functionality 1.2028242388897588e-05 -dition 1.2024453966097936e-05 -▁irre 1.2024453966097936e-05 -▁Resolution 1.2020665543298282e-05 -▁langfristig 1.2020665543298282e-05 -II 1.201687712049863e-05 -ò 1.201687712049863e-05 -▁cam 1.201687712049863e-05 -ahnen 1.2013088697698976e-05 -▁Kre 1.2013088697698976e-05 -▁vereinbart 1.2013088697698976e-05 -▁combine 1.2009300274899324e-05 -▁erg 1.2009300274899324e-05 -▁umfangreiche 1.2009300274899324e-05 -a. 1.200551185209967e-05 -▁sichere 1.200551185209967e-05 -bett 1.2001723429300018e-05 -▁Abschaffung 1.2001723429300018e-05 -▁Euch 1.2001723429300018e-05 -▁schlagen 1.2001723429300018e-05 -mitglieder 1.1997935006500364e-05 -▁Kreis 1.1997935006500364e-05 -▁sections 1.1997935006500364e-05 -▁Veränderung 1.1994146583700712e-05 -▁female 1.1994146583700712e-05 --of 1.199035816090106e-05 -nahm 1.199035816090106e-05 -pre 1.199035816090106e-05 -▁30. 1.199035816090106e-05 -▁Civil 1.199035816090106e-05 -▁obstacles 1.1986569738101407e-05 -geführt 1.1982781315301755e-05 -▁Nachbarn 1.1982781315301755e-05 -▁Rent 1.1982781315301755e-05 --wide 1.1978992892502101e-05 -▁Abhängigkeit 1.1978992892502101e-05 -OC 1.1975204469702449e-05 -anze 1.1975204469702449e-05 -▁Kanada 1.1975204469702449e-05 -▁imagine 1.1975204469702449e-05 -▁militärischen 1.1975204469702449e-05 -▁praktische 1.1975204469702449e-05 -aint 1.1971416046902795e-05 -▁05 1.1971416046902795e-05 -▁transmission 1.1971416046902795e-05 --pr 1.1967627624103143e-05 -▁Allianz 1.1967627624103143e-05 -▁Consumer 1.1967627624103143e-05 -▁Dep 1.1967627624103143e-05 -▁Vorlage 1.1967627624103143e-05 -▁Wähler 1.1967627624103143e-05 -▁vessels 1.1967627624103143e-05 -bestimmungen 1.1963839201303491e-05 -eals 1.1960050778503837e-05 -hers 1.1960050778503837e-05 -hle 1.1956262355704185e-05 -▁Imm 1.1956262355704185e-05 -▁Pläne 1.1956262355704185e-05 -▁Volkes 1.1956262355704185e-05 -▁endorse 1.1956262355704185e-05 -▁metro 1.1956262355704185e-05 -▁präsentieren 1.1956262355704185e-05 -aschen 1.1952473932904531e-05 -oral 1.1948685510104879e-05 -▁Rod 1.1948685510104879e-05 -▁gebeten 1.1948685510104879e-05 -ago 1.1944897087305226e-05 -fassung 1.1944897087305226e-05 -▁sogenannten 1.1944897087305226e-05 -▁Kommissionspräsident 1.1941108664505574e-05 -▁grosse 1.1941108664505574e-05 -▁investigation 1.1941108664505574e-05 -▁tot 1.193732024170592e-05 --6 1.1933531818906268e-05 -▁1960 1.1933531818906268e-05 -▁accurate 1.1933531818906268e-05 -▁dankbar 1.1933531818906268e-05 -▁internationally 1.1929743396106616e-05 -▁songs 1.1925954973306962e-05 -▁regardless 1.192216655050731e-05 -iat 1.1918378127707656e-05 -work 1.1918378127707656e-05 -ühle 1.1914589704908004e-05 -ifen 1.191080128210835e-05 -äumt 1.191080128210835e-05 -▁collected 1.191080128210835e-05 -▁enthaltenen 1.191080128210835e-05 -▁läßt 1.191080128210835e-05 -▁stocks 1.191080128210835e-05 -▁turning 1.191080128210835e-05 -point 1.1907012859308698e-05 -▁authent 1.1907012859308698e-05 -▁organic 1.1907012859308698e-05 -▁reflected 1.1903224436509046e-05 -▁gerichtet 1.1899436013709393e-05 -▁tab 1.1899436013709393e-05 -▁Internationale 1.1895647590909741e-05 -▁exact 1.1895647590909741e-05 -▁Product 1.1891859168110087e-05 -▁computers 1.1888070745310435e-05 -ibr 1.1884282322510781e-05 -▁besonderer 1.1884282322510781e-05 -▁vorliegende 1.1884282322510781e-05 -▁и 1.1884282322510781e-05 -baus 1.1880493899711129e-05 -phase 1.1880493899711129e-05 -▁ba 1.1880493899711129e-05 -▁gift 1.1880493899711129e-05 -▁hervorragenden 1.1880493899711129e-05 -pekt 1.1876705476911477e-05 -▁Erinnerung 1.1876705476911477e-05 -▁Großteil 1.1876705476911477e-05 -▁Islamic 1.1876705476911477e-05 -▁Regierungschefs 1.1876705476911477e-05 -▁consult 1.1876705476911477e-05 -▁exclusion 1.1876705476911477e-05 -▁süd 1.1876705476911477e-05 -▁united 1.1876705476911477e-05 -▁verboten 1.1872917054111823e-05 -▁Dritten 1.1869128631312171e-05 -▁militärische 1.1869128631312171e-05 -▁prison 1.1869128631312171e-05 -ause 1.1865340208512517e-05 -opol 1.1865340208512517e-05 -▁dargestellt 1.1865340208512517e-05 -▁generate 1.1865340208512517e-05 -nimmt 1.1861551785712865e-05 -ssysteme 1.1861551785712865e-05 -▁Meeting 1.1861551785712865e-05 -▁Schlag 1.1861551785712865e-05 -▁cancer 1.1861551785712865e-05 -▁prime 1.1861551785712865e-05 -▁Meines 1.1857763362913212e-05 -▁universities 1.1857763362913212e-05 -▁verdienen 1.1857763362913212e-05 -▁Entspannung 1.185397494011356e-05 -▁Na 1.185397494011356e-05 -▁intens 1.185397494011356e-05 -load 1.1850186517313906e-05 -▁Armee 1.1850186517313906e-05 -text 1.1846398094514254e-05 -▁Aufsichts 1.1846398094514254e-05 -▁Kontinent 1.1846398094514254e-05 -▁pun 1.1846398094514254e-05 -▁repl 1.1846398094514254e-05 -▁Volksgesundheit 1.1842609671714602e-05 -▁hingegen 1.1842609671714602e-05 -▁Konzert 1.1838821248914948e-05 -▁ausser 1.1838821248914948e-05 -▁Börse 1.1835032826115296e-05 -▁Versionen 1.1835032826115296e-05 -▁aer 1.1835032826115296e-05 -▁gap 1.1835032826115296e-05 -▁ride 1.1835032826115296e-05 -▁ca 1.182745598051599e-05 -▁schäd 1.182745598051599e-05 -▁staatliche 1.182745598051599e-05 -▁Gleichstellung 1.1823667557716337e-05 -▁kg 1.1823667557716337e-05 -▁student 1.1823667557716337e-05 -▁travelling 1.1823667557716337e-05 -▁CHF 1.1819879134916684e-05 -chs 1.1816090712117032e-05 -führen 1.1816090712117032e-05 -▁considerably 1.1816090712117032e-05 -▁geplant 1.1816090712117032e-05 -▁vorbei 1.1816090712117032e-05 -sson 1.1812302289317379e-05 -▁ECB 1.1812302289317379e-05 -▁generations 1.1812302289317379e-05 -▁notes 1.1812302289317379e-05 -▁Katastrophen 1.1808513866517727e-05 -▁Zeitung 1.1808513866517727e-05 -▁Fahrrad 1.1804725443718073e-05 -lines 1.1800937020918421e-05 -folgen 1.1797148598118767e-05 -▁covering 1.1797148598118767e-05 -▁DC 1.1793360175319115e-05 -▁Kirchen 1.1793360175319115e-05 -▁Leitung 1.1793360175319115e-05 -▁sights 1.1793360175319115e-05 -▁Modelle 1.1789571752519461e-05 -▁Urlaubs 1.1789571752519461e-05 -▁employ 1.1789571752519461e-05 -▁indirekt 1.1789571752519461e-05 -▁Augenblick 1.1785783329719809e-05 -▁disasters 1.1785783329719809e-05 -▁perm 1.1785783329719809e-05 -▁vari 1.1785783329719809e-05 -▁Befugnisse 1.1781994906920157e-05 -▁Word 1.1781994906920157e-05 -▁adapted 1.1781994906920157e-05 -▁trends 1.1781994906920157e-05 -▁fleet 1.1778206484120504e-05 -▁reaching 1.1778206484120504e-05 -DI 1.1774418061320851e-05 -▁Andere 1.1770629638521198e-05 -▁Betreiber 1.1770629638521198e-05 -▁Fein 1.1770629638521198e-05 -▁Spitzen 1.1770629638521198e-05 -▁layer 1.1770629638521198e-05 -▁Alb 1.1766841215721546e-05 -itel 1.1763052792921892e-05 -▁Bor 1.1763052792921892e-05 -▁Nav 1.1763052792921892e-05 -▁frame 1.175926437012224e-05 -▁ministers 1.175926437012224e-05 -▁Sammlung 1.1755475947322588e-05 -▁jetzigen 1.1755475947322588e-05 -▁pretty 1.1755475947322588e-05 -ila 1.1751687524522934e-05 -▁ens 1.1751687524522934e-05 -▁erfreut 1.1751687524522934e-05 -▁Dokumenten 1.1747899101723282e-05 -▁Grundlagen 1.1747899101723282e-05 -▁Luxemburg 1.1747899101723282e-05 -etch 1.1744110678923628e-05 -▁assess 1.1744110678923628e-05 -▁ju 1.1744110678923628e-05 -▁pan 1.1744110678923628e-05 -CO 1.1740322256123976e-05 -bal 1.1740322256123976e-05 -erbe 1.1740322256123976e-05 -reffen 1.1740322256123976e-05 -▁bike 1.1740322256123976e-05 -▁dignity 1.1740322256123976e-05 -ibil 1.1736533833324323e-05 -oben 1.1736533833324323e-05 -▁HU 1.1736533833324323e-05 -▁Vorausschau 1.1736533833324323e-05 -▁natürliche 1.1736533833324323e-05 -▁strategische 1.1736533833324323e-05 -zustellen 1.1732745410524671e-05 -▁Gespräch 1.1732745410524671e-05 -▁Transfer 1.1732745410524671e-05 -▁Voraus 1.1732745410524671e-05 -▁gewünschten 1.1732745410524671e-05 -▁underground 1.1732745410524671e-05 -▁zieht 1.1732745410524671e-05 -unkt 1.1728956987725018e-05 -▁Commissioners 1.1728956987725018e-05 -▁checks 1.1728956987725018e-05 -▁dra 1.1728956987725018e-05 -▁eignet 1.1728956987725018e-05 -▁till 1.1728956987725018e-05 -aces 1.1721380142125713e-05 -gi 1.1721380142125713e-05 -▁Ang 1.1721380142125713e-05 -▁Nummer 1.1721380142125713e-05 -▁labelling 1.1721380142125713e-05 -▁rück 1.1721380142125713e-05 -band 1.1717591719326059e-05 -▁ER 1.1717591719326059e-05 -▁undoubtedly 1.1717591719326059e-05 --TV 1.1713803296526407e-05 -▁Ku 1.1713803296526407e-05 -▁unabhängigen 1.1713803296526407e-05 -▁C. 1.1710014873726753e-05 -▁penalty 1.1710014873726753e-05 -stitution 1.1706226450927101e-05 -▁Dor 1.1706226450927101e-05 -durch 1.1702438028127447e-05 -▁normalerweise 1.1698649605327795e-05 -▁Beteiligten 1.1694861182528143e-05 -▁nachdrücklich 1.1694861182528143e-05 -▁58 1.169107275972849e-05 --Mod 1.1687284336928838e-05 -obil 1.1687284336928838e-05 -▁'A 1.1687284336928838e-05 -▁recip 1.1687284336928838e-05 --7 1.1683495914129184e-05 -▁Gesicht 1.1683495914129184e-05 -▁normalen 1.1683495914129184e-05 -▁Geheim 1.1679707491329532e-05 -▁hohem 1.1679707491329532e-05 -▁tested 1.1679707491329532e-05 -▁gewähren 1.1675919068529878e-05 -bung 1.1672130645730226e-05 -▁acceptance 1.1672130645730226e-05 -▁consulting 1.1672130645730226e-05 -GE 1.1668342222930574e-05 -▁Effekt 1.1668342222930574e-05 -▁Gall 1.1668342222930574e-05 -▁Märkten 1.1668342222930574e-05 -▁heim 1.1668342222930574e-05 -forschung 1.166455380013092e-05 -▁Heizung 1.166455380013092e-05 -▁Opera 1.166455380013092e-05 -▁worst 1.166455380013092e-05 -▁belief 1.1660765377331268e-05 -▁excluded 1.1660765377331268e-05 -▁gemeinschaftlichen 1.1660765377331268e-05 -onn 1.1656976954531614e-05 -▁discount 1.1656976954531614e-05 -▁zugrunde 1.1656976954531614e-05 -▁AP 1.1653188531731962e-05 -▁kontrollieren 1.1653188531731962e-05 -▁Innenstadt 1.1649400108932309e-05 -▁Kein 1.1649400108932309e-05 -▁filled 1.1649400108932309e-05 -▁leichter 1.1649400108932309e-05 -▁Binnenmarktes 1.1645611686132657e-05 -▁managing 1.1641823263333003e-05 -▁verein 1.1641823263333003e-05 -▁violations 1.1641823263333003e-05 -ktiv 1.1638034840533351e-05 -pot 1.1638034840533351e-05 -änen 1.1638034840533351e-05 -▁legisl 1.1638034840533351e-05 -▁exploitation 1.1634246417733699e-05 -▁pot 1.1634246417733699e-05 -auern 1.1626669572134393e-05 -ji 1.1626669572134393e-05 -▁Wirtschaftspolitik 1.1626669572134393e-05 -SA 1.1622881149334739e-05 -arc 1.1622881149334739e-05 -zub 1.1622881149334739e-05 -▁Muslim 1.1622881149334739e-05 -▁festzulegen 1.1622881149334739e-05 -▁fil 1.1622881149334739e-05 -▁plastic 1.1622881149334739e-05 -▁sichtbar 1.1622881149334739e-05 -▁wirtschaftlich 1.1622881149334739e-05 -▁derart 1.1619092726535087e-05 -▁edition 1.1619092726535087e-05 -▁golden 1.1619092726535087e-05 -▁Industry 1.1615304303735433e-05 -▁Mich 1.1611515880935781e-05 -▁believes 1.1611515880935781e-05 -▁enger 1.1611515880935781e-05 -▁protocol 1.1611515880935781e-05 -schn 1.1607727458136129e-05 -EX 1.1603939035336476e-05 -stit 1.1603939035336476e-05 -▁recover 1.1603939035336476e-05 -22 1.1600150612536824e-05 -▁speichern 1.1600150612536824e-05 -▁19th 1.159636218973717e-05 -▁Casa 1.159636218973717e-05 -і 1.1592573766937518e-05 -▁Reserve 1.1592573766937518e-05 -▁conver 1.1592573766937518e-05 -▁restored 1.1592573766937518e-05 -eting 1.1588785344137864e-05 -ares 1.1584996921338212e-05 -▁mittel 1.1584996921338212e-05 -▁profession 1.1584996921338212e-05 -▁reflects 1.1584996921338212e-05 -▁ähnliche 1.1584996921338212e-05 -▁constit 1.1581208498538558e-05 -egel 1.1577420075738906e-05 -ener 1.1577420075738906e-05 -▁31. 1.1577420075738906e-05 -▁Bür 1.1577420075738906e-05 -▁Prüf 1.1577420075738906e-05 -▁Häuser 1.1573631652939254e-05 -▁externen 1.1573631652939254e-05 -▁fax 1.1573631652939254e-05 --Hotel 1.15698432301396e-05 -▁Aufenthalts 1.15698432301396e-05 -▁Ursachen 1.15698432301396e-05 -▁aff 1.15698432301396e-05 -▁bedanken 1.15698432301396e-05 -▁trag 1.15698432301396e-05 -▁Esp 1.1566054807339948e-05 -▁Lohn 1.1558477961740643e-05 -▁Nero 1.1558477961740643e-05 -orient 1.1554689538940989e-05 -▁Flüchtlinge 1.1554689538940989e-05 -▁Schnell 1.1554689538940989e-05 --Aus 1.1550901116141337e-05 -▁erhoben 1.1550901116141337e-05 -▁Album 1.1547112693341685e-05 -▁Alters 1.1547112693341685e-05 -▁beschränken 1.1547112693341685e-05 -▁nine 1.1547112693341685e-05 -▁satisfy 1.1547112693341685e-05 -▁scheinen 1.1547112693341685e-05 -▁überaus 1.1547112693341685e-05 -▁transferred 1.1543324270542031e-05 -prod 1.1539535847742379e-05 -▁DS 1.1539535847742379e-05 -wärts 1.1535747424942725e-05 -jahres 1.1531959002143073e-05 -sa 1.1531959002143073e-05 -▁Emissions 1.1531959002143073e-05 -▁Reservierung 1.1531959002143073e-05 -ules 1.1528170579343419e-05 -▁Generationen 1.1524382156543767e-05 -▁equivalent 1.1520593733744115e-05 -▁komplette 1.1520593733744115e-05 -ijk 1.1516805310944462e-05 -▁Autos 1.1516805310944462e-05 -▁Non 1.1516805310944462e-05 -▁shadow 1.1516805310944462e-05 -▁Todes 1.151301688814481e-05 -▁poly 1.151301688814481e-05 -▁Gi 1.1509228465345156e-05 -▁Handlungs 1.1509228465345156e-05 -▁clause 1.1509228465345156e-05 -▁stages 1.1509228465345156e-05 -▁subsequent 1.1509228465345156e-05 -▁ut 1.1509228465345156e-05 -gers 1.1505440042545504e-05 -hard 1.1505440042545504e-05 -▁lived 1.1505440042545504e-05 -isions 1.150165161974585e-05 -▁Berichten 1.150165161974585e-05 -▁Coun 1.150165161974585e-05 -85 1.1497863196946198e-05 -▁Cross 1.1497863196946198e-05 -krise 1.1494074774146544e-05 -urr 1.1494074774146544e-05 -▁selling 1.1494074774146544e-05 -▁stuff 1.1494074774146544e-05 -TV 1.1490286351346892e-05 -holt 1.1490286351346892e-05 -▁GM 1.1490286351346892e-05 -▁Konflikte 1.1482709505747586e-05 -▁dig 1.1482709505747586e-05 -▁Rep 1.1478921082947934e-05 -viertel 1.1475132660148281e-05 -▁Gemeinsame 1.1475132660148281e-05 -▁Speisen 1.1475132660148281e-05 -▁anywhere 1.1475132660148281e-05 -▁concentrate 1.1475132660148281e-05 -ahren 1.1471344237348629e-05 -▁Account 1.1471344237348629e-05 -▁Dav 1.1471344237348629e-05 -94 1.1467555814548975e-05 -▁Frank 1.1467555814548975e-05 -▁experiment 1.1467555814548975e-05 -icated 1.1463767391749323e-05 -usch 1.1463767391749323e-05 -▁Verk 1.1463767391749323e-05 -▁erweitern 1.1463767391749323e-05 -▁hoffentlich 1.1463767391749323e-05 -▁Museen 1.1459978968949671e-05 -▁Organe 1.1459978968949671e-05 -▁existiert 1.1459978968949671e-05 -▁reliability 1.1459978968949671e-05 -pal 1.1456190546150017e-05 -▁Protocol 1.1456190546150017e-05 -▁freely 1.1456190546150017e-05 -▁supplied 1.1456190546150017e-05 -orts 1.1452402123350365e-05 -▁fördert 1.1452402123350365e-05 -▁multilateral 1.1452402123350365e-05 -▁untersucht 1.1452402123350365e-05 -bereit 1.1448613700550711e-05 -wind 1.1448613700550711e-05 -▁Textil 1.1448613700550711e-05 -ehm 1.1444825277751059e-05 -reck 1.1444825277751059e-05 -▁Aktionsplan 1.1444825277751059e-05 -▁designs 1.1444825277751059e-05 -▁festgelegten 1.1444825277751059e-05 -▁geringer 1.1444825277751059e-05 -▁losses 1.1444825277751059e-05 -agr 1.1441036854951405e-05 -iam 1.1441036854951405e-05 -▁floors 1.1441036854951405e-05 -anal 1.14334600093521e-05 -▁140 1.14334600093521e-05 -▁einheitliche 1.14334600093521e-05 -bergen 1.1429671586552448e-05 -▁Fran 1.1429671586552448e-05 -▁Georgia 1.1429671586552448e-05 -▁Verteilung 1.1429671586552448e-05 -▁beh 1.1429671586552448e-05 -▁lebens 1.1429671586552448e-05 -▁varied 1.1429671586552448e-05 -osis 1.1425883163752796e-05 -▁Alkohol 1.1425883163752796e-05 -führte 1.1422094740953142e-05 -▁Tausende 1.1422094740953142e-05 -▁Stärke 1.141830631815349e-05 -uj 1.1414517895353836e-05 -▁cart 1.1414517895353836e-05 -▁priv 1.1414517895353836e-05 -▁Fal 1.1410729472554184e-05 -▁undertaken 1.1410729472554184e-05 -▁Cookies 1.1403152626954878e-05 -▁a.m. 1.1403152626954878e-05 -eath 1.1399364204155226e-05 --Ab 1.1395575781355572e-05 -appen 1.1395575781355572e-05 -igr 1.1395575781355572e-05 -isch- 1.1395575781355572e-05 -▁Wochenende 1.1395575781355572e-05 -▁registr 1.1395575781355572e-05 -ted 1.139178735855592e-05 -г 1.139178735855592e-05 -▁Fischere 1.139178735855592e-05 -▁Location 1.139178735855592e-05 -▁Louis 1.139178735855592e-05 -▁umfassen 1.139178735855592e-05 -▁Bonus 1.1387998935756267e-05 -▁Kreditkarte 1.1387998935756267e-05 -▁severe 1.1387998935756267e-05 -projekt 1.1384210512956615e-05 -▁learned 1.1384210512956615e-05 -▁perfekte 1.1384210512956615e-05 -▁Fuss 1.1380422090156961e-05 -▁sovereignty 1.1380422090156961e-05 -▁fert 1.1376633667357309e-05 -organ 1.1372845244557655e-05 -▁Academy 1.1372845244557655e-05 -▁Filter 1.1372845244557655e-05 -bers 1.1369056821758003e-05 -ffekt 1.1369056821758003e-05 -▁Kriminalität 1.1369056821758003e-05 -▁umsetzen 1.1369056821758003e-05 -pers 1.1365268398958351e-05 -spe 1.1365268398958351e-05 -▁Would 1.1365268398958351e-05 -▁wirksame 1.1365268398958351e-05 -reat 1.1361479976158697e-05 -ehn 1.1353903130559392e-05 -ächtigen 1.1353903130559392e-05 -▁Rechten 1.1353903130559392e-05 -▁reconstruction 1.1353903130559392e-05 -▁humanitäre 1.1350114707759739e-05 -▁overlooking 1.1350114707759739e-05 -▁25. 1.1346326284960086e-05 -▁Beide 1.1346326284960086e-05 -▁push 1.1346326284960086e-05 -▁Dra 1.1342537862160434e-05 -▁cultures 1.1342537862160434e-05 -▁nützlich 1.1342537862160434e-05 -▁picturesque 1.1342537862160434e-05 -ichert 1.1338749439360782e-05 -iew 1.1338749439360782e-05 -▁Dritte 1.1338749439360782e-05 -▁non-smoking 1.1338749439360782e-05 -▁zurückzuführen 1.1338749439360782e-05 -▁1970 1.1334961016561128e-05 -▁fishermen 1.1334961016561128e-05 -▁komfortable 1.1334961016561128e-05 -▁sicheren 1.1334961016561128e-05 -▁Angel 1.1331172593761476e-05 -▁Kriegs 1.1331172593761476e-05 -EST 1.1327384170961822e-05 -▁Iron 1.1327384170961822e-05 -▁aufgebaut 1.1327384170961822e-05 -▁restore 1.1327384170961822e-05 -ennt 1.132359574816217e-05 -▁Florenz 1.132359574816217e-05 -▁Himmel 1.132359574816217e-05 -▁Tanz 1.132359574816217e-05 -▁minimal 1.1319807325362516e-05 -▁wonach 1.1316018902562864e-05 -ori 1.1312230479763212e-05 -▁Satelliten 1.1312230479763212e-05 -▁hierfür 1.1312230479763212e-05 -earing 1.1308442056963559e-05 -rep 1.1308442056963559e-05 -▁Finanzkrise 1.1308442056963559e-05 -▁Friedrich 1.1308442056963559e-05 -▁Science 1.1308442056963559e-05 -▁Tests 1.1308442056963559e-05 -▁ré 1.1308442056963559e-05 -▁Allah 1.1304653634163906e-05 -▁Sm 1.1304653634163906e-05 -▁restructuring 1.1304653634163906e-05 -▁strengthened 1.1304653634163906e-05 -▁uncon 1.1304653634163906e-05 -▁villa 1.1304653634163906e-05 -sätze 1.1300865211364253e-05 -▁CON 1.12970767885646e-05 -▁Friendly 1.12970767885646e-05 -▁Sieg 1.12970767885646e-05 -▁Würde 1.12970767885646e-05 -▁Zahlungen 1.12970767885646e-05 -▁qual 1.12970767885646e-05 -▁Rechtsstaatlichkeit 1.1293288365764947e-05 -▁Richard 1.1293288365764947e-05 -▁pas 1.1293288365764947e-05 -▁Disc 1.1289499942965295e-05 -▁Gesam 1.1289499942965295e-05 -▁arise 1.1289499942965295e-05 -esign 1.1285711520165641e-05 -▁begleitet 1.1285711520165641e-05 -▁Öffnung 1.1285711520165641e-05 -▁gefragt 1.1281923097365989e-05 -▁laufenden 1.1281923097365989e-05 -politische 1.1278134674566337e-05 -▁Bour 1.1278134674566337e-05 -▁subs 1.1278134674566337e-05 -▁Notes 1.1274346251766683e-05 -▁unabhängige 1.1274346251766683e-05 -▁56 1.1270557828967031e-05 -atal 1.1266769406167378e-05 -▁hill 1.1266769406167378e-05 -▁solar 1.1266769406167378e-05 -▁spir 1.1266769406167378e-05 -▁Sat 1.1262980983367726e-05 -▁erfordern 1.1259192560568072e-05 -▁everywhere 1.1259192560568072e-05 -rap 1.125540413776842e-05 -▁Easy 1.125540413776842e-05 -▁Verbrauch 1.125540413776842e-05 -▁kompet 1.125540413776842e-05 -▁stattfindet 1.125540413776842e-05 -ained 1.1251615714968768e-05 -▁thermal 1.1251615714968768e-05 -▁wahren 1.1251615714968768e-05 -.2010 1.1247827292169114e-05 -▁Hohe 1.1247827292169114e-05 -▁massage 1.1247827292169114e-05 -lang 1.1244038869369462e-05 -▁angewendet 1.1244038869369462e-05 -▁coordinated 1.1244038869369462e-05 -▁panoramic 1.1244038869369462e-05 -▁wünsche 1.1244038869369462e-05 -komm 1.1240250446569808e-05 -zw 1.1240250446569808e-05 -▁Prim 1.1236462023770156e-05 -▁Schwarz 1.1236462023770156e-05 -▁cook 1.1236462023770156e-05 -▁zugelassen 1.1236462023770156e-05 -▁antworten 1.1232673600970502e-05 -▁grossen 1.1232673600970502e-05 -▁stunning 1.122888517817085e-05 -ties 1.1225096755371197e-05 -ř 1.1225096755371197e-05 -▁Appartement 1.1225096755371197e-05 -▁heißen 1.1225096755371197e-05 -▁vornehmen 1.1225096755371197e-05 -sehen 1.1221308332571545e-05 -▁Quelle 1.1221308332571545e-05 -▁aufs 1.1221308332571545e-05 -34 1.1217519909771893e-05 -▁Haup 1.1217519909771893e-05 -itch 1.1213731486972239e-05 -stag 1.1213731486972239e-05 -systems 1.1213731486972239e-05 -▁editor 1.1213731486972239e-05 -▁rufen 1.1213731486972239e-05 -▁Überblick 1.1213731486972239e-05 -▁Darstellung 1.1209943064172587e-05 -▁Schön 1.1209943064172587e-05 -kreis 1.1206154641372933e-05 -▁Sir 1.1206154641372933e-05 -▁treatments 1.1206154641372933e-05 -▁Marc 1.1202366218573281e-05 -▁infl 1.1202366218573281e-05 -▁Brief 1.1198577795773627e-05 -▁Pakete 1.1194789372973975e-05 -▁Pic 1.1194789372973975e-05 -▁einzuführen 1.1191000950174323e-05 -kette 1.1187212527374669e-05 -▁formats 1.1187212527374669e-05 -▁multinational 1.1187212527374669e-05 -▁ol 1.1187212527374669e-05 -▁Basel 1.1183424104575017e-05 -▁Bereitschaft 1.1183424104575017e-05 -▁Gay 1.1183424104575017e-05 -▁individually 1.1183424104575017e-05 -▁decade 1.1179635681775364e-05 -▁klassischen 1.1175847258975712e-05 -▁neighbouring 1.1175847258975712e-05 -▁province 1.1175847258975712e-05 -▁schnellen 1.1175847258975712e-05 -▁assembly 1.1172058836176058e-05 -▁condemn 1.1172058836176058e-05 -▁incomp 1.1172058836176058e-05 -▁qualitativ 1.1172058836176058e-05 -offen 1.1168270413376406e-05 -▁Sicher 1.1168270413376406e-05 -▁deeply 1.1168270413376406e-05 -▁mehrfach 1.1168270413376406e-05 -▁mittlerweile 1.1168270413376406e-05 -list 1.1164481990576754e-05 -▁Haben 1.1164481990576754e-05 -▁listings 1.1164481990576754e-05 -▁superb 1.1164481990576754e-05 -ili 1.11606935677771e-05 -ünst 1.11606935677771e-05 -▁Weltkrieg 1.11606935677771e-05 -▁Mehrwert 1.1156905144977448e-05 -▁Que 1.1156905144977448e-05 -▁belie 1.1153116722177794e-05 -▁ordered 1.1153116722177794e-05 -uzz 1.1149328299378142e-05 -▁Doha 1.1149328299378142e-05 -▁Light 1.1149328299378142e-05 -open 1.1145539876578488e-05 -ple 1.1145539876578488e-05 -umping 1.1145539876578488e-05 -▁Amts 1.1145539876578488e-05 -▁Bil 1.1145539876578488e-05 -▁H-0 1.1145539876578488e-05 -▁bean 1.1145539876578488e-05 -▁enorm 1.1145539876578488e-05 -▁anniversary 1.1141751453778836e-05 -▁Denmark 1.1137963030979183e-05 -▁geäußert 1.1137963030979183e-05 -▁shuttle 1.1137963030979183e-05 -▁Somit 1.1134174608179531e-05 -▁afford 1.1134174608179531e-05 -▁holds 1.1134174608179531e-05 -▁Mängel 1.1130386185379879e-05 -▁bewältigen 1.1130386185379879e-05 -▁frühen 1.1126597762580225e-05 -ächen 1.1122809339780573e-05 -▁Terror 1.1122809339780573e-05 -▁Thr 1.1122809339780573e-05 -▁briefly 1.1122809339780573e-05 -▁necessarily 1.1122809339780573e-05 -edia 1.1119020916980919e-05 -▁Gre 1.1119020916980919e-05 -▁Quark 1.1119020916980919e-05 -▁acknowledge 1.1119020916980919e-05 -▁eigen 1.1119020916980919e-05 -ägen 1.1115232494181267e-05 -▁approve 1.1115232494181267e-05 -▁bestellen 1.1115232494181267e-05 -▁gestärkt 1.1115232494181267e-05 -▁unge 1.1115232494181267e-05 -▁executive 1.1111444071381613e-05 -▁saving 1.1111444071381613e-05 -aterial 1.1107655648581961e-05 -▁tem 1.1107655648581961e-05 -▁warn 1.1107655648581961e-05 -fähigen 1.1103867225782309e-05 -äte 1.1103867225782309e-05 -▁bathrooms 1.1103867225782309e-05 -echn 1.1100078802982655e-05 -kul 1.1100078802982655e-05 -▁tracks 1.1100078802982655e-05 -öse 1.1096290380183003e-05 -▁Warcraft 1.109250195738335e-05 -▁king 1.109250195738335e-05 --friendly 1.1088713534583698e-05 -▁Straßburg 1.1088713534583698e-05 -▁Wichtig 1.1088713534583698e-05 -▁eingestellt 1.1088713534583698e-05 -▁keinem 1.1088713534583698e-05 -▁rig 1.1088713534583698e-05 -98 1.1084925111784044e-05 -▁Faktor 1.1084925111784044e-05 -osed 1.1081136688984392e-05 -ych 1.1081136688984392e-05 -▁faszin 1.1081136688984392e-05 -▁hiking 1.1081136688984392e-05 -▁ziel 1.1081136688984392e-05 -▁Att 1.1077348266184738e-05 -▁Bearbeitung 1.1077348266184738e-05 -▁Dol 1.1077348266184738e-05 -▁ambition 1.1077348266184738e-05 -▁angebracht 1.1077348266184738e-05 -▁empfohlen 1.1077348266184738e-05 -werfen 1.1073559843385086e-05 -▁Debatten 1.1073559843385086e-05 -▁Niederlanden 1.1073559843385086e-05 -ité 1.1069771420585434e-05 -▁GPS 1.1069771420585434e-05 -▁Russ 1.1069771420585434e-05 -▁Steuerung 1.1069771420585434e-05 -enswert 1.106598299778578e-05 -▁Arbeitgeber 1.106598299778578e-05 -▁URL 1.106598299778578e-05 -▁fairly 1.106598299778578e-05 -▁targeted 1.1062194574986128e-05 -ikanischen 1.1058406152186474e-05 -▁guard 1.1058406152186474e-05 -ustr 1.1054617729386822e-05 -▁Process 1.1054617729386822e-05 -▁Vent 1.1054617729386822e-05 -▁magazine 1.1054617729386822e-05 -▁normally 1.1054617729386822e-05 -▁zerstört 1.1054617729386822e-05 -▁Minute 1.1050829306587169e-05 -▁bauen 1.1050829306587169e-05 -▁schweren 1.1050829306587169e-05 -▁adds 1.1047040883787517e-05 -▁het 1.1047040883787517e-05 -▁manufacture 1.1047040883787517e-05 -67 1.1043252460987865e-05 -▁Saddam 1.1043252460987865e-05 -▁gesetzlichen 1.1043252460987865e-05 -▁selber 1.1043252460987865e-05 -änderungen 1.1039464038188211e-05 -▁Jahrzehnten 1.1039464038188211e-05 -▁Vier 1.1039464038188211e-05 -äß 1.1035675615388559e-05 -▁0. 1.1035675615388559e-05 -▁Petitions 1.1035675615388559e-05 -▁damages 1.1035675615388559e-05 -ritten 1.1031887192588905e-05 -ées 1.1031887192588905e-05 -▁Vereinfachung 1.1031887192588905e-05 -▁teaching 1.1031887192588905e-05 -▁witness 1.1031887192588905e-05 -▁hy 1.1028098769789253e-05 -erte 1.10243103469896e-05 -gesetzt 1.10243103469896e-05 -▁Ticket 1.10243103469896e-05 -▁egal 1.10243103469896e-05 -▁reprod 1.10243103469896e-05 -▁verwende 1.10243103469896e-05 -annen 1.1020521924189947e-05 -▁Parkplätze 1.1020521924189947e-05 -▁sichergestellt 1.1016733501390294e-05 -▁Histor 1.1012945078590641e-05 -▁portfolio 1.1012945078590641e-05 -zeichnungen 1.1009156655790989e-05 -▁Streit 1.1009156655790989e-05 -▁Wandel 1.1009156655790989e-05 -▁excess 1.1009156655790989e-05 -▁Anal 1.1005368232991336e-05 -▁Labour 1.1005368232991336e-05 -▁meal 1.1005368232991336e-05 -▁traum 1.1005368232991336e-05 -▁Kategorien 1.1001579810191684e-05 -▁cond 1.1001579810191684e-05 -richtungen 1.099779138739203e-05 -▁processed 1.099779138739203e-05 -▁Borg 1.0994002964592378e-05 -▁factory 1.0994002964592378e-05 -▁ta 1.0994002964592378e-05 -▁Consequently 1.0990214541792724e-05 -▁Hilf 1.0990214541792724e-05 -▁Konzern 1.0990214541792724e-05 -▁überlassen 1.0990214541792724e-05 -▁überwinden 1.0990214541792724e-05 -▁HIV 1.0986426118993072e-05 -▁reputation 1.098263769619342e-05 -▁selben 1.098263769619342e-05 -DF 1.0978849273393766e-05 -esis 1.0978849273393766e-05 -▁besorgt 1.0978849273393766e-05 -▁decline 1.0978849273393766e-05 -▁digitalen 1.0978849273393766e-05 -bef 1.0975060850594114e-05 -schriften 1.0975060850594114e-05 -▁Bele 1.0975060850594114e-05 -▁SQL 1.0975060850594114e-05 -▁College 1.097127242779446e-05 -▁aw 1.097127242779446e-05 -▁mechan 1.097127242779446e-05 -ettes 1.0967484004994808e-05 -▁twice 1.0967484004994808e-05 -▁wear 1.0967484004994808e-05 -▁Energieeffizienz 1.0963695582195155e-05 -▁olive 1.0963695582195155e-05 -▁Glauben 1.0959907159395503e-05 -▁Tax 1.0959907159395503e-05 -▁smooth 1.0959907159395503e-05 -▁Erhaltung 1.0956118736595851e-05 -▁Search 1.0956118736595851e-05 -▁casino 1.0956118736595851e-05 -▁ergänzt 1.0952330313796197e-05 -▁usage 1.0952330313796197e-05 -gewicht 1.0948541890996545e-05 -▁130 1.0948541890996545e-05 -▁Branche 1.0948541890996545e-05 -▁Gh 1.0948541890996545e-05 -▁betr 1.0948541890996545e-05 -▁separ 1.0944753468196891e-05 -weiten 1.0940965045397239e-05 -▁Belgian 1.0940965045397239e-05 -▁determination 1.0940965045397239e-05 -▁reserved 1.0940965045397239e-05 -▁Zahlung 1.0937176622597585e-05 -▁highlights 1.0937176622597585e-05 -▁türkischen 1.0937176622597585e-05 -▁Zone 1.0933388199797933e-05 -▁engaged 1.0933388199797933e-05 -ordin 1.092959977699828e-05 -▁Nu 1.092959977699828e-05 -▁bonus 1.092959977699828e-05 -▁rechtzeitig 1.092959977699828e-05 -chter 1.0925811354198627e-05 -zahlungen 1.0925811354198627e-05 -▁funktion 1.0925811354198627e-05 -▁presenting 1.0925811354198627e-05 -holen 1.0922022931398975e-05 -rachten 1.0922022931398975e-05 -▁Kommissions 1.0922022931398975e-05 -▁SW 1.0922022931398975e-05 -▁Bestimmung 1.0918234508599322e-05 -▁Koch 1.0918234508599322e-05 -▁Lea 1.0918234508599322e-05 -▁Temperatur 1.0918234508599322e-05 -gaben 1.091444608579967e-05 -region 1.091444608579967e-05 -▁engines 1.091444608579967e-05 -itals 1.0910657663000016e-05 -▁Wirtschaftskrise 1.0910657663000016e-05 -▁unmittelbarer 1.0910657663000016e-05 -▁Daniel 1.0906869240200364e-05 -▁Make 1.0906869240200364e-05 -▁struktur 1.0906869240200364e-05 -gemäß 1.090308081740071e-05 -mitglied 1.090308081740071e-05 -▁Berichterstatters 1.090308081740071e-05 -▁Immer 1.090308081740071e-05 -▁Wal 1.090308081740071e-05 -arts 1.0899292394601058e-05 -ergie 1.0899292394601058e-05 -▁Ferienwohnung 1.0899292394601058e-05 -▁Greens 1.0899292394601058e-05 -▁SL 1.0899292394601058e-05 -▁Si 1.0899292394601058e-05 -▁layout 1.0899292394601058e-05 -▁threats 1.0899292394601058e-05 -üd 1.0895503971801406e-05 -▁Mah 1.0895503971801406e-05 -▁ended 1.0895503971801406e-05 -▁gesetz 1.0895503971801406e-05 -vern 1.0891715549001752e-05 -▁Währungsunion 1.0891715549001752e-05 -unes 1.08879271262021e-05 -▁Investoren 1.08879271262021e-05 -ANT 1.0884138703402447e-05 -heben 1.0884138703402447e-05 -▁350 1.0884138703402447e-05 -▁Deck 1.0884138703402447e-05 -▁Schm 1.0884138703402447e-05 -▁panel 1.0884138703402447e-05 -▁struggle 1.0884138703402447e-05 -▁untersch 1.0884138703402447e-05 -methoden 1.0880350280602794e-05 -▁spezialisiert 1.0880350280602794e-05 -▁Registrierung 1.0876561857803141e-05 -▁110 1.0872773435003489e-05 -▁Porto 1.0872773435003489e-05 -onia 1.0868985012203835e-05 -▁begins 1.0868985012203835e-05 -ATION 1.0865196589404183e-05 -itable 1.0865196589404183e-05 -▁57 1.0865196589404183e-05 -fähige 1.0861408166604531e-05 -▁Arbeiter 1.0861408166604531e-05 -▁Fernsehen 1.0857619743804877e-05 -▁kernel 1.0857619743804877e-05 -▁printed 1.0857619743804877e-05 -ão 1.0853831321005225e-05 -▁herrlichen 1.0853831321005225e-05 -▁Einz 1.0850042898205571e-05 -▁Key 1.0850042898205571e-05 -▁Weil 1.0850042898205571e-05 -▁enorme 1.0850042898205571e-05 -▁Pad 1.0846254475405919e-05 -▁frage 1.0846254475405919e-05 -▁synchron 1.0846254475405919e-05 -▁toll 1.0846254475405919e-05 -▁Zugangs 1.0842466052606266e-05 -onder 1.0838677629806614e-05 -▁RA 1.0838677629806614e-05 -▁geeignete 1.0838677629806614e-05 -ped 1.0834889207006961e-05 -▁Four 1.0834889207006961e-05 -▁Visa 1.0834889207006961e-05 -▁erzeugen 1.0834889207006961e-05 -▁pilot 1.0834889207006961e-05 -▁Parad 1.0831100784207308e-05 -▁besucht 1.0831100784207308e-05 -▁susp 1.0831100784207308e-05 -▁Andererseits 1.0827312361407656e-05 -▁Bö 1.0827312361407656e-05 -▁Zauber 1.0827312361407656e-05 -▁appropriations 1.0827312361407656e-05 -▁beenden 1.0827312361407656e-05 -▁dei 1.0827312361407656e-05 -▁nbsp 1.0827312361407656e-05 -gs 1.0823523938608002e-05 -▁Tickets 1.0823523938608002e-05 -▁diesbezüglich 1.0823523938608002e-05 -▁profits 1.0823523938608002e-05 -strasse 1.081973551580835e-05 -▁lin 1.081973551580835e-05 -▁51 1.0815947093008696e-05 -▁Area 1.0815947093008696e-05 -▁Haar 1.0815947093008696e-05 -▁meets 1.0815947093008696e-05 -▁servers 1.0815947093008696e-05 -arta 1.0812158670209044e-05 -▁Lern 1.0808370247409392e-05 -▁Szen 1.0808370247409392e-05 -▁compar 1.0808370247409392e-05 -▁entering 1.0808370247409392e-05 -▁sim 1.0808370247409392e-05 -▁Gelder 1.0804581824609738e-05 -▁Instruments 1.0804581824609738e-05 -▁Nikon 1.0804581824609738e-05 -erre 1.0800793401810086e-05 -▁Wesentlichen 1.0800793401810086e-05 -▁Wh 1.0800793401810086e-05 -▁eins 1.0800793401810086e-05 -▁griechischen 1.0800793401810086e-05 -▁promised 1.0800793401810086e-05 -▁unr 1.0800793401810086e-05 -▁Society 1.0797004979010433e-05 -atively 1.0793216556210781e-05 -zust 1.0793216556210781e-05 -▁Bug 1.0793216556210781e-05 -▁lasting 1.0793216556210781e-05 -rew 1.0785639710611475e-05 -▁Dresden 1.0785639710611475e-05 -▁Parlamente 1.0785639710611475e-05 -▁assign 1.0785639710611475e-05 -▁combines 1.0785639710611475e-05 -▁intends 1.0785639710611475e-05 -▁Beseitigung 1.0781851287811821e-05 -▁Länge 1.0781851287811821e-05 -▁integrierten 1.0781851287811821e-05 -▁recording 1.0781851287811821e-05 -▁wirken 1.0781851287811821e-05 -arl 1.0778062865012169e-05 -hop 1.0778062865012169e-05 -ikal 1.0778062865012169e-05 -▁Residence 1.0778062865012169e-05 -▁promise 1.0778062865012169e-05 -UP 1.0774274442212517e-05 -round 1.0774274442212517e-05 -▁Sonntag 1.0774274442212517e-05 -▁referring 1.0774274442212517e-05 -▁Ombudsman 1.0770486019412863e-05 -▁rechtliche 1.0770486019412863e-05 -▁opens 1.0766697596613211e-05 -▁prospects 1.0766697596613211e-05 -▁dial 1.0762909173813557e-05 -▁konsequent 1.0762909173813557e-05 -▁produces 1.0762909173813557e-05 -▁toward 1.0762909173813557e-05 -▁Laden 1.0759120751013905e-05 -▁Maschine 1.0759120751013905e-05 -▁Tools 1.0755332328214252e-05 -▁budgets 1.0755332328214252e-05 -▁gleichermaßen 1.0755332328214252e-05 -gebieten 1.07515439054146e-05 -iling 1.07515439054146e-05 -illed 1.07515439054146e-05 -▁Denk 1.0747755482614948e-05 -▁Tonnen 1.0747755482614948e-05 -▁bekannte 1.0747755482614948e-05 -▁soc 1.0747755482614948e-05 -▁urgently 1.0747755482614948e-05 -▁zulassen 1.0747755482614948e-05 -orgen 1.0743967059815294e-05 -äter 1.0743967059815294e-05 -▁efficiently 1.0743967059815294e-05 -prinzip 1.0740178637015642e-05 -▁iPod 1.0740178637015642e-05 -onom 1.0736390214215988e-05 -▁Eigentums 1.0736390214215988e-05 -▁Dam 1.0732601791416336e-05 -▁anzubieten 1.0732601791416336e-05 -▁bestand 1.0732601791416336e-05 -DS 1.0728813368616682e-05 -▁24. 1.0728813368616682e-05 -▁Dingen 1.0728813368616682e-05 -▁execution 1.0728813368616682e-05 -hafter 1.072502494581703e-05 -▁kilometres 1.072502494581703e-05 -linge 1.0721236523017376e-05 -▁ecological 1.0721236523017376e-05 -▁phenomenon 1.0721236523017376e-05 -▁verweisen 1.0721236523017376e-05 -iend 1.0717448100217724e-05 -▁Wiederaufbau 1.0717448100217724e-05 -▁Vertrieb 1.0713659677418072e-05 -ami 1.0709871254618419e-05 -▁Using 1.0709871254618419e-05 -▁mor 1.0709871254618419e-05 -▁Appl 1.0706082831818767e-05 -▁emotional 1.0706082831818767e-05 -▁sole 1.0706082831818767e-05 -mot 1.0702294409019113e-05 -▁arising 1.0702294409019113e-05 -▁gegenwärtige 1.0698505986219461e-05 -▁ländliche 1.0698505986219461e-05 -▁mistake 1.0698505986219461e-05 -▁Bewusstsein 1.0694717563419807e-05 -▁Drei 1.0694717563419807e-05 -▁Zucker 1.0694717563419807e-05 -▁cab 1.0694717563419807e-05 -▁feet 1.0694717563419807e-05 -▁relates 1.0694717563419807e-05 -bend 1.0690929140620155e-05 -eda 1.0690929140620155e-05 -edge 1.0690929140620155e-05 -spolitik 1.0690929140620155e-05 -öss 1.0690929140620155e-05 -▁EU-Mitgliedstaaten 1.0690929140620155e-05 -▁stake 1.0690929140620155e-05 -plans 1.0687140717820503e-05 -using 1.0687140717820503e-05 -▁Polit 1.0687140717820503e-05 -▁errichtet 1.0687140717820503e-05 -▁surprise 1.0687140717820503e-05 -igl 1.0683352295020849e-05 -ül 1.0683352295020849e-05 -iled 1.0679563872221197e-05 -▁Jack 1.0679563872221197e-05 -▁EU-R 1.0675775449421543e-05 -▁Renten 1.0675775449421543e-05 -▁geographical 1.0675775449421543e-05 -▁spreche 1.0675775449421543e-05 -freund 1.0671987026621891e-05 -jan 1.0671987026621891e-05 -miss 1.0671987026621891e-05 -▁Helsinki 1.0671987026621891e-05 -▁Parameter 1.0671987026621891e-05 -ners 1.0668198603822238e-05 -zeuge 1.0668198603822238e-05 -▁KDE 1.0668198603822238e-05 -▁Tauch 1.0668198603822238e-05 -▁camps 1.0668198603822238e-05 -schung 1.0664410181022586e-05 -▁calc 1.0664410181022586e-05 -UB 1.0660621758222932e-05 -▁AF 1.0660621758222932e-05 -▁Lastly 1.065683333542328e-05 -▁Wa 1.065683333542328e-05 -▁Energies 1.0653044912623628e-05 -▁Linien 1.0653044912623628e-05 -▁Verteidigungs 1.0653044912623628e-05 -▁auffordern 1.0653044912623628e-05 -▁essen 1.0653044912623628e-05 -bares 1.0649256489823974e-05 -▁Euros 1.0649256489823974e-05 -▁oldest 1.0649256489823974e-05 -▁stammt 1.0649256489823974e-05 -▁Alm 1.0645468067024322e-05 -▁native 1.0645468067024322e-05 -reibungen 1.0641679644224668e-05 -▁Katalog 1.0641679644224668e-05 -▁Maur 1.0641679644224668e-05 -▁VIP 1.0641679644224668e-05 -▁knapp 1.0641679644224668e-05 -▁lebt 1.0641679644224668e-05 -▁nom 1.0641679644224668e-05 -▁beruhen 1.0637891221425016e-05 -▁froh 1.0637891221425016e-05 -▁occurred 1.0637891221425016e-05 -▁scientists 1.0637891221425016e-05 -97 1.0634102798625362e-05 -▁Journal 1.0634102798625362e-05 -▁Zusammenhalt 1.0634102798625362e-05 -▁wheel 1.0634102798625362e-05 -▁Jugendlichen 1.063031437582571e-05 -▁medicine 1.063031437582571e-05 -▁Have 1.0626525953026058e-05 -▁Höchst 1.0626525953026058e-05 -▁Phot 1.0626525953026058e-05 -▁convert 1.0626525953026058e-05 -oco 1.0622737530226405e-05 -▁Benutzung 1.0622737530226405e-05 -▁Stad 1.0622737530226405e-05 -▁sky 1.0622737530226405e-05 -▁stärkere 1.0622737530226405e-05 -▁acquired 1.0618949107426753e-05 -▁candidates 1.0618949107426753e-05 -▁strictly 1.0618949107426753e-05 -▁untersuchen 1.0618949107426753e-05 -osc 1.0615160684627099e-05 -projekte 1.0615160684627099e-05 -ral 1.0615160684627099e-05 -▁ausgewählt 1.0615160684627099e-05 -▁mouse 1.0615160684627099e-05 -▁records 1.0615160684627099e-05 -▁Sports 1.0611372261827447e-05 -▁hills 1.0611372261827447e-05 -▁tel 1.0611372261827447e-05 -hlen 1.0607583839027793e-05 -weite 1.0607583839027793e-05 -▁highlighted 1.0607583839027793e-05 -▁Fac 1.0603795416228141e-05 -▁depth 1.0603795416228141e-05 -▁seats 1.0603795416228141e-05 -ici 1.0600006993428489e-05 -▁gekennzeichnet 1.0600006993428489e-05 -▁mental 1.0600006993428489e-05 --and 1.0596218570628835e-05 -▁Käufer 1.0596218570628835e-05 -▁negativen 1.0596218570628835e-05 -▁succeeded 1.0596218570628835e-05 -etta 1.0592430147829183e-05 -▁Strecke 1.0592430147829183e-05 -▁Ross 1.0588641725029529e-05 -▁Smart 1.0588641725029529e-05 -▁ide 1.0588641725029529e-05 -▁wel 1.0588641725029529e-05 -gern 1.0584853302229877e-05 -▁meals 1.0584853302229877e-05 -▁schätzen 1.0584853302229877e-05 --off 1.0581064879430224e-05 -▁Equ 1.0581064879430224e-05 -▁liable 1.0581064879430224e-05 -icity 1.0577276456630572e-05 -s. 1.0577276456630572e-05 -▁Religion 1.0577276456630572e-05 -▁unentgeltlich 1.0577276456630572e-05 -eams 1.0573488033830918e-05 -▁Branchen 1.0573488033830918e-05 -▁resulted 1.0573488033830918e-05 -wenden 1.0569699611031266e-05 -▁sah 1.0569699611031266e-05 -▁sophisticated 1.0569699611031266e-05 -uous 1.0565911188231614e-05 -upt 1.0565911188231614e-05 -▁Samstag 1.0565911188231614e-05 -▁beer 1.0565911188231614e-05 -▁essentially 1.0565911188231614e-05 -▁specialists 1.0565911188231614e-05 -▁Industr 1.056212276543196e-05 -▁Vermittlungs 1.056212276543196e-05 -▁tim 1.056212276543196e-05 -A. 1.0558334342632308e-05 -ented 1.0558334342632308e-05 -▁Sn 1.0558334342632308e-05 -▁Call 1.0554545919832654e-05 -▁taxi 1.0554545919832654e-05 -▁Frühjahr 1.0550757497033002e-05 -▁Further 1.0550757497033002e-05 -▁consequence 1.0550757497033002e-05 -▁Š 1.0550757497033002e-05 -▁о 1.0550757497033002e-05 -idence 1.0546969074233349e-05 -zunehmen 1.0546969074233349e-05 -▁Bedürfnissen 1.0546969074233349e-05 -ibel 1.0543180651433696e-05 -▁Rechnungs 1.0543180651433696e-05 -▁evident 1.0543180651433696e-05 -33 1.0539392228634044e-05 -KT 1.0539392228634044e-05 -▁Performance 1.0539392228634044e-05 -▁eventuell 1.0539392228634044e-05 -▁micr 1.0539392228634044e-05 -▁operator 1.0539392228634044e-05 -▁Jesus 1.0535603805834391e-05 -▁Schreiben 1.0535603805834391e-05 -▁hence 1.0535603805834391e-05 -izz 1.0528026960235085e-05 -rain 1.0528026960235085e-05 -▁Sierra 1.0528026960235085e-05 -▁Schwe 1.0524238537435433e-05 -▁complement 1.0524238537435433e-05 -▁mel 1.0524238537435433e-05 -▁Baby 1.0520450114635779e-05 -▁commission 1.0520450114635779e-05 -▁conventional 1.0520450114635779e-05 -▁denjenigen 1.0520450114635779e-05 -▁nieder 1.0520450114635779e-05 -adt 1.0516661691836127e-05 -üm 1.0516661691836127e-05 -▁Versorgung 1.0516661691836127e-05 -▁beinhalten 1.0516661691836127e-05 -ault 1.0512873269036473e-05 -vol 1.0512873269036473e-05 -▁Joint 1.0512873269036473e-05 -▁Journalisten 1.0512873269036473e-05 -▁affects 1.0512873269036473e-05 -▁vorgestellt 1.0512873269036473e-05 -96 1.0509084846236821e-05 -▁Lehrer 1.0509084846236821e-05 -▁Todesstrafe 1.0509084846236821e-05 -▁derzeitige 1.0509084846236821e-05 -▁heading 1.0509084846236821e-05 -▁incorporated 1.0509084846236821e-05 -▁streng 1.0509084846236821e-05 -▁virtually 1.0509084846236821e-05 -▁Suites 1.0505296423437169e-05 -arily 1.0501508000637516e-05 -etzung 1.0501508000637516e-05 -▁airports 1.0497719577837863e-05 -aka 1.049393115503821e-05 -wissenschaft 1.049393115503821e-05 -üren 1.049393115503821e-05 -▁Europ 1.049393115503821e-05 -▁Sicherheitspolitik 1.049393115503821e-05 -▁aviation 1.049393115503821e-05 -▁hierzu 1.049393115503821e-05 -▁Spielen 1.0490142732238558e-05 -▁upgrade 1.0486354309438904e-05 --a 1.0482565886639252e-05 -▁Ablauf 1.0482565886639252e-05 -▁Prin 1.04787774638396e-05 -▁gesp 1.04787774638396e-05 -planung 1.0474989041039946e-05 -▁Bevor 1.0474989041039946e-05 -▁Ham 1.0474989041039946e-05 -▁fly 1.0474989041039946e-05 -gas 1.0471200618240294e-05 -ject 1.0471200618240294e-05 -ober 1.0471200618240294e-05 -uce 1.0471200618240294e-05 -ń 1.0471200618240294e-05 -▁4-star 1.0471200618240294e-05 -▁72 1.0471200618240294e-05 -▁Sex 1.0471200618240294e-05 -▁choices 1.0471200618240294e-05 -▁kontrolliert 1.0471200618240294e-05 -oler 1.046741219544064e-05 -ę 1.046741219544064e-05 -▁Klass 1.046741219544064e-05 -▁considers 1.046741219544064e-05 -▁Entw 1.0463623772640988e-05 -▁gradually 1.0463623772640988e-05 -onym 1.0459835349841335e-05 -▁Berge 1.0459835349841335e-05 -▁offizielle 1.0459835349841335e-05 -inent 1.0456046927041682e-05 -▁App 1.0456046927041682e-05 -▁diplomatic 1.0456046927041682e-05 -▁entscheidenden 1.0456046927041682e-05 -cz 1.0452258504242029e-05 -läss 1.0452258504242029e-05 -▁Mexiko 1.0452258504242029e-05 -▁Vertrages 1.0452258504242029e-05 -▁eigener 1.0452258504242029e-05 -▁gedacht 1.0452258504242029e-05 -▁hidden 1.0452258504242029e-05 -▁rid 1.0452258504242029e-05 -▁senken 1.0452258504242029e-05 --Ein 1.0448470081442377e-05 -emble 1.0448470081442377e-05 -stellungs 1.0448470081442377e-05 -▁stick 1.0444681658642725e-05 -▁Bereits 1.0440893235843071e-05 -▁legend 1.0440893235843071e-05 -▁Ju 1.0437104813043419e-05 -▁verschieden 1.0437104813043419e-05 --Al 1.0433316390243765e-05 -pm 1.0433316390243765e-05 -uth 1.0433316390243765e-05 -▁Maus 1.0433316390243765e-05 -TP 1.0429527967444113e-05 -schte 1.0429527967444113e-05 -▁Dänemark 1.0429527967444113e-05 -▁flu 1.0429527967444113e-05 -ählt 1.0425739544644459e-05 -▁environmentally 1.0425739544644459e-05 -▁voc 1.0425739544644459e-05 -echt 1.0421951121844807e-05 -eins 1.0421951121844807e-05 -▁Kooperations 1.0421951121844807e-05 -▁associations 1.0421951121844807e-05 -▁critic 1.0421951121844807e-05 -▁halb 1.0421951121844807e-05 -▁Verkaufs 1.0418162699045155e-05 -▁ec 1.0418162699045155e-05 -▁Cla 1.0414374276245502e-05 -▁Trib 1.0414374276245502e-05 -▁Umweltfragen 1.0414374276245502e-05 -▁absol 1.0414374276245502e-05 -▁causing 1.0414374276245502e-05 -▁mögen 1.0414374276245502e-05 -▁warning 1.0414374276245502e-05 -▁smart 1.0410585853445849e-05 -▁¼ 1.0410585853445849e-05 -▁Beachtung 1.0406797430646196e-05 -▁Rechnungshof 1.0406797430646196e-05 -▁renoviert 1.0406797430646196e-05 -aß 1.0403009007846544e-05 -ehl 1.0403009007846544e-05 -gent 1.0403009007846544e-05 -▁Auftrags 1.0403009007846544e-05 -▁Cooperation 1.0403009007846544e-05 -▁Kaiser 1.0403009007846544e-05 -▁bedeutenden 1.0403009007846544e-05 -kle 1.039922058504689e-05 -▁Dialogs 1.039922058504689e-05 -▁Gross 1.039922058504689e-05 -▁Sobald 1.039922058504689e-05 -▁abgel 1.039922058504689e-05 -▁praktischen 1.039922058504689e-05 -kund 1.0395432162247238e-05 -▁Zuverlässigkeit 1.0395432162247238e-05 -▁realer 1.0395432162247238e-05 -▁Snow 1.0391643739447586e-05 -▁blocks 1.0391643739447586e-05 -▁producer 1.0391643739447586e-05 -▁Metr 1.0387855316647932e-05 -▁Systemen 1.0387855316647932e-05 -▁befasst 1.0387855316647932e-05 -▁festzustellen 1.0387855316647932e-05 -▁kümmern 1.0387855316647932e-05 -▁verletzt 1.0387855316647932e-05 -flächen 1.038406689384828e-05 -▁Department 1.038406689384828e-05 -▁Ster 1.038406689384828e-05 -▁Wür 1.038406689384828e-05 -▁sphere 1.038406689384828e-05 -▁teachers 1.038406689384828e-05 -▁Mechanismen 1.0380278471048626e-05 -▁Urheber 1.0380278471048626e-05 -▁Venedig 1.0380278471048626e-05 -▁reisen 1.0380278471048626e-05 -lassen 1.0376490048248974e-05 -▁Mode 1.0376490048248974e-05 -▁Vice-President 1.0376490048248974e-05 -▁explanation 1.0376490048248974e-05 -▁isol 1.0376490048248974e-05 -▁vergleich 1.0376490048248974e-05 -▁CEO 1.0372701625449321e-05 -▁Level 1.0372701625449321e-05 -▁Verfassungs 1.0372701625449321e-05 -▁steuer 1.0372701625449321e-05 -zed 1.0368913202649669e-05 -▁Constitutional 1.0368913202649669e-05 -▁ET 1.0368913202649669e-05 -▁Lesen 1.0368913202649669e-05 -▁Ü 1.0368913202649669e-05 -boden 1.0365124779850015e-05 -oven 1.0365124779850015e-05 -▁ON 1.0365124779850015e-05 -▁ordnungsgemäß 1.0365124779850015e-05 -▁Wied 1.0361336357050363e-05 -▁Wissenschaftler 1.0361336357050363e-05 -▁anzunehmen 1.0361336357050363e-05 -recher 1.0357547934250711e-05 -▁Explorer 1.0357547934250711e-05 -▁Register 1.0357547934250711e-05 -▁mot 1.0357547934250711e-05 -▁troops 1.0357547934250711e-05 -▁Dreh 1.0353759511451057e-05 -▁Hindernisse 1.0353759511451057e-05 -▁Jon 1.0353759511451057e-05 -▁widespread 1.0353759511451057e-05 --g 1.0349971088651405e-05 -des 1.0349971088651405e-05 -▁Danish 1.0349971088651405e-05 -▁Labor 1.0349971088651405e-05 -ativer 1.0346182665851751e-05 -ention 1.0346182665851751e-05 -onial 1.0346182665851751e-05 -▁GAP 1.0346182665851751e-05 -▁Three 1.0346182665851751e-05 -▁attached 1.0346182665851751e-05 -▁polit 1.0346182665851751e-05 -▁Ägypten 1.0346182665851751e-05 -▁Fläche 1.0342394243052099e-05 -▁Nokia 1.0342394243052099e-05 -▁minor 1.0342394243052099e-05 -▁offenbar 1.0342394243052099e-05 -iene 1.0338605820252445e-05 -▁encouraged 1.0338605820252445e-05 -▁revenue 1.0338605820252445e-05 -IE 1.0334817397452793e-05 -edi 1.0334817397452793e-05 -ehmen 1.0334817397452793e-05 -▁müsste 1.0334817397452793e-05 -ART 1.0331028974653141e-05 -eich 1.0331028974653141e-05 -ilder 1.0331028974653141e-05 -iva 1.0331028974653141e-05 -▁Franz 1.0331028974653141e-05 -▁bezahlt 1.0331028974653141e-05 -▁vertra 1.0331028974653141e-05 -gerät 1.0327240551853488e-05 -Ö 1.0327240551853488e-05 -▁ends 1.0327240551853488e-05 -▁Hinweise 1.0323452129053836e-05 -igo 1.0319663706254182e-05 -▁Children 1.0319663706254182e-05 -▁berechnet 1.0319663706254182e-05 -▁dance 1.0319663706254182e-05 -▁Internal 1.031587528345453e-05 -▁Sem 1.031587528345453e-05 -▁restricted 1.031587528345453e-05 -▁surprised 1.031587528345453e-05 -▁Mün 1.0312086860654876e-05 -▁Nik 1.0312086860654876e-05 -▁bür 1.0312086860654876e-05 -DP 1.0308298437855224e-05 -down 1.0308298437855224e-05 -entwick 1.0308298437855224e-05 -▁prisoners 1.0308298437855224e-05 -▁soil 1.0308298437855224e-05 -fers 1.030451001505557e-05 -sprach 1.030451001505557e-05 -ukt 1.030451001505557e-05 -ре 1.030451001505557e-05 -▁Ter 1.030451001505557e-05 -▁brachte 1.030451001505557e-05 -▁produzieren 1.030451001505557e-05 -▁wettbewerbs 1.030451001505557e-05 -▁adding 1.0300721592255918e-05 -▁participating 1.0300721592255918e-05 -▁reporting 1.0300721592255918e-05 -grenzen 1.0296933169456266e-05 -regelung 1.0296933169456266e-05 -▁Ausgang 1.0296933169456266e-05 -▁Senkung 1.0296933169456266e-05 -▁colors 1.0296933169456266e-05 -▁typischen 1.0296933169456266e-05 -CD 1.0293144746656612e-05 -▁Vertretern 1.0293144746656612e-05 -▁Wirksamkeit 1.0293144746656612e-05 -▁accidents 1.0293144746656612e-05 -▁seeks 1.0293144746656612e-05 -cil 1.028935632385696e-05 -konferenz 1.028935632385696e-05 -kons 1.028935632385696e-05 -uite 1.028935632385696e-05 -▁contextual 1.028935632385696e-05 -kel 1.0285567901057307e-05 -▁Roth 1.0285567901057307e-05 -▁angemessenen 1.0285567901057307e-05 -▁bomb 1.0285567901057307e-05 -▁puts 1.0285567901057307e-05 -ikel 1.0281779478257655e-05 -▁Appartements 1.0281779478257655e-05 -▁GP 1.0281779478257655e-05 -▁engage 1.0281779478257655e-05 -gra 1.0277991055458e-05 -▁Kohäsionspolitik 1.0277991055458e-05 -▁appl 1.0277991055458e-05 -gehende 1.0274202632658349e-05 -▁Grün 1.0274202632658349e-05 -▁Premierminister 1.0274202632658349e-05 -▁Schlacht 1.0274202632658349e-05 -▁Bü 1.0270414209858697e-05 -▁Bücher 1.0270414209858697e-05 -▁wechseln 1.0270414209858697e-05 -aken 1.0266625787059043e-05 -flow 1.0266625787059043e-05 -▁Aud 1.0266625787059043e-05 -▁Bildschirm 1.0266625787059043e-05 -▁IMF 1.0266625787059043e-05 -▁Obst 1.0266625787059043e-05 -▁geregelt 1.0266625787059043e-05 -ilation 1.0262837364259391e-05 -▁hal 1.0262837364259391e-05 -setzt 1.0259048941459737e-05 -▁Ergebnissen 1.0259048941459737e-05 -▁blo 1.0259048941459737e-05 -▁wooden 1.0259048941459737e-05 -rd 1.0255260518660085e-05 -ulen 1.0255260518660085e-05 -▁niedrig 1.0255260518660085e-05 --Re 1.0251472095860431e-05 -ourse 1.0251472095860431e-05 -ply 1.0251472095860431e-05 -▁22. 1.0251472095860431e-05 -ichtete 1.0247683673060779e-05 -▁alongside 1.0247683673060779e-05 -▁stelle 1.0247683673060779e-05 -▁Ansprüche 1.0243895250261127e-05 -▁Terms 1.0243895250261127e-05 -▁clarify 1.0243895250261127e-05 -▁erklärte 1.0243895250261127e-05 -▁fach 1.0243895250261127e-05 -raten 1.0240106827461474e-05 -▁20th 1.0240106827461474e-05 -▁Anwendungs 1.0240106827461474e-05 -▁Besuchen 1.0240106827461474e-05 -▁Conc 1.0240106827461474e-05 -▁Events 1.0240106827461474e-05 -▁incl 1.0240106827461474e-05 -▁utmost 1.0240106827461474e-05 -ivers 1.0236318404661822e-05 -undes 1.0236318404661822e-05 -▁Authority 1.0236318404661822e-05 -▁richtet 1.0236318404661822e-05 -▁stories 1.0236318404661822e-05 -wegen 1.0232529981862168e-05 -▁Schlaf 1.0232529981862168e-05 -▁Spezialitäten 1.0232529981862168e-05 -▁coun 1.0232529981862168e-05 -▁inspir 1.0232529981862168e-05 -örder 1.0228741559062516e-05 -▁Wohnzimmer 1.0228741559062516e-05 -▁marks 1.0228741559062516e-05 -ictions 1.0224953136262862e-05 -änger 1.0224953136262862e-05 -▁agents 1.0224953136262862e-05 -▁angewandt 1.0224953136262862e-05 -▁stil 1.0224953136262862e-05 -rechten 1.022116471346321e-05 -▁bug 1.022116471346321e-05 -ässt 1.0217376290663556e-05 -то 1.0217376290663556e-05 -▁Finnland 1.0217376290663556e-05 -▁Hu 1.0217376290663556e-05 -▁Verwirklichung 1.0217376290663556e-05 -▁infolge 1.0217376290663556e-05 -▁prize 1.0217376290663556e-05 -versammlung 1.0213587867863904e-05 -ünden 1.0213587867863904e-05 -▁graphics 1.0213587867863904e-05 -27 1.0209799445064252e-05 -ensive 1.0209799445064252e-05 -▁Fahrer 1.0209799445064252e-05 -▁Komp 1.0209799445064252e-05 -▁clock 1.0209799445064252e-05 -▁irischen 1.0209799445064252e-05 -▁trib 1.0209799445064252e-05 -fter 1.0206011022264598e-05 -▁grown 1.0206011022264598e-05 -anken 1.0202222599464946e-05 -▁Aufzug 1.0202222599464946e-05 -▁Fang 1.0202222599464946e-05 -▁vorn 1.0202222599464946e-05 -atorium 1.0198434176665293e-05 -gendes 1.0198434176665293e-05 -▁Int 1.0198434176665293e-05 -▁RSS 1.0198434176665293e-05 -▁älteren 1.0198434176665293e-05 -▁Long 1.0194645753865641e-05 -▁aktiven 1.0194645753865641e-05 -▁serving 1.0194645753865641e-05 -anforderungen 1.0190857331065987e-05 -plant 1.0190857331065987e-05 -▁Florence 1.0190857331065987e-05 -▁achievement 1.0190857331065987e-05 -▁harmful 1.0190857331065987e-05 -▁langfristigen 1.0190857331065987e-05 -▁observed 1.0190857331065987e-05 -▁wachsenden 1.0190857331065987e-05 -▁wesentlicher 1.0190857331065987e-05 -▁BR 1.0187068908266335e-05 -▁Datum 1.0187068908266335e-05 -▁Treaties 1.0187068908266335e-05 -▁gefährdet 1.0187068908266335e-05 -▁preserve 1.0187068908266335e-05 -▁quote 1.0187068908266335e-05 -▁soeben 1.0183280485466683e-05 -▁Leipzig 1.0179492062667029e-05 -▁ausgegeben 1.0179492062667029e-05 -▁indoor 1.0179492062667029e-05 -▁jedenfalls 1.0179492062667029e-05 -aser 1.0175703639867377e-05 -rich 1.0175703639867377e-05 -▁Drittel 1.0175703639867377e-05 -▁Jerusalem 1.0175703639867377e-05 -▁XML 1.0175703639867377e-05 -▁spite 1.0175703639867377e-05 -▁stayed 1.0175703639867377e-05 -FT 1.0171915217067723e-05 -teilung 1.0171915217067723e-05 -ariat 1.0168126794268071e-05 -▁Firefox 1.0168126794268071e-05 -▁restrict 1.0168126794268071e-05 -▁specialized 1.0168126794268071e-05 -probleme 1.0164338371468417e-05 -▁Operation 1.0160549948668765e-05 -▁Ring 1.0160549948668765e-05 -▁effektive 1.0160549948668765e-05 -▁müssten 1.0160549948668765e-05 -▁pillar 1.0160549948668765e-05 -▁politisches 1.0160549948668765e-05 -reff 1.0156761525869112e-05 -▁Anmeldung 1.0156761525869112e-05 -▁Install 1.0156761525869112e-05 -▁permission 1.0156761525869112e-05 -▁Übertragung 1.0156761525869112e-05 -fühl 1.015297310306946e-05 -itis 1.015297310306946e-05 -▁Werb 1.015297310306946e-05 -▁capabilities 1.015297310306946e-05 -▁favor 1.015297310306946e-05 -▁permit 1.015297310306946e-05 -▁quotas 1.015297310306946e-05 -usst 1.0149184680269808e-05 -▁Safe 1.0149184680269808e-05 -▁recognize 1.0149184680269808e-05 -▁Aussage 1.0145396257470154e-05 -▁Automobil 1.0145396257470154e-05 -▁Edinburgh 1.0145396257470154e-05 -▁Norm 1.0145396257470154e-05 -▁Oberfläche 1.0145396257470154e-05 -▁illegale 1.0145396257470154e-05 -▁Entfernung 1.0141607834670502e-05 -▁know-how 1.0141607834670502e-05 -▁OK 1.0137819411870848e-05 -▁een 1.0137819411870848e-05 -▁23. 1.0130242566271542e-05 -▁anzuwenden 1.0130242566271542e-05 -▁wirkt 1.0130242566271542e-05 -▁conv 1.012645414347189e-05 -wick 1.0122665720672238e-05 -▁'m 1.0122665720672238e-05 -▁convention 1.0122665720672238e-05 -▁convergence 1.0122665720672238e-05 -▁dauert 1.0122665720672238e-05 -wirken 1.0118877297872584e-05 -▁Airlines 1.0115088875072932e-05 -▁Amerikaner 1.0115088875072932e-05 -▁soul 1.0115088875072932e-05 -▁maximal 1.0111300452273279e-05 -▁purely 1.0111300452273279e-05 --ch 1.0107512029473627e-05 -▁passing 1.0107512029473627e-05 -endet 1.0103723606673973e-05 -oom 1.0103723606673973e-05 -oses 1.0103723606673973e-05 -▁SM 1.0103723606673973e-05 -▁Einen 1.0099935183874321e-05 -▁Präsenz 1.0099935183874321e-05 -▁Ts 1.0099935183874321e-05 -arbeiter 1.0096146761074667e-05 -irm 1.0096146761074667e-05 -▁Tyrol 1.0096146761074667e-05 -tragen 1.0092358338275015e-05 -▁armed 1.0092358338275015e-05 -▁ausgewogen 1.0092358338275015e-05 -▁steigern 1.0092358338275015e-05 -▁dispute 1.0088569915475363e-05 -schuss 1.0084781492675709e-05 -var 1.0084781492675709e-05 -▁Vorrang 1.0084781492675709e-05 -▁conferences 1.0084781492675709e-05 -▁supplement 1.0084781492675709e-05 -ж 1.0080993069876057e-05 -▁Stre 1.0080993069876057e-05 -▁dat 1.0080993069876057e-05 -▁effiziente 1.0080993069876057e-05 -▁seconds 1.0077204647076404e-05 -▁weisen 1.0077204647076404e-05 -achtung 1.0073416224276751e-05 -▁Handy 1.0073416224276751e-05 -▁Haupts 1.0073416224276751e-05 -ipped 1.0069627801477098e-05 -ackt 1.0065839378677446e-05 -ader 1.0065839378677446e-05 -steigen 1.0065839378677446e-05 -▁Errichtung 1.0065839378677446e-05 -▁Id 1.0065839378677446e-05 -▁Mandat 1.0065839378677446e-05 -▁Premium 1.0065839378677446e-05 -etern 1.0062050955877794e-05 -ipl 1.0062050955877794e-05 -▁Ast 1.0062050955877794e-05 -▁Chile 1.0062050955877794e-05 -▁Minderheit 1.0062050955877794e-05 -▁Prozesses 1.0062050955877794e-05 -▁Staats- 1.0062050955877794e-05 -▁Zy 1.0062050955877794e-05 -▁beeinflussen 1.0062050955877794e-05 -▁inspired 1.0062050955877794e-05 -▁Strände 1.005826253307814e-05 -oß 1.0054474110278488e-05 -ups 1.0054474110278488e-05 -äufe 1.0054474110278488e-05 -▁Modernisierung 1.0054474110278488e-05 -▁freut 1.0054474110278488e-05 -fahrer 1.0050685687478834e-05 -ouses 1.0050685687478834e-05 -▁estimated 1.0050685687478834e-05 -▁orient 1.0050685687478834e-05 -aktivitäten 1.0046897264679182e-05 -▁Siemens 1.0046897264679182e-05 -▁communicate 1.0046897264679182e-05 -▁manifest 1.0046897264679182e-05 -vorschlag 1.0039320419079876e-05 -▁Kop 1.0039320419079876e-05 -▁ausüben 1.0039320419079876e-05 -▁composition 1.0035531996280224e-05 -▁hervorragend 1.0035531996280224e-05 -active 1.0031743573480571e-05 -▁Regulierung 1.0031743573480571e-05 -▁flows 1.0031743573480571e-05 -▁humanitären 1.0027955150680918e-05 -▁Benutzern 1.0024166727881265e-05 -▁Cuba 1.0024166727881265e-05 -▁berühmt 1.0024166727881265e-05 -▁schwierige 1.0024166727881265e-05 -view 1.0020378305081613e-05 -▁Truppen 1.0020378305081613e-05 -▁festlegen 1.0020378305081613e-05 -uppe 1.0016589882281959e-05 -▁Burma 1.0016589882281959e-05 -▁oftmals 1.0016589882281959e-05 -center 1.0012801459482307e-05 -▁Tab 1.0012801459482307e-05 -ühr 1.0009013036682653e-05 -▁Extra 1.0009013036682653e-05 -▁Vorstellungen 1.0009013036682653e-05 -▁Betreuung 1.0005224613883001e-05 -▁Innovationen 1.0005224613883001e-05 -▁Rub 1.0005224613883001e-05 -▁coherent 1.0005224613883001e-05 -▁terr 1.0005224613883001e-05 -gestaltung 9.9976477682836953e-06 -lig 9.9976477682836953e-06 -▁Finde 9.9976477682836953e-06 -DR 9.9862824998847375e-06 -o- 9.9862824998847375e-06 -▁Fan 9.9862824998847375e-06 -▁Source 9.9862824998847375e-06 -▁Tele 9.9862824998847375e-06 -▁dollar 9.9862824998847375e-06 -▁europäisches 9.9862824998847375e-06 -▁teach 9.9862824998847375e-06 -▁wirkliche 9.9862824998847375e-06 -für 9.9824940770850838e-06 -▁1,5 9.9824940770850838e-06 -▁integrate 9.9824940770850838e-06 --day 9.9787056542854317e-06 -essed 9.9787056542854317e-06 -▁airlines 9.9787056542854317e-06 -regierung 9.97491723148578e-06 -tain 9.97491723148578e-06 -▁Contact 9.97491723148578e-06 -▁Muster 9.97491723148578e-06 -▁developers 9.97491723148578e-06 -▁Übersicht 9.97491723148578e-06 -anen 9.9711288086861259e-06 -▁Subject 9.9711288086861259e-06 -▁busy 9.9711288086861259e-06 -▁flag 9.9711288086861259e-06 -▁maxim 9.9711288086861259e-06 -BA 9.9673403858864739e-06 -▁LAN 9.9673403858864739e-06 -▁initially 9.9673403858864739e-06 -▁vorliegen 9.9673403858864739e-06 -▁zeichnet 9.9673403858864739e-06 -▁Innovations 9.96355196308682e-06 -▁interess 9.96355196308682e-06 -▁schm 9.96355196308682e-06 -amente 9.9597635402871681e-06 -iberal 9.9597635402871681e-06 -▁Außen- 9.9597635402871681e-06 -▁adult 9.9597635402871681e-06 -▁Bell 9.9559751174875144e-06 -▁Teams 9.9559751174875144e-06 -▁victory 9.9559751174875144e-06 -▁Jazz 9.9521866946878623e-06 -leb 9.9483982718882086e-06 -▁Major 9.9483982718882086e-06 -▁Solar 9.9483982718882086e-06 -▁Versammlung 9.9483982718882086e-06 -▁percentage 9.9483982718882086e-06 -alled 9.9446098490885565e-06 -uern 9.9446098490885565e-06 -▁abgegeben 9.9446098490885565e-06 -▁ansehen 9.9446098490885565e-06 -▁automat 9.9446098490885565e-06 -▁zweitens 9.9446098490885565e-06 --Software 9.9408214262889045e-06 -artige 9.9408214262889045e-06 -icke 9.9408214262889045e-06 -ktionen 9.9408214262889045e-06 -▁Engineering 9.9408214262889045e-06 -▁Mond 9.9408214262889045e-06 -▁raising 9.9408214262889045e-06 -▁thorough 9.9408214262889045e-06 -SC 9.93703300348925e-06 -▁stores 9.93703300348925e-06 -ishes 9.9332445806895987e-06 -▁Ratschlag 9.9332445806895987e-06 -▁diff 9.9332445806895987e-06 -▁string 9.9332445806895987e-06 -rating 9.929456157889945e-06 -▁Eind 9.929456157889945e-06 -▁Weiterentwicklung 9.929456157889945e-06 -▁Wood 9.929456157889945e-06 -▁geschäft 9.929456157889945e-06 -▁Mädchen 9.9256677350902929e-06 -▁consum 9.9256677350902929e-06 -▁vorzunehmen 9.9256677350902929e-06 -ications 9.92187931229064e-06 -▁Camping 9.92187931229064e-06 -▁italienische 9.92187931229064e-06 -ourn 9.9180908894909871e-06 -▁Jedoch 9.9180908894909871e-06 -▁clim 9.9180908894909871e-06 -▁momentan 9.9180908894909871e-06 -BS 9.9143024666913351e-06 -▁Genau 9.9143024666913351e-06 -▁mill 9.9143024666913351e-06 -▁zurückzu 9.9105140438916814e-06 -UL 9.90672562109203e-06 -▁Riviera 9.90672562109203e-06 -▁medieval 9.90672562109203e-06 -issions 9.9029371982923756e-06 -▁Arc 9.9029371982923756e-06 -▁Ausbau 9.9029371982923756e-06 -▁behält 9.9029371982923756e-06 -▁lebend 9.9029371982923756e-06 -▁wachsen 9.9029371982923756e-06 -▁Ausarbeitung 9.8991487754927235e-06 -▁Fast 9.8991487754927235e-06 -▁s. 9.8991487754927235e-06 -iale 9.89536035269307e-06 -kan 9.89536035269307e-06 -▁brea 9.89536035269307e-06 -▁kennt 9.89536035269307e-06 -▁maybe 9.89536035269307e-06 -▁multi 9.89536035269307e-06 -▁sufficiently 9.89536035269307e-06 -ived 9.8915719298934178e-06 -▁Pak 9.8915719298934178e-06 -▁Räume 9.8915719298934178e-06 -▁accounting 9.8915719298934178e-06 -▁operates 9.8915719298934178e-06 --star 9.8877835070937657e-06 -inet 9.8877835070937657e-06 -▁Wirk 9.8877835070937657e-06 -▁abst 9.8877835070937657e-06 -▁hopes 9.8877835070937657e-06 -▁Betriebe 9.883995084294112e-06 -▁trat 9.883995084294112e-06 -▁Got 9.88020666149446e-06 -▁Palma 9.88020666149446e-06 -imin 9.8764182386948062e-06 -▁Stiftung 9.8764182386948062e-06 -▁Doppelzimmer 9.8726298158951541e-06 -▁Normal 9.8726298158951541e-06 -▁Trend 9.8726298158951541e-06 -▁B5-0 9.8688413930955e-06 -▁Fot 9.8688413930955e-06 --Service 9.8650529702958484e-06 -▁angenehmen 9.8650529702958484e-06 -griffen 9.8612645474961946e-06 -losigkeit 9.8612645474961946e-06 -▁fehlen 9.8612645474961946e-06 -oria 9.8574761246965426e-06 -rü 9.8574761246965426e-06 -▁Multi 9.8574761246965426e-06 -▁promises 9.8574761246965426e-06 -▁prospect 9.8574761246965426e-06 -▁sec 9.8574761246965426e-06 -▁1988 9.85368770189689e-06 -▁absurd 9.85368770189689e-06 -▁ear 9.85368770189689e-06 -▁Gebühren 9.8498992790972368e-06 -▁erkannt 9.8498992790972368e-06 -▁expense 9.8498992790972368e-06 -▁Flughäfen 9.8461108562975847e-06 -▁professionelle 9.8461108562975847e-06 --Bericht 9.842322433497931e-06 --Pro 9.842322433497931e-06 -iga 9.842322433497931e-06 -uchs 9.842322433497931e-06 -ützt 9.842322433497931e-06 -▁12.00 9.842322433497931e-06 -▁Dritt 9.842322433497931e-06 -▁GR 9.842322433497931e-06 -▁Psych 9.842322433497931e-06 -fil 9.838534010698279e-06 -agg 9.8347455878986252e-06 -▁Desktop 9.8347455878986252e-06 -▁Sof 9.8347455878986252e-06 -▁Sä 9.8347455878986252e-06 -▁guided 9.8347455878986252e-06 -klasse 9.8309571650989732e-06 -▁edited 9.8309571650989732e-06 -▁enjoys 9.8309571650989732e-06 -▁sammeln 9.8309571650989732e-06 -dauer 9.8271687422993211e-06 -ember 9.8271687422993211e-06 -ο 9.8271687422993211e-06 -▁Optim 9.8271687422993211e-06 -▁Traditionen 9.8271687422993211e-06 -▁gewählten 9.8271687422993211e-06 -▁offiziell 9.8271687422993211e-06 -quellen 9.8233803194996674e-06 -▁Bibliothek 9.8233803194996674e-06 -▁survey 9.8233803194996674e-06 --10 9.8195918967000154e-06 -` 9.8195918967000154e-06 -eater 9.8195918967000154e-06 -wertung 9.8195918967000154e-06 -far 9.81201505110071e-06 -ängt 9.81201505110071e-06 -▁AKP 9.81201505110071e-06 -▁shortly 9.81201505110071e-06 -▁Dan 9.8082266283010558e-06 -▁recall 9.8082266283010558e-06 -ping 9.8044382055014038e-06 -▁AU 9.8044382055014038e-06 -▁NICHT 9.8044382055014038e-06 -▁Valencia 9.8044382055014038e-06 -ë 9.80064978270175e-06 -▁seemed 9.80064978270175e-06 -▁Index 9.796861359902098e-06 -▁kostenfrei 9.796861359902098e-06 -▁kill 9.793072937102446e-06 -▁sou 9.793072937102446e-06 -▁studied 9.793072937102446e-06 -INE 9.7892845143027922e-06 -gericht 9.7892845143027922e-06 -▁Copyright 9.7892845143027922e-06 -▁Zuge 9.7892845143027922e-06 -▁acceler 9.7892845143027922e-06 -▁adventure 9.7892845143027922e-06 -▁versteht 9.7892845143027922e-06 -▁wunder 9.7892845143027922e-06 -egt 9.78549609150314e-06 -union 9.78549609150314e-06 -▁Alp 9.78549609150314e-06 -▁Spitze 9.78549609150314e-06 -▁Werden 9.78549609150314e-06 -▁approaches 9.78549609150314e-06 -▁entfernen 9.78549609150314e-06 -▁implications 9.78549609150314e-06 -ausgaben 9.7817076687034864e-06 -von 9.7817076687034864e-06 -▁Ven 9.7817076687034864e-06 -▁audience 9.7817076687034864e-06 -so 9.7779192459038344e-06 -▁2.5 9.7779192459038344e-06 -▁broken 9.7779192459038344e-06 -▁crises 9.7779192459038344e-06 -▁forth 9.7779192459038344e-06 -achst 9.77413082310418e-06 -nel 9.77413082310418e-06 -roc 9.77413082310418e-06 -▁Samsung 9.77413082310418e-06 --to- 9.7703424003045286e-06 -▁Domin 9.7703424003045286e-06 -▁Orten 9.7703424003045286e-06 -▁Times 9.7703424003045286e-06 -▁classical 9.7703424003045286e-06 -▁derer 9.7703424003045286e-06 -bet 9.7665539775048766e-06 -ägt 9.7665539775048766e-06 -▁Tieren 9.7665539775048766e-06 -▁erteilt 9.7665539775048766e-06 -ld 9.7627655547052228e-06 -vertr 9.7627655547052228e-06 -▁Exper 9.7627655547052228e-06 -▁Lieferanten 9.7627655547052228e-06 -ulator 9.75897713190557e-06 -▁branch 9.75897713190557e-06 -▁extending 9.75897713190557e-06 -▁pod 9.75897713190557e-06 -▁render 9.75897713190557e-06 -▁Nah 9.755188709105917e-06 -▁Pot 9.755188709105917e-06 -▁relationships 9.755188709105917e-06 -▁beabsichtigt 9.751400286306265e-06 -▁staying 9.751400286306265e-06 -altungen 9.7476118635066113e-06 -▁1950 9.7476118635066113e-06 -▁Arbeitnehmern 9.7476118635066113e-06 -▁Bier 9.7476118635066113e-06 -▁Emissionen 9.7476118635066113e-06 -ovo 9.74382344070696e-06 -▁Gro 9.74382344070696e-06 -▁assigned 9.74382344070696e-06 -▁exhaust 9.74382344070696e-06 -▁gradu 9.74382344070696e-06 -▁meaningfully 9.74382344070696e-06 -▁täglichen 9.74382344070696e-06 --00 9.7400350179073055e-06 -▁Ausblick 9.7400350179073055e-06 -▁Mul 9.7400350179073055e-06 -ituation 9.7362465951076534e-06 -wood 9.7362465951076534e-06 -▁prompt 9.7324581723080014e-06 -▁schemes 9.7324581723080014e-06 -▁vollständigen 9.7324581723080014e-06 -66 9.7286697495083476e-06 -▁Compl 9.7286697495083476e-06 -ulating 9.7248813267086956e-06 -▁Blair 9.7210929039090419e-06 -▁baby 9.7210929039090419e-06 -▁principal 9.7210929039090419e-06 -▁weltweite 9.7210929039090419e-06 -hersteller 9.71730448110939e-06 -▁Café 9.71730448110939e-06 -▁Koordin 9.71730448110939e-06 -▁Zwar 9.71730448110939e-06 -▁booked 9.71730448110939e-06 -▁zuges 9.71730448110939e-06 -▁believed 9.7135160583097361e-06 -etzten 9.709727635510084e-06 -▁Gemeinschaften 9.709727635510084e-06 -▁Philosophie 9.709727635510084e-06 -▁Vordergrund 9.709727635510084e-06 -▁anwenden 9.709727635510084e-06 -ión 9.705939212710432e-06 -▁Pflege 9.7021507899107783e-06 -▁Rev 9.6983623671111262e-06 -▁describe 9.6983623671111262e-06 -iebs 9.69078552151182e-06 -▁Meg 9.69078552151182e-06 -▁ausger 9.69078552151182e-06 -▁bought 9.69078552151182e-06 -outh 9.6869970987121667e-06 -eits 9.6832086759125146e-06 -gangen 9.6832086759125146e-06 -ohl 9.6832086759125146e-06 -ract 9.6832086759125146e-06 -▁exhib 9.6832086759125146e-06 -eters 9.6794202531128626e-06 -▁Listings 9.6794202531128626e-06 -▁negotiation 9.6794202531128626e-06 -irk 9.6756318303132089e-06 -ype 9.6756318303132089e-06 -▁Wunder 9.6756318303132089e-06 -▁ehemalige 9.6756318303132089e-06 -▁eliminate 9.6756318303132089e-06 -▁enthalt 9.6756318303132089e-06 -▁kostenlose 9.6756318303132089e-06 -▁vorgelegten 9.6756318303132089e-06 -▁umgehen 9.6718434075135568e-06 -▁unf 9.6718434075135568e-06 -▁gefährlich 9.6680549847139031e-06 -▁Argumente 9.664266561914251e-06 -▁Serbien 9.664266561914251e-06 -▁Unterkünfte 9.664266561914251e-06 -▁genaue 9.664266561914251e-06 -▁geniessen 9.664266561914251e-06 -▁zweit 9.664266561914251e-06 -▁IWF 9.6604781391145973e-06 -▁LCD 9.6604781391145973e-06 -▁bereitet 9.6604781391145973e-06 -▁satisfactory 9.6604781391145973e-06 -kontroll 9.6566897163149453e-06 -▁Industrial 9.6566897163149453e-06 -▁Lot 9.6566897163149453e-06 -▁aktiviert 9.6566897163149453e-06 -▁günstig 9.6566897163149453e-06 -indungen 9.6529012935152915e-06 -ugs 9.6529012935152915e-06 -▁Kh 9.6529012935152915e-06 -▁constitutes 9.6529012935152915e-06 -▁ingredients 9.6529012935152915e-06 -▁repeated 9.6529012935152915e-06 -▁verringert 9.6529012935152915e-06 -▁Nigeria 9.64911287071564e-06 -▁Revision 9.64911287071564e-06 -▁earn 9.64911287071564e-06 -▁geblieben 9.64911287071564e-06 -▁linken 9.64911287071564e-06 -▁Außenminister 9.6453244479159874e-06 -▁Danach 9.6453244479159874e-06 -▁Sekunden 9.6453244479159874e-06 -45 9.6415360251163337e-06 -gress 9.6415360251163337e-06 -icient 9.6415360251163337e-06 -äck 9.6415360251163337e-06 -▁3, 9.6415360251163337e-06 -▁Konkurrenz 9.6415360251163337e-06 -▁nomin 9.6415360251163337e-06 -▁owned 9.6415360251163337e-06 -EE 9.6377476023166816e-06 -ummer 9.6377476023166816e-06 -zur 9.6377476023166816e-06 -▁Kampagne 9.6377476023166816e-06 -▁LE 9.6377476023166816e-06 -▁Weit 9.6377476023166816e-06 -▁bewerten 9.6377476023166816e-06 -▁neighbours 9.6377476023166816e-06 -▁scheduled 9.6377476023166816e-06 -ischt 9.6339591795170279e-06 -▁adv 9.6339591795170279e-06 -▁amended 9.6339591795170279e-06 -▁beseitigen 9.6339591795170279e-06 -▁entdeckst 9.6339591795170279e-06 -▁kontextuellen 9.6339591795170279e-06 -waffen 9.6301707567173759e-06 -▁Annehmlichkeiten 9.6301707567173759e-06 -▁Beschluß 9.6301707567173759e-06 -▁heads 9.6301707567173759e-06 -▁OLAF 9.6263823339177221e-06 -▁glaub 9.6263823339177221e-06 -▁hundert 9.6263823339177221e-06 -▁finanzieren 9.62259391111807e-06 -ippe 9.618805488318418e-06 -▁Order 9.618805488318418e-06 -▁bal 9.618805488318418e-06 -▁profess 9.618805488318418e-06 -▁splendid 9.618805488318418e-06 -berger 9.6150170655187643e-06 -ision 9.6150170655187643e-06 -▁Employment 9.6150170655187643e-06 -▁organise 9.6150170655187643e-06 -▁surprising 9.6150170655187643e-06 -92 9.6112286427191122e-06 -ighter 9.6112286427191122e-06 -▁poorest 9.6112286427191122e-06 -▁überwachen 9.6112286427191122e-06 -▁medicines 9.6074402199194585e-06 -iebe 9.6036517971198065e-06 -inem 9.6036517971198065e-06 -uden 9.6036517971198065e-06 -▁Bla 9.6036517971198065e-06 -▁Düsseldorf 9.6036517971198065e-06 -▁Bildern 9.5998633743201527e-06 -▁campaigns 9.5998633743201527e-06 -▁chapter 9.5998633743201527e-06 -▁collabor 9.5998633743201527e-06 -gebaut 9.5922865287208469e-06 -▁addressing 9.5922865287208469e-06 -funktion 9.5884981059211949e-06 -ician 9.5884981059211949e-06 -▁Vermögens 9.5884981059211949e-06 -▁grammatically 9.5884981059211949e-06 -▁südlichen 9.5884981059211949e-06 --Bereich 9.5847096831215429e-06 -asche 9.5847096831215429e-06 -nte 9.5847096831215429e-06 -▁Korr 9.5847096831215429e-06 -▁checked 9.5809212603218891e-06 -▁commit 9.5809212603218891e-06 -▁contextually 9.5809212603218891e-06 -▁eingebracht 9.5809212603218891e-06 -▁kontextuell 9.5809212603218891e-06 -▁manchen 9.5809212603218891e-06 -well 9.5771328375222371e-06 -▁Ausbildungs 9.5771328375222371e-06 -▁Empire 9.5771328375222371e-06 -▁concentration 9.5771328375222371e-06 -▁hinzufugen 9.5771328375222371e-06 -▁milit 9.5771328375222371e-06 -▁poker 9.5771328375222371e-06 -▁remarkable 9.5771328375222371e-06 -▁whereby 9.5771328375222371e-06 -instrumente 9.5733444147225833e-06 -kommt 9.5733444147225833e-06 -▁Ach 9.5733444147225833e-06 -▁Chairman 9.5733444147225833e-06 -▁Einer 9.5733444147225833e-06 -▁inn 9.5733444147225833e-06 -▁Cash 9.5695559919229313e-06 -▁grammatisch 9.5695559919229313e-06 -▁grünen 9.5695559919229313e-06 -▁quant 9.5695559919229313e-06 -.ch 9.5657675691232775e-06 -▁verständ 9.5657675691232775e-06 -▁Spaziergang 9.5619791463236255e-06 -▁governing 9.5619791463236255e-06 -mäßige 9.5581907235239735e-06 -▁Ford 9.5581907235239735e-06 -▁HP 9.5581907235239735e-06 -▁Herkunft 9.5581907235239735e-06 -▁drag 9.5581907235239735e-06 -ausend 9.55440230072432e-06 -flanz 9.55440230072432e-06 -ingt 9.55440230072432e-06 -▁Nizza 9.55440230072432e-06 -▁joining 9.55440230072432e-06 -erten 9.5506138779246677e-06 -▁59 9.5506138779246677e-06 -▁Bol 9.5506138779246677e-06 -▁anges 9.5506138779246677e-06 -▁eingeleitet 9.5506138779246677e-06 -▁enforcement 9.5506138779246677e-06 -▁weaken 9.5506138779246677e-06 -indung 9.5468254551250139e-06 -▁activ 9.5468254551250139e-06 -▁uneingeschränkt 9.5468254551250139e-06 -andal 9.5430370323253619e-06 -cs 9.5430370323253619e-06 -ellung 9.5430370323253619e-06 -reicht 9.5430370323253619e-06 -stil 9.5430370323253619e-06 -▁Stern 9.5430370323253619e-06 -▁verwendeten 9.5430370323253619e-06 -iffen 9.5392486095257082e-06 -kunden 9.5392486095257082e-06 -▁Gegner 9.5392486095257082e-06 -▁ausführ 9.5392486095257082e-06 -▁ultimate 9.5392486095257082e-06 -ibl 9.5354601867260561e-06 -▁Schlusselphrase 9.5354601867260561e-06 -▁Schlusselwort 9.5354601867260561e-06 -inary 9.5316717639264041e-06 -küste 9.5316717639264041e-06 -▁J. 9.5316717639264041e-06 -▁Jewish 9.5316717639264041e-06 -▁Mechanismus 9.5316717639264041e-06 -▁erwiesen 9.5316717639264041e-06 -▁technique 9.5316717639264041e-06 -ective 9.52788334112675e-06 -▁aufgezahlt 9.52788334112675e-06 -▁fell 9.52788334112675e-06 -▁schließt 9.52788334112675e-06 -fällig 9.5240949183270983e-06 -▁Ehre 9.5240949183270983e-06 -▁GT 9.5240949183270983e-06 -▁translated 9.5240949183270983e-06 -amps 9.5203064955274445e-06 -lar 9.5203064955274445e-06 -rein 9.5203064955274445e-06 -▁LED 9.5203064955274445e-06 -▁deut 9.5203064955274445e-06 -▁digitale 9.5203064955274445e-06 -▁gemeinsames 9.5203064955274445e-06 -▁urgency 9.5203064955274445e-06 -▁violation 9.5203064955274445e-06 -▁Dokumentation 9.5165180727277925e-06 -▁Intergovernmental 9.5165180727277925e-06 -▁beantwortet 9.5165180727277925e-06 -▁dual 9.5165180727277925e-06 -▁mach 9.5165180727277925e-06 -▁mixed 9.5165180727277925e-06 --oriented 9.5127296499281388e-06 -esa 9.5127296499281388e-06 -fel 9.5127296499281388e-06 -▁Universal 9.5127296499281388e-06 -▁Ut 9.5127296499281388e-06 -OV 9.5089412271284867e-06 -▁FOR 9.5089412271284867e-06 -▁sorry 9.5089412271284867e-06 -▁verbreitet 9.5089412271284867e-06 -märkte 9.505152804328833e-06 -▁Galileo 9.505152804328833e-06 -▁anim 9.505152804328833e-06 -▁spec 9.505152804328833e-06 --style 9.5013643815291809e-06 -erman 9.5013643815291809e-06 -те 9.5013643815291809e-06 -▁enjoying 9.5013643815291809e-06 -▁gewidmet 9.5013643815291809e-06 -▁innovativen 9.5013643815291809e-06 -▁prohib 9.5013643815291809e-06 -▁Slovakia 9.4975759587295289e-06 -▁Spring 9.4975759587295289e-06 -▁empty 9.4975759587295289e-06 -▁hilfreich 9.4975759587295289e-06 -esc 9.4937875359298751e-06 -▁Chem 9.4937875359298751e-06 -▁Nation 9.4937875359298751e-06 -▁Plat 9.4937875359298751e-06 -▁incentives 9.4937875359298751e-06 -gy 9.4899991131302231e-06 -▁Antr 9.4899991131302231e-06 -▁Moscow 9.4899991131302231e-06 -▁ease 9.4899991131302231e-06 -▁nächster 9.4899991131302231e-06 -▁Är 9.4899991131302231e-06 -ibt 9.48621069033057e-06 -▁Gibt 9.48621069033057e-06 -88 9.4824222675309173e-06 -block 9.4824222675309173e-06 -▁Fisheries 9.4824222675309173e-06 -▁Km 9.4824222675309173e-06 -▁Können 9.4824222675309173e-06 -▁chat 9.4824222675309173e-06 -▁variable 9.4824222675309173e-06 -▁esp 9.4786338447312636e-06 -▁max 9.4786338447312636e-06 -▁vollen 9.4786338447312636e-06 --Q 9.4748454219316115e-06 -ermaßen 9.4748454219316115e-06 -struct 9.4748454219316115e-06 -▁Anbetracht 9.4748454219316115e-06 -▁Nehmen 9.4748454219316115e-06 -▁Plug 9.4748454219316115e-06 -▁Wohnungen 9.4748454219316115e-06 -▁verstehe 9.4748454219316115e-06 -IER 9.47105699913196e-06 -▁Priv 9.47105699913196e-06 -▁boasts 9.47105699913196e-06 -▁wife 9.47105699913196e-06 -place 9.4672685763323058e-06 -▁Chat 9.4672685763323058e-06 -▁gegebenenfalls 9.4672685763323058e-06 -▁incident 9.4672685763323058e-06 -▁Publikum 9.4634801535326537e-06 -▁aufzubauen 9.4634801535326537e-06 -▁elev 9.4634801535326537e-06 -▁boats 9.459691730733e-06 -▁compulsory 9.459691730733e-06 --Prozess 9.4559033079333479e-06 -▁overlook 9.4559033079333479e-06 -▁ratification 9.4559033079333479e-06 -▁Arena 9.4521148851336942e-06 -▁Denken 9.4521148851336942e-06 -▁Lernen 9.4521148851336942e-06 -▁Saal 9.4521148851336942e-06 -▁mere 9.4521148851336942e-06 -▁cer 9.4483264623340421e-06 -▁fascinating 9.4483264623340421e-06 -▁hochwertigen 9.4407496167347364e-06 -▁reagiert 9.4407496167347364e-06 -itu 9.4369611939350843e-06 -▁Akteure 9.4369611939350843e-06 -▁Niederlande 9.4369611939350843e-06 -▁employed 9.4369611939350843e-06 -▁preferred 9.4369611939350843e-06 -glich 9.43317277113543e-06 -seh 9.43317277113543e-06 -win 9.43317277113543e-06 -▁aufbauen 9.43317277113543e-06 -▁electrical 9.43317277113543e-06 -▁girls 9.43317277113543e-06 -▁hinzugefügt 9.43317277113543e-06 -▁moments 9.43317277113543e-06 -▁ruhige 9.43317277113543e-06 --Server 9.4293843483357785e-06 -nung 9.4293843483357785e-06 -verbindung 9.4293843483357785e-06 -▁Invest 9.4293843483357785e-06 -▁chairman 9.4293843483357785e-06 -▁residential 9.4293843483357785e-06 -▁Wednesday 9.4255959255361248e-06 -▁aside 9.4255959255361248e-06 -▁halt 9.4255959255361248e-06 -▁imperial 9.4255959255361248e-06 -▁veröffentlichen 9.4255959255361248e-06 -▁-S 9.4218075027364728e-06 -▁Agr 9.4218075027364728e-06 -▁lädt 9.4218075027364728e-06 -▁steam 9.4218075027364728e-06 -hung 9.418019079936819e-06 -▁Sektors 9.418019079936819e-06 -▁marine 9.418019079936819e-06 -▁winning 9.418019079936819e-06 -icherheit 9.414230657137167e-06 -ilo 9.414230657137167e-06 -ï 9.414230657137167e-06 -▁Einkauf 9.414230657137167e-06 -▁definieren 9.414230657137167e-06 -▁keineswegs 9.414230657137167e-06 -orks 9.4104422343375149e-06 -▁Bezeichnung 9.4104422343375149e-06 -▁Hab 9.4104422343375149e-06 -▁League 9.4104422343375149e-06 -▁appointment 9.4104422343375149e-06 -▁robust 9.4104422343375149e-06 -▁schwere 9.4104422343375149e-06 -▁Ausweitung 9.4066538115378612e-06 -▁Fed 9.4066538115378612e-06 -▁completion 9.4066538115378612e-06 -▁dauern 9.4066538115378612e-06 -▁navigation 9.4066538115378612e-06 -ects 9.4028653887382091e-06 -▁confront 9.4028653887382091e-06 -▁foundations 9.4028653887382091e-06 -▁Erwachsene 9.3990769659385554e-06 -▁Pod 9.3990769659385554e-06 -▁William 9.3990769659385554e-06 -ource 9.3952885431389034e-06 -stätten 9.3952885431389034e-06 -urf 9.3952885431389034e-06 -ühl 9.3952885431389034e-06 -▁fewer 9.3952885431389034e-06 -▁reass 9.3952885431389034e-06 -OD 9.39150012033925e-06 -▁Pacific 9.39150012033925e-06 -▁Sowohl 9.39150012033925e-06 -▁deserves 9.39150012033925e-06 -▁portal 9.39150012033925e-06 -▁references 9.39150012033925e-06 -verständ 9.3877116975395976e-06 -öff 9.3877116975395976e-06 -zeichnung 9.3839232747399438e-06 -▁Cost 9.3839232747399438e-06 -▁anz 9.3839232747399438e-06 -▁charakter 9.3839232747399438e-06 -ühlen 9.3801348519402918e-06 -▁Bestell 9.37634642914064e-06 -▁array 9.37634642914064e-06 -▁landwirtschaftlichen 9.37634642914064e-06 -▁legitimacy 9.37634642914064e-06 -reh 9.372558006340986e-06 -▁Agenturen 9.372558006340986e-06 -▁Verzeichnis 9.372558006340986e-06 -▁conciliation 9.372558006340986e-06 -▁height 9.372558006340986e-06 -▁cancellation 9.368769583541334e-06 -hängen 9.36498116074168e-06 -ð 9.36498116074168e-06 -▁Jur 9.36498116074168e-06 -▁pools 9.36498116074168e-06 -▁äußer 9.36498116074168e-06 -OG 9.3611927379420282e-06 -formular 9.3611927379420282e-06 -▁Anders 9.3611927379420282e-06 -▁Souveränität 9.3611927379420282e-06 -▁chief 9.3611927379420282e-06 -zimmern 9.3574043151423744e-06 --term 9.3536158923427224e-06 -37 9.3536158923427224e-06 -ados 9.3536158923427224e-06 -alts 9.3536158923427224e-06 -antes 9.3536158923427224e-06 -▁Bestätigung 9.3536158923427224e-06 -▁Million 9.3536158923427224e-06 -umin 9.34982746954307e-06 -▁Together 9.34982746954307e-06 -▁Verz 9.34982746954307e-06 -▁Youth 9.34982746954307e-06 -▁besonderes 9.34982746954307e-06 -▁cancel 9.34982746954307e-06 -bec 9.3460390467434166e-06 -empf 9.3460390467434166e-06 -▁Belg 9.3460390467434166e-06 -▁Einfach 9.3460390467434166e-06 -▁Portal 9.3460390467434166e-06 --Fl 9.3422506239437646e-06 -▁Eff 9.3422506239437646e-06 -▁extract 9.3422506239437646e-06 -▁incorpor 9.3422506239437646e-06 -▁newsletter 9.3422506239437646e-06 -itude 9.33846220114411e-06 -▁1986 9.33846220114411e-06 -▁Eisen 9.33846220114411e-06 -▁stattgefunden 9.33846220114411e-06 -▁zul 9.33846220114411e-06 -üche 9.3346737783444588e-06 -▁Dein 9.3346737783444588e-06 -▁Fer 9.3346737783444588e-06 -▁faces 9.3346737783444588e-06 -asso 9.330885355544805e-06 -essel 9.330885355544805e-06 -▁timetable 9.330885355544805e-06 -stat 9.327096932745153e-06 -▁Select 9.327096932745153e-06 -▁Vern 9.327096932745153e-06 -eme 9.323308509945501e-06 -service 9.323308509945501e-06 -▁Bre 9.323308509945501e-06 -▁External 9.323308509945501e-06 -▁Verbrauchern 9.323308509945501e-06 -▁autumn 9.323308509945501e-06 -▁coach 9.323308509945501e-06 -unst 9.3195200871458472e-06 -▁Erf 9.3195200871458472e-06 -▁Fassung 9.3195200871458472e-06 -▁Heads 9.3195200871458472e-06 -▁authors 9.3195200871458472e-06 -▁observe 9.3195200871458472e-06 -▁thumbnail 9.3195200871458472e-06 -▁Dur 9.3157316643461952e-06 -▁herzustellen 9.3157316643461952e-06 -▁sitzen 9.3157316643461952e-06 -▁vit 9.3157316643461952e-06 -ieu 9.3119432415465414e-06 -lös 9.3119432415465414e-06 -▁Vorsitzenden 9.3119432415465414e-06 -▁ultra 9.3119432415465414e-06 -▁Print 9.30815481874689e-06 -▁bread 9.30815481874689e-06 -ission 9.3043663959472357e-06 -▁Luftfahrt 9.3043663959472357e-06 -▁erkunden 9.3043663959472357e-06 -▁theatre 9.3043663959472357e-06 -▁considerations 9.3005779731475836e-06 -▁moreover 9.3005779731475836e-06 -hang 9.29678955034793e-06 -▁Fle 9.29678955034793e-06 -▁Ministry 9.29678955034793e-06 -▁desto 9.29678955034793e-06 -neiden 9.2930011275482778e-06 -▁ständigen 9.2930011275482778e-06 -▁Anonymous 9.2892127047486258e-06 -▁erheben 9.2892127047486258e-06 -▁quest 9.2892127047486258e-06 -▁repeatedly 9.2892127047486258e-06 -▁Demokraten 9.285424281948972e-06 -▁Deutschlands 9.285424281948972e-06 -▁wil 9.285424281948972e-06 -▁är 9.285424281948972e-06 -icking 9.28163585914932e-06 -▁Naz 9.28163585914932e-06 -▁Panorama 9.28163585914932e-06 -▁Prof. 9.28163585914932e-06 -▁inadequate 9.28163585914932e-06 -ippen 9.2778474363496663e-06 -▁neighbourhood 9.2778474363496663e-06 --e 9.2740590135500142e-06 -oint 9.2740590135500142e-06 -▁Renn 9.2740590135500142e-06 -▁debating 9.2740590135500142e-06 -▁edit 9.2740590135500142e-06 -▁sheet 9.2740590135500142e-06 -▁travellers 9.2740590135500142e-06 -▁Sep 9.27027059075036e-06 -▁professionellen 9.27027059075036e-06 -▁rout 9.27027059075036e-06 --European 9.2626937451510564e-06 -icious 9.2626937451510564e-06 -indet 9.2626937451510564e-06 -statt 9.2626937451510564e-06 -▁Prozeß 9.2626937451510564e-06 -DE 9.2589053223514027e-06 -führten 9.2589053223514027e-06 -iev 9.2589053223514027e-06 -illing 9.2589053223514027e-06 -▁Ehr 9.2589053223514027e-06 -▁Komm 9.2589053223514027e-06 -▁begun 9.2589053223514027e-06 -▁berichtet 9.2589053223514027e-06 -▁zustande 9.2589053223514027e-06 -ologisch 9.25511689955175e-06 -▁Chief 9.25511689955175e-06 -▁Objekte 9.25511689955175e-06 -▁Werten 9.25511689955175e-06 -▁evol 9.25511689955175e-06 -▁fassen 9.25511689955175e-06 -▁favourable 9.25511689955175e-06 -▁generell 9.25511689955175e-06 -beln 9.2513284767520969e-06 -ähl 9.2513284767520969e-06 -▁coastal 9.2513284767520969e-06 -▁escape 9.2513284767520969e-06 -▁false 9.2513284767520969e-06 -▁journalists 9.2513284767520969e-06 -▁unnecessary 9.2513284767520969e-06 --made 9.2475400539524448e-06 -laub 9.2475400539524448e-06 -▁Phys 9.2475400539524448e-06 -▁Reparatur 9.2475400539524448e-06 -▁exposed 9.2475400539524448e-06 -iders 9.2437516311527911e-06 -reen 9.2437516311527911e-06 -▁Binnen 9.2437516311527911e-06 -▁WE 9.2437516311527911e-06 -▁bearbeiten 9.2437516311527911e-06 -▁festen 9.2437516311527911e-06 -▁secondly 9.2437516311527911e-06 -ador 9.239963208353139e-06 -icit 9.239963208353139e-06 -istent 9.239963208353139e-06 -▁Marco 9.239963208353139e-06 -▁Projekts 9.239963208353139e-06 -▁elegante 9.239963208353139e-06 -▁enge 9.239963208353139e-06 -▁qui 9.239963208353139e-06 -▁secondary 9.239963208353139e-06 -ession 9.2361747855534853e-06 -▁Eines 9.2361747855534853e-06 -▁genuinely 9.2361747855534853e-06 -▁Bosnia 9.2323863627538333e-06 -▁Dynamik 9.2323863627538333e-06 -adel 9.2285979399541812e-06 -▁Versicherungs 9.2285979399541812e-06 -ads 9.2248095171545275e-06 -▁sizes 9.2248095171545275e-06 -anced 9.2210210943548754e-06 -enth 9.2210210943548754e-06 -▁Vereinigte 9.2210210943548754e-06 -▁benef 9.2210210943548754e-06 -erie 9.2172326715552217e-06 -standard 9.2172326715552217e-06 -▁Entschlossenheit 9.2172326715552217e-06 -▁Hill 9.2172326715552217e-06 -▁logic 9.2172326715552217e-06 -▁ökologischen 9.2172326715552217e-06 -ellten 9.21344424875557e-06 -▁Serv 9.21344424875557e-06 -▁einheit 9.21344424875557e-06 -▁Erreichung 9.2096558259559159e-06 -▁codecision 9.2096558259559159e-06 -▁eingel 9.2096558259559159e-06 -▁folder 9.2096558259559159e-06 -▁chinesische 9.2058674031562639e-06 -kret 9.2020789803566118e-06 -reas 9.2020789803566118e-06 -stes 9.2020789803566118e-06 -▁Relax 9.2020789803566118e-06 -▁Schatten 9.2020789803566118e-06 -▁stip 9.2020789803566118e-06 -emp 9.1982905575569581e-06 -▁Ents 9.1982905575569581e-06 -▁Universitäten 9.1982905575569581e-06 -▁palästinensischen 9.1982905575569581e-06 -wander 9.194502134757306e-06 -▁A4 9.194502134757306e-06 -▁Auflösung 9.194502134757306e-06 -▁Ehe 9.194502134757306e-06 -▁analog 9.194502134757306e-06 -▁eingeschränkt 9.194502134757306e-06 -▁flood 9.194502134757306e-06 -▁nutzt 9.194502134757306e-06 -lan 9.1907137119576523e-06 -▁Aspekten 9.1907137119576523e-06 -▁bearing 9.1907137119576523e-06 -▁threshold 9.1907137119576523e-06 -ailing 9.186925289158e-06 -ocation 9.186925289158e-06 -regionen 9.186925289158e-06 -▁Anmerkungen 9.186925289158e-06 -▁Milan 9.186925289158e-06 -▁genauer 9.186925289158e-06 -ienten 9.1831368663583465e-06 -periode 9.1831368663583465e-06 -amed 9.1793484435586945e-06 -▁Erfolge 9.1793484435586945e-06 -▁Nordic 9.1793484435586945e-06 -▁disappear 9.1793484435586945e-06 -▁irr 9.1793484435586945e-06 -ML 9.17556002075904e-06 -▁Entwicklungshilfe 9.17556002075904e-06 -▁underlying 9.17556002075904e-06 -weilen 9.1717715979593887e-06 -▁compete 9.1717715979593887e-06 -▁cookies 9.1717715979593887e-06 -▁lively 9.1717715979593887e-06 -▁murder 9.1717715979593887e-06 -fisch 9.1679831751597366e-06 -▁Beste 9.1679831751597366e-06 -▁Getränke 9.1679831751597366e-06 -▁Nichtraucherzimmer 9.1679831751597366e-06 -▁Patten 9.1679831751597366e-06 -▁Series 9.1679831751597366e-06 -▁Volkspartei 9.1679831751597366e-06 -▁hel 9.1679831751597366e-06 -▁langer 9.1679831751597366e-06 -träge 9.1641947523600829e-06 -▁Ratifizierung 9.1641947523600829e-06 -▁Unsicherheit 9.1641947523600829e-06 -▁cod 9.1641947523600829e-06 -becken 9.1604063295604309e-06 -▁62 9.1604063295604309e-06 -▁Bade 9.1604063295604309e-06 -▁Insgesamt 9.1604063295604309e-06 -▁asset 9.1604063295604309e-06 -▁characteristic 9.1604063295604309e-06 -▁foremost 9.1604063295604309e-06 -▁geliefert 9.1604063295604309e-06 -▁gestatten 9.1604063295604309e-06 -▁speeches 9.1604063295604309e-06 -raums 9.1566179067607771e-06 -▁Elektr 9.1566179067607771e-06 -▁Storage 9.1566179067607771e-06 -ichtet 9.1528294839611251e-06 -ifizieren 9.1528294839611251e-06 -▁CAN 9.1528294839611251e-06 -▁economically 9.1528294839611251e-06 -▁listening 9.1528294839611251e-06 -▁ring 9.1528294839611251e-06 -▁sample 9.1528294839611251e-06 --Version 9.1490410611614713e-06 -ça 9.1490410611614713e-06 -▁dramatic 9.1490410611614713e-06 -▁guaranteeing 9.1490410611614713e-06 -prom 9.14525263836182e-06 -▁gelesen 9.14525263836182e-06 -▁representing 9.14525263836182e-06 -kräften 9.1414642155621672e-06 -▁Rats 9.1414642155621672e-06 -cke 9.1376757927625135e-06 -ellte 9.1376757927625135e-06 -mäßigen 9.1376757927625135e-06 -▁29. 9.1376757927625135e-06 -▁featuring 9.1376757927625135e-06 -▁Kompetenzen 9.1338873699628615e-06 -▁teuer 9.1338873699628615e-06 -▁900 9.1300989471632077e-06 -▁Ereignis 9.1300989471632077e-06 -▁Nachmittag 9.1300989471632077e-06 -▁gear 9.1300989471632077e-06 -zia 9.1263105243635557e-06 -τ 9.1263105243635557e-06 -▁managers 9.1263105243635557e-06 -▁operated 9.1263105243635557e-06 -stechnik 9.1225221015639019e-06 -zugs 9.1225221015639019e-06 -▁Fremdenverkehr 9.1225221015639019e-06 -▁RE 9.1225221015639019e-06 -▁bigger 9.1225221015639019e-06 -▁schauen 9.1225221015639019e-06 -▁vent 9.1225221015639019e-06 -arische 9.11873367876425e-06 -ients 9.11873367876425e-06 -▁Leiter 9.11873367876425e-06 -▁Runde 9.11873367876425e-06 -cal 9.1149452559645979e-06 -▁Ill 9.1149452559645979e-06 -▁Verf 9.1149452559645979e-06 -▁sucht 9.1149452559645979e-06 -add 9.1111568331649441e-06 -▁Smith 9.1111568331649441e-06 -▁erstes 9.1111568331649441e-06 -▁respects 9.1111568331649441e-06 -ι 9.1073684103652921e-06 -▁expanded 9.1073684103652921e-06 -▁subsequently 9.1073684103652921e-06 -▁Several 9.1035799875656383e-06 -▁informal 9.1035799875656383e-06 -▁neutral 9.1035799875656383e-06 -▁zweif 9.1035799875656383e-06 -aton 9.0997915647659863e-06 -▁Massage 9.0997915647659863e-06 -▁frequent 9.0997915647659863e-06 -▁instantly 9.0997915647659863e-06 -▁textile 9.0997915647659863e-06 -▁Soldaten 9.0960031419663325e-06 -▁destroy 9.0960031419663325e-06 -▁grenzüberschreitenden 9.0960031419663325e-06 -▁ho 9.0960031419663325e-06 -▁schwedischen 9.0960031419663325e-06 -brauch 9.09221471916668e-06 -▁sin 9.09221471916668e-06 -▁Designer 9.0884262963670268e-06 -▁Section 9.0884262963670268e-06 -▁gesellschaftlichen 9.0884262963670268e-06 -▁govern 9.0846378735673747e-06 -fäll 9.0808494507677227e-06 -ye 9.0808494507677227e-06 -▁McC 9.0808494507677227e-06 -▁Program 9.0808494507677227e-06 -▁anerkennen 9.0808494507677227e-06 -▁diving 9.0808494507677227e-06 -▁suggests 9.0808494507677227e-06 -icing 9.0770610279680689e-06 -▁Schwellen 9.0770610279680689e-06 -▁slopes 9.0770610279680689e-06 -▁arranged 9.0732726051684169e-06 -▁sovereign 9.0732726051684169e-06 --Dateien 9.0694841823687632e-06 -rogen 9.0694841823687632e-06 -▁D. 9.0694841823687632e-06 -▁History 9.0694841823687632e-06 -▁Schulz 9.0694841823687632e-06 -▁erwerben 9.0694841823687632e-06 -▁intentions 9.0694841823687632e-06 -▁Alps 9.0656957595691111e-06 -▁Job 9.0656957595691111e-06 -▁Mem 9.0656957595691111e-06 -▁Solana 9.0656957595691111e-06 -▁auswählen 9.0656957595691111e-06 -▁frag 9.0656957595691111e-06 -▁gemütliche 9.0656957595691111e-06 -fried 9.0619073367694574e-06 -krieg 9.0619073367694574e-06 -quelle 9.0619073367694574e-06 -▁examination 9.0619073367694574e-06 -aj 9.0581189139698053e-06 -▁Kommentare 9.0581189139698053e-06 -▁beibehalten 9.0581189139698053e-06 -▁openness 9.0581189139698053e-06 -vy 9.0543304911701533e-06 -▁Ashton 9.0543304911701533e-06 -▁enormen 9.0543304911701533e-06 -ouren 9.0505420683705e-06 -utter 9.0505420683705e-06 -▁actors 9.0505420683705e-06 --Sh 9.0467536455708475e-06 -geld 9.0467536455708475e-06 -reserv 9.0467536455708475e-06 -▁bewahren 9.0467536455708475e-06 -▁expenses 9.0429652227711938e-06 -▁terrible 9.0429652227711938e-06 -▁M. 9.0391767999715417e-06 -▁OECD 9.0391767999715417e-06 -▁Slowakei 9.0391767999715417e-06 -▁importantly 9.0391767999715417e-06 -PA 9.035388377171888e-06 -arians 9.035388377171888e-06 -onda 9.035388377171888e-06 -▁Ay 9.035388377171888e-06 -anging 9.0315999543722359e-06 -▁Betriebssystem 9.0315999543722359e-06 -▁Rhein 9.0315999543722359e-06 -▁Stamm 9.0315999543722359e-06 -▁bay 9.0315999543722359e-06 -▁fat 9.0315999543722359e-06 -▁ownership 9.0315999543722359e-06 -▁resist 9.0315999543722359e-06 -ionale 9.0278115315725822e-06 -▁rubber 9.0278115315725822e-06 -ère 9.02402310877293e-06 -▁Mikr 9.02402310877293e-06 -▁geh 9.02402310877293e-06 -▁widmen 9.02402310877293e-06 -CP 9.0202346859732781e-06 -zusetzen 9.0202346859732781e-06 -ña 9.0202346859732781e-06 -▁Haiti 9.0202346859732781e-06 -▁hell 9.0202346859732781e-06 -branche 9.0164462631736244e-06 -füll 9.0164462631736244e-06 -▁Inflation 9.0164462631736244e-06 -▁Zweiten 9.0164462631736244e-06 -▁complaints 9.0164462631736244e-06 -▁willingness 9.0164462631736244e-06 -ierer 9.0126578403739723e-06 -produkten 9.0126578403739723e-06 -▁Seele 9.0126578403739723e-06 -▁ausländischen 9.0126578403739723e-06 -▁breach 9.0126578403739723e-06 -▁einladen 9.0126578403739723e-06 -▁gegangen 9.0126578403739723e-06 -▁hohes 9.0126578403739723e-06 -▁kostenlosen 9.0126578403739723e-06 -prozeß 9.0088694175743186e-06 -▁Jud 9.0088694175743186e-06 -▁Wolf 9.0088694175743186e-06 -▁devoted 9.0088694175743186e-06 -▁schriftliche 9.0088694175743186e-06 -▁Migration 9.0050809947746665e-06 -▁Modul 9.0050809947746665e-06 -zweif 9.0012925719750128e-06 -▁patch 9.0012925719750128e-06 -▁Corporation 8.99750414917536e-06 -▁Köln 8.99750414917536e-06 -▁Zust 8.99750414917536e-06 -ieller 8.9937157263757087e-06 -ota 8.9937157263757087e-06 -▁Administration 8.9937157263757087e-06 -▁arbeit 8.9937157263757087e-06 -▁clicking 8.9937157263757087e-06 -▁dil 8.9937157263757087e-06 -▁verurteilen 8.9937157263757087e-06 -etzte 8.989927303576055e-06 -▁Durchsetzung 8.989927303576055e-06 -▁Garantien 8.989927303576055e-06 -▁Morocco 8.989927303576055e-06 -▁Sta 8.989927303576055e-06 -▁dep 8.989927303576055e-06 -▁insufficient 8.989927303576055e-06 -▁reserv 8.989927303576055e-06 -▁zweiter 8.989927303576055e-06 -▁Lor 8.9861388807764029e-06 -▁certified 8.9861388807764029e-06 -▁Archiv 8.98235045797675e-06 -▁dahin 8.98235045797675e-06 -▁defin 8.98235045797675e-06 -▁excursions 8.98235045797675e-06 -▁famili 8.98235045797675e-06 -▁vocational 8.98235045797675e-06 -märkten 8.9747736123774434e-06 -äden 8.9747736123774434e-06 -▁Bonn 8.9747736123774434e-06 -▁Montenegro 8.9747736123774434e-06 -▁Rechner 8.9747736123774434e-06 -▁jointly 8.9747736123774434e-06 -field 8.9709851895777914e-06 -uis 8.9709851895777914e-06 -▁Unterricht 8.9709851895777914e-06 -▁accessories 8.9709851895777914e-06 -▁bedroht 8.9709851895777914e-06 -▁elektronische 8.9709851895777914e-06 -heb 8.96719676677814e-06 -▁Klarheit 8.96719676677814e-06 -▁behavior 8.96719676677814e-06 --Sp 8.9634083439784856e-06 -▁abandon 8.9634083439784856e-06 -▁berühmte 8.9634083439784856e-06 -füllt 8.9596199211788335e-06 -▁187 8.9596199211788335e-06 -▁Neg 8.9596199211788335e-06 -namen 8.95583149837918e-06 -stream 8.95583149837918e-06 -▁Ret 8.95583149837918e-06 -▁aktualisiert 8.95583149837918e-06 -▁destinations 8.95583149837918e-06 -▁erlassen 8.95583149837918e-06 -▁purchased 8.95583149837918e-06 -▁swe 8.95583149837918e-06 -gro 8.9520430755795278e-06 -idet 8.9520430755795278e-06 -▁Rab 8.9520430755795278e-06 -▁Stimm 8.9520430755795278e-06 -▁apparent 8.9520430755795278e-06 -▁bereiten 8.9520430755795278e-06 -▁reb 8.9520430755795278e-06 -.com. 8.948254652779874e-06 -voller 8.948254652779874e-06 -wass 8.948254652779874e-06 -▁sunny 8.948254652779874e-06 -▁tragic 8.948254652779874e-06 -▁trouble 8.948254652779874e-06 -ikationen 8.944466229980222e-06 -itte 8.944466229980222e-06 -▁kleinere 8.944466229980222e-06 -geschwindigkeit 8.9406778071805682e-06 -quote 8.9406778071805682e-06 -▁1987 8.9406778071805682e-06 -üst 8.9368893843809162e-06 -▁Mol 8.9368893843809162e-06 -▁convenience 8.9368893843809162e-06 -▁jurisdiction 8.9368893843809162e-06 -▁Mang 8.9331009615812641e-06 -▁acquis 8.9331009615812641e-06 -▁calendar 8.9331009615812641e-06 -▁kurzer 8.9331009615812641e-06 -▁reward 8.9331009615812641e-06 -▁transl 8.9331009615812641e-06 -▁upload 8.9331009615812641e-06 -ographie 8.92931253878161e-06 -▁Guide 8.92931253878161e-06 -▁dest 8.92931253878161e-06 -▁existieren 8.92931253878161e-06 -▁twin 8.92931253878161e-06 -lein 8.9255241159819584e-06 -▁1985 8.9255241159819584e-06 -▁26. 8.9255241159819584e-06 -▁Erzeuger 8.9255241159819584e-06 -▁alternatives 8.9255241159819584e-06 -▁letters 8.9255241159819584e-06 -▁simultaneously 8.9217356931823046e-06 -wan 8.9179472703826526e-06 -▁ideally 8.9179472703826526e-06 -▁Chef 8.9141588475829988e-06 -▁Down 8.9141588475829988e-06 -▁VER 8.9141588475829988e-06 -▁Ev 8.9103704247833468e-06 -▁Interview 8.9103704247833468e-06 -▁Polize 8.9103704247833468e-06 -▁fle 8.9103704247833468e-06 -▁vacc 8.9103704247833468e-06 -gelegt 8.9065820019836947e-06 -▁Filme 8.9065820019836947e-06 -▁Gemeinden 8.9065820019836947e-06 -▁Weine 8.9065820019836947e-06 -▁beliebt 8.9065820019836947e-06 -bestand 8.902793579184041e-06 -kern 8.902793579184041e-06 -okrat 8.902793579184041e-06 -▁Empfang 8.902793579184041e-06 -▁solch 8.902793579184041e-06 -eating 8.899005156384389e-06 -oul 8.899005156384389e-06 -▁integrierte 8.899005156384389e-06 -igert 8.8952167335847352e-06 -irts 8.8952167335847352e-06 -▁EIB 8.8952167335847352e-06 -▁Krieges 8.8952167335847352e-06 -▁erteilen 8.8952167335847352e-06 -anch 8.8914283107850832e-06 -osp 8.8914283107850832e-06 -verhalten 8.8914283107850832e-06 -▁bezeichnen 8.8914283107850832e-06 -▁horr 8.8914283107850832e-06 -▁retr 8.8914283107850832e-06 -▁unterschiedlicher 8.8914283107850832e-06 -sburg 8.88763988798543e-06 -weil 8.88763988798543e-06 -zulegen 8.88763988798543e-06 -ändern 8.88763988798543e-06 -▁Amer 8.88763988798543e-06 -▁messen 8.88763988798543e-06 -▁Bergen 8.8838514651857774e-06 -▁fault 8.8838514651857774e-06 -decken 8.8800630423861237e-06 -▁accordingly 8.8800630423861237e-06 -▁clarity 8.8800630423861237e-06 -bour 8.8762746195864716e-06 -▁CR 8.8762746195864716e-06 -▁arts 8.8762746195864716e-06 -▁erarbeitet 8.8762746195864716e-06 -▁posts 8.8762746195864716e-06 -▁therm 8.8762746195864716e-06 -▁umgehend 8.8762746195864716e-06 -othe 8.87248619678682e-06 -▁ED 8.87248619678682e-06 -▁Krebs 8.87248619678682e-06 -▁Ursache 8.87248619678682e-06 -го 8.8686977739871658e-06 -▁armen 8.8686977739871658e-06 -isters 8.8649093511875138e-06 -pose 8.8649093511875138e-06 -ries 8.8649093511875138e-06 -vest 8.8649093511875138e-06 -▁Domain 8.8649093511875138e-06 -▁Missbrauch 8.8649093511875138e-06 -▁Reit 8.8649093511875138e-06 -▁getrennt 8.8649093511875138e-06 -▁vermittelt 8.86112092838786e-06 -▁Francisco 8.857332505588208e-06 -▁desktop 8.857332505588208e-06 -uert 8.8535440827885543e-06 -▁Grünbuch 8.8535440827885543e-06 -▁Sec 8.8535440827885543e-06 -▁measurement 8.8535440827885543e-06 -▁motorway 8.8535440827885543e-06 -▁sparen 8.8535440827885543e-06 -unen 8.8497556599889022e-06 -▁Entwickler 8.8497556599889022e-06 -▁Vizepräsident 8.8497556599889022e-06 -▁vary 8.8497556599889022e-06 -36 8.84596723718925e-06 -lying 8.84596723718925e-06 -▁Ausrüstung 8.84596723718925e-06 -▁Mittwoch 8.84596723718925e-06 -▁flash 8.84596723718925e-06 -▁orch 8.84596723718925e-06 -▁produkt 8.84596723718925e-06 -▁spanische 8.84596723718925e-06 -▁transfers 8.84596723718925e-06 -▁Australien 8.8421788143895964e-06 -▁Near 8.8421788143895964e-06 -▁autor 8.8421788143895964e-06 -▁mine 8.8421788143895964e-06 -▁vertritt 8.8421788143895964e-06 -▁CDs 8.8383903915899444e-06 -▁Sternen 8.8383903915899444e-06 -▁Zurich 8.8383903915899444e-06 -▁freundliche 8.8383903915899444e-06 -▁inspection 8.8383903915899444e-06 -orth 8.83460196879029e-06 -▁Beamten 8.83460196879029e-06 -▁Ursprungs 8.83460196879029e-06 -▁discl 8.83460196879029e-06 -▁falsche 8.83460196879029e-06 -▁gym 8.83460196879029e-06 -ürz 8.8308135459906386e-06 -▁IBM 8.8308135459906386e-06 -▁Reinigung 8.8308135459906386e-06 -▁pul 8.8308135459906386e-06 -▁sinn 8.8308135459906386e-06 -igkeits 8.8270251231909849e-06 -▁A4-0 8.8270251231909849e-06 -▁Ec 8.8270251231909849e-06 -▁Menschheit 8.8270251231909849e-06 -▁kombiniert 8.8270251231909849e-06 -▁transaction 8.8270251231909849e-06 -oning 8.8232367003913328e-06 -▁Nobel 8.8232367003913328e-06 -▁Wichtigkeit 8.8232367003913328e-06 -▁Wireless 8.8232367003913328e-06 -▁postal 8.8232367003913328e-06 -▁spectrum 8.8232367003913328e-06 -pes 8.8194482775916791e-06 -schicht 8.8194482775916791e-06 -▁Logistik 8.8194482775916791e-06 -▁fifth 8.8194482775916791e-06 -▁rece 8.8194482775916791e-06 -.200 8.815659854792027e-06 -OK 8.815659854792027e-06 -kräft 8.815659854792027e-06 -▁CL 8.815659854792027e-06 -▁FI 8.815659854792027e-06 -▁Fortsetzung 8.815659854792027e-06 -▁Line 8.815659854792027e-06 -▁SDL 8.815659854792027e-06 -▁plain 8.815659854792027e-06 -▁refused 8.815659854792027e-06 -opping 8.811871431992375e-06 -▁C5-0 8.811871431992375e-06 -▁buses 8.811871431992375e-06 -▁specially 8.811871431992375e-06 -▁Hände 8.8080830091927213e-06 -▁Pilot 8.8080830091927213e-06 -▁Schicksal 8.8080830091927213e-06 -▁Village 8.8080830091927213e-06 -▁continuously 8.8080830091927213e-06 -▁Vertrauens 8.80429458639307e-06 -▁erkennt 8.80429458639307e-06 -▁strike 8.80429458639307e-06 -▁typisch 8.80429458639307e-06 -▁composed 8.8005061635934155e-06 -PG 8.7967177407937634e-06 -SI 8.7967177407937634e-06 -zehn 8.7967177407937634e-06 -▁Referenz 8.7967177407937634e-06 -▁Weltwirtschaft 8.7967177407937634e-06 -▁anzupassen 8.7967177407937634e-06 -▁convey 8.7967177407937634e-06 -▁evolution 8.7967177407937634e-06 -▁fur 8.7967177407937634e-06 -gebäude 8.79292931799411e-06 -lement 8.79292931799411e-06 -▁Behörde 8.79292931799411e-06 -▁Meilen 8.79292931799411e-06 -▁Point 8.79292931799411e-06 -▁rebu 8.79292931799411e-06 -▁tower 8.79292931799411e-06 -▁wage 8.7891408951944577e-06 -▁beweisen 8.7853524723948056e-06 -▁cu 8.7853524723948056e-06 -▁forg 8.7853524723948056e-06 -▁occupation 8.7853524723948056e-06 -▁tip 8.7853524723948056e-06 -▁twenty 8.7853524723948056e-06 -ckte 8.7815640495951519e-06 -immte 8.7815640495951519e-06 -▁Häfen 8.7815640495951519e-06 -▁Mix 8.7815640495951519e-06 -▁Palestinians 8.7815640495951519e-06 -▁wirklichen 8.7815640495951519e-06 -▁überrascht 8.7815640495951519e-06 -iod 8.7777756267955e-06 -▁Arts 8.7777756267955e-06 -▁Aussagen 8.7777756267955e-06 -▁Lebensmittelsicherheit 8.7777756267955e-06 -▁Visual 8.7777756267955e-06 -▁Wasch 8.7777756267955e-06 -▁certification 8.7777756267955e-06 -▁einher 8.7777756267955e-06 -▁facilit 8.7777756267955e-06 -lager 8.7739872039958461e-06 -▁Arr 8.7739872039958461e-06 -▁Southern 8.7739872039958461e-06 -▁collapse 8.7739872039958461e-06 -▁effizienter 8.7739872039958461e-06 -ausende 8.770198781196194e-06 -iang 8.770198781196194e-06 -schlagen 8.770198781196194e-06 -spielen 8.770198781196194e-06 -iones 8.76641035839654e-06 -ändig 8.76641035839654e-06 -ützen 8.76641035839654e-06 -▁behauptet 8.76641035839654e-06 -▁grass 8.76641035839654e-06 -▁tragedy 8.76641035839654e-06 -illon 8.7626219355968883e-06 -isst 8.7626219355968883e-06 -zeichnen 8.7626219355968883e-06 -▁Bestellungen 8.7626219355968883e-06 -▁Ferienwohnungen 8.7626219355968883e-06 -▁Growth 8.7626219355968883e-06 -▁Lis 8.7626219355968883e-06 -▁coup 8.7626219355968883e-06 -▁subsid 8.7626219355968883e-06 -road 8.7588335127972362e-06 -▁Ausrichtung 8.7588335127972362e-06 -▁innere 8.7588335127972362e-06 -normen 8.7550450899975825e-06 -ufe 8.7550450899975825e-06 -▁Cab 8.7550450899975825e-06 -▁Wen 8.7550450899975825e-06 -▁Zivil 8.7550450899975825e-06 -▁carte 8.7550450899975825e-06 -▁eingetragen 8.7550450899975825e-06 -▁forgotten 8.7550450899975825e-06 -antrag 8.75125666719793e-06 -mals 8.75125666719793e-06 -▁Zeitplan 8.75125666719793e-06 -▁befürworten 8.75125666719793e-06 -▁leb 8.75125666719793e-06 -▁logo 8.75125666719793e-06 -▁verantwortungs 8.75125666719793e-06 -ichteten 8.7474682443982767e-06 -ва 8.7474682443982767e-06 -▁Govern 8.7474682443982767e-06 -▁dupl 8.7474682443982767e-06 -reibt 8.7436798215986246e-06 -▁DJ 8.7436798215986246e-06 -▁revised 8.7398913987989709e-06 -▁¶ 8.7398913987989709e-06 --th 8.7361029759993189e-06 -enne 8.7361029759993189e-06 -schätzung 8.7361029759993189e-06 -▁Befehl 8.7361029759993189e-06 -▁brauch 8.7361029759993189e-06 -▁fried 8.7361029759993189e-06 -mail 8.7323145531996651e-06 -▁Fans 8.7323145531996651e-06 -▁driven 8.7323145531996651e-06 -▁unity 8.7323145531996651e-06 -▁Assist 8.7285261304000131e-06 -▁Nachdruck 8.7285261304000131e-06 -▁finds 8.7285261304000131e-06 -▁modify 8.7285261304000131e-06 -▁umfangreichen 8.7285261304000131e-06 -▁verursachen 8.7285261304000131e-06 -urd 8.724737707600361e-06 -▁Bart 8.724737707600361e-06 -▁Finnish 8.724737707600361e-06 -▁advis 8.724737707600361e-06 -▁riding 8.724737707600361e-06 -▁vergeben 8.724737707600361e-06 -▁versehen 8.724737707600361e-06 -biet 8.7209492848007073e-06 -etes 8.7209492848007073e-06 -▁oppose 8.7209492848007073e-06 -angebote 8.7171608620010553e-06 -gebot 8.7171608620010553e-06 -▁Wahrung 8.7171608620010553e-06 -▁bon 8.7171608620010553e-06 -rak 8.7133724392014015e-06 -▁frequency 8.7133724392014015e-06 -▁innov 8.7133724392014015e-06 -▁Felder 8.70958401640175e-06 -▁geringen 8.70958401640175e-06 -▁interne 8.70958401640175e-06 -▁parks 8.70958401640175e-06 -izieren 8.7057955936020957e-06 -▁Lateinamerika 8.7057955936020957e-06 -▁afraid 8.7057955936020957e-06 -▁assert 8.7057955936020957e-06 -freien 8.7020071708024437e-06 -gehenden 8.7020071708024437e-06 -stätigkeit 8.7020071708024437e-06 -▁28. 8.7020071708024437e-06 -▁nobody 8.7020071708024437e-06 -▁photograph 8.7020071708024437e-06 -▁publish 8.7020071708024437e-06 -▁zw 8.7020071708024437e-06 -felder 8.6982187480027916e-06 -▁Country 8.6982187480027916e-06 -▁Verheugen 8.6982187480027916e-06 -▁deadline 8.6982187480027916e-06 -▁mont 8.6982187480027916e-06 -▁‚ 8.6982187480027916e-06 -zü 8.6944303252031379e-06 -▁Han 8.6944303252031379e-06 -▁Kürze 8.6944303252031379e-06 -▁Otherwise 8.6944303252031379e-06 -▁Wünschen 8.6944303252031379e-06 -▁falling 8.6944303252031379e-06 -▁manufactured 8.6944303252031379e-06 -▁unterbrochen 8.6944303252031379e-06 -bi 8.6868534796038321e-06 -▁doubts 8.6868534796038321e-06 -▁erm 8.6868534796038321e-06 -▁promoted 8.6868534796038321e-06 -▁öffentlicher 8.6868534796038321e-06 -programmen 8.68306505680418e-06 -umer 8.68306505680418e-06 -ees 8.6792766340045263e-06 -entrum 8.6792766340045263e-06 -ε 8.6792766340045263e-06 -▁Put 8.6792766340045263e-06 -▁auswirken 8.6792766340045263e-06 -▁capt 8.6792766340045263e-06 -▁sculpt 8.6792766340045263e-06 -pressed 8.6754882112048743e-06 -uns 8.6754882112048743e-06 -▁Diesen 8.6754882112048743e-06 -▁Vorgehensweise 8.6754882112048743e-06 -▁Zahn 8.6754882112048743e-06 -▁eurozone 8.6754882112048743e-06 -▁knew 8.6754882112048743e-06 -▁lenken 8.6754882112048743e-06 -▁Übernahme 8.6754882112048743e-06 -▁Weiterhin 8.67169978840522e-06 -▁mater 8.67169978840522e-06 -▁whatsoever 8.67169978840522e-06 -▁Abbau 8.6679113656055685e-06 -▁günstigen 8.6679113656055685e-06 -▁Einzelheiten 8.6641229428059165e-06 -▁basically 8.6641229428059165e-06 -▁relaxed 8.6641229428059165e-06 -▁vorliegt 8.6641229428059165e-06 -stehende 8.6603345200062627e-06 -ville 8.6603345200062627e-06 -▁Double 8.6603345200062627e-06 -▁` 8.6603345200062627e-06 -▁einges 8.6603345200062627e-06 -▁einzu 8.6603345200062627e-06 -▁geehrte 8.6603345200062627e-06 -▁zero 8.6603345200062627e-06 -nehm 8.65654609720661e-06 -▁respektieren 8.65654609720661e-06 -▁Erl 8.6527576744069569e-06 -▁comb 8.6527576744069569e-06 -▁paths 8.6527576744069569e-06 -oles 8.6489692516073049e-06 --Spiel 8.6451808288076512e-06 -istence 8.6451808288076512e-06 -kurse 8.6451808288076512e-06 -otic 8.6451808288076512e-06 -▁Pros 8.6451808288076512e-06 -▁Tun 8.6451808288076512e-06 -▁Volkswirtschaften 8.6451808288076512e-06 -▁dimensions 8.6451808288076512e-06 -krit 8.6413924060079991e-06 -schiff 8.6413924060079991e-06 -▁Language 8.6413924060079991e-06 -▁Library 8.6413924060079991e-06 -▁Vertr 8.6413924060079991e-06 -▁ablehnen 8.6413924060079991e-06 -▁stopped 8.6413924060079991e-06 -hebung 8.6376039832083471e-06 -kunde 8.6376039832083471e-06 -rennen 8.6376039832083471e-06 -rig 8.6376039832083471e-06 -▁Zimmerbeschreibung 8.6376039832083471e-06 -▁healthcare 8.6376039832083471e-06 -▁wave 8.6376039832083471e-06 --class 8.6338155604086933e-06 -fertigung 8.6338155604086933e-06 -ührt 8.6338155604086933e-06 -▁Carl 8.6338155604086933e-06 -▁Expl 8.6338155604086933e-06 -▁affecting 8.6338155604086933e-06 -▁theoret 8.6338155604086933e-06 -cial 8.6300271376090413e-06 -jet 8.6300271376090413e-06 -word 8.6300271376090413e-06 -ц 8.6300271376090413e-06 -▁California 8.6300271376090413e-06 -▁Zurück 8.6300271376090413e-06 -▁belongs 8.6300271376090413e-06 -▁körper 8.6300271376090413e-06 -card 8.6262387148093876e-06 -▁Alternativen 8.6262387148093876e-06 -▁Baden 8.6262387148093876e-06 -▁Ten 8.6262387148093876e-06 -▁Zeich 8.6262387148093876e-06 -▁verantwort 8.6262387148093876e-06 -hnt 8.6224502920097355e-06 -reift 8.6224502920097355e-06 -ét 8.6224502920097355e-06 -▁NEW 8.6224502920097355e-06 -▁beef 8.6224502920097355e-06 -plätzen 8.6186618692100818e-06 -▁Leid 8.6186618692100818e-06 -▁dring 8.6186618692100818e-06 -▁pattern 8.6186618692100818e-06 -▁pharmaceutical 8.6186618692100818e-06 -▁quantity 8.6186618692100818e-06 -▁removal 8.6186618692100818e-06 -.C 8.61487344641043e-06 -imo 8.61487344641043e-06 -▁Clubs 8.61487344641043e-06 -▁Cologne 8.61487344641043e-06 -▁Festplatte 8.61487344641043e-06 -▁expressly 8.61487344641043e-06 -▁gelang 8.61487344641043e-06 -▁losing 8.61487344641043e-06 -▁monuments 8.61487344641043e-06 -▁residents 8.61487344641043e-06 -iki 8.6110850236107777e-06 -ris 8.6110850236107777e-06 -▁Russlands 8.6110850236107777e-06 -▁1945 8.6072966008111239e-06 -▁E-Mail-Adresse 8.6072966008111239e-06 -▁aqu 8.6072966008111239e-06 -▁planen 8.6072966008111239e-06 -▁transitional 8.6072966008111239e-06 --Ges 8.6035081780114719e-06 -▁Bekannt 8.6035081780114719e-06 -▁Offenheit 8.6035081780114719e-06 -▁Plätze 8.6035081780114719e-06 -▁bewegt 8.6035081780114719e-06 -▁erleichtert 8.6035081780114719e-06 -▁examined 8.6035081780114719e-06 -▁focuses 8.6035081780114719e-06 -▁gemütlichen 8.6035081780114719e-06 -▁shr 8.6035081780114719e-06 -▁Olympic 8.5997197552118182e-06 -▁arrangement 8.5997197552118182e-06 -▁lib 8.5997197552118182e-06 -▁technisch 8.5997197552118182e-06 -imate 8.5959313324121661e-06 -▁progressive 8.5959313324121661e-06 -.The 8.5921429096125124e-06 -▁Conserv 8.5921429096125124e-06 -▁Statistiken 8.5921429096125124e-06 -IO 8.58835448681286e-06 -rachte 8.58835448681286e-06 -▁Anton 8.58835448681286e-06 -▁Cond 8.58835448681286e-06 -▁Know-how 8.58835448681286e-06 -▁Richter 8.58835448681286e-06 -▁affordable 8.58835448681286e-06 -▁loans 8.58835448681286e-06 -▁plural 8.58835448681286e-06 -▁Peace 8.5845660640132066e-06 -▁begegnen 8.5845660640132066e-06 -col 8.5807776412135545e-06 -prüfung 8.5807776412135545e-06 -▁Circ 8.5807776412135545e-06 -▁Keine 8.5807776412135545e-06 -▁Nichts 8.5807776412135545e-06 -▁Saudi 8.5807776412135545e-06 -▁Taten 8.5807776412135545e-06 -▁ausgestattete 8.5807776412135545e-06 -▁installations 8.5807776412135545e-06 -▁jun 8.5807776412135545e-06 -ziger 8.5769892184139025e-06 -▁Heim 8.5769892184139025e-06 -▁SH 8.5769892184139025e-06 -▁defeat 8.5769892184139025e-06 -▁derselben 8.5769892184139025e-06 -▁engen 8.5769892184139025e-06 -▁günstige 8.5769892184139025e-06 -▁institutionelle 8.5769892184139025e-06 -▁instruct 8.5769892184139025e-06 -▁kürz 8.5769892184139025e-06 -▁productive 8.5769892184139025e-06 -▁visits 8.5769892184139025e-06 -▁endgültige 8.5732007956142488e-06 -▁integrity 8.5732007956142488e-06 -elung 8.5694123728145967e-06 -▁Ocean 8.5694123728145967e-06 -▁Tampere 8.5694123728145967e-06 -▁zwölf 8.5694123728145967e-06 -cript 8.565623950014943e-06 -ocal 8.565623950014943e-06 -▁Aufhebung 8.565623950014943e-06 -▁Cat 8.565623950014943e-06 -▁Thermal 8.565623950014943e-06 -▁buying 8.565623950014943e-06 -▁up-to-date 8.565623950014943e-06 -chnet 8.5618355272152909e-06 -▁Profil 8.5618355272152909e-06 -▁Third 8.5618355272152909e-06 -meister 8.5580471044156372e-06 -öpfen 8.5580471044156372e-06 -▁Nied 8.5580471044156372e-06 -▁könne 8.5580471044156372e-06 -▁relief 8.5580471044156372e-06 -antly 8.5542586816159852e-06 -ases 8.5542586816159852e-06 -▁beruh 8.5542586816159852e-06 -▁Chancengleichheit 8.5504702588163331e-06 -▁abges 8.5504702588163331e-06 -▁delays 8.5504702588163331e-06 -▁fortsetzen 8.5504702588163331e-06 -▁profound 8.5504702588163331e-06 -▁segment 8.5504702588163331e-06 -▁Dev 8.54668183601668e-06 -▁Kernel 8.54668183601668e-06 -▁Mid 8.54668183601668e-06 -▁Mischung 8.54668183601668e-06 -▁Tief 8.54668183601668e-06 -▁Zielsetzungen 8.54668183601668e-06 -▁Zusch 8.54668183601668e-06 -▁davor 8.54668183601668e-06 -▁declare 8.54668183601668e-06 -▁richt 8.54668183601668e-06 -dens 8.5428934132170273e-06 -▁Geschlechter 8.5428934132170273e-06 -▁accuracy 8.5428934132170273e-06 -▁renewed 8.5428934132170273e-06 -bern 8.5391049904173736e-06 -▁66 8.5391049904173736e-06 -▁broader 8.5391049904173736e-06 -▁flexibel 8.5391049904173736e-06 -oes 8.5353165676177215e-06 -▁SMS 8.5353165676177215e-06 -▁ceiling 8.5353165676177215e-06 -▁rob 8.5353165676177215e-06 -▁wunderschöne 8.5353165676177215e-06 -zeichn 8.5315281448180678e-06 -▁Erarbeitung 8.5315281448180678e-06 -▁Steigerung 8.5315281448180678e-06 -▁angekündigt 8.5315281448180678e-06 -▁tatsächlichen 8.5315281448180678e-06 -afe 8.5277397220184158e-06 -▁Eröffnung 8.5277397220184158e-06 -▁pace 8.5277397220184158e-06 -▁suited 8.5277397220184158e-06 -▁Ministerpräsident 8.523951299218762e-06 -▁Renaissance 8.523951299218762e-06 -▁fein 8.523951299218762e-06 -▁refuse 8.523951299218762e-06 -▁turns 8.523951299218762e-06 -▁С 8.523951299218762e-06 -bewegung 8.52016287641911e-06 -eding 8.52016287641911e-06 -▁Nationalpark 8.52016287641911e-06 -▁W. 8.52016287641911e-06 -▁distort 8.52016287641911e-06 -▁electoral 8.52016287641911e-06 -▁ethical 8.52016287641911e-06 -▁gestartet 8.52016287641911e-06 -▁übermitteln 8.52016287641911e-06 -▁Syria 8.5163744536194579e-06 -▁realisiert 8.5163744536194579e-06 -▁tap 8.5163744536194579e-06 -auen 8.5125860308198042e-06 -til 8.5125860308198042e-06 -▁Einnahmen 8.5125860308198042e-06 -▁Halle 8.5125860308198042e-06 -▁Representative 8.5125860308198042e-06 -▁sogenannte 8.5125860308198042e-06 -TC 8.5087976080201521e-06 -▁Intel 8.5087976080201521e-06 -▁advise 8.5087976080201521e-06 -▁sake 8.5087976080201521e-06 -größ 8.5050091852204984e-06 -leicht 8.5050091852204984e-06 -▁92 8.5050091852204984e-06 -▁ok 8.5050091852204984e-06 -bas 8.5012207624208464e-06 -▁Aug 8.5012207624208464e-06 -▁Deep 8.5012207624208464e-06 -▁Fischler 8.5012207624208464e-06 -▁Module 8.5012207624208464e-06 -▁Palette 8.5012207624208464e-06 -▁Pent 8.5012207624208464e-06 -▁Philipp 8.5012207624208464e-06 -▁Vertriebs 8.5012207624208464e-06 -▁automotive 8.5012207624208464e-06 -▁cult 8.5012207624208464e-06 -▁erforderliche 8.5012207624208464e-06 -asien 8.4974323396211926e-06 -rachen 8.4974323396211926e-06 -uced 8.4974323396211926e-06 -▁Betrifft 8.4974323396211926e-06 -▁Gefängnis 8.4974323396211926e-06 -▁arabischen 8.4974323396211926e-06 -▁eleganten 8.4974323396211926e-06 -▁Seen 8.49364391682154e-06 -▁exhibitions 8.49364391682154e-06 -▁spiel 8.49364391682154e-06 -▁vorsieht 8.49364391682154e-06 -indliche 8.4898554940218885e-06 -oga 8.4898554940218885e-06 -▁Send 8.4898554940218885e-06 -▁advent 8.4898554940218885e-06 -▁keys 8.4898554940218885e-06 -▁literature 8.4898554940218885e-06 -stone 8.4860670712222348e-06 -tent 8.4860670712222348e-06 -▁Derzeit 8.4860670712222348e-06 -▁Laser 8.4860670712222348e-06 -▁Millennium 8.4860670712222348e-06 -ktions 8.4822786484225828e-06 -▁Left 8.4822786484225828e-06 -▁präsent 8.4822786484225828e-06 -anke 8.478490225622929e-06 -▁Bosnien 8.478490225622929e-06 -▁Look 8.478490225622929e-06 -▁zukommen 8.478490225622929e-06 -möglichkeit 8.474701802823277e-06 -▁Baut 8.474701802823277e-06 -▁Einkommens 8.474701802823277e-06 -▁Ernst 8.474701802823277e-06 -▁Kenntnisse 8.474701802823277e-06 -▁Liberalen 8.474701802823277e-06 -▁logical 8.474701802823277e-06 -▁2015 8.4709133800236232e-06 -▁Georgien 8.4709133800236232e-06 -▁Händen 8.4709133800236232e-06 -▁Partnership 8.4709133800236232e-06 -▁Wikicars 8.4709133800236232e-06 -▁abgesehen 8.4709133800236232e-06 -▁empfängt 8.4709133800236232e-06 -▁observation 8.4709133800236232e-06 -▁stressed 8.4709133800236232e-06 -ached 8.4671249572239712e-06 -▁Ausnahmen 8.4671249572239712e-06 -▁Governance 8.4671249572239712e-06 -▁architectural 8.4671249572239712e-06 -ikanischer 8.4633365344243174e-06 -stücke 8.4633365344243174e-06 -▁Schluß 8.4633365344243174e-06 -▁initi 8.4633365344243174e-06 -▁irgende 8.4633365344243174e-06 -ask 8.4595481116246654e-06 -kap 8.4595481116246654e-06 -obby 8.4595481116246654e-06 -▁Tee 8.4595481116246654e-06 -▁granting 8.4595481116246654e-06 -Гј 8.4557596888250134e-06 -▁coal 8.4557596888250134e-06 -▁erstreckt 8.4557596888250134e-06 -▁herrschen 8.4557596888250134e-06 -▁zahl 8.4557596888250134e-06 -overs 8.45197126602536e-06 -▁Bureau 8.45197126602536e-06 -▁Defence 8.45197126602536e-06 -▁Gute 8.45197126602536e-06 -▁Lad 8.45197126602536e-06 -▁27. 8.4481828432257076e-06 -▁Corn 8.4481828432257076e-06 -▁Protest 8.4481828432257076e-06 --tra 8.4443944204260538e-06 -ventions 8.4443944204260538e-06 -▁Beschränkungen 8.4443944204260538e-06 -▁Loc 8.4443944204260538e-06 -▁Ufer 8.4443944204260538e-06 -▁Vit 8.4443944204260538e-06 -▁cameras 8.4443944204260538e-06 -▁deserve 8.4443944204260538e-06 -▁ere 8.4443944204260538e-06 -▁gal 8.4443944204260538e-06 -▁parlamentarischen 8.4443944204260538e-06 -▁wars 8.4443944204260538e-06 -ifications 8.4406059976264018e-06 -▁Arzneimittel 8.4406059976264018e-06 -▁Len 8.4406059976264018e-06 -▁mechanical 8.4406059976264018e-06 -entlich 8.4368175748267481e-06 -ş 8.4368175748267481e-06 -▁Cert 8.4368175748267481e-06 -▁Geschenk 8.4368175748267481e-06 -▁Mund 8.4368175748267481e-06 -▁angenommenen 8.4368175748267481e-06 -▁deleg 8.4368175748267481e-06 -▁novel 8.4368175748267481e-06 -▁therapy 8.4368175748267481e-06 -47 8.433029152027096e-06 -irat 8.433029152027096e-06 -▁Mengen 8.433029152027096e-06 -▁Soc 8.433029152027096e-06 -▁catalogue 8.433029152027096e-06 -▁mouth 8.433029152027096e-06 -▁sailing 8.433029152027096e-06 -icy 8.429240729227444e-06 -▁Distribution 8.429240729227444e-06 -▁Zerstörung 8.429240729227444e-06 -▁painting 8.429240729227444e-06 -▁researchers 8.429240729227444e-06 -grenze 8.42545230642779e-06 -lad 8.42545230642779e-06 -nitten 8.42545230642779e-06 -otte 8.42545230642779e-06 -urer 8.42545230642779e-06 -▁Photo 8.42545230642779e-06 -▁whenever 8.42545230642779e-06 -ammer 8.4216638836281382e-06 -ommission 8.4216638836281382e-06 -zing 8.4216638836281382e-06 -▁chose 8.4216638836281382e-06 -▁deutliche 8.4216638836281382e-06 -▁newspaper 8.4216638836281382e-06 -▁pensions 8.4216638836281382e-06 -▁zugestimmt 8.4216638836281382e-06 -apping 8.4178754608284844e-06 -PM 8.4140870380288324e-06 -▁Kompl 8.4140870380288324e-06 -▁Olymp 8.4140870380288324e-06 -▁emphas 8.4140870380288324e-06 -▁erheblichen 8.4140870380288324e-06 -▁innovations 8.4140870380288324e-06 -▁v. 8.4140870380288324e-06 -äge 8.4102986152291787e-06 -▁distinction 8.4102986152291787e-06 -▁trigger 8.4102986152291787e-06 -eans 8.4065101924295266e-06 -▁18th 8.4065101924295266e-06 -▁brands 8.4065101924295266e-06 -▁fiel 8.4065101924295266e-06 -▁publishing 8.4065101924295266e-06 -aufnahme 8.4027217696298746e-06 -chtet 8.4027217696298746e-06 -▁Brok 8.4027217696298746e-06 -▁Empfänger 8.4027217696298746e-06 -▁amending 8.4027217696298746e-06 -▁berichten 8.4027217696298746e-06 -▁confident 8.4027217696298746e-06 -▁duration 8.4027217696298746e-06 -▁erstens 8.4027217696298746e-06 -schlägen 8.39893334683022e-06 -▁verzichten 8.39893334683022e-06 -ichern 8.3951449240305688e-06 -verbindungen 8.3951449240305688e-06 -▁Klang 8.3951449240305688e-06 -▁achievements 8.3951449240305688e-06 -▁obligator 8.3951449240305688e-06 -▁170 8.3913565012309151e-06 -▁soldiers 8.3913565012309151e-06 -▁Rural 8.387568078431263e-06 -▁ähn 8.387568078431263e-06 -iveness 8.38377965563161e-06 -ska 8.38377965563161e-06 -▁Ell 8.38377965563161e-06 -▁Kost 8.38377965563161e-06 -▁Werkzeuge 8.38377965563161e-06 -▁compos 8.38377965563161e-06 -▁rac 8.38377965563161e-06 -ccess 8.3799912328319572e-06 -▁Hyp 8.3799912328319572e-06 -▁bewußt 8.3799912328319572e-06 -▁constructed 8.3799912328319572e-06 -▁erwähnten 8.3799912328319572e-06 -ather 8.3762028100323035e-06 -▁Händler 8.3762028100323035e-06 -▁ocean 8.3762028100323035e-06 -▁prestigious 8.3762028100323035e-06 -▁rom 8.3762028100323035e-06 -▁trains 8.3762028100323035e-06 -aine 8.3724143872326514e-06 -edy 8.3724143872326514e-06 -▁langem 8.3724143872326514e-06 -ults 8.368625964433e-06 -urance 8.368625964433e-06 -▁Größen 8.368625964433e-06 -▁einsch 8.368625964433e-06 -▁mathem 8.368625964433e-06 -▁pressing 8.368625964433e-06 -III 8.3648375416333457e-06 -onte 8.3648375416333457e-06 -schließt 8.3648375416333457e-06 -▁Alltag 8.3648375416333457e-06 -▁Südafrika 8.3648375416333457e-06 -▁Tarifa 8.3648375416333457e-06 -▁gate 8.3648375416333457e-06 -▁tough 8.3648375416333457e-06 -▁unt 8.3648375416333457e-06 -▁üblichen 8.3648375416333457e-06 -93 8.3610491188336936e-06 -reu 8.3610491188336936e-06 -verein 8.3610491188336936e-06 -▁EU- 8.3610491188336936e-06 -▁KOM 8.3610491188336936e-06 -▁anf 8.3610491188336936e-06 -▁bro 8.3610491188336936e-06 -▁officially 8.3610491188336936e-06 -▁regulated 8.3610491188336936e-06 -ässig 8.35726069603404e-06 -▁Beijing 8.35726069603404e-06 -▁Netze 8.35726069603404e-06 -▁Privatsphäre 8.35726069603404e-06 -▁abgest 8.35726069603404e-06 -▁cancelled 8.35726069603404e-06 -▁certificate 8.35726069603404e-06 -▁partic 8.35726069603404e-06 -funktionen 8.3534722732343878e-06 -▁Erfolgs 8.3534722732343878e-06 -▁Rennen 8.3534722732343878e-06 -▁leiten 8.3534722732343878e-06 -▁prin 8.3534722732343878e-06 -abhäng 8.3496838504347341e-06 -▁conversion 8.3496838504347341e-06 -▁freight 8.3496838504347341e-06 -▁größter 8.3496838504347341e-06 -▁schedule 8.3496838504347341e-06 -fortable 8.345895427635082e-06 -▁Erhalt 8.345895427635082e-06 -▁Prom 8.345895427635082e-06 -▁Rückgang 8.345895427635082e-06 -▁Versprechen 8.345895427635082e-06 -▁chemische 8.345895427635082e-06 -▁eröffnen 8.345895427635082e-06 -▁ursprüngliche 8.345895427635082e-06 -herr 8.34210700483543e-06 -klus 8.34210700483543e-06 -▁Charme 8.34210700483543e-06 -▁bot 8.34210700483543e-06 -▁explicitly 8.34210700483543e-06 -▁problemlos 8.34210700483543e-06 -ereien 8.3383185820357763e-06 -rufen 8.3383185820357763e-06 -▁Prize 8.3383185820357763e-06 -▁Wett 8.3383185820357763e-06 -▁evalu 8.3383185820357763e-06 -▁zun 8.3383185820357763e-06 -▁Arzt 8.3345301592361242e-06 -▁Gemeinsam 8.3345301592361242e-06 -eht 8.33074173643647e-06 -heil 8.33074173643647e-06 -▁Andr 8.33074173643647e-06 -▁Bach 8.33074173643647e-06 -▁Come 8.33074173643647e-06 -▁Inhalten 8.33074173643647e-06 -▁Tower 8.33074173643647e-06 -▁beneficial 8.33074173643647e-06 --Ex 8.3269533136368184e-06 -ARD 8.3269533136368184e-06 -abor 8.3269533136368184e-06 -gliche 8.3269533136368184e-06 -stechn 8.3269533136368184e-06 -▁Gesichtspunkt 8.3269533136368184e-06 -▁Grafik 8.3269533136368184e-06 -▁einzusch 8.3269533136368184e-06 -▁lakes 8.3269533136368184e-06 -▁preparations 8.3269533136368184e-06 -▁stops 8.3269533136368184e-06 -▁63 8.3231648908371647e-06 -▁Andalus 8.3231648908371647e-06 -▁consist 8.3231648908371647e-06 -▁lab 8.3231648908371647e-06 -▁niedrigen 8.3231648908371647e-06 -▁noon 8.3231648908371647e-06 -▁unusual 8.3231648908371647e-06 -▁Anzeige 8.3193764680375127e-06 -▁beteiligten 8.3193764680375127e-06 -▁doctor 8.3155880452378589e-06 -▁genießt 8.3155880452378589e-06 -▁interessanten 8.3155880452378589e-06 -▁komfortablen 8.3155880452378589e-06 -betr 8.3117996224382069e-06 -glied 8.3117996224382069e-06 -hört 8.3117996224382069e-06 -▁sandy 8.3117996224382069e-06 -▁vereint 8.3117996224382069e-06 -anca 8.3080111996385548e-06 -isp 8.3080111996385548e-06 -▁anpassen 8.3080111996385548e-06 -▁stärksten 8.3080111996385548e-06 -▁surveillance 8.3080111996385548e-06 -anged 8.3042227768389011e-06 -prozesse 8.3042227768389011e-06 -▁NRO 8.3042227768389011e-06 -▁observ 8.3042227768389011e-06 -▁steigt 8.3042227768389011e-06 -▁zurzeit 8.3042227768389011e-06 -AND 8.300434354039249e-06 -teilungen 8.300434354039249e-06 -▁concerts 8.300434354039249e-06 -▁gemeinsamer 8.300434354039249e-06 -▁populations 8.300434354039249e-06 -▁proximity 8.300434354039249e-06 -▁territories 8.300434354039249e-06 -inf 8.2966459312395953e-06 -uctive 8.2966459312395953e-06 -war 8.2966459312395953e-06 -reiber 8.2928575084399433e-06 -▁Montage 8.2928575084399433e-06 -▁PPE-DE 8.2928575084399433e-06 -aktionen 8.28906908564029e-06 -▁machinery 8.28906908564029e-06 -▁vermutlich 8.28906908564029e-06 -▁delete 8.2852806628406375e-06 -▁eindeutige 8.2852806628406375e-06 -▁einzig 8.2852806628406375e-06 -▁gehabt 8.2852806628406375e-06 -▁stretch 8.2852806628406375e-06 -▁toilet 8.2852806628406375e-06 -ahre 8.2814922400409854e-06 -blatt 8.2814922400409854e-06 -häng 8.2814922400409854e-06 -tin 8.2814922400409854e-06 -▁Buddh 8.2814922400409854e-06 -▁Großen 8.2814922400409854e-06 -▁Sri 8.2814922400409854e-06 -▁premium 8.2814922400409854e-06 -▁split 8.2814922400409854e-06 -▁wächst 8.2814922400409854e-06 -derung 8.2777038172413317e-06 -onie 8.2777038172413317e-06 -▁KG 8.2777038172413317e-06 -▁Konvergenz 8.2777038172413317e-06 -▁Kön 8.2777038172413317e-06 -▁erweisen 8.2777038172413317e-06 -FS 8.27391539444168e-06 -otes 8.27391539444168e-06 -▁Bürokratie 8.27391539444168e-06 -▁Deposit 8.27391539444168e-06 -▁Terroristen 8.27391539444168e-06 -▁focusing 8.27391539444168e-06 -▁gebunden 8.27391539444168e-06 -örung 8.2701269716420259e-06 -▁Ltd 8.2701269716420259e-06 -▁Natural 8.2701269716420259e-06 -▁short-term 8.2701269716420259e-06 -▁verabschieden 8.2701269716420259e-06 -ichtung 8.2663385488423739e-06 -▁Prop 8.2663385488423739e-06 -▁leistet 8.2663385488423739e-06 -▁smoking 8.2663385488423739e-06 -▁undertake 8.2663385488423739e-06 -▁Klassen 8.26255012604272e-06 -▁hab 8.26255012604272e-06 -opf 8.2587617032430681e-06 -▁Minibar 8.2587617032430681e-06 -▁Repräsent 8.2587617032430681e-06 -▁greenhouse 8.2587617032430681e-06 -▁ignore 8.2587617032430681e-06 -▁д 8.2587617032430681e-06 --Br 8.2549732804434143e-06 -▁Som 8.2549732804434143e-06 -▁einiges 8.2549732804434143e-06 -▁entrepreneurs 8.2549732804434143e-06 -▁identification 8.2549732804434143e-06 -▁movies 8.2549732804434143e-06 -▁pays 8.2549732804434143e-06 -▁zentraler 8.2549732804434143e-06 -itar 8.2511848576437623e-06 -änderung 8.2511848576437623e-06 -äuf 8.2511848576437623e-06 -▁Begründung 8.2511848576437623e-06 -▁burn 8.2511848576437623e-06 -▁dieselben 8.2511848576437623e-06 -▁differ 8.2511848576437623e-06 -▁eigentliche 8.2511848576437623e-06 -▁employers 8.2511848576437623e-06 -ixel 8.24739643484411e-06 -zut 8.24739643484411e-06 -▁partition 8.24739643484411e-06 -▁publications 8.24739643484411e-06 -▁typically 8.24739643484411e-06 -▁Profit 8.2436080120444565e-06 -▁Wahr 8.2436080120444565e-06 -▁arrange 8.2436080120444565e-06 -▁dieselbe 8.2436080120444565e-06 -▁solo 8.2436080120444565e-06 -▁umso 8.2436080120444565e-06 -kunst 8.2398195892448045e-06 -▁Ausgrenzung 8.2398195892448045e-06 -▁Wiederherstellung 8.2398195892448045e-06 -▁erarbeiten 8.2398195892448045e-06 -▁harbour 8.2398195892448045e-06 -▁zugute 8.2398195892448045e-06 -beg 8.23603116644515e-06 -ießt 8.23603116644515e-06 -▁centers 8.23603116644515e-06 -idem 8.2322427436454987e-06 -kapital 8.2322427436454987e-06 -▁Alban 8.2322427436454987e-06 -▁Brücke 8.2322427436454987e-06 -▁komplexen 8.2322427436454987e-06 -▁prevented 8.2322427436454987e-06 -formation 8.228454320845845e-06 -polit 8.228454320845845e-06 -äume 8.228454320845845e-06 -▁Updates 8.228454320845845e-06 -▁afrikanischen 8.228454320845845e-06 -▁angeb 8.228454320845845e-06 -▁bege 8.228454320845845e-06 -▁drives 8.228454320845845e-06 -▁necessity 8.228454320845845e-06 -▁precious 8.228454320845845e-06 -▁rechnen 8.228454320845845e-06 -21 8.2246658980461929e-06 -▁lets 8.2246658980461929e-06 -▁organiz 8.2208774752465409e-06 -aft 8.2170890524468871e-06 -gre 8.2170890524468871e-06 -kar 8.2170890524468871e-06 -▁Capital 8.2170890524468871e-06 -▁Kreat 8.2170890524468871e-06 -▁gelangt 8.2170890524468871e-06 -▁Planet 8.2133006296472351e-06 -▁roots 8.2133006296472351e-06 -▁suspended 8.2133006296472351e-06 -▁trav 8.2133006296472351e-06 -reform 8.2095122068475813e-06 -▁Everything 8.2095122068475813e-06 -▁catalog 8.2095122068475813e-06 -▁os 8.2095122068475813e-06 -▁releases 8.2095122068475813e-06 -sprachen 8.20572378404793e-06 -æ 8.20572378404793e-06 -▁Lasten 8.20572378404793e-06 -▁List 8.20572378404793e-06 -▁Umstände 8.20572378404793e-06 -▁civilian 8.20572378404793e-06 -rän 8.2019353612482756e-06 -▁Formulierung 8.1981469384486235e-06 -▁verliehen 8.1981469384486235e-06 -egung 8.1943585156489715e-06 -etti 8.1943585156489715e-06 -usten 8.1943585156489715e-06 -ündet 8.1943585156489715e-06 -▁Bewohner 8.1943585156489715e-06 -▁eingeladen 8.1943585156489715e-06 -▁trained 8.1943585156489715e-06 -di 8.1905700928493177e-06 -▁Einsch 8.1905700928493177e-06 -▁Nic 8.1905700928493177e-06 -▁eintreten 8.1905700928493177e-06 -▁formuliert 8.1905700928493177e-06 -▁kan 8.1905700928493177e-06 -▁pen 8.1905700928493177e-06 -▁verwiesen 8.1905700928493177e-06 -▁2nd 8.1867816700496657e-06 -▁akzept 8.1867816700496657e-06 -▁conform 8.1867816700496657e-06 -▁schwe 8.1867816700496657e-06 -▁vermieden 8.1867816700496657e-06 -entlichen 8.1829932472500119e-06 -schreit 8.1829932472500119e-06 -spunkt 8.1829932472500119e-06 -▁Art. 8.1829932472500119e-06 -▁preserved 8.1829932472500119e-06 -▁österreichischen 8.1829932472500119e-06 -ested 8.17920482445036e-06 -▁125 8.17920482445036e-06 -▁Nichtraucherzonen 8.17920482445036e-06 -▁debated 8.17920482445036e-06 -▁signals 8.17920482445036e-06 -ali 8.1754164016507062e-06 -head 8.1754164016507062e-06 -iking 8.1754164016507062e-06 -▁ausführlich 8.1754164016507062e-06 -▁entr 8.1754164016507062e-06 -▁uncertainty 8.1754164016507062e-06 --Gruppe 8.1716279788510541e-06 --Mitglied 8.1716279788510541e-06 -dere 8.1716279788510541e-06 -ikes 8.1716279788510541e-06 -▁Dest 8.1716279788510541e-06 -▁Flor 8.1716279788510541e-06 -▁arrest 8.1716279788510541e-06 -▁befürwortet 8.1716279788510541e-06 -▁gewünschte 8.1716279788510541e-06 -▁moder 8.1716279788510541e-06 -▁transnational 8.1716279788510541e-06 -▁adjustment 8.1678395560514e-06 -▁favourite 8.1678395560514e-06 -▁interactive 8.1678395560514e-06 -▁invent 8.1678395560514e-06 -▁unions 8.1678395560514e-06 -obe 8.1640511332517483e-06 -▁Problematik 8.1640511332517483e-06 -▁Solutions 8.1640511332517483e-06 -▁Taliban 8.1640511332517483e-06 -▁logistics 8.1640511332517483e-06 -▁revealed 8.1640511332517483e-06 -nah 8.1602627104520963e-06 -orb 8.1602627104520963e-06 -▁admit 8.1602627104520963e-06 -esk 8.1564742876524426e-06 -▁Farm 8.1564742876524426e-06 -▁jüngste 8.1564742876524426e-06 -▁regionaler 8.1564742876524426e-06 -▁1930 8.15268586485279e-06 -▁Fakten 8.15268586485279e-06 -▁Kim 8.15268586485279e-06 -▁ment 8.15268586485279e-06 -▁senior 8.15268586485279e-06 -isons 8.1488974420531368e-06 -kriterien 8.1488974420531368e-06 -lesen 8.1488974420531368e-06 -▁GB 8.1488974420531368e-06 -▁Leon 8.1488974420531368e-06 -▁unknown 8.1488974420531368e-06 -▁musikal 8.1451090192534847e-06 -▁organize 8.1451090192534847e-06 -verteilung 8.141320596453831e-06 -▁Orient 8.141320596453831e-06 -▁dasselbe 8.141320596453831e-06 -▁insert 8.141320596453831e-06 -▁moderner 8.141320596453831e-06 -▁telecommunications 8.141320596453831e-06 -zeichnete 8.1375321736541789e-06 -▁Elektro 8.1375321736541789e-06 -▁Istanbul 8.1375321736541789e-06 -▁Night 8.1375321736541789e-06 -▁ents 8.1375321736541789e-06 -▁ermutigen 8.1375321736541789e-06 -verarbeitung 8.1337437508545269e-06 -▁DIN 8.1337437508545269e-06 -▁Logik 8.1337437508545269e-06 -▁Premier 8.1337437508545269e-06 -▁gegenseitigen 8.1337437508545269e-06 -annter 8.1299553280548732e-06 -aters 8.1299553280548732e-06 -wehr 8.1299553280548732e-06 -▁Superm 8.1299553280548732e-06 -▁ang 8.1299553280548732e-06 -andem 8.1261669052552211e-06 -rec 8.1261669052552211e-06 -▁GUE 8.1261669052552211e-06 -▁Menschenrechtsverletzungen 8.1261669052552211e-06 -▁frustr 8.1261669052552211e-06 -▁vertraut 8.1261669052552211e-06 -gekommen 8.1223784824555674e-06 -zentr 8.1223784824555674e-06 -▁Abenteuer 8.1223784824555674e-06 -▁Jugendliche 8.1223784824555674e-06 -▁PPE 8.1223784824555674e-06 -▁Zunahme 8.1223784824555674e-06 -▁winner 8.1223784824555674e-06 -möglichen 8.1185900596559153e-06 -▁Monti 8.1185900596559153e-06 -▁comprises 8.1185900596559153e-06 -▁hospital 8.1185900596559153e-06 -▁taxpayers 8.1185900596559153e-06 -bor 8.1148016368562616e-06 -stellte 8.1148016368562616e-06 -▁Aluminium 8.1148016368562616e-06 -▁judge 8.1148016368562616e-06 -umen 8.11101321405661e-06 -▁67 8.11101321405661e-06 -▁Experience 8.11101321405661e-06 -▁Rap 8.11101321405661e-06 -▁negotiated 8.11101321405661e-06 -auber 8.1072247912569558e-06 -izenz 8.1072247912569558e-06 -klar 8.1072247912569558e-06 -▁185 8.1072247912569558e-06 -▁photographs 8.1072247912569558e-06 -▁transit 8.1072247912569558e-06 -empt 8.1034363684573038e-06 -name 8.1034363684573038e-06 -▁Föderation 8.1034363684573038e-06 -▁Ker 8.1034363684573038e-06 -▁Spiegel 8.1034363684573038e-06 -▁attended 8.1034363684573038e-06 -▁kleineren 8.1034363684573038e-06 -▁verschaffen 8.1034363684573038e-06 -istor 8.0996479456576517e-06 -▁Verabschiedung 8.0996479456576517e-06 -▁aufgestellt 8.0996479456576517e-06 -▁sach 8.0996479456576517e-06 -▁schwieriger 8.0996479456576517e-06 -▁treas 8.0996479456576517e-06 -elemente 8.095859522857998e-06 -inc 8.095859522857998e-06 -osition 8.095859522857998e-06 -steil 8.095859522857998e-06 -▁abgeben 8.095859522857998e-06 -▁aussehen 8.095859522857998e-06 -▁bedauerlich 8.095859522857998e-06 -▁feedback 8.095859522857998e-06 -▁resp 8.095859522857998e-06 -▁spiritual 8.095859522857998e-06 -erweise 8.0920711000583459e-06 -hrte 8.0920711000583459e-06 -iehen 8.0920711000583459e-06 -situation 8.0920711000583459e-06 -ás 8.0920711000583459e-06 -ähe 8.0920711000583459e-06 -▁Ausbeutung 8.0920711000583459e-06 -▁ruling 8.0920711000583459e-06 -denken 8.0882826772586922e-06 -ennen 8.0882826772586922e-06 -oden 8.0882826772586922e-06 -wirkt 8.0882826772586922e-06 -▁Astr 8.0882826772586922e-06 -▁José 8.0882826772586922e-06 -▁leichte 8.0882826772586922e-06 -▁presidential 8.0882826772586922e-06 -▁reflection 8.0882826772586922e-06 -▁sel 8.0882826772586922e-06 -▁spare 8.0882826772586922e-06 -▁thanking 8.0882826772586922e-06 -▁vergangen 8.0882826772586922e-06 -▁на 8.0882826772586922e-06 -vin 8.08449425445904e-06 -▁Möbel 8.08449425445904e-06 -▁Which 8.08449425445904e-06 -▁indicators 8.08449425445904e-06 -▁integrieren 8.08449425445904e-06 -▁quot 8.08449425445904e-06 -▁Bürgerbeauftragten 8.0807058316593864e-06 -▁conservation 8.0807058316593864e-06 -▁geeigneten 8.0807058316593864e-06 -▁realize 8.0807058316593864e-06 -▁sterben 8.0807058316593864e-06 -▁Hussein 8.0769174088597344e-06 -▁kom 8.0769174088597344e-06 -▁löschen 8.0769174088597344e-06 -▁synth 8.0769174088597344e-06 -alte 8.0731289860600823e-06 -mechanismen 8.0731289860600823e-06 -uba 8.0731289860600823e-06 -▁78 8.0731289860600823e-06 -▁Empf 8.0731289860600823e-06 -▁Funktionieren 8.0731289860600823e-06 -▁Lissabon-Strategie 8.0731289860600823e-06 -▁treiben 8.0731289860600823e-06 -ouv 8.0693405632604286e-06 -▁einstellen 8.0693405632604286e-06 -▁illness 8.0693405632604286e-06 -▁medizinische 8.0693405632604286e-06 -▁mess 8.0693405632604286e-06 -▁threatened 8.0693405632604286e-06 -▁unbek 8.0693405632604286e-06 -omas 8.0655521404607765e-06 -pan 8.0655521404607765e-06 -▁Pflichten 8.0655521404607765e-06 -oys 8.0617637176611228e-06 -▁Until 8.0617637176611228e-06 -▁fails 8.0617637176611228e-06 -▁gewöhn 8.0617637176611228e-06 -▁pos 8.0617637176611228e-06 -▁Haft 8.05797529486147e-06 -▁Wärme 8.05797529486147e-06 -▁hesitate 8.05797529486147e-06 -bringung 8.054186872061817e-06 -gast 8.054186872061817e-06 -ль 8.054186872061817e-06 -▁Bow 8.054186872061817e-06 -▁CE 8.054186872061817e-06 -▁bist 8.054186872061817e-06 --Datei 8.050398449262165e-06 -ли 8.050398449262165e-06 -▁Sym 8.050398449262165e-06 -▁Wälder 8.050398449262165e-06 -▁ausländische 8.050398449262165e-06 -▁heutzutage 8.050398449262165e-06 -gebühren 8.0466100264625129e-06 -rob 8.0466100264625129e-06 -▁Arbeitszeit 8.0466100264625129e-06 -▁Inhalts 8.0466100264625129e-06 -▁Wesen 8.0466100264625129e-06 -gement 8.04282160366286e-06 -▁88 8.04282160366286e-06 -▁Finanzen 8.04282160366286e-06 -▁Holland 8.04282160366286e-06 -▁Kuba 8.04282160366286e-06 -▁Meinungen 8.04282160366286e-06 -▁Non-Smoking 8.04282160366286e-06 -▁Theorie 8.04282160366286e-06 -▁academic 8.04282160366286e-06 -▁wahre 8.04282160366286e-06 -▁Bevölkerungs 8.0390331808632071e-06 -▁Französisch 8.0390331808632071e-06 -▁Katal 8.0390331808632071e-06 -▁Produktivität 8.0390331808632071e-06 -▁Span 8.0390331808632071e-06 -▁acquisition 8.0390331808632071e-06 -▁bat 8.0390331808632071e-06 -komp 8.0352447580635534e-06 -pective 8.0352447580635534e-06 -strukturen 8.0352447580635534e-06 -▁Individual 8.0352447580635534e-06 -▁ratified 8.0352447580635534e-06 -▁sonstige 8.0352447580635534e-06 -olen 8.0314563352639014e-06 -orrow 8.0314563352639014e-06 -▁Handlungen 8.0314563352639014e-06 -▁Inhaber 8.0314563352639014e-06 -▁Mis 8.0314563352639014e-06 -▁durchzusetzen 8.0314563352639014e-06 -▁vereinbar 8.0314563352639014e-06 -.S 8.0276679124642476e-06 -▁purchasing 8.0276679124642476e-06 -▁späteren 8.0276679124642476e-06 -▁teilt 8.0276679124642476e-06 -ieve 8.0238794896645956e-06 -▁RF 8.0238794896645956e-06 -▁auss 8.0238794896645956e-06 -rant 8.0200910668649418e-06 -▁Comb 8.0200910668649418e-06 -▁Kapazität 8.0200910668649418e-06 -▁Kommissionsvorschlag 8.0200910668649418e-06 -▁Mountain 8.0200910668649418e-06 -▁gerechtfertigt 8.0200910668649418e-06 -▁heiß 8.0200910668649418e-06 -.jpg 8.01630264406529e-06 -CA 8.01630264406529e-06 -cor 8.01630264406529e-06 -▁Kennzeichnung 8.01630264406529e-06 -▁bezogen 8.01630264406529e-06 -▁gratis 8.01630264406529e-06 -▁nachhaltig 8.01630264406529e-06 -▁präzise 8.01630264406529e-06 -achtet 8.0125142212656378e-06 -ipel 8.0125142212656378e-06 -▁Genuss 8.0125142212656378e-06 -▁ranging 8.0125142212656378e-06 -▁replacement 8.0125142212656378e-06 -▁supplementary 8.0125142212656378e-06 -ieder 8.008725798465984e-06 -▁Angabe 8.008725798465984e-06 -▁Cra 8.008725798465984e-06 -▁Intern 8.008725798465984e-06 -▁Quoten 8.008725798465984e-06 -▁gebe 8.008725798465984e-06 -▁mistakes 8.008725798465984e-06 -▁silver 8.008725798465984e-06 -amar 8.004937375666332e-06 -▁Shopping 8.004937375666332e-06 -▁certainty 8.004937375666332e-06 -ен 8.0011489528666782e-06 -▁Abgeordneter 8.0011489528666782e-06 -▁Fir 8.0011489528666782e-06 -▁Stufe 8.0011489528666782e-06 -▁endgültigen 8.0011489528666782e-06 -wart 7.9973605300670262e-06 -▁Tw 7.9973605300670262e-06 -tract 7.9935721072673725e-06 -▁privileged 7.9935721072673725e-06 -▁rating 7.9935721072673725e-06 -iny 7.98978368446772e-06 -▁bisschen 7.98978368446772e-06 -▁chron 7.98978368446772e-06 -▁geringe 7.98978368446772e-06 -▁jeglicher 7.98978368446772e-06 -▁lim 7.98978368446772e-06 -museum 7.9859952616680684e-06 -▁Türen 7.9859952616680684e-06 -▁Verordnungen 7.9859952616680684e-06 -▁kommun 7.9859952616680684e-06 -▁Gate 7.9822068388684146e-06 -▁Verfolgung 7.9822068388684146e-06 -▁fehl 7.9822068388684146e-06 -▁hinein 7.9822068388684146e-06 -48 7.9784184160687626e-06 -estra 7.9784184160687626e-06 -▁Bilanz 7.9784184160687626e-06 -▁Drittstaaten 7.9784184160687626e-06 -▁Technologies 7.9784184160687626e-06 -ulf 7.9746299932691088e-06 -vey 7.9746299932691088e-06 -ütt 7.9746299932691088e-06 -▁Nat 7.9746299932691088e-06 -führungs 7.9708415704694568e-06 -gio 7.9708415704694568e-06 -rolle 7.9708415704694568e-06 -▁Kolle 7.9708415704694568e-06 -▁downloaded 7.9708415704694568e-06 -kungen 7.9670531476698031e-06 -▁360 7.9670531476698031e-06 -▁Anträge 7.9670531476698031e-06 -▁Argentina 7.9670531476698031e-06 -▁höchster 7.9670531476698031e-06 -▁retten 7.9670531476698031e-06 -▁ungewöhn 7.9670531476698031e-06 -▁verarbeitet 7.9670531476698031e-06 -gestellten 7.963264724870151e-06 -▁Li 7.963264724870151e-06 -▁bewertet 7.963264724870151e-06 -▁gesichert 7.963264724870151e-06 -▁imported 7.963264724870151e-06 -▁Konjunktur 7.9594763020704973e-06 -▁künst 7.9594763020704973e-06 -edo 7.9556878792708452e-06 -zeichneten 7.9556878792708452e-06 -▁Document 7.9556878792708452e-06 -▁Young 7.9556878792708452e-06 -▁basieren 7.9556878792708452e-06 -▁ess 7.9556878792708452e-06 -▁monument 7.9556878792708452e-06 --Über 7.9518994564711932e-06 -cast 7.9518994564711932e-06 -lossenen 7.9518994564711932e-06 -mente 7.9518994564711932e-06 -suchen 7.9518994564711932e-06 -▁Jordan 7.9518994564711932e-06 -▁Soft 7.9518994564711932e-06 -▁Watch 7.9518994564711932e-06 -▁wid 7.9518994564711932e-06 -iko 7.94811103367154e-06 -pel 7.94811103367154e-06 -▁237 7.94811103367154e-06 -▁Beit 7.94811103367154e-06 -▁demonstrates 7.94811103367154e-06 -▁farms 7.94811103367154e-06 -▁justify 7.94811103367154e-06 -▁negotiate 7.94811103367154e-06 -hausen 7.9443226108718874e-06 -ometer 7.9443226108718874e-06 -schluß 7.9443226108718874e-06 -steh 7.9443226108718874e-06 -teen 7.9443226108718874e-06 -▁1979 7.9443226108718874e-06 -▁Bewerber 7.9443226108718874e-06 -▁alike 7.9443226108718874e-06 -▁komplexe 7.9443226108718874e-06 -osh 7.9405341880722337e-06 -▁Mitglieds 7.9405341880722337e-06 -▁intent 7.9405341880722337e-06 -▁patterns 7.9405341880722337e-06 -mechanismus 7.9367457652725816e-06 -nungen 7.9367457652725816e-06 -▁deaths 7.9367457652725816e-06 -prop 7.9329573424729279e-06 -▁Bühne 7.9329573424729279e-06 -▁Kernkraft 7.9329573424729279e-06 -▁authentic 7.9329573424729279e-06 -▁citizenship 7.9329573424729279e-06 -▁generous 7.9329573424729279e-06 -▁mittlere 7.9329573424729279e-06 -▁religiösen 7.9329573424729279e-06 -▁wachsende 7.9329573424729279e-06 -▁Adriatic 7.9291689196732758e-06 -▁Summe 7.9291689196732758e-06 -▁announce 7.9291689196732758e-06 -▁moves 7.9291689196732758e-06 -100 7.9253804968736238e-06 -▁9001 7.9253804968736238e-06 -▁Catal 7.9253804968736238e-06 -▁Kart 7.9253804968736238e-06 -▁Num 7.9253804968736238e-06 -,7 7.92159207407397e-06 -nisses 7.92159207407397e-06 -▁Abstimmungs 7.92159207407397e-06 -▁Fischereipolitik 7.92159207407397e-06 -▁Picard 7.92159207407397e-06 -▁Zusammensetzung 7.92159207407397e-06 -▁boy 7.92159207407397e-06 -▁flying 7.92159207407397e-06 -▁tal 7.92159207407397e-06 --Str 7.917803651274318e-06 -ypt 7.917803651274318e-06 -▁Marokko 7.917803651274318e-06 -▁downtown 7.917803651274318e-06 -▁obs 7.917803651274318e-06 -▁patents 7.917803651274318e-06 -▁unterscheidet 7.917803651274318e-06 --Ch 7.9140152284746643e-06 -ouse 7.9140152284746643e-06 -ürzt 7.9140152284746643e-06 -ка 7.9140152284746643e-06 -▁enrich 7.9140152284746643e-06 -▁signing 7.9140152284746643e-06 -real 7.9102268056750122e-06 -▁ratio 7.9102268056750122e-06 -ienen 7.9064383828753585e-06 -▁Autom 7.9064383828753585e-06 -▁Bat 7.9064383828753585e-06 -▁Literatur 7.9064383828753585e-06 -▁NOT 7.9064383828753585e-06 -▁Roc 7.9064383828753585e-06 -▁Versuche 7.9064383828753585e-06 -▁angebotenen 7.9064383828753585e-06 -▁aufeinander 7.9064383828753585e-06 -▁consistently 7.9064383828753585e-06 -▁gestiegen 7.9064383828753585e-06 -▁heavily 7.9064383828753585e-06 -▁horizontal 7.9064383828753585e-06 -▁nationals 7.9064383828753585e-06 -lov 7.9026499600757064e-06 -ą 7.9026499600757064e-06 -▁accum 7.9026499600757064e-06 -▁cho 7.9026499600757064e-06 -▁depl 7.9026499600757064e-06 -▁occurs 7.9026499600757064e-06 -▁Conditions 7.8988615372760527e-06 -▁FA 7.8988615372760527e-06 -▁Heraus 7.8988615372760527e-06 -▁blieb 7.8988615372760527e-06 -▁empfiehlt 7.8988615372760527e-06 -▁icon 7.8988615372760527e-06 -▁notably 7.8988615372760527e-06 -▁passieren 7.8988615372760527e-06 -austausch 7.8950731144764e-06 -iente 7.8950731144764e-06 -▁188 7.8950731144764e-06 -▁Fremd 7.8950731144764e-06 -▁Hon 7.8950731144764e-06 -▁Museums 7.8950731144764e-06 -erba 7.8912846916767486e-06 -▁Anreize 7.8912846916767486e-06 -▁Dun 7.8912846916767486e-06 -▁liquid 7.8912846916767486e-06 -▁maximale 7.8912846916767486e-06 -▁wertvolle 7.8912846916767486e-06 -artet 7.8874962688770949e-06 -▁claimed 7.8874962688770949e-06 -▁emerged 7.8874962688770949e-06 -▁fäl 7.8874962688770949e-06 -icon 7.8837078460774428e-06 -iese 7.8837078460774428e-06 -▁Funktions 7.8837078460774428e-06 -▁Klimawandels 7.8837078460774428e-06 -▁Santer 7.8837078460774428e-06 -▁argue 7.8837078460774428e-06 -▁chemicals 7.8837078460774428e-06 -rück 7.8799194232777891e-06 -▁Spektrum 7.8799194232777891e-06 -▁XV 7.8799194232777891e-06 -▁beinahe 7.8799194232777891e-06 -▁clothing 7.8799194232777891e-06 -▁deficits 7.8799194232777891e-06 -▁favorite 7.8799194232777891e-06 -▁indicates 7.8799194232777891e-06 -▁recession 7.8799194232777891e-06 -▁worrying 7.8799194232777891e-06 -.. 7.876131000478137e-06 -aga 7.876131000478137e-06 -inischen 7.876131000478137e-06 -äder 7.876131000478137e-06 -▁FL 7.876131000478137e-06 -▁Scotland 7.876131000478137e-06 -▁bild 7.876131000478137e-06 -▁leather 7.876131000478137e-06 -▁mandatory 7.876131000478137e-06 -▁phones 7.876131000478137e-06 -AF 7.8723425776784833e-06 -uropäischen 7.8723425776784833e-06 -▁FAQ 7.8685541548788313e-06 -▁Gebühr 7.8685541548788313e-06 -▁Herkunfts 7.8685541548788313e-06 -▁Verlängerung 7.8685541548788313e-06 -▁sessions 7.8685541548788313e-06 -▁tram 7.8685541548788313e-06 -anto 7.86476573207918e-06 -ivate 7.86476573207918e-06 -▁Cy 7.86476573207918e-06 -▁beschreibt 7.86476573207918e-06 -▁bureaucracy 7.86476573207918e-06 -▁como 7.86476573207918e-06 -▁extens 7.86476573207918e-06 -▁ruhiger 7.86476573207918e-06 -illo 7.8609773092795255e-06 -rauch 7.8609773092795255e-06 -wendet 7.8609773092795255e-06 -ś 7.8609773092795255e-06 -▁airline 7.8609773092795255e-06 -▁dein 7.8609773092795255e-06 -▁orientation 7.8609773092795255e-06 -▁Fass 7.8571888864798734e-06 -▁apparently 7.8571888864798734e-06 -chem 7.85340046368022e-06 -ference 7.85340046368022e-06 -gener 7.85340046368022e-06 -owers 7.85340046368022e-06 -▁Aer 7.85340046368022e-06 -▁District 7.85340046368022e-06 -▁fanden 7.85340046368022e-06 -▁myth 7.85340046368022e-06 --cl 7.8496120408805677e-06 -aßnahmen 7.8496120408805677e-06 -fung 7.8496120408805677e-06 -igation 7.8496120408805677e-06 -leitungen 7.8496120408805677e-06 -▁closing 7.8496120408805677e-06 -▁creature 7.8496120408805677e-06 -▁füllen 7.8496120408805677e-06 -ego 7.8458236180809139e-06 -inth 7.8458236180809139e-06 -prä 7.8458236180809139e-06 -▁klass 7.8458236180809139e-06 -anas 7.8420351952812619e-06 -space 7.8420351952812619e-06 -ußen 7.8420351952812619e-06 -▁77 7.8420351952812619e-06 -▁Madeira 7.8420351952812619e-06 -▁Sul 7.8420351952812619e-06 -▁einstimmig 7.8420351952812619e-06 -▁returns 7.8420351952812619e-06 -▁Autoren 7.83824677248161e-06 -▁File 7.83824677248161e-06 -▁Hi 7.83824677248161e-06 -▁cooper 7.83824677248161e-06 -▁tre 7.83824677248161e-06 -▁notified 7.8344583496819561e-06 -▁Einwanderer 7.830669926882304e-06 -▁Palästinenser 7.830669926882304e-06 -▁applicant 7.830669926882304e-06 -▁predict 7.830669926882304e-06 -ardo 7.82688150408265e-06 -onts 7.82688150408265e-06 -▁Betrug 7.82688150408265e-06 -▁Excellent 7.82688150408265e-06 -▁Osteuropa 7.82688150408265e-06 -▁advertis 7.82688150408265e-06 -▁aggress 7.82688150408265e-06 -▁precision 7.82688150408265e-06 -▁vereinbarten 7.82688150408265e-06 -idel 7.8230930812829983e-06 -lassungen 7.8230930812829983e-06 -▁Cav 7.8230930812829983e-06 -▁Terminal 7.8230930812829983e-06 -▁architect 7.8230930812829983e-06 -▁aufgeführt 7.8230930812829983e-06 -▁cooking 7.8230930812829983e-06 -▁magic 7.8230930812829983e-06 -▁specify 7.8230930812829983e-06 -haushalt 7.8193046584833445e-06 -▁Darfur 7.8193046584833445e-06 -▁contributing 7.8193046584833445e-06 -ei 7.8155162356836925e-06 -prü 7.8155162356836925e-06 -▁affili 7.8155162356836925e-06 -▁intervene 7.8155162356836925e-06 -▁spektakul 7.8155162356836925e-06 -idd 7.8117278128840387e-06 -igu 7.8117278128840387e-06 -▁Tob 7.8117278128840387e-06 -▁displays 7.8117278128840387e-06 -iessen 7.8079393900843867e-06 -istenz 7.8079393900843867e-06 -methode 7.8079393900843867e-06 -▁License 7.8079393900843867e-06 -▁Wiener 7.8079393900843867e-06 -händler 7.8041509672847347e-06 -▁Bridge 7.8041509672847347e-06 -▁conventions 7.8041509672847347e-06 -▁dargelegt 7.8041509672847347e-06 -▁drinking 7.8041509672847347e-06 -▁technischer 7.8041509672847347e-06 -ews 7.8003625444850809e-06 -▁Beurteilung 7.8003625444850809e-06 -▁Erzeugung 7.8003625444850809e-06 -▁beschleunigen 7.8003625444850809e-06 -▁reveal 7.8003625444850809e-06 -▁vac 7.8003625444850809e-06 -ASS 7.7965741216854289e-06 -▁archive 7.7965741216854289e-06 -▁organisierten 7.7965741216854289e-06 -EO 7.7927856988857751e-06 -ierbar 7.7927856988857751e-06 -ugnis 7.7927856988857751e-06 -▁Brun 7.7927856988857751e-06 -▁anal 7.7927856988857751e-06 -▁angelegt 7.7927856988857751e-06 -▁tag 7.7927856988857751e-06 -▁übergeben 7.7927856988857751e-06 -altungs 7.7889972760861231e-06 -▁Olympus 7.7889972760861231e-06 -▁geschickt 7.7889972760861231e-06 -▁refund 7.7889972760861231e-06 -▁wherever 7.7889972760861231e-06 -apter 7.78520885328647e-06 -eing 7.78520885328647e-06 -kehrt 7.78520885328647e-06 -▁Menschenrechten 7.78520885328647e-06 -▁Referendum 7.78520885328647e-06 -▁englischen 7.78520885328647e-06 -▁geplanten 7.78520885328647e-06 -▁marginal 7.78520885328647e-06 -agements 7.7814204304868173e-06 -ologischer 7.7814204304868173e-06 -▁Tank 7.7814204304868173e-06 -▁Verfasser 7.7814204304868173e-06 -▁Visit 7.7814204304868173e-06 -▁kit 7.7814204304868173e-06 -▁prominent 7.7814204304868173e-06 -▁repräsent 7.7814204304868173e-06 -einen 7.7776320076871653e-06 -menü 7.7776320076871653e-06 -rez 7.7776320076871653e-06 -ungssysteme 7.7776320076871653e-06 -ährt 7.7776320076871653e-06 -▁Absch 7.7776320076871653e-06 -▁Strasse 7.7776320076871653e-06 -▁£ 7.7776320076871653e-06 -▁überwacht 7.7776320076871653e-06 -ismo 7.7738435848875115e-06 -▁Anwesenheit 7.7738435848875115e-06 -▁ausreichende 7.7738435848875115e-06 -▁mächt 7.7738435848875115e-06 -fahrten 7.77005516208786e-06 -verage 7.77005516208786e-06 -▁Albert 7.77005516208786e-06 -▁Bud 7.77005516208786e-06 -▁Erwägung 7.77005516208786e-06 -▁Klar 7.77005516208786e-06 -▁withdrawal 7.77005516208786e-06 -bauen 7.7662667392882057e-06 -büro 7.7662667392882057e-06 -ease 7.7662667392882057e-06 -pat 7.7662667392882057e-06 -▁Schla 7.7662667392882057e-06 -▁ham 7.7662667392882057e-06 -▁nov 7.7662667392882057e-06 -▁restaur 7.7662667392882057e-06 -udd 7.7624783164885537e-06 -▁Sab 7.7624783164885537e-06 -▁gelassen 7.7624783164885537e-06 -▁passt 7.7624783164885537e-06 -sters 7.7586898936889e-06 -▁Mary 7.7586898936889e-06 -▁eigenständ 7.7586898936889e-06 -▁evaluate 7.7586898936889e-06 -tag 7.7549014708892479e-06 -▁Belastung 7.7549014708892479e-06 -▁Feststellung 7.7549014708892479e-06 -▁Gil 7.7549014708892479e-06 -▁council 7.7549014708892479e-06 -▁judgment 7.7549014708892479e-06 -▁tonnes 7.7549014708892479e-06 -▁unanimously 7.7549014708892479e-06 -▁vielseit 7.7549014708892479e-06 -▁Konvention 7.7511130480895942e-06 -▁churches 7.7511130480895942e-06 -▁coordinate 7.7511130480895942e-06 -▁etabl 7.7511130480895942e-06 -fahrts 7.7473246252899421e-06 -▁converted 7.7473246252899421e-06 -erzeug 7.74353620249029e-06 -van 7.74353620249029e-06 -▁Citizens 7.74353620249029e-06 -▁Gallery 7.74353620249029e-06 -▁Präsentation 7.74353620249029e-06 -▁intelligence 7.74353620249029e-06 -emperatur 7.7397477796906363e-06 -▁Beratungen 7.7397477796906363e-06 -▁Exp 7.7397477796906363e-06 -▁Hügel 7.7397477796906363e-06 -▁bedienen 7.7397477796906363e-06 -▁eas 7.7397477796906363e-06 -▁formation 7.7397477796906363e-06 -menge 7.7359593568909843e-06 -unity 7.7359593568909843e-06 -älte 7.7359593568909843e-06 -ć 7.7359593568909843e-06 -▁Pict 7.7359593568909843e-06 -▁liegenden 7.7359593568909843e-06 -▁possess 7.7359593568909843e-06 -▁violent 7.7359593568909843e-06 -▁worry 7.7359593568909843e-06 -wür 7.73217093409133e-06 -äglich 7.73217093409133e-06 -▁BM 7.73217093409133e-06 -▁Heiligen 7.73217093409133e-06 -▁Schutzes 7.73217093409133e-06 -▁credible 7.73217093409133e-06 -▁experiencing 7.73217093409133e-06 -▁girl 7.73217093409133e-06 -▁inneren 7.73217093409133e-06 -▁measuring 7.73217093409133e-06 -BC 7.7283825112916785e-06 -▁Kos 7.7283825112916785e-06 -▁Legisl 7.7283825112916785e-06 -▁directions 7.7283825112916785e-06 -▁gewinnt 7.7283825112916785e-06 -▁ru 7.7283825112916785e-06 -GP 7.7245940884920248e-06 -ialog 7.7245940884920248e-06 -▁Gepäck 7.7245940884920248e-06 -▁Lithuania 7.7245940884920248e-06 -▁Marx 7.7245940884920248e-06 -▁einführen 7.7245940884920248e-06 -▁nehme 7.7245940884920248e-06 -▁qualifications 7.7245940884920248e-06 -▁ändert 7.7245940884920248e-06 -itzung 7.7208056656923727e-06 -▁Datens 7.7208056656923727e-06 -▁Intervention 7.7208056656923727e-06 -▁creat 7.7208056656923727e-06 -▁reibungs 7.7208056656923727e-06 -fällt 7.71701724289272e-06 -konzept 7.71701724289272e-06 -quer 7.71701724289272e-06 -üre 7.71701724289272e-06 -▁Bin 7.71701724289272e-06 -▁Steuerzahler 7.71701724289272e-06 -▁Versorgungs 7.71701724289272e-06 -▁funkt 7.71701724289272e-06 -bindung 7.7132288200930669e-06 -pend 7.7132288200930669e-06 -ritts 7.7132288200930669e-06 -wechs 7.7132288200930669e-06 -▁Lat 7.7132288200930669e-06 -▁Slovenia 7.7132288200930669e-06 -▁Song 7.7132288200930669e-06 -▁Verts 7.7132288200930669e-06 -▁slowly 7.7132288200930669e-06 -▁verstärkte 7.7132288200930669e-06 -▁Geburt 7.7094403972934149e-06 -▁Gewinner 7.7094403972934149e-06 -▁financed 7.7094403972934149e-06 -▁grün 7.7094403972934149e-06 -▁präz 7.7094403972934149e-06 -▁Ausübung 7.7056519744937612e-06 -▁Energiequellen 7.7056519744937612e-06 -▁Ergänzung 7.7056519744937612e-06 -▁Od 7.7056519744937612e-06 -▁Visum 7.7056519744937612e-06 -▁eigentlichen 7.7056519744937612e-06 -▁potentially 7.7056519744937612e-06 -▁sq 7.7056519744937612e-06 -HS 7.7018635516941091e-06 -▁elderly 7.7018635516941091e-06 -▁flo 7.7018635516941091e-06 -▁ineff 7.7018635516941091e-06 -▁placing 7.7018635516941091e-06 -▁systematic 7.7018635516941091e-06 -.- 7.6980751288944554e-06 -zubringen 7.6980751288944554e-06 -▁Gebäuden 7.6980751288944554e-06 -▁Presidents 7.6980751288944554e-06 -▁caught 7.6980751288944554e-06 -▁competences 7.6980751288944554e-06 -▁vorbehalten 7.6980751288944554e-06 -künfte 7.6942867060948033e-06 -▁unc 7.6942867060948033e-06 -objekt 7.6904982832951513e-06 -urb 7.6904982832951513e-06 -▁Democratic 7.6904982832951513e-06 -▁gelingen 7.6904982832951513e-06 -▁victim 7.6904982832951513e-06 -versicherung 7.6867098604954976e-06 -▁3.0 7.6867098604954976e-06 -▁Acht 7.6867098604954976e-06 -▁Cathedral 7.6867098604954976e-06 -▁einnehmen 7.6867098604954976e-06 -▁haus 7.6867098604954976e-06 -▁zit 7.6867098604954976e-06 -igi 7.6829214376958455e-06 -steller 7.6829214376958455e-06 -▁3rd 7.6829214376958455e-06 -▁Technical 7.6829214376958455e-06 -▁answered 7.6829214376958455e-06 -▁column 7.6829214376958455e-06 -▁komfortabel 7.6829214376958455e-06 -▁organisierte 7.6829214376958455e-06 -▁premises 7.6829214376958455e-06 -▁tropical 7.6829214376958455e-06 -apel 7.6791330148961918e-06 -fenster 7.6791330148961918e-06 -kampf 7.6791330148961918e-06 -zie 7.6791330148961918e-06 -▁450 7.6791330148961918e-06 -▁Europar 7.6791330148961918e-06 -▁Küchen 7.6791330148961918e-06 -▁Rang 7.6791330148961918e-06 -▁situ 7.6791330148961918e-06 -very 7.67534459209654e-06 -waren 7.67534459209654e-06 -ю 7.67534459209654e-06 -▁Beschwerden 7.67534459209654e-06 -▁Choose 7.67534459209654e-06 -▁Right 7.67534459209654e-06 -▁approx 7.67534459209654e-06 -▁diet 7.67534459209654e-06 -▁sagten 7.67534459209654e-06 -▁spielte 7.67534459209654e-06 -erseits 7.671556169296886e-06 -okratischen 7.671556169296886e-06 -ulin 7.671556169296886e-06 -▁Installations 7.671556169296886e-06 -▁Isol 7.671556169296886e-06 -▁brain 7.671556169296886e-06 -▁visitor 7.671556169296886e-06 -oup 7.6677677464972339e-06 -▁Anna 7.6677677464972339e-06 -▁Salz 7.6677677464972339e-06 -▁male 7.6677677464972339e-06 -holz 7.66397932369758e-06 -merk 7.66397932369758e-06 -▁Cala 7.66397932369758e-06 -▁analy 7.66397932369758e-06 -▁ehrlich 7.66397932369758e-06 -▁flav 7.66397932369758e-06 -▁geleitet 7.66397932369758e-06 -▁kultur 7.66397932369758e-06 -▁schlechte 7.66397932369758e-06 -PP 7.6601909008979282e-06 -▁Bedien 7.6601909008979282e-06 -▁Flächen 7.6601909008979282e-06 -▁Rahmenbedingungen 7.6601909008979282e-06 -▁accommodate 7.6601909008979282e-06 -▁allocation 7.6601909008979282e-06 -▁jung 7.6601909008979282e-06 -iller 7.6564024780982761e-06 -zeich 7.6564024780982761e-06 -▁BE 7.6564024780982761e-06 -▁Heimatland 7.6564024780982761e-06 -▁humanity 7.6564024780982761e-06 -▁initiated 7.6564024780982761e-06 -▁spätestens 7.6564024780982761e-06 -▁Voyager 7.6526140552986224e-06 -▁timely 7.6526140552986224e-06 -vertreter 7.64882563249897e-06 -▁1984 7.64882563249897e-06 -▁Gewinne 7.64882563249897e-06 -▁bid 7.64882563249897e-06 -▁garage 7.64882563249897e-06 -▁Ungleich 7.6450372096993166e-06 -▁sle 7.6450372096993166e-06 -▁unterwegs 7.6450372096993166e-06 -▁verzeichnen 7.6450372096993166e-06 --the 7.6412487868996645e-06 -ealth 7.6412487868996645e-06 -ibilität 7.6412487868996645e-06 -vorschläge 7.6412487868996645e-06 -▁Andreas 7.6412487868996645e-06 -▁Creat 7.6412487868996645e-06 -▁Hir 7.6412487868996645e-06 -▁creativity 7.6412487868996645e-06 -▁integral 7.6412487868996645e-06 -▁keen 7.6412487868996645e-06 -▁überlegen 7.6412487868996645e-06 -attet 7.63746036410001e-06 -▁Einschränkungen 7.63746036410001e-06 -▁Verletzungen 7.63746036410001e-06 -▁gather 7.63746036410001e-06 -▁gelegenen 7.63746036410001e-06 -▁gewalt 7.63746036410001e-06 -▁keeps 7.63746036410001e-06 -▁silence 7.63746036410001e-06 -▁trop 7.63746036410001e-06 -älter 7.6336719413003588e-06 -▁Blumen 7.6336719413003588e-06 -▁Letzt 7.6336719413003588e-06 -▁Phänomen 7.6336719413003588e-06 -▁Tsch 7.6336719413003588e-06 -▁pm 7.6336719413003588e-06 -atus 7.6298835185007067e-06 -gramm 7.6298835185007067e-06 -▁copies 7.6298835185007067e-06 -▁teilzunehmen 7.6298835185007067e-06 -uchte 7.626095095701053e-06 -▁Cent 7.626095095701053e-06 -▁Laur 7.626095095701053e-06 -▁Paz 7.626095095701053e-06 -eag 7.6223066729014e-06 -hold 7.6223066729014e-06 -▁Ibiza 7.6223066729014e-06 -▁Israelis 7.6223066729014e-06 -▁degrees 7.6223066729014e-06 -▁jährlichen 7.6223066729014e-06 -▁polnischen 7.6223066729014e-06 -▁unb 7.6223066729014e-06 -hab 7.6185182501017472e-06 -igned 7.6185182501017472e-06 -wachsen 7.6185182501017472e-06 -▁imposing 7.6185182501017472e-06 -▁metr 7.6185182501017472e-06 -▁nam 7.6185182501017472e-06 -▁veran 7.6185182501017472e-06 -▁vorrang 7.6185182501017472e-06 -print 7.6147298273020952e-06 -▁Kam 7.6147298273020952e-06 -▁charter 7.6147298273020952e-06 -▁kohär 7.6147298273020952e-06 -mission 7.6109414045024423e-06 -sprogramm 7.6109414045024423e-06 -urger 7.6109414045024423e-06 -▁Ens 7.6109414045024423e-06 -▁Integr 7.6109414045024423e-06 -▁Lar 7.6109414045024423e-06 -▁Tabelle 7.6109414045024423e-06 -▁erschien 7.6109414045024423e-06 -▁irgend 7.6109414045024423e-06 -▁ständige 7.6109414045024423e-06 -assung 7.6071529817027894e-06 -inding 7.6071529817027894e-06 -▁FC 7.6071529817027894e-06 -▁designer 7.6071529817027894e-06 -▁heated 7.6071529817027894e-06 -▁regeln 7.6071529817027894e-06 -▁virus 7.6071529817027894e-06 -stern 7.6033645589031365e-06 -▁eingeräumt 7.6033645589031365e-06 -zugehen 7.5995761361034836e-06 -▁Vort 7.5995761361034836e-06 -▁lighting 7.5995761361034836e-06 -ORT 7.5957877133038307e-06 -det 7.5957877133038307e-06 -▁Alpha 7.5957877133038307e-06 -▁Geschäften 7.5957877133038307e-06 -▁Unterstütz 7.5957877133038307e-06 -▁intense 7.5957877133038307e-06 -▁procurement 7.5957877133038307e-06 -schlüsse 7.5919992905041778e-06 -▁Om 7.5919992905041778e-06 -▁Platten 7.5919992905041778e-06 -▁respectively 7.5919992905041778e-06 -▁sonstigen 7.5919992905041778e-06 -bü 7.5882108677045249e-06 -odox 7.5882108677045249e-06 -volles 7.5882108677045249e-06 -▁Arbeitslosen 7.5882108677045249e-06 -▁medizinischen 7.5882108677045249e-06 -ns 7.5844224449048729e-06 -▁Gegenwart 7.5844224449048729e-06 -▁Route 7.5844224449048729e-06 -▁headquarters 7.5844224449048729e-06 -▁insofern 7.5844224449048729e-06 -ords 7.58063402210522e-06 -▁1.0 7.58063402210522e-06 -▁Oliven 7.58063402210522e-06 -▁dating 7.58063402210522e-06 -▁municipal 7.58063402210522e-06 -▁partial 7.58063402210522e-06 -▁state-of-the-art 7.58063402210522e-06 -▁ähnlichen 7.58063402210522e-06 -NA 7.5768455993055671e-06 -▁Secretary 7.5768455993055671e-06 -▁assurance 7.5768455993055671e-06 -▁eigenem 7.5768455993055671e-06 -▁Unterhaltung 7.5730571765059142e-06 -▁biodiversity 7.5730571765059142e-06 -▁gesundheit 7.5730571765059142e-06 -▁industriellen 7.5730571765059142e-06 -▁stattdessen 7.5730571765059142e-06 -eres 7.5692687537062613e-06 -▁EU-B 7.5692687537062613e-06 -▁Santiago 7.5692687537062613e-06 -▁gent 7.5692687537062613e-06 -▁inakzeptabel 7.5692687537062613e-06 -▁objekt 7.5692687537062613e-06 -▁wake 7.5692687537062613e-06 -▁warmly 7.5692687537062613e-06 -baum 7.5654803309066084e-06 -▁Gärten 7.5654803309066084e-06 -▁demokratischer 7.5654803309066084e-06 -▁derjenigen 7.5654803309066084e-06 -▁workshops 7.5654803309066084e-06 -▁82 7.5616919081069555e-06 -▁externe 7.5616919081069555e-06 -▁vorgehen 7.5616919081069555e-06 -brechen 7.5579034853073026e-06 -ieten 7.5579034853073026e-06 -▁Karriere 7.5579034853073026e-06 -▁Leuten 7.5579034853073026e-06 -▁entries 7.5579034853073026e-06 -▁exposure 7.5579034853073026e-06 -▁stance 7.5579034853073026e-06 -31 7.5541150625076506e-06 -▁Geben 7.5541150625076506e-06 -▁Inkrafttreten 7.5541150625076506e-06 -▁Six 7.5541150625076506e-06 -▁passende 7.5541150625076506e-06 -führungen 7.5503266397079977e-06 -zieh 7.5503266397079977e-06 -▁Adam 7.5503266397079977e-06 -▁Junior 7.5503266397079977e-06 -▁Rassismus 7.5503266397079977e-06 -▁Spannungen 7.5503266397079977e-06 -▁arbitrary 7.5503266397079977e-06 -▁fragment 7.5503266397079977e-06 -ausstattung 7.5465382169083448e-06 -▁fitted 7.5465382169083448e-06 -▁sentence 7.5465382169083448e-06 -▁stays 7.5465382169083448e-06 -▁verwirklichen 7.5465382169083448e-06 -iate 7.5427497941086919e-06 -ifiziert 7.5427497941086919e-06 -opole 7.5427497941086919e-06 -äufer 7.5427497941086919e-06 -▁isn 7.5427497941086919e-06 -▁laser 7.5427497941086919e-06 -▁pil 7.5427497941086919e-06 -▁unstable 7.5427497941086919e-06 -arz 7.538961371309039e-06 -ündete 7.538961371309039e-06 -▁beeinflusst 7.538961371309039e-06 -▁beruflichen 7.538961371309039e-06 -▁extr 7.538961371309039e-06 -▁hyp 7.538961371309039e-06 -▁Afr 7.5351729485093861e-06 -▁Anre 7.5351729485093861e-06 -▁Apache 7.5351729485093861e-06 -▁Rh 7.5351729485093861e-06 -▁contra 7.5351729485093861e-06 -▁endeavour 7.5351729485093861e-06 -▁mas 7.5351729485093861e-06 -▁nat 7.5351729485093861e-06 -▁oberen 7.5351729485093861e-06 -▁zones 7.5351729485093861e-06 -ctional 7.5313845257097332e-06 -ctions 7.5313845257097332e-06 -▁af 7.5313845257097332e-06 -▁prob 7.5313845257097332e-06 -▁terminal 7.5313845257097332e-06 -▁zeitgenöss 7.5313845257097332e-06 --Or 7.52759610291008e-06 -outs 7.52759610291008e-06 -ror 7.52759610291008e-06 -▁Einschätzung 7.52759610291008e-06 -▁Low 7.52759610291008e-06 -▁Yugoslavia 7.52759610291008e-06 -▁disputes 7.52759610291008e-06 -▁gefährden 7.52759610291008e-06 -▁posted 7.52759610291008e-06 -musik 7.5200192573107754e-06 -▁Abschluß 7.5200192573107754e-06 -▁chem 7.5200192573107754e-06 -▁lokaler 7.5200192573107754e-06 -berecht 7.5162308345111225e-06 -iciency 7.5162308345111225e-06 -▁Hitler 7.5162308345111225e-06 -▁UV 7.5162308345111225e-06 -▁agrees 7.5162308345111225e-06 -▁geistigen 7.5162308345111225e-06 -▁größt 7.5162308345111225e-06 -▁pursued 7.5162308345111225e-06 -▁rear 7.5162308345111225e-06 -▁talent 7.5162308345111225e-06 -abilität 7.51244241171147e-06 -quar 7.51244241171147e-06 -üll 7.51244241171147e-06 -▁Anbindung 7.51244241171147e-06 -▁Neues 7.51244241171147e-06 -▁cat 7.51244241171147e-06 -▁findings 7.51244241171147e-06 -▁handled 7.51244241171147e-06 -,4 7.5086539889118167e-06 -ilos 7.5086539889118167e-06 -▁Erbe 7.5086539889118167e-06 -▁appreciated 7.5086539889118167e-06 -▁respektiert 7.5086539889118167e-06 -▁shot 7.5086539889118167e-06 -dienstleistungen 7.5048655661121638e-06 -▁Swimmingpool 7.5048655661121638e-06 -▁equip 7.5048655661121638e-06 -▁merc 7.5048655661121638e-06 -liegen 7.501077143312511e-06 -ütz 7.501077143312511e-06 -▁Hub 7.501077143312511e-06 -▁beschließen 7.501077143312511e-06 -▁reinfor 7.501077143312511e-06 -oge 7.4972887205128589e-06 -ücke 7.4972887205128589e-06 -▁Shanghai 7.4972887205128589e-06 -▁beliebigen 7.4972887205128589e-06 -▁terrorists 7.4972887205128589e-06 -fordern 7.493500297713206e-06 -▁Rußland 7.493500297713206e-06 -▁chamber 7.493500297713206e-06 -▁tar 7.493500297713206e-06 -▁Batter 7.4897118749135531e-06 -▁Nag 7.4897118749135531e-06 -▁Prakt 7.4897118749135531e-06 -▁erfasst 7.4897118749135531e-06 -▁rea 7.4897118749135531e-06 -apped 7.4859234521139e-06 -here 7.4859234521139e-06 -landschaft 7.4859234521139e-06 -räumen 7.4859234521139e-06 -▁21st 7.4859234521139e-06 -▁Develop 7.4859234521139e-06 -▁Rout 7.4859234521139e-06 -▁Stich 7.4859234521139e-06 -▁Trainings 7.4859234521139e-06 -▁angeschlossen 7.4859234521139e-06 -▁ausgesch 7.4859234521139e-06 -▁bem 7.4859234521139e-06 -▁essence 7.4859234521139e-06 -▁pursuant 7.4859234521139e-06 -▁well-being 7.4859234521139e-06 -aki 7.4821350293142473e-06 -betrag 7.4821350293142473e-06 -star 7.4821350293142473e-06 -▁contractual 7.4821350293142473e-06 -▁perspectives 7.4821350293142473e-06 -▁surv 7.4821350293142473e-06 -▁Grundsätzen 7.4783466065145944e-06 -▁Info 7.4783466065145944e-06 -▁lowest 7.4783466065145944e-06 -▁maps 7.4783466065145944e-06 -▁unemployed 7.4783466065145944e-06 -bereitung 7.4745581837149416e-06 -rock 7.4745581837149416e-06 -äche 7.4745581837149416e-06 -▁Force 7.4745581837149416e-06 -▁exempt 7.4745581837149416e-06 -▁tips 7.4745581837149416e-06 -▁Konstruktion 7.4707697609152887e-06 -▁Mehrwertsteuer 7.4707697609152887e-06 -▁Palacio 7.4707697609152887e-06 -▁Prod 7.4707697609152887e-06 -▁artificial 7.4707697609152887e-06 -▁erneuerbaren 7.4707697609152887e-06 -▁prä 7.4707697609152887e-06 -▁suggestion 7.4707697609152887e-06 -▁trail 7.4707697609152887e-06 -▁ärmsten 7.4707697609152887e-06 -▁öffnet 7.4707697609152887e-06 -▁Jacques 7.4669813381156366e-06 -▁Turm 7.4669813381156366e-06 -▁proc 7.4669813381156366e-06 -▁specialities 7.4669813381156366e-06 -onien 7.4631929153159837e-06 -otyp 7.4631929153159837e-06 -ürzen 7.4631929153159837e-06 -▁Herrschaft 7.4631929153159837e-06 -▁Hum 7.4631929153159837e-06 -▁Konzentration 7.4631929153159837e-06 -▁Lib 7.4631929153159837e-06 -▁bestens 7.4631929153159837e-06 -▁lieben 7.4631929153159837e-06 -▁paradise 7.4631929153159837e-06 -▁Drag 7.4594044925163308e-06 -DEN 7.4556160697166779e-06 -gibt 7.4556160697166779e-06 -schaffen 7.4556160697166779e-06 -▁Fünf 7.4556160697166779e-06 -▁Produzenten 7.4556160697166779e-06 -▁Rücken 7.4556160697166779e-06 -▁crash 7.4556160697166779e-06 -generation 7.4518276469170251e-06 -onto 7.4518276469170251e-06 -steht 7.4518276469170251e-06 -▁DER 7.4518276469170251e-06 -▁Freuen 7.4518276469170251e-06 -▁Rubrik 7.4518276469170251e-06 -▁Unterlagen 7.4518276469170251e-06 -▁craft 7.4518276469170251e-06 -▁facto 7.4518276469170251e-06 -ampen 7.4480392241173722e-06 -ecke 7.4480392241173722e-06 -▁AKVIS 7.4480392241173722e-06 -▁Div 7.4480392241173722e-06 -▁Einwander 7.4480392241173722e-06 -▁Exc 7.4480392241173722e-06 -▁Konsum 7.4480392241173722e-06 -▁ernsthafte 7.4480392241173722e-06 -▁stroll 7.4480392241173722e-06 -▁Elevator 7.4442508013177193e-06 -▁Kod 7.4442508013177193e-06 -▁papers 7.4442508013177193e-06 -▁scr 7.4442508013177193e-06 -▁verpflichten 7.4442508013177193e-06 -▁Antonio 7.4404623785180664e-06 -▁Change 7.4404623785180664e-06 -▁Elektronik 7.4404623785180664e-06 -▁Forest 7.4404623785180664e-06 -▁convince 7.4404623785180664e-06 -▁folglich 7.4404623785180664e-06 -▁investiert 7.4404623785180664e-06 -▁kontinuierlich 7.4404623785180664e-06 -▁unl 7.4404623785180664e-06 -▁Amerikas 7.4366739557184143e-06 -▁Angeles 7.4366739557184143e-06 -▁grad 7.4366739557184143e-06 -6-0 7.4328855329187614e-06 -lor 7.4328855329187614e-06 -schreiben 7.4328855329187614e-06 -ucc 7.4328855329187614e-06 -ßt 7.4328855329187614e-06 -▁Classic 7.4328855329187614e-06 -▁alleine 7.4328855329187614e-06 -▁kontroll 7.4328855329187614e-06 -▁practically 7.4328855329187614e-06 -▁temperatures 7.4328855329187614e-06 --EU 7.4290971101191086e-06 -ehmer 7.4290971101191086e-06 -etts 7.4290971101191086e-06 -kung 7.4290971101191086e-06 -▁Besitzer 7.4290971101191086e-06 -▁coverage 7.4290971101191086e-06 -▁erweiterten 7.4290971101191086e-06 -▁platz 7.4290971101191086e-06 -ägern 7.4253086873194557e-06 -▁Bund 7.4253086873194557e-06 -▁Coast 7.4253086873194557e-06 -▁Juan 7.4253086873194557e-06 -▁Ord 7.4253086873194557e-06 -▁Vertretung 7.4253086873194557e-06 -▁workshop 7.4253086873194557e-06 -▁zer 7.4253086873194557e-06 -,8 7.4215202645198028e-06 -ablen 7.4215202645198028e-06 -ietet 7.4215202645198028e-06 -▁Consulting 7.4215202645198028e-06 -▁Gebir 7.4215202645198028e-06 -▁Inform 7.4215202645198028e-06 -▁ausgewählten 7.4215202645198028e-06 -▁kollekt 7.4215202645198028e-06 -ercial 7.41773184172015e-06 -sm 7.41773184172015e-06 -ungsmaßnahmen 7.41773184172015e-06 -▁Innerhalb 7.41773184172015e-06 -▁Samm 7.41773184172015e-06 -▁Anhang 7.413943418920497e-06 -▁Tuesday 7.413943418920497e-06 -▁blame 7.413943418920497e-06 -▁Ordner 7.4101549961208441e-06 -▁Relations 7.4101549961208441e-06 -▁amend 7.4101549961208441e-06 -▁automatische 7.4101549961208441e-06 -▁modular 7.4101549961208441e-06 -▁platforms 7.4101549961208441e-06 -quest 7.4063665733211921e-06 -▁bio 7.4063665733211921e-06 -▁decent 7.4063665733211921e-06 -▁mild 7.4063665733211921e-06 -▁russische 7.4063665733211921e-06 -▁specifications 7.4063665733211921e-06 -aves 7.4025781505215392e-06 -▁Track 7.4025781505215392e-06 -▁dollars 7.4025781505215392e-06 -▁reiz 7.4025781505215392e-06 -▁salt 7.4025781505215392e-06 -velop 7.3987897277218863e-06 -▁fortgesetzt 7.3987897277218863e-06 -▁jemals 7.3987897277218863e-06 -▁solved 7.3987897277218863e-06 -betriebe 7.3950013049222334e-06 -dom 7.3950013049222334e-06 -ranten 7.3950013049222334e-06 -▁Beschäftigten 7.3950013049222334e-06 -▁Studios 7.3950013049222334e-06 -▁desirable 7.3950013049222334e-06 -▁erbaut 7.3950013049222334e-06 -▁rust 7.3950013049222334e-06 -fordert 7.3912128821225805e-06 -▁Regionalpolitik 7.3912128821225805e-06 -▁effektiver 7.3912128821225805e-06 -▁schenken 7.3912128821225805e-06 -atics 7.3874244593229276e-06 -zif 7.3874244593229276e-06 -▁Discover 7.3874244593229276e-06 -▁Listen 7.3874244593229276e-06 -▁Verteidigungspolitik 7.3874244593229276e-06 -▁expanding 7.3874244593229276e-06 -▁kids 7.3874244593229276e-06 -▁viable 7.3874244593229276e-06 -actory 7.3836360365232747e-06 -akers 7.3836360365232747e-06 -anteil 7.3836360365232747e-06 -▁CM 7.3836360365232747e-06 -▁Obviously 7.3836360365232747e-06 -▁Proph 7.3836360365232747e-06 -▁kostengünst 7.3836360365232747e-06 -▁ober 7.3836360365232747e-06 -▁vollem 7.3836360365232747e-06 -entliche 7.3798476137236218e-06 -▁DR 7.3798476137236218e-06 -▁Hannover 7.3798476137236218e-06 -▁Malaysia 7.3798476137236218e-06 -▁breathtaking 7.3798476137236218e-06 -▁crops 7.3798476137236218e-06 -▁eindrucks 7.3798476137236218e-06 -▁kritischen 7.3798476137236218e-06 -▁occupied 7.3798476137236218e-06 -ferenz 7.37605919092397e-06 -hel 7.37605919092397e-06 -▁Nap 7.37605919092397e-06 -▁Progress 7.37605919092397e-06 -try 7.3722707681243169e-06 -▁Mineral 7.3722707681243169e-06 -▁Mitt 7.3722707681243169e-06 -▁Raums 7.3722707681243169e-06 -▁Sard 7.3722707681243169e-06 -▁demokratisch 7.3722707681243169e-06 -▁negativ 7.3722707681243169e-06 -▁remark 7.3722707681243169e-06 -▁м 7.3722707681243169e-06 -▁Kreativität 7.368482345324664e-06 -▁Solche 7.368482345324664e-06 -▁assured 7.368482345324664e-06 -▁bedauere 7.368482345324664e-06 -▁gewünscht 7.368482345324664e-06 -▁treaties 7.368482345324664e-06 -bahnen 7.3646939225250111e-06 -emeinschaft 7.3646939225250111e-06 -gründen 7.3646939225250111e-06 -icians 7.3646939225250111e-06 -oster 7.3646939225250111e-06 -same 7.3646939225250111e-06 -ри 7.3646939225250111e-06 -▁Abteilung 7.3646939225250111e-06 -▁Hen 7.3646939225250111e-06 -▁Lob 7.3646939225250111e-06 -▁Similarly 7.3646939225250111e-06 -▁erörtert 7.3646939225250111e-06 -eaus 7.3609054997253582e-06 -▁24-Stunden-Rezeption 7.3609054997253582e-06 -▁Unterzeichnung 7.3609054997253582e-06 -▁erad 7.3609054997253582e-06 -▁unterbreiten 7.3609054997253582e-06 -,6 7.3571170769257053e-06 -igs 7.3571170769257053e-06 -▁Investition 7.3571170769257053e-06 -▁Merkmale 7.3571170769257053e-06 -▁Release 7.3571170769257053e-06 -▁score 7.3571170769257053e-06 -▁zut 7.3571170769257053e-06 -icket 7.3533286541260524e-06 -▁Gn 7.3533286541260524e-06 -▁Iranian 7.3533286541260524e-06 -▁Schwimmbad 7.3533286541260524e-06 -▁angenehme 7.3533286541260524e-06 -▁describes 7.3533286541260524e-06 -▁disappointed 7.3533286541260524e-06 -element 7.3495402313263995e-06 -ento 7.3495402313263995e-06 -rechtlichen 7.3495402313263995e-06 -▁bewirken 7.3495402313263995e-06 -▁transformation 7.3495402313263995e-06 -▁weitaus 7.3495402313263995e-06 -38 7.3457518085267475e-06 -eria 7.3457518085267475e-06 -▁1978 7.3457518085267475e-06 -▁Au 7.3457518085267475e-06 -▁Direktor 7.3457518085267475e-06 -▁Has 7.3457518085267475e-06 -▁Luz 7.3457518085267475e-06 -▁Past 7.3457518085267475e-06 -▁beizutragen 7.3457518085267475e-06 -▁gültig 7.3457518085267475e-06 -▁plötzlich 7.3457518085267475e-06 -▁tight 7.3457518085267475e-06 -▁Fly 7.3419633857270946e-06 -▁bag 7.3419633857270946e-06 -▁bewiesen 7.3419633857270946e-06 -▁exam 7.3419633857270946e-06 -▁throw 7.3419633857270946e-06 -▁tor 7.3419633857270946e-06 -bür 7.3381749629274417e-06 -inks 7.3381749629274417e-06 -zo 7.3381749629274417e-06 -▁Syrien 7.3381749629274417e-06 -▁chambres 7.3381749629274417e-06 -▁lovers 7.3381749629274417e-06 -▁viewed 7.3381749629274417e-06 -▁2007-2013 7.3343865401277888e-06 -▁Venezuela 7.3343865401277888e-06 -▁choosing 7.3343865401277888e-06 -▁explains 7.3343865401277888e-06 -▁receives 7.3343865401277888e-06 --General 7.3305981173281359e-06 -agement 7.3305981173281359e-06 -yan 7.3305981173281359e-06 -▁detect 7.3305981173281359e-06 -analyse 7.326809694528483e-06 -eli 7.326809694528483e-06 -liness 7.326809694528483e-06 -▁Bit 7.326809694528483e-06 -▁G20 7.326809694528483e-06 -▁Lind 7.326809694528483e-06 -▁Panasonic 7.326809694528483e-06 -▁abschließend 7.326809694528483e-06 -▁decl 7.326809694528483e-06 -▁undermine 7.326809694528483e-06 -aro 7.32302127172883e-06 -ija 7.32302127172883e-06 -ilm 7.32302127172883e-06 -▁Cubase 7.32302127172883e-06 -▁Erschein 7.32302127172883e-06 -▁comparable 7.32302127172883e-06 -▁rect 7.32302127172883e-06 -fü 7.3192328489291781e-06 -éd 7.3192328489291781e-06 -▁Canc 7.3192328489291781e-06 -▁emerge 7.3192328489291781e-06 -▁sweet 7.3192328489291781e-06 -format 7.3154444261295252e-06 -schwe 7.3154444261295252e-06 -▁Cold 7.3154444261295252e-06 -▁Sarkozy 7.3154444261295252e-06 -▁graphic 7.3154444261295252e-06 -▁incon 7.3154444261295252e-06 -▁werfen 7.3154444261295252e-06 -▁Müller 7.3116560033298723e-06 -aturen 7.3078675805302194e-06 -gleichen 7.3078675805302194e-06 -ны 7.3078675805302194e-06 -▁1967 7.3078675805302194e-06 -▁Anhörung 7.3078675805302194e-06 -▁Ans 7.3078675805302194e-06 -▁Enter 7.3078675805302194e-06 -▁Gerichten 7.3078675805302194e-06 -▁Vergnügen 7.3078675805302194e-06 -▁performances 7.3078675805302194e-06 --Un 7.3040791577305665e-06 -.2009 7.3040791577305665e-06 -▁Bab 7.3040791577305665e-06 -▁neg 7.3040791577305665e-06 -oration 7.3002907349309136e-06 -pons 7.3002907349309136e-06 -usste 7.3002907349309136e-06 -▁Büros 7.3002907349309136e-06 -▁Touren 7.3002907349309136e-06 -▁Wellen 7.3002907349309136e-06 -▁biological 7.3002907349309136e-06 -▁geheim 7.3002907349309136e-06 -▁harmony 7.3002907349309136e-06 -▁thoroughly 7.3002907349309136e-06 -itzer 7.2965023121312607e-06 -nische 7.2965023121312607e-06 -rum 7.2965023121312607e-06 -ächte 7.2965023121312607e-06 -▁Kräften 7.2965023121312607e-06 -▁Pas 7.2965023121312607e-06 -▁Verhaltens 7.2965023121312607e-06 -▁desert 7.2965023121312607e-06 -▁ko 7.2965023121312607e-06 -▁verbreit 7.2965023121312607e-06 -▁Hyper 7.2927138893316078e-06 -▁frank 7.2927138893316078e-06 -OB 7.2889254665319558e-06 -istik 7.2889254665319558e-06 -▁Abu 7.2851370437323029e-06 -▁Finger 7.2851370437323029e-06 -▁Somm 7.2851370437323029e-06 -▁entworfen 7.2851370437323029e-06 -▁paradox 7.2851370437323029e-06 -▁teacher 7.2851370437323029e-06 -▁voters 7.2851370437323029e-06 -NE 7.28134862093265e-06 -ergebnisse 7.28134862093265e-06 -freundliche 7.28134862093265e-06 -okratische 7.28134862093265e-06 -ranken 7.28134862093265e-06 -riend 7.28134862093265e-06 -▁Communication 7.28134862093265e-06 -▁Medikamente 7.28134862093265e-06 -▁Wik 7.28134862093265e-06 -▁performing 7.28134862093265e-06 -ols 7.2775601981329971e-06 -steiger 7.2775601981329971e-06 -▁fairen 7.2775601981329971e-06 -▁fern 7.2775601981329971e-06 -▁scenery 7.2775601981329971e-06 -▁turb 7.2775601981329971e-06 --hand 7.2737717753333442e-06 -gewalt 7.2737717753333442e-06 -▁Vermittlung 7.2737717753333442e-06 -boot 7.2699833525336913e-06 -endem 7.2699833525336913e-06 -▁Buffet 7.2699833525336913e-06 -▁SCH 7.2699833525336913e-06 -▁enlarged 7.2699833525336913e-06 -▁gestellten 7.2699833525336913e-06 -▁strange 7.2699833525336913e-06 -▁Dim 7.2661949297340385e-06 -▁Hektar 7.2661949297340385e-06 -▁angel 7.2661949297340385e-06 -▁commend 7.2661949297340385e-06 -▁zweier 7.2661949297340385e-06 -▁ök 7.2661949297340385e-06 -oret 7.2624065069343856e-06 -uktion 7.2624065069343856e-06 -▁Arbeitsgruppe 7.2624065069343856e-06 -▁Hochzeit 7.2624065069343856e-06 -▁honor 7.2624065069343856e-06 -ELL 7.2586180841347335e-06 -▁Mik 7.2586180841347335e-06 -▁develops 7.2586180841347335e-06 -▁niedriger 7.2586180841347335e-06 -▁talked 7.2586180841347335e-06 -initiative 7.2548296613350806e-06 -ution 7.2548296613350806e-06 -▁Funktionalität 7.2548296613350806e-06 -▁Mauer 7.2548296613350806e-06 -▁begründet 7.2548296613350806e-06 -▁meantime 7.2548296613350806e-06 -▁Überleben 7.2548296613350806e-06 -ECT 7.2510412385354277e-06 -going 7.2510412385354277e-06 -▁Kühl 7.2510412385354277e-06 -▁Organization 7.2510412385354277e-06 -▁Unfall 7.2510412385354277e-06 -▁god 7.2510412385354277e-06 -▁preliminary 7.2510412385354277e-06 -abkommens 7.2472528157357748e-06 -zert 7.2472528157357748e-06 -zieht 7.2472528157357748e-06 -▁Ausschüsse 7.2472528157357748e-06 -▁Berater 7.2472528157357748e-06 -▁cheese 7.2472528157357748e-06 -▁defining 7.2472528157357748e-06 -▁einander 7.2472528157357748e-06 -▁vertr 7.2472528157357748e-06 -▁voneinander 7.2472528157357748e-06 -▁übermittelt 7.2472528157357748e-06 --Produkt 7.2434643929361219e-06 -fass 7.2434643929361219e-06 -iegt 7.2434643929361219e-06 -unken 7.2434643929361219e-06 -▁Fire 7.2434643929361219e-06 -▁deliber 7.2434643929361219e-06 -▁mineral 7.2434643929361219e-06 -▁substantially 7.2434643929361219e-06 -▁swift 7.2434643929361219e-06 -o. 7.2396759701364691e-06 -▁Galerie 7.2396759701364691e-06 -▁Terr 7.2396759701364691e-06 -▁avoided 7.2396759701364691e-06 -▁beschreiben 7.2396759701364691e-06 -▁superv 7.2396759701364691e-06 -itory 7.2358875473368162e-06 -kont 7.2358875473368162e-06 -▁Gulf 7.2358875473368162e-06 -▁Mount 7.2358875473368162e-06 -▁französ 7.2358875473368162e-06 -▁heft 7.2358875473368162e-06 -▁inbegriffen 7.2358875473368162e-06 -▁sees 7.2358875473368162e-06 -▁по 7.2358875473368162e-06 --Politik 7.2320991245371633e-06 -ACK 7.2320991245371633e-06 -opolitan 7.2320991245371633e-06 -▁Additionally 7.2320991245371633e-06 -▁Eigentümer 7.2320991245371633e-06 -▁Konferenzen 7.2320991245371633e-06 -▁erworben 7.2320991245371633e-06 -bu 7.2283107017375112e-06 -häusern 7.2283107017375112e-06 -ifizierungs 7.2283107017375112e-06 -iques 7.2283107017375112e-06 -reichung 7.2283107017375112e-06 -▁Ci 7.2283107017375112e-06 -▁Excel 7.2283107017375112e-06 -▁Gemäß 7.2283107017375112e-06 -▁agent 7.2283107017375112e-06 -▁partnerships 7.2283107017375112e-06 -▁schlechter 7.2283107017375112e-06 -▁sensor 7.2283107017375112e-06 -▁settled 7.2283107017375112e-06 -▁Äußerungen 7.2283107017375112e-06 -▁Active 7.2245222789378583e-06 -▁Palm 7.2245222789378583e-06 -▁Patch 7.2245222789378583e-06 -▁fährt 7.2245222789378583e-06 -▁incredible 7.2245222789378583e-06 -▁screens 7.2245222789378583e-06 -▁transp 7.2245222789378583e-06 -ateur 7.2207338561382054e-06 -ieur 7.2207338561382054e-06 -▁Mikro 7.2207338561382054e-06 -▁Ni 7.2207338561382054e-06 -▁Vorfeld 7.2207338561382054e-06 -▁cease 7.2207338561382054e-06 -▁incorrect 7.2207338561382054e-06 -▁spezial 7.2207338561382054e-06 -▁verwalten 7.2207338561382054e-06 -▁Amb 7.2169454333385526e-06 -▁Verluste 7.2169454333385526e-06 -▁emphasised 7.2169454333385526e-06 -▁photography 7.2169454333385526e-06 -icas 7.2131570105389e-06 -pect 7.2131570105389e-06 -▁Review 7.2131570105389e-06 -▁engag 7.2131570105389e-06 -▁normale 7.2131570105389e-06 -▁unbest 7.2131570105389e-06 --19 7.2093685877392468e-06 -wissen 7.2093685877392468e-06 -▁Alc 7.2093685877392468e-06 -▁Communist 7.2093685877392468e-06 -▁Hôtel 7.2093685877392468e-06 -▁Sales 7.2093685877392468e-06 -▁bearbeitet 7.2093685877392468e-06 -▁entschied 7.2093685877392468e-06 -▁gebildet 7.2093685877392468e-06 -▁kompliziert 7.2093685877392468e-06 -▁melden 7.2093685877392468e-06 -▁potent 7.2093685877392468e-06 -lebens 7.2055801649395939e-06 -oberfläche 7.2055801649395939e-06 -ogn 7.2055801649395939e-06 -rooms 7.2055801649395939e-06 -▁Motto 7.2055801649395939e-06 -▁Stoff 7.2055801649395939e-06 --Format 7.201791742139941e-06 -▁Bewältigung 7.201791742139941e-06 -▁Kunststoff 7.201791742139941e-06 -▁funded 7.201791742139941e-06 -▁gehandelt 7.201791742139941e-06 -▁gelernt 7.201791742139941e-06 -▁getötet 7.201791742139941e-06 -▁passen 7.201791742139941e-06 -elev 7.1980033193402889e-06 -eute 7.1980033193402889e-06 -förderung 7.1980033193402889e-06 -läuf 7.1980033193402889e-06 -▁centrally 7.1980033193402889e-06 -▁finest 7.1980033193402889e-06 -▁ohnehin 7.1980033193402889e-06 -▁piano 7.1980033193402889e-06 -▁signature 7.1980033193402889e-06 -▁span 7.1980033193402889e-06 -▁Zimbabwe 7.1942148965406361e-06 -▁aufgerufen 7.1942148965406361e-06 -▁israelischen 7.1942148965406361e-06 -▁joy 7.1942148965406361e-06 -aill 7.1904264737409832e-06 -imes 7.1904264737409832e-06 -▁76 7.1904264737409832e-06 -▁Roh 7.1904264737409832e-06 -▁correspond 7.1904264737409832e-06 -▁inclusive 7.1904264737409832e-06 -▁konstruktive 7.1904264737409832e-06 -▁sam 7.1904264737409832e-06 -▁winds 7.1904264737409832e-06 -aba 7.18663805094133e-06 -▁Besteuerung 7.18663805094133e-06 -▁guitar 7.18663805094133e-06 -▁neglect 7.18663805094133e-06 -r. 7.1828496281416774e-06 -verfahrens 7.1828496281416774e-06 -▁Broad 7.1828496281416774e-06 -▁Lebanon 7.1828496281416774e-06 -▁Rezession 7.1828496281416774e-06 -▁aktualisieren 7.1828496281416774e-06 -▁erwähnte 7.1828496281416774e-06 -▁geschafft 7.1828496281416774e-06 -▁schlicht 7.1828496281416774e-06 -oker 7.1790612053420245e-06 -▁Fat 7.1790612053420245e-06 -▁Nature 7.1790612053420245e-06 -▁Orts 7.1790612053420245e-06 -▁unbe 7.1790612053420245e-06 -kapazität 7.1752727825423716e-06 -viert 7.1752727825423716e-06 -▁Aufruf 7.1752727825423716e-06 -▁Parliamentary 7.1752727825423716e-06 -▁RI 7.1752727825423716e-06 -▁Spy 7.1752727825423716e-06 -▁endgültig 7.1752727825423716e-06 -imensional 7.1714843597427187e-06 -▁Schadens 7.1714843597427187e-06 -▁rivers 7.1714843597427187e-06 -▁zeigte 7.1714843597427187e-06 -alu 7.1676959369430667e-06 -indlich 7.1676959369430667e-06 -kämp 7.1676959369430667e-06 -möglich 7.1676959369430667e-06 -▁Internetzugang 7.1676959369430667e-06 -▁Short 7.1676959369430667e-06 -▁Weißbuch 7.1676959369430667e-06 -▁nachdenken 7.1676959369430667e-06 -▁Gol 7.1639075141434138e-06 -▁angewiesen 7.1639075141434138e-06 -▁birds 7.1639075141434138e-06 -▁consequently 7.1639075141434138e-06 -aper 7.1601190913437609e-06 -▁Iraqi 7.1601190913437609e-06 -▁Sitzungen 7.1601190913437609e-06 -▁gebraucht 7.1601190913437609e-06 -asste 7.156330668544108e-06 -▁Dienstleistung 7.156330668544108e-06 -▁Dritter 7.156330668544108e-06 -▁Eigenschaft 7.156330668544108e-06 -fahrzeuge 7.1525422457444551e-06 -vens 7.1525422457444551e-06 -▁Gaz 7.1525422457444551e-06 -▁Höhen 7.1525422457444551e-06 -▁Kanal 7.1525422457444551e-06 -▁interinstitutional 7.1525422457444551e-06 -▁markt 7.1525422457444551e-06 -berichte 7.1487538229448022e-06 -dokument 7.1487538229448022e-06 -▁Atlantik 7.1487538229448022e-06 -▁Everyone 7.1487538229448022e-06 -▁IA 7.1487538229448022e-06 -▁Johann 7.1487538229448022e-06 -▁erläutern 7.1487538229448022e-06 -▁tribute 7.1487538229448022e-06 -▁warrant 7.1487538229448022e-06 -▁worthy 7.1487538229448022e-06 -anes 7.1449654001451493e-06 -ersatz 7.1449654001451493e-06 -greifende 7.1449654001451493e-06 -kov 7.1449654001451493e-06 -sichtlich 7.1449654001451493e-06 -▁Along 7.1449654001451493e-06 -▁Socialists 7.1449654001451493e-06 -▁automation 7.1449654001451493e-06 -▁enth 7.1449654001451493e-06 -▁sail 7.1449654001451493e-06 -UC 7.1411769773454973e-06 -oping 7.1411769773454973e-06 -▁Eric 7.1411769773454973e-06 -▁Sher 7.1411769773454973e-06 -▁battery 7.1411769773454973e-06 -▁kre 7.1411769773454973e-06 -▁angeblich 7.1373885545458444e-06 -▁celebrate 7.1373885545458444e-06 -▁loved 7.1373885545458444e-06 -▁pin 7.1373885545458444e-06 -▁ven 7.1373885545458444e-06 -,2 7.1336001317461915e-06 -acken 7.1336001317461915e-06 -hot 7.1336001317461915e-06 -laut 7.1336001317461915e-06 -▁Ara 7.1336001317461915e-06 -▁COD 7.1336001317461915e-06 -▁arbeits 7.1336001317461915e-06 -▁exchanges 7.1336001317461915e-06 -▁measured 7.1336001317461915e-06 -▁nonetheless 7.1336001317461915e-06 -ahrung 7.1298117089465386e-06 -kauf 7.1298117089465386e-06 -mp 7.1298117089465386e-06 -uits 7.1298117089465386e-06 -verw 7.1298117089465386e-06 -▁Außer 7.1298117089465386e-06 -▁disturb 7.1298117089465386e-06 -▁einzugehen 7.1298117089465386e-06 -▁köst 7.1298117089465386e-06 -ordnungs 7.1260232861468857e-06 -wich 7.1260232861468857e-06 -▁bilateralen 7.1260232861468857e-06 -▁controversial 7.1260232861468857e-06 -▁expressing 7.1260232861468857e-06 -aktiv 7.1222348633472328e-06 -▁Bezahlung 7.1222348633472328e-06 -▁Carr 7.1222348633472328e-06 -▁erörtern 7.1222348633472328e-06 -▁unterbreitet 7.1222348633472328e-06 -▁ältere 7.1222348633472328e-06 -ception 7.11844644054758e-06 -opt 7.11844644054758e-06 -personal 7.11844644054758e-06 -vision 7.11844644054758e-06 -▁Attraktionen 7.11844644054758e-06 -▁acquire 7.11844644054758e-06 -▁crystal 7.11844644054758e-06 -▁finanziell 7.11844644054758e-06 -▁paintings 7.11844644054758e-06 -▁rechtfertigen 7.11844644054758e-06 -▁squ 7.11844644054758e-06 -▁sym 7.11844644054758e-06 -asis 7.114658017747927e-06 -▁Por 7.114658017747927e-06 -▁Studium 7.114658017747927e-06 -▁capture 7.114658017747927e-06 -▁konkur 7.114658017747927e-06 -▁schrittweise 7.114658017747927e-06 -amo 7.110869594948275e-06 -▁Bedienung 7.110869594948275e-06 -.6 7.1070811721486221e-06 -▁Engl 7.1070811721486221e-06 -▁agr 7.1070811721486221e-06 -▁controlling 7.1070811721486221e-06 -▁forese 7.1070811721486221e-06 -▁peak 7.1070811721486221e-06 -▁raises 7.1070811721486221e-06 -▁sporting 7.1070811721486221e-06 --Cl 7.1032927493489692e-06 -sitz 7.1032927493489692e-06 -uan 7.1032927493489692e-06 -▁3000 7.1032927493489692e-06 -▁Gift 7.1032927493489692e-06 -▁Marine 7.1032927493489692e-06 -▁WM 7.1032927493489692e-06 -▁brut 7.1032927493489692e-06 -▁discrimin 7.1032927493489692e-06 -eign 7.0995043265493163e-06 -etze 7.0995043265493163e-06 -ocratic 7.0995043265493163e-06 -uner 7.0995043265493163e-06 -▁bott 7.0995043265493163e-06 -▁declarations 7.0995043265493163e-06 -▁konzipiert 7.0995043265493163e-06 -▁verkauf 7.0995043265493163e-06 -abet 7.0957159037496634e-06 -einkommen 7.0957159037496634e-06 -inu 7.0957159037496634e-06 -venth 7.0957159037496634e-06 -▁Hoff 7.0957159037496634e-06 -▁Quart 7.0957159037496634e-06 -▁confusion 7.0957159037496634e-06 -▁eingebunden 7.0957159037496634e-06 -▁erfolgte 7.0957159037496634e-06 -▁gelegene 7.0957159037496634e-06 -▁zweck 7.0957159037496634e-06 -alteten 7.0919274809500105e-06 -gangenen 7.0919274809500105e-06 -▁admitted 7.0919274809500105e-06 -▁detaillierte 7.0919274809500105e-06 -▁geist 7.0919274809500105e-06 -▁stützen 7.0919274809500105e-06 -eab 7.0881390581503576e-06 -leistungs 7.0881390581503576e-06 -▁Aff 7.0881390581503576e-06 -▁Energiepolitik 7.0881390581503576e-06 -▁Heating 7.0881390581503576e-06 -▁Konzepte 7.0881390581503576e-06 -▁Lanzarote 7.0881390581503576e-06 -▁emission 7.0881390581503576e-06 -▁realen 7.0881390581503576e-06 -▁statistical 7.0881390581503576e-06 --Staaten 7.0805622125510527e-06 -▁Editor 7.0805622125510527e-06 -▁Gent 7.0805622125510527e-06 -▁Kit 7.0805622125510527e-06 -▁Map 7.0805622125510527e-06 -▁Sau 7.0805622125510527e-06 -▁Wille 7.0805622125510527e-06 -▁dust 7.0805622125510527e-06 -▁isolated 7.0805622125510527e-06 -▁tur 7.0805622125510527e-06 -elen 7.0767737897514e-06 -▁Division 7.0767737897514e-06 -▁Eth 7.0767737897514e-06 -▁Scheitern 7.0767737897514e-06 -▁beiträgt 7.0767737897514e-06 -▁kenn 7.0767737897514e-06 -▁licence 7.0767737897514e-06 -▁parameter 7.0767737897514e-06 -▁Ähn 7.0767737897514e-06 -PR 7.0729853669517469e-06 -utr 7.0729853669517469e-06 -▁Beratungs 7.0729853669517469e-06 -▁Führ 7.0729853669517469e-06 -▁Gelände 7.0729853669517469e-06 -▁Intr 7.0729853669517469e-06 -▁Vorgaben 7.0729853669517469e-06 -▁conveniently 7.0729853669517469e-06 -▁nötigen 7.0729853669517469e-06 -▁tempt 7.0729853669517469e-06 -TEN 7.069196944152094e-06 -ifi 7.069196944152094e-06 -▁About 7.069196944152094e-06 -▁Betr 7.069196944152094e-06 -▁Fluggesellschaften 7.069196944152094e-06 -▁Subsidiarität 7.069196944152094e-06 -▁acknowled 7.069196944152094e-06 -▁executed 7.069196944152094e-06 -▁exerc 7.069196944152094e-06 -▁technologische 7.069196944152094e-06 -ussen 7.0654085213524411e-06 -▁Hence 7.0654085213524411e-06 -▁Viol 7.0654085213524411e-06 -▁ce 7.0654085213524411e-06 -▁erlangen 7.0654085213524411e-06 -▁gutem 7.0654085213524411e-06 -▁modernes 7.0654085213524411e-06 -▁wages 7.0654085213524411e-06 -geräten 7.0616200985527882e-06 -usa 7.0616200985527882e-06 -▁Griff 7.0616200985527882e-06 -▁bien 7.0616200985527882e-06 -▁kommerziellen 7.0616200985527882e-06 -▁magn 7.0616200985527882e-06 -aca 7.0578316757531353e-06 -agentur 7.0578316757531353e-06 -▁Help 7.0578316757531353e-06 -▁Individ 7.0578316757531353e-06 -▁SY 7.0578316757531353e-06 -▁Serien 7.0578316757531353e-06 -▁Speed 7.0578316757531353e-06 -▁Steuers 7.0578316757531353e-06 -▁compare 7.0578316757531353e-06 -▁einhalten 7.0578316757531353e-06 -▁fears 7.0578316757531353e-06 -▁frucht 7.0578316757531353e-06 -▁lesson 7.0578316757531353e-06 -▁radik 7.0578316757531353e-06 -▁vernünftige 7.0578316757531353e-06 --like 7.0540432529534825e-06 -greifen 7.0540432529534825e-06 -ssicherheit 7.0540432529534825e-06 -▁CAM 7.0540432529534825e-06 -▁Fab 7.0540432529534825e-06 -▁Gef 7.0540432529534825e-06 -▁Leicht 7.0540432529534825e-06 -▁Luggage 7.0540432529534825e-06 -▁Stadtteil 7.0540432529534825e-06 -▁Trends 7.0540432529534825e-06 -▁finances 7.0540432529534825e-06 -▁formula 7.0540432529534825e-06 -▁grenzüberschreitende 7.0540432529534825e-06 -▁themes 7.0540432529534825e-06 -ektionen 7.05025483015383e-06 -verse 7.05025483015383e-06 -▁DI 7.05025483015383e-06 -▁Merk 7.05025483015383e-06 -▁Round 7.05025483015383e-06 -▁Total 7.05025483015383e-06 -▁begleiten 7.05025483015383e-06 -▁multimedia 7.05025483015383e-06 -▁realisieren 7.05025483015383e-06 -▁watching 7.05025483015383e-06 -inning 7.0464664073541775e-06 -▁24-Hour 7.0464664073541775e-06 -▁Aktivität 7.0464664073541775e-06 -▁Auftraggeber 7.0464664073541775e-06 -▁Spec 7.0464664073541775e-06 -▁acknowledged 7.0464664073541775e-06 -▁belonging 7.0464664073541775e-06 -▁klimatis 7.0464664073541775e-06 -▁undertakings 7.0464664073541775e-06 -ellos 7.0426779845545246e-06 -▁Cur 7.0426779845545246e-06 -▁Zubehör 7.0426779845545246e-06 -▁begangen 7.0426779845545246e-06 -▁cred 7.0426779845545246e-06 -▁drafting 7.0426779845545246e-06 -▁pact 7.0426779845545246e-06 --by 7.0388895617548717e-06 -akten 7.0388895617548717e-06 -atin 7.0388895617548717e-06 -▁Advanced 7.0388895617548717e-06 -▁Göt 7.0388895617548717e-06 -▁I. 7.0388895617548717e-06 -▁eingerichtete 7.0388895617548717e-06 -▁erinnere 7.0388895617548717e-06 -▁genetic 7.0388895617548717e-06 -▁regulate 7.0388895617548717e-06 -▁romant 7.0388895617548717e-06 -akis 7.0351011389552188e-06 -oro 7.0351011389552188e-06 -▁Kapazitäten 7.0351011389552188e-06 -▁Siehe 7.0351011389552188e-06 -▁ausz 7.0351011389552188e-06 -▁row 7.0351011389552188e-06 -bele 7.031312716155566e-06 -verband 7.031312716155566e-06 -▁Abstimmungen 7.031312716155566e-06 -▁Beobachter 7.031312716155566e-06 -▁Diam 7.031312716155566e-06 -▁Mittelmeerraum 7.031312716155566e-06 -▁registriert 7.031312716155566e-06 -▁shame 7.031312716155566e-06 -ichtungs 7.0275242933559131e-06 -ienste 7.0275242933559131e-06 -▁Bege 7.0275242933559131e-06 -▁CV 7.0275242933559131e-06 -▁Darin 7.0275242933559131e-06 -▁Integrations 7.0275242933559131e-06 -▁Wolfgang 7.0275242933559131e-06 -▁damaged 7.0275242933559131e-06 -aks 7.02373587055626e-06 -istry 7.02373587055626e-06 -▁Parks 7.02373587055626e-06 -▁Zuständigkeiten 7.02373587055626e-06 -▁BL 7.0199474477566081e-06 -▁Haushalte 7.0199474477566081e-06 -▁beurteilen 7.0199474477566081e-06 -▁equity 7.0199474477566081e-06 -▁typische 7.0199474477566081e-06 -▁örtlichen 7.0199474477566081e-06 -ehnten 7.0161590249569552e-06 -ilia 7.0161590249569552e-06 -verbrauch 7.0161590249569552e-06 -▁ignored 7.0161590249569552e-06 -lossene 7.0123706021573023e-06 -ried 7.0123706021573023e-06 -riffe 7.0123706021573023e-06 -▁Arbeitsbedingungen 7.0123706021573023e-06 -▁GS 7.0123706021573023e-06 -▁exklusiven 7.0123706021573023e-06 -▁übersch 7.0123706021573023e-06 -alty 7.0085821793576494e-06 -iable 7.0085821793576494e-06 -isses 7.0085821793576494e-06 -▁MD 7.0085821793576494e-06 -▁Macedonia 7.0085821793576494e-06 -▁Ok 7.0085821793576494e-06 -▁chairs 7.0085821793576494e-06 -▁eingeb 7.0085821793576494e-06 -▁jeopard 7.0085821793576494e-06 -▁1.2 7.0047937565579966e-06 -▁1975 7.0047937565579966e-06 -▁Benutz 7.0047937565579966e-06 -▁Betroffenen 7.0047937565579966e-06 -▁Cultural 7.0047937565579966e-06 -▁mensch 7.0047937565579966e-06 -▁womit 7.0047937565579966e-06 -▁zielt 7.0047937565579966e-06 -▁alloc 7.0010053337583437e-06 -▁spots 7.0010053337583437e-06 -▁tells 7.0010053337583437e-06 -44 6.9972169109586908e-06 -HR 6.9972169109586908e-06 -datei 6.9972169109586908e-06 -nik 6.9972169109586908e-06 -▁Bull 6.9972169109586908e-06 -▁Gemüse 6.9972169109586908e-06 -▁Queen 6.9972169109586908e-06 -▁coordin 6.9972169109586908e-06 -▁divid 6.9972169109586908e-06 -▁engineers 6.9972169109586908e-06 -▁fate 6.9972169109586908e-06 -▁hoped 6.9972169109586908e-06 -▁kritisiert 6.9972169109586908e-06 --Länder 6.9934284881590379e-06 -verschmutzung 6.9934284881590379e-06 -▁Kohle 6.9934284881590379e-06 -▁volunte 6.9934284881590379e-06 -kredit 6.9896400653593858e-06 -▁Superior 6.9896400653593858e-06 -▁aux 6.9896400653593858e-06 -▁depart 6.9896400653593858e-06 -▁tank 6.9896400653593858e-06 -lebnis 6.9858516425597329e-06 -▁71 6.9858516425597329e-06 -▁Colle 6.9858516425597329e-06 -▁Hart 6.9858516425597329e-06 -▁Inspekt 6.9858516425597329e-06 -▁Jackson 6.9858516425597329e-06 -▁Werbe 6.9858516425597329e-06 -▁Zertifik 6.9858516425597329e-06 -▁characterized 6.9858516425597329e-06 -▁interessieren 6.9858516425597329e-06 -▁mirror 6.9858516425597329e-06 -▁mithilfe 6.9858516425597329e-06 -▁tranqu 6.9858516425597329e-06 -▁Erwachsenen 6.98206321976008e-06 -▁Plenars 6.98206321976008e-06 -▁challenging 6.98206321976008e-06 -▁ehrgeiz 6.98206321976008e-06 -▁grundlegender 6.98206321976008e-06 -▁incentive 6.98206321976008e-06 -enna 6.9782747969604272e-06 -enntnis 6.9782747969604272e-06 -helfen 6.9782747969604272e-06 -▁Hung 6.9782747969604272e-06 -▁Soweit 6.9782747969604272e-06 -▁abund 6.9782747969604272e-06 -▁encounter 6.9782747969604272e-06 -▁exert 6.9782747969604272e-06 -▁schwarz 6.9782747969604272e-06 -ancies 6.9744863741607743e-06 -▁Prävention 6.9744863741607743e-06 -▁Wales 6.9744863741607743e-06 -▁congress 6.9744863741607743e-06 -▁gewisser 6.9744863741607743e-06 -▁modest 6.9744863741607743e-06 -▁prohibited 6.9744863741607743e-06 -▁vernünftig 6.9744863741607743e-06 -anna 6.9706979513611214e-06 -▁Deut 6.9706979513611214e-06 -▁Milosevic 6.9706979513611214e-06 -▁Trag 6.9706979513611214e-06 -▁faire 6.9706979513611214e-06 -▁falschen 6.9706979513611214e-06 -▁freund 6.9706979513611214e-06 -▁fulfilled 6.9706979513611214e-06 -▁pharm 6.9706979513611214e-06 --level 6.9669095285614685e-06 -bling 6.9669095285614685e-06 -uchtung 6.9669095285614685e-06 -▁Ferr 6.9669095285614685e-06 -▁NH 6.9669095285614685e-06 -▁Victoria 6.9669095285614685e-06 -▁bedauern 6.9669095285614685e-06 -▁sy 6.9669095285614685e-06 -▁verbesserte 6.9669095285614685e-06 -staats 6.9631211057618164e-06 -▁Entspannen 6.9631211057618164e-06 -▁Ib 6.9631211057618164e-06 -▁Palestine 6.9631211057618164e-06 -▁branches 6.9631211057618164e-06 -▁scandal 6.9631211057618164e-06 --gr 6.9593326829621636e-06 -ständen 6.9593326829621636e-06 -▁downloading 6.9593326829621636e-06 -▁environments 6.9593326829621636e-06 -▁Übersetzungs 6.9593326829621636e-06 -▁überd 6.9593326829621636e-06 -eling 6.9555442601625107e-06 -▁Tempo 6.9555442601625107e-06 -▁anticip 6.9555442601625107e-06 -DER 6.9517558373628578e-06 -▁Anreise 6.9517558373628578e-06 -▁Lehre 6.9517558373628578e-06 -▁Minder 6.9517558373628578e-06 -▁Stick 6.9517558373628578e-06 -▁UC 6.9517558373628578e-06 -▁Zufriedenheit 6.9517558373628578e-06 -iebt 6.9479674145632049e-06 -ommend 6.9479674145632049e-06 -rechnung 6.9479674145632049e-06 -üten 6.9479674145632049e-06 -▁Aires 6.9479674145632049e-06 -▁Lok 6.9479674145632049e-06 -▁abzusch 6.9479674145632049e-06 -▁fehler 6.9479674145632049e-06 -▁frisch 6.9479674145632049e-06 -▁login 6.9479674145632049e-06 -übt 6.944178991763552e-06 -▁1982 6.944178991763552e-06 -▁Afghan 6.944178991763552e-06 -▁Layout 6.944178991763552e-06 -▁ausgest 6.944178991763552e-06 -▁flowers 6.944178991763552e-06 -▁interakt 6.944178991763552e-06 -▁schlechten 6.944178991763552e-06 -getragen 6.9403905689638991e-06 -irtschaft 6.9403905689638991e-06 -leute 6.9403905689638991e-06 -▁Folter 6.9403905689638991e-06 -▁Schnee 6.9403905689638991e-06 -▁Sevilla 6.9403905689638991e-06 -▁Stell 6.9403905689638991e-06 -▁Taxi 6.9403905689638991e-06 -▁Vorredner 6.9403905689638991e-06 -▁ausführen 6.9403905689638991e-06 -▁ausgel 6.9403905689638991e-06 -atisf 6.9366021461642462e-06 -▁Entdecken 6.9366021461642462e-06 -▁Metern 6.9366021461642462e-06 -▁Vari 6.9366021461642462e-06 -▁brows 6.9366021461642462e-06 -▁Features 6.9328137233645942e-06 -▁INTER 6.9328137233645942e-06 -▁Mercedes 6.9328137233645942e-06 -▁Realisierung 6.9328137233645942e-06 -▁befried 6.9328137233645942e-06 -▁regrettable 6.9328137233645942e-06 --dr 6.9290253005649413e-06 -ärt 6.9290253005649413e-06 -▁herrliche 6.9290253005649413e-06 -▁publicly 6.9290253005649413e-06 -ency 6.9252368777652884e-06 -▁warming 6.9252368777652884e-06 -TH 6.9214484549656355e-06 -gebung 6.9214484549656355e-06 -▁DIE 6.9214484549656355e-06 -▁Erste 6.9214484549656355e-06 -▁Kreditkarten 6.9214484549656355e-06 -▁Ministerrat 6.9214484549656355e-06 -▁Oslo 6.9214484549656355e-06 -▁oblig 6.9214484549656355e-06 -▁zunehmende 6.9214484549656355e-06 -cket 6.9176600321659826e-06 -gebühr 6.9176600321659826e-06 -▁Engine 6.9176600321659826e-06 -▁Lehren 6.9176600321659826e-06 -▁fought 6.9176600321659826e-06 -▁iz 6.9176600321659826e-06 -▁ratifiziert 6.9176600321659826e-06 -▁tätigen 6.9176600321659826e-06 --Be 6.91387160936633e-06 -ased 6.91387160936633e-06 -schäd 6.91387160936633e-06 -▁Besuchern 6.91387160936633e-06 -▁Buenos 6.91387160936633e-06 -▁Summer 6.91387160936633e-06 -▁indirect 6.91387160936633e-06 -▁sheets 6.91387160936633e-06 -▁suff 6.91387160936633e-06 -ections 6.9100831865666768e-06 -▁Dikt 6.9100831865666768e-06 -▁Gipfeltreffen 6.9100831865666768e-06 -▁Regulierungs 6.9100831865666768e-06 -▁ansonsten 6.9100831865666768e-06 -▁demographic 6.9100831865666768e-06 -▁größerer 6.9100831865666768e-06 -▁technologischen 6.9100831865666768e-06 -▁unlike 6.9100831865666768e-06 -legte 6.9062947637670239e-06 -reformen 6.9062947637670239e-06 -▁Fas 6.9062947637670239e-06 -▁Lösungs 6.9062947637670239e-06 -▁Servic 6.9062947637670239e-06 -▁blank 6.9062947637670239e-06 -▁bureaucratic 6.9062947637670239e-06 -▁circuit 6.9062947637670239e-06 -▁einzuhalten 6.9062947637670239e-06 -▁fung 6.9062947637670239e-06 -▁thoughts 6.9062947637670239e-06 -▁thread 6.9062947637670239e-06 -asi 6.9025063409673719e-06 -greg 6.9025063409673719e-06 -iferation 6.9025063409673719e-06 -mö 6.9025063409673719e-06 -ätigen 6.9025063409673719e-06 -üste 6.9025063409673719e-06 -▁Close 6.9025063409673719e-06 -▁Instrumenten 6.9025063409673719e-06 -▁Kant 6.9025063409673719e-06 -▁este 6.9025063409673719e-06 -▁globaler 6.9025063409673719e-06 -▁schreibt 6.9025063409673719e-06 -▁shortcomings 6.9025063409673719e-06 -▁unzureichend 6.9025063409673719e-06 -▁vorhin 6.9025063409673719e-06 -arbeits 6.898717918167719e-06 -eph 6.898717918167719e-06 -igter 6.898717918167719e-06 -▁ausreichen 6.898717918167719e-06 -▁grös 6.898717918167719e-06 -iffs 6.8949294953680661e-06 -owski 6.8949294953680661e-06 -▁Bewegungen 6.8949294953680661e-06 -▁Guinea 6.8949294953680661e-06 -▁angefangen 6.8949294953680661e-06 -▁condemned 6.8949294953680661e-06 -kal 6.8911410725684132e-06 -▁Abstand 6.8911410725684132e-06 -▁Vil 6.8911410725684132e-06 -▁complexity 6.8911410725684132e-06 -▁guidance 6.8911410725684132e-06 -▁komplex 6.8911410725684132e-06 -▁survival 6.8911410725684132e-06 -▁Überarbeitung 6.8911410725684132e-06 --Technologie 6.88735264976876e-06 -behandlung 6.88735264976876e-06 -laim 6.88735264976876e-06 -▁Freiwill 6.88735264976876e-06 -▁amer 6.88735264976876e-06 -▁foods 6.88735264976876e-06 -▁translations 6.88735264976876e-06 -AY 6.8835642269691074e-06 -QU 6.8835642269691074e-06 -beit 6.8835642269691074e-06 -ivid 6.8835642269691074e-06 -ämp 6.8835642269691074e-06 -▁Allgemein 6.8835642269691074e-06 -▁Beauty 6.8835642269691074e-06 -▁Elect 6.8835642269691074e-06 -▁Finance 6.8835642269691074e-06 -▁Kompromiß 6.8835642269691074e-06 -▁Marktwirtschaft 6.8835642269691074e-06 -▁brother 6.8835642269691074e-06 -▁enttäuscht 6.8835642269691074e-06 -▁erreichte 6.8835642269691074e-06 -▁notion 6.8835642269691074e-06 -agl 6.8797758041694545e-06 -ocken 6.8797758041694545e-06 -unkel 6.8797758041694545e-06 -▁NE 6.8797758041694545e-06 -▁Techniken 6.8797758041694545e-06 -▁VI 6.8797758041694545e-06 -▁stem 6.8797758041694545e-06 -▁umfassender 6.8797758041694545e-06 -та 6.8759873813698016e-06 -▁Budgetary 6.8759873813698016e-06 -▁Gepäckraum 6.8759873813698016e-06 -▁Products 6.8759873813698016e-06 -▁Quell 6.8759873813698016e-06 -▁REACH 6.8759873813698016e-06 -▁alleged 6.8759873813698016e-06 -▁außerord 6.8759873813698016e-06 -▁founding 6.8759873813698016e-06 -▁gesonder 6.8759873813698016e-06 -▁kurzfristig 6.8759873813698016e-06 -▁shut 6.8759873813698016e-06 -▁unerlässlich 6.8759873813698016e-06 -▁vielfältigen 6.8759873813698016e-06 -adas 6.87219895857015e-06 -▁Credit 6.87219895857015e-06 -▁Hör 6.87219895857015e-06 -▁distinct 6.87219895857015e-06 -▁monitored 6.87219895857015e-06 -wen 6.8684105357704967e-06 -▁Früh 6.8684105357704967e-06 -▁Gestatten 6.8684105357704967e-06 -▁Norway 6.8684105357704967e-06 -▁advoc 6.8684105357704967e-06 -▁excellence 6.8684105357704967e-06 -▁militär 6.8684105357704967e-06 -▁structured 6.8684105357704967e-06 -agten 6.8646221129708438e-06 -akter 6.8646221129708438e-06 -▁Lido 6.8646221129708438e-06 -▁outset 6.8646221129708438e-06 -▁sequ 6.8646221129708438e-06 -alp 6.8608336901711909e-06 -icial 6.8608336901711909e-06 -right 6.8608336901711909e-06 -versicher 6.8608336901711909e-06 -äle 6.8608336901711909e-06 -▁Angriffe 6.8608336901711909e-06 -▁Bos 6.8608336901711909e-06 -▁brutal 6.8608336901711909e-06 -▁kulin 6.8608336901711909e-06 -▁namens 6.8608336901711909e-06 -▁subsidiary 6.8608336901711909e-06 -bro 6.857045267371538e-06 -öpf 6.857045267371538e-06 -▁Flag 6.857045267371538e-06 -▁derartigen 6.857045267371538e-06 -▁traditionell 6.857045267371538e-06 -▁verwaltet 6.857045267371538e-06 -▁weib 6.857045267371538e-06 --prod 6.8532568445718851e-06 -▁Verkäufer 6.8532568445718851e-06 -▁beschäft 6.8532568445718851e-06 -▁dress 6.8532568445718851e-06 -▁ped 6.8532568445718851e-06 -schutzes 6.8494684217722322e-06 -▁Luk 6.8494684217722322e-06 -▁Whirlpool 6.8494684217722322e-06 -▁accepts 6.8494684217722322e-06 -▁dauerhaft 6.8494684217722322e-06 -▁embra 6.8494684217722322e-06 -▁returning 6.8494684217722322e-06 -▁reverse 6.8494684217722322e-06 -▁sharp 6.8494684217722322e-06 -▁zwingen 6.8494684217722322e-06 -ORE 6.8456799989725793e-06 -ausenden 6.8456799989725793e-06 -▁Aust 6.8456799989725793e-06 -▁Holy 6.8456799989725793e-06 -▁Thai 6.8456799989725793e-06 -▁aufgel 6.8456799989725793e-06 -▁Spezialisten 6.8418915761729273e-06 -▁Vogel 6.8418915761729273e-06 -▁conjunction 6.8418915761729273e-06 -▁styles 6.8418915761729273e-06 -▁transatlantic 6.8418915761729273e-06 -▁yellow 6.8418915761729273e-06 -ipping 6.8381031533732744e-06 -▁autonomy 6.8381031533732744e-06 -▁bekräftigt 6.8381031533732744e-06 -iously 6.8343147305736215e-06 -▁Army 6.8343147305736215e-06 -▁Monats 6.8343147305736215e-06 -▁Vorsitzender 6.8343147305736215e-06 -▁bevorstehenden 6.8343147305736215e-06 -▁embry 6.8343147305736215e-06 -▁optical 6.8343147305736215e-06 -ears 6.8305263077739686e-06 -ätzt 6.8305263077739686e-06 -▁Declaration 6.8305263077739686e-06 -▁Lebensmitteln 6.8305263077739686e-06 -▁Nonetheless 6.8305263077739686e-06 -▁Privacy 6.8305263077739686e-06 -▁Provence 6.8305263077739686e-06 -▁Ron 6.8305263077739686e-06 -▁erläutert 6.8305263077739686e-06 -▁irische 6.8305263077739686e-06 -▁long- 6.8305263077739686e-06 -▁monat 6.8305263077739686e-06 -▁realized 6.8305263077739686e-06 -▁unterhalten 6.8305263077739686e-06 -▁verwöhnen 6.8305263077739686e-06 -▁zuf 6.8305263077739686e-06 -▁Gewähr 6.8267378849743157e-06 -▁Indonesia 6.8267378849743157e-06 -▁erzielten 6.8267378849743157e-06 -▁follow-up 6.8267378849743157e-06 -▁gran 6.8267378849743157e-06 -▁lacking 6.8267378849743157e-06 -▁meistens 6.8267378849743157e-06 -▁südlich 6.8267378849743157e-06 -anders 6.8229494621746628e-06 -rade 6.8229494621746628e-06 -на 6.8229494621746628e-06 -▁Echtzeit 6.8229494621746628e-06 -▁Ltd. 6.8229494621746628e-06 -▁Ortschaft 6.8229494621746628e-06 -▁inquiry 6.8229494621746628e-06 -kin 6.81916103937501e-06 -▁Tarif 6.81916103937501e-06 -▁gesammelt 6.81916103937501e-06 -▁rejection 6.81916103937501e-06 -gelegte 6.8153726165753571e-06 -höhe 6.8153726165753571e-06 -▁1968 6.8153726165753571e-06 -▁Kinnock 6.8153726165753571e-06 -▁bla 6.8153726165753571e-06 -WE 6.811584193775705e-06 -atalog 6.811584193775705e-06 -betten 6.811584193775705e-06 -ektors 6.811584193775705e-06 -ütter 6.811584193775705e-06 -▁happ 6.811584193775705e-06 -▁incorporate 6.811584193775705e-06 -▁invitation 6.811584193775705e-06 -▁pione 6.811584193775705e-06 -ographic 6.8077957709760521e-06 -▁Quick 6.8077957709760521e-06 -▁Spir 6.8077957709760521e-06 -▁Tap 6.8077957709760521e-06 -▁Weiterbildung 6.8077957709760521e-06 -▁begeistert 6.8077957709760521e-06 -▁finanz 6.8077957709760521e-06 -▁nutz 6.8077957709760521e-06 -▁tact 6.8077957709760521e-06 -chi 6.8040073481763992e-06 -clusive 6.8040073481763992e-06 -orld 6.8040073481763992e-06 -▁Alpine 6.8040073481763992e-06 -▁Legislativ 6.8040073481763992e-06 -▁Palazzo 6.8040073481763992e-06 -▁Stabilitätspakt 6.8040073481763992e-06 -▁extern 6.8040073481763992e-06 -▁mündlichen 6.8040073481763992e-06 --Seite 6.8002189253767463e-06 -handlungen 6.8002189253767463e-06 -p. 6.8002189253767463e-06 -▁Clearly 6.8002189253767463e-06 -▁Climate 6.8002189253767463e-06 -▁Dich 6.8002189253767463e-06 -▁checking 6.8002189253767463e-06 -▁departments 6.8002189253767463e-06 -▁sonn 6.8002189253767463e-06 -▁testen 6.8002189253767463e-06 -▁zivil 6.8002189253767463e-06 -aving 6.7964305025770935e-06 -fa 6.7964305025770935e-06 -▁Null 6.7964305025770935e-06 -▁PSE 6.7964305025770935e-06 -▁klären 6.7964305025770935e-06 -▁torture 6.7964305025770935e-06 -achsen 6.7926420797774406e-06 -design 6.7926420797774406e-06 -immungs 6.7926420797774406e-06 -ples 6.7926420797774406e-06 -▁unfortunate 6.7926420797774406e-06 --Team 6.7888536569777877e-06 -AST 6.7888536569777877e-06 -emma 6.7888536569777877e-06 -reitung 6.7888536569777877e-06 -utze 6.7888536569777877e-06 -▁Anregungen 6.7888536569777877e-06 -▁Aufforderung 6.7888536569777877e-06 -▁IR 6.7888536569777877e-06 -▁pine 6.7888536569777877e-06 -▁separately 6.7888536569777877e-06 -▁sul 6.7888536569777877e-06 -▁survive 6.7888536569777877e-06 --- 6.7850652341781348e-06 -uhr 6.7850652341781348e-06 -▁Israels 6.7850652341781348e-06 -▁Optimierung 6.7850652341781348e-06 -▁angehören 6.7850652341781348e-06 -▁anx 6.7850652341781348e-06 -▁corn 6.7850652341781348e-06 -▁searching 6.7850652341781348e-06 -▁tailored 6.7850652341781348e-06 -▁vorteil 6.7850652341781348e-06 -28 6.7812768113784827e-06 -basis 6.7812768113784827e-06 -▁Aktualisierung 6.7812768113784827e-06 -▁Sac 6.7812768113784827e-06 -▁guides 6.7812768113784827e-06 --Stand 6.77748838857883e-06 -stehenden 6.77748838857883e-06 -sweise 6.77748838857883e-06 -▁1976 6.77748838857883e-06 -▁Aufnahmen 6.77748838857883e-06 -▁Clinton 6.77748838857883e-06 -▁Rande 6.77748838857883e-06 -▁Unterdrückung 6.77748838857883e-06 -▁governed 6.77748838857883e-06 -▁ul 6.77748838857883e-06 -aner 6.773699965779177e-06 -dateien 6.773699965779177e-06 -eden 6.773699965779177e-06 -▁Feind 6.773699965779177e-06 -▁Liquid 6.773699965779177e-06 -▁Sech 6.773699965779177e-06 -▁denk 6.773699965779177e-06 -▁registrieren 6.773699965779177e-06 -▁teilgenommen 6.773699965779177e-06 -assten 6.7699115429795241e-06 -geschrieben 6.7699115429795241e-06 -▁Stadium 6.7699115429795241e-06 -▁Tschechischen 6.7699115429795241e-06 -▁Verantwortlichen 6.7699115429795241e-06 -▁delicate 6.7699115429795241e-06 -▁phot 6.7699115429795241e-06 -rage 6.7661231201798712e-06 -renz 6.7661231201798712e-06 -▁Hotelsafe 6.7661231201798712e-06 -▁Matt 6.7661231201798712e-06 -▁Moskau 6.7661231201798712e-06 -▁TNG 6.7661231201798712e-06 -▁kauf 6.7661231201798712e-06 -cul 6.7623346973802183e-06 -öten 6.7623346973802183e-06 -▁Kult 6.7623346973802183e-06 -▁abandoned 6.7623346973802183e-06 -▁diagnost 6.7623346973802183e-06 -,3 6.7585462745805654e-06 --Vor 6.7585462745805654e-06 -abeth 6.7585462745805654e-06 -adium 6.7585462745805654e-06 -▁Display 6.7585462745805654e-06 -▁Juden 6.7585462745805654e-06 -▁Sometimes 6.7585462745805654e-06 -▁entscheidet 6.7585462745805654e-06 -▁freundlichen 6.7585462745805654e-06 -▁seminars 6.7585462745805654e-06 -▁vegetables 6.7585462745805654e-06 -ogan 6.7547578517809133e-06 -rägen 6.7547578517809133e-06 -ätter 6.7547578517809133e-06 -▁Darauf 6.7547578517809133e-06 -▁Korean 6.7547578517809133e-06 -▁analys 6.7547578517809133e-06 -▁angeben 6.7547578517809133e-06 -▁characterised 6.7547578517809133e-06 -▁codes 6.7547578517809133e-06 -▁exceptions 6.7547578517809133e-06 -▁keinesfalls 6.7547578517809133e-06 -▁lights 6.7547578517809133e-06 -▁solide 6.7547578517809133e-06 -▁strenge 6.7547578517809133e-06 -▁tolerance 6.7547578517809133e-06 -▁upcoming 6.7547578517809133e-06 -▁Albania 6.7509694289812604e-06 -▁Jak 6.7509694289812604e-06 -▁vend 6.7509694289812604e-06 -platten 6.7471810061816076e-06 -wendungen 6.7471810061816076e-06 -▁Danke 6.7471810061816076e-06 -▁hielt 6.7471810061816076e-06 -aman 6.7433925833819547e-06 -aufgaben 6.7433925833819547e-06 -kontrollen 6.7433925833819547e-06 -laus 6.7433925833819547e-06 -ng 6.7433925833819547e-06 -▁Chicago 6.7433925833819547e-06 -▁Kohlen 6.7433925833819547e-06 -▁Lebensqualität 6.7433925833819547e-06 -▁Partners 6.7433925833819547e-06 -▁Reaktionen 6.7433925833819547e-06 -▁anzeigen 6.7433925833819547e-06 -▁defects 6.7433925833819547e-06 -▁discipline 6.7433925833819547e-06 -▁hosts 6.7433925833819547e-06 -▁loud 6.7433925833819547e-06 -▁reduces 6.7433925833819547e-06 -▁striking 6.7433925833819547e-06 -▁zust 6.7433925833819547e-06 --Auf 6.7396041605823018e-06 -ifer 6.7396041605823018e-06 -igur 6.7396041605823018e-06 -wachs 6.7396041605823018e-06 -▁149 6.7396041605823018e-06 -▁Color 6.7396041605823018e-06 -▁Intell 6.7396041605823018e-06 -▁fut 6.7396041605823018e-06 -▁herstellen 6.7396041605823018e-06 -▁tub 6.7396041605823018e-06 --Vertrag 6.7358157377826489e-06 -▁Cro 6.7358157377826489e-06 -▁LD 6.7358157377826489e-06 -▁derived 6.7358157377826489e-06 -▁drücken 6.7358157377826489e-06 -▁feels 6.7358157377826489e-06 -▁setup 6.7358157377826489e-06 -elson 6.732027314982996e-06 -eted 6.732027314982996e-06 -idge 6.732027314982996e-06 -künd 6.732027314982996e-06 -mb 6.732027314982996e-06 -oftware 6.732027314982996e-06 -▁Jeden 6.732027314982996e-06 -▁LateRooms.com 6.732027314982996e-06 -▁Wartung 6.732027314982996e-06 -▁appoint 6.732027314982996e-06 -▁diskrimin 6.732027314982996e-06 -▁immense 6.732027314982996e-06 -▁inde 6.732027314982996e-06 -▁rede 6.732027314982996e-06 -▁sofa 6.732027314982996e-06 -▁spiegelt 6.732027314982996e-06 -altete 6.7282388921833431e-06 -eler 6.7282388921833431e-06 -igerweise 6.7282388921833431e-06 -▁Einzelnen 6.7282388921833431e-06 -▁Rav 6.7282388921833431e-06 -▁Subventionen 6.7282388921833431e-06 -▁chocolate 6.7282388921833431e-06 -▁mitgeteilt 6.7282388921833431e-06 -▁recycling 6.7282388921833431e-06 -▁simplify 6.7282388921833431e-06 -▁virt 6.7282388921833431e-06 -BER 6.7244504693836911e-06 -üllen 6.7244504693836911e-06 -▁Avenue 6.7244504693836911e-06 -▁Ehren 6.7244504693836911e-06 -▁Vul 6.7244504693836911e-06 -▁vorbe 6.7244504693836911e-06 -▁zurückge 6.7244504693836911e-06 -einheit 6.7206620465840382e-06 -▁Litauen 6.7206620465840382e-06 -▁dish 6.7206620465840382e-06 -▁vereinfachen 6.7206620465840382e-06 --specific 6.7168736237843853e-06 -messe 6.7168736237843853e-06 -▁Emp 6.7168736237843853e-06 -▁Gewerkschaften 6.7168736237843853e-06 -▁Liter 6.7168736237843853e-06 -▁Working 6.7168736237843853e-06 -▁gele 6.7168736237843853e-06 -▁graf 6.7168736237843853e-06 -▁hr 6.7168736237843853e-06 -▁slight 6.7168736237843853e-06 -▁standen 6.7168736237843853e-06 -▁86 6.7130852009847324e-06 -▁Migrations 6.7130852009847324e-06 -▁exploring 6.7130852009847324e-06 -▁hiermit 6.7130852009847324e-06 -26 6.7092967781850795e-06 -ishment 6.7092967781850795e-06 -▁boom 6.7092967781850795e-06 -▁celebrated 6.7092967781850795e-06 -▁grants 6.7092967781850795e-06 -automat 6.7055083553854266e-06 -omaten 6.7055083553854266e-06 -quet 6.7055083553854266e-06 -▁Ath 6.7055083553854266e-06 -▁Beton 6.7055083553854266e-06 -▁Palästinens 6.7055083553854266e-06 -▁Vereinigung 6.7055083553854266e-06 -▁gegenseitige 6.7055083553854266e-06 -▁penalties 6.7055083553854266e-06 -▁philosoph 6.7055083553854266e-06 -▁receipt 6.7055083553854266e-06 -▁taught 6.7055083553854266e-06 -▁trademarks 6.7055083553854266e-06 -▁unnöt 6.7055083553854266e-06 --Daten 6.7017199325857737e-06 -aben 6.7017199325857737e-06 -dos 6.7017199325857737e-06 -eare 6.7017199325857737e-06 -ш 6.7017199325857737e-06 -▁3.5 6.7017199325857737e-06 -▁Entertainment 6.7017199325857737e-06 -▁Mazedonien 6.7017199325857737e-06 -▁Prestige 6.7017199325857737e-06 -greif 6.6979315097861208e-06 -▁automated 6.6979315097861208e-06 -▁inkl 6.6979315097861208e-06 -▁zwanzig 6.6979315097861208e-06 -rane 6.6941430869864688e-06 -▁absor 6.6941430869864688e-06 -▁aggressive 6.6941430869864688e-06 -▁ausgelöst 6.6941430869864688e-06 -▁regimes 6.6941430869864688e-06 -▁schließe 6.6941430869864688e-06 -sinn 6.6903546641868159e-06 -▁Vorsicht 6.6903546641868159e-06 -▁idealen 6.6903546641868159e-06 -▁indication 6.6903546641868159e-06 -eart 6.686566241387163e-06 -ourismus 6.686566241387163e-06 -▁Amtszeit 6.686566241387163e-06 -▁Maxim 6.686566241387163e-06 -▁Ri 6.686566241387163e-06 -▁angehen 6.686566241387163e-06 -▁bedenken 6.686566241387163e-06 -▁beiz 6.686566241387163e-06 -▁erh 6.686566241387163e-06 -▁shar 6.686566241387163e-06 -▁wording 6.686566241387163e-06 -SD 6.68277781858751e-06 -▁2,5 6.68277781858751e-06 -▁Currently 6.68277781858751e-06 -▁Erweiterungs 6.68277781858751e-06 -▁Horn 6.68277781858751e-06 -▁Medical 6.68277781858751e-06 -▁Meinungs 6.68277781858751e-06 -▁begr 6.68277781858751e-06 -▁irgendwelche 6.68277781858751e-06 -▁jeweilige 6.68277781858751e-06 -▁ster 6.68277781858751e-06 -▁veget 6.68277781858751e-06 -prochen 6.6789893957878572e-06 -▁Gumm 6.6789893957878572e-06 -▁Above 6.6752009729882043e-06 -▁Anwendungsbereich 6.6752009729882043e-06 -▁Spar 6.6752009729882043e-06 -▁accepting 6.6752009729882043e-06 -▁circle 6.6752009729882043e-06 -▁coinc 6.6752009729882043e-06 -▁finger 6.6752009729882043e-06 -▁perf 6.6752009729882043e-06 -▁poss 6.6752009729882043e-06 -▁spüren 6.6752009729882043e-06 -bten 6.6714125501885514e-06 -rey 6.6714125501885514e-06 -such 6.6714125501885514e-06 -▁Weltbank 6.6714125501885514e-06 -▁analysieren 6.6714125501885514e-06 -▁badly 6.6714125501885514e-06 -▁deutscher 6.6714125501885514e-06 -▁formul 6.6714125501885514e-06 -▁koordin 6.6714125501885514e-06 -▁relevanten 6.6714125501885514e-06 -▁superm 6.6714125501885514e-06 -▁vielfält 6.6714125501885514e-06 -uds 6.6676241273888985e-06 -▁CA 6.6676241273888985e-06 -▁Einschränkung 6.6676241273888985e-06 -▁Gerichtshofs 6.6676241273888985e-06 -▁editing 6.6676241273888985e-06 -▁sech 6.6676241273888985e-06 -.T 6.6638357045892465e-06 -deck 6.6638357045892465e-06 -▁Kra 6.6638357045892465e-06 -▁SIM 6.6638357045892465e-06 -▁Schiffs 6.6638357045892465e-06 -▁fünft 6.6638357045892465e-06 -▁kost 6.6638357045892465e-06 -▁nutzbar 6.6638357045892465e-06 -▁quota 6.6638357045892465e-06 -▁racism 6.6638357045892465e-06 -▁Age 6.6600472817895936e-06 -▁Dus 6.6600472817895936e-06 -▁Klin 6.6600472817895936e-06 -▁Spuren 6.6600472817895936e-06 -▁Zimmerservice 6.6600472817895936e-06 -▁gastronom 6.6600472817895936e-06 -▁schützt 6.6600472817895936e-06 -aciones 6.6562588589899407e-06 -emberg 6.6562588589899407e-06 -icts 6.6562588589899407e-06 -iefen 6.6562588589899407e-06 -▁Verträgen 6.6562588589899407e-06 -▁officer 6.6562588589899407e-06 -▁telling 6.6562588589899407e-06 -SL 6.6524704361902878e-06 -fig 6.6524704361902878e-06 -het 6.6524704361902878e-06 -▁Protokolls 6.6524704361902878e-06 -▁anc 6.6524704361902878e-06 -▁diplomat 6.6524704361902878e-06 -▁ignoriert 6.6524704361902878e-06 -▁reichhalt 6.6524704361902878e-06 -▁renov 6.6524704361902878e-06 -▁width 6.6524704361902878e-06 -▁DV 6.6486820133906349e-06 -▁ign 6.6486820133906349e-06 -▁interaction 6.6486820133906349e-06 -▁BC 6.644893590590982e-06 -▁Kaz 6.644893590590982e-06 -▁Kont 6.644893590590982e-06 -bey 6.6411051677913291e-06 -gefühl 6.6411051677913291e-06 -hafen 6.6411051677913291e-06 -ière 6.6411051677913291e-06 -ommiss 6.6411051677913291e-06 -▁GBP 6.6411051677913291e-06 -▁Wohlbefinden 6.6411051677913291e-06 -▁geltend 6.6411051677913291e-06 -▁kritisch 6.6411051677913291e-06 -▁Computers 6.6373167449916762e-06 -▁Kel 6.6373167449916762e-06 -▁Lav 6.6373167449916762e-06 -▁Naturally 6.6373167449916762e-06 -▁Ursprung 6.6373167449916762e-06 -▁Zweit 6.6373167449916762e-06 -▁continually 6.6373167449916762e-06 -▁erwart 6.6373167449916762e-06 -▁landscapes 6.6373167449916762e-06 -▁technolog 6.6373167449916762e-06 -▁übrig 6.6373167449916762e-06 -dez 6.6335283221920242e-06 -ifikation 6.6335283221920242e-06 -▁eure 6.6335283221920242e-06 -▁trails 6.6335283221920242e-06 -▁wenigstens 6.6335283221920242e-06 -leiten 6.6297398993923713e-06 -orate 6.6297398993923713e-06 -zeigen 6.6297398993923713e-06 -▁Erkenntnisse 6.6297398993923713e-06 -▁modes 6.6297398993923713e-06 -▁multipl 6.6297398993923713e-06 -▁muy 6.6297398993923713e-06 -▁preferences 6.6297398993923713e-06 -oque 6.6259514765927184e-06 -osing 6.6259514765927184e-06 -▁'d 6.6259514765927184e-06 -▁UND 6.6259514765927184e-06 -▁contest 6.6259514765927184e-06 -▁undertaking 6.6259514765927184e-06 -▁Ein- 6.6221630537930655e-06 -▁Kindes 6.6221630537930655e-06 -▁Silber 6.6221630537930655e-06 -▁associ 6.6221630537930655e-06 -▁configure 6.6221630537930655e-06 -▁denied 6.6221630537930655e-06 -▁gesunden 6.6221630537930655e-06 -▁interim 6.6221630537930655e-06 -▁shareholders 6.6221630537930655e-06 -▁Clean 6.6183746309934126e-06 -▁Eingabe 6.6183746309934126e-06 -▁Eurostat 6.6183746309934126e-06 -▁Guard 6.6183746309934126e-06 -▁angesch 6.6183746309934126e-06 -▁auswärtige 6.6183746309934126e-06 -▁begab 6.6183746309934126e-06 -▁cheaper 6.6183746309934126e-06 -▁deinen 6.6183746309934126e-06 -▁erneuerbare 6.6183746309934126e-06 -▁navig 6.6183746309934126e-06 -▁schrieb 6.6183746309934126e-06 -▁setzte 6.6183746309934126e-06 -keeping 6.61458620819376e-06 -otti 6.61458620819376e-06 -▁CC 6.61458620819376e-06 -▁Ecke 6.61458620819376e-06 -▁einzelner 6.61458620819376e-06 -▁responses 6.61458620819376e-06 -iments 6.6107977853941068e-06 -ittel 6.6107977853941068e-06 -lets 6.6107977853941068e-06 -page 6.6107977853941068e-06 -verz 6.6107977853941068e-06 -▁Conf 6.6107977853941068e-06 -▁Elekt 6.6107977853941068e-06 -▁Kohärenz 6.6107977853941068e-06 -▁drafted 6.6107977853941068e-06 -▁ensured 6.6107977853941068e-06 -aded 6.607009362594454e-06 -itären 6.607009362594454e-06 -mäß 6.607009362594454e-06 -▁arab 6.607009362594454e-06 -▁assumed 6.607009362594454e-06 -▁beider 6.607009362594454e-06 -▁doubl 6.607009362594454e-06 -▁gefährliche 6.607009362594454e-06 -▁gingen 6.607009362594454e-06 -▁pron 6.607009362594454e-06 -▁verp 6.607009362594454e-06 -asa 6.6032209397948019e-06 -streit 6.6032209397948019e-06 -▁DM 6.6032209397948019e-06 -▁Danube 6.6032209397948019e-06 -▁Plast 6.6032209397948019e-06 -▁Rollen 6.6032209397948019e-06 -▁Spanisch 6.6032209397948019e-06 -▁Touch 6.6032209397948019e-06 -▁kompletten 6.6032209397948019e-06 -▁reductions 6.6032209397948019e-06 -▁walks 6.6032209397948019e-06 -▁Übersetzungen 6.6032209397948019e-06 -stunden 6.599432516995149e-06 -unktion 6.599432516995149e-06 -ù 6.599432516995149e-06 -▁Love 6.599432516995149e-06 -▁MT 6.599432516995149e-06 -▁gefährlichen 6.599432516995149e-06 -▁infringement 6.599432516995149e-06 -▁reinforce 6.599432516995149e-06 -▁sustained 6.599432516995149e-06 -▁unlimited 6.599432516995149e-06 -zugreifen 6.5956440941954961e-06 -züglich 6.5956440941954961e-06 -▁Fernseher 6.5956440941954961e-06 -▁Siena 6.5956440941954961e-06 -▁T. 6.5956440941954961e-06 -▁Tourist 6.5956440941954961e-06 -▁absch 6.5956440941954961e-06 -▁combining 6.5956440941954961e-06 -▁doctors 6.5956440941954961e-06 -▁genehmigt 6.5956440941954961e-06 -▁Üb 6.5956440941954961e-06 -▁eingerichteten 6.5918556713958432e-06 -▁imprison 6.5918556713958432e-06 -▁random 6.5918556713958432e-06 -▁reiterate 6.5918556713958432e-06 -schm 6.58806724859619e-06 -▁Aufsicht 6.58806724859619e-06 -▁Konsultation 6.58806724859619e-06 -▁circulation 6.58806724859619e-06 -▁evil 6.58806724859619e-06 -▁gren 6.58806724859619e-06 -▁minds 6.58806724859619e-06 -▁missions 6.58806724859619e-06 -▁strafrecht 6.58806724859619e-06 -▁tat 6.58806724859619e-06 -▁wirkungs 6.58806724859619e-06 -▁Bast 6.5842788257965375e-06 -▁Bewertungen 6.5842788257965375e-06 -▁Lokal 6.5842788257965375e-06 -▁Weihnachts 6.5842788257965375e-06 -▁hüb 6.5842788257965375e-06 -▁verschiedensten 6.5842788257965375e-06 -▁wartet 6.5842788257965375e-06 -bot 6.5804904029968846e-06 -▁Ausgleich 6.5804904029968846e-06 -▁Peru 6.5804904029968846e-06 -▁embedded 6.5804904029968846e-06 -▁quantities 6.5804904029968846e-06 -▁stood 6.5804904029968846e-06 -atisch 6.5767019801972325e-06 -ündung 6.5767019801972325e-06 -▁Ausstellungen 6.5767019801972325e-06 -▁exklusive 6.5767019801972325e-06 -▁glück 6.5767019801972325e-06 -▁internationales 6.5767019801972325e-06 -asters 6.57291355739758e-06 -jor 6.57291355739758e-06 -▁Einwohnern 6.57291355739758e-06 -▁Wikitravel 6.57291355739758e-06 -▁tension 6.57291355739758e-06 --en 6.5691251345979267e-06 -ifiable 6.5691251345979267e-06 -onischen 6.5691251345979267e-06 -vertra 6.5691251345979267e-06 -▁Bisher 6.5691251345979267e-06 -▁Building 6.5691251345979267e-06 -▁G8 6.5691251345979267e-06 -▁Seminar 6.5691251345979267e-06 -▁Wiederauf 6.5691251345979267e-06 -▁m. 6.5691251345979267e-06 -▁thin 6.5691251345979267e-06 -▁verhalten 6.5691251345979267e-06 -Pro 6.5653367117982738e-06 -cribed 6.5653367117982738e-06 -lech 6.5653367117982738e-06 -rem 6.5653367117982738e-06 -▁220 6.5653367117982738e-06 -▁Tragödie 6.5653367117982738e-06 -▁Veranstaltungs 6.5653367117982738e-06 -▁engagiert 6.5653367117982738e-06 -▁impress 6.5653367117982738e-06 -▁participated 6.5653367117982738e-06 -ignano 6.561548288998621e-06 -▁Heritage 6.561548288998621e-06 -▁Mitarbeit 6.561548288998621e-06 -▁anwend 6.561548288998621e-06 -▁dos 6.561548288998621e-06 -▁endet 6.561548288998621e-06 -▁flex 6.561548288998621e-06 --12 6.5577598661989681e-06 -amera 6.5577598661989681e-06 -wing 6.5577598661989681e-06 -▁16th 6.5577598661989681e-06 -▁Mars 6.5577598661989681e-06 -▁fuels 6.5577598661989681e-06 -▁hire 6.5577598661989681e-06 -ighten 6.5539714433993152e-06 -▁Harbour 6.5539714433993152e-06 -▁ausges 6.5539714433993152e-06 -▁trad 6.5539714433993152e-06 -▁ungeachtet 6.5539714433993152e-06 -▁zurückkehren 6.5539714433993152e-06 -agert 6.5501830205996623e-06 -earbeit 6.5501830205996623e-06 -schirm 6.5501830205996623e-06 -wäl 6.5501830205996623e-06 -▁Grande 6.5501830205996623e-06 -▁Hinzu 6.5501830205996623e-06 -▁li 6.5501830205996623e-06 -▁nennt 6.5501830205996623e-06 -▁publ 6.5501830205996623e-06 -agog 6.54639459780001e-06 -kw 6.54639459780001e-06 -wirkungen 6.54639459780001e-06 -zentrums 6.54639459780001e-06 -▁Could 6.54639459780001e-06 -▁assembl 6.54639459780001e-06 -▁deleted 6.54639459780001e-06 -▁isolation 6.54639459780001e-06 -▁vig 6.54639459780001e-06 -zuführen 6.5426061750003573e-06 -änglich 6.5426061750003573e-06 -▁Christen 6.5426061750003573e-06 -▁Gesprächen 6.5426061750003573e-06 -▁Sy 6.5426061750003573e-06 -▁boards 6.5426061750003573e-06 -▁riesige 6.5426061750003573e-06 -▁votre 6.5426061750003573e-06 -▁äußerster 6.5426061750003573e-06 -asma 6.5388177522007045e-06 -owed 6.5388177522007045e-06 -▁Mi 6.5388177522007045e-06 -▁defending 6.5388177522007045e-06 -▁enh 6.5388177522007045e-06 -▁ov 6.5388177522007045e-06 -▁requiring 6.5388177522007045e-06 -▁tiny 6.5388177522007045e-06 -enzen 6.5350293294010516e-06 -opolis 6.5350293294010516e-06 -path 6.5350293294010516e-06 -▁Stay 6.5350293294010516e-06 -▁myst 6.5350293294010516e-06 -▁umweltfreund 6.5350293294010516e-06 -▁ökologische 6.5350293294010516e-06 -chn 6.5312409066013987e-06 -ilding 6.5312409066013987e-06 -▁Benjamin 6.5312409066013987e-06 -▁Moderne 6.5312409066013987e-06 -▁Vorbild 6.5312409066013987e-06 -▁arbeitete 6.5312409066013987e-06 -▁moderate 6.5312409066013987e-06 -▁plat 6.5312409066013987e-06 -▁transformed 6.5312409066013987e-06 -ianische 6.5274524838017458e-06 -ifft 6.5274524838017458e-06 -rikt 6.5274524838017458e-06 -▁BA 6.5274524838017458e-06 -▁Betrugs 6.5274524838017458e-06 -▁Fig 6.5274524838017458e-06 -▁HOTEL 6.5274524838017458e-06 -▁Steu 6.5274524838017458e-06 -▁consistency 6.5274524838017458e-06 -▁cul 6.5274524838017458e-06 -▁handic 6.5274524838017458e-06 -gespr 6.5236640610020929e-06 -▁Anst 6.5236640610020929e-06 -▁Geräten 6.5236640610020929e-06 -▁LA 6.5236640610020929e-06 -▁abwechslungs 6.5236640610020929e-06 -▁derog 6.5236640610020929e-06 -▁simplification 6.5236640610020929e-06 -kürz 6.51987563820244e-06 -tein 6.51987563820244e-06 -▁Charakt 6.51987563820244e-06 -▁Ernährungs 6.51987563820244e-06 -▁Fine 6.51987563820244e-06 -▁PM 6.51987563820244e-06 -▁Verstärkung 6.51987563820244e-06 -▁Zer 6.51987563820244e-06 -▁calculated 6.51987563820244e-06 -▁daughter 6.51987563820244e-06 -▁längst 6.51987563820244e-06 -▁maj 6.51987563820244e-06 -▁175 6.5160872154027879e-06 -▁Entwicklungszusammenarbeit 6.5160872154027879e-06 -▁gefällt 6.5160872154027879e-06 -▁trennen 6.5160872154027879e-06 -▁underline 6.5160872154027879e-06 -▁übersetzt 6.5160872154027879e-06 -bringt 6.5122987926031351e-06 -▁Herberge 6.5122987926031351e-06 -▁Oberflächen 6.5122987926031351e-06 -▁Statistik 6.5122987926031351e-06 -▁abgehalten 6.5122987926031351e-06 --century 6.5085103698034822e-06 -IVE 6.5085103698034822e-06 -zusprechen 6.5085103698034822e-06 -▁Manche 6.5085103698034822e-06 -▁boxes 6.5085103698034822e-06 -▁enemy 6.5085103698034822e-06 -▁sightseeing 6.5085103698034822e-06 -▁struggl 6.5085103698034822e-06 --bu 6.5047219470038293e-06 -estone 6.5047219470038293e-06 -leiben 6.5047219470038293e-06 -ockt 6.5047219470038293e-06 -uman 6.5047219470038293e-06 -▁61 6.5047219470038293e-06 -▁Kü 6.5047219470038293e-06 -▁Libanon 6.5047219470038293e-06 -▁Neub 6.5047219470038293e-06 -▁Ratsvorsitzes 6.5047219470038293e-06 -▁consisting 6.5047219470038293e-06 -▁einziges 6.5047219470038293e-06 -▁listened 6.5047219470038293e-06 -▁motions 6.5047219470038293e-06 -▁newspapers 6.5047219470038293e-06 -▁Bot 6.5009335242041764e-06 -▁DS9 6.5009335242041764e-06 -▁Flüchtlings 6.5009335242041764e-06 -▁fabric 6.5009335242041764e-06 -stätte 6.4971451014045235e-06 -▁93 6.4971451014045235e-06 -▁Karel 6.4971451014045235e-06 -▁Virtual 6.4971451014045235e-06 -▁kooper 6.4971451014045235e-06 -▁kritische 6.4971451014045235e-06 -▁resorts 6.4971451014045235e-06 -▁zunehmenden 6.4971451014045235e-06 --Distribution 6.4933566786048706e-06 -linder 6.4933566786048706e-06 -▁Dienstag 6.4933566786048706e-06 -▁Future 6.4933566786048706e-06 -▁Limited 6.4933566786048706e-06 -▁Origin 6.4933566786048706e-06 -▁Rahmenprogramm 6.4933566786048706e-06 -▁Sozialdemokraten 6.4933566786048706e-06 -▁alliance 6.4933566786048706e-06 -▁continental 6.4933566786048706e-06 -fäh 6.4895682558052177e-06 -ā 6.4895682558052177e-06 -▁announcement 6.4895682558052177e-06 -▁aufweisen 6.4895682558052177e-06 -▁bases 6.4895682558052177e-06 -▁clothes 6.4895682558052177e-06 -▁culinary 6.4895682558052177e-06 -▁gegr 6.4895682558052177e-06 -▁gemessen 6.4895682558052177e-06 -▁plane 6.4895682558052177e-06 --Ge 6.4857798330055657e-06 -rio 6.4857798330055657e-06 -▁Auslegung 6.4857798330055657e-06 -▁Bluetooth 6.4857798330055657e-06 -▁Spons 6.4857798330055657e-06 -▁endless 6.4857798330055657e-06 -▁ethnischen 6.4857798330055657e-06 -▁islam 6.4857798330055657e-06 -asp 6.4819914102059128e-06 -▁Anhänger 6.4819914102059128e-06 -▁commonly 6.4819914102059128e-06 -▁explos 6.4819914102059128e-06 -▁klassische 6.4819914102059128e-06 -mengen 6.47820298740626e-06 -▁Devisen 6.47820298740626e-06 -▁Finanzmittel 6.47820298740626e-06 -▁concentrated 6.47820298740626e-06 -▁vorherige 6.47820298740626e-06 -gewinn 6.474414564606607e-06 -map 6.474414564606607e-06 -▁Kalender 6.474414564606607e-06 -▁Ostsee 6.474414564606607e-06 -▁identical 6.474414564606607e-06 -▁investigate 6.474414564606607e-06 -▁traur 6.474414564606607e-06 --En 6.4706261418069541e-06 -iewer 6.4706261418069541e-06 -utzer 6.4706261418069541e-06 -▁Indem 6.4706261418069541e-06 -▁Shuttle 6.4706261418069541e-06 -▁large-scale 6.4706261418069541e-06 -▁presentations 6.4706261418069541e-06 -▁vicinity 6.4706261418069541e-06 -▁vorherigen 6.4706261418069541e-06 -▁älter 6.4706261418069541e-06 -ahrt 6.4668377190073012e-06 -aktions 6.4668377190073012e-06 -izität 6.4668377190073012e-06 -▁API 6.4668377190073012e-06 -▁angere 6.4668377190073012e-06 -▁beträchtliche 6.4668377190073012e-06 -▁crossing 6.4668377190073012e-06 -▁drast 6.4668377190073012e-06 -▁lohnt 6.4668377190073012e-06 -cape 6.4630492962076483e-06 -ermann 6.4630492962076483e-06 -kirche 6.4630492962076483e-06 -ów 6.4630492962076483e-06 -▁cargo 6.4630492962076483e-06 -▁homepage 6.4630492962076483e-06 -▁respecting 6.4630492962076483e-06 -aterie 6.4592608734079954e-06 -hundert 6.4592608734079954e-06 -▁128 6.4592608734079954e-06 -▁Lärm 6.4592608734079954e-06 -▁Potential 6.4592608734079954e-06 -▁camping 6.4592608734079954e-06 -▁durchgeführten 6.4592608734079954e-06 -▁prev 6.4592608734079954e-06 -▁seal 6.4592608734079954e-06 -▁sont 6.4592608734079954e-06 -meldung 6.4554724506083434e-06 -▁Libya 6.4554724506083434e-06 -▁Magic 6.4554724506083434e-06 -▁Parallel 6.4554724506083434e-06 -▁officers 6.4554724506083434e-06 -würdig 6.4516840278086905e-06 -▁Aufwand 6.4516840278086905e-06 -▁Rechenschaft 6.4516840278086905e-06 -▁cens 6.4516840278086905e-06 -▁gegenseitig 6.4516840278086905e-06 -▁inher 6.4516840278086905e-06 -▁weapon 6.4516840278086905e-06 -▁üblich 6.4516840278086905e-06 -imme 6.4478956050090376e-06 -ż 6.4478956050090376e-06 -▁Initiativ 6.4478956050090376e-06 -▁Scha 6.4478956050090376e-06 -▁Spam 6.4478956050090376e-06 -▁angep 6.4478956050090376e-06 -▁colonial 6.4478956050090376e-06 -▁permanently 6.4478956050090376e-06 -▁weekly 6.4478956050090376e-06 -▁zuverlässig 6.4478956050090376e-06 --room 6.4441071822093847e-06 -üssel 6.4441071822093847e-06 -▁Cook 6.4441071822093847e-06 -▁Eintrag 6.4441071822093847e-06 -▁Manhattan 6.4441071822093847e-06 -▁Marbella 6.4441071822093847e-06 -▁Sak 6.4441071822093847e-06 -▁Schre 6.4441071822093847e-06 -▁Week 6.4441071822093847e-06 -▁announ 6.4441071822093847e-06 -▁bond 6.4441071822093847e-06 -cribe 6.4403187594097318e-06 -ields 6.4403187594097318e-06 -▁Fertigung 6.4403187594097318e-06 -▁dell 6.4403187594097318e-06 -▁starkes 6.4403187594097318e-06 -▁ton 6.4403187594097318e-06 -gäste 6.4365303366100789e-06 -▁Leidenschaft 6.4365303366100789e-06 -▁Tol 6.4365303366100789e-06 -▁ending 6.4365303366100789e-06 -▁entertain 6.4365303366100789e-06 -ки 6.432741913810426e-06 -▁Crist 6.432741913810426e-06 -▁Schauspiel 6.432741913810426e-06 -▁Vorstand 6.432741913810426e-06 -▁humans 6.432741913810426e-06 -▁penal 6.432741913810426e-06 -▁stre 6.432741913810426e-06 -▁yo 6.432741913810426e-06 -OX 6.4289534910107731e-06 -eries 6.4289534910107731e-06 -▁Konfiguration 6.4289534910107731e-06 -▁ernste 6.4289534910107731e-06 -▁spont 6.4289534910107731e-06 --15 6.4251650682111211e-06 -bring 6.4251650682111211e-06 -во 6.4251650682111211e-06 -▁Ausfahrt 6.4251650682111211e-06 -▁Deluxe 6.4251650682111211e-06 -▁ME 6.4251650682111211e-06 -▁MPEG 6.4251650682111211e-06 -▁Slowenien 6.4251650682111211e-06 -▁connecting 6.4251650682111211e-06 -▁districts 6.4251650682111211e-06 -▁enthusiasm 6.4251650682111211e-06 -▁fu 6.4251650682111211e-06 -▁hea 6.4251650682111211e-06 -▁niederländischen 6.4251650682111211e-06 -▁publisher 6.4251650682111211e-06 -ertain 6.4213766454114682e-06 -oßen 6.4213766454114682e-06 -▁Entstehung 6.4213766454114682e-06 -▁Stefan 6.4213766454114682e-06 -▁Vot 6.4213766454114682e-06 -▁denselben 6.4213766454114682e-06 -▁refined 6.4213766454114682e-06 -▁sus 6.4213766454114682e-06 -disziplin 6.4175882226118153e-06 -rückt 6.4175882226118153e-06 -▁provin 6.4175882226118153e-06 -▁restoration 6.4175882226118153e-06 -▁rul 6.4175882226118153e-06 -▁verehrten 6.4175882226118153e-06 -dest 6.4137997998121624e-06 -indlichen 6.4137997998121624e-06 -spruch 6.4137997998121624e-06 -ulse 6.4137997998121624e-06 -▁Heiz 6.4137997998121624e-06 -▁Palästina 6.4137997998121624e-06 -▁banned 6.4137997998121624e-06 -▁dominant 6.4137997998121624e-06 -▁optimum 6.4137997998121624e-06 -▁prejud 6.4137997998121624e-06 -▁sämtlichen 6.4137997998121624e-06 -anges 6.4100113770125095e-06 -▁Bali 6.4100113770125095e-06 -▁Flugzeuge 6.4100113770125095e-06 -▁Wörter 6.4100113770125095e-06 -▁bekanntesten 6.4100113770125095e-06 -▁ethn 6.4100113770125095e-06 -▁suddenly 6.4100113770125095e-06 -▁zeichnen 6.4100113770125095e-06 -.11 6.4062229542128566e-06 -▁Lodge 6.4062229542128566e-06 -▁adher 6.4062229542128566e-06 -▁analyses 6.4062229542128566e-06 -▁aufgehoben 6.4062229542128566e-06 -▁discretion 6.4062229542128566e-06 -▁grew 6.4062229542128566e-06 -▁pursuing 6.4062229542128566e-06 --Ländern 6.4024345314132037e-06 -▁Absichten 6.4024345314132037e-06 -▁Buchungs 6.4024345314132037e-06 -▁EMU 6.4024345314132037e-06 -▁Local 6.4024345314132037e-06 -▁Mittler 6.4024345314132037e-06 -▁SSL 6.4024345314132037e-06 -▁amtierender 6.4024345314132037e-06 -▁bez 6.4024345314132037e-06 -ioni 6.3986461086135517e-06 -▁Geschäftsbedingungen 6.3986461086135517e-06 -▁Grill 6.3986461086135517e-06 -▁Portug 6.3986461086135517e-06 -▁clos 6.3986461086135517e-06 -▁e.V. 6.3986461086135517e-06 -▁exploit 6.3986461086135517e-06 -▁observers 6.3986461086135517e-06 -▁wirksamen 6.3986461086135517e-06 -amous 6.3948576858138988e-06 -oked 6.3948576858138988e-06 -▁Architekten 6.3948576858138988e-06 -▁Piet 6.3948576858138988e-06 -▁Prem 6.3948576858138988e-06 -▁Sz 6.3948576858138988e-06 -▁booth 6.3948576858138988e-06 -▁discovery 6.3948576858138988e-06 -▁exch 6.3948576858138988e-06 -▁unprecedented 6.3948576858138988e-06 -▁verhandeln 6.3948576858138988e-06 -gin 6.3910692630142459e-06 -▁Einfuhr 6.3910692630142459e-06 -▁Hass 6.3910692630142459e-06 -▁Quer 6.3910692630142459e-06 -▁continuation 6.3910692630142459e-06 -▁passes 6.3910692630142459e-06 -▁specialised 6.3910692630142459e-06 -▁Anruf 6.387280840214593e-06 -▁Bruder 6.387280840214593e-06 -▁abend 6.387280840214593e-06 -▁adaptation 6.387280840214593e-06 -▁ausgeb 6.387280840214593e-06 -▁complimentary 6.387280840214593e-06 -▁perpet 6.387280840214593e-06 -▁pull 6.387280840214593e-06 -ühen 6.38349241741494e-06 -▁Lust 6.38349241741494e-06 -▁Minimum 6.38349241741494e-06 -▁Prob 6.38349241741494e-06 -▁accord 6.38349241741494e-06 -▁bevorzug 6.38349241741494e-06 -▁nos 6.38349241741494e-06 -▁separated 6.38349241741494e-06 -▁unteren 6.38349241741494e-06 -▁verständlich 6.38349241741494e-06 -▁zahlreicher 6.38349241741494e-06 --bas 6.3797039946152872e-06 -ILL 6.3797039946152872e-06 -emissionen 6.3797039946152872e-06 -fuhren 6.3797039946152872e-06 -omed 6.3797039946152872e-06 -äne 6.3797039946152872e-06 -▁Transaktionen 6.3797039946152872e-06 -ancen 6.3759155718156344e-06 -onnen 6.3759155718156344e-06 -▁horizont 6.3759155718156344e-06 -▁konserv 6.3759155718156344e-06 -weisungen 6.3721271490159815e-06 -▁XL 6.3721271490159815e-06 -▁ambience 6.3721271490159815e-06 -▁linguistic 6.3721271490159815e-06 -▁part-session 6.3721271490159815e-06 --Netz 6.3683387262163294e-06 -ferenzen 6.3683387262163294e-06 -utzt 6.3683387262163294e-06 -äten 6.3683387262163294e-06 -▁Micro 6.3683387262163294e-06 -▁accused 6.3683387262163294e-06 -▁knowing 6.3683387262163294e-06 -▁lov 6.3683387262163294e-06 -ERS 6.3645503034166765e-06 -▁Eurex 6.3645503034166765e-06 -▁beeinträchtigt 6.3645503034166765e-06 -▁mainstream 6.3645503034166765e-06 -▁ältesten 6.3645503034166765e-06 -iated 6.3607618806170236e-06 -rang 6.3607618806170236e-06 -status 6.3607618806170236e-06 -▁DH 6.3607618806170236e-06 -▁Lul 6.3607618806170236e-06 -▁Verhältnisse 6.3607618806170236e-06 -▁hyper 6.3607618806170236e-06 -▁vergleichen 6.3607618806170236e-06 -club 6.3569734578173707e-06 -raph 6.3569734578173707e-06 -▁Bir 6.3569734578173707e-06 -▁Niemand 6.3569734578173707e-06 -▁Perspektiven 6.3569734578173707e-06 -▁akzeptabel 6.3569734578173707e-06 -▁tackling 6.3569734578173707e-06 -hops 6.3531850350177178e-06 -iess 6.3531850350177178e-06 -▁Algarve 6.3531850350177178e-06 -▁Austr 6.3531850350177178e-06 -▁Ernährung 6.3531850350177178e-06 -▁Navigation 6.3531850350177178e-06 -▁annex 6.3531850350177178e-06 -▁binden 6.3531850350177178e-06 -▁gains 6.3531850350177178e-06 -▁gefangen 6.3531850350177178e-06 -▁jump 6.3531850350177178e-06 -▁preced 6.3531850350177178e-06 -stützung 6.349396612218065e-06 -▁filling 6.349396612218065e-06 -GA 6.3456081894184121e-06 -abschluss 6.3456081894184121e-06 -day 6.3456081894184121e-06 -weight 6.3456081894184121e-06 -▁17th 6.3456081894184121e-06 -▁Abonn 6.3456081894184121e-06 -▁Brit 6.3456081894184121e-06 -▁Eintritt 6.3456081894184121e-06 -▁glaubt 6.3456081894184121e-06 -▁implies 6.3456081894184121e-06 -▁mining 6.3456081894184121e-06 -ammlung 6.3418197666187592e-06 -bilder 6.3418197666187592e-06 -gon 6.3418197666187592e-06 -schten 6.3418197666187592e-06 -uma 6.3418197666187592e-06 -▁94 6.3418197666187592e-06 -▁Cos 6.3418197666187592e-06 -▁Verzögerung 6.3418197666187592e-06 -▁alive 6.3418197666187592e-06 -▁idealer 6.3418197666187592e-06 -▁luggage 6.3418197666187592e-06 -hrop 6.3380313438191071e-06 -▁Erwer 6.3380313438191071e-06 -▁Textes 6.3380313438191071e-06 -adem 6.3342429210194542e-06 -urop 6.3342429210194542e-06 -weichen 6.3342429210194542e-06 -ν 6.3342429210194542e-06 -▁Kathedrale 6.3342429210194542e-06 -▁Maison 6.3342429210194542e-06 -▁Nan 6.3342429210194542e-06 -▁Vall 6.3342429210194542e-06 -▁ausgelegt 6.3342429210194542e-06 -▁jahrel 6.3342429210194542e-06 -▁jüng 6.3342429210194542e-06 -▁twelve 6.3342429210194542e-06 -Net 6.3304544982198013e-06 -ulates 6.3304544982198013e-06 -▁Anweisungen 6.3304544982198013e-06 -▁Bedingung 6.3304544982198013e-06 -▁Outlook 6.3304544982198013e-06 -▁Rosen 6.3304544982198013e-06 -▁einzubeziehen 6.3304544982198013e-06 -▁insp 6.3304544982198013e-06 -▁vernachläss 6.3304544982198013e-06 -KS 6.3266660754201485e-06 -gerecht 6.3266660754201485e-06 -▁Haust 6.3266660754201485e-06 -▁Koordination 6.3266660754201485e-06 -▁Meanwhile 6.3266660754201485e-06 -▁compreh 6.3266660754201485e-06 -▁heil 6.3266660754201485e-06 -▁längere 6.3266660754201485e-06 -▁portugiesischen 6.3266660754201485e-06 -▁Riv 6.3228776526204956e-06 -▁Temperaturen 6.3228776526204956e-06 -▁migrants 6.3228776526204956e-06 -▁spezif 6.3228776526204956e-06 -▁wofür 6.3228776526204956e-06 -▁überwiegend 6.3228776526204956e-06 --Mar 6.3190892298208427e-06 -osph 6.3190892298208427e-06 -▁Winds 6.3190892298208427e-06 -▁adjusted 6.3190892298208427e-06 -▁hide 6.3190892298208427e-06 -▁völl 6.3190892298208427e-06 -iesslich 6.31530080702119e-06 -spflicht 6.31530080702119e-06 -trol 6.31530080702119e-06 -▁bewundern 6.31530080702119e-06 -▁failing 6.31530080702119e-06 -▁lebenden 6.31530080702119e-06 -▁securities 6.31530080702119e-06 -igende 6.3115123842215369e-06 -ärer 6.3115123842215369e-06 -ünde 6.3115123842215369e-06 -▁Demokrat 6.3115123842215369e-06 -▁satisf 6.3115123842215369e-06 -▁schönes 6.3115123842215369e-06 -▁warranty 6.3115123842215369e-06 -afr 6.3077239614218848e-06 -iefe 6.3077239614218848e-06 -mir 6.3077239614218848e-06 -ultural 6.3077239614218848e-06 -öhn 6.3077239614218848e-06 -üsten 6.3077239614218848e-06 -▁Child 6.3077239614218848e-06 -▁Hell 6.3077239614218848e-06 -▁Lied 6.3077239614218848e-06 -▁Norwegen 6.3077239614218848e-06 -▁glücklich 6.3077239614218848e-06 -▁tape 6.3077239614218848e-06 --Ad 6.303935538622232e-06 -dex 6.303935538622232e-06 -ifel 6.303935538622232e-06 -▁Doppelbett 6.303935538622232e-06 -▁friedlichen 6.303935538622232e-06 -▁sicherer 6.303935538622232e-06 -emann 6.3001471158225791e-06 -isi 6.3001471158225791e-06 -kf 6.3001471158225791e-06 -umpen 6.3001471158225791e-06 -▁1500 6.3001471158225791e-06 -▁backing 6.3001471158225791e-06 -▁eingereichten 6.3001471158225791e-06 -igence 6.2963586930229262e-06 -upl 6.2963586930229262e-06 -▁CPU 6.2963586930229262e-06 -▁Impro 6.2963586930229262e-06 -▁air-conditioned 6.2963586930229262e-06 -▁auszuüben 6.2963586930229262e-06 -▁lifestyle 6.2963586930229262e-06 -figuration 6.2925702702232733e-06 -ässigen 6.2925702702232733e-06 -▁befürworte 6.2925702702232733e-06 -▁congratulations 6.2925702702232733e-06 -▁explicit 6.2925702702232733e-06 -▁roles 6.2925702702232733e-06 -▁verankert 6.2925702702232733e-06 -assage 6.28878184742362e-06 -ernobyl 6.28878184742362e-06 -euer 6.28878184742362e-06 -gerufen 6.28878184742362e-06 -größe 6.28878184742362e-06 -ierender 6.28878184742362e-06 -mt 6.28878184742362e-06 -orge 6.28878184742362e-06 -uum 6.28878184742362e-06 -▁1981 6.28878184742362e-06 -▁Sowjetunion 6.28878184742362e-06 -▁Untersuchungs 6.28878184742362e-06 -▁fifteen 6.28878184742362e-06 -▁instit 6.28878184742362e-06 -▁krimin 6.28878184742362e-06 -ción 6.2849934246239675e-06 -mond 6.2849934246239675e-06 -▁Berichterstattern 6.2849934246239675e-06 -▁Caribbean 6.2849934246239675e-06 -▁Divers 6.2849934246239675e-06 -▁Felsen 6.2849934246239675e-06 -▁Haushaltsplans 6.2849934246239675e-06 -▁Henry 6.2849934246239675e-06 -▁abolition 6.2849934246239675e-06 -▁beitreten 6.2849934246239675e-06 -▁beseitigt 6.2849934246239675e-06 -▁consolidation 6.2849934246239675e-06 -▁einzelstaatlichen 6.2849934246239675e-06 -▁schlimmer 6.2849934246239675e-06 -griff 6.2812050018243146e-06 -▁Allein 6.2812050018243146e-06 -▁Blo 6.2812050018243146e-06 -▁Gefangenen 6.2812050018243146e-06 -▁Sus 6.2812050018243146e-06 -▁demonstration 6.2812050018243146e-06 -▁einzigartig 6.2812050018243146e-06 -▁strive 6.2812050018243146e-06 -fit 6.2774165790246626e-06 -üch 6.2774165790246626e-06 -▁Fragestunde 6.2774165790246626e-06 -▁Granada 6.2774165790246626e-06 -▁Kurse 6.2774165790246626e-06 -▁Singapore 6.2774165790246626e-06 -▁Strecken 6.2774165790246626e-06 -▁contributes 6.2774165790246626e-06 -▁greifen 6.2774165790246626e-06 -▁skill 6.2774165790246626e-06 -PL 6.27362815622501e-06 -def 6.27362815622501e-06 -horn 6.27362815622501e-06 -strom 6.27362815622501e-06 -term 6.27362815622501e-06 -▁87 6.27362815622501e-06 -▁Gründer 6.27362815622501e-06 -▁Rechtsrahmen 6.27362815622501e-06 -▁clearer 6.27362815622501e-06 -▁noticed 6.27362815622501e-06 -▁railways 6.27362815622501e-06 -▁tangible 6.27362815622501e-06 -bles 6.2698397334253568e-06 -idity 6.2698397334253568e-06 -isa 6.2698397334253568e-06 -▁Ereignissen 6.2698397334253568e-06 -▁berufliche 6.2698397334253568e-06 -▁decreas 6.2698397334253568e-06 -▁pleas 6.2698397334253568e-06 -greifenden 6.2660513106257039e-06 -orben 6.2660513106257039e-06 -▁arrested 6.2660513106257039e-06 -▁desper 6.2660513106257039e-06 -▁economics 6.2660513106257039e-06 -▁flags 6.2660513106257039e-06 -▁obtaining 6.2660513106257039e-06 -loser 6.262262887826051e-06 -otype 6.262262887826051e-06 -▁Besonderheiten 6.262262887826051e-06 -▁Komplettpreise 6.262262887826051e-06 -▁carrier 6.262262887826051e-06 -▁cinema 6.262262887826051e-06 -▁competitors 6.262262887826051e-06 -▁demanded 6.262262887826051e-06 -▁gepr 6.262262887826051e-06 -▁Annual 6.2584744650263981e-06 -▁Monitoring 6.2584744650263981e-06 -▁Pakt 6.2584744650263981e-06 -▁Way 6.2584744650263981e-06 -▁durchsetzen 6.2584744650263981e-06 -▁refurbished 6.2584744650263981e-06 -listen 6.2546860422267452e-06 -warten 6.2546860422267452e-06 -▁Analysen 6.2546860422267452e-06 -▁Directors 6.2546860422267452e-06 -▁Fehlen 6.2546860422267452e-06 -▁Lande 6.2546860422267452e-06 -▁NT 6.2546860422267452e-06 -▁Reader 6.2546860422267452e-06 -▁S.A. 6.2508976194270923e-06 -▁SAT 6.2508976194270923e-06 -▁Schienen 6.2508976194270923e-06 -▁Zit 6.2508976194270923e-06 -▁benchmark 6.2508976194270923e-06 -▁eating 6.2508976194270923e-06 -▁influenced 6.2508976194270923e-06 -▁roughly 6.2508976194270923e-06 -ernt 6.24710919662744e-06 -gab 6.24710919662744e-06 -inas 6.24710919662744e-06 -ssektor 6.24710919662744e-06 -▁Elite 6.24710919662744e-06 -▁Randlage 6.24710919662744e-06 -▁Senior 6.24710919662744e-06 -▁Spek 6.24710919662744e-06 -▁Zusammenbruch 6.24710919662744e-06 -▁ausb 6.24710919662744e-06 -▁erfre 6.24710919662744e-06 -▁estimates 6.24710919662744e-06 -▁interven 6.24710919662744e-06 -▁rose 6.24710919662744e-06 -oh 6.2433207738277874e-06 -▁Imperial 6.2433207738277874e-06 -▁Internetseite 6.2433207738277874e-06 -▁abzielen 6.2433207738277874e-06 -▁bescheiden 6.2433207738277874e-06 -▁geg 6.2433207738277874e-06 -▁independently 6.2433207738277874e-06 -▁notification 6.2433207738277874e-06 -▁zerstören 6.2433207738277874e-06 -GH 6.2395323510281345e-06 -▁EU-Ebene 6.2395323510281345e-06 -▁doppelt 6.2395323510281345e-06 -▁fing 6.2395323510281345e-06 -▁merg 6.2395323510281345e-06 -▁personenbezogenen 6.2395323510281345e-06 -▁rum 6.2395323510281345e-06 -▁unterliegt 6.2395323510281345e-06 -▁venture 6.2395323510281345e-06 -▁verfüg 6.2395323510281345e-06 -69 6.2357439282284816e-06 -Con 6.2357439282284816e-06 -holung 6.2357439282284816e-06 -▁Hunger 6.2357439282284816e-06 -▁Pul 6.2357439282284816e-06 -▁Zwangs 6.2357439282284816e-06 -▁instant 6.2357439282284816e-06 -▁monthly 6.2357439282284816e-06 -▁schlage 6.2357439282284816e-06 -▁unlikely 6.2357439282284816e-06 -▁EVP 6.2319555054288287e-06 -▁Einfluß 6.2319555054288287e-06 -▁Pant 6.2319555054288287e-06 -▁Vi 6.2319555054288287e-06 -▁gep 6.2319555054288287e-06 -▁unreal 6.2319555054288287e-06 -eff 6.2281670826291758e-06 -fertigt 6.2281670826291758e-06 -gemein 6.2281670826291758e-06 -grenz 6.2281670826291758e-06 -iances 6.2281670826291758e-06 -rada 6.2281670826291758e-06 -schieden 6.2281670826291758e-06 -▁Komplex 6.2281670826291758e-06 -▁bor 6.2281670826291758e-06 -▁elabor 6.2281670826291758e-06 -▁trademark 6.2281670826291758e-06 --Verb 6.2243786598295229e-06 -▁bull 6.2243786598295229e-06 -▁diesmal 6.2243786598295229e-06 -▁za 6.2243786598295229e-06 -▁Bass 6.2205902370298709e-06 -▁EURO 6.2205902370298709e-06 -▁Freien 6.2205902370298709e-06 -▁Pur 6.2205902370298709e-06 -▁Reb 6.2205902370298709e-06 -▁Tatsachen 6.2205902370298709e-06 -▁distinguished 6.2205902370298709e-06 -▁distrib 6.2205902370298709e-06 -▁gleicher 6.2205902370298709e-06 -▁verstoßen 6.2205902370298709e-06 -ensystem 6.216801814230218e-06 -ützte 6.216801814230218e-06 -▁Krem 6.216801814230218e-06 -▁Much 6.216801814230218e-06 -▁Opfern 6.216801814230218e-06 -▁gekauft 6.216801814230218e-06 -▁irgendeiner 6.216801814230218e-06 -▁missed 6.216801814230218e-06 -▁permits 6.216801814230218e-06 -frau 6.2130133914305651e-06 -ometr 6.2130133914305651e-06 -▁1973 6.2130133914305651e-06 -▁Auseinandersetzung 6.2130133914305651e-06 -▁Merc 6.2130133914305651e-06 -▁Peking 6.2130133914305651e-06 -▁ambitions 6.2130133914305651e-06 -▁appart 6.2130133914305651e-06 -▁fires 6.2130133914305651e-06 -▁geopolit 6.2130133914305651e-06 -▁storm 6.2130133914305651e-06 -▁summar 6.2130133914305651e-06 -enses 6.2092249686309122e-06 -▁Bremen 6.2092249686309122e-06 -▁Hierbei 6.2092249686309122e-06 -▁Ot 6.2092249686309122e-06 -▁RS 6.2092249686309122e-06 -▁SOL 6.2092249686309122e-06 -▁Sü 6.2092249686309122e-06 -▁Theatre 6.2092249686309122e-06 -▁Transit 6.2092249686309122e-06 -▁gathered 6.2092249686309122e-06 -▁intoler 6.2092249686309122e-06 -▁reine 6.2092249686309122e-06 -▁unilateral 6.2092249686309122e-06 -▁wen 6.2092249686309122e-06 -atories 6.2054365458312593e-06 -bestimmung 6.2054365458312593e-06 -▁Did 6.2054365458312593e-06 -▁IB 6.2054365458312593e-06 -▁Split 6.2054365458312593e-06 -▁Tendenz 6.2054365458312593e-06 -▁malerischen 6.2054365458312593e-06 -▁sink 6.2054365458312593e-06 -▁vorange 6.2054365458312593e-06 -hängt 6.2016481230316064e-06 -igration 6.2016481230316064e-06 -mor 6.2016481230316064e-06 -oso 6.2016481230316064e-06 -▁bonds 6.2016481230316064e-06 -adi 6.1978597002319535e-06 -anners 6.1978597002319535e-06 -lined 6.1978597002319535e-06 -orientierte 6.1978597002319535e-06 -▁Namens 6.1978597002319535e-06 -▁forums 6.1978597002319535e-06 -▁mailing 6.1978597002319535e-06 -▁renew 6.1978597002319535e-06 -▁revol 6.1978597002319535e-06 -▁stehende 6.1978597002319535e-06 -▁venue 6.1978597002319535e-06 -▁zumal 6.1978597002319535e-06 -▁zusammenzuarbeiten 6.1978597002319535e-06 -bte 6.1940712774323006e-06 -▁1920 6.1940712774323006e-06 -▁Dekor 6.1940712774323006e-06 -▁Königs 6.1940712774323006e-06 -▁Rental 6.1940712774323006e-06 -▁Sud 6.1940712774323006e-06 -▁beabsicht 6.1940712774323006e-06 -▁besetzt 6.1940712774323006e-06 -▁capability 6.1940712774323006e-06 -▁databases 6.1940712774323006e-06 -▁errichten 6.1940712774323006e-06 -lacier 6.1902828546326486e-06 -ulationen 6.1902828546326486e-06 -winkel 6.1902828546326486e-06 -▁CAD 6.1902828546326486e-06 -▁Gegenstände 6.1902828546326486e-06 -▁Verfass 6.1902828546326486e-06 -▁ausgewogenen 6.1902828546326486e-06 -▁ax 6.1902828546326486e-06 -▁einzurichten 6.1902828546326486e-06 -▁verleiht 6.1902828546326486e-06 -ega 6.1864944318329957e-06 -html 6.1864944318329957e-06 -▁Finanzmärkte 6.1864944318329957e-06 -▁Treiber 6.1864944318329957e-06 -▁bow 6.1864944318329957e-06 -▁energies 6.1864944318329957e-06 -▁hierarch 6.1864944318329957e-06 -▁profiles 6.1864944318329957e-06 -ammen 6.1827060090333428e-06 -zustand 6.1827060090333428e-06 -▁Fundament 6.1827060090333428e-06 -▁Saf 6.1827060090333428e-06 -▁Wol 6.1827060090333428e-06 -▁ausgew 6.1827060090333428e-06 -▁authorisation 6.1827060090333428e-06 -▁employee 6.1827060090333428e-06 -▁erschienen 6.1827060090333428e-06 -▁laboratory 6.1827060090333428e-06 -▁sat 6.1827060090333428e-06 -arb 6.17891758623369e-06 -mess 6.17891758623369e-06 -ministerium 6.17891758623369e-06 -runde 6.17891758623369e-06 -▁1983 6.17891758623369e-06 -▁F. 6.17891758623369e-06 -▁Fen 6.17891758623369e-06 -▁Gelegenheiten 6.17891758623369e-06 -▁belegt 6.17891758623369e-06 -▁cope 6.17891758623369e-06 -▁inspiration 6.17891758623369e-06 -▁intim 6.17891758623369e-06 -▁nördlichen 6.17891758623369e-06 -▁produz 6.17891758623369e-06 -▁writ 6.17891758623369e-06 -bilanz 6.175129163434037e-06 -flüge 6.175129163434037e-06 -sprachigen 6.175129163434037e-06 -ünder 6.175129163434037e-06 -▁Anreiz 6.175129163434037e-06 -▁Ky 6.175129163434037e-06 -▁Tagungs 6.175129163434037e-06 -▁geladen 6.175129163434037e-06 -▁nowadays 6.175129163434037e-06 -abilities 6.1713407406343841e-06 -spiele 6.1713407406343841e-06 -▁Bah 6.1713407406343841e-06 -▁Beendigung 6.1713407406343841e-06 -▁Weltmarkt 6.1713407406343841e-06 -▁interinstitution 6.1713407406343841e-06 -▁weißen 6.1713407406343841e-06 -ettet 6.1675523178347312e-06 -fähiger 6.1675523178347312e-06 -uest 6.1675523178347312e-06 -ă 6.1675523178347312e-06 -▁89 6.1675523178347312e-06 -▁Entschließungen 6.1675523178347312e-06 -▁Ll 6.1675523178347312e-06 -▁Rich 6.1675523178347312e-06 -▁Segel 6.1675523178347312e-06 -▁investigations 6.1675523178347312e-06 -▁natur 6.1675523178347312e-06 -amerikanischen 6.1637638950350784e-06 -oba 6.1637638950350784e-06 -▁Partnerschaften 6.1637638950350784e-06 -▁Virgin 6.1637638950350784e-06 -▁deny 6.1637638950350784e-06 -▁differently 6.1637638950350784e-06 -▁palace 6.1637638950350784e-06 -▁rasche 6.1637638950350784e-06 -▁rescue 6.1637638950350784e-06 -▁stärkeren 6.1637638950350784e-06 -aining 6.1599754722354263e-06 -▁Formel 6.1599754722354263e-06 -▁Jet 6.1599754722354263e-06 -▁Kommissare 6.1599754722354263e-06 -▁Nar 6.1599754722354263e-06 -▁Stattdessen 6.1599754722354263e-06 -▁Verona 6.1599754722354263e-06 -▁aufrechterhalten 6.1599754722354263e-06 -▁jud 6.1599754722354263e-06 -▁musicians 6.1599754722354263e-06 -▁unverb 6.1599754722354263e-06 -▁vielfältige 6.1599754722354263e-06 -azione 6.1561870494357734e-06 -estern 6.1561870494357734e-06 -ißen 6.1561870494357734e-06 -▁Kloster 6.1561870494357734e-06 -▁furthermore 6.1561870494357734e-06 -▁grundlegend 6.1561870494357734e-06 -▁labels 6.1561870494357734e-06 -▁más 6.1561870494357734e-06 -▁sustain 6.1561870494357734e-06 -▁sympath 6.1561870494357734e-06 -▁vorsichtig 6.1561870494357734e-06 -osse 6.1523986266361205e-06 -▁69 6.1523986266361205e-06 -▁Erika 6.1523986266361205e-06 -▁Zahlreiche 6.1523986266361205e-06 -▁dun 6.1523986266361205e-06 -▁startet 6.1523986266361205e-06 -ls 6.1486102038364676e-06 -troll 6.1486102038364676e-06 -▁Deine 6.1486102038364676e-06 -▁Torr 6.1486102038364676e-06 -▁Verzögerungen 6.1486102038364676e-06 -▁acqu 6.1486102038364676e-06 -▁deemed 6.1486102038364676e-06 -▁japanischen 6.1486102038364676e-06 -▁stakeholders 6.1486102038364676e-06 -▁uncertain 6.1486102038364676e-06 --comp 6.1448217810368147e-06 -gelegten 6.1448217810368147e-06 -illion 6.1448217810368147e-06 -▁1974 6.1448217810368147e-06 -▁Base 6.1448217810368147e-06 -▁Estonia 6.1448217810368147e-06 -▁Industriel 6.1448217810368147e-06 -▁Mittag 6.1448217810368147e-06 -▁hectares 6.1448217810368147e-06 -▁klarer 6.1448217810368147e-06 -▁translate 6.1448217810368147e-06 -äuter 6.1410333582371619e-06 -▁3-star 6.1410333582371619e-06 -▁comforts 6.1410333582371619e-06 -▁dangers 6.1410333582371619e-06 -▁removing 6.1410333582371619e-06 -brä 6.137244935437509e-06 -lings 6.137244935437509e-06 -▁4-Sterne-Hotel 6.137244935437509e-06 -▁Ankündigung 6.137244935437509e-06 -▁Vir 6.137244935437509e-06 -▁besticht 6.137244935437509e-06 -▁gäbe 6.137244935437509e-06 -▁vierten 6.137244935437509e-06 -omer 6.1334565126378561e-06 -würdige 6.1334565126378561e-06 -▁Genf 6.1334565126378561e-06 -▁Later 6.1334565126378561e-06 -▁Rechn 6.1334565126378561e-06 -▁Sorgfalt 6.1334565126378561e-06 -▁liv 6.1334565126378561e-06 -▁preventive 6.1334565126378561e-06 -▁Ärzte 6.1334565126378561e-06 -49 6.129668089838204e-06 -uffer 6.129668089838204e-06 -▁Muss 6.129668089838204e-06 -▁steep 6.129668089838204e-06 -▁Graph 6.1258796670385511e-06 -▁Inneres 6.1258796670385511e-06 -▁Jugoslawien 6.1258796670385511e-06 -▁Princ 6.1258796670385511e-06 -▁execut 6.1258796670385511e-06 -▁tempor 6.1258796670385511e-06 -▁wünschenswert 6.1258796670385511e-06 --Ger 6.1220912442388982e-06 -icating 6.1220912442388982e-06 -▁Grab 6.1220912442388982e-06 -▁bereitzustellen 6.1220912442388982e-06 -▁mount 6.1220912442388982e-06 -▁transmitted 6.1220912442388982e-06 -.09 6.1183028214392453e-06 -ipper 6.1183028214392453e-06 -jähriger 6.1183028214392453e-06 -uly 6.1183028214392453e-06 -▁Cop 6.1183028214392453e-06 -▁authorised 6.1183028214392453e-06 -▁fruits 6.1183028214392453e-06 -▁substant 6.1183028214392453e-06 -auten 6.1145143986395925e-06 -ii 6.1145143986395925e-06 -kommens 6.1145143986395925e-06 -uktur 6.1145143986395925e-06 -▁Anzeigen 6.1145143986395925e-06 -▁Econom 6.1145143986395925e-06 -▁Freie 6.1145143986395925e-06 -▁anbietet 6.1145143986395925e-06 -▁subscribe 6.1145143986395925e-06 -infrastruktur 6.1107259758399396e-06 -lecht 6.1107259758399396e-06 -passen 6.1107259758399396e-06 -tensive 6.1107259758399396e-06 -▁Absol 6.1107259758399396e-06 -▁Arbeitsweise 6.1107259758399396e-06 -▁Delta 6.1107259758399396e-06 -▁Vorbereitungen 6.1107259758399396e-06 -▁apolog 6.1107259758399396e-06 -▁einmalige 6.1107259758399396e-06 -▁observations 6.1107259758399396e-06 -▁stringent 6.1107259758399396e-06 -▁untergraben 6.1107259758399396e-06 --Spe 6.1069375530402867e-06 -edition 6.1069375530402867e-06 -herz 6.1069375530402867e-06 -▁15th 6.1069375530402867e-06 -▁Auslands 6.1069375530402867e-06 -▁Begriffe 6.1069375530402867e-06 -▁cabin 6.1069375530402867e-06 -▁fed 6.1069375530402867e-06 -▁partially 6.1069375530402867e-06 -▁Frattini 6.1031491302406338e-06 -▁Wassers 6.1031491302406338e-06 -▁gebilligt 6.1031491302406338e-06 -▁intergovernmental 6.1031491302406338e-06 -▁kilometers 6.1031491302406338e-06 -▁kunden 6.1031491302406338e-06 -▁married 6.1031491302406338e-06 -hilfen 6.0993607074409817e-06 -iera 6.0993607074409817e-06 -mögliche 6.0993607074409817e-06 -ogo 6.0993607074409817e-06 -ption 6.0993607074409817e-06 -ström 6.0993607074409817e-06 -ussten 6.0993607074409817e-06 -weich 6.0993607074409817e-06 -zza 6.0993607074409817e-06 -▁Basil 6.0993607074409817e-06 -▁Ele 6.0993607074409817e-06 -▁Etikett 6.0993607074409817e-06 -▁Garda 6.0993607074409817e-06 -▁Mugabe 6.0993607074409817e-06 -▁Them 6.0993607074409817e-06 -▁schnelles 6.0993607074409817e-06 -merzen 6.0955722846413288e-06 -oil 6.0955722846413288e-06 -▁Abd 6.0955722846413288e-06 -▁awards 6.0955722846413288e-06 -▁kons 6.0955722846413288e-06 -▁neueste 6.0955722846413288e-06 -▁wedding 6.0955722846413288e-06 -aked 6.091783861841676e-06 -sprechend 6.091783861841676e-06 -ulierung 6.091783861841676e-06 -▁Multimedia 6.091783861841676e-06 -▁SIS 6.091783861841676e-06 -▁assessments 6.091783861841676e-06 -▁cycling 6.091783861841676e-06 -▁eur 6.091783861841676e-06 -▁freier 6.091783861841676e-06 -▁lens 6.091783861841676e-06 -▁prevail 6.091783861841676e-06 -▁sorgfält 6.091783861841676e-06 -▁vereinfacht 6.091783861841676e-06 -▁verschieben 6.091783861841676e-06 -▁vertrauen 6.091783861841676e-06 -▁winners 6.091783861841676e-06 -▁з 6.091783861841676e-06 -abstimmung 6.0879954390420231e-06 -char 6.0879954390420231e-06 -rov 6.0879954390420231e-06 -tech 6.0879954390420231e-06 -vielfalt 6.0879954390420231e-06 -▁EGF 6.0879954390420231e-06 -▁Mountains 6.0879954390420231e-06 -▁geholfen 6.0879954390420231e-06 -▁pregn 6.0879954390420231e-06 -▁speaks 6.0879954390420231e-06 -▁towels 6.0879954390420231e-06 -wirkung 6.08420701624237e-06 -▁Kommentar 6.08420701624237e-06 -▁SPA 6.08420701624237e-06 -▁Veget 6.08420701624237e-06 -▁designers 6.08420701624237e-06 -▁eg 6.08420701624237e-06 -▁gesellschaftliche 6.08420701624237e-06 -▁nutrition 6.08420701624237e-06 -▁ueber 6.08420701624237e-06 -▁zukünftig 6.08420701624237e-06 -erstoff 6.0804185934427173e-06 -usted 6.0804185934427173e-06 -▁Hollywood 6.0804185934427173e-06 -▁Tunisia 6.0804185934427173e-06 -▁ceram 6.0804185934427173e-06 -▁exquisite 6.0804185934427173e-06 -▁gefasst 6.0804185934427173e-06 -▁konstruktiven 6.0804185934427173e-06 -▁overt 6.0804185934427173e-06 -▁ris 6.0804185934427173e-06 -▁verschoben 6.0804185934427173e-06 -denk 6.0766301706430644e-06 -estro 6.0766301706430644e-06 -eur 6.0766301706430644e-06 -verbot 6.0766301706430644e-06 -ächer 6.0766301706430644e-06 -ängst 6.0766301706430644e-06 -äts 6.0766301706430644e-06 -▁Reihenfolge 6.0766301706430644e-06 -▁Return 6.0766301706430644e-06 -▁Stabilitäts- 6.0766301706430644e-06 -▁besides 6.0766301706430644e-06 -39 6.0728417478434115e-06 -offe 6.0728417478434115e-06 -rüst 6.0728417478434115e-06 -undet 6.0728417478434115e-06 -▁Implementierung 6.0728417478434115e-06 -▁Pierre 6.0728417478434115e-06 -▁Regimes 6.0728417478434115e-06 -▁capacities 6.0728417478434115e-06 -▁dominated 6.0728417478434115e-06 -▁ot 6.0728417478434115e-06 -▁utility 6.0728417478434115e-06 -artigen 6.0690533250437595e-06 -isations 6.0690533250437595e-06 -rence 6.0690533250437595e-06 -▁champions 6.0690533250437595e-06 -iences 6.0652649022441066e-06 -kehren 6.0652649022441066e-06 -vorteil 6.0652649022441066e-06 -▁Christdemokraten 6.0652649022441066e-06 -▁Straßenverkehr 6.0652649022441066e-06 -▁Wagen 6.0652649022441066e-06 -▁appreciation 6.0652649022441066e-06 -▁beliebten 6.0652649022441066e-06 -▁nützliche 6.0652649022441066e-06 -▁sick 6.0652649022441066e-06 --Tr 6.0614764794444537e-06 -writ 6.0614764794444537e-06 -▁Flüge 6.0614764794444537e-06 -▁Fotograf 6.0614764794444537e-06 -▁Konstrukt 6.0614764794444537e-06 -▁Tokyo 6.0614764794444537e-06 -▁boundaries 6.0614764794444537e-06 -▁drängen 6.0614764794444537e-06 -▁führten 6.0614764794444537e-06 -▁verschwinden 6.0614764794444537e-06 -meldungen 6.0576880566448008e-06 -oding 6.0576880566448008e-06 -▁Fix 6.0576880566448008e-06 -▁Tener 6.0576880566448008e-06 -▁Vorsitzende 6.0576880566448008e-06 -▁begreifen 6.0576880566448008e-06 -▁breites 6.0576880566448008e-06 -▁habt 6.0576880566448008e-06 -▁strengen 6.0576880566448008e-06 -▁tone 6.0576880566448008e-06 -▁vorgeschrieben 6.0576880566448008e-06 -aze 6.0538996338451479e-06 -kat 6.0538996338451479e-06 -stabilität 6.0538996338451479e-06 -zeitraum 6.0538996338451479e-06 -▁Halbinsel 6.0538996338451479e-06 -▁Innsbruck 6.0538996338451479e-06 -▁Moldova 6.0538996338451479e-06 -▁benannt 6.0538996338451479e-06 -▁catering 6.0538996338451479e-06 -▁foster 6.0538996338451479e-06 -▁frem 6.0538996338451479e-06 -▁gesunde 6.0538996338451479e-06 -▁porn 6.0538996338451479e-06 -errt 6.050111211045495e-06 -ipes 6.050111211045495e-06 -onze 6.050111211045495e-06 -quenz 6.050111211045495e-06 -▁Dial 6.050111211045495e-06 -▁ECHO 6.050111211045495e-06 -▁acted 6.050111211045495e-06 -▁harte 6.050111211045495e-06 -▁numer 6.050111211045495e-06 -▁younger 6.050111211045495e-06 -boards 6.0463227882458421e-06 -ucing 6.0463227882458421e-06 -was 6.0463227882458421e-06 -▁CN 6.0463227882458421e-06 -▁Defizit 6.0463227882458421e-06 -▁Lac 6.0463227882458421e-06 -▁RAM 6.0463227882458421e-06 -▁Rather 6.0463227882458421e-06 -▁countless 6.0463227882458421e-06 -▁junger 6.0463227882458421e-06 -▁pride 6.0463227882458421e-06 -▁spect 6.0463227882458421e-06 -▁summary 6.0463227882458421e-06 -▁zugeschnitten 6.0463227882458421e-06 -case 6.04253436544619e-06 -erous 6.04253436544619e-06 -istics 6.04253436544619e-06 -operation 6.04253436544619e-06 -partei 6.04253436544619e-06 -▁Jagd 6.04253436544619e-06 -▁Motors 6.04253436544619e-06 -▁Reichtum 6.04253436544619e-06 -▁loading 6.04253436544619e-06 -▁prol 6.04253436544619e-06 -▁supervisory 6.04253436544619e-06 -neys 6.0387459426465372e-06 -▁Bewerb 6.0387459426465372e-06 -▁Vorwand 6.0387459426465372e-06 -▁notwend 6.0387459426465372e-06 -▁promotes 6.0387459426465372e-06 -▁werte 6.0387459426465372e-06 -vet 6.0349575198468843e-06 -wohl 6.0349575198468843e-06 -xis 6.0349575198468843e-06 -öpfe 6.0349575198468843e-06 -▁Allow 6.0349575198468843e-06 -▁Between 6.0349575198468843e-06 -▁Facebook 6.0349575198468843e-06 -▁Impulse 6.0349575198468843e-06 -▁Quadrat 6.0349575198468843e-06 -▁ermitteln 6.0349575198468843e-06 -▁obl 6.0349575198468843e-06 -▁unanimous 6.0349575198468843e-06 -wesens 6.0311690970472314e-06 -▁Schwer 6.0311690970472314e-06 -▁biologischen 6.0311690970472314e-06 -▁indigenous 6.0311690970472314e-06 -ourcing 6.0273806742475785e-06 -ле 6.0273806742475785e-06 -▁Abgesehen 6.0273806742475785e-06 -▁Forst 6.0273806742475785e-06 -▁Gastfreundschaft 6.0273806742475785e-06 -▁Integrität 6.0273806742475785e-06 -▁Seeverkehr 6.0273806742475785e-06 -▁afterwards 6.0273806742475785e-06 -▁analysiert 6.0273806742475785e-06 -▁beat 6.0273806742475785e-06 -▁globalization 6.0273806742475785e-06 -▁plugin 6.0273806742475785e-06 -▁türkische 6.0273806742475785e-06 -isierungen 6.0235922514479256e-06 -rache 6.0235922514479256e-06 -▁Erfordern 6.0235922514479256e-06 -▁Folglich 6.0235922514479256e-06 -▁bedeutend 6.0235922514479256e-06 -▁belgischen 6.0235922514479256e-06 -▁feelings 6.0235922514479256e-06 -▁vineyards 6.0235922514479256e-06 -▁worauf 6.0235922514479256e-06 -KE 6.0198038286482727e-06 -arbeitungs 6.0198038286482727e-06 -days 6.0198038286482727e-06 -parks 6.0198038286482727e-06 -▁Beachten 6.0198038286482727e-06 -▁Durchschnitt 6.0198038286482727e-06 -▁Laeken 6.0198038286482727e-06 -▁Metal 6.0198038286482727e-06 -▁collections 6.0198038286482727e-06 -▁extends 6.0198038286482727e-06 -▁struck 6.0198038286482727e-06 -▁tournament 6.0198038286482727e-06 -▁verliert 6.0198038286482727e-06 -üge 6.01601540584862e-06 -▁Schalt 6.01601540584862e-06 -▁accomod 6.01601540584862e-06 -▁lect 6.01601540584862e-06 -▁margin 6.01601540584862e-06 -▁mehrerer 6.01601540584862e-06 -▁safely 6.01601540584862e-06 -amped 6.0122269830489678e-06 -artig 6.0122269830489678e-06 -bezogene 6.0122269830489678e-06 -▁Expert 6.0122269830489678e-06 -▁Golfplatz 6.0122269830489678e-06 -▁Johannes 6.0122269830489678e-06 -▁Kof 6.0122269830489678e-06 -▁Masse 6.0122269830489678e-06 -▁Mic 6.0122269830489678e-06 -▁Pel 6.0122269830489678e-06 -▁Rimini 6.0122269830489678e-06 -▁getr 6.0122269830489678e-06 -▁luxuriöse 6.0122269830489678e-06 -▁seaside 6.0122269830489678e-06 -chie 6.0084385602493149e-06 -cm 6.0084385602493149e-06 -fang 6.0084385602493149e-06 -gerechte 6.0084385602493149e-06 -ship 6.0084385602493149e-06 -untern 6.0084385602493149e-06 -yard 6.0084385602493149e-06 -▁Sing 6.0084385602493149e-06 -▁ausged 6.0084385602493149e-06 -▁consulted 6.0084385602493149e-06 -▁müsse 6.0084385602493149e-06 -▁q 6.0084385602493149e-06 -▁tiefer 6.0084385602493149e-06 -ICH 6.004650137449662e-06 -aux 6.004650137449662e-06 -bra 6.004650137449662e-06 -still 6.004650137449662e-06 -tegration 6.004650137449662e-06 -▁Appart 6.004650137449662e-06 -▁Thun 6.004650137449662e-06 -▁deck 6.004650137449662e-06 -▁deepen 6.004650137449662e-06 -▁pose 6.004650137449662e-06 -▁revers 6.004650137449662e-06 -▁sicht 6.004650137449662e-06 -▁unmittelbaren 6.004650137449662e-06 -ALL 6.0008617146500091e-06 -ahrer 6.0008617146500091e-06 -interessen 6.0008617146500091e-06 -stall 6.0008617146500091e-06 -▁Laman 6.0008617146500091e-06 -▁Mailand 6.0008617146500091e-06 -▁som 6.0008617146500091e-06 -erber 5.9970732918503562e-06 -itted 5.9970732918503562e-06 -ools 5.9970732918503562e-06 -▁Ax 5.9970732918503562e-06 -▁Jahrzehnte 5.9970732918503562e-06 -▁fotograf 5.9970732918503562e-06 -▁on-line 5.9970732918503562e-06 -onna 5.9932848690507033e-06 -▁Anim 5.9932848690507033e-06 -▁DNA 5.9932848690507033e-06 -▁Dal 5.9932848690507033e-06 -▁Hierzu 5.9932848690507033e-06 -▁Jens 5.9932848690507033e-06 -▁Leonardo 5.9932848690507033e-06 -▁Trennung 5.9932848690507033e-06 -▁Vert 5.9932848690507033e-06 -▁Webs 5.9932848690507033e-06 -▁revenues 5.9932848690507033e-06 -▁versprochen 5.9932848690507033e-06 -entral 5.9894964462510504e-06 -person 5.9894964462510504e-06 -öpfung 5.9894964462510504e-06 -▁Frei 5.9894964462510504e-06 -▁Herzeg 5.9894964462510504e-06 -▁bears 5.9894964462510504e-06 -▁dismant 5.9894964462510504e-06 -▁reaches 5.9894964462510504e-06 -▁vorne 5.9894964462510504e-06 -asses 5.9857080234513975e-06 -oca 5.9857080234513975e-06 -▁Bananen 5.9857080234513975e-06 -▁Beweise 5.9857080234513975e-06 -▁Ged 5.9857080234513975e-06 -▁Krankenhaus 5.9857080234513975e-06 -▁Memory 5.9857080234513975e-06 -▁Zwecken 5.9857080234513975e-06 -▁tausend 5.9857080234513975e-06 -iben 5.9819196006517455e-06 -ipts 5.9819196006517455e-06 -ň 5.9819196006517455e-06 -▁Care 5.9819196006517455e-06 -▁Menschenhandel 5.9819196006517455e-06 -▁lokal 5.9819196006517455e-06 -▁verknüpft 5.9819196006517455e-06 -▁vorgegeben 5.9819196006517455e-06 -adr 5.9781311778520926e-06 -gängen 5.9781311778520926e-06 -▁Co. 5.9781311778520926e-06 -▁Erleben 5.9781311778520926e-06 -▁Palast 5.9781311778520926e-06 -▁Petr 5.9781311778520926e-06 -▁Verst 5.9781311778520926e-06 -▁Zut 5.9781311778520926e-06 -▁albeit 5.9781311778520926e-06 -▁audit 5.9781311778520926e-06 -▁ausgebaut 5.9781311778520926e-06 -▁auto 5.9781311778520926e-06 -▁invested 5.9781311778520926e-06 -▁müßte 5.9781311778520926e-06 -▁ums 5.9781311778520926e-06 -▁ungarischen 5.9781311778520926e-06 -ameras 5.97434275505244e-06 -fahrung 5.97434275505244e-06 -ändigen 5.97434275505244e-06 -▁Große 5.97434275505244e-06 -▁Hochschul 5.97434275505244e-06 -ampagne 5.9705543322527868e-06 -bahnhof 5.9705543322527868e-06 -cas 5.9705543322527868e-06 -ransfer 5.9705543322527868e-06 -uent 5.9705543322527868e-06 -▁Awards 5.9705543322527868e-06 -▁Distrib 5.9705543322527868e-06 -▁Lama 5.9705543322527868e-06 -▁Ultra 5.9705543322527868e-06 -▁conviction 5.9705543322527868e-06 -zern 5.9667659094531339e-06 -▁Aufstieg 5.9667659094531339e-06 -▁Grafiken 5.9667659094531339e-06 -▁Shar 5.9667659094531339e-06 -▁Silver 5.9667659094531339e-06 -▁Südtirol 5.9667659094531339e-06 -▁defense 5.9667659094531339e-06 -▁Kompon 5.962977486653481e-06 -▁Präsidium 5.962977486653481e-06 -▁Versicher 5.962977486653481e-06 -▁weitergegeben 5.962977486653481e-06 -▁wiederher 5.962977486653481e-06 --Gipfel 5.9591890638538281e-06 -cam 5.9591890638538281e-06 -fäl 5.9591890638538281e-06 -kommission 5.9591890638538281e-06 -▁Acr 5.9591890638538281e-06 -▁Albanien 5.9591890638538281e-06 -▁Kommando 5.9591890638538281e-06 -▁Oxford 5.9591890638538281e-06 -▁abol 5.9591890638538281e-06 -▁feiern 5.9591890638538281e-06 -▁spell 5.9591890638538281e-06 -▁stir 5.9591890638538281e-06 -irn 5.9554006410541752e-06 -▁Campingplatz 5.9554006410541752e-06 -▁cup 5.9554006410541752e-06 -▁insight 5.9554006410541752e-06 -▁unterstreicht 5.9554006410541752e-06 -▁unvergesslichen 5.9554006410541752e-06 -ieger 5.9516122182545232e-06 -mung 5.9516122182545232e-06 -▁Armuts 5.9516122182545232e-06 -▁Jam 5.9516122182545232e-06 -▁Loch 5.9516122182545232e-06 -▁Sad 5.9516122182545232e-06 -▁Unions 5.9516122182545232e-06 -▁hole 5.9516122182545232e-06 --v 5.94782379545487e-06 -PO 5.94782379545487e-06 -install 5.94782379545487e-06 -▁Exhibition 5.94782379545487e-06 -▁Pract 5.94782379545487e-06 -▁climbing 5.94782379545487e-06 -▁familien 5.94782379545487e-06 -▁mehrmals 5.94782379545487e-06 -▁versetzt 5.94782379545487e-06 -otheken 5.9440353726552174e-06 -▁Benz 5.9440353726552174e-06 -▁Supervisory 5.9440353726552174e-06 -▁Toil 5.9440353726552174e-06 -▁ages 5.9440353726552174e-06 -CR 5.9402469498555645e-06 -änglichkeit 5.9402469498555645e-06 -▁ALL 5.9402469498555645e-06 -▁Burn 5.9402469498555645e-06 -▁DVDs 5.9402469498555645e-06 -▁dauerhafte 5.9402469498555645e-06 -▁ignorieren 5.9402469498555645e-06 -▁luck 5.9402469498555645e-06 -▁ties 5.9402469498555645e-06 -▁warmen 5.9402469498555645e-06 -▁weigh 5.9402469498555645e-06 -beitrag 5.9364585270559116e-06 -cement 5.9364585270559116e-06 -unn 5.9364585270559116e-06 -▁Zweif 5.9364585270559116e-06 -▁entspann 5.9364585270559116e-06 -▁motivated 5.9364585270559116e-06 -▁nachvoll 5.9364585270559116e-06 -▁query 5.9364585270559116e-06 -▁secured 5.9364585270559116e-06 -iele 5.9326701042562587e-06 -sspiel 5.9326701042562587e-06 -utreten 5.9326701042562587e-06 -öcke 5.9326701042562587e-06 -▁Letz 5.9326701042562587e-06 -▁Rindfleisch 5.9326701042562587e-06 -▁adidas 5.9326701042562587e-06 -▁anstelle 5.9326701042562587e-06 -▁dive 5.9326701042562587e-06 -▁effektiven 5.9326701042562587e-06 -▁sozusagen 5.9326701042562587e-06 -staatlichen 5.9288816814566059e-06 -ädern 5.9288816814566059e-06 -▁Regarding 5.9288816814566059e-06 -▁Simply 5.9288816814566059e-06 -▁Standpunkte 5.9288816814566059e-06 -▁dictatorship 5.9288816814566059e-06 -▁frischen 5.9288816814566059e-06 -▁reconciliation 5.9288816814566059e-06 -▁respons 5.9288816814566059e-06 -▁scenario 5.9288816814566059e-06 -▁sincerely 5.9288816814566059e-06 -▁verheer 5.9288816814566059e-06 -▁wenigsten 5.9288816814566059e-06 -oder 5.925093258656953e-06 -uda 5.925093258656953e-06 -β 5.925093258656953e-06 -▁Ped 5.925093258656953e-06 -▁geklärt 5.925093258656953e-06 -▁genetically 5.925093258656953e-06 -▁haven 5.925093258656953e-06 -▁investing 5.925093258656953e-06 -▁mould 5.925093258656953e-06 -▁spann 5.925093258656953e-06 -hou 5.9213048358573009e-06 -▁Berlusconi 5.9213048358573009e-06 -▁Exporte 5.9213048358573009e-06 -▁Run 5.9213048358573009e-06 -▁Zugleich 5.9213048358573009e-06 -▁constraints 5.9213048358573009e-06 -▁systematisch 5.9213048358573009e-06 -▁untereinander 5.9213048358573009e-06 -idal 5.917516413057648e-06 -▁Allgemeine 5.917516413057648e-06 -▁Carlos 5.917516413057648e-06 -▁bereitstellen 5.917516413057648e-06 -▁clarification 5.917516413057648e-06 -▁dreams 5.917516413057648e-06 -▁gewann 5.917516413057648e-06 -▁gezielt 5.917516413057648e-06 -fined 5.9137279902579951e-06 -ölle 5.9137279902579951e-06 -▁Diesel 5.9137279902579951e-06 -▁Effekte 5.9137279902579951e-06 -▁adj 5.9137279902579951e-06 -▁cooling 5.9137279902579951e-06 -▁orange 5.9137279902579951e-06 -▁villas 5.9137279902579951e-06 -▁wash 5.9137279902579951e-06 -agers 5.9099395674583422e-06 -▁Ausb 5.9099395674583422e-06 -▁Ludwig 5.9099395674583422e-06 -▁Vermeidung 5.9099395674583422e-06 -▁WHO 5.9099395674583422e-06 -▁Watson 5.9099395674583422e-06 -▁atom 5.9099395674583422e-06 -▁colon 5.9099395674583422e-06 -▁zutiefst 5.9099395674583422e-06 -▁ü 5.9099395674583422e-06 -emit 5.9061511446586894e-06 -erve 5.9061511446586894e-06 -füllen 5.9061511446586894e-06 -pher 5.9061511446586894e-06 -preises 5.9061511446586894e-06 -zin 5.9061511446586894e-06 -zusehen 5.9061511446586894e-06 -▁Dry 5.9061511446586894e-06 -▁Fokus 5.9061511446586894e-06 -▁Grundfreiheiten 5.9061511446586894e-06 -▁Pharma 5.9061511446586894e-06 -▁ausgearbeitet 5.9061511446586894e-06 -▁besagt 5.9061511446586894e-06 -▁in-depth 5.9061511446586894e-06 -▁seminar 5.9061511446586894e-06 -▁zunehmen 5.9061511446586894e-06 -sky 5.9023627218590365e-06 -▁Frontex 5.9023627218590365e-06 -▁Legitimität 5.9023627218590365e-06 -▁Manchmal 5.9023627218590365e-06 -▁Materials 5.9023627218590365e-06 -▁Schwier 5.9023627218590365e-06 -▁Unterhaltungs 5.9023627218590365e-06 -▁abgeb 5.9023627218590365e-06 -▁bedeutendsten 5.9023627218590365e-06 -▁friedliche 5.9023627218590365e-06 -▁konsult 5.9023627218590365e-06 -▁neutr 5.9023627218590365e-06 -▁outs 5.9023627218590365e-06 -▁radiation 5.9023627218590365e-06 -sum 5.8985742990593836e-06 -▁Forscher 5.8985742990593836e-06 -▁Mozilla 5.8985742990593836e-06 -▁Prix 5.8985742990593836e-06 -▁V. 5.8985742990593836e-06 -▁außergewöhnliche 5.8985742990593836e-06 -▁gepf 5.8985742990593836e-06 -▁landwirtschaftliche 5.8985742990593836e-06 -▁mündliche 5.8985742990593836e-06 -▁Conn 5.8947858762597307e-06 -▁Converter 5.8947858762597307e-06 -▁athl 5.8947858762597307e-06 -▁dicht 5.8947858762597307e-06 -▁discipl 5.8947858762597307e-06 -▁eligible 5.8947858762597307e-06 -▁ont 5.8947858762597307e-06 -▁scenes 5.8947858762597307e-06 -▁schwerer 5.8947858762597307e-06 -▁uphold 5.8947858762597307e-06 -▁wozu 5.8947858762597307e-06 -anwalt 5.8909974534600786e-06 -▁Advent 5.8909974534600786e-06 -▁Defin 5.8909974534600786e-06 -▁Effektivität 5.8909974534600786e-06 -▁Kamin 5.8909974534600786e-06 -▁Kluft 5.8909974534600786e-06 -▁besseres 5.8909974534600786e-06 -▁korrigieren 5.8909974534600786e-06 -▁panels 5.8909974534600786e-06 -▁separation 5.8909974534600786e-06 -▁umgekehrt 5.8909974534600786e-06 -▁vice 5.8909974534600786e-06 -phan 5.8872090306604257e-06 -rin 5.8872090306604257e-06 -stärke 5.8872090306604257e-06 -ло 5.8872090306604257e-06 -▁Chi 5.8872090306604257e-06 -▁Dringlichkeit 5.8872090306604257e-06 -▁Erreichen 5.8872090306604257e-06 -▁Leistungsfähigkeit 5.8872090306604257e-06 -▁aufre 5.8872090306604257e-06 -▁bre 5.8872090306604257e-06 -▁graph 5.8872090306604257e-06 -▁lod 5.8872090306604257e-06 --pro 5.8834206078607728e-06 -Mail 5.8834206078607728e-06 -mund 5.8834206078607728e-06 -▁Arbeitskräfte 5.8834206078607728e-06 -▁Erzeug 5.8834206078607728e-06 -▁divide 5.8834206078607728e-06 -▁flaw 5.8834206078607728e-06 -▁orth 5.8834206078607728e-06 -▁pedestrian 5.8834206078607728e-06 -▁qualities 5.8834206078607728e-06 -▁Bert 5.87963218506112e-06 -▁Dolmet 5.87963218506112e-06 -▁Hinsichtlich 5.87963218506112e-06 -▁Informationsgesellschaft 5.87963218506112e-06 -▁Kok 5.87963218506112e-06 -▁decentral 5.87963218506112e-06 -▁elegance 5.87963218506112e-06 -▁helicop 5.87963218506112e-06 -▁jahr 5.87963218506112e-06 -▁reproduction 5.87963218506112e-06 -▁stimulate 5.87963218506112e-06 -▁wunderbare 5.87963218506112e-06 --Richtlinie 5.8758437622614671e-06 -▁Cannes 5.8758437622614671e-06 -▁E-Mails 5.8758437622614671e-06 -▁Galax 5.8758437622614671e-06 -▁Spielraum 5.8758437622614671e-06 -▁destabil 5.8758437622614671e-06 -▁genügt 5.8758437622614671e-06 -▁gründlich 5.8758437622614671e-06 -▁häufiger 5.8758437622614671e-06 -▁noisy 5.8758437622614671e-06 -▁realised 5.8758437622614671e-06 --bit 5.8720553394618142e-06 -mittag 5.8720553394618142e-06 -▁Lieblings 5.8720553394618142e-06 -▁Luftverkehrs 5.8720553394618142e-06 -▁Tir 5.8720553394618142e-06 -▁Vorgang 5.8720553394618142e-06 -▁Wahrscheinlichkeit 5.8720553394618142e-06 -▁bitter 5.8720553394618142e-06 -▁explanations 5.8720553394618142e-06 -▁grüne 5.8720553394618142e-06 -▁sac 5.8720553394618142e-06 -▁sta 5.8720553394618142e-06 -MB 5.8682669166621613e-06 -defin 5.8682669166621613e-06 -spolitischen 5.8682669166621613e-06 -systemen 5.8682669166621613e-06 -▁ASEAN 5.8682669166621613e-06 -▁Drive 5.8682669166621613e-06 -▁E. 5.8682669166621613e-06 -▁Krak 5.8682669166621613e-06 -▁currencies 5.8682669166621613e-06 -▁delayed 5.8682669166621613e-06 -▁gewählte 5.8682669166621613e-06 -▁suspension 5.8682669166621613e-06 -▁umf 5.8682669166621613e-06 -zens 5.8644784938625092e-06 -▁Energieversorgung 5.8644784938625092e-06 -▁Ganzes 5.8644784938625092e-06 -▁Lange 5.8644784938625092e-06 -▁damaging 5.8644784938625092e-06 -▁finishing 5.8644784938625092e-06 -▁justification 5.8644784938625092e-06 -▁somewhere 5.8644784938625092e-06 -77 5.8606900710628563e-06 -acity 5.8606900710628563e-06 -institutionen 5.8606900710628563e-06 -oppelt 5.8606900710628563e-06 -präsident 5.8606900710628563e-06 -rost 5.8606900710628563e-06 -▁Monopol 5.8606900710628563e-06 -▁Paradies 5.8606900710628563e-06 -▁durchgesetzt 5.8606900710628563e-06 -▁undermin 5.8606900710628563e-06 -▁wea 5.8606900710628563e-06 -▁Everest 5.8569016482632035e-06 -▁Label 5.8569016482632035e-06 -▁Zulassung 5.8569016482632035e-06 -▁coalition 5.8569016482632035e-06 -▁dogs 5.8569016482632035e-06 -▁leit 5.8569016482632035e-06 -▁washing 5.8569016482632035e-06 -mittlung 5.8531132254635506e-06 -ungsbereich 5.8531132254635506e-06 -▁Appro 5.8531132254635506e-06 -▁Impf 5.8531132254635506e-06 -▁Kopie 5.8531132254635506e-06 -▁Mitwirkung 5.8531132254635506e-06 -▁Nachbarschaft 5.8531132254635506e-06 -▁Plant 5.8531132254635506e-06 -▁US-Dollar 5.8531132254635506e-06 -▁effizienten 5.8531132254635506e-06 -▁vielfach 5.8531132254635506e-06 -ku 5.8493248026638977e-06 -quipped 5.8493248026638977e-06 -ustainable 5.8493248026638977e-06 -▁Alex 5.8493248026638977e-06 -▁Bolkestein 5.8493248026638977e-06 -▁Geschichten 5.8493248026638977e-06 -▁Jews 5.8493248026638977e-06 -▁R. 5.8493248026638977e-06 -▁indispensable 5.8493248026638977e-06 -▁lawyers 5.8493248026638977e-06 -bietern 5.8455363798642448e-06 -▁5th 5.8455363798642448e-06 -▁Besonder 5.8455363798642448e-06 -▁Besteller 5.8455363798642448e-06 -▁Ce 5.8455363798642448e-06 -▁Songs 5.8455363798642448e-06 -▁accountability 5.8455363798642448e-06 -▁deutlicher 5.8455363798642448e-06 -▁finanzieller 5.8455363798642448e-06 -atible 5.8417479570645919e-06 -▁196 5.8417479570645919e-06 -▁Flotte 5.8417479570645919e-06 -▁Ry 5.8417479570645919e-06 -▁Slovak 5.8417479570645919e-06 -▁enjoyable 5.8417479570645919e-06 -▁hairdryer 5.8417479570645919e-06 -▁hinzuweisen 5.8417479570645919e-06 -▁vessel 5.8417479570645919e-06 -arket 5.837959534264939e-06 -fal 5.837959534264939e-06 -igrant 5.837959534264939e-06 -mos 5.837959534264939e-06 -träglich 5.837959534264939e-06 -zellen 5.837959534264939e-06 -▁Gleichbehandlung 5.837959534264939e-06 -▁Oracle 5.837959534264939e-06 -▁marriage 5.837959534264939e-06 -▁strikt 5.837959534264939e-06 -▁symbolic 5.837959534264939e-06 -▁vorangegangenen 5.837959534264939e-06 --Strategie 5.834171111465287e-06 -dis 5.834171111465287e-06 -obile 5.834171111465287e-06 -ohlen 5.834171111465287e-06 -▁Being 5.834171111465287e-06 -▁Benach 5.834171111465287e-06 -▁Brust 5.834171111465287e-06 -▁Kul 5.834171111465287e-06 -▁Pont 5.834171111465287e-06 -▁ausgeglichen 5.834171111465287e-06 -▁stabile 5.834171111465287e-06 -ample 5.8303826886656341e-06 -berto 5.8303826886656341e-06 -etto 5.8303826886656341e-06 -master 5.8303826886656341e-06 -ommer 5.8303826886656341e-06 -▁Abfälle 5.8303826886656341e-06 -▁Binnenmarkts 5.8303826886656341e-06 -▁LateRooms 5.8303826886656341e-06 -▁ausgestatteten 5.8303826886656341e-06 -▁berecht 5.8303826886656341e-06 -▁co-operation 5.8303826886656341e-06 -▁gaming 5.8303826886656341e-06 -bre 5.8265942658659812e-06 -einheiten 5.8265942658659812e-06 -folg 5.8265942658659812e-06 -hlt 5.8265942658659812e-06 -klen 5.8265942658659812e-06 -▁Core 5.8265942658659812e-06 -▁DB 5.8265942658659812e-06 -▁EU-A 5.8265942658659812e-06 -▁Nahrungs 5.8265942658659812e-06 -▁Vorgänger 5.8265942658659812e-06 -▁arriving 5.8265942658659812e-06 -▁pi 5.8265942658659812e-06 -▁qualifizierte 5.8265942658659812e-06 -▁weiterzu 5.8265942658659812e-06 -eckten 5.8228058430663283e-06 -hrer 5.8228058430663283e-06 -▁Ausstellungs 5.8228058430663283e-06 -▁Konsumenten 5.8228058430663283e-06 -▁Need 5.8228058430663283e-06 -▁Schuh 5.8228058430663283e-06 -▁Surf 5.8228058430663283e-06 -▁Tipps 5.8228058430663283e-06 -▁größtenteils 5.8228058430663283e-06 -▁infrastructures 5.8228058430663283e-06 -▁infring 5.8228058430663283e-06 -▁mort 5.8228058430663283e-06 -▁socially 5.8228058430663283e-06 -▁unglaub 5.8228058430663283e-06 -▁zahlt 5.8228058430663283e-06 -▁Dynam 5.8190174202666754e-06 -▁Tut 5.8190174202666754e-06 -▁Virus 5.8190174202666754e-06 -▁attempted 5.8190174202666754e-06 -▁bemerken 5.8190174202666754e-06 -▁kür 5.8190174202666754e-06 -▁limitations 5.8190174202666754e-06 -▁shoes 5.8190174202666754e-06 -▁wettbewerbsfähig 5.8190174202666754e-06 -KA 5.8152289974670225e-06 -bei 5.8152289974670225e-06 -iors 5.8152289974670225e-06 -▁Bob 5.8152289974670225e-06 -▁Vermögen 5.8152289974670225e-06 -▁meter 5.8152289974670225e-06 -▁vorhandene 5.8152289974670225e-06 -forder 5.81144057466737e-06 -gestellte 5.81144057466737e-06 -stuhl 5.81144057466737e-06 -▁1.1 5.81144057466737e-06 -▁Argentinien 5.81144057466737e-06 -▁Diensten 5.81144057466737e-06 -▁Differenz 5.81144057466737e-06 -▁Sett 5.81144057466737e-06 -▁commen 5.81144057466737e-06 -▁droht 5.81144057466737e-06 -▁flour 5.81144057466737e-06 -▁gefolgt 5.81144057466737e-06 -▁metall 5.81144057466737e-06 -▁zweimal 5.81144057466737e-06 -asing 5.8076521518677167e-06 -▁Ank 5.8076521518677167e-06 -▁Jahresbericht 5.8076521518677167e-06 -▁Tony 5.8076521518677167e-06 -▁Trop 5.8076521518677167e-06 -▁designated 5.8076521518677167e-06 -▁dram 5.8076521518677167e-06 -▁earned 5.8076521518677167e-06 -▁fehlende 5.8076521518677167e-06 -▁gleichberecht 5.8076521518677167e-06 -▁journal 5.8076521518677167e-06 -▁optimalen 5.8076521518677167e-06 -▁tit 5.8076521518677167e-06 -kolle 5.8038637290680647e-06 -ough 5.8038637290680647e-06 -ubar 5.8038637290680647e-06 -▁Elementen 5.8038637290680647e-06 -▁Harry 5.8038637290680647e-06 -▁Hind 5.8038637290680647e-06 -▁Neuigkeiten 5.8038637290680647e-06 -▁Sit 5.8038637290680647e-06 -▁Statt 5.8038637290680647e-06 -▁hypoc 5.8038637290680647e-06 -▁kommerzielle 5.8038637290680647e-06 -äut 5.8000753062684118e-06 -▁Interface 5.8000753062684118e-06 -▁Wurzeln 5.8000753062684118e-06 -▁analyt 5.8000753062684118e-06 -▁delivering 5.8000753062684118e-06 -▁flower 5.8000753062684118e-06 -▁inevitable 5.8000753062684118e-06 -▁opponents 5.8000753062684118e-06 -▁printer 5.8000753062684118e-06 -arium 5.7962868834687589e-06 -reet 5.7962868834687589e-06 -▁Aufträge 5.7962868834687589e-06 -▁Persönlichkeit 5.7962868834687589e-06 -▁Prü 5.7962868834687589e-06 -▁Quad 5.7962868834687589e-06 -▁Simbabwe 5.7962868834687589e-06 -▁Stift 5.7962868834687589e-06 -▁await 5.7962868834687589e-06 -▁curt 5.7962868834687589e-06 -▁tier 5.7962868834687589e-06 -▁veröffentlichten 5.7962868834687589e-06 -bur 5.792498460669106e-06 -gelt 5.792498460669106e-06 -idents 5.792498460669106e-06 -▁Christians 5.792498460669106e-06 -▁Informations- 5.792498460669106e-06 -▁Unionsbürger 5.792498460669106e-06 -▁greet 5.792498460669106e-06 -▁hinterlassen 5.792498460669106e-06 -▁rechtlich 5.792498460669106e-06 -roll 5.7887100378694531e-06 -▁Audi 5.7887100378694531e-06 -▁Ernennung 5.7887100378694531e-06 -▁Mitentscheid 5.7887100378694531e-06 -▁Oh 5.7887100378694531e-06 -▁embark 5.7887100378694531e-06 -▁invites 5.7887100378694531e-06 -banken 5.7849216150698e-06 -ijn 5.7849216150698e-06 -▁Cors 5.7849216150698e-06 -▁Industries 5.7849216150698e-06 -▁Zielsetzung 5.7849216150698e-06 -▁actor 5.7849216150698e-06 -▁avoiding 5.7849216150698e-06 -▁featured 5.7849216150698e-06 -▁sed 5.7849216150698e-06 -ansprüche 5.7811331922701473e-06 -establ 5.7811331922701473e-06 -unal 5.7811331922701473e-06 -▁zulässig 5.7811331922701473e-06 -▁Übereinkommens 5.7811331922701473e-06 --Abkommen 5.7773447694704944e-06 -.C. 5.7773447694704944e-06 -action 5.7773447694704944e-06 -▁Graz 5.7773447694704944e-06 -▁Kaffe 5.7773447694704944e-06 -▁Möchten 5.7773447694704944e-06 -▁Unlike 5.7773447694704944e-06 -▁Verg 5.7773447694704944e-06 -▁advocate 5.7773447694704944e-06 -▁belieb 5.7773447694704944e-06 -▁bol 5.7773447694704944e-06 -▁komm 5.7773447694704944e-06 -▁menus 5.7773447694704944e-06 -▁quantitative 5.7773447694704944e-06 -erren 5.7735563466708424e-06 -proof 5.7735563466708424e-06 -veranstaltungen 5.7735563466708424e-06 -▁4th 5.7735563466708424e-06 -▁Aussichten 5.7735563466708424e-06 -▁Brown 5.7735563466708424e-06 -▁Defizite 5.7735563466708424e-06 -▁Leder 5.7735563466708424e-06 -▁Organen 5.7735563466708424e-06 -iegeln 5.7697679238711895e-06 -ić 5.7697679238711895e-06 -ounced 5.7697679238711895e-06 -ynam 5.7697679238711895e-06 -▁2.4 5.7697679238711895e-06 -▁Ratspräsidenten 5.7697679238711895e-06 -▁Texas 5.7697679238711895e-06 -▁Tunesien 5.7697679238711895e-06 -▁epidem 5.7697679238711895e-06 -▁hilfsbereit 5.7697679238711895e-06 -▁kompatibel 5.7697679238711895e-06 -▁qualit 5.7697679238711895e-06 -▁wasn 5.7697679238711895e-06 -nutzung 5.7659795010715366e-06 -uropas 5.7659795010715366e-06 -works 5.7659795010715366e-06 -▁Bangkok 5.7659795010715366e-06 -▁Brother 5.7659795010715366e-06 -▁TU 5.7659795010715366e-06 -▁Titan 5.7659795010715366e-06 -▁dumping 5.7659795010715366e-06 -▁proportional 5.7659795010715366e-06 -▁subscription 5.7659795010715366e-06 -▁wiederhol 5.7659795010715366e-06 -ground 5.7621910782718837e-06 -kamera 5.7621910782718837e-06 -verhältnisse 5.7621910782718837e-06 -▁Alarm 5.7621910782718837e-06 -▁Stellenwert 5.7621910782718837e-06 -▁coherence 5.7621910782718837e-06 -▁einseit 5.7621910782718837e-06 -▁erwe 5.7621910782718837e-06 -▁getestet 5.7621910782718837e-06 -▁household 5.7621910782718837e-06 -▁umstritten 5.7621910782718837e-06 -ARE 5.7584026554722308e-06 -iability 5.7584026554722308e-06 -konvention 5.7584026554722308e-06 -life 5.7584026554722308e-06 -▁Annex 5.7584026554722308e-06 -▁Barnier 5.7584026554722308e-06 -▁anger 5.7584026554722308e-06 -▁beautifully 5.7584026554722308e-06 -▁diam 5.7584026554722308e-06 -▁ehr 5.7584026554722308e-06 -▁umfassendes 5.7584026554722308e-06 -fristig 5.7546142326725779e-06 -laimed 5.7546142326725779e-06 -treten 5.7546142326725779e-06 -▁Anbau 5.7546142326725779e-06 -▁Copy 5.7546142326725779e-06 -▁Festung 5.7546142326725779e-06 -▁Large 5.7546142326725779e-06 -▁Territorium 5.7546142326725779e-06 -▁beeindruck 5.7546142326725779e-06 -▁controller 5.7546142326725779e-06 -▁herein 5.7546142326725779e-06 -▁Boutique 5.750825809872925e-06 -▁Parlamenten 5.750825809872925e-06 -▁Statut 5.750825809872925e-06 -▁Verkehrsmittel 5.750825809872925e-06 -▁outlined 5.750825809872925e-06 -▁promising 5.750825809872925e-06 -▁racing 5.750825809872925e-06 -chens 5.7470373870732721e-06 -ebene 5.7470373870732721e-06 -ez 5.7470373870732721e-06 -link 5.7470373870732721e-06 -▁Ausnahmeregel 5.7470373870732721e-06 -▁Johannesburg 5.7470373870732721e-06 -▁contr 5.7470373870732721e-06 -adet 5.74324896427362e-06 -inson 5.74324896427362e-06 -machen 5.74324896427362e-06 -mont 5.74324896427362e-06 -▁-B 5.74324896427362e-06 -▁1.4 5.74324896427362e-06 -▁1977 5.74324896427362e-06 -▁Aur 5.74324896427362e-06 -▁CD-ROM 5.74324896427362e-06 -▁Forsch 5.74324896427362e-06 -▁Lü 5.74324896427362e-06 -▁Rov 5.74324896427362e-06 -▁Zellen 5.74324896427362e-06 -▁episode 5.74324896427362e-06 -▁gewerb 5.74324896427362e-06 -▁hopefully 5.74324896427362e-06 -▁multilingual 5.74324896427362e-06 -ieso 5.7394605414739672e-06 -strecken 5.7394605414739672e-06 -weiterung 5.7394605414739672e-06 -▁Finanziellen 5.7394605414739672e-06 -▁Pfad 5.7394605414739672e-06 -▁acr 5.7394605414739672e-06 -▁einzustellen 5.7394605414739672e-06 -▁handed 5.7394605414739672e-06 -.05 5.7356721186743143e-06 -▁84 5.7356721186743143e-06 -▁Autonomie 5.7356721186743143e-06 -▁Spezif 5.7356721186743143e-06 -▁Zuerst 5.7356721186743143e-06 -▁anmelden 5.7356721186743143e-06 -▁broadcast 5.7356721186743143e-06 -▁impetus 5.7356721186743143e-06 -▁reun 5.7356721186743143e-06 -▁wunderschön 5.7356721186743143e-06 -IND 5.7318836958746614e-06 -ideo 5.7318836958746614e-06 -prim 5.7318836958746614e-06 -stunde 5.7318836958746614e-06 -▁allies 5.7318836958746614e-06 -▁belegen 5.7318836958746614e-06 -▁bust 5.7318836958746614e-06 -▁getreten 5.7318836958746614e-06 -▁misunder 5.7318836958746614e-06 -▁verwirklicht 5.7318836958746614e-06 -aller 5.7280952730750085e-06 -ussy 5.7280952730750085e-06 -▁Basic 5.7280952730750085e-06 -▁Musiker 5.7280952730750085e-06 -▁Stränden 5.7280952730750085e-06 -▁decoration 5.7280952730750085e-06 -▁laying 5.7280952730750085e-06 --Intern 5.7243068502753556e-06 -aute 5.7243068502753556e-06 -herrschaft 5.7243068502753556e-06 -▁Dolomiten 5.7243068502753556e-06 -▁Inst 5.7243068502753556e-06 -▁Rahmens 5.7243068502753556e-06 -▁eingest 5.7243068502753556e-06 -▁hunger 5.7243068502753556e-06 -▁personenbezogen 5.7243068502753556e-06 -▁realistisch 5.7243068502753556e-06 -▁vorrangig 5.7243068502753556e-06 -▁vorschlägt 5.7243068502753556e-06 -▁wahrnehmen 5.7243068502753556e-06 -auert 5.7205184274757027e-06 -bem 5.7205184274757027e-06 -geordneten 5.7205184274757027e-06 -oi 5.7205184274757027e-06 -▁Eck 5.7205184274757027e-06 -▁Font 5.7205184274757027e-06 -▁Fristen 5.7205184274757027e-06 -▁Libyen 5.7205184274757027e-06 -▁Registr 5.7205184274757027e-06 -▁blocked 5.7205184274757027e-06 -▁brilliant 5.7205184274757027e-06 -▁german 5.7205184274757027e-06 -▁leichten 5.7205184274757027e-06 -▁stimul 5.7205184274757027e-06 -etra 5.71673000467605e-06 -ivo 5.71673000467605e-06 -not 5.71673000467605e-06 -udio 5.71673000467605e-06 -▁G. 5.71673000467605e-06 -▁Grundstück 5.71673000467605e-06 -▁Lady 5.71673000467605e-06 -▁Walter 5.71673000467605e-06 -▁existed 5.71673000467605e-06 -▁legendary 5.71673000467605e-06 -▁zwangsläufig 5.71673000467605e-06 -estic 5.7129415818763978e-06 -ikation 5.7129415818763978e-06 -miet 5.7129415818763978e-06 -▁Künstlern 5.7129415818763978e-06 -▁Timor 5.7129415818763978e-06 -▁entre 5.7129415818763978e-06 -▁grave 5.7129415818763978e-06 -▁heruntergeladen 5.7129415818763978e-06 -▁paint 5.7129415818763978e-06 -▁suspect 5.7129415818763978e-06 -partnern 5.7091531590767449e-06 -schnitte 5.7091531590767449e-06 -sprech 5.7091531590767449e-06 -zonen 5.7091531590767449e-06 -▁Gard 5.7091531590767449e-06 -▁Liv 5.7091531590767449e-06 -▁Markus 5.7091531590767449e-06 -▁Mühe 5.7091531590767449e-06 -▁Save 5.7091531590767449e-06 -▁bestellt 5.7091531590767449e-06 -▁check-in 5.7091531590767449e-06 -▁disagree 5.7091531590767449e-06 -▁libraries 5.7091531590767449e-06 -abteilung 5.705364736277092e-06 -legten 5.705364736277092e-06 -leid 5.705364736277092e-06 -umn 5.705364736277092e-06 -verlust 5.705364736277092e-06 -▁Abweich 5.705364736277092e-06 -▁Application 5.705364736277092e-06 -▁NO 5.705364736277092e-06 -▁Vorsorge 5.705364736277092e-06 -▁Wahrnehmung 5.705364736277092e-06 -▁entity 5.705364736277092e-06 -▁globally 5.705364736277092e-06 --We 5.7015763134774391e-06 -istern 5.7015763134774391e-06 -ivated 5.7015763134774391e-06 -▁Einheiten 5.7015763134774391e-06 -▁Korrektur 5.7015763134774391e-06 -▁Machen 5.7015763134774391e-06 -▁Verpackung 5.7015763134774391e-06 -▁gesorgt 5.7015763134774391e-06 -▁schwarzen 5.7015763134774391e-06 -▁systematically 5.7015763134774391e-06 -▁verbieten 5.7015763134774391e-06 -ealing 5.6977878906777862e-06 -▁74 5.6977878906777862e-06 -▁Days 5.6977878906777862e-06 -▁Mare 5.6977878906777862e-06 -▁compatibility 5.6977878906777862e-06 -▁complain 5.6977878906777862e-06 -▁iP 5.6977878906777862e-06 -▁opera 5.6977878906777862e-06 -▁upgrad 5.6977878906777862e-06 -▁waves 5.6977878906777862e-06 -halter 5.6939994678781334e-06 -ifiz 5.6939994678781334e-06 -mes 5.6939994678781334e-06 -nee 5.6939994678781334e-06 -▁Commander 5.6939994678781334e-06 -▁Fried 5.6939994678781334e-06 -▁Klick 5.6939994678781334e-06 -▁Winters 5.6939994678781334e-06 -▁kid 5.6939994678781334e-06 -▁killing 5.6939994678781334e-06 -▁korrekte 5.6939994678781334e-06 -▁unm 5.6939994678781334e-06 -fix 5.6902110450784805e-06 -komplex 5.6902110450784805e-06 -ondo 5.6902110450784805e-06 -▁91 5.6902110450784805e-06 -▁Baroness 5.6902110450784805e-06 -▁Blizzard 5.6902110450784805e-06 -▁Chemikalien 5.6902110450784805e-06 -▁Corr 5.6902110450784805e-06 -▁Dörfer 5.6902110450784805e-06 -▁angest 5.6902110450784805e-06 -▁benachteil 5.6902110450784805e-06 -▁plate 5.6902110450784805e-06 -▁unterhalb 5.6902110450784805e-06 -▁Dubai 5.6864226222788276e-06 -▁Jac 5.6864226222788276e-06 -▁Welthandelsorganisation 5.6864226222788276e-06 -▁decken 5.6864226222788276e-06 -▁filters 5.6864226222788276e-06 -alität 5.6826341994791755e-06 -eded 5.6826341994791755e-06 -äler 5.6826341994791755e-06 -▁Côte 5.6826341994791755e-06 -▁alpine 5.6826341994791755e-06 -▁ausgedrückt 5.6826341994791755e-06 -▁fundamentally 5.6826341994791755e-06 -▁großartige 5.6826341994791755e-06 -▁loan 5.6826341994791755e-06 -▁robots 5.6826341994791755e-06 -▁veter 5.6826341994791755e-06 -anted 5.6788457766795226e-06 -▁Argent 5.6788457766795226e-06 -▁Funk 5.6788457766795226e-06 -▁Kühlschrank 5.6788457766795226e-06 -▁Mehrere 5.6788457766795226e-06 -▁Sämtliche 5.6788457766795226e-06 -▁Tabellen 5.6788457766795226e-06 -▁Urban 5.6788457766795226e-06 -▁slaughter 5.6788457766795226e-06 -▁ungen 5.6788457766795226e-06 -,0 5.67505735387987e-06 -with 5.67505735387987e-06 -▁Canaria 5.67505735387987e-06 -▁Channel 5.67505735387987e-06 -▁HR 5.67505735387987e-06 -▁gerufen 5.67505735387987e-06 -▁reale 5.67505735387987e-06 -▁serial 5.67505735387987e-06 -▁toil 5.67505735387987e-06 -▁universe 5.67505735387987e-06 -ausbildung 5.6712689310802169e-06 -enny 5.6712689310802169e-06 -kompetenz 5.6712689310802169e-06 -onas 5.6712689310802169e-06 -orientierten 5.6712689310802169e-06 -▁Fertigungs 5.6712689310802169e-06 -▁Fortschritts 5.6712689310802169e-06 -▁Frankreichs 5.6712689310802169e-06 -▁Mong 5.6712689310802169e-06 -▁Rail 5.6712689310802169e-06 -▁architekt 5.6712689310802169e-06 -▁delivers 5.6712689310802169e-06 -▁mad 5.6712689310802169e-06 -achts 5.667480508280564e-06 -ocus 5.667480508280564e-06 -ooking 5.667480508280564e-06 -wid 5.667480508280564e-06 -öd 5.667480508280564e-06 -▁Berechnung 5.667480508280564e-06 -▁Hunder 5.667480508280564e-06 -▁Mittel- 5.667480508280564e-06 -▁Vermarktung 5.667480508280564e-06 -▁authorized 5.667480508280564e-06 -▁beunruh 5.667480508280564e-06 -▁contradict 5.667480508280564e-06 -▁installing 5.667480508280564e-06 -▁regelmäßigen 5.667480508280564e-06 -LAN 5.6636920854809111e-06 -bevölkerung 5.6636920854809111e-06 -eats 5.6636920854809111e-06 -plattform 5.6636920854809111e-06 -schäden 5.6636920854809111e-06 -zügen 5.6636920854809111e-06 -▁Airways 5.6636920854809111e-06 -▁Barack 5.6636920854809111e-06 -▁Einladung 5.6636920854809111e-06 -▁SEO 5.6636920854809111e-06 -▁Torre 5.6636920854809111e-06 -▁clin 5.6636920854809111e-06 -▁cost-eff 5.6636920854809111e-06 -▁inevitably 5.6636920854809111e-06 -▁konvention 5.6636920854809111e-06 -▁langjährige 5.6636920854809111e-06 -▁wet 5.6636920854809111e-06 -▁zuzu 5.6636920854809111e-06 -OST 5.6599036626812582e-06 -landes 5.6599036626812582e-06 -älen 5.6599036626812582e-06 -▁erweiterte 5.6599036626812582e-06 -▁locally 5.6599036626812582e-06 -▁qualitative 5.6599036626812582e-06 -▁skilled 5.6599036626812582e-06 --Men 5.6561152398816061e-06 -artikel 5.6561152398816061e-06 -ifizierte 5.6561152398816061e-06 -▁Ausfuhr 5.6561152398816061e-06 -▁BI 5.6561152398816061e-06 -▁Chaos 5.6561152398816061e-06 -▁Einblick 5.6561152398816061e-06 -▁Gleichheit 5.6561152398816061e-06 -▁Logo 5.6561152398816061e-06 -▁Pos 5.6561152398816061e-06 -▁Spenden 5.6561152398816061e-06 -▁Tok 5.6561152398816061e-06 -▁auction 5.6561152398816061e-06 -▁schöner 5.6561152398816061e-06 -iring 5.6523268170819532e-06 -iron 5.6523268170819532e-06 -ommun 5.6523268170819532e-06 -ählte 5.6523268170819532e-06 -▁...... 5.6523268170819532e-06 -▁Crew 5.6523268170819532e-06 -▁Egal 5.6523268170819532e-06 -▁Getreide 5.6523268170819532e-06 -▁Kub 5.6523268170819532e-06 -▁Little 5.6523268170819532e-06 -▁Unserer 5.6523268170819532e-06 -▁inequality 5.6523268170819532e-06 -▁interfaces 5.6523268170819532e-06 -▁schwedische 5.6523268170819532e-06 -▁surplus 5.6523268170819532e-06 -▁vorkommen 5.6523268170819532e-06 -fahrzeug 5.6485383942823e-06 -irr 5.6485383942823e-06 -▁Landschaften 5.6485383942823e-06 -▁Sich 5.6485383942823e-06 -▁WiFi 5.6485383942823e-06 -▁antiken 5.6485383942823e-06 -▁overnight 5.6485383942823e-06 -▁uncom 5.6485383942823e-06 -▁Generalsekretär 5.6447499714826475e-06 -▁Konflikten 5.6447499714826475e-06 -▁OP 5.6447499714826475e-06 -▁attraction 5.6447499714826475e-06 -▁forming 5.6447499714826475e-06 -▁greif 5.6447499714826475e-06 -▁steckt 5.6447499714826475e-06 -▁tranquil 5.6447499714826475e-06 -dy 5.6409615486829946e-06 -zzo 5.6409615486829946e-06 -ähn 5.6409615486829946e-06 -▁Anmerkung 5.6409615486829946e-06 -▁Atlant 5.6409615486829946e-06 -▁EU-Mitglied 5.6409615486829946e-06 -▁Mozart 5.6409615486829946e-06 -▁Territor 5.6409615486829946e-06 -▁Zell 5.6409615486829946e-06 -▁lands 5.6409615486829946e-06 -aph 5.6371731258833417e-06 -ielten 5.6371731258833417e-06 -tert 5.6371731258833417e-06 -▁Automobilindustrie 5.6371731258833417e-06 -▁Bag 5.6371731258833417e-06 -▁Foren 5.6371731258833417e-06 -▁Frü 5.6371731258833417e-06 -▁Nachfolger 5.6371731258833417e-06 -▁Sicherheits- 5.6371731258833417e-06 -▁Telef 5.6371731258833417e-06 -▁abzielt 5.6371731258833417e-06 -▁gradual 5.6371731258833417e-06 -▁verglichen 5.6371731258833417e-06 -wurf 5.6333847030836888e-06 -zene 5.6333847030836888e-06 -▁Term 5.6333847030836888e-06 -▁bom 5.6333847030836888e-06 -▁calculation 5.6333847030836888e-06 -▁cigar 5.6333847030836888e-06 -▁electronics 5.6333847030836888e-06 -▁mieten 5.6333847030836888e-06 -▁owing 5.6333847030836888e-06 -▁rech 5.6333847030836888e-06 -▁tatsächliche 5.6333847030836888e-06 -anien 5.6295962802840359e-06 -een 5.6295962802840359e-06 -esty 5.6295962802840359e-06 -▁-A 5.6295962802840359e-06 -▁Auskunft 5.6295962802840359e-06 -▁Konflikts 5.6295962802840359e-06 -▁Victor 5.6295962802840359e-06 -▁anlässlich 5.6295962802840359e-06 -▁retirement 5.6295962802840359e-06 -▁subjected 5.6295962802840359e-06 -▁suspend 5.6295962802840359e-06 -▁Ethernet 5.6258078574843838e-06 -▁H. 5.6258078574843838e-06 -▁Kammer 5.6258078574843838e-06 -▁Merkel 5.6258078574843838e-06 -▁Träger 5.6258078574843838e-06 -▁anstreben 5.6258078574843838e-06 -▁carries 5.6258078574843838e-06 -▁high-speed 5.6258078574843838e-06 -▁prosper 5.6258078574843838e-06 -▁workforce 5.6258078574843838e-06 -amkeit 5.622019434684731e-06 -anta 5.622019434684731e-06 -eap 5.622019434684731e-06 -idung 5.622019434684731e-06 -ommerce 5.622019434684731e-06 -▁Abwicklung 5.622019434684731e-06 -▁Behinderungen 5.622019434684731e-06 -▁Beta 5.622019434684731e-06 -▁Sekt 5.622019434684731e-06 -▁deeper 5.622019434684731e-06 -▁gewöhnlich 5.622019434684731e-06 -▁mysq 5.622019434684731e-06 -held 5.6182310118850781e-06 -ilien 5.6182310118850781e-06 -quiries 5.6182310118850781e-06 -▁Canal 5.6182310118850781e-06 -▁Cardass 5.6182310118850781e-06 -▁Schnittstelle 5.6182310118850781e-06 -▁activated 5.6182310118850781e-06 -▁cour 5.6182310118850781e-06 -▁einzeln 5.6182310118850781e-06 -▁geteilt 5.6182310118850781e-06 -▁openly 5.6182310118850781e-06 -▁portra 5.6182310118850781e-06 -▁simplified 5.6182310118850781e-06 -▁wünscht 5.6182310118850781e-06 --Um 5.6144425890854252e-06 -▁Criminal 5.6144425890854252e-06 -▁Nachteil 5.6144425890854252e-06 -▁Solange 5.6144425890854252e-06 -▁Stimmung 5.6144425890854252e-06 -▁admission 5.6144425890854252e-06 -▁bicycle 5.6144425890854252e-06 -▁dereg 5.6144425890854252e-06 -▁herself 5.6144425890854252e-06 -▁tremendous 5.6144425890854252e-06 --Ber 5.6106541662857723e-06 -acks 5.6106541662857723e-06 -ecurity 5.6106541662857723e-06 -power 5.6106541662857723e-06 -▁Flughaf 5.6106541662857723e-06 -▁Girl 5.6106541662857723e-06 -▁batt 5.6106541662857723e-06 -▁bewirkt 5.6106541662857723e-06 -▁recogn 5.6106541662857723e-06 -echnologie 5.6068657434861194e-06 -film 5.6068657434861194e-06 -frü 5.6068657434861194e-06 -rift 5.6068657434861194e-06 -ströme 5.6068657434861194e-06 -▁Aufrechterhaltung 5.6068657434861194e-06 -▁Neuer 5.6068657434861194e-06 -▁Preis-Leist 5.6068657434861194e-06 -▁Romant 5.6068657434861194e-06 -▁Substanzen 5.6068657434861194e-06 -▁Wachstumspakt 5.6068657434861194e-06 -▁Walk 5.6068657434861194e-06 -▁env 5.6068657434861194e-06 -▁sexuellen 5.6068657434861194e-06 -▁symbols 5.6068657434861194e-06 -▁viewing 5.6068657434861194e-06 -▁ä 5.6068657434861194e-06 -▁übertr 5.6068657434861194e-06 --Serie 5.6030773206864665e-06 -bil 5.6030773206864665e-06 -fred 5.6030773206864665e-06 -osure 5.6030773206864665e-06 -ños 5.6030773206864665e-06 -▁Cho 5.6030773206864665e-06 -▁beeinträchtigen 5.6030773206864665e-06 -▁maintains 5.6030773206864665e-06 -▁prevents 5.6030773206864665e-06 -▁prom 5.6030773206864665e-06 --Regierung 5.5992888978868136e-06 -▁Reden 5.5992888978868136e-06 -▁Starfleet 5.5992888978868136e-06 -▁beantragen 5.5992888978868136e-06 -▁capitalism 5.5992888978868136e-06 -▁exceptionally 5.5992888978868136e-06 -▁ideological 5.5992888978868136e-06 -▁judges 5.5992888978868136e-06 -▁nerv 5.5992888978868136e-06 -▁parlamentarische 5.5992888978868136e-06 -▁rub 5.5992888978868136e-06 -▁solving 5.5992888978868136e-06 -▁thirty 5.5992888978868136e-06 -▁wishing 5.5992888978868136e-06 -gesetzten 5.5955004750871616e-06 -ipher 5.5955004750871616e-06 -rators 5.5955004750871616e-06 -▁Annäherung 5.5955004750871616e-06 -▁Buchstaben 5.5955004750871616e-06 -▁Erdbeben 5.5955004750871616e-06 -▁ads 5.5955004750871616e-06 -▁europ 5.5955004750871616e-06 --Systeme 5.5917120522875087e-06 -alg 5.5917120522875087e-06 -hib 5.5917120522875087e-06 -unches 5.5917120522875087e-06 -▁Catholic 5.5917120522875087e-06 -▁Environmental 5.5917120522875087e-06 -▁Leiden 5.5917120522875087e-06 -▁Vere 5.5917120522875087e-06 -▁späten 5.5917120522875087e-06 -atell 5.5879236294878558e-06 -pon 5.5879236294878558e-06 -zweck 5.5879236294878558e-06 -▁Collection 5.5879236294878558e-06 -▁MR 5.5879236294878558e-06 -▁Sofia 5.5879236294878558e-06 -▁regel 5.5879236294878558e-06 -amos 5.5841352066882029e-06 -iftung 5.5841352066882029e-06 -▁Eleganz 5.5841352066882029e-06 -▁Etagen 5.5841352066882029e-06 -▁PPE-DE-Fraktion 5.5841352066882029e-06 -▁Verhandlung 5.5841352066882029e-06 -▁procedural 5.5841352066882029e-06 -▁thumb 5.5841352066882029e-06 -▁wand 5.5841352066882029e-06 -hüt 5.58034678388855e-06 -phone 5.58034678388855e-06 -ár 5.58034678388855e-06 -▁1969 5.58034678388855e-06 -▁Workshops 5.58034678388855e-06 -▁borne 5.58034678388855e-06 -erts 5.5765583610888971e-06 -wissenschaften 5.5765583610888971e-06 -▁Entdeckung 5.5765583610888971e-06 -▁Haushaltsjahr 5.5765583610888971e-06 -▁beantragt 5.5765583610888971e-06 -▁betreibt 5.5765583610888971e-06 -▁christ 5.5765583610888971e-06 -▁complaint 5.5765583610888971e-06 -▁geschätzt 5.5765583610888971e-06 -▁provis 5.5765583610888971e-06 -▁resumed 5.5765583610888971e-06 -▁silent 5.5765583610888971e-06 -ا 5.5727699382892442e-06 -▁FM 5.5727699382892442e-06 -▁Lanka 5.5727699382892442e-06 -▁bold 5.5727699382892442e-06 -▁hôtel 5.5727699382892442e-06 -▁kos 5.5727699382892442e-06 -aspekt 5.5689815154895913e-06 -ref 5.5689815154895913e-06 -usions 5.5689815154895913e-06 -▁Bayer 5.5689815154895913e-06 -▁Cube 5.5689815154895913e-06 -▁Erdöl 5.5689815154895913e-06 -▁MAN 5.5689815154895913e-06 -▁Schwar 5.5689815154895913e-06 -▁Verbindungs 5.5689815154895913e-06 -▁appro 5.5689815154895913e-06 -▁detection 5.5689815154895913e-06 -▁geometr 5.5689815154895913e-06 -▁newest 5.5689815154895913e-06 -eper 5.5651930926899393e-06 -projekten 5.5651930926899393e-06 -▁Konvents 5.5651930926899393e-06 -▁Zap 5.5651930926899393e-06 -▁befreien 5.5651930926899393e-06 -▁begrüß 5.5651930926899393e-06 -▁refriger 5.5651930926899393e-06 -▁tube 5.5651930926899393e-06 -ATE 5.5614046698902864e-06 -antas 5.5614046698902864e-06 -obs 5.5614046698902864e-06 -ulden 5.5614046698902864e-06 -▁Chechnya 5.5614046698902864e-06 -▁Erwerb 5.5614046698902864e-06 -▁Nuklear 5.5614046698902864e-06 -▁PE 5.5614046698902864e-06 -▁Ray 5.5614046698902864e-06 -▁Vulkan 5.5614046698902864e-06 -▁herz 5.5614046698902864e-06 -▁kur 5.5614046698902864e-06 -▁ließen 5.5614046698902864e-06 -▁writer 5.5614046698902864e-06 -▁Diet 5.5576162470906335e-06 -▁GVO 5.5576162470906335e-06 -▁PVC 5.5576162470906335e-06 -▁ausführliche 5.5576162470906335e-06 -▁ausg 5.5576162470906335e-06 -▁backed 5.5576162470906335e-06 -▁horses 5.5576162470906335e-06 -▁jährliche 5.5576162470906335e-06 -▁mö 5.5576162470906335e-06 -▁persönlicher 5.5576162470906335e-06 -▁verwand 5.5576162470906335e-06 --18 5.5538278242909806e-06 -bollah 5.5538278242909806e-06 -ocks 5.5538278242909806e-06 -uliert 5.5538278242909806e-06 -σ 5.5538278242909806e-06 -▁Finn 5.5538278242909806e-06 -▁GF 5.5538278242909806e-06 -▁Sozialpolitik 5.5538278242909806e-06 -▁Volkswagen 5.5538278242909806e-06 -▁closure 5.5538278242909806e-06 -▁ranges 5.5538278242909806e-06 -formationen 5.5500394014913277e-06 -pakete 5.5500394014913277e-06 -▁Ebenfalls 5.5500394014913277e-06 -▁Gültigkeit 5.5500394014913277e-06 -▁Hilfen 5.5500394014913277e-06 -▁Quar 5.5500394014913277e-06 -▁Stabil 5.5500394014913277e-06 -▁Zeitungen 5.5500394014913277e-06 -▁abends 5.5500394014913277e-06 -▁argued 5.5500394014913277e-06 -▁vacuum 5.5500394014913277e-06 -güter 5.5462509786916748e-06 -öne 5.5462509786916748e-06 -▁MIDI 5.5462509786916748e-06 -▁Säule 5.5462509786916748e-06 -▁allmählich 5.5462509786916748e-06 -▁convincing 5.5462509786916748e-06 -▁highway 5.5462509786916748e-06 -▁latein 5.5462509786916748e-06 -▁nost 5.5462509786916748e-06 -▁selecting 5.5462509786916748e-06 -▁spark 5.5462509786916748e-06 -▁utter 5.5462509786916748e-06 -afen 5.5424625558920219e-06 -datum 5.5424625558920219e-06 -icult 5.5424625558920219e-06 -izza 5.5424625558920219e-06 -termin 5.5424625558920219e-06 -ì 5.5424625558920219e-06 -▁1.3 5.5424625558920219e-06 -▁Anzeichen 5.5424625558920219e-06 -▁Disney 5.5424625558920219e-06 -▁Haw 5.5424625558920219e-06 -▁Store 5.5424625558920219e-06 -▁benötigten 5.5424625558920219e-06 -▁donors 5.5424625558920219e-06 -▁hosting 5.5424625558920219e-06 -▁islamischen 5.5424625558920219e-06 -▁mood 5.5424625558920219e-06 -▁reviewed 5.5424625558920219e-06 -perten 5.538674133092369e-06 -zeitige 5.538674133092369e-06 -▁Edel 5.538674133092369e-06 -▁Gerne 5.538674133092369e-06 -▁Rational 5.538674133092369e-06 -▁Romanian 5.538674133092369e-06 -▁beliebtesten 5.538674133092369e-06 -▁heik 5.538674133092369e-06 -▁klares 5.538674133092369e-06 -geordnet 5.534885710292717e-06 -iture 5.534885710292717e-06 -▁Phasen 5.534885710292717e-06 -▁Sortiment 5.534885710292717e-06 -▁abuses 5.534885710292717e-06 -▁anfangen 5.534885710292717e-06 -▁assum 5.534885710292717e-06 -▁damaligen 5.534885710292717e-06 -▁resid 5.534885710292717e-06 -▁thick 5.534885710292717e-06 -▁vertrag 5.534885710292717e-06 -▁zuge 5.534885710292717e-06 -CHT 5.5310972874930641e-06 -erl 5.5310972874930641e-06 -ikon 5.5310972874930641e-06 -▁Einwände 5.5310972874930641e-06 -▁Entschädigung 5.5310972874930641e-06 -▁advances 5.5310972874930641e-06 -▁breast 5.5310972874930641e-06 -▁decrease 5.5310972874930641e-06 -▁eingeben 5.5310972874930641e-06 -▁lat 5.5310972874930641e-06 -▁meint 5.5310972874930641e-06 -▁technically 5.5310972874930641e-06 -appy 5.5273088646934112e-06 -leich 5.5273088646934112e-06 -önen 5.5273088646934112e-06 -▁Consult 5.5273088646934112e-06 -▁Myth 5.5273088646934112e-06 -▁Nothing 5.5273088646934112e-06 -▁PXI 5.5273088646934112e-06 -▁Publik 5.5273088646934112e-06 -▁Terra 5.5273088646934112e-06 -▁contem 5.5273088646934112e-06 -▁distributors 5.5273088646934112e-06 -▁on-site 5.5273088646934112e-06 -▁wise 5.5273088646934112e-06 -udi 5.5235204418937583e-06 -würd 5.5235204418937583e-06 -ño 5.5235204418937583e-06 -▁Cart 5.5235204418937583e-06 -▁Früchte 5.5235204418937583e-06 -▁Fünft 5.5235204418937583e-06 -▁Hay 5.5235204418937583e-06 -▁Leser 5.5235204418937583e-06 -▁Suchen 5.5235204418937583e-06 -▁Tex 5.5235204418937583e-06 -▁Vat 5.5235204418937583e-06 -▁abstained 5.5235204418937583e-06 -▁administer 5.5235204418937583e-06 -▁griechische 5.5235204418937583e-06 -▁künd 5.5235204418937583e-06 --Kl 5.5197320190941054e-06 -büros 5.5197320190941054e-06 -▁Bologna 5.5197320190941054e-06 -▁Florida 5.5197320190941054e-06 -▁Thea 5.5197320190941054e-06 -▁chances 5.5197320190941054e-06 -▁giant 5.5197320190941054e-06 -▁personality 5.5197320190941054e-06 -▁terraces 5.5197320190941054e-06 -▁tiefe 5.5197320190941054e-06 -▁ökonomischen 5.5197320190941054e-06 -91 5.5159435962944525e-06 -BR 5.5159435962944525e-06 -andern 5.5159435962944525e-06 -él 5.5159435962944525e-06 -▁Bestand 5.5159435962944525e-06 -▁Model 5.5159435962944525e-06 -▁Rohstoff 5.5159435962944525e-06 -▁Schiffen 5.5159435962944525e-06 -▁Statute 5.5159435962944525e-06 -▁Swoboda 5.5159435962944525e-06 -▁industrielle 5.5159435962944525e-06 -▁stoppen 5.5159435962944525e-06 -ettbewerb 5.5121551734948e-06 -host 5.5121551734948e-06 -zepte 5.5121551734948e-06 -▁Cotonou 5.5121551734948e-06 -▁Gesamtheit 5.5121551734948e-06 -▁Umstrukturierung 5.5121551734948e-06 -▁bezahl 5.5121551734948e-06 -▁mixture 5.5121551734948e-06 -▁nütz 5.5121551734948e-06 -▁origins 5.5121551734948e-06 -▁reinforced 5.5121551734948e-06 -▁sen 5.5121551734948e-06 -agte 5.5083667506951468e-06 -attung 5.5083667506951468e-06 -lim 5.5083667506951468e-06 -▁Zul 5.5083667506951468e-06 -▁aluminium 5.5083667506951468e-06 -▁ausgewogene 5.5083667506951468e-06 -▁ferry 5.5083667506951468e-06 -▁genauen 5.5083667506951468e-06 -▁hearts 5.5083667506951468e-06 -Tech 5.5045783278954947e-06 -ovina 5.5045783278954947e-06 -stechnologie 5.5045783278954947e-06 -▁Compact 5.5045783278954947e-06 -▁PCs 5.5045783278954947e-06 -▁Teilnehmern 5.5045783278954947e-06 -▁Toleranz 5.5045783278954947e-06 -▁nied 5.5045783278954947e-06 -▁topical 5.5045783278954947e-06 -abot 5.5007899050958418e-06 -erer 5.5007899050958418e-06 -▁Sehen 5.5007899050958418e-06 -▁bekam 5.5007899050958418e-06 -▁gases 5.5007899050958418e-06 -▁haftet 5.5007899050958418e-06 -▁makroökonom 5.5007899050958418e-06 -▁parent 5.5007899050958418e-06 -▁passenden 5.5007899050958418e-06 --Ste 5.4970014822961889e-06 -aufwand 5.4970014822961889e-06 -oa 5.4970014822961889e-06 -▁2.2 5.4970014822961889e-06 -▁Athens 5.4970014822961889e-06 -▁Nutzungs 5.4970014822961889e-06 -▁Organis 5.4970014822961889e-06 -▁acid 5.4970014822961889e-06 -▁ausgeübt 5.4970014822961889e-06 -▁bags 5.4970014822961889e-06 -▁basket 5.4970014822961889e-06 -▁criterion 5.4970014822961889e-06 -▁distr 5.4970014822961889e-06 -▁entities 5.4970014822961889e-06 -▁founder 5.4970014822961889e-06 -▁gewe 5.4970014822961889e-06 -▁nahmen 5.4970014822961889e-06 -▁rarely 5.4970014822961889e-06 -▁verbreiten 5.4970014822961889e-06 -aha 5.493213059496536e-06 -fleisch 5.493213059496536e-06 -oad 5.493213059496536e-06 -▁Chr. 5.493213059496536e-06 -▁EEC 5.493213059496536e-06 -▁Nick 5.493213059496536e-06 -▁Solidaritäts 5.493213059496536e-06 -▁Souven 5.493213059496536e-06 -▁furnishings 5.493213059496536e-06 -▁gelöscht 5.493213059496536e-06 -▁voices 5.493213059496536e-06 -dung 5.4894246366968831e-06 -hauses 5.4894246366968831e-06 -idor 5.4894246366968831e-06 -klausel 5.4894246366968831e-06 -▁Angl 5.4894246366968831e-06 -▁Koll 5.4894246366968831e-06 -▁Möglich 5.4894246366968831e-06 -▁Navig 5.4894246366968831e-06 -▁Tempel 5.4894246366968831e-06 -▁Treib 5.4894246366968831e-06 -▁engagement 5.4894246366968831e-06 -▁repression 5.4894246366968831e-06 -bö 5.48563621389723e-06 -fügt 5.48563621389723e-06 -handlung 5.48563621389723e-06 -igerung 5.48563621389723e-06 -lichsten 5.48563621389723e-06 -spezifische 5.48563621389723e-06 -▁appr 5.48563621389723e-06 -▁beside 5.48563621389723e-06 -▁envisaged 5.48563621389723e-06 -▁rigorous 5.48563621389723e-06 -erklärung 5.4818477910975774e-06 -▁Finanz- 5.4818477910975774e-06 -▁Höhepunkt 5.4818477910975774e-06 -▁Nachbarschaftspolitik 5.4818477910975774e-06 -▁Twitter 5.4818477910975774e-06 -▁affair 5.4818477910975774e-06 -▁bekräftigen 5.4818477910975774e-06 -▁harvest 5.4818477910975774e-06 --Design 5.4780593682979253e-06 -ibus 5.4780593682979253e-06 -oking 5.4780593682979253e-06 -▁Hut 5.4780593682979253e-06 -▁Nazi 5.4780593682979253e-06 -▁Sofern 5.4780593682979253e-06 -▁gründen 5.4780593682979253e-06 -▁koordinieren 5.4780593682979253e-06 -alerie 5.4742709454982724e-06 -coming 5.4742709454982724e-06 -staatliche 5.4742709454982724e-06 -ulichen 5.4742709454982724e-06 -▁A5-00 5.4742709454982724e-06 -▁CNS 5.4742709454982724e-06 -▁EWG 5.4742709454982724e-06 -▁Formul 5.4742709454982724e-06 -▁Kredite 5.4742709454982724e-06 -▁Nob 5.4742709454982724e-06 -▁Prior 5.4742709454982724e-06 -▁Publ 5.4742709454982724e-06 -▁Punta 5.4742709454982724e-06 -▁dänischen 5.4742709454982724e-06 -▁festgehalten 5.4742709454982724e-06 -▁liter 5.4742709454982724e-06 -▁tolle 5.4742709454982724e-06 -ernal 5.4704825226986195e-06 -▁IGC 5.4704825226986195e-06 -▁Indonesien 5.4704825226986195e-06 -▁Think 5.4704825226986195e-06 -▁Tschetschenien 5.4704825226986195e-06 -▁absorb 5.4704825226986195e-06 -▁breath 5.4704825226986195e-06 -▁gesetzlich 5.4704825226986195e-06 -▁gewachsen 5.4704825226986195e-06 -▁zog 5.4704825226986195e-06 -aku 5.4666940998989666e-06 -apital 5.4666940998989666e-06 -ered 5.4666940998989666e-06 -rücken 5.4666940998989666e-06 -▁Brot 5.4666940998989666e-06 -▁Datenbanken 5.4666940998989666e-06 -▁Inanspruchnahme 5.4666940998989666e-06 -▁Netzes 5.4666940998989666e-06 -▁Sax 5.4666940998989666e-06 -▁Ult 5.4666940998989666e-06 -▁Völkern 5.4666940998989666e-06 -▁arises 5.4666940998989666e-06 -▁beweist 5.4666940998989666e-06 -▁ruled 5.4666940998989666e-06 -▁touristischen 5.4666940998989666e-06 -▁vernünftigen 5.4666940998989666e-06 -V. 5.4629056770993137e-06 -adar 5.4629056770993137e-06 -netze 5.4629056770993137e-06 -strategien 5.4629056770993137e-06 -umfang 5.4629056770993137e-06 -▁Infos 5.4629056770993137e-06 -▁Räumen 5.4629056770993137e-06 -▁exclude 5.4629056770993137e-06 -▁outd 5.4629056770993137e-06 -▁Augenmerk 5.4591172542996609e-06 -▁Kommissionsmitglied 5.4591172542996609e-06 -▁Nichtregierungsorganisationen 5.4591172542996609e-06 -▁pump 5.4591172542996609e-06 -▁ran 5.4591172542996609e-06 -▁smoke 5.4591172542996609e-06 -▁stud 5.4591172542996609e-06 -▁troubl 5.4591172542996609e-06 -▁voor 5.4591172542996609e-06 -ti 5.455328831500008e-06 -verhandlungen 5.455328831500008e-06 -▁Lift 5.455328831500008e-06 -▁Viren 5.455328831500008e-06 -▁adverse 5.455328831500008e-06 -▁besichtigen 5.455328831500008e-06 -▁nachzudenken 5.455328831500008e-06 -▁veröffentlichte 5.455328831500008e-06 -▁vorausgesetzt 5.455328831500008e-06 -enken 5.4515404087003551e-06 -portal 5.4515404087003551e-06 -straßen 5.4515404087003551e-06 -ulieren 5.4515404087003551e-06 -▁Bike 5.4515404087003551e-06 -▁Chemie 5.4515404087003551e-06 -▁Muslims 5.4515404087003551e-06 -▁Railway 5.4515404087003551e-06 -▁Tran 5.4515404087003551e-06 -▁ft 5.4515404087003551e-06 -▁globe 5.4515404087003551e-06 -▁verbrauch 5.4515404087003551e-06 -▁RT 5.447751985900703e-06 -▁criminals 5.447751985900703e-06 -▁cycl 5.447751985900703e-06 -▁gültigen 5.447751985900703e-06 -▁predomin 5.447751985900703e-06 -▁vorge 5.447751985900703e-06 -cat 5.44396356310105e-06 -enke 5.44396356310105e-06 -öll 5.44396356310105e-06 -▁Boliv 5.44396356310105e-06 -▁Halt 5.44396356310105e-06 -▁Made 5.44396356310105e-06 -▁Sid 5.44396356310105e-06 -▁immerhin 5.44396356310105e-06 -▁optimieren 5.44396356310105e-06 -▁verstärkten 5.44396356310105e-06 -akh 5.4401751403013972e-06 -peicher 5.4401751403013972e-06 -ulich 5.4401751403013972e-06 -▁Dafürhalten 5.4401751403013972e-06 -▁Ms 5.4401751403013972e-06 -▁Signale 5.4401751403013972e-06 -▁Unix 5.4401751403013972e-06 -▁attempting 5.4401751403013972e-06 -▁steuern 5.4401751403013972e-06 -▁supranational 5.4401751403013972e-06 -▁verhe 5.4401751403013972e-06 --ad 5.4363867175017444e-06 -ruhen 5.4363867175017444e-06 -würdigen 5.4363867175017444e-06 -öhnung 5.4363867175017444e-06 -▁Fels 5.4363867175017444e-06 -▁Solidarity 5.4363867175017444e-06 -▁Users 5.4363867175017444e-06 -▁accommodations 5.4363867175017444e-06 -▁interpreted 5.4363867175017444e-06 -▁lux 5.4363867175017444e-06 -▁profitable 5.4363867175017444e-06 -▁religiöse 5.4363867175017444e-06 -gangs 5.4325982947020915e-06 -üger 5.4325982947020915e-06 -▁Geber 5.4325982947020915e-06 -▁Renov 5.4325982947020915e-06 -▁keyboard 5.4325982947020915e-06 -▁macro 5.4325982947020915e-06 -▁offset 5.4325982947020915e-06 --Gr 5.4288098719024386e-06 -FA 5.4288098719024386e-06 -bücher 5.4288098719024386e-06 -esco 5.4288098719024386e-06 -finder 5.4288098719024386e-06 -▁Infrastrukturen 5.4288098719024386e-06 -▁Turk 5.4288098719024386e-06 -▁legte 5.4288098719024386e-06 -▁schmerz 5.4288098719024386e-06 -begr 5.4250214491027857e-06 -▁Aparthotel 5.4250214491027857e-06 -▁customized 5.4250214491027857e-06 -▁einbringen 5.4250214491027857e-06 -emm 5.4212330263031328e-06 -▁Balance 5.4212330263031328e-06 -▁Konzerte 5.4212330263031328e-06 -▁SU 5.4212330263031328e-06 -▁Vergüt 5.4212330263031328e-06 -▁Vorbeh 5.4212330263031328e-06 -▁adequately 5.4212330263031328e-06 -▁distribute 5.4212330263031328e-06 -▁husband 5.4212330263031328e-06 -▁secr 5.4212330263031328e-06 -▁unber 5.4212330263031328e-06 -▁vertical 5.4212330263031328e-06 -▁vorüber 5.4212330263031328e-06 --Ind 5.4174446035034807e-06 -ibliothek 5.4174446035034807e-06 -▁Beförderung 5.4174446035034807e-06 -▁Busse 5.4174446035034807e-06 -▁Orientierung 5.4174446035034807e-06 -▁carriers 5.4174446035034807e-06 -▁clinical 5.4174446035034807e-06 -▁erreichten 5.4174446035034807e-06 -▁excuse 5.4174446035034807e-06 -▁indirectly 5.4174446035034807e-06 -▁jack 5.4174446035034807e-06 -▁matches 5.4174446035034807e-06 -▁sincere 5.4174446035034807e-06 -▁zukunfts 5.4174446035034807e-06 -▁östlichen 5.4174446035034807e-06 -ounts 5.4136561807038279e-06 -shore 5.4136561807038279e-06 -steuerung 5.4136561807038279e-06 -▁Especially 5.4136561807038279e-06 -▁Kernenergie 5.4136561807038279e-06 -▁Spielern 5.4136561807038279e-06 -▁bands 5.4136561807038279e-06 -▁fünfzehn 5.4136561807038279e-06 -▁qualifizierten 5.4136561807038279e-06 -▁schwach 5.4136561807038279e-06 -▁unzul 5.4136561807038279e-06 -▁vente 5.4136561807038279e-06 -ammelt 5.409867757904175e-06 -hu 5.409867757904175e-06 -nten 5.409867757904175e-06 -▁Jahrhunderte 5.409867757904175e-06 -▁Kompromisse 5.409867757904175e-06 -▁Stalin 5.409867757904175e-06 -▁dimin 5.409867757904175e-06 -▁erfor 5.409867757904175e-06 -▁inhalt 5.409867757904175e-06 -▁nearest 5.409867757904175e-06 -inne 5.4060793351045221e-06 -▁Ansätze 5.4060793351045221e-06 -▁Flach 5.4060793351045221e-06 -▁Gesetzen 5.4060793351045221e-06 -▁Jer 5.4060793351045221e-06 -▁bul 5.4060793351045221e-06 -▁ceremony 5.4060793351045221e-06 -▁coins 5.4060793351045221e-06 -▁gesetzliche 5.4060793351045221e-06 -▁harmonised 5.4060793351045221e-06 -▁stones 5.4060793351045221e-06 --Bar 5.4022909123048692e-06 -61 5.4022909123048692e-06 -investitionen 5.4022909123048692e-06 -▁Autob 5.4022909123048692e-06 -▁Bulgarian 5.4022909123048692e-06 -▁conscious 5.4022909123048692e-06 -▁cotton 5.4022909123048692e-06 -▁dear 5.4022909123048692e-06 -▁frühere 5.4022909123048692e-06 -reens 5.3985024895052163e-06 -verkauf 5.3985024895052163e-06 -volumen 5.3985024895052163e-06 -▁Ly 5.3985024895052163e-06 -▁Mitbürger 5.3985024895052163e-06 -▁Solarium 5.3985024895052163e-06 -▁Studies 5.3985024895052163e-06 -▁Variante 5.3985024895052163e-06 -▁resolving 5.3985024895052163e-06 --year-old 5.3947140667055634e-06 -baut 5.3947140667055634e-06 -edit 5.3947140667055634e-06 -ierendes 5.3947140667055634e-06 -ssp 5.3947140667055634e-06 -▁Dipl 5.3947140667055634e-06 -▁Graf 5.3947140667055634e-06 -▁Spiels 5.3947140667055634e-06 -▁Vielmehr 5.3947140667055634e-06 -▁anderswo 5.3947140667055634e-06 -▁beig 5.3947140667055634e-06 -▁gehörte 5.3947140667055634e-06 -▁italien 5.3947140667055634e-06 -▁pir 5.3947140667055634e-06 -orous 5.3909256439059105e-06 -ziehung 5.3909256439059105e-06 -▁Entfern 5.3909256439059105e-06 -▁Ferienhaus 5.3909256439059105e-06 -▁Messenger 5.3909256439059105e-06 -▁Officer 5.3909256439059105e-06 -▁PSE-Fraktion 5.3909256439059105e-06 -▁Pav 5.3909256439059105e-06 -▁Poll 5.3909256439059105e-06 -▁Telekommunikation 5.3909256439059105e-06 -▁Tu 5.3909256439059105e-06 -▁hazardous 5.3909256439059105e-06 -▁statute 5.3909256439059105e-06 -▁UPS 5.3871372211062585e-06 -▁engagieren 5.3871372211062585e-06 -▁erb 5.3871372211062585e-06 -▁geschmackvoll 5.3871372211062585e-06 -▁investig 5.3871372211062585e-06 -▁recy 5.3871372211062585e-06 -▁transmit 5.3871372211062585e-06 -▁verläss 5.3871372211062585e-06 -▁äuß 5.3871372211062585e-06 -grundlage 5.3833487983066056e-06 -gue 5.3833487983066056e-06 -uggl 5.3833487983066056e-06 -ég 5.3833487983066056e-06 -▁Donau 5.3833487983066056e-06 -▁Ruh 5.3833487983066056e-06 -▁asks 5.3833487983066056e-06 -▁bemerkt 5.3833487983066056e-06 -▁dependence 5.3833487983066056e-06 -▁überw 5.3833487983066056e-06 -ilfe 5.3795603755069527e-06 -izin 5.3795603755069527e-06 -otto 5.3795603755069527e-06 -▁Baus 5.3795603755069527e-06 -▁Entwicklungspolitik 5.3795603755069527e-06 -▁Eurocopter 5.3795603755069527e-06 -▁deriv 5.3795603755069527e-06 -▁elf 5.3795603755069527e-06 -▁englisch 5.3795603755069527e-06 -▁formerly 5.3795603755069527e-06 -▁weitem 5.3795603755069527e-06 -craft 5.3757719527073e-06 -ifizierten 5.3757719527073e-06 -ounded 5.3757719527073e-06 -ährungs 5.3757719527073e-06 -▁Auss 5.3757719527073e-06 -▁Schwimm 5.3757719527073e-06 -▁Upon 5.3757719527073e-06 -▁Valent 5.3757719527073e-06 -▁compr 5.3757719527073e-06 -▁esc 5.3757719527073e-06 -▁fib 5.3757719527073e-06 -▁fortgeschritten 5.3757719527073e-06 -▁instability 5.3757719527073e-06 -ervices 5.3719835299076469e-06 -essourcen 5.3719835299076469e-06 -gebir 5.3719835299076469e-06 -ington 5.3719835299076469e-06 -otr 5.3719835299076469e-06 -▁Comfort 5.3719835299076469e-06 -▁Freedom 5.3719835299076469e-06 -▁adjustments 5.3719835299076469e-06 -▁eignen 5.3719835299076469e-06 -▁petr 5.3719835299076469e-06 -▁reasonably 5.3719835299076469e-06 -▁thir 5.3719835299076469e-06 -▁vorbereiten 5.3719835299076469e-06 -▁überraschen 5.3719835299076469e-06 -LIN 5.368195107107994e-06 -effekt 5.368195107107994e-06 -imb 5.368195107107994e-06 -▁Einbindung 5.368195107107994e-06 -▁Internetseiten 5.368195107107994e-06 -▁Rechtssicherheit 5.368195107107994e-06 -▁barbar 5.368195107107994e-06 -▁hinzuzufügen 5.368195107107994e-06 -▁imperative 5.368195107107994e-06 -▁saubere 5.368195107107994e-06 -▁validity 5.368195107107994e-06 ----- 5.3644066843083411e-06 -fassen 5.3644066843083411e-06 -olved 5.3644066843083411e-06 -ál 5.3644066843083411e-06 -▁Harr 5.3644066843083411e-06 -▁Insofern 5.3644066843083411e-06 -▁Trav 5.3644066843083411e-06 -▁befür 5.3644066843083411e-06 -▁unterschied 5.3644066843083411e-06 -▁Vegas 5.3606182615086882e-06 -▁accessed 5.3606182615086882e-06 -▁breaking 5.3606182615086882e-06 -▁distant 5.3606182615086882e-06 -▁einsetzt 5.3606182615086882e-06 -▁ernannt 5.3606182615086882e-06 -▁literally 5.3606182615086882e-06 -▁stating 5.3606182615086882e-06 -▁vorzubereiten 5.3606182615086882e-06 --Port 5.3568298387090362e-06 -23 5.3568298387090362e-06 -cons 5.3568298387090362e-06 -ergy 5.3568298387090362e-06 -gal 5.3568298387090362e-06 -schul 5.3568298387090362e-06 -▁142 5.3568298387090362e-06 -▁behold 5.3568298387090362e-06 -acies 5.3530414159093833e-06 -üter 5.3530414159093833e-06 -▁Naturkatastrophen 5.3530414159093833e-06 -▁Written 5.3530414159093833e-06 -▁abilities 5.3530414159093833e-06 -▁autonomous 5.3530414159093833e-06 -▁determining 5.3530414159093833e-06 -▁erfassen 5.3530414159093833e-06 -books 5.34925299310973e-06 -deckt 5.34925299310973e-06 -optionen 5.34925299310973e-06 -reams 5.34925299310973e-06 -vermögen 5.34925299310973e-06 -▁Artikels 5.34925299310973e-06 -▁Indikatoren 5.34925299310973e-06 -▁Stream 5.34925299310973e-06 -▁Szene 5.34925299310973e-06 -▁Unless 5.34925299310973e-06 -▁Zealand 5.34925299310973e-06 -▁asp 5.34925299310973e-06 -▁congratulating 5.34925299310973e-06 -▁maßgeschneider 5.34925299310973e-06 -ernity 5.3454645703100775e-06 -ipt 5.3454645703100775e-06 -onian 5.3454645703100775e-06 -ources 5.3454645703100775e-06 -▁Passagiere 5.3454645703100775e-06 -▁Plugin 5.3454645703100775e-06 -▁Ramblas 5.3454645703100775e-06 -▁civilians 5.3454645703100775e-06 -▁theoretical 5.3454645703100775e-06 -aments 5.3416761475104246e-06 -angt 5.3416761475104246e-06 -errasse 5.3416761475104246e-06 -geist 5.3416761475104246e-06 -glichen 5.3416761475104246e-06 -lessly 5.3416761475104246e-06 -weiß 5.3416761475104246e-06 -▁Erneuerung 5.3416761475104246e-06 -▁Latvia 5.3416761475104246e-06 -▁MED 5.3416761475104246e-06 -▁Rose 5.3416761475104246e-06 -▁Terrace 5.3416761475104246e-06 -▁letztes 5.3416761475104246e-06 -▁steigenden 5.3416761475104246e-06 -otos 5.3378877247107717e-06 -▁Photoshop 5.3378877247107717e-06 -▁Production 5.3378877247107717e-06 -▁Telekommunikations 5.3378877247107717e-06 -▁dauerhaften 5.3378877247107717e-06 -▁verlängert 5.3378877247107717e-06 -▁visas 5.3378877247107717e-06 -▁vorzuschlagen 5.3378877247107717e-06 -Card 5.3340993019111188e-06 -▁Frucht 5.3340993019111188e-06 -▁MAR 5.3340993019111188e-06 -▁Steinberg 5.3340993019111188e-06 -▁TE 5.3340993019111188e-06 -▁Verfassungsvertrag 5.3340993019111188e-06 -▁fortzusetzen 5.3340993019111188e-06 -▁günstiger 5.3340993019111188e-06 -▁invoice 5.3340993019111188e-06 --sp 5.3303108791114659e-06 -enta 5.3303108791114659e-06 -erd 5.3303108791114659e-06 -ifier 5.3303108791114659e-06 -imer 5.3303108791114659e-06 -kenntnisse 5.3303108791114659e-06 -l. 5.3303108791114659e-06 -lichung 5.3303108791114659e-06 -onden 5.3303108791114659e-06 -▁Andrew 5.3303108791114659e-06 -▁Cyber 5.3303108791114659e-06 -▁ungerecht 5.3303108791114659e-06 -▁workplace 5.3303108791114659e-06 --CD 5.3265224563118139e-06 -ehens 5.3265224563118139e-06 -günst 5.3265224563118139e-06 -olic 5.3265224563118139e-06 -sprü 5.3265224563118139e-06 -▁Kreatur 5.3265224563118139e-06 -▁Stellungnahmen 5.3265224563118139e-06 -▁englische 5.3265224563118139e-06 -▁fliegen 5.3265224563118139e-06 -▁harten 5.3265224563118139e-06 -▁identifizieren 5.3265224563118139e-06 -▁layers 5.3265224563118139e-06 -▁semi- 5.3265224563118139e-06 -muster 5.322734033512161e-06 -utzten 5.322734033512161e-06 -▁Altern 5.322734033512161e-06 -▁Bundesrepublik 5.322734033512161e-06 -▁EV 5.322734033512161e-06 -▁Garant 5.322734033512161e-06 -▁disagre 5.322734033512161e-06 -▁erzählt 5.322734033512161e-06 -▁vors 5.322734033512161e-06 -ammern 5.3189456107125081e-06 -ape 5.3189456107125081e-06 -kammer 5.3189456107125081e-06 -orious 5.3189456107125081e-06 -zuhalten 5.3189456107125081e-06 -▁Demokratien 5.3189456107125081e-06 -▁Korrekt 5.3189456107125081e-06 -▁Pots 5.3189456107125081e-06 -▁linear 5.3189456107125081e-06 -▁loaded 5.3189456107125081e-06 -▁pillars 5.3189456107125081e-06 -▁restrictive 5.3189456107125081e-06 -▁samples 5.3189456107125081e-06 -alys 5.3151571879128552e-06 -bereitschaft 5.3151571879128552e-06 -eutral 5.3151571879128552e-06 -ictionary 5.3151571879128552e-06 -▁Dark 5.3151571879128552e-06 -▁Landwirtschafts 5.3151571879128552e-06 -▁analyse 5.3151571879128552e-06 -▁beacht 5.3151571879128552e-06 -▁grey 5.3151571879128552e-06 -▁precond 5.3151571879128552e-06 -▁thereof 5.3151571879128552e-06 -Sp 5.3113687651132023e-06 -industr 5.3113687651132023e-06 -rot 5.3113687651132023e-06 -ubb 5.3113687651132023e-06 -wünsche 5.3113687651132023e-06 -▁Ansprechpartner 5.3113687651132023e-06 -▁Creative 5.3113687651132023e-06 -▁Jacuzzi 5.3113687651132023e-06 -▁Mechan 5.3113687651132023e-06 -▁Rücksicht 5.3113687651132023e-06 -▁TW 5.3113687651132023e-06 -▁Zinsen 5.3113687651132023e-06 -▁hängen 5.3113687651132023e-06 -▁renovierte 5.3113687651132023e-06 -▁sensitivity 5.3113687651132023e-06 -burger 5.3075803423135494e-06 -förm 5.3075803423135494e-06 -verfolg 5.3075803423135494e-06 -ет 5.3075803423135494e-06 -▁Gesetzgeber 5.3075803423135494e-06 -▁Intelle 5.3075803423135494e-06 -▁Natura 5.3075803423135494e-06 -▁healing 5.3075803423135494e-06 -▁nachzu 5.3075803423135494e-06 -▁tries 5.3075803423135494e-06 -▁weiten 5.3075803423135494e-06 -▁woods 5.3075803423135494e-06 -undert 5.3037919195138965e-06 -▁Cru 5.3037919195138965e-06 -▁Learn 5.3037919195138965e-06 -▁Termine 5.3037919195138965e-06 -▁angle 5.3037919195138965e-06 -▁beglückwünsche 5.3037919195138965e-06 -▁blend 5.3037919195138965e-06 -▁counts 5.3037919195138965e-06 -▁tighten 5.3037919195138965e-06 -icion 5.3000034967142445e-06 -igste 5.3000034967142445e-06 -ürt 5.3000034967142445e-06 -▁Brüder 5.3000034967142445e-06 -▁Vorbehalte 5.3000034967142445e-06 -▁Whilst 5.3000034967142445e-06 -▁aufgeworfen 5.3000034967142445e-06 -▁classified 5.3000034967142445e-06 -▁counterfeit 5.3000034967142445e-06 -▁cow 5.3000034967142445e-06 -▁definitions 5.3000034967142445e-06 -▁delle 5.3000034967142445e-06 -▁eff 5.3000034967142445e-06 -▁innocent 5.3000034967142445e-06 -▁pointing 5.3000034967142445e-06 -▁scrutiny 5.3000034967142445e-06 -▁tender 5.3000034967142445e-06 -GEL 5.2962150739145916e-06 -endern 5.2962150739145916e-06 -sprodukt 5.2962150739145916e-06 -teilt 5.2962150739145916e-06 -▁Demokratisierung 5.2962150739145916e-06 -▁erkennbar 5.2962150739145916e-06 -▁mangelnde 5.2962150739145916e-06 -beihilfen 5.2924266511149387e-06 -ielt 5.2924266511149387e-06 -konfl 5.2924266511149387e-06 -reif 5.2924266511149387e-06 -ähren 5.2924266511149387e-06 -▁Fragest 5.2924266511149387e-06 -▁Interpretation 5.2924266511149387e-06 -▁Komponente 5.2924266511149387e-06 -▁Spazier 5.2924266511149387e-06 -▁readers 5.2924266511149387e-06 -inations 5.2886382283152858e-06 -ladimir 5.2886382283152858e-06 -reicher 5.2886382283152858e-06 -▁Geschäftsführer 5.2886382283152858e-06 -▁Hongkong 5.2886382283152858e-06 -▁formally 5.2886382283152858e-06 -▁koordiniert 5.2886382283152858e-06 -▁mangeln 5.2886382283152858e-06 -eline 5.2848498055156329e-06 -liefer 5.2848498055156329e-06 -▁Bayern 5.2848498055156329e-06 -▁Drucker 5.2848498055156329e-06 -▁Euroraum 5.2848498055156329e-06 -▁Kleidung 5.2848498055156329e-06 -▁Linken 5.2848498055156329e-06 -▁NOR 5.2848498055156329e-06 -▁Zwischenzeit 5.2848498055156329e-06 -▁fold 5.2848498055156329e-06 -▁fühlt 5.2848498055156329e-06 -▁jazz 5.2848498055156329e-06 -▁passive 5.2848498055156329e-06 -▁plates 5.2848498055156329e-06 -▁preference 5.2848498055156329e-06 -▁vertrieben 5.2848498055156329e-06 -▁wished 5.2848498055156329e-06 -ket 5.28106138271598e-06 -▁Newspapers 5.28106138271598e-06 -▁Rhine 5.28106138271598e-06 -▁Roy 5.28106138271598e-06 -▁abzulehnen 5.28106138271598e-06 -▁advised 5.28106138271598e-06 -▁ageing 5.28106138271598e-06 -▁cathedral 5.28106138271598e-06 -▁examining 5.28106138271598e-06 -▁festhalten 5.28106138271598e-06 -▁hind 5.28106138271598e-06 -▁machten 5.28106138271598e-06 -▁reactions 5.28106138271598e-06 -▁securing 5.28106138271598e-06 -▁tägliche 5.28106138271598e-06 -▁wiederhole 5.28106138271598e-06 -dir 5.2772729599163271e-06 -iety 5.2772729599163271e-06 -ilities 5.2772729599163271e-06 -uftrag 5.2772729599163271e-06 -▁Beat 5.2772729599163271e-06 -▁HTTP 5.2772729599163271e-06 -▁Sin 5.2772729599163271e-06 -▁admir 5.2772729599163271e-06 -▁afr 5.2772729599163271e-06 -▁interrupt 5.2772729599163271e-06 -gipfel 5.2734845371166743e-06 -igel 5.2734845371166743e-06 -stät 5.2734845371166743e-06 -▁73 5.2734845371166743e-06 -▁Eintritts 5.2734845371166743e-06 -▁Strassen 5.2734845371166743e-06 -▁beding 5.2734845371166743e-06 -▁bedürfen 5.2734845371166743e-06 -▁berührt 5.2734845371166743e-06 -▁supporters 5.2734845371166743e-06 -▁surgery 5.2734845371166743e-06 -▁unvermeid 5.2734845371166743e-06 -ITE 5.2696961143170222e-06 -aison 5.2696961143170222e-06 -ancer 5.2696961143170222e-06 -maßen 5.2696961143170222e-06 -öch 5.2696961143170222e-06 -öpft 5.2696961143170222e-06 -▁Adressen 5.2696961143170222e-06 -▁Gipfels 5.2696961143170222e-06 -▁PCI 5.2696961143170222e-06 -▁Photos 5.2696961143170222e-06 -▁Pia 5.2696961143170222e-06 -▁Posit 5.2696961143170222e-06 -▁Stress 5.2696961143170222e-06 -▁audiovis 5.2696961143170222e-06 -▁critique 5.2696961143170222e-06 -▁fulfill 5.2696961143170222e-06 -▁tonight 5.2696961143170222e-06 -erungen 5.2659076915173693e-06 -regelungen 5.2659076915173693e-06 -suche 5.2659076915173693e-06 -▁Ali 5.2659076915173693e-06 -▁Nerv 5.2659076915173693e-06 -▁Porsche 5.2659076915173693e-06 -▁Resultat 5.2659076915173693e-06 -▁certificates 5.2659076915173693e-06 -▁deaktiv 5.2659076915173693e-06 -▁diversen 5.2659076915173693e-06 -▁grat 5.2659076915173693e-06 -▁imag 5.2659076915173693e-06 -▁non-governmental 5.2659076915173693e-06 -d. 5.2621192687177164e-06 -ertal 5.2621192687177164e-06 -icism 5.2621192687177164e-06 -strecke 5.2621192687177164e-06 -▁1972 5.2621192687177164e-06 -▁Kandid 5.2621192687177164e-06 -▁Provider 5.2621192687177164e-06 -▁Technolog 5.2621192687177164e-06 -▁attraktiv 5.2621192687177164e-06 -▁aufweist 5.2621192687177164e-06 -▁distinguish 5.2621192687177164e-06 -▁seitdem 5.2621192687177164e-06 -▁unanimity 5.2621192687177164e-06 -▁zivilen 5.2621192687177164e-06 -beratung 5.2583308459180635e-06 -erzeugung 5.2583308459180635e-06 -estand 5.2583308459180635e-06 -flotte 5.2583308459180635e-06 -gles 5.2583308459180635e-06 -user 5.2583308459180635e-06 -▁Italiens 5.2583308459180635e-06 -▁Therapie 5.2583308459180635e-06 -▁Weis 5.2583308459180635e-06 -▁counterpart 5.2583308459180635e-06 -▁geachtet 5.2583308459180635e-06 -flation 5.2545424231184106e-06 -gg 5.2545424231184106e-06 -öm 5.2545424231184106e-06 -▁Ganze 5.2545424231184106e-06 -▁Gibraltar 5.2545424231184106e-06 -▁Maßstab 5.2545424231184106e-06 -▁Offiz 5.2545424231184106e-06 -▁Rettungs 5.2545424231184106e-06 -▁Trep 5.2545424231184106e-06 -▁audiovisual 5.2545424231184106e-06 -▁cha 5.2545424231184106e-06 -▁couples 5.2545424231184106e-06 -▁dahingehend 5.2545424231184106e-06 -▁shooting 5.2545424231184106e-06 -▁studying 5.2545424231184106e-06 -▁tv 5.2545424231184106e-06 -▁wertvollen 5.2545424231184106e-06 -▁wohin 5.2545424231184106e-06 -▁zuverlässige 5.2545424231184106e-06 --199 5.2507540003187578e-06 -rapp 5.2507540003187578e-06 -step 5.2507540003187578e-06 -ő 5.2507540003187578e-06 -▁Artikeln 5.2507540003187578e-06 -▁Breite 5.2507540003187578e-06 -▁Gramm 5.2507540003187578e-06 -▁Kraftstoff 5.2507540003187578e-06 -▁Pag 5.2507540003187578e-06 -▁Progn 5.2507540003187578e-06 -▁Zutaten 5.2507540003187578e-06 -▁attrib 5.2507540003187578e-06 -▁ausgestellt 5.2507540003187578e-06 -▁breaks 5.2507540003187578e-06 -▁consolidated 5.2507540003187578e-06 -▁geschlossenen 5.2507540003187578e-06 -▁transported 5.2507540003187578e-06 --down 5.2469655775191049e-06 -HT 5.2469655775191049e-06 -finanzierung 5.2469655775191049e-06 -ingo 5.2469655775191049e-06 -opoul 5.2469655775191049e-06 -wig 5.2469655775191049e-06 -▁Erstellen 5.2469655775191049e-06 -▁Kolonial 5.2469655775191049e-06 -▁Verstoß 5.2469655775191049e-06 -▁concludes 5.2469655775191049e-06 -▁interference 5.2469655775191049e-06 -▁unum 5.2469655775191049e-06 -stab 5.243177154719452e-06 -▁Gefähr 5.243177154719452e-06 -▁Hauptziel 5.243177154719452e-06 -▁Jahrzehnt 5.243177154719452e-06 -▁Privileg 5.243177154719452e-06 -▁bedauer 5.243177154719452e-06 -▁compromises 5.243177154719452e-06 -▁disastrous 5.243177154719452e-06 -▁execute 5.243177154719452e-06 -▁sav 5.243177154719452e-06 -▁variables 5.243177154719452e-06 -▁wanting 5.243177154719452e-06 -ivals 5.2393887319198e-06 -körper 5.2393887319198e-06 -nete 5.2393887319198e-06 -struction 5.2393887319198e-06 -veranst 5.2393887319198e-06 -vereinbar 5.2393887319198e-06 -▁Banglad 5.2393887319198e-06 -▁Congo 5.2393887319198e-06 -▁Ganzen 5.2393887319198e-06 -▁Secure 5.2393887319198e-06 -▁gepflegt 5.2393887319198e-06 -▁unterstrichen 5.2393887319198e-06 -amment 5.235600309120147e-06 -▁Lyon 5.235600309120147e-06 -▁P. 5.235600309120147e-06 -▁Subjekt 5.235600309120147e-06 -▁Verband 5.235600309120147e-06 -▁Whit 5.235600309120147e-06 -▁Zuf 5.235600309120147e-06 -▁comfortably 5.235600309120147e-06 -ektoren 5.2318118863204941e-06 -information 5.2318118863204941e-06 -ingu 5.2318118863204941e-06 -iple 5.2318118863204941e-06 -▁Cer 5.2318118863204941e-06 -▁Entschließungsanträge 5.2318118863204941e-06 -▁Hospital 5.2318118863204941e-06 -▁Know 5.2318118863204941e-06 -▁MM 5.2318118863204941e-06 -▁Sahara 5.2318118863204941e-06 -▁arrang 5.2318118863204941e-06 -▁außergewöhnlichen 5.2318118863204941e-06 -▁interventions 5.2318118863204941e-06 -▁preservation 5.2318118863204941e-06 -▁refugee 5.2318118863204941e-06 -▁reiche 5.2318118863204941e-06 -▁ét 5.2318118863204941e-06 -▁Adria 5.2280234635208412e-06 -▁Kapit 5.2280234635208412e-06 -▁amtierende 5.2280234635208412e-06 -▁amtierenden 5.2280234635208412e-06 -▁ausgenommen 5.2280234635208412e-06 -▁galleries 5.2280234635208412e-06 -▁geschätz 5.2280234635208412e-06 -▁moralische 5.2280234635208412e-06 -▁promot 5.2280234635208412e-06 -▁unjust 5.2280234635208412e-06 -eased 5.2242350407211884e-06 -illen 5.2242350407211884e-06 -▁Mario 5.2242350407211884e-06 -▁Verbr 5.2242350407211884e-06 -▁ausmachen 5.2242350407211884e-06 -▁bezaubern 5.2242350407211884e-06 -▁clar 5.2242350407211884e-06 -▁dün 5.2242350407211884e-06 -▁erre 5.2242350407211884e-06 -▁regr 5.2242350407211884e-06 -orship 5.2204466179215355e-06 -▁Disabled 5.2204466179215355e-06 -▁Erdgas 5.2204466179215355e-06 -▁MO 5.2204466179215355e-06 -▁hört 5.2204466179215355e-06 -▁inj 5.2204466179215355e-06 -▁russ 5.2204466179215355e-06 -▁safeguarding 5.2204466179215355e-06 -▁verbindlich 5.2204466179215355e-06 -,9 5.2166581951218826e-06 -appe 5.2166581951218826e-06 -firmen 5.2166581951218826e-06 -iffe 5.2166581951218826e-06 -▁Gegebenheiten 5.2166581951218826e-06 -▁Popular 5.2166581951218826e-06 -▁plot 5.2166581951218826e-06 -▁protests 5.2166581951218826e-06 -HA 5.21286977232223e-06 -appar 5.21286977232223e-06 -igern 5.21286977232223e-06 -▁Bath 5.21286977232223e-06 -▁Cover 5.21286977232223e-06 -▁Crus 5.21286977232223e-06 -▁Looking 5.21286977232223e-06 -▁Wandern 5.21286977232223e-06 -▁autom 5.21286977232223e-06 -▁gesucht 5.21286977232223e-06 -▁pair 5.21286977232223e-06 -▁Alternativ 5.2090813495225776e-06 -▁Auflagen 5.2090813495225776e-06 -▁Finanzrahmen 5.2090813495225776e-06 -▁Ken 5.2090813495225776e-06 -▁Manuel 5.2090813495225776e-06 -▁Prost 5.2090813495225776e-06 -▁Redezeit 5.2090813495225776e-06 -▁Wur 5.2090813495225776e-06 -▁abzuschließen 5.2090813495225776e-06 -▁queries 5.2090813495225776e-06 -▁schien 5.2090813495225776e-06 --Hand 5.2052929267229247e-06 -gehend 5.2052929267229247e-06 -olz 5.2052929267229247e-06 -performance 5.2052929267229247e-06 -▁Besetzung 5.2052929267229247e-06 -▁Cit 5.2052929267229247e-06 -▁Gewährung 5.2052929267229247e-06 -▁Medina 5.2052929267229247e-06 -▁ecos 5.2052929267229247e-06 -▁inviting 5.2052929267229247e-06 -▁sla 5.2052929267229247e-06 -Hz 5.2015045039232719e-06 -ahrscheinlich 5.2015045039232719e-06 -bauten 5.2015045039232719e-06 -chitektur 5.2015045039232719e-06 -▁Hunde 5.2015045039232719e-06 -▁Zen 5.2015045039232719e-06 -▁chemischen 5.2015045039232719e-06 -▁echo 5.2015045039232719e-06 -▁mini- 5.2015045039232719e-06 -▁monopoly 5.2015045039232719e-06 --Unternehmen 5.197716081123619e-06 -easing 5.197716081123619e-06 -leuten 5.197716081123619e-06 -wärtigen 5.197716081123619e-06 -ässer 5.197716081123619e-06 -▁79 5.197716081123619e-06 -▁Adr 5.197716081123619e-06 -▁L. 5.197716081123619e-06 -▁Manchester 5.197716081123619e-06 -▁Naturschutz 5.197716081123619e-06 -▁Später 5.197716081123619e-06 -▁Strateg 5.197716081123619e-06 -▁downloads 5.197716081123619e-06 -▁encomp 5.197716081123619e-06 -▁norm 5.197716081123619e-06 -▁schwerwie 5.197716081123619e-06 -▁transatlantischen 5.197716081123619e-06 -▁verify 5.197716081123619e-06 -▁750 5.1939276583239661e-06 -▁Importe 5.1939276583239661e-06 -▁Moldau 5.1939276583239661e-06 -▁anzugehen 5.1939276583239661e-06 -▁buyer 5.1939276583239661e-06 -▁denkt 5.1939276583239661e-06 -▁disregard 5.1939276583239661e-06 -▁enlar 5.1939276583239661e-06 -▁inspect 5.1939276583239661e-06 -▁integrating 5.1939276583239661e-06 -▁intimate 5.1939276583239661e-06 -▁intuitive 5.1939276583239661e-06 -▁lehnen 5.1939276583239661e-06 -▁letzt 5.1939276583239661e-06 -▁minibar 5.1939276583239661e-06 -▁revolutionary 5.1939276583239661e-06 -▁sympathy 5.1939276583239661e-06 -▁trans-European 5.1939276583239661e-06 -ASE 5.1901392355243132e-06 -affung 5.1901392355243132e-06 -ellers 5.1901392355243132e-06 -ergel 5.1901392355243132e-06 -▁Mercosur 5.1901392355243132e-06 -▁Riesen 5.1901392355243132e-06 -▁anch 5.1901392355243132e-06 -▁annually 5.1901392355243132e-06 -▁desp 5.1901392355243132e-06 -▁wirft 5.1901392355243132e-06 -cking 5.18635081272466e-06 -cription 5.18635081272466e-06 -fügen 5.18635081272466e-06 -ritical 5.18635081272466e-06 -▁Inflations 5.18635081272466e-06 -▁Sydney 5.18635081272466e-06 -▁Vice 5.18635081272466e-06 -▁entlassen 5.18635081272466e-06 -▁multif 5.18635081272466e-06 -▁thinks 5.18635081272466e-06 -ASA 5.1825623899250074e-06 -icked 5.1825623899250074e-06 -ream 5.1825623899250074e-06 -rieren 5.1825623899250074e-06 -ρ 5.1825623899250074e-06 -▁Persönlichkeiten 5.1825623899250074e-06 -▁Spirit 5.1825623899250074e-06 -▁Tiefe 5.1825623899250074e-06 -▁behindert 5.1825623899250074e-06 -▁belt 5.1825623899250074e-06 -▁consultations 5.1825623899250074e-06 -▁erober 5.1825623899250074e-06 -▁fortschritt 5.1825623899250074e-06 -▁mem 5.1825623899250074e-06 -▁modernsten 5.1825623899250074e-06 -▁perception 5.1825623899250074e-06 -auscht 5.1787739671253554e-06 -leist 5.1787739671253554e-06 -leiste 5.1787739671253554e-06 -zahlt 5.1787739671253554e-06 -▁Festivals 5.1787739671253554e-06 -▁Hav 5.1787739671253554e-06 -▁Male 5.1787739671253554e-06 -▁Nich 5.1787739671253554e-06 -▁Rating 5.1787739671253554e-06 -▁Staatsbürger 5.1787739671253554e-06 -▁corporations 5.1787739671253554e-06 -▁europaweit 5.1787739671253554e-06 -▁kar 5.1787739671253554e-06 -▁pra 5.1787739671253554e-06 -▁refusal 5.1787739671253554e-06 -uropäische 5.1749855443257025e-06 -▁Cour 5.1749855443257025e-06 -▁Folgendes 5.1749855443257025e-06 -▁Steel 5.1749855443257025e-06 -▁Stop 5.1749855443257025e-06 -▁US-amer 5.1749855443257025e-06 -▁accompanying 5.1749855443257025e-06 -▁beachtet 5.1749855443257025e-06 -▁elite 5.1749855443257025e-06 -▁erfahrenen 5.1749855443257025e-06 -▁immen 5.1749855443257025e-06 -▁linking 5.1749855443257025e-06 -▁unweit 5.1749855443257025e-06 -metr 5.1711971215260496e-06 -sproblem 5.1711971215260496e-06 -▁Again 5.1711971215260496e-06 -▁Democracy 5.1711971215260496e-06 -▁Raumfahrt 5.1711971215260496e-06 -▁TB 5.1711971215260496e-06 -▁Zivilisation 5.1711971215260496e-06 -▁bekämpft 5.1711971215260496e-06 -▁gebrauch 5.1711971215260496e-06 -▁geschw 5.1711971215260496e-06 -▁incomes 5.1711971215260496e-06 -▁overwhelming 5.1711971215260496e-06 -▁verteilen 5.1711971215260496e-06 -▁wahrgenommen 5.1711971215260496e-06 -ael 5.1674086987263967e-06 -amas 5.1674086987263967e-06 -forest 5.1674086987263967e-06 -inj 5.1674086987263967e-06 -olitik 5.1674086987263967e-06 -ränken 5.1674086987263967e-06 -önt 5.1674086987263967e-06 -▁Bavaria 5.1674086987263967e-06 -▁Gesellschafts 5.1674086987263967e-06 -▁Lands 5.1674086987263967e-06 -▁Segment 5.1674086987263967e-06 -▁breeding 5.1674086987263967e-06 -▁feste 5.1674086987263967e-06 -▁limitation 5.1674086987263967e-06 -▁php 5.1674086987263967e-06 -▁shore 5.1674086987263967e-06 -▁surfaces 5.1674086987263967e-06 --Raum 5.1636202759267438e-06 -▁Laufzeit 5.1636202759267438e-06 -▁Method 5.1636202759267438e-06 -▁Oft 5.1636202759267438e-06 -▁automatischen 5.1636202759267438e-06 -▁katastrophal 5.1636202759267438e-06 -▁ours 5.1636202759267438e-06 -▁panorama 5.1636202759267438e-06 -▁wunderbaren 5.1636202759267438e-06 -abling 5.1598318531270909e-06 -anwendungen 5.1598318531270909e-06 -auswahl 5.1598318531270909e-06 -echan 5.1598318531270909e-06 -enntnissen 5.1598318531270909e-06 -gäng 5.1598318531270909e-06 -▁Begeisterung 5.1598318531270909e-06 -▁Contract 5.1598318531270909e-06 -▁Dog 5.1598318531270909e-06 -▁Guatemala 5.1598318531270909e-06 -▁Vic 5.1598318531270909e-06 -▁Zeile 5.1598318531270909e-06 -▁medicinal 5.1598318531270909e-06 -▁protective 5.1598318531270909e-06 -▁strikes 5.1598318531270909e-06 -erkt 5.156043430327438e-06 -iedlung 5.156043430327438e-06 -oir 5.156043430327438e-06 -pak 5.156043430327438e-06 -ulent 5.156043430327438e-06 -▁81 5.156043430327438e-06 -▁Richtigkeit 5.156043430327438e-06 -▁Werde 5.156043430327438e-06 -▁Zentrums 5.156043430327438e-06 -▁democracies 5.156043430327438e-06 -▁hung 5.156043430327438e-06 -▁zug 5.156043430327438e-06 -▁у 5.156043430327438e-06 -üs 5.1522550075277851e-06 -С 5.1522550075277851e-06 -▁Hoffnungen 5.1522550075277851e-06 -▁Städt 5.1522550075277851e-06 -▁Vorstands 5.1522550075277851e-06 -▁beobachtet 5.1522550075277851e-06 -▁direkter 5.1522550075277851e-06 -▁horm 5.1522550075277851e-06 -▁regelmäßige 5.1522550075277851e-06 -▁schlimmsten 5.1522550075277851e-06 -ICE 5.1484665847281331e-06 -ehlen 5.1484665847281331e-06 -gehörige 5.1484665847281331e-06 -ördern 5.1484665847281331e-06 -▁Frances 5.1484665847281331e-06 -▁Komplexität 5.1484665847281331e-06 -▁Vieh 5.1484665847281331e-06 -▁Yacht 5.1484665847281331e-06 -▁geräumigen 5.1484665847281331e-06 -▁unbesch 5.1484665847281331e-06 -▁verweist 5.1484665847281331e-06 -jar 5.14467816192848e-06 -luft 5.14467816192848e-06 -▁Erkenntnis 5.14467816192848e-06 -▁Herm 5.14467816192848e-06 -▁Nepal 5.14467816192848e-06 -▁Panel 5.14467816192848e-06 -▁Simon 5.14467816192848e-06 -▁Unternehm 5.14467816192848e-06 -▁accommod 5.14467816192848e-06 -▁degrad 5.14467816192848e-06 -▁holes 5.14467816192848e-06 -▁nicht- 5.14467816192848e-06 -▁posed 5.14467816192848e-06 -▁scient 5.14467816192848e-06 -▁wirksamer 5.14467816192848e-06 -quoten 5.1408897391288273e-06 -▁Bald 5.1408897391288273e-06 -▁EU-Haus 5.1408897391288273e-06 -▁Extr 5.1408897391288273e-06 -▁Gegenwärtig 5.1408897391288273e-06 -▁Ozean 5.1408897391288273e-06 -▁Schreib 5.1408897391288273e-06 -▁Seattle 5.1408897391288273e-06 -▁aiming 5.1408897391288273e-06 -▁homogen 5.1408897391288273e-06 -▁nightlife 5.1408897391288273e-06 -▁worker 5.1408897391288273e-06 -ME 5.1371013163291744e-06 -▁2.6 5.1371013163291744e-06 -▁Depending 5.1371013163291744e-06 -▁Imp 5.1371013163291744e-06 -▁Mittelme 5.1371013163291744e-06 -▁Störungen 5.1371013163291744e-06 -▁andauern 5.1371013163291744e-06 -▁anyway 5.1371013163291744e-06 -▁vereinbaren 5.1371013163291744e-06 -sing 5.1333128935295215e-06 -▁Behandlungs 5.1333128935295215e-06 -▁Clar 5.1333128935295215e-06 -▁FREE 5.1333128935295215e-06 -▁benachrichtigt 5.1333128935295215e-06 -▁bereitgestellten 5.1333128935295215e-06 -▁demonstrations 5.1333128935295215e-06 -▁inwieweit 5.1333128935295215e-06 -▁strenger 5.1333128935295215e-06 -▁tschechischen 5.1333128935295215e-06 -▁verlor 5.1333128935295215e-06 -▁† 5.1333128935295215e-06 -amerika 5.1295244707298686e-06 -eman 5.1295244707298686e-06 -hlung 5.1295244707298686e-06 -m2 5.1295244707298686e-06 -seud 5.1295244707298686e-06 -▁'E 5.1295244707298686e-06 -▁Akzeptanz 5.1295244707298686e-06 -▁Diktatur 5.1295244707298686e-06 -▁Gesundheitswesen 5.1295244707298686e-06 -▁Immunität 5.1295244707298686e-06 -▁Kalk 5.1295244707298686e-06 -▁Neither 5.1295244707298686e-06 -▁Unglück 5.1295244707298686e-06 -▁ausgewählte 5.1295244707298686e-06 -▁bloß 5.1295244707298686e-06 -▁compens 5.1295244707298686e-06 -▁developer 5.1295244707298686e-06 -▁ersuchen 5.1295244707298686e-06 -▁memories 5.1295244707298686e-06 -▁tags 5.1295244707298686e-06 -▁worried 5.1295244707298686e-06 -Station 5.1257360479302157e-06 -oded 5.1257360479302157e-06 -ögern 5.1257360479302157e-06 -▁..... 5.1257360479302157e-06 -▁Fußgänger 5.1257360479302157e-06 -▁Monaco 5.1257360479302157e-06 -▁Salon 5.1257360479302157e-06 -▁abgeschafft 5.1257360479302157e-06 -▁deadlines 5.1257360479302157e-06 -▁detention 5.1257360479302157e-06 -▁frische 5.1257360479302157e-06 -▁klin 5.1257360479302157e-06 -▁sums 5.1257360479302157e-06 --fl 5.1219476251305637e-06 -inhaber 5.1219476251305637e-06 -post 5.1219476251305637e-06 -Á 5.1219476251305637e-06 -▁Lee 5.1219476251305637e-06 -▁Lock 5.1219476251305637e-06 -▁cream 5.1219476251305637e-06 -▁portion 5.1219476251305637e-06 -▁pushed 5.1219476251305637e-06 -▁~ 5.1219476251305637e-06 -personen 5.1181592023309108e-06 -schulen 5.1181592023309108e-06 -▁Gestalt 5.1181592023309108e-06 -▁Hilton 5.1181592023309108e-06 -▁Hindernis 5.1181592023309108e-06 -▁Holding 5.1181592023309108e-06 -▁Kapitalismus 5.1181592023309108e-06 -▁SR 5.1181592023309108e-06 -▁amid 5.1181592023309108e-06 -▁anonym 5.1181592023309108e-06 -▁asiatischen 5.1181592023309108e-06 -▁basierend 5.1181592023309108e-06 -▁bestraft 5.1181592023309108e-06 -▁bub 5.1181592023309108e-06 -▁eb 5.1181592023309108e-06 -▁kopieren 5.1181592023309108e-06 -▁mi 5.1181592023309108e-06 -▁noble 5.1181592023309108e-06 -▁perceived 5.1181592023309108e-06 -▁Anyone 5.1143707795312579e-06 -▁Canadian 5.1143707795312579e-06 -▁Canary 5.1143707795312579e-06 -▁Orchestra 5.1143707795312579e-06 -▁Political 5.1143707795312579e-06 -▁Sara 5.1143707795312579e-06 -▁dismiss 5.1143707795312579e-06 -▁exklusiv 5.1143707795312579e-06 -▁fant 5.1143707795312579e-06 -▁genet 5.1143707795312579e-06 -▁kont 5.1143707795312579e-06 -reut 5.110582356731605e-06 -▁Autorität 5.110582356731605e-06 -▁Außengrenzen 5.110582356731605e-06 -▁Chianti 5.110582356731605e-06 -▁Colombia 5.110582356731605e-06 -▁Dienstes 5.110582356731605e-06 -▁Ingenie 5.110582356731605e-06 -▁Maj 5.110582356731605e-06 -▁hochwert 5.110582356731605e-06 -▁modification 5.110582356731605e-06 -institut 5.1067939339319521e-06 -ktoren 5.1067939339319521e-06 -thur 5.1067939339319521e-06 -wal 5.1067939339319521e-06 -zeile 5.1067939339319521e-06 -ätten 5.1067939339319521e-06 -▁83 5.1067939339319521e-06 -▁Begr 5.1067939339319521e-06 -▁Flüss 5.1067939339319521e-06 -▁Südost 5.1067939339319521e-06 -▁Therap 5.1067939339319521e-06 -▁Translation 5.1067939339319521e-06 -▁Veter 5.1067939339319521e-06 -▁bottle 5.1067939339319521e-06 -▁sacrific 5.1067939339319521e-06 -▁stere 5.1067939339319521e-06 -▁theatr 5.1067939339319521e-06 -▁zugreifen 5.1067939339319521e-06 -hat 5.1030055111322992e-06 -pension 5.1030055111322992e-06 -vas 5.1030055111322992e-06 -wickelt 5.1030055111322992e-06 -▁Giovanni 5.1030055111322992e-06 -▁Jahrestag 5.1030055111322992e-06 -▁Schottland 5.1030055111322992e-06 -▁einzuleiten 5.1030055111322992e-06 -▁smallest 5.1030055111322992e-06 -amine 5.0992170883326463e-06 -neten 5.0992170883326463e-06 -oors 5.0992170883326463e-06 -stens 5.0992170883326463e-06 -world 5.0992170883326463e-06 -▁LabVIEW 5.0992170883326463e-06 -▁Landwirten 5.0992170883326463e-06 -▁Olympischen 5.0992170883326463e-06 -▁Teuf 5.0992170883326463e-06 -▁bugs 5.0992170883326463e-06 -▁divisions 5.0992170883326463e-06 -▁exotic 5.0992170883326463e-06 -▁landmark 5.0992170883326463e-06 -▁neighb 5.0992170883326463e-06 -▁proves 5.0992170883326463e-06 -▁safer 5.0992170883326463e-06 -▁willk 5.0992170883326463e-06 -.7 5.0954286655329934e-06 -abetes 5.0954286655329934e-06 -cott 5.0954286655329934e-06 -samen 5.0954286655329934e-06 -ssystems 5.0954286655329934e-06 -äuft 5.0954286655329934e-06 -▁Bash 5.0954286655329934e-06 -▁Bewegungs 5.0954286655329934e-06 -▁Camer 5.0954286655329934e-06 -▁Migranten 5.0954286655329934e-06 -▁Reflex 5.0954286655329934e-06 -▁Rud 5.0954286655329934e-06 -▁XX 5.0954286655329934e-06 -▁adjacent 5.0954286655329934e-06 -▁cattle 5.0954286655329934e-06 -▁deciding 5.0954286655329934e-06 -▁delegations 5.0954286655329934e-06 -▁dynamischen 5.0954286655329934e-06 -▁nnen 5.0954286655329934e-06 -▁ruins 5.0954286655329934e-06 -▁salle 5.0954286655329934e-06 -genda 5.0916402427333414e-06 -▁Ash 5.0916402427333414e-06 -▁Wettbew 5.0916402427333414e-06 -▁confirms 5.0916402427333414e-06 -▁endors 5.0916402427333414e-06 -▁endorsed 5.0916402427333414e-06 -▁irrespective 5.0916402427333414e-06 -▁sequence 5.0916402427333414e-06 -▁stor 5.0916402427333414e-06 -▁verschw 5.0916402427333414e-06 -▁wisdom 5.0916402427333414e-06 -▁wit 5.0916402427333414e-06 --scale 5.0878518199336885e-06 -preisen 5.0878518199336885e-06 -▁105 5.0878518199336885e-06 -▁Andor 5.0878518199336885e-06 -▁Anpassungen 5.0878518199336885e-06 -▁Aufklärung 5.0878518199336885e-06 -▁Pi 5.0878518199336885e-06 -▁korr 5.0878518199336885e-06 -▁punishment 5.0878518199336885e-06 -iform 5.0840633971340356e-06 -ruckt 5.0840633971340356e-06 -zum 5.0840633971340356e-06 -▁Europäisches 5.0840633971340356e-06 -▁Königreichs 5.0840633971340356e-06 -▁abschließen 5.0840633971340356e-06 -▁admire 5.0840633971340356e-06 -▁classification 5.0840633971340356e-06 -▁hero 5.0840633971340356e-06 -▁obstacle 5.0840633971340356e-06 -▁reader 5.0840633971340356e-06 -hältnissen 5.0802749743343827e-06 -iento 5.0802749743343827e-06 -omo 5.0802749743343827e-06 -stellten 5.0802749743343827e-06 -tration 5.0802749743343827e-06 -▁Against 5.0802749743343827e-06 -▁attraktive 5.0802749743343827e-06 -▁beherbergt 5.0802749743343827e-06 -▁boutique 5.0802749743343827e-06 -▁dramatically 5.0802749743343827e-06 -▁guilty 5.0802749743343827e-06 -▁seas 5.0802749743343827e-06 -earning 5.07648655153473e-06 -izard 5.07648655153473e-06 -lave 5.07648655153473e-06 -orum 5.07648655153473e-06 -prises 5.07648655153473e-06 -ynast 5.07648655153473e-06 -▁Beschl 5.07648655153473e-06 -▁Dé 5.07648655153473e-06 -▁Einstieg 5.07648655153473e-06 -▁Gefangen 5.07648655153473e-06 -▁Guantánamo 5.07648655153473e-06 -▁Recently 5.07648655153473e-06 -▁YouTube 5.07648655153473e-06 -▁barrier 5.07648655153473e-06 -▁commerce 5.07648655153473e-06 -▁dro 5.07648655153473e-06 -▁hoc 5.07648655153473e-06 -▁tiefen 5.07648655153473e-06 -.8 5.0726981287350769e-06 -jenige 5.0726981287350769e-06 -▁CSS 5.0726981287350769e-06 -▁Heidelberg 5.0726981287350769e-06 -▁angeführt 5.0726981287350769e-06 -▁berufen 5.0726981287350769e-06 -▁exakt 5.0726981287350769e-06 -▁immunity 5.0726981287350769e-06 -▁kol 5.0726981287350769e-06 -▁sämtlicher 5.0726981287350769e-06 -63 5.068909705935424e-06 -adresse 5.068909705935424e-06 -thaus 5.068909705935424e-06 -ziehungs 5.068909705935424e-06 -▁Valle 5.068909705935424e-06 -▁Währungen 5.068909705935424e-06 -▁amp 5.068909705935424e-06 -▁ausgehend 5.068909705935424e-06 -stoß 5.0651212831357711e-06 -usik 5.0651212831357711e-06 -▁Cant 5.0651212831357711e-06 -▁Hunderte 5.0651212831357711e-06 -▁Quartal 5.0651212831357711e-06 -▁anzusehen 5.0651212831357711e-06 -▁bemerk 5.0651212831357711e-06 -▁bewahrt 5.0651212831357711e-06 -▁praise 5.0651212831357711e-06 -▁tu 5.0651212831357711e-06 -agern 5.0613328603361191e-06 -atlant 5.0613328603361191e-06 -krimin 5.0613328603361191e-06 -▁Börsen 5.0613328603361191e-06 -▁EB 5.0613328603361191e-06 -▁Herzlich 5.0613328603361191e-06 -▁Pferde 5.0613328603361191e-06 -▁festivals 5.0613328603361191e-06 -▁hör 5.0613328603361191e-06 -▁qualifiz 5.0613328603361191e-06 -We 5.0575444375364662e-06 -gehörigen 5.0575444375364662e-06 -▁Modus 5.0575444375364662e-06 -▁Prince 5.0575444375364662e-06 -▁Stan 5.0575444375364662e-06 -▁Strafgerichtshof 5.0575444375364662e-06 -▁Vortrag 5.0575444375364662e-06 -▁fastest 5.0575444375364662e-06 -▁irgendeine 5.0575444375364662e-06 -▁nit 5.0575444375364662e-06 -▁sleeping 5.0575444375364662e-06 -angehörigen 5.0537560147368133e-06 -gasse 5.0537560147368133e-06 -iso 5.0537560147368133e-06 -ittings 5.0537560147368133e-06 -usse 5.0537560147368133e-06 -wear 5.0537560147368133e-06 -ymn 5.0537560147368133e-06 -▁DES 5.0537560147368133e-06 -▁Dicht 5.0537560147368133e-06 -▁Geden 5.0537560147368133e-06 -▁Kast 5.0537560147368133e-06 -▁Luftra 5.0537560147368133e-06 -▁Reifen 5.0537560147368133e-06 -▁dorthin 5.0537560147368133e-06 -▁dürften 5.0537560147368133e-06 -▁english 5.0537560147368133e-06 -▁geführten 5.0537560147368133e-06 -▁römischen 5.0537560147368133e-06 -▁sheep 5.0537560147368133e-06 -▁tens 5.0537560147368133e-06 -▁vibrant 5.0537560147368133e-06 -▁voranzutreiben 5.0537560147368133e-06 --9 5.0499675919371604e-06 --Systems 5.0499675919371604e-06 -blicke 5.0499675919371604e-06 -steuern 5.0499675919371604e-06 -▁Additional 5.0499675919371604e-06 -▁Beitrittsverhandlungen 5.0499675919371604e-06 -▁Germans 5.0499675919371604e-06 -▁Greg 5.0499675919371604e-06 -▁krit 5.0499675919371604e-06 -▁unvor 5.0499675919371604e-06 --Le 5.0461791691375075e-06 --Lösungen 5.0461791691375075e-06 -ahmen 5.0461791691375075e-06 -imental 5.0461791691375075e-06 -night 5.0461791691375075e-06 -ordnet 5.0461791691375075e-06 -▁Castro 5.0461791691375075e-06 -▁Fremden 5.0461791691375075e-06 -▁Lufthansa 5.0461791691375075e-06 -▁SilverFast 5.0461791691375075e-06 -▁Softwar 5.0461791691375075e-06 -▁Sozialisten 5.0461791691375075e-06 -▁Wanderungen 5.0461791691375075e-06 -▁akt 5.0461791691375075e-06 -▁conserv 5.0461791691375075e-06 -▁halben 5.0461791691375075e-06 -▁sett 5.0461791691375075e-06 -▁vergleichbar 5.0461791691375075e-06 -äglichen 5.0423907463378546e-06 -örper 5.0423907463378546e-06 -▁Chris 5.0423907463378546e-06 -▁Freihandels 5.0423907463378546e-06 -▁circum 5.0423907463378546e-06 -▁completing 5.0423907463378546e-06 -▁diesbezüglichen 5.0423907463378546e-06 -▁erle 5.0423907463378546e-06 --market 5.0386023235382018e-06 -ianer 5.0386023235382018e-06 -irus 5.0386023235382018e-06 -osexual 5.0386023235382018e-06 -profit 5.0386023235382018e-06 -rine 5.0386023235382018e-06 -westen 5.0386023235382018e-06 -▁Chip 5.0386023235382018e-06 -▁Cool 5.0386023235382018e-06 -▁PAR 5.0386023235382018e-06 -▁aust 5.0386023235382018e-06 -▁beweg 5.0386023235382018e-06 -▁far-reaching 5.0386023235382018e-06 -▁gemüt 5.0386023235382018e-06 -▁geräum 5.0386023235382018e-06 -▁inland 5.0386023235382018e-06 -▁Übermittlung 5.0386023235382018e-06 -izations 5.0348139007385489e-06 -jev 5.0348139007385489e-06 -▁Fahrzeugen 5.0348139007385489e-06 -▁Herstellern 5.0348139007385489e-06 -▁Montreal 5.0348139007385489e-06 -▁Operationen 5.0348139007385489e-06 -▁Vie 5.0348139007385489e-06 -▁advisory 5.0348139007385489e-06 -▁bestanden 5.0348139007385489e-06 -▁blockiert 5.0348139007385489e-06 -▁führender 5.0348139007385489e-06 -▁gemütlich 5.0348139007385489e-06 -▁prinzip 5.0348139007385489e-06 -▁schreck 5.0348139007385489e-06 -ordnen 5.0310254779388968e-06 -▁Fülle 5.0310254779388968e-06 -▁Schließung 5.0310254779388968e-06 -▁baut 5.0310254779388968e-06 -▁gratuliere 5.0310254779388968e-06 -▁rolling 5.0310254779388968e-06 -▁stabilen 5.0310254779388968e-06 -▁textiles 5.0310254779388968e-06 -▁trials 5.0310254779388968e-06 -enia 5.0272370551392439e-06 -itary 5.0272370551392439e-06 -▁Gedanke 5.0272370551392439e-06 -▁Glaubens 5.0272370551392439e-06 -▁Iceland 5.0272370551392439e-06 -▁Mountainb 5.0272370551392439e-06 -▁Price 5.0272370551392439e-06 -▁ausgepr 5.0272370551392439e-06 -▁ausgerüstet 5.0272370551392439e-06 -▁dict 5.0272370551392439e-06 -▁ermittelt 5.0272370551392439e-06 -▁gambling 5.0272370551392439e-06 -▁б 5.0272370551392439e-06 -eway 5.023448632339591e-06 -gründe 5.023448632339591e-06 -ierbare 5.023448632339591e-06 -iop 5.023448632339591e-06 -wide 5.023448632339591e-06 -ynes 5.023448632339591e-06 -▁115 5.023448632339591e-06 -▁Consider 5.023448632339591e-06 -▁Synth 5.023448632339591e-06 -▁alph 5.023448632339591e-06 -▁baths 5.023448632339591e-06 -▁bedient 5.023448632339591e-06 -▁bekanntlich 5.023448632339591e-06 -▁bemerkens 5.023448632339591e-06 -▁gemeinschaftliche 5.023448632339591e-06 -▁improves 5.023448632339591e-06 -▁modernisation 5.023448632339591e-06 -berry 5.0196602095399381e-06 -itär 5.0196602095399381e-06 -▁Chancellor 5.0196602095399381e-06 -▁Downloads 5.0196602095399381e-06 -▁Police 5.0196602095399381e-06 -▁Upgrade 5.0196602095399381e-06 -▁ausgeliefert 5.0196602095399381e-06 -▁definitive 5.0196602095399381e-06 -▁fore 5.0196602095399381e-06 -▁gaining 5.0196602095399381e-06 -▁markiert 5.0196602095399381e-06 -▁snacks 5.0196602095399381e-06 -▁wholes 5.0196602095399381e-06 -itur 5.0158717867402853e-06 -owser 5.0158717867402853e-06 -tery 5.0158717867402853e-06 -▁Appell 5.0158717867402853e-06 -▁Scott 5.0158717867402853e-06 -▁Warsaw 5.0158717867402853e-06 -▁dynamische 5.0158717867402853e-06 -▁hunting 5.0158717867402853e-06 -▁legislativen 5.0158717867402853e-06 -▁summ 5.0158717867402853e-06 -▁witnessed 5.0158717867402853e-06 -▁österreichische 5.0158717867402853e-06 --making 5.0120833639406324e-06 -lassung 5.0120833639406324e-06 -opy 5.0120833639406324e-06 -schlüssel 5.0120833639406324e-06 -▁BAE 5.0120833639406324e-06 -▁CAR 5.0120833639406324e-06 -▁Rica 5.0120833639406324e-06 -▁buttons 5.0120833639406324e-06 -▁chart 5.0120833639406324e-06 -▁gesellschaft 5.0120833639406324e-06 -▁remedy 5.0120833639406324e-06 -▁terrain 5.0120833639406324e-06 -▁unsicher 5.0120833639406324e-06 -odge 5.0082949411409795e-06 -ränkten 5.0082949411409795e-06 -▁Cruz 5.0082949411409795e-06 -▁Schwerpunkte 5.0082949411409795e-06 -▁consolidate 5.0082949411409795e-06 -▁eco 5.0082949411409795e-06 -▁garant 5.0082949411409795e-06 -TM 5.0045065183413266e-06 -▁DG 5.0045065183413266e-06 -▁Fracht 5.0045065183413266e-06 -▁Industriepolitik 5.0045065183413266e-06 -▁VIS 5.0045065183413266e-06 -▁ung 5.0045065183413266e-06 -▁verfahren 5.0045065183413266e-06 -pet 5.0007180955416745e-06 -terior 5.0007180955416745e-06 -▁Beträge 5.0007180955416745e-06 -▁Hague 5.0007180955416745e-06 -▁Mathem 5.0007180955416745e-06 -▁Tuscany 5.0007180955416745e-06 -▁Zuliefer 5.0007180955416745e-06 -▁Zustellbetten 5.0007180955416745e-06 -▁courtyard 5.0007180955416745e-06 -▁Übertrag 5.0007180955416745e-06 -anbieter 4.9969296727420216e-06 -gesundheit 4.9969296727420216e-06 -öser 4.9969296727420216e-06 -π 4.9969296727420216e-06 -▁Feedback 4.9969296727420216e-06 -▁Projektes 4.9969296727420216e-06 -▁Scandinav 4.9969296727420216e-06 -▁Schnitt 4.9969296727420216e-06 -▁Scr 4.9969296727420216e-06 -▁abzugeben 4.9969296727420216e-06 -▁paragraphs 4.9969296727420216e-06 -▁secular 4.9969296727420216e-06 -ACE 4.9931412499423687e-06 -ITY 4.9931412499423687e-06 -eability 4.9931412499423687e-06 -ktor 4.9931412499423687e-06 -langes 4.9931412499423687e-06 -malen 4.9931412499423687e-06 -ounds 4.9931412499423687e-06 -▁A1 4.9931412499423687e-06 -▁Abt 4.9931412499423687e-06 -▁Branden 4.9931412499423687e-06 -▁multilateralen 4.9931412499423687e-06 -▁positives 4.9931412499423687e-06 -▁stagn 4.9931412499423687e-06 -▁surf 4.9931412499423687e-06 -LICH 4.9893528271427159e-06 -marktes 4.9893528271427159e-06 -trieb 4.9893528271427159e-06 -▁Analys 4.9893528271427159e-06 -▁Diejenigen 4.9893528271427159e-06 -▁Edit 4.9893528271427159e-06 -▁Mall 4.9893528271427159e-06 -▁akadem 4.9893528271427159e-06 -▁eventuelle 4.9893528271427159e-06 -▁fortress 4.9893528271427159e-06 -▁melt 4.9893528271427159e-06 -come 4.985564404343063e-06 -klär 4.985564404343063e-06 -leger 4.985564404343063e-06 -▁PA 4.985564404343063e-06 -▁mache 4.985564404343063e-06 -▁mobilen 4.985564404343063e-06 -▁modifications 4.985564404343063e-06 -▁volumes 4.985564404343063e-06 --West 4.98177598154341e-06 -acion 4.98177598154341e-06 -asm 4.98177598154341e-06 -ivirus 4.98177598154341e-06 -viel 4.98177598154341e-06 -zun 4.98177598154341e-06 -▁Algeria 4.98177598154341e-06 -▁Autonom 4.98177598154341e-06 -▁Bien 4.98177598154341e-06 -▁Bürgerrechte 4.98177598154341e-06 -▁Einreise 4.98177598154341e-06 -▁FTP 4.98177598154341e-06 -▁Harmon 4.98177598154341e-06 -▁Kongo 4.98177598154341e-06 -▁Oz 4.98177598154341e-06 -▁anspruchsvolle 4.98177598154341e-06 -▁femin 4.98177598154341e-06 -▁licensed 4.98177598154341e-06 -▁merk 4.98177598154341e-06 -▁niedrige 4.98177598154341e-06 -▁rumän 4.98177598154341e-06 -▁streben 4.98177598154341e-06 -ischof 4.9779875587437572e-06 -mack 4.9779875587437572e-06 -transport 4.9779875587437572e-06 -urity 4.9779875587437572e-06 -zähl 4.9779875587437572e-06 -▁1940 4.9779875587437572e-06 -▁Cohesion 4.9779875587437572e-06 -▁Dienststellen 4.9779875587437572e-06 -▁NG 4.9779875587437572e-06 -▁Ungleichheiten 4.9779875587437572e-06 -▁Werken 4.9779875587437572e-06 -▁costly 4.9779875587437572e-06 -▁herbe 4.9779875587437572e-06 -▁scholars 4.9779875587437572e-06 -▁tasty 4.9779875587437572e-06 -ipen 4.9741991359441043e-06 -ritte 4.9741991359441043e-06 -μ 4.9741991359441043e-06 -▁Gutes 4.9741991359441043e-06 -▁Mord 4.9741991359441043e-06 -▁Want 4.9741991359441043e-06 -▁Zak 4.9741991359441043e-06 -▁container 4.9741991359441043e-06 -▁cott 4.9741991359441043e-06 -▁disabilities 4.9741991359441043e-06 -▁introduces 4.9741991359441043e-06 -▁kommunizieren 4.9741991359441043e-06 -▁settle 4.9741991359441043e-06 -▁shoulder 4.9741991359441043e-06 -▁tendency 4.9741991359441043e-06 -▁verschärft 4.9741991359441043e-06 -▁Ök 4.9741991359441043e-06 -ammeln 4.9704107131444522e-06 -hardt 4.9704107131444522e-06 -ressourcen 4.9704107131444522e-06 -▁1990s 4.9704107131444522e-06 -▁BER 4.9704107131444522e-06 -▁Carol 4.9704107131444522e-06 -▁Jahreszeit 4.9704107131444522e-06 -▁Motoren 4.9704107131444522e-06 -▁Stere 4.9704107131444522e-06 -▁Though 4.9704107131444522e-06 -▁Verhinderung 4.9704107131444522e-06 -▁Wag 4.9704107131444522e-06 -▁Wohnsitz 4.9704107131444522e-06 -▁aufhalten 4.9704107131444522e-06 -▁erheb 4.9704107131444522e-06 -▁europä 4.9704107131444522e-06 -▁fabulous 4.9704107131444522e-06 -▁gez 4.9704107131444522e-06 -▁zusammengestellt 4.9704107131444522e-06 -agung 4.9666222903447994e-06 -anti 4.9666222903447994e-06 -ialen 4.9666222903447994e-06 -omatic 4.9666222903447994e-06 -ystemen 4.9666222903447994e-06 -▁FS 4.9666222903447994e-06 -▁Linz 4.9666222903447994e-06 -▁beruf 4.9666222903447994e-06 -▁clauses 4.9666222903447994e-06 -▁complementary 4.9666222903447994e-06 -▁konfigur 4.9666222903447994e-06 -▁meaningful 4.9666222903447994e-06 -▁sends 4.9666222903447994e-06 --bed 4.9628338675451465e-06 -echts 4.9628338675451465e-06 -künften 4.9628338675451465e-06 -▁Diskussions 4.9628338675451465e-06 -▁Impuls 4.9628338675451465e-06 -▁Treff 4.9628338675451465e-06 -▁UEN 4.9628338675451465e-06 -▁assessed 4.9628338675451465e-06 -▁dea 4.9628338675451465e-06 -▁roten 4.9628338675451465e-06 -▁verdanken 4.9628338675451465e-06 -VD 4.9590454447454936e-06 -ching 4.9590454447454936e-06 -drom 4.9590454447454936e-06 -tro 4.9590454447454936e-06 -▁CP 4.9590454447454936e-06 -▁Championship 4.9590454447454936e-06 -▁Gleichwohl 4.9590454447454936e-06 -▁Magazine 4.9590454447454936e-06 -▁SN 4.9590454447454936e-06 -▁Sozialpartner 4.9590454447454936e-06 -▁activate 4.9590454447454936e-06 -▁begrenzte 4.9590454447454936e-06 -▁mittel- 4.9590454447454936e-06 -▁tensions 4.9590454447454936e-06 -▁traditionally 4.9590454447454936e-06 -▁très 4.9590454447454936e-06 --vis 4.9552570219458407e-06 -aussch 4.9552570219458407e-06 -axis 4.9552570219458407e-06 -iza 4.9552570219458407e-06 -obb 4.9552570219458407e-06 -▁MEDA 4.9552570219458407e-06 -▁Meinungsäußerung 4.9552570219458407e-06 -▁Schlußfolgerungen 4.9552570219458407e-06 -▁Zeitschrift 4.9552570219458407e-06 -▁err 4.9552570219458407e-06 -▁hous 4.9552570219458407e-06 -▁idyll 4.9552570219458407e-06 -▁jünger 4.9552570219458407e-06 -▁psychological 4.9552570219458407e-06 -▁stead 4.9552570219458407e-06 -jack 4.9514685991461878e-06 -▁Ern 4.9514685991461878e-06 -▁Gemeinschaftsrecht 4.9514685991461878e-06 -▁Objective 4.9514685991461878e-06 -▁Sisko 4.9514685991461878e-06 -▁Verschw 4.9514685991461878e-06 -▁coastline 4.9514685991461878e-06 -▁defen 4.9514685991461878e-06 -▁gemeinschaft 4.9514685991461878e-06 -▁limiting 4.9514685991461878e-06 -▁painted 4.9514685991461878e-06 -▁strateg 4.9514685991461878e-06 -ampagnen 4.9476801763465349e-06 -quenzen 4.9476801763465349e-06 -uliche 4.9476801763465349e-06 -▁-T 4.9476801763465349e-06 -▁Commun 4.9476801763465349e-06 -▁Petersburg 4.9476801763465349e-06 -▁Relig 4.9476801763465349e-06 -▁Skigebiet 4.9476801763465349e-06 -▁selts 4.9476801763465349e-06 -▁unmittelbare 4.9476801763465349e-06 -▁zurückkommen 4.9476801763465349e-06 --Entwicklungs 4.9438917535468829e-06 -benen 4.9438917535468829e-06 -forum 4.9438917535468829e-06 -garant 4.9438917535468829e-06 -▁EU-M 4.9438917535468829e-06 -▁Follow 4.9438917535468829e-06 -▁Fäl 4.9438917535468829e-06 -▁Instit 4.9438917535468829e-06 -▁Storn 4.9438917535468829e-06 -▁aufhören 4.9438917535468829e-06 -▁birthday 4.9438917535468829e-06 -▁distinctive 4.9438917535468829e-06 -▁en-suite 4.9438917535468829e-06 -▁geschenkt 4.9438917535468829e-06 -▁loop 4.9438917535468829e-06 -▁oriented 4.9438917535468829e-06 -▁unexpected 4.9438917535468829e-06 -acle 4.94010333074723e-06 -fels 4.94010333074723e-06 -ologies 4.94010333074723e-06 -rechtlich 4.94010333074723e-06 -▁Gender 4.94010333074723e-06 -▁Hochschulen 4.94010333074723e-06 -▁Pisa 4.94010333074723e-06 -▁Stephen 4.94010333074723e-06 -▁Ziels 4.94010333074723e-06 -▁asc 4.94010333074723e-06 -▁commands 4.94010333074723e-06 -▁cum 4.94010333074723e-06 -▁gericht 4.94010333074723e-06 -▁wr 4.94010333074723e-06 -anza 4.9363149079475771e-06 -bin 4.9363149079475771e-06 -grade 4.9363149079475771e-06 -sph 4.9363149079475771e-06 -zip 4.9363149079475771e-06 -▁Körpers 4.9363149079475771e-06 -▁PayPal 4.9363149079475771e-06 -▁Spann 4.9363149079475771e-06 -▁Train 4.9363149079475771e-06 -▁advert 4.9363149079475771e-06 -▁altitude 4.9363149079475771e-06 -▁bedeutender 4.9363149079475771e-06 -▁cord 4.9363149079475771e-06 -▁perman 4.9363149079475771e-06 -▁tunnel 4.9363149079475771e-06 -▁vigil 4.9363149079475771e-06 -erhö 4.9325264851479242e-06 -uto 4.9325264851479242e-06 -yball 4.9325264851479242e-06 -▁Barcel 4.9325264851479242e-06 -▁Fabrik 4.9325264851479242e-06 -▁Lov 4.9325264851479242e-06 -▁Offen 4.9325264851479242e-06 -▁Organisations 4.9325264851479242e-06 -▁Vergessen 4.9325264851479242e-06 -▁Wechselkurs 4.9325264851479242e-06 -▁ampl 4.9325264851479242e-06 -▁baggage 4.9325264851479242e-06 -▁bankrupt 4.9325264851479242e-06 -▁betreut 4.9325264851479242e-06 -▁contrib 4.9325264851479242e-06 -▁dairy 4.9325264851479242e-06 -▁klarstellen 4.9325264851479242e-06 -OSS 4.9287380623482713e-06 -fehler 4.9287380623482713e-06 -läuft 4.9287380623482713e-06 -weren 4.9287380623482713e-06 -öder 4.9287380623482713e-06 -▁Eurob 4.9287380623482713e-06 -▁gegebenen 4.9287380623482713e-06 -▁geräumige 4.9287380623482713e-06 -▁kalt 4.9287380623482713e-06 -▁veranstaltet 4.9287380623482713e-06 -▁wenngleich 4.9287380623482713e-06 -adies 4.9249496395486184e-06 -essions 4.9249496395486184e-06 -familie 4.9249496395486184e-06 -gebungen 4.9249496395486184e-06 -ologien 4.9249496395486184e-06 -ugg 4.9249496395486184e-06 -▁Author 4.9249496395486184e-06 -▁GO 4.9249496395486184e-06 -▁Posselt 4.9249496395486184e-06 -▁Reihen 4.9249496395486184e-06 -▁identifying 4.9249496395486184e-06 -▁konstitution 4.9249496395486184e-06 -▁künstlerischen 4.9249496395486184e-06 -▁prerequisite 4.9249496395486184e-06 -▁threatening 4.9249496395486184e-06 -ikaner 4.9211612167489655e-06 -ounce 4.9211612167489655e-06 -treffen 4.9211612167489655e-06 -ätigkeit 4.9211612167489655e-06 -▁24-hour 4.9211612167489655e-06 -▁DL 4.9211612167489655e-06 -▁broadcasting 4.9211612167489655e-06 -▁passport 4.9211612167489655e-06 -▁quo 4.9211612167489655e-06 -▁rhythm 4.9211612167489655e-06 -▁städtischen 4.9211612167489655e-06 -restaurant 4.9173727939493126e-06 -º 4.9173727939493126e-06 -▁Langen 4.9173727939493126e-06 -▁Medium 4.9173727939493126e-06 -▁Mär 4.9173727939493126e-06 -▁Zuschauer 4.9173727939493126e-06 -▁attraktiven 4.9173727939493126e-06 -▁bird 4.9173727939493126e-06 -▁landing 4.9173727939493126e-06 -▁unglaublich 4.9173727939493126e-06 -▁yield 4.9173727939493126e-06 -ancement 4.9135843711496606e-06 -aschinen 4.9135843711496606e-06 -ellungen 4.9135843711496606e-06 -ergebnis 4.9135843711496606e-06 -▁10.00 4.9135843711496606e-06 -▁Ansprüchen 4.9135843711496606e-06 -▁Bruch 4.9135843711496606e-06 -▁Hallen 4.9135843711496606e-06 -▁Synerg 4.9135843711496606e-06 -▁Wilhelm 4.9135843711496606e-06 -▁begrenzen 4.9135843711496606e-06 -▁pill 4.9135843711496606e-06 -▁sorts 4.9135843711496606e-06 -cho 4.9097959483500077e-06 -elect 4.9097959483500077e-06 -oda 4.9097959483500077e-06 -onomic 4.9097959483500077e-06 -oppel 4.9097959483500077e-06 -orien 4.9097959483500077e-06 -vr 4.9097959483500077e-06 -▁Afrikas 4.9097959483500077e-06 -▁Gastronomie 4.9097959483500077e-06 -▁Hotelzimmer 4.9097959483500077e-06 -▁Pries 4.9097959483500077e-06 -▁couldn 4.9097959483500077e-06 -▁emphasize 4.9097959483500077e-06 -▁fragile 4.9097959483500077e-06 -▁muslim 4.9097959483500077e-06 -▁protects 4.9097959483500077e-06 -▁qm 4.9097959483500077e-06 -agd 4.9060075255503548e-06 -ension 4.9060075255503548e-06 -figur 4.9060075255503548e-06 -forderungen 4.9060075255503548e-06 -ourge 4.9060075255503548e-06 -wein 4.9060075255503548e-06 -üp 4.9060075255503548e-06 -▁1971 4.9060075255503548e-06 -▁Daraus 4.9060075255503548e-06 -▁Erfassung 4.9060075255503548e-06 -▁Messen 4.9060075255503548e-06 -▁appar 4.9060075255503548e-06 -▁echter 4.9060075255503548e-06 -▁ele 4.9060075255503548e-06 -▁interact 4.9060075255503548e-06 -▁mang 4.9060075255503548e-06 -▁niedr 4.9060075255503548e-06 -▁popularity 4.9060075255503548e-06 -▁vine 4.9060075255503548e-06 -MP 4.9022191027507019e-06 -clus 4.9022191027507019e-06 -ensp 4.9022191027507019e-06 -orders 4.9022191027507019e-06 -yle 4.9022191027507019e-06 -▁Fusion 4.9022191027507019e-06 -▁IND 4.9022191027507019e-06 -▁Müll 4.9022191027507019e-06 -▁Tagungen 4.9022191027507019e-06 -▁Ukrainian 4.9022191027507019e-06 -▁earthquake 4.9022191027507019e-06 -▁hunder 4.9022191027507019e-06 -▁indications 4.9022191027507019e-06 -▁oasis 4.9022191027507019e-06 -▁virtuellen 4.9022191027507019e-06 -drückt 4.898430679951049e-06 -ety 4.898430679951049e-06 -punkten 4.898430679951049e-06 -ügel 4.898430679951049e-06 -▁Beispielsweise 4.898430679951049e-06 -▁Residenz 4.898430679951049e-06 -▁benutzer 4.898430679951049e-06 -▁coat 4.898430679951049e-06 -▁overl 4.898430679951049e-06 -▁starker 4.898430679951049e-06 -diensten 4.8946422571513961e-06 -ereign 4.8946422571513961e-06 -info 4.8946422571513961e-06 -▁Andrea 4.8946422571513961e-06 -▁Ausgangs 4.8946422571513961e-06 -▁Davon 4.8946422571513961e-06 -▁Exekut 4.8946422571513961e-06 -▁Feiert 4.8946422571513961e-06 -▁Grupp 4.8946422571513961e-06 -▁Publikationen 4.8946422571513961e-06 -▁fare 4.8946422571513961e-06 -▁spl 4.8946422571513961e-06 -▁tons 4.8946422571513961e-06 --Produkte 4.8908538343517432e-06 -gien 4.8908538343517432e-06 -otion 4.8908538343517432e-06 -schrauber 4.8908538343517432e-06 -zwungen 4.8908538343517432e-06 -▁Alten 4.8908538343517432e-06 -▁Embry 4.8908538343517432e-06 -▁Endes 4.8908538343517432e-06 -▁Official 4.8908538343517432e-06 -▁Poettering 4.8908538343517432e-06 -▁Provin 4.8908538343517432e-06 -▁irgendwo 4.8908538343517432e-06 -▁massiven 4.8908538343517432e-06 -▁starb 4.8908538343517432e-06 -▁tracking 4.8908538343517432e-06 -▁wirst 4.8908538343517432e-06 -blieben 4.88706541155209e-06 -elefon 4.88706541155209e-06 -itches 4.88706541155209e-06 -xim 4.88706541155209e-06 -▁Besatzung 4.88706541155209e-06 -▁Frequenz 4.88706541155209e-06 -▁GN 4.88706541155209e-06 -▁Gegenden 4.88706541155209e-06 -▁Spock 4.88706541155209e-06 -▁Standorten 4.88706541155209e-06 -▁gebucht 4.88706541155209e-06 -▁mittelalterlichen 4.88706541155209e-06 -▁zuständige 4.88706541155209e-06 -▁übertrieben 4.88706541155209e-06 -hilf 4.8832769887524383e-06 -iane 4.8832769887524383e-06 -plate 4.8832769887524383e-06 -wider 4.8832769887524383e-06 -yram 4.8832769887524383e-06 -▁Kass 4.8832769887524383e-06 -▁Malaga 4.8832769887524383e-06 -▁Verwand 4.8832769887524383e-06 -▁Volumen 4.8832769887524383e-06 -▁algorith 4.8832769887524383e-06 -▁curr 4.8832769887524383e-06 -▁encl 4.8832769887524383e-06 -▁exploited 4.8832769887524383e-06 -▁grid 4.8832769887524383e-06 -▁leitet 4.8832769887524383e-06 -▁painful 4.8832769887524383e-06 -▁pipeline 4.8832769887524383e-06 -▁resol 4.8832769887524383e-06 --Code 4.8794885659527854e-06 --Vers 4.8794885659527854e-06 -low 4.8794885659527854e-06 -stechnologien 4.8794885659527854e-06 -vereinbarung 4.8794885659527854e-06 -zn 4.8794885659527854e-06 -▁Käse 4.8794885659527854e-06 -▁Nachteile 4.8794885659527854e-06 -▁Oliver 4.8794885659527854e-06 -▁einschl 4.8794885659527854e-06 -▁lust 4.8794885659527854e-06 -▁refreshing 4.8794885659527854e-06 -▁sichert 4.8794885659527854e-06 -ehouse 4.8757001431531325e-06 -gir 4.8757001431531325e-06 -▁Funktionsweise 4.8757001431531325e-06 -▁HI 4.8757001431531325e-06 -▁Param 4.8757001431531325e-06 -▁abhängen 4.8757001431531325e-06 -▁affirm 4.8757001431531325e-06 -▁legacy 4.8757001431531325e-06 -▁loy 4.8757001431531325e-06 -▁worlds 4.8757001431531325e-06 -Sh 4.87191172035348e-06 -ennungs 4.87191172035348e-06 -immten 4.87191172035348e-06 -▁Dream 4.87191172035348e-06 -▁Taschen 4.87191172035348e-06 -▁aboard 4.87191172035348e-06 -▁bestät 4.87191172035348e-06 -▁confidentiality 4.87191172035348e-06 -▁kreative 4.87191172035348e-06 -▁shorter 4.87191172035348e-06 -▁verhandelt 4.87191172035348e-06 -ionally 4.8681232975538267e-06 -jer 4.8681232975538267e-06 -ordnungen 4.8681232975538267e-06 -strich 4.8681232975538267e-06 -wes 4.8681232975538267e-06 -▁forestry 4.8681232975538267e-06 -▁leidet 4.8681232975538267e-06 -▁perfekten 4.8681232975538267e-06 -▁spreading 4.8681232975538267e-06 -▁untergebracht 4.8681232975538267e-06 -▁vir 4.8681232975538267e-06 -ivities 4.8643348747541738e-06 -oyd 4.8643348747541738e-06 -uctions 4.8643348747541738e-06 -ugung 4.8643348747541738e-06 -äub 4.8643348747541738e-06 -▁Ausbruch 4.8643348747541738e-06 -▁CO2-Emissionen 4.8643348747541738e-06 -▁Wäscheservice 4.8643348747541738e-06 -▁dekor 4.8643348747541738e-06 -▁erhebt 4.8643348747541738e-06 -▁freundliches 4.8643348747541738e-06 -▁in-house 4.8643348747541738e-06 -▁konsolid 4.8643348747541738e-06 -▁marble 4.8643348747541738e-06 -▁reluct 4.8643348747541738e-06 -▁reservieren 4.8643348747541738e-06 -endums 4.8605464519545209e-06 -erning 4.8605464519545209e-06 -unction 4.8605464519545209e-06 -▁Aar 4.8605464519545209e-06 -▁Feder 4.8605464519545209e-06 -▁Isch 4.8605464519545209e-06 -▁Kohäsion 4.8605464519545209e-06 -▁Kriterium 4.8605464519545209e-06 -▁Meet 4.8605464519545209e-06 -▁Rauch 4.8605464519545209e-06 -▁alert 4.8605464519545209e-06 -▁anre 4.8605464519545209e-06 -▁diplomatischen 4.8605464519545209e-06 -▁toxic 4.8605464519545209e-06 -▁Änderungs 4.8605464519545209e-06 -größte 4.856758029154868e-06 -krank 4.856758029154868e-06 -oj 4.856758029154868e-06 -wär 4.856758029154868e-06 -▁Finanzielle 4.856758029154868e-06 -▁Inzwischen 4.856758029154868e-06 -▁Jim 4.856758029154868e-06 -▁Py 4.856758029154868e-06 -▁Räumlichkeiten 4.856758029154868e-06 -▁Schritten 4.856758029154868e-06 -▁anst 4.856758029154868e-06 -▁kommende 4.856758029154868e-06 -▁persist 4.856758029154868e-06 -▁sof 4.856758029154868e-06 -ceive 4.852969606355216e-06 -edt 4.852969606355216e-06 -lect 4.852969606355216e-06 -machung 4.852969606355216e-06 -▁13th 4.852969606355216e-06 -▁Etage 4.852969606355216e-06 -▁Medic 4.852969606355216e-06 -▁Sitzungsperiode 4.852969606355216e-06 -▁intelligente 4.852969606355216e-06 -▁renewal 4.852969606355216e-06 -▁unified 4.852969606355216e-06 -▁übereinstimmen 4.852969606355216e-06 -▁Aufpreis 4.8491811835555631e-06 -▁Beibehaltung 4.8491811835555631e-06 -▁Feat 4.8491811835555631e-06 -▁Fischereiabkommen 4.8491811835555631e-06 -▁entf 4.8491811835555631e-06 -▁evenings 4.8491811835555631e-06 -▁graphical 4.8491811835555631e-06 -▁lev 4.8491811835555631e-06 -▁mittelständ 4.8491811835555631e-06 -▁pricing 4.8491811835555631e-06 -42 4.84539276075591e-06 -betreiber 4.84539276075591e-06 -umes 4.84539276075591e-06 -▁Century 4.84539276075591e-06 -▁DEM 4.84539276075591e-06 -▁Secretary-General 4.84539276075591e-06 -▁Skif 4.84539276075591e-06 -▁ausgeben 4.84539276075591e-06 -▁gestrichen 4.84539276075591e-06 -▁greift 4.84539276075591e-06 -▁irgendwie 4.84539276075591e-06 -▁ox 4.84539276075591e-06 -▁retained 4.84539276075591e-06 -atar 4.8416043379562573e-06 -▁Anschließend 4.8416043379562573e-06 -▁Genauigkeit 4.8416043379562573e-06 -▁Upper 4.8416043379562573e-06 -▁begrenzten 4.8416043379562573e-06 -▁crow 4.8416043379562573e-06 -▁incidents 4.8416043379562573e-06 -▁serb 4.8416043379562573e-06 -▁umbr 4.8416043379562573e-06 -iba 4.8378159151566044e-06 -otz 4.8378159151566044e-06 -sorge 4.8378159151566044e-06 -test 4.8378159151566044e-06 -▁-D 4.8378159151566044e-06 -▁Anleger 4.8378159151566044e-06 -▁Battle 4.8378159151566044e-06 -▁Beautiful 4.8378159151566044e-06 -▁Cancún 4.8378159151566044e-06 -▁Erz 4.8378159151566044e-06 -▁Final 4.8378159151566044e-06 -▁Franco 4.8378159151566044e-06 -▁Rate 4.8378159151566044e-06 -▁Whatever 4.8378159151566044e-06 -▁resign 4.8378159151566044e-06 -41 4.8340274923569515e-06 -bewegungen 4.8340274923569515e-06 -herapie 4.8340274923569515e-06 -male 4.8340274923569515e-06 -orgung 4.8340274923569515e-06 -stock 4.8340274923569515e-06 -▁Erziehung 4.8340274923569515e-06 -▁Flüchtlingen 4.8340274923569515e-06 -▁Gesetzgebungs 4.8340274923569515e-06 -▁Präsent 4.8340274923569515e-06 -▁erwarte 4.8340274923569515e-06 -▁herange 4.8340274923569515e-06 -▁mediterran 4.8340274923569515e-06 -etry 4.8302390695572986e-06 -freies 4.8302390695572986e-06 -ikers 4.8302390695572986e-06 -▁Dolomites 4.8302390695572986e-06 -▁Grundprinzip 4.8302390695572986e-06 -▁allt 4.8302390695572986e-06 -▁angemessener 4.8302390695572986e-06 -▁antique 4.8302390695572986e-06 -▁bathing 4.8302390695572986e-06 -▁mes 4.8302390695572986e-06 -▁quarters 4.8302390695572986e-06 -▁zoom 4.8302390695572986e-06 -asy 4.8264506467576458e-06 -icker 4.8264506467576458e-06 -kept 4.8264506467576458e-06 -kit 4.8264506467576458e-06 -schub 4.8264506467576458e-06 -ändige 4.8264506467576458e-06 -▁Bestrebungen 4.8264506467576458e-06 -▁Cardiff 4.8264506467576458e-06 -▁Dür 4.8264506467576458e-06 -▁Gewerk 4.8264506467576458e-06 -▁Weste 4.8264506467576458e-06 -▁einsetz 4.8264506467576458e-06 -▁encourages 4.8264506467576458e-06 -▁fridge 4.8264506467576458e-06 -▁versetzen 4.8264506467576458e-06 --res 4.8226622239579937e-06 -62 4.8226622239579937e-06 -jen 4.8226622239579937e-06 -▁Durchführungs 4.8226622239579937e-06 -▁Meas 4.8226622239579937e-06 -▁attacked 4.8226622239579937e-06 -▁confidential 4.8226622239579937e-06 -▁enforce 4.8226622239579937e-06 -▁morgens 4.8226622239579937e-06 -▁optimiert 4.8226622239579937e-06 -▁pflegen 4.8226622239579937e-06 -▁ras 4.8226622239579937e-06 -▁vierte 4.8226622239579937e-06 --Gesch 4.8188738011583408e-06 -bird 4.8188738011583408e-06 -fi 4.8188738011583408e-06 -fun 4.8188738011583408e-06 -füllung 4.8188738011583408e-06 -istall 4.8188738011583408e-06 -mäßigkeit 4.8188738011583408e-06 -rüstung 4.8188738011583408e-06 -stellern 4.8188738011583408e-06 -▁5000 4.8188738011583408e-06 -▁Sender 4.8188738011583408e-06 -▁Verhaltenskodex 4.8188738011583408e-06 -▁automobile 4.8188738011583408e-06 -▁settlements 4.8188738011583408e-06 -▁verwandeln 4.8188738011583408e-06 -▁vorgesehene 4.8188738011583408e-06 -blem 4.8150853783586879e-06 -isierter 4.8150853783586879e-06 -standes 4.8150853783586879e-06 -▁Croatian 4.8150853783586879e-06 -▁Engel 4.8150853783586879e-06 -▁Mannschaft 4.8150853783586879e-06 -▁Zentren 4.8150853783586879e-06 -▁anzuerkennen 4.8150853783586879e-06 -▁convers 4.8150853783586879e-06 -▁donor 4.8150853783586879e-06 -▁organs 4.8150853783586879e-06 -▁regener 4.8150853783586879e-06 -▁vermieten 4.8150853783586879e-06 --Anwendungen 4.811296955559035e-06 -sträger 4.811296955559035e-06 -▁Syr 4.811296955559035e-06 -▁dynamisch 4.811296955559035e-06 -▁hybrid 4.811296955559035e-06 -▁müh 4.811296955559035e-06 -▁refin 4.811296955559035e-06 -bestätigung 4.8075085327593821e-06 -gültig 4.8075085327593821e-06 -oland 4.8075085327593821e-06 -▁10th 4.8075085327593821e-06 -▁Diagn 4.8075085327593821e-06 -▁Erholungs 4.8075085327593821e-06 -▁Five 4.8075085327593821e-06 -▁McCart 4.8075085327593821e-06 -▁Ner 4.8075085327593821e-06 -▁Stabilisierung 4.8075085327593821e-06 -▁Verantwort 4.8075085327593821e-06 -▁nachfolgenden 4.8075085327593821e-06 -▁nick 4.8075085327593821e-06 -▁trick 4.8075085327593821e-06 -eading 4.8037201099597293e-06 -itäre 4.8037201099597293e-06 -▁1900 4.8037201099597293e-06 -▁Iz 4.8037201099597293e-06 -▁Kling 4.8037201099597293e-06 -▁Lyn 4.8037201099597293e-06 -▁Nordirland 4.8037201099597293e-06 -▁encountered 4.8037201099597293e-06 -▁fits 4.8037201099597293e-06 -▁hang 4.8037201099597293e-06 -▁jur 4.8037201099597293e-06 -▁operative 4.8037201099597293e-06 -▁recognises 4.8037201099597293e-06 -▁schriftlichen 4.8037201099597293e-06 -dukt 4.7999316871600764e-06 -reihe 4.7999316871600764e-06 -▁201 4.7999316871600764e-06 -▁Beob 4.7999316871600764e-06 -▁Somalia 4.7999316871600764e-06 -▁Verständ 4.7999316871600764e-06 -▁bedingt 4.7999316871600764e-06 -▁beeindruckende 4.7999316871600764e-06 -▁definitiv 4.7999316871600764e-06 -▁erfr 4.7999316871600764e-06 -▁exzellente 4.7999316871600764e-06 -▁freiwillig 4.7999316871600764e-06 -▁persu 4.7999316871600764e-06 -▁petition 4.7999316871600764e-06 -▁profitiert 4.7999316871600764e-06 -▁versprechen 4.7999316871600764e-06 -hoch 4.7961432643604235e-06 -zio 4.7961432643604235e-06 -▁DO 4.7961432643604235e-06 -▁Nordkorea 4.7961432643604235e-06 -▁Promenade 4.7961432643604235e-06 -▁Taste 4.7961432643604235e-06 -▁Villen 4.7961432643604235e-06 -▁Vorbe 4.7961432643604235e-06 -▁aston 4.7961432643604235e-06 -▁bestehend 4.7961432643604235e-06 -▁lifts 4.7961432643604235e-06 -▁ordering 4.7961432643604235e-06 -gefähr 4.7923548415607714e-06 -igma 4.7923548415607714e-06 -ishop 4.7923548415607714e-06 -▁Anf 4.7923548415607714e-06 -▁Keller 4.7923548415607714e-06 -▁Lit 4.7923548415607714e-06 -▁Magn 4.7923548415607714e-06 -▁Paar 4.7923548415607714e-06 -▁Patient 4.7923548415607714e-06 -▁Pixel 4.7923548415607714e-06 -▁Straß 4.7923548415607714e-06 -▁Toten 4.7923548415607714e-06 -▁aufgetreten 4.7923548415607714e-06 -▁caf 4.7923548415607714e-06 -▁processor 4.7923548415607714e-06 -▁unternehmer 4.7923548415607714e-06 -cco 4.7885664187611185e-06 -etty 4.7885664187611185e-06 -kunft 4.7885664187611185e-06 -modelle 4.7885664187611185e-06 -ätzlich 4.7885664187611185e-06 -▁Begleitung 4.7885664187611185e-06 -▁Fert 4.7885664187611185e-06 -▁Kamp 4.7885664187611185e-06 -▁Marg 4.7885664187611185e-06 -▁Mittagessen 4.7885664187611185e-06 -▁Nachweis 4.7885664187611185e-06 -▁Venet 4.7885664187611185e-06 -▁beam 4.7885664187611185e-06 -▁chips 4.7885664187611185e-06 -▁dioxide 4.7885664187611185e-06 -▁erholen 4.7885664187611185e-06 -▁incons 4.7885664187611185e-06 -arisch 4.7847779959614656e-06 -bildungen 4.7847779959614656e-06 -oints 4.7847779959614656e-06 -ubt 4.7847779959614656e-06 -zyklus 4.7847779959614656e-06 -▁Euratom 4.7847779959614656e-06 -▁Futter 4.7847779959614656e-06 -▁Parking 4.7847779959614656e-06 -▁Wid 4.7847779959614656e-06 -▁kritisieren 4.7847779959614656e-06 -▁Č 4.7847779959614656e-06 -setzungs 4.7809895731618128e-06 -▁Angeboten 4.7809895731618128e-06 -▁Chirac 4.7809895731618128e-06 -▁Famil 4.7809895731618128e-06 -▁Lut 4.7809895731618128e-06 -▁Rohstoffe 4.7809895731618128e-06 -▁Schlüss 4.7809895731618128e-06 -▁alternativen 4.7809895731618128e-06 -▁bass 4.7809895731618128e-06 -▁dub 4.7809895731618128e-06 -▁medizin 4.7809895731618128e-06 -▁safeguards 4.7809895731618128e-06 -▁temporarily 4.7809895731618128e-06 -▁unn 4.7809895731618128e-06 -▁versorgen 4.7809895731618128e-06 --2006 4.77720115036216e-06 -alitäten 4.77720115036216e-06 -uate 4.77720115036216e-06 -useum 4.77720115036216e-06 -▁Deal 4.77720115036216e-06 -▁Estland 4.77720115036216e-06 -▁Sachverständ 4.77720115036216e-06 -▁Streitkräfte 4.77720115036216e-06 -▁Sum 4.77720115036216e-06 -▁aufrufen 4.77720115036216e-06 -▁intensiven 4.77720115036216e-06 -IST 4.773412727562507e-06 -cluded 4.773412727562507e-06 -dar 4.773412727562507e-06 -flügel 4.773412727562507e-06 -itzt 4.773412727562507e-06 -olid 4.773412727562507e-06 -owse 4.773412727562507e-06 -▁Bogen 4.773412727562507e-06 -▁Bru 4.773412727562507e-06 -▁Verpackungs 4.773412727562507e-06 -▁dol 4.773412727562507e-06 -▁erzielte 4.773412727562507e-06 -▁ideology 4.773412727562507e-06 -▁kräft 4.773412727562507e-06 -▁shaped 4.773412727562507e-06 -▁tailor 4.773412727562507e-06 -▁trendy 4.773412727562507e-06 -▁verbind 4.773412727562507e-06 -acter 4.7696243047628541e-06 -antasie 4.7696243047628541e-06 -etre 4.7696243047628541e-06 -iguous 4.7696243047628541e-06 -wogen 4.7696243047628541e-06 -▁Mun 4.7696243047628541e-06 -▁Property 4.7696243047628541e-06 -▁Restaur 4.7696243047628541e-06 -▁beschleunigt 4.7696243047628541e-06 -▁eingenommen 4.7696243047628541e-06 -▁nett 4.7696243047628541e-06 -▁studios 4.7696243047628541e-06 --Qu 4.765835881963202e-06 --line 4.765835881963202e-06 -▁Megap 4.765835881963202e-06 -▁Must 4.765835881963202e-06 -▁Netzwerke 4.765835881963202e-06 -▁Zusammenfassung 4.765835881963202e-06 -▁agieren 4.765835881963202e-06 -▁blow 4.765835881963202e-06 -▁cant 4.765835881963202e-06 -▁elektron 4.765835881963202e-06 -▁liefer 4.765835881963202e-06 -▁unüber 4.765835881963202e-06 -arien 4.7620474591635491e-06 -artial 4.7620474591635491e-06 -ctor 4.7620474591635491e-06 -läufigen 4.7620474591635491e-06 -makers 4.7620474591635491e-06 -▁Kondition 4.7620474591635491e-06 -▁Stadtm 4.7620474591635491e-06 -▁Sänger 4.7620474591635491e-06 -▁ThyssenKrupp 4.7620474591635491e-06 -▁foss 4.7620474591635491e-06 -▁kapital 4.7620474591635491e-06 -▁municipality 4.7620474591635491e-06 -▁neo 4.7620474591635491e-06 -▁scripts 4.7620474591635491e-06 -Point 4.7582590363638962e-06 -insel 4.7582590363638962e-06 -riage 4.7582590363638962e-06 -▁Decision 4.7582590363638962e-06 -▁Häusern 4.7582590363638962e-06 -▁Led 4.7582590363638962e-06 -▁Mön 4.7582590363638962e-06 -▁RES 4.7582590363638962e-06 -▁Scientific 4.7582590363638962e-06 -▁batteries 4.7582590363638962e-06 -▁casual 4.7582590363638962e-06 -▁enhancing 4.7582590363638962e-06 -▁hydro 4.7582590363638962e-06 -▁männ 4.7582590363638962e-06 -▁nan 4.7582590363638962e-06 -▁playground 4.7582590363638962e-06 -▁stri 4.7582590363638962e-06 -▁tyr 4.7582590363638962e-06 -abs 4.7544706135642434e-06 -ankungen 4.7544706135642434e-06 -länd 4.7544706135642434e-06 -twer 4.7544706135642434e-06 -uiten 4.7544706135642434e-06 -örungen 4.7544706135642434e-06 -▁Attra 4.7544706135642434e-06 -▁Birma 4.7544706135642434e-06 -▁Boots 4.7544706135642434e-06 -▁Erhebung 4.7544706135642434e-06 -▁beit 4.7544706135642434e-06 -▁einrichten 4.7544706135642434e-06 -▁flexibler 4.7544706135642434e-06 -▁holder 4.7544706135642434e-06 -▁magazines 4.7544706135642434e-06 -▁merch 4.7544706135642434e-06 -▁underp 4.7544706135642434e-06 -amento 4.7506821907645905e-06 -ellt 4.7506821907645905e-06 -iversität 4.7506821907645905e-06 -kennen 4.7506821907645905e-06 -orking 4.7506821907645905e-06 -unik 4.7506821907645905e-06 -▁10.000 4.7506821907645905e-06 -▁Neuen 4.7506821907645905e-06 -▁economical 4.7506821907645905e-06 -▁impressed 4.7506821907645905e-06 -▁nad 4.7506821907645905e-06 -▁reloc 4.7506821907645905e-06 -▁shell 4.7506821907645905e-06 -▁verbundene 4.7506821907645905e-06 -,1 4.7468937679649376e-06 -imeter 4.7468937679649376e-06 -ologically 4.7468937679649376e-06 -zwecken 4.7468937679649376e-06 -▁Brosch 4.7468937679649376e-06 -▁Eden 4.7468937679649376e-06 -▁Europä 4.7468937679649376e-06 -▁Kug 4.7468937679649376e-06 -▁Romul 4.7468937679649376e-06 -▁floods 4.7468937679649376e-06 -▁responding 4.7468937679649376e-06 -beding 4.7431053451652847e-06 -etrieben 4.7431053451652847e-06 -faktor 4.7431053451652847e-06 -jana 4.7431053451652847e-06 -otted 4.7431053451652847e-06 -tex 4.7431053451652847e-06 -werb 4.7431053451652847e-06 -▁County 4.7431053451652847e-06 -▁Einzelf 4.7431053451652847e-06 -▁Feier 4.7431053451652847e-06 -▁GL 4.7431053451652847e-06 -▁Gebäudes 4.7431053451652847e-06 -▁Klaren 4.7431053451652847e-06 -▁LEG 4.7431053451652847e-06 -▁Sandstrand 4.7431053451652847e-06 -▁Self 4.7431053451652847e-06 -▁categor 4.7431053451652847e-06 -▁evaluated 4.7431053451652847e-06 -▁hervorzuheben 4.7431053451652847e-06 -▁mounted 4.7431053451652847e-06 -▁wachs 4.7431053451652847e-06 -eping 4.7393169223656318e-06 -hire 4.7393169223656318e-06 -schaftlichen 4.7393169223656318e-06 -unterricht 4.7393169223656318e-06 -verhältnis 4.7393169223656318e-06 -zlich 4.7393169223656318e-06 -▁anerkannten 4.7393169223656318e-06 -▁angem 4.7393169223656318e-06 -▁aufgeführten 4.7393169223656318e-06 -▁aufgegeben 4.7393169223656318e-06 -▁betragen 4.7393169223656318e-06 -▁erbringen 4.7393169223656318e-06 -▁gesenkt 4.7393169223656318e-06 -▁konkreter 4.7393169223656318e-06 -▁puls 4.7393169223656318e-06 -▁unzählige 4.7393169223656318e-06 -▁wire 4.7393169223656318e-06 -IEN 4.73552849956598e-06 -exe 4.73552849956598e-06 -hotels 4.73552849956598e-06 -terrasse 4.73552849956598e-06 -▁Astrium 4.73552849956598e-06 -▁Twin 4.73552849956598e-06 -▁Ungleichheit 4.73552849956598e-06 -▁Vertra 4.73552849956598e-06 -▁Wende 4.73552849956598e-06 -▁barg 4.73552849956598e-06 -▁persecution 4.73552849956598e-06 -▁potato 4.73552849956598e-06 -anium 4.7317400767663269e-06 -imon 4.7317400767663269e-06 -ioxid 4.7317400767663269e-06 -undi 4.7317400767663269e-06 -▁Assoc 4.7317400767663269e-06 -▁Bünd 4.7317400767663269e-06 -▁Cort 4.7317400767663269e-06 -▁Franzosen 4.7317400767663269e-06 -▁Konsolidierung 4.7317400767663269e-06 -▁Nutzern 4.7317400767663269e-06 -▁enemies 4.7317400767663269e-06 -▁imagination 4.7317400767663269e-06 -▁ratify 4.7317400767663269e-06 -▁stimulus 4.7317400767663269e-06 -ehand 4.727951653966674e-06 -icable 4.727951653966674e-06 -▁Bandbreite 4.727951653966674e-06 -▁Beobachtungs 4.727951653966674e-06 -▁attitudes 4.727951653966674e-06 -▁befreit 4.727951653966674e-06 -▁separat 4.727951653966674e-06 -ICA 4.7241632311670211e-06 -emos 4.7241632311670211e-06 -ieth 4.7241632311670211e-06 -iè 4.7241632311670211e-06 -▁Champions 4.7241632311670211e-06 -▁Energiem 4.7241632311670211e-06 -▁Liechtenstein 4.7241632311670211e-06 -▁Pfeiler 4.7241632311670211e-06 -▁beauftragt 4.7241632311670211e-06 -▁erhielten 4.7241632311670211e-06 -▁stützt 4.7241632311670211e-06 -▁verantwortungsbew 4.7241632311670211e-06 -heet 4.7203748083673682e-06 -itä 4.7203748083673682e-06 -offt 4.7203748083673682e-06 -spot 4.7203748083673682e-06 -▁Harmonie 4.7203748083673682e-06 -▁Implement 4.7203748083673682e-06 -▁Orth 4.7203748083673682e-06 -▁Partition 4.7203748083673682e-06 -▁Spannung 4.7203748083673682e-06 -▁Tragen 4.7203748083673682e-06 -▁broadly 4.7203748083673682e-06 -▁conducting 4.7203748083673682e-06 -▁correction 4.7203748083673682e-06 -▁fügt 4.7203748083673682e-06 -▁neighborhood 4.7203748083673682e-06 -▁remed 4.7203748083673682e-06 -▁responded 4.7203748083673682e-06 --Im 4.7165863855677153e-06 -athan 4.7165863855677153e-06 -dring 4.7165863855677153e-06 -gelegenen 4.7165863855677153e-06 -opez 4.7165863855677153e-06 -zulassen 4.7165863855677153e-06 -▁Button 4.7165863855677153e-06 -▁Disziplin 4.7165863855677153e-06 -▁Einmal 4.7165863855677153e-06 -▁Try 4.7165863855677153e-06 -▁behindern 4.7165863855677153e-06 -▁democratically 4.7165863855677153e-06 -▁gelegentlich 4.7165863855677153e-06 -▁gewaltige 4.7165863855677153e-06 -▁ggf. 4.7165863855677153e-06 -▁stecken 4.7165863855677153e-06 -.m 4.7127979627680624e-06 -ading 4.7127979627680624e-06 -anol 4.7127979627680624e-06 -übertragung 4.7127979627680624e-06 -▁Beschwerde 4.7127979627680624e-06 -▁Cape 4.7127979627680624e-06 -▁EW 4.7127979627680624e-06 -▁ble 4.7127979627680624e-06 -▁burning 4.7127979627680624e-06 -▁dependen 4.7127979627680624e-06 -▁irgendwann 4.7127979627680624e-06 -▁maßgeblich 4.7127979627680624e-06 -▁pav 4.7127979627680624e-06 -▁privileges 4.7127979627680624e-06 -▁races 4.7127979627680624e-06 -▁routine 4.7127979627680624e-06 -▁vorzugehen 4.7127979627680624e-06 -kopf 4.7090095399684095e-06 -▁ALDE 4.7090095399684095e-06 -▁Bedrohungen 4.7090095399684095e-06 -▁Fett 4.7090095399684095e-06 -▁Match 4.7090095399684095e-06 -▁Monitor 4.7090095399684095e-06 -▁Umstellung 4.7090095399684095e-06 -▁Wicht 4.7090095399684095e-06 -▁Zeilen 4.7090095399684095e-06 -▁assessing 4.7090095399684095e-06 -▁captured 4.7090095399684095e-06 -▁förder 4.7090095399684095e-06 -▁gedruckt 4.7090095399684095e-06 -▁inspections 4.7090095399684095e-06 -▁ses 4.7090095399684095e-06 -endungen 4.7052211171687575e-06 -uri 4.7052211171687575e-06 -▁Algerien 4.7052211171687575e-06 -▁Begrenzung 4.7052211171687575e-06 -▁Däm 4.7052211171687575e-06 -▁Niger 4.7052211171687575e-06 -▁Perfect 4.7052211171687575e-06 -▁buch 4.7052211171687575e-06 -▁clo 4.7052211171687575e-06 -▁fer 4.7052211171687575e-06 -▁übernahm 4.7052211171687575e-06 -access 4.7014326943691046e-06 -amme 4.7014326943691046e-06 -essim 4.7014326943691046e-06 -iquen 4.7014326943691046e-06 -uhl 4.7014326943691046e-06 -▁Father 4.7014326943691046e-06 -▁Zoom 4.7014326943691046e-06 -▁continuity 4.7014326943691046e-06 -▁geschah 4.7014326943691046e-06 -▁preserving 4.7014326943691046e-06 -▁titles 4.7014326943691046e-06 -quir 4.6976442715694517e-06 -roup 4.6976442715694517e-06 -ächten 4.6976442715694517e-06 -▁Akademie 4.6976442715694517e-06 -▁Designs 4.6976442715694517e-06 -▁Subsidiaritätsprinzip 4.6976442715694517e-06 -▁Vollständigkeit 4.6976442715694517e-06 -▁disadvantage 4.6976442715694517e-06 -▁kW 4.6976442715694517e-06 -▁visibility 4.6976442715694517e-06 -▁würd 4.6976442715694517e-06 --Programms 4.6938558487697988e-06 -poses 4.6938558487697988e-06 -uta 4.6938558487697988e-06 -verträge 4.6938558487697988e-06 -ässige 4.6938558487697988e-06 -▁Mes 4.6938558487697988e-06 -▁Portfolio 4.6938558487697988e-06 -▁Thessaloniki 4.6938558487697988e-06 -▁YOU 4.6938558487697988e-06 -▁höherer 4.6938558487697988e-06 -▁sau 4.6938558487697988e-06 -▁stetig 4.6938558487697988e-06 -▁understandable 4.6938558487697988e-06 -AX 4.6900674259701459e-06 -abend 4.6900674259701459e-06 -fect 4.6900674259701459e-06 -κ 4.6900674259701459e-06 -▁Auß 4.6900674259701459e-06 -▁Economy 4.6900674259701459e-06 -▁Ermitt 4.6900674259701459e-06 -▁Texten 4.6900674259701459e-06 -▁Zeitalter 4.6900674259701459e-06 -▁astronom 4.6900674259701459e-06 -▁fisher 4.6900674259701459e-06 -▁folgte 4.6900674259701459e-06 -▁genetisch 4.6900674259701459e-06 -▁imbalances 4.6900674259701459e-06 -▁kontr 4.6900674259701459e-06 -anzierung 4.686279003170493e-06 -ighth 4.686279003170493e-06 -plung 4.686279003170493e-06 -sport 4.686279003170493e-06 -werter 4.686279003170493e-06 -▁Jane 4.686279003170493e-06 -▁Takt 4.686279003170493e-06 -▁WordPress 4.686279003170493e-06 -▁erfolg 4.686279003170493e-06 -▁experiments 4.686279003170493e-06 -▁inappropriate 4.686279003170493e-06 -▁invasion 4.686279003170493e-06 -▁mand 4.686279003170493e-06 -easant 4.68249058037084e-06 -iein 4.68249058037084e-06 -sprechen 4.68249058037084e-06 -stär 4.68249058037084e-06 -thing 4.68249058037084e-06 -town 4.68249058037084e-06 -▁Bestände 4.68249058037084e-06 -▁Schad 4.68249058037084e-06 -▁Schauspieler 4.68249058037084e-06 -▁aufgegriffen 4.68249058037084e-06 -▁institute 4.68249058037084e-06 -▁iranischen 4.68249058037084e-06 -▁radi 4.68249058037084e-06 -▁rocks 4.68249058037084e-06 -▁verfolg 4.68249058037084e-06 -euert 4.6787021575711872e-06 -fo 4.6787021575711872e-06 -gorith 4.6787021575711872e-06 -ither 4.6787021575711872e-06 -rative 4.6787021575711872e-06 -▁1.000 4.6787021575711872e-06 -▁14th 4.6787021575711872e-06 -▁II. 4.6787021575711872e-06 -▁Unabhäng 4.6787021575711872e-06 -▁coin 4.6787021575711872e-06 -▁exacerb 4.6787021575711872e-06 -▁kommunistischen 4.6787021575711872e-06 -▁outermost 4.6787021575711872e-06 -▁pushing 4.6787021575711872e-06 -▁versuchte 4.6787021575711872e-06 --wing 4.6749137347715352e-06 -antwort 4.6749137347715352e-06 -nam 4.6749137347715352e-06 -reuen 4.6749137347715352e-06 -▁Demo 4.6749137347715352e-06 -▁Eins 4.6749137347715352e-06 -▁Enth 4.6749137347715352e-06 -▁Gam 4.6749137347715352e-06 -▁Wollen 4.6749137347715352e-06 -▁erstklassige 4.6749137347715352e-06 -▁estimate 4.6749137347715352e-06 -▁nachfol 4.6749137347715352e-06 -▁nav 4.6749137347715352e-06 -▁quoted 4.6749137347715352e-06 -RS 4.6711253119718823e-06 -UE 4.6711253119718823e-06 -elbe 4.6711253119718823e-06 -igm 4.6711253119718823e-06 -isher 4.6711253119718823e-06 -stitut 4.6711253119718823e-06 -unters 4.6711253119718823e-06 -ärten 4.6711253119718823e-06 -▁Adress 4.6711253119718823e-06 -▁Fertig 4.6711253119718823e-06 -▁Gitar 4.6711253119718823e-06 -▁Palais 4.6711253119718823e-06 -▁Parte 4.6711253119718823e-06 -▁Reichweite 4.6711253119718823e-06 -▁Warm 4.6711253119718823e-06 -▁deployment 4.6711253119718823e-06 -▁entnehmen 4.6711253119718823e-06 -▁fossil 4.6711253119718823e-06 -▁genehm 4.6711253119718823e-06 -▁gründete 4.6711253119718823e-06 -▁measurements 4.6711253119718823e-06 -▁timing 4.6711253119718823e-06 -▁verlängern 4.6711253119718823e-06 -ofen 4.6673368891722294e-06 -tegr 4.6673368891722294e-06 -▁Ausschüssen 4.6673368891722294e-06 -▁Verschlechterung 4.6673368891722294e-06 -▁Wikitravel-Benutzer 4.6673368891722294e-06 -▁anschauen 4.6673368891722294e-06 -▁closest 4.6673368891722294e-06 -▁demnächst 4.6673368891722294e-06 -▁demselben 4.6673368891722294e-06 -▁directors 4.6673368891722294e-06 -▁emer 4.6673368891722294e-06 -▁juristischen 4.6673368891722294e-06 -gebend 4.6635484663725765e-06 -oured 4.6635484663725765e-06 -pflanzen 4.6635484663725765e-06 -▁Euro-Mediterranean 4.6635484663725765e-06 -▁Himm 4.6635484663725765e-06 -▁Never 4.6635484663725765e-06 -▁activists 4.6635484663725765e-06 -▁aven 4.6635484663725765e-06 -▁injury 4.6635484663725765e-06 -▁jemanden 4.6635484663725765e-06 -▁sow 4.6635484663725765e-06 -▁verläuft 4.6635484663725765e-06 --Part 4.6597600435729236e-06 -55 4.6597600435729236e-06 -beck 4.6597600435729236e-06 -ocon 4.6597600435729236e-06 -▁Reisenden 4.6597600435729236e-06 -▁aufrechtzuerhalten 4.6597600435729236e-06 -▁begeben 4.6597600435729236e-06 -▁gemachten 4.6597600435729236e-06 -▁shad 4.6597600435729236e-06 -▁spam 4.6597600435729236e-06 -▁yacht 4.6597600435729236e-06 -IES 4.6559716207732707e-06 -OUR 4.6559716207732707e-06 -ifs 4.6559716207732707e-06 -nov 4.6559716207732707e-06 -stufe 4.6559716207732707e-06 -▁Eye 4.6559716207732707e-06 -▁Fotografie 4.6559716207732707e-06 -▁Money 4.6559716207732707e-06 -▁Seven 4.6559716207732707e-06 -▁differenz 4.6559716207732707e-06 -▁escal 4.6559716207732707e-06 -▁gefertigt 4.6559716207732707e-06 -▁underest 4.6559716207732707e-06 -▁Überwindung 4.6559716207732707e-06 -▁â 4.6559716207732707e-06 -lebt 4.6521831979736178e-06 -▁Hüt 4.6521831979736178e-06 -▁Mercure 4.6521831979736178e-06 -▁cand 4.6521831979736178e-06 -▁holen 4.6521831979736178e-06 -▁nördlich 4.6521831979736178e-06 -▁pretext 4.6521831979736178e-06 -▁propag 4.6521831979736178e-06 -▁unh 4.6521831979736178e-06 -▁zerstör 4.6521831979736178e-06 -finanz 4.6483947751739649e-06 -stan 4.6483947751739649e-06 -tries 4.6483947751739649e-06 -zung 4.6483947751739649e-06 -▁Authent 4.6483947751739649e-06 -▁Body 4.6483947751739649e-06 -▁Geburts 4.6483947751739649e-06 -▁PHARE 4.6483947751739649e-06 -▁QuarkXPress 4.6483947751739649e-06 -▁attentive 4.6483947751739649e-06 -▁credits 4.6483947751739649e-06 -▁folk 4.6483947751739649e-06 -▁ries 4.6483947751739649e-06 -▁varying 4.6483947751739649e-06 -▁Übereinkunft 4.6483947751739649e-06 --Bus 4.6446063523743129e-06 -FOR 4.6446063523743129e-06 -aires 4.6446063523743129e-06 -ât 4.6446063523743129e-06 -▁Angestellten 4.6446063523743129e-06 -▁Informatik 4.6446063523743129e-06 -▁Tav 4.6446063523743129e-06 -▁Workshop 4.6446063523743129e-06 -▁abschließ 4.6446063523743129e-06 -▁druck 4.6446063523743129e-06 -▁geeinigt 4.6446063523743129e-06 -▁gemeint 4.6446063523743129e-06 -▁problematic 4.6446063523743129e-06 -▁scheinbar 4.6446063523743129e-06 -gehalt 4.64081792957466e-06 -▁1.6 4.64081792957466e-06 -▁Anleitung 4.64081792957466e-06 -▁Handhabung 4.64081792957466e-06 -▁Klavier 4.64081792957466e-06 -▁Produkts 4.64081792957466e-06 -▁Unit 4.64081792957466e-06 -▁bedaure 4.64081792957466e-06 -▁einbeziehen 4.64081792957466e-06 -▁particip 4.64081792957466e-06 -▁statutory 4.64081792957466e-06 -▁unbed 4.64081792957466e-06 --Welt 4.6370295067750071e-06 -berge 4.6370295067750071e-06 -illes 4.6370295067750071e-06 -tax 4.6370295067750071e-06 -urses 4.6370295067750071e-06 -▁Angebots 4.6370295067750071e-06 -▁Catalunya 4.6370295067750071e-06 -▁Schall 4.6370295067750071e-06 -▁Sicherstellung 4.6370295067750071e-06 -▁Vergabe 4.6370295067750071e-06 -▁artic 4.6370295067750071e-06 -▁auszur 4.6370295067750071e-06 -▁depr 4.6370295067750071e-06 -▁devote 4.6370295067750071e-06 -▁kostenfreien 4.6370295067750071e-06 -▁metals 4.6370295067750071e-06 -▁viruses 4.6370295067750071e-06 -▁weiterentwickelt 4.6370295067750071e-06 -agne 4.6332410839753542e-06 -chio 4.6332410839753542e-06 -gewiss 4.6332410839753542e-06 -kapazitäten 4.6332410839753542e-06 -quiry 4.6332410839753542e-06 -▁Bod 4.6332410839753542e-06 -▁Rezept 4.6332410839753542e-06 -▁Tauchen 4.6332410839753542e-06 -▁doct 4.6332410839753542e-06 -▁gathering 4.6332410839753542e-06 -▁retention 4.6332410839753542e-06 -▁seekers 4.6332410839753542e-06 -▁vibr 4.6332410839753542e-06 --east 4.6294526611757013e-06 -meer 4.6294526611757013e-06 -▁Dell 4.6294526611757013e-06 -▁HT 4.6294526611757013e-06 -▁Handbuch 4.6294526611757013e-06 -▁Machine 4.6294526611757013e-06 -▁Schätzungen 4.6294526611757013e-06 -▁Spl 4.6294526611757013e-06 -▁competitions 4.6294526611757013e-06 -▁diplomacy 4.6294526611757013e-06 -▁dropped 4.6294526611757013e-06 -▁interconn 4.6294526611757013e-06 -▁launching 4.6294526611757013e-06 -▁recreation 4.6294526611757013e-06 -▁vegetable 4.6294526611757013e-06 -▁widerspiegelt 4.6294526611757013e-06 --Präsident 4.6256642383760484e-06 -glas 4.6256642383760484e-06 -hrend 4.6256642383760484e-06 -ungsverhältnis 4.6256642383760484e-06 -wählen 4.6256642383760484e-06 -▁Angola 4.6256642383760484e-06 -▁Franc 4.6256642383760484e-06 -▁Jobs 4.6256642383760484e-06 -▁Parc 4.6256642383760484e-06 -▁Schein 4.6256642383760484e-06 -▁betrieb 4.6256642383760484e-06 -▁compet 4.6256642383760484e-06 -▁erhöhte 4.6256642383760484e-06 -▁genocide 4.6256642383760484e-06 -▁öffent 4.6256642383760484e-06 -archie 4.6218758155763955e-06 -eutet 4.6218758155763955e-06 -hus 4.6218758155763955e-06 -owina 4.6218758155763955e-06 -ündeten 4.6218758155763955e-06 -▁6th 4.6218758155763955e-06 -▁Clear 4.6218758155763955e-06 -▁Living 4.6218758155763955e-06 -▁Strafe 4.6218758155763955e-06 -▁Ursprün 4.6218758155763955e-06 -▁Wat 4.6218758155763955e-06 -▁conscience 4.6218758155763955e-06 -▁cooperative 4.6218758155763955e-06 -▁industr 4.6218758155763955e-06 -▁initiate 4.6218758155763955e-06 -▁seinerzeit 4.6218758155763955e-06 -▁untersagt 4.6218758155763955e-06 -KK 4.6180873927767427e-06 -deckung 4.6180873927767427e-06 -errungen 4.6180873927767427e-06 -former 4.6180873927767427e-06 -▁3,5 4.6180873927767427e-06 -▁Anfänger 4.6180873927767427e-06 -▁Begleit 4.6180873927767427e-06 -▁Evalu 4.6180873927767427e-06 -▁Fahren 4.6180873927767427e-06 -▁Soll 4.6180873927767427e-06 -▁einheimischen 4.6180873927767427e-06 -▁feeding 4.6180873927767427e-06 -▁hoping 4.6180873927767427e-06 -▁motorcy 4.6180873927767427e-06 -▁verteidigt 4.6180873927767427e-06 -▁Übersetzer 4.6180873927767427e-06 --Partner 4.6142989699770906e-06 -chtung 4.6142989699770906e-06 -fangen 4.6142989699770906e-06 -mans 4.6142989699770906e-06 -optim 4.6142989699770906e-06 -▁Connect 4.6142989699770906e-06 -▁Finanzminister 4.6142989699770906e-06 -▁Rechtsprechung 4.6142989699770906e-06 -▁Turin 4.6142989699770906e-06 -▁eggs 4.6142989699770906e-06 -▁entziehen 4.6142989699770906e-06 -▁fasc 4.6142989699770906e-06 -▁gentle 4.6142989699770906e-06 -▁harder 4.6142989699770906e-06 -▁harsh 4.6142989699770906e-06 -▁hinder 4.6142989699770906e-06 -▁korrigiert 4.6142989699770906e-06 -▁passage 4.6142989699770906e-06 -▁stainless 4.6142989699770906e-06 -ibration 4.6105105471774377e-06 -konto 4.6105105471774377e-06 -ğ 4.6105105471774377e-06 -▁Cleaning 4.6105105471774377e-06 -▁Inc. 4.6105105471774377e-06 -▁Quite 4.6105105471774377e-06 -▁Rotterdam 4.6105105471774377e-06 -▁dan 4.6105105471774377e-06 -▁führ 4.6105105471774377e-06 -▁hosted 4.6105105471774377e-06 -▁kor 4.6105105471774377e-06 -▁laundry 4.6105105471774377e-06 -▁stro 4.6105105471774377e-06 -▁tired 4.6105105471774377e-06 -affe 4.6067221243777848e-06 -kehrungen 4.6067221243777848e-06 -porary 4.6067221243777848e-06 -richtlinien 4.6067221243777848e-06 -twort 4.6067221243777848e-06 -▁Angehörigen 4.6067221243777848e-06 -▁Aussehen 4.6067221243777848e-06 -▁Perl 4.6067221243777848e-06 -▁Tepp 4.6067221243777848e-06 -▁Vital 4.6067221243777848e-06 -▁Wellnessbereich 4.6067221243777848e-06 -▁belastet 4.6067221243777848e-06 -▁privater 4.6067221243777848e-06 -▁unkompl 4.6067221243777848e-06 -▁withdrawn 4.6067221243777848e-06 --30 4.6029337015781319e-06 --Management 4.6029337015781319e-06 -bezogenen 4.6029337015781319e-06 -chern 4.6029337015781319e-06 -unterschied 4.6029337015781319e-06 -ächtige 4.6029337015781319e-06 -▁-K 4.6029337015781319e-06 -▁Arbeitsprogramm 4.6029337015781319e-06 -▁Articles 4.6029337015781319e-06 -▁Chair 4.6029337015781319e-06 -▁Garage 4.6029337015781319e-06 -▁Inneren 4.6029337015781319e-06 -▁Stille 4.6029337015781319e-06 -▁Turnier 4.6029337015781319e-06 -▁backup 4.6029337015781319e-06 -▁forever 4.6029337015781319e-06 -▁heter 4.6029337015781319e-06 -▁schaden 4.6029337015781319e-06 -▁vermehr 4.6029337015781319e-06 -▁öst 4.6029337015781319e-06 -oner 4.599145278778479e-06 -ortium 4.599145278778479e-06 -▁Cafés 4.599145278778479e-06 -▁Propag 4.599145278778479e-06 -▁Symph 4.599145278778479e-06 -▁TEN 4.599145278778479e-06 -▁Verkehrsmitteln 4.599145278778479e-06 -▁Wine 4.599145278778479e-06 -▁escap 4.599145278778479e-06 -▁precautionary 4.599145278778479e-06 -▁prolong 4.599145278778479e-06 -▁rival 4.599145278778479e-06 -▁seed 4.599145278778479e-06 -▁widerspiegeln 4.599145278778479e-06 -alon 4.5953568559788261e-06 -dor 4.5953568559788261e-06 -hness 4.5953568559788261e-06 -orfen 4.5953568559788261e-06 -▁Container 4.5953568559788261e-06 -▁Einleitung 4.5953568559788261e-06 -▁Fü 4.5953568559788261e-06 -▁Rum 4.5953568559788261e-06 -▁Soph 4.5953568559788261e-06 -▁Toskana 4.5953568559788261e-06 -▁embargo 4.5953568559788261e-06 -▁entsprechender 4.5953568559788261e-06 -▁erzählen 4.5953568559788261e-06 -▁konsequ 4.5953568559788261e-06 -▁manually 4.5953568559788261e-06 -▁positively 4.5953568559788261e-06 -▁seg 4.5953568559788261e-06 -▁untr 4.5953568559788261e-06 -▁veranst 4.5953568559788261e-06 -▁Überwachungs 4.5953568559788261e-06 --Plan 4.5915684331791733e-06 --Res 4.5915684331791733e-06 -imar 4.5915684331791733e-06 -materialien 4.5915684331791733e-06 -▁Hit 4.5915684331791733e-06 -▁Konferenz- 4.5915684331791733e-06 -▁Nachtleben 4.5915684331791733e-06 -▁Step 4.5915684331791733e-06 -▁dirty 4.5915684331791733e-06 -▁fundament 4.5915684331791733e-06 -▁hervorr 4.5915684331791733e-06 -▁lending 4.5915684331791733e-06 -▁miß 4.5915684331791733e-06 -▁multilater 4.5915684331791733e-06 -▁versorgt 4.5915684331791733e-06 -▁verwandelt 4.5915684331791733e-06 -▁wheels 4.5915684331791733e-06 -atastrophe 4.58778001037952e-06 -erra 4.58778001037952e-06 -fahrzeugen 4.58778001037952e-06 -▁Below 4.58778001037952e-06 -▁Competition 4.58778001037952e-06 -▁Friends 4.58778001037952e-06 -▁Polo 4.58778001037952e-06 -▁Süß 4.58778001037952e-06 -▁billigen 4.58778001037952e-06 -▁offensicht 4.58778001037952e-06 -▁pursuit 4.58778001037952e-06 -▁schuldig 4.58778001037952e-06 -▁unch 4.58778001037952e-06 -OY 4.5839915875798683e-06 -VER 4.5839915875798683e-06 -atering 4.5839915875798683e-06 -ograf 4.5839915875798683e-06 -stellbar 4.5839915875798683e-06 -▁1980s 4.5839915875798683e-06 -▁Adjust 4.5839915875798683e-06 -▁Napole 4.5839915875798683e-06 -▁Pyren 4.5839915875798683e-06 -▁listing 4.5839915875798683e-06 -▁potenz 4.5839915875798683e-06 -▁unterschiedlichsten 4.5839915875798683e-06 -▁widmet 4.5839915875798683e-06 -M. 4.5802031647802154e-06 -knüp 4.5802031647802154e-06 -oney 4.5802031647802154e-06 -potenzial 4.5802031647802154e-06 -ägliche 4.5802031647802154e-06 -▁1,000 4.5802031647802154e-06 -▁Feucht 4.5802031647802154e-06 -▁Hack 4.5802031647802154e-06 -▁attending 4.5802031647802154e-06 -▁beunruhigt 4.5802031647802154e-06 -▁commodity 4.5802031647802154e-06 -▁compiled 4.5802031647802154e-06 -▁establishments 4.5802031647802154e-06 -▁sexuelle 4.5802031647802154e-06 -▁successes 4.5802031647802154e-06 -ENS 4.5764147419805625e-06 -irmen 4.5764147419805625e-06 -kos 4.5764147419805625e-06 -vorhaben 4.5764147419805625e-06 -ändlern 4.5764147419805625e-06 -▁2014 4.5764147419805625e-06 -▁Crown 4.5764147419805625e-06 -▁E-mail 4.5764147419805625e-06 -▁Haltestelle 4.5764147419805625e-06 -▁Ign 4.5764147419805625e-06 -▁Istr 4.5764147419805625e-06 -▁brit 4.5764147419805625e-06 -▁erstmal 4.5764147419805625e-06 -▁iT 4.5764147419805625e-06 -▁seam 4.5764147419805625e-06 -▁template 4.5764147419805625e-06 -▁unzureich 4.5764147419805625e-06 -▁¿ 4.5764147419805625e-06 -aufsicht 4.57262631918091e-06 -itual 4.57262631918091e-06 -rott 4.57262631918091e-06 -▁112 4.57262631918091e-06 -▁Ersten 4.57262631918091e-06 -▁Gremien 4.57262631918091e-06 -▁Kino 4.57262631918091e-06 -▁Kontinents 4.57262631918091e-06 -▁MEP 4.57262631918091e-06 -▁Seminare 4.57262631918091e-06 -▁Ship 4.57262631918091e-06 -▁lifelong 4.57262631918091e-06 -▁schr 4.57262631918091e-06 -arbeiteten 4.5688378963812568e-06 -enzo 4.5688378963812568e-06 -ombard 4.5688378963812568e-06 -verk 4.5688378963812568e-06 -▁Aufstellung 4.5688378963812568e-06 -▁Blu 4.5688378963812568e-06 -▁Brea 4.5688378963812568e-06 -▁Einerseits 4.5688378963812568e-06 -▁Internets 4.5688378963812568e-06 -▁Präsidentschafts 4.5688378963812568e-06 -▁Rosa 4.5688378963812568e-06 -▁Schriftsteller 4.5688378963812568e-06 -▁burdens 4.5688378963812568e-06 -▁horiz 4.5688378963812568e-06 -▁osteuropäischen 4.5688378963812568e-06 -▁schwäch 4.5688378963812568e-06 -▁swim 4.5688378963812568e-06 -acional 4.5650494735816039e-06 -attern 4.5650494735816039e-06 -orno 4.5650494735816039e-06 -schritt 4.5650494735816039e-06 -umgebung 4.5650494735816039e-06 -zucht 4.5650494735816039e-06 -üne 4.5650494735816039e-06 -▁Agent 4.5650494735816039e-06 -▁Einsparungen 4.5650494735816039e-06 -▁Gedächtnis 4.5650494735816039e-06 -▁beheben 4.5650494735816039e-06 -▁bookings 4.5650494735816039e-06 -▁disappeared 4.5650494735816039e-06 -▁oberste 4.5650494735816039e-06 -▁stresses 4.5650494735816039e-06 -▁vere 4.5650494735816039e-06 -ologen 4.561261050781951e-06 -technologie 4.561261050781951e-06 -trolled 4.561261050781951e-06 -ummern 4.561261050781951e-06 -▁CIA 4.561261050781951e-06 -▁Geschäftsreisende 4.561261050781951e-06 -▁Langzeit 4.561261050781951e-06 -▁Script 4.561261050781951e-06 -▁anymore 4.561261050781951e-06 -▁coordinating 4.561261050781951e-06 -▁deliberately 4.561261050781951e-06 -▁dossier 4.561261050781951e-06 -▁flexiblen 4.561261050781951e-06 -▁großzügig 4.561261050781951e-06 -▁instances 4.561261050781951e-06 -▁mirr 4.561261050781951e-06 -▁solarium 4.561261050781951e-06 -din 4.5574726279822989e-06 -lingen 4.5574726279822989e-06 -▁230 4.5574726279822989e-06 -▁Aufteilung 4.5574726279822989e-06 -▁Fraktions 4.5574726279822989e-06 -▁Kommissars 4.5574726279822989e-06 -▁betreffende 4.5574726279822989e-06 -▁dam 4.5574726279822989e-06 -▁ehren 4.5574726279822989e-06 -▁ganzheit 4.5574726279822989e-06 -▁missbrauch 4.5574726279822989e-06 -▁myster 4.5574726279822989e-06 -▁quasi 4.5574726279822989e-06 -▁rice 4.5574726279822989e-06 -.10 4.553684205182646e-06 -klicken 4.553684205182646e-06 -mietung 4.553684205182646e-06 -sicherung 4.553684205182646e-06 -▁Check-In 4.553684205182646e-06 -▁Chor 4.553684205182646e-06 -▁Exempl 4.553684205182646e-06 -▁GIMP 4.553684205182646e-06 -▁Nest 4.553684205182646e-06 -▁Stoffen 4.553684205182646e-06 -▁Type 4.553684205182646e-06 -▁conven 4.553684205182646e-06 -▁defines 4.553684205182646e-06 -▁entfernten 4.553684205182646e-06 -▁organising 4.553684205182646e-06 -▁packed 4.553684205182646e-06 -▁toys 4.553684205182646e-06 -▁vertiefen 4.553684205182646e-06 -füg 4.5498957823829931e-06 -ulette 4.5498957823829931e-06 -veloped 4.5498957823829931e-06 -widr 4.5498957823829931e-06 -zugang 4.5498957823829931e-06 -▁Analyt 4.5498957823829931e-06 -▁ELDR 4.5498957823829931e-06 -▁Erkrankungen 4.5498957823829931e-06 -▁Inspir 4.5498957823829931e-06 -▁Klingon 4.5498957823829931e-06 -▁Schriften 4.5498957823829931e-06 -▁Temple 4.5498957823829931e-06 -▁dealers 4.5498957823829931e-06 -▁disarm 4.5498957823829931e-06 -▁einschlägigen 4.5498957823829931e-06 -▁fairer 4.5498957823829931e-06 -▁portable 4.5498957823829931e-06 -▁princip 4.5498957823829931e-06 -▁Übersch 4.5498957823829931e-06 --Form 4.54610735958334e-06 --Man 4.54610735958334e-06 -verantwortung 4.54610735958334e-06 -▁Ansehen 4.54610735958334e-06 -▁Hochschule 4.54610735958334e-06 -▁Laundry 4.54610735958334e-06 -▁Ministerpräsidenten 4.54610735958334e-06 -▁aufgeben 4.54610735958334e-06 -▁collecting 4.54610735958334e-06 -▁corrupt 4.54610735958334e-06 -▁getroffenen 4.54610735958334e-06 -▁hinnehmen 4.54610735958334e-06 -▁marking 4.54610735958334e-06 -▁mines 4.54610735958334e-06 -▁mitzuteilen 4.54610735958334e-06 -▁mos 4.54610735958334e-06 -▁unannehm 4.54610735958334e-06 --X 4.5423189367836874e-06 -CAD 4.5423189367836874e-06 -fast 4.5423189367836874e-06 -ilog 4.5423189367836874e-06 -▁Abfahrt 4.5423189367836874e-06 -▁Architekt 4.5423189367836874e-06 -▁Buy 4.5423189367836874e-06 -▁Unregelmäßigkeiten 4.5423189367836874e-06 -▁maler 4.5423189367836874e-06 -▁mortg 4.5423189367836874e-06 -▁unternimmt 4.5423189367836874e-06 -▁verursachten 4.5423189367836874e-06 --Dienst 4.5385305139840345e-06 -82 4.5385305139840345e-06 -komponenten 4.5385305139840345e-06 -zept 4.5385305139840345e-06 -▁Bemühen 4.5385305139840345e-06 -▁Egyptian 4.5385305139840345e-06 -▁Erasmus 4.5385305139840345e-06 -▁Ferrari 4.5385305139840345e-06 -▁Gardens 4.5385305139840345e-06 -▁Gehör 4.5385305139840345e-06 -▁Momente 4.5385305139840345e-06 -▁Scottish 4.5385305139840345e-06 -▁gt 4.5385305139840345e-06 -▁unauf 4.5385305139840345e-06 -▁westliche 4.5385305139840345e-06 --Sicherheits 4.5347420911843816e-06 --Unter 4.5347420911843816e-06 -hänge 4.5347420911843816e-06 -olis 4.5347420911843816e-06 -▁Alexand 4.5347420911843816e-06 -▁Cambod 4.5347420911843816e-06 -▁Kolumbien 4.5347420911843816e-06 -▁Sic 4.5347420911843816e-06 -▁Wi 4.5347420911843816e-06 -▁detected 4.5347420911843816e-06 -▁eliminating 4.5347420911843816e-06 -▁emotions 4.5347420911843816e-06 -▁feasible 4.5347420911843816e-06 -cup 4.5309536683847287e-06 -oyer 4.5309536683847287e-06 -uchtet 4.5309536683847287e-06 -ufer 4.5309536683847287e-06 -▁2.1 4.5309536683847287e-06 -▁7th 4.5309536683847287e-06 -▁BV 4.5309536683847287e-06 -▁C4-0 4.5309536683847287e-06 -▁Instabilität 4.5309536683847287e-06 -▁Mind 4.5309536683847287e-06 -▁OSCE 4.5309536683847287e-06 -▁OSZE 4.5309536683847287e-06 -▁Stufen 4.5309536683847287e-06 -▁accessibility 4.5309536683847287e-06 -▁accompany 4.5309536683847287e-06 -▁buffer 4.5309536683847287e-06 -▁durchschnittlich 4.5309536683847287e-06 -▁educ 4.5309536683847287e-06 -▁foodstuffs 4.5309536683847287e-06 -▁veranlassen 4.5309536683847287e-06 -▁weakness 4.5309536683847287e-06 --end 4.5271652455850766e-06 -esta 4.5271652455850766e-06 -haushalts 4.5271652455850766e-06 -orously 4.5271652455850766e-06 -▁Chinesen 4.5271652455850766e-06 -▁Darlehen 4.5271652455850766e-06 -▁Diagnose 4.5271652455850766e-06 -▁Freilassung 4.5271652455850766e-06 -▁Zusagen 4.5271652455850766e-06 -▁alien 4.5271652455850766e-06 -▁fulfilling 4.5271652455850766e-06 -▁gewahrt 4.5271652455850766e-06 -▁liberty 4.5271652455850766e-06 -▁self-s 4.5271652455850766e-06 -▁uneingeschränkte 4.5271652455850766e-06 -▁verbindliche 4.5271652455850766e-06 -atical 4.5233768227854238e-06 -genehm 4.5233768227854238e-06 -php 4.5233768227854238e-06 -änglichen 4.5233768227854238e-06 -ärkte 4.5233768227854238e-06 -▁Geltungsbereich 4.5233768227854238e-06 -▁Mold 4.5233768227854238e-06 -▁Schwanger 4.5233768227854238e-06 -▁Vö 4.5233768227854238e-06 -▁einfaches 4.5233768227854238e-06 -▁hochrang 4.5233768227854238e-06 -▁informations 4.5233768227854238e-06 -pfung 4.5195883999857709e-06 -vez 4.5195883999857709e-06 -zutreten 4.5195883999857709e-06 -▁Gemein 4.5195883999857709e-06 -▁Industrien 4.5195883999857709e-06 -▁Init 4.5195883999857709e-06 -▁Nit 4.5195883999857709e-06 -▁Nächte 4.5195883999857709e-06 -▁administrat 4.5195883999857709e-06 -▁anscheinend 4.5195883999857709e-06 -▁obgleich 4.5195883999857709e-06 -▁overseas 4.5195883999857709e-06 -▁weaknesses 4.5195883999857709e-06 -▁wholly 4.5195883999857709e-06 -▁wing 4.5195883999857709e-06 -FL 4.515799977186118e-06 -FORM 4.515799977186118e-06 -chant 4.515799977186118e-06 -managements 4.515799977186118e-06 -organis 4.515799977186118e-06 -▁Daneben 4.515799977186118e-06 -▁Erdgeschoss 4.515799977186118e-06 -▁Gemeinschaftsebene 4.515799977186118e-06 -▁Luftverkehr 4.515799977186118e-06 -▁Niel 4.515799977186118e-06 -▁angekommen 4.515799977186118e-06 -▁banana 4.515799977186118e-06 -▁biologische 4.515799977186118e-06 -▁salon 4.515799977186118e-06 -▁selekt 4.515799977186118e-06 -ao 4.5120115543864651e-06 -iw 4.5120115543864651e-06 -izite 4.5120115543864651e-06 -ktiken 4.5120115543864651e-06 -pielen 4.5120115543864651e-06 -ziell 4.5120115543864651e-06 -▁1956 4.5120115543864651e-06 -▁Born 4.5120115543864651e-06 -▁abgedeckt 4.5120115543864651e-06 -▁gehör 4.5120115543864651e-06 -▁gänzlich 4.5120115543864651e-06 -▁hochqual 4.5120115543864651e-06 -L. 4.5082231315868122e-06 -ahm 4.5082231315868122e-06 -arme 4.5082231315868122e-06 -ofin 4.5082231315868122e-06 -▁AVI 4.5082231315868122e-06 -▁EU-Bürger 4.5082231315868122e-06 -▁Lebensbedingungen 4.5082231315868122e-06 -▁Slow 4.5082231315868122e-06 -▁Transl 4.5082231315868122e-06 -▁Warnung 4.5082231315868122e-06 -▁celebration 4.5082231315868122e-06 -▁holl 4.5082231315868122e-06 -▁persuade 4.5082231315868122e-06 -▁reminded 4.5082231315868122e-06 -▁repos 4.5082231315868122e-06 -ANN 4.5044347087871593e-06 -earl 4.5044347087871593e-06 -pielt 4.5044347087871593e-06 -rängt 4.5044347087871593e-06 -steigerung 4.5044347087871593e-06 -▁11.00 4.5044347087871593e-06 -▁Anth 4.5044347087871593e-06 -▁Korn 4.5044347087871593e-06 -▁Lettland 4.5044347087871593e-06 -▁Mari 4.5044347087871593e-06 -▁Questions 4.5044347087871593e-06 -▁Sor 4.5044347087871593e-06 -▁aufrecht 4.5044347087871593e-06 -▁coating 4.5044347087871593e-06 -▁concessions 4.5044347087871593e-06 -▁cure 4.5044347087871593e-06 -▁farb 4.5044347087871593e-06 -▁possession 4.5044347087871593e-06 -▁potenziellen 4.5044347087871593e-06 -▁verwe 4.5044347087871593e-06 -▁verwendete 4.5044347087871593e-06 -Office 4.5006462859875064e-06 -`s 4.5006462859875064e-06 -eint 4.5006462859875064e-06 -embl 4.5006462859875064e-06 -frem 4.5006462859875064e-06 -funk 4.5006462859875064e-06 -ilar 4.5006462859875064e-06 -uing 4.5006462859875064e-06 -▁-F 4.5006462859875064e-06 -▁3.1 4.5006462859875064e-06 -▁BAS 4.5006462859875064e-06 -▁Lack 4.5006462859875064e-06 -▁Stars 4.5006462859875064e-06 -▁Teils 4.5006462859875064e-06 -▁Vene 4.5006462859875064e-06 -▁accomplished 4.5006462859875064e-06 -▁fließen 4.5006462859875064e-06 -▁self-d 4.5006462859875064e-06 -▁socio-economic 4.5006462859875064e-06 -▁teleph 4.5006462859875064e-06 -▁vorgetragen 4.5006462859875064e-06 -WA 4.4968578631878544e-06 -bels 4.4968578631878544e-06 -eckte 4.4968578631878544e-06 -ehle 4.4968578631878544e-06 -gl. 4.4968578631878544e-06 -ihen 4.4968578631878544e-06 -sucht 4.4968578631878544e-06 -würfe 4.4968578631878544e-06 -▁Biokraft 4.4968578631878544e-06 -▁Brücken 4.4968578631878544e-06 -▁Empfangs 4.4968578631878544e-06 -▁Intens 4.4968578631878544e-06 -▁cosm 4.4968578631878544e-06 -▁geste 4.4968578631878544e-06 -▁irregularities 4.4968578631878544e-06 -▁networking 4.4968578631878544e-06 -▁optimized 4.4968578631878544e-06 -▁shelter 4.4968578631878544e-06 -▁unverändert 4.4968578631878544e-06 -bewusstsein 4.4930694403882015e-06 -care 4.4930694403882015e-06 -klärungen 4.4930694403882015e-06 -obi 4.4930694403882015e-06 -▁Atem 4.4930694403882015e-06 -▁Christus 4.4930694403882015e-06 -▁Corbett 4.4930694403882015e-06 -▁Depression 4.4930694403882015e-06 -▁Fuj 4.4930694403882015e-06 -▁Pisten 4.4930694403882015e-06 -▁Recycling 4.4930694403882015e-06 -▁Side 4.4930694403882015e-06 -▁Viertens 4.4930694403882015e-06 -▁aggreg 4.4930694403882015e-06 -▁appelliere 4.4930694403882015e-06 -▁enden 4.4930694403882015e-06 -▁family-run 4.4930694403882015e-06 -▁nein 4.4930694403882015e-06 -▁singer 4.4930694403882015e-06 -▁varieties 4.4930694403882015e-06 --Pak 4.4892810175885486e-06 -IFT 4.4892810175885486e-06 -eaux 4.4892810175885486e-06 -▁Frühling 4.4892810175885486e-06 -▁Liberals 4.4892810175885486e-06 -▁Motorrad 4.4892810175885486e-06 -▁Student 4.4892810175885486e-06 -▁align 4.4892810175885486e-06 -▁fatal 4.4892810175885486e-06 -▁geehrter 4.4892810175885486e-06 -▁jour 4.4892810175885486e-06 -▁mol 4.4892810175885486e-06 -▁outline 4.4892810175885486e-06 -setzungen 4.4854925947888957e-06 -▁Ausflug 4.4854925947888957e-06 -▁Beschäftigungspolitik 4.4854925947888957e-06 -▁ample 4.4854925947888957e-06 -▁anschließ 4.4854925947888957e-06 -▁bilaterale 4.4854925947888957e-06 -▁contradiction 4.4854925947888957e-06 -▁multip 4.4854925947888957e-06 -▁stärkt 4.4854925947888957e-06 -▁writers 4.4854925947888957e-06 -Cs 4.4817041719892428e-06 -amper 4.4817041719892428e-06 -edged 4.4817041719892428e-06 -essing 4.4817041719892428e-06 -mehr 4.4817041719892428e-06 -parteien 4.4817041719892428e-06 -versch 4.4817041719892428e-06 -▁Aven 4.4817041719892428e-06 -▁Fax- 4.4817041719892428e-06 -▁Salvador 4.4817041719892428e-06 -▁Sha 4.4817041719892428e-06 -▁archives 4.4817041719892428e-06 -▁herkömmlichen 4.4817041719892428e-06 -▁influences 4.4817041719892428e-06 -▁multitude 4.4817041719892428e-06 -▁tariff 4.4817041719892428e-06 -▁territorialen 4.4817041719892428e-06 -▁unp 4.4817041719892428e-06 -▁vel 4.4817041719892428e-06 -▁verknüp 4.4817041719892428e-06 -▁É 4.4817041719892428e-06 -▁überarbeitet 4.4817041719892428e-06 --Programme 4.47791574918959e-06 -atorischen 4.47791574918959e-06 -gli 4.47791574918959e-06 -itement 4.47791574918959e-06 -tehen 4.47791574918959e-06 -teous 4.47791574918959e-06 -upport 4.47791574918959e-06 -▁Dalai 4.47791574918959e-06 -▁Erweiterungen 4.47791574918959e-06 -▁Klimaschutz 4.47791574918959e-06 -▁Seas 4.47791574918959e-06 -▁Tang 4.47791574918959e-06 -▁Terrassen 4.47791574918959e-06 -▁Veranstaltungsräume 4.47791574918959e-06 -▁Verwenden 4.47791574918959e-06 -▁experimental 4.47791574918959e-06 -▁gründ 4.47791574918959e-06 -▁incredibly 4.47791574918959e-06 -▁positiver 4.47791574918959e-06 -▁pragm 4.47791574918959e-06 -▁pressures 4.47791574918959e-06 -▁speculation 4.47791574918959e-06 -▁sé 4.47791574918959e-06 -▁³ 4.47791574918959e-06 -▁überdenken 4.47791574918959e-06 -bag 4.474127326389937e-06 -bound 4.474127326389937e-06 -eware 4.474127326389937e-06 -rik 4.474127326389937e-06 -ل 4.474127326389937e-06 -▁Aufstockung 4.474127326389937e-06 -▁Differ 4.474127326389937e-06 -▁Doktor 4.474127326389937e-06 -▁Ecofin 4.474127326389937e-06 -▁Hann 4.474127326389937e-06 -▁Pocket 4.474127326389937e-06 -▁Versicherung 4.474127326389937e-06 -▁draws 4.474127326389937e-06 -▁halls 4.474127326389937e-06 -kir 4.4703389035902841e-06 -stad 4.4703389035902841e-06 -swert 4.4703389035902841e-06 -yse 4.4703389035902841e-06 -▁Beschränkung 4.4703389035902841e-06 -▁Bez 4.4703389035902841e-06 -▁Genfer 4.4703389035902841e-06 -▁Konfigurations 4.4703389035902841e-06 -▁aan 4.4703389035902841e-06 -▁broadband 4.4703389035902841e-06 -▁gedenkt 4.4703389035902841e-06 -▁pf 4.4703389035902841e-06 -▁replacing 4.4703389035902841e-06 -300 4.4665504807906321e-06 -alender 4.4665504807906321e-06 -ifically 4.4665504807906321e-06 -opro 4.4665504807906321e-06 -ulative 4.4665504807906321e-06 -▁GMOs 4.4665504807906321e-06 -▁Geburtstag 4.4665504807906321e-06 -▁Schauen 4.4665504807906321e-06 -▁Wallström 4.4665504807906321e-06 -▁besuchte 4.4665504807906321e-06 -▁concentr 4.4665504807906321e-06 -▁inequalities 4.4665504807906321e-06 -▁preparatory 4.4665504807906321e-06 -▁privileg 4.4665504807906321e-06 -▁rass 4.4665504807906321e-06 -▁thou 4.4665504807906321e-06 -ronen 4.4627620579909792e-06 -ync 4.4627620579909792e-06 -▁Befreiung 4.4627620579909792e-06 -▁Gläub 4.4627620579909792e-06 -▁Gothic 4.4627620579909792e-06 -▁dealer 4.4627620579909792e-06 -▁diesel 4.4627620579909792e-06 -▁donation 4.4627620579909792e-06 -▁generating 4.4627620579909792e-06 -▁kultureller 4.4627620579909792e-06 -▁promenade 4.4627620579909792e-06 -▁protectionism 4.4627620579909792e-06 -▁ranks 4.4627620579909792e-06 -▁reign 4.4627620579909792e-06 -▁reveals 4.4627620579909792e-06 -▁surround 4.4627620579909792e-06 -▁ter 4.4627620579909792e-06 -immungen 4.4589736351913263e-06 -lam 4.4589736351913263e-06 -rupt 4.4589736351913263e-06 -ässe 4.4589736351913263e-06 -ção 4.4589736351913263e-06 -В 4.4589736351913263e-06 -▁Bestandteile 4.4589736351913263e-06 -▁Electronic 4.4589736351913263e-06 -▁Liebhaber 4.4589736351913263e-06 -▁Tunnel 4.4589736351913263e-06 -▁Wes 4.4589736351913263e-06 -▁ausgere 4.4589736351913263e-06 -▁behinderten 4.4589736351913263e-06 -▁debts 4.4589736351913263e-06 -▁diagnosis 4.4589736351913263e-06 -▁galt 4.4589736351913263e-06 -▁hinausgehen 4.4589736351913263e-06 -▁israelische 4.4589736351913263e-06 -▁neighbour 4.4589736351913263e-06 -▁stamp 4.4589736351913263e-06 -gemacht 4.4551852123916734e-06 -ologist 4.4551852123916734e-06 -ospace 4.4551852123916734e-06 -ören 4.4551852123916734e-06 -▁1200 4.4551852123916734e-06 -▁Budd 4.4551852123916734e-06 -▁Electric 4.4551852123916734e-06 -▁Lip 4.4551852123916734e-06 -▁arena 4.4551852123916734e-06 -▁confer 4.4551852123916734e-06 -▁destr 4.4551852123916734e-06 -▁facilitating 4.4551852123916734e-06 -▁shoot 4.4551852123916734e-06 -▁turnover 4.4551852123916734e-06 --Sterne 4.4513967895920205e-06 -arma 4.4513967895920205e-06 -noten 4.4513967895920205e-06 -pling 4.4513967895920205e-06 -prot 4.4513967895920205e-06 -tage 4.4513967895920205e-06 -wachstum 4.4513967895920205e-06 -▁COS 4.4513967895920205e-06 -▁Cin 4.4513967895920205e-06 -▁Gemäl 4.4513967895920205e-06 -▁Kne 4.4513967895920205e-06 -▁Mitgliedsländer 4.4513967895920205e-06 -▁Nass 4.4513967895920205e-06 -▁Sied 4.4513967895920205e-06 -▁Zufall 4.4513967895920205e-06 -▁barr 4.4513967895920205e-06 -▁intensiver 4.4513967895920205e-06 -▁löst 4.4513967895920205e-06 -▁obsc 4.4513967895920205e-06 -▁real-time 4.4513967895920205e-06 -▁trinken 4.4513967895920205e-06 -▁umfangreich 4.4513967895920205e-06 -▁variations 4.4513967895920205e-06 -.03 4.4476083667923676e-06 -aug 4.4476083667923676e-06 -bearbeitung 4.4476083667923676e-06 -dam 4.4476083667923676e-06 -▁Conil 4.4476083667923676e-06 -▁Fortunately 4.4476083667923676e-06 -▁Infineon 4.4476083667923676e-06 -▁Learning 4.4476083667923676e-06 -▁Lieferungen 4.4476083667923676e-06 -▁Radisson 4.4476083667923676e-06 -▁Streben 4.4476083667923676e-06 -▁Tasten 4.4476083667923676e-06 -▁Wartungs 4.4476083667923676e-06 -▁catal 4.4476083667923676e-06 -▁descriptions 4.4476083667923676e-06 -▁durchd 4.4476083667923676e-06 -▁grain 4.4476083667923676e-06 -▁immers 4.4476083667923676e-06 -▁premier 4.4476083667923676e-06 -▁schlimm 4.4476083667923676e-06 -▁sowjet 4.4476083667923676e-06 -▁upgraded 4.4476083667923676e-06 -▁verbesserten 4.4476083667923676e-06 --Seiten 4.4438199439927147e-06 -defizit 4.4438199439927147e-06 -gebnis 4.4438199439927147e-06 -prob 4.4438199439927147e-06 -verständnis 4.4438199439927147e-06 -õ 4.4438199439927147e-06 -üchter 4.4438199439927147e-06 -▁Castell 4.4438199439927147e-06 -▁Equipment 4.4438199439927147e-06 -▁Herzog 4.4438199439927147e-06 -▁Residen 4.4438199439927147e-06 -▁Wassersport 4.4438199439927147e-06 -▁Zagreb 4.4438199439927147e-06 -▁Zukunfts 4.4438199439927147e-06 -▁mangel 4.4438199439927147e-06 -▁non-c 4.4438199439927147e-06 -▁pläd 4.4438199439927147e-06 -▁segments 4.4438199439927147e-06 -▁überleben 4.4438199439927147e-06 --Paket 4.4400315211930618e-06 -hill 4.4400315211930618e-06 -onne 4.4400315211930618e-06 -rel 4.4400315211930618e-06 -spir 4.4400315211930618e-06 -▁EOS 4.4400315211930618e-06 -▁Gastgeber 4.4400315211930618e-06 -▁Hem 4.4400315211930618e-06 -▁OEM 4.4400315211930618e-06 -▁configured 4.4400315211930618e-06 -▁contrad 4.4400315211930618e-06 -▁gigant 4.4400315211930618e-06 -▁macroeconomic 4.4400315211930618e-06 -▁möge 4.4400315211930618e-06 -▁objections 4.4400315211930618e-06 -▁thy 4.4400315211930618e-06 -attform 4.43624309839341e-06 -bauer 4.43624309839341e-06 -itations 4.43624309839341e-06 -leihen 4.43624309839341e-06 -nischen 4.43624309839341e-06 -reitet 4.43624309839341e-06 -schlossen 4.43624309839341e-06 -ätt 4.43624309839341e-06 -überwachung 4.43624309839341e-06 -▁Beantwortung 4.43624309839341e-06 -▁Erlaubnis 4.43624309839341e-06 -▁Gefühle 4.43624309839341e-06 -▁Glob 4.43624309839341e-06 -▁Plätzen 4.43624309839341e-06 -▁Vac 4.43624309839341e-06 -▁chef 4.43624309839341e-06 -▁descend 4.43624309839341e-06 -▁earmark 4.43624309839341e-06 -▁exemption 4.43624309839341e-06 -▁wunderbar 4.43624309839341e-06 -▁zweites 4.43624309839341e-06 -Ex 4.4324546755937569e-06 -ichtigen 4.4324546755937569e-06 -▁Babys 4.4324546755937569e-06 -▁Misch 4.4324546755937569e-06 -▁Weber 4.4324546755937569e-06 -▁disadvantaged 4.4324546755937569e-06 -▁lifting 4.4324546755937569e-06 -▁rigid 4.4324546755937569e-06 -▁shortage 4.4324546755937569e-06 -▁unterrichtet 4.4324546755937569e-06 -▁updating 4.4324546755937569e-06 -▁wiederauf 4.4324546755937569e-06 -platte 4.428666252794104e-06 -rev 4.428666252794104e-06 -▁Aquar 4.428666252794104e-06 -▁EIN 4.428666252794104e-06 -▁Flora 4.428666252794104e-06 -▁Magnet 4.428666252794104e-06 -▁Records 4.428666252794104e-06 -▁Reding 4.428666252794104e-06 -▁Symbole 4.428666252794104e-06 -▁Wiederaufnahme 4.428666252794104e-06 -▁Zinssätze 4.428666252794104e-06 -▁Zusammenhalts 4.428666252794104e-06 -▁built-in 4.428666252794104e-06 -▁electron 4.428666252794104e-06 -▁furn 4.428666252794104e-06 -▁kenne 4.428666252794104e-06 -▁magnet 4.428666252794104e-06 -▁religi 4.428666252794104e-06 -▁transparente 4.428666252794104e-06 -▁verlaufen 4.428666252794104e-06 -▁vorwiegend 4.428666252794104e-06 -▁üb 4.428666252794104e-06 --se 4.4248778299944511e-06 -IZ 4.4248778299944511e-06 -führenden 4.4248778299944511e-06 -gängig 4.4248778299944511e-06 -ofar 4.4248778299944511e-06 -▁Barbar 4.4248778299944511e-06 -▁Festplatten 4.4248778299944511e-06 -▁Kontin 4.4248778299944511e-06 -▁Kurdish 4.4248778299944511e-06 -▁Sozialdemokratischen 4.4248778299944511e-06 -▁Verlag 4.4248778299944511e-06 -▁Vorjahr 4.4248778299944511e-06 -▁Zentralbanken 4.4248778299944511e-06 -▁applaud 4.4248778299944511e-06 -▁austerity 4.4248778299944511e-06 -▁neur 4.4248778299944511e-06 -▁verweigert 4.4248778299944511e-06 -entscheidungen 4.4210894071947982e-06 -idsch 4.4210894071947982e-06 -manager 4.4210894071947982e-06 -opes 4.4210894071947982e-06 -seitigkeit 4.4210894071947982e-06 -щ 4.4210894071947982e-06 -▁Arme 4.4210894071947982e-06 -▁Baumw 4.4210894071947982e-06 -▁CRM 4.4210894071947982e-06 -▁Cambridge 4.4210894071947982e-06 -▁Hop 4.4210894071947982e-06 -▁ICT 4.4210894071947982e-06 -▁Lizenzen 4.4210894071947982e-06 -▁ehrgeizige 4.4210894071947982e-06 -▁geführte 4.4210894071947982e-06 -▁mp 4.4210894071947982e-06 -▁nächstes 4.4210894071947982e-06 -▁poorer 4.4210894071947982e-06 -▁purchases 4.4210894071947982e-06 -▁unterzeichnen 4.4210894071947982e-06 -▁versichert 4.4210894071947982e-06 -▁Äußer 4.4210894071947982e-06 -eav 4.4173009843951453e-06 -gence 4.4173009843951453e-06 -läufe 4.4173009843951453e-06 -posed 4.4173009843951453e-06 -tral 4.4173009843951453e-06 -▁Delegationen 4.4173009843951453e-06 -▁Genehm 4.4173009843951453e-06 -▁Josef 4.4173009843951453e-06 -▁Sciences 4.4173009843951453e-06 -▁Zertifizierung 4.4173009843951453e-06 -▁Zoo 4.4173009843951453e-06 -▁anticipated 4.4173009843951453e-06 -▁beliebige 4.4173009843951453e-06 -▁gefäl 4.4173009843951453e-06 -▁indischen 4.4173009843951453e-06 -▁läng 4.4173009843951453e-06 -▁march 4.4173009843951453e-06 -▁pret 4.4173009843951453e-06 -▁quart 4.4173009843951453e-06 -▁specif 4.4173009843951453e-06 -▁tissue 4.4173009843951453e-06 -▁widen 4.4173009843951453e-06 -▁wiederherzustellen 4.4173009843951453e-06 -▁wissenschaftlich 4.4173009843951453e-06 -folgedessen 4.4135125615954924e-06 -kol 4.4135125615954924e-06 -leidung 4.4135125615954924e-06 -samer 4.4135125615954924e-06 -▁Bere 4.4135125615954924e-06 -▁Course 4.4135125615954924e-06 -▁EA 4.4135125615954924e-06 -▁Hund 4.4135125615954924e-06 -▁Konservativen 4.4135125615954924e-06 -▁aussieht 4.4135125615954924e-06 -▁bulk 4.4135125615954924e-06 -▁consultants 4.4135125615954924e-06 -▁speeds 4.4135125615954924e-06 -▁sü 4.4135125615954924e-06 -aschine 4.4097241387958395e-06 -olar 4.4097241387958395e-06 -orbenen 4.4097241387958395e-06 -vere 4.4097241387958395e-06 -▁1400 4.4097241387958395e-06 -▁1965 4.4097241387958395e-06 -▁Alicante 4.4097241387958395e-06 -▁Ausserdem 4.4097241387958395e-06 -▁Entwick 4.4097241387958395e-06 -▁Found 4.4097241387958395e-06 -▁GE 4.4097241387958395e-06 -▁Keramik 4.4097241387958395e-06 -▁Share 4.4097241387958395e-06 -▁Spaniens 4.4097241387958395e-06 -▁Wave 4.4097241387958395e-06 -▁Welle 4.4097241387958395e-06 -▁birgt 4.4097241387958395e-06 -▁dal 4.4097241387958395e-06 -▁deport 4.4097241387958395e-06 -▁einräumen 4.4097241387958395e-06 -▁nar 4.4097241387958395e-06 -▁obliegt 4.4097241387958395e-06 -▁veränderten 4.4097241387958395e-06 -gewichte 4.4059357159961875e-06 -ibes 4.4059357159961875e-06 -otechn 4.4059357159961875e-06 -voraus 4.4059357159961875e-06 -▁Affiliate 4.4059357159961875e-06 -▁Berechn 4.4059357159961875e-06 -▁Knowledge 4.4059357159961875e-06 -▁Neigh 4.4059357159961875e-06 -▁Pferd 4.4059357159961875e-06 -▁Platform 4.4059357159961875e-06 -▁Verstöße 4.4059357159961875e-06 -▁abzustimmen 4.4059357159961875e-06 -▁atr 4.4059357159961875e-06 -▁bestrebt 4.4059357159961875e-06 -▁cultivation 4.4059357159961875e-06 -▁geplante 4.4059357159961875e-06 -▁rechte 4.4059357159961875e-06 -▁springs 4.4059357159961875e-06 -▁umfangreiches 4.4059357159961875e-06 -▁überschritten 4.4059357159961875e-06 -esar 4.4021472931965346e-06 -mensch 4.4021472931965346e-06 -uries 4.4021472931965346e-06 -zepts 4.4021472931965346e-06 -▁Arafat 4.4021472931965346e-06 -▁BG 4.4021472931965346e-06 -▁Kriege 4.4021472931965346e-06 -▁Monument 4.4021472931965346e-06 -▁Schwächen 4.4021472931965346e-06 -▁Unrecht 4.4021472931965346e-06 -▁confort 4.4021472931965346e-06 -▁herauszufinden 4.4021472931965346e-06 -▁negligence 4.4021472931965346e-06 -▁retreat 4.4021472931965346e-06 -▁unerwün 4.4021472931965346e-06 -agua 4.3983588703968817e-06 -betrieben 4.3983588703968817e-06 -lant 4.3983588703968817e-06 -zufriedenheit 4.3983588703968817e-06 -▁Almost 4.3983588703968817e-06 -▁Dör 4.3983588703968817e-06 -▁Estate 4.3983588703968817e-06 -▁Fische 4.3983588703968817e-06 -▁Kohäsionsfonds 4.3983588703968817e-06 -▁Ohr 4.3983588703968817e-06 -▁Sieger 4.3983588703968817e-06 -▁Trock 4.3983588703968817e-06 -▁dachte 4.3983588703968817e-06 -▁ganzes 4.3983588703968817e-06 -▁outer 4.3983588703968817e-06 -▁preview 4.3983588703968817e-06 -▁sorg 4.3983588703968817e-06 -▁не 4.3983588703968817e-06 --Tra 4.3945704475972288e-06 -46 4.3945704475972288e-06 -istet 4.3945704475972288e-06 -nez 4.3945704475972288e-06 -uras 4.3945704475972288e-06 -öß 4.3945704475972288e-06 -▁Flair 4.3945704475972288e-06 -▁Innenhof 4.3945704475972288e-06 -▁behilflich 4.3945704475972288e-06 -▁hospitals 4.3945704475972288e-06 -▁tutorial 4.3945704475972288e-06 -▁verschwunden 4.3945704475972288e-06 -▁üben 4.3945704475972288e-06 -▁Autovermietung 4.3907820247975759e-06 -▁Bestands 4.3907820247975759e-06 -▁Kanäle 4.3907820247975759e-06 -▁attracted 4.3907820247975759e-06 -▁delight 4.3907820247975759e-06 -▁monastery 4.3907820247975759e-06 -▁rhetoric 4.3907820247975759e-06 -▁truck 4.3907820247975759e-06 -▁verfasst 4.3907820247975759e-06 -▁älteste 4.3907820247975759e-06 -derte 4.386993601997923e-06 -elekt 4.386993601997923e-06 -ersey 4.386993601997923e-06 -iac 4.386993601997923e-06 -ordn 4.386993601997923e-06 -romagnet 4.386993601997923e-06 -▁Lev 4.386993601997923e-06 -▁Röm 4.386993601997923e-06 -▁anhalten 4.386993601997923e-06 -▁befindlichen 4.386993601997923e-06 -▁corresponds 4.386993601997923e-06 -▁erledigen 4.386993601997923e-06 -▁geliefer 4.386993601997923e-06 -▁instruction 4.386993601997923e-06 -▁medi 4.386993601997923e-06 -▁nachkommen 4.386993601997923e-06 -▁outbrea 4.386993601997923e-06 -▁stoßen 4.386993601997923e-06 -▁tackled 4.386993601997923e-06 -▁töd 4.386993601997923e-06 -.01 4.38320517919827e-06 -ISS 4.38320517919827e-06 -meter 4.38320517919827e-06 -onal 4.38320517919827e-06 -städte 4.38320517919827e-06 -ucks 4.38320517919827e-06 -zufordern 4.38320517919827e-06 -▁Acrobat 4.38320517919827e-06 -▁Applikationen 4.38320517919827e-06 -▁Ausschluss 4.38320517919827e-06 -▁Bajor 4.38320517919827e-06 -▁CNC 4.38320517919827e-06 -▁Intensiv 4.38320517919827e-06 -▁Liikanen 4.38320517919827e-06 -▁Reinigungs 4.38320517919827e-06 -▁Sekret 4.38320517919827e-06 -▁VP 4.38320517919827e-06 -▁accus 4.38320517919827e-06 -▁attain 4.38320517919827e-06 -▁chip 4.38320517919827e-06 -▁cos 4.38320517919827e-06 -▁indul 4.38320517919827e-06 -▁injection 4.38320517919827e-06 -▁og 4.38320517919827e-06 -▁redund 4.38320517919827e-06 -▁staatlicher 4.38320517919827e-06 -▁zeitlich 4.38320517919827e-06 -marken 4.3794167563986181e-06 -reb 4.3794167563986181e-06 -regional 4.3794167563986181e-06 -▁Befürchtungen 4.3794167563986181e-06 -▁Cadiz 4.3794167563986181e-06 -▁Gleiche 4.3794167563986181e-06 -▁Rekord 4.3794167563986181e-06 -▁Rumän 4.3794167563986181e-06 -▁Wähl 4.3794167563986181e-06 -▁apt 4.3794167563986181e-06 -▁durchschnittliche 4.3794167563986181e-06 -▁fusion 4.3794167563986181e-06 -▁laptop 4.3794167563986181e-06 -▁recipient 4.3794167563986181e-06 -▁teilnehmenden 4.3794167563986181e-06 -▁temple 4.3794167563986181e-06 -▁wahrhaft 4.3794167563986181e-06 --Entwick 4.3756283335989652e-06 -ICK 4.3756283335989652e-06 -UD 4.3756283335989652e-06 -initiativen 4.3756283335989652e-06 -irds 4.3756283335989652e-06 -ritz 4.3756283335989652e-06 -uevo 4.3756283335989652e-06 -ändigkeit 4.3756283335989652e-06 -▁Jarzembowski 4.3756283335989652e-06 -▁Tierschutz 4.3756283335989652e-06 -▁elimination 4.3756283335989652e-06 -▁geschicht 4.3756283335989652e-06 -▁relevance 4.3756283335989652e-06 -▁schrecklichen 4.3756283335989652e-06 -▁unend 4.3756283335989652e-06 -akult 4.3718399107993123e-06 -fish 4.3718399107993123e-06 -iminal 4.3718399107993123e-06 -vents 4.3718399107993123e-06 -ф 4.3718399107993123e-06 -▁Advis 4.3718399107993123e-06 -▁Ankara 4.3718399107993123e-06 -▁Archer 4.3718399107993123e-06 -▁Auszeichnung 4.3718399107993123e-06 -▁Badewanne 4.3718399107993123e-06 -▁Maybe 4.3718399107993123e-06 -▁Nahrung 4.3718399107993123e-06 -▁Patr 4.3718399107993123e-06 -▁Patri 4.3718399107993123e-06 -▁Zeugen 4.3718399107993123e-06 -▁delightful 4.3718399107993123e-06 -▁gewaltigen 4.3718399107993123e-06 -▁prest 4.3718399107993123e-06 -▁productions 4.3718399107993123e-06 --Komp 4.3680514879996594e-06 --Player 4.3680514879996594e-06 -HO 4.3680514879996594e-06 -ylon 4.3680514879996594e-06 -zwecke 4.3680514879996594e-06 -▁Bedauern 4.3680514879996594e-06 -▁Carmen 4.3680514879996594e-06 -▁Eingangs 4.3680514879996594e-06 -▁Flucht 4.3680514879996594e-06 -▁Fox 4.3680514879996594e-06 -▁GNOME 4.3680514879996594e-06 -▁Mitentscheidung 4.3680514879996594e-06 -▁Motion 4.3680514879996594e-06 -▁Reisende 4.3680514879996594e-06 -▁Schriftliche 4.3680514879996594e-06 -▁Tay 4.3680514879996594e-06 -▁Tul 4.3680514879996594e-06 -▁Ve 4.3680514879996594e-06 -▁Wohlergehen 4.3680514879996594e-06 -▁formulieren 4.3680514879996594e-06 -▁halbe 4.3680514879996594e-06 -▁häufigsten 4.3680514879996594e-06 -▁soviel 4.3680514879996594e-06 -▁vegetation 4.3680514879996594e-06 -roh 4.3642630652000065e-06 -uhe 4.3642630652000065e-06 -unami 4.3642630652000065e-06 -▁1957 4.3642630652000065e-06 -▁IMM 4.3642630652000065e-06 -▁Marie 4.3642630652000065e-06 -▁Ott 4.3642630652000065e-06 -▁Panoram 4.3642630652000065e-06 -▁Rundfunk 4.3642630652000065e-06 -▁altogether 4.3642630652000065e-06 -▁personalized 4.3642630652000065e-06 -▁threaten 4.3642630652000065e-06 -▁virtuelle 4.3642630652000065e-06 --quality 4.3604746424003536e-06 -hew 4.3604746424003536e-06 -sbereich 4.3604746424003536e-06 -λ 4.3604746424003536e-06 -▁Bürgerkrieg 4.3604746424003536e-06 -▁ERP 4.3604746424003536e-06 -▁Füßen 4.3604746424003536e-06 -▁Iber 4.3604746424003536e-06 -▁Immobilienanze 4.3604746424003536e-06 -▁factories 4.3604746424003536e-06 -▁gründet 4.3604746424003536e-06 -▁handelte 4.3604746424003536e-06 -▁schweigen 4.3604746424003536e-06 -beob 4.3566862196007008e-06 -dings 4.3566862196007008e-06 -errung 4.3566862196007008e-06 -fabrik 4.3566862196007008e-06 -fly 4.3566862196007008e-06 -ità 4.3566862196007008e-06 -▁Entschuld 4.3566862196007008e-06 -▁Frankfurter 4.3566862196007008e-06 -▁Geistes 4.3566862196007008e-06 -▁Glückwunsch 4.3566862196007008e-06 -▁Kodex 4.3566862196007008e-06 -▁São 4.3566862196007008e-06 -▁aufgeteilt 4.3566862196007008e-06 -▁conservative 4.3566862196007008e-06 -▁folgendes 4.3566862196007008e-06 -▁ihrerseits 4.3566862196007008e-06 -▁imper 4.3566862196007008e-06 -▁interoper 4.3566862196007008e-06 -▁mig 4.3566862196007008e-06 -▁qualification 4.3566862196007008e-06 -▁sprachen 4.3566862196007008e-06 -▁transeuropäischen 4.3566862196007008e-06 -▁weise 4.3566862196007008e-06 -edict 4.3528977968010479e-06 -gets 4.3528977968010479e-06 -ickets 4.3528977968010479e-06 -ispiel 4.3528977968010479e-06 -presso 4.3528977968010479e-06 -vergabe 4.3528977968010479e-06 -ünen 4.3528977968010479e-06 -▁Biotechnologie 4.3528977968010479e-06 -▁Durchbruch 4.3528977968010479e-06 -▁Protektionismus 4.3528977968010479e-06 -▁Sag 4.3528977968010479e-06 -▁Willens 4.3528977968010479e-06 -▁complied 4.3528977968010479e-06 -▁schle 4.3528977968010479e-06 -kab 4.3491093740013958e-06 -män 4.3491093740013958e-06 -▁Dig 4.3491093740013958e-06 -▁Fifth 4.3491093740013958e-06 -▁Gehirn 4.3491093740013958e-06 -▁IRC 4.3491093740013958e-06 -▁Sympt 4.3491093740013958e-06 -▁daraufhin 4.3491093740013958e-06 -▁deterior 4.3491093740013958e-06 -▁föder 4.3491093740013958e-06 -▁gefährlicher 4.3491093740013958e-06 -▁journalist 4.3491093740013958e-06 -akische 4.3453209512017429e-06 -derten 4.3453209512017429e-06 -rechtliche 4.3453209512017429e-06 -▁Dienstleistungsrichtlinie 4.3453209512017429e-06 -▁Mitgliedsstaaten 4.3453209512017429e-06 -▁Occ 4.3453209512017429e-06 -▁Weihnachten 4.3453209512017429e-06 -▁Wäldern 4.3453209512017429e-06 -▁atemberauben 4.3453209512017429e-06 -▁cave 4.3453209512017429e-06 -▁drawings 4.3453209512017429e-06 -▁problematisch 4.3453209512017429e-06 -▁strongest 4.3453209512017429e-06 -▁teeth 4.3453209512017429e-06 -▁Ökos 4.3453209512017429e-06 --Up 4.34153252840209e-06 --one 4.34153252840209e-06 --way 4.34153252840209e-06 -abler 4.34153252840209e-06 -añ 4.34153252840209e-06 -▁Ble 4.34153252840209e-06 -▁Bulgar 4.34153252840209e-06 -▁Damals 4.34153252840209e-06 -▁Innov 4.34153252840209e-06 -▁Lomé 4.34153252840209e-06 -▁Prüfungen 4.34153252840209e-06 -▁Vormittag 4.34153252840209e-06 -▁besorgniserre 4.34153252840209e-06 -▁compress 4.34153252840209e-06 -▁ruft 4.34153252840209e-06 -▁signatures 4.34153252840209e-06 -▁AKP-Staaten 4.3377441056024371e-06 -▁Brat 4.3377441056024371e-06 -▁Cass 4.3377441056024371e-06 -▁Imper 4.3377441056024371e-06 -▁Sell 4.3377441056024371e-06 -▁Spitz 4.3377441056024371e-06 -▁TACIS 4.3377441056024371e-06 -▁angehört 4.3377441056024371e-06 -▁ausgebild 4.3377441056024371e-06 -▁ausschließ 4.3377441056024371e-06 -▁barely 4.3377441056024371e-06 -▁eliminated 4.3377441056024371e-06 -▁erstre 4.3377441056024371e-06 -▁exercises 4.3377441056024371e-06 -▁flora 4.3377441056024371e-06 -▁imply 4.3377441056024371e-06 -▁incurred 4.3377441056024371e-06 -▁socialist 4.3377441056024371e-06 -▁tanks 4.3377441056024371e-06 -▁tschech 4.3377441056024371e-06 -▁ukra 4.3377441056024371e-06 -43 4.3339556828027843e-06 -icos 4.3339556828027843e-06 -ohnt 4.3339556828027843e-06 -schiffe 4.3339556828027843e-06 -▁Cany 4.3339556828027843e-06 -▁Erfahrungs 4.3339556828027843e-06 -▁Gewässern 4.3339556828027843e-06 -▁Kohl 4.3339556828027843e-06 -▁Konserv 4.3339556828027843e-06 -▁Martini 4.3339556828027843e-06 -▁Rhyth 4.3339556828027843e-06 -▁discon 4.3339556828027843e-06 -▁diskut 4.3339556828027843e-06 -▁erzeug 4.3339556828027843e-06 -▁gemeldet 4.3339556828027843e-06 -▁professionell 4.3339556828027843e-06 -▁renovation 4.3339556828027843e-06 -▁resident 4.3339556828027843e-06 -assy 4.3301672600031314e-06 -erfolg 4.3301672600031314e-06 -klick 4.3301672600031314e-06 -orgungs 4.3301672600031314e-06 -stieg 4.3301672600031314e-06 -verg 4.3301672600031314e-06 -ändiger 4.3301672600031314e-06 -▁1966 4.3301672600031314e-06 -▁Athen 4.3301672600031314e-06 -▁Konsequenz 4.3301672600031314e-06 -▁Mant 4.3301672600031314e-06 -▁Parlamentarier 4.3301672600031314e-06 -▁Rapid 4.3301672600031314e-06 -▁Sachver 4.3301672600031314e-06 -▁bridges 4.3301672600031314e-06 -▁defect 4.3301672600031314e-06 -▁distances 4.3301672600031314e-06 -▁matching 4.3301672600031314e-06 -▁midst 4.3301672600031314e-06 --ray 4.3263788372034785e-06 -anträge 4.3263788372034785e-06 -onet 4.3263788372034785e-06 -▁Norweg 4.3263788372034785e-06 -▁Singapur 4.3263788372034785e-06 -▁Verurteilung 4.3263788372034785e-06 -▁animation 4.3263788372034785e-06 -▁bester 4.3263788372034785e-06 -▁bitt 4.3263788372034785e-06 -▁diameter 4.3263788372034785e-06 -▁fireplace 4.3263788372034785e-06 -▁forbidden 4.3263788372034785e-06 -▁gastr 4.3263788372034785e-06 -▁kombin 4.3263788372034785e-06 -▁mic 4.3263788372034785e-06 -▁repercussions 4.3263788372034785e-06 -▁verarbeiten 4.3263788372034785e-06 -icia 4.3225904144038256e-06 -▁Beteil 4.3225904144038256e-06 -▁Dubrovnik 4.3225904144038256e-06 -▁Einfl 4.3225904144038256e-06 -▁Gewässer 4.3225904144038256e-06 -▁Handlung 4.3225904144038256e-06 -▁Haushaltsausschusses 4.3225904144038256e-06 -▁Jav 4.3225904144038256e-06 -▁Konf 4.3225904144038256e-06 -▁Konzeption 4.3225904144038256e-06 -▁Lago 4.3225904144038256e-06 -▁Pensions 4.3225904144038256e-06 -▁Ryan 4.3225904144038256e-06 -▁Wertpapier 4.3225904144038256e-06 -▁cocktail 4.3225904144038256e-06 -▁ea 4.3225904144038256e-06 -▁zwingend 4.3225904144038256e-06 -azz 4.3188019916041735e-06 -issionen 4.3188019916041735e-06 -onst 4.3188019916041735e-06 -tauchen 4.3188019916041735e-06 -▁Auslieferung 4.3188019916041735e-06 -▁Botschaften 4.3188019916041735e-06 -▁Brows 4.3188019916041735e-06 -▁Instanz 4.3188019916041735e-06 -▁Sort 4.3188019916041735e-06 -▁True 4.3188019916041735e-06 -▁Variablen 4.3188019916041735e-06 -▁enchanting 4.3188019916041735e-06 -▁exot 4.3188019916041735e-06 -▁propaganda 4.3188019916041735e-06 -▁schn 4.3188019916041735e-06 -▁unangem 4.3188019916041735e-06 -▁unvergleich 4.3188019916041735e-06 -pflege 4.3150135688045206e-06 -sätzen 4.3150135688045206e-06 -▁Belastungen 4.3150135688045206e-06 -▁Bene 4.3150135688045206e-06 -▁Erzeugnissen 4.3150135688045206e-06 -▁Meerblick 4.3150135688045206e-06 -▁anmerken 4.3150135688045206e-06 -▁aufw 4.3150135688045206e-06 -▁befürchten 4.3150135688045206e-06 -▁frames 4.3150135688045206e-06 -▁historisch 4.3150135688045206e-06 -▁rehabilitation 4.3150135688045206e-06 -▁sister 4.3150135688045206e-06 -▁zusätzlicher 4.3150135688045206e-06 --Verhältnis 4.3112251460048678e-06 -KW 4.3112251460048678e-06 -bez 4.3112251460048678e-06 -bilden 4.3112251460048678e-06 -udes 4.3112251460048678e-06 -▁Champs 4.3112251460048678e-06 -▁Seil 4.3112251460048678e-06 -▁architects 4.3112251460048678e-06 -▁dauer 4.3112251460048678e-06 -▁documented 4.3112251460048678e-06 -▁graus 4.3112251460048678e-06 -▁hygiene 4.3112251460048678e-06 -▁studierte 4.3112251460048678e-06 -▁triple 4.3112251460048678e-06 --Shop 4.3074367232052149e-06 -debatte 4.3074367232052149e-06 -eno 4.3074367232052149e-06 -oter 4.3074367232052149e-06 -stationen 4.3074367232052149e-06 -▁Auseinandersetzungen 4.3074367232052149e-06 -▁Behandlungen 4.3074367232052149e-06 -▁Certainly 4.3074367232052149e-06 -▁Gor 4.3074367232052149e-06 -▁Shops 4.3074367232052149e-06 -▁boarding 4.3074367232052149e-06 -▁fürchte 4.3074367232052149e-06 -▁refresh 4.3074367232052149e-06 -▁screening 4.3074367232052149e-06 -▁wins 4.3074367232052149e-06 -ambots 4.303648300405562e-06 -ellow 4.303648300405562e-06 -ername 4.303648300405562e-06 -ieden 4.303648300405562e-06 -stage 4.303648300405562e-06 -züg 4.303648300405562e-06 -▁Around 4.303648300405562e-06 -▁Bout 4.303648300405562e-06 -▁Commons 4.303648300405562e-06 -▁Erleichterung 4.303648300405562e-06 -▁Jesolo 4.303648300405562e-06 -▁Privatisierung 4.303648300405562e-06 -▁Stärken 4.303648300405562e-06 -▁deliveries 4.303648300405562e-06 -▁disrupt 4.303648300405562e-06 -▁explo 4.303648300405562e-06 -▁komfort 4.303648300405562e-06 -▁notify 4.303648300405562e-06 -▁promptly 4.303648300405562e-06 -▁vertrauens 4.303648300405562e-06 -.d 4.2998598776059091e-06 -ikalen 4.2998598776059091e-06 -meisterschaft 4.2998598776059091e-06 -▁Annan 4.2998598776059091e-06 -▁Forschungs- 4.2998598776059091e-06 -▁Lieutenant 4.2998598776059091e-06 -▁abhängt 4.2998598776059091e-06 -▁complet 4.2998598776059091e-06 -▁crop 4.2998598776059091e-06 -▁deployed 4.2998598776059091e-06 -▁fav 4.2998598776059091e-06 -▁fro 4.2998598776059091e-06 -▁pap 4.2998598776059091e-06 -▁scrat 4.2998598776059091e-06 -▁skept 4.2998598776059091e-06 -▁steigende 4.2998598776059091e-06 -▁third-country 4.2998598776059091e-06 -▁unauthor 4.2998598776059091e-06 -▁überwältigen 4.2998598776059091e-06 -spieler 4.2960714548062562e-06 -▁Appar 4.2960714548062562e-06 -▁BBC 4.2960714548062562e-06 -▁Diamant 4.2960714548062562e-06 -▁Klärung 4.2960714548062562e-06 -▁Kürzungen 4.2960714548062562e-06 -▁Mittelalter 4.2960714548062562e-06 -▁Options 4.2960714548062562e-06 -▁Pfl 4.2960714548062562e-06 -▁Rin 4.2960714548062562e-06 -▁Stolz 4.2960714548062562e-06 -▁Tan 4.2960714548062562e-06 -▁Trading 4.2960714548062562e-06 -▁beeindruckenden 4.2960714548062562e-06 -▁bürgerliche 4.2960714548062562e-06 -▁fremden 4.2960714548062562e-06 -▁jeglichen 4.2960714548062562e-06 -▁republic 4.2960714548062562e-06 -▁successor 4.2960714548062562e-06 -alm 4.2922830320066033e-06 -inian 4.2922830320066033e-06 -laf 4.2922830320066033e-06 -undung 4.2922830320066033e-06 -▁Bestreben 4.2922830320066033e-06 -▁begannen 4.2922830320066033e-06 -▁billions 4.2922830320066033e-06 -▁crafts 4.2922830320066033e-06 -▁einzus 4.2922830320066033e-06 -▁guiding 4.2922830320066033e-06 -▁protein 4.2922830320066033e-06 -eader 4.2884946092069513e-06 -führende 4.2884946092069513e-06 -haut 4.2884946092069513e-06 -ischerweise 4.2884946092069513e-06 -quart 4.2884946092069513e-06 -seiten 4.2884946092069513e-06 -▁Flusses 4.2884946092069513e-06 -▁GUI 4.2884946092069513e-06 -▁Keep 4.2884946092069513e-06 -▁Rog 4.2884946092069513e-06 -▁Universitäts 4.2884946092069513e-06 -▁Verpack 4.2884946092069513e-06 -▁Walking 4.2884946092069513e-06 -▁deckt 4.2884946092069513e-06 -▁gaps 4.2884946092069513e-06 -▁households 4.2884946092069513e-06 -▁motto 4.2884946092069513e-06 -▁nachteil 4.2884946092069513e-06 -▁powered 4.2884946092069513e-06 -▁scal 4.2884946092069513e-06 -▁shores 4.2884946092069513e-06 -▁Öff 4.2884946092069513e-06 --Zimmer 4.2847061864072984e-06 -gor 4.2847061864072984e-06 -ingang 4.2847061864072984e-06 -ozi 4.2847061864072984e-06 -schaftliche 4.2847061864072984e-06 -▁Centr 4.2847061864072984e-06 -▁Meta 4.2847061864072984e-06 -▁dokument 4.2847061864072984e-06 -▁einzigartiges 4.2847061864072984e-06 -▁tout 4.2847061864072984e-06 -atial 4.2809177636076455e-06 -beauftrag 4.2809177636076455e-06 -institution 4.2809177636076455e-06 -istin 4.2809177636076455e-06 -istisches 4.2809177636076455e-06 -▁137 4.2809177636076455e-06 -▁Asset 4.2809177636076455e-06 -▁Ausdehnung 4.2809177636076455e-06 -▁Schulung 4.2809177636076455e-06 -▁Talent 4.2809177636076455e-06 -▁decides 4.2809177636076455e-06 -▁ehrgeizigen 4.2809177636076455e-06 -▁eingetreten 4.2809177636076455e-06 -▁enttäus 4.2809177636076455e-06 -▁font 4.2809177636076455e-06 -▁gemein 4.2809177636076455e-06 -▁hergest 4.2809177636076455e-06 -▁historischer 4.2809177636076455e-06 -▁jenseits 4.2809177636076455e-06 -▁konstru 4.2809177636076455e-06 -▁organizing 4.2809177636076455e-06 -▁pec 4.2809177636076455e-06 -▁recreational 4.2809177636076455e-06 -arth 4.2771293408079926e-06 -eated 4.2771293408079926e-06 -onomen 4.2771293408079926e-06 -undering 4.2771293408079926e-06 -äb 4.2771293408079926e-06 -▁Lieb 4.2771293408079926e-06 -▁Purch 4.2771293408079926e-06 -▁Screen 4.2771293408079926e-06 -▁Verbündeten 4.2771293408079926e-06 -▁blocking 4.2771293408079926e-06 -▁brown 4.2771293408079926e-06 -▁college 4.2771293408079926e-06 -▁japanische 4.2771293408079926e-06 -▁juristische 4.2771293408079926e-06 -▁linen 4.2771293408079926e-06 -▁momentum 4.2771293408079926e-06 -▁occasionally 4.2771293408079926e-06 -▁param 4.2771293408079926e-06 -▁röm 4.2771293408079926e-06 -▁spür 4.2771293408079926e-06 -egu 4.27334091800834e-06 -otor 4.27334091800834e-06 -uary 4.27334091800834e-06 -venient 4.27334091800834e-06 -wörter 4.27334091800834e-06 -▁2050 4.27334091800834e-06 -▁Ausgewogenheit 4.27334091800834e-06 -▁Gestern 4.27334091800834e-06 -▁Plen 4.27334091800834e-06 -▁Prämien 4.27334091800834e-06 -▁Schmuck 4.27334091800834e-06 -▁anspruchsvollen 4.27334091800834e-06 -▁empfehle 4.27334091800834e-06 -▁gourmet 4.27334091800834e-06 -▁itiner 4.27334091800834e-06 -▁komment 4.27334091800834e-06 -▁offenkundig 4.27334091800834e-06 -▁slide 4.27334091800834e-06 -▁umgewandelt 4.27334091800834e-06 -ampf 4.2695524952086868e-06 -ehme 4.2695524952086868e-06 -esischen 4.2695524952086868e-06 -folgt 4.2695524952086868e-06 -gehörigkeit 4.2695524952086868e-06 -neum 4.2695524952086868e-06 -ochten 4.2695524952086868e-06 -tätigkeit 4.2695524952086868e-06 -ögert 4.2695524952086868e-06 -▁GU 4.2695524952086868e-06 -▁Heinrich 4.2695524952086868e-06 -▁Margar 4.2695524952086868e-06 -▁confronted 4.2695524952086868e-06 -▁fabrics 4.2695524952086868e-06 -▁pragmatic 4.2695524952086868e-06 -▁ästhet 4.2695524952086868e-06 -▁üpp 4.2695524952086868e-06 -NET 4.2657640724090339e-06 -ecution 4.2657640724090339e-06 -opath 4.2657640724090339e-06 -ston 4.2657640724090339e-06 -ugar 4.2657640724090339e-06 -verzeichnis 4.2657640724090339e-06 -▁Buyer 4.2657640724090339e-06 -▁Evaluierung 4.2657640724090339e-06 -▁Haushaltskontrolle 4.2657640724090339e-06 -▁Netto 4.2657640724090339e-06 -▁Pestiz 4.2657640724090339e-06 -▁Richtungen 4.2657640724090339e-06 -▁einzuf 4.2657640724090339e-06 -▁heaven 4.2657640724090339e-06 -▁hub 4.2657640724090339e-06 -▁lush 4.2657640724090339e-06 -▁projekt 4.2657640724090339e-06 -▁versp 4.2657640724090339e-06 -ardin 4.261975649609381e-06 -cements 4.261975649609381e-06 -chtest 4.261975649609381e-06 -eschl 4.261975649609381e-06 -farben 4.261975649609381e-06 -ogh 4.261975649609381e-06 -räder 4.261975649609381e-06 -▁1800 4.261975649609381e-06 -▁Cypr 4.261975649609381e-06 -▁Join 4.261975649609381e-06 -▁broaden 4.261975649609381e-06 -▁commissioned 4.261975649609381e-06 -▁fehlenden 4.261975649609381e-06 -▁livestock 4.261975649609381e-06 -▁längeren 4.261975649609381e-06 -▁massiv 4.261975649609381e-06 -▁positioned 4.261975649609381e-06 -▁undem 4.261975649609381e-06 -▁wearing 4.261975649609381e-06 -▁Öffnen 4.261975649609381e-06 -Star 4.258187226809729e-06 -brücke 4.258187226809729e-06 -enner 4.258187226809729e-06 -gesetzgebung 4.258187226809729e-06 -range 4.258187226809729e-06 -twortet 4.258187226809729e-06 -▁Beck 4.258187226809729e-06 -▁Kommen 4.258187226809729e-06 -▁Milano 4.258187226809729e-06 -▁Willi 4.258187226809729e-06 -▁enshrined 4.258187226809729e-06 -▁strukturellen 4.258187226809729e-06 -END 4.2543988040100761e-06 -empel 4.2543988040100761e-06 -gesetze 4.2543988040100761e-06 -griffe 4.2543988040100761e-06 -lighten 4.2543988040100761e-06 -risiko 4.2543988040100761e-06 -▁1948 4.2543988040100761e-06 -▁Anpass 4.2543988040100761e-06 -▁BB 4.2543988040100761e-06 -▁Controller 4.2543988040100761e-06 -▁Tort 4.2543988040100761e-06 -▁berechnen 4.2543988040100761e-06 -▁massac 4.2543988040100761e-06 -▁restlichen 4.2543988040100761e-06 -nummern 4.2506103812104232e-06 -ocket 4.2506103812104232e-06 -reffend 4.2506103812104232e-06 -ruption 4.2506103812104232e-06 -▁240 4.2506103812104232e-06 -▁Eurojust 4.2506103812104232e-06 -▁Regul 4.2506103812104232e-06 -▁Serviceleistungen 4.2506103812104232e-06 -▁Völkerm 4.2506103812104232e-06 -▁entrepreneur 4.2506103812104232e-06 -▁komplizierten 4.2506103812104232e-06 -▁repar 4.2506103812104232e-06 -▁vorang 4.2506103812104232e-06 -KR 4.24682195841077e-06 -begriff 4.24682195841077e-06 -present 4.24682195841077e-06 -unning 4.24682195841077e-06 -▁Interventionen 4.24682195841077e-06 -▁Jungen 4.24682195841077e-06 -▁Löhne 4.24682195841077e-06 -▁Zun 4.24682195841077e-06 -▁administrator 4.24682195841077e-06 -▁auszubauen 4.24682195841077e-06 -▁deinem 4.24682195841077e-06 -▁ermutigt 4.24682195841077e-06 -▁gi 4.24682195841077e-06 -▁herausgegeben 4.24682195841077e-06 -▁inex 4.24682195841077e-06 -▁junior 4.24682195841077e-06 -▁liquidity 4.24682195841077e-06 -▁sensors 4.24682195841077e-06 -▁trace 4.24682195841077e-06 -▁verification 4.24682195841077e-06 -▁vorbereit 4.24682195841077e-06 --rate 4.2430335356111174e-06 -angaben 4.2430335356111174e-06 -bing 4.2430335356111174e-06 -ichere 4.2430335356111174e-06 -laven 4.2430335356111174e-06 -ranche 4.2430335356111174e-06 -▁Campo 4.2430335356111174e-06 -▁Continental 4.2430335356111174e-06 -▁Lagerung 4.2430335356111174e-06 -▁aspirations 4.2430335356111174e-06 -▁dot 4.2430335356111174e-06 -▁erledigt 4.2430335356111174e-06 -▁makers 4.2430335356111174e-06 -GI 4.2392451128114645e-06 -IAL 4.2392451128114645e-06 -basierten 4.2392451128114645e-06 -hofer 4.2392451128114645e-06 -iffer 4.2392451128114645e-06 -ifikations 4.2392451128114645e-06 -owa 4.2392451128114645e-06 -rid 4.2392451128114645e-06 -urfing 4.2392451128114645e-06 -▁Orange 4.2392451128114645e-06 -▁Trocken 4.2392451128114645e-06 -▁Yesterday 4.2392451128114645e-06 -▁absoluten 4.2392451128114645e-06 -▁asymmetr 4.2392451128114645e-06 -▁beherrschen 4.2392451128114645e-06 -▁lend 4.2392451128114645e-06 -▁seasonal 4.2392451128114645e-06 -▁trek 4.2392451128114645e-06 -Do 4.2354566900118116e-06 -GR 4.2354566900118116e-06 -andid 4.2354566900118116e-06 -ilde 4.2354566900118116e-06 -olls 4.2354566900118116e-06 -▁Gord 4.2354566900118116e-06 -▁Hold 4.2354566900118116e-06 -▁Moder 4.2354566900118116e-06 -▁Starting 4.2354566900118116e-06 -▁Wortlaut 4.2354566900118116e-06 -▁inaug 4.2354566900118116e-06 -▁persönliches 4.2354566900118116e-06 -▁polnische 4.2354566900118116e-06 -▁voranzubringen 4.2354566900118116e-06 --Stra 4.2316682672121587e-06 -BSD 4.2316682672121587e-06 -Cl 4.2316682672121587e-06 -bled 4.2316682672121587e-06 -iculous 4.2316682672121587e-06 -rique 4.2316682672121587e-06 -▁Cham 4.2316682672121587e-06 -▁Otto 4.2316682672121587e-06 -▁Republ 4.2316682672121587e-06 -▁Routen 4.2316682672121587e-06 -▁alleg 4.2316682672121587e-06 -▁deploy 4.2316682672121587e-06 -▁geared 4.2316682672121587e-06 -▁intensity 4.2316682672121587e-06 -▁schnellstmöglich 4.2316682672121587e-06 -▁simpler 4.2316682672121587e-06 -▁unaus 4.2316682672121587e-06 -▁Deg 4.2278798444125067e-06 -▁Naples 4.2278798444125067e-06 -▁Periode 4.2278798444125067e-06 -▁allgemeiner 4.2278798444125067e-06 -▁bekannter 4.2278798444125067e-06 -▁etabliert 4.2278798444125067e-06 -▁maßgeb 4.2278798444125067e-06 -▁reaffirm 4.2278798444125067e-06 -▁sensiblen 4.2278798444125067e-06 -alia 4.2240914216128538e-06 -etion 4.2240914216128538e-06 -zeilen 4.2240914216128538e-06 -▁Anstatt 4.2240914216128538e-06 -▁CMS 4.2240914216128538e-06 -▁Cell 4.2240914216128538e-06 -▁Diplom 4.2240914216128538e-06 -▁Fontaine 4.2240914216128538e-06 -▁Hang 4.2240914216128538e-06 -▁Varianten 4.2240914216128538e-06 -▁communist 4.2240914216128538e-06 -▁echtes 4.2240914216128538e-06 -▁manoeuv 4.2240914216128538e-06 -lässigkeit 4.2203029988132009e-06 -okolle 4.2203029988132009e-06 -opfer 4.2203029988132009e-06 -räge 4.2203029988132009e-06 -ämmen 4.2203029988132009e-06 -▁Berichterstattung 4.2203029988132009e-06 -▁Leop 4.2203029988132009e-06 -▁OM 4.2203029988132009e-06 -▁Oppositions 4.2203029988132009e-06 -▁Perf 4.2203029988132009e-06 -▁Themas 4.2203029988132009e-06 -▁acoust 4.2203029988132009e-06 -▁compensate 4.2203029988132009e-06 -▁forderte 4.2203029988132009e-06 -▁millenn 4.2203029988132009e-06 -▁photographer 4.2203029988132009e-06 -schauen 4.216514576013548e-06 -udia 4.216514576013548e-06 -▁Bearbeitungs 4.216514576013548e-06 -▁Beitrittskandidaten 4.216514576013548e-06 -▁EU-In 4.216514576013548e-06 -▁Komit 4.216514576013548e-06 -▁Serbian 4.216514576013548e-06 -▁Sitzungs 4.216514576013548e-06 -▁Säulen 4.216514576013548e-06 -▁bevorzugen 4.216514576013548e-06 -▁demograf 4.216514576013548e-06 -▁eingegeben 4.216514576013548e-06 -▁licences 4.216514576013548e-06 -▁screw 4.216514576013548e-06 -▁symptoms 4.216514576013548e-06 -bekistan 4.2127261532138951e-06 -esent 4.2127261532138951e-06 -hause 4.2127261532138951e-06 -ränkungen 4.2127261532138951e-06 -wechselt 4.2127261532138951e-06 -▁Buchen 4.2127261532138951e-06 -▁Ethiop 4.2127261532138951e-06 -▁Mobiltelefon 4.2127261532138951e-06 -▁ODER 4.2127261532138951e-06 -▁Oste 4.2127261532138951e-06 -▁Photocopying 4.2127261532138951e-06 -▁Pier 4.2127261532138951e-06 -▁conversation 4.2127261532138951e-06 -▁großartigen 4.2127261532138951e-06 -▁mitt 4.2127261532138951e-06 -▁promotional 4.2127261532138951e-06 -▁zusammengefasst 4.2127261532138951e-06 --speaking 4.2089377304142422e-06 -.08 4.2089377304142422e-06 -alters 4.2089377304142422e-06 -disk 4.2089377304142422e-06 -ehnte 4.2089377304142422e-06 -igenden 4.2089377304142422e-06 -üstet 4.2089377304142422e-06 -▁Abreise 4.2089377304142422e-06 -▁Errungenschaften 4.2089377304142422e-06 -▁Filmen 4.2089377304142422e-06 -▁Outdoor 4.2089377304142422e-06 -▁Rabatt 4.2089377304142422e-06 -▁Race 4.2089377304142422e-06 -▁S-Bahn 4.2089377304142422e-06 -▁Seville 4.2089377304142422e-06 -▁TCP 4.2089377304142422e-06 -▁Unfälle 4.2089377304142422e-06 -▁atmos 4.2089377304142422e-06 -▁beha 4.2089377304142422e-06 -▁beige 4.2089377304142422e-06 -▁juice 4.2089377304142422e-06 -▁kreat 4.2089377304142422e-06 -▁versatile 4.2089377304142422e-06 -▁weitergehen 4.2089377304142422e-06 -izierten 4.2051493076145893e-06 -plement 4.2051493076145893e-06 -▁Brem 4.2051493076145893e-06 -▁EU-K 4.2051493076145893e-06 -▁Kampfes 4.2051493076145893e-06 -▁Mailing 4.2051493076145893e-06 -▁Myanmar 4.2051493076145893e-06 -▁Noten 4.2051493076145893e-06 -▁Roten 4.2051493076145893e-06 -▁eager 4.2051493076145893e-06 -▁motivation 4.2051493076145893e-06 -▁tied 4.2051493076145893e-06 -▁unent 4.2051493076145893e-06 -▁vernünft 4.2051493076145893e-06 -▁vulnerability 4.2051493076145893e-06 -krebs 4.2013608848149373e-06 -utor 4.2013608848149373e-06 -û 4.2013608848149373e-06 -▁1961 4.2013608848149373e-06 -▁Betrieben 4.2013608848149373e-06 -▁Built 4.2013608848149373e-06 -▁Cord 4.2013608848149373e-06 -▁Einsicht 4.2013608848149373e-06 -▁Fotokopiereinrichtungen 4.2013608848149373e-06 -▁Globalisation 4.2013608848149373e-06 -▁beginn 4.2013608848149373e-06 -▁disproportion 4.2013608848149373e-06 -▁reinen 4.2013608848149373e-06 --Adresse 4.1975724620152844e-06 -ilden 4.1975724620152844e-06 -kript 4.1975724620152844e-06 -ursor 4.1975724620152844e-06 -▁Lj 4.1975724620152844e-06 -▁Miles 4.1975724620152844e-06 -▁Olivenöl 4.1975724620152844e-06 -▁Snacks 4.1975724620152844e-06 -▁aged 4.1975724620152844e-06 -▁butter 4.1975724620152844e-06 -▁drohen 4.1975724620152844e-06 -▁exportieren 4.1975724620152844e-06 -▁pic 4.1975724620152844e-06 -▁privilege 4.1975724620152844e-06 -▁recognis 4.1975724620152844e-06 -▁vollständ 4.1975724620152844e-06 -elevant 4.1937840392156315e-06 -idas 4.1937840392156315e-06 -▁Clients 4.1937840392156315e-06 -▁Demokratischen 4.1937840392156315e-06 -▁Hü 4.1937840392156315e-06 -▁Meinungsfreiheit 4.1937840392156315e-06 -▁drittens 4.1937840392156315e-06 -▁dryer 4.1937840392156315e-06 -▁gesendet 4.1937840392156315e-06 -▁irrit 4.1937840392156315e-06 -▁lehnt 4.1937840392156315e-06 -▁luxuriösen 4.1937840392156315e-06 -▁orientieren 4.1937840392156315e-06 -▁pitch 4.1937840392156315e-06 -▁posting 4.1937840392156315e-06 -▁sitzt 4.1937840392156315e-06 -▁verhaftet 4.1937840392156315e-06 -Pr 4.1899956164159786e-06 -ammlungen 4.1899956164159786e-06 -läßlich 4.1899956164159786e-06 -orden 4.1899956164159786e-06 -wäsche 4.1899956164159786e-06 -äuschen 4.1899956164159786e-06 -▁103 4.1899956164159786e-06 -▁Aktualität 4.1899956164159786e-06 -▁Alzheimer 4.1899956164159786e-06 -▁Erwägungen 4.1899956164159786e-06 -▁Informationsschalter 4.1899956164159786e-06 -▁Khan 4.1899956164159786e-06 -▁Lud 4.1899956164159786e-06 -▁Patrick 4.1899956164159786e-06 -▁Posten 4.1899956164159786e-06 -▁Zar 4.1899956164159786e-06 -▁biotechnology 4.1899956164159786e-06 -▁enters 4.1899956164159786e-06 -▁envis 4.1899956164159786e-06 -▁erholsamen 4.1899956164159786e-06 -▁herausragen 4.1899956164159786e-06 -▁seid 4.1899956164159786e-06 -▁substitute 4.1899956164159786e-06 -▁übersehen 4.1899956164159786e-06 -ilater 4.1862071936163257e-06 -oves 4.1862071936163257e-06 -quin 4.1862071936163257e-06 -utzte 4.1862071936163257e-06 -▁135 4.1862071936163257e-06 -▁Batt 4.1862071936163257e-06 -▁Early 4.1862071936163257e-06 -▁Task 4.1862071936163257e-06 -▁attrakt 4.1862071936163257e-06 -▁feier 4.1862071936163257e-06 -▁financially 4.1862071936163257e-06 -▁mask 4.1862071936163257e-06 -aneous 4.1824187708166728e-06 -uble 4.1824187708166728e-06 -▁1964 4.1824187708166728e-06 -▁Bewirtschaftung 4.1824187708166728e-06 -▁Communications 4.1824187708166728e-06 -▁Fischereif 4.1824187708166728e-06 -▁GAT 4.1824187708166728e-06 -▁Nin 4.1824187708166728e-06 -▁Trent 4.1824187708166728e-06 -▁bulgar 4.1824187708166728e-06 -▁chains 4.1824187708166728e-06 -▁colourful 4.1824187708166728e-06 -▁earnings 4.1824187708166728e-06 -▁entdeckte 4.1824187708166728e-06 -▁powder 4.1824187708166728e-06 -▁snack 4.1824187708166728e-06 -▁tuna 4.1824187708166728e-06 -▁verschle 4.1824187708166728e-06 -.9 4.17863034801702e-06 -ameter 4.17863034801702e-06 -andinav 4.17863034801702e-06 -ieh 4.17863034801702e-06 -▁Cir 4.17863034801702e-06 -▁Kitz 4.17863034801702e-06 -▁Lew 4.17863034801702e-06 -▁Peters 4.17863034801702e-06 -▁Schulungen 4.17863034801702e-06 -▁auszuführen 4.17863034801702e-06 -▁density 4.17863034801702e-06 -▁ernsten 4.17863034801702e-06 -▁fakt 4.17863034801702e-06 -▁longest 4.17863034801702e-06 -▁orientiert 4.17863034801702e-06 -▁traf 4.17863034801702e-06 -▁verbleiben 4.17863034801702e-06 --Restaurant 4.174841925217367e-06 -EY 4.174841925217367e-06 -entes 4.174841925217367e-06 -izient 4.174841925217367e-06 -vorgaben 4.174841925217367e-06 -▁1962 4.174841925217367e-06 -▁Arn 4.174841925217367e-06 -▁Bristol 4.174841925217367e-06 -▁Dj 4.174841925217367e-06 -▁Kommunismus 4.174841925217367e-06 -▁Tirol 4.174841925217367e-06 -▁Trinkwasser 4.174841925217367e-06 -▁Zentrale 4.174841925217367e-06 -▁accurately 4.174841925217367e-06 -▁bone 4.174841925217367e-06 -▁cater 4.174841925217367e-06 -▁ci 4.174841925217367e-06 -▁clash 4.174841925217367e-06 -▁einwand 4.174841925217367e-06 -▁fragt 4.174841925217367e-06 -▁fähig 4.174841925217367e-06 -▁glän 4.174841925217367e-06 -▁merit 4.174841925217367e-06 -▁pig 4.174841925217367e-06 -▁placement 4.174841925217367e-06 -▁sooner 4.174841925217367e-06 -▁specification 4.174841925217367e-06 -▁tenden 4.174841925217367e-06 -▁ungehe 4.174841925217367e-06 --ins 4.171053502417715e-06 -78 4.171053502417715e-06 -achtungs 4.171053502417715e-06 -aille 4.171053502417715e-06 -crib 4.171053502417715e-06 -enza 4.171053502417715e-06 -fire 4.171053502417715e-06 -idos 4.171053502417715e-06 -vation 4.171053502417715e-06 -wicht 4.171053502417715e-06 -▁Auswirkung 4.171053502417715e-06 -▁Films 4.171053502417715e-06 -▁Kamer 4.171053502417715e-06 -▁Liga 4.171053502417715e-06 -▁Patente 4.171053502417715e-06 -▁Schweine 4.171053502417715e-06 -▁Writ 4.171053502417715e-06 -▁blicken 4.171053502417715e-06 -▁diesjährigen 4.171053502417715e-06 -▁earliest 4.171053502417715e-06 -▁elektrischen 4.171053502417715e-06 -▁hon 4.171053502417715e-06 -▁imstande 4.171053502417715e-06 -▁langjährigen 4.171053502417715e-06 -▁rush 4.171053502417715e-06 -▁ub 4.171053502417715e-06 -▁vi 4.171053502417715e-06 --y 4.1672650796180621e-06 -Pl 4.1672650796180621e-06 -apsed 4.1672650796180621e-06 -dies 4.1672650796180621e-06 -filter 4.1672650796180621e-06 -ieft 4.1672650796180621e-06 -iful 4.1672650796180621e-06 -irect 4.1672650796180621e-06 -▁Ansonsten 4.1672650796180621e-06 -▁Cocktail 4.1672650796180621e-06 -▁Coup 4.1672650796180621e-06 -▁kilom 4.1672650796180621e-06 -▁sixth 4.1672650796180621e-06 -▁stieg 4.1672650796180621e-06 -▁unberühr 4.1672650796180621e-06 -▁wissenschaftlicher 4.1672650796180621e-06 --Center 4.1634766568184092e-06 --screen 4.1634766568184092e-06 -OU 4.1634766568184092e-06 -aching 4.1634766568184092e-06 -ausstellung 4.1634766568184092e-06 -building 4.1634766568184092e-06 -food 4.1634766568184092e-06 -ieht 4.1634766568184092e-06 -izierte 4.1634766568184092e-06 -ministration 4.1634766568184092e-06 -▁Kath 4.1634766568184092e-06 -▁Stütz 4.1634766568184092e-06 -▁air-conditioning 4.1634766568184092e-06 -▁alarming 4.1634766568184092e-06 -▁eindring 4.1634766568184092e-06 -▁excluding 4.1634766568184092e-06 -▁phenomena 4.1634766568184092e-06 -▁pub 4.1634766568184092e-06 --American 4.1596882340187563e-06 -besitz 4.1596882340187563e-06 -elegenheiten 4.1596882340187563e-06 -hots 4.1596882340187563e-06 -ivial 4.1596882340187563e-06 -staates 4.1596882340187563e-06 -streben 4.1596882340187563e-06 -turn 4.1596882340187563e-06 -▁Attrib 4.1596882340187563e-06 -▁Einstimmigkeit 4.1596882340187563e-06 -▁Energieverbrauch 4.1596882340187563e-06 -▁HA 4.1596882340187563e-06 -▁Pick 4.1596882340187563e-06 -▁Politikern 4.1596882340187563e-06 -▁Uganda 4.1596882340187563e-06 -▁assort 4.1596882340187563e-06 -▁configur 4.1596882340187563e-06 -▁discounts 4.1596882340187563e-06 -▁freigegeben 4.1596882340187563e-06 -▁royal 4.1596882340187563e-06 -▁seating 4.1596882340187563e-06 -▁tariffs 4.1596882340187563e-06 --Pal 4.1558998112191034e-06 -atically 4.1558998112191034e-06 -aufnahmen 4.1558998112191034e-06 -hauptstadt 4.1558998112191034e-06 -teilte 4.1558998112191034e-06 -zesse 4.1558998112191034e-06 -▁Jones 4.1558998112191034e-06 -▁Lamy 4.1558998112191034e-06 -▁Rücktritt 4.1558998112191034e-06 -▁ankommt 4.1558998112191034e-06 -▁displaced 4.1558998112191034e-06 -▁einziger 4.1558998112191034e-06 -▁kontinuierliche 4.1558998112191034e-06 -anned 4.1521113884194505e-06 -eather 4.1521113884194505e-06 -heel 4.1521113884194505e-06 -lava 4.1521113884194505e-06 -▁Dimensionen 4.1521113884194505e-06 -▁Reception 4.1521113884194505e-06 -▁competing 4.1521113884194505e-06 -▁cozy 4.1521113884194505e-06 -▁exceeded 4.1521113884194505e-06 -▁infin 4.1521113884194505e-06 -▁kontinuier 4.1521113884194505e-06 -▁lobb 4.1521113884194505e-06 -▁simulation 4.1521113884194505e-06 -▁vorzu 4.1521113884194505e-06 -▁wertvoll 4.1521113884194505e-06 -▁überschreiten 4.1521113884194505e-06 -einrichtung 4.1483229656197977e-06 -etary 4.1483229656197977e-06 -etches 4.1483229656197977e-06 -pra 4.1483229656197977e-06 -ymph 4.1483229656197977e-06 -▁Alltags 4.1483229656197977e-06 -▁Centro 4.1483229656197977e-06 -▁Figur 4.1483229656197977e-06 -▁Folgenab 4.1483229656197977e-06 -▁Rinder 4.1483229656197977e-06 -▁Umstand 4.1483229656197977e-06 -▁Weitergabe 4.1483229656197977e-06 -▁beurteilt 4.1483229656197977e-06 -▁broke 4.1483229656197977e-06 -▁peaks 4.1483229656197977e-06 -▁saf 4.1483229656197977e-06 --owned 4.1445345428201448e-06 -arious 4.1445345428201448e-06 -icul 4.1445345428201448e-06 -ová 4.1445345428201448e-06 -rafen 4.1445345428201448e-06 -schwer 4.1445345428201448e-06 -ulture 4.1445345428201448e-06 -▁Auftreten 4.1445345428201448e-06 -▁Fitnesscenter 4.1445345428201448e-06 -▁Geheimnis 4.1445345428201448e-06 -▁Satellit 4.1445345428201448e-06 -▁Synchron 4.1445345428201448e-06 -▁Telefonnummer 4.1445345428201448e-06 -▁demokrat 4.1445345428201448e-06 -▁designing 4.1445345428201448e-06 -▁renommierten 4.1445345428201448e-06 -▁shapes 4.1445345428201448e-06 -▁sofortige 4.1445345428201448e-06 -▁voraussichtlich 4.1445345428201448e-06 -▁vorsehen 4.1445345428201448e-06 -▁Australian 4.1407461200204927e-06 -▁Behauptung 4.1407461200204927e-06 -▁Erforschung 4.1407461200204927e-06 -▁Esc 4.1407461200204927e-06 -▁Milliarde 4.1407461200204927e-06 -▁Personals 4.1407461200204927e-06 -▁Rohr 4.1407461200204927e-06 -▁Terrain 4.1407461200204927e-06 -▁Ung 4.1407461200204927e-06 -▁ausgeht 4.1407461200204927e-06 -▁christlichen 4.1407461200204927e-06 -▁gebühr 4.1407461200204927e-06 -▁somehow 4.1407461200204927e-06 --Dokument 4.13695769722084e-06 -agenturen 4.13695769722084e-06 -amour 4.13695769722084e-06 -aring 4.13695769722084e-06 -dena 4.13695769722084e-06 -klagt 4.13695769722084e-06 -verträglich 4.13695769722084e-06 -▁Anderen 4.13695769722084e-06 -▁Bevölkerungsgruppen 4.13695769722084e-06 -▁Lebensdauer 4.13695769722084e-06 -▁Umwelt- 4.13695769722084e-06 -▁aesthetic 4.13695769722084e-06 -▁begehen 4.13695769722084e-06 -▁tail 4.13695769722084e-06 -del 4.1331692744211869e-06 -uung 4.1331692744211869e-06 -▁Akteuren 4.1331692744211869e-06 -▁Belle 4.1331692744211869e-06 -▁Emer 4.1331692744211869e-06 -▁Erwärmung 4.1331692744211869e-06 -▁Jas 4.1331692744211869e-06 -▁Manip 4.1331692744211869e-06 -▁Roboter 4.1331692744211869e-06 -▁Tester 4.1331692744211869e-06 -▁angegriffen 4.1331692744211869e-06 -▁breiter 4.1331692744211869e-06 -▁fahr 4.1331692744211869e-06 -▁fount 4.1331692744211869e-06 -▁reimburs 4.1331692744211869e-06 -▁relig 4.1331692744211869e-06 -▁restriction 4.1331692744211869e-06 -▁strip 4.1331692744211869e-06 -esso 4.129380851621534e-06 -ganis 4.129380851621534e-06 -rance 4.129380851621534e-06 -reichende 4.129380851621534e-06 -roid 4.129380851621534e-06 -sprachige 4.129380851621534e-06 -uens 4.129380851621534e-06 -▁Beihilfe 4.129380851621534e-06 -▁Beobachtung 4.129380851621534e-06 -▁Kron 4.129380851621534e-06 -▁Spass 4.129380851621534e-06 -▁Zend 4.129380851621534e-06 -▁angesprochenen 4.129380851621534e-06 -▁dah 4.129380851621534e-06 -▁einzel 4.129380851621534e-06 -▁magical 4.129380851621534e-06 -▁mittelalter 4.129380851621534e-06 -▁solle 4.129380851621534e-06 -▁thrown 4.129380851621534e-06 -▁widersprechen 4.129380851621534e-06 -▁Änderungsantrags 4.129380851621534e-06 -läufig 4.1255924288218811e-06 -prinzips 4.1255924288218811e-06 -zes 4.1255924288218811e-06 -üff 4.1255924288218811e-06 -▁4000 4.1255924288218811e-06 -▁Atomwaffen 4.1255924288218811e-06 -▁Entscheidungsträger 4.1255924288218811e-06 -▁Gä 4.1255924288218811e-06 -▁Sozialmodell 4.1255924288218811e-06 -▁UR 4.1255924288218811e-06 -▁chronic 4.1255924288218811e-06 -▁derartiger 4.1255924288218811e-06 -▁rough 4.1255924288218811e-06 -▁timber 4.1255924288218811e-06 --discrimination 4.1218040060222283e-06 -aby 4.1218040060222283e-06 -irl 4.1218040060222283e-06 -▁Jahrhunderten 4.1218040060222283e-06 -▁Streitigkeiten 4.1218040060222283e-06 -▁Sturm 4.1218040060222283e-06 -▁abolished 4.1218040060222283e-06 -▁issuing 4.1218040060222283e-06 -▁pict 4.1218040060222283e-06 -▁poet 4.1218040060222283e-06 -bury 4.1180155832225754e-06 -group 4.1180155832225754e-06 -ifest 4.1180155832225754e-06 -kühl 4.1180155832225754e-06 -opolit 4.1180155832225754e-06 -unas 4.1180155832225754e-06 -währung 4.1180155832225754e-06 -▁AA 4.1180155832225754e-06 -▁Geduld 4.1180155832225754e-06 -▁Had 4.1180155832225754e-06 -▁Instand 4.1180155832225754e-06 -▁Italia 4.1180155832225754e-06 -▁Jes 4.1180155832225754e-06 -▁Managing 4.1180155832225754e-06 -▁Profile 4.1180155832225754e-06 -▁Promotion 4.1180155832225754e-06 -▁Römer 4.1180155832225754e-06 -▁Suchmaschinen 4.1180155832225754e-06 -▁caut 4.1180155832225754e-06 -▁erweist 4.1180155832225754e-06 -▁expecting 4.1180155832225754e-06 -▁freiwillige 4.1180155832225754e-06 -▁frozen 4.1180155832225754e-06 -▁sozioökonom 4.1180155832225754e-06 -▁strengere 4.1180155832225754e-06 -▁suits 4.1180155832225754e-06 -▁temper 4.1180155832225754e-06 -▁würdigen 4.1180155832225754e-06 -ammelten 4.1142271604229225e-06 -sein 4.1142271604229225e-06 -upro 4.1142271604229225e-06 -▁MIT 4.1142271604229225e-06 -▁Salzburger 4.1142271604229225e-06 -▁Typen 4.1142271604229225e-06 -▁annehm 4.1142271604229225e-06 -▁boys 4.1142271604229225e-06 -▁emergence 4.1142271604229225e-06 -▁entit 4.1142271604229225e-06 -▁konzentr 4.1142271604229225e-06 -▁trock 4.1142271604229225e-06 -▁worthwhile 4.1142271604229225e-06 --looking 4.1104387376232704e-06 -bedürft 4.1104387376232704e-06 -garde 4.1104387376232704e-06 -hamm 4.1104387376232704e-06 -wei 4.1104387376232704e-06 -▁Columbia 4.1104387376232704e-06 -▁Logos 4.1104387376232704e-06 -▁Skript 4.1104387376232704e-06 -▁assumption 4.1104387376232704e-06 -▁beherrscht 4.1104387376232704e-06 -▁bevorzugt 4.1104387376232704e-06 -▁critics 4.1104387376232704e-06 -▁geschm 4.1104387376232704e-06 -▁lifetime 4.1104387376232704e-06 -▁severely 4.1104387376232704e-06 -▁verified 4.1104387376232704e-06 -anw 4.1066503148236175e-06 -endel 4.1066503148236175e-06 -eton 4.1066503148236175e-06 -face 4.1066503148236175e-06 -isis 4.1066503148236175e-06 -itas 4.1066503148236175e-06 -ogas 4.1066503148236175e-06 -spekt 4.1066503148236175e-06 -stufen 4.1066503148236175e-06 -ägten 4.1066503148236175e-06 -▁E.ON 4.1066503148236175e-06 -▁Evans 4.1066503148236175e-06 -▁Málaga 4.1066503148236175e-06 -▁Vitorino 4.1066503148236175e-06 -▁WWU 4.1066503148236175e-06 -▁ankommen 4.1066503148236175e-06 -▁cluster 4.1066503148236175e-06 -▁criticisms 4.1066503148236175e-06 -▁injured 4.1066503148236175e-06 -▁mothers 4.1066503148236175e-06 -▁prüft 4.1066503148236175e-06 -▁rentals 4.1066503148236175e-06 -▁staat 4.1066503148236175e-06 -▁verschärfen 4.1066503148236175e-06 -▁vorbehalt 4.1066503148236175e-06 -WF 4.1028618920239646e-06 -achter 4.1028618920239646e-06 -oen 4.1028618920239646e-06 -▁Ask 4.1028618920239646e-06 -▁Audit 4.1028618920239646e-06 -▁Available 4.1028618920239646e-06 -▁Eg 4.1028618920239646e-06 -▁Geldpolitik 4.1028618920239646e-06 -▁Nahe 4.1028618920239646e-06 -▁Ponte 4.1028618920239646e-06 -▁Strafen 4.1028618920239646e-06 -▁formulated 4.1028618920239646e-06 -▁höchstem 4.1028618920239646e-06 -▁koennen 4.1028618920239646e-06 -▁magnetic 4.1028618920239646e-06 -▁multinationalen 4.1028618920239646e-06 -▁müßten 4.1028618920239646e-06 -▁realities 4.1028618920239646e-06 -▁stricter 4.1028618920239646e-06 -antast 4.0990734692243118e-06 -etal 4.0990734692243118e-06 -ettern 4.0990734692243118e-06 -g. 4.0990734692243118e-06 -grammen 4.0990734692243118e-06 -oise 4.0990734692243118e-06 -▁Arctic 4.0990734692243118e-06 -▁Kün 4.0990734692243118e-06 -▁Mans 4.0990734692243118e-06 -▁Mitgliedstaates 4.0990734692243118e-06 -▁Raketen 4.0990734692243118e-06 -▁Straftaten 4.0990734692243118e-06 -▁constituency 4.0990734692243118e-06 -▁copper 4.0990734692243118e-06 -▁dated 4.0990734692243118e-06 -▁gezahlt 4.0990734692243118e-06 -▁telefonisch 4.0990734692243118e-06 -72 4.0952850464246589e-06 -NG 4.0952850464246589e-06 -geladen 4.0952850464246589e-06 -keln 4.0952850464246589e-06 -verd 4.0952850464246589e-06 -völ 4.0952850464246589e-06 -▁Doctor 4.0952850464246589e-06 -▁Enttäuschung 4.0952850464246589e-06 -▁LIFE 4.0952850464246589e-06 -▁Lieder 4.0952850464246589e-06 -▁Vog 4.0952850464246589e-06 -▁abb 4.0952850464246589e-06 -▁backdrop 4.0952850464246589e-06 -▁cav 4.0952850464246589e-06 -▁detriment 4.0952850464246589e-06 -▁erleb 4.0952850464246589e-06 -▁holders 4.0952850464246589e-06 -▁html 4.0952850464246589e-06 -▁memorable 4.0952850464246589e-06 -▁raffin 4.0952850464246589e-06 -▁scenic 4.0952850464246589e-06 -▁styl 4.0952850464246589e-06 -▁verkehr 4.0952850464246589e-06 --state 4.091496623625006e-06 -700 4.091496623625006e-06 -▁Autof 4.091496623625006e-06 -▁Jahrt 4.091496623625006e-06 -▁Rathaus 4.091496623625006e-06 -▁Umbr 4.091496623625006e-06 -▁argu 4.091496623625006e-06 -▁bun 4.091496623625006e-06 -▁gerechte 4.091496623625006e-06 -▁parte 4.091496623625006e-06 -ankt 4.0877082008253531e-06 -ologists 4.0877082008253531e-06 -▁ALDE-Fraktion 4.0877082008253531e-06 -▁Entsp 4.0877082008253531e-06 -▁Formate 4.0877082008253531e-06 -▁Fotografen 4.0877082008253531e-06 -▁Fut 4.0877082008253531e-06 -▁Holocaust 4.0877082008253531e-06 -▁Miguel 4.0877082008253531e-06 -▁Muse 4.0877082008253531e-06 -▁SF 4.0877082008253531e-06 -▁Telec 4.0877082008253531e-06 -▁Umweltpolitik 4.0877082008253531e-06 -▁abstract 4.0877082008253531e-06 -▁beziehe 4.0877082008253531e-06 -▁containers 4.0877082008253531e-06 -▁demonstrating 4.0877082008253531e-06 -▁encryption 4.0877082008253531e-06 -▁offener 4.0877082008253531e-06 --minded 4.0839197780257e-06 -ATA 4.0839197780257e-06 -iks 4.0839197780257e-06 -reichend 4.0839197780257e-06 -▁Dez 4.0839197780257e-06 -▁Schengener 4.0839197780257e-06 -▁Supreme 4.0839197780257e-06 -▁Wesent 4.0839197780257e-06 -▁Züge 4.0839197780257e-06 -▁befand 4.0839197780257e-06 -▁begünst 4.0839197780257e-06 -▁tastefully 4.0839197780257e-06 -▁Übernachtungen 4.0839197780257e-06 -gewöhn 4.0801313552260481e-06 -lieferung 4.0801313552260481e-06 -▁BO 4.0801313552260481e-06 -▁Bibliotheken 4.0801313552260481e-06 -▁Edward 4.0801313552260481e-06 -▁Lunch 4.0801313552260481e-06 -▁breakthrough 4.0801313552260481e-06 -▁demnach 4.0801313552260481e-06 -▁einheim 4.0801313552260481e-06 -▁neck 4.0801313552260481e-06 -▁optimization 4.0801313552260481e-06 -▁pity 4.0801313552260481e-06 -attered 4.0763429324263953e-06 -ehnt 4.0763429324263953e-06 -lus 4.0763429324263953e-06 -öffent 4.0763429324263953e-06 -▁Aktual 4.0763429324263953e-06 -▁Brad 4.0763429324263953e-06 -▁Fou 4.0763429324263953e-06 -▁Morgan 4.0763429324263953e-06 -▁Neug 4.0763429324263953e-06 -▁Semin 4.0763429324263953e-06 -▁clarified 4.0763429324263953e-06 -▁declar 4.0763429324263953e-06 -▁devastating 4.0763429324263953e-06 -▁establishes 4.0763429324263953e-06 -▁installer 4.0763429324263953e-06 -▁ros 4.0763429324263953e-06 -▁varies 4.0763429324263953e-06 -▁Äng 4.0763429324263953e-06 -acon 4.0725545096267424e-06 -amba 4.0725545096267424e-06 -anzig 4.0725545096267424e-06 -cap 4.0725545096267424e-06 -forsch 4.0725545096267424e-06 -ugo 4.0725545096267424e-06 -▁Fehlern 4.0725545096267424e-06 -▁Jury 4.0725545096267424e-06 -▁Secret 4.0725545096267424e-06 -▁Substanz 4.0725545096267424e-06 -▁Wart 4.0725545096267424e-06 -▁behindertenfreundliche 4.0725545096267424e-06 -▁defended 4.0725545096267424e-06 -▁herbeizuführen 4.0725545096267424e-06 -▁mankind 4.0725545096267424e-06 -▁purchaser 4.0725545096267424e-06 -▁treating 4.0725545096267424e-06 -▁valleys 4.0725545096267424e-06 -▁wahl 4.0725545096267424e-06 -avan 4.0687660868270895e-06 -haber 4.0687660868270895e-06 -nergies 4.0687660868270895e-06 -obel 4.0687660868270895e-06 -orry 4.0687660868270895e-06 -otherapy 4.0687660868270895e-06 -speed 4.0687660868270895e-06 -▁215 4.0687660868270895e-06 -▁Administrator 4.0687660868270895e-06 -▁Auswertung 4.0687660868270895e-06 -▁Bettwäsche 4.0687660868270895e-06 -▁Bezirk 4.0687660868270895e-06 -▁Ice 4.0687660868270895e-06 -▁Programmier 4.0687660868270895e-06 -▁Vertiefung 4.0687660868270895e-06 -▁Voice 4.0687660868270895e-06 -▁generates 4.0687660868270895e-06 -▁zugewiesen 4.0687660868270895e-06 -BO 4.0649776640274366e-06 -eitet 4.0649776640274366e-06 -itative 4.0649776640274366e-06 -ländischen 4.0649776640274366e-06 -steigt 4.0649776640274366e-06 -ümer 4.0649776640274366e-06 -▁Einmischung 4.0649776640274366e-06 -▁Likewise 4.0649776640274366e-06 -▁Modellen 4.0649776640274366e-06 -▁Passagier 4.0649776640274366e-06 --Website 4.0611892412277837e-06 -atom 4.0611892412277837e-06 -blog 4.0611892412277837e-06 -proofed 4.0611892412277837e-06 -zeigt 4.0611892412277837e-06 -▁Ale 4.0611892412277837e-06 -▁Familienzimmer 4.0611892412277837e-06 -▁Gehäuse 4.0611892412277837e-06 -▁Probe 4.0611892412277837e-06 -▁beneficiaries 4.0611892412277837e-06 -▁gleichnam 4.0611892412277837e-06 -▁hes 4.0611892412277837e-06 -▁literary 4.0611892412277837e-06 -▁polizeil 4.0611892412277837e-06 -▁stattfand 4.0611892412277837e-06 -▁surve 4.0611892412277837e-06 -▁wovon 4.0611892412277837e-06 --Krise 4.0574008184281308e-06 -ERT 4.0574008184281308e-06 -beginn 4.0574008184281308e-06 -ioden 4.0574008184281308e-06 -oment 4.0574008184281308e-06 -▁108 4.0574008184281308e-06 -▁A320 4.0574008184281308e-06 -▁Cox 4.0574008184281308e-06 -▁Ecuador 4.0574008184281308e-06 -▁Maximum 4.0574008184281308e-06 -▁Particular 4.0574008184281308e-06 -▁anget 4.0574008184281308e-06 -▁biometr 4.0574008184281308e-06 -▁geradezu 4.0574008184281308e-06 -▁Überschwemmungen 4.0574008184281308e-06 -▁ört 4.0574008184281308e-06 -hens 4.0536123956284779e-06 -itim 4.0536123956284779e-06 -unge 4.0536123956284779e-06 -▁Bem 4.0536123956284779e-06 -▁Einigkeit 4.0536123956284779e-06 -▁Fabr 4.0536123956284779e-06 -▁Ful 4.0536123956284779e-06 -▁Mayor 4.0536123956284779e-06 -▁entschuldigen 4.0536123956284779e-06 -▁finnischen 4.0536123956284779e-06 -.S. 4.0498239728288259e-06 -ampe 4.0498239728288259e-06 -emed 4.0498239728288259e-06 -endar 4.0498239728288259e-06 -hältnismäß 4.0498239728288259e-06 -rateg 4.0498239728288259e-06 -▁-M 4.0498239728288259e-06 -▁Fu 4.0498239728288259e-06 -▁Gesundheitsversorgung 4.0498239728288259e-06 -▁JPEG 4.0498239728288259e-06 -▁Pressemitteil 4.0498239728288259e-06 -▁incomplete 4.0498239728288259e-06 -▁kitchenette 4.0498239728288259e-06 -▁kombinieren 4.0498239728288259e-06 -▁patio 4.0498239728288259e-06 -▁tongue 4.0498239728288259e-06 --Inter 4.046035550029173e-06 -.06 4.046035550029173e-06 -aminated 4.046035550029173e-06 -handelt 4.046035550029173e-06 -klassen 4.046035550029173e-06 -sprecher 4.046035550029173e-06 -▁Bomben 4.046035550029173e-06 -▁Kleine 4.046035550029173e-06 -▁Milit 4.046035550029173e-06 -▁Sharon 4.046035550029173e-06 -▁TOS 4.046035550029173e-06 -▁Treibhausgas 4.046035550029173e-06 -▁Verantwortungs 4.046035550029173e-06 -▁binary 4.046035550029173e-06 -▁entail 4.046035550029173e-06 -▁entfern 4.046035550029173e-06 -▁herausragende 4.046035550029173e-06 -▁sä 4.046035550029173e-06 -▁verh 4.046035550029173e-06 -ARC 4.04224712722952e-06 -earch 4.04224712722952e-06 -embles 4.04224712722952e-06 -feldern 4.04224712722952e-06 -hut 4.04224712722952e-06 -rücke 4.04224712722952e-06 -äher 4.04224712722952e-06 -öhne 4.04224712722952e-06 -▁Biom 4.04224712722952e-06 -▁Göteborg 4.04224712722952e-06 -▁Schmerz 4.04224712722952e-06 -▁Tiger 4.04224712722952e-06 -▁Treffpunkt 4.04224712722952e-06 -▁angestreb 4.04224712722952e-06 -▁avant 4.04224712722952e-06 -▁commem 4.04224712722952e-06 -▁destroying 4.04224712722952e-06 -▁nachhaltiger 4.04224712722952e-06 -▁renomm 4.04224712722952e-06 -▁transparenter 4.04224712722952e-06 -▁unterstützten 4.04224712722952e-06 -asur 4.0384587044298672e-06 -beam 4.0384587044298672e-06 -isting 4.0384587044298672e-06 -ono 4.0384587044298672e-06 -uzent 4.0384587044298672e-06 -▁Alan 4.0384587044298672e-06 -▁Fachleute 4.0384587044298672e-06 -▁Gig 4.0384587044298672e-06 -▁Kupfer 4.0384587044298672e-06 -▁Leucht 4.0384587044298672e-06 -▁Moral 4.0384587044298672e-06 -▁ausschl 4.0384587044298672e-06 -▁chaos 4.0384587044298672e-06 -▁confused 4.0384587044298672e-06 -▁diente 4.0384587044298672e-06 -▁dramatisch 4.0384587044298672e-06 -▁forecast 4.0384587044298672e-06 -▁licenses 4.0384587044298672e-06 -▁picked 4.0384587044298672e-06 --eff 4.0346702816302143e-06 -auben 4.0346702816302143e-06 -eiter 4.0346702816302143e-06 -ertoire 4.0346702816302143e-06 -prise 4.0346702816302143e-06 -verordnung 4.0346702816302143e-06 -öglichkeiten 4.0346702816302143e-06 -▁Bauer 4.0346702816302143e-06 -▁Cohn-Bendit 4.0346702816302143e-06 -▁Herman 4.0346702816302143e-06 -▁Persön 4.0346702816302143e-06 -▁Terme 4.0346702816302143e-06 -▁Unterscheidung 4.0346702816302143e-06 -▁abd 4.0346702816302143e-06 -▁discr 4.0346702816302143e-06 -▁eBay 4.0346702816302143e-06 -▁inval 4.0346702816302143e-06 -▁lays 4.0346702816302143e-06 -▁mais 4.0346702816302143e-06 -▁postponed 4.0346702816302143e-06 -▁recordings 4.0346702816302143e-06 -▁trac 4.0346702816302143e-06 -▁vereinen 4.0346702816302143e-06 -tal 4.0308818588305614e-06 -▁12th 4.0308818588305614e-06 -▁Chen 4.0308818588305614e-06 -▁IG 4.0308818588305614e-06 -▁Leone 4.0308818588305614e-06 -▁Qualifikationen 4.0308818588305614e-06 -▁Viv 4.0308818588305614e-06 -▁eindeutigen 4.0308818588305614e-06 -▁informative 4.0308818588305614e-06 -▁tent 4.0308818588305614e-06 -▁überwunden 4.0308818588305614e-06 -geschäfte 4.0270934360309085e-06 -iciencies 4.0270934360309085e-06 -positionen 4.0270934360309085e-06 -rium 4.0270934360309085e-06 -wanderung 4.0270934360309085e-06 -xen 4.0270934360309085e-06 -zuladen 4.0270934360309085e-06 -▁Drittländer 4.0270934360309085e-06 -▁Immo 4.0270934360309085e-06 -▁Knochen 4.0270934360309085e-06 -▁Kolleg 4.0270934360309085e-06 -▁Omn 4.0270934360309085e-06 -▁Taking 4.0270934360309085e-06 -▁Vermitt 4.0270934360309085e-06 -▁apr 4.0270934360309085e-06 -▁beträchtlichen 4.0270934360309085e-06 -▁phases 4.0270934360309085e-06 -▁schwarze 4.0270934360309085e-06 -erz 4.0233050132312565e-06 -pendence 4.0233050132312565e-06 -sider 4.0233050132312565e-06 -▁B2 4.0233050132312565e-06 -▁Besatz 4.0233050132312565e-06 -▁Ideologie 4.0233050132312565e-06 -▁Renault 4.0233050132312565e-06 -▁Schaff 4.0233050132312565e-06 -▁Schwarzen 4.0233050132312565e-06 -▁Staub 4.0233050132312565e-06 -▁Verbrauchs 4.0233050132312565e-06 -▁aufgezeigt 4.0233050132312565e-06 -▁documentary 4.0233050132312565e-06 -▁einb 4.0233050132312565e-06 -▁expects 4.0233050132312565e-06 -▁hö 4.0233050132312565e-06 -▁lawyer 4.0233050132312565e-06 -▁participant 4.0233050132312565e-06 -▁seiten 4.0233050132312565e-06 -▁subsidiaries 4.0233050132312565e-06 -▁unwill 4.0233050132312565e-06 -bedded 4.0195165904316036e-06 -rought 4.0195165904316036e-06 -umption 4.0195165904316036e-06 -▁Depart 4.0195165904316036e-06 -▁Griechenlands 4.0195165904316036e-06 -▁McK 4.0195165904316036e-06 -▁Nowadays 4.0195165904316036e-06 -▁Tibetan 4.0195165904316036e-06 -▁draußen 4.0195165904316036e-06 -▁endanger 4.0195165904316036e-06 -▁gast 4.0195165904316036e-06 -▁geograf 4.0195165904316036e-06 -▁icons 4.0195165904316036e-06 -▁oven 4.0195165904316036e-06 -▁storn 4.0195165904316036e-06 -▁sudden 4.0195165904316036e-06 -▁totalitarian 4.0195165904316036e-06 -▁vergröß 4.0195165904316036e-06 -Hotel 4.0157281676319507e-06 -dehnen 4.0157281676319507e-06 -esst 4.0157281676319507e-06 -irgend 4.0157281676319507e-06 -verbände 4.0157281676319507e-06 -zell 4.0157281676319507e-06 -▁Bran 4.0157281676319507e-06 -▁Energietr 4.0157281676319507e-06 -▁Etappe 4.0157281676319507e-06 -▁Reservierungen 4.0157281676319507e-06 -▁Serb 4.0157281676319507e-06 -▁Slideshows 4.0157281676319507e-06 -▁Verknüpfung 4.0157281676319507e-06 -▁Wonder 4.0157281676319507e-06 -▁Za 4.0157281676319507e-06 -▁Zuwanderung 4.0157281676319507e-06 -▁ausschlag 4.0157281676319507e-06 -▁bikes 4.0157281676319507e-06 -▁encrypt 4.0157281676319507e-06 -▁forecasts 4.0157281676319507e-06 -▁ordnungsgemäße 4.0157281676319507e-06 -▁peninsula 4.0157281676319507e-06 -▁üblicherweise 4.0157281676319507e-06 -anche 4.0119397448322978e-06 -bürger 4.0119397448322978e-06 -idation 4.0119397448322978e-06 -opic 4.0119397448322978e-06 -zwingen 4.0119397448322978e-06 -▁Aussteller 4.0119397448322978e-06 -▁ICC 4.0119397448322978e-06 -▁Russians 4.0119397448322978e-06 -▁Traff 4.0119397448322978e-06 -▁Wars 4.0119397448322978e-06 -▁arg 4.0119397448322978e-06 -▁awaiting 4.0119397448322978e-06 -▁beizubehalten 4.0119397448322978e-06 -▁coex 4.0119397448322978e-06 -▁elektr 4.0119397448322978e-06 -▁nachgewiesen 4.0119397448322978e-06 -▁pesticides 4.0119397448322978e-06 -▁unvollständ 4.0119397448322978e-06 -ovolta 4.0081513220326449e-06 -ulsion 4.0081513220326449e-06 -xx 4.0081513220326449e-06 -▁Fischfang 4.0081513220326449e-06 -▁ILO 4.0081513220326449e-06 -▁Kameras 4.0081513220326449e-06 -▁Mike 4.0081513220326449e-06 -▁Tech 4.0081513220326449e-06 -▁Veranstalter 4.0081513220326449e-06 -▁Vollendung 4.0081513220326449e-06 -▁aktiver 4.0081513220326449e-06 -▁discour 4.0081513220326449e-06 -▁indicating 4.0081513220326449e-06 -▁mounting 4.0081513220326449e-06 -▁precedent 4.0081513220326449e-06 -▁schlafen 4.0081513220326449e-06 -anet 4.004362899232992e-06 -ardi 4.004362899232992e-06 -aukasus 4.004362899232992e-06 -flicht 4.004362899232992e-06 -pul 4.004362899232992e-06 -steuers 4.004362899232992e-06 -strahlung 4.004362899232992e-06 -ugoslaw 4.004362899232992e-06 -wachstums 4.004362899232992e-06 -öck 4.004362899232992e-06 -▁EBIT 4.004362899232992e-06 -▁Seitdem 4.004362899232992e-06 -▁Speise 4.004362899232992e-06 -▁erneuerbarer 4.004362899232992e-06 -▁gröss 4.004362899232992e-06 -▁unterworfen 4.004362899232992e-06 -▁verlin 4.004362899232992e-06 --Fra 4.0005744764333391e-06 -assing 4.0005744764333391e-06 -fle 4.0005744764333391e-06 -points 4.0005744764333391e-06 -zusammen 4.0005744764333391e-06 -▁Arte 4.0005744764333391e-06 -▁Bestes 4.0005744764333391e-06 -▁Hope 4.0005744764333391e-06 -▁Jugendherberge 4.0005744764333391e-06 -▁Kundendienst 4.0005744764333391e-06 -▁N. 4.0005744764333391e-06 -▁NGL 4.0005744764333391e-06 -▁Ty 4.0005744764333391e-06 -▁brush 4.0005744764333391e-06 -▁enforced 4.0005744764333391e-06 -▁exported 4.0005744764333391e-06 -▁extremen 4.0005744764333391e-06 -▁persistent 4.0005744764333391e-06 -▁rises 4.0005744764333391e-06 -▁shipment 4.0005744764333391e-06 -5-00 3.9967860536336862e-06 -herstellung 3.9967860536336862e-06 -si 3.9967860536336862e-06 -stellers 3.9967860536336862e-06 -uffet 3.9967860536336862e-06 -urers 3.9967860536336862e-06 -wirtschaftlichen 3.9967860536336862e-06 -▁Aufschwung 3.9967860536336862e-06 -▁Erinnerungen 3.9967860536336862e-06 -▁Independent 3.9967860536336862e-06 -▁Paragraph 3.9967860536336862e-06 -▁Points 3.9967860536336862e-06 -▁Resultate 3.9967860536336862e-06 -▁Verhaft 3.9967860536336862e-06 -▁additives 3.9967860536336862e-06 -▁cache 3.9967860536336862e-06 -▁electr 3.9967860536336862e-06 -▁foreigners 3.9967860536336862e-06 -▁molec 3.9967860536336862e-06 --Em 3.9929976308340342e-06 -aced 3.9929976308340342e-06 -piece 3.9929976308340342e-06 -ransport 3.9929976308340342e-06 -spartnern 3.9929976308340342e-06 -wie 3.9929976308340342e-06 -▁Aussprachen 3.9929976308340342e-06 -▁Beförder 3.9929976308340342e-06 -▁Elis 3.9929976308340342e-06 -▁Prices 3.9929976308340342e-06 -▁Ratspräsidentin 3.9929976308340342e-06 -▁Spur 3.9929976308340342e-06 -▁Zivilisten 3.9929976308340342e-06 -▁café 3.9929976308340342e-06 -▁incoming 3.9929976308340342e-06 -▁inherent 3.9929976308340342e-06 -▁mex 3.9929976308340342e-06 -▁recruitment 3.9929976308340342e-06 -▁translators 3.9929976308340342e-06 --Fin 3.9892092080343813e-06 -tre 3.9892092080343813e-06 -▁650 3.9892092080343813e-06 -▁Currency 3.9892092080343813e-06 -▁Friedensprozess 3.9892092080343813e-06 -▁Haftbefehl 3.9892092080343813e-06 -▁Kro 3.9892092080343813e-06 -▁Kun 3.9892092080343813e-06 -▁Object 3.9892092080343813e-06 -▁Phoen 3.9892092080343813e-06 -▁Sounds 3.9892092080343813e-06 -▁Subs 3.9892092080343813e-06 -▁aren 3.9892092080343813e-06 -▁corrected 3.9892092080343813e-06 -▁illumin 3.9892092080343813e-06 -▁lyr 3.9892092080343813e-06 -▁nationality 3.9892092080343813e-06 -▁strategischer 3.9892092080343813e-06 -steine 3.9854207852347284e-06 -utzung 3.9854207852347284e-06 -▁1944 3.9854207852347284e-06 -▁3.2 3.9854207852347284e-06 -▁Administrat 3.9854207852347284e-06 -▁Evolution 3.9854207852347284e-06 -▁Firm 3.9854207852347284e-06 -▁Haftungs 3.9854207852347284e-06 -▁Hein 3.9854207852347284e-06 -▁Sendung 3.9854207852347284e-06 -▁Vervielfält 3.9854207852347284e-06 -▁Zusage 3.9854207852347284e-06 -▁anybody 3.9854207852347284e-06 -▁behand 3.9854207852347284e-06 -▁bell 3.9854207852347284e-06 -▁failures 3.9854207852347284e-06 -▁likewise 3.9854207852347284e-06 -▁reconstruct 3.9854207852347284e-06 -▁sahen 3.9854207852347284e-06 -▁scan 3.9854207852347284e-06 -▁sciences 3.9854207852347284e-06 -▁warme 3.9854207852347284e-06 -▁überraschend 3.9854207852347284e-06 -DN 3.9816323624350755e-06 -ipse 3.9816323624350755e-06 -kreuz 3.9816323624350755e-06 -orrent 3.9816323624350755e-06 -unities 3.9816323624350755e-06 -▁Automation 3.9816323624350755e-06 -▁GD 3.9816323624350755e-06 -▁ICEcat 3.9816323624350755e-06 -▁Zonen 3.9816323624350755e-06 -▁advocates 3.9816323624350755e-06 -▁appellieren 3.9816323624350755e-06 -▁ausgedehnt 3.9816323624350755e-06 -▁gene 3.9816323624350755e-06 -▁illegally 3.9816323624350755e-06 -▁overr 3.9816323624350755e-06 -▁recommends 3.9816323624350755e-06 -▁reichlich 3.9816323624350755e-06 -▁submission 3.9816323624350755e-06 -▁tox 3.9816323624350755e-06 -▁zurecht 3.9816323624350755e-06 -▁ägypt 3.9816323624350755e-06 -afrika 3.9778439396354226e-06 -onstr 3.9778439396354226e-06 -▁Custom 3.9778439396354226e-06 -▁Gehen 3.9778439396354226e-06 -▁Schwester 3.9778439396354226e-06 -▁Schwäche 3.9778439396354226e-06 -▁imagin 3.9778439396354226e-06 -▁kümmert 3.9778439396354226e-06 -▁liberalization 3.9778439396354226e-06 -▁liegende 3.9778439396354226e-06 -▁manuell 3.9778439396354226e-06 --Modus 3.97405551683577e-06 -aco 3.97405551683577e-06 -parl 3.97405551683577e-06 -zube 3.97405551683577e-06 -▁Caucasus 3.97405551683577e-06 -▁Motiv 3.97405551683577e-06 -▁Oase 3.97405551683577e-06 -▁Prad 3.97405551683577e-06 -▁Schatz 3.97405551683577e-06 -▁Wahlkampf 3.97405551683577e-06 -▁abstra 3.97405551683577e-06 -▁beziehungsweise 3.97405551683577e-06 -▁blieben 3.97405551683577e-06 -▁composer 3.97405551683577e-06 -▁connects 3.97405551683577e-06 -▁definite 3.97405551683577e-06 -▁hoteleigen 3.97405551683577e-06 -▁smell 3.97405551683577e-06 -▁vague 3.97405551683577e-06 -▁wendet 3.97405551683577e-06 -GO 3.9702670940361168e-06 -▁5.1 3.9702670940361168e-06 -▁Brunnen 3.9702670940361168e-06 -▁Kürzung 3.9702670940361168e-06 -▁Particip 3.9702670940361168e-06 -▁criticised 3.9702670940361168e-06 -▁democratisation 3.9702670940361168e-06 -▁honey 3.9702670940361168e-06 -▁konstruktiv 3.9702670940361168e-06 -▁veranlasst 3.9702670940361168e-06 -behand 3.9664786712364639e-06 -ermin 3.9664786712364639e-06 -etik 3.9664786712364639e-06 -inners 3.9664786712364639e-06 -owned 3.9664786712364639e-06 -plänen 3.9664786712364639e-06 -senal 3.9664786712364639e-06 -ubringen 3.9664786712364639e-06 -äme 3.9664786712364639e-06 -▁Dampfbad 3.9664786712364639e-06 -▁Images 3.9664786712364639e-06 -▁Jh 3.9664786712364639e-06 -▁Kohlend 3.9664786712364639e-06 -▁Konsultationen 3.9664786712364639e-06 -▁Oster 3.9664786712364639e-06 -▁archä 3.9664786712364639e-06 -▁console 3.9664786712364639e-06 -▁distanz 3.9664786712364639e-06 -▁grande 3.9664786712364639e-06 -▁gratitude 3.9664786712364639e-06 -▁incap 3.9664786712364639e-06 -▁jedermann 3.9664786712364639e-06 -▁lady 3.9664786712364639e-06 -▁tort 3.9664786712364639e-06 -▁übersetzen 3.9664786712364639e-06 -ERE 3.9626902484368119e-06 -aud 3.9626902484368119e-06 -elter 3.9626902484368119e-06 -▁Freundschaft 3.9626902484368119e-06 -▁Standorte 3.9626902484368119e-06 -▁Verspät 3.9626902484368119e-06 -▁conditioned 3.9626902484368119e-06 -▁debian 3.9626902484368119e-06 -▁gifts 3.9626902484368119e-06 -▁leistungsstark 3.9626902484368119e-06 -▁stack 3.9626902484368119e-06 -I. 3.958901825637159e-06 -exp 3.958901825637159e-06 -gewichts 3.958901825637159e-06 -▁Inde 3.958901825637159e-06 -▁Kampagnen 3.958901825637159e-06 -▁Mahlzeiten 3.958901825637159e-06 -▁UM 3.958901825637159e-06 -▁existierenden 3.958901825637159e-06 -▁fiber 3.958901825637159e-06 -▁friendship 3.958901825637159e-06 -▁inserted 3.958901825637159e-06 -▁likes 3.958901825637159e-06 -▁occurring 3.958901825637159e-06 -▁punish 3.958901825637159e-06 --Datenbank 3.9551134028375061e-06 -BI 3.9551134028375061e-06 -ceptions 3.9551134028375061e-06 -haupt 3.9551134028375061e-06 -spezifischen 3.9551134028375061e-06 -uni 3.9551134028375061e-06 -▁ALE-Fraktion 3.9551134028375061e-06 -▁Abbas 3.9551134028375061e-06 -▁Anlaß 3.9551134028375061e-06 -▁Ausgleichs 3.9551134028375061e-06 -▁Besichtigung 3.9551134028375061e-06 -▁GMO 3.9551134028375061e-06 -▁Gates 3.9551134028375061e-06 -▁Pfund 3.9551134028375061e-06 -▁STO 3.9551134028375061e-06 -▁accountable 3.9551134028375061e-06 -▁billiger 3.9551134028375061e-06 -▁delic 3.9551134028375061e-06 -▁ehe 3.9551134028375061e-06 -▁einm 3.9551134028375061e-06 -▁gezielte 3.9551134028375061e-06 -▁misleading 3.9551134028375061e-06 -▁senten 3.9551134028375061e-06 -▁travelers 3.9551134028375061e-06 -▁versteckt 3.9551134028375061e-06 -▁zügig 3.9551134028375061e-06 -29 3.9513249800378532e-06 -bildungs 3.9513249800378532e-06 -loaden 3.9513249800378532e-06 -▁Architect 3.9513249800378532e-06 -▁Cott 3.9513249800378532e-06 -▁Zigaretten 3.9513249800378532e-06 -▁allerg 3.9513249800378532e-06 -▁approxim 3.9513249800378532e-06 -▁borr 3.9513249800378532e-06 -▁demo 3.9513249800378532e-06 -▁ethnische 3.9513249800378532e-06 -▁handy 3.9513249800378532e-06 -▁hierher 3.9513249800378532e-06 -▁kroat 3.9513249800378532e-06 -▁stellten 3.9513249800378532e-06 -▁subt 3.9513249800378532e-06 -▁veto 3.9513249800378532e-06 -▁worsen 3.9513249800378532e-06 -▁Österreichs 3.9513249800378532e-06 -adores 3.9475365572382e-06 -enteil 3.9475365572382e-06 -gesetzte 3.9475365572382e-06 -itärer 3.9475365572382e-06 -izers 3.9475365572382e-06 -kampagne 3.9475365572382e-06 -loss 3.9475365572382e-06 -mart 3.9475365572382e-06 -vard 3.9475365572382e-06 -▁Behinderung 3.9475365572382e-06 -▁Bildes 3.9475365572382e-06 -▁Customs 3.9475365572382e-06 -▁Gur 3.9475365572382e-06 -▁Holl 3.9475365572382e-06 -▁Ital 3.9475365572382e-06 -▁PH 3.9475365572382e-06 -▁Package 3.9475365572382e-06 -▁belg 3.9475365572382e-06 -▁cafes 3.9475365572382e-06 -▁civilisation 3.9475365572382e-06 -▁o. 3.9475365572382e-06 -▁orn 3.9475365572382e-06 -▁prod 3.9475365572382e-06 -▁tastes 3.9475365572382e-06 -hältnismäßig 3.9437481344385474e-06 -oppeln 3.9437481344385474e-06 -rus 3.9437481344385474e-06 -ventionen 3.9437481344385474e-06 -▁0.5 3.9437481344385474e-06 -▁Breitband 3.9437481344385474e-06 -▁Expo 3.9437481344385474e-06 -▁Freiburg 3.9437481344385474e-06 -▁Planungs 3.9437481344385474e-06 -▁Rechtsetzung 3.9437481344385474e-06 -▁auferlegt 3.9437481344385474e-06 -▁gaben 3.9437481344385474e-06 -▁radioactive 3.9437481344385474e-06 -▁streichen 3.9437481344385474e-06 -▁submitting 3.9437481344385474e-06 -▁türk 3.9437481344385474e-06 -▁unvergess 3.9437481344385474e-06 -▁Ära 3.9437481344385474e-06 -renten 3.9399597116388945e-06 -texts 3.9399597116388945e-06 -▁Admir 3.9399597116388945e-06 -▁Eingliederung 3.9399597116388945e-06 -▁Kontinuität 3.9399597116388945e-06 -▁Schokolade 3.9399597116388945e-06 -▁Straft 3.9399597116388945e-06 -▁Tall 3.9399597116388945e-06 -▁Yahoo 3.9399597116388945e-06 -▁ermögl 3.9399597116388945e-06 -▁irakischen 3.9399597116388945e-06 -▁licensing 3.9399597116388945e-06 -▁quer 3.9399597116388945e-06 -schulden 3.9361712888392417e-06 -▁Ech 3.9361712888392417e-06 -▁Hamm 3.9361712888392417e-06 -▁Mob 3.9361712888392417e-06 -▁Skip 3.9361712888392417e-06 -▁afor 3.9361712888392417e-06 -▁appalling 3.9361712888392417e-06 -▁aufkommen 3.9361712888392417e-06 -▁e. 3.9361712888392417e-06 -▁eröffnete 3.9361712888392417e-06 -▁geleistete 3.9361712888392417e-06 -▁herzlichen 3.9361712888392417e-06 -▁hyd 3.9361712888392417e-06 -▁informellen 3.9361712888392417e-06 -▁unabdingbar 3.9361712888392417e-06 -nergy 3.93238286603959e-06 -▁101 3.93238286603959e-06 -▁ECOFIN 3.93238286603959e-06 -▁Finden 3.93238286603959e-06 -▁RC 3.93238286603959e-06 -▁Spielzeug 3.93238286603959e-06 -▁Tastatur 3.93238286603959e-06 -▁assumes 3.93238286603959e-06 -▁chest 3.93238286603959e-06 -▁gladly 3.93238286603959e-06 -▁unterstützte 3.93238286603959e-06 -▁verweigern 3.93238286603959e-06 --winning 3.9285944432399367e-06 -freundlich 3.9285944432399367e-06 -indest 3.9285944432399367e-06 -schle 3.9285944432399367e-06 -élé 3.9285944432399367e-06 -▁ALE 3.9285944432399367e-06 -▁Mandriva 3.9285944432399367e-06 -▁Ostse 3.9285944432399367e-06 -▁Trainer 3.9285944432399367e-06 -▁drastisch 3.9285944432399367e-06 -▁erfolgreicher 3.9285944432399367e-06 -▁judg 3.9285944432399367e-06 -▁reflekt 3.9285944432399367e-06 -▁twenty-f 3.9285944432399367e-06 -FF 3.9248060204402838e-06 -beteiligung 3.9248060204402838e-06 -heid 3.9248060204402838e-06 -isible 3.9248060204402838e-06 -tres 3.9248060204402838e-06 -vorschrift 3.9248060204402838e-06 -▁Amn 3.9248060204402838e-06 -▁Banquet 3.9248060204402838e-06 -▁Brazilian 3.9248060204402838e-06 -▁Luis 3.9248060204402838e-06 -▁Pinien 3.9248060204402838e-06 -▁Verbrechens 3.9248060204402838e-06 -▁Verwirrung 3.9248060204402838e-06 -▁breed 3.9248060204402838e-06 -▁celeb 3.9248060204402838e-06 -▁grundsätzliche 3.9248060204402838e-06 -▁idyllic 3.9248060204402838e-06 -▁menschen 3.9248060204402838e-06 -▁nette 3.9248060204402838e-06 -▁wander 3.9248060204402838e-06 -afel 3.9210175976406309e-06 -assis 3.9210175976406309e-06 -bum 3.9210175976406309e-06 -medien 3.9210175976406309e-06 -schaff 3.9210175976406309e-06 -ufung 3.9210175976406309e-06 -▁AGB 3.9210175976406309e-06 -▁Einwanderungspolitik 3.9210175976406309e-06 -▁Jos 3.9210175976406309e-06 -▁Mio 3.9210175976406309e-06 -▁Philosoph 3.9210175976406309e-06 -▁Tode 3.9210175976406309e-06 -▁Toy 3.9210175976406309e-06 -▁Weing 3.9210175976406309e-06 -▁caution 3.9210175976406309e-06 -▁high-level 3.9210175976406309e-06 -▁innovativer 3.9210175976406309e-06 -▁instrumental 3.9210175976406309e-06 -▁pued 3.9210175976406309e-06 -▁räum 3.9210175976406309e-06 -▁suppl 3.9210175976406309e-06 -Guide 3.917229174840978e-06 -dra 3.917229174840978e-06 -ellschaft 3.917229174840978e-06 -entum 3.917229174840978e-06 -hrten 3.917229174840978e-06 -idy 3.917229174840978e-06 -ifor 3.917229174840978e-06 -isciplinary 3.917229174840978e-06 -istically 3.917229174840978e-06 -prüf 3.917229174840978e-06 -pton 3.917229174840978e-06 -stanz 3.917229174840978e-06 -üte 3.917229174840978e-06 -▁Applikation 3.917229174840978e-06 -▁Caf 3.917229174840978e-06 -▁Dame 3.917229174840978e-06 -▁Ever 3.917229174840978e-06 -▁Horm 3.917229174840978e-06 -▁Komposition 3.917229174840978e-06 -▁SERV 3.917229174840978e-06 -▁Umstruktur 3.917229174840978e-06 -▁Zuhause 3.917229174840978e-06 -▁assass 3.917229174840978e-06 -▁demonstr 3.917229174840978e-06 -▁disappointment 3.917229174840978e-06 -▁geäußer 3.917229174840978e-06 -▁intermediate 3.917229174840978e-06 -▁mead 3.917229174840978e-06 -▁oils 3.917229174840978e-06 -▁overs 3.917229174840978e-06 -▁prizes 3.917229174840978e-06 -▁subway 3.917229174840978e-06 -ossa 3.9134407520413252e-06 -piele 3.9134407520413252e-06 -▁Anne 3.9134407520413252e-06 -▁Beil 3.9134407520413252e-06 -▁Haag 3.9134407520413252e-06 -▁Kund 3.9134407520413252e-06 -▁Mitteilungen 3.9134407520413252e-06 -▁Turbo 3.9134407520413252e-06 -▁Vizepräsidentin 3.9134407520413252e-06 -▁biofuels 3.9134407520413252e-06 -▁deterioration 3.9134407520413252e-06 -▁drain 3.9134407520413252e-06 -▁fuck 3.9134407520413252e-06 -▁reflecting 3.9134407520413252e-06 -▁umliegenden 3.9134407520413252e-06 -▁zitiere 3.9134407520413252e-06 -▁ökonomische 3.9134407520413252e-06 -ademie 3.9096523292416723e-06 -lac 3.9096523292416723e-06 -via 3.9096523292416723e-06 -▁Apoll 3.9096523292416723e-06 -▁Messung 3.9096523292416723e-06 -▁deposits 3.9096523292416723e-06 -▁ernsthaften 3.9096523292416723e-06 -▁folgender 3.9096523292416723e-06 -▁freiwilligen 3.9096523292416723e-06 -▁ink 3.9096523292416723e-06 -▁jenem 3.9096523292416723e-06 -▁mutually 3.9096523292416723e-06 -▁objection 3.9096523292416723e-06 -▁violated 3.9096523292416723e-06 -arest 3.9058639064420194e-06 -gefügt 3.9058639064420194e-06 -iox 3.9058639064420194e-06 -oons 3.9058639064420194e-06 -plantation 3.9058639064420194e-06 -seit 3.9058639064420194e-06 -teilten 3.9058639064420194e-06 -▁AK 3.9058639064420194e-06 -▁Capt 3.9058639064420194e-06 -▁Dalmat 3.9058639064420194e-06 -▁Stücke 3.9058639064420194e-06 -▁Wolken 3.9058639064420194e-06 -▁accessing 3.9058639064420194e-06 -▁benachbarten 3.9058639064420194e-06 -▁capitalist 3.9058639064420194e-06 -▁deutlichen 3.9058639064420194e-06 -▁emot 3.9058639064420194e-06 -▁fairs 3.9058639064420194e-06 -▁nest 3.9058639064420194e-06 -▁self-employed 3.9058639064420194e-06 --month 3.9020754836423673e-06 -cision 3.9020754836423673e-06 -erverein 3.9020754836423673e-06 -ipeline 3.9020754836423673e-06 -nels 3.9020754836423673e-06 -udget 3.9020754836423673e-06 -▁Accordingly 3.9020754836423673e-06 -▁Friedhof 3.9020754836423673e-06 -▁Kalifornien 3.9020754836423673e-06 -▁Mahl 3.9020754836423673e-06 -▁Quant 3.9020754836423673e-06 -▁Schwach 3.9020754836423673e-06 -▁aussch 3.9020754836423673e-06 -▁einschränken 3.9020754836423673e-06 -▁generic 3.9020754836423673e-06 -▁harmonise 3.9020754836423673e-06 -▁nötige 3.9020754836423673e-06 -▁understands 3.9020754836423673e-06 -▁verletzen 3.9020754836423673e-06 -register 3.8982870608427144e-06 -▁Brig 3.8982870608427144e-06 -▁Castel 3.8982870608427144e-06 -▁Elba 3.8982870608427144e-06 -▁Haushaltsmittel 3.8982870608427144e-06 -▁Various 3.8982870608427144e-06 -▁Waffe 3.8982870608427144e-06 -▁Wäre 3.8982870608427144e-06 -▁Yam 3.8982870608427144e-06 -▁appropriately 3.8982870608427144e-06 -▁fertigen 3.8982870608427144e-06 -▁kingdom 3.8982870608427144e-06 -▁ml 3.8982870608427144e-06 -▁penetr 3.8982870608427144e-06 -▁regulating 3.8982870608427144e-06 -▁urged 3.8982870608427144e-06 -▁wound 3.8982870608427144e-06 -LD 3.8944986380430615e-06 -eatr 3.8944986380430615e-06 -krip 3.8944986380430615e-06 -phas 3.8944986380430615e-06 -ständigen 3.8944986380430615e-06 -ís 3.8944986380430615e-06 -▁Mother 3.8944986380430615e-06 -▁Sammel 3.8944986380430615e-06 -▁Sebastian 3.8944986380430615e-06 -▁angen 3.8944986380430615e-06 -▁detailliert 3.8944986380430615e-06 -▁distortion 3.8944986380430615e-06 -▁entails 3.8944986380430615e-06 -▁erect 3.8944986380430615e-06 -▁moon 3.8944986380430615e-06 -▁robot 3.8944986380430615e-06 -▁slot 3.8944986380430615e-06 -▁surfing 3.8944986380430615e-06 -▁unterzogen 3.8944986380430615e-06 -▁vertrete 3.8944986380430615e-06 -▁ya 3.8944986380430615e-06 --Lösung 3.8907102152434087e-06 -Disk 3.8907102152434087e-06 -ensus 3.8907102152434087e-06 -ikum 3.8907102152434087e-06 -ilateral 3.8907102152434087e-06 -ogl 3.8907102152434087e-06 -schließung 3.8907102152434087e-06 -▁123 3.8907102152434087e-06 -▁Aviation 3.8907102152434087e-06 -▁Fitnessraum 3.8907102152434087e-06 -▁Lehne 3.8907102152434087e-06 -▁Mexican 3.8907102152434087e-06 -▁aufrichtig 3.8907102152434087e-06 -▁außergewöhnlich 3.8907102152434087e-06 -▁erschwert 3.8907102152434087e-06 -▁interc 3.8907102152434087e-06 -▁mitunter 3.8907102152434087e-06 -▁sexy 3.8907102152434087e-06 -▁ähnlicher 3.8907102152434087e-06 --Kr 3.8869217924437558e-06 -conf 3.8869217924437558e-06 -orenen 3.8869217924437558e-06 -werden 3.8869217924437558e-06 -▁Cus 3.8869217924437558e-06 -▁Laus 3.8869217924437558e-06 -▁Verschiebung 3.8869217924437558e-06 -▁Wies 3.8869217924437558e-06 -▁anonymous 3.8869217924437558e-06 -▁beginners 3.8869217924437558e-06 -▁counting 3.8869217924437558e-06 -▁elektronischer 3.8869217924437558e-06 -▁expresses 3.8869217924437558e-06 -▁fitting 3.8869217924437558e-06 -▁ni 3.8869217924437558e-06 -▁offensive 3.8869217924437558e-06 -▁veränderte 3.8869217924437558e-06 -▁zufällig 3.8869217924437558e-06 -600 3.8831333696441029e-06 -bogen 3.8831333696441029e-06 -ehörde 3.8831333696441029e-06 -haltungs 3.8831333696441029e-06 -teenth 3.8831333696441029e-06 -äufers 3.8831333696441029e-06 -▁Bezüglich 3.8831333696441029e-06 -▁Dampf 3.8831333696441029e-06 -▁Fluggesellschaft 3.8831333696441029e-06 -▁Orchester 3.8831333696441029e-06 -▁Throughout 3.8831333696441029e-06 -▁beeindruckt 3.8831333696441029e-06 -▁contamination 3.8831333696441029e-06 -▁fashionable 3.8831333696441029e-06 -▁genügen 3.8831333696441029e-06 -▁geänderten 3.8831333696441029e-06 -▁liberalen 3.8831333696441029e-06 -▁lö 3.8831333696441029e-06 -▁werdet 3.8831333696441029e-06 --Ap 3.87934494684445e-06 --Schn 3.87934494684445e-06 --Vertrags 3.87934494684445e-06 -die 3.87934494684445e-06 -odies 3.87934494684445e-06 -onische 3.87934494684445e-06 -▁Asylbewerber 3.87934494684445e-06 -▁Boy 3.87934494684445e-06 -▁Bungalows 3.87934494684445e-06 -▁Haf 3.87934494684445e-06 -▁KL 3.87934494684445e-06 -▁Kinderbetreuung 3.87934494684445e-06 -▁Latein 3.87934494684445e-06 -▁Quartet 3.87934494684445e-06 -▁Rasse 3.87934494684445e-06 -▁Underground 3.87934494684445e-06 -▁appearing 3.87934494684445e-06 -▁authoritarian 3.87934494684445e-06 -▁centr 3.87934494684445e-06 -▁donations 3.87934494684445e-06 -▁first-class 3.87934494684445e-06 -▁lacks 3.87934494684445e-06 -▁manages 3.87934494684445e-06 -▁physikal 3.87934494684445e-06 -.07 3.8755565240447971e-06 -alo 3.8755565240447971e-06 -bewertung 3.8755565240447971e-06 -cycl 3.8755565240447971e-06 -eilt 3.8755565240447971e-06 -gesehen 3.8755565240447971e-06 -rages 3.8755565240447971e-06 -riff 3.8755565240447971e-06 -spolitische 3.8755565240447971e-06 -▁1958 3.8755565240447971e-06 -▁1963 3.8755565240447971e-06 -▁3-Sterne-Hotel 3.8755565240447971e-06 -▁Airconditioning 3.8755565240447971e-06 -▁Beleg 3.8755565240447971e-06 -▁Hugh 3.8755565240447971e-06 -▁Lan 3.8755565240447971e-06 -▁Pariser 3.8755565240447971e-06 -▁Skulpt 3.8755565240447971e-06 -▁Tuscan 3.8755565240447971e-06 -▁Vertreterin 3.8755565240447971e-06 -▁Zulass 3.8755565240447971e-06 -▁begeistern 3.8755565240447971e-06 -▁consciousness 3.8755565240447971e-06 -▁entzieht 3.8755565240447971e-06 -▁grundsätz 3.8755565240447971e-06 -▁maß 3.8755565240447971e-06 -▁realization 3.8755565240447971e-06 -▁resistant 3.8755565240447971e-06 -▁therapeutic 3.8755565240447971e-06 -▁ungarische 3.8755565240447971e-06 -▁équ 3.8755565240447971e-06 -.e 3.871768101245145e-06 -zepten 3.871768101245145e-06 -▁Adopt 3.871768101245145e-06 -▁Apost 3.871768101245145e-06 -▁Beschleunigung 3.871768101245145e-06 -▁Blatt 3.871768101245145e-06 -▁Botschafter 3.871768101245145e-06 -▁Experiment 3.871768101245145e-06 -▁Sack 3.871768101245145e-06 -▁Sichtweise 3.871768101245145e-06 -▁Ziffern 3.871768101245145e-06 -▁alqu 3.871768101245145e-06 -▁e-c 3.871768101245145e-06 -▁europa 3.871768101245145e-06 -▁hebt 3.871768101245145e-06 -▁laufende 3.871768101245145e-06 -▁niedrigeren 3.871768101245145e-06 -▁portugiesische 3.871768101245145e-06 -▁scept 3.871768101245145e-06 -▁showers 3.871768101245145e-06 -▁sinnvolle 3.871768101245145e-06 -▁wholeheartedly 3.871768101245145e-06 -▁yours 3.871768101245145e-06 -▁zumeist 3.871768101245145e-06 -▁zusammenhän 3.871768101245145e-06 -aar 3.8679796784454921e-06 -belastung 3.8679796784454921e-06 -cin 3.8679796784454921e-06 -itution 3.8679796784454921e-06 -▁Berecht 3.8679796784454921e-06 -▁Dup 3.8679796784454921e-06 -▁Gemeinschaftsrechts 3.8679796784454921e-06 -▁Ing 3.8679796784454921e-06 -▁Ohren 3.8679796784454921e-06 -▁Taj 3.8679796784454921e-06 -▁bil 3.8679796784454921e-06 -▁enthusiasts 3.8679796784454921e-06 -▁ignor 3.8679796784454921e-06 -▁mußte 3.8679796784454921e-06 -▁palm 3.8679796784454921e-06 -▁static 3.8679796784454921e-06 -▁tournaments 3.8679796784454921e-06 -▁traces 3.8679796784454921e-06 --Agent 3.8641912556458393e-06 -achth 3.8641912556458393e-06 -ürgen 3.8641912556458393e-06 -▁Differenzen 3.8641912556458393e-06 -▁Entscheidungsfindung 3.8641912556458393e-06 -▁Exil 3.8641912556458393e-06 -▁Guar 3.8641912556458393e-06 -▁Informationsaustausch 3.8641912556458393e-06 -▁Millenniums 3.8641912556458393e-06 -▁disappointing 3.8641912556458393e-06 -▁emails 3.8641912556458393e-06 -▁ethischen 3.8641912556458393e-06 -▁feinen 3.8641912556458393e-06 -▁mixing 3.8641912556458393e-06 -▁namentlich 3.8641912556458393e-06 -▁scar 3.8641912556458393e-06 -▁zertifiziert 3.8641912556458393e-06 -LY 3.8604028328461864e-06 -fläch 3.8604028328461864e-06 -gency 3.8604028328461864e-06 -getreten 3.8604028328461864e-06 -grams 3.8604028328461864e-06 -käm 3.8604028328461864e-06 -ähre 3.8604028328461864e-06 -▁Barón 3.8604028328461864e-06 -▁Cann 3.8604028328461864e-06 -▁Nationalen 3.8604028328461864e-06 -▁Summ 3.8604028328461864e-06 -▁befaßt 3.8604028328461864e-06 -▁bewährten 3.8604028328461864e-06 -▁headed 3.8604028328461864e-06 -▁notable 3.8604028328461864e-06 -▁optimize 3.8604028328461864e-06 -▁schalten 3.8604028328461864e-06 -▁theater 3.8604028328461864e-06 -▁yea 3.8604028328461864e-06 -hesive 3.8566144100465335e-06 -iani 3.8566144100465335e-06 -lack 3.8566144100465335e-06 -ografie 3.8566144100465335e-06 -▁15.00 3.8566144100465335e-06 -▁Auschecken 3.8566144100465335e-06 -▁Bedauer 3.8566144100465335e-06 -▁Drink 3.8566144100465335e-06 -▁Jeff 3.8566144100465335e-06 -▁Kalten 3.8566144100465335e-06 -▁Konkur 3.8566144100465335e-06 -▁aft 3.8566144100465335e-06 -▁fauna 3.8566144100465335e-06 --Test 3.8528259872468806e-06 -ahlen 3.8528259872468806e-06 -itra 3.8528259872468806e-06 -organe 3.8528259872468806e-06 -reinigung 3.8528259872468806e-06 -trib 3.8528259872468806e-06 -usen 3.8528259872468806e-06 -ÜR 3.8528259872468806e-06 -▁Capp 3.8528259872468806e-06 -▁Disco 3.8528259872468806e-06 -▁Europäern 3.8528259872468806e-06 -▁Evangel 3.8528259872468806e-06 -▁Matthias 3.8528259872468806e-06 -▁Neuf 3.8528259872468806e-06 -▁Orch 3.8528259872468806e-06 -▁Wasserk 3.8528259872468806e-06 -▁Zugeständ 3.8528259872468806e-06 -▁climb 3.8528259872468806e-06 -▁fügen 3.8528259872468806e-06 -▁schott 3.8528259872468806e-06 -athl 3.8490375644472277e-06 -ecuted 3.8490375644472277e-06 -kanal 3.8490375644472277e-06 -kaufen 3.8490375644472277e-06 -modus 3.8490375644472277e-06 -rench 3.8490375644472277e-06 -usive 3.8490375644472277e-06 -▁Arabia 3.8490375644472277e-06 -▁Britt 3.8490375644472277e-06 -▁Louvre 3.8490375644472277e-06 -▁Maßstäbe 3.8490375644472277e-06 -▁Mé 3.8490375644472277e-06 -▁Pfeil 3.8490375644472277e-06 -▁Preises 3.8490375644472277e-06 -▁Rüstungs 3.8490375644472277e-06 -▁Zehn 3.8490375644472277e-06 -▁anspruchsvoll 3.8490375644472277e-06 -▁champion 3.8490375644472277e-06 -▁ermordet 3.8490375644472277e-06 -▁low-c 3.8490375644472277e-06 -▁zugenommen 3.8490375644472277e-06 -▁zuzustimmen 3.8490375644472277e-06 -▁½ 3.8490375644472277e-06 --Konferenz 3.8452491416475756e-06 -UST 3.8452491416475756e-06 -hart 3.8452491416475756e-06 -inski 3.8452491416475756e-06 -▁4.1 3.8452491416475756e-06 -▁4.5 3.8452491416475756e-06 -▁Clin 3.8452491416475756e-06 -▁Köst 3.8452491416475756e-06 -▁Volkswirtschaft 3.8452491416475756e-06 -▁anführen 3.8452491416475756e-06 -▁eingeschlossen 3.8452491416475756e-06 -▁exhibitors 3.8452491416475756e-06 -▁gefl 3.8452491416475756e-06 -▁gekürzt 3.8452491416475756e-06 -▁hindern 3.8452491416475756e-06 -▁judiciary 3.8452491416475756e-06 -▁pdf 3.8452491416475756e-06 -▁tall 3.8452491416475756e-06 -OF 3.8414607188479228e-06 -ieber 3.8414607188479228e-06 -kra 3.8414607188479228e-06 -ophone 3.8414607188479228e-06 -rect 3.8414607188479228e-06 -▁1949 3.8414607188479228e-06 -▁Adv 3.8414607188479228e-06 -▁Besten 3.8414607188479228e-06 -▁Cuban 3.8414607188479228e-06 -▁DivX 3.8414607188479228e-06 -▁Rückzug 3.8414607188479228e-06 -▁Schr 3.8414607188479228e-06 -▁Sogar 3.8414607188479228e-06 -▁Steine 3.8414607188479228e-06 -▁Style 3.8414607188479228e-06 -▁auszuarbeiten 3.8414607188479228e-06 -▁dringende 3.8414607188479228e-06 -▁echt 3.8414607188479228e-06 -▁einsehen 3.8414607188479228e-06 -▁einseitig 3.8414607188479228e-06 -▁geografischen 3.8414607188479228e-06 -▁geringeren 3.8414607188479228e-06 -▁locals 3.8414607188479228e-06 -▁luxuri 3.8414607188479228e-06 -▁ruin 3.8414607188479228e-06 -▁traveling 3.8414607188479228e-06 --Install 3.83767229604827e-06 -auftrag 3.83767229604827e-06 -serklärung 3.83767229604827e-06 -▁1939 3.83767229604827e-06 -▁FIFA 3.83767229604827e-06 -▁Medikamenten 3.83767229604827e-06 -▁Präzision 3.83767229604827e-06 -▁Stehende 3.83767229604827e-06 -▁Vorteilen 3.83767229604827e-06 -▁chapters 3.83767229604827e-06 -▁cocktails 3.83767229604827e-06 -▁dispatch 3.83767229604827e-06 -▁ecommerce 3.83767229604827e-06 -▁freiz 3.83767229604827e-06 -▁gemisch 3.83767229604827e-06 -▁grade 3.83767229604827e-06 -▁palästinensische 3.83767229604827e-06 -▁verank 3.83767229604827e-06 -▁übereinst 3.83767229604827e-06 -abu 3.833883873248617e-06 -atorische 3.833883873248617e-06 -chair 3.833883873248617e-06 -elsohne 3.833883873248617e-06 -ungal 3.833883873248617e-06 -▁Hyde 3.833883873248617e-06 -▁Imag 3.833883873248617e-06 -▁Komplett 3.833883873248617e-06 -▁Mehrzahl 3.833883873248617e-06 -▁SIE 3.833883873248617e-06 -▁Schnittstellen 3.833883873248617e-06 -▁Sv 3.833883873248617e-06 -▁Tram 3.833883873248617e-06 -▁amaz 3.833883873248617e-06 -▁awaits 3.833883873248617e-06 -▁beschriebenen 3.833883873248617e-06 -▁childhood 3.833883873248617e-06 -▁durchge 3.833883873248617e-06 -▁geschützten 3.833883873248617e-06 -▁gesund 3.833883873248617e-06 -▁irresponsible 3.833883873248617e-06 -▁pubs 3.833883873248617e-06 -▁sentences 3.833883873248617e-06 -aufenthalt 3.8300954504489641e-06 -ciliation 3.8300954504489641e-06 -eiteten 3.8300954504489641e-06 -elin 3.8300954504489641e-06 -entwicklungen 3.8300954504489641e-06 -esy 3.8300954504489641e-06 -farbe 3.8300954504489641e-06 -inische 3.8300954504489641e-06 -liegenden 3.8300954504489641e-06 -▁Boston 3.8300954504489641e-06 -▁Entscheid 3.8300954504489641e-06 -▁Honor 3.8300954504489641e-06 -▁Novotel 3.8300954504489641e-06 -▁adapting 3.8300954504489641e-06 -▁conceal 3.8300954504489641e-06 -▁disability 3.8300954504489641e-06 -▁internally 3.8300954504489641e-06 -▁poses 3.8300954504489641e-06 -▁unclear 3.8300954504489641e-06 -beschäftigung 3.8263070276493112e-06 -edite 3.8263070276493112e-06 -kenn 3.8263070276493112e-06 -ograp 3.8263070276493112e-06 -seher 3.8263070276493112e-06 -stext 3.8263070276493112e-06 -▁Ausflugs 3.8263070276493112e-06 -▁Etwa 3.8263070276493112e-06 -▁Ferrero-Waldner 3.8263070276493112e-06 -▁beta 3.8263070276493112e-06 -▁fla 3.8263070276493112e-06 -▁leid 3.8263070276493112e-06 -▁organisms 3.8263070276493112e-06 -▁stimulating 3.8263070276493112e-06 -egr 3.8225186048496583e-06 -praxis 3.8225186048496583e-06 -rekt 3.8225186048496583e-06 -yss 3.8225186048496583e-06 -ähle 3.8225186048496583e-06 -ätzungen 3.8225186048496583e-06 -▁3G 3.8225186048496583e-06 -▁Allen 3.8225186048496583e-06 -▁Auflage 3.8225186048496583e-06 -▁Geg 3.8225186048496583e-06 -▁Mauern 3.8225186048496583e-06 -▁Prozessen 3.8225186048496583e-06 -▁Tabl 3.8225186048496583e-06 -▁Tin 3.8225186048496583e-06 -▁acute 3.8225186048496583e-06 -▁ausländ 3.8225186048496583e-06 -▁beispiel 3.8225186048496583e-06 -▁beschädigt 3.8225186048496583e-06 -▁controvers 3.8225186048496583e-06 -▁identifiziert 3.8225186048496583e-06 -▁nous 3.8225186048496583e-06 -▁straightforward 3.8225186048496583e-06 -epper 3.8187301820500054e-06 -fu 3.8187301820500054e-06 -pler 3.8187301820500054e-06 -rand 3.8187301820500054e-06 -▁Amsterdamer 3.8187301820500054e-06 -▁Beein 3.8187301820500054e-06 -▁Concert 3.8187301820500054e-06 -▁Jeg 3.8187301820500054e-06 -▁Sozial- 3.8187301820500054e-06 -▁Submitted 3.8187301820500054e-06 -▁afghan 3.8187301820500054e-06 -▁dokumentiert 3.8187301820500054e-06 -▁doppelte 3.8187301820500054e-06 -▁empfehlenswert 3.8187301820500054e-06 -▁professionalism 3.8187301820500054e-06 -▁transposition 3.8187301820500054e-06 -▁vertraulich 3.8187301820500054e-06 -IDE 3.8149417592503534e-06 -haftigkeit 3.8149417592503534e-06 -inity 3.8149417592503534e-06 -istance 3.8149417592503534e-06 -kular 3.8149417592503534e-06 -papier 3.8149417592503534e-06 -ém 3.8149417592503534e-06 -▁Address 3.8149417592503534e-06 -▁Arzneimitteln 3.8149417592503534e-06 -▁Asp 3.8149417592503534e-06 -▁Hals 3.8149417592503534e-06 -▁Knoten 3.8149417592503534e-06 -▁Liberties 3.8149417592503534e-06 -▁acquaint 3.8149417592503534e-06 -▁bail 3.8149417592503534e-06 -▁encouragement 3.8149417592503534e-06 -▁influential 3.8149417592503534e-06 -▁morg 3.8149417592503534e-06 -▁offline 3.8149417592503534e-06 -▁weitergeleitet 3.8149417592503534e-06 -alone 3.8111533364507e-06 -atri 3.8111533364507e-06 -faktoren 3.8111533364507e-06 -▁Agrars 3.8111533364507e-06 -▁Communities 3.8111533364507e-06 -▁Gaulle 3.8111533364507e-06 -▁Investitionsbank 3.8111533364507e-06 -▁Lücken 3.8111533364507e-06 -▁Nuclear 3.8111533364507e-06 -▁Schuhe 3.8111533364507e-06 -▁Sicherheitsrat 3.8111533364507e-06 -▁Verschmutzung 3.8111533364507e-06 -▁bab 3.8111533364507e-06 -▁drew 3.8111533364507e-06 -▁eingebettet 3.8111533364507e-06 -▁illegaler 3.8111533364507e-06 -▁matur 3.8111533364507e-06 -▁moralischen 3.8111533364507e-06 -▁pending 3.8111533364507e-06 -▁proprietary 3.8111533364507e-06 -▁va 3.8111533364507e-06 -▁wechs 3.8111533364507e-06 -▁zuverläss 3.8111533364507e-06 -fürst 3.8073649136510476e-06 -ioned 3.8073649136510476e-06 -ollt 3.8073649136510476e-06 -promenade 3.8073649136510476e-06 -rs 3.8073649136510476e-06 -seg 3.8073649136510476e-06 -▁Ausländer 3.8073649136510476e-06 -▁Kais 3.8073649136510476e-06 -▁Malaria 3.8073649136510476e-06 -▁Spekt 3.8073649136510476e-06 -▁Steve 3.8073649136510476e-06 -▁Unic 3.8073649136510476e-06 -▁dictator 3.8073649136510476e-06 -▁drift 3.8073649136510476e-06 -▁judged 3.8073649136510476e-06 -▁peripheral 3.8073649136510476e-06 -▁probieren 3.8073649136510476e-06 -▁sid 3.8073649136510476e-06 -▁suicide 3.8073649136510476e-06 -▁Å 3.8073649136510476e-06 -afi 3.8035764908513947e-06 -ateien 3.8035764908513947e-06 -geordnete 3.8035764908513947e-06 -ommod 3.8035764908513947e-06 -pit 3.8035764908513947e-06 -sektors 3.8035764908513947e-06 -ulichkeit 3.8035764908513947e-06 -▁Biot 3.8035764908513947e-06 -▁Carlo 3.8035764908513947e-06 -▁Carn 3.8035764908513947e-06 -▁Goals 3.8035764908513947e-06 -▁Maustaste 3.8035764908513947e-06 -▁Philharmon 3.8035764908513947e-06 -▁Tell 3.8035764908513947e-06 -▁kreativen 3.8035764908513947e-06 -▁kristall 3.8035764908513947e-06 -▁mangelt 3.8035764908513947e-06 -▁nachzukommen 3.8035764908513947e-06 -▁onwards 3.8035764908513947e-06 -▁sched 3.8035764908513947e-06 -▁succ 3.8035764908513947e-06 -▁trocken 3.8035764908513947e-06 -KO 3.7997880680517418e-06 -ria 3.7997880680517418e-06 -▁Amazon 3.7997880680517418e-06 -▁Moon 3.7997880680517418e-06 -▁Schöne 3.7997880680517418e-06 -▁Trip 3.7997880680517418e-06 -▁applicants 3.7997880680517418e-06 -▁einmaligen 3.7997880680517418e-06 -▁romantischen 3.7997880680517418e-06 -▁ville 3.7997880680517418e-06 -▁vorab 3.7997880680517418e-06 -▁4-Sterne 3.7959996452520889e-06 -▁Delph 3.7959996452520889e-06 -▁Diplomatie 3.7959996452520889e-06 -▁E-Commerce 3.7959996452520889e-06 -▁Gründungs 3.7959996452520889e-06 -▁Mannheim 3.7959996452520889e-06 -▁Termini 3.7959996452520889e-06 -▁VII 3.7959996452520889e-06 -▁Vorgänge 3.7959996452520889e-06 -▁cad 3.7959996452520889e-06 -▁dezentral 3.7959996452520889e-06 -▁elimin 3.7959996452520889e-06 -▁fantast 3.7959996452520889e-06 -▁umzu 3.7959996452520889e-06 -As 3.7922112224524364e-06 -gment 3.7922112224524364e-06 -jour 3.7922112224524364e-06 -regel 3.7922112224524364e-06 -schild 3.7922112224524364e-06 -▁11th 3.7922112224524364e-06 -▁Böge 3.7922112224524364e-06 -▁Darm 3.7922112224524364e-06 -▁Oscar 3.7922112224524364e-06 -▁Pensionen 3.7922112224524364e-06 -▁benachteiligten 3.7922112224524364e-06 -▁geehrten 3.7922112224524364e-06 -▁gescheitert 3.7922112224524364e-06 -▁gewohnt 3.7922112224524364e-06 -▁smoothly 3.7922112224524364e-06 -▁unabhängiger 3.7922112224524364e-06 -MI 3.7884227996527835e-06 -arin 3.7884227996527835e-06 -isan 3.7884227996527835e-06 -äuse 3.7884227996527835e-06 -örm 3.7884227996527835e-06 -▁Erfordernisse 3.7884227996527835e-06 -▁Handels- 3.7884227996527835e-06 -▁Kurt 3.7884227996527835e-06 -▁MSN 3.7884227996527835e-06 -▁Opport 3.7884227996527835e-06 -▁Pok 3.7884227996527835e-06 -▁Surround 3.7884227996527835e-06 -▁achie 3.7884227996527835e-06 -▁begriffen 3.7884227996527835e-06 -▁beliebte 3.7884227996527835e-06 -▁bestimmtes 3.7884227996527835e-06 -▁columns 3.7884227996527835e-06 -▁pag 3.7884227996527835e-06 -▁parish 3.7884227996527835e-06 -▁repräsentiert 3.7884227996527835e-06 -▁slowen 3.7884227996527835e-06 -▁zuversichtlich 3.7884227996527835e-06 -IPS 3.7846343768531307e-06 -LC 3.7846343768531307e-06 -ciation 3.7846343768531307e-06 -cio 3.7846343768531307e-06 -essp 3.7846343768531307e-06 -oppy 3.7846343768531307e-06 -pi 3.7846343768531307e-06 -öpfer 3.7846343768531307e-06 -▁1990er 3.7846343768531307e-06 -▁Amal 3.7846343768531307e-06 -▁Anschein 3.7846343768531307e-06 -▁Create 3.7846343768531307e-06 -▁Ergänz 3.7846343768531307e-06 -▁Finanzdienstleistungen 3.7846343768531307e-06 -▁GA 3.7846343768531307e-06 -▁Gewiss 3.7846343768531307e-06 -▁Koalition 3.7846343768531307e-06 -▁Magazin 3.7846343768531307e-06 -▁NCC-1701 3.7846343768531307e-06 -▁Registrierungs 3.7846343768531307e-06 -▁Tageszeit 3.7846343768531307e-06 -▁Widerstands 3.7846343768531307e-06 -▁annimmt 3.7846343768531307e-06 -▁balconies 3.7846343768531307e-06 -▁celebrations 3.7846343768531307e-06 -▁elekt 3.7846343768531307e-06 -▁erricht 3.7846343768531307e-06 -▁geringfüg 3.7846343768531307e-06 -▁trucks 3.7846343768531307e-06 --Fil 3.7808459540534778e-06 -amma 3.7808459540534778e-06 -chtigkeit 3.7808459540534778e-06 -flut 3.7808459540534778e-06 -grem 3.7808459540534778e-06 -mand 3.7808459540534778e-06 -ässern 3.7808459540534778e-06 -▁Begriffs 3.7808459540534778e-06 -▁Brenner 3.7808459540534778e-06 -▁Commerce 3.7808459540534778e-06 -▁Einverständnis 3.7808459540534778e-06 -▁Erscheinung 3.7808459540534778e-06 -▁Herunterladen 3.7808459540534778e-06 -▁Mobiltelef 3.7808459540534778e-06 -▁Qualifikation 3.7808459540534778e-06 -▁Stable 3.7808459540534778e-06 -▁Swimming 3.7808459540534778e-06 -▁Tours 3.7808459540534778e-06 -▁absent 3.7808459540534778e-06 -▁approaching 3.7808459540534778e-06 -▁enfor 3.7808459540534778e-06 -▁guys 3.7808459540534778e-06 -▁transportiert 3.7808459540534778e-06 -▁unav 3.7808459540534778e-06 -allo 3.7770575312538253e-06 -eca 3.7770575312538253e-06 -gesetzes 3.7770575312538253e-06 -igan 3.7770575312538253e-06 -overy 3.7770575312538253e-06 -problemen 3.7770575312538253e-06 -tress 3.7770575312538253e-06 -zeigten 3.7770575312538253e-06 -zielen 3.7770575312538253e-06 -η 3.7770575312538253e-06 -▁HE 3.7770575312538253e-06 -▁Landhaus 3.7770575312538253e-06 -▁Nürnberg 3.7770575312538253e-06 -▁Ortes 3.7770575312538253e-06 -▁Pause 3.7770575312538253e-06 -▁Russen 3.7770575312538253e-06 -▁Salzkammer 3.7770575312538253e-06 -▁alla 3.7770575312538253e-06 -▁angemeldet 3.7770575312538253e-06 -▁beides 3.7770575312538253e-06 -▁enthusiastic 3.7770575312538253e-06 -▁impro 3.7770575312538253e-06 -▁nachhaltiges 3.7770575312538253e-06 -▁poster 3.7770575312538253e-06 -▁squar 3.7770575312538253e-06 -▁unnötig 3.7770575312538253e-06 -▁volcan 3.7770575312538253e-06 -comm 3.7732691084541724e-06 -echnologien 3.7732691084541724e-06 -gram 3.7732691084541724e-06 -unnen 3.7732691084541724e-06 -zuwirken 3.7732691084541724e-06 -▁Ambassador 3.7732691084541724e-06 -▁Champion 3.7732691084541724e-06 -▁Companies 3.7732691084541724e-06 -▁Malware 3.7732691084541724e-06 -▁Versuchen 3.7732691084541724e-06 -▁adhere 3.7732691084541724e-06 -▁alb 3.7732691084541724e-06 -▁bee 3.7732691084541724e-06 -▁dreißig 3.7732691084541724e-06 -▁emphasises 3.7732691084541724e-06 -▁empire 3.7732691084541724e-06 -▁opposing 3.7732691084541724e-06 -▁pisc 3.7732691084541724e-06 -▁potenzielle 3.7732691084541724e-06 -▁seasons 3.7732691084541724e-06 -▁singing 3.7732691084541724e-06 -▁zugeben 3.7732691084541724e-06 -▁zusammengearbeitet 3.7732691084541724e-06 -alien 3.7694806856545195e-06 -bauern 3.7694806856545195e-06 -υ 3.7694806856545195e-06 -▁10,000 3.7694806856545195e-06 -▁90er 3.7694806856545195e-06 -▁Andernfalls 3.7694806856545195e-06 -▁Crespo 3.7694806856545195e-06 -▁Dos 3.7694806856545195e-06 -▁Nähr 3.7694806856545195e-06 -▁Pedro 3.7694806856545195e-06 -▁WW 3.7694806856545195e-06 -▁fluct 3.7694806856545195e-06 -elier 3.7656922628548666e-06 -frame 3.7656922628548666e-06 -level 3.7656922628548666e-06 -mag 3.7656922628548666e-06 -mobil 3.7656922628548666e-06 -▁Azur 3.7656922628548666e-06 -▁Kurden 3.7656922628548666e-06 -▁Nutzungsbedingungen 3.7656922628548666e-06 -▁Orl 3.7656922628548666e-06 -▁Siedlung 3.7656922628548666e-06 -▁Solid 3.7656922628548666e-06 -▁einzuräumen 3.7656922628548666e-06 -▁gegenseit 3.7656922628548666e-06 -▁impair 3.7656922628548666e-06 -▁komplexer 3.7656922628548666e-06 -▁radioakt 3.7656922628548666e-06 -▁spher 3.7656922628548666e-06 -▁Öffentliche 3.7656922628548666e-06 -atility 3.7619038400552141e-06 -ifffahrt 3.7619038400552141e-06 -lander 3.7619038400552141e-06 -oleum 3.7619038400552141e-06 -strand 3.7619038400552141e-06 -ußt 3.7619038400552141e-06 -zten 3.7619038400552141e-06 -▁Daß 3.7619038400552141e-06 -▁Fourthly 3.7619038400552141e-06 -▁García 3.7619038400552141e-06 -▁Iss 3.7619038400552141e-06 -▁K. 3.7619038400552141e-06 -▁Regisse 3.7619038400552141e-06 -▁Winkel 3.7619038400552141e-06 -▁arc 3.7619038400552141e-06 -▁archaeological 3.7619038400552141e-06 -▁charging 3.7619038400552141e-06 -▁concluding 3.7619038400552141e-06 -▁hem 3.7619038400552141e-06 -▁mediterranen 3.7619038400552141e-06 -▁reichhaltiges 3.7619038400552141e-06 -▁sud 3.7619038400552141e-06 --air 3.7581154172555613e-06 -idi 3.7581154172555613e-06 -modern 3.7581154172555613e-06 -Ž 3.7581154172555613e-06 -▁Bosch 3.7581154172555613e-06 -▁Challenge 3.7581154172555613e-06 -▁Geschäfts- 3.7581154172555613e-06 -▁INI 3.7581154172555613e-06 -▁Kombin 3.7581154172555613e-06 -▁Mitchell 3.7581154172555613e-06 -▁calculations 3.7581154172555613e-06 -▁catastroph 3.7581154172555613e-06 -▁konvert 3.7581154172555613e-06 -▁ritual 3.7581154172555613e-06 -▁teen 3.7581154172555613e-06 -▁verspricht 3.7581154172555613e-06 -900 3.7543269944559084e-06 -alla 3.7543269944559084e-06 -ecutor 3.7543269944559084e-06 -making 3.7543269944559084e-06 -ministers 3.7543269944559084e-06 -stücken 3.7543269944559084e-06 -vorgang 3.7543269944559084e-06 -▁Auffassungen 3.7543269944559084e-06 -▁Customers 3.7543269944559084e-06 -▁Organismen 3.7543269944559084e-06 -▁Poliz 3.7543269944559084e-06 -▁Sites 3.7543269944559084e-06 -▁hinten 3.7543269944559084e-06 -▁jeher 3.7543269944559084e-06 -▁mold 3.7543269944559084e-06 -▁pumps 3.7543269944559084e-06 -▁restoring 3.7543269944559084e-06 -▁seeds 3.7543269944559084e-06 -▁seller 3.7543269944559084e-06 -berater 3.7505385716562555e-06 -eses 3.7505385716562555e-06 -essa 3.7505385716562555e-06 -ilität 3.7505385716562555e-06 -ugend 3.7505385716562555e-06 -▁Fashion 3.7505385716562555e-06 -▁Linke 3.7505385716562555e-06 -▁Lower 3.7505385716562555e-06 -▁Manufact 3.7505385716562555e-06 -▁Mittele 3.7505385716562555e-06 -▁Neph 3.7505385716562555e-06 -▁Remember 3.7505385716562555e-06 -▁Sommers 3.7505385716562555e-06 -▁Verweis 3.7505385716562555e-06 -▁Whenever 3.7505385716562555e-06 -▁ausgegangen 3.7505385716562555e-06 -▁comprising 3.7505385716562555e-06 -▁erbracht 3.7505385716562555e-06 -▁horny 3.7505385716562555e-06 -▁individ 3.7505385716562555e-06 -▁invo 3.7505385716562555e-06 -▁methodology 3.7505385716562555e-06 -▁monopolies 3.7505385716562555e-06 -▁scenarios 3.7505385716562555e-06 -▁verfassungs 3.7505385716562555e-06 -forderung 3.746750148856603e-06 -icle 3.746750148856603e-06 -weiger 3.746750148856603e-06 -▁Bangemann 3.746750148856603e-06 -▁Fi 3.746750148856603e-06 -▁Gesichts 3.746750148856603e-06 -▁Richtlinienvorschlag 3.746750148856603e-06 -▁Sprung 3.746750148856603e-06 -▁Verschiedene 3.746750148856603e-06 -▁angenommene 3.746750148856603e-06 -▁confined 3.746750148856603e-06 -▁economists 3.746750148856603e-06 -▁elektrische 3.746750148856603e-06 -▁entstehenden 3.746750148856603e-06 -▁hop 3.746750148856603e-06 -▁injust 3.746750148856603e-06 -▁lächer 3.746750148856603e-06 -▁null 3.746750148856603e-06 -▁restra 3.746750148856603e-06 -▁seri 3.746750148856603e-06 -▁stilvolle 3.746750148856603e-06 -chner 3.74296172605695e-06 -feuer 3.74296172605695e-06 -izzer 3.74296172605695e-06 -scheiden 3.74296172605695e-06 -verfolgung 3.74296172605695e-06 -▁1933 3.74296172605695e-06 -▁Decken 3.74296172605695e-06 -▁Grunds 3.74296172605695e-06 -▁Prozentsatz 3.74296172605695e-06 -▁Schlussfolgerung 3.74296172605695e-06 -▁Story 3.74296172605695e-06 -▁condemnation 3.74296172605695e-06 -▁getät 3.74296172605695e-06 -▁resume 3.74296172605695e-06 -▁somebody 3.74296172605695e-06 --Benz 3.7391733032572972e-06 -ints 3.7391733032572972e-06 -äufen 3.7391733032572972e-06 -▁Deliver 3.7391733032572972e-06 -▁Hybrid 3.7391733032572972e-06 -▁Laufenden 3.7391733032572972e-06 -▁Mitgefühl 3.7391733032572972e-06 -▁NGL-Fraktion 3.7391733032572972e-06 -▁Namib 3.7391733032572972e-06 -▁Werner 3.7391733032572972e-06 -▁allgemeinem 3.7391733032572972e-06 -▁ausgeweitet 3.7391733032572972e-06 -▁demonstrieren 3.7391733032572972e-06 -▁refuge 3.7391733032572972e-06 -ammt 3.7353848804576443e-06 -arks 3.7353848804576443e-06 -ungsmöglichkeiten 3.7353848804576443e-06 -▁Assozi 3.7353848804576443e-06 -▁Automotive 3.7353848804576443e-06 -▁Calle 3.7353848804576443e-06 -▁Dereg 3.7353848804576443e-06 -▁Nevada 3.7353848804576443e-06 -▁Rentner 3.7353848804576443e-06 -▁eingesetzten 3.7353848804576443e-06 -▁günst 3.7353848804576443e-06 -▁interv 3.7353848804576443e-06 -▁learnt 3.7353848804576443e-06 -▁lieb 3.7353848804576443e-06 -▁nationalism 3.7353848804576443e-06 -▁polar 3.7353848804576443e-06 -▁servants 3.7353848804576443e-06 -▁stattfindenden 3.7353848804576443e-06 -backs 3.7315964576579919e-06 -ckung 3.7315964576579919e-06 -firma 3.7315964576579919e-06 -getr 3.7315964576579919e-06 -itiz 3.7315964576579919e-06 -kä 3.7315964576579919e-06 -lysées 3.7315964576579919e-06 -messungen 3.7315964576579919e-06 -öffnung 3.7315964576579919e-06 -▁Banner 3.7315964576579919e-06 -▁Besondere 3.7315964576579919e-06 -▁Erw 3.7315964576579919e-06 -▁Getränk 3.7315964576579919e-06 -▁Horizont 3.7315964576579919e-06 -▁Münzen 3.7315964576579919e-06 -▁Oil 3.7315964576579919e-06 -▁Racing 3.7315964576579919e-06 -▁Umfrage 3.7315964576579919e-06 -▁Vernichtung 3.7315964576579919e-06 -▁Währungsfonds 3.7315964576579919e-06 -▁ext 3.7315964576579919e-06 -▁reformed 3.7315964576579919e-06 -▁tune 3.7315964576579919e-06 -▁unequiv 3.7315964576579919e-06 -▁wide-ranging 3.7315964576579919e-06 -atischer 3.727808034858339e-06 -ieri 3.727808034858339e-06 -olas 3.727808034858339e-06 -worth 3.727808034858339e-06 -ädagog 3.727808034858339e-06 -▁Assoziierungsabkommen 3.727808034858339e-06 -▁Durchschnitts 3.727808034858339e-06 -▁Kyi 3.727808034858339e-06 -▁LV 3.727808034858339e-06 -▁Province 3.727808034858339e-06 -▁Terroran 3.727808034858339e-06 -▁anerkannte 3.727808034858339e-06 -▁cellar 3.727808034858339e-06 -▁inspiriert 3.727808034858339e-06 -▁maximalen 3.727808034858339e-06 -▁requesting 3.727808034858339e-06 -▁unzähligen 3.727808034858339e-06 -born 3.7240196120586861e-06 -hungen 3.7240196120586861e-06 -konstruktion 3.7240196120586861e-06 -kämpft 3.7240196120586861e-06 -ungsverfahrens 3.7240196120586861e-06 -▁Eier 3.7240196120586861e-06 -▁Interoper 3.7240196120586861e-06 -▁Mehrheits 3.7240196120586861e-06 -▁Nationale 3.7240196120586861e-06 -▁Thematik 3.7240196120586861e-06 -▁Wahlkreis 3.7240196120586861e-06 -▁befest 3.7240196120586861e-06 -▁beverages 3.7240196120586861e-06 -▁bisweilen 3.7240196120586861e-06 -▁ceilings 3.7240196120586861e-06 -▁disparities 3.7240196120586861e-06 -▁erlaub 3.7240196120586861e-06 -▁kostspiel 3.7240196120586861e-06 -▁terra 3.7240196120586861e-06 -▁voranz 3.7240196120586861e-06 -76 3.7202311892590332e-06 -asion 3.7202311892590332e-06 -lair 3.7202311892590332e-06 -ränkte 3.7202311892590332e-06 -đ 3.7202311892590332e-06 -▁280 3.7202311892590332e-06 -▁Animation 3.7202311892590332e-06 -▁Anzahlung 3.7202311892590332e-06 -▁Birmingham 3.7202311892590332e-06 -▁Bürgermeister 3.7202311892590332e-06 -▁Chic 3.7202311892590332e-06 -▁Demonstrationen 3.7202311892590332e-06 -▁Einheim 3.7202311892590332e-06 -▁Emperor 3.7202311892590332e-06 -▁Ries 3.7202311892590332e-06 -▁Session 3.7202311892590332e-06 -▁Urlauber 3.7202311892590332e-06 -▁brasil 3.7202311892590332e-06 -▁footing 3.7202311892590332e-06 -▁mature 3.7202311892590332e-06 -▁propos 3.7202311892590332e-06 -▁stellvertreten 3.7202311892590332e-06 -drücken 3.7164427664593807e-06 -grä 3.7164427664593807e-06 -städten 3.7164427664593807e-06 -weile 3.7164427664593807e-06 - 3.7164427664593807e-06 -▁Balt 3.7164427664593807e-06 -▁Christoph 3.7164427664593807e-06 -▁Clo 3.7164427664593807e-06 -▁Eiffel 3.7164427664593807e-06 -▁GASP 3.7164427664593807e-06 -▁Gefäng 3.7164427664593807e-06 -▁Grüne 3.7164427664593807e-06 -▁Kil 3.7164427664593807e-06 -▁POS 3.7164427664593807e-06 -▁Suu 3.7164427664593807e-06 -▁Treas 3.7164427664593807e-06 -▁ineffective 3.7164427664593807e-06 -▁optimistic 3.7164427664593807e-06 -▁tele 3.7164427664593807e-06 -▁ökonom 3.7164427664593807e-06 --Con 3.7126543436597278e-06 -ROM 3.7126543436597278e-06 -Tra 3.7126543436597278e-06 -ext 3.7126543436597278e-06 -icles 3.7126543436597278e-06 -verantwort 3.7126543436597278e-06 -▁1970s 3.7126543436597278e-06 -▁Brava 3.7126543436597278e-06 -▁DSLR 3.7126543436597278e-06 -▁Oomen-Ruijten 3.7126543436597278e-06 -▁Schäd 3.7126543436597278e-06 -▁Verbraucherpolitik 3.7126543436597278e-06 -▁bestmögliche 3.7126543436597278e-06 -▁empower 3.7126543436597278e-06 -▁esta 3.7126543436597278e-06 -▁grace 3.7126543436597278e-06 -▁hellen 3.7126543436597278e-06 -▁inhuman 3.7126543436597278e-06 -▁jewel 3.7126543436597278e-06 -▁kub 3.7126543436597278e-06 -▁lengthy 3.7126543436597278e-06 -▁synthetic 3.7126543436597278e-06 -▁voltage 3.7126543436597278e-06 -▁̈ 3.7126543436597278e-06 -etrieb 3.7088659208600749e-06 -prozessen 3.7088659208600749e-06 -üllt 3.7088659208600749e-06 -ützten 3.7088659208600749e-06 -▁Democrat 3.7088659208600749e-06 -▁Dominion 3.7088659208600749e-06 -▁Meldung 3.7088659208600749e-06 -▁Plural 3.7088659208600749e-06 -▁angesiedelt 3.7088659208600749e-06 -▁auszuschließen 3.7088659208600749e-06 -▁auszut 3.7088659208600749e-06 -▁cleaned 3.7088659208600749e-06 -▁conquer 3.7088659208600749e-06 -▁erhöhten 3.7088659208600749e-06 -▁erklär 3.7088659208600749e-06 -▁exemplary 3.7088659208600749e-06 -▁forcing 3.7088659208600749e-06 -▁schlug 3.7088659208600749e-06 -▁shaping 3.7088659208600749e-06 -endete 3.705077498060422e-06 -offee 3.705077498060422e-06 -▁-E 3.705077498060422e-06 -▁Cel 3.705077498060422e-06 -▁Einzelpersonen 3.705077498060422e-06 -▁Jar 3.705077498060422e-06 -▁Javascript 3.705077498060422e-06 -▁Kofin 3.705077498060422e-06 -▁Lücke 3.705077498060422e-06 -▁MC 3.705077498060422e-06 -▁Malm 3.705077498060422e-06 -▁Nicolas 3.705077498060422e-06 -▁Schichten 3.705077498060422e-06 -▁Schwed 3.705077498060422e-06 -▁Speichern 3.705077498060422e-06 -▁Tschechische 3.705077498060422e-06 -▁allevi 3.705077498060422e-06 -▁capita 3.705077498060422e-06 -▁gefährd 3.705077498060422e-06 -▁gestrigen 3.705077498060422e-06 -▁gleichzeit 3.705077498060422e-06 -▁inability 3.705077498060422e-06 -▁niederländische 3.705077498060422e-06 -▁unbea 3.705077498060422e-06 -▁verzichtet 3.705077498060422e-06 -▁weekends 3.705077498060422e-06 -decke 3.7012890752607696e-06 -heorie 3.7012890752607696e-06 -▁Aktionsprogramm 3.7012890752607696e-06 -▁Eco 3.7012890752607696e-06 -▁Grä 3.7012890752607696e-06 -▁Handwerk 3.7012890752607696e-06 -▁Meinungsverschieden 3.7012890752607696e-06 -▁Nova 3.7012890752607696e-06 -▁Sonnent 3.7012890752607696e-06 -▁Wertsch 3.7012890752607696e-06 -▁adm 3.7012890752607696e-06 -▁castles 3.7012890752607696e-06 -▁consultancy 3.7012890752607696e-06 -▁cosmetic 3.7012890752607696e-06 -▁creatures 3.7012890752607696e-06 -▁infection 3.7012890752607696e-06 -▁patron 3.7012890752607696e-06 -PI 3.6975006524611167e-06 -bestände 3.6975006524611167e-06 -faden 3.6975006524611167e-06 -fia 3.6975006524611167e-06 -höre 3.6975006524611167e-06 -rett 3.6975006524611167e-06 -ührung 3.6975006524611167e-06 -▁Apr 3.6975006524611167e-06 -▁Luf 3.6975006524611167e-06 -▁engineer 3.6975006524611167e-06 -▁marvell 3.6975006524611167e-06 -▁nin 3.6975006524611167e-06 -▁vorübergehend 3.6975006524611167e-06 -.04 3.6937122296614638e-06 -prüfungen 3.6937122296614638e-06 -rehensible 3.6937122296614638e-06 -▁1959 3.6937122296614638e-06 -▁Blues 3.6937122296614638e-06 -▁Butter 3.6937122296614638e-06 -▁Eigentlich 3.6937122296614638e-06 -▁Monster 3.6937122296614638e-06 -▁Neuse 3.6937122296614638e-06 -▁cann 3.6937122296614638e-06 -▁dramatischen 3.6937122296614638e-06 -▁einleiten 3.6937122296614638e-06 -▁intellekt 3.6937122296614638e-06 -▁murdered 3.6937122296614638e-06 -▁natürlicher 3.6937122296614638e-06 -▁revital 3.6937122296614638e-06 -▁vorherrschen 3.6937122296614638e-06 -comes 3.6899238068618109e-06 -reden 3.6899238068618109e-06 -stru 3.6899238068618109e-06 -teien 3.6899238068618109e-06 -▁Basque 3.6899238068618109e-06 -▁Beamte 3.6899238068618109e-06 -▁Cairo 3.6899238068618109e-06 -▁Focus 3.6899238068618109e-06 -▁Ricc 3.6899238068618109e-06 -▁Schande 3.6899238068618109e-06 -▁Sozialfonds 3.6899238068618109e-06 -▁Stadtr 3.6899238068618109e-06 -▁Stone 3.6899238068618109e-06 -▁Strahl 3.6899238068618109e-06 -▁anerkennt 3.6899238068618109e-06 -▁chambre 3.6899238068618109e-06 -▁ersucht 3.6899238068618109e-06 -▁hect 3.6899238068618109e-06 -▁outlook 3.6899238068618109e-06 --Problem 3.6861353840621584e-06 -IFF 3.6861353840621584e-06 -ameworks 3.6861353840621584e-06 -ementioned 3.6861353840621584e-06 -ongs 3.6861353840621584e-06 -umfeld 3.6861353840621584e-06 -ungsmittel 3.6861353840621584e-06 -unte 3.6861353840621584e-06 -▁320 3.6861353840621584e-06 -▁Easter 3.6861353840621584e-06 -▁Fell 3.6861353840621584e-06 -▁Footprint 3.6861353840621584e-06 -▁KUKA 3.6861353840621584e-06 -▁Kleink 3.6861353840621584e-06 -▁Kyoto-Protokoll 3.6861353840621584e-06 -▁Nordamerika 3.6861353840621584e-06 -▁PAD 3.6861353840621584e-06 -▁disgra 3.6861353840621584e-06 -▁durchschnitt 3.6861353840621584e-06 -▁eingebaut 3.6861353840621584e-06 -▁geschaff 3.6861353840621584e-06 -▁gespeicher 3.6861353840621584e-06 -▁loyal 3.6861353840621584e-06 -▁multiannual 3.6861353840621584e-06 -▁verschlechtert 3.6861353840621584e-06 -▁warmth 3.6861353840621584e-06 -aches 3.6823469612625055e-06 -annung 3.6823469612625055e-06 -ategorie 3.6823469612625055e-06 -beiträge 3.6823469612625055e-06 -eads 3.6823469612625055e-06 -enda 3.6823469612625055e-06 -ermächtigungen 3.6823469612625055e-06 -hindert 3.6823469612625055e-06 -iations 3.6823469612625055e-06 -itsch 3.6823469612625055e-06 -wicklung 3.6823469612625055e-06 -▁Bündnis 3.6823469612625055e-06 -▁Football 3.6823469612625055e-06 -▁Former 3.6823469612625055e-06 -▁Knopf 3.6823469612625055e-06 -▁Table 3.6823469612625055e-06 -▁Verantwortlichkeit 3.6823469612625055e-06 -▁Wettbewerbspolitik 3.6823469612625055e-06 -▁eingehend 3.6823469612625055e-06 -▁erstklassigen 3.6823469612625055e-06 -▁exagger 3.6823469612625055e-06 -▁jam 3.6823469612625055e-06 -▁verpflicht 3.6823469612625055e-06 -▁verstößt 3.6823469612625055e-06 -▁verurs 3.6823469612625055e-06 -▁Überzeug 3.6823469612625055e-06 -marke 3.6785585384628527e-06 -ossiers 3.6785585384628527e-06 -uption 3.6785585384628527e-06 -▁Aids 3.6785585384628527e-06 -▁Favor 3.6785585384628527e-06 -▁Guad 3.6785585384628527e-06 -▁Hoheitsgebiet 3.6785585384628527e-06 -▁Late 3.6785585384628527e-06 -▁Nephi 3.6785585384628527e-06 -▁Notfall 3.6785585384628527e-06 -▁Obergrenze 3.6785585384628527e-06 -▁Paulo 3.6785585384628527e-06 -▁artw 3.6785585384628527e-06 -▁beliebig 3.6785585384628527e-06 -▁cloud 3.6785585384628527e-06 -▁comitology 3.6785585384628527e-06 -▁commodities 3.6785585384628527e-06 -▁continents 3.6785585384628527e-06 -▁desires 3.6785585384628527e-06 -▁drama 3.6785585384628527e-06 -▁inventory 3.6785585384628527e-06 -▁marina 3.6785585384628527e-06 -▁motors 3.6785585384628527e-06 -▁pupils 3.6785585384628527e-06 -▁stell 3.6785585384628527e-06 -▁vergrößern 3.6785585384628527e-06 -,50 3.6747701156631998e-06 -risiken 3.6747701156631998e-06 -sender 3.6747701156631998e-06 -ungspolitik 3.6747701156631998e-06 -wandeln 3.6747701156631998e-06 -ĺ 3.6747701156631998e-06 -▁Angela 3.6747701156631998e-06 -▁Arist 3.6747701156631998e-06 -▁Banking 3.6747701156631998e-06 -▁Dil 3.6747701156631998e-06 -▁Heathrow 3.6747701156631998e-06 -▁Invasion 3.6747701156631998e-06 -▁Krist 3.6747701156631998e-06 -▁Sah 3.6747701156631998e-06 -▁Vorreit 3.6747701156631998e-06 -▁Wikipedia 3.6747701156631998e-06 -▁abortion 3.6747701156631998e-06 -▁administrativen 3.6747701156631998e-06 -▁ausdrückliche 3.6747701156631998e-06 -▁beträchtlich 3.6747701156631998e-06 -▁brachten 3.6747701156631998e-06 -▁own-initiative 3.6747701156631998e-06 -▁relatives 3.6747701156631998e-06 -▁remembered 3.6747701156631998e-06 -▁rings 3.6747701156631998e-06 -▁templates 3.6747701156631998e-06 -▁zuteil 3.6747701156631998e-06 -befugnisse 3.6709816928635473e-06 -isedatum 3.6709816928635473e-06 -itherto 3.6709816928635473e-06 -wuchs 3.6709816928635473e-06 -zukommen 3.6709816928635473e-06 -▁Already 3.6709816928635473e-06 -▁Bein 3.6709816928635473e-06 -▁Entsprechend 3.6709816928635473e-06 -▁Freundlichkeit 3.6709816928635473e-06 -▁Jacob 3.6709816928635473e-06 -▁Leinen 3.6709816928635473e-06 -▁Pharm 3.6709816928635473e-06 -▁Schaltfläche 3.6709816928635473e-06 -▁Schild 3.6709816928635473e-06 -▁Ubuntu 3.6709816928635473e-06 -▁dent 3.6709816928635473e-06 -▁excav 3.6709816928635473e-06 -▁ther 3.6709816928635473e-06 -eaker 3.6671932700638944e-06 -einkommens 3.6671932700638944e-06 -software 3.6671932700638944e-06 -uropä 3.6671932700638944e-06 -ussia 3.6671932700638944e-06 -être 3.6671932700638944e-06 -öhnt 3.6671932700638944e-06 -▁Imaging 3.6671932700638944e-06 -▁Municip 3.6671932700638944e-06 -▁Pausch 3.6671932700638944e-06 -▁Reed 3.6671932700638944e-06 -▁Rehn 3.6671932700638944e-06 -▁atemberaubenden 3.6671932700638944e-06 -▁blauen 3.6671932700638944e-06 -▁conditional 3.6671932700638944e-06 -▁controll 3.6671932700638944e-06 -▁indicator 3.6671932700638944e-06 -▁leer 3.6671932700638944e-06 -▁soziales 3.6671932700638944e-06 -▁touched 3.6671932700638944e-06 -▁verkörp 3.6671932700638944e-06 -▁volcano 3.6671932700638944e-06 -amerikas 3.6634048472642415e-06 -ennetz 3.6634048472642415e-06 -ketten 3.6634048472642415e-06 -tritte 3.6634048472642415e-06 -▁Ander 3.6634048472642415e-06 -▁Auftritt 3.6634048472642415e-06 -▁Bäume 3.6634048472642415e-06 -▁Geister 3.6634048472642415e-06 -▁Karlsruhe 3.6634048472642415e-06 -▁Login 3.6634048472642415e-06 -▁Menu 3.6634048472642415e-06 -▁NV 3.6634048472642415e-06 -▁PER 3.6634048472642415e-06 -▁Rue 3.6634048472642415e-06 -▁Vorgehens 3.6634048472642415e-06 -▁angrenzen 3.6634048472642415e-06 -▁bequemen 3.6634048472642415e-06 -▁einl 3.6634048472642415e-06 -▁inhaft 3.6634048472642415e-06 -▁kathol 3.6634048472642415e-06 -▁minors 3.6634048472642415e-06 -▁redundant 3.6634048472642415e-06 -▁steady 3.6634048472642415e-06 -▁widersprüch 3.6634048472642415e-06 -inationen 3.659616424464589e-06 -▁Brasil 3.659616424464589e-06 -▁Pion 3.659616424464589e-06 -▁accomplish 3.659616424464589e-06 -▁brothers 3.659616424464589e-06 -▁claiming 3.659616424464589e-06 -▁festgeschrieben 3.659616424464589e-06 -▁investigated 3.659616424464589e-06 -▁kurzfristige 3.659616424464589e-06 -▁num 3.659616424464589e-06 -▁slave 3.659616424464589e-06 -▁thickness 3.659616424464589e-06 -▁virtue 3.659616424464589e-06 -▁zurückgreifen 3.659616424464589e-06 -ULT 3.6558280016649362e-06 -nisch 3.6558280016649362e-06 -papiere 3.6558280016649362e-06 -▁Anc 3.6558280016649362e-06 -▁Chapter 3.6558280016649362e-06 -▁Christopher 3.6558280016649362e-06 -▁Geis 3.6558280016649362e-06 -▁Goethe 3.6558280016649362e-06 -▁anged 3.6558280016649362e-06 -▁bisherige 3.6558280016649362e-06 -▁deprived 3.6558280016649362e-06 -▁enjoyment 3.6558280016649362e-06 -▁höchstens 3.6558280016649362e-06 -▁mentioning 3.6558280016649362e-06 -▁oberhalb 3.6558280016649362e-06 -▁predecessor 3.6558280016649362e-06 -▁sacrifice 3.6558280016649362e-06 -▁salaries 3.6558280016649362e-06 -▁spezielles 3.6558280016649362e-06 -▁zögern 3.6558280016649362e-06 -.2008 3.6520395788652833e-06 -geschlossen 3.6520395788652833e-06 -handeln 3.6520395788652833e-06 -krieges 3.6520395788652833e-06 -ouss 3.6520395788652833e-06 -pone 3.6520395788652833e-06 -raucher 3.6520395788652833e-06 -▁Buchten 3.6520395788652833e-06 -▁Gene 3.6520395788652833e-06 -▁Gleichberechtigung 3.6520395788652833e-06 -▁Mitgliedsländern 3.6520395788652833e-06 -▁Verdacht 3.6520395788652833e-06 -▁YORK 3.6520395788652833e-06 -▁cross-country 3.6520395788652833e-06 -▁gemeinnütz 3.6520395788652833e-06 -▁inhaltlich 3.6520395788652833e-06 -▁interfere 3.6520395788652833e-06 -▁sektor 3.6520395788652833e-06 -▁stup 3.6520395788652833e-06 -▁töten 3.6520395788652833e-06 -▁weite 3.6520395788652833e-06 -arbeitete 3.6482511560656304e-06 -being 3.6482511560656304e-06 -orca 3.6482511560656304e-06 -zugleichen 3.6482511560656304e-06 -▁145 3.6482511560656304e-06 -▁Abwesenheit 3.6482511560656304e-06 -▁Belange 3.6482511560656304e-06 -▁Haartrockner 3.6482511560656304e-06 -▁Kev 3.6482511560656304e-06 -▁Minist 3.6482511560656304e-06 -▁Relais 3.6482511560656304e-06 -▁Spin 3.6482511560656304e-06 -▁cofin 3.6482511560656304e-06 -▁entschloss 3.6482511560656304e-06 -▁vorzustellen 3.6482511560656304e-06 -protokoll 3.6444627332659779e-06 -öhnen 3.6444627332659779e-06 -▁Bavarian 3.6444627332659779e-06 -▁Boulevard 3.6444627332659779e-06 -▁Diskrimin 3.6444627332659779e-06 -▁Europa-Mittelmeer 3.6444627332659779e-06 -▁Gemeinschaftspolitik 3.6444627332659779e-06 -▁Mw 3.6444627332659779e-06 -▁Nine 3.6444627332659779e-06 -▁Rechtsanw 3.6444627332659779e-06 -▁Slovenian 3.6444627332659779e-06 -▁administrations 3.6444627332659779e-06 -▁getrennte 3.6444627332659779e-06 -▁infected 3.6444627332659779e-06 -▁influen 3.6444627332659779e-06 -▁oppon 3.6444627332659779e-06 -▁unverzichtbar 3.6444627332659779e-06 -▁vermin 3.6444627332659779e-06 -▁verste 3.6444627332659779e-06 -apie 3.640674310466325e-06 -ausrüstung 3.640674310466325e-06 -codes 3.640674310466325e-06 -inta 3.640674310466325e-06 -lash 3.640674310466325e-06 -lernen 3.640674310466325e-06 -most 3.640674310466325e-06 -rise 3.640674310466325e-06 -äfen 3.640674310466325e-06 -älern 3.640674310466325e-06 -▁Beyond 3.640674310466325e-06 -▁Gesundheits- 3.640674310466325e-06 -▁Gom 3.640674310466325e-06 -▁Klein- 3.640674310466325e-06 -▁Mao 3.640674310466325e-06 -▁Neur 3.640674310466325e-06 -▁Romp 3.640674310466325e-06 -▁Schmidt 3.640674310466325e-06 -▁Tam 3.640674310466325e-06 -▁antibiot 3.640674310466325e-06 -▁geschlagen 3.640674310466325e-06 -▁hochwertiger 3.640674310466325e-06 -▁logos 3.640674310466325e-06 -▁plastics 3.640674310466325e-06 -▁rote 3.640674310466325e-06 -▁third-party 3.640674310466325e-06 -utable 3.6368858876666721e-06 -verlauf 3.6368858876666721e-06 -▁Maritime 3.6368858876666721e-06 -▁Players 3.6368858876666721e-06 -▁Romagna 3.6368858876666721e-06 -▁Sensor 3.6368858876666721e-06 -▁Works 3.6368858876666721e-06 -▁findest 3.6368858876666721e-06 -▁gorge 3.6368858876666721e-06 -▁high-tech 3.6368858876666721e-06 -▁kompl 3.6368858876666721e-06 -▁peripher 3.6368858876666721e-06 -▁supposedly 3.6368858876666721e-06 -IMM 3.6330974648670192e-06 -alling 3.6330974648670192e-06 -etischen 3.6330974648670192e-06 -heizung 3.6330974648670192e-06 -iesst 3.6330974648670192e-06 -inia 3.6330974648670192e-06 -istans 3.6330974648670192e-06 -länge 3.6330974648670192e-06 -olet 3.6330974648670192e-06 -schafft 3.6330974648670192e-06 -ähere 3.6330974648670192e-06 -▁1947 3.6330974648670192e-06 -▁Bestehen 3.6330974648670192e-06 -▁Cafe 3.6330974648670192e-06 -▁Charl 3.6330974648670192e-06 -▁Indones 3.6330974648670192e-06 -▁Ivan 3.6330974648670192e-06 -▁Kenya 3.6330974648670192e-06 -▁Speicherung 3.6330974648670192e-06 -▁Warte 3.6330974648670192e-06 -▁attributes 3.6330974648670192e-06 -▁aufbew 3.6330974648670192e-06 -▁circles 3.6330974648670192e-06 -▁fifty 3.6330974648670192e-06 -▁nicely 3.6330974648670192e-06 -▁norms 3.6330974648670192e-06 -▁offences 3.6330974648670192e-06 -▁stören 3.6330974648670192e-06 -▁zukommt 3.6330974648670192e-06 -CL 3.6293090420673668e-06 -PD 3.6293090420673668e-06 -eitern 3.6293090420673668e-06 -liebhaber 3.6293090420673668e-06 -udad 3.6293090420673668e-06 -▁Blanc 3.6293090420673668e-06 -▁Futtermittel 3.6293090420673668e-06 -▁Geräus 3.6293090420673668e-06 -▁bund 3.6293090420673668e-06 -▁draftsman 3.6293090420673668e-06 -▁repressive 3.6293090420673668e-06 -▁reproductive 3.6293090420673668e-06 -▁vorgelegte 3.6293090420673668e-06 -▁ward 3.6293090420673668e-06 -ahara 3.6255206192677139e-06 -bedien 3.6255206192677139e-06 -geworfen 3.6255206192677139e-06 -ichtlinie 3.6255206192677139e-06 -osity 3.6255206192677139e-06 -willigen 3.6255206192677139e-06 -windigkeiten 3.6255206192677139e-06 -äufig 3.6255206192677139e-06 -ößen 3.6255206192677139e-06 -▁Bibione 3.6255206192677139e-06 -▁Delors 3.6255206192677139e-06 -▁Etwas 3.6255206192677139e-06 -▁Hemm 3.6255206192677139e-06 -▁Italienisch 3.6255206192677139e-06 -▁Toulouse 3.6255206192677139e-06 -▁Urheberrecht 3.6255206192677139e-06 -▁Vorzüge 3.6255206192677139e-06 -▁betrug 3.6255206192677139e-06 -▁comprom 3.6255206192677139e-06 -▁crown 3.6255206192677139e-06 -▁jüdischen 3.6255206192677139e-06 -▁musst 3.6255206192677139e-06 -▁protocols 3.6255206192677139e-06 -▁wöchentlich 3.6255206192677139e-06 -▁zivile 3.6255206192677139e-06 -abgabe 3.621732196468061e-06 -gästen 3.621732196468061e-06 -ildern 3.621732196468061e-06 -▁Bahrain 3.621732196468061e-06 -▁Check-Out 3.621732196468061e-06 -▁Computern 3.621732196468061e-06 -▁Dienstleister 3.621732196468061e-06 -▁Gab 3.621732196468061e-06 -▁Getränken 3.621732196468061e-06 -▁Pressefreiheit 3.621732196468061e-06 -▁Ruinen 3.621732196468061e-06 -▁WIT 3.621732196468061e-06 -▁Wiedergabe 3.621732196468061e-06 -▁Wortes 3.621732196468061e-06 -▁ausl 3.621732196468061e-06 -▁county 3.621732196468061e-06 -▁dancing 3.621732196468061e-06 -▁erneuert 3.621732196468061e-06 -▁expressions 3.621732196468061e-06 -▁filed 3.621732196468061e-06 -▁großzügige 3.621732196468061e-06 -▁heiligen 3.621732196468061e-06 -▁promin 3.621732196468061e-06 -▁vul 3.621732196468061e-06 -▁züg 3.621732196468061e-06 --Sterne-Hotel 3.6179437736684081e-06 -71 3.6179437736684081e-06 -800 3.6179437736684081e-06 -anzlei 3.6179437736684081e-06 -effizient 3.6179437736684081e-06 -etung 3.6179437736684081e-06 -haltestelle 3.6179437736684081e-06 -iktor 3.6179437736684081e-06 -vorsitzenden 3.6179437736684081e-06 -ymp 3.6179437736684081e-06 -▁Ei 3.6179437736684081e-06 -▁Freihandelsabkommen 3.6179437736684081e-06 -▁Installer 3.6179437736684081e-06 -▁NY 3.6179437736684081e-06 -▁Repl 3.6179437736684081e-06 -▁SLR 3.6179437736684081e-06 -▁Saar 3.6179437736684081e-06 -▁Zeitschriften 3.6179437736684081e-06 -▁abgew 3.6179437736684081e-06 -▁benötigte 3.6179437736684081e-06 -▁bets 3.6179437736684081e-06 -▁combinations 3.6179437736684081e-06 -▁dying 3.6179437736684081e-06 -▁exzellenten 3.6179437736684081e-06 -▁jury 3.6179437736684081e-06 -▁klim 3.6179437736684081e-06 -▁parad 3.6179437736684081e-06 -▁remit 3.6179437736684081e-06 -▁rows 3.6179437736684081e-06 -▁xenophobia 3.6179437736684081e-06 -▁öfter 3.6179437736684081e-06 -aufgabe 3.6141553508687556e-06 -folios 3.6141553508687556e-06 -gelegen 3.6141553508687556e-06 -ikanern 3.6141553508687556e-06 -legungen 3.6141553508687556e-06 -odus 3.6141553508687556e-06 -ühm 3.6141553508687556e-06 -▁Abänderungs 3.6141553508687556e-06 -▁CET 3.6141553508687556e-06 -▁Daily 3.6141553508687556e-06 -▁ENT 3.6141553508687556e-06 -▁Figuren 3.6141553508687556e-06 -▁Gare 3.6141553508687556e-06 -▁Panz 3.6141553508687556e-06 -▁Syl 3.6141553508687556e-06 -▁Südkorea 3.6141553508687556e-06 -▁Uruguay 3.6141553508687556e-06 -▁attributed 3.6141553508687556e-06 -▁coupled 3.6141553508687556e-06 -▁crowd 3.6141553508687556e-06 -▁ersta 3.6141553508687556e-06 -▁laufend 3.6141553508687556e-06 -▁mile 3.6141553508687556e-06 -▁towers 3.6141553508687556e-06 -▁transferring 3.6141553508687556e-06 -▁verdeutlicht 3.6141553508687556e-06 -▁übergehen 3.6141553508687556e-06 -erries 3.6103669280691027e-06 -pie 3.6103669280691027e-06 -urcht 3.6103669280691027e-06 -▁Ah 3.6103669280691027e-06 -▁Ahmad 3.6103669280691027e-06 -▁Case 3.6103669280691027e-06 -▁DNS 3.6103669280691027e-06 -▁Geltung 3.6103669280691027e-06 -▁Handelspolitik 3.6103669280691027e-06 -▁Kontr 3.6103669280691027e-06 -▁Kris 3.6103669280691027e-06 -▁Krum 3.6103669280691027e-06 -▁Sorten 3.6103669280691027e-06 -▁appealing 3.6103669280691027e-06 -▁ausdrücken 3.6103669280691027e-06 -▁ausmacht 3.6103669280691027e-06 -▁debut 3.6103669280691027e-06 -▁determines 3.6103669280691027e-06 -▁explizit 3.6103669280691027e-06 -▁gewähr 3.6103669280691027e-06 -▁hydraulic 3.6103669280691027e-06 -▁mayor 3.6103669280691027e-06 -▁pixels 3.6103669280691027e-06 -▁remov 3.6103669280691027e-06 -▁sono 3.6103669280691027e-06 -▁triumph 3.6103669280691027e-06 -▁unerm 3.6103669280691027e-06 -OBIL 3.60657850526945e-06 -clubs 3.60657850526945e-06 -eteness 3.60657850526945e-06 -etermination 3.60657850526945e-06 -▁2D 3.60657850526945e-06 -▁Arrang 3.60657850526945e-06 -▁FE 3.60657850526945e-06 -▁Gletscher 3.60657850526945e-06 -▁Highlights 3.60657850526945e-06 -▁Palermo 3.60657850526945e-06 -▁Soul 3.60657850526945e-06 -▁Verlagerung 3.60657850526945e-06 -▁alkohol 3.60657850526945e-06 -▁angewand 3.60657850526945e-06 -▁gesundheitlichen 3.60657850526945e-06 -▁harmonious 3.60657850526945e-06 -▁lucky 3.60657850526945e-06 -▁readily 3.60657850526945e-06 -hän 3.6027900824697969e-06 -immern 3.6027900824697969e-06 -niveaus 3.6027900824697969e-06 -ußts 3.6027900824697969e-06 -weist 3.6027900824697969e-06 -ymbol 3.6027900824697969e-06 -▁Büchern 3.6027900824697969e-06 -▁Fujifilm 3.6027900824697969e-06 -▁Insp 3.6027900824697969e-06 -▁Observ 3.6027900824697969e-06 -▁Ressource 3.6027900824697969e-06 -▁Sofort 3.6027900824697969e-06 -▁auslösen 3.6027900824697969e-06 -▁bewaffneten 3.6027900824697969e-06 -▁breakdown 3.6027900824697969e-06 -▁fleets 3.6027900824697969e-06 -▁jedw 3.6027900824697969e-06 -▁kraft 3.6027900824697969e-06 -▁neoliber 3.6027900824697969e-06 -▁supplying 3.6027900824697969e-06 -▁unite 3.6027900824697969e-06 -eafter 3.5990016596701445e-06 -elie 3.5990016596701445e-06 -oomla 3.5990016596701445e-06 -▁Fuerteventura 3.5990016596701445e-06 -▁Maler 3.5990016596701445e-06 -▁Rost 3.5990016596701445e-06 -▁aufgeb 3.5990016596701445e-06 -▁beklagen 3.5990016596701445e-06 -▁zurückgezogen 3.5990016596701445e-06 -gression 3.5952132368704916e-06 -idencies 3.5952132368704916e-06 -osex 3.5952132368704916e-06 -öße 3.5952132368704916e-06 -▁155 3.5952132368704916e-06 -▁Military 3.5952132368704916e-06 -▁Motivation 3.5952132368704916e-06 -▁Nationalismus 3.5952132368704916e-06 -▁PD 3.5952132368704916e-06 -▁aufgreifen 3.5952132368704916e-06 -▁clever 3.5952132368704916e-06 -▁conception 3.5952132368704916e-06 -▁employer 3.5952132368704916e-06 -▁gerät 3.5952132368704916e-06 -▁grows 3.5952132368704916e-06 -▁loose 3.5952132368704916e-06 -▁schallisol 3.5952132368704916e-06 -▁sob 3.5952132368704916e-06 -▁therap 3.5952132368704916e-06 -▁traditioneller 3.5952132368704916e-06 -▁undermining 3.5952132368704916e-06 -WB 3.5914248140708387e-06 -esserung 3.5914248140708387e-06 -igar 3.5914248140708387e-06 -э 3.5914248140708387e-06 -▁Hamb 3.5914248140708387e-06 -▁String 3.5914248140708387e-06 -▁educated 3.5914248140708387e-06 -▁hinnehmbar 3.5914248140708387e-06 -▁jenes 3.5914248140708387e-06 -▁kön 3.5914248140708387e-06 -▁lectures 3.5914248140708387e-06 -▁mistaken 3.5914248140708387e-06 -allas 3.5876363912711858e-06 -mord 3.5876363912711858e-06 -schlüsselung 3.5876363912711858e-06 -ümm 3.5876363912711858e-06 -▁80er 3.5876363912711858e-06 -▁Baust 3.5876363912711858e-06 -▁Boeing 3.5876363912711858e-06 -▁Dual 3.5876363912711858e-06 -▁No. 3.5876363912711858e-06 -▁Opern 3.5876363912711858e-06 -▁Plans 3.5876363912711858e-06 -▁Rey 3.5876363912711858e-06 -▁Schau 3.5876363912711858e-06 -▁Seien 3.5876363912711858e-06 -▁Wog 3.5876363912711858e-06 -▁Zufl 3.5876363912711858e-06 -▁blut 3.5876363912711858e-06 -▁day-to-day 3.5876363912711858e-06 -▁differing 3.5876363912711858e-06 -▁schwerwiegende 3.5876363912711858e-06 -▁sunshine 3.5876363912711858e-06 -▁tragischen 3.5876363912711858e-06 -▁vernichtet 3.5876363912711858e-06 -▁weiterver 3.5876363912711858e-06 -▁Änderungsvorschläge 3.5876363912711858e-06 -change 3.5838479684715333e-06 -zählung 3.5838479684715333e-06 -▁Bul 3.5838479684715333e-06 -▁Daimler 3.5838479684715333e-06 -▁Fallout 3.5838479684715333e-06 -▁Fru 3.5838479684715333e-06 -▁Frühstücksraum 3.5838479684715333e-06 -▁Gust 3.5838479684715333e-06 -▁Jose 3.5838479684715333e-06 -▁Kambod 3.5838479684715333e-06 -▁TS 3.5838479684715333e-06 -▁Tenden 3.5838479684715333e-06 -▁Wide 3.5838479684715333e-06 -▁bizarre 3.5838479684715333e-06 -▁boutiques 3.5838479684715333e-06 -▁brill 3.5838479684715333e-06 -▁hereby 3.5838479684715333e-06 -▁letting 3.5838479684715333e-06 -▁liqu 3.5838479684715333e-06 -▁restart 3.5838479684715333e-06 -▁vermarkt 3.5838479684715333e-06 -bedürf 3.5800595456718804e-06 -eine 3.5800595456718804e-06 -entscheidung 3.5800595456718804e-06 -offiz 3.5800595456718804e-06 -rankung 3.5800595456718804e-06 -tet 3.5800595456718804e-06 -ädt 3.5800595456718804e-06 -▁Better 3.5800595456718804e-06 -▁IX 3.5800595456718804e-06 -▁Neapel 3.5800595456718804e-06 -▁None 3.5800595456718804e-06 -▁Produktpalette 3.5800595456718804e-06 -▁Ric 3.5800595456718804e-06 -▁dritt 3.5800595456718804e-06 -▁jacuzzi 3.5800595456718804e-06 -▁mü 3.5800595456718804e-06 -▁patented 3.5800595456718804e-06 -▁reiter 3.5800595456718804e-06 -ateral 3.5762711228722275e-06 -▁BugTraq 3.5762711228722275e-06 -▁Confed 3.5762711228722275e-06 -▁Geschlechts 3.5762711228722275e-06 -▁Gesundheitsschutz 3.5762711228722275e-06 -▁Nicar 3.5762711228722275e-06 -▁Proteste 3.5762711228722275e-06 -▁Rauchen 3.5762711228722275e-06 -▁belasten 3.5762711228722275e-06 -▁brochure 3.5762711228722275e-06 -▁conceived 3.5762711228722275e-06 -▁excurs 3.5762711228722275e-06 -▁flank 3.5762711228722275e-06 -arier 3.5724827000725747e-06 -bart 3.5724827000725747e-06 -drück 3.5724827000725747e-06 -mäßiger 3.5724827000725747e-06 -quality 3.5724827000725747e-06 -spät 3.5724827000725747e-06 -ämmung 3.5724827000725747e-06 -▁Establ 3.5724827000725747e-06 -▁IDE 3.5724827000725747e-06 -▁PDA 3.5724827000725747e-06 -▁Pope 3.5724827000725747e-06 -▁Publishing 3.5724827000725747e-06 -▁geltende 3.5724827000725747e-06 -▁gleichwert 3.5724827000725747e-06 -▁klingt 3.5724827000725747e-06 -▁pak 3.5724827000725747e-06 -▁spürbar 3.5724827000725747e-06 -aunch 3.5686942772729222e-06 -ermeld 3.5686942772729222e-06 -iov 3.5686942772729222e-06 -links 3.5686942772729222e-06 -merks 3.5686942772729222e-06 -präsent 3.5686942772729222e-06 -verleih 3.5686942772729222e-06 -werben 3.5686942772729222e-06 -▁Bequem 3.5686942772729222e-06 -▁Feren 3.5686942772729222e-06 -▁Gep 3.5686942772729222e-06 -▁Oost 3.5686942772729222e-06 -▁Recher 3.5686942772729222e-06 -▁Scient 3.5686942772729222e-06 -▁Sonnenunter 3.5686942772729222e-06 -▁Straßenbahn 3.5686942772729222e-06 -▁detaillierten 3.5686942772729222e-06 -▁grenzüberschreit 3.5686942772729222e-06 -▁langwier 3.5686942772729222e-06 -▁nuklearen 3.5686942772729222e-06 -▁regelm 3.5686942772729222e-06 -▁statistischen 3.5686942772729222e-06 -▁verwir 3.5686942772729222e-06 -echen 3.5649058544732693e-06 -nahms 3.5649058544732693e-06 -spektrum 3.5649058544732693e-06 -tended 3.5649058544732693e-06 -unktionen 3.5649058544732693e-06 -œ 3.5649058544732693e-06 -▁ASPs 3.5649058544732693e-06 -▁Illustr 3.5649058544732693e-06 -▁Nahrungsmitteln 3.5649058544732693e-06 -▁Siedlungen 3.5649058544732693e-06 -▁beheiz 3.5649058544732693e-06 -▁frequ 3.5649058544732693e-06 -▁künstlerische 3.5649058544732693e-06 -▁unglück 3.5649058544732693e-06 --Benutzer 3.5611174316736164e-06 -erstattung 3.5611174316736164e-06 -inien 3.5611174316736164e-06 -iode 3.5611174316736164e-06 -reten 3.5611174316736164e-06 -uzenten 3.5611174316736164e-06 -zeugnisse 3.5611174316736164e-06 -▁Entr 3.5611174316736164e-06 -▁Sauberkeit 3.5611174316736164e-06 -▁Yugoslav 3.5611174316736164e-06 -▁anscha 3.5611174316736164e-06 -▁gewalts 3.5611174316736164e-06 -▁jüd 3.5611174316736164e-06 -▁nachts 3.5611174316736164e-06 -▁nachweis 3.5611174316736164e-06 -▁zubereitet 3.5611174316736164e-06 -ength 3.5573290088739635e-06 -ronik 3.5573290088739635e-06 -▁Demonstration 3.5573290088739635e-06 -▁Lenk 3.5573290088739635e-06 -▁Neuerungen 3.5573290088739635e-06 -▁Piano 3.5573290088739635e-06 -▁Religionsfreiheit 3.5573290088739635e-06 -▁Rettung 3.5573290088739635e-06 -▁Tschechien 3.5573290088739635e-06 -▁ambiance 3.5573290088739635e-06 -▁reduz 3.5573290088739635e-06 -▁therapeut 3.5573290088739635e-06 -▁vollzogen 3.5573290088739635e-06 -▁Übern 3.5573290088739635e-06 -euten 3.553540586074311e-06 -ierbarkeit 3.553540586074311e-06 -▁.NET 3.553540586074311e-06 -▁Diss 3.553540586074311e-06 -▁Senegal 3.553540586074311e-06 -▁andernfalls 3.553540586074311e-06 -▁entir 3.553540586074311e-06 -▁gre 3.553540586074311e-06 -▁gängigen 3.553540586074311e-06 -▁threa 3.553540586074311e-06 -anka 3.5497521632746582e-06 -erwart 3.5497521632746582e-06 -itee 3.5497521632746582e-06 -uhen 3.5497521632746582e-06 -ymb 3.5497521632746582e-06 -▁Check-in 3.5497521632746582e-06 -▁Einsatzes 3.5497521632746582e-06 -▁Gremium 3.5497521632746582e-06 -▁Oran 3.5497521632746582e-06 -▁abhalten 3.5497521632746582e-06 -▁anhaltenden 3.5497521632746582e-06 -▁gründliche 3.5497521632746582e-06 -▁jew 3.5497521632746582e-06 -▁negoti 3.5497521632746582e-06 -▁ordentlich 3.5497521632746582e-06 -▁respekt 3.5497521632746582e-06 -ciano 3.5459637404750053e-06 -hebt 3.5459637404750053e-06 -institute 3.5459637404750053e-06 -pfeiler 3.5459637404750053e-06 -ushing 3.5459637404750053e-06 -▁Apartamentos 3.5459637404750053e-06 -▁Gassen 3.5459637404750053e-06 -▁Liver 3.5459637404750053e-06 -▁Notwend 3.5459637404750053e-06 -▁Pfer 3.5459637404750053e-06 -▁brechen 3.5459637404750053e-06 -▁drehen 3.5459637404750053e-06 -▁eingestuft 3.5459637404750053e-06 -▁todo 3.5459637404750053e-06 -▁verab 3.5459637404750053e-06 --Herzeg 3.5421753176753524e-06 -VE 3.5421753176753524e-06 -ophob 3.5421753176753524e-06 -▁Brut 3.5421753176753524e-06 -▁Cards 3.5421753176753524e-06 -▁Duft 3.5421753176753524e-06 -▁Entdeck 3.5421753176753524e-06 -▁Generaldir 3.5421753176753524e-06 -▁Maz 3.5421753176753524e-06 -▁Notre 3.5421753176753524e-06 -▁Rac 3.5421753176753524e-06 -▁Rechnungshofs 3.5421753176753524e-06 -▁Umlauf 3.5421753176753524e-06 -▁Zeichnungen 3.5421753176753524e-06 -▁erfreulich 3.5421753176753524e-06 -▁kleinste 3.5421753176753524e-06 -▁venez 3.5421753176753524e-06 -gos 3.5383868948757e-06 -ourmet 3.5383868948757e-06 -ument 3.5383868948757e-06 -▁BGB 3.5383868948757e-06 -▁Command 3.5383868948757e-06 -▁Durchmesser 3.5383868948757e-06 -▁Grenzwerte 3.5383868948757e-06 -▁Hond 3.5383868948757e-06 -▁LKW 3.5383868948757e-06 -▁Massenvern 3.5383868948757e-06 -▁Robot 3.5383868948757e-06 -▁Vernunft 3.5383868948757e-06 -▁erstaun 3.5383868948757e-06 -▁runden 3.5383868948757e-06 -▁vielerlei 3.5383868948757e-06 -BF 3.534598472076047e-06 -erben 3.534598472076047e-06 -hein 3.534598472076047e-06 -orie 3.534598472076047e-06 -pret 3.534598472076047e-06 -schland 3.534598472076047e-06 -▁1955 3.534598472076047e-06 -▁Akteur 3.534598472076047e-06 -▁Antibiot 3.534598472076047e-06 -▁Emotionen 3.534598472076047e-06 -▁Frontera 3.534598472076047e-06 -▁Haushaltsausschuss 3.534598472076047e-06 -▁Herstellungs 3.534598472076047e-06 -▁Marse 3.534598472076047e-06 -▁Niederlage 3.534598472076047e-06 -▁Penn 3.534598472076047e-06 -▁erhol 3.534598472076047e-06 -▁frühzeitig 3.534598472076047e-06 -▁gramm 3.534598472076047e-06 -ollars 3.5308100492763941e-06 -tum 3.5308100492763941e-06 -▁Anforderung 3.5308100492763941e-06 -▁Bewert 3.5308100492763941e-06 -▁Firewall 3.5308100492763941e-06 -▁Legitim 3.5308100492763941e-06 -▁SEC 3.5308100492763941e-06 -▁hervorragender 3.5308100492763941e-06 -▁schock 3.5308100492763941e-06 -▁studieren 3.5308100492763941e-06 -▁zauber 3.5308100492763941e-06 -mach 3.5270216264767412e-06 -queren 3.5270216264767412e-06 -▁Besonderheit 3.5270216264767412e-06 -▁Japans 3.5270216264767412e-06 -▁Kosmetik 3.5270216264767412e-06 -▁Leist 3.5270216264767412e-06 -▁Vancouver 3.5270216264767412e-06 -▁engere 3.5270216264767412e-06 -▁kurzfristigen 3.5270216264767412e-06 -▁Česk 3.5270216264767412e-06 -ategor 3.5232332036770888e-06 -▁256 3.5232332036770888e-06 -▁Antrags 3.5232332036770888e-06 -▁Chalet 3.5232332036770888e-06 -▁Electronics 3.5232332036770888e-06 -▁Geschäftsbereich 3.5232332036770888e-06 -▁Gewichts 3.5232332036770888e-06 -▁Math 3.5232332036770888e-06 -▁demzufolge 3.5232332036770888e-06 -▁möbl 3.5232332036770888e-06 -▁necess 3.5232332036770888e-06 -▁telefon 3.5232332036770888e-06 -andschaft 3.5194447808774359e-06 -sparen 3.5194447808774359e-06 -ún 3.5194447808774359e-06 -▁Ambitionen 3.5194447808774359e-06 -▁Byrne 3.5194447808774359e-06 -▁Entschließungs 3.5194447808774359e-06 -▁Fortschritten 3.5194447808774359e-06 -▁exportiert 3.5194447808774359e-06 -▁fühl 3.5194447808774359e-06 -sprüche 3.515656358077783e-06 -▁Angreif 3.515656358077783e-06 -▁Briten 3.515656358077783e-06 -▁Einträge 3.515656358077783e-06 -▁Francis 3.515656358077783e-06 -▁Gul 3.515656358077783e-06 -▁Kontrast 3.515656358077783e-06 -▁Krat 3.515656358077783e-06 -▁Marathon 3.515656358077783e-06 -▁Schach 3.515656358077783e-06 -▁Umwandlung 3.515656358077783e-06 -▁dipl 3.515656358077783e-06 -▁englischer 3.515656358077783e-06 -▁mg 3.515656358077783e-06 -▁neuem 3.515656358077783e-06 -▁obersten 3.515656358077783e-06 -▁raschen 3.515656358077783e-06 -▁verme 3.515656358077783e-06 -lieferungen 3.51186793527813e-06 -vant 3.51186793527813e-06 -▁Boote 3.51186793527813e-06 -▁Bun 3.51186793527813e-06 -▁Erb 3.51186793527813e-06 -▁Glasg 3.51186793527813e-06 -▁PNR 3.51186793527813e-06 -▁explan 3.51186793527813e-06 -▁funktionierenden 3.51186793527813e-06 -▁prep 3.51186793527813e-06 -▁punkt 3.51186793527813e-06 -▁wissens 3.51186793527813e-06 -conn 3.5080795124784776e-06 -egation 3.5080795124784776e-06 -stags 3.5080795124784776e-06 -▁Gior 3.5080795124784776e-06 -▁Hed 3.5080795124784776e-06 -▁Höhlen 3.5080795124784776e-06 -▁Internetanschluss 3.5080795124784776e-06 -▁Menschenleben 3.5080795124784776e-06 -▁Summen 3.5080795124784776e-06 -▁identisch 3.5080795124784776e-06 -▁subst 3.5080795124784776e-06 -▁tagt 3.5080795124784776e-06 -▁widerspricht 3.5080795124784776e-06 -▁zufäl 3.5080795124784776e-06 --Haus 3.5042910896788247e-06 -berühm 3.5042910896788247e-06 -iesta 3.5042910896788247e-06 -kanzler 3.5042910896788247e-06 -▁2-3 3.5042910896788247e-06 -▁Bordeaux 3.5042910896788247e-06 -▁Chávez 3.5042910896788247e-06 -▁Franç 3.5042910896788247e-06 -▁Kinderspiel 3.5042910896788247e-06 -▁MHz 3.5042910896788247e-06 -▁Myst 3.5042910896788247e-06 -▁PV 3.5042910896788247e-06 -▁Roger 3.5042910896788247e-06 -▁US-amerikanischen 3.5042910896788247e-06 -▁aktual 3.5042910896788247e-06 -▁darzustellen 3.5042910896788247e-06 -▁debattieren 3.5042910896788247e-06 -▁inneh 3.5042910896788247e-06 -▁stehe 3.5042910896788247e-06 -itate 3.5005026668791718e-06 -lasten 3.5005026668791718e-06 -ruhe 3.5005026668791718e-06 -▁11.30 3.5005026668791718e-06 -▁Einst 3.5005026668791718e-06 -▁Erwähnung 3.5005026668791718e-06 -▁Ethik 3.5005026668791718e-06 -▁Freed 3.5005026668791718e-06 -▁Gesetzgeb 3.5005026668791718e-06 -▁González 3.5005026668791718e-06 -▁Heutzutage 3.5005026668791718e-06 -▁Ingenieur 3.5005026668791718e-06 -▁Seel 3.5005026668791718e-06 -▁Staatsangehör 3.5005026668791718e-06 -▁Tuber 3.5005026668791718e-06 -▁schwachen 3.5005026668791718e-06 -gnung 3.4967142440795189e-06 -▁1929 3.4967142440795189e-06 -▁Bedürf 3.4967142440795189e-06 -▁Dringlichkeits 3.4967142440795189e-06 -▁Edelstahl 3.4967142440795189e-06 -▁Kriegsverb 3.4967142440795189e-06 -▁colle 3.4967142440795189e-06 -▁gerechten 3.4967142440795189e-06 -▁restruct 3.4967142440795189e-06 -▁sympt 3.4967142440795189e-06 -▁transparenten 3.4967142440795189e-06 -etreten 3.4929258212798665e-06 -ördert 3.4929258212798665e-06 -ů 3.4929258212798665e-06 -▁Fuerte 3.4929258212798665e-06 -▁Gewissen 3.4929258212798665e-06 -▁Gütern 3.4929258212798665e-06 -▁Träume 3.4929258212798665e-06 -▁nationales 3.4929258212798665e-06 -▁polic 3.4929258212798665e-06 -▁sog. 3.4929258212798665e-06 -▁strategisch 3.4929258212798665e-06 -▁unterm 3.4929258212798665e-06 -▁urheber 3.4929258212798665e-06 -▁verdeut 3.4929258212798665e-06 -▁vervollständ 3.4929258212798665e-06 -▁vorantreiben 3.4929258212798665e-06 -erkrankungen 3.4891373984802136e-06 -ς 3.4891373984802136e-06 -▁Aktionäre 3.4891373984802136e-06 -▁Erleb 3.4891373984802136e-06 -▁Stammzellen 3.4891373984802136e-06 -▁blockieren 3.4891373984802136e-06 -▁erfahrene 3.4891373984802136e-06 -▁fürs 3.4891373984802136e-06 -▁verabschiedeten 3.4891373984802136e-06 -▁weiße 3.4891373984802136e-06 -▁Überraschung 3.4891373984802136e-06 -loß 3.4853489756805607e-06 -▁Halbjahr 3.4853489756805607e-06 -▁MyS 3.4853489756805607e-06 -▁Paare 3.4853489756805607e-06 -▁komplizierte 3.4853489756805607e-06 -cksilber 3.4815605528809082e-06 -▁Behinderten 3.4815605528809082e-06 -▁Kompat 3.4815605528809082e-06 -▁Kopien 3.4815605528809082e-06 -▁Tschech 3.4815605528809082e-06 -▁anstehenden 3.4815605528809082e-06 -▁ausgebildet 3.4815605528809082e-06 -▁dispos 3.4815605528809082e-06 -▁unvergessliche 3.4815605528809082e-06 -▁überflüssig 3.4815605528809082e-06 -gült 3.4777721300812553e-06 -republik 3.4777721300812553e-06 -▁Eingriff 3.4777721300812553e-06 -▁Immobilie 3.4777721300812553e-06 -▁Krit 3.4777721300812553e-06 -▁Oct 3.4777721300812553e-06 -▁Offenbar 3.4777721300812553e-06 -▁Plattformen 3.4777721300812553e-06 -▁Schröder 3.4777721300812553e-06 -▁Schwäch 3.4777721300812553e-06 -▁ausfallen 3.4777721300812553e-06 -▁dementsprechend 3.4777721300812553e-06 -▁großzügigen 3.4777721300812553e-06 -anschluss 3.4739837072816024e-06 -nehmern 3.4739837072816024e-06 -regulierung 3.4739837072816024e-06 -▁Kartoff 3.4739837072816024e-06 -▁Kritiker 3.4739837072816024e-06 -▁Malt 3.4739837072816024e-06 -▁Rehab 3.4739837072816024e-06 -▁admin 3.4739837072816024e-06 -▁besetzten 3.4739837072816024e-06 -▁besprechen 3.4739837072816024e-06 -▁verbleibenden 3.4739837072816024e-06 -attungen 3.4701952844819495e-06 -▁Ausgehend 3.4701952844819495e-06 -▁Befürworter 3.4701952844819495e-06 -▁Schreibt 3.4701952844819495e-06 -▁einzul 3.4701952844819495e-06 -▁fantastische 3.4701952844819495e-06 -▁geistige 3.4701952844819495e-06 -▁lebensl 3.4701952844819495e-06 -azin 3.4664068616822971e-06 -▁Kämpfe 3.4664068616822971e-06 -▁Lese 3.4664068616822971e-06 -▁Mandats 3.4664068616822971e-06 -▁entstandenen 3.4664068616822971e-06 -▁erschwing 3.4664068616822971e-06 -▁glaubwürdig 3.4664068616822971e-06 -▁inject 3.4664068616822971e-06 -fet 3.4626184388826442e-06 -ôte 3.4626184388826442e-06 -▁Beitrittsländer 3.4626184388826442e-06 -▁Berufung 3.4626184388826442e-06 -▁Freig 3.4626184388826442e-06 -▁Komment 3.4626184388826442e-06 -▁Phänom 3.4626184388826442e-06 -▁ausreichenden 3.4626184388826442e-06 -▁fantastischen 3.4626184388826442e-06 -▁tibet 3.4626184388826442e-06 -▁vereinfach 3.4626184388826442e-06 -rist 3.4588300160829913e-06 -ücksicht 3.4588300160829913e-06 -▁Bäder 3.4588300160829913e-06 -▁Daran 3.4588300160829913e-06 -▁Fahrplan 3.4588300160829913e-06 -▁Glanz 3.4588300160829913e-06 -▁Kommand 3.4588300160829913e-06 -▁Londoner 3.4588300160829913e-06 -▁Marktführer 3.4588300160829913e-06 -▁Neust 3.4588300160829913e-06 -▁Zivilbevölkerung 3.4588300160829913e-06 -▁bedacht 3.4588300160829913e-06 -▁begünstigt 3.4588300160829913e-06 -▁doppelten 3.4588300160829913e-06 -▁mancher 3.4588300160829913e-06 -▁veh 3.4588300160829913e-06 -▁zwingt 3.4588300160829913e-06 -LAF 3.4550415932833384e-06 -▁Inhaft 3.4550415932833384e-06 -▁einzub 3.4550415932833384e-06 -▁lauten 3.4550415932833384e-06 -▁niem 3.4550415932833384e-06 -▁ultim 3.4550415932833384e-06 -ecut 3.4512531704836859e-06 -ordan 3.4512531704836859e-06 -▁Anleihen 3.4512531704836859e-06 -▁Bügelservice 3.4512531704836859e-06 -▁Fremdenfeindlichkeit 3.4512531704836859e-06 -▁Haushaltslinie 3.4512531704836859e-06 -▁Konzerne 3.4512531704836859e-06 -▁Starten 3.4512531704836859e-06 -▁Stornierungen 3.4512531704836859e-06 -▁impos 3.4512531704836859e-06 -▁vorankommen 3.4512531704836859e-06 -▁weitergeben 3.4512531704836859e-06 --old 3.447464747684033e-06 -vorsorge 3.447464747684033e-06 -zeitig 3.447464747684033e-06 -▁Fassade 3.447464747684033e-06 -▁Föder 3.447464747684033e-06 -▁Hügeln 3.447464747684033e-06 -▁Laptop 3.447464747684033e-06 -▁Stabilitäts 3.447464747684033e-06 -▁Stipend 3.447464747684033e-06 -▁betrachte 3.447464747684033e-06 -▁unangen 3.447464747684033e-06 -▁unbegrenz 3.447464747684033e-06 -▁IP-Adresse 3.44367632488438e-06 -▁Jubiläum 3.44367632488438e-06 -▁Meldungen 3.44367632488438e-06 -▁Präzedenz 3.44367632488438e-06 -▁Unterbrechung 3.44367632488438e-06 -▁Wissenschaftlern 3.44367632488438e-06 -▁erlangt 3.44367632488438e-06 -▁konfiguriert 3.44367632488438e-06 -▁unklar 3.44367632488438e-06 -▁verhält 3.44367632488438e-06 -▁wieviel 3.44367632488438e-06 -alin 3.4398879020847273e-06 -▁Allerg 3.4398879020847273e-06 -▁Furcht 3.4398879020847273e-06 -▁Hygiene 3.4398879020847273e-06 -▁Mitgliedstaats 3.4398879020847273e-06 -▁Vorbereit 3.4398879020847273e-06 -▁cosmet 3.4398879020847273e-06 -▁dür 3.4398879020847273e-06 -▁unterrichten 3.4398879020847273e-06 -just 3.4360994792850748e-06 -örter 3.4360994792850748e-06 -▁Erwart 3.4360994792850748e-06 -▁Expansion 3.4360994792850748e-06 -▁Hervor 3.4360994792850748e-06 -▁Normalerweise 3.4360994792850748e-06 -▁beispiell 3.4360994792850748e-06 -▁besonderem 3.4360994792850748e-06 -▁dreht 3.4360994792850748e-06 -▁eingebrachten 3.4360994792850748e-06 -▁erstaunlich 3.4360994792850748e-06 -▁geringere 3.4360994792850748e-06 -▁Ökonomie 3.4360994792850748e-06 -fangs 3.4323110564854219e-06 -▁Dossier 3.4323110564854219e-06 -▁Flügel 3.4323110564854219e-06 -▁Gebiets 3.4323110564854219e-06 -▁Rahmenrichtlinie 3.4323110564854219e-06 -▁bewährte 3.4323110564854219e-06 -▁objektiv 3.4323110564854219e-06 -▁schar 3.4323110564854219e-06 -▁spannende 3.4323110564854219e-06 -▁unbeg 3.4323110564854219e-06 -falt 3.428522633685769e-06 -▁Fischereisektor 3.428522633685769e-06 -▁Fluggäste 3.428522633685769e-06 -▁Naturpark 3.428522633685769e-06 -▁Transpar 3.428522633685769e-06 -▁anderweit 3.428522633685769e-06 -▁bedroh 3.428522633685769e-06 -▁hor 3.428522633685769e-06 -▁worüber 3.428522633685769e-06 -nutzen 3.4247342108861161e-06 -▁Buchungen 3.4247342108861161e-06 -▁Festland 3.4247342108861161e-06 -▁Karibik 3.4247342108861161e-06 -▁Künd 3.4247342108861161e-06 -▁Veröffentlichungen 3.4247342108861161e-06 -▁bürgerlichen 3.4247342108861161e-06 -▁fragte 3.4247342108861161e-06 -▁herausgestellt 3.4247342108861161e-06 -▁höre 3.4247342108861161e-06 -▁postpon 3.4247342108861161e-06 -▁qualifiziert 3.4247342108861161e-06 -▁resc 3.4247342108861161e-06 -▁sportlichen 3.4247342108861161e-06 -nor 3.4209457880864637e-06 -▁Anstrengung 3.4209457880864637e-06 -▁Gewebe 3.4209457880864637e-06 -▁Panoramabl 3.4209457880864637e-06 -▁Papst 3.4209457880864637e-06 -▁Rahmenbeschluss 3.4209457880864637e-06 -fore 3.4171573652868108e-06 -▁Getr 3.4171573652868108e-06 -▁Schattenberichterstatter 3.4171573652868108e-06 -▁dictators 3.4171573652868108e-06 -▁implementiert 3.4171573652868108e-06 -▁kopiert 3.4171573652868108e-06 -▁lebte 3.4171573652868108e-06 -▁versenden 3.4171573652868108e-06 -bridge 3.4133689424871579e-06 -laß 3.4133689424871579e-06 -▁Herangehensweise 3.4133689424871579e-06 -▁Sat-TV 3.4133689424871579e-06 -▁anhaltende 3.4133689424871579e-06 -▁annähern 3.4133689424871579e-06 -▁ansteigen 3.4133689424871579e-06 -▁broadc 3.4133689424871579e-06 -▁vorgebracht 3.4133689424871579e-06 -▁Senden 3.409580519687505e-06 -▁Tunis 3.409580519687505e-06 -▁Verantwortlichkeiten 3.409580519687505e-06 -▁jetz 3.409580519687505e-06 -▁zuverlässigen 3.409580519687505e-06 -allel 3.4057920968878525e-06 -ecutive 3.4057920968878525e-06 -wertet 3.4057920968878525e-06 -ŕ 3.4057920968878525e-06 -▁Fri 3.4057920968878525e-06 -▁Intelligenz 3.4057920968878525e-06 -▁Verhütung 3.4057920968878525e-06 -▁begründen 3.4057920968878525e-06 -chsel 3.4020036740881996e-06 -irc 3.4020036740881996e-06 -reater 3.4020036740881996e-06 -▁Bruttoinland 3.4020036740881996e-06 -▁Kuchen 3.4020036740881996e-06 -▁geforderten 3.4020036740881996e-06 -▁strukturelle 3.4020036740881996e-06 -poration 3.3982152512885467e-06 -▁Fischbestände 3.3982152512885467e-06 -▁Handtücher 3.3982152512885467e-06 -▁Mütter 3.3982152512885467e-06 -▁Schweigen 3.3982152512885467e-06 -▁gültige 3.3982152512885467e-06 -▁spezieller 3.3982152512885467e-06 -beschreibung 3.3944268284888938e-06 -body 3.3944268284888938e-06 -▁Bean 3.3944268284888938e-06 -▁Befried 3.3944268284888938e-06 -▁Chin 3.3944268284888938e-06 -▁Kroat 3.3944268284888938e-06 -▁Muttersprache 3.3944268284888938e-06 -▁Tot 3.3944268284888938e-06 -▁abzubauen 3.3944268284888938e-06 -▁angestrebt 3.3944268284888938e-06 -▁conn 3.3944268284888938e-06 -▁distribut 3.3944268284888938e-06 -▁erkläre 3.3944268284888938e-06 -▁gefeiert 3.3944268284888938e-06 -▁tät 3.3944268284888938e-06 -▁zurückzukehren 3.3944268284888938e-06 -ZE 3.3906384056892414e-06 -▁bewährt 3.3906384056892414e-06 -▁detr 3.3906384056892414e-06 -▁ehemaliger 3.3906384056892414e-06 -▁Cad 3.3868499828895885e-06 -▁Generaldirektion 3.3868499828895885e-06 -▁marg 3.3868499828895885e-06 -▁trug 3.3868499828895885e-06 -agger 3.3830615600899356e-06 -indungs 3.3830615600899356e-06 -▁Konzer 3.3830615600899356e-06 -▁Satell 3.3830615600899356e-06 -▁Vergn 3.3830615600899356e-06 -▁dän 3.3830615600899356e-06 -▁folg 3.3830615600899356e-06 -▁relev 3.3830615600899356e-06 -▁wesentliches 3.3830615600899356e-06 -obal 3.3792731372902827e-06 -ocktails 3.3792731372902827e-06 -▁Kette 3.3792731372902827e-06 -▁Treat 3.3792731372902827e-06 -▁Treibhaus 3.3792731372902827e-06 -hnung 3.3754847144906302e-06 -ublik 3.3754847144906302e-06 -▁crit 3.3754847144906302e-06 -ensible 3.3716962916909773e-06 -h. 3.3716962916909773e-06 -known 3.3716962916909773e-06 -ployment 3.3716962916909773e-06 -▁kommerzi 3.3716962916909773e-06 -ertung 3.3679078688913244e-06 -klass 3.3679078688913244e-06 -ößt 3.3679078688913244e-06 -▁Virt 3.3679078688913244e-06 -izar 3.3641194460916715e-06 -▁Immun 3.3641194460916715e-06 -▁Deleg 3.3603310232920191e-06 -▁Fakt 3.3603310232920191e-06 -▁diesbezü 3.3603310232920191e-06 -QL 3.3565426004923662e-06 -amilie 3.3565426004923662e-06 -ilom 3.3565426004923662e-06 -Ç 3.3565426004923662e-06 -▁Sov 3.3565426004923662e-06 -▁entwick 3.3565426004923662e-06 -▁satell 3.3565426004923662e-06 -▁spok 3.3565426004923662e-06 -ewall 3.3527541776927133e-06 -ounge 3.3527541776927133e-06 -uers 3.3527541776927133e-06 -▁Amaz 3.3527541776927133e-06 -▁invol 3.3527541776927133e-06 -▁proport 3.3527541776927133e-06 -draul 3.3489657548930604e-06 -bends 3.3451773320934079e-06 -▁Künst 3.3451773320934079e-06 -▁Nam 3.3451773320934079e-06 -andelt 3.341388909293755e-06 -ught 3.341388909293755e-06 -urities 3.341388909293755e-06 -Р 3.3376004864941022e-06 -athon 3.3338120636944493e-06 -ingham 3.3338120636944493e-06 -istribution 3.3338120636944493e-06 -▁Admin 3.3338120636944493e-06 -▁Präm 3.3338120636944493e-06 -ilot 3.3300236408947968e-06 -▁challen 3.3300236408947968e-06 -ensicht 3.3262352180951439e-06 -indlichkeit 3.3262352180951439e-06 -▁Schattenberichterst 3.3262352180951439e-06 -▁Musl 3.322446795295491e-06 -▁angekünd 3.322446795295491e-06 -▁simultane 3.322446795295491e-06 -eases 3.3186583724958381e-06 -här 3.3186583724958381e-06 -▁Konsult 3.3186583724958381e-06 -▁Symb 3.3186583724958381e-06 -lastung 3.3148699496961857e-06 -okument 3.3148699496961857e-06 -verständlich 3.3148699496961857e-06 -▁tom 3.3148699496961857e-06 -itale 3.3110815268965328e-06 -lauben 3.3110815268965328e-06 -üben 3.3110815268965328e-06 -▁Kilom 3.3110815268965328e-06 -▁contin 3.30729310409688e-06 -ainer 3.303504681297227e-06 -bezogen 3.303504681297227e-06 -obility 3.303504681297227e-06 -▁endeav 3.303504681297227e-06 -▁mur 3.303504681297227e-06 -ählten 3.2997162584975745e-06 -▁Gü 3.2997162584975745e-06 -▁Sund 3.2997162584975745e-06 -▁embar 3.2997162584975745e-06 -▁landwirtschaft 3.2997162584975745e-06 -▁Lou 3.2959278356979216e-06 -▁Zert 3.2959278356979216e-06 -▁manufact 3.2959278356979216e-06 -iban 3.2921394128982687e-06 -▁Bearbeit 3.2921394128982687e-06 -▁Chal 3.2921394128982687e-06 -gearbeitet 3.2883509900986163e-06 -insen 3.2883509900986163e-06 -okratie 3.2883509900986163e-06 -▁Anwesen 3.2883509900986163e-06 -▁Fore 3.2845625672989634e-06 -▁Französ 3.2845625672989634e-06 -itteln 3.2807741444993105e-06 -selves 3.2807741444993105e-06 -▁Anh 3.2807741444993105e-06 -▁bz 3.2807741444993105e-06 -→ 3.2769857216996576e-06 -isode 3.2731972989000051e-06 -tle 3.2731972989000051e-06 -▁umge 3.2731972989000051e-06 -▁Überwach 3.2731972989000051e-06 -unting 3.2694088761003522e-06 -rael 3.2618320305010464e-06 -▁Ox 3.2618320305010464e-06 -▁exec 3.2618320305010464e-06 -▁mittler 3.2618320305010464e-06 -▁photograp 3.258043607701394e-06 -▁zerst 3.258043607701394e-06 -isms 3.2542551849017411e-06 -mittels 3.2542551849017411e-06 -▁Freil 3.2542551849017411e-06 -▁ausdrück 3.2504667621020882e-06 -iples 3.2466783393024353e-06 -▁Tyr 3.2466783393024353e-06 -▁Wurz 3.2466783393024353e-06 -▁exclus 3.2466783393024353e-06 -▁recru 3.2466783393024353e-06 -now 3.2428899165027828e-06 -ählen 3.2428899165027828e-06 -▁geziel 3.2428899165027828e-06 -chester 3.23910149370313e-06 -oted 3.23910149370313e-06 -sthet 3.235313070903477e-06 -▁describ 3.235313070903477e-06 -▁langfrist 3.235313070903477e-06 -▁städt 3.235313070903477e-06 -istration 3.2315246481038242e-06 -leister 3.2315246481038242e-06 -reisende 3.2315246481038242e-06 -schwung 3.2315246481038242e-06 -▁memor 3.2315246481038242e-06 -▁replac 3.2315246481038242e-06 -▁Etik 3.2277362253041717e-06 -▁Moz 3.2277362253041717e-06 -▁grenzüber 3.2277362253041717e-06 -cludes 3.2239478025045188e-06 -dress 3.2239478025045188e-06 -enty 3.2239478025045188e-06 -▁begleit 3.2239478025045188e-06 -▁geeign 3.2239478025045188e-06 -▁propriet 3.2239478025045188e-06 -▁ruh 3.2239478025045188e-06 -OME 3.2201593797048659e-06 -arbeitet 3.2201593797048659e-06 -arity 3.2201593797048659e-06 -uxe 3.2201593797048659e-06 -▁begeg 3.2201593797048659e-06 -▁democrat 3.2201593797048659e-06 -▁distingu 3.2201593797048659e-06 -aign 3.2125825341055605e-06 -ilosoph 3.2125825341055605e-06 -▁disadvant 3.2125825341055605e-06 -ething 3.2087941113059077e-06 -istical 3.2087941113059077e-06 -▁depos 3.2087941113059077e-06 -ploy 3.2050056885062548e-06 -▁consequ 3.2050056885062548e-06 -▁häuf 3.2050056885062548e-06 -itts 3.2012172657066019e-06 -standen 3.2012172657066019e-06 -▁drit 3.2012172657066019e-06 -asil 3.1974288429069494e-06 -genz 3.1974288429069494e-06 -▁Zimmers 3.1974288429069494e-06 -▁avail 3.1974288429069494e-06 -▁vict 3.1974288429069494e-06 -▁verur 3.1936404201072965e-06 -ildet 3.1898519973076436e-06 -wegs 3.1898519973076436e-06 -akischen 3.1860635745079907e-06 -reben 3.1860635745079907e-06 -▁Contin 3.1860635745079907e-06 -▁Gren 3.1860635745079907e-06 -dessen 3.1822751517083383e-06 -isol 3.1822751517083383e-06 -issens 3.1822751517083383e-06 -kas 3.1822751517083383e-06 -ulous 3.1822751517083383e-06 -▁Präz 3.1822751517083383e-06 -ن 3.1746983061090325e-06 -▁indigen 3.1746983061090325e-06 -▁opport 3.1746983061090325e-06 -▁Konzentr 3.1709098833093796e-06 -fassungs 3.1671214605097271e-06 -kommunik 3.1671214605097271e-06 -▁bekomm 3.1671214605097271e-06 -▁letz 3.1671214605097271e-06 -▁obser 3.1671214605097271e-06 -onz 3.1633330377100742e-06 -ã 3.1633330377100742e-06 -▁Proc 3.1633330377100742e-06 -messer 3.1595446149104213e-06 -schließ 3.1595446149104213e-06 -tragung 3.1595446149104213e-06 -ί 3.1595446149104213e-06 -▁Bang 3.1595446149104213e-06 -▁Chir 3.1595446149104213e-06 -▁Dut 3.1595446149104213e-06 -▁erstklass 3.1595446149104213e-06 -▁exzellent 3.1595446149104213e-06 -onomie 3.1557561921107684e-06 -spart 3.1557561921107684e-06 -unsten 3.1557561921107684e-06 -ystal 3.1557561921107684e-06 -▁Abhäng 3.1557561921107684e-06 -▁aufer 3.1557561921107684e-06 -▁famil 3.1557561921107684e-06 -zuschließen 3.151967769311116e-06 -▁Dolom 3.151967769311116e-06 -▁Mosk 3.151967769311116e-06 -▁compliment 3.151967769311116e-06 -▁indic 3.151967769311116e-06 -enze 3.1481793465114631e-06 -spezif 3.1481793465114631e-06 -ökonom 3.1481793465114631e-06 -▁Koh 3.1481793465114631e-06 -▁inhab 3.1481793465114631e-06 -folio 3.14439092371181e-06 -oleranz 3.14439092371181e-06 -▁config 3.14439092371181e-06 -▁econom 3.14439092371181e-06 -▁eindeut 3.1406025009121573e-06 -achtungen 3.1368140781125048e-06 -▁Controll 3.1368140781125048e-06 -▁Schü 3.1368140781125048e-06 -iversity 3.1330256553128519e-06 -▁Alph 3.1330256553128519e-06 -▁Gebä 3.1330256553128519e-06 -▁decis 3.1330256553128519e-06 --min 3.1292372325131991e-06 -scheinlich 3.1292372325131991e-06 -▁Rö 3.1292372325131991e-06 -stable 3.1254488097135462e-06 -yll 3.1254488097135462e-06 -▁Ausfl 3.1254488097135462e-06 -▁appropri 3.1254488097135462e-06 -▁pie 3.1254488097135462e-06 -gewiesen 3.1216603869138937e-06 -ilf 3.1216603869138937e-06 -leih 3.1216603869138937e-06 -▁Bürgerbeauftrag 3.1216603869138937e-06 -airo 3.1178719641142408e-06 -▁neol 3.1178719641142408e-06 -fare 3.1140835413145879e-06 -isely 3.1140835413145879e-06 -▁aufricht 3.1140835413145879e-06 -▁volunt 3.1140835413145879e-06 -alian 3.1102951185149354e-06 -▁Pun 3.1102951185149354e-06 -▁devast 3.1102951185149354e-06 -▁weiterentwick 3.1027182729156297e-06 -pap 3.0989298501159768e-06 -brea 3.0951414273163243e-06 -schloss 3.0951414273163243e-06 -▁Represent 3.0951414273163243e-06 -▁anwes 3.0951414273163243e-06 -bieter 3.0913530045166714e-06 -ivation 3.0913530045166714e-06 -ordination 3.0913530045166714e-06 -teid 3.0913530045166714e-06 -ähern 3.0913530045166714e-06 -▁broch 3.0913530045166714e-06 -▁verabschied 3.0913530045166714e-06 -alus 3.0875645817170185e-06 -cend 3.0875645817170185e-06 -vorsitzen 3.0875645817170185e-06 -▁Chines 3.0875645817170185e-06 -▁Zustell 3.0875645817170185e-06 -ince 3.0837761589173656e-06 -zielle 3.0837761589173656e-06 -▁münd 3.0837761589173656e-06 -itarian 3.0799877361177132e-06 -▁Inv 3.0799877361177132e-06 -▁consolid 3.0799877361177132e-06 -racy 3.0761993133180603e-06 -undern 3.0761993133180603e-06 -zte 3.0761993133180603e-06 -▁ö 3.0761993133180603e-06 -eigen 3.0724108905184074e-06 -inzips 3.0724108905184074e-06 -unsch 3.0686224677187545e-06 -ō 3.0686224677187545e-06 -▁Jed 3.0686224677187545e-06 -▁Minim 3.0686224677187545e-06 -▁sever 3.064834044919102e-06 -Script 3.0610456221194491e-06 -sichtig 3.0610456221194491e-06 -trem 3.0610456221194491e-06 -ügt 3.0610456221194491e-06 -▁behin 3.0610456221194491e-06 -▁ermut 3.0610456221194491e-06 -▁künstler 3.0610456221194491e-06 -ücher 3.0572571993197962e-06 -vement 3.0534687765201433e-06 -▁bilater 3.0534687765201433e-06 -▁erz 3.0534687765201433e-06 -▁Übersetz 3.0534687765201433e-06 -− 3.0496803537204909e-06 -▁beher 3.0496803537204909e-06 -▁bewaff 3.0496803537204909e-06 -▁bicy 3.0496803537204909e-06 -ergeb 3.045891930920838e-06 -▁Alger 3.045891930920838e-06 -▁Mig 3.045891930920838e-06 -▁Moroc 3.045891930920838e-06 -▁griech 3.045891930920838e-06 -▁Behinder 3.0421035081211851e-06 -▁Speich 3.0421035081211851e-06 -▁Verbesser 3.0421035081211851e-06 --run 3.0383150853215322e-06 -▁Ira 3.0383150853215322e-06 -▁cert 3.0383150853215322e-06 -▁lad 3.0383150853215322e-06 -▁wed 3.0345266625218797e-06 -abwe 3.0307382397222268e-06 -inein 3.0307382397222268e-06 -▁imbal 3.0307382397222268e-06 -issance 3.0269498169225739e-06 -mögen 3.0231613941229211e-06 -pg 3.0231613941229211e-06 -İ 3.0231613941229211e-06 -klärung 3.0193729713232686e-06 -▁Curr 3.0193729713232686e-06 -▁celebr 3.0193729713232686e-06 -▁enchant 3.0193729713232686e-06 -▁geden 3.0193729713232686e-06 -▁sour 3.0193729713232686e-06 -estaurant 3.0155845485236157e-06 -icans 3.0155845485236157e-06 -▁Agricult 3.0155845485236157e-06 -▁Anhör 3.0155845485236157e-06 -▁Eurost 3.0155845485236157e-06 -▁schuld 3.0155845485236157e-06 -ournal 3.0117961257239628e-06 -rohungen 3.0117961257239628e-06 -µ 3.0117961257239628e-06 -▁Glaub 3.0117961257239628e-06 -wände 3.00800770292431e-06 -öp 3.00800770292431e-06 -▁Puert 3.00800770292431e-06 -▁constr 3.00800770292431e-06 -▁zusätz 3.00800770292431e-06 -XI 3.0042192801246574e-06 -iertel 3.0042192801246574e-06 -insula 3.0042192801246574e-06 -ühungen 3.0042192801246574e-06 -▁hilf 3.0042192801246574e-06 -▁overw 3.0042192801246574e-06 -ooms 3.0004308573250045e-06 -ī 3.0004308573250045e-06 -▁korre 3.0004308573250045e-06 -▁unverzicht 3.0004308573250045e-06 -▁ww 3.0004308573250045e-06 -ategory 2.9966424345253517e-06 -hetor 2.9966424345253517e-06 -idelines 2.9966424345253517e-06 -▁Entschäd 2.9928540117256988e-06 -▁Pest 2.9928540117256988e-06 -▁Zinss 2.9928540117256988e-06 -kauft 2.9890655889260463e-06 -▁Certain 2.9890655889260463e-06 -▁introd 2.9890655889260463e-06 -▁irregular 2.9890655889260463e-06 -Ø 2.9852771661263934e-06 -ämpft 2.9852771661263934e-06 -▁amt 2.9852771661263934e-06 -aue 2.9814887433267405e-06 -▁Balk 2.9814887433267405e-06 -▁kle 2.9814887433267405e-06 -ächst 2.9777003205270876e-06 -▁competen 2.9777003205270876e-06 -▁jähr 2.9777003205270876e-06 -ischung 2.9739118977274352e-06 -▁außergewöhn 2.9739118977274352e-06 -▁kompliz 2.9739118977274352e-06 -▁wöch 2.9739118977274352e-06 -iament 2.9701234749277823e-06 -▁Nichtraucher 2.9701234749277823e-06 -▁vermut 2.9701234749277823e-06 -rohung 2.9663350521281294e-06 -▁Behaupt 2.9663350521281294e-06 -▁Medikament 2.9663350521281294e-06 -▁enthusi 2.9663350521281294e-06 -▁einzigart 2.9625466293284765e-06 -▁lif 2.9625466293284765e-06 -yles 2.958758206528824e-06 -▁Pac 2.958758206528824e-06 -chmark 2.9549697837291711e-06 -mant 2.9549697837291711e-06 -okraten 2.9549697837291711e-06 -▁Kör 2.9549697837291711e-06 -▁damal 2.9549697837291711e-06 -estens 2.9511813609295182e-06 -like 2.9511813609295182e-06 -▁tran 2.9511813609295182e-06 -LR 2.9473929381298653e-06 -gänger 2.9473929381298653e-06 -kre 2.9473929381298653e-06 -П 2.9473929381298653e-06 -▁chines 2.9473929381298653e-06 -▁lingu 2.9473929381298653e-06 -▁nic 2.9473929381298653e-06 -▁Ökonom 2.9473929381298653e-06 -NS 2.9436045153302129e-06 -▁herkömm 2.9436045153302129e-06 -▁wom 2.9436045153302129e-06 -plus 2.93981609253056e-06 -zufügen 2.93981609253056e-06 -rough 2.9360276697309071e-06 -▁ausgezeichn 2.9360276697309071e-06 -itiative 2.9284508241316017e-06 -kind 2.9284508241316017e-06 -spricht 2.9284508241316017e-06 -while 2.9284508241316017e-06 -▁Aufent 2.9284508241316017e-06 -▁Poss 2.9284508241316017e-06 -▁bewähr 2.9284508241316017e-06 -▁cloth 2.9284508241316017e-06 -eshalb 2.9246624013319488e-06 -▁constra 2.9246624013319488e-06 -▁corresp 2.9246624013319488e-06 -▁feat 2.9246624013319488e-06 -ionalität 2.9208739785322959e-06 -iveau 2.9208739785322959e-06 -▁Eure 2.9208739785322959e-06 -auso 2.9170855557326435e-06 -▁aspir 2.9170855557326435e-06 -▁heav 2.9170855557326435e-06 -▁Horiz 2.9132971329329906e-06 -▁Ukra 2.9132971329329906e-06 -beziehung 2.9095087101333377e-06 -kretär 2.9095087101333377e-06 -wise 2.9095087101333377e-06 -▁geschmack 2.9095087101333377e-06 -▁sod 2.9095087101333377e-06 -▁Interpret 2.9057202873336848e-06 -nerg 2.9019318645340323e-06 -ustom 2.9019318645340323e-06 -▁Cred 2.8981434417343794e-06 -▁engl 2.8981434417343794e-06 -strafe 2.8943550189347266e-06 -ährung 2.8943550189347266e-06 -▁Comput 2.8943550189347266e-06 -▁arbitr 2.8943550189347266e-06 -gelassen 2.8905665961350737e-06 -iech 2.8905665961350737e-06 -Gu 2.8867781733354212e-06 -berichterst 2.8867781733354212e-06 -uay 2.8867781733354212e-06 -▁entw 2.8867781733354212e-06 -criptions 2.8829897505357683e-06 -lege 2.8829897505357683e-06 -pendent 2.8829897505357683e-06 -▁appreci 2.8829897505357683e-06 -gencies 2.8792013277361154e-06 -hai 2.8792013277361154e-06 -▁bedro 2.8792013277361154e-06 -▁diagn 2.8792013277361154e-06 -▁extraordin 2.8792013277361154e-06 -istrat 2.87162448213681e-06 -iterran 2.87162448213681e-06 -jud 2.87162448213681e-06 -ml 2.87162448213681e-06 -▁Leh 2.87162448213681e-06 -▁Okt 2.87162448213681e-06 -▁asi 2.87162448213681e-06 -inzip 2.8678360593371572e-06 -katastrop 2.8678360593371572e-06 -ünft 2.8678360593371572e-06 -engen 2.8640476365375043e-06 -minuten 2.8640476365375043e-06 -▁kontakt 2.8640476365375043e-06 -B. 2.8602592137378514e-06 -gebote 2.8564707909381989e-06 -▁Initi 2.8564707909381989e-06 -▁Überrasch 2.8564707909381989e-06 -▁konzip 2.852682368138546e-06 -yo 2.8488939453388931e-06 -öglich 2.8488939453388931e-06 -▁Lic 2.8488939453388931e-06 -geschoss 2.8451055225392402e-06 -ulegen 2.8451055225392402e-06 -▁bir 2.8451055225392402e-06 -gehalten 2.8413170997395878e-06 -▁ander 2.8413170997395878e-06 -▁tang 2.8413170997395878e-06 -clear 2.8375286769399349e-06 -imens 2.8375286769399349e-06 -scheid 2.8375286769399349e-06 -wirk 2.8375286769399349e-06 -▁Zerst 2.8375286769399349e-06 -▁accompl 2.8375286769399349e-06 -ctual 2.833740254140282e-06 -orough 2.833740254140282e-06 -date 2.8299518313406291e-06 -▁spectac 2.8299518313406291e-06 -▁Germ 2.8261634085409766e-06 -▁academ 2.8261634085409766e-06 -▁bene 2.8261634085409766e-06 -lechts 2.8223749857413237e-06 -mert 2.8223749857413237e-06 -schrank 2.8223749857413237e-06 -ر 2.8223749857413237e-06 -▁Religions 2.8223749857413237e-06 -▁Reicht 2.8185865629416708e-06 -▁Thyssen 2.8185865629416708e-06 -▁ehem 2.8185865629416708e-06 -Ђ 2.8147981401420179e-06 -▁bemer 2.8147981401420179e-06 --hour 2.8110097173423655e-06 -ignen 2.8110097173423655e-06 -istiken 2.8110097173423655e-06 -atile 2.8072212945427126e-06 -▁Gastr 2.8034328717430597e-06 -▁jeg 2.8034328717430597e-06 -istrib 2.7996444489434068e-06 -kun 2.7996444489434068e-06 -odex 2.7996444489434068e-06 -zeption 2.7996444489434068e-06 -▁stet 2.7996444489434068e-06 -ayers 2.7958560261437543e-06 -etime 2.7958560261437543e-06 -oyal 2.7958560261437543e-06 -ränkt 2.7958560261437543e-06 -tei 2.7958560261437543e-06 -▁iss 2.7958560261437543e-06 -izit 2.7920676033441014e-06 -äsident 2.7920676033441014e-06 -▁Jugendher 2.7920676033441014e-06 -ensions 2.7882791805444486e-06 -▁condem 2.7882791805444486e-06 -fachen 2.7844907577447957e-06 -▁occas 2.7844907577447957e-06 -ämpfen 2.7807023349451432e-06 -▁Trink 2.7807023349451432e-06 -ahme 2.7769139121454903e-06 -bearbeit 2.7769139121454903e-06 -idden 2.7769139121454903e-06 -▁Agre 2.7769139121454903e-06 -▁Niederl 2.7769139121454903e-06 -▁Heat 2.7693370665461845e-06 -▁Rech 2.7693370665461845e-06 -▁übern 2.7693370665461845e-06 -▁zert 2.7655486437465321e-06 -ksam 2.7617602209468792e-06 -▁gekenn 2.7617602209468792e-06 -▁Columb 2.7541833753475734e-06 -ächlich 2.7503949525479209e-06 -cling 2.746606529748268e-06 -irlpool 2.746606529748268e-06 -öffnete 2.746606529748268e-06 -▁Berat 2.746606529748268e-06 -▁Großbritann 2.746606529748268e-06 -ragt 2.7428181069486151e-06 -rieden 2.7428181069486151e-06 -ulevard 2.7428181069486151e-06 -▁Quest 2.7390296841489627e-06 -▁Wed 2.7390296841489627e-06 -▁demonst 2.7390296841489627e-06 -▁stim 2.7390296841489627e-06 -eachtet 2.7352412613493098e-06 -verän 2.7352412613493098e-06 -zipl 2.7352412613493098e-06 -▁Sonnt 2.7352412613493098e-06 -▁gee 2.7352412613493098e-06 -attformen 2.7314528385496569e-06 -▁legit 2.7314528385496569e-06 -▁schrift 2.7314528385496569e-06 -strukt 2.727664415750004e-06 -▁geleist 2.727664415750004e-06 -eaking 2.7238759929503515e-06 -emark 2.7238759929503515e-06 -▁Hist 2.7238759929503515e-06 -▁Ress 2.7238759929503515e-06 -▁Schmid 2.7238759929503515e-06 -▁incur 2.7238759929503515e-06 -▁Bec 2.7200875701506986e-06 -▁determ 2.7200875701506986e-06 -hältn 2.7162991473510457e-06 -▁Ris 2.7162991473510457e-06 -▁irgendwel 2.7162991473510457e-06 -konstrukt 2.7125107245513928e-06 -stark 2.7125107245513928e-06 -umed 2.7125107245513928e-06 -▁Subsidiaritäts 2.7125107245513928e-06 -anspruch 2.7087223017517404e-06 -mitten 2.7087223017517404e-06 -▁Darf 2.7087223017517404e-06 -▁Gemü 2.7087223017517404e-06 -ether 2.7049338789520875e-06 -▁Echt 2.7049338789520875e-06 -▁negl 2.7049338789520875e-06 -ürlich 2.7011454561524346e-06 -▁astr 2.7011454561524346e-06 -▁debatt 2.7011454561524346e-06 -▁disch 2.7011454561524346e-06 -▁hervorra 2.7011454561524346e-06 -ogle 2.6973570333527817e-06 --Europ 2.6935686105531292e-06 -f. 2.6935686105531292e-06 -iegelt 2.6935686105531292e-06 -▁prepar 2.6935686105531292e-06 -arus 2.6897801877534763e-06 -▁ASP 2.6897801877534763e-06 -ά 2.6859917649538234e-06 -damer 2.6822033421541706e-06 -ieval 2.6822033421541706e-06 -oire 2.6822033421541706e-06 -yards 2.6822033421541706e-06 -▁Bom 2.6822033421541706e-06 -▁freiwill 2.6822033421541706e-06 -strument 2.6784149193545181e-06 -erium 2.6746264965548652e-06 -frastruktur 2.6746264965548652e-06 -▁näch 2.6746264965548652e-06 -▁repress 2.6746264965548652e-06 -öte 2.6708380737552123e-06 -▁Weiterbild 2.6708380737552123e-06 -▁urg 2.6708380737552123e-06 -hing 2.6670496509555594e-06 -overty 2.6670496509555594e-06 -▁Vien 2.6670496509555594e-06 -deb 2.6632612281559069e-06 -orer 2.6632612281559069e-06 -otherap 2.6632612281559069e-06 -ülle 2.6632612281559069e-06 -▁Feed 2.6632612281559069e-06 -elta 2.6594728053562541e-06 -▁ASE 2.6594728053562541e-06 -▁Sozialdem 2.6594728053562541e-06 --sized 2.6556843825566012e-06 -opher 2.6556843825566012e-06 -▁alp 2.6556843825566012e-06 -▁beschäd 2.6556843825566012e-06 -▁wirks 2.6556843825566012e-06 -holders 2.6518959597569483e-06 -inea 2.6518959597569483e-06 -▁Wit 2.6518959597569483e-06 -ingung 2.6481075369572958e-06 -▁feas 2.6481075369572958e-06 -▁valu 2.6481075369572958e-06 -▁canc 2.6443191141576429e-06 -▁hil 2.6443191141576429e-06 -kö 2.64053069135799e-06 -▁Welthandels 2.64053069135799e-06 -▁exklus 2.64053069135799e-06 -agna 2.6367422685583371e-06 -annel 2.6367422685583371e-06 -bul 2.6367422685583371e-06 -gert 2.6367422685583371e-06 -tons 2.6367422685583371e-06 -▁bedau 2.6367422685583371e-06 -▁exempl 2.6367422685583371e-06 -▁reag 2.6367422685583371e-06 -harmon 2.6329538457586847e-06 -▁Kür 2.6329538457586847e-06 -▁deserv 2.6329538457586847e-06 -▁doppel 2.6329538457586847e-06 -▁Welthand 2.6291654229590318e-06 -▁behav 2.6291654229590318e-06 -eterm 2.6253770001593789e-06 -▁beträcht 2.6253770001593789e-06 -erkennung 2.6178001545600735e-06 -wach 2.6178001545600735e-06 -anni 2.6140117317604206e-06 -mosphäre 2.6140117317604206e-06 -ruh 2.6140117317604206e-06 -uage 2.6140117317604206e-06 -▁Kä 2.6140117317604206e-06 -▁democ 2.6140117317604206e-06 -.m. 2.6102233089607677e-06 -empl 2.6102233089607677e-06 -geiz 2.6102233089607677e-06 -▁lauf 2.6102233089607677e-06 -andlung 2.6064348861611148e-06 -▁hydraul 2.6064348861611148e-06 --Mittel 2.6026464633614624e-06 -richtet 2.6026464633614624e-06 -▁Abente 2.6026464633614624e-06 -▁Baron 2.6026464633614624e-06 -▁Eleg 2.6026464633614624e-06 -▁Ausge 2.5988580405618095e-06 -▁Tipp 2.5988580405618095e-06 -arios 2.5950696177621566e-06 -breite 2.5950696177621566e-06 -isure 2.5950696177621566e-06 -▁Akte 2.5950696177621566e-06 -▁Eben 2.5950696177621566e-06 -▁unfor 2.5950696177621566e-06 -erma 2.5912811949625037e-06 -▁preserv 2.5912811949625037e-06 -national 2.5874927721628512e-06 -aubern 2.5837043493631983e-06 -gerichts 2.5837043493631983e-06 -▁E-C 2.5837043493631983e-06 -▁Jew 2.5837043493631983e-06 -▁nob 2.5837043493631983e-06 -arbeitung 2.5799159265635454e-06 -schnitts 2.5799159265635454e-06 -▁Fift 2.5799159265635454e-06 -▁fig 2.5799159265635454e-06 -abled 2.5761275037638926e-06 -bsicht 2.5761275037638926e-06 -sterreich 2.5761275037638926e-06 -ي 2.5761275037638926e-06 -aneben 2.57233908096424e-06 -heiz 2.57233908096424e-06 -ignet 2.57233908096424e-06 -onymous 2.57233908096424e-06 -wärtige 2.57233908096424e-06 -▁Beschäft 2.57233908096424e-06 -elated 2.5685506581645872e-06 -borg 2.5647622353649343e-06 -etzugang 2.5647622353649343e-06 -unately 2.5647622353649343e-06 -wiesen 2.5647622353649343e-06 -eiden 2.5609738125652818e-06 -äßt 2.5609738125652818e-06 -▁gäng 2.5609738125652818e-06 -df 2.5571853897656289e-06 -ilitation 2.5571853897656289e-06 -▁Funkt 2.5571853897656289e-06 -▁Ratsvors 2.5571853897656289e-06 --the- 2.5533969669659761e-06 -▁bran 2.5533969669659761e-06 -ό 2.5496085441663232e-06 -É 2.5458201213666707e-06 -▁erfüll 2.5458201213666707e-06 -bereiten 2.5420316985670178e-06 -▁displ 2.5420316985670178e-06 -ū 2.5382432757673649e-06 -▁chang 2.5382432757673649e-06 -▁unab 2.5382432757673649e-06 -▁vill 2.5382432757673649e-06 -force 2.534454852967712e-06 -idges 2.534454852967712e-06 -ollar 2.534454852967712e-06 -tschaft 2.534454852967712e-06 -▁Erhö 2.5306664301680596e-06 -▁fal 2.5306664301680596e-06 -▁requ 2.5306664301680596e-06 -▁unz 2.5306664301680596e-06 -▁Frühstücks 2.5268780073684067e-06 -▁offiz 2.5268780073684067e-06 -▁socio 2.5268780073684067e-06 -ächtigt 2.5230895845687538e-06 -▁Inner 2.5230895845687538e-06 -essene 2.5193011617691009e-06 -ikas 2.5193011617691009e-06 -▁beaut 2.5193011617691009e-06 -▁expend 2.5193011617691009e-06 -▁grundle 2.5193011617691009e-06 -▁japan 2.5193011617691009e-06 -▁hoff 2.5155127389694484e-06 -hereas 2.5117243161697955e-06 -tenance 2.5117243161697955e-06 -▁Beweg 2.5117243161697955e-06 -▁veränder 2.5117243161697955e-06 -mitting 2.5079358933701426e-06 -otive 2.5079358933701426e-06 -▁fascin 2.5041474705704897e-06 -kategorien 2.5003590477708373e-06 -▁woll 2.4927822021715315e-06 -ideos 2.4889937793718786e-06 -▁Assoziierungs 2.4889937793718786e-06 -orning 2.4852053565722261e-06 -reme 2.4852053565722261e-06 -Ó 2.4852053565722261e-06 -▁Ide 2.4852053565722261e-06 -▁gam 2.4852053565722261e-06 -▁taxp 2.4852053565722261e-06 -▁Booking 2.4814169337725732e-06 -▁Fron 2.4814169337725732e-06 -▁biotechn 2.4814169337725732e-06 -ennbar 2.4776285109729203e-06 -onse 2.4776285109729203e-06 -▁kü 2.4776285109729203e-06 -▁narr 2.4776285109729203e-06 -gefunden 2.4738400881732674e-06 -örderung 2.4738400881732674e-06 -▁Compet 2.4738400881732674e-06 -▁IL 2.4738400881732674e-06 --site 2.470051665373615e-06 -otograf 2.470051665373615e-06 -wohnt 2.470051665373615e-06 -▁Valen 2.470051665373615e-06 -zeugung 2.4662632425739621e-06 -▁conj 2.4662632425739621e-06 -▁crea 2.4662632425739621e-06 -▁definit 2.4662632425739621e-06 -▁israel 2.4662632425739621e-06 -Os 2.4624748197743092e-06 -euerung 2.4624748197743092e-06 -▁SME 2.4624748197743092e-06 --Sta 2.4586863969746563e-06 -amination 2.4586863969746563e-06 -annual 2.4586863969746563e-06 -geteilt 2.4586863969746563e-06 -illigt 2.4586863969746563e-06 -webe 2.4586863969746563e-06 -▁Fourth 2.4586863969746563e-06 -▁dispar 2.4586863969746563e-06 -▁vic 2.4586863969746563e-06 -hone 2.4548979741750038e-06 -▁Lithu 2.4548979741750038e-06 -▁Mex 2.4548979741750038e-06 -beschl 2.4511095513753509e-06 -▁Meth 2.4511095513753509e-06 -▁anspruchs 2.4511095513753509e-06 -iry 2.4473211285756981e-06 -örtern 2.4473211285756981e-06 -▁geein 2.4473211285756981e-06 -▁sect 2.4473211285756981e-06 -▁н 2.4473211285756981e-06 -ulpt 2.4435327057760452e-06 -ilfen 2.4397442829763927e-06 -inema 2.4397442829763927e-06 -ismen 2.4397442829763927e-06 -urement 2.4397442829763927e-06 -م 2.4397442829763927e-06 -▁überwie 2.4397442829763927e-06 -estellt 2.43595586017674e-06 -▁künft 2.43595586017674e-06 -enzial 2.428379014577434e-06 -▁Konz 2.428379014577434e-06 -▁ergän 2.428379014577434e-06 -betreuung 2.4245905917777816e-06 -nier 2.4245905917777816e-06 -▁Veg 2.4245905917777816e-06 -klaus 2.4208021689781287e-06 -ünstler 2.4208021689781287e-06 -▁Feb 2.4208021689781287e-06 -▁Rechtsstaat 2.4208021689781287e-06 -▁behaupt 2.4208021689781287e-06 -▁schein 2.4208021689781287e-06 -ungalows 2.4170137461784758e-06 -▁congratul 2.4170137461784758e-06 -apan 2.4132253233788229e-06 -ohner 2.4132253233788229e-06 -immers 2.4094369005791704e-06 -ivalent 2.4094369005791704e-06 -δ 2.4094369005791704e-06 -mischung 2.4056484777795175e-06 -mosph 2.4056484777795175e-06 -oton 2.4056484777795175e-06 -▁Ahm 2.4056484777795175e-06 -▁Krank 2.4056484777795175e-06 -onenten 2.4018600549798646e-06 -ustain 2.4018600549798646e-06 -▁inakzept 2.4018600549798646e-06 -▁prag 2.4018600549798646e-06 -wärmung 2.3980716321802117e-06 -zeugt 2.3980716321802117e-06 -▁Lom 2.3980716321802117e-06 -▁Rüst 2.3980716321802117e-06 -▁Vors 2.3980716321802117e-06 -▁elig 2.3980716321802117e-06 -hmen 2.3942832093805593e-06 -ventional 2.3942832093805593e-06 -▁Vorder 2.3942832093805593e-06 -▁erschein 2.3942832093805593e-06 -▁Ägyp 2.3942832093805593e-06 -ű 2.3904947865809064e-06 -▁angebot 2.3904947865809064e-06 -▁dif 2.3904947865809064e-06 -ifestyle 2.3867063637812535e-06 -irtschaftung 2.3867063637812535e-06 -äuser 2.3867063637812535e-06 -▁Bevölker 2.3867063637812535e-06 -▁Obs 2.3867063637812535e-06 -▁aesthet 2.3867063637812535e-06 -▁haft 2.3867063637812535e-06 -ocol 2.382917940981601e-06 -▁diplom 2.382917940981601e-06 -icular 2.3791295181819481e-06 -▁plöt 2.3791295181819481e-06 -beteil 2.3715526725826423e-06 -irms 2.3715526725826423e-06 -▁zukünft 2.3715526725826423e-06 -VIS 2.36776424978299e-06 -gebä 2.36776424978299e-06 -rection 2.36776424978299e-06 -örd 2.363975826983337e-06 -ė 2.363975826983337e-06 -▁telec 2.363975826983337e-06 -aufent 2.3601874041836841e-06 -emes 2.3601874041836841e-06 -▁ic 2.3563989813840312e-06 -apers 2.3526105585843787e-06 -▁Gebur 2.3526105585843787e-06 -▁splend 2.3526105585843787e-06 -▁abhäng 2.3488221357847258e-06 -iaries 2.3450337129850729e-06 -▁erreich 2.3450337129850729e-06 -▁impr 2.3450337129850729e-06 -▁occ 2.3450337129850729e-06 -igger 2.34124529018542e-06 -klärt 2.34124529018542e-06 -▁Köl 2.3374568673857676e-06 -▁Micros 2.3374568673857676e-06 -▁monopol 2.3374568673857676e-06 -eldet 2.3298800217864618e-06 -hagen 2.3298800217864618e-06 -imming 2.3298800217864618e-06 -itudes 2.3298800217864618e-06 -ifff 2.3260915989868089e-06 -othing 2.3260915989868089e-06 -› 2.3260915989868089e-06 -▁Bosn 2.3260915989868089e-06 -▁Kriminal 2.3260915989868089e-06 -▁leist 2.3260915989868089e-06 -▁tiss 2.3260915989868089e-06 -▁Feh 2.3223031761871564e-06 -atures 2.3185147533875036e-06 -▁Griechen 2.3185147533875036e-06 -▁erlang 2.3185147533875036e-06 -▁sharehol 2.3185147533875036e-06 -▁timet 2.3185147533875036e-06 -actions 2.3147263305878507e-06 -▁Regard 2.3147263305878507e-06 -▁offenkund 2.3147263305878507e-06 -elegen 2.3109379077881978e-06 -iveaus 2.3109379077881978e-06 -kord 2.3109379077881978e-06 -ätigkeiten 2.3109379077881978e-06 -▁schritt 2.3109379077881978e-06 -▁unan 2.3109379077881978e-06 -bean 2.3071494849885453e-06 -schied 2.3071494849885453e-06 -gänglich 2.3033610621888924e-06 -sparungen 2.3033610621888924e-06 -▁Cres 2.3033610621888924e-06 -▁Prest 2.3033610621888924e-06 -▁anerk 2.3033610621888924e-06 -▁beauf 2.3033610621888924e-06 -▁init 2.3033610621888924e-06 -angehör 2.2995726393892395e-06 -igher 2.2995726393892395e-06 -ughter 2.2995726393892395e-06 -& 2.2957842165895866e-06 -apos; 2.2957842165895866e-06 -Н 2.2957842165895866e-06 -▁irres 2.2957842165895866e-06 -anzler 2.2919957937899342e-06 -ording 2.2919957937899342e-06 -orgt 2.2919957937899342e-06 -pakt 2.2919957937899342e-06 -▁Aller 2.2919957937899342e-06 -▁Erdbe 2.2919957937899342e-06 -▁Karls 2.2919957937899342e-06 --Med 2.2882073709902813e-06 -reien 2.2882073709902813e-06 -ustausch 2.2882073709902813e-06 -▁Indik 2.2882073709902813e-06 -▁mac 2.2882073709902813e-06 -▁einstimm 2.2844189481906284e-06 -ounder 2.2806305253909755e-06 -▁Lond 2.2806305253909755e-06 -▁biet 2.2806305253909755e-06 -▁proxim 2.2806305253909755e-06 --Protokoll 2.276842102591323e-06 -iedelt 2.276842102591323e-06 -± 2.276842102591323e-06 -▁Südt 2.276842102591323e-06 -▁beeinfl 2.276842102591323e-06 -▁haz 2.276842102591323e-06 -▁sag 2.276842102591323e-06 -▁undert 2.276842102591323e-06 -penden 2.27305367979167e-06 -more 2.2692652569920172e-06 -▁intell 2.2692652569920172e-06 --sc 2.2654768341923643e-06 -▁effekt 2.2654768341923643e-06 -klang 2.2616884113927119e-06 -× 2.2616884113927119e-06 -▁freed 2.2616884113927119e-06 -▁webs 2.2616884113927119e-06 -▁getrenn 2.257899988593059e-06 -astet 2.2541115657934061e-06 -eeing 2.2541115657934061e-06 -▁Kup 2.2541115657934061e-06 -▁mak 2.2541115657934061e-06 -habt 2.2503231429937532e-06 -uled 2.2503231429937532e-06 -vors 2.2503231429937532e-06 -▁Frühstücksb 2.2503231429937532e-06 -▁Reh 2.2503231429937532e-06 -▁carr 2.2503231429937532e-06 -▁gewinn 2.2503231429937532e-06 -▁übr 2.2503231429937532e-06 -schalter 2.2465347201941007e-06 -wün 2.2465347201941007e-06 -zufolge 2.2465347201941007e-06 -▁Beif 2.2427462973944478e-06 -▁bureauc 2.2427462973944478e-06 -acio 2.2389578745947949e-06 -brit 2.2389578745947949e-06 -esien 2.2389578745947949e-06 -tendlich 2.2389578745947949e-06 -ziplin 2.2389578745947949e-06 -änderungs 2.2389578745947949e-06 -▁beschleun 2.2389578745947949e-06 -▁großzüg 2.2389578745947949e-06 -assador 2.2351694517951421e-06 -▁spac 2.2351694517951421e-06 -▁vulner 2.2351694517951421e-06 -^ 2.2313810289954896e-06 -▁Cz 2.2313810289954896e-06 -▁autonom 2.2313810289954896e-06 -verletzungen 2.2275926061958367e-06 -berat 2.2238041833961838e-06 -checken 2.2238041833961838e-06 -gedeckt 2.2238041833961838e-06 -izenzen 2.2238041833961838e-06 -▁Kohäsions 2.2238041833961838e-06 -▁roy 2.2238041833961838e-06 -üller 2.2200157605965309e-06 -▁Jahrz 2.2200157605965309e-06 -eople 2.2162273377968784e-06 -▁Rak 2.2162273377968784e-06 -ieße 2.2124389149972256e-06 -▁nörd 2.2124389149972256e-06 -icer 2.2086504921975727e-06 -▁dign 2.2086504921975727e-06 -ingungen 2.2048620693979198e-06 -▁Konfigur 2.2048620693979198e-06 -▁nament 2.2048620693979198e-06 -▁shoul 2.2048620693979198e-06 -ayout 2.2010736465982673e-06 -▁preca 2.2010736465982673e-06 -▁purs 2.2010736465982673e-06 -ellent 2.1972852237986144e-06 -schaffung 2.1972852237986144e-06 -▁Sold 2.1972852237986144e-06 -▁transatlant 2.1972852237986144e-06 -atem 2.1934968009989615e-06 -iscr 2.1934968009989615e-06 -mitted 2.1934968009989615e-06 -▁Vict 2.1934968009989615e-06 -▁unabhäng 2.1934968009989615e-06 -▁wirk 2.1934968009989615e-06 -iary 2.1897083781993091e-06 -γ 2.1897083781993091e-06 -▁Foot 2.1897083781993091e-06 -▁Leban 2.1897083781993091e-06 -▁keines 2.1897083781993091e-06 --orient 2.1859199553996562e-06 -eist 2.1859199553996562e-06 -ingual 2.1859199553996562e-06 -izienz 2.1859199553996562e-06 -▁illeg 2.1859199553996562e-06 -▁länd 2.1859199553996562e-06 --Mit 2.1821315326000033e-06 -invest 2.1821315326000033e-06 -▁Profess 2.1821315326000033e-06 --economic 2.1783431098003504e-06 -irable 2.1783431098003504e-06 -peror 2.1783431098003504e-06 -▁Verpflicht 2.1783431098003504e-06 -▁gescheit 2.1745546870006979e-06 -▁guar 2.1745546870006979e-06 -▁non-s 2.1745546870006979e-06 -▁traff 2.1745546870006979e-06 -▁verbesser 2.1745546870006979e-06 -ereg 2.170766264201045e-06 -fertigen 2.170766264201045e-06 -џ 2.170766264201045e-06 -▁hist 2.170766264201045e-06 -drucks 2.1669778414013921e-06 -gratul 2.1669778414013921e-06 -lez 2.1669778414013921e-06 -▁EZ 2.1669778414013921e-06 -▁ow 2.1669778414013921e-06 -▁österreich 2.1669778414013921e-06 -leun 2.1631894186017392e-06 -oard 2.1631894186017392e-06 -▁candid 2.1631894186017392e-06 -▁sug 2.1631894186017392e-06 -▁Sonn 2.1594009958020868e-06 -hütung 2.1556125730024339e-06 -▁Komprom 2.151824150202781e-06 -▁Schließ 2.151824150202781e-06 -palette 2.1480357274031281e-06 -terne 2.1480357274031281e-06 -▁Fisher 2.1480357274031281e-06 -▁Rechen 2.1480357274031281e-06 -▁incr 2.1480357274031281e-06 -iensten 2.1442473046034756e-06 -▁Sow 2.1442473046034756e-06 -▁scrut 2.1442473046034756e-06 -О 2.1404588818038227e-06 -▁Sept 2.1404588818038227e-06 -iciary 2.13667045900417e-06 -▁Urs 2.13667045900417e-06 -uzierung 2.132882036204517e-06 -К 2.132882036204517e-06 -▁Frem 2.132882036204517e-06 -▁Sowjet 2.1290936134048645e-06 -▁unus 2.1290936134048645e-06 -▁zugesch 2.1290936134048645e-06 -begriffen 2.1253051906052116e-06 -schuldigen 2.1253051906052116e-06 -stütz 2.1253051906052116e-06 -▁Sever 2.1253051906052116e-06 -▁Tai 2.1253051906052116e-06 -▁nutr 2.1253051906052116e-06 -▁zeich 2.1253051906052116e-06 -inery 2.1215167678055587e-06 -▁Flücht 2.1215167678055587e-06 -▁eingere 2.1215167678055587e-06 -▁eleg 2.1215167678055587e-06 -übung 2.1177283450059058e-06 -entrale 2.1139399222062533e-06 -▁fem 2.1139399222062533e-06 -▁jurisd 2.1139399222062533e-06 -reche 2.1101514994066004e-06 -sekretär 2.1063630766069476e-06 -w. 2.1063630766069476e-06 -▁einger 2.1063630766069476e-06 -▁horn 2.1063630766069476e-06 -entially 2.1025746538072947e-06 -othic 2.1025746538072947e-06 -▁Eigent 2.1025746538072947e-06 -frecht 2.0987862310076422e-06 -ienz 2.0987862310076422e-06 -zusagen 2.0987862310076422e-06 -▁Verteid 2.0987862310076422e-06 -▁Völk 2.0987862310076422e-06 -▁cele 2.0987862310076422e-06 -▁forb 2.0987862310076422e-06 -OTEL 2.0949978082079893e-06 -ţ 2.0949978082079893e-06 -▁bearbeit 2.0949978082079893e-06 -▁Handt 2.0912093854083364e-06 -inens 2.0874209626086835e-06 -▁Passag 2.0874209626086835e-06 -▁hosp 2.0798441170093782e-06 -▁ingred 2.0798441170093782e-06 -ublic 2.0760556942097253e-06 -▁Wei 2.0760556942097253e-06 -▁geforder 2.0760556942097253e-06 -yers 2.0722672714100724e-06 -▁Herange 2.0722672714100724e-06 -▁abzul 2.0722672714100724e-06 -▁entscheid 2.0722672714100724e-06 -▁sensib 2.0722672714100724e-06 -plates 2.06847884861042e-06 -uerung 2.06847884861042e-06 -▁Anges 2.06847884861042e-06 -▁Präsid 2.06847884861042e-06 -frastruct 2.064690425810767e-06 -leichen 2.064690425810767e-06 -ounter 2.064690425810767e-06 -▁Situ 2.064690425810767e-06 -▁enor 2.064690425810767e-06 -ague 2.0609020030111141e-06 -estag 2.0609020030111141e-06 -▁lar 2.0609020030111141e-06 -alition 2.0533251574118088e-06 -burgh 2.0533251574118088e-06 -chitekt 2.0533251574118088e-06 -eleist 2.0533251574118088e-06 -▁Colomb 2.0533251574118088e-06 -▁Gree 2.0533251574118088e-06 -▁Grie 2.0533251574118088e-06 -▁Kämp 2.0533251574118088e-06 -▁Migr 2.0533251574118088e-06 -holm 2.0495367346121559e-06 -stüt 2.0495367346121559e-06 -▁Freib 2.0495367346121559e-06 -ellence 2.045748311812503e-06 -sequ 2.045748311812503e-06 -ilton 2.04195988901285e-06 -hende 2.0381714662131976e-06 -ändert 2.0381714662131976e-06 -▁prere 2.0381714662131976e-06 -ürft 2.0305946206138918e-06 -▁Hekt 2.0305946206138918e-06 -▁Sog 2.0305946206138918e-06 -melden 2.026806197814239e-06 -▁betracht 2.026806197814239e-06 -▁geprü 2.026806197814239e-06 -MU 2.0230177750145865e-06 -▁detaill 2.0230177750145865e-06 -eks 2.0192293522149336e-06 -heimer 2.0192293522149336e-06 -icien 2.0192293522149336e-06 -ifikationen 2.0192293522149336e-06 -uant 2.0192293522149336e-06 -ärkt 2.0192293522149336e-06 -▁concess 2.0192293522149336e-06 -▁Voy 2.0154409294152807e-06 -▁mist 2.0154409294152807e-06 -▁parl 2.0154409294152807e-06 -chron 2.0116525066156282e-06 -earbeitet 2.0116525066156282e-06 -imension 2.0116525066156282e-06 -strö 2.0116525066156282e-06 -ungsvertrag 2.0116525066156282e-06 -▁allmäh 2.0116525066156282e-06 -▁cla 2.0116525066156282e-06 -▁traged 2.0116525066156282e-06 -ehler 2.0078640838159753e-06 -ugang 2.0078640838159753e-06 -▁inade 2.0078640838159753e-06 -▁releas 2.0078640838159753e-06 -▁uneingesch 2.0078640838159753e-06 -effizienz 2.0040756610163225e-06 -¡ 2.0040756610163225e-06 -▁Beam 2.0040756610163225e-06 -▁bathr 2.0040756610163225e-06 -utation 2.0002872382166696e-06 -▁Bib 2.0002872382166696e-06 -iefert 1.9964988154170171e-06 -ocument 1.9964988154170171e-06 -ständigkeit 1.9964988154170171e-06 -و 1.9964988154170171e-06 -▁Brü 1.9964988154170171e-06 -▁unabding 1.9964988154170171e-06 -ladung 1.9927103926173642e-06 -▁fab 1.9927103926173642e-06 -▁landscap 1.9927103926173642e-06 --country 1.9889219698177113e-06 -▁24-Stunden 1.9889219698177113e-06 -▁Kaff 1.9889219698177113e-06 -▁ful 1.9889219698177113e-06 -▁größ 1.9889219698177113e-06 -emony 1.9851335470180584e-06 -gehör 1.9851335470180584e-06 -tritts 1.9851335470180584e-06 -▁erneuer 1.9851335470180584e-06 -▁vot 1.9851335470180584e-06 -VO 1.9813451242184059e-06 -self 1.9813451242184059e-06 -uces 1.9813451242184059e-06 -▁eB 1.9813451242184059e-06 --Gen 1.9775567014187531e-06 -▁Bereit 1.9775567014187531e-06 -▁Educ 1.9775567014187531e-06 -▁NAT 1.9775567014187531e-06 -▁Similar 1.9775567014187531e-06 -▁umfang 1.9775567014187531e-06 -fg 1.9737682786191e-06 -gä 1.9737682786191e-06 -ült 1.9737682786191e-06 -▁Guant 1.9737682786191e-06 -▁Hüg 1.9737682786191e-06 -▁forts 1.9737682786191e-06 -▁unterst 1.9737682786191e-06 -Í 1.9699798558194473e-06 -ägigen 1.9699798558194473e-06 -▁Befürwort 1.9699798558194473e-06 -apse 1.9661914330197948e-06 -▁appell 1.9661914330197948e-06 -▁fabr 1.9661914330197948e-06 --Bahn 1.9624030102201419e-06 --party 1.9624030102201419e-06 -eldorf 1.9624030102201419e-06 -meld 1.9624030102201419e-06 -ή 1.9624030102201419e-06 -ت 1.9624030102201419e-06 -▁mov 1.9624030102201419e-06 -▁schol 1.9624030102201419e-06 -letter 1.958614587420489e-06 -▁dens 1.958614587420489e-06 -▁verwend 1.958614587420489e-06 -▁zwing 1.958614587420489e-06 -ademy 1.9548261646208361e-06 -iscal 1.9548261646208361e-06 -▁Landwir 1.9548261646208361e-06 -wunsch 1.9510377418211837e-06 -▁Huss 1.9510377418211837e-06 -junktur 1.9472493190215308e-06 -okol 1.9472493190215308e-06 -osev 1.9472493190215308e-06 -zeichnis 1.9472493190215308e-06 -zufinden 1.9472493190215308e-06 -▁ey 1.9472493190215308e-06 -▁wün 1.9472493190215308e-06 -stitutional 1.9434608962218779e-06 -▁Betroffen 1.9434608962218779e-06 -▁consc 1.9434608962218779e-06 -▁continu 1.9434608962218779e-06 -▁rhetor 1.9434608962218779e-06 -hole 1.939672473422225e-06 -laubnis 1.939672473422225e-06 -▁accomp 1.939672473422225e-06 -▁heut 1.939672473422225e-06 -▁sensit 1.939672473422225e-06 -ifice 1.9358840506225725e-06 -έ 1.9358840506225725e-06 -schra 1.9283072050232667e-06 -iebung 1.9245187822236138e-06 -ї 1.9245187822236138e-06 -▁Krie 1.9245187822236138e-06 -▁Holid 1.9207303594239614e-06 -▁Má 1.9207303594239614e-06 -▁Unterb 1.9207303594239614e-06 -iglich 1.9169419366243085e-06 -lässlich 1.9169419366243085e-06 -party 1.9131535138246556e-06 -▁Kosmet 1.9131535138246556e-06 -▁Stef 1.9131535138246556e-06 -▁gall 1.9131535138246556e-06 -quate 1.9093650910250027e-06 -▁caus 1.9093650910250027e-06 -▁konstrukt 1.9093650910250027e-06 -▁Salv 1.90557666822535e-06 -▁Sebast 1.90557666822535e-06 -ätigung 1.9017882454256973e-06 -▁tak 1.9017882454256973e-06 -▁untern 1.9017882454256973e-06 -amental 1.8979998226260445e-06 -ashion 1.8979998226260445e-06 -fuhr 1.8979998226260445e-06 -äsche 1.8979998226260445e-06 -▁Solidar 1.8979998226260445e-06 -▁chann 1.8979998226260445e-06 -▁transpar 1.8979998226260445e-06 -chst 1.8942113998263918e-06 -ustellen 1.8942113998263918e-06 -▁Gastfreund 1.8942113998263918e-06 -dates 1.8904229770267389e-06 -gument 1.8904229770267389e-06 -undred 1.8904229770267389e-06 -▁dement 1.8904229770267389e-06 -beschluss 1.8866345542270862e-06 -rungen 1.8866345542270862e-06 -▁Hosp 1.8866345542270862e-06 -▁Ministerr 1.8866345542270862e-06 -▁bedür 1.8866345542270862e-06 -örden 1.8828461314274333e-06 -▁TAC 1.8828461314274333e-06 -▁plann 1.8828461314274333e-06 -▁predecess 1.8828461314274333e-06 -▁Dub 1.8790577086277806e-06 -▁Zür 1.8790577086277806e-06 -▁sogen 1.8790577086277806e-06 --Amer 1.8752692858281277e-06 -fält 1.8752692858281277e-06 -ährend 1.8752692858281277e-06 -▁Fok 1.8752692858281277e-06 -usammen 1.8714808630284751e-06 -▁diox 1.8714808630284751e-06 -▁drafts 1.8714808630284751e-06 -▁intermedi 1.8714808630284751e-06 -▁plast 1.8714808630284751e-06 -▁Erne 1.8676924402288222e-06 -liederung 1.8639040174291695e-06 -renzung 1.8639040174291695e-06 -▁Tragöd 1.8639040174291695e-06 -▁einbe 1.8639040174291695e-06 -4-0 1.8601155946295166e-06 -hunder 1.8601155946295166e-06 -▁Steuerzahl 1.8601155946295166e-06 -author 1.8563271718298639e-06 -ceived 1.8563271718298639e-06 -stimmung 1.8563271718298639e-06 -ب 1.8563271718298639e-06 -▁bein 1.8563271718298639e-06 -▁beob 1.8563271718298639e-06 -▁Superv 1.852538749030211e-06 -▁cancell 1.852538749030211e-06 -.20 1.8487503262305583e-06 -astery 1.8487503262305583e-06 -abile 1.8449619034309055e-06 -amber 1.8449619034309055e-06 -findung 1.8449619034309055e-06 -▁adap 1.8449619034309055e-06 -▁oppos 1.8411734806312528e-06 -ifferenz 1.8373850578315999e-06 -▁Malays 1.8373850578315999e-06 -▁measur 1.8373850578315999e-06 -erhalten 1.8335966350319472e-06 -▁Mug 1.8335966350319472e-06 -▁refurb 1.8335966350319472e-06 -fi 1.8335966350319472e-06 -institutional 1.8298082122322945e-06 -äußer 1.8298082122322945e-06 -А 1.8298082122322945e-06 -▁frü 1.8298082122322945e-06 -▁oste 1.8298082122322945e-06 -▁stärk 1.8298082122322945e-06 -▁Besicht 1.8260197894326416e-06 -▁Tos 1.8260197894326416e-06 -▁dez 1.8260197894326416e-06 -▁panoram 1.8260197894326416e-06 -ctic 1.822231366632989e-06 -kompeten 1.822231366632989e-06 -ronic 1.822231366632989e-06 -terr 1.822231366632989e-06 -▁Freit 1.822231366632989e-06 -▁Gefü 1.822231366632989e-06 -▁Wachstumsp 1.822231366632989e-06 -▁tast 1.822231366632989e-06 -itteil 1.8184429438333361e-06 -▁Ungar 1.8184429438333361e-06 -▁kern 1.8184429438333361e-06 -geschritten 1.8146545210336834e-06 -ompl 1.8146545210336834e-06 -▁Sehens 1.8146545210336834e-06 -▁erweit 1.8146545210336834e-06 -fahrens 1.8108660982340305e-06 -älfte 1.8108660982340305e-06 -▁Energiet 1.8108660982340305e-06 -▁NCC 1.8108660982340305e-06 -vec 1.8070776754343778e-06 -▁Struct 1.8070776754343778e-06 -anies 1.8032892526347249e-06 -formance 1.8032892526347249e-06 -М 1.8032892526347249e-06 -▁Breit 1.8032892526347249e-06 -▁Exhib 1.8032892526347249e-06 -▁Verstär 1.8032892526347249e-06 -▁guilt 1.8032892526347249e-06 -unruh 1.7995008298350722e-06 -aupt 1.7957124070354193e-06 -fakt 1.7957124070354193e-06 -▁Ergeb 1.7957124070354193e-06 -▁Vermarkt 1.7957124070354193e-06 -▁Verschm 1.7957124070354193e-06 -▁Wäsch 1.7957124070354193e-06 -▁geleg 1.7957124070354193e-06 -▁verteid 1.7957124070354193e-06 -ansion 1.7919239842357667e-06 -grenzung 1.7919239842357667e-06 -roat 1.7919239842357667e-06 -▁erkl 1.7919239842357667e-06 -stuffs 1.7881355614361138e-06 -▁Schokol 1.7881355614361138e-06 -▁Än 1.7881355614361138e-06 -alking 1.7843471386364611e-06 -fassungen 1.7843471386364611e-06 -itious 1.7843471386364611e-06 -nex 1.7843471386364611e-06 -zwischen 1.7843471386364611e-06 -ľ 1.7843471386364611e-06 -Ł 1.7843471386364611e-06 -▁eigent 1.7843471386364611e-06 -overn 1.7805587158368082e-06 -Ÿ 1.7805587158368082e-06 -▁Schott 1.7805587158368082e-06 -elef 1.7767702930371555e-06 -▁forec 1.7767702930371555e-06 -▁quar 1.7767702930371555e-06 -▁reven 1.7767702930371555e-06 -flanzen 1.7729818702375026e-06 -▁Jugoslaw 1.7729818702375026e-06 -▁überflüss 1.7729818702375026e-06 -ittee 1.76919344743785e-06 -krie 1.76919344743785e-06 -propri 1.76919344743785e-06 -▁Befür 1.76919344743785e-06 -▁Dres 1.76919344743785e-06 -örp 1.7654050246381971e-06 -▁Glet 1.7654050246381971e-06 -▁capac 1.7654050246381971e-06 -brid 1.7616166018385444e-06 -▁Befrei 1.7616166018385444e-06 -▁headquar 1.7616166018385444e-06 -ergabe 1.7578281790388915e-06 -▁Frühl 1.7578281790388915e-06 -▁Kapaz 1.7578281790388915e-06 -▁attent 1.7578281790388915e-06 -ē 1.7540397562392388e-06 -▁Vorred 1.7540397562392388e-06 -▁Zigar 1.7540397562392388e-06 -▁schwier 1.7540397562392388e-06 -ateg 1.7502513334395859e-06 -▁Dubl 1.7502513334395859e-06 -▁telecommun 1.7464629106399332e-06 -▁verfü 1.7464629106399332e-06 -avor 1.7426744878402803e-06 -belast 1.7426744878402803e-06 -imedia 1.7426744878402803e-06 -würdigkeit 1.7426744878402803e-06 -▁Getre 1.7426744878402803e-06 -▁haupts 1.7426744878402803e-06 -▁kompat 1.7426744878402803e-06 -elegenheit 1.7388860650406277e-06 -junkt 1.7388860650406277e-06 -lafen 1.7388860650406277e-06 -▁wünsch 1.7388860650406277e-06 --house 1.7350976422409748e-06 -ipfel 1.7350976422409748e-06 -▁Autover 1.7350976422409748e-06 -▁kolle 1.7350976422409748e-06 -▁Cong 1.7313092194413221e-06 -▁Erweit 1.7313092194413221e-06 -▁Karr 1.7313092194413221e-06 -lights 1.7275207966416692e-06 -spartei 1.7275207966416692e-06 -▁gig 1.7275207966416692e-06 -ampag 1.7237323738420165e-06 -Ś 1.7237323738420165e-06 -‑ 1.7237323738420165e-06 -▁Cub 1.7237323738420165e-06 -▁atmosph 1.7237323738420165e-06 -cohol 1.7199439510423636e-06 -ötet 1.7199439510423636e-06 -▁beherr 1.7199439510423636e-06 -▁unnecess 1.7199439510423636e-06 -kaufs 1.716155528242711e-06 -uters 1.716155528242711e-06 -feit 1.7123671054430581e-06 -▁Verfü 1.7123671054430581e-06 -ergest 1.7085786826434054e-06 -ächtigungen 1.7085786826434054e-06 -▁Hindern 1.7085786826434054e-06 -▁parlament 1.7047902598437525e-06 -▁Satur 1.7010018370440998e-06 -▁berühm 1.7010018370440998e-06 -▁ordnungs 1.7010018370440998e-06 -▁wür 1.7010018370440998e-06 -▁Büg 1.6972134142444469e-06 -▁bekräft 1.6972134142444469e-06 -schefs 1.6934249914447942e-06 -ächtnis 1.6934249914447942e-06 -▁Schicks 1.6934249914447942e-06 -facht 1.6896365686451413e-06 -igital 1.6858481458454887e-06 -Ñ 1.6858481458454887e-06 -▁dim 1.6858481458454887e-06 -ixels 1.6820597230458358e-06 -ought 1.6820597230458358e-06 -verter 1.6820597230458358e-06 -▁jün 1.6820597230458358e-06 -lehnung 1.6782713002461831e-06 -▁tack 1.6782713002461831e-06 -▁Überein 1.6782713002461831e-06 -half 1.6744828774465302e-06 -viously 1.6744828774465302e-06 -Т 1.6744828774465302e-06 -▁Zag 1.6744828774465302e-06 -▁Rückt 1.6706944546468775e-06 -▁Face 1.6669060318472246e-06 -▁pea 1.6669060318472246e-06 -owder 1.663117609047572e-06 -▁imposs 1.663117609047572e-06 -ugnisse 1.6593291862479191e-06 -▁overwhelm 1.6593291862479191e-06 -lys 1.6555407634482664e-06 -ω 1.6555407634482664e-06 -▁Sev 1.6555407634482664e-06 -imination 1.6517523406486135e-06 -lements 1.6517523406486135e-06 -ografen 1.6517523406486135e-06 -rechung 1.6517523406486135e-06 -θ 1.6517523406486135e-06 -isy 1.6479639178489608e-06 -lever 1.6479639178489608e-06 -agnet 1.6441754950493081e-06 -itness 1.6441754950493081e-06 -zessen 1.6441754950493081e-06 -.j 1.6403870722496552e-06 -entren 1.6403870722496552e-06 -▁Result 1.6403870722496552e-06 -▁estim 1.6403870722496552e-06 -ERV 1.6365986494500026e-06 -opped 1.6365986494500026e-06 -uels 1.6365986494500026e-06 -ältigung 1.6365986494500026e-06 -▁Empfeh 1.6365986494500026e-06 -athed 1.6328102266503497e-06 -berechtigung 1.6328102266503497e-06 -rease 1.6328102266503497e-06 -änn 1.6328102266503497e-06 -▁Immobil 1.6328102266503497e-06 -▁verring 1.6328102266503497e-06 --2013 1.629021803850697e-06 -ancial 1.629021803850697e-06 -ittees 1.629021803850697e-06 -ď 1.629021803850697e-06 -▁flexib 1.629021803850697e-06 -▁lik 1.629021803850697e-06 -▁wal 1.629021803850697e-06 --mon 1.6252333810510441e-06 -ansprü 1.6252333810510441e-06 -▁bürger 1.6252333810510441e-06 -atum 1.6214449582513914e-06 -ehol 1.6214449582513914e-06 -▁Venezu 1.6214449582513914e-06 -▁bestimm 1.6214449582513914e-06 -sq 1.6176565354517385e-06 -▁Veröffent 1.6176565354517385e-06 -▁Kurd 1.6138681126520858e-06 -▁artific 1.6138681126520858e-06 -▁befürwort 1.6138681126520858e-06 -▁hors 1.6138681126520858e-06 -ailable 1.610079689852433e-06 -cluding 1.610079689852433e-06 -ärme 1.610079689852433e-06 -▁Ug 1.610079689852433e-06 -ollah 1.6062912670527803e-06 -▁restr 1.6062912670527803e-06 -▁Errungen 1.6025028442531274e-06 -▁Lös 1.6025028442531274e-06 -▁Män 1.6025028442531274e-06 -geordn 1.5987144214534747e-06 -ieties 1.5949259986538218e-06 -mine 1.5949259986538218e-06 -▁US- 1.5949259986538218e-06 -▁chea 1.5949259986538218e-06 -▁stattf 1.5949259986538218e-06 -bereitet 1.5911375758541691e-06 -ceed 1.5911375758541691e-06 -ibet 1.5911375758541691e-06 -▁Erhol 1.5911375758541691e-06 -▁Inkraft 1.5911375758541691e-06 -▁Sauber 1.5911375758541691e-06 -▁doub 1.5911375758541691e-06 -▁geänder 1.5911375758541691e-06 -lossen 1.5835607302548636e-06 -▁Champ 1.5835607302548636e-06 -▁Frid 1.5835607302548636e-06 -▁Slov 1.5835607302548636e-06 -▁trem 1.5835607302548636e-06 -▁Öffent 1.5835607302548636e-06 -azzo 1.5797723074552107e-06 -bewerber 1.5797723074552107e-06 -government 1.5797723074552107e-06 -onger 1.5797723074552107e-06 -spolit 1.5797723074552107e-06 -▁environ 1.5797723074552107e-06 -▁jed 1.5797723074552107e-06 --Leist 1.575983884655558e-06 -bea 1.575983884655558e-06 -flüss 1.575983884655558e-06 -▁Ruß 1.575983884655558e-06 -▁Ub 1.575983884655558e-06 -hrter 1.5721954618559051e-06 -▁Eas 1.5721954618559051e-06 -▁Lith 1.5721954618559051e-06 -▁transe 1.5721954618559051e-06 -zession 1.5684070390562524e-06 -economic 1.5646186162565995e-06 -erner 1.5646186162565995e-06 -graben 1.5646186162565995e-06 -ipment 1.5646186162565995e-06 -zep 1.5646186162565995e-06 -▁Umst 1.5646186162565995e-06 -▁prestig 1.5646186162565995e-06 -ehold 1.5608301934569468e-06 -Ú 1.5608301934569468e-06 -▁CD-R 1.5608301934569468e-06 -▁hazard 1.5608301934569468e-06 -epp 1.557041770657294e-06 -▁Darle 1.557041770657294e-06 -▁Hoheits 1.557041770657294e-06 -▁Stor 1.557041770657294e-06 -▁mun 1.557041770657294e-06 -ipend 1.5532533478576413e-06 -▁Adri 1.5532533478576413e-06 -▁begrenz 1.5532533478576413e-06 -jenigen 1.5494649250579884e-06 -stahl 1.5494649250579884e-06 -▁erforder 1.5494649250579884e-06 -ategie 1.5456765022583357e-06 -quem 1.5456765022583357e-06 -Ε 1.5456765022583357e-06 -▁Buen 1.5456765022583357e-06 -▁Ankünd 1.5418880794586828e-06 -cano 1.5380996566590301e-06 -chit 1.5380996566590301e-06 -folgerung 1.5380996566590301e-06 -ibar 1.5380996566590301e-06 -schlägt 1.5380996566590301e-06 -▁Artik 1.5380996566590301e-06 -▁zusammengef 1.5380996566590301e-06 -▁Gült 1.5343112338593772e-06 -▁anzup 1.5343112338593772e-06 -itage 1.5305228110597246e-06 -medi 1.5305228110597246e-06 -where 1.5305228110597246e-06 -bres 1.522945965460419e-06 -scheidung 1.522945965460419e-06 -▁Anwend 1.522945965460419e-06 -▁Donner 1.522945965460419e-06 -▁Mü 1.522945965460419e-06 -▁herv 1.522945965460419e-06 -▁recruit 1.522945965460419e-06 -affin 1.5191575426607661e-06 --Out 1.5153691198611134e-06 -olidierung 1.5153691198611134e-06 -äußerung 1.5153691198611134e-06 -د 1.5153691198611134e-06 -▁Sorg 1.5153691198611134e-06 -▁kop 1.5153691198611134e-06 -enntnisse 1.5115806970614605e-06 -ould 1.5115806970614605e-06 -venue 1.5115806970614605e-06 -▁Vize 1.5115806970614605e-06 -▁struct 1.5115806970614605e-06 -eleistungen 1.5077922742618078e-06 -▁Erwäg 1.5077922742618078e-06 -▁Niem 1.5077922742618078e-06 -kerung 1.504003851462155e-06 -vielfält 1.504003851462155e-06 -vious 1.504003851462155e-06 -zuarbeiten 1.504003851462155e-06 -▁gesam 1.504003851462155e-06 -itiveness 1.5002154286625023e-06 -positions 1.4964270058628494e-06 -ulle 1.4964270058628494e-06 -▁unterstütz 1.4964270058628494e-06 -spruchs 1.4926385830631967e-06 -ätzung 1.4926385830631967e-06 -▁gült 1.4926385830631967e-06 -wärt 1.4888501602635438e-06 -▁prel 1.4888501602635438e-06 -▁stren 1.4850617374638911e-06 -▁tsch 1.4850617374638911e-06 -hend 1.4812733146642382e-06 -ъ 1.4812733146642382e-06 -▁palästinens 1.4812733146642382e-06 -eatre 1.4774848918645856e-06 -idential 1.4774848918645856e-06 -zil 1.4774848918645856e-06 -▁kat 1.4774848918645856e-06 -orama 1.4736964690649327e-06 -ôtel 1.4736964690649327e-06 -ع 1.4736964690649327e-06 -▁zukunft 1.4736964690649327e-06 -beweg 1.46990804626528e-06 -egal 1.46990804626528e-06 -leisten 1.46990804626528e-06 -▁ICE 1.46990804626528e-06 -▁disapp 1.46990804626528e-06 -befehl 1.4661196234656273e-06 -uisine 1.4661196234656273e-06 -zusammenarbeit 1.4661196234656273e-06 -▁Diplomat 1.4661196234656273e-06 -▁Rind 1.4661196234656273e-06 -▁rehab 1.4661196234656273e-06 -oub 1.4623312006659744e-06 -überwach 1.4623312006659744e-06 -‹ 1.4623312006659744e-06 -▁Scot 1.4623312006659744e-06 -▁innoc 1.4623312006659744e-06 -▁lok 1.4623312006659744e-06 -▁müs 1.4623312006659744e-06 -helm 1.4585427778663217e-06 -▁Cult 1.4585427778663217e-06 -▁archae 1.4585427778663217e-06 -▁schwar 1.4585427778663217e-06 -▁abwechs 1.4547543550666688e-06 -iterranean 1.4509659322670162e-06 -▁unters 1.4509659322670162e-06 -uzzi 1.4471775094673633e-06 -▁überprü 1.4471775094673633e-06 -ratt 1.4433890866677106e-06 -sieht 1.4433890866677106e-06 -▁Sark 1.4433890866677106e-06 -▁Ausser 1.4396006638680577e-06 -eace 1.435812241068405e-06 -▁Maced 1.435812241068405e-06 -▁Nür 1.435812241068405e-06 -▁tow 1.435812241068405e-06 -geschneider 1.4320238182687521e-06 -reate 1.4320238182687521e-06 -ädchen 1.4320238182687521e-06 -▁Demonstr 1.4320238182687521e-06 -▁Pens 1.4320238182687521e-06 -▁vorherr 1.4320238182687521e-06 -gerung 1.4282353954690995e-06 -▁mixt 1.4282353954690995e-06 -etschen 1.4244469726694466e-06 -cuss 1.4206585498697939e-06 -ürk 1.4206585498697939e-06 -ÿ 1.4206585498697939e-06 -▁Ereign 1.4206585498697939e-06 -▁Verfüg 1.4206585498697939e-06 -▁ade 1.4206585498697939e-06 -▁foll 1.4206585498697939e-06 -▁hairdr 1.4206585498697939e-06 -▁ingr 1.4206585498697939e-06 -racies 1.416870127070141e-06 -ufficient 1.416870127070141e-06 -▁luxur 1.416870127070141e-06 -▁Barros 1.4130817042704883e-06 -▁Infl 1.4130817042704883e-06 -▁Slowen 1.4130817042704883e-06 -▁intelle 1.4130817042704883e-06 -chung 1.4092932814708354e-06 -ettbewerbs 1.4092932814708354e-06 -stood 1.4092932814708354e-06 -▁Anze 1.4092932814708354e-06 -▁Komb 1.4092932814708354e-06 -▁MID 1.4092932814708354e-06 -▁destro 1.4092932814708354e-06 -▁earthqu 1.4092932814708354e-06 -▁erörter 1.4092932814708354e-06 -▁grund 1.4092932814708354e-06 --Ben 1.4055048586711827e-06 -etzungen 1.4055048586711827e-06 -▁amen 1.4055048586711827e-06 -▁gentlem 1.4055048586711827e-06 --Ebene 1.4017164358715298e-06 -enende 1.4017164358715298e-06 -omod 1.4017164358715298e-06 -ventura 1.4017164358715298e-06 -▁Belast 1.4017164358715298e-06 -apos 1.3979280130718772e-06 -eichert 1.3979280130718772e-06 -fft 1.3979280130718772e-06 -▁abziel 1.3979280130718772e-06 -limited 1.3941395902722243e-06 -sequently 1.3941395902722243e-06 -Д 1.3941395902722243e-06 -chnen 1.3903511674725716e-06 -hann 1.3903511674725716e-06 -holfen 1.3903511674725716e-06 -ränkung 1.3903511674725716e-06 -▁Gründ 1.3903511674725716e-06 -▁Stär 1.3903511674725716e-06 -▁financ 1.3903511674725716e-06 -▁soph 1.3903511674725716e-06 -ogether 1.3865627446729187e-06 -▁benöt 1.3865627446729187e-06 -etres 1.382774321873266e-06 -hyth 1.382774321873266e-06 -pruch 1.382774321873266e-06 -▁awa 1.382774321873266e-06 -▁gestr 1.382774321873266e-06 -▁schnellst 1.382774321873266e-06 -inians 1.3789858990736131e-06 -ortion 1.3789858990736131e-06 -▁Monteneg 1.3789858990736131e-06 -ambod 1.3751974762739605e-06 -eop 1.3751974762739605e-06 -س 1.3751974762739605e-06 -▁EAD 1.3751974762739605e-06 -▁Nachhalt 1.3751974762739605e-06 -▁Woch 1.3751974762739605e-06 -▁applic 1.3751974762739605e-06 -▁benachricht 1.3751974762739605e-06 -▁rhyth 1.3751974762739605e-06 -abon 1.3714090534743076e-06 -lechter 1.3714090534743076e-06 -osit 1.3676206306746549e-06 -▁fas 1.3676206306746549e-06 -emein 1.363832207875002e-06 -inkel 1.363832207875002e-06 -▁Challen 1.363832207875002e-06 -▁gepl 1.363832207875002e-06 -cow 1.3600437850753493e-06 -eben 1.3600437850753493e-06 -endous 1.3600437850753493e-06 -staben 1.3600437850753493e-06 -▁Foll 1.3600437850753493e-06 -▁tex 1.3600437850753493e-06 -atul 1.3562553622756964e-06 -haltet 1.3562553622756964e-06 -hrase 1.3524669394760437e-06 -▁Castr 1.3524669394760437e-06 -ibliothe 1.3486785166763908e-06 -χ 1.3486785166763908e-06 -▁Montr 1.3486785166763908e-06 -▁agre 1.3486785166763908e-06 -▁vorhand 1.3486785166763908e-06 -hrow 1.3448900938767382e-06 -upten 1.3448900938767382e-06 -yen 1.3448900938767382e-06 -ßig 1.3448900938767382e-06 -äsent 1.3448900938767382e-06 -Б 1.3448900938767382e-06 -ك 1.3448900938767382e-06 -▁fift 1.3448900938767382e-06 -upts 1.3411016710770853e-06 -Ћ 1.3411016710770853e-06 -▁Verknü 1.3411016710770853e-06 -▁Yo 1.3411016710770853e-06 -operations 1.3373132482774326e-06 -verschm 1.3373132482774326e-06 -▁magnific 1.3373132482774326e-06 -prün 1.3335248254777797e-06 -▁Veranst 1.3335248254777797e-06 -▁balcon 1.3335248254777797e-06 -▁inhabit 1.3335248254777797e-06 --discr 1.329736402678127e-06 -ervor 1.329736402678127e-06 -ließen 1.329736402678127e-06 -▁Gemeins 1.329736402678127e-06 -▁Propert 1.329736402678127e-06 -À 1.3259479798784741e-06 -äbe 1.3259479798784741e-06 -▁compat 1.3259479798784741e-06 -afft 1.3221595570788215e-06 -demark 1.3221595570788215e-06 -ibilities 1.3221595570788215e-06 -ivileg 1.3221595570788215e-06 -riendly 1.3221595570788215e-06 -▁besteh 1.3221595570788215e-06 -ponsible 1.3183711342791686e-06 -▁Gonz 1.3183711342791686e-06 -▁Rass 1.3145827114795159e-06 -▁verschär 1.3145827114795159e-06 -ulgar 1.3070058658802103e-06 -▁Softw 1.3070058658802103e-06 -IDS 1.3032174430805574e-06 -▁Gipf 1.3032174430805574e-06 -bourg 1.2994290202809047e-06 -ratic 1.2994290202809047e-06 -▁ECH 1.2994290202809047e-06 -▁Erinner 1.2994290202809047e-06 -▁preg 1.2994290202809047e-06 -ettbew 1.2956405974812518e-06 -overnment 1.2956405974812518e-06 -▁conclus 1.2956405974812518e-06 -▁disappoint 1.2956405974812518e-06 -inely 1.2918521746815992e-06 -▁EU-Bür 1.2918521746815992e-06 -▁champ 1.2918521746815992e-06 -▁wissenschaft 1.2918521746815992e-06 -nütz 1.2880637518819463e-06 -¬ 1.2880637518819463e-06 -▁constituen 1.2880637518819463e-06 -▁recreat 1.2880637518819463e-06 -▁xenophob 1.2880637518819463e-06 -▁ärm 1.2880637518819463e-06 -okoll 1.2842753290822936e-06 -▁Weiterb 1.2842753290822936e-06 -▁beteil 1.2842753290822936e-06 -▁corpor 1.2842753290822936e-06 -gery 1.2804869062826409e-06 -ymmetr 1.2804869062826409e-06 -▁htt 1.2804869062826409e-06 -▁deg 1.276698483482988e-06 -entscheid 1.2729100606833353e-06 -klusive 1.2729100606833353e-06 -prene 1.2729100606833353e-06 -▁Fuer 1.2729100606833353e-06 -▁Galile 1.2729100606833353e-06 -katastrophen 1.2691216378836825e-06 -uffs 1.2691216378836825e-06 -▁Erweiter 1.2653332150840298e-06 -▁Verab 1.2653332150840298e-06 -▁empt 1.2653332150840298e-06 -▁erhöh 1.2653332150840298e-06 -▁neighbor 1.2653332150840298e-06 -vollständ 1.2615447922843769e-06 -äischen 1.2615447922843769e-06 -ämt 1.2615447922843769e-06 -▁gelö 1.2615447922843769e-06 --Sicherheit 1.2577563694847242e-06 -hrough 1.2577563694847242e-06 -meid 1.2577563694847242e-06 -uttle 1.2577563694847242e-06 -▁Affili 1.2577563694847242e-06 -▁pedestr 1.2577563694847242e-06 -erkennen 1.2539679466850713e-06 -irol 1.2539679466850713e-06 -ledge 1.2539679466850713e-06 -▁fäh 1.2539679466850713e-06 -annehm 1.2501795238854186e-06 -▁Verme 1.2501795238854186e-06 -lieder 1.2426026782861131e-06 -÷ 1.2426026782861131e-06 -▁Gipfelt 1.2388142554864602e-06 -sequent 1.2350258326868075e-06 -▁Rei 1.2350258326868075e-06 -▁Vereinfach 1.2350258326868075e-06 -haftet 1.2312374098871546e-06 -ingesch 1.2312374098871546e-06 -▁Biok 1.2312374098871546e-06 -▁atember 1.2312374098871546e-06 -▁confl 1.2312374098871546e-06 -▁enshr 1.2312374098871546e-06 -▁niederländ 1.2312374098871546e-06 -▁verhin 1.2312374098871546e-06 -aceutical 1.2274489870875019e-06 -coni 1.2274489870875019e-06 -istency 1.2274489870875019e-06 -▁sincer 1.2274489870875019e-06 -apest 1.223660564287849e-06 -▁Swed 1.223660564287849e-06 --cent 1.2198721414881963e-06 -ästinens 1.2198721414881963e-06 -▁Fischereise 1.2198721414881963e-06 -▁PN 1.2198721414881963e-06 -▁concr 1.2198721414881963e-06 -▁veröffent 1.2198721414881963e-06 -eregel 1.2160837186885435e-06 -fortun 1.2160837186885435e-06 -indestens 1.2160837186885435e-06 -▁benachbar 1.2160837186885435e-06 -abrik 1.2122952958888908e-06 -▁rar 1.2122952958888908e-06 -zne 1.2085068730892379e-06 -▁Spaz 1.2085068730892379e-06 -akzept 1.2047184502895852e-06 -umenten 1.2047184502895852e-06 -verstanden 1.2047184502895852e-06 -▁wundersch 1.2047184502895852e-06 -gerichtshof 1.2009300274899323e-06 -ibraries 1.2009300274899323e-06 -ölf 1.2009300274899323e-06 -▁Allian 1.2009300274899323e-06 -ORK 1.1971416046902796e-06 -lywood 1.1971416046902796e-06 -ourc 1.1971416046902796e-06 --Bere 1.1933531818906267e-06 -icip 1.1933531818906267e-06 -idesh 1.1933531818906267e-06 -neider 1.1933531818906267e-06 -owth 1.1895647590909741e-06 -schwemm 1.1895647590909741e-06 -ź 1.1895647590909741e-06 -▁Citiz 1.1895647590909741e-06 -▁chap 1.1895647590909741e-06 --Date 1.1857763362913212e-06 -124 1.1857763362913212e-06 -ainless 1.1857763362913212e-06 -ready 1.1857763362913212e-06 -ueber 1.1857763362913212e-06 -drop 1.1819879134916685e-06 -eitigung 1.1819879134916685e-06 -▁Jahrhunder 1.1819879134916685e-06 -▁Lett 1.1819879134916685e-06 -▁Nev 1.1819879134916685e-06 -▁freige 1.1819879134916685e-06 -▁schwed 1.1819879134916685e-06 -achläss 1.1781994906920156e-06 -endum 1.1781994906920156e-06 -ordern 1.1781994906920156e-06 -rengungen 1.1781994906920156e-06 -tags 1.1781994906920156e-06 -zubauen 1.1781994906920156e-06 -äusern 1.1781994906920156e-06 -▁Ausnahm 1.1781994906920156e-06 -icide 1.1744110678923629e-06 -▁kurzfrist 1.1744110678923629e-06 -genöss 1.17062264509271e-06 -icop 1.17062264509271e-06 -kong 1.17062264509271e-06 -▁Heidel 1.17062264509271e-06 -▁Kolumb 1.17062264509271e-06 -▁Liss 1.17062264509271e-06 -▁independ 1.17062264509271e-06 -blas 1.1668342222930573e-06 -mechan 1.1668342222930573e-06 -ochter 1.1668342222930573e-06 -ocop 1.1668342222930573e-06 -sebene 1.1668342222930573e-06 -И 1.1668342222930573e-06 -mittelsicherheit 1.1630457994934045e-06 -zuteilen 1.1630457994934045e-06 -ürf 1.1630457994934045e-06 -▁hinzuf 1.1630457994934045e-06 -▁wur 1.1630457994934045e-06 -ktop 1.1592573766937518e-06 -Fi 1.1554689538940989e-06 -rale 1.1554689538940989e-06 -▁REA 1.1554689538940989e-06 -▁batter 1.1554689538940989e-06 -usal 1.1516805310944462e-06 -▁Bibliothe 1.1516805310944462e-06 -▁Kräft 1.1516805310944462e-06 -▁Opf 1.1516805310944462e-06 -▁accompan 1.1516805310944462e-06 -▁pup 1.1516805310944462e-06 -etus 1.1478921082947933e-06 -äsch 1.1478921082947933e-06 -▁199 1.1478921082947933e-06 --Vert 1.1441036854951406e-06 -▁Bör 1.1441036854951406e-06 -▁Nep 1.1441036854951406e-06 -▁adequ 1.1441036854951406e-06 -▁anzub 1.1441036854951406e-06 -indruck 1.1403152626954877e-06 -tends 1.1403152626954877e-06 -wers 1.1403152626954877e-06 -ongue 1.1365268398958351e-06 -Ѓ 1.1365268398958351e-06 -▁distin 1.1365268398958351e-06 -ileg 1.1327384170961822e-06 -verläss 1.1327384170961822e-06 -ť 1.1327384170961822e-06 -▁Giov 1.1327384170961822e-06 -▁Sou 1.1327384170961822e-06 -ubil 1.1289499942965295e-06 -akei 1.1251615714968766e-06 -esthet 1.1251615714968766e-06 -ocolate 1.1251615714968766e-06 -¥ 1.1251615714968766e-06 -öffnet 1.1251615714968766e-06 -▁Dän 1.1251615714968766e-06 -▁cir 1.1251615714968766e-06 -▁lear 1.1251615714968766e-06 -estelle 1.1213731486972239e-06 -folgung 1.1213731486972239e-06 -ternal 1.1213731486972239e-06 -strichen 1.117584725897571e-06 -▁deter 1.117584725897571e-06 -▁vene 1.117584725897571e-06 -acent 1.1137963030979183e-06 -igrants 1.1137963030979183e-06 -▁Mean 1.1137963030979183e-06 -gerungen 1.1100078802982655e-06 -▁Bav 1.1100078802982655e-06 -▁Blizz 1.1100078802982655e-06 -▁Erleicht 1.1100078802982655e-06 -ereinander 1.1062194574986128e-06 -▁administr 1.1062194574986128e-06 -▁portugies 1.1062194574986128e-06 -Α 1.1024310346989599e-06 -ehnen 1.0986426118993072e-06 -ksamkeit 1.0986426118993072e-06 -▁nuklear 1.0986426118993072e-06 -alties 1.0948541890996545e-06 -isson 1.0948541890996545e-06 -eiler 1.0910657663000016e-06 -utragen 1.0910657663000016e-06 -vre 1.0910657663000016e-06 -Ï 1.0910657663000016e-06 -ooth 1.087277343500349e-06 -ween 1.087277343500349e-06 -▁Erwä 1.087277343500349e-06 -▁Zimb 1.087277343500349e-06 -ernet 1.0834889207006961e-06 -wh 1.0834889207006961e-06 -agent 1.0797004979010434e-06 -leisch 1.0797004979010434e-06 -ronics 1.0797004979010434e-06 -▁Eiff 1.0797004979010434e-06 -▁bout 1.0797004979010434e-06 -▁juris 1.0797004979010434e-06 -armon 1.0759120751013905e-06 -geleg 1.0759120751013905e-06 -riet 1.0759120751013905e-06 -ة 1.0759120751013905e-06 -▁Tus 1.0759120751013905e-06 -asonic 1.0721236523017378e-06 -entrums 1.0721236523017378e-06 -ύ 1.0721236523017378e-06 -embly 1.0683352295020849e-06 -haust 1.0683352295020849e-06 -stitute 1.0683352295020849e-06 -iddle 1.0645468067024322e-06 -iforn 1.0645468067024322e-06 -pts 1.0645468067024322e-06 -¹ 1.0645468067024322e-06 -▁Unterdrück 1.0645468067024322e-06 -▁vorzusch 1.0645468067024322e-06 -beschäft 1.0607583839027793e-06 -essourc 1.0607583839027793e-06 -igebiet 1.0607583839027793e-06 -▁Proced 1.0607583839027793e-06 -▁vorgesch 1.0607583839027793e-06 -ethod 1.0569699611031267e-06 -idency 1.0569699611031267e-06 -reng 1.0569699611031267e-06 -Л 1.0569699611031267e-06 -▁Räum 1.0569699611031267e-06 -▁lic 1.0569699611031267e-06 -▁empfehl 1.0531815383034738e-06 -kei 1.0493931155038211e-06 -▁Cathol 1.0493931155038211e-06 -▁Simb 1.0493931155038211e-06 -▁Stutt 1.0493931155038211e-06 -▁Thail 1.0493931155038211e-06 -irma 1.0456046927041682e-06 -merken 1.0456046927041682e-06 -▁erfahr 1.0456046927041682e-06 -▁hus 1.0456046927041682e-06 -▁schall 1.0456046927041682e-06 -öglichkeit 1.0418162699045155e-06 -Î 1.0380278471048626e-06 -▁ungar 1.0380278471048626e-06 -bitr 1.03423942430521e-06 -ennis 1.03423942430521e-06 -reuung 1.03423942430521e-06 -zeß 1.03423942430521e-06 -▁avo 1.03423942430521e-06 -▁erwä 1.03423942430521e-06 -▁licens 1.03423942430521e-06 -ekut 1.0304510015055571e-06 -gart 1.0304510015055571e-06 -usalem 1.0304510015055571e-06 -▁Ecu 1.0304510015055571e-06 -▁Sehenswürd 1.0304510015055571e-06 -etragen 1.0266625787059044e-06 -feiert 1.0266625787059044e-06 -landen 1.0266625787059044e-06 -▁Bemerk 1.0266625787059044e-06 -▁Entsteh 1.0266625787059044e-06 -▁pestic 1.0266625787059044e-06 -▁ult 1.0266625787059044e-06 -orse 1.0228741559062515e-06 -windigkeit 1.0228741559062515e-06 -▁197 1.0228741559062515e-06 -▁Grem 1.0228741559062515e-06 -▁Recy 1.0228741559062515e-06 -▁compuls 1.0228741559062515e-06 -È 1.0190857331065988e-06 -▁Euror 1.0190857331065988e-06 -▁Leip 1.0190857331065988e-06 -jing 1.0152973103069459e-06 -▁Janu 1.0152973103069459e-06 -merz 1.0115088875072932e-06 -valu 1.0115088875072932e-06 -▁Souverän 1.0115088875072932e-06 -Ê 1.0077204647076404e-06 -▁Verhe 1.0077204647076404e-06 -▁offic 1.0077204647076404e-06 -liegt 1.0039320419079877e-06 -städ 1.0039320419079877e-06 -▁Emot 1.0039320419079877e-06 -▁Ere 1.0039320419079877e-06 -kfast 1.0001436191083348e-06 -Ý 1.0001436191083348e-06 -▁Marok 1.0001436191083348e-06 -▁Wahrnehm 1.0001436191083348e-06 -▁Archit 9.96355196308682e-07 -▁verschied 9.96355196308682e-07 -φ 9.9256677350902921e-07 -▁Ansprech 9.9256677350902921e-07 -▁gefähr 9.9256677350902921e-07 -akul 9.8877835070937653e-07 -arly 9.8877835070937653e-07 -gehens 9.8877835070937653e-07 -▁FIF 9.8877835070937653e-07 -eils 9.8498992790972364e-07 -untu 9.8498992790972364e-07 -▁Beschleun 9.8498992790972364e-07 -▁elder 9.8498992790972364e-07 -ansa 9.81201505110071e-07 -ibeh 9.81201505110071e-07 -▁Pressem 9.81201505110071e-07 -▁simult 9.81201505110071e-07 --in-Office 9.7362465951076539e-07 -allery 9.7362465951076539e-07 -condition 9.7362465951076539e-07 -immung 9.7362465951076539e-07 -▁198 9.7362465951076539e-07 -▁ehemal 9.7362465951076539e-07 -ه 9.6983623671111249e-07 -▁Enttäus 9.6983623671111249e-07 -▁ausgestatt 9.6983623671111249e-07 -▁beru 9.6983623671111249e-07 -י 9.6604781391145981e-07 -▁part-s 9.6604781391145981e-07 --Stunden 9.62259391111807e-07 -iversary 9.62259391111807e-07 -▁misl 9.62259391111807e-07 -oftw 9.5847096831215424e-07 -▁Erfahr 9.5847096831215424e-07 -▁Seat 9.5847096831215424e-07 -▁Tamp 9.5847096831215424e-07 -▁biof 9.5847096831215424e-07 -bsp 9.5468254551250135e-07 -gypt 9.5468254551250135e-07 -inational 9.5468254551250135e-07 -kandidaten 9.5468254551250135e-07 -rankungen 9.5468254551250135e-07 -wieweit 9.5468254551250135e-07 -ändler 9.5468254551250135e-07 -˙ 9.5468254551250135e-07 -▁els 9.5468254551250135e-07 -▁ohn 9.5468254551250135e-07 -▁unentgelt 9.5468254551250135e-07 -empts 9.5089412271284867e-07 -▁Tast 9.5089412271284867e-07 -▁ine 9.5089412271284867e-07 -amentos 9.4710569991319589e-07 -oram 9.4710569991319589e-07 -▁aftern 9.4710569991319589e-07 -azine 9.433172771135431e-07 -cepts 9.433172771135431e-07 -▁Vergangen 9.433172771135431e-07 -▁urs 9.433172771135431e-07 -lauber 9.3952885431389031e-07 -urteilung 9.3952885431389031e-07 -▁Anp 9.3952885431389031e-07 -▁ausreich 9.3952885431389031e-07 -▁berücksicht 9.3952885431389031e-07 -▁princ 9.3952885431389031e-07 -ethe 9.3574043151423753e-07 -isko 9.3574043151423753e-07 -▁Rena 9.3574043151423753e-07 --Dollar 9.3195200871458474e-07 -▁Veränder 9.3195200871458474e-07 -▁precaution 9.3195200871458474e-07 --of-the- 9.28163585914932e-07 -gehoben 9.28163585914932e-07 -ف 9.28163585914932e-07 -▁Hels 9.28163585914932e-07 -▁Omb 9.28163585914932e-07 -▁furnish 9.28163585914932e-07 -▁getest 9.28163585914932e-07 -eständ 9.2437516311527917e-07 -ypern 9.2437516311527917e-07 -Ż 9.2437516311527917e-07 -▁endgült 9.2437516311527917e-07 -andidaten 9.2058674031562639e-07 -hesion 9.2058674031562639e-07 -prechung 9.2058674031562639e-07 -scheinend 9.2058674031562639e-07 -▁Proz 9.2058674031562639e-07 -▁multil 9.2058674031562639e-07 -arsh 9.167983175159736e-07 -є 9.167983175159736e-07 -▁Nether 9.167983175159736e-07 -▁phenomen 9.167983175159736e-07 -▁sozio 9.167983175159736e-07 -giene 9.1300989471632081e-07 -ozy 9.1300989471632081e-07 -vertreten 9.1300989471632081e-07 -¨ 9.1300989471632081e-07 -أ 9.1300989471632081e-07 -▁Dring 9.1300989471632081e-07 -▁Viet 9.1300989471632081e-07 -▁personen 9.1300989471632081e-07 -ambres 9.09221471916668e-07 -pread 9.09221471916668e-07 -verages 9.09221471916668e-07 -У 9.09221471916668e-07 -▁Maßst 9.09221471916668e-07 -▁convin 9.09221471916668e-07 -▁korrig 9.09221471916668e-07 -knü 9.0543304911701524e-07 -rebungen 9.0543304911701524e-07 -vironment 9.0543304911701524e-07 -▁Addition 9.0543304911701524e-07 -▁intu 9.0543304911701524e-07 -amik 9.0164462631736246e-07 -leys 9.0164462631736246e-07 -▁Vierten 9.0164462631736246e-07 -▁gratul 9.0164462631736246e-07 --Pres 8.9785620351770967e-07 --how 8.9785620351770967e-07 -areness 8.9785620351770967e-07 -ermo 8.9785620351770967e-07 -ranspar 8.9785620351770967e-07 -ängerung 8.9785620351770967e-07 -З 8.9785620351770967e-07 -▁behavi 8.9785620351770967e-07 -▁widersp 8.9785620351770967e-07 -enary 8.9406778071805689e-07 -zuheben 8.9406778071805689e-07 -▁Brutto 8.9406778071805689e-07 -▁Ergän 8.9406778071805689e-07 -▁Mitch 8.9406778071805689e-07 -▁orig 8.9406778071805689e-07 -adian 8.902793579184041e-07 -enness 8.902793579184041e-07 -idance 8.902793579184041e-07 -wärtig 8.902793579184041e-07 -▁Ernen 8.902793579184041e-07 -▁obst 8.902793579184041e-07 -▁thumbn 8.902793579184041e-07 -▁Überw 8.902793579184041e-07 -quot 8.8649093511875132e-07 -zicht 8.8649093511875132e-07 -Ş 8.8270251231909853e-07 -▁nachhalt 8.8270251231909853e-07 -inded 8.7891408951944574e-07 -stritten 8.7891408951944574e-07 -trock 8.7891408951944574e-07 -▁Paläst 8.7891408951944574e-07 -▁Seb 8.7891408951944574e-07 -vell 8.75125666719793e-07 -▁Sien 8.75125666719793e-07 --to-d 8.7133724392014017e-07 -▁Guatem 8.7133724392014017e-07 -▁Ora 8.7133724392014017e-07 -▁prü 8.7133724392014017e-07 -Press 8.6754882112048739e-07 -joy 8.6754882112048739e-07 -َ 8.637603983208346e-07 -▁vorank 8.637603983208346e-07 -▁Kommunik 8.5997197552118182e-07 -gleichheit 8.56183552721529e-07 -ibrary 8.56183552721529e-07 -mmern 8.56183552721529e-07 -▁GIM 8.56183552721529e-07 -▁Gelt 8.56183552721529e-07 --condition 8.5239512992187624e-07 -▁Nordir 8.5239512992187624e-07 -eoples 8.4860670712222346e-07 -mbH 8.4860670712222346e-07 -wanne 8.4860670712222346e-07 -▁Wäl 8.4860670712222346e-07 --border 8.4481828432257067e-07 -quiv 8.4481828432257067e-07 -schwing 8.4481828432257067e-07 -tains 8.4481828432257067e-07 -Τ 8.4481828432257067e-07 -ategorien 8.4102986152291789e-07 -fül 8.4102986152291789e-07 -▁blie 8.4102986152291789e-07 -▁hilfs 8.4102986152291789e-07 -utenant 8.372414387232651e-07 -Ќ 8.372414387232651e-07 -▁retir 8.372414387232651e-07 -altar 8.3345301592361232e-07 -appropri 8.3345301592361232e-07 -imir 8.3345301592361232e-07 -lieb 8.3345301592361232e-07 --suite 8.2966459312395953e-07 -▁bess 8.2966459312395953e-07 -▁küm 8.2966459312395953e-07 -▁purch 8.2966459312395953e-07 -kömm 8.2587617032430674e-07 -läß 8.2587617032430674e-07 -▁Kürz 8.2587617032430674e-07 -▁hinnehm 8.2587617032430674e-07 -▁reimb 8.2587617032430674e-07 --Emissionen 8.2208774752465406e-07 -rechend 8.2208774752465406e-07 -wann 8.2208774752465406e-07 -▁Alic 8.2208774752465406e-07 -▁Democ 8.2208774752465406e-07 -ecially 8.1829932472500128e-07 -þ 8.1829932472500128e-07 -▁Tschetschen 8.1829932472500128e-07 -▁glob 8.1829932472500128e-07 -ateful 8.1451090192534849e-07 -everkehr 8.1451090192534849e-07 -Е 8.1072247912569571e-07 -▁tatsäch 8.1072247912569571e-07 -künft 8.0693405632604292e-07 -vements 8.0693405632604292e-07 -▁ermord 8.0693405632604292e-07 -▁gewid 8.0693405632604292e-07 -▁ziem 8.0693405632604292e-07 --Office 8.0314563352639014e-07 -ivil 8.0314563352639014e-07 -▁Akzept 8.0314563352639014e-07 -▁rand 8.0314563352639014e-07 -▁wholehear 8.0314563352639014e-07 -amework 7.9935721072673735e-07 -ikanen 7.9935721072673735e-07 -ilber 7.9935721072673735e-07 -iquid 7.9556878792708457e-07 -▁Lt 7.9556878792708457e-07 -▁unaccept 7.9556878792708457e-07 -hältnisse 7.9178036512743178e-07 -▁Gere 7.9178036512743178e-07 -▁Währ 7.9178036512743178e-07 -door 7.87991942327779e-07 -eauc 7.87991942327779e-07 -ikalien 7.87991942327779e-07 -mär 7.87991942327779e-07 -▁E-M 7.87991942327779e-07 -▁Ernähr 7.87991942327779e-07 -.e. 7.8420351952812621e-07 -itives 7.8420351952812621e-07 -ovol 7.8420351952812621e-07 -▁Sloven 7.8420351952812621e-07 -● 7.8420351952812621e-07 -า 7.8041509672847342e-07 -▁Klär 7.8041509672847342e-07 -▁Subst 7.8041509672847342e-07 -▁air-condition 7.8041509672847342e-07 -bewertungen 7.7662667392882064e-07 -etext 7.7662667392882064e-07 -iovis 7.7662667392882064e-07 --spec 7.7283825112916785e-07 -fety 7.7283825112916785e-07 -udsman 7.7283825112916785e-07 -▁Düss 7.7283825112916785e-07 -stances 7.6904982832951507e-07 -▁Rett 7.6904982832951507e-07 -viron 7.6526140552986228e-07 -▁Bezieh 7.6526140552986228e-07 -aggage 7.6147298273020949e-07 -ascript 7.6147298273020949e-07 -attle 7.6147298273020949e-07 -▁Verhaltensk 7.6147298273020949e-07 -clude 7.5768455993055671e-07 -ewhere 7.5768455993055671e-07 -Ţ 7.5768455993055671e-07 -▁biod 7.5768455993055671e-07 -▁konfront 7.5768455993055671e-07 -▁tas 7.5768455993055671e-07 -iehlt 7.5389613713090392e-07 -▁Entertain 7.5389613713090392e-07 -▁Fortsch 7.5389613713090392e-07 -▁Verhin 7.5389613713090392e-07 --Fi 7.5010771433125114e-07 -ategories 7.5010771433125114e-07 -eget 7.5010771433125114e-07 -yption 7.5010771433125114e-07 -▁Matth 7.5010771433125114e-07 -▁Sier 7.5010771433125114e-07 -▁franz 7.5010771433125114e-07 -heber 7.4631929153159835e-07 -▁Amend 7.4631929153159835e-07 -▁Konver 7.4631929153159835e-07 -Ô 7.4253086873194557e-07 -▁sämt 7.4253086873194557e-07 -oubtedly 7.3874244593229278e-07 -ountry 7.3874244593229278e-07 -▁Slidesh 7.3874244593229278e-07 -▁zwangs 7.3874244593229278e-07 -conom 7.3495402313264e-07 -ifelong 7.3495402313264e-07 -itably 7.3495402313264e-07 -ufact 7.3495402313264e-07 -▁Verbrech 7.3495402313264e-07 -5-0 7.3116560033298721e-07 -quisite 7.3116560033298721e-07 - 7.3116560033298721e-07 -moking 7.2737717753333442e-07 -uggle 7.2737717753333442e-07 -▁Shang 7.2737717753333442e-07 -▁ht 7.2737717753333442e-07 -▁semin 7.2737717753333442e-07 -illance 7.2358875473368164e-07 -sprochen 7.2358875473368164e-07 -äische 7.2358875473368164e-07 -human 7.1980033193402885e-07 -ouver 7.1980033193402885e-07 -▁Auseinander 7.1980033193402885e-07 -▁reaff 7.1980033193402885e-07 -lichungen 7.1601190913437607e-07 -▁Erklär 7.1601190913437607e-07 -▁Gegeben 7.1601190913437607e-07 -▁Orig 7.1601190913437607e-07 -▁allm 7.1601190913437607e-07 -thand 7.1222348633472328e-07 -▁Berücksicht 7.1222348633472328e-07 -▁beglückwün 7.1222348633472328e-07 -▁beibeh 7.1222348633472328e-07 -ahoo 7.084350635350705e-07 -І 7.084350635350705e-07 -▁Nürn 7.084350635350705e-07 -▁bizar 7.084350635350705e-07 -▁Repar 7.0464664073541771e-07 -▁vorant 7.0464664073541771e-07 -britann 7.0085821793576492e-07 -cean 7.0085821793576492e-07 -▁Umwand 7.0085821793576492e-07 -▁fulf 7.0085821793576492e-07 -andon 6.9706979513611214e-07 -enophob 6.9706979513611214e-07 -tera 6.9706979513611214e-07 -▁spekt 6.9706979513611214e-07 -uture 6.9328137233645935e-07 -▁Forder 6.9328137233645935e-07 -▁coher 6.9328137233645935e-07 -▁Äg 6.9328137233645935e-07 -agraph 6.8949294953680657e-07 -▁Arzneim 6.8949294953680657e-07 -▁Toul 6.8949294953680657e-07 -▁exper 6.8949294953680657e-07 -itiativen 6.8570452673715378e-07 -ج 6.8570452673715378e-07 -kest 6.81916103937501e-07 -ª 6.81916103937501e-07 -Ð 6.81916103937501e-07 -ancellor 6.7812768113784821e-07 -fox 6.7812768113784821e-07 -ipedia 6.7812768113784821e-07 -▁upd 6.7812768113784821e-07 -aure 6.7433925833819542e-07 -iscipl 6.7433925833819542e-07 -▁Fotok 6.7433925833819542e-07 -▁medic 6.7433925833819542e-07 -haps 6.7055083553854264e-07 -▁Nichtregierungs 6.7055083553854264e-07 --rea 6.6676241273888985e-07 -▁Frankfur 6.6676241273888985e-07 -ambre 6.6297398993923707e-07 -dresse 6.6297398993923707e-07 -eague 6.6297398993923707e-07 -kompl 6.6297398993923707e-07 -rency 6.6297398993923707e-07 -א 6.6297398993923707e-07 -▁Klav 6.6297398993923707e-07 -▁Nahr 6.6297398993923707e-07 -▁Zin 6.6297398993923707e-07 -▁erleicht 6.6297398993923707e-07 -▁unzure 6.6297398993923707e-07 -lere 6.5918556713958428e-07 -pectives 6.5918556713958428e-07 -phrase 6.553971443399315e-07 -ร 6.553971443399315e-07 -▁Arzne 6.553971443399315e-07 -▁Freiz 6.553971443399315e-07 -▁Vorschl 6.553971443399315e-07 -▁phen 6.553971443399315e-07 -edig 6.5160872154027871e-07 -ו 6.4782029874062592e-07 -▁commod 6.4782029874062592e-07 -▁posit 6.4782029874062592e-07 -hatt 6.4403187594097314e-07 -obei 6.4403187594097314e-07 -ternehmen 6.4403187594097314e-07 -ubiläum 6.4403187594097314e-07 -uese 6.4403187594097314e-07 -▁portug 6.4403187594097314e-07 -stande 6.4024345314132046e-07 -ی 6.4024345314132046e-07 -▁Newsp 6.4024345314132046e-07 -▁twel 6.4024345314132046e-07 -brigen 6.3645503034166767e-07 -Über 6.3645503034166767e-07 -▁Auth 6.3645503034166767e-07 -▁Lear 6.3645503034166767e-07 -▁Thurs 6.3645503034166767e-07 -▁territor 6.3645503034166767e-07 -▁trium 6.3645503034166767e-07 -▁Lanz 6.3266660754201489e-07 -▁Mazed 6.3266660754201489e-07 -▁unzähl 6.3266660754201489e-07 -▁vorbeh 6.3266660754201489e-07 -teneg 6.288781847423621e-07 -′ 6.288781847423621e-07 -▁Secr 6.288781847423621e-07 -▁stär 6.288781847423621e-07 -▁unpre 6.288781847423621e-07 -mittelt 6.2508976194270932e-07 -orteil 6.2508976194270932e-07 -though 6.2508976194270932e-07 -örde 6.2508976194270932e-07 -Ő 6.2508976194270932e-07 -▁Herangehens 6.2508976194270932e-07 -fence 6.2130133914305653e-07 -oments 6.2130133914305653e-07 -▁Washing 6.2130133914305653e-07 -▁infrastruct 6.2130133914305653e-07 -▁langu 6.2130133914305653e-07 -▁sacr 6.2130133914305653e-07 -yside 6.1751291634340375e-07 -prechen 6.13724493543751e-07 -quare 6.13724493543751e-07 -▁beitr 6.13724493543751e-07 -drücklich 6.0993607074409817e-07 -shine 6.0993607074409817e-07 -น 6.0993607074409817e-07 -▁Nachfol 6.0993607074409817e-07 -▁Wohler 6.0993607074409817e-07 -▁gegenwärt 6.0993607074409817e-07 -▁grenz 6.0993607074409817e-07 -▁produc 6.0993607074409817e-07 -roökonom 6.0614764794444539e-07 -Э 6.0614764794444539e-07 -frastr 6.023592251447926e-07 -ärenz 6.023592251447926e-07 -unft 5.9857080234513982e-07 -▁Distr 5.9857080234513982e-07 -▁aufrechter 5.9857080234513982e-07 -glück 5.94782379545487e-07 -ش 5.94782379545487e-07 -▁Kinn 5.94782379545487e-07 -▁Liban 5.94782379545487e-07 -▁unang 5.94782379545487e-07 -bsite 5.9099395674583425e-07 -ployed 5.9099395674583425e-07 -pricht 5.9099395674583425e-07 -▁Subsid 5.9099395674583425e-07 -aukas 5.8720553394618146e-07 -Ф 5.8720553394618146e-07 -▁Birm 5.8720553394618146e-07 -▁Wün 5.8720553394618146e-07 -▁dut 5.8720553394618146e-07 -▁experien 5.8720553394618146e-07 -freiheiten 5.8341711114652867e-07 -kok 5.8341711114652867e-07 -olden 5.8341711114652867e-07 -▁Verring 5.8341711114652867e-07 -▁genu 5.8341711114652867e-07 -▁whe 5.8341711114652867e-07 -konom 5.7962868834687589e-07 -ώ 5.7962868834687589e-07 -▁Meld 5.7962868834687589e-07 -▁benefic 5.7962868834687589e-07 -schließlich 5.758402655472231e-07 -▁Annäher 5.758402655472231e-07 -▁Californ 5.758402655472231e-07 -▁Parl 5.758402655472231e-07 -▁Weihn 5.758402655472231e-07 -▁certific 5.758402655472231e-07 -▁jac 5.758402655472231e-07 -athedrale 5.7205184274757032e-07 -look 5.7205184274757032e-07 -ר 5.7205184274757032e-07 -▁Behand 5.7205184274757032e-07 -▁Croat 5.7205184274757032e-07 -▁Städ 5.7205184274757032e-07 -▁beur 5.7205184274757032e-07 -ecess 5.6826341994791753e-07 -▁attra 5.6826341994791753e-07 -IFE 5.6447499714826475e-07 -conditioning 5.6447499714826475e-07 -ibungs 5.6447499714826475e-07 -เ 5.6447499714826475e-07 -▁Jap 5.6447499714826475e-07 -▁Siem 5.6447499714826475e-07 -olge 5.60686574348612e-07 -▁Prozents 5.60686574348612e-07 -iago 5.5689815154895917e-07 -uminium 5.5689815154895917e-07 -▁KU 5.5689815154895917e-07 -¦ 5.5310972874930639e-07 -ح 5.5310972874930639e-07 -خ 5.5310972874930639e-07 -″ 5.5310972874930639e-07 -fl 5.5310972874930639e-07 -▁self-em 5.493213059496536e-07 -sprüch 5.4553288315000082e-07 -ußert 5.4553288315000082e-07 -Ν 5.4553288315000082e-07 -อ 5.4553288315000082e-07 -▁Tues 5.4553288315000082e-07 -▁Vice-Pres 5.4553288315000082e-07 -▁muse 5.4553288315000082e-07 -anbul 5.41744460350348e-07 -‟ 5.41744460350348e-07 -itime 5.3795603755069525e-07 -Å 5.3795603755069525e-07 -إ 5.3795603755069525e-07 -▁Völ 5.3795603755069525e-07 -itzerland 5.3416761475104246e-07 -taking 5.3416761475104246e-07 -¾ 5.3416761475104246e-07 -Љ 5.3416761475104246e-07 -ِ 5.3416761475104246e-07 -▁Syd 5.3416761475104246e-07 -▁hervorragen 5.3416761475104246e-07 -▁signific 5.3416761475104246e-07 -▁umgew 5.3416761475104246e-07 -auspiel 5.3037919195138967e-07 -istol 5.3037919195138967e-07 -undheit 5.3037919195138967e-07 -vernight 5.3037919195138967e-07 -weitet 5.3037919195138967e-07 -њ 5.2659076915173689e-07 -▁emerg 5.228023463520841e-07 -▁Fischereip 5.1901392355243132e-07 -▁Karib 5.1901392355243132e-07 -▁Untersuch 5.1901392355243132e-07 -▁kommer 5.1901392355243132e-07 -▁sub-c 5.1901392355243132e-07 -eservice 5.1522550075277853e-07 -esonder 5.1522550075277853e-07 -▁Sams 5.1522550075277853e-07 -▁ält 5.1522550075277853e-07 -ancell 5.1143707795312575e-07 -gesamt 5.1143707795312575e-07 -otokoll 5.1143707795312575e-07 -Š 5.1143707795312575e-07 -Ι 5.1143707795312575e-07 -Ο 5.1143707795312575e-07 -№ 5.1143707795312575e-07 -▁Herunter 5.1143707795312575e-07 -▁unem 5.1143707795312575e-07 -▁Beschwer 5.07648655153473e-07 -▁Verhand 5.07648655153473e-07 -▁verwirkl 5.07648655153473e-07 -eutung 5.0386023235382018e-07 -stockung 5.0386023235382018e-07 -völkerung 5.0386023235382018e-07 -ξ 5.0386023235382018e-07 -← 5.0386023235382018e-07 -▁Subsidiar 5.0386023235382018e-07 -▁interf 5.0386023235382018e-07 -▁kontext 5.0386023235382018e-07 --Mittelmeer 5.0007180955416739e-07 -demokraten 5.0007180955416739e-07 -▁Cau 4.962833867545146e-07 -▁Einstimm 4.962833867545146e-07 -▁bec 4.962833867545146e-07 -▁crimin 4.962833867545146e-07 -▁gg 4.962833867545146e-07 -ilege 4.9249496395486182e-07 -※ 4.9249496395486182e-07 -▁Kernen 4.9249496395486182e-07 -, 4.9249496395486182e-07 -Pal 4.88706541155209e-07 -isdom 4.88706541155209e-07 -gess 4.8491811835555625e-07 -zellent 4.8491811835555625e-07 -ämlich 4.8491811835555625e-07 -▁Oly 4.8491811835555625e-07 -▁scen 4.8491811835555625e-07 -的 4.8491811835555625e-07 -Я 4.8112969555590346e-07 -▁Beaut 4.8112969555590346e-07 -▁Fotokop 4.8112969555590346e-07 -▁disreg 4.8112969555590346e-07 -▁hät 4.8112969555590346e-07 -▁unforg 4.8112969555590346e-07 -PEG 4.7734127275625068e-07 -feindlichkeit 4.7734127275625068e-07 -ologne 4.7734127275625068e-07 -¢ 4.7734127275625068e-07 -‡ 4.7734127275625068e-07 -⇒ 4.7734127275625068e-07 -▁Flugh 4.7734127275625068e-07 -ofür 4.7355284995659794e-07 -utory 4.7355284995659794e-07 -▁erziel 4.7355284995659794e-07 -Tube 4.6976442715694516e-07 -inuier 4.6976442715694516e-07 -Đ 4.6976442715694516e-07 -Х 4.6976442715694516e-07 -▁Gespr 4.6976442715694516e-07 -▁Kontinu 4.6976442715694516e-07 -▁entde 4.6976442715694516e-07 -iminary 4.6597600435729237e-07 -ל 4.6597600435729237e-07 -่ 4.6597600435729237e-07 -▁Verzö 4.6597600435729237e-07 -▁Zuer 4.6597600435729237e-07 -▁verschlechter 4.6597600435729237e-07 -dehnung 4.6218758155763959e-07 -ertungen 4.6218758155763959e-07 -▁Elev 4.6218758155763959e-07 -▁irreg 4.6218758155763959e-07 -▁refres 4.6218758155763959e-07 -athem 4.583991587579868e-07 -ม 4.583991587579868e-07 -▁jeweil 4.583991587579868e-07 -▁überras 4.583991587579868e-07 -。 4.583991587579868e-07 -atastrop 4.54610735958334e-07 -ergestellt 4.54610735958334e-07 -ittle 4.54610735958334e-07 -Þ 4.54610735958334e-07 -ņ 4.54610735958334e-07 -▁Benj 4.54610735958334e-07 -▁beeintr 4.54610735958334e-07 -▁wides 4.54610735958334e-07 -中 4.54610735958334e-07 -forward 4.5082231315868123e-07 -isticated 4.5082231315868123e-07 -ocaust 4.5082231315868123e-07 -outique 4.5082231315868123e-07 -pth 4.5082231315868123e-07 -ง 4.5082231315868123e-07 -▁Berlus 4.5082231315868123e-07 -elso 4.4703389035902844e-07 -governmental 4.4703389035902844e-07 -ianti 4.4703389035902844e-07 -uführen 4.4703389035902844e-07 -▁Annehm 4.4703389035902844e-07 -▁hap 4.4703389035902844e-07 -accept 4.4324546755937566e-07 -isory 4.4324546755937566e-07 -ע 4.4324546755937566e-07 -▁Ingen 4.4324546755937566e-07 -▁tourn 4.4324546755937566e-07 -hton 4.3945704475972287e-07 -▁Geträn 4.3566862196007009e-07 -▁Wahrschein 4.3566862196007009e-07 -▁meas 4.3566862196007009e-07 -▁Byr 4.318801991604173e-07 -▁Lanzar 4.318801991604173e-07 -▁datab 4.318801991604173e-07 -IEW 4.2809177636076452e-07 -ocktail 4.2430335356111173e-07 -terity 4.2430335356111173e-07 -▁Anbet 4.2430335356111173e-07 -▁Neap 4.2430335356111173e-07 -binsel 4.2051493076145894e-07 -▁Vitor 4.2051493076145894e-07 -▁bevorzu 4.2051493076145894e-07 -▁suc 4.2051493076145894e-07 -▁Präs 4.1672650796180616e-07 -▁Tosk 4.1672650796180616e-07 -▁gewährleist 4.1672650796180616e-07 -▁municip 4.1672650796180616e-07 -▁Überschwemm 4.1672650796180616e-07 -sych 4.1293808516215337e-07 -▁Angr 4.1293808516215337e-07 -▁dang 4.1293808516215337e-07 -▁encr 4.1293808516215337e-07 -etooth 4.0914966236250064e-07 -klear 4.0914966236250064e-07 -ußer 4.0914966236250064e-07 -trockner 4.0536123956284785e-07 -twoch 4.0536123956284785e-07 -▁Thessalon 4.0536123956284785e-07 -▁betreff 4.0536123956284785e-07 -cedes 4.0157281676319507e-07 -ektionismus 4.0157281676319507e-07 -▁Bost 4.0157281676319507e-07 -▁scener 4.0157281676319507e-07 -hältnis 3.9778439396354228e-07 -ubehör 3.9778439396354228e-07 -▁Bettw 3.9778439396354228e-07 -▁appre 3.9778439396354228e-07 -▁incent 3.9778439396354228e-07 -▁proced 3.9778439396354228e-07 -▁erled 3.939959711638895e-07 -imini 3.9020754836423671e-07 -▁Carib 3.8641912556458393e-07 -erce 3.8263070276493114e-07 -izeil 3.8263070276493114e-07 -nächst 3.8263070276493114e-07 -schlossenheit 3.8263070276493114e-07 -endit 3.7884227996527835e-07 -udem 3.7884227996527835e-07 -▁Araf 3.7884227996527835e-07 --of-the-art 3.7505385716562557e-07 --reaching 3.7505385716562557e-07 -undry 3.7505385716562557e-07 -▁geplan 3.7126543436597278e-07 -▁inappropri 3.7126543436597278e-07 -ivile 3.6747701156632e-07 -meth 3.6747701156632e-07 -sphäre 3.6747701156632e-07 -wier 3.6747701156632e-07 -▁Berech 3.6747701156632e-07 -▁Wednes 3.6747701156632e-07 -▁genoc 3.6747701156632e-07 -kontr 3.6368858876666721e-07 -unya 3.6368858876666721e-07 -▁Gibr 3.6368858876666721e-07 -▁feh 3.6368858876666721e-07 --mail 3.5990016596701443e-07 -ugoslav 3.5990016596701443e-07 -▁genü 3.5990016596701443e-07 --170 3.5611174316736164e-07 -azines 3.5611174316736164e-07 -erbs 3.5611174316736164e-07 -wogenheit 3.5611174316736164e-07 -▁Dail 3.5611174316736164e-07 -▁inev 3.5611174316736164e-07 -ubeh 3.5232332036770885e-07 -öffnen 3.5232332036770885e-07 -▁Caucas 3.5232332036770885e-07 -▁archit 3.5232332036770885e-07 -▁begrü 3.5232332036770885e-07 -▁whol 3.5232332036770885e-07 -▁zunehm 3.5232332036770885e-07 -cany 3.4474647476840328e-07 -ibliot 3.4474647476840328e-07 -osur 3.4474647476840328e-07 -▁resour 3.4474647476840328e-07 --called 3.409580519687505e-07 -ettable 3.409580519687505e-07 -rechter 3.409580519687505e-07 -urecht 3.409580519687505e-07 -wirr 3.409580519687505e-07 -▁Zuverläss 3.409580519687505e-07 -▁shap 3.409580519687505e-07 -▁Berichterst 3.3716962916909771e-07 --ranging 3.3338120636944493e-07 -ossier 3.3338120636944493e-07 -oust 3.3338120636944493e-07 -porate 3.3338120636944493e-07 -Č 3.3338120636944493e-07 -▁Kaliforn 3.3338120636944493e-07 -▁nöt 3.3338120636944493e-07 --Entw 3.2959278356979214e-07 -ehow 3.2959278356979214e-07 -ilst 3.2959278356979214e-07 -▁Lae 3.2959278356979214e-07 -▁Singap 3.2959278356979214e-07 -etimes 3.2580436077013936e-07 -iefung 3.2580436077013936e-07 -kohol 3.2580436077013936e-07 -▁Liebl 3.2580436077013936e-07 -▁Vereinbar 3.2580436077013936e-07 -▁anbiet 3.2580436077013936e-07 -▁bemü 3.2580436077013936e-07 -▁hers 3.2580436077013936e-07 -leet 3.2201593797048657e-07 -omenade 3.2201593797048657e-07 -ziehe 3.2201593797048657e-07 -▁progr 3.2201593797048657e-07 -aurant 3.1822751517083384e-07 -▁Abwesen 3.1822751517083384e-07 -▁refr 3.1822751517083384e-07 -▁Änder 3.1822751517083384e-07 -rasch 3.1443909237118105e-07 -wirklichung 3.1443909237118105e-07 -▁Marb 3.1443909237118105e-07 -erstellen 3.1065066957152827e-07 -wiegend 3.1065066957152827e-07 -▁Begeister 3.1065066957152827e-07 -▁Heut 3.1065066957152827e-07 --Mediterranean 3.0686224677187548e-07 -rengung 3.0686224677187548e-07 -tworten 3.0686224677187548e-07 -ügigkeit 3.0686224677187548e-07 -▁Nok 3.0686224677187548e-07 -▁Unregelmäß 3.0686224677187548e-07 -▁beeinfluss 3.0686224677187548e-07 -▁grundlegen 3.0686224677187548e-07 -▁immedi 3.0686224677187548e-07 -ASP 3.0307382397222269e-07 -aos 3.0307382397222269e-07 -atastroph 3.0307382397222269e-07 -▁Ferrero 3.0307382397222269e-07 -▁Klimasch 3.0307382397222269e-07 -▁Perspekt 2.9928540117256991e-07 -▁assoc 2.9928540117256991e-07 -▁opin 2.9928540117256991e-07 --to-date 2.9549697837291712e-07 -igenz 2.9549697837291712e-07 -▁Manhatt 2.9549697837291712e-07 -▁Tats 2.9549697837291712e-07 -▁Untern 2.9549697837291712e-07 -▁advant 2.9549697837291712e-07 -▁tats 2.9549697837291712e-07 -kthrough 2.9170855557326434e-07 -▁jem 2.9170855557326434e-07 -bry 2.8792013277361155e-07 -ermost 2.8792013277361155e-07 -indruckt 2.8792013277361155e-07 -▁indir 2.8792013277361155e-07 -▁larg 2.8792013277361155e-07 -▁müß 2.8792013277361155e-07 -▁zuver 2.8792013277361155e-07 -ECD 2.8413170997395877e-07 -▁offl 2.8413170997395877e-07 -▁refle 2.8413170997395877e-07 -▁Milosev 2.80343287174306e-07 -▁Urspr 2.80343287174306e-07 -VIEW 2.7655486437465319e-07 -zustimmen 2.7655486437465319e-07 -▁Algar 2.7655486437465319e-07 -▁Eras 2.7655486437465319e-07 -▁Varian 2.7655486437465319e-07 -▁tä 2.7655486437465319e-07 -gestimmt 2.7276644157500041e-07 -▁criter 2.7276644157500041e-07 -expected 2.6897801877534762e-07 -inki 2.6897801877534762e-07 -ј 2.6897801877534762e-07 -▁Gip 2.6897801877534762e-07 -▁Handhab 2.6897801877534762e-07 -▁Infine 2.6897801877534762e-07 -▁Medik 2.6897801877534762e-07 -▁Stras 2.6897801877534762e-07 --Wald 2.6518959597569484e-07 -gän 2.6518959597569484e-07 -strateg 2.6518959597569484e-07 -▁eingen 2.6518959597569484e-07 -▁jen 2.6518959597569484e-07 -▁toget 2.6518959597569484e-07 -▁vorlie 2.6518959597569484e-07 -imensionen 2.6140117317604205e-07 -▁Kompeten 2.6140117317604205e-07 -▁Rotter 2.6140117317604205e-07 -▁gän 2.6140117317604205e-07 -▁incompl 2.6140117317604205e-07 -Kr 2.5761275037638927e-07 -tedly 2.5761275037638927e-07 -▁Bolog 2.5761275037638927e-07 -ckst 2.5382432757673648e-07 -ovon 2.5382432757673648e-07 -ubehalten 2.5382432757673648e-07 -▁Edin 2.5382432757673648e-07 -▁Palest 2.5382432757673648e-07 -besondere 2.5003590477708369e-07 -ugos 2.5003590477708369e-07 --Ru 2.4624748197743091e-07 -abei 2.4624748197743091e-07 -▁auth 2.4624748197743091e-07 -regelmäß 2.4245905917777812e-07 -robat 2.4245905917777812e-07 -▁Declar 2.4245905917777812e-07 -▁inten 2.4245905917777812e-07 -▁surpr 2.4245905917777812e-07 -▁toba 2.4245905917777812e-07 -ESCO 2.3867063637812534e-07 -emost 2.3867063637812534e-07 -rebt 2.3867063637812534e-07 -regungen 2.3867063637812534e-07 -▁Alz 2.3867063637812534e-07 -cknow 2.3488221357847258e-07 -▁Daim 2.3488221357847258e-07 -▁Lufth 2.3488221357847258e-07 -laubens 2.3109379077881979e-07 -merksam 2.3109379077881979e-07 -oboda 2.3109379077881979e-07 -ranational 2.3109379077881979e-07 -täus 2.3109379077881979e-07 -▁Bruss 2.3109379077881979e-07 -▁Verschlechter 2.3109379077881979e-07 -▁gewünsch 2.3109379077881979e-07 -▁unterscheid 2.3109379077881979e-07 --DE 2.27305367979167e-07 -andin 2.27305367979167e-07 -icits 2.27305367979167e-07 -kapaz 2.27305367979167e-07 -laga 2.27305367979167e-07 -odurch 2.27305367979167e-07 -▁einzur 2.27305367979167e-07 -▁indisp 2.2351694517951422e-07 -Rooms 2.1594009958020865e-07 -obald 2.1594009958020865e-07 -▁Verschied 2.1594009958020865e-07 -▁natür 2.1594009958020865e-07 -▁beseit 2.1215167678055586e-07 -▁disprop 2.1215167678055586e-07 -▁incom 2.1215167678055586e-07 -▁erhö 2.0836325398090308e-07 -aceut 2.0457483118125032e-07 -aptop 2.0457483118125032e-07 -ocopying 2.0457483118125032e-07 -▁countr 2.0457483118125032e-07 -▁incred 2.0457483118125032e-07 -▁überrasch 2.0457483118125032e-07 -bergt 2.0078640838159753e-07 -ijten 2.0078640838159753e-07 -▁beein 2.0078640838159753e-07 -▁empfeh 2.0078640838159753e-07 -bezieh 1.9699798558194475e-07 -▁Starf 1.9699798558194475e-07 -usstsein 1.9320956278229196e-07 -▁vess 1.9320956278229196e-07 -▁disastr 1.8942113998263918e-07 -▁includ 1.8942113998263918e-07 -anspruchnahme 1.8563271718298639e-07 -athedral 1.8563271718298639e-07 -ealand 1.8563271718298639e-07 -ehin 1.8563271718298639e-07 -ältigen 1.8563271718298639e-07 -▁Jarz 1.8563271718298639e-07 -▁Oomen 1.8563271718298639e-07 -▁earl 1.8563271718298639e-07 -estock 1.8184429438333361e-07 -utreiben 1.8184429438333361e-07 -weitung 1.8184429438333361e-07 -▁Ferienwohn 1.8184429438333361e-07 --Herz 1.7805587158368082e-07 -Fast 1.7805587158368082e-07 -ützlich 1.7805587158368082e-07 -▁Millenn 1.7805587158368082e-07 -▁benach 1.7805587158368082e-07 --depth 1.7426744878402803e-07 -amew 1.7426744878402803e-07 -anmar 1.7426744878402803e-07 -oeuv 1.7426744878402803e-07 -schließend 1.7426744878402803e-07 -▁Aufmerksam 1.7426744878402803e-07 -▁Einwohn 1.7426744878402803e-07 -▁unterw 1.7426744878402803e-07 -cussions 1.7047902598437525e-07 -grunde 1.7047902598437525e-07 -▁Abgeordn 1.7047902598437525e-07 -▁Göte 1.7047902598437525e-07 -▁besonder 1.7047902598437525e-07 -▁Cohn 1.6669060318472246e-07 -▁indispens 1.6669060318472246e-07 -OFIN 1.6290218038506968e-07 -befinden 1.6290218038506968e-07 -cía 1.6290218038506968e-07 -fghan 1.6290218038506968e-07 -advant 1.5911375758541692e-07 -akings 1.5911375758541692e-07 -peratur 1.5911375758541692e-07 -streicht 1.5911375758541692e-07 -▁herbeiz 1.5911375758541692e-07 -▁secon 1.5911375758541692e-07 -tragt 1.5532533478576413e-07 -▁Urug 1.5532533478576413e-07 -endigung 1.5153691198611135e-07 -ergeleitet 1.5153691198611135e-07 -soever 1.5153691198611135e-07 -▁Mobilt 1.5153691198611135e-07 -ießlich 1.4774848918645856e-07 -▁Drit 1.4774848918645856e-07 -▁Eurocop 1.4774848918645856e-07 -▁Wats 1.4774848918645856e-07 -▁geä 1.4774848918645856e-07 -inland 1.4396006638680578e-07 -yssen 1.4396006638680578e-07 -▁asyl 1.4396006638680578e-07 -▁höch 1.4396006638680578e-07 -ünf 1.40171643587153e-07 -▁recog 1.40171643587153e-07 -▁thres 1.40171643587153e-07 -leiht 1.363832207875002e-07 -stattung 1.363832207875002e-07 -urther 1.363832207875002e-07 -wirklichen 1.363832207875002e-07 -▁Kommiss 1.363832207875002e-07 -▁Thess 1.363832207875002e-07 -▁enem 1.363832207875002e-07 -hme 1.3259479798784742e-07 -▁QuarkX 1.3259479798784742e-07 -▁ermö 1.3259479798784742e-07 -▁requir 1.3259479798784742e-07 -▁verbess 1.3259479798784742e-07 -atastr 1.2880637518819463e-07 -celand 1.2880637518819463e-07 -hältnism 1.2880637518819463e-07 -tenteils 1.2880637518819463e-07 -▁Dubrov 1.2880637518819463e-07 -▁accur 1.2880637518819463e-07 -▁grunds 1.2880637518819463e-07 -▁reas 1.2880637518819463e-07 -▁unfortun 1.2880637518819463e-07 -▁veranl 1.2880637518819463e-07 -chtenstein 1.2501795238854185e-07 -ibiot 1.2501795238854185e-07 -yright 1.2501795238854185e-07 -▁Beibeh 1.2501795238854185e-07 -▁Luxem 1.2501795238854185e-07 -▁persön 1.2501795238854185e-07 -fraid 1.2122952958888906e-07 -ikely 1.2122952958888906e-07 -yond 1.2122952958888906e-07 -▁Sämt 1.2122952958888906e-07 -▁agricult 1.2122952958888906e-07 -incere 1.1744110678923629e-07 -pite 1.1744110678923629e-07 -▁Aufrechter 1.1744110678923629e-07 -▁Febru 1.1744110678923629e-07 -▁Gonzá 1.1744110678923629e-07 -▁Isra 1.1744110678923629e-07 -▁wicht 1.1744110678923629e-07 -icter 1.136526839895835e-07 -itology 1.136526839895835e-07 -▁Maust 1.136526839895835e-07 --Rezeption 1.0986426118993072e-07 -ieweit 1.0986426118993072e-07 -umän 1.0986426118993072e-07 -▁Festpl 1.0986426118993072e-07 -▁Verwirr 1.0986426118993072e-07 -▁erläut 1.0986426118993072e-07 -▁jens 1.0607583839027793e-07 -▁newsp 1.0607583839027793e-07 -astricht 1.0228741559062516e-07 -enhagen 1.0228741559062516e-07 -ettering 1.0228741559062516e-07 -folgerungen 1.0228741559062516e-07 -iffern 1.0228741559062516e-07 -▁Vanc 1.0228741559062516e-07 -▁compon 1.0228741559062516e-07 -▁disag 1.0228741559062516e-07 -▁erwähn 1.0228741559062516e-07 -; 9.8498992790972374e-08 -qualities 9.8498992790972374e-08 -sbruck 9.8498992790972374e-08 -▁& 9.8498992790972374e-08 -▁Mandr 9.8498992790972374e-08 -▁Zuständ 9.8498992790972374e-08 -elservice 9.4710569991319589e-08 -obyl 9.4710569991319589e-08 -▁Fäh 9.4710569991319589e-08 --initiative 9.09221471916668e-08 -emeins 9.09221471916668e-08 -▁Wikitra 9.09221471916668e-08 -▁reper 9.09221471916668e-08 -▁Konsequ 8.7133724392014017e-08 -▁netw 8.7133724392014017e-08 -▁propert 8.7133724392014017e-08 -▁verstär 8.7133724392014017e-08 -▁Phän 8.3345301592361232e-08 -▁Telekommunik 8.3345301592361232e-08 -▁becom 8.3345301592361232e-08 -▁neigh 8.3345301592361232e-08 -wachst 7.9556878792708459e-08 -▁umfass 7.9556878792708459e-08 -▁establ 7.5768455993055674e-08 -▁etw 7.5768455993055674e-08 --Lös 7.1980033193402888e-08 -ispiell 6.81916103937501e-08 -ofern 6.81916103937501e-08 -▁Beantwort 6.81916103937501e-08 -▁Fronte 6.81916103937501e-08 -▁anbel 6.81916103937501e-08 -▁increas 6.81916103937501e-08 -eopard 6.4403187594097317e-08 -iefst 6.4403187594097317e-08 -regierungs 6.4403187594097317e-08 -selben 6.4403187594097317e-08 -▁Jarzemb 6.4403187594097317e-08 -▁Non-S 6.4403187594097317e-08 -▁thous 6.4403187594097317e-08 -benso 6.0614764794444531e-08 -either 6.0614764794444531e-08 -gemeine 6.0614764794444531e-08 -uggage 6.0614764794444531e-08 -▁Anson 6.0614764794444531e-08 -▁Erachten 6.0614764794444531e-08 -▁Erwachs 6.0614764794444531e-08 -▁encour 6.0614764794444531e-08 -▁incor 6.0614764794444531e-08 -▁manufactur 6.0614764794444531e-08 -▁subsidiar 6.0614764794444531e-08 -iereinrichtungen 5.6826341994791752e-08 -▁Coton 5.6826341994791752e-08 -▁Deutschl 5.6826341994791752e-08 -▁Verabschied 5.6826341994791752e-08 -▁somew 5.6826341994791752e-08 --Hour 5.3037919195138966e-08 --to-day 5.3037919195138966e-08 -chtungen 5.3037919195138966e-08 -kämpfung 5.3037919195138966e-08 -namo 5.3037919195138966e-08 -▁Chá 5.3037919195138966e-08 -▁Fratt 5.3037919195138966e-08 -▁beglück 5.3037919195138966e-08 -▁holid 5.3037919195138966e-08 -▁welcom 5.3037919195138966e-08 -comings 4.9249496395486187e-08 -folged 4.9249496395486187e-08 -gricult 4.9249496395486187e-08 -icars 4.9249496395486187e-08 -theless 4.9249496395486187e-08 -uential 4.9249496395486187e-08 -▁Bolkest 4.9249496395486187e-08 -▁Entschließ 4.9249496395486187e-08 -▁NCC-170 4.9249496395486187e-08 -▁Oomen-Ru 4.9249496395486187e-08 -▁behilf 4.9249496395486187e-08 -▁charact 4.9249496395486187e-08 -▁diesbe 4.9249496395486187e-08 -▁ursprün 4.9249496395486187e-08 -▁bereitz 4.54610735958334e-08 -▁negot 4.54610735958334e-08 -▁spezi 4.54610735958334e-08 -▁Änderungsan 4.54610735958334e-08 -enever 4.1672650796180616e-08 -zutage 4.1672650796180616e-08 -▁Amster 4.1672650796180616e-08 -▁Anmer 4.1672650796180616e-08 -▁Chechn 4.1672650796180616e-08 -▁Wachst 4.1672650796180616e-08 -▁beziehungs 4.1672650796180616e-08 -▁enthusias 4.1672650796180616e-08 -▁gemeins 4.1672650796180616e-08 -▁unanim 4.1672650796180616e-08 -▁unmittel 4.1672650796180616e-08 -grees 3.7884227996527837e-08 -korea 3.7884227996527837e-08 -▁curren 3.7884227996527837e-08 -▁katastrop 3.7884227996527837e-08 -Traq 3.4095805196875051e-08 -atever 3.4095805196875051e-08 -ehear 3.4095805196875051e-08 -fortunately 3.4095805196875051e-08 -zedenz 3.4095805196875051e-08 -▁Guantá 3.4095805196875051e-08 -▁provid 3.4095805196875051e-08 -▁spea 3.4095805196875051e-08 -adurch 3.0307382397222265e-08 -chmal 3.0307382397222265e-08 -riedenheit 3.0307382397222265e-08 -▁Katastrop 3.0307382397222265e-08 -▁Vorsitzen 3.0307382397222265e-08 -▁begeist 3.0307382397222265e-08 -▁enthus 3.0307382397222265e-08 -clud 2.6518959597569483e-08 -▁Schlussel 2.6518959597569483e-08 -▁ceil 2.6518959597569483e-08 -▁entreprene 2.6518959597569483e-08 -▁exac 2.6518959597569483e-08 -▁gewün 2.6518959597569483e-08 -ifilm 2.27305367979167e-08 -orgnis 2.27305367979167e-08 -▁Cohn-B 2.27305367979167e-08 -▁Frankf 2.27305367979167e-08 -▁Meinungsver 2.27305367979167e-08 -▁veröffentl 2.27305367979167e-08 -▁wesent 2.27305367979167e-08 -adays 1.8942113998263918e-08 -esterday 1.8942113998263918e-08 -vereign 1.8942113998263918e-08 -▁Ansprü 1.8942113998263918e-08 -▁acknow 1.8942113998263918e-08 -▁guarante 1.8942113998263918e-08 -▁verläng 1.8942113998263918e-08 -inahe 1.5153691198611133e-08 -zuerhalten 1.5153691198611133e-08 -▁Klimawand 1.5153691198611133e-08 -▁besorgnis 1.5153691198611133e-08 -▁obstac 1.5153691198611133e-08 -esondere 1.136526839895835e-08 -etheless 1.136526839895835e-08 -onsten 1.136526839895835e-08 -▁Bevöl 1.136526839895835e-08 -▁Diskuss 1.136526839895835e-08 -▁Ferrero-Wald 1.136526839895835e-08 -▁erinn 1.136526839895835e-08 -▁konkr 1.136526839895835e-08 -▁rapporte 1.136526839895835e-08 -cedented 7.5768455993055664e-09 -ffice 7.5768455993055664e-09 -▁Kohäs 7.5768455993055664e-09 -▁Konfl 7.5768455993055664e-09 -▁Milliar 7.5768455993055664e-09 -▁ThyssenKr 7.5768455993055664e-09 -▁citiz 7.5768455993055664e-09 -▁increa 7.5768455993055664e-09 -▁unterstüt 7.5768455993055664e-09 -▁Etw 3.7884227996527832e-09 -▁colleag 3.7884227996527832e-09 -▁einzube 3.7884227996527832e-09 -▁humanitä 3.7884227996527832e-09 -! 0 -# 0 -$ 0 -% 0 -( 0 -) 0 -* 0 -+ 0 -/ 0 -1; 0 -3; 0 -: 0 -= 0 -? 0 -@ 0 -\ 0 -_ 0 -dernfalls 0 -{ 0 -} 0 -~ 0 -£ 0 -¤ 0 -§ 0 -© 0 -« 0 -­ 0 -® 0 -° 0 -² 0 -³ 0 -´ 0 -¶ 0 -· 0 -» 0 -¼ 0 -½ 0 -¿ 0 -̈ 0 -– 0 -— 0 -‘ 0 -’ 0 -‚ 0 -“ 0 -” 0 -„ 0 -† 0 -• 0 -… 0 -€ 0 -™ 0 -▁&# 0 -▁| 0 -▁ 0 -▁& 0 -▁> 0 -▁" 0 -▁Konfli 0 -▁safegu 0 -▁withdra 0 -� 0 diff --git a/lingvo/tasks/mt/text_input.proto b/lingvo/tasks/mt/text_input.proto deleted file mode 100644 index 14cd5059..00000000 --- a/lingvo/tasks/mt/text_input.proto +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -// Text input data formats for MT tasks. - -syntax = "proto2"; - -package tensorflow.lingvo; - -message Sentence { - optional string sentence = 1; - - // Two-letter ISO 639-1 codes for the language of the sentence: - // https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes - optional string lang = 2; -} - -message SentencePair { - optional Sentence src_sentence = 1; - optional Sentence tgt_sentence = 2; -} diff --git a/lingvo/tasks/mt/tools/__init__.py b/lingvo/tasks/mt/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/mt/tools/wmt14.01.download_moses_scripts.sh b/lingvo/tasks/mt/tools/wmt14.01.download_moses_scripts.sh deleted file mode 100755 index c4bd2868..00000000 --- a/lingvo/tasks/mt/tools/wmt14.01.download_moses_scripts.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -mkdir -p "${ROOT}" -git clone https://github.com/moses-smt/mosesdecoder.git ${ROOT}/mosesdecoder diff --git a/lingvo/tasks/mt/tools/wmt14.02.download_train.sh b/lingvo/tasks/mt/tools/wmt14.02.download_train.sh deleted file mode 100755 index a8e81fe9..00000000 --- a/lingvo/tasks/mt/tools/wmt14.02.download_train.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -mkdir -p "${ROOT}/raw" -# ============================================================================ -# Download WMT data. -# From the WMT14 website (En-De): -echo " -http://www.statmt.org/wmt13/training-parallel-europarl-v7.tgz -http://www.statmt.org/wmt13/training-parallel-commoncrawl.tgz -http://www.statmt.org/wmt14/training-parallel-nc-v9.tgz" \ - | aria2c -x16 -s1 -j1 --dir="${ROOT}/raw" -i - diff --git a/lingvo/tasks/mt/tools/wmt14.03.download_devtest.sh b/lingvo/tasks/mt/tools/wmt14.03.download_devtest.sh deleted file mode 100755 index 0153c428..00000000 --- a/lingvo/tasks/mt/tools/wmt14.03.download_devtest.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -mkdir -p "${ROOT}/raw" -echo " -http://www.statmt.org/wmt14/test-filtered.tgz -http://www.statmt.org/wmt14/dev.tgz" \ - | aria2c -x16 -s1 -j1 --dir="${ROOT}/raw" -i - diff --git a/lingvo/tasks/mt/tools/wmt14.04.unpack_train.sh b/lingvo/tasks/mt/tools/wmt14.04.unpack_train.sh deleted file mode 100755 index 16953e2d..00000000 --- a/lingvo/tasks/mt/tools/wmt14.04.unpack_train.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -mkdir -p "${ROOT}/unpacked" -for file in \ - training-parallel-commoncrawl.tgz \ - training-parallel-europarl-v7.tgz \ - training-parallel-nc-v9.tgz -do - tar xzf "${ROOT}/raw/${file}" -C "${ROOT}/unpacked" -done diff --git a/lingvo/tasks/mt/tools/wmt14.05.unpack_devtest.sh b/lingvo/tasks/mt/tools/wmt14.05.unpack_devtest.sh deleted file mode 100755 index 4bf80c25..00000000 --- a/lingvo/tasks/mt/tools/wmt14.05.unpack_devtest.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -mkdir -p "${ROOT}/unpacked" -for file in dev.tgz test-filtered.tgz; do - tar xzf "${ROOT}/raw/${file}" -C "${ROOT}/unpacked" -done diff --git a/lingvo/tasks/mt/tools/wmt14.06.tokenize_train.sh b/lingvo/tasks/mt/tools/wmt14.06.tokenize_train.sh deleted file mode 100755 index 89a4ebbd..00000000 --- a/lingvo/tasks/mt/tools/wmt14.06.tokenize_train.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -mkdir -p "${ROOT}/tokenized/train" - -function clean_train { - local base=$1 - local output_base="${ROOT}/tokenized/train/${base}" - - # Requires both directions to run and will output both. - "${TRAIN_CLEANER}" "${output_base}" ${SRC} ${TGT} \ - "${output_base}.clean" 1 100 - "${TRAIN_CLEANER}" "${output_base}" ${TGT} ${SRC} \ - "${output_base}.clean" 1 100 -} - -# TODO(drpng): eliminate en/de patterns. -tokenize "commoncrawl.de-en.de" de train commoncrawl -tokenize "commoncrawl.de-en.en" en train commoncrawl -tokenize "training/europarl-v7.de-en.de" de train europarl-v7 -tokenize "training/europarl-v7.de-en.en" en train europarl-v7 -tokenize "training/news-commentary-v9.de-en.de" de train news-commentary-v9 -tokenize "training/news-commentary-v9.de-en.en" en train news-commentary-v9 - -clean_train commoncrawl -clean_train europarl-v7 -clean_train news-commentary-v9 diff --git a/lingvo/tasks/mt/tools/wmt14.07.tokenize_devtest.sh b/lingvo/tasks/mt/tools/wmt14.07.tokenize_devtest.sh deleted file mode 100755 index 09e8d5a0..00000000 --- a/lingvo/tasks/mt/tools/wmt14.07.tokenize_devtest.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -# --- TEST SET. -mkdir -p "${ROOT}/tokenized/test" -function sgm_to_input { - local file=$1 - local output="$2" - - cat ${ROOT}/unpacked/$file | ${INPUT_FROM_SGM} > "${ROOT}/unpacked/test/$2" -} - -# TODO(drpng): eliminate en/de patterns. -sgm_to_input test/newstest2014-deen-src.${SRC}.sgm newstest2014.${SRC} -sgm_to_input test/newstest2014-deen-ref.${TGT}.sgm newstest2014.${TGT} - -tokenize test/newstest2014.${SRC} ${SRC} test newstest2014 -tokenize test/newstest2014.${TGT} ${TGT} test newstest2014 - -# --- DEV SET. -mkdir -p "${ROOT}/tokenized/dev" -# While 2008 onwards are available, we only use 2013 as the dev set. The "input" -# file is already in the tar file, so no need to pull from the SGM. -tokenize dev/newstest2013.${SRC} ${SRC} dev newstest2013 -tokenize dev/newstest2013.${TGT} ${TGT} dev newstest2013 diff --git a/lingvo/tasks/mt/tools/wmt14.08.wpm_encode_train.sh b/lingvo/tasks/mt/tools/wmt14.08.wpm_encode_train.sh deleted file mode 100755 index de5f0554..00000000 --- a/lingvo/tasks/mt/tools/wmt14.08.wpm_encode_train.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -mkdir -p ${ROOT}/wpm - -function make_input_files_list { - local lang=$1 - local indir="${ROOT}/tokenized/train" - echo "${indir}"/{news-commentary-v9,commoncrawl,europarl-v7}.clean.${lang} \ - | tr ' ' ',' -} - - -SRC_FILES=$(make_input_files_list ${SRC}) -TGT_FILES=$(make_input_files_list ${TGT}) - -TRAIN_TFRECORDS="${ROOT}/wpm/train.tfrecords-%5.5d-of-%5.5d" - -# The encoding goes at about 200 sentences per second. -# So, 4.4M sentences will take about 6 hours to run on a single core. -wpm_encode "${SRC_FILES}" "${TGT_FILES}" 200 16 ${TRAIN_TFRECORDS} - -cp -f "${WPM_VOC}" "${ROOT}/wpm" diff --git a/lingvo/tasks/mt/tools/wmt14.09.wpm_encode_devtest.sh b/lingvo/tasks/mt/tools/wmt14.09.wpm_encode_devtest.sh deleted file mode 100755 index 9675904f..00000000 --- a/lingvo/tasks/mt/tools/wmt14.09.wpm_encode_devtest.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmt14_lib.sh - -mkdir -p ${ROOT}/wpm - -DEV_SRC_FILES="${ROOT}"/tokenized/dev/newstest2013.${SRC} -TEST_SRC_FILES="${ROOT}"/tokenized/test/newstest2014.${SRC} -DEV_TGT_FILES="${ROOT}"/tokenized/dev/newstest2013.${TGT} -TEST_TGT_FILES="${ROOT}"/tokenized/test/newstest2014.${TGT} -DEV_TFRECORDS="${ROOT}/wpm/dev.tfrecords" -TEST_TFRECORDS="${ROOT}/wpm/test.tfrecords" - -wpm_encode "${DEV_SRC_FILES}" "${DEV_TGT_FILES}" 0 1 ${DEV_TFRECORDS} -wpm_encode "${TEST_SRC_FILES}" "${TEST_TGT_FILES}" 0 1 ${TEST_TFRECORDS} - -cp -f "${WPM_VOC}" "${ROOT}/wpm" diff --git a/lingvo/tasks/mt/tools/wmt14_get_data.sh b/lingvo/tasks/mt/tools/wmt14_get_data.sh deleted file mode 100755 index a27c968a..00000000 --- a/lingvo/tasks/mt/tools/wmt14_get_data.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -./wmt14.01.download_moses_scripts.sh -./wmt14.02.download_train.sh -./wmt14.03.download_devtest.sh -./wmt14.04.unpack_train.sh -./wmt14.05.unpack_devtest.sh -./wmt14.06.tokenize_train.sh -./wmt14.07.tokenize_devtest.sh -./wmt14.08.wpm_encode_train.sh -./wmt14.09.wpm_encode_devtest.sh diff --git a/lingvo/tasks/mt/tools/wmt14_lib.sh b/lingvo/tasks/mt/tools/wmt14_lib.sh deleted file mode 100755 index f850da6c..00000000 --- a/lingvo/tasks/mt/tools/wmt14_lib.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -ROOT=/tmp/wmt14 - -SRC=en -TGT=de - -TOKENIZER="${ROOT}/mosesdecoder/scripts/tokenizer" -TRAIN_CLEANER="${ROOT}/mosesdecoder/scripts/training/clean-corpus-n.perl" -INPUT_FROM_SGM="${ROOT}/mosesdecoder/scripts/ems/support/input-from-sgm.perl" - -function tokenize { - local input_filepath="$1" - local lang="$2" - local set="$3" - local out="$4" - local output_base="${ROOT}/tokenized/${set}/${out}" - - echo "$input_filepath -> ${out}.${lang}" - zcat -f "${ROOT}/unpacked/${input_filepath}" | \ - "${TOKENIZER}"/tokenizer.perl \ - -threads 20 \ - -l ${lang} > \ - "${output_base}.${lang}" -} - -WPM_VOC=../wpm-${SRC}${TGT}.voc - -function wpm_encode { - local source_files="$1" - local target_files="$2" - local max_len="$3" - local num_shards="$4" - local output_template="$5" - - rm -f convert.FAILED - for n in $(seq "${num_shards}"); do - local shard_id=$((n - 1)) - local output_filepath=$(printf ${output_template} ${shard_id} ${num_shards}) - set -x - nice -n 20 python3 -m lingvo.tools.wpm_encode_file --wpm_filepath=${WPM_VOC} --source_filepaths="${source_files}" \ - --target_filepaths="${target_files}" --num_shards="${num_shards}" --shard_id="${shard_id}" \ - --max_len="$max_len" \ - --output_filepath="${output_filepath}" --logtostderr || touch convert.FAILED & - set +x - done - wait - ! [ -f convert.FAILED ] -} - -test -f ${WPM_VOC} || (echo 1>&2 "Could not locate wpm file: ${WPM_VOC}"; exit 2) diff --git a/lingvo/tasks/mt/tools/wmtm16.01.download_moses_scripts.sh b/lingvo/tasks/mt/tools/wmtm16.01.download_moses_scripts.sh deleted file mode 100755 index bd950f50..00000000 --- a/lingvo/tasks/mt/tools/wmtm16.01.download_moses_scripts.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmtm16_lib.sh - -mkdir -p "${ROOT}" -git clone https://github.com/moses-smt/mosesdecoder.git ${ROOT}/mosesdecoder diff --git a/lingvo/tasks/mt/tools/wmtm16.02.download_data.sh b/lingvo/tasks/mt/tools/wmtm16.02.download_data.sh deleted file mode 100755 index a50ab80b..00000000 --- a/lingvo/tasks/mt/tools/wmtm16.02.download_data.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmtm16_lib.sh - -mkdir -p "${ROOT}/raw" -# ============================================================================ -# Download WMT data. -# From the WMT16 Multimodal website (En-De): -echo " -http://www.quest.dcs.shef.ac.uk/wmt16_files_mmt/training.tar.gz -http://www.quest.dcs.shef.ac.uk/wmt16_files_mmt/validation.tar.gz -http://www.quest.dcs.shef.ac.uk/wmt16_files_mmt/mmt16_task1_test.tar.gz" \ - | aria2c -x16 -s16 --dir="${ROOT}/raw" -i - diff --git a/lingvo/tasks/mt/tools/wmtm16.03.unpack_data.sh b/lingvo/tasks/mt/tools/wmtm16.03.unpack_data.sh deleted file mode 100755 index 286e0c3a..00000000 --- a/lingvo/tasks/mt/tools/wmtm16.03.unpack_data.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmtm16_lib.sh - -mkdir -p "${ROOT}/unpacked" -for file in \ - training.tar.gz \ - validation.tar.gz \ - mmt16_task1_test.tar.gz -do - tar xzf "${ROOT}/raw/${file}" -C "${ROOT}/unpacked" -done diff --git a/lingvo/tasks/mt/tools/wmtm16.04.tokenize_data.sh b/lingvo/tasks/mt/tools/wmtm16.04.tokenize_data.sh deleted file mode 100755 index 4591edd1..00000000 --- a/lingvo/tasks/mt/tools/wmtm16.04.tokenize_data.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmtm16_lib.sh - -mkdir -p "${ROOT}/tokenized/" - -function clean_train { - local base=$1 - local output_base="${ROOT}/tokenized/${base}" - - # Requires both directions to run and will output both. - "${TRAIN_CLEANER}" "${output_base}" ${SRC} ${TGT} \ - "${output_base}.clean" 1 100 - "${TRAIN_CLEANER}" "${output_base}" ${TGT} ${SRC} \ - "${output_base}.clean" 1 100 -} - -tokenize train ${SRC} -tokenize train ${TGT} -tokenize val ${SRC} -tokenize val ${TGT} -tokenize test ${SRC} -tokenize test ${TGT} - -clean_train train -clean_train val -clean_train test diff --git a/lingvo/tasks/mt/tools/wmtm16.05.wpm_encode_data.sh b/lingvo/tasks/mt/tools/wmtm16.05.wpm_encode_data.sh deleted file mode 100755 index 2a15d01d..00000000 --- a/lingvo/tasks/mt/tools/wmtm16.05.wpm_encode_data.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -. wmtm16_lib.sh - -mkdir -p ${ROOT}/wpm - -# The encoding goes at about 200 sentences per second. -for BASE in "train" "val" "test" -do - echo "Encoding ${BASE}" - TFRECORDS="${ROOT}/wpm/${BASE}.tfrecords" - SRC_FILE="${ROOT}/tokenized/${BASE}.clean.${SRC}" - TGT_FILE="${ROOT}/tokenized/${BASE}.clean.${TGT}" - wpm_encode "${SRC_FILE}" "${TGT_FILE}" 200 1 ${TFRECORDS} -done - -cp -f "${WPM_VOC}" "${ROOT}/wpm" diff --git a/lingvo/tasks/mt/tools/wmtm16_get_data.sh b/lingvo/tasks/mt/tools/wmtm16_get_data.sh deleted file mode 100755 index eb4769b3..00000000 --- a/lingvo/tasks/mt/tools/wmtm16_get_data.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -set -eu - -./wmtm16.01.download_moses_scripts.sh -./wmtm16.02.download_data.sh -./wmtm16.03.unpack_data.sh -./wmtm16.04.tokenize_data.sh -./wmtm16.05.wpm_encode_data.sh diff --git a/lingvo/tasks/mt/tools/wmtm16_lib.sh b/lingvo/tasks/mt/tools/wmtm16_lib.sh deleted file mode 100755 index 73702f84..00000000 --- a/lingvo/tasks/mt/tools/wmtm16_lib.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash -# Copyright 2018 The TensorFlow 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. -# ============================================================================== - -ROOT=/tmp/wmtm16 - -SRC=en -TGT=de - -TOKENIZER="${ROOT}/mosesdecoder/scripts/tokenizer" -TRAIN_CLEANER="${ROOT}/mosesdecoder/scripts/training/clean-corpus-n.perl" -INPUT_FROM_SGM="${ROOT}/mosesdecoder/scripts/ems/support/input-from-sgm.perl" - -function tokenize { - local input_prefix="$1" - local lang="$2" - local out=$input_prefix - local output_base="${ROOT}/tokenized/${out}" - - echo "$input_prefix.$lang -> ${out}.${lang}" - zcat -f "${ROOT}/unpacked/${input_prefix}.$lang" | \ - "${TOKENIZER}"/tokenizer.perl \ - -threads 20 \ - -l ${lang} > \ - "${output_base}.${lang}" -} - -WPM_VOC=../wpm-${SRC}${TGT}-2k.voc - -function wpm_encode { - local source_files="$1" - local target_files="$2" - local max_len="$3" - local num_shards="$4" - local output_template="$5" - - rm -f convert.FAILED - for n in $(seq "${num_shards}"); do - local shard_id=$((n - 1)) - local output_filepath=$(printf ${output_template} ${shard_id} ${num_shards}) - set -x - nice -n 20 python3 -m lingvo.tools.wpm_encode_file --wpm_filepath=${WPM_VOC} --source_filepaths="${source_files}" \ - --target_filepaths="${target_files}" --num_shards="${num_shards}" --shard_id="${shard_id}" \ - --max_len="$max_len" \ - --output_filepath="${output_filepath}" --logtostderr || touch convert.FAILED & - set +x - done - wait - ! [ -f convert.FAILED ] -} - -test -f ${WPM_VOC} || (echo 1>&2 "Could not locate wpm file: ${WPM_VOC}"; exit 2) diff --git a/lingvo/tasks/mt/wpm-ende-2k.voc b/lingvo/tasks/mt/wpm-ende-2k.voc deleted file mode 100644 index 80a37e61..00000000 --- a/lingvo/tasks/mt/wpm-ende-2k.voc +++ /dev/null @@ -1,2000 +0,0 @@ - - - -▁ -. -a -i -n -A -E -e -m -, -t -h -u -d -f -o -M -s -r -w -F -Z -T -g -p -l -v -c -ä -y -b -k -z -H -J -S -ß -G -P -ü -K -D -W -& -q -; -x -L -j -O -B -N -- -V -Y -R -ö -2 -I -C -U -3 -( -! -) -é -4 -5 -: -0 -1 -X -# -? -6 -Ö -8 -9 -Q -Ä -7 -/ -Ü -% -@ -ñ -$ -= -è -_ -à -▁. -▁a -▁A -▁E -▁m -▁, -▁t -▁h -▁d -▁f -▁M -▁s -▁r -▁w -▁F -▁T -▁g -▁p -▁l -▁v -▁b -▁H -▁S -▁K -▁B -in -it -is -ir -il -ea -en -em -et -eh -ed -es -er -el -un -ut -uf -on -om -ot -of -or -ow -ch -ck -▁i -▁n -▁Z -▁c -▁k -▁z -▁G -▁P -▁in -▁is -▁on -▁of -an -at -au -ad -as -ar -al -ay -ack -ei -eb -ein -ou -os -op -ol -▁an -▁ar -▁auf -▁Ein -▁mit -▁th -▁si -▁st -▁sh -▁sp -▁sit -▁wh -▁wit -▁wom -▁Fr -▁gr -▁bl -▁St -ers -und -ung -chen -▁o -▁y -▁J -▁D -▁W -▁L -▁j -▁ein -▁und -am -ag -ach -ie -id -ig -ic -iel -ts -ten -ter -hr -um -us -ur -wo -wei -le -lay -▁man -▁to -▁tr -▁der -▁Mä -▁Man -▁sch -▁bo -▁Sch -ind -ing -ild -ear -▁im -ass -alk -▁and -▁are -▁Eine -▁the -▁woman -▁Frau -▁R -▁ch -im -ik -ier -ue -up -oo -st -ra -re -ge -gt -äl -ähr -ber -ßen -▁at -▁dem -▁die -▁wear -▁Two -▁pe -▁play -▁von -▁vor -ite -irt -irl -eht -ehen -est -ell -own -▁Zwei -▁kl -and -all -ople -old -▁sich -▁spiel -▁sitz -▁with -erson -▁eine -▁einen -▁einem -▁einer -▁Mann -▁O -ap -ab -ahr -ich -ner -eg -eit -ting -her -og -oung -war -ver -king -▁his -▁her -▁dr -▁fr -▁red -▁wat -▁walk -▁girl -▁br -▁Hund -▁Kind -end -ens -ess -▁zu -▁Gr -▁Person -ang -eid -oup -▁steht -▁stand -▁shirt -▁whil -▁white -▁black -▁blue -▁Stra -▁out -▁Jung -▁Mäd -▁boy -▁wearing -▁people -▁klein -▁un -ain -air -ick -nner -ee -ug -ul -ft -ock -ound -sch -rei -ball -über -▁al -▁hold -▁down -▁dog -▁Mens -▁sein -▁rot -▁wei -▁loo -▁bei -▁Sp -itt -isch -ent -emd -ont -▁cl -ard -ark -▁stre -▁sitting -▁group -▁blau -▁young -▁child -upp -ält -ährend -▁playing -▁sitzt -warz -▁walking -▁standing -▁Straß -▁Mädchen -▁& -▁V -▁C -▁or -▁um -▁über -auf -ion -ne -nen -men -te -he -de -den -der -for -gr -leid -ce -ben -qu -▁as -▁aus -▁men -▁hat -▁hält -▁den -▁for -▁während -▁be -▁Hemd -ink -other -▁ihr -▁cr -eben -▁spr -▁jung -ide -▁Männer -▁schwarz -▁front -▁while -▁Junge -▁holding -▁Menschen -▁street -uppe -▁Straße -▁N -▁up -af -ak -aar -iv -ex -een -sp -side -gh -ct -än -bo -ür -▁am -▁An -▁das -▁dur -▁fl -▁sm -▁som -▁run -▁The -▁by -inter -ean -ead -ehr -uft -▁neben -▁car -ange -▁thr -▁gro -▁Wass -▁eines -acht -igh -ump -ingt -▁ander -▁Frauen -▁spielt -▁dress -▁water -▁Kinder -▁Gruppe -▁Personen -form -av -ip -ies -hing -uit -oot -sen -ren -rad -beit -kat -kleid -ze -▁sw -▁wor -▁ge -▁lä -▁back -▁ball -▁Hand -each -ern -ere -one -ong -▁ist -▁one -arben -ough -oll -▁stehen -▁women -▁green -▁Drei -▁jump -imm -iert -raun -ährt -▁spielen -▁sitzen -icht -▁roten -▁weißen -▁looking -ittle -entl -▁blauen -▁orange -▁durch -▁entl -ian -ew -eut -eck -to -hn -hirt -fer -ägt -yc -ying -bl -ks -▁mo -▁macht -▁two -▁fährt -▁sn -▁ro -▁rid -▁Fahr -▁Thr -▁gel -▁geht -▁pos -▁little -▁ver -▁vers -▁bu -▁braun -▁Kleid -ill -enn -▁co -arge -our -▁side -▁grü -▁over -amer -ood -ellow -▁Ober -▁brown -▁outside -▁kleines -▁weiß -▁through -▁Wasser -uß -uch -fen -och -oard -se -lass -br -zen -ück -▁hand -▁ra -▁person -▁large -▁Br -ine -etw -eln -elt -ot; -▁zwei -▁into -ank -ast -art -▁blon -▁yellow -▁Leut -ame -age -agen -igen -▁trägt -▁their -str -▁from -▁watch -▁kleiner -▁unter -▁&qu -teil -▁springt -▁schwarzen -ext -ight -▁entlang -▁snow -▁Three -▁build -amera -▁I -▁U -▁old -▁other -aut -if -ning -ef -ep -ey -hl -uck -ro -gen --S -▁wir -▁Fel -▁gehen -▁pl -▁bik -▁Bl -ischen -esch -unt -ort -owd -▁near -▁next -▁con -▁zus -ant -auen -ask -acket -ebä -eboard -▁grass -amp -älter -ull -▁Spiel -▁children -spiel -▁small -eans -▁dressed -kleidet -▁Fahrrad -▁" -▁building -▁it -▁etw -▁älter -tur -oc -oor -walk -ger -gel -ly -ler -▁Ar -▁hin -▁sc -▁Fuß -▁pink -▁Se -▁Ball -ille -eam -unter -otor -che -▁nach -▁Pe -▁off -ale -acke -oss -▁another -▁around -▁str -▁Str -▁jacket -ield -aby -able -▁seinem -grund -ground -▁crowd -▁some -▁running -ehrere -▁großen -kateboard -▁riding -▁Oberteil -▁Leute -ebäu -▁er -hone -fahr -farben -ool -pp -cer -zt -▁air -▁As -▁talk -▁field -▁Mehrere -▁Fl -▁Tr -▁per -▁lad -▁beh -▁beach -▁Hinter -▁Bau -ity -ist -ett -elm -orts -▁Gebäu -alten -ose -os; -▁sits -▁Frei -▁Der -▁Luft -ige -▁Schn -ieren -ander -▁stands -▁&ap -▁junge -▁smil -zeug -▁gekleidet -▁läuft -▁sidewalk -▁grünen -lasses -▁etwas -▁People -aking -iat -ect -mer -ud -way -weg -les -lat -lich -bei -ün -ör -▁table -▁hinter -▁für -▁sk -▁sur -▁sind -▁pic -▁park -▁ben -▁Bo -itar -unk -omp -oth -eball -ost -opf -▁arbeit -▁Jacke -▁schaut -▁Jungen -board -▁anderen -▁work -▁läch -▁background -▁jumping -ycle -▁Kleidung -▁blond -asket -▁Strand -▁perform -▁behind -▁Hintergrund -▁Freien -▁' -ace -iform -ub -fl -fit -cr -cing -üt -▁top -▁head -▁hoch -▁do -▁des -▁dan -▁rock -▁read -▁wall -▁Tisch -▁pr -▁bic -▁Haar -▁Kopf -eat -etr -▁city -ation -aus -aur -ade -aseball -olor -umen -▁Schl -▁player -▁vorbei -egen -▁girls -▁looks -aufen -▁junger -ake -▁carr -ennis -ucht -▁In -▁bike -untain -spieler -▁pictur -▁'s -▁en -achen -ied -uh -wimm -get -be -zug -üh -ühr -▁mid -▁tan -▁hair -▁T-S -▁bus -▁Hut -▁Son -▁Kl -oto -▁ih -▁camera -▁that -▁sie -▁Stad -amm -iger -▁schl -estaur -veral -▁Gras -angen -▁kleine -▁kleinen -▁dogs -▁beim -menge -▁Nä -ive -▁somet -▁groß -▁andere -uitar -ootball -▁gelben -▁wird -occer -▁Fußball -▁talking -▁Gebäude -asketball -ahn -iz -ping -ling -äft -kt --h -▁ad -▁Es -▁hor -▁food -▁soccer -▁ros -▁gu -▁guitar -▁bar -▁Kamera -etter -ert -▁color -▁Geh -▁Gitar -▁Park -ants -alt -eig -▁gray -▁blick -erst -ammen -iegt -ust -▁toget -▁walks -▁Hunde -▁boys -▁along -ische -▁bef -▁Menschenmenge -▁working -ennt -▁lady -▁Schnee -▁smiling -▁Boden -▁T-Shirt -estaurant -▁Nähe -▁something -▁el -▁op -▁us -ait -ev -eer -uct -dle -door -ob -ok -gn -är --s -▁ab -▁has -▁him -▁sl -▁sing -▁sand -▁rennt -▁get -▁game -▁pol -▁ber -▁Skateboard -▁Ber -▁comp -ath -atch -eich -▁them -▁Die -▁plays -▁Straßen -▁uniform -▁holds -▁Vier -▁ihren -▁schwarzer -beiter -oller -▁road -▁older -farbenen -▁Asian -latz -▁bench -▁guy -▁together -▁Y -▁ea -rop -ve --wei -▁abo -▁Aut -▁dirt -▁dunk -▁Motor -▁suit -▁skateboard -▁Four -▁go -▁gest -▁glasses -▁pur -▁pain -▁pants -▁li -▁Sand -▁Büh -ish -ette -edeck -ore -▁coup -▁kid -▁Pfer -ann -ate -ald -eing -einander -▁stage -▁shorts -uss -▁herum -▁clim -▁asiat -▁ihre -▁Feld -▁zusammen -fahrer -▁carrying -▁picture -▁Sonnen -▁Stadt --hair -▁Gitarre -az -eu -ec -eil -fr -▁Er -▁mus -▁taking -▁hell -▁fem -▁fahr -▁face -▁set -▁sehen -▁wait -▁Foto -▁gl -▁pool -▁lea -▁long -▁baby -▁Bür -▁Baby -ennen -elle -▁Ges -ather -auer -außen -eigt -olle -▁sign -▁Jeans -▁tree -eitet -ogra -▁outdoor -kleidung -▁jumps -▁mountain -▁Brille -▁unterh -▁älterer -▁Several -▁bicycle -▁Gehweg -▁befind -▁Bühne -▁couple -▁climb -▁each -ail -ien -ieht -wagen -bt -üs -▁hav -▁dab -▁dark -▁Mik -▁sun -▁ph -▁bel -▁bag -ire -irts -erk -uth -otogra -owing -▁Gel -aum -osen -olz -▁area -▁shop -achsen -ice -ican -▁boat -▁chair -app -eiten -▁draußen -enden -ockey -▁als -▁seine -▁seinen -▁Ch -achtet -▁coat -▁race -▁watching -▁hinunter -cross -▁doing -▁blickt -▁fahren -▁ex -ac -ichen -tel -haar -dy -pr -per -ves -jo -▁across -▁mic -▁motor -▁male -▁tow -▁team -▁he -▁helm -▁day -▁Mus -▁wind -▁liegt -▁band -▁Hän -▁Bild -▁Bank -▁Baseball -ome -▁Paar -arten -▁ground -▁Wand -▁jeans -▁under -tern -▁train -▁Schwimm -ischer -▁versucht -▁braunen -▁weißer -brille -▁hands -▁Un -▁strip -▁Baum -▁arbeitet -▁lächeln -▁middle -▁dabei -nt -uhl -ff -par -ple -lt -ät -▁machen -▁dar -▁wie -▁wav -▁par -▁pass -▁laufen -▁baseball -▁being -▁Holz -▁Sh -▁Boot -isten -irm -utz -ons -cht -▁kur -asche -als -ebt -▁who -▁ob -▁Dame -amil -▁toy -▁tragen -▁schauen -ingen -ree -▁att -essen -▁mouth -▁room -▁brauner -eschäft -▁lächelt -ührt -▁große -▁Essen -▁eating -▁female -▁helmet -ah -wachsen -geb -gerst -äs -kauf -▁Auf -▁football -▁Men -▁Th -▁gather -▁pre -▁le -▁betr -▁Hü -ition -eten -▁kn -ance -ase -ouch -ope -▁arm -▁sho -▁Wom -ike -ikot -ult -▁all -arkt -▁ihrem -akes -▁swea -▁swing -▁orangefarbenen -▁versch -struct -▁performing -ütze -▁Klein -▁ihm -▁horse -▁using --weiß -▁gestrei -▁purple -▁Pferd -▁music -▁microp -▁striped -aft -od -over -ßt -ül -üst -▁ag -▁halten -▁wood -▁Tennis -▁past -▁Hose -▁Sk -emp -ows -▁zeigt -att -augh -▁sieht -▁store -▁oc -tst -ument -usch -▁Rad -ikan -▁kletter -aper -here -▁look -▁cross -▁spricht -▁junges -▁runs -iese -▁cover -▁grün -▁ältere -▁arbeiten -▁enjo -▁Young -▁Motorrad -▁liest --haired -▁Bürgerst -▁unterhalten -▁Mikr -▁toward -▁prepar -struction -aw -iet -iver -ees -piel -platz -▁app -▁mar -▁tennis -▁fen -▁Meer -▁wer -▁Tag -▁There -▁gegen -▁put -▁lay -▁viel -▁basketball -▁Band -ines -ott -othing -▁catch -▁Gew -ous -▁three -▁star -unglasses -▁Wald -▁schn -▁players -▁drei -ensch -enster -aff -afarben -iving -boy -▁swimm -efon -▁its -▁Trikot -▁reading -▁Haaren --shirt -▁Berg -▁Musik -▁microphone -▁Bürgersteig -mp -round -gest -by -▁mark -▁se -▁sunglasses -▁restaurant -▁part -▁bes -▁bun -▁bear -▁Helm -▁Hosen -▁Kun -ement -ete -eren -eld -els -▁can -▁cap -▁zum -asch -▁Fris -▁Stein -ure -leep -▁schw -stand -repp -▁bright -▁cloth -▁clothing -derly -▁beob -▁during -▁flow -ecken -▁watches -▁gekleidete -ostü -▁rosa -▁kids -▁Sonnenbrille -olley -▁climbing -▁motorc -▁again -▁ocean -ax -aun -ix -ial -eet -ubl -ften -ful -oh -ry -ph -las --B -▁t-shirt -▁hill -▁singt -▁Football -▁Basketball -omm -ofon -ord -owboy -cher -▁Zusch -▁Pol -anz -arm -▁stick -▁grauen -▁outfit -▁seiner -▁blauem -▁umbr -dern -ängt -▁rides -▁gelb -▁weißem -strument -ucks -▁Felsen -▁Arm -▁dancing -▁ihnen -▁elderly -euer -▁Erwachsen -▁Gesicht -olleyball -▁against -top -uchen -des -ser -ging -pf -äuf -zer -öff -▁high -▁dis -▁führt -▁pro -▁bow -emand -ork -▁cell -▁kick -aul -auk -ouse -▁track -▁trink -▁trees -▁Mantel -▁Schild -▁they -ooden -raw -▁One -▁Spr -▁floor -▁posing -▁posiert -ullover -▁Spielzeug -ünf -▁surf -fiti -uhen -▁about -▁Auto -▁having -▁Gelän -▁window -▁darauf -▁Shorts -▁covered -▁Mikrofon -▁umbrell -▁2 -iol -ift -ment -rä -pack -vers -ving -bee -braun -▁tel -▁wooden -▁Fenster -▁bat -▁Bä -itten -iss -uter -ors -cke -▁cost -▁zw -▁zur -▁Pf -asen -igar -ume -▁trying -▁Restaurant -▁blauer -▁schwarzem -eaning -ycl -▁very -▁wirft -▁Blumen -▁construction -ampf -örper -▁workers -▁busy -▁colorful -▁open -▁getting -▁dunkel -▁waiting -▁befindet -▁klettert -▁swimming -▁market -▁ev -ahl -ib -sh -lichen -kind -ject -▁Eis -▁met -▁sehr -▁rennen -▁wr -▁Feuer -▁pull -▁path -▁bal -▁Bad -ene -err -utsch -▁ice -▁new -▁night -▁kit -▁Pullover -atue -arb -arbeiter -▁shirts -▁oder -▁Das -▁Weg -achten -ießt -use -urn -▁trick -▁book -▁Rei -▁Raum -eep -eenag -▁cart -▁versamm -ücke -iff -▁singing -▁This -▁Woman -▁fence -▁es -ium -sel -phone -len -äut -▁af -▁hang -▁Met -▁son -▁sleep -▁Trepp -▁gen -▁pie -▁line -▁light -▁langen -▁Kü -ehnt -off -▁Zeit -▁cut -▁isst -ash -▁stair -unden -ungen -▁Wiese -ush -▁truck -▁board -asse -aben -▁brick -schaft -▁rotem -▁Sport -▁Männern -▁Anzug -▁flag -▁anderer -▁Kindern -▁rail -artet -▁zusch -▁Arbeiter -▁gekleideter -others -▁rosafarben -▁laying -▁essen -▁others -ient -ountain -yp -▁Ab -▁mach -▁making -▁fest -▁Maul -▁ride -▁river -▁wartet -▁paper -▁bed -▁Sn -▁Sur -▁Some -eak -ond -▁cour -▁kar -▁ins -▁inside -▁Wei -ief -▁body -▁Schutz -▁Rück -▁Rucks -▁Ob -▁drink -▁roter -▁Ver -▁hats -▁schwarze -▁backpack -▁posieren -▁weiße -▁Bluse -licher -▁surround -▁gestreiften -▁beard -▁Frisbee -▁clothes -▁Poliz -äufer -▁cellphone -▁kitchen -▁e -▁ent -hal -dler -becken -kl -körper -zeit -ße -▁takes -▁hel -▁full -▁sell -▁skat -▁rep -▁Fünf -▁gold -▁gesch -▁laugh -▁voller -▁big -▁Buch -ots -aden -▁art -▁step -▁grill -▁Stuhl -ersch -iew -tshirt -ooking -raf -este -▁Vor -▁Viel -▁auss -änger -▁Handy -▁grassy -▁Spieler -▁Fluss -liche -boarder -▁beleb -▁towards -▁werden -▁motorcycle -▁umbrella -▁metal -eis -eel -ech -oop -stst -wand -gang -ym -band -▁Afr -▁tank -▁hard -▁sil -▁warten -▁Five -▁ger -▁viol -▁Haus -ini -▁ind -iegel -igt -ummer -▁schi -▁box -ierten -ook -ellen -ogen -▁überqu -▁springen -änk -ipp -immer -▁Oberkörper -▁blonde -strumen -▁Um -▁Seil -▁bereitet -▁compet -▁paint -▁betrachtet -▁Hügel -▁Kleinkind -▁enjoying -▁zwischen -igarette -▁Metall -▁Snow -aid -ny -häut -▁door -▁fis -▁Mütze -▁sub -▁reitet -▁po -▁lake -▁lehnt -▁bea -▁beid -▁Kr -▁Kar -ird -ile -utter -▁zieht -▁Pap -ans -ater -ating -eine -▁she -▁Stock -▁oben -eiter -▁drin -ende -ults -▁alter -ecke -▁Leuten -▁Asiat -▁Bauarbeiter -▁ready -▁tanzen -▁schläft -altung -▁above -▁painting -edeckten -▁photogra -▁Child -▁sweater -▁schneid -▁several -▁piece -▁Viele -iend -ekt -man -dr -fall -oon -ohn -bar -kor -▁ac -▁dav -▁fall -▁Mitt -▁paar -▁lil -▁leg -▁lang -▁vest -ets -esen -▁cor -▁Pl -▁onto -aschen -▁miteinander -▁stone -▁many -▁Schul -ings -▁Renn -▁Rennen -ellt -▁Or -▁herunter -▁brid -▁class -▁umg -immt -ichtung -▁buck -▁snowy -▁Arbeit -▁dance -▁tanzt -▁colored -▁shopping -▁Händen -▁beobachtet -▁Bäumen -▁Rucksack -enen -ones -ane -ender -▁going -▁wave diff --git a/lingvo/tasks/mt/wpm-ende.voc b/lingvo/tasks/mt/wpm-ende.voc deleted file mode 100644 index 6344fe11..00000000 --- a/lingvo/tasks/mt/wpm-ende.voc +++ /dev/null @@ -1,32000 +0,0 @@ - - - -▁ -, -. -t -h -e -i -n -o -f -d -r -a -u -s -z -v -ü -m -( -) -w -T -b -I -S -: -c -& -q -; -- -D -y -l -/ -E -p -! -? -U -k -ö -W -– -’ -M -ß -K -C -“ -H -P -ä -% -# -9 -1 -3 -g -B -„ -A -2 -j -” -F -Z -Y -J -R -0 -5 -4 -L -x -O -� -V -N -8 -G -* -6 -7 -+ -_ -® -Q -▅ -€ -‘ -$ -= -{ -» -Ä -… -} -« -— -à -Ö -­ -@ -° -´ -² -™ -Ü -• -X -\ -¤ -§ -· -© -¼ -è -¶ -é -‚ -£ -ô -á -и -в -† -~ -ú -¿ -³ -ã -н -а -С -½ -ó -̈ -→ -â -í -å -− -ù -à -ç -ê -Č -ý -› -Š -± -× -ñ -^ -с -¡ -‑ -е -Г -ј -‹ -ń -Ђ -д -л -я -ğ -¬ -÷ -Á -В - -ę -¥ -ł -¹ -œ -č -п -о -з -ò -˙ -Ø -Ε -ï -т -¨ -µ -ч -đ -ð -æ -● -ś -ć -κ -α -ι -Å - -Ţ -ă -у -ő -É -ø -к -′ -š -τ -ο -Р -ž -ë -‟ -ř -¦ -″ -¾ -ě -э -î -Ő -№ -← -õ -Ł -ź -Ó -※ -À -, -ş -‡ -Ê -¢ -⇒ -่ -ľ -º -ì -。 -ů -ν -ė -ы -μ -ε -ь -р -Ú -υ -ň -Ћ -г -` -ą -м -ж -й -і -ż -Ç -Ν -Α -Τ -Ο -İ -י -η -û -π -ό -σ -Н -ı -б -中 -λ -θ -ή -Ž -Д -ű -β -的 -م -ن -ţ -Î -γ -ς -ί -х -ц -ر -Ñ -џ -Ќ -ร -Ð -א -Í -Ś -Ѓ -ī -δ -ا -È -Э -φ -ρ -ά -ŕ -خ -ت -ل -د -و -س -ع -ب -А -П -З -О -щ -ª -fi -ي -เ -Ş -Е -ū -Ý -ω -ю -ו -Ÿ -У -أ -М -إ -ج -า -И -К -น -ā -ك -έ -ξ -Љ -ї -ע -ō -Ï -Ô -ÿ -Ф -ť -Þ -ĺ -Đ -ύ -Л -ר -ة -Т -ф -ف -ш -ď -Б -ม -Ι -є -ش -ه -ح -Ż -ی -ง -อ -ē -ώ -χ -þ -І -َ -ъ -ל -њ -Я -fl -Х -ِ -ņ -▁, -▁. -▁t -▁h -▁n -▁f -▁d -▁r -▁a -▁s -▁v -▁m -▁w -▁T -▁b -▁I -▁S -▁D -▁E -▁p -▁M -▁P -▁g -▁B -▁A -et -en -ed -er -ea -es -em -el -it -in -ir -is -il -ig -ot -on -of -or -om -ol -ut -un -ur -us -ch -▁e -▁o -▁z -▁c -▁& -▁l -▁k -▁W -▁K -▁C -▁H -▁F -▁R -▁V -▁G -▁in -▁is -▁of -▁un -ie -iv -im -ic -ier -ies -ion -ich -de -der -at -an -ar -as -am -al -uf -um -ul -ür -ber -ge -▁th -▁to -▁for -▁der -▁an -▁al -▁be -▁pr -ent -▁y -▁U -▁L -▁N -▁on -▁im -he -id -ow -op -ft -re -ra -st -sch -ben -ct -ce -qu -le -ly -kt -ke -▁ha -▁den -▁die -▁das -▁re -▁st -▁se -▁von -▁wir -▁In -ere -ern -est -ine -ing -ist -isch -ort -omm -ung -▁zu -▁und -icht -ation -and -▁the -▁and -▁( -▁) -▁1 -▁2 -▁Z -▁er -▁it -te -tr -ten -ter -hr -her -if -os -den -ab -ay -ap -ach -up -uch -ve -ver -ck -lich -gen -▁ne -▁für -▁de -▁dies -▁as -▁auf -▁are -▁ver -▁mit -▁we -▁wit -▁The -▁Pr -▁ge -ess -iss -urop -▁ein -▁con -▁Ver -▁that -▁Un -ungen -▁: -▁j -▁J -▁O -▁or -eh -eg -eit -ect -ib -ied -ial -for -ro -rea -ag -ug -über -pl -port -än -▁not -▁nicht -▁des -▁dem -▁sich -▁wh -▁St -▁Sie -▁Europ -▁An -end -ers -ell -ind -ill -und -▁ex -▁eine -▁ist -ant -ann -art -▁this -▁als -▁all -▁pro -▁yo -apos -▁with -▁- -▁es -▁über -ein -ik -ien -iel -nen -od -oc -ff -ah -ad -ain -ub -men -ment -pr -gr -00 -▁fr -▁at -▁ab -▁auch -▁sh -▁wer -▁will -▁Th -▁by -▁Sch -▁Die -ear -ity -igh -igen -oll -ust -▁com -▁uns -ions -ass -all -uld -quot -▁have -▁dass -ischen -ommiss -ungs -apos; -▁us -▁ch -▁um -▁ent -ts -tern -hal -iz -ip -fer -mer -we -la -ön -▁has -▁da -▁aus -▁so -▁sp -▁sch -▁sind -▁vor -▁wor -▁was -▁En -▁Er -etz -enn -ens -ean -esch -ement -ure -▁co -▁" -▁' -▁We -ive -ate -ang -ard -ident -scha -▁wird -ische -▁which -▁you -▁from -▁werden -ommission -▁i -▁en -▁ich -ew -ec -ech -ov -ob -ok -og -ous -och -ost -ould -ran -aus -age -ahr -ud -ss -pt -ät -▁hat -▁res -▁am -▁sie -▁man -▁Mit -▁Par -▁Aus -ert -erv -otel -urch -▁oder -▁can -ick -ast -ament -ult -heit -keit -▁haben -▁200 -able -lichen -form -▁European -▁" -schaft -▁/ -▁! -▁? -th -ia -out -our -fü -ue -ze -beit -cc -▁nach -▁ad -▁me -▁ma -▁wie -▁Das -▁Ein -▁pol -▁part -▁Be -▁Ar -▁Al -err -eas -eal -ition -ise -ige -ore -ord -ome -uss -▁our -▁zur -▁cl -▁kön -▁Wir -▁unter -ice -ical -ieren -dern -aten -▁sein -ations -ational -▁einen -▁Parl -▁3 -▁qu -hn -hen -eut -ekt -fen -ren -rit -ac -ail -act -uct -sp -zen -ün -yst -▁do -▁durch -▁ap -▁wur -▁bet -▁but -▁It -▁Ich -▁Sp -▁pl -▁Ber -▁Ab -ember -elt -ign -other -▁&# -▁Fra -iert -ated -ates -ark -▁bei -schl -▁reg -▁einer -▁Union -ellen -▁also -▁your -ight -▁comp -▁können -▁– -tw -tra -iet -ide -ian -oun -ak -uß -ual -se -vel -be -cl -per -pro -är -gl -ger -▁tra -▁mö -▁more -▁would -▁best -▁EU -▁Mr -▁gr -▁gen -ite -ish -chen -▁zum -▁le -▁Commission -▁Herr -▁Vor -▁Gr -ime -imm -are -ary -▁there -ommen -▁their -▁19 -ose -liche -▁einem -ugh -ystem -▁’ -hl -ep -eil -fl -au -ack -ück -üss -bl -ör -äs -äischen -▁.. -▁nur -▁fin -▁dis -▁dar -▁ar -▁ag -▁acc -▁war -▁gro -▁Auf -erm -ely -itt -ong -cht -▁comm -▁li -▁kann -▁Hotel -anz -ank -ance -amm -amp -▁ander -▁been -sten -▁its -oss -reat -ahl -▁This -▁coun -▁'s -▁ihr -▁ -▁; -▁up -▁other -ner -rat -auf -üt -con -ähr -gt -gan -glich -▁noch -▁rel -▁sup -▁soll -▁viel -▁Der -▁Dies -▁Es -▁go -ese -ild -igkeit -olle -ution -che -chte -▁zw -▁Kon -▁Ch -▁For -▁Reg -▁Ge -ierung -ame -geb -▁one -ste -▁new -▁diese -▁dieser -▁jed -▁Jahr -▁should -▁unser -▁daß -▁work -äsident -▁... -▁“ -▁5 -▁4 -to -hre -ient -zu -bar -äl -ges -▁tr -▁fre -▁aber -▁act -▁sa -▁must -▁wenn -▁per -emein -ink -one -ors -unkt -▁ev -▁out -▁ke -▁Kommission -▁Re -▁ind -▁inter -▁intern -▁Um -▁Le -str -schen -ings -▁they -▁need -▁very -▁weit -▁Pres -▁Präsident -ehen -▁prod -echn -formation -▁mehr -▁wurde -üssen -▁countr -▁Y -▁em -fe -uen -ukt -me -wo -ces -ject -▁time -▁ro -▁sow -▁müssen -▁bis -ett -eten -ead -eln -ities -ität -ile -ons -olog -utz -▁ob -▁op -ieß -ater -ammen -alt -▁alle -▁prop -▁year -▁only -reich -▁sehr -▁Pro -▁cont -▁cons -ehmen -ible -▁about -assen -etzt -rans -▁Parlament -▁Bericht -glied -▁like -▁President -▁et -▁el -tschaft -hnen -eop -nah -oy -oin -ook -fahr -ri -ring -wend -▁hin -▁fir -▁dir -▁sol -▁serv -▁Ma -▁Men -▁Member -▁ges -erg -ism -onder -usammen -▁eff -▁la -ionen -atz -ument -ular -▁these -▁any -▁bec -▁import -iden -sta -leich -▁rec -▁diesem -▁gegen -▁prov -ubl -gramm -▁Mitglied -führ -▁polit -▁Arbeit -velop -▁if -ht -ider -ious -iament -av -ake -wer -ced -cess -ld -lin -land -ks -get -xt -▁no -▁such -▁Im -▁Ihr -each -ele -els -ility -igt -iger -olit -unden -ures -▁off -att -ange -▁them -entr -own -stit -▁report -▁20 -▁Zeit -▁develop -▁Unter -ibt -fore -änd -▁who -▁Europäischen -erson -halten -ause -eople -▁% -▁Ü -ne -fol -ru -aff -zeit -ühr -mal -bs -cy -ph -ken -gest -▁he -▁hier -▁sel -▁sub -▁my -▁were -▁well -▁people -▁Pl -ete -esent -itu -its -ond -▁over -▁Rech -▁ins -▁into -▁under -iew -iven -ative -arbeit -ree -rei -stell -sche -schied -ific -ays -acht -▁gew -▁Europe -▁Parliament -▁möchte -▁international -ha -io -fr -ful -dition -sicht -wn -cil --S -lem -lung -äft -▁trans -▁hotel -▁dif -▁real -▁area -▁system -▁vom -▁mark -▁wicht -▁bed -▁bere -▁De -▁Mar -ene -enden -elle -▁europ -▁kn -▁kon -▁Fr -▁Ges -▁inv -ices -ional -icher -ase -▁eines -▁Or -ieder -▁what -▁States -▁use -▁resp -▁groß -▁support -▁product -olitik -▁Über -▁6 -▁Q -ou -akt -vent -mitt -cr -kl -äch -1; -gel -▁her -▁hel -▁dam -▁some -▁most -▁wel -▁bel -etr -esonder -itte -inn -ory -old -ustr -▁cr -▁lie -▁loc -▁Coun -▁Fl -▁incl -ict -ats -▁than -▁Land -stüt -▁str -isten -very -änder -▁when -ments -halt -▁spec -▁Entw -▁ihre -conom -▁countries -▁sowie -▁Menschen -ever -lo -kommen -ös -äische -3; -▁his -▁now -▁aut -▁bas -▁Str -▁gu -▁gibt -▁Bed -▁As -ets -eat -ont -omp -ural -▁leg -▁inc -amen -▁Nach -fter -teil -▁dec -▁dieses -essen -issen -▁just -ages -ights -halb -ween -▁Ent -ards -▁many -▁make -▁polic -▁partic -▁again -▁[ -▁zwischen -▁weiter -oint -▁first -▁important -staaten -▁Council -▁„ -fin -air -uer -wir -▁techn -▁take -▁how -▁high -▁son -▁mo -▁muss -▁Se -▁pla -▁poss -▁Bere -enz -igung -oth -ute -▁econom -▁Comm -ives -anc -any -arm -ale -als -▁US -stand -▁requ -▁Intern -abe -abl -▁ass -▁Prod -▁Verb -ection -rough -▁unsere -▁Art -▁All -▁between -blem -▁] -▁keine -▁room -▁propos -▁because -▁Entwick -▁7 -▁end -hand -nis -ough -dr -dem -ues -weit -bst -yp -öff -▁hand -▁national -▁find -▁rem -▁av -▁soc -▁min -▁mein -▁biet -▁Ihnen -▁pos -▁Me -▁Min -▁Br -ence -ote -▁Ha -▁Her -▁Fin -ating -atter -ans -geben -ows -rauch -▁Prob -▁einz -▁contr -eht -▁Europa -▁world -ements -eute -ount -▁Grund -▁199 -▁good -ublic -▁9 -▁8 -▁Ä -ax -sel -setz -zial -ler -pf -▁two -▁nat -▁fo -▁dr -▁del -▁wo -▁If -▁So -▁Ex -▁Am -eth -ole -urn -uring -▁every -▁Wenn -▁Gesch -▁information -amil -ally -▁immer -rag -cept -▁had -▁see -▁10 -▁Zusammen -achen -gend -▁verb -▁conc -forder -▁where -ternehmen -▁made -ußer -▁anderen -ält -▁even -▁Mitgliedstaaten -▁Problem -ium -ace -uc -kr -king -äm -▁dann -▁sc -▁way -▁want -▁Seit -▁gl -▁gel -▁gest -▁gesch -▁Bür -itz -ited -ital -urt -urs -chten -▁Wh -▁Kr -▁iss -immer -▁those -▁through -▁pre -▁prot -ents -ower -erein -istr -ischer -▁diesen -ellt -▁problem -adt -alls -weise -overn -ordn -▁app -schließ -trag -ganis -▁years -▁direct -▁Produkt -▁” -day -richt -wort -pen -piel -öglich -▁tak -▁fact -▁rest -▁right -▁view -▁may -▁wieder -▁Des -▁At -ina -isen -ision -ork -unity -▁zwe -▁car -▁Welt -▁Con -▁Haus -ivers -uman -▁form -▁being -ster -ister -▁vers -isse -▁On -▁order -▁There -hne -▁wurden -▁Frau -▁Diese -nahmen -ituation -▁differ -icherheit -▁damit -▁belie -▁includ -▁policy -▁Bereich -ior -für -aur -ade -vor -ys -öß -▁num -▁su -▁mon -▁mean -▁Teil -▁bus -▁Ihre -▁par -▁get -▁gut -eration -itä -iness -innen -▁own -▁cent -▁kein -▁Kl -▁Hand -▁Rat -▁beg -rechen -lex -▁habe -▁seine -▁Ziel -ändern -▁exp -▁process -press -▁Schl -ollen -▁compl -zeich -▁disc -▁Geb -▁jedoch -emeins -stitution -▁market -▁know -▁particular -▁sondern -▁0 -tel -ool -fort -ved -ll -äge -gung -gesch -jekt -▁here -▁heute -▁ful -▁fest -▁red -▁sm -▁ser -▁main -▁wür -▁bu -▁ph -itions -▁cur -▁lea -ateg -raft -ragen -bens -▁stand -▁Inter -▁Information -▁Zu -tegr -▁neue -▁deb -round -read -▁Europäische -000 -laub -▁qual -▁quest -▁agre -▁unserer -▁You -▁etw -▁Maß -▁help -stütz -öglichkeit -▁id -hin -ock -fach -zie -min -br -com --M --P -pos -▁human -▁after -▁sim -▁same -▁wen -▁public -▁great -▁gleich -▁Bar -erk -erst -eld -▁eing -▁& -▁kl -▁Kolle -▁Richt -▁Gro -ently -▁Ind -ests -▁15 -upt -▁Unternehmen -▁Sta -▁att -lagen -ouse -osten -▁clear -▁appl -▁Vorschl -▁political -▁bereits -▁place -▁possible -▁requir -▁Än -▁concern -▁est -iß -ix -over -az -satz -bew -ähl -12 -▁fl -▁führ -▁aw -▁sur -▁say -▁mich -▁Tr -▁ber -▁person -▁ger -▁ganz -▁Best -eter -enen -ines -ince -iron -▁ohne -▁lar -▁lang -▁Fran -▁offer -ierte -ction -licher -▁neuen -▁spe -▁could -obal -▁does -▁access -▁ihrer -▁sollte -rift -ruck -▁selbst -▁wichtig -aurant -▁number -▁& -▁Änder -ux -üh -äre -10 -▁rights -▁val -▁much -▁br -▁bring -▁both -▁Is -▁Da -▁point -▁Man -eral -erung -iti -uro -▁eigen -▁Hin -▁How -▁Gen -▁Germ -▁inst -ierten -ata -▁then -▁ange -▁prof -▁present -▁Ne -kehr -▁12 -▁within -lage -ertain -▁andere -▁Jahren -reichen -▁contin -▁effect -▁Arbeits -entral -▁development -▁europäischen -▁increa -▁against -▁economic -▁avail -▁zwei -hip -viron -wick --B -kun -▁hist -▁fund -▁det -▁ac -▁sign -▁vis -▁var -▁med -▁besch -▁Sicherheit -▁Mon -▁gemeins -▁Bl -▁Besch -eam -oftw -utions -urg -▁equ -iver -ivid -ics -icult -ander -ulation -reit -ständ -▁denn -▁denen -▁18 -äng -earch -ustom -▁used -ease -▁play -▁Frage -▁möglich -▁level -immt -▁therefore -▁finden -▁sollten -▁Entwicklung -öffent -eich -net -ohl -ven -ving --A -line -äß -gar -▁som -▁mod -▁System -▁Mus -▁Part -▁Person -▁Ap -▁Akt -ith -ispiel -orr -under -▁opt -▁creat -▁last -▁Kommiss -▁think -▁priv -ention -▁still -lichkeit -▁verschied -agement -ploy -▁exper -▁without -▁ausge -▁cour -zeug -ically -▁group -ährend -▁country -nahme -fahren -▁provid -▁social -▁bietet -▁Bürger -vironment -oftware -▁ens -hol -zahl -ma -les -punkt -▁term -▁fe -▁down -▁meas -▁machen -▁bo -▁Ste -▁Daten -▁gem -▁Aut -ende -isk -▁oper -▁cap -▁long -▁kle -▁He -ule -▁bef -▁impl -owing -ope -rech -▁sec -esse -▁Vers -ected -▁Stadt -ienst -▁able -ize -▁dazu -okrat -▁Wirtschaft -ritt -ünd -▁viele -▁free -▁consider -▁services -esondere -ittee -▁liegt -▁different -hor -nte -ness -fern -ds -ude -zug -ves -▁akt -▁Tra -▁ben -▁back -▁Dem -▁Em -▁govern -uture -▁call -▁letz -▁Cl -▁Fre -atur -▁prin -que -▁start -ists -ationen -▁weil -▁jetzt -ibility -▁Stand -annt -▁allen -▁aller -ahmen -▁char -▁seiner -▁appro -▁financ -▁darauf -▁industr -elegen -stellung -▁special -▁modern -urther -▁business -▁curr -ready -▁available -kunft -hö -nisse -welt -ym -ER -▁hal -▁foll -▁future -▁air -▁voll -▁bes -▁pers -▁Möglichkeit -▁Pre -erb -ination -ised -ilt -omen -▁custom -▁look -▁Rahmen -ator -ante -arten -▁already -▁before -▁Lage -leist -▁rev -ife -▁verl -▁geht -▁einfach -▁progr -▁project -ience -ahre -▁Them -ealth -▁betr -▁größ -rateg -emeinschaft -strument -▁Programm -ismus -scheid -▁believe -▁small -ext -yn -öl -äh -jor -▁set -▁mir -▁bin -▁Med -▁Mein -▁Bes -▁But -eren -urity -▁cann -▁Res -▁Verein -▁Gew -ication -ders -ath -astr -alk -▁too -▁today -▁bek -▁seit -▁erw -ability -▁geb -▁einige -ially -▁design -ellschaft -▁dafür -▁schon -▁Energ -▁environment -udget -▁30 -ossen -▁open -▁service -folg -▁products -abei -▁Finanz -▁Informationen -▁Maßnahmen -▁course -hält -spr -w. --F -led -pe -▁fac -▁data -▁mus -▁Pol -▁Ad -itiz -inen -inal -ire -orn -▁case -▁certain -▁Russ -ica -▁prom -stellen -▁Zimmer -ucht -▁deut -▁gef -▁And -▁allem -▁programm -grund -▁That -allen -ätz -▁Mittel -ultur -▁2009 -ccess -▁adv -ually -ished -▁Herrn -ample -▁ihren -▁individ -aterial -▁offers -▁Rechts -▁respons -▁Internet -▁cannot -▁ed -mö --s -los -politik -▁typ -▁nun -▁besonder -▁Sh -▁Serv -▁Eine -▁put -▁Amer -ether -isierung -ilit -olid -olution -chie -▁zusammen -▁city -▁Kin -▁Für -▁integr -iving -ately -▁impro -opt -operation -tens -iff -ified -uchen -vers -▁Uns -▁Jahre -ahn -ains -▁Schw -▁meine -▁Bei -▁zurück -▁regard -▁Commissioner -▁art -affen -▁invest -▁Qu -▁question -▁Kommissar -▁Ö -nov -ry -ron -aum -ziel -mar -besondere -▁fol -▁day -▁during -▁reas -▁wollen -▁To -▁El -▁Ph -▁Bo -itor -itive -ins -isl -omat -▁citiz -▁Ro -imes -gew -raph -tend -▁neg -▁verfü -▁cond -▁entsp -▁energ -▁Aussch -heiten -▁Als -uction -▁Spiel -flicht -▁unseren -▁activ -▁said -▁law -führt -bsite -setzung -▁global -▁würde -124 -▁implement -▁eas -ohn -uz -ways -bed -lie -lungen -lungs -ply -kon -öt -äuf -▁famil -▁rap -▁achie -▁member -▁Mill -▁Pal -▁Politik -ily -▁Wie -▁Fir -angen -amer -gehen -▁La -lei -ktion -▁stat -▁strateg -ichte -▁example -antwort -present -halts -▁cost -▁major -spar -▁| -immung -▁Form -uktur -▁rooms -▁proposal -▁issue -▁including -▁means -▁debate -▁quality -▁financial -▁each -ove -fass -red -ga -gleich -▁tun -▁hö -▁hear -▁far -▁few -▁doch -▁situation -▁since -▁während -▁bea -▁Deut -▁pass -▁gar -▁Bild -ilities -oren -▁Was -▁Ger -alen -reff -racht -▁stehen -▁sect -ands -▁16 -ache -genommen -▁necess -▁Und -▁Our -▁sicher -▁exc -▁child -▁sozial -▁result -▁meet -▁common -▁interest -▁Parlaments -▁technolog -▁etwas -▁Vorschlag -▁However -▁| -agen -ugen -ward -wert -▁fun -▁ret -▁matter -▁wissen -▁Durch -▁Euro -▁power -▁Bet -ites -ott -org -utes -▁komm -▁Viel -▁Gl -ürlich -▁Ur -▁Länder -▁Lös -opp -rad -rache -▁ref -▁stay -orth -▁erhalten -▁conf -agt -ants -ized -▁amend -zept -▁adopt -▁compet -anken -gangen -▁sagen -▁object -linie -▁Unterstütz -▁insbesondere -mittel -klär -▁Committee -▁Geschäft -▁citizens -▁� -▁ern -▁öffent -fet -falls -come --R -▁richt -▁außer -▁brea -ird -offen -offe -▁cult -▁kommen -▁Kunden -▁Fall -▁Rest -▁Gemeinschaft -ieg -ars -▁three -ential -▁New -▁represent -▁stud -▁11 -▁14 -▁25 -▁next -▁einmal -▁Verfü -indet -illion -▁expl -▁schn -▁schw -tract -▁waren -▁areas -▁bedeut -▁Markt -▁respect -▁Qual -▁author -▁found -ordnung -▁Änderungs -▁histor -▁measures -hör -ek -ipl -uge --T --c -pect -ßen -äd -äum -▁further -▁Techn -▁bl -▁budget -▁Punkt -▁give -▁Beh -esen -ining -igten -uten -utzen -unde -▁lassen -▁thank -▁beim -▁rece -▁wirtschaft -▁Zug -▁ersten -▁organis -▁democ -▁abs -etzen -ünft -▁Ital -▁China -▁employ -▁Projekt -▁direkt -▁provide -▁Zusammenarbeit -▁taken -▁success -itäten -▁discuss -▁build -▁large -▁energy -▁Unterstützung -▁ä -▁X -▁ear -.com -co --D --K -gang -joy -▁du -▁deal -▁drei -▁read -▁vot -▁pay -▁Bel -ilfe -off -orgen -▁link -▁With -▁inde -ieb -ieben -ierungs -asch -ald -▁begr -▁secon -▁wirk -▁erst -cken -▁ask -▁geben -esser -ehm -porte -▁entwick -▁schl -▁addition -▁besteht -igkeiten -▁Umwelt -▁Recht -haft -vention -▁position -▁agreement -▁establ -▁ensure -tain -nung -ogen -zess -mit -len -por -par -kf -ker -▁terr -▁fil -▁did -▁Tag -▁Sw -▁Dienst -▁pop -▁Mad -▁Port -▁ze -▁inform -ather -ash -ums -ules -▁Ländern -▁Namen -▁statt -▁steht -▁sehen -ichts -▁Stra -▁shall -▁come -▁eng -▁Beispiel -▁unterstüt -▁seinen -▁Fragen -▁company -örder -▁opport -altung -▁elect -führung -▁Deb -▁problems -▁Group -▁increas -▁necessary -▁ir -burg -EN -äss -glichen -▁ho -▁hope -▁health -▁dabei -▁sk -▁wäre -▁Dar -▁given -elf -ises -urz -ury -▁lead -▁Wer -▁War -▁Kom -▁Red -▁inner -▁ann -▁bew -▁pract -▁Not -▁imm -▁imp -schein -schrift -▁13 -▁17 -trem -▁why -asse -▁change -▁2008 -eutung -▁tradition -▁key -wendung -▁2010 -▁invol -▁legisl -wirtschaft -▁however -zieh -▁security -▁princ -hof -häng -nten -zü -man -bau --E -lt -pend -AT -AN -▁teil -▁vote -▁muß -▁Beit -▁Bew -ising -isation -igent -▁list -▁Ko -▁Vol -ief -ivil -ively -▁always -▁pres -leg -ters -▁def -▁verw -▁verst -▁je -egt -▁while -▁entfern -▁worden -▁cooperation -ätze -reng -iety -▁needs -ettbew -▁located -▁protection -▁Kollegen -▁accept -▁letzten -gaben -▁second -ift -fang -von -gem -20 -▁nor -▁sens -▁writ -▁Trans -▁Sa -iter -▁life -▁Wor -▁West -▁Hier -▁int -bereich -▁deren -rechte -▁hap -▁refer -ingungen -▁24 -genden -▁They -▁Ob -ialog -eller -undheit -▁allow -ikan -▁short -▁comb -ranspar -▁add -▁better -twort -▁account -▁commun -▁Regierung -▁50 -▁Lebens -▁transport -▁große -▁großen -▁welche -▁Entscheid -▁USA -▁control -▁Verfügung -iend -aut -wards -bo -ged -gre -▁net -▁fiel -▁fav -▁ren -▁men -▁mult -▁wish -▁Ben -▁Aber -▁Auch -▁zahl -▁kom -▁Comp -▁House -▁Fe -▁Fol -▁Famil -▁Rom -▁Lin -leicht -▁100 -abil -▁defin -▁conn -▁United -▁using -▁che -▁cho -ipp -▁Ergeb -▁2007 -sequ -▁action -unktion -▁diff -▁Bedeutung -▁legal -▁Seite -▁What -▁Gebiet -▁full -▁install -▁Kinder -markt -▁* -eign -iod -ique -zus -IN --m -ying -lossen -part -pflicht -▁herv -▁reach -▁around -▁sing -▁mov -▁Sk -▁Mal -▁Bus -eder -iges -▁cor -▁When -▁Gu -immen -atch -reen -▁ermö -text -▁notwend -ellent -▁exist -▁Schutz -▁enjoy -ünst -clus -blick -▁arr -▁zweit -gebot -▁jeder -▁working -▁perf -▁consum -▁Berichterst -▁Community -▁International -dress -▁bieten -▁current -iation -fre -vest -bt -kauf -gabe -▁near -▁sl -▁Beg -▁Air -erc -▁eben -▁crit -▁less -▁km -▁Fort -▁Gä -ared -ames -kte -▁stellen -▁1. -▁Zahl -▁erforder -träge -▁Vertrag -▁Jun -▁whether -▁Stud -▁extrem -▁alles -▁These -▁chang -▁daher -▁davon -obil -▁Mrs -▁darüber -ießen -▁become -entrum -▁production -▁based -▁Haupt -▁centre -▁particularly -▁Zukunft -▁profess -▁terms -▁Thema -hm -hmen -iar -nehmen -ood -fra -rän -uce -by -kungen -xim -▁treat -▁dist -▁Tur -▁brauch -▁San -▁Betr -▁lim -▁Wort -▁Weg -▁Chr -▁Fahr -amt -▁toget -▁another -enten -▁UN -▁step -▁orig -▁esp -ennt -▁Weise -▁research -▁2000 -▁clos -▁Frauen -▁trade -▁weitere -▁sowohl -▁gegenüber -▁subject -▁local -▁Probleme -▁eigenen -▁visit -▁befindet -▁individual -▁rapporte -▁Restaurant -▁difficult -hang -ex -zi -vious --G -▁him -▁non -▁näch -▁dort -▁run -▁mar -▁water -▁Dir -oms -uter -▁care -▁Wahl -anies -▁tour -▁state -▁erreichen -▁organ -▁express -ika -ikel -▁friend -asser -ipt -▁Ende -ecially -▁hatte -▁könnte -▁regional -▁EUR -▁gener -▁politischen -▁Ihrer -▁meiner -▁contrib -▁natürlich -▁issues -▁Haushalts -ategor -▁klar -▁besonders -▁children -▁wirklich -▁unterstützen -▁hervor -▁together -▁old -ev -ners -fäh -aden -zuf --L -yl -▁tax -▁aff -▁bar -▁book -▁bene -▁Sec -▁page -▁gent -orm -▁crea -▁Fest -geordn -▁No -reg -schutz -ocument -▁ihnen -▁address -▁Alle -ording -ritte -ails -▁arbeit -▁gesam -▁systems -▁specific -load -▁guar -▁Mehr -▁Industr -▁Staaten -▁German -▁various -▁verschiedenen -▁experience -▁Energie -▁Service -▁reason -▁conditions -▁innerhalb -▁entfernt -▁Berichterstatter -▁+ -ty -ode -dings -af -bild --C -ES -ße -▁tot -▁role -▁aim -▁women -▁beach -▁Situation -▁pea -▁pack -erien -oment -unter -▁intr -anzen -ances -▁prep -bene -isches -ifft -ifiz -▁whole -adies -▁comfort -thing -ours -▁Abst -▁companies -▁vielen -▁Forsch -▁bereit -▁basis -finden -selves -▁Seiten -▁personal -itiative -▁industry -▁Ausschuss -▁beaut -▁sector -▁benef -▁guarante -▁_ -til -oor -ried -uced -sen -kes -kung -ären -ON -▁hoch -▁di -▁distr -▁met -▁welt -▁Terr -isions -▁occ -▁col -▁Wettbew -▁Kar -▁Rep -▁Rolle -▁innov -ived -ima -imate -aster -▁though -▁top -▁prior -▁staff -▁sever -▁Zwe -uppe -▁Jan -prü -▁spea -▁close -▁möchten -▁grow -▁zwar -oney -▁effort -▁internationalen -▁really -▁restaurant -▁month -▁ideal -▁especially -iber -ober -üd -ms -way -yr -▁turn -▁feat -▁dür -▁done -▁document -▁Tour -▁Mod -▁Bas -eag -ots -▁coll -▁Woch -ican -alten -▁third -▁ans -▁beh -▁Uhr -raum -▁ste -ably -gende -ehmer -indung -▁proced -weisen -▁Engl -▁Spe -▁region -▁bestimm -▁Hotels -▁period -▁hinaus -▁Ihren -▁location -▁food -ething -▁Richtlinie -▁führen -▁private -▁government -▁members -schriften -▁create -iem -zt -▁dra -▁sum -▁site -▁mot -▁Treat -▁Ir -▁Dr -▁pat -▁port -▁glaub -▁Afr -ino -cher -▁Kü -▁instit -ival -ications -icient -amew -▁press -▁Nutz -ides -oph -▁reform -iste -▁erste -ehn -ände -ody -prom -▁enter -ization -iple -echan -▁Park -▁perform -▁event -zeitig -onds -▁gewähr -▁automat -▁Strateg -raucher -▁Germany -▁approach -▁negot -▁Geschäfts -▁favour -ton -iol -druck -wär --b --V -AR -▁million -▁Software -ette -igr -▁Wind -▁Rück -▁offen -imin -atte -ativen -ults -▁town -▁Lie -▁Nov -rael -erne -ifen -achst -▁Prot -▁job -▁Ang -ferenz -▁among -▁2006 -ordin -sprache -▁Berlin -▁general -ishing -▁itself -▁europäische -▁Gesundheit -▁struct -▁decision -▁Artikel -▁langu -▁something -▁Meinung -reffen -fety -▁single -▁rapporteur -▁several -ting -zust -zimmer -In --H -▁hät -▁home -▁fast -▁ra -▁she -▁material -▁Sel -▁post -▁Mark -ences -isis -olf -▁zus -▁lab -▁Komm -▁Fisch -▁Gleich -ierungen -▁anges -▁recht -▁Univers -antrag -▁show -ograph -▁resour -▁resolution -▁making -ians -▁genau -▁198 -ittle -▁Kontr -▁contact -▁etc -ksam -▁Gesellschaft -▁least -▁continue -▁progress -▁programme -▁excellent -kfast -▁netw -▁gentlem -amework -ning -voll -mos -cke --in -less -▁text -▁name -oto -uth -▁z. -▁zun -▁kind -▁often -iere -asst -uft -ufen -▁anal -▁beiden -▁prec -▁Invest -aps -uppen -iken -▁management -▁apart -ußen -prod -▁relations -▁going -▁unserem -▁Leben -▁opin -▁understand -ification -▁Entwicklungs -▁Zusammenhang -▁serious -▁away -▁Partner -▁Personen -▁following -▁family -▁kommt -▁schnell -▁World -▁peace -▁introd -▁gentlemen -haus -gän -▁rules -▁sea -▁sett -▁software -▁mach -▁bz -▁Sc -▁My -inz -iner -▁cas -▁colle -▁let -▁Kultur -▁Car -atives -▁National -▁online -▁streng -ingen -indest -prop -gricult -weis -okument -▁plan -▁pleas -▁Abgeordn -▁regul -▁bestehen -▁finanz -▁Dieses -▁Kont -gestellt -cript -ächst -▁crisis -▁Entschließ -▁particip -▁directive -▁Schluss -▁complet -▁optim -▁entsprechen -▁Deutschl -▁absol -▁weltweit -▁negoti -▁ident -sk -zogen -mäß -wand -ägt -gemein -50 -▁range -▁sof -▁milit -▁money -▁Do -▁Pat -ither -use -▁little -▁Weit -▁Hilfe -▁Fil -icle -uff -bereit -▁towards -▁forward -▁ant -▁price -▁once -▁rep -▁stellt -▁21 -▁Verantwort -▁Jul -ager -ension -though -▁unterstütz -▁doub -▁Span -▁Region -▁Präsidentin -▁Gesetz -▁welcome -itäts -▁etwa -▁feel -▁Demokrat -▁Lösung -▁Technolog -▁Gäste -▁® -▁il -ike -▁dest -▁sw -▁situ -▁mention -▁bal -▁Son -▁pot -▁pur -▁gilt -▁Bank -ches -chit -▁until -▁things -▁Natur -ops -▁stär -ichten -iful -liches -▁neu -▁ja -platz -unds -annten -ference -weil -▁spiel -icken -outh -▁daran -▁recog -▁Organis -armon -▁natural -▁Deshalb -▁weniger -▁bringen -▁Wirtschafts -▁customers -leistungen -▁review -fassung -▁begrü -▁Madam -▁institutions -▁bzw. -▁00 -hing -uellen -IS -ckt -▁test -▁fair -▁days -▁aid -▁bro -▁bitte -edit -otz -▁land -▁Kosten -iesen -recht -▁2. -▁23 -▁Zust -▁einge -präsident -▁umf -▁2005 -füll -▁Grün -ücksicht -▁Char -▁Gegen -▁jeden -baren -▁keep -▁indem -▁empf -reiche -oints -▁gesagt -lands -reiben -gelegt -▁strong -teilung -forderungen -ämlich -ählen -▁increase -▁persön -▁Preis -▁opportunity -▁Treaty -▁ess -ple -▁hard -▁nämlich -▁rather -▁Text -▁big -▁Ser -ems -ined -inter -ison -▁kosten -▁kurz -▁Can -▁Cent -▁Gar -▁Gren -▁Geld -stadt -istic -ommend -ify -ission -▁Vertr -plan -▁Europas -ocus -▁sogar -▁Erfahr -▁'t -▁enc -▁unterschied -▁quite -▁agree -▁40 -wohl -wendet -idence -idency -ächlich -▁Sept -▁positive -▁Sozial -▁Produkte -▁bekannt -▁activities -▁Millionen -▁check -▁growth -na -uh -cel -CH -▁tas -▁sug -▁might -▁Tür -▁Sup -▁Dis -▁Mo -▁Ass -ona -▁lo -▁line -▁Rates -atis -▁Nor -reib -raf -stück -treten -abhäng -app -apan -▁nehmen -▁dev -▁Off -ports -▁Ansicht -fert -▁partners -▁Jahres -▁Members -▁politische -werk -scheiden -▁Fried -▁Ort -▁fully -▁gerade -▁provided -elegenheit -▁program -▁ebenfalls -frage -▁Christ -▁resources -mosph -▁September -▁ü -hunder -ij -völ -mb -wahl -komm -ächt -OR -▁four -▁mag -▁Tod -▁Tats -▁besser -▁Sal -▁perm -▁Per -▁After -ennen -igungs -onger -▁Kap -ana -anst -gegen -▁yet -▁Nicht -rachten -leit -▁erre -▁erfolg -▁State -▁ihn -▁ihm -▁Austr -▁2004 -▁class -▁seinem -▁darf -▁Dieser -▁include -airs -▁request -▁verbunden -▁value -▁signific -▁gemacht -▁Unsere -▁Zugang -▁Deutschland -res -ser -side -system -bon -ling -AS -▁tem -▁dessen -▁wahr -▁wesent -▁Den -▁pa -▁park -▁gute -eless -irm -▁led -▁log -▁ladies -icial -ativ -atten -geh -▁along -▁having -▁Institution -osition -▁gehen -illa -lau -▁Aussprache -amental -▁admin -▁Bezug -spekt -▁Fraktion -programm -▁left -auer -▁solution -ühren -▁proposals -▁educ -▁technology -▁amendments -▁field -▁beautiful -propri -▁unterstützt -ros -sweise -za -mut -beh --f -▁times -▁ak -▁Sol -▁Buch -itzen -▁light -▁Hö -▁Frei -▁unver -▁derzeit -ente -stra -stant -einander -▁framework -▁above -half -schaften -ideo -prochen -▁darin -ratic -▁Regel -▁safety -▁recommend -▁Rechte -fully -lock -▁economy -▁remain -▁minutes -self -setzen -▁appear -benso -▁idea -▁Israel -▁easy -▁öffentlichen -▁Qualität -hört -▁Janu -▁dürfen -▁glaube -fäl -ähe -▁transpar -▁na -▁flex -▁viol -▁Mär -igital -unst -chs -▁Rats -alle -▁tool -▁behalf -racy -▁Instrument -esten -ichtet -▁website -▁week -▁Priv -▁einigen -▁constr -▁Verhand -▁One -▁nichts -▁challen -▁schaffen -▁Website -obe -▁2003 -▁makes -ektor -gebung -▁freed -▁Rei -▁content -▁Früh -▁Flug -▁Meer -ethod -▁prevent -▁taking -▁called -leiben -▁costs -▁adopted -▁brauchen -▁Abstimmung -▁strength -here -nehmer -most -wür --W -pre -OM -▁flo -▁focus -▁agricult -▁bank -▁Sub -▁Dank -▁Disk -irc -ured -▁never -▁Prof -▁Antwort -▁Web -▁results -▁meisten -schluss -▁ihrem -▁others -▁sollen -▁Jahrhunder -utzt -▁Verbraucher -▁handelt -▁nationalen -▁einzel -▁verbess -schließlich -▁keinen -▁beginn -▁standard -▁Kolleg -▁Groß -▁application -▁Beschäft -▁provides -▁charact -▁return -▁building -▁legislation -▁normal -wärt -▁zusätz -▁opinion -word -IT --0 -äus -▁tar -▁team -▁due -▁mass -▁TV -▁Ebene -▁past -▁points -iting -oten -▁Komp -▁Co -▁Far -▁instrument -iers -anced -▁stark -▁Inst -▁22 -achten -▁versch -▁Verl -▁Verkehr -▁Just -eits -reater -aged -earbeit -ampf -▁Leist -ringen -▁heiß -▁policies -▁weiterhin -▁Minister -▁deliver -overnment -▁simply -▁attention -▁concerned -▁follow -▁projects -▁betrifft -▁responsibility -▁perfect -▁Fortsch -uelle -bour -bare -ägen -▁host -▁dom -▁risk -▁macht -▁bor -▁Sitz -▁Bem -edi -isiert -arf -arian -ufact -lect -▁sei -istan -verkehr -▁context -▁Verw -▁Op -▁deshalb -▁young -ustain -▁2001 -▁appropri -▁besten -▁commit -ients -▁solche -▁importance -staat -▁Plan -stellt -▁known -▁Design -zeichnet -▁questions -ziehen -▁wenig -▁effective -▁groups -▁benöt -▁deutlich -▁heart -▁bedeutet -▁Änderungsan -▁total -iellen --1 -▁head -▁holid -▁rad -▁send -▁II -▁Sl -▁Pra -▁greater -▁Apr -▁obl -▁Wasser -▁Von -▁oft -atul -ators -berg -▁beispiel -▁Netz -ceed -qual -▁aufge -▁aufgrund -eite -änger -▁cover -▁reserv -▁Mitarbeit -▁Bezieh -▁clean -▁interess -▁road -frist -▁wichtige -▁heraus -▁society -▁version -▁Klima -▁complete -▁würden -▁standards -▁continu -hören -▁Besuch -▁environmental -ledge -▁strategy -züglich -▁professional -▁€ -▁000 -uit -zlich -weg -käm -äter -▁fail -▁verein -▁moment -▁method -▁bit -▁Dokument -▁EC -▁Ag -erbs -▁central -▁Kunst -▁infl -▁institution -anze -ama -ales -heb -rach -stehen -vert -verb -ada -▁Schul -▁schön -▁words -▁manufact -▁Bevöl -ritten -örden -äsent -gesetzt -frastr -sichtlich -▁Menschenrechte -▁findet -pfung -▁views -▁Richtung -▁required -▁increasing -▁principle -▁nächsten -▁November -.0 -hib -now -fall -ban --O -AD -jen -▁nutzen -▁farm -▁don -▁via -▁mount -▁IT -▁Ed -▁May -▁Pak -eau -uren -▁lear -▁Will -▁Wohn -▁Feh -▁Förder -▁fort -▁Nähe -ided -leben -▁Ins -▁Ju -▁Japan -ellung -▁user -▁vorgesch -▁quick -acy -▁training -▁Herren -▁relax -▁Umsetzung -genda -▁parl -▁clearly -▁allerdings -▁music -▁meeting -▁breakfast -▁Windows -zustellen -▁significant -ai -uell -äche -▁hoffe -▁night -▁rea -▁Such -▁Dec -▁pages -enzen -unch -▁Fern -▁Raum -ims -atal -anten -▁Nord -reet -bend -legen -verständ -▁weiß -aging -▁demand -▁extern -▁entscheiden -ips -▁sour -▁2002 -▁197 -▁works -ießlich -▁recent -▁Struktur -▁technical -▁assist -▁protect -zeichn -▁capital -▁achieve -▁cultural -▁authorities -▁Benutz -▁Selbst -▁Verantwortung -▁Demokratie -▁essential -▁suggest -▁April -▁beispielsweise -▁u -biet -lar -kerung -▁side -▁Ira -▁Date -▁gra -▁Bil -▁Brit -onen -onom -▁cla -▁longer -▁Wert -▁Wachst -heim -key -ernen -ester -▁aspect -▁gegeben -▁gehört -▁Stat -▁enough -ünf -▁according -▁suppl -▁internal -▁solutions -folgt -haltung -▁Bedingungen -wirkungen -▁Grundlage -▁directly -▁Ziele -▁Interesse -▁Vorschläge -▁fundamental -▁gemeinsamen -▁Apart -▁kleinen -▁Empf -▁facilities -▁First -▁jeweil -▁notwendig -▁distrib -▁months -▁‘ -a. -wach -best -OS -▁vier -▁Tages -▁Dam -▁Eur -▁AG -▁Aspekt -inge -isher -ones -▁Cr -▁Real -ari -alking -schn -istischen -▁erziel -verl -▁verwendet -▁demokrat -▁Anwendung -▁extra -gress -▁enthalten -▁Erfolg -▁Einz -üssel -▁vielleicht -▁Kons -ooking -ublik -▁Ihrem -▁guests -▁proposed -▁nature -▁wohl -richtung --Pro -▁history -maß -▁currently -▁responsible -▁employment -▁Direct -▁illeg -mbH -.00 -hem -ding --t -yle -▁sustain -▁vol -▁Druck -▁gere -▁Außer -urf -▁zust -▁civil -▁circ -▁Kön -▁Krit -▁Go -▁GmbH -deut -▁bevor -orteil -ape -gency -▁Verfahren -▁Opt -ienen -▁ausgest -▁sprechen -▁schwer -▁Paris -▁Parte -▁Einsatz -▁events -▁interests -▁subs -▁Please -▁From -▁60 -▁Heraus -▁Geschichte -▁requirements -▁Daher -▁Musik -▁halten -▁sozialen -▁terror -▁ermögl -▁erreicht -▁Reihe -▁character -▁ö -▁$ -▁ur -ta -30 -AP -▁sure -▁bleiben -▁Mot -▁Aug -▁Außen -erous -onstr -▁live -▁Wel -▁foreign -▁anim -rect -▁station -▁erfüll -▁web -▁übern -▁werde -merksam -lauf -▁Erweit -ensive -▁parties -▁please -▁regions -immungen -▁needed -linien -▁themselves -ables -▁apply -▁berücksicht -▁Muse -▁Möglichkeiten -▁promot -▁competition -▁voted -▁Debatte -▁involved -▁happen -▁kompl -▁extremely -▁network -▁recogn -würd -▁= -rieben -zentr -bank -AL -▁fight -▁Sport -▁Soc -▁South -▁Damen -▁camp -ull -▁force -kten -▁separ -▁Integr -ommod -▁27 -ielle -▁Erklär -ätzen -zeigt -▁maxim -itionen -▁könnten -rench -▁ago -▁upon -attet -arbeiten -▁century -▁Sicherheits -▁Aktiv -▁verschiedene -ynam -spruch -▁Öff -▁implementation -▁höch -▁schwier -▁democracy -▁establish -▁Entscheidung -▁changes -▁arbeiten -▁guarantee -▁efforts -▁language -▁freedom -▁vorgeschlagen -val --d -▁wea -▁wind -▁Doch -▁pool -▁By -inner -irt -▁lot -▁low -▁Wissen -▁Hoch -ichen -aren -amb -amous -▁Nat -rab -ingt -andid -▁Zus -iffen -abel -▁Ant -▁soon -▁schrift -obile -aussch -▁regulation -▁oppos -▁contain -▁developed -▁konkr -drück -▁Minuten -istration -▁expect -▁Frank -▁Hinblick -▁gemeinsame -▁Blick -▁Russia -▁Unser -▁verfügt -▁Industrie -▁comfortable -▁situated -▁oblig -▁ermöglicht -hes -dam -gern -▁tats -▁nu -▁nam -▁mess -▁bil -▁Du -▁publ -▁More -igte -orge -unik -▁either -▁caus -▁lange -▁Kir -▁Kraft -▁Cour -▁Rel -atory -ental -ctions -▁star -▁decl -▁contract -erseits -▁comes -▁hatten -▁Auswirkungen -gesellschaft -▁Prozess -▁Platz -▁markets -crib -▁minim -▁previous -▁France -▁vollständ -tended -▁Änderungsantrag -▁zeigt -▁cases -▁negotiations -▁Institutionen -▁akzept -▁Frühstück -▁Diskuss -▁target -▁{ -frei -ware -IC -öll -▁acr -▁bem -▁Bau -erra -usst -▁Kle -▁Hol -▁unique -ano -▁altern -reh -ktionen -adurch -▁attract -wegen -▁schein -▁amb -▁Voraus -▁final -gesehen -▁true -▁bisher -▁developing -▁1999 -▁everything -▁maintain -ählt -▁führt -▁surround -▁created -▁Steuer -rechts -▁function -▁wirtschaftlichen -▁äußer -▁Dienstleistungen -ipped -▁Ergebnis -▁erforderlich -▁gesamten -▁speak -▁Strategie -▁Kontakt -▁Tatsache -jenigen -▁» -.1 -.2 -ügen --In -EL -▁house -▁fünf -▁danken -▁Ide -▁Del -▁pict -▁Aff -eden -▁ebenso -▁cal -▁later -▁living -▁Cas -▁Host -▁Fäl -▁French -▁Rad -▁almost -▁prices -▁states -play -elling -▁allgemein -▁Schritt -▁Webs -▁Ausdruck -▁darum -▁accommod -▁Regionen -barkeit -▁Presidency -wohn -folgen -▁Landes -ymb -▁improve -▁entwickelt -▁additional -▁Familien -▁draw -▁analys -▁education -▁einzelnen -▁Kolleginnen -haps -oid -met -TO -bel -bahn -B. -▁file -▁wa -▁Team -▁Denn -▁Mont -▁getr -oft -change -▁cut -▁komp -atic -ane -arte -▁thus -▁Liber -oper -▁denke -▁Zum -plätze -▁Angebot -prise -▁share -▁Schwe -ipment -▁climate -▁que -schlag -▁leicht -▁196 -▁disk -▁liber -▁actually -phas -mitted -ändert -ception -▁included -▁monitor -▁Staats -▁Version -▁democratic -▁immedi -▁Außerdem -.000 -ländern -EC -go -jähr -▁hold -▁wide -▁ble -▁Binnen -etten -orf -▁lack -▁angenommen -▁behand -▁Lis -▁Lond -▁Luft -idd -▁seen -▁Inform -▁Zur -▁mitt -ibr -änden -▁exam -aint -▁Erst -ense -▁travel -imme -auber -ässt -▁jedem -▁Mitglieder -elef -▁internationale -▁Bereichen -▁Handels -▁General -▁gemeinsam -▁receive -▁Portug -pendent -produkt -inzip -▁Abgeordneten -▁encour -lauben -▁Verhandlungen -▁Änderungsanträge -▁… -▁} -oben -fel -fragen -feld -zun -ült -kra -▁dialog -▁setzen -▁vill -▁bi -▁Iran -▁Süd -▁Dialog -▁Pap -▁Position -ilar -igur -otos -▁euro -▁CD -▁Führ -▁Ris -▁befinden -▁stor -▁Zw -▁Zentrum -artet -▁outside -▁konnte -mitteln -▁held -▁weiteren -abled -▁restr -▁gleichzeitig -▁funds -▁Vereinigten -▁erwä -▁designed -▁type -▁links -▁happ -▁original -fähigkeit -▁hätte -▁appropriate -kämpfung -han -oom -zent -ka -▁heut -▁harmon -▁famous -▁rate -▁mind -▁wider -▁walk -▁IS -▁Mil -ender -isieren -▁eigent -▁Fund -icated -aler -▁thing -▁Leit -▁Liste -erei -▁erhö -apt -verfahren -▁neben -▁Anst -iens -lack -▁später -▁school -▁relev -ologie -▁consult -▁eleg -▁assoc -▁knowledge -▁download -▁American -▁Terror -▁features -▁procedure -▁z.B. -▁umfassen -▁heißt -▁Beziehungen -▁Bevölkerung -▁August -▁London -ikt -ound -riff -zw --st -öhn -gument -▁hon -▁funktion -▁dan -▁PC -etter -▁zuf -▁Währ -▁North -ommer -▁Vertra -▁join -rank -▁amount -▁Abkommen -iance -auen -▁Aufgabe -aufen -gebiet -▁jede -▁workers -▁propert -▁transl -▁helfen -▁Flugh -ösen -teilen -standen -▁posit -▁80 -▁throughout -▁Interessen -▁kleine -▁customer -▁looking -▁majority -▁early -▁impact -▁communication -▁Feb -▁answer -▁absolut -atisf -ba -ble -ID -ca -cies --2 -pose -▁hours -▁five -▁date -▁rund -▁aud -▁völl -▁bat -ilf -▁cop -▁corr -▁> -▁Fuß -▁univers -dert -▁angem -▁beit -kei -eiten -▁exclus -▁users -▁space -▁parts -▁doing -▁emphas -▁hinter -▁gegenwärt -▁201 -mals -▁wichtigen -▁meinen -▁gelten -▁gestellt -▁takes -▁Online -▁equal -▁Themen -▁Programme -▁certainly -▁Beitrag -▁Forschung -▁Kontrolle -▁Schlussel -▁across -iqu -ios -uck -zif --h -kn -ko -künft -öst -▁divers -▁draft -▁mechan -▁Mag -▁Pa -▁Acc -enken -esh -▁lat -▁Werk -▁Kos -▁Kampf -▁Vis -ump -▁although -▁prakt -fte -▁stop -teid -ayer -▁verantwort -issions -▁consequ -▁Verpflicht -▁Stabil -ella -▁proper -▁solid -affung -▁guest -kräft -richtungen -▁spezi -▁determ -▁investment -▁traditional -▁correct -▁colleag -▁Türkei -▁Förderung -▁> -,000 -ee -fung -va -IP -cent -länder -▁spr -▁search -▁Id -▁Met -▁Pass -▁gab -▁ever -▁lux -▁High -▁intens -▁took -▁bekommen -▁erinn -troll -hren -▁verd -esserung -▁Eng -▁resol -▁complex -clear -itted -▁relig -▁jedes -▁unseres -▁record -▁liegen -▁Verbindung -▁gestimmt -▁Handel -fortun -▁Barcel -▁concerns -gangenen -▁connection -▁existing -▁Turkey -▁Wettbewerbs -▁English -igration -▁strengthen -▁bleibt -▁Febru -▁digital -▁wün -▁bess -eath -inden -▁zul -▁card -▁Kop -▁Cor -▁Che -▁Rechn -▁Vorteil -▁inf -iehen -ande -alter -▁Let -rachen -▁wegen -formen -▁demonstr -▁enlar -ourc -zeugen -aking -fluss -üsse -▁try -▁Reform -▁Imm -haupt -▁europä -▁cross -ucing -▁detail -▁model -▁programmes -▁America -bedingungen -amera -▁richtig -▁leading -▁writing -▁friendly -bildung -▁Entschließung -▁mentioned -▁nahe -▁Court -zunehmen -nissen -ox -SE -ci -▁wunder -▁gerne -▁Bitte -ener -empt -int -ini -▁lok -▁Kor -▁Government -ara -▁Up -▁Liss -▁28 -term -abon -▁verhin -▁Prior -▁confl -▁Verordnung -▁joint -▁nothing -ending -▁extens -annte -▁vorlie -▁community -▁related -ological -▁Strand -▁decisions -ämp -▁includes -▁built -▁concerning -▁aware -▁wichtigsten -▁aktiv -▁reasons -äufig -▁Behörden -▁Nutzung -▁completely -▁parliament -▁Damit -▁Frankreich -i- -date -ric -view -ühl -ball -▁herr -▁sag -▁west -▁game -▁Bad -▁Augen -uted -una -▁Wieder -▁Home -▁unabhäng -▁Lux -quis -▁repl -▁26 -osen -▁Ober -▁allows -▁front -▁Auswahl -▁Gruppe -ördern -▁Aufent -▁committee -gesamt -▁internet -▁mehrere -▁hinsichtlich -▁basic -▁assess -▁Krise -▁einges -▁profit -▁half -▁culture -▁population -▁practice -▁Notwend -ikanischen -▁Ausschusses -▁military -▁holiday -▁Mitarbeiter -▁Einzel -face -zub -lang -AM -jahr -▁mer -▁ww -▁Since -▁Dan -▁Eff -▁Mor -▁ganzen -▁ganze -etary -inne -ired -ille -onnen -omb -unities -▁kultur -▁insp -arity -uffer -▁Link -istrat -▁Thank -▁enthält -ennung -ovo -▁nachhalt -▁warm -▁Forder -ava -stellungen -▁Verbesserung -▁agreements -▁einger -▁media -▁Standpunkt -▁advant -▁Urlaub -▁Volks -iterran -▁choice -▁ermöglichen -obei -▁Erweiterung -iddle -▁Lissabon -nern -gliche -▁wobei -▁Term -▁bur -▁body -▁Sant -▁poor -▁Macht -▁Post -ency -erweise -ush -▁Cons -▁Cont -▁Funktion -alien -gef -geführt -▁prim -▁Les -▁Now -▁geeign -▁conclus -▁Anfang -ills -▁south -ervice -▁leben -▁trotz -ology -▁becom -▁official -▁transfer -schließend -brigen -▁simple -▁values -rechend -▁Standard -▁Gewalt -▁garant -▁objectives -▁received -▁zeigen -ercial -▁package -▁jobs -▁collect -▁Lisbon -▁« -sehen -vement -ET -just -▁tel -▁ruh -▁satisf -▁E-M -unt -▁Gelegenheit -ieht -iment -▁pros -▁Ukra -▁Insel -▁Innov -andlung -▁verg -▁gehören -ession -▁Veranst -ream -▁Anal -undes -▁frü -▁Schaffung -assung -▁entire -izient -ourn -▁plann -▁computer -▁500 -▁center -▁applications -▁equipment -▁Personal -▁Club -▁regarding -▁Global -opping -▁established -▁files -▁origin -▁performance -▁Wachstum -ymbol -▁Binnenmarkt -▁völlig -eff -ias -nt -such -ED -pite -gement -AC -▁wonder -▁Su -▁Sim -▁EN -▁purch -▁Mic -▁Pf -erve -itel -▁cru -▁Kern -▁City -▁Here -▁Fach -▁Funkt -▁initi -▁Leg -▁Verwendung -▁überw -gratul -halte -▁sound -▁grund -ützen -kennen -▁below -▁move -▁einzig -sterreich -▁Gebä -▁attack -▁capac -▁objective -▁Darüber -▁keyword -▁Ergebnisse -▁spielen -▁broad -▁agricultural -▁verbessern -▁quickly -▁Barcelona -iot -iable -fonds -zentrum -wander -pekt -▁hous -▁face -▁sun -▁East -▁passen -▁Material -▁lässt -icles -entwick -▁image -▁Zweit -▁erklär -▁jud -▁Anzahl -▁Enter -spiel -▁Chin -▁reports -▁Zeitpunkt --St -▁bedro -▁großer -▁Landwirtschaft -▁See -▁goods -vorschriften -▁guten -▁leaders -▁Management -▁eigene -▁experien -▁Russland -▁gefähr -▁verfügen -iples -▁verwenden -▁benefit -▁Woche -▁Liefer -▁potential -▁Jugend -▁König -elefon -▁www. -son -seit -more -möglichkeit -wide --term -äst -ón -▁nice -▁Tem -▁Tage -igher -▁FR -▁Ferien -▁Ru -▁Gree -iminal -ature -scht -▁seems -istische -▁Prov -▁conv -▁spac -▁code -▁Mitt -ertungen -▁party -renz -spolitik -▁contains -▁recently -steller -klus -▁Amster -fordert -geschlossen -▁industrial -▁verfüg -▁achieved -▁soziale -▁Lösungen -oppel -▁indeed -▁popular -▁sense -▁double -▁Affairs -▁bathr -▁Notwendigkeit -▁ill -zer -lam -▁tw -▁feh -▁fördern -▁veh -▁vict -▁wr -▁Tat -▁Solid -▁Don -▁Eigen -▁Mich -▁los -▁künft -▁CO -▁Central -▁Rot -▁initiative -statt -▁congratul -ags -industr -ahren -ätigkeit -▁click -örper -▁archit -▁frei -▁Reise -ändig -▁self -iveness -▁moder -▁meinem -▁Finally -▁Amend -▁Krieg -▁Änderungen -▁engine -▁Business -▁June -▁treatment -▁gesamte -▁zunächst -▁beginning -verständlich -▁diskut -▁Informations -nehm -fält -rü -aß -ave -RO -▁huge -▁fra -▁rail -▁sorgen -▁block -▁Sil -▁Some -▁Mac -▁gelegen -iglich -▁oil -▁Während -▁Log -▁rein -essourc -▁note -▁vorhand -sprechen -▁Konferenz -▁Mai -▁efficient -arbeitung -▁Dez -▁highly -öffnet -▁Herzen -▁everyone -▁scient -▁carry -▁gleichen -▁Hinter -▁presented -▁Beschl -▁equipped -reiten -▁levels -▁garden -▁successful -▁Transport -▁Juni -▁Second -▁floor -▁tatsächlich -▁dialogue -▁— -oden -afen -mark -buch -lant -ging -ground -▁furn -▁rout -▁mal -▁Det -▁Dabei -▁Pers -▁Agr -iten -isc -utt -▁Wein -▁Kre -▁Ham -atastr -▁former -stances -leisten -▁Initiative -▁veröffent -▁geführt -▁eindeut -▁comprom -▁Bef -▁31 -▁Regierungen -▁active -mehr -▁governments -▁integration -▁entwickeln -▁increased -▁verstär -greifen -▁Linux -▁consumers -▁Mehrheit -▁sofort -▁Erfahrung -▁Social -▁schriftlich -▁Schlusselwort -fortunately -▁x -▁ut -tent -ued --N -gere -▁higher -▁dang -▁rat -▁age -▁sent -▁vern -▁IN -▁DV -▁Män -▁Pan -▁Balk -▁Wahr -▁Col -▁Further -▁Golf -icien -schw -▁stage -▁sex -abor -▁whose -▁Stellung -ube -pret -ausch -▁Einführung -prozess -imming -itting -▁90 -▁carried -▁series -▁agreed -▁amendment -▁ernst -aschinen -▁Debian -▁verstehen -▁Roman -▁reviews -reibung -▁Install -▁äußerst -essourcen -▁à -hab -kraft -▁Bra -ery -ering -onse -oral -organis -ico -atell -ased -▁anzu -▁Lauf -▁Nur -rew -▁29 -▁neuer -agn -▁except -▁schle -▁vorher -okoll -▁Berg -▁Vors -▁supply -▁news -▁Umgebung -fahrt -▁direction -▁office -icherung -▁high- -▁70 -minister -▁similar -ühm -▁somit -zeugt -▁Element -▁confirm -▁asked -▁hohen -▁principles -▁choose -▁innovative -▁lediglich -schnitt -▁subsid -▁Öffent -PE -▁tell -▁dynam -▁rates -▁six -▁Sum -▁DE -▁Eing -▁Aud -▁Ho -▁une -▁Lat -owers -▁Inde -▁verpflicht -anner -▁atmosph -▁shar -▁shown -▁shows -▁300 -▁beteil -▁Sprache -clud -▁traff -▁Konfl -▁perhaps -▁elekt -führen -▁hotels -▁Develop -teilt -▁avoid -▁Therefore -ployment -▁gebracht -▁matters -▁reflect -▁thanks -▁wirtschaftliche -▁Belg -▁added -▁Familie -▁gewährleisten -▁Organisation -▁erfolgreich -▁Höhe -▁Ratspräsident -▁commitment -▁influen -tt -hung -uern -wise -lassen -put -ächen -▁Sicht -itable -unsch -chn -chter -▁zentral -▁Gest -▁Gemeins -arr -entlich -▁Loc -heben -▁erk -▁erschein -iffe -▁depend -▁Of -▁Open -ends -artige -iko -ierenden -▁anderer -▁York -▁obwohl -schieden -▁stress -▁Article -▁foot -▁speed -▁Neben -▁considered -▁Vereinbar -▁Kommunik -▁Film -ufficient -▁Fehler -▁aspects -▁ausgestattet -ausschuss -▁nuclear -▁erwähn -▁latest -ees -▁amer -▁seas -▁Dinge -▁Erachten -▁ged -▁Ba -eng -eria -iles -usion -▁came -▁lik -▁Well -iegen -alu -estellt -▁condition -▁Okt -▁prob -ienste -▁enable -ously -▁Bekämpfung -▁Alt -▁Spain -▁commercial -etting -▁obtain -▁possibility -rought -▁meines -▁Produktion -▁phys -▁effects -▁promote -▁Kommissarin -▁opportunities -▁Komprom -▁zahlreiche -▁Folgen -▁non- -▁Wettbewerb -▁behind -▁bestimmte -▁Freiheit -▁learn -▁Integration -▁accommodation -▁Amsterdam -oo -besch -cing -xis -▁nied -▁fal -▁dring -▁miss -▁Transpar -▁bean -east -eland -ored -uffet -ume -▁stra -▁erhält -osp -▁einschließlich -▁Jed -▁St. -illed -▁exchange -▁entde -▁spir -▁vorgesehen -▁Präsidentschaft -altungs -▁response -setzungen -▁providing -ündet -▁easily -äume -▁Italy -▁Italien -▁ähn -▁reached -▁zweite -▁prepared -▁bestimmten -▁summer -▁materials -▁setting -▁Fortschritte -▁external -▁Aktivitäten -▁E-Mail -▁atmosphere -▁Erachtens -euer -fg -r. -IL -ych -light -lös -▁ten -▁north -▁video -▁woll -▁Dom -▁Ele -▁Pla -emin -orden -orks -▁Cal -▁Camp -▁Rea -▁Gast -stein -stoff -▁erweit -▁erster -cks -▁Oct -ängt -▁exhib -ained -zeuge -▁tragen -eph -ützt -▁decided -▁reduce -▁creation --Fra -▁außerdem -▁Republic -▁pressure -mäßig -kommens -▁sustainable -▁scheint -▁restrict -▁relevant -▁Ukraine -industrie -▁Dezember -,5 -hilfe -uk -uel -US -és -▁terra -▁nie -▁dadurch -▁death -▁size -▁vo -▁pan -▁gives -▁green -▁Booking -oler -▁Com -▁Fed -arily -ulgar -ley -▁reject -▁style -inger -istics -▁Stelle -▁franz -astic -▁bestät -▁allein -▁solchen -▁effizient -▁submitted -▁Überein -▁1998 -▁versuchen -elegenheiten -▁Preise -▁Also -▁Gemeinschafts -▁Vertrags -▁closed -▁Jahrhundert -▁British -▁Museum -▁Kosovo -▁sagte -▁capacity -atastrop -falt -uert -mann -will -IM -licht -önen -▁tro -▁sites -▁virt -▁WTO -▁Ke -▁Gra -icy -▁alten -▁Nieder -idel -▁neigh -▁concept -▁ord -ibilities -▁Stunden -endes -▁coming -ssystem -▁35 -▁regular -ounts -▁train -▁Gruppen -ught -▁Gefahr -▁actions -▁insgesamt -front -▁places -▁remains -richten -▁Convention -▁Bestimmungen -▁details -▁gemäß -▁betroffen -▁hohe -▁zweiten -▁prepar -▁Benutzer -▁Erklärung -▁Schweiz -▁französ -volle -cial --Z -OL -▁table -▁talk -▁suffer -▁Di -▁Down -eting -esp -emb -iture -▁cause -▁Wohl -▁Kur -▁island -geber -▁erheb -▁aufgenommen -▁vergangenen -▁Oper -roat -angt -▁partner -▁quiet -▁register -▁display -▁Aufmerksam -▁obs -▁March -▁credit -▁einzige -pression -▁discover -▁leave -orrow -▁Medien -▁erneut -▁announ -ategorien -▁priority -▁Africa -▁balance -▁December -maßnahmen -▁property -▁enlargement -▁grundle -▁architect -▁ändern -üg -bet -ps -äglich -ja -▁sport -▁billion -▁Mart -▁gas -▁B. -eav -ilom -ilos -▁ca. -▁Cap -▁Rec -imension -ürf -bert -▁Last -quare -▁rech -▁describ -▁Anwend -adem -lav -▁Erw -▁grant -▁genießen -▁effekt -ney -ämt -itzer -bewertungen -▁funding -▁Punkte -▁Western -▁Entscheidungen -▁connect -▁consumer -▁limited -▁Forschungs -rieden -▁Basis -▁März -▁assistance -▁Flughafen -fghan -▁Aufmerksamkeit --r -page -▁niem -▁morgen -▁mobile -▁wid -▁brought -▁Sm -▁Meth -▁Bundes -▁Abend -igungen -unternehmen -▁ko -umb -anden -aper -▁vertreten -▁jün -ugs -▁Star -▁Städ -▁Anforderungen -ollar -▁End -ecut -▁ground -gebracht -▁Berichts -▁konzentr -▁Organ -▁supported -▁forms -▁keiner -▁charge -▁conduct -▁independent -▁renov -▁movement -ategories -▁restaurants -▁Küche -▁regulations -▁Gästebewertungen -▁Server -▁administrat -iterranean -ournal -▁Oktober -▁Booking.com -.5 -sh -mm -▁fig -▁fix -▁rule -▁Sam -▁PL -etimes -▁Kan -ierter -▁Zun -▁Prü -▁Ot -▁Stär -▁export -▁chem -ritts -bei -▁leider -▁Vorschriften -strukt -stitute -▁Exper -▁geschlossen -▁Bereiche -▁Staat -▁variety -▁beschäft -▁Party -▁creating -▁airport -▁größten -▁erwarten -▁Italian -▁schließlich -▁Volk -▁limit -▁apartment -▁pleased -▁Juli -▁Grenzen -▁January -▁Beschäftigung -▁Bilder -▁eigentlich -möglichkeiten -iciency -▁Öffentlichkeit -▁Transparenz -ault -zul --l --up -yse -▁häufig -▁fisher -▁Ist -▁Every -unk -▁lic -▁King -▁Ged -▁Gip -anlage -este -▁Zeich -▁deep -▁Ansatz -▁Anfrage -▁Erzeug -fühl -▁compreh -▁leisten -ört -▁safe -▁produced -altet -▁Arbeitnehmer -▁konnten -▁wenige -▁Unternehmens -▁largest -ühungen -▁instead -▁Neu -lichkeiten -▁discussion -▁friends -▁Wochen -▁Gesundheits -▁purpose -▁Privat -▁construction -▁Verkehrs -▁u. -▁message -▁eingesetzt -▁DVD -▁October -cle -länd -kur -▁dat -▁sus -▁ways -▁Tex -▁Mut -▁Mult -orgt -chsel -▁Villa -▁thous -▁sta -▁stability -isters -▁conference -▁Jo -ibil -iker -prün -ption -spro -▁durchgeführt -▁regel -tragen -▁genommen -▁194 -ütz -▁Präsidenten -▁obvious -▁became -▁geworden -▁wichtiger -▁Meine -▁klein -▁Services -▁Bildung -▁interesting -▁study -porate -porary -▁steps -enntnis -▁Investitionen -▁benötigen -▁Empfeh -▁Diskussion -▁Solidar --Fraktion -▁urs -uis -zone -bür -yond -ki -äte -▁fall -▁rich -▁brit -▁Sign -▁Site -▁Sommer -▁Et -inder -ilfen -▁Wider -▁Kn -▁Cost -▁Gal -▁unmittel -icate -▁Nationen -▁item -▁depart -▁Start -▁regards -▁accord -▁Auftrag -chtung -▁kannst -▁freund -▁suchen -▁Gesamt -akter -▁helpful -▁berühm -▁board -▁aktuellen -▁started -▁Schwier -▁Firma -▁höher -▁film -▁allowed -▁Computer -▁relationship -▁Today -▁Januar -▁associ -dom -vision -mod --p -gra -▁fishing -▁san -▁weder -▁bear -▁Sinne -▁ES -▁Ere -▁path -▁kennen -▁Hel -▁Rights -stre -ifik -▁Veränder -▁exact -prises -oking -▁discr -▁ihres -▁produce -▁Untersuch -▁select -itude -forderung -▁medium --An -▁Systems -▁operation -▁gefunden -▁activity -▁status -▁legislative -▁contribution -▁procedures -▁angesichts -▁Gründen -▁task -gehend -▁source -drücklich -▁Portugal -▁europäischer -▁compromise -▁niedr -▁ursprün -kosten -▁hom -▁dank -▁Sache -▁purs -▁Bur -ony -orgung -utr -▁kit -▁Hung -▁Heute -▁Gott -anischen -gegeben -▁bezüglich -▁Net -legung -▁Innen -▁conven -▁Java -▁Ol -▁Ost -fordern -ellow -▁Schn -▁word -oker -▁arm -▁Auffassung -▁consist -▁provisions -eleist -▁damage -etres -eating -▁1997 -overty -▁Tradition -▁halte -liegen -▁Projekte -▁written -▁distance -▁Spanish -mosphäre -▁liberal -▁village -▁Aufenthalt -▁Development -iell -dienst -ais -EM -pts -▁treffen -▁dut -▁won -▁During -▁Mann -▁got -▁giving -▁Bon -▁Bahn -▁AL -▁Afghan -ero -▁eher -▁Geh -▁überprü -ades -ograf -▁sieht -▁ourselves -▁plant -▁compon -▁relation -geleg -▁Flor -iction -▁sonst -▁prefer -▁option -▁Democ -ronic -▁statement -▁historical -äumen -▁Rede -zusch -zusetzen -▁arrang -▁Beginn -▁documents -▁labour -▁July -▁begrüße -▁übrigen -▁instruments -▁Micros -zy -▁tom -▁bra -▁base -▁Dep -▁Many -▁Pet -▁Bulgar -enk -▁zeit -▁ziehen -▁loss -ivity -stag -▁diejenigen -▁seem -cker -▁Prinzip -ford -indruck -angs -▁Armut -ierende -▁beste -▁Regeln -▁evalu -ateral -▁firm -▁launch -▁minor -zeichnung -▁bringt -▁types -▁entsprechend -▁Beteil -▁öffentliche -▁Koh -▁Linie -freund -▁criteria -zuführen -▁benefits -▁entsprechenden -▁violence -▁Tagesordnung -▁beitragen -▁conflict -▁früher -▁Schlusselwortern -▁Ereign -▁# -▁­ -▁quot -▁urg -vors -EP -▁div -▁mem -▁wine -▁worth -▁Tre -▁Dritt -▁pen -elten -ira -▁kur -▁Fakt -▁Nacht -▁Nr. -idge -▁constant -egation -ängen -▁Anw -ellten -▁Although -ücken -▁Aufgaben -ammlung -▁Your -▁Unterschied -▁useful -▁Bedürf -▁highest -▁require -▁langfrist -▁Monaten -▁Appl -spricht -▁wissenschaft -▁Straßen -scheinlich -ränkt -▁structure -▁University -▁Bemühungen -▁alternative -▁Fällen -▁immediately -▁vergessen -▁ol -rang -zwe -ya -▁mut -▁Ta -▁Test -▁Mos -▁Mess -itness -▁zehn -▁lower -▁krit -▁Ressourcen -iebs -ummer -uls -▁section -▁Zentral -▁erlaub -▁erfolgt -ffice -griffen -▁entw -izin -▁Beruf -▁finally -rats -▁elements -▁hinzu -folge -▁Nachbar -▁Bürgern -▁getan -▁Änderung -▁integrated -▁employees -ünstler -▁Restaurants -▁Spezial -▁kostenlos -▁Centre -▁Vertreter -▁parking -▁transparency -▁Iraq -▁distribution -▁behandelt -▁mechanism -▁verhindern -▁@ -▁ign -eks -aign -ücht -wind -bit -▁hy -▁nimmt -▁d. -▁agenda -▁Tagen -▁bad -▁Sen -▁Econom -▁Bot -urance -▁late -▁Har -▁Free -▁Völ -▁Gel -▁präsent -▁stock -ienz -▁daily -ogle -▁genannten -▁anderem -▁super -▁round -▁laut -▁well- -▁drive -▁numerous -▁applied -▁ausgezeichn -▁Auto -orning -▁reading -▁affect -▁Republik -▁Dr. -▁hätten -▁tools -▁challenges -frastruct -▁mountain -▁bathroom -▁Furthermore -ted -beg --U -park -▁tim -▁teach -▁van -▁besondere -▁Ess -▁Eigent -▁poverty -ola -▁Wunsch -▁unem -iveau -▁Las -▁Inhalt -▁erh -▁Europä -laden -▁coast -▁reality -▁legit -ype -owser -▁drink -▁Kredit -▁Atmosphäre -▁versucht -▁processes -▁offering -▁Bürgerinnen -▁Milliar -▁Behandlung -▁payment -▁elections --mail -▁Betrieb -▁occas -▁apartments -cription -chitekt -▁perman -▁paar -▁illegal -▁gerecht -▁Natürlich -▁consequences -▁solidarity -▁Microsoft -▁ult -dra -setzt -craft -yer -lass -läss -polit -OT -▁sort -▁pal -▁günst -▁e. -▁Human -▁Garten -▁unbed -▁fordern -▁beyond -reifen -▁sechs -ifel -▁verm -▁Vergangen -▁überhaupt -▁unterb -bezogen -▁warum -▁goes -▁offiz -zeiten -▁welcom -▁Aller -▁Herz -▁Produktions -öße -▁saying -▁offered -▁Kommissions -▁Gerichts -▁Werte -▁Folge -▁perfekt -▁Erfahrungen -▁paid -▁construct -frastruktur -▁Wissenschaft -▁analysis -▁colleagues -▁erklärt -UR -▁sufficient -▁mü -▁Tor -▁Est -▁Mass -▁Public -▁Bea -▁ehem -▁cy -▁Hal -▁Ven -▁init -▁Lassen -leb -▁reich -▁ersch -achs -▁Stock -alled -ungs- -▁Ents -▁Altern -▁32 -▁Grand -▁Gef -▁.... -▁45 -▁interpret -▁kont -dresse -▁Minder -▁phot -▁ideas -▁requires -▁modified -ulegen -▁persons -▁größer -▁Mediterranean -▁folgen -▁practical -▁mensch -▁zahlreichen -▁thought -▁Protokoll -▁Weiter -▁Spanien -▁berücksichtigt -▁happy -raten -sych -ücke -mp --w -DF -äger -▁sämt -▁Tab -▁IP -▁SM -▁Even -▁Cop -▁Hy -dere -▁forces -▁stimmen -▁Initi -änge -gruppe -▁shopping -▁Erd -▁Regierungs -gesetz -▁gewesen -▁welches -▁geschaffen -▁curren -▁speech -▁größte -▁richtige -▁historic -▁ski -▁Security -▁Austria -earbeitet -▁Praxis -▁jeweiligen -▁erfüllen -hesion -▁minimum -▁Terrorismus -▁advantage -▁Gebäude -▁Enterprise -▁congratulate -ilosoph -▁jüng -▁staat -▁Vergangenheit -ius -penden -gy -60 -▁syn -▁sales -▁wählen -▁Dat -▁Doppel -▁Mand -▁Bro -▁ey -▁Fäh -▁infrastruct -icit -▁stets -▁Inn -abr -▁neues -▁weeks -▁einheit -▁charm -izienz -▁schützen -▁waste -▁enh -▁clients -▁betreff -▁zumindest -▁frequ -lines -▁simpl -▁attempt -▁starting -▁Finanzierung -gewiesen -▁implemented -▁Viele -▁historischen -▁organisations -▁renew -▁doubt -▁partnership -▁Agent -▁Aspekte -▁getroffen -▁crucial -▁Zweitens -▁verpflichtet -▁tomorrow -fil -sätze -cer -cape -kor -▁morning -▁Tier -▁Tal -▁ban -▁bill -▁bald -▁pie -▁Miss -▁Bud -▁Budget -itors -irl -▁lines -▁Cast -▁Green -ivität -ione -ato -▁bein -▁befür -annen -ocol -▁Schritte -ordination -▁seines -▁growing -fläche -altungen -▁Menschenrechts -▁1996 -krimin -▁server -▁sometimes -▁operating -▁Room -▁Palest -▁außerhalb -▁reference -▁Technologie -▁Center -▁Opfer -▁maximum -▁published -▁modernen -▁comprehensive -.m -vere -üge -▁Dann -▁PH -▁AC -▁lives -▁Val -▁Gem -▁uner -bericht -iding -estic -▁Prem -▁Vergleich -▁Verteid -theless -▁adapt -▁plat -▁Berat -ieten -▁genug -▁efficiency -▁meaning -▁reduction -▁Frankf -▁options -▁Rechtsvorschriften -▁Spieler -▁Firmen -▁ready -▁Airport -▁profession -▁Berichterstatterin -▁Angelegenheit -▁machine -▁repeat -▁Frieden -▁Directive -▁circum -▁Google -▁caused -▁vollständig -▁monitoring -▁honour -▁Vertrauen -▁verantwortlich -▁Amendment -▁Agrar -▁Download -▁° -IA -PS -▁tät -▁har -▁sports -▁She -▁Dav -▁Ak -itation -▁kam -▁kür -▁Video -▁Geg -▁unten -▁Nun -stelle -▁street -▁err -▁erl -upts -▁verab -▁verkauf -ungsver -obby -▁anders -▁relating -▁email -▁anything -▁Play -▁technischen -▁While -▁begin -uption -▁Dazu -▁folgt -pective -hafte -▁persönlichen -▁transparent -▁Parteien -▁Herausforderungen -▁campaign -▁pictures -▁heutigen -ämpfen -▁probably -▁Schwierigkeiten -▁erlaubt -▁Allerdings -▁´ -sb -vis -vollen -lose -karte -gn -▁ded -▁vital -▁Tar -▁Bal -▁Act -igation -▁les -▁Falls -▁Flex -ula -▁anerk -▁prü -opol -quem -▁recon -ischem -egung -▁usw. -▁ausschließlich -angel -▁Einige -▁arch -▁undert -▁Ord -standing -▁worldwide -▁Hinsicht -▁players -leistung -▁erwartet -▁deutschen -losigkeit -▁rapid -▁technologies -ehnt -▁languages -▁Entschließungs -▁swimming -▁stärker -▁border -▁bessere -▁spirit -▁accordance -iant -oot -oon -wün -▁sal -▁symbol -▁TH -▁Telefon -▁Soll -▁Eas -itten -itarian -▁cast -▁Hoff -▁Ra -▁UK -▁Lang -▁NAT -▁Zivil -uche -▁jähr -▁John -▁Annahme -schaff -▁advert -▁Einwander -spunkt -▁produc -▁Prozent -▁Ihres -▁Gespr -▁decor -▁remov -krise -▁Conference -stitutional -erkennung -▁150 -▁Österreich -▁interested -▁Swiss -▁critic -▁tourist -▁Angelegenheiten -▁challenge -▁farmers -▁terrorism -▁elegant -▁angemessen -▁erinnern -g. -80 -▁tal -▁hor -▁ris -▁sil -▁walking -▁Sonder -▁Drit -▁Mc -▁games -ena -▁cert -▁lands -▁Gold -arge -▁angen -▁angebot -▁Licht -▁ones -▁zukünft -▁erfahren -tritt -▁verurs -reas -▁Stellen -▁extent -▁übernehmen -▁Schla -▁chance -▁Einrichtung -▁195 -▁disp -▁saf -▁respond -▁wanted -▁appre -▁spezif -▁valid -▁Islam -▁privaten -▁secure -▁folgenden -äuft -▁voting -▁hervorragen -▁Modell -▁methods -▁Chinese -▁scientific -▁² -▁™ -zulegen -po -äher -gült -▁fine -▁fant -▁setzt -▁candid -▁lies -▁Gran -▁pred -▁prem -▁images -reise -▁zudem -▁italien -abkommen -ielt -izes -▁coup -▁enorm -▁upd -▁releas -▁release -ienten -▁gesehen -▁selection -▁specif -▁natür -▁wiederhol -▁beginnen -posed -▁Arbeitsplätze -hältnis -ätzlich -konom -▁sectors -rengungen -▁Obwohl -▁automatisch -▁begrüßen -▁Friedens -▁Kapital -▁bitten --Office -▁allgemeinen -▁Solidarität -▁ehemal -fs -pons -äden -gent -▁hot -▁falls -▁Ther -▁blo -▁S. -▁Mün -▁lost -▁Winter -ichern -▁beein -reibt -▁deine -▁vertra -▁Versch -ehens -▁despite -indungen -▁exerc -▁beide -▁bestimmt -immern -eilen -▁darstellt -▁counter -ildet -▁Konzept -barer -struct -▁gesetz -▁endlich -▁remember -▁expert -poses -▁Trotz -▁Generation -▁long-term -▁Heim -▁letzte -▁businesses -ördert -board -▁visitors -▁etc. -atalog -▁separate -▁Milliarden -hotel -nel -rich -betr -lern -40 -▁fear -▁powers -▁Moment -▁Bu -inens -▁las -▁lay -▁länd -▁Wann -▁Hause -▁Gas -imination -▁alt -reis -verage -▁deg -▁Prä -▁Antrag -izing -etzung -▁Ausgang -icket -▁Einfluss -fence -action -bezieh -▁genu -▁trying -▁substant -haben -▁incor -handel -▁News -▁accepted -▁motor -▁Ireland -▁raised --in-Office -▁Liberal -▁Erstens -▁absolutely -▁eindeutig -▁influence -▁dringend -hilfen -bin -IV --to -pool -RE -▁sinn -▁Sü -▁Bü -itch -order -▁e-mail -▁länger -▁kaum -▁Krank -▁Falle -▁untern -ulated -▁Leute -ids -ctional -anda -▁Verfassung -▁jem -▁jun -▁demands -▁voraus -▁Erk -▁resort -▁club -▁produz -▁cred -itzt -▁benutzt -▁Freiz -zielle -▁negative -▁strategic -indestens -▁empfeh -▁Modern -fälle -▁manufactur -▁Moreover -▁bemü -▁Hostels -▁Anstrengungen -▁Secondly -▁kitchen -▁Afghanistan -figur -bes -ML -▁fa -▁reag -▁Tätigkeit -▁grad -▁geleist -eater -▁Cy -▁verme -▁gek -ustausch -▁umfang -▁entgegen -zeigen -▁adjust -▁genannt -▁genutzt -▁Grie -auern -▁argument -▁Regional -▁Georg -ergestellt -werte -▁overall -▁Only -zeichen -▁moderne -▁größere -▁Russian -▁Ausschuß -▁irgend -▁annual -▁fields -▁komfort -▁changed -▁innovation --based -▁Zustimmung -▁zusätzliche -▁borders -▁Sitzung -▁claim -▁namely -▁Verpflichtungen -▁wrong -▁unbedingt -▁NATO -.3 -ST -kategorien -äg -QL -▁dipl -▁mis -▁Tsch -▁bir -elte -▁oben -▁calls -▁listen -▁Kauf -▁bezahl -hrte -▁eingehen -▁Verhalten -▁Ja -rogen -▁Stre -▁überzeugt -gruppen -▁entspricht -▁yourself -glück -▁worked -meer -wogen -▁President-in-Office -▁ethn -▁Nachfol -▁highlight -▁Mex -▁Amb -▁belieb -▁player -▁Reserv -▁representatives -▁earl -▁occur --Ver -▁Organisationen -▁flexible -▁bilden -▁Idee -▁speziell -▁season -▁observ -▁Other -back -bringen -cia --con -DE -▁bath -▁Its -▁Sea -▁Mobil -▁Besonder -▁Bade -uses -▁Cz -▁His -▁Voll -▁intended -icip -amin -stoffe -▁erkl -▁erkennen -teile -▁gegr -▁Anerkennung -▁proceed -ado -▁Schwer -▁entstehen -▁vorgelegt -spe -▁Abschluss -▁track -▁gesetzt -ändigen -▁Haft -▁evening -schließen -▁processing -▁award -▁selbstverständlich -▁Autom -▁menu -▁Roma -▁Wahlen -▁expressed -▁Profess -▁Verwaltung -▁initiatives -▁elektron -▁Kompromiss -head -nungs -bum -5-0 -▁tran -▁talking -▁hol -▁mindestens -▁People -isely -▁cell -▁cities -aries -asc -▁bege -ften -ples -endet -▁fresh -▁ability -▁spect -ensicht -▁client -▁plans -see -▁committed -▁goal -▁element -▁1990 -▁mainly -▁photo -▁accession -▁angegeben -▁heard -▁Qualitäts -▁intell -▁slow -▁occup -▁militär -▁unterschiedlichen -▁wesentlich -▁banks -verläss -▁berücksichtigen -▁teleph -▁traffic -▁Elekt -▁datab -▁Costa -.m. -hop -oke -wenden -brit -UN -▁IM -▁Sing -▁Agre -engen -inary -ils -orient -▁love -▁anc -▁alone -▁bee -▁Lab -▁stabil -▁Inf -lichung -▁gezeigt -▁Osten -ibl -äne -▁wert -▁Aussicht -gebaut -▁mehreren -hagen -▁Überwach -▁autom -▁placed -▁concentr -▁reduz -▁Bestand -▁brand -▁Boden -▁bewusst -kaufs -▁running -▁hochwert -▁Christian -▁Bemerk -▁Kriterien -▁Holz -▁danke -▁Immobil -▁verfügbar -▁Vereinbarung -▁terrace -eine -üb -üll -SS -LA -▁tit -▁nöt -▁rom -▁bre -▁Som -onym -ories -▁Körper -▁God -atisch -▁threa -ray -▁orient -eitet -ago -▁Ann -▁Anspruch -▁auszu -▁cohesion -▁nachdem -▁disapp -▁comments -▁trust -▁emer -▁hinweisen -▁Mensch -reiber --System -▁remind -▁aktuelle -▁approved -▁opening -▁improved -▁families -▁Durchführung -▁students -▁Waren -▁entsprechende -▁Garant -▁demokratischen -▁Stabilität -▁planning -ästinens -▁Hintergrund -▁administrative -je -70 -▁htt -▁watch -▁black -▁Dol -▁EG -▁glauben -▁Agenda -▁verz -▁mittel -▁Proc -ibly -undred -law -▁Mitteilung -ertung -▁Einsch -▁Architekt -▁Spa -▁Sprachen -▁plus -gebote -▁gesprochen -▁herunter -▁decide -▁justice -wirken -▁nationale -▁suitable -▁buy -gewöhn -▁globalen -▁Glück -▁Objekt -▁Lebensmittel -▁affected -ifizierung -▁feature -▁reforms -▁automatically -▁structural -▁Besucher -▁increasingly -▁Ident -▁Vorteile -▁geeignet -▁erwähnt -idelines -▁currency -hood -ras -mel -IF -ION -pack -▁Spr -▁Mio -▁Bi -▁Arab -itory -used -▁euch -▁Card -▁Fot -▁touch -quate -▁Inhalte -▁jeg -agraph -artigen -▁Schaden -▁usually -▁court -udem -▁regime -ounge -▁Reformen -▁emissions -▁controll -zeichnen -▁reduced -▁feed -▁box -ündung -▁benutzen -▁besonderen -aschine -haften -afft -▁Fischerei -▁angesprochen -▁collection -▁wahrscheinlich -bourg -▁Homepage -▁eingerichtet -▁conclusion -ecutive -▁circumstances -aw -mus -mack -bus -▁fert -▁mill -▁Tel -▁Sand -▁Av -ores -▁cos -▁cele -▁Kind -atik -▁began -▁Nahr -▁Nations -▁Oly -▁whom -▁Anp -izenz -▁engl -▁siehe -▁Ausgaben -▁compar -ampag -ologischen -entrale -▁kontroll -▁supports -ancen -▁1995 -▁Schließlich -▁phone -▁laws -▁sicherzustellen -▁confidence -▁Komfort -▁Koordin -▁Karte -▁administration -▁delivery -▁Irak -▁Partei -▁akzeptieren -▁Zwischen -▁extensive -cluded -▁Frankfurt -▁Griechen -het -daten -ruf -rid -last -▁fish -▁says -▁Serb -▁Sektor -▁pain -▁Mer -▁Brea -▁Ac -ita -inent -▁Wik -arz -▁regr -▁zug -▁Zudem -▁extend -▁entir -icks -▁meist -▁quant -ideos -▁freuen -struktur -iums -▁Haushalt -zieht -▁applic -▁möglicherweise -▁ausgew -▁powerful -offee -▁contribute -▁priorities -▁bestehenden -▁Salz -▁Datei -▁erzielt -▁wonderful -entwicklung -▁rechts -▁Zunächst -▁Zeichen -ibilität -▁unemployment -▁infrastructure -zut -▁hoffen -▁rise -▁mm -▁Sekt -▁poll -▁Most -▁Milit -▁Pot -une -▁ehr -▁lov -▁Haltung -arus -uly -enty -▁Nutzen -▁stre -▁geändert -▁Verfass -▁Stil -▁Any -▁chann -▁coordin -akes -ongs -▁Chance -▁400 -iente -▁Umst -▁else -riend -▁Unterkategorien -▁scope -▁festgelegt -position -▁Aktions -▁ensuring -▁electronic -▁Assoc -▁encourage -▁luxur -▁Wettbewerbsfähigkeit -▁wundersch -▁victims -▁Indeed -▁Konflikt -pendence -▁Czech -▁Olymp -overy -ded -öm -▁te -▁tabled -▁fellow -▁dienen -▁manner -▁IC -▁Each -▁getting -▁AN -▁bequem -▁Us -▁NI -schlagen -legt -▁Out -▁white -▁Schön -▁umgesetzt -▁ausreich -ickets -iertel -▁Vorbereit -▁comment -▁anderes -▁freien -▁Rev -▁2020 -▁crime -▁appoint -▁Schlüssel -▁larger -▁played -▁muse -▁Öl -▁Straße -▁notwendigen -▁introduced -▁finanziellen -▁participation -▁agriculture -▁erfüllt -▁zuletzt -▁replac -▁Funktionen -▁prüfen -▁specifically -▁http -▁east -lim -▁tre -▁nah -▁SP -▁po -▁Power -▁Pflicht -▁Beach -edia -eap -eme -urb -▁lösen -▁Waffen -▁Kenntnis -▁Code -▁thr -▁beziehen -scher -▁ergreifen -▁jene -allel -eckt -▁Ausbildung -▁3. -▁Konz -führer -▁screen -▁India -▁gering -▁effectively -▁Monate -▁functions -▁competitive -▁Beitritts -▁jederzeit -▁Fischere -▁Partnerschaft -▁pleasant -▁Technologien -äuser -▁Suche -▁sources -▁Dateien -▁animals -▁Klein -▁zufrieden -▁Kommunikation -▁platform -hy -nes -book -pp -▁hundred -▁fahren -▁rural -▁sitting -▁Bec -enhagen -inian -▁ec -▁Gemein -▁formal -▁reception -trägen -ugg -▁ship -▁Schengen -▁church -▁iP -▁unters -schlossen -▁Aufnahme -chtet -▁served -▁Asia -▁protected -▁serve -▁gleiche -▁Richtlinien -▁Kindern -▁Vielfalt -▁referred -▁installation -▁critical -▁braucht -▁aimed -prüfung -▁motion -▁purposes -▁verbessert -▁schwierig -▁Paul -▁According -▁wünschen -▁detailed -▁exactly -▁vermeiden -britann -▁é -.S -ael -▁heav -▁dead -▁mix -▁Tro -▁Meter -▁camera -▁lesen -ickt -aria -amber -▁Infrastruktur -ifying -odies -▁ausger -idents -echt -▁Einrichtungen -▁Arten -iano -▁trav -▁darunter -fahrens -werke -▁auth -▁wants -urses -▁Why -▁Produkten -sprü -losen -▁Board -▁sicherlich -▁represents -▁discussed -mitglied -▁stattfinden -▁Zweck -preis -▁Beschäftigungs -▁institutional -▁February -▁Luxem -▁becoming -▁spacious -▁CO2 -itzerland -▁risks -ham -nam -mission -TA -RA -▁fäl -▁ries -▁süd -▁moral -▁Tun -▁Sinn -▁Middle -icts -ulierung -schreit -▁stated -▁ständig -ingu -iffer -eget -▁Then -▁Schiff -▁entschieden -▁spend -ensions -▁managed -▁police -▁Arch -▁Spec -▁competen -▁developments -▁sub-c -eather -▁angezeigt -▁gemüt -▁Standards -▁facilit -▁discussions -▁precisely -▁temper -▁domin -▁Slov -▁Apartments -▁jeweils -▁Innovation -▁likely -▁Wellness -uisine -▁entweder -▁Agreement -wagen -▁fallen -▁vac -▁mid -▁mand -▁metres -▁Sehens -▁gern -▁Ky -▁Heil -▁Rein -▁Get -▁inn -atively -▁alte -▁1, -osed -▁vert -egu -ellte -▁shower -▁Wege -▁otherwise -▁Lehr -▁sold -▁Menge -werten -▁offensicht -▁selected -▁inside -▁gewün -▁remark -▁gelangen -▁01 -▁Research -▁Policy -▁budgetary -▁Switzerland -ING -▁Turk -▁programs -▁Großbritann -▁radio -▁erzielen -▁übernachten -▁komplett -freiheit -▁mittler -▁convin -▁thousands -ral -pon -kap -gers -gelt -▁mil -▁winter -▁ging -ela -inu -chmal -▁centr -▁Wass -▁Found -ida -rekt -rahmen -▁ergän -▁Stimme -▁entry -idential -▁Abschließend -▁actual -▁provider -▁Frem -▁hands -▁Grundsatz -▁wood -▁expand -▁expected -bruch -▁medical -▁Appart -▁measure -▁Rooms -▁votes -▁teilweise -▁listed -▁Bush -▁closely -▁Verwaltungs -▁covered -▁ISO -▁erhöhen -▁corresp -▁Februar -▁assessment -▁Forderung -gelegten -▁sub-categories -sam -wes -▁rent -▁sagt -▁wären -▁Sow -▁EAD -▁paper -▁Pack -▁Prag -itiveness -umm -▁impress -leisch -▁stimme -▁erf -▁erstellt -trol -▁gekommen -▁Jetzt -▁-- -▁abgel -▁chines -▁ausdrücklich -▁Arbeiten -▁Def -▁welcher -▁guidelines -▁technische -▁dram -▁seek -▁debt -▁eingel -▁Click -▁besuchen -▁Vereinten -▁break -▁kommenden -gemeine -ikanische -▁color -▁introduction -▁stärk -▁Office -▁followed -▁Klimawand -▁Status -▁vorhanden -▁appreci -▁earlier -ül -▁sauber -▁mic -▁Top -▁Mir -▁graph -omin -union -▁Wien -▁Kol -iefer -imb -ania -ala -▁anbiet -▁Name -schuld -▁Angr -▁abst -laus -▁spread -▁Ern -▁Mitte -▁betracht -▁betonen -problem -ishes -▁save -▁Leider -▁Berichte -▁Plen -▁Entwurf -▁Bereit -▁beschlossen -atoren -▁opened -▁improving --called -▁buildings -▁territor -▁informieren -▁electric -▁multil -▁enterprises -▁Leistungen -quality -▁komplex -▁funktioniert -ength -▁wollte -,00 -rum -vements -bad -SP -▁fragen -▁ade -▁breit -▁Banken -eters -itter -▁lernen -▁Kal -▁Kurz -anlagen -techn -▁verur -▁Unabhäng -▁Street -▁abge -▁uses -▁36 -▁leist -▁trip -▁beds -▁Orig -▁supporting -▁handeln -▁Teile -azz -▁operations -▁considerable -▁Belarus -▁linked -▁Warum -▁networks -▁understanding -▁Abgeordnete -leitung -▁Instrumente -▁Webseite -▁Währungs -▁exclusive -▁Check -▁Greece -▁Veränderungen -▁Botschaft -▁Entsp -▁Initiativen -▁telephone -hel -fachen -ünen -möglich -▁tele -▁rob -▁map -▁mobil -▁went -▁Thr -▁bott -▁Eisen -▁Mö -▁Mun -ora -olden -▁ober -▁cuisine -▁Wo -▁Hilf -▁GN -stechn -▁Verständ -▁Station -ield -▁dagegen -▁so-called -▁Wirk -▁Absicht -▁compens -bek -projekt -▁darstellen -oyal -▁provision -▁Zeitraum -achtung -▁criminal -istrib -▁zweif -▁marketing -▁Systeme -gewicht -▁Vielen -▁immigration -▁gewährleist -▁Engagement -▁convenient -▁Economic -▁degree -vo -IE -IG -DP -künd -▁russ -▁Maschinen -▁gain -▁gave -▁Band -▁Bos -iebe -▁Lib -▁Nah -▁Never -▁erleben -gencies -▁verring -▁gefordert -▁Angaben -▁'ll -▁resist -ünden -▁durchaus -ärm -gerungen -▁accomp -ießt -▁Zeiten -öffnen -▁Bruss --Mit -▁wenigen -▁Franz -▁brief -▁acqu -▁playing -▁founded -▁presence -▁Begriff -▁stärken -▁Pakistan -▁konkrete -▁properly -ühlen -▁veröffentlicht -▁Gipfel -▁components -▁e.g. -▁PHP -öp -13 -grad -▁match -▁Tool -▁EL -▁Po -▁Bay -▁Black -ura -▁Harmon -▁Fonds -▁Vill -▁unternehmen -aters -umin -schung -▁items -issionen -grammen -▁Schrift -▁entit -ennis -▁door -▁Absatz -▁193 -▁freue -▁Mitgliedstaat -istence -▁delay -▁glob -▁Hauses -▁klare -▁experts -▁Standort -▁typical -▁Phil -▁explo -▁Guest -▁tourism -▁destro -▁Ratsvors -▁Kritik -▁animal -▁Währung -▁purchase -▁Installation -▁erklären -▁Mio. -▁gewährleistet -house -ooms -set -EA -▁hält -▁vere -▁Te -▁coffee -▁kr -▁kor -▁Health -umän -ulate -▁wirks -▁Anteil -▁alloc -▁entscheid -iziert -▁coordination -aces -▁plants -örde -▁Geist -▁trägt -▁hinzuf -▁liebe -▁strict -▁Klicken -▁eingesch -▁beru -▁competitiveness -▁presidency -▁Worte -▁generally -▁behav -▁Grenz -▁benötigt -▁Herausforderung -▁Casino -▁2011 -▁lokalen -▁fisheries -▁duty -▁permanent -▁Schlaf -▁entirely -▁Because -▁Sehenswürd -▁EADS -▁Brussels -ua -so -mas -wesen -wasser -lad -▁tack -▁hosp -▁sorg -▁Dienste -▁Mail -▁Mount -▁Bern -▁Great -▁unn -▁erfolgen -▁Anges -▁shops -▁willing -ova -okraten -▁mail -hnung -club -flanzen -▁relate -▁indic -führten -▁Brüssel -▁Herstellung -▁klicken -▁möglichst -▁Poland -konferenz -▁refuge -▁authority -▁dealing -▁Gebieten -▁difficulties -ifiziert -▁peaceful -▁Sonnen -▁Regelung -▁Kompon -▁Netzwerk -qualität -▁Prioritäten -▁Königreich -▁erscheint -▁Immobilien -ückt -▁af -▁Titel -▁psych -▁Bran -itze -▁Kenn -▁Fen -ameter -▁Non -ortion -▁erstellen -try -hrase -vey -▁Unfortunately -▁OS -plant -uba -▁früh -▁attra -▁Environment -eals -▁gesp -attle -▁Übergang -▁Allgemein -▁Hafen -▁einzeln -▁format --Pl -▁klass -overs -▁dennoch -leistungs -▁facing -▁organisation -▁Madrid -▁combat -▁nächste -▁district -▁Zweifel -▁summit -▁finanzielle -▁recognis -▁Jahrhunderts -▁Dennoch -▁Stellungnahme -▁konzentrieren -▁Nachfolgend -work --20 -90 -▁täglich -▁hour -▁average -▁sand -▁MP -▁Mechan -▁PT -▁AK -▁Achtung -▁zerst -▁kost -▁Wander -asten -▁forget -reu -▁Int -denken -▁constit -▁jur -ranken -▁Einigung -▁Einhaltung -clude -▁Aufbau -▁percent -▁gegenseit -▁Asyl -▁einzus -▁paragraph -▁applies -▁Freund -▁Gewähr -▁meetings -▁Terrasse -▁speaking -▁machines -▁persönlich -▁unterschiedliche -▁Super -▁calc --star -▁Forderungen -▁vehicles -▁Völker -▁Entschließungsantrag -▁gegründet -▁Schwerpunkt -mär -cement -äten -▁tut -▁suc -▁PDF -oring -oman -▁letzt -aste -asts -umn -▁verfolgen -elled -▁exec -▁übertragen -odes -▁registr -▁legen -▁Formen -▁contem -▁fire -▁Unterkunft -haber -▁Flücht -▁Landwir -▁Atlant -▁numbers -▁thinking -▁Versuch -▁consideration -▁imposs -gemäß -yright -▁spielt -▁umfasst -▁Kirche -▁declar -iques -kräfte -▁Santa -▁becomes -▁Analyse -▁amerikanischen -▁fixed -▁befürwort -▁geleistet -▁Breakfast -▁Großbritannien -▁betrachtet -IR -MU -▁told -▁nennen -▁sym -▁born -▁ST -▁Sach -ema -inc -isierten -▁ep -▁Wicht -▁Fore -▁Vien -enteil -schul -▁denken -inding -▁schönen -peratur -▁Forum -steuer -▁Jahrz -▁income -▁endgült -▁Once -▁Barros -licherweise -▁medic -▁verlassen -äufer -▁functional -zeption -▁practices -tworten --Hotel -▁flexibility -▁Private -verkehrs -▁Paket -▁Motor -▁opposition -▁picture -▁unabhängig -▁diskutieren -▁Gemeinsamen -▁Texte -▁Sehenswürdigkeiten -▁succeed -d. -ress -zesse --y -karten -▁sn -▁bodies -▁Dorf -orfen -▁Wün -▁Katastrop -▁CH -▁Hard -▁Gut -arch -umen -▁imag -lying -▁concr -ibel -iking -▁3D -▁lift -▁superv -▁Umfang -▁myself -▁overl -arbeitet -▁Hersteller -▁feststellen -▁signal -▁Stein -▁advanced -▁hören -▁begrenz -ashion -▁erforderlichen -▁arbeitet -▁aims -▁load -▁domestic -▁Ideen -▁positiv -▁Rechnungs -▁Lesung -▁garantiert -▁Ruhe -▁Anwendungen -▁database -▁Nahrungs -meld -letter -komp -▁sle -▁middle -▁Typ -▁Sof -▁Sorge -▁Mel -▁Alex -▁Wall -▁Hall -arischen -ams -▁Note -reisen -▁stable -▁erleicht -upp -cknow -▁geograph -illing -arta -▁sob -▁Auss -▁Argument -▁propose -▁politics -▁Sehr -▁signed -zeugung -▁approxim -▁Polen -▁easier -▁Vielzahl -▁defined -▁himself -▁Gleichzeitig -▁Charakter -preise -▁Kompeten -esterday -▁Links -▁Hamburg -embly -▁Stärkung -▁überprüfen -▁ancient -▁Association -▁ausreichend -.4 -viel -üter -band -St -loss -▁frag -▁Sur -▁Saint -▁Mindest -▁Werb -atischen -ürz -▁NG -stimmung -▁secr -▁Ze -▁einiger -▁Under -plätzen -ellos -▁clar -▁33 -▁192 -älter -▁eth -▁hingewiesen -▁Seine -▁Entwicklungen -▁75 -▁1994 -krieg -▁Arbeitslosigkeit -▁financing -▁Resort -▁richtigen -▁informed -▁patent -▁seriously -▁Patienten -▁failure -▁establishment -▁translation -▁Rechnung -▁parliamentary -▁kulturellen -▁experienced -stattung -▁initial --Mitglied -.e -tes -no -seh -IB -öder -äck -▁hop -▁acknow -▁Digital -▁Event -▁Main -▁Künstler -▁Finn -▁una -▁threat -▁bezeichnet -quar -▁zuvor -▁erg -▁miteinander -road -▁cooper -▁Ausfl -accept -aken -▁48 -ologische -▁anyone -gestimmt -▁Defin -▁Landschaft -▁Bedarf -▁Verbrauch -kret -▁differences -▁estate -▁models -▁studies -▁connected -▁Afrika -▁promoting -▁Risiko -▁profitieren -▁dangerous -lösung -▁voice -▁tätig -▁Heimat -▁celebr -wan -wurf -▁spl -▁Dollar -eto -▁unz -icians -ichert -strateg -▁Inc -ista -herr -▁defence -▁Over -ehl -▁withdra -schau -▁19. -▁indir -▁weiterer -▁positions -▁difference -▁humanitarian -▁verloren -▁Polize -versorgung -▁combination -▁zahlen -▁organiz -▁booking -▁beaches -▁Tourismus -▁Irish -▁Irland -▁structures -▁eingeführt -▁Support -▁appears -▁Leistung -▁Leistungs -▁reservation -▁happened -jährigen -ibrary -zwischen -▁vernünft -▁neighbour -▁Kingdom -▁associated -▁Bedürfnisse -aill -üst -wel -pak -CO -▁names -▁grenz -igem -omas -▁length -▁HT -▁unaccept -▁forest -regel -▁hair -▁aufmerksam -▁Verst -terne -weist -▁courses -▁trag -▁accur -▁emb -führungen -▁gewinnen -haltet -▁Problemen -▁Teilnehmer --Pr -▁instance -▁Beschreibung -▁Trade -▁article -▁implementing -kontr -▁statements -▁carefully -▁colour -▁England -▁beginnt -▁Firstly --Programm -▁attractive -▁Effekt -▁houses -organisation -▁Altstadt -▁exhibition -▁safegu -▁impossible -ny -fu -gu -▁agencies -▁satell -▁Seg -▁Due -▁Ban -etic -etragen -instrument -▁zentrale -▁Fotos -iche -aby -▁einsetzen -▁condem -▁Os -▁überall -▁shared -ungssystem -▁enth -▁ausführ -▁spent -▁vorbereit -ektion -▁Abs -▁relativ -▁geschehen -▁reducing -▁leader -coming -ohlen -▁unterstütze -▁Beitritt -▁arrival -▁collabor -▁Geschäftsordnung -▁Diskrimin -▁üb -▁Justiz -▁immediate -▁danach -▁announced -▁architecture -▁producers -▁Überwachung -▁Ratsvorsitz -▁Angesichts -▁concrete -ión -uber -uces --3 -11 -▁Poker -▁Are -▁Foto -▁Rand -▁isol -▁unto -iegel -atically -▁Live -tenance -▁einst -▁Jeder -▁Chancen -▁truly -▁evidence -nahm -stitut -▁USB -▁geschützt -ategory -▁calling -▁charged -▁besitzt -▁Meiner -version -▁Deutsche -▁adoption -▁Technik -▁regionalen -▁reasonable -▁guarantees -bieter -andidaten -▁gegenwärtig -known -▁Priorität -▁vorliegenden -▁Michael -▁bestätigt -▁keinerlei -▁sämtliche -▁released -▁Griechenland -▁television -▁Barroso -▁• -where -II -EO -ée -▁näher -▁fle -▁ahead -▁mist -▁Dus -▁Drogen -▁cards -▁Wi -▁Fel -asil -ulations -▁Nav -▁haupts -▁verfolgt -▁Prime -▁excess -präsent -▁chair -▁ausges -eilt -▁politischer -▁großes -▁delight -▁spezielle -▁availability -▁background -▁vollkommen -▁einfache -▁Mittelmeer -▁advance -leihen -▁Festival -▁Zuständ -▁taste -▁Justice -▁Fortschritt -▁relaxing -▁recognition -▁weak -▁Elemente -▁described -▁Europäer -▁genuine -▁Foundation -▁grenzüber -räge -zig -Script --de -99 -▁roll -▁wachs -▁Mik -▁Put -▁Bis -▁Back -ili -onia -▁Custom -▁Gent -ando -rochen -ugos -▁Anlagen -▁entertain -schläge -▁dispos -▁possibilities -▁gestalten -▁monetary -▁owners -▁größeren -▁Media -mögen -▁improvement -▁160 -▁Bewertung -▁wishes -sequent -▁nearby -▁membership -wachs -▁ök -▁urban -▁Verpflichtung -rüst -iscal -▁beteiligt -▁französischen -▁Mutter -▁discrimination -▁Gerichtshof -▁photograph -▁angeboten -▁offensichtlich -öss -AG -LE -▁Syn -▁pour -olz -▁ow -▁kons -▁Wand -▁Colle -▁unf -▁unless -▁prä -▁Lo -▁Lam -steht -ibet -▁Erhö -range -heits -▁trading -örter -▁warten -erma -▁Commun -▁glass -▁Kräft -▁cars -▁gutes -▁Indien -▁continues -▁11. -▁territory -▁skills -box -▁consumption -▁Worten -▁African -▁absolute -▁tasks -▁Regelungen -▁zusätzlich -▁continued -▁garantieren -▁Globalisierung -▁figures -windigkeit -▁empfehlen -.S. -▁unacceptable -lig -▁Tisch -▁Eindruck -enkt -otten -chem -▁Wald -▁unfortunately -▁ergeben -▁einzu -▁Very -roh -▁desire -▁allowing -ador -▁dauer -▁sogen -ensus -▁enables -icking -▁34 -▁37 -▁grunds -▁gratul -▁generation -▁internationaler -▁expend -▁Maße -▁greatest -▁eingere -▁attach -▁folgende -▁Urs -▁explain -▁combined -greif -▁packages -▁raise -▁Universität -▁introduce -▁participate -▁completed -▁strongly -▁devices -▁höchst -▁surrounding -▁umfassende -▁gegenwärtigen -gypt -▁beinhaltet -zz -wissen -bett -brauch -body -can -lement -öse -ßt -äfen -▁dur -▁vul -▁SD -▁Data -▁Mond -ilst -▁liter -▁Korr -▁Karten -▁Ren -▁Nether -ido -reens -verstanden -▁abgeschlossen -▁spät -▁Europeans -▁Größe -▁command -▁solcher -▁transition -▁qualit -▁Monat --sized -▁Direkt -▁beauty -▁verbundenen -▁Märkte -▁window -▁attractions -▁religious -▁burden -▁Vorsitzen -ausschusses -▁registered -▁verabschied -▁romant -riendly -▁Turkish -▁Vienna -ih -wie -▁harm -▁river -▁mission -▁Il -▁Dut -itage -itious -irus -ilot -olge -▁Ca -▁Hist -▁Fac -▁Rome -▁ung -ammer -ürk -▁Lake -achsen -▁shop -allery -▁enor -ource -▁Bek -▁button -▁gramm -▁gross -linge -▁insurance -▁economies -▁appeal -▁Gebrauch -▁surface -▁teilnehmen -▁closer -▁meters -▁steigen -▁Nutzer -▁Gründe -▁Todes -▁wesentlichen -▁Meeres -▁Erklärungen -▁Democrats -▁Beteiligung -▁müs -▁dedicated -▁entitled --J -▁tief -▁dient -▁acht -▁sale -▁square -onic -▁ca -▁Fitness -iech -iest -▁ther -▁beglück -▁Lizenz -▁stim -istisch -istik -▁asyl -▁aufzu -▁vergleich -▁geöffnet -▁Angel -ockt -▁fruit -▁Erde -▁manage -▁betrachten -▁Grunde -▁4. -älfte -▁outd -attform -ancing -stands -▁ausgewogen -partner -enntnisse -▁Gegensatz -hebung -▁learning -▁Relig -▁erhöht -▁speziellen -klusive -▁exceptional -▁Federal -▁Städte -▁Bahnhof -▁enormous -▁Unabhängigkeit -netz -sicherheit -yt -government -ía -▁fern -▁dre -▁session -▁vision -▁buffet -▁Book -▁AR -▁langen -▁Kurs -▁Ganz -▁Good -▁union -asen -ases -leun -ifest -uchten -▁Verz -▁Anre -▁Another -ikal -▁Erhol -atever -▁Beim -▁38 -▁compared -▁reliable -▁kept -mittelt -▁gelungen -vorschlag -▁meant -▁para -▁perspective -▁installed -▁Studio -▁totally -▁Austrian -▁luxury -▁railway -▁Kohäs -▁Flexibilität -▁exercise -▁beglückwün -▁asylum -gal -▁heat -▁Day -▁gemeinschaft -esign -isierte -▁forced -▁behin -▁Ul -▁Lag -ktiv -▁erfre -▁Jac -ello -▁extended -▁spending -▁Einwohn -spart -▁trad -▁daraus --Sch --Sterne -▁Front -▁Nachdem -▁begleit -▁stands -▁humanitä -▁Hinweis -▁Aktionen -▁earth -▁informiert -▁individuals -Innen -▁Market -▁Marken -▁Weitere -▁biggest -▁holidays -▁schöne -▁claims -▁starke -▁storage -▁Kopf -▁britischen -▁Ereignisse -▁advertising -ibliot -▁Militär -▁Verständnis -▁Nevertheless -.de -hold -km -▁diss -▁sides -▁Pool -▁PPE -▁BM -▁lauf -▁Wür -▁Head -▁Rob -▁Van -iced -▁story -▁Vermitt -▁abzu -▁manchmal -▁Gefühl -▁Reich -▁emerg -iode -▁Strom -▁98 -▁science -▁Control -▁guter -▁Aktion -▁Datenschutz -▁250 -▁intelle -▁Company -▁corporate -▁Studien -▁motiv -▁device -▁Farben -▁Slow -▁failed -▁mountains -▁Martin -▁granted -▁Empfehlungen -▁urgent -▁memory -▁Süden -▁diplomat -▁convinced -▁Foreign -▁Netherlands -lag -person -LAN -▁hum -▁nieder -▁divid -▁veget -▁golf -ompl -▁core -▁Fert -▁Niveau -benen -lichem -▁verf -▁eingeb -auso -▁Ausland -▁Wirkung -▁Fraktionen -ittel -▁5. -▁elimin -▁offic -▁20. -klang -▁benach -▁advice -▁Quest -▁Palace -▁passed -▁controls -▁moving -▁beachten -▁Kontroll -▁magn -▁Realität -▁Wiederauf -▁conclusions -▁Mitteln -▁Latin -▁legitim -▁Agentur -▁Eastern -▁Hoffnung -▁intelligent -▁sorgfält --year -▁hauptsächlich -ibliothe -oe -det -bil -▁fiscal -▁rasch -▁DS -elbe -▁Croat -▁Feld -▁unit -▁unable -icious -▁prison -▁Institute -▁weight -▁Press -▁gehalten -▁einverstanden -▁Anlage -▁allgemeine -▁Third -▁Schm -▁enfor -▁canc -▁finance -▁disease -▁Regulation -▁treten -▁saw -▁herzlich -▁guide -▁contrary -▁geschieht -reitet -reitung -▁außerord --Kon -▁establishing -▁definit -▁Zahlen -▁Betriebs -▁ideale -▁perfectly -▁ECU -▁mittels -knü -▁Greek -▁Beschluss -▁Gedanken -▁Special -bas -ließen -▁Treffen -▁Dimension -▁Mas -onial -oram -▁kaufen -▁Kaff -▁Vom -ufe -gehör -▁forth -idung -▁Verkauf -▁jour -▁Journal -▁überein -▁chosen -laufen -▁ausländ -▁'re -active -▁möglichen -▁tried -▁interface --Str -▁damals -▁Haut -▁einzigen -▁scale -▁factors -▁Constitution -▁festgestellt -▁Blog -▁Programms -athed -▁Beihilfen -▁artists -▁schneller -ädt -▁definition -▁Hauptstadt -▁drawn -▁MyS -▁Voraussetzungen -▁Risiken -▁niemand -▁jüngsten -▁sobald -ries -s- -TS -▁türk -▁Peter -▁Bill -▁Kreat -▁Grad -▁unc -anische -rap -vertr -▁neither -▁verk -▁contra -▁jungen -▁Jean -▁Stad -endum -▁abhäng -▁Thus -enswert -▁quar -▁42 -▁Yet -▁laid -▁selten -wirk -▁1992 -▁rightly -▁Teilen -▁maintenance -▁somew -▁blog -▁Gebiete -▁erfordert -▁expression -▁balcon -▁Garden -▁commitments -▁vorgeschlagenen -▁weapons -going -▁spricht -▁route -▁Erwart -▁honourable -▁Gegenteil -▁unternommen -▁destroy -dukt -jet -án -▁agency -▁sage -▁win -erver -olo -▁Rumän -▁Gaz -anal -uftrag -▁Los -▁Inner -▁conver -odurch -uble -größ -▁fried -▁umwelt -▁spa -▁sched -▁Beob -▁update -▁outcome -▁outstanding -▁intervention -▁Mitgliedern -▁Grundrechte -▁photos -▁Arbeitsmarkt -▁vari -▁revolution -▁Phase -▁achieving -▁defend -▁sensitive -ategorie -igrants -▁Marketing -▁persönliche -▁übernommen -▁ambitious -gefähr -▁Männer -▁schlecht -▁chemical -▁regelmäßig -▁candidate -▁condemn -acc -zed -wachst -bea -bring -▁sell -ismen -isierungs -urm -▁Nik -reck -stät -quir -apers -▁Presse -▁Arm -▁Sprach -▁vieler -▁rock -▁gewiss -akte -ventions -▁belong -ancial -▁nationaler -▁appar -ählten -▁flight -▁aircraft -▁investig -▁Politiker --Reg -▁Zugriff -▁teilen -ränken -▁barr -▁zunehm -programme -▁Klimaanlage -▁Welche -▁surrounded -▁colleague -▁nachhaltige -▁verstärkt -▁obviously -▁constantly -▁Bestandteil -▁anbieten -▁approximately -.e. -iov -oul -rant -ügel -bindung -MS -▁wodurch -▁Tele -▁Sun -▁Dort -▁Ep -▁EP -▁Az -eable -▁category -▁Getr -▁unan -alia -▁beob -▁yesterday -▁zugleich -endung -elles -▁vorschlagen -▁Alter -▁Alban -▁compat -▁Ref -▁election -▁Mitglieds -▁someone -▁Teilnahme -▁accessible -▁Manager -▁medizin -▁kleiner -▁organised -sequently -▁limits -▁Nichtraucher -▁massive -▁continuing -▁Option -▁promotion -▁obligations -gesellschaften -▁vehicle -▁betroffenen -▁THE -▁correspond -▁dig -▁Ev -▁Ebenso -▁Bol -▁zählen -▁Hälfte -ancy -cepts -abilities -ipe -▁Wechsel -anger -ausende -▁Beif -▁goals -▁Chem -▁Registr -▁Reis -▁Refer -utzung -▁contents -▁elected -▁gewählt -▁berecht -handels -▁minute -▁Verfüg -▁successfully -▁payments -▁Komb -▁traditionellen -▁distin -▁Karl -▁Touristen -▁situations -wegs -▁equality -▁praktisch -▁Effizienz -▁Located -▁Prüfung -▁beschäftigt -bürger -▁Liberalisierung -▁title -▁gewünsch -▁satellite -ermaßen -governmental -▁zunehmend -räume -▁h. -▁hergestellt -▁race -▁wealth -▁Syr -▁Square -▁por -▁Agency -etern -eshalb -▁Win -▁Kat -▁CS -▁Fax -▁Royal -▁instruct -angebot -▁Lim -▁Lord -▁store -▁asking -▁sichern -annung -▁cool -▁ide -▁Report -▁gesellschaft -ifications -▁Gesicht -▁surv -▁bound -▁deutsche -▁erneuer -▁Internationalen -ränkungen -▁organization -fähig -▁Liebe -▁21. -▁balanced -▁recommendations -▁Prakt -▁Führung -▁besseren -▁Details -▁physical -▁Experten -▁Erhöhung -▁expenditure -TP -▁rig -▁rot -▁Merk -▁Messe -▁Morgen -▁Pop -▁Publik -oret -urr -▁kilom -▁Ros -▁Via -deck -ario -ques -▁Zimmern -apter -uropa -▁Old -▁Streit -prinzip -▁Schne -anging -▁i.e. -▁amt -sprach -▁andererseits -struction -▁lake -▁Überprüfung -istenz -▁800 -▁wichtigste -▁Island -▁Glas -▁independence -▁Straf -▁write -▁changing -▁Norden -straße -▁Ferienwohn -▁self- -▁diskutiert -▁furnished -▁medium-sized -▁JavaScript -▁criticism -▁Koordinierung -hann -nd -oß -vern -24 -▁hil -▁bars -▁Eth -▁pun -▁MEP -era -aschen -▁Those -▁Enjoy -▁Weiß -obility -▁Ausnahme -eases -▁Aufgrund -▁vielfält -▁acts -▁consists -▁weiteres -▁Verbesser -selben -richtlinie -▁begann -▁Akte -▁Autor -▁Stadtzentrum -▁Admin -▁Eltern -▁Urspr -▁irgende -▁Gästen -▁participants -▁III -▁Apartment -▁targets -▁waiting -zwungen -▁merely -▁Veranstaltungen -▁Laufe -▁Summit -▁Methode -▁kürzlich -▁fällt -▁contemporary -▁grenzüberschreit -äut -▁dimension -▁ID -▁Both -▁A3 -▁cer -▁RO -▁Rund -▁unl -▁Nu -▁seven -▁depos -▁webs -▁gee -▁Jose -▁willkommen -ahrt -▁sieben -▁finde -▁Maria -▁kontext -▁respekt -▁Übern -▁species -enzial -ablen -ansion -▁glad -▁prevention -▁aktual -▁smaller -▁seitens -▁Energy -▁putting -▁preserv -▁communities -▁slight -▁labor -gänglich -▁Inseln --200 -▁neighb -▁Methoden -▁Palestinian -▁landscape -▁jemand -▁junge -▁globale -▁einzelne -▁Diskriminierung -hat -iam -üben -TE -15 -ér -▁rac -▁Tit -▁Sä -uting -▁Cult -▁Rem -▁Rules -▁Graf -umer -▁anz -▁re- -▁reprod -▁Zoll -hrt -▁Je -▁Anliegen -wechs -▁Einkaufs -▁count -gendes -▁factor -erstellen -▁bringing --Bahn -▁Autob -▁Class -▁print -▁Formular -▁leads -▁guaranteed -▁optimal -gehoben -▁demokratische -▁contained -▁akzeptiert -produktion -▁inspir -▁prospect -▁fraud -▁sinnvoll -▁reagieren -▁Original -▁Bosn -▁disposal -zil -zip -wers -▁toler -▁nav -▁wall -▁Tiere -▁bür -▁Mat -▁Pflanzen -uters -▁echte -▁Foll -geht -▁stren -▁Zuf -▁Zul -tei -trib -▁Veröffent -▁Wegen -▁Berücksicht -▁registration -▁grounds -owned -▁assign -▁pointed -▁120 -▁capt -▁password -äumt -▁Sweden -▁acceptable --making -▁nearly -▁Begr -▁zunehmen -▁Willen -▁Korea -▁Thanks -▁planned -▁Rotary -organisationen -vereign -▁anerkannt -▁ländlichen -▁substantial -▁Vorbereitung -▁Angriff -▁hospital -▁dritten -▁Ton -▁Dubl -▁pill -▁MS -▁AB -uste -entially -rep -▁verteid -arth -▁School -▁cheap -▁sovereign -etzungen -▁cook -asting -▁betont -bef -▁griech -▁genü -▁190 -▁nochmals -▁freie -▁600 -mittag -▁Alliance -▁seeking -▁valu -▁brings -▁angeht -▁operate -▁responsibilities -▁resulting -▁blue -management -▁Gesetzgebung -▁Zustand -▁Subs -▁manufacturing -▁causes -▁behandeln -▁examples -▁positiven -▁dynamic -grade -▁Fähigkeit -▁Ordnung -▁Anpassung -▁Fenster -pass -CD -▁fuel -▁band -▁Sein -emen -isure -iler -▁lag -▁Kilom -▁River -▁GM -imal -▁bewegen -ctual -▁diesbe -▁conclude -▁Verhältnis -▁sicherstellen -▁Stunde -anne -▁umgeben -▁Chamber -▁trend -▁recovery -▁konstrukt -inning -▁Flash -▁USS -▁assum -▁1993 -▁begonnen -▁führenden -▁surpr -▁capable -▁äuß -▁engag -▁letter -▁pleasure -▁Patent -▁testing -▁strengthening -▁Reaktion -▁berühmten -▁Unterschiede -ampagne -▁Revolution -▁Klimawandel -▁withdraw -▁Dutch -rom -sein -moking -gment -64 -▁fashion -▁mang -▁Sud -▁Pin -erber -otte -orough -unsten -▁zone -▁Fischer -▁Rout -entscheid -▁Nice -ahnen -▁Theater -zeß -▁Einklang -cling -▁emp -▁decades -▁scen -▁Where -▁formul -▁angep -zahlung -▁governance -▁Freunde -▁verlieren -▁Obama -▁multiple -ipping -▁Englisch -▁identity -▁Charter -▁Fälle -▁association --how -▁spaces -lebnis -▁Minderheiten -▁Freizeit -▁Cypr -▁Architektur -▁Umständen -▁secret -vant -▁hilf -▁fand -▁fift -▁dé -▁rul -▁sets -▁Two -▁Dadurch -▁Adresse -etch -itar -igern -iern -ality -▁fordert -ktop -▁stri -▁zuverläss -▁weg -▁notice -▁Stück -▁überzeugen -▁attend -▁Argent -▁leiden -▁vielmehr -▁55 -ettable -utzer -▁consensus -fried -▁Fluss -▁Financial -▁driving -▁versions -▁parallel -▁philosoph -▁books -▁patients -▁starken -▁previously -▁copy -▁primary -▁southern -▁Perspekt -▁authent -▁adequate -▁safeguard -▁Verfügbarkeit -h. --4 -kunden -gas -▁aft -▁Bla -chnet -▁ziem -▁HD -▁beig -▁U.S. -▁Lö -lechter -▁ergriffen -grenzen -fügt -iertes -▁truth -folgerungen -stellungs -▁bedarf -▁Bedenken -▁Prodi -▁remove -▁Express -▁White -▁Handeln -▁expensive -▁Kollege -▁beschrieben -zahlt -▁Freude -▁industries -grundlage -▁Law -▁discussing -▁addressed -▁steel -▁Grenze -fertigt -▁requested -▁derzeitigen -▁zustimmen -▁Diskussionen -▁primarily -▁Romania -▁ehemaligen -▁incorpor -▁vulner -▁Kombination -hops -nach -wart -bu --of -▁nations -▁mother -▁master -▁bildet -iny -oles -▁läuft -▁WC -▁Kunde -▁Hor -▁inhab -ierend -atlant -▁styl -▁seien -▁Angebote -▁vorgenommen -oster -▁thereby -▁liability -▁Champ -aving -▁gewisse -▁Export -pielt -▁wiederum -▁Schloss -▁befassen -orgnis -▁representative -▁explan -▁kinds -▁Gesetze -▁unterschiedlich -▁höchste -.200 -▁schools -▁attacks -nehmern -▁Trek -▁talks -▁München -▁nötig -▁afford -▁Berücksichtigung -.org -mon -ky -▁sem -▁sod -▁scr -▁bon -▁Satell -▁Elect -▁pet -▁Pic -▁eur -▁Find -▁therm -▁alter -▁Präs -issens -▁Ander -▁ausgel -▁doct -▁gründ -▁consolid -▁Menü -bsicht -▁welchen -aire -▁orders -▁phase -▁beruf -▁Amerika -▁Vielleicht -▁earn -▁schließen -▁Dienstleistungs -▁default -bereiche -▁extreme -▁Mehrwert -▁turned -▁Komment -▁landwirtschaft -▁Antworten -▁konkreten -▁Voraussetzung -▁audio -▁Eigenschaften -▁reinfor -▁Johann -▁therap -▁abhängig -▁\ -▁ält -reten -afr -TER -ão -▁nation -▁disch -▁voc -▁Mach -▁Master -▁PR -▁Bearbeit -▁eight -▁zeich -▁cm -▁1000 -▁Prop -▁gepr -▁journal -▁Jes -ikas -iene -▁Schäden -▁umfass -fül -▁39 -▁betreffen -▁genauso -formationen -▁politisch -igerung -▁struktur -▁basiert -▁Expl -▁keines -▁Kompl -▁prepare -▁1980 -▁delivered -▁Dokumente -▁opposite -▁named -jährige -▁diverse -▁Untersuchung -▁evaluation -▁territorial -▁Allgemeinen -strategie -ey -fers -meth -bal -let -kop -öd -25 -Os -▁waters -▁AD -▁Cam -▁Hans -▁Ram -geiz -station -stoffen -▁reply -▁wirksam -verw -verse -▁deiner -▁Untern -enses -rance -▁grand -▁Aufsicht -ährt -▁Church -▁emot -▁noise -▁Grundsätze -▁Sound -▁gestern -▁Despite -▁18. -▁dealt -▁Kooperation -▁Westen -▁corner -▁Wirtschafts- -▁Strukturfonds -▁diversity -▁determined -zubauen -▁trotzdem -▁mutual -▁ultim -▁Premier -▁manufacturers -ho -fallen --00 --cl -▁ton -▁tex -▁hall -▁senden -▁Time -▁bran -▁SS -▁Kranken -▁Gep -▁bezieht -entwurf -▁onto -▁imper -reb -▁reicht -estr -iform -▁entlang -▁accounts -erman -▁43 -folio -ciliation -▁Minutes -▁qualified -▁wichtiges -▁Dafür -▁Datenbank -▁verlangen -▁Gewinn -▁investments -▁hoher -züg -▁Fahrzeug -▁amongst -▁feeling -▁Wachstums -▁urge -▁leadership -▁Brazil -▁unmittelbar -▁charming -▁horiz -▁Definition -▁unanim -▁ziemlich -uy -▁father -▁dah -▁volle -▁wenden -▁Take -▁Kong -▁Know -▁Caf -berecht -▁beiz -▁User -▁Lou -▁NL -stände -▁reichen -▁verändert -ehnten -anges -▁Before -▁6. -▁liefern -▁Product -▁Verbrechen -▁95 -▁führte -▁Bestell -▁spezial -▁Genehm -▁Spiele -▁außergewöhn -▁skin -abilität -▁moved -▁Skype -▁Sanktionen -▁regionale -▁Küste -▁Radio -▁Leitlinien -▁consultation -▁umfassenden -uttle -▁erhältlich -▁Signal -▁repeated -▁David -▁Kyoto -▁Munich -▁Beifall -rupt -mo -OP -ès -▁snow -▁Tibet -▁Set -▁pension -▁Balt -▁Austausch -uts -urement -▁Wil -▁COM -▁Valle -aming -ischung -▁erle -osse -▁geist -place -▁exceed -einrichtungen -▁abgesch -ungsverfahren -▁maß -▁obser -▁marked -▁geschrieben -▁Klim -▁Maßnahme -▁Heiz -▁bekannten -▁höhere -▁technological -▁historische -▁Bewegung -▁Airbus -▁runs -▁sofern -▁Disc -▁recommended -▁Schulden -▁illustr -▁fehl -▁Wahrheit -▁erweitert -▁mechanisms -beziehung -istribution -ae -uj -▁fed -▁Div -▁Dach -▁Ec -▁Egypt -▁Pe -▁gült -igsten -onte -urd -▁lock -▁Hong -atische -heber -rechten -▁eröffnet -▁Verk -▁Verträge -room -▁sprach -aker -alty -▁knows -▁mode -▁Amts -tragung -richtet -▁helping -▁attitude -venue -▁privacy -▁beträgt -▁improvements -ameras -▁Swed -▁Strategien -▁switch -▁significantly -bahnhof -▁wait -▁religi -▁experiences -▁falsch -▁Plaza -▁error -▁italienischen -▁heavy -▁Landwirte -▁essen -zza -kurs -14 -▁sicht -▁Pay -▁Bord -▁Bör -uries -▁Cook -▁inten -▁Use -gence -▁existence -▁shape -▁Schule -▁co- -erry -ieties -▁Konvent -▁Konsequ -▁output -gestellten -▁transform -▁65 -▁Que -▁requests -▁Krisen -▁compliance -▁presentation -▁Besitz -ätzung -▁configur -▁Umweltschutz -▁treated --border -▁Technology -▁zusätzlichen -▁react -▁Ferner -▁speakers -▁bedrooms -▁straight -▁Empfehlung -▁expertise -▁regret -▁streets -▁competence -▁Dublin -füllung -ader -verein -gin -▁felt -▁Tw -▁Tom -▁Tax -▁binding -▁gather -ileg -▁cold -▁lose -▁kü -arkt -▁List -▁erreich -igence -zep -eile -▁44 -eoples -▁Player -▁97 -▁drugs -▁gefördert -▁Schweden -amerika -▁kommun -▁weltweiten -▁destr -▁destination -▁kurzem -▁expectations -▁Materialien -▁künftig -▁furniture -▁erscheinen -▁entdecken -▁sanctions -▁consistent -▁launched -▁Essen -▁piece -▁blood -▁Tschech -▁birth -▁Badezimmer -▁Fremden -▁MySQL -▁websites -eb -fare -med -▁dishes -▁wild -▁Turn -▁p.m. -▁Bang -▁Baum -▁AP -itime -▁Kann -▁units -▁zugänglich -vec -▁geprü -▁convert -▁Ungleich -▁Ohne -▁Anfragen -▁plug -▁Umgang -▁Herkunft -▁parad -▁flat -▁eigenes -▁Gener -▁voller -▁besitzen -▁Mittelpunkt -▁intention -▁Studenten -▁wesentliche -▁interessiert -▁schönsten -▁recognise -▁schwierigen -▁obligation -▁Delegation -▁Führungs -▁bedroom -▁grundlegenden -▁koordin -politischen -▁jährlich -▁fantastic -▁Ambiente -▁compensation -▁Erwartungen -▁hilft -▁öffnen -fest -UM -▁fit -▁sights -▁Tim -▁Tai -▁Action -▁laufen -▁Kategorie -asters -quellen -▁zuerst -▁exists -▁entre -▁entstanden -▁spok -▁advent -▁Beweis -iana -▁relatively -▁Nachfrage -▁USD -ought -▁helps -▁browser -▁Monte -▁Roh -▁represented -▁bedeuten -▁Zahlungs -▁Studie -kommt -behörden --free -fällen -▁Welcome -▁emphasis -künfte -▁extension -▁prosper -▁subsidiar -▁rejected -▁older -▁SME -▁eye -▁hervorragende -▁Ausgangspunkt -▁zweifellos -reuung -uv -wa -▁außen -▁weshalb -▁SO -▁Malt -▁A4 -onn -▁läng -▁Rub -▁inzwischen -iessen -schule -▁stations -essene -▁Jack -▁proud -▁Erinner -cco -▁comply -▁trial -ändige -▁substances -▁1991 -▁Atom -▁Hindern -▁Show -▁articles -▁objects -bauen -▁kombin -▁keeping -▁supplement -▁Paper -▁resolve -▁Update -▁Suite -▁Temperatur -▁Konfli -▁niemals -▁ausgezeichnete -▁Verteidigungs -▁Beratung -▁Agrarpolitik -▁natürlichen -▁trees -▁appreciate -▁GNU -▁Wirklichkeit -▁sleep -fangen -CC -▁tests -▁dog -▁dict -▁dans -▁dinner -▁vess -▁Pia -inos -isp -▁Kandidaten -▁Var -▁Ga -ingesch -ichtete -▁Jew -ugnisse -▁schreiben -uds -▁arms -▁49 -▁sav -▁Nachrichten -▁reden -punkte -▁operators -klärung -▁Beispiele -ettbewerbs -▁favor -▁installiert -▁taxes -▁entered -▁1989 -▁Entwicklungsländern -▁soft -▁neuesten -▁interessant -stehenden -▁Papier -▁emphasise -▁advantages -▁ruhigen -▁engineering -▁ausgezeichneten -▁Eisenbahn -▁refugees -rüstung -▁Verbesserungen -oßen -dessen -pel -path -75 -▁fill -▁si -▁vast -▁Sens -▁Sov -▁Massen -eds -inations -usal -▁zut -▁Rich -▁Rule -üren -▁einfachen -▁whilst -▁whatever -wechsel -flug -inken -▁obst -▁laden -abling -▁7. -handlungen -▁Ministers -▁99 -▁delegation -▁estim -▁verschieden -▁approval -▁stattf -▁Safety -▁Sauna -▁offenen -▁Offen -spruchs -▁Vorsitz -▁Alternative -▁bemüht -▁applicable -▁temperature -▁centres -▁Kohäsions -▁Geträn -▁texts -nummer -uden -▁ta -▁fühlen -▁dining -▁Sou -▁paint -▁Puert -eauc -▁latter -▁Fire -iefe -▁Nein -opolit -▁einerseits -ungsbereich -etzten -formance -▁Einstellung -▁politicians -▁Überzeugung -▁finding -▁Exp -▁protest -▁helped -▁personally -▁Blut -▁Music -▁beträcht -▁Robert --Re -▁respective --Date -▁17. -▁involving -▁affairs -▁Strategy -▁anti -▁naturally -▁Normen -▁Kapaz -▁Zusatz -▁Bauern -▁happens -▁cross-border -▁ursprünglichen -▁arrangements -▁kurze -▁entscheidend -▁entertainment -▁älter -rd -maschinen -cean -▁sorgt -▁mel -▁Sy -▁Sym -▁peoples -▁Mall -▁Burg -▁Access -utter -▁Work -▁Rock -▁Govern -▁intend -▁tours -▁alarm -achte -▁verändern -▁Ungar -agon -plans -prof -▁spanischen -▁covers -▁disaster -gesundheit -ändler -▁gelegt -▁Klar -▁15. -▁kommer -▁organisiert -ächtigen -uiten -▁exclusively -▁spring -▁util -▁danger -▁Jedes -▁suffering -▁grundlegende -▁präsentiert -▁menschlichen -▁betreffenden -▁spectac -▁recognised -▁valuable -material --out -DA -19 -▁Dauer -▁Eis -▁Bucht -erten -urger -▁Kroat -▁KMU -▁Vision -ares -▁forg -▁anwes -▁stimmt -▁starten -uropäischen -▁OF -▁Stahl -antly -izit -▁spot -▁leisure -örung -▁diseases -chtigkeit -▁solve -▁gewalt -▁900 -▁08 -ählte -▁detaill -▁creative -▁musste -liefer -▁richten -legungen -weisung -▁hostels -▁höchsten -areness -▁funktionieren -▁kulturelle -▁museum -▁abgelehnt -▁ausgesprochen -▁outdoor -▁journey -▁Swedish -uens -zin -jo -▁a. -▁achten -▁mobility -▁SV -erin -isory -oli -▁Vir -▁beha -▁Like -idden -▁stone -uchte -▁aufzunehmen -▁gezogen -▁Verd -▁shift -▁Ausstattung -ailing -ysteme -▁George -▁trifft -▁Reisen -▁soweit -▁96 -▁07 -▁fulf -▁fulfil -▁beraten -▁larg -▁Franc -▁12. -▁Steuern -▁looks -▁Angst -▁2.0 -▁Britain -onomic -▁opposed -▁confirmed -▁u.a. -▁Halb -▁Nahen -meldung -kontrolle -▁Dusche -wachstum -täus -nungen -mechan -kund -▁dritte -▁au -▁vous -▁weather -▁Sad -▁pick -▁AT -eta -▁ungefähr -enter -schirm -lege -▁erb -rohung -ikes -▁umzusetzen -▁Erwachs -▁arg -reichung -▁conserv -▁subst -▁Brasil -▁Geschmack -▁10. -▁09 -geschäft -▁privileg -▁aktuell -▁familiar -▁Washing -▁Farb -▁Strukturen -▁Director --the -▁Optim -▁übernimmt -▁Engine -▁Veranstaltung -▁obtained -▁discovered -▁renewable -▁threaten -▁riesigen -▁Harmonisierung -▁gratulieren -▁intellectual --I -pal -18 -▁dich -▁Sund -▁Esp -ires -▁zählt -▁korr -▁Ka -▁Four -▁Glaub -▁GDP -ati -▁thorough -▁anstatt -▁erlauben -▁residence -▁cloth -▁durchzuführen -▁EU-Mitglied -▁trib -▁offices -▁gewonnen -aktiv -▁directives -▁qualifiz -▁Bestellung -▁verschiedener -▁bekämpfen -▁faced -▁Box -▁Tagung -▁combating -▁UNO -▁Secr -▁Küsten -▁automatic -gänge -▁determine -▁Termin -▁passengers -▁künftigen -aßt -▁effektiv -▁Berufs -▁ignor -▁verursacht -▁Proced -▁largely -iability -far -da -üste --5 -▁buchen -▁girl -erte -itts -oria -chron -▁Hof -▁GU -ulich -▁fordere -▁veran -▁vertr -pth -amente -▁Arme -▁Abschnitt -flächen -▁serves -▁Internets -▁gelöst -▁beschränkt -eichert -▁verlangt -▁Punkten -▁involvement -▁Kommun -▁Investitions -▁temporary -▁Farbe -▁protecting -umping -▁Stabilitäts -▁Hohen -▁Kommunikations -▁Übereinstimmung -▁figure -▁occasion -▁constructive -▁millions -▁lovely -inuier -▁impressive -▁enthus -▁Rumänien -▁obstac -▁¤ -vi -EG -A. -▁Szen -▁AS -▁Ath -institution -iefert -▁Ladies -▁Noch -ktor -▁Schü -izz -ipel -▁schafft -ecess -▁amounts -▁Ausführungen -▁Armen -actory -▁mögliche -ilding -▁Gegend -handelt -▁Brand -▁leaving -azine -▁flow -▁acquis --Ab --Adresse -▁beseit --sc -▁increases -▁healthy --Europ -▁treaty -▁Instead -▁Änderungsanträgen -▁bureauc -▁officials -▁uneingesch -▁lautet -▁eyes -▁removed -▁couple -▁Krankheiten -▁Hostelsclub -▁Luxembourg -hi -hle -cap -läuf -▁rank -▁Town -▁AV -ito -uster -▁Wähl -iefen -▁Luc -▁sehe -▁auftreten -grenz -▁chain -▁reserve -▁Spaz -wood -wendungen -▁Marina -▁Übersetzung -eatre -▁firstly -▁Vorschlägen -kehren -▁equally -▁individuell -▁strategies -▁beabsicht -▁Geräte -▁schnelle -▁Behinder -▁annehmen -▁president -▁intent -▁Entscheidungs -▁Feuer -▁Festlegung -▁angesehen -▁Tatsächlich --time -▁happening -▁zentralen -▁Gestaltung -▁ordnungs -▁erheblich -▁wissenschaftlichen -▁breite -▁Gentoo -hard -nischen -krit -▁hit -▁seat -▁od -▁WLAN -▁Vern -▁Vater -ani -ented -▁impression -▁stake -orte -apest -▁verwend -▁verständ -▁mitten -▁einig -▁ausgeschlossen -ätigung -▁Alp -▁newly -echnologie -meister -ergie -▁insist -wirkung -▁handling -▁Executive -▁Problems -▁ausgezeichnet -▁schwed -▁communications -▁contributions -▁hervorheben -▁aufgefordert -▁extraordin -▁preparation -▁Drittländern -▁kurzen -▁Garantie -lastung -▁mandate -▁leistungs -▁organizations -▁Zuständigkeit -▁Croatia -▁balcony -▁Kilometer -rate -aal -seite -mung -kultur -CE -▁münd -▁BSE -eries -ilen -▁catch -▁Wit -▁Himm -▁bezug -▁Late -trans -▁Their -▁Verring -▁Thomas -▁Schatten -▁Einheit -▁Child -▁Imp -▁incent -▁suite -▁requirement -▁awareness -▁doesn -▁boat -▁charges -▁bekomm -▁Boot -▁Deutsch -▁Straß -▁Entwicklungsländer -▁classic -▁remaining -▁vereinbar -▁volume -▁zufolge -▁universal -▁nachhaltigen -▁Balkan -▁license -modell -▁Fähigkeiten -▁zugunsten -▁beglückwünschen -▁discharge -▁Washington -tag -zipl -16 -▁ru -▁avec -▁wettbewerbs -▁Sicher -ofin -▁catalog -▁Riv -▁View -ask -uführen -▁derart -andal -▁abroad -ochen -▁Wird -sprech -▁durchführen -▁Regime -▁Umfeld -▁root -werb -▁Frid -▁decis -ypern -▁verbringen -▁monument -▁Aktien -▁Emp -▁Meinungs -astricht -▁advoc -▁Sonne -▁Canada -▁Japanese -▁zuständigen -▁fighting -▁surroundings -▁collective -▁Ratspräsidentschaft -▁restrictions -▁susp -▁liberalisation -▁früheren --Un -▁kontinuier -▁Professional -▁sodass -ris -32 -▁dark -▁Miet -▁gold -▁Block -▁AM -inational -▁cab -▁Cond -▁unent -decken -▁Life -ibeh -▁prove -▁entstand -▁Ausstellung -▁Kontext -▁Geheim -▁Regen -etty -▁Maastricht -führte -▁Alles -▁issued -▁debates -minuten -▁creates -▁operational -ziele -▁16. -mitten -ENT -▁rapporteurs -▁pattern -▁Gleichgewicht -▁opinions -▁parlament -▁characteristics -▁depends -▁Camping -▁bestätigen -▁renovated -▁Bulgaria -tedly -▁Automobil -▁ehrgeiz -▁präz -▁verabschiedet -▁inhabit -EF -pers -▁fasc -▁ball -▁Merc -orial -▁Word -▁album -▁Na -▁Nap -stood -orten -▁Ziffer -abwe -▁Verfahrens -▁Stift -▁Anton -▁prohib -ubs -▁ausged -▁coher -▁Ausführung -actions -▁sauna -▁64 -▁Nachricht -standards -▁Brenn -ältigen -▁param -▁Arbeitsplätzen -holen -züge -▁notwendige -▁identify -▁returned -▁volunt -▁messages -▁contracts -▁maintained -▁Websites -▁resolutions -▁Übereinkommen -▁Prüf -▁d.h. -▁decorated -▁Drittens -▁disappoint -▁Plenum -▁Through -do -bart -lights -liste -▁hängt -▁nob -▁walls -▁phen -▁push -▁gets -▁Adr -▁Award -eler -igend -oni -▁Fa -▁Rent -asche -▁derartige -▁anspruchs -▁Infl -▁Verg -▁abol -etzte -ateful -▁legend -ystems -▁Year -▁Entfern -▁fee -▁Gewicht -▁Quark -▁Deutschen -▁Redner -▁interessante -▁Ideal -▁intensive -▁einzigartige -▁freiwill -▁Detail -▁sexual -▁Zentralbank -schaffen -▁produziert -▁faith -▁bezahlen -▁Hilfs -▁Schlafzimmer -▁traditions -▁Subsid -banken --min -UT -GB -▁dream -▁ER -▁Mold -iser -orca -▁cable -▁CN -anen -▁Lt -▁Lu -andon -▁ermut -forschung -▁Anbieter -▁youth -▁übersch -udes -▁serviert -▁gewissen -ittees -▁Verbot -▁8. -istry -▁Indones -▁Arbeitsplatz -▁Partnern -ployed -▁artist -▁öffentlich -▁entwickelten -▁Speicher -▁precise -▁hardware -▁Buchung -▁verschw -▁ausgestatt -▁intensiv -▁verhindert -▁Legal -▁Regierungskonferenz -märkte -▁owner -▁Erholung -beauftrag -▁MEPs -▁geprüft -▁iron -uite -macht -äder -▁sin -▁swe -▁Trag -▁DA -▁Earth -▁Polit -▁PRO -▁Agricult -orama -▁lin -▁Wolf -▁FA -▁Viet -ici -iera -▁angeb -▁aufnehmen -▁verstanden -▁verleihen -änen -▁shad -▁Schuld -▁ausgesetzt -▁Ersatz -▁genet -▁gender -▁Max -▁reported -▁Polish -tendlich -▁involve -▁transportation -▁kurzfrist -▁Planung -▁reserves -▁österreich -▁Anti -▁konkret -▁copyright -▁conflicts -▁regularly -▁Prinzipien -▁kontakt -▁aftern -▁Procedure -▁Verringerung -pot -gä -én -▁tä -▁nov -▁mur -▁meat -▁battle -▁gone -▁Fair -▁Vice -▁unmöglich -arium -▁string -▁witness -▁geä -▁jetz -ects -▁Ankunft -▁Ansprü -▁abgestimmt -ighten -▁entsteht -▁Ausgabe -schluß -cles -▁bestimmen -örd -▁recip -▁welchem -▁Är -▁Krie -▁Congress -▁parents -ategie -▁visa -▁Road -▁passiert -▁Geschäfte -▁direkten -code -▁recommendation -usstsein -▁drawing -produkte -▁properties -▁Besonders -▁facilitate -▁erreichbar -▁Taiwan -mat -miss -car -▁dates -▁MB -erkennen -▁kämpfen -▁Kamera -▁Ferr -▁Food -▁Vo -steh -stieg -ichteten -▁Abhäng -▁supposed -▁46 -▁submit -▁legt -fined -weite -▁02 -▁geräum -▁App -▁customs -▁reveal -▁Gerichte -▁Beiträge -▁definiert -▁topic -▁anti- -▁Sony -▁Freiheiten -▁Verhandlungs -▁vollständige -kohol -▁westlichen -▁northern -▁wollten -▁conducted -▁Faktoren -▁Verteidigung -▁Haftung -▁Bemerkungen -onymous -▁hundreds -▁accompan -▁Wünsche -▁sorgfältig -▁somewhat -▁wann -▁Semin -▁gift -▁Bibliothe -▁echten -▁Hot -▁Freit -▁Gene -▁thumb -▁NE -ucher -▁Jer -▁worse -ausend -▁amaz -▁Einbeziehung -▁interior -▁Struct -▁Verbindungen -▁Deswegen -▁Aktual -▁ausgeführt -▁Training -▁geboren -cohol --En -▁define -▁metal -▁performed -▁begrüßt -CHT -▁Beziehung -▁Konsens -▁terrorist -würdigkeit -▁tatsäch -▁Führer -▁parliaments -▁Persön -▁Kreuz -▁depending -▁Jede -▁Schweizer -▁displayed -▁staatlichen -▁updated -▁vacation -▁kostet -▁Nahrungsmittel -▁erleichtern -ooth -las -▁faster -▁Sorgen -▁Dynam -▁PS -▁Videos -▁unfor -berge -▁Nic -schäd -acher -▁gezwungen -▁ju -▁James -▁Anmer -einen -rennen -▁consc -▁Asien -▁seeing -zeichnis -▁Neue -▁180 -▁Format -▁Vorschlags -▁Projekten -▁deals -▁verwand -ikaner -▁career -▁answers -▁towns -▁decision-making -▁settings -▁regulatory -▁holding -▁joined -▁geltenden -▁connections -▁höheren -▁Budapest -▁hinzufügen -▁Werbung -knüp -▁Cyprus -▁stylish -▁Ungarn -▁phenomen -▁Subsidiar -hone -uese -OW -▁dro -▁arab -▁whe -▁Dass -enze -ergeb -oma -▁ought -▁Ware -▁Wesent -▁Fleisch -▁Read -▁islands -ivals -▁Zypern -osis -▁Europol -▁abandon -izza -weile -▁Gründung -▁solange -▁Orte -▁mostly -▁techniques -▁driver -▁pharm -▁follows -▁gebaut -▁Adobe -▁13. -▁multinational -▁Protection -▁nahezu -▁Balkon -▁Vereinbarungen -▁minority -▁frequently -▁Tarif -▁emergency -▁chinesischen -▁verehrte -▁gespeichert -▁Kompetenz -▁Polizei -▁SMEs -▁Spazier -har -afe -mir -maschine -86 -ère -▁tail -▁SA -▁EM -▁BIP -orie -ometr -▁unge -icon -stat -▁noted -▁proof -▁vorlegen -▁worry -▁grateful -▁liqu -ammeln -ventionen -▁Individ -▁Genau -▁continent -▁beschleun -▁Monetary -undert -▁Shop -uzierung -▁Bildungs -▁Betracht --TV -örderung -▁organized -▁label -▁Fernseh -▁demanding -▁windows -▁heutige -▁angemessene -▁Gipf -▁bemühen -▁beeindruck -▁Gemeinde -▁verringern -▁ferner -▁Einwohner -▁ultimately -häuser -ieller -sc -17 -▁bele -▁IV -▁SC -▁Symbol -▁Dipl -erre -itra -itals -▁cash -▁Wild -▁Holid -▁UR -ingly -▁Vermögen -▁exklus -▁ausreichen -ochter -ätigkeiten -▁trop -▁Plug -▁Dev -▁invite -▁Bedien -▁Exc -▁preis -▁suites -▁complement -▁Interess -▁visual -▁integriert -▁Gesellschaften -▁hostel -▁Außenpolitik -ausschuß -▁bilater -▁erinnert -▁Audio -▁castle -▁ethnic -▁Mexico -▁Mobilität -▁electricity -▁bottom -▁delighted -▁divided -▁Veröffentlichung -kreis -gut -▁wines -▁EZ -▁Mem -▁Ball -eachtet -unning -▁ll -▁library -icke -stall -▁refr -ibar -▁überras -earing -▁Vorgehen -▁producing -iox -▁contrast -▁9. -▁drug -▁Whether -▁03 -▁Bars -▁visible -▁Blue -▁Partners -▁statistics -▁arrive -▁contacts -▁bestehende -mäßigen -▁relaxation -▁1970 -▁Hostel -▁verdient -▁Provinz -▁Kriegs -▁übrigens -▁Sollten -▁Gespräche -▁spezifischen -▁Identität -▁jegliche -▁fertig -▁Slovak -▁behavi -▁quarter -▁Gepäck -vol -wald -▁ticket -▁sah -▁migration -▁Terra -▁IB -▁Edition -▁Beam -element -iro -▁Cat -▁Games -▁Golden -arische -stream -▁stars -▁2, -▁Anschluss -▁entrance -schafts -▁nachzu -▁Yes -ruh -▁heritage -▁welfare -▁locations -▁verbind -▁expansion -äßt -▁courage -▁Author -▁Automat -▁starts -▁investors -▁Palästinens -▁Gerät -▁tourists -▁Bevölker -▁Frühstücksb -▁Webseiten -begr -▁palästinens -▁pieces -üllen -▁channels -▁Prague -▁letztlich -spartner -▁magnific -uous -▁vent -▁Equ -▁Funds -▁Ret -▁Viertel -▁GB -▁behalten -ommun -▁neutr -isses -agg -plement -▁'ve -▁Rezeption -werks -▁Image -▁2012 -▁Framework -▁Browser -▁protocol -▁beliebt -▁valley -▁presents --Ber -▁einfacher -▁Besorgnis -▁exciting -▁involves -▁visiting -▁careful -▁Zwei -mäßige -▁Canon -▁Insbesondere -▁Selbstverständlich -▁gegebenen -▁stored -▁widely -lässlich -▁Sollte -▁pollution -▁verurteilt -▁declaration -▁Putin -▁grundsätzlich -▁berechtigt -▁ordnungsgemäß -▁inhabitants -▁Vietnam -aben -zne -passen -gs -▁tea -▁narr -▁Sn -▁Mangel -▁Bö -▁Wart -▁Fun -▁unw -icans -▁Liter -schm -▁Oder -▁oral -▁showing -▁unterst -▁lebens -blieben -▁remote -▁drivers -▁verbinden -▁flights -▁letzter -▁Preisen -▁Medik -▁Respekt --Fi -▁nunmehr -▁explore -▁index -▁Beweg -ifizieren -▁gewährt -▁planet -systeme -▁Verlust -▁Optionen -▁bilateral -▁Portuguese -▁gardens -▁subsidies -▁Untersuchungen -▁Schnitt -▁einheitlichen -▁Einwanderung -▁cosy -▁afternoon -sieht -west -▁sac -▁Sat -iled -othe -▁input -iones -ulf -ulpt -▁ster -▁ersetzt -upl -▁entschlossen -ogether -ignet -hlt -ammt -▁Gefahren -▁indicated -▁gespielt -▁proved -▁understood -aktionen -klass -▁Verbreitung -▁Büro -▁Zielen --Mod -▁visited -▁privat -▁individuelle -▁Marktes -▁organisieren -▁direkte -▁installieren -▁rechtlichen -▁Moz -▁Kapitel -▁focused -▁maintaining -▁Legisl -▁virtual -▁beschäftigen -▁Copenhagen -▁wunderschön -▁Komponenten -▁collaboration -▁Baltic -▁Friday -ties -nitten -▁heating -▁fitness -▁m2 -▁Ib -▁Sar -▁Situ -▁Pact -▁Bre -emm -embl -▁lounge -▁king -▁Roll -imedia -▁Naz -osit -▁neun -izer -▁vorstellen -immungs -▁accident -▁newsp -▁47 -▁Haar -▁gestaltet -▁awa -▁signs --Ar -holm -▁Versicher -▁Email -▁Energien -▁individuellen -▁Phot -▁Zwecke -▁erfolgreiche -▁entscheidende -▁Natural -▁cutting -▁demonstrate -▁demonstrated -▁Michel -▁vorhandenen -schaffung -beziehungen -▁attrakt -▁Question -▁Autobahn -hängen -uing -uers -miet -EE -kan -ören -▁rational -▁mat -organ -▁Vict -▁Vize -▁inklusive -▁Lager -▁Nos -▁jenen -agers -▁proc -▁vorzu -bekämpfung -▁Vorstellung -acks -▁finished -▁Project -▁700 -▁06 -▁Freunden -▁facility -▁Politiken -▁14. -▁popul -▁dürfte -▁raw -▁Serie -▁unverzüglich -▁inflation -▁Fehl -▁Donner -▁britische -▁department -▁ursprünglich -▁Network -▁erläut -▁Bemerkung -▁beruht -▁Alexander -▁Kräfte -▁außerordentlich -▁spoken -▁accompanied --sp -ôtel -▁fuer -▁SL -▁Bug -igar -otive -onien -urlaub -▁categories -▁israel -ativer -ulin -▁bekräft -▁ongoing -ragt -▁era -iforn -▁wechs -ibe -wege -▁ausgeb -▁aussprechen -ación -▁Spaß -▁plenty -▁aggress -▁theme -gelassen -▁85 -▁carbon -▁0, -▁printing -▁zusammenarbeiten -▁stehenden -opped -▁schlägt -▁settlement -▁Perhaps -▁bemer -▁restruct -▁fehlt -▁high-quality -▁Städten -▁reduzieren -▁controlled -▁luxuri -▁AKP -▁dahin -▁spectacular -▁§ -ois -ozi -zehn -DR -pap -kern -▁hoff -▁Meg -▁Page -▁Auge -erie -▁commission -▁ließ -▁kamen -▁Kais -ami -gegangen -▁Niger -lehnung -▁Institut -▁Zert -hrten -▁exception -▁Ausb -▁Ausmaß -▁Alpen -▁transatlant -▁complain -▁smooth -▁personnel -▁effectiveness -▁Betrag -iegelt -▁Werke -▁distingu -▁klaren -▁documentation -▁Mitarbeitern -IDS -▁ruhig -▁verstärken -▁Erzeugnisse -▁verkaufen -▁angegebenen -▁Schönheit -▁Rechnungshof -▁eingereicht -▁BMW -▁definitely -▁в -pa -▁tickets -▁sad -▁Dal -▁gre -enary -imens -rak -▁Zür -upten -▁concluded -▁Verpack -ector -▁Sche -▁Abkommens -periode -▁committees -▁52 -▁firms -ührt -▁großzüg -aktion -▁possibly -▁Indian -▁angewand -▁explicit -▁foundation -ässig -▁marquis -▁educational -▁Israeli -▁Konsult -▁Northern -center -▁Kopenhagen -▁Amendments -▁Wohlstand -▁Depart -▁awarded -▁Salzburg -▁Sektoren -▁replaced -▁russischen -▁resistance -▁HTML -▁contrad -▁horizont -▁Malta -▁Puerto -gess -▁ve -▁phrase -▁gallery -▁Women -▁unt -ulen -▁forum -▁Luf -▁Lad -▁Nob -hereas -▁Verlauf -▁jener -▁Stimmen -▁usual -▁displ -▁Programmen -ruckt -▁crimes -▁Description -▁boot -▁boost -▁Rechtsstaat -▁citizen -▁Gericht -wertung -twortet -▁Ski -▁speaker -▁Parkplatz -▁erfolgreichen -▁Kirk -zentren -▁corruption -▁2013 -▁recorded -▁passenger -▁Männern -▁Belgium -▁minorities -▁Applause -▁erhielt -▁occasions -▁rapidly -▁wiederholen -▁Single -▁ergänzen -▁äußern -▁behaviour -▁· -zur -wählen --8 -▁TO -▁Tochter -▁SAP -▁Pen -▁Kab -▁Gere -▁stil -vertrag -▁vermitteln -▁confer -iences -ustellen -ughter -▁vieles -▁Charta -▁Leon -▁els -▁gewöhn -▁autonom -wirkt -▁Kriminal -▁Desk -▁profile -▁termin -▁competent --DE -▁bestimmter -▁Kontrollen -▁Schlussfolgerungen -▁Gesetzes -▁sugar -▁Schulen -▁Educ -▁Zusätzlich -▁Lieferung -▁trafficking -▁erhebliche -▁manufacturer -▁Bereitstellung -▁tackle -▁vorbereitet -▁Wi-Fi --Strategie -▁Gaza -▁compatible -▁configuration -ni -dauer -ror -zonen -wh -95 -▁nights -▁sla -▁woman -▁Satz -▁yes -▁U-Bahn -▁desired -▁Staff -ellers -▁exit -▁Einstellungen -hlen -▁proven -▁overcome -schefs --Sta -▁respected -▁assets -▁assume -▁verbesser -kehrt -▁Client -▁strategischen -▁Urheber -▁immigrants -▁traditionelle -▁enjoyed -▁arrest -▁Gegenstand -▁empfangen -▁Sozialdem -kommunik -onomie -▁containing -▁Chinas -▁routes -▁cycle -▁Newsletter -▁nahm -▁stimul --Sterne-Hotel -▁beigetragen -▁Soviet --European -waffen -WF -BS -▁rub -▁Dre -▁CF -▁Given -▁Güter -▁Leh -▁imposed -idet -▁theoret -▁constitution -izieren -▁scheme -▁voran -ewhere -▁partly -▁Arzne -▁Chair -geteilt -▁großem -▁Übersetz -demark -▁facts -▁brut -▁kleines -▁Rechtsgrundlage -▁Philosoph -▁films -▁princip -▁arrived -ography -▁eingehalten -▁sending -▁correctly -▁lokale -▁replace -▁amerikanische -▁Krankheit -▁credibility -▁luxurious -▁wunderschönen -▁Ernähr -uminium -fuhr -▁unfair -▁PPE-DE -▁uncertain -▁Genehmigung -oes -▁sit -▁mun -▁Tip -▁Mobile -iga -▁lobby -▁alcohol -▁prest -▁erzeugt -osa -▁constitutional -▁jung -▁Job -ferenzen -▁coal -udi -▁manager -sph -▁Konkur -▁bislang -▁Plat -reift -▁transactions -▁stronger -▁Bedrohung -▁Conc -▁centuries -▁langsam -▁Versand -▁Traum -▁Belast -▁maritime -gänger -▁Sonnt -▁professionals -▁Milch -▁verantwortungs -▁aktivieren -▁einzigart -▁Hamas -▁beteiligen -▁conditioning -▁Hungary -▁McC -▁appointed -▁emerging -▁corresponding -▁subsidiarity -▁anwesend --und -ées -ín -▁afr -▁sud -▁Sav -▁Sicherung -elated -inem -orben -▁lots -▁uniform -aser -idem -▁stolz -▁Thurs -▁vorrang -▁Erstellung -▁adults -gerufen -▁grün -▁Konstrukt -steigen -▁bedauer -▁Nachhalt -▁minister -▁Äg -▁Amt -▁concert -▁Schluß -▁config -▁Works --Gen -regeln -▁diagn -▁unvergess -strafe -▁weekend -▁Verbraucherschutz -▁distributed -▁Werkzeug -▁satisfied -▁einzigartigen -prozesses -▁certific -▁elektronischen -▁acknowled -▁beobachten -▁Following -▁Within --n -▁nutz -▁Sind -▁gearbeitet -▁Aqu -irs -igert -▁Wied -▁Kabel -▁VAT -iva -atures -anw -gefügt -▁Lok -level -ernt -real -▁abziel -ardo -▁Kongress -▁Orient -▁liefert -▁verbindet -hips -athem -▁prompt -▁globalisation -▁komme -▁adopting -▁Charles -▁entscheidender -▁declared -▁functioning -▁broadc -▁judicial -▁französische -▁Bulgarien -▁betreffend -▁verkauft -▁gekenn -▁Dolom -▁ausgerichtet -paket -▁slightly -▁destruction -▁Glaubwürdigkeit -▁Freitag -▁Structural -▁magnificent -▁ice -dienste --Q -▁hart -▁fem -▁died -▁dign -▁rental -▁miles -itim -irtschaft -▁Hen -▁inj -▁incr -anke -▁toil -▁anbel -rain -apie -▁Schon -▁auseinander -▁schicken -▁EU-B -▁genannte -rating -echnik -▁solches -werken -▁Sohn -etheless -yside -▁Staates -▁bright -▁Apple -▁courts -▁Darum -▁bereitgestellt -▁Investment -▁Ansichten -gehens -▁Flugzeug -▁Wohnung -▁suppliers -▁Balkans -▁shares -▁offiziellen -▁Kaffee -▁Konsequenzen -▁Kroatien -▁extraordinary -oub -down -ueller -65 -ás -▁Satur -empts -informationen -▁lists -▁Wäl -▁Water -alte -osion -osing -▁geziel -▁Sterne -▁Anlass -▁plays -geräte -nex -▁Überlegungen -▁Allian -▁Extrem -▁Exchange -▁Geschwindigkeit -▁surf -▁considering -▁zurückzu -▁sozialer -▁didn -▁sensible -▁Fahrt -▁taxation -▁addresses -ivalent -▁identified -▁flood --Projekt -▁zuständig -▁Montag -▁Fußball -▁suspend -▁wiederholt -▁Professor -UNG -▁eastern -▁micro -lossenen -▁Sunday -▁anbelangt -IX -35 -gor -▁Mission -erts -▁Cup -▁Hat -▁Vel -▁Gang -icing -▁stammen -▁erm -▁verste -▁weist -▁Stutt -▁plenary -ologien -▁consent -▁Import -attung -achtet -▁konfront -mittels -▁proposes -▁drop -▁Who -▁carr -▁formed -▁Add -▁Festpl -▁Kollegin -▁Ministerpräsident -▁ökonom -▁encouraging -▁Fundamental -▁Anstieg -▁honest -▁Content -▁Beschlüsse -▁beantworten -▁ähnlich -▁Domin -▁preparing -▁Zivilgesellschaft -▁milk -▁providers -▁Gewährleistung -▁genügend -▁Sudan -ziplin -▁п -.. -rufen -suchen -bach -ökonom -gart -gemeinschaft -▁nörd -▁EF -▁Pil -emark -ofern -urteilung -▁Wetter -▁inmitten -derung -asing -▁angest -stes -verän -ikationen -▁voranz -ounter -ärkt -gerung -▁Mrd. -▁Konfigur -▁Mitgliedschaft -▁suit -▁ensures -▁letztendlich -▁entspannen -▁Without --Vers -▁Normal -▁domain -▁Zusch -▁Positionen -▁resolved -▁herrscht -▁überprüft -▁Eigentum -▁disappear -▁Potenzial -▁mittleren -▁Thirdly -▁Valley -▁substance -▁Schüler -eist -PC -gearbeitet -ju -▁della -▁amen -▁TR -▁ped -empl -oration -▁ou -▁Wählen -▁Rab -▁Vene -▁Nam -▁confront -prene -▁Thail -▁Erfüllung -▁weitgehend -▁Made -▁Übrigen -▁Mini -▁Herbst -▁wohnen -▁everyday -▁pre- -omenade -▁mussten -▁artistic -▁Quellen -▁actively -▁erstmals -▁entwickelte -bildungs -▁eventually -▁machte -weilen -▁Assembly -äusern -▁roads -▁ernsthaft -▁army -▁component -▁drinks -▁updates -betrieb -▁begrenzt -▁verteidigen -▁Gerechtigkeit -NA -▁breiten -▁Pfer -irk -▁innen -▁toba -▁erz -uchs -▁verwe -▁concepts -▁japan -▁Stoffe -▁allzu -merk -enne -oche -auschen -▁betreiben -▁54 -▁inclusion -▁assure -▁deleg -▁glück -▁Next -▁Beschwer -orry -▁aktive -▁Laut -▁Gerade -▁topics -ographic -▁Kulturen -▁potenz -gebiete -▁western -effizienz -▁thousand -▁Gehminuten -▁division -▁enhance -▁reduziert -▁MP3 -▁Verzö -▁deposit -▁euros -▁Unternehmer -▁erlebt -▁Brasilien -▁Holiday -▁Arzneim -sektor -cal -ye -UL -▁vary -▁Tri -▁Treib -etes -eaus -otic -orb -▁kreat -▁CR -▁Hom -derten -▁stellte -▁gehe -▁Still -endo -▁exch -▁spoke -▁upper -▁proportion -folgung -icted -▁Klasse -▁personen -rechter -▁Travel -▁betrieben -▁musical -▁investieren -▁wirtschaftlicher -▁erstre -▁Reduzierung -▁Kühl -wahlen -▁Verletzung -▁supplies -▁university -▁Passwort -▁Eingang -▁duties -▁ACP -uggage -▁korrekt -▁decisive -▁Vizepräsident -▁elsewhere -▁Thursday -.C -MA -▁tritt -▁sen -▁Tyr -▁Tennis -▁blie -ede -eding -eak -itale -▁Rio -reche -stud -andt -▁OR -▁könnt -▁Vorhaben -▁warrant -▁solely -▁hierbei -▁Entlastung -▁proposing -▁handle -▁Meister -▁Among -ordnungen -garten -hältnisse -▁deutsch -▁absence -▁deficit -▁lessons -▁subjects -▁generated -▁hervorra -▁optimale -▁applying -▁Kontakte -▁info -▁Belgien -▁Federation -ademy -▁Castle -LAF -▁Korruption -▁angepasst -▁vulnerable -▁bekommt -▁Ägyp -dorf -ckte -CP -▁fab -▁pe -▁Mur -▁gear -▁AIDS -▁lit -▁Klaus -▁Coll -opes -▁theory -ifies -▁Jung -▁shock -▁shipping -▁Einkommen -▁unterscheiden -▁Mr. -▁Cho -▁countryside -▁hinweg -▁gelingt -▁versichern -▁führende -enende -▁Finanzierungs -▁Schwimm -▁Quality -▁packaging -▁appeared -▁nevertheless -▁suggestions -▁erwähnen -▁carrying -▁liked -ügigkeit -▁zukünftigen -▁Trotzdem -▁Mountain -▁Übergangs -▁Katastrophe -▁Hardware -▁Monday -▁Praktiken -▁voluntary --minute -▁Donnerstag -while --day -▁semin -▁pure -edy -alts -▁Lobby -rein -lech -ktions -▁ersetzen -▁einbezogen -▁description -elli -▁Schiffe -ipfel -ranche -▁manche -▁durchschnitt -▁Grünen -werben -▁Marke -▁Stras -▁Common -▁gestattet -▁greatly -▁Besides -▁XP -▁impose -▁Saison -ski -▁fourth -▁institutionellen -▁grav -▁villages -▁Risik -ommerce -▁Luxus -▁Funktions -▁wrote -▁Latein -▁entdeckt -▁departure -▁legitimate -▁Playa -▁iPhone -helm -▁einzusetzen -▁excessive -▁kontaktieren -▁tobacco -niveau -uits -bü -bre --- -▁MA -▁gained -▁Astr -orer -▁ang -▁anschließend -▁ansprechen -▁beendet -aya -▁verteilt -▁Präsent -▁desk -▁deswegen -iki -iensten -▁abuse -▁ships -▁Bege -▁finish -▁Konto -▁41 -▁Prozesse -▁Orch -▁mini -▁getragen -▁simult -ixel -▁Addition -▁bedeutende -▁electr -▁non-s -weisungen -▁repair -▁teams -▁illegalen -▁Consult -▁verfügbaren -▁freundlich -▁Hungarian -▁channel -▁philosophy -▁umfassend -▁behaupten -▁unforg -▁unterstreichen -aue -günst -▁ven -▁Tätigkeiten -▁Mov -▁Mak -▁Creat -▁Having -▁Lounge -estra -osure -▁verschl -▁mitteilen -▁Verarbeitung -▁exports -ipher -▁vorzulegen -▁Erlebnis -▁Umsatz -▁Plus -▁Plattform -▁Frist -▁Sev -▁Minist -▁delicious -▁director -geschichte -▁Genießen -▁optional -▁Eurozone -▁Guests -freien -ränkung --Gr -▁offene -behandlung -behörde -▁supplier -alerie -▁Volkswirtschaft -▁housing -▁Captain -▁konzentriert -▁langfristige -▁wissenschaftliche -▁interpretation -▁größeres -athedral -▁Agriculture -▁EZB -▁© -.20 -übt -care -cision -AV -Rooms -▁fu -▁song -▁Ill -▁Sachen -▁Ebenen -▁gemein -▁Cub -▁ingr -istent -▁diesel -ective -ieden -▁sounds -▁vorbeh -▁Erm -▁Ergän -ranten -▁Alger -▁doors -▁Spann -▁Space -▁specified -ätzt -▁regarded -▁receiving -▁teile -▁difficulty -▁Situationen -▁Rückkehr -▁Berliner -▁banking -▁reservations -▁künftige -▁vorgesehenen -▁panoram -▁Erwä -▁Budgets -▁spezifische -▁beeintr -▁comparison -▁Ausflüge -▁konfrontiert -▁Strasbourg -▁om -rist -▁batter -▁IF -▁Dist -eted -utation -▁crew -▁Rh -▁wireless -änglich -▁Stamm -▁manual -▁unterzeichnet -▁Ablehnung -▁trademark -▁Vorgehens -▁disabled -▁roof -führungs -aves -▁overview -▁invited -▁Existenz -▁standing -▁attempts -▁geraten -▁module -▁geboten -bedarf -▁Betten -▁Fahrzeuge -▁colours -▁periods -▁Weiteren -▁Willkommen -▁suggested -▁Erweiter -▁bisherigen -▁Metro -▁satisfaction -▁pursue -▁Gottes -▁Budd -▁Freizügigkeit -▁abstimmen -▁Atlantic -▁Übernachtung -afrika -zepte -▁н -zung -▁tennis -▁fas -▁burn -▁A5-0 -orse -▁cul -▁Ker -▁Ruf -▁Vort -▁forests -idad -▁stream -rog -pläne -endar -annter -▁shap -▁Schle -▁hast -▁schwe -▁Wei -▁unterliegen -▁realistic -▁lieber -▁revision -▁Vereinfach -▁gebrauch -▁edge -▁filter -▁multi -▁1.5 -▁hervorgehoben -ESCO -▁Dres -▁vorgeschlagene -▁Metall -▁Place -▁Niederl -▁Widerspruch -▁well-known -▁welcoming -▁Stockholm -▁herunterladen -▁Perspektive -▁geprägt -▁savings -▁LateRooms -▁Frühstücksbuffet -oz -lings -▁hyp -▁rain -▁rising -▁script -▁SE -▁Suiten -▁Pir -enia -chst -▁Full -iess -enth -▁LT -aga -ugt -ierender -▁Abfall -ammelt -▁53 -▁acting -ventional -öffnung -▁scene -▁Geburt -▁bridge -▁Millenn -▁passion -▁breath -▁Haushaltsplan -▁Based -▁patient -▁resource -▁Wirtschaftswachstum -▁permitted -▁Opposition -staatlichen -▁Planeten -▁reaction -▁examine -▁Gefäng -▁menschliche -▁Tabak -▁arguments -▁hochwertige -▁ergibt -▁unzure -▁murder -▁Situated -rates -ui -zellent -68 -ст -▁Bio -▁Catal -▁Game -▁anhand -▁anschließen -hein -▁rechten -essions -▁abzusch -▁enabling -ußt -ärten -▁leb -immers -▁internen -landen -▁Artik -▁04 -▁leaves -▁Islands -▁ausgehen -▁Train -▁Medizin -▁gefallen -▁Quart -▁retain -▁Urteil -▁representation -▁Family -▁behoben -▁ports -▁finanziert -▁eingegangen -▁anzuzeigen -▁confirmation -▁football -▁Abendessen -▁Anwender -▁zukünftige -▁destroyed -▁federal -▁Bevölkerungs -▁Zürich -▁Stuttgart -▁unforgettable -town -aine -AB -▁tend -▁fan -▁rely -▁Dikt -▁Euch -▁Mig -▁Bett -inf -unruh -▁caf -ulse -▁Prom -▁einsch -▁constitute -▁Oste -▁sichere -▁schlagen -▁vorange -sprachen -▁Abschaffung -ianischen -▁Leiden -male -teilungen -▁directory -▁festival -▁refers -▁blind -▁Hauptbahnhof -▁UNESCO -▁vereinbart -▁farming -▁obliged -▁praktische -▁religion -▁Corporate -▁Kanada -▁Widerstand -▁Veränderung -▁langfristig -▁Nachbarn -▁angenehm -▁umfangreiche -▁cooperate -▁Francis -▁narrow -▁municip -time -▁Miß -▁Por -▁läßt -▁Wissens -▁Rap -▁Vide -▁ye -▁Li -herstellung -▁jahr -eitigung -agements -▁Statt -gram -economic -▁grosse -▁Vorlage -▁etabl -▁Pläne -▁Finland -▁discipl -▁publication -▁politically -okratischen -▁gebeten -▁30. -oppelt -▁paying -poration -▁Volkes -bereichen -ESS -▁Tourism -useum -programms -▁enthaltenen -▁twenty -▁dankbar -▁präsentieren -▁welcomed -▁Kommissionspräsident -▁militärischen -▁sogenannten -▁instructions -▁prosperity -▁Abhängigkeit -▁Nachhaltigkeit -▁Allianz -▁personenbezogen -uben -log -kom -▁rare -▁Dritten -▁Mob -▁Pension -▁PSE -▁zivil -▁killed -▁CAP -▁Valen -▁islam -ieval -anter -rachte -iza -laim -▁sought -▁dargestellt -▁trips -▁imports -▁verboten -▁gerichtet -▁Meines -▁besonderer -▁retail -▁bedeutend -▁Regierungschefs -▁renowned -▁Internationale -▁significance -▁Großteil -▁characters -▁cleaning -▁continuous -▁verdienen -▁vorliegende -▁Kreis -beschreibung -sprogramm -▁skiing -▁horse -▁hervorragenden -▁tranqu -▁militärische -▁Facilities -▁Erinnerung -▁Mallorca -▁Thailand -▁ere -200 -▁videos -▁SK -▁Pac -▁A. -▁lö -▁lunch -▁kern -▁Kas -▁anzub -▁dependent -▁geplant -advant -grenze -ogn -auben -▁relative -▁hingegen -▁justified -▁societies -▁dry -▁Attra -ößen -▁angekünd -▁Versionen -▁Resolution -▁Poly -▁excurs -burgh -▁Sky -freie -▁portug -▁Gleichstellung -▁Kontinent -▁freedoms -▁radical -cribe -▁harmonisation -▁Volksgesundheit -rückt -▁staatliche -mitglieder -technik -▁Entspannung -ugoslaw -▁Joseph -▁entreprene -▁Piazza -▁Armee -nete -nimmt -dis -▁tables -▁none -▁fans -▁Sammlung -emon -▁Wed -▁Vista -rob -▁Andere -anning -grunde -▁attrib -halle -izen -▁schäd -▁vorbei -▁manip -▁interview -▁legally -▁prev -▁Schlag -▁Barr -▁specialist -▁looked -▁strategische -▁Betreiber -▁refle -▁referendum -▁Modelle -▁negotiating -▁Leitung -▁Augenblick -gefunden -▁Befugnisse -▁welcomes -▁Venice -▁natürliche -▁clubs -▁begeg -▁supervision -▁indirekt -▁erfreut -▁Secretary -▁Wähler -▁jetzigen -▁Education -oubtedly -▁è -▁eat -dir -betten -▁Big -▁eignet -▁zieht -▁Canc -▁Hit -arn -▁beur -▁Nummer -ichtung -▁1st -▁asp -agent -ubeh -▁nachdrücklich -▁Vorder -▁Zeitung -▁Grundlagen -▁complicated -▁surve -▁surely -ühle -▁hohem -▁contributed -▁enterprise -▁gewähren -▁appearance -▁normalen -▁normalerweise -▁Dokumenten -▁Vorausschau -▁infring -▁unabhängigen -▁Auditors -▁sharing -▁elektr -▁suffered -▁homes -▁Beteiligten -▁Beaut -▁Luxemburg -▁Katastrophen -▁forthcoming -▁gewünschten -▁hospitality -verwaltung -▁Aufsichts -nor -rik -räumen -sitz -sätzen -zusammen -pol -ächte -▁Sied -▁Dub -▁Kein -▁Holl -ulous -ürzt -leiter -▁zugrunde -▁Jos -▁ausser -▁enabled -▁compos -ußert -▁disadvant -▁68 -▁everybody -▁festzulegen -▁modules -▁Wirtschaftspolitik -▁Fahrrad -▁hardly -▁remained -▁Märkten -▁preventing -▁Kirchen -▁leichter -▁originally -▁Binnenmarktes -▁Small -▁Innenstadt -▁Oliven -▁Gespräch -▁kontrollieren -▁AND -▁Konzert -▁allocated -▁NGOs -▁indirect -▁gemeinschaftlichen -▁Heizung -▁parameters -▁amenities -austausch -TR -gro -▁nütz -▁sam -▁EX -▁ceil -▁Fein -▁Feat -▁beings -gener -▁Unions -ugang -▁exzellent -weich -▁enger -▁Befür -▁Spir -▁Spekt -rix -▁wellness -▁bedanken -▁Äußer -▁directed -bry -▁speichern -zahlen -▁studio --Er -▁Does -▁Australia -▁externen -bestimmungen -▁calm -▁Socialist -▁ähnliche -▁ordinary -ssysteme -▁superior -▁proceedings -zutage -▁museums -▁Flüchtlinge -▁Customer -▁romantic --class -▁sichtbar -▁Börse --General -▁simultane -rt -xy -V. -▁nord -▁Dän -▁Hann -▁Häuser -▁Rod -asion -▁immen -stens -▁habit -ehnen -ehme -▁Stability -▁überaus -▁Spitzen -▁Fragest -▁compact -ützlich -▁indicate -▁Berichten -oys -▁realise -▁05 -▁beschränken -▁programming -▁Should -▁Americans -▁Quad -▁wirtschaftlich -▁irre -▁movie --Vert -▁komplette -▁scheinen -▁cuts -▁1960 -▁telec -lösungen -▁firmly -▁erhoben -▁errors -▁Generationen -▁Reservierung -▁cells -kapaz -▁Ursachen -▁unentgelt -▁Saturday -IK -öch -BC -▁fördert -oted -▁zit -▁cra -▁kg -▁Fels -▁Föder -▁Gard -iegt -▁repräsent -istischer -auspiel -▁mak -▁Album -nerg -▁Asian -ypen -▁Brun -iversary -▁festgelegten -▁geringer -▁fees -▁Autos -▁Gewerk -▁erstklass -▁Transfer -▁movements -▁existiert -▁Speisen -▁Christmas -▁Museen -▁recognized -▁Aufenthalts -▁31. -▁Gemeinsame -▁missing -▁erweitern -▁Organe -▁Serbia -▁Aktionsplan -▁untersucht -▁CHF -▁barriers -▁Johannes -▁Konflikte -▁hoffentlich -▁unzureich -.net -eben -nische -bung -baus -lers -▁hes -▁sight -▁whereas -▁Tausende -erbe -▁künstler -▁Civil -▁HU -▁Vul -▁Lit -estaurant -▁Verteilung -▁spons -sson -▁Alters -ords -▁saved -▁recy -▁heim -▁insert -▁productivity -okratie -▁hearing -▁explained -▁showed -▁introducing -▁classes -▁sustainability -operations -▁Passag -▁Moroc -▁Stärke -▁Kreditkarte -▁einheitliche -▁enhanced -hyth -▁remarks -▁juris -▁functionality -▁Culture -▁korrig -▁Geneva -▁amazing -▁Kriminalität -water -yll -li -OC -▁tree -▁nützlich -▁Tok -▁DC -▁Dritte -emes -▁Ku -▁C. -▁anhalten -▁Lohn -▁aufgebaut -▁deserv -▁sicheren -ells -▁umsetzen -thand -▁Beide -▁Alb -bling -reicher -▁hierfür -▁Rechten -▁herum -▁Meeting -▁Handlungs -▁zurückzuführen -▁sections -wertet -▁combine -▁Wochenende -▁Komplex -▁Consumer -riedenheit -▁wonach -▁Nachbarschaft -▁perfekte -▁Gefangen -▁komfortable --condition -▁wirksame -▁imagine -▁humanitäre -▁vessels -▁obstacles -▁liquid -▁female -bsp -junkt -▁soph -▁songs -▁mi -▁Dor -▁EV -▁Bor -▁Bab -▁Kür -▁Hon -bergen -opf -▁Alkohol -▁58 -getragen -phase -▁transmission -▁endorse -▁Emissions -▁Reserve -▁Finanzkrise -▁gefragt -▁regardless -▁investigation -▁25. -▁Zahlungen -▁metro -▁Territor -ksamkeit -▁Textil -▁Öffnung -▁Casa -▁unabhängige -▁collected -▁Schnell -spezif -▁Französ -▁splend -▁accurate -▁begleitet -▁laufenden -▁Würde -▁niederländ -▁Cookies -▁Himmel -▁fascin -▁Rechtsstaatlichkeit --Staaten -▁portugies -viertel --6 -äbe -▁Tests -▁bearbeit -▁Sold -eab -▁Köl -▁Hels -▁Fuss -▁Vin -▁Gall -▁incon -▁united -asis -▁antworten -▁Nero -▁sequ -▁zugelassen -▁erfordern -genz -▁vermut -uggl -▁Sieg -▁exempt -▁Auseinander -▁computers -akers -▁grossen -▁genehm -▁internationally -▁angewendet -holt -ädchen -▁organic -▁generate -▁turning -▁wahren -▁heißen -▁universities -▁exclusion -▁wünsche -▁veröffentl -▁reflected -▁Opera -▁Florenz -▁stocks -▁Islamic -▁stattfindet -▁incred -ümer --wide -DI -point -▁ride -▁rufen -▁gap -▁golden -irol -▁inev -uled -▁bez -▁behaupt -▁prime -istiken -▁verhe -▁Om -▁notes -▁vornehmen -▁cancer -uctive -▁disasters -▁mehrfach -lined -klar -▁Haben -▁einzuführen -▁deliber -eldorf -▁Bürgerbeauftrag -▁considerably -▁Quelle -▁Phän -▁student -▁schnellen -▁democrat -▁Darstellung -▁covering -▁ECB -▁Pakete -▁kompet -quisite -▁Hohe -mmern -▁synth -▁travelling -▁mittlerweile -▁Bereitschaft -▁klassischen -▁distinct -ef -een -yss -chant -▁cam -▁layer -▁Kart -▁Fass -▁pretty -▁Lith -hers -▁aufs -▁enttäus -laut -▁mac -ünde -akzept -ishment -▁Commissioners -▁trends -unktionen -▁Frü -▁invent -▁Überblick -▁ministers -▁gestärkt -▁Weltkrieg -▁belief --Pres -▁Muslim -▁schwar -▁Swimming -▁bewältigen -▁reaching -▁generations -▁Filter -▁encounter -▁Account -▁frühen -gerecht -▁Mängel -▁niedriger -▁Bonus -▁adapted -▁Somit -▁fleet -▁qualitativ -▁Satelliten -▁geäußert -▁dignity -▁ört -fleisch -SA -GE -▁till -▁rück -▁bauen -▁Samm -▁BA -ila -▁Gi -▁angebracht -▁undoubtedly -▁Omb -egel -▁Erschein -▁'A -ordan -spolit -▁bestellen -▁underground -▁Gesam -▁Brief -▁keinem -▁eingestellt -orgungs -▁140 -▁Debatten -▁Industry -wärts -▁labelling -▁tested -▁empfohlen -▁checks -▁schweren -▁Steuerung -▁Niederlanden --related -▁penalty -▁Faktor -▁Anwesen -▁gesetzlichen -▁Appartement -▁zerstört -▁Wichtig -▁Jahrzehnten -▁Bearbeitung -▁Louis -▁Straßburg -▁ingred -iat -durch --7 -ypt -EX -▁filled -▁aer -▁Tanz -▁Bru -▁craft -▁Kategorien -▁Fisher -imp -alus -▁Zerst -▁gepf -issance -iban -▁sichergestellt -▁excluded -▁überlassen -▁frame -▁Schauspiel -▁schr -▁worst -▁managing -euten -▁19th -▁Represent -▁Arbeitgeber -▁selber -▁Allah -▁Minute -vorschläge -▁discount -▁edition -▁verwende -▁acceptance -▁Parkplätze -▁Friedrich -▁violations -▁consulting -▁Analys -▁Diam -▁timet -politische -▁Wikitra -▁exploitation -▁faszin -▁Vereinfachung -yan -22 -▁tab -▁fax -▁ba -▁bike -▁Sier -erer -▁egal -▁Fal -▁Friendly -▁Glauben -▁ineff -ehold -odge -▁Erhaltung -▁plastic -▁recover -▁selben -▁Haup -▁restored -▁believes -▁Bestimmung -▁equivalent -▁experiment -▁besorgt -▁Servic -▁Schwarz -▁Energieeffizienz -▁Berichterstatters -▁rechtzeitig -▁Wirtschaftskrise -▁Reihen -▁Cross -▁digitalen -▁Korrekt -jahres -▁überwinden -▁reflects -▁Location -dienstleistungen -uggle -▁ergänzt -▁Branche -▁Wandel -▁türkischen -iovis -udsman -▁Richard -▁Wikitravel -bul -äle -85 -▁selling -▁Iron -▁Somm -▁Dra -▁Bour -etung -edig -esk -itate -orld -unction -▁lived -▁Vari -▁stages -▁stuff -▁Zut -appen -ections -▁Regul -▁56 -▁Leip -▁gesundheit -werfen -▁Immer -▁subsequent -▁Clear -▁gefäl -spartei -zusehen -▁Zahlung -▁Protocol -▁Investoren -▁Science -▁flexib -▁clause --Produkt -▁transferred -▁satisfy -▁Mittag -▁unmittelbarer -freundliche -▁Academy -▁Währungsunion -▁Registrierung -▁Ferienwohnung -▁spezialisiert -usalem -eing -kette -kampf -94 -gi -▁nine -▁Sir -▁Bir -▁Bele -▁CP -▁Gay -▁erzeugen -achter -outique -▁beenden -▁reliability -▁freely -▁interv -▁objekt -▁anywhere -▁Marc -▁varied -▁besucht -▁Result -▁designs --Form -▁multi- -▁column -▁Basel -▁Doha -systems -▁floors -verbrauch -▁Fernsehen -▁supplied -▁herrlichen -▁Urlaubs -▁geeignete -▁frage -▁losses -bitr -▁Easy -▁Georgia -▁concentrate -▁Konzern -▁multilateral -▁enorme -▁diesbezüglich -▁Andererseits -▁shadow -wig -TV --on -▁tsch -▁a.m. -▁vig -▁Maschine -▁ziel -▁Would -▁CON -▁ungar -▁Länge -leute -▁severe -▁wirken -orts -▁Prim -ahme -▁Weil -▁untersch -uctions -▁Parlamente -▁Arbeiter -angehör -mittlung -owski -▁gleichermaßen -▁Beseitigung -▁bekannte -▁integrierten -ohnt -uzenten -greg -▁rechtliche -zeichnungen -▁Welthand -▁Hochschul -▁minimal -▁zulassen -▁learned -▁Gelder -▁undertaken -▁sympath -▁overlooking -ugoslav -quiry -▁griechischen --scale -▁Wesentlichen -▁Sonntag -hüt -sa -ühen -machen -DER -▁het -▁villa -▁maler -▁Tonnen -▁Ticket -▁Tickets -▁Dingen -▁pas -▁Hunder -▁Gre -▁prag -▁Light -▁Zone -▁veränder -▁verweisen -ffekt -▁enem -▁bestand -gebä -▁Deck -▁konsequent -weiten -▁restore -zahlungen -▁Clean -ronik -▁cultures -▁Notes -EST -▁anticip -▁gegenwärtige -▁fishermen -▁Petitions -▁picturesque -▁reconstruction -▁ländliche -▁Histor -▁unglaub -▁sovereignty -▁Saddam -▁EU-Mitgliedstaaten -▁URL -▁restructuring -▁non-smoking -▁anzubieten -här -pet -pread -kul -▁IR -▁Sever -isa -onst -usch -▁cart -▁Koch -▁GPS -▁inade -iments -attern -arc -quer -▁stunning -▁stammt -▁errichtet -teneg -achts -▁auffordern -▁eins -eitern -agr -▁spreche -▁Parameter -eping -▁arise -änderungen -▁everywhere -▁Zusammenhalt -▁monopol -▁Monitor -▁Blu -▁air-condition -hältn -alender -▁Bewusstsein -▁24. -▁kompliz -▁strengthened -▁Jugendlichen -▁Eigentums -▁begeist -▁Fotograf -▁coordinated -▁Telekommunik -.2010 -uum -uage -van -ji -▁tank -▁hill -▁nbsp -▁froh -▁Borg -▁HIV -▁Rail -▁uncon -ivation -reams -▁Abente -▁traum -▁lebt -▁Vorbeh -▁Konzentr -inki -▁Implement -▁knapp -▁Att -▁formats -▁Zuverläss -obald -▁editor -▁negativen -▁Laden -▁Porto -▁irres -▁130 -ijk -▁Denk -▁massage -▁Benutzung -▁ambition -▁Daniel -▁Silver -▁fantast -▁Strecke -▁Singap -▁ausgewählt -▁untersuchen -▁stärkere -▁beruhen -▁Wiederaufbau -▁cancell -▁gekennzeichnet -▁panoramic -eper -rasch -34 -▁SQL -▁Drei -▁Katalog -▁Käufer -▁H-0 -▁Renten -andern -▁impos -estens -▁ersta -veranst -▁dei -ocop -print -▁shuttle -▁Schreiben -▁schätzen -ograp -▁Parad -▁350 -▁solar -▁province -▁decade -▁assembly -▁ordered -▁Energies -▁individually -▁Warcraft -▁listings -▁Linien -▁treatments -▁marg -▁Denmark -▁Instruments -PEG -▁Samstag -▁Bedürfnissen -▁Labour -▁Process -▁Tools -kretär -▁neighbouring -▁College -▁mangeln -▁thermal --the- -▁unentgeltlich -kre -▁Si -▁SW -ori -▁Cur -▁Gh -asi -bers -▁Lern -quenz -▁denjenigen -▁110 -agd -▁executive -▁champ -▁ausdrück -▁vorgestellt -▁Einfl -▁EU-R -▁warn -▁superb -▁57 -▁saving -inkel -▁hierzu -▁incomp -▁Sicherheitspolitik -rechung -▁Versorgung -▁approve -ohner -uzz -▁necessarily -▁anniversary -▁eventuell -▁Vertrieb -▁derzeitige -▁Frühjahr -▁holds -▁bathrooms -habt -▁Key -▁deeply -▁beinhalten -▁briefly -▁acknowledge -▁Todesstrafe -▁Religion -▁Journalisten -wirklichung -nik -fox -list -prechen -98 -NG -67 -▁hiking -▁myst -▁Spl -▁Search -▁Mah -▁Pad -atum -▁forts -▁Lea -▁verknüp -▁Schicks -ollars -▁Ausgleich -▁adds -▁Bevor -▁Berge -▁Ablauf -▁tracks -▁disag -anzig -▁51 -▁Yugoslav -▁Maxim -▁0. -▁eigener -▁Residence -▁Adv -▁Spielen -region -▁fairly -reibungen -▁magazine -▁normally -▁entscheidenden -▁bevorzug -▁targeted -▁Vertrages -▁offizielle -▁kontrolliert -▁Lehrer -▁unnöt -▁Nikon -▁Dresden -hängt -kö -OB -▁toll -▁senken -▁meal -▁Sobald -ité -▁RA -▁ine -indlichen -▁übertr -▁clin -tragt -being -▁mögen -▁realer -klear -▁damages -▁Bereits -▁Consequently -▁processed --Aus -▁bedeutenden -▁Umweltfragen -▁summ -▁portfolio -▁casino -isticated -▁tempor -▁Höchst -▁Subst -kämp -▁manufacture -▁Dialogs -gebieten -▁praktischen -▁gedacht -▁Galile -gelegte -▁teaching -▁Beachtung -▁synchron -▁Mexiko -▁Branchen -methoden -▁kommerzi -▁Dänemark --Vertrag -▁jurisd -▁¼ -▁и -yard -öhnen -▁wear -▁gastr -▁Area -otechn -usive -▁Greens -ieur -▁anzunehmen -▁Lesen -▁Nok -▁reisen -ague -▁usage -▁Alph -▁accompl -▁Make -hafter -▁Fläche -▁guard -▁highlights -▁appell -▁festzustellen -eldet -▁klim -▁Systemen -▁befasst -▁verletzt -▁engaged -ANT -fähigen -▁reputation -▁verschär -▁Society -▁decline -▁determination -▁Belgian -▁renoviert -utreten -▁Tauch -▁müß -▁reichhalt -▁unnecess -▁Mechanismen -▁kümmern -▁Hindernisse -▁Helsinki -ICH -kir -▁hung -▁Siem -▁Sports -▁Maus -▁Baby -ilm -▁KDE -▁GAP -inein -verhandlungen -▁Anhör -▁adj -ealing -▁Imper -▁ended -▁controvers -▁factory -▁wiederher -▁Klass -▁Hinweise -▁presenting -▁typischen -iffs -▁engines -▁Darf -ATION -▁offenbar -▁Weiterb -▁Performance -▁Zugangs -▁reserved -▁twice -▁brachte -▁olive -▁Senkung -▁Wissenschaftler -▁Venedig -▁beeinfluss -▁produzieren -▁bezahlt -▁transp -▁threats -▁müsste -▁bonus -▁Souverän -▁Vice-Pres -▁Ägypten -▁Weihn -life -UP -▁Clin -def -▁thir -open -stable -schte -ayers -▁Vergn -ielten -adr -▁poly -▁Alm -ektors -seher -inks -strasse -▁Maur -▁gewid -▁berechnet -▁struggle -▁endeav -▁drast -▁begins -geschrieben -▁academ -▁printed -▁Besuchen -▁Photos -iegeln -▁Kooperations -▁Ergebnissen -fähige -▁Vertretern -▁Euros -verletzungen -▁geregelt -▁Veranstaltungs -▁Smart -▁Auftrags -▁angemessenen -▁layout -▁Appartements -▁anzup -▁emotional -▁Premierminister -▁Kohäsionspolitik -▁Sierra -▁Zuverlässigkeit -cow -öten -▁vine -▁wides -▁Dring -▁pilot -▁AF -esis -▁Ross -▁infolge -▁beunruh -▁Ly -▁Lic -▁stelle -▁Verwirklichung -izite -▁meets -usse -▁Wirksamkeit -staats -▁recording -▁US- -▁servers -▁profits -▁lasting -▁aktiven -▁promise -▁promised -▁richtet -▁irischen -▁bewähr -▁combines -▁entering -▁appropriations -▁audiovis -▁Visa -▁erklärte -▁panel -▁langfristigen -▁Spezialitäten -▁Bücher -▁iPod -▁Finnland -▁Jesus -▁intends -▁älteren -▁Ansprüche -▁wechseln -▁Kaiser -▁angewandt -national -uhl -ml -phrase -▁ré -▁Mode -orous -unes -▁toward -▁Anmeldung -arts -ipts -merz -▁soeben -▁traged -▁Aufzug -ermann -▁produces -▁empt -ettes -▁politisches -akei -haust -▁Übertragung -▁Sex -▁ausgegeben -▁zurückge -gehende -▁Bildschirm -▁budgets -▁referring -▁Fahrer -▁jedenfalls -leiten -▁wesentlicher -▁22. -▁illegale -▁Suites --Stunden -▁gefährdet -▁effektive -ifikation -▁urgently -▁Datum -▁Drittel -projekte -▁wachsenden -▁müssten -▁prospects -▁Ombudsman -▁q -hend -metr -DS -UB -▁bla -▁Düss -▁Dreh -▁Metr -▁Messen -eda -▁Call -▁Vent -▁unr -arl -gefühl -▁wirkt -apse -▁Prin -▁Obst -ehnte -▁Stor -▁dauert -▁vorn -▁Mitentscheid -▁arising -▁Chic -▁Level -▁opens -▁sole -▁72 -▁einzur -▁Conn -▁Schlacht -▁Trib -▁Amerikaner -▁steuer -planung -▁Wohnzimmer -▁Oberfläche -▁efficiently -▁Using -▁ecological -▁execution -▁Aussage -▁einstimm -▁kilometres -▁Entfernung -▁phenomenon -▁kernel -look -ped -KT -▁tort -▁tast -▁native -▁dial -▁Tot -▁Ster -▁gran -igger -dex -▁Labor -▁Zauber -▁verank -▁weisen -▁Annehm -▁proxim -oda -oco -▁frem -▁Errichtung -▁Einen -▁durchge -▁relates -▁Register -ström -atering -▁Strände -▁Have -iversity -▁surprise -▁feet -versammlung -bedded -▁oldest -▁steigern -▁23. -▁Edin -▁camps -▁Prozesses -▁widersp -▁anzuwenden -▁effiziente -▁Lastly -▁geographical -▁Events -▁mistake -▁Vermittlungs -▁Präsenz -97 -jan -▁hea -▁hills -▁meals -▁Snow -▁Dör -▁EW -edge -igl -unte -▁Cla -▁CEO -stab -▁Joint -oca -▁ausüben -▁Gross -ütter -▁Regulierung -▁freut -▁festlegen -▁Zucker --Bere -▁Monteneg -ziehung -▁Safe -▁taxp -▁seats --friendly -▁oftmals -▁maximal -▁schwierige -▁Staats- -▁records -▁scientists -▁Minderheit -▁Mandat -abrik -▁candidates -▁Modernisierung -▁occurred -▁acquired -▁strictly -▁medicine -▁humanitären -deckung -▁Explorer -▁Nokia -▁beeinflussen -üm -ket -▁mental -▁ET -▁Pier -iling -using -▁Halt -▁Fang -▁Roth -▁Vog -▁beherr -reut -▁depth -▁Jon -▁Oz -arket -▁Vorstellungen -▁Vorrang -▁liable -▁evident -▁bedau -▁Finde -▁Truppen -recher -▁specialists -▁verläng -▁resulted -▁Betreuung -▁wirkliche -▁taxi -ARE -▁essentially -▁Benutzern -▁bemerk -▁consequence -▁europäisches -▁Innovationen -▁Operation -▁Anwendungs -▁Sams -▁berühmt -▁niedrig -▁highlighted -▁succeeded -▁zeichnet -▁Komplett -grenzung -▁hervorragend -▁sophisticated -vard -lus -äsche -96 -33 -▁hence -▁nom -▁mouse -▁Ter -▁MED -etion -emble -inely -▁Cru -▁inakzept -▁beer -▁Lear -▁Norm -▁ON -ehol -▁Ank -antas -▁abgegeben -ountry -▁upgrade -etta -riage -hai -▁konzip -ionalität -arma -handeln -▁Haupts -▁Handy -▁Ratschlag -▁brother -▁operator -▁Versammlung -boden -▁zweitens -▁heading -▁öst -▁Außen- -▁vorliegen -▁virtually -▁affects -▁Weiterentwicklung -▁incorporated -▁wheel -mittelsicherheit -fakt -dates --pr -▁hidden -▁wom -▁wachsen -▁Sem -▁Mädchen -irn -▁kennt -▁Ring -▁Räume -▁RSS -▁unus -▁behält -▁prol -ceived -quet -▁conventional -rov -▁Anbet -anned -eins -▁vorzunehmen -▁Ausbau -▁Ausarbeitung -▁compuls -ärer -▁4-star -▁belg -▁aviation -▁gestr -▁considers -▁airports --Daten -▁choices -▁Betriebe -▁ansehen -▁Assozi -▁momentan -▁Teams -▁Jedoch -▁Doppelzimmer -▁italienische -▁gradually -▁Intell -▁diplomatic -▁Firefox -▁Department -▁Leipzig -▁с -mot --line -▁fach -▁dea -▁sammeln -▁mor -▁Eind -▁eg -▁een -▁Hu -▁Fig -▁GP -▁inher -anforderungen -ckst -▁gehör -ternal -aka -▁lebend -▁arbitr -▁warning -ettet -▁gewinn -▁gewählten -eams -▁Muster -uzzi -▁Behand -▁sky -▁Airlines -regierung -▁Repar -▁Cooperation -▁causing -▁Flughäfen -▁fehlen -▁blocks -▁erkannt -▁associations -▁Traditionen -▁Mosk -▁teachers -▁professionelle -▁angenehmen -▁Wassers -▁Stiftung -▁Jerusalem -▁sudden -▁Edinburgh -läufig -Press -▁rid -▁Tieren -▁Tenden -▁kop -icity -▁derer -▁Lud -stär -▁revenue -▁stick -▁1,5 -▁Zimmers -▁OK -▁Three -▁sphere -▁Erfordern -▁Spitze -▁Chile -fahrer -gestaltung -▁Orten -▁Übersicht -▁Internal -▁Constitutional -▁Gebühren -▁smart -▁fundament -atorium -▁environmentally -▁versteht -▁entfernen -▁preced -inery -▁kostenfrei -▁encouraged -▁Danish -▁troops -▁offiziell -▁producer -▁NICHT -regelung -▁attached -▁surprised -▁explanation -▁Children -▁Bibliothek -▁Vice-President -▁widespread -pm -kons -▁fly -▁Ts -▁BR -▁cott -▁Huss -▁Fan -▁Varian -▁beibeh -▁Long -rafen -ungal -ichtigen -▁erteilt -osc -▁verwir -oven -▁Ausblick -gerät -▁accidents -▁rough -▁Arbeitnehmern -▁reporting -▁ends -▁seeks -▁Zuge -▁Trend -▁12.00 -▁Emissionen -▁puts -▁stayed -▁Werden -▁adding -ART -▁participating -ariat -▁vollständigen -▁restricted -▁Premium -▁beeinfl -probleme -wissenschaft -▁prisoners -▁enthusias -▁beabsichtigt -gäste -▁täglichen -▁Authority -▁Siemens -▁heil -▁mort -▁bomb -▁Sort -▁Source -▁Ast -inity -ilder -igo -ching -▁liv -▁Fast -▁VIP -▁tourn -▁anwenden -istical -▁Anh -▁vorgelegten -▁plöt -örungen -anzierung -bares -▁20th -stellern -▁wooden --Al -mögliche --sh -ronics -äufe -▁Gemeinschaften -▁engage -▁colors -▁weltweite -▁alongside -▁contextual -▁Again -▁stories -▁Zwar -▁Terms -▁Innovations -▁Lieferanten -▁utmost -▁Florence -▁IMF -übung -▁Verfassungs -▁clarify -▁Philosophie -▁Vordergrund -cas --orient -27 -FT -▁trat -▁dim -▁dance -emann -▁Kampagne -▁LAN -▁Jazz -agog -▁Anze -adays -▁umgehen -▁enthalt -▁soil -etzugang -ackt -ystal -▁grown -▁geniessen -▁genaue -▁agents -▁serving -▁Unterkünfte -▁marks -ächer -ictions -▁Extra -▁geschäft -▁Conf -brid -▁flu -▁linken -▁bereitet -eague -▁kostenlose -würdig -▁aktiviert -▁Danach -▁gefährlich -streben -▁ehemalige -▁Serbien -▁graphics -▁Argumente -vereinbar -▁Listings -junktur -aos -wid -wiesen -HO -▁doppel -▁E. -ilation -ilateral -otokoll -▁Font -▁bewerten -▁prize -▁zuges -▁geblieben -▁spite -ausgaben -▁clock -▁upgrad -▁Revision -▁indoor -▁recreat -▁Sekunden -▁endors -▁capabilities -▁specialized -▁Shar -▁achievement -▁preserve -▁Treaties -▁finanzieren -▁permission -▁Solar -▁Außenminister -valu -▁Pflege -▁überwachen -▁entdeckst -▁quote -▁günstig -▁observed -▁micr -▁verringert -▁harmful -gehörigen -▁kontextuellen -▁Engineering -▁beseitigen -▁thumbn -▁Lithu -▁Annehmlichkeiten -kel -kle -▁s. -▁IWF -▁Psych -▁Bier -▁Bell -inians -▁Wa -▁reiter -▁erfahr -▁einseit -▁eingebracht -▁convention -pruch -▁'m -▁manchen -▁Einer -▁grünen -arbeiter -geladen -weitung -▁Desktop -▁know-how -ölf -▁Palma -▁schlimm -▁1988 -▁purely -▁permit -▁Bildern -▁recognize -▁Steuerzahl -▁erhöh -▁Beschluß -▁Index -▁Burma -▁quotas -▁Gemü -schlossenheit -▁südlichen -▁multilater -▁hinzufugen -▁grammatisch -▁convergence -▁kontextuell -▁pillar -aktivitäten -▁Riviera -▁Konkurrenz -zio -▁hundert -▁bug -▁Ehre -▁AU -eace -oments -▁Cuba -▁Vie -▁pra -▁Nizza -idas -schuss -leger -apital -▁manifest -ourse -▁Arc -örtern -▁Major -bres -▁gemeinsames -▁erwiesen -amerikanischen -▁passing -▁seconds -▁schließt -▁communicate -völkerung -▁aufgezahlt -▁Dokumentation -▁Schlusselphrase -▁inspired -▁beantwortet -euerung -▁armed -▁Copyright -berichterst -▁Gegner -▁dispute -▁eingeleitet -▁Mechanismus -▁digitale -▁Café -▁estimated -▁uneingeschränkt -▁verwendeten -▁Spaziergang -▁conferences -▁Tyrol -▁Š -.D --off -küste -VIS -▁vollen -▁Times -▁Bah -emed -itis -cho -▁Got -▁Gibt -▁Lernen -▁Nehmen -stock -schritt -▁aufzubauen -▁veranst -▁Anim -▁Schalt -▁sou -▁schm -▁nachfol -sprechend -▁composition -gericht -▁verbreitet -▁flows -▁halb -▁Schwar -▁absurd --Ein -▁nächster -▁innovativen -▁Denken -▁Wohnungen -▁Können -▁Contact -▁household -▁benachteil -▁Publikum -▁hilfreich -▁coherent -▁gegebenenfalls -▁verstehe -▁Valencia -▁gewidmet -▁Samsung -▁Anbetracht -SC -BA -▁Tus -▁Ever -▁Bav -▁lädt -▁Cann -▁Cred -▁Rett -▁GR -▁unber -anträge -▁Nation -oses -▁aufbauen -▁Verhe -lac -▁soul -▁Ausweitung -▁Bezeichnung -▁1950 -▁developers -inners -haltungs -aceut -▁busy -▁airlines -▁integrate -▁schwere -▁definieren -▁raising -▁Subject -▁stores -▁ruhige -▁Legitim -▁victory -▁veröffentlichen -▁licens -▁Multi -▁hinzugefügt -▁initially -▁reagiert -▁hochwertigen -▁Sektors -▁percentage -▁Akteure -▁Blair -▁keineswegs -▁prestig -▁Niederlande -s. -var -katastrop -▁dollar -▁Mul -▁Ped -▁B5-0 -▁Ach -itative -▁Wood -▁Lot -reicht -▁Vert -▁OLAF -iale -▁Stern -▁christ -▁dauern -▁schritt -▁schreck -enness -▁adult -▁clim -▁3, -acio -flow -▁Umstruktur -▁maybe -▁flag -▁Bestätigung -▁medieval -▁operates -gleichheit -▁Saal -▁accounting -INE -▁2.5 -▁sufficiently -▁Agenturen -▁irgendwel -▁Sowohl -▁hopes -▁landwirtschaftlichen -▁Erwachsene -▁Nigeria -▁telecommun -▁tschech -▁Souveränität -▁Düsseldorf -▁tournament -ciation -gäng -▁sitzen -▁Dein -▁Echt -undern -▁Km -▁Fassung -▁Vier -alon -▁LE -▁LCD -owth -▁ständigen -ischt -hersteller -ups -essed -▁einschl -▁Verab -▁Anf -▁Antr -ikation -ouses -▁Einfach -▁Einkauf -▁herz -▁herzustellen -▁guided -▁Minim -▁expense --Ben -▁promises -▁edited -▁besonderes -▁stattgefunden -ässer --Em -▁24-Stunden -▁enjoys -drucks -▁desto -fällig -▁Eurost -▁seemed -irlpool -▁Verbrauchern -instrumente -▁Vorsitzenden -▁Dringlichkeit --10 -▁Tamp -irmen -▁Wol -▁Wal -▁Wunder -▁Kh -▁Hekt -▁Ford -▁Virt -umes -▁forec -heiz -▁Zy -▁erheben -ippe -▁Ausbildungs -eiler -ergabe -▁recall -▁overw --Software -klasse -▁festen -▁survey -uxe -▁implications -▁Demokraten -▁Million -▁staying -▁studied --country -▁Werten -▁shortly -▁generell -▁Industrial -iberal -▁approaches -▁Deutschlands -▁broken -▁erkunden -▁Compl -▁Etik -▁professionellen -▁Objekte -üller -▁Champions -begriffen -▁Wednes -aceutical -mess -yles -66 -▁hect -▁nomin -▁fassen -▁röm -▁Cash -▁HP -▁Voy -▁Verkaufs -▁J. -▁Anders -▁atember -▁Erf -▁Erreichung -▁enge -▁Eines -▁climb -twoch -▁acceler -▁59 -▁interf -▁Prozeß -hton -▁crises -▁Entschlossenheit -▁berichtet -▁charakter -▁Vereinigte -▁Projekts -▁XML -▁Transl -▁booked -▁Karr -▁classical -▁zustande -▁elegante -▁audience -▁relationships -▁meaningfully -▁gradu -▁Anpass -▁extending -▁ökologischen -▁assigned -▁adventure -▁Dynamik --Bericht -▁bearbeiten -samen -zimmern -lingen -▁nutzt -▁bought -inea -inien -▁kill -▁Curr -▁Foot -▁GT -berger -estelle -▁exhaust -▁spann -▁schemes -▁Erfolge -▁Arena -flanz -▁Auflösung -▁Chat --Service -▁Order -▁believed -▁Ratifizierung -▁Step -▁Residen -▁confidential -▁render -▁Universal -▁Universitäten -▁genauer -▁Scott -▁Aspekten -ambod -▁Luftfahrt -▁Diagn -▁describe -▁Volkspartei -▁chinesische -▁accompany -▁eingeschränkt -üstet -▁Nichtraucherzimmer -▁Empire --Entw -▁Anmerkungen -▁palästinensischen -▁principal -▁Galileo -▁är -des -burger -cz -▁Ehe -▁Ehr -edite -inet -urities -▁Rena -▁LED -stil -▁Inkraft -ichtlinie -tech -▁aufrecht -▁Verzeichnis -▁-S -▁shoot -▁schauen -▁envis -▁Grem -sichtig -▁gelesen -▁geliefert -▁gestatten -▁eingest -▁Beste -▁langer -▁Neuer -wicklung -▁Unsicherheit -kontroll -▁Portal -▁Nutzungs -▁negotiation -▁Entwicklungshilfe -fertigen -wärtige -verbindung -▁Insgesamt -▁satisfactory -▁constitutes -▁Kompetenzen -▁eliminate -▁bürger -▁branch -▁Getränke -▁ingredients -▁Reparatur -zellen -45 -▁teuer -▁bul -▁Py -▁Pod -▁Kürz -▁Hab -▁Runde -▁Jur -▁chapter -formular -▁sucht -neiden -▁Nachmittag -▁owned -▁Employment -▁amended -▁erstes -kauft -▁organise -▁addressing -▁1986 -▁checked -▁Prof. -▁heads -▁Leiter -▁poorest -▁Panorama -▁29. -▁Ereignis -▁campaigns --to-d -▁Spring -▁medicines -▁unzähl -▁scheduled -stätten -▁gesellschaftlichen -▁grenzüberschreitenden -▁neighbours -▁surprising -▁Fremdenverkehr -▁schwedischen -▁Chairman -gorith -▁splendid -▁Soldaten -▁macro -▁Weiterbild -ping -KA -92 -▁häuf -▁foss -▁vibr -▁baby -▁IG -▁Eure -irable -▁lect -▁luck -▁kolle -▁Hold -▁Fle -▁FOR -▁Ruß -ulating -ulator -▁anerkennen -▁League -andin -▁erwerben -appe -ighter -▁auswählen -▁max -▁qui -bevölkerung -▁whereby -▁compliment -▁governing -▁Adri -▁Schwellen -▁bewahren -▁contextually -▁William -▁weaken -▁joining -speed -▁concentration -▁gemütliche -▁remarkable -▁enormen -▁grammatically -▁Slowakei -▁enforcement -▁Kommentare -▁Jewish -▁beibehalten -beln -IER -äub -▁halt -▁sorry -esa -ints -▁Künst -▁Fer -▁Renn -▁inhalt -▁erled -▁gegangen -▁whol -▁umge -▁vorüber -▁resc -hlung -▁Proz -ergel -▁herbe -▁technique -▁Seele -▁Kling -▁Intergovernmental -▁variable -▁Phys -▁Durchsetzung -▁hohes -▁violation -▁urgency -▁schriftliche -ECD -▁translated -kräften -▁Zweiten -▁widmen -▁Moscow -▁kostenlosen -▁Betriebssystem -▁ultimate -draul -▁Garantien -▁Tunis -▁verurteilen -▁ausländischen -▁imperial -▁Vermögens -▁Slovakia --Bereich -▁ease -.T -service -üche -well -pakt -OV -88 -▁fell -▁wil -▁wife -▁Treff -▁Sep -▁Dur -▁poker -▁Gelt -▁Print -essel -▁einladen -roc -▁Anonymous -undry -asso -ippen -ärme -▁Marco -▁europa -▁boasts -▁enjoying -▁zweiter -regionen -▁bereiten -▁Kontin -▁Sitzungs -▁bedroht -▁kleinere -▁berühmte -▁Massage -▁elektronische -▁Elektr -▁mittelalter -hetor -▁robust -▁Klarheit -▁incentives -▁Inflation -▁Pacific -▁Fisheries -▁empty -▁compulsory -cat -jing -5-00 -▁mere -▁moments -▁winning -▁pf -▁Migration -ieu -imental -ician -▁Ingen -▁erlassen -▁aside -▁Ox -▁residential -proof -osev -▁Konver -▁Chief -▁oppon -▁Rechner -▁undermin -▁Marok -▁incident -▁boats -▁employed -▁Malays -▁existieren -▁Betroffen -▁Modul -▁completion -▁Patten -▁Filme -▁kurzer --Prozess -▁ratification -cluding -▁preferred -zweif -▁Gemeinden -▁mixed -▁overlook -▁chairman -▁aktualisiert -▁maßgeb -▁girls -▁fascinating -▁Köln -▁Wednesday -funktion -uant -bec -37 -OD -ôte -▁drag -▁Hill -▁Fü -▁bezeichnen -▁Ut -ract -städ -▁erteilen -▁erarbeitet -▁vermittelt -▁conciliation -▁chat -▁umgehend -▁Weine -▁array -▁Leban -▁elev -▁solch -▁brill -▁integrierte -ötet -▁famili -▁fewer -▁foundations -ziehungs -▁references -▁marine -▁Select -▁Scot -▁unterschiedlicher -▁Designer -▁Empfang -▁pools -▁26. -▁Erzeuger -▁appointment -▁legitimacy -▁navigation -▁Krieges -▁electrical -▁deserves -▁cancellation --Entwick -andinav -win -cs -pg -▁deter -▁Together -emost -raums -bends -ingung -▁vertritt -▁Verf -▁Stimm -▁chief -▁sparen -▁spanische -rankungen -▁Mittwoch -▁Ausrüstung -▁cancel -becken -amps -▁Reicht -▁Youth -▁Unterricht -▁height -▁62 -▁External -▁Interview -ündeten -▁Besatz -peicher -▁portal -▁Grünbuch -ächtigt -▁Australien --We -▁headquar -▁reass -▁Relax -▁getrennt -▁Similar -▁falsche -▁Smith -▁freundliche -▁Peters -▁Missbrauch -▁Archiv -▁Mikr -▁Ursache -▁Heads --eff -OG -ни -▁dän -ilo -▁lingu -▁Hände -▁Rass -▁thres -kept -▁steam -plate -▁extract -▁Bergen -▁moreover -▁armen -anka -▁indigen -ieße -▁autumn -▁Ashton -▁incom -▁Entwickler -▁Ministry -ältigung -▁considerations -▁faces -▁edit -geworfen -▁Behörde -▁Fortsetzung -▁übr -▁Solana -▁Schulz -▁newsletter -▁Californ -▁umfangreich -▁Menschheit -▁authors -▁Reinigung -▁Wichtigkeit -▁observe -▁kombiniert -▁dictators -▁Beamten -▁sacr -mietung -▁timetable -▁Schicksal -▁thumbnail -zte -FOR -но -▁fühl -▁messen -▁D. -▁M. -▁Meilen -▁Publ -chmark -▁CAN -▁Häfen -▁Rhein -▁beweisen -quelle -▁erörter -▁Prest -▁coach -▁Ausrichtung -prozeß -▁Konzer -sträger -▁Haiti -▁begun -▁würd -▁debating -▁persu -▁typisch -▁wirklichen -▁Lebensmittelsicherheit -füllt -▁Series -▁Edel -▁Nordic -▁schwerwie -▁überwie -▁erkennt -▁false -▁effizienter -▁travellers -märkten -▁Aussagen -▁neighbourhood -▁Ferienwohnungen -republik -▁repeatedly -▁überrascht -▁Ministerr -▁inadequate -▁anzupassen -▁¶ -▁ärm -iop -fect -via -kammer -ögern -él -▁dual -▁sizes -▁vir -▁EB -▁Evalu -erce -▁theatre -▁Intens -ayout -▁einher -▁OECD -▁Sternen -▁escape -adel -▁grat -▁Yo --Server -▁Nachdruck -standard -▁exposed -▁eingetragen -▁secondly -▁secondary -▁favourable -▁sheet -ographie -▁analog -▁Milan -▁Krebs -▁coastal -▁genuinely -▁umfangreichen -▁innere -stechnik -▁befürworten -personen -▁Bosnia -▁journalists -▁Bestellungen -▁conscious -▁behauptet -▁unnecessary -eiden -aupt -▁tenden -▁bread -▁Mang -▁PV -esc -▁RE -▁vergeben -▁conj -schauen -ouren -▁Vorred -ologisch -▁proport -▁Program -▁Zeitplan -geld -▁versehen -▁geringen -▁187 -▁Ök -▁folder -▁öffentlicher -▁1985 -▁1987 -▁logic -ängerung -▁Konserv -▁Corporation -▁Chef -▁codecision -▁Wahrung -▁bearing -▁Bonn -▁verursachen -ampagnen -▁korre --2013 -▁Wünschen -▁Felder -▁History -▁Administration -▁neutral -sphäre -▁Lateinamerika -▁Kürze -▁Storage -▁Montenegro -▁threshold -namen -fisch -dring -zia -ça -▁Pilot -▁Ay -▁lively -▁lenken -▁Fans -▁foremost -▁Lor -▁einheim -antes -▁auswirken -▁Abbau -▁compete -akul -gerichts -immte -block -▁Diesen -▁evol -▁interne -onda -▁underlying -▁Weltwirtschaft -▁irr -▁featuring -voller -vollständ -fäll -▁characteristic -▁guaranteeing -▁28. -▁Einzelheiten -▁Logistik -▁Befehl -▁Domain -▁günstigen -▁unterbrochen -▁Weiterhin -▁speeches -▁listening -▁übereinst -▁geehrte -▁respektieren -▁cookies -▁Venezu -▁Vorgehensweise -▁‚ -jud -ña -▁sample -▁vit -▁Section -▁EIB -▁pod -▁pump -▁Migr -isode -orking -▁Wurz -▁Reit -iehlt -ierer -▁thick -▁Neg -schätzung -▁haz -▁stip -▁Jud -quote -▁managers -aco -iders -▁subsequently -ionale -▁economically -▁operated -▁ablehnen -▁Demonstr -▁Russlands -▁Zimmerbeschreibung -▁representing -▁respects -zession -▁informal -▁Guide -frecht -▁bigger -▁Freihandels -produkten -▁vorliegt -▁Alternativen -▁expanded -▁dramatic -▁Festplatte -▁Volkswirtschaften -▁Several -branche -PA -▁wed -▁Taten -▁Sah -iev -▁derselben -▁Nichts -ston -genöss -▁geh -▁Erl -▁Alps -spir -ologne -▁Plätze -▁asset -▁gelang -▁idyll -▁instantly --Fl -gehenden -▁bewegt --made -▁slopes -▁textile -▁planen -reserv -▁institutionelle -▁suggests -▁Statistiken -▁examination -▁CDs -▁E-Mail-Adresse -oleranz -▁Keine -▁arranged -▁günstige -▁frequent -▁gemütlichen -▁endgültige -▁erleichtert -▁SDL -▁Bekannt -▁Know-how -▁Offenheit -▁ausgestattete -▁Francisco -▁begegnen -normen -neten -funk -sburg -ра -▁tang -▁hell -▁diving -▁Mol -▁Mix -▁Mischung -isst -▁cod -▁Wen -▁FI -aton -amed -▁Lieb -▁seg -verhalten -illigt -▁Erarbeitung -obb -▁könne -▁Aufhebung -▁actors -▁Referenz -▁Leid -▁openness -getreten -▁invo -ionally -▁Übernahme -▁autor -▁importantly -▁Zielsetzungen -▁expenses -▁artific -▁terrible -viously -stehende -▁ALDE -▁kürz -▁Chancengleichheit -▁weakness -▁ownership -▁intentions -▁tatsächlichen -▁IBM -▁complaints -▁fortsetzen -.ch --a -lere -▁ring -▁mater -▁Sark -▁ED -▁Point -eatr -unen -derte -▁Line -stätigkeit -▁convers -illance -▁übermitteln -ads -▁compr -▁davor -▁Eröffnung -▁engen -▁propag -▁Rechen -▁realisiert -▁einzul -▁gestartet -geschwindigkeit -▁Richter -▁mediterran -▁Steigerung -▁breach -▁Object -▁erforderliche -▁Betrifft -ophob -▁devoted -▁flexibel -▁eleganten -▁Clubs -spielen -▁clicking -▁certified -▁willingness -▁sogenannte -ocktail -▁wunderschöne --Version -▁angekündigt -▁Morocco -gebäude -iary -vy -mant --and -emp -empf -ota -otion -▁körper -▁Wars -▁Kenntnisse -▁Citiz -▁Händen -arians -lein -▁zukommen -▁abgesehen -▁vorsieht -ecution -▁Ausnahmen -▁Einnahmen -hnt -ellence -▁Derzeit -▁zwölf -▁insufficient --Sh -▁damal -▁Geschlechter --Part -▁beruh -▁accessories -▁Musl -zugs -▁Palette -zustimmen -▁Nationalpark -▁empfängt -▁Profil -bestand -▁alternatives -▁Liberalen -▁purchased -ummern -▁Village -▁behinderten -▁vocational -▁arabischen -▁Nobel -▁excursions -ziger -wass -yo -▁fat -▁bay -▁DJ -▁patch -institutional -irts -▁WE -▁Ken -▁Gute -schicht -▁reward -ocation -▁Arts -▁upload -achtungs -▁Entschäd -▁Seen -▁Beschränkungen -insen -▁ideally -▁letters -▁destinations -▁Assist -fertigung -▁calendar -▁angenommenen -▁jointly -▁herrschen -▁sunny -▁sharehol -▁trouble -▁convenience -korea -▁Georgien -▁Fischereip -▁behavior -▁tragic -▁apparent -▁Bosnien -▁parlamentarischen -▁Tragöd -▁erstreckt -▁Gefängnis -▁simultaneously -▁pragm -▁jurisdiction -▁Verheugen -mine -wände -lan -OK -ко -▁dil -▁vacc -▁Tipp -▁Bart -▁Baden -utragen -▁CL -▁Cab -▁Halle -asien -▁tox -▁Ufer -▁Near -▁zugestimmt -▁dep -illon -▁Bez -▁clarity -▁arts -▁Mengen -▁technisch -▁carte --Man -▁berichten -▁Freed -▁deutliche -ätzungen -▁erstens -exp -▁Module -▁posts -▁accordingly -▁27. -felder -▁Immun -▁Kernel -▁erwähnten -▁erheblichen -ademie -▁verzichten -öpfen -▁diesbezü -ipeline -▁rubber -▁Zerstörung -field -pes -Hz -36 -OU -▁fur -▁fault -▁Tee -▁Distribution -▁Bünd -▁leiten -▁Rennen -angebote -anbul -alin -▁Nied -hebt -schrank -▁reb -▁Pros -▁constra -▁Versprechen -add -▁Arr -▁discl -▁transfers -▁konfigur -▁Country -▁Geschenk -▁langem -kunde -▁measurement -▁bewußt -fraid -▁Carl -▁Netze -▁distribut -▁obligator -▁Zurich -▁Südafrika -▁inspection -▁twin -▁Privatsphäre -▁1945 -▁motorway -▁üblichen -▁A4-0 --Dateien -▁Arzneimittel --to-date -giene -▁horr -▁vic -▁wage -▁Tief -▁Baut -▁C5-0 -▁Händler -atom -▁Inhalten -▁eröffnen -▁desktop -▁Storn -▁abges -▁spekt -▁Erhalt -▁plain -elta -▁composed -▁genießt -osex -▁commod -strand -▁Alltag -▁problemlos -schließt -▁buses -▁größter -▁refused -▁Rückgang -▁postal -▁Charme -▁interessanten -▁Empfänger -▁Southern -▁Werkzeuge -▁beteiligten -▁Gemeinsam -▁chemische -kurse -▁niedrigen -▁ursprüngliche -▁Thermal -▁spectrum -▁Sowjet -▁Ernst -soever -▁Gesichtspunkt --of-the- -▁Tampere -PG -▁dupl -▁mine -▁wächst -▁Pun -oning -▁KOM -▁Han -▁anpassen -▁Library -▁impr -ctor -▁rebu -▁convey -▁orch -▁Einkommens -▁Arzt -▁zurzeit -▁Größen -▁Growth -▁transaction -▁gemeinsamer -▁Saudi -ikanischer -▁collapse -▁steigt --Haus -▁Double -▁Patr -▁continuously -▁vereint -▁Vertrauens -▁Visual -eeing -▁eindeutige -OTEL -▁Palestinians -▁Language -▁certification --to- -▁komfortablen -▁occupation -▁stärksten -▁Ursprungs -▁fifth -▁strike -▁Governance -▁NEW -▁vermutlich -▁tragedy -iang -SI -lager -▁retr -▁mont -▁Ten -▁Mund -▁gym -▁Circ -▁Futter -▁unity -icars -▁tower -▁NRO -schiff -▁erweisen -▁gehabt -▁einiges -▁adm -▁evolution -▁produkt -▁recru -affe -▁66 -▁finds -▁driven -▁Bürokratie -ocket -▁Großen -▁modify -▁specially -▁revised -▁gebunden -zufügen --Lös -▁Protest -▁Fischler -▁eigentliche -▁Terroristen -▁zentraler -▁verabschieden -▁leistet -▁Finnish -ifestyle -▁forgotten -lieferung -▁Versicherungs -▁dieselben -▁Anzeige -▁California -▁riding -▁afraid -▁v. -▁Mid -▁Buen -▁ASE -elung -irat -▁cu -stücke -▁Intel -▁erarbeiten -tress -▁dieselbe -▁umso -▁Ausgrenzung -programmen -▁grass -eading -▁oppose -▁assert -▁Minibar -▁Brücke -anspruch -▁92 -▁advis -▁doubts -▁Gesetzgeb -▁parks -▁logo -▁herausragen -▁manufactured -▁Erfolgs -▁publish -▁2015 -▁Wiederherstellung -▁rechnen -▁falling -▁SMS -▁Otherwise -▁Wikicars -▁zugute -▁deadline -▁komplexen -▁Philipp -▁einzusch -▁Begründung -▁nobody -▁afrikanischen -▁Renaissance -zähl -yards -▁tip -▁Send -▁Pent -inema -▁Kost -▁unab -anch -verschm -▁eintreten -ibungs -▁allm -ubringen -▁abgest -▁Wireless -schlägen -igkeits -▁Repräsent -▁elder -▁Deep -▁knew -▁whatsoever -▁basically -▁sculpt -▁gelangt -bewegung -▁flash -holders -▁Formulierung -▁Wasch -klärt -▁Bewohner -▁verwiesen -ränkte -▁relaxed -▁promoted -▁Montage -▁Südt -▁eurozone -▁Updates -▁paths -▁Laser -▁Lasten -▁vermieden -▁Umstände -▁eingeladen -▁formuliert -▁österreichischen -▁Konvergenz -▁SH -▁pul -▁Peace -▁Cologne -▁Hyp -▁Fuer -ambres -gehalten -▁anf -▁prel -▁Len -▁stopped -istall -▁geplan -▁Ocean -▁dasselbe -▁plural -▁EU- -aufnahme --Sp -▁belongs -▁Art. -▁Problematik -▁Klang -▁verliehen -▁healthcare -▁renomm -▁expressly -▁regionaler -▁progressive -▁Zurück -▁Conserv -▁losing -▁affecting -▁jüngste -▁Brok -▁frequency -▁removal -▁landscap -▁Olympic -▁befürwortet -▁ausführlich -▁Nichtraucherzonen -▁gewünschte -▁Grafik -▁monuments -▁Gipfelt -▁Welthandels -kömm -▁dimensions -sucht -ville -bean --old -äußer -▁bicy -▁Differ -otograf -chester -▁Corn -asonic -▁Look -▁verschaffen -indliche -▁residents -▁unterscheid -▁up-to-date -staates -▁productive -▁geschmack -▁Klassen -▁Interpret -▁visits -▁moderner -▁170 -▁loans -▁focuses -▁Profit -verständnis -▁examined -ämpft -▁Jugendliche -▁Logik -▁Zunahme -▁arrangement -▁Fakten -▁Menschenrechtsverletzungen -▁vertraut -▁beef -▁quantity -azzo -▁gegenseitigen -▁transitional -▁affordable -▁ermutigen -▁pharmaceutical -▁Tarifa -▁Millennium --oriented -▁Verabschied -hause -TC -IO -like -▁Distr -▁Eben -▁Ell -▁Eleg -ignen -▁ohn -▁W. -▁Kohl -▁stead -▁städt -tex -▁aufgestellt -▁abgeben -▁aussehen -▁Einst -schlüssel -▁leichte -▁delays -▁suited -pressed -▁buying -▁profound -▁Stef -▁musikal -▁integrity -▁integrieren -▁Photo -▁defeat -verbindungen -▁schwieriger -▁declare -▁wave --style -▁Lissabon-Strategie -▁Funktionieren -▁geeigneten -▁kleineren -▁renewed -▁Verstär -▁accuracy -▁Ausbeutung -▁bedauerlich -▁Mobilt -▁Föderation -▁Bürgerbeauftragten -▁Verabschiedung -euert -rig -mail -bi -card -▁treiben -▁fein -▁wahre -▁Sri -▁pace -▁Bach -▁zero -▁KG -▁Cert -▁beher -entlichen -ereien -▁zugesch -▁vergangen -▁einstellen -ados -▁environ -ossier -▁relief -▁Defence -▁63 -▁Meinungen -▁Andalus -▁advise -▁folg -▁refuse -▁electoral -▁installations -▁distort -▁turns -▁broader -▁Superm -zeichnete -▁heutzutage -▁Pflichten -▁ausländische -▁medizinische -▁Syria -▁sterben -▁löschen -▁ceiling -▁Representative -hältnism -▁Abenteuer --Benutzer -tle -gelegen -XI -ún -▁shr -urer -chern -▁Wesen -▁Kuba -▁Hein -▁unbek -bern -rebt -▁segment -teien -ached -▁Theorie -▁Straft -▁Andr -▁ents -▁encr -▁durchzusetzen -▁sake -▁Left -änderung -anca -▁Finanzen -▁Produktivität -▁Handlungen -▁Baron -möglichen --Ges -▁magnet -▁logical -▁Planet -▁späteren -▁exhibitions -▁Bureau -▁sonstige -▁gerechtfertigt -▁Kommissionsvorschlag -▁observation -▁automotive -▁Kennzeichnung -▁ethical -▁literature -▁Kapazität -▁präzise -▁Partnership -▁Wälder -uhe -PM -▁Tower -▁Dest -▁Biot -usten -▁echt -▁cameras -▁Katal -▁bezogen -stone -▁Inhaber -▁gebe -▁Verfolgung -▁Verordnungen -▁deserve -▁Stufe -▁Angabe -▁chose -füg -▁Spiegel -▁wars -▁keys -▁bisschen -stellte -enever -kript -▁geringe -▁Arbeitszeit -lesen -ässern -▁teilt -▁Abgeordneter -▁Türen -▁mechanical -▁nachhaltig -▁Maced -cludes -▁stressed -▁architectural -▁granting -▁jeglicher -▁Möbel -▁endgültigen -bringung -▁Präsid -▁catalogue -▁Buddh -▁Französisch -▁atemberauben -▁ea -col -47 -ро -▁aren -▁mathem -▁bot -▁bist -▁pensions -esien -onie -▁Come -▁Rural -▁bewertet -heil -▁Inhalts -verantwort -▁verarbeitet -▁whenever -▁Anträge -eans -blatt -▁trigger -▁gesichert --Spiel -▁crow -▁basieren -▁brands -▁Genuss -▁18th -▁Shang -▁Beijing -▁Quoten -▁retten -▁amending -zusagen -▁researchers -ARD -▁Scient -▁höchster -▁publishing -▁Drittstaaten -▁ultra -▁painting -▁komplexe -III -▁duration -▁distinction -▁therapy -▁novel -93 -Über -▁tap -▁dokument -▁aqu -▁sailing -▁mol -▁mouth -itual -▁ok -▁ungewöhn -atem -▁Lanz -verteilung -verarbeitung -▁Prize -plus -oga -▁trains -▁gratis -▁Zusammensetzung -▁125 -▁Versuche -▁achievements -▁confident -▁pressing -▁regulated -▁Solutions -▁Bilanz -▁mittlere -wanderung -▁Summe -▁Gastfreund -▁constructed -▁Taliban -▁Bühne -▁Elektro -▁soldiers -▁wachsende -▁religiösen -▁GUE -kriterien -▁Ltd -▁newspaper -zusammenarbeit -▁prestigious -▁Marokko -▁Fischereipolitik -▁unterscheidet -hältnismäß -hens -nier -ozy -aug -mach -▁sach -▁DIN -▁gate -uns -▁lib -▁kan -▁künst -▁Kim -gekommen -▁tough -▁stops -▁asi -▁conversion -▁Jugoslaw -▁abwechs -▁Aluminium -▁1930 -▁overs -▁transnational -▁gestiegen -inahe -▁passieren -▁Bewerber -▁beneficial -▁empfiehlt -▁Verlag -zeichneten -▁maximale -▁Monti -▁Kernkraft -▁Capital -▁Istanbul -▁angebotenen -▁wertvolle -▁Klimawandels -▁Anreize -▁officially -▁cancelled -▁schedule -▁explicitly -▁certificate -▁blieb -▁José -▁proximity -▁unusual -hme -iago -funktionen -rolle -rale -zing --ch -▁fanden -▁füllen -▁Mis -▁Bean -elter -oter -▁ocean -▁lakes -▁Farm -▁beinahe -▁Niem -reform -▁Zin -▁aufeinander -wehr -▁clo -prozesse -▁Konjunktur -▁freight -▁noon -▁67 -▁delete -▁concerts -▁Gebühr -fortable -▁beschreibt -▁185 -▁populations -▁Abstimmungs -▁innovations -▁machinery -apping -▁Verlängerung -▁ruhiger -▁preparations -▁Deposit -▁territories -▁sandy -▁Administrat -▁doctor -▁surveillance -▁Spektrum -▁einstimmig -aj --g -FS -▁fro -▁biod -▁pays -▁Kör -▁Fuj -▁alp -▁Night -▁hab -▁Zimb -dens -indungs -annel -ocal -lair -▁spür -▁Beurteilung -▁dargelegt -▁arrange -▁elig -▁solo -▁Unterdrück -▁stretch -▁avo -▁smoking -▁publications -AND -▁short-term -▁movies -▁Betrug -▁identification -▁Zustell -▁focusing -▁aufgeführt -▁wünsch -▁Everything -▁ignore -▁Erdbe -▁undertake -▁Einwanderer -kapital -▁Slowen -▁vereinbarten -▁toilet -▁Osteuropa -▁entrepreneurs -▁Holland -▁Hussein -situation -gast -то -▁Mahl -▁Bow -unting -▁lets -arier -umed -▁angelegt -steil -▁2nd -▁dein -▁depr -▁geograf -▁Jav -anged -▁partition -▁Regard -neider -▁Menschenrechten -▁technischer -▁78 -▁88 -▁drives -▁centers -▁Autoren -▁typically -▁necessity -▁organisierten -▁employers -▁precious -ska -bereiten -▁prevented -▁greenhouse -▁Erzeugung -▁hyd -lassungen -▁harbour -▁premium -▁englischen -▁grundsätz -▁Referendum -▁beschleunigen -▁ausreichende -▁Palästinenser -▁Literatur -▁deterior -▁geplanten -▁ändert -21 -▁mächt -▁Dampf -▁passt -▁gal -▁CE -▁REA -▁unions -imo -▁haft -andem -▁nehme -▁übergeben -▁frustr -▁chea -▁trained -▁Konvention -▁Chir -▁interactive -▁roots -ouv -amilie -▁gelassen -▁geschickt -▁debated -▁eigenständ -▁signals -▁Shopping -▁inneren -▁Schutzes -▁Feststellung -▁favourite -▁civilian -▁überwacht -▁Beratungen -▁releases -▁Verfasser -▁split -▁preserved -▁Herkunfts -▁Individual -▁uncertainty -▁Belastung -▁suspended -▁Präsentation -▁Erwägung -▁Anwesenheit -▁gewinnt -▁spektakul -tin -facht -aud -ügt -▁nutr -▁Meld -isons -otes -ushing -▁Hügel -▁Fok -▁Gate -▁unknown -reien -▁einführen -▁jen -▁Jordan -ennbar -▁Ausübung -▁admit -▁360 -sekretär -▁vielseit -stru -▁bedienen -etreten -▁liegenden -▁Strasse -▁weiterentwick -▁Brut -kunst -▁organize -▁Energiequellen -▁Technologies -▁logistics -▁1979 -▁eigentlichen -aptop -▁verstärkte -▁Experience -▁adjustment -melden -▁Wiener -▁9001 -mechanismen -▁revealed -elemente -▁Ergänzung -▁Steuerzahler -▁telecommunications -KW -ét -▁wal -▁winner -▁Symb -▁Soft -▁Mug -▁Cra -▁RF -▁reibungs -vez -▁einnehmen -▁conform -▁Ankünd -▁attended -▁abstra -▁comprises -▁chron -▁auss -gebühren -zuarbeiten -▁gelingen -▁Intervention -▁Freil -▁Gewinner -pectives -▁organisierte -▁negotiated -▁spielte -▁237 -▁Museums -▁Deleg -aubern -▁beitr -ucks -▁sagten -▁Gebäuden -▁judge -▁ALE -▁Heiligen -▁Fremd -▁Non-S -▁photographs -rohungen -wirklichen -▁Picard -▁Beschwerden -▁senior -▁vorbehalten -▁taxpayers -tage -name -nisses -riet -kar -gio -ZE -▁ment -etschen -onz -▁Fri -▁Roc -▁Vit -▁VER -icits -▁verzeichnen -▁vergröß -ugnis -▁Europar -▁como -▁spare -▁unterwegs -▁Absch -▁realize -▁verbleiben -▁geleitet -▁Which -▁188 -▁Rahmenbedingungen -▁Gewinne -▁refurb -▁thanking -▁presidential -▁Santer -haushalt -▁Vertriebs -▁Verletzungen -aßnahmen -▁gelegenen -▁Panoram -▁schlechte -▁komfortabel -▁Einschränkungen -▁feedback -▁ehrlich -▁indicators -reisende -▁Heimatland -▁spätestens -▁ruling -▁Č -▁о -oen --e -klaus -OF -▁treas -▁fails -▁Tank -▁Sorg -▁Dun -▁Acht -oton -▁Comb -▁Letzt -▁ständige -▁erhol -▁Until -force -▁überlegen -ainer -▁entr -▁polnischen -etti -▁transit -▁redund -▁Indik --Br -liegenden --Kr -▁teilzunehmen -fällt -zubringen -▁eingeräumt -▁Terminal -▁illness -▁Tabelle -▁Datens -▁spiritual -▁jährlichen -strukturen -▁conservation -▁threatened -▁acquisition -▁FAQ -▁Madeira -▁reflection -▁hesitate -▁Phänomen -▁Non-Smoking -,7 -rungen -museum -CA -äuse -48 -▁rating -▁ranging -▁Sul -▁Document -▁Poss -edo -eaux -inischen -▁Rang -▁Gärten -▁derjenigen -▁regeln -ütt -conn -▁Chines -älte -▁Unterhaltung -▁insofern -▁77 -▁eigenem -▁industriellen -erba -▁certainty -▁Geschäften -▁stattdessen --Ex -hausen -▁Gegenwart -▁purchasing -▁ratified -▁ähnlichen -▁kohär -graben -▁sonstigen -▁erschien -▁Olympus -▁replacement -ülle -▁mistakes -▁medizinischen -▁supplementary -▁Darfur -▁academic -▁inakzeptabel -vin -war -äsch -▁Tob -▁Tues -▁Song -▁Bat -▁Aer -▁züg -▁Watch -▁FL -▁File -▁Rog -▁Gastr -üre -▁Leuten -▁Num -andelt -▁verwirklichen -▁schuld -▁vorgehen -▁Albert -▁Geben -▁imported -▁Flächen -▁begünst --Pak -▁Isol -▁Arbeitslosen -▁Küchen -leitungen -▁externe -▁demokratischer -▁downloaded -▁passende -▁Einschätzung -limited -▁oberen -ladung -▁NGL -▁Argentina -▁beruflichen -mechanismus -▁privileged -▁Spannungen -▁Inkrafttreten -▁Adriatic -▁Rassismus -▁Young -▁allmäh -händler -aft -bor -olen -▁Cav -▁Hi -▁Gil -▁NOT -ested -▁Präm -▁Verts -▁Anbindung -▁Erbe -zugehen -▁justify -▁Bridge -itzung -▁restaur -▁Neues -▁beschließen -▁Blumen -▁downtown -▁citizenship -▁irreg --Ebene -▁Feed -▁moves -▁generous -▁negotiate -▁farms -▁demonstrates -▁lokaler -▁gefährden -▁deaths -▁announce -▁silver -▁beliebigen -▁respektiert -▁geistigen -▁patterns -▁Excellent -▁horizontal --Gruppe -yssen -▁Swimmingpool -▁License -▁beeinflusst -▁Karriere --of-the-art -▁District -▁£ -.j -di -cast -AF -NET -▁telefon -▁flying -▁Mary -▁geden -▁Bin -ometer -▁Wärme -▁Cad -ieve -amar -▁angeschlossen -▁alike -▁stet -▁Zahn -▁notably -anto -menü -spät -▁Abschluß -▁Platten -▁Marx -▁nationals -▁Grundsätzen -bridge -▁signing -ündete -▁größt -▁XV -▁occurs -▁patents -▁ratio -▁Installations -beschl -▁Elev -büro -▁consistently -▁erfasst -vertreter -▁erneuerbaren -▁authentic -▁Conditions -▁Rußland -▁Scotland -▁ärmsten -▁öffnet -▁icon -taking -brechen -yers -▁sessions -▁boy -▁Sab -▁Etw -▁Paz -▁os -▁Face -ingual -▁Anna -anni -▁enrich -▁bestens -▁Herrschaft -▁450 -ologischer -fahrten -▁Unterlagen -▁lieben -▁Produzenten -▁phones -holfen -▁Freuen -versicherung -▁deficits -odex -wachsen -▁kompat -▁ernsthafte -▁verpflichten -▁Cala -▁zeitgenöss -▁claimed -▁orientation -▁mandatory -▁indicates -▁emerged -▁chemicals -▁Route -▁Rubrik -▁favorite --Rezeption -▁argue -papier -▁Konstruktion -▁Konzentration -▁С -zicht --Über -lov -kas -gibt -GP -▁DER -▁Pict -▁Gallery -ifferenz -achläss -▁depl -▁notified -▁Stich -▁Anhang -menge -ourmet -▁3.0 -aced -▁accum -▁alleine -▁recession -▁Entsteh -▁creature -▁airline -▁Besitzer -▁Andreas -▁Amerikas -▁investiert -▁folglich -▁closing -▁automatische -▁1984 -▁Vertretung -▁Israelis -▁returns -▁erweiterten -▁ausgewählten -▁heavily -ungssysteme -▁Innerhalb -▁apparently -▁Mehrwertsteuer -methode -▁clothing -▁bureaucracy -▁kontinuierlich -▁worrying -▁marginal -cor -100 -▁bild -inth -▁leather -▁Kä -▁FC -▁Veg -▁inject -rez -reben -teen -osh -abile -▁conventions -▁Od -illo -ungspolitik -▁schenken -▁Ens -▁cooking -▁embar -▁herkömm -▁specify -▁Exhib -▁vollem -▁prominent -▁contributing -▁Rücken -▁Superv -▁magic -ASS -▁Beschäftigten -▁fortgesetzt -▁Visum -▁effektiver -▁kritischen -▁Ordner -▁advertis -▁predict -▁jemals -▁Regionalpolitik -▁applicant -▁russische -▁Verteidigungspolitik -▁erbaut -▁Ibiza -▁displays -▁intervene -▁kollekt -pan -VO -Гј -▁funkt -▁drit -▁senten -▁Batter -▁Hir -ivate -▁Laur -opole -▁Opf -ehin -mente -prä -▁Thyssen -okol -▁Abteilung -ietet -örm -ratt -▁tram -alties -▁Unterzeichnung -▁myth -othing -▁wherever --site -▁refund -▁Turm -volles -▁kostengünst -▁Solche -▁Raums -▁zufäl -▁Santiago -▁Wachstumsp -▁unterbreiten -ocolate -▁archive -▁gewünscht -▁Kreativität -▁Merkmale --Datei -▁Subsidiaritäts -▁Treibhaus -▁bedauere -▁24-Stunden-Rezeption -▁Voyager -▁erörtert -tons -▁hinein -erz -erium -erzeug -onts -▁WW -▁Kam -iese -anas -▁bewirken -idance -▁haus -▁reven -▁eindrucks -▁abschließend -ausstattung -dernfalls -▁platz -schlüsse -▁Mazed -istency -▁directions -▁Technical -▁bewiesen -▁Junior -▁Direktor -▁affili --Ch -▁precision -▁Investition -▁Konsum -▁Visit -▁judgment -▁evaluate -▁drinking -▁angenehme -▁coordinate -▁churches -▁Syrien -▁withdrawal -▁tonnes -▁unanimously -▁beizutragen -▁gültig -▁converted -▁Schwimmbad -▁Alpha -▁plötzlich -▁Citizens -waren -HS -▁tag -▁BE -emperatur -▁Cathedral -▁Hub -▁Fünf -▁Rind -ürzen -▁Lar -▁imbal -▁reiz -verter -▁abzul -▁werfen -laß -▁3rd -ounder -▁financed -▁council -strö -▁weitaus -uerung -▁82 -▁Büros -▁formation -▁qualifications -▁Apache -▁measuring -▁Adam -▁negativ -▁Gerichten -iminary -▁preca -▁violent -▁demokratisch -iquid -▁experiencing -▁credible -▁intelligence -▁slowly -▁Elektronik -▁Slovenia -▁Kräften -▁caught -▁Anhörung -▁Lithuania -▁Lanzar -▁Zinss -hän -rück -pra -PP -▁ham -▁Bund -invest -▁Right -ali -entliche -stern -sters -sthet -apel -▁chambres -fenster -space -▁Presidents -▁systematic -▁placing -▁possess -▁Brü -▁gestellten -▁zweier -▁Gebir -▁Arbeitsgruppe -▁Paläst -▁Palacio -▁1.0 -▁Studios -▁potentially -▁fairen -OME -▁answered -▁grundlegen -betrag -▁Democratic -▁entworfen -▁premises -▁competences -kompeten -▁Jacques -▁tropical -▁Medikamente -▁Choose -▁Trainings -▁Vergnügen -▁Hektar -▁elderly -▁Shanghai -same -ägern -▁fährt -▁diet -▁Sien -▁Wett -▁Kod -▁Hum -▁Lob -▁Listen -schreiben -▁stell -▁voneinander -isch- -▁Proph -▁einander -▁übermittelt -▁ausgesch -▁Konferenzen -fahrts -landschaft -▁verbreit -▁scener -▁humanity -▁Ausschüsse -▁garage -▁begründet -grees -▁Touren -▁analy -bereitung -▁accommodate --hour -▁Funktionalität -▁initiated -▁Entertain -▁victim -▁timely -ungsvertrag -▁silence -▁Getre -▁allocation -▁AKVIS -▁sympt -▁Angeles -▁Vermittlung -▁Antonio -▁Hitler -▁Äußerungen -▁Hannover -tre -oge -▁flav -▁sq -▁bid -▁bearbeitet -▁pm -▁Mauer -onto -▁Kinn -▁Finger -▁inbegriffen -icer -▁UV -▁Low -▁Nag -▁Neph -▁degrees -▁gehandelt -▁Berater -schlägt -▁Force -▁keen -▁Mineral -▁gelernt -▁Controll -▁getötet -geschoss -▁visitor -▁beschreiben -▁creativity -▁approx -▁erworben -▁integral -▁Bewältigung -▁verwalten -▁keeps -ORT -▁normale -▁Verluste -▁Juan -▁Motto -▁copies -▁schlechter -▁Eigentümer -▁Gemäß -▁heated -▁melden -▁Consulting -▁imposing -,4 -ei --th -31 -▁dating -▁male -▁Six -▁Drag -▁Mü -igu -▁Galerie -▁unstable -ierbar -▁Luz -▁aufgerufen -▁entschied -▁Erne -udd -▁nachdenken -▁Vorfeld -▁Rezession -ismo -▁Überleben -ansprü -▁geschafft -▁Besteuerung -holz -▁gebildet -liegt -konzept -▁respectively -▁state-of-the-art -ärenz -▁lighting -▁kompliziert -▁Release -▁erwähnte -ützten -betriebe -▁Info -▁aktualisieren -▁Classic -▁konstruktive -▁therapeut -▁intense -▁procurement -▁israelischen -ubehör -▁headquarters -▁ohnehin -hatt -oup -bahnen -DEN -pat -▁fitted -▁wake -▁Müller -▁Buffet -iscr -usste -▁stance -▁stays -istor -▁geheim -▁Unfall -indlichkeit -gründen -ungsmaßnahmen -▁entries -ecke -▁partial -▁Aufruf -▁heft -achtungen -▁Internetzugang -▁assurance -▁exposure -▁angewiesen -okratische -▁Track -▁charter -gleichen -▁Dienstleistung -▁zeigte -▁posted -▁Sitzungen -▁1978 -▁workshops -▁Hochzeit -▁warmly -▁Eigenschaft -schwung -▁Dritter -▁endgültig -▁Weißbuch -▁gebraucht -▁arbitrary -▁Malaysia -▁biodiversity -,8 -f. -▁Sard -▁Pest -▁Au -igi -▁zones -▁Gn -▁isn -▁unb -berat -rec -▁erörtern -▁Ans -iller -odox -izar -▁ausl -▁Bezahlung -igned -ampen -▁bedür -▁facto -▁Schließ -geschritten -aturen -▁designer -▁schlicht -▁2007-2013 -uffs -▁sentence -▁brain -▁unterbreitet -▁talent -▁disputes -musik -▁appreciated -▁einzugehen -▁ältere -▁angeblich -ergebnisse -▁bilateralen -▁erläutern -breite -▁Cubase -▁municipal -▁Attraktionen -▁Yugoslavia -▁endeavour -▁schrittweise -▁virus -▁metr -▁ECH -▁Past -▁Patch -▁Bit -isy -▁köst -▁Fly -▁Rö -▁G20 -tritts -abkommens -▁verdeut -▁jüd -alling -ews -objekt -▁Konzepte -▁Change -▁Forest -▁Progress -▁realen -▁handled -▁findings -▁agrees -▁modular -▁terminal -▁Behaupt -▁Studium -▁Energiepolitik -▁rechtfertigen -▁finanziell -▁21st -▁erfolgte --Or -▁terrorists -▁Relations -▁1967 -▁Contin -▁gelegene -▁Panasonic -▁pursued -▁Atlantik -▁fragment -▁detaillierte -▁Bedienung -▁konzipiert -▁Venezuela -▁Sarkozy -▁tips -▁detr -▁maps -initiative -irms -olas -▁Wille -▁Hôtel -▁Gült -▁unbest -▁tow -▁Lind -▁stützen -estag -esthet -▁aufer -▁Verme -▁Vermarkt -▁Stoff -▁Schr -▁Scheitern -▁chamber -▁Abu -akischen -▁Vorgaben -uktion -cedes -▁gutem -▁modernes -▁perspectives -▁technologische --EU -inzips -▁Fluggesellschaften -▁Coast -▁suggestion -▁contractual -▁CD-R -▁pursuant -▁well-being -▁unemployed -▁Hyper -▁nötigen -▁Liban -▁eingebunden -▁essence -▁Subsidiarität -▁kommerziellen -▁beiträgt -▁irregular -,6 -hole -häusern -cend -pie -▁rear -▁merc -▁DR -▁papers -▁Mean -etts -▁CM -▁Ric -▁begangen -▁geein -▁einhalten -▁shot -outs -▁peripher -▁realisieren -▁stroll -▁Stadtteil -▁specialities -▁Abstimmungen -▁postpon -▁radik -▁coverage -▁Aktivität -▁lowest -▁Milosev -▁erinnere -▁eingerichtete -▁convince -▁Kanal -▁Auftraggeber -▁Gelände -▁photograp -▁erlangen -▁Schadens -▁registriert -▁vernünftige -▁Mittelmeerraum -▁Mikro -▁begleiten -▁grenzüberschreitende -▁scrut -▁paradise -▁Kapazitäten -▁artificial -▁Elevator -iate -viert -6-0 -▁dollars -▁mild -▁Dim -▁Pas -▁gam -otyp -unken -▁zielt -anteil -ibiot -▁desirable -ahrung -▁trail -itteln -▁solved -▁Orts -▁decent -▁Ähn -ereinander -▁Haushalte -▁Trends -▁Arbeitsbedingungen -▁equip -▁Steuers -▁Versorgungs -▁typische -gewalt --cent -▁Darin -▁practically -regungen -prüf -▁potent -▁Höhen -aneben -▁platforms -▁temperatures -▁Zuständigkeiten -▁Beobachter -▁Discover -▁exklusiven -▁beurteilen -▁örtlichen -▁klimatis -▁womit -▁Betroffenen -▁Tuesday -förderung -ucc -baum -▁viable -▁pil -emony -▁kids -▁Cold -▁Rac -ioned -atus -asste -▁mithilfe -▁Siehe -urcht -▁Spy -▁Griff -ruhe -▁crash -▁crops -▁Excel -ößt --Politik -▁Palm -▁blame -▁independ -▁Obviously --Leist -▁specifications -oberfläche -rechtlichen -apped -▁interessieren -▁Kunststoff -▁workshop -▁kritisiert -▁Prävention -▁Verhaltens -▁occupied -▁expanding -▁Soweit -▁Plenars -lossene -▁vernünftig -ocktails -▁gewisser -▁paradox -▁verbesserte -▁breathtaking -▁grundlegender -riffe -Tra -38 -NE -▁rust -▁mas -▁SCH -iry -▁lovers -▁Ci -▁Has -▁angel -ento -▁Ni -▁erad -verfahrens -▁aufricht -ego -▁Anreise -▁Eric -▁Parks -▁Alc -▁Review -alteten -▁transformation -▁bedauern -▁Entdecken -▁assured -▁76 -▁score -▁viewed -▁beauf -▁choosing -party -▁treaties -▁Iranian -▁falschen -▁describes -▁Active -▁laser -▁emerge -▁Entspannen -▁Communication -▁Zufriedenheit -▁disappointed -▁Lehre -▁Gemüse -▁Similarly -▁Vorredner -verschmutzung -o- -yen -▁IA -▁Serien -▁Fat -▁Göt -star -lywood -▁Inspekt -▁extr -ollah -▁ausführen -▁enchant -▁part-s -▁3000 -ektionen -aki -coni -steiger -▁Lehren -fahrzeuge -umenten -▁undermine -ilton -▁explains -▁receives -▁performances -▁Sales -▁Besuchern -bestände -▁Realisierung -▁Generaldir -▁Metern -▁herrliche -▁Tempo -▁COD -▁ratifiziert -▁schlechten --run -▁größerer -▁Präz -▁comparable -▁throw -helfen -analyse -▁zunehmende -▁Erwachsenen -▁erstaun -▁Ministerrat -▁Gipfeltreffen -brea -lieder -▁tight -▁rand -▁astr -▁Sher -▁Sau -▁DIE -▁Ara -onsten -omod -▁kenn -▁Certain -▁RI -▁Gol -atics -▁preg -eregel -ersatz -▁schreibt -▁vorhin -▁Erste -▁interakt -▁Leicht -▁konkur -▁Überarbeitung -▁Gebur -azin -▁Isra -▁angefangen -▁detect -▁globaler -▁technologischen -▁Folter -criptions -▁faire -▁biological -▁harmony -▁Organization -▁Kohle -▁memor -▁Broad -▁tätigen -▁einzuhalten -.2009 -▁thoroughly -▁Bewegungen -kapazität -▁unzureichend -▁Fuerte -ns -eres -berichte -▁befried -▁strange -generation -▁überd -ssicherheit -▁Ausnahm -▁Abstand -eils -▁commend -▁gesonder -staben -liness -▁develops -▁Marine -akten -▁Amaz -▁Marktwirtschaft -▁Techniken -▁voters -▁Guant --Länder -▁ansonsten -▁performing -▁Carr -▁erreichte -▁Instrumenten -▁Editor -▁Integrations -ECT -▁umfassender -▁honor -▁enlarged -ACK -▁Gestatten -▁unerlässlich -▁Beratungs -▁graphic -▁hairdr -▁vielfältigen -▁Taxi -opolitan -▁kurzfristig -▁Gepäckraum -▁Features -▁enttäuscht -▁Lanzarote -▁scenery -,2 -sq -medi -▁salt -▁I. -▁DI -▁zer -▁Kit -▁Hosp -▁Gulf -▁unbe -▁derartigen -▁rect -▁Zubehör -▁desert -indlich -einkommen -▁frucht -▁Schott -▁ausz -▁Ausge -ussen -▁grös -ährung -▁zwingen -ordnungs -▁zweck -▁irische -▁verwaltet -▁sensit -▁defining -▁settled -▁bevorstehenden -▁Nature -▁talked -zuschließen -▁Spezialisten -▁teacher -▁substantially -▁Angriffe -▁dauerhaft -greifende -▁meantime -athedrale -▁Schnee -▁Additionally -▁Sevilla -▁incredible -▁preliminary -▁avoided -wich -▁turb -▁bag -▁bien -▁Map -▁cease -▁kulin -▁Gift -aro -▁Layout -reformen -▁sees -▁frisch -▁frank -ausenden -▁unterhalten -▁finest -▁gesammelt -▁Ortschaft -▁inclusive -▁Trink -▁funded -uchtung -▁Short -sparungen -▁verwöhnen -▁Lebensmitteln -▁cheese -▁swift -▁Freiwill -▁erzielten -▁emphasised -▁Simb -▁fehler -▁Kompromiß -▁Kreditkarten -▁partnerships -▁incorrect -▁meistens -▁centrally -▁photography -▁Communist -▁Vorsitzender -▁Division -▁sweet -▁erläutert -▁bekräftigt -agentur -▁Total -▁Zimbabwe -bearbeit -gert -▁feas -▁SY -igs -ofür -▁ce -▁cat -▁kre -▁unbeg -anschluss -quest -▁Intr -ingham -▁weib -▁jew -rock -▁Stell -▁Anregungen -etze -ätigen -akings -▁Aufforderung -▁Parliamentary -everkehr -▁screens -▁monat --Med -▁klären -▁signature -▁sensor -boot -▁Benj -▁Speed -▁testen -▁1975 -▁namens -▁Colomb -▁loved -▁südlich -▁Superior -▁Verkäufer -▁Stabilitätspakt -▁mündlichen -▁Wolfgang -▁begeistert -▁Advanced -▁Weiterbildung -▁Lebanon -▁Israels -zo -vens -▁fung -▁aux -▁Dich -▁piano -▁god -▁CAM -▁Fas -▁Fab -▁Rande -oping -schwe -tends -▁aufgel -▁joy -▁Ok -▁Along -akis -befinden -▁agent -▁Aufnahmen -▁trem -▁interinstitutional -▁71 -▁angehören -▁neglect -▁Lösungs -iebt -▁teilgenommen -legte -▁Hotelsafe -▁Verantwortlichen -▁devast -▁consequently -▁Jugendher -▁Everyone -▁worthy -▁Iraqi -▁birds -▁registrieren -personal -▁Tschechischen -▁Jackson -▁Optimierung -▁tribute -▁Aktualisierung -▁Regulierungs -▁Moskau -▁Unterdrückung -eigen -sm -TEN -bele --tra -lor -UC -öte -▁hielt -▁rivers -▁Bull -▁Wellen -▁GS -alian -▁angeben -▁NH -abet -▁Stick -▁shoul -▁span -geräten -acken -▁solide -▁Flag -▁Whirlpool -▁Monats -rechnung -▁Quell -▁Darauf -▁traditionell -▁Lebensqualität -▁Feind -▁Aires -▁nonetheless -▁expressing -▁1982 -ELL -▁Danke -▁Socialists -▁Reaktionen -▁freundlichen -▁Help -▁entscheidet -▁celebrate -▁strenge -▁keinesfalls -▁sincer -▁Medikament -▁exchanges -▁Milosevic -▁vorteil -▁Tos -▁BL -utze -usa -▁Wales -▁Kant -▁CV -▁Hart -▁Gü -▁forese -▁anzeigen -▁UC -ständen -▁aufrechter -▁mitgeteilt -▁Jeden -rease -▁spiegelt -enna -ateur -astet -▁About -▁herstellen -▁mineral -▁drücken -▁gelö -▁measured -▁arbeits -▁raises -▁Subventionen -▁Juden -▁winds -datei -▁Einzelnen -▁sporting -▁automation -▁controlling -▁Mercedes -▁acquire -▁Queen -▁paintings -▁Schattenberichterst -imensional -▁pedestr -▁aspir -▁Gewerkschaften -▁Litauen -▁controversial -▁Buenos -PR -éd -▁tor -▁hors -▁MD -▁Aust -eli -▁Heating -leister -lebens -agten -mert -ueber -▁admitted -▁hiermit -▁guitar -▁sonn -geschneider -▁standen -▁Vereinigung -▁Round -▁statistical -▁confusion --Dollar -▁schließe -gedeckt -▁Compet -▁1.2 -▁disturb -schutzes -▁peak -▁idealen --Hour -▁vereinfachen -▁Declar -▁discrimin -▁cosmet -▁conveniently -▁gegenseitige -▁declarations -▁Oslo -▁isolated -▁capture -▁ausgelöst -▁Legislativ -▁Victoria -▁battery -▁на --Format -▁Mazedonien -hot -▁sail -▁wages -▁MID -ilia -▁Hence -assten -amo -▁Lido -▁Luggage -rection -ichtungs -▁negl -▁spüren -▁Ernen -▁Speich -▁compare -▁finances -▁diskrimin -▁emission -▁propriet -eopard -▁themes -▁bedenken -▁crystal -kredit -▁formula -▁angehen -ständigkeit -▁deutscher -▁schwarz -▁tempt --19 -▁jeweilige -▁relevanten -▁INTER -▁Anwendungsbereich -▁licence -sprochen -▁fears -▁watching -▁Einschränkung -▁executed -▁Gerichtshofs -▁Cultural -▁Amtszeit -▁Wartung -▁Zertifik -▁irgendwelche -▁Echtzeit -rägen -uan -zert -üten -44 -▁dust -▁Tap -▁Sech -▁pact -itudes -uner -▁Hör -icop -▁Null -▁denk -agger -anna -ocratic -▁shame -▁schützt -▁Spuren -▁genetic -▁zwanzig -venth -▁damaged -▁incur -öffnete -▁geltend -▁parameter -▁getest -▁24-Hour -▁Guinea -▁lesson -▁regulate -▁Verträgen -ija -▁analysieren -▁drafting -▁Protokolls -▁undertakings -etooth -▁Stadium -▁belonging -▁ignoriert -▁Ltd. -▁acknowledged -▁nutzbar -▁Beauty -▁Zimmerservice -▁REACH -feit -dokument -HR -▁fate -▁setzte -▁Ecke -▁gingen -▁Bom -ols -unately -▁eure -▁Chal -▁unpre -anes -▁Luk -▁Liquid -▁zukunft -▁deinen -▁jeopard -▁chairs -▁auswärtige -▁spots -▁Ein- -▁Vorsicht -flüss -▁gesunden -▁este -insula -▁Palazzo -▁bewaff -▁multimedia -▁UND -ifizierungs -▁unverzicht -▁einzelner -▁wenigstens -verband -▁1968 -▁Provence -▁engineers -▁Columb -▁tells -▁Wohlbefinden -▁ignored -▁Erkenntnisse -▁hoped -▁erneuerbare -▁schrieb -▁Macedonia -▁Kinnock -kont -▁row -▁kauf -▁Kohlen -▁Cong -▁inkl -deb -stritten -▁Inv -▁exert -▁abund -ensible -ewall -aca -▁beider -aks -gebühr -altete -arbeits -▁Übersetzungen -▁begab -ocken -▁Blizz -▁equity -fassungs -▁Beton -▁Display -▁challenging -▁Fernseher -▁1976 -▁volunte -▁characterized -▁kompletten -▁eingerichteten -▁gefährlichen -▁gefährliche -▁Summer -▁Kohärenz -vorsitzen -▁kritisch -▁Palestine -▁diplom -▁Kindes -▁Ursprung -▁fulfilled -▁prohibited -▁Konsultation -▁genehmigt -▁Credit -▁Migrations -oard -▁hüb -▁fünft -▁squ -▁wartet -▁wöch -▁Ehren -▁gall -▁gele -▁graf -▁Bisher -▁lif -▁Kult -▁Klin -▁Fift -▁Ron -▁Viol -atile -▁congress -endous -▁exklusive -▁Ausstellungen -igerweise -▁internationales -▁Marb -ancies -demokraten -▁Finance -▁scandal -comings -▁flowers -▁modest -zugreifen -▁Close -▁mirror -ilitation -▁Bewertungen -▁Spanisch -▁downloading -▁Eingabe -▁strafrecht -▁Einwohnern -▁branches -▁verschiedensten -▁incentive -▁Clinton -katastrophen -▁übrig -▁Tragödie -▁environments --hand -QU -▁nennt -▁fought -▁TNG -▁pin -eare -emma -oro -▁prere -▁2,5 -▁Jak -rooms -▁demographic -▁Thai -format -schloss -airo -▁endet -▁Weltbank --Mittel -▁brows -▁Manhatt -▁erwart -▁zurückkehren -▁149 -▁Computers -▁thoughts -▁offenkund -▁sheets -▁login -▁äußerster -ibraries -▁warming -▁Gesprächen -▁regrettable -▁riesige -kontrollen -▁ökologische -▁ungeachtet -▁unlike -▁engagiert -▁Products -▁brutal -▁Erweiterungs -▁LateRooms.com -▁mixt -▁Chicago -▁Prestige -▁hazard -.6 -TH -kind -AY -▁Matt -▁Avenue -▁zert -▁Rollen -▁unfortun -atin -▁Lom -iferation -abeth -▁übersetzt -izenzen -▁schol -▁Spar -ärt -▁embry -isms -elson -▁guidance -▁Herberge -▁publicly -▁Hinzu -höhe -versicher -▁Quick -▁Geräten -▁excellence -▁blank -▁shortcomings -▁Christen -▁arbeitete -▁foods -▁Entwicklungszusammenarbeit -ORE -▁Privacy -▁translations -▁complexity -▁twel -unkel -▁Moderne -▁abgehalten -▁condemned -▁Army -▁survival -▁längst -▁bureaucratic -▁Verstärkung -,3 -WE -▁trennen -▁Sac -eaking -omaten -▁Wäsch -▁Cro -▁Rav -▁VI -▁stem -▁sech -cket -▁neol -▁Ora -ehler -▁notion -▁Auslegung -▁outset -▁alleged -▁socio -▁einziges -▁86 -▁Manche -▁gemessen -▁Rahmenprogramm -▁founding -.com. -▁markt -wärmung -▁structured -▁Charakt -▁Norway -▁circuit -▁Holy -▁monitored -dateien -▁kritische -▁Budgetary -▁ethnischen -▁Ratsvorsitzes -▁benachricht -▁umweltfreund -▁zunehmenden -▁Horiz -▁Sozialdemokraten -irtschaftung -▁gefällt -▁Entertainment -▁Libanon -messe -messer -kov -Gu -▁gren -etus -etime -igter -onenten -▁lad -▁lohnt -▁Gumm -▁thread -▁LD -idges -▁styles -▁Indem -ifi -▁aufweisen -▁einbe -▁shut -ätter -▁Vorbild -aufgaben -▁guilt -▁geleg -▁accepts -▁Climate -ähern -▁Finanzmittel -▁wirkungs -▁Dienstag -zusprechen -▁returning -freiheiten -renzung -▁Color -▁vorherigen -▁vorherige -▁subsidiary -▁klassische -▁embra -▁beträchtliche -▁Alpine -▁Indonesia -▁transatlantic -▁Anhänger -▁conjunction -▁abwechslungs -▁Declaration -right -wen -▁suff -▁muy -elen -▁Horn -▁Ri -▁inquiry -icas -▁angesch -▁reverse -aba -▁verp -▁verhalten -agl -ogan -▁Aufwand -meid -▁realized -▁invitation -▁Seb -▁beschäd -anders -▁optical -athon -▁votre -▁Betrugs --Cl -▁checking -▁starkes -▁follow-up -▁Statistik -▁lacking -▁Korean -▁Silber -▁Wahrnehm -▁sharp -▁rejection -▁kooper -ägigen -▁Nonetheless -▁gegenseitig -▁Seminar -▁autonomy -▁diagnost -▁immense -▁durchgeführten -▁overwhelm -▁Rechenschaft -äußerung -oire -damer -bten -kürz -öpf -Fi -▁tact -▁SIM -▁pione -▁WM -aner -schra -▁denselben -agert -▁demonst -▁Ausfahrt -▁Eintrag -▁Algar -▁3.5 -▁traur -itären -▁Steu -▁Medical -▁Touch -▁ernste --Distribution -▁searching -▁departments -▁Ostsee -▁incorporate -binsel -▁Schiffs -▁verehrten -▁üblich -▁Fertigung -bringt -▁survive -▁hilfs -▁zuverlässig -▁GBP -▁Lokal -▁Konfiguration -▁Leidenschaft -▁niederländischen -▁Clearly -▁gastronom -▁torture -▁Slowenien -▁Entstehung -▁unfortunate -dos -uhr -mischung -borg -kord -▁sy -▁sul -▁codes -▁Kel -▁Kra -▁Kalender -▁CA -▁Vil -alp -▁anx -▁anwend -▁angere -▁aufgehoben -adium -▁upcoming -aterie -rikt -▁guides -▁dress -▁delicate -▁Neub -▁governed -clusive -▁Geschäftsbedingungen -▁bekanntesten -AST -▁Flugzeuge -▁separately -▁sämtlichen -▁Initiativ -▁Sometimes -▁Potential -▁Absichten -▁wirksamen -basis -▁amtierender -▁zeichnen -▁abandoned -▁tailored -▁Devisen -▁aggressive -▁Ernährungs -▁Eurostat -▁Palästina -▁Siena -bro -kong -28 -LR -▁agr -▁T. -▁Age -eling -▁lights -▁derived -entren -rey -rade -rage -stunden -ernet -▁verhandeln -ears -izität -▁vorzusch -▁unteren -▁Spons -emberg -▁Plast -hafen -▁striking -▁rede -▁reduces -▁feels -ivileg -▁defects -▁Transaktionen -▁zahlreicher -▁Comput -▁sofa -▁Peru -▁hosts -▁characterised -▁Oberflächen -▁Mittler -▁exceptions -▁Grande -▁Architekten -▁Flüchtlings -▁vegetables -▁Albania -▁tolerance -▁enthusi -▁verständlich -▁seminars -SD -kw -kest -▁tat -▁tiss -▁pine -▁MT -▁Bast -erhalten -▁G8 -dez -▁yellow -▁Lav -▁Love -▁Lärm -streit -▁220 -vertra -▁Verhältnisse -▁Anruf -einheit -ikalien -ourismus -▁Vorstand -iously --Seite -▁setup -leih -▁exploring -▁Working -automat -▁Guard -▁glaubt -▁loud -▁akzeptabel -▁penalties -▁Harbour -▁simplify -▁innoc -▁celebrated -▁vergleichen -▁Perspektiven -▁ältesten -▁taught -▁Ernährung -▁beeinträchtigt -▁trademarks -▁discipline -▁Bruder -▁Niemand -o. -26 -▁fut -▁rar -▁abend -▁sont -▁wofür -▁DM -isol -onischen -▁Kaz -▁Kathedrale -▁CC -▁HOTEL -aman -▁Lust -terr -▁Anweisungen -▁absch -▁ausgelegt -▁wording -▁Parallel -▁Eintritt -ignano -▁regimes -▁grants -▁indication -▁Bedingung -▁einzube --Be -▁boom -▁binden -▁receipt -▁absor -▁Koordination -▁idealer -▁Tourist -▁Carib -▁Textes -zentrums -▁biotechn -▁automated -▁längere -▁Temperaturen -▁Verzögerung -▁jahrel -▁recycling -▁portugiesischen -▁überwiegend -▁Benjamin -wäl -▁tub -▁telling -▁vend -▁m. -▁E-C -▁BC -emissionen -chi -ifer -platten -▁Schre -▁chocolate -rane -▁Einfuhr -▁Spam -▁Above -bey -strichen -▁Mars -▁61 -▁Haust -▁93 -laubnis -▁gefangen -leiht -▁sicherer -otti -▁175 -▁accepting -zufinden -▁Karel -▁Norwegen -▁schönes -▁circle -▁lebenden -▁Building -▁auszuüben -▁befürworte -▁eingereichten -▁Religions -▁DS9 -▁friedlichen -stätte -▁racism --spec -disziplin -▁Minimum -▁Currently -▁einzubeziehen -ière -sinn -ücher --economic -kal -▁hr -▁dish -▁greifen -▁corn -▁Fine -▁Ress -▁LA -schalter -▁Ansprech -endigung -▁Scha -▁coinc -▁Abonn -▁interaction -▁Rechtsrahmen -▁editing -▁Shuttle -▁Berichterstattern -▁Haushaltsplans --Ländern -▁broch -▁einzelstaatlichen -▁beitreten -▁vernachläss -▁Kurse -▁width -▁preferences -▁Ereignissen -▁quota -▁Doppelbett -▁konserv -▁officer -▁Limited -▁berufliche -▁beseitigt -▁Cathol -▁Naturally -▁configure -▁glücklich -▁Fragestunde -▁Gefangenen -▁verankert -▁Sowjetunion -▁landscapes -▁cheaper -▁д -SL -cul -BER -▁Mi -▁Pakt -▁Hass -▁Lied -▁denied -verz -weichen -▁'d -▁durchsetzen -▁trails -▁Grill -▁zwing -▁zwangs -▁Gefü -▁interim -▁continually -▁modes -▁ensured -▁responses -▁Ökonom -▁schlimmer -▁bewundern -▁sensib -▁Fehlen -▁Euror -▁Deluxe -▁drafted -▁Magic -▁Danube -▁badly -▁undertaking -▁Tschetschen -▁Besonderheiten -▁Origin -▁krimin -fuhren -▁Randlage -▁Navigation -▁navig -▁einzigartig -iesslich -▁Bluetooth -▁Komplettpreise -▁shareholders -▁Marbella -▁Manhattan -design -uay -bot -NS -ре -▁tur -▁dell -orate -▁Wörter -▁Future -▁unlimited -▁Lodge -▁stood -▁Stay -▁schlage -▁Erwer -▁unterliegt -▁EU-Ebene -blas -▁superm -▁embedded -▁Heritage -▁Zusammenbruch -▁reductions -▁128 -▁long- -▁combining -▁Buchungs -▁sustained -▁minds -▁Wahrschein -▁e.V. -▁Bali -▁zerstören -▁assumed -▁doctors -▁reinforce -▁API -▁Internetseite -▁abzielen -▁personenbezogenen -▁infringement -▁Analysen -kirche -▁Vogel -▁verwirkl -▁reiterate -▁imprison -▁Stefan -▁walks -▁iz -oque -fa --by -p. -▁dos -▁diox -▁missions -▁bio -▁MPEG -▁PM -▁oste -▁Heat -racies -stark -keeping -▁reine -abschluss -▁diesmal -mengen -▁vorbe -ianische -▁evil -▁contest -▁gleicher -▁bescheiden -▁Freien -olidierung -▁16th -▁verstoßen -▁Hierbei -▁multipl -▁Allein -imini -▁Gründer -▁Tatsachen -▁Crist -▁circulation -wohnt -opolis -IVE -▁Strecken -▁quantities -▁irgendeiner -lechts -▁Felsen -▁Auseinandersetzung -▁malerischen -▁Einfluß -▁Opfern -dest -reme -wunsch -lets -▁tension -▁tiny -▁hire -▁finger -▁Tol -▁IL -▁Divers -▁ME -▁Could -▁Castr -▁Gelegenheiten -asma -▁Lande -▁Zuer -▁errichten -tein -▁gekauft -▁spont -▁Befrei -▁Spek -▁Berlus -▁zumal -ieweit -aktions -othic -▁deleted -▁debatt -▁requiring -▁Montr -▁boards -▁Finanzmärkte -▁zusammenzuarbeiten -▁stehende -▁participated -▁Gegenstände -betreuung -▁Hunger -▁Völk -▁isolation -▁ausgewogenen -▁fuels -▁EVP -▁Assoziierungs -▁Tendenz -gerichtshof -▁random -warten --12 -kin -го -▁doppelt -▁rasche -▁Softw -▁Piet -asa -▁derog -▁Lul -schten -▁verleiht -änn -obyl -▁Beendigung -spflicht -▁doubl -gery -▁interinstitution -▁consistency -▁underline -▁Frühl -▁belegt -▁assembl -▁Kommissare -▁Lae -▁explos -▁defending -regierungs --Vor -▁Partnerschaften -▁Entschließungen -ächtnis -▁Salv -▁weißen -▁moderate -▁Untersuchungs -▁erschienen -▁simplification -▁stärkeren -▁adequ -▁calculated -▁Schmid -▁SSL -▁Peking -▁transformed -▁nördlichen -▁Stattdessen -▁einzurichten -▁nic -▁Sak -▁DH -▁Rak -▁thin -▁pron -▁Nan -ifiable -▁concess -endem -▁Schienen -▁daughter -▁vorsichtig -ssektor -▁Maison -▁bereitz -▁Deine -▁Quer -▁struggl -▁geladen -▁continental -▁measur -ulevard -▁startet -▁besetzt -▁Elite --Team --Technologie -▁Zahlreiche -▁klarer -▁motions -▁Fundament -▁grundlegend -▁consisting -▁resorts -▁listened -▁Annäher -▁boxes -▁Outlook -schuldigen -▁Anreiz -▁vielfältige -▁newspapers -▁japanischen -▁Verzögerungen -▁enemy -▁Eurex -.jpg -▁aufrechterhalten -▁Algarve -harmon --level -▁nos -▁aesthet -▁vierten -▁Treiber -▁gäbe -▁Rosen -▁Rüst -▁Vot -iewer -▁alliance -aded -eart -▁ausb -▁untergraben -aciones -otherap -▁besticht -▁4-Sterne-Hotel -gespr -rio -▁Üb -▁Überrasch -▁bases -▁endless -▁handic -▁94 -▁Genf -▁presentations -▁commonly -▁large-scale -ässigen -▁Industriel -▁1981 -▁identical -▁Nichtregierungs -cribed -▁announcement -▁concentrated -▁Inneres -▁investigate -▁sightseeing -▁clothes -▁fabric -▁culinary -▁Jugoslawien -▁wünschenswert -▁Ankündigung -▁Sorgfalt -tains -fig -wieweit -kok -Pro -OX -те -▁Sz -▁ov -▁Kup -▁Hell -estone -▁geg -▁Week -▁besteh -▁Dekor -▁Herange -▁Kloster -▁1500 -▁Arbeitsweise -holung -▁Autover -▁gebilligt -gewinn -▁Formel -▁einmalige -▁respecting -▁Werbe -▁homepage -▁schnelles -▁plane -▁neueste -▁unverb -▁weekly -▁crossing -▁Micro --Stand -▁Local -streicht -▁Vorbereitungen -▁anbietet -▁Libya -▁officers -▁Begriffe -status -▁Übersetzungs -ungalows -▁Virtual -▁vicinity -▁bereitzustellen -lements -▁hus -▁seal -▁gäng -▁Nep -▁repress -ernobyl -▁geholfen -igende -▁maj -▁Regimes -▁freier -▁Reb -elev -entrums -mitting -▁ending -▁cargo --Auf -▁Freie -▁gefasst -▁retir -▁corpor -▁districts -▁colonial -▁verschieben -▁verschoben --15 -▁vereinfacht -schnitts -▁publisher -▁furnish -▁Reader -beteil -▁connecting -▁penal -▁Senior -▁permanently -▁zukünftig -▁Granada -▁vertrauen -▁humans -▁gesellschaftliche -▁konstruktiven -▁Stabilitäts- -▁Henry -▁Namens -▁Reihenfolge -▁Implementierung -▁enthusiasm -map -KS -▁nament -▁bond -▁banned -▁Sus -▁SAT -▁EMU -▁grew -▁ASP -▁cens -owed -▁habt -▁refined -roökonom -▁Anzeigen -▁gesunde -cedented -▁Flüge -▁87 -▁drängen -▁prejud -▁restoration -▁begreifen -▁attent -▁führten -▁Heidel -▁Tagungs -▁optimum -▁Christdemokraten -bestimmung -▁analyses -▁Königs -ministerium -▁discretion -▁pursuing -▁Halbinsel -▁S.A. -▁beliebten -▁dominant -▁breites -▁Vorsitzende -▁strengen -▁Kommentar -▁nützliche -▁CPU -▁Imperial -▁suddenly -▁Etikett -▁complimentary -rem -▁harte -▁rhetor -▁Muss -▁Bass -▁Cos -▁Giov -▁forb -▁NT -▁Zer -▁ermitteln -abstimmung -▁geopolit -agna -griff -▁Abgesehen -▁Vorwand -checken -▁perpet -▁provin -▁Berech -▁Bremen -▁discovery -▁booth -▁benannt -▁specialised -▁passes -▁Transit -frau -▁EURO -▁continuation -▁vorgeschrieben -▁1973 -▁Directors -▁camping -▁separated -▁weapon -▁Frühstücks -▁biologischen -▁Straßenverkehr -▁adaptation -▁exploit -▁observers -▁verschwinden -▁junger -▁Monitoring -▁Reichtum -▁zugeschnitten --century -▁unprecedented -üch -äuter -GH -▁müsse -▁TAC -▁Pul -▁Blo -itably -▁luggage -▁unreal -amination -▁exempl -ikely -merzen -▁worauf -▁adher -▁accused -▁mehrerer -▁Impro -▁Krem -▁Weltmarkt -▁knowing -▁mainstream -ulationen -▁verliert -konstrukt -▁Durchschnitt -▁Darle -▁17th -▁Folglich -iaries -▁1983 -▁Integrität -▁ambience -▁analysiert --house -▁Golfplatz -ILL -▁unmittelbaren -▁Beachten -▁tackling -▁Segel -bilder -▁türkische -sprachigen -▁Impulse -▁luxuriöse -▁belgischen -▁Gastfreundschaft -▁part-session -runde -auten -wing -4-0 -▁filling -▁migrants -▁Pant -▁gains -▁F. -▁Fratt -umpen -▁annex -▁alive -opher -hrter -▁versprochen -▁jump -undet -größe -▁werte -▁vorne -▁Erleben -ensystem -oked -▁Beweise -▁1920 -▁interven -▁roy -attformen -▁Menschenhandel -▁Seeverkehr -▁mining -▁89 -▁Trennung -▁implies -▁Hierzu -▁Zwecken -wärtig -.11 -ifikationen -▁Defizit -▁Mailand -▁Jahrzehnte -▁Absol -▁Quadrat -▁Singapore -▁Split -▁verknüpft -▁linguistic -▁Meanwhile -gon -▁tiefer -▁hide -▁fing -▁dun -▁sink -▁más -▁SOL -▁pull -▁Masse -▁Pur -otype -ussten -▁CAD -▁RS -▁Grab -uma -▁Jet -ields -▁ausgebaut -▁vorgegeben -▁Erika -errt -▁unterstreicht -ünder -▁Große -ängst -▁securities -ERS -▁Bewerb -iefst -▁Saf -zustand -▁failing -▁1974 -heimer -würdige -sbruck -▁adjusted -vielfalt -▁Albanien -▁Grafiken -▁Ärzte -▁unvergesslichen -▁warranty -▁ungarischen -▁air-conditioned -▁müßte -▁Präsidium -▁Caribbean -▁ul -bte -69 -▁roles -▁rhyth -▁Torr -▁Dial -▁Base -omed -▁lifestyle -icular -▁anstelle -▁Ll -▁Nar -stuffs -▁Verona -▁Auslands -flüge -▁warmen -▁Aufstieg -▁mehrmals -▁69 -▁strive -▁weitergegeben -▁gescheit -▁versetzt -brä --Mar -beweg -▁feiern -▁backing -▁Palast -▁XL -▁contributes -▁Liebl -▁demonstration -▁congratulations -tenteils -▁hydraul -▁Annual -▁dauerhafte -▁earthqu -bilanz -▁consolidation -▁ignorieren -▁Campingplatz -▁abolition -▁Südtirol -▁refurbished -▁twelve -▁eating -mond -winkel -öpfung -Net -GA -▁tape -▁Seat -▁Way -▁Vall -▁Graph -ulates -stabilität -ifff -▁geklärt -▁Jagd -▁noticed -▁frischen -▁Theatre -▁sozusagen -igebiet -▁Gegeben -▁gewann -▁decreas -▁Exporte -▁clearer -▁flags -▁economics -rada -▁Zugleich -partei -▁Basil -▁Co. -▁wenigsten -▁demanded -arios -jähriger -▁Standpunkte -▁railways -▁obtaining -▁effektiven -▁EGF -greifenden -▁Krankenhaus -▁untereinander -▁arrested -▁verheer -▁tangible -▁unabding -▁Frattini -.- -mor -listen -PL -▁SIS -▁Did -▁Delta -▁Räum -▁Letz -estern -iedelt -▁notification -▁desper -▁ums -▁ausgearbeitet -▁nachvoll -epp -▁Forst -▁indisp -▁systematisch -▁Allgemeine -▁Grundfreiheiten -▁appart -▁Manchmal -▁besagt -▁competitors -▁bereitstellen -▁monthly -▁getrenn -▁influenced -▁Multimedia -▁Akzept -meldungen -▁Effekte -▁friedliche -▁unlikely -▁fifteen -▁landwirtschaftliche -▁außergewöhnliche -▁zutiefst -▁estimates -▁Expert -▁mündliche -▁gezielt -▁bedeutendsten -▁Pierre -▁roughly -▁Legitimität -▁Mugabe -▁Vermeidung -fit -mt --gr -Con -▁tausend -▁merg -▁Tener -▁Much -▁cinema -▁Kernen -imir -idity -herz -▁gep -adas -assage -▁Erreichen -oba -▁Abd -▁Fron -▁wozu -▁Kluft -▁Hinsichtlich -▁benchmark -▁skill --Ge -▁Forscher -astery -▁Kommando -▁besseres -▁schwerer -▁Motors -▁wunderbare -▁Informationsgesellschaft -▁missed -▁Armuts -▁Innsbruck -figuration -▁Leistungsfähigkeit -▁Effektivität -▁Bananen -▁Virgin -▁Fourth -▁venture -▁distinguished -▁carrier -▁korrigieren -▁umgekehrt -▁Fokus -▁independently -iated -df -▁fabr -▁bonds -▁gig -inas -infrastruktur -▁Wats -▁Kof -▁Cres -▁Rental -▁unilateral -lecht -▁Princ -▁Thun -▁durchgesetzt -aufent -altar -▁fires -▁hierarch -▁gewählte -ißen -▁Arbeitskräfte -▁Freib -▁Toil -▁entspann -▁Spielraum -▁intoler -▁Energieversorgung -▁summar -▁permits -▁Hollywood -▁ambitions -ción -▁E-Mails -▁transe -▁databases -▁SPA -▁mailing -▁Ganzes -▁genügt -▁gründlich -▁gathered -▁grüne -▁venue -änglichkeit -▁Garda -lacier -▁häufiger -▁Rindfleisch -▁ic -▁fotograf -▁rose -▁Distrib -edition -isierungen -▁cope -▁Kamin -▁Rimini -asp -▁forums -▁Loch -▁Lac -denk -▁aufre -▁Jens -anners -▁Mitwirkung -▁Paradies -▁vielfach -▁elabor -ergest -▁Flotte -▁nowadays -▁US-Dollar -stützung -▁Besteller -▁profiles --Ad -▁capability -horn -ölle -▁authorisation -▁intim -▁Guatem -idesh -▁Gleichbehandlung -▁finanzieller -▁Kopie -▁effizienten -▁hinzuweisen -öpfe -▁konsult -vernight -▁inspiration -▁Zulassung -▁Propert -▁Facebook -▁ECHO -▁Laeken -aze -vorteil -äts -gab -49 -▁Má -▁Benz -▁Ahm -▁kunden -▁RAM -▁Nür -▁deny -▁storm -▁stabile -▁deemed -weight -▁Ausser -▁3-star -acent -▁Abfälle -iances -bles -zeitraum -ancell -▁Geschichten -atories -▁größtenteils -▁deutlicher -▁energies -▁furthermore -▁employee -▁intu -▁zahlt -▁labels -interessen -▁musicians -▁lokal -▁Binnenmarkts -spiele --Netz -ützte -▁Estonia -wesens -▁Laman -▁investigations -▁qualifizierte -▁ausgestatteten -▁vorangegangenen -▁Tokyo -▁Tipps -▁Songs -▁Diensten -▁za -▁Fix -ioni -▁Later -▁gefolgt -▁exquisite -▁fruits -▁abgeb -▁Errungen -ahrer -▁Vorgang -▁dispar -ansa -▁zweimal -▁differently -▁instant -▁ausgeglichen -▁comforts -▁optimalen -füllen -▁Jahresbericht -hundert -▁preventive -▁translate -▁Metal -▁vorhandene -▁dangers -▁Elementen -▁Neuigkeiten -▁palace -▁Herzeg -▁removing -▁Dolmet -▁Karls -▁laboratory -▁strikt -▁Argentinien -▁fehlende -▁kommerzielle -▁stakeholders -▁wettbewerbsfähig -▁droht -▁Memory -▁bemerken -spolitischen -▁hectares -▁rescue -▁Wahrscheinlichkeit -ädern -▁Sog -▁Sämt -▁Awards -isi -untern -▁Rückt -▁Lama -estro -hrop -▁Jam -annual -adi -▁Aufträge -▁Diesel -▁Unterhaltungs -▁transmitted -▁Zielsetzung -▁gleichberecht -▁Energiet -▁Pharma -▁authorised -ekut -▁Benach --Gipfel -fähiger -▁steep -▁Winds -verbot -.09 -▁Konsumenten -▁subscribe -▁Luftverkehrs -▁Materials -▁Informations- -▁DVDs -parks -▁Mountains -▁Return -▁Leonardo -anwalt -▁EU-Bür -▁Unionsbürger -▁veröffentlichten -▁Oxford -▁Wurzeln -▁towels -liederung -▁Simbabwe -▁Ernennung --room -▁bow -▁DNA -▁Differenz -▁Ax -▁Veget -▁Lange -▁Zag -▁erlang -träglich -▁execut -annehm -aining -ultural -beitrag -hende -▁apolog -linder -▁Impf -▁Fronte -▁15th -▁awards -▁Monopol -▁revol -loser -▁married -zufolge -▁speaks -▁Sonn -▁hinterlassen -▁zulässig -▁assessments -▁Ratspräsidenten -▁Organen -▁Galax -▁Stellenwert -▁observations -▁Aussichten -▁Tunesien -▁Defizite -▁Möchten -▁kilometers -▁umfassendes -institutionen -▁stringent -▁Persönlichkeit -otheken -▁wedding -▁kompatibel --specific -uest -kommission -öcke -39 -▁winners -▁Pel -▁Petr -onze -onna -▁lens -▁Kul -▁Care -icating -▁stir -osph -uropas -▁Obs -▁umstritten -▁adidas -▁partially -rence -anzler -▁intergovernmental -strom -ransfer -▁Parlamenten -änderungs -▁numer -▁besides -▁Besicht -▁designers -▁Finanziellen -▁Festung -▁rechtlich -gängen -▁Carlos -ourcing -▁capacities -▁utility -▁Übereinkommens -▁Kurd -▁dominated -▁prevail -▁Territorium -▁Ludwig -▁Tunisia -▁pregn -▁Ausnahmeregel -▁getestet -▁Berlusconi -üsten -▁Tir -▁belegen -▁Burn -eterm -▁catering -alition -▁anmelden -▁pride -▁Label -▁dens -assador -ipper -▁vorschlägt -▁leichten -▁Aufrechter -▁acted -▁Fristen -itteil -▁Stränden -▁einzustellen -▁Converter -▁Rather -▁Autonomie -▁Rahmens -▁familien -▁Between -▁Zweif -▁wahrnehmen -fristig -▁Statut -▁getreten -▁cycling -▁appreciation -▁Libyen -▁loading --Protokoll --depth -▁Ultra -▁Bolkest -▁boundaries -▁countless -▁cabin -▁Moldova -▁vorrangig -▁summary -▁nutrition -▁verwirklicht -▁Zuerst -▁Watson -was -▁tone -▁foster -▁dicht -▁sick -▁bull -▁Acr -▁leit -▁Krak -▁Vi -iveaus -ieso -ereg -▁erwe -oso -▁gewerb -▁Anbau -▁Schuh -usted -▁bestellt -amped -neys -gestellte -▁realistisch -▁weiterzu -▁Allow -▁afterwards -▁zusammengef -iebung -▁Spezif -▁feelings -preises -▁younger -▁Pract -bietern -▁Musiker -▁promotes -gerechte -▁Verkehrsmittel -eleistungen -▁ALL -▁supervisory -▁unanimous -▁Mozilla -▁champions -▁Cannes -▁Künstlern -▁Mühe -▁zwangsläufig -▁Frontex -eur -▁ax -▁Tut -▁Machen -▁Wagen -▁R. -▁Run -still -▁struck -▁Prix -▁gepl -▁extends -▁Einheiten -▁unterhalb -▁Abweich -▁Chi -▁gesorgt -▁overt -▁großartige -▁Brust -▁verbieten -▁Interface -▁globalization -▁margin -▁Mehrere --Verb -▁collections -▁consulted -▁Pfad -▁seaside -▁Erwäg -▁deepen -▁safely -▁heruntergeladen -zuteilen -▁Kolumb -▁ceram -▁plugin -▁schwarzen -▁vineyards -▁Dörfer -stärke -▁Everest -▁indigenous -▁korrekte -▁Welthandelsorganisation -▁Wahrnehmung -▁indispens -▁Sämtliche -▁Bolkestein -oh -mund -writ -case -PO -▁pose -▁porn -emit -▁Kok -arb -ürft -▁albeit -▁Lett -▁Leder -stuhl -▁decken -▁Verhin -adian -▁Einladung -▁dismant -fahrung --Spe -klen -▁Grundstück -▁Klick -▁Trop -▁gerufen -▁langjährige --Amer -▁Advent -▁Mittel- -author -▁reaches -▁Drive -▁reale -▁Frankreichs -▁erweiterte -▁regelmäßigen -▁bears -▁ausgedrückt -▁Verpackung -▁Dolomiten -▁Kühlschrank -▁Korrektur -▁ASEAN -▁Exhibition -▁revenues -▁Oracle -▁hilfsbereit -▁Berechnung -▁ueber -findung -days -CR -▁Tony -▁Egal -▁Bert -▁Ce -stunde -▁steckt -▁Zit -oding -ipes -webe -eckten -ografen -sspiel -▁Ausstellungs -▁Einblick -aked -▁EU-A -auert -▁accomod -▁Plant -▁Landschaften -isko -▁invested -konvention -geordneten -▁Industries -▁Gleichheit -▁antiken -▁destabil -▁schöner -▁Nordir -ALL -▁Unserer -▁Generalsekretär -▁conviction -▁Biok -▁Kaffe -▁Chemikalien -▁neighbor -rebungen -▁Konflikten -▁schwedische -▁Anmerkung --Emissionen -anspruchnahme -▁Vermarktung -iben -ng -nutzung -ship -kamera -▁ties -▁mieten -▁Boutique -▁Core -▁Ry -ieger -berto -▁anlässlich -▁anstreben -▁revers -verhältnisse -▁deck -▁verglichen -adet -▁Schokol -lafen -▁vorkommen -▁Alarm -▁query -▁Abwicklung -sebene -▁noisy -phan -▁insight -▁konvention -▁secured -▁defense -▁Fortschritts -▁Sicherheits- -▁Simply -▁Harry -▁Nachfolger -herrschaft -palette -▁Behinderungen -▁Automobilindustrie -iciary -▁tatsächliche -▁Schnittstelle -▁gewöhnlich -▁abzielt -▁Supervisory -ansprüche -▁Sebast -▁Lieblings -aute -KE -▁hole -▁Sit -▁DB -▁Dry -▁Eck -▁Pont -▁Bob -▁Bolog -omer -▁Crew -▁bewirkt -agnet -▁Stimmung -iele -▁ages -▁Chaos -▁Foren -leichen --Sicherheit -▁Nachteil -▁geteilt -tegration -▁Barnier -▁investing --Richtlinie -▁Newsp --Tr -legten -▁Solange -▁villas -▁wünscht -▁Logo -▁sexuellen -▁Graz -▁Texas -▁Tabellen -▁reconciliation -▁Konflikts -IEW -▁motivated -▁parlamentarische -▁greif -▁Substanzen -▁climbing -▁Regarding -▁Wachstumspakt -▁sincerely -▁Aufrechterhaltung -ebene -oil -cm --rea --prod -▁hers -▁dreams -▁rum -▁sat -▁TU -▁beat -▁Surf -▁Hind -▁in-depth -amkeit -▁on-line -▁haven -▁weigh -▁Oh -▁atom -▁spell -▁späten -▁Auskunft -peror -zuheben -äler -▁crimin -▁Brown -▁natur -▁Reden --bu -▁Markus -▁Buchstaben -▁bitter -▁Flughaf -▁Audi -▁beantragen -▁clarification -▁cooling -▁Titan -▁scenario -▁Bangkok -▁genetically -▁PPE-DE-Fraktion -▁beeinträchtigen -▁Johannesburg -▁dictatorship -▁constraints -▁Eleganz -▁Annäherung -hou -uent -▁flaw -▁Sich -▁eB -▁ließen -▁Walter -▁V. -▁ungen -▁Ug -▁Lü -leet -▁zuzu -oboda -▁Ausfuhr -erren -▁betreibt -▁Spenden -▁Vorgänger -aux -▁uphold -▁Entdeckung -▁geschätzt -▁qualities -▁Need -▁befreien -boards -partnern -▁1.1 --Ger -▁Haushaltsjahr -▁schnellst -▁Preis-Leist -▁radiation -▁1977 -▁separation -▁elegance -▁audit -▁beantragt -▁panels -▁Etagen -▁Verhaltensk -▁stimulate -▁reproduction -▁scenes -veranstaltungen -▁Weihnachts -▁eligible -▁Erdbeben -▁Getreide -vet -MB -Mail -▁mould -▁Telef -chnen -▁cup -▁Hilfen -▁Reh -▁Rov -ctic -▁orange -überwach -▁Anzeichen -einheiten -▁athl -▁Erwerb -uda -▁finishing -▁5th -▁Zeitungen -▁realised -▁helicop -▁damaging -▁Brother -▁84 -positions -erstoff -▁Träger -▁Appro -▁persönlicher -▁Sozialpolitik -▁benötigten -schnitte -▁divide -▁Copy -▁jährliche -orientierte -▁quantitative -▁ausführliche -▁somewhere -▁Säule -▁explanations -▁Konvents -▁Syd --Abkommen -▁suspension -▁Ebenfalls -▁CD-ROM -▁pedestrian -zens -cuss -ls -pher -▁hôtel -▁dive -▁abends -▁Sett -▁Mare -▁Breit -install -▁WHO -▁Cors -▁Côte -▁G. -▁Gerne -idal -▁eingeben -ubar -▁wash -▁Einwände -▁Being -▁decentral -▁justification -▁Commander -▁meint -▁lawyers -▁accountability -▁enjoyable -▁klares -▁analyt -▁2.4 -schwemm -gesetzten -▁currencies -▁delayed -▁dogs -▁Jews -▁beliebtesten -▁Lady -▁coalition -▁Workshops -▁seminar -▁islamischen -▁damaligen -▁Entschädigung -▁allmählich -▁Gültigkeit -▁hairdryer -▁indispensable -▁м -wann -prechung -kat -kolle -77 -▁tiefe -▁Suchen -▁Aur -ordern -▁lod -▁Kämp -▁Kammer -▁Fünft -defin -alität -▁anfangen -▁-B -▁Schiffen -asses -▁washing -ausbildung -▁outs -▁heik -▁91 -azione -▁industrielle -▁arriving -▁Airways -▁colon -▁Basic --bit -▁begrüß -▁Canaria -ustainable -▁Zwangs -▁stoppen -▁Gesamtheit -▁symbolic -▁griechische -▁co-operation -▁ökonomischen -.C. -▁Dubai -▁Früchte -hütung -▁Sortiment -▁Umstrukturierung -kompetenz -▁Blizzard -fal -rin -zern -▁nahmen -▁vice -▁Sehen -▁Days -▁gaming -unal -▁commen -▁künd -▁Kub -▁GVO -anien -▁Liv -▁Large -▁Zellen -appropri -ainless -▁shoes -▁wasn -ogo -zeitige -frü -▁74 -▁verbreiten -▁Teilnehmern -▁Barack -▁ausgeübt -▁bekam -▁Phasen -inson -zügen -▁1.4 -▁Chr. -▁marriage -geordnet -▁genauen -▁Verhandlung -▁passenden -▁Vorsorge -▁limitations -▁Torre -▁infrastructures -▁attempted -▁ausgewogene -▁vessel -▁proportional -▁Baroness -▁haftet -iors -▁Toleranz -▁SEO -▁Mong -▁greet -onas -unn -chung -▁Funk -▁anger -schäden -▁dez -rost -▁Starf -aller -▁4th -▁invites -▁Überw -weiterung -▁festgehalten -▁designated -▁Finanz- -tensive -fassungen -IND -▁featured -igrant -sky -▁Christians -systemen -▁delivering -▁Affili -▁latein -▁Leser -▁architekt -▁Höhepunkt -▁Application -▁Sofern -▁Fertigungs --Regierung -▁earned -▁koordinieren -▁await -▁bekräftigen -▁makroökonom -▁Nachbarschaftspolitik -▁inevitable -▁opponents -▁dänischen -▁gelöscht -ów -▁tolle -▁meter -▁Möglich -▁curt -▁Räumen -▁beweist -▁NO -▁NCC -▁ont -owder -▁Inanspruchnahme -▁geänder -▁geforder -▁Unlike -▁Annex -ranational -▁gründen -▁Channel -älen -▁actor -landes -▁gewachsen -▁Kredite -ools -▁socially -▁Datenbanken -scheidung --suite -▁menus -▁touristischen -▁Netzes -▁Augenmerk -▁avoiding -strecken -▁Völkern -▁...... -▁Erdöl -▁gesetzlich -▁vernünftigen -▁embark -wissenschaften -▁qualitative -▁Merkel -▁advocate -▁Indonesien -▁hypoc -▁EWG -ströme -▁Nichtregierungsorganisationen -sum -master -▁dumping -▁pi -▁Pos -char -chens -▁zog -▁H. -▁Vere -▁Gibr -▁Girl -istern -hrer -acity -▁outlined -▁intermedi -affin -ondo -▁immerhin -▁Handt -▁flour -▁printer -▁promising -▁Formul -▁Beta -▁hopefully -▁Save -▁Romant -▁criter -ifizierte -▁Myth -▁beautifully -verlust -▁subscription -▁verstärkten -▁Signale -▁Kommissionsmitglied -▁vorausgesetzt -▁multilingual -▁robots -▁Dafürhalten -▁gültigen -▁Timor -▁coherence -▁nachzudenken -▁Mozart -▁Begeister -▁veröffentlichte -▁Tschetschenien -▁ot -▁orth -cam -▁tier -▁BI -▁Bib -▁Bayer -▁Busse -▁Corr -▁Cube -▁HR -▁Rational -▁GF -ulden -▁Little -▁Lanka -quipped -lez -▁rehab -▁einbringen -plates -artig -▁allies -ounced -▁legte -▁handed -▁besichtigen -▁impetus -scheinend -▁steuern -▁optimieren -▁Canal -▁erreichten -.05 -▁1969 -▁Tempel -▁decoration -▁Verschm -▁Infos -▁controller -▁Infrastrukturen -▁epidem -▁episode -▁Sofia -▁Nuklear -▁racing -▁religiöse -▁Befürwort -▁östlichen -▁Konzerte -,0 -dung -ль -▁horn -▁fäh -isations -▁libraries -▁Coton -▁Hüg -ulichen -tert -▁gewe -▁OP -ega -▁Ansätze -▁Thea -▁Erleicht -▁Mitbürger -ruhen -fred -▁Gesetzen -▁Brot -bewerber -▁Spielern -▁Vielmehr -haftet -▁existed -▁check-in -ijn -▁Jahrhunderte -öhnung -▁blocked -▁Beförderung -▁Kompromisse -▁Solidaritäts -▁gesetzliche -▁laying -▁misunder -▁Cardass -▁frühere -vielfält -▁Ethernet -befehl -▁qualifizierten -▁legendary -▁Orientierung -▁broadcast -laimed -▁brilliant -▁Kernenergie -works -bollah -▁wand -▁waves -▁bust -▁Diet -▁Bag -etto -ilien -▁killing -▁Vierten -▁Virus -▁unzul -leid -▁reun -esty -establ -▁erfor -▁-A -prim -▁entity -ussy -▁Geber -▁weitem -▁systematically -▁flower -▁Spiels --bas -▁Entwicklungspolitik -▁loan -staatliche -▁grave -OST -▁Ferienhaus -▁suspect -▁anderswo -▁Collection -zzo -▁compatibility -▁Chemie -▁engagieren -▁Rohstoff -aggage -▁PSE-Fraktion -▁disagree -▁hunger -▁gehörte -▁fundamentally -▁Variante -ancellor -bem -▁MR -▁MAN -irr -▁eignen -▁Koll -▁Ray -▁unm -uliert -abot -▁einsetzt -agte -▁Store -halter -▁ausg -enny -▁Einbindung -▁disreg -aufwand -fahrzeug -▁delivers -▁Rechtssicherheit -▁ernannt -▁schwach -▁authorized -▁filters -▁Swoboda -▁installing -▁Disney -▁Insofern -▁Eurocop -▁fünfzehn -▁PCs -▁universe -▁Recy -▁Florida -zweck -▁hinzuzufügen -▁saubere -komplex -▁vorbereiten -▁Souven -▁Internetseiten -▁Victor -▁vorzubereiten -▁bemerkt -▁slaughter -▁inevitably -▁Telekommunikation -▁alpine -datum -mont -bra -ños -▁herein -▁auction -▁Sid -▁PE -▁kos -▁Hut -▁Lift -▁zuge -abteilung -isson -plattform -artikel -nee -▁mysq -handlung -▁forming -▁surplus -▁verläss -▁skilled -▁diam -▁Naturkatastrophen -ächtigungen -▁Solarium -▁fortgeschritten -▁Kosmet -▁Balance -ianti -▁erfassen -▁globally -▁letztes -▁WiFi -▁dauerhaften -▁steigenden -▁maßgeschneider -▁überraschen -▁contradict -▁Artikels -▁Passagiere -▁inequality -▁interfaces -▁PVC -▁geschmackvoll -▁Indikatoren -▁Erneuerung -▁Bologna -▁Cotonou -ić -fix -rücken -üger -▁harten -▁veter -▁german -▁Baus -eservice -▁overnight -▁kid -▁FM -▁Nick -▁Zigar -osur -▁vertrag -anted -merken -▁adap -ailable -▁plate -▁Unterb -phone -▁Haw -▁carries -▁serial -▁opera -▁cost-eff -▁Urban -▁electronics -▁subjected -▁metall -▁identifizieren -▁fortzusetzen --Intern -▁Stellungnahmen -▁günstiger -▁Verfassungsvertrag -▁attraction -▁Environmental -▁calculation -pakete -▁owing -▁Renov -▁Szene -▁CNS -▁verlängert -.The -▁Punta -▁vorzuschlagen -held -oi -roll -mes -bur -güter -▁bol -erklärung -▁Cart -▁Hay -▁ungerecht -umfang -ulieren -▁deeper -öne -udio -▁admission -örp -▁workforce -▁weiten -▁provis -▁fliegen -▁openly -▁Quar -▁activated -▁1.3 -▁Model -▁Gesetzgeber -▁Demokratien -▁Aparthotel -▁Criminal -▁Donau -▁high-speed -▁Bundesrepublik -▁renovierte -büros -▁simplified -▁gradual -▁Fotok -▁englische -▁exceptionally -▁Kreatur -▁Twitter -▁erzählt -▁tranquil -▁tremendous -▁MIDI -▁Ansprechpartner -▁retirement -▁herself -Tech --dr -link -▁hängen -▁sed -▁Bike -enke -iring -ivated -atible -aspekt -▁dereg -▁Zinsen -▁aufgeworfen -rators -▁freige -▁solving -▁locally -▁viewing -▁wiederhole -▁festhalten -▁maintains -▁beacht -ATE -▁wishing -▁Rücksicht -▁Demokratisierung -▁prevents -▁capitalism -würdigen -▁Chechn -▁Termine -▁judges -▁erkennbar -▁hyper -▁symbols -▁ICE -stechnologie -▁Komponente -▁Navig -▁beglückwünsche -quiries -▁ideological -▁Vorbehalte -▁mangelnde -klausel -investitionen -▁Brüder -▁Catholic -▁Starfleet -▁vente -eded -eats -isdom -▁Ruh -▁uncom -▁geometr -▁Verknü -▁dement -▁schmerz -▁resumed -▁newest -folgerung -▁Flach -▁complaint -▁berührt -▁detection -ERV -▁seitdem -iftung -▁Compact -▁procedural -▁portra -▁Geschäftsführer -▁machten -▁2.2 -▁borne -▁Kleidung -belast -▁diversen -▁Linken -▁Volkswagen -▁englisch -▁Zwischenzeit -projekten -netze -▁Boliv -▁Hongkong -▁koordiniert -▁vertrieben -▁tägliche -▁Gipfels -▁Plugin -bücher -▁fühlt -▁bicycle -▁Interpretation -▁abzulehnen -▁bedürfen -▁Euroraum -Tube -91 -ño -▁nost -▁ranges -▁Tran -▁bom -▁bold -▁PXI -▁geachtet -enophob -unches -▁cigar -▁Kandid -▁Ganze -deckt -bergt -▁Nothing -ifice -▁aufweist -▁Angl -▁Think -▁ausgestellt -▁Erstellen -▁ads -avor -▁wohin -▁verbrauch -▁Maßstab -▁backed -▁Glet -▁Italiens -▁writer -▁closure -▁unvermeid -▁selecting -▁silent -▁wertvollen -▁convincing -▁zuverlässige -▁argued -▁Athens -▁Adressen -▁Breite -▁dahingehend -▁Artikeln -▁Resultat -▁horses -▁zukunfts -▁Chechnya -not -oder -aure -Pal -BR -▁mad -▁wet -▁batt -utreiben -▁Ganzen -▁GIM -▁Unix -▁überflüss -▁abuses -ealand -▁unterstrichen -▁supranational -utzten -▁elf -arbeitungs -▁decrease -▁highway -krie -▁geschlossenen -▁advances -▁Hauptziel -▁reviewed -appy -straßen -▁donors -▁Drucker -▁Landwirtschafts -▁utter -▁Romanian -▁reiche -▁Entschließungsanträge -▁predecess -▁moralische -▁Bayern -▁attraktiv -incere -▁Jahrzehnt -▁amtierenden -▁amtierende -▁außergewöhnlichen -▁refriger -▁zivilen -▁Messenger -▁deaktiv -▁precaution -fassen -▁tube -▁wise -▁Sax -usions -▁Walk -▁RT -alg -▁Ult -▁UPS -▁on-site -verfolg -▁Progn -▁Stalin -adar -▁chances -▁ausmachen -▁vors -▁vorge -▁resid -▁Eintritts -errasse -zene -zuhalten -▁beding -▁Frucht -▁auto -▁technically -▁mood --Men -▁origins -▁affair -hauses -▁Offiz -▁Officer -▁administer -▁hosting -▁Statute -▁Hoheits -▁terraces -▁abstained -öpft -▁Verstoß -berechtigung -▁Diplomat -▁Zutaten -▁thirty -▁distributors -▁recruit -▁Gegebenheiten -dy --party -power -kenntnisse -VIEW -▁hört -▁Tu -▁EEC -▁Pav -▁giant -endern -▁denkt -▁Privileg -anta -▁entities -▁ausgenommen -▁spark -▁schien -ovina -▁Später -▁gesucht -▁Stress -▁geschätz -ocks -ymmetr -▁beside -▁Besetzung -▁Finanzrahmen -▁142 -▁absorb -ifizierten -▁personality -▁topical -▁gases -▁chemischen -▁Redezeit -▁Therapie -▁Intelle -▁Gewährung -▁reinforced -▁Ramblas -▁verbindlich -▁Nazi -▁transatlantischen -▁bezaubern -▁gepflegt -▁mixture -▁furnishings -▁criterion -▁Eurocopter -ez -ii -uels -bö -▁ferry -▁bags -▁SU -▁Posit -erl -▁lehnen -▁Harr -▁Rose -▁Vat -ivo -idor -▁repression -▁wirft -ikon -ecurity -▁Auflagen -ähren -▁Importe --Serie -▁enshr -maßen -▁modernsten -▁hearts -▁breast -arsh -▁engagement -▁Studies -bereitschaft -▁Kapit -▁Subjekt -▁drafts -▁anzugehen -▁voices -▁Comfort -▁Trep -wünsche -▁vacuum -▁rigorous -▁Moldau -▁Persönlichkeiten -▁abzuschließen -▁Telekommunikations -▁Railway -▁envisaged -beschluss -▁rarely -kompl -▁nerv -▁founder -▁Tiefe -▁Poll -▁Pots -▁A5-00 -▁Wandern -▁Klav -▁unum -▁unweit -aph -▁verteilen -portal -▁exclude -aha -▁entlassen -▁env -ahrscheinlich -▁arises -ammern -▁Strassen -▁Stream -▁parent -▁bekämpft -▁Andrew -▁beachtet -▁Folgendes -▁automatischen -wärtigen --18 -▁wunderbaren -suche -bezogene -▁Gefähr -film -▁harvest --Systeme -▁Steinberg -▁behindert -▁ruled -▁Adria -▁Vergüt -volumen -▁Vulkan -▁Photoshop -▁erfahrenen -▁IGC -▁europaweit -▁Vegas -with -wanne -▁delle -▁voor -▁Ms -▁Pens -▁gg -etra -▁katastrop -▁Nerv -▁Zeile -▁erober -▁verweist -▁Oft -▁adverse -▁plast -perten -▁Gramm -flotte -ährungs -finder -▁Verband -▁feste -▁Richtigkeit -▁Auth -▁direkter -▁Werde -▁Naturschutz -▁wahrgenommen -laubens -▁Kraftstoff -▁Staatsbürger -▁profitable -▁Solidarity -▁troubl -beihilfen -▁regelmäßige -▁attempting -▁Hoffnungen -▁Zivilisation -▁Kolonial -strategien -▁Winters -▁geräumigen -▁Komplexität -▁US-amer -▁schlimmsten -▁Muslims -▁criminals -▁Begeisterung -oa -baut --en -gipfel -LIN -61 -▁Büg -erzeugung -▁Hunde -▁inwieweit -ürt -lys -ernal -ington -▁ersuchen -vermögen -▁Prost -▁Störungen -auscht -▁disastr --Ste -▁basket -▁globe -▁customized -▁verlor -ettbewerb -▁Festivals -▁bereitgestellten -▁Natura -▁Gegenwärtig -▁vereinbaren -▁fortschritt -▁distribute -▁accommodations -▁Laufzeit -▁Gesundheitswesen --Um -▁interpreted -▁predomin -▁ausgewählte -▁wirksamer -steuerung -▁beobachtet -▁strenger -▁Creative -▁tschechischen -▁Immunität -▁benachrichtigt -▁husband -▁katastrophal -▁† -tags -kandidaten -▁ran -▁sincere -▁TE -▁TW -▁Male -esco -amment -▁aluminium -▁andauern -termin -ibus -fügen -▁appr -▁bestraft -▁EU-Haus -▁offset -▁basierend -▁excuse -▁nearest -enntnissen -▁1972 --discr -▁harmonised -▁Provider -▁Bulgarian -▁Alternativ -▁Erdgas -▁Erkenntnis -▁Cyber -▁Users -ladimir -▁Segment -▁benachbar -gehörige -▁Unternehm -▁abgeschafft -▁Especially -▁Lufth -▁carriers -▁Diktatur -▁indirectly -▁clinical -▁kopieren -▁Freedom -▁asiatischen -▁adequately -▁Akzeptanz -FA -▁dear -▁bands -▁Sauber -erkt -▁Roy -▁Rating -▁stones -estand -▁zugreifen -hrough -▁Unglück -▁frische -▁coins -▁Ali -akh -▁Jahrestag -▁Production -▁73 -▁smoke --Bar -▁Trav -▁Dienstes -▁keyboard -▁barbar -bereitet -▁Raumfahrt -▁Außengrenzen -▁resolving -▁vorherr -▁Gestalt -▁Städt -sprodukt -▁Kapitalismus -▁Olympischen -▁matches -▁Landwirten -▁Jacuzzi -▁Autorität -▁ceremony -▁Hindernis -▁Viren -▁Inflations -▁Porsche -▁Ozean -▁Rettungs -▁einzuleiten -▁Schottland --reaching -▁Gibraltar -gue -GEL -ва -▁nnen -▁dün -▁Dark -▁MAR -▁Beat -igel -ulle -▁NOR -▁Zentrums -verkauf -▁dependence -▁asks -▁abschließen -▁Thess -▁Weis -▁Alic -▁Aufklärung -condition -etext -▁Mario -▁accessed -▁instability -▁acid -▁Mittelme -konfl -▁statute --Design --Kl -▁reasonably -▁Fußgänger -▁Upon -▁formerly -▁dynamischen -shore -▁Europäisches -▁Valent -▁validity -▁bloß -▁vertical -▁Königreichs -▁constituen -▁Popular -▁Banglad -▁exklusiv -▁Lyon -▁Anpassungen -▁hazardous -rieren -cany -Card -▁ft -▁dimin -▁Written -amik -▁Ub -opoul -leuten -▁stating -erner -▁Zap -▁Unless -▁exakt -gebir -finanzierung -▁berufen -▁Manuel -▁angeführt -▁ausgehend -▁Medina -optionen -▁passive -▁breaking -ikanen -▁distant -▁Secure -▁behold -▁Währungen -▁determining -▁congratulating -ictionary -▁sämtlicher -▁adjustments -▁HTTP -regelmäß -▁literally -▁irgendeine -▁imperative -▁linear -▁Terrace -▁attraktive -▁autonomous -▁Migranten -▁Hunderte -▁cotton -▁Ingenie -▁beherbergt -beratung -nächst -Fast -▁Sin -▁P. -▁Pag -erungen -otto -▁klin -▁Cover -▁anzusehen -▁Zealand -▁geführten -▁jack -iedlung -▁abilities -▁willk -obs -acies -aku -▁Gründ -▁transmit -▁Enttäus -▁Herzlich -▁geschw -▁Klär -▁visas -▁halben -leiste -▁bewahrt -▁dorthin -▁dürften -▁Modus -▁Gesellschafts -▁Sozialisten -jenige -▁civilians -▁Südost -▁PCI -▁Latvia -▁Beitrittsverhandlungen -books -▁Teuf -▁loaded -▁vergleichbar -▁Strafgerichtshof -▁theoretical -▁voranzutreiben -▁Quartal -▁römischen -▁Sahara -oad -ku -ка -▁tiefen -▁Method -orious -olved -▁Fülle -▁Riesen -▁deriv -▁L. -ref -ernity -lichsten -uropäische -ssp -ources -ektoren -▁bestanden -▁thereof -▁workplace -▁Übermittlung -▁Lands -▁79 -▁750 -▁Herstellern -▁Manchester -wickelt -▁Fahrzeugen -▁führender -▁precond -▁Salon -▁Crus -ounded -▁blockiert -▁restrictive -▁Operationen -▁Gedanke -strecke -▁layers -▁stabilen -▁gratuliere -▁gemeinschaftliche -▁pillars -▁Mercosur -▁Learn -▁invoice -▁diesbezüglichen -▁Schließung -▁Sydney -host -aison -zessen -▁tighten -▁healing -▁salle -▁MO -▁Bald -onden -onian -▁Hav -ingo -▁ermittelt -ubil -▁Schreib -aments -▁Einstieg -▁counts -▁tries -stellten -▁markiert -▁bedient -▁Flüss -▁Extr -▁Whilst --Raum -gangs -▁legislativen -▁definitions -▁analyse -▁bekanntlich -▁classified -▁dynamische -ifelong -▁counterfeit -▁ausgerüstet -▁woods -▁Wanderungen -▁Schwerpunkte -▁Pressem -▁ausgeliefert -▁refres -▁Luftra -▁samples -spezifische -▁disagre -▁scrutiny -▁Hospital -▁sensitivity -▁Guatemala -,9 -Sp -23 -▁tender -▁fib -▁wished -▁Dicht -▁Dog -▁MM -▁Bath -ered -▁Cit -geist -enta -▁Lock -▁Nich -▁Stop -▁unsicher -▁cha -ätten -ertal -zeile -▁plates -▁preference -ordnet -ixels -▁Frances -▁pointing -▁securing -▁advised -▁blend -▁Projektes -▁Beträge -ITE -▁readers -▁reactions -▁Industriepolitik -bauten -▁cycl -▁formally -▁Reflex -▁österreichische -▁Appell ----- -▁Rhine -angehörigen -▁multilateralen -▁Zustellbetten -▁Seattle -▁Newspapers -▁~ -ti -westen -läß -ли -▁mache -▁TB -▁petr -▁cathedral -▁Kalk -▁Cer -▁Geden -imer -amas -▁verfahren -▁Diejenigen -thaus -gles -blicke -▁supporters -ancer -▁81 -▁eingen -▁surgery -▁ausgepr -▁Bürgerrechte -▁critique -▁eventuelle -lessly -ASA -▁examining -▁Simon -▁ageing -▁non-governmental -▁niedrige -▁..... -namo -▁mobilen -▁abzugeben --year-old -ibliothek -▁tonight -▁anspruchsvolle -▁Pferde -▁angle -▁bemerkens -▁Tosk -▁innocent -▁Nepal -▁intuitive -▁tags -▁hör -▁baut -▁pir -▁Hilton -▁Gutes -alys -ifier -▁concludes -ehlen -ubb -▁Beibeh -▁queries -▁Vorstands -▁interrupt -▁wanting -▁Monaco -hältnissen -▁zusammengestellt -▁studying -▁Dienststellen -▁Werken -▁transported -▁Jahreszeit --199 -▁positives -▁verdanken -▁Frankfur -▁Downloads -▁couples -▁Therap -▁counterpart -▁streben -▁ét -▁gemütlich -▁execute -regelungen -▁variables -▁consolidated -▁unanimity -▁Kongo -▁Ungleichheiten -▁fulfill -▁distinguish -▁certificates -▁Spirit -▁Behandlungs -▁Holding -▁Guantá -▁Giovanni -firmen -ßig -▁roten -▁Sara -eline -inhaber -▁Glaubens -▁unvor -▁unbesch -▁aufhalten -quoten -ordnen -flation -cons -▁Challen -▁Chianti -▁Reifen -▁interference -▁Lee -▁breaks -utenant -ränkten --CD -▁comfortably -▁Gleichwohl -▁Sozialpartner -▁akadem --Ind -effekt -▁compromises -▁Gemeinschaftsrecht -▁Wohler -sproblem -▁unmittelbare -muster -▁begrenzte -Station -▁kommunizieren -▁Meinungsäußerung -▁grey -▁Schlußfolgerungen -▁verschärft -▁audiovisual -stockung -▁shooting -▁Congo -▁galleries -▁Verhinderung -▁disastrous -▁Thessalon -fft -l. -lassung -pension -äglichen -▁fold -▁rumän -▁Mord -▁Bash -otr -▁cow -▁Herm -▁unjust -▁Looking -▁aufhören -illen -▁Einreise -eutral -▁Abt -▁wenngleich -▁interventions -drop -▁minibar -▁83 -▁geschenkt -▁protests -▁Steel -▁Clar -▁prinzip -▁zurückkommen -▁Skigebiet -▁Disabled -ASE -▁Panel -▁jünger -orientierten -▁LabVIEW -▁refugee -schulen -▁preservation -▁safeguarding -▁Ecu -▁Bewegungs -▁Mountainb -▁Hochschulen -adresse -▁Bavaria -▁Translation -▁Feedback -▁Guantánamo -gasse -▁SR -▁Rud -icism -▁anonym -reif -step -ktoren -▁verify -reate -agern -gründe -thur -▁admir -▁betreut -▁Chip -ergy -▁selts -▁trans-European -▁inviting -▁105 -▁Whit -▁Ziels -▁gericht -▁Andor -▁integrating -▁bedeutender -▁intimate -▁klarstellen -▁Afrikas -▁2.6 -▁Motoren -▁downloads -ssystems -▁Democracy -▁mittel- -▁Börsen -▁Vergessen -▁Ansprüchen -▁geräumige -▁attraktiven -▁künstlerischen -▁sympathy -▁unglaublich -▁veranstaltet -▁städtischen -▁Körpers -▁Softwar -▁Heidelberg -▁disregard -körper -HT -HA -▁pair -▁Mathem -inj -▁echter -▁echo -▁Wur -▁Camer -▁CSS -anwendungen -arly -lever -abetes -önt -▁sozio -▁Erfassung -easing -▁Hotelzimmer -▁perception -▁Maj -▁Zeitschrift -▁konstitution -▁Contract -▁buyer -▁thinks -▁refusal -▁linking -▁begrenzen -▁Tagungen -▁Beispielsweise -▁Daraus -▁annually -▁Chris -▁encomp -▁consultations -künften -langes -▁FREE -▁virtuellen -▁homogen -▁Davon -▁Synth -▁revolutionary -▁Impuls -▁Orchestra -▁monopoly -expected -▁Lufthansa -▁` -gg -▁aust -▁belt -▁Dé -▁DES -▁Pi -information -olic -▁leitet -▁Kast -ierendes -▁wirst -ischof -appar -either -▁Stan -▁esc -▁amp -ongue -▁Gegenden -▁strikes -▁Nachteile -▁Handhab --Port --comp -▁massiven -▁starker -▁zuständige -ICE -▁inspect -▁Endes -▁limitation -▁archae -▁Verschlechter -▁irgendwo -▁ecos -▁Standorten -▁Publikationen -▁Synerg -▁SilverFast -▁accompanying -▁mittelalterlichen -▁Besatzung -▁incomes -▁Gastronomie -▁overwhelming -▁Castro -▁Montreal -▁corporations -läuft -Kr -ár -▁hind -institut -▁Rica -▁Vic -▁Greg -▁anch -rapp -stoß -▁erhebt -▁verhandelt -▁sichert -▁abhängen -▁shore -auswahl -▁ours -▁plot -▁Fracht -ianer -▁leidet -steuern -orship -▁anyway -weren -▁Ash -▁posed -▁Autonom -▁democracies -▁beweg -▁Feiert -▁Canadian -celand -preisen -▁protective -▁perfekten -▁degrad -▁holes -▁breeding -▁CO2-Emissionen -▁medicinal --Unternehmen -▁surfaces -▁kreative -▁Funktionsweise -▁übertrieben -▁qm -▁Cruz -▁Wäscheservice -treffen -nütz -oir -ressourcen -cano -▁sums -▁merk -▁eb -icion -▁Neither -quenzen -▁Inzwischen -▁Unregelmäß -▁jazz -▁Schritten -▁worried -▁'E -▁Ausbruch -▁untergebracht -▁Aufpreis -▁elite -malen -▁Bruch -▁YouTube -▁detention -▁Medium -▁gebucht -▁Finanzielle -▁kommende -▁115 -▁aiming -▁Canary -▁reservieren -▁Wohnsitz -▁nightlife -▁Kriterium -▁starb -▁Diskussions -▁demonstrations -▁Upgrade -▁panorama -▁Depending -▁memories -▁Fischereiabkommen -▁Kohäsion -▁diplomatischen -▁intelligente -▁Political -▁Sitzungsperiode -▁Räumlichkeiten -üs -▁tv -▁hunder -▁Scr -▁PAR -▁greift -▁Bien -▁A1 -▁Cool -▁Hallen -▁Fabrik -▁unang -uliche -▁Pries -▁Verschw -forest -▁Anschließend -lave -▁ausgeben -▁Erziehung -▁amid -ritical -▁EU-M -▁dismiss -▁très -▁perceived -ventura -▁Branden -▁modification -▁muslim -▁norm -▁multif --Out -▁Etage -▁freundliches -ponsible -▁irgendwie -▁Anyone -▁deadlines -▁begrenzten -▁konsolid -▁noble -▁pushed -▁Genauigkeit -▁gestrichen -▁Residenz -▁übereinstimmen -▁Käse -▁Colombia -▁Beibehaltung -ME -▁vierte -utory -unya -user -▁theatr -▁UEN -hrow -▁versetzen -▁Prince -agung -▁desp -▁Stere -icked -eased -▁Grupp -▁worker -▁proves -▁developer -▁deciding -▁Verbr -drückt -▁Grundprinzip -azines -▁Bestrebungen -itiativen -▁Beschleun -▁Embry -▁smallest -▁erwarte -▁confirms -▁Linz -▁cream -▁portion -ettering -▁vorgesehene --like -▁1940 -▁divisions -chitektur -▁angemessener -▁mittelständ -▁Flüchtlingen -▁ruins -▁delegations -▁BAE -▁adjacent -▁pflegen -▁sacrific -▁Verhaltenskodex -m2 -cott -63 -▁wisdom -▁bugs -▁Sisko -▁Müll -itur -itär -▁Cant -▁Fusion -▁FTP -ulent -▁anre -▁Zentren -▁einsetz -▁Anleger -ipen -ipend -▁safer -öser -▁Exekut -ynast --mon -▁Skif -▁Carol -▁optimiert -▁landmark -▁classification -▁schriftlichen -▁Magazine -▁Stabilisierung -▁Provin -▁anzuerkennen -▁Recently -▁morgens -▁vermieten -▁empfehl -▁dramatically -▁bottle -größte -▁punishment -▁obstacle -▁verwandeln -▁Vortrag -▁sequence -▁irrespective -▁Against -▁endorsed -▁admire -sing -sorge -zum -luft -▁Tast -▁Taschen -▁boutique -▁Pisa -▁cattle -▁CAR -▁GO -stande -tration -▁dekor -▁aufgetreten -▁versprechen -▁Price -ugung -▁exotic -▁allt -▁commerce -▁bedingt -▁herange -▁Germans -ymn -▁Police -▁immunity -▁definitive -▁Begleitung -▁Organisations -▁Edit -▁Nordkorea -▁profitiert -▁Mannschaft -▁english -▁Zuliefer -hardt -▁freiwillig -▁beeindruckende -▁по -▁Zuschauer -▁exzellente -▁MEDA -▁nachfolgenden --Entwicklungs -▁Petersburg -osexual -▁guilty -▁bestehend -▁Nordirland -▁bub -▁BER -erhö -usik -▁FS -▁Gonz -▁inappropri -stahl -▁stagn -▁Instit -▁erholen -▁aufrufen -ellungen -▁-T -▁umgew -izard -eway -echts -▁Param -ervices -▁Alten -gebungen -▁hero -▁festivals -▁serb -▁Frequenz -▁Weste -mäßigkeit -▁reader -▁erzielte -▁alternativen -▁Angeboten -▁intensiven -▁dynamisch -▁terrain -▁kritisieren -▁Estland -gültig -▁definitiv -▁Streitkräfte -▁PayPal -▁küm -ergebnis -▁Additional -▁Mittagessen -▁Vieh -▁praise -▁Stephen -▁vibrant -▁Posselt -hu -door -restaurant -vre -ен -▁horm -▁Toten -▁Iceland -▁SN -▁Sender -▁PA -iso -▁Häusern -▁inland -ivirus -anbieter -amine -▁Langen -trock -▁gez -▁Erhebung -ensp -performance -▁agieren -ittings -▁Nachweis -▁10.00 -▁Zusammenfassung -▁versorgen -▁php -▁Maßst -▁Andrea -▁advisory -▁far-reaching -▁fastest -▁textiles -▁completing -▁Netzwerke -▁1971 -▁Eurob -▁Paar -ACE -▁Rauch -▁Ausgangs -▁unternehmer -▁snacks -▁Rohstoffe -▁trials -▁gambling -▁eingenommen -jar -ég -▁müh -▁Sänger -▁DG -▁Dür -▁pestic -▁gän -erd -eman -omo -▁kat -▁Kass -▁Hague -▁Rate -▁erbringen -▁einrichten -oded -▁quo -▁Spock -immten -▁Kondition -▁Chancellor -▁remedy -▁Klaren -▁Nev -▁modernisation -ynes -▁improves -marktes -▁Scandinav -▁Patient -rechtlich -▁verbundene -▁aufgeführten -▁Consider -▁Oliver -▁anerkannten -▁baths -stechnologien -▁gaining -▁Poettering -▁rolling -▁sleeping -▁beschleunigt -▁flexibler -▁Birma -▁Tuscany -machung -schrauber -oors -rine --9 -▁tens -▁Taste -▁DL -▁Pixel -▁kalt -▁Feder -▁erfr -▁wholes -▁entf -▁Engel -▁Erz -▁en-suite -▁Archit -▁betragen -▁buttons -▁gesenkt -▁Meet -▁Gender -▁Stadtm -▁benutzer -▁Durchführungs -▁Malaga -▁hervorzuheben -▁Nutzern -ITY -▁Slidesh -▁aufgegeben -▁Konsolidierung -▁konkreter -▁Gebäudes -▁Rotter -beschäft -▁Brosch -▁courtyard -▁Sandstrand -▁Bandbreite -▁Franzosen -▁Sachverständ -ektionismus -▁barrier -▁consolidate -▁Ungleichheit -läufigen -▁witnessed -▁Championship -▁unzählige -▁Gesetzgebungs -night -üp -jev -▁tu -▁hunting -▁fügt -▁männ -▁Tragen -▁Isch -igste -orien -▁Want -▁Keller -berry -▁stere -▁stützt -▁Promenade -▁Verwand -egal -ialen -▁Spannung -▁Umstellung -▁Bedrohungen -▁Schlüss -▁Neuen -▁modifications -▁befreit -▁Feier -▁stecken -▁sheep --West -▁Cohesion -OSS -▁volumes -▁Pfeiler -▁IND -▁DEM -▁erhielten -▁irgendwann -▁Harmonie -▁paragraphs -wiegend -▁Follow -▁Klimasch -▁gewaltige -▁Algeria -▁Cancún -▁licensed -▁Warsaw -▁beauftragt -vas -▁hoc -▁femin -▁lust -dehnung -▁Lov -eständ -▁Zell -tery -cking -▁Jim -endums -▁übernahm -▁Though -weiß -▁genoc -▁disabilities -oyd -▁10.000 -▁geschah -▁operative -▁secular -▁costly -▁XX -▁Begrenzung -▁introduces -▁settle -▁sends -▁clauses -bestätigung -▁Einzelf -▁assessed -▁gedruckt -▁höherer -▁meaningful -▁1990s -▁kräft -▁behindern -▁Wilhelm -▁complementary -▁vorzugehen -▁fabulous -▁Algerien -▁tasty -antasie -▁maßgeblich -▁tendency -▁Subsidiaritätsprinzip -▁gelegentlich -▁ggf. -.8 -.7 -diensten -wear -world -TM -post -gir -▁tensions -▁Dream -▁Aar -▁HI -▁fortress -▁anst -leys -▁reimb -terior -▁chap -▁chart -ermost -▁5000 -strich -▁Zeitalter -▁liefer -▁Verbindungs -chtenstein -▁versuchte -garant -▁Bogen -▁activate -▁Restaur -▁iranischen -▁limiting -bildungen --Hand -▁Disziplin -▁Texten -▁coastline -▁Vollständigkeit -▁Cardiff -▁Villen -▁psychological -▁painted -▁genetisch -▁verantwortungsbew -▁Beschwerde -▁Objective -▁folgte -▁stetig -▁scholars -ounds -fels -krank -LICH -▁melt -▁baggage -▁Mön -itary -▁Wollen -▁unexpected -▁fore -▁Zen -▁demselben -▁demnächst -▁entnehmen -▁Einmal -▁commands -▁Yacht -▁Bestände -▁couldn -▁Button -▁Energiem -▁Ausschüssen -▁traditionally -▁Designs -▁II. -▁aufgegriffen -▁reaff -▁Akademie -▁altitude -▁oriented --Systems -▁juristischen -▁Erholungs -▁Reichweite -▁Beobachtungs -▁gründete -▁kommunistischen -▁birthday -▁McCart -▁erstklassige -▁distinctive -▁Schauspieler -▁Wikitravel-Benutzer -vereinbarung --Mediterranean -▁shoulder -terrasse -▁Fotokop -▁Verschlechterung -familie -wein -wider -ál -▁Eiff -▁Araf -▁kol -▁Kug -▁FIF -▁VIS -icide -▁anschauen -ereign -▁Zak -trieb -▁defen -▁verfolg -▁-D -earning -▁1900 -ystemen -ankungen -zuerhalten -drom -▁Final -itäre -▁Franco -▁Partition -punkten -▁gemachten -▁verläuft -▁24-hour -▁identifying -▁Official -▁bankrupt -▁container -zulassen -▁sorts -▁jemanden -▁holen -▁Somalia -▁Reisenden -profit -▁threatening -▁ermord -▁verlängern --Lösungen -gelegenen -▁prerequisite -▁Überwindung -hilf -fehler -jack -та -▁nett -▁ras -▁Deal -▁oasis -▁loy -▁Wende -▁yield -▁Lyn -▁Ner -▁gefertigt -▁Anleitung -▁Angestellten -▁dairy -▁Eras -▁Ermitt -anza -iento -▁indications -▁bedaure -standes -▁tunnel -▁landing -▁kostenfreien -ORK -▁protects -▁scheinbar -▁emphasize -▁Ukrainian -▁popularity -▁Übereinkunft -▁Fischereise -▁fragile -▁Sicherstellung -▁QuarkX -▁broadcasting -▁gemeint -eability -▁vigil -▁Fotografie -▁aufrechtzuerhalten -▁Einstimm -▁Chirac -▁ThyssenKr -▁geeinigt -▁rhythm -▁Handhabung -VD -▁fare -▁misl -▁Takt -▁Iz -▁Must -igma -oland -▁Wag -▁Kamp -▁Fett -▁Veter -arisch -asm -amos -alitäten -▁alph -▁begeben -schub -tro -▁abschließ -▁Schätzungen -▁Enth -ourge -acle -iane -ounce -▁Marg -▁Produkts -▁Medic -▁passport -▁Portfolio -▁Strafe -▁1.000 -▁bevorzu -▁nuklear -▁xenophob -▁Tauchen -wogenheit -▁tracking -▁painful -▁Megap -▁nördlich -▁Beautiful -▁widerspiegelt -▁erhöhte -▁weiterentwickelt -▁einbeziehen -▁earthquake -▁ThyssenKrupp -yball -▁tons -▁Däm -▁Eden -▁pipeline -▁Battle -orum -▁in-house -▁algorith -▁Led -owina -▁asc -forum -▁Angehörigen -ubt -▁aboard -▁Schad -▁entziehen -▁coat -▁Aussehen -schaftlichen -▁Eins -▁Arbeitsprogramm -▁legacy -ancement -▁worlds -▁Handbuch -▁seinerzeit -▁Finanzminister -▁Rechtsprechung -omatic -▁Fahren --Le -▁affirm -yram -▁Century -▁loop -▁Euratom -▁separat -betreiber -▁untersagt -▁exploited -▁Wechselkurs --2006 -▁verteidigt -▁korrigiert -▁confidentiality -▁einheimischen -bewegungen -▁refreshing -rot -wal -bird -Sh -MP -42 -▁Meas -edt -offt -▁Hüt -▁Gitar -▁unified -reuen -vertreten -verhältnis -▁Stille -▁auszur -▁encl -▁Einleitung -▁Vorbe -▁reluct -▁konsequ -ächten -▁belastet -gebend -▁versorgt -▁privater -▁Boots -▁13th -▁shorter -▁Liechtenstein -▁entsprechender -▁Magn -▁Konferenz- -▁Nachtleben -▁renewal -▁billigen -▁Wellnessbereich -▁graphical -▁spreading -▁forestry -▁Fertig -▁Inneren -▁Ursprün -▁Cafés -▁erzählen -▁Astrium -▁widerspiegeln -▁Anfänger -▁Toskana -,1 -test -yption -41 -62 -▁förder -▁bird -▁DO -▁Auß -itches -orders -▁cord -▁Five -▁alert -▁pricing -▁Lut -▁LEG -lebt -▁regener -▁vermehr -▁Vergabe -▁Parking -errungen -flügel -▁Rezept -▁interact -▁incidents -axis -▁evenings -▁Whatever -▁differenz -▁persist -gewiss -▁retained -ässige -▁biof -iquen -▁sexuelle -▁widmet -▁Verkehrsmitteln -▁Infine -▁zerstör -▁unterschiedlichsten -transport -▁verwandelt -▁Secretary-General -▁beunruhigt -▁Haltestelle -▁toxic -▁schuldig -abend --vis -jer -jana -▁nit -▁Adress -▁Cort -▁Cau -▁unüber -▁unkompl -ierbare -ceive -erning -plung -undi -▁fridge -izations -▁Ersten -ervor -▁Einerseits -▁Einsparungen -twer -▁Aufstellung -▁County -kehrungen -opez -▁erstmal -▁Romul -▁hervorr -▁marble -▁Geschäftsreisende -▁antique -▁Garage --down -▁Angebots -▁Informatik -▁encourages -▁attacked -▁oberste -▁quarters -▁Twin -▁Seminare -▁Dolomites -▁flexiblen -▁Gremien -▁Kontinents -▁Geburts -▁osteuropäischen -▁Property -▁Thessaloniki -Point -▁ses -▁Tepp -▁Match -▁PN -atar -▁beheben -opy -terity -▁mitzuteilen -übertragung -▁Stoffen -▁Ansehen -▁umbr -▁resign -▁ampl -icker -eutet -▁Aufteilung -▁ele -▁hinnehmen -▁10th -iversität -▁Demo -athan -▁Kommissars -▁Palais -▁entfernten -unterricht -▁Jane -▁settlements -▁Upper -▁Präsidentschafts -zyklus -▁betreffende -▁getroffenen -ungsverhältnis -▁verursachten -▁unternimmt -▁Erkrankungen -▁bathing -▁vertiefen -▁Schriftsteller -▁recognises -▁Croatian -▁enforce -▁großzügig -▁Jobs --Qu -▁einschlägigen -fun -gien -▁ht -▁fits -uto -▁cum -▁kW -▁kapital -▁RES -▁GL -▁unannehm -iein -arien -istol -▁aufgeben -ehouse -iba -▁chips -▁grid -▁kontr -▁Exempl -▁ordering -▁ganzheit -▁schwäch -▁organs -▁Instabilität -▁Bemühen -▁Zusagen -▁westliche -▁Gedächtnis -zwecken -▁Economy -▁Venet -▁Momente -▁automobile -▁Schriften -▁lifts -▁trendy -▁petition -▁veranlassen -▁uneingeschränkte -▁verbindliche -▁Ministerpräsidenten -▁encountered -▁Scientific -▁Freilassung -▁Kolumbien -▁Klavier -▁Authent -▁Unregelmäßigkeiten -▁aston -▁Daneben -insel -archie -▁anscheinend -▁Nächte -owse -▁Inspir -essim -anti -ikers -▁Schein -▁schaden -▁Parc -▁3,5 -▁Abfahrt -ishop -▁trick -▁obgleich -arbeiteten -reihe -stellbar -▁gewahrt -etrieben -▁processor -▁einfaches -▁betrieb -▁Kino -▁Gemeinschaftsebene -▁studios -▁Volumen -IST -lauber -▁donor -▁Hochschule -▁biologische -▁missbrauch -▁safeguards -▁Propag -▁Turnier -▁Mercure -ioxid -▁durchschnittlich -▁temporarily -faktor -▁Geltungsbereich -▁Chinesen -▁dioxide -▁Darlehen -▁QuarkXPress -hoch -hire -IEN -▁Tav -▁buch -▁Self -▁pup -▁Mes -ety -iguous -▁zoom -▁Cape -imeter -▁refin -▁Zeilen -herapie -▁Stufen -endungen -▁abgedeckt -▁Schall -▁Mitch -▁lev -aukas -ologies -▁Orth -▁Warnung -▁Lebensbedingungen -▁Industrien --Code -▁hochrang -▁Decision -ICA --Anwendungen -▁Gehör -▁hybrid -▁Erdgeschoss -▁enhancing -▁PHARE -▁impressed -▁ideology -▁scripts -▁verwendete -▁potenziellen -▁shaped -▁Catalunya -▁incons -▁Beantwort -▁EU-Bürger -▁Lettland -▁gänzlich -uate -vr -RS -▁vene -▁beam -▁Dell -▁categor -▁kar -ivities -anol -verträge -▁Jarz -▁vorgetragen -▁Fraktions -▁reloc -▁underp -klicken -etry -▁economical -▁Teils -▁fließen -▁angekommen -▁playground -▁Emot -▁Schwanger -▁Feucht --market -▁Begleit -▁Turin -▁Angola -▁magazines -▁Perfect -▁unverändert -▁mounted -▁Luftverkehr -▁Container -▁evaluated -▁responding -▁holder -IFE -▁OSZE -▁geehrter -▁tailor -▁Übersetzer -▁floods -▁batteries -▁municipality -▁appelliere -▁Diagnose -▁Viertens -▁¿ -zutreten -low -ägliche -ри -▁E-mail -▁Eye -▁puls -▁Body -▁Gam -▁nein -▁Unit -artial -▁überdenken -▁überarbeitet -▁enden -▁Aufstockung -fahrzeugen -werter -▁Seven -▁geste -▁attitudes -▁Try -▁Genfer -▁Beschränkung -änglichen -▁persecution -freies -▁stärkt -▁Christus -▁merch -untu -▁ratify -▁Süß -▁Beschäftigungspolitik -▁imagination --Präsident -▁Fax- -▁Matth -▁territorialen -▁bilaterale -▁Erweiterungen -robat -▁enemies -▁Veranstaltungsräume -▁herkömmlichen -▁astronom -▁Biokraft -▁neighborhood -▁Rotterdam -fi -sicherung -AX -▁dub -▁races -▁Money -▁Pisten -▁galt -itives -▁ehren -▁Father -gehalt -▁anschließ -ehear -agne -▁Schauen -▁schall -▁selekt -▁Verbrech -▁remed -▁besuchte -folged -▁Polo -▁correction -▁1.6 -▁barg -▁hochqual -▁hinausgehen -▁casual -▁potato -▁Verwenden -▁positiver -▁assessing -▁kultureller -▁inspections -▁routine -▁conducting -▁responded -▁Bestandteile -▁Ausflug -▁Inc. -▁Mitgliedsländer -▁captured -▁privileges -▁israelische -▁stimulus -verantwortung -▁gedenkt -▁Befreiung -▁Frühling -rott -makers -UE -▁hang -▁nad -▁Soph -▁Bod -irma -▁löst -▁Wid -▁Hack -▁Rum -▁dependen -▁überleben -ovol -▁pläd -beck -▁trinken -▁YOU -ändlern -▁Übertrag -▁Brücken -setzungs -▁Bettw -▁blow -rängt -▁understandable -▁administr -▁2014 -▁intensiver -▁broadly -▁Lieferungen -modelle -▁Streben -▁titles -▁ELDR -▁Rosa -▁preserving -▁Symph -▁WordPress -▁verbesserten -▁burning -▁Geburtstag -▁disadvantage -▁democratically --Produkte -▁Connect -▁Pyren -▁Liebhaber --Sicherheits --ad -pflanzen -KK -▁tyr -▁nick -▁bout -▁pushing -ofen -▁ox -▁cir -▁kenne -▁Vö -▁untr -icable -asy -▁Living -▁verlaufen -▁exac -▁shell -▁vorwiegend -▁Erlaubnis -ennungs -▁unterrichtet -▁möge -amme -ilege -▁Plätzen -▁invasion -▁mini- -▁Zusammenhalts -▁visibility -▁experiments -▁Gläub -▁14th -▁zweites -▁Terrassen -▁behilf -▁Doktor -▁Perl -▁transparente -▁continuity -▁Gemäl -▁Machine -▁Napole -▁imbalances -▁Zinssätze -▁Gefühle -▁Nürn -▁inappropriate -▁Beantwortung -dar --bed -▁Istr -▁Ign -▁Script -isierter -ologen -anium -▁Lizenzen -tehen -hrend -▁deployment -▁geführte -▁-K -ahoo -ieth -ärkte -gl. -chtungen -▁Chor -▁outermost -▁indischen -enzo -▁estimate -▁Versicherung -konto -otted -▁11.00 -▁closest -ziell -▁nächstes -unterschied -▁institute -ächtige -▁Crown -▁wunderbar -▁Analyt -▁quoted -▁Zentralbanken -▁beliebige -▁ehrgeizige -▁Sozialdemokratischen -▁verweigert -▁wholehear -▁Symbole -▁Delegationen -▁measurements -▁attentive -▁Klimaschutz -▁exacerb -sport -OUR -ât -▁Tasten -stufe -▁112 -▁einräumen -ehand -ading -▁ausgere -▁aussieht -easant -▁unterzeichnen -▁durchd -▁bestrebt -rative -▁hinnehm -▁Depression -▁aven -▁directors -▁Atem -▁versichert -zugang -uchtet -▁activists -▁Euro-Mediterranean -haushalts -▁Spaniens -▁fairer -▁230 -würfe -▁obliegt -▁herauszufinden -▁Empfangs -ombard -▁Gastgeber -▁timing -▁birgt -▁Verstöße -▁Wiederaufnahme -▁injury -kapazitäten -▁veränderten -▁wiederherzustellen -▁umfangreiches -▁Konservativen -▁Ausserdem -▁geplante -▁GIMP -folgedessen -tax -access -▁dachte -▁miß -▁mangel -▁TEN -▁bass -igm -▁Welle -▁CIA -▁Fische -▁Flair -imon -ulette -▁pretext -▁Niel -heet -▁wire -ortium -▁Unrecht -▁abzustimmen -amento -▁rocks -▁sowjet -▁underest -▁Überschwemm -▁problematic -vorhaben -▁ganzes -▁Schwächen -▁statutory -▁Student -▁Kontinu -▁devote -seitigkeit -▁Kanäle -▁unerwün -▁Langzeit -▁1956 -▁credits --Programms -▁verfasst -▁Kohäsionsfonds -▁überschritten -▁Kriege -▁Ferrari -▁Zertifizierung -▁varying -▁Friends -▁behilflich -hänge --v -kit -55 -OY -ки -▁pav -ilog -chio -▁cant -▁Vitor -ufer -▁stoßen -▁Stars -inded -ocon -▁nachkommen -üne -▁Vorjahr -steigerung -▁6th -▁dropped -richtlinien -▁befindlichen -▁retention -▁competitions -klärungen -▁metals -▁Gleiche -▁Innenhof -▁Flora -▁Applikationen -▁staatlicher -▁seekers -▁Motorrad -▁teilnehmenden -▁Zufall -▁älteste -▁gathering -▁verschwunden -imensionen -▁Workshop -▁durchschnittliche -info -▁Befürchtungen -▁recreation -▁viruses -▁diplomacy -▁Autovermietung -▁Recycling -zucht -IES -▁töd -▁BV -orbenen -▁Hund -▁Nest -▁Zoom -verages -▁jens -▁escal -ighth -▁coin -▁Auszeichnung -rency -aufsicht -▁interconn -▁geliefer -ziehe -▁Quite -parteien -iefung -zufriedenheit -▁gentle -▁üben -▁initiate -▁häufigsten -▁zeitlich -▁launching -hotels -▁Badewanne -▁Nahrung -▁Schriftliche --Plan -▁Alexand -▁hoping -▁cooperative -kopf -▁gewaltigen -▁Reisende -▁Konfigurations -▁Pferd -▁Mitentscheidung -ocaust -▁schrecklichen -gängig -▁fossil -▁erledigen -▁genocide -▁Erasmus -▁³ -CAD -▁tired -▁harsh -▁mes -▁Type -ethe -▁Röm -umgebung -▁Nit -▁stainless -▁aufgeteilt -eint -▁soviel -▁Ausschluss -acion -acter -▁gründet -▁ihrerseits -▁hinder -▁Bedauern -▁Articles -▁handelte -bewusstsein --Bus -▁folgendes -▁passage -▁Glob --Gesch -zufordern -▁2.1 -▁wahrhaft -▁hosted -▁Corbett -▁Temple -▁virtuelle -▁Herzog --wing -▁harder -▁Buy -▁feeding -▁Check-In -▁Gardens -▁formulieren -▁AVI -▁Cleaning -▁eggs -▁Füßen -▁Competition -▁Wohlergehen -iè -ihen -samer --30 -▁Ship -▁EIN -▁gt -elect -▁unend -imar -▁forever -▁Lack -▁rechte -tries -▁weise -▁Sieger -▁Anth -izeil -ermo -feiert -ologically -oyer -▁Entschuld -▁Sekret -▁Berechn -▁Bürgerkrieg -▁halbe -orno -gewichte -▁Durchbruch -▁Below -▁Kodex -▁Protektionismus -▁Damals -▁Glückwunsch -▁Tunnel -technologie -▁Immobilienanze -▁Wallström -▁withdrawn -▁vegetable -▁Mitgliedsstaaten -▁Dienstleistungsrichtlinie -▁conscience -▁Dalai -▁schweigen -▁embargo -▁Salvador -▁transeuropäischen -▁precautionary -din --pro -L. -▁rival -▁ruft -▁Side -▁Mind -▁Pocket -▁BAS -▁Aven -uta -▁lending -▁Korn -▁yacht -idsch -stan -istet -▁1,000 -▁-F -▁manually -amentos -acional -▁compiled -▁laundry -▁problematisch -pielen -▁angehört -▁daraufhin -▁Gewässern -▁Adjust -▁artic --Res -▁listing -▁benefic -▁positively -▁gefährlicher -atastrophe -▁pursuit -▁salon -▁motorcy -▁attending -▁1980s -materialien -läufe -▁Prüfungen -▁wheels -▁AKP-Staaten -▁Wäldern -▁Festplatten -FORM -M. -Office -▁dol -▁dossier -▁Aquar -▁lifelong -▁Wat -▁Kne -ifs -▁verarbeiten -▁Verurteilung -ehle -illes -▁escap -▁sprachen -▁bester -▁Vormittag -▁dirty -▁Mari -▁Übersch -▁Flucht -▁Conil -▁1200 -▁gemeldet -▁besorgnis -▁successes -ässe -gemacht -▁Haushaltsausschusses -▁portable -▁establishments -▁Radisson -ibration -▁professionell -komponenten -▁burdens -▁semi- -▁Konsequenz -▁Baumw --Im -potenzial -▁ausgebild -▁myster -▁Weihnachten -defizit -▁prolong -▁Klingon -▁Intensiv -▁Biotechnologie -▁zwingend -noten -dor -▁Tang -▁Sor -etre -olis -▁Wine -▁unh -ascript -▁anmerken -quiv -▁fürchte -roup -▁Auslieferung -▁3.1 -▁Parlamentarier -▁solarium -▁Handlung -▁Bestands -▁instances -▁backup -▁mirr -▁Gewässer -▁studierte -▁organising -▁bookings -▁Meerblick -▁zusätzlicher -▁Instanz -▁radi -▁stresses -▁Erzeugnissen -▁Botschaften -▁Tierschutz -▁befürchten -▁Konzeption -IFT -▁coordinating -▁Sachver -▁Belastungen -▁disappeared -▁Dubrov -▁deliberately -▁Singapur -widr -▁Scottish -▁Frankfurter -▁Infineon -▁Jarzemb -▁besorgniserre -hus --east -82 -▁Sic -▁EOS -▁Born -▁COS -▁Rhyth -▁toys -▁Nass -▁Init -▁erzeug -genehm -▁Ohr -ahm -▁abhängt -▁ausschließ -▁vorank -eckte -ovon -errung -▁disarm -▁anymore -▁7th -▁Laundry -▁Reding -▁defines -▁packed -▁template -qualities -▁collecting -▁Stärken -▁Privatisierung -▁wissenschaftlich -▁jeglichen -▁Trock -▁Geistes -▁Zeugen -▁steigende -▁Josef -▁Egyptian -▁Sympt -▁Athen -▁beeindruckenden -▁Equipment -▁Keramik -▁Auseinandersetzungen -▁Behandlungen -▁gigant -▁Erleichterung -üchter -▁rass -▁sau -▁bitt -▁Sha -oner -▁Cin -▁Cambod -▁C4-0 -▁Hem -▁unch -former -▁Stolz -pricht -▁spam -spot -akische -glas -gebnis -▁marking -▁deckt -▁mines -▁begannen -▁Bestreben -▁detected -▁dealers -managements -▁foodstuffs -▁1965 -▁überwältigen -iscipl -▁Records -▁Forschungs- -▁Gehirn -▁OSCE -▁eliminating -▁Flusses -▁Electronic -▁Cambridge -▁Ecofin -▁Babys -▁bürgerliche -▁Kürzungen -▁Prozents -▁Ökos -▁commodity -▁feasible -▁Alicante -▁Klärung -▁unangem -oust -vell -mans -▁fer -▁föder -▁seam -▁buffer -▁Seas -unters -▁eco -▁Hop -▁unauf -earl -▁Ausdehnung -▁Alz -hness -akult -prob -gli -▁Rekord -wirr -▁Verbündeten -▁geschicht -▁Barbar -▁accessibility -▁Monument -▁folk -▁1400 -▁historischer -▁swim -▁unvergleich -tauchen -▁liberty -▁corrupt -▁Eingangs -▁Castell -▁empfehle -▁juristische -▁Rundfunk -▁Electric -▁fulfilling -▁ehrgeizigen -▁anspruchsvollen -▁einzigartiges -▁japanische -▁Learning -▁Ausgewogenheit -▁umgewandelt -▁Arafat -▁jenseits -fast -sbereich -meter --fl -WA -FL -VER -▁holl -▁Misch -orously -▁cand -▁cure -▁Lip -▁stro -▁seed -vergabe -▁OEM -▁nachteil -führenden -klick -aires -weitet -▁Brows -▁Herunter -voraus -▁eingetreten -▁Richtungen -atorischen -▁Mittelalter -▁farb -ENS -▁Haushaltskontrolle -▁family-run -▁Schulung -▁Gestern -▁reminded -▁Questions -▁emotions -▁CRM -▁Liikanen -▁mortg -▁wholly -städte -▁persuade -▁Evaluierung -▁weaknesses -▁overseas -▁offenkundig -▁Zagreb -▁Affiliate -swert -во -▁hergest -▁druck -▁rice -▁sé -▁EA -▁BG -▁Bout -▁Kommen -iereinrichtungen -▁repos -teous -▁deinem -▁auszubauen -▁coating -eware -▁ample -zepts -▁outline -elso -ändigkeit -▁berechnen -▁possession -▁restlichen -▁Interventionen -▁varieties -ANN -▁networking -▁optimized -▁Erfahrungs -▁ukra -▁graus -▁Magnet -▁längeren -▁Acrobat -▁Reinigungs -▁celebration -▁strukturellen -▁fehlenden -ubehalten -▁Serviceleistungen -▁komplizierten -▁accomplished -▁fremden -▁irregularities -▁socio-economic -▁concessions -cussions -▁Jarzembowski -oj -ögert -Cs -▁nan -▁fatal -▁wing -▁São -▁Pfl -uri -▁Gothic -▁alien -▁align -leidung -endit -undung -▁shelter -▁polnische -▁Archer -▁Spitz -▁aggreg -chtest -▁Konf --Seiten -▁konstru -ischerweise -lieb -▁writers -▁multitude -▁singer -▁Carmen -▁absoluten -▁Patri -▁persönliches -▁üpp --Welt -▁Öffnen -▁Liberals -▁banana -▁archives -▁1957 -▁Lagerung -▁Löhne -▁Knowledge -▁ermutigt -▁contradiction -▁incredibly -▁Ankara -▁erledigt --Partner -▁Brutto -fo -kos -▁halls -▁Seil -itement -onomen -▁Cany -▁GE -ktiken -▁Zoo -abs -▁confort -▁Jungen -ehow -agua -▁Anson -▁exot -mensch -▁Weber -▁iT -uevo -▁speculation -▁wiederauf -▁experimental -▁Wortlaut -▁pressures -▁Sciences -▁Periode -▁massiv -▁Willens -▁Course -▁allgemeiner -▁draws -▁Anstatt -▁influences -ospace -▁preparatory -▁Estate --Verhältnis -▁IMM -▁replacing -▁Kaliforn -▁GMOs -romagnet -entscheidungen -▁voranzubringen -▁upgraded -▁Cadiz -▁Pestiz -überwachung -300 -▁sü -▁Tul -▁Tay -▁Pensions -initiativen -anmar -▁thou -▁forderte -▁promenade -▁Lago -▁reign -esta -versch -essing -▁Unfälle -▁quasi -▁großartigen -▁debts -▁reveals -▁Themas -▁bekannter -▁Share -▁komment -▁protectionism -▁Berichterstattung -▁generating -▁turnover -▁herausgegeben -▁donation -▁Motion -ambre -▁broadband -schwing -▁Bajor -▁premier -▁ICT -▁facilitating -▁GNOME -laga -▁ranks -▁Platform -▁etabliert -▁inequalities -▁beherrschen -▁echtes -bearbeitung -▁sensiblen -▁schnellstmöglich -▁bevorzugen -ao -wörter -▁Dig -▁Mant -unami -▁Fox -atical -amper -▁reinen -▁ODER -▁descriptions -ersey -ikalen -▁quart -ritz -▁Abreise -▁Lev -ändiger -nez -▁real-time -▁Marie -▁weitergehen -abler -▁einzuf -reffend -▁Fortunately -▁Betrieben -▁Filmen -▁eingegeben -▁tariff -.03 -▁1966 -▁Kampfes -▁Talent -▁Beitrittskandidaten --Player -▁Schmuck -gehörigkeit -▁variations -▁self-em -▁ERP -▁diagnosis -▁Variablen -▁segments --Management -▁hydro -▁Prämien -▁Errungenschaften -▁zusammengefasst -▁Fotokopiereinrichtungen -.10 -hill -▁heter -▁aan -▁sitzt -▁Iber -▁Sag -▁Ble -▁Brat -▁Birm -▁og -▁lehnt -▁Roten -▁tout -estock -▁verhaftet -ibes -▁descend -▁demograf -▁vorang -▁Einsicht -▁Umwand -veloped -▁applaud -▁objections -▁historisch -▁earmark -▁immers -▁Demokratischen -▁self-s -▁Martini -▁exportieren -lässigkeit -▁updating -▁orientieren -▁Sloven -▁Routen -▁Meinungsfreiheit -▁CNC -▁luxuriösen -▁configured -▁disadvantaged -▁exemption -▁IRC -▁macroeconomic -▁arena -▁drittens -▁Lomé -▁TACIS -▁Dubrovnik -▁Sell -▁Posten -▁Buchen -edged -uras -▁Ryan -▁thy -tral -verk -▁deport -▁aufw -▁übersehen -▁vorant -urecht -▁EU-In -▁gesendet -▁Appar -▁Advis -▁Shops -▁Toul -▁ernsten -▁dealer -▁informations -▁Bewirtschaftung -▁shortage -▁Republ -▁Lieutenant -▁Schulungen -ambots -▁built-in -▁purchases -▁Informationsschalter -▁entdeckte -▁obsc -▁vertrauens -▁cosm -▁lifting --Programme -▁Säulen -▁Champs -▁Aktualität -▁drohen -▁Snacks -▁Rabatt -▁herbeiz -▁erholsamen -▁Völkerm -▁Kurdish -▁Erwägungen -conditioning -human --X -46 -ны -▁dal -▁skept -▁widen -erfolg -itations -ofar -▁Wes -▁Rin -▁unp -ulative -▁imstande -▁stamp -▁stieg -▁seid -▁diesjährigen -pling -▁überrasch -▁auszuführen -▁Auswirkung -▁Almost -führende -presso -▁langjährigen -▁Neigh -ronen -▁blicken -▁ästhet -▁multip -▁chef -▁Patente -▁fragt -▁speeds -▁orientiert -▁Annan -wechselt -▁Jesolo -▁Kommunismus -▁elektrischen -▁Olivenöl -▁anticipated -ocopying -▁Mobiltelefon -▁Heinrich -▁fähig -läßlich -▁Ansonsten -räder -seiten -bels -IZ -UD -▁mos -▁Dimensionen -▁BBC -esar -onne -▁Göte -▁instruction -▁ankommt -▁Liga -▁reper -▁Zentrale -platte -indruckt -▁austerity -▁2050 -▁Commons -▁einziger -▁Politikern -▁retreat -▁cultivation -▁Komit -bauer -▁Asset -▁Perf -▁Oppositions -▁grain -▁attracted -▁consultants -▁springs -▁poorer -▁wissenschaftlicher -▁Fischereif -sprüch -▁Mailing -▁rigid -▁kontinuierliche -▁überschreiten -▁Rapid -▁Diamant -▁Controller -▁negligence -▁tissue -▁rhetoric -▁Rücktritt -▁Einstimmigkeit -män -bag -emos -▁Wave -▁Vac -▁begehen -stad -schiffe -ingang -▁einwand -▁Ott -▁vorsehen -▁disprop -▁Auftreten -▁outbrea -▁Umstand -▁preview -▁monastery -▁attain -▁ausgeht -▁Noten -▁Energieverbrauch -▁Universitäts -ISS -▁Moder -▁verschlechter -.01 -▁Maybe -ICK -▁Meta -▁sofortige -▁Folgenab -▁Milliarde -▁Telefonnummer -▁voraussichtlich -▁corresponds -▁tackled -▁hospitals -▁tutorial -▁Fitnesscenter -oeuv -meisterschaft -▁beurteilt -▁unberühr -▁christlichen -▁bulk -▁renommierten -▁Behauptung -debatte -▁freigegeben -▁bizar -▁catal -▁laptop -▁Cass -▁Hang -▁Vital -kthrough -ifically -griffe -▁Erforschung -▁Erwärmung -obi -schaftliche -▁unterw -▁accus -▁outer -▁elimination -gets -▁productions -▁glän -▁angegriffen -▁feier -▁Trading -ync -▁Umwelt- -▁electron -▁137 -▁Lebensdauer -▁march -ylon -▁personalized -▁Weitergabe -▁Norweg -▁Änderungsantrags -▁relevance -▁self-d -marken -▁Campo -▁1948 -zwecke -▁altogether -▁wertvoll -▁angesprochenen -▁delightful -▁Akteuren -▁Anderen -▁ungehe -▁Bevölkerungsgruppen -▁Trinkwasser -▁Fifth -▁â -KR -▁fusion -▁solle -▁Tan -▁bulgar -esischen -ilber -▁Figur -hew -▁undem -▁erweist -▁eindring -▁Occ -▁Spass -beob -▁indul -utzte -▁Margar -klagt -teilte -▁Brem -ämmen -▁factories -▁complied -▁1800 -▁Personals -▁gebühr -▁Eurojust -▁Screen -▁strengere -▁Sozialmodell -▁Jahrhunderten -▁Entscheidungsträger -▁widersprechen -▁Milano -gesetzgebung -▁breiter -▁Beobachtung -▁Streitigkeiten -▁GUI -▁derartiger -▁Geheimnis -▁freiwillige -▁recipient -▁Diplom -▁Wassersport -▁Varianten -▁würdigen -▁injection -uung -wäsche --end --ray -43 -▁fakt -▁Bene -irds -▁KU -▁Cohn -▁Cocktail -▁Gor -▁ankommen -▁imply -stage -▁Orange -assy -laven -ochten -▁Beck -▁traf -▁EU-K -▁interoper -▁Mitgliedstaates -finanz -▁qualification -▁flora -▁True -▁signatures -▁Strafen -▁240 -exe -▁Films -▁strongest -▁Geldpolitik -▁temple -▁Options -▁renovation -▁Otto -▁Geduld -▁Netto -▁exercises -▁höchstem -▁eliminated -▁journalist -▁bevorzugt -▁verschärfen -▁beherrscht -▁Fontaine -▁sozioökonom -zn -▁apt -▁Tirol -▁Bost -▁cave -▁CMS -▁Gord -▁beziehe -opfer -▁versp -▁gezahlt -▁Ung -izierten -ipedia -ardin -okolle -▁truck -▁bridges -reichende -▁Clients -▁Beihilfe --Dienst -manager -▁distances --170 -▁gerechte -▁drawings -▁Schweine -spieler -▁prüft -betrieben -▁teeth -▁Trocken -▁midst -▁matching -▁vegetation -▁Atomwaffen -▁conservative -▁Nahe -▁multinationalen -▁vorbehalt -▁tanks -krebs -▁müßten -▁Straftaten -▁telefonisch -▁incurred -▁Enttäuschung -▁Caucas -▁repercussions -▁з -iw -nummern -fly -añ -äb -▁Task -▁S-Bahn -▁Deg -▁Around -▁Centr -▁Hü -▁beige -stationen -quart -▁repar -▁neo -▁asymmetr -▁demnach -▁Stütz -▁frames -oured -pflege -▁Manip -▁socialist -▁Formate -▁powered -▁Umweltpolitik -▁annehm -▁barely -regional -▁diameter -▁Myanmar -▁1961 -hauptstadt -▁Logos -▁koennen -▁Fotografen -▁Schengener -▁fireplace -▁Typen -Star -▁Übernachtungen -▁Bearbeitungs -▁veranl --Unter -▁ALDE-Fraktion -▁Continental -▁enchanting -▁forbidden -tätigkeit -fabrik -▁mp -▁Tort -▁TCP -▁Skript -▁BB -▁cocktail -alters -istin -▁Züge -▁deployed -▁notify -▁compress -▁chip -ogh -udia -▁resident -▁Auswertung -▁Einmischung -bez -ammelten -▁Cham -▁Leop --Shop -▁Alltags -▁befand -▁documented -▁third-country -▁offener -▁deliveries -▁Suchmaschinen -▁animation -▁1964 -trolled -▁Keep -▁architects -▁hygiene -begriff -bekistan -▁Roboter -▁Satellit -▁Bibliotheken -▁Terrain -▁propaganda -▁behindertenfreundliche -▁Patrick -▁Römer -▁Certainly -▁herbeizuführen -dies -▁atr -▁sister -▁wins -▁wovon -▁Purch -▁gaps -▁Kün -▁Rinder -▁Lieder -▁Instand -lichungen -▁Jap -ugar -prinzips -▁shores -ätt -▁discon -▁Jahrt -▁guiding -▁103 -▁Attrib -vorgaben -▁gleichnam -▁Äng -▁successor -▁defect -▁Modellen -▁geradezu -äuschen -▁Willi -▁Fehlern -▁Familienzimmer -▁1962 -▁trock -▁billions -▁Salzburger -▁screening -▁stattfand -▁refresh -BSD -▁promptly -▁Substanz -▁households -▁Vertiefung -▁Bezirk -▁rehabilitation -▁Bettwäsche -▁Überschwemmungen -▁Alzheimer -währung -Do -на -edict -ilde -▁Walking -▁Kamer -▁Khan -▁Cell -▁Race -▁VP -▁unauthor -▁Lj -ername -▁zugewiesen -▁erleb -▁Join -▁OM -▁Sturm -▁entschuldigen -izierte -eschl -▁Einigkeit -▁polizeil -▁finnischen -▁disrupt -haut -frem -ouver -▁incompl -▁decides -▁Bristol -▁Rathaus -brücke -▁medi -venient -▁Autof -▁organizing -▁motto -▁republic -▁momentum -▁Miles -▁Gesundheitsversorgung -▁boarding -▁occasionally -gesetze -▁Outdoor -▁Synchron -▁kombinieren -risiko -▁diente -▁mig -▁wearing -▁Tester -▁Kitz -▁Kath -icia -arme -▁Lew -▁Nin -▁itiner -tera -▁Jones -zeilen -reichend -▁Unterscheidung -php -▁crop -▁scrat -▁Trent -undering -▁positioned -▁135 -hofer -▁college -▁unterstützten -▁Centro -▁Profile -▁Coup -▁nachhaltiger -▁Fachleute -▁blocking -▁Gehäuse -▁transparenter -▁Buyer -▁Lamy -▁Communications -▁angestreb -▁confronted -▁crafts -▁heaven -▁recreational -▁Treffpunkt -▁pragmatic -▁herausragende --Mittelmeer -▁Herangehens -▁fabrics -▁Uganda -▁Pressemitteil -ruption -GI -▁raffin -▁MIT -▁gourmet -empel -olar -▁linen -▁lush -▁Cir -▁Cord -▁GAT -ieft -ankt -▁pret -verg -▁verification -▁Jury -enner -angaben -aille -steigt -rique -▁Neug -▁Qualifikationen -▁sensors -bound -▁broaden -rechtliche -▁massac -▁aufgezeigt -▁vereinen -▁Kleine -▁Ideologie -▁überwunden -▁eindeutigen --Zimmer -▁Etappe -▁Drittländer -▁junior -▁Reservierungen -▁Griechenlands -▁dramatisch -▁beträchtlichen -▁Ferrero -▁commissioned -▁schwarze -▁livestock -▁Raketen -▁enshrined -fish -förm -IAL -cements -lighten -kab -GR -ле -▁Early -▁Pick -▁Belle -eno -▁lend -▁unvollständ -iculous -opro -▁zuver -antast -▁ausschlag -▁nachgewiesen -▁Arn -▁4000 -▁Reception -▁Yesterday -▁Probe -▁Seitdem -ursor -▁Kron -▁Zuwanderung -▁Rohr -▁Italia -▁draußen -▁verschle -▁intensity -▁Starting -▁simpler -ägten -▁Vollendung -▁üblicherweise -▁erneuerbarer -▁beizubehalten -▁Naples -▁ordnungsgemäße -▁makers -▁Treibhausgas -▁aspirations -▁Bomben -▁Energietr -▁Verknüpfung -▁Slideshows -uary -wicht --way -▁font -▁Batt -irgend -ilden -ilater -onet -▁Kameras -▁HA -▁unaus -atial -alm -▁anget -▁beziehungs -▁Lunch -verzeichnis -▁verlin -▁Verwirr -prot -▁manoeuv -▁Aussprachen -▁trace -▁Aufschwung -ammlungen -▁geschm -▁Bestes -▁aktiver --Tra -▁extremen -▁Ratspräsidentin -▁administrator -verträglich -sprachige -▁Erinnerungen -▁geared -▁Promotion -▁entrepreneur -▁liquidity -▁Resultate -▁Administrator -▁symptoms -▁Jugendherberge -▁Málaga -trockner -▁reaffirm -▁Vitorino -▁Göteborg -▁unterworfen -EY -öhne -▁Sendung -▁Dj -▁Ponte -eader -dehnen -▁Neap -▁deploy -▁Jas -forward -▁Schmerz -▁schlafen -ausstellung -▁Aussteller -iac -ologist -▁alleg -▁protein -ministration -▁brown -▁Programmier -▁strategischer -▁slide --Center -▁Zusage -▁Moral -▁licences --Up -▁reichlich -▁Zivilisten -▁Friedensprozess -▁Built -▁seasonal -▁Haftbefehl -▁Serbian -▁compensate -▁Evans -▁Ethiop -▁juice -▁sahen -▁Photocopying -▁Seville -agenturen -▁Maximum -▁appellieren -OFIN -▁photographer -ubiläum -▁überraschend -hamm --one -▁vel -▁wendet -onal -▁Fabr -▁Gehen -ivile -icter -ichere -▁storn -▁Verhaft -▁Oase -laf -lava -▁manuell -▁aged -▁warme -aufnahmen -▁Esc -▁trium -neum -▁liegende -▁scal -▁versatile -▁acoust -▁privilege -▁Schwäche -▁communist -▁posting -ASP --Wald -▁promotional -▁Globalisation -▁millenn -▁motivation -bilden -▁vulnerability -▁blieben -▁Miguel -▁Passagier -▁Schwarzen -▁Kürzung -▁conversation -▁eager --Krise -▁Columbia -▁kümmert -aukasus -▁beziehungsweise -hots -▁tied -▁nar -▁AA -▁inaug -▁LIFE -▁stellten -dena -▁Anlaß -▁übersetzen -olls -▁chains -▁enters -▁jedermann -▁triple -▁Umbr -▁substitute -▁hoteleigen -enza -▁Staub -END -▁existierenden -▁Speise -▁Fischfang -.08 -▁Edward -▁Holocaust -zwingen -▁Immo -▁Morgan -▁Veranstalter -▁Pfund -▁Audit -shine -zuladen -▁Biom -▁trek -▁Standorte -▁colourful -▁ausgedehnt -▁Konsultationen -▁gezielte -▁E.ON -ergeleitet -▁Mahlzeiten -▁Dampfbad -▁ALE-Fraktion -▁WWU -▁biotechnology -▁veranlasst -bing -cup -Cl -ähn -▁mask -▁wahl -▁Emer -▁Mans -▁gaben -ità -▁Fou -▁streichen -▁Zend -igenden -▁ausschl -▁cozy -▁clash -▁Spur -▁Abwesen -▁gröss -▁Leucht -vents -▁placement -▁Ära -▁dryer -▁Rechtsetzung -▁tuna -▁Bildes -▁Kundendienst -▁ägypt -▁irrit -▁irakischen -▁versteckt -▁specification -▁Australian -▁sooner -ifikations -besitz -▁ethnische -▁earliest -▁Freundschaft -spartnern -▁konstruktiv -▁Behinderung -▁leistungsstark -▁zügig -▁Besichtigung -▁density -▁disproportion -▁Cohn-B -vation -üff -78 -▁hub -▁rush -▁seating -▁Tiger -▁pec -▁Bauer -unft -▁ehe -▁Kampagnen -▁inex -opath -▁eröffnete -▁einb -▁Verschied -▁Verspät -▁Jh -▁excluding -▁Finden -▁Sounds -▁discounts -▁Managing -▁financially -▁Schwester -▁informellen -▁unterstützte -strahlung -▁tariffs -▁Cox -▁Eingliederung -▁Knochen -▁billiger -▁Österreichs -▁geleistete -▁snack -▁accurately -▁A320 -▁alarming -sprecher -▁phenomena --speaking -▁Verpackungs -▁Brunnen -▁air-conditioning -hältnismäßig -▁auferlegt -▁unabdingbar -▁Kontinuität -▁É -rel --quality -We -▁bun -▁SF -▁pitch -▁Brad -▁cater -▁lobb -▁Had -▁Fut -ivial -amour -quin -aching -upro -▁aufkommen -▁Vervielfält -einrichtung -ogas -▁Arte -seud -bled -▁Leone -loaden -▁108 -▁somehow -▁longest -▁Tode -uzent -▁competing --Restaurant -apsed -▁215 -▁broke -aneous -▁biometr -▁merit -rücke -▁erfolgreicher -▁Telec -▁earnings -▁exceeded -▁displaced -▁drastisch -▁herzlichen -▁grundsätzliche -▁Kupfer -▁Cohn-Bendit -bury -kol -keln -▁dot -▁TOS -▁Eg -ettern -etches -elekt -▁Writ -▁Herman -▁Fu -▁Ve -▁Gig -▁offl -schwer -roid -▁umliegenden -▁Mitteilungen -▁Alan -▁butter -▁Maust -garde -▁seiten -▁designing --Komp -▁nette -▁distanz -▁non-c -▁Karib -▁innovativer -▁Komposition -▁Motiv -▁Terme -▁ernsthaften -▁sixth -▁restriction -▁Messung -▁Einwanderungspolitik -▁Überwachungs -▁Evolution -▁geäußer -▁ökonomische -▁verweigern -▁Vizepräsidentin -▁Kohlend -▁royal -▁Schokolade -▁Ecuador -▁Verwirrung -700 -▁boys -▁gi -▁Ale -unas -▁ci -▁GD -▁JPEG -▁Stücke -▁Schatz -▁Schaff -▁mais -steuers -feindlichkeit -▁hierher -ioden -▁Ask -▁assort -▁screw -▁issuing -▁parte -▁Zuhause -▁simulation -▁verletzen -▁deutlichen -▁Haushaltsmittel -▁peaks -▁Doctor -▁AGB -urfing -▁expecting --rate -ländischen -▁Applikation -gesetzte -▁Mandr -▁archä -▁Haftungs -▁nötige -▁thrown -wachstums -▁freiwilligen -▁shapes -▁zitiere -▁Ausgleichs -▁Sharon -▁jenem -▁benachbarten -▁reimburs -▁Kalifornien -turn -fire -Ex -▁fount -▁Ice -▁powder -▁Mike -itärer -utor -▁kroat -▁Wäre -▁Waffe -▁Chen -arious -▁neur -▁verified -esso -ugo -▁projekt -▁-M -▁atmos -▁abb -▁chronic -▁unterzogen -▁suits --Pal -positionen -▁severely -▁Mayor -▁Beförder -▁ähnlicher -▁worthwhile -beginn -▁folgender -▁Verschiebung -▁emergence -▁fertigen -▁rentals -▁Renault -▁Fitnessraum -▁assumption -▁detailliert -▁vertrete -▁abolished -▁einschränken -▁reflekt -▁Omn -▁veränderte -▁aesthetic --American -äme -72 -▁dated -▁bone -▁Warm -▁Kro -rev -verordnung -▁mitunter -▁werdet -▁entzieht -oves -▁Beil -▁Bezüglich -▁Arctic -▁stricter -▁Haag -▁formulated -▁Trainer -▁Schwach -gewichts -▁Spielzeug --Ru -▁Kinderbetreuung -▁Skip -▁critics -ARC -▁Supreme -▁Friedhof -▁magical -▁Fluggesellschaft -▁liberalen -▁Vertreterin -▁erschwert -▁McK -▁elektronischer -▁Available -▁criticisms -▁Pinien -▁mothers -▁außergewöhnlich -▁Secret -▁injured -nergies -▁magnetic -▁frozen -▁zufällig -▁lifetime -▁geänderten -▁Tastatur -food -▁feinen -▁räum -▁pub -▁BO -▁Rasse -asur -ufung -entes -▁jac -▁Oster -ertoire -▁mex -▁3-Sterne-Hotel -▁genügen -stellers --Stra -▁realities -▁strip -eated -vorschrift -▁backdrop -▁Elis -▁Firm -ATA -feldern -▁Sichtweise -▁1944 -▁Ostse -▁timber -▁sinnvolle -▁holders -▁beeindruckt -▁Botschafter -▁Asylbewerber -▁tastefully -▁scenic -building -▁portugiesische -▁niedrigeren -▁ungarische -▁EBIT -▁detriment -▁aufrichtig -▁memorable -▁ICEcat --discrimination -▁constituency -▁Beschleunigung -▁html -▁б -oise -renten -xen -ção -▁Tech -▁Dame -▁B2 -esst -▁zurecht -▁caut -▁Kund -▁infin -aring -▁N. -iffern -▁einm -eiter -endel -wei -▁schalten -obel -▁zumeist -ampe -▁hebt -▁650 -teilten -▁befaßt -▁Traff -▁zusammenhän -▁mußte -▁Zukunfts -▁Nationalen -▁Verantwortungs -▁Pariser -▁containers -zube -▁Informationsaustausch -▁Gemeinschaftsrechts -▁Automation -▁poet -▁ethischen -▁laufende -▁GMO -▁Entscheidungsfindung -▁bewährten -▁Differenzen -▁encryption -▁demonstrating -▁namentlich -.9 -disk -BO -▁ter -▁fügen -▁pity -▁Points -▁Ful -▁anführen -entum -▁zugenommen -▁zuzustimmen -▁Zar -▁Ohren -ehörde -▁mankind -▁Auschecken -acon -▁argu -▁Images -▁Bedauer -▁valleys -▁varies -▁Preises -▁Phoen -äufers -▁breakthrough -▁Zugeständ -▁informative -▁Skulpt -▁treating -▁generates -▁optimization -▁Wertpapier -▁establishes -▁purchaser -▁Europäern -▁ICC -▁eingeschlossen -schulden -▁clarified -▁defended -▁Likewise -embles -▁begeistern -▁abstract -▁devastating -▁zertifiziert -▁Maßstäbe -▁ermordet -tal -beam -▁fahr -▁Ty -▁Sack -▁Prad -▁Byr -▁Bungalows -▁o. -▁Kalten -▁Gates -▁verh -▁einsehen -▁Oomen -▁cluster -▁Abbas -steine -▁Berecht -▁geschützten -▁Besten -▁Particular -▁Stehende -oppeln -▁Beleg -▁installer -▁Mehrzahl -senal -▁patio -▁Rückzug -▁ECOFIN -verbände -amba -▁Vorteilen -▁dringende --ranging -▁gekürzt -▁auszuarbeiten -▁geringeren -▁literary -▁beschriebenen --screen -▁anspruchsvoll -▁palästinensische -▁einseitig -▁geografischen -▁ILO -beteiligung -▁Freiburg -▁Sogar -▁beneficiaries -hut -wier -▁Taking -▁SIE -▁pig -▁Mauern -eton -▁lays -▁Kun -▁Köst -▁Cott -▁Hope -▁Zonen -upport -forsch -▁entail -▁schott -▁3.2 -▁Auflage -▁5.1 -▁hindern -attered -▁Expo -▁Amn -▁12th -▁Blatt -▁Admir -▁confused -wirtschaftlichen -▁identifiziert -.06 -▁Amsterdamer -▁Independent -▁physikal -▁licenses -bezogenen -▁kitchenette -▁destroying -▁picked -geschäfte -▁Orchester -▁Millenniums -▁grande -▁doppelte -▁Prozessen -▁Etwa -▁Präzision -▁beschädigt -elsohne -▁incomplete -▁tongue -▁Tall -▁bikes -▁STO -▁Dail -▁pued -▁Horm -▁Gur -anche -▁Luis -iks -▁Schuhe -ipse -▁Paragraph -▁commem -▁weitergeleitet -▁Haf -▁101 -▁phases -ständigen -▁Steine -▁gemisch --Dokument -▁documentary -▁Drink -▁Sozial- -▁Planungs -▁illegaler -▁Sicherheitsrat -▁expects -▁recordings -▁Pfeil -atastroph -▁Experiment -filter -▁vertraulich -▁moralischen -▁Package -▁eingebettet -▁subsidiaries -▁Investitionsbank -▁Arzneimitteln --Vertrags -▁forecast -▁dokumentiert -▁postponed -▁icons -▁Breitband -▁Maustaste -▁Mandriva -sider -cía --owned -DN -▁vi -▁cache -▁Wonder -▁Heut -▁unwill -ieh -umption -▁UM -▁Lücken -stufen -ová -▁Vorgänge -▁gesund -html -▁endanger -▁Exil -▁Neuf -orrent -ERT -▁binary -▁Russians -▁Quant -▁lawyer -▁einmaligen -ijten -serklärung -▁totalitarian -▁copper -▁Nowadays -▁Agrars -▁probieren -▁Knoten -▁peninsula -▁mangelt -▁Tibetan -▁Wartungs -▁Schnittstellen -▁awaiting -▁kreativen -spolitische -▁forecasts -proofed -▁Medikamenten -▁Verschmutzung -▁empfehlenswert -▁pesticides -kampagne -ähre -GO -▁afghan -anet -arest -▁gewohnt -▁Anne -▁shipment -▁chaos -▁vorab -▁Beein -▁trac -klassen -▁avant -schließung -▁discour -öglichkeiten -▁15.00 -▁Finanzdienstleistungen -▁Elba -▁annimmt -▁Koalition -▁Turbo -▁Wahlkampf -▁participant -▁mounting -▁Handels- -▁unabhängiger -iciencies -▁exported -▁bestimmtes -▁beliebte -aminated -▁1990er -▁indicating -▁romantischen -▁geehrten -medien -▁repräsentiert -▁Currency -▁Erfordernisse -▁precedent -▁benachteiligten -▁encrypt -▁gescheitert -▁Sebastian -▁Diplomatie -▁Pensionen -▁Architect -Pr -Pl -BI -▁rises -▁morg -▁Toy -▁Ech -▁pic -itution -otor -▁oven -▁Hamm -▁Laus -istisches -achth -▁neck -▁Prices -▁Anschein -ikum -▁coex -ulture -▁Einverständnis -▁gene -avan -▁sciences -ymph -▁persistent -▁Gewiss -▁Russen -▁zusammengearbeitet -▁begriffen -▁additives -▁transportiert -kaufen -▁Ortes -▁Tageszeit -▁translators -▁nachhaltiges -▁équ -▁enforced -▁nachzukommen -▁Lehne -▁Erscheinung -▁unnötig -Guide -▁recruitment -beschäftigung -ovolta -▁Herunterladen -▁zuversichtlich -üte -Disk -kühl -ÜR -▁SERV -orenen -▁lyr -ürgen -▁beides -idation -▁zugeben -▁Ziffern -▁Jeg -eiteten -plänen -▁Andernfalls -▁aussch -▁Ausländer -zeigten -▁Yahoo -▁likewise -▁anybody -▁submission -▁inval -▁nationality -▁smell -▁Daß -▁Apoll -▁Versuchen -▁Qualifikation -▁Malaria -▁recommends -▁failures -▁foreigners -▁1963 -▁angemeldet -▁corrected -▁90er -▁1958 -▁Underground -▁Ausflugs -▁Evangel -▁advocates -▁potenzielle -▁inherent -▁Nutzungsbedingungen -▁incoming -▁Mobiltelef -▁Rüstungs -▁eBay -▁½ -zell -▁vague -▁Pause -▁Cus -▁Capt -▁Hugh -▁RC -icul -▁erricht -▁Stable -pton -▁Auffassungen -zuwirken -▁overr -▁Allen -▁Brig -krip -▁scan -piele -▁verspricht -▁123 -▁Particip -ERE -▁Adopt -▁jeher -▁definite -▁Organismen -▁Magazin -▁Öffentliche -▁conditioned -▁connects -▁liberalization -▁Castel -▁reconstruct -▁criticised -▁Schöne -▁geringfüg -▁radioakt -▁komplexer -▁café -▁Siedlung -▁democratisation -▁Sammel -▁reichhaltiges -▁einzuräumen -▁E-Commerce -▁NCC-170 -▁Verbrechens -▁Caucasus -▁ub -▁pap -▁Boy -itas -unge -▁Hals -▁verkehr -essp -promenade -▁abd -▁Weing -ardi -erverein -▁leid -▁Regisse -rium -▁Landhaus -▁0.5 -▁Barón -bewertung -▁ausgegangen -ähle -▁dreißig -haftigkeit -▁Linke -▁Geschäfts- -▁Schlussfolgerung -▁illegally -▁angenommene -▁travelers -▁illumin -▁sexy -▁likes -ografie -▁occurring -▁entstehenden -▁slowen -▁Richtlinienvorschlag -▁Begriffs -plantation -▁Customs -▁Louvre -farben -▁Voice -▁gifts -▁Dalmat -▁gratitude -▁mediterranen -▁Matthias -▁Verschiedene -nels -pul -29 -▁Dup -▁Besondere -etik -▁cav -▁Lan -rages -▁Ing -ifor -▁Jeff -▁worsen -▁Mitgefühl -ossa -elevant -▁submitting -▁Apost -▁ausgeweitet --Fin -▁accountable -▁Disco -▁civilisation -▁allgemeinem -▁demonstrieren -▁eingesetzten -▁Laufenden -▁Kurden -▁1949 -ulsion -▁stattfindenden -▁1939 -basierten -▁punish -zepten -▁erbracht -▁stilvolle -▁Ergänz -▁cafes -▁elektrische -▁composer -▁inserted -élé -▁molec -▁NGL-Fraktion -▁dezentral -belastung -▁misleading -▁Prozentsatz -▁Ferrero-Wald -mart -I. -points -▁nous -▁sä -▁veto -▁ml -▁wander -▁Tabl -▁gast -▁KL -▁Viv -icos -idos -▁stack -▁4-Sterne -▁bisweilen -▁console -▁subt -▁incap -▁Grunds -▁appalling -▁getät -▁Thematik -▁Meinungsver -optim -▁Quartet -geordnete -afel -▁maximalen -▁Mittele -▁friendship -▁anerkannte -▁Münzen -▁Aviation -▁radioactive -▁Währungsfonds -▁Azur -▁Vernichtung -▁Rentner -organe -▁tastes --Datenbank -▁licensing -▁unzähligen -piece -▁Assoziierungsabkommen -otherapy -▁Nürnberg -▁Ferrero-Waldner -fle -`s -▁tent -▁afor -▁Tin -▁Mother -▁Britt -urers -▁lächer -▁Capp -▁ya -heel -tres -▁Style -assis -assing -▁umzu -▁Einheim -▁quer -rengung -▁bestmögliche -nergy -▁Umfrage -▁hinten --Agent -▁Poliz -▁Novotel -▁instrumental -▁Terroran -entwicklungen -▁Anzahlung -▁Taj -▁Hyde -▁imagin -▁Banquet -▁gladly -▁inspiriert -▁assumes -▁Brazilian -▁DivX -▁Getränk -▁Sommers -kreuz -▁Demonstrationen -▁idyllic --Paket -▁stellvertreten -▁lady -▁FIFA -▁Boston -▁̈ -zes -▁ros -▁borr -▁Dereg -▁Mé -isible -▁prizes -▁Lücke -▁Nähr -▁gefl -▁Schichten -▁auszuschließen -ateien -ahlen -stext -▁subway -▁konvert -ceptions -▁seri -ministers -▁Tram -▁brush -▁Bürgermeister -bedürft -▁schlug -▁Kombin -▁angesiedelt -▁Nationale -▁Grüne -▁Verbraucherpolitik -▁Einzelpersonen -▁judg -▁oils -▁high-level -▁reflecting --winning -▁breed -▁verzichtet -▁Sprung -▁Aktionsprogramm -▁kostspiel -▁Tschechische -▁Gaulle -▁disappointment -▁erhöhten -texts -▁Meldung -ifffahrt -▁NCC-1701 -del -sektors -▁mead -etal -▁GASP -isting -▁ersucht -▁einleiten -▁allerg -▁Schande -ollt -▁4.1 -ransport -▁offensive -▁assass -▁handy -▁Interoper -▁accessing -▁Daim -▁Steve -▁befest -▁Schwed -▁objection -▁menschen -▁Malware -▁Wahlkreis -▁generic -▁Carlo -▁violated -▁harmonise -▁Termini -▁trocken -▁Mannheim -▁Eigentlich -▁anerkennt -▁dramatischen -▁caution -▁deposits -▁ruin -▁twenty-f -▁niederländische -▁gestrigen -▁vorübergehend --Website -▁deterioration -▁Speichern -▁biofuels -isciplinary -▁self-employed -▁Meinungsverschieden --ins --res -öck -FF -600 -ет -▁honey -▁ville -▁Banner -irect -▁anonymous -opic -▁Verweis -▁entails -▁3G -▁4.5 -▁freiz -ologists -▁Plural -▁Decken -▁wound -▁geschaff -▁gleichzeit -▁Neuse -beding -▁verstößt -▁understands -▁regulating -▁fairs -▁Sozialfonds -behand -▁Nordamerika -▁Delph -▁gefährd -▁urged -▁natürlicher -▁Mexican -▁Accordingly -▁Kyoto-Protokoll -▁Various -▁Beamte -▁Böge -▁kingdom -▁erstklassigen -▁Wolken -▁Sonnent -▁intermediate -▁vorherrschen -▁Gründungs -▁verschlechtert -▁fiber -▁fitting -▁bell -▁brachten -▁Darm -▁Eier -▁pisc -onische -▁Kurt -▁Kofin -idy -verfolgung -▁eingebaut -▁demo -▁chest -▁Grä -▁counting -▁contamination -▁gespeicher -werden -▁distortion -▁expresses -▁drain -▁eingehend -▁Submitted -▁beginners -▁appropriately --state -▁Obergrenze -▁Summ -▁Wettbewerbspolitik -▁penetr -▁mutually -▁beliebig -▁Philharmon -▁Oscar -▁Registrierungs -▁MSN -▁fashionable -ulichkeit -▁Throughout --Modus -▁surfing -▁erect -▁Honor -kämpft -▁Animation -▁Horizont -▁Zigaretten -▁Hoheitsgebiet -LD -▁scept -▁Tell -▁Dos -▁Bäume -▁Asp -chner -▁Unic -▁alla -weiger -▁auszut -▁manages -▁traces -fläch -ermächtigungen -▁first-class -▁delic -fürst -▁Handwerk -hindert -▁Trip -▁brasil -isedatum -atorische -▁soziales -▁authoritarian -parl -▁Guar -▁Airconditioning -▁showers -▁appearing -▁donations -▁capitalist -▁lacks -▁administrativen -▁Knopf -▁celeb -▁Salzkammer -▁bequemen -▁Entsprechend -öpfer -farbe -▁Brenner -▁ausdrückliche -faktoren -▁Schaltfläche -▁Tuscan -▁Pedro -▁tournaments -▁atemberaubenden -▁у -.d -hungen -ugend -LY -▁hellen -▁Sv -▁Sites -▁Amal -ilität -usen -▁inhaft -▁angrenzen -▁yea -▁notable -▁Schäd -igenz -▁yours -▁Auftritt -▁emails -▁realization -▁Gesichts -▁festgeschrieben -höre -▁zurückgreifen -▁Gleichberechtigung -▁optimize -▁Verantwortlichkeit -▁García -.07 --Inter -▁spezielles -▁resistant -▁Freundlichkeit -▁Verbrauchs -▁verkörp -▁beträchtlich -▁disappointing -▁kurzfristige -reinigung -▁blauen -▁enthusiasts -▁Bündnis -▁vergrößern -▁therapeutic -▁Invasion -▁Crespo -aufenthalt -▁wholeheartedly -▁Oomen-Ru -jour -▁töten -▁nest -▁fuck -▁apr -▁Tours -▁Pok -uni -▁zögern -▁VII -▁GA -▁Za -teenth -abu -ckung -▁orn -▁Story -▁entschloss -▁Imag -▁Orl -▁Monster -amerikas -▁Urlauber -▁Gemeinschaftspolitik -▁Belange -▁Moon -▁Buchten -▁headed -▁bisherige -IDE -▁Mehrheits -▁INI -▁fauna -▁Arabia -▁oberhalb -▁höchstens -▁intellekt -▁Stadtr -▁Mitgliedsländern -▁Bangemann -▁zuteil -▁Verdacht -▁Haartrockner -▁Concert -städten -▁consciousness -▁Amazon -▁Challenge -▁Abwesenheit -▁offline -uential -lander -▁ni -▁pubs -▁cocktails -▁kub -▁K. -▁Calle -athl -▁alqu -owa -▁zukommt -▁verfassungs -▁weite -endete -▁vorzustellen -▁vorgelegte -epper -▁lieb -▁locals -▁Communities -▁findest -zugleichen -▁Address -▁Bosch -▁static -▁slot -▁Speicherung -▁Carn -▁Christoph -▁grade -drücken -▁traveling -▁judiciary -▁exhibitors -▁Gesundheits- -▁hochwertiger -üllt -▁Klein- -▁mixing -▁1933 -▁kristall -▁Champion -▁gemeinnütz -▁Cuban -▁inhaltlich -▁unverzichtbar -▁getrennte -▁Nevada -▁Mitchell -bogen -▁Session -inische -▁zivile -▁Fi -▁Fashion -▁unclear -ieber -stanz -▁stören -adores -ecuted -▁Vorzüge -auftrag -▁Geis -▁Geister -▁conceal -▁geschlagen -▁Bestehen -▁betrug -inski -▁childhood -▁Durchschnitts -▁erneuert -▁Dienstleister -▁Wortes -▁organisms -register -▁Opport -▁widersprüch -IPS -UST -▁dispatch -▁Automotive -▁ecommerce -▁Kyi -▁Pressefreiheit -▁Nuclear -▁straightforward -▁Getränken -▁awaits -▁großzügige -▁irresponsible -▁heiligen -▁Etwas -▁sentences -▁jüdischen -▁wöchentlich -▁adapting -▁chapters -oons -faden -die -kular -▁rote -▁Dil -esy -▁e-c -▁Werner -▁HE -▁Favor -▁übergehen -grä -▁ausmacht -iani -▁disability -▁internally -▁Commerce -▁esta --Benz -▁acute -▁Butter -▁musst -bauern --minded -afi -sender --Herz -wandeln -▁benötigte -▁professionalism -▁influential -▁öfter -hesive -▁Kleink -▁Liberties -▁Südkorea -▁Suu -▁Italienisch --looking -▁stimulating -▁Namib -▁exzellenten -▁champion -▁ausdrücken -▁Freihandelsabkommen -▁Javascript -praxis -▁verdeutlicht -lysées -si -lernen -LC -xx -▁drew -▁pending -▁pdf -▁Pion -elier -unnen -▁ink -▁anged -schild -istans -istically -▁aufbew -▁Schild -ermin -▁poses -▁Rechtsanw -▁Urug -▁explizit -▁Goals -▁low-c -▁encouragement -▁Handelspolitik -▁280 -▁judged -▁Ruinen -▁Brava -▁Hybrid -▁Büchern -▁Ambassador -▁acquaint -▁Surround -▁laufend -▁Nicolas -▁gesundheitlichen -▁transposition -▁Figuren -messungen -▁Geltung -▁auslösen -▁peripheral -▁Racing -▁proprietary -▁bewaffneten -▁Ressource -▁Birmingham -▁Oomen-Ruijten -hart -rand --se -kenn -KO -ло -▁Sorten -▁Bein -▁Aids -igan -▁Create -▁Hemm -▁Ries -decke -atischer -alone -▁LV -▁onwards -▁aufgreifen -▁vermin -▁chambre -izers -schafft -▁Abänderungs -▁jedw -älern -▁interc -arbeitete -▁Überzeug -▁guys -▁Seien -oleum -▁suicide -ähere -zielen -▁11th -▁Malm -▁Nationalismus -▁palm -▁1959 -▁applicants -▁robot --Konferenz -▁Dominion -▁Verlagerung -▁jenes -▁neoliber -▁Computern -▁kathol -▁hinnehmbar -zten -mand -▁Iss -▁Mw -itology -▁unbea -berater -▁Leinen -verd -pler -allo -eca -▁leer -▁Vorreit -zukommen -▁trucks -▁Reed -▁Yam -ührung -▁moon -▁scar -▁Produktpalette -▁parish -▁Manufact -▁Blues -▁Änderungsvorschläge -▁Notfall -▁traditioneller -ikanern -▁Companies -▁Nova -stücken -▁Motivation -öhnt -▁80er -▁Login -▁vernichtet -▁Paulo -▁tragischen -▁Angela -▁balconies -▁smoothly -▁Urheberrecht -▁columns -▁zurückgezogen -▁schwerwiegende -vorsitzenden --month -▁Eiffel -▁KUKA -seg -Hotel -900 -▁tall -▁null -▁drift -▁matur -▁PAD -▁Pausch -elin -▁Kil -▁Kambod -▁Clo -▁Fell -arin -tritte -▁einl -▁-E -izzer -▁adhere -▁320 -▁10,000 -▁klingt -▁beklagen -▁statistischen -▁Geräus -▁absent -▁Maler -▁approaching -▁Proteste -▁opposing -▁emphasises -▁geltende -▁Highlights -▁seasons -ungsverfahrens -▁Rauchen -▁celebrations -▁DSLR -▁empire -▁dictator -▁detaillierten -gästen -▁Footprint -▁Nephi -▁Ricc -▁enthusiastic -▁Heathrow -▁Frühstücksraum -krieges -,50 -ria -cin -pit -MI -▁bail -chair -▁Focus -▁Rehn -▁unerm -▁unav -▁Lower -▁concluding -▁Oil -beiträge -▁Arist -anzlei -amma -ossiers -▁Imaging -cycl -▁belasten -▁Europa-Mittelmeer -▁debian -▁charging -▁Boulevard -äufen -reden -▁Strahl -▁singing -ophone -▁volcan -▁Relais -▁Gesundheitsschutz -▁Sonnenunter -▁Nahrungsmitteln -▁Customers -befugnisse -modern -▁Sensor -▁Siedlungen -▁künstlerische -▁spürbar -▁Religionsfreiheit -▁Fourthly -▁Karlsruhe -▁Ubuntu -▁nuklearen -▁schallisol -▁Neapel -aar -wuchs -▁seller -▁Ei -▁MC -▁POS -ono -▁catastroph -▁Wies -▁Hamb -▁Rue -atility -▁theater -▁andernfalls -▁sektor -▁zubereitet -▁Jar -▁Stone -ennetz -▁Bequem -ermeld -feuer -haltestelle -▁restoring -▁gleichwert -▁fluct -▁langwier --Test -▁impair -▁Banking -▁Christopher -▁Einsatzes -▁Deliver -▁Installer -▁calculations -▁Remember -▁gründliche -▁Tschechien -papiere -▁не -▁anhaltenden -▁Rettung -▁Gremium -▁Neuerungen -▁pumps -▁Futtermittel -▁gängigen -▁Gletscher -▁Sauberkeit -▁archaeological -ROM -être -▁squar -▁mold -▁Eco -▁Kev -▁Gassen -▁injust -alo -▁seeds -▁vermarkt -▁confined -einkommens -grams -▁abhalten -▁resume -▁Arrang -echnologien -▁YORK -▁Menu -elservice -▁gewalts --Schn -▁somebody -ämmung -▁Kris -▁restra -▁gerät -▁vollzogen -oppy -▁Whenever -▁Guad -▁poster -▁Wertsch -▁Goethe -▁Baust -ecutor -▁kleinste -modus -gesetzes -▁Grenzwerte -▁condemnation -▁scenarios -▁Rechnungshofs -▁monopolies -▁eingestuft -▁Demonstration -▁comprising --Lösung -konstruktion -mobil -kanal -▁runden -▁sid -▁Table -▁Treas -▁brechen -▁Cel -▁Romp -▁unglück -iesst -umfeld -▁beheiz -▁Anforderung -▁Schau -▁spher -ogl -▁nachts -ußts -▁servants -▁Plans -▁economists -▁nationalism -▁Geschlechts -▁Krist -▁Durchmesser -▁Romagna -▁Haushaltsausschuss -▁reformed -▁cleaned -▁methodology -▁learnt -▁wide-ranging -▁Province -▁Niederlage -▁ordentlich -▁Zeichnungen -▁1947 -windigkeiten -▁Sofort -▁Cafe -▁drehen -▁vielerlei -▁Massenvern -group -▁Football -prüfungen -▁Schmidt -▁horny -▁Emotionen -rus -rett -making -▁fla -▁raschen -▁mature -▁cad -▁Gom -▁inhuman -▁anscha -▁beverages -▁NV -ifilm -abgabe -▁demzufolge -ungsmöglichkeiten -rankung -▁nachweis -▁beta -▁Spin -▁Umlauf -nahms -▁Emperor -▁studieren -▁145 -ädagog -▁engere -▁Warte --initiative -▁requesting -▁Fortschritten -▁unequiv -▁footing -▁Straßenbahn -▁hervorragender -▁Ambitionen -▁Besonderheit -▁Wasserk -▁frühzeitig -▁erfreulich -▁Jacob -▁Akteur -risiken -▁kurzfristigen -▁ceilings -spezifischen -▁disparities -▁Bibione -▁Toulouse -▁Japans -fia -mord -▁rings -▁bab -▁Ivan -inland -inationen -▁Cairo -▁Gare -ieri -schle -essa -▁Angreif -ahara -grem -▁Einträge -▁Mao -▁Recher -▁Menschenleben -▁String -▁Entdeck -▁sono -pfeiler -▁Neur -▁Pharm -▁Boote -▁tune -▁Geschäftsbereich -▁optimistic -▁identisch -▁neuem -▁Charl -▁Briten -▁Delors -▁funktionierenden -▁exportiert -▁Democrat -backs -▁englischer -▁obersten -▁lengthy -▁empower -▁1970s -▁shaping -▁ineffective -heizung -zählung -Traq -▁Gonzá -▁Umwandlung -cio -länge -76 -ís -▁ritual -▁Duft -▁Panz -▁loyal -▁Gab -▁inability -atri -▁forcing -▁alb -▁Nine -queren -▁conquer -egr -▁abgew -merks -blog -getr -▁Blanc -▁creatures -▁capita -▁Internetanschluss -▁Palermo -▁schwachen -▁stehe -ameworks -▁transparenten -▁weekends -▁grace -▁voltage -▁gerechten -▁widerspricht -▁consultancy -▁Summen -▁castles -▁cellar -IFF -problemen -▁Wiedergabe -▁Erwähnung -▁US-amerikanischen -▁Bahrain -schlüsselung -▁debattieren -▁exemplary -▁Ahmad -▁Heutzutage -firma -rise -born -comm -▁tagt -▁Tam -▁Easter -▁PER -ildern -▁Vanc -▁alkohol -heid -verlauf -▁aufgeb -alla -▁sog. -ementioned -▁möbl -▁darzustellen -aufgabe -▁Former -▁Repl -▁allevi -▁nationales -▁Krum -▁succ -▁Träume -▁Boeing -sparen -▁Fallout -▁Saar -▁marvell -▁Basque -▁Opern -.04 -▁Antibiot --Install -▁Staatsangehör -▁infection -▁Check-in -▁Municip -▁verabschiedeten -▁Aktionäre -ispiell -▁murdered -▁synthetic -▁Fujifilm -▁Fuerteventura -▁Gütern -▁cosmetic -▁Å -gnung -▁va -▁Syl -▁Diss -▁DNS -erwart -▁Case -▁anstehenden -▁LKW -verleih -▁Anc -▁exagger -conf -▁outlook -▁drama -▁Soul -vorgang -erstattung -▁Gewissen -▁promin -▁Kinderspiel -▁marina -▁weiße -rehensible -iktor -▁Paare -▁Kopien -▁ENT -▁blockieren -IMM -▁teen -▁Zulass -▁Halbjahr -▁BugTraq -▁großzügigen -▁unvergessliche -ULT -▁Illustr -▁Stammzellen -▁multiannual -▁komplizierte -OBIL -▁erfahrene -▁jewel -▁Überraschung -▁überflüssig -liebhaber -▁vorantreiben -▁Uruguay -tum -ketten -kanzler -▁PDA -▁Ah -eses -elie -institute -isan -▁CET -▁Gust -▁NY -heorie -▁Zehn -▁desires -▁comitology -▁ausfallen -▁polar -▁Already -▁disgra -▁relatives -▁Chapter -▁Rey -▁Plattformen -worth -▁own-initiative -▁continents -▁besetzten -marke -▁strategisch -▁Kontrast -itherto -▁templates -▁engineer -willigen -▁regelm -▁Players -▁remembered -▁Antrags -▁Immobilie -▁Wikipedia -▁Kriegsverb -▁SLR -▁Publishing -▁commodities -▁verbleibenden -▁repressive -▁dementsprechend -▁pupils -▁Daimler -pi -PI -PD -▁IX -▁pag -▁BGB -inta -▁Wog -▁Rost -ierbarkeit -▁Lenk -▁2D -▁vervollständ -▁abortion -▁schock -▁Ausgehend -▁Beyond -▁Maritime -▁weiterver -▁minors -comes -▁155 -▁flank -▁besprechen -▁Schwäch -bedien -▁Firewall -▁Höhlen -▁Kritiker -oomla -▁warmth -▁conditional -▁Berufung -▁claiming -▁touched -sprüche -▁indicator -▁Zufl -▁geistige -▁Offenbar -▁entstandenen -▁Nicar -▁ausgebildet -▁inventory -▁fantastische -▁redundant -▁ASPs -▁Kämpfe -▁Befürworter -▁Daily -nisch -lash -▁nin -olet -▁zauber -▁Erleb -udad -ussia -▁unterm -▁zwingt -▁Chá -▁lauten -▁Zeitschriften -▁bedacht -▁revital -▁Marktführer -▁Daran -▁Notre -▁enjoyment -▁glaubwürdig -▁mentioning -spektrum -▁Apartamentos -gression -▁Londoner -effizient -▁Eingriff -▁virtue -▁salaries -▁Zivilbevölkerung -▁ausreichenden -▁Beitrittsländer -▁inneh -zedenz -▁investigated -▁Ethik -.2008 -▁Phänom -▁fantastischen -▁accomplish -▁brothers -▁doppelten -▁Dringlichkeits -▁interfere -▁sacrifice -▁deprived -▁begünstigt -▁nicely -▁Kosmetik -▁predecessor -iations -uhen -software -ümm -codes -pone -▁.NET -▁ward -▁Tuber -▁Dual -▁Piano -erben -▁cofin -▁Krat -▁Fru -berühm -▁erschwing -▁Oost -▁Oran -▁Starten -enda -▁Schach -▁Schreibt -allas -ausrüstung -▁betrachte -prozessen -▁artw -▁weitergeben -▁Seel -▁Haushaltslinie -▁Kompat -▁vereinfach -▁volcano -▁Widerstands -freundlich -▁Konzerne -▁Fremdenfeindlichkeit -▁lebensl -▁Fassade -▁Bavarian -▁Kenya -▁Stornierungen -▁Observ -▁Hügeln -▁Bügelservice -▁venez -▁vorankommen -▁Slovenian --Herzeg -▁TS -▁PD -▁Bäder -▁FE -▁Feren -▁Insp -▁Inhaft -andschaft -▁fürs -▁Entr -▁mancher -▁wieviel -▁unterrichten -▁arc -▁Mitgliedstaats -folios -▁Herstellungs -▁besonderem -▁excav -offiz -▁norms -▁Fahrplan -▁third-party -▁motors -▁offences -▁logos -▁infected -▁cross-country -▁Wissenschaftlern -▁IP-Adresse -▁Mandats -▁1955 -▁Military -▁geringere -▁Check-Out -▁dreht -▁eingebrachten -entscheidung -▁Vernunft -▁supposedly -▁Normalerweise -▁attributes -▁thickness -▁konfiguriert -▁Meldungen -▁unbegrenz -▁Frontera -▁erlangt -▁Unterbrechung -▁administrations -niveaus -rs -links -WB -As -800 -▁tibet -▁dent -▁IDE -▁Dossier -▁Bul -eaker -inia -▁Cards -▁Hond -▁Furcht -▁todo -▁beispiell -▁Liver -▁verhält -▁Anleihen -protokoll -ungsmittel -▁worüber -arks -itee -flut -▁Freig -▁Rahmenrichtlinie -▁Gewichts -▁höre -▁expressions -▁No. -▁patron -▁Fluggäste -▁urheber -▁Senegal -▁Präzedenz -▁reproductive -▁protocols -▁bewährte -▁spannende -▁steady -▁erstaunlich -▁Chalet -▁draftsman -▁plastics -▁Fischereisektor -▁circles -▁bizarre -tet -ciano -CL -▁hem -▁dying -▁dancing -▁rows -▁pixels -▁gorge -itsch -utable -▁Winkel -▁unangen -▁unklar -▁towers -idi -istance -▁jam -odus -ouss -▁bets -schland -▁anderweit -▁Marse -loß -▁Rahmenbeschluss --Fil -▁combinations -mäßiger -▁antibiot -▁Buchungen -▁herausgestellt -▁Anstrengung -▁transferring -▁sportlichen -▁Establ -▁coupled --to-day -▁fragte -▁Veröffentlichungen -▁Behinderten -▁attributed -▁Kartoff -▁kopiert -▁bürgerlichen -▁Ingenieur -▁cloud -▁Panoramabl -▁triumph -▁Byrne -kä -71 -▁mile -▁MHz -▁Penn -▁unite -▁ansteigen -stags -uropä -▁Stipend -▁schar -▁vorgebracht -frame -etrieb -▁remit -▁einzub -▁versenden -▁qualifiziert -▁Neust --Ap -zeugnisse -▁implementiert -▁Better -▁Glanz -▁256 -▁filed -▁Bewert -▁Festland --Con -▁Kommand -▁Verantwortlichkeiten -▁Naturpark -▁harmonious -▁determines -▁supplying -▁high-tech -cksilber -▁appealing -▁Robot -▁lebte -▁anhaltende -▁lucky -▁Edelstahl -▁crowd -▁Česk -▁Schattenberichterstatter -▁hydraulic -▁xenophobia -▁Herangehensweise -▁Vancouver -lasten -BF -▁dritt -▁wissens -▁Senden -eafter -▁clever -▁WIT -▁Hervor -▁Laptop -▁stup -▁2-3 -▁Verhütung -▁jury -▁nicht- -echen -▁county -eteness -eads -▁Marathon -▁Allerg -▁mayor -▁Franç -ymp -bedürf -▁readily -▁annähern -regulierung -▁ambiance -▁Papst -▁bedroh -vorsorge -▁breakdown -clubs -▁1929 -▁fleets -▁11.30 -▁Glasg -▁zuverlässigen -▁Electronics -▁Expansion -lieferungen -▁objektiv -▁undermining -▁Roger -▁Ökonomie -▁Rehab -▁boutiques -▁Karibik -▁Chávez -nutzen -gos -ém -▁bund -▁pak -▁Math -▁Bun -etischen -▁letting -▁Wide -▁Hed -▁Gior -▁None -aches -▁conception -▁Jubiläum -▁abzubauen -▁Erb -erries -▁grows -▁Gewebe -idencies -attungen -▁crown -▁Flügel -▁Command -▁differing -▁hereby -▁debut -▁Fischbestände -▁educated --Problem -▁Schweigen -▁spezieller -▁erkläre -▁mistaken -▁Muttersprache -▁strukturelle -▁gültige -▁Bordeaux -▁Sat-TV -▁slave -▁zurückzukehren -▁Intelligenz -▁lectures -etermination -▁geforderten -▁Handtücher -▁PNR -▁González -aunch -mag --air -VE -▁mg -▁SEC -▁punkt -▁Myst -▁Mütter -▁Pope -▁kraft -▁Künd -▁Kuchen -▁Kette -▁Gul -iesta -▁Lese -osity -▁gefeiert -▁conceived -▁ext -▁Befried -▁trug -▁Maz -▁restart -erkrankungen -▁day-to-day -▁blut -▁employer -▁begründen -▁bewährt -fangs -▁Gebiets -▁loose -▁wesentliches -▁sunshine -▁Hygiene -▁ehemaliger -▁patented -▁fifty -▁angestrebt -▁Confed -▁Schröder -▁Generaldirektion -▁brochure -▁Bruttoinland diff --git a/lingvo/tasks/punctuator/BUILD b/lingvo/tasks/punctuator/BUILD deleted file mode 100644 index 1e5106de..00000000 --- a/lingvo/tasks/punctuator/BUILD +++ /dev/null @@ -1,70 +0,0 @@ -# Punctuator tutorial task. -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_library( - name = "input_generator", - srcs = ["input_generator.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:generic_input", - "//lingvo/core:py_utils", - "//lingvo/core:tokenizers", - ], -) - -py_test( - name = "input_generator_test", - srcs = ["input_generator_test.py"], - data = [ - "//lingvo/tasks/lm/testdata:lm1b_100", - "//lingvo/tasks/punctuator/params:brown_corpus_wpm_data", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":input_generator", - "//lingvo:compat", - "//lingvo/core:base_input_generator", - "//lingvo/core:py_utils", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "model", - srcs = ["model.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/tasks/mt:model", - ], -) - -py_test( - name = "model_test", - size = "large", - srcs = ["model_test.py"], - data = [ - "//lingvo/tasks/lm/testdata:lm1b_100", - "//lingvo/tasks/punctuator/params:brown_corpus_wpm_data", - ], - python_version = "PY3", - shard_count = 4, - srcs_version = "PY3", - deps = [ - ":input_generator", - ":model", - "//lingvo:compat", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - # Implicit numpy dependency. - ], -) diff --git a/lingvo/tasks/punctuator/__init__.py b/lingvo/tasks/punctuator/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/punctuator/input_generator.py b/lingvo/tasks/punctuator/input_generator.py deleted file mode 100644 index 2caf73ac..00000000 --- a/lingvo/tasks/punctuator/input_generator.py +++ /dev/null @@ -1,130 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Punctuator input generator.""" - -import string -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import generic_input -from lingvo.core import py_utils -from lingvo.core import tokenizers - - -class PunctuatorInput(base_input_generator.BaseSequenceInputGenerator): - """Reads text line by line and processes them for the punctuator task.""" - - @classmethod - def Params(cls): - """Defaults params for PunctuatorInput.""" - p = super().Params() - p.tokenizer = tokenizers.WpmTokenizer.Params() - return p - - def _ProcessLine(self, line): - """A single-text-line processor. - - Gets a string tensor representing a line of text that have been read from - the input file, and splits it to graphemes (characters). - We use original characters as the target labels, and the lowercased and - punctuation-removed characters as the source labels. - - Args: - line: a 1D string tensor. - - Returns: - A list of tensors, in the expected order by __init__. - """ - # Tokenize the input into integer ids. - # tgt_ids has the start-of-sentence token prepended, and tgt_labels has the - # end-of-sentence token appended. - tgt_ids, tgt_labels, tgt_paddings = self.StringsToIds( - tf.convert_to_tensor([line])) - - def Normalize(line): - # Lowercase and remove punctuation. - line = line.lower().translate(None, string.punctuation.encode('utf-8')) - # Convert multiple consecutive spaces to a single one. - line = b' '.join(line.split()) - return line - - normalized_line = tf.py_func(Normalize, [line], tf.string, stateful=False) - _, src_labels, src_paddings = self.StringsToIds( - tf.convert_to_tensor([normalized_line]), is_source=True) - # The model expects the source without a start-of-sentence token. - src_ids = src_labels - - # Compute the length for bucketing. - bucket_key = tf.cast( - tf.round( - tf.maximum( - tf.reduce_sum(1.0 - src_paddings), - tf.reduce_sum(1.0 - tgt_paddings))), tf.int32) - tgt_weights = 1.0 - tgt_paddings - - # Return tensors in an order consistent with __init__. - out_tensors = [ - src_ids, src_paddings, tgt_ids, tgt_paddings, tgt_labels, tgt_weights - ] - return [tf.squeeze(t, axis=0) for t in out_tensors], bucket_key - - def _DataSourceFromFilePattern(self, file_pattern): - """Create the input processing op. - - Args: - file_pattern: The file pattern to use as input. - - Returns: - an operation that when executed, calls `_ProcessLine` on a line read - from `file_pattern`. - """ - return generic_input.GenericInput( - file_pattern=file_pattern, - processor=self._ProcessLine, - # Pad dimension 0 to the same length. - dynamic_padding_dimensions=[0] * 6, - # The constant values to use for padding each of the outputs. - dynamic_padding_constants=[0, 1, 0, 1, 0, 0], - **self.CommonInputOpArgs()) - - def __init__(self, params): - super().__init__(params) - - # Build the input processing graph. - (self._src_ids, self._src_paddings, self._tgt_ids, self._tgt_paddings, - self._tgt_labels, - self._tgt_weights), self._bucket_keys = self._BuildDataSource() - - def InfeedBatchSize(self): - """Override BaseSequenceInputGenerator.""" - return tf.shape(self._src_ids)[0] - - def _InputBatch(self): - """Returns a single batch as a `.NestedMap` to be passed to the model.""" - ret = py_utils.NestedMap() - - ret.bucket_keys = self._bucket_keys - - ret.src = py_utils.NestedMap() - ret.src.ids = tf.cast(self._src_ids, dtype=tf.int32) - ret.src.paddings = self._src_paddings - - ret.tgt = py_utils.NestedMap() - ret.tgt.ids = self._tgt_ids - ret.tgt.labels = tf.cast(self._tgt_labels, dtype=tf.int32) - ret.tgt.weights = self._tgt_weights - ret.tgt.paddings = self._tgt_paddings - - return ret diff --git a/lingvo/tasks/punctuator/input_generator_test.py b/lingvo/tasks/punctuator/input_generator_test.py deleted file mode 100644 index ded73b15..00000000 --- a/lingvo/tasks/punctuator/input_generator_test.py +++ /dev/null @@ -1,85 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for input generator.""" - -import string -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import py_utils -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tasks.punctuator import input_generator - - -class InputGeneratorTest(test_utils.TestCase): - - def _CreatePunctuatorInputParams(self): - p = input_generator.PunctuatorInput.Params() - input_file = 'text:' + test_helper.test_src_dir_path( - 'tasks/lm/testdata/lm1b_100.txt') - p.tokenizer.vocab_filepath = test_helper.test_src_dir_path( - 'tasks/punctuator/params/brown_corpus_wpm.16000.vocab') - p.tokenizer.vocab_size = 16000 - p.file_pattern = input_file - p.file_random_seed = 314 - p.file_parallelism = 1 - p.source_max_length = 200 - p.target_max_length = 200 - p.bucket_upper_bound = [20, 40] - p.bucket_batch_limit = [1, 1] - return p - - def testBasic(self): - p = self._CreatePunctuatorInputParams() - with self.session(use_gpu=False): - inp = input_generator.PunctuatorInput(p) - # Runs a few steps. - for _ in range(10): - self.evaluate(inp.GetPreprocessedInputBatch()) - - def testSourceTargetValues(self): - max_length = 50 - p = self._CreatePunctuatorInputParams() - with self.session(use_gpu=False): - inp = input_generator.PunctuatorInput(p) - tokenizer = inp.tokenizer_dict[base_input_generator.DEFAULT_TOKENIZER_KEY] - - fetched = py_utils.NestedMap( - self.evaluate(inp.GetPreprocessedInputBatch())) - source_ids = fetched.src.ids - tgt_ids = fetched.tgt.ids - tgt_labels = fetched.tgt.labels - - expected_ref = (b'His approach was inquisitive , a meeting of artful ' - b'hesitation with fluid technique .') - - normalized_ref = expected_ref.lower().translate( - None, string.punctuation.encode('utf-8')) - normalized_ref = b' '.join(normalized_ref.split()) - _, expected_src_ids, _ = self.evaluate( - tokenizer.StringsToIds( - tf.convert_to_tensor([normalized_ref]), max_length=max_length)) - expected_tgt_ids, expected_tgt_labels, _ = self.evaluate( - tokenizer.StringsToIds( - tf.convert_to_tensor([expected_ref]), max_length=max_length)) - - self.assertAllEqual(expected_src_ids[0], source_ids[0, :max_length]) - self.assertAllEqual(expected_tgt_ids[0], tgt_ids[0, :max_length]) - self.assertAllEqual(expected_tgt_labels[0], tgt_labels[0, :max_length]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/punctuator/model.py b/lingvo/tasks/punctuator/model.py deleted file mode 100644 index 3ffaff55..00000000 --- a/lingvo/tasks/punctuator/model.py +++ /dev/null @@ -1,63 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Punctuator model.""" - -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.tasks.mt import model as mt_model - - -class RNMTModel(mt_model.RNMTModel): - """The MT model with an inference graph for punctuator.""" - - def Inference(self): - """Constructs the inference subgraphs. - - Returns: - dict: ``{'subgraph_name': (fetches, feeds)}`` - """ - subgraphs = dict() - with tf.name_scope('inference'): - subgraphs['default'] = self._InferenceSubgraph_Default() - return subgraphs - - def _InferenceSubgraph_Default(self): - with tf.name_scope('inference'): - src_strings = tf.placeholder(tf.string, shape=[None]) - _, src_ids, src_paddings = self.input_generator.StringsToIds( - src_strings, is_source=True) - - src_input_map = py_utils.NestedMap(ids=src_ids, paddings=src_paddings) - encoder_outputs = self.enc.FPropDefaultTheta(src_input_map) - decoder_outs = self.dec.BeamSearchDecode(encoder_outputs) - - topk_hyps = decoder_outs.topk_hyps - topk_ids = decoder_outs.topk_ids - topk_lens = decoder_outs.topk_lens - - # topk_lens - 1 to remove the EOS id. - topk_decoded = self.input_generator.IdsToStrings(topk_ids, topk_lens - 1) - topk_decoded = tf.reshape(topk_decoded, tf.shape(topk_hyps)) - - feeds = py_utils.NestedMap({'src_strings': src_strings}) - fetches = py_utils.NestedMap({ - 'src_ids': src_ids, - 'topk_decoded': topk_decoded, - 'topk_scores': decoder_outs.topk_scores, - 'topk_hyps': topk_hyps, - }) - - return fetches, feeds diff --git a/lingvo/tasks/punctuator/model_test.py b/lingvo/tasks/punctuator/model_test.py deleted file mode 100644 index 93bb3aab..00000000 --- a/lingvo/tasks/punctuator/model_test.py +++ /dev/null @@ -1,166 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for Punctuator model.""" - -import lingvo.compat as tf -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tasks.punctuator import input_generator -from lingvo.tasks.punctuator import model -import numpy as np - -_TF_RANDOM_SEED = 93820986 - - -class PunctuatorModelTest(test_utils.TestCase): - """Tests for the Punctuator model. - - Overriding parameters and inheriting tests from RNMTModelTest. - """ - - def _InputParams(self): - p = input_generator.PunctuatorInput.Params() - input_file = test_helper.test_src_dir_path('tasks/lm/testdata/lm1b_100.txt') - p.tokenizer.vocab_filepath = test_helper.test_src_dir_path( - 'tasks/punctuator/params/brown_corpus_wpm.16000.vocab') - p.tokenizer.vocab_size = 16000 - p.file_pattern = 'text:' + input_file - p.file_random_seed = 31415 - p.file_parallelism = 1 - p.bucket_upper_bound = [40] - p.bucket_batch_limit = [4] - p.source_max_length = 40 - p.target_max_length = 40 - return p - - def _UpdateEncoderParams(self, p): - p.emb.embedding_dim = 4 - p.emb.max_num_shards = 1 - p.lstm_cell_size = 4 - p.num_lstm_layers = 3 - p.encoder_out_dim = 4 - return p - - def _UpdateDecoderParams(self, p): - p.source_dim = 4 - p.emb.embedding_dim = 4 - p.emb.max_num_shards = 1 - p.rnn_cell_dim = 4 - p.rnn_layers = 3 - p.attention.hidden_dim = 2 - p.softmax.num_shards = 1 - return p - - def _testParams(self): - p = model.RNMTModel.Params() - p.name = 'test_mdl' - p.input = self._InputParams() - self._UpdateEncoderParams(p.encoder) - self._UpdateDecoderParams(p.decoder) - p.train.learning_rate = 2e-4 - return p - - def testConstruction(self): - with self.session(): - p = self._testParams() - mdl = p.Instantiate() - print('vars = ', mdl.vars) - flatten_vars = mdl.vars.Flatten() - print('vars flattened = ', flatten_vars) - self.assertEqual(len(flatten_vars), 27) - - # Should match tf.trainable_variables(). - self.assertEqual(len(tf.trainable_variables()), len(flatten_vars)) - - def testFProp(self, dtype=tf.float32): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - p.dtype = dtype - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(3): - vals += [self.evaluate((loss, logp))] - - print('actual vals = %s' % np.array_repr(np.array(vals))) - expected_vals = [ - [326.765106, 10.373495], - [306.018066, 10.373494], - [280.08429, 10.373492], - ] - self.assertAllClose(vals, expected_vals) - - def testBProp(self): - with self.session(): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - mdl.BProp() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(3): - vals += [self.evaluate((loss, logp, mdl.train_op))[:2]] - print('BProp actual vals = ', vals) - expected_vals = [ - [326.765106, 10.373495], - [306.013123, 10.373326], - [280.07666, 10.37321], - ] - self.assertAllClose(vals, expected_vals) - - def testFPropEvalMode(self): - with self.session(), self.SetEval(True): - tf.random.set_seed(_TF_RANDOM_SEED) - p = self._testParams() - mdl = p.Instantiate() - mdl.FPropDefaultTheta() - loss = mdl.loss - logp = mdl.eval_metrics['log_pplx'][0] - self.evaluate(tf.global_variables_initializer()) - vals = [] - for _ in range(3): - vals += [self.evaluate((loss, logp))] - print('actual vals = ', vals) - expected_vals = [ - [326.765106, 10.373495], - [306.018066, 10.373494], - [280.08429, 10.373492], - ] - self.assertAllClose(vals, expected_vals) - - def testInference(self): - with self.session(use_gpu=False) as sess, self.SetEval(True): - tf.random.set_seed(93820985) - p = self._testParams() - mdl = p.Instantiate() - fetches, feeds = mdl.Inference()['default'] - - self.evaluate(tf.global_variables_initializer()) - src_strings = ['the cat sat on the mat', 'the dog sat on the mat'] - dec_out = sess.run(fetches, {feeds['src_strings']: src_strings}) - print('dec_out', dec_out) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tasks/punctuator/params/BUILD b/lingvo/tasks/punctuator/params/BUILD deleted file mode 100644 index 237d822b..00000000 --- a/lingvo/tasks/punctuator/params/BUILD +++ /dev/null @@ -1,36 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -filegroup( - name = "brown_corpus_wpm_data", - srcs = glob(include = ["brown_corpus_wpm.*"]), -) - -py_library( - name = "codelab", - srcs = ["codelab.py"], - data = [":brown_corpus_wpm_data"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo:model_registry", - "//lingvo/core:base_model_params", - "//lingvo/tasks/mt:base_config", - "//lingvo/tasks/mt/params", - "//lingvo/tasks/punctuator:input_generator", - "//lingvo/tasks/punctuator:model", - ], -) - -py_library( - name = "params", - srcs = ["params.py"], - srcs_version = "PY3", - tags = ["keep_dep"], - deps = [ - ":codelab", - ], -) diff --git a/lingvo/tasks/punctuator/params/brown_corpus_wpm.16000.vocab b/lingvo/tasks/punctuator/params/brown_corpus_wpm.16000.vocab deleted file mode 100644 index fe761b3f..00000000 --- a/lingvo/tasks/punctuator/params/brown_corpus_wpm.16000.vocab +++ /dev/null @@ -1,15942 +0,0 @@ - - - - - -▁ -t -h -e -o -f -a -n -d -i -w -s -r -b -y -m -T -v -c -u -l -I -p -H -- -k -g -B -j -A -S -M -. -x -F -1 -W -N -q -2 -U -O -, -J -3 -0 -Y -4 -z -9 -6 -5 -P -E -G -D -8 -" -& -C -7 -K -' -R -L -( -) -V -: -Q -* -; -? -Z -/ -% -$ -X -! -{ -} -[ -] -+ -▅ -~ -` -# -< -= -> -@ -\ -^ -_ -| -th -the -▁the -in -an -on -ing -and -en -▁w -of -▁of -er -ed -or -ou -re -▁b -at -ar -al -▁f -▁and -▁h -ch -▁a -▁in -ion -to -▁to -▁p -▁for -▁c -▁s -is -▁th -▁m -om -▁wh -as -es -▁was -▁that -ent -it -ic -ation -ow -▁d -ly -Th -gh -▁be -ad -▁n -▁wi -▁with -ver -ve -un -im -▁sh -▁con -ro -▁no -ther -▁pro -le -▁re -ct -▁g -▁on -id -ld -ould -qu -▁his -▁had -us -ur -▁he -▁com -ay -am -ex -ment -▁Th -igh -ough -ce -ir -oun -▁this -▁is -▁l -ll -all -rom -▁from -tr -▁it -▁A -ble -our -▁I -if -▁ex -▁by -▁not -ain -ave -▁have -▁as -▁S -ter -ive -ol -il -ul -ut -▁but -▁will -ight -out -▁st -ck -▁su -▁whi -▁which -ction -▁t -▁are -▁would -os -ous -▁an -▁un -▁H -▁The -▁him -ity -ag -▁r -▁C -▁1 -▁M -▁at -ill -own -od -other -▁j -▁de -▁ne -um -ure -▁ch -▁B -▁her -op -over -iv -ound -ak -ist -ig -ack -est -▁tr -ard -ome -▁some -▁19 -end -ence -▁has -ally -▁v -▁e -▁or -▁wor -▁W -▁y -▁you -▁all -▁wer -▁were -▁we -▁P -ers -ess -res -ies -iz -'s -ong -▁se -ate -for -able -▁k -▁le -ud -▁out -pp -▁comp -▁exp -up -▁up -▁sp -▁F -ish -act -ect -▁gr -bout -▁tim -ap -ach -▁ev -el -imp -ost -▁new -▁dis -▁kn -em -▁who -▁when -▁been -ant -ance -▁man -ace -art -▁mor -▁pl -▁al -▁other -▁about -ition -▁app -▁D -ast -et -▁more -ust -st -ot -▁could -ort -ign -▁R -▁N -▁into -▁tw -▁us -▁" -▁comm -ab -old -▁do -ok -▁so -ook -ood -ind -▁G -▁than -ial -ical -▁work -irst -▁their -▁first -▁one -▁only -▁O -▁what -▁two -▁part -ip -▁L -▁E -▁pres -▁pr -▁imp -▁qu -▁over -▁op -▁T -▁J -▁ro -av -▁should -▁she -ious -▁cl -▁str -ang -ob -ik -▁lik -ib -▁even -rough -▁through -▁und -▁ad -▁though -▁such -vel -00 -▁2 -age -ff -ject -▁if -very -▁In -▁time -ose -ame -▁any -▁ag -▁again -ated -▁en -▁- -ult -▁high -tain -velop -▁ob -urn -ub -▁just -▁can -▁ye -▁year -▁ar -▁( -ary -ew -aw -way -ward -ac -oc -ail -▁world -ond -ress -▁ind -iff -▁diff -elf -ful -▁those -▁back -▁bo -▁its -ild -▁look -▁long -▁tak -▁sub -▁develop -▁He -▁now -▁inter -ide -▁like -▁br -fter -▁after -▁mak -reat -▁great -▁how -ire -▁there -▁i -▁our -ie -▁supp -▁most -▁may -▁me -▁must -aid -▁said -aus -are -▁pre -▁your -▁well -ark -ick -▁know -▁hand -ber -▁per -ible -▁befor -▁down -▁dr -▁child -▁they -ove -ian -▁own -▁every -▁good -▁res -▁pos -▁day -▁way -▁po -▁U -ffect -ell -▁under -form -ass -ations -pt -cept -▁coun -▁bec -▁becaus -cess -▁each -▁stud -ens -ine -ge -og -lect -▁state -ities -itt -app -▁against -ittle -▁little -▁It -▁rep -▁cont -▁fr -▁very -▁An -cre -▁cons -ubl -ublic -▁seem -▁before -ade -▁made -ree -one -ice -▁three -ident -▁did -▁des -air -ather -▁But -▁another -▁many -▁bl -cial -▁off -▁turn -▁never -ution -▁ac -▁acc -▁rec -ople -ates -amp -▁K -▁while -ield -▁still -olog -▁3 -ool -chool -▁col -▁pol -▁And -s. -▁both -ath -ific -▁interest -▁school -▁act -uct -irect -oll -ollow -▁wher -▁where -erv -ater -iver -thing -▁because -eople -▁people -▁lif -▁Wh -ative -se -▁these -s, -self -▁himself -▁spe -▁num -▁number -▁import -cl -clud -und -▁commun -▁rel -▁prov -ov -▁proble -ular -▁plan -▁place -▁add -ature -▁hous -▁effect -▁em -▁am -▁rem -▁cr -gr -gram -▁program -▁cour -uch -▁much -ness -overn -overnment -ax -creas -▁old -stem -▁result -ual -▁sto -▁Y -atter -arr -▁around -ces -ord -▁consid -ans -▁trans -ict -▁course -ock -▁go -▁prop -▁prod -oup -▁group -▁resp -▁serv -▁countr -ue -ep -ite -we -ween -tween -▁between -▁follow -▁fact -ystem -ull -uc -▁pers -▁person -▁word -▁without -▁problem -▁public -ational -ility -▁ear -▁play -▁fl -▁te -▁life -▁youn -▁poin -▁point -ink -▁think -ix -▁ph -▁make -▁system -▁sc -▁disc -ph -▁Ch -▁Sh -hip -▁them -▁form -man -▁195 -▁als -▁also -▁show -day -▁quest -▁question -▁years -▁ab -ince -▁direct -▁end -▁sm -▁small -▁This -ually -▁char -▁chang -ied -ily -ia -iet -▁get -ably -▁appro -▁last -▁att -▁home -▁since -▁social -▁St -▁present -▁comple -ile -▁differ -▁gen -▁gener -▁government -▁196 -▁enough -ience -▁call -ory -▁my -▁im -▁consider -▁same -▁become -▁V -▁came -ount -▁cent -▁open -▁alway -gan -▁found -▁bas -▁water -▁fe -term -▁exper -▁wr -▁knew -▁few -ustr -▁industr -uild -▁reg -▁young -▁feel -ember -▁increas -▁class -▁special -▁equ -▁requ -emb -embers -▁ord -▁thought -ertain -ext -az -ugh -▁ent -ient -▁giv -▁Un -▁important -000 -▁prof -ank -▁certain -". -▁right -▁development -ism -▁pass -les -▁dep -▁poss -▁possible -▁18 -▁contin -▁ass -ons -iss -ivid -▁individ -▁individual -▁contr -▁conf -▁cond -▁condition -▁Mr -▁sl -ever -▁however -▁conc -▁might -▁4 -▁art -▁oc -▁occ -▁expect -▁start -▁ref -▁itself -ision -augh -▁ke -ican -erican -▁partic -▁particular -ower -▁states -▁something -▁nothing -▁say -▁read -▁stand -▁def -▁experience -▁head -▁sur -pl -uck -▁next -▁night -▁want -▁report -land -▁build -▁keep -▁house -▁order -ically -▁record -ug -▁eng -uction -▁oper -ense -▁unders -ments -▁lead -atch -urch -▁church -▁charact -▁dec -▁adv -▁On -ning -▁war -▁jo -▁sec -▁second -▁see -▁sever -▁set -ganiz -▁opp -outh -▁general -alk -ission -▁loc -▁law -ise -read -iew -▁New -▁Af -▁position -▁simp -▁subject -▁object -▁necess -▁need -▁nor -ief -▁elect -▁remain -▁inst -▁design -▁sign -▁along -▁among -▁american -▁room -▁national -▁contro -▁bel -▁belie -▁complet -▁examp -▁determ -▁dist -▁process -tom -▁adm -iness -usiness -▁business -less -▁less -▁lef -▁left -▁level -▁tax -ty -▁let -▁members -▁kind -▁addition -▁inv -▁president -▁power -fort -ash -ase -ush -most -▁almost -▁cost -▁cap -▁cor -ors -▁thing -▁real -▁val -▁dem -▁She -▁use -▁take -▁happ -▁find -ating -▁dur -▁during -▁includ -ention -▁5 -▁ide -▁gu -▁continu -ason -▁reason -▁upon -▁light -▁several -▁bro -▁different -▁service -▁concer -ere -▁There -▁above -▁fam -▁family -▁walk -▁became -▁tech -▁techn -▁given -▁accept -▁hum -▁human -▁compan -▁arm -▁Am -▁American -idence -ear -▁appear -arge -▁large -▁white -lish -ified -▁strong -▁gl -▁stre -▁empl -▁mark -▁come -aterial -▁later -cret -ances -▁colle -ions -▁ask -▁support -▁short -pe -ike -▁once -ized -▁used -▁near -▁car -▁co -olution -▁four -▁repres -▁prev -artment -ently -▁leg -▁least -▁known -▁For -▁view -aph -▁close -▁alread -▁var -▁far -▁field -▁term -▁fin -▁went -▁case -▁cle -▁clear -▁care -▁univer -▁job -iqu -ipp -▁yet -▁respons -▁respect -ablish -ents -▁rather -▁example -▁imm -▁name -ibility -▁hard -▁mov -though -▁although -▁organiz -ize -▁offic -▁polit -▁polic -▁political -▁childr -▁children -▁men -enc -▁grow -establish -▁relig -action -anc -▁pract -▁toward -ither -gether -ith -uth -ody -▁mod -▁expl -▁express -▁reach -▁teach -▁$ -ont -▁front -▁ret -▁return -▁rest -onom -conom -▁econom -ton -▁wom -omm -▁either -▁el -led -ited -▁United -▁always -▁away -▁early -ological -▁product -▁rev -pect -ences -▁perform -▁acro -▁mean -ange -ek -▁week -▁sk -ork -▁ele -▁ey -▁eyes -▁ever -igure -▁figure -▁wid -ially -▁talk -▁began -▁rece -▁spec -▁profess -▁put -formation -icult -▁difficult -wh -hn -▁john -▁sw -▁quite -empt -▁approach -▁means -▁cur -▁caus -▁carr -▁took -ately -▁meet -▁street -▁fri -▁friend -ich -▁entire -▁remember -thod -▁method -▁meas -ohn -▁John -oy -▁employ -iety -▁except -▁ext -tim -▁provid -istr -▁distr -ffort -▁effort -▁exist -▁mus -▁whose -▁country -▁min -▁run -▁Mr. -▁Mrs. -▁secret -▁matter -▁move -▁6 -▁anything -emp -▁temp -▁death -▁eas -▁material -cr -▁descr -ccess -▁success -▁feet -▁prin -▁princ -▁princip -▁Comm -▁book -▁proper -▁mon -▁month -▁mind -▁wind -▁speak -▁ground -▁board -▁boy -▁suff -ves -elves -▁measure -istic -▁States -▁amount -aj -▁maj -▁major -▁mr -iction -▁six -▁af -af -▁side -▁too -▁o -▁establish -▁7 -▁prom -▁electr -▁face -▁evidence -▁imag -▁vis -iously -▁invol -▁If -▁necessary -▁provide -▁miss -▁mil -ven -▁attempt -atur -ency -▁college -▁local -gress -side -te -▁here -▁hop -▁prob -▁probably -▁kept -▁unt -▁until -▁united -▁represent -▁plac -▁fac -▁saw -▁market -▁watch -▁got -▁black -truction -▁stock -tern -▁del -▁hel -▁help -▁writ -▁body -▁big -▁then -▁concern -▁morning -▁par -▁give -▁gave -▁activ -▁liv -▁hour -ailable -vailable -▁value -▁valu -▁recog -▁dev -▁rad -▁radi -aving -▁having -▁hold -▁past -iod -▁period -▁thir -▁describ -▁various -▁whe -▁whether -alf -▁half -▁called -educ -▁educ -pr -▁natur -▁taken -▁receiv -▁priv -▁brough -▁vol -▁require -▁sense -wn -▁10 -▁indic -selves -▁themselves -▁const -elt -▁felt -▁free -▁camp -▁cover -▁stood -▁minut -▁letter -▁character -▁surf -▁surface -▁draw -ner -ier -ior -▁step -▁ver -urther -▁pur -▁purp -▁really -▁pop -▁project -▁discus -▁discover -▁subst -▁being -▁When -▁press -▁pressure -orn -ore -▁appl -▁music -ines -gin -▁begin -▁conv -uthor -▁main -▁pain -▁train -▁mill -ability -ten -eng -ength -▁underst -▁understand -▁love -atic -▁economic -▁control -▁specific -▁mother -▁full -inistr -▁administr -▁often -par -▁prepar -▁prec -▁regard -ilities -▁sometim -▁techniqu -▁brought -ead -ake -▁complete -▁community -▁city -▁arr -▁times -▁does -▁door -ches -oph -hind -▁behind -▁gir -▁girl -▁company -ury -▁century -▁production -▁demand -▁land -▁office -▁further -▁fre -▁gre -▁bre -▁break -▁15 -▁usually -▁students -▁already -hap -▁perhap -▁tre -▁spir -▁spirit -▁prim -▁account -▁accord -▁recogn -▁Fr -▁produc -▁road -▁trad -▁tradition -▁experim -ages -▁beginning -▁attention -▁mar -▁contain -itution -▁assoc -▁associ -oub -oice -▁voice -▁ve -▁signific -ished -ved -▁million -rist -ristian -▁list -▁asked -▁reaction -▁sound -icture -erature -▁temperature -▁sup -▁sug -▁sugg -▁suggest -▁sum -▁sure -▁summ -▁test -ibut -ization -uff -body -▁believe -▁lib -abor -ibution -ting -▁relations -▁soc -▁society -▁done -▁institution -▁private -ym -▁sym -irm -▁firm -▁requir -▁They -▁phil -▁conduct -▁picture -▁pay -▁wif -▁religious -▁common -▁assum -ium -▁everything -▁line -▁looked -▁1960 -▁met -▁swe -▁ident -▁ill -▁information -▁best -▁invest -▁committ -▁progress -truct -▁woman -▁opport -▁opportun -▁jud -▁standard -▁ty -▁type -▁typ -▁8 -▁study -▁determin -▁doub -ouble -▁separ -▁frequ -just -▁court -▁Br -▁acros -▁across -▁occur -▁maintain -▁obtain -▁moder -▁modern -ung -unction -▁function -▁det -▁detail -ted -▁deep -aff -▁neg -▁York -▁york -▁tell -▁assign -▁dark -▁remark -▁nature -▁town -▁struct -▁section -▁defense -▁together -▁His -▁neigh -eral -ional -▁outside -▁decision -resident -enn -▁oppos -▁surpr -▁depend -▁department -▁squ -▁purpose -▁shall -▁wall -▁available -▁hist -▁history -tle -▁whole -▁int -outher -▁somewh -▁volum -head -▁instead -▁plant -▁corp -▁sal -▁personal -irection -▁happen -▁lear -▁learn -▁marr -▁12 -▁milit -▁military -▁obv -attle -ling -idd -▁With -▁mom -▁moment -▁becom -▁resear -▁action -▁true -▁pat -▁pe -▁peace -▁involved -▁194 -▁turned -▁leave -ne -▁property -▁seemed -▁cut -▁stopp -▁policy -▁leaders -uture -▁future -▁research -itch -▁obs -▁observ -▁doct -▁inf -lf -▁self -▁infl -▁influ -▁cir -▁circ -▁labor -▁window -▁concept -▁thus -clus -▁author -▁treat -▁third -fully -ouch -tter -▁better -ters -▁told -▁held -▁broad -▁visit -▁ir -▁mag -▁propos -▁clos -▁direction -▁farm -omp -▁brother -ology -▁air -▁join -▁popul -▁cre -reg -▁education -come -▁Some -▁agree -▁decl -▁enter -▁actually -chie -▁why -▁16 -ene -▁We -▁women -▁earth -ources -▁attack -oud -ought -ger -get -tract -fact -▁things -▁square -▁patient -▁popular -raph -ograph -dd -▁sudd -▁sudden -▁ann -▁therefor -joy -▁enjoy -outhern -▁post -▁rock -▁countries -▁slow -▁allow -▁seen -greg -ugg -▁comb -▁hund -▁hundr -▁fund -▁fall -▁space -▁wife -▁responsibility -▁sometimes -ator -▁equip -▁equipment -▁mach -ists -▁President -▁sch -iddle -▁deal -rope -urope -▁alone -▁writt -▁famil -▁ant -▁quick -▁father -▁cool -▁mot -▁mone -▁money -▁else -▁making -▁ser -ives -▁color -avor -▁favor -▁En -▁Eng -▁impress -itud -▁attitud -▁remov -▁going -▁announ -ification -ination -", -osoph -▁philosoph -off -▁intern -▁international -▁today -▁impro -▁histor -▁wond -oot -▁ago -▁discuss -iam -▁district -▁fear -▁strength -▁feature -▁sit -▁situ -▁situation -▁pie -▁hot -▁wonder -▁finally -▁fail -▁norm -▁north -ford -▁engine -▁lo -▁To -▁building -▁coup -▁twent -ev -▁1961 -▁qual -▁dead -chieve -▁collect -▁occas -▁difference -▁influence -▁grad -ital -▁bar -▁appar -▁assist -pec -pecially -▁neighb -▁occup -▁attend -epend -▁independ -▁heav -▁change -eign -▁fire -▁five -▁police -▁abs -▁generally -▁area -▁touch -oth -▁park -▁explain -erman -aut -▁beaut -▁beautif -▁tele -▁pap -▁contribut -▁sat -▁values -▁univers -▁university -▁perhaps -▁phy -▁phys -▁physical -ry -▁getting -▁lines -▁length -▁lim -aim -▁claim -▁sim -▁simil -▁similar -▁wa -▁wait -▁industrial -▁industry -▁English -▁english -let -ley -▁At -▁beautiful -▁earl -▁earlier -▁fil -ington -▁characteristic -▁illustr -▁professional -▁bene -▁text -▁17 -▁wood -▁You -itive -▁effective -▁recomm -▁compet -▁minutes -▁moral -▁wild -▁decid -▁lower -▁desp -rop -▁democ -▁growth -▁experiment -▁div -▁lit -▁literature -▁complex -▁days -▁spr -▁sn -arily -▁neither -▁brok -▁doubt -▁capital -▁idea -▁center -▁hundred -▁scre -▁sol -▁blood -▁orig -▁origin -▁within -▁fell -▁kill -▁apparently -ential -ledge -▁knowledge -erence -▁Al -▁conver -▁congress -hood -▁food -▁detect -▁bill -urd -ivil -▁civil -attern -▁pattern -▁As -▁Ar -▁After -▁investig -▁25 -▁obviously -utom -▁block -▁relationship -▁recommend -▁distribution -▁enc -cent -▁foreign -▁sens -io -oon -▁symb -▁symbol -ices -▁services -▁dom -▁feeling -▁conclus -graph -▁basis -▁official -▁address -son -▁scient -icient -▁sufficient -▁lost -▁running -▁therefore -ute -▁poet -▁wide -▁truth -▁adjust -▁cell -▁science -▁curr -▁Of -▁spok -ival -▁south -▁middle -▁compar -▁eight -▁One -ee -▁committee -▁structure -▁vir -ode -▁god -▁reflect -▁select -▁season -ountain -▁written -▁wrong -▁centr -▁central -idge -▁basic -▁shown -▁doll -▁dollar -▁mass -▁planning -rod -▁introd -ames -▁quiet -ros -ross -▁cross -▁cal -▁es -▁especially -▁trial -▁command -▁slight -▁carried -iate -▁low -▁How -▁below -▁simple -▁simply -ensive -ension -▁areas -aster -ases -▁cases -▁ideas -ister -▁miles -,000 -▁piece -▁clean -▁flo -▁floor -▁brown -▁gone -ump -port -▁activities -like -▁recently -azz -▁liberal -ederal -▁federal -▁satis -▁population -▁natural -▁performance -▁seems -▁activity -encies -isc -▁ter -▁terr -▁throughout -▁relative -umb --f -▁glass -▁makes -▁division -▁What -▁crit -▁mrs -▁hands -▁age -road -▁cold -rem -▁extrem -▁perm -ustom -leas -▁pleas -▁whom -▁aff -▁eff -ground -▁recent -▁pick -▁acqu -▁regular -upp -uce -▁drink -▁extend -▁opportunity -▁facilities -▁thr -ift -aft -▁govern -▁movement -udience -▁ess -eness -▁benef -▁appropr -▁super -▁comfort -▁secretary -▁event -▁stress -▁demons -▁Ind -ird -▁Miss -▁creat -▁threat -▁limited -▁limit -ench -▁numb -▁numbers -urb -pital -ospital -▁pieces -▁magn -br -▁However -▁technical -▁tried -▁chair -▁serious -▁judg -▁heart -▁That -▁treatment -▁z -▁seven -▁election -▁easily -▁disp -▁disapp -▁able -▁formul -ufact -▁manufact -▁thirty -▁dre -▁dream -▁arg -▁club -▁administration -zz -▁encour -▁blue -▁hope -▁forces -▁scientific -▁final -▁mere -aught -▁importance -ify -▁fif -▁fill -▁Then -▁applied -▁emot -▁tend -ians -▁commission -▁win -ape -▁democr -▁increase -gree -▁degree -▁cult -▁corner -▁proc -ec -ashington -▁Washington -▁30 -▁20 -▁force -ides -▁student -▁sent -▁hair -▁God -▁legis -▁legisl -▁All -▁twenty -▁carry -▁unc -estern -ables -▁rom -▁9 -▁ed -▁est -▁estim -▁immed -▁obvious -▁skill -▁100 -▁displ -▁charge -ument -▁demonstr -▁suddenly -▁required -▁pull -▁image -▁table -▁prevent -▁continue -▁11 -▁film -▁symp -ues -▁bu -angu -▁langu -▁sales -ann -ane -▁emph -osph -▁lay -▁circum -▁circumst -▁circumstances -▁following -▁solid -▁hospital -▁eye -▁che -▁check -▁base -▁nations -▁interv -astic -▁somewhat -▁emphas -▁suit -▁west -▁heard -▁possibility -▁possib -▁thick -▁fair -▁affair -▁particip -▁ship -▁nuc -▁nucle -▁member -▁Thes -▁These -tegr -rote -▁wrote -ropp -▁Pro -▁language -▁stage -▁prot -▁contract -▁autom -athe -unk -▁gun -▁lack -equ -▁problems -▁determine -▁trade -▁trave -▁try -▁trouble -ert -erg -▁energ -▁emerg -▁paper -▁custom -▁produce -▁No -▁variety -err -▁los -▁compos -▁frank -▁occurr -▁flow -▁religion -▁provision -▁meaning -▁meeting -▁immedi -▁immediately -▁immediate -town -▁manag -ague -▁exact -▁paid -▁corpor -verage -▁1959 -urg -▁measur -▁thous -▁thousand -▁manner -▁Christian -ires -iol -▁viol -▁vari -▁pool -athol -atholic -▁living -▁range -▁choice -▁percent -▁county -ah -▁manufactur -▁pot -▁potential -▁essential -▁normal -▁exce -▁ball -▁quality -▁coll -▁frequently -▁opin -▁collection -▁worth -rup -ired -▁Or -▁hotel -▁constant -▁substant -▁myself -▁herself -▁ten -▁Q -▁applic -▁stat -eric -▁Americ -▁americ -▁amb -ler -▁sort -ortion -▁teleph -▁brief -▁chief -▁belief -▁party -▁existence -ural -▁model -▁revolution -▁review -▁interview -▁green -ously -▁words -▁series -▁serve -▁consequ -▁construction -▁consist -orm -amm -men -▁wanted -▁rich -▁electron -▁repl -▁sil -▁esc -▁yell -▁fellow -▁concent -▁dropp -▁poem -othe -▁concentr -▁stay -▁staff -▁ste -▁story -▁minim -int -▁domin -ale -▁pret -ories -▁spect -▁spread -▁sun -▁Cong -▁Congress -▁sex -▁saf -▁shar -epartment -▁communic -▁pretty -▁longer -▁carefully -work -wer -swer -▁answer -▁coop -▁statement -▁har -▁laugh -teen -▁colum -▁column -▁192 -▁river -▁scene -▁vict -▁novel -▁christian -▁publ -▁ox -▁oil -ux -▁successful -▁trip -▁leadership -▁differences -es, -hib -▁burn -tra -by -▁convention -▁live -▁foot -▁14 -ompan -▁merely -▁improve -▁prote -▁defin -▁annual -▁actual -eg -▁occasion -▁State -▁rate -▁narr -tal -▁total -▁surv -oint -arch -▁search -unch -▁sharp -▁optim -▁liqu -▁positive -▁England -ection -▁inde -▁belong -▁furn -▁experi -▁cop -▁appropriate -▁radio -▁expected -leep -▁offer -▁neighbor -▁neighborhood -▁clothe -▁contrast -time -owd -▁crowd -▁marri -▁married -▁marriage -▁write -▁latter -▁arch -room -▁organization -▁particularly -ottom -lin -iform -aith -▁faith -avior -▁approx -havior -▁according -ison -ais -▁rais -▁critic -▁summer -▁protect -▁ut -▁vot -▁den -▁surpris -ipt -cious -resh -agement -▁boat -▁box -atory -▁listen -acy -▁purpos -▁Europe -▁europe -itting -▁Department -▁scr -unday -▁east -atives -▁strugg -▁roll -▁unit -▁excell -▁machine -▁restr -▁corres -▁corre -▁opinion -▁extent -▁suffer -▁impos -▁previous -▁volume -▁Ad -▁mention -▁greater -▁approxim -axim -▁maxim -ship -▁possess -▁hall -▁Will -▁William -▁hor -▁horse -▁anyone -▁distance -▁distin -erc -▁exerc -inc -▁exc -▁perf -▁perfect -craft -▁uniform -▁fisc -▁fiscal -▁inside -▁believed -▁famous -ouse -▁couple -▁elements -▁buil -▁built -▁non -osphere -where -▁breath -▁voc -▁size -▁vocational -▁50 -▁sold -▁sleep -▁sof -▁sing -▁single -ators -▁reduc -ony -▁soon -ric -▁1958 --d -▁arrange -▁pref -▁satisf -▁nearly -pre -▁interpre -▁Even -▁price -▁adequ -▁descript -▁stra -▁straight -▁fight -▁democratic -ask -▁correct -▁distinct -▁Government -yond -▁beyond -▁exec -▁significant -▁bridge -▁bring -▁including -lectual -▁neck -add -▁bad -▁enem -▁stop -▁dog -▁politic -▁element -iveness -▁fifty -▁fifteen -▁seek -ants -▁Commun -▁status -▁wheel -▁shot -▁buy -▁jazz -ares -▁mouth -▁song -▁emotional -▁transf -▁jack -obile -lection -▁published -▁corresp -▁correspond -▁display -▁companies -try -arger -▁larger -▁bright -▁secur -▁principle -▁gain -jection -▁sus -▁taking -▁waited -▁From -▁yard -▁england -▁painting -aly -▁analy -▁management -▁House -offee -▁agreement -▁charg -cript -▁maximum -▁followed -▁di -▁dress -▁due -▁contribution -▁received -▁South -▁benefit -▁edit -▁changes -▁index -▁current -▁western -▁wish -▁evident -▁directly -▁recognized -▁developed -▁started -aves -▁nation -gregation -▁adver -▁protest -▁accompan -▁hear -▁leaving -▁entertain -▁23 -▁push -▁aircraft -▁assemb -▁events -ties -▁soft -▁Rep -▁slowly -usion -nection -▁connection -▁dram -▁coming -▁$1 -▁moved -▁encoun -au -▁De -ey -▁easy -▁despite -▁practice -▁game -▁schol -hington -alth -▁health -▁upp -▁proced -▁procedure -▁jun -▁Not -▁Kenn -▁studies -omb -▁bomb -▁Church -▁purch -iation -▁bank -▁Rob -▁murd -▁French -▁reached -▁red -▁exhib -▁extra -▁nuclear -▁exam -▁electronic -▁key -▁hy -▁hyp -▁hon -imin -▁transport -▁191 -▁193 -▁strange -lex -ox -▁Ex -▁Tr -▁additional -▁caught -▁chance -▁objective -▁training -uit -ivity -ailroad -uss -▁Russ -▁russ -▁argument -▁Robert -ique -▁original -▁vac -▁capac -▁capacity -▁trying -▁late -▁achieve -▁certainly -itted -band -usband -▁husband -▁German -▁german -▁compl -mber -bert -wood -rupt -ellectual -▁intellectual -udd -▁approximately -▁contact -anger -▁danger -▁warm -▁trust -ived -▁kenn -▁kitch -▁failure -▁Christ -▁refus -▁conscious -▁finished -▁protection -▁fine -▁advant -▁indicated -▁team -▁higher -orge -eorge -▁devot -▁controll -▁walked -hode -sh -▁fresh -▁comes -tic -▁decided -aturday -▁susp -▁added -▁assistance -▁son -▁samp -▁southern -▁sides -trument -▁behavior -▁emb -▁Nor -▁Neg -▁Negro -▁Now -odd -▁cause -▁request -▁construct -▁consum -aily -▁daily -ata -▁data -▁America -▁opposite -▁honor -▁discussion -theless -▁frame -▁24 -▁brush -orney -anch -▁mech -▁mechan -▁fix -▁musical -▁satisfact -▁North -ception -▁clim -▁average -▁editor -▁former -▁cooper -▁gives -▁requires -▁spot -▁lot -▁Man -▁' -▁Con -▁Cl -▁escape -field -▁conference -▁mission -ource -▁urg -▁bottom -▁appre -▁veh -▁vehic -here -▁however, -▁reve -▁reveal -▁ang -agg -ament -▁christ -▁National -▁educational -▁fundament -▁background -▁usual -▁doctor -▁radiation -▁completely -▁dest -▁destro -▁destroy -▁french -▁advantage -▁extreme -urr -co -oard -▁round -aughter -▁months -hold -▁West -ounc -▁inc -▁incident -▁resident -▁load -▁U. -▁U.S -▁requirements -▁factors -▁master -▁considerable -▁shape -▁studied -cul -▁calcul -▁Gr -▁libr -▁experienc -▁mem -▁memory -▁valley -▁virgin -▁virt -▁source -▁depart -▁resources -▁sources -▁names -▁clearly -chieved -▁subm -▁excess -▁scholar -▁spring -▁reference -▁referr -▁err -celer -▁acceler -urban -▁Although -▁operation -▁odd -partment -part -▁install -▁intens -▁intell -▁intellig -▁heavy -es. -▁Republic -▁republic -▁accur -▁accom -▁accompl -▁deb -ball -▁introduc -oviet -▁plat -▁platform -aint -aul -asons -▁reasons -▁operating -aged -agon -oid -void -ole -ille -ville -▁role -ett -▁imagination -ession -▁union -ashion -▁fashion -▁edge -well -▁aw -▁aware -wise -▁driv -▁prison -▁disturb -▁william -itiz -▁citiz -▁citizens -izing -▁execut -▁sitting -▁facult -▁gas -▁"Th -▁permit -▁cloud -▁active -▁drive -▁receive -▁danc -▁guard -udget -▁ep --c -▁announc -▁properties -▁Since -▁Se -▁Secret -▁Secretary -▁primary -▁primarily -be -▁narrow -▁include -▁promot -▁guest -▁respond -▁conditions -▁types -▁(1 -▁becomes -▁authority -▁cattle -▁battle -▁Col -▁sweet -▁washington -▁ability -▁precise -oul -ouis -ivers -▁parents -▁freed -▁freedom -▁indeed -▁desire -▁resist -▁march -change -▁exchange -▁exclus -▁convinc -▁distinction -▁rul -▁responsible -ourn -▁dramatic -▁ign -aign -▁campaign -▁membership -onscious -▁murder -▁kitchen -▁corporation -▁energy -▁hill -▁hyd -▁none -▁telephone -▁naturally -▁electric -▁quar -ipped -ed. -▁universe -▁expression -▁born -▁bal -▁balance -▁bit -▁bird -▁significance -▁equal -per -▁session -▁declar -▁weak -▁resolution -▁solution -▁justice -▁heavily -▁hours -▁attorney -▁likely -▁liter -▁literary -▁principal -▁principles -▁share -▁yes -▁yield -▁yester -▁yesterday -eneral -▁General -▁proce -akes -itute -▁merc -▁contemp -▁stream -▁divid -▁recall -▁exactly -▁liquid -▁constitution -), -). -▁13 -iar -▁familiar -▁star -▁whate -▁whatever -pir -▁Federal -▁island -chestra -▁attitude -▁knif -▁mult -▁multip -▁& -▁forward -▁cities -▁hom -rel -▁acad -▁academ -▁conce -▁robert -▁enemy -▁Act -▁previously -▁audience -etry -▁poetry -▁poor -▁match -▁agre -▁america -▁related -50 -roll -▁negro -ptember -▁substantial -▁jew -▁smil -▁instance -▁attract -▁intention -▁separate -▁shop -▁adop -▁mis -isk -▁experiences -ime -ultim -▁ultim -▁consult -▁repe -▁empt -ption -▁ideal -▁security -▁association -▁aspect -apid -▁rapid -iment -▁experiments -▁clay -▁cris -▁crisis -▁grand -cient -▁distribut -burg -▁pan -▁panel -▁altern -▁respon -▁response -▁necessarily -▁looking -▁officers -▁officer -year -ych -sych -▁psych -▁entr -▁assess -▁mess -reek -▁lock -▁la -▁Mar -unt -irt -irth -▁birth -▁repeated -▁others -▁elimin -▁understanding -▁writing -oto -▁photo -▁photograph -▁sensitive -ordin -erve -vey -▁survey -▁sport -▁counter -▁Univers -▁captain -▁mix -▁med -▁medical -board -▁arrived -▁Ph -▁Phil -▁switch -▁indicate -▁terms -▁stret -▁stretch -ushch -ushchev -rushchev -hrushchev -▁interpret -▁operations -▁everyone -▁ready -▁reading -▁rates -▁magaz -noon -▁afternoon -▁cook -▁coffee -▁bitter -▁double -mosphere -▁recognize -▁commer -▁commercial -ancial -▁financial -ery -yn -yl -▁upper -pap -▁equival -▁families -▁supply -▁childhood -▁vital -overeign -▁task -▁anim -▁animal -imm -▁snow -▁hung -▁tee -▁teeth -▁"I -▁top -▁host -▁minimum -▁reduce -top -▁beneath -▁blind -▁takes -▁Dr -▁truck -▁ordin -rove -roop -▁troop -▁grant -▁suppl -sue -▁issue -▁stopped -▁Soviet -▁agencies -oke -▁Be -▁Other -aries -▁baseball -itish -▁British -▁Brown -▁british -▁hol -arm -inner -▁dinner -▁22 -▁conviction -▁understo -▁understood -▁surr -▁drew -light -▁weight -▁sight -▁improved -▁budget -▁0 -▁Thus -▁sell -▁policies -▁apprec -▁trail -▁unique -▁twice -▁flat -▁learning -▁concerning -▁Another -▁mainten -▁typical -▁thinking -▁bul -▁bullet -▁ful -▁traff -▁combination -▁provided -▁disappear -▁interf -▁meant -▁somewhere -▁restrict -▁otherwise -▁shoot -▁atmosphere -▁increased -▁increasing -▁costs -▁suspect -▁env -▁analys -▁analysis -▁relief -▁hydro -iforn -ifornia -▁rose -▁spend -itude -▁First -▁fast -▁strik -▁loose -omas -▁moving -▁techniques -ottle -▁bottle -▁Southern -▁extensive -▁bur -ured -▁appe -▁appeal -ender -▁remind -▁him. -▁cand -▁candid -▁hit -▁determined -▁considered -▁pack -▁needed -▁cutting -then -▁railroad -rew -▁growing -▁rif -▁rifle -ney -▁machines -▁manager -ause -▁loss -▁historical -▁ourselves -▁log -▁practical -▁mountain -▁stead -anced -▁concerned -▁winter -ively -▁sexual -▁spent -▁troub -▁Jew -ule -▁culture -▁placed -▁passed -nect -▁connect -▁Char -ubb -▁expert -▁schools -▁ins -▁instrument -▁insist -▁instruction -▁surround -ront -▁confront -▁eg -▁anode -▁plastic -▁Yet -▁delight -▁roof -uous -▁avoid -class -odies -▁feed -▁reserv -▁quickly -▁shel -▁shelter -uzz -▁judge -▁George -athetic -fortun -▁fun -▁poly -▁pock -▁relatively -▁rise -▁trem -▁gold --m -▁realized -een -eenth -▁director -▁bond -▁suppose -▁suppos -▁medium -▁mount -cember -▁maintenance -abb -▁fat -att -▁remove -▁Car -▁minister -▁automobile -▁realize -▁chap -▁rout -▁presence -▁plain -▁plane -▁kid -spap -▁newspap -▁er -▁path -▁excellent -eless -▁defend -▁sky -▁sympath -▁arrang -▁ancient -▁useful -▁fully -uary -▁ordinary -section -▁et -▁moon -▁Pl -▁impl -▁impact -▁rough -▁rid -▁mid -▁integr -▁exercise -▁dust -ugust -▁hardly -▁slightly -▁60 -▁21 -crib -iber -▁lad -▁lov -▁pocket -▁neu -▁net -▁network -▁sett -▁settled -▁neutr -▁youth -▁conclusion -▁gather -▁weather -▁george --p -egre -▁practices -▁promis -▁permitted -▁philosophy -▁interior -▁conserv -icip -▁anticip -ges -▁deliber -▁deliver -▁region -uly -▁conclud -ault -▁described -▁presum -▁preserv -▁formula -iers -▁soldiers -▁created -▁May -ondon -▁envir -▁prior -▁based -▁independence -▁independent -▁soul -▁entirely -▁spiritual -▁Commission -▁happened -▁places -▁domest -▁signal -▁unless -▁giving -ournal -house -look -uel -uge -▁gradu -▁advert -▁gradually -▁assignment -▁Thomas -▁nevertheless -▁provides -ina -inet -▁ninet -▁nineteenth -ins -▁thin -▁machin -▁machinery -▁race -▁regul -▁Pr -orr -orrow -▁"the -▁setting -▁traditional -▁lives -▁lived -roph -urance -▁entrance -▁concret -▁dominant -antic -iant -▁profit -▁atom -▁advanced -▁advance -▁estimated -▁appeared -▁advis -viet -▁soviet -▁rever -▁reb -▁climb -▁shout -▁plann -▁jump -▁treated -▁react -▁traffic -▁intend -▁urban -▁intense -▁artic -▁advertis -▁War -▁knife -▁ran -▁rang -▁expos -ott -▁Jun -▁believes -▁hypoth -▁phase -▁depth -ures -▁cultural -▁negative -adow -▁shadow -how -▁somehow -▁abroad -yro -▁thyro -▁thomas -▁proportion -▁organizations -▁count -▁counc -▁council -▁suc -▁succe -▁served -▁showed -▁societ -▁greatest -▁daughter -▁encounter -▁accident -venue -▁Ed -traction -lor -▁Bl -▁stick -▁rain -▁attach -▁achievement -iest -▁comment -▁More -▁laid -▁Just -▁administrative -▁travel -▁prices -▁suprem -▁score -▁gard -ternal -▁faculty -que -▁spoke -gl -▁obl -▁oblig -▁fruit -▁rule -ipl -▁proud -▁enfor -▁vehicles -amb -▁trees -▁ultimate -▁adequate -▁crack -▁cry -▁army -▁sty -▁style -▁someone -▁ignor -▁waiting -▁scale -▁competition -▁grew -▁superv -▁junior -▁superior -▁message -▁cast -▁evil -culiar -aneously -▁Western -▁domestic -iplom -▁weap -▁speed -▁spee -▁speech -▁stepp -ke -olk -▁folk -▁concert --h -▁opportunities -▁motion -▁obtained -▁efforts -▁journal -▁writers -▁yellow -▁While -▁White -burn -▁gray -iday -▁simult -▁simultaneously -aces -ees -▁employees -▁judgment -▁continued -acr -▁sacr -ron -▁syn -▁grass -▁dropped -▁anti -▁chairman -▁Jack -▁plus -▁tub -▁surprise -▁throw -▁happy -▁smell -▁Here -anover -▁weakness -▁reject -▁library -▁East -dom -claim -▁sav -▁economy -▁thro -▁stain -▁stor -▁organ -▁expend -▁expand -▁eventually -▁uncertain -▁critical -ets -▁learned -▁everybody -▁University -▁reality -pril -▁lip -▁numer -▁communication -▁Frank -▁mail -▁device -▁Catholic -▁catholic -▁prepared -▁margin -irgin -▁Virgin -▁selection -▁pray -▁application -▁honest -lock -▁income -▁Committ -▁vill -▁village -illiant -▁brilliant -▁minor -▁afford -ones -▁tour -▁send -▁practically -▁struggle -lease -▁powerful -thetic -▁earn -▁reported -▁struck -orough -▁thorough -▁friends -akespe -▁contempor -▁contemporary -▁apply -ricult -▁agricult -▁por -▁threw -ictionary -▁seriously -abric -▁fo -▁foam -▁cat -tribut -uesday -▁opened -▁dim -▁dictionary -▁artist -▁billion -▁raised -▁using -bruary -▁included -▁abstract -▁diame -▁possibilities -▁equally -▁emphasis -isto -▁register -evit -▁engaged -▁traged -▁truly -▁nam -etition -▁proceed -▁ahead -▁However, -enry -tery -olute -▁absolute -▁associated -▁purchase -aud -udson -aise -▁results -▁located -▁spl -▁coast -▁methods -▁purposes -▁ce -▁bound -▁Qu -▁fourth -val -▁approval -▁pal -▁pound -▁pitch -▁instant -▁replace -▁catch -arget -▁target -▁bus -▁bodies -▁circl -▁scholarship -▁reduction -obb -▁rob -▁sen -ened -gen -zen -mans -▁expans -ma -▁operator -▁girls -▁port -▁U.S. -▁guid -▁engineer -▁dozen -▁goal -▁moments -▁dry -▁drunk -▁intensity -ern -▁Every -aur -▁restaur -itation -itz -ze -izz -▁tit -▁title -▁eth -▁definite -▁vote -▁affect -▁trend -▁unlike -▁drove -▁blank -▁sovereign -▁communism -▁Tw -▁fabric -oles -ateg -leg -apan -▁vision -▁examples -▁agency -▁compon -▁components -▁boys -oseph -▁stations -▁store -▁card -▁pleasure -▁adult -▁yourself -▁comparison -▁established -▁filled -▁designed -▁warr -arry -▁cow -▁Each -▁By -abilities -ancy -ancisc -onday -ined -▁diplom -▁desk -lict -▁conflict -▁confidence -▁aid -ef -▁evening -▁unf -▁rare -▁anti- -▁drawn -▁corn -agn -▁apparent -▁americans -▁convict -▁registr -▁etc -▁editorial -▁effic -▁governor -▁mechanism -mouth -ago -icago -▁Chicago -▁academic -apt -▁shr -▁nodd -chester -▁apartment -▁rapidly -▁precision -▁peculiar -ewhere -▁prep -▁efficient -▁substantially -▁garden -▁tendency -▁warning -▁flash -▁writer -omial -▁entered -▁enterpr -▁personn -▁personnel -ayburn -▁broken -▁broke -▁So -▁drug -▁assumption -▁mold -▁bes -▁beside -▁besides -aced -▁replaced -▁recover -▁Such -▁horiz -▁proposal -ocr -emocr -▁Democr -empl -▁closed -▁shift -▁shock -▁hydrogen -arg -▁involves -▁expense -▁expensive -▁jury -abit -▁habit -▁cab -▁chem -▁item -▁chemical -iency -▁efficiency -▁tast -▁vast -▁vary -▁enorm -▁fem -▁female -▁portion -▁agricultural -▁Committee -▁interference -erlin -ving -▁situations -▁tension -▁mist -▁mer -▁40 -akespeare -cle -▁muscle -▁primitive -▁huge -▁shook -▁approved -olved -▁victory -▁identification -▁pure -omin -com -▁accomplished -▁involve -▁inform -▁aband -▁abandon -▁relax -▁iron -▁location -▁died -▁settle -▁loud -▁easier -▁ou -▁Z -▁factor -▁neutral -▁Charles -▁beauty -▁access -▁acres -▁hun -▁hang -▁tang -▁hors -▁doing -▁elabor -aire -▁environ -▁environment -▁capable -▁gall -▁Louis -▁louis -0000 -▁nine -▁nav -▁circle -▁enth -▁enthus -▁enthusi -▁needs -nown -known -▁expansion -▁anx -▁anxiety -▁criticism -▁standing -▁lie -▁teachers -▁teacher -▁psychological -rol -▁intelligence -▁working -aking -ishing -egrees -berg -▁executive -▁regional -▁file -▁drop -▁changed -▁remained -▁sea -▁artery -▁romantic -▁conj -▁congregation -▁retain -▁recre -▁"W -▁hypothe -▁entit -▁entitled -lev -▁victim -▁description -ression -▁cheek -▁highly -▁highway --b -▁harm -▁increases -▁throat -▁nearby -▁sand -▁sympathetic -▁lunch -while -▁reform -▁categ -▁strateg -▁identified -▁identity -▁returned -▁arms -century -▁reflection -ope -▁liber -▁produced -▁contrary -▁possibly -▁palm -▁announce -usual -▁unusual -use -▁metaph -▁Communist -▁joy -▁metal -▁pred -▁predict -▁telev -▁television -▁fright -▁dut -▁penn -▁impression -▁risk -▁visual -▁colon -▁reasonable -umbled -▁headqu -▁headquar -▁headquarters -▁kenned -▁kennedy -▁lady -▁station -atically -▁cub -riet -inted -ointed -▁appointed -put -▁colleges -▁aggress -▁Sunday -▁sunday -▁eval -▁evalu -▁persu -▁legal -▁Japan -▁japan -▁financ -▁smile -▁smo -▁smooth -▁fundamental -▁Kenned -▁Kennedy -▁angel -▁fraction -▁suggested -▁fixed -▁Rhode -▁rhode -▁putting -▁flight -erson -ston -▁tremend -▁gent -▁gentle -▁proposed -▁numerous -▁communist -ette -agger -▁rot -▁rod -▁controver -▁capt -▁foc -▁focus -▁cryst -▁technology -▁quant --year -▁sample -▁unknown -▁telegraph -▁letters -▁safe -▁careful -▁technique -▁represents -▁content -▁conven -▁Arm -▁named -▁covered -▁grave -▁utter -▁objection -lfare -▁caused -▁confirm -▁launch -▁safet -etic -▁workers -amber -▁recognition -obby -▁weeks -▁establishment -unicip -▁Park -▁confusion -▁knock -▁tract -▁detective -▁impressive -▁questions -ecause -argest -▁largest -▁orders -▁petition -▁Two -▁convers -▁majority -▁providence -asc -exas -clos -osition -▁definition -▁spons -▁subs -▁fool -▁rules -▁weapons -▁weapon -▁virtually -▁football -▁residential -▁tone -▁stories -▁whisk -▁wit -▁witness -▁expendit -▁expenditures -omen -enomen -▁phenomen -▁electrical -▁illusion -▁guess -▁guil -▁pil -▁pilot -▁prem -▁adoles -▁internal -▁article -▁varied -er, -orld -▁World -▁neglect -▁negot -▁champ -▁date -▁insurance -▁satisfactory -▁automatically -ers, -ed, -▁course, -ades -▁invent -▁interpretation -▁san -▁sem -▁Dur -tober -usett -achusett -▁bath -▁strain -▁strip -▁norther -▁Dr. -▁gar -ovember -▁statements -▁fiction -▁fig -▁adopted -anuary -▁James -▁games -▁james -▁simpl -ervice -right -ylv -ylvan -ylvania -▁spectac -▁ge -▁gro -"? -▁desired -▁doctr -▁conventional -▁wagon -ography -▁merch -inch -▁persist -▁absence -manship -▁scar -▁wine -stand -▁band -▁houses -▁accomplish -▁sched -▁schedul -▁schedule -▁schem -ky -▁preserve -▁equivalent -▁tables -▁appearance -▁dance -▁$2 -▁payment -▁empty -▁presumably -gun -go -▁context -ats -▁swung -▁flu -▁wil -▁inevit -imony -▁testimony -▁confus -▁strike -▁humor -▁fit -▁pale -▁decreas -▁nerv -▁exercis -▁tempor -▁chos -▁chosen -▁chamber -▁driver -▁River -▁bare -▁burd -▁During -umber -▁maybe -▁horses --s -▁ought -▁resistance -▁wave -▁sought -organ -orp -▁Corp -▁blow -▁introduction -iated -▁spite -▁denied -▁cloth -▁31 -▁barr -▁eleven -▁admitted -▁reception -▁hole -ulation -point -▁appoint -▁Americans -▁league -▁lean -▁announced -▁burst -▁strict -▁motor -▁acceptance -▁accepted -▁encourage -▁sentiment -▁restaurant -▁institutions -igr -hower -enhower -isenhower -▁silence -▁involving -▁percentage -annel -▁orchestra -▁sin -oose -ociation -▁extension -fore -ien -▁alien -▁ec -▁wonderful -▁choose -▁particles -ometer -▁qualities -issue -▁wat -▁waters -▁wished -▁missile -▁promise -▁prompt -▁promin -▁root -▁vacation -▁difficulty -▁hur -▁points -▁societies -ensions -▁Prov -▁Providence -▁emotion -▁Cour -▁Court -▁opinions -▁Her -her -▁id -▁charles -▁estate -▁diameter -▁quarter -▁supplies -▁chapter -▁frequencies -▁artists -▁steps -▁millions -▁opposition -▁prove -▁pros -▁prospect -▁persons -▁steel -▁Rich -▁Richard -he -▁save -uto -▁communities -▁thyroid -avid -▁languages -▁occasional -▁pair -▁stair -▁repair -▁doctrine -▁stranger -▁streng -▁1957 -▁bron -▁bronch -▁ly -▁u -▁utop -ued -▁shut -▁closely -▁reven -▁advice -▁faces -▁bab -▁delay -▁cele -▁celebr -▁curve -▁destruction -▁richard -ra -▁irr -30 -▁automatic -▁marked -▁funds -▁thickness -▁quot -▁combined -ulated -▁liberty -sequ -▁subsequ -▁purchas -▁survival -▁stated -▁extraordin -▁coordin -▁edition -▁flex -▁Alex -▁Before -▁ease -▁Democratic -▁Khrushchev -▁khrushchev -▁hearing -oom -▁(A -▁wash -▁measurements -▁dollars -▁leaves -▁waves -▁March -▁violence -▁tall -▁taste -▁assume -▁metrop -▁raise -▁ballet -▁tree -▁favorite -▁site -▁phr -▁phone -▁cock -▁"The -▁breakf -▁breakfast -▁grown -▁Union -▁brain -▁refr -▁refrig -▁imposs -▁impossible -▁bed -▁bedroom -▁Compan -▁swift -▁mir -▁objectives -▁occasionally -▁accuracy -▁soldier -▁tough -▁planned -▁hypothal -▁hypothalam -▁tal -vember -▁older -▁surrender -▁render -▁track -▁Club -▁valuable -▁indicates -▁charm -▁mal -▁causes -▁agreed -▁angle -▁Colle -▁College -lement -▁supplement -entially -▁essentially -▁poll -▁civilization -▁equipped -▁adj -▁Aj -▁egg -▁decade -▁explan -▁reduced -▁consequences -iences -▁phrase -incol -incoln -▁devices -issipp -▁generation -▁chicago -ompson -▁investment -▁offered -▁transform -▁conform -▁assured -▁Sp -▁emergency -iable -▁relation -ama -▁Old -▁unfortun -▁transfer -▁Black -idden -▁visited -▁rush -▁ris -▁Moreover -▁moreover -▁cup -▁it. -▁expens -▁wage -▁archit -▁architect -▁suburban -▁soil -▁allowed -▁picked -▁figures -▁sail -▁prepare -▁congreg -ental -▁severe -igor -▁vigor -▁profound -▁wound -▁absor -▁teaching -▁diagn -▁imagine -▁taught -▁completion -▁metropol -▁1954 -▁peop -▁peoples -▁treas -enetr -▁London -▁london -▁exception -▁programs -aret -▁marsh -▁commerce -rest -aken -▁screen -▁prefer -▁conversation -▁libraries -▁curious -▁grace -▁providing -▁ho -▁senior -terior -▁veter -▁magnific -▁sho -▁shares -▁admission -▁70 -athem -▁mathem -▁mut -▁buck -▁bought -▁shap -cience -▁conscience -oices -▁preparation -▁tissue -▁representative -▁legislative -▁gh -▁* -▁passage -▁slaves -▁fasc -▁considerably -▁inevitably --r --l --st --1 -▁189 -▁(19 -▁plot -▁roy -ny -▁duty -▁oce -▁chain -gain -▁gross -iological -▁implic -olf --w -▁screw -▁preach -▁magnitude -▁smoke -▁minute -▁fought -▁privile -ri -▁wore -▁Re -▁royal -▁flowers -organiz -▁rank -▁concrete -▁competitive -▁candidates -▁transferr -▁porch -▁welfare -▁suburb -▁everywhere -▁dam -▁twist -▁tragedy -▁priest -▁propag -▁representatives -▁scream -▁Virginia -▁accompanied -▁baby -▁skin -▁physic -▁highest -▁effects -▁guide -▁currently -▁authorities -▁util -▁utility -▁sponsor -▁genu -▁genuine -▁marine -▁prevail -▁territ -▁effectiveness -stitution -▁penetr -▁puzz -▁grain -▁pow -▁powers -▁payments -▁separated -▁emotions -uments -▁silent -vent -▁sevent -▁transl -▁enormous -▁eat -▁stages -itial -▁initial -▁exhibit -▁disting -▁distingu -▁distinguished -iving -▁create -▁funeral -▁universal -▁universities -▁municip -tray -▁assumed -▁supposed -▁Russia -▁Russian -▁removed -▁succeed -ibr -ylind -aze -▁chest -▁plans -ongue -▁tight -▁corps -morrow -allas -▁loan -▁reorganiz -▁wear -▁talent -ctor -▁hat -▁bear -▁rear -▁measured -▁difficulties -▁November -▁strengthen -thentic -▁sick -▁myth -▁Stre -▁Street -▁voices -▁churches -▁safety -▁nobody -▁integration -▁begun -▁tear -▁watched -▁watching -▁twel -▁twelve -▁burden -▁unity -▁thrust -▁thrown -▁soph -▁sophistic -▁transition -eff -efferson -▁Off -ieff -ofieff -kofieff -▁features -▁talking -▁mic -▁clothes -▁products -▁groups -▁edges -▁pleasant -▁worr -▁worry -▁worse -▁worship -▁fellowship -▁perspect -irty -▁neighbors -▁coating -▁played -▁playing -▁virginia -▁Har -▁newspaper -▁stim -▁stimul -▁counties -▁calend -▁calendar -▁california -▁milk -▁goes -uries -▁centuries -▁tongue -▁26 -▁27 -▁discour -▁discipl -▁external -▁feather -▁concentration -▁systems -▁horr -▁employment -▁crew -▁Where -▁rh -ham -▁cam -ald -▁Af. -▁explanation -▁liquor -ander -▁brand -▁propagand -tun -▁fortun -▁volun -▁volunte -oston -▁creative -▁stem -▁stone -▁Many -▁adam -back -▁becoming -noc -ners -▁storm -▁laboratory -▁nice -▁erect -▁powd -▁authorized -artin -▁achieved -▁funny -osc -oscow -▁Und -lantic -▁parties -▁vague -▁vivid -▁mile -▁kick -▁example, -▁leader -otten -▁resum -▁valid -▁Indian -▁Count -▁storage -izes -▁rural -▁patrol -▁attribut -▁friendship -▁saying -▁says -▁alex -▁innoc -▁chall -▁channel -▁Merc -umin -▁opening -▁rational -▁forms -▁Most -▁prominent -outhe -▁readily -▁desper -▁sciences -▁darkness -▁affirm -▁affection -▁deny -▁papers -▁saving -oyal -▁loyal -arian -▁tremendous -▁seld -▁fulf -▁fulfill -▁guidance -▁worked -▁selected -▁pulled -▁expressed -▁suspic -▁suspend -▁ocean -▁calm -▁interval -ayne -gry -▁rhy -▁rhyth -▁rhythm -iance -ius -ios -issippi -▁App -▁smith -aker -▁closer -▁smaller -▁recreation -▁legislation -▁transportation -▁cooperation -▁resemb -lem -more -▁shore -gotten -ta -rietta -▁joint -ouncil -▁Council -▁coal -achel -itut -itain -▁drain -pecific -▁September -ancisco -▁hero -ato -▁rat -▁ratio -▁native -▁Co -▁City -▁personally -▁Hanover -▁hanover -ipping -▁Aj. -▁sequ -▁sequence -isdom -▁wisdom -abel -▁wel -▁welcome -▁snake -▁ambig -▁fairly -▁golf -▁matur -▁dispute -▁condem -▁pride -▁200 -▁2: -▁legislature -▁Henry -▁henry -▁arc -▁U.N -▁movements -▁stiff -▁Both -▁household -▁Once -▁vehicle -▁coinc -▁probability -▁remembered -▁alternative -▁conservative -▁lifted -▁unconscious -▁mad -▁properly -▁largely -▁alive -▁alter -▁handsome -oss -▁insect -▁career -▁muscl -▁arrangement -▁arrangements -▁lect -▁compound -▁them. -▁hunting -▁swimm -▁june -▁courage -▁occupied -▁supplied -▁cylind -▁reput -play -▁mentioned -▁vel -▁veloc -▁somebody -▁dangerous -▁classes -▁busy -elph -elphia -adelphia -gia -orgia -▁Ang -▁Angel -▁mel -▁melt -▁radical -▁Bar -▁submar -▁friendly -ieved -▁seldom -lo -▁completed -▁surpl -▁craft -▁crash -▁cred -▁credit -▁clar -ivision -▁Among -▁frag -▁mental -▁poems -▁includes -▁interested -▁discovered -aver -▁attractive -rm -▁Our -▁vers -▁version -▁consists -▁ice -▁notice -place -▁swept -▁Only -▁nur -▁led -▁Adam -apped -▁proved -▁deleg -ibilities -▁responsibilities -▁salt -inct -▁instinct -▁inches -▁therm -▁conclusions -▁morgan -▁phosph -▁compared -red -▁die -▁Em -▁El -urried -▁worried -rupted -thodox -night -mann -▁Never -▁portray -▁instruct -▁Gre -tronom -utonom -down -▁alleg -▁complic -▁complain -▁passion -▁passeng -▁Britain -▁constitute -▁said, -▁yank -ureau -▁repor -▁gift -▁June -▁1962 -▁propriet -aked -artmouth -▁performances -▁instances -▁advantages -▁king -▁perman -erk -▁clerk -▁lawy -kind -▁mankind -▁personality -ulf -bon -emble --S -igaret -▁volunt -▁ambition -▁won -▁unw -▁musicians -▁Rev -▁worst -▁knees -▁improvement -cend -▁chart -▁maturity -ality -expect -▁emission -inese -▁chlor -▁necessity -▁remot -▁France -▁Friday -▁holiday -▁friday -▁visible -avery -▁190 -▁peas -▁decor -▁expenses -▁Sch -▁absorb -▁Company -▁lose -▁fourteen -▁remarkable -▁lat -▁diffusion -▁materials -▁luck -▁subtle -istotle -rozen -oman -▁explor -▁graph -▁anybody -attered -▁sew -▁x -▁ranch -▁barn -▁merit -▁December -▁lord -▁precisely -ged -▁departments -▁Because -▁extremely -▁Berlin -▁surplus -▁territory -▁theory -▁studio -▁structural -▁homes -▁painted -▁identify -▁failed -▁berlin -▁comparable -irable -▁desirable -quir -▁comparative --degrees -ores -▁memor -▁deliberately -▁abrupt -▁manufacturers -▁composition -a, -▁owners -ohnst -ohnstamm -▁steep -▁dispos -▁Service -▁sid -▁sidew -▁sidewalk -erry -erhap -▁Perhap -▁luc -▁indirect -ustain -▁restrain -▁output -▁pul -▁ul -▁ultr -ulate -▁compul -▁outr --C -▁Though -▁Those -▁appears -istrict -▁northern -▁lyric -▁carrying -▁barb -▁advent -▁downtown -▁remains -▁permission -iles -egregation -▁desegregation -▁camer -▁refriger -▁release -▁dyn -▁oxy -▁oxygen -▁scen -▁profession -▁conjug -ites -▁stayed -rook -▁agent -▁agents -▁Pe -▁Peace -▁genius -▁purs -▁pursu -▁Sc -adequ -▁calculated -▁eag -▁specifically -▁colony -▁december -▁continuous -▁seat -▁cours -▁courses -500 -▁300 -▁removal -▁pink -▁pip -▁Scot -igger -▁winning -together -▁magazine -▁routine -▁chlorine -▁squee -▁knee -▁cruel -▁drill -▁descend -▁wire -▁unexpect -abil -▁britain -▁carbon -▁years. -▁occurred -▁october -nomial -▁polynomial -▁dise -▁disease -▁colors -▁errors -▁error -▁link -▁dism -stead -ly, -▁kiss -▁cigaret -▁cas -▁Further -▁Hudson -▁reserve -▁residence -▁satisfied -▁replied -▁satisfaction -▁Do -cell -▁mood -▁scientists -▁flood -▁russian -▁russia -▁anywhere -ude -▁prosper -degrees -▁amend -▁Chinese -naire -▁questionnaire -now -cknow -▁irreg -▁irregular -▁ones -▁patch -▁barrel -▁doctors -▁embarr -▁embarrass -▁magazines -▁observed -▁successfully -▁absurd -west -ced -▁forced -▁Mc -cular -▁institute -▁School -▁buff -▁jewish -▁symph -▁textile -ifications -▁observations -▁casual -▁extraordinary -▁inevitable -▁unable -▁sixteen -▁mineral -40 -▁apart -▁professor -born -▁despair -uces -udge -▁nose -▁introduced -▁jacket -sland -▁Island -▁atomic -fare -▁warfare -▁termin -iny -▁tiny -▁gyro -▁condemn -▁operate -▁planet -▁permanent -▁bord -ret -▁industries -▁Cent -▁surfaces -▁pun -▁widely -▁dismiss -▁oh -▁rac -▁happens -▁driven -▁Roman -▁regardless -"! -▁september -▁Conf -foot -▁rode -▁blond -▁silver -▁municipal -▁records -▁rust -▁preferr -▁interesting -▁rising -▁drawing -anish -▁finish -▁distinguish -▁furnish -▁Jewish -▁sweep -bit -▁arbit -▁Sometim -▁intersection -aust -haust -▁exhaust -▁accus -▁export -▁reporters -▁heat -▁extr -▁books -ening -▁wooden -enrietta -▁multiple -▁fle -▁proclaim -▁eternal -▁salary -▁salv -▁decide -▁veget -▁symptom -▁enterprise -▁terrible -▁driving -▁Par -▁variable -▁variables -athed -▁illustrated -▁sixty -▁manif -▁manifest -ifted -ife -▁hell -▁Therefore -▁investigation -▁scratch -▁page -▁enthusiastic -▁eld -▁convey -▁november -▁dirt -▁refused -▁assert -▁alert -▁magnetic -ifice -▁sacrifice -▁vice -▁dict -▁retired -▁retire -▁tired -▁queen -▁seventeen -▁Ass -▁Association -▁discussed -wave -▁skywave -▁Let -▁La -▁preced -lfred -▁collabor -▁jur -▁juris -▁jurisd -▁jurisdiction -▁destiny -▁angry -▁legend -▁reprod -▁ladies -▁shirt -▁leading -▁boot -▁sket -▁mistake -▁fluid -▁incomp -▁hurt -▁paint -▁height -▁insight -▁exagger -▁earning -▁estimate -▁accurate -iumph -▁triumph -▁escap -▁exclud -▁respectively -ministr -▁Administr -▁relev -▁reasonably -▁July -▁july -otton -▁cotton -orship -▁neat -▁mechanical -▁sam -▁saturday -▁Saturday -▁works -▁passing -▁circuit -▁fish -izable -acific -▁beach -▁units -▁sister -▁coat -▁prose -ansas -▁constitut -▁years, -▁entry -erted -umble -▁monument -raid -▁afraid -▁maid -▁bloom -▁aspects -▁identical -ibly -▁trib -▁label -▁bargain -aware -▁bread -▁grade -▁finding -▁fighting -▁accurately -▁commit -▁ecum -▁quarrel -▁whipp -▁articles -istry -▁outstand -▁sugar -▁route -▁thank -▁quantity -ae -▁note -▁contribute -▁defe -▁County -▁cells -cast -▁broadcast -▁fly -▁fing -▁fingers -▁finger -▁fog -▁slavery -▁consideration -▁crime -▁cru -▁crucial -▁craw -▁geograph -agraph -▁paragraph -▁paral -▁paralle -▁parallel -▁dign -▁dignity -▁der -▁derived -▁cheap -▁doc -▁document -▁Texas -▁substitut -▁organic -▁ownership -▁discussions -▁Great -▁Greek -▁greek -▁diplomatic -▁supreme -▁Rome -▁181 -istol -▁Mary -▁mary -▁offices -▁nons -alfre -▁sewage -▁branch -▁regiment -▁regions -▁regret -▁camera -▁patri -▁Ne -▁Board -▁potato -▁unp -encil -▁pencil -▁bay -▁essay -▁bunk -axon -▁nonspecific -▁Fort -▁District -▁pian -▁please -iably -▁proport -▁sad -▁realistic -▁artistic -▁pup -▁pupil -enced -aneous -▁Lord -▁wilson -▁proof -▁examination -▁examined -▁examine -▁attitudes -▁charged -▁charges -▁mort -▁texas -▁stockhold -▁stir -▁secure -▁conceived -▁helpl -▁helpless -▁flux -▁comfortable -apping -happ -▁unhapp -▁remote -▁eager -ighter -▁ster -▁sustain -▁magic -▁wise -ogue -▁virtue -▁argue -▁Through -▁pour -ourney -▁journey -▁fired -▁spectacular -▁patent -▁component -imination -▁onto -▁experienced -▁delic -▁shooting -▁defeat -▁divided -▁Eisenhower -▁sports -▁explos -▁prohib -mon -stair -▁invar -▁invariably -▁Laos -▁Cub -▁Comp -▁characters -▁confident -▁rail -▁rig -▁rigid -roduct -▁distant -▁massachusett -asp -▁grasp -▁China -▁china -▁auto -▁ren -▁renew -▁reh -▁rehabil -▁rehabilitation -▁Le -▁League -▁continuing -▁hopes -ougl -▁tribut -▁dimension -▁dimensions -▁sufficiently -▁chinese -▁coach -▁sadd -▁steam -▁leather -▁liked -▁anxious -▁ecumen -▁ecumenical -tical -▁affairs -▁brook -▁contest -inomial -▁consciousness -▁observe -▁normally -wich -▁partners -nie -▁(1) -▁Nevertheless -▁glance -▁measurement -▁displace -▁adjustment -▁phon -▁furnit -▁furniture -▁pace -bor -▁harbor -▁harv -▁plaster -thur -orton -▁Philadelphia -▁philadelphia -▁28 -▁188 -▁29 -▁mobile -▁perception -els -▁"S -▁creation -▁destruct -ulous -▁temporary -▁Their -▁formal -areness -▁awareness -▁chron -▁patron -▁chose -▁chrom -boy -▁excite -▁Mike -als -alifornia -▁California -▁logical -▁caf -▁tact --time -▁imitation -▁sweat -▁guilt -▁portra -▁"p -▁"A -▁"f -▁centers -▁bride -▁cav -▁crop -▁dial -▁volumes -employ -▁plent -alian -▁empir -▁prime -▁dynam -assachusett -ics -▁medic -▁subjects -▁April -▁marriages -▁pond -▁follows -andom -▁random -ster -▁myster -▁splend -▁split -▁frustr -▁Soc -▁Society -▁Sen -▁participation -▁frequent -▁seed -▁continually -▁weekend -▁specim -▁publication -▁cabinet -▁enforce -▁retail -▁exclusively -in' -▁troubled -▁troubles -▁Industr -▁India -▁customers -bur -burgh -▁determination -▁fifth -▁losses -▁Manchester -▁Arthur -▁Class -erences -▁conferences -▁ambiguous -▁directed -▁uncle -▁parad -▁arthur -▁advertising -outheast -▁declared -▁newspapers -adequate -▁organized -▁convinced -▁pulmon -▁leap -▁scattered -▁cars -▁Figure -▁latest -▁Prot -▁Protest -▁earnest -▁dependent -▁vent -▁substance -▁St. -▁increasingly -▁rarely -▁August -▁items -▁parts -▁binomial -▁acquired -▁talents -ys -▁metaphys -▁Army -itated -▁irres -▁heaven -olly -▁wholly -▁freight -▁irre -▁sanction -▁starting -▁mo -▁denomin -▁existing -uthentic -▁joined -▁plenty -▁Joseph -▁joseph -▁challeng -▁specified -70 -▁Penn -▁blanket -▁altogether -▁furnished -▁Luc -▁revenue -▁revenues -▁categories -▁informed -▁acquire -inda -▁pistol -▁Ear -▁temple -▁yards -▁estimates -▁roar -▁greatly -▁pushed -▁complicated -▁decades -▁steady -▁steadily -ickey -▁congo -▁paradox -▁subsequent -▁sovereignty -▁turning -▁dragg -wick -▁lew -▁Sw -▁tempt -▁chick -▁striking -loride -quest -▁wages -anta -lanta -▁antis -▁temporarily -▁Paris -load -▁bull -▁comput -▁imagin -▁tail -▁Charl --up -▁changing -▁sentence -▁innocence -▁consequence -▁consequently -▁exceed -▁Wil -▁Wilson -▁watson -▁hence -natur -▁forty -▁forth -▁arbitr -▁frames -▁france -▁contempt -▁contempl -tric -ologist -▁species -▁Under -▁threatening -▁threatened -third -▁Morgan -▁Administration -▁phenomenon -▁thoroughly -▁dirty -▁ultimately -▁mysterious -acious -▁handle -▁Why -angel -▁historian -do -▁outdo -▁argued -ino -inois -▁fallout -▁occurrence -herent -▁ju -▁flexible -▁allot -▁allotment -▁complement -▁entertainment -▁excitement -▁guilty -type -cc -▁obsc -▁frightened -▁bigger -▁lumber -▁exclusive -▁Sam -▁75 -▁attain -oven -▁slipped -▁tur -ese -▁Japanese -▁japanese -oe -▁fault -▁corrupt -▁raw -▁rubb -▁diagon -▁McC -kins -▁wal -▁category -▁Protestant -▁client -not -▁brid -▁scarce -▁skirt -▁senate -▁meanwhile -▁row -▁bronchial -▁congr -▁Three -▁convenient -▁apparat -▁apparatus -▁campus -▁mike -▁challen -▁challenge --class -▁drama -▁printed -ias -▁policem -▁interpreted -▁appreciation -▁advoc -▁combat -▁ble -▁outer -▁asking -ommun -▁slave -▁earliest -under -▁accomm -▁Edward -▁award -▁edward -▁finance -▁antagon -▁holding -▁elaborate -low -pection -▁produces -▁decline -▁Monday -▁monday -▁Me -▁cafe -▁shows -▁uns -▁saddle -▁Shakespeare -▁farther -yearold -▁bother -▁script -▁eisenhower -▁movie -▁tremb -▁fascin -▁balanced -line -▁contained -▁extract -▁laos -▁killed -▁assault -▁senator -▁housing -▁rifles -▁roles -▁bag -▁scot -▁beat -▁bell -▁ways -▁capabilities -▁probabilities -▁inspection -plane -▁counterpart -▁Fin -▁Finally -▁reluct -▁martin -▁Martin -▁releas -▁appreciate -grad -▁duties -▁metropolit -▁aboard -▁frequency -▁requirement -▁perspective -▁belt -▁demonstrated -20 -▁Out -▁trembling -sewhere -▁elsewhere -▁suffering -▁politics -▁excit -▁excited -▁him, -▁survive -▁knows -▁micros -▁inspect -▁developing -▁verb -▁literally -▁pursuant -▁cigarette -▁indian -aulk -aulkner -▁Paul -▁paul -▁divor -▁divorce -▁divine -ceived -iece -▁aside -▁discipline -▁encouraged -▁stom -▁stomach -▁ward -▁till -errill -▁guerrill -▁pill -▁assigned -▁divers -▁drivers -▁don -▁nick -▁sections -opp -▁faced -▁regime -er's -▁code -▁drum -▁accum -umn -▁collective -▁reports -sues -▁issues -▁compuls -▁describe -▁Junior -▁1952 -▁1950 -/2 --to -▁modest -▁february -▁destructive -▁buildings -▁standards -sylvania -▁Pennsylvania -▁pennsylvania -▁vigorous -▁devil -▁lewis -▁prevention -▁revol -▁deter -▁deterg -▁stared -▁Cap -▁scrap -▁eighteenth -▁excuse -antle -▁assistant -▁medicine -▁ethical -▁muse -▁museum -▁rome -▁anger -olecular -ensional -evens -agner -▁38 -keep -▁dish -▁bow -▁ow -▁pictures -▁tomorrow -▁hopeless -▁metropolitan -▁pretend -▁judgments -▁piano -▁frozen -▁crouch -▁processes -▁contrad -shop -▁jones -▁charl -▁abstraction -▁absent -▁innocent -▁punish -▁tun -▁glanced -▁transm -▁ax -▁axis -▁restor -▁forget -▁sinc -▁submitted -▁immort -▁immortality -▁immigr -▁milligr -▁compre -▁compreh -▁comprehensive -▁sympathy -mith -▁Smith -ishop -▁automob -▁automobiles -▁missiles -▁patriot -▁constantly -▁mine -▁Germany -▁enforced -▁experimental -▁High -whel -whelm -▁overwhelm -▁firmly -ingly -ing, -estival -▁flower -power -▁widow -▁David -▁david -▁thread -pread -▁roman -▁Wood -▁individuals -▁diss -▁horn -▁hide -▁hamm -▁strictly -▁crown -▁samples -▁qualified -▁mixt -▁mixture -▁appr -▁Cr -▁du -▁excessive -▁distress -case -▁extended --t -▁Art -▁paus -▁teen -▁georgia -▁outlook -▁desires -▁relieved -▁Francisco -▁exhibition -▁engagement -▁procedures -▁magnificent -▁"M -▁lung -▁speeches -▁carp -pired -▁inspired -▁fiber -▁accommod -▁boil -half -▁complaint -▁assure -▁disaster -▁regulations -▁objects -▁nut -utch -dr -ren -▁Centr -▁charter -▁curios -▁Senate -▁variation -▁legs -▁happiness -▁physiological -▁illumin -▁disch -uccess -▁quarters -▁Dallas -▁Du -▁projects -▁palace -▁sigh -ighth -inson -▁stepped -ruit -▁recruit -▁Furthermore -aution -cence -▁adolescence -▁Civil -oil -▁substitute -stitute -▁morph -▁mistaken -▁greet -▁fur -▁smiled -▁amaz -▁employee -▁troops -▁motors -iors -▁gest -▁gesture -▁surprised -▁discovery -▁faint -▁restraint -▁showing -▁dull -▁statistic -▁admit -ait -illy -▁ceil -▁represented -▁engineering -▁timber -ashed -▁devoted -▁guests -inn -awn -natural -▁publish -▁male -▁monk -▁deserv -▁motiv --range -esus -▁Jesus -▁jesus -▁dallas -▁lips -▁stup -▁stupid -velt -oosevelt -▁contend -▁1956 -▁depression -▁damn -▁damage -arn -▁bridges -▁claims -mer -ccord -▁annivers -icit -▁explicit -▁compens -▁Part -▁iod -▁Robinson -▁offers -▁Thompson -▁seeing -▁clerg -▁clock -▁retreat -▁sept -▁Lew -▁Lewis -itches -▁switches -itarian -▁intervention -▁borrow -▁Alexander -▁alexander -▁wealth -▁monop -▁bench -▁hudson -▁gallery -▁cy -▁cycle -▁harmony -▁symphony -▁favorable -after -irk -▁bask -ki -▁sir -▁orient -▁secular -▁workshop -edd -▁wedd -de -uster -kin -ousin -▁cousin -inister -▁administer -▁conjunction -ugged -▁intended -▁trained -wide -▁grinn -▁referred -ation. -▁hone -▁germany -▁My -▁crimin -ips -ifies -▁spoken -▁signs -book -▁naked -odge -▁forest -esh -▁flesh -▁enact -▁guar -▁guarant -▁Pol -leton -▁gang -▁incorp -▁suggestion -▁circular -agues -▁colleagues -▁imagined -▁Perhaps -▁calls -▁eastern -▁appointment -▁delicate -▁Boston -▁juniors -▁rebell -▁Again -▁lux -▁"H -▁trag -▁tragic -▁comprom -▁pursue -▁Joe -▁January -▁january -▁lawyer -oubted -▁undoubted -▁ton -▁tape -▁Central -▁slid -▁lovely -▁Marsh -▁Bur -▁ideological -▁cos -▁cosm -asm -▁enthusiasm -▁cash -mond -▁fib -▁bureau -▁boston -▁conducted -▁scope -▁operational -ester -▁apportion -▁34 -▁mathematic -▁talked -▁hoped -▁arranged -▁enjoyed -▁explained -▁wondered -▁hurry -▁phases -▁climbed -▁invention -▁washing -▁European -▁buzz -▁satisfy -▁interests -▁presented -▁Yank -▁prescrib -▁wearing -▁desert -▁considerations -▁communications -▁gentleman -ographic -▁symbolic -▁burning -▁formed -▁adapt -▁q -▁alarm -▁reliable -▁Brook -▁recip -▁recons -▁individually -▁supervision -▁concentrated -▁500 -▁spray -ontin -col -▁treasury -▁restrictions -vail -vailability -orth -worth -wers -war -▁flew -▁outdoor -ctober -▁October -▁unpleas -▁cowboy -▁Section -▁Instead -ilton -▁musician -▁Spe -▁mortal -▁upward -▁tonight -utumn -▁stuff -▁gardens -▁radar -▁laws -▁antib -▁confused -▁hyster -▁oxid -▁subsid -pay -▁taxpay -▁assembly -▁pulmonary -▁veteran -▁Like -▁oct -▁european -▁"C -▁officials -▁Moscow -▁moscow -▁thermal -▁copy -▁invited -▁186 -▁images -▁Well -ictor -▁sle -▁slept -▁pursuit -▁relatives -▁envelop -▁suspicion -▁unfortunately -▁astronom -▁scheme -▁transformation -▁loans -▁equation -▁Prokofieff -▁Mississippi -ensity -▁density -swers -▁answers -▁pointed -occ -occup -▁preoccup -▁Sup -▁embr -▁inquir -▁exposure -▁cease -astern -▁stern -▁furthermore -▁refuge -▁weigh -lic -▁2:3 -▁sampling -▁ambass -▁ambassad -▁"c -▁"m -▁sensit -▁sensitivity -▁Constitution -▁continuously -▁india -▁indiff -▁X -▁compil -▁briefly -▁sale -▁saline -▁explo -▁nationalism -omfort -anley -▁detergent -▁shoulder -▁powder -▁assurance -▁partnership -▁confess --inch -▁practic -rica -▁precious -anches -▁branches -▁"B -▁constr -▁keeping -▁obser -▁observer -▁velocity -▁walls -▁haz -▁hazard -▁conception -▁enl -▁movies -▁protestant -▁glob -▁robinson -▁nail -▁vacu -▁vacuum -▁curiosity -▁craz -▁clever -alog -▁Pal -▁Palmer -▁massive --five --foot -▁rib -▁ride -▁hurried -▁stands -tail -▁cocktail -▁racial -▁self- -▁assumpt -▁assumptions -▁basket -eth -abeth -izabeth -eep -▁core -▁dialect -▁engineers -▁commod -▁commodities -▁plug -▁plow -▁glow -▁await -▁francisco -▁controvers -atsu -atsuo -issance -▁discr -▁discrimination -▁scheduled -truments -▁instruments -▁Str -▁luxury -bury -jury -▁voluntary -aissance -alfrey -▁moist -▁suitcase -▁suitable -▁mirac -▁mirr -isters -▁uses -▁institut -▁corporations -▁whis -▁whisper -▁lieut -uthors -▁ruth -▁mirror -▁substances -▁citizen -▁lieuten -▁admir -▁harr -ito -ymn -▁Tuesday -▁tuesday -▁whenever -▁dwell -▁cycl -▁fishing -▁ur -▁urge -rence -▁quietly -agnetic -▁forgotten -▁diseases -▁nervous -▁annoy -▁"L -auc -▁democrats -▁artific -▁prokofieff -▁impul -▁varying -▁Reg -▁refrigerator -▁discharge -▁terror -▁Green -▁Nothing -fred -▁alfred -▁Ann -▁democracy -▁archae -ised -▁granted -▁twentie -▁twentieth -▁eccent -▁eccentric -▁tent -▁resent -▁$3 -▁1953 -osis -▁opposed -▁Educ -▁submarine -▁substr -▁brick -▁oppon -▁discret -▁prol -▁Po -▁spokes -▁wander -ifying -▁loved -▁nomin -▁partner -onn -▁horizon -ored -▁compare -▁rehear -aging -▁tap -▁ple -▁Office -▁allowances -▁repeat -▁Alfred -thlet -icense -▁license -▁od -weight -▁35 -▁2, -▁collap -▁wright -▁arrival -▁mississippi -oster -▁O' -▁Fe -▁February -▁inventory -▁philosophical -andler -▁brood --1/2 -ometric -▁intim -timore -ana -aded -▁persuaded -adjust -ado -mark -astroph -▁catastroph -ounced -ternational -▁comments -▁comed -▁sketch -och -▁Congo -▁nodded -▁handling -▁orbit -▁Little -▁Public -cription -ij -▁characteristics -▁emphasize -▁draft -▁dried -▁drift -▁Tre -asy -▁thyrogl -▁Angl -▁epic -icial -▁appreh -▁Atlantic -▁refund -ider -▁Jess -▁sincere -▁correl -oons -▁effectively -▁hungry -▁glimp -gradu -▁transcend -▁classic -conomic -away -awk -▁walking -▁milligram -▁graduate -▁moves -ulpt -▁sculpt -▁Social -▁documents -▁polar -▁verse -▁prel -▁prelimin -▁preliminary -▁analyz -▁analytic -▁Accord -▁mathemat -▁mathematical -▁phenomena -▁cream -▁revision -▁looks -▁farmers -▁spark -▁assembled -allow -▁swallow -▁traveled -▁Gl -set -▁submarines -▁visitors -ady -▁gear -▁basement -▁(the -▁muscles -▁delightful -▁van -othing -▁begins -▁Massachusett -▁barely -▁Ab -▁dogs -▁fields -▁giant -▁denot -▁curtain -▁spell -25 -▁(195 -level -▁interm -▁reporter -▁contributed -▁squeez -ivery -▁delivery -▁execution -rec -▁shoulders -▁Place -▁ban -antry -▁Republican -▁bat -▁splendid -▁examin -▁tick -▁procure -▁Lincoln -▁lincoln -▁36 -▁Rayburn -▁rayburn -▁details -▁detailed -▁poison -▁eleg -cut -▁continues -▁marginal -▁classical -▁melody -▁dimin -▁sophisticated -iations -▁negotiations -▁advances -▁breast -▁displacement -▁disappoint -▁intelligent -▁spont -▁golden -▁mature -▁literal -▁alternatives -▁Labor -▁blame -▁lieutenant -▁trick -▁retirement -▁man's -▁shelters -▁neares -▁reputation -▁paris -▁37 -▁deput -▁microsc -▁optical -▁reass -▁encouraging -▁continuity -▁Gu -▁sketches -ibald -iative -itiative -▁bi -itative -▁Law -▁Lawrence -▁correspondence -▁accelerometer -▁ru -ournament -▁africa -▁boost -▁respir -▁respiratory -▁whir -▁collected -▁resign -▁resolved -achloride -▁decomp -▁adopt -▁undert -▁undertaken -▁outstanding -▁tied -▁oclock -▁prize -▁observation -▁oxidation -onym -▁Pres -▁Wright -▁Warr -▁Warren -▁Play -▁Plato -▁diver -▁Prince -▁scientist -▁consumption -▁thereby -ichmann -▁smok -▁chicken -▁employed -▁legislators -▁rabb -▁anyway -tock -▁livestock -▁deck -▁flying -▁elev -▁limitations -sued -▁issued -▁helped -▁suffered -aude -▁1, -▁meat -▁birds -▁Jr -▁Jr. -▁Day -▁contour -▁Scotty -▁scotty -chang -▁sang -▁patience -▁eighth -▁pathology -▁Ge -▁Georgia -▁promote -▁promotion -▁convenience -▁mounted -▁rolled -▁narrative -▁tentative -water -▁"w -thodont -▁debate -▁kingdom -▁jess -itual -▁ritual -▁mutual -▁viewpoint -▁clarity -active -▁pen -oly -ico -▁poetic -▁purely -▁deeply -ounce -▁mob -▁depress -atson -▁Watson -▁Earth -▁partially -▁officially -▁Can -▁helpful -▁glad -▁tribute -▁associations -▁Hard -▁trustees -▁eleph -▁Six -▁holy -ruff -▁hypothalamic -▁worn -roke -▁factory -▁violent -▁settlement -▁morphoph -osevelt -▁roosevelt -▁cultiv -▁conceiv -y, -trachloride -▁tetrachloride -▁wrink -▁corresponding -▁controlled -▁thompson -olated -▁reorganization -▁print -▁wet -▁solved -inite -▁infinite -ubble -▁hatred -▁subordin -iter -itor -▁pit -▁discount --year- --year-old -▁supers -uring -▁morality -▁thyroglob -▁amendment -arton -aylor -▁merchants -▁cooperative -▁ghost -▁firms -▁civilian -▁dawn -▁empirical -▁conspir -▁merger -▁outcome -▁overcome -raux -▁abund -esday -nesday -ednesday -essions -▁sessions -▁bless -▁sake -▁reward -▁rein -▁reinfor -path -▁shopp -piece -▁regarded -▁Carl -▁stagger -▁reader -▁technological -▁ratios -▁compell -▁Charlie -▁Neither -▁compromise -▁lake -▁lies -▁enemies -▁candidate -▁beef -▁(wh -▁desperate -▁desperately -ai -awai -stery -▁mystery -▁isolated --J -▁reactivity -▁O'B -▁arise -isp -espread -▁widespread -▁disclos -ambling -▁indifference -▁Conference -▁portrait -▁supernatural -▁particle -▁climate -▁muscular -▁plays -▁midnight -▁disk -▁gradient -erald -▁qualifications -▁probable -▁consumer -▁peer -▁pepp -▁facts -▁emphasized -▁serves -▁Woodruff -▁towns -agr -▁diagr -alski -▁dealing -▁hypothalamus -▁til -▁mantle -▁Mantle -▁lawrence -▁immense -anche -▁containing -▁seeking -▁acting -▁Mercer -vis -▁swimming -▁1955 -▁producing -▁Uncle -mma -ilemma -▁dilemma -▁dig -▁ded -▁deduct -▁dedic -▁deduction -▁ridic -▁ridiculous -▁riding -▁scenes -ependence -▁fever -▁mercer -▁defect -▁infect -▁dear -▁tangent -rap -▁fant -▁fantastic -▁simpler -▁45 -▁400 -import -▁cooling -▁minimal -▁80 -▁flour -▁factories -▁cere -▁upstair -▁planetary -▁wholes -▁ordered -▁classroom -▁pages -▁maintained -▁stall -gre -▁degre -▁enroll -▁assessment -▁prince -odger -▁Jones -▁sacred -▁pron -▁communists -▁flexibility -bridge -▁festival -▁miriam -▁stamp -gregated -▁anticipated -▁disappointed -▁anniversary -▁noise -▁stere -asters -▁batter -▁curves -▁opera -▁operated -▁interaction -▁economical -▁Stud -▁Stanley -▁Tom -▁greg -▁diplomacy -▁1920 -▁volt -▁debt -ubber -▁punishment -▁tube -pire -ards -▁arrow -owalski -▁salvation -▁champion -▁concess -emic -eman -▁worries -▁raising -▁gloom -▁implications -▁variations -▁letting -▁evolution -▁Revolution -▁obey -▁"s -▁Italian --sh -▁Nav -▁"b -▁stores -▁stored -▁saved -cape -▁classification -▁reluctant -▁responses -▁theme -▁gate -▁depos -▁Division -▁mortg -▁Wal -▁parent -▁tapp -cknowledge -▁decay -▁decis -iolet -▁originally -▁chemistry -▁phrases -▁Spanish -▁utterly -▁diet -▁enab -▁recommended -▁marble -▁prolong -▁savage -▁elementary -▁arbitrary -▁mild -▁mc -▁clin -▁clinical -▁Canad -adium -▁surely -▁nest -▁fascinating -▁occasions -▁fluores -ifting -▁aggl -▁ages -▁debut -▁About -▁crystall -▁Peter -▁Leg -▁dancing -▁dancers -▁aj -▁warrant -▁warren -▁specimen -▁deeper -▁Jackson -▁numerical -▁jr -▁jaw -▁partly -▁warmth -▁ol -▁feelings -▁swell -▁songs -▁animals -▁struggling -▁intu -▁intuition -▁proportional -▁exceptional -▁congressional -▁panels -▁jobs -▁indign -▁barriers -overty -▁poverty -▁loyalty -▁financed -▁pump -fall -▁Juniors -▁vess -▁levels -▁Te -voc -▁projection -▁cluster -▁corr -▁Sometimes -▁Res -▁suited -▁suck -▁Pap -▁Papa -about -coming -▁yarn -▁tank -aun -▁Mich -▁mich -▁mud -▁Journal -fulness -▁soften -▁stanley -▁fence -aptist -▁ampl -▁manchester -▁dread -▁agriculture -rical -rican -exican -▁sphere -▁nowhere -▁enclos -clined -clock -'clock -▁o'clock -▁wool -▁Resear -▁nu -▁nutr -▁readers -▁Harr -▁warn -▁flavor -▁guy -lete -▁incomplete -leans -▁Utop -▁spraw -▁surge -▁dissolved -▁constructed -▁supported -▁supper -▁rival -▁smashed -itious -▁ambitious -▁prej -▁prejud -▁hal -iscons -▁1000 -mont -▁pont -▁Pont -ontrol -▁lever -▁peak -agree -▁disagree -reeze -▁boats -▁Supp -yr -▁recommendations -▁productivity -▁unfortunate -▁mechanics -▁colonial -▁scrut -▁"N -▁touched -▁shoes -▁charlie -▁fibers -▁Blue -ermans -▁converted -▁convert -▁foil -▁realization -▁disappro -▁micro -▁microm -rot -▁humble -▁squad -▁Moreover, -▁movable -▁serving -ining -▁dining -inter -torney -▁Attorney -▁shakespeare -▁revealed -▁evaluation -12 -▁Air -iat -▁whereas -▁trace -▁endless -▁shell -▁sheet -cknowled -▁hes -▁hesitated -▁ham -▁managed -▁managers -▁snatch -▁dispatch -▁sink -▁sor -▁sorry -▁Institute -▁monopoly -▁revolutionary -▁improving -▁folkl -▁folklore -lers -▁builders -ging -▁fallen -igible -▁tangible -▁leaned -▁Judge -▁bund -▁exposed -▁long-range -▁purchases -▁discre -▁Actually -▁gradual -▁Seven -▁Youn -▁indisp -▁generous -olina -ologists -rost -ciously -held -▁iv -▁om -rapist -▁unexpected -▁shared -▁confession -▁snakes -▁transmission -▁pronounced -▁Oh -▁Ohio -▁Over -▁shade -▁consistent -▁climax -▁Senator -▁harsh -▁Greg -▁speaker -▁speaking -▁marketing -▁Justice -▁vit -▁ven -▁via -athode -▁stockholders -▁Suprem -▁holes -▁whip -▁arrest -▁secondary -▁insistence --day -▁denial -ouglas -▁lover -▁Hel -▁Pat -▁pet -front -▁manufacture -▁Chief -▁fountain -▁praise -▁analog -▁demands -▁clothing -▁drinking -▁bind -▁consistency -esthetic -ises -▁Assemb -▁marvel -▁committees -▁adjustments -▁stereot -▁boating -astro -▁tests -▁propaganda -▁Govern -▁republican -▁waste -▁burned -▁arres -▁arrested -▁sitter -▁cott -▁"d -▁socially -fur -▁frankfur -▁Bank -▁governments -▁volunteers -▁ure -▁ureth -▁urethane -▁coverage -▁sprink -▁staining -▁illness -▁illinois -llinois -▁infantry -▁wants -▁elections -▁sharply -▁grim -▁grabb -▁ignore -asion -▁helium -▁pottery -▁generations -▁woodruff -iring -▁tractor -▁justify -▁remarks -▁Sec -▁Second -efficient -▁psychiat -▁joe -ghman -▁discip -▁news -▁roads -▁towards -▁mutter -▁Cons -▁Consider -▁undergradu -▁procurement -ughes -▁dancer -▁dictators -▁Hill -▁Have -▁brave -▁"G -▁arous -onymous -▁seized -▁Research -▁aim -lim -imes -▁loom -▁nearest -▁frontier -▁august -▁april -▁Any -▁"D -uman -▁imper -▁imperson -▁resource -▁registration -▁traders -▁trap -▁formulas -▁prayer -▁thyroglobul -▁Anne -▁robb -▁cried -▁financing -ings -▁measuring -▁remaining -▁determining -▁minimize -▁carries -▁survived -inning -unning -▁cabin -▁ohio -▁styles -▁hypocr -▁reaching -▁moderate -▁incorpor -▁provisions -▁negroes -car -▁shear -▁1946 -▁vic -▁vicious -▁indef -▁plants -▁map -▁lap -▁pig -ecast -▁self-c -oir -▁Cur -cer -▁lying -▁mixed -▁ivory -trous -▁bulk -▁demonstrate -▁optimal -olester -olesterol -▁cholesterol -▁90 -▁fragment -▁mont -TO -ATO -▁substrate -ateur -▁amateur -▁interrupted -▁angular -▁brass -▁arrive -▁decisive -▁bone -▁torn -ixon -▁deserted -romwell -▁precaution -▁productive -▁Corps -▁nurs -sis -▁hypothesis -thesis -▁seventeenth -▁wre -▁vector -▁defensive -▁inner -▁Atlanta -▁impat -▁impatient -▁glued -▁announcement -▁Hospital -▁invariant -izzie -▁autonom -▁indefinite -▁resc -▁160 -▁colonel -obbie -apol -apolis -▁time. -▁time, -▁eating -▁jackson -▁resort -▁airport -▁rights -▁compress -ystop -▁(which -▁actions -elangel -▁evangel -elangelo -▁motel -▁motives -▁investigations -▁fleet -▁motive -onies -▁streak -▁fortunate -▁fortune -▁latin -▁composed -▁succeeded -▁taxpayers -ymnastic -chieving -▁ell -▁relevant -fire -▁tire -▁pleased -▁messages -▁bathroom -▁impressions -▁cottage --P -▁"P -▁synthesis -▁absorption -▁Bel -orse -oral -▁naval -▁electricity -intend -▁superintend -ending -▁badly -▁painful -▁doubtful -▁patterns -▁conspiracy -face -▁Def -▁Defense -▁discontin -▁accordance -▁une -▁uneasy -▁scrutiny -iana -▁louisiana -▁anti-S -▁empire -▁decisions -▁windows -▁carpet -▁dil -▁stumbled -throp -owned -▁promised -▁seal --of --term -▁Rachel -▁rachel -▁quack -▁friction -▁thunder -▁lecture -▁dar -▁refresh -▁thresh -▁"O -▁effl -▁efflu -▁effluent -agu -▁185 -▁midst -iazz -▁eliminate --like -▁Mont -neg -▁passages -▁Still -kill -▁sets -▁brothers -seud -▁pseud -▁aud -▁audiences -verse --A -▁overall -▁(S -▁eliminated -▁applause -▁perfectly -imil -▁define -▁ille -▁telephon -annon -anny -▁armed -group -▁privilege -▁superb -▁influenced -▁influential -▁Er -▁surprising -▁meadow -▁congregations -▁taxes -▁thousands -lined -▁180 -▁183 -▁vanished -▁conversion -▁Without -va -ugene -▁inhib -▁Helva -▁parlor -▁parade -▁Gar -▁wart -▁smart -ieve -▁cellar -▁encouragement -10 -60 -,000,000 -▁spending -ept -▁skept -▁mortgage -▁obscure -iph -▁proph -▁intensive -▁implied -▁regarding -▁1951 -▁replacement -▁marks -eele -vely -velyan -▁Trevelyan -▁Treas -▁Treasury -▁assessors -▁Your -gest -▁biggest -▁Bible -▁opium -▁arter -▁filter -▁participate -imensional -five -▁navy -▁predisp -iament -liament -▁parliament -eal -▁zeal -free -▁Time -▁emper -▁Thus, -▁afterward -▁bang -▁submit -ita -itter -▁tea -▁sacrific -▁interrel -ume -▁editors -▁underground -▁captured -▁capture -▁stroke -▁Elect -▁caution -▁dealers -▁thirteen -▁Carleton -▁spectra -▁blonde -develop -▁Develop -▁border -▁Sherman -▁wreck -▁Roosevelt -range -▁longrange -▁Fig -▁insure -▁recovery -▁diagram -nut -▁entries -▁jail -▁transferred -exico -▁obligations -mas -▁discern -▁forever -▁distances -oh -uddh -itchell -▁accustom -▁Linda -▁linda -▁spontaneous -ufficient -▁sober -▁Chap -▁ros -▁possession -▁blast -▁domination -▁reflected -▁omitted -rok -▁buried -▁historic -▁historians -▁whites -▁Cool -▁Castro -▁northwest -▁virtues -▁spanish -▁refer -▁unb -▁delivered -▁considering -ulative -▁Inde -▁Indeed, -▁devotion -▁gently -▁Maybe -▁servant -▁servants -▁younger -▁mete -▁meteor -▁silently -▁presentation -▁connected -▁crossed -▁Found -acing -▁mac -▁mur -urs -ursday -▁birthday -urface -▁seasons -▁disapproval -ieval -▁medieval -▁grants -▁southeast -▁stimulation -▁administrator -▁detach -▁puzzled -▁clutch -▁rescue -▁ing -▁ingred -▁ingredient -▁depends -amin -▁adolescent -▁perp -▁perpet -kansas -▁franc -umbling -ubborn -▁gentlemen -▁Steele -▁Ste -▁surviving -▁joke -clam -▁proclam -▁proto -▁Coolidge -▁essence -▁roleplay -oday -▁Today -aymond -▁juven -▁singing -▁Prof -▁Nations -▁applications -aven -aved -avis -▁rice -▁proposition -▁width -▁fact, -▁disord -▁startling -▁haun -▁consol -▁consolid -olve -▁Curt -▁colt -▁hawk -▁hate -▁improvements -▁frightening -▁Jefferson -▁deprec -▁depreciation -▁thigh -▁Direct -aine -▁suspended -▁hired -quired -▁admired -▁facing -elix -justice -▁uncertainty -▁strongly -▁reducing -▁bol -olores -▁solve -▁foolish -▁artificial -▁repetition -▁reply -▁pause -▁ambigu -iful -▁unemploy -▁devised -▁slender -▁acid -ifier -▁acquis -▁acquisition -▁measures -▁predisposition -imate -itimate -▁legitimate -▁bowl -▁ships -▁lands -▁landscape -▁bundle -▁reservoir -▁Victor -▁crystal -▁gal -▁applies -▁companion -▁associate -▁barbec -▁fert -▁healthy -▁palmer -ered -erem -▁cerem -▁ceremony -▁ceremonies -▁insisted -▁shield -▁381 --four -▁inadequate -▁province -▁(B -▁situated -▁dominated -▁stable -▁imaginary -▁imaginative -▁traveling -ework -▁casework -▁comedy -▁sanct -▁percept -▁signed -▁vegetables -▁guit -▁guitar -▁hesit -▁San -▁confined -▁tactics -▁demonstration -▁*z -▁monitor -▁ion -▁uncomfort -▁jumped -▁ribbon -▁assignments -▁dictatorship -▁meals -▁kitt -▁decomposition -▁shapes -▁honey -▁jerk -▁realism -▁perfection -itary -▁publishing -▁stuck -▁gained -▁sixth -▁chuck -▁exciting -atis -atisf -▁dissatisf -▁stronger -eration -▁exploration -▁prisoners -▁consistently -mother -▁grandmother -lemn -▁solemn -▁rack -▁defined -alled -▁socalled --called -▁receiving -▁sleeping -going -▁explod -▁Good -▁distinctive -▁accompany -▁nineteen -▁dates -▁subsection -inqu -▁delinqu -▁relinqu -quare -▁Af, -izations -▁juvenile -▁initiative -▁arts -▁wives -▁tournament -kirk -ether -▁Whether -▁aristo -▁Blanche -▁streets -▁sounds -▁listening -▁seeds -roof -▁curric -▁curricul -▁sharpe -▁Clay -▁Clark -▁structured -ikk -dikk -dikkat -▁subd -▁discharg -▁enjoyment -▁Field -▁lift -▁embass -▁embod -▁fantasy -▁constitutional -▁infectious -▁trigger -▁pianist -▁references -▁acute -▁handled -▁param -▁paramagnetic -▁seam -▁sees -ream -▁Hardy -▁meaningful -▁peaceful -ocation -▁recommendation -jold -kjold -▁fold -▁bold -▁threshold -▁collar -▁bott -▁commonplace -necess -▁vertical -▁compass -▁chill -▁Bill -▁hills -▁colorful -▁trot -itol -ousse -ousseau -▁unpleasant -itism -emitism -▁anti-Semitism -▁enable -▁laughed -▁International -▁poured -nik -▁Business -▁bible -▁calling -▁contains -▁matters -▁detectives -▁impulse -iments -▁thumb -▁columb -▁non- -airie -▁prairie -▁reproduc -▁eligible -▁wines -▁unm -resent -▁Johnson -▁antisemitism -▁grav -chief -▁destroyed -ewise -▁likewise -▁adequately -ili -▁Port -▁escort -▁Sir -▁Spr -▁Oper -▁acceptable -▁invasion -▁cod -onic -▁filling -▁subdivision -▁misund -▁formation -draw -▁sixties -▁ended -acond -▁locations -▁straw -ainted -▁acquainted -▁judicial -rog -gu -▁1949 -▁basically -▁alb -otic -iced -▁noticed -▁cleaning -▁iodine -▁1927 -▁finds -▁stubborn -ighters -▁alumin -▁aluminum -thletic -▁32 -▁nurse -▁ordinarily -▁arose -▁1948 -▁microorgan -▁settlers -acco -bacco -coat -▁alco -▁alcoh -▁alcohol -▁*zg -ami -unami -▁ceiling -▁protein -iamond -▁suggestions -▁champions -▁chore -liness -▁reck -▁lesson -▁strikes -▁remarkably -▁doors -▁too, -▁42 --century -▁recorded -▁answered -▁asserted -alysis -mes -▁holmes -▁murm -▁amus -usk -▁premier -ouches -▁painter -▁hunter -▁chin -iami -iriam -▁softly -▁unhappy -▁dances -▁chances -▁plung -▁defens -▁segregated -▁wax -▁wednesday -▁Wednesday -uj -ujill -▁relieve -▁spectacle -▁Wagner -▁happily -▁crazy -▁illeg -▁seg -▁Belg -▁Captain -▁absolutely -▁trim -▁passengers -▁slope -▁Franklin -▁spoil -▁circles -▁Norther -▁pounds -▁reverse -alse -▁false -sey -▁oppose -▁dimensional -▁awake -▁invit -▁whit -▁shopping -▁People -▁determines -▁writes -▁retr -▁suite -▁1900 -▁corporate -▁wagner -▁bonds -▁grounds -ows -▁strang -▁stripped -▁crawled -▁backward -isconsin -ienn -ienna -▁Fre -atre -▁wake -itors -itable -▁constit -▁constitu -▁deviation -▁figured -▁prosecut -▁hearst -stream -▁Communists -▁optimistic -▁mutually -▁Represent -▁Stevens -▁dedication -▁dedicated -▁breakdown -▁lawyers -▁jung -▁inquiries -▁irrelev -▁headed -▁approached -▁formerly -▁mostly -▁cert -▁decent -▁clouds -▁curriculum -attan -hattan -▁batt -▁limits -▁composer -▁fluorescence -▁segregation -inal -▁vein -▁rejected -▁boast -▁dynamic -▁couns -▁counsel -▁counted -▁Thursday -▁thursday -▁scarcely -▁Aristotle -▁depending -▁multiplic -uplic -▁rup -▁rupees -▁microscop -▁victor -▁conductor -▁expanding -▁provin -▁Rec -▁depr -▁depict -▁engage -▁cure -aham -▁mah -▁concerns -iffith -▁Contin -▁sheets -ulose -▁migr -▁asleep -▁dystop -▁condens -▁Stein -oop -▁patients -▁ranging -▁"Wh -C. -.C. -▁(C -▁leaf -▁exped -▁expedition -▁alike -jo -▁Franc -80 -ctors -▁Greenwich -▁walnut -▁rendered -▁Avenue -▁spare -▁undoubtedly -▁wink -▁betray -▁Sal -▁vocal -▁TV -▁impart -▁choreograph -▁fuel -▁surviv -▁memorial -ymph -▁1945 -zym -▁enzym -▁honeym -ymen -▁honeymoon -▁declined -umm -▁thyroglobulin -▁salesmen -▁inserted -▁exerted -wr -▁drap -post -▁russians -▁caval -▁cavalry -▁unnecess -▁U.N. -▁statut -▁tor -▁promptly -▁openly -▁similarly -▁Que -▁sne -▁misunders -▁rubber -▁stirred -▁proportions -▁religions -▁foods -▁ens -▁flag -▁precinct -▁panic -▁angeles -▁ech -▁echo -▁stained -▁straightened -rael -▁clergy -▁attraction -▁christmas -▁Christmas -akers -▁cens -▁temper -▁unpre -aska -▁resid -▁vul -▁vulner -▁homog -▁homogene -ogan -▁voltage -▁Democrats -▁bent -▁jungle -ibaldi -ithe -▁Poland -▁crush -▁flush -▁crude -▁knight -▁presidential -orial -▁territorial -▁impressed -▁gathered -▁extending -▁laughter -▁installed -▁mistakes -▁disappointment -▁arriving -▁diagonal -▁diagonalizable -▁Long -▁concluded -▁boards -▁angl -▁engines -▁zer -▁zero -▁slamm -▁reson -▁ug -ommunist -rogen -▁stereoty -▁hidden -▁disl -▁dislik -▁carl -▁restriction -▁os -▁characterized -▁Governor -▁harvest -▁Legis -▁Legisl -▁Legislature -▁geographical -vin -▁Faulkner -▁urgent -▁urged -▁testament -▁stirr -▁damp -atang -▁grat -atom -omination -▁som -▁Com -▁willing -▁shining -▁again, -▁again. -er. -▁builder -▁contents --in -▁accent -cap -oli -▁tool -▁abol -▁streetcar -▁hundreds -yll -▁sull -▁bored -▁unions -▁convictions -▁exceptions -▁creature -▁venture -▁Samuel -▁samuel -▁implement -▁impr -▁minds -▁"a -▁influences -▁kohnstamm -▁Kohnstamm -ecut -▁persuade -build -▁symm -▁self-d -▁amounts -▁discouraged -▁Hot -▁Hotel -▁Henrietta -▁henrietta -▁embrace -▁apprent -▁apprentice -▁apprais -▁circumstance -emperature -▁exempt -▁startled -▁envelope -▁textiles -uther -oos -▁choos -BA -▁heavier -▁Cousin -▁Sever -▁Several -▁mans -▁ends -▁minority -▁diagnosis -▁yankees -▁fist -▁physically -▁receives -▁adap -▁atoms -▁Walter -▁Elizabeth -▁lamp -▁protected -▁"h -▁performed -▁encountered -▁resentment -▁fears -▁surprisingly -reece -▁cree -▁creep -▁bottles -▁discrimin -▁aggressive -▁authentic -▁lunche -▁luncheon -ildren -▁helping -lene -▁enforcement -anston -▁disgu -▁disarm -▁commonly -life -▁wildlife -▁Life -▁world. -▁renaissance -▁demographic -amey -▁franklin -anning -▁cafeter -▁walter -▁horizont -ontroll -▁era -▁curt -▁squeeze -urk -▁1. -oo -oan -▁shame -▁saint -▁auditor -▁spectr -uble -▁noble -▁anacond -▁hay -▁lb -▁lone -▁statue -three -▁Gross -▁ballistic -▁Pike -▁pike -▁pear -▁bubb -▁corrid -▁atlantic -unition -munition -▁ammunition -▁winston -▁strategy -▁stadium -▁stevens -▁astronomy -▁deserve -▁sponsored -ateful -▁grateful -▁controversy -▁foundation -▁sole -▁preceding -▁resulting -▁compete -▁competent -▁achievements -▁provincial -▁contradiction -▁contradict -▁Mark -▁plates -▁life. -state -▁chronic -▁behalf -▁summary -▁impatience -▁soci -▁Del -four -▁mask -▁masters -▁thru -▁lobby -▁haul -▁vert -▁vertex -▁Young -oultry -▁dy -▁dying -▁matching -▁knowing -▁anticipation -▁Square -▁drank -▁Mot -▁Motors -▁Mama -▁mama -▁indians -▁positions -▁poets -▁Who -▁blanche -▁bringing -▁glimpse -nessee -con -▁mainly -▁wildly -▁roughly -▁concerto -▁degrees -arewell -▁prospective -▁seemingly -icism -▁strategic -▁molecular -▁lemon -▁optimism -▁diagnost -▁washed -▁sanctuary -▁receiver -▁slide -▁Chandler -▁gambling -▁induc -▁helva -nic -▁ethnic -▁Harv -▁Harvard -▁annually -▁unre -uk -uter -▁computer -▁compr -▁compat -▁compatible -▁morphophon -▁hatch -▁runs -▁feels -▁massachusetts -▁confronted -▁fram -▁concurr -▁succession -ouri -▁Missouri -▁Four -▁notion -▁explosion -▁stereotype -▁Grand -▁Delaware -▁Carolina -▁Mill -with -alaur -alaurie -▁criminal -astically -▁mountains -▁functions -▁garage -proof -▁committed -▁rector -▁veterans -▁insol -▁insoluble -▁memories -▁adverse -▁femal -▁disposal -▁infr -roadway -▁crossroad -▁humorous -▁jefferson -ondition -▁Palfrey -▁prest -▁prestig -▁prestige -▁shipping -▁Miriam -▁holster -▁infection -▁terribly -▁necessitated -▁synthetic -▁A. -▁congregational -▁Inter -▁peter -▁shiver -▁thrill -▁manifold -▁intimate -▁Hol -▁Holmes -▁Hall -▁implies -▁criter -▁loop -▁bishop -▁fitted -▁$5 -▁recipient -▁prais -▁transom -abama -ading -▁singular -▁experts -▁fee -jured -▁papa -▁locking -▁installations -▁foster -▁insult -▁insert -▁cafeteria -▁resolve -▁tickets -▁titles -▁telling -▁teenag -▁consent -▁indispens -▁subsystem -▁System -▁harvard -▁functional -▁skilled -▁cited -▁cyn -▁caut -tons -▁tons -▁Industrial -▁evidently -▁arguments -▁(F -▁"F -▁Ruth -etical -oretical -▁metaphysical -aunt -limited -▁Step -▁relating -▁scholastic -▁package -▁filed -▁solitary -▁hostile -▁Next -▁educated -aturated -▁investigated -▁investigators -remlin -▁lonely -▁vigorously -▁analyst -writ -▁brightness -▁clamp -▁Independence -▁existed -▁exists -▁existential -▁brings -▁gets -▁(A) --wide -cellulose -▁petitioner -▁sunlight -▁deficit -▁wherever -aeus -▁separately -▁paintings -ways -▁payroll -▁Work -▁interrupt -aney -▁Much -▁elizabeth -▁anatom -▁wip -▁cuba -▁thanks -▁holds -▁Als -▁Also -▁vo -▁voters -▁pilgr -▁disciplined -▁invitation -adden -▁tappet -▁jet -▁closet -▁biography -▁Scott -▁appearances -conscious -▁competence -▁italian -▁strok -▁antibody -▁Africa -▁discrep -▁jews -▁skills -▁daytime -▁candle -▁Occ -▁greenwich -▁sandwich -na -a. -lyn -▁Brooklyn -▁brooklyn -▁seventh -▁expenditure -ureauc -▁departure -▁adventure -▁$4 -▁184 -▁Certain -certain -rim -▁Add -▁them, -ation, -western -owulf -▁cruelty -▁solely -▁rent -inent -tinent -▁pertinent -abri -▁ruled -▁punct -▁persistent -▁carpent -▁capita -▁null -▁gathering -▁unlikely -▁covering -ete -▁micromete -▁85 -▁800 -▁600 -▁serum -▁thre -▁dean -auce -▁sauce -▁benefits -▁Super -▁customer -erto -uerto -▁barbecue -▁expectations -▁O'Ban -▁O'Banion -▁ambassador -▁indication -▁politician -▁Garibaldi -white -▁novelist -▁wrapped -▁Columb -▁instructions -▁long-term -▁verd -▁verdict -▁indict -▁indul -▁undertake -▁Lake -▁seated -▁blunt -▁physician -▁players -▁fees -▁feas -▁feasible -▁boundaries -▁carleton -▁creating -ing. -▁exert -agged --and --and- -▁poland -▁operand -▁operates -▁co- -▁co-op -shire -bl -▁preferred -▁attended -lines -▁implicit -▁qualify -▁clarify -▁tracing -▁selling -▁priority -▁Michelangelo -▁michelangelo -▁biological -▁lots -antly -▁merchant -▁awful -▁provinces -▁demanded -omery -gomery -▁views -iciency -▁receipt -▁spokesmen -gers -tenna -▁agglut -▁endurance -▁gymnastic -▁representing -▁sermon -▁reactions -▁Rock -▁believing -▁belg -▁released -▁overnight -itrogen -▁chew -▁flames -▁orthodont -▁ench -▁enchant -▁Dartmouth -▁dartmouth -▁dot -▁periods -orter -▁centrif -▁ideology -▁reapp -▁revel -tection -▁detection -otte -▁cha -▁chaos -▁glory -▁advisory -▁dug -▁dash -▁disappeared -▁useless -▁scenery -▁opponents -▁opponent -▁65 -▁knocked -▁boss -▁bast -▁ben -▁bush -▁bark -ierce -▁exploit -▁Clayton -▁crises --to- -▁politicians -iac -acent -▁adjacent -acqu -iating -ulating -resco -▁sharing -▁vag -▁Hamm -ean -plan -expl -▁semi -▁neatly -ani -aniel -▁revival -▁revised -▁Morse -▁grades -ourke -▁Louisiana -▁fort -▁bombers -▁bombs -▁listed -aughters -▁polite -▁unlock -▁paused -▁utopian -inth -▁combin -▁combinations -ublin -▁Asia -▁irish -▁stairs -ureaucr -▁Poet -▁gaz -▁planets -▁motif -▁newly -▁secants -▁parame -▁paras -vas -iliar -▁pilgrim -▁voted -▁perfume -▁Something -▁wings -▁J. -▁superintendent -▁rebels -▁rebuild -▁heroic -itially -▁Profess -▁Program -▁sending -▁voting -▁Almost -▁Albert -▁ethics -abasc -thabasc -▁psychology -▁psychoan -iazza -▁phosphor -▁evid -▁evidenced -ifth -▁placing -▁consisting -▁visiting -▁unload -imilar -▁assimil -▁postwar -ometers -▁lovel -▁1910 -▁1912 -▁spontaneously -▁marshall -▁concession -▁enlist -▁forecast -▁delaware --Saxon -hes -▁cohes -▁votes -▁Turn -▁sprang -▁Flor -▁watercol -▁year. -▁continuation -▁systematic -▁chapters -ampshire -▁bills -▁resonance -▁stake -▁sturd -▁mortar -▁Tele -▁attempts -▁crest -▁apprehension -▁dialys -acle -▁abuse -▁abilities -▁tip -▁precip -rad -▁dividend -step -▁sheep -▁unaware -▁etc. -▁abundance -▁diamond -▁duplic -▁dos -vers -▁squat -▁squares -▁applicable -▁painfully -▁cement -▁ceas -▁vibr -▁solo -▁accumulation -▁circulation -▁alienation -▁Economic -▁privacy -▁transformed -▁nerves -▁strengthe -▁strengthening -▁uncon -▁ignored -▁ignorance -ibl -iblical -▁models -oz -cred -credible -▁authors -▁goals -▁rises -▁melod -▁days. -▁brut -▁brutality -▁nursery -▁misery -▁statute -▁orleans -▁Orleans -▁operators -▁informal -▁humanity -osity -▁mos -▁furnishing -▁furnace -▁cheer -▁brisk -▁Marshall -▁tested -▁Middle -▁elder -▁parker -▁fewer -▁propose -▁discretion -▁carri -▁clark -cknowledged -▁worthy -ersey -▁intermed -▁intermediate -pons -▁irrespons -hr -▁coalition -▁Des -▁Desp -▁Despite -▁Dick -▁dick -▁wick -inity -incent -▁sinister -grade -▁onset -▁fatal -▁lion -▁fogg --g -▁serge -▁inventories -eddy -onsciously -▁consciously -▁cort -▁distort -▁cord -▁murmured -▁vern -ogn -nold -▁libert -▁Gorton -▁"R -▁0. -▁radically -▁pm -▁advised -olph -aired -▁mythological -▁eighteen -▁obst -▁presently -▁tends -▁races -▁lucy -▁commend -▁bend -▁reaches -ordination -▁coordination -▁eggs -▁fortunately -▁antique -tructured -▁unstructured -▁Coop -▁mart -▁mcc -▁compulsivity -▁historically -▁allowing -▁enlarg -ymphony -▁Harold -apeut -icnic -▁picnic -▁shrink -ongh -aton -▁locked -▁stretched --B -▁commander -ering -erge -▁emerge -▁processing -eriff -▁sheriff -▁skiff -▁surrounding -itzer -acter -▁bacter -▁Parker -▁plank -▁turk -▁awk -▁awkward -▁ring -▁rip -ising -▁typew -▁typically -▁tyr -▁tyranny -▁Later -▁compartment -▁validity -amily -▁sandburg -▁passes -▁hoping -▁shouting -▁shouted -▁prox -istically -▁belly -▁belov -▁definitely -▁instantly -▁oppress -▁albumin -▁publicity -press -▁unpreced -▁romance -▁meets -▁mothers -▁Vol -▁quantitative -▁avenue -▁approve -▁printing -▁civ -▁civic -▁wedding -▁Illinois -▁limp -▁ling -▁lingu -▁linguistic -▁lid -▁congru -▁According -▁illuminated -▁stol -▁stove -▁stolen -▁combust -iberal -▁Bay -▁array -▁reappear -▁stein -▁vessel -onald -▁disguised -▁finest -▁manufacturer -▁manufacturing -▁featur -▁donald -▁selective -▁voy -▁voyage -▁Roy -avana -▁plate -▁1947 -▁shorter -▁sociology -▁shrugged -▁accused -athology -▁polished -▁compact -▁vocab -▁acquiring --hour -▁missouri -▁battens -▁amp -▁ample -▁patents -▁dang -roit -▁detroit -itr -ris -sunami -▁tsunami -▁shifted -▁reinforce -▁Jr., -▁187 -▁maladjust -▁mons -▁soap -▁ingen -ossom -▁blossom -ers. -ollywood -lywood -▁hollywood -▁Complet -▁combine -▁gotten -19 -▁monot -▁sank -▁associates -▁graduates -▁opens -arah -▁inspir -▁counting -▁adopting -▁claimed -▁ninth -▁wreath -▁antibodies -▁bitterness -▁adapted -▁explosive -▁words, -ez -yard -▁beard -▁ard -hand -▁favorably -▁optimum -▁courts -▁blocks -▁paroch -▁parochial -▁honors -▁scored -▁utopia -▁justified -▁thermometer -▁revolver -apeutic -▁dressed -▁Commer -▁Commerce -▁rabbit -▁coolidge -▁beatnik -▁bunch -▁bid -▁robber -sburgh -ittsburgh -▁affected --W -ecutive -▁atlanta -▁reserved -▁ic -▁technic -▁simplic -▁simplicity -thony -▁resulted -▁controlling -▁alongside -▁precipitated -▁harry -▁Harry -arryow -▁tribun -umulative -▁kansas -▁Kansas -▁Tech -abash -▁involution -▁bounced --run -ujillo -2) -▁(2) -▁retrieved -herited -exper -▁Are -rell -▁conformity -▁unnecessary -▁violation -▁constructive -▁respective -▁respectable -▁antitr -▁antitrust -▁marry -▁liver -▁bronchiol -▁vessels -▁baltimore -▁adjusted -▁Mayor -▁scand -▁merchand -▁Last -sec -▁reflex -▁refers -▁typewr -▁breeze -fl -hl -▁holder -eros -▁Los -ositive -▁Similar -▁cul -▁culm -▁harold -▁ears -habit -▁Edd -▁Jul -▁rect -▁odor -▁cylindrical -▁cylinder -▁"T -▁dair -▁dairy -▁bureaucr -▁autumn -▁marshal -▁unrel -jamin -▁blues -▁daughters -ilization -▁veil -▁bubbles -▁albert -▁Palace -▁attempted -illed -▁corridor -orient -▁drag -▁lag -▁lagoon -ultmann -habitants -▁Ke -▁pipe -▁discrepancy -▁copper -▁copies -▁cope -▁copern -▁stabil -ulously -▁applying -▁conn -▁recording -▁Coast -▁creatures -▁rugged -vocado -▁fraud -▁applaud -▁disposition -▁cosmic -thabascan -▁nude -▁firing -▁analyze -▁monopol -▁exclaim -rapeutic -▁introduce -heim -arse -ara -ourse -▁linear -▁progressive -▁Five -arryowen -▁arteries -▁$7 -aviolet -▁ultraviolet -▁lav -▁accumulated -▁reconstruction -▁chandler -iot -iants -▁giants -week -▁unemployment -▁daylight -▁moonlight -▁denominations -olecul -▁spokesman -▁retention -▁verbal -zold -turn -▁Look -▁1930 -▁1940 -▁250 -▁abolition -sky -insky -▁stems -▁so-called -▁statutory -▁blockade -▁Mus -▁stew -▁Matsuo -▁matsuo -▁merg -▁swing -▁neighboring -▁Negroes -▁Lucy -▁aver -▁averaged -▁urgency -ini -▁persuasion -▁semester -ars -arskjold -▁Hammarskjold -▁hammarskjold -▁summar -▁compensation -▁drugs -▁bronze -▁turns -igan -▁Michigan -▁michigan -▁aesthetic -▁interl -▁bankrupt -▁chiefly -▁lively -lessly -▁shortly -▁lightly -atisfact -▁trait -orious -▁glorious -ector -▁Rector -▁Ramey -▁coc -▁sizable -▁makers -▁earthqu -bre -ibb -▁curb -▁Ob -▁Oct -▁Shar -▁Sharpe -▁coupled -oulev -▁Barn --n -▁Method -89 -▁conceal -▁conceive -▁occupy -▁occupation -▁baker -▁150 -▁emperor -ained -oked -▁longterm -▁carved -▁congruence -▁attracted -▁rooms -▁aristotle -▁Democrat -▁mat -▁mason -▁tooth -othy -▁indispensable -ilitary -▁rebellion -▁combustion -▁Fig. -▁slee -ierre -▁autonomy -ayette -afayette -▁specialized -▁mode -▁farewell -▁fresco -▁fond -▁fury -▁vagina -▁promising -▁Swift -▁educator -▁carriers -▁casually -▁Barco -▁raz -▁razor -▁raid -abriel -▁Rel -▁buying --all -▁over-all -▁cumulative -▁Pacific -▁pacific -▁battery -iding -▁closing -▁hanging -▁creativity -▁engender -▁returning -▁ramey -▁gasp -▁erup -loy -▁facility -▁Above -▁mull -▁(and -eland -▁republicans -▁chores -▁flame -greement -▁Agreement -avement -▁recognizes -lavery --H -▁accounts -▁Table -▁oy -▁distributed -▁SBA -▁sons -nabel -▁cockp -▁cockpit -polog -ologic -pic -▁conspic -▁judges -▁knot -▁knuck -▁duck -arians -▁ensemble -▁shipments -▁castro -▁Navy -▁NATO -▁Hand -▁Otherwise -enz -arried -▁contention -▁fitting -▁instrumental -OD -▁beneficial -▁imprison -▁converse -unc -▁transpar -▁publicly -▁hollow -▁waist -▁tum -▁torr -▁touches -▁controversial -▁coarse -▁melodies -▁fertility -▁kne -▁electoral -▁disrupt -▁hated -unn -oqu -▁eloqu -▁enlight -▁well- -▁indirectly -▁ruined -▁lined -▁Relig -▁subjective -athet -▁excellence -atrical -▁pope -▁pressures -ihood -▁aspir -▁regularly -roach -reach -bach -▁Pre -▁Post -▁Pathology -▁polyether -▁Lat -▁Latin -▁congrat -▁jan -▁participating -assau -auge -ays -▁sturdy -▁Jean -▁Foreign -isan -▁partisan -ennis -▁quantities -▁loves -▁comprise -▁losing -▁preferably -▁eighty -▁mickey -▁Mickey -▁majest -▁dislike -▁African -▁african -▁bacterial -▁bapt -▁bod -▁invitations -▁consisted -▁mg -▁sway -▁soup -▁whiskey -▁whisky -▁independently -▁slack -▁motions -▁acceleration -▁Capitol -▁capitol -▁tab -▁tie -▁turb -▁tensions -▁tale -▁cart -▁correlation -atern -▁jean -▁Volume -bidden -bird -stro -kstro -kstroh -kstrohm -▁corruption -▁psychologist -▁fatig -▁fatigue -▁approaches -▁describes -▁enabling -▁enabled -▁wrinkled -▁flung -▁clung -▁prone -▁prow -▁princess -▁Report -▁Lady -▁sherman -▁registered -▁omin -▁predomin -▁auditorium -▁premium -iscover -apor -▁vapor -▁Bob -▁directors --hand -▁spectators -▁upstairs -▁hen -iffen -ymp -ympathetic -▁intact -▁Turk -▁monstrous -astrous -▁disastrous -▁fed -▁morphophonemic -▁framework -▁spectrum -▁rushed -▁bob -▁strand -▁illustration -▁illustrate -▁Communism -win -▁Within -▁afflict -▁Italy -▁slaughter -▁uncomm -▁standpoint -▁ladd -▁lane -▁belgians -▁Russians -▁jul -uls -ulsion -ulations -▁Supreme -▁tales -▁exercises -▁bridget -▁coordinated -▁emanc -▁emancip -ancing -ipus -edipus -▁slip -▁grip -▁missed -▁zg -rd -▁herd -▁vitality -▁impersonal -▁meal -▁forthcoming -▁proceedings -▁halfway -▁driveway -▁affix -▁affil -clusion -▁strongest -seas -▁overseas -▁micrometeor -▁stack -father -▁grandfather -▁railway -theast -aste -▁emerged -▁Obv -▁Obviously -▁furiously -▁Spenc -▁faded -▁reconstruct -▁reconsider -▁1040 -through -rought -▁conced -▁Red -ared -▁infrared -▁pressed -▁irrelevant -axwell -▁treasur -▁sociological -▁tense -▁calibr -ibrium -▁pion -▁pione -question -illion -▁King -▁33 -ileen -▁phonologic -▁pension -igation --M -▁Laur -▁nitrogen -▁spotted -▁vermont -▁"to -rylic -▁acrylic -▁sector -▁equitable -▁entail -questionably -▁declaration -▁exchang -▁postp -pert -▁turnp -uffled -▁diaph -▁tack -▁penal -▁frost -itical -▁necessities -▁bolt -▁forg -▁forgiveness -▁reun -▁reunion -▁grab -▁scores -ptic -▁popp --degrees-C -▁carolina -▁Cop -▁Copern -▁bearing -▁morse -▁sunset -▁Onset -▁wires -▁pioneer -▁lights -▁findings -▁monkey -▁Walk -▁countless -▁clover -▁nigger -▁championship -▁rum -▁Suppose -▁Eileen -▁spots -▁imagery -▁enterprises -▁46 -▁56 -umid -umidi -▁bru -▁5000 -▁cork -▁Ask -▁realt -▁realtors -▁dealt -tw -▁survivors -▁commerc -▁commercially -▁secant -▁landscap -▁disreg -▁disregard -▁anch -▁anchor -che -▁dialysis -quis -merican --American -american -amiliar -▁unfamiliar -▁unfold -▁unfair -▁slipp -▁microorganism -▁santa -▁Santa -▁womens -acc -dale -▁illum -▁lumin -▁shaft -wind -▁accordingly -▁insufficient -▁shovel -▁cardinal -▁cards -▁pituit -asso -icasso -▁costum -nam -noons -▁afternoons -▁untouch -▁Development -▁tremble -▁indicating -▁turnpik -▁stride -▁doorway -▁kil -▁Patch -▁Patchen -▁turnpike -▁tourist -istence -▁dome -▁parked -▁parking -▁Shayne -▁Cal -loyd -boh -▁waved -▁Fred -▁ridge -gn -▁pregn -▁Up -ffair -▁popularity -▁pupp -▁peril -▁Harbor -▁preparing -▁preparations -▁messeng -▁plastics -▁tilghman -▁Montgomery -▁montgomery -▁crimes -▁thereafter -▁statistics -▁viv -▁domain -▁grinned -▁solicit -▁1944 -▁Twent -▁separation -▁certainty -▁injury -▁whist --free -awaii -▁sunny -▁Rem -▁allocation -11 -▁descent --third -yth -ytholog -▁demytholog -▁pyth -▁python -▁readiness -▁monthly -▁vaguely -▁player -▁coincidence -▁coincide -▁trials -▁delegates -▁parasympathetic -▁laur -▁exhibited -▁studying -▁beam -▁obliged -▁sentimental -▁ultracent -▁females -▁subsequently -restr -▁proprietorship -▁hans -▁fences -▁occurrences -▁occurring -▁Maris -▁colored -▁capability -▁trading -▁Jane -▁sweater -▁Brand -log -▁crawl -▁eliminating -▁wider -▁fans -▁intr -▁intrig -▁lectures -uously -▁tremendously -▁enormously -▁weep -▁champag -▁champagne -▁revers -antern -▁lantern -▁oban -olate -iterate -▁Him -▁raises -▁tubes -▁anglos -▁anglosaxon -▁finite -▁eileen -▁elb -▁chapel -▁maintaining -▁vulnerable -▁texture -▁Education -▁psychologists -▁disposed -▁unlimited -▁rug -▁canvas -▁madison -urrow -▁aristocr -▁(T -▁listened -▁listeners -▁attached -▁gifted -▁gap -▁relationships -utter -ts -▁"E -▁gay -▁gle -▁gleam -ayabash -▁celebrated -▁remaind -▁remainder -▁participated -▁Telegraph -▁refine -▁poised -▁recurr -ang-J -juries -▁keel -▁generator -▁grind -▁susan -anned -tow -▁bestow -ware -▁dare -▁nerve -▁irrad -▁perceived -▁nap -▁Hollywood -ched -chem -chemical -cher -▁Special -▁stimulus -▁tappets -▁denomination -▁plumb -entagon -▁tasks -▁northeast -▁Mon -▁Steph -▁inhibit -phere -▁commuter -ossip -▁gossip -▁tender -▁tended -▁bars -▁intervals -ieties -▁absorbed -roduction -▁Fair -35 -▁embodiment -▁bore -▁distaste -▁preventive -▁Lee -▁spectator -▁welcom -▁freew -▁adventures -▁commitments -▁commitment -▁delayed -▁sensible -▁leads -▁guns -▁gorton -▁metaphysics -rosis -▁proceeded -odore -▁cooking -▁misunderst -▁misunderstand -▁kids -▁houston -▁bangj -pot -▁forgot -▁defeated -▁protective -▁derive -▁specialists -▁regretted -▁middle-class -▁scott -▁scour -hour -▁rejection -▁refrain -▁Holy --dr -rick -▁centrifug -▁coron -▁media -▁epis -▁episode -▁privately --ey -▁ruling -▁ok -▁Nixon -▁nixon -▁trop -▁tropical -▁squeezed -▁obanion -▁argues -▁blink -▁fan -▁middleclass -▁continental -▁foundations -▁explanations -▁fastened -▁Occasion -▁companions -▁conquest -ennial -▁solar -arious -ilborn -ilghman -▁Tilghman -ilibrium -▁equilibrium -▁dramatically -▁gentile -amel -amela -ersted -▁oersted -▁Oersted --mile -▁statist -▁statistical -iston -ieze -▁frieze -▁elected -▁promises -▁penalty -▁binding -▁customary -▁arises -▁gram -▁Bal -▁Baltimore -▁Eddie -▁childrens -▁bangjens -ang-Jens -▁periph -▁epiph -▁ambival -interest -▁travell -osed -imposed -▁hypothetical -▁ticket -▁formally -▁luminous -▁purport -▁purple -▁appet -▁charming -▁ingenious -▁yach -▁typewriter -▁expressions -▁Sept -▁crept -▁watercolor -lav -oslav -ugoslav -aug -aque -▁dialogue -▁alliance -▁faster -master -▁Would -▁Win -▁Winston -▁Women -erm -▁symbols -▁attic -▁eventual -▁quoted -▁comr -▁comrades -▁organism -▁tilted -▁gramm -human -▁solutions -lands -▁Portland -▁territories -▁envision -▁hardship -▁exercised -▁peg -▁flor -▁Century -▁shelf -▁privileged -▁Newport -▁reminded -▁Year -▁underwater -▁*y -▁unst -▁myst -▁(B) -alta -▁roberts -▁thoughts -▁electronics -▁congest -▁Pope -▁Howard -osp -iven -▁abandoned -▁dissatisfaction -▁Ac -athy -▁fabrics -▁hardy -▁frighten -▁deliberate -▁Tim -▁unav -▁unavoid -▁unavailable -▁relent -▁ruin -▁Forest -▁lob -▁lodge -burse -burst -▁diverse -▁insane -▁jane -▁Davis -▁davis -▁fires -▁fate -erosol -▁archaeology -lessness -▁continent -▁contraction -▁inclined -▁incredible -▁ov -▁inherent -▁nato -umont -▁diagnostic -▁recalled -▁lawn -▁reassurance -▁obstacle -▁Completion -sters -▁youngsters -▁spirits -▁occurs -▁proposals -▁spur -ecd -ecdot -▁alec -▁overlook -▁prevented -▁purchased -▁crowded -▁justification -▁offering -▁laughing -▁Austr -inster -minster -▁Bureau -▁sax -▁roast -▁suspicious -▁charco -▁vocabul -▁Ol -▁Fl -▁Follow -▁gratitude -tily -▁hastily -▁unsuccess -▁suggests -▁sprawled -▁decrease -▁whereby -▁gown -▁germans -umbrell -▁smoothe -▁staged -▁stature -comes -▁electrost -▁developments -pton -important -▁smiling -▁devast -▁Hans -▁sounded -▁carryover -▁Manhattan -▁tar -eegan -atanga -▁Orchestra -▁segment -▁bases -▁tumor -aker, -▁rope -▁Beyond -▁considers -▁pile -▁professors -▁cohesive -▁deficiency -▁describing -▁ratt -▁inval -▁invalid -▁elevator -▁illumination -▁"n -▁microph -▁nuclei -▁eyebr -▁Elaine -▁credited -▁resembl -▁resemblance -▁Organiz -▁translate -▁159 -▁collision -▁basketball -▁woods -lets -▁prostitution -uddy -▁shudd -flow -▁Hous -▁Houston -▁vow -▁collage -▁imply -▁Early -▁irony -▁advocate -▁undertaking -▁reactionary -▁pituitary -▁vocabulary -▁ambush -▁draf -▁negotiate -▁faulkner -▁me. -▁deposit -▁epithe -▁tools -▁cereal -▁governmental -alph -▁anchored -▁infancy -ifiable -▁columns -iev -row -▁elbow -▁pin -▁availability -▁drastically -▁cm -▁cough -ms -▁miracle -▁disarmament -▁successor -▁corners -inting -▁ignorant -vant -advant -▁disadvant -▁lev -▁lex -odium -itzerland -▁Maryland -▁polish -rish -▁Irish -▁hut -▁hook -▁codes -▁hart -wart -▁door. -esides -▁Besides -▁classified -▁conqu -tolem -▁oedipus -▁Oedipus -▁oak -akable -▁destined -▁destination -▁tones -urity -augur -▁suppress -▁slim -▁quad -ada -▁canada -▁cad -▁hand, -▁footstep -▁Should -▁cellulose -▁Therefore, -▁palfrey -▁scorn -▁admirable -▁twentyfive -▁twenty-five -▁expressing -▁abdom -▁20th -▁Anglo -▁Anglo-Saxon -▁apartments -▁fusion -tonishing -▁specimens -arc -arst -▁Hearst -OC -▁accelerated -▁originated -▁cavity -▁tunn -▁explore -▁superiority -▁prosperity -▁ninety -yan -▁mercy -▁Mason -▁permanently -▁"l -▁autonomic -▁entering -▁alve -▁uncomfortable -▁modernization -▁swoll -▁swollen -▁tears -▁burg -▁nighttime -▁conjugates -▁Vern -▁hymen -▁eloquent -▁costumes -▁elite -▁elimination -▁poultry -ogg -▁Fogg -▁adher -▁violin -▁amaze -▁breaking -▁johnson -▁johnny -▁day. -▁downward -▁Dec -▁elegant -▁shorts -▁imposed -▁drying -▁testing -▁staring -▁stars -▁falling -▁lists -▁Keith -▁dent -▁full-time -▁Viet -▁viet -▁cheese -▁commute -▁thyrox -▁porter -▁porous -▁179 -ena -itten -chestr -▁resumed -▁indications -▁Eugene -▁eugene -▁Interior -▁deterior -▁kidney -▁dece -▁perce -▁perceive -▁resume -▁resumption -▁ladder -▁ultrason -▁lend -▁loun -ql -iql -▁sisters -▁jersey -▁Jersey -▁abruptly -▁repeatedly -▁intent -▁drifting -▁missionary -▁slump -▁shaking -▁principally -amor -▁translation -▁orientation -▁translated -mosph -mospheric -▁boom -▁conceivably -▁malraux -▁suic -▁suicide -▁Harris -emorial -▁exploited -▁days, -▁tort -▁tortured -▁efficacy -▁Temp -▁teasp -▁raced -▁align -quet -▁Kitt -▁57 -87 -▁aspirations -▁Unt -▁Until -▁evaluate -▁involvement -▁"public -▁umbrell -▁uncover -lord -▁landlord -▁pupils --tw -▁Hence -see -▁(see -▁dispers -▁chip -coats -rock -roc -▁reciproc -▁scandal -▁Maj -▁Mac -▁unmist -▁incorporated -ypt -▁egypt -▁(with -▁dazz -▁speculative -▁specify -▁accommodate -▁visitor -▁surrounded -▁establishing -▁everyday -▁Mull -▁clue -▁horrible -▁horror -▁McK -▁bobby -▁couch -▁cautious -atell -▁satell -▁Mad -▁Madison -▁composite -▁spaces -itiated -▁sentences -▁meaningless -"; -▁E. -▁encroach -▁Im -▁contributions -▁Jews -▁savings -▁Sand -▁Sandburg -▁hind -▁hint -▁hail -boro -▁enlarge -umblebe -▁chromatic -matic -▁electrostatic -▁kilometer -▁Co. -▁deem -▁realities -▁Christianity -▁christianity -▁males -ayana -oft -oulevard -▁steele -▁swoop -ometry -▁electrons -▁W. -▁spinning -▁posit -▁positiv -▁posse -▁ego -▁interd -▁"g -▁childish -▁Stadium -▁inflict -▁visc -▁Force -▁que -▁carriage -▁gum -▁gulf -▁appreciated -▁appropriated -▁identities -▁mines -▁ministry -remier -▁telegram -▁woke -▁input -▁1942 -▁1943 -▁1941 -▁varieties -▁undue -ste -stein -e, -▁organize -rode -▁Symphony -▁handed -▁disturbed -▁halted -▁then, -▁lacking -▁settling -▁ripp -▁villages -▁averages -▁Gold -▁choices -▁allies -▁anonymous -▁specially -▁Harvey -▁grief -▁comfortably -▁hopp -▁freez -▁catalog -▁irradiation -▁"Y -▁mahogan -▁Frankfur -▁unadjust -▁modified -▁durable -▁films -▁pir -▁Burn -oris -▁transferor -▁proclamation -▁wishes -tes -▁grotes -▁grotesque -▁withdraw -▁hiding -▁swear -▁imb -parable -▁limb -▁fighters -▁imperative -▁imperial -▁warned -▁misfortun -▁forgive -▁slop -▁esp -▁dump -▁businessmen -▁Felix -▁felix -▁census -▁39 -▁morris -▁linguist -▁leis -▁leisure -▁planes -▁unhe -▁Corpor -tally -▁Jim -▁anaconda -▁retard -▁Brumidi -▁Brannon -▁leaped -▁intelligible -▁overhead -▁Barton -anim -▁unanim -▁namely -▁tightly -▁insulation -▁goods -▁rating -▁logic -▁maryland -usions -agging -▁Donald -▁prohibition -▁plaint -▁acquaint -▁viscosity -▁masterpiece -▁$6 -▁$8 -▁technicians -▁rubbed -'dikkat -s' -▁manip -▁Puerto -▁puerto -▁galax -▁recognizable -▁hamilton -▁lightning -▁swore -▁elastic -▁antiqu -▁missing -▁feeding -negative -onnaissance -▁(D -▁pickup -ifties -cellaneous -▁72 -▁sera -▁Republicans -umumb -▁unsatisfact -▁averaging -▁162 -irl -▁transit -▁transaction -▁wag -▁wagons -▁Warwick -▁hitting -▁distilled -▁bastard -TV -▁mileage -▁plainly -▁guarante -▁happening -▁spacious -▁barefoot -idel -▁launder -▁snapped -▁parttime -▁fulltime -▁Colon -▁concerts -ythe -▁neon -▁prejudice -vention -▁plentiful -▁ominous -▁prospects -▁cautiously -▁habitat -▁overt -law -▁satisfactor -▁satisfactorily -▁lump -rons -▁patrons -▁Nick -▁penny -▁deputy -retched -▁part-time -▁creek -wild -▁Ham -▁Hamilton -▁wrist -▁girlish --out --type -▁inhibition -▁nutrition -▁Hart -oliage -▁happier -▁Finally, -▁Broadway -▁interchange -▁Revenue -▁florid -▁differential -▁10000 -▁scout -▁academy -▁shortage -▁pas -▁mas -▁mascul -▁mol -▁assuring -▁1917 -▁accred -▁policemen -▁cigarettes -▁marines -▁preoccupied -organic -▁awaken -▁dependence -▁unprecedent -▁architecture -▁conclude -▁Pip -▁returns -▁naz -▁Naz -▁addresses -▁trevelyan -▁spat -▁spun -▁magnet -▁guided -▁stretches -▁dominance -▁cance --haired -▁Milton -▁2. -▁loudly -▁agreements -etery -emetery -▁cemetery -emph -▁Bang-Jens -▁Connect -▁weekly -iour -▁adjo -▁adjoining -▁ugly -▁composers -▁dramat -▁obstruct -▁aggreg -▁dreadful -▁youthful -▁aroused -iqlal -▁appetite -orus -▁chorus -▁Krim -▁krim -▁altered -elp -▁governing -▁melting -ovsky -▁Bord -▁curs -▁structures -▁remembers -isphere -▁disagreement -▁indictment -▁reflects -▁diminished -▁Yes, -▁unimp -▁overwhelming -lean -▁Mean -▁Meanwhile -▁worthwhile -▁eichmann -▁Eichmann -▁subsistence -existence -▁manpower -▁phony -▁adoption -▁indec -▁prede -▁predecess -▁scree -▁See -▁keen -▁keith -▁sul -▁phonemic -▁indicator -▁adhes -▁41 -▁tennessee -▁prediction -▁plaintiff -▁Affair -▁banquet -▁frankfurter -▁Johnny -▁perpetu --19 -▁inquiry -▁elegance -▁convuls -); -▁shrew -▁causing -▁BOD -▁americas -▁conceivable -▁sergeant -▁disab -oyce -▁Joyce -gar -▁S. -▁Simm -▁negl -▁negligible -▁theater -▁parish -▁loos -bb -▁cush -▁cushion -▁chromat -▁chromatography -▁Is -▁Israel -▁specialist -▁comparatively -▁magg -▁magical -▁missionaries -▁conscient -▁conscientious --mind -▁conspicuously -▁consecutive -asteur -asteurization -opasteurization -▁dystopias -▁nond -▁strat -▁democrat -▁formid -▁formidable -▁epid -▁epidemic -▁intercept -▁suscept -▁heads -ependent -▁armies -▁Forces -▁cambridge -▁sel -▁selkirk --bl -▁archaeological -▁Cuba -evelop -▁locate -▁ghet -▁ghetto -▁guerrillas -▁guerrilla -▁$10 -▁Student -▁transparent -▁natives -▁psychiatrist -▁Unfortun -▁checked -▁shaped -▁retained -▁pertain -▁unmistakable -▁dropping -▁feud -▁displeas -atism -▁conservatism -▁harvey -▁totally -ca -▁pans -▁shattered -neapolis -▁anne -▁extrud -▁Premier -▁bleed -▁bleak -▁slum -ifiers -▁gods --school -▁Nob -▁Nobody -▁cubic -itic -▁blindness -▁magnification -▁communicate -▁anticipate -ollar -▁Podger -▁arkansas -▁Arkansas -▁Artic -▁banks -sons -▁orthodox -▁induce -▁rental -ospel -laim -▁fathers -▁scholars -its -itage -▁heritage -▁na -▁naive -▁signals -▁Ben -▁formulation -olation -olas -icolas -evie -brew -▁harris -oat -aters -▁allowance -▁publications -▁emotionally -▁therefore, -▁markets -▁covers -▁collaps -▁assets -▁illiterate -▁airplane -ulton -unct -▁veto -▁gut -▁clayton -▁intuitive -garet -▁incap -▁incapable -▁Taylor -▁Column -▁doubled -▁mare -▁drastic -▁fox -▁severely -▁sectors -▁familiarity -erger -▁riot -▁socialism -elli -▁drainage -▁barton --the -▁pinch -▁hysterical -bel -▁picket -▁"J -▁well-known -▁microscope -▁energies -▁impartial -▁nonetheless -▁candy -▁silk -▁meantime -▁reconnaissance -▁dusty -▁Fromm -▁hem -▁hemisphere -▁restaurants -▁womans -▁Northern -yllables -▁D.C. -▁preservation -▁preserved -▁compelled -▁Foundation -▁representation -▁peasants -▁pressing -▁Rousseau -▁shaken -▁rehears -52 -▁tennis -▁Arnold -▁arnold -▁killpath -illing -▁unwilling -▁tribes -▁ballad -▁simultaneous -▁bust -▁hil -▁hood -▁hymn -etime -▁lifetime -▁frown -▁policeman -▁relevance -▁Outside -▁hughes -▁Hughes -▁obligation -▁gin -inary -▁supervis -▁culmin -▁litigation -▁signature -▁Min -integr -▁disintegr -▁seats -itating -▁correspondent -▁Domin -cles -▁spenc -nected -▁beh -▁behave -▁behaved -▁silh -▁silhou -▁Cook -▁Book -▁Bet -▁Between -▁B'dikkat -▁fierce -▁adams -▁denominational -▁paradoxically -byter -byterian -ges, -▁dough -▁permits -odgers -▁divide -▁Wall -▁Buddh -▁buddh -▁bud -▁tower -▁sewer -▁Mod -▁wholesome -▁recital -▁spong -▁waterfront -▁tells -▁acts -▁actor -▁actors -▁concepts -looking -▁exportimport -ois -▁escaped -▁Throughout -▁Stand -▁edd -▁eddie -▁torqu -icut -▁Connecticut -▁connecticut -▁hoag -▁shayne -▁penetrating -▁dangers -▁katanga -▁designated -▁evaluated -▁radiopasteurization -▁launched -▁elaine -36 -▁succ -ora -endora -▁re- -clusive -▁blew -▁bleach -▁71 -▁perhaps, -▁indulge -▁Professor -▁mayor -▁Mor -orig -▁aborig -orem -▁'em -avinsky -▁Congreg -prof -bably -▁Probably -viol -▁geometric -▁Geometric -quire -▁kinds -▁elephant -▁broadway -▁quaint -▁Gene -▁Geneva -▁femin -undry -▁peered -▁teenagers -▁ramp -▁articulate -▁pleading -▁ontolog -queous -▁antagonism -▁rebel -▁recognizing -archy -▁floating -▁sung -▁pitcher -▁Speaker -▁requiring -▁vegetable -▁oxford -▁locating -▁locally -▁shaw -ity, -▁permitting -▁bones -phem -asphem -▁blasphem -asma -▁plasma -▁plump -▁44 -▁speck -▁beautifully -▁districts -▁scramb -▁scrambled -▁southwest -▁steak -pace -▁sailing -▁graduated -▁lucky -▁bears -restrial -▁jim -▁Does -seven -▁skinny -▁refuse -▁traced -▁tri -▁triang -▁rectang -▁paramount -aress -▁caress -▁Press --I -▁Chapter -▁slug -▁ekstrohm -▁Ekstrohm -▁Ev -erick -▁Frederick -erable -▁miserable -▁idi -issues -▁tissues -▁tile -▁tv -▁tumbled -▁generators -▁Meek -▁journalism -▁Vienna -▁vienna -▁Vincent -▁vincent -▁vine -▁amuse -▁nucleus -▁producers -▁blush -▁Third -▁quarre -▁utilize -▁zoo -▁iodide -▁shrewd -▁meek -▁brannon -▁Russell -▁patronage -▁Family -▁deposited -▁hire -▁admire -▁boundary -▁approaching -▁year, -▁chamb -▁complained -▁advise -▁executives -▁recount -▁Johnnie -▁johnnie -▁Ernie -▁angles -▁employers -violent -▁aimed -▁telephoned -▁fled -▁knitted -▁eminent -▁emerald -▁mand -▁cave -▁decency -▁connot -iencies -▁dealer -▁prodig -▁span -▁peripheral -▁vinegar -▁populated -▁bail -▁kneel -▁supporting -▁1/2 -▁irrational -uted -▁idiom -▁unpaid -imesis -▁Times -▁maris -▁segments -▁criticized -▁critics -▁trimm -▁technically -▁grabbed -ased -▁deceased -las -▁earned -▁leagues -▁personalities -▁nickn -▁nickname -▁jar -▁seventy -▁rigor -▁slightest -▁remed -▁trunk -▁fractions -▁milligrams -▁Pittsburgh -▁pittsburgh -▁evolved -▁Kate -▁broader -▁doz -▁dozens -▁vaginal -▁prosec -▁prosecution -ellow -▁sue -▁spher -▁Representatives -▁110 -▁sunsh -▁addition, -▁flock -▁snap -chloride -▁Bang-Jensen -economic -▁serene -▁mock -armonic -▁unsuccessful -▁instance, -▁cerebr -▁Everything -nings -clination -▁doubtless -▁bon -▁bubble -ubblefield -▁airfield -▁bass -pass -▁postpon -▁unsatisfactory -▁Appar -▁Apparently -▁violently -▁cooperate -▁glaze -▁Ju -▁glasses -▁orchestr -▁zen -▁sullen -▁subscrib -haired -▁tourists -ayabashi -iability -iary -▁ardent -▁gauge -▁Vernon -odynam -▁glamor -▁reckless -incinn -▁genev -▁reign -▁Sm -▁Small -▁tom -▁tomb -m. -.m. -▁p.m. -▁realm -▁pine -eline -▁piazza -▁hostess -▁eve -world -ennings -▁kennings -▁blade -▁greeted -▁ranged -▁podger -▁(P -cy -crecy -▁penetration -▁underdevelop -▁perpetual -ache -▁utilization -▁alph -▁searching -▁Anniston -▁dreams -▁coexistence -ies, -▁pansies -▁profitable -▁arising -▁steph -creage -▁cypress -cribed -▁renewed -▁tsh -entuck -potent -ilpotent -▁nilpotent -▁Haney -▁fred -▁frederick -▁brushed -▁shelley -▁miscellaneous -▁disturbance -▁undergoing -▁racing -▁amazing -lying -▁underlying -▁multiplying -▁Sus -▁Susan -ologically -ologne -▁Start -▁Bobbie -▁Val -▁Valley -▁gig -▁gigantic -▁frantic -▁burial -▁sleeve -▁assass -▁financially -igen -▁antigen -▁Gen -▁splash -ego -▁sud -▁taxi -▁ski -rosh -roshim -iroshim -▁Unfortunately -▁disob -okes -▁dazzling -▁currency -▁230 -▁blot -▁47 -nd -▁amid -etroit -▁Detroit -onghorn -▁Anthony -▁Tit -▁Title -▁(in -▁(M -M. -▁criterion -▁criteria -▁nursing -outherner -▁osc -▁350 -▁Kitti -▁attributed -▁falls -▁intel -▁intellect -elect -print -eu -▁maneu -▁maneuver -eem -▁staircase -▁idol -▁idle -▁15th -▁Fund -▁shifting -▁antenna -▁anecdot -elihood -▁likelihood -▁likes -▁interpretations -▁wail -credibly -▁observers -▁bluff -▁palazz -▁pamp -▁pamph -▁pending -▁torment -OCS -▁Upton -▁subspace -▁comprehension -gricult -break -▁plotted -▁ecl -▁trips -▁transmitted -▁vitamin -▁purchasing -icked -▁cher -▁cherished -ager -▁chatter -▁shatter -aments -amic -amination -SH -ouglass -▁bastards -▁Religion -▁paying -trust -▁anti-trust -romb -rombie -▁bulb -▁suits -▁father's -▁screens -▁thyroxine -▁Utopia -essim -▁pessim -▁islands -16 -▁182 -▁Short -▁adjourn -aggin -▁improves -▁Zen -▁awoke -▁sleeves -▁incest -▁incom -ansom -▁Hom -▁Homeric -ahom -lahom -▁Mah -▁profits -▁devote -▁muffled -▁A.M. -▁tore -▁foliage -▁governors -two --F -▁alaska -▁distortion --shot -▁Ox -▁Oxford -oux -▁producer -gans -▁organs -▁54 -▁horm -▁hormone -▁bulky -enny -▁Philip -▁Father -▁Ell -▁summarized -▁qualification -▁Miami -▁Key -▁alternate -quar --dimensional -▁pursued -▁Use -▁referring -▁celebration -▁rook -▁Westminster -▁westminster -▁Natur -▁Simp -▁stoop -pok -poken -▁Ten -▁Tennessee -▁discomfort -ka -▁marij -▁marital -▁simplified -▁hideous -imeter -▁butter -▁meg -▁me, -gypt -▁Egypt -▁beg -▁begged -org -orgsky -▁georg -▁Director -▁geometry -▁Wisconsin -▁anatomical --made -green -▁subsidiary -▁fuzz -▁muzz -▁fame -▁ma -uing -▁forbidden -ienville -▁waving -▁wisconsin -▁dental -avors -▁Jud -weitzer -▁mariju -▁marijuana -▁filing -▁poign -▁unanimously -▁fragile -atie -▁ago, -▁Phys -▁colleague -▁48 -vol -▁posture -▁Griffith -▁amazement -▁Nevertheless, -▁joyce -▁solidar -▁solidarity -management -▁stockade -▁Giffen -▁airplan -▁cogn -▁Addition -llor -▁chancellor -▁assemble -▁resemble -▁Security -▁accelerometers -lec -▁Alec -▁yankee -▁assuming -theart -▁portions -▁mercen -▁mercenaries -▁mercury -▁compulsive -▁wounded -▁drifted -▁homeric -▁homer -▁precarious -▁acclaim -▁treaty -▁Auto -▁alveol -▁Const -▁ecst -▁tobacco -▁unquestionably -▁stroll -▁Carroll -▁static -▁stating -▁stal -▁stare -yp -priced -▁virus -▁Had -▁purposes. -▁sodium -warf -▁64 -▁memorable -▁peters -▁string -▁strings -▁applicants -▁embro -▁decre -▁sarah -▁Sarah -pirit -▁transpir -▁summit -▁summers -▁day, -▁wired -▁cancer -▁Guard -▁wolf -▁himself. -▁rejo -▁packed -cestry -▁fring -▁fringe -▁simplest -▁batting -▁(or -rov -▁replacing -▁succumb -▁russell -elled -▁(E -▁annoyance -▁bobbie -▁bdikkat -▁clergyman -▁salesman -▁lamb --conscious -▁tune -▁swiftly -▁Royal -▁Dolores -▁dolores -▁Daily -▁hudd -▁outright -▁now, -▁cooperatives -▁expanded -▁Ver -▁Vermont -▁assail -▁shots -▁planners -▁conson -▁consonant -▁shu -▁shake -▁thaw -▁indicators -▁sensual -▁Bern -▁Bird -▁bern -▁Modern -▁mustard -▁Johnston -▁johnston -▁liberalism -▁boge -▁faithful -▁opt -▁Freddy -harmonic -▁analytical -▁thirst -▁qualitative -▁legislator -etric -▁symmetric -▁integral -▁integrated -▁starts -▁nonsense -▁southerners -vet -▁wallpap -emost -anford -▁cake -▁earthquake -▁Furthermore, -▁rousseau -▁martyr -▁accr -▁accruing -▁accuse -cop -roscop -▁said: -▁sloan -▁sincer -alytic -▁Kowalski -▁kowalski -▁Calif -▁gaze -BM -ally, -eller -efeller -44 -ibrary -▁graham -▁condemned -▁griffith -▁bidd -anita -▁pathological -▁bargaining -berry -▁pharm -▁shades -▁strewn -▁victories -▁accreditation -▁proprietor -▁extrapol -view -▁Dominican -▁Eastern -▁cliff -▁fulfillment -▁breathed -▁socio -▁rolling -run -▁Gun -▁Germans -▁haney -▁conclusive -▁contacts -▁States, -wight -▁thereof -▁Cam -▁Cambridge -▁orange -▁charcoal -▁work. -▁away. -▁erected -▁carefree -▁slammed -oriented -▁rustling -▁pavement -▁schoolhouse -▁remedy -▁currents -▁loaded -▁aborigines -termination -▁lee -▁accompanying -▁stirring --six -▁meteoritic -▁stresses -▁gran -▁undergraduate -▁hillside -▁systematically -▁Article -chemistry -▁congressman -▁investors -rei -▁1935 -▁1938 -▁portland -▁superst -▁superstition -▁formulations -▁symmetry -effect -illpath -▁Killpath -▁Tomm -▁void -▁wrath -▁Kore -▁abandonment -▁prevents -▁ballot -ned -▁remarked -▁Science -▁Son -▁Common -▁Command -▁Land -▁tuck -▁refusal -▁Belgians -▁elephants -▁steering -▁muzzle -▁attendant -▁attending -▁cruising -▁shoe -▁Angeles -▁miami -▁say, -▁Beach -▁journalist -▁Colonel -▁utilized -▁utilizing -▁venge -▁vengeance -15 -14 -uart -▁guarantee -▁anxiously -▁realizing -▁nutrient -▁kate -▁Product -▁purified -▁crouched -▁out-of -▁traver -▁interdependence -▁interdependent -▁possessed -rep -▁Test -▁Testament -▁sponsors -▁Dep -▁Dan -▁Deegan -▁engulf -▁roleplaying -▁Baker -▁reverend -▁blend -▁Amb -stal -▁nostal -▁nostalgia -▁curled -▁curse -▁kore --2 -brush -▁Congregational -▁federation -icens -▁licens -▁pamphlets -▁bullsey -▁tablesp -▁nominal --gr --T -▁punch -▁Rather -dick -osdick -osh -▁Ord -▁Orient -▁transd -▁transduc -▁formulated -olored -▁taylor -▁consultant -▁snapping -▁disadvantages -▁Ralph -▁ralph -▁Big -▁(f -▁puzzle -▁puzzling -▁Belgian -▁aggression -▁pastor -▁bulletin -▁2000 -▁manage -stage -adine -▁saloon -▁newport -▁76 -▁mills -▁norms -roth -▁deprived -erent -▁indifferent -neath --story -▁constituents -▁undergraduates -▁purse -robe -▁wardrobe -fect -▁divorced -immy -▁honored -▁Everyone -▁welch -▁Continental -loan -▁Sloan -▁bleeding -▁begging -▁discernible -▁calf -▁Temple -▁adjunct -▁pepper -▁Was -▁graceful -▁Fortun -▁Fortunately -▁Es -▁apportionment -▁infant -itate -▁carrier -▁perplex -▁anniston -▁fertil -▁declarative -▁ernie -▁Sudd -▁Sudden -▁curiously -▁Dickens -▁residents -▁pole -avin -▁Gavin -▁turtle -▁Douglas -▁douglas -▁beloved -▁speakers -▁Queen -▁Washington, -▁Bre -▁Bee -▁exaggerated -▁freddy -▁diaphr -▁diaphrag -▁employes -▁dentist -▁acquies -acies -▁Wel -▁Welch -channel -usually -▁unusually -▁generosity -▁youngster -ierarchy -eld -▁administered -▁Med -▁Medical -▁cynical -ammed -urred -itched -▁dickens -▁warwick -▁stones -▁discont -▁rancher -▁junk -▁noticeable -▁antislavery -▁frustrated -EA -▁Alaska -gean -egean -▁keelson -▁volta -▁volley -▁bodybuild -rophy -▁Which -▁successive -olitical -affirm -▁reaffirm -▁consumm -▁Beowulf -▁encounters -addy -▁enr -▁enrich -▁enh -yd -▁tires -▁fearful -▁Julia -▁julia -sequently -▁Consequently -▁ripe -▁Byron -▁manned -▁frenz -▁monastic -▁buffal -▁buffalo -icus -▁copernicus -▁Copernicus -▁flourish -renaeus -▁preparatory -▁guarantees -lahoma -▁oklahoma -▁morale -▁Brid -▁Bridget -adget -▁gadget -▁toll -▁molecul -▁kern -▁earnings -▁Often -▁dilution -enheim -iless -▁700 -▁abolish -▁Laboratory -▁barrier -▁italy -▁ugliness -ymphoman -▁admiss -▁admissible -▁irresponsible --G -▁earthquakes -▁burns -▁temperatures -▁sticks -▁deplor -▁muddy -elike -▁frances -▁caliber -▁irresist -▁irresistible -▁boredom -▁teaspoon -▁stressed -port-I -▁Export-I -▁Winchester -ymphomaniac -▁encomp -▁marvelous -▁inquired -ermud -urious -▁luxurious -▁disbel -▁fer -▁ferment -▁fidel -▁practition -▁exterior -quisite -berr -▁hydr -▁investigate -▁prohibited -▁Superior -▁slash -▁Well, -▁notes -▁vectors -▁shov -nov -oves -▁successes -▁Members -crimin -▁owner -▁reinforced -▁Yankee -▁stimulated -▁1963 -▁nomination -▁mate -▁Whate -▁Whatever -▁interp -▁Rico -▁rico -▁refrigeration -▁politically -▁mexican -▁Mexican -▁mitchell -▁Mitchell -▁Music -▁reveals -▁deepest -atz -▁Week -▁comprised -▁cats -▁grok -▁groc -▁musk -▁descriptions -▁rocket -▁sterile -▁noted -mates -made -▁Gard -eb -▁dual -arine -arl --war -ologians -▁illustrations -▁couples -▁Engine -ivial -▁trivial -▁fratern -making -uscript -acon -▁screech -▁diversity -ivities -▁bould -▁bumblebe -▁tunnel -▁Billy -▁billy -▁ironic -icans -exicans -▁majestic -▁preference -herence -▁adherence -eeley -▁mamm -erget -▁energet -▁energetic -▁deemed -▁radioactive -▁stability -▁pork -▁pose -▁Alabama -▁alabama -encing -▁gracious -close -▁disclose -urray -▁Also, -▁Richmond -ommitt -▁dun -▁dorm -▁lun -▁lab -▁lurch -▁roots -▁governed -▁Viol -rosine -▁purposes, -▁congen -▁harmonies -▁dynast -▁diminishing -▁Sub -▁Philipp -▁suppression -yroid -ithyroid -▁Florid -▁entities -▁channels -▁insects -▁immigrants -▁linguists -▁negotiated -▁simplify -▁vault -▁interstate -unity -▁immunity -▁testified -▁clevel -▁vases -▁statewide -enched -▁lens -▁tailored -▁capitalism -▁blown -▁unchang -▁farmer -▁meetings -second -▁Author -cloth -▁misc -▁downstair -▁Food -▁grains -li -lies -length -▁wavelength -▁rabbi -▁forthright -▁upright -▁blades -▁giffen -▁carb -▁chord -▁Ford -▁disturbing -▁dismay -▁angu -▁lunar -elkirk -▁garibaldi -▁69 -▁willingness -▁Southerners -▁night. -aited -▁awaited -▁computed -▁accustomed -▁sizes -▁Rad -▁Radio -erate -▁worker -▁passenger -▁isolation -▁Water -▁Indians -▁regression -▁derives -▁fictional --by -irby -▁ambiguity -▁cranston -▁Cranston -▁keys -▁digital -▁dishes -▁Greville -▁litter -▁emergencies -▁appropriations -▁Baptist -▁picturesque -▁loses -herit -▁TSH -▁Town -▁queens -▁folded -▁weird -▁accomplishment -▁terrain -▁formulaic -▁cocon -▁coconut --con -▁painters -▁communion -▁lowest -▁thorn -▁plunged -▁guaranteed -▁formulate -ehead -▁forehead -e. -▁sometime -iane -bean -▁deputies -angford -▁resin -▁wonderfully -▁wondering -▁breathing -▁trends -▁frankfurters --off -▁subt -▁ft -▁fab -▁outset -▁Exper -▁Sk -irling -▁toler -▁secretaries -▁terminal -▁terminate -▁(N -glass -▁Ambass -cern -▁unconcern -▁dimly -▁singled -▁lust -ustin -▁stump -▁epithets -▁hack -attack -▁moisture -.e. -▁i.e. -▁vacant -▁portable -jun -▁amusing -▁fibrosis -▁Glendora -▁hardened -▁hardware -▁bachel -▁significantly -▁intang -▁upset -▁rend -▁rendez -▁Selkirk -▁hasten -▁Fall -▁coats -▁interfere -ancies -▁varies -▁babies -▁icy -▁authoritative -▁dan -▁garb -ba -▁sba -▁pulling -▁cathode -▁iodin -▁skirm -▁pasture -perture -▁grocery -▁pegboard -▁Orlean -▁dive -▁struggled -▁shotgun -▁quit -▁Color -▁Colorado -▁substituted -▁tray -▁spatial -▁swinging -▁dioc -odges -▁scanned -▁sprout -▁Mart -▁supplementary -▁palat -▁palatability -▁allotted -▁concentrate -▁concentrations -▁approximate -▁provoc -▁provocative -▁Ballet -▁skull -▁hull -▁enjo -▁discontent -▁oily -▁restless -▁instinctively -▁noon -▁confrontation -▁Via -▁satellite -▁astonishing -▁Gulf -▁performers -▁performing -▁advisers -ouncing -▁ulcer -▁appall -▁merciless -▁voluntarily -▁stopping -▁nagging -▁niece -▁fashionable -▁haunted -isons -▁subjected -incinnat -umni -▁attacked -pped -▁neurosis -▁chickens -▁generated -▁adults -▁bliss -▁permiss -▁prominently -▁biw -▁neglected -▁swee -▁sweetheart -▁poster -▁posterior -▁discussing -▁crossing -▁tenure -▁wiped -▁dragged -▁dragging -▁economically -ignific -▁cripp -▁philipp -▁crus -▁wheels -▁impuls -▁malaise -agrin -▁Soon -▁proportionately -▁clause -▁Upon -▁wheat -▁flutter -▁athletic -▁unconsciously -▁rage -cott -▁crisp -▁advertise -▁submerg -formed -▁responded -▁respondents -▁defended -olesworth -▁breed -▁breathe -istory -▁revulsion -region -intellectual -▁philip -▁appreciably -▁uniformity -▁rockets -▁physi -▁mimesis -▁encompass -▁Notte -▁exclam -▁perceptions -▁perceptual -▁mathematics -▁sentenced -▁summon -upon -▁allied -▁emptied -▁collapse -▁henri -annah -ahms -opped -▁ministers -▁Its -▁mechanisms -▁embodied -▁feeble -▁thermodynam -▁stray -▁helion -▁grop -▁ragged -▁quint -▁ambiguities --th -▁valued -▁linked -▁cheerful -▁manuscript -▁381( -▁chauff -ancholy -▁melancholy -▁attempting -▁revenge -ael -▁Michael -umumba -:30 -▁pok -▁concord -▁treason -▁trees, -▁gorge -▁blowing -▁lease -▁psychoanalytic -▁repay -aggini -▁superf -▁superficial -▁francis -▁greasy -▁i.e., -▁affects -▁Edythe -▁skimm -▁Hawk -▁Ho -▁Hoag -▁exceedingly -▁overwhelmingly -▁fragr -▁fragments -▁Armed -thlete -▁clubs -▁Quine -▁Quiney -▁dip -▁Rever -▁Reverend -▁deadlock -▁saloons -▁dominican -▁punished -▁yok -▁eyebrows -▁twisted -vest -▁exemption -▁interruption -▁Helion -▁trump -▁bump -▁thump -centive -mount -entucky -▁disabled -▁conversations -endix -▁tolerance -eredith -ithm -ithmetic -▁Meredith -ibles -▁mound -▁Hanover) -▁janitors -▁beck -tricate -▁hesitate -▁cultivated -▁favorites -▁Beck -▁sock -neck -oelect -▁86 -▁room, -▁Yankees -▁singers -▁apple -▁vain -▁Graham -▁interc -▁intercourse -▁Nadine -inute -▁symbolize -▁peel -alraux -▁Malraux -lycer -▁glycer -cherty -ouv -▁happened. -jack -▁facil -▁generously -▁Suddenly -cet -▁Spring -stall -lema -tolema -tolemaic -iciencies -▁deficiencies -▁handic -▁handicapped -▁suspension -ographical -zer -▁flee -eavor -▁endeavor -▁ending -▁wing -▁chant -▁indignant -▁compost -▁injustice -38 -▁1913 -▁avoided -oline -▁gasoline -▁swarm -▁prolonged -▁wretched -▁Garth -▁rotation -▁rotating -▁competitors -▁realistically -▁Point -▁powell -bell -addell -embr -▁pigment -aires -▁concessionaires -▁patchen -▁agglutin -▁Vill -▁Village -illery -▁artillery -▁Very -▁it, -o, -▁Then, -▁Lo -▁case, -▁blankets -▁regulus -▁contemporaries -▁Benjamin -▁benjamin -▁presbyterian -▁Presbyterian -▁bangjensen -▁woven -▁purity --help -▁self-help -▁nation's -ouk -▁roared -owell -elling -ella -▁passionate -▁entre -▁entrepr -▁entreprene -▁persistence -▁elbows -etary -▁monetary -egaton -▁dated -▁(R -▁usefulness -▁adding -▁Police -▁equivalents -▁dignified -▁termination -▁7th -aef -aefer -▁somers -page -▁Memorial -▁Mel -▁Mexico -▁preceded -▁Chamber -▁individualism -valued -▁pillow -rice -▁Mother -▁Mobile -▁seas -▁$500 -▁allev -▁busily -odes -▁alphab -▁teen- --E -▁Prop -▁michael -▁Form -▁brace -▁carto -▁pelt -▁distinguishing -▁simms -▁C. -iento -▁risen -▁throbb -▁spirited -▁killer -▁subordinates -▁correctly -▁engr -▁witnesses -▁transfers -▁respondent -▁gates -▁gn -▁gul -▁strode -erior -▁inferior -▁warrior -▁cuts -lot -▁oddlot -▁depot -▁guys -▁oath -▁senators -▁plantation -▁Player -▁exub -▁exuber -▁Ambassad -▁cunning -▁occupational -▁occupants -▁glist -▁55 -odging -▁Camp -▁slower -▁bullets -▁7070 -▁hierarchy -▁Ro -▁Road -▁mens -▁58 -verb -▁proverb -▁pointing -▁conting -▁opposing -▁discontinued -▁masses -▁attributes -▁unequ -▁protr -▁battles -▁congressmen -itan -▁denounced -▁stren -▁strenuous -▁Shu -▁trucks -unciation -▁Eight -▁dumb -▁precedent -▁unprepar -▁Raymond -▁raymond -▁rebut -ewish -▁frail -▁creates -▁creator -▁meter -ola -tenance -▁countenance -▁flatten -capable -▁Sav -▁Saxon -▁saxon -▁sed -▁suspected -▁zinc -▁zone -▁convertible -▁smelled -▁piled -▁clich -▁calculation -▁reflecting -▁westward -▁ellips -▁Bobby -▁lengths -▁handful -▁Burma -▁God's -▁Truman -▁adaptation --middle -▁Presidential -anna -▁witt -▁incons -▁incon -▁recon -▁slashed -▁hoof -undown -▁ware -▁decorative -giving -▁curved -▁ili -uxili -took -▁metropolis -▁ultracentr -▁ultracentrif -▁winds -▁engaging -gone -▁undergone -▁commencing -▁pronounce -▁Home -enson -▁"th -▁"that -▁spiral -▁russias -▁glitter -▁terrace -▁Mission -▁Mo -▁Moore -▁moore -▁wellknown -▁crystals -▁resembles -▁resembled -▁exempl -▁disinterest -acklin -▁Back -placed -▁misplaced -▁swim -▁guides -▁shifts -▁milton -▁Francis -▁edythe -▁Myra -amble -dam -▁goddam -ica -▁Madden -shot -▁mobility -▁Morr -▁Morris -▁Mil -alia -▁Thirty -▁buffer -▁municipalities -▁jeep -▁coupling -▁Far -▁alveolar -degreesc -cus -cathode -▁photocathode -▁photochemical -▁closest -▁vest -▁dock -▁postpone -roduc -▁Letch -▁bellow -▁reproduction -ilet -ttle -▁competing -▁1924 -ouvanna -▁liberals -▁cents -▁belongs -▁nights -▁etc.. -▁sixteenth -▁scal -▁prayers -▁calc -▁calcium -▁calif -▁induction -▁odd-l -▁odd-lot -▁automot -▁automotive -olitan -olving -▁prescribed -ocolate -▁chocolate -ofore -etofore -▁balloon -▁soloist -▁Having -▁neph -▁nephew -▁man, -▁manhattan -▁swamp -eel -▁Wheel -uxiliary -▁philosopher -▁prosperous -▁Progress -▁alternately -▁procedural -▁chairmen -▁orderly -▁patriotic -▁connois -▁home. -▁Farm -agh -▁pint -▁horizontal -aghty -▁allotments -▁ontological -atical -▁grammatical -beat -▁tomm -▁tomato -▁militia -▁grows -▁homogeneous -▁gripped -▁grill -▁arranging -▁hopeful -32 -▁Andrei -▁authoritarian -▁Trujillo -▁questioned -▁Seld -▁loath -▁pac -exp -expensive -▁vacations -▁favored -▁paradig -▁doubted -▁Center -▁harder -lay -▁differs -▁burma -▁ruins -▁sincerity -EAE -▁shallow -ubert -▁Assembly -▁merchandising -▁fancy -▁fighter -▁plato -▁platoon -▁Light -▁kitti -incinnati -orthy -ivanna -▁schnabel -▁Schnabel -uation -▁conservation -▁immature -▁unimportant -▁evoked -▁deserves -iscuit -bound -ieth -udie -rox -▁baptist -▁enforc -▁ensure -▁dice -▁residue -▁distrust -▁distract -▁acquaintance -▁Take -ugment -▁advancement -▁preoccupation -▁apolog -▁meltzer -▁unbrok -interrupted -▁Shore -▁erotic -ided -▁coincided -.M. -▁devise -▁constitutes -elier -▁prevailing -actical -▁addressed -▁consulted -▁thirties -▁intrins -▁brighter -▁lighter -▁securities -▁inch -▁50% -▁interlock -▁Emper -▁contributing -iscovery -tolemy -▁gland -▁hunch -▁Greece -▁Parliament -▁contours -▁ped -▁plywood -▁micrometeorite -▁sorrow -athedr -▁cathedr -▁contrace -▁discrepancies -▁Imp -▁conspicuous -ptuous -▁Lizzie -▁diaphragm -▁conclusively -▁handicap -▁injured -▁sword -▁satellites -arges -▁discharges -▁privileges -▁cleveland -ifle -▁Serge -▁galley -iances -▁appliances -▁vernon -▁landing -▁99 -▁49 -▁villain -▁identifies -▁invite -▁rendezv -▁graz -▁fortress -▁engross -▁Massachusetts -▁paced -can -▁Near -anda -▁amusement -iodot -▁salaries -▁salami -▁tackle -▁warriors -▁counteract -▁architectural -▁declining -affold -▁scaffold -▁slot -▁Op -pelier -▁fingerprint -▁rescued -▁subdued -▁irregularities -▁blaz -▁cove -▁utmost -rmost -udsons -▁hudsons -▁protrud -▁heroes -▁sites -oric -renic -▁olive -▁gait -▁multiplicity -rome -egan -▁deegan -▁deaf -▁forego -▁nineteenthcentury -▁theatre -▁borders -▁borden -▁Buck -▁Berger -ocket -▁monuments -▁Constant -▁enduring -▁lest -arted -▁1861 -▁bewild -▁Now, --state -▁desolate -▁fascination -▁pinpoint -▁persuas -▁accessories -▁1/ -▁duly -▁bankruptcy -uggage -▁luggage -▁interfaith -▁Decl -▁Declar -rotic -▁neurotic --two -▁himself, -▁entity -▁Borden -andy -▁diversion -▁mahayana -▁Dean -▁clues -▁statues -AM -▁Make -▁intake -▁rebuff -▁slick -marine -ubmarine -▁antisubmarine -▁fifties -▁xr -▁correction -▁somersault -▁Got -▁populations -▁schedules --level -▁pric -▁traveler -negie -▁secrecy -▁indo -DC -▁boxcar -▁novels -product -ps -psam -▁piepsam -▁papas -▁buffet -▁stunn -▁surgeon -▁brig -▁clamber -▁managerial -▁vicinity -▁Mountain -▁Main -▁lurk -▁followers -mile -cession -▁doses -makers -▁narc -▁Ref -▁Rourke -ormal -▁Standard -▁Nassau -▁nassau -▁agony --D -▁scoop -connected -▁Thank -▁Build -▁pamela -▁pa -▁pad -▁geneva -▁biwa -▁beliefs -▁Prairie -▁underneath -▁dictates -▁fuck -▁Chair -▁Chairman -man's -▁endow -trow -astrow -▁heavens -▁1933 -▁housew -▁houses, -bee -▁Room -acob -▁scenic -▁rodd -▁terrestrial -▁newcom -▁cruc -▁librarians -▁sensational -▁aunt -▁titled -▁antithyroid -▁arguing -ellowship -eur -▁comparisons -illac -▁unrec -▁passive -▁sympathize -▁Steinberg -enberg -▁steinberg -61 -▁declare -▁beaten -▁120 -▁boycott -▁humm --Western -▁ske -▁skelet -▁wrinkles -▁manners -▁enjoying -ingham -irmingham -▁Maude -▁antip -▁wartime -▁Means -▁scrutin -▁inflection -ollection -▁recollection -▁fundamentally -▁bitterly -▁displayed -▁displays -▁Boy -▁therapist -▁Planning -oelectric -olese -▁quote -▁initially -000000 -▁subtract -ism, -quarters -colored -▁landmark -▁sustained -▁accompaniment -▁souls -▁interlob -▁interlobular -▁farmhouse -▁lime -essert -▁dessert -ibet -▁anguish -children -▁grandchildren -icken -▁oxen -▁proven -▁drunken -agen -▁Handley -▁tart -▁maude -▁mexico -▁lizzie -▁practicing -▁certific -▁cobb -▁anyhow -▁cady -▁Cady -▁trifle -▁pulp -▁mother's -▁Pete -outheastern -▁manual -▁Tommy -▁tommy -▁reconc -▁designer -▁discon -shore -▁Congolese -▁unto -▁cadillac -esirable -▁undesirable -▁erupted -▁unreason -▁Cor -▁bog -▁greece -▁Ow -unted -▁grunted -▁grap -▁bour -▁feminine -▁throwing -▁bugg -▁braz -▁sprung -▁lessons -▁bug -▁dill -▁dc -▁Conc -acles -▁obstacles -▁improvis -▁"We -▁pollen -▁pollution -▁presents -interpre -▁misinterpre -▁outline -▁differed -▁knit -opular -▁schools, -▁parameters -▁idyll -▁catholics -ilian -▁conceptions -▁totalitarian -▁xray -▁stretching -aring -▁propagation -▁Lalaurie -▁temperament -▁vitally -▁killing -▁posts -▁physics -▁Katang -▁recreational -▁bacteria --stage -▁megat -▁megatons -▁stole -▁ordinance -▁benefact -▁benevol -▁grease -▁Katie -▁Free -▁manipulation -▁manipulate -▁arab -▁pall -▁hesitation -▁sanitation -▁hoop -▁fluids -▁identifiable -▁howard -▁Yalta -▁yalta -▁deterministic -▁dugout -▁witches -▁B.C. -▁convicted -▁Arch -▁propulsion -▁Men -▁Mass -▁harass -assion -▁disclosed -▁anthrop -▁Claude -▁fats -emics -▁morphophonemics -▁flashed -▁lighted -▁trevely -▁treach -▁pulse -▁aged -▁admiration -▁thermometers -▁Minn -▁Ma -▁lin -▁tablespoon -▁histories -ft -fold -▁tactical -▁foams -▁victims -▁sau -▁carroll -74 -▁throttle -▁outraged -▁ranks -▁disciples -▁alban -▁perilous -ousness -▁affiliated -▁knives -▁Power -▁Empire -▁stool -▁psychoanalysis -▁mouse -▁Cuban -iroshima -phosph -osphorus -▁lalaurie -▁Seventh -▁bruised -▁seep -▁Case -▁chase -activity -▁maker -▁others. -▁inconven -idges -▁proves -awaiian -▁Others -▁laz -▁glaz -idency -▁intimid -▁iliad -emia -▁logically -▁parameter -▁maggie -▁Marshal -▁immediacy -▁rendezvous -▁slapped -iature -▁miniature -▁Jon -▁compassion -aggie -▁Maggie -ieves -iepsam -ieving -▁excer -▁excerpt -erated -educated -▁collaborated -▁collaboration -▁sensation -▁lacked -3) -▁(3) -▁pseudo -ebread -▁Alread -▁cromwell -▁Cromwell -▁recourse -▁hip -▁katie -▁expedient -▁provoked -gust -▁disgust -▁Minister -▁kr -▁restore -▁packing -▁mg/ -anthrop -▁philanthrop --surface -▁szold -▁Szold -▁needed. -▁ire -▁aggregate -▁coordinate -▁skeptical -▁burdens -▁delph -▁emerging -▁grove -▁offset -,500 -▁Mahayana -▁vulgar -▁tablespoons -chair -▁groom -upiter -▁Remember -▁squall -▁freely -▁anglican -▁Anglican -▁digest -▁digging -ture -▁sculpture -▁Teach -▁distal -▁shelves -▁garbage -▁congenial -▁chewing -moil -▁turmoil -▁Greene -▁embassy -▁analogy -▁unnatural -▁ventil -▁deliberations -▁collapsed -34 -onnor -ylkill -ylan -▁earnestly -▁74 -▁repud -▁packaging -▁distinctly -▁unmarried -▁words. -▁crafts -▁Joan -▁Picasso -▁(about -▁narrator -▁narrower -▁proteins -▁combining -▁proportionate -bishop -▁miracles -▁England, -ifty -▁negotiation -▁rag -▁thief -▁embarrassment -▁laundry -▁redcoat -▁restoration -▁restorative -▁exaggerate -▁magnificently -ahn -▁ga -▁gaiety -▁nov --looking -▁Sur -▁pushing -▁leaning -▁fake -▁ferv -▁fel -▁pearl -▁uncommon -▁selfd -▁shores -rophores -▁repeal -▁prowestern -▁leav -oque -▁baroque -▁barco -avern -▁Nov -▁hunger -▁uneas -▁uneven -▁Record -▁Recently -▁Determ -▁Determine -▁cyclist -▁enclosure -▁discard -uard -▁(Sept -▁tin -▁rides -▁whipped -▁eclips -▁cath -▁hands. -▁ballroom -▁floors -▁tips -▁grokk -▁alk -▁alkal -▁Stal -▁Stalin -▁shrill -▁Arlene -▁griev -imburse -▁reimburse -▁penetrated -▁cortex -allic -▁metallic -▁unstable -avan -▁brav -▁bravado -▁brist -istent -▁smoothed -▁gospel -▁quirt -▁harness -ules -ullet -▁thirtyfive -▁lately -▁pilgrimage -▁duke -▁"You -▁equations -ations, -▁interrelations -▁taut -▁conjugate -▁unadjusted -▁lowered -▁104 -lington -▁Festival -▁safeg -▁safeguard -▁denver -lain -▁Plain -▁crops -▁2:0 -▁2:30 -90 -▁facade -▁attendance -▁delighted -▁(ex -▁(exclud -uylkill -▁States. -▁1914 -urp -urpose -▁courty -▁Wr -▁gamb -▁semantic -▁indeb -▁indebted -▁indecent -▁bolted -▁emancipation -▁Word -▁knuckles -▁ecstasy -▁Owen -▁masculine -▁accepting --spe -▁chauffeur -▁nineteenth-century -▁synd -▁syndic -▁Stevie -▁Stuart -▁jub -▁bald -den -▁scrub -yrate -▁alumni -▁teamsters -▁photographic -rais -▁Adjust -▁designs -▁weighed -nament -man, -clerk -auclerk -▁Advis -▁deciding -▁decides -▁ideally -▁Pierre -▁pierre -▁instituted -▁enters -▁AI -▁thoughtfully -▁uran -▁uranium -▁corros -rosse -▁radius -▁jokes -▁18th -▁19th -thood -▁garth -▁steal -▁York, -▁relied -entine -▁quine -▁quiney -▁glancing -▁supposedly -▁consequent -▁profile -atharine -son, -▁joining -▁Ambassador -▁impractical -▁Pohl -▁paragraphs -▁applicant -▁resultant -▁exhausted -▁elevated -▁Trans -▁splin -▁cooperating -▁fertiliz -▁arlene -▁ost -▁Horn -▁History -▁one's -lemish -▁reassuring -ourning -▁mourning -▁Love -▁glove -▁glendora -udged -▁surged -▁Timothy -▁sponsorship -▁treasurer -▁rider -▁terrifying -box -▁scent --owned -▁amplitude -▁amplifier -▁analyses -▁impulses -▁compilation -▁induced -▁dared -▁defenses -▁madden -▁embassies -▁magnetism -▁gracefully -▁spherical -▁beckon -▁foremost -▁habits -▁whirling -▁huddled -▁nuts -▁suburbs -▁drinks -▁shadows -▁meanings -▁Express -▁utilities -▁Claim -▁knelt -▁construed -agued -▁wholesale -▁Agricult -▁novelties -▁viewed -lements -▁elong -▁loving -▁incurred -▁slice -▁bailiff -▁1: -ota -▁finishing -eltzer -▁Too -▁pohl -▁institutional -▁broadening -▁terraces -▁terrified -▁beset -real -▁cracked -▁kicked -▁staggered -▁discriminating -aternal -cos -cest -▁ancest -▁ancestor -osium -▁symposium -▁sympathet -▁coronary -▁wealthy -partisan -▁parliamentary -▁1859 -▁expects -▁williams -▁ethic -▁rhythmic -filled -onghold -ounding -▁rim -▁spotlight -▁vex -▁premises -▁coerc -▁coin -otion -▁commotion -▁commenced -▁temptation -▁gigg -▁connoisse -▁rookie -▁psychother -▁proxim -▁Advisory -▁sensory -▁sealed -▁negotiating -▁inconsistent -urities -▁impurities -▁velocities -▁sweeping -▁impose -▁honorable -▁achieving -etically -▁hours. -▁potentially -▁Powell -▁Mos -▁fork -▁ascertain -▁cater --making -▁(as -asper -▁exasper -▁exquisite -▁concentrates -▁padd -▁(H -▁Van -▁imports --cl -▁esthetic -▁solving -▁cape -▁catastrophe -idity -▁humidity -acity -▁humil -▁coil -▁tid -ers' -'' -''" -▁McF -▁proceeding -▁dose -▁germanium -▁susceptible -▁Abel -▁Us -▁Usually -▁notably -▁config -▁configur -▁premise -▁nadine -▁gyp -eramic -▁ceramic -▁uphold -hard -▁Burton -▁rake -▁later, -▁southward -▁Protestants -▁framed -ordon -▁threaten -▁eaten -ponsored -▁promotional -▁presidency -▁disorganiz -▁loot -▁wis -wall -▁"in -ieur -▁satir -▁milling -▁failing -▁adolesc -▁adolescents -cin -uxley -inley -▁McKinley -▁teen-ag -agan -▁strokes -▁flair -▁Northwest -▁Meas -▁peasant -▁tolerant -▁selecting -▁ditch -▁inherited -▁innov -▁H. -▁repuls -▁richmond -▁Could -▁presiding -▁room. -▁halt -▁Bishop -leveland -▁Cleveland -▁Pamela -▁Piepsam --down -▁directions -ressed -▁Di -▁Dill -▁visibly -▁61 -▁retiring -▁retre -▁retreated -▁17th -▁fifteenth -arth -arthy -▁versions -▁diarr -ulletin -▁radish -▁compression -committ -▁Delph -eliness -elle -xel -▁drexel -▁hurr -▁hurrican -▁insights -▁inspector --colored -▁diverg -▁stevie -▁stuart -bright -llustr -▁stunning -▁drawings -▁Allied -▁Design -ensible -▁banner -▁usage -▁trustee -iere -▁jerked -▁compliance -▁evangelism -▁mysteries -s" -▁drought -▁skipped -▁baptized -▁homes. -▁butyrate -▁catcher -▁replaces -▁sunshine -rapy -llan -▁necessitate -▁facilitate -▁monotony -▁pony -▁quart -▁passions -houn -▁pronoun -▁quadric -▁owned -▁jeal -▁jealous -▁sensed -ospitable -▁actively -▁ireland -▁hillsboro -▁feudal -tral -str -▁hemorr -▁Torr -▁Torrio -▁B. -▁triumphantly -▁Hamrick -▁destroying -▁housed -haped -represent -▁booth -▁jewel -▁spontane -fiction -faced --tr -▁unhappily -▁moderately -▁intimately -▁Porter -▁monitoring -keeping -▁discoveries -▁galaxies -▁nickel -▁sediment -▁pirates -▁alg -▁algae -icted -▁underdeveloped -▁praised -▁sins -antically -▁frantically -▁chronological -▁confine -▁confed -▁confederate --filled -▁child's -▁striving -▁oysters -▁oyster -▁"not -▁cel -owski -▁mandatory -iates -▁burr -▁Library -▁Lublin -▁Export-Im -▁Export-Import -▁deploy -▁interviews -▁librarian -▁Been -emen -▁dissem -▁dissolve -▁pier -▁multi -itory -▁bellig -▁belligerent -pirited -▁edited -▁dictator -erator -▁distinctions -▁picasso -▁decad -▁Acad -▁Academ -▁Rh -hole -hous -▁warehous -▁uly -▁unduly -▁artificially -▁Juanita -▁crad -▁Columbia -▁ray --ray -▁trees. -▁authorizations -▁screaming -▁orthodontist -▁Invest -▁Electric -orical -▁explode -▁Code -▁Coun -▁customs -▁bos -▁bosom -omatic -onduct -endell -▁heels -▁F. -▁freezing -▁bathing -▁controls -▁ap -▁prophec -▁rub -▁meteorites -aining -▁implication -▁obtainable -osa -ucosa -▁expended -▁Shaw -▁outward -▁selections -▁Charlotte -ormick -▁None -▁havana -▁embarrassing -▁impaired -gg -▁disbelief -▁superimposed -existent -▁endorse -▁snatched -▁smother -▁proceeds -▁abus -▁flourished -▁Southeast -ubby -rosby -▁frankly -security -▁presentday -▁merge -▁ambul -ulence -▁vowel -▁experimentation -▁aids -▁Freud -ges. -▁nostalg -givanna -cinol -▁guiding -▁humiliation -▁acknowledge -▁pennant -▁Wally -▁Gabriel -▁cuban -▁columbia -▁170 -▁lash -▁sensibility -▁scraw --plane -▁dressing -▁spencer -onder -▁maneuvers -▁abundant -▁junct -rect -▁contemptuous -▁Feather -▁Feb -▁exclusion -▁1890 -▁1896 -▁incub -on, -ona -onious -aac -▁Isaac --step -▁rocks -▁ruthless -▁burgl -▁premiere -▁quill -▁quiver -▁crossroads -▁rows -▁pacif -▁prolif -▁Delphine -▁tanks -▁Lucille -▁sympathies -▁costume -▁yourselves -▁Concord -▁ultra -▁Sixties -▁year's -▁(C) -▁Marx -▁alleged -▁cleared -▁sighed -▁affixed -▁mailed -▁seced -▁juanita -▁genuinely -▁itself. -▁yelled -▁confirmed -ckinley -▁irritation -▁millenn -▁irradiated -▁disconcer -▁collecting -▁roam -▁demythologization -izational -▁civilizational -▁civilized -▁dissent -▁shocked -▁flowing -▁Similarly, -iodoty -iodotyrosine -▁country. -▁redcoats -▁instructed -▁instructor -rise -▁sunrise -anchise -▁sidewise -▁traditionally -▁(AP -▁dese -▁desegregated -▁1925 -▁Twenty -▁evaluating -▁heating -▁investigating -▁(Af -instein -▁compulsion -▁awarded -▁guards -lymouth -▁tumors -▁gib -▁preferences -▁relates -▁examiner -▁mind. -▁lavish -▁ties -▁ration -▁Salem --degrees-F -▁winchester -▁Fulton -▁fulton -▁focal -▁speculate -▁indecision -▁judged -▁judging -grily -▁1832 -▁52 -▁biscuit -itudes -▁budd -▁Agriculture -▁dusk -▁graduating -irov -crack -▁Jessica -▁swallowed -▁easiest -degree -ioles -elley -▁Shelley -▁Show -▁grams -ilt -▁revolt -▁tonal -▁penetrate -▁pessimistic -▁Yok -▁breakthrough -▁Claire -▁repression -▁Feathertop -▁screwed -▁sie -▁sore -▁prud -stiqlal -▁1960, -▁insolence -▁Measure diff --git a/lingvo/tasks/punctuator/params/codelab.py b/lingvo/tasks/punctuator/params/codelab.py deleted file mode 100644 index cbccff13..00000000 --- a/lingvo/tasks/punctuator/params/codelab.py +++ /dev/null @@ -1,125 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Params file for punctuator codelab.""" - -import os -from lingvo import model_registry -import lingvo.compat as tf -from lingvo.core import base_model_params -from lingvo.tasks.mt import base_config -from lingvo.tasks.punctuator import input_generator -from lingvo.tasks.punctuator import model - - -# This base class defines parameters for the input generator for a specific -# dataset. Specific network architectures will be implemented in subclasses. -class BrownCorpusWPM(base_model_params.SingleTaskModelParams): - """Brown Corpus data with a Word-Piece Model tokenizer.""" - - # Generated using - # lingvo/tasks/punctuator/tools:download_brown_corpus. - _DATADIR = '/tmp/punctuator_data' - _VOCAB_FILE = tf.resource_loader.get_path_to_datafile( - 'brown_corpus_wpm.16000.vocab') - # _VOCAB_SIZE needs to be a multiple of 16 because we use a sharded softmax - # with 16 shards. - _VOCAB_SIZE = 16000 - - def Train(self): - p = input_generator.PunctuatorInput.Params() - p.file_pattern = 'text:' + os.path.join(self._DATADIR, 'train.txt') - p.file_random_seed = 0 # Do not use a fixed seed. - p.file_parallelism = 1 # We only have a single input file. - - # The bucket upper bound specifies how to split the input into buckets. We - # train on sequences up to maximum bucket size and discard longer examples. - p.bucket_upper_bound = [10, 20, 30, 60, 120] - - # The bucket batch limit determines how many examples are there in each - # batch during training. We reduce the batch size for the buckets that - # have higher upper bound (batches that consist of longer sequences) - # in order to prevent out of memory issues. - # Note that this hyperparameter varies widely based on the model and - # language. Larger models may warrant smaller batches in order to fit in - # memory, for example; and ideographical languages like Chinese may benefit - # from more buckets. - p.bucket_batch_limit = [512, 256, 160, 80, 40] - - p.tokenizer.vocab_filepath = self._VOCAB_FILE - p.tokenizer.vocab_size = self._VOCAB_SIZE - p.tokenizer.pad_to_max_length = False - - # Set the tokenizer max length slightly longer than the largest bucket to - # discard examples that are longer than we allow. - p.source_max_length = p.bucket_upper_bound[-1] + 2 - p.target_max_length = p.bucket_upper_bound[-1] + 2 - return p - - # There is also a Dev method for dev set params, but we don't have a dev set. - def Test(self): - p = input_generator.PunctuatorInput.Params() - p.file_pattern = 'text:' + os.path.join(self._DATADIR, 'test.txt') - p.file_random_seed = 27182818 # Fix random seed for testing. - # The following two parameters are important if there's more than one input - # file. For this codelab it doesn't actually matter. - p.file_parallelism = 1 # Avoid randomness in testing. - # In order to make exactly one pass over the dev/test sets, we set buffer - # size to 1. Greater numbers may cause inaccurate dev/test scores. - p.file_buffer_size = 1 - - p.bucket_upper_bound = [10, 20, 30, 60, 120, 200] - p.bucket_batch_limit = [16] * 4 + [4] * 2 - - p.tokenizer.vocab_filepath = self._VOCAB_FILE - p.tokenizer.vocab_size = self._VOCAB_SIZE - p.tokenizer.pad_to_max_length = False - - p.source_max_length = p.bucket_upper_bound[-1] + 2 - p.target_max_length = p.bucket_upper_bound[-1] + 2 - return p - - -# This decorator registers the model in the Lingvo model registry. -# This file is lingvo/tasks/punctuator/params/codelab.py, -# so the model will be registered as punctuator.codelab.RNMTModel. -@model_registry.RegisterSingleTaskModel -class RNMTModel(BrownCorpusWPM): - """RNMT+ Model.""" - - def Task(self): - p = base_config.SetupRNMTParams( - model.RNMTModel.Params(), - name='punctuator_rnmt', - vocab_size=self._VOCAB_SIZE, - embedding_dim=1024, - hidden_dim=1024, - num_heads=4, - num_encoder_layers=6, - num_decoder_layers=8, - learning_rate=1e-4, - l2_regularizer_weight=1e-5, - lr_warmup_steps=500, - lr_decay_start=400000, - lr_decay_end=1200000, - lr_min=0.5, - ls_uncertainty=0.1, - atten_dropout_prob=0.3, - residual_dropout_prob=0.3, - adam_beta2=0.98, - adam_epsilon=1e-6, - ) - p.eval.samples_per_summary = 2466 - return p diff --git a/lingvo/tasks/punctuator/params/params.py b/lingvo/tasks/punctuator/params/params.py deleted file mode 100644 index 312e810a..00000000 --- a/lingvo/tasks/punctuator/params/params.py +++ /dev/null @@ -1,21 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Punctuator models hyper-parameters.""" - -# Import ModelParams to ensure that they are added to the global registry. -# pylint: disable=unused-import -import lingvo.tasks.punctuator.params.codelab -# pylint: enable=unused-import diff --git a/lingvo/tasks/punctuator/tools/BUILD b/lingvo/tasks/punctuator/tools/BUILD deleted file mode 100644 index 69e2a824..00000000 --- a/lingvo/tasks/punctuator/tools/BUILD +++ /dev/null @@ -1,15 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_binary( - name = "download_brown_corpus", - srcs = ["download_brown_corpus.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - ], -) diff --git a/lingvo/tasks/punctuator/tools/__init__.py b/lingvo/tasks/punctuator/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tasks/punctuator/tools/download_brown_corpus.py b/lingvo/tasks/punctuator/tools/download_brown_corpus.py deleted file mode 100644 index 9c558255..00000000 --- a/lingvo/tasks/punctuator/tools/download_brown_corpus.py +++ /dev/null @@ -1,140 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Downloads and processes the Brown Corpus (http://www.nltk.org/nltk_data).""" - -import os -import random -import string -from xml.etree import ElementTree -import lingvo.compat as tf - -tf.flags.DEFINE_string("outdir", "/tmp/punctuator_data", - "The output directory.") - -FLAGS = tf.flags.FLAGS - - -def main(_): - tf.logging.set_verbosity(tf.logging.INFO) - - basename = "brown_tei" - fname = basename + ".zip" - url = ("https://raw.githubusercontent.com/nltk/nltk_data/" + - "gh-pages/packages/corpora/" + fname) - sha256 = "335bec1ea6362751d5d5c46970137ebb01c80bf7d7d75558787729d275e0a687" - - tf.keras.utils.get_file( - fname, url, file_hash=sha256, cache_subdir=FLAGS.outdir, extract=True) - - tf.logging.info("\nDownload completed. Preprocessing...") - - with open(os.path.join(FLAGS.outdir, basename, "Corpus.xml"), "r") as xml: - root = ElementTree.fromstring(xml.read().replace( - 'xmlns="http://www.tei-c.org/ns/1.0"', "")) - sentences = [] - for sentence in root.findall("./TEI/text/body/p/s"): - # Example input sentence: - # The Fulton - # County Grand - # Jury said - # Friday an investigation - # of Atlanta's recent - # primary election - # produced `` no - # evidence '' that - # any irregularities - # took place . - # Example output text: - # The Fulton County Grand Jury said Friday an investigation of Atlanta's - # recent primary election produced "no evidence" that any irregularities - # took place. - text = "" - prepend_space = False - for child in sentence: - if child.tag == "w": - if prepend_space: - text += " " - text += child.text - prepend_space = True - elif child.tag == "c": - if child.text == "``": - if prepend_space: - text += " " - text += '"' - prepend_space = False - elif child.text == "''": - text += '"' - prepend_space = True - elif child.text == "'": - if prepend_space: - text += " '" - prepend_space = False - else: - text += "'" - prepend_space = True - elif child.text == "(" or child.text == "[": - if prepend_space: - text += " " - text += child.text - prepend_space = False - elif child.text == "-" or child.text == "--": - if prepend_space: - text += " " - text += child.text - prepend_space = True - else: - text += child.text - prepend_space = True - text = text.replace("!!", "!").replace("??", "?").replace("--", "-") - text = text.replace("**", "*").replace(";;", ";").replace("::", ":") - text = text.replace(",,", ",") - - # Filter out bad sentences. - if not set(text) & set(string.ascii_letters): - # No letters. - continue - if text.count('"') % 2 != 0: - # Uneven number of quotes. - continue - if text.count("(") != text.count(")") or text.count("[") != text.count("]"): - # Unbalanced parenthesis. - continue - if (text[0] == '"' and text[-1] == '"' or - text[0] == "(" and text[-1] == ")" or - text[0] == "[" and text[-1] == "]"): - text = text[1:-1] - if text[0] not in string.ascii_letters and text[0] not in string.digits: - # Doesn't start with a letter or number. - continue - text = text[:1].upper() + text[1:] - sentences.append(text) - sentences = sorted(set(sentences)) - random.seed(1234) - random.shuffle(sentences) - - with open(os.path.join(FLAGS.outdir, "train.txt"), "w") as f: - for line in sentences[:int(len(sentences) * 0.95)]: - f.write("%s\n" % line) - - with open(os.path.join(FLAGS.outdir, "test.txt"), "w") as f: - for line in sentences[int(len(sentences) * 0.95):]: - f.write("%s\n" % line) - - tf.logging.info("All done.") - - -if __name__ == "__main__": - tf.app.run(main) diff --git a/lingvo/tools/BUILD b/lingvo/tools/BUILD deleted file mode 100644 index 005f3641..00000000 --- a/lingvo/tools/BUILD +++ /dev/null @@ -1,209 +0,0 @@ -load( - "//lingvo:lingvo.bzl", - "lingvo_cc_binary", -) - -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -py_binary( - name = "gke_launch", - srcs = ["gke_launch.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - # Implicit absl.app dependency. - # Implicit absl.flags dependency. - # Implicit six dependency. - # Implicit yaml dependency. - ], -) - -py_binary( - name = "keras2ckpt", - srcs = ["keras2ckpt.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - ], -) - -py_binary( - name = "bpe_word_tokenizer", - srcs = ["bpe_word_tokenizer.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = ["//lingvo:compat"], -) - -py_binary( - name = "wpm_encode_file", - srcs = ["wpm_encode_file.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":wpm_encode_file_lib", - # Implicit six dependency. - ], -) - -py_library( - name = "wpm_encode_file_lib", - srcs = ["wpm_encode_file.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo/core:wpm_encoder", - # Implicit numpy dependency. - # Implicit six dependency. - ], -) - -py_binary( - name = "print_tf_records", - srcs = ["print_tf_records.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - # Implicit six dependency. - ], -) - -py_binary( - name = "compute_stats", - srcs = ["compute_stats.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - # Implicit numpy dependency. - ], -) - -py_library( - name = "audio_lib", - srcs = ["audio_lib.py"], - srcs_version = "PY3", - deps = [ - # Additional FFT kernels dependency. - "//lingvo:compat", - "//lingvo/core:py_utils", - "//lingvo/tasks/asr:frontend", - ], -) - -py_library( - name = "beam_utils", - srcs = ["beam_utils.py"], - srcs_version = "PY3", -) - -py_test( - name = "beam_utils_test", - srcs = ["beam_utils_test.py"], - data = ["//lingvo/tasks/mt/testdata:wmt14_ende_tfexample"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":beam_utils", - # Implicit apache_beam dependency. - "//lingvo:compat", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - ], -) - -py_binary( - name = "count_records", - srcs = ["count_records.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":beam_utils", - # Implicit network file system dependency. - # Implicit apache_beam dependency. - ], -) - -py_test( - name = "audio_lib_test", - srcs = ["audio_lib_test.py"], - data = [ - "//lingvo/tools/testdata:audio_data", - ], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":audio_lib", - "//lingvo:compat", - "//lingvo/core:test_helper", - "//lingvo/core:test_utils", - ], -) - -py_library( - name = "compare_params_lib", - srcs = ["compare_params.py"], - srcs_version = "PY3", - deps = [ - "//lingvo:compat", - "//lingvo:model_registry", - # Implicit six dependency. - ], -) - -py_test( - name = "compare_params_test", - srcs = ["compare_params_test.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":compare_params_lib", - "//lingvo:compat", - "//lingvo/core:hyperparams", - "//lingvo/core:test_utils", - ], -) - -py_binary( - name = "create_asr_features", - srcs = ["create_asr_features.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":audio_lib", - "//lingvo:compat", - ], -) - -py_binary( - name = "create_peoples_speech_asr_features", - srcs = ["create_peoples_speech_asr_features.py"], - python_version = "PY3", - srcs_version = "PY3", - deps = [ - ":audio_lib", - "//lingvo:compat", - ], -) - - -lingvo_cc_binary( - name = "generate_proto_def", - srcs = ["generate_proto_def.cc"], - deps = [ - # Implicit proto dependency. - # Implicit tensorflow C++ proto dependency. - ], -) - -sh_binary( - name = "generate_tf_dot_protos", - srcs = ["generate_tf_dot_protos.sh"], - data = [":generate_proto_def"], -) diff --git a/lingvo/tools/__init__.py b/lingvo/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lingvo/tools/audio_lib.py b/lingvo/tools/audio_lib.py deleted file mode 100644 index 0f8f7449..00000000 --- a/lingvo/tools/audio_lib.py +++ /dev/null @@ -1,150 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Audio library.""" - -import shlex -import subprocess -from tempfile import NamedTemporaryFile -import lingvo.compat as tf -from lingvo.core import py_utils -from lingvo.tasks.asr import frontend as asr_frontend - -from tensorflow.python.ops import gen_audio_ops as audio_ops # pylint: disable=g-direct-tensorflow-import - - -# There are two ways to decode a wav in tensorflow: -# Through the tensorflow native audio decoder, exported -# via framework, or via tf.contrib.ffmpeg.decode_audio. -# While the latter could technically support FLAC, it does -# not. It also adds an extra dependency on ffmpeg. - -def DecodeToWav(input_bytes, fmt): - with NamedTemporaryFile() as fh: - cmd = f'sox -t {fmt} - -t wav --channels 1 --rate 16000 --encoding signed --bits 16 {fh.name}' - p = subprocess.Popen(shlex.split(cmd), - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - _, err = p.communicate(input=input_bytes) - assert p.returncode == 0, err - out = fh.read() - return out - - -def DecodeFlacToWav(input_bytes): - """Decode a FLAC byte string to WAV.""" - p = subprocess.Popen( - ['sox', '-t', 'flac', '-', '-t', 'wav', '-'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out, err = p.communicate(input=input_bytes) - assert p.returncode == 0, err - return out - - -def DecodeWav(input_bytes): - """Decode a wav file from its contents. - - Args: - input_bytes: a byte array or Tensor with the wav file contents. - - Returns: - A pair of Tensor for sample rate, decoded samples. - """ - result = tf.audio.decode_wav(input_bytes) - return result.sample_rate, result.audio - -def read_wave_via_scipy(string_bytes): - from scipy.io.wavfile import read as wavread - import io - import numpy as np - b = string_bytes.numpy() - sample_rate, wav_file = wavread(io.BytesIO(b)) - wav_file = wav_file[:, np.newaxis] - assert wav_file.dtype == np.int16 - normalized = wav_file.astype(np.float32) * (1.0 / (1 << 15)) - assert normalized.max() <= 1.0 - assert normalized.min() >= -1.0 - return sample_rate, normalized - -""" -This should be bit-for-bit compatible with the output of DecodeWav. However, -there is no unit-test for that at the moment. -""" -def DecodeWavPyFunc(input_bytes): - sample_rate, audio = tf.py_function(read_wave_via_scipy, [input_bytes], [tf.int32, tf.float32]) - sample_rate = tf.ensure_shape(sample_rate, []) - audio = tf.ensure_shape(audio, [None, 1]) - return sample_rate, audio - - -def AudioToMfcc(sample_rate, audio, window_size_ms, window_stride_ms, - num_coefficients): - window_size_samples = sample_rate * window_size_ms // 1000 - window_stride_samples = sample_rate * window_stride_ms // 1000 - spectrogram = audio_ops.audio_spectrogram( - audio, - window_size=window_size_samples, - stride=window_stride_samples, - magnitude_squared=True) - mfcc = audio_ops.mfcc( - spectrogram, sample_rate, dct_coefficient_count=num_coefficients) - return mfcc - - -def ExtractLogMelFeatures(wav_bytes_t): - """Create Log-Mel Filterbank Features from raw bytes. - - Args: - wav_bytes_t: Tensor representing raw wav file as a string of bytes. It is - currently assumed that the wav file is encoded at 16KHz (see DecodeWav, - below). - - Returns: - A Tensor representing three stacked log-Mel filterbank energies, sub-sampled - every three frames. - """ - - # We want to use these parameters exactly. - def _CreateAsrFrontend(): - """Parameters corresponding to default ASR frontend.""" - p = asr_frontend.MelAsrFrontend.Params() - p.sample_rate = 16000. - p.frame_size_ms = 25. - p.frame_step_ms = 10. - p.num_bins = 80 - p.lower_edge_hertz = 125. - p.upper_edge_hertz = 7600. - p.preemph = 0.97 - p.noise_scale = 0. - p.pad_end = False - return p.Instantiate() - - sample_rate, audio = DecodeWav(wav_bytes_t) - audio *= 32768 - # Remove channel dimension, since we have a single channel. - audio = tf.squeeze(audio, axis=1) - # TODO(drpng): make batches. - audio = tf.expand_dims(audio, axis=0) - static_sample_rate = 16000 - mel_frontend = _CreateAsrFrontend() - with tf.control_dependencies( - [tf.assert_equal(sample_rate, static_sample_rate)]): - outputs = mel_frontend.FPropDefaultTheta( - py_utils.NestedMap(src_inputs=audio, paddings=tf.zeros_like(audio))) - log_mel = outputs.src_inputs - return log_mel diff --git a/lingvo/tools/audio_lib_test.py b/lingvo/tools/audio_lib_test.py deleted file mode 100644 index c4165650..00000000 --- a/lingvo/tools/audio_lib_test.py +++ /dev/null @@ -1,90 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for audio_lib.""" - -import lingvo.compat as tf -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tools import audio_lib - -# The testdata contains: (soxi .../gan_or_vae.wav) -# Channels : 1 -# Sample Rate : 24000 -# Precision : 16-bit -# Duration : 00:00:03.16 = 75900 samples ~ 237.188 CDDA sectors - - -class AudioLibTest(test_utils.TestCase): - - def testDecodeFlacToWav(self): - with open( - test_helper.test_src_dir_path('tools/testdata/gan_or_vae.wav'), - 'rb') as f: - wav = f.read() - with open( - test_helper.test_src_dir_path('tools/testdata/gan_or_vae.flac'), - 'rb') as f: - flac = f.read() - tf.logging.info('flac: %d bytes', len(flac)) - try: - converted = audio_lib.DecodeFlacToWav(flac) - tf.logging.info('wav: %d bytes, converted: %d bytes', len(wav), - len(converted)) - self.assertEqual(wav, converted) - except OSError: - # sox is not installed, ignore this test. - pass - - def testDecodeWav(self): - with open( - test_helper.test_src_dir_path('tools/testdata/gan_or_vae.wav'), - 'rb') as f: - wav = f.read() - with self.session(): - sample_rate, audio = self.evaluate(audio_lib.DecodeWav(wav)) - self.assertEqual(24000, sample_rate) - self.assertEqual(75900, len(audio)) - - def testAudioToMfcc(self): - with open( - test_helper.test_src_dir_path('tools/testdata/gan_or_vae.wav'), - 'rb') as f: - wav = f.read() - sample_rate, audio = audio_lib.DecodeWav(wav) - static_sample_rate = 24000 - mfcc = audio_lib.AudioToMfcc(static_sample_rate, audio, 32, 25, 40) - with self.session(): - audio_sample_rate, mfcc = self.evaluate([sample_rate, mfcc]) - assert audio_sample_rate == static_sample_rate - self.assertAllEqual(mfcc.shape, [1, 126, 40]) - - def testExtractLogMelFeatures(self): - with open( - test_helper.test_src_dir_path('tools/testdata/gan_or_vae.16k.wav'), - 'rb') as f: - wav = f.read() - - wav_bytes_t = tf.constant(wav, dtype=tf.string) - log_mel_t = audio_lib.ExtractLogMelFeatures(wav_bytes_t) - - with self.session(): - log_mel = self.evaluate(log_mel_t) - # Expect 314, 80 dimensional channels. - self.assertAllEqual(log_mel.shape, [1, 314, 80, 1]) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tools/beam_utils.py b/lingvo/tools/beam_utils.py deleted file mode 100644 index 1e746399..00000000 --- a/lingvo/tools/beam_utils.py +++ /dev/null @@ -1,115 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tools for car beam pipelines.""" - -import apache_beam as beam - - -def BeamInit(): - """Initialize the beam program. - - Typically first thing to run in main(). This call is needed before FLAGS - are accessed, for example. - """ - pass - - -def GetPipelineRoot(options=None): - """Return the root of the beam pipeline. - - Typical usage looks like: - - with GetPipelineRoot() as root: - _ = (root | beam.ParDo() | ...) - - In this example, the pipeline is automatically executed when the context is - exited, though one can manually run the pipeline built from the root object as - well. - - Args: - options: A beam.options.pipeline_options.PipelineOptions object. - - Returns: - A beam.Pipeline root object. - """ - return beam.Pipeline(options=options) - - -def GetReader(record_format, file_pattern, value_coder, **kwargs): - """Returns a beam Reader based on record_format and file_pattern. - - Args: - record_format: String record format, e.g., 'tfrecord'. - file_pattern: String path describing files to be read. - value_coder: Coder to use for the values of each record. - **kwargs: arguments to pass to the corresponding Reader object constructor. - - Returns: - A beam reader object. - - Raises: - ValueError: If an unsupported record_format is provided. - """ - if record_format == "tfrecord": - return beam.io.ReadFromTFRecord(file_pattern, coder=value_coder, **kwargs) - - raise ValueError("Unsupported record format: {}".format(record_format)) - - -def GetWriter(record_format, file_pattern, value_coder, **kwargs): - """Returns a beam Writer. - - Args: - record_format: String record format, e.g., 'tfrecord' to write as. - file_pattern: String path describing files to be written to. - value_coder: Coder to use for the values of each written record. - **kwargs: arguments to pass to the corresponding Writer object constructor. - - Returns: - A beam writer object. - - Raises: - ValueError: If an unsupported record_format is provided. - """ - if record_format == "tfrecord": - return beam.io.WriteToTFRecord(file_pattern, coder=value_coder, **kwargs) - raise ValueError("Unsupported record format: {}".format(record_format)) - - -def GetEmitterFn(record_format): - """Returns an Emitter function for the given record_format. - - An Emitter function takes in a key and value as arguments and returns - a structure that is compatible with the Beam Writer associated with - the corresponding record_format. - - Args: - record_format: String record format, e.g., 'tfrecord' to write as. - - Returns: - An emitter function of (key, value) -> Writer's input type. - - Raises: - ValueError: If an unsupported record_format is provided. - """ - - def _ValueEmitter(key, value): - del key - return [value] - - if record_format == "tfrecord": - return _ValueEmitter - raise ValueError("Unsupported record format: {}".format(record_format)) diff --git a/lingvo/tools/beam_utils_test.py b/lingvo/tools/beam_utils_test.py deleted file mode 100644 index f78646da..00000000 --- a/lingvo/tools/beam_utils_test.py +++ /dev/null @@ -1,64 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for beam_utils.""" - -import apache_beam as beam -from lingvo import compat as tf -from lingvo.core import test_helper -from lingvo.core import test_utils -from lingvo.tools import beam_utils - - -class BeamUtilsTest(test_utils.TestCase): - - def testReaders(self): - pattern = test_helper.test_src_dir_path( - 'tasks/mt/testdata/wmt14_ende_wpm_32k_test.tfrecord') - _ = beam_utils.GetReader( - 'tfrecord', - pattern, - value_coder=beam.coders.ProtoCoder(tf.train.Example)) - - with self.assertRaises(ValueError): - _ = beam_utils.GetReader( - 'unknown', - '/tmp/foo', - value_coder=beam.coders.ProtoCoder(tf.train.Example)) - - def testWriters(self): - _ = beam_utils.GetWriter( - 'tfrecord', - '/tmp/foo@1', - value_coder=beam.coders.ProtoCoder(tf.train.Example)) - - with self.assertRaises(ValueError): - _ = beam_utils.GetWriter( - 'unknown', - '/tmp/foo@1', - value_coder=beam.coders.ProtoCoder(tf.train.Example)) - - def testGetPipelineRoot(self): - with beam_utils.GetPipelineRoot() as root: - _ = root | beam.Create([1, 2, 3]) | beam.Map(lambda x: x) - - def testGetEmitterFn(self): - _ = beam_utils.GetEmitterFn('tfrecord') - with self.assertRaises(ValueError): - _ = beam_utils.GetEmitterFn('unknown') - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tools/bpe_word_tokenizer.py b/lingvo/tools/bpe_word_tokenizer.py deleted file mode 100644 index 9e391df9..00000000 --- a/lingvo/tools/bpe_word_tokenizer.py +++ /dev/null @@ -1,102 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Generates the words_to_ids file from a BPE encoded corpus and BPE vocab file. - -Extracts all the words in the corpus with their corresponding list of ids. Each -subword in the vocab file is mapped to their line number as its id. The lines of -the output file are like: -... -TAKE 43,7,50,14 -THAT 16,35 -THE 26 -THEIR 16,4,9,56 -... -Which is compatible with the BPE tokenizer op in core/tokenizer.py. - -Typical workflow: - - subword-nmt learn-bpe train_file code_file - subword-nmt apply-bpe code_file train_file train_bpe_file - subword-nmt get-vocab train_bpe_file vocab_file - - bpe_word_tokenizer train_bpe_file vocab_file words_to_ids_file -""" - -import lingvo.compat as tf - -tf.flags.DEFINE_string('encoded_filepath', '', - 'Path to the BPE encoded corpus file.') -tf.flags.DEFINE_string('vocab_filepath', '', 'Path to the BPE vocab file.') -tf.flags.DEFINE_string('output_filepath', '', - 'The output filepath (word_to_ids).') -FLAGS = tf.flags.FLAGS - - -def _GetVocabulary(vocab_filepath): - """Maps the first word in each line of the given file to its line number.""" - vocab = {} - with open(vocab_filepath, 'r') as vocab_file: - for i, line in enumerate(vocab_file): - word = line.strip('\r\n ').split(' ')[0] - if word: - vocab[word] = i - return vocab - - -def _ExtractTokenization(encoded_filepath, vocab): - """Maps the words in the encoded file to their list of token ids. - - Reads all the subwords in encoded file. Concatenates them while they have @@ - as their last two characters. The last token of a word is the subword without - @@. Maps the full word to the list of corresponding vocab ids of the subwords - from the vocab dictionary. - - Args: - encoded_filepath: String, filepath of the BPE encoded file. - vocab: Dictionary of subwords (string) to token ids (int). - - Returns: - Dictionary of words (string) to list of token ids (list of int). - """ - word_tokenization = {} - with open(encoded_filepath, 'r') as encoded_file: - for line in encoded_file: - full_word = '' - ids = [] - for word in line.strip('\r\n ').split(' '): - ids.append(vocab[word]) - if word[-2:] == '@@': - full_word += word[:-2] - else: - full_word += word - word_tokenization[full_word] = ids - full_word = '' - ids = [] - return word_tokenization - - -def main(_): - vocab = _GetVocabulary(FLAGS.vocab_filepath) - word_tokenization = _ExtractTokenization(FLAGS.encoded_filepath, vocab) - with open(FLAGS.output_filepath, 'w') as output: - for word, ids in word_tokenization.items(): - output.write(word + ' ') - output.write(','.join(map(str, ids))) - output.write('\r\n') - - -if __name__ == '__main__': - tf.app.run(main) diff --git a/lingvo/tools/compare_params.py b/lingvo/tools/compare_params.py deleted file mode 100644 index 17553625..00000000 --- a/lingvo/tools/compare_params.py +++ /dev/null @@ -1,115 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Library for comparing two models / hyperparams.""" - -from lingvo import compat as tf -from lingvo import model_registry -import six - - -def _hyperparams_text_to_dict(cfg_text): - """Converts hyperparams config text to a dictionary of key-value pairs.""" - txt_list = six.ensure_str(cfg_text).split("\n") - pair_list = [] - for v in txt_list: - if not v: - continue - vals = v.split(" : ") - if len(vals) != 2: - raise ValueError(v) - pair_list.append(vals) - return dict(pair_list) - - -def hyperparams_text_diff(cfg1_text, cfg2_text): - """Computes the differences between two hyperparams.Params texts. - - Args: - cfg1_text: A hyperparams.Params().ToText() of the first model config. - cfg2_text: A hyperparams.Params().ToText() of the second model config. - - Returns: - A tuple of 3 elements: - - - cfg1_not_cfg2: A list of keys in cfg1 but not cfg2. - - cfg2_not_cfg1: A list of keys in cfg2 but not cfg1. - - cfg1_and_cfg2_diff: A dict of common keys whose config values differ: each - value is a tuple of the config values from cfg1 and cfg2 respectively. - """ - cfg1_dict = _hyperparams_text_to_dict(cfg1_text) - cfg2_dict = _hyperparams_text_to_dict(cfg2_text) - cfg1_keys = set(cfg1_dict.keys()) - cfg2_keys = set(cfg2_dict.keys()) - - cfg1_not_cfg2 = sorted(list(cfg1_keys - cfg2_keys)) - cfg2_not_cfg1 = sorted(list(cfg2_keys - cfg1_keys)) - - def get_class_name(v): - try: - idx = v.rindex("/") - return v[idx + 1:] - except ValueError: - return v - - cfg1_and_cfg2_diff = {} - for k_intersection in cfg1_keys & cfg2_keys: - c1v = cfg1_dict[k_intersection] - c2v = cfg2_dict[k_intersection] - if k_intersection.endswith(".cls"): - c1v = get_class_name(c1v) - c2v = get_class_name(c2v) - - if c1v != c2v: - cfg1_and_cfg2_diff[k_intersection] = (c1v, c2v) - - return cfg1_not_cfg2, cfg2_not_cfg1, cfg1_and_cfg2_diff - - -def print_hyperparams_text_diff(path1, path2, cfg1_not_cfg2, cfg2_not_cfg1, - cfg1_and_cfg2_diff): - """Prints the differences of the output of hyperparams_text_diff. - - Args: - path1: Name of registered model or path to model 1. - path2: Name of registered model or path to model 2. - cfg1_not_cfg2: A list of keys in cfg1 but not cfg2. - cfg2_not_cfg1: A list of keys in cfg2 but not cfg1. - cfg1_and_cfg2_diff: A dictionary of common keys whose config values differ; - each value is a tuple of the config values from cfg1 and cfg2 - respectively. - """ - if cfg1_not_cfg2: - print("\n\nKeys in %s but not %s: \n%s\n\n" % - (path1, path2, "\n".join(cfg1_not_cfg2))) - if cfg2_not_cfg1: - print("\n\nKeys in %s but not %s: \n%s\n\n" % - (path2, path1, "\n".join(cfg2_not_cfg1))) - - if cfg1_and_cfg2_diff: - print("\n\nKeys with differences and their values: \n\n") - for k, v in sorted(cfg1_and_cfg2_diff.items()): - v1, v2 = v - print("%s: [%s] vs. [%s]" % (k, v1, v2)) - print("\n\n") - - -def get_model_params_as_text(model_path): - try: - cfg = model_registry.GetParams(model_path, "Train") - return cfg.ToText() - except LookupError: - # Try reading as file. - return tf.io.gfile.GFile(model_path).read() diff --git a/lingvo/tools/compare_params_test.py b/lingvo/tools/compare_params_test.py deleted file mode 100644 index 2c06aa57..00000000 --- a/lingvo/tools/compare_params_test.py +++ /dev/null @@ -1,45 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -"""Tests for compare_params.""" - -from lingvo import compat as tf -from lingvo.core import hyperparams -from lingvo.core import test_utils -from lingvo.tools import compare_params - - -class CompareParamsTest(test_utils.TestCase): - - def testBasic(self): - h1 = hyperparams.Params() - h1.Define('a', 1, '') - h1.Define('b', 2, '') - - h2 = hyperparams.Params() - h2.Define('a', 3, '') # different value. - h2.Define('c', 2, '') - - d1, d2, d3 = compare_params.hyperparams_text_diff(h1.ToText(), h2.ToText()) - self.assertEqual(d1, ['b']) - self.assertEqual(d2, ['c']) - self.assertEqual(d3, {'a': ('1', '3')}) - - # Exercise print function - compare_params.print_hyperparams_text_diff('h1', 'h2', d1, d2, d3) - - -if __name__ == '__main__': - tf.test.main() diff --git a/lingvo/tools/compute_stats.py b/lingvo/tools/compute_stats.py deleted file mode 100644 index 3dd710c4..00000000 --- a/lingvo/tools/compute_stats.py +++ /dev/null @@ -1,113 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Compute stats from tfrecords files.""" - -import lingvo.compat as tf -import numpy as np - -tf.flags.DEFINE_string('input_filepattern', '', - 'File pattern of binary tfrecord files.') -tf.flags.DEFINE_integer('frame_size', 1, 'Size of the frame, for reshaping.') -tf.flags.DEFINE_integer('num_buckets', 8, 'Number of buckets for the length.') -tf.flags.DEFINE_string('feature_name', None, 'Name of feature to examine.') - -FLAGS = tf.flags.FLAGS - - -class StatsCollector: - """Collect stats.""" - - def __init__(self,): - self._num_examples = 0 - self._lengths = [] - self._num_frames = 0 - self._mean_acc = np.zeros(FLAGS.frame_size, dtype=np.float64) - self._var_acc = np.zeros(FLAGS.frame_size, dtype=np.float64) - - def _AccumulateMoments(self, float_list): - frames = np.reshape(float_list, [-1, FLAGS.frame_size]) - self._num_frames += frames.shape[0] - self._mean_acc += np.sum(frames, axis=0) - self._var_acc += np.sum(frames * frames, axis=0) - - def _ComputeMeanVar(self): - mu = self._mean_acc / self._num_frames - # The user is in charge of replacing NaNs with a floor value. - v = np.sqrt(self._var_acc / self._num_frames - mu * mu) - return mu, v - - def Accumulate(self, tf_ex): - self._num_examples += 1 - if 0 == self._num_examples % 10000: - tf.logging.info('Processing example %u...', self._num_examples) - v = tf_ex.features.feature[FLAGS.feature_name] - if v.HasField('float_list'): - num_frames = len(v.float_list.value) // FLAGS.frame_size - self._AccumulateMoments(v.float_list.value) - elif v.HasField('int64_list'): - num_frames = len(v.int64_list.value) // FLAGS.frame_size - else: - tf.logging.fatal( - 'Not sure what to do with value. ' - 'Only float/int64 lists are supported: %s', v) - self._lengths.append(num_frames) - - def _PrintLengthBuckets(self): - sorted_lengths = sorted(self._lengths) - num_buckets = FLAGS.num_buckets - n = len(sorted_lengths) - idx = (n * (np.array(list(range(num_buckets - 1))) + 1)) // num_buckets - buckets = [sorted_lengths[i] for i in idx] + [sorted_lengths[-1]] - tf.logging.info('== Buckets.') - tf.logging.info('bucket upper limits: %s', buckets) - tf.logging.info('Other candidates for last bucket:') - tf.logging.info(' 0.1%% loss: %u', sorted_lengths[int(n * .999)]) - tf.logging.info(' 1%% loss: %u', sorted_lengths[int(n * .99)]) - tf.logging.info(' 2%% loss: %u', sorted_lengths[int(n * .98)]) - - def _PrintMeanVar(self): - m, v = self._ComputeMeanVar() - original = np.get_printoptions() - np.set_printoptions(threshold=np.inf) - tf.logging.info('== Mean/variance.') - tf.logging.info('mean = %s', m) - tf.logging.info('var = %s', v) - np.set_printoptions(**original) - - def Print(self): - tf.logging.info('== Total number of examples: %u', self._num_examples) - self._PrintLengthBuckets() - self._PrintMeanVar() - - -def main(_): - tf.logging.set_verbosity(tf.logging.INFO) - if not FLAGS.feature_name: - tf.logging.fatal( - 'Use a --feature_name to specify what to bucketize on. ' - 'For instance, source_id for MT or frames for ASR.') - stats = StatsCollector() - for filepath in tf.io.gfile.glob(FLAGS.input_filepattern): - records = tf.compat.v1.io.tf_record_iterator(filepath) - for serialized in records: - ex = tf.train.Example() - ex.ParseFromString(serialized) - stats.Accumulate(ex) - stats.Print() - - -if __name__ == '__main__': - tf.app.run(main) diff --git a/lingvo/tools/count_records.py b/lingvo/tools/count_records.py deleted file mode 100644 index a6c2e9d4..00000000 --- a/lingvo/tools/count_records.py +++ /dev/null @@ -1,69 +0,0 @@ -# Lint as: python3 -# Copyright 2020 The TensorFlow 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. -# ============================================================================== -r"""Tool to count number of records in a dataset. - -Most other file formats have efficient ways to fetch the number of records in a -dataset. However, some formats such as TFRecord requires you to essentially -scan the files to perform this count. - -# GALVEZ: This could be useful to us - -This is a short little beam script that can leverage many machines to read -all of the files in parallel potentially faster than a single machine script. -It is recommended that for other file formats, simply reading the metadata -available in their formats should work; this file should not really be -extended to any other format that already has efficient ways of counting -records. -""" - -from absl import app -from absl import flags - -import apache_beam as beam -from lingvo.tools import beam_utils - -flags.DEFINE_string('input_file_pattern', None, 'Path to read input') -flags.DEFINE_string('output_count_file', None, 'File to write output to.') -flags.DEFINE_string('record_format', None, - 'Record format of the input, e.g., tfrecord.') - -FLAGS = flags.FLAGS - - -def main(argv): - beam_utils.BeamInit() - - # Construct pipeline options from argv. - options = beam.options.pipeline_options.PipelineOptions(argv[1:]) - - reader = beam_utils.GetReader( - FLAGS.record_format, - FLAGS.input_file_pattern, - value_coder=beam.coders.BytesCoder()) - - with beam_utils.GetPipelineRoot(options=options) as root: - _ = ( - root - | 'Read' >> reader # Read each record. - | 'EmitOne' >> beam.Map(lambda _: 1) # Emit a 1 for each record. - | 'Count' >> beam.CombineGlobally(sum) # Sum counts. - | 'WriteToText' >> beam.io.WriteToText(FLAGS.output_count_file)) - - -if __name__ == '__main__': - flags.mark_flags_as_required( - ['input_file_pattern', 'output_count_file', 'record_format']) - app.run(main) diff --git a/lingvo/tools/create_asr_features.py b/lingvo/tools/create_asr_features.py deleted file mode 100644 index a0e2d5fd..00000000 --- a/lingvo/tools/create_asr_features.py +++ /dev/null @@ -1,218 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Encode the audio tarball contents into tfrecords.""" - -import os -import random -import re -import tarfile -import lingvo.compat as tf -from lingvo.tools import audio_lib - -tf.flags.DEFINE_string('input_tarball', '', 'Input .tar.gz file.') -tf.flags.DEFINE_string('input_text', '', 'Reference text.') -tf.flags.DEFINE_string('output_template', '', 'File of tfrecords.') - -tf.flags.DEFINE_bool('dump_transcripts', False, - 'First pass through the tarball to read the transcripts.') -tf.flags.DEFINE_string('transcripts_filepath', '', - 'Where to put the transcripts.') -tf.flags.DEFINE_bool('generate_tfrecords', False, - 'Second pass generates the tf records') - -tf.flags.DEFINE_integer('shard_id', -1, 'Processor shard.') -tf.flags.DEFINE_integer( - 'num_shards', -1, - 'Number of processor shards. Must divide num_output_shards.') -tf.flags.DEFINE_integer('output_range_begin', -1, 'Begin of output shard IDs.') -tf.flags.DEFINE_integer('output_range_end', -1, 'End of output shard IDs.') -tf.flags.DEFINE_integer('num_output_shards', -1, - 'Total number of output shards.') - -FLAGS = tf.flags.FLAGS - - -def _MakeBytesFeature(unicode_array): - value = [tf.compat.as_bytes(w) for w in unicode_array] - return tf.train.Feature(bytes_list=tf.train.BytesList(value=value)) - - -def _MakeInt64Feature(value): - return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) - - -def _MakeFloatFeature(value): - return tf.train.Feature(float_list=tf.train.FloatList(value=value)) - - -# I ought to use _MakeTfExample directly in my code. -def _MakeTfExample(uttid, frames, text): - flat_frames = frames.flatten() - feature = { - 'uttid': _MakeBytesFeature([uttid]), - 'transcript': _MakeBytesFeature([text.lower()]), - 'frames': _MakeFloatFeature(flat_frames) - } - # Okay, so let's just use tf.train.Example then. Otherwise, things get too confusing. - return tf.train.Example(features=tf.train.Features(feature=feature)) - - -def _ReadTranscriptions(): - """Read all transcription files from the tarball. - - Returns: - A map of utterance id to upper case transcription. - """ - file_obj = tf.io.gfile.GFile(FLAGS.input_tarball, mode='rb') - tar = tarfile.open(fileobj=file_obj, mode='r:gz') - n = 0 - tf.logging.info('First pass: loading text files...') - # TODO(drpng): there's more information in the following files: - # LibriSpeech/LICENSE.TXT - # LibriSpeech/README.TXT - # LibriSpeech/CHAPTERS.TXT - # LibriSpeech/SPEAKERS.TXT - # LibriSpeech/BOOKS.TXT - trans = {} - for tarinfo in tar: - if not tarinfo.isreg(): - continue - n += 1 - if 0 == n % 10000: - tf.logging.info('Scanned %d entries...', n) - if not tarinfo.name.endswith('.trans.txt'): - continue - # The file LibriSpeech/dev-clean/3170/137482/3170-137482.trans.txt - # will contain lines such as: - # 3170-137482-0000 WITH AN EDUCATION WHICH OUGHT TO ... - # 3170-137482-0001 I WAS COMPELLED BY POVERTY ... - key = tarinfo.name.strip('.trans.txt') - f = tar.extractfile(tarinfo) - u = 0 - for l in f.readlines(): - l = l.decode('utf-8') - uttid, txt = l.strip('\n').split(' ', 1) - trans[uttid] = txt - u += 1 - tf.logging.info('[%s] = %d utterances', key, u) - f.close() - tar.close() - file_obj.close() - return trans - - -def _DumpTranscripts(): - trans = _ReadTranscriptions() - with tf.io.gfile.GFile(FLAGS.transcripts_filepath, 'w') as f: - for uttid in sorted(trans): - f.write('%s %s\n' % (uttid, trans[uttid])) - - -def _LoadTranscriptionsFromFile(): - trans = {} - with tf.io.gfile.GFile(FLAGS.transcripts_filepath, 'r') as f: - for line in f.readlines(): - uttid, txt = line.strip('\n').split(' ', 1) - trans[uttid] = txt - return trans - - -def _MakeLogMelFromTensorflowBuiltin(tf_wav_bytes): - sample_rate, audio = audio_lib.DecodeWav(tf_wav_bytes) - static_sample_rate = 16000 - with tf.control_dependencies( - [tf.assert_equal(sample_rate, static_sample_rate)]): - log_mel = audio_lib.AudioToMfcc(static_sample_rate, audio, 25, 25, 40) - return log_mel - - -def _OpenSubShards(): - tf.logging.info('Shards: %d to %d', FLAGS.output_range_begin, - FLAGS.output_range_end) - recordio_writers = [] - for s in range(FLAGS.output_range_begin, FLAGS.output_range_end): - filepath = FLAGS.output_template % (s, FLAGS.num_output_shards) - tf.logging.info('Opening output shard: %s', filepath) - recordio_writers += [tf.python_io.TFRecordWriter(filepath)] - return recordio_writers - - -def _CloseSubShards(files): - for f in files: - f.close() - - -def _SelectRandomShard(files): - subshard = random.randint(0, len(files) - 1) - return files[subshard] - - -def _CreateAsrFeatures(): - # First pass: extract transcription files. - if os.path.exists(FLAGS.transcripts_filepath): - trans = _LoadTranscriptionsFromFile() - else: - tf.logging.info('Running first pass on the fly') - trans = _ReadTranscriptions() - tf.logging.info('Total transcripts: %d', len(trans)) - tf_bytes = tf.placeholder(dtype=tf.string) - # Great! It uses the frontend directly - log_mel = audio_lib.ExtractLogMelFeatures(tf_bytes) - # Second pass: transcode the flac. - file_obj = tf.io.gfile.GFile(FLAGS.input_tarball, mode='rb') - tar = tarfile.open(fileobj=file_obj, mode='r:gz') - n = 0 - recordio_writers = _OpenSubShards() - tfconf = tf.config_pb2.ConfigProto() - tfconf.gpu_options.allow_growth = True - with tf.Session(config=tfconf) as sess: - for tarinfo in tar: - if not tarinfo.name.endswith('.flac'): - continue - n += 1 - if n % FLAGS.num_shards != FLAGS.shard_id: - continue - uttid = re.sub('.*/(.+)\\.flac', '\\1', tarinfo.name) - f = tar.extractfile(tarinfo) - wav_bytes = audio_lib.DecodeFlacToWav(f.read()) - f.close() - frames = sess.run(log_mel, feed_dict={tf_bytes: wav_bytes}) - assert uttid in trans, uttid - num_words = len(trans[uttid]) - tf.logging.info('utt[%d]: %s [%d frames, %d words]', n, uttid, - frames.shape[1], num_words) - ex = _MakeTfExample(uttid, frames, trans[uttid]) - outf = _SelectRandomShard(recordio_writers) - outf.write(ex.SerializeToString()) - tar.close() - file_obj.close() - _CloseSubShards(recordio_writers) - - -def main(_): - tf.logging.set_verbosity(tf.logging.INFO) - if FLAGS.dump_transcripts: - _DumpTranscripts() - elif FLAGS.generate_tfrecords: - _CreateAsrFeatures() - else: - tf.logging.error( - 'Nothing to do! Use --dump_transcripts or --generate_tfrecords') - - -if __name__ == '__main__': - tf.disable_eager_execution() - tf.app.run(main) diff --git a/lingvo/tools/create_peoples_speech_asr_features.py b/lingvo/tools/create_peoples_speech_asr_features.py deleted file mode 100644 index 7bfb9829..00000000 --- a/lingvo/tools/create_peoples_speech_asr_features.py +++ /dev/null @@ -1,189 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Encode the audio tarball contents into tfrecords.""" - -import os -import csv -import random -import tarfile -import lingvo.compat as tf -from lingvo.tools import audio_lib - -tf.flags.DEFINE_string('input_tarball', '', 'Input .tar.gz file.') -tf.flags.DEFINE_string( - 'input_text', '', 'Reference text as csv, filename, transcript, metadata.') -tf.flags.DEFINE_string('output_template', '', 'File of tfrecords.') - -tf.flags.DEFINE_bool('dump_transcripts', False, - 'First pass through the tarball to read the transcripts.') -tf.flags.DEFINE_string('transcripts_filepath', '', - 'Where to put the transcripts.') -tf.flags.DEFINE_bool('generate_tfrecords', False, - 'Second pass generates the tf records') - -tf.flags.DEFINE_integer('shard_id', -1, 'Processor shard.') -tf.flags.DEFINE_integer( - 'num_shards', -1, - 'Number of processor shards. Must divide num_output_shards.') -tf.flags.DEFINE_integer('output_range_begin', -1, 'Begin of output shard IDs.') -tf.flags.DEFINE_integer('output_range_end', -1, 'End of output shard IDs.') -tf.flags.DEFINE_integer('num_output_shards', -1, - 'Total number of output shards.') - -FLAGS = tf.flags.FLAGS - - -def _MakeBytesFeature(unicode_array): - value = [tf.compat.as_bytes(w) for w in unicode_array] - return tf.train.Feature(bytes_list=tf.train.BytesList(value=value)) - - -def _MakeInt64Feature(value): - return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) - - -def _MakeFloatFeature(value): - return tf.train.Feature(float_list=tf.train.FloatList(value=value)) - - -def _MakeTfExample(uttid, frames, text): - flat_frames = frames.flatten() - feature = { - 'uttid': _MakeBytesFeature([uttid]), - 'transcript': _MakeBytesFeature([text.lower()]), - 'frames': _MakeFloatFeature(flat_frames) - } - return tf.train.Example(features=tf.train.Features(feature=feature)) - - -def _ReadTranscriptionsFromCSV(): - trans = {} - with tf.io.gfile.GFile(FLAGS.input_text, 'r') as f: - for row in csv.reader(f): - uttid, txt, metadata = row[:3] - # remove the gs bucket name and prefix - uttid = '/'.join(uttid.split('/')[3:]) - trans[uttid] = txt - return trans - - -def _LoadTranscriptionsFromFile(): - trans = {} - with tf.io.gfile.GFile(FLAGS.transcripts_filepath, 'r') as f: - for line in f.readlines(): - uttid, txt = line.strip('\n').split(' ', 1) - trans[uttid] = txt - return trans - - -def _MakeLogMelFromTensorflowBuiltin(tf_wav_bytes): - sample_rate, audio = audio_lib.DecodeWav(tf_wav_bytes) - static_sample_rate = 16000 - with tf.control_dependencies( - [tf.assert_equal(sample_rate, static_sample_rate)]): - log_mel = audio_lib.AudioToMfcc(static_sample_rate, audio, 25, 25, 40) - return log_mel - - -def _OpenSubShards(): - tf.logging.info('Shards: %d to %d', FLAGS.output_range_begin, - FLAGS.output_range_end) - recordio_writers = [] - for s in range(FLAGS.output_range_begin, FLAGS.output_range_end): - filepath = FLAGS.output_template % (s, FLAGS.num_output_shards) - tf.logging.info('Opening output shard: %s', filepath) - recordio_writers += [tf.python_io.TFRecordWriter(filepath)] - return recordio_writers - - -def _CloseSubShards(files): - for f in files: - f.close() - - -def _SelectRandomShard(files): - subshard = random.randint(0, len(files) - 1) - return files[subshard] - - -def _CreateAsrFeatures(): - # First pass: extract transcription files. - if False: #os.path.exists(FLAGS.transcripts_filepath): - trans = _LoadTranscriptionsFromFile() - else: - tf.logging.info('Running first pass on the fly') - trans = _ReadTranscriptionsFromCSV() - total_utts = len(trans) - tf.logging.info('Total transcripts: %d', len(trans)) - tf_bytes = tf.placeholder(dtype=tf.string) - log_mel = audio_lib.ExtractLogMelFeatures(tf_bytes) - # Second pass: transcode the flac. - file_obj = tf.io.gfile.GFile(FLAGS.input_tarball, mode='rb') - tar = tarfile.open(fileobj=file_obj, mode='r:gz') - n = 0 - recordio_writers = _OpenSubShards() - tfconf = tf.config_pb2.ConfigProto() - tfconf.gpu_options.allow_growth = True - with tf.Session(config=tfconf) as sess: - for tarinfo in tar: - # We can actually decode essentially any audio format, but we - # want to avoid non-audio data. Thus, this condition. - if not (tarinfo.name.endswith('.flac') or tarinfo.name.endswith('.wav') or - tarinfo.name.endswith('.mp3')): - continue - n += 1 - if n % FLAGS.num_shards != FLAGS.shard_id: - continue - f = tar.extractfile(tarinfo) - fmt = tarinfo.name.split('.')[-1] - uttid = tarinfo.name - audio_bytes = f.read() - f.close() - try: - wav_bytes = audio_lib.DecodeToWav(audio_bytes, fmt) - frames = sess.run(log_mel, feed_dict={tf_bytes: wav_bytes}) - except Exception as e: - # raise - trans.pop(uttid) - tf.logging.info(f'{uttid} FAILED featurization') - continue - assert uttid in trans, uttid - num_words = len(trans[uttid]) - tf.logging.info('utt[%d]: %s [%d frames, %d chars]', n, uttid, - frames.shape[1], num_words) - ex = _MakeTfExample(uttid, frames, trans[uttid]) - outf = _SelectRandomShard(recordio_writers) - outf.write(ex.SerializeToString()) - tar.close() - file_obj.close() - _CloseSubShards(recordio_writers) - tf.logging.info(f'Processed {len(trans)} / {total_utts}') - - -def main(_): - tf.logging.set_verbosity(tf.logging.INFO) - if FLAGS.dump_transcripts: - assert False, "dump_transcripts option isn't supported. TODO: Remove." - elif FLAGS.generate_tfrecords: - _CreateAsrFeatures() - else: - tf.logging.error( - 'Nothing to do! Use --dump_transcripts or --generate_tfrecords') - - -if __name__ == '__main__': - tf.disable_eager_execution() - tf.app.run(main) diff --git a/lingvo/tools/generate_proto_def.cc b/lingvo/tools/generate_proto_def.cc deleted file mode 100644 index 8ca2e8d7..00000000 --- a/lingvo/tools/generate_proto_def.cc +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright 2018 The TensorFlow 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. -==============================================================================*/ - -#include -#include -#include -#include - -#include "google/protobuf/descriptor.h" -#include "tensorflow/core/framework/graph.pb.h" -#include "tensorflow/core/framework/types.pb.h" -#include "tensorflow/core/protobuf/meta_graph.pb.h" -#include "tensorflow/core/protobuf/saver.pb.h" - -namespace { -void WriteDotProto(const google::protobuf::FileDescriptor* dot_proto, - const char* output_dirpath) { - std::string output_filepath(output_dirpath); - output_filepath += "/"; - output_filepath += dot_proto->name(); - std::ofstream output_file; - // Assumes the directory tree is already there. - output_file.open(output_filepath); - output_file << dot_proto->DebugString(); - output_file.close(); -} - -void GenerateProtoDef(const google::protobuf::FileDescriptor* dot_proto, - const char* output_dirpath, - std::unordered_set* printed_files) { - if (printed_files->find(dot_proto->name()) != printed_files->end()) { - return; - } - printed_files->insert(dot_proto->name()); - WriteDotProto(dot_proto, output_dirpath); - for (int k = 0; k < dot_proto->dependency_count(); ++k) - GenerateProtoDef(dot_proto->dependency(k), output_dirpath, printed_files); -} - -// Regurgitate the text definitions from binary. -void GenerateProtoDefs(const char* output_dirpath) { - std::unordered_set printed_files; - GenerateProtoDef(tensorflow::GraphDef::descriptor()->file(), output_dirpath, - &printed_files); - GenerateProtoDef(tensorflow::DataType_descriptor()->file(), output_dirpath, - &printed_files); - GenerateProtoDef(tensorflow::SaverDef::descriptor()->file(), output_dirpath, - &printed_files); - GenerateProtoDef(tensorflow::MetaGraphDef::descriptor()->file(), - output_dirpath, &printed_files); -} -} // namespace - -int main(const int argc, const char** argv) { - GenerateProtoDefs(argv[1]); - return 0; -} diff --git a/lingvo/tools/generate_tf_dot_protos.sh b/lingvo/tools/generate_tf_dot_protos.sh deleted file mode 100755 index 333154b2..00000000 --- a/lingvo/tools/generate_tf_dot_protos.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -# Copyright 2017 The TensorFlow 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. -# ============================================================================== - -# Generates the tensorflow .proto files. This is a thin wrapper around -# generate_proto_def. - -binary=$1 -dest=$2 - -set -e -set -u -set +x - -rm -f ${dest}/.generated - -mkdir -p ${dest}/tensorflow/core/framework -mkdir -p ${dest}/tensorflow/core/protobuf - -${binary} ${dest} - -# genrule requires statically determined outputs, so we package all -# into a single file. -tar -C ${dest} -cf ${dest}/tf_protos.tar tensorflow/core/{framework,protobuf} diff --git a/lingvo/tools/gke_launch.py b/lingvo/tools/gke_launch.py deleted file mode 100644 index 67158651..00000000 --- a/lingvo/tools/gke_launch.py +++ /dev/null @@ -1,498 +0,0 @@ -# Lint as: python3 -# Copyright 2019 The TensorFlow 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. -# ============================================================================== -r"""Launch script for GKE jobs. - -This script generates the GKE deployment configs for TPU training, GPU/CPU -decoding, and TensorBoard jobs. - -It assumes you have: - -a) Copied any input data to GCS. -b) Docker and GKE/GCP tools installed locally. -c) Created the TPU and GPU clusters using `gcloud containers create`. - -This script launches jobs by: - -1) Building a lingvo docker image built from --base_image, copying - the directory pointed to by --build, writing that image to --image - (with an automatically generated date-based tag for versioning). - -2) Identifying the full name of the GKE cluster that each accelerator - job runs in based on --trainer_cell and --decoder_cell - -3) Writing out .yaml configuration files based on --name, --model, --logdir, - that will launch the docker images for each job type. - -Usage looks something like: - - python3 lingvo/tools/gke_launch.py \ - --model=$MODEL \ - --base_image=tensorflow:lingvo_lib_gpu \ - --image=$DOCKER_IMAGE \ - --logdir=$LOGDIR \ - --tpu_type=$TPU_TYPE \ - --trainer_cell=$TPU_CLUSTER_NAME \ - --decoder_cell=$GPU_CLUSTER_NAME \ - --decoder_gpus=1 \ - --gpu_type=$GPU_TYPE \ - --decoder=dev \ - --extra_envs=KITTI_DIR=$GCS_PATH \ - --name=$EXP_NAME \ - --build=$YOUR_CODE_DIR \ - $ACTION $TARGETS - -ACTION specifies whether to start (up), stop (down) or reload the target jobs. -One can also specify "print" to just print out the .yaml configuration files. - -TARGETS specifies whether the action affects all jobs ("all") or just an -individual job ("trainer", "decoder", "tensorboard"). - -See the flags definition below for details on the arguments. -""" - -import datetime -import os -import subprocess -import tempfile - -from absl import app -from absl import flags -import six -import yaml - -FLAGS = flags.FLAGS - -# General flags for all jobs. -flags.DEFINE_string("name", None, "Base name of experiment.") -flags.DEFINE_string("model", None, "Name of registered model.") -flags.DEFINE_string( - "image", None, "Name of docker image to use. If tag is not specified " - "and --build is set, a time-based tag will be used.") -flags.DEFINE_string("logdir", None, "GCS location of base logdir.") -flags.DEFINE_string("gpu_type", "", - "Type of GPU to use, e.g., 'p100' or 'v100'.") - -# Cluster flags -flags.DEFINE_string( - "trainer_cell", "", "Query name for the GKE cluster to use for training. " - "Must uniquely identify among all active clusters.") -flags.DEFINE_string( - "decoder_cell", "", "Query name for the GKE cluster to use for decoding. " - "Must uniquely identify among all active clusters.") - -# TPU training flags. -flags.DEFINE_string("tpu_type", "v3-8", - "Type of TPU to use for training, e.g., 'v3-8' for a 2x2.") - -# Decoder flags. -flags.DEFINE_string("decoder_ram", "24G", - "Amount of CPU memory for the decoder.") -# TODO(vrv): Support launching multiple decoder jobs when this is -# comma-separated. -flags.DEFINE_string("decoder", "dev", "Split(s) of dataset for decoding.") -flags.DEFINE_integer("decoder_gpus", 0, - "Number of GPUs per machine for decoding.") - -# Docker-based flags. -flags.DEFINE_string( - "build", None, "If set, builds the docker image " - "for --image including all code from this directory.") -flags.DEFINE_string( - "base_image", "tensorflow:lingvo_lib", - "Base Lingvo docker image to use. If using GPU, make " - "sure you have built a GPU-enabled image.") -flags.DEFINE_string( - "extra_envs", "", "Extra comma-separated list of key=value environment " - "variables to set when building the docker image.") - - -def _get_or_add(cfg, name): - """Gets cfg[name], or adds 'name' with an empty dict if not present.""" - if name not in cfg: - cfg.update({name: {}}) - return cfg[name] - - -def add_gpu_to_pod(cfg, gpu_type, num_gpus): - """Sets the appropriate GPU fields to cfg. - - Args: - cfg: The YAML-based dictionary to update. - gpu_type: The type of GPU to launch on GKE. - num_gpus: The number of GPUs to launch in the task. - """ - if num_gpus == 0: - return - - if gpu_type == "p100": - gpu_str = "nvidia-tesla-p100" - elif gpu_type == "v100": - gpu_str = "nvidia-tesla-v100" - else: - raise ValueError("Invalid gpu type: ", gpu_type) - cfg["spec"].update( - {"nodeSelector": { - "cloud.google.com/gke-accelerator": gpu_str - }}) - - containers = cfg["spec"]["containers"][0] - resources = _get_or_add(containers, "resources") - resources.update({"limits": {"nvidia.com/gpu": num_gpus}}) - - -def set_pod_cpu_memory(cfg, cpu_memory): - """Sets the amount of CPU memory to request in the container.""" - containers = cfg["spec"]["containers"][0] - resources = _get_or_add(containers, "resources") - resources.update({"requests": {"memory": cpu_memory}}) - - -def decoder_template(job_name, model, image, logdir, decoder_type, - decoder_gpus): - """Constructs the base yaml config for the decoder.""" - name = six.ensure_str(job_name) + ".decoder" - container_name = name.replace(".", "-") - job = "decoder_" + six.ensure_str(decoder_type) - return """ -apiVersion: v1 -kind: Pod -metadata: - name: {job_name} -spec: - restartPolicy: Never - containers: - - name: {container_name} - image: {image} - command: ["/usr/bin/python3"] - args: ["-m", "lingvo.trainer", "--mode=sync", "--alsologtostderr", "--model={model}", "--logdir={logdir}", "--job={job}", "--decoder_gpus={decoder_gpus}", "--decoder_replicas=1", "--cluster_spec={job}=localhost:0"] - """.format( - job_name=name, - container_name=container_name, - image=image, - model=model, - logdir=logdir, - job=job, - decoder_gpus=decoder_gpus) - - -def _tpu_resource(tpu_type): - version, num_chips = six.ensure_str(tpu_type).split("-") - return "cloud-tpus.google.com/{}: {}".format(version, num_chips) - - -def tpu_training_template(job_name, model, image, logdir, tpu_type): - """Constructs the base yaml config for the TPU trainer.""" - name = six.ensure_str(job_name) + ".trainer" - container_name = name.replace(".", "-") - tpu_string = _tpu_resource(tpu_type) - return """apiVersion: batch/v1 -kind: Job -metadata: - name: {name} -spec: - template: - metadata: - annotations: - tf-version.cloud-tpus.google.com: "nightly" - spec: - restartPolicy: Never - containers: - - name: {container_name} - image: {image} - command: ["/usr/bin/python3"] - args: ["-m", "lingvo.trainer", "--mode=sync", "--alsologtostderr", "--model={model}", "--logdir={logdir}", "--tpu=$(KUBE_GOOGLE_CLOUD_TPU_ENDPOINTS)"] - resources: - limits: - {tpu_string} - """.format( - name=name, - container_name=container_name, - image=image, - model=model, - logdir=logdir, - tpu_string=tpu_string) - - -def tensorboard_template(job_name, logdir, port): - """Constructs the tensorboard YAML template.""" - job_name = six.ensure_str(job_name) + ".tensorboard" - container_name = job_name.replace(".", "-") - - print("To poll for tensorboard address, run: $ kubectl get service %s -w" % - (container_name + "-service")) - return """ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {job_name} -spec: - replicas: 1 - selector: - matchLabels: - name: {job_name} - template: - metadata: - labels: - name: {job_name} - spec: - restartPolicy: Always - containers: - - name: {container_name} - image: gcr.io/tensorflow/tpu-util:r1.11 - command: - - tensorboard - - --logdir=$(MODEL_BUCKET) - env: - - name: MODEL_BUCKET - value: {logdir} - ports: - - containerPort: {port} ---- -apiVersion: v1 -kind: Service -metadata: - name: {container_name}-service -spec: - type: LoadBalancer - selector: - name: {job_name} - ports: - - port: {port} - targetPort: {port} -""".format( - job_name=job_name, container_name=container_name, logdir=logdir, port=port) - - -def build_docker_image(image, base_image, code_directory, extra_envs): - """Build a docker image and push it to the location specified by image. - - Args: - image: String name of tag to use, e.g., 'gcr.io/foo/bar:version' - base_image: String name of base lingvo image to build from. - code_directory: Location of directory whose contents will be copied into the - image. - extra_envs: A comma-separated list of key=value environment variables to be - built into the docker. - """ - preamble = [ - "FROM %s AS lingvo" % base_image, - ] - envs = [ - "ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/home/kubernetes/bin/nvidia/lib64", - "ENV PATH=${PATH}:/home/kubernetes/bin/nvidia/bin", - ] - for env_pairs in six.ensure_str(extra_envs).split(","): - envs += ["ENV %s" % env_pairs] - - copy_code = ["WORKDIR /tmp/lingvo", "COPY . ."] - - gpu_docker_file = preamble + envs + copy_code - tmp_dockerfile = tempfile.mkstemp(suffix=".dockerfile")[1] - with open(tmp_dockerfile, "w") as f: - f.write("\n".join(gpu_docker_file)) - print("Writing Dockerfile to", tmp_dockerfile) - - os.system("docker build --tag %s --no-cache -f- %s < %s " % - (image, code_directory, tmp_dockerfile)) - os.system("docker push %s" % image) - - -def get_gke_cluster(gke_cluster_spec): - """Get the full name of the GKE cluster given shorthand `gke_cluster_spec`. - - For example, gcloud container cluster list produces: - - NAME LOCATION ... - p100-europe-west4-a-nh16 europe-west4-a ... - test-df-europe europe-west4-a ... - - Then a gke_cluster_spec of 'p100' or 'df' will produce the fully-qualified - cluster. - - A gke_cluster_spec of 'europe' will raise a ValueError because there are two - active clusters that have the string 'europe' in them. - - Args: - gke_cluster_spec: A string specifying a filter on active clusters. - - Returns: - The fully qualified GKE cluster name, or None if not found. - - Raises: - ValueError: If gke_cluster_spec does not uniquely identify an - active cluster. - """ - if not gke_cluster_spec: - return "" - - active_clusters = subprocess.check_output( - ["gcloud", "container", "clusters", "list"]) - cluster_names = [ - c.split(b" ")[0] for c in active_clusters.split(b"\n")[1:] if c - ] - filtered = [ - c for c in cluster_names if six.ensure_binary(gke_cluster_spec) in c - ] - - if len(filtered) > 1: - raise ValueError("Cluster filter was not precise enough.") - - if not filtered: - return None - - result = subprocess.check_output(["kubectl", "config", "view"]) - result = result.split(b"\n") - clusters = [ - r.lstrip() for r in result if " cluster: " in r.decode("utf-8") - ] - cluster = [r for r in clusters if filtered[0] in r] - assert len(cluster) == 1 - cluster = cluster[0].split(b" ")[1] - return cluster - - -# Not yet used, but illustrates one what needs to do. -def create_tpu_cluster(cluster_name, zone): - _ = subprocess.check_output([ - "gcloud", "container", "clusters", "create", cluster_name, - "--cluster_version=1.13", "--scopes=cloud-platform,gke-default", - "--enable-ip-alias", "--enable-tpu", "--async", - "--zone=%s" % zone - ]) - - -def validate_args(argv): - """Validates the input arguments. Raises a UsageError if invalid.""" - valid_commands = ["up", "down", "print", "reload"] - error = "" - if len(argv) < 2: - error = ("Command not provided. " - "Command must be one of %s" % valid_commands) - elif argv[1] not in valid_commands: - error = "Command must be one of %s" % valid_commands - - if len(argv) > 3: - print("Too many arguments.") - - if len(argv) == 3: - valid_targets = ["trainer", "decoder", "tensorboard", "all"] - if argv[2] not in valid_targets: - error = "Target must be one of %s" % valid_targets - - if error: - raise app.UsageError(error) - - -def main(argv): - validate_args(argv) - action = argv[1] - target = argv[2] if len(argv) == 3 else "all" - - if target == "all": - targets = ["trainer", "decoder", "tensorboard"] - else: - targets = [target] - - image = FLAGS.image - - if action == "reload": - actions = ["down", "up"] - else: - actions = [action] - - # Maybe build. - if "up" in actions and FLAGS.build: - # If image does not specify a tag, create a temporary one. - if ":" not in image: - image += ":%s" % datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") - - build_docker_image(image, FLAGS.base_image, FLAGS.build, FLAGS.extra_envs) - - # Write out the YAML deployment files to a temporary directory - # for post-hoc inspection. - root = tempfile.mkdtemp(prefix=FLAGS.name) - print("Writing out yaml configs to %s" % root) - - action_to_cmd = {"up": "create", "down": "delete"} - - # Start or stop the jobs as requested. - if "tensorboard" in targets: - # Create tensorboard config. Template consists of two yaml configs, so skip - # parsing. - tb_tmpl = tensorboard_template(FLAGS.name, FLAGS.logdir, 6006) - tb_path = os.path.join(root, "tensorboard.yaml") - with open(tb_path, "w") as f: - f.write(tb_tmpl) - if "print" in actions: - print("TB yaml: %s" % tb_tmpl) - else: - for action in actions: - cmd = "kubectl %s -f %s" % (action_to_cmd[action], tb_path) - print("Running: %s" % cmd) - os.system(cmd) - - if "trainer" in targets: - tpu_cluster = get_gke_cluster(FLAGS.trainer_cell) - assert tpu_cluster - tpu_tmpl = tpu_training_template(FLAGS.name, FLAGS.model, image, - FLAGS.logdir, FLAGS.tpu_type) - # Check that it can be loaded. - tpu_cfg = yaml.safe_load(tpu_tmpl) - - trainer_path = os.path.join(root, "tpu_train.yaml") - loaded = yaml.safe_dump(tpu_cfg) - with open(trainer_path, "w") as f: - f.write(six.ensure_str(loaded)) - - if "print" in actions: - print("\n\nTPU yaml: \n%s" % loaded) - else: - for action in actions: - cmd = "kubectl %s -f %s --cluster %s" % (action_to_cmd[action], - trainer_path, tpu_cluster) - print("Running: %s" % cmd) - os.system(cmd) - - if "decoder" in targets: - decoder_cluster = get_gke_cluster(FLAGS.decoder_cell) - decoder_tmpl = decoder_template(FLAGS.name, FLAGS.model, image, - FLAGS.logdir, FLAGS.decoder, - FLAGS.decoder_gpus) - - # Parse into YAML - decoder_cfg = yaml.safe_load(decoder_tmpl) - # Set decoder config values. - add_gpu_to_pod(decoder_cfg, FLAGS.gpu_type, FLAGS.decoder_gpus) - set_pod_cpu_memory(decoder_cfg, FLAGS.decoder_ram) - - assert decoder_cluster - decoder_path = os.path.join(root, "decoder.yaml") - with open(decoder_path, "w") as f: - f.write(six.ensure_str(yaml.safe_dump(decoder_cfg))) - - if "print" in actions: - print("Decoder yaml: \n%s" % yaml.safe_dump(decoder_cfg)) - else: - for action in actions: - cmd = "kubectl %s -f %s --cluster %s" % (action_to_cmd[action], - decoder_path, decoder_cluster) - print("Running: %s" % cmd) - os.system(cmd) - - -if __name__ == "__main__": - flags.mark_flags_as_required(["name", "model", "image", "logdir"]) - app.run(main) diff --git a/lingvo/tools/keras2ckpt.py b/lingvo/tools/keras2ckpt.py deleted file mode 100644 index e52a324c..00000000 --- a/lingvo/tools/keras2ckpt.py +++ /dev/null @@ -1,63 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Converts a keras dataset into a tf checkpoint. - -E.g. - -.. code-block:: bash - - $ bazel run lingvo/tools:keras2ckpt -- --dataset=mnist -""" - -import os -import lingvo.compat as tf -from tensorflow.python.ops import io_ops - -FLAGS = tf.flags.FLAGS - -tf.flags.DEFINE_string("dataset", "", "The dataset name.") -tf.flags.DEFINE_string("out", "", "The output checkpoint path prefix.") - - -def main(argv): - del argv # Unused. - - dataset = getattr(tf.keras.datasets, FLAGS.dataset) - (x_train, y_train), (x_test, y_test) = dataset.load_data() - - def wrap(val): - dtype = tf.as_dtype(val.dtype) - assert dtype != tf.string # tf.string is not supported by py_func. - return tf.py_func(lambda: val, [], dtype) - - out_prefix = FLAGS.out or os.path.join("/tmp", FLAGS.dataset, FLAGS.dataset) - tf.logging.info("Save %s dataset to %s ckpt." % - (FLAGS.dataset, out_prefix)) - - with tf.Session() as sess: - sess.run( - io_ops.save_v2( - prefix=out_prefix, - tensor_names=["x_train", "y_train", "x_test", "y_test"], - shape_and_slices=[""] * 4, - tensors=[wrap(x_train), - wrap(y_train), - wrap(x_test), - wrap(y_test)])) - - -if __name__ == "__main__": - tf.app.run(main) diff --git a/lingvo/tools/print_tf_records.py b/lingvo/tools/print_tf_records.py deleted file mode 100644 index 6f53c215..00000000 --- a/lingvo/tools/print_tf_records.py +++ /dev/null @@ -1,114 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Debug print tf records in text format.""" - -import lingvo.compat as tf -import six - -tf.flags.DEFINE_string('input_filepattern', '', - 'File pattern of binary tfrecord files.') -tf.flags.DEFINE_string('input_format', 'tf.Example', - 'Input format: only "tf.Example" supported for now.') -tf.flags.DEFINE_integer('skip_first_n', 0, 'Skip first records.') -tf.flags.DEFINE_integer('print_only_n', -1, - 'Only print a certain number of records.') -tf.flags.DEFINE_bool('abbreviated', True, 'Print in abbreviated format.') -tf.flags.DEFINE_bool('bytes_as_utf8', True, - 'Print byte strings as UTF-8 strings') -tf.flags.DEFINE_bool('count_only', False, - 'Don\'t print, just count number of entries') - -FLAGS = tf.flags.FLAGS - - -def _ListDebugString(values, to_string=str): - if len(values) <= 8: - return repr(values) - first_values = [to_string(v) for v in values[0:6]] - last_values = [to_string(v) for v in values[-2:]] - return '[' + ' '.join(first_values + ['...'] + last_values) + ']' - - -def _CustomShortDebugString(tf_example): - text = [] - for name, value in sorted(tf_example.features.feature.items()): - if value.HasField('bytes_list'): - if FLAGS.bytes_as_utf8: - utf8_values = [ - six.ensure_text(v, 'utf-8') for v in value.bytes_list.value - ] - value_string = _ListDebugString(utf8_values) - else: - value_string = _ListDebugString(value.bytes_list.value) - elif value.HasField('float_list'): - value_string = _ListDebugString(value.float_list.value) - elif value.HasField('int64_list'): - value_string = _ListDebugString(value.int64_list.value, to_string=repr) - text += ['%s: %s' % (name, value_string)] - return '\n'.join(text) - - -def _PrintHeader(tf_example): - """Prints table of contents.""" - # Typically, tf.Examples have the same features. - tf.logging.info('==== FEATURES ====') - for name, value in sorted(tf_example.features.feature.items()): - type_string = '' - if value.HasField('bytes_list'): - type_string = 'bytes' - elif value.HasField('float_list'): - type_string = 'float' - elif value.HasField('int64_list'): - type_string = 'int64' - tf.logging.info('%s: [%s]', name, type_string) - tf.logging.info('====') - - -def _PrintFiles(): - entry = 0 - for filepath in tf.io.gfile.glob(FLAGS.input_filepattern): - records = tf.compat.v1.io.tf_record_iterator(filepath) - for serialized in records: - if entry < FLAGS.skip_first_n: - entry += 1 - continue - if FLAGS.print_only_n >= 0 and (entry - FLAGS.skip_first_n > - FLAGS.print_only_n): - break - if FLAGS.count_only: - entry += 1 - if (entry % 100000) == 0: - tf.logging.info('Counted %d entries so far...', entry) - continue - assert FLAGS.input_format == 'tf.Example' - ex = tf.train.Example() - ex.ParseFromString(serialized) - if entry == FLAGS.skip_first_n: - _PrintHeader(ex) - text_format = _CustomShortDebugString(ex) if FLAGS.abbreviated else str( - ex) - tf.logging.info('== Record [%d]\n%s', entry, text_format) - entry += 1 - tf.logging.info('== Total entries: %d', entry) - - -def main(_): - tf.logging.set_verbosity(tf.logging.INFO) - _PrintFiles() - - -if __name__ == '__main__': - tf.app.run(main) diff --git a/lingvo/tools/testdata/BUILD b/lingvo/tools/testdata/BUILD deleted file mode 100644 index 3189e12b..00000000 --- a/lingvo/tools/testdata/BUILD +++ /dev/null @@ -1,12 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -filegroup( - name = "audio_data", - srcs = glob(include = [ - "gan_or_vae.*", - ]), -) diff --git a/lingvo/tools/testdata/gan_or_vae.16k.wav b/lingvo/tools/testdata/gan_or_vae.16k.wav deleted file mode 100644 index c960aefda976c6e8861f58309f0dcb840b9a7bea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101244 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 z 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%0A-;!)QvZz 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 diff --git a/lingvo/tools/testdata/gan_or_vae.flac b/lingvo/tools/testdata/gan_or_vae.flac deleted file mode 100644 index b04478b9450c80a96b2210c3ad188b9b17396ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90062 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

    5BrLyn!?cn)hSl>st(u9kekBMJH1--$TB;z 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 diff --git a/lingvo/tools/wpm_encode_file.py b/lingvo/tools/wpm_encode_file.py deleted file mode 100644 index 9833a171..00000000 --- a/lingvo/tools/wpm_encode_file.py +++ /dev/null @@ -1,139 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Encode file using the wpm_encoder.""" - -import lingvo.compat as tf -from lingvo.core import wpm_encoder -import numpy as np -import six -from six import text_type - -tf.flags.DEFINE_string( - 'source_filepaths', '', - 'Comma-separated list of source files. Must align with target') -tf.flags.DEFINE_string('target_filepaths', '', - 'Comma-separated list of target files.') -tf.flags.DEFINE_string('output_filepath', '', 'File of tfrecords.') -tf.flags.DEFINE_string('wpm_filepath', '', 'The wordpiece vocabulary file.') -tf.flags.DEFINE_integer('num_shards', -1, 'Total number of shards.') -tf.flags.DEFINE_integer('shard_id', -1, 'This shard id (0-based).') -tf.flags.DEFINE_integer( - 'max_len', 0, - 'Drop sentence if src/tgt tokens exceed max length, counting and . ' - 'Only use during training. A value of 0 does not filter.') - -FLAGS = tf.flags.FLAGS - - -def _MakeBytesFeature(unicode_array): - value = [tf.compat.as_bytes(w) for w in unicode_array] - return tf.train.Feature(bytes_list=tf.train.BytesList(value=value)) - - -def _MakeInt64Feature(value): - return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) - - -def _MakeFloatFeature(value): - return tf.train.Feature(float_list=tf.train.FloatList(value=value)) - - -def _AssertTextFormat(text): - assert not text.startswith('') - assert not text.endswith('') - assert not text.startswith('') - assert not text.endswith('') - - -def _MakeTfExample(enc, src_i, src_s, tgt_i, tgt_s): - """Creates TfExample from the encoded results.""" - src_i = list(src_i) + [enc.sentence_end_id] - src_s = list(src_s) + [enc.sentence_end_string] - if FLAGS.max_len > 0 and len(src_i) > FLAGS.max_len: - return None - tgt_l = list(tgt_i) + [enc.sentence_end_id] - tgt_i = [enc.sentence_start_id] + list(tgt_i) - tgt_s = [enc.sentence_start_string] + list(tgt_s) - if FLAGS.max_len > 0 and len(tgt_i) > FLAGS.max_len: - return None - feature = { - 'source_id': _MakeInt64Feature(src_i), - 'source_padding': _MakeFloatFeature(np.zeros_like(src_i)), - 'source_word': _MakeBytesFeature(src_s), - 'target_id': _MakeInt64Feature(tgt_i), - 'target_padding': _MakeFloatFeature(np.zeros_like(tgt_i)), - 'target_word': _MakeBytesFeature(tgt_s), - 'target_label': _MakeInt64Feature(tgt_l), - 'target_weight': _MakeFloatFeature(np.ones_like(tgt_l)), - 'natural_order': _MakeInt64Feature([1]), - } - return tf.train.Example(features=tf.train.Features(feature=feature)) - - -def _Preprocess(text): - if not isinstance(text, text_type): - text = six.ensure_text(text, 'utf-8') - return text.strip().replace(' ', '') - - -def _RunEncoding(): - sess = tf.Session() - enc = wpm_encoder.WpmEncoder(FLAGS.wpm_filepath) - src_txt_placeholder = tf.placeholder(tf.string, []) - src_encode_op = enc.Encode(src_txt_placeholder) - tgt_txt_placeholder = tf.placeholder(tf.string, []) - tgt_encode_op = enc.Encode(tgt_txt_placeholder) - pairs = list( - zip(FLAGS.source_filepaths.split(','), FLAGS.target_filepaths.split(','))) - with tf.python_io.TFRecordWriter(FLAGS.output_filepath) as outf: - n = 0 - for p in pairs: - with tf.io.gfile.GFile(p[0], 'r') as sourcef: - with tf.io.gfile.GFile(p[1], 'r') as targetf: - for textp in zip(sourcef.readlines(), targetf.readlines()): - n += 1 - if n % 10000 == 0: - tf.logging.info('Watermark[%d]: %d', FLAGS.shard_id, n) - if n % FLAGS.num_shards != FLAGS.shard_id: - continue - source_text = _Preprocess(textp[0]) - target_text = _Preprocess(textp[1]) - # By convention: - # * source always ends in , never starts with . - # * target never ends in , always starts with . - _AssertTextFormat(source_text) - _AssertTextFormat(target_text) - ((src_i, src_s), (tgt_i, tgt_s)) = sess.run( - [src_encode_op, tgt_encode_op], - feed_dict={ - src_txt_placeholder: source_text, - tgt_txt_placeholder: target_text - }, - ) - ex = _MakeTfExample(enc, src_i, src_s, tgt_i, tgt_s) - if not ex: # Too long. - continue - encoded = ex.SerializeToString() - outf.write(encoded) - - -def main(_): - tf.logging.set_verbosity(tf.logging.INFO) - _RunEncoding() - - -if __name__ == '__main__': - tf.app.run(main) diff --git a/lingvo/trainer.py b/lingvo/trainer.py deleted file mode 100644 index 45ebc41e..00000000 --- a/lingvo/trainer.py +++ /dev/null @@ -1,1882 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -# pylint: disable=line-too-long -"""Trainer. - -To run locally: - -.. code-block:: bash - - $ bazel build -c opt //lingvo:trainer - $ bazel-bin/lingvo/trainer --logtostderr \ - --model=image.mnist.LeNet5 --mode=sync --logdir=/tmp/lenet5 \ - --run_locally=cpu - -To use GPU, add `--config=cuda` to build command and set `--run_locally=gpu`. -""" -# pylint: enable=line-too-long -import os -import re -import sys -import threading -import time - -from lingvo import base_trial -from lingvo import datasets -from lingvo import executor -from lingvo import model_imports -from lingvo import model_registry -import lingvo.compat as tf -from lingvo.core import base_model -from lingvo.core import base_model_params -from lingvo.core import checkpointer -from lingvo.core import cluster_factory -from lingvo.core import inference_graph_exporter -from lingvo.core import metrics -from lingvo.core import py_utils -from lingvo.core import summary_utils -import numpy as np - -from lingvo import base_runner - -# pylint:disable=g-direct-tensorflow-import -from tensorflow.core.protobuf.tpu import compilation_result_pb2 as tpu_compilation_result -from tensorflow.python.tpu import device_assignment as device_assignment_lib -from tensorflow.python.tpu import tpu -from tensorflow.python.tpu import tpu_function -from tensorflow.python.tpu import training_loop as tpu_training_loop -from tensorflow.python.tpu.ops import tpu_ops -# pylint:enable=g-direct-tensorflow-import - -tf.flags.DEFINE_string( - 'model', None, 'Name of the model class to train.' - 'Must be a model defined in the model_registry.') -tf.flags.DEFINE_string( - 'model_task_name', '', 'For multitask models: ' - 'select task to train/evaluate/decode. ' - 'Empty means to sample a task (training only).') -tf.flags.DEFINE_string('logdir', '', 'Log directory.') -tf.flags.DEFINE_bool( - 'interactive', False, - 'If True, enter interactive IPython for the controller job.') - -tf.flags.DEFINE_string( - 'run_locally', '', - 'Can be empty, cpu, or gpu. If not empty, ignores cluster configuration ' - 'flags and runs controller and trainer in a single local process.') - -tf.flags.DEFINE_string('tf_master', '', 'TF runtime.') -tf.flags.DEFINE_string( - 'cluster_spec', '', 'A tf.train.ClusterSpec to override the master. ' - 'The dict is specified as: job=host1:port1,host2:port2,' - 'host3:port3@job2=host3:port4,...') - -tf.flags.DEFINE_string( - 'mode', 'async', 'How this trainer binary is used. ' - 'async: used in an async training setup; ' - 'sync: used in a sync training setup; ' - 'shell: an interactive shell for development; ' - 'inspect_evaler: print evaler dataset names; ' - 'inspect_decoder: print decoder dataset names; ' - 'write_inference_graph: write inference graphs to logdir.') -tf.flags.DEFINE_string('job', '', 'trainer/controller/eval, etc.') -tf.flags.DEFINE_integer('task', 0, 'Task id within the job.') - -tf.flags.DEFINE_string('controller_job', '/job:controller', 'Job name.') -tf.flags.DEFINE_integer('controller_gpus', 0, 'Number of controller GPUs.') - -tf.flags.DEFINE_string('worker_job', '/job:trainer', 'Job name.') -tf.flags.DEFINE_integer('worker_replicas', 1, 'Number of replicas.') -tf.flags.DEFINE_integer('worker_gpus', 0, 'Number of gpus to use per replica.') -tf.flags.DEFINE_integer('worker_tpus', 0, 'Number of tpus to use per replica.') -tf.flags.DEFINE_integer('worker_num_tpu_hosts', 0, 'Number of tpu hosts.') -tf.flags.DEFINE_integer('worker_split_size', 1, - 'Number of devices for one split.') - -tf.flags.DEFINE_string('ps_job', '/job:ps', 'Job name') -tf.flags.DEFINE_integer('ps_replicas', 1, 'Number of replicas.') -tf.flags.DEFINE_integer('ps_gpus', 0, 'Number of gpus to use per replica.') - -tf.flags.DEFINE_string('input_job', '/job:input', 'Job name') -tf.flags.DEFINE_integer('input_replicas', 0, 'Number of replicas.') -tf.flags.DEFINE_string( - 'input_targets', '', 'Target network addresses for the ' - 'input job. E.g., a single ip:port, or a list of ' - 'comma-separated grpc://ip:port, etc.') - -tf.flags.DEFINE_string('evaler_job', '/job:evaler', 'Job name') -tf.flags.DEFINE_integer('evaler_replicas', 0, 'Number of replicas.') -tf.flags.DEFINE_integer('evaler_gpus', 0, 'Number of gpus to use per replica.') - -tf.flags.DEFINE_string('decoder_job', '/job:decoder', 'Job name') -tf.flags.DEFINE_integer('decoder_replicas', 0, 'Number of replicas.') -tf.flags.DEFINE_integer('decoder_gpus', 0, 'Number of gpus to use per replica.') -tf.flags.DEFINE_integer('decoder_tpus', 0, 'Number of tpus to use per replica.') - -tf.flags.DEFINE_integer( - 'inference_graph_random_seed', None, - 'Random seed to fix when exporting inference graph. ' - 'Not fixed when set to None.') -tf.flags.DEFINE_string( - 'inference_graph_filename', None, - 'Output inference graph filename. If unspecified, output two inference ' - 'graphs, one for CPU and one for TPU using the default settings.') -tf.flags.DEFINE_string( - 'inference_graph_device', None, - 'Type of device the output inference graph is for. This flag is applicable ' - 'only when FLAGS.inference_graph_filename is specified.') - -tf.flags.DEFINE_bool( - 'evaler_in_same_address_as_controller', False, - 'Whether or not evaler is in the same address space as ' - 'controller. This flag is meant for unittest only.') - -tf.flags.DEFINE_string( - 'vizier_reporting_job', 'evaler', - 'Job responsible for reporting metrics. This specifies a ' - 'job prefix, evaler will match all evaler jobs, while ' - 'evaler_dev and decoder_dev will only match the corresponding ' - 'jobs that are on the dev set.') - -tf.flags.DEFINE_bool( - 'add_summary', None, - 'Whether we should output summaries. The default value "None", enables ' - 'summaries based on the job type.') - - -@tf.flags.validator('vizier_reporting_job') -def _ValidateVizierReportingJob(value): - if value in ['evaler', 'decoder']: - return True - if value.startswith('evaler_') or value.startswith('decoder_'): - return True - raise tf.flags.ValidationError('Invalid value %s for vizier_reporting_job' % - value) - - -tf.flags.DEFINE_integer( - 'enqueue_max_steps', None, 'Max enqueue steps. -1 meaning no limit.' - ' This flag should be set for unit-test only.') - -tf.flags.DEFINE_integer('saver_max_to_keep', None, - 'Maximum number of recent checkpoints to keep.') - -tf.flags.DEFINE_float('saver_keep_checkpoint_every_n_hours', None, - 'How often to keep a checkpoint.') - -tf.flags.DEFINE_bool( - 'checkpoint_in_trainer_tpu', False, - 'Whether to enable checkpointing in TrainerTpu, allowing for ' - 'operation without a separate Controller task.' - 'This flag also disables checkpointing from the Controller, ' - 'but still allows it to write summaries.') - -tf.flags.DEFINE_string( - 'tpu', None, - 'The Cloud TPU on GCP to use for training. This should be either the name ' - 'used when creating the Cloud TPU, or a grpc://ip.address.of.tpu:8470 ' - 'url. If set, other cluster parameters (such as --cluster_spec) will be ' - 'configured automatically with TPUClusterResolver.') -tf.flags.DEFINE_string( - 'gcp_project', None, - 'Project name for the Cloud TPU-enabled project. If not specified, we ' - 'will attempt to automatically detect the GCE project from metadata.') -tf.flags.DEFINE_string( - 'tpu_zone', None, - 'GCE zone where the Cloud TPU is located in. If not specified, we ' - 'will attempt to automatically detect the GCE project from metadata.') - -# Please consider adding model params instead of adding flags. - -FLAGS = tf.flags.FLAGS - - -# useful for debugging. -def _StartShell(local_ns=None): - # An interactive shell is useful for debugging/development. - import IPython # pylint: disable=g-import-not-at-top - - user_ns = {} - if local_ns: - user_ns.update(local_ns) - user_ns.update(globals()) - IPython.start_ipython(argv=[], user_ns=user_ns) - - -class Controller(base_runner.BaseRunner): - """Controller for a training cluster.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._job_name = 'controller' - assert not self._model_task_name, 'Controller needs all tasks!' - self._control_dir = os.path.join(self._logdir, 'control') - tf.io.gfile.makedirs(self._control_dir) - self._summary_writer = self._CreateSummaryWriter(self._control_dir) - self._checkpoint_in_controller = True - if FLAGS.checkpoint_in_trainer_tpu: - self._checkpoint_in_controller = False - - with self._graph.as_default(), tf.container(self._container_id): - with self._cluster, tf.device(self._cluster.GetPlacer()): - self._model = self.params.Instantiate() - self._params = self._model.params - self._model.ConstructFPropBPropGraph() - self._summary_op = tf.summary.merge_all() - self._initialize_tables = tf.tables_initializer() - self._initialize_local_vars = tf.local_variables_initializer() - self._initialize_global_vars = tf.global_variables_initializer() - self.enqueue_ops = tf.get_collection(py_utils.ENQUEUE_OPS) - if self._checkpoint_in_controller: - self.checkpointer = self._CreateCheckpointer( - self._train_dir, - self._model, - init_op=self._initialize_global_vars) - - self._ExportMetrics(params=self.params) - self._model_analysis, self._total_num_params = summary_utils.ModelAnalysis( - self._model) - py_utils.LogMultiLines('MODEL ANALYSIS', self._model_analysis) - self._WriteToLog(self._model_analysis, self._control_dir, - 'model_analysis.txt') - self._WriteToLog(self.params.ToText(), self._control_dir, 'params.txt') - tf.io.write_graph(self._graph.as_graph_def(), self._control_dir, - 'train.pbtxt') - - def _CreateCheckpointer(self, train_dir, model, init_op=None): - """Wrapper method for override purposes.""" - return checkpointer.Checkpointer(train_dir, model, init_op) - - def Start(self): - self._RunLoop('controller', self._Loop) - - def StartEnqueueOp(self, op): - self._RunLoop( - 'controller/enqueue_op/%s' % op.name, self._LoopEnqueue, loop_args=[op]) - - def _Loop(self): - self._summary_writer.add_graph(self._graph) - with tf.container(self._container_id), self._GetSession() as sess: - if FLAGS.interactive: - # Into interactive debugging mode. - _StartShell(locals()) - return - - # This initializes local tables - sess.run(self._initialize_tables) - # This initializes local variables. - sess.run(self._initialize_local_vars) - - # TODO(zhifengc): Moves these options into params. - tp = self.params.train - summary_interval_steps = tp.summary_interval_steps - save_interval_seconds = tp.save_interval_seconds - next_summary_step = 1 - - if not self._checkpoint_in_controller: - global_step = self._WaitUntilInit(sess) - - while True: - now = time.time() - next_iteration_seconds = now + min( - 10, save_interval_seconds) # 10 seconds or less - - if self._checkpoint_in_controller: - # Init/restore variable if needed. - self.checkpointer.RestoreIfNeeded(sess) - - global_step = sess.run(self._model.global_step) - if self._trial.ShouldStop() or self._ShouldStop(sess, global_step): - tf.logging.info('Training finished.') - if self._checkpoint_in_controller: - self.checkpointer.Save(sess, global_step) - sess.close() - self._DequeueThreadComplete() - return - - if self._checkpoint_in_controller: - # Checkpoint if it's time. - self.checkpointer.MaybeSave(sess, self._model.global_step) - - # Summary. - if self._summary_op is not None and global_step >= next_summary_step: - tf.logging.info('Write summary @%s', global_step) - summary_str = sess.run(self._summary_op) - if isinstance(summary_str, np.ndarray) and summary_str.size == 0: - tf.logging.info('Skipping summary: %s', summary_str) - else: - self._summary_writer.add_summary(summary_str, global_step) - self._SummarizeValue(global_step, 'total_num_params', - self._total_num_params) - next_summary_step = global_step + summary_interval_steps - tf.logging.info('Write summary done: step %d', global_step) - - now = time.time() - if now < next_iteration_seconds: - time.sleep(next_iteration_seconds - now) - - def _SummarizeValue(self, steps, tag, value): - self._summary_writer.add_summary( - metrics.CreateScalarSummary(tag, value), steps) - - -class Trainer(base_runner.BaseRunner): - """Trainer on non-TPU.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._job_name = 'trainer' - with self._graph.as_default(), tf.container(self._container_id): - with self._cluster, tf.device(self._cluster.GetPlacer()): - self._model = self.params.Instantiate() - self._params = self._model.params - self._model.ConstructFPropBPropGraph() - self._initialize_tables = tf.tables_initializer() - self._initialize_local_vars = tf.local_variables_initializer() - self.enqueue_ops = tf.get_collection(py_utils.ENQUEUE_OPS) - tf.logging.info('Trainer number of enqueue ops: %d', - len(self.enqueue_ops)) - - try: - self._task_probs_summary_writers = [] - for task in self._model.task_schedule.tasks: - path = os.path.join(os.path.join(self._train_dir, task)) - tf.io.gfile.makedirs(path) - self._task_probs_summary_writers.append(self._CreateSummaryWriter(path)) - except AttributeError: - tf.logging.info('AttributeError. Expected for single task models.') - self._task_probs_summary_writers = [] - - self._step_rate_tracker = summary_utils.StepRateTracker() - - # Saves the graph def. - if self.params.cluster.task > 0: - self._summary_writer = None - else: - self._WriteToLog(self.params.ToText(), self._train_dir, - 'trainer_params.txt') - self._summary_writer = self._CreateSummaryWriter(self._train_dir) - tf.io.write_graph(self._graph.as_graph_def(), self._train_dir, - 'train.pbtxt') - worker_id = self.params.cluster.task - self._start_up_delay_steps = (((worker_id + 1) * worker_id / 2) * - self.params.train.start_up_delay_steps) - - def _SummarizeValue(self, steps, tag, value, writer=None): - if writer: - writer.add_summary(metrics.CreateScalarSummary(tag, value), steps) - elif self._summary_writer: - self._summary_writer.add_summary( - metrics.CreateScalarSummary(tag, value), steps) - - def Start(self): - self._RunLoop('trainer', self._Loop) - - def StartEnqueueOp(self, op): - self._RunLoop( - 'trainer/enqueue_op/%s' % op.name, self._LoopEnqueue, loop_args=[op]) - - def _LoopEnqueue(self, op): - # Evaler/Controller jobs may find that the trial is infeasible and report - # done earlier. This is an important check since the trainer may retry - # indefinitely without it. - if self._trial.ShouldStop(): - tf.logging.info('Training skipped (trial requested to stop).') - return - return super()._LoopEnqueue(op) - - def _Loop(self): - # Evaler/Controller jobs may find that the trial is infeasible and report - # done earlier. This is an important check since the trainer may retry - # indefinitely without it. - if self._trial.ShouldStop(): - tf.logging.info('Training skipped (trial requested to stop).') - return - with tf.container( - self._container_id), self._cluster, self._GetSession() as sess: - # This initializes local tables - sess.run(self._initialize_tables) - # This initializes local variables. - sess.run(self._initialize_local_vars) - global_step = self._WaitUntilInit(sess, self._start_up_delay_steps) - - status_interval_steps = 100 - next_status_step = 1 - eval_metrics = None - while True: - if (self._trial.ShouldStopAndMaybeReport(global_step, eval_metrics) or - self._ShouldStop(sess, global_step)): - tf.logging.info('Training finished.') - if self._early_stop: - time.sleep(300) # controller hangs if it doesn't finish first - self._DequeueThreadComplete() - return - - # If a task is explicitly specified, only train that task. - if self._model_task_name: - task = self._model.GetTask(self._model_task_name) - else: - # Note: This is a slightly stale global_step value from the previous - # sess.run() call. - # For multi-task models, `self._model.task_schedule.cur_probs` will - # be updated. - task = self._model.SampleTask(global_step) - if self._task_probs_summary_writers: - for index, prob in enumerate(self._model.task_schedule.cur_probs): - self._SummarizeValue(global_step, 'task_probability', prob, - self._task_probs_summary_writers[index]) - try: - for index, task in enumerate(self._model.tasks): - self._SummarizeValue(global_step, 'task_weight', - sess.run(task.vars.task_weight), - self._task_probs_summary_writers[index]) - except AttributeError: - pass - - (_, eval_metrics, per_example_tensors) = sess.run([ - task.train_op, - task.eval_metrics, - task.per_example_tensors, - ]) - # Explicitly fetch global_step after running train_op. - # TODO(b/151181934): Investigate this behavior further. - task_global_step = sess.run(task.global_step) - task.ProcessFPropResults(sess, task_global_step, eval_metrics, - per_example_tensors) - - global_step = sess.run(self._model.global_step) - step_rate, example_rate, total_examples = ( - self._step_rate_tracker.ComputeStepRate( - global_step, eval_metrics['num_samples_in_batch'][0])) - self._SummarizeValue(global_step, 'global_step/sec', step_rate) - self._SummarizeValue(global_step, 'examples/sec', example_rate) - self._SummarizeValue(global_step, 'total_samples', total_examples) - - msg = 'step:%6d, steps/sec: %0.2f, examples/sec: %0.2f' % ( - global_step, step_rate, example_rate) - for key, (val, _) in sorted(eval_metrics.items()): - msg += ' %s:%.8g' % (key, val) - self._SummarizeValue(global_step, key, val) - if global_step >= next_status_step: - self._SetStatusMessage(msg) - self._ExportMetrics( - # Metrics expects python int, but global_step is numpy.int64. - global_step=int(global_step), - step_rate=step_rate, - example_rate=example_rate) - next_status_step = global_step + status_interval_steps - else: - tf.logging.info(msg) - self._model.ProcessFPropResults(sess, global_step, eval_metrics, - per_example_tensors) - - -class TrainerTpu(base_runner.BaseRunner): - """Trainer on TPU.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._job_name = 'trainer_tpu' - - # Multiple TPU trainer tasks not tested/implemented. - assert self._cluster.num_replicas == 1 - data_parallelism = self._cluster.num_splits_per_client - assert data_parallelism - num_devices_per_split = self._cluster.num_devices_per_split - tf.logging.info('data_parallelism: %d, num_devices_per_split: %d', - data_parallelism, num_devices_per_split) - - self._steps_per_loop = min(self.params.train.tpu_steps_per_loop, - self.params.train.max_steps) - self._step_rate_tracker = summary_utils.StepRateTracker() - - self._cluster_def = self._cluster.worker_cluster_def - self._compile_op = None - - self._initialized = threading.Event() - - tf.logging.info( - 'Creating TrainerTpu using data parallelism %s ' - 'and %s steps_per_loop', data_parallelism, self._steps_per_loop) - - @py_utils.RetryOnTransientTfError() - def _WaitUntilInitTpu(): - """Wait until the model is ready.""" - try: - # tpu.initialize_system() is called with None as embedding_config, as - # embedding_config is not available yet. Later in _Loop, it is called - # with the correct embedding_config. Since it cannot be called twice in - # the same graph with different embedding_config, we use a dummy_graph - # here. - dummy_graph = tf.Graph() - with dummy_graph.as_default(): - tpu_initialize_system_op = tf.tpu.initialize_system( - embedding_config=None, job=None) - - with self._GetSession(graph=dummy_graph) as sess: - topology = sess.run(tpu_initialize_system_op) - - device_assignment = device_assignment_lib.device_assignment( - topology, - computation_shape=py_utils.ComputationShape(num_devices_per_split, - topology), - num_replicas=data_parallelism) - py_utils.SetTpuDeviceAssignment(device_assignment) - tf.logging.info('device_assignment.core_assignment: %s', - str(device_assignment.core_assignment)) - tf.logging.info('device_assignment.topology.device_coordinates: %s', - str(device_assignment.topology.device_coordinates)) - except py_utils.transient_tf_errors as e: - tf.logging.info('TPU initialization failed: %s', e) - raise - - _WaitUntilInitTpu() - - with self._graph.as_default(), tf.container(self._container_id): - with self._cluster, tf.device(self._cluster.job_spec.name): - with cluster_factory.SetImmediatelyInstantiateVariables(False): - self._model = self.params.Instantiate() - self._task = self._model.GetTask() - self._task.input.InstantiateVariables() - self._task.input.CreateTpuEnqueueOps() - self._eval_metrics = metrics.TpuEvalMetrics() - # Needed due to the AddExtraTheta() reference to global_step when - # instantiating the InputGenerator. - _ = py_utils.GetOrCreateGlobalStepVar() - - def TpuTrainStep(*args): - """Train a shard of a batch on a single TPU core. - - Args: - *args: metrics values from previous steps. - - Returns: - New summed metrics values and a train_op. - """ - self._model.InstantiateVariables() - self._model.ConstructFPropBPropGraph() - self._load_ops = tf.get_collection(py_utils.TPU_EMBEDDING_LOAD_OPS) - self._retrieve_ops = tf.get_collection( - py_utils.TPU_EMBEDDING_RETRIEVE_OPS) - tpu_embedding_collection = tf.get_collection(py_utils.TPU_EMBEDDING) - self._tpu_embedding = ( - tpu_embedding_collection[0] if tpu_embedding_collection else None) - - per_step_eval_metrics = self._eval_metrics.SetMetrics( - self._task.eval_metrics, args) - outfeed_op = self._OutfeedEnqueue(self._task.per_example_tensors) - summed_metrics = [] - assert len(per_step_eval_metrics) == len(args) - with tf.control_dependencies([outfeed_op]): - for x, y in zip(per_step_eval_metrics, args): - summed_metrics.append(x + y) - return summed_metrics + [self._task.train_op] - - @tpu_function.on_device_training_loop - def TpuTrain(): - loop_result = tpu_training_loop.repeat( - self._steps_per_loop, - TpuTrainStep, - inputs=self._eval_metrics.initial_values, - name='train_loop') - # Final metrics are the avg across self._steps_per_loop steps. - return self._eval_metrics.FinalizeMetrics(loop_result) - - self._compile_op, batch_parallel_res = tpu.split_compile_and_shard( - TpuTrain, - num_shards=data_parallelism, - device_assignment=py_utils.GetTpuDeviceAssignment()) - outfeed_dequeue_op = self._OutfeedDequeueLoop( - self._task.per_example_tensors, self._steps_per_loop, - self._cluster.num_splits_per_client) - self._task.input.CreateTpuEmbeddingEnqueueOps() - - def _ConstructPostTrainingLoop(train_loop_op, outfeed_dequeue_op): - """Returns the op for tpu training with tail cpu computation.""" - # Adds a tail computation that is run after the tpu_training loop - # step finishes. This allows us to run certain computation that - # acts on the variable between tpu_train_loop iterations and - # amortizing the cost of the operations. Alternative of running - # tpu.outside_compilation & using tf.cond is expenseive. - with tf.control_dependencies(train_loop_op): - self._model.ConstructPostTrainingLoop() - with tf.control_dependencies([self._task.post_training_loop_op]): - return ([[tf.identity(o) for o in train_loop_op], - outfeed_dequeue_op]) - - # Get metric result from a single replica; they are all same here. - all_tpu_ops = [t[0] for t in batch_parallel_res] - self._tpu_train_ops = ( - _ConstructPostTrainingLoop(all_tpu_ops, outfeed_dequeue_op)) - - self._initialize_local_vars = tf.local_variables_initializer() - self._initialize_global_vars = tf.global_variables_initializer() - self._initialize_tables = tf.tables_initializer() - - if FLAGS.checkpoint_in_trainer_tpu: - self.checkpointer = checkpointer.Checkpointer( - self._train_dir, self._model, init_op=self._initialize_global_vars) - - self.enqueue_ops = self._task.input.tpu_infeed_op - tf.logging.info('Trainer number of enqueue ops: %d', - len(self.enqueue_ops)) - - self._summary_writer = self._CreateSummaryWriter(self._train_dir) - if FLAGS.checkpoint_in_trainer_tpu: - self._model_analysis, self._total_num_params = ( - summary_utils.ModelAnalysis(self._model)) - py_utils.LogMultiLines('MODEL ANALYSIS', self._model_analysis) - self._WriteToLog(self._model_analysis, self._train_dir, - 'model_analysis.txt') - # Saves the graph def. - tf.io.write_graph(self._graph.as_graph_def(), self._train_dir, - 'train.pbtxt') - - # Saves the trainer params. - self._WriteToLog(self.params.ToText(), self._train_dir, - 'trainer_params.txt') - - def _GetSession(self, **kwargs): - return super()._GetSession(cluster_def=self._cluster_def, **kwargs) - - def _OutfeedEnqueue(self, per_example_tensors): - if not per_example_tensors: - return tf.no_op() - per_example_tensors = py_utils.NestedMap(per_example_tensors) - return tpu_ops.outfeed_enqueue_tuple(per_example_tensors.Flatten()) - - def _OutfeedDequeueLoop(self, per_example_tensors, num_loops, num_devices): - """Process all per-example tensor outfeed data for a TPU sess.run. - - Args: - per_example_tensors: dict of key -> tensor as generated by TpuTrainStep. - num_loops: number of times that TpuTrainStep will be executed by TpuTrain. - num_devices: number of TPU cores assigned to this process. - - Returns: - A dict of per-example tensors from the latest TpuTrainStep. - """ - if not per_example_tensors: - return tf.no_op() - - tensor_shapes = [ - py_utils.GetShape(per_example_tensors[key]) - for key in sorted(per_example_tensors) - ] - tensor_types = [ - tf.as_dtype(per_example_tensors[key].dtype) - for key in sorted(per_example_tensors) - ] - - def LoopBody(i, *input_arrays): - """Process outfeed data for a single TpuTrainStep. - - Args: - i: current loop index. - *input_arrays: One tf.TensorArray per outfeed tensor. - - Returns: - i+1 (new index) plus post-write tf.TensorArray handles. - """ - # Outfeed ops execute on each JF node, so they must be located on the - # nodes. - outfeed_devices = [] - device_assignment = py_utils.GetTpuDeviceAssignment() - assert device_assignment - for replica in range(device_assignment.num_replicas): - for core in range(device_assignment.num_cores_per_replica): - with tf.device(device_assignment.host_device(replica, core)): - outfeed_devices.append( - tpu_ops.outfeed_dequeue_tuple( - tensor_types, - tensor_shapes, - device_ordinal=device_assignment.tpu_ordinal(replica, - core))) - offset = i * num_devices - output_arrays = list(input_arrays) - # Each output_array holds a different per-example tensor. We get results - # for each tensor from each TPU for each TpuTrainStep call. - for j in range(len(output_arrays)): - for k in range(len(outfeed_devices)): - output_arrays[j] = output_arrays[j].write(offset + k, - outfeed_devices[k][j]) - - return tuple([i + 1] + output_arrays) - - def LoopCond(i, *output_arrays): - del output_arrays - return i < num_loops - - output_arrays = [ - tf.TensorArray( - tensor_types[i], - size=num_loops * num_devices, - element_shape=tensor_shapes[i]) for i in range(len(tensor_shapes)) - ] - # Loop once for each time that TpuTrainStep runs. - output_arrays = tf.while_loop( - LoopCond, LoopBody, [0] + output_arrays, parallel_iterations=1)[1:] - concatenated_arrays = [array.concat() for array in output_arrays] - return dict(zip(sorted(per_example_tensors), concatenated_arrays)) - - def Start(self): - # Run training. - self._RunLoop('trainer', self._Loop) - - def _InfeedLoop(self, sess): - tf.logging.info('_InfeedLoop start') - for _ in range(self._steps_per_loop): - sess.run(self.enqueue_ops) - - def StartEnqueueOp(self, op): - # When retrieve ops for TPU embedding is present, we use _InfeedLoop above - # instead to make sure enqueue and retrieve does not happen at the same - # time as required by TPU embedding. - # We can remove this by using a tf.while_loop driven infeed op. - if self._retrieve_ops: - return - self._RunLoop( - 'trainer/enqueue_op/%s' % op.name, self._LoopEnqueue, loop_args=[op]) - - def _SummarizeValue(self, steps, tag, value): - self._summary_writer.add_summary( - metrics.CreateScalarSummary(tag, value), steps) - - def _LoopEnqueue(self, op): - # Evaler/Controller jobs may find that the trial is infeasible and report - # done earlier. This is an important check since the trainer may retry - # indefinitely without it. - if self._trial.ShouldStop(): - tf.logging.info('Training skipped (trial requested to stop).') - return - # Wait for _Loop to initialize variables first before attempting to infeed. - tf.logging.info('_LoopEnqueue waiting for _initialized...') - self._initialized.wait() - tf.logging.info('_LoopEnqueue proceeding.') - - # The global step may not be initialized in this thread if the target server - # uses session state isolation (e.g. Cloud TPUs). - sess = self._GetSession() - if FLAGS.checkpoint_in_trainer_tpu: - self.checkpointer.RestoreGlobalStepIfNeeded(sess) - - return super()._LoopEnqueue(op, sess) - - def _Loop(self): - # Evaler/Controller jobs may find that the trial is infeasible and report - # done earlier. This is an important check since the trainer may retry - # indefinitely without it. - if self._trial.ShouldStop(): - tf.logging.info('Training skipped (trial requested to stop).') - self._DequeueThreadComplete() - return - with tf.container( - self._container_id), self._cluster, self._GetSession() as sess: - config_proto = ( - self._tpu_embedding.config_proto - if self._tpu_embedding is not None else None) - sess.run( - tf.tpu.initialize_system(embedding_config=config_proto, job=None)) - sess.run(self._initialize_tables) - sess.run(self._initialize_local_vars) - - if FLAGS.run_locally == 'tpu': - sess.run(self._initialize_global_vars) - - self._SetStatusMessage('Compiling ...') - compilation_result = sess.run(self._compile_op) - comp_result_proto = tpu_compilation_result.CompilationResultProto() - comp_result_proto.ParseFromString(compilation_result) - if comp_result_proto.status_error_message: - tf.logging.fatal('Compilation failed: {}'.format( - comp_result_proto.status_error_message)) - self._SetStatusMessage('Compiling done.') - - if FLAGS.checkpoint_in_trainer_tpu: - # For b/134415393 -- better to initialize to a known state than - # rely on what's in the session on the trainer/TPU worker. - tf.logging.info('TrainerTpu: Force restore or initialize.') - self.checkpointer.Restore(sess, force_reinitialize=True) - - global_step = sess.run(self._model.global_step) - self._initialized.set() - eval_metrics = None - - sess.run(self._load_ops) - while True: - train_steps_start = time.perf_counter() - if FLAGS.checkpoint_in_trainer_tpu: - # Init/restore variable if needed. - self.checkpointer.RestoreIfNeeded(sess) - if self._trial.ShouldStopAndMaybeReport(global_step, eval_metrics): - # Early terminate gracefully by setting a new max step horizon: three - # more TPU steps to ensure that the enqueue ops can gracefully - # terminate as well. - if self._max_steps is None: - self._max_steps = global_step + 3 * self._steps_per_loop - tf.logging.info('Early stopping at step: %d', self._max_steps) - - if self._ShouldStop(sess, global_step): - tf.logging.info('Training finished.') - if FLAGS.checkpoint_in_trainer_tpu: - self.checkpointer.Save(sess, global_step) - self._DequeueThreadComplete() - return - - if self._retrieve_ops: - infeed_loop_thread = threading.Thread( - target=self._InfeedLoop, args=(sess,)) - infeed_loop_thread.start() - - tpu_train_op_start = time.perf_counter() - values, outfeeds = sess.run(self._tpu_train_ops) - tpu_train_op_secs = time.perf_counter() - tpu_train_op_start - - if self._retrieve_ops: - infeed_loop_thread.join() - tf.logging.info('Retrieve params.') - sess.run(self._retrieve_ops) - tf.logging.info('Retrieve params done.') - - self._eval_metrics.PackMetricsValues(values) - eval_metrics = self._eval_metrics.metrics - - # Note: global_step is incremented by self._steps_per_loop by the - # previous sess.run call. - task_global_step = sess.run(self._task.global_step) - global_step = sess.run(self._model.global_step) - - if not self._task.per_example_tensors: - outfeeds = {} - self._task.ProcessFPropResults(sess, task_global_step, eval_metrics, - outfeeds) - self._model.ProcessFPropResults(sess, global_step, eval_metrics, - outfeeds) - - step_rate, example_rate, total_examples = ( - self._step_rate_tracker.ComputeStepRate( - global_step, - eval_metrics['num_samples_in_batch'][0] * self._steps_per_loop)) - self._SummarizeValue(global_step, 'global_step/sec', step_rate) - self._SummarizeValue(global_step, 'examples/sec', example_rate) - self._SummarizeValue(global_step, 'total_samples', total_examples) - if FLAGS.checkpoint_in_trainer_tpu: - self._SummarizeValue(global_step, 'total_num_params', - self._total_num_params) - msg = 'step:%6d, steps/sec: %0.2f, examples/sec: %0.2f' % ( - global_step, step_rate, example_rate) - for key, (val, _) in sorted(eval_metrics.items()): - msg += ' %s:%.8g' % (key, val) - self._SummarizeValue(global_step, key, val) - - self._SetStatusMessage(msg) - - checkpoint_write_secs = 0.0 - if FLAGS.checkpoint_in_trainer_tpu: - checkpoint_write_start = time.perf_counter() - checkpoint_saved = self.checkpointer.MaybeSave( - sess, self._model.global_step) - if checkpoint_saved: - checkpoint_write_secs = time.perf_counter() - checkpoint_write_start - train_steps_secs = time.perf_counter() - train_steps_start - self._ExportMetrics( - # Metrics expects python int, but global_step is numpy.int64. - global_step=int(global_step), - step_rate=step_rate, - example_rate=example_rate, - tpu_train_op_secs=tpu_train_op_secs, - checkpoint_write_secs=checkpoint_write_secs, - total_train_steps_secs=train_steps_secs) - - -class Evaler(base_runner.BaseRunner): - """Evaler.""" - - def __init__(self, eval_type, *args, **kwargs): - super().__init__(*args, **kwargs) - self._job_name = 'evaler_' + eval_type - self._output_name = 'eval_' + eval_type - self.params.cluster.do_eval = True - self._cluster = cluster_factory.Cluster(self.params.cluster) - self._eval_dir = os.path.join(self._logdir, self._output_name) - if self._model_task_name: - self._eval_dir += '_' + str(self._model_task_name) - tf.io.gfile.makedirs(self._eval_dir) - - self._eval_path = None - # Multitask params doesn't have 'task'. - if 'task' in self.params: - self._eval_path = checkpointer.GetSpecificCheckpoint( - self.params.task.eval.load_checkpoint_from) - - self._summary_writer = self._CreateSummaryWriter(self._eval_dir) - self._should_report_metrics = self._job_name.startswith( - FLAGS.vizier_reporting_job) - - with self._graph.as_default(), tf.container(self._container_id): - with self._cluster, tf.device(self._cluster.GetPlacer()): - self._model = self.params.Instantiate() - self._params = self._model.params - self._model.ConstructFPropGraph() - self._task = self._model.GetTask(self._model_task_name) - self._initialize_tables = tf.tables_initializer() - self._initialize_local_vars = tf.local_variables_initializer() - # No queues are allowed for eval models. - self.enqueue_ops = tf.get_collection(py_utils.ENQUEUE_OPS) - assert not self.enqueue_ops - self.checkpointer = self._CreateCheckpointer(self._train_dir, self._model) - - # Saves the graph def. - self._WriteToLog(self.params.ToText(), self._eval_dir, 'params.txt') - if self.params.cluster.task == 0: - tf.io.write_graph(self._graph.as_graph_def(), self._eval_dir, - '%s.pbtxt' % self._output_name) - - def _CreateCheckpointer(self, train_dir, model): - """Wrapper method for override purposes.""" - return checkpointer.Checkpointer(train_dir, model) - - def Start(self): - self._RunLoop(self._job_name, self._Loop) - - def _Loop(self): - """The main loop.""" - with tf.container( - self._container_id), self._cluster, self._GetSession() as sess: - # This initializes local tables - sess.run(self._initialize_tables) - # This initializes local variables. - sess.run(self._initialize_local_vars) - - if self._eval_path: - self._EvalOnce(self._eval_path, sess) - else: - path = None - while True: - path = self._FindNewCheckpoint(path, sess) - if not path or self._EvalOnce(path, sess): - break - - # Maybe evaluate the last checkpoint if we are not given a specific - # checkpoint to evaluate. - if self._eval_path is None: - self.EvalLatestCheckpoint(path) - - if self._should_report_metrics: - tf.logging.info('Reporting trial done.') - self._trial.ReportDone() - tf.logging.info('Evaluation finished.') - - def EvalLatestCheckpoint(self, last_path=None): - """Runs eval once on the latest checkpoint.""" - with tf.container( - self._container_id), self._cluster, self._GetSession() as sess: - # This initializes local tables - sess.run(self._initialize_tables) - # This initializes local variables. - sess.run(self._initialize_local_vars) - path = tf.train.latest_checkpoint(self._train_dir) - if not path: - tf.logging.info('No checkpoint available.') - return - elif path == last_path: - tf.logging.info('Latest checkpoint was already evaluated.') - return - - self._EvalOnce(path, sess) - - def EvalCheckpoint(self, ckpt_id): - with tf.container(self._container_id), self._GetSession() as sess: - # This initializes local tables - sess.run(self._initialize_tables) - # This initializes local variables. - sess.run(self._initialize_local_vars) - path = '{}/ckpt-{:08d}'.format(self._train_dir, ckpt_id) - self._EvalOnce(path, sess) - - def _EvalOnce(self, path, sess): - """Runs evaluation for a batch of samples. - - Args: - path: checkpoint path. - sess: the tf Session. - - Returns: - should_stop. - """ - - if not FLAGS.evaler_in_same_address_as_controller: - self.checkpointer.RestoreFromPath(sess, path) - - global_step = sess.run(py_utils.GetGlobalStep()) - # Check after how many steps checkpoint got saved. - # And decide whether to run an evaluation. - if global_step < self._task.params.eval.start_eval_after: - return False - - if self._task.params.input.resettable: - tf.logging.info('Resetting input_generator.') - self._task.input_generator.Reset(sess) - - metrics_dict = { - name: metrics.AverageMetric() for name in self._task.eval_metrics - } - num_samples_metric = metrics_dict['num_samples_in_batch'] - while (num_samples_metric.total_value < - self._task.params.eval.samples_per_summary): - # NOTE: We intentionally do not let FProp generate summaries by default, - # because evaler calls FProp multiple times for each checkpoint. Multiple - # summaries at the same step is often confusing. Instead, models should - # update eval_metrics and generate aggregate summaries. - ans = sess.run(self._task.eval_metrics) - for name, (value, weight) in ans.items(): - metrics_dict[name].Update(value, weight) - tf.logging.info('Total examples done: %d/%d', - num_samples_metric.total_value, - self._task.params.eval.samples_per_summary) - - # Replace average values with total values for certain metrics. - if 'num_predictions' in metrics_dict: - metrics_dict['num_predictions'].total_weight = 1.0 - if 'num_words' in metrics_dict: - metrics_dict['num_words'].total_weight = 1.0 - - # When we have evaluated so many samples, generate a summary. - self._WriteSummaries( - self._summary_writer, - os.path.basename(self._eval_dir), - global_step, {k: v.Summary(k) for k, v in metrics_dict.items()}, - text_filename=os.path.join(self._eval_dir, - 'score-{:08d}.txt'.format(global_step))) - - should_stop = global_step >= self.params.train.max_steps - if self._should_report_metrics: - tf.logging.info('Reporting eval measure for step %d.' % global_step) - trial_should_stop = self._trial.ReportEvalMeasure(global_step, - metrics_dict, path) - should_stop = should_stop or trial_should_stop - return should_stop - - -def GetDecoderDir(logdir, decoder_type, model_task_name): - if model_task_name: - decoder_dir = '%s_%s' % (decoder_type, model_task_name) - else: - decoder_dir = decoder_type - return os.path.join(logdir, decoder_dir) - - -def _GetCheckpointIdForDecodeOut(ckpt_id_from_file, global_step): - """Retrieve the checkpoint id for the decoder out file. - - Compares the checkpoint id found in the checkpoint file name to global - step. If they diverge, uses the retrieved id and prints a warning. - - Args: - ckpt_id_from_file: Checkpoint Id from the checkpoint file path. - global_step: int specifying the global step of the model. - - Returns: - Checkpoint id as int. - """ - tf.logging.info('Loaded checkpoint is at global step: %d', global_step) - tf.logging.info('Checkpoint id according to checkpoint path: %d', - ckpt_id_from_file) - if global_step != ckpt_id_from_file: - tf.logging.warning( - 'Checkpoint id %d != global step %d. ' - 'Will use checkpoint id from checkpoint file for ' - 'writing decoder output.', ckpt_id_from_file, global_step) - return ckpt_id_from_file - - -class Decoder(base_runner.BaseRunner): - """Decoder.""" - - def __init__(self, decoder_type, *args, **kwargs): - super().__init__(*args, **kwargs) - self._job_name = 'decoder_' + decoder_type - self.params.cluster.do_eval = True - # Here we go! - self._cluster = cluster_factory.Cluster(self.params.cluster) - self._decoder_dir = GetDecoderDir(self._logdir, self._job_name, - self._model_task_name) - tf.io.gfile.makedirs(self._decoder_dir) - - self._decode_path = None - # Multitask params doesn't have 'task'. - if 'task' in self.params: - self._decode_path = checkpointer.GetSpecificCheckpoint( - self.params.task.eval.load_checkpoint_from) - - self._summary_writer = self._CreateSummaryWriter(self._decoder_dir) - self._should_report_metrics = self._job_name.startswith( - FLAGS.vizier_reporting_job) - - with self._graph.as_default(), tf.container(self._container_id): - with self._cluster, tf.device(self._cluster.GetPlacer()): - self._model = self.params.Instantiate() - self._params = self._model.params - self._task = self._model.GetTask(self._model_task_name) - # Note, different graphs are being constructed for different model - # tasks, which may result in different node names being chosen. - # Obviously, variable names has to be stay the same between train and - # decode. - cluster = self._cluster - with tf.device(cluster.input_device): - input_batch = (self._task.input_generator.GetPreprocessedInputBatch()) - - self._dec_output = self._task.Decode(input_batch) - self._summary_op = tf.summary.merge_all() - self.checkpointer = self._CreateCheckpointer(self._train_dir, - self._model) - self._initialize_tables = tf.tables_initializer() - self._initialize_local_vars = tf.local_variables_initializer() - # No queues are allowed for decoder models. - # Ugh, why??? - self.enqueue_ops = tf.get_collection(py_utils.ENQUEUE_OPS) - assert not self.enqueue_ops - - # Saves the graph def. - self._WriteToLog(self.params.ToText(), self._decoder_dir, 'params.txt') - if self.params.cluster.task == 0: - tf.io.write_graph(self._graph.as_graph_def(), self._decoder_dir, - '%s.pbtxt' % self._job_name) - - def _CreateCheckpointer(self, train_dir, model): - """Wrapper method for override purposes.""" - return checkpointer.Checkpointer(train_dir, model) - - def Start(self): - self._RunLoop(self._job_name, self._Loop) - - def _Loop(self): - with tf.container(self._container_id), self._cluster, self._GetSession( - inline=False) as sess: - # This initializes local tables - sess.run(self._initialize_tables) - # This initializes local variables. - sess.run(self._initialize_local_vars) - - if self._decode_path: - self.DecodeCheckpoint(sess, self._decode_path) - else: - path = None - while True: - path = self._FindNewCheckpoint(path, sess) - if not path or self.DecodeCheckpoint(sess, path): - break - - # Maybe decode the last checkpoint if we are not given a specific - # checkpoint to decode. - if self._decode_path is None: - self.DecodeLatestCheckpoint(path) - - if self._should_report_metrics: - tf.logging.info('Reporting trial done.') - self._trial.ReportDone() - tf.logging.info('Decoding finished.') - - @classmethod - def GetDecodeOutPath(cls, decoder_dir, checkpoint_id): - """Gets the path to decode out file.""" - out_dir = cls._GetTtlDir(decoder_dir, duration='7d') - return os.path.join(out_dir, 'decoder_out_%09d' % checkpoint_id) - - def GetCkptIdFromFile(self, checkpoint_path): - return int(re.sub(r'.*ckpt-', '', checkpoint_path)) - - def DecodeCheckpoint(self, sess, checkpoint_path): - """Decodes `samples_per_summary` examples using `checkpoint_path`.""" - p = self._task.params - ckpt_id_from_file = self.GetCkptIdFromFile(checkpoint_path) - if ckpt_id_from_file < p.eval.start_decoder_after: - return False - samples_per_summary = p.eval.decoder_samples_per_summary - if not samples_per_summary: - samples_per_summary = p.eval.samples_per_summary - self.checkpointer.RestoreFromPath(sess, checkpoint_path) - - global_step = sess.run(py_utils.GetGlobalStep()) - - if self._task.params.input.resettable: - tf.logging.info('Resetting input_generator.') - self._task.input.Reset(sess) - - dec_metrics = self._task.CreateDecoderMetrics() - if not dec_metrics: - tf.logging.info('Empty decoder metrics') - return - buffered_decode_out = [] - num_examples_metric = dec_metrics['num_samples_in_batch'] - start_time = time.time() - while num_examples_metric.total_value < samples_per_summary: - tf.logging.info('Fetching dec_output.') - fetch_start = time.time() - run_options = tf.RunOptions(report_tensor_allocations_upon_oom=False) - if self._summary_op is None: - # No summaries were collected. - dec_out = sess.run(self._dec_output, options=run_options) - else: - dec_out, summary = sess.run([self._dec_output, self._summary_op], - options=run_options) - self._summary_writer.add_summary(summary, global_step) - post_process_start = time.time() - tf.logging.info('Done fetching (%f seconds)' % - (post_process_start - fetch_start)) - decode_out = self._task.PostProcessDecodeOut(dec_out, dec_metrics) - if decode_out: - buffered_decode_out.extend(decode_out) - tf.logging.info( - 'Total examples done: %d/%d ' - '(%f seconds decode postprocess)', num_examples_metric.total_value, - samples_per_summary, - time.time() - post_process_start) - tf.logging.info('Done decoding ckpt: %s', checkpoint_path) - - summaries = {k: v.Summary(k) for k, v in dec_metrics.items()} - elapsed_secs = time.time() - start_time - example_rate = num_examples_metric.total_value / elapsed_secs - summaries['examples/sec'] = metrics.CreateScalarSummary( - 'examples/sec', example_rate) - self._WriteSummaries( - self._summary_writer, - os.path.basename(self._decoder_dir), - global_step, - summaries, - text_filename=os.path.join(self._decoder_dir, - 'score-{:08d}.txt'.format(global_step))) - self._ExportMetrics( - # Metrics expects python int, but global_step is numpy.int64. - decode_checkpoint=int(global_step), - dec_metrics=dec_metrics, - example_rate=example_rate) - # global_step and the checkpoint id from the checkpoint file might be - # different. For consistency of checkpoint filename and decoder_out - # file, use the checkpoint id as derived from the checkpoint filename. - checkpoint_id = _GetCheckpointIdForDecodeOut(ckpt_id_from_file, global_step) - decode_out_path = self.GetDecodeOutPath(self._decoder_dir, checkpoint_id) - - decode_finalize_args = base_model.DecodeFinalizeArgs( - decode_out_path=decode_out_path, decode_out=buffered_decode_out) - self._task.DecodeFinalize(decode_finalize_args) - - should_stop = global_step >= self.params.train.max_steps - if self._should_report_metrics: - tf.logging.info('Reporting eval measure for step %d.' % global_step) - trial_should_stop = self._trial.ReportEvalMeasure(global_step, - dec_metrics, - checkpoint_path) - should_stop = should_stop or trial_should_stop - return should_stop - - def DecodeLatestCheckpoint(self, last_path=None): - """Runs decoder on the latest checkpoint.""" - with tf.container( - self._container_id), self._cluster, self._GetSession() as sess: - # This initializes local tables - sess.run(self._initialize_tables) - # This initializes local variables. - sess.run(self._initialize_local_vars) - path = tf.train.latest_checkpoint(self._train_dir) - if not path: - tf.logging.info('No checkpoint available.') - return - elif path == last_path: - tf.logging.info('Latest checkpoint was already decoded.') - return - self.DecodeCheckpoint(sess, path) - - -def _GetClusterSpecDict(): - """Parses the cluster_spec flag and returns a dict.""" - job_specs = FLAGS.cluster_spec.split('@') - cluster_spec_dict = {} - for job_spec in job_specs: - # ps_host=worker1:1231,worker2:1234 - job_machines = job_spec.split('=') - if len(job_machines) != 2: - raise ValueError('Invalid job specification: %s', job_spec) - cluster_spec_dict[job_machines[0]] = job_machines[1].split(',') - - return cluster_spec_dict - - -class RunnerManager: - """Helper class for managing runners.""" - - # This is a hack so these classes can be overridded with internal - # non-public implementations. - # pylint: disable=invalid-name - inference_graph_exporter = inference_graph_exporter - model_registry = model_registry - Controller = Controller - Trainer = Trainer - TrainerTpu = TrainerTpu - Evaler = Evaler - Decoder = Decoder - ExecutorTpu = executor.ExecutorTpu - TpuDecoder = Decoder # TODO: Don't just fall back to Decoder! - - # pylint: enable=invalid-name - - def __init__(self, model): - self._model_name = model - - def MaybeLaunchTensorFlow(self): - """Starts TF machinary in this process.""" - if FLAGS.run_locally or FLAGS.tpu: - return - - tf.logging.info('Launching tensorflow.') - - target = FLAGS.tf_master - if not target.startswith('localhost'): - # E.g., trainer_client is configured w/ FLAGS.tf_master pointing to - # another job. In that case, start a local server. - cluster_spec_dict = _GetClusterSpecDict() - self._tf_server = tf.distribute.Server( - tf.train.ClusterSpec(cluster_spec_dict), - job_name=FLAGS.job, - task_index=FLAGS.task) - target = self._tf_server.target - if not FLAGS.tf_master: - FLAGS.tf_master = target - with tf.Session(target).as_default(): - value = (tf.constant(1.) + tf.constant(1.)).eval() - assert value == 2.0, 'Something is really wrong.' - tf.logging.info('Launched tensorflow.') - - def GetExecutorParams(self): - """Get the params needed to instantiate the ExecutorTpu. - - Returns: - Tuple (dict, params): - - - ps_params_dict: high_level task_name -> ProgramScheduleParams - - train_cfg: Either a SingleTaskModelParams or MultiTaskModelParams. - """ - cluster = cluster_factory.Current() - self.UpdateClusterParamsFromFlags(cluster.params, 'executor_tpu') - ps_params_dict, train_cfg = executor.GetExecutorParams( - self._model_name, cluster.params, self.model_registry) - - return ps_params_dict, train_cfg - - def GetParamsForDataset(self, job_name, dataset_name): - """Returns params for job `job_name` on the dataset `dataset_name`.""" - # Get the current cluster and update its params from flags. - cluster = cluster_factory.Current() - print("GALVEZ:job_name=", job_name) - self.UpdateClusterParamsFromFlags(cluster.params, job_name) - print("GALVEZ:job_name=", cluster.params.job) - import sys; sys.stdout.flush() - with cluster_factory.Cluster(cluster.params): - try: - cfg = self.model_registry.GetParams(self._model_name, dataset_name) - except base_model_params.DatasetError as e: - dataset_name_retry = dataset_name.title() - tf.logging.warning( - 'Exception configuring dataset %s, retrying as %s: %s', - dataset_name, dataset_name_retry, e) - cfg = self.model_registry.GetParams(self._model_name, - dataset_name_retry) - tf.logging.warning('Succeeded after retrying as %s.' % - dataset_name_retry) - cfg.cluster = cluster.params - - # Updates a few params based on flags. - if FLAGS.enqueue_max_steps is not None: - cfg.train.enqueue_max_steps = FLAGS.enqueue_max_steps - if FLAGS.saver_max_to_keep is not None: - cfg.train.save_max_to_keep = FLAGS.saver_max_to_keep - if FLAGS.saver_keep_checkpoint_every_n_hours is not None: - cfg.train.save_keep_checkpoint_every_n_hours = FLAGS.saver_keep_checkpoint_every_n_hours - return cfg - - def MaybeConfigRunDistributed(self): - """If given a `FLAGS.cluster_spec`, update flags for running distributed.""" - if not FLAGS.cluster_spec: - return - job_specs = FLAGS.cluster_spec.split('@') - cluster_spec_dict = _GetClusterSpecDict() - if FLAGS.job == 'trainer_client': - FLAGS.tf_master = 'grpc://%s' % cluster_spec_dict['worker'][FLAGS.task] - for job in cluster_spec_dict.keys(): - if job.startswith('decoder_'): - assert len(job_specs) == 1, 'Decoder jobs must run on their own' - assert ',' not in job_specs[0], 'Only single machine supported' - FLAGS.decoder_job = '/job:%s' % job - FLAGS.decoder_replicas = 1 - if job.startswith('evaler_'): - assert len(job_specs) == 1, 'Evaler jobs must run on their own' - assert ',' not in job_specs[0], 'Only single machine supported' - FLAGS.evaler_job = '/job:%s' % job - FLAGS.evaler_replicas = 1 - if job.startswith('tpu_decoder_'): - assert len(job_specs) == 1, 'TPU Decoder jobs must run on their own' - assert ',' not in job_specs[0], 'Only single machine supported' - FLAGS.decoder_job = '/job:%s' % job - FLAGS.decoder_replicas = 1 - if FLAGS.mode == 'sync' and FLAGS.job in ('controller', 'trainer_client', - 'worker', 'executor_tpu'): - FLAGS.worker_job = '/job:worker' - FLAGS.worker_replicas = len(cluster_spec_dict['worker']) - FLAGS.ps_job = '/job:worker' - FLAGS.ps_replicas = FLAGS.worker_replicas - if FLAGS.mode == 'async' and FLAGS.job in ('controller', 'trainer', 'ps'): - FLAGS.worker_job = '/job:trainer' - FLAGS.worker_replicas = len(cluster_spec_dict['trainer']) - FLAGS.ps_job = '/job:ps' - FLAGS.ps_replicas = len(cluster_spec_dict['ps']) - - def MaybeConfigCloudTpu(self): - """If given `FLAGS.tpu`, update flags for running on a Cloud TPU.""" - if not FLAGS.tpu: - return - - if not FLAGS.job: - FLAGS.job = 'trainer_client' - - # Is it possible that TPU just simply won't do inference? - if (FLAGS.job not in ('trainer_client', 'executor_tpu') - and not FLAGS.job.startswith('tpu_decoder_')): - raise ValueError('Only trainer_client, executor_tpu, and tpu_decoder jobs are ' - 'supported on TPU.') - - cluster_resolver = tf.distribute.cluster_resolver.TPUClusterResolver( - tpu=FLAGS.tpu, - project=FLAGS.gcp_project, - zone=FLAGS.tpu_zone, - job_name=FLAGS.job) - cluster_spec_dict = cluster_resolver.cluster_spec().as_dict() - - FLAGS.mode = 'sync' - FLAGS.tf_master = cluster_resolver.master() - - FLAGS.worker_job = '/job:{}'.format(FLAGS.job) - FLAGS.worker_replicas = 1 - FLAGS.worker_num_tpu_hosts = len(cluster_spec_dict[FLAGS.job]) - FLAGS.worker_tpus = ( - cluster_resolver.num_accelerators()['TPU'] * FLAGS.worker_num_tpu_hosts) - FLAGS.decoder_tpus = ( - cluster_resolver.num_accelerators()['TPU'] * FLAGS.worker_num_tpu_hosts) - FLAGS.decoder_replicas = 1 - FLAGS.ps_job = FLAGS.worker_job - FLAGS.ps_replicas = FLAGS.worker_replicas - - FLAGS.cluster_spec = ('@'.join('{}={}'.format(job, ','.join(hosts)) - for job, hosts in cluster_spec_dict.items())) - - FLAGS.xla_device = 'tpu' - FLAGS.enable_asserts = False - FLAGS.checkpoint_in_trainer_tpu = True - - def UpdateClusterParamsFromFlags(self, cluster, job_name): - """Update `cluster` with a training cluster configuration from flags.""" - cluster.mode = FLAGS.mode - cluster.job = job_name - cluster.task = FLAGS.task - cluster.do_eval = job_name in ['evaler', 'decoder', 'tpu_decoder'] - - cluster.controller.name = FLAGS.controller_job - cluster.controller.gpus_per_replica = FLAGS.controller_gpus - - cluster.worker.name = FLAGS.worker_job - cluster.worker.replicas = FLAGS.worker_replicas - cluster.worker.gpus_per_replica = FLAGS.worker_gpus - cluster.worker.tpus_per_replica = FLAGS.worker_tpus - cluster.worker.num_tpu_hosts = FLAGS.worker_num_tpu_hosts - cluster.worker.devices_per_split = FLAGS.worker_split_size - if FLAGS.tpu: - job_name = cluster.worker.name.replace('/job:', '', 1) - worker_hosts = _GetClusterSpecDict()[job_name] - cluster.worker.targets = ','.join( - 'grpc://{}'.format(host) for host in worker_hosts) - - cluster.ps.name = FLAGS.ps_job - cluster.ps.replicas = FLAGS.ps_replicas - cluster.ps.gpus_per_replica = FLAGS.ps_gpus - - cluster.input.name = FLAGS.input_job - cluster.input.replicas = FLAGS.input_replicas - cluster.input.targets = FLAGS.input_targets - - cluster.evaler.name = FLAGS.evaler_job - cluster.evaler.replicas = FLAGS.evaler_replicas - cluster.evaler.gpus_per_replica = FLAGS.evaler_gpus - - # There we go! - cluster.decoder.name = FLAGS.decoder_job - # When is this set? - cluster.decoder.replicas = FLAGS.decoder_replicas - cluster.decoder.gpus_per_replica = FLAGS.decoder_gpus - # I hope this wowkrs - cluster.decoder.tpus_per_replica = FLAGS.decoder_tpus - - # Consider adding: - cluster.decoder.num_tpu_hosts = FLAGS.worker_num_tpu_hosts - - cluster.add_summary = FLAGS.add_summary - - def _CreateRunner(self, job, model_task_name, logdir, tf_master, trial): - """Create a runner.""" - evaler_job_name_prefix = 'evaler_' - decoder_job_name_prefix = 'decoder_' - tpu_decoder_job_name_prefix = 'tpu_decoder_' - - tf.logging.info('Job %s start', job) - common_args = (model_task_name, logdir, tf_master, trial) - if job == 'controller': - cfg = self.GetParamsForDataset('controller', 'Train') - return self.Controller(cfg, *common_args) - elif job == 'trainer': - cfg = self.GetParamsForDataset('trainer', 'Train') - return self.Trainer(cfg, *common_args) - elif job == 'trainer_client': - cfg = self.GetParamsForDataset('trainer_client', 'Train') - if py_utils.use_tpu(): - return self.TrainerTpu(cfg, *common_args) - else: - return self.Trainer(cfg, *common_args) - elif job.startswith(evaler_job_name_prefix): - dataset_name = job[len(evaler_job_name_prefix):] - cfg = self.GetParamsForDataset('evaler', dataset_name) - return self.Evaler(dataset_name.lower(), cfg, *common_args) - elif job.startswith(decoder_job_name_prefix): - # so decoder_Inference means that dataset is "Inference" - # Currently here - dataset_name = job[len(decoder_job_name_prefix):] - cfg = self.GetParamsForDataset('decoder', dataset_name) - return self.Decoder(dataset_name.lower(), cfg, *common_args) - elif job.startswith(tpu_decoder_job_name_prefix): - dataset_name = job[len(tpu_decoder_job_name_prefix):] - cfg = self.GetParamsForDataset('tpu_decoder', dataset_name) - return self.TpuDecoder(dataset_name.lower(), cfg, *common_args) - elif job in ('ps', 'worker', 'input'): - self._tf_server.join() - elif job == 'executor_tpu': - # Need to base TpuDecoder on ExecutorTpu - ps_cfg_dict, train_cfg = self.GetExecutorParams() - return self.ExecutorTpu(train_cfg, ps_cfg_dict, model_task_name, logdir, - tf_master) - else: - raise ValueError('job %s is not supported' % job) - - def CreateRunners(self, jobs, logdir, trial=base_trial.NoOpTrial()): - """Creates a list of runners based on `FLAGS.mode`. - - Args: - jobs: a list of runner jobs. - logdir: the directory used for logging, usually on CNS. - trial: optional `Trial` object, used for reporting measures and early - stopping. - - Returns: - A list of `.BaseRunner`, one per job in `jobs`. - """ - - runners = [] - for j in jobs: - tf_master = FLAGS.tf_master - # Ensure that decoder or evaler threads do not clobber variables being - # updated by trainer by forcing them to use independent sessions. - if ('trainer' in jobs and - (j.startswith('decoder') or j.startswith('evaler'))): - tf_master = '' - - runner = self._CreateRunner(j, FLAGS.model_task_name, logdir, tf_master, - trial) - runners.append(runner) - return runners - - def StartRunners(self, runners): - """Runs `runners` in parallel threads. - - Returns when all of them finish. - - Args: - runners: a list of `.BaseRunner`. - - Returns: - None. - """ - threads = [] - tf.logging.info('Starting runners') - for runner in runners: - runner_class_name = str(runner) - t = threading.Thread(target=runner.Start, name=runner_class_name) - t.daemon = True - t.start() - threads.append(t) - if runner.enqueue_ops: - tf.logging.info('Total num runner.enqueue_ops: %d', - len(runner.enqueue_ops)) - for i, enqueue_op in enumerate(runner.enqueue_ops): - - def StartEnqueue(runner, op): - tf.logging.info('Starting enqueue op %s', op.name) - return lambda: runner.StartEnqueueOp(op) - - enqueue_name = '%s-enqueue-%d' % (runner_class_name, i) - tq = threading.Thread( - target=StartEnqueue(runner, enqueue_op), name=enqueue_name) - tq.start() - threads.append(tq) - tf.logging.info('Waiting for runners to finish...') - for t in threads: - tf.logging.info('Waiting for thread to finish: %s' % t.name) - while True: - t.join(1) - if not t.isAlive(): - break - tf.logging.info('All runners done.') - - def RunTrial(self, job, logdir, trial): - """A wrapper function for running a trial.""" - # Run each job in separate process/task - # TODO(rpang): add support for running evaler_test and decoder. - self.StartRunners(self.CreateRunners([job], logdir, trial)) - - def MaybeConfigRunLocally(self): - """Update flags if configured to run locally.""" - if not FLAGS.run_locally: - # Do nothing - return - - FLAGS.tf_master = tf.distribute.Server.create_local_server().target - - if not FLAGS.mode: - FLAGS.mode = 'sync' - - if not FLAGS.job: - if FLAGS.run_locally == 'tpu': - FLAGS.job = 'trainer_client' - elif FLAGS.mode == 'async': - FLAGS.job = 'controller,trainer' - else: - FLAGS.job = 'controller,trainer_client' - - FLAGS.task = 0 - local_job = '/job:localhost' - FLAGS.controller_job = local_job - - FLAGS.worker_job = local_job - FLAGS.worker_replicas = 1 - if FLAGS.run_locally == 'gpu': - if not FLAGS.worker_gpus: - FLAGS.worker_gpus = 1 - else: - FLAGS.worker_gpus = 0 - if FLAGS.run_locally == 'tpu': - FLAGS.xla_device = 'tpu' - FLAGS.enable_asserts = False - else: - FLAGS.worker_tpus = 0 - - if not FLAGS.worker_split_size: - FLAGS.worker_split_size = 1 - - FLAGS.ps_job = local_job - FLAGS.ps_replicas = 1 - FLAGS.ps_gpus = 0 - - FLAGS.input_job = local_job - FLAGS.input_replicas = 0 - - FLAGS.evaler_job = local_job - FLAGS.evaler_replicas = 1 - if FLAGS.run_locally == 'gpu': - FLAGS.evaler_gpus = 1 - else: - FLAGS.evaler_gpus = 0 - - FLAGS.decoder_job = local_job - FLAGS.decoder_replicas = 1 - if FLAGS.run_locally == 'gpu': - FLAGS.decoder_gpus = 1 - else: - FLAGS.decoder_gpus = 0 - - def InspectModel(self): - """Prints out model analysis for the model.""" - FLAGS.mode = 'sync' - p = self.GetParamsForDataset('controller', 'Train') - c = cluster_factory.Cluster(p.cluster) - with tf.Graph().as_default(), c, tf.device(c.GetPlacer()): - analysis, _ = summary_utils.ModelAnalysis(p.Instantiate()) - print(analysis) - - def InspectDatasets(self): - """Prints out datasets configured for the model.""" - cls = self.model_registry.GetClass(self._model_name) - print(','.join([dataset.lower() for dataset in datasets.GetDatasets(cls)])) - - def InspectDecoder(self): - """Prints out datasets configured for the decoder.""" - cls = self.model_registry.GetClass(self._model_name) - params = cls() - - has_decoder = False - if issubclass(cls, base_model_params.SingleTaskModelParams): - has_decoder = params.Task( - ).cls.CreateDecoderMetrics != base_model.BaseTask.CreateDecoderMetrics - else: - for _, task_param in params.Model().task_params.IterParams(): - has_decoder |= ( - task_param.cls.CreateDecoderMetrics != - base_model.BaseTask.CreateDecoderMetrics) - if has_decoder: - # We assume that the proper decoder is implemented. - self.InspectDatasets() - else: - print('') - - def SetModelName(self, model_name): - """Sets the model name.""" - self._model_name = model_name - - def WriteInferenceGraph(self): - """Generates the inference graphs for a given model.""" - inference_graph_dir = os.path.join(FLAGS.logdir, 'inference_graphs') - tf.io.gfile.makedirs(inference_graph_dir) - tf.logging.info('Writing inference graphs to dir: %s', inference_graph_dir) - - cfg = self.model_registry.GetParams(self._model_name, 'Test') - task_names = [FLAGS.model_task_name] - if (issubclass(cfg.cls, base_model.MultiTaskModel) and - not FLAGS.model_task_name): - task_names = base_model.MultiTaskModel.TaskNames(cfg) - - if FLAGS.inference_graph_filename: - # Custom inference graph. - for task_name in task_names: - filename_prefix = FLAGS.inference_graph_filename - if task_name: - filename_prefix = '%s_inference' % task_name - filename_prefix = os.path.join(inference_graph_dir, filename_prefix) - - device = '' - var_options = None - if FLAGS.inference_graph_device == 'tpu': - device = 'tpu' - var_options = 'ON_DEVICE' - device_options = inference_graph_exporter.InferenceDeviceOptions( - device=device, - retain_device_placement=False, - var_options=var_options, - gen_init_op=True, - dtype_override=None) - self.inference_graph_exporter.InferenceGraphExporter.Export( - model_cfg=cfg, - model_task_name=task_name, - device_options=device_options, - export_path=filename_prefix + '.pbtxt', - random_seed=FLAGS.inference_graph_random_seed) - else: - for task_name in task_names: - filename_prefix = 'inference' - if task_name: - filename_prefix = '%s_inference' % task_name - filename_prefix = os.path.join(inference_graph_dir, filename_prefix) - - # Standard inference graph. - try: - self.inference_graph_exporter.InferenceGraphExporter.Export( - model_cfg=cfg, - model_task_name=task_name, - export_path=filename_prefix + '.pbtxt', - random_seed=FLAGS.inference_graph_random_seed) - except NotImplementedError as e: - tf.logging.error('Cannot write inference graph: %s', e) - - # TPU inference graph. Not all models support it so fail silently. - try: - device_options = self.inference_graph_exporter.InferenceDeviceOptions( - device='tpu', - retain_device_placement=False, - var_options='ON_DEVICE', - gen_init_op=True, - dtype_override=None) - self.inference_graph_exporter.InferenceGraphExporter.Export( - model_cfg=cfg, - model_task_name=task_name, - device_options=device_options, - export_path=filename_prefix + '_tpu.pbtxt', - random_seed=FLAGS.inference_graph_random_seed) - except Exception as e: # pylint: disable=broad-except - tf.logging.error('Error exporting TPU inference graph: %s' % e) - - def RunEvalerOnce(self): - """Run once evaler.""" - m = re.match(r'evaler_once_([^_@]+)@(\d+)', FLAGS.job) - dataset_name, ckpt_id = m.group(1), int(m.group(2)) - cfg = self.GetParamsForDataset('evaler', dataset_name) - evaler = self.Evaler(dataset_name.lower(), cfg, FLAGS.model_task_name, - FLAGS.logdir, FLAGS.tf_master) - evaler.EvalCheckpoint(ckpt_id) - - def Start(self): - """Start the process.""" - tf.logging.set_verbosity(tf.logging.INFO) - - if FLAGS.mode == 'inspect_model': - self.InspectModel() - return - - if FLAGS.mode == 'inspect_evaler': - self.InspectDatasets() - return - - if FLAGS.mode == 'inspect_decoder': - self.InspectDecoder() - return - - if FLAGS.mode == 'write_inference_graph': - self.WriteInferenceGraph() - return - - if FLAGS.mode == 'shell': - _StartShell(locals()) - return - - assert FLAGS.mode in ['sync', 'async'] - - self.MaybeConfigRunLocally() - self.MaybeConfigRunDistributed() - self.MaybeConfigCloudTpu() - self.MaybeLaunchTensorFlow() - - if FLAGS.job.startswith('evaler_once_'): - # E.g., trainer --model=foo.bar.Model --logdir=... - # --run_locally=cpu --mode=sync --job=evaler_once_test@65200 - self.RunEvalerOnce() - return - - self.StartRunners(self.CreateRunners(FLAGS.job.split(','), FLAGS.logdir)) - - -def main(unused_argv): - RunnerManager(FLAGS.model).Start() - - -if __name__ == '__main__': - tf.disable_eager_execution() - tf.flags.mark_flag_as_required('model') - FLAGS(sys.argv, known_only=True) - model_imports.ImportParams(FLAGS.model) - FLAGS.unparse_flags() - tf.app.run(main) diff --git a/lingvo/trainer_test.py b/lingvo/trainer_test.py deleted file mode 100644 index e9faecc0..00000000 --- a/lingvo/trainer_test.py +++ /dev/null @@ -1,454 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Tests for trainer.""" - -import os -import random -import re - -from absl.testing import flagsaver -from lingvo import base_trial -from lingvo import model_registry -from lingvo import trainer -import lingvo.compat as tf -from lingvo.core import base_input_generator -from lingvo.core import base_model -from lingvo.core import base_model_params -from lingvo.core import hyperparams -from lingvo.core import inference_graph_pb2 -from lingvo.core import test_utils -from lingvo.core import trainer_test_utils -from lingvo.tasks.image.input_generator import FakeMnistData -import numpy as np - -FLAGS = tf.flags.FLAGS - - -class BaseTrainerTest(test_utils.TestCase): - """Base class for the test cases.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._trial = base_trial.NoOpTrial() - - def setUp(self): - super().setUp() - FLAGS.model_params_override = '' - FLAGS.tf_master = tf.distribute.Server.create_local_server().target - FLAGS.vizier_reporting_job = 'decoder' - - def _CreateController(self, cfg): - return trainer.Controller(cfg, FLAGS.model_task_name, FLAGS.logdir, - FLAGS.tf_master, self._trial) - - def _CreateTrainer(self, cfg): - return trainer.Trainer(cfg, FLAGS.model_task_name, FLAGS.logdir, - FLAGS.tf_master, self._trial) - - def _CreateEvalerDev(self, cfg): - return trainer.Evaler('dev', cfg, FLAGS.model_task_name, FLAGS.logdir, - FLAGS.tf_master, self._trial) - - def _CreateDecoderDev(self, cfg): - return trainer.Decoder('dev', cfg, FLAGS.model_task_name, FLAGS.logdir, - FLAGS.tf_master, self._trial) - - def _HasFile(self, files, substr): - for f in files: - if substr in f: - return True - return False - - def _GetMatchedFileName(self, files, substr): - for f in files: - if substr in f: - return f - return None - - def _HasLine(self, filename, pattern): - """Returns True iff one line in the given file matches the pattern.""" - with tf.io.gfile.GFile(filename, 'r') as f: - lines = f.readlines() - return any(re.search(pattern, _) for _ in lines) - - -class EmptyTask(base_model.BaseTask): - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'empty' - return p - - def Inference(self): - return inference_graph_pb2.InferenceGraph() - - -class EmptyMultiTaskModel(base_model.MultiTaskModel): - - @classmethod - def Params(cls): - p = super().Params() - p.name = 'empty' - p.task_params.Define('a', EmptyTask.Params(), '') - p.task_params.Define('b', EmptyTask.Params(), '') - return p - - -@model_registry.RegisterMultiTaskModel -class EmptyMultiTaskParams(base_model_params.MultiTaskModelParams): - - def Test(self): - p = base_input_generator.BaseInputGenerator.Params() - inputs = hyperparams.Params() - for task_name in ['a', 'b']: - inputs.Define(task_name, p.Copy(), '') - return inputs - - def Model(self): - return EmptyMultiTaskModel.Params() - - -class TrainerTest(BaseTrainerTest): - - def _GetTestConfig(self): - model_name = 'image.mnist.LeNet5' - # So this is how particular parameters are obtained - cfg = model_registry.GetParams(model_name, 'Train') - cfg.cluster.task = 0 - cfg.cluster.mode = 'sync' - cfg.cluster.job = 'trainer_client' - cfg.cluster.worker.name = '/job:localhost' - cfg.cluster.worker.replicas = 1 - cfg.cluster.worker.gpus_per_replica = 0 - cfg.cluster.ps.name = '/job:localhost' - cfg.cluster.ps.replicas = 1 - cfg.cluster.ps.gpus_per_replica = 0 - - # Generate 2 inputs. - cfg.input.ckpt = FakeMnistData( - self.get_temp_dir(), train_size=2, test_size=2) - cfg.input.num_samples = 2 - cfg.input.batch_size = 2 - cfg.train.max_steps = 2 - cfg.train.ema_decay = 0.9999 - return cfg - - @flagsaver.flagsaver - def testController(self): - logdir = os.path.join(tf.test.get_temp_dir(), - 'controller_test' + str(random.random())) - FLAGS.logdir = logdir - cfg = self._GetTestConfig() - - runner_manager = trainer.RunnerManager(cfg.name) - - runner_manager.StartRunners( - [self._CreateController(cfg), - self._CreateTrainer(cfg)]) - - train_files = tf.io.gfile.glob(logdir + '/train/*') - self.assertTrue(self._HasFile(train_files, 'ckpt')) - self.assertTrue(self._HasFile(train_files, 'tfevents')) - control_files = tf.io.gfile.glob(logdir + '/control/*') - self.assertTrue(self._HasFile(control_files, 'params.txt')) - self.assertTrue(self._HasFile(control_files, 'model_analysis.txt')) - self.assertTrue(self._HasFile(control_files, 'train.pbtxt')) - self.assertTrue(self._HasFile(control_files, 'tfevents')) - - # EvalerDev may not run concurrently with Controller in a single process - # because EvalerDev loads checkpoints and overwrites states like global - # steps. - runner_manager.StartRunners([self._CreateEvalerDev(cfg)]) - - dev_files = tf.io.gfile.glob(logdir + '/eval_dev/*') - self.assertTrue(self._HasFile(dev_files, 'params.txt')) - self.assertTrue(self._HasFile(dev_files, 'eval_dev.pbtxt')) - self.assertTrue(self._HasFile(dev_files, 'tfevents')) - self.assertTrue(self._HasFile(dev_files, 'score')) - self.assertTrue( - self._HasLine(self._GetMatchedFileName(dev_files, 'score'), 'log_pplx')) - - @flagsaver.flagsaver - def testDecoder(self): - logdir = os.path.join(tf.test.get_temp_dir(), - 'decoder_test' + str(random.random())) - FLAGS.logdir = logdir - cfg = self._GetTestConfig() - - runner_manager = trainer.RunnerManager(cfg.name) - - runner_manager.StartRunners( - [self._CreateController(cfg), - self._CreateTrainer(cfg)]) - runner_manager.StartRunners([self._CreateDecoderDev(cfg)]) - - dec_files = tf.io.gfile.glob(logdir + '/decoder_dev/*') - self.assertTrue(self._HasFile(dec_files, 'params.txt')) - self.assertTrue(self._HasFile(dec_files, 'decoder_dev.pbtxt')) - self.assertTrue(self._HasFile(dec_files, 'tfevents')) - # Only the score for the 2-step checkpoint should be present. - self.assertTrue( - tf.io.gfile.exists( - os.path.join(logdir, 'decoder_dev/score-00000002.txt'))) - self.assertFalse( - tf.io.gfile.exists( - os.path.join(logdir, 'decoder_dev/score-00000000.txt'))) - self.assertTrue( - self._HasLine( - self._GetMatchedFileName(dec_files, 'score'), 'examples/sec')) - - # Test customization of an eval checkpoint. Create a new logdir / decoder - # but point the eval checkpoint to the 0th checkpoint of the most - # recent experiment. - new_logdir = os.path.join(tf.test.get_temp_dir(), - 'decoder_test' + str(random.random())) - FLAGS.logdir = new_logdir - cfg = self._GetTestConfig() - cfg.task.eval.load_checkpoint_from = os.path.join(logdir, - 'train/ckpt-00000000') - - runner_manager.StartRunners([self._CreateDecoderDev(cfg)]) - # Only the score for the 0th checkpoint should be present. - self.assertTrue( - tf.io.gfile.exists( - os.path.join(new_logdir, 'decoder_dev/score-00000000.txt'))) - self.assertFalse( - tf.io.gfile.exists( - os.path.join(new_logdir, 'decoder_dev/score-00000002.txt'))) - - @flagsaver.flagsaver - def testWriteInferenceGraph(self): - random.seed() - logdir = os.path.join(tf.test.get_temp_dir(), - 'inference_graphs' + str(random.random())) - FLAGS.logdir = logdir - cfg = 'punctuator.codelab.RNMTModel' - trainer.RunnerManager(cfg).WriteInferenceGraph() - inference_files = tf.io.gfile.glob(logdir + '/inference_graphs/*') - self.assertTrue(self._HasFile(inference_files, 'inference.pbtxt')) - self.assertTrue(self._HasFile(inference_files, 'inference_tpu.pbtxt')) - - @flagsaver.flagsaver(model_task_name='a') - def testWriteOneOfMultiTaskInferenceGraph(self): - random.seed() - logdir = os.path.join(tf.test.get_temp_dir(), - 'inference_graphs' + str(random.random())) - FLAGS.logdir = logdir - cfg = 'test.EmptyMultiTaskParams' - trainer.RunnerManager(cfg).WriteInferenceGraph() - inference_files = tf.io.gfile.glob(logdir + '/inference_graphs/*') - self.assertTrue(self._HasFile(inference_files, 'a_inference.pbtxt')) - self.assertTrue(self._HasFile(inference_files, 'a_inference_tpu.pbtxt')) - self.assertFalse(self._HasFile(inference_files, 'b_inference.pbtxt')) - self.assertFalse(self._HasFile(inference_files, 'b_inference_tpu.pbtxt')) - - @flagsaver.flagsaver - def testWriteMultiTaskInferenceGraph(self): - random.seed() - logdir = os.path.join(tf.test.get_temp_dir(), - 'inference_graphs' + str(random.random())) - FLAGS.logdir = logdir - cfg = 'test.EmptyMultiTaskParams' - trainer.RunnerManager(cfg).WriteInferenceGraph() - inference_files = tf.io.gfile.glob(logdir + '/inference_graphs/*') - self.assertTrue(self._HasFile(inference_files, 'a_inference.pbtxt')) - self.assertTrue(self._HasFile(inference_files, 'a_inference_tpu.pbtxt')) - self.assertTrue(self._HasFile(inference_files, 'b_inference.pbtxt')) - self.assertTrue(self._HasFile(inference_files, 'b_inference_tpu.pbtxt')) - - def testRunLocally(self): - logdir = os.path.join(tf.test.get_temp_dir(), - 'run_locally_test' + str(random.random())) - FLAGS.logdir = logdir - FLAGS.run_locally = 'cpu' - FLAGS.mode = 'sync' - FLAGS.model = 'image.mnist.LeNet5' - FLAGS.model_params_override = ( - 'train.max_steps: 2; input.num_samples: 2; input.ckpt: %s' % - FakeMnistData(self.get_temp_dir(), train_size=2, test_size=2)) - trainer.main(None) - - train_files = tf.io.gfile.glob(logdir + '/train/*') - self.assertTrue(self._HasFile(train_files, 'ckpt')) - self.assertTrue(self._HasFile(train_files, 'tfevents')) - control_files = tf.io.gfile.glob(logdir + '/control/*') - self.assertTrue(self._HasFile(control_files, 'params.txt')) - self.assertTrue(self._HasFile(control_files, 'model_analysis.txt')) - self.assertTrue(self._HasFile(control_files, 'train.pbtxt')) - self.assertTrue(self._HasFile(control_files, 'tfevents')) - - -class TrainerWithTrialTest(TrainerTest): - - @flagsaver.flagsaver - def testControllerTrainerEvaler(self): - trial = tf.test.mock.create_autospec(base_trial.Trial, instance=True) - self._trial = trial - - logdir = os.path.join(tf.test.get_temp_dir(), - 'controller_test' + str(random.random())) - FLAGS.logdir = logdir - cfg = self._GetTestConfig() - - trial.Name.return_value = 'trial1' - - def override_model_params(model_params): - model_params.task.softmax.num_classes = 20 - model_params.task.filter_shapes = [(5, 5, 1, 10), (5, 5, 10, 50)] - model_params.task.train.lr_schedule.decay_start = 100 - return model_params - - trial.OverrideModelParams.side_effect = override_model_params - trial.ShouldStop.return_value = False - trial.ShouldStopAndMaybeReport.return_value = False - # Stop trial once ReportEvalMeasure is called. - trial.ReportEvalMeasure.return_value = True - - runners = [self._CreateController(cfg), self._CreateTrainer(cfg)] - # Param override works. - for runner in runners: - self.assertEqual(runner.params.task.softmax.num_classes, 20) - self.assertEqual(runner.params.task.filter_shapes, [(5, 5, 1, 10), - (5, 5, 10, 50)]) - self.assertEqual(runner.params.task.train.lr_schedule.decay_start, 100) - - runner_manager = trainer.RunnerManager(cfg.name) - runner_manager.StartRunners(runners) - # Controller and trainer check whether the trial is stopped. - self.assertGreater(trial.OverrideModelParams.call_count, 0) - self.assertGreater(trial.ShouldStop.call_count, 0) - self.assertGreater(trial.ShouldStopAndMaybeReport.call_count, 0) - # Controller and trainer do not call report_measure, request_trial_stop, or - # report_done. - self.assertEqual(trial.ReportEvalMeasure.call_count, 0) - - train_files = tf.io.gfile.glob(logdir + '/train/*') - self.assertTrue(self._HasFile(train_files, 'params.txt')) - self.assertTrue(self._HasFile(train_files, 'trainer_params.txt')) - self.assertTrue(self._HasFile(train_files, 'ckpt')) - self.assertTrue(self._HasFile(train_files, 'tfevents')) - control_files = tf.io.gfile.glob(logdir + '/control/*') - self.assertTrue(self._HasFile(control_files, 'params.txt')) - self.assertTrue(self._HasFile(control_files, 'model_analysis.txt')) - self.assertTrue(self._HasFile(control_files, 'train.pbtxt')) - self.assertTrue(self._HasFile(control_files, 'tfevents')) - - # EvalerDev may not run concurrently with Controller in a single process - # because EvalerDev loads checkpoints and overwrites states like global - # steps. - self._CreateEvalerDev(cfg).EvalLatestCheckpoint() - # EvalerDev calls report_measure, request_trial_stop, and report_done. - after_eval_count = trial.ReportEvalMeasure.call_count - self.assertEqual(after_eval_count, 0) - - self._CreateDecoderDev(cfg).DecodeLatestCheckpoint() - after_decoder_count = trial.ReportEvalMeasure.call_count - self.assertGreater(after_decoder_count, 0) - - dev_files = tf.io.gfile.glob(logdir + '/eval_dev/*') - self.assertTrue(self._HasFile(dev_files, 'params.txt')) - self.assertTrue(self._HasFile(dev_files, 'eval_dev.pbtxt')) - self.assertTrue(self._HasFile(dev_files, 'tfevents')) - self.assertTrue(self._HasFile(dev_files, 'score')) - self.assertTrue( - self._HasLine(self._GetMatchedFileName(dev_files, 'score'), 'log_pplx')) - - -class ProcessFPropResultsTest(BaseTrainerTest): - - @flagsaver.flagsaver - def testIdentityRegressionModel(self): - logdir = os.path.join(tf.test.get_temp_dir(), - 'identity_regression_test' + str(random.random())) - FLAGS.logdir = logdir - - steps = 100 - cfg = trainer_test_utils.IdentityRegressionModel.Params() - cfg.cluster.task = 0 - cfg.cluster.mode = 'sync' - cfg.cluster.job = 'trainer_client' - cfg.cluster.worker.name = '/job:localhost' - cfg.cluster.worker.replicas = 1 - cfg.cluster.worker.gpus_per_replica = 0 - cfg.cluster.ps.name = '/job:localhost' - cfg.cluster.ps.replicas = 1 - cfg.cluster.ps.gpus_per_replica = 0 - cfg.train.max_steps = steps - cfg.task.train.learning_rate = 0.025 - - runners = [self._CreateController(cfg), self._CreateTrainer(cfg)] - - runner_manager = trainer.RunnerManager(cfg.name) - runner_manager.StartRunners(runners) - train = runners[1] - - # ProcessFPropResults should have been called times on the task - # and times on the model. - - # There are always 2 samples in the batch. - expected_samples_in_batch = [(2, 1.0) for _ in range(steps)] - - self.assertAllEqual( - expected_samples_in_batch, - [m['num_samples_in_batch'] for m in train._model.metrics]) - self.assertAllEqual( - expected_samples_in_batch, - [m['num_samples_in_batch'] for m in train._model._task.metrics]) - - # Global steps should increment by 1 for each batch. - expected_global_steps = [i + 1 for i in range(steps)] - self.assertAllEqual(expected_global_steps, train._model.global_steps) - self.assertAllEqual(expected_global_steps, train._model._task.global_steps) - - # The CountingInputGenerator makes [2,2] inputs that increment for each - # batch, like: - # [[0, 1], [2, 3]], - # [[4, 5], [6, 7]], - # ... - expected_input_tensors = [{ - 'input': np.array([[4 * i, 4 * i + 1], [4 * i + 2, 4 * i + 3]]) - } for i in range(steps)] - - def keep_input_tensors(tensors): - return [{'input': d['input']} for d in tensors] - - self.assertAllClose( - expected_input_tensors, - keep_input_tensors(train._model.result_per_example_tensors)) - self.assertAllClose( - expected_input_tensors, - keep_input_tensors(train._model._task.result_per_example_tensors)) - - # This model is training parameters m and b such that: - # m * (input[0] + input[1]) + b = (input[0] + input[1]) - # So we expect m = 1 and b = 0 after training. - - # m is more stable so that's the one we test with a tight tolerance. - self.assertNear( - 1.0, train._model._task.result_per_example_tensors[-1]['m'][0], 0.1) - self.assertNear(1.0, train._model.result_per_example_tensors[-1]['m'][0], - 0.1) - - # b isn't so stable but shouldn't be too crazy in size. - self.assertNear( - 0.0, train._model._task.result_per_example_tensors[-1]['b'][0], 10.0) - self.assertNear(0.0, train._model.result_per_example_tensors[-1]['b'][0], - 10.0) - - -if __name__ == '__main__': - tf.test.main() diff --git a/missing_files.json b/missing_files.json deleted file mode 100644 index ba9de8aa..00000000 --- a/missing_files.json +++ /dev/null @@ -1 +0,0 @@ -["gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs06RES2154_090401/hrs06RES2154_090401.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs04W_M1100_050303/hrs04W_M1100_050303.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs05APR2359_090424bm2p/hrs05APR2359_090424bm2p.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs05RES1324_100513/hrs05RES1324_100513.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/Musical_Journeys/Musical_Journeys.HD.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs04JUD2141_071213c/hrs04JUD2141_071213c.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs04EI_GW2128_080123/hrs04EI_GW2128_080123.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/Gvtvnews-GVTVNEWS102609NCTV11225/Gvtvnews-GVTVNEWS102609NCTV11225.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs04JUD2141_110210b/hrs04JUD2141_110210b.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs01IR2172_101202/hrs01IR2172_101202.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs05RES1324_100513/hrs05RES1324_100513.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs01Ed_W2175_101207/hrs01Ed_W2175_101207.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs04JUD2141_110210b/hrs04JUD2141_110210b.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs05APR2359_090424bm2p/hrs05APR2359_090424bm2p.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs04JUD2141_071213c/hrs04JUD2141_071213c.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/Gvtvnews-GVTVNEWS102609NCTV11225-3/Gvtvnews-GVTVNEWS102609NCTV11533.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs05SCI2318_090331b/hrs05SCI2318_090331b.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/Holland_Farmers_Market/Holland_Farmers_Market.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/Gvtvnews-GVTVNEWS030409NCTV11973-3/Gvtvnews-GVTVNEWS030409NCTV11973.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs05H_A1310_100511/hrs05H_A1310_100511.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs06E_C2123_070322b/hrs06E_C2123_070322b.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs06E_C2123_070322b/hrs06E_C2123_070322b.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs04TRAN2167_101202/hrs04TRAN2167_101202.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs01IR2172_101202/hrs01IR2172_101202.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs04TRAN2167_101202/hrs04TRAN2167_101202.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/gov.house.hbs.hrs01Ed_W2175_101207/hrs01Ed_W2175_101207.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/cohmiv-Centennial_Park/Centennial_Park.asr.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs04EI_GW2128_080123/hrs04EI_GW2128_080123.auto.srt", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS/house.hbs.mars.hrs04W_M1100_050303/hrs04W_M1100_050303.auto.srt"] \ No newline at end of file diff --git a/pip_package/MANIFEST.in b/pip_package/MANIFEST.in deleted file mode 100644 index b5a1bfdd..00000000 --- a/pip_package/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -recursive-include lingvo/ *.so diff --git a/pip_package/README.md b/pip_package/README.md deleted file mode 100644 index 076b6ae7..00000000 --- a/pip_package/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# Guide to releasing a new lingvo pip package - -Update the version number in setup.py and commit it. - -Build the docker image for building the pip package - -``` -docker build --tag tensorflow:lingvo_pip - < lingvo/pip_package/build.Dockerfile -``` - -Now, we assume that the repo is stored at /tmp/lingvo for the rest of these -instructions (either through git clone or copy_to_local). - -Enter the docker image, mapping /tmp/lingvo to /tmp/lingvo: - -``` -docker run --rm -it -v /tmp/lingvo:/tmp/lingvo tensorflow:lingvo_pip bash -``` - -From the /tmp/lingvo directory, run - -``` -rm -rf /tmp/lingvo_pip_package_build -PYTHON_MINOR_VERSION=6 pip_package/build.sh -PYTHON_MINOR_VERSION=7 pip_package/build.sh -PYTHON_MINOR_VERSION=8 pip_package/build.sh -``` - -If everything goes well, this will produce a set of wheels in -/tmp/lingvo_pip_package_build. - -``` -cd /tmp/lingvo_pip_pkg_build -``` - -To upload to the test pypi server: - -``` -python3 -m twine upload --repository-url https://test.pypi.org/legacy/ *manylinux2010*.whl -``` - -To verify that it works as intended: - -``` -python3 -m pip install -i https://test.pypi.org/simple/ --no-deps lingvo -``` - -You can test that the install worked for the common case by running a model -locally like: - -``` -mkdir -p /tmp/lingvo_test/image -cp -r /tmp/lingvo/lingvo/tasks/image/params/*.py /tmp/lingvo_test/image -cd /tmp/lingvo_test -python3 -m lingvo.trainer --model=image.mnist.LeNet5 --run_locally=cpu --logdir=/tmp/lenet5 --mode=sync -``` - -This should try to start training, but will fail if you haven't downloaded the -mnist dataset (see lingvo's base README.md). - -If this works successfully, you can then upload to the production server as -follows. - -``` -python3 -m twine upload *manylinux2010*.whl -``` - -And verify with: - -``` -python3 -m pip install lingvo -``` - -Remember to update the list of releases in the main README. diff --git a/pip_package/build.Dockerfile b/pip_package/build.Dockerfile deleted file mode 100644 index cd4d24ae..00000000 --- a/pip_package/build.Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM tensorflow/tensorflow:custom-op-ubuntu16 - -ENV GITHUB_BRANCH="master" -ENV PYTHON_VERSION="3" -ENV PYTHON_MINOR_VERSION="8" -ENV PIP_MANYLINUX2010="1" - -# There are some problems with the python3 installation from custom-op-ubuntu16. -# Remove it and install new ones. -RUN apt-get remove --purge -y python3.5 python3.6 -RUN rm -f /etc/apt/sources.list.d/jonathonf-ubuntu-python-3_6-xenial.list -RUN apt-key del F06FC659 - -RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA6932366A755776 -RUN echo "deb http://ppa.launchpad.net/deadsnakes/ppa/ubuntu xenial main" > /etc/apt/sources.list.d/deadsnakes-ppa-xenial.list -RUN apt-get update && apt-get install -y python3.6 python3.7 python3.8 python3.8-distutils -RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py - -# Download and install bazel. -RUN wget https://github.com/bazelbuild/bazel/releases/download/0.29.0/bazel-0.29.0-installer-linux-x86_64.sh > /dev/null -RUN bash bazel-0.29.0-installer-linux-x86_64.sh - -RUN for python in python3.6 python3.7 python3.8; do \ - $python get-pip.py && \ - $python -m pip install --upgrade pip setuptools auditwheel && \ - $python -m pip install --upgrade \ - dataclasses \ - grpcio>=1.24.3 \ - matplotlib \ - mock \ - model-pruning-google-research \ - numpy>=1.16.0 \ - sympy \ - twine && \ - $python -m pip install tensorflow-gpu \ - ; \ - done - -WORKDIR "/tmp/lingvo" diff --git a/pip_package/build.sh b/pip_package/build.sh deleted file mode 100755 index 611b76c8..00000000 --- a/pip_package/build.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash -# Copyright 2019 The TensorFlow 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. -# ============================================================================== - -# This script uses custom-op docker, downloads code, builds and tests and then -# builds a pip package. - -# See README.md for instructions to use this script. - -set -e -x - -# Override the following env variables if necessary. -export PYTHON_VERSION="${PYTHON_VERSION:-3}" -export PYTHON_MINOR_VERSION="${PYTHON_MINOR_VERSION}" -export PIP_MANYLINUX2010="${PIP_MANYLINUX2010:-1}" - -if [[ -z "${PYTHON_MINOR_VERSION}" ]]; then - PYTHON="python${PYTHON_VERSION}" -else - PYTHON="python${PYTHON_VERSION}.${PYTHON_MINOR_VERSION}" -fi - -function write_to_bazelrc() { - echo "$1" >> .bazelrc -} - -function write_action_env_to_bazelrc() { - write_to_bazelrc "build --action_env $1=\"$2\"" -} - -# Remove .bazelrc if it already exist -[ -e .bazelrc ] && rm .bazelrc - -write_to_bazelrc "build -c opt" -write_to_bazelrc 'build --cxxopt="-std=c++11"' -write_to_bazelrc 'build --cxxopt="-D_GLIBCXX_USE_CXX11_ABI=0"' -write_to_bazelrc 'build --auto_output_filter=subpackages' -write_to_bazelrc 'build --copt="-Wall" --copt="-Wno-sign-compare"' -write_to_bazelrc 'build --linkopt="-lrt -lm"' -write_to_bazelrc 'build --incompatible_bzl_disallow_load_after_statement=false' -write_to_bazelrc 'query --incompatible_bzl_disallow_load_after_statement=false' - -TF_NEED_CUDA=0 -echo 'Using installed tensorflow' -TF_CFLAGS=( $(${PYTHON} -c 'import tensorflow as tf; print(" ".join(tf.sysconfig.get_compile_flags()))') ) -TF_LFLAGS="$(${PYTHON} -c 'import tensorflow as tf; print(" ".join(tf.sysconfig.get_link_flags()))')" - -write_action_env_to_bazelrc "TF_HEADER_DIR" ${TF_CFLAGS:2} -SHARED_LIBRARY_DIR=${TF_LFLAGS:2} -SHARED_LIBRARY_NAME=$(echo $TF_LFLAGS | rev | cut -d":" -f1 | rev) -if ! [[ $TF_LFLAGS =~ .*:.* ]]; then - if [[ "$(uname)" == "Darwin" ]]; then - SHARED_LIBRARY_NAME="libtensorflow_framework.dylib" - else - SHARED_LIBRARY_NAME="libtensorflow_framework.so" - fi -fi -write_action_env_to_bazelrc "TF_SHARED_LIBRARY_DIR" ${SHARED_LIBRARY_DIR} -write_action_env_to_bazelrc "TF_SHARED_LIBRARY_NAME" ${SHARED_LIBRARY_NAME} -write_action_env_to_bazelrc "TF_NEED_CUDA" ${TF_NEED_CUDA} -write_to_bazelrc "build:manylinux2010 --crosstool_top=//third_party/toolchains/preconfig/ubuntu16.04/gcc7_manylinux2010-nvcc-cuda10.0:toolchain" - -if [[ "$PIP_MANYLINUX2010" == "1" ]]; then - write_to_bazelrc "build --config=manylinux2010" - write_to_bazelrc "test --config=manylinux2010" -fi - -# It is expected that you have git cloned this repo at the branch you want, -# ideally in our docker. - -bazel clean -bazel build ... -# Just test the core for the purposes of the pip package. -bazel test lingvo/core/... - -DST_DIR="/tmp/lingvo_pip_pkg_build" -./pip_package/build_pip_pkg.sh "$DST_DIR" ${PYTHON_VERSION} -# Comment the following line if you run this outside of the container. -if [[ "${PIP_MANYLINUX2010}" == "1" ]]; then - find "$DST_DIR" -name *.whl | xargs -n1 ./third_party/auditwheel.sh repair --plat manylinux2010_x86_64 -w "$DST_DIR" -fi - -rm .bazelrc diff --git a/pip_package/build_pip_pkg.sh b/pip_package/build_pip_pkg.sh deleted file mode 100755 index 1f8f02fa..00000000 --- a/pip_package/build_pip_pkg.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2019 The TensorFlow 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. -# ============================================================================== - -set -e - -PLATFORM="$(uname -s | tr 'A-Z' 'a-z')" - -PIP_FILE_PREFIX="pip_package/" - -export PYTHON_VERSION="${PYTHON_VERSION:-3}" -export PYTHON_MINOR_VERSION="${PYTHON_MINOR_VERSION}" - -if [[ -z "${PYTHON_MINOR_VERSION}" ]]; then - PYTHON="python${PYTHON_VERSION}" -else - PYTHON="python${PYTHON_VERSION}.${PYTHON_MINOR_VERSION}" -fi - -function main() { - DEST=${1} - if [[ -z "${DEST}" ]]; then - echo "No destination directory provided." - exit 1 - fi - - # Create the directory, then do dirname on a non-existent file inside it to - # give us an absolute paths with tilde characters resolved to the destination - # directory. - mkdir -p "${DEST}" - DEST=$(readlink -f "${DEST}") - echo "=== destination directory: ${DEST}" - - TMPDIR=$(mktemp -d -t tmp.XXXXXXXXXX) - - echo $(date) : "=== Using tmpdir: ${TMPDIR}" - - echo "=== Copy Lingvo files" - - cp ${PIP_FILE_PREFIX}setup.py "${TMPDIR}" - cp ${PIP_FILE_PREFIX}MANIFEST.in "${TMPDIR}" - cp LICENSE "${TMPDIR}" - rsync -avm -L --exclude="*_test.py" lingvo "${TMPDIR}" - rsync -avm -L --include="*.so" --include="*_pb2.py" \ - --exclude="*.runfiles" --exclude="*_obj" --include="*/" --exclude="*" \ - bazel-bin/lingvo "${TMPDIR}" - - pushd ${TMPDIR} - echo $(date) : "=== Building wheel" - - ${PYTHON} setup.py bdist_wheel --project_name lingvo > /dev/null - cp dist/*.whl "${DEST}" - popd - rm -rf ${TMPDIR} - echo $(date) : "=== Output wheel file is in: ${DEST}" -} - -main "$@" diff --git a/pip_package/setup.py b/pip_package/setup.py deleted file mode 100644 index 96dab5d6..00000000 --- a/pip_package/setup.py +++ /dev/null @@ -1,102 +0,0 @@ -# Lint as: python3 -# Copyright 2018 The TensorFlow 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. -# ============================================================================== -"""Setup script for pip package.""" -import sys - -from setuptools import find_packages -from setuptools import setup -from setuptools.command.install import install -from setuptools.dist import Distribution -import tensorflow as tf # pylint: disable=g-explicit-tensorflow-version-import - -__version__ = '0.7.2' -project_name = 'lingvo' -if '--project_name' in sys.argv: - project_name_idx = sys.argv.index('--project_name') - project_name = sys.argv[project_name_idx + 1] - sys.argv.remove('--project_name') - sys.argv.pop(project_name_idx) - -REQUIRED_PACKAGES = [ - 'dataclasses', - 'ipykernel', - 'jupyter', - 'jupyter_http_over_ws', - 'matplotlib', - 'model-pruning-google-research', - 'Pillow', - 'protobuf>=3.8,<4', - 'sklearn', - 'sympy', - 'tensorflow-gpu==' + tf.__version__, - 'waymo-open-dataset-tf-2-2-0', -] - - -class BinaryDistribution(Distribution): - """This class is needed in order to create OS specific wheels.""" - - def has_ext_modules(self): - return True - - -class InstallCommand(install): - """Override install command. - - Following: - https://github.com/bigartm/bigartm/issues/840. - """ - - def finalize_options(self): - install.finalize_options(self) - if self.distribution.has_ext_modules(): - self.install_lib = self.install_platlib - - -setup( - name=project_name, - version=__version__, - description=('Lingvo libraries.'), - author='Lingvo Authors', - author_email='lingvo-bot@google.com', - packages=find_packages(include=['lingvo*'], exclude=[]), - include_package_data=True, - python_requires='>=3.6,<=3.8', - install_requires=REQUIRED_PACKAGES, - zip_safe=False, - cmdclass={ - 'install': InstallCommand, - }, - distclass=BinaryDistribution, - # PyPI package information. - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: Education', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Topic :: Scientific/Engineering', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', - 'Topic :: Software Development', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Python Modules', - ], - license='Apache 2.0', - keywords='Machine learning framework', -) diff --git a/publications.bib b/publications.bib deleted file mode 100644 index f74fde2f..00000000 --- a/publications.bib +++ /dev/null @@ -1,706 +0,0 @@ -% List of all Lingvo publications, grouped by topic, and listed in -% chronological order with the newest papers on the bottom of each group. - -% Common conferences. -@string{acl = {Proc. Annual Meeting of the Association for Computational Linguistics ({ACL})}} -@string{emnlp = {Proc. Conference on Empirical Methods in Natural Language Processing ({EMNLP})}} -@string{icassp = {Proc. {IEEE} International Conference on Acoustics, Speech, and Signal Processing ({ICASSP})}} -@string{iclr = {Proc. International Conference on Learning Representations ({ICLR})}} -@string{icml = {Proc. International Conference on Machine Learning ({ICML})}} -@string{interspeech = {Proc. Interspeech}} -@string{naacl = {Proc. Conference of the North American Chapter of the Association for Computational Linguistics ({NAACL})}} -@string{nips = {Advances in Neural Information Processing Systems}} -@string{slt = {Proc. {IEEE} Spoken Language Technology Workshop ({SLT})}} - -% Topic annotations, used by update_publications.sh to update the markdown index. -@string{nmt = {Translation}} -@string{tts = {Speech synthesis}} -@string{asr = {Speech recognition}} -@string{st = {Speech translation}} -@string{enhancement = {Speech enhancement}} -@string{nlu = {Language Understanding}} -@string{opt = {Optimization}} - -%%%%%%%%%%%%%%%%%%%% -% Translation papers. - -@techreport{wu2016google, - annote = nmt, - title = {Google's Neural Machine Translation System: Bridging the Gap between Human and Machine Translation}, - author = {Y. Wu and M. Schuster and Z. Chen and Q. V. Le and M. Norouzi and W. Macherey and M. Krikun and Y. Cao and Q. Gao and K. Macherey and J. Klingner and A. Shah and M. Johnson and X. Liu and \L. Kaiser and S. Gouws and Y. Kato and T. Kudo and H. Kazawa and K. Stevens and G. Kurian and N. Patil and W. Wang and C. Young and J. Smith and J. Riesa and A. Rudnick and O. Vinyals and G. Corrado and M. Hughes and J. Dean}, - year = {2016}, - pdf = {https://arxiv.org/abs/1609.08144}, -} - -@article{johnson-etal-2017-googles, - annote = nmt, - title = "{G}oogle{'}s Multilingual Neural Machine Translation System: Enabling Zero-Shot Translation", - author = "Johnson, Melvin and - Schuster, Mike and - Le, Quoc V. and - Krikun, Maxim and - Wu, Yonghui and - Chen, Zhifeng and - Thorat, Nikhil and - Vi{\'e}gas, Fernanda and - Wattenberg, Martin and - Corrado, Greg and - Hughes, Macduff and - Dean, Jeffrey", - journal = "Transactions of the Association for Computational Linguistics", - volume = "5", - year = "2017", - pdf = "https://www.aclweb.org/anthology/Q17-1024", - doi = "10.1162/tacl_a_00065", - pages = "339--351", -} - -@article{eriguchi2018zero, - annote = nmt, - title={Zero-Shot Cross-lingual Classification Using Multilingual Neural Machine Translation}, - author={Eriguchi, Akiko and Johnson, Melvin and Firat, Orhan and Kazawa, Hideto and Macherey, Wolfgang}, - journal={arXiv preprint arXiv:1809.04686}, - year={2018}, - pdf={https://arxiv.org/pdf/1809.04686.pdf}, -} - -@inproceedings{bapna2018training, - annote = nmt, - author = {A. Bapna and M. X. Chen and O. Firat and Y. Cao and Y. Wu}, - title = {Training Deeper Neural Machine Translation Models with Transparent Attention}, - booktitle = emnlp, - year = {2018}, - pdf = {https://arxiv.org/abs/1808.07561}, -} - -@inproceedings{cherry2018revisiting, - annote = nmt, - author = {C. Cherry and G. Foster and A. Bapna and O. Firat and W. Macherey}, - title = {Revisiting Character-Based Neural Machine Translation with Capacity and Compression}, - booktitle = emnlp, - year = {2018}, - pdf = {https://arxiv.org/abs/1808.09943}, -} - -@inproceedings{chen2018best, - annote = nmt, - author = {M. X. Chen and O. Firat and A. Bapna and M. Johnson and W. Macherey and G. Foster and L. Jones and M. Schuster and N. Shazeer and N. Parmar and A. Vaswani and J. Uszkoreit and L. Kaiser and Z. Chen and Y. Wu and M. Hughes}, - title = {{The Best of Both Worlds: Combining Recent Advances in Neural Machine Translation}}, - booktitle = acl, - year = {2018}, - pdf = {https://arxiv.org/abs/1804.09849}, -} - -@article{kuczmarski2018gender, - annote = nmt, - title={Gender-Aware Natural Language Translation}, - author={J. Kuczmarski and M. Johnson}, - year={2018}, - pdf = {https://www.tdcommons.org/dpubs_series/1577/}, -} - -@article{aharoni2019massively, - annote = nmt, - title={Massively Multilingual Neural Machine Translation}, - author={R. Aharoni and M. Johnson and O. Firat}, - booktitle = naacl, - year={2019}, - pdf = {https://arxiv.org/pdf/1903.00089.pdf}, -} - -@article{luo2019neural, - annote = nmt, - title={Neural Decipherment via Minimum-Cost Flow: From Ugaritic to Linear B}, - author={J. Luo and Y. Cao and R. Barzilay}, - booktitle = acl, - year={2019}, - url = {https://www.aclweb.org/anthology/P19-1303/}, -} - - -@inproceedings{milk, - annote = nmt, - title={Monotonic Infinite Lookback Attention for Simultaneous Machine Translation}, - author={Naveen Arivazhagan and Colin Cherry and Wolfgang Macherey and Chung-Cheng Chiu and Semih Yavuz and Ruoming Pang and Wei Li and Colin Raffel}, - booktitle = acl, - year={2019}, - url = {https://arxiv.org/pdf/1906.05218} -} - -@article{freitag2019text, - annote = nmt, - author = {M. Freitag and I. Caswell and S. Roy}, - title = {APE at Scale and Its Implications on MT Evaluation Biases}, - booktitle = {Proceedings of the Fourth Conference on Machine Translation (Volume 1: Research Papers)}, - publisher = {Association for Computational Linguistics}, - url = {https://www.aclweb.org/anthology/W19-5204}, - year = {2019}, - pdf = {https://arxiv.org/abs/1904.04790}, -} - -@article{arivazhagan2019, - annote = nmt, - author = {N. Arivazhagan and A. Bapna and O. Firat and D. Lepikhin and M. Johnson and M. Krikun and M. X. Chen and Y. Cao and G. Foster and C. Cherry and W. Macherey and Z. Chen and Y. Wu}, - title = {Massively Multilingual Neural Machine Translation in the Wild: Findings and Challenges}, - year = {2019}, - url = {http://arxiv.org/abs/1907.05019}, - archivePrefix = {arXiv}, - eprint = {1907.05019}, -} - -@incollection{huang2019gpipe, - annote = nmt, - author = {Y. Huang and Y. Cheng and A. Bapna and O. Firat and M. X. Chen and D. Chen and H. Lee and J. Ngiam and Q. V. Le and Y. Wu and Z. Chen}, - title = {GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism}, - booktitle = nips, - year = {2019}, - url = {https://arxiv.org/abs/1811.06965} -} - - -%%%%%%%%%%%%%%%%%%%% -% Speech recognition papers. - -@inproceedings{chiu2018state, - annote = asr, - title = {State-of-the-art Speech Recognition With Sequence-to-Sequence Models}, - author = {C.-C.Chiu and T. N. Sainath and Y. Wu and R. Prabhavalkar and P. Nguyen and Z. Chen and A. Kannan and R. J. Weiss and K. Rao and K. Gonina and N. Jaitly and B. Li and J. Chorowski and M. Bacchiani}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.01769}, -} - -@inproceedings{toshniwal2018multilingual, - annote = asr, - title = {Multilingual Speech Recognition With A Single End-To-End Model}, - author = {S. Toshniwal and T. N. Sainath and R. J. Weiss and B. Li and P. Moreno and E. Weinstein and K. Rao}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1711.01694}, -} - -@inproceedings{li2018multidialect, - annote = asr, - title = {{Multi-Dialect Speech Recognition With a Single Sequence-to-Sequence Model}}, - author = {B. Li and T. N. Sainath and K. Sim and M. Bacchiani and E. Weinstein and P. Nguyen and Z. Chen and Y. Wu and K. Rao}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.01541}, -} - -@inproceedings{sainath2018no, - annote = asr, - title = {{No Need for a Lexicon? Evaluating the Value of the Pronunciation Lexica in End-to-End Models}}, - author = {T. N. Sainath and P. Prabhavalkar and S. Kumar and S. Lee and A. Kannan and D. Rybach and V. Schogol and P. Nguyen and B. Li and Y. Wu and Z. Chen and C. C. Chiu}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.01864}, -} - -@inproceedings{lawson2018learning, - annote = asr, - title = {{Learning hard alignments with variational inference}}, - author = {D. Lawson and C. C. Chiu and G. Tucker and C. Raffel and K. Swersky and N. Jaitly}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1705.05524}, -} - -@inproceedings{kannan2018analysis, - annote = asr, - title = {An Analysis of Incorporating an External Language Model into a Sequence-to-Sequence Model}, - author = {A. Kannan and Y. Wu and P. Nguyen and T. N. Sainath and Z. Chen and R. Prabhavalkar}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.01996}, -} - -@inproceedings{prabhavalkar2018minimum, - annote = asr, - author = {R. Prabhavalkar and T. N. Sainath and Y. Wu and P. Nguyen and Z. Chen and C. C. Chiu and A. Kannan}, - title = {{Minimum Word Error Rate Training for Attention-based Sequence-to-sequence Models}}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.01818}, -} - -@inproceedings{sainath2018improving, - annote = asr, - title = {{Improving the Performance of Online Neural Transducer Models}}, - author = {T. N. Sainath and C. C. Chiu and R. Prabhavalkar and A. Kannan and Y. Wu and P. Nguyen and Z. Chen Z}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.01807}, -} - -@inproceedings{chiu2018monotonic, - annote = asr, - title = {{Monotonic Chunkwise Attention}}, - author = {C. C. Chiu and C. Raffel}, - booktitle = iclr, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.05382}, -} - -@inproceedings{williams2018contextual, - annote = asr, - title = {{Contextual Speech Recognition in End-to-End Neural Network Systems using Beam Search}}, - author = {I. Williams and A. Kannan and P. Aleksic and D. Rybach and T. N. Sainath TN}, - booktitle = interspeech, - year = {2018}, - pdf = {https://www.isca-speech.org/archive/Interspeech_2018/pdfs/2416.pdf}, -} - -@inproceedings{chui2018speech, - annote = asr, - title = {{Speech recognition for medical conversations}}, - author = {C. C. Chiu and A. Tripathi and K. Chou and C. Co and N. Jaitly and D. Jaunzeikare and A. Kannan and P. Nguyen and H. Sak and A. Sankar and J. Tansuwan and N. Wan and Y. Wu and X. Zhang}, - booktitle = interspeech, - year = {2018}, - pdf = {https://arxiv.org/abs/1711.07274}, -} - -@inproceedings{pang2018compression, - annote = asr, - title = {{Compression of End-to-End Models}}, - author = {R. Pang and T. N. Sainath and R. Prabhavalkar and S. Gupta and Y. Wu and S. Zhang and C. C. Chiu}, - booktitle = interspeech, - year = {2018}, - pdf = {https://www.isca-speech.org/archive/Interspeech_2018/pdfs/1025.pdf}, -} - -@inproceedings{toshniwal2018comparison, - annote = asr, - title = {A comparison of techniques for language model integration in encoder-decoder speech recognition}, - author = {S. Toshniwal and A. Kannan and C. C. Chiu and Y. Wu and T. N. Sainath and K. Livescu}, - booktitle = slt, - year = {2018}, - pdf = {https://arxiv.org/abs/1807.10857}, -} - -@inproceedings{pundak2018deep, - annote = asr, - title = {Deep Context: End-to-End Contextual Speech Recognition}, - author = {G. Pundak and T. N. Sainath and R. Prabhavalkar and A. Kannan and D. Zhao}, - booktitle = slt, - year = {2018}, - pdf = {https://arxiv.org/abs/1808.02480}, -} - -@inproceedings{li2019bytes, - annote = asr, - author = {B. Li and Y. Zhang and T. N. Sainath and Y. Wu and W. Chan}, - title = {Bytes are All You Need: End-to-End Multilingual Speech Recognition and Synthesis with Bytes}, - booktitle = icassp, - year = {2019}, - pdf = {https://arxiv.org/abs/1811.09021}, -} - -@inproceedings{guo2019spelling, - annote = asr, - author = {J. Guo and T. N. Sainath and R. J. Weiss}, - title = {A Spelling Correction Model for End-to-End Speech Recognition}, - booktitle = icassp, - year = {2019}, - pdf = {https://arxiv.org/abs/1902.07178}, -} - -@inproceedings{alon2019contextual, - annote = asr, - author = {U. Alon and G. Pundak and T. N. Sainath}, - title = {Contextual Speech Recognition with Difficult Negative Training Examples}, - booktitle = icassp, - year = {2019}, - pdf = {https://arxiv.org/abs/1810.12170}, -} - -@inproceedings{qin2019imperceptible, - annote = asr, - author = {Y. Qin and N. Carlini and I. Goodfellow and G. Cottrell and C. Raffel}, - title = {Imperceptible, Robust, and Targeted Adversarial Examples for Automatic Speech Recognition}, - booktitle = icml, - year = {2019}, - pdf = {https://arxiv.org/abs/1903.10346}, -} - -@inproceedings{park2019specaugment, - annote = asr, - author = {D. S. Park and W. Chan and Y. Zhang and C. Chiu and B. Zoph and E. D. Cubuk and Q. V. Le}, - title = {{SpecAugment: A Simple Data Augmentation Method for Automatic Speech Recognition}}, - booktitle = {arXiv}, - year = {2019}, - pdf = {https://arxiv.org/pdf/1904.08779.pdf} -} - -@inproceedings{li2019semisupervised, - annote = asr, - title = {Semi-supervised Training for End-to-end Models via Weak Distillation}, - author = {B. Li and T. N. Sainath and R. Pang and Z. Wu}, - booktitle = icassp, - year = {2019}, - pdf = {https://ieeexplore.ieee.org/document/8682172}, -} - -@inproceedings{chang2019joint, - annote = asr, - title = {Joint Endpointing and Decoding with End-to-end Models}, - author = {S.-Y. Chang and R. Prabhavalkar and Y. He and T. N. Sainath and G. Simko}, - booktitle = icassp, - year = {2019}, - pdf = {https://ieeexplore.ieee.org/document/8683109}, -} - -@inproceedings{heymann2019improving, - annote = asr, - title = {Improving CTC Using Stimulated Learning for Sequence Modeling}, - author = {J. Heymann and K. C. Sim and B. Li}, - booktitle = icassp, - year = {2019}, - pdf = {https://ieeexplore.ieee.org/document/8682700}, -} - -@inproceedings{bruguier2019phoebe, - annote = asr, - title = {Phoebe: Pronunciation-aware Contextualization for End-to-end Speech Recognition}, - author = {A. Bruguier and R. Prabhavalkar and G. Pundak and T. N. Sainath}, - booktitle = icassp, - year = {2019}, - pdf = {https://ieeexplore.ieee.org/document/8682441}, -} - -@inproceedings{he2019streaming, - annote = asr, - title = {Streaming End-to-end Speech Recognition for Mobile Devices}, - author = {Y. He and T. N. Sainath and R. Prabhavalkar and I. McGraw and R. Alvarez and D. Zhao and D. Rybach and A. Kannan and Y. Wu and R. Pang and Q. Liang and D. Bhatia and Y. Shangguan and B. Li and G. Pundak and K. C. Sim and T. Bagby and S.-Y. Chang and K. Rao and A. Gruenstein}, - booktitle = icassp, - year = {2019}, - pdf = {https://arxiv.org/abs/1811.06621}, -} - -@inproceedings{irie2019unit, - annote = asr, - author = {K. Irie and R. Prabhavalkar and A. Kannan and A. Bruguier and D. Rybach and P. Nguyen}, - title = {On the Choice of Modeling Unit for Sequence-to-Sequence Speech Recognition}, - booktitle = interspeech, - year = {2019}, - pdf = {https://arxiv.org/abs/1902.01955}, -} - -@inproceedings{peyser2019numerics, - annote = asr, - title = {{Improving Performance of End-to-End ASR on Numeric Sequences}}, - author = {C. Peyser and H. Zhang and T. N. Sainath and Z. Wu}, - booktitle = interspeech, - year = {2019}, - pdf = {https://arxiv.org/abs/1907.01372}, -} - -@inproceedings{zhao2019shallow, - annote = asr, - author = {D. Zhao and T. N. Sainath and D. Rybach and D. Bhatia and B. Li and R. Pang}, - title = {Shallow-Fusion End-to-End Contextual Biasing}, - booktitle = interspeech, - year = {2019}, - pdf = {https://www.isca-speech.org/archive/Interspeech_2019/pdfs/1209.pdf}, -} - -@inproceedings{TaraRuoming19, - annote = asr, - title={Two-Pass End-to-End Speech Recognition}, - author={T. N. Sainath and R. Pang and D. Rybach and Y. He and R. Prabhavalkar and W. Li and M. Visontai and Q. Liang and T. Strohman and Y. Wu and I. McGraw and C.-C. Chiu}, - booktitle=interspeech, - year={2019}, - pdf = {https://arxiv.org/pdf/1908.10992}, -} - -@inproceedings{Chiu19longform, - annote = asr, - title={A Comparison of End-to-end Models for Long-form Speech Recognition}, - author={C.-C. Chiu and W. Han and Y. Zhang and R. Pang and S. Kishchenko and P. Nguyen and A. Narayanan and H. Liao and S. Zhang and A. Kannan and R. Prabhavalkar and Z. Chen and T. Sainath and Y. Wu}, - booktitle = asru, - year={2019}, - pdf = {https://arxiv.org/pdf/1911.02242} -} - -@inproceedings{narayanan2019longform, - annote = asr, - author={A. Narayanan and R. Prabhavalkar and C.C. Chiu and D. Rybach and T.N. Sainath and T. Strohman}, - title={Recognizing Long-Form Speech Using Streaming End-to-End Models}, - booktitle = asru, - year={2019}, - pdf = {https://arxiv.org/pdf/1910.11455} -} - -@inproceedings{sainath2020, - annote = asr, - title = {An Attention-Based Joint Acoustic and Text On-Device End-to-End Model}, - author = {Tara N. Sainath and Ruoming Pang and Ron Weiss and Yanzhang He and Chung-Cheng Chiu and Trevor Strohman}, - booktitle = icassp, - year = {2020}, -} - -@inproceedings{lu2020, - annote = asr, - title = {Speech Sentiment Analysis via Pre-Trained Features from End-To-End ASR Models}, - author = {Zhiyun Lu and Liangliang Cao and Yu Zhang and Chung-Cheng Chiu and James Fan}, - booktitle = icassp, - year = {2020}, -} - -@inproceedings{park2020, - annote = asr, - title = {SpecAugment on Large Scale Datasets}, - author = {Daniel Park and Yu Zhang and Chung-Cheng Chiu and Youzheng Chen and Bo Li and William Chan and Quoc Le and Yonghui Wu}, - booktitle = icassp, - year = {2020}, - pdf = {https://arxiv.org/abs/1912.05533} -} - -@inproceedings{e2e2020, - annote = asr, - title = {A Streaming On-Device End-to-End Model Surpassing Server-Side Conventional Model Quality and Latency}, - author = {Tara Sainath and Yanzhang He and Bo Li and Arun Narayanan and Ruoming Pang and Antoine Bruguier and Shuo-yiin Chang and Wei Li and Raziel Alvarez and Zhifeng Chen and Chung-cheng Chiu and David Garcia and Alex Gruenstein and Ke Hu and Minho Jin and Anjuli Kannan and Qiao Liang and Ian McGraw and Cal Peyser and Rohit Prabhavalkar and Golan Pundak and David Rybach and Yuan Shangguan and Yash Sheth and Trevor Strohman and Mirko Visontai and Yonghui Wu and Yu Zhang and Ding Zhao}, - booktitle = icassp, - year = {2020}, -} - -@inproceedings{conformer2020, - annote = asr, - title={Conformer: Convolution-augmented Transformer for Speech Recognition}, - author={Anmol Gulati and James Qin and Chung-Cheng Chiu and Niki Parmar and Yu Zhang and Jiahui Yu and Wei Han and Shibo Wang and Zhengdong Zhang and Yonghui Wu and Ruoming Pang}, - booktitle=interspeech, - year={2020}, - pdf = {https://arxiv.org/abs/2005.08100}, -} - -@inproceedings{contextnet2020, - annote = asr, - title={ContextNet: Improving Convolutional Neural Networks for Automatic Speech Recognition with Global Context}, - author={Wei Han and Zhengdong Zhang and Yu Zhang and Jiahui Yu and Chung-Cheng Chiu and James Qin and Anmol Gulati and Ruoming Pang and Yonghui Wu}, - booktitle=interspeech, - year={2020}, - pdf = {https://arxiv.org/abs/2005.03191}, -} - -@inproceedings{rescore2020, - annote = asr, - title={Parallel Rescoring with Transformer for Streaming On-Device Speech Recognition}, - author={Wei Li and James Qin and Chung-Cheng Chiu and Ruoming Pang and Yanzhang He}, - booktitle=interspeech, - year={2020} -} - -@inproceedings{noisystudent2020, - annote = asr, - title={Improved Noisy Student Training for Automatic Speech Recognition}, - author={Daniel S. Park and Yu Zhang and Ye Jia and Wei Han and Chung-Cheng Chiu and Bo Li and Yonghui Wu and Quoc V. Le}, - booktitle=interspeech, - year={2020}, - pdf = {https://arxiv.org/abs/2005.09629} -} - -%%%%%%%%%%%%%%%%%%%% -% Language understanding papers. - -@inproceedings{kannan2018semi, - annote = nlu, - author = {A. Kannan and K. Chen and D. Jaunzeikare and A. Rajkomar}, - title = {{Semi-Supervised Learning for Information Extraction from Dialogue}}, - booktitle = interspeech, - year = {2018}, - pdf = {https://www.isca-speech.org/archive/Interspeech_2018/pdfs/1318.pdf}, -} - -@inproceedings{yavuz2018calcs, - annote = nlu, - author = {S. Yavuz and C. C. Chiu and P. Nguyen and Y. Wu}, - title = {{CaLcs: Continuously Approximating Longest Common Subsequence for Sequence Level Optimization}}, - booktitle = emnlp, - year = {2018}, - pdf = {http://aclweb.org/anthology/D18-1406}, -} - -@inproceedings{haghani2018s2p, - annote = nlu, - author = {P. Haghani and A. Narayanan and M. Bacchiani and G. Chuang and N. Gaur and P. Moreno and R. Prabhavalkar and Z. Qu and A. Waters}, - title = {{From Audio to Semantics: Approaches to End-to-End Spoken Language Understanding}}, - booktitle = slt, - year = {2018}, - pdf = {https://arxiv.org/abs/1809.09190}, -} - -@inproceedings{chen2019smartcompose, - annote = nlu, - author = {M. X. Chen and B. N. Lee and G. Bansal and Y. Cao and S. Zhang and J. Lu and J. Tsay and Y. Wang and A. M. Dai and Z. Chen and T. Sohn and Y. Wu}, - title = {Gmail Smart Compose: Real-Time Assisted Writing}, - booktitle = {Proceedings of the 25th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining}, - year = {2019}, - publisher = {Association for Computing Machinery}, - url = {https://dl.acm.org/doi/10.1145/3292500.3330723}, - pdf = {https://arxiv.org/abs/1906.00080}, -} - - -%%%%%%%%%%%%%%%%%%%% -% Speech synthesis papers. - -@inproceedings{shen2018natural, - annote = tts, - title = {Natural {TTS} Synthesis by Conditioning {WaveNet} on Mel Spectrogram Predictions}, - author = {J. Shen and R. Pang and R. J. Weiss and M. Schuster and N. Jaitly and Z. Yang and Z. Chen and Y. Zhang and Y. Wang and R.J. Skerry-Ryan and R. A. Saurous and Y. Agiomyrgiannakis and Y. Wu}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.05884}, - sound_examples = {https://google.github.io/tacotron/publications/tacotron2/index.html}, -} - -@inproceedings{chorowski2018styletransfer, - annote = tts, - title = {On Using Backpropagation for Speech Texture Generation and Voice Conversion}, - author = {J. Chorowski and R. J. Weiss and R. A. Saurous and S. Bengio}, - booktitle = icassp, - year = {2018}, - pdf = {https://arxiv.org/abs/1712.08363}, - sound_examples = {https://google.github.io/speech_style_transfer/samples.html}, -} - -@inproceedings{jia2018multispeaker, - annote = tts, - title = {Transfer Learning from Speaker Verification to Multispeaker Text-To-Speech Synthesis}, - author = {Y. Jia and Y. Zhang and R. J. Weiss and Q. Wang and J. Shen and F. Ren and Z. Chen and P. Nguyen and R. Pang and I. Lopez-Moreno and Y. Wu}, - booktitle = nips, - year = {2018}, - pdf = {https://arxiv.org/abs/1806.04558}, - sound_examples = {https://google.github.io/tacotron/publications/speaker_adaptation/index.html}, -} - -@inproceedings{hsu2019hierarchical, - annote = tts, - title = {Hierarchical Generative Modeling for Controllable Speech Synthesis}, - author = {W. N. Hsu and Y. Zhang and R. J. Weiss and H. Zen and Y. Wu and Y. Wang and Y. Cao and Y. Jia and Z. Chen and J. Shen and P. Nguyen and R. Pang}, - booktitle = iclr, - year = {2019}, - pdf = {https://arxiv.org/abs/1810.07217}, - sound_examples = {https://google.github.io/tacotron/publications/gmvae_controllable_tts/index.html}, -} - -@inproceedings{hsu2018disentangling, - annote = tts, - author = {W. N. Hsu and Y. Zhang and R. J. Weiss and Y. A. Chung and Y. Wang and Y. Wu and J. Glass}, - title = {Disentangling Correlated Speaker and Noise for Speech Synthesis via Data Augmentation and Adversarial Factorization}, - booktitle = {NeurIPS 2018 Workshop on Interpretability and Robustness in Audio, Speech, and Language}, - year = {2018}, - pdf = {https://openreview.net/forum?id=Bkg9ZeBB37}, -} - -@inproceedings{zen2019libritts, - annote = tts, - title = {{LibriTTS}: A Corpus Derived from {LibriSpeech} for Text-to-Speech}, - author = {H. Zen and V. Dang and R. Clark and Y. Zhang and R. J. Weiss and Y. Jia and Z. Chen and Y. Wu}, - booktitle = interspeech, - year = {2019}, - pdf = {https://arxiv.org/abs/1904.02882}, - data = {http://www.openslr.org/60}, -} - -@inproceedings{biadsy2019parrotron, - annote = tts, - author = {F. Biadsy and R. J. Weiss and P. Moreno and D. Kanvesky and Y. Jia}, - title = {Parrotron: An End-to-End Speech-to-Speech Conversion Model and its Applications to Hearing-Impaired Speech and Speech Separation}, - booktitle = interspeech, - year = {2019}, - pdf = {https://arxiv.org/abs/1904.04169}, - sound_examples = {https://google.github.io/tacotron/publications/parrotron}, -} - -@inproceedings{zhang2019learning, - annote = tts, - author = {Y. Zhang and R. J. Weiss and H. Zen and Y. Wu and Z. Chen and R. J. Skerry-Ryan and Y. Jia and A. Rosenberg and B. Ramabhadran}, - title = {Learning to Speak Fluently in a Foreign Language: Multilingual Speech Synthesis and Cross-Language Voice Cloning}, - booktitle = interspeech, - year = {2019}, - pdf = {https://arxiv.org/abs/1907.04448}, - sound_examples = {http://google.github.io/tacotron/publications/multilingual}, -} - -@inproceedings{sun2020finegrained, - annote = tts, - author = {G. Sun and Y. Zhang and R. J. Weiss and Y. Cao and H. Zen and Y. Wu}, - title = {Fully-hierarchical Fine-grained Prosody Modeling for Interpretable Speech Synthesis}, - booktitle = icassp, - year = {2020}, - pdf = {https://arxiv.org/abs/2002.03785}, - sound_examples = {https://google.github.io/tacotron/publications/hierarchical_prosody}, -} - -@inproceedings{sun2020prosodyprior, - annote = tts, - author = {G. Sun and Y. Zhang and R. J. Weiss and Y. Cao and H. Zen and A. Rosenberg and B. Ramabhadran and Y. Wu}, - title = {Generating Diverse and Natural Text-to-Speech Samples Using A Quantized Fine-grained {VAE} and Auto-regressive Prosody Prior}, - booktitle = icassp, - year = {2020}, - pdf = {https://arxiv.org/abs/2002.03788}, - sound_examples = {https://google.github.io/tacotron/publications/prosody_prior}, -} - -%%%%%%%%%%%%%%%%%%%% -% Speech-to-text translation papers. - -@inproceedings{weiss2017sequence, - annote = st, - title = {Sequence-to-Sequence Models Can Directly Translate Foreign Speech}, - author = {R. J. Weiss and J. Chorowski and N. Jaitly and Y. Wu and Z. Chen}, - booktitle = interspeech, - year = {2017}, - pdf = {https://arxiv.org/abs/1703.08581}, -} - -@inproceedings{jia2019leveraging, - annote = st, - author = {Y. Jia and M. Johnson and W. Macherey and R. J. Weiss and Y. Cao and C. C. Chiu and N. Ari and S. Laurenzo and Y. Wu}, - title = {Leveraging Weakly Supervised Data to Improve End-to-End Speech-to-Text Translation}, - booktitle = icassp, - year = {2019}, - pdf = {https://arxiv.org/abs/1811.02050}, -} - -@inproceedings{jia2019direct, - annote = st, - author = {Y. Jia and R. J. Weiss and F. Biadsy and W. Macherey and M. Johnson and Z. Chen and Y. Wu}, - title = {Direct speech-to-speech translation with a sequence-to-sequence model}, - booktitle = interspeech, - year = {2019}, - pdf = {https://arxiv.org/abs/1904.06037}, - sound_examples = {https://google-research.github.io/lingvo-lab/translatotron}, -} - -%%%%%%%%%%%%%%%%%%%% -% Optimization papers - -@article{anil2020second, - annote = opt, - title={Second Order Optimization Made Practical}, - author={Anil, Rohan and Gupta, Vineet and Koren, Tomer and Regan, Kevin and Singer, Yoram}, - journal={arXiv preprint arXiv:2002.09018}, - pdf = {https://arxiv.org/abs/2002.09018}, - year={2020} -} - -@article{agarwal2020disentangling, - annote = opt, - title={Disentangling Adaptive Gradient Methods from Learning Rates}, - author={Agarwal, Naman and Anil, Rohan and Hazan, Elad and Koren, Tomer and Zhang, Cyril}, - journal={arXiv preprint arXiv:2002.11803}, - pdf = {https://arxiv.org/abs/2002.11803}, - year={2020} -} - -@inproceedings{anil2019memory, - annote = opt, - title={Memory Efficient Adaptive Optimization}, - author={Anil, Rohan and Gupta, Vineet and Koren, Tomer and Singer, Yoram}, - booktitle={Advances in Neural Information Processing Systems}, - pdf = {https://arxiv.org/abs/1901.11150}, - pages={9749--9758}, - year={2019} -} From ce79d06ed3f48ec687730cb9702f49aba1e7ee57 Mon Sep 17 00:00:00 2001 From: Daniel Galvez Date: Thu, 12 May 2022 02:16:08 +0000 Subject: [PATCH 2/5] python galvasr2/align/spark/align_cuda_decoder.py runs now at least until audio_id_text_id_mapping.json is created. --- README.md | 311 +-------------------- galvasr2/align/audio.py | 2 +- galvasr2/align/dsalign_main.py | 20 +- galvasr2/align/search.py | 2 +- galvasr2/align/spark/align_cuda_decoder.py | 77 +++-- galvasr2/align/spark/align_lib.py | 107 +------ galvasr2/align/spark/dsalign_lib.py | 8 +- galvasr2/align/stats.py | 2 +- galvasr2/align/text.py | 2 +- tf_env_collect.sh | 127 --------- 10 files changed, 67 insertions(+), 591 deletions(-) delete mode 100755 tf_env_collect.sh diff --git a/README.md b/README.md index f2fdc64a..2f41515f 100644 --- a/README.md +++ b/README.md @@ -1,314 +1,15 @@ -# People's Speech Data Pipelines (Originally a fork of Lingvo) +# People's Speech Data Pipelines -[![PyPI](https://badge.fury.io/py/lingvo.svg)](https://badge.fury.io/py/lingvo) -[![Python](https://img.shields.io/pypi/pyversions/lingvo)](https://badge.fury.io/py/tensorflow) +Installation -[![Documentation](https://img.shields.io/badge/api-reference-blue.svg)](https://tensorflow.github.io/lingvo) - -[![License](https://img.shields.io/github/license/tensorflow/lingvo)](LICENSE) - -## What is it? - -Lingvo is a framework for building neural networks in Tensorflow, particularly -sequence models. - -A list of publications using Lingvo can be found [here](PUBLICATIONS.md). - -## Table of Contents - -* [Releases](#releases) - * [Major breaking changes](#major-breaking-changes) -* [Quick start](#quick-start) - * [Installation](#installation) - * [Running the MNIST image model](#running-the-mnist-image-model) - * [Running the machine translation model](#running-the-machine-translation-model) - * [Running the 3d object detection model](#running-the-3d-object-detection-model) -* [Models](#models) - * [Automatic Speech Recognition](#automatic-speech-recognition) - * [Car](#car) - * [Image](#image) - * [Language Modelling](#language-modelling) - * [Machine Translation](#machine-translation) -* [References](#references) -* [License](#license) - -## Releases - -PyPI Version | Commit ------------- | ---------------------------------------- -0.7.2 | b05642fe386ee79e0d88aa083565c9a93428519e - -

    -Older releases -

    - -PyPI Version | Commit ------------- | ------ - -Details for older releases are unavailable. - -

    - -### Major breaking changes - -#### HEAD - -* **General** - * NestedMap Flatten/Pack/Transform/Filter etc now expand descendent dicts - as well. - * Subclasses of BaseLayer extending from `abc.ABCMeta` should now extend - `base_layer.ABCLayerMeta` instead. - * Trying to call self.CreateChild outside of `__init__` now raises an - error. - * `base_layer.initializer` has been removed. Subclasses no longer need to - decorate their `__init__` function. - * Trying to call self.CreateVariable outside of `__init__` or - `_CreateLayerVariables` now raises an error. - * It is no longer possible to access self.vars or self.theta inside of - `__init__`. Refactor by moving the variable creation and access to - `_CreateLayerVariables`. The variable scope is set automatically according to - the layer name in `_CreateLayerVariables`. - -
    -Older releases -

    - -Details for older releases are available. - -

    - -## Quick start - -### Installation - -There are two ways to set up Lingvo: installing a fixed version through pip, or -cloning the repository and building it with bazel. Docker configurations are -provided for each case. - -If you would just like to use the framework as-is, it is easiest to just install -it through pip. This makes it possible to develop and train custom models using -a frozen version of the Lingvo framework. However, it is difficult to modify the -framework code or implement new custom ops. - -If you would like to develop the framework further and potentially contribute -pull requests, you should avoid using pip and clone the repository instead. - -**pip:** - -The [Lingvo pip package](https://pypi.org/project/lingvo) can be installed with -`pip3 install lingvo`. - -See the -[codelab](https://colab.research.google.com/github/tensorflow/lingvo/blob/master/codelabs/introduction.ipynb) -for how to get started with the pip package. - -**From sources:** - -The prerequisites are: - -* a TensorFlow 2.2 [installation](https://www.tensorflow.org/install/), -* a `C++` compiler (only g++ 7.3 is officially supported), and -* the bazel build system. - -Refer to [docker/dev.dockerfile](docker/dev.dockerfile) for a set of working -requirements. - -`git clone` the repository, then use bazel to build and run targets directly. -The `python -m module` commands in the codelab need to be mapped onto `bazel -run` commands. - -**docker:** - -Docker configurations are available for both situations. Instructions can be -found in the comments on the top of each file. - -* [lib.dockerfile](docker/lib.dockerfile) has the Lingvo pip package - preinstalled. -* [dev.dockerfile](docker/dev.dockerfile) can be used to build Lingvo from - sources. - -[How to install docker.](https://docs.docker.com/install/linux/docker-ce/ubuntu/) - -### Running the MNIST image model - -#### Preparing the input data - -**pip:** - -```shell -mkdir -p /tmp/mnist -python3 -m lingvo.tools.keras2ckpt --dataset=mnist -``` - -**bazel:** - -```shell -mkdir -p /tmp/mnist -bazel run -c opt //lingvo/tools:keras2ckpt -- --dataset=mnist -``` - -The following files will be created in `/tmp/mnist`: - -* `mnist.data-00000-of-00001`: 53MB. -* `mnist.index`: 241 bytes. - -#### Running the model - -**pip:** - -```shell -cd /tmp/mnist -curl -O https://raw.githubusercontent.com/tensorflow/lingvo/master/lingvo/tasks/image/params/mnist.py -python3 -m lingvo.trainer --run_locally=cpu --mode=sync --model=mnist.LeNet5 --logdir=/tmp/mnist/log ``` - -**bazel:** - -```shell -(cpu) bazel build -c opt //lingvo:trainer -(gpu) bazel build -c opt --config=cuda //lingvo:trainer -bazel-bin/lingvo/trainer --run_locally=cpu --mode=sync --model=image.mnist.LeNet5 --logdir=/tmp/mnist/log --logtostderr +python setup.py develop +cp galvasr2/*.jar $(python -c "import pyspark; print(pyspark.__path__[0])")/jars ``` -After about 20 seconds, the loss should drop below 0.3 and a checkpoint will be -saved, like below. Kill the trainer with Ctrl+C. +Run forced alignment pipeline. ``` -trainer.py:518] step: 205, steps/sec: 11.64 ... loss:0.25747201 ... -checkpointer.py:115] Save checkpoint -checkpointer.py:117] Save checkpoint done: /tmp/mnist/log/train/ckpt-00000205 +python galvasr2/align/spark/align_cuda_decoder.py --stage=0 ``` -Some artifacts will be produced in `/tmp/mnist/log/control`: - -* `params.txt`: hyper-parameters. -* `model_analysis.txt`: model sizes for each layer. -* `train.pbtxt`: the training `tf.GraphDef`. -* `events.*`: a tensorboard events file. - -As well as in `/tmp/mnist/log/train`: - -* `checkpoint`: a text file containing information about the checkpoint files. -* `ckpt-*`: the checkpoint files. - -Now, let's evaluate the model on the "Test" dataset. In the normal training -setup the trainer and evaler should be run at the same time as two separate -processes. - -**pip:** - -```shell -python3 -m lingvo.trainer --job=evaler_test --run_locally=cpu --mode=sync --model=mnist.LeNet5 --logdir=/tmp/mnist/log -``` - -**bazel:** - -```shell -bazel-bin/lingvo/trainer --job=evaler_test --run_locally=cpu --mode=sync --model=image.mnist.LeNet5 --logdir=/tmp/mnist/log --logtostderr -``` - -Kill the job with Ctrl+C when it starts waiting for a new checkpoint. - -``` -base_runner.py:177] No new check point is found: /tmp/mnist/log/train/ckpt-00000205 -``` - -The evaluation accuracy can be found slightly earlier in the logs. - -``` -base_runner.py:111] eval_test: step: 205, acc5: 0.99775392, accuracy: 0.94150388, ..., loss: 0.20770954, ... -``` - -### Running the machine translation model - -To run a more elaborate model, you'll need a cluster with GPUs. Please refer to -[`third_party/py/lingvo/tasks/mt/README.md`](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/mt/README.md) -for more information. - -### Running the 3d object detection model - -To run the StarNet model using CloudTPUs on GCP, please refer to -[`third_party/py/lingvo/tasks/car/README.md`](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/car/README.md). - -## Models - -### Automatic Speech Recogition - -* [asr.librispeech.Librispeech960Grapheme](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/asr/params/librispeech.py)1,2 -* [asr.librispeech.Librispeech960Wpm](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/asr/params/librispeech.py)1,2 - -### Car - -* [car.kitti.StarNetCarModel0701](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/car/params/kitti.py)3 -* [car.kitti.StarNetPedCycModel0704](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/car/params/kitti.py)3 -* [car.waymo.StarNetVehicle](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/car/params/waymo.py)3 -* [car.waymo.StarNetPed](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/car/params/waymo.py)3 - -### Image - -* [image.mnist.LeNet5](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/image/params/mnist.py)4 - -### Language Modelling - -* [lm.one_billion_wds.WordLevelOneBwdsSimpleSampledSoftmax](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/lm/params/one_billion_wds.py)5 - -### Machine Translation - -* [mt.wmt14_en_de.WmtEnDeTransformerBase](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/mt/params/wmt14_en_de.py)6 -* [mt.wmt14_en_de.WmtEnDeRNMT](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/mt/params/wmt14_en_de.py)6 -* [mt.wmtm16_en_de.WmtCaptionEnDeTransformer](https://github.com/tensorflow/lingvo/blob/master/lingvo/tasks/mt/params/wmtm16_en_de.py)6 - - - -\[1]: [Listen, Attend and Spell](https://arxiv.org/pdf/1508.01211.pdf). William -Chan, Navdeep Jaitly, Quoc V. Le, and Oriol Vinyals. ICASSP 2016. - -\[2]: [End-to-end Continuous Speech Recognition using Attention-based Recurrent -NN: First Results](https://arxiv.org/pdf/1412.1602.pdf). Jan Chorowski, Dzmitry -Bahdanau, Kyunghyun Cho, and Yoshua Bengio. arXiv 2014. - -\[3]: -[StarNet: Targeted Computation for Object Detection in Point Clouds](https://arxiv.org/pdf/1908.11069.pdf). -Jiquan Ngiam, Benjamin Caine, Wei Han, Brandon Yang, Yuning Chai, Pei Sun, Yin -Zhou, Xi Yi, Ouais Alsharif, Patrick Nguyen, Zhifeng Chen, Jonathon Shlens, and -Vijay Vasudevan. arXiv 2019. - -\[4]: -[Gradient-based learning applied to document recognition](http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf). -Yann LeCun, Leon Bottou, Yoshua Bengio, and Patrick Haffner. IEEE 1998. - -\[5]: -[Exploring the Limits of Language Modeling](https://arxiv.org/pdf/1602.02410.pdf). -Rafal Jozefowicz, Oriol Vinyals, Mike Schuster, Noam Shazeer, and Yonghui Wu. -arXiv, 2016. - -\[6]: [The Best of Both Worlds: Combining Recent Advances in Neural Machine -Translation](http://aclweb.org/anthology/P18-1008). Mia X. Chen, Orhan Firat, -Ankur Bapna, Melvin Johnson, Wolfgang Macherey, George Foster, Llion Jones, Mike -Schuster, Noam Shazeer, Niki Parmar, Ashish Vaswani, Jakob Uszkoreit, Lukasz -Kaiser, Zhifeng Chen, Yonghui Wu, and Macduff Hughes. ACL 2018. - - - -## References - -* [API Docs](https://tensorflow.github.io/lingvo/) -* [Codelab](https://colab.research.google.com/github/tensorflow/lingvo/blob/master/codelabs/introduction.ipynb) - -Please cite this [paper](https://arxiv.org/abs/1902.08295) when referencing -Lingvo. - -``` -@misc{shen2019lingvo, - title={Lingvo: a Modular and Scalable Framework for Sequence-to-Sequence Modeling}, - author={Jonathan Shen and Patrick Nguyen and Yonghui Wu and Zhifeng Chen and others}, - year={2019}, - eprint={1902.08295}, - archivePrefix={arXiv}, - primaryClass={cs.LG} -} -``` - -## License - -[Apache License 2.0](LICENSE) \ No newline at end of file diff --git a/galvasr2/align/audio.py b/galvasr2/align/audio.py index ec020403..08905ebc 100644 --- a/galvasr2/align/audio.py +++ b/galvasr2/align/audio.py @@ -7,7 +7,7 @@ import numpy as np from webrtcvad import Vad -from utils import LimitingPool +from galvasr2.align.utils import LimitingPool DEFAULT_RATE = 16000 DEFAULT_CHANNELS = 1 diff --git a/galvasr2/align/dsalign_main.py b/galvasr2/align/dsalign_main.py index 7a6e16cd..fef2cf81 100644 --- a/galvasr2/align/dsalign_main.py +++ b/galvasr2/align/dsalign_main.py @@ -2,20 +2,18 @@ import json import logging import argparse -import deepspeech import subprocess import os.path as path import numpy as np import textdistance import multiprocessing from collections import Counter -from search import FuzzySearch +from galvasr2.align.search import FuzzySearch from glob import glob -from text import Alphabet, TextCleaner, levenshtein, similarity -from utils import enweight, log_progress -from audio import DEFAULT_RATE, read_frames_from_file, vad_split -from generate_lm import convert_and_filter_topk, build_lm -from generate_package import create_bundle +from galvasr2.align.text import Alphabet, TextCleaner, levenshtein, similarity +from galvasr2.align.utils import enweight, log_progress +from galvasr2.align.audio import DEFAULT_RATE, read_frames_from_file, vad_split +from galvasr2.align.generate_lm import convert_and_filter_topk, build_lm BEAM_WIDTH = 500 LM_ALPHA = 1 @@ -80,13 +78,7 @@ def read_script(script_path): def init_stt(output_graph_path, scorer_path): - # global model? How are we supposed to do multiprocessing then? - global model - model = deepspeech.Model(output_graph_path) - # It definitely seems very reasonable to adapt the - # ctc_decoder_with_kenlm tensorflow operator to use this... - model.enableExternalScorer(scorer_path) - logging.debug("Process {}: Loaded models".format(os.getpid())) + raise Exception("Dead code") def stt(sample): diff --git a/galvasr2/align/search.py b/galvasr2/align/search.py index 3e7a30e0..9f0079c0 100644 --- a/galvasr2/align/search.py +++ b/galvasr2/align/search.py @@ -1,5 +1,5 @@ from collections import Counter -from text import ngrams, similarity +from galvasr2.align.text import ngrams, similarity from galvasr2.align.smith_waterman import sw_align as sw_align_sped_up diff --git a/galvasr2/align/spark/align_cuda_decoder.py b/galvasr2/align/spark/align_cuda_decoder.py index ccd0c241..adc68d73 100644 --- a/galvasr2/align/spark/align_cuda_decoder.py +++ b/galvasr2/align/spark/align_cuda_decoder.py @@ -4,6 +4,7 @@ from copy import deepcopy from concurrent.futures import ThreadPoolExecutor +import glob import shlex import subprocess import logging @@ -18,8 +19,8 @@ import pyspark import pyspark.sql.functions as F import pyspark.sql.types as T -import tensorflow as tf +import galvasr2 from galvasr2.align.spark.align_lib import ( create_audio_segments_udf, create_audio_segment_names_udf, @@ -35,11 +36,12 @@ from galvasr2.align.spark.dsalign_lib import prepare_align_udf from galvasr2.align.spark.schemas import ARCHIVE_ORG_SCHEMA from galvasr2.utils import find_runfiles -import dsalign_main +from galvasr2.align import dsalign_main FLAGS = flags.FLAGS flags.DEFINE_integer("stage", 2, "") +flags.DEFINE_integer("end_stage", sys.maxsize, "") flags.DEFINE_string( "input_catalogue", "gs://the-peoples-speech-west-europe/archive_org/Mar_7_2021/CC_BY_SA_EXPANDED_LICENSES_FILTERED_ACCESS.jsonl.gz", @@ -146,13 +148,17 @@ def main(argv): "SC_PHYS_PAGES" ) # e.g. 4015976448 mem_gib = int((mem_bytes / (1024.0 ** 3)) * 0.9) - tar_jar = os.path.join( - find_runfiles(), "__main__/galvasr2/spark/tar_spark_datasource.jar" - ) + jar_path, = galvasr2.__path__ + jars = ",".join(glob.glob( + os.path.join(jar_path, "*.jar") + )) + print("GALVEZ:jars=", jars) + os.makedirs("/tmp/spark-events", exist_ok=True) spark = ( - pyspark.sql.SparkSession.builder.master(f"local[{os.cpu_count() - 1}]") + pyspark.sql.SparkSession.builder.master(f"local[{os.cpu_count()}]") .config("spark.eventLog.enabled", "true") - .config("spark.eventLog.dir", "/spark-events") + #.config("spark.eventLog.dir", "/spark-events") + .config("spark.hadoop.fs.AbstractFileSystem.gs.impl", "com.google.cloud.hadoop.fs.gcs.GoogleHadoopFS") .config("spark.sql.execution.arrow.pyspark.enabled", "true") .config( "spark.driver.extraJavaOptions", @@ -163,9 +169,9 @@ def main(argv): "-Dio.netty.tryReflectionSetAccessible=true", ) .config("spark.driver.memory", f"{mem_gib}g") - .config("spark.history.fs.logDirectory", "/spark-events") + # .config("spark.history.fs.logDirectory", "/spark-events") .config("spark.sql.execution.arrow.maxRecordsPerBatch", "1") - .config("spark.jars", tar_jar) + .config("spark.jars", jars) .config("spark.local.dir", "/mnt/disks/spark-scratch/") .getOrCreate() ) @@ -173,6 +179,24 @@ def main(argv): logging.getLogger("py4j").setLevel(logging.ERROR) catalogue_df = load_audio_id_text_id_mapping(spark, FLAGS.input_catalogue) + audio_paths = F.concat( + F.lit(FLAGS.input_gcs_path), + F.lit("/"), + F.col("identifier"), + F.lit("/"), + F.col("audio_document_id"), + ) + srt_paths = F.concat( + F.lit(FLAGS.input_gcs_path), + F.lit("/"), + F.col("identifier"), + F.lit("/"), + F.col("text_document_id"), + ) + temp_catalogue_df = catalogue_df.withColumn("audio_paths", audio_paths).withColumn("srt_paths", srt_paths) + # print("GALVEZ:schema=") + # temp_catalogue_df.printSchema() + temp_catalogue_df.toPandas().to_json("audio_id_text_id_mapping.json", orient="records", lines=True) _, licenseurl_df = load_audio_and_text_dfs(spark, FLAGS.input_catalogue) licenseurl_df = licenseurl_df.select( @@ -194,7 +218,7 @@ def main(argv): os.makedirs(FLAGS.work_dir, exist_ok=True) wav_scp = os.path.join(FLAGS.work_dir, "wav.scp") ctm_out_dir = os.path.join(FLAGS.work_dir, "decoder_ctm_dir") - if FLAGS.stage <= 0: + if FLAGS.stage <= 0 and FLAGS.end_stage >= 0: catalogue_df = catalogue_df.cache() # catalogue_df.write.mode("overwrite").format("csv").options(header="true").save(key_int_mapping) training_sample_rows = catalogue_df.collect() @@ -223,7 +247,7 @@ def main(argv): # Can get 266x RTF with this configuration. Keep it? # bath size of 100 and num channels of 100 works just fine - if FLAGS.stage <= 1: + if FLAGS.stage <= 1 and FLAGS.end_stage >= 1: if not FLAGS.work_dir.startswith("gs://"): os.makedirs(ctm_out_dir, exist_ok=True) with TemporaryMountDirectory( @@ -282,7 +306,7 @@ def run_gpu(posix_wav_scp_shard, gpu_number): executor.shutdown(wait=True) alignments_dir = os.path.join(FLAGS.alignments_work_dir, "alignments_json_jul_28") - if FLAGS.stage <= 2: + if FLAGS.stage <= 2 and FLAGS.end_stage >= 2: # TODO: Add options to DSAlign here dsalign_args = dsalign_main.parse_args( ["--output-wer", "--output-cer"] @@ -342,7 +366,7 @@ def run_gpu(posix_wav_scp_shard, gpu_number): manifest_dir = os.path.join(FLAGS.work_dir, "dataset_manifest") tars_dir = os.path.join(FLAGS.work_dir, "dataset_tars") - if FLAGS.stage <= 3: + if FLAGS.stage <= 3 and FLAGS.end_stage >= 3: duplicate_data_path = "gs://the-peoples-speech-west-europe/forced-aligner/data_deduplication/data_deduplication_v2_lines.json" duplicates_df = spark.read.format("json").load(duplicate_data_path) @@ -557,9 +581,13 @@ def run_gpu(posix_wav_scp_shard, gpu_number): # coalesce(1) seems to make the create_audio_segments_udf function run serially output_df.write.mode("overwrite").json(manifest_dir) - repartitioned_tars_dir = os.path.join(FLAGS.work_dir, "repartitioned_dataset_tars") - tmp_tars_dir = os.path.join(FLAGS.work_dir, "repartitioned_dataset_tmp_dir") - if FLAGS.stage <= 4: + repartitioned_tars_dir = os.path.join( + FLAGS.work_dir, "repartitioned_dataset_tars" + ) + tmp_tars_dir = os.path.join( + FLAGS.work_dir, "repartitioned_dataset_tmp_dir" + ) + if FLAGS.stage <= 4 and FLAGS.end_stage >= 4: tars_df = spark.read.format("tar").load(tars_dir) # .limit(100) number_of_rows = tars_df.count() @@ -617,7 +645,7 @@ def run_gpu(posix_wav_scp_shard, gpu_number): FLAGS.work_dir, "dataset_manifest_nemo_single" ) - if FLAGS.stage <= 5: + if FLAGS.stage <= 5 and FLAGS.end_stage >= 5: json_df = spark.read.format("json").load(manifest_dir) nemo_df = json_df.select( F.explode( @@ -654,7 +682,7 @@ def run_gpu(posix_wav_scp_shard, gpu_number): single_manifest_dir = os.path.join(FLAGS.work_dir, "dataset_manifest_single") single_tar_dir = os.path.join(FLAGS.work_dir, "dataset_tars_single") # Create single tar file and single json file - if FLAGS.stage <= 6: + if FLAGS.stage <= 6 and FLAGS.end_stage >= 6: json_df = spark.read.format("json").load(manifest_dir) json_df.coalesce(1).write.format("json").mode("overwrite").save( single_manifest_dir @@ -666,16 +694,3 @@ def run_gpu(posix_wav_scp_shard, gpu_number): if __name__ == "__main__": app.run(main) - - -# GALVEZ: [ -# Row(key='\x1d\x0c\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_0.flac'), -# Row(key='�\x03\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_1.flac'), -# Row(key='X\x05\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_2.flac'), -# Row(key='�\x01\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_3.flac'), -# Row(key='�\r\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_4.flac'), -# Row(key='�\x03\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_5.flac'), -# Row(key='&\x04\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_6.flac'), -# Row(key='�\x00\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_7.flac'), -# Row(key='�\x07\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_8.flac'), -# Row(key=';\x0e\x00\x00`\x00\x00\x00urts_ca9_04-55168-gov_uscourts_ca9_04-55168_2005-07-11_mp3_9.flac')] diff --git a/galvasr2/align/spark/align_lib.py b/galvasr2/align/spark/align_lib.py index b8c8b7b3..d728fa20 100644 --- a/galvasr2/align/spark/align_lib.py +++ b/galvasr2/align/spark/align_lib.py @@ -16,7 +16,6 @@ from typing import List, Tuple import wave -import ds_ctcdecoder from ftfy import fix_text, guess_bytes import langid import numpy as np @@ -25,11 +24,10 @@ from pydub import AudioSegment import pyspark from matching.games import HospitalResident -import tensorflow as tf +import srt import tqdm import re -import srt from pyspark.sql.functions import col, pandas_udf import pyspark.sql.types as T @@ -317,109 +315,6 @@ def vad( GENERATE_LM_OUTPUT_SCHEMA = StructType([StructField("path", StringType())]) -def prepare_generate_lm_udf(kenlm_path: str, debug_work_dir: str, alphabet_path: str): - # @pandas_udf(GENERATE_LM_OUTPUT_SCHEMA) - # TODO: Need to sort the log_probabilities by int64_uttid (right?) - def generate_lm( - grouping_key: Tuple[np.str, np.str], data_df: pd.DataFrame - ) -> pd.DataFrame: - ( - identifier, - text_document_id, - ) = grouping_key - identifier = str(identifier) - text_document_id = str(text_document_id) - - transcript = data_df.transcript[0] - with tempfile.NamedTemporaryFile("w+t", dir=debug_work_dir) as input_txt: - input_txt.write(transcript) - input_txt.flush() - os.makedirs(os.path.join(debug_work_dir, identifier), exist_ok=True) - scorer_path = os.path.join( - debug_work_dir, identifier, text_document_id + ".scorer" - ) - data_lower, vocab_str = convert_and_filter_topk( - scorer_path, input_txt.name, 500000 - ) - build_lm( - scorer_path, - kenlm_path, - 5, - "85%", - "0|0|1", - True, - 255, - 8, - "trie", - data_lower, - vocab_str, - ) - os.remove(scorer_path + "." + "lower.txt.gz") - os.remove(scorer_path + "." + "lm.arpa") - os.remove(scorer_path + "." + "lm_filtered.arpa") - - create_bundle( - alphabet_path, - scorer_path + "." + "lm.binary", - scorer_path + "." + "vocab-500000.txt", - scorer_path, - False, - 0.931289039105002, - 1.1834137581510284, - ) - os.remove(scorer_path + "." + "lm.binary") - os.remove(scorer_path + "." + "vocab-500000.txt") - - with open(alphabet_path) as fh: - num_output_symbols = len(fh.readlines()) + 1 - assert num_output_symbols == 32, f"GALVEZ:{num_output_symbols}" - transcripts = [] - - id_to_symbol = {} - with open(alphabet_path) as fh: - for i, line in enumerate(fh): - id_to_symbol[i] = line.rstrip() - id_to_symbol[31] = "blank" - - for row in data_df.itertuples(): - log_probabilities = row.log_probabilities.reshape(-1, num_output_symbols) - probabilities = np.exp(log_probabilities) - # np.exp(probabilities, out=probabilities) - np.testing.assert_allclose(probabilities.sum(axis=1), 1.0, atol=1e-3) - # simple_decoder_output = [] - # for t in range(probabilities.shape[0]): - # best = np.argmax(probabilities[t,:]) - # print(np.max(probabilities[t,:])) - # if (id_to_symbol[best] != "blank"): - # simple_decoder_output.append(id_to_symbol[best]) - - # print("GALVEZ simple output:", "".join(simple_decoder_output)) - - cutoff_prob = 1.0 - cutoff_top_n = 100 - scorer = ds_ctcdecoder.Scorer() - result = scorer.init( - scorer_path.encode("utf-8"), alphabet_path.encode("utf-8") - ) - scorer.set_utf8_mode(False) - assert result == 0, result - alphabet = ds_ctcdecoder.Alphabet() - result = alphabet.init(alphabet_path.encode("utf-8")) - assert not scorer.is_utf8_mode() - assert result == 0, result - scorer = None - outputs = ds_ctcdecoder.ctc_beam_search_decoder( - probabilities, alphabet, 100, cutoff_prob, cutoff_top_n, scorer - ) - print(f"GALVEZ:output={outputs[0][1]}") - print(f"GALVEZ:length={probabilities.shape[0] * 30. / 1000.}") - transcripts.append(outputs[0][1]) - - return pd.DataFrame({"path": pd.Series(transcripts)}) - - return generate_lm - - def load_audio_and_text_dfs(spark, input_catalogue_path: str): df = spark.read.format("json").schema(ARCHIVE_ORG_SCHEMA).load(input_catalogue_path) diff --git a/galvasr2/align/spark/dsalign_lib.py b/galvasr2/align/spark/dsalign_lib.py index ceda82e9..280756db 100644 --- a/galvasr2/align/spark/dsalign_lib.py +++ b/galvasr2/align/spark/dsalign_lib.py @@ -6,12 +6,12 @@ from typing import Dict, List, Tuple from collections import Counter -from search import FuzzySearch +from galvasr2.align.search import FuzzySearch import textdistance from glob import glob -from text import Alphabet, TextCleaner, levenshtein, similarity -from utils import enweight -from dsalign_main import ALGORITHMS, NAMED_NUMBERS +from galvasr2.align.text import Alphabet, TextCleaner, levenshtein, similarity +from galvasr2.align.utils import enweight +from galvasr2.align.dsalign_main import ALGORITHMS, NAMED_NUMBERS import logging diff --git a/galvasr2/align/stats.py b/galvasr2/align/stats.py index 32018d88..841b3204 100644 --- a/galvasr2/align/stats.py +++ b/galvasr2/align/stats.py @@ -6,7 +6,7 @@ from pickle import load, dump from collections import Counter from datetime import timedelta -from utils import log_progress +from galvasr2.align.utils import log_progress def fail(message, code=1): diff --git a/galvasr2/align/text.py b/galvasr2/align/text.py index 3fc2819f..7dae72e0 100644 --- a/galvasr2/align/text.py +++ b/galvasr2/align/text.py @@ -3,7 +3,7 @@ import codecs from six.moves import range from collections import Counter -from utils import enweight +from galvasr2.align.utils import enweight class Alphabet(object): diff --git a/tf_env_collect.sh b/tf_env_collect.sh deleted file mode 100755 index 925426e4..00000000 --- a/tf_env_collect.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2017 The TensorFlow 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. -# ============================================================================== - -set -u # Check for undefined variables - -die() { - # Print a message and exit with code 1. - # - # Usage: die - # e.g., die "Something bad happened." - - echo $@ - exit 1 -} - -echo "Collecting system information..." - -OUTPUT_FILE=tf_env.txt -python_bin_path=$(which python || which python3 || die "Cannot find Python binary") - -{ - echo - echo "== cat /etc/issue ===============================================" - uname -a - uname=`uname -s` - if [ "$(uname)" == "Darwin" ]; then - echo Mac OS X `sw_vers -productVersion` - elif [ "$(uname)" == "Linux" ]; then - cat /etc/*release | grep VERSION - fi - - echo - echo '== are we in docker =============================================' - num=`cat /proc/1/cgroup | grep docker | wc -l`; - if [ $num -ge 1 ]; then - echo "Yes" - else - echo "No" - fi - - echo - echo '== compiler =====================================================' - c++ --version 2>&1 - - echo - echo '== bazel =====================================================' - bazel version - - echo - echo '== uname -a =====================================================' - uname -a - - echo - echo '== check pips ===================================================' - pip list 2>&1 | grep "proto\|numpy\|tensorflow\|lingvo" - - - echo - echo '== check for virtualenv =========================================' - ${python_bin_path} -c "import sys;print(hasattr(sys, \"real_prefix\"))" - - echo - echo '== tensorflow import ============================================' -} >> ${OUTPUT_FILE} - -cat < /tmp/check_tf.py -import tensorflow as tf; -print("tf.VERSION = %s" % tf.VERSION) -print("tf.GIT_VERSION = %s" % tf.GIT_VERSION) -print("tf.COMPILER_VERSION = %s" % tf.GIT_VERSION) -with tf.Session() as sess: - print("Sanity check: %r" % sess.run(tf.constant([1,2,3])[:1])) -EOF -${python_bin_path} /tmp/check_tf.py 2>&1 >> ${OUTPUT_FILE} - -DEBUG_LD=libs ${python_bin_path} -c "import tensorflow" 2>>${OUTPUT_FILE} > /tmp/loadedlibs - -{ - grep libcudnn.so /tmp/loadedlibs - echo - echo '== env ==========================================================' - if [ -z ${LD_LIBRARY_PATH+x} ]; then - echo "LD_LIBRARY_PATH is unset"; - else - echo LD_LIBRARY_PATH ${LD_LIBRARY_PATH} ; - fi - if [ -z ${DYLD_LIBRARY_PATH+x} ]; then - echo "DYLD_LIBRARY_PATH is unset"; - else - echo DYLD_LIBRARY_PATH ${DYLD_LIBRARY_PATH} ; - fi - - - echo - echo '== nvidia-smi ===================================================' - nvidia-smi 2>&1 - - echo - echo '== cuda libs ===================================================' -} >> ${OUTPUT_FILE} - -find /usr/local -type f -name 'libcudart*' 2>/dev/null | grep cuda | grep -v "\\.cache" >> ${OUTPUT_FILE} -find /usr/local -type f -name 'libudnn*' 2>/dev/null | grep cuda | grep -v "\\.cache" >> ${OUTPUT_FILE} - -# Remove any words with google. -mv $OUTPUT_FILE old-$OUTPUT_FILE -grep -v -i google old-${OUTPUT_FILE} > $OUTPUT_FILE - -echo "Wrote environment to ${OUTPUT_FILE}. You can review the contents of that file." -echo "and use it to populate the fields in the github issue template." -echo -echo "cat ${OUTPUT_FILE}" -echo - From 4c8aeff970ad167dc431eca633e135d3ce913d8b Mon Sep 17 00:00:00 2001 From: Daniel Galvez Date: Thu, 12 May 2022 02:34:41 +0000 Subject: [PATCH 3/5] Add java jars with git lfs. --- .gitattributes | 1 + README.md | 4 +++ galvasr2/__init__.py | 0 galvasr2/align/__init__.py | 0 galvasr2/align/spark/__init__.py | 0 .../gcs-connector-hadoop3-2.2.6-shaded.jar | 3 ++ galvasr2/tar_spark_datasource.jar | 3 ++ setup.py | 32 +++++++++++++++++++ 8 files changed, 43 insertions(+) create mode 100644 .gitattributes create mode 100644 galvasr2/__init__.py create mode 100644 galvasr2/align/__init__.py create mode 100644 galvasr2/align/spark/__init__.py create mode 100644 galvasr2/gcs-connector-hadoop3-2.2.6-shaded.jar create mode 100755 galvasr2/tar_spark_datasource.jar create mode 100644 setup.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..7c32d5f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.jar filter=lfs diff=lfs merge=lfs -text diff --git a/README.md b/README.md index 2f41515f..b289511d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ Installation + ``` +sudo apt-get install git-lfs +# Set up a virtual environment of some sort +pip install numpy Cython python setup.py develop cp galvasr2/*.jar $(python -c "import pyspark; print(pyspark.__path__[0])")/jars ``` diff --git a/galvasr2/__init__.py b/galvasr2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/galvasr2/align/__init__.py b/galvasr2/align/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/galvasr2/align/spark/__init__.py b/galvasr2/align/spark/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/galvasr2/gcs-connector-hadoop3-2.2.6-shaded.jar b/galvasr2/gcs-connector-hadoop3-2.2.6-shaded.jar new file mode 100644 index 00000000..7833ddfb --- /dev/null +++ b/galvasr2/gcs-connector-hadoop3-2.2.6-shaded.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b6f1be75928da71fe8926c8bc74b9a4e3570e4f12df3880ec79e586118b0e97 +size 33831057 diff --git a/galvasr2/tar_spark_datasource.jar b/galvasr2/tar_spark_datasource.jar new file mode 100755 index 00000000..ea2ef162 --- /dev/null +++ b/galvasr2/tar_spark_datasource.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a8775725e31eced0ebdff2b7afe2ab400d5f0fd063d1905083adcc2f34454fd +size 17333 diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..a01b7929 --- /dev/null +++ b/setup.py @@ -0,0 +1,32 @@ +from setuptools import setup, find_packages + +from Cython.Build import cythonize +import numpy + +print("GALVEZ:", find_packages()) + +setup( + name="peoples-speech", + version="1.0", + packages=find_packages(), + install_requires=[ + "pyspark==3.1.2", + "absl-py", + "pandas", + "matching", + "langid", + "tqdm", + "pydub", + "ftfy", + "srt", + "pyarrow", + "textdistance", + "sox", + "Cython", + "webrtcvad" + ], + include_package_data=True, + package_data={'galvasr2': ['*.jar']}, + ext_modules=cythonize("galvasr2/align/smith_waterman.pyx"), + include_dirs=[numpy.get_include()], +) From 76cd0e4c35657e6dde55362f3f6ffe43eb880127 Mon Sep 17 00:00:00 2001 From: Daniel Galvez Date: Thu, 26 May 2022 06:41:12 +0000 Subject: [PATCH 4/5] Run black. --- galvasr2/align/export.py | 8 +++--- galvasr2/align/spark/align_cuda_decoder.py | 29 +++++++++++----------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/galvasr2/align/export.py b/galvasr2/align/export.py index 5409ca73..71b1bddf 100644 --- a/galvasr2/align/export.py +++ b/galvasr2/align/export.py @@ -93,13 +93,13 @@ def get_sample_size(population_size): margin_of_error = 0.01 fraction_picking = 0.50 z_score = 2.58 # Corresponds to confidence level 99% - numerator = (z_score**2 * fraction_picking * (1 - fraction_picking)) / ( - margin_of_error**2 + numerator = (z_score ** 2 * fraction_picking * (1 - fraction_picking)) / ( + margin_of_error ** 2 ) sample_size = 0 for train_size in range(population_size, 0, -1): - denominator = 1 + (z_score**2 * fraction_picking * (1 - fraction_picking)) / ( - margin_of_error**2 * train_size + denominator = 1 + (z_score ** 2 * fraction_picking * (1 - fraction_picking)) / ( + margin_of_error ** 2 * train_size ) sample_size = int(numerator / denominator) if 2 * sample_size + train_size <= population_size: diff --git a/galvasr2/align/spark/align_cuda_decoder.py b/galvasr2/align/spark/align_cuda_decoder.py index adc68d73..a0d9c61d 100644 --- a/galvasr2/align/spark/align_cuda_decoder.py +++ b/galvasr2/align/spark/align_cuda_decoder.py @@ -148,17 +148,18 @@ def main(argv): "SC_PHYS_PAGES" ) # e.g. 4015976448 mem_gib = int((mem_bytes / (1024.0 ** 3)) * 0.9) - jar_path, = galvasr2.__path__ - jars = ",".join(glob.glob( - os.path.join(jar_path, "*.jar") - )) + (jar_path,) = galvasr2.__path__ + jars = ",".join(glob.glob(os.path.join(jar_path, "*.jar"))) print("GALVEZ:jars=", jars) os.makedirs("/tmp/spark-events", exist_ok=True) spark = ( pyspark.sql.SparkSession.builder.master(f"local[{os.cpu_count()}]") .config("spark.eventLog.enabled", "true") - #.config("spark.eventLog.dir", "/spark-events") - .config("spark.hadoop.fs.AbstractFileSystem.gs.impl", "com.google.cloud.hadoop.fs.gcs.GoogleHadoopFS") + # .config("spark.eventLog.dir", "/spark-events") + .config( + "spark.hadoop.fs.AbstractFileSystem.gs.impl", + "com.google.cloud.hadoop.fs.gcs.GoogleHadoopFS", + ) .config("spark.sql.execution.arrow.pyspark.enabled", "true") .config( "spark.driver.extraJavaOptions", @@ -193,10 +194,14 @@ def main(argv): F.lit("/"), F.col("text_document_id"), ) - temp_catalogue_df = catalogue_df.withColumn("audio_paths", audio_paths).withColumn("srt_paths", srt_paths) + temp_catalogue_df = catalogue_df.withColumn("audio_paths", audio_paths).withColumn( + "srt_paths", srt_paths + ) # print("GALVEZ:schema=") # temp_catalogue_df.printSchema() - temp_catalogue_df.toPandas().to_json("audio_id_text_id_mapping.json", orient="records", lines=True) + temp_catalogue_df.toPandas().to_json( + "audio_id_text_id_mapping.json", orient="records", lines=True + ) _, licenseurl_df = load_audio_and_text_dfs(spark, FLAGS.input_catalogue) licenseurl_df = licenseurl_df.select( @@ -581,12 +586,8 @@ def run_gpu(posix_wav_scp_shard, gpu_number): # coalesce(1) seems to make the create_audio_segments_udf function run serially output_df.write.mode("overwrite").json(manifest_dir) - repartitioned_tars_dir = os.path.join( - FLAGS.work_dir, "repartitioned_dataset_tars" - ) - tmp_tars_dir = os.path.join( - FLAGS.work_dir, "repartitioned_dataset_tmp_dir" - ) + repartitioned_tars_dir = os.path.join(FLAGS.work_dir, "repartitioned_dataset_tars") + tmp_tars_dir = os.path.join(FLAGS.work_dir, "repartitioned_dataset_tmp_dir") if FLAGS.stage <= 4 and FLAGS.end_stage >= 4: tars_df = spark.read.format("tar").load(tars_dir) # .limit(100) number_of_rows = tars_df.count() From 46b5fd053173d3637caa82a9f07910949aedcb1f Mon Sep 17 00:00:00 2001 From: autoblack <“‘dt.galvez@gmail.com’”> Date: Thu, 26 May 2022 06:42:27 +0000 Subject: [PATCH 5/5] fixup: Format Python code with Black --- galvasr2/align/export.py | 8 ++++---- galvasr2/align/spark/align_cuda_decoder.py | 2 +- galvasr2/align/spark/align_lib_test.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/galvasr2/align/export.py b/galvasr2/align/export.py index 71b1bddf..5409ca73 100644 --- a/galvasr2/align/export.py +++ b/galvasr2/align/export.py @@ -93,13 +93,13 @@ def get_sample_size(population_size): margin_of_error = 0.01 fraction_picking = 0.50 z_score = 2.58 # Corresponds to confidence level 99% - numerator = (z_score ** 2 * fraction_picking * (1 - fraction_picking)) / ( - margin_of_error ** 2 + numerator = (z_score**2 * fraction_picking * (1 - fraction_picking)) / ( + margin_of_error**2 ) sample_size = 0 for train_size in range(population_size, 0, -1): - denominator = 1 + (z_score ** 2 * fraction_picking * (1 - fraction_picking)) / ( - margin_of_error ** 2 * train_size + denominator = 1 + (z_score**2 * fraction_picking * (1 - fraction_picking)) / ( + margin_of_error**2 * train_size ) sample_size = int(numerator / denominator) if 2 * sample_size + train_size <= population_size: diff --git a/galvasr2/align/spark/align_cuda_decoder.py b/galvasr2/align/spark/align_cuda_decoder.py index a0d9c61d..baaf7117 100644 --- a/galvasr2/align/spark/align_cuda_decoder.py +++ b/galvasr2/align/spark/align_cuda_decoder.py @@ -147,7 +147,7 @@ def main(argv): mem_bytes = os.sysconf("SC_PAGE_SIZE") * os.sysconf( "SC_PHYS_PAGES" ) # e.g. 4015976448 - mem_gib = int((mem_bytes / (1024.0 ** 3)) * 0.9) + mem_gib = int((mem_bytes / (1024.0**3)) * 0.9) (jar_path,) = galvasr2.__path__ jars = ",".join(glob.glob(os.path.join(jar_path, "*.jar"))) print("GALVEZ:jars=", jars) diff --git a/galvasr2/align/spark/align_lib_test.py b/galvasr2/align/spark/align_lib_test.py index 147e1b8a..e8a450ca 100644 --- a/galvasr2/align/spark/align_lib_test.py +++ b/galvasr2/align/spark/align_lib_test.py @@ -128,7 +128,7 @@ def setUpClass(cls): mem_bytes = os.sysconf("SC_PAGE_SIZE") * os.sysconf( "SC_PHYS_PAGES" ) # e.g. 4015976448 - mem_gib = int((mem_bytes / (1024.0 ** 3)) * 0.9) + mem_gib = int((mem_bytes / (1024.0**3)) * 0.9) tar_jar = os.path.join( find_runfiles(), "__main__/galvasr2/spark/tar_spark_datasource.jar" )

    fHTZaV@C-m;%Ltj>0*#2Ki)XD%B;6x-h%ZSkf(1u` zIsD^Xi$6qOSuC;ZdPR{&o_?RxC;JZyPRr04@jjytU>E+ROcN1mySVWMhHmU{YZx}< z8TGCW*M;%xH>{Q>RU2D5I-02!R7claLb{@8kZ7pleFYi}HmPi|Gy2z@Lh89s>G5uz zRzhb2L$zLuoJ@Ocqsv37CdpjCZRbdvdK?z$8~8GZ(J)OqrocRIRUsW#8TuzIqf14<#BQ zbSs>L5?^vN4*p)Qf}7p{kUsSz8;-z(@xiU1^(f0nP0Z18iOpOuc`crNqGL8J8`tvI zlx62&MF9FFvlc}MVZ>`z{h}vn<8pO`>&qV)uXdT&ASD~n4r(mudDY8R{ycg_j{V148y!0vv$qw5t$7k zqkDf$4pu+zDu+o~Y zv~4Z$A}t=QUj4c1cbRZZm|oVwR{O~36A}B&po@33Z<#g^E$ww5b#?(@%eS(a8*qU7 zeqTn{Qr%9nrg76+88>tGtjX-zSRakmRp;X~(gKrc*B*wVC`|+2a)y5*IkFt(=yi;T zStto@SHHK;cW6obJ(5=T+jt4B?ZqrB2B8e-a^?}(&o^r>kD4K#W;_Epl48keJ5}Vv zq4vIb64K6u?sS?069E-~!miMli230cNe62Cu`9L_Vb$@Gcuz$lzNOzL1 zHx24tm~z&wZ+!m;x75c&Zc+ztXw^8QTW3T@{HlYxPAu*mcYc>z_1)zJ#F{ge9sjwBGI83F`D)|<&Ky@78tmMXs zhZb?aK7f0@c6&4IUe5Q!J_%~7@}n4_?vjnO@(3T>3>|uJ`ICJhUUuJTamT9iql@GC zb;8SykZf~z7<1B`Jst*|x{})?G0G(Q#|3wBjl43W&#uMU|MW1fx=UO)`wXXJgx*zv zDcT3}PcDO^FFn8SYwE}<;BWJnxc{*9Dw)|Ou35-TQq!QDz29%2u=H)h4nWAChc1{? zEM;6AC1u$Oo_=Gc%Gx3r(N;I1OW~uzqTjBv&BPnYHW=dzYZFthV0*`EKdmUgUg-Mkl=cV|Or zhgY(@7MLjPwI5m6mwdm*DMxZr&PprSf%ME&hIKj1NVVKW`En{lF3d(26TzZIJIgYs zy1yw;7s0Mi_f5YpPh_Ph3B(e|xf1rw2CMHdJ?UP5h^dk7 zM~v`74?t8#{4t%E)# z1t~UHt3~zF?0uzZzub~qjZ=?Z+SPPZz0doS(G`}HkX&;9>ZZPIY15k>5I%g z7?`t*3xQSxe=?^_FOAw>Ee=$G|l=7eAqYIog=ZKx%AOG=%vswb$MCVjA# z5dmaGyflTRCxQ&6PtEl|R<^(8m|*j|VZlYn))Y8yL6VeTXPNDoK$7VV z#9exR!X5L?JQ-27378~ux8*?%*$=xFMA)NXAH#7=BCn4^BJ)>4C5sA0XtTa%<(sNkp1J z)X6~f+Z_qutn-3 z*adNr!&;Fnx(@;Q~0fgu=DGaLEsdEc|&s?rkomoutmgeI5Ulrof;pjLW9J4xeH0)l%Qedxz@>;9u+e-(VM^6 z$>hUNM``v2=G7xTN@$Pv`QZ6N!evokBJ{^$UQ+dKD_+uG9%3soyN*uSk#K7bgpbcr z9tFBW!Fg8N;&x&o?4ry_U(X^nw~4$YBpl{5l2@E4rTpNaD_1qx8ih=enm?2SV;y%o zKVtdFzHAvsrjD6(l$NKsntRSyv!92cU%_7~u0>Ztkp)|KfZtNc8GMVrY~e`9pbJO> zKY(5XN>;gF%F(Z2;2nD4PgmP-m;+Jf6+j;%2dSIToep6nVjzR4!$@)EVuYotRL1-+ z6-xHEv@YW&zv5D0jw~D#@@;xwa%;FFeH|O~@hp3`HKOE7=))G=5sV)lUT#f#9Og-L z)F=^&m}A=h76N46hf6Gi(X*Z*%vrPsF9d|F-^iNzI?i?IW}oa#!I4BU(gLehp_Y#E z{UY(_{dc5QeQsa>N?~xR=bTj=foGV&py{{#?=6k>preU?PezTzI^f@?@!@*B*MR#r zSuW`{tjC7mbkQgauB+5%#`VkkvP;hlD*5+>SN-v+FPz>&I@ONQn}(#WzV7?qRH{EF zbyxbMqbuu1NyyX_UdfbZBPFb-c6RUWOxlo%3uuw!p+EKbjCU5yfm=HeOK%0{{2My! zIUOo@Uo1y1ezC2v&SBly=-Y7RXV){%59rnMFSXDom>Z@{Q<4C(uhuyvo&W|F^jE8S zUt+I-PkhIC{n0a^cLaIx<(UV_Y>xglEJ5XnIUBQJ?ISSIo+5P9%I}mo| zNeEmV5=yU;%e-u*@jD@3a-t0Jc*#agGf3Jka(Sn9RO`ww5D_t;_pV5+pwP^E#7S+j zm0BV^!Nf0rY>EK4Gk;no9R?@XB8x6ylF4^H7rEF9&&ct%&k^xHlaQn4Uh`pg5^U{M zgcwkGmcwK(WwEe_VAqyQRmtoVT4RwU!v~Zxn5-p}a`BGd4T^-`w<+;85-_`0ON$I5 zdyKd)Wg*~pkl%3{-{fHzbmhL>4sq!RFXz+Ao(-_;5)`BA-DQuNEOax(SYCe!uZK9T zLQk_ySViBa60L*eq>Zh`9!IwVt`hG0fPY_COy?z9ZPMD&m*lTQeI?{HDGD*!p_;jp9zKdNiY_vOvmFp7GgKUSHx#uf_ zI+vqu9(CD279q=?!jmXWg}`03FLf+r{Ek4;dx~6|j*wRYx7i-=xBFtcyJFOpQ3RS&2>H{6uzZvGUxg|TJIas~cX#`xPMv+O%XQ;F?`Z?@{ z_2(gR4)9t1c4}s$ANL_=E>r%PTRu6M;xpxuHie^?SZo_QyL&L@C_fkto2Gw~C_Q3} zkZrf$KZWx9Xq%L_Cl^AXd3M_ht9X#_l00Jx{-21)&ihmO1By}$j*j?&UHbzDca=qf zU%N=Bzj1NRF}qGA9UkmYG>4>nWH~@#98!K^G4hF zjg#lCW)IFL-Ne!TZDitLm2gT=@*kiy`zT>IbsdrZ2OaOEX}#eDEJxsQa>`!$eCjpQ za%Q@hU6rO7(RPPql0T3!UjAEAV%c*1(N{2{^dUq9g4LEDvJ=d0g*pcfoIzcxhFga; zJ3Vy&j0fzMSvwGC>pnB(=tt+$(K~$ICMdcF2S&-RpbX2KV!Kbi6)K^mKT7Y5bTlBd z>?)$oUDJd;hPZUt(MOZ%Q1tak;oj(z{Pdu!U?q$HqRbDr`jJJ0*=5vcIdFT_{X@X? zS!JcnbO>}N;(yBX3t`^mm4v+do>9uSK7x>V6vh2dT7GwXRIm@RB0`M!cw6aQA5>y{ zBYa&Vg!+>!9k2Jf>Rj0D`k-h>StVQ;5y;Jfka00(a08JaF*D2&a(@JAIX-V_Uf+4I zVX1f82aIe!y)p-^;?VMU#1CfoU$z@uWx1`+fBL|@-9bm7R|eN@Mc$@g!?uRk z^|^pnS3PRTl&noP+kwuqeZE4XQnWQSFt_U>Je$y^K3~$q2&(4}_Rf%@!-nf26^)-T zagxcd8&))=RO8#2=C~c36zguZUt{U3hhrA%9?eo)wvwzj^KqT;F1aX+R$NpRjp4K1 z{l zOLIOM&?+rIui+NknpaGH~5P zpLUSnZ>!wW^QFLrQyg7A5G);IRw(&JjJ+G9y}2q4N~67^QerA zCp3l+(9iAGlU=GWVI{_%p!pu!-yqxAM2%#-*ADo0js-fa_{IR&H~wE!dDKG=?ySol zR}24YY+cKe#pzJw zxabOodq>JnvWO^ai-+0%UA)gm!tu^4TijQN%mXWYv66=%vj~y-#6uuQEQaP1R=@-G zA5m@3F9E#cwQY9i6}KR;-*#p;8TH2=knP~-2Y}2ac5E6sC#fhzMiIt-(7L~k`yHz*#I8Fwz1`G)<1X3g%x2lvhey1A%2tQLMSGQ;6Ds5)N<#zjt#fHM z!8Yo)8+Cy`#+%kr>q@n8=G7Y>BNawSjc$(KwtXEEp^04vIxyF&IJQul6iP^<7Q!_; zwwrkponf~~n$nD4r+Cir%y)NCwM*SI!;b*B}ewIE;%6{LXBo+QB zr0!$vcB(?WCEpD#3u`pz2hmIAm~S@CVXKBqO@M3M4u=!8-KCLKdW-e8xc^MBKsd;~ z(mB*}C4C45ncW6Dl69naVa~hqUh^Hw$W!94dc6`u#jStj=s64EBy$A=%uw&&0I+4=n@3;E;r`RGp;k(05@IAw3?;57I9tF2jL!JV!EmM%s$scb$}4@EM}m zKLbXN^Vy);2|`BQasCYx$4Ipik~lSukd2UxPvRywY~4mwQyP`mx&_-PcdB1$oLANH zD3ct?h+H+nZcLI56*m9#wQz#e3GE!B*+YaZk_jX8$%xssI!;N!Tw%+|Bi1Wmp4s#Y zS~^epz4XjTY0fgpTn2ecsQ*WbDUYv)2#Fl-&>XDwUo4})UV=Z0PUU0b(YK62=V8N)6`fM)YY z_bkXI)j8ZJENU~q2zKVcqIX5LcZtyQRv5In=enUBte$)^v3mI#t~}kMlZ_m_t&L`z z=0vCYUp(FcPPd3`6UjtxSm8XF(E8|>QSE#(K9y>zb#tn=WMXptFH`F+<`AFr`uyyg zQ*1^KJwte<5M5Ooh{30zsQ}?Y9lv-(A`(0%yv1TR0L9gcB9ph9tf&`q+n`kgf*w+E zBXwx2ua$p~iRK{@%E&^P6iq#AxA_ZVrXrXVJqp&D4^VmBkx0UANxuQ9t+U{$_h!-x z&#sStNpDH`0a=olw2!d7rQBD!6v4){;R={x)hrFZ2!V55o^elDa*9&k{rj(c+fOCv0p3J3K`P>c< z)O^WyNN6eAz!$@$71VCY9Q4uBCRz`7_7v0pkfYEe5Imx_c5-cjUMooKeH^Tp@N*@^ znqEWLB|Pb8{T$4;u@48mRt-3!k-Q*2!b+wu9XmV(0pdghd!W6HZu4drotL*Hb9y3xVKSVy{Kk)TSjb&;zDbc8M-)-;(J$vvPX4M| z5(kA0x}z-;xNq{Ev-s*P^u&BMg4r!{-Y_5f%@O^*5uBu#p8|qbzv@=37kdDS%2n$U}K;2rcWlC=@)H0}lR;XoWh6NMM&4Ap1*O`=- zSkVY`Gn&&}G=9F*I&gBD*WO|vpHOkcZs;ny5(+sYxQ7Noen_-9O1%=fncD(hLu+caOn_bl7$ulb4LfbZ|@uzvb{S=Lg?H`*;%G# z2!}+gNB#3Uh)+Mn<_c1O^2;K#0Az2d(>}^t+_Znpx~p@3cEScnUI6pi_U@KjA#(z5 z6~sK1#?2_YRsH5EYuMtS&D`p{45zK`+F+iVylgOmjY2sa%xdEkSu^}Wo5&i09HpA_ zy%4VaA2R^nY;^C3%2M55l<;WPLDxa5<0lWu1N6K53+&R*_ut07{j=-8M^Z9?C$ zrO^L8Q~71(Fsqd;X)`Nuh^7Sm(XlR9l1WuZ^XJo#{fOG)2Dj37{aJKNL084$V%EtO zAk3rAsnDZRm^1DJ;kHJ4HVLdEh|fk)e2_>*kR9@c6`%+%g5I*&DA6MXeH_<0%o#Vb zUE0{PUYa0t*(PQV?2mxC&O~Z)jUvMIfFy_@IUZa4q5H_8O)vrTveRLGY$ok{kIaY5 zhoH3M3Isk1qA+#j)6TL>GsTs9f!R;1ElOplI!zC=k#4Z2U@aSkF|_;S_+jwi%NSpT zDin#=d`K3-ye?nuXe(XY_CPo&m-9rp;)JYaic9H-Q!EnKitIuYA)~fZnTnDJ5INL_ z{OOX(OCL=MUV|8dmJ36{>d#Wwt?kb#)Ft?KXN~~lb*b~tJ`v=mA7K`}|E1{UGV7q{ ztbEIMDO0XYJR1Q2C9%{{mv;(~?FjRYY%nW9^BCicO>Hpii$J|Ay`m8_P~)|ZPKNdM zuj{VrN(L&MdV5YIQMr0I>J{s~sj={don$r8yHqMnhuF{KKjyTUSWZm9DAMM4F2UVf z+sXSGvmuWF9UVgz2*@kBi9bZ!WtcxCfU>4bsGUqE9{ztF_|sf#_8D4Eb~8^gV3#`e z{s;6~emzOEOr}0q0^28k$}errdcFkFbJAhAmDu7*TkEIjidjkVcO@4T16El`=mYeC z_CKAXC3EDGO&#`@fGdV5APjZ$07`WQ6ZTCVxQhB|7rWj=fnQJAfZd?zL=D zk&{2jFMXm-d$|tHBSC0$LU+-1j-FW@yRon5eHl50TkSybBseSSX-Y6DsW zg5J@?WX!ex9?(b!^+B(rFGP5@JZ2XTbEQRz=M##~@txvF@AOyh7_FxrqTdZA0t})Tw=bRqbL9vu3$l zK|!A?g-Y7va{CTvl}CGs5lLAc%n19CJ|U1@%s@7fx{L`f%lHA!@>c>0WUc7~=n7qB z*?|ro8jkp18MO5_q75%v)_sQkMmzDS#hPuNz59uWtN3Z7jawZwtFCm^OwEPirjAEwq0SrsNpxW)dqZAnKiDWg8rcF9TOa!}2 zA%-B~y8So8j*Lut{pDvOI?32w#%N74T4It{9el&UL2vBkjSXt75pSL1#l%t;$@Q1> zo38YhyRl;J*76?|j4_2;njv?nxfT$>uhx{gjMBtVnDJ})%-m+lzdPkAtNKVc^bH>! zFiLNgrfh{fE_?s(^Ft^fLGAjz_mn3`F{g%LX|eMi_l3jp5VIUEGSZAuqz#)3PucW$ zaQO#yNF;teEX9^FG;s;#gXL9j6G#>_MeuD!1eX(MfeFBNn+nvfFJ{R|JH4NnM56gslBdn`MN!N76RV z2j9Ov;22B3im<-aePTLM-)Pt8t9_HjE4M}Ch;;u*nOk(As9|c@xhwF!eTQWk@i*uJ1!;qSgf2;Mw9q!)VKfL_La+iEZ3%(A)$>UuyiH z@x`J~)n7PulfI_ZrXw%1R_RWc!?m%O$(*V>WjkmT(IO6~B}5!2*wV_g$A;<^I0*k~ZQdu|G3c zGxCXyjQDB~-j8_Exn0!Jc(jdxSUy@xgzy94w&O6IpovWob)k)zPU{D}?p-K#N!SgK zvLQvbd;lg4bB^Hdc*rWqEm=!x^hjwCCzBQ{q#2|PU1Hb6xzBs=;JszS6@~QR2%GR; zd(~vb_Zxb=>~mC!ZcP-F`hevXg3mflT*Thbk|a1a_TB_C!pr^VmOba(fGUMDt?yh$ zeQ_|x553dUV$P3uU@I+K+gyhKpYy;|G5!3Ps_8+^N?J)Vb~m14rMkOLpwh%0BZIW> zeVTZ+v2bQoYenZ){TBLVt&e^KLd|Km0lfjC!Le88iEpB%AzAMlBdlsqTlX=i8xd}k3?RPr z0^!5*mY4x+fno`qqSsK{Cyu4(y2kV5m3`)zi|HbA*wX+oi-LUR!*38rTuyovP-cE0 zFGdxJS+F9TJ|XF)2y1n}uN@JR&S6`p(a}|9>B31YBx4038{WnWsC3E}k z7q)|3t30_2^W|ht3`FCZ1*Zr@M5V|noQ@9SRWe4gLYZW_18Lss!hHDXv;`kcI|gxQ zI`VN4fq}!nuI;o&hHj;2RG1=__aBA{lb*H8lcj+OG=-lwr+@d)wY?KGB%YSJH`HG;Qz z!Qw+gvO*{_G8>PBCKrUmrc`=HWLkB0RK1_s$n0&Xw5u;z^?^hTdNR1wi^y$I(|{n( zT>+<=F5K%n(Oe_mnA{J-iMc!h9)2yynee`1J&%p5uS+q$N*<%? zH_!ZnMV}#K_G9W?e{K|=x!39yh0X6HrfOe z^Lgrhtye_D1Ki;|R5y6V`|z7A+t2wVlumKF?Pjij8y*AD9j~F;R|ivV^arZ-C)S?? zbt!k7a8DDOH0q^q@_v&QbsA(NO&XAdTVPxlr(0}BcPot$O>bVRYZ`1}OcD(CHm0{W z4YoD)H!U?+qJx)l8=-CrH1bs@tKRgvSa0$Jn^<8m4t1BNN2Io4L(F$Pi2Ew{a;Do8YBhl-6DQsmX?HoQIvK=;d z5;@Nz%XrBj(h!Yh_4+G;`yXRd*~$yRSj4!LBS-q-+=qMln(7_q5!6ExqxyZGx$^i? zs9XXzSGiYopO-f0oZ~3r2KT5x_LWcCA!qtiG&rTQS2HHeGNVJ3npz(fff;Z(QWWte?#bK!(cYr(wA*9Y~q&rQcVaVI8;9EM5BP#osUi#?hfG zyz}9KVkKl_=l3LtetFD=rFTfh3f_!dMl0DWNa2(xx5KAV=-|(tVRh9+EY|DiXaIbA z*yR#N`)EI*=yNlA5tZb)o-3iYTHbk|-AnWAmEMmiLa%Y-))ascx|bcUmGnL zVd8b9e+ph|3NxnhrDc2{^p?qMYw!V$J;@lYzPY2Z7XJ%2r~d5FFE?=-7&O!Sxtm<9 zoeg>gqw}F2v2G?bEE&a3iGcYpU|S9>K7$I9YK}(^jy_(5h5+fZA@`l4=Rq~wy9WEw z(dz_NO0^T2o^ofK=s=L!TqTc=0fs9v(ptW!eDRkaKT z>2C!(tKG7AJ{z9Shh+Ifg_wtPi`ih-qVO3wZR!+E%i67Kz6z#iY=K2{Ae6ce^TFRd z<{CV+3~#+B1zvq}tLEG+g~XL%e+Md0^v7WE>m^r=Q#@gtFj zoP@;b+a%JYzh`$o{7cFQn($dxvL{vWP#!{DcKO4_UxQ$?RC?k!tCI0PZnoigB>ur~ z4=~Dt=?RkpthWv#qqR>UM@Q$AaL=pw=lOD>4lAu z{cEK!_@@H1j?=i@n-!0t7K7i>2)d`&aOJz-p>Ns>e<#{3GR*GO7 zmVV7-y7ub3==3mr@vtmIp^l8O;Rz;yXfO-=Yg5N!y#=FJtT_lbq%YbBKaiLlVS-o407i%#@-#QUd^(xm}xJFm1-eUaMiuwz<;i`QbO5I7PRvoKziL2GY z?$X=dZ>BWp-L{bmAU3U6{)J1OAaqgYBk-SC{#*1UGIzuR`ttYlC!PZJSzHq7)>k}o zW-zh`Q_#OF2<7$#v(56M$M|^Xle^&hz-%~7-26!a_gU)*E&~nhT4Q2TSFJH|6Sbyy z5w${<`JJz1(GzeBcB}5VUCH0-qER0p)62`QRxeti~A6EuWm(26Ls}Ro2o3E1M6b z`BOHxCTA4t~FQLX9I#KtGf`{peiz28q2Sv_oHDnd>mSH*Y>}J8Gk2S1?^ogR$-pf8`ODpf)D~7 z4`jfq_i_iVK|OF7wdLJ;U6~v1dE%xl;tVM6JjWOBx{tCc?fawvi~rch(CEzm*XDBl zj`U|XmfvP78?>#P*?KNGy189#a?s82x5+kGt=lujN_6;9di6#%hR$`tnZ`m*`t;SI z%j#h(==Z5#ZW>NaEki`Fj=H-Wlyn5Nvece(=24nC6yh+2qiUV7mAVLt~ zBCA*B`JtsYs7A}ON|*3-mepBm6?GJWk`N;aJc>a6tZP8em%I<@PJi?i`pG|<-Q3(v zM2W=dTKVn`gLQ7#OdDxb%;Uzxg18x8U8XSWU+D?uIN%T!%Iv|kL zJwhk9z6IYTXtD?KM2ZLt>9+$Jp*%_QCXerm9)mM3p1-Mcw=ygn&4I9#Z`w>R+vt(I?D)G zR0_9fHchL^zBrdHp1JaOl#(M?#j^AXQ8?>|wi}Ka8sT z_6HqbZL2w{D}M1y5mNuzNnQ6l8{(#!q8obanQUjWqDHOwhI*sbMu{d=t9oq()c)vB z>fw}T5hX03g2*khlWFD=B=O}5y$NG%{p<(QS#k-6F3~q=)uC)0?j;97v2)NmU*O8Q zE4ffUII?ZCWZw2A+@!cW-5!1GNGG<<^%ZkmmvgBT?{-`)?T#2AYrf}iTSR^(Z=dt~ zr*t?T>mO`@>UJG&n?-kGY5zed84i{+9HZqcQOZufuBG3C}+DYdu*v)bDrWPSj@NgU?;S)Z; zW6jyt-s13EpoRA9B#+Dx$AcpCB;!cmoQZUB3W!UFj>kyCn;ehdasfZs;iF$v8BUV* zoabY1_I|M5=^Uf9Mkm?6n@H+sSiN#UoPzYF*LB#`dYW=+uXHz#4&q9c8^0t0jCJ*M zj!<+enXTeOzmTBg9)EW;9D6tS9+Z&+Ik(gK22S1AEr+B)7{TRoeoEedLcCEj{iAGV zdcUwCZ*8QiVd#~K#wN(3aj20hZTBYYO;*(D+MCz5`N!G{J$YTPXmnd@gjin^>s=WL z$aP3F6lW4{PR?A^@gPWg^{r^P0|||{bGYbsUsMUhv9YIpcs_i zJTiLmD~M=X=P9w2(k!8bND3F&5iZ-oJwG1ysz;Iv$1J*dBbw?ZcX?rf3;{h?&}Z53 zez;#e*yYe!h$U?70%);jEtcF?aEL&fwDXz` z2L|rRt!`tva%>2ho+2H8HW*}cZ-Lb%+D&^?MSGN-U4Oe`dN2GFNRI~ zp|X96;{^5iGni&C{C&&CT~g-3v)y2I7$q&-5z7Fq&(wxbJtv~xJkMz$#HdHsEcY}WAW zP;O$U2xQ z7tzXK^B7p`AjGIGg96B9Oz-m9X?EqP2OlYKx%&TSaKx<4DA8dFAJg^30$~0>d+z~V zS8=rse{c&v0-Hd<#EDGznZ9YP zW&Q)~1jWII3P6to7~0$S2Yi}AxbsZ^gY%+3u?2GauABkzRPke_FV`W5HJ&`O8J;;| zyDRVC0#Uw-u^+9x71;x1o&Q4ziljkzoj!`gF%K)vtb#Jo`-G?B<%F;9SbMJr;Wz%e z?SEw(puYc(ChGs00S`evcl2+$DiE&KFJG75zOvmCwZmF>?aFqu^wuJGUF!NZ4${{x zZa0#eWj6q&HWe_v$6PIncV8*ljE5t*>7 z?R63Z-hQUj6%#A3sEdcJYHZ-q$ zg3&pIr57H69=QDqm88@XX)*_0AcQmJ3FDPGXM#`KK05Z|`oq!^@$#RiK3%c}@DvyK z{JF1r>koG+>Ea(CbkYh$5}M{igfs-Y{UWq2dNAyJ?(~t3PndkrxH*HHJGLAwA;v&5 zKi+K4{@54+#zGut4{;TY5oMBzU>?(*17x9SDOn&gh#n8S9oe%1p6BwD(%-|aWSCSs z^v}{qChv0s$XC9-Fpi4`5q%XeU4K|)#3pb@Ae9{e?%#mygMyDxj2KMY> zB$|; zH@koe;vTi^c26Z&+_-hT?lS5o6>ccHA>%rWm7A8|v``B-)HhV1&u^J>b|R`agl>kg zK6z*rt1INVX9FALcNbqHo&FjL-6^ZU!g+{O6LAEa{;c9>zw5oKN;N81Nvs|!hTfkP zzsbwwnk0y+$%nbculm29gskFJ&E8UmpY#yy4(b$Z_Wn6po5(?2A4eioi1$NerUKaj z%!7b)S>UV?;aI>Mo1kBm%uzU935;&b{k)n_J{gB4}IQHsQ0K_|Bqeo3y!} z*hSWCW#1rm{<-g-gU?=p-poWs+vpP3cpl$z3vPy})&ae;_2E0Fwte|`A+|H-|4-_M zA7wLxj@${r(G94a0qwB6biRHN-b(bs%M)nV;rcBqSvqz%n7pemyW@}dT{3U{ZEy0M zZE|p@1Z{Z@OyX4_gQ93K*97cd-5U;SJfPK^BA+q+8Xx~dz+Vdv9{ZfD4h9aGAF&7q z6Tmm@;W3n;?G*>?$G{2!eSY@aqmkH?3z`KxfZX+mK2U}ZsmxKn%iZQ^G?IMR!O2SC z!1d&3-VRk_ZJe0j$V#Cvg$MGlI2flu0VDs6v!6V70C{Ln0`v=p<}K5&!wqCjJ1F`D zKeGfv4)&V?>!&jfpW`w`FI<6qO=k46T;73PuEc+jzr$=@hG{hO2Y z?aQCqrUQKB&?xlz|LtrWNUVac2nB~6+W^1+38$Ym!OITg-slEJm;<7+MQAJJCIbNq zz?+wDX|c8;O&DR+{_Td_eB_PBsW%|uZ$ORz^23il{?)I4^V?58{p|DK{r(UC^Ti+k z_sc*1`7eL{>TiGl`XB%N*Egn6jbjchAoz$Cyf3u@wh;JW_jiJE7dNl9{7{uQN_(Jp z`=kMCsR{Uhd_P2ZhiD=pli&?3;ZT$fyn$K$0a3-g5{?q>LWomP^42D zL`kzj?tXKfzK7}*2(eM#g*TvsNY?A~Zzbvd$ipXr?`Nr)@a}O8hS^J{0;Ou>^96@s zRQUV-@Ac4b)JJ@YkHzl5<<%#R-_)jThL7n1zxMs?cZUOV!JP!Q3g007fX)6_-4WeS zIrl9+!uRwCi7(>t8*Bg<`X|9NLZ0q!CpA`yOfa|AaEhlPcHy0s>{Btqe5Cj{@ZvV& zp14?x&ZVQtseN*U0m)*-MwqfE^M^6SJWBPIJT#xIsPMIC*;S^VH(o zuxy781_`1DvA!L_u5=?XA}NzqVELf!ZzrKfwf?q5RLrg6a7rBDctt_aA1AuNdBksD zAnFC8zQ(a-xK)k0Bmaeav4(q(`1XM#_y79PMu1?Ur&{>C(Sy$)aR+d(3F|?P<!EM`mfY|UqE>Q z7wQ{L+O7t_*?ku6Y&dqy^>4W%T;;ac-A?ho&6e#uR^Jw-gYMsDH&+8xCjni4>kHkD z%iQ%jH(B;Ji?)`%n;b5;6TAOrMe07@fuXfV+#_g?_CkcF7(3gl}5A3#J4*|g#jdQIYoq!&_ z*Syd_j~48yye+5{v_X5*8djA8I%Naf@|FTM;Yp zDVWzeQGo{Fly`10KOh?aAoZ%82GY z_YdjfzyW4houTjLB1b=jTKj!I6@d5k(Mi1EZ+)LW0jFc{>oN9V03-@#Xf^zNPsH^J z%Lwoxu_`Jf&a?t+1{r_w!yhG(s)s#qcYR`ok65usp=ZwAI}KmEVXjYnIRN0DhS$rs zU#~xH&m#04&JcLc>*b{1w(PafgUE{oUqUoXIC=l==vBK7-Rp z4bX2Vb{ev`nn(BSpa!`*d2d-Ve0#-jud!##K7qQG+Qwt%t<)1Jo{UVnvPz!M_;~`I z$Twc`-zd>YH01?aS3{`6!B)L5qlfyE!%eDwsfpGnwL_%2)ehg!iM2})-q+X68L zPE*+`aCU74h+-Z&&(G>b@}u^Oh1}N z*|m>5KY$%usZ+CI+;kLp-R=J)soMg8@!c335cxvdXBGJ{(;5&EFX{4Q5XHmYA>i4P z`*KZGt^e?62i}KYEeUD`_V|beQeyZCJn@a;7iG$Ilb$}1Auv2X^l`=KPasX0>po(u z{{ZaoO<3*wK997U!A+x49k;Vn?bQjt+nBzb{~ZSKcd5GLNd8Wl-#XB*RJiGQr_FXa z3}2n>yNQb3@~>(McQ6UrL=(U2YWeT>@g3XUN_B{xyLsGoLsiZw4C1Ek%m$psy?LV7 zYIv9#)OYtefE6CJ2BvL?0ltE8Y>B`EIJ#cK^3_O|aC1`k-vet9>7S76#95QoaZp_j z5GNF~CA@x>Cn|a{c?x>yew1bf1H!ae!uv8a&<&%$&p*B~f8QwFOTOEDM2EQ?u$t~+gapa9U{B(XJ_Wz{9|vm^jfCu{?)se?8uMpEZE`!X~bCGJhUz# zDu~rOg}lW0#)BG#^%n?{J`ZolT}`T60ZU}XfR-E>itQqD@^*)J9QPJ;Z-)_8DVGN! zb`^xlkB)%{Nr>2}K;j z{~}^Vt4hV;DulKHu6QgJ0zt`L+4d*d$LA1psP#;LWsvE*I|Ly6t!QKey#Kd(N05#? z*^#H}2vs>Kq(!CTx%-T|=tnlXI*|q*y!{*=;{m2$c=F*=G4ik#!ake`@8YMQQ^X?s zCnV7N6Mg#OK zZp=Hw9K5)_tL@acYK^X{fxf-j_8OZM?YXt;+Mz0I6b@K_JmG*vHsC=(UGW*pBos-Y z2rT;jC<({Y6pmKoklh%uBpk5temPeIS27rkCHqC_iVu)yqI|$&5XgEpQIrYp%%P}U z#v3k{ z8F2 z-OqCdgCufa9aVF{1N@NZgL}heY!MOIV-W27nOc(T3JQ;k>nP;uhek-bEAEek%E(kc zao+_DO9qulGKO#`NK+-%gTZ&uanm@6uat~J_!H2SQv6XDrK^K(L`H~Z=-qUJ-tdAEEq@?g zfA`ql*ki!MXy3j3WUFSz5#+!1O)<7V1;+S|1R1Fy|L-beGuWmr1}uJ+=LPnnk0kvd z?l1UT@q&K-IlShLlt*0Q^;Zu-ek>UgGceqNZ5_z`h0Dipt?=nP%>Fi=2%b$)+N;mH z!|ROHK9EkZum7O=PXlt~#}0p*z67uC{GN9@4+@ZYlrg>!fA|5Bi)a}hJmIyQ>DTdK z(HT@eQzU{y{>g(ghbnSGsZ`46#&Es5}H5CCsaK0Nx* zWi#aWOaVSY)sUf^(rU|+eY=VIRWHk1 zu6T2`&`r9zNzqloe_fSp$ZL|D-sqg`J@Yn>Z`*2e>5?rWed7pUEC!jh$`S6L!FT|COAbXz$9E-**GE0?m*kpvFE~3ZLs;5v8h`x3a z#bRMpvwJh4iBqO}u?^tQzm|9pwgAstM@sjl9DWq5;mkFH2@TL5=fi8cRB{Gr2qXGl zBU~tq#>_h&7Osx+8|C*dIscIW-)elLDBsMvUXJfTUnknPB>Q&z@0*D6YkZnEB)TyW z_Ko`BtBc$A838v&H)QN;PW5d_Q=6ChH+Fer&X%R&^<`W1R%(0DmfiBN)!1yWqpp-r zzjI|D&|!7t0Nui)d$ul@n-LYfYCZ+8_V0l=rm_saQ~w0?0;Q^fWMWd+N4|XGb4|JD2wmEbxaB7=ra1_(`A_>S>G? zf5_9wonAnb40lJ*5Y-@w{Uf)R5BEO8ZWxU3u|8Fv8sek&a()BuPQ@M@NA7_!UdTy+ zU4q`RSNcJIO$;Mc4VUZo&p8(WUzze{)7xuUMkzU zup_mN57!af*kUBUl^@$GT+fs3^cIJ1kmEZ!D>%M4{InN7!~F!g>*3xN;Poi-Gs7Pw z-FM9*#nnLeTWbWV1rBfLpvaHu0D&V29R`ldnCAyi!y6a+g%mC#YK9&NV}c*NaNr^c z!j3<(QIDmU<3L6TNY`KV1|z-@Y3iDG8!e)NE#7_Tk};zzbGgdNDlygoKM7Q~wa+#ZeRonj?PreN;3I23IBF&?voAk3r}IO_n` zMxZs9HGaj^f8f0nKiLxp?eAVF$MzqmLqb8(Gf8WpSeXa$T!O(27hXV3gJco(JH58Q zl3wrxd?O>`V+%43g1~2P=)Sxo8UNxD`^`kY@0(?ThSw1K_JzG{!*kWYhh{*=*o+qr z+#y*)3V(w9cuy2IsTa9OSr@*vK@J;V=0L{3e4T)idD9;m^)ef5|Cc>l3rb&if&%Ul zH@+|Rg^^Cp;C=%RAgbU!Bz3Wx#cU?6R!7ZSO9XzFYRD!gpJSQZ*Wq}rdk~cGYWu09}df)&(#Q*QD0(W0k0Bw8hHe%Z+?Ow8B%yg6D z&7#f3ZYQSA3Y&@DYVLTp`YxNRNI3-O}3@ zh^t6L^nZ7i*jzTY*auVFIJk|tk$>BAu3_f&$xVLVz@h6iHo3Ho0XHMIm}5-sbhn=a ze*PTP=Y&HoajLi%q$>J8KnB2tfAHu9BD@mVe^|L+#yT9xcn9AT4&2Ct_9k86+T?DwJ)($#w2McfC<8a`_JLXiL3((tufq&r&n{JSH0)p#9xp@-1-|ntU znV`|c@@XVbo`u^*fmiA{2LiB%=nxOkxHr`g+TpT$3ZM-^?qmCEXzOQSD}UL8NKKJTF|F+`%^r4rRvuQtEjhfjzD%ZlXuxx0sF%%sbHDc zcu?ITbOL)$VzprIarQtrr{M*oD#@n^9QOr=95;}e0^Fg!fbAeSxdP1K!TeTrH@k?M zZQ0w1ZBp@yA?Gy}u1Rhe^f&cD_?A}RQf%Cn?}Ed=t>q>`2W{zY+)JxB*>U_)cY*r!b8qqn?ow-8AhN9``cN2DWf!c6ajd;a+7Pq98j{Rd=*9jJXak3(Qd z3{>=ZQ z^KkKz974=J1M($fP?ZEfC;>x$-SK$(4?A8WlfeS~dKfZ(F9jbGk7KKcpMHp2x=~lh zwM5q+!&(^RESGE*M-U`rQlv29h!O92svDRZ5{^8DrQbc=0=f+&`*ElN8U>XsQT~tq zJrB+Lf?wWyp94JPSw6SIebnypznwAg*U>SWu!QW?jsf`o0b{J~-O zH`AW~m6d<<_uq9{y?KqBmtO1Ra7#76Yo#6PU&%5;=k9p^n**w80R1^{;m@{JYzJc7 zTDG0owvO#WT)(F6MsKclyC$~Qyv^0_a$;+l`)@7uJFwxptnG}rp4x>MJ7n+1j&1q7 zvgF!=E%sb<v@yCEF zRq607@}qzKD0u`JbHCmb^ZSGXSULt;i1_{RU{{Yl{#*jY)(l2RNyUMUjv2cp@a&|) z<~Up$vwoQK%QBI%<9*=}ZfN?HaqlqkL=(bvgYpMQ;iWrrR~_FUIfG9A)E?rikiU{t zzvj0NAYO~bkx6e*GXfX)v@hP^=Xe0;a%f2S_DC>1$xgY0Z$vE+EGBUs;KugNiY*#DC@sqeykq+&-EN0VnXB`B z2N7}orOdZgxk>p=imu+XTw|%cUCnx}I`!7mD=d@8gg|@?K*|1CJ-2t=P!bH z#n=$i^j9j5fkCc|WJ5?XEg)iW-TO(z07mMPklLV9x`X1>DiS)C|H|1cPk_hqwtJz0m-% zj4J7Li5#nT1)PCZP*(*44WGh=ZG4{(^5=Kk_`(i_ag3t&(CcRG8@$Z}#LH^rUV{+c zsD$Bg{m94y@J7Sq)EqYT%QqU~l9(i;j7^$Jv=nfJ3nX%<0B%WE4j)mv-xeYfzE;TP z&S1XS;473zJJ@@qmEpQlWzWuh@`e|@x_l{?M&Ok?4D}SpK%+9!PJS3;vH47h#WO?%2KOh%0hx~>jr8--Q|l5O)zC8ys;^;4ZV0{je#*nteXa17GSNF> z?1Xod0A)lj>>obnR(NHZwv)tr(RqB@ersoS2~ok{X9@AYEdnP)B_@QE#7j<``9s>i zXe?e*Cv-p~aNC299Ozaa58@>r;@hGjzg&PD?{mljZzUFe2N8mEED(o7_gj&F8${+6 zuaFlw<8tJ-#|D@@MKHz56p26gRxax(XqI5yo0%PMM6W#IO`h=?#{N!np8x#ppUF^# zDJh<_0zTqXPoDiV{9$~t)T_M!bcl7oM$^A=CCbj=dF`w0$~YS%Exk4 zkY%K!@eJ@ExZ7Vd$-cur{tCAE&W(4@-EK{|Yn@#RcFEpUSZ$mPcCJKQ?6PSZ1FB&_ zpTF6j>AGFOEs@u44tCfCY|q-Z^luT@uJzl~*R1@zB{%m0n~DDv4&|0UeYZ;2vE&N5 zoeeuvTl}~#wVfrK)NTyfmcPlAZ3%kd8e5ijRMmpgr_q-rS8)7Wj z2|ST&4Um^xu@I6y=?U~i;wu5}P+aDK(+8~x<3_}uzyEX~*qi(4s5cli+&_reVOAvn z7{XtA`4!gui?&c0U|HR&`x4fG{sO5#eCj-y{^P!o zc;I))`Bk2>nvVT}Gg8+8B6a46jQJ5Yu^W^5HJ^xkTeR~)v))6=XXSnjaLYEqM-~q?D7qkiVfryZP zz#jyFKzONh4Bh~RK+wl95Dp?B7J4Lzf{|ZiKrD!ZBR>Ho!n^rokOESnyM8*z0GS{Q zoPeP>b3iVv@C4QfCwr~{`!J!pW=)u%xdw1#N` zt)LCGgAQm2+y%}+RZlnQftV8KKp*IbzMkj7AQ%F}Fn+=)7z5+bKVSk(f{S1ZUQo?| zSuh8kwHF{X#1dErD_|9@fpu^Rm?CC~IbwlWB36hs^wYOR>=1jz0dYi}5NE^%aYft^ zcf@1nS!B(XOLNB4w*+5kVRw(Sw>cnRb&lWM=l|zs2OUGTA-Gw6>5#z zpth(TYL7agj;IsrjJlw%s2l2zdZ3=D7wV1rpuVUd>W>DXfoKqV3=Kv@&`>lCqUA?G zd!k4*3XMi%&{#AMjYkvEL^KIaMpMvKG!0EhGtf*l3q65mqd90Unuq421!y5!gchT4 z&xMwutwrn5Q)oTffHtD1(I&JRZ9!YnHnbh>Ks(Vc^bC3y?M8dh zUi2K=hxVfb=y`My9YTlE5p)zCL&ss*o(Xgky@*bs)94I3i_W3*=mNTkE}_fl3c8A} zq3h@+=xA+*nPV21C1!XyAW{25h4wxh6ggIj_m@DRnxnmyCyvqyo#(dym!4LDt z0mW$@3!e^!j7;f90*6kiEt)d2v@?5aEHN_JP9wtoA4og2|vQ02p|H9AmSJiOoR}j(BLzi zh#-y=kwg>`O~eqfL>v)MBoK*25|K=#z<`ZuL^_c{WD;4#2_l=wA##a4BA+NA3W*}3 zm?$Aii87*`s30ndlSCC!P1F#zL>+O8s3#hTM&dNlL^KmEL@Uuov=bdfC(%WmA(eqw+)PYe=6#4s^Jj1ptSIB|iPASQ{6#1t`2%n-B095GKU5R1eTu}rKG ztHc_yPFx~PIc6Mljs?e(W5uzCksoY1b{u<-1ILl$#Bt`ha9lZV9CwZf$CKm5@#gq& zd^vs`e@*}=kQ2l?#tG&?lxd~lPH?h0IhEv{A&T!6hx;Z_ZUd}mAAE%!)z&XzuD#ra04_8O|(pdYgxqor|0$&N63(v&vcHtaC1LOi44+99~&il2)WO zX+zqQcBDP&Ksu66q%-M4x{_|BJLy4sl3t`Y=|lRGexyGcKn9XQlLcfUSwt3- zC1fdCMwXKmWF>i$tRkz)8nTwGBVovQvVm+QPm@h#GucA6l5J!=*+F)aUE~?^EZI%= zkiFzNvXAU12gvi}AUQ-1lOyCPIYy3?7sv^662@tmBB#k2a+aJU=g9?fkz68|$rW;y zTqD=XOQb1fMwwF}?M?YmzLX#3 zPX$ncR1kHH3Z_D+P%4ZHry{81R3sHeMN=_UEEPw^QwdZel|&^|DG<~qjY_97s7xx0 zIzeSqIaDr{N99umR3TMF6;mZtDOE<5Qx#Mtb&{&0s;L^Pma3ypQT0><)kvMDny6-~ zg=(eRsCKG@>ZH1;Gt^nCo9dx@sdH2x)lUsj=cz$zh#ICws8MQ+8mBH$6VxPik(#2W zsTpdPnxp2a1!|F6qL!%@YL!}})~QRBDQ!lZ(-yQPZADwtHnc5mN88g5v?J|AJJT+- zEA2+R(;l=Z?L~XjKD00GNBh$ObRZo>AESfm5IU3&qr>S4`Zyg)N72!A3>{0y(eX6I z0-%%VWIBaTrPJtiI)l!nv*;6aHl0J~(s^_~T|gJoMRYM;LYLBIbU9r?SJEfxD!Q7k zp=;?n`V?JHH_(mrX}XDSrd#M%x{Yq9JLpcji#|i2rMu}Kx|cpj_tE|I0DYbwq=)EX zdW0UO$LMkT0zE-b(iiC|dYYc0XX!b5o?f6A=_PuZUZGd%HF}-CM4K{Zj5%Y$STa_O zHDklrGIoqTBru6g5|hlNFsV!$lg?x?nM@XQg2`rbm|P~0$!7|fLZ*l* zW=fb+ri>|vhV+%pNv4XaW@?yPrj9wq)H4lCBXgQ*Vw#y2rj==9+L;cflj&m4FlU)= zribZe&M|#VKQq9bX9k%eW|$daMwu~YoVmbEFq6zhW{R0+W|&!Kj+ti`m_=rZS!Pz4 zRc4J@XD%_OtQl+0TCkR^6>H7fu(qrnYtK5cj;s^w%(}3ytQ+gjda#}>1c+jNSYOtU z^=AXvKsJay#s;$?Y$zMXhO-guaW;~TVx!p@HkOTJ;yZ>USy}( zX?BL4W#`y=c7a`Fm)K=?gq5oJ-D7+FRnM&hwIDrGq{=DEba+zHaCZx%gy8Fa|^hI+#+r1liVt9HMfRa%dO*{;?{E;xQ*P?+$L@_w}sovZR56cJGh9UH~tU7sNZp3+9FJLV01la9#xOI4_bH#f#>}@M3v!ym(#$FOiqTOXj8UQh8~- zbY2E8lb6Li!OQ05@N#*1ynJ2(gwZeJ74u4XrMxm;Ij@3O$verb;#Ko%c(uGb-YH%^ zuYuRdJI!n2HS=0{t-Ll~JFkP+$?M{s;hp7m^Llu_ymP!hUO#Vucb+%M8{!S~MtGyV zG2S@u0&jvh$-Bs#;!X2rc(c4Y-aK!Cx5!)KE%R1*tGqSdI`0zClyAm2=UebC`Br>u zz75}&ZwGZN4tz(x6W^Kd!guAn@!k0zd{4d?-<$8l_vQQX{rLg>Kz-i1*M*eAj6Tg|?!f)lb@!Rr{uFC~y)u3tR-Q z0ylxXz(e3E@Dg|nd<4D%KY_m>KoBSh5*!l*3qk~;f-ph2AVP3l5GjZfL`FzMw!*C@2yX3rYl~f-*t5ph8e7 zI4P(SR10bZwSqdqDM7uULC`2TEoc%n3t9xNf;K_BphM6p=n|X}oE3BndIY_KbAmoW zzhFRcUN9&a5)2DQ1fzm6!MNapU_vk{xG0zsObccNvw}InykJ4FC|D9K3swZHf;GXq z;F7>pXeKllS_mzLRzho`jnGzTC$twj2pxq^LT90i&{gOrbQgLEJ%wIEZ=sLSSLi47 z7X}Ceg+an&!eC*DFjN>O3>QWSj|(G(QNn0pj4)OhCyW;+2or@#!en8JFjbf)Oc!Pd zGlf~g6T)m^jxbl4C(IWX2n&Tp!eU{GuvAzkEEiS?D}^V8Rl;gvjj&c&Cp;yr7d8kR zg{Os0!e(KMuvOS5Y!`M2JB3})L+h-tTi7G)6`m9J3HyZu(D8pzI3yevjtEDEW5RLa z1>uBnQg~4~C7c${2xoa8bAStUsmM%ZF0v3=imXJ|A{&vd z$WCN0au7L+oJ7vhi_KN!CUO^fh&)AJB5#q8$XDbi@)re&0!2ZhW1?VDh$vJPCJGlt zh>nXQMNy(?QH&^76eo%oC5RG5Nup#?iYQf-CQ27&h%!Z4q7$NQQI05Alqbp;6^IH& zMWSL+iKtXmCMp+Ih$=-VMOC6|QH`ipR3|zmsuwkg8bzl?O`>K|i>Ou9CTbUTh&n}G zqBEkiqHa--s8@7O)F$>Xht+EniI{7 z7DS7pCDF2IMYJkf6RnFbiA=?2Vso*D*ivjIwiernZN+wCd$EJqQS2mk7Q2XD#cpDE zv4_}G>?QUV`-pwTeqw)dfH+VbBt9k%7KeyK#bM%bafJA|I8q!Xjuyv=W5sdecyWR_ zQJf@B7N>|)#cASnafUckoFzUX&KBp0bH#b$d~t!eP+TM~7MF-i#bx4hafP^2d{SH` zt`^sbYsGcqQ{sAYgSb(ATHGXV7Pp97#ckqtafi55+$BCEJ}d4P_lSGN=fr*De(`|# zym(MNBpw!zh)2a^;&Jf>@q~C%d{I0lo)*uDXT@{kdGUgHQM@Ex7O#j`#cSeq@g=dT z#7tr?v5;6wtR&VF8;PyNPGT={kT^=5B+e2SiL1m-;x6%!cuKq^-Vz^)uf$K{FA0zY zN`fTEB*BspNvI@D5-y359G65&q9oCh7)h)oP7*IkkR(cyB*~H#Nvb4Gk}k=RWJcvM#wKF_oH0&7~GnOR1IAT52P;mD)+| zr4CX@sgu-M>LPWOx=G!o9#T)Km(*M8BlVT~N&Te((m-jD^q4eQ8X^sqhDpPv5z^z* zNNJQbS{fsbmBvZqr3unRX_7Qqnj%e=rb*MK8PZH?mJ~Y8NpqyR(mZLtv_M)YEs_>X zOQfaJGHJQALRu+3DXo%LOKYUH(mLrWX}z>T+9*9OZIU)iTcoYhHfg)GL)t0rlAe*C zm3B*eq`lH}(mrXwbU=DuIw&2I4ogR*qtY?yxb%W_LOLnED4mi{OJ}6B(mCn8bV0f( zU6L+KSEQ@bHR-zalGIdYCNq~=$Sh@6GHaQQ%vNS6vzIx@9A!>2XPJx4RpushmwCuM zWnMCGnUBm@<|p%)1;_$rL9%1AU|EPPR2C)+mqo~q%OYh_vS?Y1ELIjLiz574&dUa6 zL$YDnh-_3gCL5PskWI)YWfx^rvT50jY*scWo0l!f7G+DaW!Z{sRkkKumtB&X%FX2F zatpbo+)8dOw~^b*?d0}y2f3r%N$xCnk-N&>~{_+5Mpgc%^ zOdc!`k%!8|gzuaVcv>*S~8_3{RJqx`hIN!~1P zk+;g*WFa#Mww!dzjYuvAzntQ9s2TZNs%Ug4l{ zR5&S|6)p-_g`2`%;i2$UcqzOUJ_=ujpTb`epa@h1DUKpQK6_*oK#dPsueYgT1B1Wl%ihIplDQ_Rx~M^6)lQZMVq2s(V^&6bScg#&MLYU zJ&In%IYpnMUooIKuNYJeDTWmzic!UwVq9@SF`<}LTvSXcrWG@aS;d@UUa_E9R4gf$ z6)TEW#hPMWaY8f;7x+^`Do=PvJ zx6()HtMpU)D+82)${^)2Ww0_t8LA9ZhAShK$CZ)FC}p%VMj5M&Q^qS3l!?kDWwJ6w znW{`vrYkd)naV8X31zl2N13b4Q|2oRl!eM7WwEkES*k2kmMbfimCBRKDrL2@Mp>(@ zQ=U@RD;t!J%G1gwWwWwH*{W<)wktc7oysod8Rc1Jx3Wjst30RdQ}!zdl;@R$$|2>j zazr_*98-=fFDNIJlgf+ADdn_sMmejTQ_d?Fl#9wG<+5@`xvE@Kt}8DoO;u(pbCre4 zQe~yGR@ta*Rdyi1dRdK3#Re~x}m842mrKnO>X{vNphALB)r8=R?R^_O2 zRe7p>Re`EdRir9bm8eQpWvX&jg{o3@QdOm@R@JC#RduRUs(Mv}s!?@X)ud`xwWwNE zZK`%vhpJQ6r8=WJtLj$usCrfBRDG&`)qv`}YEU(#8di;{Mpa{~an%LYglbZCQ8lHS zR?VnpRdcF&)q-kKwWL~Bt*BO2YpQkCC6%e#Ol_{VP+O|4)YfVnwXNDtZLfAvJF1=3 z&T1F6tJ+QNuJ%xSs=d_SY9F<)+E4AT4p0ZGgVe{=!Rioos5(p?u8vS2S4XO&)Y0k~ zb*ws09j{JMC#sXw$?6n!sya=buFgPpg~M&FU6)tGZ3yuI^BGs=L%@)MwS*>K=8k z`kcB?-LD={pH~m6ht$LB5%s8gOg*l?pq@}qsxPXi)YIx2^{jeMJ+EF+FRGW+%jy;N zs(MYmuD+x;)tG6_H5M96jg`h)W23Rv*lFxF4jM;|lg3%&qH)!@Y1}m)8c&Uv##`f~ z@zwZg{51iZKuwV5m?l^gq6yW6X~H!Tn&X;CO_U~D6QhaM#A)I+37SMrk|tS`qDj@H zY0@ z>NO3TM$KtWlcrhIqG{E%Y1%a%nodoZ=8Wd7rd!ja>D8Rm^lADv1Df-iLCuh6STmv- z)r@JzH5W7!nn}$?&6H+ZGozW+%xUH|3z|jEl4e=6qFL3fY1TEDG^ScJt-01hYpJ!; zT5D~zwpu%_z1BhNsCCjhYhARiS~sn`)KdrwuKpUtH(jLx-bMr&iVvD!Foyf#6bs7=x)Yg4qT+B9vtHba}K&C;IGW@~e_x!OE! zzP3PHs4dbKYfH4H+A?jqwnAH}J*lnIR%>gtwc0xEDQ&&BLEET3t!>gaYg@Ff+BR*w zwnN*g?b4pnp4E11d$hgUbJ{*_zji=-UOT8A(hh4!w4>TF?YQ=Wc0xO;y{MhiPHShh zv)VcBymmpms9n-7Yge?Z+BNOE_LA0AXQngPS?DZvRyu2)jm}nQr?b~N=p1!UI%l1W z&Q<58bJuz3Jat|=Z=H|MSLdhm*9GVTbwRpgx?o+1E>st$3)e;Hj_V?IQMzbdj4oCe zr;FDm=n{2Fx@297E>)MNOV?%SGId$H6S{0&jxJZ1r_0wB=n8d3x?)|4u2fg1E7w)% zDs?AyRk~_jjjmQ#r#q#q*EQ%Gb*FVrx@KLAu2t8jYu9z?I(1#TGrF_7Ze5SAS9ea= zr|Z`Z=+5f~bwj#g-H2{fH>MlcUC>SFCUqBeQ@Uy0jBZvpr<>O;=oWQLx@FypZdJFY zTi0FEnd;5-=6VagrQS+!t+&zJ>h1LQdI!Cu-bwGQchS4*-SqBy551?}OYg1s(fjKC z^#1w)eV{%_e@q{&57CF}!}Q_$2>o$=q&`X?t&h>i>f`kB`UHKVK1rXfPtm99)AZ^3 z41K0POMgP2thtvZ`T~8SzDQrJFVUCk%k<^?3Vo&iq`pdDt*_D7>g)8U^!54% zeWU)gzDeJ#Z_&5v+w|@F4t=M-OMgaxR^P4f(f8`l>HGBk`T_lU{h)qGKdc|okLt(t zgV+H`UU->eo4QqU(v7X*YxZ9OL|j-nZev(VX!n<8LSO9 z23v!j!QS9ta5Oj>oDD7pSA(0u-QZ#HGtP(zp@ z+z??nZiqBQ8KMm_hFC+KA>NQ+NHioFk_{<_R708}-H>6(G-Mf07_tpHhFn9QA>UA7 zC^QrqiVY=(QbU=c+)!btG@LY48LAC6hFU|N;gq4?&|qjZoHjHWnhh<6RzsVi-Oyp^ zG;|rx7|t5H4Lycl!#P8rq2DlIIBys<3>k(EBZg7Km|@&-!7yQ%G+Z=H8Kw<0hFQa$ zVcxJ{STrmdmJKV0Rl}NL-EhfZYGP($Zen3#X<}t!ZDM0$Yhq_&Z{lF$XyRnzY~o_# zYT{<%ZsK9$Y2sz#ZQ^4>2b%0`CvRz@GBcsq4L7s-`o8RY4t&pn?>X>22fpXP_Z;}1 z1K)Gtdk%chf$ur+JqNz$!1o;Zo&*0m2k4=vw@`Dto>sL#O}9Lq1J2)%Sn#sr!2Z%F zqwZl%i@@*~qGFu2;K9%y;YY56Ysj0WS z-gX0?M?>-I`@%Gqy&(JkP(1Imx?eVIWM1k5p7;8IEB_g*1w8M0p!79fc^+`l+dWi{ zUg_XtOSy$=r;^zpHghdh$xn?n1MZb>W`8(*A0GcoCUf~Y9i{-^siiT0{C?=gqiB1m z0bG8LF>mTGUVpyQ(?-*yq36NuUs8V&if`qXp99>qPgl!~JE82@J11TU-RK5pEZnwJC$OM-$KKtK4&T3S4YuCI{ixgL(`mp$b>fB5hw#ha z)SwQz*Z(Lt`90&Sj+O!E7kN89#Qmu4UM}DLXtFinj@~&-US^*A(ej^gw|JTNbxFbCRR=4-=$dw^$r%cS4H@IZUx9bo*$!Al^q2jrUg{cak9zFq$4FlF$w@MSIk0{^+( zgUiu12M~9M<zZ=BmT9^Ob$guzGm6jV+)MqO{xiWZ+bSQpJhQm*e z9auuQenRJ?`F3DX;dF0~6^ybPibuSivUlPaHhZsjex=k#=k#&d;mhxPrAfz&_!h%D zv2!^f_?eB*bvnws^7JeJIUOCp&HXb!)b^d|qzUu}yzrslD zG-!qoTzQ>s(>?cfi*X2+ca;T~Nek81H(t&$y)%C63AwK~6+xG!;R7*WUjEW68QXsI zD|r^GYx>@3w)V0AQ8%znyx9L_?)N*r^1$!8ehc9GiSKeqe$6O`)E4(3(e!9DdTvkN z*DF7{%<#-9(BBQ#pILs($U8_~9SrRe!23tn4yBEZYX;~B-SRe^hN1ZReJx*K{b9zO z;K$!E3DT#4erK^~^YO)xc9d#1ACC|0Ebr33HqMNDJHNin-wkq;j}AvCA2$yMc+Dqa zhtn;J9U|%b0&7mw>lcZ z{D17d2UrzH+c*BdXLnE8Q|Rpo2!be}V#N|`>?RsZqKQdN)Z|S}G4)A(@&rLa0YwA> zr6@KO3n~bT1q(J5!G>4?3yPo=3s(H@J%zL9oIQw1p8xxO*TuOmW#^vR+1c5<%{?=} z5uyi7KQdmY8?-E(_WKQ@m7?VXSNN_ffe=sbOL6oNSH+R>YdR zYSLc0Au;$g9GV4g99tIl4zTAMQ0=?Xm*HEJYTwzuN*+(gcDgYeDkn95OI>%9{n zT!BmRY9@~Dl+-H8C26{;xjr`j4nlh4`*2S?g4>Dxl9?!vBV)B1zR_T-zs;uU;Xhky zI#u)k+LK0o5HNjSiLT!@MKlx>8PJdo-~Z}-P$Y-LB_EC9H%O zLwM)%6}?SF(xU|6)JOQwh3YQVQn-(0$Od)3XiWKP<@vCabYtCjf6vYZrAc=z9&>u+ ztnI=Qj-8x$6Ylq$-+A9hD|&Nprv7Mpy<6!&gr~PE{cm+uEbqE$Y$4Zt%>{Q8_spCu z2$vZ)x6=Q}SVg0ZKNb!+fqczv|;&1=1? z^}HE9x!Tvk@RO(1nR;s+Z3{cwcNOqe?rs07848nr%jwoIEtzRF;hLmS^Y3}D2?qzh zBxxvG3#Tyv8q)Ef7gqz5HA81IPj@bLvY6|;bDB`S1%tKkiyCR~#2xW)s+WfE3kLJ- zS;62VGJJ8K>yhz4i@i2Wd3~iJ@Ekl1!N3JR-FerCkqKyH3j7Lng4amP_Kew~=FCRQ z8fewnGp9S>>Vh71BOh{UC2Gr(M0lbsGIW-V{0%IYgeq|h zYr=XLy&m`pc251U0DGZMR07nw|*EJZk{IJ-R&Hnq#HkirvYQ}oZm*#jP7)_ z-zv~M?a`fzcffr~;}_MY4P`XC)2?m67i^q7~>YDfkV`$T2yiN}dZKq)!t}#`wN{Ucg?aJOf=#yF-ffg>`zHI9!zTqUdX%5+`g zTj^`D&?R;1DxK2gO1<#X>&v;TITKqm5Ff2ES{j0(tS|P8{Alqo28ldJ{NdNBW7W;Z zXQQQZ;9HE_!?HRWzAQ&M)-%mfh_ARj&J2|fOr5HjHFMK+t?j-&>`I%^FYPtGtNDK~ zID${;&_L}Rk_ z?VS@18RuXC7>qt*JSxtjqo?La?RCZ#2%f2Z*@!nUeJs>DF&_A71V`DL9BcvaqwKH2 z&uAbm`@gn4IPzo<9phf~FT`Oe_rJEa+$HA<8j-eq<7PyuK5SejeuP1hg1JyR^upI1 zPh7;Y&6vO zHkt`cXU-JrM|Fk?Td1kL&v+;WmUh1CF65nSv(Vychp+brB{Q4uVt*T{og)7RzsN={ zS>*pP1`2hKx31G&40+{RyZZ0T9p4mc_o`}8R{KWo2DKN>i%)PFTkr8z8%N3c02zX3 zrdPi9;4Iuf1;)-MmbG8{92ibX@xEf!@PuCXT#7cwA`{231o%^kS z2fbC&QufLXN%xYWL*r|6WssmANokyS?h}k%`g$SEpPrYl^(@r1Mzf$!=|{}!B)>N} zf`0U<{*DX2u3Ms$c#V2!xQjS2hM#m{HQf5DaUiz3+s*HB=&DhHM>}2};})c9%XMMZ zamaq-c)zFT;N%8%fKmT?g&Sb!XJ;DC>%BL&_0t(+O+6aQ@s+TkccdmhsEagca^gpH z&^rh^(q6fqHme>4oE$avdJv+|@VZ}5ogzSj99I0o)#frI`-a2b21Z9k9Re_xEjo|mZ=pk{2(<)N}D1Qc0L2DsoV zXN~5&MzGjxI7~&G+kFT5`t6$B%W>I z3SP4*=~bH!3Z6TvBfs!sI3KRqP0-inO5J`V5_nFaj%3iZ+ixWQ^mGj0f8dJ64o4*y z;#xQH3}q0YQCIO5M!fkgPwky}5U%xdGciJ3XZ4p53Z8R`wU}1U(c?#i_(!msFV2;|^;IIa+i(JcqaV2gjByQ{!@bZvCQ zL){%)zA32S)VVP|+vRrqd}82F>26t?4Fx^;o|P_RVFsKV$s2I!=%EkM*clDZbb07o z2~~xIE#nU7UZ8TOq0`6JvMUja=5k@{4RV9;p`nMPqlwx|G7&@3oM$FvSM{}U65bV) zN$9MmIlg7w1WlE~fh~O{M+4B67I@ub-WXyr_xxktTSu-m8fS1--Z}=6lZ}NBiE9_Y z)-M@@_&Vp%Xm5#Hje$jlp0U^6fyLdD4aN#w$<^TyV6XFBIl>k;#%Y{p;hwA|>9n_I z;*!f)bqC5q1U(uqcjJ3FRy6k+(pEtIFw+TJ82L^pI!+{}J z>0$3HU!r;v%HXV*k*?=V9@kyB$GGvdMKzw(X{hyre#`1;XyG}ExpxlwEs{U`$2IFh z&+~#IeJ-!qO{weSdn?$I%5kgb2}jPj$|}b_&j!ZG)@q2nsFc2> zxjxt2QVVDnDx4uZ}xp#7qaU7GC*>MC&u%?|5}KdUBc($Xb7lF6jkX zoy0Ct|J^IkLjN^`sq4#+&v;T1go@3T6&cd zr3YGiprr>|dZ48TT6&cdr3YGiprr>|df>1102?#N^q7>uL8=!6*=GY=&I4&V z52WQhke2g6TFwJ$IS-`eJdl?2Kw8cNX*mz1{X3_2UoLPhMBmzs}x&Jpv9g(&zLdSg{3WTPw=COEy{Tah6oWBDh40zN`c% zgw70;w5o*1c<%jKr~S(zZe;Z93WGhJ93g;7qrTMyIg_JW;I2|0c4i|Zntaw~;y?;pi~j}= z1&NYjXur)tL24_~U_qL6$=%-(63IUQ|NXzt|xCdIJa3kU7=U7JP<8)N!^S_RpsRpU^G=rU~$>bR} z4dEPX=!Iy3E)_f!O&y8n>gh-ZF0qyaXa<%hr^Izy!aSI!j@e%Vq^S^q(c~0S zBN(Dq7GDKObC;dK-OU8jJTM=4NCeu;bcGNt8YSJ39*MwawV#`?92Vk`GzE-b{V-1< z&C(Q(X6g_fG>|ZiW|0pMk*Jw4np&;gTpUe=#}tCmEb=kJvs1%pJ~{(@a0>$z&HAsC z8D49-q@n8l>lm8adj2u|%qWMZ(KFAn;{RLGGcSKEIOyfTGtnqDA3Kw?jC#pe18Qm^ zC^gnsLowlg4LMz*gP7phS4T6@Oj;04Ei;-$(d6Y9`RLozFs;Ddka>!0V0|WH7a7DK zv;dkMSN`u}X{IZ9=!x7V*S$t0#Njrg?6x%`6!{nsjG#2r6+EX$7)lcro)DI%sT+id zm*()ZFv}QAGx;;Y7!8)h91|YVkolkU-XKqNc$(TQ{kZmT;At)~hS+ZcP_w{a5Kgne zzhsqxiYxHPJ~g!01^zd~kl~DQ+Y9`k+%Wj&1^&kcp=k>IQBe6njRpSoEYHw$Ndr;h z2wba-(JBl47j+dfI2@gi*2Sf{0S;m}YGhlB>8LY38o)5WLMJQB}TaMb@LWX$IsLw-hWX9B=@+ZYvdpJyBb1scsHa1G}&_{vN}Lp02E!>I@! z-8c@Y$r47xOjnpz&Y@uz`TWCZm;%k^KpacQ{r`l98Q^`_FM>@Gaq7tAb8sy|_e7^Q zxDL~j^twbx%zw+kEa=F!OMnpquQZ2)x!YFob90Z~Z7Wn!>JZ~E#g(mgL1V!S+Gv{d z!Wr$uF1tn}p`#iCUUDK7l~yco2&m`D&p&D5K54rG&)6F#`^hj6x)*5wLEM~VO&dI+0t=2Vm`8Ey5i-?mYh$CHb)I@$*Zan+R`g~fB!w=|4;o( zUUPdryVHV=CZ?DX0Yvp2*e4)_86iZowX=6{bW*i)c5!X(=HAA`v#nRV_TE0e9XfXM zYhL;0mD1PRxl7k>-Fx`=?A5!^Ykm9mA24vxOKT7;AV|cwVWX+W6)W>7RWQW{QA@0< zC8B!doB$FGB#Bmk;KYdeab5(*9#K;}|7V|SbG97~G9 zfqvdza!V|}?JlApwH4>g1x9qit$w8C5{1q5xza@rnQ-$C?1lP~;x22I6qMVO_Lhgf zlBN&iN?A4Zi;&_yN|7R697|?PNRWkej^$aE#D;z`Kuj+bJ#;0VfLmXkdy2YD={Cf8uX{+{@=R4cbw-UkT?JiR1Tq@T|tZrL^JFsztwb3jj`sjyGKl}RAnLkebamF_@ zt#uqL1e`)%@kcf5NB`{~^Rk~v=qEPwW0w%t^2e#)fA;B*KYaA4b(Y#Vk$?@xhhYW2_Edpf(NH%KK#3m9mkiNS9G3J|BWEm{pET*CU zHq)ppcp2kx`2xIShI7PXHl5NRZkydo#T90+w>$pZn3^s z8lZJBA-iYhx)ru+E#FCQcboO|NjnNkpF+kB$a+)un9P*r+z0tdiCxIIGiA&0pe)(S zqp)la>br&qkmKv9^4?mBM;I;bP`L$20z^19D5C9pQn1?E(e3&7L|urmp~}mxa?V3M z5&aUSta<&kKdER_Py(ZpedN0pvQ3@WIapWUg|#l&{5P%V+7h1)L|$Qedo%R!$-N1d zDX4}#seCi#hR<;%?6nEOJ0YLQ@AsEAD6Au}&AWVt?(~{Cd~CAxy;^2s1sqR=+1>KQ z!Ai$eG1s;?S)|b#+U1|)%fgGvR%|}C2SzcAg@k=3o&pUk9jKv>rG3sDvvftvXiyee8(pL zU2KvC`xFF}Ksl(c0b6RK1T3wrHS)nX-DZV|Ux>5s|3txN*P(SL)Mnr8O>C?g>+k|p z&wzbwEaZPR-8|*cJcYoOGNTllBjqM#1jHUm>_Q)Hd)`zZGY>VjEcP{m8l;m4`5WbH#_dhgZyP+FAIC^x4uiERcap! zz6n?l5$Js7Tjn@zXYkA&)2lHSn!td9=-?qBdoVgGc5B~c)=1x!F3+t7HO|ZsVGTefFHidva z@n1S~?mWn1Vo$*Qt`U3S{B3CHE|=YuU--2$0#@9G2%CgJSUV3|W7CQpxt8s+j{|Zw z9je=hed(E+44dA)E~&5-vFA@fTWq}YMGlnrKrH?!fzgRhn$79~V@<_Z9tL9u&es^$ zylR?PigRqtlr%#&J8o)1)v??3gOgNBl|r$rs1qZ~t>@!3bZ_TJVuzFD0^Cu~{#aa` z58J^Zh21QE z=nb;XlCBTO^+Ap=f%Zcb8|aO1iPRPLQ?8K^kM@sUuuMPYS;a!gR4yj;nbrrGLKy`v zJ0!=g_xs;00k?S_9ajHLN6`xgN&-GF-2t6%P~woT>unCv6qH26Ubm}l=v!|{E;{V> zwzx;)6=HgaB+rSggG2P>wc-Jab0pyNbvxlCjl53dO9y=Z)R8DIhQf-0;``3=C2&wd zBwWT^X|;<*wJ6>JA0I^@w_ddzlyt5_yjfzgP#Q(Yi1z?i-A@@sZ?&dCe!I)JUr98> zZ`Vo6@7W4VcBue^2NP8gVFO4rSf&|83`XOo<7TxC3?DIa)EjS(9`n}NapNbv{m#Tm z@4h$rzgYGAAAC6FqmQRfn{NCJ!!)~)ZXATqp;du2`+|!8gjw)IT@EM~w_Er{$s97L zPn}=7s(3^sJQRhDy5=%lMD9*39&ui_a61%_xbhBjCL6X4Ds>`BHl+5AH*bpIO3$ng zd*n~4qlclUQ)Vz(Ic9(ReJ3H&>d73!q)~h1b$07*6MZ(k9c8u71M|-gp4IAsbWI*q z^glRZk37H2dRwF(c#Wg(K*D2pHGwOJ)$Ht2{^@qe=pOjp`fr&s>E7Ym8Zax&;^AYc z6m>}b)XMj_2E%oaNC;&Y zev#~)y99z;satmX{Ro?+kFR+=e4`NXYb+gbV9MrxTS{QcxN!T4A<~-`gG7ogC@iNb z$w%?H6^5+J1jdz)v&p-S=8>H%G|IgPsv2PLG|$21#im|C8gwjc$e~ILK_9y5N4!E6 zMnw&hY?2voSEoLj;nFqRp?md$&3AwQdP!r)+0a8|pYlev)s(7=Vk@XEppk>1GmxJK z$<7bj%#z=oaugXols)CQ<{bu=M8Sw|Cwm0D?x*(2i`$_Ltjz#|XQ0k`cSce+-181` zO>sQr66SlutJQTyaI0izlnl~={!$4pm`R9%%khvp_PlJl^R-#<$Ysq2NM`Q(<&*nU*l3BAHyqI;d|dk|g8o2y}Y$K}pz;7YGTJl5-BujpRM&iI_2(-!;;Yw!;lv1y4sTV|CyBWc31xjbr95zcEnrfHN8nw` zhJ1I%M)Wop>+hUMlh>Yt^D?ok({^S#5e-hZz?it-EDH)!EKZWMAWpVeLJqdF zNU~iv(1G5n6g^2y=>1?}Tx#WT5qS%JMVo5>H24EVP-i50DB+*uq zY#}}RPKZpAWGgA52W7dRq(tZGg~aueR2*Q%1@9NbdXZJKg`eUwSlnxSiWTS5nQ+_D zLETdl?tL@itMeocL^sBfug2xu-KWk`lIVe<12LjTFw(!!lAM4l>JYscis?k0Qi(nU z?JH=*7GIP`Ef2ArcURqXXvb$F6?TP(B1 zn~Q&+3x#XUv#E#ssMuv5NL`l$hjyaX`6fxBU_Lx9JL0g$LiXJH*)BMG3Kyxizm8MZpR{>EFWC*(w_>FW zEk?7?om87=av2q_vC{NCuxDBnI>{kSF-7(;r)=bpN`Y)q#QGlYLvB)Q6-&h9zrG5q z#E;o}?0dRNEn;VNuHBuhbh`+_R)eD2gUMO;h`8~)rniQ4un zv5k{`!sdEe-}aKPb-y~Ljurj#03MQpS_t{Yeya6WH!e`U3vN*v*2D*E>h9Gy(*1>` zYN*_2{P3B9K6yiLEQKPed_g^6aA((Baj2!vTkV2EDpXlQ#rCS`f=Y@R9w=K57_qmw z5(l@!V2{)6dPwLfPwusiI?k3$QNVF>+EzEGWia2T6Y{1Ev>`jcoRLk~7zqY8RB%#7 zfR9C@rZ`!b*EB;nvau-#v!yw|wK1N?&*ic8c1nd2qOFweQPTDKM#L02(LMb8Uu!>j z%Wgis=pNXAQbNswol?7b>tIO^tp0GFOeC94e*9i;!Mh`AjQWl!W<>yG4L)u82^(kM zHln>;cAcpJ+sDMKZ;gfQic7VER}BSMAnZPjB5Qt5bCU0tq*0rh?d?Bdg`el>R?K+s zZ+&nySx%4SrbI&E$d@P6Ae5Q&DI<&PE(^4o^#HtNsSayJC@Lj(RZuJz$$tFMc9Z2@ z*4p;*254A};<4DOSiIiyI>-+CZ0(i41g=d*Zd(hn4!+zCY>M+?ujpHFW(HD=#XW1M zJnM1_E)JZ38p3DvCl?2ndEA%D&rd3?fMVOK8ragSKPOlC9MlDcRI!+_v!B7a18{Ln z{is%Xqc10d{TLZOf2Azz0W27_{Wd5g&%!YY1=pz!o#)OtA-k-!%X4vD>p!14tw=fu z_&?%P#QkKC@!uswkN>Zh{8n@cMSUyuQ9{Kbn1F)4*A@W#${bJ=s4qhiKa**p>GGlLTWFYOV%kR+(NaF!bp43 z8F&U82b>U}rHfUT>sP}y%ad;O%*?w-j7r78XLBN6eIz@0R^%5d*Kbo~luCzuMfR(7cN-(rhw0@{(upWK1&}G$H7g zdzj78HQsCiZ3ef@L~t42HBglTd-mSQ6b#$=-qH(uza+2qPty!C0FfWP`` zGW}cpb63so4^2UnUTN$L;|r4>F(&AH#OO-fLF{(kSY!jGT1T;Ks8fpIey4rz-UQF; zKVOJ&(71!7i1V*=3n@BQ?FIV_bS{+9yKwPT_qNMN-|I(w#Rq!2N4@JCTxbM^)s2=XRdPWUSC{?^#W{jEamg<)5q80m~yp5G(QOrGl#^j z7JR!`B3rFYSPKU~ZXNXdGMcpM_}~SU^xhi;?JoD9y`58Od4ASq^t%{xg2&#tR_5l| zvpvV$Y(vhH;C_!l-QxEWp|Gatefw%^@7+)h?wJ6a@rLdnjFYiw?IKeZhljD&wP$zL+pZ0CtG(_pxD z-WI6e1Q}o4lmx$jOjKF}_dk(`j0u|gr|g6y1*a5sr=Yv{+MzdkAO7x`NESOlBzs^< zSlO|qAkWT(m|C#Ua*%1>P;?b8(AjA)hI^SfuiwtWQ~#A@j7V0(R?7BR-Fa=!Sh605 zI?jy#!B@7{h8u^sN%>Axw*lg$z}AE5YH*!Uo7Db8 zObhF>bUCl>8ZdL3)(XFF`_@>tM9E?p(L%e}xN$-4bx@T7d*z=SYUmmu(FgGi9YWy( zrc_gk?j2RF%;yQ`u=U8xTENCCqN*TlIeGZ+0c-4Yhla^ZOX1nqqNi4A_EP%1#a!_! zCfBwPeSo>yD$s^JwHiXtcDh1~^(CX=`}vYvGzPEtfBlFq@~+7QmdvN<`7mUMl_K#x zF}orT1r@8p@%D`7}gEKfy5gzsu3F=tI}hS7hj@-vDvZPZA8q=XJF@c^TSc z-27OthnHZALz<2Hl`^bsiX;;Epg?q5YF#mV8;+BdFJwc$^Qap-V2(ebA5l-4A>S!- zq|bg!x(hSJv%ZTLrJx9Le=T#_#5XY5-B!HUZtW0BemibBLL9<~G=}lG3?9#8@GXi& zBunfbFE9USkA$vp5G%4|7K>YD{IZx0LPzMzL>xWfLPbH-s$;x=%!0hsfS8yJHNn_i zn8e5o%!QGK;3aj|)bKC6SZMI1Tiq|rtc?Q@lf5U+&_L?dF3qpkd`UAoresr!UQV6O zWpduAa3oaMH~$!47*+IAHfby%%r6|(lxNfw15QyVc>G~{X~-%16q~{GH`Y?B)fBse z${GADR z`qEn$yPxmPJP=u*h=K1ni*M2Q#ImHp8@+dI0sCA(vVlsFw4K++|7;wLs&JMqeOr^m zm3EY~T);5MdJbI!v!IkNuYsQA8iwtC$A;6PR-?HH=`2Fnd~ob8I}U|@VNy~2eDIoU zBai-qDRRMv5cyFCm)76SfMdUVt`_5>^mw|?n{4-tI{;?_0i%<9Nto2JO@%B)+&0AS z$~tfxHhfamHL&0F?>gV)kN?Doy5!l2{TK`BRc(E=TgMButUeOrig8`O%zOFxyf>J2 zUh+SD?~UEn6<5#q%7=Fo2bEA6g9bdZDRFPJH6Q9+@6*3?It^#tNnrQ2q75RpYEf^=*{V+{@R|Qp?0l`ooy~q9i1Ps4>K;MmR|Bd$z}_}u&6g(|dGoJc znCI!2=9po6j%>>KCY1l$@Y9@zF~9TYm3P{i^#fn_?3d+h_ZI$F2Vj4dQ}p+}lrzj0 zXo6+bSteW}Ob}e_o(53#bpbPnP9__5G`W>Ml}Tq9i!nvc<*a^Gl)iQ(tUi?iBeKkyP7)$=hZ_iESAA zQoWW@`4HJdlu0hP_TD}j*ORm5)WVsBedHi3rmPp)kRDFmeG|JnwtBsHDvsphU=ICz zgoPKoFA|b%aK*js;GdBqk|FP&O6F3-W^IOW*|m;12As79hRV2tM>j%tJOrZRnJiP^ z^r5Y~Epqm3x1fK8pN2o%&dY~0h{aUXof~Z#ydshPr$pH?X7GuxOS+O0_dJr~cb_I% zLx%chepdBA&!nVg zWhX_T#a$VOA)mCp+8tx3T{>^AfC+aM4j0-KizJ;ZdS7xUvn&Vut)!lRxH}#aaCdO- zClwvAzzq+wMX7jdC+12H$^NKFl`q?|7bNSMZ3p27w&FZ>8JF-p#1%A_RlV~@=A&c{ zS{MkOxfM(`Qe1eXmBautad7Iix7R_4Wa7V^@6lhzGGX|bk9yzxwNPuE-<&pdESqn^ z?XEHz4hIoc0AZiMd^l|G%bTNf=BTMWoX&emHjV~fR^rw31@B-J^eh`msTNRd7=;@e z!Y##gltR1?7D{U=QV&B&St*cx(xW2h?R5Dn2DS)5-e5gkI_+nbmq%*ifH-;-DBq}h}wm`D~(D7$@z8(L-Qa{zTY0_zF{X- z`flQHC|lA<<@6}Kiwe>yl@Z+{(sNW4zDHa)5xuUncv?9v+1CrhU{>O{&nrj_`!DBg zYbm{P3)B*E23dfMLtVH@?GTSE=QzeS-5DK7R2b-c z_EOFj{NjiZV2-ZyqngfdIu}tCUQ*yC+3Jz7Q5mBow?K`Xsuc9Mbz=Qe4_oi`7+#rm zaWLa0UXHL&!>5`K4Gk&!p`q}V>BL;)WE}s3-N_qQrctU56uXXEa|C2JnccE*sIgmW zN8at;<^ehTu3cZXceEZJc*|vhbK#mK4&@ZGMEl4CX8RW*7!5A5vtHvqx`+(Ng~=hx zW!5C#l4N?wXLUGz9dhmVtb=o7ms|!F140z^1Ldx4r_;1Qf(6dU>u20DzRHZa$(a6 zuh8(R=HO1RpUeo-|XJo-^LtZvX3A3~z)ri6QOs&11 zm+!OYlGeUkCHM_y&s(s#k5Cu$O`uduC^m-L+}+|q?+4v6BG=wOF(LM*Gu5jfMfGp-6* zDv_Y=xg=gk4^-@O#3da$%iz(B8~b5fZxOxMPI72#wfvl|Vk zO)BmwOzNKa-o>t#iv7~^x5Ve5s@3Tzn6Q$FvR)_XFJ{O{jAf;S><~-xaYp;Bw2ZRY zhQ8NPcn*i@6^!M|fs%AwEK>6CFEcF$TW+Hr!X%QCeXyRHCwsX2af$lSNj=X`>%Ymlirk>3*w>xuU~8NABcx@xzYtkFM#!MeBB}uw8ZZES>+E6UN8veEE3V-8VYSDBO}SlpA}std-tYD@ zm^%R_+y0NmmcK=ISwOZWLdK9A+mNf(gw3!v63 z<}=)op0)!#^FQE%g=*QgST=aAXco)v^lJzp`)0u?Ts|$HGa)ns>~Yzt&liVKT6wY^ zBX4lSo$Qs@z;m_tiWmr#W;x81MN;w;j&@ax;I!h}EP^9;ke_8NGBNs@*E4$=sHOa% zq39}iYKtlo`hS-=b|KL!(+=& z{C8!7+r|H&=!TS3C5=wh60 zCn@@gJRU#f>a;#5ROSBS`QmeoB#M^a`RT3_7q#JNbMs+P?xLzzfYn!i-z^izDMkCB zpE7w$A4To-1I+!wSL9N~zA{{9cC~$=#}TLJcu#E4j8^24__aZpO0Hak2DHhxuX*sGWi7I7go!4Ydwolsb1!I!qDKrMRDsYfk|@{$|LjAr-XM zX(%S0j@oR;%_AI+o9U2TH|jL}P=*5G?9z0u0RUmsMFn#Ohyo<7+Azs6Bp9ZQ-$UC{ z<3d~*SkQ;jiKkiZ&QspPmrba2K_$-D-Q2Z+(niut^|pYT79tsKbV)F0<#b$Pwjb0P z!aX2JZK7K+t64o`wsW{HGwvXxy=+2#VL~(N9G2b8^XOJlD%`D_M6F>WQ{mzFt!2gI z<=VO6qD*)^-fnZ}Yu}W}w>E%$*IlTzBX72ONUZ0CaCkhu4n-hx&mY}=OIN~839h=^ zNnh{0Lt*#Jrok5Lh(*fe!tsptFNdZxP7k}U?pARVqQYU+odMUv5dJ5U$~=q5n$mkQ za9H-HXNnJ5?7h-SzRfS|DR{~^`5Yaxcw{!&?S$I#IwWEwf-HIn^4V`Qj|VNEjGI2` z$j|;@QPEj!?{XpzHUjbKzKx1afD^BuSq9MwfU$2zeoyQlc+yf^*cCSg%w?X5cLgLE%^LHBI-N-k+b#oE;z#-MaH-BEu+aF zU8`%yf#V4Q!byKgMqKBP0wdXPV3V8(TN^+6Dh`QQeR&ICkz0qnCSxu+99E69e zC-&@R+Y_IR4VG-11>`-JqsUP4?GuY^7f9-<F9IYAN8hqs!Hqhdq*eYPGKbo+H%`5uSQv3q^>m~Pde`7U( zv78HvxUpm#t2-fmXCljL!pq|=I)QGKW7$i8a^CNs+81WrFHLOkHSQhZMGGb-!MKC? zU8Cy+;lGVt!?G{!8(slzFR^Jng}9PZEvMK-YAwuVRuDEGm*hMh8%*yx0@hbut5_n( z9f^PZaGb2cgL-=+l%(w0)VpgT`ZkO~1$Yp0EyJ7;R?;?-#ZVI39Wfm*jL#$& zY3s{41nad=zKEW0@$PYV?_{s2y|_G?jkkI__J}P$L3NEPrsNy?ThCj~>DNWnjx=yP z%V=}!pu3m%BWbeNhLL}8;n(HBhEl)D_SndtSjc~~eeiADYZD&5wksD-{_7kLBMa!) zan}BT9NY29v#GcTm|f=}J9z??)H9XcEc(L*F|cTmD673w-z`UQz0sZbL(?&DFWEbJ zK9qEkFUO$oQss+PNV|s1mi-UE>HF+^5;dK%ULhqtM^?e2Z@k*vdEZM`&`qX!r4;x1 z+{Swdv2YYsNw<0(vApj1cr)H#*E10O4_SP1osrLieKzg-J|F|Qw)uQsN#Q2voeHjl z?=(Qh$au1XET)`ZPq_(97)|7iR$F7ZNij!@_HxfdJ$dMQ9SJleytp3 zt@wuhkSEewrF1E}=b2J%We)e4K#?RJK}@&9x@o)Eh*>0v43RoxY_dy;v$!7X+zzYg z9=R4PU^O{FBrAX}7bE6{_gRc^=8zF(6v6qrA)7%1Y4OliTqv?=z`R&iJmM+%J)cG& zy8sAwk!gxQFqKND^)BS`P!T zWmX;h^84NO4xvUky!{-`Q+Hvh1(9^hw?oF|=q8K&4A$^lUp6rkJEwtw&2 ze`t-es&D*9>=Pv}#WfupSA(hw*b4JwW8P5n<=?-J7L%>{B{f*;yhRqxMLp*Sdj+=> z8n&S^g{v+aX9I2$)pf#_kuXQ`Jd*HLWC4}p0GP;`G zN70L{CrYqJtKQC0&JI{(1;HAx(F?#qT^;UqV}>LzfREL~;o0ynXT=_S5w`Lg2e8m# zIc4Q~en<$xy{U@pleXT*p*z=b$wz(#U2%_E`~6gwi$e;0N*A^j(~t4V;y|Xia~4>o zg5n`hF{|A}TCy1eEsN=^r5+OZ^qVRWy#fC0a^O5(x8b7&Nb){ohQzra7_iAAK4dDlk>fGh2Yzw*n! ztCKa ztcwBCPL}S3%PYIf7Ed@x-*&aLos|zoo1nEU-R6Mgi$@W_Ewilmmfvrk@xy#C3RYRk zPHl#a6p&%l@>zM5XrlZsC3~(I-f@8>+hd|!w*5Y?FI+r9fAh(lwbHlSIwqmX6Mx zRAt}xAsqst(CV=&QI<;l!vkUHLwY0n%`z|B9j*yuk3)mUb=WufbRg7p#aZ}lm?QUH z!!=PB>AqXGNpbrOFw0Tp&SNuf0h9G+7=$XzRm;9w=z%NqRNqdC(i-D4rH{_B3oY;v z5>sQW!>s112Oa(qKc4@RY@_vILZyXhwgN-}62BovPcj@QH{RQL(Wr;n2E7fwM^rV0 zy+gQ7R~tp*m>zUF#$K}iS<dHUQ#h6|5?T1%$`PJyrLc12=I zzo5=ZY&3bP-z=c5VkHAHO8o5_IeKvGT8rsb-LB4oD-w5{pTA@){qkY&>M~p+u&f*U zXjOC~VuVSTLOGiWBOb9>_6i~e--Z-u6?z;N!K!Zeowqa5v{edlyT}?OmUqjLq2`hi z?hf=Jxdiu_$4V|sNhmpc4K`7dSji#?`E^ID%Tmc!vYJu98ZNc$!@UPK=a%&R91wS@ z7h4|f@F)QBAoec>PrN>Wb^a6a88d5aO}{p87ZC3l9m8^lDSkv*FvpbG9E!Ff#zl?O zmUyg)aHLu%pQe_^fMRG}gi?GXB4iMZtbiujXejj;#z6_wG(SvXc-^U{3a{GSTU=nrF9PW*mf8GY_-s0OkYi@a5DQItakLyhvTau zxt)Bg=x9Eu*m?xI>bUS*IlJ@U`|skJA$PiQ0hASaxL$4prLJ*m+lUTD1D@4@@1b|H z3F(>U>}0E{GB*vPtfyN4viB0$pH$56Et>Q&9k@nRAR0wT+sh6!GhleK^s=;F61^b+ zN|mc|ter?~6n$ z_5o&FuU1@~ls&D1E+SLCY=eTx(1o}hE{bF@ZQ><`N>*LN@$c8;3M?xU25$aS6RCZc zOYguFYj&Ap!{-#lO}J=z<@?9hzP4C40dc1kk^RO@jl@+~(Qw)dH6pHWv~*N!M0uO7 zN5K=XIgRMx^v1e>YfZHErt=l-W92`w8p%Yr7|h_Bd{`yRwfttjGkklK|nyUni)TmA;yo#1V9<@+XN zc?QZ53NDIV?7^;e*w?;-`t=3M{XFF|f_y%-&3X4Sdg6NU{e)fc=baxI+3^AE(Q*0a zYAP_Zr7E`KD4++=5IY&Wpgnim#y96@KAccOEFN2sHj8W@z;bcM1F*+X&tt+N;+>mz zRP0Dn7?t;hKQ6eJF-cx=Fn}%n^UsS;H42=s@|qOuUp4Bf=)w3oZ_4L{LekesWc_Ce zZkgy3bH48%ML4gtArKMZx*bU79+yom$b`=;T{6n?b)sS+xogNaq*q#^7k;sa=*&vk zh>LTNbrIP{{m(@x=~37<2g0Kv{H;Y|`D)4jD-gw!<~y?YBV3kbV=bk7?2;_`QGFZ`jL zg9vorIA(G>0y|CRf8md7Lzz@ugZxjeF*0IOqog6vV#I&-qN=#tqc&mnV&wBYO5&G&_85^iy zD_l=h_X%4|9+4s6ogOk;8|Y%5(ccC%Nbbf3aKGv^WSYApG5#4f5DDof!6q1Vyt{Rwxl2VEnoMDF+$#v{0g8CQ)g8+iSckr zIba3*hOW!DH8g9i(`|IobSiuk%lW#qA7UP?cNzkKxp_> zsBl}r=3I! ztou4f6vsUbSDw|{Vv9xRAF!v?klv>2YUU7CAi*P`_+QX)5Q-esWTtvR^70bq1BJR^ zUo+}xMiGzZl+mGubD>bR1Hd7|3$CoW52{*V?;2ZOZQ4j_@scL`i%)2_@-kOYuhd@s zN^j21U$LiocKI-+LIl};ROnhr`{4SJNSF&Lg^+=V?~%?sd~TLP4(|MI33pB0m1qP~yU zLzZt`5Xjq2s`feHaq%~=yt>R`Rd@Yx06Q@(*fuH>n*)3ch%>jT5 zuMXB}0W)l_A@jJ~%q5@4|LV0lmgmu%!}I2OyNMibRf)+qR-FbA)+@qPZhT-qr&${H zCHk_-+IcY|AvoLS+$(g(!R0leSVCRA4mSodF&QXdkEwMDC@|Ub8-qMs=XIa|@#Y|) za0%xFPmyAdXF`t#Lb_;W!PYVe6o=uE{TgK-TBinZ{;t*XDZQO@?Qwey?ko)@iXEag zN-16cNkZFNs~YiL`!#lwMB5Dv-GE}1N*`%M4y1DyKDM8z3)U{#e-N92lYia5x{w8Q z@$0)g&GV!mt%tM!ZneN-6GcXhn~eCc!55)cU2mfTc}J~2y1sA_Z|(V1`j&*&=5TtX z>%wF3!U4}Md*B&BJ+;rX7Smc`s+8a}zZI(myPq;E2qqkHI@^z!iaQ?c;F|x|;7wtm z2=Gi5?Z=sIowQ4M!oK0h>^2l0k7il{HvE#fL@HHm=y8d*Iv)=7EIcB*5?twMsibm< z*y$-w3I6!m&0l5OGsE(eC%wkbwS1+G*rt5Ir|_@&9)UT!qd|r~p}%T8i9mRXtoedE zC-$W!nkc|#>)le_hRx#-IJnOus-BeN)~vujn}^&v4R`ylYK4dAsPnk2^SH!;m(@A3 z9{OYc8PX$8q*#ohsZF@Y`7jlLE2t09hv1;qGVysJozK9bNks8t7GM#EDE9Ts6iF1N zZs;7kESC0ILIN$xYN}*Dp3Y6r8CobGL}!hEI5Dj?@%ZSt((gNbK-wYW?Mk`63-Tmc=oA!>f zXgS;#OBPnZjNN{2HUVP1-z9K29ApFg)K`NI?nOI9r0C+(wHSdaa=yMDmy+-MHL~5Q z0c8$ku-|vMx9kWaxBA)R9C~cqUDDL==8W`6Eao1;aC^hCTPWD$RXsrYqSJ!j7f0R4 zB~yjg(k%Cf6JuW2S9J$MsMBz-E&aklQYpRt>&NFo+8(>1%+hsQgfw2UuC*mSTaLn- zFtATd5L}-zBUzSv6*gO8sGr{KvH({~y(;3303R5T3=;gPPV+K&rFfSH^GpVGg9QI^ z>VaR=xm?7m$r7);QQOpb^KCWH&Kg{rHM$1&R6mL~8W{7EMv-BCK-k3J&vt!JuLnDAg@+aL2Cq$1H^BIGtG+J~Oq)Yl3t-xu^lEpS zzAnzphw214*gEPB3{+N5pEtJl5>&9Cv^lU2_9LjkELeLOuJof|&ZA3{e$$Tsk1ULS zkF8g-a|E2*T2S2uwo+jD-=xmr9L$#%Fj`(_Qr?8nyhwc$w+UpKl7!+@9d@Jp*(04u zoyP{@u>(bvSdkHq1F;;^eskNC@j%viEr!Wq0H8I7+M3ZQGJ`<`)5d=i33aB91P!JUn6crH_#I7iU1r-$$#9sdU4h#&; zQ1tu$>#g_JbFGAP_c{06d(Z5%&p!L?{o6bh%`A4b7aTVvU)%k3+)z;L197Y2iucpyN>NX%gM5)v4fpQ|Gv`Zmn+>WcXdhS4=W-`S}k zv?^L$x|~`LXY`kt5aE=xFYs7>@m_RF(R00T*C)KI9UgQaq%zDmko)KmQU0G8>+f#x zdBT?GiOt1-Vs*htZF zv=GUu7yo`Vbe8{@8M z|Bx*X#oKMr9zWKz-*-{M&V0bz}%M+h`3(J)!MyZ=<&PNa(m~9FoR@ zdPFq;a%(k~?p2g>1%)YJ)<9y7GYs5PYHn5K3va^t4;&3<=*gc_m^i`wakHv_%3TenMS!c%=uj&unG_d%nV=D=Fmy#{2f6+Ryo*xU((p$}+u-cAjJ%L|!%82pf6 zB<2xW=N;2?*i2BiNjAxmJh;UEsUM!>^TKE*x2zvV@JIA>&bEH7N1WsAB;58fCk=_S z9|jrxF8AS-;Z`A({!veSfqle>tAR6QwPeg@;bu@eZ)cZDl-x1BEU84f#;*Haxuc<4 z!pYqSu_uG^rWJ63MFc|AzW4Mt8bydn-+Smdu!elQ9CtqtS?9X3A$73fG#n7>Pmddq zPeS0piMK6J8izRS!FZ5vo^$MOG_1sFam%}NSKlPLrY;GgmHM*%Ms2oeQQ6{B*Wr`u zK&YK|?NsBgM$^Kj(K@kdpB_yqBPcGMN`DL(Wh|`|lu{YslqtgW=~wm8Ov(ozlOA3e zJ4~PE!su%tve+gfU(>rfok7q(e{e!bGcxj%i^CC#FV&$Ogx3fkZym|RFe`2Mm=Wm# zCYW(2kKWN+%7)Qf1Oqn?_UtrFdYIiJxF=9Zzq~N19L@<@I*C-_naY2j;}MNCgT0L~ zWO}=CV;)8yq^IbG(V5mlI!9knD#~O}--7ooyfK8L6W?_$qL|&1+ey#LMf6<(pFD_H zG!PCwnE5q;(C39-oaL!GVf0-Sp~edk9*8Z~>bx*()Q))YnjySnw(UA(a5pglCf`RY zDfow(tWq)%c)S~C!WI>Cbd1Pgvl}a|UIodWk3x!#LYfNujL+iEetqv?=@yZ6EtgFZ zVVEd^vDrP_kKE)~dOHHCJsgJJB^;cuNoc z<#*#J?KW_qK?eO=)^#nj6<_0x_T(DBZ?kQ3J!VYXwe9YJTWF*0_tpyMHGi`=>!yc` zX8SixwW(u`(k5xHNV?0dtW!EyD;=wjPEz9vq`RQG9<^U?+Sa10SCivMDy7^^aVgY< z81xjrj;AxDeWGAu$nVugeaq%VF@U=eex`# zl}Ein!o3to5~U%Odtd(e<_Hnjbq*vkjCop@J!bJ!d0;WOv!g_qfb&ZaDDXy0azgO&__i#AaLY*sYK=L+~usP z*A4X05TDf(N1TF!NQfq3!}?*w-g^BPgsba3y2w=gut(CDyr1<&M!dzCC!QjnwrsSl zvpB+7{N$$~QWFDbP0MdX{rljxrKjz@vlG{3g8PuY??}YeRz~-&&pWN0enO5J6~kmj zR36$F>u0_SCH!d|;1VR><+MkEafvHPkYJ5CQXCBwDq!^YEy8UxpjY{Z zuhQmIa7(&gW@?SuOSz54!6*ve*s7w<&YBe6?5s&`lePIu4RuQ=AGb=>&DTh6pPiex zXku=W=<=OEebO2h{nHl<;<(iR5+b$PV);MH{H7_eG!6WXI9_er9qSL7+V4bD*fzeo z=55@nrWHSsQu8|S$7Sgh z$us2bEhYr>nq^4Hky)=yMbb(pekte`;EtVdj2X6{2`%ZZN|8U@w@5G)RiTYwpGbOM zRNm%XOrDB2pkC=MRcQeAnG~0y&0+_b;7gu?Z8Ch~5Lp0YV7A^8DU^@!zQxFjzI^c# zbq3g48F}A=j<6o&t1k+}mMRFe}wE-03_g-mMPDGwOf z5TQ6ilMQsUU?cam-?aS}jh%Jv_C}4H<30V?z^K|gcwH1t0oi_8Q&{RQw<|)M2~iDs z+HO%(w(=gox_u1PY@_R2-;mb+a5T2iJruUa{@j_sY_sz(iRjzw~8=0kP$L42-7#BKaf4rlQl!`xq3(lHk+8m00*M z`WJCnCASs+cKt~H%x?{|Eips*Aq2^HzU4{8$1LtUTopL>jUtKiSAAxZV{m$a&)o9^ zD9AOPWbMpmorl$KH?0a4chli?uWfvMS1c^JD?0QL%%2i&F zOd0(VcLbi8vzrZ#{z#O2qCuelGqbJs%f@(%sxgB}6e8FzMErpoh=66vC~}LLdx5<* zhKvz7Bi_T@?EU;3 zL{#}_4>QSVurE9no2MAgN)cT%H`jmQ0b8zuaUI#QlE(+|ma3GXXY6V{$pOVyiihRr za0DFt;#-1PwO=RCIp-RMqd!~(AK|)FpekLWuoQr(fVRm)&A5l;Eb~Rtd-V>v5oRf9 z{dgoi-K(?$U)SW;g76g$OSt3RSLIv?F5%h>)h)%^_t(x(F^B@nlLu`N>Lq8(cYuMK z>NZkAzmi|qd&(Nnd%jM&j{$PjVeoLCoDA1I!KO!?c_BGSQx_*tNeH%e?*yD!0v>uU zKSvUb+1FFu!O|xd)hZ7257?p{U{QV^!=Ws%xZH4l8U?cs(l4qY-yinj2K7{rBeRd3 z#{8OB-Pq*clfQ@pBF&m&a3%w`{2e@euUweniyxLR^dGTvK;>4*CfkRr+A&jQF(Xe2 zJtx${^WPb1OC4l9*EIdhmZ96#yYQJB08PcU2scefIeX1okTO)v#`v3m= zhxBc*?;h|S#7K_uQB$jKK+p*0WN#rB?%YCcK5`gd<&=P|{%Pw)W+50&#^&M)@`Gsz z7JK3sdP@Qb@FoL45;;2AN|f41{T8Q;+^%+i`t?&mwH|t8WD)w<++7N{-RW=zT(hYG z7WvA%Xlmw@!E5_Az6K>`%zCQhaHECkxE1ZH{WYSs7Ol~nR4a|8NbQ5zr*FTvy{|tm zsXFdOzfRp=jWHqex;P30y5+VN5gN!r27CV{Ie;bCTz)XkujX%wPk z9TTZ%Cr+t+Hfw@hUylDfyAhP?3bzzuDt=`xj_Rl6nx?HhbLeDK(vJ(DbZ_pf79PSbNy(RS`@ky6igc-;zT&OsI~O!e;Bcz*b_^t1J^1FdgGNh~2V-IX zJgWq8KR#*6<`Brv1FKBZFnO2%K&JbDqr~aE3a87Z6mb9HQ4C1VNmSD~ltN)qJvJO) zL*er$=A=LVnNiHhIGGIhu9;zwGPmfDec)E<4$mL!+9|YN3gT zo0mGy$IV-Ikd1XVYr)(6C7*3q%VSYJyLCu}7NN(@rBM6rb+^z>Z97KlZSLvar^Q|R zYpM>~H>}ZrezR}^7cb+ZQ*=6Vw}$NA?3Zu;8*5)R{+2p^A05BWn~*f^5X}t>T33yh zhPFLJRJEFpAY$$j=vXGpL0O8*rRFl(G|o+>mfm|Rr@Y4M#zJF+x;CZFRadjQW?AR9 zu%SUa?KG+pDnPdcQGdLK1Zm<3Z3bvYX@eWyX+MKn`WV|tN$=yrMY|_I--dUG>s8M1UjxS6&OC5OEpQEL*&g%(w7iZR zeHNb~s_Sg!Ip;I^jP!TcV>-=E+Qg%UDlG53^o zkL|}(wmD;r9Fcsmc+5eDe6W`0Y3mQbGsf!&zOxjPL)|y)+C6b(N>4ng~ume;LxsBh8#@g|R=y`Y0x_L)o z%0&s3+#RE1@K8V`$A#OZ<5Dw$IV!*7=qH%Adz@tF?D5l-H-4)0fh8DAv;p52A0xpR zUW)!c{2o|KD68d<22-le8=74=yU=nJw6tohZ!9!UH+>zXHl=+FbByNgnq?jHud%wZ z(DW89AK9!4=h3u*dPJ19gsUbQ6dLmsA{Qw_jMb70n8qs#O;Ni+;6)8`5j~{5(W|5< z?@p4B&ewua6M_!V4|l_WPb|EFjiSic1X~g(#XedZD3$k=2TIG(QXZ-2k~J;?qzQAY zkR7_czg=&v9$P~O zFLxtb^%jIN;WnPIhpxllz7)c3{II$=7vrj2Mvg<*`NYzK}Prkp5FSd!gOfm=Q`^j)?kV5vz5uE2CYaLb&FJz$yA#K__Df6AdE)U!sqBT6_GH`@ zN9ik~vk8!IkJ9+5YY(K6uj_k_?z9Eo6=l0*O!4RF`1wrLuMB_Q(Ct^0m-oTRizc2l z(Hn$^EyaUHbczE)&eOgKWTbkfA+l}A^5{5Ge>uQ!23_cI3L?oi%9p~oB|#<#A3va) zqVI!zD+uyn2duzAj4E1Cp^w3`^bvZBC$8!#^dpcWPxIl${HG3)#m#5-TWw5ftCYIa$im zi9!q=Udb#RWW&LPYb6gLUqj-0A$N?%p^ZBRtC=0HciJKy@o`n_~3 zw`)&)=O)C&rhA%7I(J*V)0AeDb2UZhw>PZipM4vc$Zb`n$=0@n)`GGcxOpx%@B#b*WptGUByff(%#u`>_#CQ~M zj^&D9!l?2OW8a%`3Nh-lo!`~)%9M`K|BZ%ld-+_!(Gw{0^gIW@fENmKY7&HP!9mZGPg(k*3xCYniGb$tFMt$Hm& zivX4O>Kk2L{|61*?SfD2u1?Xofl}hGC4uUc9gMrs`~B*9IQ5U72S;V}k=Nl3K{n)Bp8MnFl+@vyeP9>rhl2L4t6Abyjj2PubtspY$PLHJ%0AF*CH02@Fjs2{f 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 diff --git a/lingvo/tasks/mt/testdata/translate_ende_wmt32k-train-00511-of-00512 b/lingvo/tasks/mt/testdata/translate_ende_wmt32k-train-00511-of-00512 deleted file mode 100644 index 301038bbe535ee7de6522a88dd2dffa7563ed898..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1408590 zcmagG2UOHo6E^%ctTg&6K~M8+L1fAguRXqg~U=Jsg5YS{8zxHA<)K&0ZqbNm$GC z(m+Y%-zHY~xaQC0Epv4#z;iMC{ZG615J^+ylkkKVneYX-d>(oRoG+0uWNM|EQ6V5( z4>Uq@Y~QobSUN|-N#LOEiXRC5MBYRH2od%Enf&iZ$37@T5V(w(?PU~RXQn3w{Lt585t~i?ARYqAOujtCmYV0M;KeMET1j_Scpln|fA{-y(;&*Qp=AREgTS ze4Ynhfh`6_8OY^W*KIv=1<3=khM&c?61WH-Y9X&xv6Ds&i8Yk#W4+@C+>wdz0W{M7 z-*QJNSoUUAuwlFXKNM8jZrZj@rDM)UgGRgs+YWtqnA{W_*V_i)h5PJk?34g+zVc#h zTJ^=JX(#A-2{~e-Ys{cc7V_cP%5}Cq!7U0jWb*TWsRZi3s;b2MKCv>G<%ikxU&ccbj)HJ@7aBR?{&isZ z<%w`PA0m)_mubRGi*|Uxl%PirDT=waA5=P0J_1{i?~Z@|95y^ebmV(T>PiXl*eaJUc!6u%%QQ3m)qg7ll*uo=OJAX?GJ}-(7SRl zmewpx%z`4WAb)%QU;b`@7GH2dkusYQCsZ#WmW&;=#8PgKhdmumpWGyV&KUrn-~7x> z5OHs6wQiR1%1tA+*tji{iiC?Szt>pMwAI>&4!Q)jIWT=9 zF>l_WdCHqK?KK1*g%TYdVG@-kmDma^IK8@dGZPuxNY-wxeFeOOAQ|Bd5=Yl@D+~# z`ly{QM$!;{LC4a_WMKIjddFr-o7Z@ex%B+PGCoFS*k6iFHEVVXLe3{&LKvw z7&7v6`8+%)9;(n?@m*Uzg`k5_g4P_Dwts`LUJpLL*>~B8`O_C8Tqvuej*5@sUa8d2 z53cYku!Zb{U7|19OXQ+?G;y0jyvpK%y8AXbPNC+b#b3xB@Z8d2j_-i6Grao(su}hy z-3xT8oI_WfhfN2=;i5EWal1*LZT|zPqYopxLlT0jtTb9o6E@!EBE12|s85razI4d+ zx$@a~rb_5ZX77b?&~Cp7+GUl1`thn-T}ytSCc5nf)6dc+G|nJL&pdPC#T#&A(V!+G zu+92kE0x_7KWzLDw!|8PW^?2WrfzWoONX?6{Fz?w>k}TTFJ}L|>M?fxbnj$fPNiYY zQ+T?ko^I56EJkP8-5L^qYHRl7lh)NzKez=N^ii0bV(dYJ4LoN^%Ov>(Y#$6yCz@x^ zyYn92?@H!aWw7}s1)AwXgdZ_YR82TMH+PzrMe91WL(HznpXYq-YfHhFOCJm(Rc2jH z_~tDR|KzY39)cHKi78l;E98iZzH$HRv!+W5%0O8*TeFXSd9_s z<{!jQ&^uJYaQj5)5$y*gra892N#XLp7_x4f{4+dxQvW(8f+~UAyvHL7e< z#3Mp!)2Alfw_rr4uHfVh=RPQam=bth5AIJuWXH{9IS1JmDX~fY8lJtlyn`re$CmtF zTI=58;~?<;$A5Oh=G>)k$C58=+X>=@mVqP+84(7~LJER5>pMrOY}u-#vkV_m4D{ov zQijM2X;%ZzVC>t&wDK)dzJFze#IkzB0=Tk^fb zm*5);q*YhHOH$e3Hitg~#{=FlByOdAIbOOj3%LdYv){WK3PM5a?J2m4oS>bR^)H^M zbtnNG_c$#r8Nt?3)+Y^90O0@uzkhgS9F&fbUhpOVX8Fa2@om6VS>9$A6LTE~7i6k@ z3ZAr(PPq#V?~FsL6lu00^bR*}httt znu3S5y!bUL)4dG0XA$Q)ddTwOax-jl5mjzy`MzwKMetL&^*!@X39a{veR>VFJ3Bz( z7Kaa-BA4r8ZNoSueoZCMU0x0bDLkD@2#O|NFLe_0aIQ+xoHe#b@N8^4q)1yX!_q)j zw~s#|w;bL#0<)%25rY{Hiq@2CV6o#mk|Qrr`kN=91NRDzqXA&1j@sl_N4ug17TSD*kb zB_HTP;(C%njSE~~fEt0jmfQok;N4xwhl(tyL$)r@A@Mj=OY`B;MX1uLr>e|}wf_ta z=vVn}{8OCtCbD|T&CqicSoLNDlcDDo1??J6et_2C*S2EMQK;9nXVROeaJ~hGUwsFT zm&k9fp+w#xTEF1H&6|K~R^LP`o>FI}B#wJ{3RoG5U#tUmx{MP@kF-Mpao_z}5GfA8 z6u9K3{>9EnS{do|FAjick4hBnCJ5!*)%*={N~ z#)f(Hn(I`&EqicG*K9}rJU4ACKZ~0!a1h>FQ}A=uf9hwdRE^N-J&dQ5#r!z$I&v+n zd5xMT>RiXtj}|*lZ(Vaixe*Q!VEavXsuac+f>An~5#p4F$ zjgVX5;hh-K&s^Zj5$GCmNjhHG0F)Qhzz2qzLc-P%i6iErid=jgz06(0?sw+E0L!jI}i-FLC;q*z;Yh45vGaM9dI;3T=oM! z_|c)?hnOo2DXj))l3A1NXb^u`CI1Sqyo;_IJqv*Xz-7?GarxfOJX1!*Z<~-!_S*PR zvp1Wy6bdLulf!N(&J8gFn?m$~K1gl53tEqkgF-r`6kap7?6d1`M8|y75up{TVZGeB zb`QS%$*|5;!#VbnCow)V^seaGmOLuH|A~igW@kvaAzLhp1UDZI4XS{N3UbS?a^WA*|=Fp=96LV7#h}k`C7cXO6W!>!98v4In$Y! zfYPMNI^-DSAoinNis9GZEMioXDR`omj zUC`zUZ%#wm#C9VJzIfJTepofOnxI$*9H_|MJ}#MZBfOdL>@%V7iW@U@y-pBZz?nw< zi-*itq8ss)9Q(}Z^@O*yu36_xxHeNE z@a~`UTwKsRKb|$N(68pAFmR(u{=kpcD?ShTNZ_k=WOfiaoFy(1|2CWo0=?%V*XT20fu7;WnC3wX+1)@?aIa{~c77`92W(H&waFNv)S z@H7q~w0$k_OHa6@!@DhUv0j(y1*~3#Abn@iJrLG!KtzXB#}~!vOTZ0lOyJ-2dN+F1b)Q+^Q2Drwa9u7Jo&A zyhkdqda0MazH)zKI#^82;Jh6ZGfTg80D?JHx-sTG(%jhspK;E-#@WTD6wuXeXJ4oW zQ+BG~AGZPiZg4OgU;%VNKHxJ}ZYkvgSt2u%~Y3q6Ge+JXCK+=S9;!fQI>kfYGS zVdr!*;qc&VZv2>*0*EexcU<)FvvTNUl0qQC`ruuNkvv2%gh^seq1*5aj3NR)Ftkm6 zm;Z`)-!+a80p`EzoZvjyyeS=?PdZfzYytNrni0sR?2@Gj!hnBrvBOJ*E>b4-dl*60 zzIUxabl$b^*pm!#oNSDeDICYSk7_|aA$@bz_yBG!iAb)jHP)(0fS?R08l!#EnE3as zKWEGeRgJ#+kC{CJYPy^o~*(o#3D~4jw5)6gE(Fk zyL9f|`Maa}o*j^H^N^cT2%E+bm#y&otgxEHDT5Y3!StLhmvi7Gby)lG{>}~PNgdQ4 zBeGL>5SRva`q3~Ud2D?FG;$8@ zM8(|R3b-MwWwpv66)kc`cR&#h+|bV(w_1Op4%`0awoO=^2H{iC>ok&! zMZw$O2_}05wf*EjskBCs^L7_kbo{0oxbU~X%keMj0ISOz@XrOEeMPMPt6yrkyeL$P z*lU7WCu9j**L}bwG9nn&;I31`sjhFJ?k4PeA<^&F7L)};qHYAm;;Z7#rHN$!T4tfU zG#ep`XHm^Qa*OmeO_d&3nSdS**T+W{(8I@DWU_oB9zXE!aN5HiG&YVd*tdxlX25Aw zXoAw`Mxu@w5@VDQRDpJZG=b(dZC>WEdQA?YIq237l4TxCU7yu@tP4F`Ayw0*$E+<+Rc%sJOkGpa`Mk=85!(U z8Qga#y%ztV4o?=KZaP%!Jx38w2qQ(}Sgq4jn@3mYK_#2Y^QUf*iO1uICQS*hR?%m@ z;MTGe_QfJpKv^Z<1y8>C-uRke*uN$W*tbl}?%@=yO}pg3K&3nF&%5a){Wck#D`l>9 zmOo8&2oAo0={M%}{#L2TK86pe(v$08tx8AeM0JZotly2Ru=H7X=00dsF!E~VfZ9$_ z!x^$Df64{uOGc<@4&u_~6z8@<&2nu;OfdQ?9kQr45AlzJI7EJOsq)M5tuQ?M3G~RE4JeQ=TVEBX8eH@fmp*(L@XEo_&6j_U=lN_) zFt`p!rj|y4(@)o-cQfi6Rwp=>10G|JBF{T;G#12MlA#q|g6h#}oHLdeq-8e3piOae z(>OVMg^$f^*AN4z4;iRLRnwWcG+^#9-^nLDtx^bR(H7S*YzuEydM`ZQbO0^dQ7x2r z1^{!zi)?^<_TM&@o${*&zgk4IeTsjdr2ZOJrqWN~V zJ%91&ToQGXn38_D+V3#v{OCIV|7Yn^Wf?;IAd0#{-B@Jd#(GUe__s4uMk(BzeAoq$ zbrQ%@(5cH%5e`fu4<46q&&d9`#i}!>;LJ~ol?TyH;40IdNR3*9gZ9Vi%OVZ>!4ZN# zaspO)W5s?SucOz&yR36r)&+OCM_9BokGeo;d%6!U_W zm&f;zvm8Y^>Uv)w%V=+Q`W`&ieN8Ho_gVhyMlg=iP(=3@dkLcb_yeRi!o?q$mP=$t zmwA)hI}ydv#pA6b7E!S3c2Ca%)i{<-mR}_ZgBnzsMUB{4A*x4da736uuns>MvGF+v zaYsnp__j1DoOtoeE&U0o_epa6RM)xbw(&a3DNPq3##?8gOGqQ@jp_Y||j0l2l(gnk(Y9O2;? zBnU!tNX0G5aN6`cd<{?WG*K{?xDp`s7C4fF_6Yo6V~iJx)#p5joRgI_K%x#&Du5v>J(>6NN%`$gz<17A`=jUR1OqV~nJ(IwW2 z)5;|k#1Csuf|1Z;0=aVu68&c4S0ZjVo1y)c2zjAlcM2d>^g!J24J+xouUzWDWW4JS z#Li!a7X4fKHoT?Q@Cc1xS+laBufy(u(~bp#Zu9Hiioc{H^YiJT?X2tl1nzuM$*(H~ zb7iIT%4p<3#5?h#xj1d)^E^m&B3_>F#SNSE*aJ@Y!9j}TYW<(ZTq5~DiM`iL=Y3K^ z{5U8jBxd+_lEn6nh(|Y}I>lTX4cVyt{mpULZUO!B?58!@QMj{_Y9*0=R1z)7E>2cvCM6qt7ZtbgWhF=povA)2} zlQrCdv-1=9Wy4O*ba2Z6QHz~i_H-gitDHtrz(4<2MYn1M7T=%{+yVIe!4Yhfe}mU8 z6gr}4{c;p4A(bSOsXnJU1pyEz==te5t#d~9UNf=?S}y|A%4QDNp>I>j=<({vnNx(S zkTP0_cUj{gZbj@-H()%07Ya>0omMpSOENN2&K?ZE{*eEUe_gouGD3VCH{k=VHwI+B zSMyy)bxf1L;~bTn-;sY*&H8{B_4*1lL*_^iQWrtjnVLmfd%M=UvU0QPKc5Q3_gS`j zd+o1Lli3uR)_=py8t4RRm{n#91U8dE*23&Qdye>r6UIw(wyS2K?fTm9@m6(!(eTWB zcsA-H*PH3Id?+R2ZoQrzW5)7&ji|g5^PB8s3MGp9v|JuZi>9^Fw$3_Ow z(pFN==-bs~d{L#;dh}Fhd;#!RgVVlHJ|BOc4OgwoHB%uR(Pb#ZP$S=}Tf+r*yVgMy zXQP303=FHk4=L_IGVRzy(EHsqQK-qCS$b!9n!+4fW25b11A{|~JAK>G46l)|!e4d% z+jb1z9E78b5&zH>0=MA6N&Ozc{I=tQ3uw2Aqy&G70*C(!uZ#tKBK_*pJ7S;9aNPwu z#=jefKWNCU+V}`p{q;fgYOLa(;?I~dUA_9NmQ5$pW9tzL0v|@$pcl2#y%~aJx8~b= zA$GkdcvI);*WC9D2>!yZJE9-M=_HlgPcHX*jS>SB)(x#X6A6Alldb-nO`RlnnE!F$ z_o5Qy_*==6=>#$QoW0J}Ng6d9R#0G7@mrVr2~{J=#EXjrsJ0r=WZNRDf?`i{hbi}Y zwn;jThIJ-G+5p*Mm&wi#6()HAS9n;)p`-huQi?omh?SqfG*BeaPm~dfOROC{I-tK4 z(oKpcvABoVWrNo6GrS-v0lF6_sQZITP3;G+y_4aymN4?Y>{um8~)hpWf9Z>BKiWnE`;IH%N%XKKY=vpu4Q`ZdLEAG z(Vx>FjvGeDeSdRICELw#I{7U9Y{PAD$m992q}mmsf35%!DG)sU#cMOet#d-d(QOj# z)!`_X4DipP9kf&a1OD#k&K2i}1*O6CK0aYwg-pu50~Oo6zaM^FxQ72|-9gDmo%r@G zV1f>uD}ipt=GBI3?R#mU9WA|QwHi6p?@<2Y9SPUgykwMm1#WZBx)2B?WYRUxL{Ab@ zCYs zOT-!Io*xr6&f>~vu9`=>!C2s#0QWZMF6=o2PdA1CghoE1&K-g*<7^Tr>AGuhX&?!*28durQ#0pL2`fpdz>Q4@PrcW%f_1>bq{*Mvv8O= zKmdu+Hh~Kts0L4+`yv3}2kZ@lqjv=PzxeM^dxCM4ZVJ$M8J1-7N?N|c+c;%%`;sy( z0(86{eI1hMLKhGvJ^;TBr|!e4D9|pt1ShxF3Hn8zL4cfm;Pu>#;JE69Re-H_^0Ev& z*KQD#|C5(BOHMq1G*0Dt@Spd=K&<-n+&MUcyx6FziqK2FEY?Bth!U&Z>9cN-JOYV_x5Q`rTm%0%L>@aHonTM(Y z@82Hoj2l1M=OK=y7|Wyc`7Pi<(I0rzMCGPz6d&bi5gJt?mHXW$masO8Xyhm)IUdRN zd?`YVC_~kp5uFp><-n#{zLx0@?D;RzNel9U4pMzt|GNjP;cHo>2r_;i+UMKkTkxhD zyGLA0S&A@~;NR`q<}c|e{%U)(n0N@^as9ig>~SdyVo|P0AS?N~IfR4;lnL9>4!xG_ z`y@aWvjyjbcCtUkF7^mNu?&NOB2tsSbQ74(HLJV@*BuCc;aqVfTs7i@YpNIG5Wt@g zuCTK5?r1+v^vX!WULgR%(d5iGus1+L!kdi0$@Y_kUN9kmnA`SHc{VLL5<_{VKQJ@m9Nf-+;DxiU2JNmi#bK zMym9Vqw_)ut>F^&suO(lNMt)9Nh^#`tW>LLM1T2c|KoAO4vmWpr-L(1R4|V4Zcb6} z39+J7py~H@7viQlR5im1!DIuyci=;|;SV?0KmS5CiSGlyh$1CqHBcc3miK91p@c51 zW|qL{EMZMzvmxV^b-j3s9IZZ0t6O$1tmRqyAo+_{3YHpE%J4osyTwV<9Aav>2Q)chi-JD+M401VxOsFJ3k*aN88MBkVOgd zfX>&1O7&L0?4i*i5-z=wpmKv~-Uq<7JW?_-x#|jeqvSZ~Kmi}!xDAXR{~lLW2ce*O ziq4sG)l|GoJhT^=D0F?iFBi)f;`xe#;|ngQbp14u)66`9I0IczhPUk?%u0YHp6isk zzwl1Dz=kLe_xV`OAebf!p?D#dX>e7*i8uo*6@`jCS&=>BJLsEiWp9KoL7^wXoS zhm(4PwB#F9Xn@_Y)-7lB!lEe#M2=Z7wOUu~KSKQ4>HkoAor^5^?V8d@x`M})G;R`s z#&IyGg*i-F-2BNnkYL~w_g%?Ui_mNbhovm$6MQp=n?i~2j&9;hF0;Ziy#e6#UVt`Z zQn*u`hOtA|Dw&s$gu47tQ?1TLIS0Wwn6f(O4oKO{;#(y(F1=C}F6kb)GpZ#{WET67fWS;a>u(kJA6Qz`;6Y=%67JPSe_8~+|TS<}k z4m5zEjQsHoMUP7Qg%l7daQ`PBokrHAxIMkNIFICJdG^Kg>VQJx0-XhpnN#{ z))_n>z=Df>w6h2DH_~CZ;FaNZTm&3NVoQ?0!F0w!J{Ql;;Z&3Brpl>3?# zNQnE7f`Wfv=xaq$kz*ZM7Z+IYOtV^yRvqjPp|X0G zX7&htTv28Ec&L?1vLT5nfDGoWmiR7nACGGStuun;*!kGNk1$xv1Ul@t=R z8z_Fg#H@z3uxfn=CyT)MJXB9Oqy*+Y8YDX3sBGT6E;k3SQ)gqJaYBhx`$W8Ktp^aW zLYxMsm(AS2CWfdV)P?rW8xx%1@Eu@krD##bh^#N92WhnUjaKq)c=>?L`QLXW^G~ak z9lg6ygi}8`sJF`C6cWFnTag`M>$mGXH76lM*et|N(6y5sBDk=T-a+0$?Q9<6YB+1< z-~`O09|PCRI8Om~ZyZ`vB~zm74GF3GNf2OX!B638y-kA5XgF)!83L_gBzz_DLBe+t zNYJ~|PWQK3_AVste6Z~B&s_`>_&}RZt?3(?xjQY0+1Pvzv?B<3y@($_@V#mP6Zlhq zA`}BP@S@$APFc#KeK{c%sZwT=-j4JkdP? z!GTY^!x*|qcu&Bo!4Ll;3e|6n{OSL`-yOsLg+rG`Xz&kLlL8jM61N|QJD8M7ep(-T zAwi|den*%lKKaQSRcmmf7V0#f1)-B=(0MpbAzx09?jD0HOJabo+TxGe#%d^Nl>Ja( z$XvymW*pSaYx>Gs5#95X=ok=5y2@L20WvCSb3bQ9U*d$PAw?AQ-% z4uwXV73&5FS$>lA!*FvSDj*Tcf4oi;XFK2rOvR7WDH#9FMgw)Ue9_QcE9-(;YfQpq8Nu5xq{vqyFmi*G}JhxeK^3-~3 z{wwUg2B}b#s^BkdQ1FU^e@>7OI7o`^Yf)a7KV~l~KrD3vp7S=79U;Q10BHKQ zVi)oz0%ZM`K5&115?BwA2t(_u&0!e+JE-`TqqkuXguVU;E;=Cp2mf{3C`Wivg!*W4 zsfD7JuqIuGdMCUX&j=oBIf1L@>#vxr>`nR#$DO`s>-49z(HaS?B4)H^!U4oy(iDV_ z+VQ>&&P==O0n*&1%|}Vj!nb$V2Sn`( z2flZ3;*>m>JXA>Hr93gO1N!KI3viv#`94~MxDT}oqabzz!`spzc?>~%h`Z&%X!aV9 z+b+9C@x0kfu^{5y)X~T?eE{#sbsgpjilyZ`;yfQf65$2T0M?}2ISLlaccXQFQ@`=l zLem_edv!TulYZ>_5zR>%FVZ``A_SFB<0Ws2mlPwtOXv#sb~E@j0Pmc_7{a5T(xbTR z)C&NF{&gLKQPX3{{s6%(AVTa^t1F$r94DL{`&ERe+^3&h1J5?CCP`h`Sj$zuW+&^w z(`q`xJ>2T8`&Sjp`t-fhA!*!nq^rD#K9B(7%=24~I;y38VDw!%4o2z>#}A0^;)sK& zx4dF8-6ivxc+`xS#HUk48p1==1?bVa?9gd&Q9P!S_NJmNBTg$Q2}c%+V~m_xZ|Xh| z-LOL3^I{JjbW)!uY!dSxvzkY!^KzZae9+;(1iSpHJo+l>J8)4%hon?D=(asU?hb@K z{Q%+21FL1HX}RAAcuICc(rtLK7vUG0(m+duts<7y0x7DM#1aa&-0Y%;-}(SO{fnq$ zska&V8QZa!$^J$6FDp0ws#ucw910?A1YuVI5SsFX0|7_i*v`;2SWrj(g^xB{0|LAZKX;UBq(e5Tr@pzY2#Dp}Un)Ex_hV5xHdPG!f^VW;aih+@z-zfs1;Z z+Fg`S_iK_KH0R}fC&Uk3+^E?s7)K;Ef=a{wZ4a1Kbj?=oi<$-ag=8B(PCe!c(v~KJ zy%Qu!okV7@jLnN*D<~;vCkPKjt41>Nt6$A=)F))E!|Q~P3c)QK#Q*(+a5t`f7ra49 zHg>_Kw4%&sAoNFO_tE0e)-Pd9ru&GR|>O2&>gI@3ESJE?M+D3h_ zCu34)r!4$38@<%@1+!G<{>~Zr6isrc{d)X2W29lseXL&s8Jqxt)PFn$@1;@)7nQwd zP6xDNM@6^9$Dm8e*zaD{$Pr(?4-4MSYF`sz%OufHmv6rSwSNkWtzE6kCymQ*gK8>M z`vh|S;jnFo=?P1}$+@Axc(H+hogY1un5XVS-Soe-;3uF0UHeXSR4NZR3#6Sf4f04= z;4ulDc40@W=Ob$Lb;ByIX8JM-srKNkZ;FZ7ltiK=aH_xK-u29aOjpb{80PcAhQfV9-d`bT=AkHKKdO!*6_8rEd=hdk(@ko;U`I#{+}5zb z&Ae~^LCM1}T!Nz*tyw|*t2giN4ni7I=SnWj)kE|_&Ix_tDT)(t4OZ3~hb?<`4;Yzhj*9|DPg?eQr9zq3H zful115m?{24~H42rx4^rh|a^Mg#>7f!^<8#Q;>qf!6E39N^Qoy=fB~LpMW5S*F@{KLtS0?M*AEOB@Gsnk{;`j^0i)7v*h28khUrMkFQGrTiRDFf^+y1wQepigub{$-4qV6GjnF;LumoCw7ymxc=oIH}|TB6ccA!g8xvj zm)}D9JV`v0O|x-x)8+XxXgrh1u?zmo=ha)}- zUkxuZ(?s5!xxZxFppp4_4%20$I zD~GI7wzmaCuo?e~Soi5AXgv-aeqFKpAVo;jeG>^)f)qTvIf08Z++e!k4tk?wlPT0n z=hSoVKs8EbyE&<=W31!IiQRSo&2PynYhQ)X<8Z>KUhdY`_q@lca3q~1w)SUt`)+>! zwdn03xNMU+li~MGiIc>C<+mV^agp+~3nAe=oRp~i`9tjwU=^tA10BCa6Mo?{Qconq zP`i&~v=hSs6%Knle41!r?q!>S`3$F2_EBiZ=x!Z4?;J2Mg>;l~nkezdD9gi~nY6+k z|81VHsdjeMGdyJ@&js76t>^dI@8VbU(MS?N*Dm_uCNJwVRu&NmDT=&M4F~#f)6kY6 zVMIMlq1`EsLPCV6vsm6$RBA^-{%p@RQ&m$|elB(e=^j$QoP#0svy-soCS7DiT7=Yb zAH&*N%;eEqg2?noK3skKiAq+^VWto_EAH)&F7!M3w|HBF3+lfKQC;wT+zirka1|ZY zzNY;~5bFn7m~Ft~XcXxUH^eL{j^E+7jON|s-#1cRLnoLi!fx7?dr~hU4D6o683K<# z=Mo9^;H52$M%9JDAuQb>Dp3RQPeWU4t9&!wSijg$QudRpjXBvz&$O=1%KN?HPmw}a zve`G?B84IRo4g0hB4@C&avewAFi>di9m_slzpHLt_bR)yf^LyQndPdm1aNAE!XKDL zN@X>WwTFUI51;W%RKqULVN!@^Rcj<_5%oYjp;R4QVWem$8L;5?j3!*1;G;P)?P4Kj;I3Q?0OP>9fq zwUz@Mfwg4=5b;+CirT@T^Q*enT{84D_^E+9DW)Jrv3AH^y zP8vO(;O)wK=14H%6yZ)`&oQ__SXkac5Io*_)#-hSmOVApVW^h6QJuv(YC9W7-F)x4)`8K>7_z?_gHsjkQ zFXA85GQa5vN9Q<4!GDWw3g3&XhYD+@Aio5}X}lYAYfQx^l(eDVnkn2AYn*20`MX}T zCS>D2MxBmCV;ZtwG+hP}JT| z--nY&p;VaT3eUEjwqwSHX!^uJrZ=>BfZkHi?^-R&voS}+WLrLBNNpb3geq%Il!tx>l<`Gvztld0x?v>3T3zaBL9<)QQuwr|~%m`%e zC-6Z>{$cX>Ow@mX&I;Nc;i4xPUz(ep4v1Xs$I>7@6kch+=VuSsA{{H~)V!&Ff^1>5 zGbAY93Wy0kwqcHHhEKbhN0VZfTIK0Jd6Z$AC=$=Lt)t^8WxKfgecEljI!Bnn|NJIh zO^R8gAy%q&B9Y;t9Y@BVs4^Ik5#$| zrQ39_@%)`RAs%3xC8^8WCuih=M>w?k!m#iN2rmF(5`AVt;kYb*6E|?on`Tf-N}^xx zC-ng`Z|aTH$EdWI@P?a1**lt{370J1Y`(e-*X#4!CPv=|WO1EI;o`PG+~eShKje|p zc9%H?*6Y$|sIx&P4-P}whnfvr!?~zWu(eFq#6c2GIt>M5-dnB?`RTb?Pd8SyK?iya zEsB^$y;2fL9Vvl%N#MtGN{5mV@G)o;ou^F5ZDo%Kc+a>N5hZYTf#Y%p1*{pE*Cabd zM`{Q16-D*TrC&2%s`*XH{c{!e`1B>26MBm;Ggs&sR8|XBI7~*4#(kulN!`{UVl#BR z3E5WgW}m=IdO~laW!)uMkh}OAAyTP7cF@AV!kwGK{6eIhqtTWuo}?U_cLaQYhKr0| zO1WOrMvdcykv3D$X~TU|Vy)LKTewDLnW|$|tbUf%x3I;W2Cls(>cyjC315H7SF0g=hZmB;;*rn{fipiOaapvdQ7P_9nc; zS2Z6q#EzR4(4X1`WLVM<`BaTwc_Uo=NrZ;gp>@xBH?YP|Q$H&LeRM&%FovKY04xZ-a?&0A>MR84!bONfPP`h- zYR=>?E-!I;wG5pMC3yw+u|_C)oaM#!D|xe3rtRzbDUtLvsdXEdIwHpCB`rR?t#NYT zS|II2gBgMHbrz3NmVEN-;yDeY5+dOM#67AO$Ev(|<}2I1IGVzzhy3M9kjDafpCs;Y zbOWIgcX%47zq~nqJWZ%-oKi?N;Enzz67ll9;oR^GEZyf01G_XN;X+;vb?6*Kew=+1kDG(G1gJl4CuQ^fNmdocM3iOgq}$nwYsohVLwJ!nWuA#gtT)u=ur zE}{91jvn(klDveG@&mU#1M(%xe~|XQ$|EqPcuXyw565C5K;h$C|0nXe2l6|lqz-qD z5vi<3FrfkyEJ8U)!YQeXW3gFhSTC5mqK6sJsBf<7@-;&`;NyDg(4)*VGTQ`}Z>ZvTM*40o4*# zb|sMP1!?GLbY=YAC26Ed4%synLB{B#yN)6O8NDZ8`C|&kOb+z?R<#L!{mkPB!kSX6 zDD}vrZS4$ex0h0hdqgJyg|QcD@`6Qg8$2}QBwLf|QZK|QzcHzHyEvH}GT|(sS4=S= zJ?!-gu779D?amtKIq_>*6B2E@P8s(Ty03{GquRcXTe-B3{t?|$la=u`M_g&B%DpT+|0 zG>7j~3NFo%{x!I~pjy=`Qfh^!Q2ZRuQ6q*4YeFTJUEqZ8B$ zKh*NV6$Ystkdl8zBzfo$?(Ymee=OEC6>qZ&*-6ica^^-1|#_=^uSMU-z zjU-O;b-7@v7AL7K{H-a+t%OG?qvV;f+2Et!z;a`ZvY#u@hV{^xlx&gw=_hFdHCx?d zEuUKY2Y|NVwr-Cb7>AMNGRTYArB;%JkoK|i(bz`$#Yum9 zgTpF*hdFuFhrM$c+#K$44;6#pCi*wc7n;8cZ-IGd30j2wE)xKM4+4&1@078xb zm;x%PN5`%sL=B^l4G;aJY7DV@&ydwW$iKtiF5F8>Z>jr?FOz0~ z|K2Ze&%(SRjhTf6y6{uL|oC6QXv$AIs3BguD;@pwJDKd%` z{)70JFT%1UYuz>2jod0tz_@RxV6jp49&KV`n5nxA-}tUmFh- zZe8zhv(t2I1&3F-<~Da6>thkkpk}m`wq+*T$>;chx+t+7?md zof%ki>M01H^H1?PUP~`qlS&Ww+8PDs>lo#Vc;zDt%!GpS#jo>8GW#B$8+Cungh4uIZ~nxh z1NqTl9520N?6ed0)k2#m36;8y+{H`W&?yf4O5fpgwp{ka_$DDkPQCU9qK`wF@Y0Cc zHUGpXU2aMptFR^SY$JP9sTt z&CZdBa!4sCOe>8?dLSm}9)%a9J%&BdL`5in7Wd60pV6UourrX)7P+G7u`@eKQ*RJ8 z>nzm*v=1h4p89CA8E{4=jMj*K#eCJ4xu$50@hY!HajYN31_slGI( z{yp_V)Y#D5`U+Z@r$(I-~xthWCg=Q4!m zq#*Rh>~*syO&fQ1=)WD4Oo(skR#pKgp?N{ucXF$NFDm9=TVBVn)kO~}lM;FnZutP! z#FHtWo+ZTNT@(LRyM)!4pU1xJ?;=rg<BDR>eEG?*XElq&t=6`ibGVkpJi&fJ%ZR@R*g!=i( zvkPOJI4Pnc9!sZaW@awV{zSTC!yj=qa$5~wd+*ERZkkJ z(HIRgK|+$4D-?Mju~taGC82o~BUZ$YM31@`n{*8x=$6or<%;M9M~V22TH*kCfc2?K zGEh@e%%KMwUGPkB^-jFX=rUvB$(KvpB!1~y$uwc>wf2vr&4*<|Hm2R^ncQWbaLHd z%e_8S>SaiphXgC~U{MJ~iEb<)SW5)8A`#sZS)QTp8r&>`v#h}46`MHX0ZlHQ=tKp7 zB6J3_=`nu&1(IbA6a3Y13?i$+gOr`sJMV7TnjX<1S;d2;h`Vz%uP_WW*S#e%^Ugg4 zL@8UbE?56C%_Z-{l$*?GkZ_xrc!{EuIFFCGkI?0AI7vpo%J&CNC)HR~AgcO1bZNu* zS?9qxm((C}HF=-Jjkbw-3&*tMgaZiamO$5Vn|84UuCD~GcjiyXTJz5w?DxsB zK8S9=fI@E|X+qk#+uSq~MCEEDPqKylsSGr^$nvHhxql7wN2l;oq}vX0(ff{S{* zg;jm;UBSb``aWJO?GT&|%TbLZFXUer=Sc!8AXd{{idwH^p8TxZ(vKuwTODU=LcPLp zA%YvJOAZ3*5kcH?;o(&ZO)KhD*<}=p^?7ik+0M*Wd2>pr3a7LEqk@YR1A8PP1Zc%m z>RA3VQkKmB$a0UQXX!d#(i$oe#Y7e4AR9yHR?Z{JsaVE%IQk$os*8BZB{t$?vOHRk zY1Xsz71{V;?F+ZUuo za|Fx-`re*GA^%yfN|o>)%Msq!-U_J;=h9S%NI>P_rtzCG8uh5vYE~EqWHqTpv+{*= z>Sf}y=8>ClKsY!1`d_1!Z*BIvKUZNSnIosqpM=x1s!As2)G)=+H=il+KM_u`-dJ33 zOLS1y^k6Cv9-bb({7>cSW{i8joB5qf_?TJ)_jrWb&)gApj=m=`3Kz<6^v|cqL`o9L znTPO>oi@3z-v0z#J`XusCbKq^iMPU`TgE(=ZHfE7>=qbf`u*?{{+1)8>LHZ;TQVaZ zXG5*7-9-V)R+Q{co5R+wjqQ7c3Va#PJV6kmfq?3Qk&hvq$YZv8ju2kbYHWuqULbt+ z*XzTu^5wLPc6S6FlU;dk`1O8+eSc{jLzw5JMfS*O{ZaEa2+)x~nR|}N&kZN-E>D9& z5)lW+w^m%7c&ZNa1yBB^-PE(NXS-S-oX^?RbAwH+Frt(`DrkfAq@)s~zh$Fz&J(W* z&+I#Ru?FtE*uO6|Lj@c2;gFLus4l5#<@&3QIJUK~?8YLsb3cl*#kX=mreYpQn{Udr z@ zu{=rh3VVlrGIJq+$j#n*K(LW)s^ho$S{VwCS^&MV(;1}hS|`QPgX zH|Xf0@jLfy(0ns7_7omYo=tFK_9cQ=2$nP2KKx0Nrs9N^j&o>@3?>zEZ6`;`FZ4pw zDlKmsRo`69@&UboPDq$`1UjjvTR^CNYIQ=KrK;tkb3*s|L>TLWsq0||kC|&?c@T+| z3y?@l=cqK}$o7lZ+2nj>bHAvgzO9$W#qv`T2~Wn&PcCGO>mgE0d}P&|WaQoX@YJw2)r_eQf|uFwlFirF zdcl`}7N?7-3> z3K`?P;L~8SF9D1*Q6V$#-;@hnnD~^wr=lpdJV8~!Q`J9Opp|YXOWODcERLSRH5_4= za1~}Ov35xdMT8>7uYNJ`V2r5u`hsjYIucbN-1?z*eYNss-TfkY@dF!W#gNV4o;EOC zSFy}QmhM?n$M-fN68iIR<~U8#GdueVG10jEc@5WiopwKg>PM&G{>mNt@nxnbnMA^P zeBIOjrMH9m!|+%v^u&V0WuC9t-EcH&+*OM055s9PE@T}5T~=>5LMOGh*myCbANq@5pllu^#hL zkg2aXCOmk>YE@oLiCQW^K?JmnPSaxw_dp@<{?7ApP-Tq`jF8kf za=k$~?>YoUCybTi zpI6s1DxWpC+`1x{E|Ts_Iyzuk6aSPWSs#P?%@)nuL)6lKlrWjOpjC7grrWAj4L-dI zPy0$>*)gL$5MFB*N-;VkiIu(*h&yIfGRZ1djSz_X=c7}uPvnY)INm0g$Z6fQp$9CM zw@#{{tdr$f6CVcqO*}?SAz_gG!<1&8;O04KoH3V*lX%dALO}q=0@@*LJSx`y%XfW^ z>N{OIbw=~J+Hu%NO6dz1;k>FO8tWz+1%!EIa&(>8lX?4HHj3jWGPzwKZp7W2OF!qm z*OcG&R%6e9nJcVZ@5^|jfK4Omra%zZfF-uW3>wL+-#3I%XAIc9(b7zUwOiwcm8+aZ z`QZbx=p1iH$w2dHB;Ey}rh+FZybCX9yg?u?*>T00?4w0xxW{KUeeHiM^_DYBZyL_n zqH%hh8~E{d$WJB1Ya8aNS@Ptb`Fo)lP`(M~2La<{iT5e6c+Spw((7q^U@Hr(8yg zW%JYO?SCj=#m(DecQV_#BmZ#`a-^QGtKmIF7lKz{|6E*vV89gI*aeuQXOA~ez1!$D zt3TvQ@H_NaU`^$y;u^5%A-1fc2v%X?YTfddqh7!*s5%a*MhgCcMda?*&B{iMoZj*q zzO8R+j))PJ`wUJv4|n5{h{^A^MH*q$m4w2+vtV(MofU_EeLRX8>o_mKFbXuzVKs&( zR!-L(1mlGy2PQG~p)e(b*DHB0O=J<=8vb3S|0*wjt7PdP%s<>+yCJ(p60UAly@8?{ zVjaX&zsyS#qVr0$h}s{EDIetBP%m}-;Rc)a;3}_NnvoR!a@=jFcWt0$@{oUsIf+yC z`E~bG!SeX`w7By)y!rXcEf}R1hP(51gVK3_DO;likh%1I84B)Z+16Iby9CR72Nyt`Z(_F$Jx)ds+2Gdo2eRI!RPuN`j+Z_L$c|Cz+0MTP#O)w zxDjonlC+yXp~iPFqT8@o(y9xMxM&r)?fAvI!8p5$;tV{fPIJFQLUB)$c;raSoeWNEyZgGpcD>)2tzJa>M&#}6Juu2; zY$g_HX-n_Ve?8UT=tGNX`+~suU2x-+`(0=M1H!wO27cyj=iVx+f5~!NxAUfxPP!^{SD)U`B00(YRKpyndSHgA`}T>PJeKRcYO`cr8vBFOyVmyl0`S(KPMg z$jVUGK%k<`ev*~)o&6EB{RDHAFqK~yUWK5Cl?k{mO@&wb!BvpDtzq2N@50^S=Hy#H z@pG^eVq2tTutUb^BM7;!f@}TVP0nu17=35Z#Z)-3j34O+w}0Xva5(4&$IT)`q4uYD z8aD?$7)S0IDbCU`uFUDK;;8yT0n74Jfq&op=HzSrQ@5SpR-6iWlCV%9`yEoZ-5$j6 zr4+}U8C~au=fw-&Gmo@9ZkD>OG!>5G1xDW*l;8%DmiMjL!`Vy4xj`LUV#VEdxHQPb zv4X3U5cGYKN+lO=gU3{##ceAkXJ^~xW82oFSf6cJDt(eTnmyCq*=C@5d~|}mf*!%b z-jU?_%bx!;TRGMpj=Pp9;o@d~E6?_jGd6=0K1cZWW$etj zh6#dRa|2i*$PTi$jDS(<8;Cp0fOC3reoda?-)@eX>TLioRQBs|v{M73+>Peq9J zLT0aeJyeTcguppESoi>8NM=rL{iZSDTH<5D;3|38h?9}u*uueFH6=$1S1am(=D%m( zUa9+ad|mX@cqkj3cnry*;<4Z0CmeruU6X3LBKXKwyKr zEzTI-quhrGumoT50o=rcby<&tw?t4Y97~;$2;9V~zkGn1!Ys|faXMwZYg+g#c8|bS zA#ANyJ+ywmfPTXlzgx3eTonM~Di1Pv0RlUdG3WTs67li1 zgDZ6cXvN>XVCE@YHu=o^;KGGHbISYnmSC+GQWDHRPh%gAj=*g@oJX%Mj3??9q;&`> z$KkL8k{H$1p0q=o(DVY@q6#iSW%pFggbZSra|DF*mH-7<_v(UWFy&cK-Y`Mmz>2bfE&D!v3uC$Mq_qJ)qA<@~`c8_exQDP$-vlYG zD&q;gkB4wE4|p$Yft2x|#o80Te&PzJFN|K4@e@t&X2EijavZLxPh1}_V4o2p?J2cz zUCa~tN*6M>jjlUy-#CU7PR^j<&FY>S6Kg=$L91i`0+@OVc=_U_P z!1B3L{et~aJ~XZg1&6G5Hq#k1zeX!nGKil;tLBxFE6>TO=cv*WU|v{|WF*Wa@^1dC zO|9Xenr*`ZJJYpB%IZx7)YB*|B9niZ8jBms;J2{P_S#P|)3#OV=$7DD$6`zR_RV3S zhunH3*P@UHMrp;&Y)Go_ZLKLc+dgbR60YV;WO8O zTpu@5cGQ48D1w#hrK^Q+_keZ*dBX-fxzhA_)wanep27qyqbn@~<9+dfdyCJA1#)AY ztmqTGP$iL?-hyd`!zEs?ApCnCSoRnAXnc9Mo3pMm!Q{V|e}BL1nHA|TZXKF7{X`>* zmHUbPbq3pt({yL5J*3!dk4=WI*pK&Kn{_cdKzjmuGSnu6x7q$_C)}{Y^92<_>AQUF zhcVNBV&=W}zGA?1#05fDUD=OzGi#Z05qsQgQ2riIc>y`2*^`SjSy))?D!ktveO+#| z-X>;47gq_@^Cf_VBd}s4=Yb1e;D8pl_Hx z+2j;uA4M)g3r+c5gpUuQQD`)dy8TNX{8m{E%g6R=pqTp>ty)0UL6nVl94^GqF#Gm% z_8T1E8pBM7M=LL3{(N=tF#jnDygd?aHvF?KQk*JcInj66<^0&M-Be+x;8L9c4|GrJ z=$<7tLju*a#v4aFwGMiwY89k+Z}~xCx(bnas$(hMC?{l;z&=cN6r>D#&yB!*!@db3 zMz4ilOpEUL8?saC4<4YvIG-l3g}MCXHXwT_%;~N{8dnmp*Nl|YxqOtB5%d+4KJbGn zVeICNn?~Btdqeu2^u`2mt7M^D284{|zYT`)TeuR4FpTboq<>}yjME0d_zM^q_eUtN^Y4YYy@HPHu7VQb8L)bDO)a+f>Y>Waoi<3v; zI%Fc6;N#SuKT}apb#Kbaf&0I|zdgj@%a4w;;s}f@aRw0%Mq`6`+HM zO}s-lbi#oXAUh|_z|3MC3E8z$MVh?&ZUm%e;!{*#(zN;Lz{iyE)!3Gmbz1c=Aka8- zStX>xGYYAt?rL2iTJnw!Fma_|_chf5S5=BT*Iu`{f{EuqZ4JDPv2%;rkwN)!_=hsNNO-g`-DOj_^u)jcT|D_Okv7%&*SH4a6KEcbQhcmb z=+sMDIP?hi+}XNs?RG2gb=8ml|1y7bjf%4%mB)q}q;Cf#2*>|c|AXVNs(~%wL(Y)B zcVJ}&c_%xkKRy)Kv)?4rj`Jqfkyx-{<=(GEY)P>GBOYNIm|y`}gjkq7RsUYttg-Jq zfBy5q7(zK+7hDjoY|Iy~JZEQP`1%o~#lQ_paC55((XGjbPz^Ff+5)9m{kzV3$YP|p z3&Q;%iS6xN_#6UAU187SbUQq@c5*$GObA(%QUEtXAv6n&5BGnPgMnV+56-@JcQJ#2 zGp|2PnB?p?qZRo%PKc#TzX)u(t;QOQBb<^|PRjHye-HKOZ+T}kh97FT>BaD(^(3E9D1Xq6r$Y|EIFV&}mu!`k zE)C_KGmgReusIO}h|)-$y}Ql3mvN=G9Oh#S|1?~YHS;Q<8rpS^z_WjanG{*4QVr5n zJNaprzANG8PH~A$b_}bi5`%k zRj4`fmFCzY??7jQ^ShPr*H6I{N}eG}kkY(|L`zzu-C#m04jQBHPT$Lkz;SYvC4B0N zrQnqWnN(i+kUar?P^u71gnnMTx}gC=sa|&4=2k9W5iuu2z{;ljdmQM~wkLI9uR20k zW zec?OnP;kf~{w8m%w}l#zUsgE$_I`5l)a44uCzo__raxg!teCpBY#D>!Kda8sF;1%< zO$+paP$}7GV^c4`Veo0^wPuL>dmGC;b`?j#ocMTX*noH_Ro5+NhALb6xV3x{c4E$o zGu7T-W@_)w?zwcoz8%G4G>>@eozt}4%!Afd3#&M$92|}L`MHGqr zLrrJ@3zTeZ=5T#|Me7BG<$N$?+fL5!8=KB`8d###XlDUBO` zQh5ts9K<x=_u3Y|+1u0JBsY&&{s!%A}4_5T@(2ojE8yGx@G6nuRSw ztg5FvM%1yJPGSioh+@a=7md`w%G+2tH9eI+j_J5XQ_6~tD;xjCDVGCjEETrcsvIH; zZ*0{hc)HSOwd{`g3`1X)p@etuFwq_(no6-a$TNLJZudJpp&Uq*#zj#9@Ai+__W57m znF7jP_C4!m5HuXYy>(YWix%(i@BMdT8pLG6^wHCq#Qmre^1cXxvSz;8gQL*PLU6mx zqX5;7Of#R>=qiDiSmt3+erPE-`^#A!*KN%-$|Mng9Z?bC>WV`0+LC+ywC5p@ZR{0V zL|v7xcy4}#S#pmr4-uZV!=`dOwu>JQ*NU|}dIur*%E=0ylg^R~xz#hMFsz#GwP=bs zkk9H-u90G-if;7nUxAg^ZASZXG`DoJSm~;Kuu3Hnr+tXr%;#Yipvsi)6ky`wg-9WX z-ti+ExOmdT55SMIr$S7@2&9Ea*$u%rV)Q0yxNZlJHB^cI)$XL^IxoViuni_Ke!Nm< z^N1L&*Yw0$G1Q{qCkOUSH{tdHnv0 z(|%dPDV#yC+3$lg3W98|2Z%C zI$MqzH&;WEC|e{xvMits<$yy$mT<_NTjI;&8@PX{%3!eg3`r`5B{J#KiSo_O%91EJX$oSX}uLN24W^O!-jb$C2f6l0L= z4q?KzBLou0OD9+$zr?8B!YCq+@_)Q(sZ#a`tGUhTGI^PX1_7Xi7qA;5~& z=KiSJ`tgwRaekp^b5?!>JOMMQ1F3u2a6#)aC3uhEclusA1jK_d0&SN+&5+lPJ<1nR zuElWO6#to(1Y=9M>!|bVGFs@3ffiLj!^eYl_mDt{+oqi$nV z1Au$ltHjB`+CVLmF>5_iB^!MS6B7JVX~44sI)N%LfrbR7nvv zq+Xjk|BFC+k_ur236e7wc=h35`;!9D(5@d2nf*|`eb9|myM!&XS&%G!BGioj?ZLvs zIx*{`7Y0-Nc0V0*9pCpeTi3t29h^I4x|A3m*0_KYLA#58)y?+7YLSV>#O?btLn?Wa zNn6CR*3}OIof`So#EkCQFr|Th_?Ufd9EpVg?%>6t{k*9mrv?^0gO%+l4laXN*7$wj z99`q$4cj^zKn4G&y&65D!k)0w_O2Zkd3{RLM{3F1ZU~ZV5TWf z>(crW8V2X}v#T|a|C!&0XP2O(fR{j?TLSS@%A5x)9E^w=YpFr5GI(|@Bu3TJ0a^zc z`RQWJrrlr1gTHR^_`JfF!I#Vww9 zP9Um~W(pfHH1*A#mN)7VVs&gU{6LsRxD{mH4v$ieq~#vgkv|@+LzPDDXeaUvf-N@& z^E7kUp8fGjzkD3qstf3OT-w^oug^h&%8R*y-j)#>TNb?eJ|Z9J2(5}7uzAW4u)ZqT zHsOq>-9@T?IGW^0CQi75dKu<}{L)RF@31l+w;`oV4iy6b=~WPaJOS4&9yGGrRm6mU zS%A&ao5jqCz5zWW+L7TmXDwc~&1ZO{FC~RTO#k zju@?gmxMyA^WIvppk8ZhQ{LD!QMK>S3o}rbCfyW^Dq)QP`)h(_#ud0TAGZPDKx$lW zdBOFeXbMLoY6AUj_Us($3!`0FsL4w#Mf>gkT@bUHwFp6vz9fZS&}MYZ05h&Pa^cb7 zxQRaevL0X}1q&3#Oj%PJCt#tp#~-y@m3Q;YAh&zCB5>+^dWwRmye-IWcq5G*dt{TR z70x_~QWhwxa#AF6ke=u#deee6IM`Hfc&1(d!BjGfNfKdIrW^54yQq3E2kxspG}diE zi%jQk?L)&5a=Grr(79aZItN%)!Ve$PNJqeXCmL7JL1JXKBI$Y`IArNO+6yvKt8R;G zmI-~3_%f)Pyvb{}IU&0$^+OEDH?b^N>bX$_I+PSFjUX=tx}hn6q}652`9T%FU?n~% zjkCr{f0|8;YMGVzitZ6rwdj$OY%13pV~OIF?_%2FdOx*jj61)GEsEmKMx0is9e@Uh z%9C)o5{?*;FEY)oZ4kauML-A;v~KG z*Tw|vq0e5Mbam$24U)&IU%7bh>!3TV(H_kxH--_aei)_%9 zyAHc!{Hzo%M}VZ*{;J9ZCu|>@0&et|yd5$$7JJ5cqaWULm$e+>o?!{^)N1++lf(-E z-5mKqs+P*K=$d+As^})_E`%>`VHcx(lb{p}8d*aqY7ef*Q^ksbH(G;a&{X5F9JFvR z;bHU;TgYX)xugia_?x|jHw@o;LCOgJ#1YpF#2NgIdAUtcXiGGT4}(^(GF0Gq|iBF6+T*8@o=?_Om9HtwG zW#GC5i$J7^UzKL4oFbL{R(2I+^>rLn6yFC->JpncO=EmbcP*&i87pYe&l{7a0rCBx z9{5N3g_bp4E=3YDC%$`^uac32QihA#*Tn48DJ2!WroqklcNHpIQxHey36gNnI3}F^ zPPsMTTaWGR-tErJH*-prvZ4$Wv(HK;QJd;*7S7kf!2$>Me?O>4e+~w{p0jxY$48YD zh-z>|nyD+ijRSHAt!P|HnnQ<-P-XqSwwqwKvNr%?7PBK8w=f}TzyUQRw5HP)uF&h! zxt)A4WANxq7cIgbYb?Pd3wGk*KCh-)##K9TpaHm~q8gbMPt<%6(;2oOE(u7o6|S<7 zw94Q}#@dz~!M;LFv4paV$37Hp=FsK!^iIC7G*^6u+LMw^+Cn&ry8`Qrl`~f<-_w6bb6i|2psw;OT@ImcKw7url%9xk(`<1PpnY~G zyRG1USOLH03B|3(A!G7{3sDW2D#4#k!Y-K;1fK?D_0$jN7jt$~oE6Mi5Pv(F`xb?M4;YNzuNz%Jbi@Y_6&Yi=xBIr zCb};_Lfz;4GLGhza7`Q|a(Kt5Us4E8FvpO z?k#%Y^D~(JUZb6qjTtYJE%qA!Dw|dF3DMl#4M*#UOv`n{m5o#jebR_Sk$&G}R@IX; zQp55|!5f01-~%ejzYWX#>HJ+j>CK2ZJ`A?C{znX-PvzRY?jGYsxar$7!^_d^HGNT& z9#vypfZBsP9DHJ?bn4(p&;f2!HUyzA1YRakT`J{PQw=#zYSfF9Dw|2?nX#LFhR)Bo*V zSaTh#@2lO&K3~`%-+v9>n%Dnbq)E`lHRBUZxI=8O50DhrfR>XkQ&s_%uT9tnV1X~q zWLP)-z&TX!8C*h@yJq^N-a*F=?G7e@mL>|@533-0w;1^h*EXl(Sa!R!=3=k%Aco;%*^jdKmWK);Ob6{H$JL>95rdSaGQnXys(Dt9$s0D* z|LfP=UZ=N)S>=Ke^lnvuz+8zt&^>C8jng)duA@pT;1a)QKn4cfto6HvHxW>DhgdP) zn`##adthYP^28E(zCB@_L#*0YzG?#5d;8)dKxXwzr*kJkrncbKFh8B4aX33{y0dr| zcXr^|5a;#+U!BVLgoyD1>X9lL^l*6?JmU?QCd&#`N*o^u9Wg?q`kiUOme=c7imHAd zEG#^Npn;j6IrNHezwcct@D%N?peZh}vJ80-N4&7Yb(goW%wXcqG&gvnCBG|l)D&Mv z8O{$|!p%dx(OSV6s~dZ`ead`gt7h0WrekK=s;^NBbCxEN z2M^V%zs|n6;w+p4+*v-Oj`YLJbHR>JReT}RF8eeDnd97TqK5t&DZ<-)mTg(^uS(7U z;uS~#W)%&g@(i1TJw4vxzkN>gnISch@hN{Iq@BI};Zk&w+*RJIu(Ywymaek}c zIyBQTqzrJOj2W#{jthyCS|?7--0F;8LewiU@1=9*$Akqch4Km+u8MFae~>r+-B{71 zA9(P7@tHWTW}ExCr!Kgp_jE@R>GxDxeu%BAoFv&l>P{U zDoD&yIAa#|9_Q?qIlPe;X^UE_s*jZs8uW-7omJ678bZY+)F{ft)SDfuxz&YT-?trr z;upYA8`y6|ujGi7utf^}4}XF-WPj5d+4LKAH+U?$jZ?OC(YwK%sY|c#UR3mCj3f(G zQ$^BSS{-acnJZ!3s?)@`37WkZ<@pA`_nhR$i#WTAvqN*}vG$I_N(_eBkFhdlE`H(Cs3W*d3xbI2M|T98y)um=k<; zsXzNpmVNE=ihxlFI&0uH2mPoIAexvCG*N?w8( zk7*ijD9H<{_<~iity-Il(Mmc)VErBPqOBHYnkic1dK8$p(`9_ zPL20te(eyC{>AioG+>30tFdaqqN)uWjJ)X6I}hH0`LEKzOY6)q)l#$bi&%^76a038 zh-yN(C)6rH)qj4ylTCUXnDu!?l1%~+K#n`Su(DWI4>hvU$DvE?G;P1>0zb5aK*9!d z^gR^jK^r59(|;EXhaIBxV175#=??sk9l7Q3lywb{vN~M|#wRvvk=j|fGV7H{Jeh*= z^ZT7M?);7iW5B|QlO^DSqk8nFv%(iNFu9che4Cp;XNcS2$V~K_PJp*Ik_DfBKO!VC zSbKg&^#j^^aI5BXP)|)Tg<1;92GXa}T$RV*rir~zA=4vkLglPf@A0-%V$MLyF)Yi1 zJC=_H=&qi!p)Lz7yu#7G@AE#u=IfJF@Bt5vdwU6{_-y^~do8iuO!`M5JouP>mpPhg zEt~U!Q~bplQ4Fu%M0+Rsr2<&_Krr6E#@aUz@H2gVqvc@NKVIM;$bBEpWyQ9Z)NY& ze|xQx`3v`vU&c3u`DI86P{?z@(Kcr~<-7S%G4xpfVp{ccCxk&2js@R*yvKQqModZ* zj!vMj^G+gWioR&eTx^1z?;0Yp5cW_5rt}!6a6kf&QBeSRM*^u7-oaU7{-Wa1+!DNpnr`XT7D9q8Cl55isk`iWj#6n+BzG&D7yoq$I#V2ij%7XdC>NGh61i!;>Kj?6r6u__RC25Gt| zJ6RPA0qUCQg3K=BDjYVi<3$UGni)4_O7G3=fEPAAVKc?S=QPsX*4RCb5|?yX}O4SzgQ>lC=N^`TrPgG&~P zh6}jo;0ABxAp}L| z@w43Q!ZmcrDWeQtH9mUAv*E`(C21UVY?wMvpK$@FCC;1%jW7#5w&z9vpm zwpmN)qkHifsCO-`1!1WZ${7@BM}*T9&wsLjpanJBX7I>5hkTy5>T?`9LjO#VbA+Ru zk>lbANYf#OKltOMD7a-3O{b&ry6}djX~;P!LgQU+D$5&{Gu6wmIouJ}rFhLPvE}1n zXr3sW-Y@4jQe{+ct=az1{l78_aNBZrhNN<6=EBH};IG*Kn+DB|{!{sYlpGjNdb|>c z+}GwwKYC5QB_Z*fs#OlEqw7HUj?V@JZuGO&rvx9TJ~|3r^Uv?>mLyZi7-nO8+yF^* z|5~Dh2$~}VjTRgwOsj0Sf=0~7SBAdzT{2DCiq|zqV)0ur?;A37^|TpH#oE`tPGgv_ zkn&f-!J>@~o|=E{Hws(Arz$g;#yGgKqN~>#%=NT1)BM$ zej(6dzTmDrj$Jib^xcy}=ET0Iu)kD1P}(P;8r1|DJ5g71XqqGB#TwrtbS}idsxk(qa4@BIq zI-49TwrSAqIH@;?v!T;|9YG+u87x)>|VC7UyN*LatQ zC7SwUeE63qi>GoU{+xOD@NZ!$Se*6GL395pI4iO20FWY!wzTu^Z z=;l>#{v7Q9R)(3d)q~s-^waJi{>OO`c53Ib!DqmP!I^9Pi1I}8x(~pwX9*?8h0jCc zm7mWS>*YCpy+>hGm4!yNBW15-D-8Bf=M2nqA($_J;*h(svl(8{@?+%8m|CY416mbp z@%;@hQD-NPloeSXklu%}xe(2l-(`)`zKM??=h5QBh7Sk!5Sw?RNBl6+%gy3jF2yA!wti zCkU;c!B<^!S0+L|iiLU{T4%a@gSLvl_EWngUYZQWrLWjgi7U`^d29PH6jftt8Xh=< zv#3=>sX()YFi+NG&Nw3yzXt#9PqrmZBd1hvpevg@3sLWK*s~!XhtKJ?+aw(NC1(+j zvMu{aM)epxkq^5P4;Iox8L(2pbKquBVo;NHi*MU2L}im3c_O#LU;1js@xojZs2Mo< zH2qZ0(Zq1_p{EI^jMP_`5aMI;QD`rU=tpM>sVegtQ;)F+&jLSJ`&<3`Y2vbnDbinf z--bBZdcpxy%M)dgA*VkdrFOj&@Th$zy);)vu&A%^C?bL=_G1@~wKm7Z+6c_%6Bf;z z-;OAEWUlbz9q0oRg3{VMn&i$Ta!7P+5)tm##CYJ**#WoV-0yQe#Z)=Xf+Fz^E&n>= zDo`mYpJ3LqGa8;F-9?f;!#dZp=NfTRLK4hL<61J}m{ocP=t_#`*s9fQwwY<%XELUh zNf8fkcTax&F9pAHeL^zSj7dBT6`OI}UK_soXJxPYba+<{^Ap!l;2mMv$SVw_H;LR| zm6ZI_rQ=DgSHx&wK|nBz)W8j2-=tuzXT?}-y}+v_&MAM%B*{M}orka~vNka}M`qbz zCBO&!=T6tK=atS1`EH}~x~JUQ*E2tJ)eCGc(9e#g8n$`Cn&*6^Ok-eH*v%Inv-so< zTJMBGZ2W>tF?Go}j%lDGI3}PqFxSzM058p-?T~2r4lHp+`VaECes=B8$@y~DErF)q z@-U&a4eOXwO^e7WlEju^Sbn`r#jN>pE#ob>yCmrajab1Y6`O3V1NjNGy&vSvQQnv3 zQ^7f`qvz6Nr{cGT9_+-@9U&qn`!un4t3A(RA0%g9o{HZw){_oGU7Yoh<3iDW%O@yd zNa-E(m|JJUnQLN_vE(vM;)j*foLfAkiV5yYsuVgz&CAEOLxEJWMzs6- ztQAbgj~2&qM~mDFwQ$(kHvF=q-$7g{t*VtR_<%yNS)XP&ayHT&B~1C+@ke>Tz2l35 z&3%mT(4>3}^^)r0i$YAUO0Up^qkd}EXJ?hMUoWeuYs6yZa_Rvu+K5b3n7Z@#$z!)G z$UlEGHM4q5H@-5lthUN;f{9;F(Z0YUjT@cg4AP2K@vpNV%qHgL5Kf;$cMhyRKLCQP z-SGrX?5Q5~KgUqrNr50>>qT~#&qY?Sq2ef*KWow3RpmoE#?9B$aj*0!u{tTV zY$*rv*$MY8F8qM}N>$1lk!$wR5$Ue4!6HsJ{xB_iF4lf1MIw$G68Re86dTfB^Y!s5wCWL7 zI7Vfl;d=GF*NyP#cP-N2N6>x{4qFvwL2N&>AiIu47~@@w^NqeseHZX8)850ePtX*J z6#VzQLw2fZ9JcD3=({MA9H(nBoPy(3!oM$s9m*TIoY)=6JOyTB-{`A=J)OctTvQh{ zYWSvL9;Qc~Pz53^RWDX;qwBYnEE*I{#{WiDWkZFH2l7PUfZ)WMV$B9s$CJ;|)!{_} z2=fP`H;=^y`6FK#UhNHc$HtByh-O0p_IQuGzux0Eyqz&~>KwmG%W*UQJZpgbsI||gOM6ir6TYP&5*kgf{D+T`kCJ&6 zgG)ArU^&8kCif!*Psw*^{F(m<@R-^F93k!<9+SlxyDa-|ULi&C`H1&*>~}>`jEa5Z z5zIZkV5LdiU6>U%(I`G-Vr!)ltDAzF@<(O47gLqbwq$#ZA(PCmV7_dB#_Ek` zMrOe;ruK&sWBd^{8B}vWRXn>29ncKjW|+Xsg<4m*tP)Rf4b(6yaoG8O@k9l;XNT!L zPj-A;`t{){vf{;)YRU9}F;FEgpGzc)aIH92mlogJ-YI`Sz~9PUKk)lcQ}dCcu<9n% zV5-Q3{j=Iove4;_%=WA*Exw_Hz_E4=sPW*=D8bwRPZh&Etet#6!!HXmL_}SR!&|Ru z51;g?&=oM`8PWYua-E_$PL9rnwk9jsd zrfq;#@)B|3_qbKjFCTrn8UlX41WCkIoamgV`j~$Ym0a0(H%6o>A98g4R2h>B3@71c zm?jVg3Db_DoVtKuWq9h35BEaniLdIgeyGsK_FTQL-pvpOaDvhoJ9A{}=!Mc)9Bv6hp@z$HT3 zJ|Zj8kzLf^3jo*sa9WRK2@nFXzL>{zntR^YKSc2fmzf3&74VuBaq61AmK>aG1F(%Vgsq~Ejai+UsQ%*f)Dz>ym)yP zBX|g}*dPj5WuHU!ke9o?`cHCB!E}BryNB)1Jd&~Ej9V7YZj&#lJ}~DaT3PVnE!>}s z2{+S!GatJspRkg4!L2aF{SK3->Vc6yE>ZaKZy38`sgCH&sQ=fcdzz>7y+Id7ge}|hQr1en6kz2D^Jr9@dXVAO7zjNEnHQjJcC#TG zQR5E|@LmcEap8}W<1eFxqGI6^vGy8CS{4e@4}pdJo+wpti#9!Cm%?wPTPc*zdhN(@ zKK^H^NsI1Xt#%m0{`Uje-erv)lehpMjyMy^!VmMtSZ`6%|#i z(Y8Ik0slqV(qH)}Q~MH&;m8Y$o3$L<$aNw<&SReP)5j(JQ8K0t#QO&{kT>>{OTu?L z2|u>&MWk@L_=X(wM~PIm{pk&Ct%}5LfbHf0KWG-E+j`E9VQ^12=St(yEy^dXM3!>4 zA@SzqrCmGo<`fkCMI$CNo*|EK!Oa-7$O?mUjR)-5%w^e4#a&jf#X_cb-G$AGJF4_^ z29qHzyuHSUB&7c{wRiBr_NpG6Gt%1j)301HD<@oai6GI2?s5?|>LHT3<}#d+7ww0^ znUnG*vYD#G5^X+`a{_hh3GXfs1xSMs_-C-@BRf=cCGRee`5`2F-a(t!nmV?%>IG`A zSNXe;V(J_+g{T*!Fgb+feX&zIQ7eFFe<7Gz;f|pi=3X+C&b~7_l87^yyS#SZJsW&# zw}x*};Rbj)*gIVO587j3{c?xDytllvLWvb>=J)Fwf*WCJYf0-({ak~qx2c&So#^FgY#3}CXerY|G|)vfHxmIhpL`KN5n|A_>f>0FGV77bVdLaOaFvS5fh^K zC5i)en9DjvLb3&EE6se(VinyK8Vi@n&^fQg(Y)LHu>Vwzs8Fq2hGlmfuOX499t~de zRtA{l&fsU@QjpH6{k}$dt4rVeZDhTE*Y@4%V!egTe8YXg7*aFEo$HbDt@Jim>3CLg zwz>x+fCJ;1!Pe0;$G8h>!G2aEiJ%d2i{A)VxLy|sYmCs~_Pq18l|I8S{)`BCvGl~x z!E@xNpp4aDax+&hhwQrPVlq9f73>nUw&K7sUGx1TKd@OIcfdFzvVQixS7l0MVA$sQ?=XOKMi(Pyx9&Ge3nq8p0$6Zqs zuQHdusgan<&a~|+j^-t9Bx(Tpl`TZ@5=BGxdC*RR2svp#12RY4JO6>@^d6KdqSJUc zh3ZtMK^O)@|2)K7aYromlkGQDS8V3F^K3n-Ho9w{HNF)7rsQj=>I{&SiFm7zAA7H}q5nHy4tkb@MLSa(gG_cL_0`87H zDRyur{Zp+mX=l~M%Y6kw`te5Lwh1a&LOtsZ|I};bY3t=40)D9x>9LOd0_O!WlpW~ZfiU**DzJUtpD1MrFx5*g;OkCCxIw86pQ#_y}7vfuhKajdfuAGLpS;8t$ z_{?_^#F(7JQ7PfY{vmgiH>q;-pBl8^L^Qyc6@j8+s@GJ0P#z=d28Rznm_J{gVk#3v z$};+T8Jk6PB5Jm~Zj0y{AaJ z{`K_%ZqfUN+2~?-=4UtS{7s*93kExt@h3jge?K+?(+%BTxK^oL`^;(?CVN;tjyO-> zKM1NvTnPYU@!rc28U%qtEQAX(#0z4yHQ1(b!k!TUG5bfg*))&AjZWF1j3|hb z)Ji^hgYNd7u2wDLV{y>J4)7dTk6L>|<*FY<+qtXKwncpTIoctA;k{+Vs6gZmtkxl2`@z%~=R$sKcpkNLmVIIh1vi(~x<|Pc%c!*rD&>S9{5ey+}X1Il0cM zZnC>cNEKZ9&Uaa)W$Lyo-(CH#AQf1e=gDmk$ML@$if2K%=6;8F8mTjmRYCnAY)oIn z@S6CK^@CXWXAIuAQ@k4zLD`F+MS}K#>OnXO;PVJ|I2;NoGj0#s?{NF4Ly28zSC$Jhi8kld@wF$MyLZ4M(>-S!r(Zw?du1! zUT+<<<`FNQCjYi-vA`NuQv7-s?m$B3T6Ru5?I5dOe@@==M@luitY*K5{rvTRYg~6I zrY!@67tFJ%q!8FF-Vs3l&L${q2!!j+V0?bFZ~yM+a7;%}QQYX}j(smQjTgzS*&f^1 z6%?e^7XFG3mw*Ykc{O8&=%N`#(C)Z89i}AlwZk;p|1V+R9T&yb_5Yr`Gqbz1yX>;` zMXG=T!G>b*E%uVwV@=e=BpPEhCdNpUq9B5Rih_VN1t|&$hz$e;0YOwm1Vp5%Ac|ta z@;kGx`n==wd4GRIG3?&CGjq$i=X{G2rMp(5;`d#lc(0M@5*5wmJH9}=2QVk(&oY3% z@)n%2_!lC6vyIr^72InCt2Bf6&^-IbU~hBjS|7nftt-r##bvue3wPCy_4hNO^(Xyi zJif}d7%0m%ljKODMHQ#q>iRiA+N#>Z{!uW>e=2=`NU+Y;A??dz`}vVai#o5 zpMILF!S8g;6EqzJW1k<6^$2PNS?Hu)%Ysa+LqXqpfy-1cH;A!~rz)!;;`<7%T__1p z@>g~I(KU@OUh#HX*I$qB13I^NYvts^mAPebI|qWd;jQw4co&F^VS?j8dmG!d2_eFj z&yC(8#Vwu~2K+?-@Ccm8Kl(NKE|5iNm*k!b=Cw@G@Z86MxTO0_)BhUtaTWN$qu`iFN-1hj}y} z`sd8wn}y-=J+@VtqI}g;_BIkirf8!WU5PRWuq2Mkh!?S{{Kaj?&>19fA0)Vd*|B>8hJv5ZAxv#}uXX!DINrR&SRmLEa{)ndY|Yc#rFtxOi`mvg=XbTk{Z=MxVx; zTqR>ACiU_(-2T;)oyMPUQjNA8H&{=%bjPdlT$Va&;zQBjnj6&d_Snj9{Q}x`PnJg+ z4bJUz=r)|~l{@>9eF8Xj5oatT5zE>@lkM{an$y_(0R!CU9wy}yc$F!NhZj|OVqOldJdz?uOg@od&SQS zZD?`0GCx`Rh>@J55{TasREosC`|ge%cL6O<++M>zQ#TKIAZ4$bNecJE6Bnq^($n<` zg$^_vhRJ%m54$k{tjq(q1p-V~eLdw@P3K2`kag!};hRuUes?HWV&*WpqKo3*?oMOw z8K%x7t1Cd!rwu}69DkW#P6}Y&5Cj32|GWzcT=(>i(qwPe#5j3U9HP!&X#aw?L`Q_^ zqo%xa68}`4s9V_rql#H0PX9bf+Y`<(Oe6}RyQ1j|xFo}!rDl&EFl6JKj+DtrCEZ{k zM)@NPUxPwm8X!e$P9faUQCqRjeI3!ne`maW-ZT#Zxi;@0$hs^q2oGpoCjPjeSFcVS z8zb<{2dz|HGXBzpk8XgR*33Sq7E0d5oTTq4Zg4d~KGdo~h71tTZ`o5PfqtYoLj}!K z6(5GT6;Ms7>PGEa+=A;ov3z5hk3I~XZD6*nH5JrNCNi4e>nn5cB-7Ec%z~Hn;yJ;j z4=#jgZFOC~h2iwn!eFRdk26;9{-P3(;`lS_sD(rm>?0oyd6i<%Fqiwk@J0>(8r^5+ z*Bx-r$t31sJ#a<4>IT7Ga1xJ_&!e~i9Fqd`TVINK1jV7BC#_lwzYHx2F1Qie_DM3l z*Ct^-(0|RJK}XfQf)Admkz#T|Fn0#_hBxxO$Wse>Z7TGYbqp!f6T0;%utw#TUo89t zoVDv6Dg}QQfQNNW0l%`#6Q$fE*WM{GAb$FyI@(3p2_f|e@}gd1js5g__=d>uMa6yJ}ZS_TTjQ`n9#lrZ9dRG5i!HKHHK&5 zVp2QMX3*D~`S>p2>rg~8nSWZ9)N3JHW98@%bAlKrWSXRl(N}6EQTf=Y`8R(sJu!J=0Gie^`J2$FzD9>r#fM6m+A@1?2wCwlCW_p@+1D}h=THQr-okIsJShTYd62C zIGCA$x*t2qRn_rJa854uRyi-L8-VF3gsmgqTk_}Lp?-tD93@*6v4C!S;&$<>W^to7 z2FBt-eZ-$*(pTar(iT#G7Ba{gu6$R*_d(=M8WSJdB1%5`ils+S=kHxw(WU$j6E{Jp zOYAHpq6>P#tWM2cHQtV&7K!{W?pMU$zkLPLTSBH=C9h+o?E4ur?t8=NCvxit1&}Ew zZ)WImj@GJfs~X%3rGe{c=MzvePtWt;L(A}tvd&HQjL%r&$tpzY_q+(*?r2YlfH`5@ z=75V3NKsh^T-sRp)=VBb+EG@2(X4`tPN8 ztX>`kE~V=;Kw`(+$9QSTX|i&mFj7zt&xe!V-B2NaaUWvSfacu9T8aD5>S6sU(P69V z#ad5m&<&NoFh1@Feg@{Q%N`=L>BsCBqUjO0{ zABzF#+hK2zTBmp?XoAyC@Ke7GWdkV}h{JdI3XW|%Hrv?5Ra^V3hQ7-$3uV4+iw;-N zPE$HkX-eT!xoP+gkuZFR>9N_^Nj~49>#j;FlTg={^3%$ zk8$abkrkM}9O=1r6mI+_le42BupLHa>JHJnMTf0DqLn4E^=03wumUcrGIqrOB;JO- zbh$4|CETCAbW0`+psCNx#eBCOUWoN{34^AkxCkK(h^JQyCuOwM+J3stnk|-V(r&G4gBdk zT8yr=iMGw~h7ome0p5?F=aV`yH6&|oh*i+o0_9yTA2>x_UVsvDMTm5+_X9`v-6aGA zTp0CiQBDx-xV00mCBf8Ig`;KZxN_#vctgV4_|x6O!_#M=sLb2f&!#C&?7Yx1fn7)ITkmpjpW>`@G?4UTD5416yBvPhsgR38>u zBVXjMebpBIV>7)i5mj_vq5i_Dm)~@UX)bu%y=Wo_!sUS|QTDFMpg_u;XG)DuA*|I> z_ylfHhf#+Aw3txG`@|yj?C{qqDL_Bf6;Z8vwR+dNLkfTQ;ebh%!pq}455Qv!_f2LQ zthwhC2pwDU9L!QG&_g|;8o~x#*7YJZpqcc%swy4s=+!I_n|{3tI$D5cZ;on(5eRBM!vy~j!2WT6!r%Ds+?e+Ah>)5FMg=bzzs_5O%ZI84dkqrXsN#E<(l zZ{?%K1FvS=M5A5nE$SFW#9krL`&;b_m~aG7D_F@t<_S?&`EcDp97LTPdsa;1Zb74h zh^}wOyXB`X0+=J@LIt9`{TGjTrhE{@;lc+^EVJ{Z7E0RAux?qQnGvF{K->ksi+;LPjrC)Vs*J7RpWuIe;+fxo1QQ z(ikjKW);m7XV6H+)^V!=$GsCsIC|n9UlK9}eSt2+lL*LR&%vt0Bh|S4Up~+Hm8#|J z(s$3{y#4lKVuO|WgUec!)!=M{xM8h*V+JFJF?Yu!(=xjR;b3GA<}vFET*-s2N|)Ub zW$Gbx4AtwRCpjSX+EY#>1M?^^!D%MJ6=;O+yTeVc1~Kx|riXk9OCENwQ6k%itwuwm z@%1QJo{f?aD_6ykPRwMH-korn=rqD{po$sA*@*?W;U|0rd}?imj?>2`(v!fKWYQ1j z-dcHx1cp3@$_N_nfRHHV831M$RO`{c3!8d|+xOAe%e04bqt1+9OAW3l!Q|>u(s7<%p#V1aD(H zW%Ww^KQyC$`Pxm_FfYnGspLP0B$I=H;$r#Bs)vx(=N+ntaGQVM_aP8JdzrW#E2>}U z?hI)!fJ1`puUz0*7m|Nj6i%PP?p67mhe~VCCd200A=E3~b~}Zu7+hNSd>kL{^Zln` zy-2lyo!9tp85G^dBG21tv6;}Yg>G3;a0c`%6y)?N2yufC|BR<%>LCc}9PZOowBKxb zw2E&1`MS)7DcVM#oO;R&+_vPPYf(HEngQ1_Yxl*x%tCm!j>j(q4c7um&JS8CVbC4~ zyQGAV5XumAV5Ac|h7O%0@>7(~#n>rWBJ4B>>4RHKl=RFV=+YDCWI|6_8$Xn(@O#=Pq7AW0|wd)OfH>4!%A87^S zYn@sH?v5U}fesp(^Ltq@0sOnsfOUM(qY;A0 zl1Rbo6sv0y5VnkF-WDRr^U`r{yz$G3r~2oydnS2e=`s8Jms1ZB1p1EHW%0p?=GbJW zI1b46ux%?*XK8xPpj%eTTLUqpl|ZiRV-V_U*Le?4<5s48xDYSfCiuHkbc)7XByH^Sy~DBcwPn%UPXJlnT2PRER*RorO>Li(K!8{<_HJ3(-SU&ZKA1q9m5yw8PUM zB>VNf%Z@X$0;niFFZxxL=Ro>QK~v4tnb{j zuEyZq=JEgZ`M;NY^ow{ul>Bn;Ht0|pPWxAAX|(O@7fDR5eVdMwZ|-`%jt^`rf43F zleN}Sd@8&X$CgeTbX4v0?LK3RbOqimUrTPU{G762D6z9g^|mlkv{?*Fd}CB-{e3^u*O#F%bjh4ZDC=SDGpmk@c|qDeT>>e>4w_-fej{zwl3a;hzmF9cLd6^a8u!|XD;?dwy6IB$X}6{3k{pT@#e7Dbvemb#~aNw9UT6>M-Q@QMdDAY*)`9`ro@-^gZMiy)m{r0e3`2qo1b&d9O9G0u@z6x#rQdnwy-c6#LVw&xSy( zG{^wyD11`DeZjjf$acdxm__v#{Q7P^Df=cC+$#}RUm#-qxz#9lLM3=CKT7;oXRvFa zaq?@1$2jLz3Qd}K2z`ZR!Y7XJ^}gJ1HlT4>oiw!X6UQ50L0o9&7#=B^m#p~bjP~Zf z8!za)ff$u`S5tdXn&jd_L@xO2;8W(y3SS^?=j8;|*bPC_W0 zo)*dp1bjh!#uRcHrXfafA7&GJlkzP5zzXL)tp*Oc4iF{|n=vS&*~!ELrepf~35chVD@K2!ejL_gPW<#%bVez^#Xb>`_*_`#uY5d#1p5MYXq7PAyb}YT zn%<4`mWdAi^n*Jvd#o#>{6(4l65`NEJ*K9aic zZ1f~0eV!hHIy|6;R)9?^8UnnesRA-#K;kLZ#RbjYNf8JH^m(TrFkVl zzc>O>D@I8>X9wajd(N!uUi^b2PxZ&d4*_awT810SRA8Rt2{b`5LRN>~bCN}jcmnb= zF*^5Rgn&1~4}b%~WmIVFGzZz8hfs-! z2J{OkH3|5OtX|9{%kyC`F_0DN3WMZ`2;d@J{`68CHa+hpPe-`1p}Ix;O-2jS(O?L1 zhZxvcdxe31Y$==^%I!n0EJ2aUF2Xv;fSD@D?5)sWpp3zS_}pNJrF;POv`>RcO?XM^ z*^YwGq4G|n2^;+c!`>;F{+Loap~z)0H0C5>0?>$V51vHNN<9VSy*tRFEp+^Ud^@K` z@?ZKrz{o+X(3>jp8 z`>PDr*pD)F^0Jc@`$0?=SRsJg@}Ictnn~~x`8w}Yztwg&hc|W_96E|-2qQ9f!o3N` zCZ}12wu`{k3Ja0(f{o~Y2eaSIwPy*H_vw|7LVA&0W`!(;ws1e1I* z;=4s*21aa=9U^OJ>3LIZ{OtT4da-(#hu^7_z~I6CCTa{~8Pdxz&lRTXo!JGM8_k|| zLxE%YZlv>@GdFrSTYE+Vjl(MI6jHy)E77A4NEwm^nQ1fW!tpcoBuVpMYFi(f5NV$_Q~y;0+#MeM z0FM4e9{hfQuvu#=f2V)&o#y}Tjb+Y#P{biaAl<2tt-6R~`@h1laf|SGD_)$*FWnAN zgENS|WtJ%kl9}J)Hd%{LgG623HBkdYNFVA9kmV>titN}w7hX*Z)jX5$4Gd5Jg z=p&)<1{ZKsCZu+TO8)uQSJwiz>dE;mW?h3x`RZ<^5^;odQYTPKjNyJ2VRz{ z^1t3-+}Eztk_ZoFFCVK_=y@xv=XNU22VEIQDPq?}|4}5}XT+qE8~WPMK)!!7N?fDc zT<|cRKv6B`1NzrFuKvGKFeZr(k7dgr!-1FJ{*%$I^RU9%sz4u8lUJY=t_>4KFbB95 znva6Hvm-vBJ?VTN8%mj&SH``3gcnnyUdb9Xm;nA_1mKElzek?in)b{a7! z_2sw90hP3Ij&d@<-Ugil^{M-1Eret0SX?%{1$`;P;6gvl*wusQDujz+e`bB2r4v-+ z*fD<8v9oYe|F6r~*d&fm6S{;wFebyaeIae^>`u58oh6%MbZHp|=;p_Jg!ENzP0;(q z(nf&Y<28?XK{=1Lnl7LU{$SeO;Tufd1`9YE(sQoz&R@#|8A-<#Xk;agsyAfuPW?+@ zGon=knf+1?E#VijMVBm%rqSSujx~BPX;Iw)L;xClx<7XnLR4sp#skKYkj#1;^ zB@qxa{&+v7x0Wlyp36I{o7L9YrblRp2ivJQ=8Q8S<>V?p~;&45|C)99DllCBf>{sF^~8{CA?MWAB9m=9?;1Hu&jO@P@(d-gSH-13i?ess7q`2xnt-xo%gkNCRAd<&;{`8iKk|dWnqQ38K8w`~E`=&87IEchmLW4U_tI zLg->2R8t~`uf_+_(gMd6vmiQ(ycs?5nf_5!vz=>S!MH#nN42k&Coknhmcs3roy4Cl zU(?Yx>f?LEF9XyYx4#plKzT8J-- zZwq=wd}{{k-_fn${jW4yXoxkQO=s}m7@NCQ13g*Nh#TjFp-a;@X|65X^v`8>Bj1|$ zpdFT4QA$^FzwhPQ7cAoCQHIaArAc}#`ZSBR)7S}|zQb*%4u|^}@?Y6hsuTx;`#{x+ zk<~h+#?IVcN$k8u>vnyWE<#VVDmxA?An(_&C%@9ptIov?Cp91s)QVLZP^qMweneFY zKA6Ki7hI6{yXVmE2_hGvw#9vNR4gu1VP6(}o;%Z3Q`xVt$+r7Y_x`&26zEtRl`dz& z9ww;MC9f$=!{Rtxs|=R;f7{Xm4Jq)*T)GWY=&Ul{9`OxIF0XXB33e~f!a?lXCST8c zSx%oXrG^~`Fga`_*))3>8W5ZB5}kni$07Z%V-^V@jSU8?fn?vNotQo2Z$M3S1}15G z`U!BM>V$epJ~XtUpgIv%PeAv~czjt^iEIAi%UtlqGhRxzmkBQSuO)@FhhB|%rx;!H zbWY1FZ1#CU1bx>AHw$|dluIY)s4K@H-4gr3d)R8WN-0K;b4v7b1T|ujmy~)b5H2Z^ zH8Es$wgCUxBors^!8~UpEuP?r&8K;-)?+S|qn*SA<~6&qLDf^XTXIXK z^Jk?2UYjp{H>6x@=ny)1QgscIh{~4GD%(HhPpmFDKEZS2%pm{!&YH2R@zCVgU^lc% zvIIu?6T_CDhi7a~G#p_`2{GPF85>Ei?E$^xYuu1iB|RJfhoT|QaD5SXSl}6q`N8rw z^uwTp{h8b_sF899voosmb1m*YoxSRgxj42WKhKK5rY%5f#4IYohXu^>DEob_`@F)flam=yhjugYEQE}~(B zU}`Uq%Rt-UJe1@kKYS@y!g86=f?Nf-54wp%PoOe>?0j$3D!t;5SaOF~sxwA+iq(yy zq{#zBJWB|^cXw3GS3baLG-GpP*fh<{{O@kAoTlwdG@^=>2Zo=gyW|#z5<=M>ZxA*ryunn~CVr6w zB*s|0>XLQ&ArQCWDd`JCya>y5;<*#_UroQlNZvn%gu5^Uq3YG3=ai>^Ye}yK5wmKv zjIKFjCsuo}u;PW8ydCnc8%c!t2z+}<e(2ZYYs4M(V-}fSXllR{!4w1cy zgnc7UqES2KHO6+~I8(C!aYd+`^x~$*p}Oxe{yDwZEyGE3|FahOJjQL7=SXdGd3D{; z11Kj2_?u6qVVO$8it)Vsb0vkp`JrZ@4=?Ah`ZNjD)EJZfOps8~Kmxc`k*_j~c|v6x zx{buh4vZH-?9;SVKTVg`KfL#dMM229pRYVwHIW_PhS1&@v(G8X{Z^DjzCljWxrSHQ zsJYL9LBO$~pNqR$oIsK;D$NCEN)st_73*!7u5C!7Mao1+>;a02?3@4-2j#bzdesS`uti~o0XN%B6UZdHxdW%4A>~sa zb1Nv%`F%gt_dcTc#r|`Xz?3lIw6K}nm_v)2Nd`j0f>_iN6u^MuVOMx+3B0A$q?R7` z%2M7fbJ^l85cf#&fPPOPSfb;~Xg4d>lq|k*+lFf6pipRBKJ~;uIR2Auw}lD{FtdJ3 zaNpj;*cW?xgEq+HZ4|uCJzbEyefe8O5jYlWwb(dbmCDafsNpyld_A!tuj~S6TqTQ> z>>b^vlq!6uIeM-^K6`ou&`Sldw=mY3r@vzGX^yC;lS%mab68o}0cb$;X`-oT&yc!T z@Jb+gp!c9ZoIEpP!T$%TK8Kc0DXJS;bjUID**lDYgT4;`fYCg+*q*2ud z1~|Th$*b8}W>o5q$IEGF+!8X-k?bs-HO!t6Ma1a{2n^=VX?&!5HQDJ0k|yJ@#pm}! zqk|KAGSPY;E;Hwx4_9&>en&Val0u2;t9qBL5$Cg8fFjFk zYyb#y;RYm+NNWXcp)bnhI^)S7?}1-#^`sBO6jYRUM-Zz+L{ArJk)l)L0VC3w2&2+; z5386Hx_Y^oEaaNAZjtIH3(J}3@D4XV)&E3E_#$&v4TAh?|0c|WlQt6PcW|}uHQgUh365{kKgk6~JQVJJD#N(_F#Lpvd39@O z&95ipq{)*7tEorba#%vOD zd_SJN)f+~G)*JHxDp4qU*$$_sz1M3MGFUFwIf9DKCL>3M5~Mb&$4_73VXNN_s2nTO zyLn~ph$}F>ULcxN&dKq9kt#?YjOIyIKj}(sgU(^f;IGXr;$!+l$oSBde4vy%EAhR##Eq*qZbaoMN$_N{6zI~#3bj)N>oOa2&hct;~Z ztB9$kg%?+eRz=G9Kpr}F>;-a8w}m*$YH969PWymKpd7D}=hA(!Ie597z+lzhaqV-& zFLr|!$Fq7Xo72e)C3%QAllx3^FNJpPzs0l%aL2zdipWE*Ow0NASx-DR+a*q{*m7U= zS`dNb`M1lvhYiv^z?*qXn{W^0g;K|c-6GVVRRn<+2A`j-RK>EQxb-eb^j!nrogOok ze+K5wv3w>BMU`m%QIRw{w#4`CNr{rUA539#g8upzJq)r#$ zOE8iklTG)5K4V)z=lqVk6|Q2}mmohf`tK1@3xw*oU zSALiS=WUZuzDGE;RCwagYQZq;h%Xp<1mN9x(?Nk1!c`zEOTZd0WxTng3HOqtw z82d`x31zC@jz>r45{0)wbaZ6zky?HWk0W)1fBMU&D#*l@bHfCA+hUwB@;f=m@vPBN z21%&x^y@*=qc?yajA3ZNNr!OFbwA3m@xVzbx)mbhu3qS)B5R!`jGpE!8gmlY0bPFu zE-BY`_=&U;%`QG5uNDsK=cY$pH0RKxHC#oHA8vnc$*F0&ssb6d(ZijSAvhGuh>=9y zsLNn-o^~Pb8#)G97W63{v(5I=TB_XooFGL0IzW83={ZJc}2>)ByGvM-Tb9 z{!dX&W+$R2YKh*3U7*lz1C%lh$u z=~@zl(bCZklGlUx8cvi~nc6eDhqmvt@=rsp3)Gw*Gh@{(P2Yc+JZm3H`tz_w*{0VbCk7lN7Iy?2U$=wEc6Anx?Dx0<;A00t1kr?-IW&@}MK)AotM0=)XTg~ZFn2by< zB04R!s2Po__0$Pde1Ze+s6UIHTKaiaQyV7@QFYO|fbIg4CW3!fP72rs`j<^RgP~f< zNbq`c=gBXB-tHwxj)HJZattQY3tH>)Htvwb^j5LHDOk0|ug!3{{#zv`$eL$$=Y?zT z<>Qb?&+nhs|F-#$P0DeqG3=;2^tZzPjR@NB7qsph>ZyY;A_cT=8gK`Wvv5d$XGof4 zIJ$kNSe&$cb`I-8`{{?szzvpY|8iM~{pZ^$Y&ak$c2g{4`&ND@DL_DOU;vmM6k@6F z2%4GmLlq2D*SXw{tEJYiBZX<)F6YK1AOYwGv=^AgdK)MKhyV6pk^SO?guCk z`pyq(rrh}5BMPqSB#tAYMxEa)Id0jc#f>bl8$6eh{nWfN1k?9O1d9`bh*gzSuz1g9 zJ%l9!QJRA1=d2KSrU?IBD{fR70{)I#MeetM3w0Lygo*72Vv1PCA}&!94gB(5aNX zR9-8=s2RhB@{uQQgH|&XqaWpIu_$#B2@C;OZWf*Rj}l1)s4#9k1VciEt#HIz>Y@{E zHfpP%rAII%dqJu@%8!n4^8WUX)~_dIH4EcYL0@fuvG3Oc+fkz{av)+r*N;05>c4jD z1S<#BPA~q4Q&DFFpFq1W__`D88j45?W~1K4fe@fQ?{nHvHBk`OTgpC`>p4%(-w)@E zTDeYkGoFT0XSJobK8f0#I4#Apc3cz=1^2PJ=MHH`l+mF1|HB^O%IN=V1~f{v6@9WM zBw>~jC$q3haaA+`y^>H~w+^U$gJEwBMe#b`vT}h!hB^hoD=N>x*fISZlnfQYZPs7J zst4HAZ z_M3gRgdwtQNPkPf{c{~?Q! z)Cvkvc~Y$m^Ixn`hrBI-i0O6a*3mW{e`Pt8QF`2!7ZzKL2;1KJ5-y~Hnfl7t?V4;V zwMaC3oG9xlBn+E(tf_TDBEsy#LO3wOjNB^*3#m^k+;BX34a`n2bxf5`9JqdKk=X&L zrxOXYwE%UL=(om*R^S@3$5sUJcW(A%UWJ8c&=ogKQAXr@b*OhVlIP{&#AUuHLn6K@ zsiN}h0E5RNpO;w&l|5^lXK0wf;7C)b*elL(P9@DBhUKRh!*}+=^3EFxu^Y(l78lQ4 z1O0SfHzjMvPw))!KyLi=v?xs4sk3l}+_Z}#gC4+%pRUX*P%u=0?9F#0wfeQVkZuR} zekqoF5LWU{omq@6^IjsRjtUBNyB)G6LhWR{!7mK!r|IsS-{npwCX$0zl3J{!hV;L? z@W_ZyZ8%Qym)o#j=&V!>r|yA%NdH6xHaaGa)y?W%39Wjk{y9qSpfVpSr3F^3;UB$u z$+v$R#zg9h`-at`C+9cmvU!;1&fapvTq8Iy-c62~i}B3hqZM}uQhw&v0De_G4)#kY zIZOV{TB7;VeBHP>D4w|gDZF_CXU!zHPzZpzFP$GIz>rY6)km;%P|5~uOXC6YZ;4zHC zUh*0URdjX^Ebr6z&Jar70&3S>45^>3dDVgPL#}w6ytpDk8o7qy zMn0kU9gncCj3SFSt}3Fj_fhSQ(Oh_p#e0BxB^Fq`cp^T$!j8w zb|F`$UUz{vi_IlNTvfdV~A)o-D`P%y9J z8th{Slcj9Hrf8w0s|=#OAcM3F?p2TB?>#8}Ct17WF+L9V;Age3J)qhRtQRy&ZuYzC z38P-=chF>P2Q9n~_4b&?H%6Cv&iCR9?JaiXvJU^d%g1PShl%JvdhF=q3s{?}pIFtG87q=p6iL{jfkmFQFMM^8R*-LIA-UyAAHcI z(>B2Abr77UuH=K{t&%-8u41^u;eD`oFEIR$?1rXhHs8zVr#Wx`#1-^Oaju>zwk|@d%i}S6q z9ywul3Qrc9Hz`b-6$SA?PbfYG&t|O%GLD;ox%;>QiQ%AA1Zur+J|1Vohi@zl-RABG zE=l0G2fbn0n_?`{1Z$sFB1O3yXeB2Q;vjmL9IbhXtx@kx*~l+s?7IXN(0u?f(nJl) zG|5=};f8IJ2z7=thR7cv*o;GN99pL(PSKNv&RTu<5`6P`3o7J%uuSFi=I{BT$jn5& zN+|sSE1w4$S7V1m9JH*=s)~+~`OU%7JFz`{0m>0*h!v2|OHljQEy=?o`1O82X|3aj$fb$qk43Eojg|*nu5!Uw~l%1Ns){VaW3=XZ8 ze)!efsRJGSMj8FP_Zi2E?I@cjJ*erL*P`-iY%nJv^|5P%RE})_%e1iQ+mTvlAY#!a zgdV*6s)H649f3sX+dUx8N}fi&mNX!*4ArvIl%m~S6_Nj1o{3Sqv*_EU=CxlDCW%(Y zD^Pm#O*7n0gcgHnp)_X{YF5uVcMmzHug0Fm@H|m^-hL@bk+D_A(q|M(S_|Oh#WBP9 zVmd26b#%V!TXt>+T)_wi3rGzlTXJ$rig|K1JQZj^kPl)!1V;sLBA{jY3ein?oCaMz zBdw_9BT%0KbklcLlARV!w&z;`3x+qLlPaGi&WBJ;34c+qou->2P49VO-EO@Dfa5)p zOm-?a!P}t8)2|=MvyFX+y*$K(2jdFti7c; zZexQv!A$wmaGd$)edE95LEJ^VZumm z9`MV6_XR#w2de2*{cr=}(nhIjm!*wUTM;yjB-8D^zEZx&wdvQdv`X?c9ccMC zIhW1(!X^12O}So14%%%4QTSlLx%BM;*9V3SC{PRw>nWnr*=2hx-BokLs;_|90%Vwy?Pux7OPsSp(GZhO?u9&lCyzURa^mVeQFdZ$m?_M7m<(V zre1_QHKQpYe>RtJ&-2dERX!|Jgjmz7A~N1pFPg}^Q67e5>!L!FDBkd_Aw#|2bQob( z*k}Gqd-*ZAN*;p(Z zgX~-E7(ai1Zq(dJ#`Z?fxMa>8fiCFbf%X}CThCb5;CI8%0%|TSGH(~;&t@dqCP!5W z2RVA=@eDppo3}9Ep($r?cm{#vcTMa>d}JCaw7othNh++q1K)7^26KWaM%GNi$HdL6 z;5LKVpBLtds`2dDHd=Bu8gbq~a4mReAY02&f>Er9ENUK&$HTp_;NA2jS4f**u3!sj z=_Td_x@M7Hl7G3y;o5uwJbX97_LSzO%Fo{#%O-OsQ6HCRHSfPcOuZ1FlaKNAzzvGLiS z9E#Sw$aEr~GduUwEvQ}o8qoR@bqjj<4Anjk^>CYEs{6{@WF_L8u<;5?*N;I-iJen> zavH8wVh`c7D!4ngT8Q>A8@ob4r;76VZ#VEzVW=#!B6cK7cHp153lJr8h);vldbv6d ziHck6ogEi3PxMJBu>0q6sxNK79&dz+d5GWLBcASbwRgDES>kmAwhLhW9NA=EDxVie zWkP>NuKpEBr$ssFqUDwj^so>aQwo>Oh2LG`bRhVrgg8Modyp}_G>Cb@T$f7Pb-hK( zQxi~Zs`^9}?^?Y-Ju5YlrcoBt2{AcKc$PDhZqG7}f1AIDXU=9fmCY~syrefL8ZVK}n! zev{LC!7EQegJ)pyRz?YJ}}@ji%m8@;Q9Z@XtfKpQ;c0 z)rLLd+e04xl`L(Q_`suo4o|@X1qR?{#wfe&B6cr@So6yYA9Cj(Of$#krObS$nRws5 z&x&TB0Q=A9_SER)n2lO@3OU$d1@+YN{UxaUU7wWEx&OKu#L0|qzk_$bzRAWyB3T$M z(%X81^mw z%4}ws>?UiPX&5PN^MXA?%P6dXdEbJ11v(bULNS)h)pZewR<)(W**b_+^o&36e41NQX?^ri3mGW`%QHz1LOwMrZ;|AKLltrJ@68SZRR*}`(jO?tn=PL42 zs~rZRvS*K1e`5dnm9>`19vxu63Dq0Il=cS(VEXQ$H|%`?GZ2t$_xK3Vb%OO<(UEtU z>*oE+$5PaW^^rv3TyjlZIhZFD%!_^8b@GRlOq{26jh4R(FOWt3H)o{w5A$>hv;Nhk1xbs07uH13x$nm!>E4fWAK2N#4vB!XmxK)+ifz8{UH z^^>}2%7mF&l1h)z61E-z8GVvzv$42ur`LGy0MFYi%@2VCBwX0oRb zI&tXsbY2D{GwUEB4pVf%s|nf5Cq6W7N#Aq$R_aCJS3AEJ9qH{uG6=r?W@C zad(4@vb27PzM8Z?IZkuUH@9jX*yt|=#JJTUP*y2gen!xPll|PI)i}XdP_{g1S(bvj zCPhK1Byi+Nshv$Iex#k>0a_V!tpt_rv{1~Kqe}8iPB92FxJuZE=!LPkOjNE%blX&o z6PhMTb>jwyNtFw#1}f|99oObDk|(!8zuSrQ8sR>)V$@2fE_>^XebP2z>G1oScwLDm z=i=PBx|a1KEE1AFmHJ&kwS(P$m;DzVM0#33P{r+nULy{Eb3y5tNVN!7UNW9t))x>(oRGX^)x60rAEQ1!O}Fr4aO$O z4_q_hUP>L6zNLOmS}cH12*;tz&;|lpKS-(0l{F zIX+-M%6o_|5$+%JyTQLF)u}CPxd>-wrDqdZl>{pHPJ zXTSUWwAG$<`?y9s;E`6RP?FOHSLtKYC>i-XmAZgH)2n1)9@J>ra82V#>;PwT{Obf+ z*o3(?{nbG*$KKaehE!MvhTn48o*yF>#O2 zefc2xQ^Fcb4y%$BH!vSKz;99KN=;iDVM0EpecWJW#tyGdQNf(v7zTxtML33 z@ZB?-bjen|s)G>Qq;=IaQS9d!-}`X7Jr_8lEE0Ew$IJ6iRB`B+@m(D;bW^62*LC2h z_25tav&Z2jaLzKXsppL2O7#i7$M&Ah-)efHQGDRR$8#xsfU$rCw^&)etyq?A_)y!R z9@@EZfiGWn?t4R4O%LB1ft5r(N$jvnD*>#GT(inhv?aU5>ZV?WczJ$q_`;{cehg2!(z^MO84gSj!C_ zLlto6%*4&kmzHTRd@Ff|4nfqZ(c&AG5Eia+P!>r+sgB?d&RNwwPagF}M+bR%hs0KP$YX3tZIr|bH zpZ4J&yZCTp9{jEl`)(?UE=fIwSRmZiT_n@E(2o8K`o(|{%s~vOgtE-OqePW6~RN%ccOSUc(j^g zz(@1O!99s>wdB1@Qa{^6PHKhBtv!&DisX?1NywI6`_bQBTG~6|1Y}_vG5>t$*LHMZN&MbIu+Bh7p@+HfCa}6BA;XsiWCbe7lE*)3?+7N zD|%m$Gp=8$73=uEi2)3TX@o87T_eH|OW7)$nrZ6KUa)2#=7V*-@?*;ve$@B26u=J` z$r~Oio{ty{gKR;rqxdo4l5lJ)-Zvk$G9;61Hy>ypKa%JsDyo&rEKZWjOZ#CDKAe&t zRNj+z$*m8^K|>H!mcyMN6J*4$Ot7{Rd}qq9yoZ9}$iM6FvIid12=%`&Ekw}QV?^VA z;rR0ilRR2N3fu5CE3zfMb1l)cs}zc~1>Az$Y#<$?9&yZ z`nfD$AouSTo&w7nxpOXG%U~bD5KqdMOV7_lIn3l@crR5xe}IbDBpc=qJa1&fmLOhh zC%dlrduw#w%bvC%&<>Tv&i$D_ZQr1(uLJBugaHVAnfUNk_c6_w{U{mB6DQ4pcDud0 z9g#3mafvOMT|KX90P_Ily@Q{^i(kGm^%KaC2w!i}ssLw`eDGI3pSFK%F}FnVQ7!=j z?;|j^dN24lK%LMFDef|d6RS~+d19~IgW{UOtjp@@J!naSgb}4H)V|gi(*P5t$YnnP z@u>HZkqcV>qO;11-iX7LteQE3kAC9;yTaEnZ_NlYu&+~SuXT+){#Rbj1QX#~^J@L$ z3wDm6FPFl71!9+6P@?qlzsD5$3(rRar-#EQVJFj;Pl^Bu_HWHtw8HgW_eRTm)o#5A z!F~AOjb)yKGSpu^-1lJ)gb@~%Q0OY$t%ICqM3ifu@<-nrUM4$M9)t%lENBNPnY)wn zgd_>6K1dzeMzV*G}pj=QC$xkWIv{$xyTiGI#6o4 zf5GvgO*P~l%lx}F2JfQBSjx7e`o1}xklZK4x7KBgwIr> zz~6jYp>scwQV>4Vvv_|BR`jEy7r5w;)M{rcxmb)pv|2=LF8Xr`LKKHPKC&wMZ(ya45yS#&JE}b_0j-A zcMZIn`aoFWhccIT59B8xb3=TuBlFxOQpqb@i936^Z@sSD=Bs@%#V$OZSjdCC@i&Sg z{(g{SabwHZKNnxj~fvUEB-WxQp{+IqPwdhE|JDC4J!oE8oimQG1_nw`d zot@ofSvpHcs)`Nl1+m0#)M$*Fm}oS{sIetRO#`VSy@;S70@6Doil8DQqEf727ey3N zR8SC6x$mrtes}op@BX2{?#!7pXU?4Smgjw*06FD{HY}II-4_c9iz2~KQ$*ceE80y4 zrH@Z~4!gPfOMd+<%Km5?Mqz2+>ESK<8QD-E%-4}T9kB3#;Lo=fH_O27C`9RE*vQ6h z?K$=3)2p@1*z#UjjYvf|Ye6s}Ln?DbryGfceP9GusEP8*|&02yL5(zO-pS2Ivt^*@?`uSl{smZfAf#lOQ z%DY0kj!{%uciJ_I9A(FzgQ8G8o~p1Fsfa663>0mEx-WhjNtknB7kqwHsz5o?R$Mv= zL0{Znb6@m29>(q1aLQ%xkI6C=f7oG3*yQhpk2M}p!QiGKl%^#u~-(r^t~RoQ706$DG73^!?b{E4j7X->9CDk_AWhgPx?$3hC>tkp%>e zov6*{|1clU?yLmY|V$>lCH%VOYx`Mb?g1@coY)m)_&1&_6;t$6L%mG`U`A&Smf;EBQI-6 zZ~nPUH($znYFo!cW;@tEd5EvWnE%OH{_eK*@D4XYK~e^9!?m2vUQ({bVI zykGb&Kc2+%L=Fplse6hX2a-W0KEX6d_e@8_dm;t$d4I6E@Qua;!qh{tkU5PCWytR+ z{@~8R#lHXCcU4WrG5w(iSPA=BKw74dE~|YJSP*)0Tz5V^xek|pa2--WMhHPkpH=Ir>*+AvpLL+?X790CMRaqO?tTbi8%9 zHveVgS$0>5MHRH@WYu6Ul=BBqDp$ z1z{$?_;*0ZmZypr94s=m5d4e_yTGFnpRgo)o;hFjj_2ddpz0a&0YzmT6`BOgyo%`~ zBNI)ZjxN`v9{a)J?_y}jfw6-+M0AJ96!X~b*ea$)KgI4;0E`)uJngqTp?yvfGk_S&=_%OYQ( zDw{#N1_rnfBh`nbIRIr4XdjK!L)i#tTBl}0I+w@fWj3xN2h?k61-O2L6w`j7kBDNf zs1OPB&#f-mnrL)S$OTMHwmj;?E6Oqdw0f!(Gfcj%Wsg6D9P{v_aNNOlD_)5oWVuOR zk+YHuzxaPY&b;R5F<1A)2Mmk1di`6%bDw!3siMDM$p%Ie^oLqeVM$!~5|~S~k&I76 z%qiX5B=srvsGiNku?6JVy478l#&|&wn?=+9y{!Yd5sw z?d=`%W*DyzJhQ7h4N@McEJM2+Vq$UP8&5_2Si;C~?lxz^D@IeF%Ejmh%ZD#_&33T&bsOFt|` z8H2|(8T|mn*O}d{xNZ=V8{Jub)SBsd21I-wJPjHAe`vQ05mPlMK@nw`V~)@OqR65K zU=P=Po{B#xm$Ki667!jC&3xTkmGHC)%VH5XzjH!W?`zC+KDuusXot+NDh;MEvY9>kFsmN+ax zeohaVw6SFJE;6d6Zwx%-O7(3%chX2{$m%DIF4jU>5`Scmc}3|KIp8+rh0#NM^XTWi z#NFUe(^3`wx|#?nOFjBb+IV^`Qu+DE!;Zxwk3Qb(d=^#_*=_4+WJ~QedGH*!FPl!l zrDa-IG{S9qlOFJL!?axl-&7H^dm4$Rbhu5g%PWBWs$Y zEP2@R2u$9~F2td@ra%5WTnxm8^r1P`?;QP0>&bL^G2^Vw-SEc`3X{ps<>s+cGA>Dj|&}^)zZsw!Qpp|TlMG}D<4vlwAcO_Vg6qSr7 zVKGa(+f>lRn#Bsf^f7Ni+=6-WOa;d9@6B~&j)~#`3AY$o0*lzFD!8-e`Vo|{c!ezB zZ3Yz@3cD*ehpr+J=Q2xa8i>BW0(*Z}*uAknPr`IG#Sv@b=3QU6Jk|P~G*W{}AOF-mkV~=g zG0sP2S^L2XVb`~Y-v73J&A)ih_9qqjD}zqM3-oAwgf$FtYSFq)vIiYK2Hi@ngsWKb z?@vlJw9+?Ywk_zo2S0ShL)wMeWr&Vq9O&nx&p~6*6)4nq6F3^%?W2$j%;l)aQJl+i zKo1dB=m`O$phE(Qvi8=N|7}t&zxg|jDj{%j{LFIhRmdQjIOn)mbbFMoMBWn zuKck2p+2rAh)asXdT@~sTOkN0sL7ZhN0~C@t31?405{#X7Q3n+nwr0dm$`VTQw&8q zw=5EfrU%vZr?{jab<#M6Ra`XOzwOEBccu)-m-mD{!96iPAjp+E^7F!CIPi1XzX{}T zHcxn75g~S$py-X#DOkK8X;dk@=m}kn zQI29q{B?G+0$Fk=-8S4POO=VL8SM))$8%qYPP?b4Pn{v6XdNcbo?|}L_h+s^tdN%F z*~fAV!otrhsEU3nkouTY4#gc2nR4l8ihTWb3-9a$y*>S77M2)0O1R&HoOSC2(jAq! z(QIwZ1Dz;|*1mm^;}3dm>;={j_mAlnp~?IpFs~r~Z6MSQ{9wQt5&^>7nphSgOB3d@ zH02?bi{(CS_Lx!2=Hl``jTJvS)%v!%}TjpB*rsZWId{vcoppB|+=- z5+}O#`!`2^^%VrqFbbg!4neI*x*LnZFH@NlPCvi|t$mQ4^)1JfzhS5(mHJ|d`nHUK z_+n^_$KW?3ogx=CLMetrC|Mx*A>I$nI)15}^Q<|_wZKK?lS8NeB>mmY> zgC6O0{Bz3XS~CZmx-h2S>i^G^MFGhm?FN!9n)^n!+Z`HK$C+VSqT;mqeW5IiCGSj9 zaLIU#>(<s& ziLw46u&`Vhdks%deZ0}|vU&(K9(t`{YkED}{`U(?d#-20CiOx7v$HdGgJ!l&*kwTk^_0rhm(C@uJ`e$(#CjTQ<=lfgVth>VQBv^{rGkMo2SF@`EVXm*9 zINxY~`bg37eQ*f%G)Dcg!$kW*EFe(flx9hP>WV_MfuNT^*lU5i5fTi-E@M=XdM(J8 zFvVOQy5@lAV)2?FJ->lZ&8QM@h?*p#vUJV}Fd;C%Kr7RroD25rFIDl$%FThwhPK!Bqg@#%pB^8jm#k8S0eUJXmw+>(ME_SN#cW1x7yV>?$_Z#Lx z&Nt)K-UEA^VCkTcVKH8?OL|7OacA*zFVU>;WIajsAbCt`aif^{C{b zQXN!0?X>R(HogY(kY#Azzq*aOmmVX+Bv8Joj7mb$lmiw;4hJSw)PWh<9SE1OG=R<% zBz~3s&6Q1h6|SV@IWllaU*}?prhuyrz)FH{c=gMPX|3#ex^l`RdjY&jm%q+c4}|UD z7!jM*t7iPLfFqYsh2qF@mvDJ4qv@PS4Mc6T;+T=zU_P?4NC(I79&*#xo<7KU>9z7k#V%Yh#tDr0kL7TR_edq zz7UFWJHm2m;<-OWS6Z^7XcBeosEh&*Ua_ZNrRkdex)5s3wIBc{$;q=sJ-ULD?PpOM4^aj^Evjn6 zIsNiJ5waMFltd^FUWA>-$E9`4y-a=Cs`(vyIp4)t9dR5_l?nrvTrr?aC(_Asg34^U z5Ge8!YRdNL)JeZXvV_|O_+iEevK?lUq_xGmW$h4m7^X{(62iX#IYu2Y%wBeK z?llKGRfo=6pR0+{jTB%dQP)yk2)5XXi=ztG&v!TLxg+cKQD9$ zzRllK4f;rE)FC2BM-~+YOfnubD86;it1@oX8$Ix}LY(rO zP=-LBR{~_yb|Z(kw%C%17c{A}(}t;P*pnv0(j z3CCoCJ`Oz|wd*vPv6sJYA%cUNm@*X}D*=v|_#Q{{y|kOmi&u|OUipauSQpruXVfY1qRIc{Ad^&}Y_OA}v74 z2l_}V9QncB6#K`cy*7!Iw0)&+Ggq1*PY@OygdAVcKA?l-nXS?U;dO5WigK-m|8mrY zFmL`u>dOqv9~kWG0D zLcl{Jdd69W{1+Uv<*Bad(0)9+Py=zZjS{RJ%_)ZG?&Q>n>`5oEENC`)Vf_ZWU&FKC zUbcX{3r^_Ml7!+E^;q`g$9ZYyEt)|{vLi#L!sCFnMWcHMM zE0e59jo`$}Tk$Y`mis?<{@hUl2!Wey^mYZE=|NtMk2aiDA@p{^n)80`QY745x2ENT zy5l!v!eZGjU@-{Sv$lkQ5{(Q$h@f6PgPK0>-msH$O#yj$4Qh6iQ`J!5uyjGFbwUBO zh9h9@TjD>zZA%Q1pG<}i)`2llE+Y@db*xUz2C`#f+=3fZI|byPS-!Bd6~TVqaA)lD1yuT4vPn(jCwbZ}FT2C3Bh8Yva{7?P^L@+a@Z1O<5{aRbk{@Nh6Gkr+K8 z{y&@mttaCM8c6<6;4HOA_(g_&Fh{q3e(Ocfy644m+s!X-F|YT9o0_SX<~d-S{1jR@ z8rF^0xE~5Llc#rzCyu}~(%QW!eD z4(^WZlSA@Q+`dp8?+H($;0Ggwn>532ExQF81<_;~Nw58FcJ@eYS6dX1ZKXc%ICW9<%2|4u!8|DD3z!!kx2 z--Ge8R65#$siULVPKlsNP$5v2y7Lnk9+uC7AemNjN8NxXK>*z-p(T$b7*r5|-GfOx z{#9RW&Pe5X_HX_W?+<|%+pKpW?A-8l$a@r*(Uiprx`_5ZTk%;-CIN%85!g;IRjV-i z5cp7uPKOMob)t05kp4|_>A`kPOslAYf7MA2fUCohFf9TN#I*k2;BgKxyy@5V+pj#} zIe%R)q9jF8VD%QMg@gKrji|KHi`aQz{Q#zjZ z#}MC7`eX>02LyT2aX@kv*BHf;YHN!)19D74f@o%&OT$pY%xN8u9h?98#Z%D$-IqQ= zl?H<;B$kRFwXXgTAv{?G|D@pz^6mt2Vzmi@Y-KzuINZLhiH#hEDl%EEd?P*0#) z?kIT*k?#Jr^ZQ54gdIp|>PcVh1Wa{>{I%DP7vZ+}w9=`>R&EKOUI)#=ilTK#u}o}! z-d1!f`T7RCrsNBE35Eq@DYF40vn`*(bDg|W821x2^iBSmHwAbF5v9}%dLKmqHC%k~ZAvEOVp{sm9_>SUk&pc{PT5 zu*gY*FRFqMuJ&sblicX5F zvw5JOw@lnH@FprgPJX?nYP$E_E8i2v9m|L@5tI6?9Vpvr=n2d@4G)Ai4GMY$M6vdF zZ!NnP)9c{!9NIx-r@f?0hH#2Q!w-p$N|C}qJCm`yJ|cBt3~9j)0B<|=Km0{+PYcj#=l+yuu${da!5W2cL>{8>ZP5)NJhNFeIL@4=%6{6P37gOWJP-!@ z3pC1CH`PL}T=rx9H?Lx$h3u1xs5_p>)5KMsV|CC~7i~8#ABYMXpFI8`P+}g#7djN- zTTd*U{1h%3G)BWY^hM}Z_mTI7G-HKrT_TV_b^OVW)xN?ZO+L_6$7dVmhM5eJ%s#a%6^r2hrv2Omn&7E zwYA8l`~#Tj2Kd7CviA^ZW)(4_aWwA8*|BJmx|abo`O9#nStG|Xg*RdL{MbY0M|vC zq~N2}{l$wPbL9a_{yL+hDa&NT-N+eCUYFM+4x)YTC~A+{ znf~~^6_Yl0Il1Cnobd$~*;Ugi4$a&~#>X4%WE+j@r)3$elUy{g4iy;fFo=cyX7{E^ z>0w6@E^_fGT)PQqGB(Z5-OXWkVy~|Fd%Ai6vi`W4jb06J18o<4KNX49Z&Bl^I@6nI zL?7gUEEb>Vk_r_NrU(*EaOgRpUGpbMZCKyQv*&nFl*|EHFX0ic#Aw7Zmj>hi;c%TX z3|2(3?>GPhKcww3&dr>2#H5!7b3Y0-W^o5<-NJ&*9~r?P|EO<6t-H$GJx#i;v)RMD zLH`benk-{AqN#XSWY_l_w=WVjXzEJ8bN0TZ94_N^`8ahyYXgbx>kN48aQ^FT>8are zaX#}F-1UvFi%G(n^dq{rK-gKyy%5gx%m3~Srid?Tr$Hf-sD*u-@|RHg0<=^o zJ&mZm$x|oey+sX`aH8+mwYo^KktR+4`k;8KxNJzc9&?I5fJNcG3R{mZ4tW4An$9DR z+f@i^9B&QrB5stkJ2Exg) zKANvgb#4obM?Wyi8h+&K6g(LL6+M3AK&1oQZ$ZbbKyqPtO$MA6CzH7g#u+ylwtcZO zmt+d`E2gyQ?^-EOHxwjOc>uT5ewd!5-b#;;?A**I2-xD!ZfGtjF`$?ZCNUmFn1-IH zw1J|jDq+Votdq36Axaxby{GHt&W~ojLzkWE(h;Q<;m?l#3AaHw#7i&cy9(`A62U&|{9sL8%PiwhE&p}qV!(Ir3{XwtM7BF43K?kSf4q8&N#qT3;23^zo(RDUa`moIeIW%bxbRh%l`B|5&WAg7Olu z)v*4AV|xO)jlRFS%%Mutp?vsL)9u_1U?xcz?cyE=J`IpR`4Twq{OeU61}!bys-XBg zBB?3{tLGcjZQZ5(yYYu+Emv*A#u`VCH1`OCnwPjwFKQRA!c)vroUV^89pUM+Ea0EJ zAc;dfDdGO_I1YQ)PDYBeHr%_}#$Wx`ULW|z;H0hli2uAh_L|Tdqrc`PfLI=we{j;< zFTEju!ZC~gxW@*YflrFm)1`+GlSdVLRlH~DJF{oZJ>l~@VDe4Q9)g+!NJdyD8yE+B ztUr=nUNR>W9|{yd2EYdTCFGXFy{~o@z$Npy5X+uIBcT_;(hQw;v>u-^J%`q9Y?v(V zT6ELtv~JK(`bg`YhYyJ_{DUi{>n^G9?fhlEu%+)Tc82H3w&)L(z)`ybus%J0Vo>*F*5oT+5TAU|z8K z*unq(bvAkTgLPK+THGcExMVqM-ufd8K-x$S{pryd3YD}=kC?Gz*q5t6z+ zz**nq*hz>lz$IlTEPIG-fA|qsYEMs0>1TOQ=~w>7_9(Uk38(m&4Wt;}>ohbHQSdXhmK{4Rm&!$4C-t`rIJFZ30@ znXpYm!q+bL*tR^* zNuJQxb|H63?2zj3y&8nI_pXM=U)-T|agkJCHD42vF!Ky8@Bhu6RMV&1M#HlV5DzV9 zbZ`BSdMO#0$J}M#1D_c)MWxNW>Q2!A0E(28;P3c@tix1%cGwHapgKh~XFc(G`rG75Oq%00 zvre-JJ3kX)JM-S+faGF2cn@em7*7L>0mvI)pEQgi=f6o=`+Vq%@N#g&cq*>{LJxDF zd+JX1Bo6st`Zlq(`wrr*hr}G4+QbsS-<}-@v&UvPqT%W#5N?VL1S`xjWXL{#Q94uF z1ejWRvAlptQ~ya>Alsz+!m20?>byakxnvXD`cL)3CK#h?RXWBUClCA6r?=x<Xxa+KOy|aKH$mYdpO!jIsH(lI>A}Vy{t5L-mZg?!aYlV^2v!eOc zbkr!MUh4C}DOV$sB0EQ&mM~co*HL%_e0tjv=O#%{6eCYCWqqFIT2i+XDS8UYMgg-I zJsd2%iPyb7up$Z8Jdl9_Y9RlN5VC2>;T{7 zGvU6;hW8Vh+cE~Dh!ylu+As_QfC8{d+xe}#`l;vT1UIp+y=(}RB)fEJ4!&vC^ergX zDYtLE3RcaKk1P^Jy82YHi%6CsNfy>j$1C$8PY(uUO^~Dx2qZa>=IBpa6t_%`%61U5 z37t&pd${->X+2Jj${Fa(lAr@nGdM>Nmuq8pw2u2p976XuWo_oH82vM=Kn z2+^}*Mz@G6`^GbOsVaESF}+s!!w+5xZVsgx?5u+(8FS7M;kjW{%T}vx`zQIb3?A{- zJ3GR7P*u)M(>+|3+-||^y`UY$68v-A=|OiFk_6l1%a9tTDqDhmvzPr%%Jx%P>yb zJg(8)XhNVaszhR)falcWuZ7*ivj+&VvExn`f0sszhTWRHI}ttK%)S+Fh%O*6tW^DK zN(VFPXu_0Ix= z3skRK?Eoa5iu}ssC`j9u9F*Z1VfKa2JEi}5&ac~zY$~$xFE72U!+{mg1-lvMRAf>QB!zfqApCW9&t}LTBNo zwHE$N-a&{QP(_P5=djmG9W5wC4eViNLBm~9!uD5eWtOT9>E|})mCCY3zGZ;tAHFna zC@zwJeT?41ADJkf!>63B&cuF=1xOt zrqKjX`rb82WwMQk?U-p3bS0q{2O!|*KyEkV$}xVaGB|@?KJ#zm;$7-F(L-X%%gQF` z#X%>PdI)hm&jphVv$aVcT>>!1D z8Eg8z)7$rFPAl>w%u-rj!+3GgaALsjUn0N$8d^6*Bdu}F;DZ#J`MxI*7lP_SeZw|Y ztGl+|%=;hq0i}(B z^YYr!kL}Wa0eO8dI)w||0GA)c7I<9f8rP)0y`he;FmlwZ4xw?D-_nZ|eVm=t6$#=* zMTMYpJF*7RXIQGsM&3qZ$)x=44k=J?oGN)rJGZw-35SxTCSF{{?6^ zk42 zH2^9mmt#Et5}d%7*hu%ovtad&$6Wn;A{)RtHlJdnKPs28--nWD*t`n2fYgy5v^2sl zZ@Nw$67Z7Q(eUggpT3`64t7|Tk-5Oc4s=h^`70FpqX>2+I1B2R&P zG)`L&0Xe4=F4E>QxnER8&f@Dk^U7^;!Ne9m(3r|?dj{^zP^w$de?bA&h&-s{uRt?q z6Y<)7yj3cL7tJHr1|L9Asig?_6gF6^i;(`g6u#jSH=)ZMqZ=mJ>;YI43G`meLk6#a za>K1UPH>B6ynoJm2*P-r{?RX_{dwU&p2(C*gh+l{mIIgmUd^rSRFPB^{|fR&rpp`Z zpup19RfO5{&fCBQ3wKIu@1VSK6l^O6(Hb1QP8r?J#SLsf&j}U(_WHLuV5MpjV|EU& z6}qYx1u(T`V<0az$YZE6*{SI-vv{jHXw5!ZI9Y2f4q{&&Oj=ao>fQ{eNEBv(+h!4nvw0A^uQvT8+(4|lXXjk6Wld! z3#In1bA>lG8+7HTop2Qhn5I14D}(y!T~MjP?9=G%7d!d+oDT}dPwX+ja0@qqc>&G3 zQGve|FEkgmY$(vnrY?-53x(wQEOK|0(D}nx$4sgbemAe*rQ!1hgc6F`lmb z`JBT`4h5Z852DAiWq-0~d+!xDiN7+-^w0%(gMn2VQ6Go?HKQdtQ?$}-&4~Nv$qspa zJ=~y?lw5|ge(5mNpWLLPV}Yx7KqtNDiEL2*%C0r3py;a;hR)d`S6qez>mI}XX*Kz< z)LXP)gj9|soK(Rf&BylSGCVQ7F%Sj*XjhH0{!w6h2F^N@x2EEU`k8Q2c^z>Rf}?uT z`bMSH6Ife0l=gX#enF(b)6$B5ygvAgC%A<^AKE^1q59Su3&Z@dJeg;bMF}tKSnSq1 z4msYqiN-UuFRIPbDw1h_gtqJNrUAnf9lb7ITVrVmx-iB+YuR=JYPQ$^N`zfMM>;VmtEEnX@B)mP`uWC9q|uYVd3ropofko$>aQ+ZFW3i6#^4-}%HlXw>$ijQTr0z$4&vr4KUwk^%m1zSeH&xcq4#(X47XLa(BxPc0fj#YZ9mqJu$DKhBsFG7 z*K``3K*u|D)z98M!X@q({`aEnS-Ry6&72-;fneX zuds*E|Kq`4q~pt228-O#wZS~}KCrA49DM{ag7-!SLoi#9YTSPYo0SjGErpDM%P;}D zXC?YUy0J_HydD&8bM7Ym``l^vU062LzW5~a^~jZMv<9(^`F0tbCDc`S=r)N`E|c8N zz!`b13`$0lMX3ZYvVXG>r^Hd`vvc{&3a=XF&{UxhxGIqq;WC6vgA9tqAxysBkxVf8 z&u^ZRkYV>0Xvo@x&l}N!ZI@9G*J_9^<|2twBqkomv^wvcG(Fn_hhx>}r>F`uad?JA zM7PMC>V+3;pnH7YYOgw=+rK4ytjj^#`Ry&pSdyV*^k%&<>({6zG=VOoabt`2(EVlS z7U^LDD1sD&F2FTs8?67NB~f4`Ek!eFOJe*uH}(6j#*wmG_s?pXopksR0Ll2QhbZ!>Sd~?Jew-HO9v3iN$pv-{`8YU zyS#x86Mk_^P@)-*o93Q{DMo?nq3O{@<^fd`^gdqcrTEdmjI5E!&*&v;j7nM=Kqc8- zUy#j`!FeQHgns93kcS#;Q}L0&gpCqaaC-@VomyW%;AtR)Oy+#e&9?+;=6-~07UA8y z=IcsS02z6s&d0W|;)XD`zO#`>^=tF##Z{RyCTKq+vNyAZD^nao^*PB;DunzPI?%IzW>+{iLqk`me4+2ygTqyBxBBoBidA38=F^4jo9C1Yl&j87N`sG?Z?h(>0)4Vt z8pKP_@^%#;GSx!XE+SLsR+S+GlH~rF!%#5-4ciSOD&BFdMvWdxS)@;e=5dprzn@LG zSEI&#wP#Y;1txj5SAf&4@HJG=S8pw}S~Mh+#ytOlqs{-A%t7rI}< zi`~$)hfD136%orOxkE%1_z!uHg}uuGI^T7+6rDVPpu5S@Gr8dCP~=!f6=2Em@mcgJHVUfq85AWVJt{|`ms;ttTH+7=|B zM-mS^op%gu?)OBhK!KJ}gg_zVz3hOdVp2G@0aN%3G%AIsUZ^OL_&z_jVjy|n4K1k8 zCy9sn(eFm@82EbK6KR3%sXmXF;TwLc4@yxV&?KzZLdj#Z^JSp-nl918_57;--%ogJ z4A0W7?sLnHoYXoDQ}+nOe$qr%90HB>9L3bR5sYaYQp@0!;0KeN5ngEX8vnyNaY$&N zXLmOXY%Z=9KKeD(2ZG_KKsxhH6}&)Z;)Akx1O4V*!53?&;ka{Osz*z$M#y`Kx17!e zjq}+U8@Q~4bX@W_!Mh=KobBJ|^RreN@DuRB5+>&_Y105Y-0bzJeXDVIKe_u9&$QxY z;Qxxkp!;({GqkRKj7}K+Sj=3YmVeN=uGh?xh_OU=nZ@GocvT3H51Cl+-voPR+{Iu! z0{@?CJ*Li5(h}-2qQE9jU1_cM1C z&Hi|fJY9N1;NUaCOQ@@E1hM6biUX`4=@`Uq3spCUC-6gUvp=mqDq)}_Zdf*%CnHpp zd4Sf%!*BzOnHef|eWO1_e=kh6M>74vw?NJ%8!k|?B#Ee*)s~@0eM`@jisGgoAGW-= z8oZ%St5s`47{L=&Sq|Hx1$x&^l&dh&5oB#ExrICsNw4m@tWXb?nlqY@=Nf@&yNdJR zQ1(-i{$%-$zaLB%#w>TTJwp7$p?!NsGcrNr_4|IhA9JNCYiv@ZlZdIevY(>h-5V(Q zNs}%L0Mo&_n2v?tk`}t3QD1YT%6EU03c603WdYG);QV&$kvigy43Sgh~IJ_Jo%prDjjrVTDer)~x3a(SGVOQtdy>hWk zpS6l>=xHc!2YJFg1RH;GbF7?eOKDlV%{>%UZKcmTx&zK3V$vuK)L&yb@7G6;);`I| zj8GxSo!Ar$*ch#Y^kByd;I1YLM44Y8?Tcm$;H1+Qy+74=Wzq3 za%;Is40~HSoP>(VTfK4mT9&ea$1;tM=7*=UOELSxlj`rvR5}+wPngwR*x@Du%mmR~DiqYJ}i+k0# z+M@IM&CORKG<#UET-fU+YQVF0LJu zJZ9fOy?rjIc%&3gQ#zn|Z@-;|r!4ab*!MN?Q`LKPbgmA!*IIt`?A2J`;A6Glavna=m?;f8*8 z90>D=h+J_)VNP9#j_*q?Tl(%`^s^appe+?D6XlU&qwoolW4eDAUKXTyAhCn!d(pWO zS@ZsU(_eFZSC&BmilePcb!KYZ);CT5eL>zTb9cUmi4+v&j)E9t(s z;QVme1h!3#QUd$iG<^#%&_gr69P==x43skWs z>5!3XIs`+l&jo%WBp`lFB|30+eV&Z*C8(UyIm!i3v9&4`gD^s|($wJ+p zwtbWzcQ8GqwOX*%uta?yJw>qJf4UDuh zdAG}clvk{agbAHmZ{X(M3YcQ|b0q%0~vlF;*lG|U4sdo;TdU$pZS>$e`yOebVF4|rf z?B2~uwA8y1qpYbO4l#8cu=i~oyp$wKMtL$9QE*Z~KbDA-6(4`Sj)upiE{&JI>YK|r z_Qygj^hS>Uo(Znfb}_kR5OoI(-`W>SE33ikoIw)ZkO*CigptigQqN3>z4zDXNsU`bLtyYv)A%|Px zKf_#itNSujyk!~x$eP7@yfet#KQ zg}@$a|03)WzsBt(;Z6#fd1jr^qwmvokqd3>m%lGYM6-uMi}>*OF+Y|tbay)F=^PlR zdwAToR#vL(8v?r>rf)mg3GL%sPHwS*oHd&uDOiIWKb!T>MXQvZFyz;? zb}S)bZ5L+!^T?9B+JO?dG_-`7MTQ6N$JC$70Pd3Df}cIhf9sFx7<0jBcLjTouxX!D z?6IK4dg3T(Htr^N#ZvJ>BgJ6|-*63%*{8tkJ};(v^}7sBR10QJIz~n|AnQkaHwf=I z(c>xEOB1`$xx!7__*0X@-w3mRx+e7w6v3wAV*Km8zypjG=LB|}Rz8!ck2qbd<)lVc*PT-p`~MPgF^UHvk>2qcS}`<8p*5nBE!TNV#WEQ626?$#6Y z{B=&w*~0J*=%F+yweC?@^pM@{Vc*GGm#OX*QPcIDFB-cK8TKbD>;C5XOrPmOENsdd zWWcI^mmd3a59+y<(L~~g8uq~~7D;K%GAHao#)sOJ2EL-61CdiueOR|WcUX;pJIuzH zX`6%P(=UKc8y+GewB#&ZJ>@C83wzupFZ+9%dO8iW)`&-+$MVx`QiTgtR{2@so=I-P zftc$S`Srp3wD^3Rs+1#I;=Ds@mZatssvA~h43V=1oFe+C!EsSKvt#ee-dun)yI{{Q zSa)H~xQG2`?iCAe35MnL-@e;vNV}0>?|?3|Zn3}zzLffEJc-Bk7{-xZ`ZrwdK zYtR{s{r|pIo-<%h%x~2p36|X%;0pzAq+X|%Jv3iPlFH!{$W!&|wX(^MOt?tC>@xGr zL`K6*h_WE}gFwiA6J*dS!7pBaE64Mj1msZPUY~k037OD3zd8U-q%EI^RK*u`svKm2 z1~=ZqgFnU*?vEFuK!aC$twz4UwH#!S;4GAfZHk?(?oJ!2*W$kP9{A4eMjyF4bnxVEM%bHj!0Tq~ao1hE+Hk=Q_0!d?{p$)_+Y9e{Z~{h66L)}^at(yDiak?^ zteiSFVzP^@bm{o4Y6#XO9n{pLdV>N>(N8%gciAcZn!0ova^ON2XmpxTL6E2|%FTt< z&2VaAaUP5>Qr^V41+smAnnlZ%Kd?(rjKd3r&3qoqxxPm$A=ql!?Q$@beE7FaAU|dz zB$<@$p>aZz(|_5c;a^*Rxt;~_N-y%G3cb_$mRTb(Gt1@kEkAemV_ER0eEz3nHr8$Ymk+R5@M&(( zP)=i~l#I$H`X^^0pV8qTu;Sv{YV;gD$py;O3u>k_H?1Ftm}F2$gE@@8r-JK zvn@!jDN~?8)Z9_JG!G7PUJ*YY^H}Cl4%R1UVL-?HmVqVLs4c3AA70V0-9{(fM#?7D za64n=y+3LWs>h&5VwrE7V-;`yE;w@p-0hvi=&eE4hFf=?LBqVz&?r^s1soC>y^%*# z$+R2QO0XDiSi$$xy*p9E^XLRxk~d|WLvg}#ge&XC!n;w0 zhRO0{qC*0%NfIoK(Pn(1S}%O)HqQ{SP?hDOp}OPL>KdCH>Ll&acahE#5=o$0zqhbgxhd8qrE3XI=Ex9-v zcihcR2o+s(Bjya3-r+vU=5Wg{pC9@#r4u`3ebDfbxw@+M47f|+u2u`X#qTC?jZ4|u z3#fObt7?mDmhc?7%Ni9RF$PRJU{l_RiR8dy)~oz6DnV z1*hRSlet2piKaS*ZhB8=45exY1y%x*MhMGiopg>s{)t|r5+cZ4e6kb?fS%lf0P}F}r0;`o64du;$OC_2N+Gxo&}otzrL}?8 zO^~F^%Lrks81D`U`8s&(bdJEgy5mX}|4H&Fj=wwtuZ-h#a>&$yV)S(11%fzk4%U1y zCDTQRZo%plmU+kJ-uRpMm&uSkc9{QYj5bcNIXFLkGMRae>iF`EME6h-+_Ut{0_ovx zDHbPSTgz@ZY?bkq+0%P)&Iio2Q0mm_=Q~AwG7?^d%mj^BR3y8DxhpC|SM+|U#!i8& zfb!EiDAtxelf1*iyl3z_8d=}+sq5l=GPQyc$6-avS&rM6U*$g{Mb~hzR-+#>fusJd z$yVsz5I<4Fs=~p#?bv4CflhH-T7&U41trRy&{CCw;jUF%FNf z!Uw&4Dt-QCW?)#s7kdwb6T=Bl=AP_O*rfROLa=wu6z66AH* zjj{lkA%A6=z>kLUSjV;?8FC9Dmk3cDZ^N|M_XB*1?_p@cx_`qPn07tw1$%xdpxkNg z`i-TMMyR$XD-l%^#_S)}NQ<7a2c!>32F+CYfg6{3SpInlUZ`Bae#H@T*9nnKK-GA^ z&Gxrjq4XDZqhS_GHi-)9E2-ZYG9c!APt(1VN|;^9I?Ry|q|-KU4HIQ;tkgFf+*a2~ zf~ET;=W&_$IlST5Z1t?zJxlDwX7sX2Q)VsDsDM0aV=3giLdYHvYBsD2UY*B~l|J;Y zQCDngQ1V%*d0wYap46!42wC!=_Zq?D&kPG<3-$3#vhs?JkULEFfbt$tAv*d8-X99B>}#PhE>n|?pN5XO1HsO#!2m*{uu@(So&2{q+v5%J&fv_a~>R)gC~TL zS0PG}|Zp&qQg&8urInFcK_eLk8w08OlqbSZgt-V=h7rLq;2aqi#8F*FZm1 zwBqC2g8%K(l<(hQK<}omND+_Q4Xe;NX+Z`8JhaDFP%)?`>dCt_KNd-8-V`KgB5Za% z7N_6CJieROl%lOY*EwiWhDJHhM63`yoA{2hin|We_hW{(-cCF)9*%qwxuu=$9ZWAX zn3;zmm1VeSQyU&g%D>a2c4|ez1Br*=8axvvDl%L!H;h})veoe;`P@M~ z{SuQb^yXfTN?TGTUZ9am>$mBZO)U~98b10zoV|HmPU-(Y{(fEOoO?O<*1p_nl@^qw z2+3sM84QE5j%Cb@VVJiMW{fsPNxL?bb`^!B6-i2^NF+jbDND8p`Mu8VmiZj>{cV4E zxbM4ju5+F1T<2O|ujc|W6*%CD-UUJ1N+xUE4ofqFAdyB{mplqL;+N=fj-JLJ;~O;D zqi!e6P6=7>O3V$mUo!#6AQ{QWK03|A^h+$^+UYtq2={Vgv{-L zq$;#{?|WK;Utfbh3iL{eXa~l+-)Nxl*SHj77`N>8#1cyhj@thyQRDqK(B5wrx6JV> zwP#9E|4@3W>ZnvXVk4?B>4Q0g?l)Lv)b)>(j}(Ag<`BWaxRJy{_kYzyU6D?Q(|xv@ zd>BPK1Ys5Mda4w~3xdbKaS4)lI%C4~M9N3kQ)V9=re zJ|q5G)oW0BSW<6hl#PNJj|AK35uJv?y|3N{@`U;M!HgE$vxpthJ#h|plDgT5L;Cq8 z9imgch5~Q%BZH@@Zy{U}>xGi}mfd%tR2r~)^{DL`36+br8*FzB@SBDm_1M>8YbV8?$9Saf1C5LhP=qU`7B2?G zp&(+oQprAD7JLWtUD`CI4bs~t6}b>9C;8NYNec=_Xl;1<3kuZey_v3LLnz%=OU3yC z7qAls`^*?GP=}@d8$Eh8>rMFn6K9zIt@I%h3emDE-NE-`VI__k`;i8G99LD`_<@*g z@wm66v{fH_($6KqR=MNFMJPCfC+gGZm{uB{9R}yvX^$(XUOqvQQ|_F>!B)Ej;)iG< z`)_QXbD^NiJz?AzI9;X#l;!_5Wn)DFZcjY*v4xJW+|XoY6ui}+Mdaz{M_C8oxX zBZlzPY3H{?q72{vpew(L)_m?QpB!`eY{qOsxMJ}IqwC?&zVL}vTM_6Ds_tK~;PpCA z62KpB57{W+DY*ML+F-(6If2l{y(%JfkRUXX!)G29^-KiS$4AJ{4*vA<)2|24 z!G)gwf`41aaLaT9+fX-Oy);{+BGwBf`{bUG>O=G#J^H5Erj{b}Zk6hH(xO+2LMPF#6 zBHb;9e(5N!Sn*p(Z=(ycjWIS4cOgO=VGzgV_8K$Q&7rpI?$iA!&6TFC@z-RiLr`zk zH5wJhF%-hbE_TzQ{&7&s0`Zz)^))_wvhboh#<3}|b;yO^i{>+m4DVSh1I*AqpBZObv5Kkmz(9*tIYj*s$as#Q zEVb^+!`HEWwI@9EAdPWoWKd8INyE}l!*1MXeN(8y$~aB*uuV)nw}|GuZ)7u7IP&ay38kaauC~w@reA}@_UseI zHK-cYus}{GIdSQi#^-QgA}a2+$IW}JzBRX3cE0SRuS8+izem`Pez7*H_T)|w7BM7N zUsRb73$*EjLLt)lVoD*)p@GIlMCX_gP*hB>+6{XJNHPuqIVA1}{2 z?2X|HVH**pIy_jr@_s8PmiiU84y~*)m4fRFHg~i%c+#msx_gF`8#6B+ME-v5jGs}? zWQ%Ueevk$uH-{)M-UjBvM?mOEWfj&JK zYtIj-xLlhVmkb^Q{*29nw=h2H8QY?KPLpN8GL2OgDYdGmlAi@W)tSKI=vJ@ZdP z($K=~@bCac-Gyd^fC*8B{Xr2}I_A{^n#9EZz%|pzFYl#`avozMC?Z{WWX=kvo>#XeQH!Utw8VYtUuXP55V$+Ome0Jjy%9HA+Dr{YCXNUb6x%^Et{rpXM75=PKa2YtSzTo!uPIn6xb1VVSb(W-ORZ zwLAE8yr#jxy7#cl_LzIr!U;s;lq%H4?*)5c%vjy4=@79YC>GK9Eu1*v%nN^8@wp|u zq{!Z$qfnQ*T#5+~jl``GsMDk)uEFTK(PaVI+fySJ>LHkrX?p|l1s0|rgCb!UH{J8A zoFXVV1N)ociew6k@fn|Swhgi4{(&g%ahpm{IY*Vn_)&L?H5Pb_Ty z*0K9y|NFR+iG7#y?u;h|W1KTM)CAvKUGofs3>P8YY`UEmy&M=kovFCXwI(0Y>{7q1 z>JQ2F>67zTe$pQo zv^A0cT4g3kwy16cPM}BiVdDo3;f`$nu9nR({?08x@HP;B#H#S6i99?>n7j3JFVL&I z0(y;j9o5+x?nlP&HXw7p6qRVsmDML$pMySUyL1}z2VNeC>h(PO{k)uU9&RQt!9y$G zh+L(j%Ymuy`A%Nw#;i|Vep~}X3~%WtoaWtE5W2nRVT8{6d5Xu{)=NFK{AQ4%Lioq& zMi_9oDENq!42&Ttiz^{;;jv+{PcNBkbKnOww$@tK$K#E{_khA`Dt_Jl!Q-yNcx;4KU%UR%06NbdA|eNoB- z5zM!A_t4?r8-2NE+cx3m1?pS8lKoo|dec~U&rqsp|8DBr*I;8(pjkNoBZgnZ*M_0C zUe@S;8JXUR0$_TA%=+`8xXV6|9@T}~ih%bZe~00V=c1VZ@No@1Rp*JcHzlMG4a%nD z2Wj(uH>CP+CBJ6}BZ6CHG3F|9gQ~zAs$Q~)G=5Mz7B=Y0nnz#EyjI4wU@3Yl9j`f#pfJB}}7m#O=wTKtO#v_{Pp=gaZX zW_$#J_nu5P6kCW=rFeOMOd;9Ee^goyXrzh6m6Rys5IlrZ0k=@WOk27@!@kSzkeLYv z0w}O^9ID47=kYiac_VkbOhfjtEJmbwgEUfHHJAL5FS+!W$Zu>}7s!o_RY%l`F}!eF z0}0wqn3Vl$8M(UIA0>;CRgB9Cjzcc}bTUD4lU_-^8pdo>KAB^NdC!oycKn^di0&S! zS{J{v^e6FbmK8Y!7l)+vJA4YO_DPhkW=z~vL`XkkJg78Lm0XzHf0tF~H$fbe;kz#{ z$^_*&b__?4P@ny3t7dYE=HUvCNA&53qa#1AV6>A&qG#Md&X=W7 zpH!RyDX5wGOwaJs53fRCi?ZBUnk>YL*CuYHCZEH^b7kQCkzNTOG=X+8sKA#6d0#9N;gpHQszKN`DbR(2z>FgXp6XUoOB3+MigfyH63}l% zkN#{zd5{WAGW5Yj6WI-nwLYaUE8aN@`lG5_gr0UFBEar(>oR^=q(SqEX5rlHsCh39 zP^JqMaj(HN3cO@zgySoTzhdlfFWjn6fT^I6l;K1@(c7SThq^)?@dF>QXs^dheWFoB z`%i(EYtADM|7qVkePkaWBAn6-U0DyO$0TnDW~(OIZ#%@(p5CT;E*nk_PnI4*A1uyp z3pokh2{hG~0BucLS^(8DHdb=2vYludK8jVdZ+_JY*@VXJgV2*t6NQ z`m56hu>-VA*n=qe{3Ssc0ZJ5g4Fcnfzum>Ykt6J|Lo?uFIFE}0FJ%&mMvj~?5;*OI za>}t3q?7iqXw>S zhqU&|ELNRyzQtgjrb?rWphBOkP&ukLZo>$KZs1SJKW42u0S|uDJ1awPUdcg?28a_S z5|3m|DJ(GBON_9XV+(Mz?hs3_YJellEBo$)#j4L(mAd6zQ5O(@(3ow~dzSj)PO-av z7-9bWy-7+4n2uO+cE%}%(p%o56kM-`)IBP3TK;M#a5W=KLiLGoIuxqE`^SNo53E|k z6G!Cj9NQ2D&)I#%OVX7WW;IMhsTX@%7R+kgCZ~6xWf?Xaf3MbtNc1*w?O1YJR58dx zc6Ughr>lqua~BSeeSZWZM^RAfU_8A}eREbb&R<2HaX!Oq{X6J^_G6up$@ITWIUEK#D@f52Z5xNGH|lq zP&z}aeg2fuX18c*4pTn79+jR-sRN+XJe_Gle!bS+Rs?)mOK#%l-R>n*Zv08X>|QUw z;b%m7Q#fW9=HC8B7-gzRl^oL)ctC+fSR=;TqTFBid$&osfnC4)We|8R?!=gsqkq2) zGaoMmyuKT<_Hd6w8dD9*yz( z(u}vkO1YaMVp0QmBNmvn6yTlk2as^!PKlV1EzXo?46uEodEEg; zNm%O+0lsAp6<#>BNn(WMa-65dKjxg^XE>&JPf99T4rA{ogyE@_<>>rr+O6cYjUoqcmMz8ttA)fX~oCWH@o&?#WQ31{Z`lfy(i)_SC_-F+E z)&ilQ9)L#4nf`~MaHO|$%LX${4TO#_=OBfA_U7t;9GVsge+H7mT}YxXrR>7>pX#7s z%(_gi?Mhu03>_CK7p3jjuPsA3 zlLNlw?AH;E|8@9B`;fW3EL}7~tcZ}Pg z1LKkz)Ud@D4vC{i#>FU_DGQ@Gu^e~Kk3*kMw)>af`>+h|8II^9tr&y40@XNmz8aW; zOIbzdd6Y7iA7LXcicn3tLxIbh$5S`#h*S@7ch`UOf)g3$Cs|*yoa!eh_y1b!(UQ6` zwPqD4sCg7RCXih&Cy#@hV0NYx2>?m?$dX^#J(t0sX|R%dS={D!IJ`B|C2=X6B>473 zFWCqA8F!RO*ry}9A{z1%YRARS$&p3gg9WD~G1_FNaHRP`9D}t5vIpiA%!>cR_IGtP zcSk;0hnaoe!msl0_f*W#L%7A!<4jjY2T;s9S*iJLkL{G822M%Z5hC!_o+BLoahkzE zUvHFUDf&+SW;(GzBglHGCp?fgG8`}HySq!v_)8dGj^T{4XCnB&LW!2M5UsCxEbEZ_ z8*9^H^Dy`tE*C$A4*7@i`{^{1=#m{5$>Ed6Kc8=45!|;@-&&_oaKOgO6;F@oT{t5T zr144B;Ceym;Iqi&AKHe5A*}8f z<#KlE8+O?lC>D1Jk8g!VUetb?h;Y1qi?Hx(;60>t{5v_h23S=XxFW-vUu2Z)xJiOvN-!EH-S#vjH@=;izF?3KyUBNC%2(P_cRWw#fcRm-s)TV ztgFrg5#Q}`pzkv-%O$(q8w1$wKjlr17_ja82Q;$vkw7)1oo$GKs_7_ca?G$mdYj97 zxqg{P`z!mgcAtwN1_NWqT~TakdjSpZDX`SFYI1jB*PMd?%h;k?@dWtLgXhx3%PCK! zqk0vI(|_MOo}lvaF*%}Ess+M_zxyxdT7{1vy+(F3M)6wlfX5h zGFW+LE1buEg2CJF^@OW(2k)Ypqu2{mvzMFbgyqh)6*+V=MO9}_BE3Zj4cM=(ee+!Vcjgc-zgPS(bymS z?VFYTWU)q3qx|hfx4xkueDw9HJn=Igr046YGf4UvIsD-e>FlY=&9(gcbO!)<2bTPN zIee4)DQIu^-GHfS9uA?dN3f~U4dXD!gwZdIQ_cORRiFpYz8+`R?nBS}jb4gyhoi&% zrWuunf@P$mHy1T>{?h!tl{Tyu7hXC-h@Vgvw?`%H`+^*ni}q?e|8kDmD%AE`)t^HG z|56lpj~6lG?TC%HWx`|aM95zLY*D#8(+r}zd4;2y1h+(ZF(7I;bYVog8T#S8A8&Gp zW8LvlR4xdDP@gPixpSZO?LIMDxZ+4SSm~tnm*SFug1-A3Kde$W%&^J#e@D>@9>UJJ z9G8neRNqP$=Uu=&Obuw8}6&taaQ_enx_qGB7e}Wua9=kh)zy3kUKvq1tq{&xxI7_eq>~D!DhjhMW_d&@VKFmuzEY zJ!KzAvQnGAD>w|t!H4TEe3FjpbOYOC!|u;+8}Mf-nmc*ILFRj6QS3!-^{k5of!0-* zs!!gGx+~%*q-y;psi}cVJ#GzvH1RlI_Hxjd?$}q^o3%MRA^`qY1xJ}O+HQaEr#6)u z68}l>XZZ@R+V778fz!ja4z!@vxA;@LJ{j>Hrypv)GU{%zrCbWjCJ-6vC_G2k2={> zoq$TXIIRxGJP(1zo)s#n2X6)bxfo+qdl3oM-vY>UO>s0$pAtNsD0(rg27!(}WjT3Z z>7Iv4VG0iUUKo8)-Eq&Xi!{*S!?YW;ZoNR~&lEP6-0nW4D?4<)&3O!Qp`ZxI4zb z`M$#{8mXxQ*L67*=g0cFOztxlTEsq*W1$(cy{jb#0}pzUfEJKW`T4wQFgH5>Xy5u~ zt-AKTm?2*58h-b@B;L+hPDSghR0i+aMjQjt6PsNKqsUWM&q*-NjKyNUT3x>stGuIL zuGMq;(^jP^$S96G<*bwrJa81`S@~cV>vVs)tbVzK-RDQcrbX>;@-6f_5xJq-Nab z^YRth8uukn20xlb20mKDXa(NF2n`CtwEluFIFC$pBl>^vCR&fbH%ehrXZrnD(lq(N z;3nDRRldrE@7n-9yg8B2nhmdex^XW%Mg>2ws{dC0#cq002F)hLL2!CfGmOM8MWQzf zg9KgcMDN+IbtYZwj?XgF_7M8j0T;NW+l~BWcZP0VM4pXWiGLw7m7?8ii&P2mjsuS+ zH>931fI2`=V9)4awoylLMbpt=Z>JH)JdGWj=T2=%gQ$VEUQnw~0X@2G>?n1g8ac;4 z2G`!a;C^&(LciB}=6aMB_Xya*^&itOom)q##cD*mMN8R4!#8kiCtN}F#(uZ|jO0#Un_+X9zI6x}2c&=*ml^qjv=d=T0SxJBYl5+;34sPqZLWXNM%h%}kV04f~h^DJR{H&vwC=TbiN#mYvEm z-gG|~iG~3v+>1PL$%}dCEIya4N^D90S>{gik3!dR*W7_QF^(TzP4)hpAI9L_jPY$x zvXhR_gc$ogDqfncD?Awro~YfM@fP!;ev&ZY);3Yv4)u^}B5W0sG`TQz0;$p6nArxh z##vtW?!iPDg7HSy<;%_3Df_UtE+N&heZ$@za8CBhy;JAv+{QE2>IcE#6BfaVnEdpUiIWxekii`01~mSf`+{)@9Wd$R zHaMcZuhD!AF4K>tCoz)Q^lNJAI`vbPt^2D2Bg$jCub|pfZ6kE6WrygcFHT$sjgtk5 zJv6fWe{KUg2t^Wd)SVzTmp?7{1A;tR82f3Q0i^f%?>aChWq@v&gCl?trdL|89FyoHvFFa+o31t z_uqONd=@U}_=4T{suGcFBDcsGb-C=$rKXZ(q4bK-;~RqSaaawEzCFLJk6QsD1ylVO zW`bF=XzW_)=zG1jxT0(6&EvyK#JB0E!1DMxFtrZ-aXE}>GFZZws2bOOGj2*(c8E%@ zrz``i_d`e}B=>wGY@b;k1{oN{dObGHaQ7QXGYqA@*FHDv>XZ3*?7Vw}?#UA0tNIK- z8iArd|Gx_Ax5DT=Pg9aNHHB;$-Rx@aHKE#85S;@^>FnGErx(l36DFFRvTog>!gccp zor#jr-UeouNQ?p5bXRtKMU3GBt+XEGIl0>R&qI})}%h=RMm8L_X@ZUhd9g1ue`&%=g)sRIQ2vBwLjIvjat%` zr|3|@n0IMt$Dn<%i0n6cDI?|dWeXyDNhyMj4t`&ThOQ>}aLMwOVw%gR5Mbp#&~qY_ zLKvS)q_ReLLFhin8~=12L9u2%X#$#>d;OplR z+)2T_il@chI}qub<1&fR*DJvdp(%6@*&#Had5Xb?{CbKhSdEs&|2Rw1^%g3#plTf> zwRog^uYakGLz+|Y;C>p7pP#RFd% zADJdRwi}ry#P>M@A`VRtgW)yEpW0Y6vv#N+@i-6U-A)Ke0>#xHDF&zqk8R8+8>9X2 zUyKAbN6DipX}M5C>FVr0157Xf)2IXV&(f-9HWuJTxDo@WGha$vk}@iZr{2d)D@D?N zIdt;v$u64chR$}Fbe|htql#Z;!h!dCaCEK)N z2UXqyz5%e?#T4P;o^Uu6Oy4aad!&o&8)2X2YcCSva1tX7`*Zj1;sxJ$xYNQgB(t^7 zJ*_qBt&Bd5it$%vL8T~Y{o_t}E%1P@`JxiNd#3<@;{bDfxK{Vs-!FYFsCSOJ3(VP` zX}Fb7LYwe1ijo;woY0i)$Jr|KvB&2Bhd~B@7#w;66P7p`C5h5^a+?PJAU$Go(%6LcmdYI-L>jp|f3c^&G{BH8 z$c0_)__jV=j%B?O?CXsXh$l&)hZE5Dzq_0<_4vaDqiuYtOMbo}Y)Zxxknd_l7W#?{ zN5HhezP$)*V@RYo4ewLAD{`GP!AXL&f(_SSp-RXxu9NZl>3!x3OFvpw;@CioB8Y5< zj<9MkN*elT5jwn&kKoX5Sacldj^RStIUjWq)aG6b{M0X4-s;)+S)Z?X(61@4Rn1E6 zbXP%U-*-~tAtU`Y5cvO2MHz_!MJW_wKvS+=D?er-QiU)1l1vvSTYsp8yPK4%qw6H9 zg0yniJ=8aHsWUlAaZB{qGd+zyYs(@5rt`nc`HLG~Q1uZmBy4hoS7=U}uzWshINulL z2xY|-ratT$VT}nv6ikk=->YA0CKPjx#qacwaKT_U#R(ZQ!^;fDCJ;lNbgPtQvX1Sr z6`trnDS#;-|EJv(qGCWEBASnO8{g$QSE`#TvsSx|Ee8x{eBRTj5EZq5dBBH$?;%u^ zl!n2}A%*tG7gv8vV5gp4ZF9bh~+ze;%?3u2d#!d-`OljvIc79_Wcc4r?)kG2x!hS6(23}u) zN#W80IoUxlKa4&b2O&)p|2M*4sT`_6F15?Ecf>sYXMn4uNKp3USp(_7ojIJ!Lah2yU(C8u{+El$j8P=F)$A*x9k*s*Eoh~9F`(dM3znolnYERmXQglg( zH+fe88`_TMZ?U?nUn3;{Ee=mGfad+Sa&6hYj0{|%6YvFY4?^zScSWS#PNcJ<}y zL}d=NHweh%Fes9eQ)sp{DBdP*)fQ6vBPri>C>XeED6Z0^L)#7kE3Xh=vIqY?T2p)p z$eY)&H3F4l30}8pAvuc^c0xKRQ}QtNP)z_*c*2 z99(#7YUBGka3~)ZrJjUuyhS7%cDWkW2$LNy_C-YcdD>5mcfprTd9TEmbnLDzH(ef_ zfxe{VCY(A7zxmqZbN2p&$we_J^oDxr{*cOmb!>e!XZ?NJ=J@jM-`o*l^yt@zEh2uD znwK9vnu$C6i;O?nt&w7q6$q5^3vtc<4KOhM@XHYi$9-2#P6OiOgD?{k(kPlEnl_@nyislD=euNLxkYpjK@ zR`>{>4=YWFZQnDVfyi&%Z785SRx=%ERr@o3lr5Wy8mH?q?r9)8gxxWij_Sq|-_85x z*D0+i82bA-<7o9Ix6QdQk=@0uu|LlYDpGs!*dqP5vk0(xE+xy#bx4<}?ECGD+cigi z8=D9p#JLY)h%8m!#X;xie(C)x!IldiES&AA8)TMqs$rqLv|Vd0A^kk8uR7u89wwOyKUUER)0sC75ktHAz?mwJ&2 zie}k&%|Aqu=f*#}@2Ak`Yqu>K$4jN!pTa635R_MdIk5N<2y__Px<`guLW6o=OYXpxr#pExsj zbxHj>h(s9=+*t6RTqV$GLgi7r~|BA_vxaz%`q~0gl#Kx#o&x%lWce%hhV)- z*9@|fB)x_>*FUdm;nqi_9OA-XGxBGhY{BeT5N~zD9pT(ot^rf__IXR)C;20Tw_|wJ z5!Jvy9tG2>)@@qPfAAWL4~&PGie_XPOlZ&R6!8J`4&w**Cuc@Y+aq>V7l5I6`otjf zoF3?9{=>Ouh(CptOMJuZ?>T+5Nc|Yp977%?GRVAd6@qLNA^C5N?x3nDrnmLq!W)fj zk@k@u7`*BGrRS)G3C{IQWPZ9#CxY)t$z|PUu(~LWK>>@x-eLC3VdcW!-|#m6JXpbl zL5fpMIz+w=a`aXC`D=K(#OnD zmV9OsqVm4pYA3p7`9X%mZ!fi@W-2m0;7^OEzaReDv{#1g=~>t#4%Kl@bW3nYf%LrD zOZdc(Z@b5ovC7fx$aqZq4Tyu;ACPbsj#nHwyS5|P>w6W#?P!9 z_NgqQE7=3jU{lV#5=-WAk3?fGd+lkz@;|QXWkro}zz#Ma!){;lMV_GC!fu|(?MROP zPmA#jT5wh(@^lhZJ9!aNwG(dYiieCw>`oO_Lcd)FXzU<6>q*KrFiEO%XDL|8?C6Jw z5HFm<$7r5`N5c=HG56jC7=lVJ!yfc{XSDx2x2FceVxUU~l;_}%?zPQPiT#kzXXBlw z{sT`O3(6=!NtTJTYc+KrD6d$=;_EqDpmUILHIi~Q!Wv2|mVQ7KBF=WMcx~r9)R+da zO*O|9Rm5$u#5}OtImR-PT(_NXgvZ=re4hd79Y{{kb6SPwl@5CcKi&(bWmWKaou$OH zj#Ff5>&+;3dq9wB5J@6sC<%|=KYet&%c5ZNo${F{)Ln1-hhitT2+;5K^K7CLSTsh* z`r9_~C^uXRDtE(V3|-fGK{hL-{TJ_=Bg>yArc;-plopd^-i4x*?==Ln@=u2%3|Pe< z;Rt2jXJh;Dg)zgSOZP(xB)S@uLcq*RLc+G1cC5^wrno&Io9*?fa7FN-vS0FS4FqU+ zRgOm?21IJnrCT*9%Rqe=uE_5mSjSJl^0IY(gM_t?X!3Umj)y>7PppjpKOWW=r<(? zQ*HsBf@ais?J!f7TI)@=x`u%CbJ2DPhYs(hn=pd{rGsv9in`)!I(qA}iJX1&=ZdQs zO=tRZPU5Ljf)IQkYS!Ie36xQa+jW`f2uT-c9~^kq6)U&u%h;*X055=@lEh!D;Z+eF zc>&g?(DhqK1jOV6;c!4G*^;P-{GqKW5VMGcm%~ZX5p0d`b3pLsNA7?NcBJkmn3bSc zw3NNRxdl-KiYCI@Ha;@Jk@vc}b;vz+tsibk*umcZD#Mda&coCMJ#pzA(MI>D8!exS zl9)GI9uWU~s`&{|5@m3%x1S)HdIUMXFt4QqO>ejU65@D4s~%DLmswQG<34mB{95SV z{WUDfOK1_w&AjcI#dL8D=WL@-iTt0C4 z5cd(QJNlgXzt3XcL9c)VlYK2pruz#bJ7Mo_IAif|$iX`fPnf0ua^NzE*|1*S!KRFS zwi(lR{Ls+Lr3Q9;2HCzAJynti)b!g69#W)t8$8o?dU_hdewA*!1G)F$D6{G1j3?eW z{qeVl`I(@vafE2onCZ32-3Bwai7q-+iA4Ic>@iQrYwt&9$B%FNC-L@6yg0?X0mD)F z@iq95NEY1cSLcKRp=)Za#kBtjoA2a4wD>jL7Wj|ocn$?W9$)T91e))0xq~Z$J=RIu z!YAgkK-}(sr8RS6o*N4A@7IkS*XuFr;bKLmnml+kN8KF$#>;YXBH*d9=0TdZupFa& z-@e}mmaPi}bb%t+^`h#=`SMgAblq-X(0oC%S31Qmxqo}VW6b3^IF94!$KJ!rc>!!b z496wOA_j1WxlMU@(_#!6Mmr+9@%aBFj<21@VI0}3HuwD?ssjo+$w6Mk8AZs4$&bil za)8OD(bN$|{5b=xYP#7QP_jQ!U+m#vf_|YYA*05mVDVY?O@FpR!(IO8v2lZPJ^M$@ zSechTdcbp&>DEkee6WnU39(+}ZQm}jqxTg260^n8SNkShT3TBKr(%I|zi8}u+|A47 zYzFM?SL>8vc*gh~ZjSG=>ox47w8R%j-_cIr!02lRIFloSY^WA$jvHLyC_6&(rwP=a z@cf?GVec{3ci5HR9@50@fPqs6#M*hW_-@^y4$SLh*wmg!98p!(cCR-%z4j6+!pwOv za?E`7LTp7qpLX^tVGsXfGDV$+L_iMWb~g0yqZ+oH7GF~&3NAH~ZYe$~YHwXIisxwa zH=|KGUpG{g>q|T$;FZ{mT!k21{C)YQv~M9x9&UqE+6Q(*q=BLx^;*at>WkhVV;#)g z(7~T#ejmLte(Y%Lt)d$`JIMtj6uV&Fx+(f@j1?%t--GnzM5mKFN=_oYftwK%zC4Xc zP$F?81kNy@YRO3f*7bXN9ZCeG*fwYI-BE`OhJ2}#7q?4Bqwmj^R#masyi<1GNL9y_ z<2V+#LolPbp`(@(#IIXL`1PUr#^Wx}DIzMijTR|h%3o`7)$`RL^zF?Gzk}#tt)hHz zBIEmXDCkW4R-4zOdD-p+J#X08hb(*V8nSZbaQl?CVVWA{0)5<&?jxcDR42K;R+O@~ zLafoi5q9saCCk2iBYs46^qsZX9DI87EfQgSP#UD4%v!wk_uDBNQ>pidS&OoPx`~r~C@kFVtrT8h)tyl_`oo7n9W=mV+fHwcI_f2GJM*#dVa=4 z){44CM0VFbagq-u!Z)1)vtyYD#;Wh6p!8iVZkkQHuc?BLt=Gt7p6VA0JAI{#UsEZsTftc~qK4@_00PHqYS#t7rRZx+va_zqsyf%Pf<+Ii%J6M}7n z$8w1bY2$xWV6R@Yi>XD?cg$6Tdd%}QT^s#ovbvJPeymSH#u*RDyc<`BM+=bvB+y2I z%?lXO_^JgOHLn>)yV!b#gHmS)^{@smYz))FMBX36-v!&Khu62?_{OuzgyWlko;Orm zm1s?5md~UMQ7-eOvR=|UwuL=CGVsqJ%Pa*s+gotP_>3d!jMrMd`i&e3gB`_C(}~bV zw9^R^QFn}td@W)Q!;6J@z`33i$l?AkMMdM{9GLfJCyau6+^B}&?@7Y}#5AN-q3%Vk z18K2rn!IBcX`itY?*jvm-61)DP@xr~mO@_0qp zZs>@IYG|f>t#PSu(K#dbA%y6JOp)%JQG68inyJ&Xr71Bmp}5bhlYs4Z_9ugk3iU(z zkRH5&E&EzaFb;e)N$>HUOr%SO24)MW@sr8ooCyVeieKKwM&Bu9r++jjx8_-#9< zZH6*OpC8z8WZffC!l`Bzkn>-Ytes2oNLar?L|2BO@?JaB+1?vBJNWLF+SMD}r zgxzLg57`;bHtH;<3Pu%2T;NG=@;X}p>X-+c(Wx5I1Vsm)a)|*x?xk`N3J#X%!7fa5 zKX?hjJqfxSJe;{>vG+udfj1;~*IzsgCr?yk|I*|pAc2ACrJ6Lj`up?rw{xrHKNdnR zq^TBZd|pfb*fiX%otw#pTcxc-PeX{{G=#qcY~tx?AiuTulcD&wKj_<(eOvv^HZ6{IP8|J>6bI$BPh{3x|KMJ=oHG! z@Nyp92|&U5Go)`n`!{-1@u=|o=rjZZ*?wzHd~JlgCO>n|qAMwtW#%tmI<(4>5;d^b($Cj1tpOL89Ou zTCL-sq6JLzAtD7$ANUy4h8PiW`+ZJ|s)5_Oj<`JiMh-IlHo321vfmTpNIFph;6!)!3a1IpCa(tKt*N}r zbmuV#HmhH*#oq<~&HjnZ+RyWdqtb!3|CD_XlAax#wiB2S=Yd4@i<0rDCv|0zk2 zacATqoAp+rPTuo3zyB{zFO=iyTpHiugWl03b!FVO^K96C9Oe6SZB*FQx>S4(#a_Zo zXw3VTer}Ts<0|y-50a-0DK~#C3Ce`$Lm9{CD3BjY@9ejA{sWEo9uU<_wVvgRL+yVJ z%m~zL_Y!8T#W`XGhIl>YD_*^TPlS?xFLY|>0Hf|gkD@~iTjv|ck8$y~OFP658B)e7 z3{GomZa<9#z!ZOYpy_PCU!|L9KR&1!vd+%j^aCq@PV#gfnY|)`L|M zG#xu8nOo28{>M5f7?gorM0s@#JvLA;<^^OuL1B=OkO(^QhRv|=*BI2duwrc&7n0M0 z+-At@cQp=th4^cmCp6v!#@qZ#0S3Ej`|hd7`SLt->I0aaTNNDzF%B3M!#UF*Iel{u zkB(f>Tm|vzV69VQ6X%(dLuTor>*4L0PY> z3=e6-dpDExPAEblU$1f)iPF}-Mztb*N%R!ZUK^|WRRXI{wt0lpN6`W3J;D>tBQ^S5 z5HJ;E28^$>^Q2k7X3)E?YuA9%JsW#yjBQnSQJ>kXbJ>T$IXrhSPgPc)E*>YLwj4ai ztja5dBtz*AD!BkmU+#pQy&dc2G-r z$53>Cu&0LR9$YvUKL+uV0_5@gtYBSvZO$93UTX(51&c$`@kA z_-uU_&V)75n3KkBK^s#k4#XVx$*6UJ=?JI26r0pr(xJFii4w3#VhRw&YDNQM&hQ;&%b4*z;x`svv3ZL0^@FF1A$EX$qHD~&-g}~3ro7kAeA}tPQ_X9WX27CHx7rQo1@cAlQ0yA+=H;qdDtG$DQIfq zxmXmS`JG!ah3*~lgYb@H%+w{oI3c~ATTt-RrZ0w+7)TnC^U3*o2JVEa2@@PKC-z>b zct~G@%=572CP>mn(WgObfu>UN!}U^c{RHYv_F(dC~+;Qnb;}&5zL6%(D=u{S`0_t*B6x1%vMP$=jfo{B(s` zQDj%wqmb^lOyZ3b#_1-xzqQCy1!0rX-?vNS9T8uZio$##k6RD& z@iH#z!SnMe29nlk7kVU_?7&#nu{Bq>@^&;lRDJPBxHj#aM9^>)=zP&dD%~VjhCL+L zn$A3i3ZOxPr&Kmx{O@b7Hu~&dc67}_CV65k&ey% zsgIt;Y(Kc7rM1WLbsU_S#qDDG5}S8>TsaD%_I^=t$719@xG8|jMfRZhXqN!D{T_SBal}^K7x;~=W)i~|7{si0KTWdV_Lhl z;Fqnw5aAq1C5n@uz{+qRxZks3@XguR*mzd7D;h0rl8qd`iDWny!Nf04q-78}tMCC_&_Jh-^BFL3r~8E@9K%tGC-F6G>%$3vU}4QB zD;8Z)H$>c7^G3GjD|3Z{HAPz2rNS2sZU8&vqV2lJV?|EOwB@nhy!H5-%;Ky8oG4*hm4zsC;;$af}rC-3vT%CHD zqa%(S;|Gx{!9QkKV>TSQn4Dga$I2V*blY&AY5U5D`(z!^r%Fzr7$BevnF7X(FVgLj z9Q#HPK7)vgR%J2*ge5nREM)j}XCFingzF0mnJd;;$3?Fe7BZnczT)RSNOx^=^q0#A z!EbY#K7XHju_WICZ*J$8oBK)cN9Kp&&o#{V+I@HkvRRt!g)i`4VF*c}D}MT|R?Iul z{OG(RQ3ru~6<{<#?w<{$%4>(Hp+MPHD`~mVZt8$7ke8qL| z3%!jmu3>4^s&;rU&ojqTx%hjx4lD1y|HAF&s1uKxBcH8h#oRFna3%7@T`%+w@%9&C z&ESI>!RjnLB2Dl1Aj3|LH!2l2XYkIf!tOwZ^w|z$)Fgbu&+tTE7rZwv$L8>_8fzH ze=Bp14e<2NK}B)i~l#I+PzI5 z+OXvAdgc9{O}=&=6{$HZ?Q1hW{Z7xFiiO9Qp=0zNotTS|X@h3t!dog;BU@Kj+r4G8 z|J+?>4}&B*A82Im4Ul|tBOcoBy|k0Klw4eSXP>9?vY+9@98Zyzf$toaZ-w5C2PvHL zDH&ZIH=>`v1_eua$OU*@h!OM!nHg|V5d-6vim)Q}mq#At&Hm&9O&-LJ;K_@cta(K_ zuzRSCJeS7*n$dz87R$G&2SKO~9Ol)R%lmwFrP zmORi4o)Pl`@}=wq=L~6tY8`zNhHQK5jY1w#vv27MFQ&u5A&Dya`b0RS43br=HH2l2 zP`0=d@5x~LnQigP&V3dc=A_@1XljJ;AizlOk`#y&>V2KY>p3xF&!UhDdXx`z5ygYeVylu-nBl0K z^||eQIs~gAdLTbc`<&wU;Hr&GDn*Pu&>XQbf_@GhYC+D7{yNYSJF?9KHg*k2y8~L) zYm^ZeF8;DN;_}~))?BxKQYJofO?-hk%F8$Om>p9;JB_f3cA>ik zZaoM(>Kz3I6Km$I_>YO%j z$+N#U`~!_l7^#qd0S=OGLc(~0B3{Nn-Z3D2TL(Hvol-GX`V}|d*2^}C>l)Nb^pUgR z;yBOyY<*g+Nx!RbbKHGl?o1t$qJ~>u5OS8E21&&!&lioua+NplG{%kR)f^DdWZjgwpAj z&GOi_N9-~Xfs=lHwun$C(&7Wmx78f%A%_k~IyZQYATMp!R;$D!cci z)OO>V@ixgJ;kxpMmUa1UVk%j2>CZQ$xGd5r91aIFn+RQ_)g?=U#Wl_QlT9}=1Iyo_!LGR8VV+lU%g}&>jG%J{Kxk-i& zz8&-4q@)?N!bDF;L~Jx7FUC4E`;%di-_nQUn}v$suA5v^yUs)Il${7tGgV(B_wQ5J!r%~Uzyx5FpVHH~Z09*DORG!VC1j$Uat*s0(8>H!{9=Kx zdI9JnL3kvX_Z%=Ncw6R<_akk^)u_Dr!5^w+bq;@uAL)gz&z`0kLhYw!2d|oivu8!` zQ&be|pMj4yxwn9)ZgWm+MIZVj|6)B5Rg}l2FQtNZ&vk5(+#F*r^yj|J&h!JXX}0^x zDL?2G6Z#Q_A5>n*0xfWlR+)9M@qqFsb?q1ZF9z|w916`?H8S!&_#ur;o6h+6Fu0Gu zAz3^A4lNBZ6xmbEfY{K5I>s+}XmV+uAZjPbbCUey%Q7Gw!UDFt3=x5&R%F@!h;uk*ddNsOCzss^jI(M)Cel*-pZ6V;4uh^ps!^%bJA! z~d@BiJ3<;70-)yb|-t@~16^^2={as_YF^@&+Vx~9s@cowob+UInL zPmHo(q}fcLImF^pKfinml>l#yay8IMqi*OFN1Ymho0(3h(+CfVHtn?_WnTSGZ@V-y zWo`-8tb#9=sL2f~)q+49Ipr@)o+(XBwvYehkLIIO9ldX62$GTlvG@$+nMg@ki&hZO`Ep;}&$NMS~xfh21M z(YGI?136B*-_)f~C;zZPCrFFzP@u=LQzO`wd|2ce3R+m4;PhpsbI`M6Y%lA)mnTvP zkz$*W$1f{KIw)*G-pQBOQT>YSi7(Ci`(7OILgn~yx#2+P*W>xmbHQhfCA$xV zS@{mQJSlFmg|T1ULQsez56@nbhE&b zZt)-ww4;X{Has(qe#79Exf!lhzPJJOt1$Vl=Ijvlm+wPRvJg}7hx)NmF3Q{OzjWne zEa2l-s`Ml`Dgz!&3P3of0k5J$4OSs(9&$wT@FK{VVkMA{qX3 z^-js;3jO#aFjL=ams<*Sy2EXvlSl?!0`URX#^>CPLFMTEEifj-L6r_E2CaCF8pu!) z`SPW)a73pG+=(DhEUbZZ)a&c8JYd-C?|7~BaZ+HGPDFi9Zhkc-V>AWlo3yxm+nkLq z4bEU`(4PMLplx2uh}*-{hRfP43hg(3uTat9hM4~I=!`9U^!F;nbDWBP80w`QwV%LX z8RP!d(x0LwqMy+%E+o&6(0Q6q1cEx_x>%4QUd;Xw#9^(BmnUH(seC${b?CVc*D>Ci zkz!>7>3gA8X7`cDJa8j1>U!8HUn84XU<14t%yS+6Lr}r8F*yXEn8;BR8Qc5r+3O8u zYz&Sw0-IztmB2pMCngEm4XN@7EO#m%px?44G>sLqGz(tZ*BcIBgH~4}#cpu{U?EU6 zERWIQ9(VKA{5_o2hqJ#lhd{-7aM7N7yMNFKvy3Snqfdaa-O9?T-HPtSEm!e1QiEr+ zuOgR*5>I|;yLXe(uB+}O*tshL9s~a@iapg^rGSPn;pDwUYhk`>?TS~t7gKo)>jf9RG50{8JLGyo&7v_8R8$(gkHUJ={W7n<$!;Js zs#z3;oOe~W(}D)w+=O`c-EetsV?OvFf+SHDEUaz=A4^4GHDM*bL5-o^NgQr04n0TY zWZK1~YfSavBHM~U2z7y$;o6UvifXATB^3RM`fvu2SMwqpz2{;uZ}sv2*8IPm%j&M` zVbz>!!RA7Y2(Q^eeEgLIetB2gJjVCyATiPmJz8Gl3+F|C`~&KNq;VyW+;Vy&MH2cj zcYGH|2^);Nk))uh7NU%W#R$y7{&3tL1?$oO<5tSYUmeaZXya-@-lq~YL$1TLH0L0J z+Nj+FN&n#C8Ncl1F{jswGb~O`-#yreR~Zg_mj^S50{i=+Y?p<`nSAhD;I{d`!Kc1i zyjRX$>sVSME*J1`6HfEDVdYrCx?4aSkb1q~`D`?bj01bFwydkh=U_h19&38Sc9ykJ zs(^TKg#xx-5e;G?w*63W$lE#oAAd3#g(ljNY^9=cKnBMs;Dr1G;Dd0=IC_u%ZiDE> zLeb_(1kdAraA9j_q0N?+FY|Wdiz~jmtoqFku^E+>=AV9rw#Ez$t~;+ z7t?ajsO!561na}P=D3gR9u+|9H#S?w^ch2r{(R1?OzeTx#*;8U79Rs;G3fmNr?;=) zF!=R)-u8m&hkkszu?S0fDX^=lwyf}}b!jw*OIC2szR&Va)WqmK~ zf_$?$Nv7pHTNb@iI(L5tauHxfYX!W5C(uDz@y9K|!-aH5ANvYZwv8u~gddPECd-(G z1aKFO;dy-zYT0^YT$wX*X#h&Ph!UvZeLx%CGp zd57q|=9%AUSr$P{JYafVa9Uf(nC!eg(@4yI(Jx??_cL5JWBaQ;!T07XPo{E|U^5nY z-J4MbZUTAHJ_s0AL92+c?BU{fCk(A!GvJdZeZ)QrLkz#oZ?WN&#BiVLjJ2|-4=kTBqC_D}SG|LCn(5RMpUsHa`4p)$Q zz6yt}hE7<@7o#Cx+6@;mZ1;V8-NwI_3u)cuWxwLi!UgNJtLQL6KMg2W>6(r0R14xo zf9e?b6L(9(uVQlPAPiux>Jxq@c`zBf!*N7gx)4Q5tR`TY`LQ|IzLl-zcY#0hyCd@L z6ZOyud36TUsql#+{O|W-Z%#RH@aMX=T>8n^PhtL0HjRu zH_JjT4{vDu8^dHOF?!O=GSL%4KN!4F7NGJ`MRz$=^N+aa)TaU9kFgqmv| z!MG~;a`{)LByYO0-C+@;A>LxpN>cMr-CcOBs)-kq9GuTqL9gk#7UIHptir3wv7uvh z7b1Z!7V0E(id{HXrNRD6ka@giEmu$230$3eC=bE`X(7pCKMXV?T;cOduHYZ=6WP!=iGV(kj^p2w z0lsm_Sj7J@yKf^t=fsi2D^@74HfOAr7g88;@Ta&d2lRW(pfo4u{A)mWmB!u%7w z_+WU00dUl`)b6x(vc4!E?{2w3p!{|beNmn$L%E;Bs4B+R%Jtga)_Q}PfzG>>$EiIU zv&wQ*+Q28@5^)T2>5oc-WF3jKZluIbin2-(2;_>p_#4g9VB_3=7$YeBtJ!q4>OrlHFI8{~;#m8Ui|_oPC(bAUtvAL6(x2I~ah7B^RlM>DM`z2n_ZcbT{lz>N z!964hZUGawzKMO^WM?#RvRpw0a$yalUg)3@`QYw^9ilqHeGDU>Bn_%P=hQ^F-a zAV#ZX9qHG(4@&Ccsm1kKlIV4o-IfPiz#|-LhFr&dgC%;GR&Ca1FT^i{ogrzq*4 zp2&#kb|#ieR3Qsoc^DRI9Dbe&I0p4pl_!)sAcUajD*7(7WJ8|*S~4nanvCqF76d!i z(H#S<|J}O6c&A{)W`iHN{s3nt-JR(=D~{*L0*Tt3s?lnZ-g3$@jS(2`Md2@}PM|-c zYLV!oRT+A>5472tH08@o8m%{y8S&aEb+VDOj{xb!H6G}>U~;RP%GY&v$jw1AyH&Ew zHkQZNt$J?a%vAPq%>q~O0sTGktQN_>UvB-zrFi4y^!6{S)fWeygDIj=jSje4iBGOj zo~aXzNLv2VXH!KT+H|){`CjlVP`D}FxLuMBo?Znj%!0-eiu8xO}~tZ zLKC=K?YNycz0aE8`IBUF(^_v_vED8*snUU-85TF>Rxz`ma8osB;1MN1re52DQUe9h zBud{X=RY4v=w{uPt(gNKm>?7}DGyTrL`YIDVlEGC66AjCCwrun79&k;MAuxbAV6)oDMCE@UL{4uYGta4-s{ zk^N+3x(R-5o%yFW8d=X+unm_!c)Iw&doVi;v2bbpNxBJk#=?yyPbW4(%__33^mSg(0S)~S@^#70pA!lBpDIxsNDis8A=+tLib~W6 z3r>hqiEOqY)){Y*%K`SK5)~z+5;d?AHISD>^4EQ?L{0wZ=8!u(C$iP30ahmZ91;Nz z+M$zLG35Y=8t8F!#*`p0cr@R`vT_y=qyJn@z!23yf%Bk$AJnO`4pxWX_+y50;9g%p zau}fv954D`S2&Uk+9ysRAOoA{t`$Aa_a4EvZFz3`NG@`MYc83#gARtPC+L2YB#q93 z>#h(Z+6_-g)5N_z%KevV_y4rec!J1@JB!%`iZky9ve4GMln-5kMg2ow(b62j1ua&` zK2pC^dy!SLz$Q<$Tf&x^99f(~Wi41ZCSPP#N>8c6wo)bz`!q{=i=ZBEhY;_aYX$V&`Ikf3Td(=nt&$Tp9r|U$Da-&@+J)!vvITrM8&?>dmu$K$ zw9NSV`t&Sb*e}x>7A!70&aQwQ)4`yKJ}*(hkcP@=S?gTn1du)vWBkNZ?SMA!tIvf6 zh){Fextph_T%k3lPx+MzBh1q;4u3P~H38EIw}?Bc^B%ng)BJ57{@iv46pV1TF$svY zA?3TS(VExwvkM?f9J9Thuivfjfn4B*-6I@3c={KXVSUp%M&HMkyig^9wEoqZ@}#wR zTD4F=^q;3P;wIBioB4D*kNx7Uqu*kb)Wf5~*JSaCm%9G7)xJ$oo{dmN-&xNF9QOiG z8-DW0+@a_DA%*CROmIQMgEYSUnBaZ}JdzxDf%Lgr%NP32I`&%`#r}KKHvI|YEzLB% zWomogsv`tuw-RzpFxF>CA|*dyg4~;cbGX+|RMaYMy{0qkDfN(toKI7p8io77S*(P4 za&vt$7t_hHE6yMZG{$1$DSnAV&T`aI$h5hWgJ^nhxPAhzTOAdvv@Wtfy!<3wQGYw@ z7P}tL`*LLkxm6HV`I|8&iRx$YZ+(Ak=V`$sy@q=zT>W3#nx(0c5(rw+dy!s*i7J8< zD{cv&8&ELkz^_-hVpJ1*w{wNAPs+f3_u-QT&wlWn^ACYvl!T^YCR9VRiL~$ts6LK> z^L&iRuu2J+k+i4S{1kex*jui9`njD*=OVS9Rzi41(Lg5Y#R*MM@ACM0_s}}=pMSht zt=5FlUJ5RIKEA_2Kyz%L+cMRKGWza@e5Cadg$aeH`GI`(i{FjQ;qU$?V7C5wyD4E< zqxuM{Y88yyU#A8xR6zP2NMD+^sn)n*nMJfVtKeWVUT*|9Wy;Nr4UbEPXz6(-)FfMb zN3qBkEdIX4pKN)y{~?~dbG4+_VsTqB1PFH&K&IAhM!$X6qv^a%t?nuJb)vo5JTez# zhnB6?VM*T7o)#Bjyz-=aw{Wl8`8kFMC7C+vF8M46^uD%68xG)y>$Kdbj`p_*F%dr! zi;ph5fC}J8m-(O=(a~j*ptC7t+n`8Dc??<~v@_;nCpe&fEd5Ydc`MMyqd%XME<%Eq zNJxE*^YYmbNck2rNs|-2;Kq^IK>RlmDzonD5NFFj0=Mp&?U@VHP!<_uKqbtBdGg9) zm{uPKW0(W70oVyn$T*CNntfH0%=6L^WiJ*H(BWr`ebArohn3!a+a7?0%KB10* zh2ZpcT*fI*U?Ju*0?xykP83lyQI|$Sk)(lk2cgLoGwbKN_sHgK{!=>o1svuHlldv( zFfJYhUW4@$G-~Cl3qDSYQ_0gku*ED$^^C1FikD^n6!;4;Oy|ii0o+l~GJG2VMdCyk zFn_Wj%%JB)p_dmj${A!1lW5$ z`U8F97oiuwNLQOK)j08sJb{XzB8?FWP}mZ?!VPNg8UJV9GGF9i`vhwej>D?5p<$vh zCpO;y8}DfWCpf{OBy8Ju5}9GJa18xzcKZu&1<`J`qgdwjtX%O*a~&HO`j1a9A@@(s zHEapT%&A9+!ddX6a)kOljRmE`7+q2q0nQoVF6-30zA2W6-hNqA%ESQW4*6MVj*~$# zsP3CSp^b?$4ILB)NW{kSNvOLE9V=~EMhUhYC*+qxNp>pdOhyS~o zn&B-2HG3L(4m}N=l_{GzE87hyXg&K4POk5|T)a5(f1}<-QBzNyQSDaUoC@tu4X7z9 zv5$7d2f^KgkMWt~BTf+W9ILar(1=wwVV>lo)jm38;q1L|hR+Y_PeHAB?4O*15{{u@ zfN^t$S^ydvsYigP_ye!TWi`I;+%YW;+|>%G-EeI|{oHbE{SWU!hAh8H2z-8ywHd=k zHjn3U7;)^qqXX&uNj%i2ikaAC$&bKPmti?Sk1ce;H+`C93D6v8g^mQYm8G^ zhSL7CSn7U^n6G`YX{qwr>LZ2>(aW5$;=puOHwpu~r4$y!ckxsQ&RsaBgiY#k5!})6 zy#Hy6SZc(Y-`8teOV%(OKH`qif5W^1M{eMy&OAvi zPhsHifr&bX5cgq@2(QRRioO0z%bBlnCjsu(1BSySeDMjpKa+~!)6~isD93GWChua| z51)B9+R>OOKyYty+aNQ(;@XLlOWPC9jT)>%@+$ZDv26Yj3n;Y+jZVB0` zQM{mfpq5&z7bJXZgytpZIRuzYX*dJ)rAc@Ys&QasJz-HGwGJkjPI}07Fu(g^dnhQ$ zkCwtdI(SS@JQxkzbfFCXS(P|BYU)3l2N#f6Yc?ZP_2`cwzVLPh$~eC_e)0^&kGqg# z6C_+ zW`fN8fub&^fS{$@IpWz}M5g+ojxl-s3wrlaD<=sXMZP3`74cBHETer|ZnJxdHj`i*uLSpqMf859)sK2(1l+ zvg#mCLWjGAy^_ZZk!r@3o{VZ3d5_`K$b*Bncm+WDv=>9Bttzo_S&jduUPFBR?gTh? z5wYL8ig)RAM8I6HFJtm%&6QDY(n#Ydk;!cl`w#!Yh8L<%hOV1$97Y`|M88k@rh#j* zab-LxhnzoD{^*4D>uaDIONqjPkl<`osYNl*5HKOfhgvaLDz!9~5X zFAKr@+#f|u%%EX%K41NRZpwsyC_0Nee@Hnrn7)J|%o^r6xs9RU^~-}tPE|fq2Xk30 zHq0RpXr9u*E2QpH`+nrnHH?p_kYa;!Tz07DDM3`uHLQ7bQU9~n7;8H_F1=3U&U!As zaS!tV<7j*B+QE_y_q^PutFf0~QGyU_>gV<8sE!;Dexzk|`IwN0a8`?zj3(mZYf!Tv zj(dY-l&4A3vH+;Yr?aegG!$>s^lI^Qy28}P*HMljOAsF1qT9_+dvTAxRVZ5Y<8#RS@mpAX&mQSu*HHX zvT|Bg{k@j(==XULumZ2cO&?815pd+aLafF+jeGOhWg=rt`gOd(36uPaXywD6_saJ@ zTeQx}>qVPhy2NUL#D~G#D7b~AMiRxl#g26F6c6F^U__BU#tacwC>K+50m3W`nMgJ?xQxQ0p{{7#4f|GQ0>A?Smv*JV{)C` zyk&IT{OD+SGxHd(uB#5|vfIg+6|dL0D!WUrX)QvgNN!l?(kK=8zMivJTI(J<1PPF8$}*Rs=wY!ieG8SzG;}I1tXy-o1DhISiWLq!9llT7}5VG&de=$^P^F zJ8v(vw$puX^%Z@5wqPQ6oan4Uc|bhy_dK>TF*KI-I}AfCj$%*vCLSW|G{GkzQ=7R9lHP$8>&<~sqA zrtk$UYm^^@lx5fxE&%Q<%v2oLFNN1-F=U1XB9T9habm!}JlK%l5X%W$`dZk3!ftQOinQ&oN%=P~-_Bcvzp*2){`uKIo? z6|cWn-RX8V7^0bW1y^-u{gT{hyzdHJYR#e(vb0Hz7*Od zDs4xgWs_imfUXsfr!PtT1VXyCKgMX6@J|>T=F%=e<`%Pi8&~RVE4Cnyv?|=#Jn%8J zNC;yZjZg<03G>#v%mb!P#@_fCM&Oxe7cR}=?zf!mw!t|~Q74A)W6R*OA-u$)(V6&|#~+aU!q$<^9OcdkchYefbp%m2pi$(y=@W{S zZREQnn?dC!|1C$w>|Vm%`wzJiW1so*Zp)3t)VN%)=65%gEIX}2?&&@mu*RWwxf?Pf zc<4M8A?ZW1uG*7v0Y(pm*p6#u;RH^>8Mh3m)WrF5IU1|k%S!ivB%kM;WRx-Rsl`ck zhp}y&lFbX)cc6doJT}%dWcnN`hEsi%CzbGQ>L;93?>$zUk)OIx`5GL$v1>f5Yr^3~ zNl^VnP72c(ofjcZRhi+~fg|2ZRe#73^pWm0d^D*9o*H_MLM4g_b^I>Q_NYX#w9$6o z#<#dLe+3CmP`yj$_}GhY194q2HX4%U^Q2wl_w%wTy~-bWXAN0ikO7*fsGIvCWJA5x z#g%B$XViu?C_7m4tJ50hCa}^XpG^k^>F62AmiSo;=x!atm$vcBW~0i;d_Q}-IucxV z@C5Cm7SwlZ(0s3dowCL6VpR@)H)gh=h$-2K@?-Qxv9NfR;65#Uh{397l<@m^7RrN) z!oc>_;*GPBFTGWbPaoG(sBLE&jbSZ!ZtQJ%78WF$KTUuj`}n!Cr+*$bbtB13D`c^z z$xDCyCRsx>RjX)BIS&)_`7~+!r)J~6Jh-=le9+dzEPfSLh3?J(`xkxEK0l0G(`C#_ zU>5e?s$$ZoL5d4obSaMK8@!njPU3YRjv<`_fx)fGXH`lWfk6Qh8(_NT3N-yHJjqmO zY13`xaEPBfEUb_2Cv)N3Sv&t^}NzVG5AlAga%Ryylt zF2qT2-h5R&$sVcWQ2cj{JCdNaVGAfBCn+0BMu}@ySJREWGja^EzCJMH*KNw-Q_2r; z8%iBE$wjSo%t(93s}5p_T*g4crh^v&Ui%yoU_fM19L#+ibd(!4(xd|2Q5DNe>=Z{GP%LvIzSQS|L^&Y-Xqu`f5)mPxR{AsN^fz4FP!Yh zU$>luuUp`wI5QgY_lg3(n*M?~-8+`gG0vi3W-nckFd}OZLH;OwnyiI-c+M+#*82Pk zS%#A>$OYnbEB;Iw{%oXs0=&^=D>IJ4xncUOfX*Sx;+;l^hNR#7@9!hKwSMB#Rt~I` z<6)mCx!MEo4@vd}&r#Pkahq1R`M7zb_B%F&z(r!8c?CIYXq6a;Lme-+pWoaHZE}W` zy1;`;YnYP~wP%5#7LGgDlYxzS=asXmG+^^sHbKnIe-8g$-ZUoT+bgb+`+KzEKOQ2z z$MF6d5+!MdF5rtj$S?jxGfOHFr%kTXFOrhy7Mc~j!fjbKSZ-#!Wb$J;cMfi=OYr|Y z2%g49t2nQ_!S83p+HVBe&tb$JEzyFS5m$bg@96R)pI-Kh&@oQaQzp!ECjR%~d^?y4 zmXjNhz#SVHP4*D2vsbNdt3I8lhY2*L9mx`xtLJHgU7IL>^R>U*3$j}x*n zNC&yMs;@kl@?5EiqToW7EgX-SPJY2UyfoKI*?QExITk)?vuB{C&i?~ze}YYjxL_R% z#G{}iaP;U7rDkxn%_UJLmdFmhoQ z{g$V``9=fC5Sw~eSW49k36hJeT1`l|J_@4P0XKCsW1(}SOqD&hikwv4BaQr_j9f(h zB4k2%9R(h0!h1Tq5$hqM_hrIMd%TCOU!I0S6=-;a{IT;)tee|eLV~RkZ*k?HE0M5< zxj+|cWm;U?1AK7-g=||svhZL?S>dY9KSalAsa?XIj+uIu9G~_y07?!Zi0hMscyIqp z2Vu`@w+VN3zB^?Y*{1@X`$OJU8#wu9j@6U7D+j4$iE5<&ZS%*Z)R<2{?Z6rqA$^pm z@|ZklEi!KK>XjQ|*$*EqSy}K7T9`U?v5l zKNwVoC?CUj<}dyit4hQz%PUkd52p0pbh2@QuAn|T4?X_(G~yKqchXuHcV~mI;qx zwwGw>E2@UV+$|cjM9@rxcOia76%Nzm#&1t>lL{O>wilcJqR#3TL-u_Lbu^>{)S5G4 z`I*D;p+Bn3D5iYsXv`|%H~520y%YEWmoS!uyG($nfjC(49Jmi9-bF{+zU^yMQ;}Vg&EqcTVPHf`|?-}i} z4(VI!#PzTA}JI?l6N zPD8#>V%hEke!8t!fIT#Zcjx;QSI`fE$`a5a{r~=G$$ohiUTn&I1~Vmtl4fKL$gYQO zhGBmx9OwEB-W`IVMWGgEmu9i*IoG7A+T04JF}-)FP-rcHY}AY^z`ErjZ?#I1*x;=e z5e`JY&`Tn#zJeo^G)50&gBSQu4ZAR>k6q`2p$gzQwL#HPsvQroYU)pj&& zmUJPlkci%ECfw5vBKowsV&e8DlNHI<;jcuR0h06t6!#f@anKk$b_sA%dqPPp#Rp`X zc;2RZy$7YhI}GC5L~!tfJN%cp9!uZv9KucGauajj3+gmHM2jcuRZ!>1y*J>3VqM~h zVWj>Q-1#G7x!Hi=88^4{Hnk7ssd=-NRGfFJ3B~ui`l?<*N`C*@@&YH52C>lBzA_zz zrBA@Rd~q43rzSji;_182`s4>;JcXJAIytr{Y`%?D4v}hs@iZokUBW+)ks>zP^cwO^ zvc>l9j#M4qn?M>Ve5u_{7DY%6J4Gz>C$r|uBwMWa4YM#3W)Jj{N|R(B6xItYlJFFC244Q}a6ZzbV+T1hgOQ+Lw?rBX zVUwEGZ#mrB)P&UtVUtoWLi7?Z!3oRrlRUL6w`b0af&jIu?b9l?sa{SFw4v z*pF=oId(N?#4u<*qy*B1hfK=*o}@(2RDmT)E&5{-^QDyFsle z3i5gL_~QCm0qRYC@2=-v7YDmQXkTCX81~e(v9RZK%Q7yOe)gB~SBUxKp*#i}RRaQi zP!kfEiY1ZsIXY!9U!!nqG#xQbvcZ?`(rltEN1f@L=PbDJa{lM=-L$)btshH>n(~VDHJ}cc} zD%fGOMLD-d&urbsZ04d`B1d{0f=V{SYl2d@hNq!wY1mL230U8b>oyF( zF*LmjjAq6Qer(=(WCZm_;tALFFPQA1Cr$2YAqBzSKE|V-b2kf48sj{1Mz?3M}R5uYo;?dWt18r9M35dZsnaCIygSw^qx7*slL z)k9x6rji6_`k+=IPOr9cm)3KllpLLq<05=rykPKM>NG)MMXH|s`GgB(ob;f2BZcn} z@5Uhf7B$v zehs9$!7K{8bp}m~Q$EJFquqwAumz_uuz2z0o0s+{oKr4`lZ^JT^d>tQEQS0AHBc_YCxW3>K2a`79?xtIAbvVa`=p z>HQwAESHh`r7~fXjf}A7G|3GDR0+x!2(t+h2_5MUNe|=lh1(7{gi0(_mqSJ^<4JHs z`Mjx&xZk{{Dd^{=Es*9A6tu54L-uX zuw7z*ntD-)Mi>%$Au(*+;5O4y#GdcEKnIE92b2%ICU_^4Fej!fA~)>@SEpXQ1KBI< z)X^>9+^;5Xvhrd6cWnqq+RLEK2aU@_uzJI8f?U+wmQl_A;By1i?wVtW=WWT4KXof%sl-e4aIsAAX(hRK1RkYBN*s_g4mX-$dAeRT{ z9drx!@!CgUUA+AEQ7Be#?LQ6!7>87l4SH~W3{*JQ(jE(Ki^v`?g>Ss^%bK@bZyB=$ zdBl!@=g=tgXCy8Q#@vg9i5Wa>maP{E%K|f3vlFRchP+0zpOHJ8iFs>(mzAwLCBI^Q zGAs=JgS|?WxwqmnwgpY_dTQzHvh9^7xV%-olfSBiXr-t|#O%ut(NfSk{QlKc92+FU=118$7kj zAI?mzutfvpkLDRsZk2KCXnqRt>8pHUrwHjV4g>EseH3f; z3H|;Aq@*EN@QVoGMG8??Yq#S?zThK50{f`mg*GD0JP0O%DlQa#ST2Z#qicB);^L2x zbBHx!kYuHIlKiRn2lLw*bv~A@o!tkSX>fL2l<24r43l^yqE?K&?gV??i$apD!`#(y z(+$SV{082geSjsdX3Re7KD1fAI1gcFPgTeawMIGV|G^b*^Ce|#1|7FEkA;>K5NPZ2 z0Ax+jVPdZM>k8JlA4f;?u#Z~*3(8^mO!2U}cS5bwTfuyHkoq->(8WY>6UN>ij^v%T zrIxeL>gpFvo`T<(TAr02dV{dqv${J@416!~9>xonCvNR~!^W4IG6E~feV;OY`x4`-&zd9?6mE24fY}k?5yQH@Cv_X4H>qFh+&n z2T+8xPU2R~6m1NTc*e;TacVA;26noF?w+ey_@1d76$KmZrD4)ygC|pEor06#t5F;W zHTp?^@#(ffuh?uc7jNBjlFqwHC#RP)#J>i*Lm_4*Pttf0#+*={jA8t!ZB+x+a)sA0 z2$?P%v_40J@dw#{? z!c0c*+l9D;65M@ZuP7Q?zGwEy$%dvF#opacMfB5j;d}VA5;XJj%pb!b>5~p{T_{1I z0%6qZcrE?OX-!fH2a(+7au7U^MTXsnVaPC?XU?ytZy7pVGyGt~+up9$^JDIBJhqjK zZqPJg47OQ|S|js5Y}B%=<5_BCm%=-?IQKMlj0*Zk$90F9W*u+)JXRoPd7usQzM4(y z+)CzscZJu_tVJYN)En-fdqIw;%&W3Z-CV(I6h^B+X0`W-B;_0HIbLkV9REr{#vs&# z?4vW+@Lm-|{Pz#TfexIb`0Bv)T!G>DWw84<;hl+TI^<&?`4j0m2}z$Vyk&ly-TJ>` z#29|p1>PS(zy@?}kz;$=>2PCJFcYVbTiK`Oe)AH~zy{OEGhl|tUDaq9m@BZXra%4s zt{K%FT*i}A@gV8k_R$qm7olCTH?DHMvg^GBn7z-bpPnUS&X)I9bV6Te%VBFuq z#B1Yu5t`0wZ}=}=;oiI))?MaCF#|z)VD=njSVOK#-^_r9YQZm)VpB*JW{Ez#F81Xeflidax9x5hvh`H(dFp zF&fy@u_kop->*23Y7Y6Ifv?d1u3eHR2|&>r?afht99vj*0Mr{uh*~JghuPslR{=_84LD&w;S%q{hq>^ZN!1EjM$5zFek+{Z#Dlo^!$!VU5m7u03+j5 zeYWxXdAbnGE*EkP_Ju&SKcIuEEujXxD`(fKw0&)i3osmoYJ;4q@i1a=p9IPu;+b4+ zeP6w+7_fA@dm74rs2yu;BI@I(+V@Bp1joWD^A~D~xTHVqC+Zr6bQ+DuLo(PMflDmj z=A1jQCKZzYurRd{mk-TdsGx3ZSaKPc?T(p}hnxw|ts<%`hU-$Deq#6KZ%)FgO_I$0 z@E`_^65fK5W}%HIOy0Sdyb?*G@dmjJsb_Qr-yahS@%>daqVjg4Fi&)0Ick<_Y@@iS zdXCq5H3S^#?n7{BL{1juSQgxa+Zdv%vJ={#y$`d`iiy>)eX2}L!KRK4rgAYdPh3EA z)K_P_5BDD;&%;!$WR#+nH*0y-q?=Z|qLK{uH6+*x`Mr5V{`^0r!>)}_8*W&g_Dn(m$&<>0bK#vf0=DTXD{f~CF~BK#^H7DOGG zV#(@)y%Kps`JkQ52jhq1OT01o&M%uanK?b#s1RCzH1cajryU_YFOwIYj#5sc)!N!~ zn8>dfI}K{|F9w6JjH?)JP91^sh zbcsVvQT?&E=5Ehq96|2V`_(!6RfZmX?rH1G4p(w2#5oJn1I@>_TH|&b*dL)5q15z~ zg)(j0n~!f2;&Q$D>4pmabHgvgppYrtdeCk!1bX89zW*G@--q`*Z>@6t+bfoAL`G6h z?$jWQvTvRvq%qu@(*X1J10d1l{kO=*gLlR)3=e=6*#?E?)oXa6ACahb#p}5Zv_ld8zLjK*w zmW`xJ|5-L@rL2jUY6NZ9KQF^D!ujTVX53iU{9dzHQM_B;1sgLyf=1m)nUD5_Z4H*= zq^+0Xl-(_%YcM3xF#`TIs$FCgtxo!0CITeJOB;HKl+xPPd&;4$AFoPsjg1m;ng2hx zI*X5_5BmH@^5!b&{_)yjcP*UsKxp;^V%l?lCZ>b+G_EFryxNttkeqQ8Ov+Zj&JA_W z?CrFIwfUyzv}#9Lr;!P(D$LR&@*mC6$m{Q+Eo7lnNyZ4XO#BI#PH7WD(}`gj^Z-^} zAZKoiDt33_mj|q%(VQ->N{6@n+?nZ8yL4@mG*WVoWUb}NTO^B3YZo-DXVN(~Qv4s* zZ#KsQN;UDCmZ0oP-w$(2mh1fdkUj=1W5Pk+O$$!F5@*xNr0A3DYO|m#3+T|FW0(8W zC&l!>id92EKiFKa2{-+|iZ-;5h*FVMyb%{gJ-+CHYj=(d|a87e-8maaegOMlz^4X&t zp*M`N@3~y=3D&o58x;PF%N8DbwRDZXhb(`>J>oGI=~E^O6>Wyl^f~iq!>uT|d;Z%? zzbReSDL=cIGp`8CI@!CArhwiFU=rZE@GfpY;_}JT%0alDy^~GUwA)h5_u$tfgX5$9 zP)mmkwkHv$;BTHp{Hfn9tf@LiU9N*vv~u+JlB-2Pc7~0YJko290?C|ryzP?1^hCxB zOKHtdIr18+AY1~PsEi*d6N`J@HOIsz@k+@^)#FoHHKm# zXqs@lq|}4FjDYSoFiRw7_@la=aa7X3INI@`cNCZy?EEK^PenluNcX5#Bc;}04}RcE zUbBkefWh{)bmA{(cHGq#9-|6X1?@AJ2fqL_^`%i5WFlqcn3FKhHt2_74I-R#Y85); z)tKSHKsWuV00f1q7~?kkbRIGUL&6}=ga}pX=-K%7uYE2I$(-Zwh6-$$<(MO2SJLgm zp5CU=DRTq8fVUt4QiM+p5$Q+4X}{erdz3Gb-Q2`wjMrCT#<~UC@4!09IcE=SJmpHX zFr9tuXQbJg+*2}t`YMQHT|oGs+c`IT!EHqHxe*j{R<&j#%kOd@!7PVt6l!5Xx8lU1MZC5F|2kpn z%QKFgZJqQ(<}q<^3sbj2A-81H$hS&l}mp=Zf7?((v<_eEFe!rYxX)*yK4p&`<9 zR%xIjRR|HF)HN0&@M#6}{3&UMi6G$`WN8&)u${Q0Zh4Y7`X4t@p7j|e!0{5&4@01B z5sPtH_T^<)wEo)Y|HsxZmB;JHg0=d@^B$HIB@CZxuf zlIUmPx6k0+rHh(y`S3T$;2n*G?&)L3`ZyjO6s^5`q=oWAcxvZw zxz{+jB~E1iGHM4SSz97V)3;TQn%gJFVYfS6*Tm3#{+cSgYZ}qtvM(6d!;MbCI{1^c zL;5y(YzZ8nW}d&sefViDxnBb8n-=m7O0ssWd_H~{3p|1BErU)fAJ65Vqmj+zD;K^E zy#@BaivwAz4o{@u>G?a>G>7;O@q{pXE)uyvgbORAdm^B2ZvHoCC=q>Gi%321!VLbt zzSu)9-2HPK|5KaJyZR+fj+?vJ_MuwZuNKS;=Q!qED(WI%w<~kUvgaq%ppawOduN}x zh1|#-M@^y7@!#t7feJ7i|3it)@g_?m!d>3(lEvp#=4p8F6F*?l*D>nd3Fx9Q<1r7Tt=ZGp z!KDo*kN@|}c+bx8TrZCQ5Gi2$AWH>jE8$-9-zP{EC;aEmarNb#0~`WAP7v^GsgfF_ zPfKIasbKRI+`MV8Ldm}lx@|EsVY_k^&3TS)NQ%diL#Y{z;(6}|k)!+wNQD}8WK$Aj zneiJbg8$rwH}&lA3}RnmZcRwNLAbvC^Wpk$-#{e>9b!G95pGT=!sp+g9dXZ6fGn{E z4m;9u^)w!_3<{JzNN;@dGn4l<{?g_Hoja1aCO|97B7ZZDc-xd_0eLDYxBEbm`hN;% zlZaywDkA~Bqk5^NgI8|X!Mg)^{KhsTtg%aCeyKJ(tNMY?A-S?}m8Sx7`54?GPOBq*S3-q3-IrH)Du&<(Ohg)vn*wx%m+gT9xAzgPEx4$M{?FBZ!SeuJpKVA(xOr# zM7Y6OK8oaJ^rK*H=fuHWkC!7VQm{2}dmDAxfdxig7Z+?zqv{%9<@X^P{7zFkR4lm9 zKc-$H6w%>E@_6xKVo1L@$|9eTpsHUywVnQa`hQs&3bwXs`EO_2Ot!vjQ&Ej9<$DSH zrJqJ%e>VFz!{N5z+SD9-*cQv5@<>w@Gc4V|WvlYB{9xmH zo7uu8{ClX`G*?A*h9}L}@?1Hm8s_>jm%$Aok8y1LjXQ*%1M_Df>8V3`F?2T~t769R z8EUS+aMaLCfYS35_maRs5WkZ@nvzYi%WD6pA2*sSjq1mbqlg(rQ4n)PQBgqQ ze|j8bZ~MIW`S5;Nmzn9Vs_w3?aLzB7Tw&0qrCul?LeZlbG%SXAV{C5OzZUFyAUg^e zrMO|_n~utk;-lGc(HAPV@0vZs<`I={K{SqSLTu$9Yc)cxaF%fTXJ0btbZ)>euIdFi z63OgoHnKvSJ)5r4E#~-T(F#0;ai!mW7f=t*%Hxh1$&buxOM`T?E~*ay`(Rx0R6ar8lP|GI630}@p34sDv|t4Z94yi5c9$ybv-KF85*TNia?miwHv0bNwt(DOln zg=|CFD7%p-II&yKQuErPgMbD~FXx;YGx^<|p*^@lcMkv^LC~+Fw{th+ec&z_{&m8# ze3CZ5z^>U@cdIzJ@};!fLrG&zI(nGkHm5YWCn@dskFR*^j2Fr^qU%%D}G0OxyDuC=R%v4LsKK zX2s@p=^@I#+DlvFtUdi|p#z2AT-Ay2_EymyaqijPpzqA|Oc_cZ)TV;^(TLh1|=>(5Ur5>A}*Ti3p%8Q_MPRbZWGgXbhLLKGZ zpHi_8%?GhJr-@7BIS+=*ac$=xTIo+JCS4VfB;4}ymzExYLN;cK%cI^s>JZr=#^wq! zc?AmT4==UxqH`-=_74GJ-D|Yb!yf%%TPqP1{_}C(Q$3sk(#H?Gyi9pt5_b>r$5jMp z0?8QMuB~~EN8Kjs8iIP}?_=a$-4H`L8P}3#OK%zhTuxfvX<+lxgn~I~&a0j(FS*z6nuF{} zaT?ks68NORhdfXV5)_VAYoL?q!J>X?Qw?({vi>8esV9$Y)(;uJcw7vuOoHP}5keyB z=jRV*QZ^iVbjZ!`ZkaCWcEdpRmS2V(V{iv!NVDH!`~T>>cR$FsFyEDo`v(~X_eYl} zfsU^SDT{?bfjCC3d@RwkK0*8_`K3Cj5$L(hAOnoqTCb|G#;JHs6`-tnsu%Aj1ieLa zLC(yPOY9H-R7kP3#z29u&}s~RKSkpX9fA4bIS)THaS6z)Kfm~jn06)a=~s6}k9VPa z!YRoUqx40r1hi;(6BIcJb6+7bmJ-I11G<7p0Xc0Q3?B@mR_wk6ivpda6i zZKMhXr3WEpih^&jzVB)kzF;mir-H+BC-T5n(pd}U8qr=rD&GMLjYx70kwE*B1m(gj zHI4gguMWL2Nn)DMC*=YsKb52(#K`IndJRYV)!-w&pUtn~GR!&Tpw@-v8EP~Jjw&H+ z6ssI2yr6fI?8!w8JTCdGVxZGX&2=GhF_aK`s?Dz>=w#XOAl^hbrxR zOy^e*h#sT-STs|+`wDt{)m9@NMqgZe0JI+lLi?f&st1_j?fWO=2$mXfuRz)Je7^3X zv9oh!*c${7bQ+7WdD6_Kn|0r4Uxrab@Gl}%{COE+6RGflU_`OV%7PpS9w zURhqzk<7MfS+)Cj#CIN}KA2v-hOC2e#PZkg-tQfs`_Z?u zZjH;d(P)Htsag#q3_~rP z8`5k|Awp;ut6V_^5%p|MegaRBMXJW6s}aMC)J@8<2CmXBKOi~lIm@%-CZcK>Jtn!o zMWC>#i^Z|NAN$SAy~_KY@X-A&6Y)i~E*~1Kx@Y*JfdO5{Z^peQkp}ZrlYbiLO6A1h z&Qtp2Ti>y|boz+JD*J`)VhovKwR8LKy8?*5cl4(s#8nFm3Lyjgur(HPf>C`Y3skQ< z1%uB07)VA`ghBkW&LWH=)tFsd8)?w&U}Wky^a>8bGcSiO;-I29tP*8dENtlMv56JX zVf;=F6{XE_f>WwPlV5O%_RC(!i@XFa1gZ%S)TNV z#Gm$_93?@|1wE!!HZ6t@5&4tGzbM!hX7+Z0^71X73i6nF3`Ymz>ZC~XQ4w_MKq^=- z(tJYuqDb?723R3?C6lfZX+Cm=M(MyC@PtH~59%_%G?hK@+Pto5HOd<7$5z(ijt0pk zz3VT9dz3-?L1+Z33JwoRLmdV|FlvX~pbo3eomJ}c zaCD&`^vhDUe;*BNY|lD{PVrcz!VaV+eYZuSmCGK45+}+dY6qdssoQT0Iqw8_H{m@k zG7V>7het*c^NPOPCC>f`I>XLQzexO6otyrI%(Wa-!FPqw4Gj*-EO?4DKPk^PNccW- zm?oX@4pbE9BE)m9QNkDzjYyVpF|+qhL)6xKvlU#u;(4EY&s5oD#;J%j(4PHBhGv&1 zIR(RxdyzH|(q&XJ{jDeoE?Nb;g7BSMh&SP}KeaEyAK4$;`{57I=zhNcX_56-D^!@a z>I21Zcnc`3b=30Y3z&TrVa=nKfBxx&3fX6|_M!wMH3mKFv)z>sq8D?-8Pk4MYjj}2 z1^0ywS}kOJDpfLiuURmmZX&o*V`#19+^Cp8A~1Ge%&$BJ2;TW(-FJ%2x#|sh-`|7( zb#nal)W7EZ6Q$O^JkVgl`+gD29z9g(JP*;e<5wt~)GHmo{_}lPFuLxgf%}-%=(kk7 zT9-aF`UbB>9z^!UK>n{$6k#*U*y&W==?~khHM4XH?KkJ#Uf{8^KI>9Rt`y~J6@Jip zE+p{lN_3ZRFe;yg?Oxf}yA&EP+diF_1N_)ya8JilT$=&Cm6cHYMf&2Cza`B$KkvhK zo}^5&VN(ImT_&%eh0xpKY5kn|Ot^)%vI+{?qrP5|s=TNl-wn`alD<@lxZ>0;vi#lS zBWxJcDbv1V?hOz13Htf+{h@f{tF4>%KY-&KP>0y`x<()|efbIw*1&0j7dAA0A2%oMLu*H(0M@>+&H{n}PFXRyjF#mp7Y)Jb! zo-*lC6@+c_egF&Ub2o9%H$?N&H11Y(`rAH@!_cahKg8O z{6nJV!{OR}MKGw*r+R+lODT31Bm?Q_v@hVW@Hp?Q!7Z!uXQ0;H-xi&~!{(~DsZSSG zRlvmhiPtB-iv%A1e765#2*tR;7ebv8BYCq-V=b$qv0I8Cth|DIx3*zqgBGJaWdtHf zYqkcou?=xrI^y0D<@4?3HE`aeWHU0F70MhEO43!!NFpFdXyfQ1>&pld^Fs|gc%D!y z@PuMbPsr3|N4u?k{c)Xa4YT^dK!Q?#QzY?!-_w+B3buF&8ma2{v^KfS*CD&Di(f!~ zKA^%*qOig-MJ|t>(@ZDmyyeZ7y_Ck4gZbVxbiU4Ph04d^DoPOU`-#U+sU!BLA`L7I zN6v*)cg>GfUcoHUud~^$H;0)~Lgl57h1KD6mpL?ym%Yxc+F@c z`&295RPC@(nnE%q2`r$18#~G7MfFFO1Fs4C;slk4YQZ#F8u;x;Gv5SozX%uAECTpJ z{c*Kkcn)?c4>+YYE#NKOp8nnn6T9J&P4&uF&eoPSF_(ea{JBSdEnCH`+%wwz=|AVz z@NR@dgrt!~nqHsufDA8#N8*anu8VK-SabNf_WBv&a6ARYp+0ES9&aF3=b90cf4jwR zc#E%v;O_gO&PVI}^6>@=HF-_1g|{W_BjNIw|5ewK!zdCe&mF+l#?SE})$@E%_(qg7 zom9BM{_&SQQQx%;+3He<2XW-eBZ%4Oz`aY+tpsG zyvm(351eC==*7YP1OZa+iYfID3>B0W;v$?y+FqwgfUXye#_zqxcs~Z=3ia>te_8Y$ z>b(Y?C7iS+Q`V(I1nQI2@K}5}-}ZGeg}qK8T+_563G`8(wlv9cZy~gw!0xfz4HICJ z8P_z?k~e|gr|b3x@^}(g<_uB%wU6e<1c0TfmmgfgBUcgJy(~CjzKV1)+DHUz;Nklb zaMDGMlfB@uRO|Vq4#FLU`v;jwZ)+tmNVE`6e@5@6L~%q~s)~&@>T9f)OoLHtwng7} zi9cyFFVem<9;u2?)=^wdK8{||UuB?LtktD~d1p4c+hY-FcSR-$o0PVxU~KbZ9?{i* zX5~{3?b=!2tu_qezxIcOeBiyo3qZE$XAH+0*=pviZCKQGxoYK5)f_0H_)BjOxpNte zJSQEtcx?Y@g!v#8mguO0zM@Z2PO)zA2r-7cgFt7{vtiN3fyS==$<^P@PfOk-gH&=! z^VZ*u1AXwhoRu7Lm~WHM&&4e7&R2f~aUKt6Eqxblh(e)EB&^h+4^XOE za31vvuof~8DCq9Sy34d9mo@t3=Kp5cgp;yxX0w7{v zk*YW_RC0@CyOZO|>^#H4PM^??HdK>7ni&l5R51L19v22}K@j4ks_R*e>%qH@O+@uc zKQigG2$jI;&N)6y(AZqjlser~xa5sR=Km6{!=Vs!c3ek;;b8!n8%G|4_&k*2BF_+B zf)WjuiKP%KJnBVwaT{7JYtY4-O_Omsg{jrx8wjN>xaJNKUDsq!mUPqC!odiSp0>%% z_M`Itt$RK>jOqL$HTFn5Y?M8n-wZwD5ge9E!bUwh1bgp6-XOX}Edj67>mDuN|;kU5^ zj`%_$BRO{!%5_Z-^GGh0Vd)B)v$Jfwbu23R1oZl4!qakB&V^Y|5bH{WgJ zOFYlwlc1@Fe%z1&m*V+aT_7#vBYkV(UNp!{WN!0ZghJ%OJ<}D?K0;I^5QP)~y&>^>#6MTIE0prsgz&Gj6Oi%8LwXt8Ow-*1T!a$ltiiSYcs=6tt?*J)BUDpj+HeJ4 zZ-}3ez8hX_e!euoR^#2T50URYDfmG`tMZ|3(=RAso&lTE>|LZC0~x}rvS4P$ct6ZO z;F4}TFJlQQnBBQ#x9qa!B@mXSfdqYrK3_C zVRJTvYWm$EEvbPzzUVc-YmiXXMC*r`r3}F^XBb@8mK&hGOYIc>v<`e6?~-8x-6@Ka ze+sIpVzDiVFh3~E{OZwCp6K4j<`o%~C>G^~=|o*}(-XyR%%u1D6;Vh<$2-N{LC;JX zthZo9hq{HL3hx#5{jSVpV|350J!W$@nj8stgH}rIXum8t3Hc7ssJ7jJ+0E-t(VdXx z5Wb|V5D;I(f5pDdTqHH>mFrnI@%6=cYz0Lm=+$-sc(cP_gV9qFpVU)04YVXZQ@yhyPJ+cy}>G zd|x{CmWeQD?*0~dsz)aJ(SyCE-`B|6e+b+Q%{EEAiRG(&C&#fV4`h9Oo@suO{lskF zmaq@aIm~NB=nwwkC0y57w(0hWgoVTF$;fO$fZ4fW5o%<4WC8@w3~z=h{$H&u2U_(i@4LoD6fAK0XT7(wG5On;K438a!~w(@xC_;ACLNpy(j-x- z=|(!mra^{S2uG7ra$ZF!ILnit4}&4=>+d2c-iM#|Q|*M-uYL9dWsuCz!5~qM0!AS` z&(5XXx9V55y+Tn9xZlTDx&g=Az2agF|j=F?3DkY$CcK8MyVydauf34gdzT|{thE`1Z?2V6RA)kJ=rbHf(=D;cY;Mv!LmWjd`qfso? z(tNtSnm2iS;_%QrkcR%$&SOTYFFhTqFJGn+yw#YsFp>*7bid(`jbDU_BcjEpJz3+0Wq+i)PqZF$xY zmo~*72Y%R=xL`Ov%1=E3lRdJKjZHCN9yjdCbh1_>dF^cZ<3&hBnlnZF_u`?oQ)a2w z>v`XW17n@>k&g6^+p}7UIJx~l-7U;bf}rist&ay;3^02kjOrnDYX-#K2cF4`0{AFo z0m{;+a|J8YZHaJ*2fy<=tYJkw1oU%@C)O=O!Ab-gT!)1>tcm)TAsx~zsDstXFW{A~ z>*5*|(AZ*rmae5}S4#Ni*ZdTOM<9b)SdW~YETk&e3sifa$-!n*e;x^fEVibUc)6B&d4v+&2zdTsR*9g9K+Sc4YDA4m-|$SRua#5 zj_LcAu|qPgl7%?oEU#%j6{YMdKFcr8WY8FzqbN0VQG__kU{pT@>|`6z&jo^&PvP}F zNS<+MK6yBXByQTTCARg?+i>RtpHgvnx^%7IYvWDN3!=%$98)4GZHEV;^(CF7>Po4aSgi~d0x?ph@61gM-0 zfuto5hpwHPcNsF=N%IYe9l=}jsp#@iu5`~TekAR`P=YjMFDsJd0g}kOP!R~uar%ex zO&u1X7Pwk@Ro6eczjBd31=}vDU^Fro;&9o^B7~86!knFE-Fyx%5Dl%lUl{noJzRFp;hSa*8bq{9h^ek8neZ$Z9P7%<_P!(3*0W?(;&?%^W z1Gjg$PT&U3Ioh)>F663l;7w5Tv&=LUtwd?bV%~WP5vrY_^gn`^lV&pTzt0nhsxe7S zXYCY33rPsCm?!LHw-j6&nL}D9+;W9Rsa}q4m5P;kUaQ76@SLVvI}R=P!nBfzo>=bLJyufgj6DOPKp?Y`AyO zSub15d>suvWM#vYdTE}@>+cVpLio5Vg89lx1CZx%V@)W|H5W8Qi8zgvla(li{pqW( z2P6CDmwx!gjuRBw;)jz_rBOM-S4R*qN44w>s9p>~Ejnx>myZ<vM`GAKH6ST1_!C*q-BBSlQ!GmL?M zu}_GDOg|WBH?{Knagv7(z-ualK*weDQ$dVK{$EcTHhZ!jCxFSJ7GF|VJA?0e50wN@ zk3elXG=pxat?yKPfl!w3^G&ppSukex=!IKPm|A(?v4`{}#i?*e-!iin;xT)%ao3-D zwaQCv-?HKtriRT!_P!$u#m_)Da?a%Gnk!UZzhh+Y;BroMH6(B?^9`=lt zdzi*+Hb{iwOt)bH3%}vgMbJvSbZF;vTsJ4_ShYF%xGLaW=(rr~y4&=cD3a3Xv{SQfWJXY~skp-iLaRQcHE!B#gHo!<|;Fza=T zoOD6wYmf1lFCzHD6Q)ivZDF@$EHiVM>ugoG2$-8Q2g9|lo`bm(SsWF%>+7!2<%mfK~j0@Hweo5oS#Jh%mxbO80IK7J#&f!9R&V*ysL*w{Qk97GZ zXCOvDbUy#m3Xe-rw6Teg|HGeM)uZ9P3Hx?(G;6~)c4>$*#_&A+ESA#|09D zGEtuZ&l^wHJGS0`0N zD%VpeRzty0RFxC;V?8kt*{twHlU^Mpd?!^n3#dIcK3i*|3hq0cM(oFp4!dt@sz8n^S_sw~ zphX(vr2Ts%X5e09tk>$V%S$=pX&lvzY95_#WP&QZV|LpM=}f9(7(xc=OQQa1LeyqA zq#uj0U0jau8_}Wus4JkKu|}ueR$}^;=d;~X{C36jQ7x+syM`$#4{8%t5-wUDRWw@MOt1FO*NR>OfZz^YjZjz$o5O)(P~J+Q9Vr zXQ)Li3c-h8Vm>~HY>6XX1kE2rZzz7Nx*;wc4})4wBn&MvCv=*Mz?bs1)e)kE&3a7A zqS5yqO7zBII@*F|SnpCD5lvE8f@J|&Rf1K}8C zu|jCf8}V8hbB6xl5E>8XjbE>vLLr>v%*C0?tM?W~MKUwc+oHD{>&bK&tpZJS<`$h( zIlglO#zNDjPuLlr8{B&&tx(f5j@BcC9y$~r0*!-MwCmqhvD{gXi!mwrJKj`yUD_$~ zlO}748ZcbvA!txRg%Uq;fey|jI9uEjgs}TMZi*=!nr+h@>d|qes zun+fMyaJ?i#w>);iBuA{qr>8z%9Uk?jgO%04<6YodxtuZNNQ$TBe0&Rg$ujLVRpmJ zMa{W~LD;-Q`r}_oPm?(#T>FPDBknzlGJ;xh9Y#7RMJbBMh;B7H*E+J4ZTf+C)r^ft zamE#c(rE?4dk7DhvSz{)+{I7G;Pq#FMzA`q$}{QOpA3)v~m06fEf}ZsypN7E1Ms?zo7_`#GE(y zs;F6Jf~quU6EUoRi1yVtT-hcOp6BBN44Yuw*Mv_7uo~?~vyq>E=Wk5%dsHlh`zx?r#o;iEjPRJc# z$~R{4m&9la8zso>%q7TN%!XT1VZA1btICU20@R=4J|OJsn+dMUCM)ggI9#L$yvfo! zZGPKWp)^ZH)eEz}_$iBgBv<%oaN&rlg;;feQ$alLOul$Xq5Nf=ZW;3LE|P5WGz}Wt zxcI=>s}p)wcZ;>fi7w=bUcM`OoR+@D)lFUo@ZPuKT^{j+_TST#4N__cbKZG_vpm7j z$0O9P{V6|V7GSJ)2SYet?6wEKhJB+%m2j*Q7O}%G{1_g`MRIY3LDIF_p?yOd*|WT^ z9C>5uBXB-UCDQhf@E%nd0?t68O@0vYCO-VT%%OGYscF1O9H76z+JsWoyWylHSnu6e zZe%x{q2s8yNjKx*Wel{Ef~_bU!uOh#!^BZ*%b_t0R7S}6K_{QcE)Ndrr`jk6RjfrV`3elGlXc+5@jWfS%^!i^i?W=XH-lCAOZ9>V`X`9wJN2YHdB; zYO8-~edC{hURwWh6JadHd+_NZOL7B~X4mx+Hjx?)aoKiBa2MvE(UVEi#z-3NkMKLs zf)RPmkjp==jc_4JH^H2rB(TaSG2K8eY8OtFT-HF88!gegv_5Y2Q9jO&v3p#blznBP z)ywTn;}=Noyn~ovDo_)Ip9M1dtV+yCw;_e&RS5beg)PXH8s&K=igkEG18>I8-daY~ z!nra8Rk_QoKCZ`#a?lG_K*FY#KL+hY{iLP)dE`wxbWA1wAHmN&VrnaqJeO4c^?sRm z9KQI>p!=;*{`+ZCEauQn8@Lx-TZ4yoqioWGpU}5_W{ud7uap+fWEF^ zBr{;-4kI%myBN}rfMma^KU`PK(;dlN6L^nC-NA+x2g{YuYr?Dhe>11Q#NLIWbumk= zv~uAO%DDjd3}P^FVROn24#AFxNVtqzhQ4v_q`_Cav^JXFw)jE@SLaZ!9QYF zXycZJe!C+B&D{&29F8n28^sPy9By_Pb*u^j21lY`+~9uMqi|)!_ZVEEjjT5odKb0q zjE&cr5`pld1E00J+d)ek999by3hmbC8IKqLsq^XMb>!{Ij6E>f=Ai_PrR%*KE zq8v%rz*3Q@0p@#2PePAb&iBX@6I?l$L<;<`)CQZT6waBv#EyY6eM~elE>}=Aq=WZM%K*(9O3`L7@4=ol!e|plJ2E z2hh%%zRX*^vuvKSzHrqTH;iptejbhb$z+UPjuyE*O}NjrEdp5CP*zt!7^{i(aoLMG z@JV+76YGgoBQn^ISv>a?h`#fA}!#TXZ~KcwqKp{(N~3>t7guAA@#e@^c*hsi|+wH zWSxrRA&UKrk1BA~?i5^x__ZoC0B|r2?g&8&4rNdR?1~y$ja9yj@9+N7DGj6p7 zD6ek3s>@miqd1(ELZXg4;&iu!+j^Rr({0xwk4~2g-g%-HXfUAB(@i1SvZ5O?M&Kw7 z+WZL4^HbP_3m3ZL^meGtPh&!jD}CUc58QMV`(j*AN%{0!4;0ui zNY%OIN^)KyHkA#`&**zj<78tP$4k=OZ>R9Zk#$gLZH~?n?+u2Jd_lIy!}#A23?{#R`}uVB(oN{Ab`^14LV^e6hFTUc;dA9loslBj$t<4&y5~ zY3Cy3Ifh>dV#c-LT@an2;&fdo>1!U^N1Pcu5x-=`ky|y%C4G-b1i8tZ9*S7aGQCmQ<3JeGb zQWyPVeDA>&d=vA$m6M#|0J_;-g<`~Q(ru;CJ=ldx6P`s|yQaCq zQq@At7=97we`i%ON#n^lej_=prV+xIcho_TisF-A9GcpN<9h0~^VA`}%B$-Yb{q3~ zHW?HLv-CP(MrT;`pDkM(&G_ z{#>MfCj#*Na*PMxp56EPq*F=?oLq)X4Tqd?6b35zQNq+1CVh?N910C4i2nQX&T7-72@RGyRcW#!WFRSFqGh(Yk0mLP-D7V zctwnVq0a$HAQ4*pJ45c8bPGwf5xsk`VBM!rx^yroPJG3bPZvH_?J`PLkMO`U5J$9k zd~UGvvLe@y{K%le3ui7dcscu?G#dOpP#C|%Kw2SJeJc3C)0I9KW8Y7ZXgjQ9H2!|ix*pGgMbD%Y+SuB)c;F=;=oo`edFZ8Vm~s0{LyElr=qTWfI@ z(e@q|&J^s9AG6X(xtCiSbES@nC2U@*_nH4C+sXXiA?_gq?Gr)tz;Hd;IM5r;K7t(^ z6Di^8JCG}k0=m)@E?WyQf-_~u#x2Nl@mRjPh;QbyCexY-E6t1erLaSYO!jb02lj-HxNjleQS$0R6^S-$z_yx&t2Pf1y~BnFV|@V8YxmW!T;Lad4LS#PP;n9cgVK^ z;Kq=0(D1ZEw70R|b>8hc=q8kDBPknyoW`YXqjq2nyxkE)ut|Y5{uBr2+YsZQB9!?G zCAl+h>7@v_?vPAriv*VyJUjkUB)1*d4`+9@k{=$3iR6dOk)_O(141&(RO7?X#C`+j zjvILAF|^?u6z{%D*&WudJW+rI15OCIh{OxhsVC8pWkkHC!FG!={^i3D5VYuhbO#kj zq+|Yv?%@#4t2QWJEIo1-GsJ@eWN&#PV{wym05)>dOLz}@pUrQwUznesYvLX{Bpo4^ zLyjJY*1_bNJXM>7xD$jn|B<}?oI0skd1MQ5b0^M!#D+tWz;6S&fzRN%CW-s;6yV8e z2jP|&xmGC$K;@6S8Gr{wUjUCtFhBY?!C1qZ6Cq$sK3H{*BZ<>8*ih^beZ!6d98kZ2 zBx)llE8{PKqXT#mC?IKG0MMYVUNW zq*!;0jE-PuJ7n$oOZj(M*|2Yfx9#1#>~O|SReFrv&qB#B@)`$ri>ln9drT5<|@gVAIW)uzzi%2c(M$auy|fPl3)YeB}=u zndtnhvhU5A^zv`q`g3kWsEnOzPf0buzpFms^h!_Nr$)8YL}%3KOkZfX6jlqS>t`FC z)WO@p6+tWg-C; z+717bbpD)z-E5A*+nQy{ij;Cf9L2rrMV*Tyeo@nJu_+#wi`nSQ=mi~ME^tvWN4Uy2 z`}=n;E<1*he)eedXu+Vgk=iF%(ziHtT`WghfTJf>zT%=t@Mb``=3zje!{e#~8u^u? zVTU!*)vlX`0NVDw5DE*)FHh*;DNpbL_6+{2OzXP>g=g)pI@lTx6-L<;FZ>xoSGbZ=#ww1wMS3=z_X3u{zLPy-Sf@I!=7q&5 zP4BnrSwHqxaP}wcQ3hR6@N{-cHXxU}QX*o%GJFN7zw&NW)IBhckhpe%`KMA;pY=nO z@=xv2=Ap_bZ#C`UJSON%c3kl;)o8IdC=d_T^HJVyd^qH;sq7zTmomfO`Z4&~)oN8o zu$oqqzGI>!d%=@tTD8PiN4r?W8J1eeBh(|*wt9*0YSpX6!B1{b&&IEfKaa5rGJ@X* zK5~1X=yhZMYK!MVd173Z08rtbH~V^uy%L=EJ{E$A?yZ%9=i z2t#;Xyxl0|be6oQ+|=$fL>LI);OZJ2bWw-y} zM5j0itokrqn$TC63Jqb21^OzRU@hDX`?YS+abgbJzC}s6t&-FFPHmy6*G+FlgL*&B zjJe9Ewq@GfFyupXW}d&Ag_HFQ3@`=V4lVk79|MbwE;bomK1RU>aJdExwPM{q}UFm7sH%nI|+Ez3eX)2Gfg=)PXbl ziP8~T0=f&~B+qH~06zL**Vq+_{Q38o2RwWIAii+_R-Vgp!Hs^tGleuB#QwpKYTQvt zS(Bdlu#*yxNk~^dTqUox(*HgaF}^CEB1H8k7G~co5D=)d#4mi?zy@L zHGF0Mv6>Qtg=-eYuEnC zRI@v}-qoE#BdbRBsdbydMVT(z#op6UTXBY0PMz|{5DYOxuyd}xNWWx3DwV=78{+QJ z1fTvslnCw)ZVvv!_KT1&o!=`j z&AT=A4pQhjped+oo$G?qbhy4Tp+HS;vY%icrRP2U~o6zkAEdw#!d}-_n^A_IbBkNMf-WqXe z4e~|beEs1M-5Y|IWQcSMfgE0ewTH1$2X)udXKn~=&fyu?TFH^CzkjI;VSh?SO481I zo9ag3kNH%|ao~jvjy?{~c#4;K=GP}lp?cx?_^lz}19=nUG1i6z-CEPsvCi+NKEI1Y z3m*3rN880}S4VL+rJVG!LA!1}?14w$%kQn=J*L&)^s^5`)d5kz3v67ZU*BC-z+8`< zhX64CV4?A9F$HlxM~8EySUGdsQJo@^$$P1f`m|Z`U~X%!x?96@N1-FHn|g1?(vG>J zQ$7)JR0pHTHheM4FdiMS)*HoH9e~X1V64zY`JqGG%ka@tlrF&fyKMBsMk&YnGs{PfynO(wv0HeKw>G*ThG#0nqD1>$P(GA#C-he&Hozh1JC@-fs&OPDtWO(Jho^tK$J4YuhkIp z+juPoC9CsP^iRaYes_o^D7i>iN5TEbQwX)<&1`#EiVF^R=sUKp97t#x?GYj@)2M1s|4{A3+E@SVJq&V;?z0n8f~1pU?L z>s1(G{kIa^G#9HNU$_RMZo?~n9)*0K1WJ9;=r@#_v})=xp5PAnO%z@n5WqxYX4LuZ z%Oq}^gv(9Xc6i(zzW4!^X@rV}s8m?WdU%!1AyXL})dPc)FF>F-xW9yR2OwL6eEK!Y z;p}gZ(YLpNk}Rkt8s&5FI(B@%4u$&P%r-uvPpdE+O=nHVt%H5(RKEUYZChjtAgsmY zmA#+Z2|-mkthS{U70PEykBE9sp(+>Jo?LK2)$K#Dd%S(jAHqc;8_|MFQAiY2sq1!1 zKb?UT_{{8>|9jm!kkCg*5^0?U{p9;oMX2UiM0xNp4r7-X+;!_dAmZCVo~&xU3+7rE z`+XQKk?sbYGH<|bb7Mz%5NAtj+Se5naC&ry2-VVONdZ;7((St+u|1JPO8oVMB#gR> z*mPFku*Dm0n#2i&YJTJm{CH!6c>zKz1mBeX?yT%{S-;)PMteEV*0PW^s35K&f+yZxtx`y|)Em0CgL6SHpzgV(d;IEi zSK!q(*ec(%LG9sRe&6LZq07$Uv*0}{1diNA0UMu{=S?1nByF3L_?F$)7oepM%m{L9 zGc{aQu1v`@vK+%65oY*t3f}lxpWh@W*#u_X!@qpIai5?p&OV*irMsiyya{Qa-dj`@ z>Hi9Xzm9dsSivK3G^>MLe9idxa1?XO@$~vx`6RyH^wTALPdQAD60*f)FzZZy0TnkP z&4YYcP6Ec{YMV@;YcLW~g-fe1FnhJ4f7}TL{!;3ZM?X$@8Bf4ud5m7!nK{N=hUE@(Ue;$jT?WQFmqD?2~i0|naDzwKMv`N01W8S?-->_~+9g`Zh@G zhoW6-`ahv?OH`NyIaKnFFOv~{M=$Z;c1wgg3Eqo-AQJw)_8Vm_J4Sndhf0(j{N1l= z`(NV7e1LI@1I%Ubp-+wIo*?dTh9VYsHb1Zqx&_`#!u9y2Y}A3fzYOJjh^8(T2W5dy za2>>co%rWPtuO@QgAoy`alf8mdbo$=n=tCE!!Pt?rs z&+KQ}@x5y(MH>09$NP}!e}9C+uLtv!eA-`tvA=~Wb9wi%%ypjT7n~6yA}?MopyM{y zI7j!xknocfcF6x?b`6}^90B;4u`CpI%7LeAod#hXDq5f@4!PqMP_PfzfI1T9bvzh7 zNP*rG+bEQ=>1qX>7lw?{kKsHb?zYNTEkJ70sb>3qtTckGY@{dTK>qBA;qQOt9ne3( z@06Wk^#s0{^*(!b{HLQQ4sVF%%z9j{l^l0hF-H<85 z5Y0vN>UsI>Tw?I~{wnMu^Px!lD)J{23=Pv&DbVV4`=40l6FHl{s*{QOf{IaO_e>yd z7V<+OoFVUxUka)lK?OnNF7QaJ!=!858!5ueFSC(muE42BI5_dE#O)a{BKR~IHwrD8 zyKV$SC1nk#1j1zOc_Fn-=%D8gNt89-*8nH(_t zu}tGxBzB+(U$n5>x{#7vvmw197?InL&4~xg0 zC6%I=)MAxt%#9y`JI>Nnagk~@>q^4~i}x1s|5c=?)@6hQF#dXfGCsyNhNl)EY(}YA zn@v@&(0l<7M`M}&Z0culfyW%r)hJ$?Y5=nJ6|^L@PhrVNZ)|X}Cv4CznyM?U#CD=$ zUc$V9VVft!^A;cfpa6L+>l^oH%Vsh&@-;u5dWL>Mg^Fv8EYYGbbqZOwzzq^fCvB(i z_D#@wc{qgnLxW9wGE^WF>Vt8Bp)b8GkN63~d7ZSC_&5$h7j(a-If^hG{C$i@u)G!X zNt1&_4bn*_`(@Zh^bO5kiObsDg(1+UHIeR=wXywfd|xXSq(F!4m3mJHEREvJZ<6N{ za)Rs=`-g+aFzwzlXN%bqFnji?fXmk{o3|@pg4jI|f5N>lM_1#M!|U$h6Ru$3JzbsX zJ|U?hG7a^l?}-GUb6vbM(!Hf;gs1)?fgHZ=>)Ym!z9ht8u6-fhiXi`USRcX zIqQlb1fSJxS*R(^A92%^{>ZP>6!tGOPKHk-a!V0*_T@fDd%TrZ_Nw4;LH65ziiXo_ z{-j7{&xXW)$DZI)C(28J8=Z~7sEaV++q;4{tSA(+`<~P+*`aKf(yWMs1xg;+CLui* zq3RQczQb@eVI}|W*gf2`S4c;OgK7sap6wFcT-&xtKWzJH`n;7Sg=P^l)ii%8YOhwO zW5!+6p1xltjX9)f4!<_wIFnJHSj=9N>uv{VZv!a{fue}@?CUNgHCx_TIe7xuQbfhu;@>(KIQ@K6f_MY^i`H7Y0!;q+LF84)K3qhI+ zt0lulUBwRU(PU4}JdA#CG1R7nAPBVd8x`xoyD-2{Ld4q2f^ufgQEFdYo^{FI9EIHJNaEcBjB=;RQN`1#faQC}|M>i(TMx__hbwDt`<6tHS>9uekZ0<>yYckDz8uoD0#>XtKXB%t+oMX#vl19=oA=^0-fq zVpCiPln#|IFq0wWAwtG;Z9Hh0)`QgS~Fo;mYQZ{K5Z zVhLU}Cr*H7+gEqoPK)VWRz0lYTX) z_acayU&W92!fo(fGZ({Bpxd_?>(T+Z!2h^??wJ45JqC_Vg?bZ_3rSgeM9yb3CPzsP zeVXckYLfSz?$E~~-DtSXqOrN%RVI9qQNJDI5au~bAb;JigEQr@xrl0-H}MVCDCVnQWnTww z_}Jm@NO7R>9mTI3z8msM&>IAFr!81(*W?3-nvep8;bW&q^@Yk;PW{j@RVI(!ar7<= z6?~r{$+;I0zMyVL{|hQj_KDw_ALWH^?N92cRuZq7DJr-PMp54;e)oaSnSc2KTpq%r zb$0Z1u}!j#%=$pRqqo8N<~{5> zos;k4Fl$+~v&l^gW&DnwfRFLW^6a71ap;>Ys>TKK7lL@G@uun~bK$wtcb&hhL~zV8 zD%NZR+P%CpMnt)lE3NUujDoCr9!SppbO&``2PH6jC>E;Ldj|_ZhGrN4kFx)Os^aS2 zfbnminYq2u;nJiC_JRo56?>1pMPrM`B=*-N8k5jLRJtOd(yJ&aVgcy_0wPVtf`|na zR0Ip4SpIviSIs;A{_py}wX!1j-Z^dN%sKn){p{yS17AsrVN5%;?SYU(NC9!eC~stE z6s8&k+caA3oqaL|RDXG{d4{fX5-rc1%XrRxbp)b+lS=RW!1~%-NWcMf51jg?&|G!J zxvO~Ubw?lh%Pe@{3?YZX((8{01b*Fup8%4|U4U0+GZbm<8r zbnM^CguUm0*--6@&E@IjS-Ow3IJG*2x@fo8is{n7Z)H%^06BX=n!X1POj@x)nl!cu zO}&C*cwqf-9n(shdtEcg9ojtpBohp0_P{-Jsgv$4evQ_R-Vs9(sjSoG37NOC=A$cR zBpBkSGH1j+PBQz%?JL9A1VhPDI62VeAYzT5Enuffn;oG=QC z1?Hai9+d9g!{YR}`bJISyjGlJrExKNpr=JASRa;jEBNPG!qj4D?pZ zI9VyG#v6a$7MWRfLXjS>7<@H-5%F~4ZMl1s`nB3>G22u0fJJA1o)o>5RwB#ahSnfk zatjpnz$wTtsDulP!hd-MVSiVIL9Hv&iLwWm|BH6&^1Q1*GZ!aM^kBqKmCuRw$PNvi z^7l}*z8UFA;{G|I)uG+(1ajhikLv5X$Q@E+AUqf0 zTN58biILT!JX`~(UXUs{hg;5|n;qjXfwNL!kS`uiXGohJ_fnw(!`~EpD6Qxn^WtSK zY{;3rim40f_m0-sII_SZ3z{8c$j{-sUs&s0F_H%%*7w@lwLUl0w>vWJ(R$G}5Fww~ z=lkskN`zT@!}P~uzREpI9>pPVo>r($v4Xy_wn*A)n>b3>+flHG>Lo1h@#$kA-Bti& z6B0pcAw>!T{xWBZWS>DNv2a<1Kx_UHMvfj@VT&exf9W;GFw5kWaOk^QbQefZYVsd$ zN*RjzzIUdw!sb}7x3tCbaSG^VL)b3&|7^f9a!-_TjM%9Nym|6ufev=D0PdQJj{b8- zaG4yS8d71;OXx;>W_#GSYjNsverCO&^<|hUO%~(2$y5TJ$J4rERf@}f;qJQ&I;;aX z`k6kXd97P6{ItR5T_^H&o6(Pj89X-hC>TIrq)Mpkq6AXUdNR1qeuqt}8kOe%yTXo9 zcXUb=wv(RugAWh!ZdW#E;U7*~cPt>RbLf!Y)xB;y9akXQfws)&XQRr<{$k^^2fTQ+ z@#`Q`7j676QqR$j*t5}JHg*E&MvlFaoci;m5G&L$vU#k3$^-P*cgY*`Z3S zXh+u4zop!r%RzE+u-UHuAUt6g2HX1i^1BzmK>~gjRe~+||Jt&-vWLh71-f5RG>dr7 z;5SY;4F0~J9x6>>j@*QDyoJ{%c>d>tdc0-6zl;%`XNj3V4_KvP%n7qb%bR+4pFq;t zB}e-|9D8h4B%=zn-tU+Hlj;Xd);P%=A|957PheUwWau^w{*oGm-CO##d$W}@MbjS| zxgJu7T7x}7h)ZSN0uR`5i|;nbYx;ewXxvx#?}L~H!C>esdAL3{8tz&3aJOhg|6^7@ zFd35A{$7xP$21Chr~aD4xoMVPhN4vJi*)x`nb7+?Ff>>%;5R8cwA*$wY)q7k8_CK* z#aC-FUU@;IUZB&YD1b5e7V18lS+Hm{7VveFR&g_&xtU`>`eniJoZ+e8pQXd4SH;GG zdicc$L5j|oFAE}|LGm1se*iP-Z{`^eaN_Y*$o!a$(`dl!)w>% z_iz7m-<*rMEM65mBrX%@3_s{h!WX_`SwM|&|{h?_!~Q_0HyOVL=95kMHl${Gzq;&;grX=#DenM|!VWBpi|6`^o+?Y?tt=BMP z1xtnx_R{FkjF%nPG{j<_$uQB+N>HwI@f66^*Jgvuws7MQR4+eDh|J(|>xU!JpfQe? zYc)PIJVnLoo9|&rY#*J&)c#^%4lFLL0c{EIl~$;E>#+7Qq*6foINI%XgVT`^zWd{! z!K!F!4uVE4A?>96Ao8>io9NbCmJV8J6AuvIQ30pG zuu(jg#%stMVNL%NQNX*uA6}h>Fb9`W?hs&CIM%8E^S^=6SUrFoFR3Z?qF{H2iw}M4W?DsU^K=b12S#fo~R$@XH7qj8^9OLY8W(}Ms-Vxhf{zO+%-dvt*WrRdl8ZrxmT4$s~`WUXC^knl#D zcWOyK^5O-PGuK^rz@figIaH6cGI5skj=vrQac>0~8)pj^#LLE|4{84cN65X$)5RB! z&kuV3W0>|)id->5=TyOQxc-Y}>#$fv@7?>}YU*zFJyuihaQtP-^R7^Cl!>%c`ao>B zA2*SF`N;`_1VOiMspfFj{f;sW@8&k+A?2Gr%)wQ=fZEpDuTyJV+RUfV;cgG{CvVfX z_+n_`S4riey%>3<#kL324&Vvz_cMMGE7g5b=bCcBD(9RD(nNakLWdj1>R>29tv8Cf zmQj>5do<&Ew~Ui`iVr-kd_tp=51OKEhBgZf6Xv7mwr=X7nLnu8Q_FyJ+DoFl7I{hb z4$zukGh;uKZB3BO`vs#%hhymA32RSq&E{7;(S=PqD4EG#ui0QlPMEyWG~YL+3Vj^T zlXntMt`#RiWX$cG&_&ao69Mu;F?g3G;Z3Fw$`P~#jg~F zQ!8y)o-hhp`rpyyQWC8D^3tnVdP-eM37^K$*ZHxQXr6b<-N8wUiF|ZP2{IS*U;p@i zp*y&N8S;`yg+(&KYpZ(_Oh`Km7A^V&seJHexeaX1^AL{kH1ozk#olBhg z%n5X~9~_4By?qyS^X~H4mM{q|lemoFiDT9h4Av#$;azf3uxG?aOxp_9tHI}zZ2$N2 zYwG86)wB4jsG5Zge>O2zDSr?x-(Lxg#Yw%eDn#NfL-e7RH)O0yMa}q9a526_6+zXc zH@u-I^q+owW!;Xu@aF zmTjThC-vAGRNW!Rw5L(H0sW<1sXkf{s9H#i?htlc{XEKjpxncf${nVVddJ}0Q3&mC znW@9dws=4?gEJW)MpGYmh;z6lCtU*Pk)&?tnSlh^t4ZZ7`s5*m;y>cyBYe{HQyBr4 zd`5T)76*U7%lCrJO1SptSwf`GDUBOsK((UR0@(>s`~_8zSr4DDf_5`P9xMzyN3zF5 zR3Z4{Ryz&KlOQ?)4mW)PV_q{4bwcJC>%FUvEV*@&C5{T|1j0in0zT;P8Ys<&{UxaM zNu2v(XFK32ds{ktNWyW!4|Isruc<}m`49B=`uSPGn1U;M;S47`pkd0x{*imZMe;DX^%Tkk z;}s!Df3+ zbz#(AjF(|Va@s>(3c_q8Kdn~3T|3{e+>DID9Kq`>nH*VMz@$@3ZjL-XDrB59Lyl32 zZGe*XF+9N`+Ay-8tH7p?{gZX2aG6^-vw|_0!1aD;8lKOQLtyKRf1=ba{yFTmz9<|} zk@)jWAEz868Z})SrgcH*5j2b>uVx8o|4#7#PI|+U{PL&|t3E~tOg)kX?=Tbk886z1 z-$fC>?d0|(fyeK!x8v>Sl7mhEqjW;Dq!~QXNJ)GNl96RIKh=n&#CfzceNYl_ATQN_ zt6glx)JW^+;+}~4OhH2v!$GpA3?FEqW1asFb&qRrj{8HjovZA3(;x2Yv*(Y`AkPfR zBg7xpK{TX~sLg?W{*Wq+cP9)R_Wbhd^i%c`^{Ot0#-CA};~>oAORr1o-Qm1D8dJ1C z89O*0mtBR_dpJ`=LE)@A4G(oEJ_DUOPs$%NgkpJB-t7Oom&8%kw(Wfkx|cX_?6mTB z)VPv|I=J}b%EH+;9lh8m#SO%KTi%28-aGm^xeaaeYZpf5fNWCtlD2Ig|B*ebgbhuY zllgFA$d+5`UMXsKUlrBhXQ$_lE?o zAK<_cOX5AN{Qw|niAKf9-q}-`gD7Od$97W0f+D;8A^Fr1o{E{1d;hQ4>s%>=H;d76 zmS%ma3gK)I7S~KXxb*Bow^9Be*>?#1tu4`7b(&HoJfGPIw;NAk!3@b$(ik1OMq)_;-+|8rx1gD7!m}BgB_+uYVd|S|Fm&Pn zgN**%3@JBEE=sWvcn+tx1|Ssv5_7);su-7daLf&=G*7~1`T$?6mLbK40_V9r)Ei1- zpEUQtp6X0K>d<+U7{0XgC-PyM+{dK&9-d)LDtKX1LFjOzs;llsxKD2OmJ@^HZDM5X*G+P3g#md%1~(<$u}SCirxlo?ceWBQQwpg0aK13QkTRj)xUQr)j~selV?h*Xh+h!zPAMtw*5HJi?6J;&<#|F`-Q5KQeSu{{4j40Ac2*5fk)H zc-bA(iIC={r`KC(7!!j$GSN_X9P}G>m@(ad1%H5vJ=x-Z2LPT<|EJzj-Tuy(o05># z&+yA0t4a)*{tF5n^!foeQyXl_?CY~u<;6{p7C+^Y%MmcudSuLdK+xpxH($T6Qr8$> zVs=w>g%z>*QYY$lA%%%27*|z5C4GNtgw0+(o_7-XT0S1k2wqF+{rrz+@!AhX^%am! z7RU<3@~ivcK_9<%pkMO%Z)`%v2sQd4SAaBN2}D47mq~J|8%! z2o77H0GDQ1SpzS@=%iVeD#x9B9E|LRXpjZA-ZT6+F-rgWW&e>RZM@3l2^cpq>W1KivBS*h%dfR|E0r1}b_FoL4-!wt)wA(vN zs8O=RzpwQCO>l)gULw28N7L_{b-%77RCb zGKZHjCq<;x(vkELx6OC!FAp<6DC~XK>H>lsb=cV0-#jx7uD!t#7gqVJ=*KIVVV+XV zBbA0KZfcttqz?&I6g@;G9P(ol^g1?Y+?0W9d|(UvBbm|-sORY^9sC^qRH&r&#fqM# zoFSTax)f%a2mFos3wEIjc|{p?g@E-Uk0tL{>E_tVLlrDp@-a9J=6xegRym@Gz}L(6 z4<~;)_ORXu2YRKVcl(rc12acGG1UB>W?xo0RUW`}I*P6rAadCS%mzGr$S(Kacw`(7 z%zC!|!(r%*0F+>Ekj?DdGVN(P6!gWMhR`3qxwI&gzOBoFyn>VG-HGNf?P@zv7h7{sqO6+bj= zy9!AwN)kaV3gZ(4?QY|uw~)F};v=j<9lSd)q3(BAy%JZPw*&1Dt)HmA0Z*EKO2tY! z8NBD<$;g~RbBsKNkA&miu-gZB0}r5*R!)1R&`&X{nYF3}d8tM`O|}nLS339Q-fj^J z>2Vg3@X2H1ZH3XIGCn^;yYVz+w?N`U-xaV@40`!P2y#c5_mVjXJ_2X8}8b$j^MfeIvy+y@oL4R$k0;y@Ha#jwH=-3Aa{ zR2;ya_g8~k5>6P8^X|Ti1?k845HalPxTj{`3Zc~7m}qz#M?3|Y^q_2b5sY)8I$5X% z*KmLN4yU1=s^}lprk>*~rsmDsvtTqS9&=H1A5@znf6T@^q)l-Zr`5LZle>GUy+Q-C zhKGudkpD{o(l`joHcp@)YpH7#++MIU%-F&=3D3ubWo7ZR3IOW zJD@-owC$zt%lTs*f~ohr1JTZT%G@;z`b&J>kZrG1d*jGp>Xj3bt7Iw<4=^R}<=|)A zHeOS^b={r4fOxzM+XV;rsb^Vdgxw<%I?cF=#*S%Ivkjh5R_jB5td&(9WYvhDj&nk& zUhrUvQw-$ofg4PP)&&cm-RVj&P0{=i2%2oviwimIhZ@TFsB{uQ;vsFQCq?JWZ^>3a zm?J>3ltU6jFS&Ag@2?_->pM50)2~y6LwH z5hNfy$kUTo2%OSk9ud9;6S|2nK&&tAD@29#Mix83omtbx`;J3p2$Zh)%Q1wJ=LM=X z(Y+TEYwg!to{A**R>vU#F?Rlu?G!wzpZLrFPA&KRi;Or$XECf>DS>%NV%B4mTx(7a9m>#4a>}N?ouD5~B8bk)#X_%j9D{>2jqFDx_ zL(@La+*Q9!e1qT64$m1$5TjGF8zX%=a7T%oHrG$I`+N_&p5dRHa}QCr~4W^2QVxy1EuR(SSI_^0Vc`8te==kw16y396OxqLC&4Q77CST84@oJ8RfT zSLyswdkfS}ox1aEJNpuIWUG#87vcH8Pez~m(HGX;C7)Cm(MdRaK-CW&z`qK~GgFCd zw$@c}=SN>f(j}W9MEcBjoA_+v6pfe6r(ewwUBt%&PzU>#E&uwKsqzjvE!-{uTk~tY zLlEpvuo=-|O>wK?#w)YV=uM8oEY4%2wn;uU;rNlK**%iWt#T zq>bt!jhflC6_YPA;TL zD11=x04B7LD~8@$WSOE6$$s*hmdEPagy)0wnJjXP{NjGNt~vXfnbk{$+lZZ+S_l8q zt)H|Fqnx?3eb1vH>@}@hNPGHHfe>mUc|hqn@n%lOPZu$}0LSsumrYwmy8TUqiZK1v zr(|6c8`q_KXhXKabo%9r*>?=k(o#40a>X_}VD_ih%-+lD8sMcONK1g~HaB7EOGS<+ z?*9}Fi*DP(B@g6)Meh&F74X}kpbf(|ak|k~m*!s^tJv=Z{E{g|T3rR*g!j=@kV~_F z-`>GHA|{?1W%Kj*0~0&u}unGpC@b=~f$uX^oRP zeXCN!G*>0cY)qGJ1T_&SaIEBlIiPZxhGC{MPv+4R3kMz&q)Q=-28Lq`=| z5fuSa&wqBSt5h<_Hp(A*zt3)4SK}Ve64<#-b78htfWhusx_9P`33Z8(e@_CONy`m06=6_s&#XxCS z^OpJQf&DONq9+1HI7gT?x&z+OepDkR%;nR`Lg6F*TJ{C~W`QQDn$}gk6B42wsys?* z-kn1(i>ljj!pBDHEaG^`IBhh_ZV+j!I~bJr9e=YO`Lu)&b{L`Sii`cJptE+4wqp}g zh$}x|D`Y=BTsiUBHK=I><^?9vjM=`o2Z!f}ODOo*Cfl0Dwb8xXbPW>U;Y~e?;9C@& z`1~*plzJ#W_UWp7)>EYU=KO4H)kTxuhi#dszE^6kK5p$v7=$evS^%EyaLm-N2IBL< z6l>?u0cCHEegLuqV5B8YGUOvfZ`^}l&(hgLDfqK6A?Jj;iK6Y{#v=5la4dP_XL`|q zf^3likZ}u%YO}{TK&Xjcr0!J#IYRSJ;dZ(}R$>0btE+IFdP@X5i~gwALJdQGGA3wW{2Sa1Jg)&ebqN=0_)Ez$k_&=x1hccv_fE(@62}Ywe+U zbgkx?TFw3YK}Oii@ZmULcXT1zsUw}=pZeb}*q5=m^c2Dk za#NsH!b^oz_L%bR3GUjO5X4>k^KJcw@k1zBdE>P2|IS`AzGHrka?zP0+^0)d`az@V zg*VWyF&zC=h2qz;hM_7z6*k4qUUIZYMZ4F3N4S93(zi>9$gfYM>1`0VdZ9u~p;%t4 z**Yu_UuOqt$x#mIor7s6mwRXwnj}o1&;8M4vtRQ&3!%Tv;2Fd)=UCoOZnI?jEK$Jt zCL8%h#dV@nXc9zq?IJUJ$C|cd{)AW0yQwG^hN+7a{%R`ARnM5d14p`uvc3_=d3zZ( z*a%%)x2x| zu;(D=rNG8ZdYvxBw1$4-1YOf!p?10JRpbNnJvC{LZyfkIQhkPUB|8--3_3F_@VH9Sg~?S zxPOMa;?L<79D0NfiicW6Z-jn}q;HsmOR6Er@<6}4o*+0dx0hDJajZn-@-JwCiUl*3 zx9I7RU4gvwEx^oGT}^sl`v`L1KF()Rn0e6&XA(y(+&enG75;g>xN{@Ed9M7fCF|=d( z%Ary{l3@FJo7wCDPNJjnebG^Kew9g%GihD%dZIfNJ%;vP=>iK{{X><-vkfmOGKY<+ zg=u1LKpuq;tUIRaPj!Rl3pM(4j@?i<)g4^^Xh7wD9WhH#Ar z5VK|2YyZ@~xtUod^5~hmIO_o5`V;ZT>HVCfb1qD*&<}wW+Rv0{<$Ft^2T%MU8H&v0 z&o?v4sziOxUUYsi?IYs{z(e#!?^PT(d}M})oRHuFbWVNZhYg`~vsp$9-_`7P0Rr2)it*7q;6D`G4WI8ps z&FtO4gjT#ykEvJQaLl4{9|~qNdmS{{ng5T@C{L~#J?6`%I#fACH26dXoV<)`e&}Sr zSqu4Q_kT|j35pz;2H}&bF)(3kbsnq~8qv>%#gIQHmi0Y-_w|9QY^)%Nd=1YQq=+Qx zB@hEW{nkA&DV~(uBQY0_{3;XI%{)HrZ4RaZwkNYknUD8D^3>oIrQI>A7>Y+%;0r$? zVZohC>f81{$?p$Q;Wu_%2wE3DS!j79Oh?5u(gIhO){exeV)LS0@D)52`_Tb|7{kL9 z5*)k}P;D+G_BUz!iVa5)%@WZQd(B0kK6E3Uh-bbxl1ZCw=?UJr02%2V=ovuotk~=; zDDMQ90%Vu=9pro&M$11g+ao68%RgsEK$b@Myy{@2YER}9o)6_e$nM&6h@8xvd<3%= zX6rNzbjyK?5${(Z=z0Cjrw01)X<)GfZ(kA}APBbna{IjiQ9Ahc8+%k@K&*K#^B@j9 z`jPS1!R;>a?hSA^Qd|OQm|?QmkiDe&GGP8xF^cy}Jr(Vzz~)93boHA(zI~a;75>4xUuaHm)2Q zDpp&M3=rh@8;uRy75_4=Y#Fn$e)Jhco7GC(-(gA+xi>`qQE#8XFvL!uL`x?lNAn}4 z*EA->nWV@icO7&1{eAlRrh;xIl{?;mq*B*OC6!^^&>C6SJk#dK>bT2>RuZ}Ant57fFjRPerjfEcZt1;^38YfY27I`# z@}dfqooQUn3Zq>=0w{jziIrh)9#&dKBzZ>oZxa!f4M*seKiC}3g_j4x+Ygdgk+4Wa zRga8@O4IO(EY+!Na%LIV-RAh^i8?6lM7F;ve5o}Vv@n9gXywitWW^aC%7h4GG3ye1 z5FPXBJ1P6!-pL^&k3fg$TQ7Fh_s0~@k_>}5xwu|_d9dqUe1EQpYPTl)AXkM=uKDDf z%PwR=)1X331#0^1LndI}>6t|>i@9EQj(f-Wf+FV~Je|eFy~GNA1rxR+gArYg{MCi5 zdyVTq+1FFQ++dUVHoaC9IX@lI!sZc(T*#Kx!MjUPW*%b~E zV0FdF-}r#N=?mEm?Z#2g{&24jJjR|yGwrYNIrSWP7smugZA5f2qV{2_%;40xtMbI( zZ$5w;mgMA{gH0fDOd@^n(Zt?SJM$PYUoW+Bj~c~c>*G-E*8vp@soIM2QSDx+Vua_v zyEG;X`cZoYTC0P!ur;pXq%PqvI&Yp;3Qt17j?74-GT)#cj`p1<(9N{hJdLQ3V!OB_K{o57|FTK zhx=kgu?>Fqr^Hw6`{xh1Cq#HZc4^v|w`;6&v}okND^^H92pR+$m!{#YnNOcHMw^q;ZYh<8-^K1BcQ zK_Vw=_Ha5$H(VNMZWH)@jbOH-Zh}p{08{Up2+klyiIRnsalP%xhk$jYeSZ{>zv^1V z|E@&8J_lmYflY(#b4B|5SQO!wRhMo4_XA5oP}rI%_=P1dPqVE>H`zB2?BW&38-~HY zwWTV0^G{c`*wDTIP>(w*)~pca%{VL!1vGtzwPx1eVrdF@rc6+O4IC+SN+=K{BB0xu|n26{IAndFioE34^~}$Ri#bks^XR? zy?E!=nND>-8ux8V6}?h%6MjD9qhkfBkx|)7|KiGI6c5IoJC?!N%7wp?cIlPiDZ>k< zSSILhgEZcL^>IS{HmUfxt8dO6!{psJQ zOk5hQ5DeX;EA*4`q->#J>P0S`S~24(b8uoNpKbMaI{2JI*d8{NG$s@-4f!@jr({Iq z%x?NbG02|1f~4iswOyGLWLE<%KIa7b6JPAwZ$(6pb;FUqS^kNhStoL)^!6JF(zl8g zX!@|O#;x^Bw*POS%tyl0=qBijptDD18)=L%Gg%!+Ft}y%%RAtQN>f4V7I`?YB5PIn)tC{Q6T~TvF zanc=7u!m2#Ol{x~rAwtpPNGDIAbq{d0Db|{82XkyQF8%eygW8KVED|HWRSmG8J&u3 znAn051{Z1CVoCb?Q_B%d@;K`-7v{nVgOy$ejFcZD1v_JwAWy{`^G?FwFS9u}48N7l zE}^`QG|bN5K|gmsFJ&P1f=dROuPT4S2lg$Q_GqjRuSNL`rN4W#v9ClOowpre^XUrH zyZ9)@?{@p5)rYaAS!YVV<7`(P!t>2-#7!%2MI89*jgm#RfcsJ=g>ra2)=wvK%4rAx z@6MxNg?Bv`u1!5N{Ro-{*H^IuSX@_nhCjSne%={e6QNUQ)7x7x?NmJY|I!=*9utDd zJwqNAI(X&dI}RwDgZ;ihvh3aX!GEUc)KZ1DASThJykS4}+%A#w*zmvu6({hajQqMb4pt5n^{=2EXKJc6M_Q4BYTCb`u#{d z>EA4JVlAQ1%d-l)ZG6KIxe5*n6w*UFrwk7Yf@H$uJF@#FkSu*3-6}-6yj?qDj&%3neAA-I5S7={V(*cnDQQCCrwu}e=UyWeeD&l^7-1-V}ZXu8gzz+i6g z{&&<}T1{=Q7u{lWLca7#CFoLsWQoUh6U(6Wdz<6BkAFNl{2athVQ$Z0F7$ffPQrMz z1Dwe<)9%d`wzrcZb^gIVsr^pd-Za9rxs<`N7N>2m3~(Rl@*X)1zB0hinXp&18hz`hQfDXBicE^3dmKSf_i(cR_gIPd z0Ho5;3Wck^kzG%E)||M_O141y{l?zyQ%wAs5L!rI(7_4?)<+7@s0Taa`FI+MB}6}d zomESfi$x1_$^L(SKD#N8xoTN5{A??%6{SW(#$C8?R?J8)@(}UU|JaPPeb3**ptOFE zPo@SD6^X^p@cJV-MFP`4IAiptr$T3aoi@cOqpo$8*7&$MR#qB1=6t_I} zqKizvPkT6hk2Qw4Aw-8Z@?=fgsL*C$eWW^AP~YxwKP!>Pjtd$mc3{b4qc@7vu!GZs z2M1@V$F#<*w@{S9=J0)#_+B|#S@V5q008+JH&&x{dkp1A%SZH zCO)0)X*nUp5G4vxInAFqS(r3buqW27U+?NVJH#|0Z1^N7h@3PH~I%EaF%MFdc@o0DQ#_`8vM$!OMa|)(q>W)3| z3M$bzrdNiGB`=`ED^@+2=9mvAq9{Q9gwKN+uN)^Dvwz8L-JsPM^iYYJ1yfYpDhxhU z;&Ln8uQ^_En{ov`m#yI=k}+)WW^#eCInZZkiyLtv#C6t3Ri+acclJ$v`n~-J?w{^!^ACbyj+OkjW3uS{kB57BS_<%s#6T432z4esEIg6@0^QZM zPDCaU-irYSe{KE%6;?K-y96Km-(TFgfuGvXXT*^ac~r@i*=%9g&3>z=g_eVnO$TPT zzLoNw1blC2;&~pX9d^R(?iyK6KVE}^pJ?))94j7ZWP zt4R1dAjy}}Ou8iMsUD0(eEG+&A9Owq#d(aRt^(QzTuXpyI-f?<%8o|zbtrE%dPZ2~ z@x(69M4Ho&B=%Ik>l?loTG{?js$`loz~ygyE3s0$iXe^NJzm$SltH(8o4J|gHBO(iLai#wrQRR4K{!r+KNW7u)V9ancR-ogM_7{@x0 zxlA7pIyV?E>SH!swEeH*M87wFBZ?wc+9#|Ry2=)YnpfdTKqF#Wy+l1@UUY4|D%wLt zTnA!1U? zVvsxJQWGa@AMSwi6o?T#{s5vZ7ie5f#NGn!i^EUKBl=WMGAvvdijJ=RBth$02l!9N z5g~H@^Mw6N`KM@S5rcYumr~KBj~65H!@i+Uez>G{=szbJgdVa9UnP)m1781GKG26o zpc}$n@4%y@NU}lL&47M`c02zH@Q-Xpoza?!GR&k(b%K^*25u#&it*C;tff1!iZq>K z;j_(*z!y%9pdVBKgPWA*i(f)Gat9n_NZwjppgpCf49b`dG0F1xSo%>{Gx5(n#z?JW*K*BoF50c zz}Z@ND-cM0QFI@5?zi<)bT6;ZW7kUVfpW9Hs6j?Vw*G@k=JNchLczAVv(B4Gyc* zj|HdL4T>vU6vsRj<}N)r*H%0CHR!VV9S8!m?RstEK}ope;Zzw7VDo{_;vM}uAC~G z{E9cy&O=b-Mtt^wW`J<9tzCHw9NmLB+)n1@cjXF+dAa__t=_DLJg=YfMQUH=L%#IyH8V^th#&LuGFc7qj2S5tPfe!u$W{Qr?wNS`UKiOOAkDwi_J4lgt(S;0KVOj^n|4cpJ_)aibWEzYI+U z*?(<`T$)F<`6ZjLUj%hFzKwJX=Dd!2AjX8CXPS56e4mL;ad1o-z?>g~2F2Mks4a;^ z`U1uA4etem4h?hluzr*fz0cjH5*a#SZqVS1!#S4z<%aapt3 zj$MoL2B*Zr1EF!}+;-ss$y@tkNSbAEXlK1NPj`{qMVn7Q<{mydDQ)-3o0m`{@Y2JD{fEo^TN0B)OFs1S`QZ>r!rZMC`a677G5;FT zAy6IwQ2;pP1|VzGqh|2v8wvgekP!gSdmSBCjp&*2*HeRm>eQ(;Ec$dec`Jb_tpPyY6Yt33=8sL7=?FG%l!c5@Bf==0hiCL+2_RcF%g z$Vs5L_&c5W$w;4weQs@qAvp?xe{)cY^E0kfSG;aHB`uHv{3lxkwaD zf0%>BM%fPWEFqSnT&If}^zs}OX_>g3fPE*RnnD9Wm|=6k6R8l-&c-{y@hbg)t0w=j zs*=||ehcoYp0iJ%#vVhnfR#A4u#$LFKHt z^}MSPZtSv+9Nfx~>+{GJ3wQY?;jQt;(K{LmdcKPypi?#sR1@FiBUQ#2mop)HpRUJNS09N6Vf9m>Szem)W{H^m zwZpOo@4#SXV%fY>PSoHW)Jx?R zcQF0S&F-{`+@0zqio;8V=ZDl$q5If^)o>HtuHG5%v=h{;_PZPP{aFek4*R!poCxlM$Gl?Nu(X)wwR)5sGn~PJ8zR^>#a~ytk+Po(}nw?|Z zlgVw;I{${@UyhT_YoQYTov$uKwYF~v^ol0R8-^XLBz8y3Agr&ppNUdbk0&Vc9Y(Tq zmFhb$5Z^GQpvQGhi__|Uf7Z(30HAjd-D~_20TP4goKRe4Q_z&lUdG&d1u53iy)eDr z?+(~JS?i%S`=DTKyJ@wgoH7jR^(+v2?4?|`CTpM8eW&mp7z(GDI&Govz#z#f)cg#= zLnMxe3jjeRDg3FOZxML}da_l@58K;t+nT-UQzOUBo{jBV9Egw1tYOjhP3keIGd#ID z6!*x`+kNbuGoaKBrdse2N(a(#7ey5X zNBHLyJL#EL5;f4OXCm9<)US$ zb&V70`SsIk5CI_mkhI}jDtSnTYCo=fKKfR@nq{h zjs0s%qTsMY%rApjW+B}EgQ=HE5bXzXlb)L$B>DJWQ~k*1@n?3@#PzWKrM) z^y}Uh2}?%!@;$1dnpMS~NoYT4w&SA_@s@v)db?CcCTxB2!zEKKRc&oF%bmzYBK=om zyN;Vb-+Ig$nSCVwTjNY*WLJQ=Orw@3uKJz?OL~w#Q-g@J%;#S%nzxZh-|PIVMZ{Q5 zK;s8{-$rMM&dIUOoo_t0gsO-9nZJ;8Xxbew%MkJmnzmQ|xgr|w?Svj?tm} zuoBa2P>dL{vG`_jK_XW%wY?OgMb5~)^y5;@Qdzp609W>82<5pJF^1Tl_qg z=PBoiX8S^=CNH1=Sic>0CaMYM*h~)k&$S*W$%wR}+3u6Yto4lL;nUyG7E~b+m z;~XAELY_11`@vF>f+Qr0sB#FO(5CPF=Knf;hG}-K^RqztOWV^tSA&P3+1lgo5?x8) zG7OQtdRF;tc)40k} z^ma@RqMvN`hg^zNSNWOsm2)IAe*XI~KdXYCS0O}RZRO+0#8yFbFQ%S7S#m9lSG*+T zKBQT1c>jdSDT}-pFl%cec78OmLZ+JdH;_1L{~a(b?PpkO;%Fs0?T10zn?q~ij(x}} zh$fOa+VP_R5tVoB52Er?EgTrX7jb*hY$UnbNM1Q4W{CeNJn5esv3Wa(pO4e&3uH~3 zK@<#5knu0&!)+9CQ<5B`6KqWQn}heKJhHWa7c=5)1nVc_nHZDGuCwVOKWYDP;;{Pm zeAZE!`PG_IsY%6NhKhl_i=#23xcIh~JM@{DDA7km4)U^l90PB#`U5jqt}$&the!}D zYK`B8f@b9r@VKB#1eo&W395v0{#9F|QlkbGo`yms9Ys3Jh`xmZ5c``q=t*BHTnUM# z+VWtYiRw!PbHM2s-`?EG%X!WAtuq^A>@AGi@PsJNF+wSV76>ZHeV`DyVaG!@t13QU zB|$v*>li01CyK^Da)L3NkxsIfe#+a6MxKX`CR^6VZ)>3rABFGl6v5lomo(DX%tk4~ zi+qfN>yyh+Nxe7YU7MCn?xK_3_xtkef$Cc+Ki|?J^83cOklzIle?NT^bFx&~VK}Hz z$V3vSS7_cuvgqqFMWG~4(zYL(S#U2G3hHp+GSQOy03fd?@14y|*eX4s z&3jJ-sz5zI!L)!>QMfXHf4v<1N7yv$BrKb!6>TdB)|eeG({ke@#vpRnylXU;xGlXQ zz$;Z3b`ZR-oQ1y)`g+S%!soHtAv)$asC2sr!z#9P{_bva$sHuMk_%5UKSetfW4?PN zZ}*bH-E56kg){?TQapieNdIT>1qE1cO~OaV~3ZQ zL7%tCef-%u%*K&N1|$vd-~IubW5Lu-kSwuD*nIRgTpC-#pIy12YPAFtf-Ym2g4ICD z`CcN5N76%lPMnyAac+M_4Kwy%)$qPlWBA(*3~UR}c2T8tU=r zXLP4rA1^)z_wD?j-`_xT{kx!M)%jwLbb9n0k(S zAaIdhRM&`Z97o$FyhZto8>OymT1@tl_^n6~bsX#sHz)OB38voT(wsW;s+1I?oQ!76 zXw8e%gCdApohnQqZ}{?SR`Moo6k=2Tm9F^;J*_u*UID@miUCJuzmd-8#P(0;mixKq-PA*OMuN#d^vB^ev#Ihzc$p*K(anUonx&XE;t8;OPa=1hrl#-sY$nfJf)-j??SGzABJ;s1-Y*C zGGDme0^_uT#on3^-O>G5kBTh8;w$VeF`t`kTi+6=1F57eF{uMjQFqmu*h5U z_;>G`)ngnwq7q-LK-;+A%$N_jm%js&Dp)2q~Kg z5I|p$o0aMa@8H65kH;RfV^3fpAn(tkr;s?5Pd6;xmkc`|%Ppii!Vhx(8_A6`?0xzB z>x|#7+$!4KAZpT@g?7;M+1`CNX&;y&p?@eIto9+M!7kz;MA|BQpId);ce;?x z0*dgwaGaBC#*s2IZsMEOpM(HGj3@5H(ja#z&jhAUh7NAQUEQmg^EH0Ugnaape}f-z zcr2UAJ4knE^$x!Z9){`Xz%sd)<_SY&V*Y?!V9DUKhJtR3T^5|pgA?MLo+KjeaNHB* zWlYDWN)mMpX|D!uE!D{Dmx%9E3=w2OHfHppCcrmbRH?kbFQVBZPA$b3FWQOJ^ZkWN z?e3rP^M%=M{q2!KCpGi8BK4F1%(=uNV)px|Qqr{Pf@U-rUp7q_ys~tA2xHE{<mGUs7Mk_J~qGq(t|q3<%)|IYZ($ z|8h9->x+T1#BG&0l-wbS+uQ|bv>Gkhf1Sk$%t+jp0e@YmKj_3DXmAgfXdUij-(m6C zRrP08CB*f(Ixult;qa2x42n#9uwq+?<`M(1BTN&w)%w8eg&w*o4wA~hAKO%jI@jJqtgyO0UDfsZGf`d#FLgM- zY_C;F-wF}yl1bdQIb(CsqK%2$T+Jl=ZDV`i=!aLl^9T8{5h1>q{Z&c|D9IfiBo6^e zJmW~*wWo?M8y@JT`D`(1s-)ZNr~@ld{&2c)5Qt;?EG&j=pySKP zUx<#3XI^QKiR{8t)07V>AS0!3K>k9zO^ZZbf+%Ddeu8vxS1r60fpWaJ&#VmN0)PpW z#|<8n0n69lK>9{eI1LwbqCd8#*S z({eCC>T48rqATQ5-}hubQYo_lO3z7$4saNdI9u25W{YlUq4K@kv;BqB%<5EVrc6h#pfMNkmL@OHH=%(uMn-nV{hz4hKdN>|kh zb<Umc-)M0F$RSB-gDbLRy=2*Wu z@i2t}u#R$Tf@5e!$Xh6&+RSq->x00^^&ZHu^l{zqs0{51n|q!aYx>zbZbcFrwRSM< zv{B#-PZ>OLjjiPsT3@%h5`=yqOYzzD;s0dV_~E=d>A$W-hN?( z2nD$hACT90eZ0w?I=J3~p*~4JPVo8!KdBIQBU_ z2v?oSyA@e*_aL|oFzT=+8fPJ_m>Hj)8!dqpd zwF`6fWITCHLAU=Z6BGzNOM%A&j!%73f+2c23!=#z;$x^8{-@}jOxqD z-vCLqKxWn_(l)IZd=eQ0_-~RgyroH<8vYrpMLsygX&$*`AEHJ+^!~sj=rcS0cPfy# z?}0>L!^!Eys9QpE^~ZEZ_;QY~uz|1SM^F9Nf*T8p7+|8lzKfptEE0si9_RvbH^66` zc3cnru{%!+!+O5%Nv6g+YJB*OL`=h%(I~MomfYe8mclWIi=*gP3h$)y-^%wwMH7k* z>(t2DmwILuEr{zeaZ;&4fTEmceYkwB%e##K&)zV=gFx=(6G0qOB#tF3n7%ghP@e)3 zx)YI#?RJlvL0(fz=S{9*&2QWte3IsuXLpV@t?GyI{P`MHLgmw)_-Q4@4H>|Ju5jY{ z`(^Ob4w^c)oV9GOKZ`wN0z#rlew1*rZU-#rnONpT~`5-wwT15H!fxE^97&r7Np-LoHL*EaLf+nQt%b0d5 z*X8RhSfVqZ+4YTs%LnGa>HpC39NLBRhUt4jV6Q~QSx+)Dci0)xAtQe7Fye*sYkTHp z&Zi5Vl$Q#fuqGV)T2*&0AB`~=3?14Xg!+c9^chk2-$==BTeOD*X61y(C0SMTiYW}d z2^0$-B?B`0eFKyYw|c3#Ozk5WSEDDnj?N6Vp1|_)bn-%bmrPODCoYt=N44Z#Y4 zw4b8?Dw;wQPdZx?bqlBRUALyWb4Ad(WHtBo46iVPcd{s<8gLydeqOGUm)*f z&-j3>#a~DEkBU{yiYvCx2O!7#wRI{y*KhpYcPf2zl<1BpxVvD;@Z)|Jz1?+v_;KS4 zdBMD*?=ITWKK$pSnx`xE8Ko%%*Y5232f%Gk76@tn)E+tJR} zaeI3{hqajFP-u(>O_m%PxNcC z67AE0cBBsn;cODfSjEuegU!6)kT19_WcLP~y@}_rQafjq6QIujt1P69FB}mGLq7xb2K5h{oy4ZC4@PX=7(K_h@l54BGfF;ft!Cs z3XQ0Lm5Y)}$;@l^sHywwIpe=nADoZooE^-cT;hy2Uh3dfXL3zO-6Mh}K}ax2He#4_ zk-|rt^vZDXWA62B7~{!qd0GeERC~2PLvW2n$#O^TSZ{UqJ6Pa<94^Fw|GK^w@90hB z3>oa-``vF7c(BC$Qz)k*obz*%)nW2_hl%=VwjvZd8VW}f>0xx=B@mJCqT)H(BPyt& z?^%cPH$;oI)*b44mE`&?(kBjX^DSZz0B%t~Ef1Ws{pC_|YA5Uuhm=atb6JG(g_*rp za_N#LcYRgLDBc2^KbJ)J^QD`11P!_}td1nmesFt;CbF`jY~h(XeiSZwoyJ)a7(yE~ zD1<>k@f#iX7i*d3Iv`snA>~2~26{1Ujx91azq9<;(&s}j4$0T3U#hX~hE%&z<4dlU zxVdR&UhCSz{wq{&B1KJrVW%0$#ud9n<|aC>a2ic~tb1)$y+2{&|su zk8SBpw*)r8ZgBWyGF9u&u+3U|?xafhgGSm&4VDz_w++)WN>l@FWT*Z$rsx}#!}-Am zPE|-0evgW-h1|ywuO2{A)uv<&mC*C+l}5_i=9g&RISAz^L)I)R?*TlqbNfM)@-!@# zxUt9+F5_7?*(2HmJ3O?btU~Kx&-a5z?H4T@8_N^#S6Da%SX|2gQq-;E;JabZ#FeHO zHe888Q>)&6wFtFS%V~XA==-{VkQSLZmT8%K;~u1=FG*qQX(+EA>_%?<<*$LZ&7jd6 z`*Hy0j$MsDKw-`F%Rs?8tyDF;b|0W0f^8OW{4WIxzA_pcy;KOU$cGkvyy?RZU+x+j z#b=}56|BBM&o}`A#GB?TBd|G%y{7Uo2DnIr+tO>h`%meGdLa)rKQX&NU6O9J zZ&9=QF{Xn56{630F3Hs-OzZkDQ2msDl6EzHkCuZ-XAn0VOT^XAaA@X5q`SFoq@OT2 zWY+SwSlF|WZe6xVf*vqtdDnZ-Rm^5)el1A&rJ$Y|0)dL|$4tx2$+({3=b@DUk*_LZ z4=;U3uV!N`NBFONN60$YHj`d#Ov5o#4Eh`rxSfmh<2Kijebj|1aW1&yFwGfBrkeC< z)HJV+2V=JnKo4)2b9_R*y*FQCTFht!u{)NjT{TvEV+-|e&v51W4<&=*q1_w9C3z^Y zM}$q5j?rV#SSd;m_8NY~xh zskQJk?BxY_hjc6p-mc%8j3#fd@zPN^0Nl^A!=Tf+p7OPMZ1QqPki%n(!sWe2l_GzM z`-)F1yddT|q_AV1Lxz*H_OZ!3iE+sAlWW#})b{vI`q2k&4)a2pVn4&XBdX24B_ufq zb`Km$!VC^C!gv2+ksZyQEjUsg=sY_289I4U){&*)Hc$C?#>fMwpw#+p0ANI-ZxN(9 z(tB-B^~f&7h{U(_X&vRWd}^;i;NK778{e$R=so(+htBbWF#rJFO^&vo_^hVJ$w^u% zdsVZ=S6*O_-|X|wy@tCdfAWNVrU%y2^2`OsCv4U7q=DM3u|}z8%Z-#9 zQfQ%FMJ$x*XwtvPc)c>+LJys{xOu;5|KK>z4sSXe26N^bj1Ye(v{2HQ4R3u+$|52apFNM})Fu7g*dNb_arXzVfv!odA z&$v|xk~>uU44o4~jT|lX$^ZRN26 z&7u36Lb;gc@xoRo{tz#Dfy_G>)LB)u{TgK0&`vUU!wuf(gHSh1bcXMA49Tqgbw$)+ zb}ryfCHKjB=+20k@?WZ7SKke8ic35)xDoP=6l;zfxi2?vP_RLaURyt{Sf{F&gLwF6 z^!;=A)NYFkNV@tBlhEEy$b^w15FHgF-Uuai=nVmyHm+=pKG(xF#e<)P9D>WfbD`dB zCyNe(}XykY}+?&5*9S;OlQB@ zJX!t(1h(oXXbA(=UI>vT@TcQvL3nwXY*4RoUN5wn;nJWbP*(y#xKWu0`JD*J;dcz? zKcCZX*rRh!!W!1DbTVC@K3-Mxaf_@&P~wVnfY@(aDQ_f&625JkO%prmafuIyeC`0V zGivibPd z-`S>gO)l+TW0f+>QGBf%Mr_XfqGc1b5?AC|RTcsMPO$Hc2NLxRCe+hf9~}#~7(@kN z%ZS~_MZncxCf6HBteU`IQ1Q>xoA?d?hvhNu61#1 zca)q1+X4Z4sGHOyVa)#>b_tn?a^W-oQeQs-$v+BXlVD}+gfG}7{Y{-aX_d{dkp%pR zTTPY^@9P0mDaJeHDi~fojlo`R;O~ax$ga504KgFwnS7K5SUz1(9jrz!jPMi?o>pW$ zVxj2n8W+_ERP1W)bsSGB0ZY!HXe0=)&5=ep9SjAOKdJn&VIg6aEh3(P;Pm{naDTk$ z_)t+bg!WFFc*y9OehZ69_^Z3EH!C(W>uac6;z#@wAWpY-8y)+H{LzwgljX_iZofD7 z&NZkRtrKn4Z0KvYZ@E8Xxh<~%YI@yG!m&ccrzYQrXE6}A{wyNdr)nLxm%SUb4@ip_ zXg5xcae=IfIYNFlDP$%jzJXj6Svs}pS>9XK;9B}Q?IhC-3{oI|ZIo?_KbYSRQ#S4b zkN%@XK}e!QMas+61W(g&ecvi78$)&UFIXv%ve9%~Ma6DFg{8n@+1q(#lmFyp^SiqCIKV96At35Px=f@ErLx+&B$DqnQrfAkbE`?)v`;pYnGy8 zHOtPt_#fGfwnh4_jHe=ep`K|v#l~wqn9hUXUw374kysoP(G=C>fH64KgPqP5(Bt;- zOo`w=JIVggT~nnzX8=@Vbo*YrSm>WL<1(>J(VAfyVagtLZiANJwA1DAc^|kyy+r#t z;awi~9FIrHvCtyeJpY12+F$k#Zl>~#UUIx$W?N4w?_iYW^8scBs&4v9%D1?ymu4!a zGgI4mQg4Y`M>wqDC!^J_V1Te`k<~dca@Ln0({noxWhQY)V6x!+2x9e=mybtj3U@vE z7!#DB&A-PRYA{#6cg;PmgaMuGti7iDos<%-(VO*1pBi4%;%dF=ZexG!jlq9rX~hsZ zf=mq(Ym^AkNp}+Iw=gO#fX*Ly-ndrCijJG!zk-38WxYOI$nqSjretVDZz8e0v6c(G zt(A#?0-jc9>L%5DjrhXi&XKD(3@|H}AX3>_nlbVP|H@x!#^tZzuLE>4-_EKNFXyn~ zGT*GbLv9SH!eJ1s)&dS}y31*h^c42|%|;chIjUJPH3#=h*RxB)XQ~PhkRNeH3s}#t zHdVUOPD6-v=J$^&zSNy6z{mjB;KL<#?v(EVk~01@S>tWZsuU)OT%bYhjDWN$mmmMa zF|%@wImd?;B2?O$R-kB1to;KZdjeY6!}@QIfy*bUUO4Sa=MmgB8F#&~wnCFp zzugQcBuRsErCD$goqt$7*Rzw`oK&rUh%C<~VIQ`-G2Y;Gwt- zLKk^B`e8^y|BrnhA&e0TA%nFJ2lICW7BiJ8S(UC9-F+ghq!av1aX7x z)lkoskMyQbXuy-2{oWCCqE&sH>br!#bX6*?0 zns*Hb2QUYA8a{9#dv!VtkP7!TSYTM+bHdj;VBE1y=fF;RqJ0@v((_0KnDJojFO8=- z-=JAnmnsx`j85B<=7*3B`_|tW)M`VQ%=Ux$->@Rw4cYsX@M(MK*T-Iy*k~l_=fNd4 zdp|DU)0nOTlm?`$^r}-rxk!E(ana_F++daPk$tTIkM##M%HDxl&?G|Lw+N6w<*Nlq z^D&Bu{VK+G4;y73Ki{|b>~wIJr4|B#&ulvV6pVdGb$FwKu(+wul|Db32I+eJ9Qj@L z6nk`@DPFx{fzA!@_kZH>J#h{`<-}>;C&u8Z9um{ex~GM6ITBCkyi>zNN6aGKtCBdD z&w#7-8@_bp1=}PK34Lk&hrfdK%937FI+-g>fYG zytI&C?Ey2XU^i&t+5FOBB|>&XUE79USP3rq)E||iBQWzINfzt}b^VAEkcmDCE|8D2 z5^+O$Tyy*{$hUm%rNwuuZ%0K*%?XrG*ClV%F7S&fcR{`8Q}YuwN<;#8>%N=5jKIh& zbst&1X8jx_XWZwSwSKEAV{_Wk6{+4iQG5T3E`y5)xzdmI0&rDh|2J8NuL))MHvGVi zgXVxUk!}zz(0A2Iv+*8YJ$a;dY{qC`n=8Z67WbUQ2<;9K`8`rkKhijO2m;N~6dVFE zLUz&RI*e+lPR-|mXs2SDjelH|5eJtIw7+CjiC|RLr2EjQ&bn0mue?~5zpu95%Ii~q z;Q&N9dx4w&xGI&WBJ;sRVVwq^4GzMmUHLtaGRZmzF!*?1p%ReHMX7jf$q|lazC8G7 zt32l_8vHWW@8ZAJY$N({i*?4@_gGV};O8Un1hUfEf6BX{>ap8L52-Uq5VD|azEG+~ zJ^DM;WS#S6kl94=sw|h>*;KP}^mE>E&3Uq0(~>5|&ZSXG9Jf`K(o|ahNq$e~%aJ%j z!l#zHI^|6yB7?gOKgAx0>VocBWU68^J zg5~hCBFG<)Qpq?EW$pRnIR|{tvF3&HhN8}11bFL{`(tV#LYE%{F7yFyqjO@pGg5PD zA;99y;*Z-%NetwQS?3=Y{5wUrBsL8RI-2)`JlRQ-E5;-(8e@Ck7#8RQ zRYn-%OO__i_+HflGe=o@qML&AMW+tF`3aJBB7{BP_M3~7z3VfajJ~0oO@nC*#0Ap# z145{r!9*gyq@FW46in==wqJv@YaiJ)xWHQ+H5-2w+D=pzw{_6PlQ=7L=LVEFI$8?n z{+!tQeX8TRKd~&1hdX2vRp|pUbT4VR-lIM-c(T}azBSlMm8(k3mXoGl+Rt~BqqOP* zd=!_?cs2Z|Z}(9RbIjIm-j+C{aBHYPbfN(IPf%<4+Jl3!;0%vCX{o4#uNF;&{cNPy z#G-2geh0)gLCENJSaB}e`0dz~pF8+^nHrM!`~+7LGP(hekj{8C1SzYr`af#Nq;bv+imoqIO(A0>|o}GCt>I@5@z?C7X>K0EfqS=YEplN`~Z18B1w#9 z?z&W-u;>}9LVI-TF`BEw{ZCivN<0=)gVVSisY%;~F>2~%8XY4Fdf0jQN0FgsQ}R%w ziQ1?6%85_0vg01Gdi-TxkiHW4r*9a|l+f3-1y}mZF@4Wb1+yMnhHu2_s_Ik){?cp4Zl?Cz0J;0*jPY{?hEZSAV=$&qtEv?m{IG*6Mt-*N zQnS>m?1aX3*U@^sUlRRu&AdbX@c?Q1!q`pS8_C{K@U$KIp3koK9PR85UhB+ANHg5= zLKUGltx`N-5KI59sgqu;#9FWG>Z32#1nN1@Quk zssbMmqq+;EK05CGhejTul;3mX;KrbEQwX;A898cV@69~h}@B!$Q?St;4nP3Icb9=Kz01_zj%s721T^( zC(QX9);(Wp4`>W;&{RsjOnvbEv3o$0Mz5Ttv8^o;8Z=pP)&tUZa7DF_A9j&G4(a$z zyB#ZFrfA-^-BM)>%Tyn8f{7#y(*1s|r8_8GfZ27>YUX+R09@a)-!#m?-Q;3#cauxA zOJ@Z3_kycRA;H_}N8tG=jPrJex6?HX z%{>Ru!URRn#W}OEQ@I=Xu^|*IHilsM6B>TgwcCyIC*2u;4aGV1tJ+}rv@#xsBRfOb zbD&qcfSX?K)>?#sO+55#^d;4x`x@Inx2roRE1Z~#nMN1+k6E<-r^SWa1k}eU(0MLt z*O2<_$U~1qdpYp)t^lp;y(-BFLCEA)Z@x6w^x_G)vhw~gGxfZ&Hz86VtNUYjmn>2& zVCGG!;;~WRBv$RXL!rQO8%hpb_klf&J;1V2qY!1YFRQ&Da79-47|fKW;2LW`ccL@# zrFz_Tlnbwb;A>#A>0lcMyP^f}!)hI6?2g5g|9Z@s;yLG$i(ng{0C)nyFt{xpt2+du z<67x!=M?j(Ry`yTiITA6hf9I?+5c^zg$C5s)m;q zCi`*kDIHEQt!YMuYDu4yOt9QbQ2w3CeW7nch6jC7M& zF!i5d-y&RJb_2Z(H_(sem7BM#I!)8ta~q0LbKO|pb|=5_8(G^S z2Lt$btWJ_(uTB9Y#-Br1DM`2uosp=A3r+H!cy;=jai7U#>RU2X)rh#jchYP_I6NS;gOm*qlM%s114 zyd5OWALK6*QK!UzUF_MFZeYaq7*(fy<wM!|V4mY3oC?hi)^C19;NpO8b7mu4F?6s2avrIy%#SbfISAfE zCu0k8LZ1G1LT;vB?wfhk4OnWmZG|oox>1DK9Eihnv=UlxobT($N213lt>6c z(cz3fEyA12So0ERf7?0W5QD_)H>qU6HSis-f#zopIdN=_9DsO|j+ZU1d0{`$2i-xF zx;gd*za3$q-v~A&>74CjcAP3^Hcbt90B#Y02KsrUDNZbK@%kuY5a~~+eG+ycwdISw8-F$o{$Qa+$Gkb{4C?S0_u~{k@D7P?}S$~l&ER$ zyfP$ni#)3ocC~;j>d6n5n8r@4l_N-rxrI^>3mSHIr8KV7_UGzFa4bb_ys8m%9HsuY zhx#C1{*D?I#Y^VzV=uD0j3H{QI8C0My{$BDPwD<5l{h%`9cnLN@1kBisuq_B#xjMi z=yxcy}q{t-ChoC`{EYCRihu6*iW7vY=9cmOZ z`sY#36&Dbs7XG?-G*sv_J^l`&kib?qS`8tsE@Bj?erY*|ik!%UX~xepV^!1ZyFkj0 z!Po>266Px1GAQ5vwZq4YEaV+o_u?DTaa}Nfj=v`5J0`UnP{%J?7AU(W5dPjaU6?QxJ@NAKYDlZ*C}{6Y{{okpGHl78fE&jayj z-VW%#lT57nl*Y4Oqnj4O}1)V*m=0>yb? z&O%&2U$e^j*>F=5mJfAW;al&``DB^Ldt{dENy4Z5>6P4o%Qpaj(85z{{ye|f0T9+A#p{=7ZT?JRVvuD5B`xDy|nFLy3m zbu7)X`to<=^pDQ?RIA8k$oYPy0{z$Sgcc>+(uXnZ)m<|WCprcReY-Z<=Rtw`5@%q4 zzx`8#2Yh{zLz4eLyg}I3o4ra0u`jSa*|u_ilwKYhB9RAs)B!mMvcox{{yj8b(||fE z6pceiF<*Xxid++6aMyf~0lqxftopNF0x|+3&n=34R@3?7)Ee+NKK&XAlYiAo+#)+4 z33qO?G?~*Ade`?;4TY9ZOO7%zgvse13MrsS0_Hq47Qs3}))|=WGB0kL$9fiOp1!(a z9qlsDWyvdnR}{(~1ojG7bD7s>`C0qPV!ZxDTJU?(e7X~tuTifRx0liSuRj7_e`yvA z#jPM4Ww`%6xXddW?6dsxD7Tq=^?#csrwZkN4=mY!g9@(SZbzSBe#<#=M zV7z?Eh?bw&2Rfw|a%7i&eU#1b-V*%BFlMC7y#0gkjk%f!A9pL=aI3lpgA!Si`p=1< z_ziRM1uo-8A@UmdnYqkM>x62(7d#TgR^^ZT7Uv|1g*ZPV?X}Cia{aa;zG(ZoVI+7H z10~MzEC@NCuq@BH*VhSCah56nJjsZ!YP;WqC;ZH~5oT4_^r3X28zNb-3p_drcgN5T zORhT=>d%wCUwx=gMG_=PsP?rbu7GTQM@e<|w05@6K*P;I!=zgHshg6j?Rk6UtJb5btoqe5gAT($78hnHxS2=Rl+ zK6mfINakNJ(J>Octzk(BDCL%(Vi#l&yR3G@h~F^m_-c7OI-~zwCjWdxH8CD9?0JtV z{g%iWqZ?&IPYFh4_`^jJIjMbo?!JMKwYGjPhW)=GINq?V04|GiF)D0pe6OcNbV<51 z6wTX;0mbQ%Ik^W3|A)34ga2ep!nQ_LLh8B!9R(l^j|0DjWbd$de-#S6;Ko?L`DLWW zs9=&=hP4mlJVdArDC7FOp325k1-%6k@U9qz)B-i$0v>sVEd88SrC76rme=d0gJUD_ zo|NCni_pA+mdPQOH5)m5h;IX*DvXb#?s@GE(CdBG1W!yD=$$?#>M7`ao+dtT8)3uh zH5cB(*cg*ilmh0Sa}2wZt4Xh#oE5~-fk7am9;*vJ^m$T`lZDrejh<1eM*hm~#95_N z;p|MWg>;7cV<#_o>;cbye{A71o{nXtw~|oz2+^lzv_2Y)o{o7aemkXCu1I;=%oA#d z2TX8X;cSG};yOk?S>Ig#!6wXLFDgauPl4){$hCWWtMRjKo}+gUs-Va|hbTBbbYvMk z+ZHnD>>p*|()XGtoS6T>Hp~Di1pX`y+bg$a78H~?ALPuD`A zR6adq?ngzM`ao?dlu2$o<4!1pcxoU(I9Prr9d!B1OZv&haAox2fg(!z^Zkg@IyB66 zMUoQa-9BRNodD~EsA?u^m_qUrVNQ>Xo;3mybwy-!%S88fI0!R(zU}`B0hB&gWDkc` zgM$l;aZG_8pJ{#touHxCPM_rE3pA-zL)?T5H7n?wq@Jl`@t|DxpeMX`(SCDrelPa$ zt+(K$EZ&MR8Vf=XLOtWn-^l~Pij007j{zsF?kbH$H5!6gV`1C;z_Bvqm_0T_mkM$r zZT3OrIIAAbHHl}yYv~4*3dayg%gKar`it+CPs-vMejD68DX4?pcEc^LFqg;%!UgM@Mco&Pk`k$TVgr#jQZd8?}ih$#kiZPB z_#N(RY(BBWXUHXZTMB7M!7jZWwUM-Ib^1pLEVE2~e!V1)wq?=#n!RJuaTD}I#hi}@P zUqIVih!6glHsb|emrQSJ!SGTTiKo|38IuiFOYBd(X6#@>2rD1-b&LLo;x}gNVdgb$ zBaMfbePTu==p_on)I4aaSs>ptD8*#SOCz*oNbUGkWizBtK%SN_7f3i{iIZ3H9jT;C z@DRq$`luEUP3xb1f59jUWTzo|?TYtSB*|{SRFk^6F?)@-LrNIn=E5(H<>Y1p{x!pV z)m!yu9;_`Rheu+0el5_Hj*gi7z1N)Cm@W!}nh$W%(gR}ynA)>wTdtOgPo+UU3hfHA z%ixL|=sbbTT5p)XcQT>I&Ug=MN=VY(L0t4lnmSUlUXcD81cm$92LlXMS@k^wK@i;I zfgju-w$C9p>Mf*hJd^|vCbJ*uWVG$4+g>Nu+RY(3`mHxO~d z2hz4GkfAFnadc1XINFa-Ry+OP2KP|B5-)v_1PmYJ4B4yHd$tM?BVKL!>`&In!`<)= z>39M60s!MpF5H2`0|;Y!Va}sbWLn>Fk}lsm_6TqG8MHB?+A&_QF?QPnCttwIbn?cb zR!*2ACO8gc^1#zDVWL~VkRf${vb49NtfIjls>(O`6fa^|8qUexu+4m)>}n-wO5e~~ z2D&k*-jY@h$;Q5BXP3`1h&AB95|9^s;X~w%V?%$r+mRtRR$ott3nKA-BSr7l9`#%J zsU-9&JKW@d9eTTPmo5>ch( z99X-tTbYa@t6#GJd$ZGld3V<;K}z@33ll)czR+;f6G4)-WKxdqb)0tQ#}xCquAnhp zU!f8>lrgoT0J0rJP0ktO^cTqQfj>X~O4=EzGrEN;k#p9KR9l`FJE9z<_F?fcjOhEu zqv>ISiR3r0g7HqI6Vnzy)|MX@%j*rE9|5yXz1e$(i?1(0i^iHdv%;Rcsg(}tjsdmk zu4!L{Hn-p=M|Dp82WR0!jz4tOABEdyvrtUD9FTqfmM%F@)L#hHDwK3h+^M++HLo$M z%f%_d28qraML9Jvp<1TxCHJCOndX1qXoi0OdB;sa&&AxmmozvFAydqR<|eVaaKA`3#CH*Ysd-*de0zD?;b*F z!8?)R*pzUWu)1SYs zReedHLbsPQKXF&f(^Rb$KWgn9bl1G!cz7{QoN7Vz8qqtMbfBC(A^%Oer=V(M8SjoZ zd5v~ZCfjMRSWiESMiz8ZXk4rJoP<_9In(QFsC3lyOcFDSbchke zNDp{6H&zr+WHSWbldxc}kLa5;`bfJ@bCa2lomTCodgrepI~=f5)Zi74s8rm*%l}>! zN-O`@Y)OUMTzIjdX@W3QKa=(TagPd5rJn*}q!*Og9wTQ&*;dC$ibE6mv|{BFQ}Vb%6l%r>oVi!dvMDJ;gpw3O@^*h)k_t!!y)+z?77=0Krig~ zJ-lQJIZvIJ3L|!A4N4c3Ic1Ut_L$6VkRc7SFKP5z?Q(|TEPB(%Yf#D$GNf!&CP|YW z7=clmyODq^)YV&Ffjo8Mn{+fas#(8;a$K&y(QNE`K5oDx+p=4T+UV2h4{_!%N8@&-+y;%+ zWip>$ap-tk`zMq6kE;3aUS0Y-^EY>)!@$|_FQKy?=WquV1FeW({2Dro*gAin{GNb^ z_jovVzTJCc={0yVtA`-ZyVXnA11z$z#KI`Z5sS$_4&e`;Zaz=oTc*98iNT>Sq*w0#9d}Nm#xnARcdc7il49!RAiZS|+ z4+?feB6Drh)Ek6-Dow_(vc|j-=JdW-@v1wjE}EvTc*xxPRzj~{T07M|tpMi8uNt0v zh==}QfeVG^T@;52 z$z4jAVY5ZB;eOA8@h2rDYX)1~7|K@^f?$-7Me@{c$j-RXT{W=>C zJeOl_s6e_938D*6%_nE{8kT>Wetm|}52}CnUVoSBbw;L}Ljqa1CD*AHz!!InL|W5h z@Z6vRd>q?D!JmmB4p*QOT!}nS(P^U)4d)`Pv+R<`ioGAQkhv&0Bu| z?N^QWpm+x;%ss`EN2qvs4o%eU;iOLb#1cD=)WrgNkh@LlqYqh&N5Au8=9Ai@J3%F<+TCMSkXJv zg~5p+tK_|=N%eKOJe#EHl0BmcY6~?{!VaAdbER#HNOMfb{gBD;FpR+KH86(7#YIlQ zMm6dl)k?b74m#T^1je*^fZz=-o%23;=5baWj!zPOLQ`plC`qc?YJhY1lfY@ZQ`v~rEm*alI<<4w;0W-Rg28jHWJF*ft@TSyrH zx&@l&@nEWx(|gXM%iTsr%c)0JF;YRRk-j0GsNvjt6q*!LwX=dDG7)yAB2*HmkFVXX z+`M>^29s3%XTSN9eZ11WK&%?cTx5qcK*Khs9gu;b+wB_$jONHRb2fX1q3=MNW*{;+ zFuE6Y)nTsOQ#V%<p>_sIlQ>d43uap@owT=Yhv@{EZjNcgx2foLU> z5YGsWLf~ZoG*bbG#^ouA7ZU0P)Q=ZA6|5p3GoWFiFrQ_7M};P8%k%LutSdkMUv`8@ z^jQOQ45^@ZBQ@FgUMQF`@7@ZUExnYd{B=Vobea1RysoUp6PsITN(koVnwWj9PATt< z2otB7dclcMXrBH*a@-J)31uX3+YPH)n-{NuG%ah8mhxU*hEIn3|MG%s=8F{9!77h)H@jeU{^w}TP*-;wBP(1>TsR*qBm_zq&;czp%^eTzc9q74Hxu07R>!Y z7b)3(oL{p|wd{nS?3ssp3LMs)|0Q0c!0Ga^@^Lv5wi4V!>Hiio@vpQV|7_c^Tvd;| z@tZT4rQEOcl;cOrn#vTEix7)@7J8m_D7u1{c0N_7pFP6&gwou_mD?TCmnqlJUGZe) zyU7y9^zfGJYj4`cZ{R^q;U-Vc0g5vhw6wyzS{P7dn>d7TXMJJKiW$1~;v*t*l$swP z$t0J5`fonw|47sK*|=|$404?D=Kz(O5go4h?=Y zgqb7knua^yfpFs)sZ$8mybqQid5DWZ&jhmpk<9gMFe%Z{eQVgd9RFNVxH8R*mg#30 zNiSuf8x${@u}H&f+UFwR9EkStrKimg_KzmVkHHnCYEuc#K7$KFCA=B-@*Ldf^BW@J zK5P4Z`>!v)9u!Z3c?`SZteUIz5U&aYoKL{)cS}QXXa2Cq_BOfr%3$q}F_2|Z76ezN z{oZIJXxMyalScbasXQ5j;NEJuheGxCQvTM5J?eOv)?yzii39g^xFS5ndo9Hr^@juu z6F2E!L1xi~LEu4EX`h~O;23DXav^~e9~+%tPsCF{7{3(qFA4odmRdCRZA0*?rJ7wo zADHnYgR(LD@$;THA;J5TFV#3=bY*b=;$oP3Vbau(Cm@Y%HT4^LVh{8`H_ZI`oU$Y! z;xggh8M*5`rr<`1u6YI+m>edF3gPa;wsrN7GE&^|4HawrVzOh2(-oj1;5PG`&!l^4YtUvzk|C<1;meNpIl3+L?IR^$-aUcuM*-ykCwwKU60X zl+YzJY79$JNYzy;t~YKQw~Gz;;YO4?CPJ;t&fgTOy&x>xv@-w7zEU-w6wAlDL`Ik=IyYExHwiwrU= z|NS%?Jc@T*;)LCD%xdXaV=}&v-UEDU|8THia z-gXG(ctdB}1=56?``)XX@`UE?y{*B4hY!M-Rb=wCopr-l>K$WSIW)%_8EB(bl-^t>OO|37djK|z4fet zj@y5sonZEl=Q#fPeMqYl4Q09(jL%O2+zcjJvId~pIafwa}=@iaE*r=v)AA0+%cT#&t3NYY@Mn=lGvv6)yFik~C z+D%hTC}qqW)nI{m4=vX76#@4o?60&;B|*s_cCD6vs#gg7Vem%=;UZ(Z3Rm(^8y+;N zP5|MhDp>x+e3n7y!rdZ1mAo)@fIDxDF6CjCVv6SH28&Klv@{8*czKf{zQCmJJ{_)b zWQN=)fh%<)+Q8EUJ(e{CadZ3x#BFhqyw;3NccC2eLLl)Nm=M8zIfqFNodqDPtUnYuMl_(coS@c?X zh3Ia`7l|HHwLOolA{`95|NEHJ)o^-w_QDtS@PQ(aF2flKDV7Gxy!w+XYtegjgQvTp zK~KMpcYj8z2h54V3#=NRL9SespuwW6$g%ZDh2&A6cSKm*6Cqa{*wXN||L%V6Ulczx zo6^9`+AS8EC5wXke%y0TU~ryinmpxp+~4TMPoZ5L|&Rbf~ryrLCF$m@Dz z655d7G=k0rYmEmh${xE@j+UO2w}O$yQ;*^uIW+iO9=)KctX-k_r*ZtEHVf!b8FEi3p09AY;e- z5wy^?4GwMlz@o;uV>!qw#4hwG#Aje^{t0+=42(_pH9*jm$Fo^o3ctYzXK8=UMbedv zzSZKL9KZpGYbr(yi7DG4$Ejid3q9TUyQK2Z)@OPr>l_bMMjvxM*q6`&H1?g%m&Yys z_4u5Vi+CWH&CKsmHG+5FBY{jCV7$TCMo{i0ItOajxg*4ESdlu#{?YkI&X5Sm{Nqq4 zhKE+dRgD@DA^X*CV)X_L_NqU40rV-xhnZ=QYW#-&y2p|S zzZJ?87p>zN^FAHJ1K7CV4G`SQ0(Z7|kB6TXo0tv%?-lI@oU-j#eVEGekZ{`l@xV&? z?*<*cL~o_p{VJ!R-${74e|9;#W!OXaH>c(}bTnEW;dMRia}f%{V6ddeN#Un)=l{gz z@Vag4UCue3gHe3M2yC$n>Rrq%6?ezbrMDy79 znLVRsrB8r|%Mt_=O57jCO0yk1MA{6~y z$V7P%?vH4C1QSGA3#ghw=O!TOfFF1o-uoV_!Ue9sMG6pA4k!{-H6qRj5Is-!SdMj* zMY<9;8wZR~nV^N-+t*F%=UC>8_wp{kL)8(tB8Xj!LM!TaSzJKYpIxJJlVSt2&SUU{ zwNKX4F><@x<5+Ugo4f?}D5AQ2M%OOes_SyAPHev%T%id*9jeAYn=}!Bmuu%~%n+(> zVG18K!`^#)db%gsm}M$$Rm9HKjz6a)jb62{m?w_ zWA;%fdaG-tNrh>%g3rO@J0?-Dkj~K4WToVrg7o{hfDU{Nq0%vXy8MFXX-XVWo#NZg3v$bti$U%%_U}n8x^j zt3@9@?rb*D-e?xQ!0Y{xze$ojV`MQwev2FYitP^-Z?z)$WF>pM_l2MMxtlkMuZj<` z+u85BYN^D5gz{!xGDrKVuX>F{^pZ%CRAa8oaN#3KfUuLGsd9`DQQmhM&R*c3+u}%F z6QB(nz0kIAQ#NR;mi@BM_rEd+36~45YRNI35YuvT)siE>6d14ifH9#_E9B2ZU_}%% z%Fd_0+}Dp@xwOGe(`x@K>z|9gow0dF zG2IE|nBp>J@kW@~(;q)cq>e@FxID;}oK$--Lhkj;E$a-#GQ0(PFUfE!z5v-zkX1zw z^)KtUWuAv;6_6`x)Q(km$aw(?(+c`$;3jug&mH8csw?Zah3`dM0asEM_vA&q!@x#@ zjZp@Lse2nEDCQJ{WA?nZa1S3t2+4omP18DNcp(H#gqO{ldmWSuaq~WFuLN0=W@j(J zPJ2Uy{5L!$SKx2mjkQj(hFR@Tl28Od8MC&sK(B2fVeBoKkA3-x4%ZEa0bV#&UR=zv}B)1Pw? zn$R`ulu^e=MvADEEwuc&^}WpxNzza<*QplN4npE_pvRmaC?nD+oR8iNF70)MU;odI zR(K^)()V(|AD=lpdIx$zlJQ>geR+E;Br2kCNv~iQp3YP?`VT#Oix?Ep;1Fj7g8p?2 zwI04F;nDC&Jn=m7Ah`}^B}K4&M3xnidg87@K?i+716*Ark%( zjanDDyBlZ8MDdDa=TtAVnZDhN{Qy>W_v(DbTxK>4?nLakF*yUN28oYB2XX#E-^njK zDxS?b`hY;^1XNXGGt!%Iw}6Xi z;{@nTadiQzKyp!i)pIB{2tEKv+9>-Zz)Jko{hmsu_;`B4wm?4L?P6uMn+rui?<*0JrJ|((@jnSFk^(iQcR;FBqIz&tsMS!SKhf3%zNSw(k z-;uqmpAXWx1MVrX;wscEJ1QdME;&$V)?FLrNS*v!P_X*oCCCtvETHb2m$3tJb$4Sa z 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

    5G#WAbmRXWX73&a7Rdr3*o_X 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�jeN_n0VT+Wi?>GOmaUEXVUxaRMu=AN$B$oG8K@ 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 z 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≶wK5v}3Eq8!zRLROyq$2~#QGcXlyIR2 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%0D9>I1qCKZ1_ z0FkQ23F)2!@EQ@+f!NQk>3-3)l&b0e>f_qAsNS{3wN%laen@#ytK{_dIT1I(+i3;t zEr*M!i&;K(%MB=I5dBdThnl zxot{c6Du0`DsEM7rye;AI1QAU(BUNZi~YG67;rNyT4}QyWfgnIeKz`o`h&nYHqeDV zD#sAdJjbRZpgII>0nd#_2Pbr5@$i)Jfa;aCjF;45baUleSX%!?)kHwLcTl_1;!8~= zn`K$eS9oXsWrTIT6_}sDHL`g2hOw~Z8bmN$4C9~3^J&Is$v?G5XyYXRgA{v@G^z+( zBdW`Uy+Azaex(Z}arV&`0bY*5_h1jz+du!G{xy21?FN zpde>jHY~#k^?S(g?7>Vp&~^pooeweY(wN@eMx>#!;FSTW@-&>3&yi+(0wY0Q{Q*iq zLYS2Y;4B6{1M>^ycAxXkSskkE1KYM=)atH_ZN!JaMNwPGZdS5o2%9}u5JtI4U*f|) zv*)IZU$i9bY4U-uXnzo7)}Y`$1rps(d01Vrljey^ zei`*~i+ zp~L4rf~R>f6QiHo@1K~qgR^Ymur-vXvo?cVm}G;jAe+!@76^pi)3B%VOQ7khJAHIq zm=vp5k;xHTz{^pD|1TNIrC;?7G!yRnOO334)JfPS$N9Xrw!Ngpfx7!m=@kDh;;K!0 zM;u3u!e=5u@B&Rwe4thLGOJ57W}bFOxnkXvyC(^@j1MDpl9_P!qV~*-o>iorZSjf- zXFrqXj`wM?@dXBR7yB=P8#9wJ1U$x<4;399=Mt3xB{Vr03x_*elKs7i=mxqBsuiSn z^qYcpaNs~ZRbaP)z#Qq8NNEO>-JX4Gkgrt*L@HmM;(RXD3J!dEFB761(U$=ZWf>~o zoCUVa#PpW-ljIwnNPWXya+_Be+nz}=!Ae|QiK01NO!-Oj_%5}A(`;t1GG=)y?3h$c z=&B)#H%_P7TkWux|1l7BM_p}Ey7T~A8g+WSEvpCf|J46NzL#6G=~5K@Hj z)2R?DQRI4Gw5Ipua~nbBYrW(U$FomLdkz_~f9M=`UlH714U)onDKpJawTNtut{jKN zlFwI377rVX^vHgd7kScc6haU_Rs9r0Cm1sq2oO#M*%F{BFqmMrbGFA0`i0SDD` zPa-|l!p3%c5%|+=G(P>eKgyJWXS>2}kS}9yTl!1LpGn}6Ne7_n_*r(}AXJPDWh%^% z{uUcQ*DE1-S||nMogXv+NViNX}oVlnvnJ}9_TK6R>4%;P(~8f%{LX! zEllu%&GQ`CkO8G%7PP7rofBdps31643iHT=pDefcnYB(aUvlFO9QT7VDD((f1IfR0 z-{j*F&v>@l)sp)n4>nTxxo5lmUkTh77CwU&R+}J*l+MnETXx*E+Fxj)%-RDEv)mUR z+X&l-D{DTnJsN^|&?+?(EU%llJ~uN`NV=u$0m11R2YwT*{2|*~`gxi(Ua3|}J%}-? z9w}`1z$fO*U71H_X>bI#SzkkU$(t#U)*BZd!H2$!f5*1Kw^B%x40QIluHk${ywi(^ zQRc|Q8=D6i=bmp{(#TK$SxTSxYQ06*CD~{mx%g60`2*{tbBfd=f1HY|UbevqfCtV4 zYS8>w3#GIxXs7G+`&ksxYb4iYpgITaY1j>`=spJf@SuQx`pa=aEZ##PPUN%*$|$B5 z`Du%Fmg@(c6Bo`(SB&36i4MYHb-jz?SeVAuLmt@G2BpugAha)al;nVABjm*zqukuIp7avd&G{tvj%b4uQ3@!JGpXA&R%(xQ`@# zUwriS1$s~jq~UXO29n;raJt{~UQ=^N6u@qJAe}@hb}CQhpxYS4)VCId8ZJ`34wun_ z22B|)*Yp}^KL4nZ&ZYR@_MqQhKX1au8FBor!Bp*IuHGItwoQ4qV6%Hndpj9yCE@+E z1mfy%X}6B21+(iFd|ov!e%Na2F1c(c^K>}ANC$vl1lj?uECNxshk)}pgb0gVw!WZ9 zWcJd!ru!MkpdBDzO}KiB!+!~CxU9IR(wcNf%s&ZFfNw-d(sa z2WBLNgCghx+}IB5dWll*LzH;f0$HYIA9^QxU~L#KS%U61(C5Hz$Q7?ARcNVYw|zv& zmoro00TV|(w-=|5!1qytQnlsSJKciunqs$qbJ4RRfg^ropAO_dqQ;*D1}UZR-1*(zco7gDL^V(Q63{HV3ElGLr^+vE*Mn%rOHR#q%X~z7GJ+8W>Oul2E@sVQ@ zogM(B^`Y(?I#rr0muJIv)E<7sm>(zxi%X1jz3@%fTt{V36g(jLL(|rd(os;Z?%}=mX90R9VxRGx0X2J{mw503>s7w_%lM%@RyVEN7*;ie80f4w zS;ctajf`iJVf8Fq{@0Gr*$@RM`Y?Qt{#QZpntF0JJ~s`0*QueCX-ts`+)H(o-Bf+-Ka`x zkGxLbx~KF{Kd4?ra(kT;T%gniS=Hcx5|-mFR?vsuL&H?J)lT8kqQz2ycuf9UA zWH>563mdyZ)#R~vAk>pA{)w>fG0R|$5X1SQx!Vwb8p*lr{7X>fEfA;&!af@Y;=0Q3 zsPIH0|D_)w5354H5SRiEL(G#L_DvhpqtsgUIo8>XPkZ>|Qw4DX(i&4ZUBN7_#IMH# znW8Kd?)COZt75j6hcjrldb7l$K@7}OlLw?L6yO>gsq`*j-MTtCos14CRa0KRXlMns z5Y%Z0$+Yhq`ITH;M0tyrE8{_Y7nbxdfb!8d+gz0fQZdADihThbv+)!i9PI<=+XuJx zbQrvDGV$u^C~5M zMU^Q`q+}Fo5?Ilxv~a@++aSdOI7+J_Bge0j;)~}MmAWhh?xN^67~SEB(KoB?vw-#o zg%~b3-o*B%C0T6p1irq%@CdbqLUCmynhiWHJ}eY2iU!Zwm!w{6ApNs5qaDem01yRF zt`Z_*pGLW3G5PukZPr)`@2rdts;1LU6Srjp>D$q4B-5F*W7>+&b&fkKg6#*XubsD1 zxUt5Ae#Hdai!Y;$A1$e8(qm>1 z|A0NJ;2Z%ODB`1;I>zn0uOqgqlOZ+pr{;|XW2}uacoWghn{Lv4D4M!QQ=`3b5sfqw zvh|cIielGLdr1Q9myvAwiA20y4_gp=4ra!|+Vv2+3WWDYa+dLX$!$Vf#r-B1=aSdM zW86hc&%xDQ@Ldp?a{oe^Lqk6#W;oehfnDk|E(TH5O0{hiFyLdvBv1fPHC>>EUEm}QCL6z8^MdUFlwW#h;u{FB^RS{$2pJH zZEpc7##fW%YS>_|sr&*u*qsuK>zf{8$lTd4e%>30+Bg*KlanH>U!T{sSjNsSM1=#V zv=I@us3h+|&@%3`L&8{{)P#QTfHGugJOgd*Jt&HJuNgKj@bkaRV8s_xRPW*Wz=P0q5 zH#EUxb~o8+g)iN8`+7ti-1{(O-r+${SlBJU!;o#^Fu*ZhxU$#w9|Hwa&o;Q9t-7c; zvoziGGPw7vD|z(@BzY|=oRMSDD_9S1JGV?7`=(<{#yp}kVUthdgEk%;{?1gaK-1aL z56;4i@AtG`>j%qhc62xq4rL181r8SVlgLegh^4SD95-mNh19vs)&e2P{gb-j&WQPY zYzZatJpe%%uU6Awg{I#@d!3SK@|q^MrU#PJv15~< z(moyE6LYOGFz6t&8h?%CEHz@ONz*usVT4Yn+4UN7u{irZTS%PZ{{HfmT zN8QHW7T_1``bbqjjZ6g@)B1T^CdOmikCvY_w09g4@Ca($S}Lb(d;qbcJF17PG2~gE z3FkLtyV-6Hc~zu$ei{WW4fegYuf@H-BP!l-0i)m5X1)Q7jU@PxyE$V}ZIsqa^I^Cl zN*hA>k(v)`6w$?tGP)g{RnOlu17e4>m*nhYo`dIAP+bDHjEBFg#R0}?3!dkiEY$L* z!V}J*xu(1-P0chiR?I$Y4ET1JsH!k3AQ_EL@>6WB#B_qq)((Mfj+&8?y{`j(Ucu!y z!2yi1956KgES{UJb`Wv8ooIbqI=ZU}(|daCpDm{A8jZK}6Dr zsI=$vTv$DN(?UlPouS+U9rvTR_4DvSx>W74(KeCJRDBi;ovVPsQ8tP2`06k1e8g&t z3a63LITsG2|HG@|Vb26+BR>d61HvdGdpK;+VKl0%LdS1kW5ANM`Ozt`O^o&5kSZH; zbn3G&ciRRK-(66@h?}77Pr>sslQG2LaYA`>TBY1whnE3q(9VznHFa&T+qV3>v=OSs zf%KDl8}#k6eA`$HX1IUrApgXwz!(ZQnng8VL;V`rT(#!P_4nU@S^u9Z&>JDzTHr<; zXfmab75e$NLyN{*TFCHLNn;qp!CBOaX>i<=NU7pc$(wq_baOKgWIWrf*a;`bUyB3> z<)80MeAj{+qBt-y0FV9wLnDX9>;dwzi_%Am0aeEq(Qdca zqetEE5@>1i=(x^akj~;}-~Dyg&nP2L2J7NguyRO}7dJ6HAw-JQq|6Tzl(y0|OSC+! zCu2+`T2Tgfc^}^|8i<*7>(vv{yIMHiK+dFUYUcOh;sCG?BH?t!d`mn(G5f#%gtYl< zXNPY0QF=8c*`m~&T#&_Og98P}=z`NA=+-0W83-il|7*YKDZ~zT4Lc6`KaIGe422U8 zwQb+nee}5+vWletPr=nx+?lp)G-3J;cP?#N26aGr^j%2!2IqIO&+Da7{hrGCT5m5g z3UMBuMHTJXRww(sLggaj_z6h@Huq1#I?DR$8O}vW^@Z>cf2(J>2bYI_%s!RBvwft| zU1qePDIJ75bppPod@%j{N8Q~5gPm(s8u+d1o9c>>@dm?Bc9p&9E8Y-3Re+Ds8EuP1 zq7Ippy!scIp;)3_zJNYR3aP{IuOHUCV^*h$!jYVbcLW@ii#I@@!(Aph(J3Lu^twaQL9CFDlw#zGjbo_ZH+NxS%U#rb-p@beoH^y*J$KH`oHWo+wtWBj2GBt1=_LskqAYf0+rVPJX)1%yHU~`C!^rR8o0F-!nt8XvYjqW zEt0cYJKzB=Y0%L1H!u-Kx~Ii}0_Waz>lAMC23ka45I+P_7})?1Rn7Tugt2V4CzhKp zBKqah;->`vPeuQF-wzW&a#=h{vPVr8wwV>D!_SqjRh032&#Rn8a>3{4Pj7p}LWj>vJ#!i|#3--cWeVQL?zD0LK zBVbgcvyU-5*la_`Hu+oLp?WOFu-GY^>AcaH>G1Uqmhv(sL7vkok3r9&b{?~t(qvOy zCbbnXXxYYLF;+(#U~;Pc9+7Oi&xveUa0Wsz@FaFQ1X&+=taz4y=n^3L{nxyGkuG}bK!~`zARLZ4|?^w zJ=s8WVSaGqDwM$8SL_`A9h(7C9bg6gZq+((qFDOqqgq;8DXr^4xZRc-?wITLpHID+ z&}(}k$hhbt*!;?YBEU0#wWRVG?D~aYq2**uXiqY{&f)A;Ot~sudl%N)$&S0LqpW3r z>K>sE--)lE_6SDgfVU3lQLbzdCH+7+F6}GXxxvocE5KbAnS+-b=OW;|`0^*#R!yYB z`3!o3En)cW{WoF(8Dg$Mt#eH6v{eX=TVXGQ!X|wMaheBqhSzOvi_1J-J<={H)32@WdF;WV}9EG%Zg$a^1V}vunIsSWW3d z;QbpuBfoe&O@q)lhuK+NnFA(HaZyq zRiPd8NcCMl&qIi1$_6;+;1D|#9W-KQg%69i#AqBSi7kDtU_82dsYYT@5D%^pT|%r5 zJOC!FdkoZK9Pv}?kwIfw>Dgh=9zo$qYd$Q8!x+0NueHlT8rxmAI3J4pF;f1QM@Qi^ z_kDKs)%6aFk#mQa_TS-2mPk^-bKgzaCAsO4%UqQWz(<6Jf^+;EF%Gdskn;A@C9tfw zO(J@#Z1s-Jhk}(brEP4o$}`0|x@Vy5mI_si6~&l{bL0;xtSrj?RxO;wq=HG-1=)~s z8b07JUJtmB!FW#d-K;|GPLAI+A1;1{`mT6^8yYiS{V%`^_3~A9`+whu zALgF}E+RV5sS-{jT6G|*-VnLpKa<%9BE;0B2u!@$Xl1zzU1~;`%~|J-VM$K@5KiNR z2gJHh#mGEe@7F>4a0TC~F#^8lpRms}>LhFzNpRDLe6#lF4I#LdGOTKp7Q@c=*rcWd zQT`ur049`>lFsZLRjGCOwP+ zhV_>Xf%vkV$+W)#ufmEMcGU?_H_NMA8%#Nu38&HmjM-INU7u@>elL6$LK(@NQrk56h2clQjI2qD3$&T5^V2IF5p{SQxqo zmUIt#kF-y*%PL6Z2U(=B4tM!QX54qX=WY*!3fW<6#n2!T@47Ai$F?q6KU6AF;q-Xo zk}qu*yGxS%z)hZIDXviOfqSpH$rsH|dk5)3z8Q_N?wguOYUTi@sLn$S(A^BKn}}ut z7;cKyqL;aZ)}fg00vYdo1+b)z&KR^9R5}QkDNgIf=XsGaFVe?UCp7T+!(R51PT+eA zf65pAoMygqClt*H0R%q(i+}`B#0bRg*OS2uDa}@j+d`e;r0HIZQKyL6y1XB|&PnNc z=L+nfEuD`^9YX&b{K;m`>0nZ^0c3M7^+uUhS^3pfOMlq?;XSkpKSS5Lm3+U^Ibhm& z?npoM$}Sumh#u9%+ef+Q=OD{vcK`#Y zoP|o%CkMgiyeupZ+`HZ*{@wS#a>L^g$%=t3qvCo;SyW&U_&q&0e7cZvXzrJ#I_4Nw z1eKU}W8nTdPdy&}=RQG))GVj|^T`E<_4X@*bJl@Go%uQXZ_${xt(|T(snu4io57xJ z9$iIZnF--g1=4b7hYO!w=rP_O)~Wp!WPGNw|4NsW{t$sj>{It4fXcKRZ+~92V9KU3 zmo&$_CXhLIC9;Q9>O*jI$X8&l%VyeS+onYW%=Azj&vX2pRHpztEX_W zq8ht9qnGoG$Gx+0*wXJ=tHqe#o2rpDW1JX<)p~sw;CX(_pCz$y_lF`faO=DAbMK5} zWFe`o#7w;!6%Eb5{iRshcIqtHFmOAUjJcQu#C3C`jgcrLysw0zNJNAhgkV-L4m34$ zHhM*KBunrS);2`;#sq)|`UegxJBp20e5?W5>!$x=Jpj5P(cxBPV?dK~* zbwsfbqR{iZ5)mr+AC~+Cs`2ySkbA z%(mv>{8*tjAsBxdVFvzkU_G`dJWv?QMVm34((PUWCZ!J&W>^NYhkiwfLmBa))~X=e zF=8sZs<4!;2Nf2$_2q5-@^W(;G+pK%{oik{EW9p8QknkBVEKg?2bV~2eatEi_13~n z@uik1Q;(Uiq%_GCmp~0}BX8ZJ#M>Xito{e+lfGwC_*Bi^F>{hnMmIamMAh|(@&_`T zr*Xwe$o3yv?nHjF6$W`;nUd!Z#r~+)wyu<3zl9Ml2;l<2=K-?|dOUD`9ZB6~`BwY2 zk`~Q{?)6CS*eza5VE8@qX)HM<#+mDp{9t00(5~;h#0_4LW@phDBVuCLK&uT*xA66# zkcMDpYtl7XzPbCIS-e{PHR(0WSrhqTYl-@oI+D~YSzK#LCX7##X2JvK%UT8hzh^BH zsoz=X`*`Rs$$mN6Lb}$otn}wi)Y8dz6LzAtXoIX+{B)=tKh0w5nV4NQLlyx(3v4x+ zaCi^QN!t$(2B6`8Ejj)A8@W3G1LUH*{p{3(7MSV!EITmq8su9%l~34R2^#L;P-zGq z%>CYVIdzX*wB%p%-%sT$0F%)imDcv|=Xnp#YtWft30U!R(D6AzgqMUO*TU{&;LJ6V zYNlTi!`*wm-y&}h*;QLIU}`pFQ)tuP!*n<|>K_d7maolA^pVwjk8PonZ>N>6GI zbBD#HP8DLz=(JA6HbCeDsH%rlS(uAD6F2wZJ(u92Ywd3EF97xsCu76^I!cmYg~Vrz zKP3N}h$okOT%e-vpSSLS^LdS3HGQPZ&TVi#67Kbv)<~XBPIMx&FHSPg@QHt(cC>!L zRug>6J(4pQKgG*MF45t%8GIE6cc87=k|FMzt#yLb+9osHP75}|YQEYzW@}qHOuAsA zP1EggAu1sR>%@}8YfFxxDDs&UPRs8=MD(8Gwg61CXzYA&I#TdR6jjV7P% ztb=OXgdluedOBt=x$QY=6K&x&#Br}h1}!OMMjgZGDP(M*QbL#6?!8_q%#8%=+8f(c(5Jvp*2g z4cs=^PF*L*;E|k7>$``D`edV18-K#!=R*UVt(adTf?Ce7dCd2fB12F(T4+Zp7eZ|T zUIbia7Q%*sY1hHVGD9@Palt!cJ_kesN6H;U>|xjvgxMGPdCB!D_m{MB<=Tylk8z9t zmh-U5H#PlAz=a}EMpr$_0J)E**MF%RM4+ul~>`rHQ#5&V_w%c{2}MfbN$jr z&1R+fcOk(0zz@mxwQtn@dKd-4HEcDFj;TzK8!l-j@cJ10+)!9*kKIZKUmef0yc)eh z{~Z&k(0Z}%v*vS|UjGK`S2V3dZ-+znxR5izpdrfCLH=|z9DldSb){wY-0%5pI(E`J zr+0P_O&Bb5vf>IXwNQE_`CDLt%V3+ zG8b>^$qUIxBj;W z`Wo8!gv_q4gh3eDKW1hCO5ua-r5D`&;h@dAei+B>=yHhuDq;dU-oAfWT)7%f)PmY7 zvp^$fzaMGya-63kMSYE+|i?BXh%sK#?q)Nbgw3Sg<(9 zG3smZ>@^(A|C#1^^FL(+*_k08Vkg|XYf{1?G)T~2|?I)%5i7! zu+eDSfYWd69^x^7j3Q1wik{a+f>{o4+ODGOaZTds2#dEy^g5>XFV64Hzg7M8+ekFM zM-)sL+IAIN?)!xUw?)P!3Ls@DXo3NC7A7B12QdEI?kuEhXa}@LiZTNh;>qm+^t5VA z4r6KPFs24H$ACKmT>>fP{x2~nx2%Z_J1j+!*{@zsrZ~T;(pIO z6Dic$?A!}MJu4vm&k87`OXxl^=zqI^^qpaf@B8c|liqrYfq=p#wlt=M!AGD024<+D zw^@rCe@}IHqN96==!)0H+o%K=+dwK}AQAsMy5j8{;UJ0_h?Em^Bndj)=)QxAIH04M zI#Ovu6m_J}=&unaG^iT!eZwkm#AF%^<^|HZ8e1E8h6yIYz}l_YTf@=n(utU#<8QQ> zAJS&j{G1NFPF^^Xm~M4UzMgr2?z{e9{_1eT^sfn)v;&OLq@!wpAwm^HG%=}hReGwI z{gh@8#qFdr|GX&M_b$`7U^y`JKV|#}myokDz(jcVw-4!)(<`6=#(SPp)_&45>vB9S z=lWiG0=s@&uo?40AIL?++O~##DuKG(B@=K9mr2$X!^U`6m57?y7@0KAE5>2IMHy6V z1KEMkU&nx~db_t7&V9u{&K~tG%gNM8dctkSgF71qZNrq+7_9ns4~M%W?&rY?OuJw; zsElw|9j>9HOw-)v?jvOU=vK{aI)pNsMyPmQ*%oGYr7K2$C7y?VY3+ZnXO-99Tu_H@ z!pLanplXOo!JP^JxU%zq;m=&;IA`qwp(9MX^b;p8T4mKKm1XH>#Q~0A_f1j z7WHsHuaiG*l8`7eeD~Kjajlq36A#zwbNQhn+MT)1UUqpF5_r4yJ9U zkdDY)+)^{gy}z9UR!c#iOC(Gw!1EyO1mQ&r9aO@g^m0_>Uj$y)c)MH zF<1_xis|Z@&`do5C*<^T(w{I-7DB8Kvvww~g7d*3agH&(w83q`UNqdZpy^6p?3px0a7apwq>8t2&0C zDPS}@hrE_B*?PV5zU7jSli;4z{__JUqvpeb*^`OqaK6)gvQK{avy)X&q(pUd%y-$2 zb}pI<>WTW|2H-tKP`lfm@Au3ZZC6@;t?9?VYtmOoKA!bG$HOnV2P;qEEt%CmdBbGu zUrGlh%*LF%OUgh|e+A7@@MQy~IC1=p)Akpst%R%VllJL#ADPRlkN2v53f1HmaDhz5 zd)C)qC%gVFy9YHBaBRy*<;*dct*tg91rxWSmnGLD$Mzn>F4rVu*ryd3HMJUE;_1^*xIIY_YA+wB}oisT%ek%PwL)TclMOEvv8x1(ouhGVOcg`t!^GX(C2{ z9KtmEHq9VuB9Gy>l+xw$2@|D-Xt>TT7EsRW_I+nW!@Mx`+)>Z%pnt!mm9buGgqhAl zU?Etm+6*0J+XefjlqQbCyo~&M8zT07^?rvUn`6>?HQ(0jJ5*-UJZuHAya43A6(o`? zlH42h)`)c&fl6}v1Yo?IXeZ1;A2GCr<%fvne0S6b#eh3{-xos#X4a`!HGZw&TO}ze zm|TX8RguMHCfP{7el+V_(R+F9dNi3lwg_DVQTNLCTM8IZSd!<0lhv#CJFw53zo&V4 z8{UpBe&qzvLH7LXTohYsADKH3$dKc-n|#R^lEbhPbw)qM^m}VW5=kQ@fF_o-A5?bB zn=HBOTLf2Wj5yFJKWvXriE|#R#bWdjtOI4PIB#+idG9JFt;hI6eDrM$U|eQ61)eC^RZ*g)n_~f zyQASF(b7qg_R>qACImvMKe*qP`zrz!H8YgA`W>`LO9SR#aBk?8AabmC*lHLdTQ9<< zSPjNaOml1>gX!M!723MRh`4gk(iYg-P3kuc4-?*}HUqP#JDGh=&ONkmA0u6=B3#!C z!?%7qSzYgqnrmjbpC2HZJ|C8B~ra4E4`gozLx6r<+G2(@|KGbg_}j(LM z=li1yIHR|C`4CB;pr2Q{^fuV*(bEr##4Tu!bk@D#w>@ZrrQPYf?*M&xWDA`J3!w(@ zcEY=4a9h4;-{6(ME5w^tgMx3mYBAMKa#o7U=ffjA_V#ho^Cr-DWKR=dm8*!}IO?R5 zd?v5k3=ixj4|?R1LJ6j;KRi-Im&qh_CbicGwwZ2oeF0=+yn}rVMxk5`y}qlYN9XoP zb+`nVrSad3k6Wa^af~8Q@Kyh8WYhMyvGs3e9=plyQ3~b7?(!5w8wULtZrz{}=UZpM z9<-M&9?0^Bt#2tJ7>9V;)$PvsLIT&vN>1B^isCJv!l7}gT~kJq1lKihh!b2Z``FT< zpJO5-RaDQRC8{+Va+2CSB0UO{$vp`_tZsa)M3kYDhzq9DHQdwLw)ARLq%W10sFsW* zC7*;jo}by;F@n&Cl`Uf;7z^~GDNzN99dibXtKRILL$|iiQ`={eQxfGepAwOH*C+}7 z6y0y=n?C8Y7!f8SOj5UYOZK+k<6IO0w)7Q`E$UElEA+&13_<#{@}}xOt=wu^(*KkP z!e48FEfi_}u?4g(y`N_3IMp@ro#7i@7Un~00Gi9wJu&7|q&$Nj+9VebI3+uXi6eKB z92CKPw^6m@B>2{sMbB(T(M@(Dx&W82&t=iOIVy?c&B@ihmY_Vh2*$}z*;l(1iF0W2 zb}HA0-rLn{T-AB_Xy_WW0D5`jHr)Ao8?=2_8WRNGE5uwsb8ta}_11|6hX?=#K!&u? z{0+YGP;QYKS?dvKFpm2@b8JWpe-1AaW=2{L^-@xo0oyU)H^P#Y02k_DK_(?Vp43r% zsVq-=YxKMo(X|V*X#@(bixws*gKB{WtD~zJtrA}qSQS%{$?9kU9qZCo5{k+l3mYH8 z9f-xuzw4YEN0A;U*lt1OtSIX1(^SF~*`tjZk7+-HWbTZb%a%&R_e113=XNS5`QT^UElMU`)B;I?Bw!&dTMFkfejIjhqPv~^M0(T@axy?pq+I>X;&{wG zcKHqLd(Q>Kg`V_?>0g6Ev9a&n5oN`|yt6{I&tr8B#-p&_HTKwPC}+-tw04Fx-X>JZ za?8-B7`tW;jT-z{E#$r%|Fz${E1836Jz#J3Y|MjvuqnR@VXd9=FsF0>ZZVq7v*3KC z0-kJxvsQ_rpum6~N}kM}cG~Fm=HP0XcERTCqamre?lDQGs$mNy>;mGVu{>Jn|-&m-#HeT^!zFnwA z2&N^A1`P%k^LfMcy)`;F8aA3xdvSXA=LJs7+8FzR!|E@&cqJDibY8y=nj64fd;WP{ z=qaW@PJ8?{>}ptR^PMyX8rStdl6ehS`?Y|?AR`(nL?AoJBc9bUlDP0irVdy1xP@@hU~%*-rVC}rE(8}y#}RaY zUwQ!y2N*1e&|*m1R|6Mgwcfp@PCxltqhucLCYOCRfg6emanovlX4IEUVbeU`J}Xjb=N&YJoZN&V%VgJo^gE9|G5_T02e8qbl@=&A z7eUd-i_}Q!vRP@SSela!N5WulCQ`;PhVLg&9jr>aGyAge99|FpB#YW$_5Q1$CQ&iI zMLWTL={K!3G{G84XpZcQWmbjqZd>+(;=H7q^x*v}iod{j_;)cCfXeP9=(<%LWRntx zaZIA-j6kJ29{_0y1k+6oXqof=LN6{dA;pb(Jk*0WLyTO^+zR88DkmQDRrq8X>y&;hEwV_$ZJCU zFdFki%5IS~S9UNTm!nlaHsJue{oKCyC8p_l1=|FH0$y$A`kC{VvwAU2C5q%;dJf?I zb)nN<88LFy=rLoBo@!LplK&1EPwqO-;W3mZib7w`W5o5~KiL*~rwF|?(W^07Bti7e zr^FFM>4p7wirnPaS+}@ph=~Ovm3=e=0DUIp)uy!R)}*&fJ> z`V`zoV=4>ZUiB925!!%3NnE*ygI zkWYzpZ;$5E>vTWv#~OKl0f^~kVseg-f*Y^$NM5Fe4 zZUML4-!}Z7$&yeuYj}b)xxgwHNLE^j>kD98zY8d~HlBe!mXE#GO3^-SgJ`*(B`G zhjlYwQQ%d~Jvli@;(x^?9CslM&2A^Tl1Z;T}NHSIG{% zH|p|$h-$kf|EAbI_wE2X;O#m}v^NYEp*wJCI;d)?$D;W0L+JuBaXM>}Z0V%|oZ`rk*ttn-#@*Bt!)Ml(TvI-O8|%yT-LG#deosvMfw3(A4It=1vj=*uwXbpnR6 zjDAf}uF(N?a*h69Ly^UxW+1<`R*a>Xb^)fn@KV|U{aPNPJS$)AKR^wJp%CjT3!Leqdi_-&t`kg=1OgM9Rlfor=v%KRLz4jjBmW>0g%(; zoN+ZCGKBa|V;)aID^+Ehc#r4+S$-cgQZ0c1c|U(>WLHYqy_muA8)@|*k7~Lu4_2Ip zoEy;0o?Hht-B56B)ZLgz+UKCngtHc(olNL0kW;v;=8bl;BziX$sNTMtNq`WB8^!{m285H2vF>t zaRsb&t+&kZ@RSAzkUO3{6-+0` z(F`^nwvtA5a4=!`L2QOa*;Ckw?gdxL{4&_3K|hBKYiabzqMuVi38n7N-*@Yy}hm2Al;-_8%Rmctg_2C=iXSFff6Tzc>xGKf}^zCOX1dba&VdA7$C%-o3^*7#}I&J!lnQy)Q&aBz*zV{EaPdD43 z^U^cy@OPvCw-QZ#64FuDBOO9T6B6=w0m3Aw6M|wyK?LKtzlNb|hv#)yTb=oye9DdK zF;Ahn)?0S=DJ=cweKa8b;)H7wRqatP%0-WZSV$1tp@Xm0niBqjsOph;xMhr9JY>hO(pox0@DPyehD zZIjnIY^p*l&KNcY1<6?OSK|W?a2pu!c%Q`2q}!XI?i4hAfWbU->^!aVzd4x#g@4!) zPQxWBFaQDJJkN`+29O9$SrZIot-HgR*PpRz;%}sR!=!0r3ou)@J-4>s!fxFvMr3)P z{xZt_z{feC|FheA4@_Vi@~?#hQk`->FM}=fVbkDwxp1uegU8OUT|ncC2)l=O)ok>X6v_%*U5_Y3KiV zKiz?<#w8(qHGutHTz#Q&!U&DfPH?)l$q=*gHpEm>s!Qi#xc}P}b!|Cp&7_Ozbcj%l z&<?ssMtwMZ_Z9}-d2$3gDmAa|!A>opklE*c^^ zOm-|{`#dG0Knl&so+zHcS{Sy3lAM>uv&x%x1-~~z2Hb`xaF_{2iM>lai&OSmZEGnH z@A3E}5xz&Lt<$=}qLs2x+xVf{hXlEA*#x~CsoiSPA~6w9>UA&}Fb(`!7xwb+B|y&; zOXvlX1`AQCQoOJf7KukM68C<7-?+y>R#S&p1AW&*wEV-p!};@eG!(KA#Z5q; z14-f-{yq2!B2)01H5$+J_z()hnn9c;T_o=H-Wq}}Vmu>n?I+PaL2U2@JP21)D$#m+ zk(|X#@UyU=#$S(f9MlvVXAu6M#GmO9$FXF*KZWZCJG8CxLK_Y{ncZ{74F-GAaK9Pe zS+xtH#^NW|F^qeARQFU$gSHdN7@8tTGErkyYw@t3!$sn-TD_HWIc{6kC0>HqoKMbt ze`F)ZVo&MwVC+MT`ZlMbr}~))iE<9CXVq0B$!u=bXDqj{dw}DhRTwTS$CBq<+#z2~ zA%g7c8db!24%g0|C@P<}PmQ@W21Z{4mwM;3ZjDZwi;l;R0;9dD8hi-e_&lvk`nyTa zp){G2Pg44@R`)GD)w|H;oYx!9od4T4B=w&k^CSL2&@emRW1ai^7|3BcshYKmAuH%e zsI`c2sp_KP)`{`Qtg-m@Y7jXED^$qp_$f{@AzzbS4CWtMK{uf8&{}$V&h9P|z6(oW z1zx(Xpj{4-4XF?@-M!4o4*R^yW>iBRyPn;|-d0`j`d{AB7oX-&<7?DIrP%KtI6xT73ZjD2Vo}$e2%8e2}bWpxCJ4e z=Z_(0S`N#K!5x*QD`K>?r8^sy-PH=2zQWOSrpJ2*Ol1qj)(g$g}LRAEZ6sR^2A_sqB zXe89GCwe1wMxu=AHgfGy*k_#h`4dHB1FYrscs;v#&Wv)EW!1pE_wh8)B^QPqpy->D zfFEW^(nS{Q=`9>fXJLSH-yd;;Biu|ru}80f>EacX#jw31FE{&BvM11NFIjJMSfs;I z>^%>vT_xCuE)tFV?k>I6x;H(~^_acbx47$q6u1n#nPJl+L|*F{mfq~J3`tQiYBmI~vHRcw9HdW6_RzL`FT}=|&lYJwPmL?W^wbb)N?kjrt;U4Y zq;5b-v0_9_4Nmo)5mPOFMSna~^JHDTGc#zc7je!gg~^Kz5b7laYuBaeX}4aqpO-;L zg9_?>_3Q2B_Coun9oTgGT727Kc=@Wn$GBJ}7bCP_m{%Lqz}?qVJL}(}7*A|m*T2R! z+a}N;05UQU66hfSG8$y?O^v%7*ZFtnQ%?x02%ABJqVyfZUGu!%VNL)s*$17-^+sEw zt!l6{o?B_(I($Ju7U>*>jx560!#Y$kTohf+aB<9-`I=iH9bI{um1-v@0!#MXB{J+_ zuwdraY`vZYr%O7$KLVDVN8MSCyS4OCEbMTOJcqYr1hogp(JMa;Zp_@^gEygL5~zvU zYU07ASoC;8lqvnMEQaV{ltB`G2j6nGTTUlgC&HN2|0KTlrN=K9W%nmmtJ!xNqcEQD z1+OIqP`?uPyUwE*ppt75M%(hP=+`)R={w;T#BSi89&uxd_myZ+7T7Ac{SoYR@(uMP zRP?>pfX^QE>e&=bIhO=}X|S~x7*FjJ9!dFW#te-0Xdf2seCY}__ewhk%p)Z^N?cA2 zdy4kpr0alJHEm;Yrpr|HC;ZtzShAFzi^`8sYUNOm&)eFS)oT2qPko9fGC28RaR+$L z9`XC+zhfeMf9{cORbcz9?>{#OqBis?odS1B&StP(dJ4Wg18lYxnGyhJs9kVD#?KWC zLEW{cu~NQ!$0#zm>-qV#m1liVQW zmzcW1o-aG?)M^)B>PZ3MJei3^j6VX}>Hs=xtEZzYf$X`;1(_ zxY&+M;xI}RLUDmq1{&c$t$>?fPr4b2u{f40_u|Plk^1pn41lqe#t;fD{e;9L(se03 z2`YIy(@3p_NqFdO=Cp`pYtN(Vc{MAKm-5|yQpq=yLkh`#c&e-ym$G@#D3Uj;N)g{) z>$BGuBWtA0#vHBpb?E4%okbfy<360ApDH(4ma?lg#A`7)mPpdYc#1s;!%}8jw&4iiDO&7F2Vgj zdqT~Q#5Ojq`u4RG+*0NL^4QU#8B1|zt6 z9ccTQXj%w&pLE$vRF@=9cwRt@fO9Kgt7?l>8St8z-b6%8=qSnxA$r}!zSDubC_R8T!IRpOw*JfdX)nh2Z?fw;|}nC9r`)BDIP+uz<}`BfHr z-C?BcP_<;grM>oeLcyx8MU;xSC1?zWT13+>skDku{_ud-0;W0g@9;o8x+k9|VjC1v z7I&GelHln#!K+$?r<*?O=|>Ft4DXw>WRi94RXi*;QbWV=ckJRW;4!^*RV@7=SgrK? z=w3W9M|e#YCW6VuXfi>{FoNf+q+c2B(}vPX4kCP-c4La(B@ZB-C`xQjC=ROdr&F-! zy;ddJ4Lj}mZY##g`Txc1zvEkXluWVHjBb)gU4vl;l{T6v1Tz)R|3)vpA^+o(nZEAJ z^aR!c(aB6ge>>!;b}6a7ARsOofeeKs!J3i8WGGSoFC(Ss#7q8fhlts+3IZje1!;wK zEDM&}7eudMv=C;hf-7PIG*}|N1ro|3j8`HGyb{4whJ?FFoxFoC>_NT>-^?q9zhqsj zSn1Y95VadsfKATbZcS1AmPek*m;JtS2${`aC*vps`X=M)8(LQ9eu8q)p%@I;Y!^zg z(mjJBt|H4xrxGCvg4q*no<%g;0!Co%R1XYM+x84@>P>YDG=G!-d$P)>;T2T9Eh4dG z?L%OG_n!alt&tHf`Fo-E7d|$v9Y)E{kP+{DuiQTb>RVm-`{h`VFvgAxr@9rzKz!ep z5hns>?kLrQcYMvFqA=EG&(4G)Hs^A=}6V?rPT~y%%MJ^FB8+0@tu zxdV*_O9B`z=yYL#%=#5Qd^RbX!DxN32_hQ-4DhWm8SGA92u3|Wx?!v+K|o3;Ec_6fGXc-dr@WFb?wBW!G~E-ZF@`*sp~sMmfSyT=nm`P2*mLc zvRFk1t-`?L>}KY{A5j*kEWXh`s%E@w&s%6!ZyCxxTnnDVW3 zLH7;wfqyyA%3t@)T?3`-ApcF0Jr1MdC|LcIU9o+b_#rdFJIQ8F3nwi9=*@}V3l@O&6_sWiNgKN+ z494@pD(t3DT~5#Te%Lz+&vBX>Yvtxl<$b+?6D zwU(J4X26}qfHObBd`pMk+`Ze2_2P|gO?z^iJ0#3>N`vO$tEdV&o$_UhSVXUvx>of~ zoh?qmBp=nj_xrEu=Y9>zh4#bUR#Y-h^ahdWoca-SbIz^*FO^+$!o0p;C)$Mao_}ZTV+RDk?|QTl*R)`5j^w+le`VF{+pHh$r8r#Sh2DgS)S`BvfegimY zpQu_vN+nL^_GeLgz0PjJz1NPss$A}fCJN-)V#zI3ls^?87dyO0pA~z(8%9YS}$<1-}~Sti5IVDoLT^H5%@EdxLKt|XH2mx zsJGLhbUU%tF9?!b1E3J?MVNVs!rV2Y3E^!A=#yIWWh0u~N!64ba{)q%UL8j=-2%_` zEPv4 zxgAs53g(fdtwkR>46#8^>gt8?^Se)p0)GwPPDs6A%jRl2;Io;am(-LjjfOhCrgmI%&GNO+h24y4c(5t>Q zqUbQmJi-`V7qo|Z;iEXUn=dS(G>a*2Att5Y4x6Yb(M0aZLdf9=Cp~U$eZhJMGLfJ^ zZjsfZK@qd^ieZtBePK3Sw^-Qq$*@cqIiKG61j1(D@RZG`&wiJMx@wFQW3_9jG=uD3 ziH5CKyV>71yLq174EvqdHG=!yAG3Z(v5)tNBHV+1%H`tiHZBwLmGh zG*avSDw|BNktiD+16ITPWCQ(GFWyoK3bea|EUhO zLbGCe;Efnqq|UX*w0yzD=h1w8xiw5AoO6uZ1b^ANG>HheT(Ou^-{^`bolVX^bggy3 z(^{>%>n0mozk_~RW}vEX zZ{eCd3>B^u%~ir(BGC{nS`7Q(CfrA3s2b@db#IKU(I62mvmobD-ahQS0B$iu-X6A_ zdAm{W$kI{j3}$4$s0~rY`Jz@tji(pGS=5R*$wa)5QSlb^E!rcKf?V#oukQydxQqNF>$L(aWuHs$wGF z<`7S=&<`bK5e#U!gV+}->9eX49@7{u`w8%gY3Ts$*-QeVz=f{IC>w=|u#y}d$HP8! zj17kRkF-73Qp1)mZ)3S(PRS>^L?zcGV2^kPG2(k7LX@Gd9DAwvg_kz7& ztee?hW*a(06vJ%o7zw96G)^$3381)Vq|z>HHPkdg6Z33Paew9QCQvr)2jv!as2}7_ zwv1B6htc~@=^0{5^~4oR!_gz*UePP z7eKl~#7CJCuOEIe6wXM-PIF9w$JS>c0pxe`p%ncY+Iv5+%Yp@vC9{lA2IiS^#rRC0 z5+M6!>xbV@f*C}7K^3wDlo7+j=GMtVd%Vrknd3&=p2M6*&&UqSOBJ{Udee8wwHeD= zfC+{Rv!;bx--kXq10LEb&!Bk^jdq7kw%29^L;L%WK0*7rwxD8N_YRw6pG2Vvf1T_k zb(d16L1nM?qwY|@?%D)VbYFL>2sh(}vlvaFf+(|Z0b{p!0^8SmPP`P$2klv^d6Au!cChx!eCYEjOHE;5%{yTn)p_>lFs;y8w@~^jhY#k%>kgoAV(tZ|2 z9o=4xBpvf##m)8xv5O^pZ$ZSL_zQ}~ihX9(J;v0b8p8h8q4*0n$%Z5JtO3e#W zy6M)7cENGwuTEZisISkxEb2PH;WM`iCyhMxnu4X)z zO|S9{Tc zdkm$ikSC81Cb5AenRye9{{2T)NJxcxxl(+22W*>~<0QW>*_i^2Xs1-!q}|L{JPQQdMHEv97{9kp_l;vchF={d`$pC}Y&HDb$8Td6waBrAbGb3Scp&K@%Ah7>JQ=WWD z11;5lw;&2lz8YlrmC|Cb@z<$t!SVpRV#dW&I`6v&^MR4pWk6sE-gfOC|BxDZ1_go# zo<&Yl?JRRkHDsxKGi)Q*nDa93rl#BO@dy9AHV-qgq93tC@yO-?*tHCUqO}q?bsi}| z(z@`CTb99YA)||7%4RB2V{g^EI_1s;jW$yT@U%{OUS>k+`f6#{Y2Ye)JhH_%A}2|f z6Z!l`pTe;W5;%U69GBzn>V*=SIyRKQX&$T{G3x-miaf=*rZMZ_JXmR8{NP+Z{?Dy$ zd^8=_dm9NFi)k4D*t8|6jl=%d6n~iWSH7fF(y^1V^tVx&M`|JbeJPkRMjK=B%hdH1 zkger*y)}1S5Bd z;U5M_iY+xc2yT*atHtZjL4H2m_#)`zxYx*_)#3@~ZlbB~fpO$$5~i3b)`U-q`Nlbu zuub1?K(<=gGk+(76XRj&fQ^4_QuSDFK@L^H_&;xfx>)?o`&yUC@r}PSvOA=IETwKb z$)iK|MCUQbRy9G=3J7CmoOQT3U?XTBo-Cfo*0Bk6kB!n~I$WvV`lGXq--{>| zoUl0UI-PqI0NXEtJ4OqvSH}9bxHC`Dy(!%~JRHX4z~`Hipmu1tvcC!>vX;M!Q=veD zX%k+Z?z!y%{*F;asTgKpa}b$dhiMC|&{1kF?9GQ1S~loat9^qaT;_kZx)4_(Y8mXZ z4u1wae$9Cs4cwT>*Hc0F{vli8sf5>k)>v>EZyt+<-L9c>?RzX%Z8{5cmU(4-@z*z~ zt6XAx(k_(VRt6E))nqfk%PWHnuA5N+(BqgL7u9?xqXwg@pt}_x7**ihZf>XnyuH$1 zFcnAIo4C6j!scb7xj-;3PM5WE(H;o59pWf|^yWpSWh%^c*wC|-k!)v&+g(xMrA5Ujp#)aSc$G`(`wKw(p%XFbHR;nL+43VD&qSbxGDfY1Z$xELhSh3G zDoD21gQYw}K93rfwF++HS#_&xAuE|wLX)pr;vf-C5^Dc&n#8-%*2?5j7!=cD39oy6 z=EoGIdIj_a<&g*lRjKn(8FVDYy2Cde17F?sJ{8#)bzUPwSU`!3hn zeWF6mHaI|Spm=0^aeYtwm*PL7LB+(dy7N}2=QOuVu>yEEOq=w~cmm_PiFVw`%v(Q_ zgz4CJ$lufVbVy#UB?IQ9Uom1-9WvERm&|b}17_6Dc}&?#JEWbze7(1@cTyWqwj+{% zbIV>h(M9|6wHHixz3!M`?hMfSnK8)QxbbFZG239GZf@_{Ug-Rdgu59*4XQ77XWohf zMqmGLR0Yw*94hF+by(4k5Wj|KeO*eu=mf?K|Ob{BnUIf{Vce^?vws zin?_%_O}#X9z&n@jy39Zi4N;YZKae3(^+k&(y1>pmEmIU+@Lzmmc>Zii^n!^gOO`u zAT9uocD>XsRSflxXrw94kdW^B;5>^Edp@7kW-46C9gt`~uNb*K8g_qhG!T|R+1Leh znmoEitU=L+z20vPEo+8=ZzC`oa>IBs5!0N8D_QAg`>vM;wTL~7f7zW4C42ecCmj!n z<|?{}%l-w8nkv3^pn2N*aUAr>D6GpH!mkYtaZn+{BOPL!TxgTligpITD)%9 zY0D`XQAPeNbq=S{_V&hz1nTrBp}((}%QpN_=}=MxBeaP?`~>U;8$pALii-xCz*cHu zKr>9ZW{3L5EaMw3ID;V2P$<-O(fu__NUO6u5o$Jq8C8{>U&kfhJ)*foxSPZ`N5p4! z3YM;Ru%(Ofa_#;fBUAj~j#mH+#|NRS#l^WI`jDb`n2ZNm6w6mCB_}XbR6K*ID_Z4r zp_k;27l}F#+pmGU*4-PER6T;bGCB#aPD(R!QTDaiH>Ul_rhpcZ_|#kf#sM><6jVuB3Uk?D|*PQ>L5}j!V&mRk+T9= z@wgQ~O88r4KL~KzBo<$!)5-eZRsp6%^KI;PKyetfAvt|}U7_bu7F$4mNTIszv=+QB zirAIPn{QjLa{9Vg&oF8=_1;P{PnPJ-#<-#M)&43U2Eu8ZwGbvt{BVLR`n1Rw*`CMS zWAv=)cwJ1oQT8@Ev{a*V@)k$@+*U9@I$q(^nr{Jd-g#IW1`W(k2?cNNqQ-iO|DX2X zOeX&sw_5cQa-D&JfJv0Y|5fxFW3)5xrXawZgg|EbRc8%m8CW~(dS1aCFVlx}XZ5&L6{V@9xN-_z3ox36&(ugd zSR&g6^MciWtvIj;_w zkauyO&HdH^*KA>3Zuuw#)?IZblwY7R^8$y;{geIkidoohCz8WqRz+QE31uo!HS7bo=E-j*qvB1DB^7MXQz8pt_{+ltQQs`i z3%e%iHh?}t|D7@)FxJEqKt~@sBS4+C@x?6W%RNusV#7HX#RCJWkmh@)ia(l-l zlj?8C+515Jqk$U|^zYHPseVLHU%mhxSL_H#RT!AGkA z#T17FZfW3q-&Y*R(i>k{@ro+D*SV8Wb`1@aC|LWC$#?d`y}5yJWl9e$hmrUzvtc1- z!CXIJk3yoQM+dz~9{5(-W{zF@Z`&5Q?GVm3`GG^13TqWEY&KNBN%4fMgIK8KX+# z)c0KKvb!MU$>+x(?38n7=gyuvGjryg?~xQ@Z`TU_KGkCzwZrU$8xBFTIC&8?x0EPUgUq=_p&_ahF}P$6#^5o?Fh!4owMJ zi{E61(9-v2go~KPXmn^*+xYPNn;@Sup%Y|6;Zn1c^d-uG-h`;^_wYEeLy--)_-D2T z3tr2`#WW+^Xz-d4KWYH|;5&|9WzO#V4jqHvL;w0?a`QMyJptk4SA%lqdXC=kyD!Vp zue#S@GsgpAkNs-2B|JQ~hI;CWThHS0;XPk*KHETk!BCbl?kv0!F)5$;am9v+2%umU zIno!lLahKIu;pVq7rexc&z(R%uW!+_;~LZ8mB8M&alHwrM6yr=lWZ}&;NT;8g!@5^ zAosH83enX~U~)i+i}k++HEB?fwPp4Yt5E;J*;oq+!@vdO4ltZFu8mFVt#A{cSbU z>MyM$-8c@l2|iwJryD`3!YMwCx+4qfhL@ZVl0mY^HjF(ylYF!p#_}6kAGv!DY%b^vSK6|R513Yi&PTgPNW#V+=iKvH|i*L$rDQ5;P3(Lnh$%|%9mWR zJ8Zt&HUdS|_g*d{#cLCh;slz}IY&$|s+TYhD0XiAY%D#Q0;hV^ymJ`*yTopRQ(FXd zU0#2NAi54Z;WJnMt4PN9vk1=6Ib-IP4tpSL^wP8N=o+}YlhF=M4-tOhVeV`;T%O!1 z8-uHQ7DGCUrur}2D#@7XRQAEtC*{J zvoWGd`NekB0Tp(ft%#r1DR}bBfhgr)-b)BO9M4r8qJN4rfp~a(NG91q61U9 zoz4F1e0y(2x*9pM^zP5-oUBI#n2M|Z7x$LlxJ#rBLJ=%=j$>9)_~I5N@ZU{?Xs zbTw^59O{H4OR{=P23bnjL7fb?Cb zIzimNd&O?lLnP9ZRN=HsP+^lfL(WK+tdhKUl?pLpGU1X%r5l`)qr%jtt18eb#~aAK z?q-s3d9FLkk832Kd?dL%tuzFpK9V}dM*`aL2LE>z`MS))=-}dqpJu-+n035!?t42G zzfPq{kOb2gI!TK@I%#@fe1Two@RGX~3(q?kKAq%vW(4E-Y(mJq6-EgL_WskP`yK-s z^Zrses{CRfmG*AIh+s5yc}KmQ7*Qv~Cm_XJ8%Qwy=ofkwpYIPLp2HR_qP@_U4+w4wWcbzWq9|3SF)iTm@@Q^!JM1adY z1CODIelK0r3zL1E!+Hj8g856gLPuGVsz(S^n-gbojxkK->Pw7U!j z^g}6kQh7lOjQbPK>qYF{z zdHLu9Zh4hFFdQu3sR$K`@9NW^{H&CfP>d&TVDG&D;Ol~((@+6x7jCGxPBo{ytN0ph zECzy}2&)*yl0E3%EP87H-ox*hSt;h#=6c_)?lpMGxsR7p;!s?Ra=XYQ6=5%78(FGP z$I>MBJbbX1aMvL4Jd}tzS<&p5a%O8Nytc(&JhQ#aub0#7RH32`a8b!s^?Zen9~9Jn z#|BvLBzr6sn?0KlnhBp>D+uaFlKv%YIE9V(o`#>C0<)K*=sId8ziaLr-o?N z5T(tsP1007)%KV>?VF@^N?hHOLEQsNi$beF@T6*fPtyECx`hX{inR*V8`f3%^@E;v zTkB8W!#*ob;zJeuN*xld3RKU4f83fx(`;Td-*#Jww>+s;tyNI})-9E5gz^+rSGq{+ z%lKDK_CSw_s)F$M3F5^UICy)4jhIgSgQYX5WV(pkN%HkMI>(Wv6HJ#{R$Gc|W2IqC z?wOJ1;_gq`wS1<72?iy9j{089AhQ}J21bLKe&t|8UpKpdA1?lS2K@ndcf3N5NV!;- zHT_8HLdI23@0JhH75zSxpEDb_dtOSP)pKk}z6TNgeyqzC!e#ZYp_C7J?805~PEz2& z(8Bdiw|E)_qkx`lAake5Gf9r4*kYO3p#J%hMKs3X_A_K%4xr`dySowpEI}+lDGQ?4 zFAX+ZqG|_QnYsBJ=Ebl)@H5;#M@*x?*cuxy!Hz!ec$PXidfK)1g6_aF*kELT3q;kR`C61mJ}j0yb~t^}a*nsZBg5!&vMF_0Rryl&9ne*;)FP zlv}Jvx)qKh7q^0$m-r3#>c7fEV&~hahT#7haImglw4L6H&fV4RbI{;NXPfo&R=ZBS zq+yn~T%!(?(rEA20(ot*fD)`z)Hlr7=Hk0qPE&mAklt-9YA(sOa&HsqjS{|@FmFAh zF5nx|^(GHIYJwYg(ovLZ7sW?V-@ECrA6Fpd@9u)a?)L7B<>8Qi9E#^%1t}ji@Wm0h zG`A!gCOmM?4aOxlvbPYS)6{0{aVOGu*Gecl0PDI9FO%?r{<9Y|sB54$D*p4v4cl$j1dO0zx6ggml?WDi#RQXeMHi z8S=;~XrOAT{Vh+>KlnSrAccSJH1WpCF9fj)!|kWyfP(P*>WYCI0@m3Krp%*_#jDWE2+-8xY^az|Vse2GB1bZP2^g`+Ff-L$aSUeiW zq?!1&?ZMG82EK`KWBz>dB^{#@0wOTmXmMIN>|To~zaepsck)6rR$fPSYl4Hz#aJth*MfeVB7ONLjoY)Pr6} ziqOmLlb>;=FT*htvK<19=e+{bvBOpo?sA0gam$?O44TCeoON#$iI!N=+sTLW+7aaP zYzhc=>mM)V?N*q*orjt@q??o0B0se4&$ofH4J372eLFLtsoGuyIAO zV-4Y#I23LHT1*-{74(pA&!{T;9%yb3pENU(qRMN~Y7BZ$x`e!nVGv$R)VY zS$h2lBwmCN^gO6*u)=Y#`~==UB*Ua!QLVk`Usot5af0s$rJ=9~{v-kr>Y`oF3x}Zx`@PCyDC5a?nlCN@r!!-o>G>K-R`q+<@o%F&i<~x!jCM>qHRHvvH;1i+y-;JX z@54S0g{l-F6Yo%N*7}Hhiu)SJ#C5R36RsKxT2kAmw2Hs72a080j*j<4DWcn*&3D4` z|8)JylPN*8{75qP*@$&V@s@$?pc5uagIzqQqhQ2K^g*=`mym^nRx+!(t{HHnW5|bl zEKP}YJ@~>YiKmpWA`%bsb65V7)upwt`ghzYrHCX|Wb#tONF(CLe|&^tHc2`)^jT_{ zB9^Rlk?tpFa?v~9u*|HSm-1Pv-K#-Tt@r|aN`cQ;$#3S?FYF@>r@Y5sk@GK`j-C&R z>H*>Jl6UDI)mhshF@$&B99E_5m3T=^n~jm8V*wJdj*V+A}K3JQsG zCASWijbQ0RlwN04$L+8ZfMm8H1`R-G4YDet>FW3<;e>F)nvl zJz9XcDdxHRtT!aekD9Ha>6f3>o=-dD1$%neY^v;MmcD};x>Pwk4IQL&jFlS*o5+fN zB=o5gEFEw^^(+cf)yPLHI{h2O{1Y?%?x%I z2wjghA=q+_bR7g4v2>nv56f;Ipm$D^g3GGV=z!Zivjz{CI~4R)T-5~p@1yo~=rTx9 z^f-b&;@9YcAbA6-8o*$jZ5HfpyWml8PCeZmHc6*`syTV{kTW_TXt-r>=$U2%%{4We zwZg@%&V}j`s49TJFUZh;XfGHva&JVOf)v0E!&gO+L1RFXl%&^)uL_?x&}V^ zSB9dg5%oc7J|c~;-%_I4uwbS=15!)dI0!lu8Y9E;Yt>dHA4-Lx0X(RuyyT9t$g{MGfn}-ndzAF_(!j~zf ze9jMyVfmO7l>TpLemf=0{%}ta3?Y)S{B1)K-UeN=+hTCkn$)7U8J5gq5T)+aN_bv^ z;-I_=&@l2`C&@v<8z7mStpdv!e;m-^;>yFO#snJ=oeV z<4DB1NigA)$Rq@I;a_*SaStXqL`(JkzC*c&Hac4O)(w+abGA2^1#R&RsJpk;Xh$Pk zcboL~LY)+EJG{31kVZ{G$w~Wx^(*e@<*9%22Wk;jbkFB}P~`%j)ie|9QtI=uMVD{Q z8L2rF)$%$frH6YFBg3I-mazJ?K)Y+WqI6Zdb@z>!UM8(@*b!d1!ZOwP52LusiaBp!6=G z3zY$Sx(E%^Io2G0gx zHp*4@9`j%|UjOCLq~QBhOt>0c?g+shBfC|u0QaFU9Ni`-^p5L#kXYV04%L5Gl2UPS z#{dL1I!asafKsu$%=`dey1PzJwtiqxDn6eLskzu^L0hXe_3$>n8u;g0I|>bM)yOtp z$ZC~q6{ueVsRg*JckXEec^gP{gu`smcGv5)TWUm?6>kHp-QEkcYkBcN2i?3V5V)Fa z);=((p4bL{taSEqE|k^wONsfLkqD_JpquH23mviBh5SJ}oX1+4oQ<@np@h}=Zoe+c>}mAUXPDM#H5 z(9wJ`Yn2Bl7}uXGO_p0T&t+sy2)K#NcN<4I;{;Kno#?dJc*%0`7LEAsJcWA z@aeuBru%fyo>O6B{vZnOc3Z=hvh0pPcwP>oXJ2+m>J><(58W=^E;6a>FV4MMhEHP* z8al4V@g3+p;$()UJt)o@FOqVpbNo(Hf~#QL_}xt<@H;a1HP8Xs*C01Fe*Wg!=D9{| zDa~TdyxQWVqpEF1%`RSbJkqS*MgU{hx%q9F#I3InEn2JL@VFYijuOz+EFtwmWQt6Qw89q0AHph?9_hBf5RtROKJGlU$%XRA`NTbMvm3i zg9|uXIyuITs9ppAQj=l+5Ax0TENZ#t2a2}kv^ZCzKy7cMl@yGpbnmtr*yP_C4YgN% zOR{b6tVX5rwwhV3S8l0*4^=xY1yU+Rq+Lm!`CxKi$*}?J*1^&lqdfu<$7M{{b;D1D zLjg4uBSvl%P4n0|beYILEf3^tAo&E?MJyIc&tdG! zbM(^zCo92@IYm6X(DyL4F+WSj0S*8kaU3aud2j0bA58mj z`izf0o;mB2PiN2h?DM(vzWDO1ufLi9?Sk(XF8Y4)4=sLUixpZju}jy6>yNiR(^@?r z-E_$oPN~8uelzv1O9_Q=)W_&usKDFv5Ke5WP{LXTMwhMU@O}v z8VuVl@)zBN+Q*_)1_kou*(bz3XyN|_`H!7vjuKlOqYnZ7l--Xgh`8Ze zHF_b%*m~2!Hm7lqW?5Jc>$bs(&*-&%So{{WAhbwu3L@Wi-}m)rbJ2%e=mL`6nj}ns znHhQ}*;JZ>71+O$IUWmV`}e6BDZ7Q7 zjey;=>z;}KulL6j1~l~v+iqa(^G30pQud5xX-k2(S9Uqb=14j3UgN|sNdP>iYI@$c zK?V=7!lT&%NM(F8V5gNLb$E^GE|GyOl2Z5@4nI#61w*4TO1fMBG>UdJW;3z(-CnQ# z$h-d7`~A2BfAY%||1-$nTf}NZwVL33OHxyi{ww*Ggs(=q=FW*>_(V1797(BmQv7x* z`vdt5U;6s*YdVrm8L&PVuLiP3WGgfN1{}9ujjkFY)D=U*Kg6g7o+NVU3S<2wG^(l% zjG+HpnE}xsR7EmjC9_p8_dR2hwI1UeY@uo`sBO%M?QO%L&EQV-YXcr!B=`AMl`vV* zGCh`&F43LI$H$4eKW#3FaXG^g<~oG4j_$ouL{ZFk8Q~ARTp{y$((ZRj}~lJ=oI8UU=d~&oC6_wcr28$Lry1k1;#+zeSL<2iqIJ!8I9U5_jSY z^k!7M!S07(d{g9bSe3FLq;cpleA*TK7h!-_eN&s|V@Q{KKMJiXFlLr*K2L02nY78v zHluFO>CqS0-`h8NLh9Jd#{K>zMysf5!)xP0xZ52mI3^D3R-}~Paf5=}uojU~;V1p= z7Nr&|t&X6K_eR%3-c5(pZY{P>gB@mTBCpQ3|LqhF_snglY!b=6`}FPij!tKgx3;E9 zz`Hd`=x0QQ_+}5umH)~RX+n?W+g;(xfpvK)CIj!&cg<#=r^FQqfeV2UIkcSH-Qg^r z?AF3|Aj4!kU_U}t;Qhb5(RQ+eCk$P|FIY>Io6N=08^PC{UMoRyR{_;cw)iio`ec$yzD zFDGs@%99@!?=Top`{dC_@EKYYBuz6oehj$Vk!Ymd?Yu&dkv*axAoSU4*e>1DH|Ll6 zDDrEWx^o}j{C3__ozM0Fic^-p{ z!LP~`YJ*HKm=c;Q;yC)e=-BW>+y$6w;j72cVG6cbSuma;0>{Yi5t4@v!nN3CijD0b z1KhSzRaR5oG5SOv^W>6XCiy?X>1zI(zaU@lWcDm=BmFSwBN14B}d9)Jd*E(o#3rFAV zaB;O$7$JF(s7L%(!qM);7d5X!$710q!__{t9|j&UKP{rGtoMu35is-DKKMNs-o1>o zuNy)Qp5oVnd5NW;wLJ9W19SbzR)j=6-^cwvyp|yBVeux6LUEq5Duit`%7I$?aR&6Y z-kW|CT-MQ(BAf3vXCsK?K08hB$}{_Hzk<*kN9ar5a0kEOrDFow%~*vPT#ycvp^i^} z>HzLB-Ow@PsykSnCne>;?d!>2Kv$E}TsXN6qwj5FNCG)4*&+3y$a7h#B@NhM#o(oW#y8l|{TAej&yxN#-C)7}&ts81*=rT1u zwWg5J%q#V@OEmP&bqbwU$Xc#yxkfjxeXI1^8u#x1Aat1;`)G1TBPTQ_bZ~;MA|2e( zP^2lEn)0o(NdqgWeQI+>>m@q3+TU+T2GMha$xatmfc2%jx_Iw}F1LycFu&m=<$5>YB zoPf4oJO)p~2Oc3MconUb>@??`{y zpMD>%GV*57W>wJOoF5YoD<>&}CaGUt_g!*tk$HIM z8%y?Q;o7IbcgXa?HOVx?eAB#38)2U)N4c}Z?vH~|vOm{L@c>?%)}f{b6e;5MFvHXQ z%;=3DO0a*@_twMFA0y!5%%UZzCJ&$jxI3QYG?P&dSCrH19#~Y@1Jf8;v}{qh{r(|k zT`9;kJw86Q&xC&cA3>`AQvN*L=fjEgKr(DalS+TZd3c%#L&8aK9sN)V14A;{_f>!O zEL#rQ14ATRRslk-W}1GagIvnTeU;-0K6+pMdypG%lT8irYwBzy+DF~YqoCDCTd8bQ zRdXV~py{l5lu{j`_*Cloyf>%}^U&uCwu0O$Xw<0l(pFe<(e`hQgiZ_>Izd4rW5yvk@L=mq zo7~a0am)9Iz&Y7VqjQqY4(GkG8)dV(*A~~EPR{Wx!MK2A*L8SfB*e4yr_Q}wxC}y; zV6ed4!FTPG?M~4w{anJWvyjF4!i0HIeJZ;7{bwDjy5GI#4kx8Ub$nw7Jbxm9+lv`q zV(toEFc>k*%ION+k!4T1K1H<66P{hhFJ+@A%;$2Z%&)LK^ZTOT9r(QQLzov=V zy(!f)ig%|h{ow|UgL_c2_NS*?@N6Dyea?)GeEhClmNz+Pyh$D8+$#t{Cfqduy_1Dl zT;6ln4j`^@Zt^Zi|BC{+kmbv12gSZjI(xDQd6@%+9Wrf7zust;)q$*8ii%Q1x7z{1 zjL(nzBB4X{hf6u}gHhRL>xzLDZ*zzA9tm8{e%RCj^(Hs}Bc?;-O1;j%Y;NArhw?pq zTnEC;y;Bb9q$BFa;lxKqkhO5m;+kRN zWFZRdTBtA%w^Q7*Ky$QFhX%aDds2Pqmqu*mRFgn@sU@BP+8)M12kdmSpSoE_2XUi_ZUdoC5}k$G z#-(i{Yev*8qt^5P#d2O_Pu8{#Sxeim=H;!iLl-f2jxnGikcQ6&j~ zhsmEvN*ZVN2^!&io2IoF75GsYkjWW~&Jm-n2Bo+0Iyn-$-WO+07&4Y`Xq|jKVwLzs z&reaYkql?a!z9R9M2@rfL{FYVr$#C%=`$$%HCSULqId7CfN;@qB^key8M_o7cg*aw zMO3bzExCb$C(9J$B9-g&A-zw(dz@elt_b=oZ1>Ykk|^@%HmsRAgmj5wB&kbcr8x@z z{`-<6wk=De&kr;EY0&l(yWUZ2&)+*)Po+|KD{ zcd^*Tf9RDz>sNEtppA3*YB`3PZq)8SAu5b=S&qTayYHc@$u(bCZ^}$#7Zfs2^a!_! z8Mq9byL*Yft+=aQB7WZKM3xipI;uH-Y;6diNai+cu#07GFoO5fOUHf8K+-V~2pXO0 zvvd++9?@tjL5|t(VhczieUln>%)V-gurM5?4hgQnm-X;FssURLvKTnWV!0k8rk5rF zdnyrJ2WOgac?aMiZmJ%k3$hPEdcCkg!Qmim$+nPy18`+>wwb}9`@$CQmNB7^e>$-H z^`6{F<--NLm5hrkncfYLOH*ApfSv~+WQpt{#z|mbnBnc=o~_`LMW!q985Uirt0hC1 zO{=K{v)VKfTlhGUjbtFLi--7hiXs>%7?k!#oKGE!`Cxot_1H{T}J;&i-~ z^sNMfzPbgX7K*iv4I4bV+HYN?^%Xn@+SX~eco$T+fxn?We_JVOHnp2)TAk>+rnAo3 z{4W)1$^f0p=p0cVd4$04>3X@M)!jl%;i^5ltyU?=fAIp`W8vi3Z=ae=Ux!1bI~ecZ z15bPZ=x~6%ehJ1EqVb3Vf7=x#=f#ZY4 zjdcTZ8vy`6K~e`FM?aU3dXI671z$-i8r1ECbGM=E^#!C$sj)Y=o<&N(j$v17zd(|e z1*v~*ncyQOdx5!Ycd2*CAq>VSy=rvqJUlnjj|zj_z8JOMdBk4$DMLiM_(CSbS17_4 zZZ{x_foK@V`+cdu9WVXSE`#5Z1$#{iKmKJIh(49_PWeM~Ntwgzo|C5|+Q%yP(0Qo9 zh`3$)Uec4?@xw@Up&Wm0QR*LS$fJn|XP^J{nq^%JlI?$ok0wW#@WN;9)RvD~;dH}V z+I39Ilwy~qA0Kfk>h@zT5#MQ3NAHe=?-cPt@ZbZJrx)lMMSMWe0ogw6B#4AGnhyGP z?z9@0oSkrhPdW!n#Q_~Kf{X#-^~>*pvF``D3vgm1YLITz(uT?13cG4nHRo6zxmKCD zp;8w!W7)sNx~(;%$<_>ymd9GE`i6h3wv8Kals2u9))6fh4%^9;3XS3RQYVM3@qk#_$ss2J!$;H4#fPC%!X}HX=-r|#q7vaC zz3#_{Y=U8->5d<-uLd(kCR;kLh-VHnXi&RRKOWDl$s&Tr0GFXul}>uloA3Y}L)M^@ z#In%T`wl#TO-hdm7}T4tevif4iXr#&;04X??)`AEn;zMZPTfLbfRx<6kL7ha53b16 zk0*sEAk!$-om84|NOx6KQUIPKY)D}`oc?ti$3@%~G&hB&9utDiXSs1)(C>F(^;)0{ zO~pa9S*~8Vly%vO`xr~Lw>;EYnPDtmz6$i{)2E>B&H7v53vDspO-=U~kqEWd;sR1& zoOrDvws=iyj^7kML#*8K;|fK(b7>A7{{FZJ5z^hJZ|oL_)}Vhrs|HcokQ=?+{2)*- zZ(?FDYuhUT+vcgR*0i5&8`ay;(VB0LT4?hh1>n&k|22ymM1H$Jb*~Yv z$@X^DX)Of03Yv~Lf>Dd^MYMruG*?O?uQ?b(zTE@S_<-&a(JXD(DCoFoYJD7lJ4xd#!7M19|Gi!1p0$k*xtJbB~qWxQqw5r5=dgddw3BJgVb-j_kzS< z^KQaEcAJZRHg3N58W`?$Ie!j9aLYAmTaTojAVK?KYw07SoT-T-$ujH2zsVhy{Y#T4 zD~CmPFkjJ##^yFXfawV>C)(fABt{DRi&>X zEE&wN%Ze4(-}kaw4)F%TeaU|+AA>Z#<6#z~Bs1%o3#Cwm>jm*EeY& z9F|DE5ya-=9*F-|ltvp#HV;`SNeTs#dnA1ZGlSg#J z^+{nK7>~&MD{W!6B)*@!$?c9;^-p#>2qPXkR|UXP(Mnf%DvF)QgPYe zt3u$2^se=;X{LRBTu68a+dTTl+-uozrVQ@Br$!=md}jPe8L6<0>h_`-BXI00Kz~%* zQUyW9nb(lhpSkvw*6f1V1 zX!8-|Ru5@)KG+E_O|qVYz85V`u-eNbSNbJDrBeUq=Vx6mj+ELSVDJWTgpWLYk>s&h z;`bQhEpj_(jYb;nM(0CPI@n>4NalB@nuWN*}glw z+PnQJI@RNuH^LoG!Xv=imZVop&YE@7Oqcv>Ue+j_+M3W5S8d9-Q>L|5qlMlK|3=_~ z7FN{e%dW1N-R*j??Zj=TL{p+xi20LEd)-V*l};hDmVYiJ(pv|+FOhR6e%>>Tc}(p9 zE86`mRL&_U>at*KHjE}C!+)`%pNeY5LyZxWPBU(|A%V+qL`NMjGOmLxeZ~%Q;WfN` z_X%pXx1w<_H|~;jr42^Lj*yaZ-OW<|W2N3`{464ikY44;NDK^4d}Td+W#|ADr=M<1 zMy`-!kScoHYY=Hm5~b{ho?fAn4yP`IVjow|`AO8~D`&P^Z@U#ymm_3kq0%4LmV=yI zxln5=DrL#CI)Sl*+9~&w#GxI&ei2bSo_XEOVu)Pnpnf_n1wQ4RJ+mUOl@T{e4lfrW9?0pX7kRB9hF$snjSr?)$%BXqWy&N4#;vV4(pPan6z zzUCNii>~gng`iErX8}fdrd*;ldrjDVh!y_H_Tx}VN-Pg~K@DA~*K@O85k`QUqRb*J z;`sjeF{9idGuUQF7kq2y2T){H*0ci#Xg|HOtN3tlK zr~lLCajjaaRrYVKvb8#OR#R?1=vsfR|8CpNKHp4o%CmMb+&APf5I*z$pd+xRM_nG|6J_Kg!s-3^Rtx*j1B$5RpNuo%4I)WNPn$(CMkj`)t%fZI%yZ@hMM_wd zR8n6Bad`CO(z{^1DhsStnS}7!YaqN&wFe%r)ovS!+N)1bti^ zzkU*tjy4TkB=pO$oY(iIZvCL|{+)Tl(_sk9mSAA?6qYCu!>y=O7RuuT2H&U-kP8ek z%=y8nPNYdzWrOiC({o~d3{ij7VFgVp{`h7$8RM8d#b?gFBq)+dZw$UOa@S3$w!usG z0rrPV;Z`0Hsg3Cwc8BSuq7f=V(8ixKw9fdweg&QSdekTUGdaRTY7asYsLld^x@An5*89?W zZ3ARo+o&3}ijCBbiMl{c8d+__x#k4?y`~MrRie5~_>1H^B1o;8GIRmP%Um_3|7J2y zOb6Lia0Zd1vc17?4g$G)SX@MY*+liXNce-H%ex+-2P_weJgMRI1tJ_1x;>c}0D&gVb9Nia5#@y7IY%fT8r)I%SZgva&_?l? zJOg&xf~RagJpd8Xw>WWh_y*t#l~}x;1Y+MY(BDzuis@oF)CW=h%@L#g%EBlKyH1u0 zL7!BWLylem7pg=YAHRT0d*I5)6Q(aH)iVfy^)#M;{*jMu^B+tL$mcE9&HY5$MU2Sf z+Vt8bf^UcJQuV`##!;j88mZY+{KFQu4-Jz~ZLiQg+3MQaM#XMhm(e=9#`RKT(QBrL z)s<;xhr|72P>a^zxQ(w|zpHBBo9o(0XbeJqR@dvly{y>)@n~Ss2pN8fQWa7BSt_z) zjAQykIEk^K%^K4ij7B&eaK72i4T?UIRJvS$KMcJg?#R%muHOh>vh6F>LZhF;t2a=- z6hxBp#~=!#PMa@B?V^;)<8RM9aRi=hMz4jFT~5tK=YW1Fs@|BDS>HJi3L&$>=>3>8 zupEw!HCjt2e*fGTqzC!cF|d~jp`Y!BM8>~gz|`CUap@r60>>?1S`hrU+9$9Fx5W4r zarNSBr~U)GwRWezvo2FH{6poNQ*fH8m0}K(S}FF9@PD6@&rE2sThaFlaHRF0E*f-&$VavVFc&i2e`kDsicjx1syf*TZrd32XoXQ7Zn zp|k51+zW!$ET8=4TE~ERSR^Shl{$q+fXFc=0S+!Q+CL=FiGrv6b=bykexI-21uJY~ zUP5Rnpa%OI4&6g|Gw$|H*kn`Sj$z5eJ~Q*1whX;Wq`^OyQ4b+!Tp5LB-Y)0S_0fD^ z=Z|dhciPCVp$s2o!1ndvBf2rRuv@x~KJMK#MB{7y6Q-&W*vFpMerGn~MTVwRkz@D0k zVKr;uMkBJf5_EVwg_`_t7ja$qi?%-^9MN^(2sR8(8(2k+|Oi=k*3OgAuSB5CQcrU7%ayjeJMHho3)4W z`aj5%4#854aOV4Y~+ky`@?msp#D*iOKO38P8g(ecjZ3s>=Wrq6l)sZYXaNiJamBuB6} zOTlRe%8BGxaDE-wNfCW0_6z--;d}6Oxk}6&<{rIY*rn#WN2c@wqXJW&>|ByVkkz96;@xim`b$Ch=e%a6-Q-3&mpDZW;T`N)NGu z_Q4I(pH#UJi}b#m9wB1%nFYtB>{-VuI8Nqn`c{Y~j-t$U>MEx?GQ>tjVPj{!Y zNg^8i)Pf78Vlasg1zoM^yWmQqRN`&W1Al(57QRD30jc9<1Nt>n4QF6fyaFjuiJ1!r zRml=I3n7O(>>lq;MwXk3=`t}(yAqMzwj;ND(kuDX`s~~k zWSA^bj7p5n3yRNyzxX-mksz|1E&ukC6P;qrrZWWFdpV=WbKpi|2hdI$?VW`16hRX9 z%mU8;23%Y+>iT-b!NbVpy&iX2fFUo^nLLN-?8t%L*C`6R3x>+m2TpxJ^jwfEb;_t8 z=<76VhCwdU@euTj(FBvn^(*?>tO%Ze*#5Yky7)?0Sv>=_T-9=oMmWP}gz6dfbPFLX z-cF)H!ACXm?G{QEg1aWFl0P*rt#j2Ux5)HOYv7h?rT$BOtGnMM+Hz^v@QUs^Uy2GV z%lmK8Vm7f?KD{v(kz%|!&J~`>5WH%m?OxMoc3VEeePOBm`I1wjtuX7Fl$({V##ezEkQ~*p<3397Z2md<_kd zqi`CeT9xT3JBwG47;^S5$UDxAAveN6ZoZCwMVBn(mS4fZ%l?vO`dr=X2Ea49IGqZ^ zzzLRToDWLMzNj(iSy2t4&Qn-=wUWg)m5W(Az~<~=maZ{iFE|Iyjx*)TzU+D77%Y{a z0?1#3$Ni(?!*<`z%we-LVD>r1CcCF-YS(UL1dX4&UKOLOR9Be}sn|xcdRzrHYPBR9 z&Na_zDHQ572CWk5sQgD^K6P!bw6;%81ubu+-nvI>AZWHbQa_G1J9z5OwB$F_RL*Ps zg|@U8T}D*ygm)!s8_kBKt})Sl1<>+vG^=TN^|pPg4Uqcqu&nMe(I{;i3LPnp!Kuw& zRNIgiP^x1X%1d1aZvIoA<>HVT2ruJ7dMpgR-CpIvjX&@bpjHHi%G2n?V%Rnz_5vK9 z`LW^p9dMxg=Z#Vjqjr||i19n7muxy9yGj%+i01;Mh;!wFX4FzD3V<_&% zzAbwNFFuN|fc;zN>JfQXU~`bbz76wOjoAOMe@SM}GUA3lD>9x3li(iH<{J+%s@iVEq=L@al{6yuadK zxIopg*l8w;nq`EOY*Rr~if^m6O4ZW=S{ybJn#xc z4{U`C)!<1xne`BD7hxzZnv}f-DiJNF&dX_LKHy{0r|&%BiS#eipB#l7*qzjJdc-~I z3;r@wfi@bKNb(-Y89rXfONx|tKapmuNSD8=DNm9C8;nSwOLn+-G$u&}aONE;?_UI| zT?(WK*<$fg@4xOzPWJt2BJV@39eiLg`FMQYiy|*T5sdYxGjN=Ee;p_IhPkaK2HGN@)X3G6bx$@HG$!h<2DR@3 zHA6}?IQR5#%ahtP+9leD_2wR=JVrcb<}X_O%g;aQKO(9M!rvF{mFP`kg9sA?I>ju$ z8{s^lzr|xJ$!RwTF2={?H6jywmB@h3@2l9~l@;)QqoMl-vbv+2m3UUJ5qc3HHo@Rx zbgof{V3WNh-JGjo4)4Vub3cXPpRs7#!{r$yIdLvZAF`OefE7uQCN3v$47UOuj0&um zGjjk>`xn2%97`wDeo92z9N6K$qh2|PlPu;L1OgK#VqJL*tz67x&>ZbEC9bdF13eAh znCWQFj~eyER@g_T4y>3hq9aUJ;jfRc6$|dX9)b-g(OY4aFewLiKt5dA>jOmhOqV6% zcj0SAn4sC!&oOevenULNR0td5ne(vMX5RUeS?kgq9!O$7eW(;KO9kPcJ#z2-fCl^nWhxV3dzpP)P z=S@)2{EJ$>yS8SzVZ+*{pIBpwZW$=L9+&sS>2GcJtpqKy@_>PZ{-=ir4^fY|%wr%d zoon=UP+bAOSYws>7qiV4EUk>z0^d?W)5_Myl-kn&Z-KkDdsMVuSF@m{w{25-8wa9A zGW8K!MK>+QOKH^`im##kq-TTS3Fify2HxxI^I?qjp{=m(0E8A|mzd?l;4!!}8>^jUAO8?YL@zjhE4 z5WY}UMV&)?O>|62^@I4i*SmQ^no-(iu&Mm^(ID(lvh=0sS?}wea%ZEX&IYQ`3lT*w z+=qmDAx^Rl)U{9ZZ8uOQDX^~3+D(8!q6cRm`ufFm*JZG5!797Zc@$JTWaAkN>c(!_ z0POhe;OP&$VpHoKj{mr86ux-kVvm*hUZnOXd17}f1LU%8woA!|uEMX7wF&FilZfBF zz)yN|H{9z=Y%a2u?+U}cH4g6j;%DYhtKot}xf^UWJ5N8}il;T~td|=uBRc`({o(bg zQQ^op^r2_M_5ye9+FJVtS@FNhr562(TW`4a+6HqZ)WNrzumROU?EzguR{~U`x}W^2 z8+2@7`=Y^HX2@%Q)Mwr0$}+O2DeZO|{Iin<5qB4E=yqK&rrdsP@;k!0AM zG{4zN=JB)KR}v?BGN-Z-%&vFKFN2i1>7xUe-0gE6m+Dp~!0zumYO??QI_|IM{J8HA z&Mx!^S+exh2T6_|Ho?;)CL;g68jrl!z@AmqjoHY)HO})Jm>qe!63*X;b&zjcatIC# ze}Xnjhpj{#oP+lR&YHvWt1ezpD!rKn$A1(hOAAq&tn9n2;|t6AoB1od-WHlbJ?IP>>AlCqYYHi9Cy8Io#t0jD}JfMP)gs3tZSSR$b6KnbT3?$ut&#T zgwwd`iCpK_@0O-P4-7q+6pLOF@H%N#>(6k}gNvZItzLnZZeSgtpwQS9 z6GpxeIK)2Mt|$kB$1Ie1M}Ym7Syy8~{&_>@DPn%3|EgXP)rpTYsOZ`6k{QNS&gfTe zvL_Z2MNb(w2p!i7(7A0wc$x7?fH+Qn?onC`<)t7d+D%m5`O-7}GV1r0VyL`ulYE(ZP6*_vWj! z!8DJ)9u1W=!h^ZtjsEyaMl*zNW6U)D+Qg$ta%o-BuFh8nd@IylV+)PVLm|Wj-`GaD zMbA2e{VJ3@NRGpxqjS0T2=~!MuRF?=}L=d-)xSWutj!LA6OCq(8 zHlf`|v(KfM(%`l6@aNK+KSIi3zHr-2?|@N7wAc%eob*HK$f_B1k`F8|0~Vpa&KR=A zViGrZGdka{Fz5CmHr^ZZT@XyOC>e^;*BId<-rty85P=w$g2&P)KeKUEpY@C^nGQpz zpcmw>Xw(naJD>UHGenr&j?r}nb`P>M89?Xjvn5Kg@Ns4^;cD|6272|bEkPvx?Dy$? z*q+{*;a5{YOh1ya2ZZ>x*rAci92h3s&v;VI+|qdb=>q{Hw}tgwXE0#ysXj@+&F9uj z8ARzyQlfLvRE)OvLe%5c1NdN#!<$=p9bqNAYKnHhxPERmb_YH3<#%(OyNgeDm6e}Tj!82kL2v^w-4Hx}Fwrfpng~Z-J^5_^Yk2xh<|3%_~}5A#|<) zwS{WpUpv{jaJDy`JIqcel--MjDX5;an^tK&=b z@KK{7yD>(5gtH&FdSF&Z_wLo~@qWkt!gE0~c#4UBhJ~OSe;-4$6Qd&W;aDJ(9+~1f zx|BLK{bebPBK$dWV-t8wCXwydnesKH>luAhV!Kwl$qVe$$U5;QM+(w%V5xDgrQ$`0 z_^$UXMbc^p;WO>F&5C^m^4K95AnzU}y+=vbyLdAwGOqIW@y>~*AhL11)3w9{WcgLF z=K>H%qa)8D^BNdS9gUW~n{OC})=Ggy|6}rPnOX2(`qFd`8vyRuj|85=7Un zW4{sg{G6HL6@j1zQ>2@~Hr{|t#L>`?Fhwx={my+4@?NosfL7N-a`4^8&SA2lcRS(Y zIv8RbSOBA?HV2o&fyrc|W73FJ{ksmnOKT6zs9@7Fb8DE~x+Fs5C0?tQP5wYFlC@Z2 z)^d&RXgh7#4G5w0w4252jYf6&8=|T~>uuvqYMvps;~=09kCyzQ{uG@bIKDgpq0#O!WM};)Us+G)UhhE={k8dMV8VoO6S`O zz5W%jAli)_0pB4W2%k~h@3nCiR2ZDu4PnFPb5&4P0eK$=_IGnZ7LA4-j^%_aCoFxJ zM;?8xN5|5gJv**3mDO>;bX3^?LlYTaGf6x5~pLd*SjDx zSbPGV@_To+@eZ#)5Z^f!=f{UBgd?98bKVD48SuA2`{&eg`_X3A`2W&cJDzPTX(Ju) z+r*-7l`+<2;wF@~56=W8+c*AMUExA4?x zW*4?e9zX&^|9{%P11^edd-ywdW@lz+ciE-GA_yp0KoNV_*h|zTCeauZ6ZIvgJd?b< z#FUprIw&0!X%@tSqJn}25l}!x5YZqOM66&%R1_76j!rjd<_YZYY(?Muh-`uH|Nc<#~Y#4a3qu-ZCneG8a6<$h((N1odv<`yViGQ-2A+2xN~N$x-;CV^iKzsF=`gc*MxlO|)%2 z2^`A%9tO9o@H7d=w|5> zaT;RCQ_0eMaK&z08c1IDDI|-HR{Sro6xNe*6RVw|_BIdYZ z!q{#`QEtSWN;W15Q=_5om3C2eD^&E~ot*+MgM1;s$@01B6SGzGNl)uk^8zlG^(4e# z)~1hgkAT5h6sEs>MAkEA!9xj(lF?xN%Rbw6Gp@_!vnrjdtYuFO*JYt@Xu5R_nua{X zckE^$WS)V}t0XRNO zB+DkUkij8m!6c+7AGQ?l{}|h=`H*}L$=4FMZ^@wdZkm#hwmR@RX0of2Zn7oY0L{RH zDc`sD+us1R%1V>mfbB!J5i?I=mEmoue_u{2GHQ)J;~-?241{3!}QoBUMjSWEz^TEw?`TXWVk($eciq z{wbMpkw0VV9SVAk%$+woBiR)TD``wSA4J*RtAPy*f+gsmTXYg0JONMcp=$F1K%zDH zwuxQsw6ay8C%ehb-kk@}hGl$1w!zT_7%sE21|$Y6J)nYL*~51rJqgiv*LrF)^}D>?V4`#OJIh>KKJk`8wy8#x1Tv4H~ZAv>;8D zrb27-UTYm%gPZ9z=m4|>d$-=)YKuk#rmko@TwfvTi|DsUj+<>`ZPt+ov?368Nx9|3 z@Z(E(-XPC~08~4eAwhaanGl*Qhb-DK7cCkC;HL-r(j&GpU#_-BZ)P&=E_C-ZH>AsH zw#e$i+dd%mHCeJzO}-s+utzdUmtw+swurH!m%NK-hxA~$t6u@-G)ZF~N%sR8;vo~V z*8%6xNR#EW_O ziOJS1y@ScS4;kw1aUea%%*W3!khl>J!%Qn4u`DX!(R3|aqE8FociLb`Xbc{}7Odno zCp$S>$mj9%N>g0FL!=zIOK=Vs>#Lh*jx1>4#y2-ep7Iy<{>cGax65Hp}3Af;0mbNHhGs%ENso_$H#NTZ%xYKzOb``CPV=z6}c^49mUWsQW-{lVB z5AnaNWWerDqw~6dp!Yy#MTd@5Zb0Y}Ne;DAvH~uv@h;^eyBn6B2WjLy!%V`JdBc6D znR%a6uyKxG-vs&ty%okk?dY2e#fFS?4pPbhu#w(ix!hQwv)979bFiZudxwuSQ+OL^ z$yaiQ*9;J)qs>YLm&Y|f5KPaGh?YQOYV}N#CQYR}B}zF}??lYI_!txKU7)s89P@r1 z&fEd-5tt38V8WUi?B;Hdtni4;N1NL84q&5AyX*mrr5}@TZG4Oat?9>htdJ~aC(ZdG zqFd&Wlz!3}N$Pm|DPgZN-grN_3yTk6b6qzNd%~QvULhlIU4o~C$)Q)^#pORe54Pa= zSZ$Jj8VaIpzXfY=M+__Z3x(9Dek%=RnEN4;`M9mR^Ick3q~1PRJR0rXm!xOjN*=W((a`);y`YB=Np zOvSqtR7)>OBBZG(C?*$Th&Vsnr|aS15cy)+dVOCwGS;NW8u{KifeTQi3jX|n{xY-a zNsw|ITw%S_Y7~4@V9?(TdM5d7z@W@mrW5QPV5jDzzCa04aECyJyX<};kYqFjxTQib zF)z4CU!f}Ttoed=z?^P_FhsUP^fw13D1v=wR!rXchQs?K1TNWE?wXQnMBQz$sAAH&$5 zV(d5dW*+vryvXG9=WMjz;>Z`U3+00TM;{%z*1&RitqttOUw-whAx&pU?fs!$YdZY4 z0yfnZ?zCk&4TA>VDVib8rN%ukzQbGTCkEDlp>a;5_dy(`-bnH5DS<)V*$oHEFrYF^ z7nyV22{Yg4M=~x;U*88MEjP%aB9v^wk3y15y0DL`cNOYnzAIo|+2eKhXpUfov0s3t8$5$a55VcM&moPqK!x-{s^K}~ zdOtSa0jNV(CG*4H^+a7X+WZUemSWx~>lu`>*Wb%u1ARRWl`a;p+c8SeulMGzmcgU{ zjXI8ZJCw3u;QlDMHQ#$3K%{NhN5^f@I>J-R9NeqjNK}gFdY8lC-hd-fpkI)UM<2R2 z3Z`&n(conjYR1*VW5vEH?p@H~U_}=zde162yW}ZPs%J;eUeDP`JC!>1Stk!;m8FWq z?@>_yY@F(*U3ITd>mF~T*sRB<|9d^btT%GCrrt-Im3L;wm)%wG9CA+b1q|M77|Cw^C50OJ;2ccZ@3;yc zDP^}`z$*0aSp&=I^PgC68>1d0-%qmpV}3B?aNq1#A+E;Jo zDG*HUwqv08;rAZGOK0D4_g2AW7g)^)PCjt}uJ#Q_R=+~^``IFRnh1f$7cr&r=g3v- z-(qu6{r0bWwLwh&7;mZ;C3Yc-5D8?*lvB_B0C#>!YsN2)yT1vkTNbpDTt_@>1M)f5>x;$DuB; zjhS1_>d92YDv9ZeVLm0)-e$2qy2 zbf^9#eaB^rBI#+2uf0jtCm7C|Fm(Oc8Zb;Es4s9%>1KslLFZqZPZK3 zXkYtSDOs>jjv>CwP?B?89RbowPXJXB-zU(+YK3#&+%3u?eFmN8h#`8%byw{q+%A>j zBx%BcVDK|wV=%}~HRgh+*!BJ}a|0@MHllq@=|mx6wHHO-?c#WY)~An2%9ZXguak7g zWISJ65Q67_)1FnXb!68t%(dovl;*1^!6(!!r+_j(lap%5CH?S6%sa0)-7;Aw= z&YVq%*};Mc{p>NDO zuLb$iLW&j@Eo5k=@&<6E36)p^kmkpxIl<%W00hWRVM<`2p#TYssF1c|XFm5=>*1K)L z9Id2MU|O7<#3;JS0@W2G7JeBpzk;jezusz+dQnh^!Ap!X>Nn(XHs7jRg&yi9u(P+3 zJK58-rY~a@X#42%KhW5I(W;wp_cMw0gcvjVA#2ie3^UwIDRUWCv;rG(jQC-2jIj($PZwFGtCmODYU;z#Knl1%1f z${sRgo3(JaaE0VcID~G2?n62${1wH@E=5Z|?UPKp7rkY*gJerFikXmIoWso77b9^0 z2+`}10}pk+J*`Wo5$$6sTuBV+wCWHX0e=`x@780{pB|5hdZZy1AMJY^J_~g={1}r8 z`RZR`2^~W{=h?BzW@uoB5=Wi-TS_N!u{5{yXBG?f&wARQ{A=u-NA?MjXmeJIR$Oe| zi{OY*11tKtY_mL*WlHf;+fC~qm2CC|&S#-zWkTmWrq@|AHp34xKEaXH7CDCSLq`F+ z9QH^FHe?=vi#+B0g!)#W5MZv|5&^WImmoq%!;Qk)x zEq&~KgIOZPI3ROZO6t>(GS1<8cruUV@#e^nFK1oD;jLS!TYuSY67E(%G3NXj)DMBb z|E5s10R z2#i`ZY|P*|GJOM|*M}5LUy8=u$SDK+deT>fV6f|`HQxqynJ(W>zenz@0rTAib4xsv zUVs0it5SK!$f{5FPyLGw+;4V?Olf-`A7_dXkTq#0%uf}ZiHF- z-Kj1|P33{Z>`ryroB%~(4*{fCil+#hiIx!zyzvWXOLjaNCYssqV61cjQbd&X8 zgK0a~j_FlOuNt}*pL@s*AN!O!`vC6vVg_`z(b?{W1+eoP_C}8R+Erb)%6Lbf^`ooQgU@4zm(oG#jW@Xh6<){eg|AsRenoNcbXBASYJ?--)C=+!Q|YIi`~b+vKv zztO;&7k^sboiDEoEvCboI~KO2J*pzsE(U8wbFLMKtfd-TWF8SvjIAfJ6s@8qQgk~=+2=?Ak|y4FbObH2-1 zRqrRZ41EygbYZR{jyq4`jq(+>7y0%Om8qn?O`f5e&T-xx^eTul4Fqftt!{;pH}3Hk zRIWv}naqio#Hd*dGNoPBrb$?9{qU=)3v4KBYW!N1TT=jP!W)!Bl;}sX6JDqdO7sII2A51Q zOnp=pY+*@Hp`ozno&N}OgV2~~h#u=qz~tO0$l$V=cvU1z4or{eoU#(Ke(Ov;E$$8w zhHN2>oB26U*!o@;ldM{b^e5Z!JeoU8?n?MwOUa2ZH(rO+b~u%N7IoWkGby&eGD3*5 z_vjcLCm7F8ezyjedtfa2uZs%tM8A2CbryBh4K|Y^Kh(zilABv#yhCaqloJML8y0bL zq>GTepzg+bAz!)Vna#db!K1EnB#I=cL3ba6PVzn9>))$j!Pif2fd}Tp-fX%Pb+vV- zg^QNc#|_i042(kbF8=3<+4>(>_K9ADSB4w#hOW-VpMSus`E?W59DwtR#j^ZrsQ%hG zeG4r6?=<6!^D883KNBIS;7!|2pXOmE?7>Hj(O~?3=Y#4+*6M0p!)K|GdlFp_1K~6y zTcY-`lLul5bbcUC@!&M18DkCs_jNE5F9b5kwl9{$B8N+Onf_RY6WFScFgsm9uVgL- zpM71@DWie$fB$vw&-nvN{*H!H=@O;BK=CD1?c&4NV2sg;IOxnoQ*k}FED_Sk_2-Kw zZu>0Ia@Q1Zu$O&#lac3rv-LCk;tg!tj0ul`t{gpP5eYhF+7oGXp-9kh0$Lpt+b+d^DQ8qJe(x zu;Gu(!Ng5%ocs*j9V8j9z~7RTc9rEkgIXrc_V70zi#87rjDj-jN@`&ewVmuABu`e? zbbb5FyhJbwv@D2mWIZPMRpCM9npxg!5`93$=0IGpE#t<% zTXY|N@uW{=!cACGcXvt^{+1Pbq^|F^nX&gp6ggPsSa8Rg6BOBNkWmazrx->?VBoTKz@6&Va(8FX%$#;4 z2MW4G214S{YDwbI7Y>!|dE)*FZtY!;LOQ&qR9qgUI!hvLQl)34+@v8+ifb9LV>|p! zLGvDNWg@EWiRxzwKMs=Y|6Q>S<$~e)jJ)v;yAf~F|BGw87%RM8UpbKN*zOCEUygW!LNn&YV!6P zS+e1^SRfxg;Yx>I|D5FOqn39~cXF$bytGg8f0CtSheZC+f}ArT9%kfJEu4Pmlp|SV zK=%AgBER$k$bBA-Xql6)#Kb<{UM};WgyWAsh4m%iGh_WgJdK317`yu_+R*;aha|yW zgJOGBg?jKS2WO$bMJe?+6JH`thyu>sJzw`O_Vct+iF||{C)nBLPLwpiu zSg;G<0;?@*Os=oOLxWvLEL<-Glb1E1S5{{{wT7&fUK)WB#McNh$9hK?7%x!zT*k}Sf{`oUG9tsH4ZYN;>K)_LMUFAbv$)%Ez|nYC^yeP@ zNpn2(2(Ae_+RH|Q_nMnz(CdwBdsN%48o@(*bi8NtB@utG*>0(vsIL(IBKcn>hwe-H zoHUwCM~@GJH-xLlhtPx$W4Xi9Dhl)Ya^W<=Fx)RcAU9YNXGZ9}5$igW{c^Ik7q{Dq zcr7_WVfrGntt&@ISf6KVQ6`zYY(^2Lz*3dnflnFdO0Iy=yYEBFawz)j(~}0|-9m?l zouqW-*AK_Dbe7^2g=)Pl=W9#>Im)ax>r;0ZNEb577t|?=qe~R*Nr%Iz7OdAohFI%6 zyXwD7xSz;94&AV5Ogp?}BTsq@$+3=h6D)@nVI<$Uk0OvTb<+~Z6@baRLKGa0!NHa@ zin*2C*yVb3Q3b{^3HS{is(ja@zVRB92Ic5k<>(X!yT9!s#m`y>Pjo~Eazvv$kP}|v?O$pq*lTS?@TymtaA!o$qlJhyiZ?3|3j2DH~znVow@&4 z_wQ->O=mtz^?zl?(nw}o5Kdhx*=kTJEkf5&5fM)6L7F0pu56+U(Uw+Pukl411ojA} z&ZhW7)R|7QyVl%kNbT9xZ)EP*iR0H3qs{iRl7FR)_Msn2>~@fZ`(U!gmF&6*`LnVY zMV*EFR7NN~_%V%i8y@xHT^mpKG;BVBe*WQEkS-x+vJ31QtMWuBnD}J=&R*HH3v8OZ zX|${|1VWf9I`!v+=<|eUXOl7L21|HZ!(1nxz(K^sq(C^tjp@IWlBYYomjw%}$O;un z9v3uSe#P7slQ|4-=wAM>OVTy*;68e%8{Ai%?1i=`{S9tQ<4I)if+)enq@mxLvoKN~ zkO-1udhf8Bn;72e>#GybO_#@Qgb05;5>;cG!!4Y8jKR@Jq??2OtdwA2$A^uhg{Pby zw2^)~09EJ7-tf28AYq>5_rE^OomNqR_9^*h>F>F%fstk@$6$Qp#|Y!rI?EH67@ebq zjJ5I;6V(kbRQ7%O9!%sH0uw?<3jS^1-DYHo6Fqkwh zRSbQfE*z27FWJ)Sua1F0SkDn5 z-#gbZ{tRx81k%uN`RHJ{HIAE93o&8fau@Q)bsb#)akT&%9 z?v+VmU=ki>hh#d@nP{pAWO>Inh>FMu{!1Vfd%RB&IEJ^~AGG6{ib zID1rYtK_ob&{%L+#V?QuUX>afgyii z?OnYQ^b8kdqHpv6g$bt+(oBf#tbsJG`B}8+(wgsu;A_!JS8e~IRy%1}UUj68pcD|2 zvWpswG*W^_CiY5yH94LbyAxm{M*}f-*8d5;{U?$2B1 z3bcO;%w*AKZ7vG9f##Kf9$%XyU}k|6yBeY|!oJa$IgB~BN6tE*qVg~(!3OUdn*R!W-0m>0@c+bZZ({8+OixZQbjr3mtp41a>YxT zO24}*1`ZJnb{lE`({geMiN#)6iQ$cEOob-|SydA0`g@?SSdi0|TRu4ih9sjG*A#YLTF;!X9bS1{_#rUPgL zBOQCS*1I)$3$L^~f3k+YyiY51t#PAvfSzemu~y2h5jSnP4ykKF)Y`0S6Sk(B4pq|u zG!eDe_4C$VqE=Qw3#|RwZPotKlC*NOj%%Z{2yFlw)<-;MJ^W3!uza#p==8_Y5Y*!s zIW>XA4jkzEmETQ}Z@3S8_0h0A!B&2^M?Ax?GL0t#PkoR$STDxe;1ddVJb}M=fOi$d zgSTF;#ORT7))`eb@mz?iqm;KX3%fPO=f#Ux-|4efqh}F+?g_Z}s4@06mStImk|1mWlG*N(jE(k*}svvU8VfqiK~RJC;ufpCme(B1`Z`X z9mq#lsMCg|$CXYy?n1JWFR3PXJEMK!z*`vGQeS@J6sF?A@13>t7Upbk&?)Wu!iNzq zOPJ$C>1QoO?7xnv&>lv4(X? z+ukCZKO}jxkI}im>2d3lpdN@ENtnT_uVH~6mqQohN&Ch_7(fU_1cb_lIc<|F@?`AgRCpQz0eir@?*e}~PfWkRHfjc!*S&9|$*(7z ze?8WJ#;^kOOvrM6ViNt~rz=0Y=@cyCzTZmae6re{D3X03ML&)fDtP$$zP`L>0cMcf zFo2=A4|VSxU{GLFW%e(pAH3Y*>ZnhhgUEX79ux}kdjr7ft|?~lK7w+IM0%7efQIRF zK3TIsxYYBM>VI>`?yCsvlTbGL0Nm*sEsZ7PJf$Z{l2p3VD3vkA0RH(VWLUPuumBGI z`!UUw2or}(zPSn4Mw!w{6n+D~KOM%LQwT4KarI%8qU;-?yo35VphqMq9!VeCP;m4w z^jkS%OIAzim(+T0G@UAEYlJ{n7=Txoz25@$b>Odch`qNE5?f+LEAHK|ofh_$Pv9ai zqz!s==Mzmv)q*qWFry9v+Tza^xA z++x+ls&8Q7#4HO%mPLCZ_|+OZ%3w+zKw;$O!Pje{`Xo&I&FDa{C=-`4$+*k9mGvf` zpJi@?JU1Y@bAvzqi(FIe3kDKih>E=Ut+3|;#P%qp$+ItKNGamFW;^U1_aNOL1ArH}ogaM7B9>n>xQdij_54);PJ z-D2Zl$ssjTqlQY?X?MTL9M`LvCZY@yN)BSW8{4Ba9|0DpH^Zud$6etX$_Od&Nj81W z@$3&}zo&!zTHiwI*~nbAJn`+W2EvyY+8f=-h68KBM1~-HSsWSlWrjBtOv7j56XWp8 zEC&4-$M7%YD^xC+VcS?BWrs-GhkF~?bxF?pNIqGKM%hg)$%|6EQ@jgRrzb*{kSAPG z>Cf9L$=eT=gwCZI^Yv6Qdl=(v2}w)R$)rmZxi7tI61x({#SNyf;Vpq>tQ6x`A7C(J z=r`F??%Vnzoh_}8>gKMfGkJFW3GJrShAURih^3`;jtK>KOeSrW>36?PcPeJ3xh7$f zP$?UyI`D61sXjhfzw9TB_pwrSiz7)i7c+>XYptS;^^<5-uyqAqUVH%oHj|!mR_Oaw zW7;r_VG@lKX-tFm*>oV8GfsgKy-q7~nJgkq@&#PAN1tp)5w48wtN;B9I4V0t9$7NZ z#cZx{xq|N6-RE`kO@l}Kz-Pf%(nMqZKoenMmZB?Ip@S6P4o|D>i_LH|3xkV)pcg2O zr5|>e(!0^V`#I%!SU++8HQp)@COIu5H)yf}l#DW4unT`aU-3ZZT8pxSL6q@wK-ie|FWtm6?%O(q)61r1x;hT`j4 ztZOketxAL8x(-YSAbx7@V7Jx+^tLiw8?0TckAoxzGctaCgY0&B&+Fn%2a%?=Q)4r( zXqDPU9E~utnmEMAY4E6MN*zJ*VN?nPvOe5BIT^q&momS2Qz@lj;weF@>IzRK&$FQT zozBORC?BWCTw%!U2Aao5up9i45DtEP3*uy+dei!^rkC1&G*OoGgWz^n`iXsnu|Nar z6M3&epR?g1lKawY<~`P=gJc=C5h=&M-eY8OQamP>=*gB#dsTK_V?vlDT3+^tKVkL_ zla-J&@wN%}w%y?fvTF zW8ccec-B3A;iY^cM(>(U`oDyzkrnqa((56kFYv{t&XWB-$0c|D< zot8@@jeXc$Rm>E@^Z)~H_q0A7206)SwEh51A9&2(=3p@E=wbLvmPrX_5q@+%`BR1U zSVpDY1xbHi>Tjh^f0{TIpLN)a7zBMB78e-l$JxK^dvLq!Ly0}$WqD3W)u zr${#uP~8oiHe9<_x7Bq)S1tzDL^1W(=O&{|^!acFe?TOMO&q8<0>8e4hZK!-gYF}n zLZFE?iNP9udRn-MvFKLQVBp&{rC=Z_T>m#7jf05VpYY3xoeei9g{sw z$moJ@+hjYN-L2Hz^n%J$U$E&2kow5eF{9f?cX&a$4CQdHrGNm8*fJCQ4%peXPhKo2 z<0L6E3^r;=`^dQmpRYr;afI!b54lQ6?z)!3WFjXPm44=HdE!UcOknrOl!rbHOaap? zWaX?B_J&y`6#jKdMQ$$;2|jyH)dPirudg%VvQK3#)d@1oQR6u*u7?v{}m>MxCs{2*IC?3y`7LgCkR zF<6fN(Ea~ywD|#amMv4JImokNg-U*(PtiX01m=9$a|@Bq?O|JP)hLaR)j1YIzDyP-`u?-tVY|v|l=( z7v!2tPeF-3SI6F+BH!`|!n#)NLtWTIkSH$%VMMmu3xw$R=gc3!XY4VyaNdLA2QlMo zHvQ%0k+RzhlxHX2v$4ksD45x;$NTXOXU=$6lOR4$`A=gaYv6x}uv<4~vMy4$hN$hq zMG(0SkQR==SxakC^4R1EplKx8Z2H)o_j2SYvGHQCmV8~Cuk?M5A$2)rHOIYqOY07z zNvqv-@kP{h;>{vUGahN84Ubk)P4TQUDD?q~Pob_-S?eJ18*hjYg~%QYdA|V2c8AK( zlBIT`rUje9TF&R(hRzRr=luK9KIm*Q^_SiK^KAae7@IsUI|$@?fe;>v_hc`>@h3gf z`zy=!E>*$Bz9{IFkWA+_z4HuhG22A(p1!;jWHYeBoWR~ zB#WzXBfjL8k!|%yh>GebE&9yEBv-f4UH^dqD|gSO`-o;|d}DHzH#a(CbNg{0fAG zK%55*yyp%1-jKCGqP(qAx*Mj?EB#S&We8bsW1IgN9)-bdQlVO|bk$QnTHpdnONrM{ z1~)&s;{~YXdeZxjeENh#?nHK!e_`FtRS-1)Np=DauLjNSqiN~a;!>>rZteH56^#6J zE+<}XoK|l~I=8j$SU424lv`tA~IUU?quQs@O{H#+VpYd@wv=R0=`8n|ltF(RfOMQ=u0_8+f<< z?H8B$ASE9tb`Yrr^<&^4wtf)N`oTpsZq4n@C0;D&g=TXjqIVG1!EW_tuezOeAq@xE z*L5Ci$!#>5Ofx4D=K>8JK}6HqiUY5Zx$}~geEu+2}6K^reCdzZ2BpN%A7ceFjn1eP4 z|LVNF6w*e0fvjtF@299M8@A;w597Uy?Uu!ZHTJsA_i|OsQ{md%J7)YPkM19G9SW@l zj~^G);9D5)x5O-;OdGQ}3Cv{`gR}bYK;_^k{5JIov@^$xl@thPpO2E?`83^<@MD-9 z*_97U=$DCT*daeEJv5GjrL+h0Xh9M`<~Cfza|!QfyYURT&=-h7I6E6a6h| zr&VNv9`?Ru6*+M>pZ%9zq{(?S$+u2|jh18+%hxrfU;u)UL3?5OAClE0(;dktNubu4 z4r5OzP@iH?KD4zXXyeY-kh!&sEwNarw};81rbA-5tflg8sB{pM8}U}>uMQ&1MG(0j z^g13=Ey;vty}2w_YSN1}H#UJpW8R1YMF-cN1H@W%U^)QQgz~5)Z`92NP-@J^>qEH& zLALFoP8O(F%hlgzS3E#+)`Y3_hm2WO|RN;m(4?@MBuXr zBu9IkqQ1aeKc7;#0$cAu>DPw0sV6*cW<3Es2`}{uBY<&d@BQ)xJnrGnZXL`9gTHy6 z^{r3qIy?nNk!ZkSzpV@^{VIN!NVlt>b^ihrWYHUx(js8vU?-<4?N3gUVNODnWzKLC zZr}hYqZ3%}l76a0e>n+;1X?LoCeTlTJ54Qes`#tIe)AqUvdS=n9)yCz~rf#j*#OPj2m?j<6EjRP{j+dIH0r8UNNm0BkMrNB~ zf~AkRF7j2^r*7q1E{J$tcWK1@653iM@x{=DiZ6x^w0R}OOQLqFh4v}b9=s~#Jc z-N{1!0*UOksq5@fdHzw@b<%ORuRAo*&%ULyk^5n?+_c4!SOlbo?kU2^RMlT`DtV!(h* zD=2#uO-|Vx_#oY?3#oJgLcSwCJ&3(8Y~!j8y3KM|-<1hT66)e96_d5#DkD9wQ1?`f zEMj`-C&$$H=xJnP6rek@FWgf-F+OH)5H`dN<3r1mW)l9)dor&)c=46D1rBxCWxlnm zU=)+cx$-Lq;#FW^Fzj9jHx!Ryi@D2hcQFa?W2jkdyuY{G==h&6eY|6ekb~$2z2Oh9 zeK5ti62GvPyNPPgi5g?~7}-7|4*x#34q|$5w)+GJ^nCB({D)Ig2nr{*=PkC}K_TX@ zGLbv)xKQ}Y)uv>!OXu4dYFfBX?75fu(fiM?XAB432Xc<^YuSJ^;BW0^TfnUwYX2v` z{3?8^;CrOwX4i<(?U9Ve7|I>#*LI(_Tg7#B?Tu-#-P)$@9(;5&b8ocSD(#2)e0Jz5 zy}IAU5W_X|#%)n0Qlp01r1jBvevt$}b*gsggNf{;4MMqUSX~NR{|bkTv-iA%kKL}9tEW%b+7`nubVhZ&)nnNEPdWWz zE6^XW!gAQVD8lIPKx?F8J9Nd%G{Ec=anSIN6f8JyG>K|(mHycBNCu6kQ&?u>Bc6f6(ssQ3_ssm6FPWDQm zR(YHzD{Cz4FfUDj4*TL$HIC*9lzJP*Z=s&fJrA?_YRLz8-gld2v||__bp>KQq4W8} zFy%%Ns5~Itah(^)ujT=IVCWg-uSHsHzni?kaQ{XJoc6yL{LbpFFn(_#oSuBl5uGl+ z+dgtfCKw68W9DS~R@mDqb2^z|ZJ#ve#ahVwq_zq?qtUCI_oxAH@X-&KTevNSlA%dF ze@RkXi`JiSU!m-W3*&E8KyC$eJ#4k(4vbWKKL&fF=iiMtsJbqwQjg|C=;-tz`42&_ z?~P#*BS&cf*r-1|W^AOq4})Ze*}xZll&fB%vhK9}##7W=GqW)8M=o@ZDu9DC|Ng3s_dm)PybeIEkyx_Ra@KJ?j+shdk_i|E{jT2L2H=71bHc^ls_g_A&NX z|8;SWR}ge98YMSd&(thtW~KGs;En5P?Ek%ApGf9|m%TejhQUa5=Llvz$1USX&+#E9 zE2y&)!SqPDED{&Y8uxwcqN+$s2XML7F0X{qTAj7gyAh9d{%Yc}Xjv1v-;BRWkDi*e zq*1Y`d(si1gs(lY&U0D(;;mKk*XF}?8ik+{jR*H!N`08(GpVVmG#9(AeH2Oj6cUqpq>t$pA%`?Y^}TxFpts5w>y+ znhjsX&?M|JRP|?nv1U7TsqNdxeUz}}FOzfwHZKA6hSEq7^thW{R}*FK^y(hlKg9<= z)A#X&0V~161tZMiI?TtMO$O-Ek`@Zr_@(L>{63Be8M}Q+BP|#}M`J=OZ~d!_>u2X0 zX#jR8m)!M5FVdApR~LIgo;=t@;ZXz@<&tnx0M|@S1;Vg}@_>IGh6}zhpwKLh;GmQG zLLt7FG~0+ne|`=r&h~c?L!uo8tKMF+GN|Fme{KIa3weJfzoYq=j-}M=D73}D&8;Yd zne>L$(3QKh3~&6EsS@cU=7z~e`1K@o9}q}MS1IYu-2$cS%(&9-iP$)oDku3@gk}tq zoj794d^*TrlZ;(wM26DEpQf_cr#@9*H@e;F?D#8&acH`ISyf6UjxeO-zpGgWCLH~u z+wJLpqZOk0Ns02Xyu9aWSNJO4sctS6`X?PC7kcje@+5FYUXcIG!7#Yt38l7pUy{5e z>&q#w_aV&C>k#Z3e2qNo-tfvRi1InUw^a@+y=}^UyjyA-V^xK*mHd^0R;H-?+W&e` z-OowUYW$p>zWcc(MJ+_lzD!mab56@Gf50%_fpC!5AL4701{o}KT~ch?r*2sq9(0Pb zn7UTN3X?MNG*Ni9v+m{G2xb-WI0!Z4 z4e2PJ-2;kz1B?G%WQWXg!mMKS?VS(=O2LvXaRk>e&P^cbm^o5cNpl(0Wx${Bs1T>K z<6E^`th@AYzk9nq+U;Ot+g=yJ6IN6O)0oangkOn(TIElo4O)CisA%(|mNgxo^tu31 z{?!&x)Ij;Pe%MhA$6Wq?qdLCat;m-`~=T3SHWx>^umkkoLwNh8is~&BeKl=D7G+#J2_|~k` zbQ$GOQocyGdSZ4l6Amo8@m0EqV6Eh9?0*<`g~M%Yd9l8|(e)rW>czADdpunO>w6mS-;YaC%al>L;0-S%nIJ3uZz0KM5)`L9ZYpd~)*)H{IBZDFapH!FEG)YVz68=x)+{z}`_ zzU`;CY;9TBa4ywp(gA2KUq=>5Yz8qHI;M!+Asv=@zP*~9`B0jD2`=vi@_xMZ1HB_x zAr0sw5pZ8Jq!S9KblN_M>DPyjN57~e5^No^RnqDFV8ib#<Sx+OMV*J^(!+q=1xs)#i|}iEA!v;6Zm{O62A+rj=UAnX!^3A`1-VLJkXh{k^Jfx_ zWEU^x8aopqh+bnw)!Pq^k1WI9ujTYoR@kcatYpIlaAI0xR>y2|WlV-<6L-mAuZ<6lLQ*wYqeF~T-J{re&o|`l;SuV^cjQ$+nC~aTGz0g% zi$PA#{!p?V>>j>At(nlQ)iZc=p9r%e~Wy8Sl3Z{%V@g!AD3& z?%f}$*MlV{X0mN+SA}|vMJQ~TJAJ~yU^K_O)vst7sL@E1XRfmGBMF}kXXC)&)@-?F zw<>bQ_|d0b4Fc04iCIstS5pvWAbZi3@DZ~58E-fY8sqTeQ9kc`gDL~O5Skc2M_p&j zzp%$?W92hblDrAFo`Gbkd{ro>2017}otX!bd+SBe{K>w)?wmGmfptHR4oc;> zD&IgT`Ibbb%_wtX+U+Kdl-Nvywn!7MS#BDrWeVPY50O)x_#okGO_V;qit~yXiO?kh zS`ZN`Ain1tw{qiO?X9d+Yq_^|>e}{4EluaN>ZoIHGSyy3sWCi4B;`kMHX{59`F+?$ zWiwwFj;b`e3d#SS5;#oeOWa8!y`}=A78qZKhh+Z&==^{WOMnj@vOw+-isDKFDVa7y zm|_PNIdLUzmVyDH?F;<@&FNAHd7{et#$x_pw~zSB=}+&&X{U53a_n(}v%^Z+kcX7K z(hZ*35{w5hu&Y}Y_^?Jn@;%BN&R}033Sv!)NiZv0*Z+h(s~3hmCSjlM9QviFq;9NU z>0LO6UUh>{$j?mSpBk>SCUR@Dc!GDe2~+WZe?Fx=@4D-J!Zq)~Zt~`DtUUmX;Bgx^c&qAi~2xSQns; z(?U%PkDDuKC#Z9ZY24`I^r3xOu$^%t%_1N)E@}yFS=TrU2_C2IG#FIzI(!=P>DcntmNwO3x;2IsXh*{Cair&VoH|j&reF5AJu8(sK zSQM}exG;!AxxQ|L*Nq+a{v}Eu@8K1Ja9GN0>z|0N%guHeaktcakHc+uSVbvLW2oAE@2mAI3CRx!f+yKwq-&-kfRG5TchIsxw!+P^m$ zJMM>#7a?iHWpl5AGh;1FpG}#1(!Zg8Cxb0Ul5~cJEEj2$& z-qF|il6T$FFE-;VhDINd0C@MR%$XB%Fb{h-SDx_|hL^0j2sos-xdvw3?o96=$8JIc z{!~=Qzxd2CiS<%QJ0-xawnejhOCp| zF#42?{dm_sAVpuEn#|nc_vk6EkTm>}ERT@d)o{z2^)Wc9pW}_lQbpa(#`F1fhH;WHn=ta$z{0N-iR5p%Uk*(E zq)dGGskD$A$d~cJ+lKX&IXY-isdA2qwe|mx!rwgV5};MYkr;^O5=l}g#!u=w6|a5j z?>cplqb@}KLeRLiwGr!`&D-zM?8DN2-!^)*(IlGmM<*wB2PV+$BBbRNm9?IL`VsIC znvR{XS`cGdt-`d#Fp-LgWY#f1X>G<7n=<8^|OK?A_!a;sJ=U4FDnfON%NLX zvg{FDcLQ1S;NneC{GlYxK-j?^FrmIGtKY)%`Yp`IJ%-Nw@g?rvZgzP#%(dr(%V3u= zG^f|fp(XPV6$wYP=W!tH;E-=5M)eaY@2FEp&8qylf+Ox2?`fXNK1X|bX(lY6kT0_> z`1q_$eeaL1i_wzm&qveTE`zLo6#2;Zh)meXjhjCX`I zp!vxH5y{5zBCEiIUdSKV3V#pZ{b6T246K>)?!{pqAi(W*Xg5UsEwovke_Ke&6af#vT#v#5ek zQ@(Pf`F0=D=A~u_M%S6Tx6x0uNffBDWMb`_FwJr!GQ9TPtH`#|1+~~U_0=}^PIFV+ zyG3_vXzmEHvhEQ_fM^|anh!P2C3FrDH?nHH))HZ?y33#g5nY#8SRGuaHWo;Cbr2%9 zD4H;_1=3ue<~7kMZPj44YbbRT#fMXMX6O7dn0^t>SLsVbC50aHA0j6`u zl#I+@wc!i93ggSuC)izL3&LUK1t_o&Tc{vC{mA7`y}b0))t%)V6|2=A8BlI0>s)G4ZuXl^5Kc}*IS)0*_K-yZ&T!0^}8eqXZ}6hpG)b|?`F5Nrh;jF-RM%x z*4tB-OG3l&6{ZVHhRvoCB+};DpkMP&K*|b`3=$0KD@O(m7{5yBx`v8zG;-}Jd7BsJ zJwK)MS~N^f0Uv8#i0H-dk}c$Otm+;lJBJoIK7(VVm>-PqQU5Z&kYjZ*Te!WxqL>}R zPLw6mOgeYE2;#b@Sz%nSx-rZ4!NnPP05VAVx5;Fql->noO-I5&O6^DS%P6!x*B>_I4mk?XEN{|7>14n=xGga30rXBbb7le!8*I**!?4?u zp=0P`imibWdVx#OWW5f5;?(+w;W|6um_fyl484vvt=J0OHq#e4@-Z1uiRld-uTR04 zn(t)L&XsbjC$Qh@#lqE+b7;+)Nz=Y8+c3`vy`@B@$l%eT8?Pgi@u2$xs)4?~49@GZ zX|k0vl0yys|EyhiKvc)`|INPluDomXa)1;;M8)2_*lS`mnwXfV$v4IH-b|!}iXy#< zpkhH$1VIE55V0#o5KxL@0TBT!3bx<8gM$O^Ku!EZ-p-cyc5ilecXsA8Xw_DW$ZLTR z+RXa?Cfc+k`Ngzau$}TBf~V{Kz1nzpzUkv4*lHb%TvIkAA8w(=`iEPj{BvgOOzE5R zv2CtZ?&OVUZVWJHsA&ADd_vKbX1%LR0*jO{h)3$|Lx7 zaI|C`AYAS-mhwc z+P*<#-Bwa`GoIB3-|>*9-V?*oKBAEVJ+hFXTWW^1xE`&SR$sc z348Vqw0H{rO%XQx8Suj_RvaIRP7d+3Hl4@kZyCnDb~19wRz0EVI(EfQs9Xy}w8(Xu z@Sa-ncYL`3o8Tq(I64-;2em$g=S~srK~HQ(-I1J?u1Jw993{?*AC44eQpaSy2gVKN zG-BdN{G!>nNHdX)8HJvp?r;`-;anLP_?SMTo1}YRh>)vi(Ui~-s|XSjU{)+kCi{m$ zqm=w_g8_*}n}T$r8x65S^7SvKF0|mhe))uf_ho`#%8XsbhS>)6R(=2~WPTPH=8a2T zbV8RtX;qCE>G-J^wH03~tPt@eSDEn99EvR`ayu(7x9$>R&2k^>BmhDa9acN)noXJT> zoW&z^))S|lh^1PM(oW%(xSNFNJI+K`Mcbi7uUOyyJa4h%E^rHkorj<-6?6(0XCi_) zMoo;PldW`;esaV$x1j${;h*#VIr43jfuDi#aX2J7bpVJs93!6$-OZ0e^~+%(FL^r! zI&MOe#Se$iy{KI;A;cNKw}Q-3=H!!b46Ylv^bEUi6+=WJU%4Cxx1q#$5~8fs>QH`cm-Ohd)gHuw?<) z$*StlUXrqC=GIv-=7BsL*t4&o(Nzi_HPB@$GDZbgR6P~%qBLeGZ{52$Sn8??ZUs=Q zp?wMRXTUb9IX}J@somUepHR+bwSP5DYz4jh*{!*Rrdi&JjzQEloUN5?@LB`Wv2gpb zP^Q$51xM_1JX$g}cUqCtsfQ{A_91Dz`M*WvHwn5V)H0*CL0BJ*pGKi64jbtU)^P*p ziLJIwXOei~Lzz(}MlUTGqwREzWWu3PU_5B?+XuBjzuluK7zzjNaUEaM2!c9r##l4gFTeE(jZ$ zWQS7hb^*GP-xQ*y%Pj|HJ%VbTI|BJqbY9Pb;7xFdikw{1TkO5UV#5gkkC_KqQ2ZP2 zYi>}tb|Pfv)xR*r04W_C{oWR9MY*~K=VxXPGw`5?>L-q8WR0uprX=vhA=kli8<^KE zkn*zU1f$Ww#U)SO!CAQXdw(;rx0MqD>1%(hho7|L-OA6*#qVJCV@r>v!xn}o@{R$f zXw=bxJBH&IRxhw-*XoLQq=WVQ9yV$E$yAwPES22=Ne26dN{r$u#kY&&KaU$Zt8fr@ zp7m3s8&%G{uBL74Ze9(tS#yNR@m*B!BFn_|%`+9@P&G@wE*n;@%U)_bB4ktYvlN=E z6`usz<|e#!jR6U|V&+S$H^ERZve#I)^V36*(9Q9b(CPs?4n6sxZ0Yrpxiay!A>Kq5 z=L66EfVuSdz4ta7yBn;3yKYLJR;*SymE5P1l0BKeRq&S{npKFCodNw5#_JXFvnY3d z{(BK@O*R5CVW5N;7)j#XNf>O#m8 z9^#^wg$L@dM}4>y2k7Q&L&?0{Wn7^n*4 z2WJDKzZQMZyCXlgyqZm>hW47Hce^JxYF_D3)lYUuenqQNn8b^od|?~tZ2p~sRNBqJ zeqRpAYISYmzO#@eewa7#5<_k-XN&mAW!}<@OB(c#L!GwM889>sjDTx#aQ)*Qu&3uq zyS>}+M&s@p*fDL_vNC`CkeES_4R&bRdhCbr_hZKD-5KpIHA$t1&Aa5VnaW~5gOhMj z7r#Ep?8pG?r+E3KsNiHsYA0^H0?aphFU0p(nTu}OyL`E^PqOVzobF|nU{E`LkG#xC z>|KR%kkc-~!&ygHAwrvH-lnjYwRXjaQ?9VVGS*w&A*2c9=y3G#->tPRM)4gvuVcEs zp@o`UHKf)eM!wFT66wO*YJ+(PW!ZS-RbyO(>kPS8R}-~>5#3m@O1I0+*h~$ zZQWpVVpF}^Tgf%;d--OmR~?h)u~IWK9eUkGvWrn@$!Z;hP7iIP;;(Y*@TBB!6zh!I zZZs;}1mS>D&bfGfg@pXni!JNVsh35KyTLkUi1=YoZh7Ci6^dt#Am>PcWTw>`X1e$6 z=6XDgD&e*P4h$hKe6E6pJ0a_^!JQ0Rz!looX*olpcuSA>5X*V`@KZ7;ND%~0AfoAK z=yN|NF~Orb<+vYi_w9a@CHHGuf$f|_^W5Fbn9^uyL@z5Yi~|NH`usN;w$5NP)#6~G zmAr;pB>v(u^Aa9xgGF}Rd|*>w(QnNyS)}%T$B<#85|b4e-;JqIs&$0it%VTGii&qX|- zI4r#bb`hcynnR!Yul&9Hs@{LLtnuBu{ItrH4p9tVm)-%FwFOR9Q!DLDcPiCNQ0HXh zNyb=iDu!jk6FT&1J;X|>U_U-Bhyvk3OF@Gxe3ir());5xh)W6v?}Fo^UX6+pYt!YI(@Dowe---p`W z`?__c9=`vC18%Jr@~<*=K!lTB`EFkZNC#}A5TFH($BFT8w=6RFT8|`zt{MAs)W@du z5gFh3w~VR5GzNS?U7!cl4-jS&{E{Gj1aCpz;Mb$B(LCC-0*<`J=WNA13EIaMfTQrh zD8yGYenEJXOYxXY3w#aB2b}&rCK#-VqFbW|2u=45uX=ykkMzrTqlBDmu4!oo`2O}0 zrxGWR#Hj2Kl3cLhH)osr3vT{3dWSgTy$!TsA{Q>I5Q9rO2SLu4_wBPtAZhrG|3Na_ zg#Aze(Hw?Ic2F%H0N(t_k!L|Gf@jG2=xm|}EIhB13D1E8LXVpnAM&UrXov8<-#%sU zOSq^Go%GtTZs(+VZ>Z|N*2*0nShhV=DShnvW9)|4ukpsP-<>Aj%vrPN%th~sR;BhH zO1_I?w^OSS3%Hy@n1BF2AL076JhV16KZhs7@o{z1G0CPTv&$*kZ%7M4ix;EY!x36i zM9Y6vTWXTl)nRGi2>!E>x#_%eSmTa(yL);N|51WJCS4uYQVGo`m!u5AcW#8dvE_0) zL{h3Jq*Y#cg1PVx|EoxbcIz2FGWI_3e0mD$ys^Fp*yIU4eiR6f-3KY1#_!>6aR0A| z@4i5l&pGoHoz{;ePr(ID4}=1TKTKckcalB`(O9*KSe8#9&-*>5m9J~x9sEYJh3wNd zoYY_O2X*1)6n_u1^W!Tm7;dFO8a_mnONO3hew-(j{;MSpXK$WIbBi}>?)S?*uH`zDxQS$O3de99*D z+ht^P+L>&i=gw^UlgK@(h~-7|BgP(BQn~?+-L9O6LvJSvMYTgPMm4$k-nV@df4Pwl zm?ytl0P%gVAAxI@N6x}@xicK-5jF6D3t>w(fahO2Yoy|U9kA_PthfA*j{ex{1&PjZ zZ}N2`+(M=yN{e0fd03fmveMT`bmbGzzit>tf_N&%?&`%3O&q>89nwW%+83vCJH>O3 zQ1-{_Muk4R(ide)6swg7j zKG0G0Eq2o`?Fe9p3hUwFRmgyyXbHK-{71pwmh-CX-H(&l-Lf|iS=Tiub;%X4pV=KB zwL6Y`P<$SR33pRv^Ix*&x?i! zUxiEz?~C@BMcR8I`35B1g!0ke$Qiy;QG{eCZ_D@_pQI5TJ&b-(^!8fj#wQsT4V!U` zFN)EVrTk#xNaxeL2i@ERC1cFV@2T$^Y}H3KPT1dm);=46jJ?$R;LtOM7zjeNT^eI7 z+ci|a-fCwt7`TX38o!Q_+Vy0m`iW@}Ja!r{klrmJAhu`WFDx8~4 zaND+SJtarW1rKWWSL+rO>L@sw%ouf8o@F)gSuxO!qIgEhqkP(XgmL5eN8WG^HIsuA zrr)s68;V$v{1JlZ2Kp{C?v^L;yaEGyKuHE{(yc|bI)NluAHj=CV5$~c;53QD4%_kgzv(a3tE$p{{ob7`RiqmW z!b)-&1X}qFPqJy)3u5xr?#xz%4;Ym=m>(AmF2>xcI#iv~(2dBQ3H$PO_*3Or-4!de z*Yc^k)>FmbXx{FrX|5efbD}Nqqahx8+LcpgsU<3{*H||VJ3!_9?Rp_0`WEYOb=8R$Jw&_FA(C zu5bb-Ia81MWJbD7((rLQKTtZlH-f@rX5<~TRtrSgag4r1`0f(28;s9|LHMi$*)f>( z$q{A{(q=q`O@9 zch|xOH?kH4Q*NVE`hPt`yX5!Q%cEB-rK&m@+$ors((=67iu9FQ?6Q#BO!kI`Fp%d! zej3;l?WUl*gvumS^VoUbX`-l^j|!5?r0n>pev+!4q5u6>SxslvkTmtSJG!RP=+d0M zZG6;^Obb>00G(0UDyLEM!xVdns)+$>d5l(6A5n<$t?v=P_Ma?X82!!IYS~48B#_S) zqA3dD!o)R~VepyN_rf58B@qjJT!6u-GzCwg=p(J9?8ng0b0Me2RFH2ZleF^kVO7CCQ%7-9T5O)nRiJErLr! z6M3@X7tFb;to!6%W#q0_u`$}6Cop)PIx)C_vWx1|WJTChyar-K#7VXn%ui0p()lN< z4~NI*VN+)40at08HLIjpaGQj@lny^V0+sArM<0XbS1?F1~e zxt$A5%N#v0{Djp$rUW*udUgP=eZG8gOz(L_4?)#fY@v7-xwX^_E|`}pn&55_tQIIg&m_o_* zZ#IwQl+JfJ{rMq=PWQ&E<@gT<9{iIo?)z96Pk3~ae9}WGf=iQJCNdA12rW1^o!-6? zxRUW7XysbXn4h-z`$_Akw$N^CBNUel!4`_J!EJ6IT%Wmhg-YU-E!)(-zQKV*|e7(=81>7Dn)Sbi>Mn=mZjKkWdF{ zT3=h@!C(S?%8oBNTg&lKiH3#Y7|1gKgJyU2PmLsOC3P+OGzq>o*{1fXOM%B_b`W}R zYHuvp;GNtBQ@5+}YUR2s)m?rE0EUic(aKq>wwZrpP^*b0T7Ua)9=c`?Wv(U`QPR~i zh$^vLW>FOAz%k89Hcw@lJWk0`!IVztC6H(+cDQYrX2qWFxy1$2 z>{l;hQB2&QIoH0R=TqtP53Z1w$^Dd_c0qT)AC#^GW=k!^@KJs#>v)==qU3MA7D;qo zF#WIkAL3V<+r0Q+9<$Ll*N`X5Xo7@mh@v-gLfUwTr!u|xaL8MdV&L`{vki)tq>+bA z0}JJ*qE;#8GmiZ2VEcQmxF#ByUREb$MX)m#f!wzdu0gxyZu%`nW&U!W)kWs4C8yMC zd+j!GU41pxxb$EU5fK5&l%!SWCIN#eMcI*}Wci zZh<=E!6)`Y?O>8QI%OoOeZonxXA2X4+A%FF4PFQpLxFHORg7rULc->2*Wz}f1VW4- zo`9<(F(7Mb8MqDccLD1J7qW4hsI6H~+96Mu8=FVdxuKyT^ARL6Wd?H<<1gN~FFlO=9r;vy78`&Z?tEE!PshuM&tL2;KX?9HLszp5bcaz-_ct!f)_did2 zv@DX;?wQ9~R~cKQrn5yLFW_#VG_QWyz#TT)E{H6+DllN!1ziFIt# zn>`}(`xp*~m{s!m!TL8W>nh-&m5WUHc={e`m}PqJwGci;Qbb;wIsUwH(ltC)pI}x5 z{&v{DkwsP5-Ja3Z2Dn8#jV5#vn%Xt~vrc@GDV{1^D=PAU9n=|HK`~F)168KaWjtRv zFO8dqa3+a(EzXseJy_~u$7Pa-UoW7``#fF41%=G<{f~FF9c$xHREe5b)Ov z3V^GTwHepqYqD2;pVo+8#s;A4zyV#MKFoL8##1q`GauU_ctP6kC2aY5@@A;Ns{Ktt)Qi(pX8b zz|C~SAQ$brUXKD0pkTd_Y=v9G^<({Pb7t(|@CsbDiez88RRhMiIcihF7dlz&k`WA+ z+LR4bn`R-x%}e;`npU1gkF2R%m`RU~oVX}!|3W{e`b)My46E>6i{0rfm+ih__a)jZ zin}VWHcy3EuVIs=oRJz<)Na<%=4|D9+5KPT%Bk_M>gw6@tZ5NdvurB0VBBB!s>%Xd zK*@1E&89}i={h3HU_u7W^83N#>zrA!L6B@G{d4REQ_4BxAreT$movGV(T5-(<;KmWg+2ApL7?R3oMT^Z z|04mLM!#pfbz=y7xfkMCitS6ymiKT>aCS!$+@GgGjV9tIYaU) z!Mop@MA$V9(abGOcIzRaBqiF?2!(4Q5;3iAWrOXdXCTIX#C62Rnpg84H{&;JGKy4c z#WiqsGx?QxyQNU~tld&VBWJf}sA2IgNt*bdqpO(%syCG`#_8jFwJgiMXFaN5w@KN2 zHOn&M$D|_qpSopPMTB&FzofCO>hj%czOSyd8k!nXtMx}~9-G^CB0o;#nS@Ozo2dD9 zu5g5xGMsGxi@7_NQas8zuJp?vE=W(*&KfShk=H!lrVABkeD#Y*F%tJ<1q1a zaChL-F`{wOlen@KtQJ&IvP3fa#%wH8EV$3mEr6 zUv61y;cT~EEO>!Y-0*VgoR)TPEsPsJ-HhAmhE7TOdtKgR0FC z1=r9b5HG~C*dW+W9!eS#xMMkjhIaM`Q-W-?j{GZItSQvanyYEfQ7*yVmuSCQB9fQJ zDjG91+&wAw;jNXpeY{$8Bjr3&BX%?j8#R)pRaCsvgF4}%MrNoL6)nWpb(0Ids@}|8 z`NON}+N+Q>cE1zAcIWYUrBP^KNZptyXle#VNzyz(ZNqb2WlvE=$uV|Q9<{}tludTF zNSxqeab+^&wX|eV!i1usp%>sv&+IXb_tN4)&KBWL;JY*sHEE$vaA3muF>RE6^A1dS zq8AfSH zMd9@`w?Ir8%-u>@e@il5&OUt%`c)PUQx96VG}bT+PuP)o*=@us-3SH-D#~@*hsXiCxf+gA;OdhnMrKj)C+Jk677?l;4>0%XrSRY8T|m$otSBu$ylRt(V#l=#}*F~;M>pkvaxPhf*q`jX126ddelXdl(G zYX12mdJVfprVhg41mkH)5V&ECWd!dWWqi+ygFGNJ9zY~D#?qsIxsilZn;xYGY0f%x9pxyx^f)SE*Q|RV) zv(RcFTCYf^9okp8Y3!96w72k$f<27?5X%Gy zO*Ri8F=mgA`Sjj6xJRz*kR#vl9JRH#$VQnK=t&NcvrI6qjQ9~+8zj1cf)a@!P~a#K zj56JGAC~b}U=TmvC26Js8AuoDY~f0Ncy&qrh{3Ug4#8Vmw4?Rj6_YqLQ=zHXC%ifta78dy)7`fiuJi9oH?b5H^2it$7{KYX4EL zDD7pkYD%Mg<}#E5S?yoVQd&(;tKi1t;7%3M9Y)E~&MAQMU~G@9y0#6{Vdm$jw7jU} zJd>w_XC#XuGo!yrhpYPOeDY9aF?2gUhYb^yR>5`y!WKxMN5VYuQ2Synzt2R!9ml;` zQja|bi>xB3M`ZtOW5kt8>a#`HX48mca2ZM=gl(Yf{zPj|y^V6`ziMVYnKd4dZX*$n zD^l3>{s?jxiiMwF;>p=P27it{N)GCUeU8BZW|BW&ln&gaPo;lVP4-2yHOGk%tloZT-HTh-`c529_v zS1i9hNFKeji03|U{ggXB7CofB$o5a`5w~Gv1hs9Imtpg?HewGr60eyjQCZ5tj^|nj zPc_Hp%lCsd7qNgt`4VxH`-NpdIh^Lp_2Na?{$7~JId*bWd+E%dBL7;468=Ae_*U|8 z*X-L32RHMwD(7~3?vj=NLta)fKxH?oF;rAeZxu<<{=la85Y~S{*X6s))tsj}2RHL? zp4n>j(jxU#TBg!Mm+cDPVpf&cTZ_^LN`~VDc4T2_p z^lG{^Mf+`Uss-0;K>r^wO6GIAjugPzk?3la4a%YeE)Ni<)0?*df_WhdSv^QXjMCrd zb49o;`l0;A_=)x71ss7)x=tsOvlJP?HJ3?YXhyHo5WF64EW05|TC^(x7G3O9IF?86 zvoEJ`n`6+=edbOr?Ieg|Oyh*YA}R__M*#wXb4NpOs%Q4X4cZv}*M%i#4nZnk%T44; zZ-B>Hu%NeaPp>ROkK)saOY*-#itPF5!6IpxSA??a*9zNcI?npS7T8_^s5TCmkn0EQ ze`h=>y1@JxlZ);z-Y`JuZL3`+EEQ_qA(EZs8C+m=2W`ZQ<%7o8W5Hn9S|Yg(UTB^v zaVmz|SB%OMKI{DXm zuCfW{-#=DK_Y1cx)dXRPcd^B$`Lf<36y)otucvs63C^n{54A~&tt1o;JWE9 zquf3hR?bBr$#8hgcqc*t;}{2cK)|9rHlxpxLm<2913?IwoU~d|RE#-`mXjc%vxvMO zqqB&|c9_K(`H(0x`EKr0c0d23^a-9qo`@QCz9=$4#LGxJ91`$FZ%5E8H_i2WKRFJn z4g+(c0JiIL7(dN(8ex8nUZVP1|7;PY*aQj5G|rjX;w9*9?Qe5ypmFT%`%0+}!!8g}>26 zxs!W=a-M4v6`JVnZbVyEs+?zNvw5d_r%lcuuTzVi2r5!a#RgnmvCg#46csek=FD?@ zK8w5E;$(a8<<_6rDhW>6&O?(7@`J!0P^Xke1#p)yQfw5|m3UQJ3!bXF;!3Dh)jZW_ z?*0(%!CIYTV~IS5u+an+x*K7w85&}pkzV69@OTeMP(AF=1m12IT@Y@=nm@UveatzD zFI4h+aFhpB5wqq`GUnYE?1sJ#yo-xo{snyX{oO1yIkVd&%!uY5m=q?@7z zT9rPpxUT@+$saEk#{Pmv*iXU0By>79T7g%}Ji&p9LhcFcCVzO#kuzY3{ztOrPm`RU zbk6W+PJ}7-18H$zl5Qgju|2QX(o%zndv6zC>BU)qD>~Qi+b#+*sa2)JJ3w|!5@xQ0 zWK_VP>G}MpY}`|!@AyS4t@V1uharLrR7C#^i>Z#kLwl5yj%Vgq@}t(&q$axhiTSFf zJCy5?9{;5kSoeMFSVCQ2+tJi@ zoTF)43e?k8y5Hra>e%3Ohv8un392TF*s5>+ zJTdmU%aoi4uKI^0c-%7%ir`x4h+rClNG6+f4P+yFSvqQG0~! zaSmQ{Cx0kA{Ev8pzQZ}9S1WeRL&NK#t?xO}U!} zp72fkciJhs-`2xIghBkbvalTo9f1E^aD4Z6D0KW3_=UDSX38PeT$o+hr+N& zV0(z5Q*EdR%zY^!F?QmMfw;58&__qlxh1wJmZd+t>}TU;Y@j+_rqXS_!~B=E$urG4 z%ImVW%dpoe+a==4%c4%p}cC6ZPFm?ULkCE$0@Hd-EQ8$dP0+gP|($j z0}a%6qM=O`{F~@18at~d59)o5bo4>d!M4L(I~E)9tW zGO}^_?s1Ma;CcueCZ<-veF>>I*=L0Q$9ESNRlt?rWaLeoN7I~yZk8F-7;@j21DC_N zs^r{izn)LS;LJ7dAml$G%Ly2pnosUihZxyoA=%{vN$Y_lk{U;NHZ`PgF_Ywl3d<{T+S6 zzP^s(Et%lV|}fz7r2O1m{tY?L(0Yhl2v~ z(Gev3Q}KO7=@o@fKwWU3gkTUm_A%Gba3%(Fmb3D~uvwkXO-d=CDl$4)?sbvXW=zT* zonE=1T{|b(E~|B!*@YfcBvz^E*Je|y1UGBu^AIO;(fW}S4Bl%ZD`Ky}nm%h;*`AN? zX_MNQXriV|@8k1be?5hklI5r2xP=Gsy@kP}NCsLY%c2is*!3`T5<9+d;1i=1EfQjt zJnFU^FsTfQaD+$Gc2M_%@CS~M25GpAM0F=%Bs!ha)u*tSe~j#Xn`Oi|jb7%&~N6&xTnEKgGP^=@9UKyvR3!CG<* zwy>k=qhoKrdp8)EdWzq)`XRm$rw?c5Qv*FDwUQkIlWTsEAjxMC``-yKgt4akePC`m z!>(VKRee1<>1T`W40-^a(IH-`kf^PDZuyg?Nq8QNGN{DXJ+<>6!`vofW;93fM#a>sy`qf-86LkqpBYGB9ZTq4;dp8Mm=$QPB?(w~bo@fj?1`Qs zc|fHO;ConZ`dx5z8glPBu~rGiL&u})`90}s8n|^-^IZ;xc+JJ=)wW_E6}y)(nav^kx!Q~qO7HLgLXPx z?VC#o%c=HxdI@88Ov?~dX>HDj1MY-5H!FdcYaO7AGT@OlPqKa+Bnt$I+9(MNs1-ZN zUePr}GRV&`!Ey?wk_Nx%kt`zH>}9*{Tqr^Hgr}nr9(xzKjQwbzNFmaS9e9af-+BS@ zvtwm#5|OM`$he|~cb7Jz(KU1C1&Sz6wms-m)hCYNV_xSg6#mfpn@^rsuI2(Z^qU<1xEfNBmO) zbyQ0~e&I#*$;cN!&~Zo{;CBPgj(CPkp}2nW=D`&?D5GT|TK4<2HhRPR?9T7&5EPm_ zkaXIEDJ7-K#85xWy9~|OelPZl5q4hTm7Lt@Sgd2Z?L4hKHES->K1Vh6W(185bvw23PBcN=4r@(DSt_me!&#pxvxP`oJRV9P(HZ_I>?A7dx`;JQRFPBC;XD>i%bpU^8RlJ1B@3syp~OBHqeQjEOV& zW8IIWCDABSNj%b1No-_P5_e2h5~UO-1T76@iZQWS#NBZlavwb9@XC)q*#HtBj3GAV z0v!wVrM}(>7uf_Dg6v%>h8l~^!|+CragV8pC5B)ojj-F6@EoP#BGQP)2)Qls_x zbFyJJ)C;sGt~~@jT1NA(EuUV4sF|@p9Ji9@@C688Ns4iPw;R47o+D8Xl42;ZTW<`g z*t&B4*AK6-5_Y=+s#*NHBeh=#6)ChaRI@Rv0r@RpZ)&)$YBeKJGbcK3MwA;Kj zwKUK1?rL|F-%Yl8NA;ZIGErS6)>YH~M>z}{w>-@m|BgGlzcVE#XFT{&wtf2vEQ&=WUz;IWeYDg#SI#!~Zri_}?8<{I8V4|CGXNWQFuo zmiwZmpAJ7t9B;wXLF2_y>!C>c`IZ6hjOf4^n=&1Fa!;|(Z$1=80Y&8zFZi=!!_c!J zwNEe%7tL+rxzp|+e9D$aK%#DPPjdAotR`G(@rfmWz8?&}E4VstyzMR|dd|ZiV_>LV zY~NG8U-AkVGR%2u(zwKns0p8DwGI6Fn*X@T(yk& zrrg(#!&>*)|Kg$j_FS22cgAZD9D7uSjT0$(JjKRRZiHA3JtEpC(O>+q2$~FCZ^QWa z$TRYnM>Z7oxzzXiaszjL&lPCyg2t*PQjy=+@%-nOqAID*VRRlW5U-bsYtSa7;w-q4 za$@`YN!-CMD1pP0wb>vo`TR2&>HXogd+-tt3+BdZhg1QYl+1nS8LAe2M=wWP+~sbc za}Yf5u=xg_woSv2tVi`@!{2pv-~lzm>U*zc0(9|ftZvI>r6zx|IkJZ|BDCtDAyAFh zGh+9gfAk@S(xBzHWe6s;^{tu+*a6{B;RckEO6i)OWO}MAGSuvA5XPXr@83Q$XN<1n z+3)yYk5OsnOXc)l!=zKW0<>VgW1#QW8^P~=gaK4;pVYx4{w8`CP$PO+K%Agp$e{rl zu|Tl3XT-#tb`_>E=Li>DAk~qqwq2Z%4HYQ})GShT7Kf=$SI)*-m*_v@iABkzFV{^x z)a>2~|G4!&|CdhD@HAr#H4tWxCdg5N@J{MQV1_Jz|2#S=?EBQ0Ild9LzU4#9Ss!_t zzFQTz7{Yf(!%H}50bGh}RL_+p=-eebT4k z5dwQ}qB{n@Md(5i;|jR>(F3@&oZ$*^CCOy1M2rF&Jc(Du-sFdnYvcs@qf7E^?Q)86 zg1w8pAw)xdS>~*j_tsV|`uSMh6h`1;nq+NWBOcGMEvAhtASx65dkP@-+NHKKl7 zIIR7-Q+*q%)aK{Qd*i3yZ!!M*z}4|vwqC*>R&&3z6Xa3AZs}TdsW#toVD6HnCR1he ztvOe74g%(4G(p~%-+uqg&p+vS5jooPx|1iLo^^1vXSC?bMMt#wTMPvWB#=1>Utb5s z^C@d#Dlx+C9p&S^j*<%J7GwubOVH7*9cv1#@wcq@d7(c#Zm?yfP za7N*nxvlRJhzx^M)^|-0j14i{2X*UV-1s|xZ8Gr@4H4|@foFAJ@V1|S3ipox0*|BP z1)2S;7&fA^qpKgPO)IU&XR4##s=J1|%jZr~?aQ=JN!fG_<_${5?^0HF5|N#SBnr(o zIoT@y)IM~Oi7LsiB;mgVU+A)bU1X~1dKaXQONUCjZFJN>sU4~gM3te!#}1>#`qOPn za8Fw9MzhXzi9_Wd%tp!vA+8I;z`_&I!T<3u#1kG|g4>g$t-@zY^D7~2qLh3zF6suj z-GGKck#68#GwzjJ2dZ&;)Ofq1j2)93^@Dv#MCeE4xyC`4w${*n3R;JoY*T6;dV zi~{UnQYf4!XYRv^`HrSr%OT^}lhz-NI~fI29ZjQ*P9fmEC!8&S>>scHnHs{WxVdVZ zws@Se=S*w&!I$|5JRpWW zI_{ia42$T&4}q!Bdu|qPlSFTQ-xHpiNPQ1M{Zuq#(5;m15hf3h-4C-&u77&b=JeD( zLjOD5mp2?S?3v$kLcJK{Kb6guu|66$PK_W(9fu3PLnSKZl1)ubP5zoDOn zT6R&(BJGH8$QfN!Q{|G*&#%WR9ZPeZb52E_kwM9iP;3fyX!XU5U_G~-G`fRz1%^Md zEi!3bX&^?M&1reOXjHK8qIQ#fork?wM(Kziy@Z&trSnA_5v%AXJmtALf)}Zl_5Fs6 z_=nCkhgLJ8G@HK*4))iNvbj2;yjjFjGF0U{Oo5khh1xB<5C3%WlMBUqi=l) z$?bvQ4Q&;-lDL7kdKKAlQaZsm4{oJF=shq`A>Zi5J8b^=*jq;hwY^OIVV?tnFe%Rrm1B*SoL4S+! z{^jOkr(DRg)G3^h7Qh*}?V_gNCp8JcG*MXRhBKgkWM#$@k)oBm?_u0iC*cKOxKs$% zL)dzgcxPbb{taMG5*$vUqm%e7I#1g!6g`6UWe>9<(C*Oh$LbIOZivp}2pv&GF5For zK6MI$*1?(-IQjt zqHR&@fg^yD`%C#Vy{D}K7n)VjOm=2`tVj$t;4XEuf^U*yk(m?t8f5Dk(6Zei=)8H%*lQqG|a5*BP^Pdy&oYn z=_mpJAQM17D4xxuZ@<8Myq({<1%Yv{h|h5{!h-2P(AHIa<}hLEI!LmXG+qIJ2|pf6 zg#y2Cye-lf<%dBw!B4rzbH`Ob8ERoWb>!DGuuF^3C%)J%<`*#qtnj*yqLm?ZvLI}T z?)JBuFgOYE6FdS$17OG3t2wiN9-0L%$K(PJN495t_>$~&fuYHrph@Qd^r zq!+t|sveU0&4!zzh3FG1#_5T*# zMa5Q#tf245#5zPbLQ@nNJU8rnpIqn_q(kasf%$^W43O|V2gZ;r$XO`A@loc$u*c!)+%V#P4>q&J$OCQ@lP9v{{0kPj8y|Q;0*zS({?UwxDov_71ns6u9eLo+XdB7iGt5+<2$U(!>uk?@>`m*AK` zAzDEYH$R{XVR`s8r~UCptQ|xuAfs~o3fG}K-7^d|B+)+L3w&1r@4S!*oXpM(5flic zSe`{)^7bXZVqp~@!(^V-M`I=h$CmcuOq959BlKMn{{}6HM)gg<0m4X%SNJhPBxv>b zz5(~w!gwKFkASZO13|&VM7!TL$5&E zlb0bX5DrS`rI-p${eQMb!D)TrZJiHJy1~UX_{i`!;TRN~ zDdnA@c-xm~(GeqCTK-k*)&OC!j4q`3is2D*VQfS2$od94r_R5oipOfJB1&0G`B(5N z=t8P$tJkXdT7_HQ(!NgnRI|32_0n+6R8Cqo=gdxPO$UN<>A}BUFEL%@cCprU%zBlz zrnQX9iqpxl4i}xjpW6Lu(y~e~r@i9YVCfq=SgIK}By||6wxr6KH9xA1O~(#*N~`Z( zyRNoRyup-cJ|3%MA*J(kXJx9YhW6EKzIw?%eY+{!Rkp^wf2G81`$b>(sS^iiB&e`P zw~w88r9GjQbJRSvXBm|gTdlG!E2HF>DYl5ZC2^CiI{~@BpnXUE4~^g0mXA2T2dx|n%KU(z`IYB(IH4oJcv2*aKu zaW(=&Vb(n)mwv7%hQ3fIygXU3ZuCZInN`Wt zmW3H&c!48#fotRX(b!EVTb4@ltIc9IOUB)Mb}~3og2cY+FVwreN1Yl==6~1HYt+_I zR0FwPM2Ze&*pM~_%?3)oj$%Ej*@&|F2#)}?^AC0Wpxa;=D8N$y94EbpxeK)`Vh%Qg5W$K_BOZOmR+kQ(h(MeapWdXsQ;(lXlec>cpI9a8^jL8in|?x0O{IgFOc9?> zp}n>0fNc8Z(nq%!3wi`W!@N8`S;(LKi|G?)BE1_gy=wVFggg~&r|$oe0aziDI|Us! z7}kH{!d(LLKUd&T%D%sAIDu|38Ybb8KVK61!r)t9YjGpyqBY=$z{9XiKsVYaFPDbf znH)Amlb+n8FyIGn7}gpKbRRGqZ@}f>u-T{$&@>}JYUyaKdxvtfN6#-R0F7Gy1-3}* zQ9yZpth(bFJ!WikG}I2fpyIKw^e}j}%Y;{LM@>03Bo!-%Ry}5iVjK>KTH_t%oL0Zd zeG}^I?v~n@*SJ;coS|XI)P7r}KGk85^e<6LUTu`x_w&tDod-faMa}T2AB;}aj-!*P zvPq1l@1CSu*N)+)#sO?fMhw5m_Vgxx|CyeCv zVm%Of)r6CDUS$8L_4janzGfQz`3S-jSv?|pJ9UDC&?^deJS(rg1X1RyGY0tt6=?em zf-VAn*gHdw_09-I7gmXGGNL*^@Z;sy1D2R`94?vIUW1e#Hy~}0&JNU>fA?sT-8OAe zxh%T(?GMR)a%A2ysd!CqwD30BDBH|(fk4>Xg17y~y?>8+Fjy?gJP)Cwrvv)#fA2^D zpuOtID#?1NpXjcSh;9~+OuQDp-l|$OJYKXOCG5y^h~nl6)-B@~D41I{`5sN!oo0B? zaylLiQb<)Gj3-X)`?1z%e+XOy=5qGV_hR`YKSQ|dKKmJYI{qtsKIt%j(E zq)u|#cG@;{U9s!jR)J{SWjS_V+p4ZqPn(@uhDtbCFWAeJ@UqGNM{uzk^2grmi;A)o z**SE4=3iMDSqBHKDA+l#K*wLaSz>m<3o42r;y9f=-?`lO3Anv?sUe@f?xkR`NebTkRe^8=j`80`JJ=)&d&-snzB~vl zXdgF-t4DPxw$3%+(w8$zJI2R=&oS>*Ti}=9AzYUvyS%{3dOZ2xb@ZY~V{n84@2z#r zfZq41bRV1_hT))mX9+%V8vyD%nh0;9adpA$u1eBHM5l~IzO;UDl7XQ zO1_Ii4^DqL&J$hS1G`Q4^S6>uRm2{T-9G5lbujp|o!Bd-HcLjHb%d8AANCSG5b~a_ zeAJ)W0?UidAK!u#e`c-RY#gGGH(#aE&{zu0Pj2;=_-e| z%ImcaTE*X0*l+Y6aUXX?bOZECj(|b<47Pd)VBlsZflkSCk_7oul}i|+E> zOAi0$R}MN?0u^Yd6>H%{?}y*dTjEKT6R-KVCXXi;M!Wf^cqcw#NFNK|>F(${lW&lE z9?HjwJXoDO*atrZnAP`!$_B^|fz_g9Cdld-+J^KResk*MO^`F}j&<36I6i7TIY&m} zH@g`)L&>nyJ76kyVh>4q+XZj`Qt2Ziw!oAiDy}Fh7mi7T!_oizI27~6L4MFkUFVU< zrYu(}6t^W-f1mxoUt8ApfO%vo`#{QGQu#5@Q*sQ%eUjSu;hE`2!{O0&a7~B%>rr>) z?LubpYjT0-bpl*&!2U(XqMNHKN93?RS{umckDCy|!&u~_#lQXS>4g~1Y(X`f-ec3W zkpA8zL_Z6rIrD3&Bty;xC<2vy;*iz*10j#n**oVW#HNk=O)p0O#`ItsLz0};6Al@>b+r2#yG;Khw)trykw`+Eq|9D*bnhSp`(tq+4Vn8fxj`^I2WzPqu>d113czyEdFqmIQ1b{s`FlpB}G4Rf}nP-4G?GI6X ztLVW+ql;}Tr%XufmF-I2Vtp1Ix8%OuCmWb8iPyskQ}orL#CHth7ki)yP6BKmeot3! zl{CF++08-vDjyPUL%}uiL2~FA7@YX$=r^a)XKtt9nXvRc1h|9ww@>~)P7$94&Qr+4 zRUSiA8L_w3^H``gldb=M;@$%;%H#PT|II$-o_nrx^rNGIh`mRRy~G%!CdQH&OJZV7 zVtTJMrP~lu5s@N_4N(LIMQJL<0-~s3K?NJ4*sy{Bd4Pih?f{c~zJFh@gx%TlJkQ?l z%XjwP0R4#9dn?{d}xhi4H#2I;nw?=}F*q zbW2s$c~D~%rvkmQ&P<=I&`H-xY+_~D+#Xy{sO0u^)T$g4?#KCOjf&5^L|sn!n`Fn# z{L!+w?Z6(PH?b+s12s#UGp}9I18JQry2Qy{8FWDzbMVh3B+lEU`Xx~t`t`B@(I`2og#2N4AQHJ!xZ_mCyOluLGX<%pkObsOFl;mZ=mLr@6L`WR@3J33cXNP>#yg{lX3YY^S&4cXls>-Hf-4Dxw%CwR-i+bs&bH2TG^Ki2j)r`H;KvW0>ZS1i z+k0?6A0_`kQ6)7WOMOH5FX=jdf%c4leVxW}*Ni5Mp!-^Tlz2*w$Z9*N%CF7rZ!?}X zkaQDPOf9#aYhV1!0fSRpVA<>~qZwjvi=g^Fmy5_EHn`{S?ykf}CHTz}ej}H28f+}C zc8lmqVwDlyk6>LF$I7pd_aInsoH7dTDJ?z)L(GlNKLs0haN$fzGR8VvNs?6El1q;%H*%z_zxo2n z`=i3q-4}M570bwj`A^vLZVSIVMIY^dOgY-9>W>|JK%!M+A>rNNpeCLwJP3(y=24JVW>~f{B|4stT@9+&oB!mabvF;s^>3rKtSJk>Y7om zWd2u@U{|u2I@~vpBsjWGHpv{g(E_hx`}uSdw(FPQ*)V?nIONOZ;wQ0p!0z%f_mW zDW1=7l(I*NI)m`3-pV--oa7 zM^mhsKa|m=j2@Ucg6tz)+#CK(k38Fpy$xl>h-_Q^@HlZ zc5aDlqy@;*%<6lB5MZGebV*0II!OAon~hZ)Q#`uz>(pmymR56~-!lJ-++GKFZzt(r z3IX=p$4Sq2)QipP(B=F2t%_H*HWh|eYw-Sq-?lxywN|IKvL^OaZB;~r{|37T4eHEJ zVzt+y@7c33tc+0c)G?Zh1`FWSP%Dfs^`f!evKSDv4;?K z@|>-h-sDF1d>&3C)Tp;{EKg2Y^k^U%%CHWHVsRUoW?CrL8|BJwbm`@1SPTj`ix;lo zPJXSNsd$M{TfZJ2l-^}0Dc%O5iwLt$Jan(S0iNUOhUQhAayLmfFbo7z z`}ZScF13m6bf^&mrecxsW(Yki7fm4@(v8Y!E?hD6jFBh`Mh#0Hw+V*3LV$(XOI65t zNv@Opk1!J7*eQDmz14C_@6~ephFt=@kw&GUQHD2rOvZUFL59?2H5*G4gEtUhOlUap zwuv}_5uf7>Wr3y``{vIBeh5JV=^5zjXplzesHq%%a_TYJYU+dYMr_p`Fx`h+Nr!zF`}m_lm`Bj)uOWXyfDc7V91h-Bbh% zZS)1%NvXF}{1$2_8E~Wb+iwMkk-a6!H=u%6`c{aTx`~-GtCc3m-l7@F7Sq#4{2A#y zqby<^K-cn}I)o?3e$fkfc?n#H1Wmu^K-R$si~zEfStOSFBMAGX8K2o!F?k-sz4nM) z-uSl3Z^wA-ERse@vIC!F+9Xd_U38(~+*UaFlgWraLf^gzVaSNJG`84;p16PF_J7P+ zKJSyl?~ciKQ|r{`lB_J)=b+|u9sOZlkL$9oYXYI{6hzM1X^;5J%1bAkdpJilimYHt zy^=z$XwugO#%v(;NR^h#_5umETqk-ki?A^Yk|rWg8M4)sJ77pmM|z54XYEr3bGK(5 zdFLQ{VI8D*(qzX8xII%;egbkNqrQD4>tMe3gXnBD&#M%LjQ4Rm#9_zJti|*WmFT0B z^SO_%n%};Iqi3~yj$BmIEoCK?@+7ZAnq4IukODcqI&yiNz+~f_d;8i=x$`rVg!W83 zN0r7xo)HFzrZ>L1=_lKxBHGvLJi;_84eW0IE~W!IN5d(ox(ni=VV89+WB6F z;afk5_7(iR$7{XfKGo#IDwbXsf>?}CpTmml_;2T|SYrMr^i_co#>>25lD{KhxsmuPyY!ceR0*X1{N!Mh1dz*cZ>^91Dz^08Q} zoPq+Pq<1I|z`foZPJ{ej?Vf*XQu%SP^8_vj>^>-Pa((ZgI8QWFan?gMx9uj~ zy7*|l)cQa1)YN*0R;BU3uZT}NW8(h3CuC0%0x0l|8dpG6H z8jTkI{Z~fwuRbcRkIDafNQ9x&8kVMmqLtGc9}$06OMc~2>MV*sOlhr2V$Uuj#&vve z^THu-lj(h&PSNG6b)t-e0>W*iHMwX=F3plAj9&Gp32|IPt(BHZ78xJ+fO90@X1%mP zzEMV0zbuc2OP@>XIbW(&j=lOCkg>n>k>+u=cJ$H!vgECRyI}M%2f1GYHGvO#4jtdB z84*8d1;(c5x4-5&*n8&rP&l(d%5RE?i}qUvtP-Ccxb%m!UQlU^m$ibrN5~(TW|PVc z&bJBZv>K*Hkiw2@SXK6iL0K?*g}f;PoU37gD_N~i{*%wQaWp#D(YXHE(hJ0`bE3=Y zJrK7FUnH>xV@F8VbbZdIeUmic&U7-@pRDO}4Fji0YB``$zWMe=B%eJ*N|?ei^)GaN z7{Ag^&0jtTCR$HY+;zoo-A`+gQfUF*4^3%cG3n=a2y7dfVMeE2LqngyddBO$P`iAa zmymC>$0Uk)jTAWKv5!wui;1nY%#qDA=C^edcqC$+wXl!UumEj-@s9zV+vuc?;(yH> zoAlo#Bh$)lDJ`0rT7N*_d-k*Ie2U_Ksn>3u1vHj6V+L)rJ*}0kNouX>Hj~WOpRVcl&s zc_?%wyfZd(irojJ=Vqipct`&U30$xB?$ELPPcO(F=-N9_;o{nRV(9lIu$$o4yhh|c z`{JjGDZngyF5FO`OMvv5C&J;v_wp>2WcL$Ala2Lq@u@pUyvUEGcN+}Q>#9@ z@kaU|k{ah-zt()T!HH};0xN$=Hd-{CSXJ=DcR zu$uguSq%GCMdmRRqs3dsDKlM_LdZ1fdHXivu4Q_dMqysm<)PteUs46jtC>yYF_v zPV)M@50cS_&Y{8;5{lvUZmY4Xb+NNvSSwP8_gN%V7~@yb%op&r!RT7U>W-N}Sl? z)NEM2_AQ+WZHhY*5FCyAv%P3qq;sFJBKz#GZj)=pK0GmHtn-!s4 zDqb}>!@O+rBWkE;H-D>(6Qe0o=mq%yA{in^1W^9PfYEVw?!~F$p9b=%*~91nL=!+5 zjQ~%W#ol_8IrsU)@wbZ5&F#XE^@*ZFc+)U46u|_1QOh>2Xcq*$xr8QlrCh>UFia5R zu`Sl>?)N2u7!8S(j3!S96Yes8$5p;|j$VdUw!?%eYIq0A?YTSP+x35^RBtg0 z^xQu;tJf^s+*%m%CUw-%LDHu^*Xt?TvJV7n)V-_Mtl#!;R~w~}?o>0`RG)1SQ5x~1 z{kIkQqb+UzLAr&2ioKe#`r~?merqH1jK3@w9EWwA6lua!wtsLMYw8}H)~ULOu61g| zz`jyKd-PkRwSNx&mev;kBBd^(aMO=Bqf6m<4@V=P-SC`J6r6wyudTLT`duK^3$sCg zl2fjP9DWED<^=PFXtamseW8Jn-^HK11RkQ5#QpCYSCa5fX_pFWaEREV#5j~*Io+bV z|Irc54b&;)%O@0Y_zl(ggvTpk%~4pg8g`-I!twsB-;jI$ zAdUSrdunJXoLvuZzGbn<;&w4$xLWs_PWyf|xkP>~5leBW6C;$voZ2F`D0a{gShbb+O0?M8Ua1abySMw^%LHO}I?>4o00R%dwB?MX-79(t}2LJdx>;_#iZkKk{9o^qU*}J z+*EiE&0YU#vwzE)?uXbZXi(E3Q$Pw3%vMt~4oaU7R#2 z)g@AzNegXRp(ia%>fIUYMQ+l%+zot<>>naK{+GkhWK3Qy(`1bPQAXFmB26>4JX)+b zL_<%Z)X5aTms-eP}TVm$2;1km1BA)yr2X-^^n?cY(2B)9O% zg?x}co+k~UU)Z94zr+h3&r9lY!52=%LLe`Vp1bq2A$aNWj<0-Ax0h_r?=fFu+;Dut zAyPzV_vquaLgJz*Nq`LF6V{SFQ(}Zj596GSimWl-dJNgbe79b@wZ}VM!whQkV=juY zt{>KA2cM^j(Kxn_yqNPf$WmGiSTg^`UzKFP$!6K@N$wb#__bn$wndZq9i6K&G+y8B zZMB!#OwPwL1=e$C=I|n%m!u5i$!CL|S zR?OARq>YO;dwYHxp){DReblr3KWS5^mS1b zJY3DE_BSpBw7I?c1Ws1KqhOFf7`t-VmGvM$_31Ob+n>15=!MIjyKrDDteKR)4x}eG z!qG)|&>2x2i~bXf=-)G8`OlBG!d1g!nZ$dv`vZ*Hab2P^dTy2e)W2O+hD&qwmx3iPjY$&i^?M99Z z!EPe^=XBmOg*nZXeAny66L==dbA$D81kS@Uv&S>fL;9d%n5dL3gIv`!v!{G*sM})R z@Wm<+BHD_czA+@oX@xrm-wk|oVC8tX*O$#=hHp%PTvGwi5yMKDUp2q?qbNxv#DnNn z8!=e>hi5cDJlU}o*1N*7G~hhVg67~YlU3X>@7Z!cs&e#;&e)htw%ji6K|cmjMl#6# zULQyw6p=k_(kS}yM9~K~-=m=NI4n?nlQ~#)Y1p|;e4#VobrW^~rssTc0g{oTOVU@#`_=4{BYLn zIdebyc-|-f{waET(2)6jJu*_WfSQSf=8XIwap$%g>EYguWDOtJ^LOx6m$SX=1!CcM zsvr5Ca>1>o1(R4r)TnZM*_=t}+SRu1^Vd3-U^8B!8TwX`-`Y_{djs~0)ddqE?I!4B z-(Uu|no*fI>2_u|Mr?jU-jmadM80$s z4J8AfS3(L=c9R8(MozZebrTKEo{T=p4`+`$<#s2Zu*c^fV92nibc~EFw%9M8k__mZ zjLv*EW_|4C%G00=kH`Ue=P|YS|IP)^*h~cS;V!E0gG^OYSx&o38WzI`4z!7CK46-9Z zke7*QCfz(B021u z@a1=)DXr4*AkEbpoRM@>91w#W#=fTP7a@v#aTFYY<#<(e!#LAI*x^&~>^O{87FD43 zl8*1c14ccsbwkF>;g+UhXF+`$a1m?&p09Xg)JoGNVjEuMwtjr^%yDLu@qHL~^rfJb;#-C8xxoX|5O?IE`^N^K5A^N(-s zgIbhne__isf1Nb$+lz?$Ea6WRw<%`ZeIXvk+N)BTzJdowuoSn`E7-B?DWlVG=ahoU z)v*$PJ0JOduyC8=K72?#sHD^5B<73Q+?m8I@=Yd9^%l||_3S(DnxV9>y~lP?#{-5_ zTbB)CbMhj=W?VcRkX5mM-%3B6>rK1x+lbW2U8c5A8h-o%RLnsGbVL~5EIR-<2Swoy zUm7U683)c~4-g*SXgd))KL8aSqV1hrSxjw*X#022$Ky~CZqEj6Cm${hK%;vRU;QVu zm%TIWaS5pQVRY!)r++xkHn3~wBK)qM^U2(C-gHr4bR>B(-lNw>4)NeE0!dNdKIM*E z5mYsJ?r3)Y{eBVzA|dGmi8MJGk>RTGHo}W37o!=N+13DLHN1ZnbW{cXau8pFcb{Fd z9^)wTJCqanpl>7|Bkwuyc8BTco*H#$>a2PZ8hPUn3%;mUjoXp|X;x$Fi@9zm-JUuc z%bzyuwo*ZNk#4GP;JJDyx88c&G!)b){<%|H{c>z&#nlzHx$boEpI2O7O%|Na%K(kK zzdlSEo|_wv(Xo_z8^y;^+iGCahy0NjFgk7%L|7i4iLTuJQu-fInM*Ek4|)*~l|#X9 zut^yqH(wbD+uXorUI3}?mlqBpXelvX@{C$7E`+`8RW=eMIYtoTD}QFFy2@hMPbo$T z*e)EELoqBOcP$7nkt>+9=Bk@2;*t=HmKi%N<_3%Ff+Un!4#i-I`|VQ<%c!snm_SnO zSJ7B^GfUjZ_1QxO*gvr#2*J2VHYV&rS^d|(Pl6*MPgzFA%qN{g!6-Bj90n*h zc2g8>f_-9_GHS2877QA=tBCwbH0l^xV-!h0VW#J(JM zS|Id~eV;`p)AH`Yv0_Lm1l*i_eVI1C_<=NLDFpoZH&k~tlN1dsz#je^>#H{BtyYCL zr*L!8p_Okjm1_x2W^6k%Iw!V3)J$%!KCjAD8r92KzWiGc`TD{`E1=p zQQFj;E5Jm+Vel+~n7P{)LkW67jNh!>stEUn2x$~4C#4i7%c)q7j4Y)LT+_gv5tki; zbb^5-4)r=OF-XRp`^UqxC7y^mfj7`;9Z83BS%+05ohVnz#g@Ltdl_-*bn;P$qNI@MSFBt%UMIp^fX({|oZSn%%5aVnONjB|XR|202t>1Tf+~ zxTmnX{5#fwT&r19 zHxtvQ>g8d2xmJAvze3+7yJpU`_;~#U-D9CmZF^jV8qGdmXIA^`^``X>3l_98GJ-Kt z!v;kIp(;34#8P)GRzsvimQC?zlE@A>AwCKG&99n}ELFxd>GN5bis~oz$^l~UF1`FM z$*6!-k#uo7V8LBycX(=i*Icz7S8jDjS4NW5NpjU>CB^zuxJ~HiJ!Hf9n{J?*wkr#& zNB2hoz7A}iUUa$to%<~?+52gh>}`zVWb@~^5;MH)biQQy;B~@JW!Fg@`75HSwU})+ zjy@7;9jQh(b#3>%U&Kz#?7C*La3(1t8BRq5^AvqDi|wzgXHc-`Gs*_>0F*IIFyCZ3PcS1}N0YAx z+v4`lO?st6`a5RiuZT~Ud@KacPNHms#kKo1mw8uBsyaXGPBzk>CT8|7JYGStl5txx zkd>g7ZK0CWEap}iK?AqZ92MH!tZQ7t+wQdO8X7CRHeuv-TOZY#8nyKpj9Am4ky}Bj z(M{8zs`YtH7Rhp`gQja)LSLJF9rpJN+X?qZt!FXp#xkC(?0eV-eJ;PL5^)y{M97nb3U|)HFxe(ZK;KE_3X^<$GUzIZpDSa5-fAfNB8k4t za2Yg0|Beg-OAI1?SG-EZS>`Bvn0t%YT2v#B${!UF1Zh;Fl_~APiq5!@k?)sM)h5>= zn4CrBoT$hjt^<9_$iOa#z9i2o%=M4jNJBZ!Kd>-Tk!c8UoLEGdTG0a>a44i*mxzoSCFJKSHj z$P2kN|BO0_wn?ZqBG)^dAkt00)YdED9>+k(J1)DY-({wL=4Jam9$`I1#D!lMh_+*B z?Nr;9a|FpZi7__5;6a%62Z8AtDRRm@48~b~FQ(%KZ-Eu@rwXi$Dz*Z{?xxmCRMDp) zz)yg%+ie;`Uos!Mrb898kK#{QldqOgS+nky!gWswK$HpZmB1|RMgF?fGl?hCK-Es_ zoxdvQ2|0XGvp{r^#*Yc31BVO3Wglf<6b~*$n#C+MDpn+z68^ITlA4rfA!=Q?Yw*3|XH=KL8vuwzi zlMdoDl4Wo*1O}!|{y@OXf9_o5LT&-#A-F1OcQ%fgDrkjY-;M4NN}iLua1J&is9BF1 zN=V#((_p6}aEt2V@w14nvmhq&xN1EX(a_U8Jwoi{nWu=X^dv&i_mBw8m{- z)0wDaHs~PfFeqHPG#bDxx0(HcP@o*Z?ls*g|CY=WN;lrpO&X2i| z3w<+Op{^-N=S)<^d-75~$87C0jB4Zhx$`~Z-&wsn8rE9g_rf~^wNXtZR3e`5GKa4B z6cSJJ!`(h;YZpky{S^}qHoH3i_RDGJ^V<4Rcr&WqnTz|E?2=C-Wu!k}IRT-}{Gp1v z)^n$dEStRK?>pbysN(T5{tm-?%rriX#^wn6GAYe~-}Z(Oj@YT{ed)sqYitAlcH~1u zWE(Y2!0YE@OoXb0O(vN{co&ubA|R^7?{k{_G=&GZ( z4w8nQwNlh@C~r@l#*@yg)$Z1vFK(2rc|?tdayg`$TFh?*5-;WoM5L#3kwjK4ztj09 zMy%{IIkdMym+s}=eM~XJBl{Gpt$UxR?$fI*dt1}1NR(KVM#2Yir$-3t;qJtpCeOx2 zfh+~J#xsBT(b9vDz=`;il1`ySJ{S_&ljoNGMMm1?x}dRK{swgT^Kvr0-1+A`+bsi7 zx#CR*p=hk8UByLC;uyGWXef)Ak2UI=(PgyAhxov2ejUAJ;&?mpS(7X@bo}a=p#ZLL zc$)L0R*FS2N`g{Pvq zFf|{1Ppg@G;B|SKi-2!LV;nE=0fX+}da)wXbIIquYvVYyy!F*M?!4;nr=z|WNq17$UQw&2 zuHyO=s3yQpv)awF+U8y@i<(fhGynOSR)_qkBWZ~W-Evf?2+*+gW`CP#OB-&yS|;l6 z!0Io0d98nvrvLa9EwQnSQpZw=D?VOwT{69o{iTj)-wrqlOs<{k7s=8}@MV%}AbLA= z@HMbFCH1C9#6|&=Z&)^joW+pyTL(#Rxe~=LQ^z1Y{ch|T#!C}EJ792O8MqDB6rrnc9GK<83HDxBsXu-C z7d$}*zx$OW?j*$J!{7HDoYQOyzsazZMh@J!6JjnwvCQG#TXILVV19d#%&}^s>vlNy z-u+%^o^h0(kP9crkqo`L+M4ZUZ&+CjsOXXz2+wyxDn0*D2AG5y%&SsJ zL%|Q#tLNYK_Jvd*sP4NP5r6t^9qX=mi7PA#`Ug?xm{@8+T}!j1Gr;UUfY#*b|AC%F zu_|)-UGkWV{EMvp%G(EBG}leB@R}FI6oI4$!``!13a85PK^q|*byfqm54!3HmpgPc zb9vBVZAW*1Am3+>n!5KH_#Z%ykSm@pDgBl?DG#E)H}8}21wLV=!7|Z^uK8xJ*50sV z;1T1;%1>+2T67x{X;!(u9gjY`j+v}Ahqd-KIwx);vDwsZBim$VYu)CyF}G`HxYqRM zdF}cApB`wn*+&y2y%q({3C;7hc6B<5&Ab#qYRMQJyZ7hVx26Ad{9geYgqEpL;CK73o`DEq0m9!6D+qAs!(5TwT10lqeO`A zTc7Qd^p!m?M_8peh+=oblg|=y7y8_JH@OYug!}{>B17cB8tZ)$(<(7n!+LPT=>1Yr zm4bqcsueO)E~&Okcwox4<| z=>}fZ={@4V6@Zrm@BIpmDYeoiz!9K?iQH3~82$0av zy>pr;oCt<;>-2EE^)otUhTD2n3BJTLfoio&07K5t-wKI@9HMLw{LE}eCQn?9swn%@ zY~JLTJ1|7ZOE_g#Ta$t?I6-bxYllyjepW6esI4`7$%Z(WSRtU=7Q&vPOXN5pN{Hqg$y*ma+jD#||Ue_2K@ zpn#ec+AoF7C>RqM2(IZMEtKAvx=vB5xaI>Fy7XU!7d!vT(ngQkun9*)SD#i)kz1{* zuTUSoIcL@RXdW$BH4$Zx5p_1f5VKan>>^5Oz|tEOqCA(|2)JjCJhnOs^5_%F*_Laq zy$p_-(1>~*N{%zP=nNv0Jn8)k^jJxHp6!o-7ANw)sa&OQZ-NgGX=>%y{S7f$YC#yll zBJW=-@kTL`7s>GLL?z~%K6`hul*_TFXRgBCNa<5#ip2Z{Bkp-w{Mw;j-NiRTfIg|@ zj!ZWl83@X!0w>-`ezaWYEJ~1CXU^arTa(2seVv3j83c2(Q^RjE2@pImmOK;?G?t8h z!I+kq|0o>GLdnmmdxM>3t%gA-UlKuR_T6f>4F<@j|!JQG~jX>(`r;~3h|d( z9#}LSv6Ww~;RxG!JZ*$29iVaIc$E%XtJ_+cWvhj?@v+*j(Nu%^ukBOyh!VZhpk+Y| zp`cA@DHZA{`XmC-P<|R?TApBGE?46Yox3#d1 z34w4DuVCqy)o>C>z6dSWs^~%4)Jnukq!Eq$n=_rQW$E1wa4WI~VjZ~wJe<*Ygx+Iaxd;tDS^BV)!~c$V zC-0XavOoJ3L@cH?^F9=P^N|T z7uDU9BH)`Kf*1UB< zhdY?<@4ZXn$e^?IK!m6fOa#I?5q44?3>5+BQXVcX07RHX^8mB7S?s;PmYWuLUP^jp z?|{wrf!#eVHrYQhiVT8@-1F5CK0eA1*2yjK{W~c)bAlrOrIEEe#SL<`lAVwru&_Rk zs)%$F$_hbv56<$0FYU+|5s?@c0hcOzU+FaIx&`X%z+cld=X8@>PPi5n?FpxyZa(?6 zCtZt4rH9b$^wCCE>rC=L1gh7|B=izJH4d*qAHL?+hL%&vAjDT z{{*DSSo?!8ZreWi-9Gp8yD1QF_OzdmC3%aFN(biY-zyd%#;)xh4>Voy#)H^-o8v2h z*<~4Nh0gHjEh|QCls+{}v@yz=K*34N3*@*m+5YJx`1>3)?j`3ac?2~zu*wR(00(4l z1J>b56ck+olRPU%Q87VYP=ZcDZjk(GKWX~VA$=lntBsrYagol+KiiP&l+YYM^4Ne? zRwszB+#BD`@MtYO(IbhHjjJfNyhnjy}nf3YJ zs}N^@wu4g@>rb6~0jly<$Vf|wV->TB8TSt1J($wJ5^V4(XtCPzs$9ePnl6M^H?Mj@ zsT^>x>ruASFTH&Iq-tm*1ajN4nU&V^p9_9x$N|5{uxoxikoUCS?)3*N{r%m-3H zoeccmS8;LFdP;+OSQkyOx7SH;4^d~GMkA>P9W;Neaj&07)CUQ_pZwG}-lc>2%5EF3 z!0I`sB05;MvqRxHF88aucI(;5Sx-Y?y^EZUV_EYxA3S|;IVkQ(yxCR6dswJ*54^b$ z83$V9xhHCs_mg z_Q4#Yya0Q+C?#6q;leqi=L%ahQFQMQbW$}eU;A$}mfw(V!TSY51K-z9G-LKnxw!t^ ze-Gr-!g{8?b54sU-G@*0O=$SPwPBi*w zigpQqLQB_`PN^~O*#T-ZWO9AFR)oM}#^K4>`)gqBw+o1`JL!mjQH)C@8IB}+-2ENE z7^gmf?AP$#BLK*lev2M}@%9dPOUww!Vqn|OJ?%;g8+l4e?|KUpiQqk?Ib4e(|x(aM8UvkD5TZe+cLd;ANL5m`McY2Stj=H#=Ujs2H^_6wMhVOJl+0 z`Es;s<#r<7?t#Pi^;4-c-6VwtbWrO z-MUjpBNuGO1NCOLr?Ree(@k$}4nsaPYIhe=YP_C!Nd|;|dPw@B-}61d@Y@EJ&X-;k z@y14{rJsAvyEhcW;pTv9{q#Mes%|kBsEqfAE0z(G+Nc@!R~(8g&~M`GOW2tSuG|uG zl_KVRnxo{jG_S{t0o$P3eCm?E{6@J8nznR)=Vc@enJ`sax)9TRwph(43^GUa{5VG! z^DX8N7eQqAMB@;gP@AC?fqTQKV?cb=>6~iXM$2`ds47w083yZ3N$0;-zQN*!qMK}y zc<6xTUpx49mQTAA1lu^NBw1`++Z6?Sr})vIxqhzx>xyN%lh88Vl6M_ZL_0*If+CQK zoFFG(Nv`+6t?0P$4=<@tzko<(N;=5r@h1(8g%SDufvv+L4fzv9p@Y}>4-bI^$RMCga5cZNLug!Fr;A!@izLCf|QTZrK2>RgXDzx` zvSEwZ)cvwif!4mdI&1NMjrt~6N?lDbq^_0l!#p&|of>W$WWo`48%^kF63rX9iMop~ z4NsfcQM{G?;M%OpJ{JVf#GW@qLJ&fs#j7YYYI!tjZeyeNR zC7HTt;M0p;Efe93Vxt!vUBfeI zhBYiy=MPx7p>L%~YoDUAH))|ZZr;3kUi)sMv8*x0w>m}Js;8q|u!Gd8dDAReE%Biv z*-S$GvusnjqGzY1y}{NiLv$4A5+yBZI)n?y9g_Vy>@NHNYX9}x**~8zm+`qe&ZT=n zy$ASRy3eIL#$2$X(?hP`@3vm5n-jLyMqe49(s&}>P1HLveg}y$+HFbusC=ykez9G3 zr|-l5m&~Gf!E&HB)4$sMy4w;hY6>iBDd@1B>5$DNLN48buHhzI2EO@#h%PI`CD>9h z5ce~9Z-^g3^j5mQ#l7AGU11A`=jbrVjtzzvu*b?e2ClJ+lj57`(I`AxR%AzfR7w3r znl|_%I^Kdq+qn%chiD6LLSL{iCtD|b$q=$>XtLF*!Bc)scc9~YW!-@}7wj>dKxb}) zi;ixw=^I5-)00y#*uU5S#8Y|KAPVs7%1q3kf9PxV<1+|}$BQg(nd3%k(`B@^$Lprl zeQ?448G5b-*<$b}+)F2%ONraQVpD|W-yOz9EY~(p^hyj!H3RaGK^Qm+ZNjH zQIbt_6Nh^)aEwTWaN}M}znyzyUX^%a*%zu!cpe-OCNnGd0naa`%dM_Ku;e98Qa_d^ zd_{7G1a+1s8(JJ6**Bg!Jp&zC&H4qqlhF5qRc`PIRnvhao^gkxtmY-xOeeXd@;WN6 zTfSD*8u0IUj9vt9?H{jtiAIEoL=(?6d|$E)HjX$41ynp^bgy?W;YEfPO-uU7@p|t- z40T$l68>L5bl)Y@IYWkKAA;Hng^pO1&(@bsl&-h(WO;;uzhrbDtC(F0;K61WL7xY{js1G6zPdnM2KAzEp-XtTFH_O(VaI~N`BQ(uw=d{xr zh4w4$gY<55bSz7O)Yj5`^>tX=qLsy{UDRZ^(`2Kg-?X=h86;n%4`5F7%j@LjEr|Mt zt;wi08!KHyqy2HU=_X&;+~8CDBb$C!eGY-?8%)Fsh&qoT>~3j~aNv_VrgY6$o(+Q7 zOvw2u<2EcsACoJc4vz}^1}&D^RrIk(uwv??d~~}FU<;(AYnFHkLGqyvRY9VrpPS zHi?A2pBxK<=WOQ8$2{!pU%}w3o+18*DT#Q8M^-zySvZnA?YjZSI%-XZKo&r7O2CZ>h<^RrNtV!?f z%rr3q;}aWo0^2Bc427OoIUnPUnOZE}DdHBVf$OA7r>lL0hycq~H3Z`T+y+|D92)iQ za%1aKgk8nc?``nz?#9?<`NEtuu=b=BPn3$pi@>}tx85aRgF?*eHjE0f%OkN8#luvX zMXF@n0ed&b8tq<9$udTC4(wh!6kG*n%P4LwT$#RQ6yEwUK1w%++cL@y9~AL=Hpc}T zKkw>6hWgkYls^-dbhh5)uxnDb1$okm>~=0U^BI^SA)Dl;CpZ!{DH_6o!%#%q49Nqs z{O{+=NAwz+;HR#kx!O+_`gdcxmpT1?m z<+0gwVsT~SL@t#2Lc>(H5oU6hB4{EL3*yW-eCTp}>m~jDdtdnqP4rKTZyG4Y7%26& z=ymx^3TO0y>D67{u0I++Rd^KeIZ{4M&xY3v)Ske*X|qFpoKV+BPn#j?;)VjPEsj{n zAo-QL4l6G~jjo_ibxZFiv%1OL)xMU|bAow&$k1WKb@!lys)M9)H*1e(1s^qHWQ#sp z5OkQR2=Ftyt*13Zqy5u0r1k%X?Ja2{_Zl+Vzju9F-_QOk^!81WW|EY@r?scQMyX3E z{vw)Bry8R4Dx3HAE|>MEzi;mP=SBZp>wfGePPb9e%{paU!J@f zC3&n4@B-%7JsnCH5m#(CCgH86s=D->_V-BHyyps%AsS({$?C>&NJba$XQ~lKsfG)|0a46j%bS2n0l+A1S5#%bZ;YE!`hz<>xhJ|S@C#_^Non*@r zZHjhUv-#F(eW9}=e-#?cr8VfrR(a#sGF~%ZTW2054Sp$H_N}8PKL+z~X)!xIQQbin zim@CGQOjM-5;T6k1qU5u)sj3!3fw~bvFNyxs2;j@ES;#%vrRyogD7e(v!_EQYM*O6 z7Lq!;*6ue^-J;)ZsPjryH0{=X=;_I!wNJ&;;oA{rDb-}?tv{HWL8i6uBi_r<9*Vs2 z6wK(F@nBv3$xHdo_;dUk1@EWhTBQoq4*}tybq}2X!H{j`QI%EGk}mF%rG1278i6q+b@9 zcTHT>`3D_XQMCvoRcnO_`CP>TS46{YlYaNXJzS-)ns@$yg%w$#t73gXX0idwZ=M3#%FB`@=mLl9qsKg;;vI0G@oheLQ~OJC$~adiN&? z5f-UVLW835M#=1BLo@ralLW8nIN4s3MPdyEEPeBLR2S{gn)fY!i>YJ3#WkQ*U7AcB z2ixA2-e#U;V{CRQ8bRHVdyG3UCu$fofjqQBoHK7K^ZQbVgZR}i(1HW~*m54A>z&VD z7}AH|zXcAB*=r{Gd6S3@2>?glPmJNFTtRY8jEHAX$(XVJ#tFpLAD&T;#rC)GR{s0_ znXe74m({*q=DG9x<@}Y#TeU)>E+Bj!DKy9?)f1%IDo< zPI1*8c2I@~t?!LvI67+VrjOj5(sB@7t@8_npt#w8DcQwEKwa@es*oag-jj2if05(W z-AY52t}Qsz6+swxa>@iJ>z7o5f-7~xi!*P6_h=DLWRM+3HaRi<90<70%WxwVV^l~* z0k_UM3c@8F6@GX*xZWg+vAZc2)7jU-JZdG}a;|*8QgjVf6Gxe6WY|py@^BZ1GcE~+ z)vpommO+$-Y0vE{y8?$E2OQZl9QRkhd25r?w_ZVascvc={&H(&x+7vYZ_za0TCUNw z#f-=JFgmaFPlFoGs!=7P<6k>DVk0BePU&KLt*|_;qiD19W+U=C>Z!FAx0u$cyR9XL z=sUk_?qPL&uYRWw%}8i}V9T_wPu6SBSvwY5U)|1RYlG8#c=V4Ba=-@lc08qyqxc=v z{&xk1Rw4C8SnuP+U!FMQU9LzLWh#EjeDJI@VrVdUPCA3CIq?zpj))YJjR?-fT~>LH zy*pJVaG%}I6)cDM_iP1wkGiTfe1ls7N9a$%bFAR8UP8Swc@q^2f-(+c8+{`Rg!km0 zuV*Wd&}rv@tUQWVkgC(m;bRI;NiUejGyKV);-kU1ma|9)M~zMW&CMxeIb7(*yf9pe z7gt53wy3~Ns2pZ`h*j0*qiJcO?bai()UsSs5Dn)Q#*d)9#|dWrFM^=>)1@39|0OZr zGsQa-3XK&$qPQO~JEG4Db#AP&Kipy$`mh!oRT+lkoyuekybmUq4?QWXY3d#s8HN&ytYS% zDjv46(h%o($f9a5S~vVjW?Mn~+ywVWY~+ms8ytutW^nU z=e5&#>~EIU^2z?;%7dVKaVr07&wPG`g2yv%3yZ?bpuPb7dHqLU{ZD9$cTum@Y+hZH z*ZkE5n%}gnX-ipuyGwySuenR+11hv-3Vaa*OSGZE#P1Xn4 z4UC9Rlycjx@sR4xUJ!W?d^9E>Pi5)L5lN<05U8J2c!k)ow;*nA|3bfdMoca z)nEEK$Jt_(wc7?F1j7+%E!#HT4&FO%xgX1BHJe_=Z#=uxdG?7LBQdcrimTYoJ zbV;IE8VPfH1k2VL_^RE8-t4$y$o?@eKQ}n@-p=l`PC0N-FnT(Fmc0wd;V!*hUw|L6qyT#PR7E{JJS|Q{MdtJB_-f(e`hyuSQe4Ca3wUYbw@lCv?})P1Oz& z(RPhS6WBTw%(@l={+!O8hGuo@61=rEjgZyxx23FYd4aCkT(l-gn@Z0n_ukDj&2}1X zW&io6qi#HOd~`FCt}zg3O?Msb8HKtLYC0C0T=QXQnQv<(T9()ELyEOF9VaREF$x1M zr_c0xFNl$x&4*>`8|R_eRGL7dJLMS{vXV^1diH|V{jOMFFM}-6Noq?hmZPoBfOWR+ z;)Aa6;Wo-4<=bP1eE7uZDeI}{5VY(B0clIy>O;OMz;;{ z`?D*s*qtzO->0Ict%U5N%)%Z(nq4sVm}tG#E9g1;oqH!BMTq0c+r zo>^}BB_A}L?h?G^*)wNicoNJF8*}VAsEXzzuS=Fs_}%ESi$hFj?_kL4E0;px~?}Thj5G8{C&kE@Cjs-xAy)=etBE|2ar6<-o#-cTaS=Y8L544t<`+ zMY4HTmv+EB=QX&)xSE9yuoSGImK$Y}X@wr-8c2%XbvR^^*IOd+KvKs7iGMgS7)UBc z@N~F2Y=1Q*$Aisn$H!w_-N`!crA63BiOh9WgJba#$8f`3xEu~S<`XCgK}VMBwgm|N z0!e=HVFT-hbk-kF()mO=k1>KYI3^c}`aIza5s$GpQ~{z5ns0SL5dG!y^p2=TJttaA z?pPiH#6d)N$Bkc+9f-1D?hZTOIlyi?2nd-J`=f|{F-=5!xolVNdkk#mAT%e>;sk_b zv}v#T@FFGp1~C+qWI}a6DjCqad`%v>cE9$yjL7^u4Sa5ZogR>iv2h34V{s_3Po@wq zS>)55^i7?VF_}99FRhME^u*tO{Zn{Xd}W-^u3AI#EVRIX%0!8uiG6>Yv!4}XN3vz) z6oiQaskNjO*0Ojlu$DXDUFez&j3e|qCvFq(Dzd}CM8C}ur_rXu=S8E`?6Q|LrI+^i z8D+jgDh^l+1w96?{S~hTe1EGXzIT8@xCP;T*Wzm>!Y1fYR~PlfxmKgf*MKQ~TL7KB zZgEqj?B)T!1;Bn3O?II^XC2yOP(dWF_Y8?2w3R{M`} zO`0*}_Ga}l?mBu?_N|@Mu<~~N)s}hxE8ws8es25fwLT~<#G3ZF2sqQ?4g@f<>9lID}O5n=e&C?SZ2Y>3u+mCFQ{COCUh8M4cpr{mVrsf%i(7v7! zf-sGHT;V`EphM+DNuO=(TY=14f=p^Hsbf>~&uj-1iGQbsgOSHbo=h2Y$QwdB|5n#w zib~q&9{6-;2R|k!7QRFVcZeFz{<8lHn4Iz;i>EW$Jpw@LyxA_~bp$+SE`FQvYmD6u zRVl`FK{NWDtYkYUxjy>Hzzr}u*Us4JqTymWYxLaU?tM6EO4xpn39%o&8U&WMgKTyX zqACn1R_(e9^^Y$T6XQeFLO!~W({5^|BHPLf&WS9d#lHozk3M?zX8-l*n-~UJ)A^cn z&@r5HBu4v>LyhF~?9Smn@VBH`Rr`sPiX^+O<9KopBoSSV>ppBApX*LuIN+Inz!ArF zlg(Vd@3+-BCmi0au(^m3RsSLUS5xlL>^Ro)#3h8n7C2h#&GeV*FKJ;Yqh0mUy`?`V z)l1UsYr}T(i3)zXE-RfEpneMc<7Vew?XGm6s{cRcz5^<%Id1c?eZIq=gDn-PC z4LkO(vBYSiu|%V$*Lk=h?D~}2j_7ot z=Gt1f_`8OD(~(QM;zwV)Xvr5{2~v~NY?kZ(*54-UQ0niAbtpQDI&FYa^|Q48KbgUQ za_xV7l9sTGq*N;@K8#vrI#6;0)%t%PH}ABuZ_dPno8V*s90#~7yGonwm!!`rHL>>m zkV$gA@M%VCG@ldbs2Hn!hAOT7lFVe#%O9zHc?2vM40JyZz9SeFzt|%H*Y0dKI7x4J7XB=q6#7B6tUPMIL;i<{ z<%yX+o-Q`3p|i7pJW-!6_-NF^r>XQz;6Pn9day$w<6B*u(JS0?2uoD z)FCKEN*HO0ZFdWAcD)zzY<6dm^f0MtLP15h*a=sgGbK#bBXEj=FL~_P*U%$A0;963 zf=wR))*H7#xkElI$8qsBA%aZ|1klA)LIjX?XP_VhdT$;xi{CpB)qXC-TPgHvTo=rp zN}~CJ%EPX&Cq(*ymtABJ=K9c1&s3X&;V~H_E#R+zd~PxL&KESB3v|nQfFBOB;2vw( z*i19SbxY$)}!52r&R;;6&tuQqPgp_&Mo=awkr_1{uTKe0z62#bw9MK%2o8lMz^YH z&-Pr1y=Qxo>?!>UcRp{EW{IQKAo5|R=7#`+p2o&dx#Tahoza3`~ zr9K`?(@;_OiX@@`3cenz(L&a0z?QI{fc(1-HGoY8BIoJk*2Fg+?P%s^CZa}=TL+&A z^d&ANMBRv6lLAvQE%f&y^GYld^2WAZNn>N@WGIHjWo<&u)1kxm%P`%1?r~~t85Pb5 zg|N#V%G!{x+CCs-$M=0m=v4Llh(BIp1d7iOCc`_3a~1UY!AbI-@%UZm2VVuw$1DNp zl`RDeYuB|f+~EO1BcW0%1RlvKNL+mUd;OK%&w$O=Gf+Lj$PRi7x$gc%=jYe6x@{n@ zfh>IsS|de`kZbMf)%NmlDP%ZXfNId_2NqE(Pl{hiHT|ut?`r5sM@3@<1;MSm+44hR zeV2VK?}$cu=1;^apS?j)bast6Q#f`9!|g7jSIr_VOngMLo%6Pct>`d{OTPs7KYbz` zte2Wql2aIabE#kg*c9PRQy5R)lzu{A+y&b|72fcW7A-yq@)d5f>wk)qMVg}b6Ch|i zSc!sRm&B?(htO^&D-XjDOFZQcygtf|BIA{w>%^s!u6L*eW8Q9^^l7oD`JG7+H}Q@iVKp3-v?Bl2}97ol#T2xM2rZwr#E#xMTx3Eui0<;{c$bA zi_`kftBZ)rlkf|P;oq@NZEQT>SdKBezB_D#V?-q$y}U~9uA`Nc-ew=^?YccIoz z=`GtjN$tg3@7cgwZj5B)RBUj`*C*EPWeq;*ZUQtZKE~M2MZ+<=Lr_1ZGYUEhy}r>#C`3n zg<3m7sNHngkOh{A0j~7w_~`Q`_Lp&g9fA5;+}DrW@u}Tai=wGMP$9t+JxV%0Y$18p1%no?|3%8L2!a*|2BlBUAJ^Sw&T$TP;_Wa7 zq!Ptk;q)s?3JM1~e(1PpA?zKwITrd#e45?347+AoMu8eChbtX#^*DfcnHa(&!M^eURAhtM?LmY$ zQ?BnRiHm{VXk3=csr$bXcg|ixaQ87dMRSrs%A1!Qfp+I<$xWuEAO=;+vX*K|*qy(C zPObRa7;7&Xp7uD50h*-z-Zo{Y;MR2z;xr50nV4vP??*&+|Fn#j+&8ZsiobqSocsh| zG{qBqYtH2Rv)}CDy%oH#5m`_QDzvt_U!T`C6dg9#Bx?Cv|3ROxG2I|*W2RAV7BUPn zZw*nGcN?7&HtO{k4Nsz4NchTzb~T2jY(qd8hH4n1HQKbHQD#|QJtZ7r(Gj?%>8Fo)H4wLDT#&TSxd9KcQN!DYHvLpM&6EIdicc<`+}^ zQbmjO?lwow{WHPx^!M%<-xtkFV_NvvH_sI>rR+9rwOkJNy2LJGQUPrt=m2&d-Xa!$}g6 zCkQ^fg6oO{dRHI7V`#AWEGKRf)2E}lQ4016#yn>^^NMi-?X#Z?$;=`)j3e}KSo=O> zf{r9-IrIycxldoeU%;6Ua%dY%d3Q5y+jUPq8p_a5#N*{v5^)7QXc0crioPdSdxo$Z zaM5r3IU4m9nCuYTr`o;(VpTEDhg6Ikd;orUupJR{d_L`%2j+!v3zyfk-La*YoM|}Z zh&B^}=7XFY>J3}jnW1754xb&MehF$xhbt459qGlQ5Gk@G#)eH$i8fCYt+x z25#G~85-9yduEIY>>}98%^_1QqTZn1Tln8zaPM_}c9P4RL3y|n4~4Au7L}ohiaHEK z#g!ifCsy)Q++qVBu=xOExEN{H1oz64g|eGN)r5 zz2l&HPod0;zIhqN;opm>47z=;Wy}X%_wxS<4*&)6wS)PtBD{@{vGoE?O-*8Se*=p%`5}2nD_9wR49L-NM(G(`# zG>UB|OF-~Q`YR#T46zosT!H<$?MJoR|n zo2W2sNL52;4t=$!5r)&YUHNd#M`Oo*{K@zUpH7@KS)Y@%ZaJ+!iD-*u z0D{2q$&77EA)LAj@+1*mRR)TzB``f}K<>EY!2wdDS3IxPG78J6VMpo5px820XxvG^ zSp0_<3AxDUu;yooV&|kc9|YSb{1o%^8fpj1)hnT}%l^9K>kqE}9IvSDY#+J0o8g>I zCEQ~$I?grN@DN1U>j%G*a=Q7m2skP}5QrLt$1cx4I_wKvg#FGrax$R0tGmXT`>h0@ zZ_yaoMl|!{IZ%Ykv2Hxat*{9HgH^Xp=-i>HlEKep2ifNMmg8IDl3R8Ub5tK=$zl;% z#BPBbHrxX%#mSkg?1c7SXFw=S{m0Kz6B?`I?J!e+%H!tC=!ZG&wwz`i+7VPmL& zj>{docO#&q_`AC}88`_WIH&gPWz6ZjK}OS7uC~@VY}c59yXtwN|dF*Frg0?nl8RxGJASl7DvAuD)1y;O14gWXjBN-qmS2J< z!k0YADw5i|`u4`Kwy7bvxBof|8esBlnya&~UdIBq(G4`ZpuUjRL>|zEnTUPV?h+%3 z;mXWb76Ws^^krw;WYfFu(AQ(g(51>dA20p<(G9pUmHEj7Zc{}JD&_1SV~E&T7N07} zUrQZ#hQR`S_G@)#Jv?d~`$57kaAEnNP>^IShF72G!d0ioT}pnuz}$1p8txCL{y_h9 zwNmpkoZ!e}I;$0VSrZ5rdva0~!^MzUi|K-4VI6~r;=w+!Iq$6E*NwYC7o9|rMEG$j zPH1hJ4G}atB{D9n$r223tW$v`(o+Yboj+Agr?hX}C0WJV-+mcwP&AhYebNX)5J zg%Z@Z%ENlxu(l=})`OIfc1B-+a-Mu}6GL*DeR{?^35~Tv*q1+dIZwi-qN9HWSwfUK zY2fh}2J^Z=9w?lZWn`(t5zz;&BfpZw%nQdb83m$DXVr5=8$c(|$u3F@ov&H82{z|} z-38NR(^aETO5#qzu$0tH{Qtt)>blwt+~D(n;pMm0)#G&yXKVj8%;kXwPF9*pZ%BMJ zc8l5gx{WWXx9Kz}59_y9pQ6@%7$oz{>JC%Bl**gJkms?gLQ%3Q88rBZ=bIsQGfcPI zFGl+eF)=~xsO2`Rt)dgrh2#;m3Z~2j6|MN1gw5m0N2bS!qSzOnP!8>1N?H2yVwheX z%Hcl4E^prKi6>zM=OeZ57iSVp7V&XkRz$!po@^3xMWQlGBD|>rb}?!V>^W}Z84zcQ z>gIaF7tDBhCA`E|f}lhDH0B5PHAD1DkgtY!h#e|hO)VFTmeJyposjYSE^xg}5wXWd zA0t>%BDvpb(BKtl+$4TTGPo~%q(ofSgFZU%>ZC2aXzn@rput;IksB~Rxs~fZvG|~Q zMW1X38XZqD!Y>7-e5&`1Gb&yi+qi{uwfu}OE zsvVkgDneq!M{y0S~;NIO=!sr_rojGeOGS7At! zgXfH`Olb;~lH7H$ZLaqc*!Bb&PrW#y#`QFLB)LrR0xFh{2gl8$s`}rY@MI5k4>Vsr zqG~8&)MTVXvNu8a?3&Ca5s=4q$CdFg1{eRJWd@^6$OFq9*QwaTKl>Sr^FdH$dwA}0 z#+rh2qa_DqX9}Q&-}jL}txg1W?Diopfh7I_EO!pH`}UY0Tt_pkP*{r^&wcaBruouT zyuMlt7~aO8YQ4V*9&7{VQBPs9nRtxLIEAa$QH8I$ls{~WjvnX&=*J*-36ZWQ%PG&t z=prXYgNliM+_^Rsl&E%ak_s}lIyJITxs7>_u7hE%a`AlNIof94r!oem9Vmft+XK~M8J*EMzb3wao5=$;LP2L4t(CVYb)s9hDVb|EJz07i4 zCOGrh0hirz!ZoE}o4jr{>@`332d-%EA7H1}b*e@wU{D0&U7Rtx+EB|_rXjjEP5ZeT z_)m+X^{9g;XpF99GNvhlH?g%^-cU+~_J&I+G(c^?lQO+$zep(#V}1M6PiMGO$F10d zZfUbCxyKaQ_Bs6=ME+*a$FM=BWI)74$ZZ$+MS{z6d2`uyTC}Ot{Y*fI@o3b>sO9uQ zzzZ+ybSjgoQm%&0B%h*7BH$&z9GWEeCSMh~hH#M%2!vBjH4zKc6Qmb9Kc=vzy*hY5 zglh^@SF}{!7Xc>i&!XM=K30UHFx@WOZIKI$LD zx`##P+{k&7mE$f+Ck(=7v5O_v;t3}e;wF2qKxSPVCz+ZSjt>#+Y^yBkl%b`#9{e|Z zLoKXk%}=gtG%Z+n7|*G8!!eR?h^$A`OTN{z{B6^yPMcQK@;f2WYb{2MuIo!*3_pKjJVoYQ?B2hL%$!EbOK_AXN*aOA$ z)EVV6HZ=@B#^;qpbg>vVsBof}_Y={@lT0R8f^E}4FSvja;fp6dXjA?RH|3&(1HDUT zK4|#ZJsr-$)1g1Q;_l9TH!1T1CQ`=TJ_Yi07`u0d6P-9Ao@0wJz|Z|IVj8ouI~7lA z=C>QB-}z&&?2zM>R7u2d1AI&uVnBEbPO~Iozz&n1Df}|PP$2YgC9t=pl^~c?dS~=r zWyjq$O(a<~d-0FdRj|#pr_UCcw#x2-?BN+;=%Z3LVWf!OpemkZi`bfxf0)j5dmvj* z%W|=a!k$<|*k(2@nqSB&JX9tpDlUQb4+8)8M}cM;H~RC ztTkGn|81)}t?>_xfO}{rnGwOGMK&UX8WchAl&LSL-Yu;y&6xk$fx^f~*CW5vhc>$K zyN$rxO6!i0VTIT#j8l1}Rf5PG?IxCj{~5?}+l=P7DrMqFC;uUm)_V|~q5ypgRU2E~ zybjFDF{JewyYdN3#(Q+bo&6FtTh4+23+dj$Xz!~oXx)1M9*FC7RY}?vOt#Zh|F-vy zxsQSYgS4J(W|3V6OH5Z0>sT?uCyL8()6*km_@&twWs(EUCHOK+ga;{b&d-NaCox7R zzowOHTPUm>x$<{)<}vs*b1_&xFehEtic~omly&GZ)$TGFQ6{n`JfD9GETfvUtIbtP ze+8mrTv6?i@BcQm=>k+u@j~M*mH9c4ED7ja8VFT$QTx5}XP;2#c0sSpu)NlAVqXviIDCfW2yF2w)ysFO7|yPBM^JgUf)m?`s@ zL(Ruw&E5+m!Sl9obg@iZz?>PUE3>cj7a`!qQ0V<(^5MRmnx!;u&HWU9yTXvtU*;zgH_>NX_TS9uz~=q(Ivw4^V#;scb7c1W%Y&0djJlQV#}sh%r{V2N&_z zZi|j|C3HUJH!*EU5?RD1c0%CwLYDXt1VGu$KWL%efp7YLZrTtN*^0RvxqY@=ujQ}^ zR!e)06+dvmP4+GMBIxzN;qkOP=r;EsRwo@fuCpe+bBeKC1wZ`Gy+mx zG&8%u7cR}%76*$T!!Br-?)I>|^cf`G0lR(cU@LB?FZR79wn&#&L88dI^<~FJmgSZw zFjNk6{R;#OmfoJpmrb!q6iNO5!RQea^TE@6=t%@|DjKpn|BNz#av zmFyfSIoeDRfr?Y3d~Ju_ruJ~8P1;Kcoc+=hEM0y|5oN6hl0pJxuf9geqBh7hDfM1}@`xxW<|y1xv<_rfj|>0W8D@N?#@+bqX@ighhKB)z|ya^l*0E7aogecQLfvfu7}9y^)GHu{gwCM{p1MRsH;>=4~Z1;1?Q zq<+&Fe^cpb_R}Y?z{+c$JM7yfAb5U)O?yUg1;&BMVAb2Z+Y4y{XrG4>I;F#6j z01Tb?q|+#6$WKRDv%3>1xJeyt8E@L@390^qf5&LQN*dz}EtR#cyqM1{BG+Mkn{MvA zzA4rXxQnbC6btKi zV(h<`67AehVQmo7MbI4@zU9`;dqXU(q3N-<9#89{PU^C;k(42p)}|R??>f?3_C?j% zI?S%i?Rv~E;nADDp6#$^&g%;aUCC@XSC3kkta~yTO61+M-%`WQUZ;RgLLUi*53ai% z@upNB6kkOG#Xh168U55|?d+`9r0r=CACeWo&6OaeeMn74fN7t;i{OSy$I~1ITc~sl z$SX^uPjo1|1R|ke+dfBZE!wVkCnv0$(Lw_O{e%X>tXw0dvUJ5o;iAQ-iRdDtGnUQ{rkd4EV6CoY z99H)V%UuCQHLopz1>oDVN9?&Rua+W$OXc++CzT7$hGA9=D}=UxsT9mP=?7U+od z2E7EtJ~_Ztii?MK{%RfID@+QuKn@W%`6#5_CdVww%GH}9txU`lK(@!}v^|lXwm0qN zwqqXQh>r+Ah4gZJNW`s7Kf7#lSay>(Im(iK_S~^8Am52fqe`kNUXrDA;u32New0j$ z&&d<1ZMgKb{RKVMj`PED6hi-A*XMpNzLRjDoVO(A2~OgGCZ;*~v*nVuDZfn5v&D0{ zLm>n{fa!1KEFCg3lM?SUa}C7c6#o|po7KKFx^q+)^+iit+{j{$w(U01o&l2JTWN$W zTw4f%o(#IUy#duLKrHC@|EISC8B5k%nf^zFx@}walaXc3*g60I3Wc+JJdF*89{qnb z6t&Ihj|rxY42$;4uJ_Di5TjZ<^p%tht9(;qYU%! z`NVBGLoS0Lzx-&$F?r_;<`YSqan7gYh1@D8S!zwP$T{}$pm-UvU&NiaT`D49I&S>8 z-TE&*FoaKDBKXV|HiFxFNiu`*71(l@ZC7Az7}|3v&#`9Ln?2ykd1lsma&#1_K>*y2OF%@mjgjyy6zv` zr8b$7HukJH>6R4dRcghF$Oycc*cX>I9rLjNZwqHd1TN5VAwCciCvEYK zqx@np?nV=537)v0e0a2%6B!jsvN^Lm%~9Zm7}z6ergZ}%;VqWYjjY+oVvUw|8V4@@ zcJn?1uB_xY25Uawi72ekER&R!Z-CTSTk==-77uUem?zMpR3 z`dFuiPC`cmTwnKEjp$$P$ny^Mw2Ja6b=#I$N)<)%5!9W|>y?TB$PW+e83I?idt;-f zZ<29=mLU)j2!#V!dWHEOzLR+|A+l%AMc*RoHY<`H-wmle8IU86hl}=S_*9H)j-Yep z?LuH3+l*evE;i#HbFnS({`MXoB)3-~Y?wHp8GZOTxZBn^ITnh~_LAR+11JV?L%Ygc zc1R-1_QX2}SMn9Rjz|CYBa%*cX~jd%T_P)yj^QdGg6lMQG-~H`pn%k<3cZ| z%pbgtLa}0*sSlb#_ucXJ6~c);skUzDF7`San8Q@2n|TG4avnX&SD$3MS-Gy~+?tfN zEd%;Kb%@Hf8gQQD%n!gxygO3t9lk>xMNdG4x}}}R_r=SFwIEIyi}+vZ9lpw>=wto! zOixpyxk-$XHxK?H))w!3u8b4sSHcsQH)A;Q+%zGy`+J0eeh&)>E%vCk=&DDk)0e$P zjHvz9j91sq(M=iMVO0wg?ZN`Ejlu1WW`4i__QpW4soHh6(GNNcvXQ0utz^CRxuIm; ziCl!i8|s$v97?sFLM)f0VRi#+rnp6!dE%G;r{LQ+bky{-N@*T5uw8uL)dLFns5?;9 zJ3*4pbnA44P{Fiq>r2leVI!<_hZ9O7d1HS8&ZYuE6?Qy$JFFd>VU3fXTy8zkQ+{iH zOG#l86peolR497+w>dU6{q_XccEh$ z!n!aXv!j}ne4R8efSy%MZMqLTe`KPmT`n)#6XucslytcBr%VwccgcsBjF}&cp#~-A zmcV1@uy2?+2eVJr;$dN{^S^ricc6oNs4K*2NqIwXij7cn`G58#r;sAbeCxDW$4 zGmjJ7b;`5i3E!2H^#fN*zm5CCYa>Lr-{u3B2bn)7tpMjPi9OJi_AX_8neZ2Tua_o% zZBbw5r2oO3M#I3MeZbaCeP^YgD4KryLU@V5=Z1Af4Td{^5AN67p*D2Q)~H#aGXr!I zx_sXdSJ%taVslZ`>D_FP)U5d4mhuODfr4LTL@Jbksu<7+?d|{S4TB!R*thF^p(dgG zrFE$Wzf1cYg=ZrwHeIEfUg|$OBWp`qqxS{1=5WSJzboAUsF1NHbS^GhIKQQscD8U) zZ+JpA+7VNaBi@pHCx$`BspAs8r+Q4&AYhyp7a3t74OyFY1bBklBj6!Qb%5gcQpNu= zA*!dYtFOQca{jYB6JOIW#&t>?wBEIh8Drh;MH!^XRV$aFmjOXwu4quiQRmDBbNW%QE? zxfIM$3weTi8UZmoVQ;(RJK<)hm3hFt`66;BHXQy6_JyHaL6Uj&59z-X$qt+4CKkF) z3oYmLTZwLGq38pV`V8WNAok}wqc%**cf$i&v4z>CY0}7WSZ3!hI!)&cj59Y&NAr}0 zbRfjY7NjZ_YJ_rS&cJ=Ro^77Gtsk@Jyl4O#{xYKNWo0c_IdmsCCQ7Bkkw;W zpU%&M@C6qiz?n19Ve)cXm;F4Odykn;o{Tro0C5B$Y$mm%_o|5*Ltp&r?V@1}6WSBj zNH_a{xI+<$X2$DH4s{c#4i5%x?)B?DT)fhJ8OMpXfIE$x4G_Mx;cI--_bI7cxz{;H}H9bg0PtxQcrezN$ zua%N&x|F&P5sKT8LqMV5HKD(?OFF&KWDkbUEczFfHOz0o~=f?-;pX3zx z$@&i#xrmn!+M0`eKO}W<(#D#_z36;FEWcjyA55$aUqHX*xY4e?Cd`Q1V7x)b(!8K{ zO{}T=UX=_-YI;jkf>Oxff#@Zu$^oNtHuB$FR8ON>hZ)9*W;(=`OG!5tFX^{6;(i1 zsgmr1R$&Bogw{YU?S-yU7JTJPI5an9x|9#VW$5>3@SHrK^kJdv4IVw|x^G~2HC6E` z3syAsZ;oi3$_jGZy!#BEZJq0y`GMr;Q0isx?t~xa%vX(GJ@j!qf>#n3=zN$j$@an^ z_bIMxc7iW0<-2_I4EEw0-gE~@KYCfCQte&zmu1>PutSY>EImv4Xk1=4Y;8R>rkU3? zJ5OeXM5Ww3`TUSQO^8Hrki21uhQp~h;X*m&_t;bfPG~@!@9Pvi?-SC^D(_({T@`hmwVd!H-5Q@J&$url=tEH%~s@@6OkjHhlV^ zWK!CtJ0f?cz{D;Rb>(3^;**w`w8(0+7wrM4GbRWI%1SnxF9!Q0*_ACol*?wR^F}1* z!<#vbnGi(f$d@)3Z0t{p){6g}ut_@0175(LTM%gA_E3JpquO?BAca>63(3d`5;RMeLgvgqXQWX zb9jnN2ZwOjBEo1BWzoL-gY_YBwjPwrTiz4$IAGoiA5=3g-7zo@)2u!HP_7u~J zpkNhyC8lj(bIoNN$$bcv(dp)*MdQrreRk)lK_;Pc@oC9EyEx)Unfr(fq?NM<6v#I1 zRR<5KsXVwOAH!cHU__10kguLe zHSZNrLsKMl0xdd6p2MKQo7oqlXB10cWd z4zhiw=j97cty)bm!JF&B;wCnQEk$MM`s$S({r#z$C0r45?iqhV!ABcAe7zeKR0Hx> z$aLyRDH>4#7tQ0TxIZ4bE<0aulA9v`dMQFc1%kg=ToXYiqE0)3n6%}(@Mlj zn#HX4BBK#E9x;-$=U06g{zaiAL{&L^D?wJj=1Fe#%8!Q;TRvXm0~mHyLHKju$9H`; zW6cHJQLSNm@9-r3os|V=5jII^%<^~=q|Qidd;C-8g!5B&Vi!I@tl|?z@oe$%w60ZX zyDE@#nJy}!nWv6Ky4Vt3MR;t0xwa0Dv!2~g-GdRw#*)!xu79ni)1`N23ihtA2X#3Z z1&O9k(Ovorci%VQv6dS3xb zWM}gPZoi6OB;ViE8QnwFg)Nr`LDE$)+X+GDOUEa0mzu*!br&p2??AGpl?N5P9yZRw zm+mF#@tr8L*)UKV(mi+yNY~gA<>DCdV$FQfo@+hRdNED-cDd|QYp+DGh@#SxFevQQ z6H~XlJ*F1}!LWBH##YY2)dj}wl%)hgc+;e5f~U;!jw@Ue%_%^1u9z0xbw1(xEnxEP zhVH#8=rngApy^G;bqYO^`2Yh#x*Sv(C;f4Q2ACtLRLb1wDHlKYf>N{;dWmwV=@WVlI{%C4ihk1}u^ar2T|`C$m!n}1dGd)31H6Krf%GxQlL#UBzqSvCy4 z&$D!)VgsBQyhtXv&TN6(um(fx?m8#@)&fHlFKlu$9x=7v&a_|n$70J)z|LzP1-fr6^;4< z^pDZCC*uEByKg@Oe!1`1WaEk(D1||q3?I<%g6bCFEz`UHGs;QtoY5%O(-bnqf(B{z zw^V$t*6wB_r9w0HH0rvn_pLrvm4N1djO5-A7@<9~M;3*8ROPKc+mgWI-X~8eGO73a zB*=w37Dx8N8rZ}Hr@_&VGkS09;y!Z7QRnJ1Y1ZnZqXwE?n7QUJOOg*me`+0#sEn$C z$*2eZu57ZpoK$in9fZRHqo;$S%IZhQDyv*)#TC2CriYe8U#aqeJDj@$ zmeoC!|LEv9ByTDf;}5-oqt4UD3-A2Vaq(JU++7tRu0QgxFrb*L;^a%XLkn#^vMipn zqtB0y=SF$_q1cH!?cX5gRLpx}c=9p6~uRO=&5HQe=`=_3-J zxS77DiG$w^c|p66r_tvGO%RKZf%kMwKbm{3pK zHxJ=7?%%7E>bZN>zGw$*=#7->8LV!iEC^EZ6*%9XeM(Lw;S|IVFZtMN<4qYNhGk#ULNs5-H(c!3wGP0p-;-d6F84ABhwcG=D@5?!S)tUTEV-p}|#z!wxOUywKfo6dCG)sewmZBfwB=IFeWY#jPXc4)~J!y)j{=>!J z@B4uB0=L8RChJiLv__J_MYY3|DiMx8sK{9b^kFkm&F66U<~l7ht+u(pZ+)r{ThoI~ zf}g$L{D6vEWa_X70ci4uk^9zOUD@h>dON%s>~kJ=0f*&x{yBE?5sNZv_#wc~*j5pd zCxtDV&>J?u?4^HS?EkT*RRm!lKK7_LgpdO2hRNop&FIAO>C?#M{s&YSP-76FVicQt zM3VfWhsHD9Vdbq1uqvM9P}a8pL0G>6`JO*#D6mXEV&ws!W8{q85LfBt=_S=O!4OTG z-;CrNA!`|m>X&%eVCvPo$hR+xIwg#(jrEqj2CNf|ncM3fMGWf98c8&CLg6nNi|V5J z`3-NYbv|D&p*I=^M#0dTOJ_@<_tl!@+dowQ8~O93&LE?Zs16bS09nI!E4u>nOgKrR zU=#J6=2N=w>$46S80&Tgc4p});j7uX$!K)f}dw&Rw3ZOxKM1^*VvCn)$sT1S*zO0|RH zw^6%YBj!|4mom0jHH6g3aB9+p;>mc{X?sN8WXRwE>TitpNzFnCciV&sp&}V0| z!0wwAnP(wnk7ybEM(}q%-gGIwU|<&6z7&{tS?!&BZ<&=WIrc+~7igNoJP5;~FJZpq zm&xzIEbRz=8g|$J}r{=kSv0^z@5M!NqRJiL>^>tBOvS47B`SB9%M*Wt+Zp0s=;9!>{pZJ_t zJ#io;^+bX?X-FQLQBV2ho8Y{HyBQ7+nPLIC zE|^8&=pvl8n_8{43G08BYBJnm*>(`$vCbZW@ukkf{>4ad zd#3A2)m?75xIkh`ueI5;47iP=a>=uwaBFzS%zTsB7GvRfC<3^)V?7~?6p2{8PP{m{ zBZ2{L=E7_!I8zRC^K&wG?wi$kSa2wvy^?g4ZS#WxcxuRQdGTBNp-5DTD9k~SE0ew6 zg<;7T;<@X{NcQx%SLbmU6Yv5>O6K)ML~QSY2M}7Cu3QbT2*U6cx5ldKogqWcrfrQ8z7k*qBQ^^@>W%{ z{+ZqI8Ql0=>on$#Df9k8jrIH)bVZqQA*Pp8E5`I68X?XaBeQDD=twDpiBDp4xh8CN z$T5pn=C}EkQr^$lHt+}4cl@`8_ki`O1|+|6lI}p(rTAQ}@QE8iwI2917#U5UvGAn_ z@^7S)Fs5i)2+3z-`KBQ>T4Cea6f$zWnOzZ)VM&Gk4y%^S}H4haZ3X`IldR`~8nU|N48u zKmRU7UlI@S1TXN$W2+DNA_^%&nLr>0eO!WKFBlNn50;@Z&vHb84u=(hVeC-16bVrf zjSiWy5C^Lv9ugoCRmp47R4EzO895|2${ny*Z-Ya^U#eSKPEaaDaXC!g{MX4YcM*2* zeyi0@X5U*4H`_MBv@Fay>N=#%kom&$vHmj>`^d@jk-^(xRi7(Et~XLR%O7LY_^lc}Vz_QS4a{%cf(cuG#&XD-QozBqLY4hMvWZCDCePLU>nZi`~O!*T>eB_66S=48Aad)0DZ5 zVC|ac=Ob&1<^56K=OhSy%&h6X4(0H9P}F`vP514}I=qMl#uuKpPW^m{UGZ;_?yfvWHaJDdX{5m5LSW0x^URyVk0C6((Bvbay>;hnbqxss~1 zSo`?{jIq-Dd5;@SU!BCCK>ZS5P!II%nqe1OtDPz3-!TWsQbN48!gThYXq8>KH_#Pt zKr>0a4JjSe;>I9981Tw#Y&kW`tg*s^S*$_BPfaw@`eN??TZ(aE+lZVx39R+>`w7x& zWz7-qub!_yiZuxp^Y4E%+9qhsLRT5qU%)LSkamDFEK-!0vUg|}Xjce&j+ z#&8DQbn2AAMl03r;On;MMpuXb+{$}3@UGTr2Kb_yRG&NQD2-x_w-$OgPBBor9%nR$ zz@OAoj_jvY`4pc=#XQFw*27blm8#WB(&GIvwM*I1M=p7?%#JN3DTjdBk1CL)zGq-t6<$}ylSu{6n;GK02KEgdL0&zBOV$Lh zNQ34&k*a|~POnel0(EOCEbd6W$|16y)W6rpmJ(l@JxL=dS!~M)GutDFaWfD!)}Hk& zhJ2PBIs`+8cFUMzcI+2B;&|dVe&U7i+NBlY%e+(jf zV7om@LhX*U{5wCI+??BY$g>@g=eoupg7Ipb>>G$N50BfOp%q@{dDBV=IoW(ek0TB~ zJAj<}4%J^LWm~^ugs_O6ORfT<3>RatT~zX^21>DSs6g66?djLo-USVwyDaRm`vfGp zSX_7t;b!yWgTZENpCbeNN_ zd!O2TK-~syA*HG${7aHm1%1thV6{)Uz4ix(a0jPd{Zv#0$t^YPoHY0hImB$UTxBJu zGtF4~tauM|<0$OGo0PjIGXqLtuZ&(x6whQ8Osd4h-#Mk3_;&ZlXsotlRK`6#u%}sw z65Q^!T?M(eL4*W}6FWT)N8h5WBl7K|uK-y)vtr&OGT@SfP{@2AJYNkM#CdQ?XH$V- zB0FYYDfj7Qo~}qhP=?4b$TK~GaLehR2|N&HZ(i&HfpezqdC#}r7Kp+h?mVFhPzY@*Kv3&XL|LEu34z#0?&TQuTls= z6}8TYnoGFvtR1sCjQ6utap2Rn^v}}mAMQB|`3`7OVa=~>RoL~%pysh?uJNH`G2<5w zA;Zu7!Y(GBmZ@B6r)t2|jOwsTe5hz`n|qPa#pNd($$mzXDJ=TMx!ps{5ots zf?+`U?XtqJBwL0Y=*cRJq!DeU+1nvnmE~l%>NKLAUly^5dD9ZjUZlr+6-0rD^Me=Q zZ@UHq8?9#wzZyMZQUSf4?BrI!S>{crsS~0Gm=!tpTf2}%OsPVQM8d~qz=~{e`jRhb z6Xys2O22gUah>Duh~5A(a(75ShG4Uo8_Met@b3Z1`Kb?PFOoc_BZNa^q%qrWqZ?7f3uxV1`L0iIT{f!=7+4OC zk~GU=t$N~pAKukh!#adeKk%~Ca#1|RG7H- zyA72}?#fy4O@#Bl7}$h@7sP~4W_m40y{K*8Fxt;-HT6Vdz6?wbI*9`DSDHw2(vOj>NxWT$){iQUrp{f^QuIAMoCvu!PHUd9S9Y3`LqzS=P&?bc7z zP*sW+!K}#%T14;ZBPw)h=IU$8-JreaJ}mW+2jG&XNdu3omj_RM7;_prb679)*6paKDM5 zhpN^oT#FU_oWsr%i|DTs%OUj>MjB6XwC_T)ZOX1+rHkn| zR*$=ci5GSV?15plTcmz+YoOwinOX1uVHa=+uDphqE5Yv1-}lb{WVB0?gI6A$!-HE4 zoM$rbLKTavBqGn(J1J|#o8C0g3bs;v(f8IO#tPKX4P_gmX&a(7=9W^g4NcNeBs*Wy zYbA)$w{*QJy=2H@dyJQ9`uSo>6Z)!{d}V6Vp5Ew2GUaZqny_@~kS%zf+w?w_)50wZ zjUa<;N$D3Pk_mG}zU+CV3o@Tr&6HeEHA{-s{D?JH%1sh%Svsf15}QG=U*|Mw$aE| z*nvPZ6M7p4qywEL#{HO)_K&vi zs9}39ZP0~LsPk%?CKp}336-o(`Ckt0H*E*cNQlAHFkbxozzaVxo!0?Rp}w1+PN`Wu zr(2h{;?K%?glMS0|GEyUE5KiD%;l8kH#5G?(J!H&uEp1^Suh&PZz!)u!PAH$YDKVc z-EUAE{~A=NsDEx`RWLwqaD7l`8dxN1w@!_DXkfv3hmD5bf?znwBSy1fm?{lZ7#IP4 z(z}ny+eV?G5vafP)uR~x*7{W5PiuLsB&wH$FDE4x!1VT1<LE;KHNRYu3ZHexzkU7;2$oOr80(Yv6QC z=81;hetahE<+|8Nd{xJ{!1?>Yu+^`W=Ft|)BIk1>?BZ^#>x8zD^8UT9xh)xy3hlO{ zI%;HfK_WT5z)2G53tJY!E7skyYPc+yay$}>_!5ymkYSDClj3_`=(2`#{`CApxPrEl zEn`f-P62h6{~A4V!-X5ptqZl?sB(s2 zq_W(JnBFKOG&0Uk_dDu!kNy>Hg>Z4hP8z4$PrrO0)oOtHkQ7)am_i0+DpT+B1)XC>go z#i?>>(ail2kE(%RO*WefE+9V~Fn`^SU^r*=0T9(N>NBSrej)T5Z#aQL7~b@~*V^XE ztZR-kVkIk|p4Fc&6mx|`W5(oNgVIPS=WmF}a5sdot>%gdZoJ&jeu|+3EZ6@ePrPj%t^Oe%ZJhrp!Iq9xv&OF{GZU@U94} z8kWp5lch<7a0Z29GBdCDt?{KRfLS`#z_&9ef zWV;>55V?)-!NfeNw`a{#d-qPv0e+!^$2bk_?D^mS^7Bt-#YA~^^~p=|sc z33<5_Jfncz5s~0~fPs21=Knw5zB?|8r1|?(Ju@3-6FDq72$*xuIcM)Y^^&I-JfHKT z=bg`UCrOc@h=OE95CjAh5){D*CO}ja6;TX`C@3l-BI;WMEbJ~z@Xp`sA2J=LXQsEh zs=B)BTOg<_!HhQMhpaG-Vj61N-Y~n?#_$O32ySF(I>lR81Y2CN2g&CNc6?949cPlt z2;JMj3gd^MmyhI{c$n(?5Mx`JXHn7EfVJ&F6 zj?Y_thvAicBDg+Uk}iA@2Ik8Xi$FlfqoST_JTy$WycU-4(+L>8u^!i9AiiaaXxpr( zMBuFL$=d{%(2$Jp5MiSGM)-yd#oQTb`nnJPaHdMfou2bzJof>$AlzaiuuA-bPbZT5 zXd?by3o%E+hYLAi6FLNc>?~fTNNeN;(^0SJD=be^2a`C25YxwICRjIbk073zWD^Jf z2}EC;Xf782j`1#2^W~@LyV_Q6;#byK<0#11I(P&Gr9F9H<>0Hcn9@q4q%Q&a9bj*% zORwg#207M3l&edw)+CjZOj40MiamYPUzLbwMYwbQU-g_d>SeLgKS}eye~Dk#8fVf} zuTrN{Py%XDt#7L&Rc%x)s7(tk67h(|&D0~2un5N4bR2e=k{_hlWK6$TrqexS;x>N}Ph4GUpU5Oh z?A+eqffy((vwk^4CD_;8=Uh40{*oON<9?dTf1>Zeza_|8>vwv|07@hk=#aqSMLk?Q& zY}Q3hGJ<7@&$&YEPy5E(M~XK3 zVsY42REQkusA|mbHQiWLNc-?6Z+H&foiVcP)im<52glXKFMBv^2kk!eK zxWRtW#_t~cfd_6*?@P^aY^CH|C`<%4RT9bGPlsjvN1gbNH}R@du?P#O&eCw$@anBIP91W6U2By{kY@Mqvw1+fRf#7p=PD$CL5W;3{$ z1!3Z_W5aikuLUy6%2_Mo%P)mB8E{S@D7W(Xl7eibQkv)6qk2C~AtAbgn5idL*3gDA z+828X3PyfeJA+F;Rnm``I}c0Xkx?&&uaQaQuSvobU)I&5W7#CMv}B1l3?n7|1V?)D zH$!|+K>~A4u$9GB5Vu6KJo6AThMMz`=?#`SU9==|UwDi7k`3DYucFDwe1f=ZN#;-2 z!UZkY{=^?5jPg4RUd;A~Li;ivU4>o|jkjJ{iyBMyqNag_$0>iN#vd;DgRLQA@mw{C z`=WM+Va9NTEsfX(m8)-%=!Lg`;L+iD-@k;`ZX|>bY>1FzlqjPe+W){kJ3d?&rLG>W zm~s?Lxmlz8$5*Y%_@;VI!d|6LMe=JXw5ANG*DEQK&6mwO0-K{Gt&dUi42n&orp`~a zx|joHtbV{B29KtRi^tqdgtt>i9AGBglicj>QVvr@xR5u`a_;i{4s<_)`HI=#G50^U zknKUCD{5HF?>@uG8XMcq7huQmOOoUo^k)}yeoHd98YmuRX&0mYcn27W>y02O`Yv-r z^z3&}zqt(v3gKsng?0~KfB76W{B?g7+t`u?{jAT}6wSEsAItjNZDmJ4MMp9x@#?B9 zcqSEJd=3x3TJ!K5lVn$78^zkO_vdZ0!OmCCgRHoQ^Gmy8{%Z&#vUobf2-1JTlVGrW z^Jms2ESwsdPaR;o-|`0A1R!~n|I6Mz2L?L0wk^a3yiONG!62;1A-PTj`u{a8MWX@1 z^yx~d+yjwnHp4RmzkkRh)7TeLsIj;io7`SxTVUDrC~=sXEAQaKLoz$PdVuk5 zgDa*T;b68EtKKM+zCkJ|HwfZ(FaI7lUP1ItwA$8%3eC1z&_KhjRH>7&#&R<;w|mu0 z?e}^u68+UE)aAp1zhG;n>;_FP1vMZ?cf)5IrMeO)(V&R5a(Y_t8?WL>tTK!Sy`&>2 zBICwS_=m$N-KKPj+?#W~*0}v`Gw*;JiL^-wZ8f$+5${*6kzU@fvWcdeX@|RFeOhxK zQxJ?|@hJaQnVh!1zU{X6n}k+-zu!bO^=_IG^&KYbt0Y0zQ}VSG>rB0nRhe5?&N*a5 ztoKvwDp{skPPb1;=WP28xSipW;r*RyW0TwEdAl~*>j9Da2`d>A?Y&Jj5`iT!Xip6>nn5ns7j@+ZyD%mZH zu$Y2xAB(+wR_rXqm}}g+#T!mcmxykeimuDIQ{Q|4_}Cv4)QhV0x?iV7Rb)S3yiZ&b z3kN%q{^Sy|45O|w#O_|A2a7Mm5t8dhEMjUmu#m1@Oy3?@n- z^#AzA!AhV|A7GHCA(}3PTnc4&UGwJUHIdg4_613GB{%yJx|mexk+I`ENYM}UsxT=T zhkKd~2_A~3P`$&X=8`AB^UHpSI*J+M1eqwuyQK)~i_Zj@*OLIN?^o&5b6REY^ zLZxvLCx_iE9xlDx#gGpD0tq)Avk8BC8w7XBm9B?nWGo2;2Vt=x`65m%l&t$MPQp*p z>EQzP=@1Y}`Rj$|&zYwpZL4y^yNmw%hDfH45VPU1|PkcNVc&Ekxl>2rH9+r%;b!!yHRNUlD z)>ZalJe4?EtNNQuW$I;xfk9yfP?okTeN_toL`-WKKtomQ2S(i+dUHSs8s&01$jgAe zuPks{zg7`NjfJ+QPGf7cIBAjy?{%RhKAJC8g=;&d28;KHbJ*f0NqssxG!-po1n1_x z=0a2F=1FO~YG*#wnj5D_lfxG08WhO8VMMz8{aBAnZTQ}Miez@jCFS+{rwvFm)% zpwtmWyj0@%QRc5DosK}n*C)G|v*I0t*2#G6eYs95o*1F)Q}|LZ@cZZ)WRtirFHUC9 zyTD#iq7!hyI#0D$i1vy^u03y&v){<><-tH+h{%rJ(NFZ?C2U*8h(+Oc2iF6$k=S7@ z&+S>HXq-6Z5C3767s2A~Gbo;PJ`%VT=5=y?N8Vrkc1yZ@_7lYu&Tw17u;&~hL;lc` z%&HvAu3C{uZTP0x6@9e`2k%q=oJ5B`s|vy0;mxvC`T5HTMJ~nX9GX#XE@o?&injg7 ze-9X3yaomt2~g3=-wm;bL@$Q~^N5^fSNT$>c~^Ep0(c@{ilHV>{%nP)bWo&rC+=fp z!3Lev7@a5-1W`KA0JHOAZizXM8GW~Ti(i2EH%pIgLmP2O0G!GK26w^@j|%74O}k}9 zqS(#$WH-Inw0>S>F@`UO-=YqT9=I0updX*Xe(C^bXyPzeTgYm~y6zCO(9}s&T??11 z=1$F5oRn+>76rL$bJhcmcWJED5Lm1fFwD9&OXWHue}y4o1bvY*dTF1OWB|P%-s-uK z!Mm6ODi#Cjg+4YJg0s9*zAl}ob>s-t1;Xb%+HsCRSm*}4K*=85t4Hnv`p8OtEI7`= zr0_1ZcB)ySL0gggJ&Q%0d@tL{P8SqJaNC}@fI9-K(vo{}vm-G|iLz)+h36r?*P8Gcd(~EfY z@IOB&Hav}AvU8f9nW>-ww!)?VKwCCYi?b!RpwCvl?%Ue|w?iFmG`G>z($PX?v#q3A zj-N1b(&Q;qr%j(R6LUJN52;>m&=WM*V#olS)l9Z&lsuJUlPI&4DCmXRsIv5UZ!A2e zLdh=X%{@rPBo&t_KI%)o{49;%z;1rjgb`bV;NBlk3Auszf5vYK-(xp86W1?^`Oa&w zSALm#Al$lxOPujQ>m5zIIgxvF7Ve0b}uU;?ZxM zEM=ASIfkQ=RJgeM!K^Tzx4~(#cG&OpFeUJQc&07%k>0Q(TUo(tEsV~S#1=sS5`f2M z*nAD{4Mq$B!5f0jLbM~tvH~ZHA8TZVn%gP+TryVi60Oxhz^Opk|saXri(Z;KR-c4a+eF< zc6s>U5E3cSeMP?(@}0*X!=rw)6iX_)o9{p^`ZoKp7Hs)k;W)l%&C+{6ABYC4w>|xq z?=ab&2$uW4x_l4^%V8oI&(V$)U9v1QAL2X9c2xI$?g~QfF03T|?dQKJfJg3N@O(~5B8YO0HXMaJl*ypimOd*sO*p5! zpXwaF3mE-I20DN|k+OHZ;hEj(?unhs&EHNsSBW{n@XYY%?#IYGdIo!H*8TY@hGM7Z zc$0#s{nxsImzGR(Asjn(*KOjb4H-Zw*i0S!)3ftU%Y8=sjYFo`uXlziSNghpfF-fN z;|`f?NZdsKQg~%7zB28io*jE{;>)L~+;W)$U-J>iNbEzAv_!ZvPjpwGYZ0^Y4*hfv z{AnUJ-e+3i^* zK5e5Dh|Kbm*pXqAcR|KiCv}{l)DKE`K&JfZsH~q)N%T3!Dci!crPA;?rID)|Sr?kV zaEFo*$lC+TJxn(9VurtDaGFp-y=r!C@ZN{8uGgVaTc-q&Gd%-vzKon9f@H&G)XOjI zw_3WcaSPwoNc3OUMaFK?WKVw!a`Y6g(P&R^p_8_km_v~h%wFT^q&oLfFRSeQ+iX;1 zr)inEx82*+JA8<&3K|!^N~H!4pt9&&S;>lOl||gLpjgZef14c8TDwqlH4<`4q-Ixf z1#z^;1Ut?O>t{v7Y^eHE;oK-bUE|-DQ=qX%bzV9EYuRFE@X)o%V`@Jo-$SwS)R}>u zl4IfBf^6M~hI;#KgNGFAV6wwg2{7`Z{?1-W<{NM)9l*D????LYvYt=?X+t)zj3l{{ z(6<~8!_BS~oTpj$AN337P1#Tlnby(^GK8&dFm87dy%YN|SIo)=Jp(eW^QKqeO?2uI zhQqi{r(74evw5V7bwm?klumUDSmaTAcLB*@*wYeXKi~fvY<~=KC5TRIylxUQiKLYX zBGUP=b=DBkrC9Ei*VFoSGREw4w{OH~%U#wy(Jmdav)x%)-+b%Ia~|sNe~7Hl`_tIF z@T35kzLgWt(%a}4_`Jikktn>IsB$9Fn{(&0preT2d-hv#1NPfFK_=Yem&|*|BMVAq z)b{pcL|5{Gc_dx&!bFUl`lu6th$jt$Ut5QX#YntXykJ7-$1*24zUbabDZdIMKZMCI{c=2TGU7Ah>No(pN{p;Dz%Xt{ZRujY0- zTeL{2g}JesT$PWJsk_!7W+f#~X`4+Sh8yl`oxU zB843U`jIyOqSb#MRDcid^Ock!;M$^c>Mp-VCyCtlgxuwH+85{}?eww0zX+=RAytH) zN2k|t;~@-w3iXQ4qHH7~wuxw^C=n_82%J8ieUWeaR*T&CfE3vByYSLsV4_(xJk--< zzW!Ebb~%ZfPggyH>M8g=W&xvz93Z$kyGM3Wx=Wt+mmU7&q7?;QS9+DDHUdu;z3elV zO_F@L@THD1>9%{R2(AB%f+p6lsg6z86wej|@+e>e$^%(D4yOzKx)b-}?fr>-vDn(E08R zl^+H3czRpDnIEKOKkR(Z@B+ki3>p!M`90%4opSRmW)giUo*yg~B^c|M{`Cee49*iz z9RUC3eX^!Kc7mDq!yZ2ZgN?miyJnC`C>(Wrm7~EH+v^UFBZ@))lEwWH#I9f}A{AYd(nFuN3f&0%*qg@z`G&<@BpFE=e4$?X>nux#K0Ld2H4nf|R`l7Q>FmxD! z!7xr^Smod|q!3)Y)%DE(7_n`tv^Jwso=h#MU6W6BPx6Cg+v_IQ70%>=&gzG&qG9c% zS07*W2OiOTgeg(EPP;g8%2|%g%|&v(YlGf}ApVYC@e#1NPWMgA1=-C-*4qLJ|oibo9{58z2^r~N0&G+@pT6#;xCLSXL9)`PJwMMKVtfhkAe>Y zSJelNXpW?;3@`}I2XxP#gZl-Dgyrf4C*T&0?f)(qzSO1tX5@YpYx<;jG#;p!Om%jG zOW>?S=NWmk8J}jSBOAwh-E?WjD+m=LURI^;j*pm{Y08fbToh~SW61Og#D~xodi-FW z+rz>)!iLW?4UrnXgr2O#hwy@!T#1e3%vLCyIqFb6_-HkL&Gnwb5L%cQ1ZVp`CV4BW z`uX+f6XOCl$I$6J@NX9}adN`9HNIn9Be>8|(uFKl8r$ z;V*em_hS*}N8&yd9qke8J zFBlXWq}+thZPu9?X2Fh7809+r_%&29^GW}AdJ%BqCd_T_)hAjcl!OI?b|m$(6VDN@ z9R=Nn)(m7Zb`W1#;*Kl{RM9YZd9E!>J^gv zsF?3`xNB%A+-dGDOw+}e5iIf+`b9$gf4Tef(NufMINzSl<@CQ^gGG} zln#@4{MO^k8ywtQxZ`Pliso7lDyT(Ei;5OlL+U-ajJq07^q+$qw@not6VCs4f*Iwu ziuFKI(70D_0brxPD^`g_Qr`h%S)+;YJUuajkUUCXq*&fJmS(1ba~KNCZ5CTA(RinK z6J=$Y7J2vi2Il7Mr^}Me?~j}CR?g*H(zK{^17U<+<7g-8rqPyeAeq&WV1_5V zgmq@d50<_Z?*9qb_8F@{JhP2~Y?R;ZT3tKR+)oFWyG5VdlM4T`j2pe4;=KU>^AL;a z8|`nwbz(Wue%}c&h@R_XW^#|(&kN=ov51&n><9$a@{C;&FnK){eGU8rF|QIRj2%(8cY)en|gG zyiM{Msnwa&xkh+I*C`4oVy8onR>)|3(HnWJS=u>xspVOYaH8T&c)cB-aUC}E3U%2i zX?zc!-eqqLxUNirGC1<3SYNb&*%ZSN+=xy<>F?b5IMSudh(Hkf~EX z>!#soRlxw%IYvvnK*xhGIy9CdNWJOoRO4M5EA)pXxoIAS`xf5aEez4scDxVtOM#Uei&GyzCTW^t@wAR*Iy}F+o>(Z(k@XvvRmiACgyyvM! z&~vejp`$R~{?p$E`wPfu?iz2Rp9xW8eydC9XoC$H)ba7Wtj>mMm{HI$AMG>U2A}yz z7*3IT6hrN9nT$3sh?*A*JK4+hDQi4|#lb><-&hK^LpCEeL%033b_JXUI!ODD@C!#7 zy#)ihxo0L}xH$LsH=$`hS}gaHk})&u$K;O6KRqeY>Ji3wmhsN;emN?nkJ}R7I`j`( zk5K6t;+@+W3vWj7=o|1kJ9>y|HLlzFef7j=gIg&M|-bq5A=S017x3`rqEF%Pb+2%7B-D{P`L=h^O$~iyc1|=BogPR;H;Mio zxM+sog|CTuvPTg8piE#6$HCDFsYdyKa0)G2tOX~G-tq8DyV#4O#`=;f682-VNcO_g zPOtk_{RV$fd@Q?jK!LD$aP8t5;yJ%M!>}S|bvd^$-Edwoa8IM;?k_&!%*M-D^n_9v zWD+PjW~tpt{qExiU(zuP_)q^#`0ti7DC1>Mt%5*dj_@OC6se(H2su;>9Q^y~$)^nX zHIy!$D^1gh`LxD@loN)W(p^3m(@VtV|c-#mf!m^fP|e#WB%=INbe~Xl$rz^^h^-C2@$g`PCRod&Ci>8 zx5^cl=uPv{gMw5#K0_Vr+4>Z<($ZpfR4dw>mR6htRA!;Rz@izl>HOZ8lKW8XM#}N8 zlU<7@U;gES+2=Jws(RXbk*)HhAuvr|NQ+DLER*!#=?*HKcYZspab)Cy{lpyhNvFg` zPTC|Ekw--9t;MDP3pfZHCNSNvQYPLsPgL&?b<|vEmli^+Mvwu|-4M?|8c_CG8a-vi z0qH<2jkvWf4iuNeMGO}Eala!e9pD-QuifCNpkOE_D0ye0@FVhASoWnj$_O2O-Y`qU zXY3{>nRcPWya`(*742RmJL*a-j8oz;LVN!h{TqaG)UiX1){}VPzn5GHf(!iyp`E)_-<8+BRF6v#a?0+e!RsC1d>C;K5?}@e&qq`zBUGvAc{v#P zP<^T}tHY)}kU(aNH|q`Cz>6DdeBrMHQzv#0)xAzX)LZ>#y4W!Raz3J-P8Rtk|!&eN!u~^H#ZT*wQGz^NVW=*iEtz z<*U;oz^0Xz+E}f+KL2^C-Id*r<4+q5kwsOeX|0D*@?eS$pz0OGgh6|Z#pwbrZ-*y1 z(M#FeQvI7`PZoH&0fulUNo%0MC{^}}SmY>AFhN8^lUt4?gB*PT1H*Ak%>~7}canE5 zFv11>-E#?HL*-s@BNvP&4oLqqY|1@M@N<@g2@n@DheuwstjoMTKdixo7-CVeGwFUT z4$=#OobBF_J%p55=g3|=LKq$2#ghwJNu)B&b@YmV!5g+!K=c42ekYyNwHz{avF2!B z@`Rb3E&DiyTxY(s&(-5#6S8Gw3zP8^nZHx`F`Kq9;Teb`rAzgayNJ^6fWaM8UZl2u z{WG4-3xbRv8lM|DXdC(N5fx0|GG4*mF5bdqBc$LC1jUqRLN;W7^5ib0Yl}S=6w)h1 zNxtAqJUe;d!NXM1HHRx=ondDX%$X+&NJYl;yyqj)Qnox50h829EPmTFa^4|$nkPCQ(G%_1%i}Qwxa(~LF0V3jvlgrVHOOnYWJ0a1 z2BjSL);1mK!zGdIXulT64$2{~Z3iZ_wEg{)hqCLN^>PJZX}!VafKZUn$2_;ab9FzG zCX2hxzT1D2wkwSL*&phdS>Bj*XWxEspH;0txP*R49r8xCzgq>#?xb7CkgvNfOs6i+ z;rA|?thG>bY4maZ8~0)PJ1t)C3_eDjzV3upC;@#{?hZV9f4B2^+*bcco}Z0*{9j0< z{N*(Z`FrLPGzZN-VEffW#*l+ht`rIgkB$^;J6t6_TxIk4?xa#k=)2=Sb|c+t1?I0f zke(1B+Mtj~4SRq5#ZE2B+7v_u=hNFTH$|Ra1B^1sNCr=_u>L`N2?{P~c=~hb_7M2< z_CjIT3z$fh8%3}c6tt?lXfKoq1nUv#kQaz(m%Sa)9rmp@z1M;RXm-S)*9{_Hnm%PL zNM*R1!&W$oYp+{-?F}I?|0ETP zDp@s?NYj=2FW&wS&Gl&Q7j3zTP{VC(#VV#!t$ePedaZuS#GS8RR8m~BDESGBJxYx! zSxgq>7!UCLX=4&FKZpVxijG6sGKY=)4ImC+cFx=Xy)Dn$M<6=n2v_;Hj8_j!#}I6%K9YDr=Ut$ zlmpYV`iD|yEUtI!zP=WUN1pkLxd&Wg_5;)=qpWW{f00FNFRuRnXgA+ykW>OsmmL^1 zjamj|MVR#1?yqvKC1B!cx=W|Xd zzC471Y^cDD))QabX6%QDcH-zR-hU!5*NJx}`z8H&_xSN^Tq@wyBY0rQb-rU~L#~rC zxW6hL_B)f%^NY)US(o19!&Ds}mksID^gF31YVN zr}62(ZY2NpUW4153#QTQ!P~arCS)&TAIhC(Zz_h2!AZg}`nnHD#nnF_>njaD2FC;_ zLq5RaY7>PkE>tPK19aXRkz;)eI#(g!)(fiS+_I#Bz%^98Z!$BLL%$Yo&ZvQ@^B?tc zOC^nSH)*YxhI$QEsxPJ3iZ<;qTfaj?3umcNm}UJa`DTjsru^wNc@lZrU&65f{>9Ei z=&dsj&f0C-p9oIqBy`O$21|@OmhsPs9QjeM;78~UxDw4u`)C=bSk}$pV_X!#&Ll227(IyUIR>?5yYOMjxv3m`L4Dc6WI0w6P}yiUX5@ePvM9m&&Q_M zV=MZd;eDM^YHVQiCS74mPLK$VTwueGjKp1x;EKcWPrQh?=^6NW0yAWZ_7TfP*hsmg1h23m>t!o2*a|2zY0KD zmec%6JbJG#@i7(b*5AukQ85DHyyYPhY!*Bh^Ox`H$&GefZnfWv=cvt>oMSSU2kX`7 zEP8Sb5bQd9b}7xB!CnKWUiER~%d`83ju+GpPh2AH>8x|25`5`B5}I$H5cYn~;w31V0my4hs&l9E?Z z>{IIQq+EU~b!?_>vMAd+kjkRchG*;DHQPE^(q|tN8UYV5_QGSb^zJar+HY+GsTeP) z`glFK{>I#bKx$(dNY=x?uijpU>Yh^aioy)8%f{mperFUZi-ept5=q&DTl|M&AFh7) z=*VO^G5rQ9G>W$LfmhUtUOpJJj^3%MV-9_ECk!l$5gj0OW!?^8BDaAgkV^f+dzz$= zMQIQOKZXd?=;1{J&KXwThv;c<^y9Xp8!~QU?@sa-zv&k@$VL0M-FmpW6SmmAWy4Yc zU-r_+ah46$JKjRd42$p_*!>3YGPx(YYqrHKsQ@lL;uM)3s|#kOj;<0!ch0wZ+nwRQ z>W%(BCY5^I)Amlc#AV6Pz?{df}nc-AUo|iUwR5 zUSno*;0-VZf7tG0EUWHA@I8<$^H~QHZb07wFVdmJF5BAGev8?%#3e-oQV+m7I62{n zwGS-2V|gbXZ1LoDpbtDDCD!L|Vw01p_Io3pSH9GDOiL$Pth#L z+l}=2AjABVLDcS1E3zrgQa_%O$5L!G(*JE+CSd5>uYB-V@SYRY1Ih8Y6WIa9uq+aS zqCvPUQGUm69mZA!Qn}TTFTpq_>e-+a^7bqDOX%dDKhV{Z`_=a`$MkatoR}?5r4DvZ znenIy+$S)h#oX_2W3hLE`(Q?LZ`5U}-fSzkVjy`Q4g#GxGqKdfYee zx6pkcg{1fKofANiAnV^Qkn*xUQU+1OIX0pGsvHu>G5wO!w?r0A1C%*jHol4>kM}Lw zAlgGucLfwTDg!^wGR4CW8HekqQQW;>6m3U%I@hO7zOK%&n|Ux^sk7mhdja*$fyV7X z3*BFXo?lD$lA+Ua@8%gYD-BY3S!GXEo&Mw%WJIHSlQ@}rMazT zZOa-(DtpcWU-LPwVrn`{3hMpZB#l-@x(bab6`IQl6Q1b!~jY@os;9mDFs^5WM02-GMk#MlW7yLkZ*UO|93?!Fhw#QJeF`{ zPQnCdeg(B!RJ>+Qr^;z3225ukm}YH+(r=jVWm~}MGB7b#dlzGnHDlrn=O^9~u`YVg zs=56^xIjODu-jG`U!#3;M!dFlm_Co)UIAAYB-=j5=BEj;ZKdydbYQZ-L@NqUb;GDz zdgQVTd1>He$V(>Y7R4Cp)S2K3afpqWO)-3Amq3>4g-o>Hc>ydwo6I{)wn=J-;i1e{ z+Hve>{jPR7ZKGfALM%d;7j`D%DB6*UAfodVCp=i=2F0|f#s}hY$EA4Oi_yLW$93Hf zBW5KKy9>Nn!bWuYZ?!Qu1ZO_-LJoSa`(rP#yh?pG`7Fu(`iG~3%e4-zME@TDxsRzc zlS*wS+lPwxx{%}@pyzKTX5F^Hx=>g%KVJWtQFta8gr9*hFUXPLc91jowK(NbaKqLY zMpL>=jdtikY9{Z75VbZ(Q*Tr$s}#5Z?G_d47Ez5IT2!{EXdH&i+^X2=$58SpijAPE z>fn9~Vt&%Q+1;H>VIwc8ui!zSH-C_)^IU(~&d(hbXF>0UGHw2%5M8&mARS4cpUHnI zU1@4_z7lLUQ6~R;BNisHU)N_Qe<$lN+-tr$9J@{RM+mg0K~kii!@MoRW=YLySL0n$ z9=AvY(FUb!P~J;7f-}E_vmx9>_yRs%DTo%6`#MJur+3^|9rE=-{c$d^$t-6Z9FXin zOJdO(ILGW@PG)k@Y!6_+H3FE?Z_rh%kUD_}G75sLv=@4a=(AqHuZ21XE^$=PeI~h> z1nVPdsK9w@vP|G9$bjW^{Y7x##eB(a@fW zL9jf2TrNTMwiJv6eV}z_lE6HIder@g=33b+OKFP@D_a=npCS< zMCMgca0QV+By1^}dT}w%UMyJ6!~9>y7p!lJN+Fgncu)jyLQp9)-^2wb22sZ+@aUai zC3ZSxSq~*pi?DfjiQ_@IqbHz?D1KNuFg-nolltCC_dsx2<|p7uPE-8Dau6h# zhVsbJ^OffRloI^9oQN0AMJd2i>hrG$67c=7}BG&NW#^C8g!zE2)xp%*&^nSV?9 z_yHB;gq|y36~JRDdNQI}8Fhx1s?_@d{;B?$8V`^mNg2!NQM2k z-c7xQt!>5Hq*#*bT9SIXBU%&oQCF=yP*zf=8ZKL?RLbg;+O=kFN==>m5XTMNd~{?E zI`F}*b!jmXTt$n?Xj8dVnw?f=2CLP#ChR#Seewt;KTNR)sY2pCqDo}hZ^eTF!=^p8 z>=dvO^e+Eh1_eTKJO&f$65dI{n;$D50E3J2Z5kBuYPP}p>tOPD$U|PDGl*UwPskRs zQEN)R73~vWGe;QRZpIZ^aMs~)pU+4Pui-YY&8Vi{kuJCgf4spoWe?IpZ~ZyYsTq4} zDzbuMUUI@4#uo71`It)WbRwep%(AW~Bg;gcB8*?&f!l@~&_{`NpRtQ?z0=Y34bhKGN=!q0RNSpP8e3)Xk%Z%Kwa>G#itjUSEffxdv>b!=h8 zur(jM0VXw1{C*1>>WxKd)1p5+=7R8!4P8GrirNkcdA|YXT>lQk99frGxcghx6T|{O z_opAnK&N6R`mJB><(ETNHUjjY5Rfh$l5nuFf;t=t#h;VAygs;?Q#&g z&xn**2H-0v>gsr|tg$ zB2>+nt&?v{0#+wzMOm?h;&Owulnbr9QK~afg;}k&dWG83gk;%j$5tC!24**_9g6s* z)^Bb-F0cJpW5Zh8Xe%a2T@}&L$PP6 z{5jF|9QL%?ZJRp*n8;Cd^9;Q9fLr74fAZhQSHj`WV_o(ub+=GwO2*z0ZOuE`F)FSwsd;4I| zDsePDhV_|LTM5yF@?_iSv)5q!gfM4dzE}O=0Td(-9TQJ=^0Q9-kJSE;M8;-C9q;g_jJTH_nB1e1ltE>|FSC_aMN{1d`grz64O=Mi_)j@URffpN%gmPtwT{^ zzCNPp5`u2z@Fx$*G}wk-4A0nDF=3r(y)t?C2Rk8oYUB?E^vRE2?8Y2e==kxnhizph zga%>Q{eu-e(e(rfHqM>DRoFeNH!6+qlPt335^Pzctf{ry`u+_H#NAf!#13t>nKr{h zHu-%b3Q5ymWiuuBrdTgLS6{db&yC9=fIRyz0ua-6lV4;&(aOy!D5JcDLC0Y(iSz;s zrTViBjWuF^4TCRx)%T%s9LYH+d&Zn81gTKz2}`mv zVf*Bbkc^Ta-6nVQYgjUAWi6OXc2N(fFd^aK8vu9{Q{)0Uw7JA@Jl1a4dWn1f!(=n~ zGng~le6zMjavj=py^iFlE#F7`0yVareuB9oZ_vE^Lk=Cn6AG`tr4_Jb4~OGFm42;r zuaoX`oXo(I-`;-PD9hnq;S2Vj^dIkW+NO=RTg@$6wzsTNCj91ILt>3+* zI2%9?D^8Q1)3tVIL`SEk_xCCWX2rw5yGQcv13teiKrLOoTTC)~B+m9gNU3E8B(~qR z;U@;I9W5SqXyBtXm{K%7f;fJ$Nk1h4RdEX5Ej{c0{#-rE^{ZcwsadQqRnA{c4E3~m4Ky+nA+!zmV+5cxEoHvh;It&IZ=c(ld& zSZ7c)BS#Cnb-(fsvcC(RpTdNU{+CX1&D5&tN(0%dRQ&@QRwZdQ+HJeoI%O+bR4BW# z@J|hjnmC05y1A`3Q?r(>Eo;4JiG#eD}5&3OYCrP)J!7!@#T^->C zmVVkR-&psnRGQcO3MJ4zGHNb;S#oXRW5j+n+h)eUXj?5z1K|@`beEqbFf>lqdOCit zc@5>&HIUH0I`ilyhcW4|H{7P^3gK;8wwAUdU$Ab)RqM--!OG9_(RLUmIX$D05pY*$ zXSfI(ia2-%rBxy9;ex>tn`R~Ozh20roy-!@HkZr@93_sVLimE>a|7k9-m)OQ|K|B` zMI_10{PiDi!hqC@NbtCq!jDDmc3hc0+G z?pr|POTQY9Ytx6wYK)=~2kEFIDHt}ldNdh(f0-}+coLb7IIPj4s&TyCQE>GjTna`B zoT!^@SJx%4!M30`rNQ3FS6iPAc70Ki!w;!kyat{tByb_^)@`!<}i|I~Ie=)}F zIfKYGCF6ZL?|4UA6e{oJ>;XxsV3Ab=PTL{oIPORz*C;=bBtW<}DPl;e+;hBm*V65< zIJ*vxAwD7{@4S0@NCoHzp;tZc;u0?AHhE2u#W@pSg3r>aw8B)CUH%Tyu}dRPoieQ^ zSbM#Nceh|WbIBJx7cl2g*&lRj+H_H*dDcLAau4yT9#spre{ts9Q-4wrybGS%isOwx z_WF_Y#`*w`>&oVoHYvMBW82iN)T&14jm3XO;VL()lY0}b;{i;IBW(pPjDlZu2;=a* zI&)#f?{R}#SGA%>SD9w!-5m0z;iz51P>L!{u++5Gj-%u|DRu{?Ox~~S{n?n&*D#0H zywP|}NPCOoz#&3UJJ|!G#F&cfwXo;Ubt^Y4<5G0>!t-ZeI^2kdy-<-}+B?FY9CkSD z5IVhqO}H7-`$-RB#aDFUN+FFSlx~Odf1sgs1RTbaI+8O{BV2We&WrR8oE zov?cU0tEbKPLK$w=_l>8ML-_G%maMWc)5@c$cJ9a?`v{ekSq1F=PTC@zAona87`_o zaekVi(~IDscEkPiupW=kxwkDBM1IS69UvF4zCOl{&XmVtaGbk_=#0ZLn zZ(KS5%SQ<{n}Zs4fwQ?f%UFLIhkd8fmF@iyBw8$QjTK6(x|K`Ki(Rc&W%+ijpuWge z$_*?2eGThs+H$j=qgW*!;td^K?j0MmW4#orn_~61cNf?GPYmnIvO(oqBW<)**$S~J zSF4doE5(ixi+V2=V$o3G=m){Bn>C|kN{+5Sd#OD(!wzhMVr#E`Ab(<+Hgd6>5#ep5 z`dt*2eD?J7y55(hFQ+97{0FVIATvbgP06j^i;tqk{r05lzjg9fS`hn$uOm;OQt@22 zQ?Q<4pP&^ZEfze)q;jF#ejq!BZXQs31;hzGVr=_VEZu|lfte+;UtcdS3xp%^nEl@O z%iBvfX^Yv*UyIgLu9*;147aH7w`=RyT6kQ?ai<;}I?oH<7$SheD6_Kmomk7@}B!m)DF3XuWYFHGR>i-)EK&4D*(Wb|*vX`0H=s%?}p! zhMM!#D(ngWtBL$M zVJnH*Ia|Z)<4|y?^c{Vax!gIF=W5HBlv78>x!nSyRn@iE{ytTohY!$2fNsN4QDijc zvHYDYm6EoxO>f}l_kZnMla+dA9mv82K7 zfS#AAD<$EJY+mr^!S#*lLyoa4OxU%O|349EZPJI*sf)rL=)?8z=t#=_l&%pFTB9Db z5#$KIvR*|1H5J+tI$WEwgi*UzlQ8Nsqp{p9Et<9!m|H`$FZenlNB-kTn%?eTUbOdd z>g{SD*ltm629%1eOh$|9=GoyXh2FD{O0i;9l>8~hKBh_}ZUa(Az4)Pe&`X0Hawiqe ztc7Lg2VI@C(Kcd9!aBl)ci(P$-zLQ%;Ua`$iUF_nP;m+TR-Rf9iSk=($=V?mhrxS{ z`{3G1whd#r*hFci80_qIEEJi63-MVG5rdn1lUqZ*b#dG{-Iu>q3G(3VS|~JlJ}TFy zmaj-jn2t#dDJYGC=VrymiaYamLtP@=+yj*hvh`v<3M0xdeAmo_ecUsL#Eka&{m$&C zUC-`;qtseM?)B^6ZsXwXrd;8nZSh*-YVoAE+ZVxu%9e8vreNPZw~tc{*4n7dLh}6U zldn!%hHuAJHw@*$J{WUYyJmL6f0#aTf8FivgvJq!n<;t$d#tyczV(7U=#ynrF_)n( zn4K6k1;cM*z-T}9lwq$SbkDOqkXs)kV!dLM6{4Figeioz*3yjau-gy$()F>6qv)>1 zI@@5Q?&VosdF4V+;mGrn1NJX$ij2PN9x-IXRp!#44`(4%=N7&**mcf%1m}CnZsh?J z$ah0m&??~u5|{wChsYuA^Fz0G_G7AXpLUca7(f1mVYyT;wdn1OiGNz|y%j52Y0)<) z$<|n@WQS~{U9Cy2sMta`S1YdAr_i(uz7O)dz!szJ86%Y?{>)AhKiGmC)HbKNe9?KM zj%n8!RU?erp&F2u>h#(HR?kIf*}W`3G3c_Ej}&tS=aP#UKwz56jXKV6`=LX>eLo+^nuXnhaDdC1tGdA3+7s(>#UEIzZvk*mJ_(Wck6%Aqi4m0 zG=6W7r|8+8)|rAlNw*y)Pr>=uY{Tb8kir|=t!IsaU@WHiKFjF#b{+i)A9R}GXTjdp z(m?Rgk9>eb{qLo;{0Pv_0k8{yn9{93y8hg_gDiD=HHUpH{Qwv`iWzNX$J9=9eJaY1 z^l23(O7qPswJHUrg$8viVK~H^t3Ca&fw{-9ed+T(nWMc zJE95>MM3qDq(OB*S?au&VP3^7xnCBPilgw{vYdyRCC1p@>BZ!IbA!AVk-x?eC-G{% zH^!Z~!N?!giP*FA=5F!i3hnL>cw?eg;k<2Ja)#<&W8R?;qoTemW*#&mv$~Aw%BJy7 ziLQ>gZIl4SZdjQy-c(5UNf|fBU2xLmv(7PtcMU`adz7r@ay10$d{P5dtey42Pi5Da z+S(0E8dNIoa}4cB9&jCQK-~odDY4u#ioK+Llh)TcVZ#&caQJMD$3o6{i_h+4_wD*F zn1OQF!0EF#Eyig`+blDNAZHr8m+cc1DD@3cLSZF5O zdP-I!*`^`~#M{CMtXRUWt^KCss5UyZ(WG#ZtUqPzU_TArG*s#8DOW0AiyK&)hN^mq z3D&K7OISzbuLxUB&d9>MZiWiMLR8Gkgm%yZQ za#4$;!0XVkdQq6Y!BAm;!JFCMraT(2wb5Pbx@5z6lwa;Tm8;QEY9o10DpymmD;F-K z!vu+gNj=>u!^Xu+CcOp{yTFisivdi5kw7GstND`6jsQI1x})! zgiOko?xB0EH=A@9Kj(RR__lM68^gitZ#iQ=ST1>=B1k8a$ioq}l04AQ5%m|Q3ieYyFlAQwuF2LmJU;-?t z#pmnBFJS}^j=`ow`0)G87fC#B&ZKHm2odR{R|xG;&TCi$4k)v_yj=~INJ2zfQS{1Hjme>v)oL-`b|o* zr;^MgAUH)k3f85gH=HRBkScgKRr@Y1Dx7(0rESy!{0#P7D|^L6knaUHL6dCO#L$}S zT4im@JWK6bBMVcjRNf%9cKu;uu3KCAO}wn$zTL52$_D25zoQYf=AYcCe9woAxo-ZM z+q!C%1X^uSqlb zUtRd}q>x;#gt%nLd<-Oda>)iLEdZ#CF9Xj)d$rwr~EtWn2v3SN%l6jS<&I7}<4xVI08Vf$8x^xlLPqb&7Q?2E3+&A7Xp) zL>GEQ8##$h%BLOngU7#o=RA5uR5mypORV=C1l!ah@%C4pq5GkwQ-~HI(H-UBMC;|m!7-+6D} z+kIPMsY_95iXBCQz4sbpPd7!=6Mw137(hS)MWot6!G;2&sMx?lQ2_-zSWqn3Q2`ND z`0iU+*o9rdB;Psz&yy48&D@zcZ}yg%JGYc~q~FNTL7GV)I{&Ln^gWcDsEqH6+VSP> zXyxQL8+5FJ?^bwYA%$OAw*#u7+Q70JBYDwx)us&^<+z|MMZ^jgw>k z7sFe2zsLYSebrfoH)sFNYU%|`j*4Qy+V@xC?h)oU;XE^se-2P2fHXtI7C62^8@}Zj zb}q?5gn7h`aBjn92RpwajC3P=oFzgL))=HGyHu z1?5u9M5_-rxkeG?0lDyT7yPz+-D;+@-kie2QgqXZ4xx7$rwGs6hTsR<@DJ=a{H?RV<7-VB`Kp^zS+l_w}>a;nLqT6Orj?^{$XYxZ^RJ2gm)(Gg9 z|K)9kVG3w$>DL;_*1*(}UZ>72-N?V)VEUo|gMe|m$kE(WONhg_w;p-wAe-BCw16~? znsxC;^TZv{QX0ims_hgeqdNE+kku{upPzUcToE(koUTN&j1*dDG2*oLIbFl1ac@+| zEeSXLPcHwfLx92I5%PAa6S{%XwIAKG~z1eB(5}iXp4ER)=;CX zW8p)*ns(yeu(t(~)>fCh4oL#HL+?07`h(f_GB}HNItTFe;tkXZV{~)N>AIU*hP#_X zGG=a}UD&?0fAtN5bDN`EL9M1k9W z+|Bqwiq%DnrOv9=LnXIQgWydR5J`J+;$cYaEB5B17%u-bq|+lAPifXxs1j`cbY(sa zK;J%htY1%uFT5e`f`*0t?!!SLhS=pgVIGu%-kwm^{kAhgO?Aw>wxmvyXCA0N&iTlB z0}{RZ(EsYzt2qkLX=@ERXu-TSW$1Hr*QFM{1i<;}^Xn}G72YyD`NyHNwCwuyb0S^i z>DIvLFiMvmt?SY(lvZPHky~cyj#RTA`i|&tS*L>(1|GEVptZ%;;fpRpGpmnla4+k;US<@D!=;Fj=!t9Tb%hC94+Rito2h`cEeSr)~#g2;h|&g#zQw*(ZE*sZ#7_T%<8uU@7BF#I&U;&?IXJ2 z)*n|qDOEM$UXg1f`79@B<>G5%e;N&5F7sinMAlcujLY5L=NTgG{HPlyvz?;0_vyXJ z+8T3p1Q3@10U1g4_Rl~Xp|#srAUDYq=W7`~Ix%TGsqJxqKohbS76J<1aY$%H>C(x)0plw_>U~T(mic**YeP zP>ndFzDF%?SzFX>A$9-ixv8enZiX%0`Yt(B;v4>JtaHNhBd>mx0!(ZUaX zMJ`E8Dr@5V`Ef5*9)d1juG1rB?J+y+CGZPI@uDoYN*%I6%FSiYu?PB&h+F|?swGNN zY~rxhRkAq&5>PXE?5hLctBzMfBCjVrZL-$sWf3@;q%-^F0mz$znexFegrwX(bQIyr z=p+B5;FWO3Wq}vQ&S>kF=@k5n1#w zkDm;OVep=M~ABUfz&3p)Bg_A!PT%c5kBHwm%lg3v~|pMT0Tc2&U6ZyoyGVq zd>9Ra@%#}emYic4%`oCM0t<44;>zAP-NZ`#7FL=Dm<3*d3+rIOGunboXp1+_9Y-et z7vsv5ZN3&pFMT(q2;%3ix(`JdzG+9-E_}+v z2j=9j(USwrE{u@iCg!5Rxx9Z!HI&?k_0v#bd*~6^8g8eTMhpmS9axrX0y-LG~5`L5!ExoJs~eiw(%Pa47kX#(kDV60o{PN=#Bbn zI#UT(HUj#YR$`z@d(2EIZbD8eBL*3@+cA|5 zKx2(xhy9dAi=Ggp4MdtgP~jz42E$7VlXs!XY?ixG+%HC#mcnHWkj+b~Jys`x&2sOP zw*?bfUWQANNIXUec>6Z+?zu0iKp7@-VCWh#CMP~55Wb}HDA7{^!GyuKF}OEeLR=K| zb&BEp6WiG*?RJx+W|;nroWsb~af*ubTr6OC zNk+7&*Q0h37?BV+A7}s_+2c*McL@Umt9%H?KR8O3P@b^E3(P!a zXFifdl75>iVX(^Xp zi#i`3`^2&a>@K!H?sQrq4qFah`(alET$NgETL!*^Lfc@J2}H$6I5~s!ZR0C*B9Gw3 z5@>U5hK~n?M!@cS@W>{793Q>SlQ?>ln=Co^{_X_Gno?o5oRDO0e!uOQ3Tde~S=s4T z-=!p?3a)mpx&aYR%sKm)PA@TB5m#wZxd>v6&kJglkx(FD%Gcx7+)`EH>`U<&(=Mo2 z=o7pW3$&SQ+Ar+g*kjP9Zm`r&0W=XWhP5`BW3zs=oNz!z6F=hs+LJ7q!2BNmUe!$E z`mmcSbZF$T%@AlI-@0%;K?^O(CZVzaIE;g|4b2kxE88YVskiaCY@X{i3FE>aYa089 zw{{4FaE%)pKWpNz>0YdBUzzvl*|d*7k4u{zfe)*6xkThyg2(%T`KE+Y-0$9B_EK2h zrROZmH*gawhW8nI8a!aPDH~3;dp-YdX9_X|^d$)MfGnXh1ft3P?m6H7_}Lb~l#lk( zY=%83Vbn4p2tuI78y-z1CmzH7Y4MK5MnZmMD~z!l@EmsM!(e900W@yi1r;&{X3se; z5?Nm#7~NlZbsrpQcgC2lq+j8oWG{vGr90u$SRzTWd7fQXn9oa_!t@|OSgBlRIx*AgHVhaJFeNmoPD{-n7ZTo{OWCf+y*(XC2qnZ z%R%)VxRTZ#Q}w@5f30S}*obpB3*2m@9tWaY9&E5zZ9`qNE)?h8pj@gXsuzSSB^Mde z;g?y~OM4qJE|Hzcomstdp2O{dA2Ix%kbVH3gIGF#2W*hBA)R-3G2dpiRbGy=HI>*- z*#EP;`~vruU6~fu{5Llz$XFer@aTWXr3p=F#z8x>@G-6W!{C zO+<+Qi|q1zcmfE+%{@fn9IB?m#EIm$&0M>qqxX=j;=$d1Dk9e>sYjZ1<~qcP{hgQu za-OlB;sFUHT)-grSd8%>*h_*{|LPzyy)`*e2-I?XN2NWy*w6C;h=xlRdAEv-Y982#O zxs5V%IZk?8AN+kMB(|$nfWA#YQXMMQ{YCi*)lF-3I%squS}|T))uUBFx~`J7TZ2|~ ztOcn9)P?77GW4p~G&HNxh&20GKdWyoPglbkWbAxHW9HtvXM-Z3me1FzOzKUk)rEiU zN8WTP-f9_IUJK1awQddcKp9+?hKA-A=&;rqCy`RcQ`~mymGC)SAbx>xU>fOoXBN|M zjo=`?Nbr2}QgSB~wiaM4&=B0OY%{WoH7OV5eRDV;jo7^HWi_o=ZzJ?dgE99B>1O@$|iop@7sw> zRP!b0&q1CdoPMDs-v?5^$-PH~-G;3_$A7x0uXuygYMP`PmE%DPdu8r}E3PHK49Kuf z>~1NJVgp^eU}FHKuZe%|}T zU>^RR5KFSjtqBq)Fa#3itFu5>-akuT!-S$;wwnE^9nY`BSs@WKi|OM<5Mvj&l5p5 z9O{%|idWxhUPHP|>JIj8x_r`$^HaPv&Ns#Jx*7whu0dI?zk2Y}P*nq3HYzpUt~IXF zrxn#xr^^EBo;a<4=e2PD1f@!+Fq%RcCYQ01TBKFOGb8c+tq_z6@tB=~O;>E@uR6Y9 z{5qchD8tSN7DLwfs#$v{35qQx?Wc!2cG<2voDA|3KX}D>cEEr#YPcxq;v<BEb~NcKahiaJ7#F zbqIp|wsNrku{SNVhFN!K;x zyMF4goEFGBKkE4s5f;BI=_CXW6ZpVRMx14P)8V!!JeM+e5@F17yzEl9r3+o1`g!!d z0v^VjAzT{wW2CL@^VM*O!%s>=imZjQxLyj&F&x$P&dKDl3R4?ZFeM!?&5Hg|5^7!( z%TF~M%!b%8zbC@TQ}ma>-yJmBt7_tK>x#+da`7~Q)C3dP+n~3yi2~OEl!0p9_;RfR z(w}cwtE-*}O0^h2>)9tVdX;$1u!B+HP|Y&W86Mw5zBnqWDE6%`P}pQ?(U8vh+!K(CLsnkMh@gpg$ZxW#N`^JUyo`xMg=<#nL6zFzE>idlOUklE^Wq+O-vKh=z5dqn)JZ zJ4F4A8BX?5^i9|2r~pU5`5Hs_w=ZqA@=J; zJ!9K#P)Rdnwcnt@?-RmoGxny#{`p@qmen7|GIhwU!1bySvC_xZo>rUT5~=tkdC+R8 z3Wu~$aR5|Kr+>E#s>kdU2Fir7?Xo&`usscN;v#2Zq4^EmDnB+Gb(1oB42<+4AWpwc z^nIZ2yM452>K>IKuPM86~bn_UgjH`(OAt)w$> z9cW0j>DD(AZ<@!(LQU_p9;v3Mss%x}EaMmNts1kqBiT#2O>&+`Ha$GOjn1=?IaXtR z=FnkUKaegKw#sr}tG8CM@j&%<{w*4?H#XnZvU_PfJ{MpBok~IV6u8Gtu<4&w7{WmV zo2@1&sEcIHJ!vqm6@Qx|A&0x7dK<9)lxi=9>E&uIJg<2LIOnRND_>E{NXN(=Ls0AI*M-FUL5tgqdVaO9wiPypg4H2bChDAFFX`~ej6oW-PLgv z?KQm2_z7nBBF<-wOb=88@4jOkJvsQJHq%}ikt-fR&U`Py8Q!!(6JPN`k@&XA`8xYh zkg8mPf@zh#C%iJUa?gQ{qZ5oJYiN!!$vq9b?m$BS{b%tK2+9=E{+Oy_`>5iNFFB&E z*fZxUDvRldW8(v#LFm;toBj@+?$6R7xD!9|3^ql;t^6X-ZTfvn zQmB;pcY5{BV%}Q-nWTP;3Jul&Gc<10%Mey$y+OooX!ws$63roeV@&4*5;oa2YAC)j zi)hVdifgaNx>rM^7^=5u)X?Zr6Cp%*o2b?$wH~r#Q&7&gLBIKms9qAToTwvG@v*dm z;AsGa3M&Sp67e=oFdxLw2nZtYt{pb}EM^zEh^)c)P^v+QnorD3r${#^C+Mq2^WPxJPvS}xP zdFV+>Y|)7beXiw^zVKWaFKZVzSrm+_LYtHHFS8!G$G{XLI`tQpE)pp%pR#II>#(1; zdBb6*+y)bb!zLhoFM{P&Nv7p~+MIuD7M{^id68yj)VJG3aM>={EfQ@PODhQb3^$^? zKts*fLO_TUn1*}95=K4WVLwTInZu5E@i2Z^#!eyTNSmkwJ?2VbS%RY-EM#a$M?o!n0Ud2T6pN5*G>ZYsAuiK-g7SFKaTYm|*mfB0I3r=j}hE)MH({jIyc zRflGQ@$2>P0dIlo25{FJu_*(O&2X>QF|c}8rp3uxQEX0nH?$dMXI!S1kUmJMk}15n zb$?2Z<-p@$xaSE2P_Hz9=1NS7b{=(Kj|S|=6s+P(f_Z?gmeYB1*Xt}Ju8aY*!Xag@ zd9?Wck0B`_2qca^r(wygeQ5MFWCeG`gj?qYmvdk}#!5m38h3TNWYeTa1D;8iP$?K` zag9+bYAY@oaqH*g8j10}@3=0Em{i7j!IpODu-k_0Bc1-ornLTZ0^W>eQ}@p_7wT%GRU_l&@#Y71IhKbm#V^_US}|C?N< z93Q;_?=^Uo{QBgu6^l@9bLA&vKX)9Q2_;uQEAfR@-}+fgo)f1^XJgEpcXI<6|Aoe7 z?F*^9d}@nGF9;JjaXIIqa*iOl!^(bpg%YkN1C1j7HsxPf(gP=oKZ1J2CLBfm${Hg+ zZl(A)d5!c#k}G(_#tENLQPy6R7}q(%nBO1p2R;2_T0`UpP*;DoA%w2S)3-0Q0-be1 z8W6Y@CBrz5EnI~jpJNSyH_IM07l6$JHQ%ax8~HY{ox8LS+AtJ{DR4FZw}nJ?m2mmw zz+dmtaSr#MppgN8HAt>mc3)&l7br`I3*#J^RYIb8L{R&3btA+`Jo~V85F9}GC%BUy z>j7~b>F|8Gc^{Ohz5~~zjXN57Uj(U#2xE@K(ZU^V<@Y-lC~vky37ai^c|{#O^D`E$ z$;o2!S%ETTDq5lc9AGZ=hThiOsKF5sAXe^Mi>Y(B^e7!pQW!=v$Yb7%!={uf!x-7i zO~RkU2C~{#bV+)xU8cJ*wVOip!W-ySG#f|D_ZqKF0a1an$eg!BKlFPUD5FK0v~YHu zL%D_M*{~H3IQ4skH>r0?Lfa(8eN3%;&=?KuFb?P>xsTvE^T1uW?w7c9@N`z;OilsfKRhp4w-JwRkVEAXNJ$1I!uH%iN4_LcSyWH4tx^Y`e&yBaSjp4Q0n>1-v zL^bRe!zfiS#Vx07GIC+NwKVx->Emzib2k*MuYFEBnpDod*e21H_*yfwZ^%W3bhd-g zN0B>>-AJ$N!{3HWF?I53xC2|seU@~M6?9K}=gmo2$4wiQ0f#J3wx8BvSQvfQ&WOh0 zhY{w6i2M{bxjP*PM#`|{C2S-6;YPt@(6jq$afOxw>RG0G_ zx1aQ*kw;CDbYm5J2=5jj^%X-OQWW4P#nRTh6?~+cV%(rKt=A`UwFPE#Lb9=RY`nkUZ{7+5IZx$skJoTmTJ)J%P2-Z2f{1At4X)E( zlJuT5KFpqDi5Q{%GMDLU) z?DWtk=`J{&5>5Lsr4u^-;X8NtFcrs5>5|YF{q%}~R}B3yV?A|JimiU5FneYjF#>rF zKcgg{_j}yNn7`RQ0Pip--rpj?DBiVHAGj2@i0~e|R*%JBu0Etzxqlx{Z>@rZ2f<9T zeg&LXEE(+oC%Q63itqfbDi@^eM2pl@BwM~w{2}knXAvmkays1kn{cBsG+xC?aM+L3 zegL)zxGEYQ`abjmbVFEJMH}biIkom37s<%`?GMu4#1|LM|4%D-ysz{5KD^U4vPzYu zG(Ad;@jrg>1JwVP(x~GS^MK-v3^+XBQJpe+wLkdpgxvP8kyG%O$UObeSVC3zATviZ zCc8Ngi(}ysjqK>Y#A0#*>2wZ<`?nU}TG*}^qnlRh!|}IQV77$q`hr2$p}Ss0Z-U~S z2F0IRPk(cyF1p#Tt5f{A!_t4|nWbSV1|o`T-9iG<_by^RB3*SVoFR=HbzM{53v|G%6>eJBq_UH9I`6y-9c> zWZGJ&$M-w}ZQuOx@SDU%aP#9E!(Q&g7}v-#Ckmcf1)PI&xGFCg@3_7SWKpu?oo8sKFw*YWb;vZ1wa?!H@(Y3&HD&>@4!8p+VE0!(b0d7=h2S2&clMdi)dT${ z5~qb}jLB>BE_J;nPU1T!p?W8Nm^(lFpq~$W!;xNo@2;EdBYeIBzR$~p>*F!T?Xw{$ z1di%CJ8NjRDVb{wO7O4Ij&a-XDpA{rK474(vBm)-C9awFhRpp+i#D}Oe8_;r=E zLR5{pldhYvrOwrdp-`OM9ktTx-$k(ttxxmD_cHtsJZx5#M4C78v2BrH`$x}=-iVD>ej{U~v0n@D2(ofk?MwMcm2@p)@k;gW zREfu*-GieX1W4LSJy(Bd8D=#m=U8jowFZNP_#L-`FHgC0CUd5Am$+ z69oxvrpkDze2LXdGFA3qte*)c6fv<#fAB-y3HF`2KfK`{$^NSw-@4yP|IekrmN(}% z+){rt+X@h^1dvS)+*{G#00`$};Kuhcs2%`UtWS{X*3}CEEq-2KY>t^5WZ6x*^4r!l zJ{;&i>zoVf9x&8V!(H!MN`>J+!YOz3={+e(cEHHkyC{>B=G2OK8@;f@ft|Z_gb#pKTLo;n-64Rv7Xu{pCC$YsAxU_<1dicjwmOR84}1QGhUo=B1gvUq6qHCFPa)p&l4JNwU($K+(OVVvlNLq zgxK-d#&~jXp$BjOX&Ym`GC&-)J39jW6kN<5e-C@h9V_W|2{2>g)U=0tCS#Ran7mPpd+EM*e- zP8TQA6@oNixbs$PyC6IN53h6UkhdORbqa=&*C6UPG`ua!Hip+lS-nsC@EGejPd(SU z8ge$k1{y9>>jCNuBCcNJp|7sRWXR2R((%Qv4V0p1N8bDh`dTqqcD>U0AyM5U+#M3% zHLERZg^ER1`yr?;ttMs<(z`%x#Cxh`D^4;Z>tn>@=Ud9Jg!>u!K0a>oUFi{lVzHIT z7ci{*l?AqR@;1y=MEZ8CF|&MWIrOY(8z-WxApnUI-LMldvjax6t`gc3X3Fb3m|1X| z(CC)Al77Rne2ftLYdGY7^i#%QLDO-}{WcpV7>FfHCcPwdc_I;tid52qm4HRwtGX`^ z71y={s>AqF8CBObn4x#VN+Bj$>U>f-y&i`Df`+cS zWNQ%ys9a-{JT?LN+E4iD)BcKKKfM$P7fZ-m1_Q(3_YaGKi$}o|(=Ux}6ZL)9I%Dj> zIpq_%?TY^*>1n-?X}+LCzWN{Z5Oo95>MJ!SOzmgAWZAUR0HM}ab1$_d&m&58pW^OP zw>WR*(wWbxH6g&Kqh01jc22zkD-Yv2#IF*DZ?Me#?!{U#@dv9sX|y0lHpY2b7-Y6f zyb9@<&5nX&3)+iAhQ53PE8TF2ol{dgOT34C*Ox1)tyk zF6P_a?)jr7kuFO|e>R#%JCAK$N=6Yr& z;7Z+G_;f;3NCve9XC%I8t`;BV2Wv=NTWi5gz7j@ELT(OdN`HrU1aPG4s!Y$0h zmyj0#ORaAJQ&4^<%hVyMCwMqr35E9kuFG)+Pnp+#D^6fXG8`XtW;z8&gV9%3 zwraXq{gP8o9-lqyR?=bj6A*-jLXI~$Ft=C0*Uy9DZ&U3hKuLzKN=>d-Dna!ExYFh= z8ualz4I|d@)YibD@t`5Fx2}n?o7jVN*bj)N^vQaJ~~jOe@92m^L}3` zz2%-Oxh~2!A4F$zuY(~ziAVkn+eH%FdZgx&U#JK77Y}0UVd`m23-QO&uST)zUo^^s z&}$WE+YVBr{~HClk6|nlgv4(#ytk&ek`A(o{d5KWkQElseP*<4JyPtX1Bx~1M?IF_ zhW>@B?3o+eEcscm<1yX>AtK9!_^@BNrgytp8!%r{JgQEQ3$|GdwAqQ*FKVn`RRn6_ zY;$F1KR?NmpJj^uMmXE_?&x2{Q{rmtM2nDpI8s{o3y#%Yjz_?n-WfMx*JH4y_xnTb zw6(Au9njYic^R+!hu+yC zXnkJ$epdTK5ySDI+SF)g5m8}8m^aO%xcc6@S#;6dY#YX5EyEo>Bk8HqI(IkM+FYB4 z{F))HT4Y~g;N+!Y0J_JlMo-Pl!P-{+0mJmzMt^x)cmjOzvLug*dGOqCQ&z zJ@%uHsu0e;f9xQvfaF}bHRoxk9+$`6_k=?542DM^9}b4)Ty4!n$?kfe!+EGKF9y?8jJB^j>OllW3(|i>?<5PZ$VF`TloWqpda+OE9_eECbvcy3cV;mZ z(}hrtE8v!6VOrE?^ZyF^+<9J>^}oK`Ccx1n35 z$w2Ouyi*!{?tE~!dgcV9p70%1c3Htt&=+r4yWU(xEJDVuOUL+B^ZQ*iS_+rg_))T* zr$AtQo4sY?i1VQ!zXsipo|5G6Gu$4{B^GYU7}vbS16Sk>{opV*xfVfbx7A{F;`k5G zY#K^vfdrc1x&8`6*xzEkA@XRBL>iGp11<1ZE=_g<8ndTXavLrGY>cteY^P=$>)C4S zpmgJ?)S5#!*V90oPIdhQhc@>bsNnz^kUw9;01|u3 zn$Z7CA4p8(`GOV)sH>YO)bN113S7Pw2SG4W#dKn?cG?#lz}A`iOuxMjj@uvB6dHw;^h}@5RUNJceB2o~ggN!Z#lX zCX-4vX1BpJs5uVBNgx}v91}gUw3_T!{S*Y--C3UK719$Ezl!7YwcWv#Nu%}&U6KV+ zq}CI$so>Yx+t>fTTDpbC+t)h3mb$yb7J9VMthsz_C73QH@y6`Ubu`r0s9x(4J&^Q9 zD9{kRhQqzp-)7mNe%{x(wnd(25a1s$LmggN&k3Jjw;NPbw*y!r%w@UP@_%i?qqXw& znk)^rHP@!KldHvu7Jyr5=8;ezh`zdJrq>9ZUmd;3q}gP*28j&gPGc0&`)mL$nnMb$ zHr+j>=1B)V3_R3hO+%rP^#hlqwR=2DsZuEJAmtv5%7`6k;Vlh{MJpQaLmQH3s7 z)vB+rXTdQQNs_EFbz8F)65IHKeEYbw*Wt*!#V*FF7BvOP>8hVYaSYyMKBngPyFojK z{OD&lVJ)3bpHaDqci?U*^UyNFJUko~tg(f}j zRdNg0k*iHH2&p*Q5B$WX^S1X|gUOn3RLkja_M&p-(BShQpfV)rJ-*|96BuifLWj!Z ziAO59a`=fOsz<$?>dj;^CYxmUriiQL1FvB!xMda+yR^lyfA-0{5H#nqc(c@3wRQ|2 z8T2j&h2ZuN6b#_yW_Lg0zk6u3Wk!mCWXYSSQ9((rij zi8kFt$YVHxHrb*}b1>)=zNo+Ks!a6aE{1v#DppMkyap=?JA3mUD3`JE#=CzZLt~}k z{kCA9Ses2mG3zk1RAEFTt*0mwKm7!|@B2i0=NIBP`G!E4Mn3_(GRJS&!reiOS)BGo zh>-J_8QT-#9&C<)2iMSVOhoTxHVW9?k}Ttap1+dUa`aBin318pMIMMM@V)_aRPlX# zMeKvXUC><`KaAZx3BwVD^^^%u_s<*=*Xan7rVWDhm*F-wlX^sXI4tdikqag4R>5b1 zAYDOLk??(BMCXe7%RH&24iCoSwE!ee|0Ek#TsIivZ;GoGY)t4t!QB~3`T=!~T|wHN zN5kS|xIlYS8RksPSoSa+XJg2)v-B%myf=OaC;ncy9mxMiewQnk@$ODdj!MO#x((dT zmNZI@Frps+4b^MukDA`UH{Yq*)@B>)&tLjZR{A2aMUEV}#Z?r|WwGo5lMq@M!3N7i zL|esqqCi`7mxnaLRIkyH(1FS7dIOdj-@spc!9)7B=KBR2fc(yqV3Eg%x!~I^ zQ`X*RYArcRk_6YxBhAlEKlFYyb7`uC$)C7`EJM4Frr%K8;XnF>#stWlto*CbX*^)C z+~BAE7njsLVjIF-^cnWe6t#5)oh{5GR3==28q9uE0f7SLK}*G2CZA4Q59}VYoN{?g4?FqoCw=Hr zY8MD;Pa!+I3ImDVk-giOPb6*>3*TZmP>ec#jXGmDZRjajM;=aOreWCqnz7$@6XgrP zL|9+wi-|s`_@U=tyFlmnYdE96gbsBfx8SoFW-(#u)ka04FX4P2BRXh|&y3s+7{}~6 z#kf?!EwHQ+Fz?3%Le;1+874=sg0(Zg1e>eW1z?|y!tu_}VdxT$4Bn_B^cEwnS`R7V zGrJSfW3q?1)ChWCqVfSvRdVpGw#tajhjv@LUxT!K{a#)so=;$hYAaGTx9_Hxt3Z$2ojwiR@(`)@Na z2AT|1H$L>~s9PKQ=qojtqM0-_^Obc^Zyort2~HTOXaQB!%{ok5_n`qlbV?1l(E3t6 z)-Vl<0u93G{MC;=+qCv3>nPP~ii@DSToZ`ZJWC!ad^4riGt7PcoIFwhnXrAH;IPq2 zC&%PBAUkO3c$2lr7Q3D4a1YIJ&;WI&KO0I;h+R#^M{FggCMzV@NNg#@T1YNCTwrm_ zaEAVCv{7kK8TU&9n&;eZ+2VxC8P}K$OUhQj1HJhqMSIN zblOYSxNaDieh!uiZYweB!sIk~#C-|wSE*7~{9qyBx&#Uj*rz>%@#Ir4D42^ul#J?H zn1eEP{X3m%bM_gZRFIhqPdcrq3QZ;u4`xn!C>Y;GeO{Q6sRuLuv|=g!Vs_*mwZ7_- zKX@?N;(}TE4}4&`RaT!hGiIx^+EA zwtTcE71Mnpzl(&MVc_62go5JU#;3jNgzlhO7Z!3gvVR?nT#IV7icQqwjn5eRVOy+i z1kmVF&1D+bX`UPZRlAjGDusN3!z4PIyTQ9eb&GH}$gc{*_)=uqAQ2{G_+XpDNWrX> zvXOFWDaEIzQ{w5EUCd%Wn!}2!>1e%qMT-OQIwP^n&={ z=LfW68z#vUS%(?3F&M6A)GpfS1FYs zyDZd|A|jDy7ozbWm1zHyYW~Ua-+y8A(XbrNf4`yD`lT>XXtF|Di_;`UosM*^e`vfw zO_xYqzeJklk*e2cr!UOOypW3hv|C9sQL z1A;5o``X~UMz$HfiROhU8U0tMWG0k~NJZ9m6DK=28b;rMryViP?Od*mtQHN#faMak zH%{%Ba~sE1jn89Fmw{6_UG_Ig6t*oUpYj~=2aEf*D$a}7L>_<-O~r|l!yUJvz<#Kx zlFjY(2;P`43dFXZf-P=W-Z?RNbck_&JD&fY`T*JAp{F5Bj@L=#`@3IF=8nj@rF!mL zUV^F|xaTb$gXvgq))$aFtZ%qv387R$ z6t|2TyV@zZ1QKD?yQe7WD?)}Uq#IZaQM`e*#i%+jCtMMdOe?&|V1r=;8k^cY1jRYQ zAm3bAG$D_qk9r{6VNVL|1122jF5#{SmC`GfqN_CXiWt=oL{jDSlYzn-$H;e5*%xhU zVV_u|jJxiBbHXV8R304!1^xJlJmn5MVed=^$NvW3B)vQB6~W!B3D7(|4bipz>_4P&E9HHl5Tnsh}SfnyN|=eVT;pX)F8UXvHm^3@?Syc zwH09~W;P6M6aNqhGhqYQhknW~w-919l-aRN2L(ESWXMiKv@&`XUN{YlTML`1hn%o* z_|`TtaHAiutB13q4hVJ*vs9+QXFd)Yc$zPv!ieR@{)6ubchbW3_>PTWl#O)1-{lz4 z4`DGi>M}kpT_|fOjpoom=a8w=gGK+yf8`9R^+VTf8JpHLIyhFol5W2$m9XLx@NnR7_C1k?4ooe)2;U5e-A_Wj}&>oQ@-$H?;zz9UHx zyy#Ok5~cSKiGUdazBk14LdcM(He_6vLo(xyBCX*p$WhB7cDcLJl;ruRjac6Ss2qIl z%OQBSZ(vD2qn3P>4RJkr=4TNuNd&jY^T!;L5l@gfX8+Ze82dkfHn|SjQGoB^1>`Pi z64cJhFei-o!r`P1DYCsc=;*unO}@XBjfu6~t|&Dga*%$STFWz@jspcE+tXy z5#%7`Tf)|Obzh}(uVHc3p+mvXq$z*3GYxCk}n{ye;(p$mk1bG z(0vOUgpXHUW@ow;Ux9U!yrD0;y>hwqQH~w*Lr3#9m9}*bYdvP}Fz41N_}^4>u-r|l zaGMrKy_6?WJxl0F(}Brwj4d5rMDC5>0ZX7D786MAdjPTnF6H)#gI>w@cJ_g-wnf}y z`7YO8J7BZ>&Bbt5(C2#Z?iDzSC}4k-485Xy@<$IP20ro z=&Hyb>%84OI|-iSDK?eK1kH?#IDW<#zrWvRqvBi|2w(mFX1!+e{O4kl%_*#;@15Vt50lR_!p4vVG`Juzj6X3YH z=&IRiv_}B>%RTC)5@fat;eE4LZf&as3R9%FZRJ7OWd^RolS zvgHOvl|s1lX4Dy>MHK2nhIGLE3byno zjn*Nyyj98oZCz&*iJ(Uf+F+O39CUMS+PkdQ!gbKOYHgucv&~$v0U=ifDhx7NrV(b% z|7~FBZ(nLg0BE!RCgpm*Z@8dOBGlH^%S*ilu2hSS7gMU+6nB%lN?F}BJulw=!-Z22 z-81N0?xvt{*7m({_b{e&Su*~J@y3BZ!2b=HC*l9RV2~p6S#_sScxZPh6b|~iJX zg1)`bu8aQ*xZeNCdwcys;A46=2S*{?aONBx^y3I*eLPrt9;k;Z{otwkd9%^!?+Sb1 zMX1hHo|eFftACH-)I;#y^6k~Vo_$dJilFg*aVD6XN_YCV0kfm?;5A6!w`Xg<a)Ejdw;DdtESJ$3F|`Uv6PH`dkcCg(*-YPNnyjpw zZLFP`VGzSDypdzqM@Ap^t&5g6C#9<+mn`9~Yq5^AlnQ-Vv#B=@iy?D-k<&4Ei-a1; zToB=uKy8Hy&hb-rRKQlt=bmJ_P`>m8WT%3_>Gn8zbPmv{h6$HE?NfFO%#yzJqbIno zJ_I|C!NYFG8S>L>L1Ng+vYx`w8aouhopr|Dg26n@6T~s| z`1I7qz5;PHJX>&CygeRXw@;7Ljs>&4 z-X6mTNG27og`gRv3gbgP+ztXycse*0_T$O-6c-l>Z6_f~Z~tUki1=iO(N2k{!ED2T zfmg`d^I#Tf<}GTgdaxa$d|-?hWCeib4bxQCc(J9GJ3l<*=Nj}FR`-(l?0~SWkXJ9c>p+0?gVYFgBeLd>6t4QXDW@~v;jQvSI?i54@` zVlT}W4RaVSRF55q2dEZu3v_5n=RvCfidnt4(gDy2?W z@1zA`=mdG*2hrfZmpbsp!7evor(+D@PWJ&-VYA-mra2DP_oQ&t6lsR-dgg>k9^OHK zRW|vPEbrZd9Fo&*cmv8tQ+tFMZ-XdS3Psm9!Yuk!G1P*2e@wyVzFySxWVk6okV?_8 zpBD8h6wAGBZPnwH`@$OAKF4U`UV+k^-+Mz!L3K#7)zqg&KDt_*rW9nDYy^8oP5g6aoi6#^vU#T5McF?K zys6K$!|;k|nd+n1Z5>?epctz=2Zkq3$Q_!XT(NxY!g z-YQ$PJQAEEoPD^1atjgwn0Nx5W4a#{q$wpMuKsXN=2QevtuJdvuF4Tr@t z@wo*Px*R3z=n^~B3oQwOi0)V5sL6|T5Cp*2OTR4q^KWAO`zGs^p^)BXd4GZA>u2)m z`%goaS)mDD`kQ!Uc~xx~~e8axAR?K4QqlK-gumTAeNABCe(fEsnbhZEn=S-y<+R??bVL z-824>f!?0_(^_~C)f?cdnnTzoZ3Z#8QG;O|)^2GYdkwWVgSss>X`wb>#4tBaZAdjl z-G~Wj>uGcZjzKts4Y~%PtpuyLQhQtYTEV8(i+j{yxN?)It`qJG8T-_nbWRtz_$c1B zUTtOT0SAjLs@w&C@zTJ8s7R zM5vnO&T6sM3`&JDMvhSV<9qnSM6q`clzo%>t6;)+CgC1r`C2%NS{m0w=pOnZxv&f* zh3(ujyQY7&DEYn5D@3NR2b_f-d!(zm)j+*&6ELrI3Eb>l zOh4o9+6OBNVfxhqh??|rPLDh?@nSHfxaap$9T%{P->dsvb1DE{H*@G9Xgau^9J%H< zf&9_9ZJv-a;)t;tF43$20_JH!&3WA?(;Ea`PI{4-R;Kwq`GM|Jrq{ktS3R&wA=FkH z`)R7{`%Mfazel=k=-LVClR-q8XS%h&(bdbKD_Fhme7m+$&0c+f<(BdbQ(mKUnp1!BBI{UNFzxZ;_+^@d==G*VS|KZ1<=Kb&IUw-}V_dn+Ux!|vbe|wDp(C`U@eA=UXSLI|91Wy zrft!j6c6MTV?Rnz)%@fPU8!(4Nscyg}? zld6e5>#tS)zEZa*no(MVNKYLFfwG9X=F}s<=fn*vwv?>f5BJ^`*p`yxbNgr$J3n|G zU?J2_K`5;yb^5AX9i{JO)GzUey7y01O7DrnSe7SjYU#Y-Fc8*_f$>oCEs(Nr|9xa~ zH_>9__xJ7wPvkeE<_O!CKF=K|i6V1-1l^`tdPYSsm#Hss*e;? zXo$h&jGJK#dc1FgSlEs^=HoC%X98e$IN0^C5xbT!In0l!_waNVRJVY;@&9`!_z!tr zeK+B`o(m7v$$y3aUs2qCITzYM@_v*Gvwkk3Z1QD_SxXfdaYu9$PST7~5V6Ym`)-eT zQJ|TK4yV{PIgpCxNyS6ZEu7AmE!_)k>05xQoRUZYQL0-pylhD~5xtSV!LkL^J}6}v zY|V8xQt&1_aogb~ebtGA^BA~`-awll7TxxmVX9|aSO*0%nFc{T;M$uj0P_gn+HKK zPPFsK2dSrh`Pn^XHjCARE|V&!lo^$N6L$mzCyg#MEbatgmq3Yj(oRUQ2>NkPn z&yBi<%m2zosl_#E0BGHcsG(Z(`OU$!yfg@*UI_#L)7;?kfA#t!2QwH%M!ap}F7`MT zN-~8QPezp{yd$X1$H^y5HGSh)%tx?4M2}nx!?_0v@$2!^mz@TIDl7-83qf$CCs}j= z1j9|z+CP+Ga@B1(u6lu5_M2n114fy0J_`=HCBtiR~k_fFR z=TXwLE6Drplt^U~WGD6}KYxBx5YN1zkK?7$a(dfgNT)G1 zRj}+se0{Xz$u^SHlCAeaV0V-tE1dfrSGXTvL|(WRyd-3@hj^#6bI8lefLwY&d>h#q!-Daw0+tCgpqNYOftlH`jfT?Q7nq9 zC76Tl(IF5V-T+I!N89p|2f*&KvGIcepSJJ!;77rV|HIvTfK_!gapT|YDfirSD;+K< zAV?7l_8NQF7&S&?Vu~6QqcJhjH*b27B27dE0i}o{*ua8{AS#Mt0R`-W3WC_hf+#lh zn{%by3+G}<-sk!MzxR3GxI4Rh?m5fs?Ck8!Z&;6y0H5`{>xw>I`6CW%&!gc_%JTQt zz>3Ld$K;?tdCH`c|7Ku>*G(CMeExQUTFECX`gdYYZ%uo+6O_J5z|W)6r_JlbDAV5$ zO_G;(WR{^4G2;0#!M4c0L>p_HbH=A+ArkBUe^!u7lM>y#Sed?j0pY#!dM=qL_XRIa z^d7X}to@#qWxOw>$a&ClxAUbz5zXb{KjcZ{n&BHJuzy9GRhoudMPt&O07&Z}d>qO; zpmMIXPGI>%ZR7sSJRi`%la+vIzxI2)GUm7P!HM8C-4+LzQRhab^QM29hY2I=)H2Kn7R+t#$GZ75!K<*I(U-o;ekeN?BW>q+{mT<1qB zRuDAzMyK|hpt%NoS<{CDy*1P5)>JfY(QAj;>-4BE)|VQGap|<_6lHR~3cg*#dR_1_ zkBWKi$^047JSO}@avyR=ZM+3Zk?7>u-3E2uebS-3+I?D}I?*R~Zr~j2aJu^%*P*^L z`q*a3>=TbzH^KHj6O{c{bx>UYO-fhsOSntMt9u|ijk_YnI>-*QFVI^Rk{Z@uO0JTX z=%=aeP@*n?m9o{qeb?P4q+?(D*as9uQDZLJ$5V=mG+=438Ir9Sa%Vx8x>`r}mR{$w zC~1jWXzRP4QCzh+B=@D3K)JJ!(@22QERher472mh3rsOyr-#}b6i506+ihW@+u%Bk zq6$nE8#_p>^2|@kSh1zZy;_I4(gi3%l6!i?Dexa;S?bEtR|GkQ@9Yissok#f@eGOJ zjxL6SnYiHpc*1wiHj>T^n8jG^#3ZoPN^??;db*!WnRVfeD>i#)^6E8a4R$tFg1|x9iumegGmQ9UyK|cGZnC zXgIuKy^f!BQZX6q)3~8AMd!zPPXlUs?K*8zg6T^yky>Mc_F8#H>j<=Z>7_5L^lSB! zOF!DLp|}#0QCixuzp@{1sM(c!k80-2uT}9OVjS}VG>G2y*pTpQYNidg8EnwM-pN+|l`D5UH|n_5>AN=3mj&J!+E_tohtye&8>kA+p`uo9%>)-@|{b>c^TsA$ZWRtAKl* zx{_AAWqk4=kCq9h9~0A8PhpH-&LpyVr}4a$PbCjZB3;UCHT;hK5E}y5eE|2D3gdn6lG{#h3@@(^!-3T8IZY$0}P`67841SD;P8h)5 zJjs$wtdxw^m=-00tdg8zEZ3WDCILc}b~V-QjNCE^@tnKGDbFY)Y{-Qlyyq}vWE4Yf zqjo9T>aFH5g95%(%;5;3DH63(%o$~LwPGH9VqG9Zkd3!i5cW22xcIpqtAxtAkCz-0|XS!JrpL)l1S)BEl^Fv z3um<8ZZz96CL|4%13$sbXf#w1bpgFcVsew_ibk_^I5`Kz7Xw;GC77^@^ipVRlVsaG z)0*8j9~9cBkbRU%E<&VjPMUkqS(>AW85F{C1heCiH9(|i6}0bv!zEmM zHQwR(9Jc@!-L+49xAdslx_|64HixfP3K7kXmAxv^R04nB(AI4T)91B3VE{_dz&HbB zy8?Oj@Uu~eG^Z8=M`iZD7)qTH6fnTg+kE)e&c?utT z116^*=DL9+{O{pz@`b;Ks`E78EVkEF@HyUKy*dQU7uyqA7+vH+^6&r$I6@(hAAOD@ z@-R9n0bCyrz3cADZ$}YTMhTAzRAiIq(#IC-ruW8l&#qT~a=c?ITR=S_tm$I=($9(T zTh6j!x8dkx@cPD4Q^d+EZ7U~MZiGLzZM}MQx*v_ON!1pFKZA*@ zzc(+mYmqUFUJ)jXm{0t!+WDIr{ zYY?N=3AcsyJF*w0@u2u>l4c?qN4B!0Ct?m(am~GGZmaqQV|3Oug5^xcSqV5w>~V^` zDhwDGLGS2A=CesOmuRQD(JI=MaN}00$_NRTj9HGRk6_%?@i|7pl5^xP-gtY{2p8x# zHYNs$QPs!v^JWJWLY5O@n??Ku;ps%^?x9)@(z`&vq!v++Kr(J-pv>e3;f~36tAeOC z9d6Fti)LFXosSo`mq{!M!yW$TrLb1wPwsL?_T}%fMk^Q$iM6T5P3~ex;!Cw(Ow^gs z@u9pNwCaY6Rb|OYDOm!itk@|xVKv{eXLkY0uvCLki5dOhQyzh8$Tv+Hj3F-$n`tBU zHkEWWT%p!cyDiB&N}`^(<~{spvx9}q3ILi zNt1TcYPtaEP*bDsk)1!yVz_xKu9}{?ikeg*Q!K}Cm@&(D#Pgc{*eJ|i$v;v3tIj)3 zF+)RUf1AEKjzMoZ>@B+^)UoaeKDnNO*HD_(6u**6uYwpWpRRX^NvZq3shFWA48l|< z;j(>GjqY59sV`ezDTbv=DIGJBtmCucgt26b=O>)nDp%<_ow%GMZ8H?7pp4Czt~8ZC zM)tWrc6Bm5af^l%U#S(Vzj>exmKhx$KJ=xjl+Nm7G&DoiL$QV+_;#4BHJS%AR40rh z;c!cGOhyuS0NFA2xII~bHwEE8GK;#JZy!)3S1V#B#%oLvJzG8?S+2Opg{iC&)})g1 zoBO$B=^T0uu!8 zQKc|&zeOhE#~UT?h20R+VF|MuwbnDeQ1xuLo899+v^kyE@3BOiD&3b1O}z>q^$xO* z?Q^*YvmEff;yjRni+{}ePX+ht6>&jV$ip}joF=2AYJ^OS+ye_|F3N&OW;>=>(BZO} ziPk$?tG9EDO-wSSY^1`Y*LZeHNc*L&4okh8V}QGWSc2F-trP#+GmkJhVS((v;Sw5O z`X6U>F_>mRR}s`@6a8DVhIe7LH(Z!D3zPl4^|-eh@cPa|RH~@LS?jRZP-l8w{cUtA z1A5f!$Ocp^Lu4^Mt7%%j?uB0CVkrUj7rmux+7H1*BdmE;P=kMTzMlbM=TQ6T{}O%q zH_b_XPkxy?HvI3 zc&|gSAGNCuiXx1NU}aZ_kyU5l`37jYEFYAa@faVCupY$(7f=4kdAEUi(ssW2rrx`L zS6X(7{;zL$k3DdKBXRaeA#j2v85u51`QGWU+5^KOyow>u6b(n!Gj4xQ{cwTXuC|*; zlPblL2$rqy#gfbAQ+D`Zc zQXxO%w2+tv*vo_-#{NozR8+e{g?#M+?X!Hr$LuNH@@Y1l{d|yW-k%J0;r+F?=2`uj zy>d&}E9l+!f%D>zQ`|S*2J;K+;hvQ|)e_(1UrFWiMlqAGV_rk;hPc-ty=fM1(5lPP z;$_$A4od=Rbb&{wzx{>x{pY!`8mF-k(wi9ywiXvw;?#QlrPsCF>0Tv@xE#NJd>wGN zzTefq7AMv~qBbEwMR`YzK^pw^o2ZI2gD$o4sgpmYbLc!wX|gCjlPV#Xd)qmsK1B;O zUyTdz08BV@2pnhK`s}e~(L5M_TeIX2Tpa2+dDg>c5bC<<+kEJFCIF+S$iz+|f9Ekv zl!WImI&QX>^Vv3`txl|fBXhk*gxrIa8B9kOPnso|)E)s!FY&TlQ=ekqV^y_#B?h!d ztpxr~7RE+p#{%=!Vs7$FS43UYoF5hG56msft>f5O*r?bsRY@@AlXe8TjrX*tzltFd zbC?$ARZDW=zEL72i>8j)R*+k3G0`i3c6zcy##LaqZ<@MPy_T9Aa6}A!_tM`JxKO8bnD~1*q76TblL0%1_AYQUz50riz zH=G%nNVPoc0jOFL*?F2c^*T`ngRHyl9-v%ff8_*Rw?LGNpi2VO1pe$E`qcx{3t{Y0 zSy&k`54t$<2W8~Q%=yg^rooKU9k)0CTb|06tN^CM51t!udIlLC%Wh!={Bo4R3hM+$ z_`u||z(m6~_^sEAwXlE8oq;d-tLS2aKl-o9$Oi0OTNPz%QHFjkL`4(qT+^h>zm3a{ zIr-9Yw_rOznoguw~XzmgWDN8U9 z>f&!|avw4*5E&_+!sst54StSS8BbC z!65d`7Ej>isPk7Lj>;WD$J&uIinPJZ_+_@c8QiU37|{i)%og;n_m>eb$cBjS7WCs` ztI-QEM?$70YO21F(mPd0?+C$Nkw07q(lag=yj)j-3qfnS2l!lq3^cJz=-m^KPei1D z+YAdtMP5o2xh7@ZQaSS7vlv+AxEj{cXap!kc@d0J9*_N@t+X9l@1;)xftDYDzx5_i zti>z}hXxm_&RgE*FsT;ilnV2L1h@1yOIHGVKr90 z=771qV&DTQT{@{a5Tj|hwURjg#;$$ynuUv_%&-43z>r+>DD zPR47BRTMtB+Vnb0o;e6#xJY?L!WK{~KIg)4c(YPk5(7IV>@w*wilkFy;E&QRlNt0| z*@JE|`{OX0kHb3(^-+lwQ5m-3<;k7d%svV~V(W4+aybL5I?_o-S1_kpG1r{94EzyM z#xMdWvO`~br$se(zbwV;9_%hm&KK?Q)axQlk&C*FRIpONStv=-ir@HB!-r_lf8*ub zUTVBux}|z_o5l;d)|13=i~d=)$$m{X3b;3V9ds&!1GVUhvvz)JcZ|3FS9FJIf+)WE zymt4g(YOBTCe*;KydE_2AK^qbIcH7Gg;YaU|JlyC-N`L`tn=jY%RkmMw92z2N~KgB)+WB>2J-$4g%+g)E}o%uLfr`EN|h z$ZY`_DabEu*)k;?mW-vh$^POW85pq!715li#p40vBgWLJUgvS?C2IQ zq`D4;wvcA(O>`YOj)gngC1?te5aUf6A6i*l23q(5oj9hd^HR-TG$iee-RoYadeK z7cDo7MQH7@oitmil99gV{JD>pOIELj#h)*fk3T7yi&DtXNAeV^?JK+Qn9bhkRMH-_Ln`@)>_oDY$uv-+caqXk)Q8FVGCH zd&4bat6zsEE(jW|_cy8P$pmc$*M2=FZXBhFrTAzn6v8_x<-ETKIJR8A9_-!^c9*xX zY;i-TF|9lc=MrEmJjoSo%u35aPD^N2DyWvw`<+n&$WEB-;v?JnoB^l%5+thbOZGdX z`;>18q*Y-4X4#1!*K}8~n0k3IAE-Hp%EU>RokV_yPO>T*B}j@;(Dtw#vXdViNBFym z5ZSJ>Q$EO^uZQP&q|9z@y_u;Q+9qk*ayn%Xn2$Bp4jTQimt84_lMG@_BOU;=fR00l zuC7l}%UuieNr3X!R9myMA;`NWY{>6g+HW(a{4*?xRP%__u%&zi12queb{~zR36^;9-TRC;e_BEQ0!G?z3n&KTBQ5z8q zUz)lZLbK-|)t{zr5zTeNUm<~(^zpgb)*SulTgJY!8E5ps&4k{t4_;z~Wu{q}h4s(s zPXs72ww6_epwMziD1-{9oL&xUZCfq7;?<;gG4xfe!Q?UYh7R$+Dwo0PBKXpsUM=N5 z^*7_HkdSj7S>$KDPjR4^&3eV4s9JX*Tga;+_mwZGR}Gb1679)h>Y48zz%etM%q}P0 zKWAxQ)N)S|``c8KnIwXa>}ZlnUT|DRXELA^u|pOxk|%T`YRW4Vfh4kHHBB#8N;VHJ zwe;PM(d>A(E5aM9VA#MqssfGsm9z>Zq4aK(osxO7rWmer9{GU7Lkgz9vDnN-(C&(e zxgN8Sp+?LMGIm(k;f3jzg+S6xeH_l4k3Q$_KnFL!1}Ve2Kv|`*>HgDHp`xmlAsPDS zLR3de>J%FZ%r_B^b$uF3$ycHh!@L?FOKNwJw_@&Bo(YDA7@~`Tht=NYzGjSgvpBWh zIBeJ0;j8hGyUoKEKdOP*Ty${#kNgA0KML|}z$gY;M5Mv+2K%lpIz06+7Da*RADi%t zRZhIGS&j-LA>&`bRuJdPvyg|>EX@1N&y{~H*nYb z61Q3CN?ZXI`_b>Z6~BS=`PFL#?$_tZX;wD30)BU0dJ<$CxET+63-G6A2SefDPHop@ z$-(z!bLITeCs4i>wz%?9?uSAlt}|(InsquB42MrcBtdUzrPKZ&7m~_rfOIlrP1}oP zvD~=YsQ_I{+$!V1+(&L5iYQ|m^d?GB#ArTl^NZ9(fW@3R&( z{OOJr&hicB3!s8uY=xfLvMqEjm)`n&oS#IA9B0T4p_HFPY8dQMrOyZSN*b1@`Rbed zRq)XDuIx{I%u%bfKeFu8y#--dAQJ7B1)5CYGv3rd(Rd}*8W?KL2(Ry9;BE!4h~60N zrO}V@JL-4XM50+o_yls0ML;Txp~Y znoeg`OTp#@EurUAN?>~ckWi`Qh6UM0?d4e-U4Ya0(;NcS z6M`Nb(sKK(AY5QiiA4dYC2ShT`Z!5GoyTG3xXWN8n5ufVJA=?R0ssX5$Z<-tj4Odu zwDbL=bS*lnS))Hm6ME9y^sU`11jh*-z*|1&AU3q5))++Evp)>>H|zNc{$+ zcNO(-HLlTS@S9C}Xj;vw^TQtZlxzqlSJYBtI=Yk( z?n@jl0)2*(+~Za=2jM(_WBCMsymADl3nPWSZ4B1uE(Thv)wmGQn#8xawv*`&5pv z9QzbAN~c14^ZW`7)4uG)l6bYZoz*&V=cW`*B6 zL>qa!9p3>lWK$L>1K#)PdmhIWOROGz+h~+JUVYiAV9K7sVGhxs6-)B=LIU2ytTso? z!3&Z^2F?7!`6RWwS2FarSPeKI(h?ck+&9YD>qHt&HZWYc^-V!$1sgCY*D|W1g=mQ0 zU^N=7>MgfM%SnXLNT+`{!t1nr$-jL2U}!yvD)aiC^=k%ExZki_gP_VrTY%1Ebcziu zi2noYx2KDRmV9E?;do|i=wLiPVkYtCD@ZzcHfsmhI}M{PMzzr z#iW#yV=UrdLb1*A1lVib(k}+g3tzyCS@M<{>a{HCyjHQT+09Uhoji)Tg;=65|3kdd zTmdl_$B6YJ+S;l`2Kr0v7}@gh58=sRd(qg~G`+oQM=-EHL~g}nqwTT&#CRb!C@KIf z$wFc!1W%s+b2nnVM9sEE6Vu3qX_2SEjg6D-!o&gS#bzT*YO($)CQ6b&&c>6&4fJ+6 zZkE}BL{ixM05{KR{X`}SE!unBh(aNy<~EqaqEWx9 z=NkO5ox+@a#$Q8#&@{}UbxysslX0<|pynsDa?j1c6jOIv{E3#zqVFdc*KFi|6)=Xs z@V{{65?^6&L&a0Ncz5CWZm`8*gJ9BYvay+Nt8Q6Pb3`vBAAsf_@a2X!3IiDw^jG+6 zYdRZ6qXnxa=&agCMcsufo_9_M5X5j%FZO^$6C^pO`E(bk(3UCHh722@)KSK>_I+uzfMcw(*o1ybMHx7A}<|xGyvy&*@ zFY$J9?3`S6qH*9v<`I`{F`tkvd_I*7(V&^yn{Wa6OL7!8O=jc^=*&?|ZbJ2-i#>8DgkiS1-5X$$D!dDW zo-fAV$N!Lt(Gbs>-IhLtVzy)g>XW`Ahn`wVper_Xz5K=L;#7myNuBOGMIHS`ocii{ z`fd8PSBjSyt&x+MZYMsj-pMF}(qO)xCDhLY6@nj-4@~n0!PK#z++(fRncg25)uvd= zouypXxo+u$No48Hhe=?^z6qmtlUp`uijb^TMbqwAe>c*Yr^v&gv<+m@JPd%d9VYCnwxl+khQV1ugbofs@3a-cy%mzcJ zgT`znJB$Qmn@<#5)m222djra5%rh#v10Swd#eM9}N{ za%P9-lzPtxF=1dwPJdg`C)kENJTP8P?v0PO;|g!X_37?@>=8sK#=}lw2sjKap*v2p`cIAZn!mRvn6Hovak7mr*Pnyt3Gk2J)VNoxZ0j$|8LV* z&y@4d1G~TJNd7=QFQ-wO6pG(UHH#mi-n9z8Z2o-UDm7R0_h4y1qtoYrD>x0k*ZmSL zl@vb(KVM+Z{jo9*<=sX6553&(c^mGy!0jKA$j3zdT^Vp27OQ^D%wUbM%G2GM;b= z@v!Y9mm_V}1Zg}|&qC}m7}NitRlCISYROuhnt z;x;*|o^sQTn|BSiawQx(YQ~vN`^0}x^{o3WIVF`&dFu3}PsyhVFg~kqp^79dfGoss z<%FrTUfW}*6w^xMQ`DzS=Usu7WLGiXcQC}rpVI5`@V`Uzs;^Sv11AZVmVF$N;xkrs4PfSGjab>|2$ zzo+AwSAI1f?#qeW{(mIpcZ%%_(rH zC5dAZ&hen8AQ;H9D#Tu#ymJE-mEwLl?S-UdYt5PSRAF{3zgflmiEKiZpt+CX8irW% zO&LnRpMI^Ol}Wr+oN8`>T<7}LRJ-9eKD>VOSwLz0DTEpv5h)`}emV_1lxvN=Kl^D9 zpK9JCv-N3^t`n-bjUE%ZaDn#+VsZ;F zjpE7WDMqOB9HR1hhg^ttE!++tvY##@4&D$fvBwt)QYU$U1@_A9c^V{l`IY~%&kTT{ z=xfp{1k@o>%z7iZV)`Huln;MpSbx|;!fi_w_B$uW{!CJA;(UN`E3BI5mm{a(vrZnu ztP9$o#wQj?@QL&y@}BZEuiicw-|iRM+GuU9* zRDu=}!=r>3ZK73x$Y}ix=}!yR>Lx*?AU-chZ7?M|)kf2yL#Ueu(SrVb=+#6c zq(}Ig>?E@U3Afhht_`L=&E)OO1Oew|RR&J$kR~7f6g9eT9!^=Lg1A1GCJ#vX&%wU$ zZ?btTbv_EqCZF!wVd5#94<-khJ5MN!gjE~~m!2zwJUjWui%@YHZ0FOrWM2urj`o*_ zu$VSv$*32VuyxoTtM6q&tUvls92??v=pG~mgU6TIgHY;Kv8B%;*ZQmpe};L&5%vN1 zh~#v)yLk&D$N|7qa#y|#Z9@j56L=iEK`r=lmaYU8eU6s0by6Y-KUtyXQQN&O7msr! z+vIk0qL|Y{GN2K86k!)qAs*!Nc*k>A+d7hp&2VjueCtv;?tbN$u4MC^$I_c)&rOa* z^N9ALyAFfn6RrwHupj_sxqQn*+?xHs%sMs%!w>VkV4Yb8_5CA8D+1$ zcmRecu7#r}#y2g9e14~e`%=&|B~@x9n1~6{1>SrAVta zU(7!Xk-HH~A;41pl;VFs_0`ER$W*MMAN4NthufVn1{)%Zy*ur!*}}8S`flcTsNPiw z7%+_1vpJR;ak?l$gzF8F46cTM+)%rI-q|~%2{~JLi~U9p<*#;?H+R$N?RHZ0_yvP5{=`gNV}A=nsQ)_Idvx)L zbMt@<-bnL7@*s!rO)FQxGd98x?tHO#y7@g5`Bp0Pd(5?MdfNTr6WBRy=L9+S?Se7b zFZfwLWJb2+cs^8ZgumtYJcQ>JSmdBa84B~K&Y%69j9%!Rgkthtjx`o_%cOxziH;HN z4Z2{^$(!}k=Ei-CuKKtJuU+=FPv+4gTnn%nKxup_-kUl_)Sp;wXn7e-ruvV)Ns~-+ z4U?&H$0!c%wZT#WcMAco5`hs)S7_-a30)*(>3B0w`{D^#SPR;Vl~~;y{w49AS|qy$ z>r|@TZd|4W|BD#ngx2ye+MU(@6b6qf9#CbO=%jZ+dYU3nW!-Np&UViyn_w@^(Kn&k zI2j|gx^G}kPF{i@F0C=O&K$C|s&WSWaQlXwk(I+NQIC8Rc52RH2<)&uPY=^)+Q{1og_ECae? z2Yi^=I&kv5!J#+MuJ-BnFSeB<=sbI(Z?SQbgu?+Lu$CzA*qdA+xUa{SVOy`w*Fh_d+(p5L{9W|6@3rq2!yPaQfB(kZ^-y@n9JE;_0q zv0=R)hrRAh)~Jhs8!mdvc?O!tz&{jI>fdF5YP9pzZy9wvL~TJwBNr`-YUlb;>ZqOT z-B&hI5kpyj*I+ZlGb6N!Y}9q62J=%tI|hz2q7$P-6rCfzGs~MzQKQ*GytjJO-%?70 z$sB{Jv(1+GL)Q}2v}0-Bk9v3w%zgi0BZI&xGz9!UI;l*053`S~r38>oZBHlHM1q&R zx1|{#AWu}Ur0|wC8|*rnO;|v!FqQ?$Q)l?%qVBfII>kv|PIdJijQ%R}VXs_pViDFe ziTjK=$vT~wbt4MJX(${azG2WiQPJYAt(@OsAxmeYzD6r!(fenAV`3-|2HkFs;K^ex zj5>mlCdXjG2;4$jJS3x>dscrXvN66yVcHgg z80`Ckpf{Pk0fwz*eIZLd;nG#Oi*AoUJS69>)=OTp|6Q`(FSlW)4i3OZFhUG`mUVP=y?K1-jcTHMUNJeTZ8r_{8>Xrzllh43GxmLAsd- zQG3zsl;DqXoT!u@FG6eWk72 zouD_t5>iH%5J{D=_pU*(5;o4dSgP=$Bs-{nJ?;An=?r5eap+<2v)ZHzq!}s!Z(_hD zS?g^^tVR1odbc&+(DF-LU9CA;x4QGC3x8SluRE=Z?Nt9OngKaqC(YlcP$&8MU1FBl zHAJ(T@GJkHNWm($GGF;$7;dRkU9@Yv0di2+ZaM=I#!=irSsPUUzv6~}@3{u}>NUDU zliXIvUbHA^M7PRAAS>yapAW=|?}P_h?|4Yv3grD*%orw5`EoU0kjpQ>*9jBrMKvdj z-JKGR3**rXU@sL<^S9f!ENXRg1)Tmc!t$77;`p9&o138}Lyqwx+zj%f}To1%l)prvZ#6tdDtr+Ye`M2dK8 z9B;O3=-%civc97MBkvJppwFw<^Xp)AslVD2av??(ApiX!_MsW|ROXwn@G1 zCWd+A+r-jn$gS;Dz>GpQW2~jy4uaZ?cOb-1+|vTP40H43&zj(yN5iP9cB$-LtK>2G z&`j=6t#0A={nqwX(Lxf(5}fE#87ePv5?QtJ*TAn4+1W#g2Jd5o{(mbXbk?xWk?6HI zks^_;9Xr)Mi$`qj+N1eHqNzX}0@CbWI|;qaa_bagF#z|$i+0j>X9j*r>{o07uAk2?;Rz^j(CPc-nUynNb6h5^|`bc z?zuz|$t54KMI^0o$xn0FGt?_s={Z_BvA6XGuIK6weIr;S46bRr zn_^vvgG=`l5*q{>p63Uvs@QSuzlNgNLEiZ_ zyZ*>2gGTem{#|VC`h=Hte|5vwrP}S|4Z9lZCPo*dX}2|l4fWs)x?T7U^(-4pX`(59 zIrWm{t$~3Xxuvw?VkzD#y9hAP*=k}z`xp^_vf7kOZaLsT-s5eySR?zTqJ~#0wU{&) z10B6Unv@Pk7<8G*a{eTjdC++P>WBAOVxJg|%3cc9x?Fg;gCdCcG$I4kKC-qe30jo` zgWy~G+*|qcgE*KB`M9yj7bW(Cl#Kf`kf)nIbGMX?5 z=$RdaBf9t8Z%hYb`tERNOvqAWaWc6>gm+b2Wtt7RU{bIE4$#dp1auuL)|b36!;2Tp zH`Ue$20Ct2OS5Idhhb;Q0osJVN^2ja?O1LZ4$?)Y5AlVuARwo0ZWp->inGvmg=6-Z zoM!q74fFb1l;Ji0z_vwwkc?Wou!3$f7Q#zhgzd=b{8BM>?e!^RU!%}yZI8NK@h`X> ziahSkNWv*lq^EwCAuYgwPOpC(PP<;cGOQjoYH3m@s&A~Ux!0tS z!2!+h_QZQJ%Q?K)r0zua@e*6?G#ER{HX#i08RjADb{iGjEX(}1l@7+`7vcU_B){zp z%+7uA4A`EKwR!0tdI<&glkL1_@UbQ!g;$9kE?5!ri6Kcl?fq1R!j`PqI6K-ymb)o#;U zurUx`up!)W6E01@jFw{9bRk7ouY;IB4m?FqjhpXtKK2_X1;c|e`_Xs*&a{mbcNZ{i zi=v#3vynU?$Eop!)ao`VG&zoe(aB1#q?PrfPnW=E`aR_#n(Q9aFJ&8?zlUSMX7XClaCbVvhC6 zW}8jaUa*J}GT^%3r8c77X~cS-47K+HMH-u+jAwe#%YI!TA^qs%n}Mu)@1~`9XG!~q z7)r1h4{CTHOMNjk5~LUCzVsGYp9?YF(+4qML%GR%jx1gWzk0UYmI8O%lH1k^W+dy= zpG zY^Dx0?QsZ%d~Md48RoDgoViPTp*zjTi{fA~hiI^omB6IX7af4dc(=y>aOXjD4*1iC z{o(30I)^5Cp%E;oen`K_SY48C*|1*lX1%mmq}qE=!39wC4ybnFzWFZH#8zXt$>7#g z-ypIH>5%HTA#KUPUL;D^--sI2L{p3X;Q#mXR$DNrY^Br}uDqAB24XzPvqASPb=I-m za?WwgYrTN%HCb+++eW?|gi7v^;%jhDCb&PZdjb9VDmF73ju}ln@*%F%%N@^e!h{zO zY59Yx@&h*gy_8pF&c7_%iO`ifH*>_6zHr%(9 z6|)!;yVrz$#4KX=pktcjkhL7izNNq z)Gxe8>aXFC;_ zYsU1RagcNN`eFGm`5<4|wMb4jO-_KK553-7NG4S60^h$je88xlZvZ*2&=lMUdCZC^ zu07SR(Kq2%!^~LIQqOvgN;-^JThwJhQ8sHtY`|%+t-3sruanj6weLMIjpzN8f(2cC zCLV$2fgs4<>3^@C8u1Q|rs;o1sGao2|MHu4w8zzy2Cph(sKd-(XPk&1-DyahRU~=V zDz1EL%SGt^?>lKJWxbbNTL)RZWfny#tcP5ucJhex%4gct8a9bLpJP{9l58Z{{ zod*YXs$i@-T{IW+wZMprB&R*;a;?)+rqF834+k+4RAajH8F;i^{7Ggv<0Vq^@k_gs zFPQl~PmIpG2SH>*@3U~y_F|Vfvd(d>u~4$)J$_Gw;!l%&Fj7CJJ>iE8=GUgfX1O<> ztUc%ju&7l@066;EO+WO<GQcGVNF%-AM zKF&{8=CBm8&6Qo{`OMYtZSf9~V8?kqOTS&~CLbp&lg#&n5XWciT}1f32A3jJ- z(k9z>#^dH2haMelrv36Q-O@V0A6b1d3;g* z1>{Mh$w%KY5@$@}%=S2{KM@QHCQ~jbB~fu``3s5z#E#r5IgpMghym;oc1#dwv%tu~ zDvPgR7PK325Vm%g2C|Cv_KRRsIb5F)Ya?OHOy(pE^kLdBFx{=?v*TayYb|h@8Uf=M z?t=U5!K{?^{Vk+-2z>;!tz=~c6j{z(1fD%450|6+jfX2@t~PsM zg|?mwS+ylgpLaIG#I^-!nAuDdb_pzIb~9vXT07|@z+`1llo%>nJMaQ#YTL*kHJ04? z&U2u33RSZRo+u-mNiW`py?|jI#K?Ztbi}c2eh~13=aoS3gkJr~0DFX`53@1qnP!9` z%gw9$kmJ+O^(7HAl~!BEoGw7S1vnOJ8cbOgT!}(CVgGrWoKwC?CU6xeoxx9xn^s8f1pJOpQMev64hw$fut9yN6^(E>g_2U_fK>G8RjuGWH#9-9Ch9Vd{hgW z_y$jUgD$m3{zmScdSi7e|3*?YL(HP3zXPx9u#0Z70lSD*-;QJSd+YK~eI^FvpF=u2 zRXU|frTFdCNjaua9^`tqdziF(5yiGi%fB*KWVGuE^QUh6H$!X`tn`A# z@FErp&?lz9kMx$U|A@-=d?rH5t$-7zvg@-6zZ4&EeYPxX>N#1?ui4>nMACsZ^(hA1 z4>aE_caO%i)o@#~A_L6L3cfu{%Mb4c$1|reg3fexws5zKPGfEJ+U<`(ur|w_nI3JH zo`da7Qp;uju!j(QFNPFb$^HBUEoCiO8W-7zv>~nfM|QF$!pq>>JMwG)^8S`YIFgl8 zGgdD zF%`>ZY7oD_N6Q!AS1pBQX)t)#T{~QM;t-_U z$)d*PwGMR-ZHBtm_1UA?2?+<_>h$;susv>>*9NCqRWNq>=LJj+ zu2vxGtO+sOoS|kzbRDKfEb z;zegfqJ?b>DqsG-@L~JsKu@Z+X->zPZQTZ*ZH-1RAtxcP13F95dEYtHp~Fu?h)Fsg zvgfNw)fjT9C5}*>rjdycMxoYq33d5bfA0+!fZq90_269+3BAW=Io6(-@3E24CqL`J z(#W;S51K0$@p)ayvwbjIB5*AC;3Q=CO7E309|Gkp{uC-?o(1SEcGDrQM7VXWSP6^z zeM^L2CuuB~yBUEC1V5}rtaQ7*(W{>&9%|A#yayfpbJ|=9ebWcK##6$VA%D{AZh(=5 zehQd`S30HOH}Y!89zXDS&yEiIUTE^g&z)~7-EK+y^(g7J3?04$+B!TVl1jP5MR2&* zfi9YRmy)2?Hm<$<6)d10nPnlNw@<;B-D9J^unMX<)@tdm72I=FJ0p8b1;(6`@J*D} zHFU5`!s$u;ddJ+bn*BRm{nht;h)VEEZsfrDdX#S6KXa{ltTC-Ir4F>WiKeDlu8Cm= zo5UK#Xf=yL_<0n(b#~7Ha*x`3B#_%v54bRx-wrM;)0LYLA%_P??{t3wqgimfOp`=zlL z%-PdJ@J}Q>e;!jz=dp*Z2}04tz9+d{0A>HZ<5bFV>zi+01q|ZfAELPpZPE~>D6+*i z%iJe`5HP8X3EEkA4(Gg_$WdBxM3d?s$TiQKds!m6nFyTlzShl74cH|up55r(pt&v_e+~T6#mH|&6YHeIL^V2@ZZF+(?Xqb+qTXIy=Xaq$ zP`{=RLcoU`FfrFIYF+JHzbvL+HP)(hHKhh+F-_?WX!`1rUQJI|(yiz!-nHe1PWKJ! zXrZdV@mxY_f+>DM)2sU4*eYR3uR`Bpe|`&Oah=-SHBIhXFlb{-nR`dm!9*N*}X#gk7ioyI}Rm&ndX^ecCa!OAj8h-sA|#Q`CznnhUb zhN+)rTH`jGPKq79JDg%+nYDHW>+@?}?adfhB*6U* zm=n9`a-=Q~zE!!FNnicjfHn21cY;1>vA1TIo;{=OWPZ1bUo5gqSA*s$U}E5=q=9}y z&IY!%w;U+ey)msZ<<*}5wscXw7Bhj=J%(J6Ld z67%DB+M=-KjeWSmUSN8n6PRxg2}|$iC8?jQ+lF#`;3_NR%Xy%UhUGO+wd%2R z-NOH;_8}dZmsc5aI(F(@w;EpWj@igk(@S))7*UL-Ra-wJ1mE{S;y<{H~d5nCU2RPqKhv84N!PgUFdIe%e9AiIIa+Fx|kzAyN zPtsK5NhzvB^|%$Vg3S45nbl!co7$1NOa~R{KyOlABWAFr@8fw1)=KgdUvC94`V^$HT6#s6kvtu^fcrx;&Wxit^epopZA#D-Q ze+@3Hd^4kJ_H*^@5e59Re>d4h-626Yn7(ci))jOmgdS0^qVFWr)KR?an~oFdS4;Xo zTIGd*R)1r6k7&vXf0N+d+sy!Y_HCX;fGMG)qyr<^y>v#eOfWhL=?lPRft}H$xmS$8 zu<@p&i1a}KVC(?vJ7Pg|#9KY4AOi+UH+49|_C2P!NO1$T?K!x+Cn+5c63LWHtyfwc zqJ61N`#^ee(1PX^JSJZh%AO65)#OTqjG^d*tUjQUCSjTlABvq4$UJQ(rMD3ePC}o; z>#|_@A`;NkUy9-T0j3u1g)qOPoIAcIX^kz?So9!caW};-xP^MF)?q5>ds5P~dn#u_ zdl0G3Lzi{5v_Oe?06lk6=iIk(0PIjp=>ImSbI^X3FX2u# zpM(MWNuAVf1BjwRw$!dUy-3KW?V|ofk#25cv;b9=mqLrAeTs3Vm5J555xZ0tG=B6r zFZ3f!0~;d;muL8e#be>QYgxx)^6%%g5R=07C_S$isRm`~YZ~gr>uU-k(zkYxu8C!j z6tg1KB9hngt~gYI=uHf6)Qu8I3>i9X_=u73>pxAurajo#lj=(~!M+V*dqu8*bbt6w zhd1n3R~HKS|NqrSEy^}X)Ze8=*r(nI@frqsUGr=Brmndm1J!bm78pUmKfs%tx{W%K z(yXKS1k4h9RP`}?k%WKk2~`*<|7apcrwww${KW^f!Ebz|M3&uZ?>sQOWAeSsdmgF& zXk7sOj#2wUzPbYwwub?_!wml2H5kD@J2~~b?I=4fU(`d+KQ@xxF&SL45ti)2{IE_J zSHRI}l#Q5OJO7k9!^MGRG7@4DI;{gH*}`%8*>^!SPM=pnMBs%4%qKKDA< z9bo!AcgN9xos}ei?Y6FO*nU8d>OHfF=@DXOy2gv#9}w}`Q{4XX{$*GyiIB0fTPAMz zem!A;X)@O9LqW`}c~5a3%=H*Vx^mHqy0jb?9!d zI?8V2_QsStqqxp?E8a_-Dhh(D-&Mbcf~64wg^3ugp>?uBA0A=8MbCOApeX|WyrD&1 zOMmU+u3zu<%e=1ib_?3zvejF^dZw5Ll?K=*|2Pa<|LcWX{pU7Ss+$14!s}*3r@c;; zX$uV}rvAuUH#1x#(3b{NqpdcWA56ngF9#1$ntc@J`+3Nh!fFZPZG2pix zuV2p_?T7QN<*Q`mw_e#~lVc^FVdYf9lv9ZvyT+nY(?1YOPy%9%Z#LOA^*rM(E&Qyc z8>11r2y!V9G=CM_lrRY{5AVai?tK#aANl;aIl98y=8fBrxosiiS#nR#*G7!4?y<<%9i(5O5WWQUp)-b|+b{g-eWF-g!G#1OZ^ z>-wU?X629BF9Lx(3sKJeg{@$|wv(B073(jVbq|fH-L$_IN{w=U8Vbw_>Jh#3W0I5$y^HOu zx(Zdfc1(rMv-yB2z@LzN#>1S$eiB*`6k4^98FtcG!xsg^X4Hu9W&`txo`ojbhaygd zwQIeBY-kw9q2H`u)4#(I^LA;nec*9c!m9wQtv z-TI|D;d0bOI63SGqs}>WvDBSjNn6~QM$T-<3$~fna_J%J3T{e2BV<;xBbkO2waF{e zRYmf0P~S|3itfQ|pBzY<`+tdh@3<(E=YROAp0KmCi5!-ofPfi6&m7OJr`{RP3wqHz zJ;MbrXQE^g5J3=8QN#c$0*Yb;5kyR=h=>Sc78Ah$>aS*DVS!yl@AG{<{vp%VH9a%E z)z#hARquN2P>L|agPio~otcnZ0v8s2qZcWo^CbF%BB-x12j4xi)N;2-EtKlNtUKjO zgpEj!*)3XMg&-0~u!Ymqd5|5JJ@!H=B&|bQGz}<2#thQlzuKI{iGn#6t_8RDE&b;JI#uB;xh&c zm}RFs-Nvcgs=mn_c331Y2%eDkVzD50Sbp43RT2!G@kDha5?3r1h{6s`FOZAm{UC`M zWaH19=JvmVoBO#FzdKdguy;O}702@r6haI|s}pN?qQnTWy4SXaH~*DeNTF7@s#o3d z-|Ds8t%al|DiqHQ`87=jaWz~N!q|n0MAI{A%*blGU@g~6QBFTG*aT}Bl5z`;*8Ph+ zZUga`OTk@q-%|G657oivpVXtmnqTF zjwD)y@yJhUE~sOsL9g}caP2UtZpk$ub{jFWISc(5mgqme4~W#W;ZqFvy#&=!AS&sK zEn*9@(uGl91Q-wuy1Lj)6xS)2AqaJ|Adc+Qi|Q*b89zdNdK6u{FaR~0m6u&~|4~BP z*^^vkPwxm=FBQ3a!3gnFHyB8}B5K{BWWB6Q;PHVd^bh%nQ(dA2&j{fry|cV{gV;Rn zmXM92bm0zVdRZJrwTHbWu&1HNh`@_)lJj{T!g35;KKC3tz8)a> zL5;K#PEX}`5iB1aGWPTqFUTI{8i{#?-Xdo@lI%=4IF)#QOO85{6Upe}>-F|?49Y!^XX@Nvm+=REMZ4bwP|wXF()JL8^A*u`9&6e1^+#{aRJelC0V z2Qhka6HElw^Y}W}n4-(%^9M468@Lu*4#KOMeH%Ibu)QRxJ68Ss`Q?{z!yEp^`>tCY zUJBKHBiN)oYP)Bx#Xt-6Sniubb3b z{67hTyv&9rz@MF~X7Pvq_bDb{OK~+GzNu__x^C4%*Fw@hlVg~(7I18>w`(B^lZ;k1 z>Oqv~AN9Pw+EW{!T87@_Z!@`SDzuU?-ICO1jW(HTElnb=r)bL|Nh0MhPL;&SW5H`P zu!YuF;K^z@Hq2O3VktFR>r`bVd5RffjU|b=H9d-o_-IiR#crg#r z{2kJJM)7q}Vzdsoto;)mD?2>-9Q}$D9i_EaOXM45W4j!O7|C9y$3;3yPrCLQSUiy& zQqJqB^d5uAbI-u|(lo^FS}{>-j2!qL(kQ*`AA~ceU1TaN$)G0T(a1jrMORSMR zi5KrhwhWuhN9i5(0jx1{?~{sb1$xqs{X#3jM7d>Hta4bFyHoA2dk`k?EIge%VSmrm zFAEkk#+Rr{k;5fYDUx={aZuGS`NQ$I^d6NRj&6ViC*x`(tgjPoUJTpWx!H;sGVfC7 z&Apce!!3hhe2{IHOM$&o_+-_SDR9e!^NG;mmUEwrT^;yky{bk zcebJJHZ3%5j_6jac6n(`A9V&Qk z`>H*6~3IJD#hD$fjiEg-ak` zaQ;t2;e{}nJ-$;YY#DZsOCC)7idC>4Q?_wDCv}En<>Y6d-vbchGQYg(RX}khrtc)yKMZ9nc{qfydEhmeIX|i5(^G`lzHnT14e@N2bR@RVzim->(^4QOeY=hjqu3gV zE`l@YFPx7K!f|LOoG3Y_NSSGIos_(Df7p@{C)WVuF#}(X+%PDz zdiUFgUUk6_^uGxN6FWEdG-g8Tul^LXZps|H{8;D^J_0`xsZ2A{U0pPCD%1J1-_#}Z383Vg+v&t+r>yabP&SL zJSkFRN?KO=^(1GyVwLMsIDSt!fbWI(S4zcn%~v-F?@G4wMCu)}X{ zR{H6yg*`hoOv`>_-rvQqk_(>a+I{k$fD#Xb9_gBU+C$elsqA$cz=PfG(V7s^+HT*Q=_PZUykkCD6)O}L^;nlqY zir#VvwUHDcqRMqJbLdjytO#aqFN54am;z~x{1$|DFHZv#e5%WZk((*S^A06YA@%4Z zL5kv%!BVn%BXp1st~Jezg(oY)SoJPX@l3HyYLsB>{S12em_0O8QFqF1^Pa=#m!>X` zGnXv`NM*+(~5&>^lSUz&>7| zmGkjX|4v6YZ{=63bdl-}lfKPwzgQaFpnHq|& zp_tplS3{)jfh~Hc%x1l}IkuzO#f)+bXe{AKY zh|tUDVwXZHzlT%VcX+kHD_Gv| zML!+)@^^Wz6w!YUqeT6MtS$-PJnWpp*;(D{@XYe~h=|X<(TVvmQ~bV|KJrDd#Wzz% z1v)+&T0&pXfDNPM+?4{5TlkQ1Gft5+azjjK_VP!@ z+Xh=F&U$n$Awi5 z{Q8h}?S%^-?y;QsnOxNy7@rJ8TsD4_WhWx8n3sDM+&}onBKMsca-01uKE+zL>po2Q zy~6%_AWXQnka_k2eUqLce=Ju)Zwl$BwA#vF2OwBe;QqsP8?xGl5NcNG*t<86U~Jch z2APEEu(jXgAyDoIes8n+@U?WpYcXpawX|_x8&wus>I9vneMzhSI8BA~T0dwTKVh3y znm=NTYn{I-R%rPt`E#VnR+2-MS%lBP(OC1o7)gbABZT5kCJoiv8}ub9gyr z<(IKgq1ZC#h~x^ek|=h@*6zDkuE#7uz%q4dXCC~bu#$%PGBX`64eQ+xUoAVC_MQt2{Y37qiBR4oR=i&e%8|IBC z;7f0nE{Cm%+c!pXK)<9@y0K&vkxn+=orgt-A@U#PAuZ?EfsM1_r=w#Dbp@F#D@(pD#7jbhN zc8pM7RLz&m`7;{Ux-3eWLGcHv{2kpxIytbi5@~5L*X%Q!{g9>sP&v1C{~v05XpQeo&-y+yx-Uhx-Ivh6~;pxbFs+`t6!pYWutg zG0q;l!|9nDJPuINPxgD>BY>+s4NS6uvP-PF`Kr_KY!8GFzH$b|yuZ&hMo}^Js0WmO ze(#%jU+A!h9LM;6rP0$#j$Z82euX1e*o6+oM`k%}vJ4%nD1CcXIlKr1KYjE}y~~Gw zn}$hwq%S$gb%qr~PZ^R24#pKj_eJ2WdiJklHz4etoD{6D=qI@yEX4U$cki1nS03Zj z9qtV>Qw^MFn2^ZK*8Mc>n|JG*)WK^|Tk!wc*BNzcJ5*_#T2gyu3w`;@Vp|HezD|o; z)gU$5`ZP_~Jc8Y|(hr58#7Q*2*<`6$Fpwdu8e7(OpxYF++E~M$PS} zJ!J_|ULt%Uv3l|+d5#&ami6Wy?S?Z3qGaQJj7`85$dirzIWqh#6rq>~b*-8r9~dw+ z5?oG`V_#1xGfSRP3oDEsLKsOlJ~`w7eGfhOCej&CVBgQ*Ea^_FAe35WaNrOu`>h09 zx54!u@@$UUZ5UsT>W6n zPX#6&-zFUKn$oP!k9z3H6?g*X)vcMnqSJ`ur(s1WAM+$*va}r1??5LJ0!Ec_Ud`V&M^kOn6))i*>eync zN1x_uPRn&$TItO-j&3cZp-Qw~HtCm^S55k+r-;8YyJ6Z@_DwM3FUo}lN^2QWgZ^lC zSaS`eyQV$qbF^6s{x>bYvK1W+Eyx0vb&xp+Ra(HeRz%yC}7r+Qzh| zUDTS(7QaqoQMQ6ohEiy=+5_vvA8loaon@lwe&Z8v!@W^g?4@U!z2=ko&>0SV@UN?8 zZR~g--54*p!WC*Ok$V4=4-O$?4Q_A8wHzPc(>{VYB9U5@K=Qx?{g0#qDZP@@M`c zO52j@7k@s?)fwb|Iuv_+x*5zCH}29y9YSoJniqx$zdiJn}4tok$u z6D>Y8RQ|KxdF&s1%^z5$<2vlP;`ww{Dwt>Naq!y#Cad+O8G73&AxKCPNGF<0SI}8{ z7>j9;r8u|`nAv160=xL~d$>n{uCbCklGDAe{e~%Xw#nug9d+=LO8mA!j%+eU_4Kh{ z)h~T$o-=|g9kf#N#jMMD>1qkQX)bI*U|&-j+ri#$(QK2k4Y{qa(k{I%x3&VXTdU=j zdI+}|^3CIp+N{(X;o+hgHLYn0{f|wxal7`a=2RX}Mb)`&?hqx~^skXrYS4iFKZ)rj z7>qNZU}5GC=)^|Tgd4cme9(aPXsMySte%s5D9zVR2$~evY=>zhjxn-aiD)BmWy3|g zM46aI@U2)Q^iBe{lqKDhXqCTH^r+g-gsx!mFGGI5Bx-CbSISl3E%d&@HpH7?69vow z%Oc9qXE-s;FqPe(J2OeXb&@{z8x0ju->hW~n+7SZ)%v%tV5jmBY_>uIxfdBbGuaQ8(iAFPT&YCYMy z5#C?bGD>TR=YnmVH8m6q;kPU81zEJ=1y%0vpYa<2b=TbE4Z4WdVIk2g4y z1J8EB3Wua4c+zYTZu@XbjXQKGheW+f9{sJ*g&BdRNkNY-q5^~Y!QV-io`$XSQ^dw~ zo8kB`6*HejL7ZML8-D{j1WY4OV!=4L7*@NuCP3Z{%-0vOS9vWMGgeZWj=fX5X{_c3uW1uqO@xDR&pa+K`uyrO@$rSu=^ zUUJ0n)bB((uTq8yJ?|O5djGppG-lIVn$NE927C!VY#p26k%~u zznr*38I-{uE{eY66o?@&fpqZ4`Q7XK_;d=8C<@(3>8D4i4_0xK6e^8*RtuXy=q%mI z1lxD1dAEq((n*RB+uMyu`mE6l9ph*e+!ed8N5WT2{zSjD&1smi_APDgsiK+pquUoT z%i~hfa@2qA0_LD~y2-nXNLPL@oBjgfyS<1M?W;YGcKtDEeE0yPGY%^UV&hWZf6~xk z%6n^>lGVdphoaCyZqh>98MITIn>#vbXVOk>achI#Ya@QG&1SSEKtA4^O>)5rC)x^= z%02HKxQJUxlQNR3m%tKnBv)YWfr~{eSMm{p^myor{Ur3R8KRTruwtP2^swVj#2RHa zTpq?7K45SL`+?3f7sYpahHiPke3Xsez4}Qm>)D?zZ_Z!|w~~ArpeG{E=h1L*PJEDg zyznzW2WtV0C~nlWO~APd*h3sT8ta?lgg(e=nV!s*h(lXf4FtZ(YPU&P8Da98k|4|#cOi6qwU)RpeOKXTW&XFx_hVK6tzPLe0#()6+Y zk!MF()76yd0dso_E6agUV*!*p%}|u9y|o09Duh{&h&(9rv>bWzxFNK*f2*^%YF^u*DHk8bn7|#&HUjT ze*RWc(2CI7ZM_{VY9mQotZmh`0{PTr*1T5l{NK=@Sl5)JdxlZh=$L6*k#)^AEhm>( zmK&#<=I)mDC4QBzX<02DnltgMuiw01bgWHU^@~8z)i(nDmHnZ~(AYP4p|yJoH74df zN_mXJ%oHJ1t!c$Xe{dhZoEgJMm~QBq$zTG8u6|so#CEl@SMQ3&lPx0t9gWvp5Z}nJ zBPlTS&O7--dExwv+qre zs#Mn>pn6n%Nx}AhV^--^P=81sOwY=M9g1s13M|rpJw-1E^o1U}wy3eaF*7Bue;$A( z^ubUVUCoA3tkJf=gj}!wvO{zr$?_y&4rvp5&y;Mw34Y8Hx&Y#*y@-JambGqR@@Q!E zC&}zpYS$9jaunSy*_oKm3xlp4vmd|%JWdHb2L~iU$}OWO;Y^uk?f&Os`)}1R`UiQ( zXbF8sZogEDP)&8_B!BD<^jHfQ8|0vl51k%twDPaBsoJpaTTNxAhkW{2V^7j*}dsiBp- zXzDpUWxeY#S^WxEnQnsV;Ry!z!-f^`?)iPNqyJFSH8vOCjhB7+kjd}{lPH`Y-oVGW zL4mZ=BdrLnblYdtj@#&)Lf(Gu@cthibhKsY1Vp6?-UP387Fg}3F7%SWM&8TF> za}$Kn-3tT4Ayu@GN@puzGvq_SJh~dO0o9%SDuLBYN)mECT$L|{ReIO(SYnxB7WnAL zdO(q_)j2#WBhI|>UppKdlP1z)R$3@2)hv5u)zEN+} zk@;{9^M@sPTVqUxD(K*4SHmwUKan7|!E+4g)jl zhic0kzkVXVDAB*k{(RcSE`?Pc0BxI78~Zy`??E+b_16h}4bR6n{Y=r6GLquMsg+Ej z!BHq>y$}mo_PyeyNu|X4<~tkUhHBJQiM3H^^@K z3UC7T!9teaAQ62L3+qHHm>M3uH^Z}k%f^M-)Xw`%97by859k zea#VRpIS!G>-G2vIz`y+{e(GV_W0*=J`EZRFQecZAqece#T#9fE{_*&dMnJxI-XfU zMr%6rmyh?Ev$SJ zH2E6Efif2OHDLT?`UCKEgX<(U4l*u4UjOuHIJdaR!F~rsdKv-}bA6e#5)R#H$M$*uWJ|-Jno=jJ~ z>@a1i#5PR7+p*633t*^4|I}$`Cm#{T-N59Z<|+o%(EYEa@@Sjuvv$#$otw4!4ssM*=DL!&F4d1< zJ4wlcrB3CGFZHT#18K1s!0thO=}n2iyk#CvsU93|cYO)$|6s&j+%EeYWp4mF@9Xmx zwX~V5@)LDFgAQF4>bwUdIp<*)~H80>nW>LOfvFKss1Ylrx>Ee8pW-f}r-h z63h%5X&DV~eJ|SAWiK2C(SfgzJ%n|=an>-uwJ6T|20fg<@^fK_obN5m{$j66H}yG8 zk(}uk^|!e%K?nl#63%npAOgRq6j#xlu*yy3nGJ-;AW* zqig^Be*(Mk#+|0CGyWHJ9lfW$TQsPMP}?*M-l3E?DgGLjGiZZd38|&OOU#AX0nDbM zn`f(Z)~yT}%3qI%F?_wIiKfD-)mvcnUB)q-3X6xTxqgmrBktoEc7t$gZ_oT9P!vCb za660%+dSaGm1C zXu*bWQk}eiUfL@zA762$-oAS6c%yTIIkjy-EuHX5Ev@=ia-(zKW!(=OR~p}Cwln;0 zL^7`b=yDi*USASU9n1v`@ro~mRXsZy$@5hHDgX~M0;eJv<$q4#?lC`a;x{AaA0i1shH(Z_|19D-Tl{*w-SG;rFTET zayeID6n>Y&e-2EQN8tX6_tjznSL=yaYMRywTXmf~x1~s5`IuKSwD&#QZm93yjt3GN z46vRLKHbG{RvBPho!NU5r5oX2v^*o#(RfR;Mp@&sg-e_68f8;BKwC_$?5T}m2(NXQ zR_1ylzqx5`6+}I zL;2buxLyJ|RNfz=>E}3`%=dZ~;s&+~*2vC*Xu7a836tNPfkbqsM8J{fbRoz*=~Kom zjoE+DO7dkg;fh5Chrvl~d4@j2e0={y!5#7{Y%#-=j?2t(^wY7L_3)n{=I0WVFXH%~ z9vFa~s!*iRr8X?>HlJG=2*tneeru;O`RXHaytQKAh!TC4cICNHdZ#fbzG@ypD|QWL zv)922PuOQd3Mnqt3H_4I4(Tf{({&De3=YF)skjOv`GCbq%vE|R(2;x%o5bj!haXnm zhEK>UmP|)jMp8BX47Chi@+;wqXa~7tc?Qu?e>i50KqN1(h9{N45law|b-PH0|g@CwjM)PM-l zUa#X^tNrepI+|@D3x;#8izD@@ZZWazn3h}4v>KDCR?D}F*e>(8rfIwV*V>HWI%&-i z8`K4f>Yre`YQ!m7MJZQOn4_%zly?VB@<3WmCiOX~e9SDpgQKheLxp5*9aLExWJ0Y- z>SgvVnSVk**r=`>j_VE-6!*%TdjMv}$d#8_X;p_c&=)i5Wc$D|)6mcHXt$bV+=qvy z!1PV(ZY&J$L{>m|!kCVlJ59_uBYBMj@)?4@bmWzg?Dsre#CKq9VUx4;p& zYJB{|!h2xwVmn~Uo2k#3t+(O8PyNWPn;;Ev2}51+_72%x-=6n)H}LOzagXrMF;o~@ zM#&K<6LtGh$zx1|j~|42zy)eie$RA?bO!=#-$D4s@=+_qwF(61AFFhmv+)9$NVX0= zt}ne~ELHNm_rhgYGJ4UKp%=Q7K!vpQ1-6{ct$jOkM)B8s$ee)1!lx|X2i2buABp6F za}ArwT0Fuqh^pxrxcz`cDGLpS8V^i3ea8|>f@h$oOCTxj;Lek-k{sg2ddrnn9i(xx z)ygZB!Nw_=lL#>`>B;=@?^16FV;cQbw0mZ#$8|eIxuqPp@Vd|{%i6`b>3F97QrpO> zQ&cr7)PmaLZ@t_~yuO_FOH{+;{l%>>QIX9C$|zitpp9F+UBKJDQzFIL6-juu21v6X@4JR z<|q_A`Gs619_6#Y4J1$4o0 zu<&L|*1}YyBdoH^$zj2iefo$RS`l3d%l#oX9g3%HoA4*UT!icqR>E8-Jufv2#ML72 zqh+6`9_wvtlQt{c<9yt{2HMBv;MiV16&`rwrbiRzmVeRYgj`9K4+vjD)(+BRBM=nv z=M^rv%0D$K(8_eBV+6fxu;EFm$jv&pe;JXj9)(*FL1+Teg^2c-gjk6Xz)9vC0H!RaE)dZTjj13N#Y+(4;3^o(ybMl@ z4D)U1&?#a%O?qmS2RiaCAF2=zy;ACBRh2}o-ZfZAA*agEmSXl4E@grtfX4+lkdHOXL#=>)~j!Gv&hCq)6Xh73sL zoMhqbDPwUoIfFM(eBR%he$3(5pQeb=;_h!mvkvhC?nCEgl-Qq@on%Rb@d;+P*pS}F zt|6?OH8-GY7@cGEu0B1>&H9dLfF&lg+sEe0=-|I=Xdh=c{qi3k;Wp07$2g7FyNJ=S z@LlnZT@6!pOhm_MKKV_Zky}OezL|8q@~&4hG+zU}g8dcM=mV|6@tWI}=95ulN$Xsr zi>P(FX+~@@y|k~WZFd#Hm6jAb!UnN!Ef@H(`_H zW-xTZ;B_)t$>-e%&jFQ&W3yRb=IH|{z%h&lMs|LPnQ?vsPI>{bg(L4|VtDqEWe`PP z9uRQ(JCJbPC^g(#>^773s54f#6_Uz6s)fQM`~T`G+1vc$B%(UZ>@@w>y$|@7ay-X0+cLzevb4 zlqc$_-2B-D#FUg>mtL33?AJI{ez^2Y^49j=Cy9_cDG!7^vk#q1a9^7abFWy0WkOMB z4BB4>Q@ct0``H&8q>P~8_H;8zA&a<}S7u%K{a;B?XN*%{G&+s)U9l(RbQVNxZBD8KsG|~3E$ykNVXy%1j+hH zx{Wa?M$8yhWR`8IrI{oOHLu|7*^D?`)z@>wjTHt|Bj(iC`NK$mpoqAf%?abY@B`sGYf)&l>9;qNV@ zSUQrzj4)4RXQ7%hr0xLje59ZFFT7G$7EPg(~c|W*vh4Pbn>888E0`s+2 z!Wj~O2$;hX5uHUJ=L}`1MWUXV+2;EgL(Hw3MX!XOY?z%jy_6Txg*Xah4TKNTV>dC@ zbWtXH;fR8aR(Ci~^(v>yZpnxcyW2D?ghGvUvirDFpT1?v)tHIsW70La#F*&3MnUk+ z^tO+`JxClZ$3*kRl;}PydP0%4SQ9SdZi_AOjah%h5c^CBQKEf2>Cu<*W9FRy z>ZYs#I?63%;3VEp$}iSpW&A`ri~mUD$81A1n{zcUHHI3sApcL`kJNL(fBf6_RMDbV z+hp;&w8m*e>jl40!?|S#C=-F-0`YKK!IuQXraiFn0WcS++_$TLdsGP-y#H4_O)|Tm z>_N_$6@!XG`Yy2z>M)v>9O`Gh)(>v&!erF0-$(8jgu$D+N}aW0bGjmU)gFSmFRQ1n zop2O)b7({WkaQhdzX8MhwV2h~xJ5N4voqH(2iU#3*TT!sJk_2yrmSPAHIF@?qQ)$`-BFIoR4=Gs>uas_l}1WA$Yb>D*8G)3@b|fZ4(ug*gddl-oVTsbu8ZF#O2Nc89%4 z1ilD*u{QQ%Mj}L>0gsNyyW=1Cj_w^#cD+2(z2kDHDEoxTiH+U6+6}f&R<%?3b;9H} zdV*Q**;^RcXDOcWYYE)DUGQL1U8DuVK)0gE)s3TRvP1JGx=C@-b=j zKoLe2(JS=saGbtgF6iyXqsS|!7`2SH1a{4GTrxbMw!>*eWx?p^K|fbGiz`S9p>yb* zyC48C-b{sER@t~b?q0{y1-(S{eT&4tj4QDt*?l~qH{S?56on*dK?{VxGMPh6E z&d1AK;RU$(Zh<-q;So1E6S@Z9L*yJ_>LjZWA22YbPbd^s$|T_43+E8+<`%=2nLpu{ zqdyleNrW)1W#TL}ZV1P#;s^!((+qjqVAlB>%k4J4Xzy=9!JSHw8%D~U<3)GO&m!XU zaZ?UcJz|{pyLRHWn7eMX3{5BNKuG*>PG8j`{fMrT37>pWv>wD|dP1YZ+p6C3%Tphi z-VhlkP|parUXdcC@d{Ns3unFES6+wbxRTmPHbNCVro}OgkiQ-$m;W#rVi~#KJ3G1;KImHpH$iGlO$1f<5thu z_{|~oorr-9OFmj@?{9A$%BTjfU>%R4eoCS;;RfX<{WhOIfTx?!l#274z^zC9sGZU! z;wp6V;=@rfJTaV&XeI&RZa8-x*-9T}r6XgU~M>A?<8ao`B?6T;fHF3@-N1O#!KGbqplDZ6GA+4Wj+w2xTF& z_|qudW@b6VO14jv_Grvcyxi&{?IGz$-gyZ|=f|4gW9E1XaZF2n>n-x7WUE|A0lZ0! z7Ikmq02`sBl3_Jf`8C3h9N;CtIu2Hi@SO$@{0(%!Di=#784f$T7;HsEgq1@BVct1X zB=H@e=?yO(_7jpw&r@Q~cDD}s!WjQQg?wu%u{z2vB)567rSLW{w;{?JRXSz4MTMqB zH(Yo7+COkh!!$i~vXwU?suxoy}Kko#q2s;6s)IoCA zofPebPA;=fTl@5xa%crE6t*)fhLp}-FWKr`cmPU7{vdf~zU=$6%zj%|a?0x1Cz1o* z%f|YcU$_s93a;VQBt=vm1b4b1HY*mpxL$>ozHrn)n$WA%`BXk!k(U}@5aX(8In0W2 z1+OXYJy+X?a5ud$Z%4R(l!;4G=wzCP`%sXFZr@|jIJ&9K^Fp~pFQ^S2#tu5=i%E@a==4W@&6uBH!q&&So zehBEaDD54|Rm75)ga*O6?o!F&e(O-h^Fg{iN7TbyQ+#z_Ynj~Dq~>C*yQC@L&9Aiz zxK1%{R?#HHt*^9+HOIXP5QL@&h<4?novLYK#f6|IljgNL(7o-@vi97elSf+xAJ?=s ziK3Jd6dy*l9O380zJ(BhdF1Yhtj~Nfa6#G~IE|;9+6K4Zq8dDXj#Le7WTk~N4(Z^J zqE$os%m+4zyHqSbETn$3DpYJ;C{nRB4d+omBq@d?>?9sFYK&xVIE^Fts__Ym%@NKP z>$+a%gr@b1sDaslwppuFlkU2avkhZ+)EWzV>%t3Siw3eDy`JHs1E6lW2-U`EYe7+P z!hfM+>v}!nvilAcO+3J^uum5w8db&-C>CGl!u3=`+p1{*wjSAfvRniXy$8%5N5NLx zFswzimovAm=Rve;U6^hvJEkYp$!2Yl(@kxyW6V`!_bGo?fV;9%6}EjLN;MWOHzaQ6 z?ktx&VTaf{Rqq+DZOS$t`eJ3n)IDu@$$Wl=f)CR+b-xPXS(_BJ61rAQtq2XF)4J&O z0nfT?b=2q}H;g^)wXLXc3ZSS~W*UFBXtL&Nt9ePQ5^F(hrn*#xmRd`@&Qen~T;v)r zTlmN|6i;f*-Wik<)64Cn`kxCy%)-_GzOa8XyHoVk+w$bE7*{v={kp`Z(gzrehx`q_ z5X1l|f&20<#}tdQ-ns+_tHC7wgURo+WpG-C2Y1U5Xvd{47|8Rf47r#MLrWlY=5=Y* z=wdTz*guL!Y&nCpy~pI_`{8kI~NX> zCc`?JV~AucW_=S{IMuaarE1y^CCB(BKg3~SFHVbVfJ}Cy`vxBe-{0=xyneDz7xgup zoiAAA-i~0p1b>}8IdHrJ{ddZZ9gWt0dut*>OxNRF7ka=d4}tGVFeb0N?A(csk~_}K zSdfMmnR%h39_$An_?xd$uBrgz^TFWSGj9(pr^?Ykr^B%X2tt)Oaqvci$(4VQx7006 zl6o28H7-G7;1JhOKsva$6wZ8jS-O!ue-bR#I4>o0vKL&RxnU3jg-4Ocf8a#EQrAST z9`w20Rf%_5K3gO!b0bUX{7~@6GzY7w0mtO#ofQ5C z_ZC`*z)?Ctw35Cxk%AiPY&UC}w@aRp=&&TVcl2tLOyaOlf~kv|n-! z{HYrB792Knw9~1EiTa%{M!(DFDTD~kR%iWBL0JQQWveZ=)}L0jwbfnIc;2szMHd~X zX&qIq$*m~{8nWJYE!rqG82G3rL0v{E11a8*N+)y>yWeQHo=eF*rUab#Q+hog;7@{d z74?Z9)RRa3kga4dbCE!uCKM)$OE<+TVej?-!Xo{)E}U9_%QODF)_|KAru!$nB#l= zY~)-y{8GcaWZ=6X@A0|#EosR(wwhY3XT3x)JtPH*V=Fu7oJFLxd`HCen1#>-5{1{O z!U8xrp)nh@we0#z6oUjmmy=ZHVpS8tFzwVkwac#aG(%b^IakI`U ztrc2{%`8Q)=Fr@>3E3sJ1{A5sbIU3CHRjt^6L5?0jCRey>dCgn-_~=l_UTqlZ8qC# zD`?KZ*%~X&bV`{<@u}2A$r4HJQBXV{>au-k0X^leA1o)n0_sy7%QsYHf0e>aS)-z} zSL7{+w9h+&-Fyu!TS&V0&CG$_VNg3r;`g<(;uF=lR;=qf$5wM(E4-cgo#S={g30}_ zelyMa3`4ptH2+qRC?_yUdbE$}4-!#UBq_23!~SB&;&329M! z2m@UaI9YLHs$y+5oSMIEVdGd&yN&*0O<#&>jDz9Y0HtccE0SE{X1C>L^BcdK*ZppTE}wx(7erFe%@B`4$gdwf|DWW$S>od+@y4qbi%QL63(0OOww7_E zs0CX0Uv1Leiq7B+xh7}j?{gmQ?kq~^gSb=XzlIY}G(Z1!SyDMsE{o-}$5%lX zKSTa&u4Sqe>3V*279R$uMy%<0?>(`UePMl6B>p1TQeeely+WI*NDb-S z{MTz<(-XNCY=?k;DJ`PZ|+XmOz$<$ z+|6`Y%Yvp7YsYnyVynMuuHJO(vxXYoDy&hLsV^FWDx3d#U1$4d9fF1S^hrBgoRN$B#$bg3cL1W&q_Z`8@r{^(7<}#^!WID=7P7t5(U}?YX~|Ako)rS z-QsMbC_LLgE$y-sF&2Gcn;+U6LmiHg53n0fOHY!m>}mxmGG>i-g~O>Z=Sx$^@4_R)DUlL%vOR;qvGvAZ^bgd%-T=xDtaZ|&b{hflNKKQI zKc&Q}%8PoC0Al@ZWTT;>r`>Y}i30tRZ@H$yXNE`Yet*{C1ff@>NAwW+ig9ZWK(Sy*YPWi3$*#vOQ*zETOlN z<$R_-`h^S7ot}zH=7>H%;3e+)4vsw;1X~|NQSXdNOz(-u#*qzB`1T#v;&MEMb(8Ib zKv+*QiKm@_T1j$^Hu5F1J7Plbuuc>oaWs7{FF^M%kvLa&#*r)07q1#cZ!!#|&UKCO z1y}y4qy7^aKIHLKY#_aZQ@KAp3V@q{iFr2?g`Nq;bktV@lFxgoYO}KmBKqkOqC;c_m&BQ)BFWk z8U|blqHrWZZCeheBig3cKpwoSFC%W_?q^97^_vEV)rsuc9gsYhT5Z>%mLnpwh zb1Rkq%99ISceHxOD;ZzK@Tz*vJZQX^j*VvpC~pIQqvgJ{o#mEVyP@2uyjxtfoYGpC z+feDvmAGc9r9#(IR%`G#H-hW#5gnb-G=;V31>M`0R{haNcW9g0G&`mKT1!{7B&oZG z;}Q6}5zb>jrQA#LyQxre9A}O?aTTp}{r*Y@x-0i!LPX@lso$#9+Mh=E`dxl+=?98~ zPZ1m!5uv3jh@B#|^W4NKFS}$S0!}l9=5Yqft&7MZa%|kS9&SXf*o+EbA0JE5Uksmu zi+p#o7E@a-S8SNHQ~xS+!KIJnfK-_J9QbpFsNaIAU?bODkghkM|6D1F?o75)(NJ3k zm*>SML%|uiASvo})ek!Cbn*Ocvl#jp%O$SgSIzBUcFtxMS0u^b4x{gl?5%2X370eX zbRz6x?u~r^pnQIeD`D&d;~`DFdLO>XiLd??^pTX6JXad0{=nqR(nFzSVw^BdC3f9x z8!{l+;`zJFS0Z$XJh==Ha@L6FZp5|vE$Qrs!hL(ml0K|a8tE-PGSvZdQuH?7O>V!0 z4&BbDqC;J{)S;-;ii2=<@HF=uL{S4G!H^x@#H@gKCz-k0uUkdFqXaV67e2)NnrP8@a|EL~BZu z*}APd_3@1=xT9^xwrlJCb6?5xN`{7@V{LqDnqY{~YMiVCT6MM)P$;w#>a zH~V;Pg%%y4v)H_6>PwCNlcX_i#8JvvieE!jF#IidC}vkK#q_wM@u5tZO*9oFd2SkQ z9`f%kRCG@ak1EIGYCI1d$lL=qH!N}8O(cEScHDLyyoe8jX27-ia>_&`u-(&FP12^&hjc{i6>ACJBd|>x`Cwu_6 z9DAVBV57*`j~g>qh=eY_4h+tNe1S>fJ}9#}r5^w%ejt1o{{i{Em2)cxl!ibL!Oa_Y zNYANiWkdeN%Yabqjz=c>)ANj0bw|f!#$FXAyhNGx&F(I&hdgQA{IZ@|ctWV+e7j$_ zqVvH1P1uc}ukM?PpCzg2ZQz4>mJq6A*))g2*Ol`tzfUErxjl?GPXK5hW==|!*N8*+GV^=<__H!sgg-fNTLk#MSkclz%SD>J*daIS|K#4 z!Fr{qi(aqM+pcYhGtkbYo!Y|pO`Wtz2uFLx4X;(LX6BzXL48yZCFXm+L9+13my4v( z-bMy}8U?Bz%0DPGPWAAn;&-H41;XMPV{yck*t!sBtD06&qBx$E}q7@6ps~B3JMP*HRIO( zDjr9=zm;jCKd!p7m`*?gYltbANta{xEEUVR0ZxdQcvf8GsLIGl&&s6l!6@?7nfy>r zs;$^c$rmqi_bTLksHD!)To&cZWsI@8OAd`ji{lbmHi)0XdIWL1wfNUyUOJnXH0;pYD_T)o5wv)5@W-IhHR1p$(*YA2?EYsP&{vZ{ zf7?zN_mVosEQl}^z*wx}(?M>tV(`sh#Ik~gjH22N6zDL(ImP|7sn-Gg$**-;i+$}w zPK+Xh-pdw^Tm7zAH|x7pz6&WBu2`Yq4jqBi-EaaAuS=$i7aW~bHsvPV>bIlg;lERP zm+db>KIIPfSP-0wgy1j5N!&;q(MoPN{Ykn8XQA>EgON=MyU@?_0+DSMACQTk zfpvBeME<}a5?q?L70`O3TuU$lLQ8jO9Mh|Vg&Lwrbp(cv8a-z0xbYL(LU9!WRu?Q> z^zC=w|FHPSpML)3*WZ5sjNatK2i42K9<36T&5t020SFGQnNSG(~Rck<1@ws}b#E+rqRBlWgQFq+OznwitA+p)EHq!+G3Z7eEp3` zahrWBK2ZLg{I~n@I=@2LVXLgr;ouksBfL3@0!A4(jKzGoJ0Ya=pgo8hf?NV*N z?VrzomsXpX9|Pw1+f!4yi#$eN1Z5%TZC1(e*I&C24zhygWH8WTI|ioRX8Vo5G|S*1 zYwX4PDK^>!vV=FTU9>}jhVi(!qvd{feIu1_uyc^3T4&l9G1iM4EWn>gJAcmKmm{Qe z>;3g@P~HIaL)EHDnz@arf;t_Alh*d|zM7{^4lR{hNQOolM2*W9jydXGHjf^HYJb7o zM0tbo<>Zlg_4HjP2hBejb;(iRdO@M0fXata(ev1whhXo!$<`Z)qLw-IK8FXaA>=O-FBMoMDQ}k90 zmbirUxF@Sz441y+4vdUNcrglg{S-IJ`}?C@UxFcnwo&NajN7#xwrUKz96^Tnykah` z=;vDslj)r(E>*>kxunLp;T;%YDsIkU>W2@)-R(qULfe5%f(LOEor>idAw<6PLF+;xU~!9I>yoq ztWHP|yW0|e%@mi#?9G|xq5jZT&WC7n(_u?n<{WPxY(@4-wnRgR5Y4W=&;kAEPO9)u zkuPE0@6*e_uW{W0wZ& zAO+?cpE>e47s5Unzer;~t>A+bb7hN%Uj5<%9-6YC|CqVKJ>{o6+;Kp!Wt_nm20xi` z=U>-`#du;U!!3BQ+r&IlyWoU9hB(T2Z|KTrO`P|i(DvE*#DgZmlUPbrUHhl=K4^P5BPPIe5NE|Di>jz*FZQ|mFbkZj4oV!89T-Wv0?aRpYda5Cc` zX%U-^G~B*KgEHXxKfVI%gY>*u4mGdtP1~NtA_5h6guKwG$EwG3fRl`doWhwH^?&V|yTAn1}7*@Kw_r zdJl=lhj^P2dSf_9v|XE#I~}E!fiOGJrsYdYvnyeC6_Z4&Y2n^@h}jv1@FoI)S=djb zxOj4Is*@sUIJcFR($T!6=@~c*f*}^|15W`VM*Mw2Ef_~%v74uoASN8H(mUaf1c!^+ z@f>l}o-w6;8pdPNy4iO1L{bahk~5Guc8$8+Eo>Abrecdii6np4X613!6nd5Pl*>ST zABlD>1$(RnlCG4}SvISQ1ziP6FueI~g~3K~uvBc3e+HVzz(05;=GHQx2`*_i)@y#~iO5JJ z0jWnYkd>yzrC_FK`OB<-eKF^kpFjS2&KKX!`Tmz#zkKz>_vSjrX=Oabi<&F1p#T4h5b%ZvIcrV&m$fQ z!~4twA7-AJs|>}!j&=zU(WxLB7!k2}!f~nP;AXKnI8_n%}TE*N|1hG4QnWR(5VmWe)fZ_eSF@ zf4a|2xcm%+7teC9LRq|(nmDTEcci{C*xxhn&o>i>Q{Y~@-qH{f`fmrh6p=Ru>Ono- zvL2MQaXVv5vzTl32<9Z)7NdJHSp;ro~0`YJL21YO6e9<_ILF-&{((vN@jXRLogx5 z1aCkRM7bSk|I#e;M?4HkFLuHSYV(4k1FERp8>y@ zmbG4h5L>NG=T4=;)eQIq*Y&@PBPQna^g>QBUBPsTPR%2-l{LVDG_CFz?_HdVmwo?C zv}3WAN_6!xAQ~@5BJ1?uB^fPWNHAK#j0yh;G!=+o3mJ@oan^WLbdm)> ziciy9;a5|dl@uP>DoQyFM_bYEyqEL%ypEP87k1v-ojWSRE)Oi`xDDtolhW2=$8E`2AaZU4_3Yc|xA9drMWGQ^o^EIV&`1ndS& zt3WsdYt{E8E){Zw8$XA-95kgne+TWV`LkBKeh@yK>|*8)4xYGaAGk#91#7=$P`VY+ zo_p^K3^IS)8DozK6%L}4n9W&uKKmhCUxz^CzX=fLvO}a}aL?}s3r~*U3H|S4Fe%R~ z>}neA5rmO^d+&c{)G!-B-p7@Xa4PF8C`WFnK0HC^vR_2YP|?vYhjL#tVqFiHuZ}NQ~c?yVPuYqozcY#hLMDp}9_Diob8-8uA=y&H!${O#g{V z$3~j*)qaGbnr zA&Ww+$@bCXsi3>VBi*K+Bi=t*egu?ebAcWe+5po~vrNivkg6(-pE!r@rsG{CJX}fH+yvxri z9r!L!-VQ;DTQOyCTf%G5#@H!zRxY~~{Sk9XhDA2Gd-uUHU5oqRwbX6dxWzxFqTZRu zplU{%P2JpXmIl1R&7A2HQJ98Ssy9i+m#Ndi<>hLLVc6=mJkxxgc$8=k5x#&-z<32V zYi!UVQR@++jg<7T8_AKbMhhp4*=uZ6K4r+UzkZC?RC~zT^T2R5jus!Rv5~ep7!IGD zBwYHp!|BsRLYJZvFBnZma`|`%`(4W*;@eG{0LZ46)9cT`g9WgsU-Vs!B;WHMo^}>V zrwA8!D*BAV=PHbs1glnY0|k#9L8oZ%@XNg}7B!&U^pWJS^gXj+RUrPBs0t-Cw(x;t zQfc>x5`_t z7RTW!BW+VDmBzO_=wgv49rV~^4MHOaSxeE!bTj*{Q@530)>{e}f!Y8I$r!~61Jmwh zrCa6pVRSatCi0AM(AW;U5ey}h>x7P;c{Q<=xnkr&YdXR7(MWI|kJeefP8kGK~87V7l) z4OAm6?nFN0&y#CICX%;mCGrwUl5}qQ9sFsspxmN73XTzZPC9gmH7EPvs@%094%+xC znfXm#UT0V61-sAzC<}ek_<{$p&=-7c0`GwQ*dU*5yjES_r{6_74W~sY?AJuf0^5&S zOfnVnQ2een$U0 zH{k;Hb%I)!!ku=j5$?QxNbl;B(|+~9URBnUCcYVN6RnEiPxtmlx#ujzduw;16oyX3 zk-6V-;+u7>Q1&b3d=${<__sjOa_RU?s754Kr1d z4rzogrAuriDEYQ(Y__W6QSLZowpBYNkgaXfD5qzD-tLbeh$n|S2om-R9At~4fU9$~ z5yEF5WKYVZ70Ly&qvqUt+j&zPgz(G|$McLbl;a9%#X2+Ty>HM#5WBrc;>pZK%GfpmRm=3~yWCz1G|2*)5ggD?sMSnU@zE=&GjAM3gA47f)`rB}Nt5APqmej^K@o7(Rz0OU1NPm7{q#OUZz20B0SEwJ z1A~XtcO+@;kE=b>`5tIY+x~R#XjL+*kgiglJgVAXX4q2P^*HQhf*FrCr+e42$=ofx zJmXIHe$YRe8~IZbwZPNjst+9fBo$+MqgZ0-TP!M@4w+@}Qok=&qGy!iz8ncZF6sAA zXoQS^Yhy~op~8r+M`%tqr&?|>TGCtQTIxnkmzgfn3sv8I6cW^BqBlOe32pkI(^O)- z^A|hl7};Nn$~o;8TwK`ci#jm@r$;j_>r4wfV?igM(0`h@SL34oQ@?i$`DbQ=)0eSr z&`r=(0)MSF>1X zIS~XG$^c`T_ggrlomba`XJNbyUE$Hi_ekva-5rW!o-Wzy(*%K{;c;z0=-qF;jE> zATtkge@;9MM+eQ5qG3@wO7f-Do*Pi|=~9m^zl21A?G;9LKq>^Lb<`K+D2V~ri*pvc&CkG{?Qv)O0lAL0Z|}yvUq7Etx2O55o{~NR+zl@{ z=@z#V%+{cHYn5QDBy4bF=|q=w$zpj$CN6nv!Jp~!^LLiDBogBd3ohE?*SER}EXuJB zL{ohGGIl*8Jlc|EicOp7P0ZPt5a&iQsxgO3g=+w#9h`3R>dkY5(=Uo6Q-!AJ&7P?L zplzdbYfo#f%&Mq zvYkW=;u8#yxf*B3LgVRMNu$frvQd%jO7OR?a1l0XH_1*)2#BMmBL=0pabfE7RvuRo z!}~x5L{idR{*AMI4+J~%XuLPWa6`{Bgz4f?&iYc)5rfwKow$JlPtJFMcuE%+#v#|TM-KF;JCr!o78ArWoSBTBiGDWFHFKE?SQ z1XolFoaB%2hW;L0d?ywA5wr-|-+q%yF2OE^V9V3Z2j~x0;D+_|nXCKa8rS0h$JjG` zwDO-htu;@3<5_OJ1Q}3T&g8~i)5L8^7*1b20*o8GVws@o>pvv){! z+oV%qt%;e^(a*8s1}Y4atd!@bod4Lo3XcS+mRs7ExQ$7RL2CsKM$RhzVa+Mnr@>G) zT8~KP6!V-TZdDUD4q#4vu<#5qH95{ z7m4Q3D|4#+OOF$zi}xo-`3-Q%VH1V%+t-oX$junx)mJmUkMpe_tmQ6xBYD{7loNWq z%52*S3c_)gS0_jRymcam=5GIRL=A<}KRE$SCa;-L$&OL{5(0;e(`(ga7Wtl zDP)ZUg8zJJeub`*Dt*i zy`<6FAyy_vYp}3rD8Zdmlab{UqCxl3`vk>t*P|VzG@drm7n55CQoncY9^e5wh7Rmf zpblc#3L-t*>lS=kDM_+mL!eS}L2Z%GCRrWtj4aao-f@WD0`pX;)APfq=^>CdjH@4)#L%)N!4SC?Hh`<3_|#v`6WQUb-YKR8C|KC9OydFeivCvKJ``w zK?TBS#2!GDo~c|{MA1Qzw>I(amBp})o{ER$ix!CJGeKRmMl;wD1GR?f|(`C z^9*=4MXQ_ChMq@*i?ZpB(dRL$eb1ft7~T!f+1kRAE*NO-*}t!KB{un#gkH&v?H-5f z)XE1y7x%ybvvf6voXdcbhq&3*h?MNZN)$oA)S;ettMmbaN;x#fh!;SAM30F&25pw0 zt*4CTD^d&us&Q6K#2QH0R&I0jq^p5QZINv$RvU6Ag=^zE@*l7^X>z(Yo(#qmgR4u? z3I;@jtf5awi{cg;MYGDm1l?Gt3R%yMg9!nV{TdL?t;G*%y3x_GfrT0kb=Y*R*dt4bw!3-ZOi_YXVEJ69J#`N#~0d@qZE{R7sFer$~t zW*(fMQf;^R5@=iH&ta0(%3GY!(w4u##^LsnPdbL%oOMHsA3?8MZAyBsfk_=@Q4;qB zW5N!A`6>s*Y8uhkv)sbA-@d;{zcovs%2mEXz8|enZ}e{K*@v&TwixkV0&XJueR=|?M zsZo5^{?I&PKlfQOlpKac$o?Va z6eM;@gzXzJ_iQeMTgyd&_O}=9pZEwMT)8Ab(!XPs{Suo8{g}{F9CN{Y&S;OP6McGf zz-aT-w8v|$Qfp;R9f$-v6>BF()8Cn~5<}<`TRYQjTBx4IkR3B2uuJ4f$u>2Y?zmA# zH*oT1#kseobzS3mE>^mkl(8hiWc*w&eVnR1X8QE`r+H$PN zK}zvh<3n+Zq-$>x|uc@Q7C}wf=P13@<7NKLcZR z*k-K+r!{AQTiDO3)*%S;G3LQj^*Wb@T)N!C&i5`HT?E`EN|`}MuzrfPIH>F1&O&iZ zmL!uzkWE?=xP#`H&}&3s^Sy_7RX_$1C^3UyYSqZ}o2ODQR-zVzekY$yy!f$}Q*a!P=W= z>iiuWA71|hb~x`}m)`qp?)F*`f*fHT;r;f)q@zD=^BCIjTJACTz-J*m9z>>(s+k9C zdhO#K3g>qR_3ufk*Kin8A=o~`gLh61|IW86PAfs~>~r0r0ir7d@@WKanC4QuC4CRG}zc zlxQVJ6{De47bhE1`6bPVaX$)$Ebm3#vo5+1b&ueHbR?Zlr1O<;T(Y>wY_O6$o$3G{hNa#a=4kzUbT6*x_L8Uq?J>*Rq}&sf^4{d;D2@US@nt7wG));c_R z73a}rS?}ef`djR*p>Ug%%{8`HLb(uWrm>%0XMh$6UKk1{RQ8OGnk}LIP^Jid9a5|? zi|B}vUhuz18}IhvEEfHD71#Zzs$Ul3^R!Z+UacsSNCbFYd@A}ds-iCgnh2HtdIIOR zcHYmo-K|z-orlYejop^M>*N;r=2sKYIbx$j5o&rI&eM{O4w^-vz!B>YMw7x^*vvFj+CvbbeJjJFU zbUemaEepnl!{LefNW~p)OAaK+=y*rMVW#xs*SXs^{8dHuIJ*6Z>B`3TZmra{H!3qC zh+slB1uxKDgElxyh%{}UH!VW33of3bN(h+PKJXjt_C?FDnJ4I^(nG6VN;aw1aMayal|f7ByOA znp6nlRN*YCE7AQI!yIc_)~uGm+oe{D(8RL2v^mw%Uo-r_u5Kv6TPv|nJ{nI!mfF4=;n&BUu8!wvFd= zHiA%_@qZIc*JexmT^Hb14zJq>X5%kW{N?>9T+XAtXWeJBNf6|3?xHe_B<45qs9Y=ccaEfMd&#qeJDX5g$I1DV zKkjpK$!#Bh9?WbX;TE>8-Q!_auVV3dFb%_ z2G}u3Zh5{R3HT`3JQrb?xpkb(AFa!N*zvi0fpjq(ISv8uEyq!rzX8H0$n)BUkIE2w`|BaR2g~QSPYQ%B-^mlCPw&71`r#^|s^33AlS5F)CC-dW)Se^MArepb z14|GMD-G6rkzm_86+-s?Et^Jt$S*`za0n&bd@kkJAiUKzs6j^>Egit+lw41oE{mmU$x< zz_oEFjscOm@-b_m;)gT8FX8P=brNB z!~B`axk9Mmv!r4vZ0LN&(I0}WJS`Dac=Zf+p5k1OeF*t$f9x5ctkXW#M@H6aJXy;l zOW=?+Y7HENm3@fpCTyX0tMElG^gaf@t8u#bDrmodqSZA*9q5A!J2n2xGDG;e2OS&g z;N|XPW(I~fZky^$yy(Fz2Nn*(9~@s|)jkL_U(53Ob>K7j;OtCFmSJ;!4P3NEOd;MA zQEAUWB~^!4({_Aw)Di^Blz^B~t^ zl>>%b@jS>Ew~rl{AazfJ=imH!-`ev-wAbRJB&6fuw66Hv+txcQi$Qo)rrRcd6)c;0 z;J4GVSe4vr728qvNTMwM;7H$0uxtEqRHl+QSFyF`Ek0}^<(vlS$Aia#I8!ULAJO;_ z-ix$YgkLVxVyS+mOw08gb9!s||EJ_@p)%`j5=B7!`}H@FnTA(vxJ5Sd@iyJWbct@o zFzT%L-*TnoS2c_Ha7weB;+Imyk=}G0!p+8VF=)Mx;KS*}CP5~!J_#i8JiUqcQ=}8F z7!AIwU>{lmk~oPF`xh%U&U=zZhCe{9c^aG=bLuJvwYURjWAR*0Z?Yi!&FLfEE$F?G zuwVW!n|zms6v0QenDq2^`9>=#EzJ09mIdEVenfAfuJOItaC+c1vXk7LPZZP|Tvw!J zm^;wtFpar&R`;!gl#Ol3Rl5DQHZ1K+p$xdmZL~rWu$RW~8HjID2efw9r$oSQ5>6vB zK@1ryZTLtw$XwF&CBx}k5=j-I1fkGN>+G}?%ILB0UZqgGI3!HK?L!XUmm}`PNw@(1bz^C zZvmRe_>ksLG-%W4ZQ5~H4@W~EV5>*ZUa!w*MlFp9CXVinVGq0G)BhgJia;8_c4&nc z3BgyAcny_}8e zgK~2m?(5Qehuf0vL9ls14ZCaxhD5C!Y(^B<|GSkA7|(FQ(hkZ+QILT9kxaPW%OZ+b z(VNnPLvEjj04uIewl*8_@ey_;rym7H9~^+00$Nvq)xb4fW7N-+p+@6v zmOG}alpIaYqRmtIg;UPBf-%Lwn#6dO(x0WKo0~4tU$I)L#aEl~wm$)y z65x-$Xo_mfP0rJ%$=8&4nOUgQF_`$@QuSaW*HR;)m*Y?9ZN3juntY1iM|oc9S`Gzm zo>0p5hjBrz?>5hdET6b}z>apxEH5I=zs`rB!RaVao@jH#y<>hQSfA@r_vIr>c}f7# zlyCX@_=FU|=6=rEy^-DaEQ3h*L;i5<5HPlU=*Z-;^HX8Ga(5Ei1QS-4q@6^{`VOPV z{i zaBWz~X0TpB_bHGs|NcoEdjya_2l0K5hM|wZ*0)H%C6>iYK&m|R1L-%9&)kWZ`ZIz@ z=RJeqw&w?tAkwe)F(|d!5=77XBtmjPb%$J)YgT7LyP^WvH4HZovh?q)_cF@6PrwEt zt#E>ubNG&yF()99smX-+Jh1i}A={)%Myt_iE{mcMV-@cWz{vsx~1_;nDvwLu>*t{T%#^Oxe~rb;H6T367-(@lw&VeVGy znBaFLn_zMSvL2TkCV32lXJq}Lo8qmAhbHl7vW{_qF~wNey`atuiW_$ICM&T&B>!v( zp@w=>WBLCaG*5wl^b&e@XXA$%Q;a_o7mb&!X4*{a1s%MgSnmKNQuFmg{EEx{rkU^( ziB=|panWl|4Z~?*GH7Y2KOq17A= zxYMo@sz=MWSskU!pW54Y`ML9wQSt3byIRLFvDEkEskCG$hMAS~$Cm*6+h)d(!H{|8 zOVJ`>K-m$*3w#STK<6F3QFn5UAX?JBWnd=DI|PfnX?T2i&R!r3VVz{9tUp;zr?VHR zx0bmR#G^PNz3z%WX#;8sp%Q$F9N|+6?Pbv)B(<~Mp(Jm1uKwI-=3lZ4_)2(@Tr{UB z0e0pUuL9_UQ+z7I_W`T1xt+xJKKUYH+- zfCAb_hk}oF9D1$UUQLFuNMI^K_L!34m&p;(#0vxS3QR06_;vHUNv4}l=!AOn|CeB4 zp^7oDic?H-uz?Ua%$j6bqXJ_d=7f`(k=XYlnrFa2eu308k`_jUR!*9)Qn#z=%BD+j zciU>AgI+Kn|yUSU*nz+=lY+M%gf<9+){8m5n1IFK`*K^az&Y4&~OZR zaQqfXLn47rMOL>+CFf;aK^OFXXn({SJqUI(>^c_=h=2zrJXBpB7eQEhbw>+Dg+zMY z)|*h*2tq^V#=<87T5L#*p0#E@&O5$?5HW=+GG{%rpLU8+tONQUtCB9K{n7Dq|54Z) z2{q(8#uY;IIs}5WxyM$6Dw&nWS>8Agn>10eh~W|}J@H!&`tzT0JnCXWpH)ccIx}=w zn@>M-Cfv8n;ktxggxCSG!Wpd_eM_)nP3XGKo(+JfbC2RlQQK#P0^?ZFcij;V#)*rw zq%Y<|)>&XA>%UqdIYOR0lfqc=?tHl;r$9e8!NJTyz=n~YRI1J{!ttVop+$gAI@r1z z`{xE;8DywA$Ej|-jK7roq&`RJ!b3E3Msv#K9krL}8Mb5%dgmxo&xHMxP}!R{fWJ zv`!jJ?4yA%u}(e)cP4JjgTrskZ@brQ{yU?KeaP}pC(50x??U#hiDU`Cj+jr|;aoCj zQ#?9Q9)s$Oz(&!&c>MWcWhs2wr5vWMwJ+lE$hHbKC)M2tt8baHDAMqS^dyvGpg02Aq3$fQ8K-z71ybb1G zhG=Dqqz@lR$$ag1FNVLmpf&U=me+N;Z1z03vJoTRGvDR(U40kEr;s9y$M{f&kX&0p z)A^LcX^$;!w{i#zCTnhkcXX$^jayCg*O$P(V!_z^?k!dnJYU!-ftUbf{maVCvO>}00tcUTh zS3@tUv!>HCQ*%T}y{3EhP)dWQl|ht0DFix!wz<9@xD7V-7U-0xx}qU!6~V>y7Av8X zz{|lVlT#7)F;ApzsvX*ANsCVnjG#@$)4Z}LHI(abYgKD`xQ`%;us8^ zS;}FcshtR8CQu%av?y~TBfPB|28(Q!q-ypK=I#vOZcCppM6~S2XD^)E;{@7#bAEqF z4dbC;+#j7WL=vS*r1%6Xd7z4ZPOW1t9zlwXoxcp8PQ?(A4)Ie`;MNAHtb{PzlUAEJ zl|l$v<&bq0IxKd}mK>0C0lmjcpP+xhJ?d$%o9);lveXgHdmM!9CQiTkA$pUQL?EZG z#*mFT=r`c}pI+#V|Hcj2IVf%5mOfV;9!s$8A0d#^GjtE!<({HGey57m54ai+L~_D% zBjkUZsZO3y$ZUme!mbW6w^_R|2|MBuy|1$>)J!@b2WhqESf}gCHyr6m4&UoxA2W|) zNt-0%%~_0g&(dGGWcTa*X*~1%p|Y#9UHoP)oR-ozGBB=4zsireEwdzw zhwlxI{(k)hV3*4f-YWY0HGU9IDnC-K{hBKpa2{LckfsJgA0zGzR`}x{jcfzqpK3iT zZNn;_kP%xX%el*hzS8kz-(zs4T^i}MMbai@TKlz(79RXLu?Qf~J8i@`mgsofiB$eJ z`D&*lVK4&oIf~?eI|bloQaG8t!glQi^R{j4`>+>Q5Y^Y4Y9&S&TII9D*!m;T3>F>z#YQyUa+MLyi5x>slZ|<@N|Q@r%%59~ zt?ZEf)$cXl)~6`4Pz?HQchmFe18?3Rc;h=?4A**-O>3O;v(j2l`8DjB$){)6 zsH5{>r~8!$;6GvGJXp%uhLSs^Q5IaX=d<3}pm~NT9nbZuV;noJ+!|f{Y7}`{z*Kk< z*Mt1Fdl0t|;!!T-3@tIA)lpVvPEOF9Lg3F*vo1T$_n70Z@y(l%Z@UQcB4KkZYSoLj z!-=!dw%j_*#{5szl3UhUkX%Z3+dhL)M$BQ{SDt{|_KV(qBLBIcA6KlhB@?w}@w4pD zlBTkW_j^?RCQt9;R{_t>+;2pIbwitp+Y4dA_}V^2v`-$s)LM#cq834LEihe9TnEzM zm)*fG>$D2W4@1;M6fS`sj*hS7FM_sqN&hj;+maBb#~s{pw46k-ie( zm8uIrux|gdh-d-`y5l=M+X9(RHi}4Q=j4zXW_h!h*nhQ)(?a_C+eJX!)GDw!)^kM% ze>-XRU0~>Ky!1&YobbWY=vcV^i5a=J6ci&X=@RIB)wYNKUjV?B3&-n&9}ob!bzQ}%XUY|+;)S-EE)jPixG zZL59ZIOPjFE`ir~j4!Nz`{Br)(*z2qEfEZ%BQ`=25&BNFr^DF&M6w@F?!s*xNg#@J z4OcLiu{lkh=xWA2?|xHZW&O+E!jb)MJt>LRKK-)_*7q>I(C9|p@nm_U@Qk4&8N$kmiu_R%9+SmO{$RxZR zkIIDSt^u+R8~@z)<=6F2>pT9nXj%@~7Y!5yL~tpvGQS^kb^(Ua%G?b(g|O{Qa*TQ0 zrSlF_V~PN%;{A1%gic9D($W%9=3)~Im8k|%gDaCWc!)5@XfgTzTB9#Yuv zmcO-fy4Az`aN(;hD8x;rR}b+q%lmVAMkrV>Q~n$dPnmW#R9i=k}=W zS6c@5TLocLHVj$=`yB#t!(io5xgMB+LAwYFlqOk~4^*m0BGr_S1nTq^C|THCBYh)O z5&Jb#L4>hK)jUm~h&7$)C7&m5; zN)chB4(PqU93<-<_jEqU(6NJ_QoYhYxbc@+j@zU~^YM-X0grJlp|uUP1iVXm2xet` z4=?a$)eDKXjke?ow7K!|W#%BGdjOfE)s8GNQ%{MPI|aBpCc`RJvFsj5C)nWb+3c8!H0ltpvP zXzHEIw&f9s8(62~`ZXGg4C>+A?v);y(1SkI zZ7&>l^C9*434%y9ULbXdEG>Qx{oRZS^V7dmzeU>?8Qp_LiCX6w(d8fr3Gzo7TL+9z z2wi})uqYH3O_FJ=Y0C&7Cx7}!pyQcR*mx1fANrAk%ZmF@a|n(yS3H)h2+ue5vtL5~ z>?qScr6v&KrJJt<)1R~xLUDDecEM0FH(^7kj=L?l4&9jrk0`t(@%)od{QNwv3F%tv z4;|`eQBePJv6 zJCs{RNpWS3>!>=q`w0HDQZU*T)semKRs ztqAu)a~JsAud>4sk*T-=5T`a==(JX*2XrJH>0hd_Sc zP0(kI=ClY~G?7|i3Bw{$ZZISoafeA+X^K5Wm8aVr!*XGDIJCW~S=r_MW!P-SZ|#!< z8-EHp0vp?qjXxEPIj)fN8-Mb1X8a+7N}qhEi+#0H(=V*w_6+czQDfzP>*oVmka!!c zPa)vaHIx8{-^hV?M_;Xl6#G@%5Qe`1F~kY~V3u>)z~VGuGN+-elv_UfNPa-EEFVHJ z;QEBZe+8S#j=L7z0PBKId}<1qWyu#GMYDZ2uKywjgd!H=t=7MFgt9sr1LHT?kw0@s zxchwmnPq9G>n;mcf^~}P#Zlpo@;?21_QGz-jCxO>C%~S=!_a1Yv6F|;pUAa#CVF7{ zR>3Of<|qmhmw~km-ywei8Y+ilc;jK)+86dZHKgOTqZ2oHkw@Lv#DG8jZYAuQQc(03%x%0wilQDc zmT7h95xIyQS^~R9hx>r+=sGC*@5Vg%e(evJKB(#(K$pAY4iM2ajI@I~A!uq|nDU$x ztuIP!-RZf-g-PRm$P2n@-56E8u{afl5ue$7BdBxvcevI$)V3v0b94)yb}C^u%6ewH z$DD@4^i%iV+25TWKN&sldPx*J%V6@YT(pI%>r*rC_QxFx1+#a=j3wVLv}_;#&GKI@ z==tc9zY)UO;8XAkorM;d4x%jVRLNp|Ev^7I$ZFjW(=}a}uL3h0`nYR;mm2mA#z9&~ z)6bQX%ZuQF8M)i@o%#KSqod5}_l}LXQ5>JmZQ~I6HVWwb-sDm@fxq{|>VNN+VJ8e* zH%G_ZXxfGM($_DcvQGP?rDx%CXB+yEHE~qb3|M&^@wv{u)1hiOQI#;)MYx;Oa(CDQ zQjA8Gf-4PqJ`a{p3Zl8k|C*d$1!nKWO5eZchLMf2)FI&GjnuV|Ho^VAyMTVclX|L= zw?h>c0d|fXXfMR$$ry7l4F26FikHR^R*@DCN2r1HzVRpd;k3`!5*EYx!K&LxqO`;b5#+t@L}ZCG9h5`bF@jzRaNnX6TATvo?AAhVcKJdo@m0pvQ*uI$gzkF7W_C?3etr`qikF15?23^B_H2ahWt-Qy4b z*U?sOtK4ym;JBEh*wb&xRGY_E3*Ora=|pl*o=BuC`xB0y_o?Jl#FL}anc{&Pp@5T~ zqS2y|E*dp(#J3S}ar(BmSvs-1=s{QfnK_@Bkr59ib`}3w zLC67!j)NZCEbdAxKe&b}-SCGn|0^_92vS%eqMcHz+(btyCBn_^;%By{TYqei~C=6?g4h@fo%qHBK1WFT2VHAs6 z*1SMPX0+=l+dBNVX0G>Ah>SoHQa_?pMQ+bU#F%o)hE9YZRpq=XR*<~qQ*0O3!g!Z# zH1K0(f(#?`2)4c9d3^}kH^%q(n{G8~%zL}tyA1MuQ)DQ+TK%>Br^8GpJenH*|Gler{=8eOn;PNxtF1~H==I*l-9w_`+s-gi=1N+ZilfBDY7CylmDB9$OV~?+ZwpplAYkc--{I+F} zK=`;`5s%P^b<#vTKC%B|$5D@zRip4Y$#>2=1Q!Nx`uCGzC(Vf6G3HT^^JIUw*dk!c zx@FpWF>+qxyo8h0_I6*fALQ=d>mk_%kF#ZeWvY)7p~~p}-B#G!{}7b`{w2T+4tKrb zTH71P0%aT`I~r}IbW>SW0N@_T6kTtF)k>9Cikh6OOk2mjerppgXGTv;+|{`BCakGi z&+kya-UXbA{!Nhe4E0Rh*S%cBf3DVxdU{>SuM=rT|6543Rx+)LuZA*1(bQUARBW{% znmk_$1+RadXY~CIgjPPh^`aLKi%_Grp=d5^@%<(;tqzMYGGg$&$7J-Aw%OA39}lYH zdVb})z>9QPP!9}ZP0|gtP}`I8{pe2>T?GEWg_4l5T}U)+VeEce%+9jf?X)Bm7}U{4 zI-l7Gr+Xd8U0Lm)vF>GH%cq=&B2JUjr_3s27laQU{hj4;c}}18pOGxjV~7+k&uqUn z1SZ=b3Wmx{;Ceq6fiQ1&kS`p6r}NVRTWPzeUoUkbyXl7B_#1DBI%&SxdJfhMych%d z=*}`=zQ>g*^0Ug4H;?tY)^0o1P|^IiDu>l=mosVcU@ecIR?29Ehmh@VPp;tUg3NQO zO2z;3z{xk!p4_*cW#D*?(D2->JhV9HPX3kBmDxO>gq#CX@OAo!LQ#UW&q53*>s74efmah&0E1?Vd{j(VO z*0S-{TfC9H@r@|BxkLz#d+Qjic>6j+eqvl0vLG212E&q5Aj>l!cB7HkV_Z#(x}N{& zi0s_Enn-!LOcv5LYw+tW5+5Nr$u8+4EEo;vo za!^%`$PzYUyj_TAKz}mU=^&$6P1eXqc)O9wMsv6Z{#w2W z6jhXRjYUT9jgl}4{!29@8%A!Fa0T*pcq()Oz77pD&)z1YrY3MjzKJTM>6e)A!o~SH@o=Cs~WZd<+z&%$+DT>|F zF&<6aQTV7wT|RJ)tVif4QjG2(2BNMt+T#--bBlQaHsewj(#4xlhBiTgT4PgS=NZuI9r(xw4&)Q@)nv zzY#YW5n8=0)~~IfMo>Hbtf@3s)0vm)SJ5LkY#&-J6!Y)uY0!d?Z074=O0$UK1E^s5 zc*?Dj^e;oqE;7|1?f&Ts$?)0N-a*{^gY2PAQ2Dmt>-X`@DCi_@6HTOZ+4q zA%EQy2xeBAG`WrkwoiMxB?={tzhW;MC#2+TO+tH-t_Zh0QhLj#WRkQ@%0!d~JRX>8r+0kNvnp&+`?Y=gj5c2)d#hG9RLwgjBj^eJZIS*3 zgvf=gKx#&G6-kMJy1syTq(Fj4s%#h=;KR1eJn=H!})x0*nE7F=UWv| z(K?(WGXf>D^W92!LcRizx1kU@)McRyu{1wg3Tsqf@-=_uFeI?A)ibp`s=a%4hd8{K zje~{s|4vFGTrb^)Yj14qg2vll-ct~_cl({27Txt4^%j}IKekxQ%KMI6f{y#zTZ;u| znyBAnHKbfh;^e7}JnPL&Qb0PMom*~p&0;!PNPn45Iq&|1U_jYAb(fz98)!DpisU@g6MZ>*^oFf zgtiDImxrXdLF&oSNwV~3xSR#nfnof=b$I<;EL7Q-D8Pz@kW?upNY37*e5OY!Zmbi(fIcG za0En(rWUG)48_F+8wr)3b$<$E&N{$tVUOhT0?whu+8&(%0)1A zIE@hg!ljAImnrldy6QfjP<2cooPm7UYLWCD@=k%3Zf;LMMg>ir;a>pD-3bN}E96P3Tz+jRhLP#` zpvSV$65+d54xVvzC^Pzt8v|WsjkBDBW3JC7o;+r8a?ew=oZ9k+sU4;ETm(t&HQ11X zt8-2>p7Y>Bhor4w)jX$5vca=+@cT)#nDbrUJjh?~Z$95ofHxjcx)fsGEh4vn3}Uaz45JnRhTsua(9Yg+-OQKEc96!I@A>B!X3UEpsTxj0kAx zBSu3zgJyDbN{7>7OlkHpYOPE#Rx&I%%$qQgHmGKhRjNKLr+a?hxW;#$8Zm*tV)oD4 z@V4qf|JKAwZ@=^Ioi=^O%=bU|@S~4EnKk><&*m7tr_Qpe*YraeS##kUd%lE<~P>6$f> z(j-uPEEP=0gRAA?`_R^<@)%_AgYA)UXd-DBkLI$!U9@+V-T%}j9C6*l@ZvC5b5SUh zTgmbqaIG3+h0(y}yBMotF4fcW@RaP%StLNSELVUhJ1Mzi@wNXw2=_E!mIgtj99Fk2 zF9H8QPbNZW-|e6LRmd)|7(-@!*=`=KLw?(@BL39s1>h}PNE6w0=2mZWe+A)XMe_Y6 zfU9>X zN(3C4F^6Ew$j25_gUBwcy_`^b){6BdUx!aFn6U`F-42eDud!TC*;yX%o!R}ETk+Sp za77Hovn7V}q}dWlgcG?m1RvlWN8Km#F8uC}OUD-5pZdyLpl$lhWj?tiK}3vS!srrd3=e@&d$=7V=I^!iX8FnUZn;V0->nvqjToqPal$ zGV({?l94Q(+ovA5TtqLwrL4E8hd^?G#944%cT!dK$e*4}L$t6S^l`g6GkaikaKfh0 zJ$BNKY)YgH6w*ikMoly-&gvz%E`p8yRax2e40FdQ8EHT-8{{-Il)HweVapVf{b=x{ z9=VG8sgjnCwef-_XiV7ESBARybQ&M=PMfeYD7p(4-Rg7UsPqw?t(2y1=7=C<^Ee3_Na5=UDf%iqmFsbp?RrKQE7=4g(y5%+1Z~M`87m;2M7R-c z2=v%UD1=9lNZ+z0K{jgog9pHMqf;$IdlLHeL--VNB#@26Bgs0Xe4V^~ZIj#Df&G9dW;=ZM+<##Psi{-K87 zt9;cBoRK(u0l|d)dp&XsDcCa8BnXWYP5Z{y6D@a@%|JqYk)CADqcnRcJhnvC0?Nld zW@(+Y_?D?u9NdM$hm>R)6XTrFW(`FadmOc>Rfg_^{0WFPHH!B+3>gvNl3feu%3$?o zI6Ur>4SB*_?vTnd6(l_orjxrQg)Jrjm$>(ii|Ts*ho3pO?A^N!mcB|?5wS`{}L9-}6vnO>y#BF&CU6HyRFlwwyDv5Q?n6tQ5$f+!*)&s<<(m)%7)pWo~2A97E- zy$L^E>QD}Edr10B->f9cvvKIKE9E1Nl>W|#Uy6gDV?13{WhgoHB1&-?@sMwx$8F*MiIt4+b1CHRIg~Dn&Q?{T^ zOr#@ZX8yo~i^wAe`S3Bte);-JcyA4UhccRaHez!W+C_7p!%E_TCdnb0oky=c2p27) z`$)GfWEKyQbgAtgc@0LD4BnRw8P1RI!0H)3qDWNeXM?x&89XF$knIMW!vNzaSA8YD z&uspkg5WPa#3?;`9#^<~6GmXYzIrz0k3sbi__Egt4shw*Eb#fiGRL7U-M~Pbfod&( zlIDFH%Y0;$X3d{c`B1zk6**WTxL9thr|&ugP3I_d>D^18=EGr?7;QEUL@s3P9l56a z$-cAcHE>%_{f!hiQ2rsuKx=mLq(@o0*8q}atUHl6~E)323I)l zw#bAop`*|m(>9;p4Y$y|e@ph`dXR0dN=d((^A0((?} ziR;O=j&+}Lv3LaAf?&$SZbPDCF*wsvww!y;b1UCFS_Ie3Mfeu0ZI4kbeKHZU(EmLY zqM&P_5$0IVq4Fdf`>!XWn@a8gDiupY;CAQo&OMX=RukxJXw)>DY1OTe#VqHEDW$HM>J&kqQ4c0Js=LUGTRb3X;S;e zim_-qij-a_U-#=^8o?y=B@!9pr%laH$D55!_WSegI?J|8!b(%JMYybqN|ANDd8WJRU#RZE0YRzRT|WGn@d*M11&C3D%}G+#ca;t6D$&|K&iE4 zJA9KaT1(xx88gIB6-w^gTms{6vUTI0EJG_Aci+Z~z2R6iAVF$;fBGpqh70eP!3b3P zb6438$TPWb^T7!l6Z$qU-qxl+eQuaQofrZnE1@}Y!#DnjNRr>A8+Y|oh( z$gTUV-3Ko%Iea$Z3Pi=0+omBZ5M1rGmKt*UF} ZNSlyHzYMn1&Vxqi~+5mvB`r+ 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

    !k(cf{g(YX*$3gc7MkW2}`(%vs9^3cO+NB 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�xc~VXFz(c*l`5)q`?+pz#-$_N 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�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�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 z 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 z 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�Q3zLu0-?)h4!?@+ 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 z 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

ZE)n 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�f7|~@ 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